From c6ed4b037aa6732c7aa873c98f8b9aebfcd6082a Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sat, 22 Dec 2018 18:42:40 +0100 Subject: [PATCH 001/428] Prep for release 6.4.1 --- RELEASENOTES.md | 11 + arduino/version 2.5.0/boards.txt | 6086 +++++++++++++++++ arduino/version 2.5.0/platform.txt | 156 + platformio.ini | 165 +- sonoff/_changelog.ino | 13 +- sonoff/core_esp8266_timer.c | 6 +- sonoff/core_esp8266_wiring_digital.c | 6 +- sonoff/core_esp8266_wiring_pwm.c | 6 +- sonoff/i18n.h | 2 +- sonoff/language/bg-BG.h | 32 +- sonoff/language/cs-CZ.h | 2 + sonoff/language/de-DE.h | 2 + sonoff/language/el-GR.h | 2 + sonoff/language/en-GB.h | 2 + sonoff/language/es-AR.h | 2 + sonoff/language/fr-FR.h | 2 + sonoff/language/he-HE.h | 2 + sonoff/language/hu-HU.h | 2 + sonoff/language/it-IT.h | 54 +- sonoff/language/nl-NL.h | 2 + sonoff/language/pl-PL.h | 2 + sonoff/language/pt-BR.h | 2 + sonoff/language/pt-PT.h | 2 + sonoff/language/ru-RU.h | 2 + sonoff/language/sk-SK.h | 611 ++ sonoff/language/sv-SE.h | 2 + sonoff/language/tr-TR.h | 2 + sonoff/language/uk-UK.h | 2 + sonoff/language/zh-CN.h | 2 + sonoff/language/zh-TW.h | 2 + sonoff/my_user_config.h | 4 + sonoff/settings.h | 2 +- sonoff/sonoff.h | 2 +- sonoff/sonoff.ino | 3 +- sonoff/sonoff_letsencrypt.h | 102 + sonoff/sonoff_post.h | 8 + sonoff/sonoff_template.h | 9 +- sonoff/sonoff_version.h | 2 +- sonoff/support.ino | 2 +- sonoff/support_features.ino | 4 +- sonoff/support_wifi.ino | 2 +- sonoff/xdrv_01_webserver.ino | 16 +- sonoff/xdrv_02_mqtt.ino | 15 +- sonoff/xdrv_03_energy.ino | 30 +- sonoff/xdrv_04_light.ino | 26 +- sonoff/xdrv_09_timers.ino | 4 +- sonoff/xdrv_10_rules.ino | 2 +- sonoff/xdrv_11_knx.ino | 2 +- sonoff/xdrv_12_home_assistant.ino | 245 +- sonoff/xdrv_16_tuyadimmer.ino | 9 + sonoff/xdrv_18_armtronix_dimmers.ino | 9 + sonoff/xdrv_19_ps16dz_dimmer.ino | 9 + sonoff/xplg_wemohue.ino | 2 +- sonoff/xsns_01_counter.ino | 2 +- sonoff/xsns_04_snfsc.ino | 6 +- sonoff/xsns_05_ds18b20.ino | 3 +- sonoff/xsns_05_ds18x20.ino | 3 +- sonoff/xsns_05_ds18x20_legacy.ino | 2 +- sonoff/xsns_06_dht.ino | 5 +- sonoff/xsns_07_sht1x.ino | 5 +- sonoff/xsns_08_htu21.ino | 5 +- sonoff/xsns_09_bmp.ino | 96 +- sonoff/xsns_11_veml6070.ino | 6 +- sonoff/xsns_13_ina219.ino | 7 +- sonoff/xsns_14_sht3x.ino | 4 +- sonoff/xsns_15_mhz19.ino | 2 +- sonoff/xsns_17_senseair.ino | 4 +- sonoff/xsns_19_mgs.ino | 2 +- sonoff/xsns_20_novasds.ino | 4 +- sonoff/xsns_22_sr04.ino | 3 +- sonoff/xsns_23_sdm120.ino | 45 +- sonoff/xsns_25_sdm630.ino | 33 +- sonoff/xsns_26_lm75ad.ino | 3 +- sonoff/xsns_32_mpu6050.ino | 14 +- sonoff/xsns_34_hx711.ino | 9 +- sonoff/xsns_35_tx20.ino | 9 +- sonoff/xsns_37_rfsensor.ino | 222 +- sonoff/xsns_38_az7798.ino | 305 + .../fw_efm8bb1/RF-Bridge-EFM8BB1-20181023.hex | 492 ++ .../fw_efm8bb1/RF-Bridge-EFM8BB1-20181102.hex | 452 ++ 80 files changed, 8918 insertions(+), 518 deletions(-) create mode 100644 arduino/version 2.5.0/boards.txt create mode 100644 arduino/version 2.5.0/platform.txt create mode 100644 sonoff/language/sk-SK.h create mode 100644 sonoff/sonoff_letsencrypt.h create mode 100644 sonoff/xsns_38_az7798.ino create mode 100644 tools/fw_efm8bb1/RF-Bridge-EFM8BB1-20181023.hex create mode 100644 tools/fw_efm8bb1/RF-Bridge-EFM8BB1-20181102.hex diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 973658a4c..9a3f8198e 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -184,6 +184,17 @@ The following binary downloads have been compiled with ESP8266/Arduino library c | USE_DISPLAY | - | - | - | - | - | - | ## Changelog +Version 6.4.1 20181223 + * Change RAM usage BMP/BME I2C sensors + * Change FallbackTopic detection (#4706) + * Change Hass discovery to short MQTT messages as used by Hass 0.81 and up (#4711) + * Fix possible dtostrf buffer overflows by increasing buffers + * Fix wifi strongest signal detection (#4704) + * Fix Alexa "this value is outside the range of the device". Needs power cycle and Alexa deletion/discovery cycle. (#3159, #4712) + * Add support for AZ-Instrument 7798 CO2 meter/datalogger (#4672) + * Add define WIFI_SOFT_AP_CHANNEL in my_user_config.h to set Soft Access Point Channel number between 1 and 13 as used by Wifi Manager web GUI (#4673) + * Add define USE_MQTT_TLS_CA_CERT for checking MQTT TLS against root ca using Let's Encrypt cert from sonoff_letsencrypt.h - not supported with core 2.3.0 (#4703) + Version 6.4.0 20181217 * Change GUI Configure Module by using AJAX for data fetch to cut page size (and memory use) by 40%. In case of web page errors clear your browser cache or do Page Reload (F5 or Ctrl+R) diff --git a/arduino/version 2.5.0/boards.txt b/arduino/version 2.5.0/boards.txt new file mode 100644 index 000000000..2334302b8 --- /dev/null +++ b/arduino/version 2.5.0/boards.txt @@ -0,0 +1,6086 @@ +# +# Do not create pull-requests for this file only, CI will not accept them. +# You *must* edit/modify/run boards.txt.py to regenerate boards.txt. +# All modified files after running with option "--allgen" must be included in the pull-request. +# + +menu.BoardModel=Model +menu.baud=Upload Speed +menu.UploadTool=Upload Using +menu.xtal=CPU Frequency +menu.CrystalFreq=Crystal Frequency +menu.eesz=Flash Size +menu.FlashMode=Flash Mode +menu.FlashFreq=Flash Frequency +menu.ResetMethod=Reset Method +menu.ESPModule=Module +menu.dbg=Debug port +menu.lvl=Debug Level +menu.ip=lwIP Variant +menu.vt=VTables +menu.exception=Exceptions +menu.led=Builtin Led +menu.wipe=Erase Flash + +############################################################## +generic.name=Generic ESP8266 Module +generic.build.board=ESP8266_GENERIC +generic.upload.tool=esptool +generic.upload.maximum_data_size=81920 +generic.upload.wait_for_upload_port=true +generic.upload.erase_cmd= +generic.serial.disableDTR=true +generic.serial.disableRTS=true +generic.build.mcu=esp8266 +generic.build.core=esp8266 +generic.build.variant=generic +generic.build.spiffs_pagesize=256 +generic.build.debug_port= +generic.build.debug_level= + +generic.menu.UploadTool.esptool=Serial +generic.menu.UploadTool.esptool.upload.tool=esptool +generic.menu.UploadTool.esptool.upload.verbose=-vv +generic.menu.UploadTool.espupload=OTA_upload +generic.menu.UploadTool.espupload.upload.tool=espupload + +generic.menu.xtal.80=80 MHz +generic.menu.xtal.80.build.f_cpu=80000000L +generic.menu.xtal.160=160 MHz +generic.menu.xtal.160.build.f_cpu=160000000L +generic.menu.vt.flash=Flash +generic.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +generic.menu.vt.heap=Heap +generic.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +generic.menu.vt.iram=IRAM +generic.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +generic.menu.exception.enabled=Enabled +generic.menu.exception.enabled.build.exception_flags=-fexceptions +generic.menu.exception.enabled.build.stdcpp_lib=-lstdc++ +generic.menu.exception.disabled=Disabled +generic.menu.exception.disabled.build.exception_flags=-fno-exceptions +generic.menu.exception.disabled.build.stdcpp_lib=-lstdc++-nox +generic.menu.ResetMethod.ck=ck +generic.menu.ResetMethod.ck.upload.resetmethod=ck +generic.menu.ResetMethod.nodemcu=nodemcu +generic.menu.ResetMethod.nodemcu.upload.resetmethod=nodemcu +generic.menu.ResetMethod.none=none +generic.menu.ResetMethod.none.upload.resetmethod=none +generic.menu.ResetMethod.dtrset=dtrset +generic.menu.ResetMethod.dtrset.upload.resetmethod=dtrset +generic.menu.CrystalFreq.26=26 MHz +generic.menu.CrystalFreq.40=40 MHz +generic.menu.CrystalFreq.40.build.extra_flags=-DF_CRYSTAL=40000000 -DESP8266 +generic.menu.FlashFreq.40=40MHz +generic.menu.FlashFreq.40.build.flash_freq=40 +generic.menu.FlashFreq.80=80MHz +generic.menu.FlashFreq.80.build.flash_freq=80 +generic.menu.FlashMode.qio=QIO +generic.menu.FlashMode.qio.build.flash_mode=qio +generic.menu.FlashMode.qout=QOUT +generic.menu.FlashMode.qout.build.flash_mode=qout +generic.menu.FlashMode.dio=DIO +generic.menu.FlashMode.dio.build.flash_mode=dio +generic.menu.FlashMode.dout=DOUT +generic.menu.FlashMode.dout.build.flash_mode=dout +generic.menu.eesz.512K=512K (no SPIFFS) +generic.menu.eesz.512K.build.flash_size=512K +generic.menu.eesz.512K.build.flash_size_bytes=0x80000 +generic.menu.eesz.512K.build.flash_ld=eagle.flash.512k.ld +generic.menu.eesz.512K.build.spiffs_pagesize=256 +generic.menu.eesz.512K.upload.maximum_size=499696 +generic.menu.eesz.512K.build.rfcal_addr=0x7C000 +generic.menu.eesz.512K32=512K (32K SPIFFS) +generic.menu.eesz.512K32.build.flash_size=512K +generic.menu.eesz.512K32.build.flash_size_bytes=0x80000 +generic.menu.eesz.512K32.build.flash_ld=eagle.flash.512k32.ld +generic.menu.eesz.512K32.build.spiffs_pagesize=256 +generic.menu.eesz.512K32.upload.maximum_size=466928 +generic.menu.eesz.512K32.build.rfcal_addr=0x7C000 +generic.menu.eesz.512K32.build.spiffs_start=0x73000 +generic.menu.eesz.512K32.build.spiffs_end=0x7B000 +generic.menu.eesz.512K32.build.spiffs_blocksize=4096 +generic.menu.eesz.512K64=512K (64K SPIFFS) +generic.menu.eesz.512K64.build.flash_size=512K +generic.menu.eesz.512K64.build.flash_size_bytes=0x80000 +generic.menu.eesz.512K64.build.flash_ld=eagle.flash.512k64.ld +generic.menu.eesz.512K64.build.spiffs_pagesize=256 +generic.menu.eesz.512K64.upload.maximum_size=434160 +generic.menu.eesz.512K64.build.rfcal_addr=0x7C000 +generic.menu.eesz.512K64.build.spiffs_start=0x6B000 +generic.menu.eesz.512K64.build.spiffs_end=0x7B000 +generic.menu.eesz.512K64.build.spiffs_blocksize=4096 +generic.menu.eesz.512K128=512K (128K SPIFFS) +generic.menu.eesz.512K128.build.flash_size=512K +generic.menu.eesz.512K128.build.flash_size_bytes=0x80000 +generic.menu.eesz.512K128.build.flash_ld=eagle.flash.512k128.ld +generic.menu.eesz.512K128.build.spiffs_pagesize=256 +generic.menu.eesz.512K128.upload.maximum_size=368624 +generic.menu.eesz.512K128.build.rfcal_addr=0x7C000 +generic.menu.eesz.512K128.build.spiffs_start=0x5B000 +generic.menu.eesz.512K128.build.spiffs_end=0x7B000 +generic.menu.eesz.512K128.build.spiffs_blocksize=4096 +generic.menu.eesz.1M=1M (no SPIFFS) +generic.menu.eesz.1M.build.flash_size=1M +generic.menu.eesz.1M.build.flash_size_bytes=0x100000 +generic.menu.eesz.1M.build.flash_ld=eagle.flash.1m.ld +generic.menu.eesz.1M.build.spiffs_pagesize=256 +generic.menu.eesz.1M.upload.maximum_size=1023984 +generic.menu.eesz.1M.build.rfcal_addr=0xFC000 +generic.menu.eesz.1M64=1M (64K SPIFFS) +generic.menu.eesz.1M64.build.flash_size=1M +generic.menu.eesz.1M64.build.flash_size_bytes=0x100000 +generic.menu.eesz.1M64.build.flash_ld=eagle.flash.1m64.ld +generic.menu.eesz.1M64.build.spiffs_pagesize=256 +generic.menu.eesz.1M64.upload.maximum_size=958448 +generic.menu.eesz.1M64.build.rfcal_addr=0xFC000 +generic.menu.eesz.1M64.build.spiffs_start=0xEB000 +generic.menu.eesz.1M64.build.spiffs_end=0xFB000 +generic.menu.eesz.1M64.build.spiffs_blocksize=4096 +generic.menu.eesz.1M128=1M (128K SPIFFS) +generic.menu.eesz.1M128.build.flash_size=1M +generic.menu.eesz.1M128.build.flash_size_bytes=0x100000 +generic.menu.eesz.1M128.build.flash_ld=eagle.flash.1m128.ld +generic.menu.eesz.1M128.build.spiffs_pagesize=256 +generic.menu.eesz.1M128.upload.maximum_size=892912 +generic.menu.eesz.1M128.build.rfcal_addr=0xFC000 +generic.menu.eesz.1M128.build.spiffs_start=0xDB000 +generic.menu.eesz.1M128.build.spiffs_end=0xFB000 +generic.menu.eesz.1M128.build.spiffs_blocksize=4096 +generic.menu.eesz.1M144=1M (144K SPIFFS) +generic.menu.eesz.1M144.build.flash_size=1M +generic.menu.eesz.1M144.build.flash_size_bytes=0x100000 +generic.menu.eesz.1M144.build.flash_ld=eagle.flash.1m144.ld +generic.menu.eesz.1M144.build.spiffs_pagesize=256 +generic.menu.eesz.1M144.upload.maximum_size=876528 +generic.menu.eesz.1M144.build.rfcal_addr=0xFC000 +generic.menu.eesz.1M144.build.spiffs_start=0xD7000 +generic.menu.eesz.1M144.build.spiffs_end=0xFB000 +generic.menu.eesz.1M144.build.spiffs_blocksize=4096 +generic.menu.eesz.1M160=1M (160K SPIFFS) +generic.menu.eesz.1M160.build.flash_size=1M +generic.menu.eesz.1M160.build.flash_size_bytes=0x100000 +generic.menu.eesz.1M160.build.flash_ld=eagle.flash.1m160.ld +generic.menu.eesz.1M160.build.spiffs_pagesize=256 +generic.menu.eesz.1M160.upload.maximum_size=860144 +generic.menu.eesz.1M160.build.rfcal_addr=0xFC000 +generic.menu.eesz.1M160.build.spiffs_start=0xD3000 +generic.menu.eesz.1M160.build.spiffs_end=0xFB000 +generic.menu.eesz.1M160.build.spiffs_blocksize=4096 +generic.menu.eesz.1M192=1M (192K SPIFFS) +generic.menu.eesz.1M192.build.flash_size=1M +generic.menu.eesz.1M192.build.flash_size_bytes=0x100000 +generic.menu.eesz.1M192.build.flash_ld=eagle.flash.1m192.ld +generic.menu.eesz.1M192.build.spiffs_pagesize=256 +generic.menu.eesz.1M192.upload.maximum_size=827376 +generic.menu.eesz.1M192.build.rfcal_addr=0xFC000 +generic.menu.eesz.1M192.build.spiffs_start=0xCB000 +generic.menu.eesz.1M192.build.spiffs_end=0xFB000 +generic.menu.eesz.1M192.build.spiffs_blocksize=4096 +generic.menu.eesz.1M256=1M (256K SPIFFS) +generic.menu.eesz.1M256.build.flash_size=1M +generic.menu.eesz.1M256.build.flash_size_bytes=0x100000 +generic.menu.eesz.1M256.build.flash_ld=eagle.flash.1m256.ld +generic.menu.eesz.1M256.build.spiffs_pagesize=256 +generic.menu.eesz.1M256.upload.maximum_size=761840 +generic.menu.eesz.1M256.build.rfcal_addr=0xFC000 +generic.menu.eesz.1M256.build.spiffs_start=0xBB000 +generic.menu.eesz.1M256.build.spiffs_end=0xFB000 +generic.menu.eesz.1M256.build.spiffs_blocksize=4096 +generic.menu.eesz.1M512=1M (512K SPIFFS) +generic.menu.eesz.1M512.build.flash_size=1M +generic.menu.eesz.1M512.build.flash_size_bytes=0x100000 +generic.menu.eesz.1M512.build.flash_ld=eagle.flash.1m512.ld +generic.menu.eesz.1M512.build.spiffs_pagesize=256 +generic.menu.eesz.1M512.upload.maximum_size=499696 +generic.menu.eesz.1M512.build.rfcal_addr=0xFC000 +generic.menu.eesz.1M512.build.spiffs_start=0x7B000 +generic.menu.eesz.1M512.build.spiffs_end=0xFB000 +generic.menu.eesz.1M512.build.spiffs_blocksize=4096 +generic.menu.eesz.2M=2M (no SPIFFS) +generic.menu.eesz.2M.build.flash_size=2M +generic.menu.eesz.2M.build.flash_size_bytes=0x200000 +generic.menu.eesz.2M.build.flash_ld=eagle.flash.2m.ld +generic.menu.eesz.2M.build.spiffs_pagesize=256 +generic.menu.eesz.2M.upload.maximum_size=1044464 +generic.menu.eesz.2M.build.rfcal_addr=0x1FC000 +generic.menu.eesz.2M128=2M (128K SPIFFS) +generic.menu.eesz.2M128.build.flash_size=2M +generic.menu.eesz.2M128.build.flash_size_bytes=0x200000 +generic.menu.eesz.2M128.build.flash_ld=eagle.flash.2m128.ld +generic.menu.eesz.2M128.build.spiffs_pagesize=256 +generic.menu.eesz.2M128.upload.maximum_size=1044464 +generic.menu.eesz.2M128.build.rfcal_addr=0x1FC000 +generic.menu.eesz.2M128.build.spiffs_start=0x1E0000 +generic.menu.eesz.2M128.build.spiffs_end=0x1FB000 +generic.menu.eesz.2M128.build.spiffs_blocksize=4096 +generic.menu.eesz.2M256=2M (256K SPIFFS) +generic.menu.eesz.2M256.build.flash_size=2M +generic.menu.eesz.2M256.build.flash_size_bytes=0x200000 +generic.menu.eesz.2M256.build.flash_ld=eagle.flash.2m256.ld +generic.menu.eesz.2M256.build.spiffs_pagesize=256 +generic.menu.eesz.2M256.upload.maximum_size=1044464 +generic.menu.eesz.2M256.build.rfcal_addr=0x1FC000 +generic.menu.eesz.2M256.build.spiffs_start=0x1C0000 +generic.menu.eesz.2M256.build.spiffs_end=0x1FB000 +generic.menu.eesz.2M256.build.spiffs_blocksize=4096 +generic.menu.eesz.2M512=2M (512K SPIFFS) +generic.menu.eesz.2M512.build.flash_size=2M +generic.menu.eesz.2M512.build.flash_size_bytes=0x200000 +generic.menu.eesz.2M512.build.flash_ld=eagle.flash.2m512.ld +generic.menu.eesz.2M512.build.spiffs_pagesize=256 +generic.menu.eesz.2M512.upload.maximum_size=1044464 +generic.menu.eesz.2M512.build.rfcal_addr=0x1FC000 +generic.menu.eesz.2M512.build.spiffs_start=0x180000 +generic.menu.eesz.2M512.build.spiffs_end=0x1FB000 +generic.menu.eesz.2M512.build.spiffs_blocksize=8192 +generic.menu.eesz.2M1M=2M (1M SPIFFS) +generic.menu.eesz.2M1M.build.flash_size=2M +generic.menu.eesz.2M1M.build.flash_size_bytes=0x200000 +generic.menu.eesz.2M1M.build.flash_ld=eagle.flash.2m1m.ld +generic.menu.eesz.2M1M.build.spiffs_pagesize=256 +generic.menu.eesz.2M1M.upload.maximum_size=1044464 +generic.menu.eesz.2M1M.build.rfcal_addr=0x1FC000 +generic.menu.eesz.2M1M.build.spiffs_start=0x100000 +generic.menu.eesz.2M1M.build.spiffs_end=0x1FB000 +generic.menu.eesz.2M1M.build.spiffs_blocksize=8192 +generic.menu.eesz.4M=4M (no SPIFFS) +generic.menu.eesz.4M.build.flash_size=4M +generic.menu.eesz.4M.build.flash_size_bytes=0x400000 +generic.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld +generic.menu.eesz.4M.build.spiffs_pagesize=256 +generic.menu.eesz.4M.upload.maximum_size=1044464 +generic.menu.eesz.4M.build.rfcal_addr=0x3FC000 +generic.menu.eesz.4M1M=4M (1M SPIFFS) +generic.menu.eesz.4M1M.build.flash_size=4M +generic.menu.eesz.4M1M.build.flash_size_bytes=0x400000 +generic.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld +generic.menu.eesz.4M1M.build.spiffs_pagesize=256 +generic.menu.eesz.4M1M.upload.maximum_size=1044464 +generic.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 +generic.menu.eesz.4M1M.build.spiffs_start=0x300000 +generic.menu.eesz.4M1M.build.spiffs_end=0x3FB000 +generic.menu.eesz.4M1M.build.spiffs_blocksize=8192 +generic.menu.eesz.4M2M=4M (2M SPIFFS) +generic.menu.eesz.4M2M.build.flash_size=4M +generic.menu.eesz.4M2M.build.flash_size_bytes=0x400000 +generic.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld +generic.menu.eesz.4M2M.build.spiffs_pagesize=256 +generic.menu.eesz.4M2M.upload.maximum_size=1044464 +generic.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 +generic.menu.eesz.4M2M.build.spiffs_start=0x200000 +generic.menu.eesz.4M2M.build.spiffs_end=0x3FB000 +generic.menu.eesz.4M2M.build.spiffs_blocksize=8192 +generic.menu.eesz.4M3M=4M (3M SPIFFS) +generic.menu.eesz.4M3M.build.flash_size=4M +generic.menu.eesz.4M3M.build.flash_size_bytes=0x400000 +generic.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld +generic.menu.eesz.4M3M.build.spiffs_pagesize=256 +generic.menu.eesz.4M3M.upload.maximum_size=1044464 +generic.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 +generic.menu.eesz.4M3M.build.spiffs_start=0x100000 +generic.menu.eesz.4M3M.build.spiffs_end=0x3FB000 +generic.menu.eesz.4M3M.build.spiffs_blocksize=8192 +generic.menu.eesz.8M6M=8M (6M SPIFFS) +generic.menu.eesz.8M6M.build.flash_size=8M +generic.menu.eesz.8M6M.build.flash_size_bytes=0x800000 +generic.menu.eesz.8M6M.build.flash_ld=eagle.flash.8m6m.ld +generic.menu.eesz.8M6M.build.spiffs_pagesize=256 +generic.menu.eesz.8M6M.upload.maximum_size=1044464 +generic.menu.eesz.8M6M.build.rfcal_addr=0x7FC000 +generic.menu.eesz.8M6M.build.spiffs_start=0x200000 +generic.menu.eesz.8M6M.build.spiffs_end=0x7FB000 +generic.menu.eesz.8M6M.build.spiffs_blocksize=8192 +generic.menu.eesz.8M7M=8M (7M SPIFFS) +generic.menu.eesz.8M7M.build.flash_size=8M +generic.menu.eesz.8M7M.build.flash_size_bytes=0x800000 +generic.menu.eesz.8M7M.build.flash_ld=eagle.flash.8m7m.ld +generic.menu.eesz.8M7M.build.spiffs_pagesize=256 +generic.menu.eesz.8M7M.upload.maximum_size=1044464 +generic.menu.eesz.8M7M.build.rfcal_addr=0x7FC000 +generic.menu.eesz.8M7M.build.spiffs_start=0x100000 +generic.menu.eesz.8M7M.build.spiffs_end=0x7FB000 +generic.menu.eesz.8M7M.build.spiffs_blocksize=8192 +generic.menu.eesz.16M14M=16M (14M SPIFFS) +generic.menu.eesz.16M14M.build.flash_size=16M +generic.menu.eesz.16M14M.build.flash_size_bytes=0x1000000 +generic.menu.eesz.16M14M.build.flash_ld=eagle.flash.16m14m.ld +generic.menu.eesz.16M14M.build.spiffs_pagesize=256 +generic.menu.eesz.16M14M.upload.maximum_size=1044464 +generic.menu.eesz.16M14M.build.rfcal_addr=0xFFC000 +generic.menu.eesz.16M14M.build.spiffs_start=0x200000 +generic.menu.eesz.16M14M.build.spiffs_end=0xFFB000 +generic.menu.eesz.16M14M.build.spiffs_blocksize=8192 +generic.menu.eesz.16M15M=16M (15M SPIFFS) +generic.menu.eesz.16M15M.build.flash_size=16M +generic.menu.eesz.16M15M.build.flash_size_bytes=0x1000000 +generic.menu.eesz.16M15M.build.flash_ld=eagle.flash.16m15m.ld +generic.menu.eesz.16M15M.build.spiffs_pagesize=256 +generic.menu.eesz.16M15M.upload.maximum_size=1044464 +generic.menu.eesz.16M15M.build.rfcal_addr=0xFFC000 +generic.menu.eesz.16M15M.build.spiffs_start=0x100000 +generic.menu.eesz.16M15M.build.spiffs_end=0xFFB000 +generic.menu.eesz.16M15M.build.spiffs_blocksize=8192 +generic.menu.led.2=2 +generic.menu.led.2.build.led=-DLED_BUILTIN=2 +generic.menu.led.0=0 +generic.menu.led.0.build.led=-DLED_BUILTIN=0 +generic.menu.led.1=1 +generic.menu.led.1.build.led=-DLED_BUILTIN=1 +generic.menu.led.3=3 +generic.menu.led.3.build.led=-DLED_BUILTIN=3 +generic.menu.led.4=4 +generic.menu.led.4.build.led=-DLED_BUILTIN=4 +generic.menu.led.5=5 +generic.menu.led.5.build.led=-DLED_BUILTIN=5 +generic.menu.led.6=6 +generic.menu.led.6.build.led=-DLED_BUILTIN=6 +generic.menu.led.7=7 +generic.menu.led.7.build.led=-DLED_BUILTIN=7 +generic.menu.led.8=8 +generic.menu.led.8.build.led=-DLED_BUILTIN=8 +generic.menu.led.9=9 +generic.menu.led.9.build.led=-DLED_BUILTIN=9 +generic.menu.led.10=10 +generic.menu.led.10.build.led=-DLED_BUILTIN=10 +generic.menu.led.11=11 +generic.menu.led.11.build.led=-DLED_BUILTIN=11 +generic.menu.led.12=12 +generic.menu.led.12.build.led=-DLED_BUILTIN=12 +generic.menu.led.13=13 +generic.menu.led.13.build.led=-DLED_BUILTIN=13 +generic.menu.led.14=14 +generic.menu.led.14.build.led=-DLED_BUILTIN=14 +generic.menu.led.15=15 +generic.menu.led.15.build.led=-DLED_BUILTIN=15 +generic.menu.ip.lm2f=v2 Lower Memory +generic.menu.ip.lm2f.build.lwip_include=lwip2/include +generic.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +generic.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +generic.menu.ip.hb2f=v2 Higher Bandwidth +generic.menu.ip.hb2f.build.lwip_include=lwip2/include +generic.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +generic.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +generic.menu.ip.lm2n=v2 Lower Memory (no features) +generic.menu.ip.lm2n.build.lwip_include=lwip2/include +generic.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +generic.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +generic.menu.ip.hb2n=v2 Higher Bandwidth (no features) +generic.menu.ip.hb2n.build.lwip_include=lwip2/include +generic.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +generic.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +generic.menu.ip.lm6f=v2 IPv6 Lower Memory +generic.menu.ip.lm6f.build.lwip_include=lwip2/include +generic.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +generic.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +generic.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +generic.menu.ip.hb6f.build.lwip_include=lwip2/include +generic.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +generic.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +generic.menu.ip.hb1=v1.4 Higher Bandwidth +generic.menu.ip.hb1.build.lwip_lib=-llwip_gcc +generic.menu.ip.hb1.build.lwip_flags=-DLWIP_OPEN_SRC +generic.menu.ip.src=v1.4 Compile from source +generic.menu.ip.src.build.lwip_lib=-llwip_src +generic.menu.ip.src.build.lwip_flags=-DLWIP_OPEN_SRC +generic.menu.ip.src.recipe.hooks.sketch.prebuild.1.pattern=make -C "{runtime.platform.path}/tools/sdk/lwip/src" install TOOLS_PATH="{runtime.tools.xtensa-lx106-elf-gcc.path}/bin/xtensa-lx106-elf-" +generic.menu.dbg.Disabled=Disabled +generic.menu.dbg.Disabled.build.debug_port= +generic.menu.dbg.Serial=Serial +generic.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +generic.menu.dbg.Serial1=Serial1 +generic.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +generic.menu.lvl.None____=None +generic.menu.lvl.None____.build.debug_level= +generic.menu.lvl.SSL=SSL +generic.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +generic.menu.lvl.TLS_MEM=TLS_MEM +generic.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +generic.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +generic.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +generic.menu.lvl.HTTP_SERVER=HTTP_SERVER +generic.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +generic.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +generic.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +generic.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +generic.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +generic.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +generic.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +generic.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +generic.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +generic.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +generic.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +generic.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +generic.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +generic.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +generic.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +generic.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +generic.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +generic.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +generic.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +generic.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +generic.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +generic.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +generic.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +generic.menu.lvl.CORE=CORE +generic.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +generic.menu.lvl.WIFI=WIFI +generic.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +generic.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +generic.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +generic.menu.lvl.UPDATER=UPDATER +generic.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +generic.menu.lvl.OTA=OTA +generic.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +generic.menu.lvl.OOM=OOM +generic.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +generic.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOM=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM +generic.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOM.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM +generic.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM +generic.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM +generic.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +generic.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +generic.menu.wipe.none=Only Sketch +generic.menu.wipe.none.upload.erase_cmd= +generic.menu.wipe.sdk=Sketch + WiFi Settings +generic.menu.wipe.sdk.upload.erase_cmd=-ca "{build.rfcal_addr}" -cz 0x4000 +generic.menu.wipe.all=All Flash Contents +generic.menu.wipe.all.upload.erase_cmd=-ca 0x0 -cz "{build.flash_size_bytes}" +generic.menu.baud.115200=115200 +generic.menu.baud.115200.upload.speed=115200 +generic.menu.baud.9600=9600 +generic.menu.baud.9600.upload.speed=9600 +generic.menu.baud.57600=57600 +generic.menu.baud.57600.upload.speed=57600 +generic.menu.baud.230400.linux=230400 +generic.menu.baud.230400.macosx=230400 +generic.menu.baud.230400.upload.speed=230400 +generic.menu.baud.256000.windows=256000 +generic.menu.baud.256000.upload.speed=256000 +generic.menu.baud.460800.linux=460800 +generic.menu.baud.460800.macosx=460800 +generic.menu.baud.460800.upload.speed=460800 +generic.menu.baud.512000.windows=512000 +generic.menu.baud.512000.upload.speed=512000 +generic.menu.baud.921600=921600 +generic.menu.baud.921600.upload.speed=921600 + +############################################################## +esp8285.name=Generic ESP8285 Module +esp8285.build.board=ESP8266_ESP01 +esp8285.build.variant=esp8285 +esp8285.upload.tool=esptool +esp8285.upload.maximum_data_size=81920 +esp8285.upload.wait_for_upload_port=true +esp8285.upload.erase_cmd= +esp8285.serial.disableDTR=true +esp8285.serial.disableRTS=true +esp8285.build.mcu=esp8266 +esp8285.build.core=esp8266 +esp8285.build.spiffs_pagesize=256 +esp8285.build.debug_port= +esp8285.build.debug_level= +esp8285.menu.xtal.80=80 MHz +esp8285.menu.xtal.80.build.f_cpu=80000000L +esp8285.menu.xtal.160=160 MHz +esp8285.menu.xtal.160.build.f_cpu=160000000L +esp8285.menu.vt.flash=Flash +esp8285.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +esp8285.menu.vt.heap=Heap +esp8285.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +esp8285.menu.vt.iram=IRAM +esp8285.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +esp8285.menu.exception.enabled=Enabled +esp8285.menu.exception.enabled.build.exception_flags=-fexceptions +esp8285.menu.exception.enabled.build.stdcpp_lib=-lstdc++ +esp8285.menu.exception.disabled=Disabled +esp8285.menu.exception.disabled.build.exception_flags=-fno-exceptions +esp8285.menu.exception.disabled.build.stdcpp_lib=-lstdc++-nox +esp8285.menu.ResetMethod.ck=ck +esp8285.menu.ResetMethod.ck.upload.resetmethod=ck +esp8285.menu.ResetMethod.nodemcu=nodemcu +esp8285.menu.ResetMethod.nodemcu.upload.resetmethod=nodemcu +esp8285.menu.ResetMethod.none=none +esp8285.menu.ResetMethod.none.upload.resetmethod=none +esp8285.menu.ResetMethod.dtrset=dtrset +esp8285.menu.ResetMethod.dtrset.upload.resetmethod=dtrset +esp8285.menu.CrystalFreq.26=26 MHz +esp8285.menu.CrystalFreq.40=40 MHz +esp8285.menu.CrystalFreq.40.build.extra_flags=-DF_CRYSTAL=40000000 -DESP8266 +esp8285.build.flash_mode=dout +esp8285.build.flash_freq=40 +esp8285.menu.eesz.1M=1M (no SPIFFS) +esp8285.menu.eesz.1M.build.flash_size=1M +esp8285.menu.eesz.1M.build.flash_size_bytes=0x100000 +esp8285.menu.eesz.1M.build.flash_ld=eagle.flash.1m.ld +esp8285.menu.eesz.1M.build.spiffs_pagesize=256 +esp8285.menu.eesz.1M.upload.maximum_size=1023984 +esp8285.menu.eesz.1M.build.rfcal_addr=0xFC000 +esp8285.menu.eesz.1M64=1M (64K SPIFFS) +esp8285.menu.eesz.1M64.build.flash_size=1M +esp8285.menu.eesz.1M64.build.flash_size_bytes=0x100000 +esp8285.menu.eesz.1M64.build.flash_ld=eagle.flash.1m64.ld +esp8285.menu.eesz.1M64.build.spiffs_pagesize=256 +esp8285.menu.eesz.1M64.upload.maximum_size=958448 +esp8285.menu.eesz.1M64.build.rfcal_addr=0xFC000 +esp8285.menu.eesz.1M64.build.spiffs_start=0xEB000 +esp8285.menu.eesz.1M64.build.spiffs_end=0xFB000 +esp8285.menu.eesz.1M64.build.spiffs_blocksize=4096 +esp8285.menu.eesz.1M128=1M (128K SPIFFS) +esp8285.menu.eesz.1M128.build.flash_size=1M +esp8285.menu.eesz.1M128.build.flash_size_bytes=0x100000 +esp8285.menu.eesz.1M128.build.flash_ld=eagle.flash.1m128.ld +esp8285.menu.eesz.1M128.build.spiffs_pagesize=256 +esp8285.menu.eesz.1M128.upload.maximum_size=892912 +esp8285.menu.eesz.1M128.build.rfcal_addr=0xFC000 +esp8285.menu.eesz.1M128.build.spiffs_start=0xDB000 +esp8285.menu.eesz.1M128.build.spiffs_end=0xFB000 +esp8285.menu.eesz.1M128.build.spiffs_blocksize=4096 +esp8285.menu.eesz.1M144=1M (144K SPIFFS) +esp8285.menu.eesz.1M144.build.flash_size=1M +esp8285.menu.eesz.1M144.build.flash_size_bytes=0x100000 +esp8285.menu.eesz.1M144.build.flash_ld=eagle.flash.1m144.ld +esp8285.menu.eesz.1M144.build.spiffs_pagesize=256 +esp8285.menu.eesz.1M144.upload.maximum_size=876528 +esp8285.menu.eesz.1M144.build.rfcal_addr=0xFC000 +esp8285.menu.eesz.1M144.build.spiffs_start=0xD7000 +esp8285.menu.eesz.1M144.build.spiffs_end=0xFB000 +esp8285.menu.eesz.1M144.build.spiffs_blocksize=4096 +esp8285.menu.eesz.1M160=1M (160K SPIFFS) +esp8285.menu.eesz.1M160.build.flash_size=1M +esp8285.menu.eesz.1M160.build.flash_size_bytes=0x100000 +esp8285.menu.eesz.1M160.build.flash_ld=eagle.flash.1m160.ld +esp8285.menu.eesz.1M160.build.spiffs_pagesize=256 +esp8285.menu.eesz.1M160.upload.maximum_size=860144 +esp8285.menu.eesz.1M160.build.rfcal_addr=0xFC000 +esp8285.menu.eesz.1M160.build.spiffs_start=0xD3000 +esp8285.menu.eesz.1M160.build.spiffs_end=0xFB000 +esp8285.menu.eesz.1M160.build.spiffs_blocksize=4096 +esp8285.menu.eesz.1M192=1M (192K SPIFFS) +esp8285.menu.eesz.1M192.build.flash_size=1M +esp8285.menu.eesz.1M192.build.flash_size_bytes=0x100000 +esp8285.menu.eesz.1M192.build.flash_ld=eagle.flash.1m192.ld +esp8285.menu.eesz.1M192.build.spiffs_pagesize=256 +esp8285.menu.eesz.1M192.upload.maximum_size=827376 +esp8285.menu.eesz.1M192.build.rfcal_addr=0xFC000 +esp8285.menu.eesz.1M192.build.spiffs_start=0xCB000 +esp8285.menu.eesz.1M192.build.spiffs_end=0xFB000 +esp8285.menu.eesz.1M192.build.spiffs_blocksize=4096 +esp8285.menu.eesz.1M256=1M (256K SPIFFS) +esp8285.menu.eesz.1M256.build.flash_size=1M +esp8285.menu.eesz.1M256.build.flash_size_bytes=0x100000 +esp8285.menu.eesz.1M256.build.flash_ld=eagle.flash.1m256.ld +esp8285.menu.eesz.1M256.build.spiffs_pagesize=256 +esp8285.menu.eesz.1M256.upload.maximum_size=761840 +esp8285.menu.eesz.1M256.build.rfcal_addr=0xFC000 +esp8285.menu.eesz.1M256.build.spiffs_start=0xBB000 +esp8285.menu.eesz.1M256.build.spiffs_end=0xFB000 +esp8285.menu.eesz.1M256.build.spiffs_blocksize=4096 +esp8285.menu.eesz.1M512=1M (512K SPIFFS) +esp8285.menu.eesz.1M512.build.flash_size=1M +esp8285.menu.eesz.1M512.build.flash_size_bytes=0x100000 +esp8285.menu.eesz.1M512.build.flash_ld=eagle.flash.1m512.ld +esp8285.menu.eesz.1M512.build.spiffs_pagesize=256 +esp8285.menu.eesz.1M512.upload.maximum_size=499696 +esp8285.menu.eesz.1M512.build.rfcal_addr=0xFC000 +esp8285.menu.eesz.1M512.build.spiffs_start=0x7B000 +esp8285.menu.eesz.1M512.build.spiffs_end=0xFB000 +esp8285.menu.eesz.1M512.build.spiffs_blocksize=4096 +esp8285.menu.led.2=2 +esp8285.menu.led.2.build.led=-DLED_BUILTIN=2 +esp8285.menu.led.0=0 +esp8285.menu.led.0.build.led=-DLED_BUILTIN=0 +esp8285.menu.led.1=1 +esp8285.menu.led.1.build.led=-DLED_BUILTIN=1 +esp8285.menu.led.3=3 +esp8285.menu.led.3.build.led=-DLED_BUILTIN=3 +esp8285.menu.led.4=4 +esp8285.menu.led.4.build.led=-DLED_BUILTIN=4 +esp8285.menu.led.5=5 +esp8285.menu.led.5.build.led=-DLED_BUILTIN=5 +esp8285.menu.led.6=6 +esp8285.menu.led.6.build.led=-DLED_BUILTIN=6 +esp8285.menu.led.7=7 +esp8285.menu.led.7.build.led=-DLED_BUILTIN=7 +esp8285.menu.led.8=8 +esp8285.menu.led.8.build.led=-DLED_BUILTIN=8 +esp8285.menu.led.9=9 +esp8285.menu.led.9.build.led=-DLED_BUILTIN=9 +esp8285.menu.led.10=10 +esp8285.menu.led.10.build.led=-DLED_BUILTIN=10 +esp8285.menu.led.11=11 +esp8285.menu.led.11.build.led=-DLED_BUILTIN=11 +esp8285.menu.led.12=12 +esp8285.menu.led.12.build.led=-DLED_BUILTIN=12 +esp8285.menu.led.13=13 +esp8285.menu.led.13.build.led=-DLED_BUILTIN=13 +esp8285.menu.led.14=14 +esp8285.menu.led.14.build.led=-DLED_BUILTIN=14 +esp8285.menu.led.15=15 +esp8285.menu.led.15.build.led=-DLED_BUILTIN=15 +esp8285.menu.ip.lm2f=v2 Lower Memory +esp8285.menu.ip.lm2f.build.lwip_include=lwip2/include +esp8285.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +esp8285.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +esp8285.menu.ip.hb2f=v2 Higher Bandwidth +esp8285.menu.ip.hb2f.build.lwip_include=lwip2/include +esp8285.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +esp8285.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +esp8285.menu.ip.lm2n=v2 Lower Memory (no features) +esp8285.menu.ip.lm2n.build.lwip_include=lwip2/include +esp8285.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +esp8285.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +esp8285.menu.ip.hb2n=v2 Higher Bandwidth (no features) +esp8285.menu.ip.hb2n.build.lwip_include=lwip2/include +esp8285.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +esp8285.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +esp8285.menu.ip.lm6f=v2 IPv6 Lower Memory +esp8285.menu.ip.lm6f.build.lwip_include=lwip2/include +esp8285.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +esp8285.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +esp8285.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +esp8285.menu.ip.hb6f.build.lwip_include=lwip2/include +esp8285.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +esp8285.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +esp8285.menu.ip.hb1=v1.4 Higher Bandwidth +esp8285.menu.ip.hb1.build.lwip_lib=-llwip_gcc +esp8285.menu.ip.hb1.build.lwip_flags=-DLWIP_OPEN_SRC +esp8285.menu.ip.src=v1.4 Compile from source +esp8285.menu.ip.src.build.lwip_lib=-llwip_src +esp8285.menu.ip.src.build.lwip_flags=-DLWIP_OPEN_SRC +esp8285.menu.ip.src.recipe.hooks.sketch.prebuild.1.pattern=make -C "{runtime.platform.path}/tools/sdk/lwip/src" install TOOLS_PATH="{runtime.tools.xtensa-lx106-elf-gcc.path}/bin/xtensa-lx106-elf-" +esp8285.menu.dbg.Disabled=Disabled +esp8285.menu.dbg.Disabled.build.debug_port= +esp8285.menu.dbg.Serial=Serial +esp8285.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +esp8285.menu.dbg.Serial1=Serial1 +esp8285.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +esp8285.menu.lvl.None____=None +esp8285.menu.lvl.None____.build.debug_level= +esp8285.menu.lvl.SSL=SSL +esp8285.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +esp8285.menu.lvl.TLS_MEM=TLS_MEM +esp8285.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +esp8285.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +esp8285.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +esp8285.menu.lvl.HTTP_SERVER=HTTP_SERVER +esp8285.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +esp8285.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +esp8285.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +esp8285.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +esp8285.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +esp8285.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +esp8285.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +esp8285.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +esp8285.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +esp8285.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +esp8285.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +esp8285.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +esp8285.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +esp8285.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +esp8285.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +esp8285.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +esp8285.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +esp8285.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +esp8285.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +esp8285.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +esp8285.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +esp8285.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +esp8285.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +esp8285.menu.lvl.CORE=CORE +esp8285.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +esp8285.menu.lvl.WIFI=WIFI +esp8285.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +esp8285.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +esp8285.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +esp8285.menu.lvl.UPDATER=UPDATER +esp8285.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +esp8285.menu.lvl.OTA=OTA +esp8285.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +esp8285.menu.lvl.OOM=OOM +esp8285.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +esp8285.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOM=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM +esp8285.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOM.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM +esp8285.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM +esp8285.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM +esp8285.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +esp8285.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +esp8285.menu.wipe.none=Only Sketch +esp8285.menu.wipe.none.upload.erase_cmd= +esp8285.menu.wipe.sdk=Sketch + WiFi Settings +esp8285.menu.wipe.sdk.upload.erase_cmd=-ca "{build.rfcal_addr}" -cz 0x4000 +esp8285.menu.wipe.all=All Flash Contents +esp8285.menu.wipe.all.upload.erase_cmd=-ca 0x0 -cz "{build.flash_size_bytes}" +esp8285.menu.baud.115200=115200 +esp8285.menu.baud.115200.upload.speed=115200 +esp8285.menu.baud.9600=9600 +esp8285.menu.baud.9600.upload.speed=9600 +esp8285.menu.baud.57600=57600 +esp8285.menu.baud.57600.upload.speed=57600 +esp8285.menu.baud.230400.linux=230400 +esp8285.menu.baud.230400.macosx=230400 +esp8285.menu.baud.230400.upload.speed=230400 +esp8285.menu.baud.256000.windows=256000 +esp8285.menu.baud.256000.upload.speed=256000 +esp8285.menu.baud.460800.linux=460800 +esp8285.menu.baud.460800.macosx=460800 +esp8285.menu.baud.460800.upload.speed=460800 +esp8285.menu.baud.512000.windows=512000 +esp8285.menu.baud.512000.upload.speed=512000 +esp8285.menu.baud.921600=921600 +esp8285.menu.baud.921600.upload.speed=921600 + +############################################################## +espduino.name=ESPDuino (ESP-13 Module) +espduino.build.board=ESP8266_ESP13 +espduino.build.variant=ESPDuino +espduino.menu.ResetMethod.v2=ESPduino-V2 +espduino.menu.ResetMethod.v2.upload.resetmethod=nodemcu +espduino.menu.ResetMethod.v1=ESPduino-V1 +espduino.menu.ResetMethod.v1.upload.resetmethod=ck +espduino.menu.UploadTool.esptool=Serial +espduino.menu.UploadTool.esptool.upload.tool=esptool +espduino.menu.UploadTool.esptool.upload.verbose=-vv +espduino.menu.UploadTool.espota=OTA +espduino.menu.UploadTool.espota.upload.tool=espota +espduino.upload.tool=esptool +espduino.upload.maximum_data_size=81920 +espduino.upload.wait_for_upload_port=true +espduino.upload.erase_cmd= +espduino.serial.disableDTR=true +espduino.serial.disableRTS=true +espduino.build.mcu=esp8266 +espduino.build.core=esp8266 +espduino.build.spiffs_pagesize=256 +espduino.build.debug_port= +espduino.build.debug_level= +espduino.menu.xtal.80=80 MHz +espduino.menu.xtal.80.build.f_cpu=80000000L +espduino.menu.xtal.160=160 MHz +espduino.menu.xtal.160.build.f_cpu=160000000L +espduino.menu.vt.flash=Flash +espduino.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +espduino.menu.vt.heap=Heap +espduino.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +espduino.menu.vt.iram=IRAM +espduino.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +espduino.menu.exception.enabled=Enabled +espduino.menu.exception.enabled.build.exception_flags=-fexceptions +espduino.menu.exception.enabled.build.stdcpp_lib=-lstdc++ +espduino.menu.exception.disabled=Disabled +espduino.menu.exception.disabled.build.exception_flags=-fno-exceptions +espduino.menu.exception.disabled.build.stdcpp_lib=-lstdc++-nox +espduino.build.flash_mode=dio +espduino.build.flash_freq=40 +espduino.menu.eesz.4M=4M (no SPIFFS) +espduino.menu.eesz.4M.build.flash_size=4M +espduino.menu.eesz.4M.build.flash_size_bytes=0x400000 +espduino.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld +espduino.menu.eesz.4M.build.spiffs_pagesize=256 +espduino.menu.eesz.4M.upload.maximum_size=1044464 +espduino.menu.eesz.4M.build.rfcal_addr=0x3FC000 +espduino.menu.eesz.4M1M=4M (1M SPIFFS) +espduino.menu.eesz.4M1M.build.flash_size=4M +espduino.menu.eesz.4M1M.build.flash_size_bytes=0x400000 +espduino.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld +espduino.menu.eesz.4M1M.build.spiffs_pagesize=256 +espduino.menu.eesz.4M1M.upload.maximum_size=1044464 +espduino.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 +espduino.menu.eesz.4M1M.build.spiffs_start=0x300000 +espduino.menu.eesz.4M1M.build.spiffs_end=0x3FB000 +espduino.menu.eesz.4M1M.build.spiffs_blocksize=8192 +espduino.menu.eesz.4M2M=4M (2M SPIFFS) +espduino.menu.eesz.4M2M.build.flash_size=4M +espduino.menu.eesz.4M2M.build.flash_size_bytes=0x400000 +espduino.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld +espduino.menu.eesz.4M2M.build.spiffs_pagesize=256 +espduino.menu.eesz.4M2M.upload.maximum_size=1044464 +espduino.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 +espduino.menu.eesz.4M2M.build.spiffs_start=0x200000 +espduino.menu.eesz.4M2M.build.spiffs_end=0x3FB000 +espduino.menu.eesz.4M2M.build.spiffs_blocksize=8192 +espduino.menu.eesz.4M3M=4M (3M SPIFFS) +espduino.menu.eesz.4M3M.build.flash_size=4M +espduino.menu.eesz.4M3M.build.flash_size_bytes=0x400000 +espduino.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld +espduino.menu.eesz.4M3M.build.spiffs_pagesize=256 +espduino.menu.eesz.4M3M.upload.maximum_size=1044464 +espduino.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 +espduino.menu.eesz.4M3M.build.spiffs_start=0x100000 +espduino.menu.eesz.4M3M.build.spiffs_end=0x3FB000 +espduino.menu.eesz.4M3M.build.spiffs_blocksize=8192 +espduino.menu.ip.lm2f=v2 Lower Memory +espduino.menu.ip.lm2f.build.lwip_include=lwip2/include +espduino.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +espduino.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +espduino.menu.ip.hb2f=v2 Higher Bandwidth +espduino.menu.ip.hb2f.build.lwip_include=lwip2/include +espduino.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +espduino.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +espduino.menu.ip.lm2n=v2 Lower Memory (no features) +espduino.menu.ip.lm2n.build.lwip_include=lwip2/include +espduino.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +espduino.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +espduino.menu.ip.hb2n=v2 Higher Bandwidth (no features) +espduino.menu.ip.hb2n.build.lwip_include=lwip2/include +espduino.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +espduino.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +espduino.menu.ip.lm6f=v2 IPv6 Lower Memory +espduino.menu.ip.lm6f.build.lwip_include=lwip2/include +espduino.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +espduino.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +espduino.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +espduino.menu.ip.hb6f.build.lwip_include=lwip2/include +espduino.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +espduino.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +espduino.menu.ip.hb1=v1.4 Higher Bandwidth +espduino.menu.ip.hb1.build.lwip_lib=-llwip_gcc +espduino.menu.ip.hb1.build.lwip_flags=-DLWIP_OPEN_SRC +espduino.menu.ip.src=v1.4 Compile from source +espduino.menu.ip.src.build.lwip_lib=-llwip_src +espduino.menu.ip.src.build.lwip_flags=-DLWIP_OPEN_SRC +espduino.menu.ip.src.recipe.hooks.sketch.prebuild.1.pattern=make -C "{runtime.platform.path}/tools/sdk/lwip/src" install TOOLS_PATH="{runtime.tools.xtensa-lx106-elf-gcc.path}/bin/xtensa-lx106-elf-" +espduino.menu.dbg.Disabled=Disabled +espduino.menu.dbg.Disabled.build.debug_port= +espduino.menu.dbg.Serial=Serial +espduino.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +espduino.menu.dbg.Serial1=Serial1 +espduino.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +espduino.menu.lvl.None____=None +espduino.menu.lvl.None____.build.debug_level= +espduino.menu.lvl.SSL=SSL +espduino.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +espduino.menu.lvl.TLS_MEM=TLS_MEM +espduino.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +espduino.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +espduino.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +espduino.menu.lvl.HTTP_SERVER=HTTP_SERVER +espduino.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +espduino.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +espduino.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +espduino.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +espduino.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +espduino.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +espduino.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +espduino.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +espduino.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +espduino.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +espduino.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +espduino.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +espduino.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +espduino.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +espduino.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +espduino.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +espduino.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +espduino.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +espduino.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +espduino.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +espduino.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +espduino.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +espduino.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +espduino.menu.lvl.CORE=CORE +espduino.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +espduino.menu.lvl.WIFI=WIFI +espduino.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +espduino.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +espduino.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +espduino.menu.lvl.UPDATER=UPDATER +espduino.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +espduino.menu.lvl.OTA=OTA +espduino.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +espduino.menu.lvl.OOM=OOM +espduino.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +espduino.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOM=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM +espduino.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOM.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM +espduino.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM +espduino.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM +espduino.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +espduino.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +espduino.menu.wipe.none=Only Sketch +espduino.menu.wipe.none.upload.erase_cmd= +espduino.menu.wipe.sdk=Sketch + WiFi Settings +espduino.menu.wipe.sdk.upload.erase_cmd=-ca "{build.rfcal_addr}" -cz 0x4000 +espduino.menu.wipe.all=All Flash Contents +espduino.menu.wipe.all.upload.erase_cmd=-ca 0x0 -cz "{build.flash_size_bytes}" +espduino.menu.baud.115200=115200 +espduino.menu.baud.115200.upload.speed=115200 +espduino.menu.baud.9600=9600 +espduino.menu.baud.9600.upload.speed=9600 +espduino.menu.baud.57600=57600 +espduino.menu.baud.57600.upload.speed=57600 +espduino.menu.baud.230400.linux=230400 +espduino.menu.baud.230400.macosx=230400 +espduino.menu.baud.230400.upload.speed=230400 +espduino.menu.baud.256000.windows=256000 +espduino.menu.baud.256000.upload.speed=256000 +espduino.menu.baud.460800.linux=460800 +espduino.menu.baud.460800.macosx=460800 +espduino.menu.baud.460800.upload.speed=460800 +espduino.menu.baud.512000.windows=512000 +espduino.menu.baud.512000.upload.speed=512000 +espduino.menu.baud.921600=921600 +espduino.menu.baud.921600.upload.speed=921600 + +############################################################## +huzzah.name=Adafruit Feather HUZZAH ESP8266 +huzzah.build.board=ESP8266_ESP12 +huzzah.build.variant=adafruit +huzzah.upload.tool=esptool +huzzah.upload.maximum_data_size=81920 +huzzah.upload.wait_for_upload_port=true +huzzah.upload.erase_cmd= +huzzah.serial.disableDTR=true +huzzah.serial.disableRTS=true +huzzah.build.mcu=esp8266 +huzzah.build.core=esp8266 +huzzah.build.spiffs_pagesize=256 +huzzah.build.debug_port= +huzzah.build.debug_level= +huzzah.menu.xtal.80=80 MHz +huzzah.menu.xtal.80.build.f_cpu=80000000L +huzzah.menu.xtal.160=160 MHz +huzzah.menu.xtal.160.build.f_cpu=160000000L +huzzah.menu.vt.flash=Flash +huzzah.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +huzzah.menu.vt.heap=Heap +huzzah.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +huzzah.menu.vt.iram=IRAM +huzzah.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +huzzah.menu.exception.enabled=Enabled +huzzah.menu.exception.enabled.build.exception_flags=-fexceptions +huzzah.menu.exception.enabled.build.stdcpp_lib=-lstdc++ +huzzah.menu.exception.disabled=Disabled +huzzah.menu.exception.disabled.build.exception_flags=-fno-exceptions +huzzah.menu.exception.disabled.build.stdcpp_lib=-lstdc++-nox +huzzah.upload.resetmethod=nodemcu +huzzah.build.flash_mode=qio +huzzah.build.flash_freq=40 +huzzah.menu.eesz.4M=4M (no SPIFFS) +huzzah.menu.eesz.4M.build.flash_size=4M +huzzah.menu.eesz.4M.build.flash_size_bytes=0x400000 +huzzah.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld +huzzah.menu.eesz.4M.build.spiffs_pagesize=256 +huzzah.menu.eesz.4M.upload.maximum_size=1044464 +huzzah.menu.eesz.4M.build.rfcal_addr=0x3FC000 +huzzah.menu.eesz.4M1M=4M (1M SPIFFS) +huzzah.menu.eesz.4M1M.build.flash_size=4M +huzzah.menu.eesz.4M1M.build.flash_size_bytes=0x400000 +huzzah.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld +huzzah.menu.eesz.4M1M.build.spiffs_pagesize=256 +huzzah.menu.eesz.4M1M.upload.maximum_size=1044464 +huzzah.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 +huzzah.menu.eesz.4M1M.build.spiffs_start=0x300000 +huzzah.menu.eesz.4M1M.build.spiffs_end=0x3FB000 +huzzah.menu.eesz.4M1M.build.spiffs_blocksize=8192 +huzzah.menu.eesz.4M2M=4M (2M SPIFFS) +huzzah.menu.eesz.4M2M.build.flash_size=4M +huzzah.menu.eesz.4M2M.build.flash_size_bytes=0x400000 +huzzah.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld +huzzah.menu.eesz.4M2M.build.spiffs_pagesize=256 +huzzah.menu.eesz.4M2M.upload.maximum_size=1044464 +huzzah.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 +huzzah.menu.eesz.4M2M.build.spiffs_start=0x200000 +huzzah.menu.eesz.4M2M.build.spiffs_end=0x3FB000 +huzzah.menu.eesz.4M2M.build.spiffs_blocksize=8192 +huzzah.menu.eesz.4M3M=4M (3M SPIFFS) +huzzah.menu.eesz.4M3M.build.flash_size=4M +huzzah.menu.eesz.4M3M.build.flash_size_bytes=0x400000 +huzzah.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld +huzzah.menu.eesz.4M3M.build.spiffs_pagesize=256 +huzzah.menu.eesz.4M3M.upload.maximum_size=1044464 +huzzah.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 +huzzah.menu.eesz.4M3M.build.spiffs_start=0x100000 +huzzah.menu.eesz.4M3M.build.spiffs_end=0x3FB000 +huzzah.menu.eesz.4M3M.build.spiffs_blocksize=8192 +huzzah.menu.ip.lm2f=v2 Lower Memory +huzzah.menu.ip.lm2f.build.lwip_include=lwip2/include +huzzah.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +huzzah.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +huzzah.menu.ip.hb2f=v2 Higher Bandwidth +huzzah.menu.ip.hb2f.build.lwip_include=lwip2/include +huzzah.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +huzzah.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +huzzah.menu.ip.lm2n=v2 Lower Memory (no features) +huzzah.menu.ip.lm2n.build.lwip_include=lwip2/include +huzzah.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +huzzah.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +huzzah.menu.ip.hb2n=v2 Higher Bandwidth (no features) +huzzah.menu.ip.hb2n.build.lwip_include=lwip2/include +huzzah.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +huzzah.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +huzzah.menu.ip.lm6f=v2 IPv6 Lower Memory +huzzah.menu.ip.lm6f.build.lwip_include=lwip2/include +huzzah.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +huzzah.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +huzzah.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +huzzah.menu.ip.hb6f.build.lwip_include=lwip2/include +huzzah.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +huzzah.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +huzzah.menu.ip.hb1=v1.4 Higher Bandwidth +huzzah.menu.ip.hb1.build.lwip_lib=-llwip_gcc +huzzah.menu.ip.hb1.build.lwip_flags=-DLWIP_OPEN_SRC +huzzah.menu.ip.src=v1.4 Compile from source +huzzah.menu.ip.src.build.lwip_lib=-llwip_src +huzzah.menu.ip.src.build.lwip_flags=-DLWIP_OPEN_SRC +huzzah.menu.ip.src.recipe.hooks.sketch.prebuild.1.pattern=make -C "{runtime.platform.path}/tools/sdk/lwip/src" install TOOLS_PATH="{runtime.tools.xtensa-lx106-elf-gcc.path}/bin/xtensa-lx106-elf-" +huzzah.menu.dbg.Disabled=Disabled +huzzah.menu.dbg.Disabled.build.debug_port= +huzzah.menu.dbg.Serial=Serial +huzzah.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +huzzah.menu.dbg.Serial1=Serial1 +huzzah.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +huzzah.menu.lvl.None____=None +huzzah.menu.lvl.None____.build.debug_level= +huzzah.menu.lvl.SSL=SSL +huzzah.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +huzzah.menu.lvl.TLS_MEM=TLS_MEM +huzzah.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +huzzah.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +huzzah.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +huzzah.menu.lvl.HTTP_SERVER=HTTP_SERVER +huzzah.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +huzzah.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +huzzah.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +huzzah.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +huzzah.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +huzzah.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +huzzah.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +huzzah.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +huzzah.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +huzzah.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +huzzah.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +huzzah.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +huzzah.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +huzzah.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +huzzah.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +huzzah.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +huzzah.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +huzzah.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +huzzah.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +huzzah.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +huzzah.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +huzzah.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +huzzah.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +huzzah.menu.lvl.CORE=CORE +huzzah.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +huzzah.menu.lvl.WIFI=WIFI +huzzah.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +huzzah.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +huzzah.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +huzzah.menu.lvl.UPDATER=UPDATER +huzzah.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +huzzah.menu.lvl.OTA=OTA +huzzah.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +huzzah.menu.lvl.OOM=OOM +huzzah.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +huzzah.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOM=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM +huzzah.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOM.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM +huzzah.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM +huzzah.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM +huzzah.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +huzzah.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +huzzah.menu.wipe.none=Only Sketch +huzzah.menu.wipe.none.upload.erase_cmd= +huzzah.menu.wipe.sdk=Sketch + WiFi Settings +huzzah.menu.wipe.sdk.upload.erase_cmd=-ca "{build.rfcal_addr}" -cz 0x4000 +huzzah.menu.wipe.all=All Flash Contents +huzzah.menu.wipe.all.upload.erase_cmd=-ca 0x0 -cz "{build.flash_size_bytes}" +huzzah.menu.baud.115200=115200 +huzzah.menu.baud.115200.upload.speed=115200 +huzzah.menu.baud.9600=9600 +huzzah.menu.baud.9600.upload.speed=9600 +huzzah.menu.baud.57600=57600 +huzzah.menu.baud.57600.upload.speed=57600 +huzzah.menu.baud.230400.linux=230400 +huzzah.menu.baud.230400.macosx=230400 +huzzah.menu.baud.230400.upload.speed=230400 +huzzah.menu.baud.256000.windows=256000 +huzzah.menu.baud.256000.upload.speed=256000 +huzzah.menu.baud.460800.linux=460800 +huzzah.menu.baud.460800.macosx=460800 +huzzah.menu.baud.460800.upload.speed=460800 +huzzah.menu.baud.512000.windows=512000 +huzzah.menu.baud.512000.upload.speed=512000 +huzzah.menu.baud.921600=921600 +huzzah.menu.baud.921600.upload.speed=921600 + +############################################################## +inventone.name=Invent One +inventone.build.board=ESP8266_GENERIC +inventone.build.variant=inventone +inventone.upload.tool=esptool +inventone.upload.maximum_data_size=81920 +inventone.upload.wait_for_upload_port=true +inventone.upload.erase_cmd= +inventone.serial.disableDTR=true +inventone.serial.disableRTS=true +inventone.build.mcu=esp8266 +inventone.build.core=esp8266 +inventone.build.spiffs_pagesize=256 +inventone.build.debug_port= +inventone.build.debug_level= +inventone.menu.xtal.80=80 MHz +inventone.menu.xtal.80.build.f_cpu=80000000L +inventone.menu.xtal.160=160 MHz +inventone.menu.xtal.160.build.f_cpu=160000000L +inventone.menu.vt.flash=Flash +inventone.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +inventone.menu.vt.heap=Heap +inventone.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +inventone.menu.vt.iram=IRAM +inventone.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +inventone.menu.exception.enabled=Enabled +inventone.menu.exception.enabled.build.exception_flags=-fexceptions +inventone.menu.exception.enabled.build.stdcpp_lib=-lstdc++ +inventone.menu.exception.disabled=Disabled +inventone.menu.exception.disabled.build.exception_flags=-fno-exceptions +inventone.menu.exception.disabled.build.stdcpp_lib=-lstdc++-nox +inventone.upload.resetmethod=nodemcu +inventone.build.flash_mode=dio +inventone.build.flash_freq=40 +inventone.menu.eesz.4M=4M (no SPIFFS) +inventone.menu.eesz.4M.build.flash_size=4M +inventone.menu.eesz.4M.build.flash_size_bytes=0x400000 +inventone.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld +inventone.menu.eesz.4M.build.spiffs_pagesize=256 +inventone.menu.eesz.4M.upload.maximum_size=1044464 +inventone.menu.eesz.4M.build.rfcal_addr=0x3FC000 +inventone.menu.eesz.4M1M=4M (1M SPIFFS) +inventone.menu.eesz.4M1M.build.flash_size=4M +inventone.menu.eesz.4M1M.build.flash_size_bytes=0x400000 +inventone.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld +inventone.menu.eesz.4M1M.build.spiffs_pagesize=256 +inventone.menu.eesz.4M1M.upload.maximum_size=1044464 +inventone.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 +inventone.menu.eesz.4M1M.build.spiffs_start=0x300000 +inventone.menu.eesz.4M1M.build.spiffs_end=0x3FB000 +inventone.menu.eesz.4M1M.build.spiffs_blocksize=8192 +inventone.menu.eesz.4M2M=4M (2M SPIFFS) +inventone.menu.eesz.4M2M.build.flash_size=4M +inventone.menu.eesz.4M2M.build.flash_size_bytes=0x400000 +inventone.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld +inventone.menu.eesz.4M2M.build.spiffs_pagesize=256 +inventone.menu.eesz.4M2M.upload.maximum_size=1044464 +inventone.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 +inventone.menu.eesz.4M2M.build.spiffs_start=0x200000 +inventone.menu.eesz.4M2M.build.spiffs_end=0x3FB000 +inventone.menu.eesz.4M2M.build.spiffs_blocksize=8192 +inventone.menu.eesz.4M3M=4M (3M SPIFFS) +inventone.menu.eesz.4M3M.build.flash_size=4M +inventone.menu.eesz.4M3M.build.flash_size_bytes=0x400000 +inventone.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld +inventone.menu.eesz.4M3M.build.spiffs_pagesize=256 +inventone.menu.eesz.4M3M.upload.maximum_size=1044464 +inventone.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 +inventone.menu.eesz.4M3M.build.spiffs_start=0x100000 +inventone.menu.eesz.4M3M.build.spiffs_end=0x3FB000 +inventone.menu.eesz.4M3M.build.spiffs_blocksize=8192 +inventone.menu.ip.lm2f=v2 Lower Memory +inventone.menu.ip.lm2f.build.lwip_include=lwip2/include +inventone.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +inventone.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +inventone.menu.ip.hb2f=v2 Higher Bandwidth +inventone.menu.ip.hb2f.build.lwip_include=lwip2/include +inventone.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +inventone.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +inventone.menu.ip.lm2n=v2 Lower Memory (no features) +inventone.menu.ip.lm2n.build.lwip_include=lwip2/include +inventone.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +inventone.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +inventone.menu.ip.hb2n=v2 Higher Bandwidth (no features) +inventone.menu.ip.hb2n.build.lwip_include=lwip2/include +inventone.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +inventone.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +inventone.menu.ip.lm6f=v2 IPv6 Lower Memory +inventone.menu.ip.lm6f.build.lwip_include=lwip2/include +inventone.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +inventone.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +inventone.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +inventone.menu.ip.hb6f.build.lwip_include=lwip2/include +inventone.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +inventone.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +inventone.menu.ip.hb1=v1.4 Higher Bandwidth +inventone.menu.ip.hb1.build.lwip_lib=-llwip_gcc +inventone.menu.ip.hb1.build.lwip_flags=-DLWIP_OPEN_SRC +inventone.menu.ip.src=v1.4 Compile from source +inventone.menu.ip.src.build.lwip_lib=-llwip_src +inventone.menu.ip.src.build.lwip_flags=-DLWIP_OPEN_SRC +inventone.menu.ip.src.recipe.hooks.sketch.prebuild.1.pattern=make -C "{runtime.platform.path}/tools/sdk/lwip/src" install TOOLS_PATH="{runtime.tools.xtensa-lx106-elf-gcc.path}/bin/xtensa-lx106-elf-" +inventone.menu.dbg.Disabled=Disabled +inventone.menu.dbg.Disabled.build.debug_port= +inventone.menu.dbg.Serial=Serial +inventone.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +inventone.menu.dbg.Serial1=Serial1 +inventone.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +inventone.menu.lvl.None____=None +inventone.menu.lvl.None____.build.debug_level= +inventone.menu.lvl.SSL=SSL +inventone.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +inventone.menu.lvl.TLS_MEM=TLS_MEM +inventone.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +inventone.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +inventone.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +inventone.menu.lvl.HTTP_SERVER=HTTP_SERVER +inventone.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +inventone.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +inventone.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +inventone.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +inventone.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +inventone.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +inventone.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +inventone.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +inventone.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +inventone.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +inventone.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +inventone.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +inventone.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +inventone.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +inventone.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +inventone.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +inventone.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +inventone.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +inventone.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +inventone.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +inventone.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +inventone.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +inventone.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +inventone.menu.lvl.CORE=CORE +inventone.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +inventone.menu.lvl.WIFI=WIFI +inventone.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +inventone.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +inventone.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +inventone.menu.lvl.UPDATER=UPDATER +inventone.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +inventone.menu.lvl.OTA=OTA +inventone.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +inventone.menu.lvl.OOM=OOM +inventone.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +inventone.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOM=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM +inventone.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOM.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM +inventone.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM +inventone.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM +inventone.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +inventone.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +inventone.menu.wipe.none=Only Sketch +inventone.menu.wipe.none.upload.erase_cmd= +inventone.menu.wipe.sdk=Sketch + WiFi Settings +inventone.menu.wipe.sdk.upload.erase_cmd=-ca "{build.rfcal_addr}" -cz 0x4000 +inventone.menu.wipe.all=All Flash Contents +inventone.menu.wipe.all.upload.erase_cmd=-ca 0x0 -cz "{build.flash_size_bytes}" +inventone.menu.baud.115200=115200 +inventone.menu.baud.115200.upload.speed=115200 +inventone.menu.baud.9600=9600 +inventone.menu.baud.9600.upload.speed=9600 +inventone.menu.baud.57600=57600 +inventone.menu.baud.57600.upload.speed=57600 +inventone.menu.baud.230400.linux=230400 +inventone.menu.baud.230400.macosx=230400 +inventone.menu.baud.230400.upload.speed=230400 +inventone.menu.baud.256000.windows=256000 +inventone.menu.baud.256000.upload.speed=256000 +inventone.menu.baud.460800.linux=460800 +inventone.menu.baud.460800.macosx=460800 +inventone.menu.baud.460800.upload.speed=460800 +inventone.menu.baud.512000.windows=512000 +inventone.menu.baud.512000.upload.speed=512000 +inventone.menu.baud.921600=921600 +inventone.menu.baud.921600.upload.speed=921600 + +############################################################## +cw01.name=XinaBox CW01 +cw01.build.board=ESP8266_GENERIC +cw01.build.variant=xinabox +cw01.upload.tool=esptool +cw01.upload.maximum_data_size=81920 +cw01.upload.wait_for_upload_port=true +cw01.upload.erase_cmd= +cw01.serial.disableDTR=true +cw01.serial.disableRTS=true +cw01.build.mcu=esp8266 +cw01.build.core=esp8266 +cw01.build.spiffs_pagesize=256 +cw01.build.debug_port= +cw01.build.debug_level= +cw01.menu.xtal.80=80 MHz +cw01.menu.xtal.80.build.f_cpu=80000000L +cw01.menu.xtal.160=160 MHz +cw01.menu.xtal.160.build.f_cpu=160000000L +cw01.menu.vt.flash=Flash +cw01.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +cw01.menu.vt.heap=Heap +cw01.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +cw01.menu.vt.iram=IRAM +cw01.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +cw01.menu.exception.enabled=Enabled +cw01.menu.exception.enabled.build.exception_flags=-fexceptions +cw01.menu.exception.enabled.build.stdcpp_lib=-lstdc++ +cw01.menu.exception.disabled=Disabled +cw01.menu.exception.disabled.build.exception_flags=-fno-exceptions +cw01.menu.exception.disabled.build.stdcpp_lib=-lstdc++-nox +cw01.upload.resetmethod=nodemcu +cw01.menu.CrystalFreq.26=26 MHz +cw01.menu.CrystalFreq.40=40 MHz +cw01.menu.CrystalFreq.40.build.extra_flags=-DF_CRYSTAL=40000000 -DESP8266 +cw01.build.flash_mode=qio +cw01.build.flash_freq=40 +cw01.menu.eesz.4M=4M (no SPIFFS) +cw01.menu.eesz.4M.build.flash_size=4M +cw01.menu.eesz.4M.build.flash_size_bytes=0x400000 +cw01.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld +cw01.menu.eesz.4M.build.spiffs_pagesize=256 +cw01.menu.eesz.4M.upload.maximum_size=1044464 +cw01.menu.eesz.4M.build.rfcal_addr=0x3FC000 +cw01.menu.eesz.4M1M=4M (1M SPIFFS) +cw01.menu.eesz.4M1M.build.flash_size=4M +cw01.menu.eesz.4M1M.build.flash_size_bytes=0x400000 +cw01.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld +cw01.menu.eesz.4M1M.build.spiffs_pagesize=256 +cw01.menu.eesz.4M1M.upload.maximum_size=1044464 +cw01.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 +cw01.menu.eesz.4M1M.build.spiffs_start=0x300000 +cw01.menu.eesz.4M1M.build.spiffs_end=0x3FB000 +cw01.menu.eesz.4M1M.build.spiffs_blocksize=8192 +cw01.menu.eesz.4M2M=4M (2M SPIFFS) +cw01.menu.eesz.4M2M.build.flash_size=4M +cw01.menu.eesz.4M2M.build.flash_size_bytes=0x400000 +cw01.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld +cw01.menu.eesz.4M2M.build.spiffs_pagesize=256 +cw01.menu.eesz.4M2M.upload.maximum_size=1044464 +cw01.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 +cw01.menu.eesz.4M2M.build.spiffs_start=0x200000 +cw01.menu.eesz.4M2M.build.spiffs_end=0x3FB000 +cw01.menu.eesz.4M2M.build.spiffs_blocksize=8192 +cw01.menu.eesz.4M3M=4M (3M SPIFFS) +cw01.menu.eesz.4M3M.build.flash_size=4M +cw01.menu.eesz.4M3M.build.flash_size_bytes=0x400000 +cw01.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld +cw01.menu.eesz.4M3M.build.spiffs_pagesize=256 +cw01.menu.eesz.4M3M.upload.maximum_size=1044464 +cw01.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 +cw01.menu.eesz.4M3M.build.spiffs_start=0x100000 +cw01.menu.eesz.4M3M.build.spiffs_end=0x3FB000 +cw01.menu.eesz.4M3M.build.spiffs_blocksize=8192 +cw01.menu.ip.lm2f=v2 Lower Memory +cw01.menu.ip.lm2f.build.lwip_include=lwip2/include +cw01.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +cw01.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +cw01.menu.ip.hb2f=v2 Higher Bandwidth +cw01.menu.ip.hb2f.build.lwip_include=lwip2/include +cw01.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +cw01.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +cw01.menu.ip.lm2n=v2 Lower Memory (no features) +cw01.menu.ip.lm2n.build.lwip_include=lwip2/include +cw01.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +cw01.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +cw01.menu.ip.hb2n=v2 Higher Bandwidth (no features) +cw01.menu.ip.hb2n.build.lwip_include=lwip2/include +cw01.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +cw01.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +cw01.menu.ip.lm6f=v2 IPv6 Lower Memory +cw01.menu.ip.lm6f.build.lwip_include=lwip2/include +cw01.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +cw01.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +cw01.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +cw01.menu.ip.hb6f.build.lwip_include=lwip2/include +cw01.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +cw01.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +cw01.menu.ip.hb1=v1.4 Higher Bandwidth +cw01.menu.ip.hb1.build.lwip_lib=-llwip_gcc +cw01.menu.ip.hb1.build.lwip_flags=-DLWIP_OPEN_SRC +cw01.menu.ip.src=v1.4 Compile from source +cw01.menu.ip.src.build.lwip_lib=-llwip_src +cw01.menu.ip.src.build.lwip_flags=-DLWIP_OPEN_SRC +cw01.menu.ip.src.recipe.hooks.sketch.prebuild.1.pattern=make -C "{runtime.platform.path}/tools/sdk/lwip/src" install TOOLS_PATH="{runtime.tools.xtensa-lx106-elf-gcc.path}/bin/xtensa-lx106-elf-" +cw01.menu.dbg.Disabled=Disabled +cw01.menu.dbg.Disabled.build.debug_port= +cw01.menu.dbg.Serial=Serial +cw01.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +cw01.menu.dbg.Serial1=Serial1 +cw01.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +cw01.menu.lvl.None____=None +cw01.menu.lvl.None____.build.debug_level= +cw01.menu.lvl.SSL=SSL +cw01.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +cw01.menu.lvl.TLS_MEM=TLS_MEM +cw01.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +cw01.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +cw01.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +cw01.menu.lvl.HTTP_SERVER=HTTP_SERVER +cw01.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +cw01.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +cw01.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +cw01.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +cw01.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +cw01.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +cw01.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +cw01.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +cw01.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +cw01.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +cw01.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +cw01.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +cw01.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +cw01.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +cw01.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +cw01.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +cw01.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +cw01.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +cw01.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +cw01.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +cw01.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +cw01.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +cw01.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +cw01.menu.lvl.CORE=CORE +cw01.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +cw01.menu.lvl.WIFI=WIFI +cw01.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +cw01.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +cw01.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +cw01.menu.lvl.UPDATER=UPDATER +cw01.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +cw01.menu.lvl.OTA=OTA +cw01.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +cw01.menu.lvl.OOM=OOM +cw01.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +cw01.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOM=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM +cw01.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOM.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM +cw01.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM +cw01.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM +cw01.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +cw01.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +cw01.menu.wipe.none=Only Sketch +cw01.menu.wipe.none.upload.erase_cmd= +cw01.menu.wipe.sdk=Sketch + WiFi Settings +cw01.menu.wipe.sdk.upload.erase_cmd=-ca "{build.rfcal_addr}" -cz 0x4000 +cw01.menu.wipe.all=All Flash Contents +cw01.menu.wipe.all.upload.erase_cmd=-ca 0x0 -cz "{build.flash_size_bytes}" +cw01.menu.baud.115200=115200 +cw01.menu.baud.115200.upload.speed=115200 +cw01.menu.baud.9600=9600 +cw01.menu.baud.9600.upload.speed=9600 +cw01.menu.baud.57600=57600 +cw01.menu.baud.57600.upload.speed=57600 +cw01.menu.baud.230400.linux=230400 +cw01.menu.baud.230400.macosx=230400 +cw01.menu.baud.230400.upload.speed=230400 +cw01.menu.baud.256000.windows=256000 +cw01.menu.baud.256000.upload.speed=256000 +cw01.menu.baud.460800.linux=460800 +cw01.menu.baud.460800.macosx=460800 +cw01.menu.baud.460800.upload.speed=460800 +cw01.menu.baud.512000.windows=512000 +cw01.menu.baud.512000.upload.speed=512000 +cw01.menu.baud.921600=921600 +cw01.menu.baud.921600.upload.speed=921600 + +############################################################## +espresso_lite_v1.name=ESPresso Lite 1.0 +espresso_lite_v1.build.board=ESP8266_ESPRESSO_LITE_V1 +espresso_lite_v1.build.variant=espresso_lite_v1 +espresso_lite_v1.upload.tool=esptool +espresso_lite_v1.upload.maximum_data_size=81920 +espresso_lite_v1.upload.wait_for_upload_port=true +espresso_lite_v1.upload.erase_cmd= +espresso_lite_v1.serial.disableDTR=true +espresso_lite_v1.serial.disableRTS=true +espresso_lite_v1.build.mcu=esp8266 +espresso_lite_v1.build.core=esp8266 +espresso_lite_v1.build.spiffs_pagesize=256 +espresso_lite_v1.build.debug_port= +espresso_lite_v1.build.debug_level= +espresso_lite_v1.menu.xtal.80=80 MHz +espresso_lite_v1.menu.xtal.80.build.f_cpu=80000000L +espresso_lite_v1.menu.xtal.160=160 MHz +espresso_lite_v1.menu.xtal.160.build.f_cpu=160000000L +espresso_lite_v1.menu.vt.flash=Flash +espresso_lite_v1.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +espresso_lite_v1.menu.vt.heap=Heap +espresso_lite_v1.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +espresso_lite_v1.menu.vt.iram=IRAM +espresso_lite_v1.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +espresso_lite_v1.menu.exception.enabled=Enabled +espresso_lite_v1.menu.exception.enabled.build.exception_flags=-fexceptions +espresso_lite_v1.menu.exception.enabled.build.stdcpp_lib=-lstdc++ +espresso_lite_v1.menu.exception.disabled=Disabled +espresso_lite_v1.menu.exception.disabled.build.exception_flags=-fno-exceptions +espresso_lite_v1.menu.exception.disabled.build.stdcpp_lib=-lstdc++-nox +espresso_lite_v1.build.flash_mode=dio +espresso_lite_v1.build.flash_freq=40 +espresso_lite_v1.menu.eesz.4M=4M (no SPIFFS) +espresso_lite_v1.menu.eesz.4M.build.flash_size=4M +espresso_lite_v1.menu.eesz.4M.build.flash_size_bytes=0x400000 +espresso_lite_v1.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld +espresso_lite_v1.menu.eesz.4M.build.spiffs_pagesize=256 +espresso_lite_v1.menu.eesz.4M.upload.maximum_size=1044464 +espresso_lite_v1.menu.eesz.4M.build.rfcal_addr=0x3FC000 +espresso_lite_v1.menu.eesz.4M1M=4M (1M SPIFFS) +espresso_lite_v1.menu.eesz.4M1M.build.flash_size=4M +espresso_lite_v1.menu.eesz.4M1M.build.flash_size_bytes=0x400000 +espresso_lite_v1.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld +espresso_lite_v1.menu.eesz.4M1M.build.spiffs_pagesize=256 +espresso_lite_v1.menu.eesz.4M1M.upload.maximum_size=1044464 +espresso_lite_v1.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 +espresso_lite_v1.menu.eesz.4M1M.build.spiffs_start=0x300000 +espresso_lite_v1.menu.eesz.4M1M.build.spiffs_end=0x3FB000 +espresso_lite_v1.menu.eesz.4M1M.build.spiffs_blocksize=8192 +espresso_lite_v1.menu.eesz.4M2M=4M (2M SPIFFS) +espresso_lite_v1.menu.eesz.4M2M.build.flash_size=4M +espresso_lite_v1.menu.eesz.4M2M.build.flash_size_bytes=0x400000 +espresso_lite_v1.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld +espresso_lite_v1.menu.eesz.4M2M.build.spiffs_pagesize=256 +espresso_lite_v1.menu.eesz.4M2M.upload.maximum_size=1044464 +espresso_lite_v1.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 +espresso_lite_v1.menu.eesz.4M2M.build.spiffs_start=0x200000 +espresso_lite_v1.menu.eesz.4M2M.build.spiffs_end=0x3FB000 +espresso_lite_v1.menu.eesz.4M2M.build.spiffs_blocksize=8192 +espresso_lite_v1.menu.eesz.4M3M=4M (3M SPIFFS) +espresso_lite_v1.menu.eesz.4M3M.build.flash_size=4M +espresso_lite_v1.menu.eesz.4M3M.build.flash_size_bytes=0x400000 +espresso_lite_v1.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld +espresso_lite_v1.menu.eesz.4M3M.build.spiffs_pagesize=256 +espresso_lite_v1.menu.eesz.4M3M.upload.maximum_size=1044464 +espresso_lite_v1.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 +espresso_lite_v1.menu.eesz.4M3M.build.spiffs_start=0x100000 +espresso_lite_v1.menu.eesz.4M3M.build.spiffs_end=0x3FB000 +espresso_lite_v1.menu.eesz.4M3M.build.spiffs_blocksize=8192 +espresso_lite_v1.menu.ResetMethod.ck=ck +espresso_lite_v1.menu.ResetMethod.ck.upload.resetmethod=ck +espresso_lite_v1.menu.ResetMethod.nodemcu=nodemcu +espresso_lite_v1.menu.ResetMethod.nodemcu.upload.resetmethod=nodemcu +espresso_lite_v1.menu.ip.lm2f=v2 Lower Memory +espresso_lite_v1.menu.ip.lm2f.build.lwip_include=lwip2/include +espresso_lite_v1.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +espresso_lite_v1.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +espresso_lite_v1.menu.ip.hb2f=v2 Higher Bandwidth +espresso_lite_v1.menu.ip.hb2f.build.lwip_include=lwip2/include +espresso_lite_v1.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +espresso_lite_v1.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +espresso_lite_v1.menu.ip.lm2n=v2 Lower Memory (no features) +espresso_lite_v1.menu.ip.lm2n.build.lwip_include=lwip2/include +espresso_lite_v1.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +espresso_lite_v1.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +espresso_lite_v1.menu.ip.hb2n=v2 Higher Bandwidth (no features) +espresso_lite_v1.menu.ip.hb2n.build.lwip_include=lwip2/include +espresso_lite_v1.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +espresso_lite_v1.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +espresso_lite_v1.menu.ip.lm6f=v2 IPv6 Lower Memory +espresso_lite_v1.menu.ip.lm6f.build.lwip_include=lwip2/include +espresso_lite_v1.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +espresso_lite_v1.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +espresso_lite_v1.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +espresso_lite_v1.menu.ip.hb6f.build.lwip_include=lwip2/include +espresso_lite_v1.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +espresso_lite_v1.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +espresso_lite_v1.menu.ip.hb1=v1.4 Higher Bandwidth +espresso_lite_v1.menu.ip.hb1.build.lwip_lib=-llwip_gcc +espresso_lite_v1.menu.ip.hb1.build.lwip_flags=-DLWIP_OPEN_SRC +espresso_lite_v1.menu.ip.src=v1.4 Compile from source +espresso_lite_v1.menu.ip.src.build.lwip_lib=-llwip_src +espresso_lite_v1.menu.ip.src.build.lwip_flags=-DLWIP_OPEN_SRC +espresso_lite_v1.menu.ip.src.recipe.hooks.sketch.prebuild.1.pattern=make -C "{runtime.platform.path}/tools/sdk/lwip/src" install TOOLS_PATH="{runtime.tools.xtensa-lx106-elf-gcc.path}/bin/xtensa-lx106-elf-" +espresso_lite_v1.menu.dbg.Disabled=Disabled +espresso_lite_v1.menu.dbg.Disabled.build.debug_port= +espresso_lite_v1.menu.dbg.Serial=Serial +espresso_lite_v1.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +espresso_lite_v1.menu.dbg.Serial1=Serial1 +espresso_lite_v1.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +espresso_lite_v1.menu.lvl.None____=None +espresso_lite_v1.menu.lvl.None____.build.debug_level= +espresso_lite_v1.menu.lvl.SSL=SSL +espresso_lite_v1.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +espresso_lite_v1.menu.lvl.TLS_MEM=TLS_MEM +espresso_lite_v1.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +espresso_lite_v1.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +espresso_lite_v1.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +espresso_lite_v1.menu.lvl.HTTP_SERVER=HTTP_SERVER +espresso_lite_v1.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +espresso_lite_v1.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +espresso_lite_v1.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +espresso_lite_v1.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +espresso_lite_v1.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +espresso_lite_v1.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +espresso_lite_v1.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +espresso_lite_v1.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +espresso_lite_v1.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +espresso_lite_v1.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +espresso_lite_v1.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +espresso_lite_v1.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +espresso_lite_v1.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +espresso_lite_v1.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +espresso_lite_v1.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +espresso_lite_v1.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +espresso_lite_v1.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +espresso_lite_v1.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +espresso_lite_v1.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +espresso_lite_v1.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +espresso_lite_v1.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +espresso_lite_v1.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +espresso_lite_v1.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +espresso_lite_v1.menu.lvl.CORE=CORE +espresso_lite_v1.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +espresso_lite_v1.menu.lvl.WIFI=WIFI +espresso_lite_v1.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +espresso_lite_v1.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +espresso_lite_v1.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +espresso_lite_v1.menu.lvl.UPDATER=UPDATER +espresso_lite_v1.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +espresso_lite_v1.menu.lvl.OTA=OTA +espresso_lite_v1.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +espresso_lite_v1.menu.lvl.OOM=OOM +espresso_lite_v1.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +espresso_lite_v1.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOM=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM +espresso_lite_v1.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOM.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM +espresso_lite_v1.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM +espresso_lite_v1.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM +espresso_lite_v1.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +espresso_lite_v1.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +espresso_lite_v1.menu.wipe.none=Only Sketch +espresso_lite_v1.menu.wipe.none.upload.erase_cmd= +espresso_lite_v1.menu.wipe.sdk=Sketch + WiFi Settings +espresso_lite_v1.menu.wipe.sdk.upload.erase_cmd=-ca "{build.rfcal_addr}" -cz 0x4000 +espresso_lite_v1.menu.wipe.all=All Flash Contents +espresso_lite_v1.menu.wipe.all.upload.erase_cmd=-ca 0x0 -cz "{build.flash_size_bytes}" +espresso_lite_v1.menu.baud.115200=115200 +espresso_lite_v1.menu.baud.115200.upload.speed=115200 +espresso_lite_v1.menu.baud.9600=9600 +espresso_lite_v1.menu.baud.9600.upload.speed=9600 +espresso_lite_v1.menu.baud.57600=57600 +espresso_lite_v1.menu.baud.57600.upload.speed=57600 +espresso_lite_v1.menu.baud.230400.linux=230400 +espresso_lite_v1.menu.baud.230400.macosx=230400 +espresso_lite_v1.menu.baud.230400.upload.speed=230400 +espresso_lite_v1.menu.baud.256000.windows=256000 +espresso_lite_v1.menu.baud.256000.upload.speed=256000 +espresso_lite_v1.menu.baud.460800.linux=460800 +espresso_lite_v1.menu.baud.460800.macosx=460800 +espresso_lite_v1.menu.baud.460800.upload.speed=460800 +espresso_lite_v1.menu.baud.512000.windows=512000 +espresso_lite_v1.menu.baud.512000.upload.speed=512000 +espresso_lite_v1.menu.baud.921600=921600 +espresso_lite_v1.menu.baud.921600.upload.speed=921600 + +############################################################## +espresso_lite_v2.name=ESPresso Lite 2.0 +espresso_lite_v2.build.board=ESP8266_ESPRESSO_LITE_V2 +espresso_lite_v2.build.variant=espresso_lite_v2 +espresso_lite_v2.upload.tool=esptool +espresso_lite_v2.upload.maximum_data_size=81920 +espresso_lite_v2.upload.wait_for_upload_port=true +espresso_lite_v2.upload.erase_cmd= +espresso_lite_v2.serial.disableDTR=true +espresso_lite_v2.serial.disableRTS=true +espresso_lite_v2.build.mcu=esp8266 +espresso_lite_v2.build.core=esp8266 +espresso_lite_v2.build.spiffs_pagesize=256 +espresso_lite_v2.build.debug_port= +espresso_lite_v2.build.debug_level= +espresso_lite_v2.menu.xtal.80=80 MHz +espresso_lite_v2.menu.xtal.80.build.f_cpu=80000000L +espresso_lite_v2.menu.xtal.160=160 MHz +espresso_lite_v2.menu.xtal.160.build.f_cpu=160000000L +espresso_lite_v2.menu.vt.flash=Flash +espresso_lite_v2.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +espresso_lite_v2.menu.vt.heap=Heap +espresso_lite_v2.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +espresso_lite_v2.menu.vt.iram=IRAM +espresso_lite_v2.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +espresso_lite_v2.menu.exception.enabled=Enabled +espresso_lite_v2.menu.exception.enabled.build.exception_flags=-fexceptions +espresso_lite_v2.menu.exception.enabled.build.stdcpp_lib=-lstdc++ +espresso_lite_v2.menu.exception.disabled=Disabled +espresso_lite_v2.menu.exception.disabled.build.exception_flags=-fno-exceptions +espresso_lite_v2.menu.exception.disabled.build.stdcpp_lib=-lstdc++-nox +espresso_lite_v2.build.flash_mode=dio +espresso_lite_v2.build.flash_freq=40 +espresso_lite_v2.menu.eesz.4M=4M (no SPIFFS) +espresso_lite_v2.menu.eesz.4M.build.flash_size=4M +espresso_lite_v2.menu.eesz.4M.build.flash_size_bytes=0x400000 +espresso_lite_v2.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld +espresso_lite_v2.menu.eesz.4M.build.spiffs_pagesize=256 +espresso_lite_v2.menu.eesz.4M.upload.maximum_size=1044464 +espresso_lite_v2.menu.eesz.4M.build.rfcal_addr=0x3FC000 +espresso_lite_v2.menu.eesz.4M1M=4M (1M SPIFFS) +espresso_lite_v2.menu.eesz.4M1M.build.flash_size=4M +espresso_lite_v2.menu.eesz.4M1M.build.flash_size_bytes=0x400000 +espresso_lite_v2.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld +espresso_lite_v2.menu.eesz.4M1M.build.spiffs_pagesize=256 +espresso_lite_v2.menu.eesz.4M1M.upload.maximum_size=1044464 +espresso_lite_v2.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 +espresso_lite_v2.menu.eesz.4M1M.build.spiffs_start=0x300000 +espresso_lite_v2.menu.eesz.4M1M.build.spiffs_end=0x3FB000 +espresso_lite_v2.menu.eesz.4M1M.build.spiffs_blocksize=8192 +espresso_lite_v2.menu.eesz.4M2M=4M (2M SPIFFS) +espresso_lite_v2.menu.eesz.4M2M.build.flash_size=4M +espresso_lite_v2.menu.eesz.4M2M.build.flash_size_bytes=0x400000 +espresso_lite_v2.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld +espresso_lite_v2.menu.eesz.4M2M.build.spiffs_pagesize=256 +espresso_lite_v2.menu.eesz.4M2M.upload.maximum_size=1044464 +espresso_lite_v2.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 +espresso_lite_v2.menu.eesz.4M2M.build.spiffs_start=0x200000 +espresso_lite_v2.menu.eesz.4M2M.build.spiffs_end=0x3FB000 +espresso_lite_v2.menu.eesz.4M2M.build.spiffs_blocksize=8192 +espresso_lite_v2.menu.eesz.4M3M=4M (3M SPIFFS) +espresso_lite_v2.menu.eesz.4M3M.build.flash_size=4M +espresso_lite_v2.menu.eesz.4M3M.build.flash_size_bytes=0x400000 +espresso_lite_v2.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld +espresso_lite_v2.menu.eesz.4M3M.build.spiffs_pagesize=256 +espresso_lite_v2.menu.eesz.4M3M.upload.maximum_size=1044464 +espresso_lite_v2.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 +espresso_lite_v2.menu.eesz.4M3M.build.spiffs_start=0x100000 +espresso_lite_v2.menu.eesz.4M3M.build.spiffs_end=0x3FB000 +espresso_lite_v2.menu.eesz.4M3M.build.spiffs_blocksize=8192 +espresso_lite_v2.menu.ResetMethod.ck=ck +espresso_lite_v2.menu.ResetMethod.ck.upload.resetmethod=ck +espresso_lite_v2.menu.ResetMethod.nodemcu=nodemcu +espresso_lite_v2.menu.ResetMethod.nodemcu.upload.resetmethod=nodemcu +espresso_lite_v2.menu.ip.lm2f=v2 Lower Memory +espresso_lite_v2.menu.ip.lm2f.build.lwip_include=lwip2/include +espresso_lite_v2.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +espresso_lite_v2.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +espresso_lite_v2.menu.ip.hb2f=v2 Higher Bandwidth +espresso_lite_v2.menu.ip.hb2f.build.lwip_include=lwip2/include +espresso_lite_v2.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +espresso_lite_v2.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +espresso_lite_v2.menu.ip.lm2n=v2 Lower Memory (no features) +espresso_lite_v2.menu.ip.lm2n.build.lwip_include=lwip2/include +espresso_lite_v2.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +espresso_lite_v2.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +espresso_lite_v2.menu.ip.hb2n=v2 Higher Bandwidth (no features) +espresso_lite_v2.menu.ip.hb2n.build.lwip_include=lwip2/include +espresso_lite_v2.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +espresso_lite_v2.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +espresso_lite_v2.menu.ip.lm6f=v2 IPv6 Lower Memory +espresso_lite_v2.menu.ip.lm6f.build.lwip_include=lwip2/include +espresso_lite_v2.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +espresso_lite_v2.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +espresso_lite_v2.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +espresso_lite_v2.menu.ip.hb6f.build.lwip_include=lwip2/include +espresso_lite_v2.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +espresso_lite_v2.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +espresso_lite_v2.menu.ip.hb1=v1.4 Higher Bandwidth +espresso_lite_v2.menu.ip.hb1.build.lwip_lib=-llwip_gcc +espresso_lite_v2.menu.ip.hb1.build.lwip_flags=-DLWIP_OPEN_SRC +espresso_lite_v2.menu.ip.src=v1.4 Compile from source +espresso_lite_v2.menu.ip.src.build.lwip_lib=-llwip_src +espresso_lite_v2.menu.ip.src.build.lwip_flags=-DLWIP_OPEN_SRC +espresso_lite_v2.menu.ip.src.recipe.hooks.sketch.prebuild.1.pattern=make -C "{runtime.platform.path}/tools/sdk/lwip/src" install TOOLS_PATH="{runtime.tools.xtensa-lx106-elf-gcc.path}/bin/xtensa-lx106-elf-" +espresso_lite_v2.menu.dbg.Disabled=Disabled +espresso_lite_v2.menu.dbg.Disabled.build.debug_port= +espresso_lite_v2.menu.dbg.Serial=Serial +espresso_lite_v2.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +espresso_lite_v2.menu.dbg.Serial1=Serial1 +espresso_lite_v2.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +espresso_lite_v2.menu.lvl.None____=None +espresso_lite_v2.menu.lvl.None____.build.debug_level= +espresso_lite_v2.menu.lvl.SSL=SSL +espresso_lite_v2.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +espresso_lite_v2.menu.lvl.TLS_MEM=TLS_MEM +espresso_lite_v2.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +espresso_lite_v2.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +espresso_lite_v2.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +espresso_lite_v2.menu.lvl.HTTP_SERVER=HTTP_SERVER +espresso_lite_v2.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +espresso_lite_v2.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +espresso_lite_v2.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +espresso_lite_v2.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +espresso_lite_v2.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +espresso_lite_v2.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +espresso_lite_v2.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +espresso_lite_v2.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +espresso_lite_v2.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +espresso_lite_v2.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +espresso_lite_v2.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +espresso_lite_v2.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +espresso_lite_v2.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +espresso_lite_v2.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +espresso_lite_v2.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +espresso_lite_v2.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +espresso_lite_v2.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +espresso_lite_v2.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +espresso_lite_v2.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +espresso_lite_v2.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +espresso_lite_v2.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +espresso_lite_v2.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +espresso_lite_v2.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +espresso_lite_v2.menu.lvl.CORE=CORE +espresso_lite_v2.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +espresso_lite_v2.menu.lvl.WIFI=WIFI +espresso_lite_v2.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +espresso_lite_v2.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +espresso_lite_v2.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +espresso_lite_v2.menu.lvl.UPDATER=UPDATER +espresso_lite_v2.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +espresso_lite_v2.menu.lvl.OTA=OTA +espresso_lite_v2.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +espresso_lite_v2.menu.lvl.OOM=OOM +espresso_lite_v2.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +espresso_lite_v2.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOM=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM +espresso_lite_v2.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOM.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM +espresso_lite_v2.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM +espresso_lite_v2.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM +espresso_lite_v2.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +espresso_lite_v2.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +espresso_lite_v2.menu.wipe.none=Only Sketch +espresso_lite_v2.menu.wipe.none.upload.erase_cmd= +espresso_lite_v2.menu.wipe.sdk=Sketch + WiFi Settings +espresso_lite_v2.menu.wipe.sdk.upload.erase_cmd=-ca "{build.rfcal_addr}" -cz 0x4000 +espresso_lite_v2.menu.wipe.all=All Flash Contents +espresso_lite_v2.menu.wipe.all.upload.erase_cmd=-ca 0x0 -cz "{build.flash_size_bytes}" +espresso_lite_v2.menu.baud.115200=115200 +espresso_lite_v2.menu.baud.115200.upload.speed=115200 +espresso_lite_v2.menu.baud.9600=9600 +espresso_lite_v2.menu.baud.9600.upload.speed=9600 +espresso_lite_v2.menu.baud.57600=57600 +espresso_lite_v2.menu.baud.57600.upload.speed=57600 +espresso_lite_v2.menu.baud.230400.linux=230400 +espresso_lite_v2.menu.baud.230400.macosx=230400 +espresso_lite_v2.menu.baud.230400.upload.speed=230400 +espresso_lite_v2.menu.baud.256000.windows=256000 +espresso_lite_v2.menu.baud.256000.upload.speed=256000 +espresso_lite_v2.menu.baud.460800.linux=460800 +espresso_lite_v2.menu.baud.460800.macosx=460800 +espresso_lite_v2.menu.baud.460800.upload.speed=460800 +espresso_lite_v2.menu.baud.512000.windows=512000 +espresso_lite_v2.menu.baud.512000.upload.speed=512000 +espresso_lite_v2.menu.baud.921600=921600 +espresso_lite_v2.menu.baud.921600.upload.speed=921600 + +############################################################## +phoenix_v1.name=Phoenix 1.0 +phoenix_v1.build.board=ESP8266_PHOENIX_V1 +phoenix_v1.build.variant=phoenix_v1 +phoenix_v1.upload.tool=esptool +phoenix_v1.upload.maximum_data_size=81920 +phoenix_v1.upload.wait_for_upload_port=true +phoenix_v1.upload.erase_cmd= +phoenix_v1.serial.disableDTR=true +phoenix_v1.serial.disableRTS=true +phoenix_v1.build.mcu=esp8266 +phoenix_v1.build.core=esp8266 +phoenix_v1.build.spiffs_pagesize=256 +phoenix_v1.build.debug_port= +phoenix_v1.build.debug_level= +phoenix_v1.menu.xtal.80=80 MHz +phoenix_v1.menu.xtal.80.build.f_cpu=80000000L +phoenix_v1.menu.xtal.160=160 MHz +phoenix_v1.menu.xtal.160.build.f_cpu=160000000L +phoenix_v1.menu.vt.flash=Flash +phoenix_v1.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +phoenix_v1.menu.vt.heap=Heap +phoenix_v1.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +phoenix_v1.menu.vt.iram=IRAM +phoenix_v1.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +phoenix_v1.menu.exception.enabled=Enabled +phoenix_v1.menu.exception.enabled.build.exception_flags=-fexceptions +phoenix_v1.menu.exception.enabled.build.stdcpp_lib=-lstdc++ +phoenix_v1.menu.exception.disabled=Disabled +phoenix_v1.menu.exception.disabled.build.exception_flags=-fno-exceptions +phoenix_v1.menu.exception.disabled.build.stdcpp_lib=-lstdc++-nox +phoenix_v1.build.flash_mode=dio +phoenix_v1.build.flash_freq=40 +phoenix_v1.menu.eesz.4M=4M (no SPIFFS) +phoenix_v1.menu.eesz.4M.build.flash_size=4M +phoenix_v1.menu.eesz.4M.build.flash_size_bytes=0x400000 +phoenix_v1.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld +phoenix_v1.menu.eesz.4M.build.spiffs_pagesize=256 +phoenix_v1.menu.eesz.4M.upload.maximum_size=1044464 +phoenix_v1.menu.eesz.4M.build.rfcal_addr=0x3FC000 +phoenix_v1.menu.eesz.4M1M=4M (1M SPIFFS) +phoenix_v1.menu.eesz.4M1M.build.flash_size=4M +phoenix_v1.menu.eesz.4M1M.build.flash_size_bytes=0x400000 +phoenix_v1.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld +phoenix_v1.menu.eesz.4M1M.build.spiffs_pagesize=256 +phoenix_v1.menu.eesz.4M1M.upload.maximum_size=1044464 +phoenix_v1.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 +phoenix_v1.menu.eesz.4M1M.build.spiffs_start=0x300000 +phoenix_v1.menu.eesz.4M1M.build.spiffs_end=0x3FB000 +phoenix_v1.menu.eesz.4M1M.build.spiffs_blocksize=8192 +phoenix_v1.menu.eesz.4M2M=4M (2M SPIFFS) +phoenix_v1.menu.eesz.4M2M.build.flash_size=4M +phoenix_v1.menu.eesz.4M2M.build.flash_size_bytes=0x400000 +phoenix_v1.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld +phoenix_v1.menu.eesz.4M2M.build.spiffs_pagesize=256 +phoenix_v1.menu.eesz.4M2M.upload.maximum_size=1044464 +phoenix_v1.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 +phoenix_v1.menu.eesz.4M2M.build.spiffs_start=0x200000 +phoenix_v1.menu.eesz.4M2M.build.spiffs_end=0x3FB000 +phoenix_v1.menu.eesz.4M2M.build.spiffs_blocksize=8192 +phoenix_v1.menu.eesz.4M3M=4M (3M SPIFFS) +phoenix_v1.menu.eesz.4M3M.build.flash_size=4M +phoenix_v1.menu.eesz.4M3M.build.flash_size_bytes=0x400000 +phoenix_v1.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld +phoenix_v1.menu.eesz.4M3M.build.spiffs_pagesize=256 +phoenix_v1.menu.eesz.4M3M.upload.maximum_size=1044464 +phoenix_v1.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 +phoenix_v1.menu.eesz.4M3M.build.spiffs_start=0x100000 +phoenix_v1.menu.eesz.4M3M.build.spiffs_end=0x3FB000 +phoenix_v1.menu.eesz.4M3M.build.spiffs_blocksize=8192 +phoenix_v1.menu.ResetMethod.ck=ck +phoenix_v1.menu.ResetMethod.ck.upload.resetmethod=ck +phoenix_v1.menu.ResetMethod.nodemcu=nodemcu +phoenix_v1.menu.ResetMethod.nodemcu.upload.resetmethod=nodemcu +phoenix_v1.menu.ip.lm2f=v2 Lower Memory +phoenix_v1.menu.ip.lm2f.build.lwip_include=lwip2/include +phoenix_v1.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +phoenix_v1.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +phoenix_v1.menu.ip.hb2f=v2 Higher Bandwidth +phoenix_v1.menu.ip.hb2f.build.lwip_include=lwip2/include +phoenix_v1.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +phoenix_v1.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +phoenix_v1.menu.ip.lm2n=v2 Lower Memory (no features) +phoenix_v1.menu.ip.lm2n.build.lwip_include=lwip2/include +phoenix_v1.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +phoenix_v1.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +phoenix_v1.menu.ip.hb2n=v2 Higher Bandwidth (no features) +phoenix_v1.menu.ip.hb2n.build.lwip_include=lwip2/include +phoenix_v1.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +phoenix_v1.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +phoenix_v1.menu.ip.lm6f=v2 IPv6 Lower Memory +phoenix_v1.menu.ip.lm6f.build.lwip_include=lwip2/include +phoenix_v1.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +phoenix_v1.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +phoenix_v1.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +phoenix_v1.menu.ip.hb6f.build.lwip_include=lwip2/include +phoenix_v1.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +phoenix_v1.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +phoenix_v1.menu.ip.hb1=v1.4 Higher Bandwidth +phoenix_v1.menu.ip.hb1.build.lwip_lib=-llwip_gcc +phoenix_v1.menu.ip.hb1.build.lwip_flags=-DLWIP_OPEN_SRC +phoenix_v1.menu.ip.src=v1.4 Compile from source +phoenix_v1.menu.ip.src.build.lwip_lib=-llwip_src +phoenix_v1.menu.ip.src.build.lwip_flags=-DLWIP_OPEN_SRC +phoenix_v1.menu.ip.src.recipe.hooks.sketch.prebuild.1.pattern=make -C "{runtime.platform.path}/tools/sdk/lwip/src" install TOOLS_PATH="{runtime.tools.xtensa-lx106-elf-gcc.path}/bin/xtensa-lx106-elf-" +phoenix_v1.menu.dbg.Disabled=Disabled +phoenix_v1.menu.dbg.Disabled.build.debug_port= +phoenix_v1.menu.dbg.Serial=Serial +phoenix_v1.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +phoenix_v1.menu.dbg.Serial1=Serial1 +phoenix_v1.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +phoenix_v1.menu.lvl.None____=None +phoenix_v1.menu.lvl.None____.build.debug_level= +phoenix_v1.menu.lvl.SSL=SSL +phoenix_v1.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +phoenix_v1.menu.lvl.TLS_MEM=TLS_MEM +phoenix_v1.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +phoenix_v1.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +phoenix_v1.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +phoenix_v1.menu.lvl.HTTP_SERVER=HTTP_SERVER +phoenix_v1.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +phoenix_v1.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +phoenix_v1.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +phoenix_v1.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +phoenix_v1.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +phoenix_v1.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +phoenix_v1.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +phoenix_v1.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +phoenix_v1.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +phoenix_v1.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +phoenix_v1.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +phoenix_v1.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +phoenix_v1.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +phoenix_v1.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +phoenix_v1.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +phoenix_v1.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +phoenix_v1.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +phoenix_v1.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +phoenix_v1.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +phoenix_v1.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +phoenix_v1.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +phoenix_v1.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +phoenix_v1.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +phoenix_v1.menu.lvl.CORE=CORE +phoenix_v1.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +phoenix_v1.menu.lvl.WIFI=WIFI +phoenix_v1.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +phoenix_v1.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +phoenix_v1.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +phoenix_v1.menu.lvl.UPDATER=UPDATER +phoenix_v1.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +phoenix_v1.menu.lvl.OTA=OTA +phoenix_v1.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +phoenix_v1.menu.lvl.OOM=OOM +phoenix_v1.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +phoenix_v1.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOM=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM +phoenix_v1.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOM.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM +phoenix_v1.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM +phoenix_v1.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM +phoenix_v1.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +phoenix_v1.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +phoenix_v1.menu.wipe.none=Only Sketch +phoenix_v1.menu.wipe.none.upload.erase_cmd= +phoenix_v1.menu.wipe.sdk=Sketch + WiFi Settings +phoenix_v1.menu.wipe.sdk.upload.erase_cmd=-ca "{build.rfcal_addr}" -cz 0x4000 +phoenix_v1.menu.wipe.all=All Flash Contents +phoenix_v1.menu.wipe.all.upload.erase_cmd=-ca 0x0 -cz "{build.flash_size_bytes}" +phoenix_v1.menu.baud.115200=115200 +phoenix_v1.menu.baud.115200.upload.speed=115200 +phoenix_v1.menu.baud.9600=9600 +phoenix_v1.menu.baud.9600.upload.speed=9600 +phoenix_v1.menu.baud.57600=57600 +phoenix_v1.menu.baud.57600.upload.speed=57600 +phoenix_v1.menu.baud.230400.linux=230400 +phoenix_v1.menu.baud.230400.macosx=230400 +phoenix_v1.menu.baud.230400.upload.speed=230400 +phoenix_v1.menu.baud.256000.windows=256000 +phoenix_v1.menu.baud.256000.upload.speed=256000 +phoenix_v1.menu.baud.460800.linux=460800 +phoenix_v1.menu.baud.460800.macosx=460800 +phoenix_v1.menu.baud.460800.upload.speed=460800 +phoenix_v1.menu.baud.512000.windows=512000 +phoenix_v1.menu.baud.512000.upload.speed=512000 +phoenix_v1.menu.baud.921600=921600 +phoenix_v1.menu.baud.921600.upload.speed=921600 + +############################################################## +phoenix_v2.name=Phoenix 2.0 +phoenix_v2.build.board=ESP8266_PHOENIX_V2 +phoenix_v2.build.variant=phoenix_v2 +phoenix_v2.upload.tool=esptool +phoenix_v2.upload.maximum_data_size=81920 +phoenix_v2.upload.wait_for_upload_port=true +phoenix_v2.upload.erase_cmd= +phoenix_v2.serial.disableDTR=true +phoenix_v2.serial.disableRTS=true +phoenix_v2.build.mcu=esp8266 +phoenix_v2.build.core=esp8266 +phoenix_v2.build.spiffs_pagesize=256 +phoenix_v2.build.debug_port= +phoenix_v2.build.debug_level= +phoenix_v2.menu.xtal.80=80 MHz +phoenix_v2.menu.xtal.80.build.f_cpu=80000000L +phoenix_v2.menu.xtal.160=160 MHz +phoenix_v2.menu.xtal.160.build.f_cpu=160000000L +phoenix_v2.menu.vt.flash=Flash +phoenix_v2.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +phoenix_v2.menu.vt.heap=Heap +phoenix_v2.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +phoenix_v2.menu.vt.iram=IRAM +phoenix_v2.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +phoenix_v2.menu.exception.enabled=Enabled +phoenix_v2.menu.exception.enabled.build.exception_flags=-fexceptions +phoenix_v2.menu.exception.enabled.build.stdcpp_lib=-lstdc++ +phoenix_v2.menu.exception.disabled=Disabled +phoenix_v2.menu.exception.disabled.build.exception_flags=-fno-exceptions +phoenix_v2.menu.exception.disabled.build.stdcpp_lib=-lstdc++-nox +phoenix_v2.build.flash_mode=dio +phoenix_v2.build.flash_freq=40 +phoenix_v2.menu.eesz.4M=4M (no SPIFFS) +phoenix_v2.menu.eesz.4M.build.flash_size=4M +phoenix_v2.menu.eesz.4M.build.flash_size_bytes=0x400000 +phoenix_v2.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld +phoenix_v2.menu.eesz.4M.build.spiffs_pagesize=256 +phoenix_v2.menu.eesz.4M.upload.maximum_size=1044464 +phoenix_v2.menu.eesz.4M.build.rfcal_addr=0x3FC000 +phoenix_v2.menu.eesz.4M1M=4M (1M SPIFFS) +phoenix_v2.menu.eesz.4M1M.build.flash_size=4M +phoenix_v2.menu.eesz.4M1M.build.flash_size_bytes=0x400000 +phoenix_v2.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld +phoenix_v2.menu.eesz.4M1M.build.spiffs_pagesize=256 +phoenix_v2.menu.eesz.4M1M.upload.maximum_size=1044464 +phoenix_v2.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 +phoenix_v2.menu.eesz.4M1M.build.spiffs_start=0x300000 +phoenix_v2.menu.eesz.4M1M.build.spiffs_end=0x3FB000 +phoenix_v2.menu.eesz.4M1M.build.spiffs_blocksize=8192 +phoenix_v2.menu.eesz.4M2M=4M (2M SPIFFS) +phoenix_v2.menu.eesz.4M2M.build.flash_size=4M +phoenix_v2.menu.eesz.4M2M.build.flash_size_bytes=0x400000 +phoenix_v2.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld +phoenix_v2.menu.eesz.4M2M.build.spiffs_pagesize=256 +phoenix_v2.menu.eesz.4M2M.upload.maximum_size=1044464 +phoenix_v2.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 +phoenix_v2.menu.eesz.4M2M.build.spiffs_start=0x200000 +phoenix_v2.menu.eesz.4M2M.build.spiffs_end=0x3FB000 +phoenix_v2.menu.eesz.4M2M.build.spiffs_blocksize=8192 +phoenix_v2.menu.eesz.4M3M=4M (3M SPIFFS) +phoenix_v2.menu.eesz.4M3M.build.flash_size=4M +phoenix_v2.menu.eesz.4M3M.build.flash_size_bytes=0x400000 +phoenix_v2.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld +phoenix_v2.menu.eesz.4M3M.build.spiffs_pagesize=256 +phoenix_v2.menu.eesz.4M3M.upload.maximum_size=1044464 +phoenix_v2.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 +phoenix_v2.menu.eesz.4M3M.build.spiffs_start=0x100000 +phoenix_v2.menu.eesz.4M3M.build.spiffs_end=0x3FB000 +phoenix_v2.menu.eesz.4M3M.build.spiffs_blocksize=8192 +phoenix_v2.menu.ResetMethod.ck=ck +phoenix_v2.menu.ResetMethod.ck.upload.resetmethod=ck +phoenix_v2.menu.ResetMethod.nodemcu=nodemcu +phoenix_v2.menu.ResetMethod.nodemcu.upload.resetmethod=nodemcu +phoenix_v2.menu.ip.lm2f=v2 Lower Memory +phoenix_v2.menu.ip.lm2f.build.lwip_include=lwip2/include +phoenix_v2.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +phoenix_v2.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +phoenix_v2.menu.ip.hb2f=v2 Higher Bandwidth +phoenix_v2.menu.ip.hb2f.build.lwip_include=lwip2/include +phoenix_v2.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +phoenix_v2.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +phoenix_v2.menu.ip.lm2n=v2 Lower Memory (no features) +phoenix_v2.menu.ip.lm2n.build.lwip_include=lwip2/include +phoenix_v2.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +phoenix_v2.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +phoenix_v2.menu.ip.hb2n=v2 Higher Bandwidth (no features) +phoenix_v2.menu.ip.hb2n.build.lwip_include=lwip2/include +phoenix_v2.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +phoenix_v2.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +phoenix_v2.menu.ip.lm6f=v2 IPv6 Lower Memory +phoenix_v2.menu.ip.lm6f.build.lwip_include=lwip2/include +phoenix_v2.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +phoenix_v2.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +phoenix_v2.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +phoenix_v2.menu.ip.hb6f.build.lwip_include=lwip2/include +phoenix_v2.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +phoenix_v2.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +phoenix_v2.menu.ip.hb1=v1.4 Higher Bandwidth +phoenix_v2.menu.ip.hb1.build.lwip_lib=-llwip_gcc +phoenix_v2.menu.ip.hb1.build.lwip_flags=-DLWIP_OPEN_SRC +phoenix_v2.menu.ip.src=v1.4 Compile from source +phoenix_v2.menu.ip.src.build.lwip_lib=-llwip_src +phoenix_v2.menu.ip.src.build.lwip_flags=-DLWIP_OPEN_SRC +phoenix_v2.menu.ip.src.recipe.hooks.sketch.prebuild.1.pattern=make -C "{runtime.platform.path}/tools/sdk/lwip/src" install TOOLS_PATH="{runtime.tools.xtensa-lx106-elf-gcc.path}/bin/xtensa-lx106-elf-" +phoenix_v2.menu.dbg.Disabled=Disabled +phoenix_v2.menu.dbg.Disabled.build.debug_port= +phoenix_v2.menu.dbg.Serial=Serial +phoenix_v2.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +phoenix_v2.menu.dbg.Serial1=Serial1 +phoenix_v2.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +phoenix_v2.menu.lvl.None____=None +phoenix_v2.menu.lvl.None____.build.debug_level= +phoenix_v2.menu.lvl.SSL=SSL +phoenix_v2.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +phoenix_v2.menu.lvl.TLS_MEM=TLS_MEM +phoenix_v2.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +phoenix_v2.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +phoenix_v2.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +phoenix_v2.menu.lvl.HTTP_SERVER=HTTP_SERVER +phoenix_v2.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +phoenix_v2.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +phoenix_v2.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +phoenix_v2.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +phoenix_v2.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +phoenix_v2.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +phoenix_v2.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +phoenix_v2.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +phoenix_v2.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +phoenix_v2.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +phoenix_v2.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +phoenix_v2.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +phoenix_v2.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +phoenix_v2.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +phoenix_v2.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +phoenix_v2.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +phoenix_v2.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +phoenix_v2.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +phoenix_v2.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +phoenix_v2.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +phoenix_v2.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +phoenix_v2.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +phoenix_v2.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +phoenix_v2.menu.lvl.CORE=CORE +phoenix_v2.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +phoenix_v2.menu.lvl.WIFI=WIFI +phoenix_v2.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +phoenix_v2.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +phoenix_v2.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +phoenix_v2.menu.lvl.UPDATER=UPDATER +phoenix_v2.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +phoenix_v2.menu.lvl.OTA=OTA +phoenix_v2.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +phoenix_v2.menu.lvl.OOM=OOM +phoenix_v2.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +phoenix_v2.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOM=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM +phoenix_v2.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOM.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM +phoenix_v2.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM +phoenix_v2.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM +phoenix_v2.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +phoenix_v2.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +phoenix_v2.menu.wipe.none=Only Sketch +phoenix_v2.menu.wipe.none.upload.erase_cmd= +phoenix_v2.menu.wipe.sdk=Sketch + WiFi Settings +phoenix_v2.menu.wipe.sdk.upload.erase_cmd=-ca "{build.rfcal_addr}" -cz 0x4000 +phoenix_v2.menu.wipe.all=All Flash Contents +phoenix_v2.menu.wipe.all.upload.erase_cmd=-ca 0x0 -cz "{build.flash_size_bytes}" +phoenix_v2.menu.baud.115200=115200 +phoenix_v2.menu.baud.115200.upload.speed=115200 +phoenix_v2.menu.baud.9600=9600 +phoenix_v2.menu.baud.9600.upload.speed=9600 +phoenix_v2.menu.baud.57600=57600 +phoenix_v2.menu.baud.57600.upload.speed=57600 +phoenix_v2.menu.baud.230400.linux=230400 +phoenix_v2.menu.baud.230400.macosx=230400 +phoenix_v2.menu.baud.230400.upload.speed=230400 +phoenix_v2.menu.baud.256000.windows=256000 +phoenix_v2.menu.baud.256000.upload.speed=256000 +phoenix_v2.menu.baud.460800.linux=460800 +phoenix_v2.menu.baud.460800.macosx=460800 +phoenix_v2.menu.baud.460800.upload.speed=460800 +phoenix_v2.menu.baud.512000.windows=512000 +phoenix_v2.menu.baud.512000.upload.speed=512000 +phoenix_v2.menu.baud.921600=921600 +phoenix_v2.menu.baud.921600.upload.speed=921600 + +############################################################## +nodemcu.name=NodeMCU 0.9 (ESP-12 Module) +nodemcu.build.board=ESP8266_NODEMCU +nodemcu.build.variant=nodemcu +nodemcu.upload.tool=esptool +nodemcu.upload.maximum_data_size=81920 +nodemcu.upload.wait_for_upload_port=true +nodemcu.upload.erase_cmd= +nodemcu.serial.disableDTR=true +nodemcu.serial.disableRTS=true +nodemcu.build.mcu=esp8266 +nodemcu.build.core=esp8266 +nodemcu.build.spiffs_pagesize=256 +nodemcu.build.debug_port= +nodemcu.build.debug_level= +nodemcu.menu.xtal.80=80 MHz +nodemcu.menu.xtal.80.build.f_cpu=80000000L +nodemcu.menu.xtal.160=160 MHz +nodemcu.menu.xtal.160.build.f_cpu=160000000L +nodemcu.menu.vt.flash=Flash +nodemcu.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +nodemcu.menu.vt.heap=Heap +nodemcu.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +nodemcu.menu.vt.iram=IRAM +nodemcu.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +nodemcu.menu.exception.enabled=Enabled +nodemcu.menu.exception.enabled.build.exception_flags=-fexceptions +nodemcu.menu.exception.enabled.build.stdcpp_lib=-lstdc++ +nodemcu.menu.exception.disabled=Disabled +nodemcu.menu.exception.disabled.build.exception_flags=-fno-exceptions +nodemcu.menu.exception.disabled.build.stdcpp_lib=-lstdc++-nox +nodemcu.upload.resetmethod=nodemcu +nodemcu.build.flash_mode=qio +nodemcu.build.flash_freq=40 +nodemcu.menu.eesz.4M=4M (no SPIFFS) +nodemcu.menu.eesz.4M.build.flash_size=4M +nodemcu.menu.eesz.4M.build.flash_size_bytes=0x400000 +nodemcu.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld +nodemcu.menu.eesz.4M.build.spiffs_pagesize=256 +nodemcu.menu.eesz.4M.upload.maximum_size=1044464 +nodemcu.menu.eesz.4M.build.rfcal_addr=0x3FC000 +nodemcu.menu.eesz.4M1M=4M (1M SPIFFS) +nodemcu.menu.eesz.4M1M.build.flash_size=4M +nodemcu.menu.eesz.4M1M.build.flash_size_bytes=0x400000 +nodemcu.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld +nodemcu.menu.eesz.4M1M.build.spiffs_pagesize=256 +nodemcu.menu.eesz.4M1M.upload.maximum_size=1044464 +nodemcu.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 +nodemcu.menu.eesz.4M1M.build.spiffs_start=0x300000 +nodemcu.menu.eesz.4M1M.build.spiffs_end=0x3FB000 +nodemcu.menu.eesz.4M1M.build.spiffs_blocksize=8192 +nodemcu.menu.eesz.4M2M=4M (2M SPIFFS) +nodemcu.menu.eesz.4M2M.build.flash_size=4M +nodemcu.menu.eesz.4M2M.build.flash_size_bytes=0x400000 +nodemcu.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld +nodemcu.menu.eesz.4M2M.build.spiffs_pagesize=256 +nodemcu.menu.eesz.4M2M.upload.maximum_size=1044464 +nodemcu.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 +nodemcu.menu.eesz.4M2M.build.spiffs_start=0x200000 +nodemcu.menu.eesz.4M2M.build.spiffs_end=0x3FB000 +nodemcu.menu.eesz.4M2M.build.spiffs_blocksize=8192 +nodemcu.menu.eesz.4M3M=4M (3M SPIFFS) +nodemcu.menu.eesz.4M3M.build.flash_size=4M +nodemcu.menu.eesz.4M3M.build.flash_size_bytes=0x400000 +nodemcu.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld +nodemcu.menu.eesz.4M3M.build.spiffs_pagesize=256 +nodemcu.menu.eesz.4M3M.upload.maximum_size=1044464 +nodemcu.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 +nodemcu.menu.eesz.4M3M.build.spiffs_start=0x100000 +nodemcu.menu.eesz.4M3M.build.spiffs_end=0x3FB000 +nodemcu.menu.eesz.4M3M.build.spiffs_blocksize=8192 +nodemcu.menu.ip.lm2f=v2 Lower Memory +nodemcu.menu.ip.lm2f.build.lwip_include=lwip2/include +nodemcu.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +nodemcu.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +nodemcu.menu.ip.hb2f=v2 Higher Bandwidth +nodemcu.menu.ip.hb2f.build.lwip_include=lwip2/include +nodemcu.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +nodemcu.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +nodemcu.menu.ip.lm2n=v2 Lower Memory (no features) +nodemcu.menu.ip.lm2n.build.lwip_include=lwip2/include +nodemcu.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +nodemcu.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +nodemcu.menu.ip.hb2n=v2 Higher Bandwidth (no features) +nodemcu.menu.ip.hb2n.build.lwip_include=lwip2/include +nodemcu.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +nodemcu.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +nodemcu.menu.ip.lm6f=v2 IPv6 Lower Memory +nodemcu.menu.ip.lm6f.build.lwip_include=lwip2/include +nodemcu.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +nodemcu.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +nodemcu.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +nodemcu.menu.ip.hb6f.build.lwip_include=lwip2/include +nodemcu.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +nodemcu.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +nodemcu.menu.ip.hb1=v1.4 Higher Bandwidth +nodemcu.menu.ip.hb1.build.lwip_lib=-llwip_gcc +nodemcu.menu.ip.hb1.build.lwip_flags=-DLWIP_OPEN_SRC +nodemcu.menu.ip.src=v1.4 Compile from source +nodemcu.menu.ip.src.build.lwip_lib=-llwip_src +nodemcu.menu.ip.src.build.lwip_flags=-DLWIP_OPEN_SRC +nodemcu.menu.ip.src.recipe.hooks.sketch.prebuild.1.pattern=make -C "{runtime.platform.path}/tools/sdk/lwip/src" install TOOLS_PATH="{runtime.tools.xtensa-lx106-elf-gcc.path}/bin/xtensa-lx106-elf-" +nodemcu.menu.dbg.Disabled=Disabled +nodemcu.menu.dbg.Disabled.build.debug_port= +nodemcu.menu.dbg.Serial=Serial +nodemcu.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +nodemcu.menu.dbg.Serial1=Serial1 +nodemcu.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +nodemcu.menu.lvl.None____=None +nodemcu.menu.lvl.None____.build.debug_level= +nodemcu.menu.lvl.SSL=SSL +nodemcu.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +nodemcu.menu.lvl.TLS_MEM=TLS_MEM +nodemcu.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +nodemcu.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +nodemcu.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +nodemcu.menu.lvl.HTTP_SERVER=HTTP_SERVER +nodemcu.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +nodemcu.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +nodemcu.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +nodemcu.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +nodemcu.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +nodemcu.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +nodemcu.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +nodemcu.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +nodemcu.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +nodemcu.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +nodemcu.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +nodemcu.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +nodemcu.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +nodemcu.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +nodemcu.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +nodemcu.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +nodemcu.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +nodemcu.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +nodemcu.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +nodemcu.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +nodemcu.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +nodemcu.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +nodemcu.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +nodemcu.menu.lvl.CORE=CORE +nodemcu.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +nodemcu.menu.lvl.WIFI=WIFI +nodemcu.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +nodemcu.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +nodemcu.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +nodemcu.menu.lvl.UPDATER=UPDATER +nodemcu.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +nodemcu.menu.lvl.OTA=OTA +nodemcu.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +nodemcu.menu.lvl.OOM=OOM +nodemcu.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +nodemcu.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOM=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM +nodemcu.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOM.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM +nodemcu.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM +nodemcu.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM +nodemcu.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +nodemcu.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +nodemcu.menu.wipe.none=Only Sketch +nodemcu.menu.wipe.none.upload.erase_cmd= +nodemcu.menu.wipe.sdk=Sketch + WiFi Settings +nodemcu.menu.wipe.sdk.upload.erase_cmd=-ca "{build.rfcal_addr}" -cz 0x4000 +nodemcu.menu.wipe.all=All Flash Contents +nodemcu.menu.wipe.all.upload.erase_cmd=-ca 0x0 -cz "{build.flash_size_bytes}" +nodemcu.menu.baud.115200=115200 +nodemcu.menu.baud.115200.upload.speed=115200 +nodemcu.menu.baud.9600=9600 +nodemcu.menu.baud.9600.upload.speed=9600 +nodemcu.menu.baud.57600=57600 +nodemcu.menu.baud.57600.upload.speed=57600 +nodemcu.menu.baud.230400.linux=230400 +nodemcu.menu.baud.230400.macosx=230400 +nodemcu.menu.baud.230400.upload.speed=230400 +nodemcu.menu.baud.256000.windows=256000 +nodemcu.menu.baud.256000.upload.speed=256000 +nodemcu.menu.baud.460800.linux=460800 +nodemcu.menu.baud.460800.macosx=460800 +nodemcu.menu.baud.460800.upload.speed=460800 +nodemcu.menu.baud.512000.windows=512000 +nodemcu.menu.baud.512000.upload.speed=512000 +nodemcu.menu.baud.921600=921600 +nodemcu.menu.baud.921600.upload.speed=921600 + +############################################################## +nodemcuv2.name=NodeMCU 1.0 (ESP-12E Module) +nodemcuv2.build.board=ESP8266_NODEMCU +nodemcuv2.build.variant=nodemcu +nodemcuv2.upload.tool=esptool +nodemcuv2.upload.maximum_data_size=81920 +nodemcuv2.upload.wait_for_upload_port=true +nodemcuv2.upload.erase_cmd= +nodemcuv2.serial.disableDTR=true +nodemcuv2.serial.disableRTS=true +nodemcuv2.build.mcu=esp8266 +nodemcuv2.build.core=esp8266 +nodemcuv2.build.spiffs_pagesize=256 +nodemcuv2.build.debug_port= +nodemcuv2.build.debug_level= +nodemcuv2.menu.xtal.80=80 MHz +nodemcuv2.menu.xtal.80.build.f_cpu=80000000L +nodemcuv2.menu.xtal.160=160 MHz +nodemcuv2.menu.xtal.160.build.f_cpu=160000000L +nodemcuv2.menu.vt.flash=Flash +nodemcuv2.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +nodemcuv2.menu.vt.heap=Heap +nodemcuv2.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +nodemcuv2.menu.vt.iram=IRAM +nodemcuv2.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +nodemcuv2.menu.exception.enabled=Enabled +nodemcuv2.menu.exception.enabled.build.exception_flags=-fexceptions +nodemcuv2.menu.exception.enabled.build.stdcpp_lib=-lstdc++ +nodemcuv2.menu.exception.disabled=Disabled +nodemcuv2.menu.exception.disabled.build.exception_flags=-fno-exceptions +nodemcuv2.menu.exception.disabled.build.stdcpp_lib=-lstdc++-nox +nodemcuv2.upload.resetmethod=nodemcu +nodemcuv2.build.flash_mode=dio +nodemcuv2.build.flash_freq=40 +nodemcuv2.menu.eesz.4M=4M (no SPIFFS) +nodemcuv2.menu.eesz.4M.build.flash_size=4M +nodemcuv2.menu.eesz.4M.build.flash_size_bytes=0x400000 +nodemcuv2.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld +nodemcuv2.menu.eesz.4M.build.spiffs_pagesize=256 +nodemcuv2.menu.eesz.4M.upload.maximum_size=1044464 +nodemcuv2.menu.eesz.4M.build.rfcal_addr=0x3FC000 +nodemcuv2.menu.eesz.4M1M=4M (1M SPIFFS) +nodemcuv2.menu.eesz.4M1M.build.flash_size=4M +nodemcuv2.menu.eesz.4M1M.build.flash_size_bytes=0x400000 +nodemcuv2.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld +nodemcuv2.menu.eesz.4M1M.build.spiffs_pagesize=256 +nodemcuv2.menu.eesz.4M1M.upload.maximum_size=1044464 +nodemcuv2.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 +nodemcuv2.menu.eesz.4M1M.build.spiffs_start=0x300000 +nodemcuv2.menu.eesz.4M1M.build.spiffs_end=0x3FB000 +nodemcuv2.menu.eesz.4M1M.build.spiffs_blocksize=8192 +nodemcuv2.menu.eesz.4M2M=4M (2M SPIFFS) +nodemcuv2.menu.eesz.4M2M.build.flash_size=4M +nodemcuv2.menu.eesz.4M2M.build.flash_size_bytes=0x400000 +nodemcuv2.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld +nodemcuv2.menu.eesz.4M2M.build.spiffs_pagesize=256 +nodemcuv2.menu.eesz.4M2M.upload.maximum_size=1044464 +nodemcuv2.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 +nodemcuv2.menu.eesz.4M2M.build.spiffs_start=0x200000 +nodemcuv2.menu.eesz.4M2M.build.spiffs_end=0x3FB000 +nodemcuv2.menu.eesz.4M2M.build.spiffs_blocksize=8192 +nodemcuv2.menu.eesz.4M3M=4M (3M SPIFFS) +nodemcuv2.menu.eesz.4M3M.build.flash_size=4M +nodemcuv2.menu.eesz.4M3M.build.flash_size_bytes=0x400000 +nodemcuv2.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld +nodemcuv2.menu.eesz.4M3M.build.spiffs_pagesize=256 +nodemcuv2.menu.eesz.4M3M.upload.maximum_size=1044464 +nodemcuv2.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 +nodemcuv2.menu.eesz.4M3M.build.spiffs_start=0x100000 +nodemcuv2.menu.eesz.4M3M.build.spiffs_end=0x3FB000 +nodemcuv2.menu.eesz.4M3M.build.spiffs_blocksize=8192 +nodemcuv2.menu.ip.lm2f=v2 Lower Memory +nodemcuv2.menu.ip.lm2f.build.lwip_include=lwip2/include +nodemcuv2.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +nodemcuv2.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +nodemcuv2.menu.ip.hb2f=v2 Higher Bandwidth +nodemcuv2.menu.ip.hb2f.build.lwip_include=lwip2/include +nodemcuv2.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +nodemcuv2.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +nodemcuv2.menu.ip.lm2n=v2 Lower Memory (no features) +nodemcuv2.menu.ip.lm2n.build.lwip_include=lwip2/include +nodemcuv2.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +nodemcuv2.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +nodemcuv2.menu.ip.hb2n=v2 Higher Bandwidth (no features) +nodemcuv2.menu.ip.hb2n.build.lwip_include=lwip2/include +nodemcuv2.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +nodemcuv2.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +nodemcuv2.menu.ip.lm6f=v2 IPv6 Lower Memory +nodemcuv2.menu.ip.lm6f.build.lwip_include=lwip2/include +nodemcuv2.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +nodemcuv2.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +nodemcuv2.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +nodemcuv2.menu.ip.hb6f.build.lwip_include=lwip2/include +nodemcuv2.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +nodemcuv2.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +nodemcuv2.menu.ip.hb1=v1.4 Higher Bandwidth +nodemcuv2.menu.ip.hb1.build.lwip_lib=-llwip_gcc +nodemcuv2.menu.ip.hb1.build.lwip_flags=-DLWIP_OPEN_SRC +nodemcuv2.menu.ip.src=v1.4 Compile from source +nodemcuv2.menu.ip.src.build.lwip_lib=-llwip_src +nodemcuv2.menu.ip.src.build.lwip_flags=-DLWIP_OPEN_SRC +nodemcuv2.menu.ip.src.recipe.hooks.sketch.prebuild.1.pattern=make -C "{runtime.platform.path}/tools/sdk/lwip/src" install TOOLS_PATH="{runtime.tools.xtensa-lx106-elf-gcc.path}/bin/xtensa-lx106-elf-" +nodemcuv2.menu.dbg.Disabled=Disabled +nodemcuv2.menu.dbg.Disabled.build.debug_port= +nodemcuv2.menu.dbg.Serial=Serial +nodemcuv2.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +nodemcuv2.menu.dbg.Serial1=Serial1 +nodemcuv2.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +nodemcuv2.menu.lvl.None____=None +nodemcuv2.menu.lvl.None____.build.debug_level= +nodemcuv2.menu.lvl.SSL=SSL +nodemcuv2.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +nodemcuv2.menu.lvl.TLS_MEM=TLS_MEM +nodemcuv2.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +nodemcuv2.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +nodemcuv2.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +nodemcuv2.menu.lvl.HTTP_SERVER=HTTP_SERVER +nodemcuv2.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +nodemcuv2.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +nodemcuv2.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +nodemcuv2.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +nodemcuv2.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +nodemcuv2.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +nodemcuv2.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +nodemcuv2.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +nodemcuv2.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +nodemcuv2.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +nodemcuv2.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +nodemcuv2.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +nodemcuv2.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +nodemcuv2.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +nodemcuv2.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +nodemcuv2.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +nodemcuv2.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +nodemcuv2.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +nodemcuv2.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +nodemcuv2.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +nodemcuv2.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +nodemcuv2.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +nodemcuv2.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +nodemcuv2.menu.lvl.CORE=CORE +nodemcuv2.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +nodemcuv2.menu.lvl.WIFI=WIFI +nodemcuv2.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +nodemcuv2.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +nodemcuv2.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +nodemcuv2.menu.lvl.UPDATER=UPDATER +nodemcuv2.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +nodemcuv2.menu.lvl.OTA=OTA +nodemcuv2.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +nodemcuv2.menu.lvl.OOM=OOM +nodemcuv2.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +nodemcuv2.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOM=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM +nodemcuv2.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOM.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM +nodemcuv2.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM +nodemcuv2.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM +nodemcuv2.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +nodemcuv2.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +nodemcuv2.menu.wipe.none=Only Sketch +nodemcuv2.menu.wipe.none.upload.erase_cmd= +nodemcuv2.menu.wipe.sdk=Sketch + WiFi Settings +nodemcuv2.menu.wipe.sdk.upload.erase_cmd=-ca "{build.rfcal_addr}" -cz 0x4000 +nodemcuv2.menu.wipe.all=All Flash Contents +nodemcuv2.menu.wipe.all.upload.erase_cmd=-ca 0x0 -cz "{build.flash_size_bytes}" +nodemcuv2.menu.baud.115200=115200 +nodemcuv2.menu.baud.115200.upload.speed=115200 +nodemcuv2.menu.baud.9600=9600 +nodemcuv2.menu.baud.9600.upload.speed=9600 +nodemcuv2.menu.baud.57600=57600 +nodemcuv2.menu.baud.57600.upload.speed=57600 +nodemcuv2.menu.baud.230400.linux=230400 +nodemcuv2.menu.baud.230400.macosx=230400 +nodemcuv2.menu.baud.230400.upload.speed=230400 +nodemcuv2.menu.baud.256000.windows=256000 +nodemcuv2.menu.baud.256000.upload.speed=256000 +nodemcuv2.menu.baud.460800.linux=460800 +nodemcuv2.menu.baud.460800.macosx=460800 +nodemcuv2.menu.baud.460800.upload.speed=460800 +nodemcuv2.menu.baud.512000.windows=512000 +nodemcuv2.menu.baud.512000.upload.speed=512000 +nodemcuv2.menu.baud.921600=921600 +nodemcuv2.menu.baud.921600.upload.speed=921600 + +############################################################## +modwifi.name=Olimex MOD-WIFI-ESP8266(-DEV) +modwifi.build.board=MOD_WIFI_ESP8266 +modwifi.build.variant=modwifi +modwifi.upload.tool=esptool +modwifi.upload.maximum_data_size=81920 +modwifi.upload.wait_for_upload_port=true +modwifi.upload.erase_cmd= +modwifi.serial.disableDTR=true +modwifi.serial.disableRTS=true +modwifi.build.mcu=esp8266 +modwifi.build.core=esp8266 +modwifi.build.spiffs_pagesize=256 +modwifi.build.debug_port= +modwifi.build.debug_level= +modwifi.menu.xtal.80=80 MHz +modwifi.menu.xtal.80.build.f_cpu=80000000L +modwifi.menu.xtal.160=160 MHz +modwifi.menu.xtal.160.build.f_cpu=160000000L +modwifi.menu.vt.flash=Flash +modwifi.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +modwifi.menu.vt.heap=Heap +modwifi.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +modwifi.menu.vt.iram=IRAM +modwifi.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +modwifi.menu.exception.enabled=Enabled +modwifi.menu.exception.enabled.build.exception_flags=-fexceptions +modwifi.menu.exception.enabled.build.stdcpp_lib=-lstdc++ +modwifi.menu.exception.disabled=Disabled +modwifi.menu.exception.disabled.build.exception_flags=-fno-exceptions +modwifi.menu.exception.disabled.build.stdcpp_lib=-lstdc++-nox +modwifi.upload.resetmethod=ck +modwifi.build.flash_mode=qio +modwifi.build.flash_freq=40 +modwifi.menu.eesz.2M=2M (no SPIFFS) +modwifi.menu.eesz.2M.build.flash_size=2M +modwifi.menu.eesz.2M.build.flash_size_bytes=0x200000 +modwifi.menu.eesz.2M.build.flash_ld=eagle.flash.2m.ld +modwifi.menu.eesz.2M.build.spiffs_pagesize=256 +modwifi.menu.eesz.2M.upload.maximum_size=1044464 +modwifi.menu.eesz.2M.build.rfcal_addr=0x1FC000 +modwifi.menu.eesz.2M128=2M (128K SPIFFS) +modwifi.menu.eesz.2M128.build.flash_size=2M +modwifi.menu.eesz.2M128.build.flash_size_bytes=0x200000 +modwifi.menu.eesz.2M128.build.flash_ld=eagle.flash.2m128.ld +modwifi.menu.eesz.2M128.build.spiffs_pagesize=256 +modwifi.menu.eesz.2M128.upload.maximum_size=1044464 +modwifi.menu.eesz.2M128.build.rfcal_addr=0x1FC000 +modwifi.menu.eesz.2M128.build.spiffs_start=0x1E0000 +modwifi.menu.eesz.2M128.build.spiffs_end=0x1FB000 +modwifi.menu.eesz.2M128.build.spiffs_blocksize=4096 +modwifi.menu.eesz.2M256=2M (256K SPIFFS) +modwifi.menu.eesz.2M256.build.flash_size=2M +modwifi.menu.eesz.2M256.build.flash_size_bytes=0x200000 +modwifi.menu.eesz.2M256.build.flash_ld=eagle.flash.2m256.ld +modwifi.menu.eesz.2M256.build.spiffs_pagesize=256 +modwifi.menu.eesz.2M256.upload.maximum_size=1044464 +modwifi.menu.eesz.2M256.build.rfcal_addr=0x1FC000 +modwifi.menu.eesz.2M256.build.spiffs_start=0x1C0000 +modwifi.menu.eesz.2M256.build.spiffs_end=0x1FB000 +modwifi.menu.eesz.2M256.build.spiffs_blocksize=4096 +modwifi.menu.eesz.2M512=2M (512K SPIFFS) +modwifi.menu.eesz.2M512.build.flash_size=2M +modwifi.menu.eesz.2M512.build.flash_size_bytes=0x200000 +modwifi.menu.eesz.2M512.build.flash_ld=eagle.flash.2m512.ld +modwifi.menu.eesz.2M512.build.spiffs_pagesize=256 +modwifi.menu.eesz.2M512.upload.maximum_size=1044464 +modwifi.menu.eesz.2M512.build.rfcal_addr=0x1FC000 +modwifi.menu.eesz.2M512.build.spiffs_start=0x180000 +modwifi.menu.eesz.2M512.build.spiffs_end=0x1FB000 +modwifi.menu.eesz.2M512.build.spiffs_blocksize=8192 +modwifi.menu.eesz.2M1M=2M (1M SPIFFS) +modwifi.menu.eesz.2M1M.build.flash_size=2M +modwifi.menu.eesz.2M1M.build.flash_size_bytes=0x200000 +modwifi.menu.eesz.2M1M.build.flash_ld=eagle.flash.2m1m.ld +modwifi.menu.eesz.2M1M.build.spiffs_pagesize=256 +modwifi.menu.eesz.2M1M.upload.maximum_size=1044464 +modwifi.menu.eesz.2M1M.build.rfcal_addr=0x1FC000 +modwifi.menu.eesz.2M1M.build.spiffs_start=0x100000 +modwifi.menu.eesz.2M1M.build.spiffs_end=0x1FB000 +modwifi.menu.eesz.2M1M.build.spiffs_blocksize=8192 +modwifi.menu.ip.lm2f=v2 Lower Memory +modwifi.menu.ip.lm2f.build.lwip_include=lwip2/include +modwifi.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +modwifi.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +modwifi.menu.ip.hb2f=v2 Higher Bandwidth +modwifi.menu.ip.hb2f.build.lwip_include=lwip2/include +modwifi.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +modwifi.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +modwifi.menu.ip.lm2n=v2 Lower Memory (no features) +modwifi.menu.ip.lm2n.build.lwip_include=lwip2/include +modwifi.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +modwifi.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +modwifi.menu.ip.hb2n=v2 Higher Bandwidth (no features) +modwifi.menu.ip.hb2n.build.lwip_include=lwip2/include +modwifi.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +modwifi.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +modwifi.menu.ip.lm6f=v2 IPv6 Lower Memory +modwifi.menu.ip.lm6f.build.lwip_include=lwip2/include +modwifi.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +modwifi.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +modwifi.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +modwifi.menu.ip.hb6f.build.lwip_include=lwip2/include +modwifi.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +modwifi.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +modwifi.menu.ip.hb1=v1.4 Higher Bandwidth +modwifi.menu.ip.hb1.build.lwip_lib=-llwip_gcc +modwifi.menu.ip.hb1.build.lwip_flags=-DLWIP_OPEN_SRC +modwifi.menu.ip.src=v1.4 Compile from source +modwifi.menu.ip.src.build.lwip_lib=-llwip_src +modwifi.menu.ip.src.build.lwip_flags=-DLWIP_OPEN_SRC +modwifi.menu.ip.src.recipe.hooks.sketch.prebuild.1.pattern=make -C "{runtime.platform.path}/tools/sdk/lwip/src" install TOOLS_PATH="{runtime.tools.xtensa-lx106-elf-gcc.path}/bin/xtensa-lx106-elf-" +modwifi.menu.dbg.Disabled=Disabled +modwifi.menu.dbg.Disabled.build.debug_port= +modwifi.menu.dbg.Serial=Serial +modwifi.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +modwifi.menu.dbg.Serial1=Serial1 +modwifi.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +modwifi.menu.lvl.None____=None +modwifi.menu.lvl.None____.build.debug_level= +modwifi.menu.lvl.SSL=SSL +modwifi.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +modwifi.menu.lvl.TLS_MEM=TLS_MEM +modwifi.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +modwifi.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +modwifi.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +modwifi.menu.lvl.HTTP_SERVER=HTTP_SERVER +modwifi.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +modwifi.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +modwifi.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +modwifi.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +modwifi.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +modwifi.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +modwifi.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +modwifi.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +modwifi.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +modwifi.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +modwifi.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +modwifi.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +modwifi.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +modwifi.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +modwifi.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +modwifi.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +modwifi.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +modwifi.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +modwifi.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +modwifi.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +modwifi.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +modwifi.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +modwifi.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +modwifi.menu.lvl.CORE=CORE +modwifi.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +modwifi.menu.lvl.WIFI=WIFI +modwifi.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +modwifi.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +modwifi.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +modwifi.menu.lvl.UPDATER=UPDATER +modwifi.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +modwifi.menu.lvl.OTA=OTA +modwifi.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +modwifi.menu.lvl.OOM=OOM +modwifi.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +modwifi.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOM=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM +modwifi.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOM.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM +modwifi.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM +modwifi.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM +modwifi.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +modwifi.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +modwifi.menu.wipe.none=Only Sketch +modwifi.menu.wipe.none.upload.erase_cmd= +modwifi.menu.wipe.sdk=Sketch + WiFi Settings +modwifi.menu.wipe.sdk.upload.erase_cmd=-ca "{build.rfcal_addr}" -cz 0x4000 +modwifi.menu.wipe.all=All Flash Contents +modwifi.menu.wipe.all.upload.erase_cmd=-ca 0x0 -cz "{build.flash_size_bytes}" +modwifi.menu.baud.115200=115200 +modwifi.menu.baud.115200.upload.speed=115200 +modwifi.menu.baud.9600=9600 +modwifi.menu.baud.9600.upload.speed=9600 +modwifi.menu.baud.57600=57600 +modwifi.menu.baud.57600.upload.speed=57600 +modwifi.menu.baud.230400.linux=230400 +modwifi.menu.baud.230400.macosx=230400 +modwifi.menu.baud.230400.upload.speed=230400 +modwifi.menu.baud.256000.windows=256000 +modwifi.menu.baud.256000.upload.speed=256000 +modwifi.menu.baud.460800.linux=460800 +modwifi.menu.baud.460800.macosx=460800 +modwifi.menu.baud.460800.upload.speed=460800 +modwifi.menu.baud.512000.windows=512000 +modwifi.menu.baud.512000.upload.speed=512000 +modwifi.menu.baud.921600=921600 +modwifi.menu.baud.921600.upload.speed=921600 + +############################################################## +thing.name=SparkFun ESP8266 Thing +thing.build.board=ESP8266_THING +thing.build.variant=thing +thing.upload.tool=esptool +thing.upload.maximum_data_size=81920 +thing.upload.wait_for_upload_port=true +thing.upload.erase_cmd= +thing.serial.disableDTR=true +thing.serial.disableRTS=true +thing.build.mcu=esp8266 +thing.build.core=esp8266 +thing.build.spiffs_pagesize=256 +thing.build.debug_port= +thing.build.debug_level= +thing.menu.xtal.80=80 MHz +thing.menu.xtal.80.build.f_cpu=80000000L +thing.menu.xtal.160=160 MHz +thing.menu.xtal.160.build.f_cpu=160000000L +thing.menu.vt.flash=Flash +thing.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +thing.menu.vt.heap=Heap +thing.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +thing.menu.vt.iram=IRAM +thing.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +thing.menu.exception.enabled=Enabled +thing.menu.exception.enabled.build.exception_flags=-fexceptions +thing.menu.exception.enabled.build.stdcpp_lib=-lstdc++ +thing.menu.exception.disabled=Disabled +thing.menu.exception.disabled.build.exception_flags=-fno-exceptions +thing.menu.exception.disabled.build.stdcpp_lib=-lstdc++-nox +thing.upload.resetmethod=ck +thing.build.flash_mode=qio +thing.build.flash_freq=40 +thing.menu.eesz.512K=512K (no SPIFFS) +thing.menu.eesz.512K.build.flash_size=512K +thing.menu.eesz.512K.build.flash_size_bytes=0x80000 +thing.menu.eesz.512K.build.flash_ld=eagle.flash.512k.ld +thing.menu.eesz.512K.build.spiffs_pagesize=256 +thing.menu.eesz.512K.upload.maximum_size=499696 +thing.menu.eesz.512K.build.rfcal_addr=0x7C000 +thing.menu.eesz.512K32=512K (32K SPIFFS) +thing.menu.eesz.512K32.build.flash_size=512K +thing.menu.eesz.512K32.build.flash_size_bytes=0x80000 +thing.menu.eesz.512K32.build.flash_ld=eagle.flash.512k32.ld +thing.menu.eesz.512K32.build.spiffs_pagesize=256 +thing.menu.eesz.512K32.upload.maximum_size=466928 +thing.menu.eesz.512K32.build.rfcal_addr=0x7C000 +thing.menu.eesz.512K32.build.spiffs_start=0x73000 +thing.menu.eesz.512K32.build.spiffs_end=0x7B000 +thing.menu.eesz.512K32.build.spiffs_blocksize=4096 +thing.menu.eesz.512K64=512K (64K SPIFFS) +thing.menu.eesz.512K64.build.flash_size=512K +thing.menu.eesz.512K64.build.flash_size_bytes=0x80000 +thing.menu.eesz.512K64.build.flash_ld=eagle.flash.512k64.ld +thing.menu.eesz.512K64.build.spiffs_pagesize=256 +thing.menu.eesz.512K64.upload.maximum_size=434160 +thing.menu.eesz.512K64.build.rfcal_addr=0x7C000 +thing.menu.eesz.512K64.build.spiffs_start=0x6B000 +thing.menu.eesz.512K64.build.spiffs_end=0x7B000 +thing.menu.eesz.512K64.build.spiffs_blocksize=4096 +thing.menu.eesz.512K128=512K (128K SPIFFS) +thing.menu.eesz.512K128.build.flash_size=512K +thing.menu.eesz.512K128.build.flash_size_bytes=0x80000 +thing.menu.eesz.512K128.build.flash_ld=eagle.flash.512k128.ld +thing.menu.eesz.512K128.build.spiffs_pagesize=256 +thing.menu.eesz.512K128.upload.maximum_size=368624 +thing.menu.eesz.512K128.build.rfcal_addr=0x7C000 +thing.menu.eesz.512K128.build.spiffs_start=0x5B000 +thing.menu.eesz.512K128.build.spiffs_end=0x7B000 +thing.menu.eesz.512K128.build.spiffs_blocksize=4096 +thing.menu.ip.lm2f=v2 Lower Memory +thing.menu.ip.lm2f.build.lwip_include=lwip2/include +thing.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +thing.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +thing.menu.ip.hb2f=v2 Higher Bandwidth +thing.menu.ip.hb2f.build.lwip_include=lwip2/include +thing.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +thing.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +thing.menu.ip.lm2n=v2 Lower Memory (no features) +thing.menu.ip.lm2n.build.lwip_include=lwip2/include +thing.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +thing.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +thing.menu.ip.hb2n=v2 Higher Bandwidth (no features) +thing.menu.ip.hb2n.build.lwip_include=lwip2/include +thing.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +thing.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +thing.menu.ip.lm6f=v2 IPv6 Lower Memory +thing.menu.ip.lm6f.build.lwip_include=lwip2/include +thing.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +thing.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +thing.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +thing.menu.ip.hb6f.build.lwip_include=lwip2/include +thing.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +thing.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +thing.menu.ip.hb1=v1.4 Higher Bandwidth +thing.menu.ip.hb1.build.lwip_lib=-llwip_gcc +thing.menu.ip.hb1.build.lwip_flags=-DLWIP_OPEN_SRC +thing.menu.ip.src=v1.4 Compile from source +thing.menu.ip.src.build.lwip_lib=-llwip_src +thing.menu.ip.src.build.lwip_flags=-DLWIP_OPEN_SRC +thing.menu.ip.src.recipe.hooks.sketch.prebuild.1.pattern=make -C "{runtime.platform.path}/tools/sdk/lwip/src" install TOOLS_PATH="{runtime.tools.xtensa-lx106-elf-gcc.path}/bin/xtensa-lx106-elf-" +thing.menu.dbg.Disabled=Disabled +thing.menu.dbg.Disabled.build.debug_port= +thing.menu.dbg.Serial=Serial +thing.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +thing.menu.dbg.Serial1=Serial1 +thing.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +thing.menu.lvl.None____=None +thing.menu.lvl.None____.build.debug_level= +thing.menu.lvl.SSL=SSL +thing.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +thing.menu.lvl.TLS_MEM=TLS_MEM +thing.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +thing.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +thing.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +thing.menu.lvl.HTTP_SERVER=HTTP_SERVER +thing.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +thing.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +thing.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +thing.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +thing.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +thing.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +thing.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +thing.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +thing.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +thing.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +thing.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +thing.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +thing.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +thing.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +thing.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +thing.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +thing.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +thing.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +thing.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +thing.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +thing.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +thing.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +thing.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +thing.menu.lvl.CORE=CORE +thing.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +thing.menu.lvl.WIFI=WIFI +thing.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +thing.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +thing.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +thing.menu.lvl.UPDATER=UPDATER +thing.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +thing.menu.lvl.OTA=OTA +thing.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +thing.menu.lvl.OOM=OOM +thing.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +thing.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOM=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM +thing.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOM.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM +thing.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM +thing.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM +thing.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +thing.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +thing.menu.wipe.none=Only Sketch +thing.menu.wipe.none.upload.erase_cmd= +thing.menu.wipe.sdk=Sketch + WiFi Settings +thing.menu.wipe.sdk.upload.erase_cmd=-ca "{build.rfcal_addr}" -cz 0x4000 +thing.menu.wipe.all=All Flash Contents +thing.menu.wipe.all.upload.erase_cmd=-ca 0x0 -cz "{build.flash_size_bytes}" +thing.menu.baud.115200=115200 +thing.menu.baud.115200.upload.speed=115200 +thing.menu.baud.9600=9600 +thing.menu.baud.9600.upload.speed=9600 +thing.menu.baud.57600=57600 +thing.menu.baud.57600.upload.speed=57600 +thing.menu.baud.230400.linux=230400 +thing.menu.baud.230400.macosx=230400 +thing.menu.baud.230400.upload.speed=230400 +thing.menu.baud.256000.windows=256000 +thing.menu.baud.256000.upload.speed=256000 +thing.menu.baud.460800.linux=460800 +thing.menu.baud.460800.macosx=460800 +thing.menu.baud.460800.upload.speed=460800 +thing.menu.baud.512000.windows=512000 +thing.menu.baud.512000.upload.speed=512000 +thing.menu.baud.921600=921600 +thing.menu.baud.921600.upload.speed=921600 + +############################################################## +thingdev.name=SparkFun ESP8266 Thing Dev +thingdev.build.board=ESP8266_THING_DEV +thingdev.build.variant=thing +thingdev.upload.tool=esptool +thingdev.upload.maximum_data_size=81920 +thingdev.upload.wait_for_upload_port=true +thingdev.upload.erase_cmd= +thingdev.serial.disableDTR=true +thingdev.serial.disableRTS=true +thingdev.build.mcu=esp8266 +thingdev.build.core=esp8266 +thingdev.build.spiffs_pagesize=256 +thingdev.build.debug_port= +thingdev.build.debug_level= +thingdev.menu.xtal.80=80 MHz +thingdev.menu.xtal.80.build.f_cpu=80000000L +thingdev.menu.xtal.160=160 MHz +thingdev.menu.xtal.160.build.f_cpu=160000000L +thingdev.menu.vt.flash=Flash +thingdev.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +thingdev.menu.vt.heap=Heap +thingdev.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +thingdev.menu.vt.iram=IRAM +thingdev.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +thingdev.menu.exception.enabled=Enabled +thingdev.menu.exception.enabled.build.exception_flags=-fexceptions +thingdev.menu.exception.enabled.build.stdcpp_lib=-lstdc++ +thingdev.menu.exception.disabled=Disabled +thingdev.menu.exception.disabled.build.exception_flags=-fno-exceptions +thingdev.menu.exception.disabled.build.stdcpp_lib=-lstdc++-nox +thingdev.upload.resetmethod=nodemcu +thingdev.build.flash_mode=dio +thingdev.build.flash_freq=40 +thingdev.menu.eesz.512K=512K (no SPIFFS) +thingdev.menu.eesz.512K.build.flash_size=512K +thingdev.menu.eesz.512K.build.flash_size_bytes=0x80000 +thingdev.menu.eesz.512K.build.flash_ld=eagle.flash.512k.ld +thingdev.menu.eesz.512K.build.spiffs_pagesize=256 +thingdev.menu.eesz.512K.upload.maximum_size=499696 +thingdev.menu.eesz.512K.build.rfcal_addr=0x7C000 +thingdev.menu.eesz.512K32=512K (32K SPIFFS) +thingdev.menu.eesz.512K32.build.flash_size=512K +thingdev.menu.eesz.512K32.build.flash_size_bytes=0x80000 +thingdev.menu.eesz.512K32.build.flash_ld=eagle.flash.512k32.ld +thingdev.menu.eesz.512K32.build.spiffs_pagesize=256 +thingdev.menu.eesz.512K32.upload.maximum_size=466928 +thingdev.menu.eesz.512K32.build.rfcal_addr=0x7C000 +thingdev.menu.eesz.512K32.build.spiffs_start=0x73000 +thingdev.menu.eesz.512K32.build.spiffs_end=0x7B000 +thingdev.menu.eesz.512K32.build.spiffs_blocksize=4096 +thingdev.menu.eesz.512K64=512K (64K SPIFFS) +thingdev.menu.eesz.512K64.build.flash_size=512K +thingdev.menu.eesz.512K64.build.flash_size_bytes=0x80000 +thingdev.menu.eesz.512K64.build.flash_ld=eagle.flash.512k64.ld +thingdev.menu.eesz.512K64.build.spiffs_pagesize=256 +thingdev.menu.eesz.512K64.upload.maximum_size=434160 +thingdev.menu.eesz.512K64.build.rfcal_addr=0x7C000 +thingdev.menu.eesz.512K64.build.spiffs_start=0x6B000 +thingdev.menu.eesz.512K64.build.spiffs_end=0x7B000 +thingdev.menu.eesz.512K64.build.spiffs_blocksize=4096 +thingdev.menu.eesz.512K128=512K (128K SPIFFS) +thingdev.menu.eesz.512K128.build.flash_size=512K +thingdev.menu.eesz.512K128.build.flash_size_bytes=0x80000 +thingdev.menu.eesz.512K128.build.flash_ld=eagle.flash.512k128.ld +thingdev.menu.eesz.512K128.build.spiffs_pagesize=256 +thingdev.menu.eesz.512K128.upload.maximum_size=368624 +thingdev.menu.eesz.512K128.build.rfcal_addr=0x7C000 +thingdev.menu.eesz.512K128.build.spiffs_start=0x5B000 +thingdev.menu.eesz.512K128.build.spiffs_end=0x7B000 +thingdev.menu.eesz.512K128.build.spiffs_blocksize=4096 +thingdev.menu.ip.lm2f=v2 Lower Memory +thingdev.menu.ip.lm2f.build.lwip_include=lwip2/include +thingdev.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +thingdev.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +thingdev.menu.ip.hb2f=v2 Higher Bandwidth +thingdev.menu.ip.hb2f.build.lwip_include=lwip2/include +thingdev.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +thingdev.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +thingdev.menu.ip.lm2n=v2 Lower Memory (no features) +thingdev.menu.ip.lm2n.build.lwip_include=lwip2/include +thingdev.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +thingdev.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +thingdev.menu.ip.hb2n=v2 Higher Bandwidth (no features) +thingdev.menu.ip.hb2n.build.lwip_include=lwip2/include +thingdev.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +thingdev.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +thingdev.menu.ip.lm6f=v2 IPv6 Lower Memory +thingdev.menu.ip.lm6f.build.lwip_include=lwip2/include +thingdev.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +thingdev.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +thingdev.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +thingdev.menu.ip.hb6f.build.lwip_include=lwip2/include +thingdev.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +thingdev.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +thingdev.menu.ip.hb1=v1.4 Higher Bandwidth +thingdev.menu.ip.hb1.build.lwip_lib=-llwip_gcc +thingdev.menu.ip.hb1.build.lwip_flags=-DLWIP_OPEN_SRC +thingdev.menu.ip.src=v1.4 Compile from source +thingdev.menu.ip.src.build.lwip_lib=-llwip_src +thingdev.menu.ip.src.build.lwip_flags=-DLWIP_OPEN_SRC +thingdev.menu.ip.src.recipe.hooks.sketch.prebuild.1.pattern=make -C "{runtime.platform.path}/tools/sdk/lwip/src" install TOOLS_PATH="{runtime.tools.xtensa-lx106-elf-gcc.path}/bin/xtensa-lx106-elf-" +thingdev.menu.dbg.Disabled=Disabled +thingdev.menu.dbg.Disabled.build.debug_port= +thingdev.menu.dbg.Serial=Serial +thingdev.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +thingdev.menu.dbg.Serial1=Serial1 +thingdev.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +thingdev.menu.lvl.None____=None +thingdev.menu.lvl.None____.build.debug_level= +thingdev.menu.lvl.SSL=SSL +thingdev.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +thingdev.menu.lvl.TLS_MEM=TLS_MEM +thingdev.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +thingdev.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +thingdev.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +thingdev.menu.lvl.HTTP_SERVER=HTTP_SERVER +thingdev.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +thingdev.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +thingdev.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +thingdev.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +thingdev.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +thingdev.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +thingdev.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +thingdev.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +thingdev.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +thingdev.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +thingdev.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +thingdev.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +thingdev.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +thingdev.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +thingdev.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +thingdev.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +thingdev.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +thingdev.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +thingdev.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +thingdev.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +thingdev.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +thingdev.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +thingdev.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +thingdev.menu.lvl.CORE=CORE +thingdev.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +thingdev.menu.lvl.WIFI=WIFI +thingdev.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +thingdev.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +thingdev.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +thingdev.menu.lvl.UPDATER=UPDATER +thingdev.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +thingdev.menu.lvl.OTA=OTA +thingdev.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +thingdev.menu.lvl.OOM=OOM +thingdev.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +thingdev.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOM=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM +thingdev.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOM.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM +thingdev.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM +thingdev.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM +thingdev.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +thingdev.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +thingdev.menu.wipe.none=Only Sketch +thingdev.menu.wipe.none.upload.erase_cmd= +thingdev.menu.wipe.sdk=Sketch + WiFi Settings +thingdev.menu.wipe.sdk.upload.erase_cmd=-ca "{build.rfcal_addr}" -cz 0x4000 +thingdev.menu.wipe.all=All Flash Contents +thingdev.menu.wipe.all.upload.erase_cmd=-ca 0x0 -cz "{build.flash_size_bytes}" +thingdev.menu.baud.115200=115200 +thingdev.menu.baud.115200.upload.speed=115200 +thingdev.menu.baud.9600=9600 +thingdev.menu.baud.9600.upload.speed=9600 +thingdev.menu.baud.57600=57600 +thingdev.menu.baud.57600.upload.speed=57600 +thingdev.menu.baud.230400.linux=230400 +thingdev.menu.baud.230400.macosx=230400 +thingdev.menu.baud.230400.upload.speed=230400 +thingdev.menu.baud.256000.windows=256000 +thingdev.menu.baud.256000.upload.speed=256000 +thingdev.menu.baud.460800.linux=460800 +thingdev.menu.baud.460800.macosx=460800 +thingdev.menu.baud.460800.upload.speed=460800 +thingdev.menu.baud.512000.windows=512000 +thingdev.menu.baud.512000.upload.speed=512000 +thingdev.menu.baud.921600=921600 +thingdev.menu.baud.921600.upload.speed=921600 + +############################################################## +esp210.name=SweetPea ESP-210 +esp210.build.board=ESP8266_ESP210 +esp210.upload.tool=esptool +esp210.upload.maximum_data_size=81920 +esp210.upload.wait_for_upload_port=true +esp210.upload.erase_cmd= +esp210.serial.disableDTR=true +esp210.serial.disableRTS=true +esp210.build.mcu=esp8266 +esp210.build.core=esp8266 +esp210.build.variant=generic +esp210.build.spiffs_pagesize=256 +esp210.build.debug_port= +esp210.build.debug_level= +esp210.menu.xtal.80=80 MHz +esp210.menu.xtal.80.build.f_cpu=80000000L +esp210.menu.xtal.160=160 MHz +esp210.menu.xtal.160.build.f_cpu=160000000L +esp210.menu.vt.flash=Flash +esp210.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +esp210.menu.vt.heap=Heap +esp210.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +esp210.menu.vt.iram=IRAM +esp210.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +esp210.menu.exception.enabled=Enabled +esp210.menu.exception.enabled.build.exception_flags=-fexceptions +esp210.menu.exception.enabled.build.stdcpp_lib=-lstdc++ +esp210.menu.exception.disabled=Disabled +esp210.menu.exception.disabled.build.exception_flags=-fno-exceptions +esp210.menu.exception.disabled.build.stdcpp_lib=-lstdc++-nox +esp210.upload.resetmethod=ck +esp210.build.flash_mode=qio +esp210.build.flash_freq=40 +esp210.menu.eesz.4M=4M (no SPIFFS) +esp210.menu.eesz.4M.build.flash_size=4M +esp210.menu.eesz.4M.build.flash_size_bytes=0x400000 +esp210.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld +esp210.menu.eesz.4M.build.spiffs_pagesize=256 +esp210.menu.eesz.4M.upload.maximum_size=1044464 +esp210.menu.eesz.4M.build.rfcal_addr=0x3FC000 +esp210.menu.eesz.4M1M=4M (1M SPIFFS) +esp210.menu.eesz.4M1M.build.flash_size=4M +esp210.menu.eesz.4M1M.build.flash_size_bytes=0x400000 +esp210.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld +esp210.menu.eesz.4M1M.build.spiffs_pagesize=256 +esp210.menu.eesz.4M1M.upload.maximum_size=1044464 +esp210.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 +esp210.menu.eesz.4M1M.build.spiffs_start=0x300000 +esp210.menu.eesz.4M1M.build.spiffs_end=0x3FB000 +esp210.menu.eesz.4M1M.build.spiffs_blocksize=8192 +esp210.menu.eesz.4M2M=4M (2M SPIFFS) +esp210.menu.eesz.4M2M.build.flash_size=4M +esp210.menu.eesz.4M2M.build.flash_size_bytes=0x400000 +esp210.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld +esp210.menu.eesz.4M2M.build.spiffs_pagesize=256 +esp210.menu.eesz.4M2M.upload.maximum_size=1044464 +esp210.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 +esp210.menu.eesz.4M2M.build.spiffs_start=0x200000 +esp210.menu.eesz.4M2M.build.spiffs_end=0x3FB000 +esp210.menu.eesz.4M2M.build.spiffs_blocksize=8192 +esp210.menu.eesz.4M3M=4M (3M SPIFFS) +esp210.menu.eesz.4M3M.build.flash_size=4M +esp210.menu.eesz.4M3M.build.flash_size_bytes=0x400000 +esp210.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld +esp210.menu.eesz.4M3M.build.spiffs_pagesize=256 +esp210.menu.eesz.4M3M.upload.maximum_size=1044464 +esp210.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 +esp210.menu.eesz.4M3M.build.spiffs_start=0x100000 +esp210.menu.eesz.4M3M.build.spiffs_end=0x3FB000 +esp210.menu.eesz.4M3M.build.spiffs_blocksize=8192 +esp210.menu.ip.lm2f=v2 Lower Memory +esp210.menu.ip.lm2f.build.lwip_include=lwip2/include +esp210.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +esp210.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +esp210.menu.ip.hb2f=v2 Higher Bandwidth +esp210.menu.ip.hb2f.build.lwip_include=lwip2/include +esp210.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +esp210.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +esp210.menu.ip.lm2n=v2 Lower Memory (no features) +esp210.menu.ip.lm2n.build.lwip_include=lwip2/include +esp210.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +esp210.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +esp210.menu.ip.hb2n=v2 Higher Bandwidth (no features) +esp210.menu.ip.hb2n.build.lwip_include=lwip2/include +esp210.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +esp210.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +esp210.menu.ip.lm6f=v2 IPv6 Lower Memory +esp210.menu.ip.lm6f.build.lwip_include=lwip2/include +esp210.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +esp210.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +esp210.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +esp210.menu.ip.hb6f.build.lwip_include=lwip2/include +esp210.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +esp210.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +esp210.menu.ip.hb1=v1.4 Higher Bandwidth +esp210.menu.ip.hb1.build.lwip_lib=-llwip_gcc +esp210.menu.ip.hb1.build.lwip_flags=-DLWIP_OPEN_SRC +esp210.menu.ip.src=v1.4 Compile from source +esp210.menu.ip.src.build.lwip_lib=-llwip_src +esp210.menu.ip.src.build.lwip_flags=-DLWIP_OPEN_SRC +esp210.menu.ip.src.recipe.hooks.sketch.prebuild.1.pattern=make -C "{runtime.platform.path}/tools/sdk/lwip/src" install TOOLS_PATH="{runtime.tools.xtensa-lx106-elf-gcc.path}/bin/xtensa-lx106-elf-" +esp210.menu.dbg.Disabled=Disabled +esp210.menu.dbg.Disabled.build.debug_port= +esp210.menu.dbg.Serial=Serial +esp210.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +esp210.menu.dbg.Serial1=Serial1 +esp210.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +esp210.menu.lvl.None____=None +esp210.menu.lvl.None____.build.debug_level= +esp210.menu.lvl.SSL=SSL +esp210.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +esp210.menu.lvl.TLS_MEM=TLS_MEM +esp210.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +esp210.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +esp210.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +esp210.menu.lvl.HTTP_SERVER=HTTP_SERVER +esp210.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +esp210.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +esp210.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +esp210.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +esp210.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +esp210.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +esp210.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +esp210.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +esp210.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +esp210.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +esp210.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +esp210.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +esp210.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +esp210.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +esp210.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +esp210.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +esp210.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +esp210.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +esp210.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +esp210.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +esp210.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +esp210.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +esp210.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +esp210.menu.lvl.CORE=CORE +esp210.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +esp210.menu.lvl.WIFI=WIFI +esp210.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +esp210.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +esp210.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +esp210.menu.lvl.UPDATER=UPDATER +esp210.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +esp210.menu.lvl.OTA=OTA +esp210.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +esp210.menu.lvl.OOM=OOM +esp210.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +esp210.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOM=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM +esp210.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOM.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM +esp210.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM +esp210.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM +esp210.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +esp210.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +esp210.menu.wipe.none=Only Sketch +esp210.menu.wipe.none.upload.erase_cmd= +esp210.menu.wipe.sdk=Sketch + WiFi Settings +esp210.menu.wipe.sdk.upload.erase_cmd=-ca "{build.rfcal_addr}" -cz 0x4000 +esp210.menu.wipe.all=All Flash Contents +esp210.menu.wipe.all.upload.erase_cmd=-ca 0x0 -cz "{build.flash_size_bytes}" +esp210.menu.baud.57600=57600 +esp210.menu.baud.57600.upload.speed=57600 +esp210.menu.baud.9600=9600 +esp210.menu.baud.9600.upload.speed=9600 +esp210.menu.baud.115200=115200 +esp210.menu.baud.115200.upload.speed=115200 +esp210.menu.baud.230400.linux=230400 +esp210.menu.baud.230400.macosx=230400 +esp210.menu.baud.230400.upload.speed=230400 +esp210.menu.baud.256000.windows=256000 +esp210.menu.baud.256000.upload.speed=256000 +esp210.menu.baud.460800.linux=460800 +esp210.menu.baud.460800.macosx=460800 +esp210.menu.baud.460800.upload.speed=460800 +esp210.menu.baud.512000.windows=512000 +esp210.menu.baud.512000.upload.speed=512000 +esp210.menu.baud.921600=921600 +esp210.menu.baud.921600.upload.speed=921600 + +############################################################## +d1_mini.name=LOLIN(WEMOS) D1 R2 & mini +d1_mini.build.board=ESP8266_WEMOS_D1MINI +d1_mini.build.variant=d1_mini +d1_mini.upload.tool=esptool +d1_mini.upload.maximum_data_size=81920 +d1_mini.upload.wait_for_upload_port=true +d1_mini.upload.erase_cmd= +d1_mini.serial.disableDTR=true +d1_mini.serial.disableRTS=true +d1_mini.build.mcu=esp8266 +d1_mini.build.core=esp8266 +d1_mini.build.spiffs_pagesize=256 +d1_mini.build.debug_port= +d1_mini.build.debug_level= +d1_mini.menu.xtal.80=80 MHz +d1_mini.menu.xtal.80.build.f_cpu=80000000L +d1_mini.menu.xtal.160=160 MHz +d1_mini.menu.xtal.160.build.f_cpu=160000000L +d1_mini.menu.vt.flash=Flash +d1_mini.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +d1_mini.menu.vt.heap=Heap +d1_mini.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +d1_mini.menu.vt.iram=IRAM +d1_mini.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +d1_mini.menu.exception.enabled=Enabled +d1_mini.menu.exception.enabled.build.exception_flags=-fexceptions +d1_mini.menu.exception.enabled.build.stdcpp_lib=-lstdc++ +d1_mini.menu.exception.disabled=Disabled +d1_mini.menu.exception.disabled.build.exception_flags=-fno-exceptions +d1_mini.menu.exception.disabled.build.stdcpp_lib=-lstdc++-nox +d1_mini.upload.resetmethod=nodemcu +d1_mini.build.flash_mode=dio +d1_mini.build.flash_freq=40 +d1_mini.menu.eesz.4M=4M (no SPIFFS) +d1_mini.menu.eesz.4M.build.flash_size=4M +d1_mini.menu.eesz.4M.build.flash_size_bytes=0x400000 +d1_mini.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld +d1_mini.menu.eesz.4M.build.spiffs_pagesize=256 +d1_mini.menu.eesz.4M.upload.maximum_size=1044464 +d1_mini.menu.eesz.4M.build.rfcal_addr=0x3FC000 +d1_mini.menu.eesz.4M1M=4M (1M SPIFFS) +d1_mini.menu.eesz.4M1M.build.flash_size=4M +d1_mini.menu.eesz.4M1M.build.flash_size_bytes=0x400000 +d1_mini.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld +d1_mini.menu.eesz.4M1M.build.spiffs_pagesize=256 +d1_mini.menu.eesz.4M1M.upload.maximum_size=1044464 +d1_mini.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 +d1_mini.menu.eesz.4M1M.build.spiffs_start=0x300000 +d1_mini.menu.eesz.4M1M.build.spiffs_end=0x3FB000 +d1_mini.menu.eesz.4M1M.build.spiffs_blocksize=8192 +d1_mini.menu.eesz.4M2M=4M (2M SPIFFS) +d1_mini.menu.eesz.4M2M.build.flash_size=4M +d1_mini.menu.eesz.4M2M.build.flash_size_bytes=0x400000 +d1_mini.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld +d1_mini.menu.eesz.4M2M.build.spiffs_pagesize=256 +d1_mini.menu.eesz.4M2M.upload.maximum_size=1044464 +d1_mini.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 +d1_mini.menu.eesz.4M2M.build.spiffs_start=0x200000 +d1_mini.menu.eesz.4M2M.build.spiffs_end=0x3FB000 +d1_mini.menu.eesz.4M2M.build.spiffs_blocksize=8192 +d1_mini.menu.eesz.4M3M=4M (3M SPIFFS) +d1_mini.menu.eesz.4M3M.build.flash_size=4M +d1_mini.menu.eesz.4M3M.build.flash_size_bytes=0x400000 +d1_mini.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld +d1_mini.menu.eesz.4M3M.build.spiffs_pagesize=256 +d1_mini.menu.eesz.4M3M.upload.maximum_size=1044464 +d1_mini.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 +d1_mini.menu.eesz.4M3M.build.spiffs_start=0x100000 +d1_mini.menu.eesz.4M3M.build.spiffs_end=0x3FB000 +d1_mini.menu.eesz.4M3M.build.spiffs_blocksize=8192 +d1_mini.menu.ip.lm2f=v2 Lower Memory +d1_mini.menu.ip.lm2f.build.lwip_include=lwip2/include +d1_mini.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +d1_mini.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +d1_mini.menu.ip.hb2f=v2 Higher Bandwidth +d1_mini.menu.ip.hb2f.build.lwip_include=lwip2/include +d1_mini.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +d1_mini.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +d1_mini.menu.ip.lm2n=v2 Lower Memory (no features) +d1_mini.menu.ip.lm2n.build.lwip_include=lwip2/include +d1_mini.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +d1_mini.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +d1_mini.menu.ip.hb2n=v2 Higher Bandwidth (no features) +d1_mini.menu.ip.hb2n.build.lwip_include=lwip2/include +d1_mini.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +d1_mini.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +d1_mini.menu.ip.lm6f=v2 IPv6 Lower Memory +d1_mini.menu.ip.lm6f.build.lwip_include=lwip2/include +d1_mini.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +d1_mini.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +d1_mini.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +d1_mini.menu.ip.hb6f.build.lwip_include=lwip2/include +d1_mini.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +d1_mini.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +d1_mini.menu.ip.hb1=v1.4 Higher Bandwidth +d1_mini.menu.ip.hb1.build.lwip_lib=-llwip_gcc +d1_mini.menu.ip.hb1.build.lwip_flags=-DLWIP_OPEN_SRC +d1_mini.menu.ip.src=v1.4 Compile from source +d1_mini.menu.ip.src.build.lwip_lib=-llwip_src +d1_mini.menu.ip.src.build.lwip_flags=-DLWIP_OPEN_SRC +d1_mini.menu.ip.src.recipe.hooks.sketch.prebuild.1.pattern=make -C "{runtime.platform.path}/tools/sdk/lwip/src" install TOOLS_PATH="{runtime.tools.xtensa-lx106-elf-gcc.path}/bin/xtensa-lx106-elf-" +d1_mini.menu.dbg.Disabled=Disabled +d1_mini.menu.dbg.Disabled.build.debug_port= +d1_mini.menu.dbg.Serial=Serial +d1_mini.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +d1_mini.menu.dbg.Serial1=Serial1 +d1_mini.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +d1_mini.menu.lvl.None____=None +d1_mini.menu.lvl.None____.build.debug_level= +d1_mini.menu.lvl.SSL=SSL +d1_mini.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +d1_mini.menu.lvl.TLS_MEM=TLS_MEM +d1_mini.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +d1_mini.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +d1_mini.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +d1_mini.menu.lvl.HTTP_SERVER=HTTP_SERVER +d1_mini.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +d1_mini.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +d1_mini.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +d1_mini.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +d1_mini.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +d1_mini.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +d1_mini.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +d1_mini.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +d1_mini.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +d1_mini.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +d1_mini.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +d1_mini.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +d1_mini.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +d1_mini.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +d1_mini.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +d1_mini.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +d1_mini.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +d1_mini.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +d1_mini.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +d1_mini.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +d1_mini.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +d1_mini.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +d1_mini.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +d1_mini.menu.lvl.CORE=CORE +d1_mini.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +d1_mini.menu.lvl.WIFI=WIFI +d1_mini.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +d1_mini.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +d1_mini.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +d1_mini.menu.lvl.UPDATER=UPDATER +d1_mini.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +d1_mini.menu.lvl.OTA=OTA +d1_mini.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +d1_mini.menu.lvl.OOM=OOM +d1_mini.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +d1_mini.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOM=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM +d1_mini.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOM.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM +d1_mini.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM +d1_mini.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM +d1_mini.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +d1_mini.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +d1_mini.menu.wipe.none=Only Sketch +d1_mini.menu.wipe.none.upload.erase_cmd= +d1_mini.menu.wipe.sdk=Sketch + WiFi Settings +d1_mini.menu.wipe.sdk.upload.erase_cmd=-ca "{build.rfcal_addr}" -cz 0x4000 +d1_mini.menu.wipe.all=All Flash Contents +d1_mini.menu.wipe.all.upload.erase_cmd=-ca 0x0 -cz "{build.flash_size_bytes}" +d1_mini.menu.baud.921600=921600 +d1_mini.menu.baud.921600.upload.speed=921600 +d1_mini.menu.baud.9600=9600 +d1_mini.menu.baud.9600.upload.speed=9600 +d1_mini.menu.baud.57600=57600 +d1_mini.menu.baud.57600.upload.speed=57600 +d1_mini.menu.baud.115200=115200 +d1_mini.menu.baud.115200.upload.speed=115200 +d1_mini.menu.baud.230400.linux=230400 +d1_mini.menu.baud.230400.macosx=230400 +d1_mini.menu.baud.230400.upload.speed=230400 +d1_mini.menu.baud.256000.windows=256000 +d1_mini.menu.baud.256000.upload.speed=256000 +d1_mini.menu.baud.460800.linux=460800 +d1_mini.menu.baud.460800.macosx=460800 +d1_mini.menu.baud.460800.upload.speed=460800 +d1_mini.menu.baud.512000.windows=512000 +d1_mini.menu.baud.512000.upload.speed=512000 + +############################################################## +d1_mini_pro.name=LOLIN(WEMOS) D1 mini Pro +d1_mini_pro.build.board=ESP8266_WEMOS_D1MINIPRO +d1_mini_pro.build.variant=d1_mini +d1_mini_pro.upload.tool=esptool +d1_mini_pro.upload.maximum_data_size=81920 +d1_mini_pro.upload.wait_for_upload_port=true +d1_mini_pro.upload.erase_cmd= +d1_mini_pro.serial.disableDTR=true +d1_mini_pro.serial.disableRTS=true +d1_mini_pro.build.mcu=esp8266 +d1_mini_pro.build.core=esp8266 +d1_mini_pro.build.spiffs_pagesize=256 +d1_mini_pro.build.debug_port= +d1_mini_pro.build.debug_level= +d1_mini_pro.menu.xtal.80=80 MHz +d1_mini_pro.menu.xtal.80.build.f_cpu=80000000L +d1_mini_pro.menu.xtal.160=160 MHz +d1_mini_pro.menu.xtal.160.build.f_cpu=160000000L +d1_mini_pro.menu.vt.flash=Flash +d1_mini_pro.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +d1_mini_pro.menu.vt.heap=Heap +d1_mini_pro.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +d1_mini_pro.menu.vt.iram=IRAM +d1_mini_pro.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +d1_mini_pro.menu.exception.enabled=Enabled +d1_mini_pro.menu.exception.enabled.build.exception_flags=-fexceptions +d1_mini_pro.menu.exception.enabled.build.stdcpp_lib=-lstdc++ +d1_mini_pro.menu.exception.disabled=Disabled +d1_mini_pro.menu.exception.disabled.build.exception_flags=-fno-exceptions +d1_mini_pro.menu.exception.disabled.build.stdcpp_lib=-lstdc++-nox +d1_mini_pro.upload.resetmethod=nodemcu +d1_mini_pro.build.flash_mode=dio +d1_mini_pro.build.flash_freq=40 +d1_mini_pro.menu.eesz.16M14M=16M (14M SPIFFS) +d1_mini_pro.menu.eesz.16M14M.build.flash_size=16M +d1_mini_pro.menu.eesz.16M14M.build.flash_size_bytes=0x1000000 +d1_mini_pro.menu.eesz.16M14M.build.flash_ld=eagle.flash.16m14m.ld +d1_mini_pro.menu.eesz.16M14M.build.spiffs_pagesize=256 +d1_mini_pro.menu.eesz.16M14M.upload.maximum_size=1044464 +d1_mini_pro.menu.eesz.16M14M.build.rfcal_addr=0xFFC000 +d1_mini_pro.menu.eesz.16M14M.build.spiffs_start=0x200000 +d1_mini_pro.menu.eesz.16M14M.build.spiffs_end=0xFFB000 +d1_mini_pro.menu.eesz.16M14M.build.spiffs_blocksize=8192 +d1_mini_pro.menu.eesz.16M15M=16M (15M SPIFFS) +d1_mini_pro.menu.eesz.16M15M.build.flash_size=16M +d1_mini_pro.menu.eesz.16M15M.build.flash_size_bytes=0x1000000 +d1_mini_pro.menu.eesz.16M15M.build.flash_ld=eagle.flash.16m15m.ld +d1_mini_pro.menu.eesz.16M15M.build.spiffs_pagesize=256 +d1_mini_pro.menu.eesz.16M15M.upload.maximum_size=1044464 +d1_mini_pro.menu.eesz.16M15M.build.rfcal_addr=0xFFC000 +d1_mini_pro.menu.eesz.16M15M.build.spiffs_start=0x100000 +d1_mini_pro.menu.eesz.16M15M.build.spiffs_end=0xFFB000 +d1_mini_pro.menu.eesz.16M15M.build.spiffs_blocksize=8192 +d1_mini_pro.menu.ip.lm2f=v2 Lower Memory +d1_mini_pro.menu.ip.lm2f.build.lwip_include=lwip2/include +d1_mini_pro.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +d1_mini_pro.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +d1_mini_pro.menu.ip.hb2f=v2 Higher Bandwidth +d1_mini_pro.menu.ip.hb2f.build.lwip_include=lwip2/include +d1_mini_pro.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +d1_mini_pro.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +d1_mini_pro.menu.ip.lm2n=v2 Lower Memory (no features) +d1_mini_pro.menu.ip.lm2n.build.lwip_include=lwip2/include +d1_mini_pro.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +d1_mini_pro.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +d1_mini_pro.menu.ip.hb2n=v2 Higher Bandwidth (no features) +d1_mini_pro.menu.ip.hb2n.build.lwip_include=lwip2/include +d1_mini_pro.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +d1_mini_pro.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +d1_mini_pro.menu.ip.lm6f=v2 IPv6 Lower Memory +d1_mini_pro.menu.ip.lm6f.build.lwip_include=lwip2/include +d1_mini_pro.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +d1_mini_pro.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +d1_mini_pro.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +d1_mini_pro.menu.ip.hb6f.build.lwip_include=lwip2/include +d1_mini_pro.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +d1_mini_pro.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +d1_mini_pro.menu.ip.hb1=v1.4 Higher Bandwidth +d1_mini_pro.menu.ip.hb1.build.lwip_lib=-llwip_gcc +d1_mini_pro.menu.ip.hb1.build.lwip_flags=-DLWIP_OPEN_SRC +d1_mini_pro.menu.ip.src=v1.4 Compile from source +d1_mini_pro.menu.ip.src.build.lwip_lib=-llwip_src +d1_mini_pro.menu.ip.src.build.lwip_flags=-DLWIP_OPEN_SRC +d1_mini_pro.menu.ip.src.recipe.hooks.sketch.prebuild.1.pattern=make -C "{runtime.platform.path}/tools/sdk/lwip/src" install TOOLS_PATH="{runtime.tools.xtensa-lx106-elf-gcc.path}/bin/xtensa-lx106-elf-" +d1_mini_pro.menu.dbg.Disabled=Disabled +d1_mini_pro.menu.dbg.Disabled.build.debug_port= +d1_mini_pro.menu.dbg.Serial=Serial +d1_mini_pro.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +d1_mini_pro.menu.dbg.Serial1=Serial1 +d1_mini_pro.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +d1_mini_pro.menu.lvl.None____=None +d1_mini_pro.menu.lvl.None____.build.debug_level= +d1_mini_pro.menu.lvl.SSL=SSL +d1_mini_pro.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +d1_mini_pro.menu.lvl.TLS_MEM=TLS_MEM +d1_mini_pro.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +d1_mini_pro.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +d1_mini_pro.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +d1_mini_pro.menu.lvl.HTTP_SERVER=HTTP_SERVER +d1_mini_pro.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +d1_mini_pro.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +d1_mini_pro.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +d1_mini_pro.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +d1_mini_pro.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +d1_mini_pro.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +d1_mini_pro.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +d1_mini_pro.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +d1_mini_pro.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +d1_mini_pro.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +d1_mini_pro.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +d1_mini_pro.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +d1_mini_pro.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +d1_mini_pro.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +d1_mini_pro.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +d1_mini_pro.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +d1_mini_pro.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +d1_mini_pro.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +d1_mini_pro.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +d1_mini_pro.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +d1_mini_pro.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +d1_mini_pro.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +d1_mini_pro.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +d1_mini_pro.menu.lvl.CORE=CORE +d1_mini_pro.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +d1_mini_pro.menu.lvl.WIFI=WIFI +d1_mini_pro.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +d1_mini_pro.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +d1_mini_pro.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +d1_mini_pro.menu.lvl.UPDATER=UPDATER +d1_mini_pro.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +d1_mini_pro.menu.lvl.OTA=OTA +d1_mini_pro.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +d1_mini_pro.menu.lvl.OOM=OOM +d1_mini_pro.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +d1_mini_pro.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOM=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM +d1_mini_pro.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOM.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM +d1_mini_pro.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM +d1_mini_pro.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM +d1_mini_pro.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +d1_mini_pro.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +d1_mini_pro.menu.wipe.none=Only Sketch +d1_mini_pro.menu.wipe.none.upload.erase_cmd= +d1_mini_pro.menu.wipe.sdk=Sketch + WiFi Settings +d1_mini_pro.menu.wipe.sdk.upload.erase_cmd=-ca "{build.rfcal_addr}" -cz 0x4000 +d1_mini_pro.menu.wipe.all=All Flash Contents +d1_mini_pro.menu.wipe.all.upload.erase_cmd=-ca 0x0 -cz "{build.flash_size_bytes}" +d1_mini_pro.menu.baud.921600=921600 +d1_mini_pro.menu.baud.921600.upload.speed=921600 +d1_mini_pro.menu.baud.9600=9600 +d1_mini_pro.menu.baud.9600.upload.speed=9600 +d1_mini_pro.menu.baud.57600=57600 +d1_mini_pro.menu.baud.57600.upload.speed=57600 +d1_mini_pro.menu.baud.115200=115200 +d1_mini_pro.menu.baud.115200.upload.speed=115200 +d1_mini_pro.menu.baud.230400.linux=230400 +d1_mini_pro.menu.baud.230400.macosx=230400 +d1_mini_pro.menu.baud.230400.upload.speed=230400 +d1_mini_pro.menu.baud.256000.windows=256000 +d1_mini_pro.menu.baud.256000.upload.speed=256000 +d1_mini_pro.menu.baud.460800.linux=460800 +d1_mini_pro.menu.baud.460800.macosx=460800 +d1_mini_pro.menu.baud.460800.upload.speed=460800 +d1_mini_pro.menu.baud.512000.windows=512000 +d1_mini_pro.menu.baud.512000.upload.speed=512000 + +############################################################## +d1_mini_lite.name=LOLIN(WEMOS) D1 mini Lite +d1_mini_lite.build.board=ESP8266_WEMOS_D1MINILITE +d1_mini_lite.build.variant=d1_mini +d1_mini_lite.upload.tool=esptool +d1_mini_lite.upload.maximum_data_size=81920 +d1_mini_lite.upload.wait_for_upload_port=true +d1_mini_lite.upload.erase_cmd= +d1_mini_lite.serial.disableDTR=true +d1_mini_lite.serial.disableRTS=true +d1_mini_lite.build.mcu=esp8266 +d1_mini_lite.build.core=esp8266 +d1_mini_lite.build.spiffs_pagesize=256 +d1_mini_lite.build.debug_port= +d1_mini_lite.build.debug_level= +d1_mini_lite.menu.xtal.80=80 MHz +d1_mini_lite.menu.xtal.80.build.f_cpu=80000000L +d1_mini_lite.menu.xtal.160=160 MHz +d1_mini_lite.menu.xtal.160.build.f_cpu=160000000L +d1_mini_lite.menu.vt.flash=Flash +d1_mini_lite.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +d1_mini_lite.menu.vt.heap=Heap +d1_mini_lite.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +d1_mini_lite.menu.vt.iram=IRAM +d1_mini_lite.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +d1_mini_lite.menu.exception.enabled=Enabled +d1_mini_lite.menu.exception.enabled.build.exception_flags=-fexceptions +d1_mini_lite.menu.exception.enabled.build.stdcpp_lib=-lstdc++ +d1_mini_lite.menu.exception.disabled=Disabled +d1_mini_lite.menu.exception.disabled.build.exception_flags=-fno-exceptions +d1_mini_lite.menu.exception.disabled.build.stdcpp_lib=-lstdc++-nox +d1_mini_lite.upload.resetmethod=nodemcu +d1_mini_lite.build.flash_mode=dout +d1_mini_lite.build.flash_freq=40 +d1_mini_lite.menu.eesz.1M=1M (no SPIFFS) +d1_mini_lite.menu.eesz.1M.build.flash_size=1M +d1_mini_lite.menu.eesz.1M.build.flash_size_bytes=0x100000 +d1_mini_lite.menu.eesz.1M.build.flash_ld=eagle.flash.1m.ld +d1_mini_lite.menu.eesz.1M.build.spiffs_pagesize=256 +d1_mini_lite.menu.eesz.1M.upload.maximum_size=1023984 +d1_mini_lite.menu.eesz.1M.build.rfcal_addr=0xFC000 +d1_mini_lite.menu.eesz.1M64=1M (64K SPIFFS) +d1_mini_lite.menu.eesz.1M64.build.flash_size=1M +d1_mini_lite.menu.eesz.1M64.build.flash_size_bytes=0x100000 +d1_mini_lite.menu.eesz.1M64.build.flash_ld=eagle.flash.1m64.ld +d1_mini_lite.menu.eesz.1M64.build.spiffs_pagesize=256 +d1_mini_lite.menu.eesz.1M64.upload.maximum_size=958448 +d1_mini_lite.menu.eesz.1M64.build.rfcal_addr=0xFC000 +d1_mini_lite.menu.eesz.1M64.build.spiffs_start=0xEB000 +d1_mini_lite.menu.eesz.1M64.build.spiffs_end=0xFB000 +d1_mini_lite.menu.eesz.1M64.build.spiffs_blocksize=4096 +d1_mini_lite.menu.eesz.1M128=1M (128K SPIFFS) +d1_mini_lite.menu.eesz.1M128.build.flash_size=1M +d1_mini_lite.menu.eesz.1M128.build.flash_size_bytes=0x100000 +d1_mini_lite.menu.eesz.1M128.build.flash_ld=eagle.flash.1m128.ld +d1_mini_lite.menu.eesz.1M128.build.spiffs_pagesize=256 +d1_mini_lite.menu.eesz.1M128.upload.maximum_size=892912 +d1_mini_lite.menu.eesz.1M128.build.rfcal_addr=0xFC000 +d1_mini_lite.menu.eesz.1M128.build.spiffs_start=0xDB000 +d1_mini_lite.menu.eesz.1M128.build.spiffs_end=0xFB000 +d1_mini_lite.menu.eesz.1M128.build.spiffs_blocksize=4096 +d1_mini_lite.menu.eesz.1M144=1M (144K SPIFFS) +d1_mini_lite.menu.eesz.1M144.build.flash_size=1M +d1_mini_lite.menu.eesz.1M144.build.flash_size_bytes=0x100000 +d1_mini_lite.menu.eesz.1M144.build.flash_ld=eagle.flash.1m144.ld +d1_mini_lite.menu.eesz.1M144.build.spiffs_pagesize=256 +d1_mini_lite.menu.eesz.1M144.upload.maximum_size=876528 +d1_mini_lite.menu.eesz.1M144.build.rfcal_addr=0xFC000 +d1_mini_lite.menu.eesz.1M144.build.spiffs_start=0xD7000 +d1_mini_lite.menu.eesz.1M144.build.spiffs_end=0xFB000 +d1_mini_lite.menu.eesz.1M144.build.spiffs_blocksize=4096 +d1_mini_lite.menu.eesz.1M160=1M (160K SPIFFS) +d1_mini_lite.menu.eesz.1M160.build.flash_size=1M +d1_mini_lite.menu.eesz.1M160.build.flash_size_bytes=0x100000 +d1_mini_lite.menu.eesz.1M160.build.flash_ld=eagle.flash.1m160.ld +d1_mini_lite.menu.eesz.1M160.build.spiffs_pagesize=256 +d1_mini_lite.menu.eesz.1M160.upload.maximum_size=860144 +d1_mini_lite.menu.eesz.1M160.build.rfcal_addr=0xFC000 +d1_mini_lite.menu.eesz.1M160.build.spiffs_start=0xD3000 +d1_mini_lite.menu.eesz.1M160.build.spiffs_end=0xFB000 +d1_mini_lite.menu.eesz.1M160.build.spiffs_blocksize=4096 +d1_mini_lite.menu.eesz.1M192=1M (192K SPIFFS) +d1_mini_lite.menu.eesz.1M192.build.flash_size=1M +d1_mini_lite.menu.eesz.1M192.build.flash_size_bytes=0x100000 +d1_mini_lite.menu.eesz.1M192.build.flash_ld=eagle.flash.1m192.ld +d1_mini_lite.menu.eesz.1M192.build.spiffs_pagesize=256 +d1_mini_lite.menu.eesz.1M192.upload.maximum_size=827376 +d1_mini_lite.menu.eesz.1M192.build.rfcal_addr=0xFC000 +d1_mini_lite.menu.eesz.1M192.build.spiffs_start=0xCB000 +d1_mini_lite.menu.eesz.1M192.build.spiffs_end=0xFB000 +d1_mini_lite.menu.eesz.1M192.build.spiffs_blocksize=4096 +d1_mini_lite.menu.eesz.1M256=1M (256K SPIFFS) +d1_mini_lite.menu.eesz.1M256.build.flash_size=1M +d1_mini_lite.menu.eesz.1M256.build.flash_size_bytes=0x100000 +d1_mini_lite.menu.eesz.1M256.build.flash_ld=eagle.flash.1m256.ld +d1_mini_lite.menu.eesz.1M256.build.spiffs_pagesize=256 +d1_mini_lite.menu.eesz.1M256.upload.maximum_size=761840 +d1_mini_lite.menu.eesz.1M256.build.rfcal_addr=0xFC000 +d1_mini_lite.menu.eesz.1M256.build.spiffs_start=0xBB000 +d1_mini_lite.menu.eesz.1M256.build.spiffs_end=0xFB000 +d1_mini_lite.menu.eesz.1M256.build.spiffs_blocksize=4096 +d1_mini_lite.menu.eesz.1M512=1M (512K SPIFFS) +d1_mini_lite.menu.eesz.1M512.build.flash_size=1M +d1_mini_lite.menu.eesz.1M512.build.flash_size_bytes=0x100000 +d1_mini_lite.menu.eesz.1M512.build.flash_ld=eagle.flash.1m512.ld +d1_mini_lite.menu.eesz.1M512.build.spiffs_pagesize=256 +d1_mini_lite.menu.eesz.1M512.upload.maximum_size=499696 +d1_mini_lite.menu.eesz.1M512.build.rfcal_addr=0xFC000 +d1_mini_lite.menu.eesz.1M512.build.spiffs_start=0x7B000 +d1_mini_lite.menu.eesz.1M512.build.spiffs_end=0xFB000 +d1_mini_lite.menu.eesz.1M512.build.spiffs_blocksize=4096 +d1_mini_lite.menu.ip.lm2f=v2 Lower Memory +d1_mini_lite.menu.ip.lm2f.build.lwip_include=lwip2/include +d1_mini_lite.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +d1_mini_lite.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +d1_mini_lite.menu.ip.hb2f=v2 Higher Bandwidth +d1_mini_lite.menu.ip.hb2f.build.lwip_include=lwip2/include +d1_mini_lite.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +d1_mini_lite.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +d1_mini_lite.menu.ip.lm2n=v2 Lower Memory (no features) +d1_mini_lite.menu.ip.lm2n.build.lwip_include=lwip2/include +d1_mini_lite.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +d1_mini_lite.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +d1_mini_lite.menu.ip.hb2n=v2 Higher Bandwidth (no features) +d1_mini_lite.menu.ip.hb2n.build.lwip_include=lwip2/include +d1_mini_lite.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +d1_mini_lite.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +d1_mini_lite.menu.ip.lm6f=v2 IPv6 Lower Memory +d1_mini_lite.menu.ip.lm6f.build.lwip_include=lwip2/include +d1_mini_lite.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +d1_mini_lite.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +d1_mini_lite.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +d1_mini_lite.menu.ip.hb6f.build.lwip_include=lwip2/include +d1_mini_lite.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +d1_mini_lite.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +d1_mini_lite.menu.ip.hb1=v1.4 Higher Bandwidth +d1_mini_lite.menu.ip.hb1.build.lwip_lib=-llwip_gcc +d1_mini_lite.menu.ip.hb1.build.lwip_flags=-DLWIP_OPEN_SRC +d1_mini_lite.menu.ip.src=v1.4 Compile from source +d1_mini_lite.menu.ip.src.build.lwip_lib=-llwip_src +d1_mini_lite.menu.ip.src.build.lwip_flags=-DLWIP_OPEN_SRC +d1_mini_lite.menu.ip.src.recipe.hooks.sketch.prebuild.1.pattern=make -C "{runtime.platform.path}/tools/sdk/lwip/src" install TOOLS_PATH="{runtime.tools.xtensa-lx106-elf-gcc.path}/bin/xtensa-lx106-elf-" +d1_mini_lite.menu.dbg.Disabled=Disabled +d1_mini_lite.menu.dbg.Disabled.build.debug_port= +d1_mini_lite.menu.dbg.Serial=Serial +d1_mini_lite.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +d1_mini_lite.menu.dbg.Serial1=Serial1 +d1_mini_lite.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +d1_mini_lite.menu.lvl.None____=None +d1_mini_lite.menu.lvl.None____.build.debug_level= +d1_mini_lite.menu.lvl.SSL=SSL +d1_mini_lite.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +d1_mini_lite.menu.lvl.TLS_MEM=TLS_MEM +d1_mini_lite.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +d1_mini_lite.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +d1_mini_lite.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +d1_mini_lite.menu.lvl.HTTP_SERVER=HTTP_SERVER +d1_mini_lite.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +d1_mini_lite.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +d1_mini_lite.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +d1_mini_lite.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +d1_mini_lite.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +d1_mini_lite.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +d1_mini_lite.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +d1_mini_lite.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +d1_mini_lite.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +d1_mini_lite.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +d1_mini_lite.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +d1_mini_lite.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +d1_mini_lite.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +d1_mini_lite.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +d1_mini_lite.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +d1_mini_lite.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +d1_mini_lite.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +d1_mini_lite.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +d1_mini_lite.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +d1_mini_lite.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +d1_mini_lite.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +d1_mini_lite.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +d1_mini_lite.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +d1_mini_lite.menu.lvl.CORE=CORE +d1_mini_lite.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +d1_mini_lite.menu.lvl.WIFI=WIFI +d1_mini_lite.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +d1_mini_lite.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +d1_mini_lite.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +d1_mini_lite.menu.lvl.UPDATER=UPDATER +d1_mini_lite.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +d1_mini_lite.menu.lvl.OTA=OTA +d1_mini_lite.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +d1_mini_lite.menu.lvl.OOM=OOM +d1_mini_lite.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +d1_mini_lite.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOM=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM +d1_mini_lite.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOM.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM +d1_mini_lite.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM +d1_mini_lite.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM +d1_mini_lite.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +d1_mini_lite.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +d1_mini_lite.menu.wipe.none=Only Sketch +d1_mini_lite.menu.wipe.none.upload.erase_cmd= +d1_mini_lite.menu.wipe.sdk=Sketch + WiFi Settings +d1_mini_lite.menu.wipe.sdk.upload.erase_cmd=-ca "{build.rfcal_addr}" -cz 0x4000 +d1_mini_lite.menu.wipe.all=All Flash Contents +d1_mini_lite.menu.wipe.all.upload.erase_cmd=-ca 0x0 -cz "{build.flash_size_bytes}" +d1_mini_lite.menu.baud.921600=921600 +d1_mini_lite.menu.baud.921600.upload.speed=921600 +d1_mini_lite.menu.baud.9600=9600 +d1_mini_lite.menu.baud.9600.upload.speed=9600 +d1_mini_lite.menu.baud.57600=57600 +d1_mini_lite.menu.baud.57600.upload.speed=57600 +d1_mini_lite.menu.baud.115200=115200 +d1_mini_lite.menu.baud.115200.upload.speed=115200 +d1_mini_lite.menu.baud.230400.linux=230400 +d1_mini_lite.menu.baud.230400.macosx=230400 +d1_mini_lite.menu.baud.230400.upload.speed=230400 +d1_mini_lite.menu.baud.256000.windows=256000 +d1_mini_lite.menu.baud.256000.upload.speed=256000 +d1_mini_lite.menu.baud.460800.linux=460800 +d1_mini_lite.menu.baud.460800.macosx=460800 +d1_mini_lite.menu.baud.460800.upload.speed=460800 +d1_mini_lite.menu.baud.512000.windows=512000 +d1_mini_lite.menu.baud.512000.upload.speed=512000 + +############################################################## +d1.name=WeMos D1 R1 +d1.build.board=ESP8266_WEMOS_D1R1 +d1.build.variant=d1 +d1.upload.tool=esptool +d1.upload.maximum_data_size=81920 +d1.upload.wait_for_upload_port=true +d1.upload.erase_cmd= +d1.serial.disableDTR=true +d1.serial.disableRTS=true +d1.build.mcu=esp8266 +d1.build.core=esp8266 +d1.build.spiffs_pagesize=256 +d1.build.debug_port= +d1.build.debug_level= +d1.menu.xtal.80=80 MHz +d1.menu.xtal.80.build.f_cpu=80000000L +d1.menu.xtal.160=160 MHz +d1.menu.xtal.160.build.f_cpu=160000000L +d1.menu.vt.flash=Flash +d1.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +d1.menu.vt.heap=Heap +d1.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +d1.menu.vt.iram=IRAM +d1.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +d1.menu.exception.enabled=Enabled +d1.menu.exception.enabled.build.exception_flags=-fexceptions +d1.menu.exception.enabled.build.stdcpp_lib=-lstdc++ +d1.menu.exception.disabled=Disabled +d1.menu.exception.disabled.build.exception_flags=-fno-exceptions +d1.menu.exception.disabled.build.stdcpp_lib=-lstdc++-nox +d1.upload.resetmethod=nodemcu +d1.build.flash_mode=dio +d1.build.flash_freq=40 +d1.menu.eesz.4M=4M (no SPIFFS) +d1.menu.eesz.4M.build.flash_size=4M +d1.menu.eesz.4M.build.flash_size_bytes=0x400000 +d1.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld +d1.menu.eesz.4M.build.spiffs_pagesize=256 +d1.menu.eesz.4M.upload.maximum_size=1044464 +d1.menu.eesz.4M.build.rfcal_addr=0x3FC000 +d1.menu.eesz.4M1M=4M (1M SPIFFS) +d1.menu.eesz.4M1M.build.flash_size=4M +d1.menu.eesz.4M1M.build.flash_size_bytes=0x400000 +d1.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld +d1.menu.eesz.4M1M.build.spiffs_pagesize=256 +d1.menu.eesz.4M1M.upload.maximum_size=1044464 +d1.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 +d1.menu.eesz.4M1M.build.spiffs_start=0x300000 +d1.menu.eesz.4M1M.build.spiffs_end=0x3FB000 +d1.menu.eesz.4M1M.build.spiffs_blocksize=8192 +d1.menu.eesz.4M2M=4M (2M SPIFFS) +d1.menu.eesz.4M2M.build.flash_size=4M +d1.menu.eesz.4M2M.build.flash_size_bytes=0x400000 +d1.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld +d1.menu.eesz.4M2M.build.spiffs_pagesize=256 +d1.menu.eesz.4M2M.upload.maximum_size=1044464 +d1.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 +d1.menu.eesz.4M2M.build.spiffs_start=0x200000 +d1.menu.eesz.4M2M.build.spiffs_end=0x3FB000 +d1.menu.eesz.4M2M.build.spiffs_blocksize=8192 +d1.menu.eesz.4M3M=4M (3M SPIFFS) +d1.menu.eesz.4M3M.build.flash_size=4M +d1.menu.eesz.4M3M.build.flash_size_bytes=0x400000 +d1.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld +d1.menu.eesz.4M3M.build.spiffs_pagesize=256 +d1.menu.eesz.4M3M.upload.maximum_size=1044464 +d1.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 +d1.menu.eesz.4M3M.build.spiffs_start=0x100000 +d1.menu.eesz.4M3M.build.spiffs_end=0x3FB000 +d1.menu.eesz.4M3M.build.spiffs_blocksize=8192 +d1.menu.ip.lm2f=v2 Lower Memory +d1.menu.ip.lm2f.build.lwip_include=lwip2/include +d1.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +d1.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +d1.menu.ip.hb2f=v2 Higher Bandwidth +d1.menu.ip.hb2f.build.lwip_include=lwip2/include +d1.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +d1.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +d1.menu.ip.lm2n=v2 Lower Memory (no features) +d1.menu.ip.lm2n.build.lwip_include=lwip2/include +d1.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +d1.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +d1.menu.ip.hb2n=v2 Higher Bandwidth (no features) +d1.menu.ip.hb2n.build.lwip_include=lwip2/include +d1.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +d1.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +d1.menu.ip.lm6f=v2 IPv6 Lower Memory +d1.menu.ip.lm6f.build.lwip_include=lwip2/include +d1.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +d1.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +d1.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +d1.menu.ip.hb6f.build.lwip_include=lwip2/include +d1.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +d1.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +d1.menu.ip.hb1=v1.4 Higher Bandwidth +d1.menu.ip.hb1.build.lwip_lib=-llwip_gcc +d1.menu.ip.hb1.build.lwip_flags=-DLWIP_OPEN_SRC +d1.menu.ip.src=v1.4 Compile from source +d1.menu.ip.src.build.lwip_lib=-llwip_src +d1.menu.ip.src.build.lwip_flags=-DLWIP_OPEN_SRC +d1.menu.ip.src.recipe.hooks.sketch.prebuild.1.pattern=make -C "{runtime.platform.path}/tools/sdk/lwip/src" install TOOLS_PATH="{runtime.tools.xtensa-lx106-elf-gcc.path}/bin/xtensa-lx106-elf-" +d1.menu.dbg.Disabled=Disabled +d1.menu.dbg.Disabled.build.debug_port= +d1.menu.dbg.Serial=Serial +d1.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +d1.menu.dbg.Serial1=Serial1 +d1.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +d1.menu.lvl.None____=None +d1.menu.lvl.None____.build.debug_level= +d1.menu.lvl.SSL=SSL +d1.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +d1.menu.lvl.TLS_MEM=TLS_MEM +d1.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +d1.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +d1.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +d1.menu.lvl.HTTP_SERVER=HTTP_SERVER +d1.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +d1.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +d1.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +d1.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +d1.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +d1.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +d1.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +d1.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +d1.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +d1.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +d1.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +d1.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +d1.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +d1.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +d1.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +d1.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +d1.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +d1.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +d1.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +d1.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +d1.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +d1.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +d1.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +d1.menu.lvl.CORE=CORE +d1.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +d1.menu.lvl.WIFI=WIFI +d1.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +d1.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +d1.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +d1.menu.lvl.UPDATER=UPDATER +d1.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +d1.menu.lvl.OTA=OTA +d1.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +d1.menu.lvl.OOM=OOM +d1.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +d1.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOM=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM +d1.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOM.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM +d1.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM +d1.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM +d1.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +d1.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +d1.menu.wipe.none=Only Sketch +d1.menu.wipe.none.upload.erase_cmd= +d1.menu.wipe.sdk=Sketch + WiFi Settings +d1.menu.wipe.sdk.upload.erase_cmd=-ca "{build.rfcal_addr}" -cz 0x4000 +d1.menu.wipe.all=All Flash Contents +d1.menu.wipe.all.upload.erase_cmd=-ca 0x0 -cz "{build.flash_size_bytes}" +d1.menu.baud.921600=921600 +d1.menu.baud.921600.upload.speed=921600 +d1.menu.baud.9600=9600 +d1.menu.baud.9600.upload.speed=9600 +d1.menu.baud.57600=57600 +d1.menu.baud.57600.upload.speed=57600 +d1.menu.baud.115200=115200 +d1.menu.baud.115200.upload.speed=115200 +d1.menu.baud.230400.linux=230400 +d1.menu.baud.230400.macosx=230400 +d1.menu.baud.230400.upload.speed=230400 +d1.menu.baud.256000.windows=256000 +d1.menu.baud.256000.upload.speed=256000 +d1.menu.baud.460800.linux=460800 +d1.menu.baud.460800.macosx=460800 +d1.menu.baud.460800.upload.speed=460800 +d1.menu.baud.512000.windows=512000 +d1.menu.baud.512000.upload.speed=512000 + +############################################################## +espino.name=ESPino (ESP-12 Module) +espino.build.board=ESP8266_ESP12 +espino.build.variant=espino +espino.upload.tool=esptool +espino.upload.maximum_data_size=81920 +espino.upload.wait_for_upload_port=true +espino.upload.erase_cmd= +espino.serial.disableDTR=true +espino.serial.disableRTS=true +espino.build.mcu=esp8266 +espino.build.core=esp8266 +espino.build.spiffs_pagesize=256 +espino.build.debug_port= +espino.build.debug_level= +espino.menu.xtal.80=80 MHz +espino.menu.xtal.80.build.f_cpu=80000000L +espino.menu.xtal.160=160 MHz +espino.menu.xtal.160.build.f_cpu=160000000L +espino.menu.vt.flash=Flash +espino.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +espino.menu.vt.heap=Heap +espino.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +espino.menu.vt.iram=IRAM +espino.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +espino.menu.exception.enabled=Enabled +espino.menu.exception.enabled.build.exception_flags=-fexceptions +espino.menu.exception.enabled.build.stdcpp_lib=-lstdc++ +espino.menu.exception.disabled=Disabled +espino.menu.exception.disabled.build.exception_flags=-fno-exceptions +espino.menu.exception.disabled.build.stdcpp_lib=-lstdc++-nox +espino.menu.ResetMethod.ck=ck +espino.menu.ResetMethod.ck.upload.resetmethod=ck +espino.menu.ResetMethod.nodemcu=nodemcu +espino.menu.ResetMethod.nodemcu.upload.resetmethod=nodemcu +espino.build.flash_mode=qio +espino.build.flash_freq=40 +espino.menu.eesz.4M=4M (no SPIFFS) +espino.menu.eesz.4M.build.flash_size=4M +espino.menu.eesz.4M.build.flash_size_bytes=0x400000 +espino.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld +espino.menu.eesz.4M.build.spiffs_pagesize=256 +espino.menu.eesz.4M.upload.maximum_size=1044464 +espino.menu.eesz.4M.build.rfcal_addr=0x3FC000 +espino.menu.eesz.4M1M=4M (1M SPIFFS) +espino.menu.eesz.4M1M.build.flash_size=4M +espino.menu.eesz.4M1M.build.flash_size_bytes=0x400000 +espino.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld +espino.menu.eesz.4M1M.build.spiffs_pagesize=256 +espino.menu.eesz.4M1M.upload.maximum_size=1044464 +espino.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 +espino.menu.eesz.4M1M.build.spiffs_start=0x300000 +espino.menu.eesz.4M1M.build.spiffs_end=0x3FB000 +espino.menu.eesz.4M1M.build.spiffs_blocksize=8192 +espino.menu.eesz.4M2M=4M (2M SPIFFS) +espino.menu.eesz.4M2M.build.flash_size=4M +espino.menu.eesz.4M2M.build.flash_size_bytes=0x400000 +espino.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld +espino.menu.eesz.4M2M.build.spiffs_pagesize=256 +espino.menu.eesz.4M2M.upload.maximum_size=1044464 +espino.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 +espino.menu.eesz.4M2M.build.spiffs_start=0x200000 +espino.menu.eesz.4M2M.build.spiffs_end=0x3FB000 +espino.menu.eesz.4M2M.build.spiffs_blocksize=8192 +espino.menu.eesz.4M3M=4M (3M SPIFFS) +espino.menu.eesz.4M3M.build.flash_size=4M +espino.menu.eesz.4M3M.build.flash_size_bytes=0x400000 +espino.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld +espino.menu.eesz.4M3M.build.spiffs_pagesize=256 +espino.menu.eesz.4M3M.upload.maximum_size=1044464 +espino.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 +espino.menu.eesz.4M3M.build.spiffs_start=0x100000 +espino.menu.eesz.4M3M.build.spiffs_end=0x3FB000 +espino.menu.eesz.4M3M.build.spiffs_blocksize=8192 +espino.menu.ip.lm2f=v2 Lower Memory +espino.menu.ip.lm2f.build.lwip_include=lwip2/include +espino.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +espino.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +espino.menu.ip.hb2f=v2 Higher Bandwidth +espino.menu.ip.hb2f.build.lwip_include=lwip2/include +espino.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +espino.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +espino.menu.ip.lm2n=v2 Lower Memory (no features) +espino.menu.ip.lm2n.build.lwip_include=lwip2/include +espino.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +espino.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +espino.menu.ip.hb2n=v2 Higher Bandwidth (no features) +espino.menu.ip.hb2n.build.lwip_include=lwip2/include +espino.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +espino.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +espino.menu.ip.lm6f=v2 IPv6 Lower Memory +espino.menu.ip.lm6f.build.lwip_include=lwip2/include +espino.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +espino.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +espino.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +espino.menu.ip.hb6f.build.lwip_include=lwip2/include +espino.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +espino.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +espino.menu.ip.hb1=v1.4 Higher Bandwidth +espino.menu.ip.hb1.build.lwip_lib=-llwip_gcc +espino.menu.ip.hb1.build.lwip_flags=-DLWIP_OPEN_SRC +espino.menu.ip.src=v1.4 Compile from source +espino.menu.ip.src.build.lwip_lib=-llwip_src +espino.menu.ip.src.build.lwip_flags=-DLWIP_OPEN_SRC +espino.menu.ip.src.recipe.hooks.sketch.prebuild.1.pattern=make -C "{runtime.platform.path}/tools/sdk/lwip/src" install TOOLS_PATH="{runtime.tools.xtensa-lx106-elf-gcc.path}/bin/xtensa-lx106-elf-" +espino.menu.dbg.Disabled=Disabled +espino.menu.dbg.Disabled.build.debug_port= +espino.menu.dbg.Serial=Serial +espino.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +espino.menu.dbg.Serial1=Serial1 +espino.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +espino.menu.lvl.None____=None +espino.menu.lvl.None____.build.debug_level= +espino.menu.lvl.SSL=SSL +espino.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +espino.menu.lvl.TLS_MEM=TLS_MEM +espino.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +espino.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +espino.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +espino.menu.lvl.HTTP_SERVER=HTTP_SERVER +espino.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +espino.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +espino.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +espino.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +espino.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +espino.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +espino.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +espino.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +espino.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +espino.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +espino.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +espino.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +espino.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +espino.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +espino.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +espino.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +espino.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +espino.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +espino.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +espino.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +espino.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +espino.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +espino.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +espino.menu.lvl.CORE=CORE +espino.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +espino.menu.lvl.WIFI=WIFI +espino.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +espino.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +espino.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +espino.menu.lvl.UPDATER=UPDATER +espino.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +espino.menu.lvl.OTA=OTA +espino.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +espino.menu.lvl.OOM=OOM +espino.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +espino.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOM=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM +espino.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOM.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM +espino.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM +espino.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM +espino.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +espino.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +espino.menu.wipe.none=Only Sketch +espino.menu.wipe.none.upload.erase_cmd= +espino.menu.wipe.sdk=Sketch + WiFi Settings +espino.menu.wipe.sdk.upload.erase_cmd=-ca "{build.rfcal_addr}" -cz 0x4000 +espino.menu.wipe.all=All Flash Contents +espino.menu.wipe.all.upload.erase_cmd=-ca 0x0 -cz "{build.flash_size_bytes}" +espino.menu.baud.115200=115200 +espino.menu.baud.115200.upload.speed=115200 +espino.menu.baud.9600=9600 +espino.menu.baud.9600.upload.speed=9600 +espino.menu.baud.57600=57600 +espino.menu.baud.57600.upload.speed=57600 +espino.menu.baud.230400.linux=230400 +espino.menu.baud.230400.macosx=230400 +espino.menu.baud.230400.upload.speed=230400 +espino.menu.baud.256000.windows=256000 +espino.menu.baud.256000.upload.speed=256000 +espino.menu.baud.460800.linux=460800 +espino.menu.baud.460800.macosx=460800 +espino.menu.baud.460800.upload.speed=460800 +espino.menu.baud.512000.windows=512000 +espino.menu.baud.512000.upload.speed=512000 +espino.menu.baud.921600=921600 +espino.menu.baud.921600.upload.speed=921600 + +############################################################## +espinotee.name=ThaiEasyElec's ESPino +espinotee.build.board=ESP8266_ESP13 +espinotee.build.variant=espinotee +espinotee.upload.tool=esptool +espinotee.upload.maximum_data_size=81920 +espinotee.upload.wait_for_upload_port=true +espinotee.upload.erase_cmd= +espinotee.serial.disableDTR=true +espinotee.serial.disableRTS=true +espinotee.build.mcu=esp8266 +espinotee.build.core=esp8266 +espinotee.build.spiffs_pagesize=256 +espinotee.build.debug_port= +espinotee.build.debug_level= +espinotee.menu.xtal.80=80 MHz +espinotee.menu.xtal.80.build.f_cpu=80000000L +espinotee.menu.xtal.160=160 MHz +espinotee.menu.xtal.160.build.f_cpu=160000000L +espinotee.menu.vt.flash=Flash +espinotee.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +espinotee.menu.vt.heap=Heap +espinotee.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +espinotee.menu.vt.iram=IRAM +espinotee.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +espinotee.menu.exception.enabled=Enabled +espinotee.menu.exception.enabled.build.exception_flags=-fexceptions +espinotee.menu.exception.enabled.build.stdcpp_lib=-lstdc++ +espinotee.menu.exception.disabled=Disabled +espinotee.menu.exception.disabled.build.exception_flags=-fno-exceptions +espinotee.menu.exception.disabled.build.stdcpp_lib=-lstdc++-nox +espinotee.upload.resetmethod=nodemcu +espinotee.build.flash_mode=qio +espinotee.build.flash_freq=40 +espinotee.menu.eesz.4M=4M (no SPIFFS) +espinotee.menu.eesz.4M.build.flash_size=4M +espinotee.menu.eesz.4M.build.flash_size_bytes=0x400000 +espinotee.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld +espinotee.menu.eesz.4M.build.spiffs_pagesize=256 +espinotee.menu.eesz.4M.upload.maximum_size=1044464 +espinotee.menu.eesz.4M.build.rfcal_addr=0x3FC000 +espinotee.menu.eesz.4M1M=4M (1M SPIFFS) +espinotee.menu.eesz.4M1M.build.flash_size=4M +espinotee.menu.eesz.4M1M.build.flash_size_bytes=0x400000 +espinotee.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld +espinotee.menu.eesz.4M1M.build.spiffs_pagesize=256 +espinotee.menu.eesz.4M1M.upload.maximum_size=1044464 +espinotee.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 +espinotee.menu.eesz.4M1M.build.spiffs_start=0x300000 +espinotee.menu.eesz.4M1M.build.spiffs_end=0x3FB000 +espinotee.menu.eesz.4M1M.build.spiffs_blocksize=8192 +espinotee.menu.eesz.4M2M=4M (2M SPIFFS) +espinotee.menu.eesz.4M2M.build.flash_size=4M +espinotee.menu.eesz.4M2M.build.flash_size_bytes=0x400000 +espinotee.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld +espinotee.menu.eesz.4M2M.build.spiffs_pagesize=256 +espinotee.menu.eesz.4M2M.upload.maximum_size=1044464 +espinotee.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 +espinotee.menu.eesz.4M2M.build.spiffs_start=0x200000 +espinotee.menu.eesz.4M2M.build.spiffs_end=0x3FB000 +espinotee.menu.eesz.4M2M.build.spiffs_blocksize=8192 +espinotee.menu.eesz.4M3M=4M (3M SPIFFS) +espinotee.menu.eesz.4M3M.build.flash_size=4M +espinotee.menu.eesz.4M3M.build.flash_size_bytes=0x400000 +espinotee.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld +espinotee.menu.eesz.4M3M.build.spiffs_pagesize=256 +espinotee.menu.eesz.4M3M.upload.maximum_size=1044464 +espinotee.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 +espinotee.menu.eesz.4M3M.build.spiffs_start=0x100000 +espinotee.menu.eesz.4M3M.build.spiffs_end=0x3FB000 +espinotee.menu.eesz.4M3M.build.spiffs_blocksize=8192 +espinotee.menu.ip.lm2f=v2 Lower Memory +espinotee.menu.ip.lm2f.build.lwip_include=lwip2/include +espinotee.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +espinotee.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +espinotee.menu.ip.hb2f=v2 Higher Bandwidth +espinotee.menu.ip.hb2f.build.lwip_include=lwip2/include +espinotee.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +espinotee.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +espinotee.menu.ip.lm2n=v2 Lower Memory (no features) +espinotee.menu.ip.lm2n.build.lwip_include=lwip2/include +espinotee.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +espinotee.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +espinotee.menu.ip.hb2n=v2 Higher Bandwidth (no features) +espinotee.menu.ip.hb2n.build.lwip_include=lwip2/include +espinotee.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +espinotee.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +espinotee.menu.ip.lm6f=v2 IPv6 Lower Memory +espinotee.menu.ip.lm6f.build.lwip_include=lwip2/include +espinotee.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +espinotee.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +espinotee.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +espinotee.menu.ip.hb6f.build.lwip_include=lwip2/include +espinotee.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +espinotee.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +espinotee.menu.ip.hb1=v1.4 Higher Bandwidth +espinotee.menu.ip.hb1.build.lwip_lib=-llwip_gcc +espinotee.menu.ip.hb1.build.lwip_flags=-DLWIP_OPEN_SRC +espinotee.menu.ip.src=v1.4 Compile from source +espinotee.menu.ip.src.build.lwip_lib=-llwip_src +espinotee.menu.ip.src.build.lwip_flags=-DLWIP_OPEN_SRC +espinotee.menu.ip.src.recipe.hooks.sketch.prebuild.1.pattern=make -C "{runtime.platform.path}/tools/sdk/lwip/src" install TOOLS_PATH="{runtime.tools.xtensa-lx106-elf-gcc.path}/bin/xtensa-lx106-elf-" +espinotee.menu.dbg.Disabled=Disabled +espinotee.menu.dbg.Disabled.build.debug_port= +espinotee.menu.dbg.Serial=Serial +espinotee.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +espinotee.menu.dbg.Serial1=Serial1 +espinotee.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +espinotee.menu.lvl.None____=None +espinotee.menu.lvl.None____.build.debug_level= +espinotee.menu.lvl.SSL=SSL +espinotee.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +espinotee.menu.lvl.TLS_MEM=TLS_MEM +espinotee.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +espinotee.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +espinotee.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +espinotee.menu.lvl.HTTP_SERVER=HTTP_SERVER +espinotee.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +espinotee.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +espinotee.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +espinotee.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +espinotee.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +espinotee.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +espinotee.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +espinotee.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +espinotee.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +espinotee.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +espinotee.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +espinotee.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +espinotee.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +espinotee.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +espinotee.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +espinotee.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +espinotee.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +espinotee.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +espinotee.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +espinotee.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +espinotee.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +espinotee.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +espinotee.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +espinotee.menu.lvl.CORE=CORE +espinotee.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +espinotee.menu.lvl.WIFI=WIFI +espinotee.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +espinotee.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +espinotee.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +espinotee.menu.lvl.UPDATER=UPDATER +espinotee.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +espinotee.menu.lvl.OTA=OTA +espinotee.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +espinotee.menu.lvl.OOM=OOM +espinotee.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +espinotee.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOM=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM +espinotee.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOM.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM +espinotee.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM +espinotee.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM +espinotee.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +espinotee.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +espinotee.menu.wipe.none=Only Sketch +espinotee.menu.wipe.none.upload.erase_cmd= +espinotee.menu.wipe.sdk=Sketch + WiFi Settings +espinotee.menu.wipe.sdk.upload.erase_cmd=-ca "{build.rfcal_addr}" -cz 0x4000 +espinotee.menu.wipe.all=All Flash Contents +espinotee.menu.wipe.all.upload.erase_cmd=-ca 0x0 -cz "{build.flash_size_bytes}" +espinotee.menu.baud.115200=115200 +espinotee.menu.baud.115200.upload.speed=115200 +espinotee.menu.baud.9600=9600 +espinotee.menu.baud.9600.upload.speed=9600 +espinotee.menu.baud.57600=57600 +espinotee.menu.baud.57600.upload.speed=57600 +espinotee.menu.baud.230400.linux=230400 +espinotee.menu.baud.230400.macosx=230400 +espinotee.menu.baud.230400.upload.speed=230400 +espinotee.menu.baud.256000.windows=256000 +espinotee.menu.baud.256000.upload.speed=256000 +espinotee.menu.baud.460800.linux=460800 +espinotee.menu.baud.460800.macosx=460800 +espinotee.menu.baud.460800.upload.speed=460800 +espinotee.menu.baud.512000.windows=512000 +espinotee.menu.baud.512000.upload.speed=512000 +espinotee.menu.baud.921600=921600 +espinotee.menu.baud.921600.upload.speed=921600 + +############################################################## +wifinfo.name=WifInfo +wifinfo.menu.ESPModule.ESP12.build.board=ESP8266_ESP12 +wifinfo.menu.ESPModule.ESP12.upload.maximum_size=1044464 +wifinfo.menu.ESPModule.ESP12.build.spiffs_pagesize=256 +wifinfo.menu.ESPModule.ESP12.build.flash_ld=eagle.flash.4m1m.ld +wifinfo.menu.ESPModule.ESP07192.build.spiffs_blocksize=4096 +wifinfo.menu.ESPModule.ESP07192.build.spiffs_end=0xFB000 +wifinfo.menu.ESPModule.ESP12=ESP12 (4M/1M SPIFFS) +wifinfo.menu.ESPModule.ESP12.build.spiffs_start=0x300000 +wifinfo.menu.ESPModule.ESP12.build.spiffs_end=0x3FB000 +wifinfo.menu.ESPModule.ESP07192.build.spiffs_start=0xCB000 +wifinfo.menu.ESPModule.ESP07192.build.board=ESP8266_ESP07 +wifinfo.menu.ESPModule.ESP12.build.spiffs_blocksize=8192 +wifinfo.menu.ESPModule.ESP12.build.flash_size=4M +wifinfo.build.board=WIFINFO +wifinfo.build.variant=wifinfo +wifinfo.menu.ESPModule.ESP07192.build.flash_ld=eagle.flash.1m192.ld +wifinfo.menu.ESPModule.ESP07192.build.flash_size=1M +wifinfo.menu.ESPModule.ESP07192=ESP07 (1M/192K SPIFFS) +wifinfo.menu.ESPModule.ESP07192.upload.maximum_size=827376 +wifinfo.upload.tool=esptool +wifinfo.upload.maximum_data_size=81920 +wifinfo.upload.wait_for_upload_port=true +wifinfo.upload.erase_cmd= +wifinfo.serial.disableDTR=true +wifinfo.serial.disableRTS=true +wifinfo.build.mcu=esp8266 +wifinfo.build.core=esp8266 +wifinfo.build.spiffs_pagesize=256 +wifinfo.build.debug_port= +wifinfo.build.debug_level= +wifinfo.menu.xtal.80=80 MHz +wifinfo.menu.xtal.80.build.f_cpu=80000000L +wifinfo.menu.xtal.160=160 MHz +wifinfo.menu.xtal.160.build.f_cpu=160000000L +wifinfo.menu.vt.flash=Flash +wifinfo.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +wifinfo.menu.vt.heap=Heap +wifinfo.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +wifinfo.menu.vt.iram=IRAM +wifinfo.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +wifinfo.menu.exception.enabled=Enabled +wifinfo.menu.exception.enabled.build.exception_flags=-fexceptions +wifinfo.menu.exception.enabled.build.stdcpp_lib=-lstdc++ +wifinfo.menu.exception.disabled=Disabled +wifinfo.menu.exception.disabled.build.exception_flags=-fno-exceptions +wifinfo.menu.exception.disabled.build.stdcpp_lib=-lstdc++-nox +wifinfo.upload.resetmethod=nodemcu +wifinfo.build.flash_mode=qio +wifinfo.menu.FlashFreq.40=40MHz +wifinfo.menu.FlashFreq.40.build.flash_freq=40 +wifinfo.menu.FlashFreq.80=80MHz +wifinfo.menu.FlashFreq.80.build.flash_freq=80 +wifinfo.menu.eesz.1M=1M (no SPIFFS) +wifinfo.menu.eesz.1M.build.flash_size=1M +wifinfo.menu.eesz.1M.build.flash_size_bytes=0x100000 +wifinfo.menu.eesz.1M.build.flash_ld=eagle.flash.1m.ld +wifinfo.menu.eesz.1M.build.spiffs_pagesize=256 +wifinfo.menu.eesz.1M.upload.maximum_size=1023984 +wifinfo.menu.eesz.1M.build.rfcal_addr=0xFC000 +wifinfo.menu.eesz.1M64=1M (64K SPIFFS) +wifinfo.menu.eesz.1M64.build.flash_size=1M +wifinfo.menu.eesz.1M64.build.flash_size_bytes=0x100000 +wifinfo.menu.eesz.1M64.build.flash_ld=eagle.flash.1m64.ld +wifinfo.menu.eesz.1M64.build.spiffs_pagesize=256 +wifinfo.menu.eesz.1M64.upload.maximum_size=958448 +wifinfo.menu.eesz.1M64.build.rfcal_addr=0xFC000 +wifinfo.menu.eesz.1M64.build.spiffs_start=0xEB000 +wifinfo.menu.eesz.1M64.build.spiffs_end=0xFB000 +wifinfo.menu.eesz.1M64.build.spiffs_blocksize=4096 +wifinfo.menu.eesz.1M128=1M (128K SPIFFS) +wifinfo.menu.eesz.1M128.build.flash_size=1M +wifinfo.menu.eesz.1M128.build.flash_size_bytes=0x100000 +wifinfo.menu.eesz.1M128.build.flash_ld=eagle.flash.1m128.ld +wifinfo.menu.eesz.1M128.build.spiffs_pagesize=256 +wifinfo.menu.eesz.1M128.upload.maximum_size=892912 +wifinfo.menu.eesz.1M128.build.rfcal_addr=0xFC000 +wifinfo.menu.eesz.1M128.build.spiffs_start=0xDB000 +wifinfo.menu.eesz.1M128.build.spiffs_end=0xFB000 +wifinfo.menu.eesz.1M128.build.spiffs_blocksize=4096 +wifinfo.menu.eesz.1M144=1M (144K SPIFFS) +wifinfo.menu.eesz.1M144.build.flash_size=1M +wifinfo.menu.eesz.1M144.build.flash_size_bytes=0x100000 +wifinfo.menu.eesz.1M144.build.flash_ld=eagle.flash.1m144.ld +wifinfo.menu.eesz.1M144.build.spiffs_pagesize=256 +wifinfo.menu.eesz.1M144.upload.maximum_size=876528 +wifinfo.menu.eesz.1M144.build.rfcal_addr=0xFC000 +wifinfo.menu.eesz.1M144.build.spiffs_start=0xD7000 +wifinfo.menu.eesz.1M144.build.spiffs_end=0xFB000 +wifinfo.menu.eesz.1M144.build.spiffs_blocksize=4096 +wifinfo.menu.eesz.1M160=1M (160K SPIFFS) +wifinfo.menu.eesz.1M160.build.flash_size=1M +wifinfo.menu.eesz.1M160.build.flash_size_bytes=0x100000 +wifinfo.menu.eesz.1M160.build.flash_ld=eagle.flash.1m160.ld +wifinfo.menu.eesz.1M160.build.spiffs_pagesize=256 +wifinfo.menu.eesz.1M160.upload.maximum_size=860144 +wifinfo.menu.eesz.1M160.build.rfcal_addr=0xFC000 +wifinfo.menu.eesz.1M160.build.spiffs_start=0xD3000 +wifinfo.menu.eesz.1M160.build.spiffs_end=0xFB000 +wifinfo.menu.eesz.1M160.build.spiffs_blocksize=4096 +wifinfo.menu.eesz.1M192=1M (192K SPIFFS) +wifinfo.menu.eesz.1M192.build.flash_size=1M +wifinfo.menu.eesz.1M192.build.flash_size_bytes=0x100000 +wifinfo.menu.eesz.1M192.build.flash_ld=eagle.flash.1m192.ld +wifinfo.menu.eesz.1M192.build.spiffs_pagesize=256 +wifinfo.menu.eesz.1M192.upload.maximum_size=827376 +wifinfo.menu.eesz.1M192.build.rfcal_addr=0xFC000 +wifinfo.menu.eesz.1M192.build.spiffs_start=0xCB000 +wifinfo.menu.eesz.1M192.build.spiffs_end=0xFB000 +wifinfo.menu.eesz.1M192.build.spiffs_blocksize=4096 +wifinfo.menu.eesz.1M256=1M (256K SPIFFS) +wifinfo.menu.eesz.1M256.build.flash_size=1M +wifinfo.menu.eesz.1M256.build.flash_size_bytes=0x100000 +wifinfo.menu.eesz.1M256.build.flash_ld=eagle.flash.1m256.ld +wifinfo.menu.eesz.1M256.build.spiffs_pagesize=256 +wifinfo.menu.eesz.1M256.upload.maximum_size=761840 +wifinfo.menu.eesz.1M256.build.rfcal_addr=0xFC000 +wifinfo.menu.eesz.1M256.build.spiffs_start=0xBB000 +wifinfo.menu.eesz.1M256.build.spiffs_end=0xFB000 +wifinfo.menu.eesz.1M256.build.spiffs_blocksize=4096 +wifinfo.menu.eesz.1M512=1M (512K SPIFFS) +wifinfo.menu.eesz.1M512.build.flash_size=1M +wifinfo.menu.eesz.1M512.build.flash_size_bytes=0x100000 +wifinfo.menu.eesz.1M512.build.flash_ld=eagle.flash.1m512.ld +wifinfo.menu.eesz.1M512.build.spiffs_pagesize=256 +wifinfo.menu.eesz.1M512.upload.maximum_size=499696 +wifinfo.menu.eesz.1M512.build.rfcal_addr=0xFC000 +wifinfo.menu.eesz.1M512.build.spiffs_start=0x7B000 +wifinfo.menu.eesz.1M512.build.spiffs_end=0xFB000 +wifinfo.menu.eesz.1M512.build.spiffs_blocksize=4096 +wifinfo.menu.ip.lm2f=v2 Lower Memory +wifinfo.menu.ip.lm2f.build.lwip_include=lwip2/include +wifinfo.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +wifinfo.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +wifinfo.menu.ip.hb2f=v2 Higher Bandwidth +wifinfo.menu.ip.hb2f.build.lwip_include=lwip2/include +wifinfo.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +wifinfo.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +wifinfo.menu.ip.lm2n=v2 Lower Memory (no features) +wifinfo.menu.ip.lm2n.build.lwip_include=lwip2/include +wifinfo.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +wifinfo.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +wifinfo.menu.ip.hb2n=v2 Higher Bandwidth (no features) +wifinfo.menu.ip.hb2n.build.lwip_include=lwip2/include +wifinfo.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +wifinfo.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +wifinfo.menu.ip.lm6f=v2 IPv6 Lower Memory +wifinfo.menu.ip.lm6f.build.lwip_include=lwip2/include +wifinfo.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +wifinfo.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +wifinfo.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +wifinfo.menu.ip.hb6f.build.lwip_include=lwip2/include +wifinfo.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +wifinfo.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +wifinfo.menu.ip.hb1=v1.4 Higher Bandwidth +wifinfo.menu.ip.hb1.build.lwip_lib=-llwip_gcc +wifinfo.menu.ip.hb1.build.lwip_flags=-DLWIP_OPEN_SRC +wifinfo.menu.ip.src=v1.4 Compile from source +wifinfo.menu.ip.src.build.lwip_lib=-llwip_src +wifinfo.menu.ip.src.build.lwip_flags=-DLWIP_OPEN_SRC +wifinfo.menu.ip.src.recipe.hooks.sketch.prebuild.1.pattern=make -C "{runtime.platform.path}/tools/sdk/lwip/src" install TOOLS_PATH="{runtime.tools.xtensa-lx106-elf-gcc.path}/bin/xtensa-lx106-elf-" +wifinfo.menu.dbg.Disabled=Disabled +wifinfo.menu.dbg.Disabled.build.debug_port= +wifinfo.menu.dbg.Serial=Serial +wifinfo.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +wifinfo.menu.dbg.Serial1=Serial1 +wifinfo.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +wifinfo.menu.lvl.None____=None +wifinfo.menu.lvl.None____.build.debug_level= +wifinfo.menu.lvl.SSL=SSL +wifinfo.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +wifinfo.menu.lvl.TLS_MEM=TLS_MEM +wifinfo.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +wifinfo.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +wifinfo.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +wifinfo.menu.lvl.HTTP_SERVER=HTTP_SERVER +wifinfo.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +wifinfo.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +wifinfo.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +wifinfo.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +wifinfo.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +wifinfo.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +wifinfo.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +wifinfo.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +wifinfo.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +wifinfo.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +wifinfo.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +wifinfo.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +wifinfo.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +wifinfo.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +wifinfo.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +wifinfo.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +wifinfo.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +wifinfo.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +wifinfo.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +wifinfo.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +wifinfo.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +wifinfo.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +wifinfo.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +wifinfo.menu.lvl.CORE=CORE +wifinfo.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +wifinfo.menu.lvl.WIFI=WIFI +wifinfo.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +wifinfo.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +wifinfo.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +wifinfo.menu.lvl.UPDATER=UPDATER +wifinfo.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +wifinfo.menu.lvl.OTA=OTA +wifinfo.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +wifinfo.menu.lvl.OOM=OOM +wifinfo.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +wifinfo.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOM=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM +wifinfo.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOM.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM +wifinfo.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM +wifinfo.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM +wifinfo.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +wifinfo.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +wifinfo.menu.wipe.none=Only Sketch +wifinfo.menu.wipe.none.upload.erase_cmd= +wifinfo.menu.wipe.sdk=Sketch + WiFi Settings +wifinfo.menu.wipe.sdk.upload.erase_cmd=-ca "{build.rfcal_addr}" -cz 0x4000 +wifinfo.menu.wipe.all=All Flash Contents +wifinfo.menu.wipe.all.upload.erase_cmd=-ca 0x0 -cz "{build.flash_size_bytes}" +wifinfo.menu.baud.115200=115200 +wifinfo.menu.baud.115200.upload.speed=115200 +wifinfo.menu.baud.9600=9600 +wifinfo.menu.baud.9600.upload.speed=9600 +wifinfo.menu.baud.57600=57600 +wifinfo.menu.baud.57600.upload.speed=57600 +wifinfo.menu.baud.230400.linux=230400 +wifinfo.menu.baud.230400.macosx=230400 +wifinfo.menu.baud.230400.upload.speed=230400 +wifinfo.menu.baud.256000.windows=256000 +wifinfo.menu.baud.256000.upload.speed=256000 +wifinfo.menu.baud.460800.linux=460800 +wifinfo.menu.baud.460800.macosx=460800 +wifinfo.menu.baud.460800.upload.speed=460800 +wifinfo.menu.baud.512000.windows=512000 +wifinfo.menu.baud.512000.upload.speed=512000 +wifinfo.menu.baud.921600=921600 +wifinfo.menu.baud.921600.upload.speed=921600 + +############################################################## +arduino-esp8266.name=Arduino +arduino-esp8266.menu.BoardModel.starottodeved.build.board=ESP8266_ARDUINO_STAR_OTTO +arduino-esp8266.menu.BoardModel.primo.build.extra_flags=-DF_CRYSTAL=40000000 -DESP8266 +arduino-esp8266.menu.BoardModel.starottodeved.build.extra_flags=-DF_CRYSTAL=40000000 -DESP8266 +arduino-esp8266.menu.BoardModel.starottodeved.build.variant=arduino_uart +arduino-esp8266.menu.BoardModel.unowifideved.build.board=ESP8266_ARDUINO_UNOWIFI +arduino-esp8266.menu.BoardModel.unowifideved.build.extra_flags=-DF_CRYSTAL=40000000 -DESP8266 +arduino-esp8266.menu.BoardModel.primo=Primo +arduino-esp8266.menu.BoardModel.unowifideved.build.variant=arduino_uart +arduino-esp8266.menu.BoardModel.primo.build.variant=arduino_spi +arduino-esp8266.menu.BoardModel.starottodeved=Star OTTO +arduino-esp8266.build.board=ESP8266_ARDUINO +arduino-esp8266.menu.BoardModel.primo.build.board=ESP8266_ARDUINO_PRIMO +arduino-esp8266.menu.BoardModel.unowifideved=Uno WiFi +arduino-esp8266.upload.tool=esptool +arduino-esp8266.upload.maximum_data_size=81920 +arduino-esp8266.upload.wait_for_upload_port=true +arduino-esp8266.upload.erase_cmd= +arduino-esp8266.serial.disableDTR=true +arduino-esp8266.serial.disableRTS=true +arduino-esp8266.build.mcu=esp8266 +arduino-esp8266.build.core=esp8266 +arduino-esp8266.build.variant=generic +arduino-esp8266.build.spiffs_pagesize=256 +arduino-esp8266.build.debug_port= +arduino-esp8266.build.debug_level= +arduino-esp8266.menu.xtal.80=80 MHz +arduino-esp8266.menu.xtal.80.build.f_cpu=80000000L +arduino-esp8266.menu.xtal.160=160 MHz +arduino-esp8266.menu.xtal.160.build.f_cpu=160000000L +arduino-esp8266.menu.vt.flash=Flash +arduino-esp8266.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +arduino-esp8266.menu.vt.heap=Heap +arduino-esp8266.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +arduino-esp8266.menu.vt.iram=IRAM +arduino-esp8266.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +arduino-esp8266.menu.exception.enabled=Enabled +arduino-esp8266.menu.exception.enabled.build.exception_flags=-fexceptions +arduino-esp8266.menu.exception.enabled.build.stdcpp_lib=-lstdc++ +arduino-esp8266.menu.exception.disabled=Disabled +arduino-esp8266.menu.exception.disabled.build.exception_flags=-fno-exceptions +arduino-esp8266.menu.exception.disabled.build.stdcpp_lib=-lstdc++-nox +arduino-esp8266.upload.resetmethod=ck +arduino-esp8266.build.flash_mode=qio +arduino-esp8266.build.flash_freq=40 +arduino-esp8266.menu.eesz.4M=4M (no SPIFFS) +arduino-esp8266.menu.eesz.4M.build.flash_size=4M +arduino-esp8266.menu.eesz.4M.build.flash_size_bytes=0x400000 +arduino-esp8266.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld +arduino-esp8266.menu.eesz.4M.build.spiffs_pagesize=256 +arduino-esp8266.menu.eesz.4M.upload.maximum_size=1044464 +arduino-esp8266.menu.eesz.4M.build.rfcal_addr=0x3FC000 +arduino-esp8266.menu.eesz.4M1M=4M (1M SPIFFS) +arduino-esp8266.menu.eesz.4M1M.build.flash_size=4M +arduino-esp8266.menu.eesz.4M1M.build.flash_size_bytes=0x400000 +arduino-esp8266.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld +arduino-esp8266.menu.eesz.4M1M.build.spiffs_pagesize=256 +arduino-esp8266.menu.eesz.4M1M.upload.maximum_size=1044464 +arduino-esp8266.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 +arduino-esp8266.menu.eesz.4M1M.build.spiffs_start=0x300000 +arduino-esp8266.menu.eesz.4M1M.build.spiffs_end=0x3FB000 +arduino-esp8266.menu.eesz.4M1M.build.spiffs_blocksize=8192 +arduino-esp8266.menu.eesz.4M2M=4M (2M SPIFFS) +arduino-esp8266.menu.eesz.4M2M.build.flash_size=4M +arduino-esp8266.menu.eesz.4M2M.build.flash_size_bytes=0x400000 +arduino-esp8266.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld +arduino-esp8266.menu.eesz.4M2M.build.spiffs_pagesize=256 +arduino-esp8266.menu.eesz.4M2M.upload.maximum_size=1044464 +arduino-esp8266.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 +arduino-esp8266.menu.eesz.4M2M.build.spiffs_start=0x200000 +arduino-esp8266.menu.eesz.4M2M.build.spiffs_end=0x3FB000 +arduino-esp8266.menu.eesz.4M2M.build.spiffs_blocksize=8192 +arduino-esp8266.menu.eesz.4M3M=4M (3M SPIFFS) +arduino-esp8266.menu.eesz.4M3M.build.flash_size=4M +arduino-esp8266.menu.eesz.4M3M.build.flash_size_bytes=0x400000 +arduino-esp8266.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld +arduino-esp8266.menu.eesz.4M3M.build.spiffs_pagesize=256 +arduino-esp8266.menu.eesz.4M3M.upload.maximum_size=1044464 +arduino-esp8266.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 +arduino-esp8266.menu.eesz.4M3M.build.spiffs_start=0x100000 +arduino-esp8266.menu.eesz.4M3M.build.spiffs_end=0x3FB000 +arduino-esp8266.menu.eesz.4M3M.build.spiffs_blocksize=8192 +arduino-esp8266.menu.ip.lm2f=v2 Lower Memory +arduino-esp8266.menu.ip.lm2f.build.lwip_include=lwip2/include +arduino-esp8266.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +arduino-esp8266.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +arduino-esp8266.menu.ip.hb2f=v2 Higher Bandwidth +arduino-esp8266.menu.ip.hb2f.build.lwip_include=lwip2/include +arduino-esp8266.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +arduino-esp8266.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +arduino-esp8266.menu.ip.lm2n=v2 Lower Memory (no features) +arduino-esp8266.menu.ip.lm2n.build.lwip_include=lwip2/include +arduino-esp8266.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +arduino-esp8266.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +arduino-esp8266.menu.ip.hb2n=v2 Higher Bandwidth (no features) +arduino-esp8266.menu.ip.hb2n.build.lwip_include=lwip2/include +arduino-esp8266.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +arduino-esp8266.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +arduino-esp8266.menu.ip.lm6f=v2 IPv6 Lower Memory +arduino-esp8266.menu.ip.lm6f.build.lwip_include=lwip2/include +arduino-esp8266.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +arduino-esp8266.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +arduino-esp8266.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +arduino-esp8266.menu.ip.hb6f.build.lwip_include=lwip2/include +arduino-esp8266.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +arduino-esp8266.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +arduino-esp8266.menu.ip.hb1=v1.4 Higher Bandwidth +arduino-esp8266.menu.ip.hb1.build.lwip_lib=-llwip_gcc +arduino-esp8266.menu.ip.hb1.build.lwip_flags=-DLWIP_OPEN_SRC +arduino-esp8266.menu.ip.src=v1.4 Compile from source +arduino-esp8266.menu.ip.src.build.lwip_lib=-llwip_src +arduino-esp8266.menu.ip.src.build.lwip_flags=-DLWIP_OPEN_SRC +arduino-esp8266.menu.ip.src.recipe.hooks.sketch.prebuild.1.pattern=make -C "{runtime.platform.path}/tools/sdk/lwip/src" install TOOLS_PATH="{runtime.tools.xtensa-lx106-elf-gcc.path}/bin/xtensa-lx106-elf-" +arduino-esp8266.menu.dbg.Disabled=Disabled +arduino-esp8266.menu.dbg.Disabled.build.debug_port= +arduino-esp8266.menu.dbg.Serial=Serial +arduino-esp8266.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +arduino-esp8266.menu.dbg.Serial1=Serial1 +arduino-esp8266.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +arduino-esp8266.menu.lvl.None____=None +arduino-esp8266.menu.lvl.None____.build.debug_level= +arduino-esp8266.menu.lvl.SSL=SSL +arduino-esp8266.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +arduino-esp8266.menu.lvl.TLS_MEM=TLS_MEM +arduino-esp8266.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +arduino-esp8266.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +arduino-esp8266.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +arduino-esp8266.menu.lvl.HTTP_SERVER=HTTP_SERVER +arduino-esp8266.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +arduino-esp8266.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +arduino-esp8266.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +arduino-esp8266.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +arduino-esp8266.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +arduino-esp8266.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +arduino-esp8266.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +arduino-esp8266.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +arduino-esp8266.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +arduino-esp8266.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +arduino-esp8266.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +arduino-esp8266.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +arduino-esp8266.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +arduino-esp8266.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +arduino-esp8266.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +arduino-esp8266.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +arduino-esp8266.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +arduino-esp8266.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +arduino-esp8266.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +arduino-esp8266.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +arduino-esp8266.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +arduino-esp8266.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +arduino-esp8266.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +arduino-esp8266.menu.lvl.CORE=CORE +arduino-esp8266.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +arduino-esp8266.menu.lvl.WIFI=WIFI +arduino-esp8266.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +arduino-esp8266.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +arduino-esp8266.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +arduino-esp8266.menu.lvl.UPDATER=UPDATER +arduino-esp8266.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +arduino-esp8266.menu.lvl.OTA=OTA +arduino-esp8266.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +arduino-esp8266.menu.lvl.OOM=OOM +arduino-esp8266.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +arduino-esp8266.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOM=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM +arduino-esp8266.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOM.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM +arduino-esp8266.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM +arduino-esp8266.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM +arduino-esp8266.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +arduino-esp8266.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +arduino-esp8266.menu.wipe.none=Only Sketch +arduino-esp8266.menu.wipe.none.upload.erase_cmd= +arduino-esp8266.menu.wipe.sdk=Sketch + WiFi Settings +arduino-esp8266.menu.wipe.sdk.upload.erase_cmd=-ca "{build.rfcal_addr}" -cz 0x4000 +arduino-esp8266.menu.wipe.all=All Flash Contents +arduino-esp8266.menu.wipe.all.upload.erase_cmd=-ca 0x0 -cz "{build.flash_size_bytes}" +arduino-esp8266.menu.baud.115200=115200 +arduino-esp8266.menu.baud.115200.upload.speed=115200 +arduino-esp8266.menu.baud.9600=9600 +arduino-esp8266.menu.baud.9600.upload.speed=9600 +arduino-esp8266.menu.baud.57600=57600 +arduino-esp8266.menu.baud.57600.upload.speed=57600 +arduino-esp8266.menu.baud.230400.linux=230400 +arduino-esp8266.menu.baud.230400.macosx=230400 +arduino-esp8266.menu.baud.230400.upload.speed=230400 +arduino-esp8266.menu.baud.256000.windows=256000 +arduino-esp8266.menu.baud.256000.upload.speed=256000 +arduino-esp8266.menu.baud.460800.linux=460800 +arduino-esp8266.menu.baud.460800.macosx=460800 +arduino-esp8266.menu.baud.460800.upload.speed=460800 +arduino-esp8266.menu.baud.512000.windows=512000 +arduino-esp8266.menu.baud.512000.upload.speed=512000 +arduino-esp8266.menu.baud.921600=921600 +arduino-esp8266.menu.baud.921600.upload.speed=921600 + +############################################################## +gen4iod.name=4D Systems gen4 IoD Range +gen4iod.build.board=GEN4_IOD +gen4iod.build.f_cpu=160000000L +gen4iod.build.variant=generic +gen4iod.upload.tool=esptool +gen4iod.upload.maximum_data_size=81920 +gen4iod.upload.wait_for_upload_port=true +gen4iod.upload.erase_cmd= +gen4iod.serial.disableDTR=true +gen4iod.serial.disableRTS=true +gen4iod.build.mcu=esp8266 +gen4iod.build.core=esp8266 +gen4iod.build.spiffs_pagesize=256 +gen4iod.build.debug_port= +gen4iod.build.debug_level= +gen4iod.menu.xtal.80=80 MHz +gen4iod.menu.xtal.80.build.f_cpu=80000000L +gen4iod.menu.xtal.160=160 MHz +gen4iod.menu.xtal.160.build.f_cpu=160000000L +gen4iod.menu.vt.flash=Flash +gen4iod.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +gen4iod.menu.vt.heap=Heap +gen4iod.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +gen4iod.menu.vt.iram=IRAM +gen4iod.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +gen4iod.menu.exception.enabled=Enabled +gen4iod.menu.exception.enabled.build.exception_flags=-fexceptions +gen4iod.menu.exception.enabled.build.stdcpp_lib=-lstdc++ +gen4iod.menu.exception.disabled=Disabled +gen4iod.menu.exception.disabled.build.exception_flags=-fno-exceptions +gen4iod.menu.exception.disabled.build.stdcpp_lib=-lstdc++-nox +gen4iod.upload.resetmethod=nodemcu +gen4iod.build.flash_mode=dio +gen4iod.build.flash_freq=80 +gen4iod.menu.eesz.512K=512K (no SPIFFS) +gen4iod.menu.eesz.512K.build.flash_size=512K +gen4iod.menu.eesz.512K.build.flash_size_bytes=0x80000 +gen4iod.menu.eesz.512K.build.flash_ld=eagle.flash.512k.ld +gen4iod.menu.eesz.512K.build.spiffs_pagesize=256 +gen4iod.menu.eesz.512K.upload.maximum_size=499696 +gen4iod.menu.eesz.512K.build.rfcal_addr=0x7C000 +gen4iod.menu.eesz.512K32=512K (32K SPIFFS) +gen4iod.menu.eesz.512K32.build.flash_size=512K +gen4iod.menu.eesz.512K32.build.flash_size_bytes=0x80000 +gen4iod.menu.eesz.512K32.build.flash_ld=eagle.flash.512k32.ld +gen4iod.menu.eesz.512K32.build.spiffs_pagesize=256 +gen4iod.menu.eesz.512K32.upload.maximum_size=466928 +gen4iod.menu.eesz.512K32.build.rfcal_addr=0x7C000 +gen4iod.menu.eesz.512K32.build.spiffs_start=0x73000 +gen4iod.menu.eesz.512K32.build.spiffs_end=0x7B000 +gen4iod.menu.eesz.512K32.build.spiffs_blocksize=4096 +gen4iod.menu.eesz.512K64=512K (64K SPIFFS) +gen4iod.menu.eesz.512K64.build.flash_size=512K +gen4iod.menu.eesz.512K64.build.flash_size_bytes=0x80000 +gen4iod.menu.eesz.512K64.build.flash_ld=eagle.flash.512k64.ld +gen4iod.menu.eesz.512K64.build.spiffs_pagesize=256 +gen4iod.menu.eesz.512K64.upload.maximum_size=434160 +gen4iod.menu.eesz.512K64.build.rfcal_addr=0x7C000 +gen4iod.menu.eesz.512K64.build.spiffs_start=0x6B000 +gen4iod.menu.eesz.512K64.build.spiffs_end=0x7B000 +gen4iod.menu.eesz.512K64.build.spiffs_blocksize=4096 +gen4iod.menu.eesz.512K128=512K (128K SPIFFS) +gen4iod.menu.eesz.512K128.build.flash_size=512K +gen4iod.menu.eesz.512K128.build.flash_size_bytes=0x80000 +gen4iod.menu.eesz.512K128.build.flash_ld=eagle.flash.512k128.ld +gen4iod.menu.eesz.512K128.build.spiffs_pagesize=256 +gen4iod.menu.eesz.512K128.upload.maximum_size=368624 +gen4iod.menu.eesz.512K128.build.rfcal_addr=0x7C000 +gen4iod.menu.eesz.512K128.build.spiffs_start=0x5B000 +gen4iod.menu.eesz.512K128.build.spiffs_end=0x7B000 +gen4iod.menu.eesz.512K128.build.spiffs_blocksize=4096 +gen4iod.menu.ip.lm2f=v2 Lower Memory +gen4iod.menu.ip.lm2f.build.lwip_include=lwip2/include +gen4iod.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +gen4iod.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +gen4iod.menu.ip.hb2f=v2 Higher Bandwidth +gen4iod.menu.ip.hb2f.build.lwip_include=lwip2/include +gen4iod.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +gen4iod.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +gen4iod.menu.ip.lm2n=v2 Lower Memory (no features) +gen4iod.menu.ip.lm2n.build.lwip_include=lwip2/include +gen4iod.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +gen4iod.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +gen4iod.menu.ip.hb2n=v2 Higher Bandwidth (no features) +gen4iod.menu.ip.hb2n.build.lwip_include=lwip2/include +gen4iod.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +gen4iod.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +gen4iod.menu.ip.lm6f=v2 IPv6 Lower Memory +gen4iod.menu.ip.lm6f.build.lwip_include=lwip2/include +gen4iod.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +gen4iod.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +gen4iod.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +gen4iod.menu.ip.hb6f.build.lwip_include=lwip2/include +gen4iod.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +gen4iod.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +gen4iod.menu.ip.hb1=v1.4 Higher Bandwidth +gen4iod.menu.ip.hb1.build.lwip_lib=-llwip_gcc +gen4iod.menu.ip.hb1.build.lwip_flags=-DLWIP_OPEN_SRC +gen4iod.menu.ip.src=v1.4 Compile from source +gen4iod.menu.ip.src.build.lwip_lib=-llwip_src +gen4iod.menu.ip.src.build.lwip_flags=-DLWIP_OPEN_SRC +gen4iod.menu.ip.src.recipe.hooks.sketch.prebuild.1.pattern=make -C "{runtime.platform.path}/tools/sdk/lwip/src" install TOOLS_PATH="{runtime.tools.xtensa-lx106-elf-gcc.path}/bin/xtensa-lx106-elf-" +gen4iod.menu.dbg.Disabled=Disabled +gen4iod.menu.dbg.Disabled.build.debug_port= +gen4iod.menu.dbg.Serial=Serial +gen4iod.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +gen4iod.menu.dbg.Serial1=Serial1 +gen4iod.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +gen4iod.menu.lvl.None____=None +gen4iod.menu.lvl.None____.build.debug_level= +gen4iod.menu.lvl.SSL=SSL +gen4iod.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +gen4iod.menu.lvl.TLS_MEM=TLS_MEM +gen4iod.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +gen4iod.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +gen4iod.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +gen4iod.menu.lvl.HTTP_SERVER=HTTP_SERVER +gen4iod.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +gen4iod.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +gen4iod.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +gen4iod.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +gen4iod.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +gen4iod.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +gen4iod.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +gen4iod.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +gen4iod.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +gen4iod.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +gen4iod.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +gen4iod.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +gen4iod.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +gen4iod.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +gen4iod.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +gen4iod.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +gen4iod.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +gen4iod.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +gen4iod.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +gen4iod.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +gen4iod.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +gen4iod.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +gen4iod.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +gen4iod.menu.lvl.CORE=CORE +gen4iod.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +gen4iod.menu.lvl.WIFI=WIFI +gen4iod.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +gen4iod.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +gen4iod.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +gen4iod.menu.lvl.UPDATER=UPDATER +gen4iod.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +gen4iod.menu.lvl.OTA=OTA +gen4iod.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +gen4iod.menu.lvl.OOM=OOM +gen4iod.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +gen4iod.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOM=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM +gen4iod.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOM.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM +gen4iod.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM +gen4iod.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM +gen4iod.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +gen4iod.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +gen4iod.menu.wipe.none=Only Sketch +gen4iod.menu.wipe.none.upload.erase_cmd= +gen4iod.menu.wipe.sdk=Sketch + WiFi Settings +gen4iod.menu.wipe.sdk.upload.erase_cmd=-ca "{build.rfcal_addr}" -cz 0x4000 +gen4iod.menu.wipe.all=All Flash Contents +gen4iod.menu.wipe.all.upload.erase_cmd=-ca 0x0 -cz "{build.flash_size_bytes}" +gen4iod.menu.baud.115200=115200 +gen4iod.menu.baud.115200.upload.speed=115200 +gen4iod.menu.baud.9600=9600 +gen4iod.menu.baud.9600.upload.speed=9600 +gen4iod.menu.baud.57600=57600 +gen4iod.menu.baud.57600.upload.speed=57600 +gen4iod.menu.baud.230400.linux=230400 +gen4iod.menu.baud.230400.macosx=230400 +gen4iod.menu.baud.230400.upload.speed=230400 +gen4iod.menu.baud.256000.windows=256000 +gen4iod.menu.baud.256000.upload.speed=256000 +gen4iod.menu.baud.460800.linux=460800 +gen4iod.menu.baud.460800.macosx=460800 +gen4iod.menu.baud.460800.upload.speed=460800 +gen4iod.menu.baud.512000.windows=512000 +gen4iod.menu.baud.512000.upload.speed=512000 +gen4iod.menu.baud.921600=921600 +gen4iod.menu.baud.921600.upload.speed=921600 + +############################################################## +oak.name=Digistump Oak +oak.build.board=ESP8266_OAK +oak.build.variant=oak +oak.upload.maximum_size=1040368 +oak.upload.tool=esptool +oak.upload.maximum_data_size=81920 +oak.upload.wait_for_upload_port=true +oak.upload.erase_cmd= +oak.serial.disableDTR=true +oak.serial.disableRTS=true +oak.build.mcu=esp8266 +oak.build.core=esp8266 +oak.build.spiffs_pagesize=256 +oak.build.debug_port= +oak.build.debug_level= +oak.menu.xtal.80=80 MHz +oak.menu.xtal.80.build.f_cpu=80000000L +oak.menu.xtal.160=160 MHz +oak.menu.xtal.160.build.f_cpu=160000000L +oak.menu.vt.flash=Flash +oak.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +oak.menu.vt.heap=Heap +oak.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +oak.menu.vt.iram=IRAM +oak.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +oak.menu.exception.enabled=Enabled +oak.menu.exception.enabled.build.exception_flags=-fexceptions +oak.menu.exception.enabled.build.stdcpp_lib=-lstdc++ +oak.menu.exception.disabled=Disabled +oak.menu.exception.disabled.build.exception_flags=-fno-exceptions +oak.menu.exception.disabled.build.stdcpp_lib=-lstdc++-nox +oak.upload.resetmethod=none +oak.build.flash_mode=dio +oak.build.flash_freq=40 +oak.menu.eesz.4M=4M (no SPIFFS) +oak.menu.eesz.4M.build.flash_size=4M +oak.menu.eesz.4M.build.flash_size_bytes=0x400000 +oak.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld +oak.menu.eesz.4M.build.spiffs_pagesize=256 +oak.menu.eesz.4M.upload.maximum_size=1044464 +oak.menu.eesz.4M.build.rfcal_addr=0x3FC000 +oak.menu.eesz.4M1M=4M (1M SPIFFS) +oak.menu.eesz.4M1M.build.flash_size=4M +oak.menu.eesz.4M1M.build.flash_size_bytes=0x400000 +oak.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld +oak.menu.eesz.4M1M.build.spiffs_pagesize=256 +oak.menu.eesz.4M1M.upload.maximum_size=1044464 +oak.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 +oak.menu.eesz.4M1M.build.spiffs_start=0x300000 +oak.menu.eesz.4M1M.build.spiffs_end=0x3FB000 +oak.menu.eesz.4M1M.build.spiffs_blocksize=8192 +oak.menu.eesz.4M2M=4M (2M SPIFFS) +oak.menu.eesz.4M2M.build.flash_size=4M +oak.menu.eesz.4M2M.build.flash_size_bytes=0x400000 +oak.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld +oak.menu.eesz.4M2M.build.spiffs_pagesize=256 +oak.menu.eesz.4M2M.upload.maximum_size=1044464 +oak.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 +oak.menu.eesz.4M2M.build.spiffs_start=0x200000 +oak.menu.eesz.4M2M.build.spiffs_end=0x3FB000 +oak.menu.eesz.4M2M.build.spiffs_blocksize=8192 +oak.menu.eesz.4M3M=4M (3M SPIFFS) +oak.menu.eesz.4M3M.build.flash_size=4M +oak.menu.eesz.4M3M.build.flash_size_bytes=0x400000 +oak.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld +oak.menu.eesz.4M3M.build.spiffs_pagesize=256 +oak.menu.eesz.4M3M.upload.maximum_size=1044464 +oak.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 +oak.menu.eesz.4M3M.build.spiffs_start=0x100000 +oak.menu.eesz.4M3M.build.spiffs_end=0x3FB000 +oak.menu.eesz.4M3M.build.spiffs_blocksize=8192 +oak.menu.ip.lm2f=v2 Lower Memory +oak.menu.ip.lm2f.build.lwip_include=lwip2/include +oak.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +oak.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +oak.menu.ip.hb2f=v2 Higher Bandwidth +oak.menu.ip.hb2f.build.lwip_include=lwip2/include +oak.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +oak.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +oak.menu.ip.lm2n=v2 Lower Memory (no features) +oak.menu.ip.lm2n.build.lwip_include=lwip2/include +oak.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +oak.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +oak.menu.ip.hb2n=v2 Higher Bandwidth (no features) +oak.menu.ip.hb2n.build.lwip_include=lwip2/include +oak.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +oak.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +oak.menu.ip.lm6f=v2 IPv6 Lower Memory +oak.menu.ip.lm6f.build.lwip_include=lwip2/include +oak.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +oak.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +oak.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +oak.menu.ip.hb6f.build.lwip_include=lwip2/include +oak.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +oak.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +oak.menu.ip.hb1=v1.4 Higher Bandwidth +oak.menu.ip.hb1.build.lwip_lib=-llwip_gcc +oak.menu.ip.hb1.build.lwip_flags=-DLWIP_OPEN_SRC +oak.menu.ip.src=v1.4 Compile from source +oak.menu.ip.src.build.lwip_lib=-llwip_src +oak.menu.ip.src.build.lwip_flags=-DLWIP_OPEN_SRC +oak.menu.ip.src.recipe.hooks.sketch.prebuild.1.pattern=make -C "{runtime.platform.path}/tools/sdk/lwip/src" install TOOLS_PATH="{runtime.tools.xtensa-lx106-elf-gcc.path}/bin/xtensa-lx106-elf-" +oak.menu.dbg.Disabled=Disabled +oak.menu.dbg.Disabled.build.debug_port= +oak.menu.dbg.Serial=Serial +oak.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +oak.menu.dbg.Serial1=Serial1 +oak.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +oak.menu.lvl.None____=None +oak.menu.lvl.None____.build.debug_level= +oak.menu.lvl.SSL=SSL +oak.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +oak.menu.lvl.TLS_MEM=TLS_MEM +oak.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +oak.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +oak.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +oak.menu.lvl.HTTP_SERVER=HTTP_SERVER +oak.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +oak.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +oak.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +oak.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +oak.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +oak.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +oak.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +oak.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +oak.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +oak.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +oak.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +oak.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +oak.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +oak.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +oak.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +oak.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +oak.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +oak.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +oak.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +oak.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +oak.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +oak.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +oak.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +oak.menu.lvl.CORE=CORE +oak.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +oak.menu.lvl.WIFI=WIFI +oak.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +oak.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +oak.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +oak.menu.lvl.UPDATER=UPDATER +oak.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +oak.menu.lvl.OTA=OTA +oak.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +oak.menu.lvl.OOM=OOM +oak.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +oak.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOM=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM +oak.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOM.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM +oak.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM +oak.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM +oak.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +oak.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +oak.menu.wipe.none=Only Sketch +oak.menu.wipe.none.upload.erase_cmd= +oak.menu.wipe.sdk=Sketch + WiFi Settings +oak.menu.wipe.sdk.upload.erase_cmd=-ca "{build.rfcal_addr}" -cz 0x4000 +oak.menu.wipe.all=All Flash Contents +oak.menu.wipe.all.upload.erase_cmd=-ca 0x0 -cz "{build.flash_size_bytes}" +oak.menu.baud.921600=921600 +oak.menu.baud.921600.upload.speed=921600 +oak.menu.baud.9600=9600 +oak.menu.baud.9600.upload.speed=9600 +oak.menu.baud.57600=57600 +oak.menu.baud.57600.upload.speed=57600 +oak.menu.baud.115200=115200 +oak.menu.baud.115200.upload.speed=115200 +oak.menu.baud.230400.linux=230400 +oak.menu.baud.230400.macosx=230400 +oak.menu.baud.230400.upload.speed=230400 +oak.menu.baud.256000.windows=256000 +oak.menu.baud.256000.upload.speed=256000 +oak.menu.baud.460800.linux=460800 +oak.menu.baud.460800.macosx=460800 +oak.menu.baud.460800.upload.speed=460800 +oak.menu.baud.512000.windows=512000 +oak.menu.baud.512000.upload.speed=512000 + +############################################################## +wifiduino.name=WiFiduino +wifiduino.build.board=WIFIDUINO_ESP8266 +wifiduino.build.variant=wifiduino +wifiduino.upload.tool=esptool +wifiduino.upload.maximum_data_size=81920 +wifiduino.upload.wait_for_upload_port=true +wifiduino.upload.erase_cmd= +wifiduino.serial.disableDTR=true +wifiduino.serial.disableRTS=true +wifiduino.build.mcu=esp8266 +wifiduino.build.core=esp8266 +wifiduino.build.spiffs_pagesize=256 +wifiduino.build.debug_port= +wifiduino.build.debug_level= +wifiduino.menu.xtal.80=80 MHz +wifiduino.menu.xtal.80.build.f_cpu=80000000L +wifiduino.menu.xtal.160=160 MHz +wifiduino.menu.xtal.160.build.f_cpu=160000000L +wifiduino.menu.vt.flash=Flash +wifiduino.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +wifiduino.menu.vt.heap=Heap +wifiduino.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +wifiduino.menu.vt.iram=IRAM +wifiduino.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +wifiduino.menu.exception.enabled=Enabled +wifiduino.menu.exception.enabled.build.exception_flags=-fexceptions +wifiduino.menu.exception.enabled.build.stdcpp_lib=-lstdc++ +wifiduino.menu.exception.disabled=Disabled +wifiduino.menu.exception.disabled.build.exception_flags=-fno-exceptions +wifiduino.menu.exception.disabled.build.stdcpp_lib=-lstdc++-nox +wifiduino.upload.resetmethod=nodemcu +wifiduino.build.flash_mode=dio +wifiduino.build.flash_freq=40 +wifiduino.menu.eesz.4M=4M (no SPIFFS) +wifiduino.menu.eesz.4M.build.flash_size=4M +wifiduino.menu.eesz.4M.build.flash_size_bytes=0x400000 +wifiduino.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld +wifiduino.menu.eesz.4M.build.spiffs_pagesize=256 +wifiduino.menu.eesz.4M.upload.maximum_size=1044464 +wifiduino.menu.eesz.4M.build.rfcal_addr=0x3FC000 +wifiduino.menu.eesz.4M1M=4M (1M SPIFFS) +wifiduino.menu.eesz.4M1M.build.flash_size=4M +wifiduino.menu.eesz.4M1M.build.flash_size_bytes=0x400000 +wifiduino.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld +wifiduino.menu.eesz.4M1M.build.spiffs_pagesize=256 +wifiduino.menu.eesz.4M1M.upload.maximum_size=1044464 +wifiduino.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 +wifiduino.menu.eesz.4M1M.build.spiffs_start=0x300000 +wifiduino.menu.eesz.4M1M.build.spiffs_end=0x3FB000 +wifiduino.menu.eesz.4M1M.build.spiffs_blocksize=8192 +wifiduino.menu.eesz.4M2M=4M (2M SPIFFS) +wifiduino.menu.eesz.4M2M.build.flash_size=4M +wifiduino.menu.eesz.4M2M.build.flash_size_bytes=0x400000 +wifiduino.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld +wifiduino.menu.eesz.4M2M.build.spiffs_pagesize=256 +wifiduino.menu.eesz.4M2M.upload.maximum_size=1044464 +wifiduino.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 +wifiduino.menu.eesz.4M2M.build.spiffs_start=0x200000 +wifiduino.menu.eesz.4M2M.build.spiffs_end=0x3FB000 +wifiduino.menu.eesz.4M2M.build.spiffs_blocksize=8192 +wifiduino.menu.eesz.4M3M=4M (3M SPIFFS) +wifiduino.menu.eesz.4M3M.build.flash_size=4M +wifiduino.menu.eesz.4M3M.build.flash_size_bytes=0x400000 +wifiduino.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld +wifiduino.menu.eesz.4M3M.build.spiffs_pagesize=256 +wifiduino.menu.eesz.4M3M.upload.maximum_size=1044464 +wifiduino.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 +wifiduino.menu.eesz.4M3M.build.spiffs_start=0x100000 +wifiduino.menu.eesz.4M3M.build.spiffs_end=0x3FB000 +wifiduino.menu.eesz.4M3M.build.spiffs_blocksize=8192 +wifiduino.menu.ip.lm2f=v2 Lower Memory +wifiduino.menu.ip.lm2f.build.lwip_include=lwip2/include +wifiduino.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +wifiduino.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +wifiduino.menu.ip.hb2f=v2 Higher Bandwidth +wifiduino.menu.ip.hb2f.build.lwip_include=lwip2/include +wifiduino.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +wifiduino.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +wifiduino.menu.ip.lm2n=v2 Lower Memory (no features) +wifiduino.menu.ip.lm2n.build.lwip_include=lwip2/include +wifiduino.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +wifiduino.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +wifiduino.menu.ip.hb2n=v2 Higher Bandwidth (no features) +wifiduino.menu.ip.hb2n.build.lwip_include=lwip2/include +wifiduino.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +wifiduino.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +wifiduino.menu.ip.lm6f=v2 IPv6 Lower Memory +wifiduino.menu.ip.lm6f.build.lwip_include=lwip2/include +wifiduino.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +wifiduino.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +wifiduino.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +wifiduino.menu.ip.hb6f.build.lwip_include=lwip2/include +wifiduino.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +wifiduino.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +wifiduino.menu.ip.hb1=v1.4 Higher Bandwidth +wifiduino.menu.ip.hb1.build.lwip_lib=-llwip_gcc +wifiduino.menu.ip.hb1.build.lwip_flags=-DLWIP_OPEN_SRC +wifiduino.menu.ip.src=v1.4 Compile from source +wifiduino.menu.ip.src.build.lwip_lib=-llwip_src +wifiduino.menu.ip.src.build.lwip_flags=-DLWIP_OPEN_SRC +wifiduino.menu.ip.src.recipe.hooks.sketch.prebuild.1.pattern=make -C "{runtime.platform.path}/tools/sdk/lwip/src" install TOOLS_PATH="{runtime.tools.xtensa-lx106-elf-gcc.path}/bin/xtensa-lx106-elf-" +wifiduino.menu.dbg.Disabled=Disabled +wifiduino.menu.dbg.Disabled.build.debug_port= +wifiduino.menu.dbg.Serial=Serial +wifiduino.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +wifiduino.menu.dbg.Serial1=Serial1 +wifiduino.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +wifiduino.menu.lvl.None____=None +wifiduino.menu.lvl.None____.build.debug_level= +wifiduino.menu.lvl.SSL=SSL +wifiduino.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +wifiduino.menu.lvl.TLS_MEM=TLS_MEM +wifiduino.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +wifiduino.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +wifiduino.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +wifiduino.menu.lvl.HTTP_SERVER=HTTP_SERVER +wifiduino.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +wifiduino.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +wifiduino.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +wifiduino.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +wifiduino.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +wifiduino.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +wifiduino.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +wifiduino.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +wifiduino.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +wifiduino.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +wifiduino.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +wifiduino.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +wifiduino.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +wifiduino.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +wifiduino.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +wifiduino.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +wifiduino.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +wifiduino.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +wifiduino.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +wifiduino.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +wifiduino.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +wifiduino.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +wifiduino.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +wifiduino.menu.lvl.CORE=CORE +wifiduino.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +wifiduino.menu.lvl.WIFI=WIFI +wifiduino.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +wifiduino.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +wifiduino.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +wifiduino.menu.lvl.UPDATER=UPDATER +wifiduino.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +wifiduino.menu.lvl.OTA=OTA +wifiduino.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +wifiduino.menu.lvl.OOM=OOM +wifiduino.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +wifiduino.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOM=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM +wifiduino.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOM.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM +wifiduino.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM +wifiduino.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM +wifiduino.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +wifiduino.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +wifiduino.menu.wipe.none=Only Sketch +wifiduino.menu.wipe.none.upload.erase_cmd= +wifiduino.menu.wipe.sdk=Sketch + WiFi Settings +wifiduino.menu.wipe.sdk.upload.erase_cmd=-ca "{build.rfcal_addr}" -cz 0x4000 +wifiduino.menu.wipe.all=All Flash Contents +wifiduino.menu.wipe.all.upload.erase_cmd=-ca 0x0 -cz "{build.flash_size_bytes}" +wifiduino.menu.baud.921600=921600 +wifiduino.menu.baud.921600.upload.speed=921600 +wifiduino.menu.baud.9600=9600 +wifiduino.menu.baud.9600.upload.speed=9600 +wifiduino.menu.baud.57600=57600 +wifiduino.menu.baud.57600.upload.speed=57600 +wifiduino.menu.baud.115200=115200 +wifiduino.menu.baud.115200.upload.speed=115200 +wifiduino.menu.baud.230400.linux=230400 +wifiduino.menu.baud.230400.macosx=230400 +wifiduino.menu.baud.230400.upload.speed=230400 +wifiduino.menu.baud.256000.windows=256000 +wifiduino.menu.baud.256000.upload.speed=256000 +wifiduino.menu.baud.460800.linux=460800 +wifiduino.menu.baud.460800.macosx=460800 +wifiduino.menu.baud.460800.upload.speed=460800 +wifiduino.menu.baud.512000.windows=512000 +wifiduino.menu.baud.512000.upload.speed=512000 + +############################################################## +wifi_slot.name=Amperka WiFi Slot +wifi_slot.build.board=AMPERKA_WIFI_SLOT +wifi_slot.build.variant=wifi_slot +wifi_slot.upload.tool=esptool +wifi_slot.upload.maximum_data_size=81920 +wifi_slot.upload.wait_for_upload_port=true +wifi_slot.upload.erase_cmd= +wifi_slot.serial.disableDTR=true +wifi_slot.serial.disableRTS=true +wifi_slot.build.mcu=esp8266 +wifi_slot.build.core=esp8266 +wifi_slot.build.spiffs_pagesize=256 +wifi_slot.build.debug_port= +wifi_slot.build.debug_level= +wifi_slot.menu.xtal.80=80 MHz +wifi_slot.menu.xtal.80.build.f_cpu=80000000L +wifi_slot.menu.xtal.160=160 MHz +wifi_slot.menu.xtal.160.build.f_cpu=160000000L +wifi_slot.menu.vt.flash=Flash +wifi_slot.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +wifi_slot.menu.vt.heap=Heap +wifi_slot.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +wifi_slot.menu.vt.iram=IRAM +wifi_slot.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +wifi_slot.menu.exception.enabled=Enabled +wifi_slot.menu.exception.enabled.build.exception_flags=-fexceptions +wifi_slot.menu.exception.enabled.build.stdcpp_lib=-lstdc++ +wifi_slot.menu.exception.disabled=Disabled +wifi_slot.menu.exception.disabled.build.exception_flags=-fno-exceptions +wifi_slot.menu.exception.disabled.build.stdcpp_lib=-lstdc++-nox +wifi_slot.upload.resetmethod=nodemcu +wifi_slot.menu.FlashFreq.40=40MHz +wifi_slot.menu.FlashFreq.40.build.flash_freq=40 +wifi_slot.menu.FlashFreq.80=80MHz +wifi_slot.menu.FlashFreq.80.build.flash_freq=80 +wifi_slot.menu.FlashMode.qio=QIO +wifi_slot.menu.FlashMode.qio.build.flash_mode=qio +wifi_slot.menu.FlashMode.qout=QOUT +wifi_slot.menu.FlashMode.qout.build.flash_mode=qout +wifi_slot.menu.FlashMode.dio=DIO +wifi_slot.menu.FlashMode.dio.build.flash_mode=dio +wifi_slot.menu.FlashMode.dout=DOUT +wifi_slot.menu.FlashMode.dout.build.flash_mode=dout +wifi_slot.menu.eesz.1M=1M (no SPIFFS) +wifi_slot.menu.eesz.1M.build.flash_size=1M +wifi_slot.menu.eesz.1M.build.flash_size_bytes=0x100000 +wifi_slot.menu.eesz.1M.build.flash_ld=eagle.flash.1m.ld +wifi_slot.menu.eesz.1M.build.spiffs_pagesize=256 +wifi_slot.menu.eesz.1M.upload.maximum_size=1023984 +wifi_slot.menu.eesz.1M.build.rfcal_addr=0xFC000 +wifi_slot.menu.eesz.1M64=1M (64K SPIFFS) +wifi_slot.menu.eesz.1M64.build.flash_size=1M +wifi_slot.menu.eesz.1M64.build.flash_size_bytes=0x100000 +wifi_slot.menu.eesz.1M64.build.flash_ld=eagle.flash.1m64.ld +wifi_slot.menu.eesz.1M64.build.spiffs_pagesize=256 +wifi_slot.menu.eesz.1M64.upload.maximum_size=958448 +wifi_slot.menu.eesz.1M64.build.rfcal_addr=0xFC000 +wifi_slot.menu.eesz.1M64.build.spiffs_start=0xEB000 +wifi_slot.menu.eesz.1M64.build.spiffs_end=0xFB000 +wifi_slot.menu.eesz.1M64.build.spiffs_blocksize=4096 +wifi_slot.menu.eesz.1M128=1M (128K SPIFFS) +wifi_slot.menu.eesz.1M128.build.flash_size=1M +wifi_slot.menu.eesz.1M128.build.flash_size_bytes=0x100000 +wifi_slot.menu.eesz.1M128.build.flash_ld=eagle.flash.1m128.ld +wifi_slot.menu.eesz.1M128.build.spiffs_pagesize=256 +wifi_slot.menu.eesz.1M128.upload.maximum_size=892912 +wifi_slot.menu.eesz.1M128.build.rfcal_addr=0xFC000 +wifi_slot.menu.eesz.1M128.build.spiffs_start=0xDB000 +wifi_slot.menu.eesz.1M128.build.spiffs_end=0xFB000 +wifi_slot.menu.eesz.1M128.build.spiffs_blocksize=4096 +wifi_slot.menu.eesz.1M144=1M (144K SPIFFS) +wifi_slot.menu.eesz.1M144.build.flash_size=1M +wifi_slot.menu.eesz.1M144.build.flash_size_bytes=0x100000 +wifi_slot.menu.eesz.1M144.build.flash_ld=eagle.flash.1m144.ld +wifi_slot.menu.eesz.1M144.build.spiffs_pagesize=256 +wifi_slot.menu.eesz.1M144.upload.maximum_size=876528 +wifi_slot.menu.eesz.1M144.build.rfcal_addr=0xFC000 +wifi_slot.menu.eesz.1M144.build.spiffs_start=0xD7000 +wifi_slot.menu.eesz.1M144.build.spiffs_end=0xFB000 +wifi_slot.menu.eesz.1M144.build.spiffs_blocksize=4096 +wifi_slot.menu.eesz.1M160=1M (160K SPIFFS) +wifi_slot.menu.eesz.1M160.build.flash_size=1M +wifi_slot.menu.eesz.1M160.build.flash_size_bytes=0x100000 +wifi_slot.menu.eesz.1M160.build.flash_ld=eagle.flash.1m160.ld +wifi_slot.menu.eesz.1M160.build.spiffs_pagesize=256 +wifi_slot.menu.eesz.1M160.upload.maximum_size=860144 +wifi_slot.menu.eesz.1M160.build.rfcal_addr=0xFC000 +wifi_slot.menu.eesz.1M160.build.spiffs_start=0xD3000 +wifi_slot.menu.eesz.1M160.build.spiffs_end=0xFB000 +wifi_slot.menu.eesz.1M160.build.spiffs_blocksize=4096 +wifi_slot.menu.eesz.1M192=1M (192K SPIFFS) +wifi_slot.menu.eesz.1M192.build.flash_size=1M +wifi_slot.menu.eesz.1M192.build.flash_size_bytes=0x100000 +wifi_slot.menu.eesz.1M192.build.flash_ld=eagle.flash.1m192.ld +wifi_slot.menu.eesz.1M192.build.spiffs_pagesize=256 +wifi_slot.menu.eesz.1M192.upload.maximum_size=827376 +wifi_slot.menu.eesz.1M192.build.rfcal_addr=0xFC000 +wifi_slot.menu.eesz.1M192.build.spiffs_start=0xCB000 +wifi_slot.menu.eesz.1M192.build.spiffs_end=0xFB000 +wifi_slot.menu.eesz.1M192.build.spiffs_blocksize=4096 +wifi_slot.menu.eesz.1M256=1M (256K SPIFFS) +wifi_slot.menu.eesz.1M256.build.flash_size=1M +wifi_slot.menu.eesz.1M256.build.flash_size_bytes=0x100000 +wifi_slot.menu.eesz.1M256.build.flash_ld=eagle.flash.1m256.ld +wifi_slot.menu.eesz.1M256.build.spiffs_pagesize=256 +wifi_slot.menu.eesz.1M256.upload.maximum_size=761840 +wifi_slot.menu.eesz.1M256.build.rfcal_addr=0xFC000 +wifi_slot.menu.eesz.1M256.build.spiffs_start=0xBB000 +wifi_slot.menu.eesz.1M256.build.spiffs_end=0xFB000 +wifi_slot.menu.eesz.1M256.build.spiffs_blocksize=4096 +wifi_slot.menu.eesz.1M512=1M (512K SPIFFS) +wifi_slot.menu.eesz.1M512.build.flash_size=1M +wifi_slot.menu.eesz.1M512.build.flash_size_bytes=0x100000 +wifi_slot.menu.eesz.1M512.build.flash_ld=eagle.flash.1m512.ld +wifi_slot.menu.eesz.1M512.build.spiffs_pagesize=256 +wifi_slot.menu.eesz.1M512.upload.maximum_size=499696 +wifi_slot.menu.eesz.1M512.build.rfcal_addr=0xFC000 +wifi_slot.menu.eesz.1M512.build.spiffs_start=0x7B000 +wifi_slot.menu.eesz.1M512.build.spiffs_end=0xFB000 +wifi_slot.menu.eesz.1M512.build.spiffs_blocksize=4096 +wifi_slot.menu.eesz.2M=2M (no SPIFFS) +wifi_slot.menu.eesz.2M.build.flash_size=2M +wifi_slot.menu.eesz.2M.build.flash_size_bytes=0x200000 +wifi_slot.menu.eesz.2M.build.flash_ld=eagle.flash.2m.ld +wifi_slot.menu.eesz.2M.build.spiffs_pagesize=256 +wifi_slot.menu.eesz.2M.upload.maximum_size=1044464 +wifi_slot.menu.eesz.2M.build.rfcal_addr=0x1FC000 +wifi_slot.menu.eesz.2M128=2M (128K SPIFFS) +wifi_slot.menu.eesz.2M128.build.flash_size=2M +wifi_slot.menu.eesz.2M128.build.flash_size_bytes=0x200000 +wifi_slot.menu.eesz.2M128.build.flash_ld=eagle.flash.2m128.ld +wifi_slot.menu.eesz.2M128.build.spiffs_pagesize=256 +wifi_slot.menu.eesz.2M128.upload.maximum_size=1044464 +wifi_slot.menu.eesz.2M128.build.rfcal_addr=0x1FC000 +wifi_slot.menu.eesz.2M128.build.spiffs_start=0x1E0000 +wifi_slot.menu.eesz.2M128.build.spiffs_end=0x1FB000 +wifi_slot.menu.eesz.2M128.build.spiffs_blocksize=4096 +wifi_slot.menu.eesz.2M256=2M (256K SPIFFS) +wifi_slot.menu.eesz.2M256.build.flash_size=2M +wifi_slot.menu.eesz.2M256.build.flash_size_bytes=0x200000 +wifi_slot.menu.eesz.2M256.build.flash_ld=eagle.flash.2m256.ld +wifi_slot.menu.eesz.2M256.build.spiffs_pagesize=256 +wifi_slot.menu.eesz.2M256.upload.maximum_size=1044464 +wifi_slot.menu.eesz.2M256.build.rfcal_addr=0x1FC000 +wifi_slot.menu.eesz.2M256.build.spiffs_start=0x1C0000 +wifi_slot.menu.eesz.2M256.build.spiffs_end=0x1FB000 +wifi_slot.menu.eesz.2M256.build.spiffs_blocksize=4096 +wifi_slot.menu.eesz.2M512=2M (512K SPIFFS) +wifi_slot.menu.eesz.2M512.build.flash_size=2M +wifi_slot.menu.eesz.2M512.build.flash_size_bytes=0x200000 +wifi_slot.menu.eesz.2M512.build.flash_ld=eagle.flash.2m512.ld +wifi_slot.menu.eesz.2M512.build.spiffs_pagesize=256 +wifi_slot.menu.eesz.2M512.upload.maximum_size=1044464 +wifi_slot.menu.eesz.2M512.build.rfcal_addr=0x1FC000 +wifi_slot.menu.eesz.2M512.build.spiffs_start=0x180000 +wifi_slot.menu.eesz.2M512.build.spiffs_end=0x1FB000 +wifi_slot.menu.eesz.2M512.build.spiffs_blocksize=8192 +wifi_slot.menu.eesz.2M1M=2M (1M SPIFFS) +wifi_slot.menu.eesz.2M1M.build.flash_size=2M +wifi_slot.menu.eesz.2M1M.build.flash_size_bytes=0x200000 +wifi_slot.menu.eesz.2M1M.build.flash_ld=eagle.flash.2m1m.ld +wifi_slot.menu.eesz.2M1M.build.spiffs_pagesize=256 +wifi_slot.menu.eesz.2M1M.upload.maximum_size=1044464 +wifi_slot.menu.eesz.2M1M.build.rfcal_addr=0x1FC000 +wifi_slot.menu.eesz.2M1M.build.spiffs_start=0x100000 +wifi_slot.menu.eesz.2M1M.build.spiffs_end=0x1FB000 +wifi_slot.menu.eesz.2M1M.build.spiffs_blocksize=8192 +wifi_slot.menu.ip.lm2f=v2 Lower Memory +wifi_slot.menu.ip.lm2f.build.lwip_include=lwip2/include +wifi_slot.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +wifi_slot.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +wifi_slot.menu.ip.hb2f=v2 Higher Bandwidth +wifi_slot.menu.ip.hb2f.build.lwip_include=lwip2/include +wifi_slot.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +wifi_slot.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +wifi_slot.menu.ip.lm2n=v2 Lower Memory (no features) +wifi_slot.menu.ip.lm2n.build.lwip_include=lwip2/include +wifi_slot.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +wifi_slot.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +wifi_slot.menu.ip.hb2n=v2 Higher Bandwidth (no features) +wifi_slot.menu.ip.hb2n.build.lwip_include=lwip2/include +wifi_slot.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +wifi_slot.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +wifi_slot.menu.ip.lm6f=v2 IPv6 Lower Memory +wifi_slot.menu.ip.lm6f.build.lwip_include=lwip2/include +wifi_slot.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +wifi_slot.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +wifi_slot.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +wifi_slot.menu.ip.hb6f.build.lwip_include=lwip2/include +wifi_slot.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +wifi_slot.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +wifi_slot.menu.ip.hb1=v1.4 Higher Bandwidth +wifi_slot.menu.ip.hb1.build.lwip_lib=-llwip_gcc +wifi_slot.menu.ip.hb1.build.lwip_flags=-DLWIP_OPEN_SRC +wifi_slot.menu.ip.src=v1.4 Compile from source +wifi_slot.menu.ip.src.build.lwip_lib=-llwip_src +wifi_slot.menu.ip.src.build.lwip_flags=-DLWIP_OPEN_SRC +wifi_slot.menu.ip.src.recipe.hooks.sketch.prebuild.1.pattern=make -C "{runtime.platform.path}/tools/sdk/lwip/src" install TOOLS_PATH="{runtime.tools.xtensa-lx106-elf-gcc.path}/bin/xtensa-lx106-elf-" +wifi_slot.menu.dbg.Disabled=Disabled +wifi_slot.menu.dbg.Disabled.build.debug_port= +wifi_slot.menu.dbg.Serial=Serial +wifi_slot.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +wifi_slot.menu.dbg.Serial1=Serial1 +wifi_slot.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +wifi_slot.menu.lvl.None____=None +wifi_slot.menu.lvl.None____.build.debug_level= +wifi_slot.menu.lvl.SSL=SSL +wifi_slot.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +wifi_slot.menu.lvl.TLS_MEM=TLS_MEM +wifi_slot.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +wifi_slot.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +wifi_slot.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +wifi_slot.menu.lvl.HTTP_SERVER=HTTP_SERVER +wifi_slot.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +wifi_slot.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +wifi_slot.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +wifi_slot.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +wifi_slot.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +wifi_slot.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +wifi_slot.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +wifi_slot.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +wifi_slot.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +wifi_slot.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +wifi_slot.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +wifi_slot.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +wifi_slot.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +wifi_slot.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +wifi_slot.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +wifi_slot.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +wifi_slot.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +wifi_slot.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +wifi_slot.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +wifi_slot.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +wifi_slot.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +wifi_slot.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +wifi_slot.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +wifi_slot.menu.lvl.CORE=CORE +wifi_slot.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +wifi_slot.menu.lvl.WIFI=WIFI +wifi_slot.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +wifi_slot.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +wifi_slot.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +wifi_slot.menu.lvl.UPDATER=UPDATER +wifi_slot.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +wifi_slot.menu.lvl.OTA=OTA +wifi_slot.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +wifi_slot.menu.lvl.OOM=OOM +wifi_slot.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +wifi_slot.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOM=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM +wifi_slot.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOM.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM +wifi_slot.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM +wifi_slot.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM +wifi_slot.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +wifi_slot.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +wifi_slot.menu.wipe.none=Only Sketch +wifi_slot.menu.wipe.none.upload.erase_cmd= +wifi_slot.menu.wipe.sdk=Sketch + WiFi Settings +wifi_slot.menu.wipe.sdk.upload.erase_cmd=-ca "{build.rfcal_addr}" -cz 0x4000 +wifi_slot.menu.wipe.all=All Flash Contents +wifi_slot.menu.wipe.all.upload.erase_cmd=-ca 0x0 -cz "{build.flash_size_bytes}" +wifi_slot.menu.baud.115200=115200 +wifi_slot.menu.baud.115200.upload.speed=115200 +wifi_slot.menu.baud.9600=9600 +wifi_slot.menu.baud.9600.upload.speed=9600 +wifi_slot.menu.baud.57600=57600 +wifi_slot.menu.baud.57600.upload.speed=57600 +wifi_slot.menu.baud.230400.linux=230400 +wifi_slot.menu.baud.230400.macosx=230400 +wifi_slot.menu.baud.230400.upload.speed=230400 +wifi_slot.menu.baud.256000.windows=256000 +wifi_slot.menu.baud.256000.upload.speed=256000 +wifi_slot.menu.baud.460800.linux=460800 +wifi_slot.menu.baud.460800.macosx=460800 +wifi_slot.menu.baud.460800.upload.speed=460800 +wifi_slot.menu.baud.512000.windows=512000 +wifi_slot.menu.baud.512000.upload.speed=512000 +wifi_slot.menu.baud.921600=921600 +wifi_slot.menu.baud.921600.upload.speed=921600 + +############################################################## +wiolink.name=Seeed Wio Link +wiolink.build.board=ESP8266_WIO_LINK +wiolink.build.variant=wiolink +wiolink.upload.tool=esptool +wiolink.upload.maximum_data_size=81920 +wiolink.upload.wait_for_upload_port=true +wiolink.upload.erase_cmd= +wiolink.serial.disableDTR=true +wiolink.serial.disableRTS=true +wiolink.build.mcu=esp8266 +wiolink.build.core=esp8266 +wiolink.build.spiffs_pagesize=256 +wiolink.build.debug_port= +wiolink.build.debug_level= +wiolink.menu.xtal.80=80 MHz +wiolink.menu.xtal.80.build.f_cpu=80000000L +wiolink.menu.xtal.160=160 MHz +wiolink.menu.xtal.160.build.f_cpu=160000000L +wiolink.menu.vt.flash=Flash +wiolink.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +wiolink.menu.vt.heap=Heap +wiolink.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +wiolink.menu.vt.iram=IRAM +wiolink.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +wiolink.menu.exception.enabled=Enabled +wiolink.menu.exception.enabled.build.exception_flags=-fexceptions +wiolink.menu.exception.enabled.build.stdcpp_lib=-lstdc++ +wiolink.menu.exception.disabled=Disabled +wiolink.menu.exception.disabled.build.exception_flags=-fno-exceptions +wiolink.menu.exception.disabled.build.stdcpp_lib=-lstdc++-nox +wiolink.upload.resetmethod=nodemcu +wiolink.build.flash_mode=qio +wiolink.build.flash_freq=40 +wiolink.menu.eesz.4M=4M (no SPIFFS) +wiolink.menu.eesz.4M.build.flash_size=4M +wiolink.menu.eesz.4M.build.flash_size_bytes=0x400000 +wiolink.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld +wiolink.menu.eesz.4M.build.spiffs_pagesize=256 +wiolink.menu.eesz.4M.upload.maximum_size=1044464 +wiolink.menu.eesz.4M.build.rfcal_addr=0x3FC000 +wiolink.menu.eesz.4M1M=4M (1M SPIFFS) +wiolink.menu.eesz.4M1M.build.flash_size=4M +wiolink.menu.eesz.4M1M.build.flash_size_bytes=0x400000 +wiolink.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld +wiolink.menu.eesz.4M1M.build.spiffs_pagesize=256 +wiolink.menu.eesz.4M1M.upload.maximum_size=1044464 +wiolink.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 +wiolink.menu.eesz.4M1M.build.spiffs_start=0x300000 +wiolink.menu.eesz.4M1M.build.spiffs_end=0x3FB000 +wiolink.menu.eesz.4M1M.build.spiffs_blocksize=8192 +wiolink.menu.eesz.4M2M=4M (2M SPIFFS) +wiolink.menu.eesz.4M2M.build.flash_size=4M +wiolink.menu.eesz.4M2M.build.flash_size_bytes=0x400000 +wiolink.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld +wiolink.menu.eesz.4M2M.build.spiffs_pagesize=256 +wiolink.menu.eesz.4M2M.upload.maximum_size=1044464 +wiolink.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 +wiolink.menu.eesz.4M2M.build.spiffs_start=0x200000 +wiolink.menu.eesz.4M2M.build.spiffs_end=0x3FB000 +wiolink.menu.eesz.4M2M.build.spiffs_blocksize=8192 +wiolink.menu.eesz.4M3M=4M (3M SPIFFS) +wiolink.menu.eesz.4M3M.build.flash_size=4M +wiolink.menu.eesz.4M3M.build.flash_size_bytes=0x400000 +wiolink.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld +wiolink.menu.eesz.4M3M.build.spiffs_pagesize=256 +wiolink.menu.eesz.4M3M.upload.maximum_size=1044464 +wiolink.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 +wiolink.menu.eesz.4M3M.build.spiffs_start=0x100000 +wiolink.menu.eesz.4M3M.build.spiffs_end=0x3FB000 +wiolink.menu.eesz.4M3M.build.spiffs_blocksize=8192 +wiolink.menu.ip.lm2f=v2 Lower Memory +wiolink.menu.ip.lm2f.build.lwip_include=lwip2/include +wiolink.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +wiolink.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +wiolink.menu.ip.hb2f=v2 Higher Bandwidth +wiolink.menu.ip.hb2f.build.lwip_include=lwip2/include +wiolink.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +wiolink.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +wiolink.menu.ip.lm2n=v2 Lower Memory (no features) +wiolink.menu.ip.lm2n.build.lwip_include=lwip2/include +wiolink.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +wiolink.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +wiolink.menu.ip.hb2n=v2 Higher Bandwidth (no features) +wiolink.menu.ip.hb2n.build.lwip_include=lwip2/include +wiolink.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +wiolink.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +wiolink.menu.ip.lm6f=v2 IPv6 Lower Memory +wiolink.menu.ip.lm6f.build.lwip_include=lwip2/include +wiolink.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +wiolink.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +wiolink.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +wiolink.menu.ip.hb6f.build.lwip_include=lwip2/include +wiolink.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +wiolink.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +wiolink.menu.ip.hb1=v1.4 Higher Bandwidth +wiolink.menu.ip.hb1.build.lwip_lib=-llwip_gcc +wiolink.menu.ip.hb1.build.lwip_flags=-DLWIP_OPEN_SRC +wiolink.menu.ip.src=v1.4 Compile from source +wiolink.menu.ip.src.build.lwip_lib=-llwip_src +wiolink.menu.ip.src.build.lwip_flags=-DLWIP_OPEN_SRC +wiolink.menu.ip.src.recipe.hooks.sketch.prebuild.1.pattern=make -C "{runtime.platform.path}/tools/sdk/lwip/src" install TOOLS_PATH="{runtime.tools.xtensa-lx106-elf-gcc.path}/bin/xtensa-lx106-elf-" +wiolink.menu.dbg.Disabled=Disabled +wiolink.menu.dbg.Disabled.build.debug_port= +wiolink.menu.dbg.Serial=Serial +wiolink.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +wiolink.menu.dbg.Serial1=Serial1 +wiolink.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +wiolink.menu.lvl.None____=None +wiolink.menu.lvl.None____.build.debug_level= +wiolink.menu.lvl.SSL=SSL +wiolink.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +wiolink.menu.lvl.TLS_MEM=TLS_MEM +wiolink.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +wiolink.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +wiolink.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +wiolink.menu.lvl.HTTP_SERVER=HTTP_SERVER +wiolink.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +wiolink.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +wiolink.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +wiolink.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +wiolink.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +wiolink.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +wiolink.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +wiolink.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +wiolink.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +wiolink.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +wiolink.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +wiolink.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +wiolink.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +wiolink.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +wiolink.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +wiolink.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +wiolink.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +wiolink.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +wiolink.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +wiolink.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +wiolink.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +wiolink.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +wiolink.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +wiolink.menu.lvl.CORE=CORE +wiolink.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +wiolink.menu.lvl.WIFI=WIFI +wiolink.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +wiolink.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +wiolink.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +wiolink.menu.lvl.UPDATER=UPDATER +wiolink.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +wiolink.menu.lvl.OTA=OTA +wiolink.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +wiolink.menu.lvl.OOM=OOM +wiolink.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +wiolink.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOM=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM +wiolink.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOM.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM +wiolink.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM +wiolink.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM +wiolink.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +wiolink.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +wiolink.menu.wipe.none=Only Sketch +wiolink.menu.wipe.none.upload.erase_cmd= +wiolink.menu.wipe.sdk=Sketch + WiFi Settings +wiolink.menu.wipe.sdk.upload.erase_cmd=-ca "{build.rfcal_addr}" -cz 0x4000 +wiolink.menu.wipe.all=All Flash Contents +wiolink.menu.wipe.all.upload.erase_cmd=-ca 0x0 -cz "{build.flash_size_bytes}" +wiolink.menu.baud.115200=115200 +wiolink.menu.baud.115200.upload.speed=115200 +wiolink.menu.baud.9600=9600 +wiolink.menu.baud.9600.upload.speed=9600 +wiolink.menu.baud.57600=57600 +wiolink.menu.baud.57600.upload.speed=57600 +wiolink.menu.baud.230400.linux=230400 +wiolink.menu.baud.230400.macosx=230400 +wiolink.menu.baud.230400.upload.speed=230400 +wiolink.menu.baud.256000.windows=256000 +wiolink.menu.baud.256000.upload.speed=256000 +wiolink.menu.baud.460800.linux=460800 +wiolink.menu.baud.460800.macosx=460800 +wiolink.menu.baud.460800.upload.speed=460800 +wiolink.menu.baud.512000.windows=512000 +wiolink.menu.baud.512000.upload.speed=512000 +wiolink.menu.baud.921600=921600 +wiolink.menu.baud.921600.upload.speed=921600 + +############################################################## +espectro.name=ESPectro Core +espectro.build.board=ESP8266_ESPECTRO_CORE +espectro.build.variant=espectro +espectro.upload.tool=esptool +espectro.upload.maximum_data_size=81920 +espectro.upload.wait_for_upload_port=true +espectro.upload.erase_cmd= +espectro.serial.disableDTR=true +espectro.serial.disableRTS=true +espectro.build.mcu=esp8266 +espectro.build.core=esp8266 +espectro.build.spiffs_pagesize=256 +espectro.build.debug_port= +espectro.build.debug_level= +espectro.menu.xtal.80=80 MHz +espectro.menu.xtal.80.build.f_cpu=80000000L +espectro.menu.xtal.160=160 MHz +espectro.menu.xtal.160.build.f_cpu=160000000L +espectro.menu.vt.flash=Flash +espectro.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +espectro.menu.vt.heap=Heap +espectro.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +espectro.menu.vt.iram=IRAM +espectro.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +espectro.menu.exception.enabled=Enabled +espectro.menu.exception.enabled.build.exception_flags=-fexceptions +espectro.menu.exception.enabled.build.stdcpp_lib=-lstdc++ +espectro.menu.exception.disabled=Disabled +espectro.menu.exception.disabled.build.exception_flags=-fno-exceptions +espectro.menu.exception.disabled.build.stdcpp_lib=-lstdc++-nox +espectro.upload.resetmethod=nodemcu +espectro.build.flash_mode=dio +espectro.build.flash_freq=40 +espectro.menu.eesz.4M=4M (no SPIFFS) +espectro.menu.eesz.4M.build.flash_size=4M +espectro.menu.eesz.4M.build.flash_size_bytes=0x400000 +espectro.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld +espectro.menu.eesz.4M.build.spiffs_pagesize=256 +espectro.menu.eesz.4M.upload.maximum_size=1044464 +espectro.menu.eesz.4M.build.rfcal_addr=0x3FC000 +espectro.menu.eesz.4M1M=4M (1M SPIFFS) +espectro.menu.eesz.4M1M.build.flash_size=4M +espectro.menu.eesz.4M1M.build.flash_size_bytes=0x400000 +espectro.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld +espectro.menu.eesz.4M1M.build.spiffs_pagesize=256 +espectro.menu.eesz.4M1M.upload.maximum_size=1044464 +espectro.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 +espectro.menu.eesz.4M1M.build.spiffs_start=0x300000 +espectro.menu.eesz.4M1M.build.spiffs_end=0x3FB000 +espectro.menu.eesz.4M1M.build.spiffs_blocksize=8192 +espectro.menu.eesz.4M2M=4M (2M SPIFFS) +espectro.menu.eesz.4M2M.build.flash_size=4M +espectro.menu.eesz.4M2M.build.flash_size_bytes=0x400000 +espectro.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld +espectro.menu.eesz.4M2M.build.spiffs_pagesize=256 +espectro.menu.eesz.4M2M.upload.maximum_size=1044464 +espectro.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 +espectro.menu.eesz.4M2M.build.spiffs_start=0x200000 +espectro.menu.eesz.4M2M.build.spiffs_end=0x3FB000 +espectro.menu.eesz.4M2M.build.spiffs_blocksize=8192 +espectro.menu.eesz.4M3M=4M (3M SPIFFS) +espectro.menu.eesz.4M3M.build.flash_size=4M +espectro.menu.eesz.4M3M.build.flash_size_bytes=0x400000 +espectro.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld +espectro.menu.eesz.4M3M.build.spiffs_pagesize=256 +espectro.menu.eesz.4M3M.upload.maximum_size=1044464 +espectro.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 +espectro.menu.eesz.4M3M.build.spiffs_start=0x100000 +espectro.menu.eesz.4M3M.build.spiffs_end=0x3FB000 +espectro.menu.eesz.4M3M.build.spiffs_blocksize=8192 +espectro.menu.ip.lm2f=v2 Lower Memory +espectro.menu.ip.lm2f.build.lwip_include=lwip2/include +espectro.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +espectro.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +espectro.menu.ip.hb2f=v2 Higher Bandwidth +espectro.menu.ip.hb2f.build.lwip_include=lwip2/include +espectro.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +espectro.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +espectro.menu.ip.lm2n=v2 Lower Memory (no features) +espectro.menu.ip.lm2n.build.lwip_include=lwip2/include +espectro.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +espectro.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +espectro.menu.ip.hb2n=v2 Higher Bandwidth (no features) +espectro.menu.ip.hb2n.build.lwip_include=lwip2/include +espectro.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +espectro.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +espectro.menu.ip.lm6f=v2 IPv6 Lower Memory +espectro.menu.ip.lm6f.build.lwip_include=lwip2/include +espectro.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +espectro.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +espectro.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +espectro.menu.ip.hb6f.build.lwip_include=lwip2/include +espectro.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +espectro.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +espectro.menu.ip.hb1=v1.4 Higher Bandwidth +espectro.menu.ip.hb1.build.lwip_lib=-llwip_gcc +espectro.menu.ip.hb1.build.lwip_flags=-DLWIP_OPEN_SRC +espectro.menu.ip.src=v1.4 Compile from source +espectro.menu.ip.src.build.lwip_lib=-llwip_src +espectro.menu.ip.src.build.lwip_flags=-DLWIP_OPEN_SRC +espectro.menu.ip.src.recipe.hooks.sketch.prebuild.1.pattern=make -C "{runtime.platform.path}/tools/sdk/lwip/src" install TOOLS_PATH="{runtime.tools.xtensa-lx106-elf-gcc.path}/bin/xtensa-lx106-elf-" +espectro.menu.dbg.Disabled=Disabled +espectro.menu.dbg.Disabled.build.debug_port= +espectro.menu.dbg.Serial=Serial +espectro.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +espectro.menu.dbg.Serial1=Serial1 +espectro.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +espectro.menu.lvl.None____=None +espectro.menu.lvl.None____.build.debug_level= +espectro.menu.lvl.SSL=SSL +espectro.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +espectro.menu.lvl.TLS_MEM=TLS_MEM +espectro.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +espectro.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +espectro.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +espectro.menu.lvl.HTTP_SERVER=HTTP_SERVER +espectro.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +espectro.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +espectro.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +espectro.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +espectro.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +espectro.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +espectro.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +espectro.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +espectro.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +espectro.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +espectro.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +espectro.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +espectro.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +espectro.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +espectro.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +espectro.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +espectro.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +espectro.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +espectro.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +espectro.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +espectro.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +espectro.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +espectro.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +espectro.menu.lvl.CORE=CORE +espectro.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +espectro.menu.lvl.WIFI=WIFI +espectro.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +espectro.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +espectro.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +espectro.menu.lvl.UPDATER=UPDATER +espectro.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +espectro.menu.lvl.OTA=OTA +espectro.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +espectro.menu.lvl.OOM=OOM +espectro.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +espectro.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOM=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM +espectro.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOM.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM +espectro.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM +espectro.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM +espectro.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +espectro.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +espectro.menu.wipe.none=Only Sketch +espectro.menu.wipe.none.upload.erase_cmd= +espectro.menu.wipe.sdk=Sketch + WiFi Settings +espectro.menu.wipe.sdk.upload.erase_cmd=-ca "{build.rfcal_addr}" -cz 0x4000 +espectro.menu.wipe.all=All Flash Contents +espectro.menu.wipe.all.upload.erase_cmd=-ca 0x0 -cz "{build.flash_size_bytes}" +espectro.menu.baud.115200=115200 +espectro.menu.baud.115200.upload.speed=115200 +espectro.menu.baud.9600=9600 +espectro.menu.baud.9600.upload.speed=9600 +espectro.menu.baud.57600=57600 +espectro.menu.baud.57600.upload.speed=57600 +espectro.menu.baud.230400.linux=230400 +espectro.menu.baud.230400.macosx=230400 +espectro.menu.baud.230400.upload.speed=230400 +espectro.menu.baud.256000.windows=256000 +espectro.menu.baud.256000.upload.speed=256000 +espectro.menu.baud.460800.linux=460800 +espectro.menu.baud.460800.macosx=460800 +espectro.menu.baud.460800.upload.speed=460800 +espectro.menu.baud.512000.windows=512000 +espectro.menu.baud.512000.upload.speed=512000 +espectro.menu.baud.921600=921600 +espectro.menu.baud.921600.upload.speed=921600 + diff --git a/arduino/version 2.5.0/platform.txt b/arduino/version 2.5.0/platform.txt new file mode 100644 index 000000000..39988668a --- /dev/null +++ b/arduino/version 2.5.0/platform.txt @@ -0,0 +1,156 @@ + +# ESP8266 platform +# ------------------------------ + +# For more info: +# https://github.com/arduino/Arduino/wiki/Arduino-IDE-1.5---3rd-party-Hardware-specification + +name=ESP8266 Boards (2.5.0-beta2) +version=2.5.0-beta2 + + + +runtime.tools.signing={runtime.platform.path}/tools/signing.py + +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_include=lwip/include +build.lwip_flags=-DLWIP_OPEN_SRC + +build.vtable_flags=-DVTABLES_IN_FLASH + +#build.exception_flags=-fexceptions +build.exception_flags=-fno-exceptions +#build.stdcpp_lib=-lstdc++ +build.stdcpp_lib=-lstdc++-nox + +#build.float=-u _printf_float -u _scanf_float +build.float= +build.led= + +compiler.path={runtime.tools.xtensa-lx106-elf-gcc.path}/bin/ +compiler.sdk.path={runtime.platform.path}/tools/sdk +compiler.libc.path={runtime.platform.path}/tools/sdk/libc/xtensa-lx106-elf +compiler.cpreprocessor.flags=-D__ets__ -DICACHE_FLASH -U__STRICT_ANSI__ "-I{compiler.sdk.path}/include" "-I{compiler.sdk.path}/{build.lwip_include}" "-I{compiler.libc.path}/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 {build.exception_flags} -ffunction-sections -fdata-sections {build.exception_flags} + +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 app_entry {build.float} -Wl,-static "-L{compiler.sdk.path}/lib" "-L{compiler.sdk.path}/ld" "-L{compiler.libc.path}/lib" "-T{build.flash_ld}" -Wl,--gc-sections -Wl,-wrap,system_restart_local -Wl,-wrap,spi_flash_read + +compiler.c.elf.cmd=xtensa-lx106-elf-gcc +compiler.c.elf.libs=-lhal -lphy -lpp -lnet80211 {build.lwip_lib} -lwpa -lcrypto -lmain -lwps -lbearssl -laxtls -lespnow -lsmartconfig -lairkiss -lwpa2 {build.stdcpp_lib} -lm -lc -lgcc + +compiler.cpp.cmd=xtensa-lx106-elf-g++ +compiler.cpp.flags=-c {compiler.warning_flags} -Os -g -mlongcalls -mtext-section-literals -fno-rtti -falign-functions=4 -std=c++11 -MMD -ffunction-sections -fdata-sections {build.exception_flags} + +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 +recipe.hooks.core.prebuild.1.pattern=python "{runtime.tools.signing}" --mode header --publickey "{build.source.path}/public.key" --out "{build.path}/core/Updater_Signing.h" + + + +## windows-compatible version without git +recipe.hooks.core.prebuild.1.pattern.windows=cmd.exe /c rem cannot sign on windows + + + +## Build the app.ld linker file +recipe.hooks.linking.prelink.1.pattern="{compiler.path}{compiler.c.cmd}" -CC -E -P {build.vtable_flags} "{runtime.platform.path}/tools/sdk/ld/eagle.app.v6.common.ld.h" -o "{build.path}/local.eagle.app.v6.common.ld" + +## 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}" {build.led} {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}" {build.led} {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}" {build.led} {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} "{archive_file_path}" "{object_file}" + +## Combine gc-sections, archives, and objects +recipe.c.combine.pattern="{compiler.path}{compiler.c.elf.cmd}" -Wl,-Map "-Wl,{build.path}/{build.project_name}.map" {compiler.c.elf.flags} {compiler.c.elf.extra_flags} -o "{build.path}/{build.project_name}.elf" -Wl,--start-group {object_files} "{archive_file_path}" {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.1.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 +recipe.objcopy.hex.2.pattern=python "{runtime.tools.signing}" --mode sign --privatekey "{build.source.path}/private.key" --bin "{build.path}/{build.project_name}.bin" --out "{build.path}/{build.project_name}.bin.signed" + +# No signing on Windows +recipe.objcopy.hex.1.pattern.windows="{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 +recipe.objcopy.hex.2.pattern.windows= + +## 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}" {upload.erase_cmd} -ca 0x00000 -cf "{build.path}/{build.project_name}.bin" +tools.esptool.upload.network_pattern="{network_cmd}" "{runtime.platform.path}/tools/espota.py" -i "{serial.port}" -p "{network.port}" "--auth={network.password}" -f "{build.path}/{build.project_name}.bin" + +tools.mkspiffs.cmd=mkspiffs +tools.mkspiffs.cmd.windows=mkspiffs.exe +tools.mkspiffs.path={runtime.tools.mkspiffs.path} + +tools.espupload.cmd=python +tools.espupload.cmd.windows=python.exe +tools.espupload.path={runtime.platform.path}/tools +tools.espupload.upload.protocol=espupload +tools.espupload.upload.params.verbose= +tools.espupload.upload.params.quiet= +tools.espupload.upload.pattern="{cmd}" "{path}/espupload.py" -f "{build.path}/{build.project_name}.bin" diff --git a/platformio.ini b/platformio.ini index 14e554263..2546e6864 100644 --- a/platformio.ini +++ b/platformio.ini @@ -34,67 +34,132 @@ src_dir = sonoff ;env_default = sonoff-PT ;env_default = sonoff-RU ;env_default = sonoff-SE +;env_default = sonoff-SK ;env_default = sonoff-TR ;env_default = sonoff-TW ;env_default = sonoff-UK -[common] ; ************************************************************ +[esp82xx_defaults] +build_flags = -D NDEBUG + -mtarget-align + -Wl,-Map,firmware.map + +[core_2_3_0] ; *** Esp8266 core for Arduino version 2.3.0 -;platform = espressif8266@1.5.0 +platform = espressif8266@1.5.0 +build_flags = ${esp82xx_defaults.build_flags} + -Wl,-Tesp8266.flash.1m0.ld + +[core_2_4_2] ; *** Esp8266 core for Arduino version 2.4.2 -platform = espressif8266@1.8.0 +platform = espressif8266@1.8.0 +build_flags = ${esp82xx_defaults.build_flags} + -Wl,-Teagle.flash.1m0.ld + -lstdc++ -lsupc++ +; lwIP 1.4 (Default) +; -DPIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH +; lwIP 2 - Low Memory +; -DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY +; lwIP 2 - Higher Bandwidth (Tasmota default) + -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH + -DVTABLES_IN_FLASH + +[core_2_5_0] +; *** Esp8266 core for Arduino version Core 2.5.0 beta tested for Tasmota +platform = https://github.com/Jason2866/platform-espressif8266.git#Tasmota +build_flags = ${esp82xx_defaults.build_flags} + -Wl,-Teagle.flash.1m.ld +; lwIP 1.4 (Default) +; -DPIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH +; lwIP 2 - Low Memory +; -DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY +; lwIP 2 - Higher Bandwidth +; -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH +; lwIP 2 - Higher Bandwidth Low Memory no Features +; -DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY_LOW_FLASH +; lwIP 2 - Higher Bandwidth no Features (Tasmota default) + -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH + -DVTABLES_IN_FLASH + -fno-exceptions + -lstdc++-nox + +[core_stage] ; *** Esp8266 core for Arduino version latest beta -;platform = https://github.com/platformio/platform-espressif8266.git#feature/stage -; *** Esp8266 core for Arduino current version (located in %USERPROFILE%\.platformio\platforms\espressif8266) -;platform = espressif8266 +platform = https://github.com/platformio/platform-espressif8266.git#feature/stage +build_flags = ${esp82xx_defaults.build_flags} + -Wl,-Teagle.flash.1m.ld +; lwIP 1.4 (Default) +; -DPIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH +; lwIP 2 - Low Memory +; -DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY +; lwIP 2 - Higher Bandwidth +; -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH +; lwIP 2 - Higher Bandwitdh Low Memory no Features +; -DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY_LOW_FLASH +; lwIP 2 - Higher Bandwitdh no Features + -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH +; VTABLES in Flash (default) + -DVTABLES_IN_FLASH +; VTABLES in Heap +; -DVTABLES_IN_DRAM +; VTABLES in IRAM +; -DVTABLES_IN_IRAM +; enable one option set -> No exception recommended +; No exception code in firmware + -fno-exceptions + -lstdc++-nox +; Exception code in firmware /needs much space! 90k +; -fexceptions +; -lstdc++ -framework = arduino -board = esp01_1m -board_build.flash_mode = dout +[core_active] +; Select one core set for platform and build_flags +;platform = ${core_2_3_0.platform} +;build_flags = ${core_2_3_0.build_flags} +platform = ${core_2_4_2.platform} +build_flags = ${core_2_4_2.build_flags} +;platform = ${core_2_5_0.platform} +;build_flags = ${core_2_5_0.build_flags} +;platform = ${core_stage.platform} +;build_flags = ${core_stage.build_flags} -; set CPU frequency to 80MHz (default) or 160MHz -board_build.f_cpu = 80000000L -;board_build.f_cpu = 160000000L +[common] +framework = arduino +board = esp01_1m +board_build.flash_mode = dout + +platform = ${core_active.platform} +build_flags = ${core_active.build_flags} +; -DUSE_CLASSIC +; -DBE_MINIMAL +; -DUSE_SENSORS +; -DUSE_BASIC +; -DUSE_KNX_NO_EMULATION +; -DUSE_DISPLAYS +; -DUSE_CONFIG_OVERRIDE ; *** Fix espressif8266@1.7.0 induced undesired all warnings -build_unflags = -Wall +build_unflags = -Wall -build_flags = -; if using esp8266 core 2.5.0 (stage) or up -; -Wl,-Tesp8266.flash.1m.ld -Wl,-Map,firmware.map -; if using esp8266 core < 2.5.0 - -Wl,-Tesp8266.flash.1m0.ld -Wl,-Map,firmware.map - -mtarget-align -; -DUSE_CONFIG_OVERRIDE -; lwIP 2 - Higher Bandwitdh (core 2.4.2) - -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH -; lwIP 2 - Higher Bandwitdh no Features (core 2.5.0) -; -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH - -DVTABLES_IN_FLASH +; set CPU frequency to 80MHz (default) or 160MHz +board_build.f_cpu = 80000000L +;board_build.f_cpu = 160000000L -; if using esp8266 core 2.5.0 (stage) or up -; No exception code in firmware -; -fno-exceptions -; -lstdc++-nox - -; *** Serial Monitor options -monitor_speed = 115200 +monitor_speed = 115200 +upload_speed = 115200 +upload_resetmethod = nodemcu ; *** Upload Serial reset method for Wemos and NodeMCU -upload_speed = 115200 -upload_resetmethod = nodemcu -upload_port = COM5 - -; *** Fix Esp/Arduino core 2.4.x induced Tasmota unused floating point includes -extra_scripts = pio/strip-floats.py +upload_port = COM5 +extra_scripts = pio/strip-floats.py ; *** Upload file to OTA server using SCP -;upload_port = user@host:/path -;extra_scripts = pio/strip-floats.py, pio/sftp-uploader.py +;upload_port = user@host:/path +;extra_scripts = pio/strip-floats.py, pio/sftp-uploader.py -; *** Upload file to OTA server using HTTP -;upload_port = domus1:80/api/upload-arduino.php -;extra_scripts = pio/strip-floats.py, pio/http-uploader.py +; *** Upload file to OTA server in folder api/arduino using HTTP +;upload_port = domus1:80/api/upload-arduino.php +;extra_scripts = pio/strip-floats.py, pio/http-uploader.py ; ********************************************************************* @@ -420,6 +485,20 @@ upload_resetmethod = ${common.upload_resetmethod} upload_speed = ${common.upload_speed} extra_scripts = ${common.extra_scripts} +[env:sonoff-SK] +platform = ${common.platform} +framework = ${common.framework} +board = ${common.board} +board_build.flash_mode = ${common.board_build.flash_mode} +board_build.f_cpu = ${common.board_build.f_cpu} +build_unflags = ${common.build_unflags} +build_flags = ${common.build_flags} -DMY_LANGUAGE=sk-SK +monitor_speed = ${common.monitor_speed} +upload_port = ${common.upload_port} +upload_resetmethod = ${common.upload_resetmethod} +upload_speed = ${common.upload_speed} +extra_scripts = ${common.extra_scripts} + [env:sonoff-TR] platform = ${common.platform} framework = ${common.framework} diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index c3ab8b097..aa0b4381a 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -1,4 +1,15 @@ -/* 6.4.0 20181217 +/* 6.4.1 20181223 + * Change RAM usage BMP/BME I2C sensors + * Change FallbackTopic detection (#4706) + * Change Hass discovery to short MQTT messages as used by Hass 0.81 and up (#4711) + * Fix possible dtostrf buffer overflows by increasing buffers + * Fix wifi strongest signal detection (#4704) + * Fix Alexa "this value is outside the range of the device". Needs power cycle and Alexa deletion/discovery cycle. (#3159, #4712) + * Add support for AZ-Instrument 7798 CO2 meter/datalogger (#4672) + * Add define WIFI_SOFT_AP_CHANNEL in my_user_config.h to set Soft Access Point Channel number between 1 and 13 as used by Wifi Manager web GUI (#4673) + * Add define USE_MQTT_TLS_CA_CERT for checking MQTT TLS against root ca using Let's Encrypt cert from sonoff_letsencrypt.h - not supported with core 2.3.0 (#4703) + * + * 6.4.0 20181217 * Change GUI Configure Module by using AJAX for data fetch to cut page size (and memory use) by 40% In case of web page errors clear your browser cache or do Page Reload (F5 or Ctrl+R) * Change enforcing flashmode dout but it is still mandatory diff --git a/sonoff/core_esp8266_timer.c b/sonoff/core_esp8266_timer.c index fdc7342e5..81c3a76e0 100644 --- a/sonoff/core_esp8266_timer.c +++ b/sonoff/core_esp8266_timer.c @@ -18,8 +18,10 @@ License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +// Use PWM from core 2.4.0 as all other version produce LED flickering when settings are saved to flash. Still true for 2.5.0 //#include -//#ifdef ARDUINO_ESP8266_RELEASE_2_3_0 +//#if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_1) || defined(ARDUINO_ESP8266_RELEASE_2_4_2) //#warning **** Tasmota is using v2.4.0 timer.c as planned **** #include "wiring_private.h" @@ -106,4 +108,4 @@ void ICACHE_RAM_ATTR timer0_detachInterrupt(void) { ETS_CCOMPARE0_DISABLE(); } -//#endif // ARDUINO_ESP8266_RELEASE_2_3_0 +//#endif // ARDUINO_ESP8266_RELEASE diff --git a/sonoff/core_esp8266_wiring_digital.c b/sonoff/core_esp8266_wiring_digital.c index b51a85aad..33de53c75 100644 --- a/sonoff/core_esp8266_wiring_digital.c +++ b/sonoff/core_esp8266_wiring_digital.c @@ -18,8 +18,10 @@ License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +// Use PWM from core 2.4.0 as all other version produce LED flickering when settings are saved to flash. Still true for 2.5.0 //#include -//#ifdef ARDUINO_ESP8266_RELEASE_2_3_0 +//#if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_1) || defined(ARDUINO_ESP8266_RELEASE_2_4_2) //#warning **** Tasmota is using v2.4.0 wiring_digital.c as planned **** #define ARDUINO_MAIN @@ -212,4 +214,4 @@ extern int digitalRead(uint8_t pin) __attribute__ ((weak, alias("__digitalRead") extern void attachInterrupt(uint8_t pin, voidFuncPtr handler, int mode) __attribute__ ((weak, alias("__attachInterrupt"))); extern void detachInterrupt(uint8_t pin) __attribute__ ((weak, alias("__detachInterrupt"))); -//#endif // ARDUINO_ESP8266_RELEASE_2_3_0 +//#endif // ARDUINO_ESP8266_RELEASE diff --git a/sonoff/core_esp8266_wiring_pwm.c b/sonoff/core_esp8266_wiring_pwm.c index 92d73a4b7..780190059 100644 --- a/sonoff/core_esp8266_wiring_pwm.c +++ b/sonoff/core_esp8266_wiring_pwm.c @@ -18,8 +18,10 @@ License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +// Use PWM from core 2.4.0 as all other version produce flicker when settings are saved to flash. Still true for 2.5.0 //#include -//#ifdef ARDUINO_ESP8266_RELEASE_2_3_0 +//#if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_1) || defined(ARDUINO_ESP8266_RELEASE_2_4_2) //#warning **** Tasmota is using v2.4.0 wiring_pwm.c as planned **** #include "wiring_private.h" @@ -224,4 +226,4 @@ extern void analogWrite(uint8_t pin, int val) __attribute__ ((weak, alias("__ana extern void analogWriteFreq(uint32_t freq) __attribute__ ((weak, alias("__analogWriteFreq"))); extern void analogWriteRange(uint32_t range) __attribute__ ((weak, alias("__analogWriteRange"))); -//#endif // ARDUINO_ESP8266_RELEASE_2_3_0 +//#endif // ARDUINO_ESP8266_RELEASE diff --git a/sonoff/i18n.h b/sonoff/i18n.h index b1bec3289..2347cba22 100644 --- a/sonoff/i18n.h +++ b/sonoff/i18n.h @@ -548,7 +548,7 @@ const char HTTP_SNS_SEAPRESSURE[] PROGMEM = "%s{s}%s " D_PRESSUREATSEALEVEL "{m} const char HTTP_SNS_ANALOG[] PROGMEM = "%s{s}%s " D_ANALOG_INPUT "%d{m}%d{e}"; // {s} = , {m} = , {e} = const char HTTP_SNS_ILLUMINANCE[] PROGMEM = "%s{s}%s " D_ILLUMINANCE "{m}%d " D_UNIT_LUX "{e}"; // {s} = , {m} = , {e} = -#if defined(USE_MHZ19) || defined(USE_SENSEAIR) +#if defined(USE_MHZ19) || defined(USE_SENSEAIR) || defined(USE_AZ7798) const char HTTP_SNS_CO2[] PROGMEM = "%s{s}%s " D_CO2 "{m}%d " D_UNIT_PARTS_PER_MILLION "{e}"; // {s} = , {m} = , {e} = #endif // USE_WEBSERVER diff --git a/sonoff/language/bg-BG.h b/sonoff/language/bg-BG.h index 621a0fdb7..222c62b58 100644 --- a/sonoff/language/bg-BG.h +++ b/sonoff/language/bg-BG.h @@ -28,7 +28,7 @@ * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. * Use online command Prefix to translate cmnd, stat and tele. * - * Updated until v6.2.1.20 + * Updated until v6.4.0.1 \*********************************************************************/ //#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) @@ -77,14 +77,14 @@ #define D_DATA "Данни" #define D_DARKLIGHT "Тъмна" #define D_DEBUG "Дебъгване" -#define D_DISABLED "Деактивиран" +#define D_DISABLED "Забранен" #define D_DISTANCE "Разстояние" #define D_DNS_SERVER "DNS Сървър" #define D_DONE "Изпълнено" -#define D_DST_TIME "DST" +#define D_DST_TIME "Лятно време" #define D_ECO2 "eCO₂" #define D_EMULATION "Емулация" -#define D_ENABLED "Активиран" +#define D_ENABLED "Разрешен" #define D_ERASE "Изтриване" #define D_ERROR "Грешка" #define D_FAHRENHEIT "Фаренхайт" @@ -128,11 +128,11 @@ #define D_POWERUSAGE_APPARENT "Пълна мощност" #define D_POWERUSAGE_REACTIVE "Реактивна мощност" #define D_PRESSURE "Налягане" -#define D_PRESSUREATSEALEVEL "Налягане на морското ниво" +#define D_PRESSUREATSEALEVEL "Налягане при морското ниво" #define D_PROGRAM_FLASH_SIZE "Размер на флаш паметта за програми" #define D_PROGRAM_SIZE "Размер на програмата" #define D_PROJECT "Проект" -#define D_RAIN "Rain" +#define D_RAIN "Дъжд" #define D_RECEIVED "Получено" #define D_RESTART "Рестарт" #define D_RESTARTING "Рестартиране" @@ -393,7 +393,7 @@ #define D_CONFIGURE_TIMER "Конфигуриране на таймер" #define D_TIMER_PARAMETERS "Параметри на таймера" #define D_TIMER_ENABLE "Активиране на таймера" -#define D_TIMER_ARM "Arm" +#define D_TIMER_ARM "Активиран" #define D_TIMER_TIME "Време" #define D_TIMER_DAYS "Дни" #define D_TIMER_REPEAT "Повтори" @@ -537,7 +537,9 @@ #define D_SENSOR_SSPI_SCLK "SSPI SCLK" #define D_SENSOR_SSPI_CS "SSPI CS" #define D_SENSOR_SSPI_DC "SSPI DC" -#define D_SENSOR_RF_SENSOR "RF Sensor" +#define D_SENSOR_RF_SENSOR "RF датчик" +#define D_SENSOR_AZ_RX "AZ Rx" +#define D_SENSOR_AZ_TX "AZ Tx" // Units #define D_UNIT_AMPERE "A" @@ -597,13 +599,13 @@ #define D_LOG_WIFI "WIF: " // Wifi //SDM220 -#define D_PHASE_ANGLE "Phase Angle" -#define D_IMPORT_ACTIVE "Import Active" -#define D_EXPORT_ACTIVE "Export Active" -#define D_IMPORT_REACTIVE "Import Reactive" -#define D_EXPORT_REACTIVE "Export Reactive" -#define D_TOTAL_REACTIVE "Total Reactive" +#define D_PHASE_ANGLE "Фазов ъгъл" +#define D_IMPORT_ACTIVE "Входна активна мощност" +#define D_EXPORT_ACTIVE "Изходна активна мощност" +#define D_IMPORT_REACTIVE "Входна реактивна мощност" +#define D_EXPORT_REACTIVE "Изходна реактивна мощност" +#define D_TOTAL_REACTIVE "Общо реактивна мощност" #define D_UNIT_KWARH "kVArh" -#define D_UNIT_ANGLE "Deg" +#define D_UNIT_ANGLE "°" #endif // _LANGUAGE_BG_BG_H_ diff --git a/sonoff/language/cs-CZ.h b/sonoff/language/cs-CZ.h index 461de3a4d..45f55af6a 100644 --- a/sonoff/language/cs-CZ.h +++ b/sonoff/language/cs-CZ.h @@ -538,6 +538,8 @@ #define D_SENSOR_SSPI_CS "SSPI CS" #define D_SENSOR_SSPI_DC "SSPI DC" #define D_SENSOR_RF_SENSOR "RF Sensor" +#define D_SENSOR_AZ_RX "AZ Rx" +#define D_SENSOR_AZ_TX "AZ Tx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/de-DE.h b/sonoff/language/de-DE.h index 293de0830..23d8de9ed 100644 --- a/sonoff/language/de-DE.h +++ b/sonoff/language/de-DE.h @@ -538,6 +538,8 @@ #define D_SENSOR_SSPI_CS "SSPI CS" #define D_SENSOR_SSPI_DC "SSPI DC" #define D_SENSOR_RF_SENSOR "RF Sensor" +#define D_SENSOR_AZ_RX "AZ Rx" +#define D_SENSOR_AZ_TX "AZ Tx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/el-GR.h b/sonoff/language/el-GR.h index 2148baf42..a8b48ed35 100644 --- a/sonoff/language/el-GR.h +++ b/sonoff/language/el-GR.h @@ -538,6 +538,8 @@ #define D_SENSOR_SSPI_CS "SSPI CS" #define D_SENSOR_SSPI_DC "SSPI DC" #define D_SENSOR_RF_SENSOR "RF Sensor" +#define D_SENSOR_AZ_RX "AZ Rx" +#define D_SENSOR_AZ_TX "AZ Tx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/en-GB.h b/sonoff/language/en-GB.h index 70825c5e3..2ea9c249a 100644 --- a/sonoff/language/en-GB.h +++ b/sonoff/language/en-GB.h @@ -538,6 +538,8 @@ #define D_SENSOR_SSPI_CS "SSPI CS" #define D_SENSOR_SSPI_DC "SSPI DC" #define D_SENSOR_RF_SENSOR "RF Sensor" +#define D_SENSOR_AZ_RX "AZ Rx" +#define D_SENSOR_AZ_TX "AZ Tx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/es-AR.h b/sonoff/language/es-AR.h index 011c7a248..435f230ad 100644 --- a/sonoff/language/es-AR.h +++ b/sonoff/language/es-AR.h @@ -538,6 +538,8 @@ #define D_SENSOR_SSPI_CS "SSPI CS" #define D_SENSOR_SSPI_DC "SSPI DC" #define D_SENSOR_RF_SENSOR "RF Sensor" +#define D_SENSOR_AZ_RX "AZ Rx" +#define D_SENSOR_AZ_TX "AZ Tx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/fr-FR.h b/sonoff/language/fr-FR.h index 642b4686d..0638b408a 100644 --- a/sonoff/language/fr-FR.h +++ b/sonoff/language/fr-FR.h @@ -538,6 +538,8 @@ #define D_SENSOR_SSPI_CS "SSPI CS" #define D_SENSOR_SSPI_DC "SSPI DC" #define D_SENSOR_RF_SENSOR "RF Sensor" +#define D_SENSOR_AZ_RX "AZ Rx" +#define D_SENSOR_AZ_TX "AZ Tx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/he-HE.h b/sonoff/language/he-HE.h index 7dc8585db..cd1b0e92e 100644 --- a/sonoff/language/he-HE.h +++ b/sonoff/language/he-HE.h @@ -538,6 +538,8 @@ #define D_SENSOR_SSPI_CS "SSPI CS" #define D_SENSOR_SSPI_DC "SSPI DC" #define D_SENSOR_RF_SENSOR "RF Sensor" +#define D_SENSOR_AZ_RX "AZ Rx" +#define D_SENSOR_AZ_TX "AZ Tx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/hu-HU.h b/sonoff/language/hu-HU.h index 707bb24ec..244d55776 100644 --- a/sonoff/language/hu-HU.h +++ b/sonoff/language/hu-HU.h @@ -538,6 +538,8 @@ #define D_SENSOR_SSPI_CS "SSPI CS" #define D_SENSOR_SSPI_DC "SSPI DC" #define D_SENSOR_RF_SENSOR "RF Sensor" +#define D_SENSOR_AZ_RX "AZ Rx" +#define D_SENSOR_AZ_TX "AZ Tx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/it-IT.h b/sonoff/language/it-IT.h index 4e1481cec..a6d0d693c 100644 --- a/sonoff/language/it-IT.h +++ b/sonoff/language/it-IT.h @@ -172,10 +172,10 @@ #define D_UV_INDEX_6 "BurnL3" #define D_UV_INDEX_7 "OoR" #define D_UV_LEVEL "Livello UV" -#define D_UV_POWER "UV Power" +#define D_UV_POWER "Intensità UV" #define D_VERSION "Versione" #define D_VOLTAGE "Tensione" -#define D_WEIGHT "Weight" +#define D_WEIGHT "Peso" #define D_WARMLIGHT "Calda" #define D_WEB_SERVER "Web Server" @@ -218,7 +218,7 @@ #define D_ERASED_SECTOR "Settore cancellato" // xdrv_02_webserver.ino -#define D_NOSCRIPT "To use Tasmota, please enable JavaScript" +#define D_NOSCRIPT "Abilitare JavaScript per utilizzare Tasmota" #define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "MINIMAL firmware - effettuare aggiornamento" #define D_WEBSERVER_ACTIVE_ON "Web server attivo su" #define D_WITH_IP_ADDRESS "con indirizzo IP" @@ -392,7 +392,7 @@ // xdrv_09_timers.ino #define D_CONFIGURE_TIMER "Configura Timer" #define D_TIMER_PARAMETERS "Parametri Timer" -#define D_TIMER_ENABLE "Abilita Timers" +#define D_TIMER_ENABLE "Abilita Timer" #define D_TIMER_ARM "Attiva" #define D_TIMER_TIME "Ora" #define D_TIMER_DAYS "Giorni" @@ -458,23 +458,23 @@ #define D_GZ_AXIS "Gyro Z-Axis" // xsns_34_hx711.ino -#define D_HX_CAL_REMOVE "Remove weigth" -#define D_HX_CAL_REFERENCE "Load reference weigth" -#define D_HX_CAL_DONE "Calibrated" -#define D_HX_CAL_FAIL "Calibration failed" -#define D_RESET_HX711 "Reset Scale" -#define D_CONFIGURE_HX711 "Configure Scale" -#define D_HX711_PARAMETERS "Scale parameters" -#define D_ITEM_WEIGHT "Item weight" -#define D_REFERENCE_WEIGHT "Reference weigth" -#define D_CALIBRATE "Calibrate" -#define D_CALIBRATION "Calibration" +#define D_HX_CAL_REMOVE "Rimuovere peso" +#define D_HX_CAL_REFERENCE "Caricare peso di riferimento" +#define D_HX_CAL_DONE "Calibrato" +#define D_HX_CAL_FAIL "Calibrazione fallita" +#define D_RESET_HX711 "Reset Scala" +#define D_CONFIGURE_HX711 "Configura Scala" +#define D_HX711_PARAMETERS "Parametri Scala" +#define D_ITEM_WEIGHT "Peso oggetto" +#define D_REFERENCE_WEIGHT "Peso di riferimento" +#define D_CALIBRATE "Calibrato" +#define D_CALIBRATION "Calibrazione" //xsns_35_tx20.ino -#define D_TX20_WIND_DIRECTION "Wind Direction" -#define D_TX20_WIND_SPEED "Wind Speed" -#define D_TX20_WIND_SPEED_AVG "Wind Speed Avg" -#define D_TX20_WIND_SPEED_MAX "Wind Speed Max" +#define D_TX20_WIND_DIRECTION "Direzione Vento" +#define D_TX20_WIND_SPEED "Velocità Vento" +#define D_TX20_WIND_SPEED_AVG "Velocità Media Vento" +#define D_TX20_WIND_SPEED_MAX "Velocità Massima Vento" #define D_TX20_NORTH "N" #define D_TX20_EAST "E" #define D_TX20_SOUTH "S" @@ -538,6 +538,8 @@ #define D_SENSOR_SSPI_CS "SSPI CS" #define D_SENSOR_SSPI_DC "SSPI DC" #define D_SENSOR_RF_SENSOR "RF Sensor" +#define D_SENSOR_AZ_RX "AZ Rx" +#define D_SENSOR_AZ_TX "AZ Tx" // Units #define D_UNIT_AMPERE "A" @@ -597,13 +599,13 @@ #define D_LOG_WIFI "WIF: " // Wifi //SDM220 -#define D_PHASE_ANGLE "Phase Angle" -#define D_IMPORT_ACTIVE "Import Active" -#define D_EXPORT_ACTIVE "Export Active" -#define D_IMPORT_REACTIVE "Import Reactive" -#define D_EXPORT_REACTIVE "Export Reactive" -#define D_TOTAL_REACTIVE "Total Reactive" +#define D_PHASE_ANGLE "Angolo Fase" +#define D_IMPORT_ACTIVE "Potenza Attiva Importata" +#define D_EXPORT_ACTIVE "Potenza Attiva Esportata" +#define D_IMPORT_REACTIVE "Potenza Reattiva Importata" +#define D_EXPORT_REACTIVE "Potenza Reattiva Esportata" +#define D_TOTAL_REACTIVE "Potenza Reattiva Totale" #define D_UNIT_KWARH "kVArh" -#define D_UNIT_ANGLE "Deg" +#define D_UNIT_ANGLE "°" #endif // _LANGUAGE_IT_IT_H_ diff --git a/sonoff/language/nl-NL.h b/sonoff/language/nl-NL.h index 3383228ed..b3bf10989 100644 --- a/sonoff/language/nl-NL.h +++ b/sonoff/language/nl-NL.h @@ -538,6 +538,8 @@ #define D_SENSOR_SSPI_CS "SSPI CS" #define D_SENSOR_SSPI_DC "SSPI DC" #define D_SENSOR_RF_SENSOR "RF Sensor" +#define D_SENSOR_AZ_RX "AZ Rx" +#define D_SENSOR_AZ_TX "AZ Tx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/pl-PL.h b/sonoff/language/pl-PL.h index 0019c8d53..d80826751 100644 --- a/sonoff/language/pl-PL.h +++ b/sonoff/language/pl-PL.h @@ -538,6 +538,8 @@ #define D_SENSOR_SSPI_CS "SSPI CS" #define D_SENSOR_SSPI_DC "SSPI DC" #define D_SENSOR_RF_SENSOR "RF Sensor" +#define D_SENSOR_AZ_RX "AZ Rx" +#define D_SENSOR_AZ_TX "AZ Tx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/pt-BR.h b/sonoff/language/pt-BR.h index e8e536606..7add978f0 100644 --- a/sonoff/language/pt-BR.h +++ b/sonoff/language/pt-BR.h @@ -538,6 +538,8 @@ #define D_SENSOR_SSPI_CS "SSPI CS" #define D_SENSOR_SSPI_DC "SSPI DC" #define D_SENSOR_RF_SENSOR "RF Sensor" +#define D_SENSOR_AZ_RX "AZ Rx" +#define D_SENSOR_AZ_TX "AZ Tx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/pt-PT.h b/sonoff/language/pt-PT.h index 58a39fff0..9869762b8 100644 --- a/sonoff/language/pt-PT.h +++ b/sonoff/language/pt-PT.h @@ -538,6 +538,8 @@ #define D_SENSOR_SSPI_CS "SSPI CS" #define D_SENSOR_SSPI_DC "SSPI DC" #define D_SENSOR_RF_SENSOR "RF Sensor" +#define D_SENSOR_AZ_RX "AZ Rx" +#define D_SENSOR_AZ_TX "AZ Tx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/ru-RU.h b/sonoff/language/ru-RU.h index 85813373a..d0a3f6933 100644 --- a/sonoff/language/ru-RU.h +++ b/sonoff/language/ru-RU.h @@ -538,6 +538,8 @@ #define D_SENSOR_SSPI_CS "SSPI CS" #define D_SENSOR_SSPI_DC "SSPI DC" #define D_SENSOR_RF_SENSOR "RF Sensor" +#define D_SENSOR_AZ_RX "AZ Rx" +#define D_SENSOR_AZ_TX "AZ Tx" // Units #define D_UNIT_AMPERE "А" diff --git a/sonoff/language/sk-SK.h b/sonoff/language/sk-SK.h new file mode 100644 index 000000000..f46372426 --- /dev/null +++ b/sonoff/language/sk-SK.h @@ -0,0 +1,611 @@ +/* + sk-SK.h - localization for Slovak with diacritics - Slovak for Sonoff-Tasmota + + Copyright (C) 2018 Vladimír Jendroľ + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef _LANGUAGE_SK_SK_H_ +#define _LANGUAGE_SK_SK_H_ + +/*************************** ATTENTION *******************************\ + * + * Due to memory constraints only UTF-8 is supported. + * To save code space keep text as short as possible. + * Time and Date provided by SDK can not be localized (yet). + * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. + * Use online command Prefix to translate cmnd, stat and tele. + * + * Updated until v6.2.1.14 +\*********************************************************************/ + +//#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) + +#define LANGUAGE_LCID 1029 +// HTML (ISO 639-1) Language Code +#define D_HTML_LANGUAGE "sk" + +// "2017-03-07T11:08:02" - ISO8601:2004 +#define D_YEAR_MONTH_SEPARATOR "-" +#define D_MONTH_DAY_SEPARATOR "-" +#define D_DATE_TIME_SEPARATOR "T" +#define D_HOUR_MINUTE_SEPARATOR ":" +#define D_MINUTE_SECOND_SEPARATOR ":" + +#define D_DAY3LIST "Ne Po Ut St Št Pi So" +#define D_MONTH3LIST "JanFebMarAprMájJúnJúlAugSepOktNovDec" + +// Non JSON decimal separator +#define D_DECIMAL_SEPARATOR "," + +// Common +#define D_ADMIN "Admin" +#define D_AIR_QUALITY "Kvalita vzduchu" +#define D_AP "AP" // Access Point +#define D_AS "ako" +#define D_AUTO "AUTO" +#define D_BLINK "Blikanie" +#define D_BLINKOFF "BlikanieVyp" +#define D_BOOT_COUNT "Počítadlo spustení" +#define D_BRIGHTLIGHT "Svetlý" +#define D_BSSID "BSSId" +#define D_BUTTON "Tlačídlo" +#define D_BY "by" // Written by me +#define D_BYTES "Bytov" +#define D_CELSIUS "°C" +#define D_CHANNEL "Kanál" +#define D_CO2 "CO2" +#define D_CODE "kód" // Button code +#define D_COLDLIGHT "Studené svetlo" +#define D_COMMAND "Príkaz" +#define D_CONNECTED "...pripojené" +#define D_COUNT "Počítaj" +#define D_COUNTER "Počítadlo" +#define D_CURRENT "Prúd" // As in Voltage and Current +#define D_DATA "Dáta" +#define D_DARKLIGHT "Tmavý" +#define D_DEBUG "Debug" +#define D_DISABLED "Zablokované" +#define D_DISTANCE "Vzdialenosť" +#define D_DNS_SERVER "Server DNS" +#define D_DONE "Hotovo" +#define D_DST_TIME "DST" +#define D_ECO2 "eCO2" +#define D_EMULATION "Emulácia" +#define D_ENABLED "Otvorený" +#define D_ERASE "Zmaž" +#define D_ERROR "Chyba" +#define D_FAHRENHEIT "Fahrenheita" +#define D_FAILED "chyba" +#define D_FALLBACK "Záložný" +#define D_FALLBACK_TOPIC "Záložný topic" +#define D_FALSE "Nepravda" +#define D_FILE "Súbor" +#define D_FREE_MEMORY "Voľná pamäť" +#define D_FREQUENCY "Frekvencia" +#define D_GAS "Plyn" +#define D_GATEWAY "Predvolená brána" +#define D_GROUP "Skupina" +#define D_HOST "Server" +#define D_HOSTNAME "Názov servera" +#define D_HUMIDITY "Vlhkosť" +#define D_ILLUMINANCE "Osvetlenie" +#define D_IMMEDIATE "Okamžité" // Button immediate +#define D_INDEX "Index" +#define D_INFO "Informácie" +#define D_INFRARED "Infračervené" +#define D_INITIALIZED "Inicializovaný" +#define D_IP_ADDRESS "Adresa IP" +#define D_LIGHT "Svetlo" +#define D_LWT "LWT" +#define D_MODULE "Modul" +#define D_MQTT "MQTT" +#define D_MULTI_PRESS "multi-stlačenie" +#define D_NOISE "Hluk" +#define D_NONE "Žiadny" +#define D_OFF "Vyp." +#define D_OFFLINE "Neaktívny" +#define D_OK "OK" +#define D_ON "Zap." +#define D_ONLINE "Aktívny" +#define D_PASSWORD "Heslo" +#define D_PORT "Port" +#define D_POWER_FACTOR "Účinník" +#define D_POWERUSAGE "Príkon" +#define D_POWERUSAGE_ACTIVE "Činný príkon" +#define D_POWERUSAGE_APPARENT "Zdanlivý príkon" +#define D_POWERUSAGE_REACTIVE "Jalový príkon" +#define D_PRESSURE "Tlak" +#define D_PRESSUREATSEALEVEL "Tlak na hladine mora" +#define D_PROGRAM_FLASH_SIZE "Veľkosť flash pamäte" +#define D_PROGRAM_SIZE "Veľkosť programu" +#define D_PROJECT "Projekt" +#define D_RAIN "Dážď" +#define D_RECEIVED "Prijatý" +#define D_RESTART "Reštart" +#define D_RESTARTING "Reštartuje sa" +#define D_RESTART_REASON "Príčina reštartu" +#define D_RESTORE "Obnoviť" +#define D_RETAINED "Zachované" +#define D_RULE "Pravidlo" +#define D_SAVE "Ulož" +#define D_SENSOR "Senzor" +#define D_SSID "SSID" +#define D_START "Spustiť" +#define D_STD_TIME "STD" +#define D_STOP "Stop" +#define D_SUBNET_MASK "Maska podsiete" +#define D_SUBSCRIBE_TO "Prihlásiť do" +#define D_SUCCESSFUL "úspešné." +#define D_SUNRISE "Svitanie" +#define D_SUNSET "Súmrak" +#define D_TEMPERATURE "Teplota" +#define D_TO "do" +#define D_TOGGLE "Prepni" +#define D_TOPIC "Topic" +#define D_TRANSMIT "Odošli" +#define D_TRUE "Pravda" +#define D_TVOC "TVOC" +#define D_UPGRADE "aktualizáciu" +#define D_UPLOAD "Nahrávanie..." +#define D_UPTIME "Uptime" +#define D_USER "Používateľ" +#define D_UTC_TIME "UTC" +#define D_UV_INDEX "UV Index" +#define D_UV_INDEX_1 "Nízky" +#define D_UV_INDEX_2 "Stredný" +#define D_UV_INDEX_3 "Vysoký" +#define D_UV_INDEX_4 "Nebezpečný" +#define D_UV_INDEX_5 "Popál1/2" +#define D_UV_INDEX_6 "Popál3" +#define D_UV_INDEX_7 "MimoRozsah" +#define D_UV_LEVEL "úroveň UV" +#define D_UV_POWER "UV Power" +#define D_VERSION "Verzia" +#define D_VOLTAGE "Napätie" +#define D_WEIGHT "Hmotnosť" +#define D_WARMLIGHT "Teplé svetlo" +#define D_WEB_SERVER "Web Server" + +// sonoff.ino +#define D_WARNING_MINIMAL_VERSION "UPOZORNENIE Táto verzia nepodporuje trvalé nastavenia" +#define D_LEVEL_10 "úroveň 1-0" +#define D_LEVEL_01 "úroveň 0-1" +#define D_SERIAL_LOGGING_DISABLED "Logovanie na sériovom porte ukončené" +#define D_SYSLOG_LOGGING_REENABLED "Obnovený zápis do Syslog" + +#define D_SET_BAUDRATE_TO "Nastaviť rýchlosti prenosu na" +#define D_RECEIVED_TOPIC "Prijatý topic" +#define D_DATA_SIZE "Veľkosť dát" +#define D_ANALOG_INPUT "Analógový vstup" + +// support.ino +#define D_OSWATCH "osWatch" +#define D_BLOCKED_LOOP "Zablokovanie slučky" +#define D_WPS_FAILED_WITH_STATUS "Chyba WPSconfig so statusom" +#define D_ACTIVE_FOR_3_MINUTES "aktívny 3 minúty" +#define D_FAILED_TO_START "nepodarilo sa spustiť" +#define D_PATCH_ISSUE_2186 "Chyba 2186" +#define D_CONNECTING_TO_AP "Pripájanie k AP" +#define D_IN_MODE "v režime" +#define D_CONNECT_FAILED_NO_IP_ADDRESS "Chyba pripojenia, nebola obdržaná IP adresa" +#define D_CONNECT_FAILED_AP_NOT_REACHED "Chyba pripojenia, nedostupný AP" +#define D_CONNECT_FAILED_WRONG_PASSWORD "Chyba pripojenia, nesprávne heslo pre AP" +#define D_CONNECT_FAILED_AP_TIMEOUT "Chyba pripojenia, uplynul AP timeout" +#define D_ATTEMPTING_CONNECTION "Pripájanie..." +#define D_CHECKING_CONNECTION "Skúška spojenia..." +#define D_QUERY_DONE "Vyslanie požiadavky. Nájdená služba MQTT" +#define D_MQTT_SERVICE_FOUND "Služba MQTT bola nájdená" +#define D_FOUND_AT "nájdené v" +#define D_SYSLOG_HOST_NOT_FOUND "Syslog Host nebol nájdený" + +// settings.ino +#define D_SAVED_TO_FLASH_AT "Uložené do flash pamäte v" +#define D_LOADED_FROM_FLASH_AT "Stiahnuté z flash pamäte z" +#define D_USE_DEFAULTS "Použi prednastavené hodnoty" +#define D_ERASED_SECTOR "Zmazaný sektor" + +// xdrv_02_webserver.ino +#define D_NOSCRIPT "Pre používanie prostredia Tasmota povoľte JavaScript" +#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "Firmware MINIMÁLNY - prosím aktualizujte" +#define D_WEBSERVER_ACTIVE_ON "Aktívny Web server" +#define D_WITH_IP_ADDRESS "na IP adrese" +#define D_WEBSERVER_STOPPED "Web server zastavený" +#define D_FILE_NOT_FOUND "Súbor nebol nájdený" +#define D_REDIRECTED "Presmerovanie na vlastný portál" +#define D_WIFIMANAGER_SET_ACCESSPOINT_AND_STATION "Wi-Fi manažér nastaví AP a zachová STA" +#define D_WIFIMANAGER_SET_ACCESSPOINT "Wi-Fi manažér nastaví Access Point" +#define D_TRYING_TO_CONNECT "Skúšam pripojiť zariadenie k sieti" + +#define D_RESTART_IN "Reštart" +#define D_SECONDS "sekúnd" +#define D_DEVICE_WILL_RESTART "Zariadenie bude reštartované behom niekoľkých sekúnd" +#define D_BUTTON_TOGGLE "Prepínač" +#define D_CONFIGURATION "Nastavenia" +#define D_INFORMATION "Informácie" +#define D_FIRMWARE_UPGRADE "Aktualizácia firmware" +#define D_CONSOLE "Konzola" +#define D_CONFIRM_RESTART "Potvrdenie reštartu" + +#define D_CONFIGURE_MODULE "Nastavenia modulu" +#define D_CONFIGURE_WIFI "Nastavenia WiFi" +#define D_CONFIGURE_MQTT "Nastavenia MQTT" +#define D_CONFIGURE_DOMOTICZ "Nastavenia Domoticz" +#define D_CONFIGURE_LOGGING "Nastavenia logovania" +#define D_CONFIGURE_OTHER "Ostatné nastavenia" +#define D_CONFIRM_RESET_CONFIGURATION "Potvrdenie resetu nastavení" +#define D_RESET_CONFIGURATION "Reset nastavení" +#define D_BACKUP_CONFIGURATION "Záloha nastavení" +#define D_RESTORE_CONFIGURATION "Obnovenie nastavení" +#define D_MAIN_MENU "Hlavné menu" + +#define D_MODULE_PARAMETERS "Nastavenia modulu" +#define D_MODULE_TYPE "Typ modulu" +#define D_GPIO "GPIO" +#define D_SERIAL_IN "Serial In" +#define D_SERIAL_OUT "Serial Out" + +#define D_WIFI_PARAMETERS "Nastavenia WiFi" +#define D_SCAN_FOR_WIFI_NETWORKS "Sken WiFi sietí" +#define D_SCAN_DONE "Sken dokončený" +#define D_NO_NETWORKS_FOUND "Sieť nebola nájdená" +#define D_REFRESH_TO_SCAN_AGAIN "Zopakovať sken" +#define D_DUPLICATE_ACCESSPOINT "Kópia AP" +#define D_SKIPPING_LOW_QUALITY "Preskočenie z dôvodu kvality signálu" +#define D_RSSI "RSSI" +#define D_WEP "WEP" +#define D_WPA_PSK "WPA PSK" +#define D_WPA2_PSK "WPA2 PSK" +#define D_AP1_SSID "AP1 SSID" +#define D_AP1_PASSWORD "Heslo AP1" +#define D_AP2_SSID "AP2 SSID" +#define D_AP2_PASSWORD "Heslo AP2" + +#define D_MQTT_PARAMETERS "Nastavenia MQTT" +#define D_CLIENT "Klient" +#define D_FULL_TOPIC "Celý topic" + +#define D_LOGGING_PARAMETERS "Voľba logovania" +#define D_SERIAL_LOG_LEVEL "Sériová úroveň logu" +#define D_WEB_LOG_LEVEL "Webová úroveň logu" +#define D_SYS_LOG_LEVEL "Systemová úroveň logu" +#define D_MORE_DEBUG "Viac debug informácí" +#define D_SYSLOG_HOST "Syslog host" +#define D_SYSLOG_PORT "Syslog port" +#define D_TELEMETRY_PERIOD "Interval telemetrie" + +#define D_OTHER_PARAMETERS "Ostatné nastavenia" +#define D_WEB_ADMIN_PASSWORD "Heslo Web administrátora" +#define D_MQTT_ENABLE "MQTT aktívne" +#define D_FRIENDLY_NAME "Friendly Name" +#define D_BELKIN_WEMO "Belkin WeMo" +#define D_HUE_BRIDGE "Hue Bridge" +#define D_SINGLE_DEVICE "single device" +#define D_MULTI_DEVICE "multi device" + +#define D_SAVE_CONFIGURATION "Ulož nastavenia" +#define D_CONFIGURATION_SAVED "Nastavenia uložené" +#define D_CONFIGURATION_RESET "Nastavenia resetované" + +#define D_PROGRAM_VERSION "Verzia programu" +#define D_BUILD_DATE_AND_TIME "Datum a čas kompilácie" +#define D_CORE_AND_SDK_VERSION "Verzia Core/SDK" +#define D_FLASH_WRITE_COUNT "Počet zápisov do pamäte" +#define D_MAC_ADDRESS "Adresa MAC" +#define D_MQTT_HOST "Host MQTT" +#define D_MQTT_PORT "Port MQTT" +#define D_MQTT_CLIENT "Klient MQTT" +#define D_MQTT_USER "Používateľ MQTT" +#define D_MQTT_TOPIC "Topic MQTT" +#define D_MQTT_GROUP_TOPIC "Topic skupiny MQTT" +#define D_MQTT_FULL_TOPIC "Celý topic MQTT" +#define D_MDNS_DISCOVERY "Získavanie mDNS" +#define D_MDNS_ADVERTISE "Rozosielanie mDNS" +#define D_ESP_CHIP_ID "ID systému ESP" +#define D_FLASH_CHIP_ID "ID systému flash pamäte" +#define D_FLASH_CHIP_SIZE "Veľkosť flash" +#define D_FREE_PROGRAM_SPACE "Voľné místo pre program" + +#define D_UPGRADE_BY_WEBSERVER "Aktualizácia z Web serveru" +#define D_OTA_URL "URL OTA" +#define D_START_UPGRADE "Spustiť aktualizáciu" +#define D_UPGRADE_BY_FILE_UPLOAD "Aktualizácia nahraním súboru" +#define D_UPLOAD_STARTED "Nahrávanie spustené" +#define D_UPGRADE_STARTED "Aktualizácia spustená" +#define D_UPLOAD_DONE "Nahrávanie ukončené" +#define D_UPLOAD_ERR_1 "Súbor nebol vybraný" +#define D_UPLOAD_ERR_2 "Málo miesta" +#define D_UPLOAD_ERR_3 "Magický byte má hodnotu inú než 0xE9" +#define D_UPLOAD_ERR_4 "Veľkosť programu je väčšia než skutočná veľkosť flash pamäte" +#define D_UPLOAD_ERR_5 "Chyba nahrávania, nesúhlasia porovnávané bity" +#define D_UPLOAD_ERR_6 "Chyba nahrávania. Spustený zápis do logu na úrovni 3" +#define D_UPLOAD_ERR_7 "Nahrávanie prerušené" +#define D_UPLOAD_ERR_8 "Nesprávny súbor" +#define D_UPLOAD_ERR_9 "Súbor je príliš veľký" +#define D_UPLOAD_ERR_10 "Chyba inicializácie RF chipu" +#define D_UPLOAD_ERR_11 "Chyba zmazania RF chipu" +#define D_UPLOAD_ERR_12 "Chyba pri zápise do RF chipu" +#define D_UPLOAD_ERR_13 "Chyba dekódovania RF firmwaru" +#define D_UPLOAD_ERROR_CODE "Chyba nahrávania" + +#define D_ENTER_COMMAND "Vlož príkaz" +#define D_ENABLE_WEBLOG_FOR_RESPONSE "Zapni úroveň 2 zápisu Weblog, ak je očekávaná odpoveď" +#define D_NEED_USER_AND_PASSWORD "Vyžadovaný používateľ=&heslo=" + +// xdrv_01_mqtt.ino +#define D_FINGERPRINT "Verifikuj odtlačok TLS..." +#define D_TLS_CONNECT_FAILED_TO "Nepripojené TLS do" +#define D_RETRY_IN "Zopakujem o" +#define D_VERIFIED "Overený odtlačok " +#define D_INSECURE "Nesprávne pripojenie z dôvodu chybného odtlačku TLS" +#define D_CONNECT_FAILED_TO "Nepodarilo sa nadviazať spojenie" + +// xplg_wemohue.ino +#define D_MULTICAST_DISABLED "Multicast je vypnutý" +#define D_MULTICAST_REJOINED "Multicast opäť pripojený" +#define D_MULTICAST_JOIN_FAILED "Multicast neúspešný" +#define D_FAILED_TO_SEND_RESPONSE "Nepodarilo sa odoslať odpoveď" + +#define D_WEMO "WeMo" +#define D_WEMO_BASIC_EVENT "WeMo základná udalosť" +#define D_WEMO_EVENT_SERVICE "WeMo servisná udalosť" +#define D_WEMO_META_SERVICE "WeMo meta udalosť" +#define D_WEMO_SETUP "WeMo setup" +#define D_RESPONSE_SENT "Odpoveď odoslaná" + +#define D_HUE "Hue" +#define D_HUE_BRIDGE_SETUP "Hue setup" +#define D_HUE_API_NOT_IMPLEMENTED "Hue API nie je implementované" +#define D_HUE_API "Hue API" +#define D_HUE_POST_ARGS "Hue POST args" +#define D_3_RESPONSE_PACKETS_SENT "3 pakety odpovede odoslané" + +// xdrv_07_domoticz.ino +#define D_DOMOTICZ_PARAMETERS "Nastavenia Domoticz" +#define D_DOMOTICZ_IDX "Idx" +#define D_DOMOTICZ_KEY_IDX "Key idx" +#define D_DOMOTICZ_SWITCH_IDX "Spinac idx" +#define D_DOMOTICZ_SENSOR_IDX "Sensor idx" + #define D_DOMOTICZ_TEMP "Temp" + #define D_DOMOTICZ_TEMP_HUM "Temp,Vlhk" + #define D_DOMOTICZ_TEMP_HUM_BARO "Temp,Vlhk,Tlak" + #define D_DOMOTICZ_POWER_ENERGY "Príkon,Energia" + #define D_DOMOTICZ_ILLUMINANCE "Osvetlenie" + #define D_DOMOTICZ_COUNT "Počítadlo/PM1" + #define D_DOMOTICZ_VOLTAGE "Napätie/PM2,5" + #define D_DOMOTICZ_CURRENT "Prúd/PM10" + #define D_DOMOTICZ_AIRQUALITY "AirQuality" +#define D_DOMOTICZ_UPDATE_TIMER "Aktualizácia časovača" + +// xdrv_09_timers.ino +#define D_CONFIGURE_TIMER "Nastavenia časovača" +#define D_TIMER_PARAMETERS "Časovač" +#define D_TIMER_ENABLE "Povoľ časovače" +#define D_TIMER_ARM "Aktívne" +#define D_TIMER_TIME "Čas" +#define D_TIMER_DAYS "Dni" +#define D_TIMER_REPEAT "Opakovať" +#define D_TIMER_OUTPUT "Výstup" +#define D_TIMER_ACTION "Napájanie" + +// xdrv_10_knx.ino +#define D_CONFIGURE_KNX "Nastavenie KNX" +#define D_KNX_PARAMETERS "KNX parametre" +#define D_KNX_GENERAL_CONFIG "Všeobecné" +#define D_KNX_PHYSICAL_ADDRESS "Fyzická adresa" +#define D_KNX_PHYSICAL_ADDRESS_NOTE "( Musí být jedinečná v sieti KNX )" +#define D_KNX_ENABLE "Povoľ KNX" +#define D_KNX_GROUP_ADDRESS_TO_WRITE "Dáta na odoslanie na skupinové adresy" +#define D_ADD "Pridať" +#define D_DELETE "Zmazať" +#define D_REPLY "Odpoveď" +#define D_KNX_GROUP_ADDRESS_TO_READ "Skupinové adresy pre príjem dát z" +#define D_LOG_KNX "KNX: " +#define D_RECEIVED_FROM "Prijaté z" +#define D_KNX_COMMAND_WRITE "Zapíš" +#define D_KNX_COMMAND_READ "Čítaj" +#define D_KNX_COMMAND_OTHER "Iné" +#define D_SENT_TO "pošli" +#define D_KNX_WARNING "Skupinová adresa ( 0 / 0 / 0 ) je rezervovaná a nemôže byť použitá." +#define D_KNX_ENHANCEMENT "Communication Enhancement" +#define D_KNX_TX_SLOT "KNX TX" +#define D_KNX_RX_SLOT "KNX RX" + +// xdrv_03_energy.ino +#define D_ENERGY_TODAY "Spotreba dnes" +#define D_ENERGY_YESTERDAY "Spotreba včera" +#define D_ENERGY_TOTAL "Celková spotreba" + +// xsns_05_ds18b20.ino +#define D_SENSOR_BUSY "Sensor DS18x20 obsadený" +#define D_SENSOR_CRC_ERROR "Sensor DS18x20 chyba CRC" +#define D_SENSORS_FOUND "Nanájdený senzor DS18x20" + +// xsns_06_dht.ino +#define D_TIMEOUT_WAITING_FOR "Čakanie na" +#define D_START_SIGNAL_LOW "nízký štartovací signál" +#define D_START_SIGNAL_HIGH "vysoký štartovací signál" +#define D_PULSE "impulz" +#define D_CHECKSUM_FAILURE "Chybný kontrolný súčet" + +// xsns_07_sht1x.ino +#define D_SENSOR_DID_NOT_ACK_COMMAND "Senzor neobdržal príkaz ACK" +#define D_SHT1X_FOUND "SHT1X nanájdený" + +// xsns_18_pms5003.ino +#define D_STANDARD_CONCENTRATION "CF-1 PM" // Standard Particle CF-1 Particle Matter +#define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter +#define D_PARTICALS_BEYOND "častíc" + +// xsns_32_mpu6050.ino +#define D_AX_AXIS "Accel. os-X" +#define D_AY_AXIS "Accel. os-Y" +#define D_AZ_AXIS "Accel. os-Z" +#define D_GX_AXIS "Gyro os-X" +#define D_GY_AXIS "Gyro os-Y" +#define D_GZ_AXIS "Gyro os-Z" + +// xsns_34_hx711.ino +#define D_HX_CAL_REMOVE "Odstráňte záťaž" +#define D_HX_CAL_REFERENCE "Vložte referenčnú záťaž" +#define D_HX_CAL_DONE "Skalibrováné" +#define D_HX_CAL_FAIL "Chyba kalibráce" +#define D_RESET_HX711 "Reset váhy" +#define D_CONFIGURE_HX711 "Konfiguráce váhy" +#define D_HX711_PARAMETERS "Parametre váhy" +#define D_ITEM_WEIGHT "Vlastná hmotnosť" +#define D_REFERENCE_WEIGHT "Referenčná hmotnosť" +#define D_CALIBRATE "Kalibruj" +#define D_CALIBRATION "Kalibrácia" + +//xsns_35_tx20.ino +#define D_TX20_WIND_DIRECTION "Smer vetra" +#define D_TX20_WIND_SPEED "Rýchlosť vetra" +#define D_TX20_WIND_SPEED_AVG "Priemerná rýchlosť vetra" +#define D_TX20_WIND_SPEED_MAX "Maximálna rýchlosť vetra" +#define D_TX20_NORTH "S" +#define D_TX20_EAST "V" +#define D_TX20_SOUTH "J" +#define D_TX20_WEST "Z" + +// sonoff_template.h +#define D_SENSOR_NONE "Žiaden" +#define D_SENSOR_DHT11 "DHT11" +#define D_SENSOR_AM2301 "AM2301" +#define D_SENSOR_SI7021 "SI7021" +#define D_SENSOR_DS18X20 "DS18x20" +#define D_SENSOR_I2C_SCL "I2C SCL" +#define D_SENSOR_I2C_SDA "I2C SDA" +#define D_SENSOR_WS2812 "WS2812" +#define D_SENSOR_DFR562 "MP3 Player" +#define D_SENSOR_IRSEND "IRsend" +#define D_SENSOR_SWITCH "Spínač" // Suffix "1" +#define D_SENSOR_BUTTON "Tlačidlo" // Suffix "1" +#define D_SENSOR_RELAY "Relé" // Suffix "1i" +#define D_SENSOR_LED "Led" // Suffix "1i" +#define D_SENSOR_PWM "PWM" // Suffix "1", +#define D_SENSOR_COUNTER "Počítadlo" // Suffix "1" +#define D_SENSOR_IRRECV "IRrecv" +#define D_SENSOR_MHZ_RX "MHZ Rx" +#define D_SENSOR_MHZ_TX "MHZ Tx" +#define D_SENSOR_PZEM004_RX "PZEM004 Rx" +#define D_SENSOR_PZEM016_RX "PZEM016 Rx" +#define D_SENSOR_PZEM017_RX "PZEM017 Rx" +#define D_SENSOR_PZEM0XX_TX "PZEM0XX Tx" +#define D_SENSOR_SAIR_RX "SAir Rx" +#define D_SENSOR_SAIR_TX "SAir Tx" +#define D_SENSOR_SPI_CS "SPI CS" +#define D_SENSOR_SPI_DC "SPI DC" +#define D_SENSOR_BACKLIGHT "BkLight" +#define D_SENSOR_PMS5003 "PMS5003" +#define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" +#define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" +#define D_SENSOR_SBR_RX "SerBr Rx" +#define D_SENSOR_SBR_TX "SerBr Tx" +#define D_SENSOR_SR04_TRIG "SR04 Tri" +#define D_SENSOR_SR04_ECHO "SR04 Ech" +#define D_SENSOR_SDM120_TX "SDM120/220 Tx" +#define D_SENSOR_SDM120_RX "SDM120/220 Rx" +#define D_SENSOR_SDM630_TX "SDM630 Tx" +#define D_SENSOR_SDM630_RX "SDM630 Rx" +#define D_SENSOR_TM1638_CLK "TM16 CLK" +#define D_SENSOR_TM1638_DIO "TM16 DIO" +#define D_SENSOR_TM1638_STB "TM16 STB" +#define D_SENSOR_HX711_SCK "HX711 SCK" +#define D_SENSOR_HX711_DAT "HX711 DAT" +#define D_SENSOR_TX20_TX "TX20" +#define D_SENSOR_RFSEND "RFSend" +#define D_SENSOR_RFRECV "RFrecv" +#define D_SENSOR_TUYA_TX "Tuya Tx" +#define D_SENSOR_TUYA_RX "Tuya Rx" +#define D_SENSOR_MGC3130_XFER "MGC3130 Xfer" +#define D_SENSOR_MGC3130_RESET "MGC3130 Reset" +#define D_SENSOR_SSPI_MISO "SSPI MISO" +#define D_SENSOR_SSPI_MOSI "SSPI MOSI" +#define D_SENSOR_SSPI_SCLK "SSPI SCLK" +#define D_SENSOR_SSPI_CS "SSPI CS" +#define D_SENSOR_SSPI_DC "SSPI DC" +#define D_SENSOR_RF_SENSOR "RF Senzor" +#define D_SENSOR_AZ_RX "AZ Rx" +#define D_SENSOR_AZ_TX "AZ Tx" + +// Units +#define D_UNIT_AMPERE "A" +#define D_UNIT_CENTIMETER "cm" +#define D_UNIT_HERTZ "Hz" +#define D_UNIT_HOUR "hod" +#define D_UNIT_KILOGRAM "kg" +#define D_UNIT_INCREMENTS "inc" +#define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" +#define D_UNIT_KILOOHM "kOhm" +#define D_UNIT_KILOWATTHOUR "kWh" +#define D_UNIT_LUX "lx" +#define D_UNIT_MICROGRAM_PER_CUBIC_METER "ug/m3" +#define D_UNIT_MICROMETER "um" +#define D_UNIT_MICROSECOND "us" +#define D_UNIT_MILLIAMPERE "mA" +#define D_UNIT_MILLIMETER "mm" +#define D_UNIT_MILLIMETER_MERCURY "mmHg" +#define D_UNIT_MILLISECOND "ms" +#define D_UNIT_MINUTE "min" +#define D_UNIT_PARTS_PER_BILLION "ppb" +#define D_UNIT_PARTS_PER_DECILITER "ppd" +#define D_UNIT_PARTS_PER_MILLION "ppm" +#define D_UNIT_PRESSURE "hPa" +#define D_UNIT_SECOND "sek" +#define D_UNIT_SECTORS "sektory" +#define D_UNIT_VA "VA" +#define D_UNIT_VAR "VAr" +#define D_UNIT_VOLT "V" +#define D_UNIT_WATT "W" +#define D_UNIT_WATTHOUR "Wh" +#define D_UNIT_WATT_METER_QUADRAT "W/m²" + +// Log message prefix +#define D_LOG_APPLICATION "APP: " // Application +#define D_LOG_BRIDGE "BRG: " // Bridge +#define D_LOG_CONFIG "CFG: " // Settings +#define D_LOG_COMMAND "CMD: " // Command +#define D_LOG_DEBUG "DBG: " // Debug +#define D_LOG_DHT "DHT: " // DHT sensor +#define D_LOG_DOMOTICZ "DOM: " // Domoticz +#define D_LOG_DSB "DSB: " // DS18xB20 sensor +#define D_LOG_HTTP "HTP: " // HTTP webserver +#define D_LOG_I2C "I2C: " // I2C +#define D_LOG_IRR "IRR: " // Infra Red Received +#define D_LOG_LOG "LOG: " // Logging +#define D_LOG_MODULE "MOD: " // Module +#define D_LOG_MDNS "DNS: " // mDNS +#define D_LOG_MQTT "MQT: " // MQTT +#define D_LOG_OTHER "OTH: " // Other +#define D_LOG_RESULT "RSL: " // Result +#define D_LOG_RFR "RFR: " // RF Received +#define D_LOG_SERIAL "SER: " // Serial +#define D_LOG_SHT1 "SHT: " // SHT1x sensor +#define D_LOG_UPLOAD "UPL: " // Upload +#define D_LOG_UPNP "UPP: " // UPnP +#define D_LOG_WIFI "WIF: " // Wifi + +//SDM220 +#define D_PHASE_ANGLE "Phase Angle" +#define D_IMPORT_ACTIVE "Import Active" +#define D_EXPORT_ACTIVE "Export Active" +#define D_IMPORT_REACTIVE "Import Reactive" +#define D_EXPORT_REACTIVE "Export Reactive" +#define D_TOTAL_REACTIVE "Total Reactive" +#define D_UNIT_KWARH "kVArh" +#define D_UNIT_ANGLE "Deg" + +#endif // _LANGUAGE_SK_SK_H_ diff --git a/sonoff/language/sv-SE.h b/sonoff/language/sv-SE.h index 4ae68e78c..f36162a4b 100644 --- a/sonoff/language/sv-SE.h +++ b/sonoff/language/sv-SE.h @@ -538,6 +538,8 @@ #define D_SENSOR_SSPI_CS "SSPI CS" #define D_SENSOR_SSPI_DC "SSPI DC" #define D_SENSOR_RF_SENSOR "RF Sensor" +#define D_SENSOR_AZ_RX "AZ Rx" +#define D_SENSOR_AZ_TX "AZ Tx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/tr-TR.h b/sonoff/language/tr-TR.h index 43acbaa77..06d82b9cb 100755 --- a/sonoff/language/tr-TR.h +++ b/sonoff/language/tr-TR.h @@ -538,6 +538,8 @@ #define D_SENSOR_SSPI_CS "SSPI CS" #define D_SENSOR_SSPI_DC "SSPI DC" #define D_SENSOR_RF_SENSOR "RF Sensor" +#define D_SENSOR_AZ_RX "AZ Rx" +#define D_SENSOR_AZ_TX "AZ Tx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/uk-UK.h b/sonoff/language/uk-UK.h index a924a6cf4..509c90753 100644 --- a/sonoff/language/uk-UK.h +++ b/sonoff/language/uk-UK.h @@ -538,6 +538,8 @@ #define D_SENSOR_SSPI_CS "SSPI CS" #define D_SENSOR_SSPI_DC "SSPI DC" #define D_SENSOR_RF_SENSOR "RF Sensor" +#define D_SENSOR_AZ_RX "AZ Rx" +#define D_SENSOR_AZ_TX "AZ Tx" // Units #define D_UNIT_AMPERE "А" diff --git a/sonoff/language/zh-CN.h b/sonoff/language/zh-CN.h index dfd5662e0..05bedca81 100644 --- a/sonoff/language/zh-CN.h +++ b/sonoff/language/zh-CN.h @@ -538,6 +538,8 @@ #define D_SENSOR_SSPI_CS "SSPI CS" #define D_SENSOR_SSPI_DC "SSPI DC" #define D_SENSOR_RF_SENSOR "RF Sensor" +#define D_SENSOR_AZ_RX "AZ Rx" +#define D_SENSOR_AZ_TX "AZ Tx" // Units #define D_UNIT_AMPERE "安" diff --git a/sonoff/language/zh-TW.h b/sonoff/language/zh-TW.h index abc3df93d..2780181aa 100644 --- a/sonoff/language/zh-TW.h +++ b/sonoff/language/zh-TW.h @@ -538,6 +538,8 @@ #define D_SENSOR_SSPI_CS "SSPI CS" #define D_SENSOR_SSPI_DC "SSPI DC" #define D_SENSOR_RF_SENSOR "RF Sensor" +#define D_SENSOR_AZ_RX "AZ Rx" +#define D_SENSOR_AZ_TX "AZ Tx" // Units #define D_UNIT_AMPERE "安" diff --git a/sonoff/my_user_config.h b/sonoff/my_user_config.h index 46cf3f015..5fcbecabf 100644 --- a/sonoff/my_user_config.h +++ b/sonoff/my_user_config.h @@ -209,6 +209,7 @@ //#define MY_LANGUAGE pt-BR // Portuguese in Brazil //#define MY_LANGUAGE pt-PT // Portuguese in Portugal //#define MY_LANGUAGE ru-RU // Russian in Russia +//#define MY_LANGUAGE sk-SK // Slovak in Slovakia //#define MY_LANGUAGE sv-SE // Swedish in Sweden //#define MY_LANGUAGE tr-TR // Turkish in Turkey //#define MY_LANGUAGE uk-UK // Ukrainian in Ukrain @@ -216,6 +217,7 @@ //#define MY_LANGUAGE zh-TW // Chinese (Traditional) in Taiwan // -- Wifi Config tools --------------------------- +#define WIFI_SOFT_AP_CHANNEL 1 // Soft Access Point Channel number between 1 and 13 as used by Wifi Manager web GUI //#define USE_WPS // Add support for WPS as initial wifi configuration tool (+33k code, 1k mem (5k mem with core v2.4.2+)) //#define USE_SMARTCONFIG // Add support for Wifi SmartConfig as initial wifi configuration tool (+23k code, +0.6k mem) @@ -247,6 +249,7 @@ // -- MQTT - TLS ---------------------------------- // !!! TLS uses a LOT OF MEMORY so be careful to enable other options at the same time !!! //#define USE_MQTT_TLS // Use TLS for MQTT connection (+53k code, +15k mem) +// #define USE_MQTT_TLS_CA_CERT // Use LetsEncrypt Certificate from sonoff_letsencrypt.h - Not supported with core 2.3.0 // -- KNX IP Protocol ----------------------------- //#define USE_KNX // Enable KNX IP Protocol Support (+9.4k code, +3k7 mem) @@ -366,6 +369,7 @@ #define TUYA_DIMMER_ID 0 // Default dimmer Id #define USE_ARMTRONIX_DIMMERS // Add support for Armtronix Dimmers (+1k4 code) #define USE_PS_16_DZ // Add support for PS-16-DZ Dimmer +//#define USE_AZ7798 // Add support for AZ-Instrument 7798 CO2 datalogger (+1k6 code) // Power monitoring sensors ----------------------- #define USE_PZEM004T // Add support for PZEM004T Energy monitor (+2k code) diff --git a/sonoff/settings.h b/sonoff/settings.h index d18ab6886..4de813d18 100644 --- a/sonoff/settings.h +++ b/sonoff/settings.h @@ -68,7 +68,7 @@ typedef union { // Restricted by MISRA-C Rule 18.4 bu uint32_t time_append_timezone : 1; // bit 2 (v6.2.1.2) uint32_t gui_hostname_ip : 1; // bit 3 (v6.2.1.20) uint32_t tuya_apply_o20 : 1; // bit 4 (v6.3.0.4) - uint32_t hass_short_discovery_msg : 1; // bit 5 (v6.3.0.7) + uint32_t spare5 : 1; uint32_t use_wifi_scan : 1; // bit 6 (v6.3.0.10) uint32_t use_wifi_rescan : 1; // bit 7 (v6.3.0.10) uint32_t receive_raw : 1; // bit 8 (v6.3.0.11) diff --git a/sonoff/sonoff.h b/sonoff/sonoff.h index 71c761b28..7c2de21e6 100644 --- a/sonoff/sonoff.h +++ b/sonoff/sonoff.h @@ -245,7 +245,7 @@ enum XsnsFunctions {FUNC_SETTINGS_OVERRIDE, FUNC_MODULE_INIT, FUNC_PRE_INIT, FUN FUNC_MQTT_SUBSCRIBE, FUNC_MQTT_INIT, FUNC_MQTT_DATA, FUNC_SET_POWER, FUNC_SET_DEVICE_POWER, FUNC_SHOW_SENSOR, FUNC_RULES_PROCESS, FUNC_SERIAL, FUNC_FREE_MEM, FUNC_BUTTON_PRESSED, - FUNC_WEB_ADD_BUTTON, FUNC_WEB_ADD_MAIN_BUTTON, FUNC_WEB_ADD_HANDLER}; + FUNC_WEB_ADD_BUTTON, FUNC_WEB_ADD_MAIN_BUTTON, FUNC_WEB_ADD_HANDLER, FUNC_SET_CHANNELS}; enum CommandSource { SRC_IGNORE, SRC_MQTT, SRC_RESTART, SRC_BUTTON, SRC_SWITCH, SRC_BACKLOG, SRC_SERIAL, SRC_WEBGUI, SRC_WEBCOMMAND, SRC_WEBCONSOLE, SRC_PULSETIMER, SRC_TIMER, SRC_RULE, SRC_MAXPOWER, SRC_MAXENERGY, SRC_LIGHT, SRC_KNX, SRC_DISPLAY, SRC_WEMO, SRC_HUE, SRC_RETRY, SRC_MAX }; diff --git a/sonoff/sonoff.ino b/sonoff/sonoff.ino index 362b48ced..b262c7d9b 100755 --- a/sonoff/sonoff.ino +++ b/sonoff/sonoff.ino @@ -467,7 +467,8 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) if (XdrvMqttData(topicBuf, sizeof(topicBuf), dataBuf, sizeof(dataBuf))) return; grpflg = (strstr(topicBuf, Settings.mqtt_grptopic) != NULL); - fallback_topic_flag = (strstr(topicBuf, mqtt_client) != NULL); + snprintf_P(stemp1, sizeof(stemp1), PSTR(D_CMND "/%s/"), mqtt_client); // Full Fallback topic = cmnd/DVES_xxxxxxxx + fallback_topic_flag = (!strncmp(topicBuf, stemp1, strlen(stemp1))); type = strrchr(topicBuf, '/'); // Last part of received topic is always the command (type) index = 1; diff --git a/sonoff/sonoff_letsencrypt.h b/sonoff/sonoff_letsencrypt.h new file mode 100644 index 000000000..fa00c9f29 --- /dev/null +++ b/sonoff/sonoff_letsencrypt.h @@ -0,0 +1,102 @@ +/* + sonoff_letsencrypt.h - TLS Lets Encrypt certificate for Sonoff-Tasmota + + Copyright (C) 2018 Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef USE_MQTT_TLS_CA_CERT +/*********************************************************************************************\ + * LetsEncrypt IdenTrust DST Root CA X3 certificate valid until 20210930 + * + * https://letsencrypt.org/certificates/ + * Downloaded from https://www.identrust.com/support/downloads +\*********************************************************************************************/ + +#define MQTT_TLS_CA_CERT_LENGTH 846 // Letsencrypt +#define MQTT_TLS_CA_CERT { \ + 0x30, 0x82, 0x03, 0x4a, 0x30, 0x82, 0x02, 0x32, 0xa0, 0x03, 0x02, 0x01, \ + 0x02, 0x02, 0x10, 0x44, 0xaf, 0xb0, 0x80, 0xd6, 0xa3, 0x27, 0xba, 0x89, \ + 0x30, 0x39, 0x86, 0x2e, 0xf8, 0x40, 0x6b, 0x30, 0x0d, 0x06, 0x09, 0x2a, \ + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x3f, \ + 0x31, 0x24, 0x30, 0x22, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x1b, 0x44, \ + 0x69, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x61, \ + 0x74, 0x75, 0x72, 0x65, 0x20, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x43, \ + 0x6f, 0x2e, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, \ + 0x0e, 0x44, 0x53, 0x54, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41, \ + 0x20, 0x58, 0x33, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x30, 0x30, 0x39, 0x33, \ + 0x30, 0x32, 0x31, 0x31, 0x32, 0x31, 0x39, 0x5a, 0x17, 0x0d, 0x32, 0x31, \ + 0x30, 0x39, 0x33, 0x30, 0x31, 0x34, 0x30, 0x31, 0x31, 0x35, 0x5a, 0x30, \ + 0x3f, 0x31, 0x24, 0x30, 0x22, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x1b, \ + 0x44, 0x69, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x20, 0x53, 0x69, 0x67, 0x6e, \ + 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, \ + 0x43, 0x6f, 0x2e, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x03, \ + 0x13, 0x0e, 0x44, 0x53, 0x54, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, \ + 0x41, 0x20, 0x58, 0x33, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, \ + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, \ + 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, \ + 0x00, 0xdf, 0xaf, 0xe9, 0x97, 0x50, 0x08, 0x83, 0x57, 0xb4, 0xcc, 0x62, \ + 0x65, 0xf6, 0x90, 0x82, 0xec, 0xc7, 0xd3, 0x2c, 0x6b, 0x30, 0xca, 0x5b, \ + 0xec, 0xd9, 0xc3, 0x7d, 0xc7, 0x40, 0xc1, 0x18, 0x14, 0x8b, 0xe0, 0xe8, \ + 0x33, 0x76, 0x49, 0x2a, 0xe3, 0x3f, 0x21, 0x49, 0x93, 0xac, 0x4e, 0x0e, \ + 0xaf, 0x3e, 0x48, 0xcb, 0x65, 0xee, 0xfc, 0xd3, 0x21, 0x0f, 0x65, 0xd2, \ + 0x2a, 0xd9, 0x32, 0x8f, 0x8c, 0xe5, 0xf7, 0x77, 0xb0, 0x12, 0x7b, 0xb5, \ + 0x95, 0xc0, 0x89, 0xa3, 0xa9, 0xba, 0xed, 0x73, 0x2e, 0x7a, 0x0c, 0x06, \ + 0x32, 0x83, 0xa2, 0x7e, 0x8a, 0x14, 0x30, 0xcd, 0x11, 0xa0, 0xe1, 0x2a, \ + 0x38, 0xb9, 0x79, 0x0a, 0x31, 0xfd, 0x50, 0xbd, 0x80, 0x65, 0xdf, 0xb7, \ + 0x51, 0x63, 0x83, 0xc8, 0xe2, 0x88, 0x61, 0xea, 0x4b, 0x61, 0x81, 0xec, \ + 0x52, 0x6b, 0xb9, 0xa2, 0xe2, 0x4b, 0x1a, 0x28, 0x9f, 0x48, 0xa3, 0x9e, \ + 0x0c, 0xda, 0x09, 0x8e, 0x3e, 0x17, 0x2e, 0x1e, 0xdd, 0x20, 0xdf, 0x5b, \ + 0xc6, 0x2a, 0x8a, 0xab, 0x2e, 0xbd, 0x70, 0xad, 0xc5, 0x0b, 0x1a, 0x25, \ + 0x90, 0x74, 0x72, 0xc5, 0x7b, 0x6a, 0xab, 0x34, 0xd6, 0x30, 0x89, 0xff, \ + 0xe5, 0x68, 0x13, 0x7b, 0x54, 0x0b, 0xc8, 0xd6, 0xae, 0xec, 0x5a, 0x9c, \ + 0x92, 0x1e, 0x3d, 0x64, 0xb3, 0x8c, 0xc6, 0xdf, 0xbf, 0xc9, 0x41, 0x70, \ + 0xec, 0x16, 0x72, 0xd5, 0x26, 0xec, 0x38, 0x55, 0x39, 0x43, 0xd0, 0xfc, \ + 0xfd, 0x18, 0x5c, 0x40, 0xf1, 0x97, 0xeb, 0xd5, 0x9a, 0x9b, 0x8d, 0x1d, \ + 0xba, 0xda, 0x25, 0xb9, 0xc6, 0xd8, 0xdf, 0xc1, 0x15, 0x02, 0x3a, 0xab, \ + 0xda, 0x6e, 0xf1, 0x3e, 0x2e, 0xf5, 0x5c, 0x08, 0x9c, 0x3c, 0xd6, 0x83, \ + 0x69, 0xe4, 0x10, 0x9b, 0x19, 0x2a, 0xb6, 0x29, 0x57, 0xe3, 0xe5, 0x3d, \ + 0x9b, 0x9f, 0xf0, 0x02, 0x5d, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x42, \ + 0x30, 0x40, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, \ + 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0e, 0x06, 0x03, 0x55, \ + 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, \ + 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xc4, 0xa7, \ + 0xb1, 0xa4, 0x7b, 0x2c, 0x71, 0xfa, 0xdb, 0xe1, 0x4b, 0x90, 0x75, 0xff, \ + 0xc4, 0x15, 0x60, 0x85, 0x89, 0x10, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, \ + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, \ + 0x01, 0x00, 0xa3, 0x1a, 0x2c, 0x9b, 0x17, 0x00, 0x5c, 0xa9, 0x1e, 0xee, \ + 0x28, 0x66, 0x37, 0x3a, 0xbf, 0x83, 0xc7, 0x3f, 0x4b, 0xc3, 0x09, 0xa0, \ + 0x95, 0x20, 0x5d, 0xe3, 0xd9, 0x59, 0x44, 0xd2, 0x3e, 0x0d, 0x3e, 0xbd, \ + 0x8a, 0x4b, 0xa0, 0x74, 0x1f, 0xce, 0x10, 0x82, 0x9c, 0x74, 0x1a, 0x1d, \ + 0x7e, 0x98, 0x1a, 0xdd, 0xcb, 0x13, 0x4b, 0xb3, 0x20, 0x44, 0xe4, 0x91, \ + 0xe9, 0xcc, 0xfc, 0x7d, 0xa5, 0xdb, 0x6a, 0xe5, 0xfe, 0xe6, 0xfd, 0xe0, \ + 0x4e, 0xdd, 0xb7, 0x00, 0x3a, 0xb5, 0x70, 0x49, 0xaf, 0xf2, 0xe5, 0xeb, \ + 0x02, 0xf1, 0xd1, 0x02, 0x8b, 0x19, 0xcb, 0x94, 0x3a, 0x5e, 0x48, 0xc4, \ + 0x18, 0x1e, 0x58, 0x19, 0x5f, 0x1e, 0x02, 0x5a, 0xf0, 0x0c, 0xf1, 0xb1, \ + 0xad, 0xa9, 0xdc, 0x59, 0x86, 0x8b, 0x6e, 0xe9, 0x91, 0xf5, 0x86, 0xca, \ + 0xfa, 0xb9, 0x66, 0x33, 0xaa, 0x59, 0x5b, 0xce, 0xe2, 0xa7, 0x16, 0x73, \ + 0x47, 0xcb, 0x2b, 0xcc, 0x99, 0xb0, 0x37, 0x48, 0xcf, 0xe3, 0x56, 0x4b, \ + 0xf5, 0xcf, 0x0f, 0x0c, 0x72, 0x32, 0x87, 0xc6, 0xf0, 0x44, 0xbb, 0x53, \ + 0x72, 0x6d, 0x43, 0xf5, 0x26, 0x48, 0x9a, 0x52, 0x67, 0xb7, 0x58, 0xab, \ + 0xfe, 0x67, 0x76, 0x71, 0x78, 0xdb, 0x0d, 0xa2, 0x56, 0x14, 0x13, 0x39, \ + 0x24, 0x31, 0x85, 0xa2, 0xa8, 0x02, 0x5a, 0x30, 0x47, 0xe1, 0xdd, 0x50, \ + 0x07, 0xbc, 0x02, 0x09, 0x90, 0x00, 0xeb, 0x64, 0x63, 0x60, 0x9b, 0x16, \ + 0xbc, 0x88, 0xc9, 0x12, 0xe6, 0xd2, 0x7d, 0x91, 0x8b, 0xf9, 0x3d, 0x32, \ + 0x8d, 0x65, 0xb4, 0xe9, 0x7c, 0xb1, 0x57, 0x76, 0xea, 0xc5, 0xb6, 0x28, \ + 0x39, 0xbf, 0x15, 0x65, 0x1c, 0xc8, 0xf6, 0x77, 0x96, 0x6a, 0x0a, 0x8d, \ + 0x77, 0x0b, 0xd8, 0x91, 0x0b, 0x04, 0x8e, 0x07, 0xdb, 0x29, 0xb6, 0x0a, \ + 0xee, 0x9d, 0x82, 0x35, 0x35, 0x10 } + +#endif // USE_MQTT_TLS_CA_CERT diff --git a/sonoff/sonoff_post.h b/sonoff/sonoff_post.h index f8798e454..12a68fe47 100755 --- a/sonoff/sonoff_post.h +++ b/sonoff/sonoff_post.h @@ -135,6 +135,7 @@ void KNX_CB_Action(message_t const &msg, void *arg); #define USE_RF_SENSOR // Add support for RF sensor receiver (434MHz or 868MHz) (+0k8 code) // #define USE_THEO_V2 // Add support for decoding Theo V2 sensors as documented on https://sidweb.nl using 434MHz RF sensor receiver (+1k4 code) #define USE_ALECTO_V2 // Add support for decoding Alecto V2 sensors like ACH2010, WS3000 and DKW2012 using 868MHz RF sensor receiver (+1k7 code) +//#define USE_AZ7798 // Add support for AZ-Instrument 7798 CO2 datalogger #endif // USE_SENSORS /*********************************************************************************************\ @@ -193,6 +194,7 @@ void KNX_CB_Action(message_t const &msg, void *arg); #undef USE_RF_SENSOR // Disable support for RF sensor receiver (434MHz or 868MHz) (+0k8 code) #undef DEBUG_THEO // Disable debug code #undef USE_DEBUG_DRIVER // Disable debug code +#undef USE_AZ7798 // Disable support for AZ-Instrument 7798 CO2 datalogger #endif // USE_CLASSIC /*********************************************************************************************\ @@ -313,6 +315,7 @@ void KNX_CB_Action(message_t const &msg, void *arg); #undef USE_RF_SENSOR // Disable support for RF sensor receiver (434MHz or 868MHz) (+0k8 code) #undef DEBUG_THEO // Disable debug code #undef USE_DEBUG_DRIVER // Disable debug code +#undef USE_AZ7798 // Disable support for AZ-Instrument 7798 CO2 datalogger #endif // USE_BASIC /*********************************************************************************************\ @@ -375,6 +378,7 @@ void KNX_CB_Action(message_t const &msg, void *arg); #undef USE_RF_SENSOR // Disable support for RF sensor receiver (434MHz or 868MHz) (+0k8 code) #undef DEBUG_THEO // Disable debug code #undef USE_DEBUG_DRIVER // Disable debug code +#undef USE_AZ7798 // Disable support for AZ-Instrument 7798 CO2 datalogger #endif // BE_MINIMAL /*********************************************************************************************\ @@ -422,4 +426,8 @@ void KNX_CB_Action(message_t const &msg, void *arg); #define ARDUINO_ESP8266_RELEASE "STAGE" #endif +#ifdef ARDUINO_ESP8266_RELEASE_2_3_0 // Disable not supported features in core 2.3.0 +#undef USE_MQTT_TLS_CA_CERT +#endif + #endif // _SONOFF_POST_H_ diff --git a/sonoff/sonoff_template.h b/sonoff/sonoff_template.h index 91eec21c7..48af9cf4b 100644 --- a/sonoff/sonoff_template.h +++ b/sonoff/sonoff_template.h @@ -141,6 +141,8 @@ enum UserSelectablePins { GPIO_SSPI_CS, // Software SPI Chip Select GPIO_SSPI_DC, // Software SPI Data or Command GPIO_RF_SENSOR, // Rf receiver with sensor decoding + GPIO_AZ_TXD, // AZ-Instrument 7798 Serial interface + GPIO_AZ_RXD, // AZ-Instrument 7798 Serial interface GPIO_SENSOR_END }; // Programmer selectable GPIO functionality offset by user selectable GPIOs @@ -201,7 +203,8 @@ const char kSensorNames[] PROGMEM = D_SENSOR_TUYA_TX "|" D_SENSOR_TUYA_RX "|" D_SENSOR_MGC3130_XFER "|" D_SENSOR_MGC3130_RESET "|" D_SENSOR_SSPI_MISO "|" D_SENSOR_SSPI_MOSI "|" D_SENSOR_SSPI_SCLK "|" D_SENSOR_SSPI_CS "|" D_SENSOR_SSPI_DC "|" - D_SENSOR_RF_SENSOR; + D_SENSOR_RF_SENSOR "|" + D_SENSOR_AZ_TX "|" D_SENSOR_AZ_RX; /********************************************************************************************/ @@ -454,6 +457,10 @@ const uint8_t kGpioNiceList[] PROGMEM = { GPIO_MGC3130_XFER, GPIO_MGC3130_RESET #endif +#ifdef USE_AZ7798 + GPIO_AZ_TXD, // AZ-Instrument 7798 CO2 datalogger Serial interface + GPIO_AZ_RXD // AZ-Instrument 7798 CO2 datalogger Serial interface +#endif }; const uint8_t kModuleNiceList[MAXMODULE] PROGMEM = { diff --git a/sonoff/sonoff_version.h b/sonoff/sonoff_version.h index ef0fdab18..6e635a341 100644 --- a/sonoff/sonoff_version.h +++ b/sonoff/sonoff_version.h @@ -20,7 +20,7 @@ #ifndef _SONOFF_VERSION_H_ #define _SONOFF_VERSION_H_ -#define VERSION 0x06040000 +#define VERSION 0x06040100 #define D_PROGRAMNAME "Sonoff-Tasmota" #define D_AUTHOR "Theo Arends" diff --git a/sonoff/support.ino b/sonoff/support.ino index 540191855..5c80651e9 100644 --- a/sonoff/support.ino +++ b/sonoff/support.ino @@ -1113,7 +1113,7 @@ void AddLog(byte loglevel) snprintf_P(mxtime, sizeof(mxtime), PSTR("%02d" D_HOUR_MINUTE_SEPARATOR "%02d" D_MINUTE_SECOND_SEPARATOR "%02d "), RtcTime.hour, RtcTime.minute, RtcTime.second); if (loglevel <= seriallog_level) { - Serial.printf("%s%s\n", mxtime, log_data); + Serial.printf("%s%s\r\n", mxtime, log_data); } #ifdef USE_WEBSERVER if (Settings.webserver && (loglevel <= Settings.weblog_level)) { diff --git a/sonoff/support_features.ino b/sonoff/support_features.ino index 652116b2f..75ddaf327 100644 --- a/sonoff/support_features.ino +++ b/sonoff/support_features.ino @@ -370,7 +370,9 @@ void GetFeatures(void) #ifdef USE_ALECTO_V2 feature_sns2 |= 0x00020000; #endif -// feature_sns2 |= 0x00040000; +#ifdef USE_AZ7798 + feature_sns2 |= 0x00040000; +#endif // feature_sns2 |= 0x00080000; // feature_sns2 |= 0x00100000; // feature_sns2 |= 0x00200000; diff --git a/sonoff/support_wifi.ino b/sonoff/support_wifi.ino index 7f2771dde..8e45f58ea 100644 --- a/sonoff/support_wifi.ino +++ b/sonoff/support_wifi.ino @@ -249,7 +249,7 @@ void WifiBeginAfterScan() uint8_t* bssid = WiFi.BSSID(); // Get current bssid memcpy((void*) &wifi_bssid, (void*) bssid, sizeof(wifi_bssid)); best_network_db = WiFi.RSSI(); // Get current rssi and add threshold - if (best_network_db < -WIFI_RSSI_THRESHOLD) { best_network_db +WIFI_RSSI_THRESHOLD; } + if (best_network_db < -WIFI_RSSI_THRESHOLD) { best_network_db += WIFI_RSSI_THRESHOLD; } wifi_scan_state = 3; } // Init scan diff --git a/sonoff/xdrv_01_webserver.ino b/sonoff/xdrv_01_webserver.ino index b5c73aea5..bd76b3c0e 100644 --- a/sonoff/xdrv_01_webserver.ino +++ b/sonoff/xdrv_01_webserver.ino @@ -27,6 +27,10 @@ #define XDRV_01 1 +#ifndef WIFI_SOFT_AP_CHANNEL +#define WIFI_SOFT_AP_CHANNEL 1 // Soft Access Point Channel number between 1 and 11 as used by SmartConfig web GUI +#endif + #define HTTP_REFRESH_TIME 2345 // milliseconds #define HTTP_RESTART_RECONNECT_TIME 9000 // milliseconds #define HTTP_OTA_RESTART_RECONNECT_TIME 20000 // milliseconds @@ -165,7 +169,7 @@ const char HTTP_SCRIPT_MODULE2[] PROGMEM = "x.send();" "}"; const char HTTP_SCRIPT_MODULE3[] PROGMEM = - "}1'%d'>%02d %s}2"; // "}1" and "}2" means do not use "}x" in Module name and Sensor name + "}1'%d'>%s (%02d)}2"; // "}1" and "}2" means do not use "}x" in Module name and Sensor name const char HTTP_SCRIPT_INFO_BEGIN[] PROGMEM = "function i(){" @@ -441,7 +445,11 @@ void WifiManagerBegin(void) StopWebserver(); DnsServer = new DNSServer(); - WiFi.softAP(my_hostname); + + int channel = WIFI_SOFT_AP_CHANNEL; + if ((channel < 1) || (channel > 13)) { channel = 1; } + WiFi.softAP(my_hostname, NULL, channel); + delay(500); // Without delay I've seen the IP address blank /* Setup the DNS server redirecting all the domains to the apIP */ DnsServer->setErrorReplyCode(DNSReplyCode::NoError); @@ -798,14 +806,14 @@ void HandleModuleConfiguration(void) for (byte i = 0; i < MAXMODULE; i++) { midx = pgm_read_byte(kModuleNiceList + i); snprintf_P(stemp, sizeof(stemp), kModules[midx].name); - snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SCRIPT_MODULE3, midx, midx +1, stemp); + snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SCRIPT_MODULE3, midx, stemp, midx +1); page += mqtt_data; } page += "}3"; // String separator means do not use "}3" in Module name and Sensor name for (byte j = 0; j < sizeof(kGpioNiceList); j++) { midx = pgm_read_byte(kGpioNiceList + j); if (!GetUsedInModule(midx, cmodule.gp.io)) { - snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SCRIPT_MODULE3, midx, midx, GetTextIndexed(stemp, sizeof(stemp), midx, kSensorNames)); + snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SCRIPT_MODULE3, midx, GetTextIndexed(stemp, sizeof(stemp), midx, kSensorNames), midx); page += mqtt_data; } } diff --git a/sonoff/xdrv_02_mqtt.ino b/sonoff/xdrv_02_mqtt.ino index bc969d3f2..a19f80f82 100644 --- a/sonoff/xdrv_02_mqtt.ino +++ b/sonoff/xdrv_02_mqtt.ino @@ -51,11 +51,14 @@ #define MQTT_LIBRARY_TYPE MQTT_PUBSUBCLIENT // Use PubSubClient library as it only supports TLS #endif -#endif +#endif // USE_MQTT_TLS /*********************************************************************************************/ #ifdef USE_MQTT_TLS +#ifdef USE_MQTT_TLS_CA_CERT + #include "sonoff_letsencrypt.h" // LetsEncrypt certificate +#endif WiFiClientSecure EspClient; // Wifi Secure Client #else WiFiClient EspClient; // Wifi Client @@ -467,6 +470,15 @@ boolean MqttCheckTls(void) Settings.mqtt_host, Settings.mqtt_port, mqtt_retry_counter); AddLog(LOG_LEVEL_DEBUG); } else { +#ifdef USE_MQTT_TLS_CA_CERT + unsigned char tls_ca_cert[] = MQTT_TLS_CA_CERT; + if (EspClient.setCACert(tls_ca_cert, MQTT_TLS_CA_CERT_LENGTH)) { + if (EspClient.verifyCertChain(Settings.mqtt_host)) { + AddLog_P(LOG_LEVEL_INFO, S_LOG_MQTT, PSTR(D_VERIFIED "CA")); + result = true; + } + } +#else if (EspClient.verify(fingerprint1, Settings.mqtt_host)) { AddLog_P(LOG_LEVEL_INFO, S_LOG_MQTT, PSTR(D_VERIFIED "1")); result = true; @@ -475,6 +487,7 @@ boolean MqttCheckTls(void) AddLog_P(LOG_LEVEL_INFO, S_LOG_MQTT, PSTR(D_VERIFIED "2")); result = true; } +#endif } if (!result) AddLog_P(LOG_LEVEL_INFO, S_LOG_MQTT, PSTR(D_FAILED)); EspClient.stop(); diff --git a/sonoff/xdrv_03_energy.ino b/sonoff/xdrv_03_energy.ino index 1c54c12e5..b97c53fc2 100644 --- a/sonoff/xdrv_03_energy.ino +++ b/sonoff/xdrv_03_energy.ino @@ -402,12 +402,11 @@ boolean EnergyCommand(void) break; } } - char energy_yesterday_chr[10]; - char energy_daily_chr[10]; - char energy_total_chr[10]; - + char energy_total_chr[33]; dtostrfd(energy_total, Settings.flag2.energy_resolution, energy_total_chr); + char energy_daily_chr[33]; dtostrfd(energy_daily, Settings.flag2.energy_resolution, energy_daily_chr); + char energy_yesterday_chr[33]; dtostrfd((float)Settings.energy_kWhyesterday / 100000, Settings.flag2.energy_resolution, energy_yesterday_chr); snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"%s\":{\"" D_JSON_TOTAL "\":%s,\"" D_JSON_YESTERDAY "\":%s,\"" D_JSON_TODAY "\":%s}}"), @@ -549,18 +548,6 @@ const char HTTP_ENERGY_SNS4[] PROGMEM = "%s" void EnergyShow(boolean json) { - char voltage_chr[10]; - char current_chr[10]; - char active_power_chr[10]; - char apparent_power_chr[10]; - char reactive_power_chr[10]; - char power_factor_chr[10]; - char frequency_chr[10]; - char energy_daily_chr[10]; - char energy_period_chr[10]; - char energy_yesterday_chr[10]; - char energy_total_chr[10]; - char speriod[20]; char sfrequency[20]; @@ -568,6 +555,10 @@ void EnergyShow(boolean json) float power_factor = energy_power_factor; + char apparent_power_chr[33]; + char reactive_power_chr[33]; + char power_factor_chr[33]; + char frequency_chr[33]; if (!energy_type_dc) { float apparent_power = energy_apparent_power; if (isnan(apparent_power)) { @@ -602,14 +593,21 @@ void EnergyShow(boolean json) } } + char voltage_chr[33]; dtostrfd(energy_voltage, Settings.flag2.voltage_resolution, voltage_chr); + char current_chr[33]; dtostrfd(energy_current, Settings.flag2.current_resolution, current_chr); + char active_power_chr[33]; dtostrfd(energy_active_power, Settings.flag2.wattage_resolution, active_power_chr); + char energy_daily_chr[33]; dtostrfd(energy_daily, Settings.flag2.energy_resolution, energy_daily_chr); + char energy_yesterday_chr[33]; dtostrfd((float)Settings.energy_kWhyesterday / 100000, Settings.flag2.energy_resolution, energy_yesterday_chr); + char energy_total_chr[33]; dtostrfd(energy_total, Settings.flag2.energy_resolution, energy_total_chr); float energy = 0; + char energy_period_chr[33]; if (show_energy_period) { if (energy_period) energy = (float)(energy_kWhtoday - energy_period) / 100; energy_period = energy_kWhtoday; diff --git a/sonoff/xdrv_04_light.ino b/sonoff/xdrv_04_light.ino index 52eba7d2d..ae6644dda 100644 --- a/sonoff/xdrv_04_light.ino +++ b/sonoff/xdrv_04_light.ino @@ -828,30 +828,20 @@ void LightAnimate(void) } } } + XdrvMailbox.index = light_device; + XdrvMailbox.data = (char*)cur_col; + XdrvMailbox.data_len = sizeof(cur_col); + if (XdrvCall(FUNC_SET_CHANNELS)) { + // Serviced + } #ifdef USE_WS2812 // ************************************************************************ - if (LT_WS2812 == light_type) { + else if (LT_WS2812 == light_type) { Ws2812SetColor(0, cur_col[0], cur_col[1], cur_col[2], cur_col[3]); } #endif // USE_ES2812 ************************************************************************ - if (light_type > LT_WS2812) { + else if (light_type > LT_WS2812) { LightMy92x1Duty(cur_col[0], cur_col[1], cur_col[2], cur_col[3], cur_col[4]); } -#ifdef USE_TUYA_DIMMER - if (light_type == LT_SERIAL1 && Settings.module == TUYA_DIMMER ) { - LightSerialDuty(cur_col[0]); - } -#endif // USE_TUYA_DIMMER -#ifdef USE_ARMTRONIX_DIMMERS - if (light_type == LT_SERIAL2) { - LightSerial2Duty(cur_col[0],cur_col[1]); - } -#endif // USE_ARMTRONIX_DIMMERS -#ifdef USE_PS_16_DZ - if (light_type == LT_SERIAL1 && Settings.module == PS_16_DZ) { - PS16DZSerialDuty(cur_col[0]); - } -#endif // USE_PS_16_DZ - } } } diff --git a/sonoff/xdrv_09_timers.ino b/sonoff/xdrv_09_timers.ino index fb7c2cef5..8273201d5 100644 --- a/sonoff/xdrv_09_timers.ino +++ b/sonoff/xdrv_09_timers.ino @@ -488,7 +488,7 @@ boolean TimerCommand(void) if (XdrvMailbox.data_len) { Settings.longitude = (int)(CharToDouble(XdrvMailbox.data) *1000000); } - char lbuff[32]; + char lbuff[33]; dtostrfd(((double)Settings.longitude) /1000000, 6, lbuff); snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, lbuff); } @@ -496,7 +496,7 @@ boolean TimerCommand(void) if (XdrvMailbox.data_len) { Settings.latitude = (int)(CharToDouble(XdrvMailbox.data) *1000000); } - char lbuff[32]; + char lbuff[33]; dtostrfd(((double)Settings.latitude) /1000000, 6, lbuff); snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, lbuff); } diff --git a/sonoff/xdrv_10_rules.ino b/sonoff/xdrv_10_rules.ino index 0a06fe149..c9d880128 100644 --- a/sonoff/xdrv_10_rules.ino +++ b/sonoff/xdrv_10_rules.ino @@ -94,7 +94,7 @@ uint8_t rules_trigger_count[MAX_RULE_SETS] = { 0 }; uint8_t rules_teleperiod = 0; char event_data[100]; -char vars[MAX_RULE_VARS][10] = { 0 }; +char vars[MAX_RULE_VARS][33] = { 0 }; /*******************************************************************************************/ diff --git a/sonoff/xdrv_11_knx.ino b/sonoff/xdrv_11_knx.ino index 665583767..9095b4efc 100644 --- a/sonoff/xdrv_11_knx.ino +++ b/sonoff/xdrv_11_knx.ino @@ -551,7 +551,7 @@ void KNX_CB_Action(message_t const &msg, void *arg) device_parameters_t *chan = (device_parameters_t *)arg; if (!(Settings.flag.knx_enabled)) { return; } - char tempchar[25]; + char tempchar[33]; if (msg.data_len == 1) { // COMMAND diff --git a/sonoff/xdrv_12_home_assistant.ino b/sonoff/xdrv_12_home_assistant.ino index ad9e71a4c..350fe1ca6 100644 --- a/sonoff/xdrv_12_home_assistant.ino +++ b/sonoff/xdrv_12_home_assistant.ino @@ -22,77 +22,6 @@ #define XDRV_12 12 const char HASS_DISCOVER_RELAY[] PROGMEM = - "{\"name\":\"%s\"," // dualr2 1 - "\"command_topic\":\"%s\"," // cmnd/dualr2/POWER2 - "\"state_topic\":\"%s\"," // stat/dualr2/RESULT (implies "\"optimistic\":\"false\",") - "\"value_template\":\"{{value_json.%s}}\"," // POWER2 - "\"payload_off\":\"%s\"," // OFF - "\"payload_on\":\"%s\"," // ON -// "\"optimistic\":\"false\"," // false is Hass default when state_topic is set - "\"availability_topic\":\"%s\"," // tele/dualr2/LWT - "\"payload_available\":\"" D_ONLINE "\"," // Online - "\"payload_not_available\":\"" D_OFFLINE "\""; // Offline - -const char HASS_DISCOVER_BUTTON_SWITCH[] PROGMEM = - "{\"name\":\"%s\"," // dualr2 1 BTN - "\"state_topic\":\"%s\"," // cmnd/dualr2/POWER (implies "\"optimistic\":\"false\",") -// "\"value_template\":\"{{value_json.%s}}\"," // POWER2 - "\"payload_on\":\"%s\"," // TOGGLE / ON -// "\"optimistic\":\"false\"," // false is Hass default when state_topic is set - "\"availability_topic\":\"%s\"," // tele/dualr2/LWT - "\"payload_available\":\"" D_ONLINE "\"," // Online - "\"payload_not_available\":\"" D_OFFLINE "\""; // Offline - -const char HASS_DISCOVER_BUTTON_SWITCH_TOGGLE[] PROGMEM = - "%s,\"off_delay\":1"; // Hass has no support for TOGGLE, fake it by resetting to OFF after 1s - -const char HASS_DISCOVER_BUTTON_SWITCH_ONOFF[] PROGMEM = - "%s,\"force_update\":true," // In ON/OFF case, enable force_update to make automations work - "\"payload_off\":\"%s\""; // OFF - -const char HASS_DISCOVER_LIGHT_DIMMER[] PROGMEM = - "%s,\"brightness_command_topic\":\"%s\"," // cmnd/led2/Dimmer - "\"brightness_state_topic\":\"%s\"," // stat/led2/RESULT - "\"brightness_scale\":100," // 100% - "\"on_command_type\":\"brightness\"," // power on (first), power on (last), no power on (brightness) - "\"brightness_value_template\":\"{{value_json." D_CMND_DIMMER "}}\""; - -const char HASS_DISCOVER_LIGHT_COLOR[] PROGMEM = - "%s,\"rgb_command_topic\":\"%s2\"," // cmnd/led2/Color2 - "\"rgb_state_topic\":\"%s\"," // stat/led2/RESULT - "\"rgb_value_template\":\"{{value_json." D_CMND_COLOR ".split(',')[0:3]|join(',')}}\""; - -const char HASS_DISCOVER_LIGHT_WHITE[] PROGMEM = - "%s,\"white_value_command_topic\":\"%s\"," // cmnd/led2/White - "\"white_value_state_topic\":\"%s\"," // stat/led2/RESULT - "\"white_value_scale\":100," - "\"white_value_template\":\"{{ value_json.Channel[3] }}\""; - -const char HASS_DISCOVER_LIGHT_CT[] PROGMEM = - "%s,\"color_temp_command_topic\":\"%s\"," // cmnd/led2/CT - "\"color_temp_state_topic\":\"%s\"," // stat/led2/RESULT - "\"color_temp_value_template\":\"{{value_json." D_CMND_COLORTEMPERATURE "}}\""; - -const char HASS_DISCOVER_SENSOR[] PROGMEM = - "{\"name\":\"%s\"," // dualr2 1 BTN - "\"state_topic\":\"%s\"," // cmnd/dualr2/POWER (implies "\"optimistic\":\"false\",") - "\"availability_topic\":\"%s\"," // tele/dualr2/LWT - "\"payload_available\":\"" D_ONLINE "\"," // Online - "\"payload_not_available\":\"" D_OFFLINE "\""; // Offline - -const char HASS_DISCOVER_SENSOR_TEMP[] PROGMEM = - "%s,\"unit_of_measurement\":\"°%c\"," // °C / °F - "\"value_template\":\"{{value_json['%s'].Temperature}}\""; // "SI7021-14":{"Temperature":null,"Humidity":null} -> {{ value_json['SI7021-14'].Temperature }} - -const char HASS_DISCOVER_SENSOR_HUM[] PROGMEM = - "%s,\"unit_of_measurement\":\"%%\"," // % - "\"value_template\":\"{{value_json['%s'].Humidity}}\"," // "SI7021-14":{"Temperature":null,"Humidity":null} -> {{ value_json['SI7021-14'].Humidity }} - "\"device_class\":\"humidity\""; // temperature / humidity - -const char HASS_DISCOVER_SENSOR_ANY[] PROGMEM = - "%s,\"value_template\":\"{{value_json['%s'].%s}}\""; // "COUNTER":{"C1":0} -> {{ value_json['COUNTER'].C1 }} - -const char HASS_DISCOVER_RELAY_SHORT[] PROGMEM = "{\"name\":\"%s\"," // dualr2 1 "\"cmd_t\":\"%s\"," // cmnd/dualr2/POWER2 "\"stat_t\":\"%s\"," // stat/dualr2/RESULT (implies "\"optimistic\":\"false\",") @@ -104,7 +33,7 @@ const char HASS_DISCOVER_RELAY_SHORT[] PROGMEM = "\"pl_avail\":\"" D_ONLINE "\"," // Online "\"pl_not_avail\":\"" D_OFFLINE "\""; // Offline -const char HASS_DISCOVER_BUTTON_SWITCH_SHORT[] PROGMEM = +const char HASS_DISCOVER_BUTTON_SWITCH[] PROGMEM = "{\"name\":\"%s\"," // dualr2 1 BTN "\"stat_t\":\"%s\"," // cmnd/dualr2/POWER (implies "\"optimistic\":\"false\",") // "\"value_template\":\"{{value_json.%s}}\"," // POWER2 @@ -114,63 +43,86 @@ const char HASS_DISCOVER_BUTTON_SWITCH_SHORT[] PROGMEM = "\"pl_avail\":\"" D_ONLINE "\"," // Online "\"pl_not_avail\":\"" D_OFFLINE "\""; // Offline -const char HASS_DISCOVER_BUTTON_SWITCH_TOGGLE_SHORT[] PROGMEM = +const char HASS_DISCOVER_BUTTON_SWITCH_TOGGLE[] PROGMEM = "%s,\"off_delay\":1"; // Hass has no support for TOGGLE, fake it by resetting to OFF after 1s -const char HASS_DISCOVER_BUTTON_SWITCH_ONOFF_SHORT[] PROGMEM = +const char HASS_DISCOVER_BUTTON_SWITCH_ONOFF[] PROGMEM = "%s,\"frc_upd\":true," // In ON/OFF case, enable force_update to make automations work "\"pl_off\":\"%s\""; // OFF -const char HASS_DISCOVER_LIGHT_DIMMER_SHORT[] PROGMEM = +const char HASS_DISCOVER_LIGHT_DIMMER[] PROGMEM = "%s,\"bri_cmd_t\":\"%s\"," // cmnd/led2/Dimmer "\"bri_stat_t\":\"%s\"," // stat/led2/RESULT "\"bri_scl\":100," // 100% "\"on_cmd_type\":\"brightness\"," // power on (first), power on (last), no power on (brightness) "\"bri_val_tpl\":\"{{value_json." D_CMND_DIMMER "}}\""; -const char HASS_DISCOVER_LIGHT_COLOR_SHORT[] PROGMEM = +const char HASS_DISCOVER_LIGHT_COLOR[] PROGMEM = "%s,\"rgb_cmd_t\":\"%s2\"," // cmnd/led2/Color2 "\"rgb_stat_t\":\"%s\"," // stat/led2/RESULT "\"rgb_val_tpl\":\"{{value_json." D_CMND_COLOR ".split(',')[0:3]|join(',')}}\""; -const char HASS_DISCOVER_LIGHT_WHITE_SHORT[] PROGMEM = +const char HASS_DISCOVER_LIGHT_WHITE[] PROGMEM = "%s,\"whit_val_cmd_t\":\"%s\"," // cmnd/led2/White "\"whit_val_stat_t\":\"%s\"," // stat/led2/RESULT "\"white_value_scale\":100," // (No abbreviation defined) "\"whit_val_tpl\":\"{{ value_json.Channel[3] }}\""; -const char HASS_DISCOVER_LIGHT_CT_SHORT[] PROGMEM = +const char HASS_DISCOVER_LIGHT_CT[] PROGMEM = "%s,\"clr_temp_cmd_t\":\"%s\"," // cmnd/led2/CT "\"clr_temp_stat_t\":\"%s\"," // stat/led2/RESULT "\"clr_temp_val_tpl\":\"{{value_json." D_CMND_COLORTEMPERATURE "}}\""; -const char HASS_DISCOVER_LIGHT_SCHEME_SHORT[] PROGMEM = +const char HASS_DISCOVER_LIGHT_SCHEME[] PROGMEM = "%s,\"fx_cmd_t\":\"%s\"," // cmnd/led2/Scheme "\"fx_stat_t\":\"%s\"," // stat/led2/RESULT "\"fx_val_tpl\":\"{{value_json." D_CMND_SCHEME "}}\"," "\"fx_list\":[\"0\",\"1\",\"2\",\"3\",\"4\"]"; // string list with reference to scheme parameter. -const char HASS_DISCOVER_SENSOR_SHORT[] PROGMEM = +const char HASS_DISCOVER_SENSOR[] PROGMEM = "{\"name\":\"%s\"," // dualr2 1 BTN "\"stat_t\":\"%s\"," // cmnd/dualr2/POWER (implies "\"optimistic\":\"false\",") "\"avty_t\":\"%s\"," // tele/dualr2/LWT "\"pl_avail\":\"" D_ONLINE "\"," // Online "\"pl_not_avail\":\"" D_OFFLINE "\""; // Offline -const char HASS_DISCOVER_SENSOR_TEMP_SHORT[] PROGMEM = +const char HASS_DISCOVER_SENSOR_TEMP[] PROGMEM = "%s,\"unit_of_meas\":\"°%c\"," // °C / °F "\"val_tpl\":\"{{value_json['%s'].Temperature}}\""; // "SI7021-14":{"Temperature":null,"Humidity":null} -> {{ value_json['SI7021-14'].Temperature }} -const char HASS_DISCOVER_SENSOR_HUM_SHORT[] PROGMEM = +const char HASS_DISCOVER_SENSOR_HUM[] PROGMEM = "%s,\"unit_of_meas\":\"%%\"," // % "\"val_tpl\":\"{{value_json['%s'].Humidity}}\"," // "SI7021-14":{"Temperature":null,"Humidity":null} -> {{ value_json['SI7021-14'].Humidity }} "\"dev_cla\":\"humidity\""; // humidity -const char HASS_DISCOVER_SENSOR_ANY_SHORT[] PROGMEM = - "%s,\"val_tpl\":\"{{value_json['%s'].%s}}\""; // "COUNTER":{"C1":0} -> {{ value_json['COUNTER'].C1 }} +const char HASS_DISCOVER_SENSOR_PRESS[] PROGMEM = + "%s,\"unit_of_meas\":\"%s\"," // PressureUnit() setting + "\"val_tpl\":\"{{value_json['%s'].Pressure}}\"," // "BME280":{"Temperature":19.7,"Humidity":27.8,"Pressure":990.1} -> {{ value_json['BME280'].Pressure }} + "\"dev_cla\":\"pressure\""; // pressure -const char HASS_DISCOVER_DEVICE_INFO_SHORT[] PROGMEM = +//ENERGY +const char HASS_DISCOVER_SENSOR_KWH[] PROGMEM = + "%s,\"unit_of_meas\":\"kWh\"," // kWh + "\"val_tpl\":\"{{value_json['%s'].%s}}\""; // "ENERGY":{"TotalStartTime":null,"Total":null,"Yesterday":null,"Today":null,"Power":null,"ApparentPower":null,"ReactivePower":null,"Factor":null,"Voltage":null,"Current":null} -> {{ value_json['ENERGY'].Total/Yesterday/Today }} + +const char HASS_DISCOVER_SENSOR_WATT[] PROGMEM = + "%s,\"unit_of_meas\":\"W\"," // W + "\"val_tpl\":\"{{value_json['%s'].%s}}\""; // "ENERGY":{"TotalStartTime":null,"Total":null,"Yesterday":null,"Today":null,"Power":null,"ApparentPower":null,"ReactivePower":null,"Factor":null,"Voltage":null,"Current":null} -> {{ value_json['ENERGY'].POWER }} + +const char HASS_DISCOVER_SENSOR_VOLTAGE[] PROGMEM = + "%s,\"unit_of_meas\":\"V\"," // V + "\"val_tpl\":\"{{value_json['%s'].%s}}\""; // "ENERGY":{"TotalStartTime":null,"Total":null,"Yesterday":null,"Today":null,"Power":null,"ApparentPower":null,"ReactivePower":null,"Factor":null,"Voltage":null,"Current":null} -> {{ value_json['ENERGY'].Voltage }} + +const char HASS_DISCOVER_SENSOR_AMPERE[] PROGMEM = + "%s,\"unit_of_meas\":\"A\"," // A + "\"val_tpl\":\"{{value_json['%s'].%s}}\""; // "ENERGY":{"TotalStartTime":null,"Total":null,"Yesterday":null,"Today":null,"Power":null,"ApparentPower":null,"ReactivePower":null,"Factor":null,"Voltage":null,"Current":null} -> {{ value_json['ENERGY'].Current }} + +const char HASS_DISCOVER_SENSOR_ANY[] PROGMEM = + "%s,\"unit_of_meas\":\" \"," // " " As unit of measurement to get a value graph in Hass + "\"val_tpl\":\"{{value_json['%s'].%s}}\""; // "COUNTER":{"C1":0} -> {{ value_json['COUNTER'].C1 }} + +const char HASS_DISCOVER_DEVICE_INFO[] PROGMEM = "%s,\"uniq_id\":\"%s\"," "\"device\":{\"identifiers\":[\"%06X\"]," "\"name\":\"%s\"," @@ -244,66 +196,52 @@ void HAssAnnounceRelayLight(void) GetTopic_P(state_topic, TELE, mqtt_topic, D_RSLT_STATE); GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT); FindPrefix(command_topic, state_topic, prefix); - if (Settings.flag3.hass_short_discovery_msg) { - Shorten(&command_topic, prefix); - Shorten(&state_topic, prefix); - Shorten(&availability_topic, prefix); - } - snprintf_P(mqtt_data, sizeof(mqtt_data), Settings.flag3.hass_short_discovery_msg?HASS_DISCOVER_RELAY_SHORT:HASS_DISCOVER_RELAY, + Shorten(&command_topic, prefix); + Shorten(&state_topic, prefix); + Shorten(&availability_topic, prefix); + snprintf_P(mqtt_data, sizeof(mqtt_data), HASS_DISCOVER_RELAY, name, command_topic, state_topic, value_template, Settings.state_text[0], Settings.state_text[1], availability_topic); if (is_light) { char *brightness_command_topic = stemp1; GetTopic_P(brightness_command_topic, CMND, mqtt_topic, D_CMND_DIMMER); - if (Settings.flag3.hass_short_discovery_msg) - Shorten(&brightness_command_topic, prefix); - snprintf_P(mqtt_data, sizeof(mqtt_data), Settings.flag3.hass_short_discovery_msg?HASS_DISCOVER_LIGHT_DIMMER_SHORT:HASS_DISCOVER_LIGHT_DIMMER, - mqtt_data, brightness_command_topic, state_topic); + Shorten(&brightness_command_topic, prefix); + snprintf_P(mqtt_data, sizeof(mqtt_data), HASS_DISCOVER_LIGHT_DIMMER, mqtt_data, brightness_command_topic, state_topic); if (light_subtype >= LST_RGB) { char *rgb_command_topic = stemp1; GetTopic_P(rgb_command_topic, CMND, mqtt_topic, D_CMND_COLOR); - if (Settings.flag3.hass_short_discovery_msg) - Shorten(&rgb_command_topic, prefix); - snprintf_P(mqtt_data, sizeof(mqtt_data), Settings.flag3.hass_short_discovery_msg?HASS_DISCOVER_LIGHT_COLOR_SHORT:HASS_DISCOVER_LIGHT_COLOR, - mqtt_data, rgb_command_topic, state_topic); + Shorten(&rgb_command_topic, prefix); + snprintf_P(mqtt_data, sizeof(mqtt_data), HASS_DISCOVER_LIGHT_COLOR, mqtt_data, rgb_command_topic, state_topic); char *effect_command_topic = stemp1; GetTopic_P(effect_command_topic, CMND, mqtt_topic, D_CMND_SCHEME); - if (Settings.flag3.hass_short_discovery_msg) { - Shorten(&effect_command_topic, prefix); - snprintf_P(mqtt_data, sizeof(mqtt_data), HASS_DISCOVER_LIGHT_SCHEME_SHORT, mqtt_data, effect_command_topic, state_topic); - } + Shorten(&effect_command_topic, prefix); + snprintf_P(mqtt_data, sizeof(mqtt_data), HASS_DISCOVER_LIGHT_SCHEME, mqtt_data, effect_command_topic, state_topic); } if (LST_RGBW == light_subtype) { char *white_temp_command_topic = stemp1; GetTopic_P(white_temp_command_topic, CMND, mqtt_topic, D_CMND_WHITE); - if (Settings.flag3.hass_short_discovery_msg) - Shorten(&white_temp_command_topic, prefix); - snprintf_P(mqtt_data, sizeof(mqtt_data), Settings.flag3.hass_short_discovery_msg?HASS_DISCOVER_LIGHT_WHITE_SHORT:HASS_DISCOVER_LIGHT_WHITE, - mqtt_data, white_temp_command_topic, state_topic); + Shorten(&white_temp_command_topic, prefix); + snprintf_P(mqtt_data, sizeof(mqtt_data), HASS_DISCOVER_LIGHT_WHITE, mqtt_data, white_temp_command_topic, state_topic); } if ((LST_COLDWARM == light_subtype) || (LST_RGBWC == light_subtype)) { char *color_temp_command_topic = stemp1; GetTopic_P(color_temp_command_topic, CMND, mqtt_topic, D_CMND_COLORTEMPERATURE); - if (Settings.flag3.hass_short_discovery_msg) - Shorten(&color_temp_command_topic, prefix); - snprintf_P(mqtt_data, sizeof(mqtt_data), Settings.flag3.hass_short_discovery_msg?HASS_DISCOVER_LIGHT_CT_SHORT:HASS_DISCOVER_LIGHT_CT, - mqtt_data, color_temp_command_topic, state_topic); + Shorten(&color_temp_command_topic, prefix); + snprintf_P(mqtt_data, sizeof(mqtt_data), HASS_DISCOVER_LIGHT_CT, mqtt_data, color_temp_command_topic, state_topic); } } - if (Settings.flag3.hass_short_discovery_msg) { - snprintf_P(stemp1, sizeof(stemp1), kModules[Settings.module].name); - snprintf_P(mqtt_data, sizeof(mqtt_data), HASS_DISCOVER_DEVICE_INFO_SHORT, mqtt_data, - unique_id, ESP.getChipId(), - Settings.friendlyname[0], stemp1, my_version, my_image, "Tasmota"); - snprintf_P(mqtt_data, sizeof(mqtt_data), HASS_DISCOVER_TOPIC_PREFIX, mqtt_data, prefix); - } + snprintf_P(stemp1, sizeof(stemp1), kModules[Settings.module].name); + snprintf_P(mqtt_data, sizeof(mqtt_data), HASS_DISCOVER_DEVICE_INFO, mqtt_data, + unique_id, ESP.getChipId(), + Settings.friendlyname[0], stemp1, my_version, my_image, "Tasmota"); + snprintf_P(mqtt_data, sizeof(mqtt_data), HASS_DISCOVER_TOPIC_PREFIX, mqtt_data, prefix); snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s}"), mqtt_data); } MqttPublish(stopic, true); @@ -342,26 +280,18 @@ void HAssAnnounceButtonSwitch(byte device, char* topic, byte present, byte key, GetTopic_P(state_topic, CMND, topic, value_template); // State of button is sent as CMND TOGGLE, state of switch is sent as ON/OFF GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT); FindPrefix(state_topic, availability_topic, prefix); - if (Settings.flag3.hass_short_discovery_msg) { - Shorten(&state_topic, prefix); - Shorten(&availability_topic, prefix); - } - snprintf_P(mqtt_data, sizeof(mqtt_data), Settings.flag3.hass_short_discovery_msg?HASS_DISCOVER_BUTTON_SWITCH_SHORT:HASS_DISCOVER_BUTTON_SWITCH, + Shorten(&state_topic, prefix); + Shorten(&availability_topic, prefix); + snprintf_P(mqtt_data, sizeof(mqtt_data), HASS_DISCOVER_BUTTON_SWITCH, name, state_topic, Settings.state_text[toggle?2:1], availability_topic); - if (toggle) snprintf_P(mqtt_data, sizeof(mqtt_data), - Settings.flag3.hass_short_discovery_msg?HASS_DISCOVER_BUTTON_SWITCH_TOGGLE_SHORT:HASS_DISCOVER_BUTTON_SWITCH_TOGGLE, - mqtt_data); - if (!toggle) snprintf_P(mqtt_data, sizeof(mqtt_data), - Settings.flag3.hass_short_discovery_msg?HASS_DISCOVER_BUTTON_SWITCH_ONOFF_SHORT:HASS_DISCOVER_BUTTON_SWITCH_ONOFF, - mqtt_data, Settings.state_text[0]); + if (toggle) snprintf_P(mqtt_data, sizeof(mqtt_data), HASS_DISCOVER_BUTTON_SWITCH_TOGGLE, mqtt_data); + else snprintf_P(mqtt_data, sizeof(mqtt_data), HASS_DISCOVER_BUTTON_SWITCH_ONOFF, mqtt_data, Settings.state_text[0]); - if (Settings.flag3.hass_short_discovery_msg) { - snprintf_P(stemp1, sizeof(stemp1), kModules[Settings.module].name); - snprintf_P(mqtt_data, sizeof(mqtt_data), HASS_DISCOVER_DEVICE_INFO_SHORT, mqtt_data, - unique_id, ESP.getChipId(), - Settings.friendlyname[0], stemp1, my_version, my_image, "Tasmota"); - snprintf_P(mqtt_data, sizeof(mqtt_data), HASS_DISCOVER_TOPIC_PREFIX, mqtt_data, prefix); - } + snprintf_P(stemp1, sizeof(stemp1), kModules[Settings.module].name); + snprintf_P(mqtt_data, sizeof(mqtt_data), HASS_DISCOVER_DEVICE_INFO, mqtt_data, + unique_id, ESP.getChipId(), + Settings.friendlyname[0], stemp1, my_version, my_image, "Tasmota"); + snprintf_P(mqtt_data, sizeof(mqtt_data), HASS_DISCOVER_TOPIC_PREFIX, mqtt_data, prefix); snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s}"), mqtt_data); } MqttPublish(stopic, true); @@ -452,29 +382,42 @@ void HAssAnnounceSensor(const char* sensorname, const char* subsensortype) GetTopic_P(state_topic, TELE, mqtt_topic, PSTR(D_RSLT_SENSOR)); GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT); FindPrefix(state_topic, availability_topic, prefix); - if (Settings.flag3.hass_short_discovery_msg) { - Shorten(&state_topic, prefix); - Shorten(&availability_topic, prefix); - } - snprintf_P(mqtt_data, sizeof(mqtt_data), Settings.flag3.hass_short_discovery_msg?HASS_DISCOVER_SENSOR_SHORT:HASS_DISCOVER_SENSOR, + Shorten(&state_topic, prefix); + Shorten(&availability_topic, prefix); + + snprintf_P(mqtt_data, sizeof(mqtt_data), HASS_DISCOVER_SENSOR, name, state_topic, availability_topic); if (!strcmp_P(subsensortype, PSTR(D_JSON_TEMPERATURE))) { - snprintf_P(mqtt_data, sizeof(mqtt_data), Settings.flag3.hass_short_discovery_msg?HASS_DISCOVER_SENSOR_TEMP_SHORT:HASS_DISCOVER_SENSOR_TEMP, + snprintf_P(mqtt_data, sizeof(mqtt_data), HASS_DISCOVER_SENSOR_TEMP, mqtt_data, TempUnit(), sensorname); } else if (!strcmp_P(subsensortype, PSTR(D_JSON_HUMIDITY))) { - snprintf_P(mqtt_data, sizeof(mqtt_data), Settings.flag3.hass_short_discovery_msg?HASS_DISCOVER_SENSOR_HUM_SHORT:HASS_DISCOVER_SENSOR_HUM, + snprintf_P(mqtt_data, sizeof(mqtt_data), HASS_DISCOVER_SENSOR_HUM, mqtt_data, sensorname); - } else { - snprintf_P(mqtt_data, sizeof(mqtt_data), Settings.flag3.hass_short_discovery_msg?HASS_DISCOVER_SENSOR_ANY_SHORT:HASS_DISCOVER_SENSOR_ANY, + } else if (!strcmp_P(subsensortype, PSTR(D_JSON_PRESSURE))) { + snprintf_P(mqtt_data, sizeof(mqtt_data), HASS_DISCOVER_SENSOR_PRESS, + mqtt_data, PressureUnit().c_str(), sensorname); + } else if (!strcmp_P(subsensortype, PSTR(D_JSON_TOTAL)) || !strcmp_P(subsensortype, PSTR(D_JSON_TODAY)) || !strcmp_P(subsensortype, PSTR(D_JSON_YESTERDAY))){ + snprintf_P(mqtt_data, sizeof(mqtt_data), HASS_DISCOVER_SENSOR_KWH, + mqtt_data, sensorname, subsensortype); + } else if (!strcmp_P(subsensortype, PSTR(D_JSON_POWERUSAGE))){ + snprintf_P(mqtt_data, sizeof(mqtt_data), HASS_DISCOVER_SENSOR_WATT, + mqtt_data, sensorname, subsensortype); + } else if (!strcmp_P(subsensortype, PSTR(D_JSON_VOLTAGE))){ + snprintf_P(mqtt_data, sizeof(mqtt_data), HASS_DISCOVER_SENSOR_VOLTAGE, + mqtt_data, sensorname, subsensortype); + } else if (!strcmp_P(subsensortype, PSTR(D_JSON_CURRENT))){ + snprintf_P(mqtt_data, sizeof(mqtt_data), HASS_DISCOVER_SENSOR_AMPERE, mqtt_data, sensorname, subsensortype); } - if (Settings.flag3.hass_short_discovery_msg) { - snprintf_P(stemp1, sizeof(stemp1), kModules[Settings.module].name); - snprintf_P(mqtt_data, sizeof(mqtt_data), HASS_DISCOVER_DEVICE_INFO_SHORT, mqtt_data, - unique_id, ESP.getChipId(), - Settings.friendlyname[0], stemp1, my_version, my_image, "Tasmota"); - snprintf_P(mqtt_data, sizeof(mqtt_data), HASS_DISCOVER_TOPIC_PREFIX, mqtt_data, prefix); + else { + snprintf_P(mqtt_data, sizeof(mqtt_data), HASS_DISCOVER_SENSOR_ANY, + mqtt_data, sensorname, subsensortype); } + snprintf_P(stemp1, sizeof(stemp1), kModules[Settings.module].name); + snprintf_P(mqtt_data, sizeof(mqtt_data), HASS_DISCOVER_DEVICE_INFO, mqtt_data, + unique_id, ESP.getChipId(), + Settings.friendlyname[0], stemp1, my_version, my_image, "Tasmota"); + snprintf_P(mqtt_data, sizeof(mqtt_data), HASS_DISCOVER_TOPIC_PREFIX, mqtt_data, prefix); snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s}"), mqtt_data); } MqttPublish(stopic, true); diff --git a/sonoff/xdrv_16_tuyadimmer.ino b/sonoff/xdrv_16_tuyadimmer.ino index 1312a7199..eb564a8e8 100644 --- a/sonoff/xdrv_16_tuyadimmer.ino +++ b/sonoff/xdrv_16_tuyadimmer.ino @@ -133,6 +133,12 @@ boolean TuyaSetPower(void) return status; } +boolean TuyaSetChannels(void) +{ + LightSerialDuty(((uint8_t*)XdrvMailbox.data)[0]); + return true; +} + void LightSerialDuty(uint8_t duty) { if (duty > 0 && !tuya_ignore_dim && TuyaSerial) { @@ -416,6 +422,9 @@ boolean Xdrv16(byte function) case FUNC_EVERY_SECOND: if(TuyaSerial && tuya_wifi_state!=WifiState()) { TuyaSetWifiLed(); } break; + case FUNC_SET_CHANNELS: + result = TuyaSetChannels(); + break; } } return result; diff --git a/sonoff/xdrv_18_armtronix_dimmers.ino b/sonoff/xdrv_18_armtronix_dimmers.ino index 0cab44e1d..795149bff 100644 --- a/sonoff/xdrv_18_armtronix_dimmers.ino +++ b/sonoff/xdrv_18_armtronix_dimmers.ino @@ -40,6 +40,12 @@ int8_t armtronix_knobState[2]; // Dimmer state values. * Internal Functions \*********************************************************************************************/ +boolean ArmtronixSetChannels(void) +{ + LightSerial2Duty(((uint8_t*)XdrvMailbox.data)[0], ((uint8_t*)XdrvMailbox.data)[1]); + return true; +} + void LightSerial2Duty(uint8_t duty1, uint8_t duty2) { if (ArmtronixSerial && !armtronix_ignore_dim) { @@ -186,6 +192,9 @@ boolean Xdrv18(byte function) } } break; + case FUNC_SET_CHANNELS: + result = ArmtronixSetChannels(); + break; } } return result; diff --git a/sonoff/xdrv_19_ps16dz_dimmer.ino b/sonoff/xdrv_19_ps16dz_dimmer.ino index d8c8138e1..3c895cf98 100644 --- a/sonoff/xdrv_19_ps16dz_dimmer.ino +++ b/sonoff/xdrv_19_ps16dz_dimmer.ino @@ -91,6 +91,12 @@ boolean PS16DZSetPower(void) return status; } +boolean PS16DZSetChannels(void) +{ + PS16DZSerialDuty(((uint8_t*)XdrvMailbox.data)[0]); + return true; +} + void PS16DZSerialDuty(uint8_t duty) { if (duty > 0 && !ps16dz_ignore_dim && PS16DZSerial) { @@ -236,6 +242,9 @@ boolean Xdrv19(byte function) case FUNC_SET_DEVICE_POWER: result = PS16DZSetPower(); break; + case FUNC_SET_CHANNELS: + result = PS16DZSetChannels(); + break; } } return result; diff --git a/sonoff/xplg_wemohue.ino b/sonoff/xplg_wemohue.ino index 64ac969ae..143d0e6ba 100644 --- a/sonoff/xplg_wemohue.ino +++ b/sonoff/xplg_wemohue.ino @@ -568,7 +568,7 @@ void HueLightStatus1(byte device, String *response) { float hue = 0; float sat = 0; - float bri = 0; + float bri = 254; uint16_t ct = 500; if (light_type) { diff --git a/sonoff/xsns_01_counter.ino b/sonoff/xsns_01_counter.ino index 4f3b7d0f4..6b501d49f 100644 --- a/sonoff/xsns_01_counter.ino +++ b/sonoff/xsns_01_counter.ino @@ -93,12 +93,12 @@ const char HTTP_SNS_COUNTER[] PROGMEM = void CounterShow(boolean json) { char stemp[10]; - char counter[16]; byte dsxflg = 0; byte header = 0; for (byte i = 0; i < MAX_COUNTERS; i++) { if (pin[GPIO_CNTR1 +i] < 99) { + char counter[33]; if (bitRead(Settings.pulse_counter_type, i)) { dtostrfd((double)RtcSettings.pulse_counter[i] / 1000000, 6, counter); } else { diff --git a/sonoff/xsns_04_snfsc.ino b/sonoff/xsns_04_snfsc.ino index d31f6b5bb..d3329e3e5 100644 --- a/sonoff/xsns_04_snfsc.ino +++ b/sonoff/xsns_04_snfsc.ino @@ -113,12 +113,12 @@ const char HTTP_SNS_SCPLUS[] PROGMEM = void SonoffScShow(boolean json) { if (sc_value[0] > 0) { - char temperature[10]; - char humidity[10]; - float t = ConvertTemp(sc_value[1]); float h = sc_value[0]; + + char temperature[33]; dtostrfd(t, Settings.flag2.temperature_resolution, temperature); + char humidity[33]; dtostrfd(h, Settings.flag2.humidity_resolution, humidity); if (json) { diff --git a/sonoff/xsns_05_ds18b20.ino b/sonoff/xsns_05_ds18b20.ino index 864e03f51..dfd4394dd 100644 --- a/sonoff/xsns_05_ds18b20.ino +++ b/sonoff/xsns_05_ds18b20.ino @@ -190,8 +190,7 @@ void Ds18b20EverySecond(void) void Ds18b20Show(boolean json) { if (ds18b20_valid) { // Check for valid temperature - char temperature[10]; - + char temperature[33]; dtostrfd(ds18b20_temperature, Settings.flag2.temperature_resolution, temperature); if(json) { snprintf_P(mqtt_data, sizeof(mqtt_data), JSON_SNS_TEMP, mqtt_data, ds18b20_types, temperature); diff --git a/sonoff/xsns_05_ds18x20.ino b/sonoff/xsns_05_ds18x20.ino index 09f656730..1061df86d 100644 --- a/sonoff/xsns_05_ds18x20.ino +++ b/sonoff/xsns_05_ds18x20.ino @@ -424,12 +424,11 @@ void Ds18x20EverySecond(void) void Ds18x20Show(boolean json) { - char temperature[10]; - for (uint8_t i = 0; i < ds18x20_sensors; i++) { uint8_t index = ds18x20_sensor[i].index; if (ds18x20_sensor[index].valid) { // Check for valid temperature + char temperature[33]; dtostrfd(ds18x20_sensor[index].temperature, Settings.flag2.temperature_resolution, temperature); Ds18x20Name(i); diff --git a/sonoff/xsns_05_ds18x20_legacy.ino b/sonoff/xsns_05_ds18x20_legacy.ino index dda92f831..5a89a0abe 100644 --- a/sonoff/xsns_05_ds18x20_legacy.ino +++ b/sonoff/xsns_05_ds18x20_legacy.ino @@ -170,7 +170,6 @@ void Ds18x20Type(uint8_t sensor) void Ds18x20Show(boolean json) { - char temperature[10]; char stemp[10]; float t; @@ -178,6 +177,7 @@ void Ds18x20Show(boolean json) for (byte i = 0; i < Ds18x20Sensors(); i++) { if (Ds18x20Read(i, t)) { // Check if read failed Ds18x20Type(i); + char temperature[33]; dtostrfd(t, Settings.flag2.temperature_resolution, temperature); if (json) { diff --git a/sonoff/xsns_06_dht.ino b/sonoff/xsns_06_dht.ino index fa91e1f9b..72f94edd7 100644 --- a/sonoff/xsns_06_dht.ino +++ b/sonoff/xsns_06_dht.ino @@ -207,11 +207,10 @@ void DhtEverySecond(void) void DhtShow(boolean json) { - char temperature[10]; - char humidity[10]; - for (byte i = 0; i < dht_sensors; i++) { + char temperature[33]; dtostrfd(Dht[i].t, Settings.flag2.temperature_resolution, temperature); + char humidity[33]; dtostrfd(Dht[i].h, Settings.flag2.humidity_resolution, humidity); if (json) { diff --git a/sonoff/xsns_07_sht1x.ino b/sonoff/xsns_07_sht1x.ino index fdb06328a..0bf2d774c 100644 --- a/sonoff/xsns_07_sht1x.ino +++ b/sonoff/xsns_07_sht1x.ino @@ -188,10 +188,9 @@ void ShtEverySecond(void) void ShtShow(boolean json) { if (sht_valid) { - char temperature[10]; - char humidity[10]; - + char temperature[33]; dtostrfd(sht_temperature, Settings.flag2.temperature_resolution, temperature); + char humidity[33]; dtostrfd(sht_humidity, Settings.flag2.humidity_resolution, humidity); if (json) { diff --git a/sonoff/xsns_08_htu21.ino b/sonoff/xsns_08_htu21.ino index 87332287a..3e22c17f3 100644 --- a/sonoff/xsns_08_htu21.ino +++ b/sonoff/xsns_08_htu21.ino @@ -249,10 +249,9 @@ void HtuEverySecond(void) void HtuShow(boolean json) { if (htu_valid) { - char temperature[10]; - char humidity[10]; - + char temperature[33]; dtostrfd(htu_temperature, Settings.flag2.temperature_resolution, temperature); + char humidity[33]; dtostrfd(htu_humidity, Settings.flag2.humidity_resolution, humidity); if (json) { diff --git a/sonoff/xsns_09_bmp.ino b/sonoff/xsns_09_bmp.ino index 68eef5c14..6023ba5cc 100755 --- a/sonoff/xsns_09_bmp.ino +++ b/sonoff/xsns_09_bmp.ino @@ -42,23 +42,26 @@ #define BMP_MAX_SENSORS 2 const char kBmpTypes[] PROGMEM = "BMP180|BMP280|BME280|BME680"; + +typedef struct { + uint8_t bmp_address; // I2C bus address + char bmp_name[7]; // Sensor name - "BMPXXX" + uint8_t bmp_type; + uint8_t bmp_model; +#ifdef USE_BME680 + uint8_t bme680_state; + float bmp_gas_resistance; +#endif // USE_BME680 + float bmp_temperature; + float bmp_pressure; + float bmp_humidity; +} bmp_sensors_t; + uint8_t bmp_addresses[] = { BMP_ADDR1, BMP_ADDR2 }; uint8_t bmp_count = 0; uint8_t bmp_once = 1; -struct BMPSTRUCT { - uint8_t bmp_address; // I2C bus address - char bmp_name[7]; // Sensor name - "BMPXXX" - uint8_t bmp_type = 0; - uint8_t bmp_model = 0; -#ifdef USE_BME680 - uint8_t bme680_state = 0; - float bmp_gas_resistance = 0.0; -#endif // USE_BME680 - float bmp_temperature = 0.0; - float bmp_pressure = 0.0; - float bmp_humidity = 0.0; -} bmp_sensors[BMP_MAX_SENSORS]; +bmp_sensors_t *bmp_sensors = NULL; /*********************************************************************************************\ * BMP085 and BME180 @@ -83,7 +86,7 @@ struct BMPSTRUCT { #define BMP180_OSS 3 -struct BMP180CALIBDATA { +typedef struct { int16_t cal_ac1; int16_t cal_ac2; int16_t cal_ac3; @@ -94,10 +97,17 @@ struct BMP180CALIBDATA { uint16_t cal_ac4; uint16_t cal_ac5; uint16_t cal_ac6; -} bmp180_cal_data[BMP_MAX_SENSORS]; +} bmp180_cal_data_t; + +bmp180_cal_data_t *bmp180_cal_data = NULL; boolean Bmp180Calibration(uint8_t bmp_idx) { + if (!bmp180_cal_data) { + bmp180_cal_data = (bmp180_cal_data_t*)malloc(BMP_MAX_SENSORS * sizeof(bmp180_cal_data_t)); + } + if (!bmp180_cal_data) { return false; } + bmp180_cal_data[bmp_idx].cal_ac1 = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_AC1); bmp180_cal_data[bmp_idx].cal_ac2 = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_AC2); bmp180_cal_data[bmp_idx].cal_ac3 = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_AC3); @@ -140,6 +150,8 @@ boolean Bmp180Calibration(uint8_t bmp_idx) void Bmp180Read(uint8_t bmp_idx) { + if (!bmp180_cal_data) { return; } + I2cWrite8(bmp_sensors[bmp_idx].bmp_address, BMP180_REG_CONTROL, BMP180_TEMPERATURE); delay(5); // 5ms conversion time int ut = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_REG_RESULT); @@ -211,8 +223,7 @@ void Bmp180Read(uint8_t bmp_idx) #define BME280_REGISTER_DIG_H5 0xE5 #define BME280_REGISTER_DIG_H6 0xE7 -struct BME280CALIBDATA -{ +typedef struct { uint16_t dig_T1; int16_t dig_T2; int16_t dig_T3; @@ -231,12 +242,19 @@ struct BME280CALIBDATA uint8_t dig_H1; uint8_t dig_H3; int8_t dig_H6; -} Bme280CalibrationData[BMP_MAX_SENSORS]; +} Bme280CalibrationData_t; + +Bme280CalibrationData_t *Bme280CalibrationData = NULL; boolean Bmx280Calibrate(uint8_t bmp_idx) { // if (I2cRead8(bmp_address, BMP_REGISTER_CHIPID) != BME280_CHIPID) return false; + if (!Bme280CalibrationData) { + Bme280CalibrationData = (Bme280CalibrationData_t*)malloc(BMP_MAX_SENSORS * sizeof(Bme280CalibrationData_t)); + } + if (!Bme280CalibrationData) { return false; } + Bme280CalibrationData[bmp_idx].dig_T1 = I2cRead16LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_T1); Bme280CalibrationData[bmp_idx].dig_T2 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_T2); Bme280CalibrationData[bmp_idx].dig_T3 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_T3); @@ -270,6 +288,8 @@ boolean Bmx280Calibrate(uint8_t bmp_idx) void Bme280Read(uint8_t bmp_idx) { + if (!Bme280CalibrationData) { return; } + int32_t adc_T = I2cRead24(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_TEMPDATA); adc_T >>= 4; @@ -324,7 +344,7 @@ void Bme280Read(uint8_t bmp_idx) #include -struct bme680_dev gas_sensor[BMP_MAX_SENSORS]; +struct bme680_dev *gas_sensor = NULL; static void BmeDelayMs(uint32_t ms) { @@ -333,6 +353,11 @@ static void BmeDelayMs(uint32_t ms) boolean Bme680Init(uint8_t bmp_idx) { + if (!gas_sensor) { + gas_sensor = (bme680_dev*)malloc(BMP_MAX_SENSORS * sizeof(bme680_dev)); + } + if (!gas_sensor) { return false; } + gas_sensor[bmp_idx].dev_id = bmp_sensors[bmp_idx].bmp_address; gas_sensor[bmp_idx].intf = BME680_I2C_INTF; gas_sensor[bmp_idx].read = &I2cReadBuffer; @@ -377,6 +402,8 @@ boolean Bme680Init(uint8_t bmp_idx) void Bme680Read(uint8_t bmp_idx) { + if (!gas_sensor) { return; } + int8_t rslt = BME680_OK; if (BME680_CHIPID == bmp_sensors[bmp_idx].bmp_type) { @@ -421,6 +448,13 @@ void BmpDetect(void) { if (bmp_count) return; + int bmp_sensor_size = BMP_MAX_SENSORS * sizeof(bmp_sensors_t); + if (!bmp_sensors) { + bmp_sensors = (bmp_sensors_t*)malloc(bmp_sensor_size); + } + if (!bmp_sensors) { return; } + memset(bmp_sensors, 0, bmp_sensor_size); // Init defaults to 0 + for (byte i = 0; i < BMP_MAX_SENSORS; i++) { uint8_t bmp_type = I2cRead8(bmp_addresses[i], BMP_REGISTER_CHIPID); if (bmp_type) { @@ -458,6 +492,8 @@ void BmpDetect(void) void BmpRead(void) { + if (!bmp_sensors) { return; } + for (byte bmp_idx = 0; bmp_idx < bmp_count; bmp_idx++) { switch (bmp_sensors[bmp_idx].bmp_type) { case BMP180_CHIPID: @@ -491,15 +527,11 @@ void BmpEverySecond(void) void BmpShow(boolean json) { + if (!bmp_sensors) { return; } + for (byte bmp_idx = 0; bmp_idx < bmp_count; bmp_idx++) { if (bmp_sensors[bmp_idx].bmp_type) { float bmp_sealevel = 0.0; - char temperature[10]; - char pressure[10]; - char sea_pressure[10]; - char humidity[10]; - char name[10]; - if (bmp_sensors[bmp_idx].bmp_pressure != 0.0) { bmp_sealevel = (bmp_sensors[bmp_idx].bmp_pressure / FastPrecisePow(1.0 - ((float)Settings.altitude / 44330.0), 5.255)) - 21.6; bmp_sealevel = ConvertPressure(bmp_sealevel); @@ -507,17 +539,22 @@ void BmpShow(boolean json) float bmp_temperature = ConvertTemp(bmp_sensors[bmp_idx].bmp_temperature); float bmp_pressure = ConvertPressure(bmp_sensors[bmp_idx].bmp_pressure); + char name[10]; snprintf(name, sizeof(name), bmp_sensors[bmp_idx].bmp_name); if (bmp_count > 1) { snprintf_P(name, sizeof(name), PSTR("%s-%02X"), name, bmp_sensors[bmp_idx].bmp_address); // BMXXXX-XX } + char temperature[33]; dtostrfd(bmp_temperature, Settings.flag2.temperature_resolution, temperature); + char pressure[33]; dtostrfd(bmp_pressure, Settings.flag2.pressure_resolution, pressure); + char sea_pressure[33]; dtostrfd(bmp_sealevel, Settings.flag2.pressure_resolution, sea_pressure); + char humidity[33]; dtostrfd(bmp_sensors[bmp_idx].bmp_humidity, Settings.flag2.humidity_resolution, humidity); #ifdef USE_BME680 - char gas_resistance[10]; + char gas_resistance[33]; dtostrfd(bmp_sensors[bmp_idx].bmp_gas_resistance, 2, gas_resistance); #endif // USE_BME680 @@ -530,17 +567,14 @@ void BmpShow(boolean json) char json_gas[40]; snprintf_P(json_gas, sizeof(json_gas), PSTR(",\"" D_JSON_GAS "\":%s"), gas_resistance); - snprintf_P(mqtt_data, - sizeof(mqtt_data), - PSTR("%s,\"%s\":{\"" D_JSON_TEMPERATURE "\":%s%s,\"" D_JSON_PRESSURE "\":%s%s%s}"), + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"%s\":{\"" D_JSON_TEMPERATURE "\":%s%s,\"" D_JSON_PRESSURE "\":%s%s%s}"), mqtt_data, name, temperature, (bmp_sensors[bmp_idx].bmp_model >= 2) ? json_humidity : "", pressure, (Settings.altitude != 0) ? json_sealevel : "", - (bmp_sensors[bmp_idx].bmp_model >= 3) ? json_gas : "" - ); + (bmp_sensors[bmp_idx].bmp_model >= 3) ? json_gas : ""); #else snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"%s\":{\"" D_JSON_TEMPERATURE "\":%s%s,\"" D_JSON_PRESSURE "\":%s%s}"), mqtt_data, name, temperature, (bmp_sensors[bmp_idx].bmp_model >= 2) ? json_humidity : "", pressure, (Settings.altitude != 0) ? json_sealevel : ""); diff --git a/sonoff/xsns_11_veml6070.ino b/sonoff/xsns_11_veml6070.ino index 590f61300..0177fcc63 100644 --- a/sonoff/xsns_11_veml6070.ino +++ b/sonoff/xsns_11_veml6070.ino @@ -273,12 +273,12 @@ double Veml6070UvPower(double uvrisk) void Veml6070Show(boolean json) { if (veml6070_type) { - char str_uvlevel[6]; // e.g. 99999 inc = UVLevel - char str_uvrisk[6]; // e.g. 25.99 text = UvIndex - char str_uvpower[6]; // e.g. 0.399 W/m² = UvPower // convert double values to string + char str_uvlevel[33]; // e.g. 99999 inc = UVLevel dtostrfd((double)uvlevel, 0, str_uvlevel); + char str_uvrisk[33]; // e.g. 25.99 text = UvIndex dtostrfd(uvrisk, 2, str_uvrisk); + char str_uvpower[33]; // e.g. 0.399 W/m² = UvPower dtostrfd(uvpower, 3, str_uvpower); if (json) { #ifdef USE_VEML6070_SHOW_RAW diff --git a/sonoff/xsns_13_ina219.ino b/sonoff/xsns_13_ina219.ino index 2ca2f0288..ae5999a7e 100644 --- a/sonoff/xsns_13_ina219.ino +++ b/sonoff/xsns_13_ina219.ino @@ -232,13 +232,12 @@ const char HTTP_SNS_INA219_DATA[] PROGMEM = "%s" void Ina219Show(boolean json) { if (ina219_valid) { - char voltage[10]; - char current[10]; - char power[10]; - float fpower = ina219_voltage * ina219_current; + char voltage[33]; dtostrfd(ina219_voltage, Settings.flag2.voltage_resolution, voltage); + char power[33]; dtostrfd(fpower, Settings.flag2.wattage_resolution, power); + char current[33]; dtostrfd(ina219_current, Settings.flag2.current_resolution, current); if (json) { diff --git a/sonoff/xsns_14_sht3x.ino b/sonoff/xsns_14_sht3x.ino index 84bc86be8..6738b866a 100755 --- a/sonoff/xsns_14_sht3x.ino +++ b/sonoff/xsns_14_sht3x.ino @@ -98,15 +98,15 @@ void Sht3xShow(boolean json) if (sht3x_count) { float t; float h; - char temperature[10]; - char humidity[10]; char types[11]; for (byte i = 0; i < sht3x_count; i++) { if (Sht3xRead(t, h, sht3x_sensors[i].address)) { if (0 == i) { SetGlobalValues(t, h); } + char temperature[33]; dtostrfd(t, Settings.flag2.temperature_resolution, temperature); + char humidity[33]; dtostrfd(h, Settings.flag2.humidity_resolution, humidity); snprintf_P(types, sizeof(types), PSTR("%s-0x%02X"), sht3x_sensors[i].types, sht3x_sensors[i].address); // "SHT3X-0xXX" diff --git a/sonoff/xsns_15_mhz19.ino b/sonoff/xsns_15_mhz19.ino index 523dd3a66..968c0d021 100644 --- a/sonoff/xsns_15_mhz19.ino +++ b/sonoff/xsns_15_mhz19.ino @@ -322,7 +322,7 @@ void MhzInit(void) void MhzShow(boolean json) { - char temperature[10]; + char temperature[33]; dtostrfd(mhz_temperature, Settings.flag2.temperature_resolution, temperature); GetTextIndexed(mhz_types, sizeof(mhz_types), mhz_type -1, kMhzTypes); diff --git a/sonoff/xsns_17_senseair.ino b/sonoff/xsns_17_senseair.ino index 04360457f..967563972 100644 --- a/sonoff/xsns_17_senseair.ino +++ b/sonoff/xsns_17_senseair.ino @@ -147,9 +147,9 @@ void SenseairInit(void) void SenseairShow(boolean json) { - char temperature[10]; - char humidity[10]; + char temperature[33]; dtostrfd(senseair_temperature, Settings.flag2.temperature_resolution, temperature); + char humidity[33]; dtostrfd(senseair_humidity, Settings.flag2.temperature_resolution, humidity); GetTextIndexed(senseair_types, sizeof(senseair_types), senseair_type -1, kSenseairTypes); diff --git a/sonoff/xsns_19_mgs.ino b/sonoff/xsns_19_mgs.ino index a9fdf7d9e..36ce969f7 100644 --- a/sonoff/xsns_19_mgs.ino +++ b/sonoff/xsns_19_mgs.ino @@ -63,7 +63,7 @@ const char HTTP_MGS_GAS[] PROGMEM = "%s{s}MGS %s{m}%s " D_UNIT_PARTS_PER_MILLION void MGSShow(boolean json) { - char buffer[25]; + char buffer[33]; if (json) { snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"MGS\":{\"NH3\":%s"), mqtt_data, measure_gas(NH3, buffer)); snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"CO\":%s"), mqtt_data, measure_gas(CO, buffer)); diff --git a/sonoff/xsns_20_novasds.ino b/sonoff/xsns_20_novasds.ino index 909711472..dfb08ac5a 100644 --- a/sonoff/xsns_20_novasds.ino +++ b/sonoff/xsns_20_novasds.ino @@ -189,11 +189,11 @@ const char HTTP_SDS0X1_SNS[] PROGMEM = "%s" void NovaSdsShow(boolean json) { if (novasds_valid) { - char pm10[10]; - char pm2_5[10]; float pm10f = (float)(novasds_data.pm100) / 10.0f; float pm2_5f = (float)(novasds_data.pm25) / 10.0f; + char pm10[33]; dtostrfd(pm10f, 1, pm10); + char pm2_5[33]; dtostrfd(pm2_5f, 1, pm2_5); if (json) { snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"SDS0X1\":{\"PM2.5\":%s,\"PM10\":%s}"), mqtt_data, pm2_5, pm10); diff --git a/sonoff/xsns_22_sr04.ino b/sonoff/xsns_22_sr04.ino index 77ea5e829..0278011e1 100644 --- a/sonoff/xsns_22_sr04.ino +++ b/sonoff/xsns_22_sr04.ino @@ -51,8 +51,7 @@ void Sr04Show(boolean json) distance = (real64_t)(sonar->ping_median(5))/ US_ROUNDTRIP_CM; if (distance != 0) { // Check if read failed - char distance_chr[10]; - + char distance_chr[33]; dtostrfd(distance, 3, distance_chr); if(json) { diff --git a/sonoff/xsns_23_sdm120.ino b/sonoff/xsns_23_sdm120.ino index 7ec23d55d..9f27927e8 100644 --- a/sonoff/xsns_23_sdm120.ino +++ b/sonoff/xsns_23_sdm120.ino @@ -203,28 +203,28 @@ void SDM120250ms(void) // Every 250 mSec #ifdef USE_SDM220 case 8: sdm120_phase_angle = value; - break; - + break; + case 9: sdm120_import_active = value; - break; + break; case 10: sdm120_export_active = value; - break; + break; case 11: sdm120_import_reactive = value; - break; + break; case 12: sdm120_export_reactive = value; - break; + break; case 13: sdm120_total_reactive = value; - break; -#endif // USE_SDM220 + break; +#endif // USE_SDM220 } // end switch sdm120_read_state++; @@ -283,35 +283,34 @@ const char HTTP_SNS_SDM120_DATA[] PROGMEM = "%s" void SDM120Show(boolean json) { - char voltage[10]; - char current[10]; - char active_power[10]; - char apparent_power[10]; - char reactive_power[10]; - char power_factor[10]; - char frequency[10]; - char energy_total[10]; - char phase_angle[10]; - char import_active[10]; - char export_active[10]; - char import_reactive[10]; - char export_reactive[10]; - char total_reactive[10]; - + char voltage[33]; dtostrfd(sdm120_voltage, Settings.flag2.voltage_resolution, voltage); + char current[33]; dtostrfd(sdm120_current, Settings.flag2.current_resolution, current); + char active_power[33]; dtostrfd(sdm120_active_power, Settings.flag2.wattage_resolution, active_power); + char apparent_power[33]; dtostrfd(sdm120_apparent_power, Settings.flag2.wattage_resolution, apparent_power); + char reactive_power[33]; dtostrfd(sdm120_reactive_power, Settings.flag2.wattage_resolution, reactive_power); + char power_factor[33]; dtostrfd(sdm120_power_factor, 2, power_factor); + char frequency[33]; dtostrfd(sdm120_frequency, Settings.flag2.frequency_resolution, frequency); + char energy_total[33]; dtostrfd(sdm120_energy_total, Settings.flag2.energy_resolution, energy_total); #ifdef USE_SDM220 + char phase_angle[33]; dtostrfd(sdm120_phase_angle, 2, phase_angle); + char import_active[33]; dtostrfd(sdm120_import_active, Settings.flag2.wattage_resolution, import_active); + char export_active[33]; dtostrfd(sdm120_export_active, Settings.flag2.wattage_resolution, export_active); + char import_reactive[33]; dtostrfd(sdm120_import_reactive,Settings.flag2.wattage_resolution, import_reactive); + char export_reactive[33]; dtostrfd(sdm120_export_reactive,Settings.flag2.wattage_resolution, export_reactive); + char total_reactive[33]; dtostrfd(sdm120_total_reactive, Settings.flag2.wattage_resolution, total_reactive); #endif // USE_SDM220 if (json) { diff --git a/sonoff/xsns_25_sdm630.ino b/sonoff/xsns_25_sdm630.ino index 39614efc5..9060444b3 100644 --- a/sonoff/xsns_25_sdm630.ino +++ b/sonoff/xsns_25_sdm630.ino @@ -269,38 +269,37 @@ const char HTTP_SNS_SDM630_DATA[] PROGMEM = "%s" void SDM630Show(boolean json) { - char voltage_l1[10]; - char voltage_l2[10]; - char voltage_l3[10]; - char current_l1[10]; - char current_l2[10]; - char current_l3[10]; - char active_power_l1[10]; - char active_power_l2[10]; - char active_power_l3[10]; - char reactive_power_l1[10]; - char reactive_power_l2[10]; - char reactive_power_l3[10]; - char power_factor_l1[10]; - char power_factor_l2[10]; - char power_factor_l3[10]; - char energy_total[10]; - + char voltage_l1[33]; dtostrfd(sdm630_voltage[0], Settings.flag2.voltage_resolution, voltage_l1); + char voltage_l2[33]; dtostrfd(sdm630_voltage[1], Settings.flag2.voltage_resolution, voltage_l2); + char voltage_l3[33]; dtostrfd(sdm630_voltage[2], Settings.flag2.voltage_resolution, voltage_l3); + char current_l1[33]; dtostrfd(sdm630_current[0], Settings.flag2.current_resolution, current_l1); + char current_l2[33]; dtostrfd(sdm630_current[1], Settings.flag2.current_resolution, current_l2); + char current_l3[33]; dtostrfd(sdm630_current[2], Settings.flag2.current_resolution, current_l3); + char active_power_l1[33]; dtostrfd(sdm630_active_power[0], Settings.flag2.wattage_resolution, active_power_l1); + char active_power_l2[33]; dtostrfd(sdm630_active_power[1], Settings.flag2.wattage_resolution, active_power_l2); + char active_power_l3[33]; dtostrfd(sdm630_active_power[2], Settings.flag2.wattage_resolution, active_power_l3); + char reactive_power_l1[33]; dtostrfd(sdm630_reactive_power[0], Settings.flag2.wattage_resolution, reactive_power_l1); + char reactive_power_l2[33]; dtostrfd(sdm630_reactive_power[1], Settings.flag2.wattage_resolution, reactive_power_l2); + char reactive_power_l3[33]; dtostrfd(sdm630_reactive_power[2], Settings.flag2.wattage_resolution, reactive_power_l3); + char power_factor_l1[33]; dtostrfd(sdm630_power_factor[0], 2, power_factor_l1); + char power_factor_l2[33]; dtostrfd(sdm630_power_factor[1], 2, power_factor_l2); + char power_factor_l3[33]; dtostrfd(sdm630_power_factor[2], 2, power_factor_l3); + char energy_total[33]; dtostrfd(sdm630_energy_total, Settings.flag2.energy_resolution, energy_total); if (json) { diff --git a/sonoff/xsns_26_lm75ad.ino b/sonoff/xsns_26_lm75ad.ino index ba2f39b16..3b548fa8f 100644 --- a/sonoff/xsns_26_lm75ad.ino +++ b/sonoff/xsns_26_lm75ad.ino @@ -81,9 +81,8 @@ float LM75ADGetTemp(void) { void LM75ADShow(boolean json) { if (lm75ad_type) { - char temperature[10]; - float t = LM75ADGetTemp(); + char temperature[33]; dtostrfd(t, Settings.flag2.temperature_resolution, temperature); if (json) { diff --git a/sonoff/xsns_32_mpu6050.ino b/sonoff/xsns_32_mpu6050.ino index b80366194..8fbdfc6f3 100644 --- a/sonoff/xsns_32_mpu6050.ino +++ b/sonoff/xsns_32_mpu6050.ino @@ -177,19 +177,19 @@ void MPU_6050Show(boolean json) MPU_6050PerformReading(); double tempConv = (MPU_6050_temperature / 340.0 + 35.53); - char temperature[10]; + char temperature[33]; dtostrfd(tempConv, Settings.flag2.temperature_resolution, temperature); - char axis_ax[10]; + char axis_ax[33]; dtostrfd(MPU_6050_ax, Settings.flag2.axis_resolution, axis_ax); - char axis_ay[10]; + char axis_ay[33]; dtostrfd(MPU_6050_ay, Settings.flag2.axis_resolution, axis_ay); - char axis_az[10]; + char axis_az[33]; dtostrfd(MPU_6050_az, Settings.flag2.axis_resolution, axis_az); - char axis_gx[10]; + char axis_gx[33]; dtostrfd(MPU_6050_gx, Settings.flag2.axis_resolution, axis_gx); - char axis_gy[10]; + char axis_gy[33]; dtostrfd(MPU_6050_gy, Settings.flag2.axis_resolution, axis_gy); - char axis_gz[10]; + char axis_gz[33]; dtostrfd(MPU_6050_gz, Settings.flag2.axis_resolution, axis_gz); if (json) { diff --git a/sonoff/xsns_34_hx711.ino b/sonoff/xsns_34_hx711.ino index 400f0991e..c3f10ded9 100644 --- a/sonoff/xsns_34_hx711.ino +++ b/sonoff/xsns_34_hx711.ino @@ -204,7 +204,7 @@ bool HxCommand(void) } if (show_parms) { - char item[10]; + char item[33]; dtostrfd((float)Settings.weight_item / 10, 1, item); snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"Sensor34\":{\"" D_JSON_WEIGHT_REF "\":%d,\"" D_JSON_WEIGHT_CAL "\":%d,\"" D_JSON_WEIGHT_MAX "\":%d,\"" D_JSON_WEIGHT_ITEM "\":%s}}"), Settings.weight_reference, Settings.weight_calibration, Settings.weight_max * 1000, item); @@ -331,7 +331,6 @@ const char HTTP_HX711_CAL[] PROGMEM = "%s" void HxShow(boolean json) { - char weight_chr[10]; char scount[30] = { 0 }; uint16_t count = 0; @@ -345,6 +344,7 @@ void HxShow(boolean json) } weight = (float)hx_weight / 1000; // kilograms } + char weight_chr[33]; dtostrfd(weight, Settings.flag2.weight_resolution, weight_chr); if (json) { @@ -452,10 +452,9 @@ void HxSaveSettings(void) void HxLogUpdates(void) { - char weigth_ref_chr[10]; - char weigth_item_chr[10]; - + char weigth_ref_chr[33]; dtostrfd((float)Settings.weight_reference / 1000, Settings.flag2.weight_resolution, weigth_ref_chr); + char weigth_item_chr[33]; dtostrfd((float)Settings.weight_item / 10000, 4, weigth_item_chr); snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_WIFI D_JSON_WEIGHT_REF " %s, " D_JSON_WEIGHT_ITEM " %s"), diff --git a/sonoff/xsns_35_tx20.ino b/sonoff/xsns_35_tx20.ino index 8ab868b45..a198c766f 100644 --- a/sonoff/xsns_35_tx20.ino +++ b/sonoff/xsns_35_tx20.ino @@ -172,14 +172,13 @@ void Tx20Init(void) { void Tx20Show(boolean json) { - char wind_speed_string[10]; - char wind_speed_max_string[10]; - char wind_speed_avg_string[10]; - char wind_direction_string[4]; - + char wind_speed_string[33]; dtostrfd(tx20_wind_speed_kmh, 2, wind_speed_string); + char wind_speed_max_string[33]; dtostrfd(tx20_wind_speed_max, 2, wind_speed_max_string); + char wind_speed_avg_string[33]; dtostrfd(tx20_wind_speed_avg, 2, wind_speed_avg_string); + char wind_direction_string[4]; GetTextIndexed(wind_direction_string, sizeof(wind_direction_string), tx20_wind_direction, kTx20Directions); if (json) { diff --git a/sonoff/xsns_37_rfsensor.ino b/sonoff/xsns_37_rfsensor.ino index 4829a71b1..efb864924 100644 --- a/sonoff/xsns_37_rfsensor.ino +++ b/sonoff/xsns_37_rfsensor.ino @@ -19,10 +19,15 @@ #ifdef USE_RF_SENSOR /*********************************************************************************************\ - * RF receive based on work by Paul Tonkes (www.nodo-domotica.nl) + * RF receiver based on work by Paul Tonkes (www.nodo-domotica.nl) + * + * Supported 434MHz receiver is Aurel RX-4M50RR30SF + * Supported 868MHz receiver is Aurel RX-AM8SF + * + * Connect one of above receivers with a 330 Ohm resistor to any GPIO * * USE_THEO_V2 Add support for 434MHz Theo V2 sensors as documented on https://sidweb.nl - * USE_ALECTO_V2 Add support for 868MHz Alecto V2 sensors like ACH2010, WS3000 and DKW2012 + * USE_ALECTO_V2 Add support for 868MHz Alecto V2 sensors like ACH2010, WS3000 and DKW2012 weather stations \*********************************************************************************************/ #define XSNS_37 37 @@ -41,24 +46,26 @@ #define RFSNS_SIGNAL_TIMEOUT 10 // Pulse timings in mSec. Beyond this value indicate end of message #define RFSNS_SIGNAL_REPEAT_TIME 500 // (500) Tijd in mSec. waarbinnen hetzelfde event niet nogmaals via RF mag binnenkomen. Onderdrukt ongewenste herhalingen van signaal -struct RawSignalStruct // Variabelen geplaatst in struct zodat deze later eenvoudig kunnen worden weggeschreven naar SDCard +typedef struct RawSignalStruct // Variabelen geplaatst in struct zodat deze later eenvoudig kunnen worden weggeschreven naar SDCard { int Number; // aantal bits, maal twee omdat iedere bit een mark en een space heeft. byte Repeats; // Aantal maal dat de pulsreeks verzonden moet worden bij een zendactie. byte Multiply; // Pulses[] * Multiply is de echte tijd van een puls in microseconden unsigned long Time; // Tijdstempel wanneer signaal is binnengekomen (millis()) - byte Pulses[RFSNS_RAW_BUFFER_SIZE+2]; // Tabel met de gemeten pulsen in microseconden gedeeld door rfsns_raw_signal.Multiply. Dit scheelt helft aan RAM geheugen. + byte Pulses[RFSNS_RAW_BUFFER_SIZE+2]; // Tabel met de gemeten pulsen in microseconden gedeeld door rfsns_raw_signal->Multiply. Dit scheelt helft aan RAM geheugen. // Om legacy redenen zit de eerste puls in element 1. Element 0 wordt dus niet gebruikt. -} rfsns_raw_signal = {0, 0, 0, 0L}; +} raw_signal_t; +raw_signal_t *rfsns_raw_signal = NULL; uint8_t rfsns_rf_bit; uint8_t rfsns_rf_port; +uint8_t rfsns_any_sensor = 0; /*********************************************************************************************\ * Fetch signals from RF pin \*********************************************************************************************/ -boolean RfSnsFetchSignal(byte DataPin, boolean StateSignal) +bool RfSnsFetchSignal(byte DataPin, bool StateSignal) { uint8_t Fbit = digitalPinToBitMask(DataPin); uint8_t Fport = digitalPinToPort(DataPin); @@ -73,15 +80,15 @@ boolean RfSnsFetchSignal(byte DataPin, boolean StateSignal) // rust tussen de signalen. Op deze wijze wordt het aantal zinloze captures teruggebracht. unsigned long PulseLength = 0; - if (rfsns_raw_signal.Time) { // Eerst een snelle check, want dit bevindt zich in een tijdkritisch deel... - if (rfsns_raw_signal.Repeats && (rfsns_raw_signal.Time + RFSNS_SIGNAL_REPEAT_TIME) > millis()) { // ...want deze check duurt enkele micro's langer! + if (rfsns_raw_signal->Time) { // Eerst een snelle check, want dit bevindt zich in een tijdkritisch deel... + if (rfsns_raw_signal->Repeats && (rfsns_raw_signal->Time + RFSNS_SIGNAL_REPEAT_TIME) > millis()) { // ...want deze check duurt enkele micro's langer! PulseLength = micros() + RFSNS_SIGNAL_TIMEOUT *1000; // Wachttijd - while (((rfsns_raw_signal.Time + RFSNS_SIGNAL_REPEAT_TIME) > millis()) && (PulseLength > micros())) { + while (((rfsns_raw_signal->Time + RFSNS_SIGNAL_REPEAT_TIME) > millis()) && (PulseLength > micros())) { if ((*portInputRegister(Fport) & Fbit) == FstateMask) { PulseLength = micros() + RFSNS_SIGNAL_TIMEOUT *1000; } } - while (((rfsns_raw_signal.Time + RFSNS_SIGNAL_REPEAT_TIME) > millis()) && ((*portInputRegister(Fport) & Fbit) != FstateMask)); + while (((rfsns_raw_signal->Time + RFSNS_SIGNAL_REPEAT_TIME) > millis()) && ((*portInputRegister(Fport) & Fbit) != FstateMask)); } } @@ -89,7 +96,7 @@ boolean RfSnsFetchSignal(byte DataPin, boolean StateSignal) bool Ftoggle = false; unsigned long numloops = 0; unsigned long maxloops = RFSNS_SIGNAL_TIMEOUT * LoopsPerMilli; - rfsns_raw_signal.Multiply = RFSNS_RAWSIGNAL_SAMPLE; // Ingestelde sample groote. + rfsns_raw_signal->Multiply = RFSNS_RAWSIGNAL_SAMPLE; // Ingestelde sample groote. do { // lees de pulsen in microseconden en plaats deze in de tijdelijke buffer rfsns_raw_signal numloops = 0; while(((*portInputRegister(Fport) & Fbit) == FstateMask) ^ Ftoggle) { // while() loop *A* @@ -98,19 +105,19 @@ boolean RfSnsFetchSignal(byte DataPin, boolean StateSignal) PulseLength = (numloops *1000) / LoopsPerMilli; // Bevat nu de pulslengte in microseconden if (PulseLength < RFSNS_MIN_PULSE_LENGTH) { break; } Ftoggle = !Ftoggle; - rfsns_raw_signal.Pulses[RawCodeLength++] = PulseLength / (unsigned long)rfsns_raw_signal.Multiply; // sla op in de tabel rfsns_raw_signal + rfsns_raw_signal->Pulses[RawCodeLength++] = PulseLength / (unsigned long)rfsns_raw_signal->Multiply; // sla op in de tabel rfsns_raw_signal } - while(RawCodeLength < RFSNS_RAW_BUFFER_SIZE && numloops <= maxloops); // Zolang nog ruimte in de buffer, geen timeout en geen stoorpuls + while(RawCodeLength < RFSNS_RAW_BUFFER_SIZE && numloops <= maxloops); // Zolang nog ruimte in de buffer, geen timeout en geen stoorpuls if ((RawCodeLength >= RFSNS_MIN_RAW_PULSES) && (RawCodeLength < RFSNS_RAW_BUFFER_SIZE -1)) { - rfsns_raw_signal.Repeats = 0; // Op dit moment weten we nog niet het type signaal, maar de variabele niet ongedefinieerd laten. - rfsns_raw_signal.Number = RawCodeLength -1; // Aantal ontvangen tijden (pulsen *2) - rfsns_raw_signal.Pulses[rfsns_raw_signal.Number] = 0; // Laatste element bevat de timeout. Niet relevant. - rfsns_raw_signal.Time = millis(); + rfsns_raw_signal->Repeats = 0; // Op dit moment weten we nog niet het type signaal, maar de variabele niet ongedefinieerd laten. + rfsns_raw_signal->Number = RawCodeLength -1; // Aantal ontvangen tijden (pulsen *2) + rfsns_raw_signal->Pulses[rfsns_raw_signal->Number] = 0; // Laatste element bevat de timeout. Niet relevant. + rfsns_raw_signal->Time = millis(); return true; } else - rfsns_raw_signal.Number = 0; + rfsns_raw_signal->Number = 0; } return false; @@ -151,8 +158,6 @@ typedef struct { uint8_t volt; } theo_v2_t1_t; -theo_v2_t1_t rfsns_theo_v2_t1[RFSNS_THEOV2_MAX_CHANNEL]; - typedef struct { uint32_t time; int16_t temp; @@ -160,11 +165,19 @@ typedef struct { uint8_t volt; } theo_v2_t2_t; -theo_v2_t2_t rfsns_theo_v2_t2[RFSNS_THEOV2_MAX_CHANNEL]; +theo_v2_t1_t *rfsns_theo_v2_t1 = NULL; +theo_v2_t2_t *rfsns_theo_v2_t2 = NULL; -boolean RfSnsAnalyzeTheov2(void) +void RfSnsInitTheoV2(void) { - if (rfsns_raw_signal.Number != RFSNS_THEOV2_PULSECOUNT) return false; + rfsns_theo_v2_t1 = (theo_v2_t1_t*)malloc(RFSNS_THEOV2_MAX_CHANNEL * sizeof(theo_v2_t1_t)); + rfsns_theo_v2_t2 = (theo_v2_t2_t*)malloc(RFSNS_THEOV2_MAX_CHANNEL * sizeof(theo_v2_t2_t)); + rfsns_any_sensor++; +} + +void RfSnsAnalyzeTheov2(void) +{ + if (rfsns_raw_signal->Number != RFSNS_THEOV2_PULSECOUNT) { return; } byte Checksum; // 8 bits Checksum over following bytes byte Channel; // 3 bits channel @@ -174,7 +187,6 @@ boolean RfSnsAnalyzeTheov2(void) int Payload2; // 16 bits byte b, bytes, bits, id; - char log[128]; byte idx = 3; byte chksum = 0; @@ -182,7 +194,7 @@ boolean RfSnsAnalyzeTheov2(void) b = 0; for (bits = 0; bits <= 7; bits++) { - if ((rfsns_raw_signal.Pulses[idx] * rfsns_raw_signal.Multiply) > RFSNS_THEOV2_RF_PULSE_MID) { + if ((rfsns_raw_signal->Pulses[idx] * rfsns_raw_signal->Multiply) > RFSNS_THEOV2_RF_PULSE_MID) { b |= 1 << bits; } idx += 2; @@ -216,13 +228,13 @@ boolean RfSnsAnalyzeTheov2(void) } } - if (Checksum != chksum) { return false; } - if (Channel == 0) { return false; } + if (Checksum != chksum) { return; } + if ((Channel == 0) || (Channel > RFSNS_THEOV2_MAX_CHANNEL)) { return; } + Channel--; - rfsns_raw_signal.Repeats = 1; // het is een herhalend signaal. Bij ontvangst herhalingen onderdukken + rfsns_raw_signal->Repeats = 1; // het is een herhalend signaal. Bij ontvangst herhalingen onderdukken int Payload3 = Voltage & 0x3f; - Channel--; switch (Type) { case 1: // Temp / Lux @@ -242,11 +254,9 @@ boolean RfSnsAnalyzeTheov2(void) snprintf_P(log_data, sizeof(log_data), PSTR("RFS: TheoV2, ChkCalc %d, Chksum %d, id %d, Type %d, Ch %d, Volt %d, BattLo %d, Pld1 %d, Pld2 %d"), chksum, Checksum, id, Type, Channel +1, Payload3, (Voltage & 0x80) >> 7, Payload1, Payload2); AddLog(LOG_LEVEL_DEBUG); - - return true; } -void RfSnsTheoV2Show(boolean json) +void RfSnsTheoV2Show(bool json) { bool sensor_once = false; @@ -254,7 +264,7 @@ void RfSnsTheoV2Show(boolean json) if (rfsns_theo_v2_t1[i].time) { char sensor[10]; snprintf_P(sensor, sizeof(sensor), PSTR("TV2T1C%d"), i +1); - char voltage[10]; + char voltage[33]; dtostrfd((float)rfsns_theo_v2_t1[i].volt / 10, 1, voltage); if (rfsns_theo_v2_t1[i].time < LocalTime() - RFSNS_VALID_WINDOW) { @@ -263,7 +273,7 @@ void RfSnsTheoV2Show(boolean json) mqtt_data, sensor, GetDT(rfsns_theo_v2_t1[i].time).c_str(), voltage); } } else { - char temperature[10]; + char temperature[33]; dtostrfd(ConvertTemp((float)rfsns_theo_v2_t1[i].temp / 100), Settings.flag2.temperature_resolution, temperature); if (json) { @@ -291,7 +301,7 @@ void RfSnsTheoV2Show(boolean json) if (rfsns_theo_v2_t2[i].time) { char sensor[10]; snprintf_P(sensor, sizeof(sensor), PSTR("TV2T2C%d"), i +1); - char voltage[10]; + char voltage[33]; dtostrfd((float)rfsns_theo_v2_t2[i].volt / 10, 1, voltage); if (rfsns_theo_v2_t2[i].time < LocalTime() - RFSNS_VALID_WINDOW) { @@ -301,10 +311,10 @@ void RfSnsTheoV2Show(boolean json) } } else { float temp = ConvertTemp((float)rfsns_theo_v2_t2[i].temp / 100); - char temperature[10]; - dtostrfd(temp, Settings.flag2.temperature_resolution, temperature); float humi = (float)rfsns_theo_v2_t2[i].hum / 100; - char humidity[10]; + char temperature[33]; + dtostrfd(temp, Settings.flag2.temperature_resolution, temperature); + char humidity[33]; dtostrfd(humi, Settings.flag2.humidity_resolution, humidity); if (json) { @@ -414,15 +424,19 @@ typedef struct { uint8_t wdir; } alecto_v2_t; -alecto_v2_t rfsns_alecto_v2; - +alecto_v2_t *rfsns_alecto_v2 = NULL; uint16_t rfsns_alecto_rain_base = 0; -//unsigned long rfsns_alecto_time = 60000; -boolean RfSnsAnalyzeAlectov2() +void RfSnsInitAlectoV2(void) { - if (!(((rfsns_raw_signal.Number >= RFSNS_ACH2010_MIN_PULSECOUNT) && - (rfsns_raw_signal.Number <= RFSNS_ACH2010_MAX_PULSECOUNT)) || (rfsns_raw_signal.Number == RFSNS_DKW2012_PULSECOUNT))) { return false; } + rfsns_alecto_v2 = (alecto_v2_t*)malloc(sizeof(alecto_v2_t)); + rfsns_any_sensor++; +} + +void RfSnsAnalyzeAlectov2() +{ + if (!(((rfsns_raw_signal->Number >= RFSNS_ACH2010_MIN_PULSECOUNT) && + (rfsns_raw_signal->Number <= RFSNS_ACH2010_MAX_PULSECOUNT)) || (rfsns_raw_signal->Number == RFSNS_DKW2012_PULSECOUNT))) { return; } byte c = 0; byte rfbit; @@ -437,11 +451,11 @@ boolean RfSnsAnalyzeAlectov2() float factor; char buf1[16]; - if (rfsns_raw_signal.Number > RFSNS_ACH2010_MAX_PULSECOUNT) { maxidx = 9; } + if (rfsns_raw_signal->Number > RFSNS_ACH2010_MAX_PULSECOUNT) { maxidx = 9; } // Get message back to front as the header is almost never received complete for ACH2010 byte idx = maxidx; - for (byte x = rfsns_raw_signal.Number; x > 0; x = x-2) { - if (rfsns_raw_signal.Pulses[x-1] * rfsns_raw_signal.Multiply < 0x300) { + for (byte x = rfsns_raw_signal->Number; x > 0; x = x-2) { + if (rfsns_raw_signal->Pulses[x-1] * rfsns_raw_signal->Multiply < 0x300) { rfbit = 0x80; } else { rfbit = 0; @@ -461,49 +475,47 @@ boolean RfSnsAnalyzeAlectov2() msgtype = (data[0] >> 4) & 0xf; rc = (data[0] << 4) | (data[1] >> 4); - if (checksum != checksumcalc) { return false; } - if ((msgtype != 10) && (msgtype != 5)) { return true; } + if (checksum != checksumcalc) { return; } + if ((msgtype != 10) && (msgtype != 5)) { return; } - rfsns_raw_signal.Repeats = 1; // het is een herhalend signaal. Bij ontvangst herhalingen onderdukken + rfsns_raw_signal->Repeats = 1; // het is een herhalend signaal. Bij ontvangst herhalingen onderdukken // Test set -// rfsns_raw_signal.Number = RFSNS_DKW2012_PULSECOUNT; // DKW2012 +// rfsns_raw_signal->Number = RFSNS_DKW2012_PULSECOUNT; // DKW2012 // data[8] = 11; // WSW factor = 1.22; // (1.08) -// atime = rfsns_raw_signal.Time - rfsns_alecto_time; +// atime = rfsns_raw_signal->Time - rfsns_alecto_time; // if ((atime > 10000) && (atime < 60000)) factor = (float)60000 / atime; -// rfsns_alecto_time = rfsns_raw_signal.Time; +// rfsns_alecto_time = rfsns_raw_signal->Time; // Serial.printf("atime %d, rfsns_alecto_time %d\n", atime, rfsns_alecto_time); - rfsns_alecto_v2.time = LocalTime(); - rfsns_alecto_v2.type = (RFSNS_DKW2012_PULSECOUNT == rfsns_raw_signal.Number); - rfsns_alecto_v2.temp = (float)(((data[1] & 0x3) * 256 + data[2]) - 400) / 10; - rfsns_alecto_v2.humi = data[3]; + rfsns_alecto_v2->time = LocalTime(); + rfsns_alecto_v2->type = (RFSNS_DKW2012_PULSECOUNT == rfsns_raw_signal->Number); + rfsns_alecto_v2->temp = (float)(((data[1] & 0x3) * 256 + data[2]) - 400) / 10; + rfsns_alecto_v2->humi = data[3]; uint16_t rain = (data[6] * 256) + data[7]; // check if rain unit has been reset! if (rain < rfsns_alecto_rain_base) { rfsns_alecto_rain_base = rain; } if (rfsns_alecto_rain_base > 0) { - rfsns_alecto_v2.rain += ((float)rain - rfsns_alecto_rain_base) * 0.30; + rfsns_alecto_v2->rain += ((float)rain - rfsns_alecto_rain_base) * 0.30; } rfsns_alecto_rain_base = rain; - rfsns_alecto_v2.wind = (float)data[4] * factor; - rfsns_alecto_v2.gust = (float)data[5] * factor; - if (rfsns_alecto_v2.type) { - rfsns_alecto_v2.wdir = data[8] & 0xf; + rfsns_alecto_v2->wind = (float)data[4] * factor; + rfsns_alecto_v2->gust = (float)data[5] * factor; + if (rfsns_alecto_v2->type) { + rfsns_alecto_v2->wdir = data[8] & 0xf; } snprintf_P(log_data, sizeof(log_data), PSTR("RFS: " D_ALECTOV2 ", ChkCalc %d, Chksum %d, rc %d, Temp %d, Hum %d, Rain %d, Wind %d, Gust %d, Dir %d, Factor %s"), checksumcalc, checksum, rc, ((data[1] & 0x3) * 256 + data[2]) - 400, data[3], (data[6] * 256) + data[7], data[4], data[5], data[8] & 0xf, dtostrfd(factor, 3, buf1)); AddLog(LOG_LEVEL_DEBUG); - - return true; } void RfSnsAlectoResetRain(void) { if ((RtcTime.hour == 0) && (RtcTime.minute == 0) && (RtcTime.second == 5)) { - rfsns_alecto_v2.rain = 0; // Reset Rain + rfsns_alecto_v2->rain = 0; // Reset Rain } } @@ -537,40 +549,43 @@ const char HTTP_SNS_ALECTOV2_WDIR[] PROGMEM = "%s" "{s}" D_ALECTOV2 " " D_TX20_WIND_DIRECTION "{m}%s{e}"; #endif -void RfSnsAlectoV2Show(boolean json) +void RfSnsAlectoV2Show(bool json) { - if (rfsns_alecto_v2.time) { - if (rfsns_alecto_v2.time < LocalTime() - RFSNS_VALID_WINDOW) { + if (rfsns_alecto_v2->time) { + if (rfsns_alecto_v2->time < LocalTime() - RFSNS_VALID_WINDOW) { if (json) { snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"" D_ALECTOV2 "\":{\"" D_JSON_RFRECEIVED "\":\"%s\"}"), - mqtt_data, GetDT(rfsns_alecto_v2.time).c_str()); + mqtt_data, GetDT(rfsns_alecto_v2->time).c_str()); } } else { - float temp = ConvertTemp(rfsns_alecto_v2.temp); - char temperature[10]; + float temp = ConvertTemp(rfsns_alecto_v2->temp); + char temperature[33]; dtostrfd(temp, Settings.flag2.temperature_resolution, temperature); - float humi = (float)rfsns_alecto_v2.humi; - char humidity[10]; + float humi = (float)rfsns_alecto_v2->humi; + char humidity[33]; dtostrfd(humi, Settings.flag2.humidity_resolution, humidity); - char rain[10]; - dtostrfd(rfsns_alecto_v2.rain, 2, rain); - char wind[10]; - dtostrfd(rfsns_alecto_v2.wind, 2, wind); - char gust[10]; - dtostrfd(rfsns_alecto_v2.gust, 2, gust); + char rain[33]; + dtostrfd(rfsns_alecto_v2->rain, 2, rain); + char wind[33]; + dtostrfd(rfsns_alecto_v2->wind, 2, wind); + char gust[33]; + dtostrfd(rfsns_alecto_v2->gust, 2, gust); char wdir[4]; char direction[20]; - if (rfsns_alecto_v2.type) { - GetTextIndexed(wdir, sizeof(wdir), rfsns_alecto_v2.wdir, kAlectoV2Directions); + if (rfsns_alecto_v2->type) { + GetTextIndexed(wdir, sizeof(wdir), rfsns_alecto_v2->wdir, kAlectoV2Directions); snprintf_P(direction, sizeof(direction), PSTR(",\"Direction\":\"%s\""), wdir); } if (json) { snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"" D_ALECTOV2 "\":{\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_HUMIDITY "\":%s,\"Rain\":%s,\"Wind\":%s,\"Gust\":%s%s}"), - mqtt_data, temperature, humidity, rain, wind, gust, (rfsns_alecto_v2.type) ? direction : ""); + mqtt_data, temperature, humidity, rain, wind, gust, (rfsns_alecto_v2->type) ? direction : ""); if (0 == tele_period) { #ifdef USE_DOMOTICZ - // Use a rule + // Use a rules to send data to Domoticz where also a local BMP280 is connected: + // on tele-alectov2#temperature do var1 %value% endon on tele-alectov2#humidity do var2 %value% endon on tele-bmp280#pressure do publish domoticz/in {"idx":68,"svalue":"%var1%;%var2%;0;%value%;0"} endon + // on tele-alectov2#wind do var1 %value% endon on tele-alectov2#gust do publish domoticz/in {"idx":69,"svalue":"0;N;%var1%;%value%;22;24"} endon"} + // on tele-alectov2#rain do publish domoticz/in {"idx":70,"svalue":"0;%value%"} endon #endif // USE_DOMOTICZ } #ifdef USE_WEBSERVER @@ -578,7 +593,7 @@ void RfSnsAlectoV2Show(boolean json) snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_TEMP, mqtt_data, D_ALECTOV2, temperature, TempUnit()); snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_HUM, mqtt_data, D_ALECTOV2, humidity); snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_ALECTOV2, mqtt_data, rain, wind, gust); - if (rfsns_alecto_v2.type) { + if (rfsns_alecto_v2->type) { snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_ALECTOV2_WDIR, mqtt_data, wdir); } #endif // USE_WEBSERVER @@ -590,25 +605,37 @@ void RfSnsAlectoV2Show(boolean json) void RfSnsInit(void) { - rfsns_rf_bit = digitalPinToBitMask(pin[GPIO_RF_SENSOR]); - rfsns_rf_port = digitalPinToPort(pin[GPIO_RF_SENSOR]); - pinMode(pin[GPIO_RF_SENSOR], INPUT); + rfsns_raw_signal = (raw_signal_t*)(malloc(sizeof(raw_signal_t))); + if (rfsns_raw_signal) { + memset(rfsns_raw_signal, 0, sizeof(raw_signal_t)); // Init defaults to 0 +#ifdef USE_THEO_V2 + RfSnsInitTheoV2(); +#endif +#ifdef USE_ALECTO_V2 + RfSnsInitAlectoV2(); +#endif + if (rfsns_any_sensor) { + rfsns_rf_bit = digitalPinToBitMask(pin[GPIO_RF_SENSOR]); + rfsns_rf_port = digitalPinToPort(pin[GPIO_RF_SENSOR]); + pinMode(pin[GPIO_RF_SENSOR], INPUT); + } else { + free(rfsns_raw_signal); + rfsns_raw_signal = NULL; + } + } } void RfSnsAnalyzeRawSignal(void) { - snprintf_P(log_data, sizeof(log_data), PSTR("RFS: Pulses %d"), (int)rfsns_raw_signal.Number); + snprintf_P(log_data, sizeof(log_data), PSTR("RFS: Pulses %d"), (int)rfsns_raw_signal->Number); AddLog(LOG_LEVEL_DEBUG); -// if (Settings.flag3.rf_type) { #ifdef USE_THEO_V2 RfSnsAnalyzeTheov2(); #endif -// } else { #ifdef USE_ALECTO_V2 RfSnsAnalyzeAlectov2(); #endif -// } } void RfSnsEverySecond(void) @@ -618,12 +645,11 @@ void RfSnsEverySecond(void) #endif } -void RfSnsShow(boolean json) +void RfSnsShow(bool json) { #ifdef USE_THEO_V2 RfSnsTheoV2Show(json); #endif - #ifdef USE_ALECTO_V2 RfSnsAlectoV2Show(json); #endif @@ -635,13 +661,13 @@ void RfSnsShow(boolean json) boolean Xsns37(byte function) { - boolean result = false; + bool result = false; - if (pin[GPIO_RF_SENSOR] < 99) { + if ((pin[GPIO_RF_SENSOR] < 99) && (FUNC_INIT == function)) { + RfSnsInit(); + } + else if (rfsns_raw_signal) { switch (function) { - case FUNC_INIT: - RfSnsInit(); - break; case FUNC_LOOP: if ((*portInputRegister(rfsns_rf_port) &rfsns_rf_bit) == rfsns_rf_bit) { if (RfSnsFetchSignal(pin[GPIO_RF_SENSOR], HIGH)) { diff --git a/sonoff/xsns_38_az7798.ino b/sonoff/xsns_38_az7798.ino new file mode 100644 index 000000000..f909bb061 --- /dev/null +++ b/sonoff/xsns_38_az7798.ino @@ -0,0 +1,305 @@ +/* + xsns_38_az7798.ino - AZ_Instrument 7798 CO2/temperature/humidity meter support for Sonoff-Tasmota + + Copyright (C) 2018 Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef USE_AZ7798 + +#define XSNS_38 38 + +/*********************************************************************************************\ + * CO2, temperature and humidity meter and data logger + * Known by different names (brief survey 2018-12-16): + * - AZ-Instrument 7798 (http://www.az-instrument.com.tw) + * - co2meter.com AZ-0004 + * - Extech CO200 + * - BES CO7788 (https://www.aliexpress.com) + * - AZ CO87 (https://www.aliexpress.com) + * - no doubt there are more ... + * + * Hardware Serial will be selected if GPIO1 = [AZ Tx] and GPIO3 = [AZ Rx] + * + * Inside the meter, the serial comms wire with the red stripe goes to GPIO1. + * The other one therefore to GPIO3. + * WeMos D1 Mini is powered from the incoming 5V. + * + * This implementation was derived from xsns_15_mhz19.ino from + * Sonoff-Tasmota-6.3.0 by Arthur de Beun. + * + * The serial comms protocol is not publicly documented, that I could find. + * The info below was obtained by reverse-engineering. + * Port settings: 9600 8N1 + * The suppied USB interface has a CP20x USB-serial bridge. + * The 3-way, 2.5mm jack has tip=RxD, middle=TxD and base=0V + * The TxD output swing is 3V3. + * + * There is never a space before the 0x0d, but the other spaces are there. + * + * serial number / ID + * request: I 0x0d + * response: i 12345678 7798V3.4 0x0d + * + * log info + * request: M 0x0d + * response: m 45 1 C 1af4 0cf4 0x0d + * + * 45 = number of records, but there are only 15 lines of 3 values each) + * 1 = sample rate in seconds + * C = celcius, F + * 1af4 0cf4 = seconds since 2000-01-01 00:00:00 + * + * start time 2014-04-30 19:35:16 + * end time 2014-04-30 19:35:30 + * + * download log data + * request: D 0x0d + * response: m 45 1 C 1af4 0cf4 0x0d + * d 174 955 698 0x0d + * 174 = temp in [C * 10] + * 955 = CO2 [ppm] + * 698 = RH in [% * 10] + * d 174 990 694 0x0d + * ... + * d 173 929 654 0x0d + * + * 15 lines in total, 1 second apart + * + * Sync datalogger time with PC + * request: C 452295746 0x0d + * response: > 0x0d + * + * 452295746 = seconds since 2000-01-01 00:00:00 + * + * Identifier: + * request: J -------- 1 0x0d + * + * the characters (dashes) in the above become the first part of the response to the I command (12345678 above) + * + * Set sample rate + * request: S 10 0x0d + * response: m 12 10 C 1af5 7be1 0x0d + * + * Other characters that seem to give a response: + * A responds with > + * so is similar to the response to C, so other characters may be required + * A is the beep alarm perhaps? + * parameters would be CO2 level and on/off, as per front panel P1.3 setting? + * + * L responds with > + * L perhaps sets the limits for the good and normal levels (P1.1 and P1.2)? + * + * Q responds with > + * Q is reset maybe (P4.1)? + * + * : responds with : T19.9C:C2167ppm:H57.4% + * This one gives the current readings. + ********************************************************************************************** + +/*********************************************************************************************/ + +#include + +#ifndef CO2_LOW +#define CO2_LOW 800 // Below this CO2 value show green light +#endif +#ifndef CO2_HIGH +#define CO2_HIGH 1200 // Above this CO2 value show red light +#endif + +#define AZ_READ_TIMEOUT 400 // Must be way less than 1000 but enough to read 9 bytes at 9600 bps + +TasmotaSerial *AzSerial; + +const char ktype[] = "AZ7798"; +uint8_t az_type = 1; +uint16_t az_co2 = 0; +double az_temperature = 0; +double az_humidity = 0; +uint8_t az_received = 0; +uint8_t az_state = 0; + +/*********************************************************************************************/ + +void AzEverySecond(void) +{ + az_state++; + if (5 == az_state) { // every 5 seconds + az_state = 0; + + AzSerial->flush(); // sync reception + AzSerial->write(":\r", 2); + az_received = 0; + + uint8_t az_response[32]; + unsigned long start = millis(); + uint8_t counter = 0; + uint8_t i, j; + uint8_t response_substr[16]; + + do { + if (AzSerial->available() > 0) { + az_response[counter] = AzSerial->read(); + if(az_response[counter] == 0x0d) { az_received = 1; } + counter++; + } else { + delay(5); + } + } while(((millis() - start) < AZ_READ_TIMEOUT) && (counter < sizeof(az_response)) && !az_received); + + AddLogSerial(LOG_LEVEL_DEBUG_MORE, az_response, counter); + + if (!az_received) { + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 comms timeout")); + return; + } + + i = 0; + while((az_response[i] != 'T') && (i < counter)) {i++;} // find the start of response + if(az_response[i] != 'T') { + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 failed to find start of response")); + return; + } + i++; // advance to start of temperature value + j = 0; + // find the end of temperature + while((az_response[i] != 'C') && (az_response[i] != 'F') && (i < counter)) { + response_substr[j++] = az_response[i++]; + } + if((az_response[i] != 'C') && (az_response[i] != 'F')){ + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 failed to find end of temperature")); + return; + } + response_substr[j] = 0; // add null terminator + az_temperature = CharToDouble((char*)response_substr); // units (C or F) depends on meter setting + if(az_response[i] == 'C') { // meter transmits in degC + az_temperature = ConvertTemp((float)az_temperature); // convert to degF, depending on settings + } else { // meter transmits in degF + az_temperature = ConvertTemp((az_temperature - 32) / 1.8); // convert to degC and then C or F depending on setting + } + i++; // advance to first delimiter + if(az_response[i] != ':') { + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 error first delimiter")); + return; + } + i++; // advance to start of CO2 + if(az_response[i] != 'C') { + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 error start of CO2")); + return; + } + i++; // advance to start of CO2 value + j = 0; + // find the end of CO2 + while((az_response[i] != 'p') && (i < counter)) { + response_substr[j++] = az_response[i++]; + } + if(az_response[i] != 'p') { + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 failed to find end of CO2")); + return; + } + response_substr[j] = 0; // add null terminator + az_co2 = atoi((char*)response_substr); + LightSetSignal(CO2_LOW, CO2_HIGH, az_co2); + i += 3; // advance to second delimiter + if(az_response[i] != ':') { + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 error second delimiter")); + return; + } + i++; // advance to start of humidity + if(az_response[i] != 'H') { + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 error start of humidity")); + return; + } + i++; // advance to start of humidity value + j = 0; + // find the end of humidity + while((az_response[i] != '%') && (i < counter)) { + response_substr[j++] = az_response[i++]; + } + if(az_response[i] != '%') { + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 failed to find end of humidity")); + return; + } + response_substr[j] = 0; // add null terminator + az_humidity = CharToDouble((char*)response_substr); + } +} + +/*********************************************************************************************/ + +void AzInit(void) +{ + az_type = 0; + if ((pin[GPIO_AZ_RXD] < 99) && (pin[GPIO_AZ_TXD] < 99)) { + AzSerial = new TasmotaSerial(pin[GPIO_AZ_RXD], pin[GPIO_AZ_TXD], 1); + if (AzSerial->begin(9600)) { + if (AzSerial->hardwareSerial()) { ClaimSerial(); } + az_type = 1; + } + } +} + +void AzShow(boolean json) +{ + char temperature[33]; + dtostrfd(az_temperature, Settings.flag2.temperature_resolution, temperature); + char humidity[33]; + dtostrfd(az_humidity, Settings.flag2.humidity_resolution, humidity); + + if (json) { + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"%s\":{\"" D_JSON_CO2 "\":%d,\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_HUMIDITY "\":%s}"), mqtt_data, ktype, az_co2, temperature, humidity); +#ifdef USE_DOMOTICZ + if (0 == tele_period) DomoticzSensor(DZ_AIRQUALITY, az_co2); +#endif // USE_DOMOTICZ +#ifdef USE_WEBSERVER + } else { + snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_CO2, mqtt_data, ktype, az_co2); + snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_TEMP, mqtt_data, ktype, temperature, TempUnit()); + snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_HUM, mqtt_data, ktype, humidity); +#endif // USE_WEBSERVER + } +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +boolean Xsns38(byte function) +{ + boolean result = false; + + if(az_type){ + switch (function) { + case FUNC_INIT: + AzInit(); + break; + case FUNC_EVERY_SECOND: + AzEverySecond(); + break; + case FUNC_JSON_APPEND: + AzShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_APPEND: + AzShow(0); + break; +#endif // USE_WEBSERVER + } + } + return result; +} + +#endif // USE_AZ7798 diff --git a/tools/fw_efm8bb1/RF-Bridge-EFM8BB1-20181023.hex b/tools/fw_efm8bb1/RF-Bridge-EFM8BB1-20181023.hex new file mode 100644 index 000000000..e193e8efe --- /dev/null +++ b/tools/fw_efm8bb1/RF-Bridge-EFM8BB1-20181023.hex @@ -0,0 +1,492 @@ +:020000040000FA +:1000000002142AED24F8FEEFD39E4015ED2408FDDE +:10001000E433FCC3EF9DEC6480F874809850028058 +:1000200001C32202139A7597A522220218387EFF77 +:10003000EFD394004022EF9480501DE4FDED75F065 +:1000400005A4240CF582E4341EF583E493B507047B +:10005000AE0580040DBD06E5AF06220215CD121DCA +:100060004053D87853DBFE121D04E4900085F02243 +:10007000D2DE2202188BD202121ABAC290C296D2D3 +:1000800080E4FBFD7F10121CEB1206CE74A4F0D2AC +:10009000AFE4F53BF53CD296053CE53C7002053BF0 +:1000A000B410F3E53BB427EEC296120026300209E5 +:1000B0001219F48E3E8F3F8006753E01753F00E5B4 +:1000C0003F7004E53E640170409000CCE07007F59D +:1000D0003BF53C02028D053CE53C7002053BD394A8 +:1000E00010E53B94274002D296D3E53C9430E53BA3 +:1000F0009475500302028DE4F53BF53C9000CCF082 +:100100009000CBF0C29602028DE4F53BF53C9000E6 +:10011000CCE014602A14700302025F147003020220 +:100120001D1470030202332404600302028DE53FB4 +:1001300064AA600302028D9000CC04F002028DE5F7 +:100140003F9000CBF0A37402F0E53F120C3902059A +:10015000A00173A10185A501A9A601BDA701C6A89B +:1001600001E2A901C9B001D2B1019AC0028DFF001C +:100170000002141206879000A87401F0E490007544 +:10018000F07FA1806E12005E9000CC7404F0753D8B +:1001900008E4F54175400902028D9000CC7404F02A +:1001A000E4F54175400202028DE49000A8F0900051 +:1001B00075F07FA612071174A6F002028D1206CE0A +:1001C00074A4F002028D753D089000CC7403F00217 +:1001D000028D9000757401F07FB1120711EFF002EB +:1001E000028D1206879000A8E0F53AE4F0900075C1 +:1001F000F07FA91219B790007CEFF07D307C75126A +:10020000070002028D1206EE90007CEFF0E49000F1 +:10021000CCF08079E49000CBF0A3F08070E4F5415D +:10022000E53FF540E540D394009000CC402C7404A9 +:10023000F0805A74042541F582E43400F583E53FEB +:10024000F00541E541B540059000CC800DE541C386 +:10025000947040397540709000CC7402F0802EE5A7 +:100260003F645570289000CCF0C2029000CBE0248F +:1002700060601824FC600F24FE600B14600824F6F4 +:100280006004241070077FA0121CB8D2029000CB2B +:10029000E0120C3902B6A102E0A402F2A503DAA62C +:1002A00003F6A805C0A905FDB00670B103A6C003FA +:1002B000C8FF000000AA900084E030E70F7DC87CF2 +:1002C000001206B77FA312192E0205DA121CFC4099 +:1002D000030200AA7DE87C031206B77FA20205F59F +:1002E000900084E020E7030200AA7FA412192E02E6 +:1002F00003EE9000CCE060030200AA900085E024A9 +:10030000FC6070240460030200AA153D900008E020 +:10031000FEA3E0FF7C007D64120C5FC006C00712E4 +:100320001573D007D006120BA78F4B900006E0FE86 +:10033000A3E0FF8E4CF54D7C007D64120C5FC0067F +:10034000C007121573D007D006120BA78F4E754F3A +:1003500018900008E0FAA3E0FB900004E0FCA3E0A2 +:10036000FDA3E0FEA3E0FF1217569000747406F0A0 +:100370000204B97F0112002E12159960030200AA2F +:10038000E53D601A7F0112002EEF12154912158DFE +:1003900050030205B81206E2121D2E0205B812061D +:1003A000EE7FA00205F59000CCE060030200AA9069 +:1003B0000004E0FCA3E0FD120700D296121D2EC23D +:1003C000967FA0121CB8800A7F01121CB8E490002E +:1003D000CCF0D2021206F70200AA900084E020E7D7 +:1003E000030200AA547FF543FD7FA61214B9E490DE +:1003F0000084F00200AA9000CCE060030200AA9002 +:100400000085E024FC7003020569240460030200F7 +:10041000AA900004E0FF2480600804700DE4F51D3C +:100420008003751D017542FF800F12002E8F42E57B +:1004300042F46005121599F51D153D12005EE51D8B +:1004400014607C0460030200AA900004E0647F70E2 +:1004500024A31215BAA3E0FAA3E0FBA3E0F54BA393 +:10046000E0F54CA3E0F54DA3E0F54EA3E01207083C +:10047000740CF0803BE542F46031121547F5828E32 +:10048000831215AA740593FA740693FB740993F505 +:100490004B740793F54C740893F54D740A93F54E1D +:1004A000740B931207087401F08005E49000CBF000 +:1004B0009000CBE070030200AA121BF00200AA9089 +:1004C0000004E06480704E900074740CF090000B97 +:1004D000E0FF7E00900009E0FCA3E0FD120A47C0A7 +:1004E00006C00790000C1215C6C007C00690000D8C +:1004F0001215C6AA06AB0790000E1215C68E4F8FB6 +:10050000508A4D8B4ED04BD04C9000051215BA90AE +:10051000000FE08048E542F4700302062F121547F1 +:100520009000747401F08544828E83740612156401 +:10053000C006C0077407121564C007C0067408120D +:100540001564AA06AB0774091215648E4F8F508A82 +:100550004D8B4ED04BD04C1215AA740A93F551D046 +:1005600003D0021217D00200AAE53D70101206F760 +:100570007FA0121CB8D202E4F51D0200AAE51D14EA +:10058000601B0460030200AAE542F4600E121547E6 +:1005900012158D40061206E2121D2E801BE542F454 +:1005A000601612154712158D400EEFFD7C007F017D +:1005B0007E00121B0D121D2EE4900085F00200AA91 +:1005C000900084E0FF30E71C547FF5437DC87C0039 +:1005D00012069AAD437FAB1214B9E4900084F0D2B6 +:1005E000020200AA121CFC40030200AA7DE87C0360 +:1005F00012069A7FAA121CB8D2020200AA90000426 +:10060000E025E0F5439000CCE060030200AA9000F2 +:1006100085E0700512005E800B900085E064046048 +:10062000030200AAE5436007E540C394045008E4D0 +:100630009000CBF00200AA74062543F9E43400755B +:100640004801F549894AC3E540954324FEF54B909E +:100650000005E0F54C7B017A0079061216527FA066 +:10066000121CB890007CE0FF1219B7D2020200AA57 +:10067000900084E020E7030200AA7FB11216D4E4C0 +:10068000900084F00200AA7D327C007F017E00127F +:100690001B0DD296121D2EC296227F017E00121BC8 +:1006A0000DD296121D2EC2969000A8E53AF0900049 +:1006B0007CE0FF1219B7227F017E00121B0DD2963B +:1006C000121D2EC29690007CE0FF1219B7229000F6 +:1006D000A87401F0E4900075F07FA41219B790009F +:1006E0007C22EFFD7C007F017E00121B0D2290001A +:1006F0007CE0FF1219B72290007CE0FF1219B722AC +:100700007F017E00121B0D22F54F121756900074C8 +:10071000221219B790007C22AFE9AEEA120DB68E14 +:10072000088F092093030209A285080A85090BC3D3 +:10073000E509950DF511E508950CF510900075E0AB +:1007400014700302099C0460030209C3900085E051 +:100750001460650460030209C3900084E060030232 +:1007600009C39000A8E0FFAB11AA10AD0FAC0E12A8 +:1007700010558F1AE51A648070030209C390007F38 +:10078000120E6B900002E510F0A3E511120DC1E40A +:10079000900074F0900077F090007EF0F51BF51C4F +:1007A000FE7F70FD7B017A007904120C8CE49000CE +:1007B0007DF090008504F022E51A120D998E23F544 +:1007C00024E51A120DFA70030208D004600302092E +:1007D000C3900004120CBBFFD39400400B90007E2A +:1007E000E09F5004E004F022120E13AE10AF11ABE4 +:1007F00007AA06E50F2BFFE50E3AFEE433FDE433CE +:10080000FCC004A905AA06AB07AE0EAF0F120E730B +:10081000D000120BA78F22120D80FD120003401989 +:1008200090000A120D83FDAF22120003400B120C40 +:10083000B8120E646003020954120D80FDAF22123B +:1008400000035009120CB8C3120E444013D3E50F35 +:10085000951CE50E951B402B120CB8120E647023EC +:10086000900086120E6B120CC4C083C082120E0B55 +:100870007401A806088002C333D8FC4FD082D0830D +:10088000F0801790007A120E6BC290D3951CE50E83 +:10089000951B4006850E1B850F1C900088E0700D8F +:1008A000120CC6120D78FF121C4D120DBD120CB8A1 +:1008B000120E6460030209C3121CF45005E4900098 +:1008C00083F0120D7570030209941209D70209947E +:1008D000120CB8FF7E00900004120AE4FDACF01286 +:1008E0000A47120DE18E258F26120E13E4850F158F +:1008F000850E14F513F512851119851018F517F5E5 +:1009000016900006120CF81209C4500F900007123E +:100910000CF21209F55004C2908043900008120CAA +:10092000F21209C4502EAA23A9247BFF90000912B9 +:100930000CF81209F5501D120CC4C083C082120EAF +:100940000B7401A806088002C333D8FC4FD082D0B4 +:1009500083F0800AE4900084F0C290A3F02290001B +:1009600088E0700D120CC6120D78FF121C4D120D8E +:10097000BDAA23A9247BFF90000A120A08120E6464 +:100980007041121CF45005E4900083F0120D756064 +:10099000031209D7C290E4900085F022AF11AE1087 +:1009A000801E85080C85090DC3E509950BF50FE53B +:1009B00008950AF50E900075E0147007AF0FAE0EA3 +:1009C000120E9B22E5159FFFE5149EFE121CACC380 +:1009D000EF9526EE952522121D047D207C037F01D4 +:1009E0007E00121AE490007DE0900083F0A3E51AE7 +:1009F000F04480F022E5199FFFE5189EFE121CAC22 +:100A0000C3EF9526EE952522BB010CE58229F582E0 +:100A1000E5833AF583E0225006E92582F8E622BB19 +:100A2000FE06E92582F8E222E58229F582E5833A8D +:100A3000F583E49322BB010689828A83F022500267 +:100A4000F722BBFE01F322EF8DF0A4A8F0CF8CF0CB +:100A5000A428CE8DF0A42EFE22BC000BBE0029EFF0 +:100A60008DF084FFADF022E4CCF875F008EF2FFF95 +:100A7000EE33FEEC33FCEE9DEC984005FCEE9DFE63 +:100A80000FD5F0E9E4CEFD22EDF8F5F0EE8420D2AA +:100A90001CFEADF075F008EF2FFFED33FD40079819 +:100AA0005006D5F0F222C398FD0FD5F0EA22C2D548 +:100AB000EC30E709B2D5E4C39DFDE49CFCEE30E7E1 +:100AC00015B2D5E4C39FFFE49EFE120A59C3E49D0C +:100AD000FDE49CFC8003120A5930D507C3E49FFF54 +:100AE000E49EFE22BB0110E58229F582E5833AF5FA +:100AF00083E0F5F0A3E0225009E92582F886F008AA +:100B0000E622BBFE0AE92582F8E2F5F008E222E5DA +:100B1000832AF583E993F5F0A3E99322E88FF0A403 +:100B2000CC8BF0A42CFCE98EF0A42CFC8AF0EDA474 +:100B30002CFCEA8EF0A4CDA8F08BF0A42DCC3825A7 +:100B4000F0FDE98FF0A42CCD35F0FCEB8EF0A4FE87 +:100B5000A9F0EB8FF0A4CFC5F02ECD39FEE43CFC1C +:100B6000EAA42DCE35F0FDE43CFC2275F008758238 +:100B700000EF2FFFEE33FECD33CDCC33CCC5823327 +:100B8000C5829BED9AEC99E58298400CF582EE9B2C +:100B9000FEED9AFDEC99FC0FD5F0D6E4CEFBE4CD4A +:100BA000FAE4CCF9A88222B800C1B90059BA002DE4 +:100BB000EC8BF084CFCECDFCE5F0CBF97818EF2F9D +:100BC000FFEE33FEED33FDEC33FCEB33FB10D703CC +:100BD000994004EB99FB0FD8E5E4F9FA227818EF75 +:100BE0002FFFEE33FEED33FDEC33FCC933C910D7D4 +:100BF000059BE99A4007EC9BFCE99AF90FD8E0E4E1 +:100C0000C9FAE4CCFB2275F010EF2FFFEE33FEEDB6 +:100C100033FDCC33CCC833C810D7079BEC9AE89986 +:100C2000400AED9BFDEC9AFCE899F80FD5F0DAE468 +:100C3000CDFBE4CCFAE4C8F922D083D082F8E49367 +:100C40007012740193700DA3A393F8740193F5824D +:100C50008883E4737402936860EFA3A3A380DFEC3E +:100C60008EF0A4CCC5F0CCCDF8EFA4CEC5F02DFD10 +:100C7000E43CFCE8A42EC8C5F03DFDE43CFCEFA438 +:100C8000FFE5F028FEE43DFDE43CFC22EF4E60125F +:100C9000EF60010EEDBB010B89828A83F0A3DFFCBC +:100CA000DEFA2289F05007F709DFFCA9F022BBFE2B +:100CB000FCF309DFFCA9F02290000BAA23A9247BF6 +:100CC000FF020A08D290900077E024FFFFE434FF8F +:100CD000FE7C007D08120AAE74042FF58274003E7B +:100CE000F58322FF900074E02404F582E43400F5DB +:100CF0008322AA23A9247BFF120A08FD7C0090000E +:100D000004120AE4FFAEF0120A47C322120BA7AB8B +:100D100007AA06E4F9F87F407E427D0FFC120BA77C +:100D2000E47BFFFAF9F8120B1CA804A905AA06AB8C +:100D3000077F207ED77D757C01120BA7EFF404227C +:100D4000E53725E0248AF582E43400F58322AE2AD3 +:100D5000AF2B7C007D1F120A598E2E8F2F7C007DB9 +:100D600005120A59C3E52F9FFDE52E9EFCD3E52908 +:100D70009DE5289C22900083E0FF90007DE06F229B +:100D8000900009AA23A9247BFF020A0853DBFE5323 +:100D9000DAFE53F7DF53F7BF2275F005A4240EF5F2 +:100DA00082E4341EF583E4740193FA740293AE0274 +:100DB000227D64120A597C007D0A020A4790007D58 +:100DC000EFF09000887408F022C3E5379FFDE53608 +:100DD0009ED3FCE5339DE5329C2275F0FFA4FFAE67 +:100DE000F07C007D64020A59AA2EA92F7BFF900097 +:100DF00002120AE4F53785F0362275F005A4240DB9 +:100E0000F582E4341EF583E4931422E0FF90008819 +:100E1000E0FE22900088E014F0900077E004F022D9 +:100E2000E52F2FFFE52E3EFEC3E5299FE5289E22F4 +:100E3000AB48AA49A94A854D82758300020A0890E9 +:100E40000081E0D3FF900077E09F22900078E527B3 +:100E5000F0A3E528F022852F82852E83E493FA748F +:100E60000193FB22FF900077E06F22E50EF0A3E5EF +:100E70000FF0227C007D64020C5F540F75F002A419 +:100E8000F58285F083227B007A007929AF28AE278E +:100E900022D3E52B9494E52A9411228E278F289053 +:100EA0000085E0146035147003020F802402600393 +:100EB000021054C290900084E06003021054AF28E6 +:100EC000AE27121C7A4003021054E4900001F01285 +:100ED0000E4B9000857401F0D29022900078E0FCD7 +:100EE000A3E0FDAE047802CEC313CE13D8F92DFFD4 +:100EF000EC3EFED3E5289FE5279E4009120E4BE409 +:100F0000900001F022AF28AE271218DE501F90008B +:100F100001E094004017E4900074F090008874049D +:100F2000F0E490007DF09000857402F022C3E52883 +:100F30009464E52794005003020FE6120E86121106 +:100F4000BE5020E529120D42E0FEA3E02528FFE572 +:100F5000273EC313FEEF13FFE529120D42EEF0A367 +:100F6000EFF022900001E0120D42120E4E900001AF +:100F7000E004F0E0D3940F5003021054E402104553 +:100F8000B290AF28AE271218DE505E121CF4500546 +:100F9000E4900083F0120D75604A121D047DF47C0C +:100FA000017F017E00121AE490007DE0900083F042 +:100FB000900088E07019120CE4C083C082E0FF90BA +:100FC0000001E0540FFEEF4ED082D083F0800E90EF +:100FD0000001E0C454F0440F120CE3EFF0900084E1 +:100FE000E04480F0C290E48067120E8612126450D2 +:100FF00057900088E0B40410E529C454F0120CE3C3 +:10100000EFF0E4900088F022900074E0FF120CE80A +:10101000E0FEE529540FFDEE4DF074042F120CEAAA +:10102000120D78FF121C4DEFF0900074E004F09068 +:1010300000887404F0900074E0D394704016121D80 +:1010400004E4900001F08008121D04E4900001F017 +:10105000900085F0228F278C288D298A2A8B2B756A +:101060002C80E5277071E4F52D7F0112002EEF65CD +:101070002D701B120E914054E52B9450E52A944696 +:10108000504A120D4E4045120E2050408073E52DFF +:10109000120D998E2EF52FE52D120DFA60180470A1 +:1010A0002B120E56C002C003120DE8D003D002125C +:1010B000110750188013120E56C002C003120DE81B +:1010C000D003D0021211075003852D2C052DE52DDC +:1010D000C394064094802DAF2712002E8F2D7F01E0 +:1010E00012002EEF652D701C120E914017E52B9407 +:1010F00050E52A9446500D120D4E4008120E205015 +:1011000003852D2CAF2C22AD2BAC2AAF29AE281293 +:101110001113228E308F318C328D33C2007C007DD2 +:1011200064AF03AE02120DB38F828E83AE36AF373B +:10113000120DB1D3E58294F4E583940140050C7D52 +:10114000F48004AC83AD828C838D82D3EF94F4EE73 +:10115000940140067C017DF48004AC06AD07AE042A +:10116000AF05D3EB9400EA94004038EB9582FDEA9A +:101170009583FCD3E5319DE5309C403FEB2582FD16 +:10118000EA3583FCC3E5319DE5309C502E120DC934 +:101190004029E5372FFDE5363EC3120DD2501CD253 +:1011A000008018120DC94013E5372FFFE5363EFECB +:1011B000C3E5339FE5329E5002D200A200228E2A60 +:1011C0008F2B8B2C8A2D892EC2001212645005D2CF +:1011D00000021261E52BAE2A7803CEC313CE13D8DA +:1011E000F9FDAC06E52BAE2A7802CEC313CE13D898 +:1011F000F92DFDEE3CFCE52AC4F854F0C868FEE584 +:101200002BC4540F482DF531EC3EF530E4F52F900A +:101210000001E0FFE52FC39F5047C3E52B9531FF49 +:10122000E52A9530FEE52F120D42E0FCA3E0FDD348 +:101230009FEC9E4028E52B2531FFE52A3530FEC383 +:10124000ED9FEC9E5017E52E452D452C600BAB2CE9 +:10125000AA2DA92EE52F120A35D2008004052F8071 +:10126000AEA200228E328F338B348A358936C2018A +:10127000E4F537900001E0FFE537C39F4003021318 +:1012800005E533AE327803CEC313CE13D8F9FDACE7 +:1012900006E533AE327802CEC313CE13D8F92DF55E +:1012A00039EE3CF538120D40E0FEA3E0FFC395395E +:1012B000EE953850028004AE38AF398E388F39122F +:1012C0000D40E0FEA3E0FFC39539FDEE9538FCC369 +:1012D000ED9533EC95325028E5392FFFE5383EFE89 +:1012E000C3E5339FE5329E5017E5364535453460FA +:1012F0000BAB34AA35A936E537120A35D201800581 +:101300000537021273A201224200C700004200C347 +:1013100000004200C900004200C500004100CC00AE +:101320004100CB00011D00410084004100850041C7 +:1013300000A8004100750041007C00410076564144 +:101340000089AB410000004100820042007F0000A4 +:101350004200020000420086000042007A00004184 +:10136000008100410088004100770041007E00417B +:101370000074004100830041007D004100010041F4 +:1013800000C0004100C1004100BE004100BF00415B +:1013900000C2004100BD00C10300C0E0C0F0C08336 +:1013A000C082C0D075D000C000C001C002C003C060 +:1013B00004C005C006C007E5985403F55AF45298D6 +:1013C000E55A30E017121D499000C0121A52EFF092 +:1013D0009000C0E004F0E0B41402E4F0E55A30E11B +:1013E0002E9000C2E0D39400401A9000BFE0245B2E +:1013F000F8E6FF121D469000BFE004F09000C2E046 +:1014000014F08002D2039000BFE0B42002E4F0D0D8 +:1014100007D006D005D004D003D002D001D000D030 +:10142000D0D082D083D0F0D0E032121D4F787FE44C +:10143000F6D8FD75817A021474020076E493A3F85D +:10144000E493A34003F68001F208DFF48029E493DB +:10145000A3F85407240CC8C333C4540F4420C883D2 +:101460004004F456800146F6DFE4800B01020408D4 +:1014700010204080901308E47E019360BCA3FF54C9 +:101480003F30E509541FFEE493A360010ECF54C022 +:1014900025E060A840B8E493A3FAE493A3F8E493AA +:1014A000A3C8C582C8CAC583CAF0A3C8C582C8CAB2 +:1014B000C583CADFE9DEE780BE8F458D46E4FEFDC9 +:1014C000F547121A3F247AF582E4341EF58374013D +:1014D00093FA7402938A48F5497FAA121BB0AF456C +:1014E000121A3C2479F582E4341EF583E4931460E7 +:1014F0000E047019AA48A9497BFF90000B8009AA25 +:1015000048A9497BFF90000A120A08F547EEC395E7 +:1015100047500774082EFE0D80F3ED04FF121A3CAD +:101520002478F582E4341EF583E493FF121BB0E4C3 +:10153000FEEEC39D500974042E121A2A0E80F27F0B +:1015400055121BB0021D43E54275F005A424A2F517 +:1015500082E4341DF583740193FA740293AE028E13 +:1015600043F5442293FF7E00740493FC740593FDBD +:10157000020A47900006E0FEA3E0FF900009E02F7A +:10158000FF900008E03EAB07FAE4F9F822F5828EFE +:1015900083740C93FFD3940022EF75F005A424A16B +:1015A000F582E4341DF583E49322E493FE74019301 +:1015B000FF740293FC740393FD22E0FEA3E0FFA3FB +:1015C000E0FCA3E0FD22E0FF7E00020A47C0E0C08D +:1015D000F0C083C082C0D075D000C000C001C0027E +:1015E000C003C004C005C006C007E5D85487F52174 +:1015F000F452D8E5F730E508E5F730E603121A5D56 +:1016000053F7DFE52130E708E5D930E003121D503C +:10161000E52130E008E5DA30E003121B5DE521301A +:10162000E108E5DB30E003120718E52130E208E5C8 +:10163000DC30E003121D51D007D006D005D004D015 +:1016400003D002D001D000D0D0D082D083D0F0D04F +:10165000E0328B458A468947120D8C53E2FDE4F552 +:101660004DE54DC3954B504FAB45AA46A947C00326 +:10167000C002C001120E30C4120E7AD001D002D0C6 +:1016800003120AE4F54F85F04ED2801216C6AB4520 +:10169000AA46A947C003C002C001120E30120E7A3A +:1016A000D001D002D003120AE4F54F85F04EC2807B +:1016B0001216C6054D80AAB290AF4C154CEF709E25 +:1016C00043E202C29022FDAC4E7F0A7E00121A8EC7 +:1016D000121D2E22AE07E4F545121BA9900001E071 +:1016E00004FF121BB012174F900001E0FFE545C345 +:1016F0009F5012121A46121A2F121A46F583121A06 +:1017000036054580E3900078E0FF121BB09000782A +:10171000121A3612174FE4F545900074E02401FFC9 +:10172000E433FEC3E5459FEE6480F874809850175B +:1017300074042545121A2A0545E545541F70DA122E +:101740001D43121D3C80D27F55121BB0021D431257 +:101750001D43121D3C228E458F468C478D48AE029C +:10176000AF03120E73C007AF4BAB07E4FAF9F8D022 +:1017700007120D0C900000F0AE4CAF4D120E73C06E +:1017800007AF4EAB07E4FAF9F8D007120D0C900042 +:1017900082F0E54B120DDA900076EFF0E54E120D77 +:1017A000DA900089EFF090007FE545F0A3E546F080 +:1017B000900002E547F0A3E548F0900081E54FF086 +:1017C00043DA0153F7DF43F74053DBFE75F9FF229D +:1017D0008A498B4A755380120D8C53E2FD90007F2D +:1017E000EEF0A3EFF0900002ECF0A3EDF0121B3648 +:1017F000E4F552E552C395515032120CE4E05553D2 +:10180000600AAD50AC4FAF4EAE4D8008AD4CAC4B06 +:10181000AF4AAE49121BCCE553C313F55370099080 +:101820000074E004F0755380055280C743E202C2A1 +:10183000909000857404F022C0E0C083C082C0D0C4 +:1018400075D000C004C005C006C00753C87F900013 +:10185000C7E0FEA3E0FF4E700353C8FB9000C31225 +:1018600019AC50099000C7E4F0A3F0800DC39000BC +:10187000C8E09DF09000C7E09CF0D007D006D005EE +:10188000D004D0D0D082D083D0E032C0E0C083C0BA +:1018900082C0D075D000C004C005C006C0075391F7 +:1018A0007F9000C9E0FEA3E0FF4E70035391FB90D0 +:1018B00000C51219AC50099000C9E4F0A3F0800DE6 +:1018C000C39000CAE09DF09000C9E09CF0D007D022 +:1018D00006D005D004D0D0D082D083D0E032AB0780 +:1018E000AA06900078E0FEA3E0FF7C00120DB1D3C1 +:1018F000EF94F4EE940140050C7DF48004AC06AD49 +:1019000007AE04AF05C3900079E09FFD900078E03A +:101910009EFCC3ED9BEC9A5013A3E02FFF90007840 +:10192000E03EFEC3EB9FEA9E50028001C322AE0759 +:10193000E4FDF545121BA9900002E0FF121BB090D8 +:101940000002121A3690007AE0FF121BB090007A63 +:10195000121A36900086E0FF121BB0900086121A11 +:101960003674042D121A2A0DBD03F67F55121BB0D2 +:10197000021D43AB07AA06E4F9F87F407E427D0FC3 +:10198000FC120BA7A804A905AA06AB077F207ED7E7 +:101990007D757C01120BA7C3E49FFFE49EFE22AB82 +:1019A00007AA06E4F9F87FE87E03FD22E0FCA3E045 +:1019B000FDC3EF9DEE9C228F4590007CE0F5467FB5 +:1019C0000B121D4C43DB01120D8F1200707D0A7C3F +:1019D000007F017E00121B0D121D2E43E202E490D7 +:1019E0000085F0900084F09000CBE545F090007CFD +:1019F000F0AF46229000C0E0FF9000BEE0B50705C2 +:101A00007E017F00229000BE121A52E0FD7C009001 +:101A100000BEE004F0E0B41402E4F09000BDE0FE8B +:101A2000EE4204E4F0AE04AF0522F582E43400F5A2 +:101A300083E0FF021BB0A3E0FF021BB0121BB0E566 +:101A40004675F005A422E54525E0248AF582E434B4 +:101A50000022E024A9F582E43400F58322120E3F2F +:101A60004003021C2F120CE4120E0BEFA806088094 +:101A700002C313D8FC30E00B900000E0FF121D4CB5 +:101A8000D29022900082E0FF121D4CC290228E560E +:101A90008F578C588D59121973121C94E55924BF15 +:101AA0009000CAF0E55834FF9000C9F09000C5E5F9 +:101AB00056F0A3E557F04391042212002A121D0BA1 +:101AC000121D12121CC3121D34121C64121CE112CE +:101AD0001CCD121CD7121CA0121D19121D20121D84 +:101AE00038021D278E2A8F2B8C2C8D2D12199F12B8 +:101AF0001980121C889000C7E52CF0A3E52DF0900A +:101B000000C3E52AF0A3E52BF043C804228E478FDB +:101B1000488C498D4A12199F121980121C9490000A +:101B2000C9E549F0A3E54AF09000C5E547F0A3E513 +:101B300048F043910422D290D28090007F121B5033 +:101B4000121D2EC290C280900002121B50021D2E48 +:101B5000E0FCA3E0FD7F0A7E00121A8E229000882E +:101B6000E07008900074E004120DC1120E1990008C +:101B700088E014F0120E3F5003021B8253E2FDC2B4 +:101B80008022120CE4120E0BEFA806088002C31389 +:101B9000D8FC30E0059000768003900089E07D005D +:101BA000FCE4FF121C1122AE077FAA121BB0AF0685 +:101BB000C2039000C1E0B42002E4F09000C1E02430 +:101BC0005BF8A607E004F0A3E004F0228C548D55E6 +:101BD000D280AD07AC067F0A7E00121A8E121D2E2F +:101BE000C280AD55AC547F0A7E00121A8E021D2EA3 +:101BF000120DC2E014F09000777401F0900085742B +:101C000003F0121B8253E2FD121B3643E202020074 +:101C100070AB07AF04EB14600C14600E2402700E5E +:101C20008DFB8FFC228DE98FEA228DEB8FEC22E475 +:101C3000FDFCFF121C11120D8F121D4053D878535A +:101C4000E2FDC280C2909000857404F0227E1DE403 +:101C5000FDEF30E70625E06EFF8004EF25E0FF0D85 +:101C6000BD08EE22AF885388AF758CA0758DCBEF81 +:101C70005440FEEF54104E428822C3EF94ACEE94D1 +:101C80000D4003D38001C322AD07AC06ECF5CBAF0A +:101C9000058FCA22AD07AC06ECF593AF058F9222F3 +:101CA000C2DE75D90575F9FF75960122EE30E7079A +:101CB000C3E49FFFE49EFE22121BA77F55121BB0B8 +:101CC000021D4375E34075E10175E20222E591547E +:101CD000045391FB429122758E547589224388509A +:101CE00022E5C8540453C8FB42C82253984FEB4F17 +:101CF0004DF59822E5C8C320E201D322E591C32027 +:101D0000E201D32253C8FB53C87F2275A41175D4B6 +:101D1000CE2275A54175D5772253F77F75DA4A2211 +:101D200053F77F75DB302275E69075A8B022E591F8 +:101D300020E2FB22E4F5A922439810223003FD2281 +:101D4000C2DE22D299228F9922AF99228F8C222231 +:101D500022220190307000064001904B19180012A9 +:101D6000C005DC0002BC012C461E28080BB8232845 +:101D700000044C01904B191800251C0BB80003847B +:101D80000140461E1800000072D80702BC012C2634 +:101D90004040000BB81C520190010303011846009B +:101DA0000100FF1D520200FF1D5F0300FF1D6C04B8 +:101DB00000FF1D790500FF1D860601FF1D930190A0 +:101DC000307000064001904B19180012C005DC006D +:101DD00002BC012C461E28080BB8232800044C0125 +:101DE000904B191800251C0BB80003840140461EB7 +:101DF0001800000072D80702BC012C264040000BDE +:101E0000B81C520190010303011846000100FF1D98 +:101E1000BE0200FF1DCB0300FF1DD80400FF1DE51F +:101E20000500FF1DF20601FF1DFF01903070000646 +:101E30004001904B19180012C005DC0002BC012CB7 +:101E4000461E28080BB8232800044C01904B191893 +:101E500000251C0BB80003840140461E180000003A +:101E600072D80702BC012C264040000BB81C52015E +:101E700090010303011846000100FF1E2A0200FF23 +:101E80001E370300FF1E440400FF1E510500FF1E05 +:061E90005E0601FF1E6B5F +:00000001FF diff --git a/tools/fw_efm8bb1/RF-Bridge-EFM8BB1-20181102.hex b/tools/fw_efm8bb1/RF-Bridge-EFM8BB1-20181102.hex new file mode 100644 index 000000000..4e6a245df --- /dev/null +++ b/tools/fw_efm8bb1/RF-Bridge-EFM8BB1-20181102.hex @@ -0,0 +1,452 @@ +:020000040000FA +:1000000002135C7E1DE4FDEF30E70625E06EFF8005 +:1000100004EF25E0FF0DBD08EE2253984FEB4F4D46 +:10002000F598220212CC7597A522220216418E590C +:100030008F5A8C5B8D5C121807121A3EE55C24BF48 +:100040009000D8F0E55B34FF9000D7F09000D3E546 +:1000500059F0A3E55AF043910422220213EB121A3D +:10006000CF53D87853DAFE121A9AE4900080F02227 +:10007000D2DE22021694D202121972C290C296C225 +:1000800080E4FBFD7F1012001A12066274A4F0D205 +:10009000AFE4F53CF53DD296053DE53D7002053CEB +:1000A000B410F3E53CB427EEC296120026300209E4 +:1000B0001218C28E3F8F408006753F01754000E5E3 +:1000C000407004E53F640170409000BCE07007F5AB +:1000D0003CF53D020293053DE53D7002053CD3949D +:1000E00010E53C94274002D296D3E53D9430E53CA0 +:1000F00094755003020293E4F53CF53D9000BCF08A +:100100009000A3F0C296020293E4F53CF53D900006 +:10011000BCE014602A14700302026514700302022A +:100120002314700302023924046003020293E540A1 +:1001300064AA60030202939000BC04F0020293E5FB +:10014000409000A3F09000BC7402F0E540120B4018 +:10015000020DA00175A10185A501A9A601BFA701F6 +:10016000C8A801E4A901CBB001D4B1019AC002939F +:10017000FF0000021812061BE49000A2F090007429 +:10018000F07FA1807212005E9000BC7404F0753E96 +:1001900008E4F5427541090202939000BC7404F032 +:1001A000E4F5427541020202939000A27480F0E4EB +:1001B000900074F07FA612069274A6F002029312C9 +:1001C000066274A4F0020293753E089000BC7403AA +:1001D000F00202939000747401F07FB1120692EF66 +:1001E000F002029312061B9000A2E0F53B7480F02F +:1001F000E4900074F07FA912188B90007AEFF07DE4 +:10020000307C757F017E0012192102029312067460 +:1002100090007AEFF0E48005E49000A3F09000BC39 +:10022000F08070E4F542E540F541E541D39400905B +:1002300000BC402C7404F0805A74032542F582E41B +:100240003400F583E540F00542E542B541059000F4 +:10025000BC800DE542C3947040397541709000BC7C +:100260007402F0802EE540645570289000BCF0C206 +:10027000029000A3E02460601824FC600F24FE605C +:100280000B14600824F66004241070077FA0121A73 +:1002900025D2029000A3E0120B4002BCA102E6A40A +:1002A00002F8A50399A603B3A8055EA90595B006B3 +:1002B00004B10366C00387FF000000AA90007FE03E +:1002C00030E70F7DC87C0012064B7FA31217C202D5 +:1002D0000578121A9240030200AA7DE87C031206F8 +:1002E0004B7FA20205FC90007FE020E7030200AAFA +:1002F0007FA41217C20203AB9000BCE060030200AF +:10030000AA12176A6043240360030200AA153E1272 +:10031000005E900005E0FAA3E0FBFFAE0212176159 +:10032000900007E0F8A3E0888385834BF54C858334 +:100330004DF54E900005E0F54FA3E0F550E4F55182 +:10034000755218755306020534E53E601112177197 +:10035000500302054E12067D121ABD02054E12060A +:10036000747FA00205FC9000BCE060030200AA12AA +:1003700017617F017E00121921D296121ABDC29612 +:100380007FA0121A25800A7F02121A25E49000BC71 +:10039000F0D2021206890200AA90007FE020E70353 +:1003A0000200AA547FFD7FA61216E7E490007FF0BA +:1003B0000200AA9000BCE060030200AA12176A7053 +:1003C0000302053A240360030200AA900003E0FF41 +:1003D000248070057543FF80028F43153E12005E36 +:1003E000900003E06480600302046EA3E0FCA3E0DD +:1003F000FD900008E0FAA3E0FBFFAE02120991C0F5 +:1004000006C007900006E0FCA3E0FDAF03AE0212B9 +:100410000991C006C00790000AE0FF7E0090000826 +:10042000E0FCA3E0FD120991C006C00790000B128A +:10043000175AC007C00690000C12175AAA06AB073D +:1004400090000D12175A8E4F8F508A4D8B4ED04B05 +:10045000D04C90000EE0F551A3E0F55275530DD04D +:1004600003D002D005D004D007D006020534E543FE +:10047000F470030205C012175324E2F582E4341A23 +:10048000F583E493FF7E0012175324E5F582E434EC +:100490001AF583E493FA740193FDAC02120991C03A +:1004A00006C00712175324E3F582E4341AF583E4F7 +:1004B0009312175BC006C00712175324E7F582E4B6 +:1004C000341A121733C006C00712175324E8F582F6 +:1004D000E4341A121733C007C00612175324E9F583 +:1004E00082E4341A121733AA06AB0712175324EA10 +:1004F000F582E4341A12173312175324E4F582E418 +:10050000341AF583E493FD12175324EBF582E43497 +:100510001AF583E493F5528D518E4F8F508A4D8B8F +:100520004ED04BD04C755301D003D002D005D0042F +:10053000D007D00612156C0200AAE53E6018E5430C +:10054000F4600B121771400612067D121ABDE4907A +:100550000080F00200AA1206897FA00205FC90002C +:100560007FE030E71D7DC87C0012062E90007FE002 +:10057000547FFD7FAB1216E7E490007FF0D20202B9 +:1005800000AA121A9240030200AA7DE87C03120618 +:100590002E7FAA8067900003E025E0F5449000BC20 +:1005A000E060030200AA12176A6047240360030296 +:1005B00000AA12005EE5446007E541C394045008B8 +:1005C000E49000A3F00200AA74052544F9E4340085 +:1005D000754801F549894AC3E541954424FEF54B28 +:1005E000900004E0F54C7B017A0079051215DE02DB +:1005F00000AA90007AE0FF12188B7FA0121A25D271 +:10060000020200AA90007FE020E7030200AA7FB167 +:10061000121470E490007FF00200AA7D327C007F0B +:10062000017E00121921D296121ABDC296227F01B4 +:100630007E00121921D296121ABDC2969000A2E530 +:100640003BF090007AE0FF12188B227F017E0012AF +:100650001921D296121ABDC29690007AE0FF1218A4 +:100660008B22E49000A2F0900074F07FA412188B0B +:1006700090007A2290007AE0FF12188B22EFFD7C26 +:10068000007F017E001219212290007AE0FF1218EB +:100690008B2212188B90007A228C228D238A248B35 +:1006A00025AE07900080E014607F046003020921FA +:1006B00090007FE06003020921C3E5239414E52242 +:1006C0009405500EC3E5259414E5249405500302C7 +:1006D00009219000A2E0FFAD0685242C85252DABD5 +:1006E00023AA2212184B90007DEFF0648070030261 +:1006F0000921900001E524F0A3E525F09000837422 +:1007000008F0E4900075F090007CF0F508F509FE23 +:100710007F70FD7B017A007903120B66E490007B09 +:10072000F0D29090008004F02290007DE0FD75F002 +:100730000DA42454F582E4341B120D82FC74019341 +:10074000120E4C90007DE075F00DA4244CF582E46F +:10075000341BF583E493FFD39400400B90007CE0BE +:100760009F5004E004F02290007DE0F96053FD12F8 +:100770000E94120D82120EB59000C1120DF7245086 +:10078000F582E4341B120E021209919000C3120D7F +:10079000F72451F582E4341BF583E493FFE9120D4D +:1007A00088120EB59000C5120DF72452F582E4347C +:1007B0001B120E021209919000C7EEF0A3EFF08019 +:1007C0003E900078E0FEA3E0FF9000C1EEF0A3EFC2 +:1007D000F0900081E0FCA3E0FD9000C3ECF0A3EDFD +:1007E000F0A3ECF0A3EDF0A3120DF72454F582E48E +:1007F000341B120E02900078E0FCA3E0120E4C9025 +:100800000083E014F0900075E004F0AE22AF23E422 +:10081000FCFD9000C9120B34AE24AF25E4FCFD9022 +:1008200000CD120B34120E5C9000C1120922500E42 +:100830009000CD120E5F9000C3120922400CC3122B +:100840000EA05020120D9AB5071A900078E522F0FC +:10085000A3E523F0C290D3120EA2406A8522088538 +:1008600023098062120E5C9000C512093A500E9066 +:1008700000CD120E5F9000C712093A400CD3120E41 +:10088000A04033120D9A6F702D900081E522F0A3E5 +:10089000E523F0D290120DDAC083C082E0FF900011 +:1008A00083E0FE7401A806088002C333D8FC4FD051 +:1008B00082D083F08010E490007FF0C290A3F0908B +:1008C000007D7480F022900083E07012120DDA1225 +:1008D0000E33FF120003EFF09000837408F0120D46 +:1008E0009A6F703D121A8A5005E490007EF0120E45 +:1008F000306027121A9A7D207C037F017E00121837 +:10090000F890007BE090007EF090007DE090007F0A +:10091000F04480F090007D7480F0C290E4900080FC +:10092000F022FFE0FCA3E0FDC3EF9DFFEE9CFE1272 +:100930001A56C3EF9527EE952622FFE0FCA3E0FDB3 +:10094000C3EF9DFFEE9CFE121A56C3EF9527EE955E +:100950002622BB010CE58229F582E5833AF583E086 +:10096000225006E92582F8E622BBFE06E92582F838 +:10097000E222E58229F582E5833AF583E49322BBFE +:10098000010689828A83F0225002F722BBFE01F31E +:1009900022EF8DF0A4A8F0CF8CF0A428CE8DF0A487 +:1009A0002EFE22BC000BBE0029EF8DF084FFADF0BF +:1009B00022E4CCF875F008EF2FFFEE33FEEC33FCA9 +:1009C000EE9DEC984005FCEE9DFE0FD5F0E9E4CEDF +:1009D000FD22EDF8F5F0EE8420D21CFEADF075F0AE +:1009E00008EF2FFFED33FD4007985006D5F0F222B7 +:1009F000C398FD0FD5F0EA22C2D5EC30E709B2D595 +:100A0000E4C39DFDE49CFCEE30E715B2D5E4C39F42 +:100A1000FFE49EFE1209A3C3E49DFDE49CFC800359 +:100A20001209A330D507C3E49FFFE49EFE22BB0159 +:100A300010E58229F582E5833AF583E0F5F0A3E03D +:100A4000225009E92582F886F008E622BBFE0AE971 +:100A50002582F8E2F5F008E222E5832AF583E9939E +:100A6000F5F0A3E9932275F008758200EF2FFFEEF1 +:100A700033FECD33CDCC33CCC58233C5829BED9ACA +:100A8000EC99E58298400CF582EE9BFEED9AFDEC28 +:100A900099FC0FD5F0D6E4CEFBE4CDFAE4CCF9A86E +:100AA0008222B800C1B90059BA002DEC8BF084CF76 +:100AB000CECDFCE5F0CBF97818EF2FFFEE33FEED4D +:100AC00033FDEC33FCEB33FB10D703994004EB9977 +:100AD000FB0FD8E5E4F9FA227818EF2FFFEE33FE8A +:100AE000ED33FDEC33FCC933C910D7059BE99A40BF +:100AF00007EC9BFCE99AF90FD8E0E4C9FAE4CCFBD7 +:100B00002275F010EF2FFFEE33FEED33FDCC33CC2A +:100B1000C833C810D7079BEC9AE899400AED9BFDB3 +:100B2000EC9AFCE899F80FD5F0DAE4CDFBE4CCFAC6 +:100B3000E4C8F922ECF0A3EDF0A3EEF0A3EFF0226D +:100B4000D083D082F8E4937012740193700DA3A344 +:100B500093F8740193F5828883E473740293686058 +:100B6000EFA3A3A380DFEF4E6012EF60010EEDBB99 +:100B7000010B89828A83F0A3DFFCDEFA2289F05020 +:100B800007F709DFFCA9F022BBFEFCF309DFFCA993 +:100B9000F0228E228F23900080E014604A147003AC +:100BA000020C6F24026003020D4DC29090007FE0A2 +:100BB0006003020D4DAF23AE22121A174003020D3F +:100BC0004DE4F524E524120D51E4F0A3F00524E5ED +:100BD00024B40FF0E4900000F0F524120E709000A1 +:100BE000807401F0D29022900076E0FCA3E0FDAE8C +:100BF000047802CEC313CE13D8F92DFFEC3EFED3FA +:100C0000E5239FE5229E4009120E70E4900000F05B +:100C100022AF23AE2212177B501F900000E09400F9 +:100C20004017E4900073F09000837404F0E49000A7 +:100C30007BF09000807402F022C3E5239464E522E7 +:100C400094005003020D3E120EBF1210E05003023A +:100C50000D4D900000E0120D51120E73900000E057 +:100C600004F0E0D3940F5003020D4DE4020D48B29E +:100C700090120EBF12122A5059900083E0B4041053 +:100C8000E524C454F0120D73EFF0E4900083F022D9 +:100C9000900073E0FF120D78E0FEE524540FFDEEA6 +:100CA0004DF074032F120D7A120E33FF120003EF72 +:100CB000F0900073E004F09000837404F0900073EF +:100CC000E0D394705003020D4D121A9AE490000084 +:100CD0008076AF23AE2212177B5066121A8A500517 +:100CE000E490007EF0120E306052121A9A7DF47C6D +:100CF000017F017E001218F890007BE090007EF0EA +:100D0000900083E07019120D74C083C082E0FF90E0 +:100D10000000E0540FFEEF4ED082D083F0800E90A2 +:100D20000000E0C454F0440F120D73EFF090007314 +:100D3000E0FF120EDB90007FE04480F0C290E48080 +:100D400008121A9AE4900000F0900080F022FCE56E +:100D50002A25E02484F582E43400F583228E398F3D +:100D60003AE53075F00DA4244BF582E4341BF5838D +:100D7000E49322FF900073E02403F582E43400F54D +:100D80008322F583E493FFED7E0075F00DA4244DDE +:100D9000F582E4341BF583E4932290007DE075F046 +:100DA0000DA42453F582E4341BF583E493FF9000F3 +:100DB00075E0227C007D641209A37C007D0A120983 +:100DC00091D3EF94F4EE940122EF75F00DA4245624 +:100DD000F582E4341BF583E49322900075E024FF50 +:100DE000FFE434FFFE7C007D081209F874032FF540 +:100DF0008274003EF58322EEF0A3EFF0E975F00D6A +:100E0000A422F583E493FF7E0022E4F9C3ED9BF571 +:100E100082EC9AF583D3EF9582EE958322FFE5303D +:100E200075F00DA4244AF582E4341BF583E4932283 +:100E300090007EE0FF90007BE06F22E0FCA3E0FDED +:100E4000EC547FFAD3ED9400EA940022FD1209914C +:100E50007C007D641209A38E268F27229000C9A3EF +:100E6000A3E0FEA3E022FF7E00AC37AD380209917B +:100E7000900076E522F0A3E523F022852C34852D21 +:100E800035AB2BAA2AAD2922ED2BFDEC3AFCC3EFA2 +:100E90009DEE9C2275F00DA4244FF582E4341B22B4 +:100EA000E5239509E522950822540F75F002A4F573 +:100EB0008285F08322FA740193FDAC020209917BD2 +:100EC000007A007924AF23AE2222E0FEA3E0FFC324 +:100ED00022EC4480AF05F0A3EFF0228F25E4F52744 +:100EE000F526E5252401FFE433FEC3E5269FEE64E5 +:100EF00080F8748098503974032526120D7AE0C466 +:100F0000540FFF120D51120E3B4007EF120D5112FC +:100F10000ED174032526120D7AE0540FFF120D51E5 +:100F2000120E3B4007EF120D51120ED1052680B272 +:100F3000E4F526900000E0FFE526C39F4003020F82 +:100F4000F7E526120D51E030E703020FE9AF2690D6 +:100F50000000E0FEEFC39E4003020FE5EF25E02412 +:100F600086F582E43400F583E0FCA3E0FDEF120D8A +:100F700051ECF0A3EDF0E4FEE5252401FDE433FCA3 +:100F8000EEC39DEC6480F8748098505574032E1263 +:100F90000D7AE0F9C4540FFDEF2401FBE433FAEDC0 +:100FA000B50316E4B50212E9540FFDEFC454F04D39 +:100FB000FD74032E120D7AEDF0EF2401FDE433FCF5 +:100FC00074032E120D7AE0F9540FB50511E4B5043F +:100FD0000DE954F04FFD74032E120D7AEDF00E80E2 +:100FE000970F020F4F05278009E526120D51E05497 +:100FF0007FF00526020F33900000E0C39527F02212 +:101000008F30E4F536120DC96D6003AF3622E5303E +:101010007028120D61120E1DFED39F400AEEFD7C5A +:1010200000AF03AE02800A120D61FD7C00AE34AF4A +:10103000351209A38E378F38800CE530120D8AF5F2 +:1010400037740193F538120D61120E1DD39F402E97 +:101050007C007D64AF03AE02120DB740070C7E0129 +:101060007FF48000120D5D120E66AC06AD07AF0373 +:10107000AE02AB3AAA391219FBEF602D8028AE34CC +:10108000AF35120DB340070C7E017FF48000120DC6 +:101090005D120E66AC06AD07AB3AAA39AF35AE3479 +:1010A000121A09EF6003753601E5366401702EE50A +:1010B00030120E94120E02120E69900078EEF0A318 +:1010C000EFF0E53075F00DA42451F582E4341B12E5 +:1010D0000E02120991900081EEF0A3EFF0AF3622DC +:1010E0008E258F268B278A288929C20012122A5022 +:1010F00005D200021184E4F52A900000E0FFE52A01 +:10110000C39F4003021184120D4FE0FCA3E0FDAE2B +:10111000047803CEC313CE13D8F9FBAA06EDAE04B0 +:101120007802CEC313CE13D8F92BFDEE3A120D4E32 +:10113000E0C4F854F0C868FEA3E0C4540F482DFF83 +:10114000EC3EFEC3E5269FFDE5259E120D4EE0FA1E +:10115000A3E0FBD39DEA9C4026E5262FFFE5253E34 +:10116000FEC3EB9FEA9E5017E52945284527600BF3 +:10117000AB27AA28A929E52A12097FD200800505F4 +:101180002A0210F9A20022AFFBAEFC7C007D0A12FD +:1011900009919000BFEEF0A3EFF09000BF120ECACD +:1011A0009464EE9400500302122930933B8E0A8F10 +:1011B0000BC3EF950DF511EE950CF510900074E052 +:1011C00014601F04706390007DE0FF64806007126C +:1011D0000DC964016053AB11AA10AD0FAC0EE4FF52 +:1011E000803DAF11AE10803E9000BFE0FEA3E08EC8 +:1011F0000CF50DC3950BF50FEE950AF50E900074E6 +:10120000E014601E04702290007DE0FF64806005A1 +:10121000120DC96014AB0FAA0EAD11AC107F010204 +:101220000699AF0FAE0E120B92228E2B8F2C8B2DA8 +:101230008A2E892FC201E4F530900000E0FFE530EE +:10124000C39F40030212C9E52CAE2B7803CEC31313 +:10125000CE13D8F9FDAC06E52CAE2B7802CEC31325 +:10126000CE13D8F92DF532EE3CF531E530120D51A3 +:10127000120ECA9532EE953150028004AE31AF3273 +:101280008E318F32E530120D51120ECA9532FDEEBD +:101290009531FCC3ED952CEC952B5028E5322FFFB2 +:1012A000E5313EFEC3E52C9FE52B9E5017E52F450B +:1012B0002E452D600BAB2DAA2EA92FE53012097FEC +:1012C000D20180050530021239A20122C0E0C0F02F +:1012D000C083C082C0D075D000C000C001C002C0B1 +:1012E00003C004C005C006C007E5985403F55DF4CB +:1012F0005298E55D30E017121AD89000BB1219B56C +:10130000EFF09000BBE004F0E0B41402E4F0E55D1F +:1013100030E12E9000BEE0D39400401A9000BAE075 +:10132000245EF8E6FF121AD59000BAE004F09000AF +:10133000BEE014F08002D2039000BAE0B42002E4D0 +:10134000F0D007D006D005D004D003D002D001D011 +:1013500000D0D0D082D083D0F0D0E03212005A78C2 +:101360007FE4F6D8FD75817D0213A6020076E49332 +:10137000A3F8E493A34003F68001F208DFF4802988 +:10138000E493A3F85407240CC8C333C4540F442077 +:10139000C8834004F456800146F6DFE4800B010266 +:1013A0000408102040809014F2E47E019360BCA3F6 +:1013B000FF543F30E509541FFEE493A360010ECFB4 +:1013C00054C025E060A840B8E493A3FAE493A3F8DE +:1013D000E493A3C8C582C8CAC583CAF0A3C8C5829E +:1013E000C8CAC583CADFE9DEE780BEC0E0C0F0C07E +:1013F00083C082C0D075D000C000C001C002C0034D +:10140000C004C005C006C007E5D85487F521F452D2 +:10141000D8E5F730E508E5F730E603121ADF53F7B1 +:10142000DFE52130E708E5D930E003121ADEE521D7 +:1014300030E008E5DA30E003121187E52130E108F9 +:10144000E5DB30E003121AE0E52130E208E5DC30AC +:10145000E003121AE1D007D006D005D004D003D0A3 +:1014600002D001D000D0D0D082D083D0F0D0E032F2 +:10147000AE07E4F545121999900000E004FF121937 +:10148000C91214EB900000E0FFE545C39F50121213 +:1014900019A91219C51219A9F5831219A3054580B6 +:1014A000E3900076E0FF1219C99000761219A3129A +:1014B00014EBE4F545900073E02401FFE433FEC330 +:1014C000E5459FEE6480F8748098501774032545B5 +:1014D0001219C00545E545541F70DA121AD2121AC6 +:1014E000CB80D27F551219C9021AD2121AD2121AFF +:1014F000CB224200D500004200D100004200D700BC +:10150000004200D3000041007F0041008000410004 +:10151000A2804100740041007D8041007A004200B9 +:101520000100004200810000420078000041008379 +:10153000004100750041007C004100730041007EC5 +:101540000041007B00410000004100BB004100BDA4 +:10155000004100B9004100BA004100BE004100B89E +:1015600000C1034100BC004100A300008A498B4A2E +:1015700085535575568012194AE551D39400401988 +:10158000E4F554E554C39551500FAD50AC4FAF4EF8 +:10159000AE4D12194A055480EAE4F554E554C3955A +:1015A00052503274032555120D7AE05556600AAD3B +:1015B00050AC4FAF4EAE4D8008AD4CAC4BAF4AAEC9 +:1015C0004912194AE556C313F55670050555755667 +:1015D00080055480C7C2909000807403F0228B4530 +:1015E0008A468947E4F54DE54DC3954B5043AB45DD +:1015F000AA46A947C003C002C001AB48AA49A94AEC +:10160000854D82758300120952FDC4120EA9D001C6 +:10161000D002D003120A2EFFAEF0AB45AA46A9476E +:10162000ED120EA9120A2EFDACF012194A054D80DA +:10163000B6AF4C154CEF70ACC2909000807403F0C4 +:1016400022C0E0C083C082C0D075D000C004C005F5 +:10165000C006C00753C87F9000D5E0FEA3E0FF4E50 +:10166000700353C8FB9000D112184050099000D568 +:10167000E4F0A3F0800DC39000D6E09DF09000D57B +:10168000E09CF0D007D006D005D004D0D0D082D0D6 +:1016900083D0E032C0E0C083C082C0D075D000C02B +:1016A00004C005C006C00753917F9000D7E0FEA399 +:1016B000E0FF4E70035391FB9000D3121840500985 +:1016C0009000D7E4F0A3F0800DC39000D8E09DF027 +:1016D0009000D7E09CF0D007D006D005D004D0D041 +:1016E000D082D083D0E032AE05AD07E4FCFB7FAA08 +:1016F0001219C9AF051219C9EE75F00DA424BBF576 +:1017000082E4341BF583E493FFECC39F5007740815 +:101710002CFC0B80F4EB04FF12199BE4FCECC39B44 +:10172000500974032C1219C00C80F27F551219C98C +:10173000021AD2F583E493FF7E00E54375F00DA411 +:1017400024E5F582E4341AF583E493FC740193FDF7 +:10175000020991E54375F00DA422E0FF7E00020925 +:1017600091900003E0FCA3E0FD22900080E024FDC6 +:1017700022901AEDE493FFD3940022AB07AA0690BF +:101780000076E0FEA3E0FF120DB340050C7DF4806F +:1017900004AC06AD07AE04AF05C3900077E09FFD33 +:1017A000900076E09EFCC3ED9BEC9A5013A3E02FD3 +:1017B000FF900076E03EFEC3EB9FEA9E5002800160 +:1017C000C322AE07E4FDF545121999900001E0FF30 +:1017D0001219C99000011219A3900078E0FF1219A4 +:1017E000C99000781219A3900081E0FF1219C990E6 +:1017F00000811219A374032D1219C00DBD03F67FC9 +:10180000551219C9021AD2AB07AA06E4F9F87F40AB +:101810007E427D0FFC120AA2A804A905AA06AB0706 +:101820007F207ED77D757C01120AA2C3E49FFFE46E +:101830009EFE22AB07AA06E4F9F87FE87E03FD22AC +:10184000E0FCA3E0FDC3EF9DEE9C228F288D298A4A +:101850002A8B2B752E80E5282480701EE4F52F122C +:101860000E7BAF2F121000EF6005852F2E8019051B +:101870002FE52FC3940840E7800E120E7BAF28128D +:101880001000EF600385282EAF2E228F4590007A3E +:10189000E0F5467F0B121ADB43DA011200707D0A75 +:1018A0007C007F017E00121921121ABDE490008095 +:1018B000F090007FF09000A3E545F090007AF0AF43 +:1018C00046229000BBE0FF9000B9E0B507057E011D +:1018D0007F00229000B91219B5E0FD7C009000B99C +:1018E000E004F0E0B41402E4F09000B8E0FEEE4250 +:1018F00004E4F0AE04AF05228E288F298C2A8D2BAC +:10190000121833121814121A329000D5E52AF0A3D7 +:10191000E52BF09000D1E528F0A3E529F043C804B9 +:10192000228E478F488C498D4A1218331218141290 +:101930001A3E9000D7E549F0A3E54AF09000D3E5C0 +:1019400047F0A3E548F0439104228C578D58D2907C +:10195000D280AD07AC067F0A7E0012002E121ABD9F +:10196000C290C280AD58AC577F0A7E0012002E0292 +:101970001ABD12002A121AA1121AA8121A62121AF9 +:10198000C31219E5121A80121A6C121A76121A4A28 +:10199000121AAF121AC7021AB67FAA1219C9AF06D5 +:1019A0000219C9A3E0FF0219C9E54525E02484F521 +:1019B00082E4340022E024A4F582E43400F583229A +:1019C000F582E43400F583E0FFC2039000BDE0B48B +:1019D0002002E4F09000BDE0245EF8A607E004F0E9 +:1019E000A3E004F022AF885388AF758CA0758DCB2F +:1019F000EF5440FEEF54104E428822120E0A400669 +:101A0000120E88500109AF0122120E0A4006120E72 +:101A100088500109AF0122C3EF9414EE94054003EE +:101A2000D38001C322AE071219997F551219C9023A +:101A30001AD2AD07AC06ECF5CBAF058FCA22AD07C5 +:101A4000AC06ECF593AF058F9222C2DE75D9057511 +:101A5000F9FF75960122EE30E707C3E49FFFE49E8D +:101A6000FE2275E34075E10175E20122E59154041F +:101A70005391FB429122758E5475892243885022DE +:101A8000E5C8540453C8FB42C822E5C8C320E2019C +:101A9000D322E591C320E201D32253C8FB53C87F70 +:101AA0002275A41175D4CF2275A54175D57722531F +:101AB000F77F75DA302275E69075A8B022E591209F +:101AC000E2FB22E4F5A922439810223003FD22C252 +:101AD000DE22D299228F9922AF99228F8C22222244 +:101AE0002222011F00015E01030301183C00000DCA +:101AF0000400017201020201283C080008170001DD +:101B00008601030301183C00001F0A00012C010399 +:101B10000301183C0000003B0701F40302010240EE +:101B20003C0000071200019001030301183C000073 +:101B300017010001C201020201183C00012401004A +:101B400000D2010202010C3C0001011F00015E01F4 +:101B5000030301183C00000D0400017201020201A0 +:101B6000283C0800081700018601030301183C0007 +:101B7000001F0A00012C01030301183C0000003B78 +:101B80000701F403020102403C000007120001902B +:101B900001030301183C000017010001C201020209 +:101BA00001183C000124010000D2010202010C3C9A +:101BB0000001011F00015E01030301183C00000D3C +:101BC0000400017201020201283C0800081700010C +:101BD0008601030301183C00001F0A00012C0103C9 +:101BE0000301183C0000003B0701F403020102401E +:101BF0003C0000071200019001030301183C0000A3 +:101C000017010001C201020201183C000124010079 +:0A1C100000D2010202010C3C0001A9 +:00000001FF From a46829d593c9cb5e5245f90a19b241fbdd87bb34 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sun, 23 Dec 2018 15:06:26 +0100 Subject: [PATCH 002/428] Release 6.4.1 --- RELEASENOTES.md | 2 ++ sonoff/_changelog.ino | 2 ++ sonoff/sonoff.ino | 24 +++++++++++++++++++----- sonoff/xdrv_01_webserver.ino | 7 +++---- sonoff/xdrv_02_mqtt.ino | 7 +++---- tools/decode-config.py | 6 +++++- 6 files changed, 34 insertions(+), 14 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 9a3f8198e..7ce640e57 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -186,11 +186,13 @@ The following binary downloads have been compiled with ESP8266/Arduino library c ## Changelog Version 6.4.1 20181223 * Change RAM usage BMP/BME I2C sensors + * Change FallbackTopic from cmnd/\/ to cmnd/\_fb/ to discriminate from Topic (#1528) * Change FallbackTopic detection (#4706) * Change Hass discovery to short MQTT messages as used by Hass 0.81 and up (#4711) * Fix possible dtostrf buffer overflows by increasing buffers * Fix wifi strongest signal detection (#4704) * Fix Alexa "this value is outside the range of the device". Needs power cycle and Alexa deletion/discovery cycle. (#3159, #4712) + * Add Slovak language file (#4663) * Add support for AZ-Instrument 7798 CO2 meter/datalogger (#4672) * Add define WIFI_SOFT_AP_CHANNEL in my_user_config.h to set Soft Access Point Channel number between 1 and 13 as used by Wifi Manager web GUI (#4673) * Add define USE_MQTT_TLS_CA_CERT for checking MQTT TLS against root ca using Let's Encrypt cert from sonoff_letsencrypt.h - not supported with core 2.3.0 (#4703) diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index aa0b4381a..d305600c7 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -1,10 +1,12 @@ /* 6.4.1 20181223 * Change RAM usage BMP/BME I2C sensors + * Change FallbackTopic from cmnd// to cmnd/_fb/ to discriminate from Topic (#1528) * Change FallbackTopic detection (#4706) * Change Hass discovery to short MQTT messages as used by Hass 0.81 and up (#4711) * Fix possible dtostrf buffer overflows by increasing buffers * Fix wifi strongest signal detection (#4704) * Fix Alexa "this value is outside the range of the device". Needs power cycle and Alexa deletion/discovery cycle. (#3159, #4712) + * Add Slovak language file (#4663) * Add support for AZ-Instrument 7798 CO2 meter/datalogger (#4672) * Add define WIFI_SOFT_AP_CHANNEL in my_user_config.h to set Soft Access Point Channel number between 1 and 13 as used by Wifi Manager web GUI (#4673) * Add define USE_MQTT_TLS_CA_CERT for checking MQTT TLS against root ca using Let's Encrypt cert from sonoff_letsencrypt.h - not supported with core 2.3.0 (#4703) diff --git a/sonoff/sonoff.ino b/sonoff/sonoff.ino index b262c7d9b..be5964a94 100755 --- a/sonoff/sonoff.ino +++ b/sonoff/sonoff.ino @@ -245,20 +245,25 @@ char* GetOtaUrl(char *otaurl, size_t otaurl_size) return otaurl; } -void GetTopic_P(char *stopic, byte prefix, char *topic, const char* subtopic) +char* GetTopic_P(char *stopic, byte prefix, char *topic, const char* subtopic) { /* prefix 0 = Cmnd prefix 1 = Stat prefix 2 = Tele + prefix 4 = Cmnd fallback + prefix 5 = Stat fallback + prefix 6 = Tele fallback */ char romram[CMDSZ]; String fulltopic; snprintf_P(romram, sizeof(romram), subtopic); - if (fallback_topic_flag) { + if (fallback_topic_flag || (prefix > 3)) { + prefix &= 3; fulltopic = FPSTR(kPrefixes[prefix]); fulltopic += F("/"); fulltopic += mqtt_client; + fulltopic += F("_fb"); // cmnd/_fb } else { fulltopic = Settings.mqtt_fulltopic; if ((0 == prefix) && (-1 == fulltopic.indexOf(F(MQTT_TOKEN_PREFIX)))) { @@ -280,6 +285,12 @@ void GetTopic_P(char *stopic, byte prefix, char *topic, const char* subtopic) fulltopic.replace(F("//"), "/"); if (!fulltopic.endsWith("/")) fulltopic += "/"; snprintf_P(stopic, TOPSZ, PSTR("%s%s"), fulltopic.c_str(), romram); + return stopic; +} + +char* GetFallbackTopic_P(char *stopic, byte prefix, const char* subtopic) +{ + return GetTopic_P(stopic, prefix +4, NULL, subtopic); } char* GetStateText(byte state) @@ -467,8 +478,10 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) if (XdrvMqttData(topicBuf, sizeof(topicBuf), dataBuf, sizeof(dataBuf))) return; grpflg = (strstr(topicBuf, Settings.mqtt_grptopic) != NULL); - snprintf_P(stemp1, sizeof(stemp1), PSTR(D_CMND "/%s/"), mqtt_client); // Full Fallback topic = cmnd/DVES_xxxxxxxx + + GetFallbackTopic_P(stemp1, CMND, ""); // Full Fallback topic = cmnd/DVES_xxxxxxxx_fb/ fallback_topic_flag = (!strncmp(topicBuf, stemp1, strlen(stemp1))); + type = strrchr(topicBuf, '/'); // Last part of received topic is always the command (type) index = 1; @@ -2742,8 +2755,9 @@ void setup(void) } blink_powersave = power; - snprintf_P(log_data, sizeof(log_data), PSTR(D_PROJECT " %s %s (" D_CMND_TOPIC " %s, " D_FALLBACK " %s, " D_CMND_GROUPTOPIC " %s) " D_VERSION " %s%s-" ARDUINO_ESP8266_RELEASE), - PROJECT, Settings.friendlyname[0], mqtt_topic, mqtt_client, Settings.mqtt_grptopic, my_version, my_image); + char stopic[TOPSZ]; + snprintf_P(log_data, sizeof(log_data), PSTR(D_PROJECT " %s %s " D_VERSION " %s%s-" ARDUINO_ESP8266_RELEASE), + PROJECT, Settings.friendlyname[0], my_version, my_image); AddLog(LOG_LEVEL_INFO); #ifdef BE_MINIMAL snprintf_P(log_data, sizeof(log_data), PSTR(D_WARNING_MINIMAL_VERSION)); diff --git a/sonoff/xdrv_01_webserver.ino b/sonoff/xdrv_01_webserver.ino index bd76b3c0e..046181283 100644 --- a/sonoff/xdrv_01_webserver.ino +++ b/sonoff/xdrv_01_webserver.ino @@ -1323,13 +1323,12 @@ void HandleInformation(void) if (Settings.flag.mqtt_enabled) { func += F("}1" D_MQTT_HOST "}2"); func += Settings.mqtt_host; func += F("}1" D_MQTT_PORT "}2"); func += String(Settings.mqtt_port); - func += F("}1" D_MQTT_CLIENT " &
 " D_FALLBACK_TOPIC "}2"); func += mqtt_client; func += F("}1" D_MQTT_USER "}2"); func += Settings.mqtt_user; + func += F("}1" D_MQTT_CLIENT "}2"); func += mqtt_client; func += F("}1" D_MQTT_TOPIC "}2"); func += Settings.mqtt_topic; func += F("}1" D_MQTT_GROUP_TOPIC "}2"); func += Settings.mqtt_grptopic; - GetTopic_P(stopic, CMND, mqtt_topic, ""); - func += F("}1" D_MQTT_FULL_TOPIC "}2"); func += stopic; - + func += F("}1" D_MQTT_FULL_TOPIC "}2"); func += GetTopic_P(stopic, CMND, mqtt_topic, ""); + func += F("}1" D_MQTT " " D_FALLBACK_TOPIC "}2"); func += GetFallbackTopic_P(stopic, CMND, ""); } else { func += F("}1" D_MQTT "}2" D_DISABLED); } diff --git a/sonoff/xdrv_02_mqtt.ino b/sonoff/xdrv_02_mqtt.ino index a19f80f82..e3f848ce8 100644 --- a/sonoff/xdrv_02_mqtt.ino +++ b/sonoff/xdrv_02_mqtt.ino @@ -406,9 +406,7 @@ void MqttConnected(void) if (strstr(Settings.mqtt_fulltopic, MQTT_TOKEN_TOPIC) != NULL) { GetTopic_P(stopic, CMND, Settings.mqtt_grptopic, PSTR("#")); MqttSubscribe(stopic); - fallback_topic_flag = 1; - GetTopic_P(stopic, CMND, mqtt_client, PSTR("#")); - fallback_topic_flag = 0; + GetFallbackTopic_P(stopic, CMND, PSTR("#")); MqttSubscribe(stopic); } @@ -417,7 +415,8 @@ void MqttConnected(void) if (mqtt_initial_connection_state) { snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_MODULE "\":\"%s\",\"" D_JSON_VERSION "\":\"%s%s\",\"" D_JSON_FALLBACKTOPIC "\":\"%s\",\"" D_CMND_GROUPTOPIC "\":\"%s\"}"), - my_module.name, my_version, my_image, mqtt_client, Settings.mqtt_grptopic); + my_module.name, my_version, my_image, GetFallbackTopic_P(stopic, CMND, ""), Settings.mqtt_grptopic); +// my_module.name, my_version, my_image, mqtt_client, Settings.mqtt_grptopic); MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_INFO "1")); #ifdef USE_WEBSERVER if (Settings.webserver) { diff --git a/tools/decode-config.py b/tools/decode-config.py index b53dbaae9..d1daccf22 100644 --- a/tools/decode-config.py +++ b/tools/decode-config.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -VER = '2.1.0014' +VER = '2.1.0015' """ decode-config.py - Backup/Restore Sonoff-Tasmota configuration data @@ -811,7 +811,11 @@ Setting_6_3_0_16['flag3'][0].update ({ 'button_switch_force_local':(' Date: Mon, 24 Dec 2018 14:36:06 +0100 Subject: [PATCH 003/428] Release 6.4.1 --- RELEASENOTES.md | 3 ++- sonoff/_changelog.ino | 1 + sonoff/xdrv_02_mqtt.ino | 9 ++++----- sonoff/xdrv_19_ps16dz_dimmer.ino | 5 +++-- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 7ce640e57..08114dd2a 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -184,11 +184,12 @@ The following binary downloads have been compiled with ESP8266/Arduino library c | USE_DISPLAY | - | - | - | - | - | - | ## Changelog -Version 6.4.1 20181223 +Version 6.4.1 20181225 * Change RAM usage BMP/BME I2C sensors * Change FallbackTopic from cmnd/\/ to cmnd/\_fb/ to discriminate from Topic (#1528) * Change FallbackTopic detection (#4706) * Change Hass discovery to short MQTT messages as used by Hass 0.81 and up (#4711) + * Change MQTT GUI password handling (#4723) * Fix possible dtostrf buffer overflows by increasing buffers * Fix wifi strongest signal detection (#4704) * Fix Alexa "this value is outside the range of the device". Needs power cycle and Alexa deletion/discovery cycle. (#3159, #4712) diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index d305600c7..158d0c5fe 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -3,6 +3,7 @@ * Change FallbackTopic from cmnd// to cmnd/_fb/ to discriminate from Topic (#1528) * Change FallbackTopic detection (#4706) * Change Hass discovery to short MQTT messages as used by Hass 0.81 and up (#4711) + * Change MQTT GUI password handling (#4723) * Fix possible dtostrf buffer overflows by increasing buffers * Fix wifi strongest signal detection (#4704) * Fix Alexa "this value is outside the range of the device". Needs power cycle and Alexa deletion/discovery cycle. (#3159, #4712) diff --git a/sonoff/xdrv_02_mqtt.ino b/sonoff/xdrv_02_mqtt.ino index e3f848ce8..7ba5cf47a 100644 --- a/sonoff/xdrv_02_mqtt.ino +++ b/sonoff/xdrv_02_mqtt.ino @@ -858,7 +858,7 @@ const char HTTP_FORM_MQTT[] PROGMEM = "
" D_PORT " (" STR(MQTT_PORT) ")

" "
" D_CLIENT " ({m0)

" "
" D_USER " (" MQTT_USER ")

" - "
" D_PASSWORD "

" + "
" D_PASSWORD "

" "
" D_TOPIC " = %topic% (" MQTT_TOPIC ")

" "
" D_FULL_TOPIC " (" MQTT_FULLTOPIC ")

"; @@ -885,7 +885,6 @@ void HandleMqttConfiguration(void) page.replace(F("{m2"), String(Settings.mqtt_port)); page.replace(F("{m3"), Settings.mqtt_client); page.replace(F("{m4"), (Settings.mqtt_user[0] == '\0')?"0":Settings.mqtt_user); - page.replace(F("{m5"), (Settings.mqtt_pwd[0] == '\0')?"0":Settings.mqtt_pwd); page.replace(F("{m6"), Settings.mqtt_topic); page.replace(F("{m7"), Settings.mqtt_fulltopic); @@ -921,9 +920,9 @@ void MqttSaveSettings(void) WebGetArg("mu", tmp, sizeof(tmp)); strlcpy(Settings.mqtt_user, (!strlen(tmp)) ? MQTT_USER : (!strcmp(tmp,"0")) ? "" : tmp, sizeof(Settings.mqtt_user)); WebGetArg("mp", tmp, sizeof(tmp)); - strlcpy(Settings.mqtt_pwd, (!strlen(tmp)) ? MQTT_PASS : (!strcmp(tmp,"0")) ? "" : tmp, sizeof(Settings.mqtt_pwd)); - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_MQTT D_CMND_MQTTHOST " %s, " D_CMND_MQTTPORT " %d, " D_CMND_MQTTCLIENT " %s, " D_CMND_MQTTUSER " %s, " D_CMND_MQTTPASSWORD " %s, " D_CMND_TOPIC " %s, " D_CMND_FULLTOPIC " %s"), - Settings.mqtt_host, Settings.mqtt_port, Settings.mqtt_client, Settings.mqtt_user, Settings.mqtt_pwd, Settings.mqtt_topic, Settings.mqtt_fulltopic); + strlcpy(Settings.mqtt_pwd, (!strlen(tmp)) ? "" : (strchr(tmp,'*')) ? Settings.mqtt_pwd : tmp, sizeof(Settings.mqtt_pwd)); + snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_MQTT D_CMND_MQTTHOST " %s, " D_CMND_MQTTPORT " %d, " D_CMND_MQTTCLIENT " %s, " D_CMND_MQTTUSER " %s, " D_CMND_TOPIC " %s, " D_CMND_FULLTOPIC " %s"), + Settings.mqtt_host, Settings.mqtt_port, Settings.mqtt_client, Settings.mqtt_user, Settings.mqtt_topic, Settings.mqtt_fulltopic); AddLog(LOG_LEVEL_INFO); } #endif // USE_WEBSERVER diff --git a/sonoff/xdrv_19_ps16dz_dimmer.ino b/sonoff/xdrv_19_ps16dz_dimmer.ino index 3c895cf98..7671249eb 100644 --- a/sonoff/xdrv_19_ps16dz_dimmer.ino +++ b/sonoff/xdrv_19_ps16dz_dimmer.ino @@ -159,8 +159,9 @@ void PS16DZSerialInput(void) memset(ps16dz_rx_buffer, 0, PS16DZ_BUFFER_SIZE); ps16dz_byte_counter = 0; } - if (ps16dz_byte_counter || (!ps16dz_byte_counter && serial_in_byte == 'A')); - ps16dz_rx_buffer[ps16dz_byte_counter++] = serial_in_byte; + if (ps16dz_byte_counter || (!ps16dz_byte_counter && serial_in_byte == 'A')) { + ps16dz_rx_buffer[ps16dz_byte_counter++] = serial_in_byte; + } } else { ps16dz_rx_buffer[ps16dz_byte_counter++] = 0x00; From 54c2eb283a02e4287640a4595e506bc6eadbd7f2 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Mon, 24 Dec 2018 16:29:57 +0100 Subject: [PATCH 004/428] Release 6.4.1 --- RELEASENOTES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 08114dd2a..1198a6165 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -168,6 +168,7 @@ The following binary downloads have been compiled with ESP8266/Arduino library c | USE_TUYA_DIMMER | - | x | - | x | x | x | | USE_ARMTRONIX_DIMMERS | - | x | - | x | x | x | | USE_PS_16_DZ | - | x | - | x | x | x | +| USE_AZ7798 | - | - | - | - | - | - | | USE_IR_REMOTE | - | - | - | x | x | x | | USE_IR_HVAC | - | - | - | - | - | x | | USE_IR_RECEIVE | - | - | - | x | x | x | From d85278f1b2e556acd0a6cfb09cc527f52cde9360 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Wed, 13 Mar 2019 18:00:15 +0100 Subject: [PATCH 005/428] Prep for release 6.5 Prep for release 6.5 --- .github/ISSUE_TEMPLATE/Bug_report.md | 50 +- .github/ISSUE_TEMPLATE/Custom.md | 39 +- .github/PULL_REQUEST_TEMPLATE.md | 10 + .github/stale.yml | 6 +- API.md | 2 + CODE_OF_CONDUCT.md | 76 + CONTRIBUTING.md | 63 + README.md | 11 +- RELEASENOTES.md | 91 +- SUPPORT.md | 22 + TEMPLATE.md | 86 + arduino/version 2.5.0/boards.txt | 303 +-- arduino/version 2.5.0/platform.txt | 31 +- lib/ArduinoJson-5.11.2/ArduinoJson.h | 8 - lib/ArduinoJson-5.11.2/README.md | 130 -- .../JsonHttpClient/JsonHttpClient.ino | 184 -- .../examples/JsonServer/JsonServer.ino | 76 - .../examples/JsonUdpBeacon/JsonUdpBeacon.ino | 57 - lib/ArduinoJson-5.11.2/library.properties | 9 - lib/ArduinoJson-5.11.2/src/ArduinoJson.h | 12 - .../src/ArduinoJson/Data/ValueSetter.hpp | 51 - .../src/ArduinoJson/Polyfills/isInteger.hpp | 22 - .../src/ArduinoJson/RawJson.hpp | 23 - .../TypeTraits/IsSignedIntegral.hpp | 33 - .../TypeTraits/IsUnsignedIntegral.hpp | 33 - lib/ArduinoJson-5.13.4/ArduinoJson.h | 5 + .../CHANGELOG.md | 317 ++- .../LICENSE.md | 2 +- lib/ArduinoJson-5.13.4/README.md | 110 + .../JsonConfigFile/JsonConfigFile.ino | 144 ++ .../JsonGeneratorExample.ino | 29 +- .../JsonHttpClient/JsonHttpClient.ino | 112 + .../JsonParserExample/JsonParserExample.ino | 31 +- .../examples/JsonServer/JsonServer.ino | 109 + .../examples/JsonUdpBeacon/JsonUdpBeacon.ino | 101 + .../ProgmemExample/ProgmemExample.ino | 38 +- .../examples/StringExample/StringExample.ino | 33 +- .../keywords.txt | 0 lib/ArduinoJson-5.13.4/library.properties | 11 + lib/ArduinoJson-5.13.4/src/ArduinoJson.h | 17 + .../src/ArduinoJson.hpp | 9 +- .../src/ArduinoJson/Configuration.hpp | 10 +- .../src/ArduinoJson/Data/Encoding.hpp | 7 +- .../ArduinoJson/Data/JsonBufferAllocated.hpp | 7 +- .../src/ArduinoJson/Data/JsonFloat.hpp | 7 +- .../src/ArduinoJson/Data/JsonInteger.hpp | 7 +- .../src/ArduinoJson/Data/JsonVariantAs.hpp | 7 +- .../ArduinoJson/Data/JsonVariantContent.hpp | 7 +- .../ArduinoJson/Data/JsonVariantDefault.hpp | 7 +- .../src/ArduinoJson/Data/JsonVariantType.hpp | 7 +- .../src/ArduinoJson/Data/List.hpp | 7 +- .../ArduinoJson/Data/ListConstIterator.hpp | 7 +- .../src/ArduinoJson/Data/ListIterator.hpp | 7 +- .../src/ArduinoJson/Data/ListNode.hpp | 7 +- .../src/ArduinoJson/Data/NonCopyable.hpp | 7 +- .../src/ArduinoJson/Data/ReferenceType.hpp | 7 +- .../src/ArduinoJson/Data/ValueSaver.hpp | 52 + .../ArduinoJson/Deserialization/Comments.hpp | 7 +- .../Deserialization/JsonParser.hpp | 29 +- .../Deserialization/JsonParserImpl.hpp | 35 +- .../Deserialization/StringWriter.hpp | 7 +- .../src/ArduinoJson/DynamicJsonBuffer.hpp | 12 +- .../src/ArduinoJson/JsonArray.hpp | 48 +- .../src/ArduinoJson/JsonArrayImpl.hpp | 7 +- .../src/ArduinoJson/JsonArraySubscript.hpp | 61 +- .../src/ArduinoJson/JsonBuffer.hpp | 24 +- .../src/ArduinoJson/JsonBufferBase.hpp | 21 +- .../src/ArduinoJson/JsonBufferImpl.hpp | 7 +- .../src/ArduinoJson/JsonObject.hpp | 195 +- .../src/ArduinoJson/JsonObjectImpl.hpp | 7 +- .../src/ArduinoJson/JsonObjectSubscript.hpp | 41 +- .../src/ArduinoJson/JsonPair.hpp | 7 +- .../src/ArduinoJson/JsonVariant.hpp | 115 +- .../src/ArduinoJson/JsonVariantBase.hpp | 15 +- .../src/ArduinoJson/JsonVariantCasts.hpp | 9 +- .../ArduinoJson/JsonVariantComparisons.hpp | 41 +- .../src/ArduinoJson/JsonVariantImpl.hpp | 15 +- .../src/ArduinoJson/JsonVariantOr.hpp | 52 + .../src/ArduinoJson/JsonVariantSubscripts.hpp | 35 +- .../src/ArduinoJson/Polyfills/attributes.hpp | 9 +- .../src/ArduinoJson/Polyfills/ctype.hpp | 9 +- .../src/ArduinoJson/Polyfills/isFloat.hpp | 9 +- .../src/ArduinoJson/Polyfills/isInteger.hpp | 19 + .../src/ArduinoJson/Polyfills/math.hpp | 9 +- .../src/ArduinoJson/Polyfills/parseFloat.hpp | 11 +- .../ArduinoJson/Polyfills/parseInteger.hpp | 9 +- .../src/ArduinoJson/RawJson.hpp | 46 + .../ArduinoJson/Serialization/DummyPrint.hpp | 7 +- .../Serialization/DynamicStringBuilder.hpp | 7 +- .../ArduinoJson/Serialization/FloatParts.hpp | 9 +- .../Serialization/IndentedPrint.hpp | 7 +- .../Serialization/JsonPrintable.hpp | 21 +- .../Serialization/JsonSerializer.hpp | 14 +- .../Serialization/JsonSerializerImpl.hpp | 7 +- .../ArduinoJson/Serialization/JsonWriter.hpp | 11 +- .../ArduinoJson/Serialization/Prettyfier.hpp | 7 +- .../Serialization/StaticStringBuilder.hpp | 7 +- .../Serialization/StreamPrintAdapter.hpp | 7 +- .../src/ArduinoJson/StaticJsonBuffer.hpp | 14 +- .../StringTraits/ArduinoStream.hpp | 20 +- .../ArduinoJson/StringTraits/CharPointer.hpp | 32 +- .../ArduinoJson/StringTraits/FlashString.hpp | 25 +- .../ArduinoJson/StringTraits/StdStream.hpp | 20 +- .../ArduinoJson/StringTraits/StdString.hpp | 27 +- .../ArduinoJson/StringTraits/StringTraits.hpp | 28 +- .../src/ArduinoJson/TypeTraits/EnableIf.hpp | 9 +- .../ArduinoJson/TypeTraits/FloatTraits.hpp | 54 +- .../src/ArduinoJson/TypeTraits/IsArray.hpp | 9 +- .../src/ArduinoJson/TypeTraits/IsBaseOf.hpp | 9 +- .../src/ArduinoJson/TypeTraits/IsChar.hpp | 9 +- .../src/ArduinoJson/TypeTraits/IsConst.hpp | 9 +- .../TypeTraits/IsFloatingPoint.hpp | 9 +- .../src/ArduinoJson/TypeTraits/IsIntegral.hpp | 15 +- .../src/ArduinoJson/TypeTraits/IsSame.hpp | 9 +- .../TypeTraits/IsSignedIntegral.hpp | 28 + .../TypeTraits/IsUnsignedIntegral.hpp | 28 + .../src/ArduinoJson/TypeTraits/IsVariant.hpp | 9 +- .../ArduinoJson/TypeTraits/RemoveConst.hpp | 9 +- .../TypeTraits/RemoveReference.hpp | 9 +- .../src/ArduinoJson/version.hpp | 10 + lib/FrogmoreScd30/FrogmoreScd30.cpp | 653 ++++++ lib/FrogmoreScd30/FrogmoreScd30.h | 105 + lib/LinkedList-1.2.3/LICENSE.txt | 21 + lib/LinkedList-1.2.3/LinkedList.h | 325 +++ lib/LinkedList-1.2.3/README.md | 171 ++ .../examples/ClassList/ClassList.pde | 81 + .../SimpleIntegerList/SimpleIntegerList.pde | 58 + lib/LinkedList-1.2.3/keywords.txt | 28 + lib/LinkedList-1.2.3/library.json | 12 + lib/LinkedList-1.2.3/library.properties | 9 + lib/NewPing-1.9.1/README.md | 2 +- .../NewPing15SensorsTimer.pde | 2 +- .../NewPing3Sensors/NewPing3Sensors.pde | 8 +- .../NewPingTimerMedian/NewPingTimerMedian.pde | 2 +- .../examples/TimerExample/TimerExample.pde | 2 +- lib/NewPing-1.9.1/keywords.txt | 1 - lib/NewPing-1.9.1/src/NewPing.cpp | 2 +- lib/NewPing-1.9.1/src/NewPing.h | 6 +- lib/TasmotaModbus-1.1.0/src/TasmotaModbus.cpp | 1 - .../README.md | 0 .../examples/swsertest/swsertest.ino | 0 .../keywords.txt | 1 + .../library.json | 2 +- .../library.properties | 2 +- .../src/TasmotaSerial.cpp | 8 +- .../src/TasmotaSerial.h | 4 +- sonoff/Parsing.cpp | 623 ++++++ sonoff/_changelog.ino | 140 +- sonoff/core_esp8266_timer.c | 10 +- sonoff/core_esp8266_wiring_digital.c | 10 +- sonoff/core_esp8266_wiring_pwm.c | 10 +- sonoff/i18n.h | 35 +- sonoff/language/bg-BG.h | 160 +- sonoff/language/cs-CZ.h | 158 +- sonoff/language/de-DE.h | 160 +- sonoff/language/el-GR.h | 158 +- sonoff/language/en-GB.h | 158 +- sonoff/language/es-AR.h | 160 +- sonoff/language/fr-FR.h | 182 +- sonoff/language/he-HE.h | 158 +- sonoff/language/hu-HU.h | 612 +++--- sonoff/language/it-IT.h | 158 +- sonoff/language/ko-KO.h | 647 ++++++ sonoff/language/nl-NL.h | 158 +- sonoff/language/pl-PL.h | 184 +- sonoff/language/pt-BR.h | 158 +- sonoff/language/pt-PT.h | 158 +- sonoff/language/ru-RU.h | 158 +- sonoff/language/sk-SK.h | 158 +- sonoff/language/sv-SE.h | 210 +- sonoff/language/tr-TR.h | 158 +- sonoff/language/uk-UK.h | 158 +- sonoff/language/zh-CN.h | 256 ++- sonoff/language/zh-TW.h | 158 +- sonoff/my_user_config.h | 38 +- sonoff/settings.h | 143 +- sonoff/settings.ino | 301 ++- sonoff/sonoff.h | 21 +- sonoff/sonoff.ino | 1131 +++++----- sonoff/sonoff_letsencrypt.h | 2 +- sonoff/sonoff_post.h | 58 +- sonoff/sonoff_template.h | 1154 ++++++++-- sonoff/sonoff_version.h | 4 +- sonoff/support.ino | 409 ++-- sonoff/support_button.ino | 242 ++ sonoff/support_features.ino | 36 +- sonoff/support_rotary.ino | 151 ++ sonoff/support_rtc.ino | 101 +- sonoff/support_switch.ino | 221 ++ sonoff/support_wifi.ino | 95 +- sonoff/user_config_override_sample.h | 8 +- sonoff/xdrv_01_webserver.ino | 1951 ++++++++++------- sonoff/xdrv_02_mqtt.ino | 249 ++- sonoff/xdrv_03_energy.ino | 122 +- sonoff/xdrv_04_light.ino | 320 ++- sonoff/xdrv_05_irremote.ino | 112 +- sonoff/xdrv_06_snfbridge.ino | 30 +- sonoff/xdrv_07_domoticz.ino | 126 +- sonoff/xdrv_08_serial_bridge.ino | 95 +- sonoff/xdrv_09_timers.ino | 157 +- sonoff/xdrv_10_rules.ino | 742 ++++++- sonoff/xdrv_11_knx.ino | 397 ++-- sonoff/xdrv_12_home_assistant.ino | 307 +-- sonoff/xdrv_13_display.ino | 71 +- sonoff/xdrv_14_mp3.ino | 10 +- sonoff/xdrv_15_pca9685.ino | 15 +- sonoff/xdrv_16_tuyadimmer.ino | 65 +- sonoff/xdrv_17_rcswitch.ino | 19 +- sonoff/xdrv_18_armtronix_dimmers.ino | 33 +- sonoff/xdrv_19_ps16dz_dimmer.ino | 46 +- sonoff/xdrv_99_debug.ino | 516 +++++ sonoff/xdrv_interface.ino | 46 +- sonoff/xdsp_01_lcd.ino | 32 +- sonoff/xdsp_02_ssd1306.ino | 11 +- sonoff/xdsp_03_matrix.ino | 30 +- sonoff/xdsp_04_ili9341.ino | 13 +- sonoff/xdsp_05_epaper_29.ino | 20 +- sonoff/xdsp_interface.ino | 21 +- sonoff/xnrg_01_hlw8012.ino | 129 +- sonoff/xnrg_02_cse7766.ino | 20 +- sonoff/xnrg_03_pzem004t.ino | 10 +- sonoff/xnrg_04_mcp39f501.ino | 189 +- sonoff/xnrg_05_pzem_ac.ino | 9 +- sonoff/xnrg_06_pzem_dc.ino | 9 +- sonoff/xnrg_interface.ino | 21 +- sonoff/xplg_wemohue.ino | 62 +- sonoff/xplg_ws2812.ino | 80 +- sonoff/xsns_01_counter.ino | 25 +- sonoff/xsns_02_analog.ino | 12 +- sonoff/xsns_04_snfsc.ino | 18 +- sonoff/xsns_05_ds18b20.ino | 24 +- sonoff/xsns_05_ds18x20.ino | 95 +- sonoff/xsns_05_ds18x20_legacy.ino | 32 +- sonoff/xsns_06_dht.ino | 35 +- sonoff/xsns_07_sht1x.ino | 24 +- sonoff/xsns_08_htu21.ino | 13 +- sonoff/xsns_09_bmp.ino | 27 +- sonoff/xsns_10_bh1750.ino | 17 +- sonoff/xsns_11_veml6070.ino | 27 +- sonoff/xsns_12_ads1115.ino | 106 +- sonoff/xsns_12_ads1115_i2cdev.ino | 19 +- sonoff/xsns_13_ina219.ino | 17 +- sonoff/xsns_14_sht3x.ino | 15 +- sonoff/xsns_15_mhz19.ino | 82 +- sonoff/xsns_16_tsl2561.ino | 21 +- sonoff/xsns_17_senseair.ino | 23 +- sonoff/xsns_18_pms5003.ino | 12 +- sonoff/xsns_19_mgs.ino | 13 +- sonoff/xsns_20_novasds.ino | 23 +- sonoff/xsns_21_sgp30.ino | 17 +- sonoff/xsns_22_sr04.ino | 12 +- sonoff/xsns_23_sdm120.ino | 11 +- sonoff/xsns_24_si1145.ino | 15 +- sonoff/xsns_25_sdm630.ino | 11 +- sonoff/xsns_26_lm75ad.ino | 13 +- sonoff/xsns_27_apds9960.ino | 39 +- sonoff/xsns_28_tm1638.ino | 34 +- sonoff/xsns_29_mcp230xx.ino | 38 +- sonoff/xsns_30_mpr121.ino | 34 +- sonoff/xsns_31_ccs811.ino | 14 +- sonoff/xsns_32_mpu6050.ino | 17 +- sonoff/xsns_33_ds3231.ino | 47 +- sonoff/xsns_34_hx711.ino | 81 +- sonoff/xsns_35_tx20.ino | 10 +- sonoff/xsns_36_mgc3130.ino | 20 +- sonoff/xsns_37_rfsensor.ino | 55 +- sonoff/xsns_38_az7798.ino | 14 +- sonoff/xsns_39_max31855.ino | 176 ++ sonoff/xsns_40_pn532.ino | 606 +++++ sonoff/xsns_41_max44009.ino | 179 ++ sonoff/xsns_42_scd30.ino | 494 +++++ sonoff/xsns_interface.ino | 26 +- sonoff/zzzz_debug.ino | 309 +++ tools/decode-config.html | 39 +- tools/decode-config.md | 42 +- tools/decode-config.py | 180 +- tools/decode-status.py | 99 +- tools/fw_server/fw-server.py | 22 +- 278 files changed, 16830 insertions(+), 7725 deletions(-) create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 CODE_OF_CONDUCT.md create mode 100644 CONTRIBUTING.md create mode 100644 SUPPORT.md create mode 100644 TEMPLATE.md delete mode 100644 lib/ArduinoJson-5.11.2/ArduinoJson.h delete mode 100644 lib/ArduinoJson-5.11.2/README.md delete mode 100644 lib/ArduinoJson-5.11.2/examples/JsonHttpClient/JsonHttpClient.ino delete mode 100644 lib/ArduinoJson-5.11.2/examples/JsonServer/JsonServer.ino delete mode 100644 lib/ArduinoJson-5.11.2/examples/JsonUdpBeacon/JsonUdpBeacon.ino delete mode 100644 lib/ArduinoJson-5.11.2/library.properties delete mode 100644 lib/ArduinoJson-5.11.2/src/ArduinoJson.h delete mode 100644 lib/ArduinoJson-5.11.2/src/ArduinoJson/Data/ValueSetter.hpp delete mode 100644 lib/ArduinoJson-5.11.2/src/ArduinoJson/Polyfills/isInteger.hpp delete mode 100644 lib/ArduinoJson-5.11.2/src/ArduinoJson/RawJson.hpp delete mode 100644 lib/ArduinoJson-5.11.2/src/ArduinoJson/TypeTraits/IsSignedIntegral.hpp delete mode 100644 lib/ArduinoJson-5.11.2/src/ArduinoJson/TypeTraits/IsUnsignedIntegral.hpp create mode 100644 lib/ArduinoJson-5.13.4/ArduinoJson.h rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/CHANGELOG.md (68%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/LICENSE.md (96%) create mode 100644 lib/ArduinoJson-5.13.4/README.md create mode 100644 lib/ArduinoJson-5.13.4/examples/JsonConfigFile/JsonConfigFile.ino rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/examples/JsonGeneratorExample/JsonGeneratorExample.ino (63%) create mode 100644 lib/ArduinoJson-5.13.4/examples/JsonHttpClient/JsonHttpClient.ino rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/examples/JsonParserExample/JsonParserExample.ino (61%) create mode 100644 lib/ArduinoJson-5.13.4/examples/JsonServer/JsonServer.ino create mode 100644 lib/ArduinoJson-5.13.4/examples/JsonUdpBeacon/JsonUdpBeacon.ino rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/examples/ProgmemExample/ProgmemExample.ino (52%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/examples/StringExample/StringExample.ino (67%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/keywords.txt (100%) create mode 100644 lib/ArduinoJson-5.13.4/library.properties create mode 100644 lib/ArduinoJson-5.13.4/src/ArduinoJson.h rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson.hpp (75%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/Configuration.hpp (94%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/Data/Encoding.hpp (80%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/Data/JsonBufferAllocated.hpp (68%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/Data/JsonFloat.hpp (56%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/Data/JsonInteger.hpp (70%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/Data/JsonVariantAs.hpp (80%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/Data/JsonVariantContent.hpp (79%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/Data/JsonVariantDefault.hpp (68%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/Data/JsonVariantType.hpp (84%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/Data/List.hpp (93%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/Data/ListConstIterator.hpp (85%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/Data/ListIterator.hpp (86%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/Data/ListNode.hpp (70%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/Data/NonCopyable.hpp (67%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/Data/ReferenceType.hpp (74%) create mode 100644 lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/ValueSaver.hpp rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/Deserialization/Comments.hpp (88%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/Deserialization/JsonParser.hpp (77%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/Deserialization/JsonParserImpl.hpp (85%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/Deserialization/StringWriter.hpp (79%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/DynamicJsonBuffer.hpp (94%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/JsonArray.hpp (82%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/JsonArrayImpl.hpp (75%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/JsonArraySubscript.hpp (69%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/JsonBuffer.hpp (74%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/JsonBufferBase.hpp (88%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/JsonBufferImpl.hpp (72%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/JsonObject.hpp (57%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/JsonObjectImpl.hpp (79%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/JsonObjectSubscript.hpp (66%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/JsonPair.hpp (53%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/JsonVariant.hpp (72%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/JsonVariantBase.hpp (59%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/JsonVariantCasts.hpp (89%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/JsonVariantComparisons.hpp (75%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/JsonVariantImpl.hpp (87%) create mode 100644 lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonVariantOr.hpp rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/JsonVariantSubscripts.hpp (70%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/Polyfills/attributes.hpp (74%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/Polyfills/ctype.hpp (50%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/Polyfills/isFloat.hpp (75%) create mode 100644 lib/ArduinoJson-5.13.4/src/ArduinoJson/Polyfills/isInteger.hpp rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/Polyfills/math.hpp (52%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/Polyfills/parseFloat.hpp (89%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/Polyfills/parseInteger.hpp (75%) create mode 100644 lib/ArduinoJson-5.13.4/src/ArduinoJson/RawJson.hpp rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/Serialization/DummyPrint.hpp (64%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/Serialization/DynamicStringBuilder.hpp (80%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/Serialization/FloatParts.hpp (91%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/Serialization/IndentedPrint.hpp (89%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/Serialization/JsonPrintable.hpp (80%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/Serialization/JsonSerializer.hpp (79%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/Serialization/JsonSerializerImpl.hpp (94%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/Serialization/JsonWriter.hpp (91%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/Serialization/Prettyfier.hpp (93%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/Serialization/StaticStringBuilder.hpp (77%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/Serialization/StreamPrintAdapter.hpp (77%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/StaticJsonBuffer.hpp (89%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/StringTraits/ArduinoStream.hpp (64%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/StringTraits/CharPointer.hpp (54%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/StringTraits/FlashString.hpp (64%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/StringTraits/StdStream.hpp (63%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/StringTraits/StdString.hpp (70%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/StringTraits/StringTraits.hpp (55%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/TypeTraits/EnableIf.hpp (58%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/TypeTraits/FloatTraits.hpp (68%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/TypeTraits/IsArray.hpp (67%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/TypeTraits/IsBaseOf.hpp (73%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/TypeTraits/IsChar.hpp (69%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/TypeTraits/IsConst.hpp (61%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/TypeTraits/IsFloatingPoint.hpp (60%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/TypeTraits/IsIntegral.hpp (50%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/TypeTraits/IsSame.hpp (62%) create mode 100644 lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/IsSignedIntegral.hpp create mode 100644 lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/IsUnsignedIntegral.hpp rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/TypeTraits/IsVariant.hpp (50%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/TypeTraits/RemoveConst.hpp (59%) rename lib/{ArduinoJson-5.11.2 => ArduinoJson-5.13.4}/src/ArduinoJson/TypeTraits/RemoveReference.hpp (60%) create mode 100644 lib/ArduinoJson-5.13.4/src/ArduinoJson/version.hpp create mode 100644 lib/FrogmoreScd30/FrogmoreScd30.cpp create mode 100644 lib/FrogmoreScd30/FrogmoreScd30.h create mode 100644 lib/LinkedList-1.2.3/LICENSE.txt create mode 100644 lib/LinkedList-1.2.3/LinkedList.h create mode 100644 lib/LinkedList-1.2.3/README.md create mode 100644 lib/LinkedList-1.2.3/examples/ClassList/ClassList.pde create mode 100644 lib/LinkedList-1.2.3/examples/SimpleIntegerList/SimpleIntegerList.pde create mode 100644 lib/LinkedList-1.2.3/keywords.txt create mode 100644 lib/LinkedList-1.2.3/library.json create mode 100644 lib/LinkedList-1.2.3/library.properties rename lib/{TasmotaSerial-2.2.0 => TasmotaSerial-2.3.0}/README.md (100%) rename lib/{TasmotaSerial-2.2.0 => TasmotaSerial-2.3.0}/examples/swsertest/swsertest.ino (100%) rename lib/{TasmotaSerial-2.2.0 => TasmotaSerial-2.3.0}/keywords.txt (95%) rename lib/{TasmotaSerial-2.2.0 => TasmotaSerial-2.3.0}/library.json (94%) rename lib/{TasmotaSerial-2.2.0 => TasmotaSerial-2.3.0}/library.properties (94%) rename lib/{TasmotaSerial-2.2.0 => TasmotaSerial-2.3.0}/src/TasmotaSerial.cpp (97%) rename lib/{TasmotaSerial-2.2.0 => TasmotaSerial-2.3.0}/src/TasmotaSerial.h (96%) create mode 100644 sonoff/Parsing.cpp create mode 100644 sonoff/language/ko-KO.h create mode 100644 sonoff/support_button.ino create mode 100644 sonoff/support_rotary.ino create mode 100644 sonoff/support_switch.ino create mode 100644 sonoff/xdrv_99_debug.ino create mode 100644 sonoff/xsns_39_max31855.ino create mode 100644 sonoff/xsns_40_pn532.ino create mode 100644 sonoff/xsns_41_max44009.ino create mode 100644 sonoff/xsns_42_scd30.ino create mode 100644 sonoff/zzzz_debug.ino diff --git a/.github/ISSUE_TEMPLATE/Bug_report.md b/.github/ISSUE_TEMPLATE/Bug_report.md index 2616af48f..a0ab5e7e3 100644 --- a/.github/ISSUE_TEMPLATE/Bug_report.md +++ b/.github/ISSUE_TEMPLATE/Bug_report.md @@ -4,31 +4,61 @@ about: Create a report to help us improve --- -**Describe the bug** + + + + + + + + + + + +### BUG DESCRIPTION _A clear and concise description of what the bug is._ -_Also, make sure these boxes are checked [x] before submitting your issue - Thank you!_ -- [ ] _Searched the problem in issues and in the wiki_ -- [ ] _Hardware used_ : -- [ ] _Provide the output of command_``status 0`` : +### REQUESTED INFORMATION +_Make sure these boxes are checked before submitting your issue. Thank you_ + +**FAILURE TO COMPLETE THE REQUESTED INFORMATION WILL RESULT IN YOUR ISSUE BEING CLOSED** + +- [ ] Read the [Contributing Guide and Policy](https://github.com/arendst/Sonoff-Tasmota/blob/development/CONTRIBUTING.md) and [the Code of Conduct](https://github.com/arendst/Sonoff-Tasmota/blob/development/CODE_OF_CONDUCT.md) +- [ ] Searched the problem in issues (https://github.com/arendst/Sonoff-Tasmota/issues) +- [ ] Searched the problem in the wiki (https://github.com/arendst/Sonoff-Tasmota/wiki/Troubleshooting) +- [ ] Searched the problem in the forum (https://groups.google.com/d/forum/sonoffusers) +- [ ] Searched the problem in the chat (https://discord.gg/Ks2Kzd4) +- [ ] Device used (i.e. Sonoff Basic) : _____ +- [ ] Tasmota binary firmware version number used : ____ / (pre-compiled or self-compiled ?) +- [ ] Development IDE - Compiler / Upload tools used : ____ / ____ +- [ ] Provide the output of command ``status 0`` : ``` -STATUS 0 OUTPUT HERE +STATUS 0 OUTPUT HERE: + + +``` +- [ ] Provide the output of console when you experience your issue if apply : +_(Please use_ ``weblog 4`` _for more debug information)_ +``` +CONSOLE OUTPUT HERE: + + ``` -**To Reproduce** +### TO REPRODUCE _Steps to reproduce the behavior:_ -**Expected behavior** +### EXPECTED BEHAVIOUR _A clear and concise description of what you expected to happen._ -**Screenshots** +### SCREENSHOTS _If applicable, add screenshots to help explain your problem._ -**Additional context** +### ADDITIONAL CONTEXT _Add any other context about the problem here._ diff --git a/.github/ISSUE_TEMPLATE/Custom.md b/.github/ISSUE_TEMPLATE/Custom.md index 159549128..44bd847cb 100644 --- a/.github/ISSUE_TEMPLATE/Custom.md +++ b/.github/ISSUE_TEMPLATE/Custom.md @@ -4,16 +4,47 @@ about: Users Troubleshooting Help --- -Make sure these boxes are checked [x] before submitting your issue - Thank you! + + + + + + + + + + + + +### ISSUE DESCRIPTION - TROUBLESHOOTING +_A clear description of what the issue is and be as extensive as possible_ + + +### REQUESTED INFORMATION +_Make sure these boxes are checked before submitting your issue. Thank you_ + +**FAILURE TO COMPLETE THE REQUESTED INFORMATION WILL RESULT IN YOUR ISSUE BEING CLOSED** + +- [ ] Read the [Contributing Guide and Policy](https://github.com/arendst/Sonoff-Tasmota/blob/development/CONTRIBUTING.md) and [the Code of Conduct](https://github.com/arendst/Sonoff-Tasmota/blob/development/CODE_OF_CONDUCT.md) - [ ] Searched the problem in issues (https://github.com/arendst/Sonoff-Tasmota/issues) - [ ] Searched the problem in the wiki (https://github.com/arendst/Sonoff-Tasmota/wiki/Troubleshooting) - [ ] Searched the problem in the forum (https://groups.google.com/d/forum/sonoffusers) - [ ] Searched the problem in the chat (https://discord.gg/Ks2Kzd4) -- [ ] Hardware used : +- [ ] Device used (i.e. Sonoff Basic) : _____ +- [ ] Tasmota binary firmware version number used : ____ / (pre-compiled or self-compiled ?) +- [ ] Development IDE - Compiler / Upload tools used : ____ / ____ - [ ] Provide the output of command ``status 0`` : ``` -STATUS 0 OUTPUT HERE -``` +STATUS 0 OUTPUT HERE: + +``` +- [ ] Provide the output of console when you experience your issue if apply : +_(Please use_ ``weblog 4`` _for more debug information)_ +``` +CONSOLE OUTPUT HERE: + + +``` **(Please, remember to close the issue when the problem has been addressed)** diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000..aea7cd816 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,10 @@ +## Description: + +**Related issue (if applicable):** fixes # + +## Checklist: + - [ ] The pull request is done against the dev branch + - [ ] Only relevant files were touched (Also beware if your editor has auto-formatting feature enabled) + - [ ] Only one feature/fix was added per PR. + - [ ] The code change is tested and works. + - [ ] The code change pass travis tests. **Your PR cannot be merged unless tests pass** diff --git a/.github/stale.yml b/.github/stale.yml index 88c5f7c21..484010321 100644 --- a/.github/stale.yml +++ b/.github/stale.yml @@ -1,9 +1,9 @@ # Number of days of inactivity before an Issue or Pull Request becomes stale -daysUntilStale: 45 +daysUntilStale: 25 # Number of days of inactivity before a stale Issue or Pull Request is closed. # Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale. -daysUntilClose: 15 +daysUntilClose: 5 # Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable exemptLabels: @@ -37,4 +37,4 @@ closeComment: > limitPerRun: 30 # Limit to only `issues` or `pulls` -only: issues +#only: issues diff --git a/API.md b/API.md index 23e45369d..caec908c6 100644 --- a/API.md +++ b/API.md @@ -23,6 +23,8 @@ FUNC_JSON_APPEND | | | | x | | Extend tele FUNC_WEB_APPEND | | | | x | | Extend webgui ajax info FUNC_SAVE_BEFORE_RESTART | | | | x | | Just before a planned restart FUNC_COMMAND | x | | x | x | | When a command is not recognized +FUNC_COMMAND_DRIVER | x | 6.4.1.21 | x | | | When command Driver\ is executed +FUNC_COMMAND_SENSOR | x | 6.4.1.21 | | x | | When command Sensor\ is executed FUNC_MQTT_SUBSCRIBE | | 5.12.0k | x | | | At end of MQTT subscriptions FUNC_MQTT_INIT | | 5.12.0k | x | | | Once at end of MQTT connection FUNC_MQTT_DATA | x | 5.12.0k | x | | | Before decoding command diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..1408ce048 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,76 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, sex characteristics, gender identity and expression, +level of experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at https://sidweb.nl/cms3/en/contact. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see +https://www.contributor-covenant.org/faq diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..7cfcdfadb --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,63 @@ +# Contributing to Sonoff-Tasmota + +**Any contribution helps our team and makes Tasmota better for the entire community!** + +Everybody is welcome and invited to contribute to Sonoff-Tasmota Project by: + +* Testing newly released features and reporting issues. +* Providing Pull Requests (Features, Proof of Concepts, Language files or Fixes) +* Contributing missing documentation for features and devices on our [Wiki](https://github.com/arendst/Sonoff-Tasmota/wiki/Contributing) + +This document describes rules that are in effect for this repository, meant for handling issues by contributors in the issue tracker and PRs. + +## Opening New Issues + +**Issue tracker is NOT a general discussion forum!** +1. Opening an issue means that a problem exists in the code and should be addressed by the project contributors. +2. When opening an issue, it is required to fill out the presented template. The requested information is important! If the template is ignored or insufficient info about the issue is provided, the issue may be closed. +3. Questions of type "How do I..." or "Can you please help me with..." or "Can Tasmota do..." WILL NOT be handled here. Such questions should be directed at a discussion forum or to the Tasmota Support Chat. All issues of this type will be closed with a simple reference to this contributing policy. +4. Issues about topics already handled in the documentation will be closed in a similar manner. +5. Issues for unmerged PRs will be closed. If there is an issue with a PR, the explanation should be added to the PR itself. +6. Issues with accompanied investigation that shows the root of the problem should be given priority. +7. Duplicate issues will be closed. + +## Triaging of Issues/PR's + +1. Any contributor to the project can participate in the triaging process, if he/she chooses to do so. +2. An issue that needs to be closed, either due to not complying with this policy, or for other reasons, should be closed by a contributor. +3. Issues that are accepted should be marked with appropriate labels. +4. Issues that could impact functionality for many users should be considered severe. +5. Issues caused by the SDK or chip should not be marked severe, as there usually isn’t much to be done. Common sense should be applied when deciding. Such issues should be documented in the Wiki, for reference by users. +6. Issues with feature requests should be discussed for viability/desirability. +7. Feature requests or changes that are meant to address a very specific/limited use case, especially if at the expense of increased code complexity, may be denied, or may be required to be redesigned, generalized, or simplified. +8. Feature requests that are not accompanied by a PR: + * could be closed immediately (denied). + * could be closed after some predetermined period of time (left as candidate for somebody to pick up). +9. In some cases, feedback may be requested from the issue reporter, either as additional info for clarification, additional testing, or other. If no feedback is provided, the issue may be closed by a contributor or after 30 days by the STALE bot. + +## Pull requests + +A Pull Request (PR) is the process where code modifications are managed in GitHub. + +The process is straight-forward. + + - Read [How to get faster PR reviews](https://github.com/kubernetes/community/blob/master/contributors/guide/pull-requests.md#best-practices-for-faster-reviews) by Kubernetes (but skip step 0) + - Fork the Sonoff-Tasmota Repository [git repository](https://github.com/arendst/Sonoff-Tasmota). + - Write/Change the code in your Fork for a new feature, bug fix, new sensor, optimization, etc. + - Ensure tests work. + - Create a Pull Request against the [**dev**](https://github.com/arendst/Sonoff-Tasmota/tree/dev) branch of Sonoff-Tasmota. + +1. All pull requests must be done against the dev branch. +2. Only relevant files should be touched (Also beware if your editor has auto-formatting feature enabled). +3. Only one feature/fix should be added per PR. +4. If adding a new functionality (new hardware, new library support) not related to an existing component move it to it's own modules (.ino file). +5. PRs that don't compile (break Travis) or cause coding errors will not be merged. Please fix the issue. Same goes for PRs that are raised against older commit in dev - you might need to rebase and resolve conflicts. +6. All pull requests should undergo peer review by at least one contributor other than the creator, excepts for the owner. +7. All pull requests should consider updates to the documentation. +8. Pull requests that address an outstanding issue, particularly an issue deemed to be severe, should be given priority. +9. If a PR is accepted, then it should undergo review and updated based on the feedback provided, then merged. +10. Pull requests that don't meet the above will be denied and closed. + + + + diff --git a/README.md b/README.md index a59f0b69c..b2cf7500c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ## Sonoff-Tasmota -Alternative firmware for _ESP8266 based devices_ like [iTead](https://www.itead.cc/) _**Sonoff**_ with **web**, **timers**, 'Over The Air' (**OTA**) firmware updates and **sensors support**, allowing control under **Serial**, **HTTP**, **MQTT** and **KNX**, so as to be used on **Smart Home Systems**. Written for Arduino IDE and PlatformIO. +Alternative firmware for _ESP8266 based devices_ like [iTead](https://www.itead.cc/) _**Sonoff**_ with **web UI, rules and timers, OTA updates, custome device templates and sensors support**. Allows control over **MQTT**, **HTTP**, **Serial** and **KNX** for integrations with smart home systems. Written for Arduino IDE and PlatformIO. [![GitHub version](https://img.shields.io/github/release/arendst/Sonoff-Tasmota.svg)](https://github.com/arendst/Sonoff-Tasmota/releases/latest) [![GitHub download](https://img.shields.io/github/downloads/arendst/Sonoff-Tasmota/total.svg)](https://github.com/arendst/Sonoff-Tasmota/releases/latest) @@ -24,6 +24,9 @@ A Sonoff device is not a toy. It uses Mains AC so there is a danger of electrocu We don't take any responsibility nor liability for using this software nor for the installation or any tips, advice, videos, etc. given by any member of this site or any related site. +### Note +Please do not ask to add devices where you can't provide a basic working configuration (other than sonoff). Since there are thousands of them.. + ### Quick Install Download one of the released binaries from https://github.com/arendst/Sonoff-Tasmota/releases and flash it to your hardware as documented in the wiki. @@ -101,6 +104,7 @@ You can contribute to Sonoff-Tasmota by - providing Pull Requests (Features, Proof of Concepts, Language files or Fixes) - testing new released features and report issues - donating to acquire hardware for testing and implementing or out of gratitude +- contributing missing documentation for features and devices on our [Wiki](https://github.com/arendst/Sonoff-Tasmota/wiki) [![donate](https://img.shields.io/badge/donate-PayPal-blue.svg)](https://paypal.me/tasmota) @@ -121,9 +125,11 @@ Libraries used with Sonoff-Tasmota are: - [C2 Programmer](http://app.cear.ufpb.br/~lucas.hartmann/tag/efm8bb1/) - [esp-epaper-29-ws-20171230-gemu](https://github.com/gemu2015/Sonoff-Tasmota/tree/displays/lib) - [esp-knx-ip](https://github.com/envy/esp-knx-ip) +- FrogmoreScd30 - [I2Cdevlib](https://github.com/jrowberg/i2cdevlib) - [IRremoteEsp8266](https://github.com/markszabo/IRremoteESP8266) - [JobaTsl2561](https://github.com/joba-1/Joba_Tsl2561) +- [LinkedList](https://github.com/ivanseidel/LinkedList) - [Liquid Cristal](https://github.com/marcoschwartz/LiquidCrystal_I2C) - [MultiChannelGasSensor](http://wiki.seeedstudio.com/Grove-Multichannel_Gas_Sensor/) - [NeoPixelBus](https://github.com/Makuna/NeoPixelBus) @@ -157,7 +163,8 @@ People helping to keep the show on the road: - Andre Thomas for providing [thehackbox](http://thehackbox.org/tasmota/) OTA support and daily development builds - Joel Stein and digiblur for their Tuya research and driver - Frogmore42 and Jason2866 for providing many issue answers -- Many more providing Tips, Pocs or PRs +- Blakadder for editing the wiki and providing template management +- Many more providing Tips, Wips, Pocs or PRs ### License diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 1198a6165..b33fd51f9 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -82,6 +82,15 @@ Module | Description 58 PS-16-DZ | PS-16-DZ Wifi dimmer for Incandescent Lights and Led 59 Teckin US | Teckin US and ZooZee SA102 Wifi Smart Switch with Energy Monitoring 60 Manzoku strip | Manzoku Wifi Smart Power Strip with four Relays +61 OBI Socket 2 | OBI 2 Wifi Smart Socket +62 YTF IR Bridge | YTF Infra Red Wifi Bridge +63 Digoo DG-SP202 | Digoo DG-SP202 Dual Wifi Smart Switch with Energy Monitoring +64 KA10 | Smanergy KA10 Wifi Smart Wall Switch with Energy Monitoring +65 Luminea ZX2820 | Luminea ZX2820 Wifi Smart Switch with Energy Monitoring +66 Mi Desk Lamp | Mi Desk Lamp with rotary switch and Wifi +67 SP10 | Tuya SP10 Wifi Smart Switch with Energy Monitoring +68 WAGA CHCZ02MB | WAGA life CHCZ02MB Wifi Smart Switch with Energy Monitoring +69 SYF05 | Sunyesmart SYF05 RGBWW Wifi Led Bulb ## Provided Binary Downloads The following binary downloads have been compiled with ESP8266/Arduino library core version **2.4.2** patched with the Alexa fix. @@ -98,9 +107,6 @@ The following binary downloads have been compiled with ESP8266/Arduino library c | Feature or Sensor | minimal | basic | classic | sonoff | knx | sensors | Remarks |--------------------------------|---------|-------|---------|--------|------|---------|-------- -| ESP/Arduino lib v2.3.0 | 346k | 429k | 482k | 504k | 522k | 535k | -| ESP/Arduino lib v2.4.2 | 366k | 446k | 496k | 522k | 538k | 551k | No wifi sleep -| | | | | | | | | MY_LANGUAGE en-GB | x | x | x | x | x | x | | MQTT_LIBRARY_TYPE PUBSUBCLIENT | x | x | x | x | x | x | | USE_WPS | - | - | x | - | - | - | WPS @@ -150,6 +156,8 @@ The following binary downloads have been compiled with ESP8266/Arduino library c | USE_MPU6050 | - | - | - | - | - | - | | USE_DS3231 | - | - | - | - | - | - | | USE_MGC3130 | - | - | - | - | - | - | +| USE_MAX44009 | - | - | - | - | - | - | +| USE_SCD30 | - | - | - | - | - | - | | | | | | | | | | Feature or Sensor | minimal | basic | classic | sonoff | knx | sensors | | USE_SPI | - | - | - | - | - | - | @@ -169,6 +177,7 @@ The following binary downloads have been compiled with ESP8266/Arduino library c | USE_ARMTRONIX_DIMMERS | - | x | - | x | x | x | | USE_PS_16_DZ | - | x | - | x | x | x | | USE_AZ7798 | - | - | - | - | - | - | +| USE_PN532_HSU | - | - | - | - | - | - | | USE_IR_REMOTE | - | - | - | x | x | x | | USE_IR_HVAC | - | - | - | - | - | x | | USE_IR_RECEIVE | - | - | - | x | x | x | @@ -182,80 +191,10 @@ The following binary downloads have been compiled with ESP8266/Arduino library c | USE_TX20_WIND_SENSOR | - | - | - | x | x | x | | USE_RC_SWITCH | - | - | - | x | x | x | | USE_RF_SENSOR | - | - | - | - | - | x | AlectoV2 only +| USE_SM16716 | - | x | x | x | x | x | | USE_DISPLAY | - | - | - | - | - | - | ## Changelog -Version 6.4.1 20181225 - * Change RAM usage BMP/BME I2C sensors - * Change FallbackTopic from cmnd/\/ to cmnd/\_fb/ to discriminate from Topic (#1528) - * Change FallbackTopic detection (#4706) - * Change Hass discovery to short MQTT messages as used by Hass 0.81 and up (#4711) - * Change MQTT GUI password handling (#4723) - * Fix possible dtostrf buffer overflows by increasing buffers - * Fix wifi strongest signal detection (#4704) - * Fix Alexa "this value is outside the range of the device". Needs power cycle and Alexa deletion/discovery cycle. (#3159, #4712) - * Add Slovak language file (#4663) - * Add support for AZ-Instrument 7798 CO2 meter/datalogger (#4672) - * Add define WIFI_SOFT_AP_CHANNEL in my_user_config.h to set Soft Access Point Channel number between 1 and 13 as used by Wifi Manager web GUI (#4673) - * Add define USE_MQTT_TLS_CA_CERT for checking MQTT TLS against root ca using Let's Encrypt cert from sonoff_letsencrypt.h - not supported with core 2.3.0 (#4703) +Version 6.5.0 20190315 -Version 6.4.0 20181217 - * Change GUI Configure Module by using AJAX for data fetch to cut page size (and memory use) by 40%. - In case of web page errors clear your browser cache or do Page Reload (F5 or Ctrl+R) - * Change enforcing flashmode dout but it is still mandatory - * Change bootcount update (being first) flash write to 10 seconds after restart - * Change display and epaper drivers - * Change command WebSend Host header field from IP address to hostname (#4331) - * Change log buffer size from 512 to 520 to accommodate http sensor data (#4354) - * Change default WIFI_CONFIG_TOOL from WIFI_WAIT to WIFI_RETRY in my_user_config.h (#4400) - * Change webgui refresh time delay for Save Settings and local OTA Upload (#4423) - * Change SR-04 driver to use NewPing library (#4488) - * Change MCP230xx driver to support interrupt retention over teleperiod (#4547) - * Change support for MPU6050 using DMP (#4581) - * Fix unintended function overload of WifiState - * Fix wifi connection errors using wifi disconnect and ESP.reset instead of ESP.restart - * Fix Sonoff Pow R2 and Sonoff S31 Serial interface hang caused by Sonoff Basic R2 driver delay implementation (and possibly core bug) - * Fix MQTT connection error after restart - * Fix wifi re-scan connection baseline - * Fix possible strncat buffer overflows - * Fix intermittent Pzem sensor energy overflow calculation error - * Fix shelly2 ghost switching caused by lack of pull-up inputs (#4255) - * Fix hardware serial pin configuration. To keep using hardware serial swap current Rx/Tx pin configuration only (#4280) - * Fix MqttRetry values above 255 seconds (#4424) - * Fix WifiManager functionality on initial installation (#4433) - * Fix ArduinoOTA for Core 2.5.0 (#4620) - * Add minutes to commands Timezone to allow all possible world timezones - * Add more strict checks for GPIO selections - * Add code image and optional commit number to version - * Add dynamic delay to main loop providing time for wifi background tasks - * Add additional start-up delay during initial wifi connection - * Add support for decoding Theo V2 sensors as documented on https://sidweb.nl using 434MHz RF sensor receiver - * Add support for decoding Alecto V2 sensors like ACH2010, WS3000 and DKW2012 weather stations using 868MHz RF sensor receiver - * Add user definition of defines WIFI_RSSI_THRESHOLD (default 10) and WIFI_RESCAN_MINUTES (default 44) - * Add command SetOption58 0/1 to enable IR raw data info in JSON message (#2116) - * Add command IRSend \|0,\,\,.. to allow raw data transmission (#2116) - * Add command SetOption56 0/1 to enable wifi network scan and select highest RSSI (#3173) - * Add command SetOption57 0/1 to enable wifi network re-scan every 44 minutes with a rssi threshold of 10 to select highest RSSI (#3173) - * Add support for SDM220 (#3610) - * Add default sleep 1 to sonoff-basic to lower energy consumption (#4217) - * Add wifi status to Tuya (#4221) - * Add delays to reduce CPU usage at boot time (#4233) - * Add command SetOption24 0/1 to select pressure unit as hPa or mmHg (#4241) - * Add optional hardware serial when GPIO13(Rx) and GPIO15(Tx) are selected removing hardware serial from GPIO01(Tx) and GPIO03(Rx) (#4288) - * Add support for Gosund SP1 v2.3 Power Socket with Energy Monitoring (#4297) - * Add support for Armtronix dimmers. See wiki for info (#4321) - * Add to command WebSend option to send a direct path when command starts with a slash (#4329) - * Add support for LG HVac and IrRemote (#4377) - * Add initial support for Hass sensor discovery (#4380) - * Add support for Fujitsu HVac and IrRemote (#4387) - * Add support for I2C MGC3130 Electric Field Effect sensor by Christian Baars (#3774, #4404) - * Add command CalcRes to set number of decimals (0 - 7) used in commands ADD, SUB, MULT and SCALE (#4420) - * Add CPU average load to state message (#4431) - * Add command SetOption59 0/1 to change state topic from tele/STATE to stat/RESULT (#4450) - * Add support for SM Smart Wifi Dimmer PS-16-DZ (#4465) - * Add support for Teckin US Power Socket with Energy Monitoring (#4481) - * Add command SetOption60 0/1 to select dynamic sleep (0) or sleep (1) (#4497) - * Add support for iFan02 Fanspeed in Domoticz using a selector (#4517) - * Add support for GPIO02 for newer Sonoff Basic (#4518) - * Add Announce Switches to MQTT Discovery (#4531) - * Add support for Manzoku Power Strip (#4590) + * Tbd diff --git a/SUPPORT.md b/SUPPORT.md new file mode 100644 index 000000000..46fa03ebc --- /dev/null +++ b/SUPPORT.md @@ -0,0 +1,22 @@ +# Sonoff-Tasmota Support + +If you're looking for support on **Sonoff-Tasmota** there are some options available: + +### Documentation: + +* [Wiki Pages](https://github.com/arendst/Sonoff-Tasmota/wiki): For information on how to Flash Tasmota, configure and use it. +* [Troubleshooting Information](https://github.com/arendst/Sonoff-Tasmota/wiki/Troubleshooting): For information on common problems and solutions. +* [Commands Information](https://github.com/arendst/Sonoff-Tasmota/wiki/Commands): For information on all the commands supported by Tasmota. + +### Support's Community: + +* [Tasmota Forum](https://groups.google.com/d/forum/sonoffusers): For usage and discussions. +* [Tasmota Support Chat](https://discord.gg/Ks2Kzd4): For support, troubleshooting and general questions. You have better chances to get fast answers from members of the Tasmota Community. +* [Search in Issues](https://github.com/arendst/Sonoff-Tasmota/issues): You might find an answer to your question by searching current or closed issues. + +### Developers' Community: + +* [Bug Report](https://github.com/arendst/Sonoff-Tasmota/issues/new?template=Bug_report.md): For reporting Bugs of Tasmota Software. +* [Feature Request](https://github.com/arendst/Sonoff-Tasmota/issues/new?template=Feature_request.md): For requesting features/functions to Tasmota Software. +* [Troubleshooting](https://github.com/arendst/Sonoff-Tasmota/issues/new?template=Custom.md): As a last resort, you can open new *Troubleshooting* issue on GitHub if the solution could not be found using the other channels. Just remember: the more info you provide the more chances you'll have to get an accurate answer. +* [Issue a question](https://github.com/arendst/Sonoff-Tasmota/issues/new/choose): As a last resort, you can open new *Question* issue on GitHub if the answer could not be found using the other channels. Just remember: the more info you provide the more chances you'll have to get an accurate answer. diff --git a/TEMPLATE.md b/TEMPLATE.md new file mode 100644 index 000000000..67431f651 --- /dev/null +++ b/TEMPLATE.md @@ -0,0 +1,86 @@ +## Sonoff-Tasmota template information +Sonoff-Tasmota uses Device or Module information to control peripherals connected to GPIOs. This information is stored in the ``sonoff_template.h`` file as a device specific template. The template contains information about what GPIO should be connected to what peripheral and what GPIO may be configured online using the ``GPIO`` command or GUI Configure Module menu. In addition a device may need specific coding to process the data from these peripherals. The module number as provided by the ``Modules`` command is used to select this coding. + +Starting with version 6.4.1.16 Sonoff-Tasmota Modules can be extended by users online using a template. To provide easy processing by Sonoff-Tasmota a user template is written as JSON text and could look like this: + +{"NAME":"UserModule1","GPIO":[17,148,29,149,7,255,255,255,138,255,139,255,255],"FLAG":0,"BASE":18} + +The four properties with UPPERCASE property names have the following functionality: + +Property name | Property value description +--------------|------------------------------------------------------------------------------------------------------------------- +NAME | Up to 14 characters for the Module name +GPIO | Up to 13 decimal numbers from 0 to 255 representing GPIO0 to GPIO5, GPIO09, GPIO10 and GPIO12 to GPIO16 +FLAG | 8 bit mask flag register +BASE | Module number of a hard-coded device to be used when device specific functionality is needed + +The above example, based on the Generic Module does not allow ADC0 input. + +## GPIO functionality +The GPIO functionality numbers are the same is shown by command ``GPIOs``. In addition code 255 is added to select a GPIO as user configurable via the GUI Configure Module menu. + +## FLAG functionality +The FLAG value is an 8-bit mask where each bit controls a features. Add FLAG values to set multiple bits. + +FLAG | Mask | Feature description +-----|----------|------------------------------ + 1 | xxxxxxx1 | Allowing to use Analog0 (ADC0) as input if define USE_ADC_VCC in ``my_user_config.h`` is disabled + 2 | xxxxxx1x | Enable GUI pull-up control message + 4 | xxxxx1xx | Not used + 8 | xxxx1xxx | Not used + 16 | xxx1xxxx | Not used + 32 | xx1xxxxx | Not used + 64 | x1xxxxxx | Not used + 128 | 1xxxxxxx | Not used + +## BASE functionality +The following table lists hard-coded device specific functionality. Notice that not all device modules need special handling. + +BASE | Module | Description +-----|----------------|---------------------------------------------- + 4 | Sonoff Dual | Process relay and button via hardware serial interface using GPIO01 and GPIO03. Change baudrate to 19200 bps. Process buttons as single press only + 9 | Sonoff Touch | Invert ledstate 1 functionality + 10 | Sonoff LED | Set light type to 2 PWM channels disregarding SetOption15. Fix device specific LED instabilities by disabling GPIO04, GPIO5 and GPIO14 + 12 | 4 Channel | See 4 + 13 | Motor C/AC | Force all relays ON at Power On and disable command ``PowerOnState`` + 15 | EXS Relay(s) | Enable pulse latching using even/odd numbered relay pairs + 18 | Generic | Show Wemos specific pin information in GUI + 19 | H801 | Change hardware UART Tx from GPIO01 to GPIO02 + 20 | Sonoff SC | Enable and Process data via hardware serial interface using GPIO01 and GPIO03. Change baudrate to 19200 bps + 21 | Sonoff BN-SZ | Set light type to 1 PWM channel disregarding SetOption15 + 22 | Sonoff 4CH Pro | Button handling disregarding SetOption13 only allowing single press to enable RF learning while holding the button + 24 | Sonoff Bridge | Enable and Process data via hardware serial interface using GPIO01 and GPIO03. Change baudrate to 19200 bps. Process 16 buttons in web GUI. Enable EFM8BB1 firmware upload + 25 | Sonoff B1 | Set light type to RGBWC using MY92x1 + 26 | AiLight | Set light type to RGBW using MY92x1 + 27 | Sonoff T1 1CH | See 9 + 28 | Sonoff T1 2CH | See 9 + 29 | Sonoff T1 3CH | See 9 + 38 | Sonoff Dual R2 | Process buttons as single press only + 43 | Sonoff iFan02 | Enable command ``Fanspeed``. Disable Interlock and PulseTime. Tune status information, MQTT data and GUI. Sync with microcontroller. Process Domoticz Fan state + 47 | Xiaomi Philips | Process Color Temperature using PWM2 and Intensity using PWM1 + 53 | Tuya Dimmer | Enable and Process data via software or hardware serial interface using GPIO 148 and 149 or forced GPIO01 and GPIO03. Change baudrate to 9600 bps. Process all Buttons + 55 | ARMTR Dimmer | Enable and Process data via software or hardware serial interface using GPIO 148 and 149. Change baudrate to 115200 bps. + 57 | PS-16-DZ | Enable and Process data via software or hardware serial interface using GPIO 148 and 149. Change baudrate to 19200 bps. + 61 | YTF IR Bridge | Disable serial interface to stop loopback + 65 | Mi Desk Lamp | Process rotary and Button1 data specific to this device + +## Usage +A user provided template can be stored in Sonoff-Tasmota using the ``Template`` command. It has the following options. + +Command | Payload | Description +---------|----------|--------------------------------------- +Template | | Show current user template +Template | 0 | Copy active module template to user template +Template | 1 .. 69 | Copy hard-coded module template to user template + +The following command will store a complete template based on the Generic module +``Template {"NAME":"UserModule1","GPIO":[17,148,29,149,7,255,255,255,138,255,139,255,255],"FLAG":0,"BASE":18}`` + +The following command will update the name of a stored template +``Template {"NAME":"UserModule2"}`` + +The following command will update the flag of a stored template +``Template {"FLAG":1}`` + +The following command will update the base of a stored template to Generic +``Template {"BASE":0}`` \ No newline at end of file diff --git a/arduino/version 2.5.0/boards.txt b/arduino/version 2.5.0/boards.txt index 2334302b8..300a608c4 100644 --- a/arduino/version 2.5.0/boards.txt +++ b/arduino/version 2.5.0/boards.txt @@ -54,12 +54,12 @@ generic.menu.vt.heap=Heap generic.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM generic.menu.vt.iram=IRAM generic.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM -generic.menu.exception.enabled=Enabled -generic.menu.exception.enabled.build.exception_flags=-fexceptions -generic.menu.exception.enabled.build.stdcpp_lib=-lstdc++ generic.menu.exception.disabled=Disabled generic.menu.exception.disabled.build.exception_flags=-fno-exceptions -generic.menu.exception.disabled.build.stdcpp_lib=-lstdc++-nox +generic.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +generic.menu.exception.enabled=Enabled +generic.menu.exception.enabled.build.exception_flags=-fexceptions +generic.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc generic.menu.ResetMethod.ck=ck generic.menu.ResetMethod.ck.upload.resetmethod=ck generic.menu.ResetMethod.nodemcu=nodemcu @@ -75,14 +75,18 @@ generic.menu.FlashFreq.40=40MHz generic.menu.FlashFreq.40.build.flash_freq=40 generic.menu.FlashFreq.80=80MHz generic.menu.FlashFreq.80.build.flash_freq=80 -generic.menu.FlashMode.qio=QIO -generic.menu.FlashMode.qio.build.flash_mode=qio -generic.menu.FlashMode.qout=QOUT -generic.menu.FlashMode.qout.build.flash_mode=qout +generic.menu.FlashMode.dout=DOUT (compatible) +generic.menu.FlashMode.dout.build.flash_mode=dout +generic.menu.FlashMode.dout.build.flash_flags=-DFLASHMODE_DOUT generic.menu.FlashMode.dio=DIO generic.menu.FlashMode.dio.build.flash_mode=dio -generic.menu.FlashMode.dout=DOUT -generic.menu.FlashMode.dout.build.flash_mode=dout +generic.menu.FlashMode.dio.build.flash_flags=-DFLASHMODE_DIO +generic.menu.FlashMode.qout=QOUT +generic.menu.FlashMode.qout.build.flash_mode=qout +generic.menu.FlashMode.qout.build.flash_flags=-DFLASHMODE_QOUT +generic.menu.FlashMode.qio=QIO (fast) +generic.menu.FlashMode.qio.build.flash_mode=qio +generic.menu.FlashMode.qio.build.flash_flags=-DFLASHMODE_QIO generic.menu.eesz.512K=512K (no SPIFFS) generic.menu.eesz.512K.build.flash_size=512K generic.menu.eesz.512K.build.flash_size_bytes=0x80000 @@ -490,12 +494,12 @@ esp8285.menu.vt.heap=Heap esp8285.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM esp8285.menu.vt.iram=IRAM esp8285.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM -esp8285.menu.exception.enabled=Enabled -esp8285.menu.exception.enabled.build.exception_flags=-fexceptions -esp8285.menu.exception.enabled.build.stdcpp_lib=-lstdc++ esp8285.menu.exception.disabled=Disabled esp8285.menu.exception.disabled.build.exception_flags=-fno-exceptions -esp8285.menu.exception.disabled.build.stdcpp_lib=-lstdc++-nox +esp8285.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +esp8285.menu.exception.enabled=Enabled +esp8285.menu.exception.enabled.build.exception_flags=-fexceptions +esp8285.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc esp8285.menu.ResetMethod.ck=ck esp8285.menu.ResetMethod.ck.upload.resetmethod=ck esp8285.menu.ResetMethod.nodemcu=nodemcu @@ -508,6 +512,7 @@ esp8285.menu.CrystalFreq.26=26 MHz esp8285.menu.CrystalFreq.40=40 MHz esp8285.menu.CrystalFreq.40.build.extra_flags=-DF_CRYSTAL=40000000 -DESP8266 esp8285.build.flash_mode=dout +esp8285.build.flash_flags=-DFLASHMODE_DOUT esp8285.build.flash_freq=40 esp8285.menu.eesz.1M=1M (no SPIFFS) esp8285.menu.eesz.1M.build.flash_size=1M @@ -764,13 +769,14 @@ espduino.menu.vt.heap=Heap espduino.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM espduino.menu.vt.iram=IRAM espduino.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM -espduino.menu.exception.enabled=Enabled -espduino.menu.exception.enabled.build.exception_flags=-fexceptions -espduino.menu.exception.enabled.build.stdcpp_lib=-lstdc++ espduino.menu.exception.disabled=Disabled espduino.menu.exception.disabled.build.exception_flags=-fno-exceptions -espduino.menu.exception.disabled.build.stdcpp_lib=-lstdc++-nox +espduino.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +espduino.menu.exception.enabled=Enabled +espduino.menu.exception.enabled.build.exception_flags=-fexceptions +espduino.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc espduino.build.flash_mode=dio +espduino.build.flash_flags=-DFLASHMODE_DIO espduino.build.flash_freq=40 espduino.menu.eesz.4M=4M (no SPIFFS) espduino.menu.eesz.4M.build.flash_size=4M @@ -946,14 +952,15 @@ huzzah.menu.vt.heap=Heap huzzah.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM huzzah.menu.vt.iram=IRAM huzzah.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM -huzzah.menu.exception.enabled=Enabled -huzzah.menu.exception.enabled.build.exception_flags=-fexceptions -huzzah.menu.exception.enabled.build.stdcpp_lib=-lstdc++ huzzah.menu.exception.disabled=Disabled huzzah.menu.exception.disabled.build.exception_flags=-fno-exceptions -huzzah.menu.exception.disabled.build.stdcpp_lib=-lstdc++-nox +huzzah.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +huzzah.menu.exception.enabled=Enabled +huzzah.menu.exception.enabled.build.exception_flags=-fexceptions +huzzah.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc huzzah.upload.resetmethod=nodemcu huzzah.build.flash_mode=qio +huzzah.build.flash_flags=-DFLASHMODE_QIO huzzah.build.flash_freq=40 huzzah.menu.eesz.4M=4M (no SPIFFS) huzzah.menu.eesz.4M.build.flash_size=4M @@ -1129,14 +1136,15 @@ inventone.menu.vt.heap=Heap inventone.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM inventone.menu.vt.iram=IRAM inventone.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM -inventone.menu.exception.enabled=Enabled -inventone.menu.exception.enabled.build.exception_flags=-fexceptions -inventone.menu.exception.enabled.build.stdcpp_lib=-lstdc++ inventone.menu.exception.disabled=Disabled inventone.menu.exception.disabled.build.exception_flags=-fno-exceptions -inventone.menu.exception.disabled.build.stdcpp_lib=-lstdc++-nox +inventone.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +inventone.menu.exception.enabled=Enabled +inventone.menu.exception.enabled.build.exception_flags=-fexceptions +inventone.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc inventone.upload.resetmethod=nodemcu inventone.build.flash_mode=dio +inventone.build.flash_flags=-DFLASHMODE_DIO inventone.build.flash_freq=40 inventone.menu.eesz.4M=4M (no SPIFFS) inventone.menu.eesz.4M.build.flash_size=4M @@ -1312,17 +1320,18 @@ cw01.menu.vt.heap=Heap cw01.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM cw01.menu.vt.iram=IRAM cw01.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM -cw01.menu.exception.enabled=Enabled -cw01.menu.exception.enabled.build.exception_flags=-fexceptions -cw01.menu.exception.enabled.build.stdcpp_lib=-lstdc++ cw01.menu.exception.disabled=Disabled cw01.menu.exception.disabled.build.exception_flags=-fno-exceptions -cw01.menu.exception.disabled.build.stdcpp_lib=-lstdc++-nox +cw01.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +cw01.menu.exception.enabled=Enabled +cw01.menu.exception.enabled.build.exception_flags=-fexceptions +cw01.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc cw01.upload.resetmethod=nodemcu cw01.menu.CrystalFreq.26=26 MHz cw01.menu.CrystalFreq.40=40 MHz cw01.menu.CrystalFreq.40.build.extra_flags=-DF_CRYSTAL=40000000 -DESP8266 -cw01.build.flash_mode=qio +cw01.build.flash_mode=dio +cw01.build.flash_flags=-DFLASHMODE_DIO cw01.build.flash_freq=40 cw01.menu.eesz.4M=4M (no SPIFFS) cw01.menu.eesz.4M.build.flash_size=4M @@ -1498,13 +1507,14 @@ espresso_lite_v1.menu.vt.heap=Heap espresso_lite_v1.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM espresso_lite_v1.menu.vt.iram=IRAM espresso_lite_v1.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM -espresso_lite_v1.menu.exception.enabled=Enabled -espresso_lite_v1.menu.exception.enabled.build.exception_flags=-fexceptions -espresso_lite_v1.menu.exception.enabled.build.stdcpp_lib=-lstdc++ espresso_lite_v1.menu.exception.disabled=Disabled espresso_lite_v1.menu.exception.disabled.build.exception_flags=-fno-exceptions -espresso_lite_v1.menu.exception.disabled.build.stdcpp_lib=-lstdc++-nox +espresso_lite_v1.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +espresso_lite_v1.menu.exception.enabled=Enabled +espresso_lite_v1.menu.exception.enabled.build.exception_flags=-fexceptions +espresso_lite_v1.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc espresso_lite_v1.build.flash_mode=dio +espresso_lite_v1.build.flash_flags=-DFLASHMODE_DIO espresso_lite_v1.build.flash_freq=40 espresso_lite_v1.menu.eesz.4M=4M (no SPIFFS) espresso_lite_v1.menu.eesz.4M.build.flash_size=4M @@ -1684,13 +1694,14 @@ espresso_lite_v2.menu.vt.heap=Heap espresso_lite_v2.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM espresso_lite_v2.menu.vt.iram=IRAM espresso_lite_v2.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM -espresso_lite_v2.menu.exception.enabled=Enabled -espresso_lite_v2.menu.exception.enabled.build.exception_flags=-fexceptions -espresso_lite_v2.menu.exception.enabled.build.stdcpp_lib=-lstdc++ espresso_lite_v2.menu.exception.disabled=Disabled espresso_lite_v2.menu.exception.disabled.build.exception_flags=-fno-exceptions -espresso_lite_v2.menu.exception.disabled.build.stdcpp_lib=-lstdc++-nox +espresso_lite_v2.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +espresso_lite_v2.menu.exception.enabled=Enabled +espresso_lite_v2.menu.exception.enabled.build.exception_flags=-fexceptions +espresso_lite_v2.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc espresso_lite_v2.build.flash_mode=dio +espresso_lite_v2.build.flash_flags=-DFLASHMODE_DIO espresso_lite_v2.build.flash_freq=40 espresso_lite_v2.menu.eesz.4M=4M (no SPIFFS) espresso_lite_v2.menu.eesz.4M.build.flash_size=4M @@ -1870,13 +1881,14 @@ phoenix_v1.menu.vt.heap=Heap phoenix_v1.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM phoenix_v1.menu.vt.iram=IRAM phoenix_v1.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM -phoenix_v1.menu.exception.enabled=Enabled -phoenix_v1.menu.exception.enabled.build.exception_flags=-fexceptions -phoenix_v1.menu.exception.enabled.build.stdcpp_lib=-lstdc++ phoenix_v1.menu.exception.disabled=Disabled phoenix_v1.menu.exception.disabled.build.exception_flags=-fno-exceptions -phoenix_v1.menu.exception.disabled.build.stdcpp_lib=-lstdc++-nox +phoenix_v1.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +phoenix_v1.menu.exception.enabled=Enabled +phoenix_v1.menu.exception.enabled.build.exception_flags=-fexceptions +phoenix_v1.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc phoenix_v1.build.flash_mode=dio +phoenix_v1.build.flash_flags=-DFLASHMODE_DIO phoenix_v1.build.flash_freq=40 phoenix_v1.menu.eesz.4M=4M (no SPIFFS) phoenix_v1.menu.eesz.4M.build.flash_size=4M @@ -2056,13 +2068,14 @@ phoenix_v2.menu.vt.heap=Heap phoenix_v2.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM phoenix_v2.menu.vt.iram=IRAM phoenix_v2.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM -phoenix_v2.menu.exception.enabled=Enabled -phoenix_v2.menu.exception.enabled.build.exception_flags=-fexceptions -phoenix_v2.menu.exception.enabled.build.stdcpp_lib=-lstdc++ phoenix_v2.menu.exception.disabled=Disabled phoenix_v2.menu.exception.disabled.build.exception_flags=-fno-exceptions -phoenix_v2.menu.exception.disabled.build.stdcpp_lib=-lstdc++-nox +phoenix_v2.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +phoenix_v2.menu.exception.enabled=Enabled +phoenix_v2.menu.exception.enabled.build.exception_flags=-fexceptions +phoenix_v2.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc phoenix_v2.build.flash_mode=dio +phoenix_v2.build.flash_flags=-DFLASHMODE_DIO phoenix_v2.build.flash_freq=40 phoenix_v2.menu.eesz.4M=4M (no SPIFFS) phoenix_v2.menu.eesz.4M.build.flash_size=4M @@ -2242,14 +2255,15 @@ nodemcu.menu.vt.heap=Heap nodemcu.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM nodemcu.menu.vt.iram=IRAM nodemcu.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM -nodemcu.menu.exception.enabled=Enabled -nodemcu.menu.exception.enabled.build.exception_flags=-fexceptions -nodemcu.menu.exception.enabled.build.stdcpp_lib=-lstdc++ nodemcu.menu.exception.disabled=Disabled nodemcu.menu.exception.disabled.build.exception_flags=-fno-exceptions -nodemcu.menu.exception.disabled.build.stdcpp_lib=-lstdc++-nox +nodemcu.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +nodemcu.menu.exception.enabled=Enabled +nodemcu.menu.exception.enabled.build.exception_flags=-fexceptions +nodemcu.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc nodemcu.upload.resetmethod=nodemcu nodemcu.build.flash_mode=qio +nodemcu.build.flash_flags=-DFLASHMODE_QIO nodemcu.build.flash_freq=40 nodemcu.menu.eesz.4M=4M (no SPIFFS) nodemcu.menu.eesz.4M.build.flash_size=4M @@ -2425,14 +2439,15 @@ nodemcuv2.menu.vt.heap=Heap nodemcuv2.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM nodemcuv2.menu.vt.iram=IRAM nodemcuv2.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM -nodemcuv2.menu.exception.enabled=Enabled -nodemcuv2.menu.exception.enabled.build.exception_flags=-fexceptions -nodemcuv2.menu.exception.enabled.build.stdcpp_lib=-lstdc++ nodemcuv2.menu.exception.disabled=Disabled nodemcuv2.menu.exception.disabled.build.exception_flags=-fno-exceptions -nodemcuv2.menu.exception.disabled.build.stdcpp_lib=-lstdc++-nox +nodemcuv2.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +nodemcuv2.menu.exception.enabled=Enabled +nodemcuv2.menu.exception.enabled.build.exception_flags=-fexceptions +nodemcuv2.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc nodemcuv2.upload.resetmethod=nodemcu nodemcuv2.build.flash_mode=dio +nodemcuv2.build.flash_flags=-DFLASHMODE_DIO nodemcuv2.build.flash_freq=40 nodemcuv2.menu.eesz.4M=4M (no SPIFFS) nodemcuv2.menu.eesz.4M.build.flash_size=4M @@ -2608,14 +2623,15 @@ modwifi.menu.vt.heap=Heap modwifi.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM modwifi.menu.vt.iram=IRAM modwifi.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM -modwifi.menu.exception.enabled=Enabled -modwifi.menu.exception.enabled.build.exception_flags=-fexceptions -modwifi.menu.exception.enabled.build.stdcpp_lib=-lstdc++ modwifi.menu.exception.disabled=Disabled modwifi.menu.exception.disabled.build.exception_flags=-fno-exceptions -modwifi.menu.exception.disabled.build.stdcpp_lib=-lstdc++-nox +modwifi.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +modwifi.menu.exception.enabled=Enabled +modwifi.menu.exception.enabled.build.exception_flags=-fexceptions +modwifi.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc modwifi.upload.resetmethod=ck modwifi.build.flash_mode=qio +modwifi.build.flash_flags=-DFLASHMODE_QIO modwifi.build.flash_freq=40 modwifi.menu.eesz.2M=2M (no SPIFFS) modwifi.menu.eesz.2M.build.flash_size=2M @@ -2801,14 +2817,15 @@ thing.menu.vt.heap=Heap thing.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM thing.menu.vt.iram=IRAM thing.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM -thing.menu.exception.enabled=Enabled -thing.menu.exception.enabled.build.exception_flags=-fexceptions -thing.menu.exception.enabled.build.stdcpp_lib=-lstdc++ thing.menu.exception.disabled=Disabled thing.menu.exception.disabled.build.exception_flags=-fno-exceptions -thing.menu.exception.disabled.build.stdcpp_lib=-lstdc++-nox +thing.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +thing.menu.exception.enabled=Enabled +thing.menu.exception.enabled.build.exception_flags=-fexceptions +thing.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc thing.upload.resetmethod=ck thing.build.flash_mode=qio +thing.build.flash_flags=-DFLASHMODE_QIO thing.build.flash_freq=40 thing.menu.eesz.512K=512K (no SPIFFS) thing.menu.eesz.512K.build.flash_size=512K @@ -2984,14 +3001,15 @@ thingdev.menu.vt.heap=Heap thingdev.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM thingdev.menu.vt.iram=IRAM thingdev.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM -thingdev.menu.exception.enabled=Enabled -thingdev.menu.exception.enabled.build.exception_flags=-fexceptions -thingdev.menu.exception.enabled.build.stdcpp_lib=-lstdc++ thingdev.menu.exception.disabled=Disabled thingdev.menu.exception.disabled.build.exception_flags=-fno-exceptions -thingdev.menu.exception.disabled.build.stdcpp_lib=-lstdc++-nox +thingdev.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +thingdev.menu.exception.enabled=Enabled +thingdev.menu.exception.enabled.build.exception_flags=-fexceptions +thingdev.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc thingdev.upload.resetmethod=nodemcu thingdev.build.flash_mode=dio +thingdev.build.flash_flags=-DFLASHMODE_DIO thingdev.build.flash_freq=40 thingdev.menu.eesz.512K=512K (no SPIFFS) thingdev.menu.eesz.512K.build.flash_size=512K @@ -3167,14 +3185,15 @@ esp210.menu.vt.heap=Heap esp210.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM esp210.menu.vt.iram=IRAM esp210.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM -esp210.menu.exception.enabled=Enabled -esp210.menu.exception.enabled.build.exception_flags=-fexceptions -esp210.menu.exception.enabled.build.stdcpp_lib=-lstdc++ esp210.menu.exception.disabled=Disabled esp210.menu.exception.disabled.build.exception_flags=-fno-exceptions -esp210.menu.exception.disabled.build.stdcpp_lib=-lstdc++-nox +esp210.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +esp210.menu.exception.enabled=Enabled +esp210.menu.exception.enabled.build.exception_flags=-fexceptions +esp210.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc esp210.upload.resetmethod=ck esp210.build.flash_mode=qio +esp210.build.flash_flags=-DFLASHMODE_QIO esp210.build.flash_freq=40 esp210.menu.eesz.4M=4M (no SPIFFS) esp210.menu.eesz.4M.build.flash_size=4M @@ -3350,14 +3369,15 @@ d1_mini.menu.vt.heap=Heap d1_mini.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM d1_mini.menu.vt.iram=IRAM d1_mini.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM -d1_mini.menu.exception.enabled=Enabled -d1_mini.menu.exception.enabled.build.exception_flags=-fexceptions -d1_mini.menu.exception.enabled.build.stdcpp_lib=-lstdc++ d1_mini.menu.exception.disabled=Disabled d1_mini.menu.exception.disabled.build.exception_flags=-fno-exceptions -d1_mini.menu.exception.disabled.build.stdcpp_lib=-lstdc++-nox +d1_mini.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +d1_mini.menu.exception.enabled=Enabled +d1_mini.menu.exception.enabled.build.exception_flags=-fexceptions +d1_mini.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc d1_mini.upload.resetmethod=nodemcu d1_mini.build.flash_mode=dio +d1_mini.build.flash_flags=-DFLASHMODE_DIO d1_mini.build.flash_freq=40 d1_mini.menu.eesz.4M=4M (no SPIFFS) d1_mini.menu.eesz.4M.build.flash_size=4M @@ -3533,14 +3553,15 @@ d1_mini_pro.menu.vt.heap=Heap d1_mini_pro.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM d1_mini_pro.menu.vt.iram=IRAM d1_mini_pro.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM -d1_mini_pro.menu.exception.enabled=Enabled -d1_mini_pro.menu.exception.enabled.build.exception_flags=-fexceptions -d1_mini_pro.menu.exception.enabled.build.stdcpp_lib=-lstdc++ d1_mini_pro.menu.exception.disabled=Disabled d1_mini_pro.menu.exception.disabled.build.exception_flags=-fno-exceptions -d1_mini_pro.menu.exception.disabled.build.stdcpp_lib=-lstdc++-nox +d1_mini_pro.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +d1_mini_pro.menu.exception.enabled=Enabled +d1_mini_pro.menu.exception.enabled.build.exception_flags=-fexceptions +d1_mini_pro.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc d1_mini_pro.upload.resetmethod=nodemcu d1_mini_pro.build.flash_mode=dio +d1_mini_pro.build.flash_flags=-DFLASHMODE_DIO d1_mini_pro.build.flash_freq=40 d1_mini_pro.menu.eesz.16M14M=16M (14M SPIFFS) d1_mini_pro.menu.eesz.16M14M.build.flash_size=16M @@ -3699,14 +3720,15 @@ d1_mini_lite.menu.vt.heap=Heap d1_mini_lite.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM d1_mini_lite.menu.vt.iram=IRAM d1_mini_lite.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM -d1_mini_lite.menu.exception.enabled=Enabled -d1_mini_lite.menu.exception.enabled.build.exception_flags=-fexceptions -d1_mini_lite.menu.exception.enabled.build.stdcpp_lib=-lstdc++ d1_mini_lite.menu.exception.disabled=Disabled d1_mini_lite.menu.exception.disabled.build.exception_flags=-fno-exceptions -d1_mini_lite.menu.exception.disabled.build.stdcpp_lib=-lstdc++-nox +d1_mini_lite.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +d1_mini_lite.menu.exception.enabled=Enabled +d1_mini_lite.menu.exception.enabled.build.exception_flags=-fexceptions +d1_mini_lite.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc d1_mini_lite.upload.resetmethod=nodemcu d1_mini_lite.build.flash_mode=dout +d1_mini_lite.build.flash_flags=-DFLASHMODE_DOUT d1_mini_lite.build.flash_freq=40 d1_mini_lite.menu.eesz.1M=1M (no SPIFFS) d1_mini_lite.menu.eesz.1M.build.flash_size=1M @@ -3922,14 +3944,15 @@ d1.menu.vt.heap=Heap d1.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM d1.menu.vt.iram=IRAM d1.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM -d1.menu.exception.enabled=Enabled -d1.menu.exception.enabled.build.exception_flags=-fexceptions -d1.menu.exception.enabled.build.stdcpp_lib=-lstdc++ d1.menu.exception.disabled=Disabled d1.menu.exception.disabled.build.exception_flags=-fno-exceptions -d1.menu.exception.disabled.build.stdcpp_lib=-lstdc++-nox +d1.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +d1.menu.exception.enabled=Enabled +d1.menu.exception.enabled.build.exception_flags=-fexceptions +d1.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc d1.upload.resetmethod=nodemcu d1.build.flash_mode=dio +d1.build.flash_flags=-DFLASHMODE_DIO d1.build.flash_freq=40 d1.menu.eesz.4M=4M (no SPIFFS) d1.menu.eesz.4M.build.flash_size=4M @@ -4105,17 +4128,18 @@ espino.menu.vt.heap=Heap espino.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM espino.menu.vt.iram=IRAM espino.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM -espino.menu.exception.enabled=Enabled -espino.menu.exception.enabled.build.exception_flags=-fexceptions -espino.menu.exception.enabled.build.stdcpp_lib=-lstdc++ espino.menu.exception.disabled=Disabled espino.menu.exception.disabled.build.exception_flags=-fno-exceptions -espino.menu.exception.disabled.build.stdcpp_lib=-lstdc++-nox +espino.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +espino.menu.exception.enabled=Enabled +espino.menu.exception.enabled.build.exception_flags=-fexceptions +espino.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc espino.menu.ResetMethod.ck=ck espino.menu.ResetMethod.ck.upload.resetmethod=ck espino.menu.ResetMethod.nodemcu=nodemcu espino.menu.ResetMethod.nodemcu.upload.resetmethod=nodemcu espino.build.flash_mode=qio +espino.build.flash_flags=-DFLASHMODE_QIO espino.build.flash_freq=40 espino.menu.eesz.4M=4M (no SPIFFS) espino.menu.eesz.4M.build.flash_size=4M @@ -4291,14 +4315,15 @@ espinotee.menu.vt.heap=Heap espinotee.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM espinotee.menu.vt.iram=IRAM espinotee.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM -espinotee.menu.exception.enabled=Enabled -espinotee.menu.exception.enabled.build.exception_flags=-fexceptions -espinotee.menu.exception.enabled.build.stdcpp_lib=-lstdc++ espinotee.menu.exception.disabled=Disabled espinotee.menu.exception.disabled.build.exception_flags=-fno-exceptions -espinotee.menu.exception.disabled.build.stdcpp_lib=-lstdc++-nox +espinotee.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +espinotee.menu.exception.enabled=Enabled +espinotee.menu.exception.enabled.build.exception_flags=-fexceptions +espinotee.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc espinotee.upload.resetmethod=nodemcu espinotee.build.flash_mode=qio +espinotee.build.flash_flags=-DFLASHMODE_QIO espinotee.build.flash_freq=40 espinotee.menu.eesz.4M=4M (no SPIFFS) espinotee.menu.eesz.4M.build.flash_size=4M @@ -4491,14 +4516,15 @@ wifinfo.menu.vt.heap=Heap wifinfo.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM wifinfo.menu.vt.iram=IRAM wifinfo.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM -wifinfo.menu.exception.enabled=Enabled -wifinfo.menu.exception.enabled.build.exception_flags=-fexceptions -wifinfo.menu.exception.enabled.build.stdcpp_lib=-lstdc++ wifinfo.menu.exception.disabled=Disabled wifinfo.menu.exception.disabled.build.exception_flags=-fno-exceptions -wifinfo.menu.exception.disabled.build.stdcpp_lib=-lstdc++-nox +wifinfo.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +wifinfo.menu.exception.enabled=Enabled +wifinfo.menu.exception.enabled.build.exception_flags=-fexceptions +wifinfo.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc wifinfo.upload.resetmethod=nodemcu wifinfo.build.flash_mode=qio +wifinfo.build.flash_flags=-DFLASHMODE_QIO wifinfo.menu.FlashFreq.40=40MHz wifinfo.menu.FlashFreq.40.build.flash_freq=40 wifinfo.menu.FlashFreq.80=80MHz @@ -4729,14 +4755,15 @@ arduino-esp8266.menu.vt.heap=Heap arduino-esp8266.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM arduino-esp8266.menu.vt.iram=IRAM arduino-esp8266.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM -arduino-esp8266.menu.exception.enabled=Enabled -arduino-esp8266.menu.exception.enabled.build.exception_flags=-fexceptions -arduino-esp8266.menu.exception.enabled.build.stdcpp_lib=-lstdc++ arduino-esp8266.menu.exception.disabled=Disabled arduino-esp8266.menu.exception.disabled.build.exception_flags=-fno-exceptions -arduino-esp8266.menu.exception.disabled.build.stdcpp_lib=-lstdc++-nox +arduino-esp8266.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +arduino-esp8266.menu.exception.enabled=Enabled +arduino-esp8266.menu.exception.enabled.build.exception_flags=-fexceptions +arduino-esp8266.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc arduino-esp8266.upload.resetmethod=ck arduino-esp8266.build.flash_mode=qio +arduino-esp8266.build.flash_flags=-DFLASHMODE_QIO arduino-esp8266.build.flash_freq=40 arduino-esp8266.menu.eesz.4M=4M (no SPIFFS) arduino-esp8266.menu.eesz.4M.build.flash_size=4M @@ -4913,14 +4940,15 @@ gen4iod.menu.vt.heap=Heap gen4iod.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM gen4iod.menu.vt.iram=IRAM gen4iod.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM -gen4iod.menu.exception.enabled=Enabled -gen4iod.menu.exception.enabled.build.exception_flags=-fexceptions -gen4iod.menu.exception.enabled.build.stdcpp_lib=-lstdc++ gen4iod.menu.exception.disabled=Disabled gen4iod.menu.exception.disabled.build.exception_flags=-fno-exceptions -gen4iod.menu.exception.disabled.build.stdcpp_lib=-lstdc++-nox +gen4iod.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +gen4iod.menu.exception.enabled=Enabled +gen4iod.menu.exception.enabled.build.exception_flags=-fexceptions +gen4iod.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc gen4iod.upload.resetmethod=nodemcu gen4iod.build.flash_mode=dio +gen4iod.build.flash_flags=-DFLASHMODE_DIO gen4iod.build.flash_freq=80 gen4iod.menu.eesz.512K=512K (no SPIFFS) gen4iod.menu.eesz.512K.build.flash_size=512K @@ -5097,14 +5125,15 @@ oak.menu.vt.heap=Heap oak.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM oak.menu.vt.iram=IRAM oak.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM -oak.menu.exception.enabled=Enabled -oak.menu.exception.enabled.build.exception_flags=-fexceptions -oak.menu.exception.enabled.build.stdcpp_lib=-lstdc++ oak.menu.exception.disabled=Disabled oak.menu.exception.disabled.build.exception_flags=-fno-exceptions -oak.menu.exception.disabled.build.stdcpp_lib=-lstdc++-nox +oak.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +oak.menu.exception.enabled=Enabled +oak.menu.exception.enabled.build.exception_flags=-fexceptions +oak.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc oak.upload.resetmethod=none oak.build.flash_mode=dio +oak.build.flash_flags=-DFLASHMODE_DIO oak.build.flash_freq=40 oak.menu.eesz.4M=4M (no SPIFFS) oak.menu.eesz.4M.build.flash_size=4M @@ -5280,14 +5309,15 @@ wifiduino.menu.vt.heap=Heap wifiduino.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM wifiduino.menu.vt.iram=IRAM wifiduino.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM -wifiduino.menu.exception.enabled=Enabled -wifiduino.menu.exception.enabled.build.exception_flags=-fexceptions -wifiduino.menu.exception.enabled.build.stdcpp_lib=-lstdc++ wifiduino.menu.exception.disabled=Disabled wifiduino.menu.exception.disabled.build.exception_flags=-fno-exceptions -wifiduino.menu.exception.disabled.build.stdcpp_lib=-lstdc++-nox +wifiduino.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +wifiduino.menu.exception.enabled=Enabled +wifiduino.menu.exception.enabled.build.exception_flags=-fexceptions +wifiduino.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc wifiduino.upload.resetmethod=nodemcu wifiduino.build.flash_mode=dio +wifiduino.build.flash_flags=-DFLASHMODE_DIO wifiduino.build.flash_freq=40 wifiduino.menu.eesz.4M=4M (no SPIFFS) wifiduino.menu.eesz.4M.build.flash_size=4M @@ -5463,25 +5493,29 @@ wifi_slot.menu.vt.heap=Heap wifi_slot.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM wifi_slot.menu.vt.iram=IRAM wifi_slot.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM -wifi_slot.menu.exception.enabled=Enabled -wifi_slot.menu.exception.enabled.build.exception_flags=-fexceptions -wifi_slot.menu.exception.enabled.build.stdcpp_lib=-lstdc++ wifi_slot.menu.exception.disabled=Disabled wifi_slot.menu.exception.disabled.build.exception_flags=-fno-exceptions -wifi_slot.menu.exception.disabled.build.stdcpp_lib=-lstdc++-nox +wifi_slot.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +wifi_slot.menu.exception.enabled=Enabled +wifi_slot.menu.exception.enabled.build.exception_flags=-fexceptions +wifi_slot.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc wifi_slot.upload.resetmethod=nodemcu wifi_slot.menu.FlashFreq.40=40MHz wifi_slot.menu.FlashFreq.40.build.flash_freq=40 wifi_slot.menu.FlashFreq.80=80MHz wifi_slot.menu.FlashFreq.80.build.flash_freq=80 -wifi_slot.menu.FlashMode.qio=QIO -wifi_slot.menu.FlashMode.qio.build.flash_mode=qio -wifi_slot.menu.FlashMode.qout=QOUT -wifi_slot.menu.FlashMode.qout.build.flash_mode=qout +wifi_slot.menu.FlashMode.dout=DOUT (compatible) +wifi_slot.menu.FlashMode.dout.build.flash_mode=dout +wifi_slot.menu.FlashMode.dout.build.flash_flags=-DFLASHMODE_DOUT wifi_slot.menu.FlashMode.dio=DIO wifi_slot.menu.FlashMode.dio.build.flash_mode=dio -wifi_slot.menu.FlashMode.dout=DOUT -wifi_slot.menu.FlashMode.dout.build.flash_mode=dout +wifi_slot.menu.FlashMode.dio.build.flash_flags=-DFLASHMODE_DIO +wifi_slot.menu.FlashMode.qout=QOUT +wifi_slot.menu.FlashMode.qout.build.flash_mode=qout +wifi_slot.menu.FlashMode.qout.build.flash_flags=-DFLASHMODE_QOUT +wifi_slot.menu.FlashMode.qio=QIO (fast) +wifi_slot.menu.FlashMode.qio.build.flash_mode=qio +wifi_slot.menu.FlashMode.qio.build.flash_flags=-DFLASHMODE_QIO wifi_slot.menu.eesz.1M=1M (no SPIFFS) wifi_slot.menu.eesz.1M.build.flash_size=1M wifi_slot.menu.eesz.1M.build.flash_size_bytes=0x100000 @@ -5743,14 +5777,15 @@ wiolink.menu.vt.heap=Heap wiolink.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM wiolink.menu.vt.iram=IRAM wiolink.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM -wiolink.menu.exception.enabled=Enabled -wiolink.menu.exception.enabled.build.exception_flags=-fexceptions -wiolink.menu.exception.enabled.build.stdcpp_lib=-lstdc++ wiolink.menu.exception.disabled=Disabled wiolink.menu.exception.disabled.build.exception_flags=-fno-exceptions -wiolink.menu.exception.disabled.build.stdcpp_lib=-lstdc++-nox +wiolink.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +wiolink.menu.exception.enabled=Enabled +wiolink.menu.exception.enabled.build.exception_flags=-fexceptions +wiolink.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc wiolink.upload.resetmethod=nodemcu wiolink.build.flash_mode=qio +wiolink.build.flash_flags=-DFLASHMODE_QIO wiolink.build.flash_freq=40 wiolink.menu.eesz.4M=4M (no SPIFFS) wiolink.menu.eesz.4M.build.flash_size=4M @@ -5926,14 +5961,15 @@ espectro.menu.vt.heap=Heap espectro.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM espectro.menu.vt.iram=IRAM espectro.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM -espectro.menu.exception.enabled=Enabled -espectro.menu.exception.enabled.build.exception_flags=-fexceptions -espectro.menu.exception.enabled.build.stdcpp_lib=-lstdc++ espectro.menu.exception.disabled=Disabled espectro.menu.exception.disabled.build.exception_flags=-fno-exceptions -espectro.menu.exception.disabled.build.stdcpp_lib=-lstdc++-nox +espectro.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +espectro.menu.exception.enabled=Enabled +espectro.menu.exception.enabled.build.exception_flags=-fexceptions +espectro.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc espectro.upload.resetmethod=nodemcu espectro.build.flash_mode=dio +espectro.build.flash_flags=-DFLASHMODE_DIO espectro.build.flash_freq=40 espectro.menu.eesz.4M=4M (no SPIFFS) espectro.menu.eesz.4M.build.flash_size=4M @@ -6083,4 +6119,3 @@ espectro.menu.baud.512000.windows=512000 espectro.menu.baud.512000.upload.speed=512000 espectro.menu.baud.921600=921600 espectro.menu.baud.921600.upload.speed=921600 - diff --git a/arduino/version 2.5.0/platform.txt b/arduino/version 2.5.0/platform.txt index 39988668a..8e9d8b51c 100644 --- a/arduino/version 2.5.0/platform.txt +++ b/arduino/version 2.5.0/platform.txt @@ -5,11 +5,11 @@ # For more info: # https://github.com/arduino/Arduino/wiki/Arduino-IDE-1.5---3rd-party-Hardware-specification -name=ESP8266 Boards (2.5.0-beta2) -version=2.5.0-beta2 - - +name=ESP8266 Boards (2.5.0) +version=2.5.0 +runtime.tools.xtensa-lx106-elf-gcc.path={runtime.platform.path}/tools/xtensa-lx106-elf +runtime.tools.esptool.path={runtime.platform.path}/tools/esptool runtime.tools.signing={runtime.platform.path}/tools/signing.py compiler.warning_flags=-w @@ -24,10 +24,8 @@ build.lwip_flags=-DLWIP_OPEN_SRC build.vtable_flags=-DVTABLES_IN_FLASH -#build.exception_flags=-fexceptions build.exception_flags=-fno-exceptions -#build.stdcpp_lib=-lstdc++ -build.stdcpp_lib=-lstdc++-nox +build.stdcpp_lib=-lstdc++ #build.float=-u _printf_float -u _scanf_float build.float= @@ -39,7 +37,7 @@ compiler.libc.path={runtime.platform.path}/tools/sdk/libc/xtensa-lx106-elf compiler.cpreprocessor.flags=-D__ets__ -DICACHE_FLASH -U__STRICT_ANSI__ "-I{compiler.sdk.path}/include" "-I{compiler.sdk.path}/{build.lwip_include}" "-I{compiler.libc.path}/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 {build.exception_flags} -ffunction-sections -fdata-sections {build.exception_flags} +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 {build.exception_flags} compiler.S.cmd=xtensa-lx106-elf-gcc compiler.S.flags=-c -g -x assembler-with-cpp -MMD -mlongcalls @@ -80,31 +78,31 @@ compiler.elf2hex.extra_flags= ## generate file with git version number ## needs bash, git, and echo recipe.hooks.core.prebuild.1.pattern=python "{runtime.tools.signing}" --mode header --publickey "{build.source.path}/public.key" --out "{build.path}/core/Updater_Signing.h" - - +recipe.hooks.core.prebuild.2.pattern=bash -c "mkdir -p {build.path}/core && echo \#define ARDUINO_ESP8266_GIT_VER 0x`git --git-dir {runtime.platform.path}/.git rev-parse --short=8 HEAD 2>/dev/null || echo ffffffff` >{build.path}/core/core_version.h" +recipe.hooks.core.prebuild.3.pattern=bash -c "mkdir -p {build.path}/core && echo \#define ARDUINO_ESP8266_GIT_DESC `cd "{runtime.platform.path}"; git describe --tags 2>/dev/null || echo unix-{version}` >>{build.path}/core/core_version.h" ## windows-compatible version without git recipe.hooks.core.prebuild.1.pattern.windows=cmd.exe /c rem cannot sign on windows - - +recipe.hooks.core.prebuild.2.pattern.windows=cmd.exe /c mkdir {build.path}\core & (echo #define ARDUINO_ESP8266_GIT_VER 0x00000000 & echo #define ARDUINO_ESP8266_GIT_DESC win-{version} ) > {build.path}\core\core_version.h +recipe.hooks.core.prebuild.3.pattern.windows=cmd.exe /c if exist {build.source.path}\public.key echo #error Cannot automatically build signed binaries on Windows > {build.path}\core\Updater_Signing.h ## Build the app.ld linker file recipe.hooks.linking.prelink.1.pattern="{compiler.path}{compiler.c.cmd}" -CC -E -P {build.vtable_flags} "{runtime.platform.path}/tools/sdk/ld/eagle.app.v6.common.ld.h" -o "{build.path}/local.eagle.app.v6.common.ld" ## 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}" {build.led} {compiler.c.extra_flags} {build.extra_flags} {includes} "{source_file}" -o "{object_file}" +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}" {build.led} {build.flash_flags} {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}" {build.led} {compiler.cpp.extra_flags} {build.extra_flags} {includes} "{source_file}" -o "{object_file}" +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}" {build.led} {build.flash_flags} {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}" {build.led} {compiler.c.extra_flags} {build.extra_flags} {includes} "{source_file}" -o "{object_file}" +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}" {build.led} {build.flash_flags} {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} "{archive_file_path}" "{object_file}" ## Combine gc-sections, archives, and objects -recipe.c.combine.pattern="{compiler.path}{compiler.c.elf.cmd}" -Wl,-Map "-Wl,{build.path}/{build.project_name}.map" {compiler.c.elf.flags} {compiler.c.elf.extra_flags} -o "{build.path}/{build.project_name}.elf" -Wl,--start-group {object_files} "{archive_file_path}" {compiler.c.elf.libs} -Wl,--end-group "-L{build.path}" +recipe.c.combine.pattern="{compiler.path}{compiler.c.elf.cmd}" {build.exception_flags} -Wl,-Map "-Wl,{build.path}/{build.project_name}.map" {compiler.c.elf.flags} {compiler.c.elf.extra_flags} -o "{build.path}/{build.project_name}.elf" -Wl,--start-group {object_files} "{archive_file_path}" {compiler.c.elf.libs} -Wl,--end-group "-L{build.path}" ## Create eeprom recipe.objcopy.eep.pattern= @@ -154,3 +152,4 @@ tools.espupload.upload.protocol=espupload tools.espupload.upload.params.verbose= tools.espupload.upload.params.quiet= tools.espupload.upload.pattern="{cmd}" "{path}/espupload.py" -f "{build.path}/{build.project_name}.bin" + diff --git a/lib/ArduinoJson-5.11.2/ArduinoJson.h b/lib/ArduinoJson-5.11.2/ArduinoJson.h deleted file mode 100644 index 896503a6c..000000000 --- a/lib/ArduinoJson-5.11.2/ArduinoJson.h +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright Benoit Blanchon 2014-2017 -// MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! - -#include "src/ArduinoJson.h" diff --git a/lib/ArduinoJson-5.11.2/README.md b/lib/ArduinoJson-5.11.2/README.md deleted file mode 100644 index 6ee8a37a6..000000000 --- a/lib/ArduinoJson-5.11.2/README.md +++ /dev/null @@ -1,130 +0,0 @@ -[![Build status](https://ci.appveyor.com/api/projects/status/m7s53wav1l0abssg/branch/master?svg=true)](https://ci.appveyor.com/project/bblanchon/arduinojson/branch/master) [![Build Status](https://travis-ci.org/bblanchon/ArduinoJson.svg?branch=master)](https://travis-ci.org/bblanchon/ArduinoJson) [![Coverage Status](https://img.shields.io/coveralls/bblanchon/ArduinoJson.svg)](https://coveralls.io/r/bblanchon/ArduinoJson?branch=master) [![Star this project](http://githubbadges.com/star.svg?user=bblanchon&repo=ArduinoJson&style=flat&color=fff&background=007ec6)](https://github.com/bblanchon/ArduinoJson) - -![ArduinoJson's logo](banner.svg) - -ArduinoJson - C++ JSON library for IoT -==================== - -*An elegant and efficient JSON library for embedded systems.* - -It's designed to have the most intuitive API, the smallest footprint and is able to work without any allocation on the heap (no malloc). - -It has been written with Arduino in mind, but it isn't linked to Arduino libraries so you can use this library in any other C++ project. -For instance, it supports Aduino's `String` and `Stream`, but also `std::string`, `std::istream` and `std::ostream`. - -Features --------- - -* JSON decoding (comments are supported) -* JSON encoding (with optional indentation) -* Elegant API, very easy to use -* Fixed memory allocation (zero malloc) -* No data duplication (zero copy) -* Portable (written in C++98) -* Self-contained (no external dependency) -* Small footprint -* Header-only library -* MIT License - -Works on --------- - -* Arduino boards: Uno, Due, Mini, Micro, Yun... -* ESP8266, ESP32 -* Teensy -* RedBearLab boards (BLE Nano...) -* Intel Edison and Galileo -* WeMos boards: D1... -* Computers: Windows, Linux, OSX... -* PlatformIO -* Particle -* Energia - -Quick start ------------ - -#### Decoding / Parsing - -```c++ -char json[] = "{\"sensor\":\"gps\",\"time\":1351824120,\"data\":[48.756080,2.302038]}"; - -StaticJsonBuffer<200> jsonBuffer; - -JsonObject& root = jsonBuffer.parseObject(json); - -const char* sensor = root["sensor"]; -long time = root["time"]; -double latitude = root["data"][0]; -double longitude = root["data"][1]; -``` - -[See JsonParserExample.ino](examples/JsonParserExample/JsonParserExample.ino) - -Use [ArduinoJson Assistant](https://bblanchon.github.io/ArduinoJson/assistant/) to compute the buffer size. - -#### Encoding / Generating - -```c++ -StaticJsonBuffer<200> jsonBuffer; - -JsonObject& root = jsonBuffer.createObject(); -root["sensor"] = "gps"; -root["time"] = 1351824120; - -JsonArray& data = root.createNestedArray("data"); -data.add(48.756080); -data.add(2.302038); - -root.printTo(Serial); -// This prints: -// {"sensor":"gps","time":1351824120,"data":[48.756080,2.302038]} -``` - -[See JsonGeneratorExample.ino](examples/JsonGeneratorExample/JsonGeneratorExample.ino) - -Use [ArduinoJson Assistant](https://bblanchon.github.io/ArduinoJson/assistant/) to compute the buffer size. - - -Documentation -------------- - -The documentation is available online in the [ArduinoJson Website](https://bblanchon.github.io/ArduinoJson/). - -The [ArduinoJson Assistant](https://bblanchon.github.io/ArduinoJson/assistant/) helps you get started with the library. - - -Donators --------- - -Special thanks to the following persons and companies who made generous donations to the library author: - -* Robert Murphy USA -* Surge Communications USA -* Alex Scott United Kingdom -* Firepick Services LLC USA -* A B Doodkorte Netherlands -* Scott Smith USA -* Johann Stieger Austria -* Gustavo Donizeti Gini Brazil -* Charles-Henri Hallard France -* Martijn van den Burg Netherlands -* Nick Koumaris Greece -* Jon Williams USA -* Kestutis Liaugminas Lithuania -* Darlington Adibe Nigeria -* Yoeri Kroon Netherlands -* Andrew Melvin United Kingdom -* Doanh Luong Vietnam -* Christoph Schmidt Germany -* OpenEVSE LLC USA -* Prokhoryatov Alexey Russia -* Google Inc. USA -* Charles Haynes Australia -* Charles Walker USA -* Günther Jehle Liechtenstein -* Patrick Elliott - - ---- - -Found this library useful? Please star this project or [help me back with a donation!](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=donate%40benoitblanchon%2efr&lc=GB&item_name=Benoit%20Blanchon&item_number=Arduino%20JSON¤cy_code=EUR&bn=PP%2dDonationsBF%3abtn_donate_LG%2egif%3aNonHosted) :smile: diff --git a/lib/ArduinoJson-5.11.2/examples/JsonHttpClient/JsonHttpClient.ino b/lib/ArduinoJson-5.11.2/examples/JsonHttpClient/JsonHttpClient.ino deleted file mode 100644 index 5edb817f1..000000000 --- a/lib/ArduinoJson-5.11.2/examples/JsonHttpClient/JsonHttpClient.ino +++ /dev/null @@ -1,184 +0,0 @@ -// Sample Arduino Json Web Client -// Downloads and parse http://jsonplaceholder.typicode.com/users/1 -// -// Copyright Benoit Blanchon 2014-2017 -// MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! - -#include -#include -#include - -EthernetClient client; - -const char* server = "jsonplaceholder.typicode.com"; // server's address -const char* resource = "/users/1"; // http resource -const unsigned long BAUD_RATE = 9600; // serial connection speed -const unsigned long HTTP_TIMEOUT = 10000; // max respone time from server -const size_t MAX_CONTENT_SIZE = 512; // max size of the HTTP response - -// The type of data that we want to extract from the page -struct UserData { - char name[32]; - char company[32]; -}; - -// ARDUINO entry point #1: runs once when you press reset or power the board -void setup() { - initSerial(); - initEthernet(); -} - -// ARDUINO entry point #2: runs over and over again forever -void loop() { - if (connect(server)) { - if (sendRequest(server, resource) && skipResponseHeaders()) { - UserData userData; - if (readReponseContent(&userData)) { - printUserData(&userData); - } - } - } - disconnect(); - wait(); -} - -// Initialize Serial port -void initSerial() { - Serial.begin(BAUD_RATE); - while (!Serial) { - ; // wait for serial port to initialize - } - Serial.println("Serial ready"); -} - -// Initialize Ethernet library -void initEthernet() { - byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; - if (!Ethernet.begin(mac)) { - Serial.println("Failed to configure Ethernet"); - return; - } - Serial.println("Ethernet ready"); - delay(1000); -} - -// Open connection to the HTTP server -bool connect(const char* hostName) { - Serial.print("Connect to "); - Serial.println(hostName); - - bool ok = client.connect(hostName, 80); - - Serial.println(ok ? "Connected" : "Connection Failed!"); - return ok; -} - -// Send the HTTP GET request to the server -bool sendRequest(const char* host, const char* resource) { - Serial.print("GET "); - Serial.println(resource); - - client.print("GET "); - client.print(resource); - client.println(" HTTP/1.0"); - client.print("Host: "); - client.println(host); - client.println("Connection: close"); - client.println(); - - return true; -} - -// Skip HTTP headers so that we are at the beginning of the response's body -bool skipResponseHeaders() { - // HTTP headers end with an empty line - char endOfHeaders[] = "\r\n\r\n"; - - client.setTimeout(HTTP_TIMEOUT); - bool ok = client.find(endOfHeaders); - - if (!ok) { - Serial.println("No response or invalid response!"); - } - - return ok; -} - -// Parse the JSON from the input string and extract the interesting values -// Here is the JSON we need to parse -// { -// "id": 1, -// "name": "Leanne Graham", -// "username": "Bret", -// "email": "Sincere@april.biz", -// "address": { -// "street": "Kulas Light", -// "suite": "Apt. 556", -// "city": "Gwenborough", -// "zipcode": "92998-3874", -// "geo": { -// "lat": "-37.3159", -// "lng": "81.1496" -// } -// }, -// "phone": "1-770-736-8031 x56442", -// "website": "hildegard.org", -// "company": { -// "name": "Romaguera-Crona", -// "catchPhrase": "Multi-layered client-server neural-net", -// "bs": "harness real-time e-markets" -// } -// } -bool readReponseContent(struct UserData* userData) { - // Compute optimal size of the JSON buffer according to what we need to parse. - // See https://bblanchon.github.io/ArduinoJson/assistant/ - const size_t BUFFER_SIZE = - JSON_OBJECT_SIZE(8) // the root object has 8 elements - + JSON_OBJECT_SIZE(5) // the "address" object has 5 elements - + JSON_OBJECT_SIZE(2) // the "geo" object has 2 elements - + JSON_OBJECT_SIZE(3) // the "company" object has 3 elements - + MAX_CONTENT_SIZE; // additional space for strings - - // Allocate a temporary memory pool - DynamicJsonBuffer jsonBuffer(BUFFER_SIZE); - - JsonObject& root = jsonBuffer.parseObject(client); - - if (!root.success()) { - Serial.println("JSON parsing failed!"); - return false; - } - - // Here were copy the strings we're interested in - strcpy(userData->name, root["name"]); - strcpy(userData->company, root["company"]["name"]); - // It's not mandatory to make a copy, you could just use the pointers - // Since, they are pointing inside the "content" buffer, so you need to make - // sure it's still in memory when you read the string - - return true; -} - -// Print the data extracted from the JSON -void printUserData(const struct UserData* userData) { - Serial.print("Name = "); - Serial.println(userData->name); - Serial.print("Company = "); - Serial.println(userData->company); -} - -// Close the connection with the HTTP server -void disconnect() { - Serial.println("Disconnect"); - client.stop(); -} - -// Pause for a 1 minute -void wait() { - Serial.println("Wait 60 seconds"); - delay(60000); -} diff --git a/lib/ArduinoJson-5.11.2/examples/JsonServer/JsonServer.ino b/lib/ArduinoJson-5.11.2/examples/JsonServer/JsonServer.ino deleted file mode 100644 index 555842b82..000000000 --- a/lib/ArduinoJson-5.11.2/examples/JsonServer/JsonServer.ino +++ /dev/null @@ -1,76 +0,0 @@ -// Sample Arduino Json Web Server -// Created by Benoit Blanchon. -// Heavily inspired by "Web Server" from David A. Mellis and Tom Igoe - -#include -#include -#include - -byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; -IPAddress ip(192, 168, 0, 177); -EthernetServer server(80); - -bool readRequest(EthernetClient& client) { - bool currentLineIsBlank = true; - while (client.connected()) { - if (client.available()) { - char c = client.read(); - if (c == '\n' && currentLineIsBlank) { - return true; - } else if (c == '\n') { - currentLineIsBlank = true; - } else if (c != '\r') { - currentLineIsBlank = false; - } - } - } - return false; -} - -JsonObject& prepareResponse(JsonBuffer& jsonBuffer) { - JsonObject& root = jsonBuffer.createObject(); - - JsonArray& analogValues = root.createNestedArray("analog"); - for (int pin = 0; pin < 6; pin++) { - int value = analogRead(pin); - analogValues.add(value); - } - - JsonArray& digitalValues = root.createNestedArray("digital"); - for (int pin = 0; pin < 14; pin++) { - int value = digitalRead(pin); - digitalValues.add(value); - } - - return root; -} - -void writeResponse(EthernetClient& client, JsonObject& json) { - client.println("HTTP/1.1 200 OK"); - client.println("Content-Type: application/json"); - client.println("Connection: close"); - client.println(); - - json.prettyPrintTo(client); -} - -void setup() { - Ethernet.begin(mac, ip); - server.begin(); -} - -void loop() { - EthernetClient client = server.available(); - if (client) { - bool success = readRequest(client); - if (success) { - // Use https://bblanchon.github.io/ArduinoJson/assistant/ to - // compute the right size for the buffer - StaticJsonBuffer<500> jsonBuffer; - JsonObject& json = prepareResponse(jsonBuffer); - writeResponse(client, json); - } - delay(1); - client.stop(); - } -} diff --git a/lib/ArduinoJson-5.11.2/examples/JsonUdpBeacon/JsonUdpBeacon.ino b/lib/ArduinoJson-5.11.2/examples/JsonUdpBeacon/JsonUdpBeacon.ino deleted file mode 100644 index b3bd5fc50..000000000 --- a/lib/ArduinoJson-5.11.2/examples/JsonUdpBeacon/JsonUdpBeacon.ino +++ /dev/null @@ -1,57 +0,0 @@ -// Send a JSON object on UDP at regular interval -// -// You can easily test this program with netcat: -// $ nc -ulp 8888 -// -// by Benoit Blanchon, MIT License 2015-2017 - -#include -#include -#include - -byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; -IPAddress localIp(192, 168, 0, 177); -IPAddress remoteIp(192, 168, 0, 109); -unsigned int remotePort = 8888; -unsigned localPort = 8888; -EthernetUDP udp; - -JsonObject& buildJson(JsonBuffer& jsonBuffer) { - JsonObject& root = jsonBuffer.createObject(); - - JsonArray& analogValues = root.createNestedArray("analog"); - for (int pin = 0; pin < 6; pin++) { - int value = analogRead(pin); - analogValues.add(value); - } - - JsonArray& digitalValues = root.createNestedArray("digital"); - for (int pin = 0; pin < 14; pin++) { - int value = digitalRead(pin); - digitalValues.add(value); - } - - return root; -} - -void sendJson(JsonObject& json) { - udp.beginPacket(remoteIp, remotePort); - json.printTo(udp); - udp.println(); - udp.endPacket(); -} - -void setup() { - Ethernet.begin(mac, localIp); - udp.begin(localPort); -} - -void loop() { - delay(1000); - - // Use https://bblanchon.github.io/ArduinoJson/assistant/ to - // compute the right size for the buffer - StaticJsonBuffer<300> jsonBuffer; - JsonObject& json = buildJson(jsonBuffer); - sendJson(json); -} diff --git a/lib/ArduinoJson-5.11.2/library.properties b/lib/ArduinoJson-5.11.2/library.properties deleted file mode 100644 index 9809a3c0b..000000000 --- a/lib/ArduinoJson-5.11.2/library.properties +++ /dev/null @@ -1,9 +0,0 @@ -name=ArduinoJson -version=5.11.2 -author=Benoit Blanchon -maintainer=Benoit Blanchon -sentence=An efficient and elegant JSON library for Arduino. -paragraph=Like this project? Please star it on GitHub! -category=Data Processing -url=https://bblanchon.github.io/ArduinoJson/ -architectures=* diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson.h b/lib/ArduinoJson-5.11.2/src/ArduinoJson.h deleted file mode 100644 index c1ec7c02f..000000000 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson.h +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright Benoit Blanchon 2014-2017 -// MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! - -#pragma once - -#include "ArduinoJson.hpp" - -using namespace ArduinoJson; diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Data/ValueSetter.hpp b/lib/ArduinoJson-5.11.2/src/ArduinoJson/Data/ValueSetter.hpp deleted file mode 100644 index 7eb3ed63b..000000000 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Data/ValueSetter.hpp +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright Benoit Blanchon 2014-2017 -// MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! - -#pragma once - -#include "../JsonBuffer.hpp" -#include "../JsonVariant.hpp" -#include "../StringTraits/StringTraits.hpp" -#include "../TypeTraits/EnableIf.hpp" - -namespace ArduinoJson { -namespace Internals { - -template -struct ValueSetter { - template - static bool set(JsonBuffer*, TDestination& destination, TSourceRef source) { - destination = source; - return true; - } -}; - -template -struct ValueSetter::should_duplicate>::type> { - template - static bool set(JsonBuffer* buffer, TDestination& destination, - TSourceRef source) { - const char* copy = buffer->strdup(source); - if (!copy) return false; - destination = copy; - return true; - } -}; - -template -struct ValueSetter::should_duplicate>::type> { - template - static bool set(JsonBuffer*, TDestination& destination, TSourceRef source) { - // unsigned char* -> char* - destination = reinterpret_cast(source); - return true; - } -}; -} -} diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Polyfills/isInteger.hpp b/lib/ArduinoJson-5.11.2/src/ArduinoJson/Polyfills/isInteger.hpp deleted file mode 100644 index ea39f2a63..000000000 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Polyfills/isInteger.hpp +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright Benoit Blanchon 2014-2017 -// MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! - -#pragma once - -#include "./ctype.hpp" - -namespace ArduinoJson { -namespace Polyfills { - -inline bool isInteger(const char* s) { - if (!s) return false; - if (issign(*s)) s++; - while (isdigit(*s)) s++; - return *s == '\0'; -} -} -} diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/RawJson.hpp b/lib/ArduinoJson-5.11.2/src/ArduinoJson/RawJson.hpp deleted file mode 100644 index 6db195c32..000000000 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/RawJson.hpp +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright Benoit Blanchon 2014-2017 -// MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! - -#pragma once - -namespace ArduinoJson { - -// A special type of data that can be used to insert pregenerated JSON portions. -class RawJson { - public: - explicit RawJson(const char* str) : _str(str) {} - operator const char*() const { - return _str; - } - - private: - const char* _str; -}; -} diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/TypeTraits/IsSignedIntegral.hpp b/lib/ArduinoJson-5.11.2/src/ArduinoJson/TypeTraits/IsSignedIntegral.hpp deleted file mode 100644 index fde6e13b0..000000000 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/TypeTraits/IsSignedIntegral.hpp +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright Benoit Blanchon 2014-2017 -// MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! - -#pragma once - -#include "../Configuration.hpp" -#include "IsSame.hpp" - -namespace ArduinoJson { -namespace TypeTraits { - -// A meta-function that returns true if T is an integral type. -template -struct IsSignedIntegral { - static const bool value = TypeTraits::IsSame::value || - TypeTraits::IsSame::value || - TypeTraits::IsSame::value || - TypeTraits::IsSame::value || -#if ARDUINOJSON_USE_LONG_LONG - TypeTraits::IsSame::value || -#endif - -#if ARDUINOJSON_USE_INT64 - TypeTraits::IsSame::value || -#endif - false; -}; -} -} diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/TypeTraits/IsUnsignedIntegral.hpp b/lib/ArduinoJson-5.11.2/src/ArduinoJson/TypeTraits/IsUnsignedIntegral.hpp deleted file mode 100644 index 173763e05..000000000 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/TypeTraits/IsUnsignedIntegral.hpp +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright Benoit Blanchon 2014-2017 -// MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! - -#pragma once - -#include "../Configuration.hpp" -#include "IsSame.hpp" - -namespace ArduinoJson { -namespace TypeTraits { - -// A meta-function that returns true if T is an integral type. -template -struct IsUnsignedIntegral { - static const bool value = TypeTraits::IsSame::value || - TypeTraits::IsSame::value || - TypeTraits::IsSame::value || - TypeTraits::IsSame::value || -#if ARDUINOJSON_USE_LONG_LONG - TypeTraits::IsSame::value || -#endif - -#if ARDUINOJSON_USE_INT64 - TypeTraits::IsSame::value || -#endif - false; -}; -} -} diff --git a/lib/ArduinoJson-5.13.4/ArduinoJson.h b/lib/ArduinoJson-5.13.4/ArduinoJson.h new file mode 100644 index 000000000..9f78b9f18 --- /dev/null +++ b/lib/ArduinoJson-5.13.4/ArduinoJson.h @@ -0,0 +1,5 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#include "src/ArduinoJson.h" diff --git a/lib/ArduinoJson-5.11.2/CHANGELOG.md b/lib/ArduinoJson-5.13.4/CHANGELOG.md similarity index 68% rename from lib/ArduinoJson-5.11.2/CHANGELOG.md rename to lib/ArduinoJson-5.13.4/CHANGELOG.md index 490e25c8c..3616b176a 100644 --- a/lib/ArduinoJson-5.11.2/CHANGELOG.md +++ b/lib/ArduinoJson-5.13.4/CHANGELOG.md @@ -1,6 +1,78 @@ ArduinoJson: change log ======================= +v5.13.4 +------- + +* Removed spurious files in the Particle library + +v5.13.3 +------- + +* Improved float serialization when `-fsingle-precision-constant` is used +* Fixed `JsonVariant::is()` that returned true for empty strings +* Fixed `JsonVariant::is()` (closes #763) + +v5.13.2 +------- + +* Fixed `JsonBuffer::parse()` not respecting nesting limit correctly (issue #693) +* Fixed inconsistencies in nesting level counting (PR #695 from Zhenyu Wu) +* Fixed null values that could be pass to `strcmp()` (PR #745 from Mike Karlesky) +* Added macros `ARDUINOJSON_VERSION`, `ARDUINOJSON_VERSION_MAJOR`... + +v5.13.1 +------- + +* Fixed `JsonVariant::operator|(int)` that returned the default value if the variant contained a double (issue #675) +* Allowed non-quoted key to contain underscores (issue #665) + +v5.13.0 +------- + +* Changed the rules of string duplication (issue #658) +* `RawJson()` accepts any kind of string and obeys to the same rules for duplication +* Changed the return type of `strdup()` to `const char*` to prevent double duplication +* Marked `strdup()` as deprecated + +> ### New rules for string duplication +> +> | type | duplication | +> |:---------------------------|:------------| +> | const char* | no | +> | char* | ~~no~~ yes | +> | String | yes | +> | std::string | yes | +> | const __FlashStringHelper* | yes | +> +> These new rules make `JsonBuffer::strdup()` useless. + +v5.12.0 +------- + +* Added `JsonVariant::operator|` to return a default value (see below) +* Added a clear error message when compiled as C instead of C++ (issue #629) +* Added detection of MPLAB XC compiler (issue #629) +* Added detection of Keil ARM Compiler (issue #629) +* Added an example that shows how to save and load a configuration file +* Reworked all other examples + +> ### How to use the new feature? +> +> If you have a block like this: +> +> ```c++ +> const char* ssid = root["ssid"]; +> if (!ssid) +> ssid = "default ssid"; +> ``` +> +> You can simplify like that: +> +> ```c++ +> const char* ssid = root["ssid"] | "default ssid"; +> ``` + v5.11.2 ------- @@ -42,27 +114,26 @@ v5.10.0 * Fixed error `IsBaseOf is not a member of ArduinoJson::TypeTraits` (issue #495) * Fixed error `forming reference to reference` (issue #495) -### BREAKING CHANGES :warning: - -| Old syntax | New syntax | -|---------------------------------|---------------------| -| `double_with_n_digits(3.14, 2)` | `3.14` | -| `float_with_n_digits(3.14, 2)` | `3.14f` | -| `obj.set("key", 3.14, 2)` | `obj["key"] = 3.14` | -| `arr.add(3.14, 2)` | `arr.add(3.14)` | - -| Input | Old output | New output | -|-----------|------------|------------| -| `3.14159` | `3.14` | `3.14159` | -| `42.0` | `42.00` | `42` | -| `0.0` | `0.00` | `0` | - -| Expression | Old result | New result | -|--------------------------------|------------|------------| -| `JsonVariant(42).is()` | `true` | `true` | -| `JsonVariant(42).is()` | `false` | `true` | -| `JsonVariant(42).is()` | `false` | `true` | - +> ### BREAKING CHANGES :warning: +> +> | Old syntax | New syntax | +> |:--------------------------------|:--------------------| +> | `double_with_n_digits(3.14, 2)` | `3.14` | +> | `float_with_n_digits(3.14, 2)` | `3.14f` | +> | `obj.set("key", 3.14, 2)` | `obj["key"] = 3.14` | +> | `arr.add(3.14, 2)` | `arr.add(3.14)` | +> +> | Input | Old output | New output | +> |:----------|:-----------|:-----------| +> | `3.14159` | `3.14` | `3.14159` | +> | `42.0` | `42.00` | `42` | +> | `0.0` | `0.00` | `0` | +> +> | Expression | Old result | New result | +> |:-------------------------------|:-----------|:-----------| +> | `JsonVariant(42).is()` | `true` | `true` | +> | `JsonVariant(42).is()` | `false` | `true` | +> | `JsonVariant(42).is()` | `false` | `true` | v5.9.0 ------ @@ -116,24 +187,23 @@ v5.8.0 * Added support for `Stream` (issue #300) * Reduced memory consumption by not duplicating spaces and comments -### BREAKING CHANGES :warning: - -`JsonBuffer::parseObject()` and `JsonBuffer::parseArray()` have been pulled down to the derived classes `DynamicJsonBuffer` and `StaticJsonBufferBase`. - -This means that if you have code like: - -```c++ -void myFunction(JsonBuffer& jsonBuffer); -``` - -you need to replace it with one of the following: - -```c++ -void myFunction(DynamicJsonBuffer& jsonBuffer); -void myFunction(StaticJsonBufferBase& jsonBuffer); -template void myFunction(TJsonBuffer& jsonBuffer); -``` - +> ### BREAKING CHANGES :warning: +> +> `JsonBuffer::parseObject()` and `JsonBuffer::parseArray()` have been pulled down to the derived classes `DynamicJsonBuffer` and `StaticJsonBufferBase`. +> +> This means that if you have code like: +> +> ```c++ +> void myFunction(JsonBuffer& jsonBuffer); +> ``` +> +> you need to replace it with one of the following: +> +> ```c++ +> void myFunction(DynamicJsonBuffer& jsonBuffer); +> void myFunction(StaticJsonBufferBase& jsonBuffer); +> template void myFunction(TJsonBuffer& jsonBuffer); +> ``` v5.7.3 ------ @@ -166,27 +236,26 @@ v5.7.0 * Added example `StringExample.ino` to show where `String` can be used * Increased default nesting limit to 50 when compiled for a computer (issue #349) -### BREAKING CHANGES :warning: - -The non-template functions `JsonObject::get()` and `JsonArray.get()` have been removed. This means that you need to explicitely tell the type you expect in return. - -Old code: - -```c++ -#define ARDUINOJSON_USE_ARDUINO_STRING 0 -JsonVariant value1 = myObject.get("myKey"); -JsonVariant value2 = myArray.get(0); -``` - -New code: - -```c++ -#define ARDUINOJSON_ENABLE_ARDUINO_STRING 0 -#define ARDUINOJSON_ENABLE_STD_STRING 1 -JsonVariant value1 = myObject.get("myKey"); -JsonVariant value2 = myArray.get(0); -``` - +> ### BREAKING CHANGES :warning: +> +> The non-template functions `JsonObject::get()` and `JsonArray.get()` have been removed. This means that you need to explicitely tell the type you expect in return. +> +> Old code: +> +> ```c++ +> #define ARDUINOJSON_USE_ARDUINO_STRING 0 +> JsonVariant value1 = myObject.get("myKey"); +> JsonVariant value2 = myArray.get(0); +> ``` +> +> New code: +> +> ```c++ +> #define ARDUINOJSON_ENABLE_ARDUINO_STRING 0 +> #define ARDUINOJSON_ENABLE_STD_STRING 1 +> JsonVariant value1 = myObject.get("myKey"); +> JsonVariant value2 = myArray.get(0); +> ``` v5.6.7 ------ @@ -278,8 +347,9 @@ v5.1.0 * Added support of `long long` (issue #171) * Moved all build settings to `ArduinoJson/Configuration.hpp` -**BREAKING CHANGE**: -If you defined `ARDUINOJSON_ENABLE_STD_STREAM`, you now need to define it to `1`. +> ### BREAKING CHANGE :warning: +> +> If you defined `ARDUINOJSON_ENABLE_STD_STREAM`, you now need to define it to `1`. v5.0.8 ------ @@ -293,10 +363,10 @@ v5.0.7 * Made library easier to use from a CMake project: simply `add_subdirectory(ArduinoJson/src)` * Changed `String` to be a `typedef` of `std::string` (issues #142 and #161) -### BREAKING CHANGES :warning: - -- `JsonVariant(true).as()` now returns `"true"` instead of `"1"` -- `JsonVariant(false).as()` now returns `"false"` instead of `"0"` +> ### BREAKING CHANGES :warning: +> +> - `JsonVariant(true).as()` now returns `"true"` instead of `"1"` +> - `JsonVariant(false).as()` now returns `"false"` instead of `"0"` v5.0.6 ------ @@ -350,11 +420,11 @@ v5.0.0 * Redesigned `JsonVariant` to leverage converting constructors instead of assignment operators (issue #66) * Switched to new the library layout (requires Arduino 1.0.6 or above) -### BREAKING CHANGES :warning: - -- `JsonObject::add()` was renamed to `set()` -- `JsonArray::at()` and `JsonObject::at()` were renamed to `get()` -- Number of digits of floating point value are now set with `double_with_n_digits()` +> ### BREAKING CHANGES :warning: +> +> - `JsonObject::add()` was renamed to `set()` +> - `JsonArray::at()` and `JsonObject::at()` were renamed to `get()` +> - Number of digits of floating point value are now set with `double_with_n_digits()` **Personal note about the `String` class**: Support of the `String` class has been added to the library because many people use it in their programs. @@ -407,106 +477,7 @@ v4.0 * Unified parser and generator API (issue #23) * Updated library layout, now requires Arduino 1.0.6 or newer -**BREAKING CHANGE**: API changed significantly, see [Migrating code to the new API](https://github.com/bblanchon/ArduinoJson/wiki/Migrating-code-to-the-new-API). +> ### BREAKING CHANGES :warning: +> +> API changed significantly since v3, see [Migrating code to the new API](https://arduinojson.org/doc/migration/). - -v3.4 ----- - -* Fixed escaped char parsing (issue #16) - - -v3.3 ----- - -* Added indented output for the JSON generator (issue #11), see example bellow. -* Added `IndentedPrint`, a decorator for `Print` to allow indented output - -Example: - - JsonOject<2> json; - json["key"] = "value"; - json.prettyPrintTo(Serial); - -v3.2 ----- - -* Fixed a bug when adding nested object in `JsonArray` (bug introduced in v3.1). - -v3.1 ----- - -* Calling `Generator::JsonObject::add()` twice with the same `key` now replaces the `value` -* Added `Generator::JsonObject::operator[]`, see bellow the new API -* Added `Generator::JsonObject::remove()` (issue #9) - -Old generator API: - - JsonObject<3> root; - root.add("sensor", "gps"); - root.add("time", 1351824120); - root.add("data", array); - -New generator API: - - JsonObject<3> root; - root["sensor"] = "gps"; - root["time"] = 1351824120; - root["data"] = array; - -v3.0 ----- - -* New parser API, see bellow -* Renamed `JsonHashTable` into `JsonObject` -* Added iterators for `JsonArray` and `JsonObject` (issue #4) - -Old parser API: - - JsonHashTable root = parser.parseHashTable(json); - - char* sensor = root.getString("sensor"); - long time = root.getLong("time"); - double latitude = root.getArray("data").getDouble(0); - double longitude = root.getArray("data").getDouble(1); - -New parser API: - - JsonObject root = parser.parse(json); - - char* sensor = root["sensor"]; - long time = root["time"]; - double latitude = root["data"][0]; - double longitude = root["data"][1]; - -v2.1 ----- - -* Fixed case `#include "jsmn.cpp"` which caused an error in Linux (issue #6) -* Fixed a buffer overrun in JSON Parser (issue #5) - -v2.0 ----- - -* Added JSON encoding (issue #2) -* Renamed the library `ArduinoJsonParser` becomes `ArduinoJson` - -**Breaking change**: you need to add the following line at the top of your program. - - using namespace ArduinoJson::Parser; - -v1.2 ----- - -* Fixed error in JSON parser example (issue #1) - -v1.1 ----- - -* Example: changed `char* json` into `char[] json` so that the bytes are not write protected -* Fixed parsing bug when the JSON contains multi-dimensional arrays - -v1.0 ----- - -Initial release diff --git a/lib/ArduinoJson-5.11.2/LICENSE.md b/lib/ArduinoJson-5.13.4/LICENSE.md similarity index 96% rename from lib/ArduinoJson-5.11.2/LICENSE.md rename to lib/ArduinoJson-5.13.4/LICENSE.md index 9f35ed446..f0c4b5ae7 100644 --- a/lib/ArduinoJson-5.11.2/LICENSE.md +++ b/lib/ArduinoJson-5.13.4/LICENSE.md @@ -1,7 +1,7 @@ The MIT License (MIT) --------------------- -Copyright © 2014-2017 Benoit BLANCHON +Copyright © 2014-2018 Benoit BLANCHON Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: diff --git a/lib/ArduinoJson-5.13.4/README.md b/lib/ArduinoJson-5.13.4/README.md new file mode 100644 index 000000000..8ddc698fc --- /dev/null +++ b/lib/ArduinoJson-5.13.4/README.md @@ -0,0 +1,110 @@ +![ArduinoJson](banner.svg) + +--- + +[![Build status](https://ci.appveyor.com/api/projects/status/m7s53wav1l0abssg/branch/master?svg=true)](https://ci.appveyor.com/project/bblanchon/arduinojson/branch/master) [![Build Status](https://travis-ci.org/bblanchon/ArduinoJson.svg?branch=master)](https://travis-ci.org/bblanchon/ArduinoJson) [![Coverage Status](https://img.shields.io/coveralls/bblanchon/ArduinoJson.svg)](https://coveralls.io/r/bblanchon/ArduinoJson?branch=master) [![Star this project](http://githubbadges.com/star.svg?user=bblanchon&repo=ArduinoJson&style=flat&color=fff&background=007ec6)](https://github.com/bblanchon/ArduinoJson) + +ArduinoJson is a C++ JSON library for Arduino and IoT (Internet Of Things). + +## Features + +* JSON decoding (comments are supported) +* JSON encoding (with optional indentation) +* Elegant API, easy to use +* Fixed memory allocation (zero malloc) +* No data duplication (zero copy) +* Portable (written in C++98, can be used in any C++ project) +* Self-contained (no external dependency) +* Small footprint +* Input and output streams +* [100% code coverage](https://coveralls.io/github/bblanchon/ArduinoJson) +* [Header-only library](https://en.wikipedia.org/wiki/Header-only) +* [MIT License](https://en.wikipedia.org/wiki/MIT_License) +* [Comprehensive documentation](https://arduinojson.org?utm_source=github&utm_medium=readme) + +## Compatibility + +ArduinoJson works on the following hardware: + +* Arduino boards: [Uno](https://www.arduino.cc/en/Main/ArduinoBoardUno), [Due](https://www.arduino.cc/en/Main/ArduinoBoardDue), [Mini](https://www.arduino.cc/en/Main/ArduinoBoardMini), [Micro](https://www.arduino.cc/en/Main/ArduinoBoardMicro), [Yun](https://www.arduino.cc/en/Main/ArduinoBoardYun)... +* Espressif chips: [ESP8266](https://en.wikipedia.org/wiki/ESP8266), [ESP32](https://en.wikipedia.org/wiki/ESP32) +* WeMos boards: [D1](https://wiki.wemos.cc/products:d1:d1), [D1 mini](https://wiki.wemos.cc/products:d1:d1_mini), ... +* RedBearLab boards: [BLE Nano](http://redbearlab.com/blenano/), [BLE Mini](http://redbearlab.com/blemini/), [WiFi Micro](https://redbear.cc/product/wifi/wifi-micro.html), [LOLIN32](https://wiki.wemos.cc/products:lolin32:lolin32)... +* [Teensy](https://www.pjrc.com/teensy/) boards +* Intel boards: Edison, Galileo... +* Particle boards: [Photon](https://www.particle.io/products/hardware/photon-wifi-dev-kit), [Electron](https://www.particle.io/products/hardware/electron-cellular-dev-kit)... +* Texas Instruments boards: [MSP430](http://www.ti.com/microcontrollers/msp430-ultra-low-power-mcus/overview/overview.html)... + +ArduinoJson compiles with zero warning on the following compilers, IDEs, and platforms: + +* [Arduino IDE](https://www.arduino.cc/en/Main/Software) +* [PlatformIO](http://platformio.org/) +* [Energia](http://energia.nu/) +* [Visual Micro](http://www.visualmicro.com/) +* [Atmel Studio](http://www.atmel.com/microsite/atmel-studio/) +* [IAR Embedded Workbench](https://www.iar.com/iar-embedded-workbench/) +* [Atollic TrueSTUDIO](https://atollic.com/truestudio/) +* [Keil uVision](http://www.keil.com/) +* [MPLAB X IDE](http://www.microchip.com/mplab/mplab-x-ide) +* [GCC](https://gcc.gnu.org/) +* [Clang](https://clang.llvm.org/) +* [Visual Studio](https://www.visualstudio.com/) + +## Quickstart + +### Deserialization + +Here is a program that parses a JSON document with ArduinoJson. + +```c++ +char json[] = "{\"sensor\":\"gps\",\"time\":1351824120,\"data\":[48.756080,2.302038]}"; + +StaticJsonBuffer<200> jsonBuffer; + +JsonObject& root = jsonBuffer.parseObject(json); + +const char* sensor = root["sensor"]; +long time = root["time"]; +double latitude = root["data"][0]; +double longitude = root["data"][1]; +``` + +See the [tutorial on arduinojson.org](https://arduinojson.org/doc/decoding/?utm_source=github&utm_medium=readme) + +### Serialization + +Here is a program that generates a JSON document with ArduinoJson: + +```c++ +StaticJsonBuffer<200> jsonBuffer; + +JsonObject& root = jsonBuffer.createObject(); +root["sensor"] = "gps"; +root["time"] = 1351824120; + +JsonArray& data = root.createNestedArray("data"); +data.add(48.756080); +data.add(2.302038); + +root.printTo(Serial); +// This prints: +// {"sensor":"gps","time":1351824120,"data":[48.756080,2.302038]} +``` + +See the [tutorial on arduinojson.org](https://arduinojson.org/doc/encoding/?utm_source=github&utm_medium=readme) + +## Documentation + +The documentation is available on [arduinojson.org](https://arduinojson.org/?utm_source=github&utm_medium=readme), here are some shortcuts: + +* The [Examples](https://arduinojson.org/example/?utm_source=github&utm_medium=readme) show how to use the library in various situations. +* The [API Reference](https://arduinojson.org/api/?utm_source=github&utm_medium=readme) contains the description of each class and function. +* The [FAQ](https://arduinojson.org/faq/?utm_source=github&utm_medium=readme) has the answer to virtually every question. +* The [ArduinoJson Assistant](https://arduinojson.org/assistant/?utm_source=github&utm_medium=readme) writes programs for you! + +--- + +Do you like this library? Please [star this project on GitHub](https://github.com/bblanchon/ArduinoJson/stargazers)! + +What? You don't like it but you *love* it? +We don't take donations anymore, but [we sell a book](https://arduinojson.org/book/?utm_source=github&utm_medium=readme), so you can help and learn at the same time! \ No newline at end of file diff --git a/lib/ArduinoJson-5.13.4/examples/JsonConfigFile/JsonConfigFile.ino b/lib/ArduinoJson-5.13.4/examples/JsonConfigFile/JsonConfigFile.ino new file mode 100644 index 000000000..2ccf7d673 --- /dev/null +++ b/lib/ArduinoJson-5.13.4/examples/JsonConfigFile/JsonConfigFile.ino @@ -0,0 +1,144 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License +// +// This example shows how to store your project configuration in a file. +// It uses the SD library but can be easily modified for any other file-system. +// +// The file contains a JSON document with the following content: +// { +// "hostname": "examples.com", +// "port": 2731 +// } + +#include +#include +#include + +// Configuration that we'll store on disk +struct Config { + char hostname[64]; + int port; +}; + +const char *filename = "/config.txt"; // <- SD library uses 8.3 filenames +Config config; // <- global configuration object + +// Loads the configuration from a file +void loadConfiguration(const char *filename, Config &config) { + // Open file for reading + File file = SD.open(filename); + + // Allocate the memory pool on the stack. + // Don't forget to change the capacity to match your JSON document. + // Use arduinojson.org/assistant to compute the capacity. + StaticJsonBuffer<512> jsonBuffer; + + // Parse the root object + JsonObject &root = jsonBuffer.parseObject(file); + + if (!root.success()) + Serial.println(F("Failed to read file, using default configuration")); + + // Copy values from the JsonObject to the Config + config.port = root["port"] | 2731; + strlcpy(config.hostname, // <- destination + root["hostname"] | "example.com", // <- source + sizeof(config.hostname)); // <- destination's capacity + + // Close the file (File's destructor doesn't close the file) + file.close(); +} + +// Saves the configuration to a file +void saveConfiguration(const char *filename, const Config &config) { + // Delete existing file, otherwise the configuration is appended to the file + SD.remove(filename); + + // Open file for writing + File file = SD.open(filename, FILE_WRITE); + if (!file) { + Serial.println(F("Failed to create file")); + return; + } + + // Allocate the memory pool on the stack + // Don't forget to change the capacity to match your JSON document. + // Use https://arduinojson.org/assistant/ to compute the capacity. + StaticJsonBuffer<256> jsonBuffer; + + // Parse the root object + JsonObject &root = jsonBuffer.createObject(); + + // Set the values + root["hostname"] = config.hostname; + root["port"] = config.port; + + // Serialize JSON to file + if (root.printTo(file) == 0) { + Serial.println(F("Failed to write to file")); + } + + // Close the file (File's destructor doesn't close the file) + file.close(); +} + +// Prints the content of a file to the Serial +void printFile(const char *filename) { + // Open file for reading + File file = SD.open(filename); + if (!file) { + Serial.println(F("Failed to read file")); + return; + } + + // Extract each characters by one by one + while (file.available()) { + Serial.print((char)file.read()); + } + Serial.println(); + + // Close the file (File's destructor doesn't close the file) + file.close(); +} + +void setup() { + // Initialize serial port + Serial.begin(9600); + while (!Serial) continue; + + // Initialize SD library + while (!SD.begin()) { + Serial.println(F("Failed to initialize SD library")); + delay(1000); + } + + // Should load default config if run for the first time + Serial.println(F("Loading configuration...")); + loadConfiguration(filename, config); + + // Create configuration file + Serial.println(F("Saving configuration...")); + saveConfiguration(filename, config); + + // Dump config file + Serial.println(F("Print config file...")); + printFile(filename); +} + +void loop() { + // not used in this example +} + +// See also +// -------- +// +// https://arduinojson.org/ contains the documentation for all the functions +// used above. It also includes an FAQ that will help you solve any +// serialization or deserialization problem. +// +// The book "Mastering ArduinoJson" contains a case study of a project that has +// a complex configuration with nested members. +// Contrary to this example, the project in the book uses the SPIFFS filesystem. +// Learn more at https://arduinojson.org/book/ +// Use the coupon code TWENTY for a 20% discount ❤❤❤❤❤ diff --git a/lib/ArduinoJson-5.11.2/examples/JsonGeneratorExample/JsonGeneratorExample.ino b/lib/ArduinoJson-5.13.4/examples/JsonGeneratorExample/JsonGeneratorExample.ino similarity index 63% rename from lib/ArduinoJson-5.11.2/examples/JsonGeneratorExample/JsonGeneratorExample.ino rename to lib/ArduinoJson-5.13.4/examples/JsonGeneratorExample/JsonGeneratorExample.ino index 0f636faf8..7b38227b3 100644 --- a/lib/ArduinoJson-5.11.2/examples/JsonGeneratorExample/JsonGeneratorExample.ino +++ b/lib/ArduinoJson-5.13.4/examples/JsonGeneratorExample/JsonGeneratorExample.ino @@ -1,23 +1,21 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License // -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! +// This example shows how to generate a JSON document with ArduinoJson. #include void setup() { + // Initialize Serial port Serial.begin(9600); - while (!Serial) { - // wait serial port initialization - } + while (!Serial) continue; // Memory pool for JSON object tree. // // Inside the brackets, 200 is the size of the pool in bytes. - // If the JSON object is more complex, you need to increase that value. - // See https://bblanchon.github.io/ArduinoJson/assistant/ + // Don't forget to change this value to match your JSON document. + // Use arduinojson.org/assistant to compute the capacity. StaticJsonBuffer<200> jsonBuffer; // StaticJsonBuffer allocates memory on the stack, it can be @@ -68,3 +66,16 @@ void setup() { void loop() { // not used in this example } + +// See also +// -------- +// +// https://arduinojson.org/ contains the documentation for all the functions +// used above. It also includes an FAQ that will help you solve any +// serialization problem. +// +// The book "Mastering ArduinoJson" contains a tutorial on serialization. +// It begins with a simple example, like the one above, and then adds more +// features like serializing directly to a file or an HTTP request. +// Learn more at https://arduinojson.org/book/ +// Use the coupon code TWENTY for a 20% discount ❤❤❤❤❤ diff --git a/lib/ArduinoJson-5.13.4/examples/JsonHttpClient/JsonHttpClient.ino b/lib/ArduinoJson-5.13.4/examples/JsonHttpClient/JsonHttpClient.ino new file mode 100644 index 000000000..4ce1c20d1 --- /dev/null +++ b/lib/ArduinoJson-5.13.4/examples/JsonHttpClient/JsonHttpClient.ino @@ -0,0 +1,112 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License +// +// This example shows how to parse a JSON document in an HTTP response. +// It uses the Ethernet library, but can be easily adapted for Wifi. +// +// It performs a GET resquest on arduinojson.org/example.json +// Here is the expected response: +// { +// "sensor": "gps", +// "time": 1351824120, +// "data": [ +// 48.756080, +// 2.302038 +// ] +// } + +#include +#include +#include + +void setup() { + // Initialize Serial port + Serial.begin(9600); + while (!Serial) continue; + + // Initialize Ethernet library + byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; + if (!Ethernet.begin(mac)) { + Serial.println(F("Failed to configure Ethernet")); + return; + } + delay(1000); + + Serial.println(F("Connecting...")); + + // Connect to HTTP server + EthernetClient client; + client.setTimeout(10000); + if (!client.connect("arduinojson.org", 80)) { + Serial.println(F("Connection failed")); + return; + } + + Serial.println(F("Connected!")); + + // Send HTTP request + client.println(F("GET /example.json HTTP/1.0")); + client.println(F("Host: arduinojson.org")); + client.println(F("Connection: close")); + if (client.println() == 0) { + Serial.println(F("Failed to send request")); + return; + } + + // Check HTTP status + char status[32] = {0}; + client.readBytesUntil('\r', status, sizeof(status)); + if (strcmp(status, "HTTP/1.1 200 OK") != 0) { + Serial.print(F("Unexpected response: ")); + Serial.println(status); + return; + } + + // Skip HTTP headers + char endOfHeaders[] = "\r\n\r\n"; + if (!client.find(endOfHeaders)) { + Serial.println(F("Invalid response")); + return; + } + + // Allocate JsonBuffer + // Use arduinojson.org/assistant to compute the capacity. + const size_t capacity = JSON_OBJECT_SIZE(3) + JSON_ARRAY_SIZE(2) + 60; + DynamicJsonBuffer jsonBuffer(capacity); + + // Parse JSON object + JsonObject& root = jsonBuffer.parseObject(client); + if (!root.success()) { + Serial.println(F("Parsing failed!")); + return; + } + + // Extract values + Serial.println(F("Response:")); + Serial.println(root["sensor"].as()); + Serial.println(root["time"].as()); + Serial.println(root["data"][0].as()); + Serial.println(root["data"][1].as()); + + // Disconnect + client.stop(); +} + +void loop() { + // not used in this example +} + +// See also +// -------- +// +// https://arduinojson.org/ contains the documentation for all the functions +// used above. It also includes an FAQ that will help you solve any +// serialization problem. +// +// The book "Mastering ArduinoJson" contains a tutorial on deserialization +// showing how to parse the response from Yahoo Weather. In the last chapter, +// it shows how to parse the huge documents from OpenWeatherMap +// and Weather Underground. +// Learn more at https://arduinojson.org/book/ +// Use the coupon code TWENTY for a 20% discount ❤❤❤❤❤ diff --git a/lib/ArduinoJson-5.11.2/examples/JsonParserExample/JsonParserExample.ino b/lib/ArduinoJson-5.13.4/examples/JsonParserExample/JsonParserExample.ino similarity index 61% rename from lib/ArduinoJson-5.11.2/examples/JsonParserExample/JsonParserExample.ino rename to lib/ArduinoJson-5.13.4/examples/JsonParserExample/JsonParserExample.ino index 719d2ee28..6c16211b5 100644 --- a/lib/ArduinoJson-5.11.2/examples/JsonParserExample/JsonParserExample.ino +++ b/lib/ArduinoJson-5.13.4/examples/JsonParserExample/JsonParserExample.ino @@ -1,23 +1,21 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License // -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! +// This example shows how to deserialize a JSON document with ArduinoJson. #include void setup() { + // Initialize serial port Serial.begin(9600); - while (!Serial) { - // wait serial port initialization - } + while (!Serial) continue; // Memory pool for JSON object tree. // - // Inside the brackets, 200 is the size of the pool in bytes, - // If the JSON object is more complex, you need to increase that value. - // See https://bblanchon.github.io/ArduinoJson/assistant/ + // Inside the brackets, 200 is the size of the pool in bytes. + // Don't forget to change this value to match your JSON document. + // Use arduinojson.org/assistant to compute the capacity. StaticJsonBuffer<200> jsonBuffer; // StaticJsonBuffer allocates memory on the stack, it can be @@ -65,3 +63,16 @@ void setup() { void loop() { // not used in this example } + +// See also +// -------- +// +// https://arduinojson.org/ contains the documentation for all the functions +// used above. It also includes an FAQ that will help you solve any +// deserialization problem. +// +// The book "Mastering ArduinoJson" contains a tutorial on deserialization. +// It begins with a simple example, like the one above, and then adds more +// features like deserializing directly from a file or an HTTP request. +// Learn more at https://arduinojson.org/book/ +// Use the coupon code TWENTY for a 20% discount ❤❤❤❤❤ diff --git a/lib/ArduinoJson-5.13.4/examples/JsonServer/JsonServer.ino b/lib/ArduinoJson-5.13.4/examples/JsonServer/JsonServer.ino new file mode 100644 index 000000000..e693ae176 --- /dev/null +++ b/lib/ArduinoJson-5.13.4/examples/JsonServer/JsonServer.ino @@ -0,0 +1,109 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License +// +// This example shows how to implement an HTTP server that sends JSON document +// in the responses. +// It uses the Ethernet library but can be easily adapted for Wifi. +// +// It sends the value of the analog and digital pins. +// The JSON document looks like the following: +// { +// "analog": [ 0, 1, 2, 3, 4, 5 ], +// "digital": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 ] +// } + +#include +#include +#include + +byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; +EthernetServer server(80); + +void setup() { + // Initialize serial port + Serial.begin(9600); + while (!Serial) continue; + + // Initialize Ethernet libary + if (!Ethernet.begin(mac)) { + Serial.println(F("Failed to initialize Ethernet library")); + return; + } + + // Start to listen + server.begin(); + + Serial.println(F("Server is ready.")); + Serial.print(F("Please connect to http://")); + Serial.println(Ethernet.localIP()); +} + +void loop() { + // Wait for an incomming connection + EthernetClient client = server.available(); + + // Do we have a client? + if (!client) return; + + Serial.println(F("New client")); + + // Read the request (we ignore the content in this example) + while (client.available()) client.read(); + + // Allocate JsonBuffer + // Use arduinojson.org/assistant to compute the capacity. + StaticJsonBuffer<500> jsonBuffer; + + // Create the root object + JsonObject& root = jsonBuffer.createObject(); + + // Create the "analog" array + JsonArray& analogValues = root.createNestedArray("analog"); + for (int pin = 0; pin < 6; pin++) { + // Read the analog input + int value = analogRead(pin); + + // Add the value at the end of the array + analogValues.add(value); + } + + // Create the "digital" array + JsonArray& digitalValues = root.createNestedArray("digital"); + for (int pin = 0; pin < 14; pin++) { + // Read the digital input + int value = digitalRead(pin); + + // Add the value at the end of the array + digitalValues.add(value); + } + + Serial.print(F("Sending: ")); + root.printTo(Serial); + Serial.println(); + + // Write response headers + client.println("HTTP/1.0 200 OK"); + client.println("Content-Type: application/json"); + client.println("Connection: close"); + client.println(); + + // Write JSON document + root.prettyPrintTo(client); + + // Disconnect + client.stop(); +} + +// See also +// -------- +// +// https://arduinojson.org/ contains the documentation for all the functions +// used above. It also includes an FAQ that will help you solve any +// serialization problem. +// +// The book "Mastering ArduinoJson" contains a tutorial on serialization. +// It begins with a simple example, then adds more features like serializing +// directly to a file or an HTTP client. +// Learn more at https://arduinojson.org/book/ +// Use the coupon code TWENTY for a 20% discount ❤❤❤❤❤ diff --git a/lib/ArduinoJson-5.13.4/examples/JsonUdpBeacon/JsonUdpBeacon.ino b/lib/ArduinoJson-5.13.4/examples/JsonUdpBeacon/JsonUdpBeacon.ino new file mode 100644 index 000000000..b2328a62d --- /dev/null +++ b/lib/ArduinoJson-5.13.4/examples/JsonUdpBeacon/JsonUdpBeacon.ino @@ -0,0 +1,101 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License +// +// This example shows how to send a JSON document to a UDP socket. +// At regular interval, it sends a UDP packet that contains the status of +// analog and digital pins. +// The JSON document looks like the following: +// { +// "analog": [ 0, 1, 2, 3, 4, 5 ], +// "digital": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 ] +// } +// +// If you want to test this program, you need to be able to receive the UDP +// packets. +// For example, you can run netcat on your computer +// $ ncat -ulp 8888 +// See https://nmap.org/ncat/ + +#include +#include +#include + +byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; +IPAddress remoteIp(192, 168, 0, 108); // <- EDIT!!!! +unsigned short remotePort = 8888; +unsigned short localPort = 8888; +EthernetUDP udp; + +void setup() { + // Initialize serial port + Serial.begin(9600); + while (!Serial) continue; + + // Initialize Ethernet libary + if (!Ethernet.begin(mac)) { + Serial.println(F("Failed to initialize Ethernet library")); + return; + } + + // Enable UDP + udp.begin(localPort); +} + +void loop() { + // Allocate JsonBuffer + // Use arduinojson.org/assistant to compute the capacity. + StaticJsonBuffer<500> jsonBuffer; + + // Create the root object + JsonObject& root = jsonBuffer.createObject(); + + // Create the "analog" array + JsonArray& analogValues = root.createNestedArray("analog"); + for (int pin = 0; pin < 6; pin++) { + // Read the analog input + int value = analogRead(pin); + + // Add the value at the end of the array + analogValues.add(value); + } + + // Create the "digital" array + JsonArray& digitalValues = root.createNestedArray("digital"); + for (int pin = 0; pin < 14; pin++) { + // Read the digital input + int value = digitalRead(pin); + + // Add the value at the end of the array + digitalValues.add(value); + } + + // Log + Serial.print(F("Sending to ")); + Serial.print(remoteIp); + Serial.print(F(" on port ")); + Serial.println(remotePort); + root.printTo(Serial); + + // Send UDP packet + udp.beginPacket(remoteIp, remotePort); + root.printTo(udp); + udp.println(); + udp.endPacket(); + + // Wait + delay(10000); +} + +// See also +// -------- +// +// https://arduinojson.org/ contains the documentation for all the functions +// used above. It also includes an FAQ that will help you solve any +// serialization problem. +// +// The book "Mastering ArduinoJson" contains a tutorial on serialization. +// It begins with a simple example, then adds more features like serializing +// directly to a file or any stream. +// Learn more at https://arduinojson.org/book/ +// Use the coupon code TWENTY for a 20% discount ❤❤❤❤❤ diff --git a/lib/ArduinoJson-5.11.2/examples/ProgmemExample/ProgmemExample.ino b/lib/ArduinoJson-5.13.4/examples/ProgmemExample/ProgmemExample.ino similarity index 52% rename from lib/ArduinoJson-5.11.2/examples/ProgmemExample/ProgmemExample.ino rename to lib/ArduinoJson-5.13.4/examples/ProgmemExample/ProgmemExample.ino index 195b014fb..ddde8fd1d 100644 --- a/lib/ArduinoJson-5.11.2/examples/ProgmemExample/ProgmemExample.ino +++ b/lib/ArduinoJson-5.13.4/examples/ProgmemExample/ProgmemExample.ino @@ -1,21 +1,19 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License // -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! +// This example shows the different ways you can use Flash strings with +// ArduinoJson. +// +// Use Flash strings sparingly, because ArduinoJson duplicates them in the +// JsonBuffer. Prefer plain old char*, as they are more efficient in term of +// code size, speed, and memory usage. #include -// About -// ----- -// This example shows the different ways you can use PROGMEM with ArduinoJson. -// Please don't see this as an invitation to use PROGMEM. -// On the contrary, you should always use char[] when possible, it's much more -// efficient in term of code size, speed and memory usage. - void setup() { -#ifdef PROGMEM +#ifdef PROGMEM // <- check that Flash strings are supported + DynamicJsonBuffer jsonBuffer; // You can use a Flash String as your JSON input. @@ -39,6 +37,9 @@ void setup() { // JsonBuffer. root["sensor"] = F("gps"); + // It works with RawJson too: + root["sensor"] = RawJson(F("\"gps\"")); + // You can compare the content of a JsonVariant to a Flash String if (root["sensor"] == F("gps")) { // ... @@ -54,3 +55,16 @@ void setup() { void loop() { // not used in this example } + +// See also +// -------- +// +// https://arduinojson.org/ contains the documentation for all the functions +// used above. It also includes an FAQ that will help you solve any memory +// problem. +// +// The book "Mastering ArduinoJson" contains a quick C++ course that explains +// how your microcontroller stores strings in memory. It also tells why you +// should not abuse Flash strings with ArduinoJson. +// Learn more at https://arduinojson.org/book/ +// Use the coupon code TWENTY for a 20% discount ❤❤❤❤❤ diff --git a/lib/ArduinoJson-5.11.2/examples/StringExample/StringExample.ino b/lib/ArduinoJson-5.13.4/examples/StringExample/StringExample.ino similarity index 67% rename from lib/ArduinoJson-5.11.2/examples/StringExample/StringExample.ino rename to lib/ArduinoJson-5.13.4/examples/StringExample/StringExample.ino index 50b8db6ec..fc7503d0e 100644 --- a/lib/ArduinoJson-5.11.2/examples/StringExample/StringExample.ino +++ b/lib/ArduinoJson-5.13.4/examples/StringExample/StringExample.ino @@ -1,19 +1,15 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License // -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! +// This example shows the different ways you can use String with ArduinoJson. +// +// Use String objects sparingly, because ArduinoJson duplicates them in the +// JsonBuffer. Prefer plain old char[], as they are more efficient in term of +// code size, speed, and memory usage. #include -// About -// ----- -// This example shows the different ways you can use String with ArduinoJson. -// Please don't see this as an invitation to use String. -// On the contrary, you should always use char[] when possible, it's much more -// efficient in term of code size, speed and memory usage. - void setup() { DynamicJsonBuffer jsonBuffer; @@ -44,6 +40,9 @@ void setup() { // WARNING: the content of the String will be duplicated in the JsonBuffer. root["sensor"] = sensor; + // It works with RawJson too: + root["sensor"] = RawJson(sensor); + // You can also concatenate strings // WARNING: the content of the String will be duplicated in the JsonBuffer. root[String("sen") + "sor"] = String("gp") + "s"; @@ -61,3 +60,15 @@ void setup() { void loop() { // not used in this example } + +// See also +// -------- +// +// https://arduinojson.org/ contains the documentation for all the functions +// used above. It also includes an FAQ that will help you solve any problem. +// +// The book "Mastering ArduinoJson" contains a quick C++ course that explains +// how your microcontroller stores strings in memory. On several occasions, it +// shows how you can avoid String in your program. +// Learn more at https://arduinojson.org/book/ +// Use the coupon code TWENTY for a 20% discount ❤❤❤❤❤ diff --git a/lib/ArduinoJson-5.11.2/keywords.txt b/lib/ArduinoJson-5.13.4/keywords.txt similarity index 100% rename from lib/ArduinoJson-5.11.2/keywords.txt rename to lib/ArduinoJson-5.13.4/keywords.txt diff --git a/lib/ArduinoJson-5.13.4/library.properties b/lib/ArduinoJson-5.13.4/library.properties new file mode 100644 index 000000000..67ccbb95e --- /dev/null +++ b/lib/ArduinoJson-5.13.4/library.properties @@ -0,0 +1,11 @@ +name=ArduinoJson +version=5.13.4 +author=Benoit Blanchon +maintainer=Benoit Blanchon +sentence=An efficient and elegant JSON library for Arduino. +paragraph=ArduinoJson supports ✔ serialization, ✔ deserialization, ✔ fixed allocation, ✔ zero-copy, ✔ streams, and more. It is the most popular Arduino library on GitHub ❤❤❤❤❤. Check out arduinojson.org for a comprehensive documentation. +category=Data Processing +url=https://arduinojson.org/?utm_source=meta&utm_medium=library.properties +architectures=* +repository=https://github.com/bblanchon/ArduinoJson.git +license=MIT diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson.h b/lib/ArduinoJson-5.13.4/src/ArduinoJson.h new file mode 100644 index 000000000..3782aeabc --- /dev/null +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson.h @@ -0,0 +1,17 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#ifdef __cplusplus + +#include "ArduinoJson.hpp" + +using namespace ArduinoJson; + +#else + +#error ArduinoJson requires a C++ compiler, please change file extension to .cc or .cpp + +#endif diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson.hpp similarity index 75% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson.hpp index 949fa5ca0..c493c06a9 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson.hpp @@ -1,12 +1,11 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once +#include "ArduinoJson/version.hpp" + #include "ArduinoJson/DynamicJsonBuffer.hpp" #include "ArduinoJson/JsonArray.hpp" #include "ArduinoJson/JsonObject.hpp" diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Configuration.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Configuration.hpp similarity index 94% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/Configuration.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/Configuration.hpp index a1015a6f3..82483adfa 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Configuration.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Configuration.hpp @@ -1,15 +1,13 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once // Small or big machine? #ifndef ARDUINOJSON_EMBEDDED_MODE -#if defined(ARDUINO) || defined(__IAR_SYSTEMS_ICC__) +#if defined(ARDUINO) || defined(__IAR_SYSTEMS_ICC__) || defined(__XC) || \ + defined(__ARMCC_VERSION) #define ARDUINOJSON_EMBEDDED_MODE 1 #else #define ARDUINOJSON_EMBEDDED_MODE 0 diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Data/Encoding.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/Encoding.hpp similarity index 80% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/Data/Encoding.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/Encoding.hpp index dba785aae..a0efa2c74 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Data/Encoding.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/Encoding.hpp @@ -1,9 +1,6 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Data/JsonBufferAllocated.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/JsonBufferAllocated.hpp similarity index 68% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/Data/JsonBufferAllocated.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/JsonBufferAllocated.hpp index ff74adc55..443aae4df 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Data/JsonBufferAllocated.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/JsonBufferAllocated.hpp @@ -1,9 +1,6 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Data/JsonFloat.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/JsonFloat.hpp similarity index 56% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/Data/JsonFloat.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/JsonFloat.hpp index a4a8e937f..0ed42140f 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Data/JsonFloat.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/JsonFloat.hpp @@ -1,9 +1,6 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Data/JsonInteger.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/JsonInteger.hpp similarity index 70% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/Data/JsonInteger.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/JsonInteger.hpp index 574a5e109..c8ddd00b4 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Data/JsonInteger.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/JsonInteger.hpp @@ -1,9 +1,6 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Data/JsonVariantAs.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/JsonVariantAs.hpp similarity index 80% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/Data/JsonVariantAs.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/JsonVariantAs.hpp index 3f19fd23a..8f202c5eb 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Data/JsonVariantAs.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/JsonVariantAs.hpp @@ -1,9 +1,6 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Data/JsonVariantContent.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/JsonVariantContent.hpp similarity index 79% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/Data/JsonVariantContent.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/JsonVariantContent.hpp index b27716299..c525a6060 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Data/JsonVariantContent.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/JsonVariantContent.hpp @@ -1,9 +1,6 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Data/JsonVariantDefault.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/JsonVariantDefault.hpp similarity index 68% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/Data/JsonVariantDefault.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/JsonVariantDefault.hpp index a59fd46c9..57ecc83ee 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Data/JsonVariantDefault.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/JsonVariantDefault.hpp @@ -1,9 +1,6 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Data/JsonVariantType.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/JsonVariantType.hpp similarity index 84% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/Data/JsonVariantType.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/JsonVariantType.hpp index ba6a24999..21f890e52 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Data/JsonVariantType.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/JsonVariantType.hpp @@ -1,9 +1,6 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Data/List.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/List.hpp similarity index 93% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/Data/List.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/List.hpp index 8a24666ec..506308cc3 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Data/List.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/List.hpp @@ -1,9 +1,6 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Data/ListConstIterator.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/ListConstIterator.hpp similarity index 85% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/Data/ListConstIterator.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/ListConstIterator.hpp index bce1bfa5e..a6af685e5 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Data/ListConstIterator.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/ListConstIterator.hpp @@ -1,9 +1,6 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Data/ListIterator.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/ListIterator.hpp similarity index 86% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/Data/ListIterator.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/ListIterator.hpp index a491866f0..01fa287f7 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Data/ListIterator.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/ListIterator.hpp @@ -1,9 +1,6 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Data/ListNode.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/ListNode.hpp similarity index 70% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/Data/ListNode.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/ListNode.hpp index 712e15e1f..c0907120e 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Data/ListNode.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/ListNode.hpp @@ -1,9 +1,6 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Data/NonCopyable.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/NonCopyable.hpp similarity index 67% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/Data/NonCopyable.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/NonCopyable.hpp index 98ebd8fb3..73f3d8edb 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Data/NonCopyable.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/NonCopyable.hpp @@ -1,9 +1,6 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Data/ReferenceType.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/ReferenceType.hpp similarity index 74% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/Data/ReferenceType.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/ReferenceType.hpp index bbc9046be..1e491172f 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Data/ReferenceType.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/ReferenceType.hpp @@ -1,9 +1,6 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/ValueSaver.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/ValueSaver.hpp new file mode 100644 index 000000000..9750f1ac5 --- /dev/null +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Data/ValueSaver.hpp @@ -0,0 +1,52 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include "../JsonBuffer.hpp" +#include "../JsonVariant.hpp" +#include "../StringTraits/StringTraits.hpp" +#include "../TypeTraits/EnableIf.hpp" + +namespace ArduinoJson { +namespace Internals { + +template +struct ValueSaver { + template + static bool save(JsonBuffer*, Destination& destination, Source source) { + destination = source; + return true; + } +}; + +template +struct ValueSaver< + Source, typename EnableIf::should_duplicate>::type> { + template + static bool save(JsonBuffer* buffer, Destination& dest, Source source) { + if (!StringTraits::is_null(source)) { + typename StringTraits::duplicate_t dup = + StringTraits::duplicate(source, buffer); + if (!dup) return false; + dest = dup; + } else { + dest = reinterpret_cast(0); + } + return true; + } +}; + +// const char*, const signed char*, const unsigned char* +template +struct ValueSaver< + Char*, typename EnableIf::should_duplicate>::type> { + template + static bool save(JsonBuffer*, Destination& dest, Char* source) { + dest = reinterpret_cast(source); + return true; + } +}; +} +} diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Deserialization/Comments.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Deserialization/Comments.hpp similarity index 88% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/Deserialization/Comments.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/Deserialization/Comments.hpp index 9f281da49..c2c48ebcc 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Deserialization/Comments.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Deserialization/Comments.hpp @@ -1,9 +1,6 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Deserialization/JsonParser.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Deserialization/JsonParser.hpp similarity index 77% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/Deserialization/JsonParser.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/Deserialization/JsonParser.hpp index 3e605ef25..4cbaf454c 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Deserialization/JsonParser.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Deserialization/JsonParser.hpp @@ -1,9 +1,6 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once @@ -47,19 +44,18 @@ class JsonParser { const char *parseString(); bool parseAnythingTo(JsonVariant *destination); - FORCE_INLINE bool parseAnythingToUnsafe(JsonVariant *destination); inline bool parseArrayTo(JsonVariant *destination); inline bool parseObjectTo(JsonVariant *destination); inline bool parseStringTo(JsonVariant *destination); - static inline bool isInRange(char c, char min, char max) { + static inline bool isBetween(char c, char min, char max) { return min <= c && c <= max; } - static inline bool isLetterOrNumber(char c) { - return isInRange(c, '0', '9') || isInRange(c, 'a', 'z') || - isInRange(c, 'A', 'Z') || c == '+' || c == '-' || c == '.'; + static inline bool canBeInNonQuotedString(char c) { + return isBetween(c, '0', '9') || isBetween(c, '_', 'z') || + isBetween(c, 'A', 'Z') || c == '+' || c == '-' || c == '.'; } static inline bool isQuote(char c) { @@ -74,7 +70,7 @@ class JsonParser { template struct JsonParserBuilder { - typedef typename Internals::StringTraits::Reader InputReader; + typedef typename StringTraits::Reader InputReader; typedef JsonParser TParser; static TParser makeParser(TJsonBuffer *buffer, TString &json, @@ -84,10 +80,9 @@ struct JsonParserBuilder { }; template -struct JsonParserBuilder< - TJsonBuffer, TChar *, - typename TypeTraits::EnableIf::value>::type> { - typedef typename Internals::StringTraits::Reader TReader; +struct JsonParserBuilder::value>::type> { + typedef typename StringTraits::Reader TReader; typedef StringWriter TWriter; typedef JsonParser TParser; @@ -103,5 +98,5 @@ inline typename JsonParserBuilder::TParser makeParser( return JsonParserBuilder::makeParser(buffer, json, nestingLimit); } -} -} +} // namespace Internals +} // namespace ArduinoJson diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Deserialization/JsonParserImpl.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Deserialization/JsonParserImpl.hpp similarity index 85% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/Deserialization/JsonParserImpl.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/Deserialization/JsonParserImpl.hpp index 1c41bdaac..504267355 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Deserialization/JsonParserImpl.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Deserialization/JsonParserImpl.hpp @@ -1,9 +1,6 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once @@ -20,18 +17,9 @@ inline bool ArduinoJson::Internals::JsonParser::eat( } template -inline bool ArduinoJson::Internals::JsonParser< - TReader, TWriter>::parseAnythingTo(JsonVariant *destination) { - if (_nestingLimit == 0) return false; - _nestingLimit--; - bool success = parseAnythingToUnsafe(destination); - _nestingLimit++; - return success; -} - -template -inline bool ArduinoJson::Internals::JsonParser< - TReader, TWriter>::parseAnythingToUnsafe(JsonVariant *destination) { +inline bool +ArduinoJson::Internals::JsonParser::parseAnythingTo( + JsonVariant *destination) { skipSpacesAndComments(_reader); switch (_reader.current()) { @@ -49,6 +37,9 @@ inline bool ArduinoJson::Internals::JsonParser< template inline ArduinoJson::JsonArray & ArduinoJson::Internals::JsonParser::parseArray() { + if (_nestingLimit == 0) return JsonArray::invalid(); + _nestingLimit--; + // Create an empty array JsonArray &array = _buffer->createArray(); @@ -70,6 +61,7 @@ ArduinoJson::Internals::JsonParser::parseArray() { SUCCESS_EMPTY_ARRAY: SUCCES_NON_EMPTY_ARRAY: + _nestingLimit++; return array; ERROR_INVALID_VALUE: @@ -92,6 +84,9 @@ inline bool ArduinoJson::Internals::JsonParser::parseArrayTo( template inline ArduinoJson::JsonObject & ArduinoJson::Internals::JsonParser::parseObject() { + if (_nestingLimit == 0) return JsonObject::invalid(); + _nestingLimit--; + // Create an empty object JsonObject &object = _buffer->createObject(); @@ -118,6 +113,7 @@ ArduinoJson::Internals::JsonParser::parseObject() { SUCCESS_EMPTY_OBJECT: SUCCESS_NON_EMPTY_OBJECT: + _nestingLimit++; return object; ERROR_INVALID_KEY: @@ -142,8 +138,7 @@ inline bool ArduinoJson::Internals::JsonParser::parseObjectTo( template inline const char * ArduinoJson::Internals::JsonParser::parseString() { - typename TypeTraits::RemoveReference::type::String str = - _writer.startString(); + typename RemoveReference::type::String str = _writer.startString(); skipSpacesAndComments(_reader); char c = _reader.current(); @@ -169,7 +164,7 @@ ArduinoJson::Internals::JsonParser::parseString() { } } else { // no quotes for (;;) { - if (!isLetterOrNumber(c)) break; + if (!canBeInNonQuotedString(c)) break; _reader.move(); str.append(c); c = _reader.current(); diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Deserialization/StringWriter.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Deserialization/StringWriter.hpp similarity index 79% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/Deserialization/StringWriter.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/Deserialization/StringWriter.hpp index 42ee640d8..fd5507ea5 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Deserialization/StringWriter.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Deserialization/StringWriter.hpp @@ -1,9 +1,6 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/DynamicJsonBuffer.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/DynamicJsonBuffer.hpp similarity index 94% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/DynamicJsonBuffer.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/DynamicJsonBuffer.hpp index 65e744bf2..bdbd5dd90 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/DynamicJsonBuffer.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/DynamicJsonBuffer.hpp @@ -1,9 +1,6 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once @@ -22,6 +19,7 @@ #endif namespace ArduinoJson { +namespace Internals { class DefaultAllocator { public: void* allocate(size_t size) { @@ -154,6 +152,7 @@ class DynamicJsonBufferBase Block* _head; size_t _nextBlockCapacity; }; +} #if defined(__clang__) #pragma clang diagnostic pop @@ -166,5 +165,6 @@ class DynamicJsonBufferBase // Implements a JsonBuffer with dynamic memory allocation. // You are strongly encouraged to consider using StaticJsonBuffer which is much // more suitable for embedded systems. -typedef DynamicJsonBufferBase DynamicJsonBuffer; +typedef Internals::DynamicJsonBufferBase + DynamicJsonBuffer; } diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/JsonArray.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonArray.hpp similarity index 82% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/JsonArray.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonArray.hpp index 27404fb7b..2acd2a1a5 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/JsonArray.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonArray.hpp @@ -1,16 +1,13 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once #include "Data/JsonBufferAllocated.hpp" #include "Data/List.hpp" #include "Data/ReferenceType.hpp" -#include "Data/ValueSetter.hpp" +#include "Data/ValueSaver.hpp" #include "JsonVariant.hpp" #include "Serialization/JsonPrintable.hpp" #include "StringTraits/StringTraits.hpp" @@ -29,7 +26,9 @@ namespace ArduinoJson { // Forward declarations class JsonObject; class JsonBuffer; +namespace Internals { class JsonArraySubscript; +} // An array of JsonVariant. // @@ -50,28 +49,26 @@ class JsonArray : public Internals::JsonPrintable, : Internals::List(buffer) {} // Gets the value at the specified index - const JsonArraySubscript operator[](size_t index) const; + const Internals::JsonArraySubscript operator[](size_t index) const; // Gets or sets the value at specified index - JsonArraySubscript operator[](size_t index); + Internals::JsonArraySubscript operator[](size_t index); // Adds the specified value at the end of the array. // // bool add(TValue); // TValue = bool, long, int, short, float, double, RawJson, JsonVariant, - // const std::string&, const String&, - // const JsonArray&, const JsonObject& + // std::string, String, JsonArray, JsonObject template - typename TypeTraits::EnableIf::value, bool>::type add( - const T &value) { + bool add(const T &value) { return add_impl(value); } // // bool add(TValue); - // TValue = const char*, const char[N], const FlashStringHelper* + // TValue = char*, const char*, const FlashStringHelper* template - bool add(const T *value) { - return add_impl(value); + bool add(T *value) { + return add_impl(value); } // // bool add(TValue value, uint8_t decimals); @@ -84,28 +81,25 @@ class JsonArray : public Internals::JsonPrintable, // Sets the value at specified index. // - // bool add(size_t index, TValue); + // bool add(size_t index, const TValue&); // TValue = bool, long, int, short, float, double, RawJson, JsonVariant, - // const std::string&, const String&, - // const JsonArray&, const JsonObject& + // std::string, String, JsonArray, JsonObject template - typename TypeTraits::EnableIf::value, bool>::type set( - size_t index, const T &value) { + bool set(size_t index, const T &value) { return set_impl(index, value); } // // bool add(size_t index, TValue); - // TValue = const char*, const char[N], const FlashStringHelper* + // TValue = char*, const char*, const FlashStringHelper* template - bool set(size_t index, const T *value) { - return set_impl(index, value); + bool set(size_t index, T *value) { + return set_impl(index, value); } // // bool set(size_t index, TValue value, uint8_t decimals); // TValue = float, double template - typename TypeTraits::EnableIf::value, - bool>::type + typename Internals::EnableIf::value, bool>::type set(size_t index, T value, uint8_t decimals) { return set_impl(index, JsonVariant(value, decimals)); } @@ -211,14 +205,14 @@ class JsonArray : public Internals::JsonPrintable, bool set_impl(size_t index, TValueRef value) { iterator it = begin() += index; if (it == end()) return false; - return Internals::ValueSetter::set(_buffer, *it, value); + return Internals::ValueSaver::save(_buffer, *it, value); } template bool add_impl(TValueRef value) { iterator it = Internals::List::add(); if (it == end()) return false; - return Internals::ValueSetter::set(_buffer, *it, value); + return Internals::ValueSaver::save(_buffer, *it, value); } }; diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/JsonArrayImpl.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonArrayImpl.hpp similarity index 75% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/JsonArrayImpl.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonArrayImpl.hpp index 6ebd39baa..924b7ea7a 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/JsonArrayImpl.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonArrayImpl.hpp @@ -1,9 +1,6 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/JsonArraySubscript.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonArraySubscript.hpp similarity index 69% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/JsonArraySubscript.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonArraySubscript.hpp index 5bd6208a5..afb4dc1ec 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/JsonArraySubscript.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonArraySubscript.hpp @@ -1,9 +1,6 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once @@ -16,6 +13,7 @@ #endif namespace ArduinoJson { +namespace Internals { class JsonArraySubscript : public JsonVariantBase { public: FORCE_INLINE JsonArraySubscript(JsonArray& array, size_t index) @@ -28,10 +26,9 @@ class JsonArraySubscript : public JsonVariantBase { // Replaces the value // - // operator=(TValue) + // operator=(const TValue&) // TValue = bool, long, int, short, float, double, RawJson, JsonVariant, - // const std::string&, const String&, - // const JsonArray&, const JsonObject& + // std::string, String, JsonArray, JsonObject template FORCE_INLINE JsonArraySubscript& operator=(const T& src) { _array.set(_index, src); @@ -39,9 +36,9 @@ class JsonArraySubscript : public JsonVariantBase { } // // operator=(TValue) - // TValue = const char*, const char[N], const FlashStringHelper* + // TValue = char*, const char*, const FlashStringHelper* template - FORCE_INLINE JsonArraySubscript& operator=(const T* src) { + FORCE_INLINE JsonArraySubscript& operator=(T* src) { _array.set(_index, src); return *this; } @@ -51,7 +48,7 @@ class JsonArraySubscript : public JsonVariantBase { } template - FORCE_INLINE typename Internals::JsonVariantAs::type as() const { + FORCE_INLINE typename JsonVariantAs::type as() const { return _array.get(_index); } @@ -62,19 +59,18 @@ class JsonArraySubscript : public JsonVariantBase { // Replaces the value // - // bool set(TValue) + // bool set(const TValue&) // TValue = bool, long, int, short, float, double, RawJson, JsonVariant, - // const std::string&, const String&, - // const JsonArray&, const JsonObject& + // std::string, String, JsonArray, JsonObject template FORCE_INLINE bool set(const TValue& value) { return _array.set(_index, value); } // // bool set(TValue) - // TValue = const char*, const char[N], const FlashStringHelper* + // TValue = char*, const char*, const FlashStringHelper* template - FORCE_INLINE bool set(const TValue* value) { + FORCE_INLINE bool set(TValue* value) { return _array.set(_index, value); } // @@ -91,21 +87,6 @@ class JsonArraySubscript : public JsonVariantBase { const size_t _index; }; -#if ARDUINOJSON_ENABLE_STD_STREAM -inline std::ostream& operator<<(std::ostream& os, - const JsonArraySubscript& source) { - return source.printTo(os); -} -#endif - -inline JsonArraySubscript JsonArray::operator[](size_t index) { - return JsonArraySubscript(*this, index); -} - -inline const JsonArraySubscript JsonArray::operator[](size_t index) const { - return JsonArraySubscript(*const_cast(this), index); -} - template inline JsonArraySubscript JsonVariantSubscripts::operator[]( size_t index) { @@ -118,7 +99,23 @@ inline const JsonArraySubscript JsonVariantSubscripts::operator[]( return impl()->template as()[index]; } -} // namespace ArduinoJson +#if ARDUINOJSON_ENABLE_STD_STREAM +inline std::ostream& operator<<(std::ostream& os, + const JsonArraySubscript& source) { + return source.printTo(os); +} +#endif +} + +inline Internals::JsonArraySubscript JsonArray::operator[](size_t index) { + return Internals::JsonArraySubscript(*this, index); +} + +inline const Internals::JsonArraySubscript JsonArray::operator[]( + size_t index) const { + return Internals::JsonArraySubscript(*const_cast(this), index); +} +} #ifdef _MSC_VER #pragma warning(pop) diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/JsonBuffer.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonBuffer.hpp similarity index 74% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/JsonBuffer.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonBuffer.hpp index 06bda0ef4..26101e086 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/JsonBuffer.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonBuffer.hpp @@ -1,9 +1,6 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once @@ -41,20 +38,21 @@ class JsonBuffer : Internals::NonCopyable { // Duplicates a string // - // char* strdup(TValue); + // const char* strdup(TValue); // TValue = const std::string&, const String&, template - typename TypeTraits::EnableIf::value, - char *>::type - strdup(const TString &src) { + DEPRECATED("char* are duplicated, you don't need strdup() anymore") + typename Internals::EnableIf::value, + const char *>::type strdup(const TString &src) { return Internals::StringTraits::duplicate(src, this); } // - // char* strdup(TValue); - // TValue = const char*, const char[N], const FlashStringHelper* + // const char* strdup(TValue); + // TValue = char*, const char*, const FlashStringHelper* template - char *strdup(const TString *src) { - return Internals::StringTraits::duplicate(src, this); + DEPRECATED("char* are duplicated, you don't need strdup() anymore") + const char *strdup(TString *src) { + return Internals::StringTraits::duplicate(src, this); } // Allocates n bytes in the JsonBuffer. diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/JsonBufferBase.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonBufferBase.hpp similarity index 88% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/JsonBufferBase.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonBufferBase.hpp index 480cd8307..1e771bfdb 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/JsonBufferBase.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonBufferBase.hpp @@ -1,15 +1,13 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once #include "Deserialization/JsonParser.hpp" namespace ArduinoJson { +namespace Internals { template class JsonBufferBase : public JsonBuffer { public: @@ -28,8 +26,8 @@ class JsonBufferBase : public JsonBuffer { // JsonArray& parseArray(TString); // TString = const std::string&, const String& template - typename TypeTraits::EnableIf::value, - JsonArray &>::type + typename Internals::EnableIf::value, + JsonArray &>::type parseArray(const TString &json, uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT) { return Internals::makeParser(that(), json, nestingLimit).parseArray(); @@ -65,8 +63,8 @@ class JsonBufferBase : public JsonBuffer { // JsonObject& parseObject(TString); // TString = const std::string&, const String& template - typename TypeTraits::EnableIf::value, - JsonObject &>::type + typename Internals::EnableIf::value, + JsonObject &>::type parseObject(const TString &json, uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT) { return Internals::makeParser(that(), json, nestingLimit).parseObject(); @@ -94,8 +92,8 @@ class JsonBufferBase : public JsonBuffer { // JsonVariant parse(TString); // TString = const std::string&, const String& template - typename TypeTraits::EnableIf::value, - JsonVariant>::type + typename Internals::EnableIf::value, + JsonVariant>::type parse(const TString &json, uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT) { return Internals::makeParser(that(), json, nestingLimit).parseVariant(); @@ -126,3 +124,4 @@ class JsonBufferBase : public JsonBuffer { } }; } +} diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/JsonBufferImpl.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonBufferImpl.hpp similarity index 72% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/JsonBufferImpl.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonBufferImpl.hpp index 98d5de595..cdea374bb 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/JsonBufferImpl.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonBufferImpl.hpp @@ -1,9 +1,6 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/JsonObject.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonObject.hpp similarity index 57% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/JsonObject.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonObject.hpp index a3e30ab27..caf698a3e 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/JsonObject.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonObject.hpp @@ -1,16 +1,13 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once #include "Data/JsonBufferAllocated.hpp" #include "Data/List.hpp" #include "Data/ReferenceType.hpp" -#include "Data/ValueSetter.hpp" +#include "Data/ValueSaver.hpp" #include "JsonPair.hpp" #include "Serialization/JsonPrintable.hpp" #include "StringTraits/StringTraits.hpp" @@ -29,6 +26,10 @@ namespace ArduinoJson { // Forward declarations class JsonArray; class JsonBuffer; +namespace Internals { +template +class JsonObjectSubscript; +} // A dictionary of JsonVariant indexed by string (char*) // @@ -45,25 +46,24 @@ class JsonObject : public Internals::JsonPrintable, // Create an empty JsonArray attached to the specified JsonBuffer. // You should not use this constructor directly. // Instead, use JsonBuffer::createObject() or JsonBuffer.parseObject(). - explicit JsonObject(JsonBuffer* buffer) throw() - : Internals::List(buffer) {} + explicit JsonObject(JsonBuffer* buffer) throw() + : Internals::List(buffer) {} // Gets or sets the value associated with the specified key. // // JsonObjectSubscript operator[](TKey) // TKey = const std::string&, const String& template - typename TypeTraits::EnableIf::value, - JsonObjectSubscript >::type - operator[](const TString& key) { - return JsonObjectSubscript(*this, key); + Internals::JsonObjectSubscript operator[]( + const TString& key) { + return Internals::JsonObjectSubscript(*this, key); } // // JsonObjectSubscript operator[](TKey) - // TKey = const char*, const char[N], const FlashStringHelper* + // TKey = char*, const char*, char[], const char[N], const FlashStringHelper* template - JsonObjectSubscript operator[](const TString* key) { - return JsonObjectSubscript(*this, key); + Internals::JsonObjectSubscript operator[](TString* key) { + return Internals::JsonObjectSubscript(*this, key); } // Gets the value associated with the specified key. @@ -71,21 +71,19 @@ class JsonObject : public Internals::JsonPrintable, // const JsonObjectSubscript operator[](TKey) const; // TKey = const std::string&, const String& template - typename TypeTraits::EnableIf< - !TypeTraits::IsArray::value, - const JsonObjectSubscript >::type - operator[](const TString& key) const { - return JsonObjectSubscript(*const_cast(this), - key); + const Internals::JsonObjectSubscript operator[]( + const TString& key) const { + return Internals::JsonObjectSubscript( + *const_cast(this), key); } // // const JsonObjectSubscript operator[](TKey) const; // TKey = const char*, const char[N], const FlashStringHelper* template - const JsonObjectSubscript operator[]( - const TString* key) const { - return JsonObjectSubscript(*const_cast(this), - key); + const Internals::JsonObjectSubscript operator[]( + TString* key) const { + return Internals::JsonObjectSubscript( + *const_cast(this), key); } // Sets the specified key with the specified value. @@ -93,43 +91,35 @@ class JsonObject : public Internals::JsonPrintable, // bool set(TKey, TValue); // TKey = const std::string&, const String& // TValue = bool, long, int, short, float, double, RawJson, JsonVariant, - // const std::string&, const String&, - // const JsonArray&, const JsonObject& + // std::string, String, JsonArray, JsonObject template - typename TypeTraits::EnableIf::value && - !TypeTraits::IsArray::value, - bool>::type - set(const TString& key, const TValue& value) { + bool set(const TString& key, const TValue& value) { return set_impl(key, value); } // // bool set(TKey, TValue); // TKey = const std::string&, const String& - // TValue = const char*, const char[N], const FlashStringHelper* + // TValue = char*, const char*, const FlashStringHelper* template - typename TypeTraits::EnableIf::value, - bool>::type - set(const TString& key, const TValue* value) { - return set_impl(key, value); + bool set(const TString& key, TValue* value) { + return set_impl(key, value); } // - // bool set(TKey, TValue); - // TKey = const char*, const char[N], const FlashStringHelper* + // bool set(TKey, const TValue&); + // TKey = char*, const char*, const FlashStringHelper* // TValue = bool, long, int, short, float, double, RawJson, JsonVariant, - // const std::string&, const String&, - // const JsonArray&, const JsonObject& + // std::string, String, JsonArray, JsonObject template - typename TypeTraits::EnableIf::value, bool>::type - set(const TString* key, const TValue& value) { - return set_impl(key, value); + bool set(TString* key, const TValue& value) { + return set_impl(key, value); } // // bool set(TKey, TValue); - // TKey = const char*, const char[N], const FlashStringHelper* - // TValue = const char*, const char[N], const FlashStringHelper* + // TKey = char*, const char*, const FlashStringHelper* + // TValue = char*, const char*, const FlashStringHelper* template - bool set(const TString* key, const TValue* value) { - return set_impl(key, value); + bool set(TString* key, TValue* value) { + return set_impl(key, value); } // // bool set(TKey, TValue, uint8_t decimals); @@ -137,50 +127,43 @@ class JsonObject : public Internals::JsonPrintable, // TValue = float, double template DEPRECATED("Second argument is not supported anymore") - typename TypeTraits::EnableIf::value && - !TypeTraits::IsArray::value, - bool>::type + typename Internals::EnableIf::value, + bool>::type set(const TString& key, TValue value, uint8_t) { return set_impl(key, JsonVariant(value)); } // // bool set(TKey, TValue, uint8_t decimals); - // TKey = const char*, const char[N], const FlashStringHelper* + // TKey = char*, const char*, const FlashStringHelper* // TValue = float, double template DEPRECATED("Second argument is not supported anymore") - typename TypeTraits::EnableIf::value, - bool>::type - set(const TString* key, TValue value, uint8_t) { - return set_impl(key, - JsonVariant(value)); + typename Internals::EnableIf::value, + bool>::type + set(TString* key, TValue value, uint8_t) { + return set_impl(key, JsonVariant(value)); } // Gets the value associated with the specified key. // - // TValue get(TKey); + // TValue get(TKey) const; // TKey = const std::string&, const String& // TValue = bool, char, long, int, short, float, double, - // const std::string&, const String&, - // const JsonArray&, const JsonObject& + // std::string, String, JsonArray, JsonObject template - typename TypeTraits::EnableIf< - !TypeTraits::IsArray::value, - typename Internals::JsonVariantAs::type>::type - get(const TString& key) const { + typename Internals::JsonVariantAs::type get( + const TString& key) const { return get_impl(key); } // - // TValue get(TKey); - // TKey = const char*, const char[N], const FlashStringHelper* + // TValue get(TKey) const; + // TKey = char*, const char*, const FlashStringHelper* // TValue = bool, char, long, int, short, float, double, - // const std::string&, const String&, - // const JsonArray&, const JsonObject& + // std::string, String, JsonArray, JsonObject template - typename Internals::JsonVariantAs::type get( - const TString* key) const { - return get_impl(key); + typename Internals::JsonVariantAs::type get(TString* key) const { + return get_impl(key); } // Checks the type of the value associated with the specified key. @@ -189,23 +172,19 @@ class JsonObject : public Internals::JsonPrintable, // bool is(TKey) const; // TKey = const std::string&, const String& // TValue = bool, char, long, int, short, float, double, - // const std::string&, const String&, - // const JsonArray&, const JsonObject& + // std::string, String, JsonArray, JsonObject template - typename TypeTraits::EnableIf::value, - bool>::type - is(const TString& key) const { + bool is(const TString& key) const { return is_impl(key); } // // bool is(TKey) const; - // TKey = const char*, const char[N], const FlashStringHelper* + // TKey = char*, const char*, const FlashStringHelper* // TValue = bool, char, long, int, short, float, double, - // const std::string&, const String&, - // const JsonArray&, const JsonObject& + // std::string, String, JsonArray, JsonObject template - bool is(const TString* key) const { - return is_impl(key); + bool is(TString* key) const { + return is_impl(key); } // Creates and adds a JsonArray. @@ -213,16 +192,14 @@ class JsonObject : public Internals::JsonPrintable, // JsonArray& createNestedArray(TKey); // TKey = const std::string&, const String& template - typename TypeTraits::EnableIf::value, - JsonArray&>::type - createNestedArray(const TString& key) { + JsonArray& createNestedArray(const TString& key) { return createNestedArray_impl(key); } // JsonArray& createNestedArray(TKey); - // TKey = const char*, const char[N], const FlashStringHelper* + // TKey = char*, const char*, char[], const char[], const FlashStringHelper* template - JsonArray& createNestedArray(const TString* key) { - return createNestedArray_impl(key); + JsonArray& createNestedArray(TString* key) { + return createNestedArray_impl(key); } // Creates and adds a JsonObject. @@ -230,17 +207,15 @@ class JsonObject : public Internals::JsonPrintable, // JsonObject& createNestedObject(TKey); // TKey = const std::string&, const String& template - typename TypeTraits::EnableIf::value, - JsonObject&>::type - createNestedObject(const TString& key) { + JsonObject& createNestedObject(const TString& key) { return createNestedObject_impl(key); } // // JsonObject& createNestedObject(TKey); - // TKey = const char*, const char[N], const FlashStringHelper* + // TKey = char*, const char*, char[], const char[], const FlashStringHelper* template - JsonObject& createNestedObject(const TString* key) { - return createNestedObject_impl(key); + JsonObject& createNestedObject(TString* key) { + return createNestedObject_impl(key); } // Tells weither the specified key is present and associated with a value. @@ -248,17 +223,15 @@ class JsonObject : public Internals::JsonPrintable, // bool containsKey(TKey); // TKey = const std::string&, const String& template - typename TypeTraits::EnableIf::value, - bool>::type - containsKey(const TString& key) const { + bool containsKey(const TString& key) const { return findKey(key) != end(); } // // bool containsKey(TKey); - // TKey = const char*, const char[N], const FlashStringHelper* + // TKey = char*, const char*, char[], const char[], const FlashStringHelper* template - bool containsKey(const TString* key) const { - return findKey(key) != end(); + bool containsKey(TString* key) const { + return findKey(key) != end(); } // Removes the specified key and the associated value. @@ -266,17 +239,15 @@ class JsonObject : public Internals::JsonPrintable, // void remove(TKey); // TKey = const std::string&, const String& template - typename TypeTraits::EnableIf::value, - void>::type - remove(const TString& key) { + void remove(const TString& key) { remove(findKey(key)); } // // void remove(TKey); - // TKey = const char*, const char[N], const FlashStringHelper* + // TKey = char*, const char*, char[], const char[], const FlashStringHelper* template - void remove(const TString* key) { - remove(findKey(key)); + void remove(TString* key) { + remove(findKey(key)); } // // void remove(iterator) @@ -315,16 +286,22 @@ class JsonObject : public Internals::JsonPrintable, template bool set_impl(TStringRef key, TValueRef value) { + // ignore null key + if (Internals::StringTraits::is_null(key)) return false; + + // search a matching key iterator it = findKey(key); if (it == end()) { + // add the key it = Internals::List::add(); if (it == end()) return false; - bool key_ok = - Internals::ValueSetter::set(_buffer, it->key, key); + Internals::ValueSaver::save(_buffer, it->key, key); if (!key_ok) return false; } - return Internals::ValueSetter::set(_buffer, it->value, value); + + // save the value + return Internals::ValueSaver::save(_buffer, it->value, value); } template @@ -347,5 +324,5 @@ struct JsonVariantDefault { return JsonObject::invalid(); } }; -} -} +} // namespace Internals +} // namespace ArduinoJson diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/JsonObjectImpl.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonObjectImpl.hpp similarity index 79% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/JsonObjectImpl.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonObjectImpl.hpp index 9741bfadd..e7689b507 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/JsonObjectImpl.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonObjectImpl.hpp @@ -1,9 +1,6 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/JsonObjectSubscript.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonObjectSubscript.hpp similarity index 66% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/JsonObjectSubscript.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonObjectSubscript.hpp index 99dfe7cd0..6ac476370 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/JsonObjectSubscript.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonObjectSubscript.hpp @@ -1,9 +1,6 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once @@ -17,6 +14,7 @@ #endif namespace ArduinoJson { +namespace Internals { template class JsonObjectSubscript @@ -34,23 +32,20 @@ class JsonObjectSubscript // Set the specified value // - // operator=(TValue); + // operator=(const TValue&); // TValue = bool, char, long, int, short, float, double, - // const std::string&, const String&, - // const JsonArray&, const JsonObject& + // std::string, String, JsonArray, JsonObject template - FORCE_INLINE - typename TypeTraits::EnableIf::value, - this_type&>::type - operator=(const TValue& src) { + FORCE_INLINE typename EnableIf::value, this_type&>::type + operator=(const TValue& src) { _object.set(_key, src); return *this; } // // operator=(TValue); - // TValue = const char*, const char[N], const FlashStringHelper* + // TValue = char*, const char*, const FlashStringHelper* template - FORCE_INLINE this_type& operator=(const TValue* src) { + FORCE_INLINE this_type& operator=(TValue* src) { _object.set(_key, src); return *this; } @@ -60,7 +55,7 @@ class JsonObjectSubscript } template - FORCE_INLINE typename Internals::JsonVariantAs::type as() const { + FORCE_INLINE typename JsonVariantAs::type as() const { return _object.get(_key); } @@ -71,20 +66,17 @@ class JsonObjectSubscript // Sets the specified value. // - // bool set(TValue); + // bool set(const TValue&); // TValue = bool, char, long, int, short, float, double, RawJson, JsonVariant, - // const std::string&, const String&, - // const JsonArray&, const JsonObject& + // std::string, String, JsonArray, JsonObject template - FORCE_INLINE - typename TypeTraits::EnableIf::value, - bool>::type - set(const TValue& value) { + FORCE_INLINE typename EnableIf::value, bool>::type set( + const TValue& value) { return _object.set(_key, value); } // // bool set(TValue); - // TValue = const char*, const char[N], const FlashStringHelper* + // TValue = char*, const char, const FlashStringHelper* template FORCE_INLINE bool set(const TValue* value) { return _object.set(_key, value); @@ -110,7 +102,8 @@ inline std::ostream& operator<<(std::ostream& os, return source.printTo(os); } #endif -} // namespace ArduinoJson +} +} #ifdef _MSC_VER #pragma warning(pop) diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/JsonPair.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonPair.hpp similarity index 53% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/JsonPair.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonPair.hpp index 5a32601c0..417243045 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/JsonPair.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonPair.hpp @@ -1,9 +1,6 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/JsonVariant.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonVariant.hpp similarity index 72% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/JsonVariant.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonVariant.hpp index 9037d54d1..43c51b770 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/JsonVariant.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonVariant.hpp @@ -1,9 +1,6 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once @@ -39,7 +36,7 @@ class JsonObject; // - a char, short, int or a long (signed or unsigned) // - a string (const char*) // - a reference to a JsonArray or JsonObject -class JsonVariant : public JsonVariantBase { +class JsonVariant : public Internals::JsonVariantBase { template friend class Internals::JsonSerializer; @@ -59,8 +56,8 @@ class JsonVariant : public JsonVariantBase { // JsonVariant(double value); // JsonVariant(float value); template - JsonVariant(T value, typename TypeTraits::EnableIf< - TypeTraits::IsFloatingPoint::value>::type * = 0) { + JsonVariant(T value, typename Internals::EnableIf< + Internals::IsFloatingPoint::value>::type * = 0) { using namespace Internals; _type = JSON_FLOAT; _content.asFloat = static_cast(value); @@ -68,8 +65,8 @@ class JsonVariant : public JsonVariantBase { template DEPRECATED("Second argument is not supported anymore") JsonVariant(T value, uint8_t, - typename TypeTraits::EnableIf< - TypeTraits::IsFloatingPoint::value>::type * = 0) { + typename Internals::EnableIf< + Internals::IsFloatingPoint::value>::type * = 0) { using namespace Internals; _type = JSON_FLOAT; _content.asFloat = static_cast(value); @@ -82,9 +79,11 @@ class JsonVariant : public JsonVariantBase { // JsonVariant(signed long) // JsonVariant(signed char) template - JsonVariant(T value, typename TypeTraits::EnableIf< - TypeTraits::IsSignedIntegral::value || - TypeTraits::IsSame::value>::type * = 0) { + JsonVariant( + T value, + typename Internals::EnableIf::value || + Internals::IsSame::value>::type * = + 0) { using namespace Internals; if (value >= 0) { _type = JSON_POSITIVE_INTEGER; @@ -99,8 +98,8 @@ class JsonVariant : public JsonVariantBase { // JsonVariant(unsigned long) template JsonVariant(T value, - typename TypeTraits::EnableIf< - TypeTraits::IsUnsignedIntegral::value>::type * = 0) { + typename Internals::EnableIf< + Internals::IsUnsignedIntegral::value>::type * = 0) { using namespace Internals; _type = JSON_POSITIVE_INTEGER; _content.asInteger = static_cast(value); @@ -113,14 +112,14 @@ class JsonVariant : public JsonVariantBase { template JsonVariant( const TChar *value, - typename TypeTraits::EnableIf::value>::type * = + typename Internals::EnableIf::value>::type * = 0) { _type = Internals::JSON_STRING; _content.asString = reinterpret_cast(value); } // Create a JsonVariant containing an unparsed string - JsonVariant(RawJson value) { + JsonVariant(Internals::RawJsonString value) { _type = Internals::JSON_UNPARSED; _content.asString = value; } @@ -147,14 +146,13 @@ class JsonVariant : public JsonVariantBase { // unsigned int as() const; // unsigned long as() const; template - const typename TypeTraits::EnableIf::value, T>::type + const typename Internals::EnableIf::value, T>::type as() const { return variantAsInteger(); } // bool as() const template - const typename TypeTraits::EnableIf::value, - T>::type + const typename Internals::EnableIf::value, T>::type as() const { return variantAsInteger() != 0; } @@ -162,8 +160,8 @@ class JsonVariant : public JsonVariantBase { // double as() const; // float as() const; template - const typename TypeTraits::EnableIf::value, - T>::type + const typename Internals::EnableIf::value, + T>::type as() const { return variantAsFloat(); } @@ -171,9 +169,9 @@ class JsonVariant : public JsonVariantBase { // const char* as() const; // const char* as() const; template - typename TypeTraits::EnableIf::value || - TypeTraits::IsSame::value, - const char *>::type + typename Internals::EnableIf::value || + Internals::IsSame::value, + const char *>::type as() const { return variantAsString(); } @@ -181,7 +179,7 @@ class JsonVariant : public JsonVariantBase { // std::string as() const; // String as() const; template - typename TypeTraits::EnableIf::has_append, T>::type + typename Internals::EnableIf::has_append, T>::type as() const { const char *cstr = variantAsString(); if (cstr) return T(cstr); @@ -193,9 +191,9 @@ class JsonVariant : public JsonVariantBase { // JsonArray& as const; // JsonArray& as const; template - typename TypeTraits::EnableIf< - TypeTraits::IsSame::type, - JsonArray>::value, + typename Internals::EnableIf< + Internals::IsSame::type, + JsonArray>::value, JsonArray &>::type as() const { return variantAsArray(); @@ -203,9 +201,9 @@ class JsonVariant : public JsonVariantBase { // // const JsonArray& as const; template - typename TypeTraits::EnableIf< - TypeTraits::IsSame::type, - const JsonArray>::value, + typename Internals::EnableIf< + Internals::IsSame::type, + const JsonArray>::value, const JsonArray &>::type as() const { return variantAsArray(); @@ -214,9 +212,9 @@ class JsonVariant : public JsonVariantBase { // JsonObject& as const; // JsonObject& as const; template - typename TypeTraits::EnableIf< - TypeTraits::IsSame::type, - JsonObject>::value, + typename Internals::EnableIf< + Internals::IsSame::type, + JsonObject>::value, JsonObject &>::type as() const { return variantAsObject(); @@ -225,9 +223,9 @@ class JsonVariant : public JsonVariantBase { // JsonObject& as const; // JsonObject& as const; template - typename TypeTraits::EnableIf< - TypeTraits::IsSame::type, - const JsonObject>::value, + typename Internals::EnableIf< + Internals::IsSame::type, + const JsonObject>::value, const JsonObject &>::type as() const { return variantAsObject(); @@ -235,8 +233,8 @@ class JsonVariant : public JsonVariantBase { // // JsonVariant as const; template - typename TypeTraits::EnableIf::value, - T>::type + typename Internals::EnableIf::value, + T>::type as() const { return *this; } @@ -254,33 +252,34 @@ class JsonVariant : public JsonVariantBase { // bool is() const; // bool is() const; template - typename TypeTraits::EnableIf::value, bool>::type - is() const { + typename Internals::EnableIf::value, bool>::type is() + const { return variantIsInteger(); } // // bool is() const; // bool is() const; template - typename TypeTraits::EnableIf::value, - bool>::type + typename Internals::EnableIf::value, bool>::type is() const { return variantIsFloat(); } // // bool is() const template - typename TypeTraits::EnableIf::value, bool>::type + typename Internals::EnableIf::value, bool>::type is() const { return variantIsBoolean(); } // // bool is() const; // bool is() const; + // bool is() const; template - typename TypeTraits::EnableIf::value || - TypeTraits::IsSame::value, - bool>::type + typename Internals::EnableIf::value || + Internals::IsSame::value || + Internals::StringTraits::has_append, + bool>::type is() const { return variantIsString(); } @@ -289,11 +288,10 @@ class JsonVariant : public JsonVariantBase { // bool is const; // bool is const; template - typename TypeTraits::EnableIf< - TypeTraits::IsSame< - typename TypeTraits::RemoveConst< - typename TypeTraits::RemoveReference::type>::type, - JsonArray>::value, + typename Internals::EnableIf< + Internals::IsSame::type>::type, + JsonArray>::value, bool>::type is() const { return variantIsArray(); @@ -303,11 +301,10 @@ class JsonVariant : public JsonVariantBase { // bool is const; // bool is const; template - typename TypeTraits::EnableIf< - TypeTraits::IsSame< - typename TypeTraits::RemoveConst< - typename TypeTraits::RemoveReference::type>::type, - JsonObject>::value, + typename Internals::EnableIf< + Internals::IsSame::type>::type, + JsonObject>::value, bool>::type is() const { return variantIsObject(); @@ -357,4 +354,4 @@ DEPRECATED("Decimal places are ignored, use the double value instead") inline JsonVariant double_with_n_digits(double value, uint8_t) { return JsonVariant(value); } -} +} // namespace ArduinoJson diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/JsonVariantBase.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonVariantBase.hpp similarity index 59% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/JsonVariantBase.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonVariantBase.hpp index a010e253f..44acf2e14 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/JsonVariantBase.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonVariantBase.hpp @@ -1,23 +1,24 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once #include "JsonVariantCasts.hpp" #include "JsonVariantComparisons.hpp" +#include "JsonVariantOr.hpp" #include "JsonVariantSubscripts.hpp" #include "Serialization/JsonPrintable.hpp" namespace ArduinoJson { +namespace Internals { template -class JsonVariantBase : public Internals::JsonPrintable, +class JsonVariantBase : public JsonPrintable, public JsonVariantCasts, public JsonVariantComparisons, + public JsonVariantOr, public JsonVariantSubscripts, - public TypeTraits::JsonVariantTag {}; + public JsonVariantTag {}; +} } diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/JsonVariantCasts.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonVariantCasts.hpp similarity index 89% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/JsonVariantCasts.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonVariantCasts.hpp index b3b41b4ba..68f5bd7dd 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/JsonVariantCasts.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonVariantCasts.hpp @@ -1,9 +1,6 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once @@ -11,6 +8,7 @@ #include "Polyfills/attributes.hpp" namespace ArduinoJson { +namespace Internals { template class JsonVariantCasts { @@ -58,3 +56,4 @@ class JsonVariantCasts { } }; } +} diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/JsonVariantComparisons.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonVariantComparisons.hpp similarity index 75% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/JsonVariantComparisons.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonVariantComparisons.hpp index eb0ef5c2c..47f9d6322 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/JsonVariantComparisons.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonVariantComparisons.hpp @@ -1,9 +1,6 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once @@ -12,6 +9,7 @@ #include "TypeTraits/IsVariant.hpp" namespace ArduinoJson { +namespace Internals { template class JsonVariantComparisons { @@ -23,10 +21,8 @@ class JsonVariantComparisons { } template - friend - typename TypeTraits::EnableIf::value, - bool>::type - operator==(TComparand comparand, const JsonVariantComparisons &variant) { + friend typename EnableIf::value, bool>::type + operator==(TComparand comparand, const JsonVariantComparisons &variant) { return variant.equals(comparand); } @@ -37,10 +33,8 @@ class JsonVariantComparisons { } template - friend - typename TypeTraits::EnableIf::value, - bool>::type - operator!=(TComparand comparand, const JsonVariantComparisons &variant) { + friend typename EnableIf::value, bool>::type + operator!=(TComparand comparand, const JsonVariantComparisons &variant) { return !variant.equals(comparand); } @@ -97,7 +91,7 @@ class JsonVariantComparisons { } template - const typename Internals::JsonVariantAs::type as() const { + const typename JsonVariantAs::type as() const { return impl()->template as(); } @@ -107,17 +101,16 @@ class JsonVariantComparisons { } template - typename TypeTraits::EnableIf::value, - bool>::type - equals(const TString &comparand) const { + typename EnableIf::has_equals, bool>::type equals( + const TString &comparand) const { const char *value = as(); - return Internals::StringTraits::equals(comparand, value); + return StringTraits::equals(comparand, value); } template - typename TypeTraits::EnableIf::value && - !TypeTraits::IsString::value, - bool>::type + typename EnableIf::value && + !StringTraits::has_equals, + bool>::type equals(const TComparand &comparand) const { return as() == comparand; } @@ -136,9 +129,11 @@ class JsonVariantComparisons { if (is() && right.template is()) return as() == right.template as(); if (is() && right.template is()) - return strcmp(as(), right.template as()) == 0; + return StringTraits::equals(as(), + right.template as()); return false; } }; -} +} // namespace Internals +} // namespace ArduinoJson diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/JsonVariantImpl.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonVariantImpl.hpp similarity index 87% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/JsonVariantImpl.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonVariantImpl.hpp index eb6a4735d..31f96ce1a 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/JsonVariantImpl.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonVariantImpl.hpp @@ -1,9 +1,6 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once @@ -61,7 +58,7 @@ inline T JsonVariant::variantAsInteger() const { return T(~_content.asInteger + 1); case JSON_STRING: case JSON_UNPARSED: - return Polyfills::parseInteger(_content.asString); + return parseInteger(_content.asString); default: return T(_content.asFloat); } @@ -89,7 +86,7 @@ inline T JsonVariant::variantAsFloat() const { return -static_cast(_content.asInteger); case JSON_STRING: case JSON_UNPARSED: - return Polyfills::parseFloat(_content.asString); + return parseFloat(_content.asString); default: return static_cast(_content.asFloat); } @@ -109,7 +106,7 @@ inline bool JsonVariant::variantIsInteger() const { using namespace Internals; return _type == JSON_POSITIVE_INTEGER || _type == JSON_NEGATIVE_INTEGER || - (_type == JSON_UNPARSED && Polyfills::isInteger(_content.asString)); + (_type == JSON_UNPARSED && isInteger(_content.asString)); } inline bool JsonVariant::variantIsFloat() const { @@ -117,7 +114,7 @@ inline bool JsonVariant::variantIsFloat() const { return _type == JSON_FLOAT || _type == JSON_POSITIVE_INTEGER || _type == JSON_NEGATIVE_INTEGER || - (_type == JSON_UNPARSED && Polyfills::isFloat(_content.asString)); + (_type == JSON_UNPARSED && isFloat(_content.asString)); } #if ARDUINOJSON_ENABLE_STD_STREAM diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonVariantOr.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonVariantOr.hpp new file mode 100644 index 000000000..d8022fcb2 --- /dev/null +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonVariantOr.hpp @@ -0,0 +1,52 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include "Data/JsonVariantAs.hpp" +#include "Polyfills/attributes.hpp" +#include "TypeTraits/EnableIf.hpp" +#include "TypeTraits/IsIntegral.hpp" + +namespace ArduinoJson { +namespace Internals { + +template +class JsonVariantOr { + public: + // Returns the default value if the JsonVariant is undefined of incompatible + template + typename EnableIf::value, T>::type operator|( + const T &defaultValue) const { + if (impl()->template is()) + return impl()->template as(); + else + return defaultValue; + } + + // Returns the default value if the JsonVariant is undefined of incompatible + // Special case for string: null is treated as undefined + const char *operator|(const char *defaultValue) const { + const char *value = impl()->template as(); + return value ? value : defaultValue; + } + + // Returns the default value if the JsonVariant is undefined of incompatible + // Special case for integers: we also accept double + template + typename EnableIf::value, Integer>::type operator|( + const Integer &defaultValue) const { + if (impl()->template is()) + return impl()->template as(); + else + return defaultValue; + } + + private: + const TImpl *impl() const { + return static_cast(this); + } +}; +} // namespace Internals +} // namespace ArduinoJson diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/JsonVariantSubscripts.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonVariantSubscripts.hpp similarity index 70% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/JsonVariantSubscripts.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonVariantSubscripts.hpp index 2143236a6..279ee019f 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/JsonVariantSubscripts.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/JsonVariantSubscripts.hpp @@ -1,9 +1,6 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once @@ -13,6 +10,7 @@ #include "TypeTraits/EnableIf.hpp" namespace ArduinoJson { +namespace Internals { // Forward declarations. class JsonArraySubscript; @@ -44,19 +42,18 @@ class JsonVariantSubscripts { // const JsonObjectSubscript operator[](TKey) const; // TKey = const std::string&, const String& template - FORCE_INLINE typename TypeTraits::EnableIf< - Internals::StringTraits::has_equals, - const JsonObjectSubscript >::type - operator[](const TString &key) const { + FORCE_INLINE + typename EnableIf::has_equals, + const JsonObjectSubscript >::type + operator[](const TString &key) const { return impl()->template as()[key]; } // // const JsonObjectSubscript operator[](TKey) const; // TKey = const std::string&, const String& template - FORCE_INLINE typename TypeTraits::EnableIf< - Internals::StringTraits::has_equals, - JsonObjectSubscript >::type + FORCE_INLINE typename EnableIf::has_equals, + JsonObjectSubscript >::type operator[](const TString &key) { return impl()->template as()[key]; } @@ -64,9 +61,8 @@ class JsonVariantSubscripts { // JsonObjectSubscript operator[](TKey); // TKey = const char*, const char[N], const FlashStringHelper* template - FORCE_INLINE typename TypeTraits::EnableIf< - Internals::StringTraits::has_equals, - JsonObjectSubscript >::type + FORCE_INLINE typename EnableIf::has_equals, + JsonObjectSubscript >::type operator[](const TString *key) { return impl()->template as()[key]; } @@ -74,10 +70,10 @@ class JsonVariantSubscripts { // JsonObjectSubscript operator[](TKey); // TKey = const char*, const char[N], const FlashStringHelper* template - FORCE_INLINE typename TypeTraits::EnableIf< - Internals::StringTraits::has_equals, - const JsonObjectSubscript >::type - operator[](const TString *key) const { + FORCE_INLINE + typename EnableIf::has_equals, + const JsonObjectSubscript >::type + operator[](const TString *key) const { return impl()->template as()[key]; } @@ -87,3 +83,4 @@ class JsonVariantSubscripts { } }; } +} diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Polyfills/attributes.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Polyfills/attributes.hpp similarity index 74% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/Polyfills/attributes.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/Polyfills/attributes.hpp index e4f9406ac..b49091ddc 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Polyfills/attributes.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Polyfills/attributes.hpp @@ -1,15 +1,12 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once #ifdef _MSC_VER // Visual Studio -#define FORCE_INLINE __forceinline +#define FORCE_INLINE // __forceinline causes C4714 when returning std::string #define NO_INLINE __declspec(noinline) #define DEPRECATED(msg) __declspec(deprecated(msg)) diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Polyfills/ctype.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Polyfills/ctype.hpp similarity index 50% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/Polyfills/ctype.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/Polyfills/ctype.hpp index f13e3edf6..2d52703cd 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Polyfills/ctype.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Polyfills/ctype.hpp @@ -1,14 +1,11 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once namespace ArduinoJson { -namespace Polyfills { +namespace Internals { inline bool isdigit(char c) { return '0' <= c && c <= '9'; diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Polyfills/isFloat.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Polyfills/isFloat.hpp similarity index 75% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/Polyfills/isFloat.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/Polyfills/isFloat.hpp index f6b9c97d9..973b89fe9 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Polyfills/isFloat.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Polyfills/isFloat.hpp @@ -1,9 +1,6 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once @@ -11,7 +8,7 @@ #include "./ctype.hpp" namespace ArduinoJson { -namespace Polyfills { +namespace Internals { inline bool isFloat(const char* s) { if (!s) return false; diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/Polyfills/isInteger.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Polyfills/isInteger.hpp new file mode 100644 index 000000000..8049079a7 --- /dev/null +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Polyfills/isInteger.hpp @@ -0,0 +1,19 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include "./ctype.hpp" + +namespace ArduinoJson { +namespace Internals { + +inline bool isInteger(const char* s) { + if (!s || !*s) return false; + if (issign(*s)) s++; + while (isdigit(*s)) s++; + return *s == '\0'; +} +} // namespace Internals +} // namespace ArduinoJson diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Polyfills/math.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Polyfills/math.hpp similarity index 52% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/Polyfills/math.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/Polyfills/math.hpp index 9dbd161d6..48773edd2 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Polyfills/math.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Polyfills/math.hpp @@ -1,14 +1,11 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once namespace ArduinoJson { -namespace Polyfills { +namespace Internals { template bool isNaN(T x) { return x != x; diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Polyfills/parseFloat.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Polyfills/parseFloat.hpp similarity index 89% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/Polyfills/parseFloat.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/Polyfills/parseFloat.hpp index e5d99f1bf..49b0f6fcd 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Polyfills/parseFloat.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Polyfills/parseFloat.hpp @@ -1,9 +1,6 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once @@ -12,11 +9,11 @@ #include "./math.hpp" namespace ArduinoJson { -namespace Polyfills { +namespace Internals { template inline T parseFloat(const char* s) { - typedef TypeTraits::FloatTraits traits; + typedef FloatTraits traits; typedef typename traits::mantissa_type mantissa_t; typedef typename traits::exponent_type exponent_t; diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Polyfills/parseInteger.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Polyfills/parseInteger.hpp similarity index 75% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/Polyfills/parseInteger.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/Polyfills/parseInteger.hpp index 023d7bcbd..e8f197494 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Polyfills/parseInteger.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Polyfills/parseInteger.hpp @@ -1,9 +1,6 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once @@ -13,7 +10,7 @@ #include "./ctype.hpp" namespace ArduinoJson { -namespace Polyfills { +namespace Internals { template T parseInteger(const char *s) { if (!s) return 0; // NULL diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/RawJson.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/RawJson.hpp new file mode 100644 index 000000000..4beb980ee --- /dev/null +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/RawJson.hpp @@ -0,0 +1,46 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +namespace ArduinoJson { + +namespace Internals { +// A special type of data that can be used to insert pregenerated JSON portions. +template +class RawJsonString { + public: + explicit RawJsonString(T str) : _str(str) {} + operator T() const { + return _str; + } + + private: + T _str; +}; + +template +struct StringTraits, void> { + static bool is_null(RawJsonString source) { + return StringTraits::is_null(static_cast(source)); + } + + typedef RawJsonString duplicate_t; + + template + static duplicate_t duplicate(RawJsonString source, Buffer* buffer) { + return duplicate_t(StringTraits::duplicate(source, buffer)); + } + + static const bool has_append = false; + static const bool has_equals = false; + static const bool should_duplicate = StringTraits::should_duplicate; +}; +} + +template +inline Internals::RawJsonString RawJson(T str) { + return Internals::RawJsonString(str); +} +} diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Serialization/DummyPrint.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/DummyPrint.hpp similarity index 64% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/Serialization/DummyPrint.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/DummyPrint.hpp index 656aa9ed6..9fdf2d6a0 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Serialization/DummyPrint.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/DummyPrint.hpp @@ -1,9 +1,6 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Serialization/DynamicStringBuilder.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/DynamicStringBuilder.hpp similarity index 80% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/Serialization/DynamicStringBuilder.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/DynamicStringBuilder.hpp index a344588de..41be6392c 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Serialization/DynamicStringBuilder.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/DynamicStringBuilder.hpp @@ -1,9 +1,6 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Serialization/FloatParts.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/FloatParts.hpp similarity index 91% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/Serialization/FloatParts.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/FloatParts.hpp index fa650723d..c14e3b553 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Serialization/FloatParts.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/FloatParts.hpp @@ -1,9 +1,6 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once @@ -59,7 +56,7 @@ struct FloatParts { } static int16_t normalize(TFloat& value) { - typedef TypeTraits::FloatTraits traits; + typedef FloatTraits traits; int16_t powersOf10 = 0; int8_t index = sizeof(TFloat) == 8 ? 8 : 5; diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Serialization/IndentedPrint.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/IndentedPrint.hpp similarity index 89% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/Serialization/IndentedPrint.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/IndentedPrint.hpp index 66ca6ac2f..864f9aaa4 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Serialization/IndentedPrint.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/IndentedPrint.hpp @@ -1,9 +1,6 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Serialization/JsonPrintable.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/JsonPrintable.hpp similarity index 80% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/Serialization/JsonPrintable.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/JsonPrintable.hpp index f33e5584e..43d413a85 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Serialization/JsonPrintable.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/JsonPrintable.hpp @@ -1,9 +1,6 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once @@ -32,9 +29,8 @@ template class JsonPrintable { public: template - typename TypeTraits::EnableIf::value, - size_t>::type - printTo(Print &print) const { + typename EnableIf::has_append, size_t>::type printTo( + Print &print) const { JsonWriter writer(print); JsonSerializer >::serialize(downcast(), writer); return writer.bytesWritten(); @@ -59,8 +55,8 @@ class JsonPrintable { } template - typename TypeTraits::EnableIf::has_append, size_t>::type - printTo(TString &str) const { + typename EnableIf::has_append, size_t>::type printTo( + TString &str) const { DynamicStringBuilder sb(str); return printTo(sb); } @@ -82,15 +78,14 @@ class JsonPrintable { } template - typename TypeTraits::EnableIf::value, - size_t>::type + typename EnableIf::has_append, size_t>::type prettyPrintTo(Print &print) const { IndentedPrint indentedPrint(print); return prettyPrintTo(indentedPrint); } template - typename TypeTraits::EnableIf::has_append, size_t>::type + typename EnableIf::has_append, size_t>::type prettyPrintTo(TString &str) const { DynamicStringBuilder sb(str); return prettyPrintTo(sb); diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Serialization/JsonSerializer.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/JsonSerializer.hpp similarity index 79% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/Serialization/JsonSerializer.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/JsonSerializer.hpp index 86ed73ee1..0cb537f7d 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Serialization/JsonSerializer.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/JsonSerializer.hpp @@ -1,9 +1,6 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once @@ -12,14 +9,15 @@ namespace ArduinoJson { class JsonArray; -class JsonArraySubscript; class JsonObject; -template -class JsonObjectSubscript; class JsonVariant; namespace Internals { +class JsonArraySubscript; +template +class JsonObjectSubscript; + template class JsonSerializer { public: diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Serialization/JsonSerializerImpl.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/JsonSerializerImpl.hpp similarity index 94% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/Serialization/JsonSerializerImpl.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/JsonSerializerImpl.hpp index f544488af..0faae2769 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Serialization/JsonSerializerImpl.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/JsonSerializerImpl.hpp @@ -1,9 +1,6 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Serialization/JsonWriter.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/JsonWriter.hpp similarity index 91% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/Serialization/JsonWriter.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/JsonWriter.hpp index 2d54a2a0b..146d51dcb 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Serialization/JsonWriter.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/JsonWriter.hpp @@ -1,9 +1,6 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once @@ -82,14 +79,14 @@ class JsonWriter { template void writeFloat(TFloat value) { - if (Polyfills::isNaN(value)) return writeRaw("NaN"); + if (isNaN(value)) return writeRaw("NaN"); if (value < 0.0) { writeRaw('-'); value = -value; } - if (Polyfills::isInfinity(value)) return writeRaw("Infinity"); + if (isInfinity(value)) return writeRaw("Infinity"); FloatParts parts(value); diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Serialization/Prettyfier.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/Prettyfier.hpp similarity index 93% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/Serialization/Prettyfier.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/Prettyfier.hpp index 377138b4f..8b4f0d2eb 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Serialization/Prettyfier.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/Prettyfier.hpp @@ -1,9 +1,6 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Serialization/StaticStringBuilder.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/StaticStringBuilder.hpp similarity index 77% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/Serialization/StaticStringBuilder.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/StaticStringBuilder.hpp index 2df932fd4..9617bbd97 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Serialization/StaticStringBuilder.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/StaticStringBuilder.hpp @@ -1,9 +1,6 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Serialization/StreamPrintAdapter.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/StreamPrintAdapter.hpp similarity index 77% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/Serialization/StreamPrintAdapter.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/StreamPrintAdapter.hpp index 9c2f86fd7..60f0af4a3 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/Serialization/StreamPrintAdapter.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/Serialization/StreamPrintAdapter.hpp @@ -1,9 +1,6 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/StaticJsonBuffer.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/StaticJsonBuffer.hpp similarity index 89% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/StaticJsonBuffer.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/StaticJsonBuffer.hpp index 8eaa4bb53..267d9d018 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/StaticJsonBuffer.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/StaticJsonBuffer.hpp @@ -1,15 +1,13 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once #include "JsonBufferBase.hpp" namespace ArduinoJson { +namespace Internals { class StaticJsonBufferBase : public JsonBufferBase { public: @@ -93,6 +91,7 @@ class StaticJsonBufferBase : public JsonBufferBase { size_t _capacity; size_t _size; }; +} #if defined(__clang__) #pragma clang diagnostic push @@ -108,9 +107,10 @@ class StaticJsonBufferBase : public JsonBufferBase { // The template paramenter CAPACITY specifies the capacity of the buffer in // bytes. template -class StaticJsonBuffer : public StaticJsonBufferBase { +class StaticJsonBuffer : public Internals::StaticJsonBufferBase { public: - explicit StaticJsonBuffer() : StaticJsonBufferBase(_buffer, CAPACITY) {} + explicit StaticJsonBuffer() + : Internals::StaticJsonBufferBase(_buffer, CAPACITY) {} private: char _buffer[CAPACITY]; diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/StringTraits/ArduinoStream.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/StringTraits/ArduinoStream.hpp similarity index 64% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/StringTraits/ArduinoStream.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/StringTraits/ArduinoStream.hpp index 87d1672d5..5db0852b8 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/StringTraits/ArduinoStream.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/StringTraits/ArduinoStream.hpp @@ -1,9 +1,6 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once @@ -46,14 +43,17 @@ struct ArduinoStreamTraits { return c; } }; + + static const bool has_append = false; + static const bool has_equals = false; }; template -struct StringTraits::type>::value>::type> +struct StringTraits< + TStream, + // match any type that is derived from Stream: + typename EnableIf< + IsBaseOf::type>::value>::type> : ArduinoStreamTraits {}; } } diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/StringTraits/CharPointer.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/StringTraits/CharPointer.hpp similarity index 54% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/StringTraits/CharPointer.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/StringTraits/CharPointer.hpp index 203e3924e..98896ccfb 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/StringTraits/CharPointer.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/StringTraits/CharPointer.hpp @@ -1,9 +1,6 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once @@ -33,26 +30,35 @@ struct CharPointerTraits { }; static bool equals(const TChar* str, const char* expected) { - return strcmp(reinterpret_cast(str), expected) == 0; + const char* actual = reinterpret_cast(str); + if (!actual || !expected) return actual == expected; + return strcmp(actual, expected) == 0; } + static bool is_null(const TChar* str) { + return !str; + } + + typedef const char* duplicate_t; + template - static char* duplicate(const TChar* str, Buffer* buffer) { + static duplicate_t duplicate(const TChar* str, Buffer* buffer) { if (!str) return NULL; size_t size = strlen(reinterpret_cast(str)) + 1; void* dup = buffer->alloc(size); if (dup != NULL) memcpy(dup, str, size); - return static_cast(dup); + return static_cast(dup); } static const bool has_append = false; static const bool has_equals = true; - static const bool should_duplicate = false; + static const bool should_duplicate = !IsConst::value; }; +// char*, unsigned char*, signed char* +// const char*, const unsigned char*, const signed char* template -struct StringTraits::value>::type> +struct StringTraits::value>::type> : CharPointerTraits {}; -} -} +} // namespace Internals +} // namespace ArduinoJson diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/StringTraits/FlashString.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/StringTraits/FlashString.hpp similarity index 64% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/StringTraits/FlashString.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/StringTraits/FlashString.hpp index 37e36b8d2..0701b9ba2 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/StringTraits/FlashString.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/StringTraits/FlashString.hpp @@ -1,9 +1,6 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once @@ -34,23 +31,31 @@ struct StringTraits { }; static bool equals(const __FlashStringHelper* str, const char* expected) { - return strcmp_P(expected, (const char*)str) == 0; + const char* actual = reinterpret_cast(str); + if (!actual || !expected) return actual == expected; + return strcmp_P(expected, actual) == 0; } + static bool is_null(const __FlashStringHelper* str) { + return !str; + } + + typedef const char* duplicate_t; + template - static char* duplicate(const __FlashStringHelper* str, Buffer* buffer) { + static duplicate_t duplicate(const __FlashStringHelper* str, Buffer* buffer) { if (!str) return NULL; size_t size = strlen_P((const char*)str) + 1; void* dup = buffer->alloc(size); if (dup != NULL) memcpy_P(dup, (const char*)str, size); - return static_cast(dup); + return static_cast(dup); } static const bool has_append = false; static const bool has_equals = true; static const bool should_duplicate = true; }; -} -} +} // namespace Internals +} // namespace ArduinoJson #endif diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/StringTraits/StdStream.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/StringTraits/StdStream.hpp similarity index 63% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/StringTraits/StdStream.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/StringTraits/StdStream.hpp index 35049d941..227c74406 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/StringTraits/StdStream.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/StringTraits/StdStream.hpp @@ -1,9 +1,6 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once @@ -45,14 +42,17 @@ struct StdStreamTraits { return _stream.eof() ? '\0' : static_cast(_stream.get()); } }; + + static const bool has_append = false; + static const bool has_equals = false; }; template -struct StringTraits::type>::value>::type> +struct StringTraits< + TStream, + // match any type that is derived from std::istream: + typename EnableIf::type>::value>::type> : StdStreamTraits {}; } } diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/StringTraits/StdString.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/StringTraits/StdString.hpp similarity index 70% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/StringTraits/StdString.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/StringTraits/StdString.hpp index 959c47466..35f4461d8 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/StringTraits/StdString.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/StringTraits/StdString.hpp @@ -1,9 +1,6 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once @@ -22,13 +19,20 @@ namespace Internals { template struct StdStringTraits { + typedef const char* duplicate_t; + template - static char* duplicate(const TString& str, Buffer* buffer) { + static duplicate_t duplicate(const TString& str, Buffer* buffer) { if (!str.c_str()) return NULL; // <- Arduino string can return NULL size_t size = str.length() + 1; void* dup = buffer->alloc(size); if (dup != NULL) memcpy(dup, str.c_str(), size); - return static_cast(dup); + return static_cast(dup); + } + + static bool is_null(const TString& str) { + // Arduino's String::c_str() can return NULL + return !str.c_str(); } struct Reader : CharPointerTraits::Reader { @@ -36,7 +40,10 @@ struct StdStringTraits { }; static bool equals(const TString& str, const char* expected) { - return 0 == strcmp(str.c_str(), expected); + // Arduino's String::c_str() can return NULL + const char* actual = str.c_str(); + if (!actual || !expected) return actual == expected; + return 0 == strcmp(actual, expected); } static void append(TString& str, char c) { @@ -64,7 +71,7 @@ struct StringTraits : StdStringTraits { template <> struct StringTraits : StdStringTraits {}; #endif -} -} +} // namespace Internals +} // namespace ArduinoJson #endif diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/StringTraits/StringTraits.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/StringTraits/StringTraits.hpp similarity index 55% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/StringTraits/StringTraits.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/StringTraits/StringTraits.hpp index 55bacca26..dd5694b2e 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/StringTraits/StringTraits.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/StringTraits/StringTraits.hpp @@ -1,9 +1,6 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once @@ -12,13 +9,17 @@ #include "../TypeTraits/EnableIf.hpp" #include "../TypeTraits/IsBaseOf.hpp" #include "../TypeTraits/IsChar.hpp" +#include "../TypeTraits/IsConst.hpp" #include "../TypeTraits/RemoveReference.hpp" namespace ArduinoJson { namespace Internals { template -struct StringTraits {}; +struct StringTraits { + static const bool has_append = false; + static const bool has_equals = false; +}; template struct StringTraits : StringTraits {}; @@ -33,18 +34,3 @@ struct StringTraits : StringTraits {}; #include "FlashString.hpp" #include "StdStream.hpp" #include "StdString.hpp" - -namespace ArduinoJson { -namespace TypeTraits { -template -struct IsString { - static const bool value = false; -}; - -template -struct IsString::has_equals>::type> { - static const bool value = Internals::StringTraits::has_equals; -}; -} -} diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/TypeTraits/EnableIf.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/EnableIf.hpp similarity index 58% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/TypeTraits/EnableIf.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/EnableIf.hpp index 654b308e6..83fc5e07f 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/TypeTraits/EnableIf.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/EnableIf.hpp @@ -1,14 +1,11 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once namespace ArduinoJson { -namespace TypeTraits { +namespace Internals { // A meta-function that return the type T if Condition is true. template diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/TypeTraits/FloatTraits.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/FloatTraits.hpp similarity index 68% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/TypeTraits/FloatTraits.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/FloatTraits.hpp index a52e1e879..648cc82fd 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/TypeTraits/FloatTraits.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/FloatTraits.hpp @@ -1,9 +1,6 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once @@ -13,7 +10,7 @@ #include "../Polyfills/math.hpp" namespace ArduinoJson { -namespace TypeTraits { +namespace Internals { template struct FloatTraits {}; @@ -47,28 +44,46 @@ struct FloatTraits { static T positiveBinaryPowerOfTen(int index) { static T factors[] = { - 1e1, 1e2, 1e4, 1e8, 1e16, 1e32, - // workaround to support platforms with single precision literals - forge(0x4D384F03, 0xE93FF9F5), forge(0x5A827748, 0xF9301D32), - forge(0x75154FDD, 0x7F73BF3C)}; + 1e1, + 1e2, + 1e4, + 1e8, + 1e16, + forge(0x4693B8B5, 0xB5056E17), // 1e32 + forge(0x4D384F03, 0xE93FF9F5), // 1e64 + forge(0x5A827748, 0xF9301D32), // 1e128 + forge(0x75154FDD, 0x7F73BF3C) // 1e256 + }; return factors[index]; } static T negativeBinaryPowerOfTen(int index) { static T factors[] = { - 1e-1, 1e-2, 1e-4, 1e-8, 1e-16, 1e-32, - // workaround to support platforms with single precision literals - forge(0x32A50FFD, 0x44F4A73D), forge(0x255BBA08, 0xCF8C979D), - forge(0x0AC80628, 0x64AC6F43)}; + forge(0x3FB99999, 0x9999999A), // 1e-1 + forge(0x3F847AE1, 0x47AE147B), // 1e-2 + forge(0x3F1A36E2, 0xEB1C432D), // 1e-4 + forge(0x3E45798E, 0xE2308C3A), // 1e-8 + forge(0x3C9CD2B2, 0x97D889BC), // 1e-16 + forge(0x3949F623, 0xD5A8A733), // 1e-32 + forge(0x32A50FFD, 0x44F4A73D), // 1e-64 + forge(0x255BBA08, 0xCF8C979D), // 1e-128 + forge(0x0AC80628, 0x64AC6F43) // 1e-256 + }; return factors[index]; } static T negativeBinaryPowerOfTenPlusOne(int index) { static T factors[] = { - 1e0, 1e-1, 1e-3, 1e-7, 1e-15, 1e-31, - // workaround to support platforms with single precision literals - forge(0x32DA53FC, 0x9631D10D), forge(0x25915445, 0x81B7DEC2), - forge(0x0AFE07B2, 0x7DD78B14)}; + 1e0, + forge(0x3FB99999, 0x9999999A), // 1e-1 + forge(0x3F50624D, 0xD2F1A9FC), // 1e-3 + forge(0x3E7AD7F2, 0x9ABCAF48), // 1e-7 + forge(0x3CD203AF, 0x9EE75616), // 1e-15 + forge(0x398039D6, 0x65896880), // 1e-31 + forge(0x32DA53FC, 0x9631D10D), // 1e-63 + forge(0x25915445, 0x81B7DEC2), // 1e-127 + forge(0x0AFE07B2, 0x7DD78B14) // 1e-255 + }; return factors[index]; } @@ -80,6 +95,9 @@ struct FloatTraits { return forge(0x7ff00000, 0x00000000); } + // constructs a double floating point values from its binary representation + // we use this function to workaround platforms with single precision literals + // (for example, when -fsingle-precision-constant is passed to GCC) static T forge(uint32_t msb, uint32_t lsb) { union { uint64_t integerBits; diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/TypeTraits/IsArray.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/IsArray.hpp similarity index 67% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/TypeTraits/IsArray.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/IsArray.hpp index 713808aed..259923115 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/TypeTraits/IsArray.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/IsArray.hpp @@ -1,14 +1,11 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once namespace ArduinoJson { -namespace TypeTraits { +namespace Internals { // A meta-function that return the type T without the const modifier template diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/TypeTraits/IsBaseOf.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/IsBaseOf.hpp similarity index 73% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/TypeTraits/IsBaseOf.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/IsBaseOf.hpp index 9e68debc3..bf24e965e 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/TypeTraits/IsBaseOf.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/IsBaseOf.hpp @@ -1,14 +1,11 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once namespace ArduinoJson { -namespace TypeTraits { +namespace Internals { // A meta-function that returns true if Derived inherits from TBase is an // integral type. diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/TypeTraits/IsChar.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/IsChar.hpp similarity index 69% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/TypeTraits/IsChar.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/IsChar.hpp index bd1b5fdd5..d97cec213 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/TypeTraits/IsChar.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/IsChar.hpp @@ -1,16 +1,13 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once #include "IsSame.hpp" namespace ArduinoJson { -namespace TypeTraits { +namespace Internals { // A meta-function that returns true if T is a charater template diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/TypeTraits/IsConst.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/IsConst.hpp similarity index 61% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/TypeTraits/IsConst.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/IsConst.hpp index 7ffed8443..512ee5ca0 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/TypeTraits/IsConst.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/IsConst.hpp @@ -1,14 +1,11 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once namespace ArduinoJson { -namespace TypeTraits { +namespace Internals { // A meta-function that return the type T without the const modifier template diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/TypeTraits/IsFloatingPoint.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/IsFloatingPoint.hpp similarity index 60% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/TypeTraits/IsFloatingPoint.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/IsFloatingPoint.hpp index c04ce2a63..e41a6824c 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/TypeTraits/IsFloatingPoint.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/IsFloatingPoint.hpp @@ -1,16 +1,13 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once #include "IsSame.hpp" namespace ArduinoJson { -namespace TypeTraits { +namespace Internals { // A meta-function that returns true if T is a floating point type template diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/TypeTraits/IsIntegral.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/IsIntegral.hpp similarity index 50% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/TypeTraits/IsIntegral.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/IsIntegral.hpp index e34add52e..17ae5f284 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/TypeTraits/IsIntegral.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/IsIntegral.hpp @@ -1,9 +1,6 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once @@ -12,14 +9,14 @@ #include "IsUnsignedIntegral.hpp" namespace ArduinoJson { -namespace TypeTraits { +namespace Internals { // A meta-function that returns true if T is an integral type. template struct IsIntegral { - static const bool value = TypeTraits::IsSignedIntegral::value || - TypeTraits::IsUnsignedIntegral::value || - TypeTraits::IsSame::value; + static const bool value = IsSignedIntegral::value || + IsUnsignedIntegral::value || + IsSame::value; // CAUTION: differs from std::is_integral as it doesn't include bool }; diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/TypeTraits/IsSame.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/IsSame.hpp similarity index 62% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/TypeTraits/IsSame.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/IsSame.hpp index d96a5b554..06567c93b 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/TypeTraits/IsSame.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/IsSame.hpp @@ -1,14 +1,11 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once namespace ArduinoJson { -namespace TypeTraits { +namespace Internals { // A meta-function that returns true if types T and U are the same. template diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/IsSignedIntegral.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/IsSignedIntegral.hpp new file mode 100644 index 000000000..7334eb9c7 --- /dev/null +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/IsSignedIntegral.hpp @@ -0,0 +1,28 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include "../Configuration.hpp" +#include "IsSame.hpp" + +namespace ArduinoJson { +namespace Internals { + +// A meta-function that returns true if T is an integral type. +template +struct IsSignedIntegral { + static const bool value = + IsSame::value || IsSame::value || + IsSame::value || IsSame::value || +#if ARDUINOJSON_USE_LONG_LONG + IsSame::value || +#endif +#if ARDUINOJSON_USE_INT64 + IsSame::value || +#endif + false; +}; +} +} diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/IsUnsignedIntegral.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/IsUnsignedIntegral.hpp new file mode 100644 index 000000000..938423f5c --- /dev/null +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/IsUnsignedIntegral.hpp @@ -0,0 +1,28 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include "../Configuration.hpp" +#include "IsSame.hpp" + +namespace ArduinoJson { +namespace Internals { + +// A meta-function that returns true if T is an integral type. +template +struct IsUnsignedIntegral { + static const bool value = + IsSame::value || IsSame::value || + IsSame::value || IsSame::value || +#if ARDUINOJSON_USE_LONG_LONG + IsSame::value || +#endif +#if ARDUINOJSON_USE_INT64 + IsSame::value || +#endif + false; +}; +} +} diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/TypeTraits/IsVariant.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/IsVariant.hpp similarity index 50% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/TypeTraits/IsVariant.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/IsVariant.hpp index 8297cf5cd..f8b299f7a 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/TypeTraits/IsVariant.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/IsVariant.hpp @@ -1,16 +1,13 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once #include "IsBaseOf.hpp" namespace ArduinoJson { -namespace TypeTraits { +namespace Internals { class JsonVariantTag {}; diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/TypeTraits/RemoveConst.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/RemoveConst.hpp similarity index 59% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/TypeTraits/RemoveConst.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/RemoveConst.hpp index 0186234ef..39d4cb5a5 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/TypeTraits/RemoveConst.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/RemoveConst.hpp @@ -1,14 +1,11 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once namespace ArduinoJson { -namespace TypeTraits { +namespace Internals { // A meta-function that return the type T without the const modifier template diff --git a/lib/ArduinoJson-5.11.2/src/ArduinoJson/TypeTraits/RemoveReference.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/RemoveReference.hpp similarity index 60% rename from lib/ArduinoJson-5.11.2/src/ArduinoJson/TypeTraits/RemoveReference.hpp rename to lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/RemoveReference.hpp index 4968997e7..395a12889 100644 --- a/lib/ArduinoJson-5.11.2/src/ArduinoJson/TypeTraits/RemoveReference.hpp +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/TypeTraits/RemoveReference.hpp @@ -1,14 +1,11 @@ -// Copyright Benoit Blanchon 2014-2017 +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 // MIT License -// -// Arduino JSON library -// https://bblanchon.github.io/ArduinoJson/ -// If you like this project, please add a star! #pragma once namespace ArduinoJson { -namespace TypeTraits { +namespace Internals { // A meta-function that return the type T without the reference modifier. template diff --git a/lib/ArduinoJson-5.13.4/src/ArduinoJson/version.hpp b/lib/ArduinoJson-5.13.4/src/ArduinoJson/version.hpp new file mode 100644 index 000000000..34c78461d --- /dev/null +++ b/lib/ArduinoJson-5.13.4/src/ArduinoJson/version.hpp @@ -0,0 +1,10 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#define ARDUINOJSON_VERSION "5.13.4" +#define ARDUINOJSON_VERSION_MAJOR 5 +#define ARDUINOJSON_VERSION_MINOR 13 +#define ARDUINOJSON_VERSION_REVISION 4 diff --git a/lib/FrogmoreScd30/FrogmoreScd30.cpp b/lib/FrogmoreScd30/FrogmoreScd30.cpp new file mode 100644 index 000000000..32bbee5ba --- /dev/null +++ b/lib/FrogmoreScd30/FrogmoreScd30.cpp @@ -0,0 +1,653 @@ +/* +# Copyright (c) 2019 Frogmore42 +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ +#include +#include +#include +#include +#include + +#define COMMAND_SCD30_CONTINUOUS_MEASUREMENT 0x0010 +#define COMMAND_SCD30_MEASUREMENT_INTERVAL 0x4600 +#define COMMAND_SCD30_GET_DATA_READY 0x0202 +#define COMMAND_SCD30_READ_MEASUREMENT 0x0300 +#define COMMAND_SCD30_CALIBRATION_TYPE 0x5306 +#define COMMAND_SCD30_FORCED_RECALIBRATION_FACTOR 0x5204 +#define COMMAND_SCD30_TEMPERATURE_OFFSET 0x5403 +#define COMMAND_SCD30_ALTITUDE_COMPENSATION 0x5102 +#define COMMAND_SCD30_SOFT_RESET 0xD304 +#define COMMAND_SCD30_GET_FW_VERSION 0xD100 +#define COMMAND_SCD30_STOP_MEASUREMENT 0x0104 + +#define SCD30_DATA_REGISTER_BYTES 2 +#define SCD30_DATA_REGISTER_WITH_CRC 3 +#define SCD30_MEAS_BYTES 18 + +#ifdef SCD30_DEBUG +enum LoggingLevels {LOG_LEVEL_NONE, LOG_LEVEL_ERROR, LOG_LEVEL_INFO, LOG_LEVEL_DEBUG, LOG_LEVEL_DEBUG_MORE, LOG_LEVEL_ALL}; +char scd30log_data[180]; +#endif + +void FrogmoreScd30::begin(TwoWire *pWire, uint8_t i2cAddress) +{ + this->i2cAddress = i2cAddress; + if (pWire == NULL) + { + this->pWire = &Wire; + } + else + { + this->pWire = pWire; + } + + co2NewDataLocation = -1; // indicates there is no data, so the 1st data point needs to fill up the median filter + this->pWire->setClockStretchLimit(200000); + this->ambientPressure = 0; +} + +void FrogmoreScd30::begin(uint8_t i2cAddress) +{ + begin(NULL, i2cAddress); +} + +void FrogmoreScd30::begin(TwoWire *pWire) +{ + begin(pWire, SCD30_ADDRESS); +} + +void FrogmoreScd30::begin(void) +{ + begin(NULL, SCD30_ADDRESS); +} + +/*--------------------------------------------------------------------------- + Function : opt_med5() In : pointer to array of 5 values + Out : a uint16_t which is the middle value of the sorted array + Job : optimized search of the median of 5 values + Notice : found on sci.image.processing cannot go faster unless assumptions are made on the nature of the input signal. + ---------------------------------------------------------------------------*/ +#define PIX_SORT(a,b) { if ((a)>(b)) PIX_SWAP((a),(b)); } +#define PIX_SWAP(a,b) { uint16_t temp=(a);(a)=(b);(b)=temp; } + +uint16_t opt_med5(uint16_t * p) +{ + PIX_SORT(p[0], p[1]); + PIX_SORT(p[3], p[4]); + PIX_SORT(p[0], p[3]); + PIX_SORT(p[1], p[4]); + PIX_SORT(p[1], p[2]); + PIX_SORT(p[2], p[3]); + PIX_SORT(p[1], p[2]); + return(p[2]); +} + +// twi_status() attempts to read out any data left that is holding SDA low, so a new transaction can take place +// something like (http://www.forward.com.au/pfod/ArduinoProgramming/I2C_ClearBus/index.html) +int FrogmoreScd30::clearI2CBus(void) +{ +#ifdef SCD30_DEBUG + snprintf_P(scd30log_data, sizeof(scd30log_data), "clearI2CBus"); + AddLog(LOG_LEVEL_DEBUG_MORE); +#endif + return (twi_status()); +} + +#ifdef SCD30_DEBUG +void FrogmoreScd30::AddLog(uint8_t loglevel) +{ + if (loglevel <= LOG_LEVEL_INFO) + { + Serial.printf("%s\r\n", scd30log_data); + } +} +#endif + +uint8_t FrogmoreScd30::computeCRC8(uint8_t data[], uint8_t len) +// Computes the CRC that the SCD30 uses +{ + uint8_t crc = 0xFF; //Init with 0xFF + + for (uint8_t x = 0 ; x < len ; x++) + { + crc ^= data[x]; // XOR-in the next input byte + for (uint8_t i = 0 ; i < 8 ; i++) + { + if ((crc & 0x80) != 0) + crc = (uint8_t)((crc << 1) ^ 0x31); + else + crc <<= 1; + } + } + + return crc; //No output reflection +} + +// Sends stream of bytes to device +int FrogmoreScd30::sendBytes(void *pInput, uint8_t len) +{ + uint8_t *pBytes = (uint8_t *) pInput; + int result; + uint8_t errorBytes = 0; // number of bytes that had an error in transmission +#ifdef SCD30_DEBUG + snprintf_P(scd30log_data, sizeof(scd30log_data), "Scd30SendBytes: data: 0x %02X %02X %02X | 0x %02X %02X %02X | 0x %02X %02X %02X", pBytes[0], pBytes[1], pBytes[2], pBytes[3], pBytes[4], pBytes[5], pBytes[6], pBytes[7], pBytes[8]); + AddLog(LOG_LEVEL_DEBUG_MORE); +#endif + pWire->beginTransmission(this->i2cAddress); + errorBytes = len - (pWire->write(pBytes, len)); + result = pWire->endTransmission(); + if (errorBytes || result) + { +#ifdef SCD30_DEBUG + snprintf_P(scd30log_data, sizeof(scd30log_data), "Scd30SendBytes: errorBytes: %d | Wire.end: %d", errorBytes, result); + AddLog(LOG_LEVEL_INFO); +#endif + } + + result <<= 8; // leave room for error bytes number + result |= errorBytes; // low byte has number of bytes that were not written correctly + return (result); +} + +// Gets a number of bytes from device +int FrogmoreScd30::getBytes(void *pOutput, uint8_t len) +{ + uint8_t *pBytes = (uint8_t *) pOutput; + uint8_t result; + + result = pWire->requestFrom(this->i2cAddress, len); + if (len != result) + { +#ifdef SCD30_DEBUG + snprintf_P(scd30log_data, sizeof(scd30log_data), "Scd30GetBytes: wire request expected %d got: %d", len, result); + AddLog(LOG_LEVEL_INFO); +#endif + return (ERROR_SCD30_NOT_ENOUGH_BYTES_ERROR); + } + + if (pWire->available()) + { + for (int x = 0; x < len; x++) + { + pBytes[x] = pWire->read(); + } +#ifdef SCD30_DEBUG + snprintf_P(scd30log_data, sizeof(scd30log_data), "Scd30GetBytes: data: 0x %02X %02X %02X | 0x %02X %02X %02X | 0x %02X %02X %02X", pBytes[0], pBytes[1], pBytes[2], pBytes[3], pBytes[4], pBytes[5], pBytes[6], pBytes[7], pBytes[8]); + AddLog(LOG_LEVEL_DEBUG_MORE); +#endif + return (ERROR_SCD30_NO_ERROR); + } + + return (ERROR_SCD30_UNKNOWN_ERROR); +} + +//Sends just a command, no arguments, no CRC +int FrogmoreScd30::sendCommand(uint16_t command) +{ + uint8_t data[2]; + data[0] = command >> 8; + data[1] = command & 0xFF; + int error = sendBytes(data, sizeof(data)); + if (error) + { +#ifdef SCD30_DEBUG + snprintf_P(scd30log_data, sizeof(scd30log_data), "Scd30SendCommand: Scd30SendBytes failed: 0x%lX", error); + AddLog(LOG_LEVEL_INFO); +#endif + } + return (error); +} + +//Sends a command along with arguments and CRC +int FrogmoreScd30::sendCommandArguments(uint16_t command, uint16_t arguments) +{ + uint8_t data[5]; + data[0] = command >> 8; + data[1] = command & 0xFF; + data[2] = arguments >> 8; + data[3] = arguments & 0xFF; + data[4] = computeCRC8(&data[2], 2); //Calc CRC on the arguments only, not the command + int error = sendBytes(data, sizeof(data)); + if (error) + { +#ifdef SCD30_DEBUG + snprintf_P(scd30log_data, sizeof(scd30log_data), "Scd30SendCommandArguments: Scd30SendBytes failed: 0x%lX", error); + AddLog(LOG_LEVEL_INFO); +#endif + } + return (error); +} + +int FrogmoreScd30::get16BitRegCheckCRC(void* pInput, uint16_t *pData) +{ + uint8_t *pBytes = (uint8_t *) pInput; + uint8_t expectedCRC = computeCRC8(pBytes, SCD30_DATA_REGISTER_BYTES); + if (expectedCRC != pBytes[SCD30_DATA_REGISTER_BYTES]) + { +#ifdef SCD30_DEBUG + snprintf_P(scd30log_data, sizeof(scd30log_data), "Scd30get16BitRegCheckCRC: expected: 0x%02X, but got: 0x%02X", expectedCRC, pBytes[SCD30_DATA_REGISTER_BYTES]); + AddLog(LOG_LEVEL_INFO); + snprintf_P(scd30log_data, sizeof(scd30log_data), "Scd30get16BitRegCheckCRC: data: 0x%02X, 0x%02X, 0x%02X", pBytes[0], pBytes[1], pBytes[2]); + AddLog(LOG_LEVEL_INFO); +#endif + return (ERROR_SCD30_CRC_ERROR); + } + *pData = (uint16_t) pBytes[0] << 8 | pBytes[1]; // data from SCD30 is Big-Endian + return (ERROR_SCD30_NO_ERROR); +} + +// gets 32 bits, (2) 16-bit chunks, and validates the CRCs +// +int FrogmoreScd30::get32BitRegCheckCRC(void *pInput, float *pData) +{ + uint16_t tempU16High; + uint16_t tempU16Low; + uint8_t *pBytes = (uint8_t *) pInput; + uint32_t rawInt = 0; + + int error = get16BitRegCheckCRC(pBytes, &tempU16High); + if (error) { + return (error); + } + + error = get16BitRegCheckCRC(pBytes + SCD30_DATA_REGISTER_WITH_CRC, &tempU16Low); + if (error) { + return (error); + } + + // data from SCD is Big-Endian + rawInt |= tempU16High; + rawInt <<= 16; + rawInt |= tempU16Low; + + *pData = * (float *) &rawInt; +#ifdef SCD30_DEBUG + snprintf_P(scd30log_data, sizeof(scd30log_data), "get32BitRegCheckCRC: got: tempUs 0x%lX, %lX", tempU16High, tempU16Low); + AddLog(LOG_LEVEL_DEBUG); +#endif + + if (isnan(*pData) || isinf(*pData)) + { +#ifdef SCD30_DEBUG + snprintf_P(scd30log_data, sizeof(scd30log_data), "get32BitRegCheckCRC: not a floating point number: rawInt 0x%lX", rawInt); + AddLog(LOG_LEVEL_INFO); +#endif + return (ERROR_SCD30_NOT_A_NUMBER_ERROR); + } + + return (ERROR_SCD30_NO_ERROR); +} + +//Gets two bytes (and check CRC) from SCD30 +int FrogmoreScd30::readRegister(uint16_t registerAddress, uint16_t* pData) +{ + int error = sendCommand(registerAddress); + if (error) + { +#ifdef SCD30_DEBUG + snprintf_P(scd30log_data, sizeof(scd30log_data), "Scd30ReadRegister: SendCommand error: 0x%lX", error); + AddLog(LOG_LEVEL_INFO); +#endif + return (error); + } + delay(1); // the SCD30 uses clock streching to give it time to prepare data, waiting here makes it work + uint8_t data[SCD30_DATA_REGISTER_WITH_CRC]; + error = getBytes(data, sizeof(data)); + if (error) + { +#ifdef SCD30_DEBUG + snprintf_P(scd30log_data, sizeof(scd30log_data), "Scd30ReadRegister: Scd30GetBytes error: 0x%lX", error); + AddLog(LOG_LEVEL_INFO); +#endif + return (error); + } + uint16 regValue; + error = get16BitRegCheckCRC(data, ®Value); + if (error) + { +#ifdef SCD30_DEBUG + snprintf_P(scd30log_data, sizeof(scd30log_data), "Scd30ReadRegister: Scd30get16BitRegCheckCRC error: 0x%lX", error); + AddLog(LOG_LEVEL_INFO); +#endif + return (error); + } + + *pData = regValue; + return (ERROR_SCD30_NO_ERROR); +} + +int FrogmoreScd30::softReset(void) +{ + return (sendCommand(COMMAND_SCD30_SOFT_RESET)); +} + +int FrogmoreScd30::getAltitudeCompensation(uint16_t *pHeight_meter) +{ + return (readRegister(COMMAND_SCD30_ALTITUDE_COMPENSATION, pHeight_meter)); +} + +int FrogmoreScd30::getAmbientPressure(uint16_t *pAirPressure_mbar) +{ + *pAirPressure_mbar = ambientPressure; + return (ERROR_SCD30_NO_ERROR); +} + +int FrogmoreScd30::getCalibrationType(uint16_t *pIsAuto) +{ + uint16_t value = 0; + int error = readRegister(COMMAND_SCD30_CALIBRATION_TYPE, &value); + if (!error) + { + *pIsAuto = value != 0; + } + return (error); +} + +int FrogmoreScd30::getFirmwareVersion(uint8_t *pMajor, uint8_t *pMinor) +{ + uint16_t value; + int error = readRegister(COMMAND_SCD30_GET_FW_VERSION, &value); + if (!error) + { + *pMajor = value >> 8; + *pMinor = value & 0xFF; + } + return (error); +} + +int FrogmoreScd30::getForcedRecalibrationFactor(uint16_t *pCo2_ppm) +{ + return (readRegister(COMMAND_SCD30_FORCED_RECALIBRATION_FACTOR, pCo2_ppm)); +} + +int FrogmoreScd30::getMeasurementInterval(uint16_t *pTime_sec) +{ + return (readRegister(COMMAND_SCD30_MEASUREMENT_INTERVAL, pTime_sec)); +} + +int FrogmoreScd30::getTemperatureOffset(float *pOffset_degC) +{ + uint16_t value; + int error = readRegister(COMMAND_SCD30_TEMPERATURE_OFFSET, &value); + if (!error) + { + // result is in centi-degrees, need to convert to degrees + *pOffset_degC = (float) value / 100.0; + } + return (error); +} + +int FrogmoreScd30::getTemperatureOffset(uint16_t *pOffset_centiDegC) +{ + uint16_t value; + int error = readRegister(COMMAND_SCD30_TEMPERATURE_OFFSET, &value); + if (!error) + { + // result is in centi-degrees, need to convert to degrees + *pOffset_centiDegC = value; + } + return (error); +} + +int FrogmoreScd30::setAltitudeCompensation(uint16_t height_meter) +{ + return (sendCommandArguments(COMMAND_SCD30_ALTITUDE_COMPENSATION, height_meter)); +} + +int FrogmoreScd30::setAmbientPressure(uint16_t airPressure_mbar) +{ + ambientPressure = airPressure_mbar; + return (beginMeasuring(ambientPressure)); +} + +int FrogmoreScd30::setAutoSelfCalibration(void) +{ + bool isAuto = true; + return (setCalibrationType(isAuto)); +} + +int FrogmoreScd30::setCalibrationType(bool isAuto) +{ + bool value = !!isAuto; // using NOT operator twice makes sure value is 0 or 1 + return (sendCommandArguments(COMMAND_SCD30_CALIBRATION_TYPE, value)); +} + +int FrogmoreScd30::setForcedRecalibrationFactor(uint16_t co2_ppm) +{ + return (sendCommandArguments(COMMAND_SCD30_FORCED_RECALIBRATION_FACTOR, co2_ppm)); +} + +int FrogmoreScd30::setManualCalibration(void) +{ + bool isAuto = false; + return (setCalibrationType(isAuto)); +} + +int FrogmoreScd30::setMeasurementInterval(uint16_t time_sec) +{ + if (time_sec < 2) time_sec = 2; + if (time_sec > 1800) time_sec = 1800; + return (sendCommandArguments(COMMAND_SCD30_MEASUREMENT_INTERVAL, time_sec)); +} + +int FrogmoreScd30::setTemperatureOffset(float offset_degC) +{ + uint16_t offset_centiDegC; + if (offset_degC >= 0) + { + offset_centiDegC = (uint16_t) offset_degC * 100; + return (sendCommandArguments(COMMAND_SCD30_TEMPERATURE_OFFSET, offset_centiDegC)); + } + else + { + return (ERROR_SCD30_INVALID_VALUE); + } + +} + +int FrogmoreScd30::setTemperatureOffset(uint16_t offset_centiDegC) +{ + return (sendCommandArguments(COMMAND_SCD30_TEMPERATURE_OFFSET, offset_centiDegC)); +} + +int FrogmoreScd30::beginMeasuring(void) +{ + return (beginMeasuring(ambientPressure)); +} + +int FrogmoreScd30::beginMeasuring(uint16_t airPressure_mbar) +{ + ambientPressure = airPressure_mbar; + return(sendCommandArguments(COMMAND_SCD30_CONTINUOUS_MEASUREMENT, ambientPressure)); +} + +int FrogmoreScd30::isDataAvailable(bool *pIsAvailable) +{ + uint16_t isDataAvailable = false; + int error = readRegister(COMMAND_SCD30_GET_DATA_READY, &isDataAvailable); + if (!error) + { + *pIsAvailable = isDataAvailable != 0; + } + return (error); +} + +int FrogmoreScd30::readMeasurement( + uint16 *pCO2_ppm, + uint16 *pCO2EAvg_ppm, + float *pTemperature, + float *pHumidity +) +{ + bool isAvailable = false; + int error = 0; + float tempCO2; + float tempHumidity; + float tempTemperature; + + error = isDataAvailable(&isAvailable); + if (error) + { + return (error); + } + + if (!isAvailable) + { + return (ERROR_SCD30_NO_DATA); + } + +#ifdef SCD30_DEBUG + snprintf_P(scd30log_data, sizeof(scd30log_data), "Scd30ReadMeasurement: have data"); + AddLog(LOG_LEVEL_DEBUG_MORE); +#endif + + error = sendCommand(COMMAND_SCD30_READ_MEASUREMENT); + if (error) + { +#ifdef SCD30_DEBUG + snprintf_P(scd30log_data, sizeof(scd30log_data), "Scd30ReadMeasurement: send command failed: 0x%lX", error); + AddLog(LOG_LEVEL_INFO); +#endif + return (error); + } + delay(1); // the SCD30 uses clock streching to give it time to prepare data, waiting here makes it work + + uint8_t bytes[SCD30_MEAS_BYTES]; + // there are (6) 16-bit values, each with a CRC in the measurement data + // the chip does not seem to like sending this data, except all at once + error = getBytes(bytes, SCD30_MEAS_BYTES); + if (error) + { +#ifdef SCD30_DEBUG + snprintf_P(scd30log_data, sizeof(scd30log_data), "Scd30ReadMeasurement: Scd30GetBytes command failed: 0x%lX", error); + AddLog(LOG_LEVEL_INFO); +#endif + return (error); + } + +#ifdef SCD30_DEBUG + snprintf_P(scd30log_data, sizeof(scd30log_data), "Scd30ReadMeasurement: Scd30GetBytes data: 0x %02X %02X %02X | 0x %02X %02X %02X | 0x %02X %02X %02X", bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], bytes[8]); + AddLog(LOG_LEVEL_DEBUG_MORE); + snprintf_P(scd30log_data, sizeof(scd30log_data), "Scd30ReadMeasurement: Scd30GetBytes data: 0x %02X %02X %02X | 0x %02X %02X %02X | 0x %02X %02X %02X", bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15], bytes[16], bytes[17]); + AddLog(LOG_LEVEL_DEBUG_MORE); +#endif + + error = get32BitRegCheckCRC(&bytes[0], &tempCO2); + if (error) + { +#ifdef SCD30_DEBUG + snprintf_P(scd30log_data, sizeof(scd30log_data), "Scd30ReadMeasurement: Scd30Get32BitsCheckCRC 1st command failed: 0x%lX", error); + AddLog(LOG_LEVEL_INFO); +#endif + return (error); + } + + error = get32BitRegCheckCRC(&bytes[6], &tempTemperature); + if (error) + { +#ifdef SCD30_DEBUG + snprintf_P(scd30log_data, sizeof(scd30log_data), "Scd30ReadMeasurement: Scd30Get32BitsCheckCRC 2nd command failed: 0x%lX", error); + AddLog(LOG_LEVEL_INFO); +#endif + return (error); + } + + error = get32BitRegCheckCRC(&bytes[12], &tempHumidity); + if (error) + { +#ifdef SCD30_DEBUG + snprintf_P(scd30log_data, sizeof(scd30log_data), "Scd30ReadMeasurement: Scd30Get32BitsCheckCRC 3rd command failed: 0x%lX", error); + AddLog(LOG_LEVEL_INFO); +#endif + return (error); + } + + if (tempCO2 == 0) + { + return (ERROR_SCD30_CO2_ZERO); + } + + if (co2NewDataLocation < 0) + { + co2EAverage = tempCO2; + for (int x = 0; x < SCD30_MEDIAN_FILTER_SIZE; x++) + { + co2History[x] = tempCO2; + co2NewDataLocation = 1; + } + } + else + { + co2History[co2NewDataLocation++] = tempCO2; + if (co2NewDataLocation >= SCD30_MEDIAN_FILTER_SIZE) + { + co2NewDataLocation = 0; + } + } + +#ifdef SCD30_DEBUG + snprintf_P(scd30log_data, sizeof(scd30log_data), "Scd30ReadMeasurement: co2History: %ld, %ld, %ld, %ld, %ld", co2History[0], co2History[1], co2History[2], co2History[3], co2History[4]); + AddLog(LOG_LEVEL_DEBUG_MORE); +#endif + // copy array since the median filter function will re-arrange it + uint16_t temp[SCD30_MEDIAN_FILTER_SIZE]; + for (int x = 0; x < SCD30_MEDIAN_FILTER_SIZE; x++) + { + temp[x] = co2History[x]; + } +#ifdef SCD30_DEBUG + snprintf_P(scd30log_data, sizeof(scd30log_data), "Scd30ReadMeasurement: temp: %ld, %ld, %ld, %ld, %ld", temp[0], temp[1], temp[2], temp[3], temp[4]); + AddLog(LOG_LEVEL_DEBUG_MORE); +#endif + + *pCO2_ppm = opt_med5(temp); +#ifdef SCD30_DEBUG + snprintf_P(scd30log_data, sizeof(scd30log_data), "Scd30ReadMeasurement: CO2_ppm: %ld", *pCO2_ppm); + AddLog(LOG_LEVEL_DEBUG_MORE); +#endif + if (pCO2EAvg_ppm) + { + int16_t delta = (int16_t) *pCO2_ppm - (int16_t) co2EAverage; + int16_t change = delta / 32; + co2EAverage += change; +#if 0 + uint16_t remain = co2EAverage % 5; + uint16_t dividend = co2EAverage / 5; + uint16_t co2EAReported = dividend * 5; + if (remain > 2) + { + co2EAReported += 5; + } + *pCO2EAvg_ppm = co2EAReported; +#else + *pCO2EAvg_ppm = co2EAverage; +#endif + + } + + *pTemperature = tempTemperature; + *pHumidity = tempHumidity; + return (ERROR_SCD30_NO_ERROR); +} + +int FrogmoreScd30::stopMeasuring(void) +{ + return (sendCommand(COMMAND_SCD30_STOP_MEASUREMENT)); +} + diff --git a/lib/FrogmoreScd30/FrogmoreScd30.h b/lib/FrogmoreScd30/FrogmoreScd30.h new file mode 100644 index 000000000..d1f2d1309 --- /dev/null +++ b/lib/FrogmoreScd30/FrogmoreScd30.h @@ -0,0 +1,105 @@ +/* +# Copyright (c) 2019 Frogmore42 +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ +#pragma once + +#include "Arduino.h" + +//#define SCD30_DEBUG + +#define SCD30_ADDRESS 0x61 +#define ERROR_SCD30_NO_ERROR 0 +#define ERROR_SCD30_NO_DATA 0x80000000 +#define ERROR_SCD30_CO2_ZERO 0x90000000 +#define ERROR_SCD30_UNKNOWN_ERROR 0x1000000 +#define ERROR_SCD30_CRC_ERROR 0x2000000 +#define ERROR_SCD30_NOT_ENOUGH_BYTES_ERROR 0x3000000 +#define ERROR_SCD30_NOT_FOUND_ERROR 0x4000000 +#define ERROR_SCD30_NOT_A_NUMBER_ERROR 0x5000000 +#define ERROR_SCD30_INVALID_VALUE 0x6000000 + +#define SCD30_MEDIAN_FILTER_SIZE 5 + +class FrogmoreScd30 +{ + public: + FrogmoreScd30() {}; + // Constructors + // the SCD30 only lists a single i2c address, so not necesary to specify + // + void begin(void); + void begin(uint8_t _i2cAddress); + void begin(TwoWire *pWire); + void begin(TwoWire *pWire, uint8_t _i2cAddress); + + int softReset(void); + int clearI2CBus(void); // this is a HARD reset of the IC2 bus to restore communication, it will disrupt the bus + + int getAltitudeCompensation(uint16_t *pHeight_meter); + int getAmbientPressure(uint16_t *pAirPressure_mbar); + int getCalibrationType(uint16_t *pIsAuto); + int getFirmwareVersion(uint8_t *pMajor, uint8_t *pMinor); + int getForcedRecalibrationFactor(uint16_t *pCo2_ppm); + int getMeasurementInterval(uint16_t *pTime_sec); + int getTemperatureOffset(float *pOffset_degC); + int getTemperatureOffset(uint16_t *pOffset_centiDegC); + + int setAltitudeCompensation(uint16_t height_meter); + int setAmbientPressure(uint16_t airPressure_mbar); + int setAutoSelfCalibration(void); + int setCalibrationType(bool isAuto); + int setForcedRecalibrationFactor(uint16_t co2_ppm); + int setManualCalibration(void); + int setMeasurementInterval(uint16_t time_sec); + int setTemperatureOffset(float offset_degC); + int setTemperatureOffset(uint16_t offset_centiDegC); + + int beginMeasuring(void); + int beginMeasuring(uint16_t airPressure_mbar); // also sets ambient pressure offset in mbar/hPascal + int isDataAvailable(bool *pIsAvailable); + int readMeasurement( + uint16 *pCO2_ppm, + uint16 *pCO2EAvg_ppm, + float *pTemperature, + float *pHumidity + ); + int stopMeasuring(void); + + private: + uint8_t i2cAddress; + TwoWire *pWire; + uint16_t ambientPressure; + uint16_t co2AvgExtra; + uint16_t co2History[SCD30_MEDIAN_FILTER_SIZE]; + uint16_t co2EAverage; + int8_t co2NewDataLocation; // location to put new CO2 data for median filter + + uint8_t computeCRC8(uint8_t data[], uint8_t len); + int sendBytes(void *pInput, uint8_t len); + int getBytes(void *pOutput, uint8_t len); + int sendCommand(uint16_t command); + int sendCommandArguments(uint16_t command, uint16_t arguments); + int get16BitRegCheckCRC(void* pInput, uint16_t* pData); + int get32BitRegCheckCRC(void* pInput, float* pData); + int readRegister(uint16_t registerAddress, uint16_t* pData); +#ifdef SCD30_DEBUG + void AddLog(uint8_t loglevel); +#endif +}; \ No newline at end of file diff --git a/lib/LinkedList-1.2.3/LICENSE.txt b/lib/LinkedList-1.2.3/LICENSE.txt new file mode 100644 index 000000000..5c02604a0 --- /dev/null +++ b/lib/LinkedList-1.2.3/LICENSE.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Ivan Seidel + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/lib/LinkedList-1.2.3/LinkedList.h b/lib/LinkedList-1.2.3/LinkedList.h new file mode 100644 index 000000000..371b14ac7 --- /dev/null +++ b/lib/LinkedList-1.2.3/LinkedList.h @@ -0,0 +1,325 @@ +/* + LinkedList.h - V1.1 - Generic LinkedList implementation + Works better with FIFO, because LIFO will need to + search the entire List to find the last one; + + For instructions, go to https://github.com/ivanseidel/LinkedList + + Created by Ivan Seidel Gomes, March, 2013. + Released into the public domain. +*/ + + +#ifndef LinkedList_h +#define LinkedList_h + +#include + +template +struct ListNode +{ + T data; + ListNode *next; +}; + +template +class LinkedList{ + +protected: + int _size; + ListNode *root; + ListNode *last; + + // Helps "get" method, by saving last position + ListNode *lastNodeGot; + int lastIndexGot; + // isCached should be set to FALSE + // everytime the list suffer changes + bool isCached; + + ListNode* getNode(int index); + +public: + LinkedList(); + ~LinkedList(); + + /* + Returns current size of LinkedList + */ + virtual int size(); + /* + Adds a T object in the specified index; + Unlink and link the LinkedList correcly; + Increment _size + */ + virtual bool add(int index, T); + /* + Adds a T object in the end of the LinkedList; + Increment _size; + */ + virtual bool add(T); + /* + Adds a T object in the start of the LinkedList; + Increment _size; + */ + virtual bool unshift(T); + /* + Set the object at index, with T; + Increment _size; + */ + virtual bool set(int index, T); + /* + Remove object at index; + If index is not reachable, returns false; + else, decrement _size + */ + virtual T remove(int index); + /* + Remove last object; + */ + virtual T pop(); + /* + Remove first object; + */ + virtual T shift(); + /* + Get the index'th element on the list; + Return Element if accessible, + else, return false; + */ + virtual T get(int index); + + /* + Clear the entire array + */ + virtual void clear(); + +}; + +// Initialize LinkedList with false values +template +LinkedList::LinkedList() +{ + root=NULL; + last=NULL; + _size=0; + + lastNodeGot = root; + lastIndexGot = 0; + isCached = false; +} + +// Clear Nodes and free Memory +template +LinkedList::~LinkedList() +{ + ListNode* tmp; + while(root!=NULL) + { + tmp=root; + root=root->next; + delete tmp; + } + last = NULL; + _size=0; + isCached = false; +} + +/* + Actualy "logic" coding +*/ + +template +ListNode* LinkedList::getNode(int index){ + + int _pos = 0; + ListNode* current = root; + + // Check if the node trying to get is + // immediatly AFTER the previous got one + if(isCached && lastIndexGot <= index){ + _pos = lastIndexGot; + current = lastNodeGot; + } + + while(_pos < index && current){ + current = current->next; + + _pos++; + } + + // Check if the object index got is the same as the required + if(_pos == index){ + isCached = true; + lastIndexGot = index; + lastNodeGot = current; + + return current; + } + + return false; +} + +template +int LinkedList::size(){ + return _size; +} + +template +bool LinkedList::add(int index, T _t){ + + if(index >= _size) + return add(_t); + + if(index == 0) + return unshift(_t); + + ListNode *tmp = new ListNode(), + *_prev = getNode(index-1); + tmp->data = _t; + tmp->next = _prev->next; + _prev->next = tmp; + + _size++; + isCached = false; + + return true; +} + +template +bool LinkedList::add(T _t){ + + ListNode *tmp = new ListNode(); + tmp->data = _t; + tmp->next = NULL; + + if(root){ + // Already have elements inserted + last->next = tmp; + last = tmp; + }else{ + // First element being inserted + root = tmp; + last = tmp; + } + + _size++; + isCached = false; + + return true; +} + +template +bool LinkedList::unshift(T _t){ + + if(_size == 0) + return add(_t); + + ListNode *tmp = new ListNode(); + tmp->next = root; + tmp->data = _t; + root = tmp; + + _size++; + isCached = false; + + return true; +} + +template +bool LinkedList::set(int index, T _t){ + // Check if index position is in bounds + if(index < 0 || index >= _size) + return false; + + getNode(index)->data = _t; + return true; +} + +template +T LinkedList::pop(){ + if(_size <= 0) + return T(); + + isCached = false; + + if(_size >= 2){ + ListNode *tmp = getNode(_size - 2); + T ret = tmp->next->data; + delete(tmp->next); + tmp->next = NULL; + last = tmp; + _size--; + return ret; + }else{ + // Only one element left on the list + T ret = root->data; + delete(root); + root = NULL; + last = NULL; + _size = 0; + return ret; + } +} + +template +T LinkedList::shift(){ + if(_size <= 0) + return T(); + + if(_size > 1){ + ListNode *_next = root->next; + T ret = root->data; + delete(root); + root = _next; + _size --; + isCached = false; + + return ret; + }else{ + // Only one left, then pop() + return pop(); + } + +} + +template +T LinkedList::remove(int index){ + if (index < 0 || index >= _size) + { + return T(); + } + + if(index == 0) + return shift(); + + if (index == _size-1) + { + return pop(); + } + + ListNode *tmp = getNode(index - 1); + ListNode *toDelete = tmp->next; + T ret = toDelete->data; + tmp->next = tmp->next->next; + delete(toDelete); + _size--; + isCached = false; + return ret; +} + + +template +T LinkedList::get(int index){ + ListNode *tmp = getNode(index); + + return (tmp ? tmp->data : T()); +} + +template +void LinkedList::clear(){ + while(size() > 0) + shift(); +} + +#endif diff --git a/lib/LinkedList-1.2.3/README.md b/lib/LinkedList-1.2.3/README.md new file mode 100644 index 000000000..bdb16fdbd --- /dev/null +++ b/lib/LinkedList-1.2.3/README.md @@ -0,0 +1,171 @@ +# LinkedList + +This library was developed targeting **`Arduino`** applications. However, works just great with any C++. + +Implementing a buffer for objects takes time. If we are not in the mood, we just create an `array[1000]` with enough size. + +The objective of this library is to create a pattern for projects. +If you need to use a List of: `int`, `float`, `objects`, `Lists` or `Wales`. **This is what you are looking for.** + +With a simple but powerful caching algorithm, you can get subsequent objects much faster than usual. Tested without any problems with Lists bigger than 2000 members. + +## Installation + +1. [Download](https://github.com/ivanseidel/LinkedList/archive/master.zip) the Latest release from gitHub. +2. Unzip and modify the Folder name to "LinkedList" (Remove the '-version') +3. Paste the modified folder on your Library folder (On your `Libraries` folder inside Sketchbooks or Arduino software). +4. Reopen the Arduino software. + +**If you are here, because another Library requires this class, just don't waste time reading bellow. Install and ready.** + +------------------------- + +## Getting started + +### The `LinkedList` class + +In case you don't know what a LinkedList is and what it's used for, take a quick look at [Wikipedia::LinkedList](https://en.wikipedia.org/wiki/Linked_list) before continuing. + +#### To declare a LinkedList object +```c++ +// Instantiate a LinkedList that will hold 'integer' +LinkedList myLinkedList = LinkedList(); + +// Or just this +LinkedList myLinkedList; + +// But if you are instantiating a pointer LinkedList... +LinkedList *myLinkedList = new LinkedList(); + +// If you want a LinkedList with any other type such as 'MyClass' +// Make sure you call delete(MyClass) when you remove! +LinkedList *myLinkedList = new LinkedList(); +``` + +#### Getting the size of the linked list +```c++ +// To get the size of a linked list, make use of the size() method +int theSize = myList.size(); + +// Notice that if it's pointer to the linked list, you should use -> instead +int theSize = myList->size(); +``` + +#### Adding elements + +```c++ +// add(obj) method will insert at the END of the list +myList.add(myObject); + +// add(index, obj) method will try to insert the object at the specified index +myList.add(0, myObject); // Add at the beginning +myList.add(3, myObject); // Add at index 3 + +// unshift(obj) method will insert the object at the beginning +myList.unshift(myObject); +``` + +#### Getting elements + +```c++ +// get(index) will return the element at index +// (notice that the start element is 0, not 1) + +// Get the FIRST element +myObject = myList.get(0); + +// Get the third element +myObject = myList.get(2); + +// Get the LAST element +myObject = myList.get(myList.size() - 1); +``` + +#### Changing elements +```c++ +// set(index, obj) method will change the object at index to obj + +// Change the first element to myObject +myList.set(0, myObject); + +// Change the third element to myObject +myList.set(2, myObject); + +// Change the LAST element of the list +myList.set(myList.size() - 1, myObject); +``` + +#### Removing elements +```c++ +// remove(index) will remove and return the element at index + +// Remove the first object +myList.remove(0); + +// Get and Delete the third element +myDeletedObject = myList.remove(2); + +// pop() will remove and return the LAST element +myDeletedObject = myList.pop(); + +// shift() will remove and return the FIRST element +myDeletedObject = myList.shift(); + +// clear() will erase the entire list, leaving it with 0 elements +// NOTE: Clear wont DELETE/FREE memory from Pointers, if you +// are using Classes/Poiners, manualy delete and free those. +myList.clear(); +``` + +------------------------ + +## Library Reference + +### `ListNode` struct + +- `T` `ListNode::data` - The object data + +- `ListNode` `*next` - Pointer to the next Node + +### `LinkedList` class + +**`boolean` methods returns if succeeded** + +- `LinkedList::LinkedList()` - Constructor. + +- `LinkedList::~LinkedList()` - Destructor. Clear Nodes to minimize memory. Does not free pointer memory. + +- `int` `LinkedList::size()` - Returns the current size of the list. + +- `bool` `LinkedList::add(T)` - Add element T at the END of the list. + +- `bool` `LinkedList::add(int index, T)` - Add element T at `index` of the list. + +- `bool` `LinkedList::unshift(T)` - Add element T at the BEGINNING of the list. + +- `bool` `LinkedList::set(int index, T)` - Set the element at `index` to T. + +- `T` `LinkedList::remove(int index)` - Remove element at `index`. Return the removed element. Does not free pointer memory + +- `T` `LinkedList::pop()` - Remove the LAST element. Return the removed element. + +- `T` `LinkedList::shift()` - Remove the FIRST element. Return the removed element. + +- `T` `LinkedList::get(int index)` - Return the element at `index`. + +- `void` `LinkedList::clear()` - Removes all elements. Does not free pointer memory. + +- **protected** `int` `LinkedList::_size` - Holds the cached size of the list. + +- **protected** `ListNode` `LinkedList::*root` - Holds the root node of the list. + +- **protected** `ListNode` `LinkedList::*last` - Holds the last node of the list. + +- **protected** `ListNode*` `LinkedList::getNode(int index)` - Returns the `index` node of the list. + +### Version History + +* `1.1 (2013-07-20)`: Cache implemented. Getting subsequent objects is now O(N). Before, O(N^2). +* `1.0 (2013-07-20)`: Original release + +![LinkedList](https://d2weczhvl823v0.cloudfront.net/ivanseidel/LinkedList/trend.png) diff --git a/lib/LinkedList-1.2.3/examples/ClassList/ClassList.pde b/lib/LinkedList-1.2.3/examples/ClassList/ClassList.pde new file mode 100644 index 000000000..9a8ea9d99 --- /dev/null +++ b/lib/LinkedList-1.2.3/examples/ClassList/ClassList.pde @@ -0,0 +1,81 @@ +/* + LinkedList Example + Link: http://github.com/ivanseidel/LinkedList + + Example Created by + Tom Stewart, github.com/tastewar + + Edited by: + Ivan Seidel, github.com/ivanseidel +*/ + +#include + +// Let's define a new class +class Animal { + public: + char *name; + bool isMammal; +}; + +char catname[]="kitty"; +char dogname[]="doggie"; +char emuname[]="emu"; + +LinkedList myAnimalList = LinkedList(); + +void setup() +{ + + Serial.begin(9600); + Serial.println("Hello!" ); + + // Create a Cat + Animal *cat = new Animal(); + cat->name = catname; + cat->isMammal = true; + + // Create a dog + Animal *dog = new Animal(); + dog->name = dogname; + dog->isMammal = true; + + // Create a emu + Animal *emu = new Animal(); + emu->name = emuname; + emu->isMammal = false; // just an example; no offense to pig lovers + + // Add animals to list + myAnimalList.add(cat); + myAnimalList.add(emu); + myAnimalList.add(dog); +} + +void loop() { + + Serial.print("There are "); + Serial.print(myAnimalList.size()); + Serial.print(" animals in the list. The mammals are: "); + + int current = 0; + Animal *animal; + for(int i = 0; i < myAnimalList.size(); i++){ + + // Get animal from list + animal = myAnimalList.get(i); + + // If its a mammal, then print it's name + if(animal->isMammal){ + + // Avoid printing spacer on the first element + if(current++) + Serial.print(", "); + + // Print animal name + Serial.print(animal->name); + } + } + Serial.println("."); + + while (true); // nothing else to do, loop forever +} \ No newline at end of file diff --git a/lib/LinkedList-1.2.3/examples/SimpleIntegerList/SimpleIntegerList.pde b/lib/LinkedList-1.2.3/examples/SimpleIntegerList/SimpleIntegerList.pde new file mode 100644 index 000000000..1bcbe9c37 --- /dev/null +++ b/lib/LinkedList-1.2.3/examples/SimpleIntegerList/SimpleIntegerList.pde @@ -0,0 +1,58 @@ +/* + LinkedList Example + Link: http://github.com/ivanseidel/LinkedList + + Example Created by + Tom Stewart, github.com/tastewar + + Edited by: + Ivan Seidel, github.com/ivanseidel +*/ +#include + +LinkedList myList = LinkedList(); + +void setup() +{ + + Serial.begin(9600); + Serial.println("Hello!"); + + // Add some stuff to the list + int k = -240, + l = 123, + m = -2, + n = 222; + myList.add(n); + myList.add(0); + myList.add(l); + myList.add(17); + myList.add(k); + myList.add(m); +} + +void loop() { + + int listSize = myList.size(); + + Serial.print("There are "); + Serial.print(listSize); + Serial.print(" integers in the list. The negative ones are: "); + + // Print Negative numbers + for (int h = 0; h < listSize; h++) { + + // Get value from list + int val = myList.get(h); + + // If the value is negative, print it + if (val < 0) { + Serial.print(" "); + Serial.print(val); + } + } + + while (true); // nothing else to do, loop forever +} + + diff --git a/lib/LinkedList-1.2.3/keywords.txt b/lib/LinkedList-1.2.3/keywords.txt new file mode 100644 index 000000000..3ae496859 --- /dev/null +++ b/lib/LinkedList-1.2.3/keywords.txt @@ -0,0 +1,28 @@ +####################################### +# Syntax Coloring +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +LinkedList KEYWORD1 +ListNode KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +size KEYWORD2 +add KEYWORD2 +unshift KEYWORD2 +set KEYWORD2 +remove KEYWORD2 +pop KEYWORD2 +shift KEYWORD2 +get KEYWORD2 +clear KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### diff --git a/lib/LinkedList-1.2.3/library.json b/lib/LinkedList-1.2.3/library.json new file mode 100644 index 000000000..4179b248d --- /dev/null +++ b/lib/LinkedList-1.2.3/library.json @@ -0,0 +1,12 @@ +{ + "name": "LinkedList", + "keywords": "pattern", + "description": "A fully implemented LinkedList (int, float, objects, Lists or Wales) made to work with Arduino projects", + "repository": + { + "type": "git", + "url": "https://github.com/ivanseidel/LinkedList.git" + }, + "frameworks": "arduino", + "platforms": "*" +} diff --git a/lib/LinkedList-1.2.3/library.properties b/lib/LinkedList-1.2.3/library.properties new file mode 100644 index 000000000..77b1423c0 --- /dev/null +++ b/lib/LinkedList-1.2.3/library.properties @@ -0,0 +1,9 @@ +name=LinkedList +version=1.2.3 +author=Ivan Seidel +maintainer=Ivan Seidel +sentence=A fully implemented LinkedList made to work with Arduino projects +paragraph=The objective of this library is to create a pattern for projects. If you need to use a List of: int, float, objects, Lists or Wales. This is what you are looking for. +category=Data Processing +url=https://github.com/ivanseidel/LinkedList +architectures=* diff --git a/lib/NewPing-1.9.1/README.md b/lib/NewPing-1.9.1/README.md index 8760c35ff..5044422a4 100644 --- a/lib/NewPing-1.9.1/README.md +++ b/lib/NewPing-1.9.1/README.md @@ -1,3 +1,3 @@ # NewPing Arduino Library for Arduino -## See the [NewPing Wiki](https://bitbucket.org/teckel12/arduino-new-ping/wiki/Home) for documentation \ No newline at end of file +## See the [NewPing Wiki](https://bitbucket.org/teckel12/arduino-new-ping/wiki/Home) for documentation diff --git a/lib/NewPing-1.9.1/examples/NewPing15SensorsTimer/NewPing15SensorsTimer.pde b/lib/NewPing-1.9.1/examples/NewPing15SensorsTimer/NewPing15SensorsTimer.pde index a42c792b8..f34c6d7d3 100644 --- a/lib/NewPing-1.9.1/examples/NewPing15SensorsTimer/NewPing15SensorsTimer.pde +++ b/lib/NewPing-1.9.1/examples/NewPing15SensorsTimer/NewPing15SensorsTimer.pde @@ -75,4 +75,4 @@ void oneSensorCycle() { // Sensor ping cycle complete, do something with the res Serial.print("cm "); } Serial.println(); -} \ No newline at end of file +} diff --git a/lib/NewPing-1.9.1/examples/NewPing3Sensors/NewPing3Sensors.pde b/lib/NewPing-1.9.1/examples/NewPing3Sensors/NewPing3Sensors.pde index 061d7f3ee..c55832084 100644 --- a/lib/NewPing-1.9.1/examples/NewPing3Sensors/NewPing3Sensors.pde +++ b/lib/NewPing-1.9.1/examples/NewPing3Sensors/NewPing3Sensors.pde @@ -8,8 +8,8 @@ #define MAX_DISTANCE 200 // Maximum distance (in cm) to ping. NewPing sonar[SONAR_NUM] = { // Sensor object array. - NewPing(4, 5, MAX_DISTANCE), // Each sensor's trigger pin, echo pin, and max distance to ping. - NewPing(6, 7, MAX_DISTANCE), + NewPing(4, 5, MAX_DISTANCE), // Each sensor's trigger pin, echo pin, and max distance to ping. + NewPing(6, 7, MAX_DISTANCE), NewPing(8, 9, MAX_DISTANCE) }; @@ -17,7 +17,7 @@ void setup() { Serial.begin(115200); // Open serial monitor at 115200 baud to see ping results. } -void loop() { +void loop() { for (uint8_t i = 0; i < SONAR_NUM; i++) { // Loop through each sensor and display results. delay(50); // Wait 50ms between pings (about 20 pings/sec). 29ms should be the shortest delay between pings. Serial.print(i); @@ -26,4 +26,4 @@ void loop() { Serial.print("cm "); } Serial.println(); -} \ No newline at end of file +} diff --git a/lib/NewPing-1.9.1/examples/NewPingTimerMedian/NewPingTimerMedian.pde b/lib/NewPing-1.9.1/examples/NewPingTimerMedian/NewPingTimerMedian.pde index 5908f5858..16fe3fff3 100644 --- a/lib/NewPing-1.9.1/examples/NewPingTimerMedian/NewPingTimerMedian.pde +++ b/lib/NewPing-1.9.1/examples/NewPingTimerMedian/NewPingTimerMedian.pde @@ -57,4 +57,4 @@ void oneSensorCycle() { // All iterations complete, calculate the median. } Serial.print(uS[it >> 1]); Serial.println("cm"); -} \ No newline at end of file +} diff --git a/lib/NewPing-1.9.1/examples/TimerExample/TimerExample.pde b/lib/NewPing-1.9.1/examples/TimerExample/TimerExample.pde index 35e1db227..58dc5219f 100644 --- a/lib/NewPing-1.9.1/examples/TimerExample/TimerExample.pde +++ b/lib/NewPing-1.9.1/examples/TimerExample/TimerExample.pde @@ -22,4 +22,4 @@ void loop() { void toggleLED() { digitalWrite(LED_PIN, !digitalRead(LED_PIN)); // Toggle the LED. -} \ No newline at end of file +} diff --git a/lib/NewPing-1.9.1/keywords.txt b/lib/NewPing-1.9.1/keywords.txt index 0487ff26d..abf479714 100644 --- a/lib/NewPing-1.9.1/keywords.txt +++ b/lib/NewPing-1.9.1/keywords.txt @@ -28,4 +28,3 @@ convert_cm KEYWORD2 ################################### # Constants (LITERAL1) ################################### - diff --git a/lib/NewPing-1.9.1/src/NewPing.cpp b/lib/NewPing-1.9.1/src/NewPing.cpp index 05c8af4c6..e71f6181f 100644 --- a/lib/NewPing-1.9.1/src/NewPing.cpp +++ b/lib/NewPing-1.9.1/src/NewPing.cpp @@ -153,7 +153,7 @@ boolean NewPing::ping_trigger() { #if ONE_PIN_ENABLED == true pinMode(_triggerPin, OUTPUT); // Set trigger pin to output. #endif - + digitalWrite(_triggerPin, LOW); // Set the trigger pin low, should already be low, but this will make sure it is. delayMicroseconds(4); // Wait for pin to go low. digitalWrite(_triggerPin, HIGH); // Set trigger pin high, this tells the sensor to send out a ping. diff --git a/lib/NewPing-1.9.1/src/NewPing.h b/lib/NewPing-1.9.1/src/NewPing.h index 9afb261ac..49db4349a 100644 --- a/lib/NewPing-1.9.1/src/NewPing.h +++ b/lib/NewPing-1.9.1/src/NewPing.h @@ -15,7 +15,7 @@ // Blog: http://arduino.cc/forum/index.php/topic,106043.0.html // // DISCLAIMER: -// This software is furnished "as is", without technical support, and with no +// This software is furnished "as is", without technical support, and with no // warranty, express or implied, as to its usefulness for any purpose. // // BACKGROUND: @@ -23,7 +23,7 @@ // it worked. Quickly I realized the problem wasn't the sensor, it was the // available ping and ultrasonic libraries causing the problem. The NewPing // library totally fixes these problems, adds many new features, and breaths -// new life into these very affordable distance sensors. +// new life into these very affordable distance sensors. // // FEATURES: // * Works with many different ultrasonic sensors: SR04, SRF05, SRF06, DYP-ME007, URM37 & Parallax PING)))™. @@ -47,7 +47,7 @@ // max_cm_distance - [Optional] Maximum distance you wish to sense. Default=500cm. // // METHODS: -// sonar.ping([max_cm_distance]) - Send a ping and get the echo time (in microseconds) as a result. [max_cm_distance] allows you to optionally set a new max distance. +// sonar.ping([max_cm_distance]) - Send a ping and get the echo time (in microseconds) as a result. [max_cm_distance] allows you to optionally set a new max distance. // sonar.ping_in([max_cm_distance]) - Send a ping and get the distance in whole inches. [max_cm_distance] allows you to optionally set a new max distance. // sonar.ping_cm([max_cm_distance]) - Send a ping and get the distance in whole centimeters. [max_cm_distance] allows you to optionally set a new max distance. // sonar.ping_median(iterations [, max_cm_distance]) - Do multiple pings (default=5), discard out of range pings and return median in microseconds. [max_cm_distance] allows you to optionally set a new max distance. diff --git a/lib/TasmotaModbus-1.1.0/src/TasmotaModbus.cpp b/lib/TasmotaModbus-1.1.0/src/TasmotaModbus.cpp index 0be2c9de3..207fc07f8 100644 --- a/lib/TasmotaModbus-1.1.0/src/TasmotaModbus.cpp +++ b/lib/TasmotaModbus-1.1.0/src/TasmotaModbus.cpp @@ -27,7 +27,6 @@ TasmotaModbus::TasmotaModbus(int receive_pin, int transmit_pin) : TasmotaSerial( uint16_t CalculateCRC(uint8_t *frame, uint8_t num) { uint16_t crc = 0xFFFF; - uint16_t flag; for (uint8_t i = 0; i < num; i++) { crc ^= frame[i]; diff --git a/lib/TasmotaSerial-2.2.0/README.md b/lib/TasmotaSerial-2.3.0/README.md similarity index 100% rename from lib/TasmotaSerial-2.2.0/README.md rename to lib/TasmotaSerial-2.3.0/README.md diff --git a/lib/TasmotaSerial-2.2.0/examples/swsertest/swsertest.ino b/lib/TasmotaSerial-2.3.0/examples/swsertest/swsertest.ino similarity index 100% rename from lib/TasmotaSerial-2.2.0/examples/swsertest/swsertest.ino rename to lib/TasmotaSerial-2.3.0/examples/swsertest/swsertest.ino diff --git a/lib/TasmotaSerial-2.2.0/keywords.txt b/lib/TasmotaSerial-2.3.0/keywords.txt similarity index 95% rename from lib/TasmotaSerial-2.2.0/keywords.txt rename to lib/TasmotaSerial-2.3.0/keywords.txt index 87974971e..9cf6d825c 100644 --- a/lib/TasmotaSerial-2.2.0/keywords.txt +++ b/lib/TasmotaSerial-2.3.0/keywords.txt @@ -14,6 +14,7 @@ TasmotaSerial KEYWORD1 ####################################### begin KEYWORD2 +hardwareSerial KEYWORD2 read KEYWORD2 write KEYWORD2 available KEYWORD2 diff --git a/lib/TasmotaSerial-2.2.0/library.json b/lib/TasmotaSerial-2.3.0/library.json similarity index 94% rename from lib/TasmotaSerial-2.2.0/library.json rename to lib/TasmotaSerial-2.3.0/library.json index 23a2ddab9..fad36bcc6 100644 --- a/lib/TasmotaSerial-2.2.0/library.json +++ b/lib/TasmotaSerial-2.3.0/library.json @@ -1,6 +1,6 @@ { "name": "TasmotaSerial", - "version": "2.2.0", + "version": "2.3.0", "keywords": [ "serial", "io", "TasmotaSerial" ], diff --git a/lib/TasmotaSerial-2.2.0/library.properties b/lib/TasmotaSerial-2.3.0/library.properties similarity index 94% rename from lib/TasmotaSerial-2.2.0/library.properties rename to lib/TasmotaSerial-2.3.0/library.properties index 54c79e218..095077d8e 100644 --- a/lib/TasmotaSerial-2.2.0/library.properties +++ b/lib/TasmotaSerial-2.3.0/library.properties @@ -1,5 +1,5 @@ name=TasmotaSerial -version=2.2.0 +version=2.3.0 author=Theo Arends maintainer=Theo Arends sentence=Implementation of software serial with hardware serial fallback for ESP8266. diff --git a/lib/TasmotaSerial-2.2.0/src/TasmotaSerial.cpp b/lib/TasmotaSerial-2.3.0/src/TasmotaSerial.cpp similarity index 97% rename from lib/TasmotaSerial-2.2.0/src/TasmotaSerial.cpp rename to lib/TasmotaSerial-2.3.0/src/TasmotaSerial.cpp index 3df42f834..eecdeb124 100644 --- a/lib/TasmotaSerial-2.2.0/src/TasmotaSerial.cpp +++ b/lib/TasmotaSerial-2.3.0/src/TasmotaSerial.cpp @@ -1,7 +1,7 @@ /* TasmotaSerial.cpp - Minimal implementation of software serial for Tasmota - Copyright (C) 2018 Theo Arends + Copyright (C) 2019 Theo Arends This library is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -100,7 +100,7 @@ TasmotaSerial::TasmotaSerial(int receive_pin, int transmit_pin, int hardware_fal m_buffer = (uint8_t*)malloc(TM_SERIAL_BUFFER_SIZE); if (m_buffer == NULL) return; // Use getCycleCount() loop to get as exact timing as possible - m_bit_time = ESP.getCpuFreqMHz() *1000000 /TM_SERIAL_BAUDRATE; + m_bit_time = F_CPU / TM_SERIAL_BAUDRATE; pinMode(m_rx_pin, INPUT); tms_obj_list[m_rx_pin] = this; attachInterrupt(m_rx_pin, ISRList[m_rx_pin], FALLING); @@ -145,7 +145,7 @@ bool TasmotaSerial::begin(long speed, int stop_bits) { } } else { // Use getCycleCount() loop to get as exact timing as possible - m_bit_time = ESP.getCpuFreqMHz() *1000000 /speed; + m_bit_time = F_CPU / speed; m_high_speed = (speed > 9600); } return m_valid; @@ -257,7 +257,7 @@ void TasmotaSerial::rxRead() TM_SERIAL_WAIT; } // Store the received value in the buffer unless we have an overflow - int next = (m_in_pos+1) % TM_SERIAL_BUFFER_SIZE; + unsigned int next = (m_in_pos+1) % TM_SERIAL_BUFFER_SIZE; if (next != (int)m_out_pos) { m_buffer[m_in_pos] = rec; m_in_pos = next; diff --git a/lib/TasmotaSerial-2.2.0/src/TasmotaSerial.h b/lib/TasmotaSerial-2.3.0/src/TasmotaSerial.h similarity index 96% rename from lib/TasmotaSerial-2.2.0/src/TasmotaSerial.h rename to lib/TasmotaSerial-2.3.0/src/TasmotaSerial.h index de991526a..9481ef370 100644 --- a/lib/TasmotaSerial-2.2.0/src/TasmotaSerial.h +++ b/lib/TasmotaSerial-2.3.0/src/TasmotaSerial.h @@ -1,7 +1,7 @@ /* TasmotaSerial.h - Minimal implementation of software serial for Tasmota - Copyright (C) 2018 Theo Arends + Copyright (C) 2019 Theo Arends This library is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -22,7 +22,7 @@ /*********************************************************************************************\ * TasmotaSerial supports up to 115200 baud with fixed buffer size of 64 bytes using optional no iram * - * Based on EspSoftwareSerial v3.3.1 by Peter Lerup (https://github.com/plerup/espsoftwareserial) + * Based on EspSoftwareSerial v3.4.3 by Peter Lerup (https://github.com/plerup/espsoftwareserial) \*********************************************************************************************/ #define TM_SERIAL_BAUDRATE 9600 // Default baudrate diff --git a/sonoff/Parsing.cpp b/sonoff/Parsing.cpp new file mode 100644 index 000000000..22c59acda --- /dev/null +++ b/sonoff/Parsing.cpp @@ -0,0 +1,623 @@ +/* + Parsing.cpp - HTTP request parsing. + + Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling) +*/ + +// Use patched Parsing.cpp to fix ALEXA parsing issue in v2.4.2 +#include +#if defined(ARDUINO_ESP8266_RELEASE_2_4_2) +#warning **** Tasmota is using v2.4.2 patched Parsing.cpp as planned **** + +#include +#include "WiFiServer.h" +#include "WiFiClient.h" +#include "ESP8266WebServer.h" +#include "detail/mimetable.h" + +//#define DEBUG_ESP_HTTP_SERVER +#ifdef DEBUG_ESP_PORT +#define DEBUG_OUTPUT DEBUG_ESP_PORT +#else +#define DEBUG_OUTPUT Serial +#endif + +static const char Content_Type[] PROGMEM = "Content-Type"; +static const char filename[] PROGMEM = "filename"; + +static char* readBytesWithTimeout(WiFiClient& client, size_t maxLength, size_t& dataLength, int timeout_ms) +{ + char *buf = nullptr; + dataLength = 0; + while (dataLength < maxLength) { + int tries = timeout_ms; + size_t newLength; + while (!(newLength = client.available()) && tries--) delay(1); + if (!newLength) { + break; + } + if (!buf) { + buf = (char *) malloc(newLength + 1); + if (!buf) { + return nullptr; + } + } + else { + char* newBuf = (char *) realloc(buf, dataLength + newLength + 1); + if (!newBuf) { + free(buf); + return nullptr; + } + buf = newBuf; + } + client.readBytes(buf + dataLength, newLength); + dataLength += newLength; + buf[dataLength] = '\0'; + } + return buf; +} + +bool ESP8266WebServer::_parseRequest(WiFiClient& client) { + // Read the first line of HTTP request + String req = client.readStringUntil('\r'); + client.readStringUntil('\n'); + //reset header value + for (int i = 0; i < _headerKeysCount; ++i) { + _currentHeaders[i].value =String(); + } + + // First line of HTTP request looks like "GET /path HTTP/1.1" + // Retrieve the "/path" part by finding the spaces + int addr_start = req.indexOf(' '); + int addr_end = req.indexOf(' ', addr_start + 1); + if (addr_start == -1 || addr_end == -1) { +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("Invalid request: "); + DEBUG_OUTPUT.println(req); +#endif + return false; + } + + String methodStr = req.substring(0, addr_start); + String url = req.substring(addr_start + 1, addr_end); + String versionEnd = req.substring(addr_end + 8); + _currentVersion = atoi(versionEnd.c_str()); + String searchStr = ""; + int hasSearch = url.indexOf('?'); + if (hasSearch != -1){ + searchStr = url.substring(hasSearch + 1); + url = url.substring(0, hasSearch); + } + _currentUri = url; + _chunked = false; + + HTTPMethod method = HTTP_GET; + if (methodStr == F("POST")) { + method = HTTP_POST; + } else if (methodStr == F("DELETE")) { + method = HTTP_DELETE; + } else if (methodStr == F("OPTIONS")) { + method = HTTP_OPTIONS; + } else if (methodStr == F("PUT")) { + method = HTTP_PUT; + } else if (methodStr == F("PATCH")) { + method = HTTP_PATCH; + } + _currentMethod = method; + +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("method: "); + DEBUG_OUTPUT.print(methodStr); + DEBUG_OUTPUT.print(" url: "); + DEBUG_OUTPUT.print(url); + DEBUG_OUTPUT.print(" search: "); + DEBUG_OUTPUT.println(searchStr); +#endif + + //attach handler + RequestHandler* handler; + for (handler = _firstHandler; handler; handler = handler->next()) { + if (handler->canHandle(_currentMethod, _currentUri)) + break; + } + _currentHandler = handler; + + String formData; + // below is needed only when POST type request + if (method == HTTP_POST || method == HTTP_PUT || method == HTTP_PATCH || method == HTTP_DELETE){ + String boundaryStr; + String headerName; + String headerValue; + bool isForm = false; + bool isEncoded = false; + uint32_t contentLength = 0; + //parse headers + while(1){ + req = client.readStringUntil('\r'); + client.readStringUntil('\n'); + if (req == "") break;//no moar headers + int headerDiv = req.indexOf(':'); + if (headerDiv == -1){ + break; + } + headerName = req.substring(0, headerDiv); + headerValue = req.substring(headerDiv + 1); + headerValue.trim(); + _collectHeader(headerName.c_str(),headerValue.c_str()); + + #ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("headerName: "); + DEBUG_OUTPUT.println(headerName); + DEBUG_OUTPUT.print("headerValue: "); + DEBUG_OUTPUT.println(headerValue); + #endif + + if (headerName.equalsIgnoreCase(FPSTR(Content_Type))){ + using namespace mime; + if (headerValue.startsWith(FPSTR(mimeTable[txt].mimeType))){ + isForm = false; + } else if (headerValue.startsWith(F("application/x-www-form-urlencoded"))){ + isForm = false; + isEncoded = true; + } else if (headerValue.startsWith(F("multipart/"))){ + boundaryStr = headerValue.substring(headerValue.indexOf('=') + 1); + boundaryStr.replace("\"",""); + isForm = true; + } + } else if (headerName.equalsIgnoreCase(F("Content-Length"))){ + contentLength = headerValue.toInt(); + } else if (headerName.equalsIgnoreCase(F("Host"))){ + _hostHeader = headerValue; + } + } + + if (!isForm){ + size_t plainLength; + char* plainBuf = readBytesWithTimeout(client, contentLength, plainLength, HTTP_MAX_POST_WAIT); + if (plainLength < contentLength) { + free(plainBuf); + return false; + } + if (contentLength > 0) { + if(isEncoded){ + //url encoded form + if (searchStr != "") searchStr += '&'; + searchStr += plainBuf; + } + _parseArguments(searchStr); + if(!isEncoded||(0==_currentArgCount)){ // @20180124OF01: Workarround for Alexa Bug + //plain post json or other data + RequestArgument& arg = _currentArgs[_currentArgCount++]; + arg.key = F("plain"); + arg.value = String(plainBuf); + } + + #ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("Plain: "); + DEBUG_OUTPUT.println(plainBuf); + #endif + free(plainBuf); + } else { + // No content - but we can still have arguments in the URL. + _parseArguments(searchStr); + } + } + + if (isForm){ + _parseArguments(searchStr); + if (!_parseForm(client, boundaryStr, contentLength)) { + return false; + } + } + } else { + String headerName; + String headerValue; + //parse headers + while(1){ + req = client.readStringUntil('\r'); + client.readStringUntil('\n'); + if (req == "") break;//no moar headers + int headerDiv = req.indexOf(':'); + if (headerDiv == -1){ + break; + } + headerName = req.substring(0, headerDiv); + headerValue = req.substring(headerDiv + 2); + _collectHeader(headerName.c_str(),headerValue.c_str()); + + #ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("headerName: "); + DEBUG_OUTPUT.println(headerName); + DEBUG_OUTPUT.print("headerValue: "); + DEBUG_OUTPUT.println(headerValue); + #endif + + if (headerName.equalsIgnoreCase("Host")){ + _hostHeader = headerValue; + } + } + _parseArguments(searchStr); + } + client.flush(); + +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("Request: "); + DEBUG_OUTPUT.println(url); + DEBUG_OUTPUT.print(" Arguments: "); + DEBUG_OUTPUT.println(searchStr); +#endif + + return true; +} + +bool ESP8266WebServer::_collectHeader(const char* headerName, const char* headerValue) { + for (int i = 0; i < _headerKeysCount; i++) { + if (_currentHeaders[i].key.equalsIgnoreCase(headerName)) { + _currentHeaders[i].value=headerValue; + return true; + } + } + return false; +} + +void ESP8266WebServer::_parseArguments(String data) { +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("args: "); + DEBUG_OUTPUT.println(data); +#endif + if (_currentArgs) + delete[] _currentArgs; + _currentArgs = 0; + if (data.length() == 0) { + _currentArgCount = 0; + _currentArgs = new RequestArgument[1]; + return; + } + _currentArgCount = 1; + + for (int i = 0; i < (int)data.length(); ) { + i = data.indexOf('&', i); + if (i == -1) + break; + ++i; + ++_currentArgCount; + } +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("args count: "); + DEBUG_OUTPUT.println(_currentArgCount); +#endif + + _currentArgs = new RequestArgument[_currentArgCount+1]; + int pos = 0; + int iarg; + for (iarg = 0; iarg < _currentArgCount;) { + int equal_sign_index = data.indexOf('=', pos); + int next_arg_index = data.indexOf('&', pos); +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("pos "); + DEBUG_OUTPUT.print(pos); + DEBUG_OUTPUT.print("=@ "); + DEBUG_OUTPUT.print(equal_sign_index); + DEBUG_OUTPUT.print(" &@ "); + DEBUG_OUTPUT.println(next_arg_index); +#endif + if ((equal_sign_index == -1) || ((equal_sign_index > next_arg_index) && (next_arg_index != -1))) { +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("arg missing value: "); + DEBUG_OUTPUT.println(iarg); +#endif + if (next_arg_index == -1) + break; + pos = next_arg_index + 1; + continue; + } + RequestArgument& arg = _currentArgs[iarg]; + arg.key = urlDecode(data.substring(pos, equal_sign_index)); + arg.value = urlDecode(data.substring(equal_sign_index + 1, next_arg_index)); +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("arg "); + DEBUG_OUTPUT.print(iarg); + DEBUG_OUTPUT.print(" key: "); + DEBUG_OUTPUT.print(arg.key); + DEBUG_OUTPUT.print(" value: "); + DEBUG_OUTPUT.println(arg.value); +#endif + ++iarg; + if (next_arg_index == -1) + break; + pos = next_arg_index + 1; + } + _currentArgCount = iarg; +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("args count: "); + DEBUG_OUTPUT.println(_currentArgCount); +#endif + +} + +void ESP8266WebServer::_uploadWriteByte(uint8_t b){ + if (_currentUpload->currentSize == HTTP_UPLOAD_BUFLEN){ + if(_currentHandler && _currentHandler->canUpload(_currentUri)) + _currentHandler->upload(*this, _currentUri, *_currentUpload); + _currentUpload->totalSize += _currentUpload->currentSize; + _currentUpload->currentSize = 0; + } + _currentUpload->buf[_currentUpload->currentSize++] = b; +} + +uint8_t ESP8266WebServer::_uploadReadByte(WiFiClient& client){ + int res = client.read(); + if(res == -1){ + while(!client.available() && client.connected()) + yield(); + res = client.read(); + } + return (uint8_t)res; +} + +bool ESP8266WebServer::_parseForm(WiFiClient& client, String boundary, uint32_t len){ + (void) len; +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("Parse Form: Boundary: "); + DEBUG_OUTPUT.print(boundary); + DEBUG_OUTPUT.print(" Length: "); + DEBUG_OUTPUT.println(len); +#endif + String line; + int retry = 0; + do { + line = client.readStringUntil('\r'); + ++retry; + } while (line.length() == 0 && retry < 3); + + client.readStringUntil('\n'); + //start reading the form + if (line == ("--"+boundary)){ + RequestArgument* postArgs = new RequestArgument[32]; + int postArgsLen = 0; + while(1){ + String argName; + String argValue; + String argType; + String argFilename; + bool argIsFile = false; + + line = client.readStringUntil('\r'); + client.readStringUntil('\n'); + if (line.length() > 19 && line.substring(0, 19).equalsIgnoreCase(F("Content-Disposition"))){ + int nameStart = line.indexOf('='); + if (nameStart != -1){ + argName = line.substring(nameStart+2); + nameStart = argName.indexOf('='); + if (nameStart == -1){ + argName = argName.substring(0, argName.length() - 1); + } else { + argFilename = argName.substring(nameStart+2, argName.length() - 1); + argName = argName.substring(0, argName.indexOf('"')); + argIsFile = true; +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("PostArg FileName: "); + DEBUG_OUTPUT.println(argFilename); +#endif + //use GET to set the filename if uploading using blob + if (argFilename == F("blob") && hasArg(FPSTR(filename))) + argFilename = arg(FPSTR(filename)); + } +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("PostArg Name: "); + DEBUG_OUTPUT.println(argName); +#endif + using namespace mime; + argType = FPSTR(mimeTable[txt].mimeType); + line = client.readStringUntil('\r'); + client.readStringUntil('\n'); + if (line.length() > 12 && line.substring(0, 12).equalsIgnoreCase(FPSTR(Content_Type))){ + argType = line.substring(line.indexOf(':')+2); + //skip next line + client.readStringUntil('\r'); + client.readStringUntil('\n'); + } +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("PostArg Type: "); + DEBUG_OUTPUT.println(argType); +#endif + if (!argIsFile){ + while(1){ + line = client.readStringUntil('\r'); + client.readStringUntil('\n'); + if (line.startsWith("--"+boundary)) break; + if (argValue.length() > 0) argValue += "\n"; + argValue += line; + } +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("PostArg Value: "); + DEBUG_OUTPUT.println(argValue); + DEBUG_OUTPUT.println(); +#endif + + RequestArgument& arg = postArgs[postArgsLen++]; + arg.key = argName; + arg.value = argValue; + + if (line == ("--"+boundary+"--")){ +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.println("Done Parsing POST"); +#endif + break; + } + } else { + _currentUpload.reset(new HTTPUpload()); + _currentUpload->status = UPLOAD_FILE_START; + _currentUpload->name = argName; + _currentUpload->filename = argFilename; + _currentUpload->type = argType; + _currentUpload->totalSize = 0; + _currentUpload->currentSize = 0; +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("Start File: "); + DEBUG_OUTPUT.print(_currentUpload->filename); + DEBUG_OUTPUT.print(" Type: "); + DEBUG_OUTPUT.println(_currentUpload->type); +#endif + if(_currentHandler && _currentHandler->canUpload(_currentUri)) + _currentHandler->upload(*this, _currentUri, *_currentUpload); + _currentUpload->status = UPLOAD_FILE_WRITE; + uint8_t argByte = _uploadReadByte(client); +readfile: + while(argByte != 0x0D){ + if (!client.connected()) return _parseFormUploadAborted(); + _uploadWriteByte(argByte); + argByte = _uploadReadByte(client); + } + + argByte = _uploadReadByte(client); + if (!client.connected()) return _parseFormUploadAborted(); + if (argByte == 0x0A){ + argByte = _uploadReadByte(client); + if (!client.connected()) return _parseFormUploadAborted(); + if ((char)argByte != '-'){ + //continue reading the file + _uploadWriteByte(0x0D); + _uploadWriteByte(0x0A); + goto readfile; + } else { + argByte = _uploadReadByte(client); + if (!client.connected()) return _parseFormUploadAborted(); + if ((char)argByte != '-'){ + //continue reading the file + _uploadWriteByte(0x0D); + _uploadWriteByte(0x0A); + _uploadWriteByte((uint8_t)('-')); + goto readfile; + } + } + + uint8_t endBuf[boundary.length()]; + client.readBytes(endBuf, boundary.length()); + + if (strstr((const char*)endBuf, boundary.c_str()) != NULL){ + if(_currentHandler && _currentHandler->canUpload(_currentUri)) + _currentHandler->upload(*this, _currentUri, *_currentUpload); + _currentUpload->totalSize += _currentUpload->currentSize; + _currentUpload->status = UPLOAD_FILE_END; + if(_currentHandler && _currentHandler->canUpload(_currentUri)) + _currentHandler->upload(*this, _currentUri, *_currentUpload); +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("End File: "); + DEBUG_OUTPUT.print(_currentUpload->filename); + DEBUG_OUTPUT.print(" Type: "); + DEBUG_OUTPUT.print(_currentUpload->type); + DEBUG_OUTPUT.print(" Size: "); + DEBUG_OUTPUT.println(_currentUpload->totalSize); +#endif + line = client.readStringUntil(0x0D); + client.readStringUntil(0x0A); + if (line == "--"){ +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.println("Done Parsing POST"); +#endif + break; + } + continue; + } else { + _uploadWriteByte(0x0D); + _uploadWriteByte(0x0A); + _uploadWriteByte((uint8_t)('-')); + _uploadWriteByte((uint8_t)('-')); + uint32_t i = 0; + while(i < boundary.length()){ + _uploadWriteByte(endBuf[i++]); + } + argByte = _uploadReadByte(client); + goto readfile; + } + } else { + _uploadWriteByte(0x0D); + goto readfile; + } + break; + } + } + } + } + + int iarg; + int totalArgs = ((32 - postArgsLen) < _currentArgCount)?(32 - postArgsLen):_currentArgCount; + for (iarg = 0; iarg < totalArgs; iarg++){ + RequestArgument& arg = postArgs[postArgsLen++]; + arg.key = _currentArgs[iarg].key; + arg.value = _currentArgs[iarg].value; + } + if (_currentArgs) delete[] _currentArgs; + _currentArgs = new RequestArgument[postArgsLen]; + for (iarg = 0; iarg < postArgsLen; iarg++){ + RequestArgument& arg = _currentArgs[iarg]; + arg.key = postArgs[iarg].key; + arg.value = postArgs[iarg].value; + } + _currentArgCount = iarg; + if (postArgs) + delete[] postArgs; + return true; + } +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.print("Error: line: "); + DEBUG_OUTPUT.println(line); +#endif + return false; +} + +String ESP8266WebServer::urlDecode(const String& text) +{ + String decoded = ""; + char temp[] = "0x00"; + unsigned int len = text.length(); + unsigned int i = 0; + while (i < len) + { + char decodedChar; + char encodedChar = text.charAt(i++); + if ((encodedChar == '%') && (i + 1 < len)) + { + temp[2] = text.charAt(i++); + temp[3] = text.charAt(i++); + + decodedChar = strtol(temp, NULL, 16); + } + else { + if (encodedChar == '+') + { + decodedChar = ' '; + } + else { + decodedChar = encodedChar; // normal ascii char + } + } + decoded += decodedChar; + } + return decoded; +} + +bool ESP8266WebServer::_parseFormUploadAborted(){ + _currentUpload->status = UPLOAD_FILE_ABORTED; + if(_currentHandler && _currentHandler->canUpload(_currentUri)) + _currentHandler->upload(*this, _currentUri, *_currentUpload); + return false; +} + +#endif // ARDUINO_ESP8266_RELEASE \ No newline at end of file diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index 158d0c5fe..0ef542e5e 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -1,4 +1,142 @@ -/* 6.4.1 20181223 +/* 6.5.0 20190315 + * + * 6.4.1.21 20190309 + * Fix exception on GUI Configure Logging and Configure Other (#5424) + * Add support for sensor SCD30 (#5434) + * Add support for commands in sensor drivers + * Add 0x to IRRemote (SetOption29) and RCSwitch (SetOption28) received hexadecimal data (#5431) + * Add button control when no relay configured (#4682) + * + * 6.4.1.20 20190304 + * Changed webserver content handling from single String to small Chunks increasing RAM + * Changed logging message handling + * Fix additional characters in fallbacktopic, hostname and mqttclient on core 2.5.0 (#5359, #5417) + * Add command Template 255 to copy module configuration over to current active template and store as user template named Merged (#5371) + * + * 6.4.1.19 20190222 + * Add command SetOption37 for RGBCW color mapping (#5326) + * Add Korean language translations (#5344) + * Fix Energy TotalStartTime when commands EnergyReset0 and/or EnergyReset3 used (#5373) + * Fix DS18S20 temperature calculation (#5375) + * Fix float calculations in range from 0 to -1 (#5386) + * + * 6.4.1.18 20190221 + * Fix some exceptions and watchdogs due to lack of stack space - part 1 (#5215) + * Fix some exceptions and watchdogs due to lack of stack space - part 2 + * Add command SetOption62 0/1 to disable retain on Button or Swith hold messages (#5299) + * Add option WifiConfig 7 to allow reset of device in AP mode without admin password (#5297) + * Fix command WebSend when using a port number as regression from 6.4.1.17 (#5304) + * + * 6.4.1.17 20190214 + * Change template update by removing possibility to add user module config keeping template as defined (#5222) + * Fix regression from 6.4.1.16 where GPIO9 and GPIO10 connected devices did not work (#5197) + * Fix GUI wifi password acception starting with asteriks (*) (#5231, #5242) + * Add rule expression enabled by define USE_EXPRESSION in my_user_config.h (#5210) + * Add Configure Template menu option to GUI (#5222) + * Remove command SetOption62 as it's functionality is replaced by user changing the device template (#5255) + * Add property LinkCount to state and status 11 message representing number of Wifi Link re-connections + * Add property MqttCount to status 6 message representing number of Mqtt re-connections + * Add property Downtime to state and status 11 message representing the duration of wifi connection loss + * Fix command WebSend intermittent results (#5273) + * + * 6.4.1.16 20190211 + * Initial support for online template change using command Template or GUI Configure Other (#5177) + * Add parameter CFG_HOLDER to status 1 message (#5206) + * Update GUI + * + * 6.4.1.15 20190208 + * Change image name BE_MINIMAL to FIRMWARE_MINIMAL (#5106) + * Change image names USE_xyz to FIRMWARE_xyz (#5106) + * Add command SerialDelimiter 128 to filter reception of only characters between ASCII 32 and 127 (#5131) + * Add status message to former declined group commands (#5145) + * + * 6.4.1.14 20190203 + * Add SetOption32 until SetOption49 diagnostic information to Status 3 report as replacement for second property value in SetOption property name + * Add Resolution property to Status 3 report providing previous SetOption second value property + * Fix IR local echo + * Add user configuration of HLW8012 and HJL-01/BL0937 Energy Monitoring as used in Sonoff S31, Pow Ra and many Tuya based devices + * Add user configuration of MCP39F501 Energy Monitoring as used in Shelly2 + * Add support for multiple ADS1115 I2C devices (#5083) + * Add rule support for "==", "!=" ">=" and "<=" (#5122) + * Add Hass status sensor (#5139) + * Change GUI weblog solving possible empty screens (#5154) + * Change PN532 support from I2C to Serial for more stability (#5162) + * Add MHZ19 Temperature as Domoticz Temperature selection (#5128) + * + * 6.4.1.13 20190130 + * Add command SetOption36 to control boot loop default restoration (#4645, #5063) + * Add resiliency to saved Settings (#5065) + * + * 6.4.1.12 20190128 + * Change code use of boolean to bool and byte to uint8_t + * Change code uint8_t flags to bool flags + * + * 6.4.1.11 20190124 + * Remove command SetOption14 as it has been superseded by command Interlock + * Remove command SetOption63 as it has been superseded by command Interlock + * Add command Interlock 0 / 1 / 1,2 3,4 .. to control interlock ON/OFF and add up to 8 relays in 1 to 4 interlock groups (#5014) + * Add core version conditional compile options to provided PWM files (#4917) + * Add support for inverted buttons and inverted buttons without pullup (#4914) + * + * 6.4.1.10 20190121 + * Fix Hass discovery of MHZ19(B) sensors (#4992) + * Fix Hass Software Watchdog exception during discovery (#4988) + * Add support for MAX44009 Ambient Light sensor (#4907) + * + * 6.4.1.9 20190115 + * Add support for Mi LED Desk Lamp with rotary switch (#4887) + * Fix mDNS addService (#4938, #4951) + * Fix allowable MAX_RULE_VARS to 16 (#4933) + * Add (S)SerialSend3 escape sequence \x to allow hexadecimal byte value (#3560, #4947) + * Add SerialBridge command SSerialSend5 + * + * 6.4.1.8 20190107 + * Change sonoff_template.h layout regarding optional module flags like ADC0 + * Add command SetOption62 1 to force no Button/Switch pullup on dedicated modules. Currently only supported on Shelly2 (#4841) + * Fix Display exception 28 when JSON value is NULL received + * Fix Home Assistant Sensor Discovery Software Watchdog restart (#4831) + * Add support for OBI Power Socket 2 (#4829) + * Add support for YTF IR Bridge (#4855) + * Change web authentication (#4865) + * Add support for Digoo DG-SP202 Smart Socket with Energy monitoring (#4891) + * Add support for Smanergy KA10 Smart Wall Socket with Energy monitoring + * Add support for Luminea ZX2820 Smart Socket with Energy monitoring (#4921) + * Add define MDNS_ENABLE to control initial mDNS state (#4923) + * Add split interlock part 1 (#4910) + * + * 6.4.1.7 20190106 + * Fix HLW8012, HJL01 and BL0937 based energy sensors low Power (below 10W) measurement regression from 6.4.1.6 + * Add Power status functionality to LED2 when configured leaving LED1 for Link status indication + * Add no pull-up control to Shelly 2 module (default is pull-up, change GPIO2 to Switch3n for no pull-up) (#4841) + * Add 4 seconds startup delay to button control (#4829) + * Change button driver making it modular + * + * 6.4.1.6 20190105 + * Add commands PowerCal, VoltageCal and CurrentCal for HLW8012, HJL01 and BL0937 based energy sensors + * + * 6.4.1.5 20190103 + * Remove command SetOption35 0-255 for mDNS start-up delay (#4793) + * Add command SetOption55 0/1 to disable/enable mDNS (#4793) + * + * 6.4.1.4 20190101 + * Update Copyright (C) 2019 + * Fix epaper driver (#4785) + * Add support for Near Field Communication (NFC) controller PN532 using I2C (#4791) + * + * 6.4.1.3 20181229 + * Change sonoff_template.h module lay-out by removing non-configurable GPIOs + * Add support for MAX31855 K-Type thermocouple sensor using softSPI (#4764) + * + * 6.4.1.2 20181228 + * Change switch driver making it modular and introduce input filter (#4665, #4724) + * Add define DS18B20_INTERNAL_PULLUP to select internal input pullup when only one DS18B20 sensor is connected eliminating external resistor (#4738) + * Add variable %timestamp% to rules (#4749) + * + * 6.4.1.1 20181224 + * Fix most compiler warnings + * Change switch input detection by optimizing switch debounce (#4724) + * + * 6.4.1 20181224 * Change RAM usage BMP/BME I2C sensors * Change FallbackTopic from cmnd// to cmnd/_fb/ to discriminate from Topic (#1528) * Change FallbackTopic detection (#4706) diff --git a/sonoff/core_esp8266_timer.c b/sonoff/core_esp8266_timer.c index 81c3a76e0..bf852784c 100644 --- a/sonoff/core_esp8266_timer.c +++ b/sonoff/core_esp8266_timer.c @@ -19,10 +19,10 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -// Use PWM from core 2.4.0 as all other version produce LED flickering when settings are saved to flash. Still true for 2.5.0 -//#include -//#if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_1) || defined(ARDUINO_ESP8266_RELEASE_2_4_2) -//#warning **** Tasmota is using v2.4.0 timer.c as planned **** +// Use PWM from core 2.4.0 as all versions below 2.5.0-beta3 produce LED flickering when settings are saved to flash +#include +#if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_1) || defined(ARDUINO_ESP8266_RELEASE_2_4_2) +#warning **** Tasmota is using v2.4.0 timer.c as planned **** #include "wiring_private.h" #include "pins_arduino.h" @@ -108,4 +108,4 @@ void ICACHE_RAM_ATTR timer0_detachInterrupt(void) { ETS_CCOMPARE0_DISABLE(); } -//#endif // ARDUINO_ESP8266_RELEASE +#endif // ARDUINO_ESP8266_RELEASE diff --git a/sonoff/core_esp8266_wiring_digital.c b/sonoff/core_esp8266_wiring_digital.c index 33de53c75..f8d521748 100644 --- a/sonoff/core_esp8266_wiring_digital.c +++ b/sonoff/core_esp8266_wiring_digital.c @@ -19,10 +19,10 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -// Use PWM from core 2.4.0 as all other version produce LED flickering when settings are saved to flash. Still true for 2.5.0 -//#include -//#if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_1) || defined(ARDUINO_ESP8266_RELEASE_2_4_2) -//#warning **** Tasmota is using v2.4.0 wiring_digital.c as planned **** +// Use PWM from core 2.4.0 as all versions below 2.5.0-beta3 produce LED flickering when settings are saved to flash +#include +#if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_1) || defined(ARDUINO_ESP8266_RELEASE_2_4_2) +#warning **** Tasmota is using v2.4.0 wiring_digital.c as planned **** #define ARDUINO_MAIN #include "wiring_private.h" @@ -214,4 +214,4 @@ extern int digitalRead(uint8_t pin) __attribute__ ((weak, alias("__digitalRead") extern void attachInterrupt(uint8_t pin, voidFuncPtr handler, int mode) __attribute__ ((weak, alias("__attachInterrupt"))); extern void detachInterrupt(uint8_t pin) __attribute__ ((weak, alias("__detachInterrupt"))); -//#endif // ARDUINO_ESP8266_RELEASE +#endif // ARDUINO_ESP8266_RELEASE diff --git a/sonoff/core_esp8266_wiring_pwm.c b/sonoff/core_esp8266_wiring_pwm.c index 780190059..d7e179b9b 100644 --- a/sonoff/core_esp8266_wiring_pwm.c +++ b/sonoff/core_esp8266_wiring_pwm.c @@ -19,10 +19,10 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -// Use PWM from core 2.4.0 as all other version produce flicker when settings are saved to flash. Still true for 2.5.0 -//#include -//#if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_1) || defined(ARDUINO_ESP8266_RELEASE_2_4_2) -//#warning **** Tasmota is using v2.4.0 wiring_pwm.c as planned **** +// Use PWM from core 2.4.0 as all versions below 2.5.0-beta3 produce LED flickering when settings are saved to flash +#include +#if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_1) || defined(ARDUINO_ESP8266_RELEASE_2_4_2) +#warning **** Tasmota is using v2.4.0 wiring_pwm.c as planned **** #include "wiring_private.h" #include "pins_arduino.h" @@ -226,4 +226,4 @@ extern void analogWrite(uint8_t pin, int val) __attribute__ ((weak, alias("__ana extern void analogWriteFreq(uint32_t freq) __attribute__ ((weak, alias("__analogWriteFreq"))); extern void analogWriteRange(uint32_t range) __attribute__ ((weak, alias("__analogWriteRange"))); -//#endif // ARDUINO_ESP8266_RELEASE +#endif // ARDUINO_ESP8266_RELEASE diff --git a/sonoff/i18n.h b/sonoff/i18n.h index 2347cba22..73e228124 100644 --- a/sonoff/i18n.h +++ b/sonoff/i18n.h @@ -1,7 +1,7 @@ /* i18n.h - internationalization for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends + Copyright (C) 2019 Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -44,6 +44,7 @@ #define D_JSON_CHANNEL "Channel" #define D_JSON_CO2 "CarbonDioxide" #define D_JSON_COMMAND "Command" +#define D_JSON_CONFIG_HOLDER "CfgHolder" #define D_JSON_CONNECT_FAILED "Connect failed" #define D_JSON_COREVERSION "Core" #define D_JSON_COUNT "Count" @@ -53,6 +54,7 @@ #define D_JSON_DISTANCE "Distance" #define D_JSON_DNSSERVER "DNSServer" #define D_JSON_DONE "Done" +#define D_JSON_DOWNTIME "Downtime" #define D_JSON_ECO2 "eCO2" #define D_JSON_EMPTY "Empty" #define D_JSON_ENDDST "EndDST" // End Daylight Savings Time @@ -72,6 +74,7 @@ #define D_JSON_FROM "from" #define D_JSON_GAS "Gas" #define D_JSON_GATEWAY "Gateway" +#define D_JSON_GROUPS "Groups" #define D_JSON_HEAPSIZE "Heap" #define D_JSON_HIGH "High" #define D_JSON_HOST_NOT_FOUND "Host not found" @@ -87,11 +90,14 @@ #define D_JSON_INFRARED "Infrared" #define D_JSON_UNKNOWN "Unknown" #define D_JSON_LIGHT "Light" +#define D_JSON_LINK_COUNT "LinkCount" #define D_JSON_LOCAL_TIME "Local" #define D_JSON_LOW "Low" #define D_JSON_MAC "Mac" #define D_JSON_MASK "Mask" #define D_JSON_MINIMAL "minimal" +#define D_JSON_MODEL "Model" +#define D_JSON_MQTT_COUNT "MqttCount" #define D_JSON_NO "No" #define D_JSON_NOISE "Noise" #define D_JSON_NONE "None" @@ -106,9 +112,12 @@ #define D_JSON_PRESSURE "Pressure" #define D_JSON_PRESSUREATSEALEVEL "SeaPressure" #define D_JSON_PRESSURE_UNIT "PressureUnit" +#define D_JSON_PROBETEMPERATURE "ProbeTemperature" #define D_JSON_PROGRAMFLASHSIZE "ProgramFlashSize" #define D_JSON_PROGRAMSIZE "ProgramSize" +#define D_JSON_REFERENCETEMPERATURE "ReferenceTemperature" #define D_JSON_RESET "Reset" +#define D_JSON_RESOLUTION "Resolution" #define D_JSON_RESTARTING "Restarting" #define D_JSON_RESTARTREASON "RestartReason" #define D_JSON_RSSI "RSSI" @@ -155,6 +164,7 @@ #define D_JSON_ZERO_POINT_CALIBRATION "Zero Point Calibration" #define D_RSLT_ENERGY "ENERGY" +#define D_RSLT_HASS_STATE "HASS_STATE" #define D_RSLT_INFO "INFO" #define D_RSLT_MARGINS "MARGINS" #define D_RSLT_POWER "POWER" @@ -164,6 +174,8 @@ #define D_RSLT_UPTIME "UPTIME" #define D_RSLT_WARNING "WARNING" +#define D_LOG_SOME_SETTINGS_RESET "Some settings have been reset" + // Commands sonoff.ino #define D_CMND_BACKLOG "Backlog" #define D_CMND_DELAY "Delay" @@ -236,8 +248,10 @@ #define D_WCFG_4_RETRY "Retry" #define D_WCFG_5_WAIT "Wait" #define D_WCFG_6_SERIAL "Serial" + #define D_WCFG_7_WIFIMANAGER_RESET_ONLY "ManagerRst" #define D_CMND_FRIENDLYNAME "FriendlyName" #define D_CMND_SWITCHMODE "SwitchMode" +#define D_CMND_INTERLOCK "Interlock" #define D_CMND_TELEPERIOD "TelePeriod" #define D_CMND_RESTART "Restart" #define D_JSON_ONE_TO_RESTART "1 to restart" @@ -254,7 +268,11 @@ #define D_CMND_SERIALSEND "SerialSend" #define D_CMND_SERIALDELIMITER "SerialDelimiter" #define D_CMND_BAUDRATE "Baudrate" -#define D_LOG_SOME_SETTINGS_RESET "Some settings have been reset" +#define D_CMND_TEMPLATE "Template" + #define D_JSON_NAME "NAME" + #define D_JSON_GPIO "GPIO" + #define D_JSON_FLAG "FLAG" + #define D_JSON_BASE "BASE" // Commands xdrv_01_mqtt.ino #define D_CMND_MQTTHOST "MqttHost" @@ -405,6 +423,7 @@ /********************************************************************************************/ #define D_ASTERIX "********" +#define D_ASTERISK_PWD "****" #ifndef MY_LANGUAGE #include "language/en-GB.h" @@ -522,7 +541,8 @@ const char kWifiConfig[MAX_WIFI_OPTION][WCFG_MAX_STRING_LENGTH] PROGMEM = { D_WCFG_3_WPSCONFIG, D_WCFG_4_RETRY, D_WCFG_5_WAIT, - D_WCFG_6_SERIAL }; + D_WCFG_6_SERIAL, + D_WCFG_7_WIFIMANAGER_RESET_ONLY }; const char kPrefixes[3][PRFX_MAX_STRING_LENGTH] PROGMEM = { D_CMND, D_STAT, @@ -548,12 +568,17 @@ const char HTTP_SNS_SEAPRESSURE[] PROGMEM = "%s{s}%s " D_PRESSUREATSEALEVEL "{m} const char HTTP_SNS_ANALOG[] PROGMEM = "%s{s}%s " D_ANALOG_INPUT "%d{m}%d{e}"; // {s} = , {m} = , {e} = const char HTTP_SNS_ILLUMINANCE[] PROGMEM = "%s{s}%s " D_ILLUMINANCE "{m}%d " D_UNIT_LUX "{e}"; // {s} = , {m} = , {e} = -#if defined(USE_MHZ19) || defined(USE_SENSEAIR) || defined(USE_AZ7798) +#if defined(USE_MHZ19) || defined(USE_SENSEAIR) || defined(USE_AZ7798) || defined(USE_SCD30) const char HTTP_SNS_CO2[] PROGMEM = "%s{s}%s " D_CO2 "{m}%d " D_UNIT_PARTS_PER_MILLION "{e}"; // {s} = , {m} = , {e} = -#endif // USE_WEBSERVER +#endif // USE_MHZ19 + +#if defined(USE_SCD30) +const char HTTP_SNS_CO2EAVG[] PROGMEM = "%s{s}%s " D_ECO2 "{m}%d " D_UNIT_PARTS_PER_MILLION "{e}"; // {s} = , {m} = , {e} = +#endif // USE_SCD30 const char S_MAIN_MENU[] PROGMEM = D_MAIN_MENU; const char S_CONFIGURATION[] PROGMEM = D_CONFIGURATION; +const char S_CONFIGURE_TEMPLATE[] PROGMEM = D_CONFIGURE_TEMPLATE; const char S_CONFIGURE_MODULE[] PROGMEM = D_CONFIGURE_MODULE; const char S_CONFIGURE_WIFI[] PROGMEM = D_CONFIGURE_WIFI; const char S_NO_NETWORKS_FOUND[] PROGMEM = D_NO_NETWORKS_FOUND; diff --git a/sonoff/language/bg-BG.h b/sonoff/language/bg-BG.h index 222c62b58..6d44af3ff 100644 --- a/sonoff/language/bg-BG.h +++ b/sonoff/language/bg-BG.h @@ -1,7 +1,7 @@ /* bg-BG.h - localization for Bulgaria - Bulgarian for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends + Copyright (C) 2019 Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -28,7 +28,7 @@ * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. * Use online command Prefix to translate cmnd, stat and tele. * - * Updated until v6.4.0.1 + * Updated until v6.4.1.18 \*********************************************************************/ //#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) @@ -148,6 +148,7 @@ #define D_STOP "Стоп" #define D_SUBNET_MASK "Маска на подмрежата" #define D_SUBSCRIBE_TO "Записване за" +#define D_UNSUBSCRIBE_FROM "Отписване от" #define D_SUCCESSFUL "Успешно" #define D_SUNRISE "Изгрев" #define D_SUNSET "Залез" @@ -253,6 +254,7 @@ #define D_MODULE_PARAMETERS "Параметри на модула" #define D_MODULE_TYPE "Тип на модула" +#define D_PULLUP_ENABLE "Без pull-up за бутон/ключ" #define D_GPIO "GPIO" #define D_SERIAL_IN "Сериен вход" #define D_SERIAL_OUT "Сериен изход" @@ -287,6 +289,8 @@ #define D_TELEMETRY_PERIOD "Период на телеметрия" #define D_OTHER_PARAMETERS "Други параметри" +#define D_TEMPLATE "Модел" +#define D_ACTIVATE "Активирай" #define D_WEB_ADMIN_PASSWORD "Парола на уеб администратора" #define D_MQTT_ENABLE "Активиране на MQTT" #define D_FRIENDLY_NAME "Приятелско име" @@ -295,6 +299,14 @@ #define D_SINGLE_DEVICE "Единично" #define D_MULTI_DEVICE "Мулти" +#define D_CONFIGURE_TEMPLATE "Конфигуриране на модел" +#define D_TEMPLATE_PARAMETERS "Параметри на модел" +#define D_TEMPLATE_NAME "Име" +#define D_BASE_TYPE "Базиран на" +#define D_TEMPLATE_FLAGS "Флагове" +#define D_ALLOW_ADC0 "ADC0 вход" +#define D_ALLOW_PULLUP "Потребителски избор на pull-up" + #define D_SAVE_CONFIGURATION "Запазване на конфигурацията" #define D_CONFIGURATION_SAVED "Конфигурацията е запазена" #define D_CONFIGURATION_RESET "Конфигурацията е изчистена" @@ -480,66 +492,90 @@ #define D_TX20_SOUTH "Ю" #define D_TX20_WEST "З" -// sonoff_template.h -#define D_SENSOR_NONE "Няма" -#define D_SENSOR_DHT11 "DHT11" -#define D_SENSOR_AM2301 "AM2301" -#define D_SENSOR_SI7021 "SI7021" -#define D_SENSOR_DS18X20 "DS18x20" -#define D_SENSOR_I2C_SCL "I2C SCL" -#define D_SENSOR_I2C_SDA "I2C SDA" -#define D_SENSOR_WS2812 "WS2812" -#define D_SENSOR_DFR562 "MP3 плейър" -#define D_SENSOR_IRSEND "IRsend" -#define D_SENSOR_SWITCH "Ключ" // Suffix "1" -#define D_SENSOR_BUTTON "Бутон" // Suffix "1" -#define D_SENSOR_RELAY "Реле" // Suffix "1i" -#define D_SENSOR_LED "Led" // Suffix "1i" -#define D_SENSOR_PWM "PWM" // Suffix "1" -#define D_SENSOR_COUNTER "Брояч" // Suffix "1" -#define D_SENSOR_IRRECV "IRrecv" -#define D_SENSOR_MHZ_RX "MHZ Rx" -#define D_SENSOR_MHZ_TX "MHZ Tx" -#define D_SENSOR_PZEM004_RX "PZEM004 Rx" -#define D_SENSOR_PZEM016_RX "PZEM016 Rx" -#define D_SENSOR_PZEM017_RX "PZEM017 Rx" -#define D_SENSOR_PZEM0XX_TX "PZEM0XX Tx" -#define D_SENSOR_SAIR_RX "SAir Rx" -#define D_SENSOR_SAIR_TX "SAir Tx" -#define D_SENSOR_SPI_CS "SPI CS" -#define D_SENSOR_SPI_DC "SPI DC" -#define D_SENSOR_BACKLIGHT "Подсветка" -#define D_SENSOR_PMS5003 "PMS5003" -#define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" -#define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" -#define D_SENSOR_SBR_RX "SerBr Rx" -#define D_SENSOR_SBR_TX "SerBr Tx" -#define D_SENSOR_SR04_TRIG "SR04 Tri" -#define D_SENSOR_SR04_ECHO "SR04 Ech" -#define D_SENSOR_SDM120_TX "SDM120/220 Tx" -#define D_SENSOR_SDM120_RX "SDM120/220 Rx" -#define D_SENSOR_SDM630_TX "SDM630 Tx" -#define D_SENSOR_SDM630_RX "SDM630 Rx" -#define D_SENSOR_TM1638_CLK "TM16 CLK" -#define D_SENSOR_TM1638_DIO "TM16 DIO" -#define D_SENSOR_TM1638_STB "TM16 STB" -#define D_SENSOR_HX711_SCK "HX711 SCK" -#define D_SENSOR_HX711_DAT "HX711 DAT" -#define D_SENSOR_TX20_TX "TX20" -#define D_SENSOR_RFSEND "RFSend" -#define D_SENSOR_RFRECV "RFrecv" -#define D_SENSOR_TUYA_TX "Tuya Tx" -#define D_SENSOR_TUYA_RX "Tuya Rx" -#define D_SENSOR_MGC3130_XFER "MGC3130 Xfer" -#define D_SENSOR_MGC3130_RESET "MGC3130 Reset" -#define D_SENSOR_SSPI_MISO "SSPI MISO" -#define D_SENSOR_SSPI_MOSI "SSPI MOSI" -#define D_SENSOR_SSPI_SCLK "SSPI SCLK" -#define D_SENSOR_SSPI_CS "SSPI CS" -#define D_SENSOR_SSPI_DC "SSPI DC" -#define D_SENSOR_RF_SENSOR "RF датчик" -#define D_SENSOR_AZ_RX "AZ Rx" -#define D_SENSOR_AZ_TX "AZ Tx" +// sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box +#define D_SENSOR_NONE "Няма" +#define D_SENSOR_USER "Потребит." +#define D_SENSOR_DHT11 "DHT11" +#define D_SENSOR_AM2301 "AM2301" +#define D_SENSOR_SI7021 "SI7021" +#define D_SENSOR_DS18X20 "DS18x20" +#define D_SENSOR_I2C_SCL "I2C SCL" +#define D_SENSOR_I2C_SDA "I2C SDA" +#define D_SENSOR_WS2812 "WS2812" +#define D_SENSOR_DFR562 "MP3 плейър" +#define D_SENSOR_IRSEND "IRsend" +#define D_SENSOR_SWITCH "Ключ" // Suffix "1" +#define D_SENSOR_BUTTON "Бутон" // Suffix "1" +#define D_SENSOR_RELAY "Реле" // Suffix "1i" +#define D_SENSOR_LED "Led" // Suffix "1i" +#define D_SENSOR_PWM "PWM" // Suffix "1" +#define D_SENSOR_COUNTER "Брояч" // Suffix "1" +#define D_SENSOR_IRRECV "IRrecv" +#define D_SENSOR_MHZ_RX "MHZ Rx" +#define D_SENSOR_MHZ_TX "MHZ Tx" +#define D_SENSOR_PZEM004_RX "PZEM004 Rx" +#define D_SENSOR_PZEM016_RX "PZEM016 Rx" +#define D_SENSOR_PZEM017_RX "PZEM017 Rx" +#define D_SENSOR_PZEM0XX_TX "PZEM0XX Tx" +#define D_SENSOR_SAIR_RX "SAir Rx" +#define D_SENSOR_SAIR_TX "SAir Tx" +#define D_SENSOR_SPI_CS "SPI CS" +#define D_SENSOR_SPI_DC "SPI DC" +#define D_SENSOR_BACKLIGHT "Подсветка" +#define D_SENSOR_PMS5003 "PMS5003" +#define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" +#define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" +#define D_SENSOR_SBR_RX "SerBr Rx" +#define D_SENSOR_SBR_TX "SerBr Tx" +#define D_SENSOR_SR04_TRIG "SR04 Tri" +#define D_SENSOR_SR04_ECHO "SR04 Ech" +#define D_SENSOR_SDM120_TX "SDMx20 Tx" +#define D_SENSOR_SDM120_RX "SDMx20 Rx" +#define D_SENSOR_SDM630_TX "SDM630 Tx" +#define D_SENSOR_SDM630_RX "SDM630 Rx" +#define D_SENSOR_TM1638_CLK "TM16 CLK" +#define D_SENSOR_TM1638_DIO "TM16 DIO" +#define D_SENSOR_TM1638_STB "TM16 STB" +#define D_SENSOR_HX711_SCK "HX711 SCK" +#define D_SENSOR_HX711_DAT "HX711 DAT" +#define D_SENSOR_TX20_TX "TX20" +#define D_SENSOR_RFSEND "RFSend" +#define D_SENSOR_RFRECV "RFrecv" +#define D_SENSOR_TUYA_TX "Tuya Tx" +#define D_SENSOR_TUYA_RX "Tuya Rx" +#define D_SENSOR_MGC3130_XFER "MGC3130 Xfr" +#define D_SENSOR_MGC3130_RESET "MGC3130 Rst" +#define D_SENSOR_SSPI_MISO "SSPI MISO" +#define D_SENSOR_SSPI_MOSI "SSPI MOSI" +#define D_SENSOR_SSPI_SCLK "SSPI SCLK" +#define D_SENSOR_SSPI_CS "SSPI CS" +#define D_SENSOR_SSPI_DC "SSPI DC" +#define D_SENSOR_RF_SENSOR "RF датчик" +#define D_SENSOR_AZ_RX "AZ Rx" +#define D_SENSOR_AZ_TX "AZ Tx" +#define D_SENSOR_MAX31855_CS "MX31855 CS" +#define D_SENSOR_MAX31855_CLK "MX31855 CLK" +#define D_SENSOR_MAX31855_DO "MX31855 DO" +#define D_SENSOR_NRG_SEL "HLWBL SEL" // Suffix "i" +#define D_SENSOR_NRG_CF1 "HLWBL CF1" +#define D_SENSOR_HLW_CF "HLW8012 CF" +#define D_SENSOR_HJL_CF "BL0937 CF" +#define D_SENSOR_MCP39F5_TX "MCP39F5 Tx" +#define D_SENSOR_MCP39F5_RX "MCP39F5 Rx" +#define D_SENSOR_MCP39F5_RST "MCP39F5 Rst" +#define D_SENSOR_CSE7766_TX "CSE7766 Tx" +#define D_SENSOR_CSE7766_RX "CSE7766 Rx" +#define D_SENSOR_PN532_TX "PN532 Tx" +#define D_SENSOR_PN532_RX "PN532 Rx" +#define D_SENSOR_SM16716_CLK "SM16716 CLK" +#define D_SENSOR_SM16716_DAT "SM16716 DAT" +#define D_SENSOR_SM16716_POWER "SM16716 PWR" +#define D_SENSOR_MY92X1_DI "MY92x1 DI" +#define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI" +#define D_SENSOR_ARIRFRCV "ALux IrRcv" +#define D_SENSOR_TXD "Serial Tx" +#define D_SENSOR_RXD "Serial Rx" +#define D_SENSOR_ROTARY "Rotary" // Suffix "1A" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/cs-CZ.h b/sonoff/language/cs-CZ.h index 45f55af6a..e4053ea75 100644 --- a/sonoff/language/cs-CZ.h +++ b/sonoff/language/cs-CZ.h @@ -1,7 +1,7 @@ /* cs-CZ.h - localization for Czech with diacritics - Czech for Sonoff-Tasmota - Copyright (C) 2018 Vladimír Synek + Copyright (C) 2019 Vladimír Synek This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -148,6 +148,7 @@ #define D_STOP "Stop" #define D_SUBNET_MASK "Maska podsítě" #define D_SUBSCRIBE_TO "Přihlaš se do" +#define D_UNSUBSCRIBE_FROM "Unsubscribe from" #define D_SUCCESSFUL "úspěšné." #define D_SUNRISE "Svítání" #define D_SUNSET "Soumrak" @@ -253,6 +254,7 @@ #define D_MODULE_PARAMETERS "Nastavení modulu" #define D_MODULE_TYPE "Typ modulu" +#define D_PULLUP_ENABLE "No Button/Switch pull-up" #define D_GPIO "GPIO" #define D_SERIAL_IN "Serial In" #define D_SERIAL_OUT "Serial Out" @@ -287,6 +289,8 @@ #define D_TELEMETRY_PERIOD "Interval telemetrie" #define D_OTHER_PARAMETERS "Další nastavení" +#define D_TEMPLATE "Template" +#define D_ACTIVATE "Activate" #define D_WEB_ADMIN_PASSWORD "Heslo Web administrátora" #define D_MQTT_ENABLE "MQTT aktivní" #define D_FRIENDLY_NAME "Friendly Name" @@ -295,6 +299,14 @@ #define D_SINGLE_DEVICE "single device" #define D_MULTI_DEVICE "multi device" +#define D_CONFIGURE_TEMPLATE "Configure Template" +#define D_TEMPLATE_PARAMETERS "Template parameters" +#define D_TEMPLATE_NAME "Name" +#define D_BASE_TYPE "Based on" +#define D_TEMPLATE_FLAGS "Options" +#define D_ALLOW_ADC0 "ADC0 input" +#define D_ALLOW_PULLUP "User pull-up selection" + #define D_SAVE_CONFIGURATION "Ulož nastavení" #define D_CONFIGURATION_SAVED "Nastavení uloženo" #define D_CONFIGURATION_RESET "Nastavení resetováno" @@ -480,66 +492,90 @@ #define D_TX20_SOUTH "J" #define D_TX20_WEST "Z" -// sonoff_template.h -#define D_SENSOR_NONE "Není" -#define D_SENSOR_DHT11 "DHT11" -#define D_SENSOR_AM2301 "AM2301" -#define D_SENSOR_SI7021 "SI7021" -#define D_SENSOR_DS18X20 "DS18x20" -#define D_SENSOR_I2C_SCL "I2C SCL" -#define D_SENSOR_I2C_SDA "I2C SDA" -#define D_SENSOR_WS2812 "WS2812" -#define D_SENSOR_DFR562 "MP3 Player" -#define D_SENSOR_IRSEND "IRsend" -#define D_SENSOR_SWITCH "Spínač" // Suffix "1" -#define D_SENSOR_BUTTON "Tlačítko" // Suffix "1" -#define D_SENSOR_RELAY "Relé" // Suffix "1i" -#define D_SENSOR_LED "Led" // Suffix "1i" -#define D_SENSOR_PWM "PWM" // Suffix "1", -#define D_SENSOR_COUNTER "Počítadlo" // Suffix "1" -#define D_SENSOR_IRRECV "IRrecv" -#define D_SENSOR_MHZ_RX "MHZ Rx" -#define D_SENSOR_MHZ_TX "MHZ Tx" -#define D_SENSOR_PZEM004_RX "PZEM004 Rx" -#define D_SENSOR_PZEM016_RX "PZEM016 Rx" -#define D_SENSOR_PZEM017_RX "PZEM017 Rx" -#define D_SENSOR_PZEM0XX_TX "PZEM0XX Tx" -#define D_SENSOR_SAIR_RX "SAir Rx" -#define D_SENSOR_SAIR_TX "SAir Tx" -#define D_SENSOR_SPI_CS "SPI CS" -#define D_SENSOR_SPI_DC "SPI DC" -#define D_SENSOR_BACKLIGHT "BkLight" -#define D_SENSOR_PMS5003 "PMS5003" -#define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" -#define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" -#define D_SENSOR_SBR_RX "SerBr Rx" -#define D_SENSOR_SBR_TX "SerBr Tx" -#define D_SENSOR_SR04_TRIG "SR04 Tri" -#define D_SENSOR_SR04_ECHO "SR04 Ech" -#define D_SENSOR_SDM120_TX "SDM120/220 Tx" -#define D_SENSOR_SDM120_RX "SDM120/220 Rx" -#define D_SENSOR_SDM630_TX "SDM630 Tx" -#define D_SENSOR_SDM630_RX "SDM630 Rx" -#define D_SENSOR_TM1638_CLK "TM16 CLK" -#define D_SENSOR_TM1638_DIO "TM16 DIO" -#define D_SENSOR_TM1638_STB "TM16 STB" -#define D_SENSOR_HX711_SCK "HX711 SCK" -#define D_SENSOR_HX711_DAT "HX711 DAT" -#define D_SENSOR_TX20_TX "TX20" -#define D_SENSOR_RFSEND "RFSend" -#define D_SENSOR_RFRECV "RFrecv" -#define D_SENSOR_TUYA_TX "Tuya Tx" -#define D_SENSOR_TUYA_RX "Tuya Rx" -#define D_SENSOR_MGC3130_XFER "MGC3130 Xfer" -#define D_SENSOR_MGC3130_RESET "MGC3130 Reset" -#define D_SENSOR_SSPI_MISO "SSPI MISO" -#define D_SENSOR_SSPI_MOSI "SSPI MOSI" -#define D_SENSOR_SSPI_SCLK "SSPI SCLK" -#define D_SENSOR_SSPI_CS "SSPI CS" -#define D_SENSOR_SSPI_DC "SSPI DC" -#define D_SENSOR_RF_SENSOR "RF Sensor" -#define D_SENSOR_AZ_RX "AZ Rx" -#define D_SENSOR_AZ_TX "AZ Tx" +// sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box +#define D_SENSOR_NONE "Není" +#define D_SENSOR_USER "User" +#define D_SENSOR_DHT11 "DHT11" +#define D_SENSOR_AM2301 "AM2301" +#define D_SENSOR_SI7021 "SI7021" +#define D_SENSOR_DS18X20 "DS18x20" +#define D_SENSOR_I2C_SCL "I2C SCL" +#define D_SENSOR_I2C_SDA "I2C SDA" +#define D_SENSOR_WS2812 "WS2812" +#define D_SENSOR_DFR562 "MP3 Player" +#define D_SENSOR_IRSEND "IRsend" +#define D_SENSOR_SWITCH "Spínač" // Suffix "1" +#define D_SENSOR_BUTTON "Tlačítko" // Suffix "1" +#define D_SENSOR_RELAY "Relé" // Suffix "1i" +#define D_SENSOR_LED "Led" // Suffix "1i" +#define D_SENSOR_PWM "PWM" // Suffix "1", +#define D_SENSOR_COUNTER "Počítadlo" // Suffix "1" +#define D_SENSOR_IRRECV "IRrecv" +#define D_SENSOR_MHZ_RX "MHZ Rx" +#define D_SENSOR_MHZ_TX "MHZ Tx" +#define D_SENSOR_PZEM004_RX "PZEM004 Rx" +#define D_SENSOR_PZEM016_RX "PZEM016 Rx" +#define D_SENSOR_PZEM017_RX "PZEM017 Rx" +#define D_SENSOR_PZEM0XX_TX "PZEM0XX Tx" +#define D_SENSOR_SAIR_RX "SAir Rx" +#define D_SENSOR_SAIR_TX "SAir Tx" +#define D_SENSOR_SPI_CS "SPI CS" +#define D_SENSOR_SPI_DC "SPI DC" +#define D_SENSOR_BACKLIGHT "BkLight" +#define D_SENSOR_PMS5003 "PMS5003" +#define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" +#define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" +#define D_SENSOR_SBR_RX "SerBr Rx" +#define D_SENSOR_SBR_TX "SerBr Tx" +#define D_SENSOR_SR04_TRIG "SR04 Tri" +#define D_SENSOR_SR04_ECHO "SR04 Ech" +#define D_SENSOR_SDM120_TX "SDMx20 Tx" +#define D_SENSOR_SDM120_RX "SDMx20 Rx" +#define D_SENSOR_SDM630_TX "SDM630 Tx" +#define D_SENSOR_SDM630_RX "SDM630 Rx" +#define D_SENSOR_TM1638_CLK "TM16 CLK" +#define D_SENSOR_TM1638_DIO "TM16 DIO" +#define D_SENSOR_TM1638_STB "TM16 STB" +#define D_SENSOR_HX711_SCK "HX711 SCK" +#define D_SENSOR_HX711_DAT "HX711 DAT" +#define D_SENSOR_TX20_TX "TX20" +#define D_SENSOR_RFSEND "RFSend" +#define D_SENSOR_RFRECV "RFrecv" +#define D_SENSOR_TUYA_TX "Tuya Tx" +#define D_SENSOR_TUYA_RX "Tuya Rx" +#define D_SENSOR_MGC3130_XFER "MGC3130 Xfr" +#define D_SENSOR_MGC3130_RESET "MGC3130 Rst" +#define D_SENSOR_SSPI_MISO "SSPI MISO" +#define D_SENSOR_SSPI_MOSI "SSPI MOSI" +#define D_SENSOR_SSPI_SCLK "SSPI SCLK" +#define D_SENSOR_SSPI_CS "SSPI CS" +#define D_SENSOR_SSPI_DC "SSPI DC" +#define D_SENSOR_RF_SENSOR "RF Sensor" +#define D_SENSOR_AZ_RX "AZ Rx" +#define D_SENSOR_AZ_TX "AZ Tx" +#define D_SENSOR_MAX31855_CS "MX31855 CS" +#define D_SENSOR_MAX31855_CLK "MX31855 CLK" +#define D_SENSOR_MAX31855_DO "MX31855 DO" +#define D_SENSOR_NRG_SEL "HLWBL SEL" // Suffix "i" +#define D_SENSOR_NRG_CF1 "HLWBL CF1" +#define D_SENSOR_HLW_CF "HLW8012 CF" +#define D_SENSOR_HJL_CF "BL0937 CF" +#define D_SENSOR_MCP39F5_TX "MCP39F5 Tx" +#define D_SENSOR_MCP39F5_RX "MCP39F5 Rx" +#define D_SENSOR_MCP39F5_RST "MCP39F5 Rst" +#define D_SENSOR_CSE7766_TX "CSE7766 Tx" +#define D_SENSOR_CSE7766_RX "CSE7766 Rx" +#define D_SENSOR_PN532_TX "PN532 Tx" +#define D_SENSOR_PN532_RX "PN532 Rx" +#define D_SENSOR_SM16716_CLK "SM16716 CLK" +#define D_SENSOR_SM16716_DAT "SM16716 DAT" +#define D_SENSOR_SM16716_POWER "SM16716 PWR" +#define D_SENSOR_MY92X1_DI "MY92x1 DI" +#define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI" +#define D_SENSOR_ARIRFRCV "ALux IrRcv" +#define D_SENSOR_TXD "Serial Tx" +#define D_SENSOR_RXD "Serial Rx" +#define D_SENSOR_ROTARY "Rotary" // Suffix "1A" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/de-DE.h b/sonoff/language/de-DE.h index 23d8de9ed..d93f77b89 100644 --- a/sonoff/language/de-DE.h +++ b/sonoff/language/de-DE.h @@ -1,7 +1,7 @@ /* de-DE.h - localization for German - Germany for Sonoff-Tasmota - Copyright (C) 2018 VinceMasuka + Copyright (C) 2019 VinceMasuka This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -28,7 +28,7 @@ * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. * Use online command Prefix to translate cmnd, stat and tele. * - * Updated until v6.3.0.17 + * Updated until v6.4.1.18 \*********************************************************************/ //#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) @@ -148,6 +148,7 @@ #define D_STOP "Stop" #define D_SUBNET_MASK "Subnetzmaske" #define D_SUBSCRIBE_TO "abonniere" +#define D_UNSUBSCRIBE_FROM "löse abo. von" #define D_SUCCESSFUL "erfolgreich" #define D_SUNRISE "Sonnenaufgang" #define D_SUNSET "Sonnenuntergang" @@ -253,6 +254,7 @@ #define D_MODULE_PARAMETERS "Geräte-Einstellungen" #define D_MODULE_TYPE "Gerätetyp" +#define D_PULLUP_ENABLE "Kein Taster/Schalter Pull-up" #define D_GPIO "GPIO" #define D_SERIAL_IN "serieller Eingang [serial in]" #define D_SERIAL_OUT "serieller Ausgang [serial out]" @@ -287,6 +289,8 @@ #define D_TELEMETRY_PERIOD "Telemetrieperiode" #define D_OTHER_PARAMETERS "Sonstige Einstellungen" +#define D_TEMPLATE "Vorlage" +#define D_ACTIVATE "Aktivieren" #define D_WEB_ADMIN_PASSWORD "Passwort für Web Oberfläche" #define D_MQTT_ENABLE "MQTT aktivieren" #define D_FRIENDLY_NAME "Name [friendly name]" @@ -295,6 +299,14 @@ #define D_SINGLE_DEVICE "Einzelnes Gerät" #define D_MULTI_DEVICE "Mehrfachgerät" +#define D_CONFIGURE_TEMPLATE "Vorlage konfigurieren" +#define D_TEMPLATE_PARAMETERS "Vorlage Parameter" +#define D_TEMPLATE_NAME "Name" +#define D_BASE_TYPE "basiert auf" +#define D_TEMPLATE_FLAGS "Options" +#define D_ALLOW_ADC0 "ADC0 input" +#define D_ALLOW_PULLUP "Nutzer pull-up Auswahl" + #define D_SAVE_CONFIGURATION "Konfiguration speichern" #define D_CONFIGURATION_SAVED "Konfiguration gespeichert" #define D_CONFIGURATION_RESET "Konfiguration zurücksetzen" @@ -480,66 +492,90 @@ #define D_TX20_SOUTH "S" #define D_TX20_WEST "W" -// sonoff_template.h -#define D_SENSOR_NONE "None" -#define D_SENSOR_DHT11 "DHT11" -#define D_SENSOR_AM2301 "AM2301" -#define D_SENSOR_SI7021 "SI7021" -#define D_SENSOR_DS18X20 "DS18x20" -#define D_SENSOR_I2C_SCL "I2C SCL" -#define D_SENSOR_I2C_SDA "I2C SDA" -#define D_SENSOR_WS2812 "WS2812" -#define D_SENSOR_DFR562 "MP3 Player" -#define D_SENSOR_IRSEND "IRSend" -#define D_SENSOR_SWITCH "Switch " // Suffix "1" -#define D_SENSOR_BUTTON "Button " // Suffix "1" -#define D_SENSOR_RELAY "Relay " // Suffix "1i" -#define D_SENSOR_LED "LED " // Suffix "1i" -#define D_SENSOR_PWM "PWM " // Suffix "1" -#define D_SENSOR_COUNTER "Counter" // Suffix "1" -#define D_SENSOR_IRRECV "IRRecv" -#define D_SENSOR_MHZ_RX "MHZ Rx" -#define D_SENSOR_MHZ_TX "MHZ Tx" -#define D_SENSOR_PZEM004_RX "PZEM004 Rx" -#define D_SENSOR_PZEM016_RX "PZEM016 Rx" -#define D_SENSOR_PZEM017_RX "PZEM017 Rx" -#define D_SENSOR_PZEM0XX_TX "PZEM0XX Tx" -#define D_SENSOR_SAIR_RX "SAir Rx" -#define D_SENSOR_SAIR_TX "SAir Tx" -#define D_SENSOR_SPI_CS "SPI CS" -#define D_SENSOR_SPI_DC "SPI DC" -#define D_SENSOR_BACKLIGHT "BkLight" -#define D_SENSOR_PMS5003 "PMS5003" -#define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" -#define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" -#define D_SENSOR_SBR_RX "SerBr Rx" -#define D_SENSOR_SBR_TX "SerBr Tx" -#define D_SENSOR_SR04_TRIG "SR04 Tri" -#define D_SENSOR_SR04_ECHO "SR04 Ech" -#define D_SENSOR_SDM120_TX "SDM120/220 Tx" -#define D_SENSOR_SDM120_RX "SDM120/220 Rx" -#define D_SENSOR_SDM630_TX "SDM630 Tx" -#define D_SENSOR_SDM630_RX "SDM630 Rx" -#define D_SENSOR_TM1638_CLK "TM16 CLK" -#define D_SENSOR_TM1638_DIO "TM16 DIO" -#define D_SENSOR_TM1638_STB "TM16 STB" -#define D_SENSOR_HX711_SCK "HX711 SCK" -#define D_SENSOR_HX711_DAT "HX711 DAT" -#define D_SENSOR_TX20_TX "TX20" -#define D_SENSOR_RFSEND "RFSend" -#define D_SENSOR_RFRECV "RFrecv" -#define D_SENSOR_TUYA_TX "Tuya Tx" -#define D_SENSOR_TUYA_RX "Tuya Rx" -#define D_SENSOR_MGC3130_XFER "MGC3130 Xfer" -#define D_SENSOR_MGC3130_RESET "MGC3130 Reset" -#define D_SENSOR_SSPI_MISO "SSPI MISO" -#define D_SENSOR_SSPI_MOSI "SSPI MOSI" -#define D_SENSOR_SSPI_SCLK "SSPI SCLK" -#define D_SENSOR_SSPI_CS "SSPI CS" -#define D_SENSOR_SSPI_DC "SSPI DC" -#define D_SENSOR_RF_SENSOR "RF Sensor" -#define D_SENSOR_AZ_RX "AZ Rx" -#define D_SENSOR_AZ_TX "AZ Tx" +// sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box +#define D_SENSOR_NONE "None" +#define D_SENSOR_USER "User" +#define D_SENSOR_DHT11 "DHT11" +#define D_SENSOR_AM2301 "AM2301" +#define D_SENSOR_SI7021 "SI7021" +#define D_SENSOR_DS18X20 "DS18x20" +#define D_SENSOR_I2C_SCL "I2C SCL" +#define D_SENSOR_I2C_SDA "I2C SDA" +#define D_SENSOR_WS2812 "WS2812" +#define D_SENSOR_DFR562 "MP3 Player" +#define D_SENSOR_IRSEND "IRsend" +#define D_SENSOR_SWITCH "Switch" // Suffix "1" +#define D_SENSOR_BUTTON "Button" // Suffix "1" +#define D_SENSOR_RELAY "Relay" // Suffix "1i" +#define D_SENSOR_LED "Led" // Suffix "1i" +#define D_SENSOR_PWM "PWM" // Suffix "1" +#define D_SENSOR_COUNTER "Counter" // Suffix "1" +#define D_SENSOR_IRRECV "IRrecv" +#define D_SENSOR_MHZ_RX "MHZ Rx" +#define D_SENSOR_MHZ_TX "MHZ Tx" +#define D_SENSOR_PZEM004_RX "PZEM004 Rx" +#define D_SENSOR_PZEM016_RX "PZEM016 Rx" +#define D_SENSOR_PZEM017_RX "PZEM017 Rx" +#define D_SENSOR_PZEM0XX_TX "PZEM0XX Tx" +#define D_SENSOR_SAIR_RX "SAir Rx" +#define D_SENSOR_SAIR_TX "SAir Tx" +#define D_SENSOR_SPI_CS "SPI CS" +#define D_SENSOR_SPI_DC "SPI DC" +#define D_SENSOR_BACKLIGHT "BkLight" +#define D_SENSOR_PMS5003 "PMS5003" +#define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" +#define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" +#define D_SENSOR_SBR_RX "SerBr Rx" +#define D_SENSOR_SBR_TX "SerBr Tx" +#define D_SENSOR_SR04_TRIG "SR04 Tri" +#define D_SENSOR_SR04_ECHO "SR04 Ech" +#define D_SENSOR_SDM120_TX "SDMx20 Tx" +#define D_SENSOR_SDM120_RX "SDMx20 Rx" +#define D_SENSOR_SDM630_TX "SDM630 Tx" +#define D_SENSOR_SDM630_RX "SDM630 Rx" +#define D_SENSOR_TM1638_CLK "TM16 CLK" +#define D_SENSOR_TM1638_DIO "TM16 DIO" +#define D_SENSOR_TM1638_STB "TM16 STB" +#define D_SENSOR_HX711_SCK "HX711 SCK" +#define D_SENSOR_HX711_DAT "HX711 DAT" +#define D_SENSOR_TX20_TX "TX20" +#define D_SENSOR_RFSEND "RFSend" +#define D_SENSOR_RFRECV "RFrecv" +#define D_SENSOR_TUYA_TX "Tuya Tx" +#define D_SENSOR_TUYA_RX "Tuya Rx" +#define D_SENSOR_MGC3130_XFER "MGC3130 Xfr" +#define D_SENSOR_MGC3130_RESET "MGC3130 Rst" +#define D_SENSOR_SSPI_MISO "SSPI MISO" +#define D_SENSOR_SSPI_MOSI "SSPI MOSI" +#define D_SENSOR_SSPI_SCLK "SSPI SCLK" +#define D_SENSOR_SSPI_CS "SSPI CS" +#define D_SENSOR_SSPI_DC "SSPI DC" +#define D_SENSOR_RF_SENSOR "RF Sensor" +#define D_SENSOR_AZ_RX "AZ Rx" +#define D_SENSOR_AZ_TX "AZ Tx" +#define D_SENSOR_MAX31855_CS "MX31855 CS" +#define D_SENSOR_MAX31855_CLK "MX31855 CLK" +#define D_SENSOR_MAX31855_DO "MX31855 DO" +#define D_SENSOR_NRG_SEL "HLWBL SEL" // Suffix "i" +#define D_SENSOR_NRG_CF1 "HLWBL CF1" +#define D_SENSOR_HLW_CF "HLW8012 CF" +#define D_SENSOR_HJL_CF "BL0937 CF" +#define D_SENSOR_MCP39F5_TX "MCP39F5 Tx" +#define D_SENSOR_MCP39F5_RX "MCP39F5 Rx" +#define D_SENSOR_MCP39F5_RST "MCP39F5 Rst" +#define D_SENSOR_CSE7766_TX "CSE7766 Tx" +#define D_SENSOR_CSE7766_RX "CSE7766 Rx" +#define D_SENSOR_PN532_TX "PN532 Tx" +#define D_SENSOR_PN532_RX "PN532 Rx" +#define D_SENSOR_SM16716_CLK "SM16716 CLK" +#define D_SENSOR_SM16716_DAT "SM16716 DAT" +#define D_SENSOR_SM16716_POWER "SM16716 PWR" +#define D_SENSOR_MY92X1_DI "MY92x1 DI" +#define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI" +#define D_SENSOR_ARIRFRCV "ALux IrRcv" +#define D_SENSOR_TXD "Serial Tx" +#define D_SENSOR_RXD "Serial Rx" +#define D_SENSOR_ROTARY "Rotary" // Suffix "1A" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/el-GR.h b/sonoff/language/el-GR.h index a8b48ed35..8a3fe75a5 100644 --- a/sonoff/language/el-GR.h +++ b/sonoff/language/el-GR.h @@ -1,7 +1,7 @@ /* el-GR.h - localization for Greek - Greece for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends (translated by Nick Galfas) + Copyright (C) 2019 Theo Arends (translated by Nick Galfas) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -148,6 +148,7 @@ #define D_STOP "Τερματισμός" #define D_SUBNET_MASK "Μάσκα υποδικτύου" #define D_SUBSCRIBE_TO "Εγγραφή στο" +#define D_UNSUBSCRIBE_FROM "Unsubscribe from" #define D_SUCCESSFUL "Επιτυχές" #define D_SUNRISE "Σούρουπο" #define D_SUNSET "Ηλιοβασίλεμα" @@ -253,6 +254,7 @@ #define D_MODULE_PARAMETERS "Παράμετροι μονάδας" #define D_MODULE_TYPE "Τύπος μονάδας" +#define D_PULLUP_ENABLE "No Button/Switch pull-up" #define D_GPIO "GPIO" #define D_SERIAL_IN "Serial In" #define D_SERIAL_OUT "Serial Out" @@ -287,6 +289,8 @@ #define D_TELEMETRY_PERIOD "Περίοδος τηλεμετρίας" #define D_OTHER_PARAMETERS "Άλλες παράμετροι" +#define D_TEMPLATE "Template" +#define D_ACTIVATE "Activate" #define D_WEB_ADMIN_PASSWORD "Κωδικός διαχειριστή" #define D_MQTT_ENABLE "Ενεργοποίηση MQTT" #define D_FRIENDLY_NAME "Φιλική ονομασία" @@ -295,6 +299,14 @@ #define D_SINGLE_DEVICE "μονή συσκευή" #define D_MULTI_DEVICE "πολλαπλές συσκευές" +#define D_CONFIGURE_TEMPLATE "Configure Template" +#define D_TEMPLATE_PARAMETERS "Template parameters" +#define D_TEMPLATE_NAME "Name" +#define D_BASE_TYPE "Based on" +#define D_TEMPLATE_FLAGS "Options" +#define D_ALLOW_ADC0 "ADC0 input" +#define D_ALLOW_PULLUP "User pull-up selection" + #define D_SAVE_CONFIGURATION "Αποθήκευση ρυθμίσεων" #define D_CONFIGURATION_SAVED "Οι ρυθμίσεις αποθηκεύτηκαν" #define D_CONFIGURATION_RESET "Επαναφορά ρυθμίσεων" @@ -480,66 +492,90 @@ #define D_TX20_SOUTH "Ν" #define D_TX20_WEST "Δ" -// sonoff_template.h -#define D_SENSOR_NONE "Κανένα" -#define D_SENSOR_DHT11 "DHT11" -#define D_SENSOR_AM2301 "AM2301" -#define D_SENSOR_SI7021 "SI7021" -#define D_SENSOR_DS18X20 "DS18x20" -#define D_SENSOR_I2C_SCL "I2C SCL" -#define D_SENSOR_I2C_SDA "I2C SDA" -#define D_SENSOR_WS2812 "WS2812" -#define D_SENSOR_DFR562 "MP3 Player" -#define D_SENSOR_IRSEND "IRsend" -#define D_SENSOR_SWITCH "Διακόπτης" // Suffix "1" -#define D_SENSOR_BUTTON "Κουμπί" // Suffix "1" -#define D_SENSOR_RELAY "Ρελέ" // Suffix "1i" -#define D_SENSOR_LED "Led" // Suffix "1i" -#define D_SENSOR_PWM "PWM" // Suffix "1" -#define D_SENSOR_COUNTER "Μετρητής" // Suffix "1" -#define D_SENSOR_IRRECV "IRrecv" -#define D_SENSOR_MHZ_RX "MHZ Rx" -#define D_SENSOR_MHZ_TX "MHZ Tx" -#define D_SENSOR_PZEM004_RX "PZEM004 Rx" -#define D_SENSOR_PZEM016_RX "PZEM016 Rx" -#define D_SENSOR_PZEM017_RX "PZEM017 Rx" -#define D_SENSOR_PZEM0XX_TX "PZEM0XX Tx" -#define D_SENSOR_SAIR_RX "SAir Rx" -#define D_SENSOR_SAIR_TX "SAir Tx" -#define D_SENSOR_SPI_CS "SPI CS" -#define D_SENSOR_SPI_DC "SPI DC" -#define D_SENSOR_BACKLIGHT "BkLight" -#define D_SENSOR_PMS5003 "PMS5003" -#define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" -#define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" -#define D_SENSOR_SBR_RX "SerBr Rx" -#define D_SENSOR_SBR_TX "SerBr Tx" -#define D_SENSOR_SR04_TRIG "SR04 Tri" -#define D_SENSOR_SR04_ECHO "SR04 Ech" -#define D_SENSOR_SDM120_TX "SDM120/220 Tx" -#define D_SENSOR_SDM120_RX "SDM120/220 Rx" -#define D_SENSOR_SDM630_TX "SDM630 Tx" -#define D_SENSOR_SDM630_RX "SDM630 Rx" -#define D_SENSOR_TM1638_CLK "TM16 CLK" -#define D_SENSOR_TM1638_DIO "TM16 DIO" -#define D_SENSOR_TM1638_STB "TM16 STB" -#define D_SENSOR_HX711_SCK "HX711 SCK" -#define D_SENSOR_HX711_DAT "HX711 DAT" -#define D_SENSOR_TX20_TX "TX20" -#define D_SENSOR_RFSEND "RFSend" -#define D_SENSOR_RFRECV "RFrecv" -#define D_SENSOR_TUYA_TX "Tuya Tx" -#define D_SENSOR_TUYA_RX "Tuya Rx" -#define D_SENSOR_MGC3130_XFER "MGC3130 Xfer" -#define D_SENSOR_MGC3130_RESET "MGC3130 Reset" -#define D_SENSOR_SSPI_MISO "SSPI MISO" -#define D_SENSOR_SSPI_MOSI "SSPI MOSI" -#define D_SENSOR_SSPI_SCLK "SSPI SCLK" -#define D_SENSOR_SSPI_CS "SSPI CS" -#define D_SENSOR_SSPI_DC "SSPI DC" -#define D_SENSOR_RF_SENSOR "RF Sensor" -#define D_SENSOR_AZ_RX "AZ Rx" -#define D_SENSOR_AZ_TX "AZ Tx" +// sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box +#define D_SENSOR_NONE "Κανένα" +#define D_SENSOR_USER "User" +#define D_SENSOR_DHT11 "DHT11" +#define D_SENSOR_AM2301 "AM2301" +#define D_SENSOR_SI7021 "SI7021" +#define D_SENSOR_DS18X20 "DS18x20" +#define D_SENSOR_I2C_SCL "I2C SCL" +#define D_SENSOR_I2C_SDA "I2C SDA" +#define D_SENSOR_WS2812 "WS2812" +#define D_SENSOR_DFR562 "MP3 Player" +#define D_SENSOR_IRSEND "IRsend" +#define D_SENSOR_SWITCH "Διακόπτης" // Suffix "1" +#define D_SENSOR_BUTTON "Κουμπί" // Suffix "1" +#define D_SENSOR_RELAY "Ρελέ" // Suffix "1i" +#define D_SENSOR_LED "Led" // Suffix "1i" +#define D_SENSOR_PWM "PWM" // Suffix "1" +#define D_SENSOR_COUNTER "Μετρητής" // Suffix "1" +#define D_SENSOR_IRRECV "IRrecv" +#define D_SENSOR_MHZ_RX "MHZ Rx" +#define D_SENSOR_MHZ_TX "MHZ Tx" +#define D_SENSOR_PZEM004_RX "PZEM004 Rx" +#define D_SENSOR_PZEM016_RX "PZEM016 Rx" +#define D_SENSOR_PZEM017_RX "PZEM017 Rx" +#define D_SENSOR_PZEM0XX_TX "PZEM0XX Tx" +#define D_SENSOR_SAIR_RX "SAir Rx" +#define D_SENSOR_SAIR_TX "SAir Tx" +#define D_SENSOR_SPI_CS "SPI CS" +#define D_SENSOR_SPI_DC "SPI DC" +#define D_SENSOR_BACKLIGHT "BkLight" +#define D_SENSOR_PMS5003 "PMS5003" +#define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" +#define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" +#define D_SENSOR_SBR_RX "SerBr Rx" +#define D_SENSOR_SBR_TX "SerBr Tx" +#define D_SENSOR_SR04_TRIG "SR04 Tri" +#define D_SENSOR_SR04_ECHO "SR04 Ech" +#define D_SENSOR_SDM120_TX "SDMx20 Tx" +#define D_SENSOR_SDM120_RX "SDMx20 Rx" +#define D_SENSOR_SDM630_TX "SDM630 Tx" +#define D_SENSOR_SDM630_RX "SDM630 Rx" +#define D_SENSOR_TM1638_CLK "TM16 CLK" +#define D_SENSOR_TM1638_DIO "TM16 DIO" +#define D_SENSOR_TM1638_STB "TM16 STB" +#define D_SENSOR_HX711_SCK "HX711 SCK" +#define D_SENSOR_HX711_DAT "HX711 DAT" +#define D_SENSOR_TX20_TX "TX20" +#define D_SENSOR_RFSEND "RFSend" +#define D_SENSOR_RFRECV "RFrecv" +#define D_SENSOR_TUYA_TX "Tuya Tx" +#define D_SENSOR_TUYA_RX "Tuya Rx" +#define D_SENSOR_MGC3130_XFER "MGC3130 Xfr" +#define D_SENSOR_MGC3130_RESET "MGC3130 Rst" +#define D_SENSOR_SSPI_MISO "SSPI MISO" +#define D_SENSOR_SSPI_MOSI "SSPI MOSI" +#define D_SENSOR_SSPI_SCLK "SSPI SCLK" +#define D_SENSOR_SSPI_CS "SSPI CS" +#define D_SENSOR_SSPI_DC "SSPI DC" +#define D_SENSOR_RF_SENSOR "RF Sensor" +#define D_SENSOR_AZ_RX "AZ Rx" +#define D_SENSOR_AZ_TX "AZ Tx" +#define D_SENSOR_MAX31855_CS "MX31855 CS" +#define D_SENSOR_MAX31855_CLK "MX31855 CLK" +#define D_SENSOR_MAX31855_DO "MX31855 DO" +#define D_SENSOR_NRG_SEL "HLWBL SEL" // Suffix "i" +#define D_SENSOR_NRG_CF1 "HLWBL CF1" +#define D_SENSOR_HLW_CF "HLW8012 CF" +#define D_SENSOR_HJL_CF "BL0937 CF" +#define D_SENSOR_MCP39F5_TX "MCP39F5 Tx" +#define D_SENSOR_MCP39F5_RX "MCP39F5 Rx" +#define D_SENSOR_MCP39F5_RST "MCP39F5 Rst" +#define D_SENSOR_CSE7766_TX "CSE7766 Tx" +#define D_SENSOR_CSE7766_RX "CSE7766 Rx" +#define D_SENSOR_PN532_TX "PN532 Tx" +#define D_SENSOR_PN532_RX "PN532 Rx" +#define D_SENSOR_SM16716_CLK "SM16716 CLK" +#define D_SENSOR_SM16716_DAT "SM16716 DAT" +#define D_SENSOR_SM16716_POWER "SM16716 PWR" +#define D_SENSOR_MY92X1_DI "MY92x1 DI" +#define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI" +#define D_SENSOR_ARIRFRCV "ALux IrRcv" +#define D_SENSOR_TXD "Serial Tx" +#define D_SENSOR_RXD "Serial Rx" +#define D_SENSOR_ROTARY "Rotary" // Suffix "1A" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/en-GB.h b/sonoff/language/en-GB.h index 2ea9c249a..e459cd02d 100644 --- a/sonoff/language/en-GB.h +++ b/sonoff/language/en-GB.h @@ -1,7 +1,7 @@ /* en-GB.h - localization for English - United Kingdom for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends + Copyright (C) 2019 Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -148,6 +148,7 @@ #define D_STOP "Stop" #define D_SUBNET_MASK "Subnet Mask" #define D_SUBSCRIBE_TO "Subscribe to" +#define D_UNSUBSCRIBE_FROM "Unsubscribe from" #define D_SUCCESSFUL "Successful" #define D_SUNRISE "Sunrise" #define D_SUNSET "Sunset" @@ -253,6 +254,7 @@ #define D_MODULE_PARAMETERS "Module parameters" #define D_MODULE_TYPE "Module type" +#define D_PULLUP_ENABLE "No Button/Switch pull-up" #define D_GPIO "GPIO" #define D_SERIAL_IN "Serial In" #define D_SERIAL_OUT "Serial Out" @@ -287,6 +289,8 @@ #define D_TELEMETRY_PERIOD "Telemetry period" #define D_OTHER_PARAMETERS "Other parameters" +#define D_TEMPLATE "Template" +#define D_ACTIVATE "Activate" #define D_WEB_ADMIN_PASSWORD "Web Admin Password" #define D_MQTT_ENABLE "MQTT enable" #define D_FRIENDLY_NAME "Friendly Name" @@ -295,6 +299,14 @@ #define D_SINGLE_DEVICE "single device" #define D_MULTI_DEVICE "multi device" +#define D_CONFIGURE_TEMPLATE "Configure Template" +#define D_TEMPLATE_PARAMETERS "Template parameters" +#define D_TEMPLATE_NAME "Name" +#define D_BASE_TYPE "Based on" +#define D_TEMPLATE_FLAGS "Options" +#define D_ALLOW_ADC0 "ADC0 input" +#define D_ALLOW_PULLUP "User pull-up selection" + #define D_SAVE_CONFIGURATION "Save configuration" #define D_CONFIGURATION_SAVED "Configuration saved" #define D_CONFIGURATION_RESET "Configuration reset" @@ -480,66 +492,90 @@ #define D_TX20_SOUTH "S" #define D_TX20_WEST "W" -// sonoff_template.h -#define D_SENSOR_NONE "None" -#define D_SENSOR_DHT11 "DHT11" -#define D_SENSOR_AM2301 "AM2301" -#define D_SENSOR_SI7021 "SI7021" -#define D_SENSOR_DS18X20 "DS18x20" -#define D_SENSOR_I2C_SCL "I2C SCL" -#define D_SENSOR_I2C_SDA "I2C SDA" -#define D_SENSOR_WS2812 "WS2812" -#define D_SENSOR_DFR562 "MP3 Player" -#define D_SENSOR_IRSEND "IRsend" -#define D_SENSOR_SWITCH "Switch" // Suffix "1" -#define D_SENSOR_BUTTON "Button" // Suffix "1" -#define D_SENSOR_RELAY "Relay" // Suffix "1i" -#define D_SENSOR_LED "Led" // Suffix "1i" -#define D_SENSOR_PWM "PWM" // Suffix "1" -#define D_SENSOR_COUNTER "Counter" // Suffix "1" -#define D_SENSOR_IRRECV "IRrecv" -#define D_SENSOR_MHZ_RX "MHZ Rx" -#define D_SENSOR_MHZ_TX "MHZ Tx" -#define D_SENSOR_PZEM004_RX "PZEM004 Rx" -#define D_SENSOR_PZEM016_RX "PZEM016 Rx" -#define D_SENSOR_PZEM017_RX "PZEM017 Rx" -#define D_SENSOR_PZEM0XX_TX "PZEM0XX Tx" -#define D_SENSOR_SAIR_RX "SAir Rx" -#define D_SENSOR_SAIR_TX "SAir Tx" -#define D_SENSOR_SPI_CS "SPI CS" -#define D_SENSOR_SPI_DC "SPI DC" -#define D_SENSOR_BACKLIGHT "BkLight" -#define D_SENSOR_PMS5003 "PMS5003" -#define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" -#define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" -#define D_SENSOR_SBR_RX "SerBr Rx" -#define D_SENSOR_SBR_TX "SerBr Tx" -#define D_SENSOR_SR04_TRIG "SR04 Tri" -#define D_SENSOR_SR04_ECHO "SR04 Ech" -#define D_SENSOR_SDM120_TX "SDM120/220 Tx" -#define D_SENSOR_SDM120_RX "SDM120/220 Rx" -#define D_SENSOR_SDM630_TX "SDM630 Tx" -#define D_SENSOR_SDM630_RX "SDM630 Rx" -#define D_SENSOR_TM1638_CLK "TM16 CLK" -#define D_SENSOR_TM1638_DIO "TM16 DIO" -#define D_SENSOR_TM1638_STB "TM16 STB" -#define D_SENSOR_HX711_SCK "HX711 SCK" -#define D_SENSOR_HX711_DAT "HX711 DAT" -#define D_SENSOR_TX20_TX "TX20" -#define D_SENSOR_RFSEND "RFSend" -#define D_SENSOR_RFRECV "RFrecv" -#define D_SENSOR_TUYA_TX "Tuya Tx" -#define D_SENSOR_TUYA_RX "Tuya Rx" -#define D_SENSOR_MGC3130_XFER "MGC3130 Xfer" -#define D_SENSOR_MGC3130_RESET "MGC3130 Reset" -#define D_SENSOR_SSPI_MISO "SSPI MISO" -#define D_SENSOR_SSPI_MOSI "SSPI MOSI" -#define D_SENSOR_SSPI_SCLK "SSPI SCLK" -#define D_SENSOR_SSPI_CS "SSPI CS" -#define D_SENSOR_SSPI_DC "SSPI DC" -#define D_SENSOR_RF_SENSOR "RF Sensor" -#define D_SENSOR_AZ_RX "AZ Rx" -#define D_SENSOR_AZ_TX "AZ Tx" +// sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box +#define D_SENSOR_NONE "None" +#define D_SENSOR_USER "User" +#define D_SENSOR_DHT11 "DHT11" +#define D_SENSOR_AM2301 "AM2301" +#define D_SENSOR_SI7021 "SI7021" +#define D_SENSOR_DS18X20 "DS18x20" +#define D_SENSOR_I2C_SCL "I2C SCL" +#define D_SENSOR_I2C_SDA "I2C SDA" +#define D_SENSOR_WS2812 "WS2812" +#define D_SENSOR_DFR562 "MP3 Player" +#define D_SENSOR_IRSEND "IRsend" +#define D_SENSOR_SWITCH "Switch" // Suffix "1" +#define D_SENSOR_BUTTON "Button" // Suffix "1" +#define D_SENSOR_RELAY "Relay" // Suffix "1i" +#define D_SENSOR_LED "Led" // Suffix "1i" +#define D_SENSOR_PWM "PWM" // Suffix "1" +#define D_SENSOR_COUNTER "Counter" // Suffix "1" +#define D_SENSOR_IRRECV "IRrecv" +#define D_SENSOR_MHZ_RX "MHZ Rx" +#define D_SENSOR_MHZ_TX "MHZ Tx" +#define D_SENSOR_PZEM004_RX "PZEM004 Rx" +#define D_SENSOR_PZEM016_RX "PZEM016 Rx" +#define D_SENSOR_PZEM017_RX "PZEM017 Rx" +#define D_SENSOR_PZEM0XX_TX "PZEM0XX Tx" +#define D_SENSOR_SAIR_RX "SAir Rx" +#define D_SENSOR_SAIR_TX "SAir Tx" +#define D_SENSOR_SPI_CS "SPI CS" +#define D_SENSOR_SPI_DC "SPI DC" +#define D_SENSOR_BACKLIGHT "BkLight" +#define D_SENSOR_PMS5003 "PMS5003" +#define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" +#define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" +#define D_SENSOR_SBR_RX "SerBr Rx" +#define D_SENSOR_SBR_TX "SerBr Tx" +#define D_SENSOR_SR04_TRIG "SR04 Tri" +#define D_SENSOR_SR04_ECHO "SR04 Ech" +#define D_SENSOR_SDM120_TX "SDMx20 Tx" +#define D_SENSOR_SDM120_RX "SDMx20 Rx" +#define D_SENSOR_SDM630_TX "SDM630 Tx" +#define D_SENSOR_SDM630_RX "SDM630 Rx" +#define D_SENSOR_TM1638_CLK "TM16 CLK" +#define D_SENSOR_TM1638_DIO "TM16 DIO" +#define D_SENSOR_TM1638_STB "TM16 STB" +#define D_SENSOR_HX711_SCK "HX711 SCK" +#define D_SENSOR_HX711_DAT "HX711 DAT" +#define D_SENSOR_TX20_TX "TX20" +#define D_SENSOR_RFSEND "RFSend" +#define D_SENSOR_RFRECV "RFrecv" +#define D_SENSOR_TUYA_TX "Tuya Tx" +#define D_SENSOR_TUYA_RX "Tuya Rx" +#define D_SENSOR_MGC3130_XFER "MGC3130 Xfr" +#define D_SENSOR_MGC3130_RESET "MGC3130 Rst" +#define D_SENSOR_SSPI_MISO "SSPI MISO" +#define D_SENSOR_SSPI_MOSI "SSPI MOSI" +#define D_SENSOR_SSPI_SCLK "SSPI SCLK" +#define D_SENSOR_SSPI_CS "SSPI CS" +#define D_SENSOR_SSPI_DC "SSPI DC" +#define D_SENSOR_RF_SENSOR "RF Sensor" +#define D_SENSOR_AZ_RX "AZ Rx" +#define D_SENSOR_AZ_TX "AZ Tx" +#define D_SENSOR_MAX31855_CS "MX31855 CS" +#define D_SENSOR_MAX31855_CLK "MX31855 CLK" +#define D_SENSOR_MAX31855_DO "MX31855 DO" +#define D_SENSOR_NRG_SEL "HLWBL SEL" // Suffix "i" +#define D_SENSOR_NRG_CF1 "HLWBL CF1" +#define D_SENSOR_HLW_CF "HLW8012 CF" +#define D_SENSOR_HJL_CF "BL0937 CF" +#define D_SENSOR_MCP39F5_TX "MCP39F5 Tx" +#define D_SENSOR_MCP39F5_RX "MCP39F5 Rx" +#define D_SENSOR_MCP39F5_RST "MCP39F5 Rst" +#define D_SENSOR_CSE7766_TX "CSE7766 Tx" +#define D_SENSOR_CSE7766_RX "CSE7766 Rx" +#define D_SENSOR_PN532_TX "PN532 Tx" +#define D_SENSOR_PN532_RX "PN532 Rx" +#define D_SENSOR_SM16716_CLK "SM16716 CLK" +#define D_SENSOR_SM16716_DAT "SM16716 DAT" +#define D_SENSOR_SM16716_POWER "SM16716 PWR" +#define D_SENSOR_MY92X1_DI "MY92x1 DI" +#define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI" +#define D_SENSOR_ARIRFRCV "ALux IrRcv" +#define D_SENSOR_TXD "Serial Tx" +#define D_SENSOR_RXD "Serial Rx" +#define D_SENSOR_ROTARY "Rotary" // Suffix "1A" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/es-AR.h b/sonoff/language/es-AR.h index 435f230ad..9f2187bbf 100644 --- a/sonoff/language/es-AR.h +++ b/sonoff/language/es-AR.h @@ -1,7 +1,7 @@ /* es-AR.h - localization for Spanish - Argentina for Sonoff-Tasmota - Copyright (C) 2018 Adrian Scillato + Copyright (C) 2019 Adrian Scillato This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -148,6 +148,7 @@ #define D_STOP "Detener" #define D_SUBNET_MASK "Máscara Subred" #define D_SUBSCRIBE_TO "Suscribir a" +#define D_UNSUBSCRIBE_FROM "Unsubscribe from" #define D_SUCCESSFUL "Exitosa" #define D_SUNRISE "Salida del Sol" #define D_SUNSET "Puesta del Sol" @@ -253,6 +254,7 @@ #define D_MODULE_PARAMETERS "Parámetros del módulo" #define D_MODULE_TYPE "Tipo de módulo" +#define D_PULLUP_ENABLE "Botón/Llave sin pull-up" #define D_GPIO "GPIO" #define D_SERIAL_IN "Serial In" #define D_SERIAL_OUT "Serial Out" @@ -287,6 +289,8 @@ #define D_TELEMETRY_PERIOD "Período de Telemetría" #define D_OTHER_PARAMETERS "Otros parámetros" +#define D_TEMPLATE "Template" +#define D_ACTIVATE "Activate" #define D_WEB_ADMIN_PASSWORD "Clave Administrador Web" #define D_MQTT_ENABLE "Habilitar MQTT" #define D_FRIENDLY_NAME "Nombre Amigable" @@ -295,6 +299,14 @@ #define D_SINGLE_DEVICE "dispositivo simple" #define D_MULTI_DEVICE "dispositivo múltiple" +#define D_CONFIGURE_TEMPLATE "Configure Template" +#define D_TEMPLATE_PARAMETERS "Template parameters" +#define D_TEMPLATE_NAME "Name" +#define D_BASE_TYPE "Based on" +#define D_TEMPLATE_FLAGS "Options" +#define D_ALLOW_ADC0 "ADC0 input" +#define D_ALLOW_PULLUP "User pull-up selection" + #define D_SAVE_CONFIGURATION "Grabar configuración" #define D_CONFIGURATION_SAVED "Configuración grabada" #define D_CONFIGURATION_RESET "Configuración restablecida" @@ -462,7 +474,7 @@ #define D_HX_CAL_REFERENCE "Poner Peso de Referencia" #define D_HX_CAL_DONE "Calibrado" #define D_HX_CAL_FAIL "Falló Calibración" -#define D_RESET_HX711 "Restableces Escala" +#define D_RESET_HX711 "Restablecer Escala" #define D_CONFIGURE_HX711 "Configurar Escala" #define D_HX711_PARAMETERS "Parámetros de Escala" #define D_ITEM_WEIGHT "Peso" @@ -480,66 +492,90 @@ #define D_TX20_SOUTH "S" #define D_TX20_WEST "O" -// sonoff_template.h -#define D_SENSOR_NONE "Ninguno" -#define D_SENSOR_DHT11 "DHT11" -#define D_SENSOR_AM2301 "AM2301" -#define D_SENSOR_SI7021 "SI7021" -#define D_SENSOR_DS18X20 "DS18x20" -#define D_SENSOR_I2C_SCL "I2C SCL" -#define D_SENSOR_I2C_SDA "I2C SDA" -#define D_SENSOR_WS2812 "WS2812" -#define D_SENSOR_DFR562 "MP3 Player" -#define D_SENSOR_IRSEND "IR TX" -#define D_SENSOR_SWITCH "Llave" // Suffix "1" -#define D_SENSOR_BUTTON "Botón" // Suffix "1" -#define D_SENSOR_RELAY "Relé" // Suffix "1i" -#define D_SENSOR_LED "Led" // Suffix "1i" -#define D_SENSOR_PWM "PWM" // Suffix "1" -#define D_SENSOR_COUNTER "Contador" // Suffix "1" -#define D_SENSOR_IRRECV "IR RX" -#define D_SENSOR_MHZ_RX "MHZ Rx" -#define D_SENSOR_MHZ_TX "MHZ Tx" -#define D_SENSOR_PZEM004_RX "PZEM004 Rx" -#define D_SENSOR_PZEM016_RX "PZEM016 Rx" -#define D_SENSOR_PZEM017_RX "PZEM017 Rx" -#define D_SENSOR_PZEM0XX_TX "PZEM0XX Tx" -#define D_SENSOR_SAIR_RX "SAir Rx" -#define D_SENSOR_SAIR_TX "SAir Tx" -#define D_SENSOR_SPI_CS "SPI CS" -#define D_SENSOR_SPI_DC "SPI DC" -#define D_SENSOR_BACKLIGHT "BkLight" -#define D_SENSOR_PMS5003 "PMS5003" -#define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" -#define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" -#define D_SENSOR_SBR_RX "SerBr Rx" -#define D_SENSOR_SBR_TX "SerBr Tx" -#define D_SENSOR_SR04_TRIG "SR04 Tri" -#define D_SENSOR_SR04_ECHO "SR04 Ech" -#define D_SENSOR_SDM120_TX "SDM120/220 Tx" -#define D_SENSOR_SDM120_RX "SDM120/220 Rx" -#define D_SENSOR_SDM630_TX "SDM630 Tx" -#define D_SENSOR_SDM630_RX "SDM630 Rx" -#define D_SENSOR_TM1638_CLK "TM16 CLK" -#define D_SENSOR_TM1638_DIO "TM16 DIO" -#define D_SENSOR_TM1638_STB "TM16 STB" -#define D_SENSOR_HX711_SCK "HX711 SCK" -#define D_SENSOR_HX711_DAT "HX711 DAT" -#define D_SENSOR_TX20_TX "TX20" -#define D_SENSOR_RFSEND "RFSend" -#define D_SENSOR_RFRECV "RFrecv" -#define D_SENSOR_TUYA_TX "Tuya Tx" -#define D_SENSOR_TUYA_RX "Tuya Rx" -#define D_SENSOR_MGC3130_XFER "MGC3130 Xfer" -#define D_SENSOR_MGC3130_RESET "MGC3130 Reset" -#define D_SENSOR_SSPI_MISO "SSPI MISO" -#define D_SENSOR_SSPI_MOSI "SSPI MOSI" -#define D_SENSOR_SSPI_SCLK "SSPI SCLK" -#define D_SENSOR_SSPI_CS "SSPI CS" -#define D_SENSOR_SSPI_DC "SSPI DC" -#define D_SENSOR_RF_SENSOR "RF Sensor" -#define D_SENSOR_AZ_RX "AZ Rx" -#define D_SENSOR_AZ_TX "AZ Tx" +// sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box +#define D_SENSOR_NONE "Ninguno" +#define D_SENSOR_USER "User" +#define D_SENSOR_DHT11 "DHT11" +#define D_SENSOR_AM2301 "AM2301" +#define D_SENSOR_SI7021 "SI7021" +#define D_SENSOR_DS18X20 "DS18x20" +#define D_SENSOR_I2C_SCL "I2C SCL" +#define D_SENSOR_I2C_SDA "I2C SDA" +#define D_SENSOR_WS2812 "WS2812" +#define D_SENSOR_DFR562 "MP3 Player" +#define D_SENSOR_IRSEND "IR Tx" +#define D_SENSOR_SWITCH "Llave" // Suffix "1" +#define D_SENSOR_BUTTON "Botón" // Suffix "1" +#define D_SENSOR_RELAY "Relé" // Suffix "1i" +#define D_SENSOR_LED "Led" // Suffix "1i" +#define D_SENSOR_PWM "PWM" // Suffix "1" +#define D_SENSOR_COUNTER "Contador" // Suffix "1" +#define D_SENSOR_IRRECV "IR Rx" +#define D_SENSOR_MHZ_RX "MHZ Rx" +#define D_SENSOR_MHZ_TX "MHZ Tx" +#define D_SENSOR_PZEM004_RX "PZEM004 Rx" +#define D_SENSOR_PZEM016_RX "PZEM016 Rx" +#define D_SENSOR_PZEM017_RX "PZEM017 Rx" +#define D_SENSOR_PZEM0XX_TX "PZEM0XX Tx" +#define D_SENSOR_SAIR_RX "SAir Rx" +#define D_SENSOR_SAIR_TX "SAir Tx" +#define D_SENSOR_SPI_CS "SPI CS" +#define D_SENSOR_SPI_DC "SPI DC" +#define D_SENSOR_BACKLIGHT "BkLight" +#define D_SENSOR_PMS5003 "PMS5003" +#define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" +#define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" +#define D_SENSOR_SBR_RX "SerBr Rx" +#define D_SENSOR_SBR_TX "SerBr Tx" +#define D_SENSOR_SR04_TRIG "SR04 Tri" +#define D_SENSOR_SR04_ECHO "SR04 Ech" +#define D_SENSOR_SDM120_TX "SDMx20 Tx" +#define D_SENSOR_SDM120_RX "SDMx20 Rx" +#define D_SENSOR_SDM630_TX "SDM630 Tx" +#define D_SENSOR_SDM630_RX "SDM630 Rx" +#define D_SENSOR_TM1638_CLK "TM16 CLK" +#define D_SENSOR_TM1638_DIO "TM16 DIO" +#define D_SENSOR_TM1638_STB "TM16 STB" +#define D_SENSOR_HX711_SCK "HX711 SCK" +#define D_SENSOR_HX711_DAT "HX711 DAT" +#define D_SENSOR_TX20_TX "TX20" +#define D_SENSOR_RFSEND "RFSend" +#define D_SENSOR_RFRECV "RFrecv" +#define D_SENSOR_TUYA_TX "Tuya Tx" +#define D_SENSOR_TUYA_RX "Tuya Rx" +#define D_SENSOR_MGC3130_XFER "MGC3130 Xfr" +#define D_SENSOR_MGC3130_RESET "MGC3130 Rst" +#define D_SENSOR_SSPI_MISO "SSPI MISO" +#define D_SENSOR_SSPI_MOSI "SSPI MOSI" +#define D_SENSOR_SSPI_SCLK "SSPI SCLK" +#define D_SENSOR_SSPI_CS "SSPI CS" +#define D_SENSOR_SSPI_DC "SSPI DC" +#define D_SENSOR_RF_SENSOR "RF Sensor" +#define D_SENSOR_AZ_RX "AZ Rx" +#define D_SENSOR_AZ_TX "AZ Tx" +#define D_SENSOR_MAX31855_CS "MX31855 CS" +#define D_SENSOR_MAX31855_CLK "MX31855 CLK" +#define D_SENSOR_MAX31855_DO "MX31855 DO" +#define D_SENSOR_NRG_SEL "HLWBL SEL" // Suffix "i" +#define D_SENSOR_NRG_CF1 "HLWBL CF1" +#define D_SENSOR_HLW_CF "HLW8012 CF" +#define D_SENSOR_HJL_CF "BL0937 CF" +#define D_SENSOR_MCP39F5_TX "MCP39F5 Tx" +#define D_SENSOR_MCP39F5_RX "MCP39F5 Rx" +#define D_SENSOR_MCP39F5_RST "MCP39F5 Rst" +#define D_SENSOR_CSE7766_TX "CSE7766 Tx" +#define D_SENSOR_CSE7766_RX "CSE7766 Rx" +#define D_SENSOR_PN532_TX "PN532 Tx" +#define D_SENSOR_PN532_RX "PN532 Rx" +#define D_SENSOR_SM16716_CLK "SM16716 CLK" +#define D_SENSOR_SM16716_DAT "SM16716 DAT" +#define D_SENSOR_SM16716_POWER "SM16716 PWR" +#define D_SENSOR_MY92X1_DI "MY92x1 DI" +#define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI" +#define D_SENSOR_ARIRFRCV "ALux IrRcv" +#define D_SENSOR_TXD "Serial Tx" +#define D_SENSOR_RXD "Serial Rx" +#define D_SENSOR_ROTARY "Rotary" // Suffix "1A" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/fr-FR.h b/sonoff/language/fr-FR.h index 0638b408a..6b26a6212 100644 --- a/sonoff/language/fr-FR.h +++ b/sonoff/language/fr-FR.h @@ -1,7 +1,7 @@ /* fr-FR.h - localization for French - France for Sonoff-Tasmota - Copyright (C) 2018 Olivier Francais + Copyright (C) 2019 Olivier Francais This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -28,7 +28,7 @@ * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. * Use online command Prefix to translate cmnd, stat and tele. * - * Updated until v6.3.0.17 + * Updated until v6.4.1.18 \*********************************************************************/ #define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) @@ -98,8 +98,8 @@ #define D_GAS "Gaz" #define D_GATEWAY "Passerelle" #define D_GROUP "Groupe" -#define D_HOST "Host" -#define D_HOSTNAME "Hostname" +#define D_HOST "Hôte" +#define D_HOSTNAME "Nom d'Hôte" #define D_HUMIDITY "Humidité" #define D_ILLUMINANCE "Éclairement" #define D_IMMEDIATE "immédiat" // Button immediate @@ -138,7 +138,7 @@ #define D_RESTARTING "Redémarre" #define D_RESTART_REASON "Raison du redémarrage" #define D_RESTORE "restaurer" -#define D_RETAINED "retenu" +#define D_RETAINED "persistant" // MQTT #define D_RULE "Règle" #define D_SAVE "Enregistrer" #define D_SENSOR "Capteur" @@ -147,7 +147,8 @@ #define D_STD_TIME "STD" #define D_STOP "Stop" #define D_SUBNET_MASK "Masque sous-réseau" -#define D_SUBSCRIBE_TO "Souscrire à" +#define D_SUBSCRIBE_TO "S'abonner à" +#define D_UNSUBSCRIBE_FROM "Se désabonner de" #define D_SUCCESSFUL "Réussi" #define D_SUNRISE "Lever du jour" #define D_SUNSET "Tombée de la nuit" @@ -184,7 +185,7 @@ #define D_LEVEL_10 "level 1-0" #define D_LEVEL_01 "level 0-1" #define D_SERIAL_LOGGING_DISABLED "Journalisation série désactivée" -#define D_SYSLOG_LOGGING_REENABLED "Jounalisation syslog réactivée" +#define D_SYSLOG_LOGGING_REENABLED "Jounalisation SysLog réactivée" #define D_SET_BAUDRATE_TO "Définir le débit à" #define D_RECEIVED_TOPIC "Topic reçu" // Terme MQTT @@ -209,7 +210,7 @@ #define D_QUERY_DONE "Requête terminée. Services MQTT trouvés" #define D_MQTT_SERVICE_FOUND "Service MQTT trouvé sur" #define D_FOUND_AT "trouvé à" -#define D_SYSLOG_HOST_NOT_FOUND "Host syslog introuvable" +#define D_SYSLOG_HOST_NOT_FOUND "Hôte SysLog introuvable" // settings.ino #define D_SAVED_TO_FLASH_AT "Enregistré en flash à" @@ -253,6 +254,7 @@ #define D_MODULE_PARAMETERS "Paramètres module" #define D_MODULE_TYPE "Type de module" +#define D_PULLUP_ENABLE "Inter. sans pull-up" #define D_GPIO "GPIO" #define D_SERIAL_IN "Entrée série" #define D_SERIAL_OUT "Sortie série" @@ -280,13 +282,15 @@ #define D_LOGGING_PARAMETERS "Paramètres du journal" #define D_SERIAL_LOG_LEVEL "Niveau de journalisation série" #define D_WEB_LOG_LEVEL "Niveau de journalisation web" -#define D_SYS_LOG_LEVEL "Niveau Syslog" +#define D_SYS_LOG_LEVEL "Niveau SysLog" #define D_MORE_DEBUG "Plus de debug" -#define D_SYSLOG_HOST "Hôte Syslog" -#define D_SYSLOG_PORT "Port Syslog" +#define D_SYSLOG_HOST "Hôte SysLog" +#define D_SYSLOG_PORT "Port SysLog" #define D_TELEMETRY_PERIOD "Période télémétrie" #define D_OTHER_PARAMETERS "Autres paramètres" +#define D_TEMPLATE "Modèle" +#define D_ACTIVATE "Activer" #define D_WEB_ADMIN_PASSWORD "Mot de passe Web Admin" #define D_MQTT_ENABLE "MQTT activé" #define D_FRIENDLY_NAME "Surnom" @@ -295,6 +299,14 @@ #define D_SINGLE_DEVICE "module unique" #define D_MULTI_DEVICE "multi module" +#define D_CONFIGURE_TEMPLATE "Configuration du modèle" +#define D_TEMPLATE_PARAMETERS "Paramètres du modèle" +#define D_TEMPLATE_NAME "Nom" +#define D_BASE_TYPE "Basé sur" +#define D_TEMPLATE_FLAGS "Options" +#define D_ALLOW_ADC0 "Entrée ADC0" +#define D_ALLOW_PULLUP "Pull-up utilisateur" + #define D_SAVE_CONFIGURATION "Enregistrer la configuration" #define D_CONFIGURATION_SAVED "Configuration enregistrée" #define D_CONFIGURATION_RESET "Configuration réinitialisée" @@ -330,7 +342,7 @@ #define D_UPLOAD_ERR_3 "L'octet magique n'est pas 0xE9" #define D_UPLOAD_ERR_4 "La taille du programme à flasher est plus grande que la taille réelle de la mémoire flash" #define D_UPLOAD_ERR_5 "Erreur de comparaison du buffer de téléchargement" -#define D_UPLOAD_ERR_6 "Téléchargement échoué. Activer Weblog 3" +#define D_UPLOAD_ERR_6 "Téléchargement échoué. Activer WebLog 3" #define D_UPLOAD_ERR_7 "Téléchargement annulé" #define D_UPLOAD_ERR_8 "Fichier invalide" #define D_UPLOAD_ERR_9 "Fichier trop grand" @@ -341,7 +353,7 @@ #define D_UPLOAD_ERROR_CODE "Code d'erreur téléchargement" #define D_ENTER_COMMAND "Saisir une commande" -#define D_ENABLE_WEBLOG_FOR_RESPONSE "Activer Weblog 2 si une réponse est attendue" +#define D_ENABLE_WEBLOG_FOR_RESPONSE "Activer WebLog 2 si une réponse est attendue" #define D_NEED_USER_AND_PASSWORD "Nécessite utilisateur=&password=" // xdrv_01_mqtt.ino @@ -480,66 +492,90 @@ #define D_TX20_SOUTH "S" #define D_TX20_WEST "O" -// sonoff_template.h -#define D_SENSOR_NONE "Aucun" -#define D_SENSOR_DHT11 "DHT11" -#define D_SENSOR_AM2301 "AM2301" -#define D_SENSOR_SI7021 "SI7021" -#define D_SENSOR_DS18X20 "DS18x20" -#define D_SENSOR_I2C_SCL "I2C SCL" -#define D_SENSOR_I2C_SDA "I2C SDA" -#define D_SENSOR_WS2812 "WS2812" -#define D_SENSOR_DFR562 "MP3 Player" -#define D_SENSOR_IRSEND "ÉmetIR" -#define D_SENSOR_SWITCH "Inter." // Suffix "1" -#define D_SENSOR_BUTTON "Bouton" // Suffix "1" -#define D_SENSOR_RELAY "Relais" // Suffix "1i" -#define D_SENSOR_LED "LED" // Suffix "1i" -#define D_SENSOR_PWM "PWM" // Suffix "1" -#define D_SENSOR_COUNTER "Compteur" // Suffix "1" -#define D_SENSOR_IRRECV "RécptIR" -#define D_SENSOR_MHZ_RX "MHZ Rx" -#define D_SENSOR_MHZ_TX "MHZ Tx" -#define D_SENSOR_PZEM004_RX "PZEM004 Rx" -#define D_SENSOR_PZEM016_RX "PZEM016 Rx" -#define D_SENSOR_PZEM017_RX "PZEM017 Rx" -#define D_SENSOR_PZEM0XX_TX "PZEM0XX Tx" -#define D_SENSOR_SAIR_RX "SAir Rx" -#define D_SENSOR_SAIR_TX "SAir Tx" -#define D_SENSOR_SPI_CS "SPI CS" -#define D_SENSOR_SPI_DC "SPI DC" -#define D_SENSOR_BACKLIGHT "RétroÉcl" -#define D_SENSOR_PMS5003 "PMS5003" -#define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" -#define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" -#define D_SENSOR_SBR_RX "SerBr Rx" -#define D_SENSOR_SBR_TX "SerBr Tx" -#define D_SENSOR_SR04_TRIG "SR04 Tri" -#define D_SENSOR_SR04_ECHO "SR04 Ech" -#define D_SENSOR_SDM120_TX "SDM120/220 Tx" -#define D_SENSOR_SDM120_RX "SDM120/220 Rx" -#define D_SENSOR_SDM630_TX "SDM630 Tx" -#define D_SENSOR_SDM630_RX "SDM630 Rx" -#define D_SENSOR_TM1638_CLK "TM16 CLK" -#define D_SENSOR_TM1638_DIO "TM16 DIO" -#define D_SENSOR_TM1638_STB "TM16 STB" -#define D_SENSOR_HX711_SCK "HX711 SCK" -#define D_SENSOR_HX711_DAT "HX711 DAT" -#define D_SENSOR_TX20_TX "TX20" -#define D_SENSOR_RFSEND "RFSend" -#define D_SENSOR_RFRECV "RFrecv" -#define D_SENSOR_TUYA_TX "Tuya Tx" -#define D_SENSOR_TUYA_RX "Tuya Rx" -#define D_SENSOR_MGC3130_XFER "MGC3130 Xfer" -#define D_SENSOR_MGC3130_RESET "MGC3130 Reset" -#define D_SENSOR_SSPI_MISO "SSPI MISO" -#define D_SENSOR_SSPI_MOSI "SSPI MOSI" -#define D_SENSOR_SSPI_SCLK "SSPI SCLK" -#define D_SENSOR_SSPI_CS "SSPI CS" -#define D_SENSOR_SSPI_DC "SSPI DC" -#define D_SENSOR_RF_SENSOR "RF Sensor" -#define D_SENSOR_AZ_RX "AZ Rx" -#define D_SENSOR_AZ_TX "AZ Tx" +// sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box +#define D_SENSOR_NONE "Aucun" +#define D_SENSOR_USER "Utilisateur" +#define D_SENSOR_DHT11 "DHT11" +#define D_SENSOR_AM2301 "AM2301" +#define D_SENSOR_SI7021 "SI7021" +#define D_SENSOR_DS18X20 "DS18x20" +#define D_SENSOR_I2C_SCL "I2C SCL" +#define D_SENSOR_I2C_SDA "I2C SDA" +#define D_SENSOR_WS2812 "WS2812" +#define D_SENSOR_DFR562 "MP3 Player" +#define D_SENSOR_IRSEND "ÉmetIR" +#define D_SENSOR_SWITCH "Inter." // Suffix "1" +#define D_SENSOR_BUTTON "Bouton" // Suffix "1" +#define D_SENSOR_RELAY "Relais" // Suffix "1i" +#define D_SENSOR_LED "LED" // Suffix "1i" +#define D_SENSOR_PWM "PWM" // Suffix "1" +#define D_SENSOR_COUNTER "Compteur" // Suffix "1" +#define D_SENSOR_IRRECV "RécptIR" +#define D_SENSOR_MHZ_RX "MHZ Rx" +#define D_SENSOR_MHZ_TX "MHZ Tx" +#define D_SENSOR_PZEM004_RX "PZEM004 Rx" +#define D_SENSOR_PZEM016_RX "PZEM016 Rx" +#define D_SENSOR_PZEM017_RX "PZEM017 Rx" +#define D_SENSOR_PZEM0XX_TX "PZEM0XX Tx" +#define D_SENSOR_SAIR_RX "SAir Rx" +#define D_SENSOR_SAIR_TX "SAir Tx" +#define D_SENSOR_SPI_CS "SPI CS" +#define D_SENSOR_SPI_DC "SPI DC" +#define D_SENSOR_BACKLIGHT "RétroÉcl" +#define D_SENSOR_PMS5003 "PMS5003" +#define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" +#define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" +#define D_SENSOR_SBR_RX "SerBr Rx" +#define D_SENSOR_SBR_TX "SerBr Tx" +#define D_SENSOR_SR04_TRIG "SR04 Tri" +#define D_SENSOR_SR04_ECHO "SR04 Ech" +#define D_SENSOR_SDM120_TX "SDMx20 Tx" +#define D_SENSOR_SDM120_RX "SDMx20 Rx" +#define D_SENSOR_SDM630_TX "SDM630 Tx" +#define D_SENSOR_SDM630_RX "SDM630 Rx" +#define D_SENSOR_TM1638_CLK "TM16 CLK" +#define D_SENSOR_TM1638_DIO "TM16 DIO" +#define D_SENSOR_TM1638_STB "TM16 STB" +#define D_SENSOR_HX711_SCK "HX711 SCK" +#define D_SENSOR_HX711_DAT "HX711 DAT" +#define D_SENSOR_TX20_TX "TX20" +#define D_SENSOR_RFSEND "RFSend" +#define D_SENSOR_RFRECV "RFrecv" +#define D_SENSOR_TUYA_TX "Tuya Tx" +#define D_SENSOR_TUYA_RX "Tuya Rx" +#define D_SENSOR_MGC3130_XFER "MGC3130 Xfr" +#define D_SENSOR_MGC3130_RESET "MGC3130 Rst" +#define D_SENSOR_SSPI_MISO "SSPI MISO" +#define D_SENSOR_SSPI_MOSI "SSPI MOSI" +#define D_SENSOR_SSPI_SCLK "SSPI SCLK" +#define D_SENSOR_SSPI_CS "SSPI CS" +#define D_SENSOR_SSPI_DC "SSPI DC" +#define D_SENSOR_RF_SENSOR "RF Sensor" +#define D_SENSOR_AZ_RX "AZ Rx" +#define D_SENSOR_AZ_TX "AZ Tx" +#define D_SENSOR_MAX31855_CS "MX31855 CS" +#define D_SENSOR_MAX31855_CLK "MX31855 CLK" +#define D_SENSOR_MAX31855_DO "MX31855 DO" +#define D_SENSOR_NRG_SEL "HLWBL SEL" // Suffix "i" +#define D_SENSOR_NRG_CF1 "HLWBL CF1" +#define D_SENSOR_HLW_CF "HLW8012 CF" +#define D_SENSOR_HJL_CF "BL0937 CF" +#define D_SENSOR_MCP39F5_TX "MCP39F5 Tx" +#define D_SENSOR_MCP39F5_RX "MCP39F5 Rx" +#define D_SENSOR_MCP39F5_RST "MCP39F5 Rst" +#define D_SENSOR_CSE7766_TX "CSE7766 Tx" +#define D_SENSOR_CSE7766_RX "CSE7766 Rx" +#define D_SENSOR_PN532_TX "PN532 Tx" +#define D_SENSOR_PN532_RX "PN532 Rx" +#define D_SENSOR_SM16716_CLK "SM16716 CLK" +#define D_SENSOR_SM16716_DAT "SM16716 DAT" +#define D_SENSOR_SM16716_POWER "SM16716 PWR" +#define D_SENSOR_MY92X1_DI "MY92x1 DI" +#define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI" +#define D_SENSOR_ARIRFRCV "ALux IrRcv" +#define D_SENSOR_TXD "Serial Tx" +#define D_SENSOR_RXD "Serial Rx" +#define D_SENSOR_ROTARY "Rotary" // Suffix "1A" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/he-HE.h b/sonoff/language/he-HE.h index cd1b0e92e..9f3458889 100644 --- a/sonoff/language/he-HE.h +++ b/sonoff/language/he-HE.h @@ -1,7 +1,7 @@ /* he-HE.h - localization for Hebrew - Israel for Sonoff-Tasmota - Copyright (C) 2018 Yuval Mejahez + Copyright (C) 2019 Yuval Mejahez This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -148,6 +148,7 @@ #define D_STOP "עצירה" #define D_SUBNET_MASK "רשת מסכת משנה" #define D_SUBSCRIBE_TO "הרשם ל" +#define D_UNSUBSCRIBE_FROM "Unsubscribe from" #define D_SUCCESSFUL "הצליח" #define D_SUNRISE "זריחה" #define D_SUNSET "שקיעה" @@ -253,6 +254,7 @@ #define D_MODULE_PARAMETERS "מודול פרמטרים" #define D_MODULE_TYPE "סוג מודול" +#define D_PULLUP_ENABLE "No Button/Switch pull-up" #define D_GPIO " רגל " #define D_SERIAL_IN "כניסת סריאל" #define D_SERIAL_OUT "יציאת סריאל" @@ -287,6 +289,8 @@ #define D_TELEMETRY_PERIOD "Telemetry period" #define D_OTHER_PARAMETERS "פרמטרים שונים" +#define D_TEMPLATE "Template" +#define D_ACTIVATE "Activate" #define D_WEB_ADMIN_PASSWORD "סיסמת מנהל - אתר" #define D_MQTT_ENABLE "MQTT אפשר" #define D_FRIENDLY_NAME "שם ידידותי" @@ -295,6 +299,14 @@ #define D_SINGLE_DEVICE "התקן בודד" #define D_MULTI_DEVICE "התקנים" +#define D_CONFIGURE_TEMPLATE "Configure Template" +#define D_TEMPLATE_PARAMETERS "Template parameters" +#define D_TEMPLATE_NAME "Name" +#define D_BASE_TYPE "Based on" +#define D_TEMPLATE_FLAGS "Options" +#define D_ALLOW_ADC0 "ADC0 input" +#define D_ALLOW_PULLUP "User pull-up selection" + #define D_SAVE_CONFIGURATION "שמירת הגדרות" #define D_CONFIGURATION_SAVED "הגדרות נשמרו" #define D_CONFIGURATION_RESET "איפוס הגדרות" @@ -480,66 +492,90 @@ #define D_TX20_SOUTH "S" #define D_TX20_WEST "W" -// sonoff_template.h -#define D_SENSOR_NONE "None" -#define D_SENSOR_DHT11 "DHT11" -#define D_SENSOR_AM2301 "AM2301" -#define D_SENSOR_SI7021 "SI7021" -#define D_SENSOR_DS18X20 "DS18x20" -#define D_SENSOR_I2C_SCL "I2C SCL" -#define D_SENSOR_I2C_SDA "I2C SDA" -#define D_SENSOR_WS2812 "WS2812" -#define D_SENSOR_DFR562 "MP3 Player" -#define D_SENSOR_IRSEND "IRsend" -#define D_SENSOR_SWITCH "מתג" // Suffix "1" -#define D_SENSOR_BUTTON "לחצן" // Suffix "1" -#define D_SENSOR_RELAY "ממסר" // Suffix "1i" -#define D_SENSOR_LED "לד" // Suffix "1i" -#define D_SENSOR_PWM "PWM" // Suffix "1" -#define D_SENSOR_COUNTER "מונה" // Suffix "1" -#define D_SENSOR_IRRECV "IRrecv" -#define D_SENSOR_MHZ_RX "MHZ Rx" -#define D_SENSOR_MHZ_TX "MHZ Tx" -#define D_SENSOR_PZEM004_RX "PZEM004 Rx" -#define D_SENSOR_PZEM016_RX "PZEM016 Rx" -#define D_SENSOR_PZEM017_RX "PZEM017 Rx" -#define D_SENSOR_PZEM0XX_TX "PZEM0XX Tx" -#define D_SENSOR_SAIR_RX "SAir Rx" -#define D_SENSOR_SAIR_TX "SAir Tx" -#define D_SENSOR_SPI_CS "SPI CS" -#define D_SENSOR_SPI_DC "SPI DC" -#define D_SENSOR_BACKLIGHT "BkLight" -#define D_SENSOR_PMS5003 "PMS5003" -#define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" -#define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" -#define D_SENSOR_SBR_RX "SerBr Rx" -#define D_SENSOR_SBR_TX "SerBr Tx" -#define D_SENSOR_SR04_TRIG "SR04 Tri" -#define D_SENSOR_SR04_ECHO "SR04 Ech" -#define D_SENSOR_SDM120_TX "SDM120/220 Tx" -#define D_SENSOR_SDM120_RX "SDM120/220 Rx" -#define D_SENSOR_SDM630_TX "SDM630 Tx" -#define D_SENSOR_SDM630_RX "SDM630 Rx" -#define D_SENSOR_TM1638_CLK "TM16 CLK" -#define D_SENSOR_TM1638_DIO "TM16 DIO" -#define D_SENSOR_TM1638_STB "TM16 STB" -#define D_SENSOR_HX711_SCK "HX711 SCK" -#define D_SENSOR_HX711_DAT "HX711 DAT" -#define D_SENSOR_TX20_TX "TX20" -#define D_SENSOR_RFSEND "RFSend" -#define D_SENSOR_RFRECV "RFrecv" -#define D_SENSOR_TUYA_TX "Tuya Tx" -#define D_SENSOR_TUYA_RX "Tuya Rx" -#define D_SENSOR_MGC3130_XFER "MGC3130 Xfer" -#define D_SENSOR_MGC3130_RESET "MGC3130 Reset" -#define D_SENSOR_SSPI_MISO "SSPI MISO" -#define D_SENSOR_SSPI_MOSI "SSPI MOSI" -#define D_SENSOR_SSPI_SCLK "SSPI SCLK" -#define D_SENSOR_SSPI_CS "SSPI CS" -#define D_SENSOR_SSPI_DC "SSPI DC" -#define D_SENSOR_RF_SENSOR "RF Sensor" -#define D_SENSOR_AZ_RX "AZ Rx" -#define D_SENSOR_AZ_TX "AZ Tx" +// sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box +#define D_SENSOR_NONE "None" +#define D_SENSOR_USER "User" +#define D_SENSOR_DHT11 "DHT11" +#define D_SENSOR_AM2301 "AM2301" +#define D_SENSOR_SI7021 "SI7021" +#define D_SENSOR_DS18X20 "DS18x20" +#define D_SENSOR_I2C_SCL "I2C SCL" +#define D_SENSOR_I2C_SDA "I2C SDA" +#define D_SENSOR_WS2812 "WS2812" +#define D_SENSOR_DFR562 "MP3 Player" +#define D_SENSOR_IRSEND "IRsend" +#define D_SENSOR_SWITCH "מתג" // Suffix "1" +#define D_SENSOR_BUTTON "לחצן" // Suffix "1" +#define D_SENSOR_RELAY "ממסר" // Suffix "1i" +#define D_SENSOR_LED "לד" // Suffix "1i" +#define D_SENSOR_PWM "PWM" // Suffix "1" +#define D_SENSOR_COUNTER "מונה" // Suffix "1" +#define D_SENSOR_IRRECV "IRrecv" +#define D_SENSOR_MHZ_RX "MHZ Rx" +#define D_SENSOR_MHZ_TX "MHZ Tx" +#define D_SENSOR_PZEM004_RX "PZEM004 Rx" +#define D_SENSOR_PZEM016_RX "PZEM016 Rx" +#define D_SENSOR_PZEM017_RX "PZEM017 Rx" +#define D_SENSOR_PZEM0XX_TX "PZEM0XX Tx" +#define D_SENSOR_SAIR_RX "SAir Rx" +#define D_SENSOR_SAIR_TX "SAir Tx" +#define D_SENSOR_SPI_CS "SPI CS" +#define D_SENSOR_SPI_DC "SPI DC" +#define D_SENSOR_BACKLIGHT "BkLight" +#define D_SENSOR_PMS5003 "PMS5003" +#define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" +#define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" +#define D_SENSOR_SBR_RX "SerBr Rx" +#define D_SENSOR_SBR_TX "SerBr Tx" +#define D_SENSOR_SR04_TRIG "SR04 Tri" +#define D_SENSOR_SR04_ECHO "SR04 Ech" +#define D_SENSOR_SDM120_TX "SDMx20 Tx" +#define D_SENSOR_SDM120_RX "SDMx20 Rx" +#define D_SENSOR_SDM630_TX "SDM630 Tx" +#define D_SENSOR_SDM630_RX "SDM630 Rx" +#define D_SENSOR_TM1638_CLK "TM16 CLK" +#define D_SENSOR_TM1638_DIO "TM16 DIO" +#define D_SENSOR_TM1638_STB "TM16 STB" +#define D_SENSOR_HX711_SCK "HX711 SCK" +#define D_SENSOR_HX711_DAT "HX711 DAT" +#define D_SENSOR_TX20_TX "TX20" +#define D_SENSOR_RFSEND "RFSend" +#define D_SENSOR_RFRECV "RFrecv" +#define D_SENSOR_TUYA_TX "Tuya Tx" +#define D_SENSOR_TUYA_RX "Tuya Rx" +#define D_SENSOR_MGC3130_XFER "MGC3130 Xfr" +#define D_SENSOR_MGC3130_RESET "MGC3130 Rst" +#define D_SENSOR_SSPI_MISO "SSPI MISO" +#define D_SENSOR_SSPI_MOSI "SSPI MOSI" +#define D_SENSOR_SSPI_SCLK "SSPI SCLK" +#define D_SENSOR_SSPI_CS "SSPI CS" +#define D_SENSOR_SSPI_DC "SSPI DC" +#define D_SENSOR_RF_SENSOR "RF Sensor" +#define D_SENSOR_AZ_RX "AZ Rx" +#define D_SENSOR_AZ_TX "AZ Tx" +#define D_SENSOR_MAX31855_CS "MX31855 CS" +#define D_SENSOR_MAX31855_CLK "MX31855 CLK" +#define D_SENSOR_MAX31855_DO "MX31855 DO" +#define D_SENSOR_NRG_SEL "HLWBL SEL" // Suffix "i" +#define D_SENSOR_NRG_CF1 "HLWBL CF1" +#define D_SENSOR_HLW_CF "HLW8012 CF" +#define D_SENSOR_HJL_CF "BL0937 CF" +#define D_SENSOR_MCP39F5_TX "MCP39F5 Tx" +#define D_SENSOR_MCP39F5_RX "MCP39F5 Rx" +#define D_SENSOR_MCP39F5_RST "MCP39F5 Rst" +#define D_SENSOR_CSE7766_TX "CSE7766 Tx" +#define D_SENSOR_CSE7766_RX "CSE7766 Rx" +#define D_SENSOR_PN532_TX "PN532 Tx" +#define D_SENSOR_PN532_RX "PN532 Rx" +#define D_SENSOR_SM16716_CLK "SM16716 CLK" +#define D_SENSOR_SM16716_DAT "SM16716 DAT" +#define D_SENSOR_SM16716_POWER "SM16716 PWR" +#define D_SENSOR_MY92X1_DI "MY92x1 DI" +#define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI" +#define D_SENSOR_ARIRFRCV "ALux IrRcv" +#define D_SENSOR_TXD "Serial Tx" +#define D_SENSOR_RXD "Serial Rx" +#define D_SENSOR_ROTARY "Rotary" // Suffix "1A" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/hu-HU.h b/sonoff/language/hu-HU.h index 244d55776..649796121 100644 --- a/sonoff/language/hu-HU.h +++ b/sonoff/language/hu-HU.h @@ -1,7 +1,7 @@ /* hu-HU.h - localization for Hungarian in Hungary for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends + Copyright (C) 2019 Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -52,36 +52,36 @@ // Common #define D_ADMIN "Admin" -#define D_AIR_QUALITY "Levegő minőség" +#define D_AIR_QUALITY "Levegőminőség" #define D_AP "AP" // Access Point #define D_AS "mint" #define D_AUTO "AUTO" #define D_BLINK "Villogás" -#define D_BLINKOFF "Villogás Ki" -#define D_BOOT_COUNT "Bootolások száma" +#define D_BLINKOFF "Villogás ki" +#define D_BOOT_COUNT "Újraindulások száma" #define D_BRIGHTLIGHT "Max. fényerő" -#define D_BSSID "BSSId" +#define D_BSSID "BSSID" #define D_BUTTON "Gomb" -#define D_BY "tőle:" // Written by me +#define D_BY "by" // Written by me #define D_BYTES "Byte-ok" #define D_CELSIUS "Celsius" -#define D_CHANNEL "Channel" +#define D_CHANNEL "Csatorna" #define D_CO2 "Szén-dioxid" #define D_CODE "kód" // Button code -#define D_COLDLIGHT "Hideg" +#define D_COLDLIGHT "Hideg fény" #define D_COMMAND "Parancs" -#define D_CONNECTED "Csatlakozva" +#define D_CONNECTED "Csatlakoztatva" #define D_COUNT "Szám" #define D_COUNTER "Számláló" -#define D_CURRENT "Áram" // As in Voltage and Current +#define D_CURRENT "Áramerősség" // As in Voltage and Current #define D_DATA "Adat" #define D_DARKLIGHT "Min. fényerő" #define D_DEBUG "Debug" #define D_DISABLED "Letiltva" -#define D_DISTANCE "Distance" -#define D_DNS_SERVER "DNS Szerver" +#define D_DISTANCE "Távolság" +#define D_DNS_SERVER "DNS szerver" #define D_DONE "Kész" -#define D_DST_TIME "DST" +#define D_DST_TIME "nyári idő" #define D_ECO2 "eCO2" #define D_EMULATION "Emuláció" #define D_ENABLED "Engedélyezve" @@ -90,24 +90,24 @@ #define D_FAHRENHEIT "Fahrenheit" #define D_FAILED "Sikertelen" #define D_FALLBACK "Fallback" -#define D_FALLBACK_TOPIC "Fallback Téma" +#define D_FALLBACK_TOPIC "fallback topik" #define D_FALSE "Hamis" -#define D_FILE "File" -#define D_FREE_MEMORY "Szabad Memória" -#define D_FREQUENCY "Frequency" +#define D_FILE "Fájl" +#define D_FREE_MEMORY "Szabad memória" +#define D_FREQUENCY "Frekvencia" #define D_GAS "Gáz" -#define D_GATEWAY "Gateway" +#define D_GATEWAY "Átjáró" #define D_GROUP "Csoport" -#define D_HOST "Host" -#define D_HOSTNAME "Hostname" +#define D_HOST "Hoszt" +#define D_HOSTNAME "Hosztnév" #define D_HUMIDITY "Páratartalom" #define D_ILLUMINANCE "Megvilágítás" #define D_IMMEDIATE "azonnali" // Button immediate #define D_INDEX "Index" #define D_INFO "Info" -#define D_INFRARED "Infrared" +#define D_INFRARED "Infravörös" #define D_INITIALIZED "Inicializálva" -#define D_IP_ADDRESS "IP Cím" +#define D_IP_ADDRESS "IP cím" #define D_LIGHT "Fény" #define D_LWT "LWT" #define D_MODULE "Modul" @@ -117,89 +117,90 @@ #define D_NONE "nincs" #define D_OFF "Ki" #define D_OFFLINE "Offline" -#define D_OK "Ok" +#define D_OK "OK" #define D_ON "Be" #define D_ONLINE "Online" #define D_PASSWORD "Jelszó" #define D_PORT "Port" -#define D_POWER_FACTOR "Teljesítmény tényező" +#define D_POWER_FACTOR "Teljesítménytényező" #define D_POWERUSAGE "Energiafelhasználás" -#define D_POWERUSAGE_ACTIVE "Active Power" -#define D_POWERUSAGE_APPARENT "Apparent Power" -#define D_POWERUSAGE_REACTIVE "Reactive Power" +#define D_POWERUSAGE_ACTIVE "Aktív teljesítmény" +#define D_POWERUSAGE_APPARENT "Látszólagos teljesítmény" +#define D_POWERUSAGE_REACTIVE "Reaktív teljesítmény" #define D_PRESSURE "Nyomás" #define D_PRESSUREATSEALEVEL "Tengerszinti nyomás" -#define D_PROGRAM_FLASH_SIZE "Program Flash Méret" -#define D_PROGRAM_SIZE "Program Méret" +#define D_PROGRAM_FLASH_SIZE "Program flash méret" +#define D_PROGRAM_SIZE "Program méret" #define D_PROJECT "Projekt" -#define D_RAIN "Rain" +#define D_RAIN "Eső" #define D_RECEIVED "Érkezett" #define D_RESTART "Újraindítás" #define D_RESTARTING "Újraindítás" -#define D_RESTART_REASON "Újraindítás oka:" +#define D_RESTART_REASON "Utolsó újraindulás oka" #define D_RESTORE "Visszaállítás" -#define D_RETAINED "mentve" -#define D_RULE "Rule" +#define D_RETAINED "retained" +#define D_RULE "Szabály" #define D_SAVE "Mentés" #define D_SENSOR "Szenzor" -#define D_SSID "SSId" +#define D_SSID "SSID" #define D_START "" #define D_STD_TIME "STD" #define D_STOP "Leállítás" -#define D_SUBNET_MASK "Subnet Mask" -#define D_SUBSCRIBE_TO "Feliratkozás a" +#define D_SUBNET_MASK "Alhálózati maszk" +#define D_SUBSCRIBE_TO "Feliratkozás a(z)" +#define D_UNSUBSCRIBE_FROM "Unsubscribe from" #define D_SUCCESSFUL "Sikeres" -#define D_SUNRISE "Sunrise" -#define D_SUNSET "Sunset" +#define D_SUNRISE "Napkelte" +#define D_SUNSET "Napnyugta" #define D_TEMPERATURE "Hőmérséklet" #define D_TO "-nak" -#define D_TOGGLE "Toggle" -#define D_TOPIC "Téma" +#define D_TOGGLE "Megfordítás" +#define D_TOPIC "Topic" #define D_TRANSMIT "Továbbít" #define D_TRUE "Igaz" #define D_TVOC "TVOC" -#define D_UPGRADE "frissítés" +#define D_UPGRADE "Frissítés" #define D_UPLOAD "Feltöltés" #define D_UPTIME "Üzemidő" #define D_USER "Felhasználó" #define D_UTC_TIME "UTC" -#define D_UV_INDEX "UV Index" -#define D_UV_INDEX_1 "Low" -#define D_UV_INDEX_2 "Mid" -#define D_UV_INDEX_3 "High" -#define D_UV_INDEX_4 "Danger" -#define D_UV_INDEX_5 "BurnL1/2" -#define D_UV_INDEX_6 "BurnL3" -#define D_UV_INDEX_7 "OoR" -#define D_UV_LEVEL "UV Szint" -#define D_UV_POWER "UV Power" +#define D_UV_INDEX "UV index" +#define D_UV_INDEX_1 "alacsony" +#define D_UV_INDEX_2 "közepes" +#define D_UV_INDEX_3 "magas" +#define D_UV_INDEX_4 "veszélyes" +#define D_UV_INDEX_5 "égés L1/2" +#define D_UV_INDEX_6 "égés L3" +#define D_UV_INDEX_7 "tartományon kívül" +#define D_UV_LEVEL "UV szint" +#define D_UV_POWER "UV teljesítmény" #define D_VERSION "Verzió" #define D_VOLTAGE "Feszültség" -#define D_WEIGHT "Weight" -#define D_WARMLIGHT "Meleg" -#define D_WEB_SERVER "Web Szerver" +#define D_WEIGHT "Tömeg" +#define D_WARMLIGHT "Meleg fény" +#define D_WEB_SERVER "Webszerver" // sonoff.ino -#define D_WARNING_MINIMAL_VERSION "VIGYÁZZ Ez a verzió nem támogat tartós beállításokat" +#define D_WARNING_MINIMAL_VERSION "VIGYÁZZ! Ez a verzió nem támogat tartós beállításokat" #define D_LEVEL_10 "szint 1-0" #define D_LEVEL_01 "szint 0-1" -#define D_SERIAL_LOGGING_DISABLED "Serial logolás kikapcsolva" -#define D_SYSLOG_LOGGING_REENABLED "Syslog logolás újra-engedélyezve" +#define D_SERIAL_LOGGING_DISABLED "Soros naplózás kikapcsolva" +#define D_SYSLOG_LOGGING_REENABLED "Syslog logolás újraengedélyezve" #define D_SET_BAUDRATE_TO "Baudrate beállítása" -#define D_RECEIVED_TOPIC "Érkezett Téma" -#define D_DATA_SIZE "Adat Méret" +#define D_RECEIVED_TOPIC "Érkezett topic" +#define D_DATA_SIZE "Adatméret" #define D_ANALOG_INPUT "Analóg" // support.ino #define D_OSWATCH "osWatch" -#define D_BLOCKED_LOOP "Blocked Loop" +#define D_BLOCKED_LOOP "Tiltott hurok" #define D_WPS_FAILED_WITH_STATUS "WPSconfig SIKERTELEN státusz:" #define D_ACTIVE_FOR_3_MINUTES "aktválás 3 percre" #define D_FAILED_TO_START "sikertelen indítás" #define D_PATCH_ISSUE_2186 "Patch issue 2186" -#define D_CONNECTING_TO_AP "Csatlakozás az (AP): " -#define D_IN_MODE "be mód" +#define D_CONNECTING_TO_AP "AP-hoz csatlakozás:" +#define D_IN_MODE "mód:" #define D_CONNECT_FAILED_NO_IP_ADDRESS "Sikertelen csatlakozás, nincs kiosztott IP cím" #define D_CONNECT_FAILED_AP_NOT_REACHED "Sikertelen csatlakozás, AP nem elérhető" #define D_CONNECT_FAILED_WRONG_PASSWORD "Sikertelen csatlakozás, hibás AP jelszó" @@ -207,150 +208,161 @@ #define D_ATTEMPTING_CONNECTION "Csatlakozás..." #define D_CHECKING_CONNECTION "Kapcsolat ellenőrzése..." #define D_QUERY_DONE "Lekérés kész. MQTT szolgáltatás aktív" -#define D_MQTT_SERVICE_FOUND "élő MQTT szolgáltatás a" -#define D_FOUND_AT "a" -#define D_SYSLOG_HOST_NOT_FOUND "Syslog Host nem található" +#define D_MQTT_SERVICE_FOUND "élő MQTT szolgáltatás a(z)" +#define D_FOUND_AT "a(z)" +#define D_SYSLOG_HOST_NOT_FOUND "Syslog hoszt nem található" // settings.ino -#define D_SAVED_TO_FLASH_AT "Flash-re mentve a" -#define D_LOADED_FROM_FLASH_AT "Flash-ről betöltve a" +#define D_SAVED_TO_FLASH_AT "Flash-re mentve a(z)" +#define D_LOADED_FROM_FLASH_AT "Flash-ről betöltve a(z)" #define D_USE_DEFAULTS "Alapértelmezett beáll. használata" #define D_ERASED_SECTOR "Szektor törlése" // xdrv_02_webserver.ino -#define D_NOSCRIPT "To use Tasmota, please enable JavaScript" -#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "MINIMAL firmware - frissítsd!" -#define D_WEBSERVER_ACTIVE_ON "Web szerver aktív a" -#define D_WITH_IP_ADDRESS "IP címe:" -#define D_WEBSERVER_STOPPED "Webs zerver leállítva" -#define D_FILE_NOT_FOUND "File Nem Található" +#define D_NOSCRIPT "A Tasmota használatához engedélyezd a Javascriptet!" +#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "MINIMÁLIS firmware - frissítsd!" +#define D_WEBSERVER_ACTIVE_ON "Webszerver aktív:" +#define D_WITH_IP_ADDRESS "IP cím:" +#define D_WEBSERVER_STOPPED "Webszerver leállítva" +#define D_FILE_NOT_FOUND "Fájl nem található" #define D_REDIRECTED "Átírányítás captive portálra" -#define D_WIFIMANAGER_SET_ACCESSPOINT_AND_STATION "Wifimanager AccessPoint(AP) és Station(ST) beállítása" +#define D_WIFIMANAGER_SET_ACCESSPOINT_AND_STATION "WifiManager AccessPoint(AP) és Station(ST) beállítása" #define D_WIFIMANAGER_SET_ACCESSPOINT "Wifimanager AccessPoint(AP) beállítása" #define D_TRYING_TO_CONNECT "Csatlakozás a hálózatra..." #define D_RESTART_IN "Újraindítás..." #define D_SECONDS "másodperc" -#define D_DEVICE_WILL_RESTART "Az eszköz hamarosan újraindul" -#define D_BUTTON_TOGGLE "Toggle" -#define D_CONFIGURATION "Konfigurációk" +#define D_DEVICE_WILL_RESTART "Az eszköz hamarosan újraindul..." +#define D_BUTTON_TOGGLE "Megfordítás" +#define D_CONFIGURATION "Beállítások" #define D_INFORMATION "Információ" -#define D_FIRMWARE_UPGRADE "Firmware Frissítés" +#define D_FIRMWARE_UPGRADE "Firmware frissítés" #define D_CONSOLE "Konzol" -#define D_CONFIRM_RESTART "Újraindítás megerősítése" +#define D_CONFIRM_RESTART "Biztosan újraindítsam a modult?" -#define D_CONFIGURE_MODULE "Eszköz konfiguráció" +#define D_CONFIGURE_MODULE "Eszközbeállítások" #define D_CONFIGURE_WIFI "WiFi konfiguráció" #define D_CONFIGURE_MQTT "MQTT konfiguráció" #define D_CONFIGURE_DOMOTICZ "Domoticz konfiguráció" -#define D_CONFIGURE_LOGGING "Logolás konfiguráció" -#define D_CONFIGURE_OTHER "Egyéb konfiguráció" -#define D_CONFIRM_RESET_CONFIGURATION "Konfig resetelés megerősítve?" -#define D_RESET_CONFIGURATION "Konfiguráció reset" -#define D_BACKUP_CONFIGURATION "Konfiguráció backup" -#define D_RESTORE_CONFIGURATION "Konfiguráció visszaállítás" +#define D_CONFIGURE_LOGGING "Naplózás beállításai" +#define D_CONFIGURE_OTHER "Egyéb beállítások" +#define D_CONFIRM_RESET_CONFIGURATION "Biztosan töröljem a beállításokat?" +#define D_RESET_CONFIGURATION "Beállítások törlése" +#define D_BACKUP_CONFIGURATION "Beállítások mentése" +#define D_RESTORE_CONFIGURATION "Beállítások visszatöltése" #define D_MAIN_MENU "Menü" #define D_MODULE_PARAMETERS "Modul paraméterek" -#define D_MODULE_TYPE "Modul típus" +#define D_MODULE_TYPE "Alkalmazott modul" +#define D_PULLUP_ENABLE "Nincs felhúzó ellenállás" #define D_GPIO "GPIO" -#define D_SERIAL_IN "Serial In" -#define D_SERIAL_OUT "Serial Out" +#define D_SERIAL_IN "Soros BE" +#define D_SERIAL_OUT "Soros KI" -#define D_WIFI_PARAMETERS "Wifi paraméterek" -#define D_SCAN_FOR_WIFI_NETWORKS "Wifi hálózat keresése" +#define D_WIFI_PARAMETERS "WiFi paraméterek" +#define D_SCAN_FOR_WIFI_NETWORKS "WiFi hálózat keresése" #define D_SCAN_DONE "Keresés kész" #define D_NO_NETWORKS_FOUND "Nincs elérhető hálózat" -#define D_REFRESH_TO_SCAN_AGAIN "Frissíts az újra kereséshez" -#define D_DUPLICATE_ACCESSPOINT "Duplicate AccessPoint" -#define D_SKIPPING_LOW_QUALITY "Kihagyás, alacsony jelminőség" +#define D_REFRESH_TO_SCAN_AGAIN "Frissíts az újrakereséshez" +#define D_DUPLICATE_ACCESSPOINT "Duplikált access point" +#define D_SKIPPING_LOW_QUALITY "Rossz, alacsony jelminőség" #define D_RSSI "RSSI" #define D_WEP "WEP" #define D_WPA_PSK "WPA PSK" #define D_WPA2_PSK "WPA2 PSK" #define D_AP1_SSID "AP1 SSID" -#define D_AP1_PASSWORD "AP1 Jelszó" +#define D_AP1_PASSWORD "AP1 megosztott kulcs" #define D_AP2_SSID "AP2 SSID" -#define D_AP2_PASSWORD "AP2 Jelszó" +#define D_AP2_PASSWORD "AP2 megosztott kulcs" #define D_MQTT_PARAMETERS "MQTT paraméterek" #define D_CLIENT "Kliens" -#define D_FULL_TOPIC "Teljes téma" +#define D_FULL_TOPIC "Teljes topic" -#define D_LOGGING_PARAMETERS "Logolás paraméterek" -#define D_SERIAL_LOG_LEVEL "Serial logolás szint" -#define D_WEB_LOG_LEVEL "Web logolás szint" +#define D_LOGGING_PARAMETERS "Naplózási paraméterek" +#define D_SERIAL_LOG_LEVEL "Soros naplózási szint" +#define D_WEB_LOG_LEVEL "Web naplózási szint" #define D_SYS_LOG_LEVEL "Syslog szint" -#define D_MORE_DEBUG "Részletes debug" -#define D_SYSLOG_HOST "Syslog host" +#define D_MORE_DEBUG "Részletes hibakeresés" +#define D_SYSLOG_HOST "Syslog hoszt" #define D_SYSLOG_PORT "Syslog port" -#define D_TELEMETRY_PERIOD "Telemetria (sec)" +#define D_TELEMETRY_PERIOD "Telemetria (mp.)" -#define D_OTHER_PARAMETERS "Egyéb paraméterek" -#define D_WEB_ADMIN_PASSWORD "Web Admin Jelszó" -#define D_MQTT_ENABLE "MQTT engedélyezés" +#define D_OTHER_PARAMETERS "Egyéb beállítások" +#define D_TEMPLATE "Template" +#define D_ACTIVATE "Activate" +#define D_WEB_ADMIN_PASSWORD "Web admin jelszó" +#define D_MQTT_ENABLE "MQTT engedélyezése" #define D_FRIENDLY_NAME "Név" #define D_BELKIN_WEMO "Belkin WeMo" #define D_HUE_BRIDGE "Hue Bridge" #define D_SINGLE_DEVICE "single device" #define D_MULTI_DEVICE "multi device" -#define D_SAVE_CONFIGURATION "Konfiguráció mentése" -#define D_CONFIGURATION_SAVED "Konfiguráció elmentve" -#define D_CONFIGURATION_RESET "Konfiguráció visszaállítása" +#define D_CONFIGURE_TEMPLATE "Configure Template" +#define D_TEMPLATE_PARAMETERS "Template parameters" +#define D_TEMPLATE_NAME "Name" +#define D_BASE_TYPE "Based on" +#define D_TEMPLATE_FLAGS "Options" +#define D_ALLOW_ADC0 "ADC0 input" +#define D_ALLOW_PULLUP "User pull-up selection" -#define D_PROGRAM_VERSION "Program Verzió" -#define D_BUILD_DATE_AND_TIME "Build Dátum & Idő" -#define D_CORE_AND_SDK_VERSION "Core/SDK Verzió" -#define D_FLASH_WRITE_COUNT "Flashelések száma" -#define D_MAC_ADDRESS "MAC Cím" -#define D_MQTT_HOST "MQTT Host" -#define D_MQTT_PORT "MQTT Port" -#define D_MQTT_CLIENT "MQTT Cliens" -#define D_MQTT_USER "MQTT Felhasználó" -#define D_MQTT_TOPIC "MQTT Téma" -#define D_MQTT_GROUP_TOPIC "MQTT Csoport Téma" -#define D_MQTT_FULL_TOPIC "MQTT Teljes téma" -#define D_MDNS_DISCOVERY "mDNS Láthatóság" -#define D_MDNS_ADVERTISE "mDNS Hírdetés" -#define D_ESP_CHIP_ID "ESP Chip Id" -#define D_FLASH_CHIP_ID "Flash Chip Id" -#define D_FLASH_CHIP_SIZE "Flash Méret" -#define D_FREE_PROGRAM_SPACE "Szabad Program Hely" +#define D_SAVE_CONFIGURATION "Beállítások mentése" +#define D_CONFIGURATION_SAVED "Beállítások elmentve" +#define D_CONFIGURATION_RESET "Beállítások visszaállítása" -#define D_UPGRADE_BY_WEBSERVER "Frissítés web szerverrel" -#define D_OTA_URL "OTA Url" +#define D_PROGRAM_VERSION "Program verzió" +#define D_BUILD_DATE_AND_TIME "Build ideje" +#define D_CORE_AND_SDK_VERSION "Core/SDK verzió" +#define D_FLASH_WRITE_COUNT "Flash írások száma" +#define D_MAC_ADDRESS "MAC cím" +#define D_MQTT_HOST "MQTT hoszt" +#define D_MQTT_PORT "MQTT port" +#define D_MQTT_CLIENT "MQTT kliens" +#define D_MQTT_USER "MQTT felhasználó" +#define D_MQTT_TOPIC "MQTT topic" +#define D_MQTT_GROUP_TOPIC "MQTT csoport topic" +#define D_MQTT_FULL_TOPIC "MQTT teljes topic" +#define D_MDNS_DISCOVERY "mDNS láthatóság" +#define D_MDNS_ADVERTISE "mDNS hirdetés" +#define D_ESP_CHIP_ID "ESP chip ID" +#define D_FLASH_CHIP_ID "Flash chip ID" +#define D_FLASH_CHIP_SIZE "Flash mérete" +#define D_FREE_PROGRAM_SPACE "Szabad programhely" + +#define D_UPGRADE_BY_WEBSERVER "Frissítés távoli szerverről" +#define D_OTA_URL "OTA URL" #define D_START_UPGRADE "Frissítés" -#define D_UPGRADE_BY_FILE_UPLOAD "Frissítés file feltöltéssel" +#define D_UPGRADE_BY_FILE_UPLOAD "Frissítés helyi fájllal" #define D_UPLOAD_STARTED "Feltöltés elindítva" #define D_UPGRADE_STARTED "Frissítés elindítva" #define D_UPLOAD_DONE "Feltöltés kész" -#define D_UPLOAD_ERR_1 "Nincs file kijelölve" +#define D_UPLOAD_ERR_1 "Nincs fájl kijelölve" #define D_UPLOAD_ERR_2 "Nincs elég memória" -#define D_UPLOAD_ERR_3 "Magic byte is not 0xE9" -#define D_UPLOAD_ERR_4 "Program flash méret nagyobb a valós flash méretnél" -#define D_UPLOAD_ERR_5 "Feltöltés buffer hiba" -#define D_UPLOAD_ERR_6 "Feltöltés sikertelen. Endegélyezz 3-mas logolást" +#define D_UPLOAD_ERR_3 "A \"magic byte\" nem 0xE9" +#define D_UPLOAD_ERR_4 "A program flashméret nagyobb a valós flashméretnél" +#define D_UPLOAD_ERR_5 "Feltöltési buffer hiba" +#define D_UPLOAD_ERR_6 "Feltöltés sikertelen. Endegélyezd a 3. szintű naplózást" #define D_UPLOAD_ERR_7 "Feltöltés megszakítva" -#define D_UPLOAD_ERR_8 "Érvénytelen file" -#define D_UPLOAD_ERR_9 "File túl nagy" -#define D_UPLOAD_ERR_10 "Failed to init RF chip" -#define D_UPLOAD_ERR_11 "Failed to erase RF chip" -#define D_UPLOAD_ERR_12 "Failed to write to RF chip" -#define D_UPLOAD_ERR_13 "Failed to decode RF firmware" -#define D_UPLOAD_ERROR_CODE "Feltöltés hiba kód" +#define D_UPLOAD_ERR_8 "Érvénytelen fájl" +#define D_UPLOAD_ERR_9 "A fájl túl nagy" +#define D_UPLOAD_ERR_10 "Az RF chip inicializálása sikertelen" +#define D_UPLOAD_ERR_11 "Az RF chip törlése sikertelen" +#define D_UPLOAD_ERR_12 "Az RF chip írása sikertelen" +#define D_UPLOAD_ERR_13 "Az RF firmware dekódolása sikertelen" +#define D_UPLOAD_ERROR_CODE "Feltöltési hibakód" -#define D_ENTER_COMMAND "Parancsolj" -#define D_ENABLE_WEBLOG_FOR_RESPONSE "Engedélyezz 2-es weblogolást több információért" -#define D_NEED_USER_AND_PASSWORD "Kell felhasználó=&jelszó=" +#define D_ENTER_COMMAND "Kérem a parancsot..." +#define D_ENABLE_WEBLOG_FOR_RESPONSE "Engedélyezz 2-es szintű webnaplózást több információért" +#define D_NEED_USER_AND_PASSWORD "Szükséges a user=&password= paraméter" // xdrv_01_mqtt.ino #define D_FINGERPRINT "TLS fingerprint hitelesítése..." -#define D_TLS_CONNECT_FAILED_TO "TLS Csatlakozás sikertelen a" +#define D_TLS_CONNECT_FAILED_TO "TLS csatlakozás sikertelen a(z)" #define D_RETRY_IN "Újrapróbálás" -#define D_VERIFIED "Hitelesítve Fingerprint" -#define D_INSECURE "Nem biztonságos kapcsolat érvénytelen Fingerprint miatt" -#define D_CONNECT_FAILED_TO "Sikertelen csatlakozás a" +#define D_VERIFIED "Fingerprint hitelesítve" +#define D_INSECURE "Nem biztonságos kapcsolat érvénytelen fingerprint miatt" +#define D_CONNECT_FAILED_TO "Sikertelen csatlakozás a(z)" // xplg_wemohue.ino #define D_MULTICAST_DISABLED "Multicast kikapcsolva" @@ -369,8 +381,8 @@ #define D_HUE_BRIDGE_SETUP "Hue beállítás" #define D_HUE_API_NOT_IMPLEMENTED "Hue API nincs implementálva" #define D_HUE_API "Hue API" -#define D_HUE_POST_ARGS "Hue POST args" -#define D_3_RESPONSE_PACKETS_SENT "3 válaszcsomagok elküldve" +#define D_HUE_POST_ARGS "Hue POST argumentumok" +#define D_3_RESPONSE_PACKETS_SENT "3 válaszcsomag elküldve" // xdrv_07_domoticz.ino #define D_DOMOTICZ_PARAMETERS "Domoticz paraméterek" @@ -378,71 +390,71 @@ #define D_DOMOTICZ_KEY_IDX "Key idx" #define D_DOMOTICZ_SWITCH_IDX "Kapcsoló idx" #define D_DOMOTICZ_SENSOR_IDX "Szenzor idx" - #define D_DOMOTICZ_TEMP "Temp" - #define D_DOMOTICZ_TEMP_HUM "Hőm,Párat" - #define D_DOMOTICZ_TEMP_HUM_BARO "Hőm,Párat,Légny" - #define D_DOMOTICZ_POWER_ENERGY "Teljesítmény,Energia" + #define D_DOMOTICZ_TEMP "Hőmérséklet" + #define D_DOMOTICZ_TEMP_HUM "Hőmérséklet, páratartalom" + #define D_DOMOTICZ_TEMP_HUM_BARO "Hőmérséklet, páratartalom, légnyomás" + #define D_DOMOTICZ_POWER_ENERGY "Teljesítmény, energia" #define D_DOMOTICZ_ILLUMINANCE "Fényerő" #define D_DOMOTICZ_COUNT "Szám/PM1" #define D_DOMOTICZ_VOLTAGE "Feszültség/PM2.5" #define D_DOMOTICZ_CURRENT "Áram/PM10" #define D_DOMOTICZ_AIRQUALITY "Légminőség" -#define D_DOMOTICZ_UPDATE_TIMER "Update időzítő" +#define D_DOMOTICZ_UPDATE_TIMER "Frissítési időzítő" // xdrv_09_timers.ino -#define D_CONFIGURE_TIMER "Configure Timer" -#define D_TIMER_PARAMETERS "Timer parameters" -#define D_TIMER_ENABLE "Enable Timers" -#define D_TIMER_ARM "Arm" -#define D_TIMER_TIME "Time" -#define D_TIMER_DAYS "Days" -#define D_TIMER_REPEAT "Repeat" -#define D_TIMER_OUTPUT "Output" -#define D_TIMER_ACTION "Action" +#define D_CONFIGURE_TIMER "Időzítő beállításai" +#define D_TIMER_PARAMETERS "Időzítő paraméterei" +#define D_TIMER_ENABLE "Időzítők engedélyezve" +#define D_TIMER_ARM "Be" +#define D_TIMER_TIME "Idő" +#define D_TIMER_DAYS "Napok" +#define D_TIMER_REPEAT "Ismétlés" +#define D_TIMER_OUTPUT "Kimenet" +#define D_TIMER_ACTION "Művelet" // xdrv_10_knx.ino -#define D_CONFIGURE_KNX "Configure KNX" -#define D_KNX_PARAMETERS "KNX Parameters" -#define D_KNX_GENERAL_CONFIG "General" -#define D_KNX_PHYSICAL_ADDRESS "Physical Address" -#define D_KNX_PHYSICAL_ADDRESS_NOTE "( Must be unique on the KNX network )" -#define D_KNX_ENABLE "Enable KNX" -#define D_KNX_GROUP_ADDRESS_TO_WRITE "Data to Send to Group Addresses" -#define D_ADD "Add" -#define D_DELETE "Delete" -#define D_REPLY "Reply" -#define D_KNX_GROUP_ADDRESS_TO_READ "Group Addresses to Receive Data from" +#define D_CONFIGURE_KNX "KNX beállításai" +#define D_KNX_PARAMETERS "KNX paraméterei" +#define D_KNX_GENERAL_CONFIG "Általános" +#define D_KNX_PHYSICAL_ADDRESS "Fizikai cím" +#define D_KNX_PHYSICAL_ADDRESS_NOTE "(egyedinek kell lennie a KNX hálózaton)" +#define D_KNX_ENABLE "KNX engedélyezése" +#define D_KNX_GROUP_ADDRESS_TO_WRITE "Íráshoz használt csoportcímek" +#define D_ADD "Hozzáadás" +#define D_DELETE "Törlés" +#define D_REPLY "Válasz" +#define D_KNX_GROUP_ADDRESS_TO_READ "Fogadáshoz használt csoportcímek" #define D_LOG_KNX "KNX: " -#define D_RECEIVED_FROM "Received from" -#define D_KNX_COMMAND_WRITE "Write" -#define D_KNX_COMMAND_READ "Read" -#define D_KNX_COMMAND_OTHER "Other" -#define D_SENT_TO "sent to" -#define D_KNX_WARNING "The group address ( 0 / 0 / 0 ) is reserved and can not be used." +#define D_RECEIVED_FROM "Fogadva tőle:" +#define D_KNX_COMMAND_WRITE "Írás" +#define D_KNX_COMMAND_READ "Olvasás" +#define D_KNX_COMMAND_OTHER "Egyáb" +#define D_SENT_TO "küldve neki:" +#define D_KNX_WARNING "A csoportcím ( 0 / 0 / 0 ) fenntartott és nem használható." #define D_KNX_ENHANCEMENT "Communication Enhancement" #define D_KNX_TX_SLOT "KNX TX" #define D_KNX_RX_SLOT "KNX RX" // xdrv_03_energy.ino -#define D_ENERGY_TODAY "Mai Energia" -#define D_ENERGY_YESTERDAY "Tegnapi Energia" -#define D_ENERGY_TOTAL "Összes Energia" +#define D_ENERGY_TODAY "Mai energia" +#define D_ENERGY_YESTERDAY "Tegnapi energia" +#define D_ENERGY_TOTAL "Összes energia" // xsns_05_ds18b20.ino -#define D_SENSOR_BUSY "Szenzor elfoglalt" +#define D_SENSOR_BUSY "Szenzor foglalt" #define D_SENSOR_CRC_ERROR "Szenzor CRC hiba" #define D_SENSORS_FOUND "Szenzorok megtalálva" // xsns_06_dht.ino -#define D_TIMEOUT_WAITING_FOR "Timeout waiting for" -#define D_START_SIGNAL_LOW "start signal low" -#define D_START_SIGNAL_HIGH "start signal high" -#define D_PULSE "pulse" -#define D_CHECKSUM_FAILURE "Checksum failure" +#define D_TIMEOUT_WAITING_FOR "Időtúllépés, várakozás" +#define D_START_SIGNAL_LOW "startjel alacsony" +#define D_START_SIGNAL_HIGH "startjel magas" +#define D_PULSE "impulzus" +#define D_CHECKSUM_FAILURE "Checksum hiba" // xsns_07_sht1x.ino -#define D_SENSOR_DID_NOT_ACK_COMMAND "Szenzor nem ismerte el(ACK) a parancsot" -#define D_SHT1X_FOUND "SHT1X found" +#define D_SENSOR_DID_NOT_ACK_COMMAND "A szenzor nem nyugtázta a parancsot" +#define D_SHT1X_FOUND "SHT1X megtalálva" // xsns_18_pms5003.ino #define D_STANDARD_CONCENTRATION "CF-1 PM" // Standard Particle CF-1 Particle Matter @@ -450,102 +462,126 @@ #define D_PARTICALS_BEYOND "Részecskék" // xsns_32_mpu6050.ino -#define D_AX_AXIS "Accel. X-Axis" -#define D_AY_AXIS "Accel. Y-Axis" -#define D_AZ_AXIS "Accel. Z-Axis" -#define D_GX_AXIS "Gyro X-Axis" -#define D_GY_AXIS "Gyro Y-Axis" -#define D_GZ_AXIS "Gyro Z-Axis" +#define D_AX_AXIS "Gyorsulásm. X-tengely" +#define D_AY_AXIS "Gyorsulásm. Y-tengely" +#define D_AZ_AXIS "Gyorsulásm. Z-tengely" +#define D_GX_AXIS "Giroszkóp X-tengely" +#define D_GY_AXIS "Giroszkóp Y-tengely" +#define D_GZ_AXIS "Giroszkóp Z-tengely" // xsns_34_hx711.ino -#define D_HX_CAL_REMOVE "Remove weigth" -#define D_HX_CAL_REFERENCE "Load reference weigth" -#define D_HX_CAL_DONE "Calibrated" -#define D_HX_CAL_FAIL "Calibration failed" -#define D_RESET_HX711 "Reset Scale" -#define D_CONFIGURE_HX711 "Configure Scale" -#define D_HX711_PARAMETERS "Scale parameters" -#define D_ITEM_WEIGHT "Item weight" -#define D_REFERENCE_WEIGHT "Reference weigth" -#define D_CALIBRATE "Calibrate" -#define D_CALIBRATION "Calibration" +#define D_HX_CAL_REMOVE "Távolítsa el a súlyt" +#define D_HX_CAL_REFERENCE "Helyezze fel a referenciasúlyt" +#define D_HX_CAL_DONE "Kalibrálva" +#define D_HX_CAL_FAIL "Kalibrálási hiba" +#define D_RESET_HX711 "Skála újrabeállítása" +#define D_CONFIGURE_HX711 "Skála konfigurálása" +#define D_HX711_PARAMETERS "Skálaparaméterek" +#define D_ITEM_WEIGHT "Tárgy tömege" +#define D_REFERENCE_WEIGHT "Referenciatömeg" +#define D_CALIBRATE "Kalibrálás" +#define D_CALIBRATION "Kalibrálás" //xsns_35_tx20.ino -#define D_TX20_WIND_DIRECTION "Wind Direction" -#define D_TX20_WIND_SPEED "Wind Speed" -#define D_TX20_WIND_SPEED_AVG "Wind Speed Avg" -#define D_TX20_WIND_SPEED_MAX "Wind Speed Max" -#define D_TX20_NORTH "N" -#define D_TX20_EAST "E" -#define D_TX20_SOUTH "S" -#define D_TX20_WEST "W" +#define D_TX20_WIND_DIRECTION "Szélirány" +#define D_TX20_WIND_SPEED "Szélsebesség" +#define D_TX20_WIND_SPEED_AVG "Átlag szélsebesség" +#define D_TX20_WIND_SPEED_MAX "Max. szélsebesség" +#define D_TX20_NORTH "É" +#define D_TX20_EAST "K" +#define D_TX20_SOUTH "D" +#define D_TX20_WEST "NY" -// sonoff_template.h -#define D_SENSOR_NONE "Nincs" -#define D_SENSOR_DHT11 "DHT11" -#define D_SENSOR_AM2301 "AM2301" -#define D_SENSOR_SI7021 "SI7021" -#define D_SENSOR_DS18X20 "DS18x20" -#define D_SENSOR_I2C_SCL "I2C SCL" -#define D_SENSOR_I2C_SDA "I2C SDA" -#define D_SENSOR_WS2812 "WS2812" -#define D_SENSOR_DFR562 "MP3 Player" -#define D_SENSOR_IRSEND "IRadó" -#define D_SENSOR_SWITCH "Kapcsoló" // Suffix "1" -#define D_SENSOR_BUTTON "Gomb" // Suffix "1" -#define D_SENSOR_RELAY "Relé" // Suffix "1i" -#define D_SENSOR_LED "Led" // Suffix "1i" -#define D_SENSOR_PWM "PWM" // Suffix "1" -#define D_SENSOR_COUNTER "Számláló" // Suffix "1" -#define D_SENSOR_IRRECV "IRvevő" -#define D_SENSOR_MHZ_RX "MHZ Rx" -#define D_SENSOR_MHZ_TX "MHZ Tx" -#define D_SENSOR_PZEM004_RX "PZEM004 Rx" -#define D_SENSOR_PZEM016_RX "PZEM016 Rx" -#define D_SENSOR_PZEM017_RX "PZEM017 Rx" -#define D_SENSOR_PZEM0XX_TX "PZEM0XX Tx" -#define D_SENSOR_SAIR_RX "SAir Rx" -#define D_SENSOR_SAIR_TX "SAir Tx" -#define D_SENSOR_SPI_CS "SPI CS" -#define D_SENSOR_SPI_DC "SPI DC" -#define D_SENSOR_BACKLIGHT "Háttérvil" -#define D_SENSOR_PMS5003 "PMS5003" -#define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" -#define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" -#define D_SENSOR_SBR_RX "SerBr Rx" -#define D_SENSOR_SBR_TX "SerBr Tx" -#define D_SENSOR_SR04_TRIG "SR04 Tri" -#define D_SENSOR_SR04_ECHO "SR04 Ech" -#define D_SENSOR_SDM120_TX "SDM120/220 Tx" -#define D_SENSOR_SDM120_RX "SDM120/220 Rx" -#define D_SENSOR_SDM630_TX "SDM630 Tx" -#define D_SENSOR_SDM630_RX "SDM630 Rx" -#define D_SENSOR_TM1638_CLK "TM16 CLK" -#define D_SENSOR_TM1638_DIO "TM16 DIO" -#define D_SENSOR_TM1638_STB "TM16 STB" -#define D_SENSOR_HX711_SCK "HX711 SCK" -#define D_SENSOR_HX711_DAT "HX711 DAT" -#define D_SENSOR_TX20_TX "TX20" -#define D_SENSOR_RFSEND "RFSend" -#define D_SENSOR_RFRECV "RFrecv" -#define D_SENSOR_TUYA_TX "Tuya Tx" -#define D_SENSOR_TUYA_RX "Tuya Rx" -#define D_SENSOR_MGC3130_XFER "MGC3130 Xfer" -#define D_SENSOR_MGC3130_RESET "MGC3130 Reset" -#define D_SENSOR_SSPI_MISO "SSPI MISO" -#define D_SENSOR_SSPI_MOSI "SSPI MOSI" -#define D_SENSOR_SSPI_SCLK "SSPI SCLK" -#define D_SENSOR_SSPI_CS "SSPI CS" -#define D_SENSOR_SSPI_DC "SSPI DC" -#define D_SENSOR_RF_SENSOR "RF Sensor" -#define D_SENSOR_AZ_RX "AZ Rx" -#define D_SENSOR_AZ_TX "AZ Tx" +// sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box +#define D_SENSOR_NONE "Nincs" +#define D_SENSOR_USER "User" +#define D_SENSOR_DHT11 "DHT11" +#define D_SENSOR_AM2301 "AM2301" +#define D_SENSOR_SI7021 "SI7021" +#define D_SENSOR_DS18X20 "DS18x20" +#define D_SENSOR_I2C_SCL "I2C SCL" +#define D_SENSOR_I2C_SDA "I2C SDA" +#define D_SENSOR_WS2812 "WS2812" +#define D_SENSOR_DFR562 "MP3 lejátszó" +#define D_SENSOR_IRSEND "IR adó" +#define D_SENSOR_SWITCH "Kapcsoló" // Suffix "1" +#define D_SENSOR_BUTTON "Gomb" // Suffix "1" +#define D_SENSOR_RELAY "Relé" // Suffix "1i" +#define D_SENSOR_LED "LED" // Suffix "1i" +#define D_SENSOR_PWM "PWM" // Suffix "1" +#define D_SENSOR_COUNTER "Számláló" // Suffix "1" +#define D_SENSOR_IRRECV "IR vevő" +#define D_SENSOR_MHZ_RX "MHZ Rx" +#define D_SENSOR_MHZ_TX "MHZ Tx" +#define D_SENSOR_PZEM004_RX "PZEM004 Rx" +#define D_SENSOR_PZEM016_RX "PZEM016 Rx" +#define D_SENSOR_PZEM017_RX "PZEM017 Rx" +#define D_SENSOR_PZEM0XX_TX "PZEM0XX Tx" +#define D_SENSOR_SAIR_RX "SAir Rx" +#define D_SENSOR_SAIR_TX "SAir Tx" +#define D_SENSOR_SPI_CS "SPI CS" +#define D_SENSOR_SPI_DC "SPI DC" +#define D_SENSOR_BACKLIGHT "Háttérfény" +#define D_SENSOR_PMS5003 "PMS5003" +#define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" +#define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" +#define D_SENSOR_SBR_RX "SerBr Rx" +#define D_SENSOR_SBR_TX "SerBr Tx" +#define D_SENSOR_SR04_TRIG "SR04 Tri" +#define D_SENSOR_SR04_ECHO "SR04 Ech" +#define D_SENSOR_SDM120_TX "SDMx20 Tx" +#define D_SENSOR_SDM120_RX "SDMx20 Rx" +#define D_SENSOR_SDM630_TX "SDM630 Tx" +#define D_SENSOR_SDM630_RX "SDM630 Rx" +#define D_SENSOR_TM1638_CLK "TM16 CLK" +#define D_SENSOR_TM1638_DIO "TM16 DIO" +#define D_SENSOR_TM1638_STB "TM16 STB" +#define D_SENSOR_HX711_SCK "HX711 SCK" +#define D_SENSOR_HX711_DAT "HX711 DAT" +#define D_SENSOR_TX20_TX "TX20" +#define D_SENSOR_RFSEND "RFSend" +#define D_SENSOR_RFRECV "RFrecv" +#define D_SENSOR_TUYA_TX "Tuya Tx" +#define D_SENSOR_TUYA_RX "Tuya Rx" +#define D_SENSOR_MGC3130_XFER "MGC3130 Xfr" +#define D_SENSOR_MGC3130_RESET "MGC3130 Rst" +#define D_SENSOR_SSPI_MISO "SSPI MISO" +#define D_SENSOR_SSPI_MOSI "SSPI MOSI" +#define D_SENSOR_SSPI_SCLK "SSPI SCLK" +#define D_SENSOR_SSPI_CS "SSPI CS" +#define D_SENSOR_SSPI_DC "SSPI DC" +#define D_SENSOR_RF_SENSOR "RF Sensor" +#define D_SENSOR_AZ_RX "AZ Rx" +#define D_SENSOR_AZ_TX "AZ Tx" +#define D_SENSOR_MAX31855_CS "MX31855 CS" +#define D_SENSOR_MAX31855_CLK "MX31855 CLK" +#define D_SENSOR_MAX31855_DO "MX31855 DO" +#define D_SENSOR_NRG_SEL "HLWBL SEL" // Suffix "i" +#define D_SENSOR_NRG_CF1 "HLWBL CF1" +#define D_SENSOR_HLW_CF "HLW8012 CF" +#define D_SENSOR_HJL_CF "BL0937 CF" +#define D_SENSOR_MCP39F5_TX "MCP39F5 Tx" +#define D_SENSOR_MCP39F5_RX "MCP39F5 Rx" +#define D_SENSOR_MCP39F5_RST "MCP39F5 Rst" +#define D_SENSOR_CSE7766_TX "CSE7766 Tx" +#define D_SENSOR_CSE7766_RX "CSE7766 Rx" +#define D_SENSOR_PN532_TX "PN532 Tx" +#define D_SENSOR_PN532_RX "PN532 Rx" +#define D_SENSOR_SM16716_CLK "SM16716 CLK" +#define D_SENSOR_SM16716_DAT "SM16716 DAT" +#define D_SENSOR_SM16716_POWER "SM16716 PWR" +#define D_SENSOR_MY92X1_DI "MY92x1 DI" +#define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI" +#define D_SENSOR_ARIRFRCV "ALux IrRcv" +#define D_SENSOR_TXD "Serial Tx" +#define D_SENSOR_RXD "Serial Rx" +#define D_SENSOR_ROTARY "Rotary" // Suffix "1A" // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CENTIMETER "cm" #define D_UNIT_HERTZ "Hz" -#define D_UNIT_HOUR "ó" +#define D_UNIT_HOUR "h" #define D_UNIT_INCREMENTS "inc" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" @@ -559,12 +595,12 @@ #define D_UNIT_MILLIMETER "mm" #define D_UNIT_MILLIMETER_MERCURY "mmHg" #define D_UNIT_MILLISECOND "ms" -#define D_UNIT_MINUTE "p" +#define D_UNIT_MINUTE "min" #define D_UNIT_PARTS_PER_BILLION "ppb" #define D_UNIT_PARTS_PER_DECILITER "ppd" #define D_UNIT_PARTS_PER_MILLION "ppm" #define D_UNIT_PRESSURE "hPa" -#define D_UNIT_SECOND "m" +#define D_UNIT_SECOND "s" #define D_UNIT_SECTORS "szektorok" #define D_UNIT_VA "VA" #define D_UNIT_VAR "VAr" @@ -599,13 +635,13 @@ #define D_LOG_WIFI "WIF: " // Wifi //SDM220 -#define D_PHASE_ANGLE "Phase Angle" -#define D_IMPORT_ACTIVE "Import Active" -#define D_EXPORT_ACTIVE "Export Active" -#define D_IMPORT_REACTIVE "Import Reactive" -#define D_EXPORT_REACTIVE "Export Reactive" -#define D_TOTAL_REACTIVE "Total Reactive" +#define D_PHASE_ANGLE "Fázisszög" +#define D_IMPORT_ACTIVE "Bejövő aktív" +#define D_EXPORT_ACTIVE "Kimenő aktív" +#define D_IMPORT_REACTIVE "Bejövő reaktív" +#define D_EXPORT_REACTIVE "Kimenő reaktív" +#define D_TOTAL_REACTIVE "Összes reaktív" #define D_UNIT_KWARH "kVArh" -#define D_UNIT_ANGLE "Deg" +#define D_UNIT_ANGLE "fok" #endif // _LANGUAGE_HU_HU_H_ diff --git a/sonoff/language/it-IT.h b/sonoff/language/it-IT.h index a6d0d693c..4d735426f 100644 --- a/sonoff/language/it-IT.h +++ b/sonoff/language/it-IT.h @@ -1,7 +1,7 @@ /* it-IT.h - localization for Italian - Italy for Sonoff-Tasmota - Copyright (C) 2018 Gennaro Tortone - some mods by Antonio Fragola + Copyright (C) 2019 Gennaro Tortone - some mods by Antonio Fragola This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -148,6 +148,7 @@ #define D_STOP "Stop" #define D_SUBNET_MASK "Maschera sottorete" #define D_SUBSCRIBE_TO "Sottoscrivi a" +#define D_UNSUBSCRIBE_FROM "Unsubscribe from" #define D_SUCCESSFUL "Riuscito" #define D_SUNRISE "Alba" #define D_SUNSET "Tramonto" @@ -253,6 +254,7 @@ #define D_MODULE_PARAMETERS "Parametri del modulo" #define D_MODULE_TYPE "Tipo modulo" +#define D_PULLUP_ENABLE "No Button/Switch pull-up" #define D_GPIO "GPIO" #define D_SERIAL_IN "Serial In" #define D_SERIAL_OUT "Serial Out" @@ -287,6 +289,8 @@ #define D_TELEMETRY_PERIOD "Periodo Telemetria" #define D_OTHER_PARAMETERS "Altri parametri" +#define D_TEMPLATE "Template" +#define D_ACTIVATE "Activate" #define D_WEB_ADMIN_PASSWORD "Password Amministratore Web" #define D_MQTT_ENABLE "Abilita MQTT" #define D_FRIENDLY_NAME "Nome confidenziale" @@ -295,6 +299,14 @@ #define D_SINGLE_DEVICE "dispositivo singolo" #define D_MULTI_DEVICE "dispositivo multiplo" +#define D_CONFIGURE_TEMPLATE "Configure Template" +#define D_TEMPLATE_PARAMETERS "Template parameters" +#define D_TEMPLATE_NAME "Name" +#define D_BASE_TYPE "Based on" +#define D_TEMPLATE_FLAGS "Options" +#define D_ALLOW_ADC0 "ADC0 input" +#define D_ALLOW_PULLUP "User pull-up selection" + #define D_SAVE_CONFIGURATION "Salva configurazione" #define D_CONFIGURATION_SAVED "Configurazione salvata" #define D_CONFIGURATION_RESET "Configurazione azzerata" @@ -480,66 +492,90 @@ #define D_TX20_SOUTH "S" #define D_TX20_WEST "W" -// sonoff_template.h -#define D_SENSOR_NONE "Nessuno" -#define D_SENSOR_DHT11 "DHT11" -#define D_SENSOR_AM2301 "AM2301" -#define D_SENSOR_SI7021 "SI7021" -#define D_SENSOR_DS18X20 "DS18x20" -#define D_SENSOR_I2C_SCL "I2C SCL" -#define D_SENSOR_I2C_SDA "I2C SDA" -#define D_SENSOR_WS2812 "WS2812" -#define D_SENSOR_DFR562 "MP3 Player" -#define D_SENSOR_IRSEND "IRsend" -#define D_SENSOR_SWITCH "Switch" // Suffix "1" -#define D_SENSOR_BUTTON "Button" // Suffix "1" -#define D_SENSOR_RELAY "Relay" // Suffix "1i" -#define D_SENSOR_LED "Led" // Suffix "1i" -#define D_SENSOR_PWM "PWM" // Suffix "1" -#define D_SENSOR_COUNTER "Counter" // Suffix "1" -#define D_SENSOR_IRRECV "IRrecv" -#define D_SENSOR_MHZ_RX "MHZ Rx" -#define D_SENSOR_MHZ_TX "MHZ Tx" -#define D_SENSOR_PZEM004_RX "PZEM004 Rx" -#define D_SENSOR_PZEM016_RX "PZEM016 Rx" -#define D_SENSOR_PZEM017_RX "PZEM017 Rx" -#define D_SENSOR_PZEM0XX_TX "PZEM0XX Tx" -#define D_SENSOR_SAIR_RX "SAir Rx" -#define D_SENSOR_SAIR_TX "SAir Tx" -#define D_SENSOR_SPI_CS "SPI CS" -#define D_SENSOR_SPI_DC "SPI DC" -#define D_SENSOR_BACKLIGHT "BkLight" -#define D_SENSOR_PMS5003 "PMS5003" -#define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" -#define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" -#define D_SENSOR_SBR_RX "SerBr Rx" -#define D_SENSOR_SBR_TX "SerBr Tx" -#define D_SENSOR_SR04_TRIG "SR04 Tri" -#define D_SENSOR_SR04_ECHO "SR04 Ech" -#define D_SENSOR_SDM120_TX "SDM120/220 Tx" -#define D_SENSOR_SDM120_RX "SDM120/220 Rx" -#define D_SENSOR_SDM630_TX "SDM630 Tx" -#define D_SENSOR_SDM630_RX "SDM630 Rx" -#define D_SENSOR_TM1638_CLK "TM16 CLK" -#define D_SENSOR_TM1638_DIO "TM16 DIO" -#define D_SENSOR_TM1638_STB "TM16 STB" -#define D_SENSOR_HX711_SCK "HX711 SCK" -#define D_SENSOR_HX711_DAT "HX711 DAT" -#define D_SENSOR_TX20_TX "TX20" -#define D_SENSOR_RFSEND "RFSend" -#define D_SENSOR_RFRECV "RFrecv" -#define D_SENSOR_TUYA_TX "Tuya Tx" -#define D_SENSOR_TUYA_RX "Tuya Rx" -#define D_SENSOR_MGC3130_XFER "MGC3130 Xfer" -#define D_SENSOR_MGC3130_RESET "MGC3130 Reset" -#define D_SENSOR_SSPI_MISO "SSPI MISO" -#define D_SENSOR_SSPI_MOSI "SSPI MOSI" -#define D_SENSOR_SSPI_SCLK "SSPI SCLK" -#define D_SENSOR_SSPI_CS "SSPI CS" -#define D_SENSOR_SSPI_DC "SSPI DC" -#define D_SENSOR_RF_SENSOR "RF Sensor" -#define D_SENSOR_AZ_RX "AZ Rx" -#define D_SENSOR_AZ_TX "AZ Tx" +// sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box +#define D_SENSOR_NONE "Nessuno" +#define D_SENSOR_USER "User" +#define D_SENSOR_DHT11 "DHT11" +#define D_SENSOR_AM2301 "AM2301" +#define D_SENSOR_SI7021 "SI7021" +#define D_SENSOR_DS18X20 "DS18x20" +#define D_SENSOR_I2C_SCL "I2C SCL" +#define D_SENSOR_I2C_SDA "I2C SDA" +#define D_SENSOR_WS2812 "WS2812" +#define D_SENSOR_DFR562 "MP3 Player" +#define D_SENSOR_IRSEND "IRsend" +#define D_SENSOR_SWITCH "Switch" // Suffix "1" +#define D_SENSOR_BUTTON "Button" // Suffix "1" +#define D_SENSOR_RELAY "Relay" // Suffix "1i" +#define D_SENSOR_LED "Led" // Suffix "1i" +#define D_SENSOR_PWM "PWM" // Suffix "1" +#define D_SENSOR_COUNTER "Counter" // Suffix "1" +#define D_SENSOR_IRRECV "IRrecv" +#define D_SENSOR_MHZ_RX "MHZ Rx" +#define D_SENSOR_MHZ_TX "MHZ Tx" +#define D_SENSOR_PZEM004_RX "PZEM004 Rx" +#define D_SENSOR_PZEM016_RX "PZEM016 Rx" +#define D_SENSOR_PZEM017_RX "PZEM017 Rx" +#define D_SENSOR_PZEM0XX_TX "PZEM0XX Tx" +#define D_SENSOR_SAIR_RX "SAir Rx" +#define D_SENSOR_SAIR_TX "SAir Tx" +#define D_SENSOR_SPI_CS "SPI CS" +#define D_SENSOR_SPI_DC "SPI DC" +#define D_SENSOR_BACKLIGHT "BkLight" +#define D_SENSOR_PMS5003 "PMS5003" +#define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" +#define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" +#define D_SENSOR_SBR_RX "SerBr Rx" +#define D_SENSOR_SBR_TX "SerBr Tx" +#define D_SENSOR_SR04_TRIG "SR04 Tri" +#define D_SENSOR_SR04_ECHO "SR04 Ech" +#define D_SENSOR_SDM120_TX "SDMx20 Tx" +#define D_SENSOR_SDM120_RX "SDMx20 Rx" +#define D_SENSOR_SDM630_TX "SDM630 Tx" +#define D_SENSOR_SDM630_RX "SDM630 Rx" +#define D_SENSOR_TM1638_CLK "TM16 CLK" +#define D_SENSOR_TM1638_DIO "TM16 DIO" +#define D_SENSOR_TM1638_STB "TM16 STB" +#define D_SENSOR_HX711_SCK "HX711 SCK" +#define D_SENSOR_HX711_DAT "HX711 DAT" +#define D_SENSOR_TX20_TX "TX20" +#define D_SENSOR_RFSEND "RFSend" +#define D_SENSOR_RFRECV "RFrecv" +#define D_SENSOR_TUYA_TX "Tuya Tx" +#define D_SENSOR_TUYA_RX "Tuya Rx" +#define D_SENSOR_MGC3130_XFER "MGC3130 Xfr" +#define D_SENSOR_MGC3130_RESET "MGC3130 Rst" +#define D_SENSOR_SSPI_MISO "SSPI MISO" +#define D_SENSOR_SSPI_MOSI "SSPI MOSI" +#define D_SENSOR_SSPI_SCLK "SSPI SCLK" +#define D_SENSOR_SSPI_CS "SSPI CS" +#define D_SENSOR_SSPI_DC "SSPI DC" +#define D_SENSOR_RF_SENSOR "RF Sensor" +#define D_SENSOR_AZ_RX "AZ Rx" +#define D_SENSOR_AZ_TX "AZ Tx" +#define D_SENSOR_MAX31855_CS "MX31855 CS" +#define D_SENSOR_MAX31855_CLK "MX31855 CLK" +#define D_SENSOR_MAX31855_DO "MX31855 DO" +#define D_SENSOR_NRG_SEL "HLWBL SEL" // Suffix "i" +#define D_SENSOR_NRG_CF1 "HLWBL CF1" +#define D_SENSOR_HLW_CF "HLW8012 CF" +#define D_SENSOR_HJL_CF "BL0937 CF" +#define D_SENSOR_MCP39F5_TX "MCP39F5 Tx" +#define D_SENSOR_MCP39F5_RX "MCP39F5 Rx" +#define D_SENSOR_MCP39F5_RST "MCP39F5 Rst" +#define D_SENSOR_CSE7766_TX "CSE7766 Tx" +#define D_SENSOR_CSE7766_RX "CSE7766 Rx" +#define D_SENSOR_PN532_TX "PN532 Tx" +#define D_SENSOR_PN532_RX "PN532 Rx" +#define D_SENSOR_SM16716_CLK "SM16716 CLK" +#define D_SENSOR_SM16716_DAT "SM16716 DAT" +#define D_SENSOR_SM16716_POWER "SM16716 PWR" +#define D_SENSOR_MY92X1_DI "MY92x1 DI" +#define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI" +#define D_SENSOR_ARIRFRCV "ALux IrRcv" +#define D_SENSOR_TXD "Serial Tx" +#define D_SENSOR_RXD "Serial Rx" +#define D_SENSOR_ROTARY "Rotary" // Suffix "1A" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/ko-KO.h b/sonoff/language/ko-KO.h new file mode 100644 index 000000000..fb85e5ca2 --- /dev/null +++ b/sonoff/language/ko-KO.h @@ -0,0 +1,647 @@ +/* + ko-KO.h - localization for Korean - Korean for Sonoff-Tasmota + + Copyright (C) 2019 Theo Arends (translated by NyaamZ) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef _LANGUAGE_KO_KO_H_ +#define _LANGUAGE_KO_KO_H_ + +/*************************** ATTENTION *******************************\ + * + * Due to memory constraints only UTF-8 is supported. + * To save code space keep text as short as possible. + * Time and Date provided by SDK can not be localized (yet). + * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. + * Use online command Prefix to translate cmnd, stat and tele. + * + * Updated until v6.2.1.11 +\*********************************************************************/ + +//#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) +// https://www.science.co.il/language/Locale-codes.php +#define LANGUAGE_LCID 1042 +// HTML (ISO 639-1) Language Code +#define D_HTML_LANGUAGE "ko" + +// "2017-03-07T11:08:02" - ISO8601:2004 +#define D_YEAR_MONTH_SEPARATOR "-" +#define D_MONTH_DAY_SEPARATOR "-" +#define D_DATE_TIME_SEPARATOR "T" +#define D_HOUR_MINUTE_SEPARATOR ":" +#define D_MINUTE_SECOND_SEPARATOR ":" + +#define D_DAY3LIST "일 월 화 수 목 금 토 " +#define D_MONTH3LIST "1월 2월 3월 4월 5월 6월 7월 8월 9월 10월11월12월" + +// Non JSON decimal separator +#define D_DECIMAL_SEPARATOR "." + +// Common +#define D_ADMIN "Admin" +#define D_AIR_QUALITY "공기질" +#define D_AP "AP" // Access Point +#define D_AS "as" +#define D_AUTO "자동" +#define D_BLINK "깜박임" +#define D_BLINKOFF "깜박임 끄기" +#define D_BOOT_COUNT "가동횟수" +#define D_BRIGHTLIGHT "밝기" +#define D_BSSID "BSSId" +#define D_BUTTON "버튼" +#define D_BY "by" // Written by me +#define D_BYTES "Bytes" +#define D_CELSIUS "섭씨" +#define D_CHANNEL "채널" +#define D_CO2 "이산화탄소" +#define D_CODE "코드" // Button code +#define D_COLDLIGHT "차갑게" +#define D_COMMAND "명령" +#define D_CONNECTED "연결됨" +#define D_COUNT "횟수" +#define D_COUNTER "Counter" +#define D_CURRENT "전류" // As in Voltage and Current +#define D_DATA "데이터" +#define D_DARKLIGHT "어둡게" +#define D_DEBUG "디버그" +#define D_DISABLED "사용 불가" +#define D_DISTANCE "거리" +#define D_DNS_SERVER "DNS 서버" +#define D_DONE "완료" +#define D_DST_TIME "DST" +#define D_ECO2 "eCO2" +#define D_EMULATION "에뮬레이션" +#define D_ENABLED "사용 가능" +#define D_ERASE "삭제" +#define D_ERROR "에러" +#define D_FAHRENHEIT "화씨" +#define D_FAILED "실패" +#define D_FALLBACK "Fallback" +#define D_FALLBACK_TOPIC "Fallback Topic" +#define D_FALSE "거짓" +#define D_FILE "파일" +#define D_FREE_MEMORY "남은 메모리" +#define D_FREQUENCY "빈도" +#define D_GAS "가스" +#define D_GATEWAY "게이트웨이" +#define D_GROUP "그룹" +#define D_HOST "호스트" +#define D_HOSTNAME "호스트이름" +#define D_HUMIDITY "습도" +#define D_ILLUMINANCE "조도" +#define D_IMMEDIATE "immediate" // Button immediate +#define D_INDEX "인덱스" +#define D_INFO "정보" +#define D_INFRARED "적외선" +#define D_INITIALIZED "초기화 완료" +#define D_IP_ADDRESS "IP 주소" +#define D_LIGHT "밝게" +#define D_LWT "LWT" +#define D_MODULE "모듈" +#define D_MQTT "MQTT" +#define D_MULTI_PRESS "multi-press" +#define D_NOISE "소음" +#define D_NONE "없음" +#define D_OFF "꺼짐" +#define D_OFFLINE "오프라인" +#define D_OK "Ok" +#define D_ON "켜짐" +#define D_ONLINE "온라인" +#define D_PASSWORD "비밀번호" +#define D_PORT "포트" +#define D_POWER_FACTOR "Power Factor" +#define D_POWERUSAGE "전원" +#define D_POWERUSAGE_ACTIVE "Active Power" +#define D_POWERUSAGE_APPARENT "Apparent Power" +#define D_POWERUSAGE_REACTIVE "Reactive Power" +#define D_PRESSURE "기압" +#define D_PRESSUREATSEALEVEL "해수면기압" +#define D_PROGRAM_FLASH_SIZE "플래시 용량" +#define D_PROGRAM_SIZE "프로그램 용량" +#define D_PROJECT "프로젝트" +#define D_RAIN "비" +#define D_RECEIVED "받음" +#define D_RESTART "재시작" +#define D_RESTARTING "재시작 중" +#define D_RESTART_REASON "재시작 이유" +#define D_RESTORE "복구" +#define D_RETAINED "보류" +#define D_RULE "규칙" +#define D_SAVE "저장" +#define D_SENSOR "센서" +#define D_SSID "SSId" +#define D_START "시작" +#define D_STD_TIME "STD" +#define D_STOP "정지" +#define D_SUBNET_MASK "서브넷 마스크" +#define D_SUBSCRIBE_TO "구독" +#define D_UNSUBSCRIBE_FROM "구독 해제" +#define D_SUCCESSFUL "성공" +#define D_SUNRISE "일출" +#define D_SUNSET "일몰" +#define D_TEMPERATURE "온도" +#define D_TO "to" +#define D_TOGGLE "전환" +#define D_TOPIC "Topic" +#define D_TRANSMIT "전송" +#define D_TRUE "참" +#define D_TVOC "TVOC" +#define D_UPGRADE "업그레이드" +#define D_UPLOAD "업로드" +#define D_UPTIME "가동시간" +#define D_USER "User" +#define D_UTC_TIME "UTC" +#define D_UV_INDEX "UV 색인" +#define D_UV_INDEX_1 "낮음" +#define D_UV_INDEX_2 "보통" +#define D_UV_INDEX_3 "높음" +#define D_UV_INDEX_4 "위험" +#define D_UV_INDEX_5 "BurnL1/2" +#define D_UV_INDEX_6 "BurnL3" +#define D_UV_INDEX_7 "OoR" // Out of Range +#define D_UV_LEVEL "UV 레벨" +#define D_UV_POWER "UV 파워" +#define D_VERSION "버전" +#define D_VOLTAGE "전압" +#define D_WEIGHT "무게" +#define D_WARMLIGHT "따뜻하게" +#define D_WEB_SERVER "웹 서버" + +// sonoff.ino +#define D_WARNING_MINIMAL_VERSION "경고: 이 버전은 영구 설정을 지원하지 않습니다" +#define D_LEVEL_10 "level 1-0" +#define D_LEVEL_01 "level 0-1" +#define D_SERIAL_LOGGING_DISABLED "Serial log 사용 안함" +#define D_SYSLOG_LOGGING_REENABLED "Syslog log 다시 사용" + +#define D_SET_BAUDRATE_TO "Set Baudrate to" +#define D_RECEIVED_TOPIC "Received Topic" +#define D_DATA_SIZE "데이터 용량" +#define D_ANALOG_INPUT "아날로그" + +// support.ino +#define D_OSWATCH "osWatch" +#define D_BLOCKED_LOOP "Blocked Loop" +#define D_WPS_FAILED_WITH_STATUS "WPS설정 실패" +#define D_ACTIVE_FOR_3_MINUTES "3분동안 활성화" +#define D_FAILED_TO_START "시작 실패" +#define D_PATCH_ISSUE_2186 "Patch issue 2186" +#define D_CONNECTING_TO_AP "AP에 연결 중" +#define D_IN_MODE "in mode" +#define D_CONNECT_FAILED_NO_IP_ADDRESS "IP 주소가 수신되지 않아 연결이 실패했습니다" +#define D_CONNECT_FAILED_AP_NOT_REACHED "연결이 닿지 않아 AP에 연결할 수 없습니다" +#define D_CONNECT_FAILED_WRONG_PASSWORD "비밀번호가 틀려 AP에 연결할 수 없습니다" +#define D_CONNECT_FAILED_AP_TIMEOUT "시간초과로 AP에 연결할 수 없습니다" +#define D_ATTEMPTING_CONNECTION "연결 시도 중..." +#define D_CHECKING_CONNECTION "연결 체크 중..." +#define D_QUERY_DONE "쿼리 완료. MQTT 서비스 발견" +#define D_MQTT_SERVICE_FOUND "MQTT 서비스 발견" +#define D_FOUND_AT "다음에서 발견" +#define D_SYSLOG_HOST_NOT_FOUND "Syslog 호스트가 발견되지 않았습니다" + +// settings.ino +#define D_SAVED_TO_FLASH_AT "플래시에 저장" +#define D_LOADED_FROM_FLASH_AT "플래시에서 로드" +#define D_USE_DEFAULTS "디폴트 사용" +#define D_ERASED_SECTOR "삭제된 섹터" + +// xdrv_02_webserver.ino +#define D_NOSCRIPT "Tasmota를 사용하려면 JavaScript를 활성화 하십시오." +#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "MINIMAL firmware - 업그레이드가 필요합니다" +#define D_WEBSERVER_ACTIVE_ON "Web 서버 작동 중" +#define D_WITH_IP_ADDRESS "IP 주소" +#define D_WEBSERVER_STOPPED "Web 서버 멈춤" +#define D_FILE_NOT_FOUND "파일을 찾을 수 없습니다" +#define D_REDIRECTED "인증 페이지로 리디렉션" +#define D_WIFIMANAGER_SET_ACCESSPOINT_AND_STATION "와이파이 매니저가 AccessPoint와 keep Station을 설정" +#define D_WIFIMANAGER_SET_ACCESSPOINT "와이파이 매니저가 AccessPoint를 설정" +#define D_TRYING_TO_CONNECT "장치를 네트워크에 연결하려고 시도 중" + +#define D_RESTART_IN "재시작" +#define D_SECONDS "초" +#define D_DEVICE_WILL_RESTART "이 장치는 몇 초 후 재시작됩니다" +#define D_BUTTON_TOGGLE "켜기/끄기" +#define D_CONFIGURATION "설정" +#define D_INFORMATION "정보" +#define D_FIRMWARE_UPGRADE "펌웨어 업그레이드" +#define D_CONSOLE "콘솔" +#define D_CONFIRM_RESTART "재시작" + +#define D_CONFIGURE_MODULE "모듈 설정" +#define D_CONFIGURE_WIFI "WiFi 설정" +#define D_CONFIGURE_MQTT "MQTT 설정" +#define D_CONFIGURE_DOMOTICZ "Domoticz 설정" +#define D_CONFIGURE_LOGGING "로그 설정" +#define D_CONFIGURE_OTHER "기타 설정" +#define D_CONFIRM_RESET_CONFIGURATION "설정 초기화 확인" +#define D_RESET_CONFIGURATION "설정 초기화" +#define D_BACKUP_CONFIGURATION "백업 설정" +#define D_RESTORE_CONFIGURATION "복원 설정" +#define D_MAIN_MENU "메인 메뉴" + +#define D_MODULE_PARAMETERS "모듈 상세" +#define D_MODULE_TYPE "모듈 타입" +#define D_PULLUP_ENABLE "No Button/Switch pull-up" +#define D_GPIO "GPIO" +#define D_SERIAL_IN "Serial In" +#define D_SERIAL_OUT "Serial Out" + +#define D_WIFI_PARAMETERS "Wifi 상세" +#define D_SCAN_FOR_WIFI_NETWORKS "Wifi 네트워크를 검색 중" +#define D_SCAN_DONE "검색 완료" +#define D_NO_NETWORKS_FOUND "발견된 네트워크가 없습니다" +#define D_REFRESH_TO_SCAN_AGAIN "검색 재시도" +#define D_DUPLICATE_ACCESSPOINT "중복된 AccessPoint" +#define D_SKIPPING_LOW_QUALITY "약한 네트워크 신호 무시" +#define D_RSSI "RSSI" +#define D_WEP "WEP" +#define D_WPA_PSK "WPA PSK" +#define D_WPA2_PSK "WPA2 PSK" +#define D_AP1_SSID "AP1 SSId" +#define D_AP1_PASSWORD "AP1 비밀번호" +#define D_AP2_SSID "AP2 SSId" +#define D_AP2_PASSWORD "AP2 비밀번호" + +#define D_MQTT_PARAMETERS "MQTT 상세" +#define D_CLIENT "클라이언트" +#define D_FULL_TOPIC "Full Topic" + +#define D_LOGGING_PARAMETERS "로그 상세" +#define D_SERIAL_LOG_LEVEL "시리얼 로그 레벨" +#define D_WEB_LOG_LEVEL "Web 로그 레벨" +#define D_SYS_LOG_LEVEL "Syslog 로그 레벨" +#define D_MORE_DEBUG "More debug" +#define D_SYSLOG_HOST "Syslog 호스트" +#define D_SYSLOG_PORT "Syslog 포트" +#define D_TELEMETRY_PERIOD "보고 주기" + +#define D_OTHER_PARAMETERS "기타 상세" +#define D_TEMPLATE "템플릿" +#define D_ACTIVATE "활성" +#define D_WEB_ADMIN_PASSWORD "Web Admin 비밀번호" +#define D_MQTT_ENABLE "MQTT 사용" +#define D_FRIENDLY_NAME "별칭" +#define D_BELKIN_WEMO "Belkin WeMo" +#define D_HUE_BRIDGE "Hue Bridge" +#define D_SINGLE_DEVICE "single device" +#define D_MULTI_DEVICE "multi device" + +#define D_CONFIGURE_TEMPLATE "템플릿 설정" +#define D_TEMPLATE_PARAMETERS "템플릿 상세" +#define D_TEMPLATE_NAME "이름" +#define D_BASE_TYPE "Based on" +#define D_TEMPLATE_FLAGS "옵션" +#define D_ALLOW_ADC0 "ADC0 입력" +#define D_ALLOW_PULLUP "User pull-up selection" + +#define D_SAVE_CONFIGURATION "설정 저장" +#define D_CONFIGURATION_SAVED "설정 저장 완료" +#define D_CONFIGURATION_RESET "설정 초기화" + +#define D_PROGRAM_VERSION "프로그램 버전" +#define D_BUILD_DATE_AND_TIME "빌드 날짜" +#define D_CORE_AND_SDK_VERSION "Core/SDK 버전" +#define D_FLASH_WRITE_COUNT "플래시 쓰기 횟수" +#define D_MAC_ADDRESS "MAC 주소" +#define D_MQTT_HOST "MQTT 호스트" +#define D_MQTT_PORT "MQTT 포트" +#define D_MQTT_CLIENT "MQTT 클라이언트" +#define D_MQTT_USER "MQTT 아이디" +#define D_MQTT_TOPIC "MQTT Topic" +#define D_MQTT_GROUP_TOPIC "MQTT Group Topic" +#define D_MQTT_FULL_TOPIC "MQTT Full Topic" +#define D_MDNS_DISCOVERY "mDNS Discovery" +#define D_MDNS_ADVERTISE "mDNS Advertise" +#define D_ESP_CHIP_ID "ESP Chip Id" +#define D_FLASH_CHIP_ID "Flash Chip Id" +#define D_FLASH_CHIP_SIZE "Flash 용량" +#define D_FREE_PROGRAM_SPACE "여유 프로그램 공간" + +#define D_UPGRADE_BY_WEBSERVER "웹 서버에서 업그레이드" +#define D_OTA_URL "OTA Url" +#define D_START_UPGRADE "업그레이드 시작" +#define D_UPGRADE_BY_FILE_UPLOAD "업로드 된 파일로 업그레이드" +#define D_UPLOAD_STARTED "업로드 시작됨" +#define D_UPGRADE_STARTED "업그레이드 시작됨" +#define D_UPLOAD_DONE "업그레이드 완료" +#define D_UPLOAD_ERR_1 "파일이 선택되지 않았습니다" +#define D_UPLOAD_ERR_2 "용량이 충분하지 않습니다" +#define D_UPLOAD_ERR_3 "Magic 바이트가 0xE9가 아닙니다" +#define D_UPLOAD_ERR_4 "플래시 프로그램이 실제 플래시 용량보다 큽니다" +#define D_UPLOAD_ERR_5 "업로드 버퍼가 일치하지 않습니다" +#define D_UPLOAD_ERR_6 "업로드 실패. 로그 3 사용" +#define D_UPLOAD_ERR_7 "업로드 중단" +#define D_UPLOAD_ERR_8 "파일이 유효하지 않습니다" +#define D_UPLOAD_ERR_9 "용량이 초과되었습니다" +#define D_UPLOAD_ERR_10 "RF chip 초기화 실패" +#define D_UPLOAD_ERR_11 "RF chip 삭제 실패" +#define D_UPLOAD_ERR_12 "RF chip 쓰기 실패" +#define D_UPLOAD_ERR_13 "RF 펌웨어 decode 실패" +#define D_UPLOAD_ERROR_CODE "업로드 에러 코드" + +#define D_ENTER_COMMAND "명령 입력" +#define D_ENABLE_WEBLOG_FOR_RESPONSE "응답이 있다면 Weblog 2를 사용" +#define D_NEED_USER_AND_PASSWORD "user=<아이디>&password=<비밀번호> 필요" + +// xdrv_01_mqtt.ino +#define D_FINGERPRINT "TLS 지문 확인..." +#define D_TLS_CONNECT_FAILED_TO "TLS 연결 실패" +#define D_RETRY_IN "재시도 중" +#define D_VERIFIED "지문 확인 완료" +#define D_INSECURE "유효하지 않은 지문으로 연결이 되지 않았습니다" +#define D_CONNECT_FAILED_TO "연결 실패" + +// xplg_wemohue.ino +#define D_MULTICAST_DISABLED "Multicast 사용 불가" +#define D_MULTICAST_REJOINED "Multicast (다시)가입됨" +#define D_MULTICAST_JOIN_FAILED "Multicast 가입 실패" +#define D_FAILED_TO_SEND_RESPONSE "요청 전송 실패" + +#define D_WEMO "WeMo" +#define D_WEMO_BASIC_EVENT "WeMo 기본 이벤트" +#define D_WEMO_EVENT_SERVICE "WeMo 이벤트 서비스" +#define D_WEMO_META_SERVICE "WeMo meta 서비스" +#define D_WEMO_SETUP "WeMo 설정" +#define D_RESPONSE_SENT "요청 전송됨" + +#define D_HUE "Hue" +#define D_HUE_BRIDGE_SETUP "Hue 설정" +#define D_HUE_API_NOT_IMPLEMENTED "Hue API가 포함되지 않음" +#define D_HUE_API "Hue API" +#define D_HUE_POST_ARGS "Hue POST args" +#define D_3_RESPONSE_PACKETS_SENT "3 요청 패킷이 전송됨" + +// xdrv_07_domoticz.ino +#define D_DOMOTICZ_PARAMETERS "Domoticz 상세" +#define D_DOMOTICZ_IDX "Idx" +#define D_DOMOTICZ_KEY_IDX "Key idx" +#define D_DOMOTICZ_SWITCH_IDX "스위치 idx" +#define D_DOMOTICZ_SENSOR_IDX "센서 idx" + #define D_DOMOTICZ_TEMP "온도" + #define D_DOMOTICZ_TEMP_HUM "온도,습도" + #define D_DOMOTICZ_TEMP_HUM_BARO "온도,습도,기압" + #define D_DOMOTICZ_POWER_ENERGY "전력,전력량" + #define D_DOMOTICZ_ILLUMINANCE "조도" + #define D_DOMOTICZ_COUNT "횟수/PM1" + #define D_DOMOTICZ_VOLTAGE "전압/PM2.5" + #define D_DOMOTICZ_CURRENT "전류/PM10" + #define D_DOMOTICZ_AIRQUALITY "공기질" +#define D_DOMOTICZ_UPDATE_TIMER "타이머 갱신" + +// xdrv_09_timers.ino +#define D_CONFIGURE_TIMER "타이머 설정" +#define D_TIMER_PARAMETERS "타이머 상세" +#define D_TIMER_ENABLE "타이머 사용" +#define D_TIMER_ARM "Arm" +#define D_TIMER_TIME "시간" +#define D_TIMER_DAYS "일" +#define D_TIMER_REPEAT "반복" +#define D_TIMER_OUTPUT "출력" +#define D_TIMER_ACTION "행동" + +// xdrv_10_knx.ino +#define D_CONFIGURE_KNX "KNX 설정" +#define D_KNX_PARAMETERS "KNX 상세" +#define D_KNX_GENERAL_CONFIG "일반" +#define D_KNX_PHYSICAL_ADDRESS "물리적 주소" +#define D_KNX_PHYSICAL_ADDRESS_NOTE "( KNX 네트워크 상에서 반드시 고유한 이름이어야 합니다 )" +#define D_KNX_ENABLE "KNX 사용" +#define D_KNX_GROUP_ADDRESS_TO_WRITE "그룹 주소로 데이타를 보냅니다" +#define D_ADD "추가" +#define D_DELETE "삭제" +#define D_REPLY "응답" +#define D_KNX_GROUP_ADDRESS_TO_READ "받은 데이터의 그룹 주소" +#define D_LOG_KNX "KNX: " +#define D_RECEIVED_FROM "다음에서 받음" +#define D_KNX_COMMAND_WRITE "쓰기" +#define D_KNX_COMMAND_READ "읽기" +#define D_KNX_COMMAND_OTHER "기타" +#define D_SENT_TO "다음으로 보내기" +#define D_KNX_WARNING "그룹 주소 ( 0 / 0 / 0 )은 예약되어 사용할 수 없습니다" +#define D_KNX_ENHANCEMENT "커뮤니케이션 강화" +#define D_KNX_TX_SLOT "KNX TX" +#define D_KNX_RX_SLOT "KNX RX" + +// xdrv_03_energy.ino +#define D_ENERGY_TODAY "금일 전력 사용량" +#define D_ENERGY_YESTERDAY "어제 전력 사용량" +#define D_ENERGY_TOTAL "총 전력 사용량" + +// xsns_05_ds18b20.ino +#define D_SENSOR_BUSY "센서가 사용 중" +#define D_SENSOR_CRC_ERROR "센서 CRC 에러" +#define D_SENSORS_FOUND "센서 발견" + +// xsns_06_dht.ino +#define D_TIMEOUT_WAITING_FOR "대기 시간 초과" +#define D_START_SIGNAL_LOW "시작 신호 낮음" +#define D_START_SIGNAL_HIGH "시작 신호 높음" +#define D_PULSE "pulse" +#define D_CHECKSUM_FAILURE "체크섬 실패" + +// xsns_07_sht1x.ino +#define D_SENSOR_DID_NOT_ACK_COMMAND "센서가 ACK 명령을 수행하지 않음" +#define D_SHT1X_FOUND "SHT1X 발견" + +// xsns_18_pms5003.ino +#define D_STANDARD_CONCENTRATION "CF-1 PM" // Standard Particle CF-1 Particle Matter +#define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter +#define D_PARTICALS_BEYOND "입자" + +// xsns_32_mpu6050.ino +#define D_AX_AXIS "Accel. X-Axis" +#define D_AY_AXIS "Accel. Y-Axis" +#define D_AZ_AXIS "Accel. Z-Axis" +#define D_GX_AXIS "Gyro X-Axis" +#define D_GY_AXIS "Gyro Y-Axis" +#define D_GZ_AXIS "Gyro Z-Axis" + +// xsns_34_hx711.ino +#define D_HX_CAL_REMOVE "중량 제거" +#define D_HX_CAL_REFERENCE "참조 중량 로드" +#define D_HX_CAL_DONE "교정됨" +#define D_HX_CAL_FAIL "교정 실패" +#define D_RESET_HX711 "스케일 초기화" +#define D_CONFIGURE_HX711 "스케일 설정" +#define D_HX711_PARAMETERS "스케일 상세" +#define D_ITEM_WEIGHT "아이템 중량" +#define D_REFERENCE_WEIGHT "참조 중량" +#define D_CALIBRATE "교정" +#define D_CALIBRATION "교정" + +//xsns_35_tx20.ino +#define D_TX20_WIND_DIRECTION "풍향" +#define D_TX20_WIND_SPEED "풍속" +#define D_TX20_WIND_SPEED_AVG "평균 풍속" +#define D_TX20_WIND_SPEED_MAX "최대 풍속" +#define D_TX20_NORTH "N" +#define D_TX20_EAST "E" +#define D_TX20_SOUTH "S" +#define D_TX20_WEST "W" + +// sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box +#define D_SENSOR_NONE "없음" +#define D_SENSOR_USER "User" +#define D_SENSOR_DHT11 "DHT11" +#define D_SENSOR_AM2301 "AM2301" +#define D_SENSOR_SI7021 "SI7021" +#define D_SENSOR_DS18X20 "DS18x20" +#define D_SENSOR_I2C_SCL "I2C SCL" +#define D_SENSOR_I2C_SDA "I2C SDA" +#define D_SENSOR_WS2812 "WS2812" +#define D_SENSOR_DFR562 "MP3 Player" +#define D_SENSOR_IRSEND "IRsend" +#define D_SENSOR_SWITCH "Switch" // Suffix "1" +#define D_SENSOR_BUTTON "Button" // Suffix "1" +#define D_SENSOR_RELAY "Relay" // Suffix "1i" +#define D_SENSOR_LED "Led" // Suffix "1i" +#define D_SENSOR_PWM "PWM" // Suffix "1" +#define D_SENSOR_COUNTER "Counter" // Suffix "1" +#define D_SENSOR_IRRECV "IRrecv" +#define D_SENSOR_MHZ_RX "MHZ Rx" +#define D_SENSOR_MHZ_TX "MHZ Tx" +#define D_SENSOR_PZEM004_RX "PZEM004 Rx" +#define D_SENSOR_PZEM016_RX "PZEM016 Rx" +#define D_SENSOR_PZEM017_RX "PZEM017 Rx" +#define D_SENSOR_PZEM0XX_TX "PZEM0XX Tx" +#define D_SENSOR_SAIR_RX "SAir Rx" +#define D_SENSOR_SAIR_TX "SAir Tx" +#define D_SENSOR_SPI_CS "SPI CS" +#define D_SENSOR_SPI_DC "SPI DC" +#define D_SENSOR_BACKLIGHT "BkLight" +#define D_SENSOR_PMS5003 "PMS5003" +#define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" +#define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" +#define D_SENSOR_SBR_RX "SerBr Rx" +#define D_SENSOR_SBR_TX "SerBr Tx" +#define D_SENSOR_SR04_TRIG "SR04 Tri" +#define D_SENSOR_SR04_ECHO "SR04 Ech" +#define D_SENSOR_SDM120_TX "SDMx20 Tx" +#define D_SENSOR_SDM120_RX "SDMx20 Rx" +#define D_SENSOR_SDM630_TX "SDM630 Tx" +#define D_SENSOR_SDM630_RX "SDM630 Rx" +#define D_SENSOR_TM1638_CLK "TM16 CLK" +#define D_SENSOR_TM1638_DIO "TM16 DIO" +#define D_SENSOR_TM1638_STB "TM16 STB" +#define D_SENSOR_HX711_SCK "HX711 SCK" +#define D_SENSOR_HX711_DAT "HX711 DAT" +#define D_SENSOR_TX20_TX "TX20" +#define D_SENSOR_RFSEND "RFSend" +#define D_SENSOR_RFRECV "RFrecv" +#define D_SENSOR_TUYA_TX "Tuya Tx" +#define D_SENSOR_TUYA_RX "Tuya Rx" +#define D_SENSOR_MGC3130_XFER "MGC3130 Xfr" +#define D_SENSOR_MGC3130_RESET "MGC3130 Rst" +#define D_SENSOR_SSPI_MISO "SSPI MISO" +#define D_SENSOR_SSPI_MOSI "SSPI MOSI" +#define D_SENSOR_SSPI_SCLK "SSPI SCLK" +#define D_SENSOR_SSPI_CS "SSPI CS" +#define D_SENSOR_SSPI_DC "SSPI DC" +#define D_SENSOR_RF_SENSOR "RF Sensor" +#define D_SENSOR_AZ_RX "AZ Rx" +#define D_SENSOR_AZ_TX "AZ Tx" +#define D_SENSOR_MAX31855_CS "MX31855 CS" +#define D_SENSOR_MAX31855_CLK "MX31855 CLK" +#define D_SENSOR_MAX31855_DO "MX31855 DO" +#define D_SENSOR_NRG_SEL "HLWBL SEL" // Suffix "i" +#define D_SENSOR_NRG_CF1 "HLWBL CF1" +#define D_SENSOR_HLW_CF "HLW8012 CF" +#define D_SENSOR_HJL_CF "BL0937 CF" +#define D_SENSOR_MCP39F5_TX "MCP39F5 Tx" +#define D_SENSOR_MCP39F5_RX "MCP39F5 Rx" +#define D_SENSOR_MCP39F5_RST "MCP39F5 Rst" +#define D_SENSOR_CSE7766_TX "CSE7766 Tx" +#define D_SENSOR_CSE7766_RX "CSE7766 Rx" +#define D_SENSOR_PN532_TX "PN532 Tx" +#define D_SENSOR_PN532_RX "PN532 Rx" +#define D_SENSOR_SM16716_CLK "SM16716 CLK" +#define D_SENSOR_SM16716_DAT "SM16716 DAT" +#define D_SENSOR_SM16716_POWER "SM16716 PWR" +#define D_SENSOR_MY92X1_DI "MY92x1 DI" +#define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI" +#define D_SENSOR_ARIRFRCV "ALux IrRcv" +#define D_SENSOR_TXD "Serial Tx" +#define D_SENSOR_RXD "Serial Rx" +#define D_SENSOR_ROTARY "Rotary" // Suffix "1A" + +// Units +#define D_UNIT_AMPERE "A" +#define D_UNIT_CENTIMETER "cm" +#define D_UNIT_HERTZ "Hz" +#define D_UNIT_HOUR "시" +#define D_UNIT_INCREMENTS "inc" +#define D_UNIT_KILOGRAM "kg" +#define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" +#define D_UNIT_KILOOHM "kOhm" +#define D_UNIT_KILOWATTHOUR "kWh" +#define D_UNIT_LUX "lx" +#define D_UNIT_MICROGRAM_PER_CUBIC_METER "ug/m3" +#define D_UNIT_MICROMETER "마이크로미터" +#define D_UNIT_MICROSECOND "마이크로초" +#define D_UNIT_MILLIAMPERE "mA" +#define D_UNIT_MILLIMETER "mm" +#define D_UNIT_MILLIMETER_MERCURY "mmHg" +#define D_UNIT_MILLISECOND "밀리초" +#define D_UNIT_MINUTE "분" +#define D_UNIT_PARTS_PER_BILLION "ppb" +#define D_UNIT_PARTS_PER_DECILITER "ppd" +#define D_UNIT_PARTS_PER_MILLION "ppm" +#define D_UNIT_PRESSURE "hPa" +#define D_UNIT_SECOND "초" +#define D_UNIT_SECTORS "섹터" +#define D_UNIT_VA "VA" +#define D_UNIT_VAR "VAr" +#define D_UNIT_VOLT "V" +#define D_UNIT_WATT "W" +#define D_UNIT_WATTHOUR "Wh" +#define D_UNIT_WATT_METER_QUADRAT "W/m²" + +// Log message prefix +#define D_LOG_APPLICATION "APP: " // Application +#define D_LOG_BRIDGE "BRG: " // Bridge +#define D_LOG_CONFIG "CFG: " // Settings +#define D_LOG_COMMAND "CMD: " // Command +#define D_LOG_DEBUG "DBG: " // Debug +#define D_LOG_DHT "DHT: " // DHT sensor +#define D_LOG_DOMOTICZ "DOM: " // Domoticz +#define D_LOG_DSB "DSB: " // DS18xB20 sensor +#define D_LOG_HTTP "HTP: " // HTTP webserver +#define D_LOG_I2C "I2C: " // I2C +#define D_LOG_IRR "IRR: " // Infra Red Received +#define D_LOG_LOG "LOG: " // Logging +#define D_LOG_MODULE "MOD: " // Module +#define D_LOG_MDNS "DNS: " // mDNS +#define D_LOG_MQTT "MQT: " // MQTT +#define D_LOG_OTHER "OTH: " // Other +#define D_LOG_RESULT "RSL: " // Result +#define D_LOG_RFR "RFR: " // RF Received +#define D_LOG_SERIAL "SER: " // Serial +#define D_LOG_SHT1 "SHT: " // SHT1x sensor +#define D_LOG_UPLOAD "UPL: " // Upload +#define D_LOG_UPNP "UPP: " // UPnP +#define D_LOG_WIFI "WIF: " // Wifi + +//SDM220 +#define D_PHASE_ANGLE "Phase Angle" +#define D_IMPORT_ACTIVE "Import Active" +#define D_EXPORT_ACTIVE "Export Active" +#define D_IMPORT_REACTIVE "Import Reactive" +#define D_EXPORT_REACTIVE "Export Reactive" +#define D_TOTAL_REACTIVE "Total Reactive" +#define D_UNIT_KWARH "kVArh" +#define D_UNIT_ANGLE "Deg" + +#endif // _LANGUAGE_KO_KO_H_ diff --git a/sonoff/language/nl-NL.h b/sonoff/language/nl-NL.h index b3bf10989..dd3fb1806 100644 --- a/sonoff/language/nl-NL.h +++ b/sonoff/language/nl-NL.h @@ -1,7 +1,7 @@ /* nl-NL.h - localization for Dutch - Nederland for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends + Copyright (C) 2019 Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -148,6 +148,7 @@ #define D_STOP "Stop" #define D_SUBNET_MASK "Subnet Masker" #define D_SUBSCRIBE_TO "Abonneer op" +#define D_UNSUBSCRIBE_FROM "Unsubscribe from" #define D_SUCCESSFUL "Gelukt" #define D_SUNRISE "Zonsopgang" #define D_SUNSET "Zonsondergang" @@ -253,6 +254,7 @@ #define D_MODULE_PARAMETERS "Module parameters" #define D_MODULE_TYPE "Module soort" +#define D_PULLUP_ENABLE "Geen schakelaar pull-up" #define D_GPIO "GPIO" #define D_SERIAL_IN "Serieel In" #define D_SERIAL_OUT "Serieel Uit" @@ -287,6 +289,8 @@ #define D_TELEMETRY_PERIOD "Telemetry periode" #define D_OTHER_PARAMETERS "Overige parameters" +#define D_TEMPLATE "Template" +#define D_ACTIVATE "Activate" #define D_WEB_ADMIN_PASSWORD "Web Admin Wachtwoord" #define D_MQTT_ENABLE "MQTT ingeschakeld" #define D_FRIENDLY_NAME "Beschrijvende naam" @@ -295,6 +299,14 @@ #define D_SINGLE_DEVICE "een apparaat" #define D_MULTI_DEVICE "meer apparaten" +#define D_CONFIGURE_TEMPLATE "Configure Template" +#define D_TEMPLATE_PARAMETERS "Template parameters" +#define D_TEMPLATE_NAME "Name" +#define D_BASE_TYPE "Based on" +#define D_TEMPLATE_FLAGS "Options" +#define D_ALLOW_ADC0 "ADC0 input" +#define D_ALLOW_PULLUP "User pull-up selection" + #define D_SAVE_CONFIGURATION "Bewaar configuratie" #define D_CONFIGURATION_SAVED "Configuratie opgeslagen" #define D_CONFIGURATION_RESET "Configuratie ge-reset" @@ -480,66 +492,90 @@ #define D_TX20_SOUTH "S" #define D_TX20_WEST "W" -// sonoff_template.h -#define D_SENSOR_NONE "Geen" -#define D_SENSOR_DHT11 "DHT11" -#define D_SENSOR_AM2301 "AM2301" -#define D_SENSOR_SI7021 "SI7021" -#define D_SENSOR_DS18X20 "DS18x20" -#define D_SENSOR_I2C_SCL "I2C SCL" -#define D_SENSOR_I2C_SDA "I2C SDA" -#define D_SENSOR_WS2812 "WS2812" -#define D_SENSOR_DFR562 "MP3 Speler" -#define D_SENSOR_IRSEND "IRsend" -#define D_SENSOR_SWITCH "Switch" // Suffix "1" -#define D_SENSOR_BUTTON "Button" // Suffix "1" -#define D_SENSOR_RELAY "Relais" // Suffix "1i" -#define D_SENSOR_LED "Led" // Suffix "1i" -#define D_SENSOR_PWM "PWM" // Suffix "1" -#define D_SENSOR_COUNTER "Teller" // Suffix "1" -#define D_SENSOR_IRRECV "IRrecv" -#define D_SENSOR_MHZ_RX "MHZ Rx" -#define D_SENSOR_MHZ_TX "MHZ Tx" -#define D_SENSOR_PZEM004_RX "PZEM004 Rx" -#define D_SENSOR_PZEM016_RX "PZEM016 Rx" -#define D_SENSOR_PZEM017_RX "PZEM017 Rx" -#define D_SENSOR_PZEM0XX_TX "PZEM0XX Tx" -#define D_SENSOR_SAIR_RX "SAir Rx" -#define D_SENSOR_SAIR_TX "SAir Tx" -#define D_SENSOR_SPI_CS "SPI CS" -#define D_SENSOR_SPI_DC "SPI DC" -#define D_SENSOR_BACKLIGHT "BkLight" -#define D_SENSOR_PMS5003 "PMS5003" -#define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" -#define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" -#define D_SENSOR_SBR_RX "SerBr Rx" -#define D_SENSOR_SBR_TX "SerBr Tx" -#define D_SENSOR_SR04_TRIG "SR04 Tri" -#define D_SENSOR_SR04_ECHO "SR04 Ech" -#define D_SENSOR_SDM120_TX "SDM120/220 Tx" -#define D_SENSOR_SDM120_RX "SDM120/220 Rx" -#define D_SENSOR_SDM630_TX "SDM630 Tx" -#define D_SENSOR_SDM630_RX "SDM630 Rx" -#define D_SENSOR_TM1638_CLK "TM16 CLK" -#define D_SENSOR_TM1638_DIO "TM16 DIO" -#define D_SENSOR_TM1638_STB "TM16 STB" -#define D_SENSOR_HX711_SCK "HX711 SCK" -#define D_SENSOR_HX711_DAT "HX711 DAT" -#define D_SENSOR_TX20_TX "TX20" -#define D_SENSOR_RFSEND "RFSend" -#define D_SENSOR_RFRECV "RFrecv" -#define D_SENSOR_TUYA_TX "Tuya Tx" -#define D_SENSOR_TUYA_RX "Tuya Rx" -#define D_SENSOR_MGC3130_XFER "MGC3130 Xfer" -#define D_SENSOR_MGC3130_RESET "MGC3130 Reset" -#define D_SENSOR_SSPI_MISO "SSPI MISO" -#define D_SENSOR_SSPI_MOSI "SSPI MOSI" -#define D_SENSOR_SSPI_SCLK "SSPI SCLK" -#define D_SENSOR_SSPI_CS "SSPI CS" -#define D_SENSOR_SSPI_DC "SSPI DC" -#define D_SENSOR_RF_SENSOR "RF Sensor" -#define D_SENSOR_AZ_RX "AZ Rx" -#define D_SENSOR_AZ_TX "AZ Tx" +// sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box +#define D_SENSOR_NONE "Geen" +#define D_SENSOR_USER "Gebruiker" +#define D_SENSOR_DHT11 "DHT11" +#define D_SENSOR_AM2301 "AM2301" +#define D_SENSOR_SI7021 "SI7021" +#define D_SENSOR_DS18X20 "DS18x20" +#define D_SENSOR_I2C_SCL "I2C SCL" +#define D_SENSOR_I2C_SDA "I2C SDA" +#define D_SENSOR_WS2812 "WS2812" +#define D_SENSOR_DFR562 "MP3 Speler" +#define D_SENSOR_IRSEND "IRsend" +#define D_SENSOR_SWITCH "Switch" // Suffix "1" +#define D_SENSOR_BUTTON "Button" // Suffix "1" +#define D_SENSOR_RELAY "Relais" // Suffix "1i" +#define D_SENSOR_LED "Led" // Suffix "1i" +#define D_SENSOR_PWM "PWM" // Suffix "1" +#define D_SENSOR_COUNTER "Teller" // Suffix "1" +#define D_SENSOR_IRRECV "IRrecv" +#define D_SENSOR_MHZ_RX "MHZ Rx" +#define D_SENSOR_MHZ_TX "MHZ Tx" +#define D_SENSOR_PZEM004_RX "PZEM004 Rx" +#define D_SENSOR_PZEM016_RX "PZEM016 Rx" +#define D_SENSOR_PZEM017_RX "PZEM017 Rx" +#define D_SENSOR_PZEM0XX_TX "PZEM0XX Tx" +#define D_SENSOR_SAIR_RX "SAir Rx" +#define D_SENSOR_SAIR_TX "SAir Tx" +#define D_SENSOR_SPI_CS "SPI CS" +#define D_SENSOR_SPI_DC "SPI DC" +#define D_SENSOR_BACKLIGHT "BkLight" +#define D_SENSOR_PMS5003 "PMS5003" +#define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" +#define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" +#define D_SENSOR_SBR_RX "SerBr Rx" +#define D_SENSOR_SBR_TX "SerBr Tx" +#define D_SENSOR_SR04_TRIG "SR04 Tri" +#define D_SENSOR_SR04_ECHO "SR04 Ech" +#define D_SENSOR_SDM120_TX "SDMx20 Tx" +#define D_SENSOR_SDM120_RX "SDMx20 Rx" +#define D_SENSOR_SDM630_TX "SDM630 Tx" +#define D_SENSOR_SDM630_RX "SDM630 Rx" +#define D_SENSOR_TM1638_CLK "TM16 CLK" +#define D_SENSOR_TM1638_DIO "TM16 DIO" +#define D_SENSOR_TM1638_STB "TM16 STB" +#define D_SENSOR_HX711_SCK "HX711 SCK" +#define D_SENSOR_HX711_DAT "HX711 DAT" +#define D_SENSOR_TX20_TX "TX20" +#define D_SENSOR_RFSEND "RFSend" +#define D_SENSOR_RFRECV "RFrecv" +#define D_SENSOR_TUYA_TX "Tuya Tx" +#define D_SENSOR_TUYA_RX "Tuya Rx" +#define D_SENSOR_MGC3130_XFER "MGC3130 Xfr" +#define D_SENSOR_MGC3130_RESET "MGC3130 Rst" +#define D_SENSOR_SSPI_MISO "SSPI MISO" +#define D_SENSOR_SSPI_MOSI "SSPI MOSI" +#define D_SENSOR_SSPI_SCLK "SSPI SCLK" +#define D_SENSOR_SSPI_CS "SSPI CS" +#define D_SENSOR_SSPI_DC "SSPI DC" +#define D_SENSOR_RF_SENSOR "RF Sensor" +#define D_SENSOR_AZ_RX "AZ Rx" +#define D_SENSOR_AZ_TX "AZ Tx" +#define D_SENSOR_MAX31855_CS "MX31855 CS" +#define D_SENSOR_MAX31855_CLK "MX31855 CLK" +#define D_SENSOR_MAX31855_DO "MX31855 DO" +#define D_SENSOR_NRG_SEL "HLWBL SEL" // Suffix "i" +#define D_SENSOR_NRG_CF1 "HLWBL CF1" +#define D_SENSOR_HLW_CF "HLW8012 CF" +#define D_SENSOR_HJL_CF "BL0937 CF" +#define D_SENSOR_MCP39F5_TX "MCP39F5 Tx" +#define D_SENSOR_MCP39F5_RX "MCP39F5 Rx" +#define D_SENSOR_MCP39F5_RST "MCP39F5 Rst" +#define D_SENSOR_CSE7766_TX "CSE7766 Tx" +#define D_SENSOR_CSE7766_RX "CSE7766 Rx" +#define D_SENSOR_PN532_TX "PN532 Tx" +#define D_SENSOR_PN532_RX "PN532 Rx" +#define D_SENSOR_SM16716_CLK "SM16716 CLK" +#define D_SENSOR_SM16716_DAT "SM16716 DAT" +#define D_SENSOR_SM16716_POWER "SM16716 PWR" +#define D_SENSOR_MY92X1_DI "MY92x1 DI" +#define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI" +#define D_SENSOR_ARIRFRCV "ALux IrRcv" +#define D_SENSOR_TXD "Serial Tx" +#define D_SENSOR_RXD "Serial Rx" +#define D_SENSOR_ROTARY "Rotary" // Suffix "1A" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/pl-PL.h b/sonoff/language/pl-PL.h index d80826751..24e2f60ae 100644 --- a/sonoff/language/pl-PL.h +++ b/sonoff/language/pl-PL.h @@ -1,7 +1,7 @@ /* pl-PL-d.h - localization for Polish with diacritics - Poland for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends (translated by roblad - Robert L., upgraded by R. Turala) + Copyright (C) 2019 Theo Arends (translated by roblad - Robert L., upgraded by R. Turala) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -62,7 +62,7 @@ #define D_BRIGHTLIGHT "Jasny" #define D_BSSID "BSSId" #define D_BUTTON "Przycisk" -#define D_BY "przez" // Written by me +#define D_BY "by" // Written by me #define D_BYTES "Bajtow" #define D_CELSIUS "Celsiusza" #define D_CHANNEL "Kanał" @@ -139,7 +139,7 @@ #define D_RESTART_REASON "Przyczyna restartu" #define D_RESTORE "Przywracanie" #define D_RETAINED "Zachowane" -#define D_RULE "Rule" +#define D_RULE "Reguła" #define D_SAVE "Zapisz" #define D_SENSOR "Czujnik" #define D_SSID "SSID" @@ -148,6 +148,7 @@ #define D_STOP "Stop" #define D_SUBNET_MASK "Maska podsieci" #define D_SUBSCRIBE_TO "Subskrybuj do" +#define D_UNSUBSCRIBE_FROM "Unsubscribe from" #define D_SUCCESSFUL "Powodzenie" #define D_SUNRISE "Wschód słońca" #define D_SUNSET "Zachód słońca" @@ -235,7 +236,7 @@ #define D_BUTTON_TOGGLE "Przełącznik" #define D_CONFIGURATION "Konfiguracja" #define D_INFORMATION "Informacje" -#define D_FIRMWARE_UPGRADE "Uaktualnienie oprogramowania" +#define D_FIRMWARE_UPGRADE "Aktualizacja oprogramowania" #define D_CONSOLE "Konsola" #define D_CONFIRM_RESTART "Potwierdź restart" @@ -248,11 +249,12 @@ #define D_CONFIRM_RESET_CONFIGURATION "Potwierdź reset ustawień" #define D_RESET_CONFIGURATION "Reset ustawień" #define D_BACKUP_CONFIGURATION "Kopia ustawień" -#define D_RESTORE_CONFIGURATION "Przywrócenie ustawień" +#define D_RESTORE_CONFIGURATION "Przywracanie ustawień" #define D_MAIN_MENU "Menu główne" -#define D_MODULE_PARAMETERS "Parametery modułu" +#define D_MODULE_PARAMETERS "Parametry modułu" #define D_MODULE_TYPE "Typ modułu" +#define D_PULLUP_ENABLE "No Button/Switch pull-up" #define D_GPIO "GPIO" #define D_SERIAL_IN "Serial In" #define D_SERIAL_OUT "Serial Out" @@ -273,28 +275,38 @@ #define D_AP2_SSID "AP2 SSID" #define D_AP2_PASSWORD "Hasło AP2" -#define D_MQTT_PARAMETERS "Parametery MQTT" +#define D_MQTT_PARAMETERS "Parametry MQTT" #define D_CLIENT "Klient" -#define D_FULL_TOPIC "Pełen temat" +#define D_FULL_TOPIC "Pełny temat" #define D_LOGGING_PARAMETERS "Opcje dziennika" #define D_SERIAL_LOG_LEVEL "Serial poziom dziennika" #define D_WEB_LOG_LEVEL "Web poziom dziennika" #define D_SYS_LOG_LEVEL "System poziom dziennika" -#define D_MORE_DEBUG "Więcej informacji debugujacych" +#define D_MORE_DEBUG "Więcej informacji debug" #define D_SYSLOG_HOST "Syslog host" #define D_SYSLOG_PORT "Syslog port" #define D_TELEMETRY_PERIOD "Okres telemetrii" -#define D_OTHER_PARAMETERS "Inne parametery" +#define D_OTHER_PARAMETERS "Inne parametry" +#define D_TEMPLATE "Template" +#define D_ACTIVATE "Activate" #define D_WEB_ADMIN_PASSWORD "Hasło administratora Web" #define D_MQTT_ENABLE "MQTT aktywne" -#define D_FRIENDLY_NAME "Przyjazna nazwa" +#define D_FRIENDLY_NAME "Twoja nazwa" #define D_BELKIN_WEMO "Belkin WeMo" #define D_HUE_BRIDGE "Hue Bridge" #define D_SINGLE_DEVICE "single device" #define D_MULTI_DEVICE "multi device" +#define D_CONFIGURE_TEMPLATE "Configure Template" +#define D_TEMPLATE_PARAMETERS "Template parameters" +#define D_TEMPLATE_NAME "Name" +#define D_BASE_TYPE "Based on" +#define D_TEMPLATE_FLAGS "Options" +#define D_ALLOW_ADC0 "ADC0 input" +#define D_ALLOW_PULLUP "User pull-up selection" + #define D_SAVE_CONFIGURATION "Zapisz ustawienia" #define D_CONFIGURATION_SAVED "Ustawienia zapisane" #define D_CONFIGURATION_RESET "Ustawienia zresetowane" @@ -390,18 +402,18 @@ #define D_DOMOTICZ_UPDATE_TIMER "Zaktualizuj czasomierz" // xdrv_09_timers.ino -#define D_CONFIGURE_TIMER "Skonfiguruj harmonogram" +#define D_CONFIGURE_TIMER "Konfiguruj harmonogram" #define D_TIMER_PARAMETERS "Parametry harmonogramów" #define D_TIMER_ENABLE "Włącz Harmonogramy" #define D_TIMER_ARM "Włącz" -#define D_TIMER_TIME "Czas" +#define D_TIMER_TIME "Według godziny" #define D_TIMER_DAYS "Dni" #define D_TIMER_REPEAT "Powtarzaj" #define D_TIMER_OUTPUT "Wyjście" #define D_TIMER_ACTION "Akcja" // xdrv_10_knx.ino -#define D_CONFIGURE_KNX "Skonfiguruj KNX" +#define D_CONFIGURE_KNX "Konfiguruj KNX" #define D_KNX_PARAMETERS "Parametry KNX" #define D_KNX_GENERAL_CONFIG "Ogólne" #define D_KNX_PHYSICAL_ADDRESS "Adres Fizyczny" @@ -480,66 +492,90 @@ #define D_TX20_SOUTH "S" #define D_TX20_WEST "W" -// sonoff_template.h -#define D_SENSOR_NONE "Brak" -#define D_SENSOR_DHT11 "DHT11" -#define D_SENSOR_AM2301 "AM2301" -#define D_SENSOR_SI7021 "SI7021" -#define D_SENSOR_DS18X20 "DS18x20" -#define D_SENSOR_I2C_SCL "I2C SCL" -#define D_SENSOR_I2C_SDA "I2C SDA" -#define D_SENSOR_WS2812 "WS2812" -#define D_SENSOR_DFR562 "MP3 Player" -#define D_SENSOR_IRSEND "IRsend" -#define D_SENSOR_SWITCH "Przela" // Suffix "1" -#define D_SENSOR_BUTTON "Przyci" // Suffix "1" -#define D_SENSOR_RELAY "Przek" // Suffix "1i" -#define D_SENSOR_LED "Led" // Suffix "1i" -#define D_SENSOR_PWM "PWM" // Suffix "1" -#define D_SENSOR_COUNTER "Liczni" // Suffix "1" -#define D_SENSOR_IRRECV "IRrecv" -#define D_SENSOR_MHZ_RX "MHZ Rx" -#define D_SENSOR_MHZ_TX "MHZ Tx" -#define D_SENSOR_PZEM004_RX "PZEM004 Rx" -#define D_SENSOR_PZEM016_RX "PZEM016 Rx" -#define D_SENSOR_PZEM017_RX "PZEM017 Rx" -#define D_SENSOR_PZEM0XX_TX "PZEM0XX Tx" -#define D_SENSOR_SAIR_RX "SAir Rx" -#define D_SENSOR_SAIR_TX "SAir Tx" -#define D_SENSOR_SPI_CS "SPI CS" -#define D_SENSOR_SPI_DC "SPI DC" -#define D_SENSOR_BACKLIGHT "BkLight" -#define D_SENSOR_PMS5003 "PMS5003" -#define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" -#define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" -#define D_SENSOR_SBR_RX "SerBr Rx" -#define D_SENSOR_SBR_TX "SerBr Tx" -#define D_SENSOR_SR04_TRIG "SR04 Tri" -#define D_SENSOR_SR04_ECHO "SR04 Ech" -#define D_SENSOR_SDM120_TX "SDM120/220 Tx" -#define D_SENSOR_SDM120_RX "SDM120/220 Rx" -#define D_SENSOR_SDM630_TX "SDM630 Tx" -#define D_SENSOR_SDM630_RX "SDM630 Rx" -#define D_SENSOR_TM1638_CLK "TM16 CLK" -#define D_SENSOR_TM1638_DIO "TM16 DIO" -#define D_SENSOR_TM1638_STB "TM16 STB" -#define D_SENSOR_HX711_SCK "HX711 SCK" -#define D_SENSOR_HX711_DAT "HX711 DAT" -#define D_SENSOR_TX20_TX "TX20" -#define D_SENSOR_RFSEND "RFSend" -#define D_SENSOR_RFRECV "RFrecv" -#define D_SENSOR_TUYA_TX "Tuya Tx" -#define D_SENSOR_TUYA_RX "Tuya Rx" -#define D_SENSOR_MGC3130_XFER "MGC3130 Xfer" -#define D_SENSOR_MGC3130_RESET "MGC3130 Reset" -#define D_SENSOR_SSPI_MISO "SSPI MISO" -#define D_SENSOR_SSPI_MOSI "SSPI MOSI" -#define D_SENSOR_SSPI_SCLK "SSPI SCLK" -#define D_SENSOR_SSPI_CS "SSPI CS" -#define D_SENSOR_SSPI_DC "SSPI DC" -#define D_SENSOR_RF_SENSOR "RF Sensor" -#define D_SENSOR_AZ_RX "AZ Rx" -#define D_SENSOR_AZ_TX "AZ Tx" +// sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box +#define D_SENSOR_NONE "Brak" +#define D_SENSOR_USER "User" +#define D_SENSOR_DHT11 "DHT11" +#define D_SENSOR_AM2301 "AM2301" +#define D_SENSOR_SI7021 "SI7021" +#define D_SENSOR_DS18X20 "DS18x20" +#define D_SENSOR_I2C_SCL "I2C SCL" +#define D_SENSOR_I2C_SDA "I2C SDA" +#define D_SENSOR_WS2812 "WS2812" +#define D_SENSOR_DFR562 "MP3 Player" +#define D_SENSOR_IRSEND "IRsend" +#define D_SENSOR_SWITCH "Przela" // Suffix "1" +#define D_SENSOR_BUTTON "Przyci" // Suffix "1" +#define D_SENSOR_RELAY "Przek" // Suffix "1i" +#define D_SENSOR_LED "Led" // Suffix "1i" +#define D_SENSOR_PWM "PWM" // Suffix "1" +#define D_SENSOR_COUNTER "Liczni" // Suffix "1" +#define D_SENSOR_IRRECV "IRrecv" +#define D_SENSOR_MHZ_RX "MHZ Rx" +#define D_SENSOR_MHZ_TX "MHZ Tx" +#define D_SENSOR_PZEM004_RX "PZEM004 Rx" +#define D_SENSOR_PZEM016_RX "PZEM016 Rx" +#define D_SENSOR_PZEM017_RX "PZEM017 Rx" +#define D_SENSOR_PZEM0XX_TX "PZEM0XX Tx" +#define D_SENSOR_SAIR_RX "SAir Rx" +#define D_SENSOR_SAIR_TX "SAir Tx" +#define D_SENSOR_SPI_CS "SPI CS" +#define D_SENSOR_SPI_DC "SPI DC" +#define D_SENSOR_BACKLIGHT "BkLight" +#define D_SENSOR_PMS5003 "PMS5003" +#define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" +#define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" +#define D_SENSOR_SBR_RX "SerBr Rx" +#define D_SENSOR_SBR_TX "SerBr Tx" +#define D_SENSOR_SR04_TRIG "SR04 Tri" +#define D_SENSOR_SR04_ECHO "SR04 Ech" +#define D_SENSOR_SDM120_TX "SDMx20 Tx" +#define D_SENSOR_SDM120_RX "SDMx20 Rx" +#define D_SENSOR_SDM630_TX "SDM630 Tx" +#define D_SENSOR_SDM630_RX "SDM630 Rx" +#define D_SENSOR_TM1638_CLK "TM16 CLK" +#define D_SENSOR_TM1638_DIO "TM16 DIO" +#define D_SENSOR_TM1638_STB "TM16 STB" +#define D_SENSOR_HX711_SCK "HX711 SCK" +#define D_SENSOR_HX711_DAT "HX711 DAT" +#define D_SENSOR_TX20_TX "TX20" +#define D_SENSOR_RFSEND "RFSend" +#define D_SENSOR_RFRECV "RFrecv" +#define D_SENSOR_TUYA_TX "Tuya Tx" +#define D_SENSOR_TUYA_RX "Tuya Rx" +#define D_SENSOR_MGC3130_XFER "MGC3130 Xfr" +#define D_SENSOR_MGC3130_RESET "MGC3130 Rst" +#define D_SENSOR_SSPI_MISO "SSPI MISO" +#define D_SENSOR_SSPI_MOSI "SSPI MOSI" +#define D_SENSOR_SSPI_SCLK "SSPI SCLK" +#define D_SENSOR_SSPI_CS "SSPI CS" +#define D_SENSOR_SSPI_DC "SSPI DC" +#define D_SENSOR_RF_SENSOR "RF Sensor" +#define D_SENSOR_AZ_RX "AZ Rx" +#define D_SENSOR_AZ_TX "AZ Tx" +#define D_SENSOR_MAX31855_CS "MX31855 CS" +#define D_SENSOR_MAX31855_CLK "MX31855 CLK" +#define D_SENSOR_MAX31855_DO "MX31855 DO" +#define D_SENSOR_NRG_SEL "HLWBL SEL" // Suffix "i" +#define D_SENSOR_NRG_CF1 "HLWBL CF1" +#define D_SENSOR_HLW_CF "HLW8012 CF" +#define D_SENSOR_HJL_CF "BL0937 CF" +#define D_SENSOR_MCP39F5_TX "MCP39F5 Tx" +#define D_SENSOR_MCP39F5_RX "MCP39F5 Rx" +#define D_SENSOR_MCP39F5_RST "MCP39F5 Rst" +#define D_SENSOR_CSE7766_TX "CSE7766 Tx" +#define D_SENSOR_CSE7766_RX "CSE7766 Rx" +#define D_SENSOR_PN532_TX "PN532 Tx" +#define D_SENSOR_PN532_RX "PN532 Rx" +#define D_SENSOR_SM16716_CLK "SM16716 CLK" +#define D_SENSOR_SM16716_DAT "SM16716 DAT" +#define D_SENSOR_SM16716_POWER "SM16716 PWR" +#define D_SENSOR_MY92X1_DI "MY92x1 DI" +#define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI" +#define D_SENSOR_ARIRFRCV "ALux IrRcv" +#define D_SENSOR_TXD "Serial Tx" +#define D_SENSOR_RXD "Serial Rx" +#define D_SENSOR_ROTARY "Rotary" // Suffix "1A" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/pt-BR.h b/sonoff/language/pt-BR.h index 7add978f0..3d24e2dbc 100644 --- a/sonoff/language/pt-BR.h +++ b/sonoff/language/pt-BR.h @@ -1,7 +1,7 @@ /* pt-BR.h - localization for Portuguese - Brazil for Sonoff-Tasmota - Copyright (C) 2018 Fabiano Bovo + Copyright (C) 2019 Fabiano Bovo This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -148,6 +148,7 @@ #define D_STOP "Parar" #define D_SUBNET_MASK "Máscara sub rede" #define D_SUBSCRIBE_TO "Subescrever para" +#define D_UNSUBSCRIBE_FROM "Unsubscribe from" #define D_SUCCESSFUL "Successo" #define D_SUNRISE "Nascer do sol" #define D_SUNSET "Por do sol" @@ -253,6 +254,7 @@ #define D_MODULE_PARAMETERS "Parâmetros do módulo" #define D_MODULE_TYPE "Tipo de módulo" +#define D_PULLUP_ENABLE "No Button/Switch pull-up" #define D_GPIO "GPIO" #define D_SERIAL_IN "Entrada serial" #define D_SERIAL_OUT "Saída serial" @@ -287,6 +289,8 @@ #define D_TELEMETRY_PERIOD "Período de telemetria" #define D_OTHER_PARAMETERS "Outros parâmetros" +#define D_TEMPLATE "Template" +#define D_ACTIVATE "Activate" #define D_WEB_ADMIN_PASSWORD "Senha de WEB Admin" #define D_MQTT_ENABLE "MQTT habilitado" #define D_FRIENDLY_NAME "Nome amigável" @@ -295,6 +299,14 @@ #define D_SINGLE_DEVICE "Dispositivo único" #define D_MULTI_DEVICE "Múltiplos dispositivos" +#define D_CONFIGURE_TEMPLATE "Configure Template" +#define D_TEMPLATE_PARAMETERS "Template parameters" +#define D_TEMPLATE_NAME "Name" +#define D_BASE_TYPE "Based on" +#define D_TEMPLATE_FLAGS "Options" +#define D_ALLOW_ADC0 "ADC0 input" +#define D_ALLOW_PULLUP "User pull-up selection" + #define D_SAVE_CONFIGURATION "Gravar configuração" #define D_CONFIGURATION_SAVED "Configuração gravada" #define D_CONFIGURATION_RESET "Reinicialização da configuração" @@ -480,66 +492,90 @@ #define D_TX20_SOUTH "S" #define D_TX20_WEST "W" -// sonoff_template.h -#define D_SENSOR_NONE "Nenhum" -#define D_SENSOR_DHT11 "DHT11" -#define D_SENSOR_AM2301 "AM2301" -#define D_SENSOR_SI7021 "SI7021" -#define D_SENSOR_DS18X20 "DS18x20" -#define D_SENSOR_I2C_SCL "I2C SCL" -#define D_SENSOR_I2C_SDA "I2C SDA" -#define D_SENSOR_WS2812 "WS2812" -#define D_SENSOR_DFR562 "MP3 Player" -#define D_SENSOR_IRSEND "IRsend" -#define D_SENSOR_SWITCH "Interruptor" // Suffix "1" -#define D_SENSOR_BUTTON "Botão" // Suffix "1" -#define D_SENSOR_RELAY "Relé" // Suffix "1i" -#define D_SENSOR_LED "Led" // Suffix "1i" -#define D_SENSOR_PWM "PWM" // Suffix "1" -#define D_SENSOR_COUNTER "Contador" // Suffix "1" -#define D_SENSOR_IRRECV "IRrecv" -#define D_SENSOR_MHZ_RX "MHz Rx" -#define D_SENSOR_MHZ_TX "MHz Tx" -#define D_SENSOR_PZEM004_RX "PZEM004 Rx" -#define D_SENSOR_PZEM016_RX "PZEM016 Rx" -#define D_SENSOR_PZEM017_RX "PZEM017 Rx" -#define D_SENSOR_PZEM0XX_TX "PZEM0XX Tx" -#define D_SENSOR_SAIR_RX "SAIR Rx" -#define D_SENSOR_SAIR_TX "SAIR Tx" -#define D_SENSOR_SPI_CS "SPI CS" -#define D_SENSOR_SPI_DC "SPI DC" -#define D_SENSOR_BACKLIGHT "Luz de fundo" -#define D_SENSOR_PMS5003 "PMS5003" -#define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" -#define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" -#define D_SENSOR_SBR_RX "SerBr Rx" -#define D_SENSOR_SBR_TX "SerBr Tx" -#define D_SENSOR_SR04_TRIG "SR04 Tri" -#define D_SENSOR_SR04_ECHO "SR04 Ech" -#define D_SENSOR_SDM120_TX "SDM120/220 Tx" -#define D_SENSOR_SDM120_RX "SDM120/220 Rx" -#define D_SENSOR_SDM630_TX "SDM630 Tx" -#define D_SENSOR_SDM630_RX "SDM630 Rx" -#define D_SENSOR_TM1638_CLK "TM16 CLK" -#define D_SENSOR_TM1638_DIO "TM16 DIO" -#define D_SENSOR_TM1638_STB "TM16 STB" -#define D_SENSOR_HX711_SCK "HX711 SCK" -#define D_SENSOR_HX711_DAT "HX711 DAT" -#define D_SENSOR_TX20_TX "TX20" -#define D_SENSOR_RFSEND "RFSend" -#define D_SENSOR_RFRECV "RFrecv" -#define D_SENSOR_TUYA_TX "Tuya Tx" -#define D_SENSOR_TUYA_RX "Tuya Rx" -#define D_SENSOR_MGC3130_XFER "MGC3130 Xfer" -#define D_SENSOR_MGC3130_RESET "MGC3130 Reset" -#define D_SENSOR_SSPI_MISO "SSPI MISO" -#define D_SENSOR_SSPI_MOSI "SSPI MOSI" -#define D_SENSOR_SSPI_SCLK "SSPI SCLK" -#define D_SENSOR_SSPI_CS "SSPI CS" -#define D_SENSOR_SSPI_DC "SSPI DC" -#define D_SENSOR_RF_SENSOR "RF Sensor" -#define D_SENSOR_AZ_RX "AZ Rx" -#define D_SENSOR_AZ_TX "AZ Tx" +// sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box +#define D_SENSOR_NONE "Nenhum" +#define D_SENSOR_USER "User" +#define D_SENSOR_DHT11 "DHT11" +#define D_SENSOR_AM2301 "AM2301" +#define D_SENSOR_SI7021 "SI7021" +#define D_SENSOR_DS18X20 "DS18x20" +#define D_SENSOR_I2C_SCL "I2C SCL" +#define D_SENSOR_I2C_SDA "I2C SDA" +#define D_SENSOR_WS2812 "WS2812" +#define D_SENSOR_DFR562 "MP3 Player" +#define D_SENSOR_IRSEND "IRsend" +#define D_SENSOR_SWITCH "Interruptor" // Suffix "1" +#define D_SENSOR_BUTTON "Botão" // Suffix "1" +#define D_SENSOR_RELAY "Relé" // Suffix "1i" +#define D_SENSOR_LED "Led" // Suffix "1i" +#define D_SENSOR_PWM "PWM" // Suffix "1" +#define D_SENSOR_COUNTER "Contador" // Suffix "1" +#define D_SENSOR_IRRECV "IRrecv" +#define D_SENSOR_MHZ_RX "MHZ Rx" +#define D_SENSOR_MHZ_TX "MHZ Tx" +#define D_SENSOR_PZEM004_RX "PZEM004 Rx" +#define D_SENSOR_PZEM016_RX "PZEM016 Rx" +#define D_SENSOR_PZEM017_RX "PZEM017 Rx" +#define D_SENSOR_PZEM0XX_TX "PZEM0XX Tx" +#define D_SENSOR_SAIR_RX "SAir Rx" +#define D_SENSOR_SAIR_TX "SAir Tx" +#define D_SENSOR_SPI_CS "SPI CS" +#define D_SENSOR_SPI_DC "SPI DC" +#define D_SENSOR_BACKLIGHT "Luz de fundo" +#define D_SENSOR_PMS5003 "PMS5003" +#define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" +#define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" +#define D_SENSOR_SBR_RX "SerBr Rx" +#define D_SENSOR_SBR_TX "SerBr Tx" +#define D_SENSOR_SR04_TRIG "SR04 Tri" +#define D_SENSOR_SR04_ECHO "SR04 Ech" +#define D_SENSOR_SDM120_TX "SDMx20 Tx" +#define D_SENSOR_SDM120_RX "SDMx20 Rx" +#define D_SENSOR_SDM630_TX "SDM630 Tx" +#define D_SENSOR_SDM630_RX "SDM630 Rx" +#define D_SENSOR_TM1638_CLK "TM16 CLK" +#define D_SENSOR_TM1638_DIO "TM16 DIO" +#define D_SENSOR_TM1638_STB "TM16 STB" +#define D_SENSOR_HX711_SCK "HX711 SCK" +#define D_SENSOR_HX711_DAT "HX711 DAT" +#define D_SENSOR_TX20_TX "TX20" +#define D_SENSOR_RFSEND "RFSend" +#define D_SENSOR_RFRECV "RFrecv" +#define D_SENSOR_TUYA_TX "Tuya Tx" +#define D_SENSOR_TUYA_RX "Tuya Rx" +#define D_SENSOR_MGC3130_XFER "MGC3130 Xfr" +#define D_SENSOR_MGC3130_RESET "MGC3130 Rst" +#define D_SENSOR_SSPI_MISO "SSPI MISO" +#define D_SENSOR_SSPI_MOSI "SSPI MOSI" +#define D_SENSOR_SSPI_SCLK "SSPI SCLK" +#define D_SENSOR_SSPI_CS "SSPI CS" +#define D_SENSOR_SSPI_DC "SSPI DC" +#define D_SENSOR_RF_SENSOR "RF Sensor" +#define D_SENSOR_AZ_RX "AZ Rx" +#define D_SENSOR_AZ_TX "AZ Tx" +#define D_SENSOR_MAX31855_CS "MX31855 CS" +#define D_SENSOR_MAX31855_CLK "MX31855 CLK" +#define D_SENSOR_MAX31855_DO "MX31855 DO" +#define D_SENSOR_NRG_SEL "HLWBL SEL" // Suffix "i" +#define D_SENSOR_NRG_CF1 "HLWBL CF1" +#define D_SENSOR_HLW_CF "HLW8012 CF" +#define D_SENSOR_HJL_CF "BL0937 CF" +#define D_SENSOR_MCP39F5_TX "MCP39F5 Tx" +#define D_SENSOR_MCP39F5_RX "MCP39F5 Rx" +#define D_SENSOR_MCP39F5_RST "MCP39F5 Rst" +#define D_SENSOR_CSE7766_TX "CSE7766 Tx" +#define D_SENSOR_CSE7766_RX "CSE7766 Rx" +#define D_SENSOR_PN532_TX "PN532 Tx" +#define D_SENSOR_PN532_RX "PN532 Rx" +#define D_SENSOR_SM16716_CLK "SM16716 CLK" +#define D_SENSOR_SM16716_DAT "SM16716 DAT" +#define D_SENSOR_SM16716_POWER "SM16716 PWR" +#define D_SENSOR_MY92X1_DI "MY92x1 DI" +#define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI" +#define D_SENSOR_ARIRFRCV "ALux IrRcv" +#define D_SENSOR_TXD "Serial Tx" +#define D_SENSOR_RXD "Serial Rx" +#define D_SENSOR_ROTARY "Rotary" // Suffix "1A" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/pt-PT.h b/sonoff/language/pt-PT.h index 9869762b8..d675efe54 100644 --- a/sonoff/language/pt-PT.h +++ b/sonoff/language/pt-PT.h @@ -1,7 +1,7 @@ /* pt-PT.h - localization for Portuguese - Portugal for Sonoff-Tasmota - Copyright (C) 2018 Paulo Paiva + Copyright (C) 2019 Paulo Paiva This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -148,6 +148,7 @@ #define D_STOP "Parar" #define D_SUBNET_MASK "Mascara sub rede" #define D_SUBSCRIBE_TO "Subescrever para" +#define D_UNSUBSCRIBE_FROM "Unsubscribe from" #define D_SUCCESSFUL "Successo" #define D_SUNRISE "Sunrise" #define D_SUNSET "Sunset" @@ -253,6 +254,7 @@ #define D_MODULE_PARAMETERS "Parametros do Módulo" #define D_MODULE_TYPE "Tipo de Módulo" +#define D_PULLUP_ENABLE "No Button/Switch pull-up" #define D_GPIO "GPIO" #define D_SERIAL_IN "Serial Entrada" #define D_SERIAL_OUT "Serial Saída" @@ -287,6 +289,8 @@ #define D_TELEMETRY_PERIOD "Periodo de Telemetria" #define D_OTHER_PARAMETERS "Outros parametros" +#define D_TEMPLATE "Template" +#define D_ACTIVATE "Activate" #define D_WEB_ADMIN_PASSWORD "Palavra Chave de WEB Admin" #define D_MQTT_ENABLE "MQTT habilitado" #define D_FRIENDLY_NAME "Nome amigável" @@ -295,6 +299,14 @@ #define D_SINGLE_DEVICE "dispositivo único" #define D_MULTI_DEVICE "multiplos dispositivos" +#define D_CONFIGURE_TEMPLATE "Configure Template" +#define D_TEMPLATE_PARAMETERS "Template parameters" +#define D_TEMPLATE_NAME "Name" +#define D_BASE_TYPE "Based on" +#define D_TEMPLATE_FLAGS "Options" +#define D_ALLOW_ADC0 "ADC0 input" +#define D_ALLOW_PULLUP "User pull-up selection" + #define D_SAVE_CONFIGURATION "Salvar configuração" #define D_CONFIGURATION_SAVED "Configuração guardada" #define D_CONFIGURATION_RESET "Reinicialização da configuração" @@ -480,66 +492,90 @@ #define D_TX20_SOUTH "S" #define D_TX20_WEST "W" -// sonoff_template.h -#define D_SENSOR_NONE "Nenhum" -#define D_SENSOR_DHT11 "DHT11" -#define D_SENSOR_AM2301 "AM2301" -#define D_SENSOR_SI7021 "SI7021" -#define D_SENSOR_DS18X20 "DS18x20" -#define D_SENSOR_I2C_SCL "I2C SCL" -#define D_SENSOR_I2C_SDA "I2C SDA" -#define D_SENSOR_WS2812 "WS2812" -#define D_SENSOR_DFR562 "MP3 Player" -#define D_SENSOR_IRSEND "IRsend" -#define D_SENSOR_SWITCH "Interruptor" // Suffix "1" -#define D_SENSOR_BUTTON "Botão" // Suffix "1" -#define D_SENSOR_RELAY "Relé" // Suffix "1i" -#define D_SENSOR_LED "Led" // Suffix "1i" -#define D_SENSOR_PWM "PWM" // Suffix "1" -#define D_SENSOR_COUNTER "Contador" // Suffix "1" -#define D_SENSOR_IRRECV "IRrecv" -#define D_SENSOR_MHZ_RX "MHZ Rx" -#define D_SENSOR_MHZ_TX "MHZ Tx" -#define D_SENSOR_PZEM004_RX "PZEM004 Rx" -#define D_SENSOR_PZEM016_RX "PZEM016 Rx" -#define D_SENSOR_PZEM017_RX "PZEM017 Rx" -#define D_SENSOR_PZEM0XX_TX "PZEM0XX Tx" -#define D_SENSOR_SAIR_RX "SAir Rx" -#define D_SENSOR_SAIR_TX "SAir Tx" -#define D_SENSOR_SPI_CS "SPI CS" -#define D_SENSOR_SPI_DC "SPI DC" -#define D_SENSOR_BACKLIGHT "Luz negra" -#define D_SENSOR_PMS5003 "PMS5003" -#define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" -#define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" -#define D_SENSOR_SBR_RX "SerBr Rx" -#define D_SENSOR_SBR_TX "SerBr Tx" -#define D_SENSOR_SR04_TRIG "SR04 Tri" -#define D_SENSOR_SR04_ECHO "SR04 Ech" -#define D_SENSOR_SDM120_TX "SDM120/220 Tx" -#define D_SENSOR_SDM120_RX "SDM120/220 Rx" -#define D_SENSOR_SDM630_TX "SDM630 Tx" -#define D_SENSOR_SDM630_RX "SDM630 Rx" -#define D_SENSOR_TM1638_CLK "TM16 CLK" -#define D_SENSOR_TM1638_DIO "TM16 DIO" -#define D_SENSOR_TM1638_STB "TM16 STB" -#define D_SENSOR_HX711_SCK "HX711 SCK" -#define D_SENSOR_HX711_DAT "HX711 DAT" -#define D_SENSOR_TX20_TX "TX20" -#define D_SENSOR_RFSEND "RFSend" -#define D_SENSOR_RFRECV "RFrecv" -#define D_SENSOR_TUYA_TX "Tuya Tx" -#define D_SENSOR_TUYA_RX "Tuya Rx" -#define D_SENSOR_MGC3130_XFER "MGC3130 Xfer" -#define D_SENSOR_MGC3130_RESET "MGC3130 Reset" -#define D_SENSOR_SSPI_MISO "SSPI MISO" -#define D_SENSOR_SSPI_MOSI "SSPI MOSI" -#define D_SENSOR_SSPI_SCLK "SSPI SCLK" -#define D_SENSOR_SSPI_CS "SSPI CS" -#define D_SENSOR_SSPI_DC "SSPI DC" -#define D_SENSOR_RF_SENSOR "RF Sensor" -#define D_SENSOR_AZ_RX "AZ Rx" -#define D_SENSOR_AZ_TX "AZ Tx" +// sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box +#define D_SENSOR_NONE "Nenhum" +#define D_SENSOR_USER "User" +#define D_SENSOR_DHT11 "DHT11" +#define D_SENSOR_AM2301 "AM2301" +#define D_SENSOR_SI7021 "SI7021" +#define D_SENSOR_DS18X20 "DS18x20" +#define D_SENSOR_I2C_SCL "I2C SCL" +#define D_SENSOR_I2C_SDA "I2C SDA" +#define D_SENSOR_WS2812 "WS2812" +#define D_SENSOR_DFR562 "MP3 Player" +#define D_SENSOR_IRSEND "IRsend" +#define D_SENSOR_SWITCH "Interruptor" // Suffix "1" +#define D_SENSOR_BUTTON "Botão" // Suffix "1" +#define D_SENSOR_RELAY "Relé" // Suffix "1i" +#define D_SENSOR_LED "Led" // Suffix "1i" +#define D_SENSOR_PWM "PWM" // Suffix "1" +#define D_SENSOR_COUNTER "Contador" // Suffix "1" +#define D_SENSOR_IRRECV "IRrecv" +#define D_SENSOR_MHZ_RX "MHZ Rx" +#define D_SENSOR_MHZ_TX "MHZ Tx" +#define D_SENSOR_PZEM004_RX "PZEM004 Rx" +#define D_SENSOR_PZEM016_RX "PZEM016 Rx" +#define D_SENSOR_PZEM017_RX "PZEM017 Rx" +#define D_SENSOR_PZEM0XX_TX "PZEM0XX Tx" +#define D_SENSOR_SAIR_RX "SAir Rx" +#define D_SENSOR_SAIR_TX "SAir Tx" +#define D_SENSOR_SPI_CS "SPI CS" +#define D_SENSOR_SPI_DC "SPI DC" +#define D_SENSOR_BACKLIGHT "Luz negra" +#define D_SENSOR_PMS5003 "PMS5003" +#define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" +#define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" +#define D_SENSOR_SBR_RX "SerBr Rx" +#define D_SENSOR_SBR_TX "SerBr Tx" +#define D_SENSOR_SR04_TRIG "SR04 Tri" +#define D_SENSOR_SR04_ECHO "SR04 Ech" +#define D_SENSOR_SDM120_TX "SDMx20 Tx" +#define D_SENSOR_SDM120_RX "SDMx20 Rx" +#define D_SENSOR_SDM630_TX "SDM630 Tx" +#define D_SENSOR_SDM630_RX "SDM630 Rx" +#define D_SENSOR_TM1638_CLK "TM16 CLK" +#define D_SENSOR_TM1638_DIO "TM16 DIO" +#define D_SENSOR_TM1638_STB "TM16 STB" +#define D_SENSOR_HX711_SCK "HX711 SCK" +#define D_SENSOR_HX711_DAT "HX711 DAT" +#define D_SENSOR_TX20_TX "TX20" +#define D_SENSOR_RFSEND "RFSend" +#define D_SENSOR_RFRECV "RFrecv" +#define D_SENSOR_TUYA_TX "Tuya Tx" +#define D_SENSOR_TUYA_RX "Tuya Rx" +#define D_SENSOR_MGC3130_XFER "MGC3130 Xfr" +#define D_SENSOR_MGC3130_RESET "MGC3130 Rst" +#define D_SENSOR_SSPI_MISO "SSPI MISO" +#define D_SENSOR_SSPI_MOSI "SSPI MOSI" +#define D_SENSOR_SSPI_SCLK "SSPI SCLK" +#define D_SENSOR_SSPI_CS "SSPI CS" +#define D_SENSOR_SSPI_DC "SSPI DC" +#define D_SENSOR_RF_SENSOR "RF Sensor" +#define D_SENSOR_AZ_RX "AZ Rx" +#define D_SENSOR_AZ_TX "AZ Tx" +#define D_SENSOR_MAX31855_CS "MX31855 CS" +#define D_SENSOR_MAX31855_CLK "MX31855 CLK" +#define D_SENSOR_MAX31855_DO "MX31855 DO" +#define D_SENSOR_NRG_SEL "HLWBL SEL" // Suffix "i" +#define D_SENSOR_NRG_CF1 "HLWBL CF1" +#define D_SENSOR_HLW_CF "HLW8012 CF" +#define D_SENSOR_HJL_CF "BL0937 CF" +#define D_SENSOR_MCP39F5_TX "MCP39F5 Tx" +#define D_SENSOR_MCP39F5_RX "MCP39F5 Rx" +#define D_SENSOR_MCP39F5_RST "MCP39F5 Rst" +#define D_SENSOR_CSE7766_TX "CSE7766 Tx" +#define D_SENSOR_CSE7766_RX "CSE7766 Rx" +#define D_SENSOR_PN532_TX "PN532 Tx" +#define D_SENSOR_PN532_RX "PN532 Rx" +#define D_SENSOR_SM16716_CLK "SM16716 CLK" +#define D_SENSOR_SM16716_DAT "SM16716 DAT" +#define D_SENSOR_SM16716_POWER "SM16716 PWR" +#define D_SENSOR_MY92X1_DI "MY92x1 DI" +#define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI" +#define D_SENSOR_ARIRFRCV "ALux IrRcv" +#define D_SENSOR_TXD "Serial Tx" +#define D_SENSOR_RXD "Serial Rx" +#define D_SENSOR_ROTARY "Rotary" // Suffix "1A" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/ru-RU.h b/sonoff/language/ru-RU.h index d0a3f6933..50eabbc35 100644 --- a/sonoff/language/ru-RU.h +++ b/sonoff/language/ru-RU.h @@ -1,7 +1,7 @@ /* ru-RU.h - localization for Russian - Rissia for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends / roman-vn + Copyright (C) 2019 Theo Arends / roman-vn This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -148,6 +148,7 @@ #define D_STOP "Стоп" #define D_SUBNET_MASK "Маска Подсети" #define D_SUBSCRIBE_TO "Подписаться на" +#define D_UNSUBSCRIBE_FROM "Unsubscribe from" #define D_SUCCESSFUL "Успешно" #define D_SUNRISE "Sunrise" #define D_SUNSET "Sunset" @@ -253,6 +254,7 @@ #define D_MODULE_PARAMETERS "Параметры модуля" #define D_MODULE_TYPE "Тип модуля" +#define D_PULLUP_ENABLE "No Button/Switch pull-up" #define D_GPIO "GPIO" #define D_SERIAL_IN "Serial вход" #define D_SERIAL_OUT "Serial выход" @@ -287,6 +289,8 @@ #define D_TELEMETRY_PERIOD "Период телеметрии" #define D_OTHER_PARAMETERS "Параметры Прочие" +#define D_TEMPLATE "Template" +#define D_ACTIVATE "Activate" #define D_WEB_ADMIN_PASSWORD "Пароль Web администратора" #define D_MQTT_ENABLE "MQTT активен" #define D_FRIENDLY_NAME "Дружественное Имя" @@ -295,6 +299,14 @@ #define D_SINGLE_DEVICE "одиночное" #define D_MULTI_DEVICE "мульти" +#define D_CONFIGURE_TEMPLATE "Configure Template" +#define D_TEMPLATE_PARAMETERS "Template parameters" +#define D_TEMPLATE_NAME "Name" +#define D_BASE_TYPE "Based on" +#define D_TEMPLATE_FLAGS "Options" +#define D_ALLOW_ADC0 "ADC0 input" +#define D_ALLOW_PULLUP "User pull-up selection" + #define D_SAVE_CONFIGURATION "Сохранить конфигурацию" #define D_CONFIGURATION_SAVED "Конфигурация сохранена " #define D_CONFIGURATION_RESET "Конфигурация сброшена" @@ -480,66 +492,90 @@ #define D_TX20_SOUTH "S" #define D_TX20_WEST "W" -// sonoff_template.h -#define D_SENSOR_NONE "-нет-" -#define D_SENSOR_DHT11 "DHT11" -#define D_SENSOR_AM2301 "AM2301" -#define D_SENSOR_SI7021 "SI7021" -#define D_SENSOR_DS18X20 "DS18x20" -#define D_SENSOR_I2C_SCL "I2C SCL" -#define D_SENSOR_I2C_SDA "I2C SDA" -#define D_SENSOR_WS2812 "WS2812" -#define D_SENSOR_DFR562 "MP3 Player" -#define D_SENSOR_IRSEND "IRsend" -#define D_SENSOR_SWITCH "Свич" // Suffix "1" -#define D_SENSOR_BUTTON "Кнопка" // Suffix "1" -#define D_SENSOR_RELAY "Реле" // Suffix "1i" -#define D_SENSOR_LED "Led" // Suffix "1i" -#define D_SENSOR_PWM "PWM" // Suffix "1" -#define D_SENSOR_COUNTER "Счетчик" // Suffix "1" -#define D_SENSOR_IRRECV "IRrecv" -#define D_SENSOR_MHZ_RX "MHZ Rx" -#define D_SENSOR_MHZ_TX "MHZ Tx" -#define D_SENSOR_PZEM004_RX "PZEM004 Rx" -#define D_SENSOR_PZEM016_RX "PZEM016 Rx" -#define D_SENSOR_PZEM017_RX "PZEM017 Rx" -#define D_SENSOR_PZEM0XX_TX "PZEM0XX Tx" -#define D_SENSOR_SAIR_RX "SAir Rx" -#define D_SENSOR_SAIR_TX "SAir Tx" -#define D_SENSOR_SPI_CS "SPI CS" -#define D_SENSOR_SPI_DC "SPI DC" -#define D_SENSOR_BACKLIGHT "BkLight" -#define D_SENSOR_PMS5003 "PMS5003" -#define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" -#define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" -#define D_SENSOR_SBR_RX "SerBr Rx" -#define D_SENSOR_SBR_TX "SerBr Tx" -#define D_SENSOR_SR04_TRIG "SR04 Tri" -#define D_SENSOR_SR04_ECHO "SR04 Ech" -#define D_SENSOR_SDM120_TX "SDM120/220 Tx" -#define D_SENSOR_SDM120_RX "SDM120/220 Rx" -#define D_SENSOR_SDM630_TX "SDM630 Tx" -#define D_SENSOR_SDM630_RX "SDM630 Rx" -#define D_SENSOR_TM1638_CLK "TM16 CLK" -#define D_SENSOR_TM1638_DIO "TM16 DIO" -#define D_SENSOR_TM1638_STB "TM16 STB" -#define D_SENSOR_HX711_SCK "HX711 SCK" -#define D_SENSOR_HX711_DAT "HX711 DAT" -#define D_SENSOR_TX20_TX "TX20" -#define D_SENSOR_RFSEND "RFSend" -#define D_SENSOR_RFRECV "RFrecv" -#define D_SENSOR_TUYA_TX "Tuya Tx" -#define D_SENSOR_TUYA_RX "Tuya Rx" -#define D_SENSOR_MGC3130_XFER "MGC3130 Xfer" -#define D_SENSOR_MGC3130_RESET "MGC3130 Reset" -#define D_SENSOR_SSPI_MISO "SSPI MISO" -#define D_SENSOR_SSPI_MOSI "SSPI MOSI" -#define D_SENSOR_SSPI_SCLK "SSPI SCLK" -#define D_SENSOR_SSPI_CS "SSPI CS" -#define D_SENSOR_SSPI_DC "SSPI DC" -#define D_SENSOR_RF_SENSOR "RF Sensor" -#define D_SENSOR_AZ_RX "AZ Rx" -#define D_SENSOR_AZ_TX "AZ Tx" +// sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box +#define D_SENSOR_NONE "-нет-" +#define D_SENSOR_USER "User" +#define D_SENSOR_DHT11 "DHT11" +#define D_SENSOR_AM2301 "AM2301" +#define D_SENSOR_SI7021 "SI7021" +#define D_SENSOR_DS18X20 "DS18x20" +#define D_SENSOR_I2C_SCL "I2C SCL" +#define D_SENSOR_I2C_SDA "I2C SDA" +#define D_SENSOR_WS2812 "WS2812" +#define D_SENSOR_DFR562 "MP3 Player" +#define D_SENSOR_IRSEND "IRsend" +#define D_SENSOR_SWITCH "Свич" // Suffix "1" +#define D_SENSOR_BUTTON "Кнопка" // Suffix "1" +#define D_SENSOR_RELAY "Реле" // Suffix "1i" +#define D_SENSOR_LED "Led" // Suffix "1i" +#define D_SENSOR_PWM "PWM" // Suffix "1" +#define D_SENSOR_COUNTER "Счетчик" // Suffix "1" +#define D_SENSOR_IRRECV "IRrecv" +#define D_SENSOR_MHZ_RX "MHZ Rx" +#define D_SENSOR_MHZ_TX "MHZ Tx" +#define D_SENSOR_PZEM004_RX "PZEM004 Rx" +#define D_SENSOR_PZEM016_RX "PZEM016 Rx" +#define D_SENSOR_PZEM017_RX "PZEM017 Rx" +#define D_SENSOR_PZEM0XX_TX "PZEM0XX Tx" +#define D_SENSOR_SAIR_RX "SAir Rx" +#define D_SENSOR_SAIR_TX "SAir Tx" +#define D_SENSOR_SPI_CS "SPI CS" +#define D_SENSOR_SPI_DC "SPI DC" +#define D_SENSOR_BACKLIGHT "BkLight" +#define D_SENSOR_PMS5003 "PMS5003" +#define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" +#define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" +#define D_SENSOR_SBR_RX "SerBr Rx" +#define D_SENSOR_SBR_TX "SerBr Tx" +#define D_SENSOR_SR04_TRIG "SR04 Tri" +#define D_SENSOR_SR04_ECHO "SR04 Ech" +#define D_SENSOR_SDM120_TX "SDMx20 Tx" +#define D_SENSOR_SDM120_RX "SDMx20 Rx" +#define D_SENSOR_SDM630_TX "SDM630 Tx" +#define D_SENSOR_SDM630_RX "SDM630 Rx" +#define D_SENSOR_TM1638_CLK "TM16 CLK" +#define D_SENSOR_TM1638_DIO "TM16 DIO" +#define D_SENSOR_TM1638_STB "TM16 STB" +#define D_SENSOR_HX711_SCK "HX711 SCK" +#define D_SENSOR_HX711_DAT "HX711 DAT" +#define D_SENSOR_TX20_TX "TX20" +#define D_SENSOR_RFSEND "RFSend" +#define D_SENSOR_RFRECV "RFrecv" +#define D_SENSOR_TUYA_TX "Tuya Tx" +#define D_SENSOR_TUYA_RX "Tuya Rx" +#define D_SENSOR_MGC3130_XFER "MGC3130 Xfr" +#define D_SENSOR_MGC3130_RESET "MGC3130 Rst" +#define D_SENSOR_SSPI_MISO "SSPI MISO" +#define D_SENSOR_SSPI_MOSI "SSPI MOSI" +#define D_SENSOR_SSPI_SCLK "SSPI SCLK" +#define D_SENSOR_SSPI_CS "SSPI CS" +#define D_SENSOR_SSPI_DC "SSPI DC" +#define D_SENSOR_RF_SENSOR "RF Sensor" +#define D_SENSOR_AZ_RX "AZ Rx" +#define D_SENSOR_AZ_TX "AZ Tx" +#define D_SENSOR_MAX31855_CS "MX31855 CS" +#define D_SENSOR_MAX31855_CLK "MX31855 CLK" +#define D_SENSOR_MAX31855_DO "MX31855 DO" +#define D_SENSOR_NRG_SEL "HLWBL SEL" // Suffix "i" +#define D_SENSOR_NRG_CF1 "HLWBL CF1" +#define D_SENSOR_HLW_CF "HLW8012 CF" +#define D_SENSOR_HJL_CF "BL0937 CF" +#define D_SENSOR_MCP39F5_TX "MCP39F5 Tx" +#define D_SENSOR_MCP39F5_RX "MCP39F5 Rx" +#define D_SENSOR_MCP39F5_RST "MCP39F5 Rst" +#define D_SENSOR_CSE7766_TX "CSE7766 Tx" +#define D_SENSOR_CSE7766_RX "CSE7766 Rx" +#define D_SENSOR_PN532_TX "PN532 Tx" +#define D_SENSOR_PN532_RX "PN532 Rx" +#define D_SENSOR_SM16716_CLK "SM16716 CLK" +#define D_SENSOR_SM16716_DAT "SM16716 DAT" +#define D_SENSOR_SM16716_POWER "SM16716 PWR" +#define D_SENSOR_MY92X1_DI "MY92x1 DI" +#define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI" +#define D_SENSOR_ARIRFRCV "ALux IrRcv" +#define D_SENSOR_TXD "Serial Tx" +#define D_SENSOR_RXD "Serial Rx" +#define D_SENSOR_ROTARY "Rotary" // Suffix "1A" // Units #define D_UNIT_AMPERE "А" diff --git a/sonoff/language/sk-SK.h b/sonoff/language/sk-SK.h index f46372426..4f741ac51 100644 --- a/sonoff/language/sk-SK.h +++ b/sonoff/language/sk-SK.h @@ -1,7 +1,7 @@ /* sk-SK.h - localization for Slovak with diacritics - Slovak for Sonoff-Tasmota - Copyright (C) 2018 Vladimír Jendroľ + Copyright (C) 2019 Vladimír Jendroľ This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -148,6 +148,7 @@ #define D_STOP "Stop" #define D_SUBNET_MASK "Maska podsiete" #define D_SUBSCRIBE_TO "Prihlásiť do" +#define D_UNSUBSCRIBE_FROM "Unsubscribe from" #define D_SUCCESSFUL "úspešné." #define D_SUNRISE "Svitanie" #define D_SUNSET "Súmrak" @@ -253,6 +254,7 @@ #define D_MODULE_PARAMETERS "Nastavenia modulu" #define D_MODULE_TYPE "Typ modulu" +#define D_PULLUP_ENABLE "No Button/Switch pull-up" #define D_GPIO "GPIO" #define D_SERIAL_IN "Serial In" #define D_SERIAL_OUT "Serial Out" @@ -287,6 +289,8 @@ #define D_TELEMETRY_PERIOD "Interval telemetrie" #define D_OTHER_PARAMETERS "Ostatné nastavenia" +#define D_TEMPLATE "Template" +#define D_ACTIVATE "Activate" #define D_WEB_ADMIN_PASSWORD "Heslo Web administrátora" #define D_MQTT_ENABLE "MQTT aktívne" #define D_FRIENDLY_NAME "Friendly Name" @@ -295,6 +299,14 @@ #define D_SINGLE_DEVICE "single device" #define D_MULTI_DEVICE "multi device" +#define D_CONFIGURE_TEMPLATE "Configure Template" +#define D_TEMPLATE_PARAMETERS "Template parameters" +#define D_TEMPLATE_NAME "Name" +#define D_BASE_TYPE "Based on" +#define D_TEMPLATE_FLAGS "Options" +#define D_ALLOW_ADC0 "ADC0 input" +#define D_ALLOW_PULLUP "User pull-up selection" + #define D_SAVE_CONFIGURATION "Ulož nastavenia" #define D_CONFIGURATION_SAVED "Nastavenia uložené" #define D_CONFIGURATION_RESET "Nastavenia resetované" @@ -480,66 +492,90 @@ #define D_TX20_SOUTH "J" #define D_TX20_WEST "Z" -// sonoff_template.h -#define D_SENSOR_NONE "Žiaden" -#define D_SENSOR_DHT11 "DHT11" -#define D_SENSOR_AM2301 "AM2301" -#define D_SENSOR_SI7021 "SI7021" -#define D_SENSOR_DS18X20 "DS18x20" -#define D_SENSOR_I2C_SCL "I2C SCL" -#define D_SENSOR_I2C_SDA "I2C SDA" -#define D_SENSOR_WS2812 "WS2812" -#define D_SENSOR_DFR562 "MP3 Player" -#define D_SENSOR_IRSEND "IRsend" -#define D_SENSOR_SWITCH "Spínač" // Suffix "1" -#define D_SENSOR_BUTTON "Tlačidlo" // Suffix "1" -#define D_SENSOR_RELAY "Relé" // Suffix "1i" -#define D_SENSOR_LED "Led" // Suffix "1i" -#define D_SENSOR_PWM "PWM" // Suffix "1", -#define D_SENSOR_COUNTER "Počítadlo" // Suffix "1" -#define D_SENSOR_IRRECV "IRrecv" -#define D_SENSOR_MHZ_RX "MHZ Rx" -#define D_SENSOR_MHZ_TX "MHZ Tx" -#define D_SENSOR_PZEM004_RX "PZEM004 Rx" -#define D_SENSOR_PZEM016_RX "PZEM016 Rx" -#define D_SENSOR_PZEM017_RX "PZEM017 Rx" -#define D_SENSOR_PZEM0XX_TX "PZEM0XX Tx" -#define D_SENSOR_SAIR_RX "SAir Rx" -#define D_SENSOR_SAIR_TX "SAir Tx" -#define D_SENSOR_SPI_CS "SPI CS" -#define D_SENSOR_SPI_DC "SPI DC" -#define D_SENSOR_BACKLIGHT "BkLight" -#define D_SENSOR_PMS5003 "PMS5003" -#define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" -#define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" -#define D_SENSOR_SBR_RX "SerBr Rx" -#define D_SENSOR_SBR_TX "SerBr Tx" -#define D_SENSOR_SR04_TRIG "SR04 Tri" -#define D_SENSOR_SR04_ECHO "SR04 Ech" -#define D_SENSOR_SDM120_TX "SDM120/220 Tx" -#define D_SENSOR_SDM120_RX "SDM120/220 Rx" -#define D_SENSOR_SDM630_TX "SDM630 Tx" -#define D_SENSOR_SDM630_RX "SDM630 Rx" -#define D_SENSOR_TM1638_CLK "TM16 CLK" -#define D_SENSOR_TM1638_DIO "TM16 DIO" -#define D_SENSOR_TM1638_STB "TM16 STB" -#define D_SENSOR_HX711_SCK "HX711 SCK" -#define D_SENSOR_HX711_DAT "HX711 DAT" -#define D_SENSOR_TX20_TX "TX20" -#define D_SENSOR_RFSEND "RFSend" -#define D_SENSOR_RFRECV "RFrecv" -#define D_SENSOR_TUYA_TX "Tuya Tx" -#define D_SENSOR_TUYA_RX "Tuya Rx" -#define D_SENSOR_MGC3130_XFER "MGC3130 Xfer" -#define D_SENSOR_MGC3130_RESET "MGC3130 Reset" -#define D_SENSOR_SSPI_MISO "SSPI MISO" -#define D_SENSOR_SSPI_MOSI "SSPI MOSI" -#define D_SENSOR_SSPI_SCLK "SSPI SCLK" -#define D_SENSOR_SSPI_CS "SSPI CS" -#define D_SENSOR_SSPI_DC "SSPI DC" -#define D_SENSOR_RF_SENSOR "RF Senzor" -#define D_SENSOR_AZ_RX "AZ Rx" -#define D_SENSOR_AZ_TX "AZ Tx" +// sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box +#define D_SENSOR_NONE "Žiaden" +#define D_SENSOR_USER "User" +#define D_SENSOR_DHT11 "DHT11" +#define D_SENSOR_AM2301 "AM2301" +#define D_SENSOR_SI7021 "SI7021" +#define D_SENSOR_DS18X20 "DS18x20" +#define D_SENSOR_I2C_SCL "I2C SCL" +#define D_SENSOR_I2C_SDA "I2C SDA" +#define D_SENSOR_WS2812 "WS2812" +#define D_SENSOR_DFR562 "MP3 Player" +#define D_SENSOR_IRSEND "IRsend" +#define D_SENSOR_SWITCH "Spínač" // Suffix "1" +#define D_SENSOR_BUTTON "Tlačidlo" // Suffix "1" +#define D_SENSOR_RELAY "Relé" // Suffix "1i" +#define D_SENSOR_LED "Led" // Suffix "1i" +#define D_SENSOR_PWM "PWM" // Suffix "1", +#define D_SENSOR_COUNTER "Počítadlo" // Suffix "1" +#define D_SENSOR_IRRECV "IRrecv" +#define D_SENSOR_MHZ_RX "MHZ Rx" +#define D_SENSOR_MHZ_TX "MHZ Tx" +#define D_SENSOR_PZEM004_RX "PZEM004 Rx" +#define D_SENSOR_PZEM016_RX "PZEM016 Rx" +#define D_SENSOR_PZEM017_RX "PZEM017 Rx" +#define D_SENSOR_PZEM0XX_TX "PZEM0XX Tx" +#define D_SENSOR_SAIR_RX "SAir Rx" +#define D_SENSOR_SAIR_TX "SAir Tx" +#define D_SENSOR_SPI_CS "SPI CS" +#define D_SENSOR_SPI_DC "SPI DC" +#define D_SENSOR_BACKLIGHT "BkLight" +#define D_SENSOR_PMS5003 "PMS5003" +#define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" +#define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" +#define D_SENSOR_SBR_RX "SerBr Rx" +#define D_SENSOR_SBR_TX "SerBr Tx" +#define D_SENSOR_SR04_TRIG "SR04 Tri" +#define D_SENSOR_SR04_ECHO "SR04 Ech" +#define D_SENSOR_SDM120_TX "SDMx20 Tx" +#define D_SENSOR_SDM120_RX "SDMx20 Rx" +#define D_SENSOR_SDM630_TX "SDM630 Tx" +#define D_SENSOR_SDM630_RX "SDM630 Rx" +#define D_SENSOR_TM1638_CLK "TM16 CLK" +#define D_SENSOR_TM1638_DIO "TM16 DIO" +#define D_SENSOR_TM1638_STB "TM16 STB" +#define D_SENSOR_HX711_SCK "HX711 SCK" +#define D_SENSOR_HX711_DAT "HX711 DAT" +#define D_SENSOR_TX20_TX "TX20" +#define D_SENSOR_RFSEND "RFSend" +#define D_SENSOR_RFRECV "RFrecv" +#define D_SENSOR_TUYA_TX "Tuya Tx" +#define D_SENSOR_TUYA_RX "Tuya Rx" +#define D_SENSOR_MGC3130_XFER "MGC3130 Xfr" +#define D_SENSOR_MGC3130_RESET "MGC3130 Rst" +#define D_SENSOR_SSPI_MISO "SSPI MISO" +#define D_SENSOR_SSPI_MOSI "SSPI MOSI" +#define D_SENSOR_SSPI_SCLK "SSPI SCLK" +#define D_SENSOR_SSPI_CS "SSPI CS" +#define D_SENSOR_SSPI_DC "SSPI DC" +#define D_SENSOR_RF_SENSOR "RF Senzor" +#define D_SENSOR_AZ_RX "AZ Rx" +#define D_SENSOR_AZ_TX "AZ Tx" +#define D_SENSOR_MAX31855_CS "MX31855 CS" +#define D_SENSOR_MAX31855_CLK "MX31855 CLK" +#define D_SENSOR_MAX31855_DO "MX31855 DO" +#define D_SENSOR_NRG_SEL "HLWBL SEL" // Suffix "i" +#define D_SENSOR_NRG_CF1 "HLWBL CF1" +#define D_SENSOR_HLW_CF "HLW8012 CF" +#define D_SENSOR_HJL_CF "BL0937 CF" +#define D_SENSOR_MCP39F5_TX "MCP39F5 Tx" +#define D_SENSOR_MCP39F5_RX "MCP39F5 Rx" +#define D_SENSOR_MCP39F5_RST "MCP39F5 Rst" +#define D_SENSOR_CSE7766_TX "CSE7766 Tx" +#define D_SENSOR_CSE7766_RX "CSE7766 Rx" +#define D_SENSOR_PN532_TX "PN532 Tx" +#define D_SENSOR_PN532_RX "PN532 Rx" +#define D_SENSOR_SM16716_CLK "SM16716 CLK" +#define D_SENSOR_SM16716_DAT "SM16716 DAT" +#define D_SENSOR_SM16716_POWER "SM16716 PWR" +#define D_SENSOR_MY92X1_DI "MY92x1 DI" +#define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI" +#define D_SENSOR_ARIRFRCV "ALux IrRcv" +#define D_SENSOR_TXD "Serial Tx" +#define D_SENSOR_RXD "Serial Rx" +#define D_SENSOR_ROTARY "Rotary" // Suffix "1A" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/sv-SE.h b/sonoff/language/sv-SE.h index f36162a4b..dd6b31469 100644 --- a/sonoff/language/sv-SE.h +++ b/sonoff/language/sv-SE.h @@ -1,7 +1,7 @@ /* sv-SE.h - localization for Swedish - Svenska for Sonoff-Tasmota - Copyright (C) 2018 Gunnar Norin + Copyright (C) 2019 Gunnar Norin This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -132,11 +132,11 @@ #define D_PROGRAM_FLASH_SIZE "Program-flashstorlek" #define D_PROGRAM_SIZE "Programstorlek" #define D_PROJECT "Projekt" -#define D_RAIN "Rain" +#define D_RAIN "Regn" #define D_RECEIVED "Mottagen" #define D_RESTART "Omstart" #define D_RESTARTING "Startar om" -#define D_RESTART_REASON "Restart Reason" +#define D_RESTART_REASON "Omstartsorsak" #define D_RESTORE "återställ" #define D_RETAINED "bevarad" #define D_RULE "Regel" @@ -148,6 +148,7 @@ #define D_STOP "Stoppa" #define D_SUBNET_MASK "Nätmask" #define D_SUBSCRIBE_TO "Prenumera på" +#define D_UNSUBSCRIBE_FROM "Unsubscribe from" #define D_SUCCESSFUL "Lyckat" #define D_SUNRISE "Soluppgång" #define D_SUNSET "Solnedgång" @@ -168,13 +169,13 @@ #define D_UV_INDEX_2 "Med" #define D_UV_INDEX_3 "Hög" #define D_UV_INDEX_4 "Farligt" -#define D_UV_INDEX_5 "BurnL1/2" -#define D_UV_INDEX_6 "BurnL3" -#define D_UV_INDEX_7 "OoR" // Out of Range +#define D_UV_INDEX_5 "Skadligt" +#define D_UV_INDEX_6 "Extremt" +#define D_UV_INDEX_7 "UO" // Out of Range #define D_UV_LEVEL "UV nivå" #define D_UV_POWER "UV kraft" #define D_VERSION "Version" -#define D_VOLTAGE "Voltage" +#define D_VOLTAGE "Volttal" #define D_WEIGHT "Vikt" #define D_WARMLIGHT "Varm" #define D_WEB_SERVER "Webbserver" @@ -197,7 +198,7 @@ #define D_WPS_FAILED_WITH_STATUS "WPS-konfigurering MISSLYCKADES med status" #define D_ACTIVE_FOR_3_MINUTES "aktiv för 3 minuter" #define D_FAILED_TO_START "misslyckades att starta" -#define D_PATCH_ISSUE_2186 "Patch issue 2186" +#define D_PATCH_ISSUE_2186 "Laga problem 2186" #define D_CONNECTING_TO_AP "Ansluter till AP" #define D_IN_MODE "i läge" #define D_CONNECT_FAILED_NO_IP_ADDRESS "Anslutning misslyckades mottog ingen IP-adress" @@ -253,6 +254,7 @@ #define D_MODULE_PARAMETERS "Modulparameterar" #define D_MODULE_TYPE "Modultyp" +#define D_PULLUP_ENABLE "Ingen knapp/brytare pull-up" #define D_GPIO "GPIO" #define D_SERIAL_IN "Seriell in" #define D_SERIAL_OUT "Seriell ut" @@ -280,13 +282,15 @@ #define D_LOGGING_PARAMETERS "Loggningsparametrar" #define D_SERIAL_LOG_LEVEL "Seriell loggnivå" #define D_WEB_LOG_LEVEL "Webb loggnivå" -#define D_SYS_LOG_LEVEL "Syslog-nivp" +#define D_SYS_LOG_LEVEL "Syslog-nivå" #define D_MORE_DEBUG "Mer debugging" #define D_SYSLOG_HOST "Syslog-värd" #define D_SYSLOG_PORT "Syslog-port" #define D_TELEMETRY_PERIOD "Telemetriperiod" #define D_OTHER_PARAMETERS "Andra parametrar" +#define D_TEMPLATE "Template" +#define D_ACTIVATE "Activate" #define D_WEB_ADMIN_PASSWORD "Webbadmin-lösenord" #define D_MQTT_ENABLE "MQTT aktivera" #define D_FRIENDLY_NAME "Läsbart namn" @@ -295,13 +299,21 @@ #define D_SINGLE_DEVICE "soloenhet" #define D_MULTI_DEVICE "multienhet" +#define D_CONFIGURE_TEMPLATE "Configure Template" +#define D_TEMPLATE_PARAMETERS "Template parameters" +#define D_TEMPLATE_NAME "Name" +#define D_BASE_TYPE "Based on" +#define D_TEMPLATE_FLAGS "Options" +#define D_ALLOW_ADC0 "ADC0 input" +#define D_ALLOW_PULLUP "User pull-up selection" + #define D_SAVE_CONFIGURATION "Spara konfiguration" #define D_CONFIGURATION_SAVED "Konfiguration sparad" #define D_CONFIGURATION_RESET "Konfiguration nollställd" #define D_PROGRAM_VERSION "Programversion" -#define D_BUILD_DATE_AND_TIME "Build datum & tid" -#define D_CORE_AND_SDK_VERSION "Core/SDK Version" +#define D_BUILD_DATE_AND_TIME "Kompilerings datum & tid" +#define D_CORE_AND_SDK_VERSION "Core/SDK version" #define D_FLASH_WRITE_COUNT "Flash-skrivningsräknare" #define D_MAC_ADDRESS "MAC-adress" #define D_MQTT_HOST "MQTT-värd" @@ -338,11 +350,11 @@ #define D_UPLOAD_ERR_11 "Misslyckades rensa RF chip" #define D_UPLOAD_ERR_12 "Misslyckades skriva till RF chip" #define D_UPLOAD_ERR_13 "Misslyckades avkoda RF firmware" -#define D_UPLOAD_ERROR_CODE "Upladdningsfelkod" +#define D_UPLOAD_ERROR_CODE "Uppladdningsfelkod" #define D_ENTER_COMMAND "Ange kommando" -#define D_ENABLE_WEBLOG_FOR_RESPONSE "Aktivera weblog 2 om svar förväntas" -#define D_NEED_USER_AND_PASSWORD "Behöver användarnamn=&lösenord=" +#define D_ENABLE_WEBLOG_FOR_RESPONSE "Aktivera webblogg 2 om svar förväntas" +#define D_NEED_USER_AND_PASSWORD "Behöver user=&password=" // xdrv_01_mqtt.ino #define D_FINGERPRINT "Verifierar TLS fingeravtryck..." @@ -391,14 +403,14 @@ // xdrv_09_timers.ino #define D_CONFIGURE_TIMER "Konfigurera timer" -#define D_TIMER_PARAMETERS "timerparametrar" +#define D_TIMER_PARAMETERS "Timerparametrar" #define D_TIMER_ENABLE "Aktivera timer" #define D_TIMER_ARM "Aktivera" #define D_TIMER_TIME "Tid" #define D_TIMER_DAYS "Dagar" #define D_TIMER_REPEAT "Repetera" -#define D_TIMER_OUTPUT "Output" -#define D_TIMER_ACTION "Action" +#define D_TIMER_OUTPUT "Utgång" +#define D_TIMER_ACTION "Åtgärd" // xdrv_10_knx.ino #define D_CONFIGURE_KNX "Konfigurera KNX" @@ -476,77 +488,101 @@ #define D_TX20_WIND_SPEED_AVG "Vindstyrka medel" #define D_TX20_WIND_SPEED_MAX "Vindstyrka max" #define D_TX20_NORTH "N" -#define D_TX20_EAST "E" +#define D_TX20_EAST "Ö" #define D_TX20_SOUTH "S" -#define D_TX20_WEST "W" +#define D_TX20_WEST "V" -// sonoff_template.h -#define D_SENSOR_NONE "Ingen" -#define D_SENSOR_DHT11 "DHT11" -#define D_SENSOR_AM2301 "AM2301" -#define D_SENSOR_SI7021 "SI7021" -#define D_SENSOR_DS18X20 "DS18x20" -#define D_SENSOR_I2C_SCL "I2C SCL" -#define D_SENSOR_I2C_SDA "I2C SDA" -#define D_SENSOR_WS2812 "WS2812" -#define D_SENSOR_DFR562 "MP3-spelare" -#define D_SENSOR_IRSEND "IRsend" -#define D_SENSOR_SWITCH "Omkopplare" // Suffix "1" -#define D_SENSOR_BUTTON "Knapp" // Suffix "1" -#define D_SENSOR_RELAY "Relä" // Suffix "1i" -#define D_SENSOR_LED "Led" // Suffix "1i" -#define D_SENSOR_PWM "PWM" // Suffix "1" -#define D_SENSOR_COUNTER "Räknare" // Suffix "1" -#define D_SENSOR_IRRECV "IRrecv" -#define D_SENSOR_MHZ_RX "MHZ Rx" -#define D_SENSOR_MHZ_TX "MHZ Tx" -#define D_SENSOR_PZEM004_RX "PZEM004 Rx" -#define D_SENSOR_PZEM016_RX "PZEM016 Rx" -#define D_SENSOR_PZEM017_RX "PZEM017 Rx" -#define D_SENSOR_PZEM0XX_TX "PZEM0XX Tx" -#define D_SENSOR_SAIR_RX "SAir Rx" -#define D_SENSOR_SAIR_TX "SAir Tx" -#define D_SENSOR_SPI_CS "SPI CS" -#define D_SENSOR_SPI_DC "SPI DC" -#define D_SENSOR_BACKLIGHT "BkLight" -#define D_SENSOR_PMS5003 "PMS5003" -#define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" -#define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" -#define D_SENSOR_SBR_RX "SerBr Rx" -#define D_SENSOR_SBR_TX "SerBr Tx" -#define D_SENSOR_SR04_TRIG "SR04 Tri" -#define D_SENSOR_SR04_ECHO "SR04 Ech" -#define D_SENSOR_SDM120_TX "SDM120/220 Tx" -#define D_SENSOR_SDM120_RX "SDM120/220 Rx" -#define D_SENSOR_SDM630_TX "SDM630 Tx" -#define D_SENSOR_SDM630_RX "SDM630 Rx" -#define D_SENSOR_TM1638_CLK "TM16 CLK" -#define D_SENSOR_TM1638_DIO "TM16 DIO" -#define D_SENSOR_TM1638_STB "TM16 STB" -#define D_SENSOR_HX711_SCK "HX711 SCK" -#define D_SENSOR_HX711_DAT "HX711 DAT" -#define D_SENSOR_TX20_TX "TX20" -#define D_SENSOR_RFSEND "RFSend" -#define D_SENSOR_RFRECV "RFrecv" -#define D_SENSOR_TUYA_TX "Tuya Tx" -#define D_SENSOR_TUYA_RX "Tuya Rx" -#define D_SENSOR_MGC3130_XFER "MGC3130 Xfer" -#define D_SENSOR_MGC3130_RESET "MGC3130 Reset" -#define D_SENSOR_SSPI_MISO "SSPI MISO" -#define D_SENSOR_SSPI_MOSI "SSPI MOSI" -#define D_SENSOR_SSPI_SCLK "SSPI SCLK" -#define D_SENSOR_SSPI_CS "SSPI CS" -#define D_SENSOR_SSPI_DC "SSPI DC" -#define D_SENSOR_RF_SENSOR "RF Sensor" -#define D_SENSOR_AZ_RX "AZ Rx" -#define D_SENSOR_AZ_TX "AZ Tx" +// sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box +#define D_SENSOR_NONE "Ingen" +#define D_SENSOR_USER "User" +#define D_SENSOR_DHT11 "DHT11" +#define D_SENSOR_AM2301 "AM2301" +#define D_SENSOR_SI7021 "SI7021" +#define D_SENSOR_DS18X20 "DS18x20" +#define D_SENSOR_I2C_SCL "I2C SCL" +#define D_SENSOR_I2C_SDA "I2C SDA" +#define D_SENSOR_WS2812 "WS2812" +#define D_SENSOR_DFR562 "MP3-spelare" +#define D_SENSOR_IRSEND "IRsend" +#define D_SENSOR_SWITCH "Omkopplare" // Suffix "1" +#define D_SENSOR_BUTTON "Knapp" // Suffix "1" +#define D_SENSOR_RELAY "Relä" // Suffix "1i" +#define D_SENSOR_LED "Led" // Suffix "1i" +#define D_SENSOR_PWM "PWM" // Suffix "1" +#define D_SENSOR_COUNTER "Räknare" // Suffix "1" +#define D_SENSOR_IRRECV "IRrecv" +#define D_SENSOR_MHZ_RX "MHZ Rx" +#define D_SENSOR_MHZ_TX "MHZ Tx" +#define D_SENSOR_PZEM004_RX "PZEM004 Rx" +#define D_SENSOR_PZEM016_RX "PZEM016 Rx" +#define D_SENSOR_PZEM017_RX "PZEM017 Rx" +#define D_SENSOR_PZEM0XX_TX "PZEM0XX Tx" +#define D_SENSOR_SAIR_RX "SAir Rx" +#define D_SENSOR_SAIR_TX "SAir Tx" +#define D_SENSOR_SPI_CS "SPI CS" +#define D_SENSOR_SPI_DC "SPI DC" +#define D_SENSOR_BACKLIGHT "BkLight" +#define D_SENSOR_PMS5003 "PMS5003" +#define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" +#define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" +#define D_SENSOR_SBR_RX "SerBr Rx" +#define D_SENSOR_SBR_TX "SerBr Tx" +#define D_SENSOR_SR04_TRIG "SR04 Tri" +#define D_SENSOR_SR04_ECHO "SR04 Ech" +#define D_SENSOR_SDM120_TX "SDMx20 Tx" +#define D_SENSOR_SDM120_RX "SDMx20 Rx" +#define D_SENSOR_SDM630_TX "SDM630 Tx" +#define D_SENSOR_SDM630_RX "SDM630 Rx" +#define D_SENSOR_TM1638_CLK "TM16 CLK" +#define D_SENSOR_TM1638_DIO "TM16 DIO" +#define D_SENSOR_TM1638_STB "TM16 STB" +#define D_SENSOR_HX711_SCK "HX711 SCK" +#define D_SENSOR_HX711_DAT "HX711 DAT" +#define D_SENSOR_TX20_TX "TX20" +#define D_SENSOR_RFSEND "RFSend" +#define D_SENSOR_RFRECV "RFrecv" +#define D_SENSOR_TUYA_TX "Tuya Tx" +#define D_SENSOR_TUYA_RX "Tuya Rx" +#define D_SENSOR_MGC3130_XFER "MGC3130 Xfr" +#define D_SENSOR_MGC3130_RESET "MGC3130 Rst" +#define D_SENSOR_SSPI_MISO "SSPI MISO" +#define D_SENSOR_SSPI_MOSI "SSPI MOSI" +#define D_SENSOR_SSPI_SCLK "SSPI SCLK" +#define D_SENSOR_SSPI_CS "SSPI CS" +#define D_SENSOR_SSPI_DC "SSPI DC" +#define D_SENSOR_RF_SENSOR "RF Sensor" +#define D_SENSOR_AZ_RX "AZ Rx" +#define D_SENSOR_AZ_TX "AZ Tx" +#define D_SENSOR_MAX31855_CS "MX31855 CS" +#define D_SENSOR_MAX31855_CLK "MX31855 CLK" +#define D_SENSOR_MAX31855_DO "MX31855 DO" +#define D_SENSOR_NRG_SEL "HLWBL SEL" // Suffix "i" +#define D_SENSOR_NRG_CF1 "HLWBL CF1" +#define D_SENSOR_HLW_CF "HLW8012 CF" +#define D_SENSOR_HJL_CF "BL0937 CF" +#define D_SENSOR_MCP39F5_TX "MCP39F5 Tx" +#define D_SENSOR_MCP39F5_RX "MCP39F5 Rx" +#define D_SENSOR_MCP39F5_RST "MCP39F5 Rst" +#define D_SENSOR_CSE7766_TX "CSE7766 Tx" +#define D_SENSOR_CSE7766_RX "CSE7766 Rx" +#define D_SENSOR_PN532_TX "PN532 Tx" +#define D_SENSOR_PN532_RX "PN532 Rx" +#define D_SENSOR_SM16716_CLK "SM16716 CLK" +#define D_SENSOR_SM16716_DAT "SM16716 DAT" +#define D_SENSOR_SM16716_POWER "SM16716 PWR" +#define D_SENSOR_MY92X1_DI "MY92x1 DI" +#define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI" +#define D_SENSOR_ARIRFRCV "ALux IrRcv" +#define D_SENSOR_TXD "Serial Tx" +#define D_SENSOR_RXD "Serial Rx" +#define D_SENSOR_ROTARY "Rotary" // Suffix "1A" // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CENTIMETER "cm" #define D_UNIT_HERTZ "Hz" -#define D_UNIT_HOUR "Hr" -#define D_UNIT_INCREMENTS "inc" +#define D_UNIT_HOUR "Tim" +#define D_UNIT_INCREMENTS "ink" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" #define D_UNIT_KILOOHM "kOhm" @@ -599,12 +635,12 @@ #define D_LOG_WIFI "WIF: " // Wifi //SDM220 -#define D_PHASE_ANGLE "Phase Angle" -#define D_IMPORT_ACTIVE "Import Active" -#define D_EXPORT_ACTIVE "Export Active" -#define D_IMPORT_REACTIVE "Import Reactive" -#define D_EXPORT_REACTIVE "Export Reactive" -#define D_TOTAL_REACTIVE "Total Reactive" +#define D_PHASE_ANGLE "Fasvinkel" +#define D_IMPORT_ACTIVE "Import aktiv" +#define D_EXPORT_ACTIVE "Export aktiv" +#define D_IMPORT_REACTIVE "Import reaktiv" +#define D_EXPORT_REACTIVE "Export reaktiv" +#define D_TOTAL_REACTIVE "Total reaktiv" #define D_UNIT_KWARH "kVArh" #define D_UNIT_ANGLE "Deg" diff --git a/sonoff/language/tr-TR.h b/sonoff/language/tr-TR.h index 06d82b9cb..f6f61ec56 100755 --- a/sonoff/language/tr-TR.h +++ b/sonoff/language/tr-TR.h @@ -1,7 +1,7 @@ /* tr-TR.h - localization for Turkish - Turkey for Sonoff-Tasmota - Copyright (C) 2018 Ali Sait Teke and Theo Arends + Copyright (C) 2019 Ali Sait Teke and Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -148,6 +148,7 @@ #define D_STOP "Durdur" #define D_SUBNET_MASK "Altağ Geçidi Maskesi" #define D_SUBSCRIBE_TO "Abone olunan" +#define D_UNSUBSCRIBE_FROM "Unsubscribe from" #define D_SUCCESSFUL "Başarıyla Tamamlandı" #define D_SUNRISE "Gün doğumu" #define D_SUNSET "Gün batımı" @@ -253,6 +254,7 @@ #define D_MODULE_PARAMETERS "Modül parametreleri" #define D_MODULE_TYPE "Modul türü" +#define D_PULLUP_ENABLE "No Button/Switch pull-up" #define D_GPIO "GPIO" #define D_SERIAL_IN "Serial In" #define D_SERIAL_OUT "Serial Out" @@ -287,6 +289,8 @@ #define D_TELEMETRY_PERIOD "Telemetri peryodu" #define D_OTHER_PARAMETERS "Diğer parametreler" +#define D_TEMPLATE "Template" +#define D_ACTIVATE "Activate" #define D_WEB_ADMIN_PASSWORD "Web Yönetici Şifresi" #define D_MQTT_ENABLE "MQTT aktif" #define D_FRIENDLY_NAME "Kullanıcı Dostu İsim" @@ -295,6 +299,14 @@ #define D_SINGLE_DEVICE "tekli cihaz" #define D_MULTI_DEVICE "çoklu cihaz" +#define D_CONFIGURE_TEMPLATE "Configure Template" +#define D_TEMPLATE_PARAMETERS "Template parameters" +#define D_TEMPLATE_NAME "Name" +#define D_BASE_TYPE "Based on" +#define D_TEMPLATE_FLAGS "Options" +#define D_ALLOW_ADC0 "ADC0 input" +#define D_ALLOW_PULLUP "User pull-up selection" + #define D_SAVE_CONFIGURATION "Ayarları Kaydet" #define D_CONFIGURATION_SAVED "Ayarlar kaydedildi" #define D_CONFIGURATION_RESET "Ayarlar resetlendi" @@ -480,66 +492,90 @@ #define D_TX20_SOUTH "S" #define D_TX20_WEST "W" -// sonoff_template.h -#define D_SENSOR_NONE "None" -#define D_SENSOR_DHT11 "DHT11" -#define D_SENSOR_AM2301 "AM2301" -#define D_SENSOR_SI7021 "SI7021" -#define D_SENSOR_DS18X20 "DS18x20" -#define D_SENSOR_I2C_SCL "I2C SCL" -#define D_SENSOR_I2C_SDA "I2C SDA" -#define D_SENSOR_WS2812 "WS2812" -#define D_SENSOR_DFR562 "MP3 Player" -#define D_SENSOR_IRSEND "IRsend" -#define D_SENSOR_SWITCH "Switch" // Suffix "1" -#define D_SENSOR_BUTTON "Button" // Suffix "1" -#define D_SENSOR_RELAY "Relay" // Suffix "1i" -#define D_SENSOR_LED "Led" // Suffix "1i" -#define D_SENSOR_PWM "PWM" // Suffix "1" -#define D_SENSOR_COUNTER "Counter" // Suffix "1" -#define D_SENSOR_IRRECV "IRrecv" -#define D_SENSOR_MHZ_RX "MHZ Rx" -#define D_SENSOR_MHZ_TX "MHZ Tx" -#define D_SENSOR_PZEM004_RX "PZEM004 Rx" -#define D_SENSOR_PZEM016_RX "PZEM016 Rx" -#define D_SENSOR_PZEM017_RX "PZEM017 Rx" -#define D_SENSOR_PZEM0XX_TX "PZEM0XX Tx" -#define D_SENSOR_SAIR_RX "SAir Rx" -#define D_SENSOR_SAIR_TX "SAir Tx" -#define D_SENSOR_SPI_CS "SPI CS" -#define D_SENSOR_SPI_DC "SPI DC" -#define D_SENSOR_BACKLIGHT "BkLight" -#define D_SENSOR_PMS5003 "PMS5003" -#define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" -#define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" -#define D_SENSOR_SBR_RX "SerBr Rx" -#define D_SENSOR_SBR_TX "SerBr Tx" -#define D_SENSOR_SR04_TRIG "SR04 Tri" -#define D_SENSOR_SR04_ECHO "SR04 Ech" -#define D_SENSOR_SDM120_TX "SDM120/220 Tx" -#define D_SENSOR_SDM120_RX "SDM120/220 Rx" -#define D_SENSOR_SDM630_TX "SDM630 Tx" -#define D_SENSOR_SDM630_RX "SDM630 Rx" -#define D_SENSOR_TM1638_CLK "TM16 CLK" -#define D_SENSOR_TM1638_DIO "TM16 DIO" -#define D_SENSOR_TM1638_STB "TM16 STB" -#define D_SENSOR_HX711_SCK "HX711 SCK" -#define D_SENSOR_HX711_DAT "HX711 DAT" -#define D_SENSOR_TX20_TX "TX20" -#define D_SENSOR_RFSEND "RFSend" -#define D_SENSOR_RFRECV "RFrecv" -#define D_SENSOR_TUYA_TX "Tuya Tx" -#define D_SENSOR_TUYA_RX "Tuya Rx" -#define D_SENSOR_MGC3130_XFER "MGC3130 Xfer" -#define D_SENSOR_MGC3130_RESET "MGC3130 Reset" -#define D_SENSOR_SSPI_MISO "SSPI MISO" -#define D_SENSOR_SSPI_MOSI "SSPI MOSI" -#define D_SENSOR_SSPI_SCLK "SSPI SCLK" -#define D_SENSOR_SSPI_CS "SSPI CS" -#define D_SENSOR_SSPI_DC "SSPI DC" -#define D_SENSOR_RF_SENSOR "RF Sensor" -#define D_SENSOR_AZ_RX "AZ Rx" -#define D_SENSOR_AZ_TX "AZ Tx" +// sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box +#define D_SENSOR_NONE "None" +#define D_SENSOR_USER "User" +#define D_SENSOR_DHT11 "DHT11" +#define D_SENSOR_AM2301 "AM2301" +#define D_SENSOR_SI7021 "SI7021" +#define D_SENSOR_DS18X20 "DS18x20" +#define D_SENSOR_I2C_SCL "I2C SCL" +#define D_SENSOR_I2C_SDA "I2C SDA" +#define D_SENSOR_WS2812 "WS2812" +#define D_SENSOR_DFR562 "MP3 Player" +#define D_SENSOR_IRSEND "IRsend" +#define D_SENSOR_SWITCH "Switch" // Suffix "1" +#define D_SENSOR_BUTTON "Button" // Suffix "1" +#define D_SENSOR_RELAY "Relay" // Suffix "1i" +#define D_SENSOR_LED "Led" // Suffix "1i" +#define D_SENSOR_PWM "PWM" // Suffix "1" +#define D_SENSOR_COUNTER "Counter" // Suffix "1" +#define D_SENSOR_IRRECV "IRrecv" +#define D_SENSOR_MHZ_RX "MHZ Rx" +#define D_SENSOR_MHZ_TX "MHZ Tx" +#define D_SENSOR_PZEM004_RX "PZEM004 Rx" +#define D_SENSOR_PZEM016_RX "PZEM016 Rx" +#define D_SENSOR_PZEM017_RX "PZEM017 Rx" +#define D_SENSOR_PZEM0XX_TX "PZEM0XX Tx" +#define D_SENSOR_SAIR_RX "SAir Rx" +#define D_SENSOR_SAIR_TX "SAir Tx" +#define D_SENSOR_SPI_CS "SPI CS" +#define D_SENSOR_SPI_DC "SPI DC" +#define D_SENSOR_BACKLIGHT "BkLight" +#define D_SENSOR_PMS5003 "PMS5003" +#define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" +#define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" +#define D_SENSOR_SBR_RX "SerBr Rx" +#define D_SENSOR_SBR_TX "SerBr Tx" +#define D_SENSOR_SR04_TRIG "SR04 Tri" +#define D_SENSOR_SR04_ECHO "SR04 Ech" +#define D_SENSOR_SDM120_TX "SDMx20 Tx" +#define D_SENSOR_SDM120_RX "SDMx20 Rx" +#define D_SENSOR_SDM630_TX "SDM630 Tx" +#define D_SENSOR_SDM630_RX "SDM630 Rx" +#define D_SENSOR_TM1638_CLK "TM16 CLK" +#define D_SENSOR_TM1638_DIO "TM16 DIO" +#define D_SENSOR_TM1638_STB "TM16 STB" +#define D_SENSOR_HX711_SCK "HX711 SCK" +#define D_SENSOR_HX711_DAT "HX711 DAT" +#define D_SENSOR_TX20_TX "TX20" +#define D_SENSOR_RFSEND "RFSend" +#define D_SENSOR_RFRECV "RFrecv" +#define D_SENSOR_TUYA_TX "Tuya Tx" +#define D_SENSOR_TUYA_RX "Tuya Rx" +#define D_SENSOR_MGC3130_XFER "MGC3130 Xfr" +#define D_SENSOR_MGC3130_RESET "MGC3130 Rst" +#define D_SENSOR_SSPI_MISO "SSPI MISO" +#define D_SENSOR_SSPI_MOSI "SSPI MOSI" +#define D_SENSOR_SSPI_SCLK "SSPI SCLK" +#define D_SENSOR_SSPI_CS "SSPI CS" +#define D_SENSOR_SSPI_DC "SSPI DC" +#define D_SENSOR_RF_SENSOR "RF Sensor" +#define D_SENSOR_AZ_RX "AZ Rx" +#define D_SENSOR_AZ_TX "AZ Tx" +#define D_SENSOR_MAX31855_CS "MX31855 CS" +#define D_SENSOR_MAX31855_CLK "MX31855 CLK" +#define D_SENSOR_MAX31855_DO "MX31855 DO" +#define D_SENSOR_NRG_SEL "HLWBL SEL" // Suffix "i" +#define D_SENSOR_NRG_CF1 "HLWBL CF1" +#define D_SENSOR_HLW_CF "HLW8012 CF" +#define D_SENSOR_HJL_CF "BL0937 CF" +#define D_SENSOR_MCP39F5_TX "MCP39F5 Tx" +#define D_SENSOR_MCP39F5_RX "MCP39F5 Rx" +#define D_SENSOR_MCP39F5_RST "MCP39F5 Rst" +#define D_SENSOR_CSE7766_TX "CSE7766 Tx" +#define D_SENSOR_CSE7766_RX "CSE7766 Rx" +#define D_SENSOR_PN532_TX "PN532 Tx" +#define D_SENSOR_PN532_RX "PN532 Rx" +#define D_SENSOR_SM16716_CLK "SM16716 CLK" +#define D_SENSOR_SM16716_DAT "SM16716 DAT" +#define D_SENSOR_SM16716_POWER "SM16716 PWR" +#define D_SENSOR_MY92X1_DI "MY92x1 DI" +#define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI" +#define D_SENSOR_ARIRFRCV "ALux IrRcv" +#define D_SENSOR_TXD "Serial Tx" +#define D_SENSOR_RXD "Serial Rx" +#define D_SENSOR_ROTARY "Rotary" // Suffix "1A" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/uk-UK.h b/sonoff/language/uk-UK.h index 509c90753..71b7665c1 100644 --- a/sonoff/language/uk-UK.h +++ b/sonoff/language/uk-UK.h @@ -1,7 +1,7 @@ /* uk-UK.h - localization for Ukrainian - Ukrain for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends / vadym-adik + Copyright (C) 2019 Theo Arends / vadym-adik This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -148,6 +148,7 @@ #define D_STOP "Стоп" #define D_SUBNET_MASK "Маска Підмережі" #define D_SUBSCRIBE_TO "Підписатись на" +#define D_UNSUBSCRIBE_FROM "Unsubscribe from" #define D_SUCCESSFUL "Успішно" #define D_SUNRISE "Схід сонця" #define D_SUNSET "Захід сонця" @@ -253,6 +254,7 @@ #define D_MODULE_PARAMETERS "Параметри модулю" #define D_MODULE_TYPE "Тип модулю" +#define D_PULLUP_ENABLE "No Button/Switch pull-up" #define D_GPIO "GPIO" #define D_SERIAL_IN "Serial вхід" #define D_SERIAL_OUT "Serial вихід" @@ -287,6 +289,8 @@ #define D_TELEMETRY_PERIOD "Період телеметрії" #define D_OTHER_PARAMETERS "Параметри Інше" +#define D_TEMPLATE "Template" +#define D_ACTIVATE "Activate" #define D_WEB_ADMIN_PASSWORD "Гасло Web адміністратора" #define D_MQTT_ENABLE "MQTT активний" #define D_FRIENDLY_NAME "Дружнє Ім'я" @@ -295,6 +299,14 @@ #define D_SINGLE_DEVICE "одиночне" #define D_MULTI_DEVICE "мульти" +#define D_CONFIGURE_TEMPLATE "Configure Template" +#define D_TEMPLATE_PARAMETERS "Template parameters" +#define D_TEMPLATE_NAME "Name" +#define D_BASE_TYPE "Based on" +#define D_TEMPLATE_FLAGS "Options" +#define D_ALLOW_ADC0 "ADC0 input" +#define D_ALLOW_PULLUP "User pull-up selection" + #define D_SAVE_CONFIGURATION "Зберегти конфігурацію" #define D_CONFIGURATION_SAVED "Конфігурація збережена " #define D_CONFIGURATION_RESET "Конфігурація скинута" @@ -480,66 +492,90 @@ #define D_TX20_SOUTH "S" #define D_TX20_WEST "W" -// sonoff_template.h -#define D_SENSOR_NONE "-відсутньо-" -#define D_SENSOR_DHT11 "DHT11" -#define D_SENSOR_AM2301 "AM2301" -#define D_SENSOR_SI7021 "SI7021" -#define D_SENSOR_DS18X20 "DS18x20" -#define D_SENSOR_I2C_SCL "I2C SCL" -#define D_SENSOR_I2C_SDA "I2C SDA" -#define D_SENSOR_WS2812 "WS2812" -#define D_SENSOR_DFR562 "MP3 Player" -#define D_SENSOR_IRSEND "IRsend" -#define D_SENSOR_SWITCH "Перемикач" // Suffix "1" -#define D_SENSOR_BUTTON "Кнопка" // Suffix "1" -#define D_SENSOR_RELAY "Реле" // Suffix "1i" -#define D_SENSOR_LED "Led" // Suffix "1i" -#define D_SENSOR_PWM "PWM" // Suffix "1" -#define D_SENSOR_COUNTER "Лічильник" // Suffix "1" -#define D_SENSOR_IRRECV "IRrecv" -#define D_SENSOR_MHZ_RX "MHZ Rx" -#define D_SENSOR_MHZ_TX "MHZ Tx" -#define D_SENSOR_PZEM004_RX "PZEM004 Rx" -#define D_SENSOR_PZEM016_RX "PZEM016 Rx" -#define D_SENSOR_PZEM017_RX "PZEM017 Rx" -#define D_SENSOR_PZEM0XX_TX "PZEM0XX Tx" -#define D_SENSOR_SAIR_RX "SAir Rx" -#define D_SENSOR_SAIR_TX "SAir Tx" -#define D_SENSOR_SPI_CS "SPI CS" -#define D_SENSOR_SPI_DC "SPI DC" -#define D_SENSOR_BACKLIGHT "BkLight" -#define D_SENSOR_PMS5003 "PMS5003" -#define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" -#define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" -#define D_SENSOR_SBR_RX "SerBr Rx" -#define D_SENSOR_SBR_TX "SerBr Tx" -#define D_SENSOR_SR04_TRIG "SR04 Tri" -#define D_SENSOR_SR04_ECHO "SR04 Ech" -#define D_SENSOR_SDM120_TX "SDM120/220 Tx" -#define D_SENSOR_SDM120_RX "SDM120/220 Rx" -#define D_SENSOR_SDM630_TX "SDM630 Tx" -#define D_SENSOR_SDM630_RX "SDM630 Rx" -#define D_SENSOR_TM1638_CLK "TM16 CLK" -#define D_SENSOR_TM1638_DIO "TM16 DIO" -#define D_SENSOR_TM1638_STB "TM16 STB" -#define D_SENSOR_HX711_SCK "HX711 SCK" -#define D_SENSOR_HX711_DAT "HX711 DAT" -#define D_SENSOR_TX20_TX "TX20" -#define D_SENSOR_RFSEND "RFSend" -#define D_SENSOR_RFRECV "RFrecv" -#define D_SENSOR_TUYA_TX "Tuya Tx" -#define D_SENSOR_TUYA_RX "Tuya Rx" -#define D_SENSOR_MGC3130_XFER "MGC3130 Xfer" -#define D_SENSOR_MGC3130_RESET "MGC3130 Reset" -#define D_SENSOR_SSPI_MISO "SSPI MISO" -#define D_SENSOR_SSPI_MOSI "SSPI MOSI" -#define D_SENSOR_SSPI_SCLK "SSPI SCLK" -#define D_SENSOR_SSPI_CS "SSPI CS" -#define D_SENSOR_SSPI_DC "SSPI DC" -#define D_SENSOR_RF_SENSOR "RF Sensor" -#define D_SENSOR_AZ_RX "AZ Rx" -#define D_SENSOR_AZ_TX "AZ Tx" +// sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box +#define D_SENSOR_NONE "-відсутньо-" +#define D_SENSOR_USER "User" +#define D_SENSOR_DHT11 "DHT11" +#define D_SENSOR_AM2301 "AM2301" +#define D_SENSOR_SI7021 "SI7021" +#define D_SENSOR_DS18X20 "DS18x20" +#define D_SENSOR_I2C_SCL "I2C SCL" +#define D_SENSOR_I2C_SDA "I2C SDA" +#define D_SENSOR_WS2812 "WS2812" +#define D_SENSOR_DFR562 "MP3 Player" +#define D_SENSOR_IRSEND "IRsend" +#define D_SENSOR_SWITCH "Перемикач" // Suffix "1" +#define D_SENSOR_BUTTON "Кнопка" // Suffix "1" +#define D_SENSOR_RELAY "Реле" // Suffix "1i" +#define D_SENSOR_LED "Led" // Suffix "1i" +#define D_SENSOR_PWM "PWM" // Suffix "1" +#define D_SENSOR_COUNTER "Лічильник" // Suffix "1" +#define D_SENSOR_IRRECV "IRrecv" +#define D_SENSOR_MHZ_RX "MHZ Rx" +#define D_SENSOR_MHZ_TX "MHZ Tx" +#define D_SENSOR_PZEM004_RX "PZEM004 Rx" +#define D_SENSOR_PZEM016_RX "PZEM016 Rx" +#define D_SENSOR_PZEM017_RX "PZEM017 Rx" +#define D_SENSOR_PZEM0XX_TX "PZEM0XX Tx" +#define D_SENSOR_SAIR_RX "SAir Rx" +#define D_SENSOR_SAIR_TX "SAir Tx" +#define D_SENSOR_SPI_CS "SPI CS" +#define D_SENSOR_SPI_DC "SPI DC" +#define D_SENSOR_BACKLIGHT "BkLight" +#define D_SENSOR_PMS5003 "PMS5003" +#define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" +#define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" +#define D_SENSOR_SBR_RX "SerBr Rx" +#define D_SENSOR_SBR_TX "SerBr Tx" +#define D_SENSOR_SR04_TRIG "SR04 Tri" +#define D_SENSOR_SR04_ECHO "SR04 Ech" +#define D_SENSOR_SDM120_TX "SDMx20 Tx" +#define D_SENSOR_SDM120_RX "SDMx20 Rx" +#define D_SENSOR_SDM630_TX "SDM630 Tx" +#define D_SENSOR_SDM630_RX "SDM630 Rx" +#define D_SENSOR_TM1638_CLK "TM16 CLK" +#define D_SENSOR_TM1638_DIO "TM16 DIO" +#define D_SENSOR_TM1638_STB "TM16 STB" +#define D_SENSOR_HX711_SCK "HX711 SCK" +#define D_SENSOR_HX711_DAT "HX711 DAT" +#define D_SENSOR_TX20_TX "TX20" +#define D_SENSOR_RFSEND "RFSend" +#define D_SENSOR_RFRECV "RFrecv" +#define D_SENSOR_TUYA_TX "Tuya Tx" +#define D_SENSOR_TUYA_RX "Tuya Rx" +#define D_SENSOR_MGC3130_XFER "MGC3130 Xfr" +#define D_SENSOR_MGC3130_RESET "MGC3130 Rst" +#define D_SENSOR_SSPI_MISO "SSPI MISO" +#define D_SENSOR_SSPI_MOSI "SSPI MOSI" +#define D_SENSOR_SSPI_SCLK "SSPI SCLK" +#define D_SENSOR_SSPI_CS "SSPI CS" +#define D_SENSOR_SSPI_DC "SSPI DC" +#define D_SENSOR_RF_SENSOR "RF Sensor" +#define D_SENSOR_AZ_RX "AZ Rx" +#define D_SENSOR_AZ_TX "AZ Tx" +#define D_SENSOR_MAX31855_CS "MX31855 CS" +#define D_SENSOR_MAX31855_CLK "MX31855 CLK" +#define D_SENSOR_MAX31855_DO "MX31855 DO" +#define D_SENSOR_NRG_SEL "HLWBL SEL" // Suffix "i" +#define D_SENSOR_NRG_CF1 "HLWBL CF1" +#define D_SENSOR_HLW_CF "HLW8012 CF" +#define D_SENSOR_HJL_CF "BL0937 CF" +#define D_SENSOR_MCP39F5_TX "MCP39F5 Tx" +#define D_SENSOR_MCP39F5_RX "MCP39F5 Rx" +#define D_SENSOR_MCP39F5_RST "MCP39F5 Rst" +#define D_SENSOR_CSE7766_TX "CSE7766 Tx" +#define D_SENSOR_CSE7766_RX "CSE7766 Rx" +#define D_SENSOR_PN532_TX "PN532 Tx" +#define D_SENSOR_PN532_RX "PN532 Rx" +#define D_SENSOR_SM16716_CLK "SM16716 CLK" +#define D_SENSOR_SM16716_DAT "SM16716 DAT" +#define D_SENSOR_SM16716_POWER "SM16716 PWR" +#define D_SENSOR_MY92X1_DI "MY92x1 DI" +#define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI" +#define D_SENSOR_ARIRFRCV "ALux IrRcv" +#define D_SENSOR_TXD "Serial Tx" +#define D_SENSOR_RXD "Serial Rx" +#define D_SENSOR_ROTARY "Rotary" // Suffix "1A" // Units #define D_UNIT_AMPERE "А" diff --git a/sonoff/language/zh-CN.h b/sonoff/language/zh-CN.h index 05bedca81..586591278 100644 --- a/sonoff/language/zh-CN.h +++ b/sonoff/language/zh-CN.h @@ -1,7 +1,7 @@ /* zh-CN.h - localization for Chinese (Simplified) - China for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends (translated by killadm) + Copyright (C) 2019 Theo Arends (translated by killadm) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -65,7 +65,7 @@ #define D_BY "汉化: killadm 作者:" // Written by me #define D_BYTES "大小:" #define D_CELSIUS "摄氏" -#define D_CHANNEL "Channel" +#define D_CHANNEL "频道" #define D_CO2 "二氧化碳" #define D_CODE "代码" // Button code #define D_COLDLIGHT "冷" @@ -132,7 +132,7 @@ #define D_PROGRAM_FLASH_SIZE "固件 Flash 大小" #define D_PROGRAM_SIZE "固件大小" #define D_PROJECT "项目:" -#define D_RAIN "Rain" +#define D_RAIN "降水量" #define D_RECEIVED "已接收" #define D_RESTART "重启" #define D_RESTARTING "正在重启" @@ -148,6 +148,7 @@ #define D_STOP "停止" #define D_SUBNET_MASK "子网掩码" #define D_SUBSCRIBE_TO "订阅" +#define D_UNSUBSCRIBE_FROM "退订" #define D_SUCCESSFUL "成功" #define D_SUNRISE "日出" #define D_SUNSET "日落" @@ -164,18 +165,18 @@ #define D_USER "用户名" #define D_UTC_TIME "UTC" #define D_UV_INDEX "紫外线指数" -#define D_UV_INDEX_1 "Low" -#define D_UV_INDEX_2 "Mid" -#define D_UV_INDEX_3 "High" -#define D_UV_INDEX_4 "Danger" +#define D_UV_INDEX_1 "低" +#define D_UV_INDEX_2 "中" +#define D_UV_INDEX_3 "高" +#define D_UV_INDEX_4 "危险" #define D_UV_INDEX_5 "BurnL1/2" #define D_UV_INDEX_6 "BurnL3" #define D_UV_INDEX_7 "OoR" #define D_UV_LEVEL "紫外线水平" -#define D_UV_POWER "UV Power" +#define D_UV_POWER "紫外线功率 " #define D_VERSION "版本" #define D_VOLTAGE "电压" -#define D_WEIGHT "Weight" +#define D_WEIGHT "重量" #define D_WARMLIGHT "暖" #define D_WEB_SERVER "Web Server" @@ -218,7 +219,7 @@ #define D_ERASED_SECTOR "擦除扇区" // webserver.ino -#define D_NOSCRIPT "To use Tasmota, please enable JavaScript" +#define D_NOSCRIPT "Tasmota要求浏览器支持 JavaScript" #define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "当前是精简版固件 - 请升级" #define D_WEBSERVER_ACTIVE_ON "Web 服务器地址:" #define D_WITH_IP_ADDRESS "IP 地址:" @@ -253,6 +254,7 @@ #define D_MODULE_PARAMETERS "模块设置" #define D_MODULE_TYPE "模块类型" +#define D_PULLUP_ENABLE "没有按钮/开关上拉" #define D_GPIO "GPIO" #define D_SERIAL_IN "串口输入(RX)" #define D_SERIAL_OUT "串口输出(TX)" @@ -287,6 +289,8 @@ #define D_TELEMETRY_PERIOD "上报周期" #define D_OTHER_PARAMETERS "其他设置" +#define D_TEMPLATE "模板" +#define D_ACTIVATE "启用" #define D_WEB_ADMIN_PASSWORD "WEB 管理密码" #define D_MQTT_ENABLE "启用MQTT" #define D_FRIENDLY_NAME "昵称" @@ -295,6 +299,14 @@ #define D_SINGLE_DEVICE "单设备" #define D_MULTI_DEVICE "多设备" +#define D_CONFIGURE_TEMPLATE "模板配置" +#define D_TEMPLATE_PARAMETERS "模板参数" +#define D_TEMPLATE_NAME "名称" +#define D_BASE_TYPE "基于" +#define D_TEMPLATE_FLAGS "选项" +#define D_ALLOW_ADC0 "ADC0 输入" +#define D_ALLOW_PULLUP "用户上拉选择" + #define D_SAVE_CONFIGURATION "保存设置" #define D_CONFIGURATION_SAVED "设置已保存" #define D_CONFIGURATION_RESET "设置已重置" @@ -450,104 +462,128 @@ #define D_PARTICALS_BEYOND "颗粒物直径大于" // xsns_32_mpu6050.ino -#define D_AX_AXIS "Accel. X-Axis" -#define D_AY_AXIS "Accel. Y-Axis" -#define D_AZ_AXIS "Accel. Z-Axis" -#define D_GX_AXIS "Gyro X-Axis" -#define D_GY_AXIS "Gyro Y-Axis" -#define D_GZ_AXIS "Gyro Z-Axis" +#define D_AX_AXIS "加速度计X轴分量" +#define D_AY_AXIS "加速度计Y轴分量" +#define D_AZ_AXIS "加速度计Z轴分量" +#define D_GX_AXIS "绕X轴旋转的角速度" +#define D_GY_AXIS "绕Y轴旋转的角速度" +#define D_GZ_AXIS "绕Z轴旋转的角速度" // xsns_34_hx711.ino -#define D_HX_CAL_REMOVE "Remove weigth" -#define D_HX_CAL_REFERENCE "Load reference weigth" -#define D_HX_CAL_DONE "Calibrated" -#define D_HX_CAL_FAIL "Calibration failed" -#define D_RESET_HX711 "Reset Scale" -#define D_CONFIGURE_HX711 "Configure Scale" -#define D_HX711_PARAMETERS "Scale parameters" -#define D_ITEM_WEIGHT "Item weight" -#define D_REFERENCE_WEIGHT "Reference weigth" -#define D_CALIBRATE "Calibrate" -#define D_CALIBRATION "Calibration" +#define D_HX_CAL_REMOVE "去除重量" +#define D_HX_CAL_REFERENCE "加载参考重量" +#define D_HX_CAL_DONE "已校准" +#define D_HX_CAL_FAIL "校准失败" +#define D_RESET_HX711 "秤重置" +#define D_CONFIGURE_HX711 "秤配置" +#define D_HX711_PARAMETERS "秤参数" +#define D_ITEM_WEIGHT "物品中粮" +#define D_REFERENCE_WEIGHT "参考重量" +#define D_CALIBRATE "校准" +#define D_CALIBRATION "校准" //xsns_35_tx20.ino -#define D_TX20_WIND_DIRECTION "Wind Direction" -#define D_TX20_WIND_SPEED "Wind Speed" -#define D_TX20_WIND_SPEED_AVG "Wind Speed Avg" -#define D_TX20_WIND_SPEED_MAX "Wind Speed Max" -#define D_TX20_NORTH "N" -#define D_TX20_EAST "E" -#define D_TX20_SOUTH "S" -#define D_TX20_WEST "W" +#define D_TX20_WIND_DIRECTION "风向" +#define D_TX20_WIND_SPEED "风速" +#define D_TX20_WIND_SPEED_AVG "平均风速" +#define D_TX20_WIND_SPEED_MAX "最高风速" +#define D_TX20_NORTH "北" +#define D_TX20_EAST "东" +#define D_TX20_SOUTH "南" +#define D_TX20_WEST "西" -// sonoff_template.h -#define D_SENSOR_NONE "无" -#define D_SENSOR_DHT11 "DHT11" -#define D_SENSOR_AM2301 "AM2301" -#define D_SENSOR_SI7021 "SI7021" -#define D_SENSOR_DS18X20 "DS18x20" -#define D_SENSOR_I2C_SCL "I2C SCL" -#define D_SENSOR_I2C_SDA "I2C SDA" -#define D_SENSOR_WS2812 "WS2812" -#define D_SENSOR_DFR562 "MP3 Player" -#define D_SENSOR_IRSEND "IRsend" -#define D_SENSOR_SWITCH "Switch" // Suffix "1" -#define D_SENSOR_BUTTON "Button" // Suffix "1" -#define D_SENSOR_RELAY "Relay" // Suffix "1i" -#define D_SENSOR_LED "Led" // Suffix "1i" -#define D_SENSOR_PWM "PWM" // Suffix "1" -#define D_SENSOR_COUNTER "Counter" // Suffix "1" -#define D_SENSOR_IRRECV "IRrecv" -#define D_SENSOR_MHZ_RX "MHZ Rx" -#define D_SENSOR_MHZ_TX "MHZ Tx" -#define D_SENSOR_PZEM004_RX "PZEM004 Rx" -#define D_SENSOR_PZEM016_RX "PZEM016 Rx" -#define D_SENSOR_PZEM017_RX "PZEM017 Rx" -#define D_SENSOR_PZEM0XX_TX "PZEM0XX Tx" -#define D_SENSOR_SAIR_RX "SAir Rx" -#define D_SENSOR_SAIR_TX "SAir Tx" -#define D_SENSOR_SPI_CS "SPI CS" -#define D_SENSOR_SPI_DC "SPI DC" -#define D_SENSOR_BACKLIGHT "BkLight" -#define D_SENSOR_PMS5003 "PMS5003" -#define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" -#define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" -#define D_SENSOR_SBR_RX "SerBr Rx" -#define D_SENSOR_SBR_TX "SerBr Tx" -#define D_SENSOR_SR04_TRIG "SR04 Tri" -#define D_SENSOR_SR04_ECHO "SR04 Ech" -#define D_SENSOR_SDM120_TX "SDM120/220 Tx" -#define D_SENSOR_SDM120_RX "SDM120/220 Rx" -#define D_SENSOR_SDM630_TX "SDM630 Tx" -#define D_SENSOR_SDM630_RX "SDM630 Rx" -#define D_SENSOR_TM1638_CLK "TM16 CLK" -#define D_SENSOR_TM1638_DIO "TM16 DIO" -#define D_SENSOR_TM1638_STB "TM16 STB" -#define D_SENSOR_HX711_SCK "HX711 SCK" -#define D_SENSOR_HX711_DAT "HX711 DAT" -#define D_SENSOR_TX20_TX "TX20" -#define D_SENSOR_RFSEND "RFSend" -#define D_SENSOR_RFRECV "RFrecv" -#define D_SENSOR_TUYA_TX "Tuya Tx" -#define D_SENSOR_TUYA_RX "Tuya Rx" -#define D_SENSOR_MGC3130_XFER "MGC3130 Xfer" -#define D_SENSOR_MGC3130_RESET "MGC3130 Reset" -#define D_SENSOR_SSPI_MISO "SSPI MISO" -#define D_SENSOR_SSPI_MOSI "SSPI MOSI" -#define D_SENSOR_SSPI_SCLK "SSPI SCLK" -#define D_SENSOR_SSPI_CS "SSPI CS" -#define D_SENSOR_SSPI_DC "SSPI DC" -#define D_SENSOR_RF_SENSOR "RF Sensor" -#define D_SENSOR_AZ_RX "AZ Rx" -#define D_SENSOR_AZ_TX "AZ Tx" +// sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box +#define D_SENSOR_NONE "无" +#define D_SENSOR_USER "User" +#define D_SENSOR_DHT11 "DHT11" +#define D_SENSOR_AM2301 "AM2301" +#define D_SENSOR_SI7021 "SI7021" +#define D_SENSOR_DS18X20 "DS18x20" +#define D_SENSOR_I2C_SCL "I2C SCL" +#define D_SENSOR_I2C_SDA "I2C SDA" +#define D_SENSOR_WS2812 "WS2812" +#define D_SENSOR_DFR562 "MP3 Player" +#define D_SENSOR_IRSEND "IRsend" +#define D_SENSOR_SWITCH "Switch" // Suffix "1" +#define D_SENSOR_BUTTON "Button" // Suffix "1" +#define D_SENSOR_RELAY "Relay" // Suffix "1i" +#define D_SENSOR_LED "Led" // Suffix "1i" +#define D_SENSOR_PWM "PWM" // Suffix "1" +#define D_SENSOR_COUNTER "Counter" // Suffix "1" +#define D_SENSOR_IRRECV "IRrecv" +#define D_SENSOR_MHZ_RX "MHZ Rx" +#define D_SENSOR_MHZ_TX "MHZ Tx" +#define D_SENSOR_PZEM004_RX "PZEM004 Rx" +#define D_SENSOR_PZEM016_RX "PZEM016 Rx" +#define D_SENSOR_PZEM017_RX "PZEM017 Rx" +#define D_SENSOR_PZEM0XX_TX "PZEM0XX Tx" +#define D_SENSOR_SAIR_RX "SAir Rx" +#define D_SENSOR_SAIR_TX "SAir Tx" +#define D_SENSOR_SPI_CS "SPI CS" +#define D_SENSOR_SPI_DC "SPI DC" +#define D_SENSOR_BACKLIGHT "BkLight" +#define D_SENSOR_PMS5003 "PMS5003" +#define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" +#define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" +#define D_SENSOR_SBR_RX "SerBr Rx" +#define D_SENSOR_SBR_TX "SerBr Tx" +#define D_SENSOR_SR04_TRIG "SR04 Tri" +#define D_SENSOR_SR04_ECHO "SR04 Ech" +#define D_SENSOR_SDM120_TX "SDMx20 Tx" +#define D_SENSOR_SDM120_RX "SDMx20 Rx" +#define D_SENSOR_SDM630_TX "SDM630 Tx" +#define D_SENSOR_SDM630_RX "SDM630 Rx" +#define D_SENSOR_TM1638_CLK "TM16 CLK" +#define D_SENSOR_TM1638_DIO "TM16 DIO" +#define D_SENSOR_TM1638_STB "TM16 STB" +#define D_SENSOR_HX711_SCK "HX711 SCK" +#define D_SENSOR_HX711_DAT "HX711 DAT" +#define D_SENSOR_TX20_TX "TX20" +#define D_SENSOR_RFSEND "RFSend" +#define D_SENSOR_RFRECV "RFrecv" +#define D_SENSOR_TUYA_TX "Tuya Tx" +#define D_SENSOR_TUYA_RX "Tuya Rx" +#define D_SENSOR_MGC3130_XFER "MGC3130 Xfr" +#define D_SENSOR_MGC3130_RESET "MGC3130 Rst" +#define D_SENSOR_SSPI_MISO "SSPI MISO" +#define D_SENSOR_SSPI_MOSI "SSPI MOSI" +#define D_SENSOR_SSPI_SCLK "SSPI SCLK" +#define D_SENSOR_SSPI_CS "SSPI CS" +#define D_SENSOR_SSPI_DC "SSPI DC" +#define D_SENSOR_RF_SENSOR "RF Sensor" +#define D_SENSOR_AZ_RX "AZ Rx" +#define D_SENSOR_AZ_TX "AZ Tx" +#define D_SENSOR_MAX31855_CS "MX31855 CS" +#define D_SENSOR_MAX31855_CLK "MX31855 CLK" +#define D_SENSOR_MAX31855_DO "MX31855 DO" +#define D_SENSOR_NRG_SEL "HLWBL SEL" // Suffix "i" +#define D_SENSOR_NRG_CF1 "HLWBL CF1" +#define D_SENSOR_HLW_CF "HLW8012 CF" +#define D_SENSOR_HJL_CF "BL0937 CF" +#define D_SENSOR_MCP39F5_TX "MCP39F5 Tx" +#define D_SENSOR_MCP39F5_RX "MCP39F5 Rx" +#define D_SENSOR_MCP39F5_RST "MCP39F5 Rst" +#define D_SENSOR_CSE7766_TX "CSE7766 Tx" +#define D_SENSOR_CSE7766_RX "CSE7766 Rx" +#define D_SENSOR_PN532_TX "PN532 Tx" +#define D_SENSOR_PN532_RX "PN532 Rx" +#define D_SENSOR_SM16716_CLK "SM16716 CLK" +#define D_SENSOR_SM16716_DAT "SM16716 DAT" +#define D_SENSOR_SM16716_POWER "SM16716 PWR" +#define D_SENSOR_MY92X1_DI "MY92x1 DI" +#define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI" +#define D_SENSOR_ARIRFRCV "ALux IrRcv" +#define D_SENSOR_TXD "Serial Tx" +#define D_SENSOR_RXD "Serial Rx" +#define D_SENSOR_ROTARY "Rotary" // Suffix "1A" // Units #define D_UNIT_AMPERE "安" #define D_UNIT_CENTIMETER "厘米" #define D_UNIT_HOUR "时" #define D_UNIT_INCREMENTS "inc" -#define D_UNIT_KILOGRAM "kg" -#define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" +#define D_UNIT_KILOGRAM "千克" +#define D_UNIT_KILOMETER_PER_HOUR "公里/时" // or "km/h" #define D_UNIT_KILOOHM "千欧" #define D_UNIT_KILOWATTHOUR "千瓦时" #define D_UNIT_LUX "勒克斯" @@ -555,8 +591,8 @@ #define D_UNIT_MICROMETER "微米" #define D_UNIT_MICROSECOND "微秒" #define D_UNIT_MILLIAMPERE "毫安" -#define D_UNIT_MILLIMETER "mm" -#define D_UNIT_MILLIMETER_MERCURY "mmHg" +#define D_UNIT_MILLIMETER "毫米" +#define D_UNIT_MILLIMETER_MERCURY "毫米汞柱" #define D_UNIT_MILLISECOND "毫秒" #define D_UNIT_MINUTE "分" #define D_UNIT_PARTS_PER_BILLION "ppb" @@ -565,13 +601,13 @@ #define D_UNIT_PRESSURE "百帕" #define D_UNIT_SECOND "秒" #define D_UNIT_SECTORS "扇区" -#define D_UNIT_VA "VA" -#define D_UNIT_VAR "VAr" +#define D_UNIT_VA "伏安" +#define D_UNIT_VAR "无功伏安" #define D_UNIT_VOLT "伏" #define D_UNIT_WATT "瓦" #define D_UNIT_WATTHOUR "瓦时" #define D_UNIT_HERTZ "赫兹" -#define D_UNIT_WATT_METER_QUADRAT "W/m²" +#define D_UNIT_WATT_METER_QUADRAT "瓦/平米" // Log message prefix #define D_LOG_APPLICATION "APP: " // Application @@ -599,13 +635,13 @@ #define D_LOG_WIFI "WIF: " // Wifi //SDM220 -#define D_PHASE_ANGLE "Phase Angle" -#define D_IMPORT_ACTIVE "Import Active" -#define D_EXPORT_ACTIVE "Export Active" -#define D_IMPORT_REACTIVE "Import Reactive" -#define D_EXPORT_REACTIVE "Export Reactive" -#define D_TOTAL_REACTIVE "Total Reactive" -#define D_UNIT_KWARH "kVArh" -#define D_UNIT_ANGLE "Deg" +#define D_PHASE_ANGLE "相位角" +#define D_IMPORT_ACTIVE "有功输入" +#define D_EXPORT_ACTIVE "有功输出" +#define D_IMPORT_REACTIVE "无功输入" +#define D_EXPORT_REACTIVE "无功输出" +#define D_TOTAL_REACTIVE "总无功功率" +#define D_UNIT_KWARH "千乏时" +#define D_UNIT_ANGLE "度" #endif // _LANGUAGE_ZH_CN_H_ diff --git a/sonoff/language/zh-TW.h b/sonoff/language/zh-TW.h index 2780181aa..ae6d4df07 100644 --- a/sonoff/language/zh-TW.h +++ b/sonoff/language/zh-TW.h @@ -1,7 +1,7 @@ /* zh-TW.h - localization for Chinese (Traditional) - Taiwan for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends (translated by dannydu) + Copyright (C) 2019 Theo Arends (translated by dannydu) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -148,6 +148,7 @@ #define D_STOP "停止" #define D_SUBNET_MASK "子網遮罩" #define D_SUBSCRIBE_TO "訂閱" +#define D_UNSUBSCRIBE_FROM "退訂" #define D_SUCCESSFUL "成功" #define D_SUNRISE "Sunrise" #define D_SUNSET "Sunset" @@ -253,6 +254,7 @@ #define D_MODULE_PARAMETERS "模塊設置" #define D_MODULE_TYPE "模塊類型" +#define D_PULLUP_ENABLE "No Button/Switch pull-up" #define D_GPIO "GPIO" #define D_SERIAL_IN "串口輸入(RX)" #define D_SERIAL_OUT "串口輸出(TX)" @@ -287,6 +289,8 @@ #define D_TELEMETRY_PERIOD "上報周期" #define D_OTHER_PARAMETERS "其他設置" +#define D_TEMPLATE "Template" +#define D_ACTIVATE "Activate" #define D_WEB_ADMIN_PASSWORD "WEB管理密碼" #define D_MQTT_ENABLE "啟用MQTT" #define D_FRIENDLY_NAME "昵稱" @@ -295,6 +299,14 @@ #define D_SINGLE_DEVICE "單設備" #define D_MULTI_DEVICE "多設備" +#define D_CONFIGURE_TEMPLATE "Configure Template" +#define D_TEMPLATE_PARAMETERS "Template parameters" +#define D_TEMPLATE_NAME "Name" +#define D_BASE_TYPE "Based on" +#define D_TEMPLATE_FLAGS "Options" +#define D_ALLOW_ADC0 "ADC0 input" +#define D_ALLOW_PULLUP "User pull-up selection" + #define D_SAVE_CONFIGURATION "保存設置" #define D_CONFIGURATION_SAVED "設置已保存" #define D_CONFIGURATION_RESET "設置已重置" @@ -480,66 +492,90 @@ #define D_TX20_SOUTH "S" #define D_TX20_WEST "W" -// sonoff_template.h -#define D_SENSOR_NONE "None" -#define D_SENSOR_DHT11 "DHT11" -#define D_SENSOR_AM2301 "AM2301" -#define D_SENSOR_SI7021 "SI7021" -#define D_SENSOR_DS18X20 "DS18x20" -#define D_SENSOR_I2C_SCL "I2C SCL" -#define D_SENSOR_I2C_SDA "I2C SDA" -#define D_SENSOR_WS2812 "WS2812" -#define D_SENSOR_DFR562 "MP3 Player" -#define D_SENSOR_IRSEND "IRsend" -#define D_SENSOR_SWITCH "Switch" // Suffix "1" -#define D_SENSOR_BUTTON "Button" // Suffix "1" -#define D_SENSOR_RELAY "Relay" // Suffix "1i" -#define D_SENSOR_LED "Led" // Suffix "1i" -#define D_SENSOR_PWM "PWM" // Suffix "1" -#define D_SENSOR_COUNTER "Counter" // Suffix "1" -#define D_SENSOR_IRRECV "IRrecv" -#define D_SENSOR_MHZ_RX "MHZ Rx" -#define D_SENSOR_MHZ_TX "MHZ Tx" -#define D_SENSOR_PZEM004_RX "PZEM004 Rx" -#define D_SENSOR_PZEM016_RX "PZEM016 Rx" -#define D_SENSOR_PZEM017_RX "PZEM017 Rx" -#define D_SENSOR_PZEM0XX_TX "PZEM0XX Tx" -#define D_SENSOR_SAIR_RX "SAir Rx" -#define D_SENSOR_SAIR_TX "SAir Tx" -#define D_SENSOR_SPI_CS "SPI CS" -#define D_SENSOR_SPI_DC "SPI DC" -#define D_SENSOR_BACKLIGHT "BkLight" -#define D_SENSOR_PMS5003 "PMS5003" -#define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" -#define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" -#define D_SENSOR_SBR_RX "SerBr Rx" -#define D_SENSOR_SBR_TX "SerBr Tx" -#define D_SENSOR_SR04_TRIG "SR04 Tri" -#define D_SENSOR_SR04_ECHO "SR04 Ech" -#define D_SENSOR_SDM120_TX "SDM120/220 Tx" -#define D_SENSOR_SDM120_RX "SDM120/220 Rx" -#define D_SENSOR_SDM630_TX "SDM630 Tx" -#define D_SENSOR_SDM630_RX "SDM630 Rx" -#define D_SENSOR_TM1638_CLK "TM16 CLK" -#define D_SENSOR_TM1638_DIO "TM16 DIO" -#define D_SENSOR_TM1638_STB "TM16 STB" -#define D_SENSOR_HX711_SCK "HX711 SCK" -#define D_SENSOR_HX711_DAT "HX711 DAT" -#define D_SENSOR_TX20_TX "TX20" -#define D_SENSOR_RFSEND "RFSend" -#define D_SENSOR_RFRECV "RFrecv" -#define D_SENSOR_TUYA_TX "Tuya Tx" -#define D_SENSOR_TUYA_RX "Tuya Rx" -#define D_SENSOR_MGC3130_XFER "MGC3130 Xfer" -#define D_SENSOR_MGC3130_RESET "MGC3130 Reset" -#define D_SENSOR_SSPI_MISO "SSPI MISO" -#define D_SENSOR_SSPI_MOSI "SSPI MOSI" -#define D_SENSOR_SSPI_SCLK "SSPI SCLK" -#define D_SENSOR_SSPI_CS "SSPI CS" -#define D_SENSOR_SSPI_DC "SSPI DC" -#define D_SENSOR_RF_SENSOR "RF Sensor" -#define D_SENSOR_AZ_RX "AZ Rx" -#define D_SENSOR_AZ_TX "AZ Tx" +// sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box +#define D_SENSOR_NONE "None" +#define D_SENSOR_USER "User" +#define D_SENSOR_DHT11 "DHT11" +#define D_SENSOR_AM2301 "AM2301" +#define D_SENSOR_SI7021 "SI7021" +#define D_SENSOR_DS18X20 "DS18x20" +#define D_SENSOR_I2C_SCL "I2C SCL" +#define D_SENSOR_I2C_SDA "I2C SDA" +#define D_SENSOR_WS2812 "WS2812" +#define D_SENSOR_DFR562 "MP3 Player" +#define D_SENSOR_IRSEND "IRsend" +#define D_SENSOR_SWITCH "Switch" // Suffix "1" +#define D_SENSOR_BUTTON "Button" // Suffix "1" +#define D_SENSOR_RELAY "Relay" // Suffix "1i" +#define D_SENSOR_LED "Led" // Suffix "1i" +#define D_SENSOR_PWM "PWM" // Suffix "1" +#define D_SENSOR_COUNTER "Counter" // Suffix "1" +#define D_SENSOR_IRRECV "IRrecv" +#define D_SENSOR_MHZ_RX "MHZ Rx" +#define D_SENSOR_MHZ_TX "MHZ Tx" +#define D_SENSOR_PZEM004_RX "PZEM004 Rx" +#define D_SENSOR_PZEM016_RX "PZEM016 Rx" +#define D_SENSOR_PZEM017_RX "PZEM017 Rx" +#define D_SENSOR_PZEM0XX_TX "PZEM0XX Tx" +#define D_SENSOR_SAIR_RX "SAir Rx" +#define D_SENSOR_SAIR_TX "SAir Tx" +#define D_SENSOR_SPI_CS "SPI CS" +#define D_SENSOR_SPI_DC "SPI DC" +#define D_SENSOR_BACKLIGHT "BkLight" +#define D_SENSOR_PMS5003 "PMS5003" +#define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" +#define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" +#define D_SENSOR_SBR_RX "SerBr Rx" +#define D_SENSOR_SBR_TX "SerBr Tx" +#define D_SENSOR_SR04_TRIG "SR04 Tri" +#define D_SENSOR_SR04_ECHO "SR04 Ech" +#define D_SENSOR_SDM120_TX "SDMx20 Tx" +#define D_SENSOR_SDM120_RX "SDMx20 Rx" +#define D_SENSOR_SDM630_TX "SDM630 Tx" +#define D_SENSOR_SDM630_RX "SDM630 Rx" +#define D_SENSOR_TM1638_CLK "TM16 CLK" +#define D_SENSOR_TM1638_DIO "TM16 DIO" +#define D_SENSOR_TM1638_STB "TM16 STB" +#define D_SENSOR_HX711_SCK "HX711 SCK" +#define D_SENSOR_HX711_DAT "HX711 DAT" +#define D_SENSOR_TX20_TX "TX20" +#define D_SENSOR_RFSEND "RFSend" +#define D_SENSOR_RFRECV "RFrecv" +#define D_SENSOR_TUYA_TX "Tuya Tx" +#define D_SENSOR_TUYA_RX "Tuya Rx" +#define D_SENSOR_MGC3130_XFER "MGC3130 Xfr" +#define D_SENSOR_MGC3130_RESET "MGC3130 Rst" +#define D_SENSOR_SSPI_MISO "SSPI MISO" +#define D_SENSOR_SSPI_MOSI "SSPI MOSI" +#define D_SENSOR_SSPI_SCLK "SSPI SCLK" +#define D_SENSOR_SSPI_CS "SSPI CS" +#define D_SENSOR_SSPI_DC "SSPI DC" +#define D_SENSOR_RF_SENSOR "RF Sensor" +#define D_SENSOR_AZ_RX "AZ Rx" +#define D_SENSOR_AZ_TX "AZ Tx" +#define D_SENSOR_MAX31855_CS "MX31855 CS" +#define D_SENSOR_MAX31855_CLK "MX31855 CLK" +#define D_SENSOR_MAX31855_DO "MX31855 DO" +#define D_SENSOR_NRG_SEL "HLWBL SEL" // Suffix "i" +#define D_SENSOR_NRG_CF1 "HLWBL CF1" +#define D_SENSOR_HLW_CF "HLW8012 CF" +#define D_SENSOR_HJL_CF "BL0937 CF" +#define D_SENSOR_MCP39F5_TX "MCP39F5 Tx" +#define D_SENSOR_MCP39F5_RX "MCP39F5 Rx" +#define D_SENSOR_MCP39F5_RST "MCP39F5 Rst" +#define D_SENSOR_CSE7766_TX "CSE7766 Tx" +#define D_SENSOR_CSE7766_RX "CSE7766 Rx" +#define D_SENSOR_PN532_TX "PN532 Tx" +#define D_SENSOR_PN532_RX "PN532 Rx" +#define D_SENSOR_SM16716_CLK "SM16716 CLK" +#define D_SENSOR_SM16716_DAT "SM16716 DAT" +#define D_SENSOR_SM16716_POWER "SM16716 PWR" +#define D_SENSOR_MY92X1_DI "MY92x1 DI" +#define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI" +#define D_SENSOR_ARIRFRCV "ALux IrRcv" +#define D_SENSOR_TXD "Serial Tx" +#define D_SENSOR_RXD "Serial Rx" +#define D_SENSOR_ROTARY "Rotary" // Suffix "1A" // Units #define D_UNIT_AMPERE "安" diff --git a/sonoff/my_user_config.h b/sonoff/my_user_config.h index 5fcbecabf..5214ad46c 100644 --- a/sonoff/my_user_config.h +++ b/sonoff/my_user_config.h @@ -1,7 +1,7 @@ /* my_user_config.h - user specific configuration for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends + Copyright (C) 2019 Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -54,9 +54,10 @@ #define SAVE_DATA 1 // [SaveData] Save changed parameters to Flash (0 = disable, 1 - 3600 seconds) #define SAVE_STATE 1 // [SetOption0] Save changed power state to Flash (0 = disable, 1 = enable) +#define BOOT_LOOP_OFFSET 1 // [SetOption36] Number of boot loops before starting restoring defaults (0 = disable, 1..200 = boot loops offset) // -- Wifi ---------------------------------------- -#define WIFI_IP_ADDRESS "0.0.0.0" // [IpAddress1] Set to 0.0.0.0 for using DHCP or IP address +#define WIFI_IP_ADDRESS "0.0.0.0" // [IpAddress1] Set to 0.0.0.0 for using DHCP or enter a static IP address #define WIFI_GATEWAY "192.168.1.1" // [IpAddress2] If not using DHCP set Gateway IP address #define WIFI_SUBNETMASK "255.255.255.0" // [IpAddress3] If not using DHCP set Network mask #define WIFI_DNS "192.168.1.1" // [IpAddress4] If not using DHCP set DNS IP address (might be equal to WIFI_GATEWAY) @@ -121,6 +122,7 @@ // -- MQTT - Telemetry ---------------------------- #define TELE_PERIOD 300 // [TelePeriod] Telemetry (0 = disable, 10 - 3600 seconds) +#define TELE_ON_POWER 0 // [SetOption59] send tele/STATE together with stat/RESULT (0 = Disable, 1 = Enable) // -- MQTT - Domoticz ----------------------------- #define DOMOTICZ_UPDATE_TIMER 0 // [DomoticzUpdateTimer] Send relay status (0 = disable, 1 - 3600 seconds) @@ -134,6 +136,9 @@ #define FRIENDLY_NAME "Sonoff" // [FriendlyName] Friendlyname up to 32 characters used by webpages and Alexa #define EMULATION EMUL_NONE // [Emulation] Select Belkin WeMo (single relay/light) or Hue Bridge emulation (multi relay/light) (EMUL_NONE, EMUL_WEMO or EMUL_HUE) +// -- mDNS ---------------------------------------- +#define MDNS_ENABLED 0 // [SetOption55] Use mDNS (0 = Disable, 1 = Enable) + // -- Time - Up to three NTP servers in your region #define NTP_SERVER1 "pool.ntp.org" // [NtpServer1] Select first NTP server by name or IP address (129.250.35.250) #define NTP_SERVER2 "nl.pool.ntp.org" // [NtpServer2] Select second NTP server by name or IP address (5.39.184.5) @@ -204,6 +209,7 @@ //#define MY_LANGUAGE he-HE // Hebrew in Israel //#define MY_LANGUAGE hu-HU // Hungarian in Hungary //#define MY_LANGUAGE it-IT // Italian in Italy +//#define MY_LANGUAGE ko-KO // Korean in Korea //#define MY_LANGUAGE nl-NL // Dutch in the Netherlands //#define MY_LANGUAGE pl-PL // Polish in Poland //#define MY_LANGUAGE pt-BR // Portuguese in Brazil @@ -243,7 +249,7 @@ #define DOMOTICZ_OUT_TOPIC "domoticz/out" // Domoticz Output Topic // -- MQTT - Home Assistant Discovery ------------- -#define USE_HOME_ASSISTANT // Enable Home Assistant Discovery Support (+2k code) +#define USE_HOME_ASSISTANT // Enable Home Assistant Discovery Support (+7k code) #define HOME_ASSISTANT_DISCOVERY_PREFIX "homeassistant" // Home Assistant discovery prefix // -- MQTT - TLS ---------------------------------- @@ -274,6 +280,8 @@ // -- Rules --------------------------------------- #define USE_RULES // Add support for rules (+4k4 code) +// #define USE_EXPRESSION // Add support for expression evaluation in rules (+3k2 code, +64 bytes mem) +// #define SUPPORT_MQTT_EVENT // Support trigger event with MQTT subscriptions (+3k5 code) // -- Internal Analog input ----------------------- #define USE_ADC_VCC // Display Vcc in Power status. Disable for use as Analog input on selected devices @@ -283,6 +291,7 @@ //#define USE_DS18x20_LEGACY // Optional for more than one DS18x20 sensors with dynamic scan using library OneWire (+1k5 code) #define USE_DS18x20 // Optional for more than one DS18x20 sensors with id sort, single scan and read retry (+1k3 code) // #define W1_PARASITE_POWER // If using USE_DS18x20 then optimize for parasite powered sensors +// #define DS18B20_INTERNAL_PULLUP // Use INPUT_PULLUP internal pullup resistors for single DS18B20 // -- I2C sensors --------------------------------- #define USE_I2C // I2C using library wire (+10k code, 0k2 mem, 124 iram) @@ -321,6 +330,8 @@ // #define USE_DS3231 // Enable DS3231 external RTC in case no Wifi is avaliable. See docs in the source file (+1k2 code) // #define USE_RTC_ADDR 0x68 // Default I2C address 0x68 // #define USE_MGC3130 // Enable MGC3130 Electric Field Effect Sensor (I2C address 0x42) (+2k7 code, 0k3 mem) +// #define USE_MAX44009 // Enable MAX44009 Ambient Light sensor (I2C addresses 0x4A and 0x4B) (+0k8 code) +// #define USE_SCD30 // Enable Sensiron SCd30 CO2 sensor (I2C address 0x61) (+3k3 code) // #define USE_DISPLAY // Add I2C Display Support (+2k code) #define USE_DISPLAY_MODES1TO5 // Enable display mode 1 to 5 in addition to mode 0 @@ -335,7 +346,6 @@ #define MTX_ADDRESS6 0x76 // [DisplayAddress6] I2C address of sixth 8x8 matrix module #define MTX_ADDRESS7 0x00 // [DisplayAddress7] I2C address of seventh 8x8 matrix module #define MTX_ADDRESS8 0x00 // [DisplayAddress8] I2C address of eigth 8x8 matrix module - #endif // USE_I2C // -- SPI sensors --------------------------------- @@ -370,6 +380,10 @@ #define USE_ARMTRONIX_DIMMERS // Add support for Armtronix Dimmers (+1k4 code) #define USE_PS_16_DZ // Add support for PS-16-DZ Dimmer //#define USE_AZ7798 // Add support for AZ-Instrument 7798 CO2 datalogger (+1k6 code) +//#define USE_PN532_HSU // Add support for PN532 using HSU (Serial) interface (+1k8 code, 140 bytes mem) +// #define USE_PN532_CAUSE_EVENTS // Cause event execution for PN532_UID= and PN532_DATA=[if defined] (+ 30 bytes code) +// #define USE_PN532_DATA_FUNCTION // Add sensor40 command support for erase, setting data block content (+1k7 code, 388 bytes mem) +// #define USE_PN532_DATA_RAW // Allow DATA block to be used by non-alpha-numberic data (+ 80 bytes code, 48 bytes ram) // Power monitoring sensors ----------------------- #define USE_PZEM004T // Add support for PZEM004T Energy monitor (+2k code) @@ -378,6 +392,8 @@ #define USE_MCP39F501 // Add support for MCP39F501 Energy monitor as used in Shelly 2 (+3k1 code) // -- Low level interface devices ----------------- +//#define USE_MAX31855 // Add support for MAX31855 K-Type thermocouple sensor using softSPI + #define USE_IR_REMOTE // Send IR remote commands using library IRremoteESP8266 and ArduinoJson (+4k3 code, 0k3 mem, 48 iram) // #define USE_IR_HVAC // Support for HVAC (Toshiba, Mitsubishi and LG) system using IR (+3k5 code) #define USE_IR_RECEIVE // Support for IR receiver (+7k2 code, 264 iram) @@ -407,6 +423,8 @@ // #define USE_THEO_V2 // Add support for decoding Theo V2 sensors as documented on https://sidweb.nl using 434MHz RF sensor receiver (+1k4 code) // #define USE_ALECTO_V2 // Add support for decoding Alecto V2 sensors like ACH2010, WS3000 and DKW2012 weather stations using 868MHz RF sensor receiver (+1k7 code) +#define USE_SM16716 // Add support for SM16716 RGB LED controller (+0k7 code) + /*********************************************************************************************\ * Debug features are only supported in development branch \*********************************************************************************************/ @@ -419,12 +437,12 @@ * See RELEASENOTES.md for selected features \*********************************************************************************************/ -//#define USE_CLASSIC // Create sonoff-classic with initial configuration tools WPS, SmartConfig and WifiManager -//#define USE_BASIC // Create sonoff-basic with no sensors -//#define USE_SENSORS // Create sonoff-sensors with useful sensors enabled -//#define USE_KNX_NO_EMULATION // Create sonoff-knx with KNX but without Emulation -//#define USE_DISPLAYS // Create sonoff-display with display drivers enabled -//#define BE_MINIMAL // Create sonoff-minimal as intermediate firmware for OTA-MAGIC +//#define FIRMWARE_CLASSIC // Create sonoff-classic with initial configuration tools WPS, SmartConfig and WifiManager +//#define FIRMWARE_BASIC // Create sonoff-basic with no sensors +//#define FIRMWARE_SENSORS // Create sonoff-sensors with useful sensors enabled +//#define FIRMWARE_KNX_NO_EMULATION // Create sonoff-knx with KNX but without Emulation +//#define FIRMWARE_DISPLAYS // Create sonoff-display with display drivers enabled +//#define FIRMWARE_MINIMAL // Create sonoff-minimal as intermediate firmware for OTA-MAGIC /*********************************************************************************************\ * No user configurable items below diff --git a/sonoff/settings.h b/sonoff/settings.h index 4de813d18..342ff1612 100644 --- a/sonoff/settings.h +++ b/sonoff/settings.h @@ -1,7 +1,7 @@ /* settings.h - setting variables for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends + Copyright (C) 2019 Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -25,57 +25,57 @@ typedef union { // Restricted by MISRA-C Rule 18.4 but so useful... uint32_t data; // Allow bit manipulation using SetOption struct { // SetOption0 .. SetOption31 - uint32_t save_state : 1; // bit 0 - uint32_t button_restrict : 1; // bit 1 - uint32_t value_units : 1; // bit 2 - uint32_t mqtt_enabled : 1; // bit 3 - uint32_t mqtt_response : 1; // bit 4 - uint32_t mqtt_power_retain : 1; // CMND_POWERRETAIN - uint32_t mqtt_button_retain : 1; // CMND_BUTTONRETAIN - uint32_t mqtt_switch_retain : 1; // CMND_SWITCHRETAIN - uint32_t temperature_conversion : 1; // bit 8 - uint32_t mqtt_sensor_retain : 1; // CMND_SENSORRETAIN - uint32_t mqtt_offline : 1; // bit 10 - uint32_t button_swap : 1; // bit 11 (v5.1.6) - uint32_t stop_flash_rotate : 1; // bit 12 (v5.2.0) - uint32_t button_single : 1; // bit 13 (v5.4.0) - uint32_t interlock : 1; // bit 14 (v5.6.0) - uint32_t pwm_control : 1; // bit 15 (v5.8.1) - uint32_t ws_clock_reverse : 1; // bit 16 (v5.8.1) - uint32_t decimal_text : 1; // bit 17 (v5.8.1) - uint32_t light_signal : 1; // bit 18 (v5.10.0c) - uint32_t hass_discovery : 1; // bit 19 (v5.11.1a) - uint32_t not_power_linked : 1; // bit 20 (v5.11.1f) - uint32_t no_power_on_check : 1; // bit 21 (v5.11.1i) - uint32_t mqtt_serial : 1; // bit 22 (v5.12.0f) - uint32_t mqtt_serial_raw : 1; // bit 23 (v6.1.1c) // Was rules_enabled until 5.14.0b - uint32_t pressure_conversion : 1; // bit 24 (v6.3.0.2) // Was rules_once until 5.14.0b - uint32_t knx_enabled : 1; // bit 25 (v5.12.0l) KNX - uint32_t device_index_enable : 1; // bit 26 (v5.13.1a) - uint32_t knx_enable_enhancement : 1; // bit 27 (v5.14.0a) KNX - uint32_t rf_receive_decimal : 1; // bit 28 (v6.0.0a) - uint32_t ir_receive_decimal : 1; // bit 29 (v6.0.0a) - uint32_t hass_light : 1; // bit 30 (v6.0.0b) - uint32_t global_state : 1; // bit 31 (v6.1.0) + uint32_t save_state : 1; // bit 0 - SetOption0 - Save power state and use after restart + uint32_t button_restrict : 1; // bit 1 - SetOption1 - Control button multipress + uint32_t value_units : 1; // bit 2 - SetOption2 - Add units to JSON status messages + uint32_t mqtt_enabled : 1; // bit 3 - SetOption3 - Control MQTT + uint32_t mqtt_response : 1; // bit 4 - SetOption4 - Switch between MQTT RESULT or COMMAND + uint32_t mqtt_power_retain : 1; // bit 5 - CMND_POWERRETAIN + uint32_t mqtt_button_retain : 1; // bit 6 - CMND_BUTTONRETAIN + uint32_t mqtt_switch_retain : 1; // bit 7 - CMND_SWITCHRETAIN + uint32_t temperature_conversion : 1; // bit 8 - SetOption8 - Switch between Celsius or Fahrenheit + uint32_t mqtt_sensor_retain : 1; // bit 9 - CMND_SENSORRETAIN + uint32_t mqtt_offline : 1; // bit 10 - SetOption10 - Control MQTT LWT message format + uint32_t button_swap : 1; // bit 11 (v5.1.6) - SetOption11 - Swap button single and double press functionality + uint32_t stop_flash_rotate : 1; // bit 12 (v5.2.0) - SetOption12 - Switch between dynamic or fixed slot flash save location + uint32_t button_single : 1; // bit 13 (v5.4.0) - SetOption13 - Support only single press to speed up button press recognition + uint32_t interlock : 1; // bit 14 (v5.6.0) - CMND_INTERLOCK + uint32_t pwm_control : 1; // bit 15 (v5.8.1) - SetOption15 - Switch between commands PWM or COLOR/DIMMER/CT/CHANNEL + uint32_t ws_clock_reverse : 1; // bit 16 (v5.8.1) - SetOption16 - Switch between clockwise or counter-clockwise + uint32_t decimal_text : 1; // bit 17 (v5.8.1) - SetOption17 - Switch between decimal or hexadecimal output + uint32_t light_signal : 1; // bit 18 (v5.10.0c) - SetOption18 - Pair light signal with CO2 sensor + uint32_t hass_discovery : 1; // bit 19 (v5.11.1a) - SetOption19 - Control Home Assistantautomatic discovery (See SetOption59) + uint32_t not_power_linked : 1; // bit 20 (v5.11.1f) - SetOption20 - Control power in relation to Dimmer/Color/Ct changes + uint32_t no_power_on_check : 1; // bit 21 (v5.11.1i) - SetOption21 - Show voltage even if powered off + uint32_t mqtt_serial : 1; // bit 22 (v5.12.0f) - CMND_SERIALSEND and CMND_SERIALLOG + uint32_t mqtt_serial_raw : 1; // bit 23 (v6.1.1c) - CMND_SERIALSEND3 + uint32_t pressure_conversion : 1; // bit 24 (v6.3.0.2) - SetOption24 - Switch between hPa or mmHg pressure unit + uint32_t knx_enabled : 1; // bit 25 (v5.12.0l) - CMND_KNX_ENABLED + uint32_t device_index_enable : 1; // bit 26 (v5.13.1a) - SetOption26 - Switch between POWER or POWER1 + uint32_t knx_enable_enhancement : 1; // bit 27 (v5.14.0a) - CMND_KNX_ENHANCED + uint32_t rf_receive_decimal : 1; // bit 28 (v6.0.0a) - SetOption28 - RF receive data format + uint32_t ir_receive_decimal : 1; // bit 29 (v6.0.0a) - SetOption29 - IR receive data format + uint32_t hass_light : 1; // bit 30 (v6.0.0b) - SetOption30 - Enforce HAss autodiscovery as light + uint32_t global_state : 1; // bit 31 (v6.1.0) - SetOption31 - Control link led blinking }; } SysBitfield; typedef union { // Restricted by MISRA-C Rule 18.4 but so useful... uint32_t data; // Allow bit manipulation using SetOption struct { // SetOption50 .. SetOption81 - uint32_t timers_enable : 1; // bit 0 (v6.1.1b) - uint32_t user_esp8285_enable : 1; // bit 1 (v6.1.1.14) - uint32_t time_append_timezone : 1; // bit 2 (v6.2.1.2) - uint32_t gui_hostname_ip : 1; // bit 3 (v6.2.1.20) - uint32_t tuya_apply_o20 : 1; // bit 4 (v6.3.0.4) - uint32_t spare5 : 1; - uint32_t use_wifi_scan : 1; // bit 6 (v6.3.0.10) - uint32_t use_wifi_rescan : 1; // bit 7 (v6.3.0.10) - uint32_t receive_raw : 1; // bit 8 (v6.3.0.11) - uint32_t hass_tele_on_power : 1; // bit 9 (v6.3.0.13) + uint32_t timers_enable : 1; // bit 0 (v6.1.1b) - CMND_TIMERS + uint32_t user_esp8285_enable : 1; // bit 1 (v6.1.1.14) - SetOption51 - Enable ESP8285 user GPIO's + uint32_t time_append_timezone : 1; // bit 2 (v6.2.1.2) - SetOption52 - Append timezone to JSON time + uint32_t gui_hostname_ip : 1; // bit 3 (v6.2.1.20) - SetOption53 - Show hostanme and IP address in GUI main menu + uint32_t tuya_apply_o20 : 1; // bit 4 (v6.3.0.4) - SetOption54 - Apply SetOption20 settings to Tuya device + uint32_t mdns_enabled : 1; // bit 5 (v6.4.1.4) - SetOption55 - Control mDNS service + uint32_t use_wifi_scan : 1; // bit 6 (v6.3.0.10) - SetOption56 - Scan wifi network at restart for configured AP's + uint32_t use_wifi_rescan : 1; // bit 7 (v6.3.0.10) - SetOption57 - Scan wifi network every 44 minutes for configured AP's + uint32_t receive_raw : 1; // bit 8 (v6.3.0.11) - SetOption58 - Add IR Raw data to JSON message + uint32_t hass_tele_on_power : 1; // bit 9 (v6.3.0.13) - SetOption59 - Send tele/%topic%/STATE in addition to stat/%topic%/RESULT uint32_t sleep_normal : 1; // bit 10 (v6.3.0.15) - SetOption60 - Enable normal sleep instead of dynamic sleep - uint32_t button_switch_force_local : 1;// bit 11 - uint32_t spare12 : 1; + uint32_t button_switch_force_local : 1;// bit 11 (v6.3.0.16) - SetOption61 - Force local operation when button/switch topic is set + uint32_t no_hold_retain : 1; // bit 12 (v6.4.1.19) - SetOption62 - Don't use retain flag on HOLD messages uint32_t spare13 : 1; uint32_t spare14 : 1; uint32_t spare15 : 1; @@ -163,6 +163,20 @@ typedef union { }; } Mcp230xxCfg; +typedef union { + uint8_t data; + struct { + uint8_t spare0 : 1; + uint8_t spare1 : 1; + uint8_t spare2 : 1; + uint8_t spare3 : 1; + uint8_t spare4 : 1; + uint8_t spare5 : 1; + uint8_t spare6 : 1; + uint8_t mhz19b_abc_disable : 1; // Disable ABC (Automatic Baseline Correction for MHZ19(B) (0 = Enabled (default), 1 = Disabled with Sensor15 command) + }; +} SensorCfg1; + /* struct SYSCFG { unsigned long cfg_holder; // 000 Pre v6 header @@ -183,21 +197,21 @@ struct SYSCFG { char ota_url[101]; // 017 char mqtt_prefix[3][11]; // 07C uint8_t baudrate; // 09D - byte seriallog_level; // 09E + uint8_t seriallog_level; // 09E uint8_t sta_config; // 09F - byte sta_active; // 0A0 + uint8_t sta_active; // 0A0 char sta_ssid[2][33]; // 0A1 - Keep together with sta_pwd as being copied as one chunck with reset 4/5 char sta_pwd[2][65]; // 0E3 - Keep together with sta_ssid as being copied as one chunck with reset 4/5 char hostname[33]; // 165 char syslog_host[33]; // 186 uint8_t rule_stop; // 1A7 uint16_t syslog_port; // 1A8 - byte syslog_level; // 1AA + uint8_t syslog_level; // 1AA uint8_t webserver; // 1AB - byte weblog_level; // 1AC + uint8_t weblog_level; // 1AC uint8_t mqtt_fingerprint[2][20]; // 1AD - byte free_1D5[20]; // 1D5 Free since 5.12.0e + uint8_t free_1D5[20]; // 1D5 Free since 5.12.0e char mqtt_host[33]; // 1E9 uint16_t mqtt_port; // 20A @@ -271,6 +285,7 @@ struct SYSCFG { uint8_t ws_color[4][3]; // 475 uint8_t ws_width[3]; // 481 myio my_gp; // 484 + uint8_t test_step; // 495 uint16_t light_pixels; // 496 uint8_t light_color[5]; // 498 uint8_t light_correction; // 49D @@ -281,15 +296,13 @@ struct SYSCFG { uint8_t light_speed; // 4A2 uint8_t light_scheme; // 4A3 uint8_t light_width; // 4A4 - byte knx_GA_registered; // 4A5 Number of Group Address to read + uint8_t knx_GA_registered; // 4A5 Number of Group Address to read uint16_t light_wakeup; // 4A6 - byte knx_CB_registered; // 4A8 Number of Group Address to write + uint8_t knx_CB_registered; // 4A8 Number of Group Address to write char web_password[33]; // 4A9 - - uint8_t ex_switchmode[4]; // 4CA Free since 6.0.0a - + uint8_t interlock[MAX_INTERLOCKS]; // 4CA char ntp_server[3][33]; // 4CE - byte ina219_mode; // 531 + uint8_t ina219_mode; // 531 uint16_t pulse_timer[MAX_PULSETIMERS]; // 532 uint16_t button_debounce; // 542 uint32_t ip_address[4]; // 544 @@ -308,17 +321,17 @@ struct SYSCFG { uint16_t knx_physsical_addr; // 6B8 (address_t is a uint16_t) uint16_t knx_GA_addr[MAX_KNX_GA]; // 6BA (address_t is a uint16_t) x KNX_max_GA uint16_t knx_CB_addr[MAX_KNX_CB]; // 6CE (address_t is a uint16_t) x KNX_max_CB - byte knx_GA_param[MAX_KNX_GA]; // 6E2 Type of Input (relay changed, button pressed, sensor read <-teleperiod) - byte knx_CB_param[MAX_KNX_CB]; // 6EC Type of Output (set relay, toggle relay, reply sensor value) + uint8_t knx_GA_param[MAX_KNX_GA]; // 6E2 Type of Input (relay changed, button pressed, sensor read <-teleperiod) + uint8_t knx_CB_param[MAX_KNX_CB]; // 6EC Type of Output (set relay, toggle relay, reply sensor value) Mcp230xxCfg mcp230xx_config[16]; // 6F6 uint8_t mcp230xx_int_prio; // 716 - - byte free_717[1]; // 717 - + SensorCfg1 SensorBits1; // 717 On/Off settings used by Sensor Commands uint16_t mcp230xx_int_timer; // 718 uint8_t rgbwwTable[5]; // 71A + uint8_t user_template_base; // 71F + mytmplt user_template; // 720 29 bytes - byte free_71F[117]; // 71F + uint8_t free_73D[87]; // 73D uint32_t drivers[3]; // 794 uint32_t monitors; // 7A0 @@ -327,7 +340,7 @@ struct SYSCFG { uint32_t energy_kWhtotal_time; // 7B4 unsigned long weight_item; // 7B8 Weight of one item in gram * 10 - byte free_7BC[2]; // 7BC + uint8_t free_7BC[2]; // 7BC uint16_t weight_max; // 7BE Total max weight in kilogram unsigned long weight_reference; // 7C0 Reference weight in gram @@ -347,7 +360,7 @@ struct RTCRBT { struct RTCMEM { uint16_t valid; // 290 (RTC memory offset 100) - byte oswatch_blocked_loop; // 292 + uint8_t oswatch_blocked_loop; // 292 uint8_t ota_loader; // 293 unsigned long energy_kWhtoday; // 294 unsigned long energy_kWhtotal; // 298 @@ -377,7 +390,7 @@ struct XDRVMAILBOX { uint16_t data_len; uint16_t payload16; int16_t payload; - uint8_t grpflg; + bool grpflg; uint8_t notused; char *topic; char *data; diff --git a/sonoff/settings.ino b/sonoff/settings.ino index 46c1d849d..dff0d849f 100644 --- a/sonoff/settings.ino +++ b/sonoff/settings.ino @@ -1,7 +1,7 @@ /* settings.ino - user settings for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends + Copyright (C) 2019 Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -97,7 +97,7 @@ void RtcSettingsLoad(void) RtcSettings.valid = RTC_MEM_VALID; RtcSettings.energy_kWhtoday = Settings.energy_kWhtoday; RtcSettings.energy_kWhtotal = Settings.energy_kWhtotal; - for (byte i = 0; i < MAX_COUNTERS; i++) { + for (uint8_t i = 0; i < MAX_COUNTERS; i++) { RtcSettings.pulse_counter[i] = Settings.pulse_counter[i]; } RtcSettings.power = Settings.power; @@ -106,7 +106,7 @@ void RtcSettingsLoad(void) rtc_settings_crc = GetRtcSettingsCrc(); } -boolean RtcSettingsValid(void) +bool RtcSettingsValid(void) { return (RTC_MEM_VALID == RtcSettings.valid); } @@ -147,7 +147,7 @@ void RtcRebootLoad(void) rtc_reboot_crc = GetRtcRebootCrc(); } -boolean RtcRebootValid(void) +bool RtcRebootValid(void) { return (RTC_MEM_VALID == RtcReboot.valid); } @@ -171,6 +171,139 @@ extern "C" uint32_t _SPIFFS_end; // Version 5.2 allow for more flash space #define CFG_ROTATES 8 // Number of flash sectors used (handles uploads) +/*********************************************************************************************\ + * EEPROM support based on EEPROM library and tuned for Tasmota +\*********************************************************************************************/ + +uint32_t eeprom_sector = SPIFFS_END; +uint8_t* eeprom_data = 0; +size_t eeprom_size = 0; +bool eeprom_dirty = false; + +void EepromBegin(size_t size) +{ + if (size <= 0) { return; } + if (size > SPI_FLASH_SEC_SIZE - sizeof(Settings) -4) { size = SPI_FLASH_SEC_SIZE - sizeof(Settings) -4; } + size = (size + 3) & (~3); + + // In case begin() is called a 2nd+ time, don't reallocate if size is the same + if (eeprom_data && size != eeprom_size) { + delete[] eeprom_data; + eeprom_data = new uint8_t[size]; + } else if (!eeprom_data) { + eeprom_data = new uint8_t[size]; + } + eeprom_size = size; + + size_t flash_offset = SPI_FLASH_SEC_SIZE - eeprom_size; + uint8_t* flash_buffer; + flash_buffer = new uint8_t[SPI_FLASH_SEC_SIZE]; + noInterrupts(); + spi_flash_read(eeprom_sector * SPI_FLASH_SEC_SIZE, reinterpret_cast(flash_buffer), SPI_FLASH_SEC_SIZE); + interrupts(); + memcpy(eeprom_data, flash_buffer + flash_offset, eeprom_size); + delete[] flash_buffer; + + eeprom_dirty = false; // make sure dirty is cleared in case begin() is called 2nd+ time +} + +size_t EepromLength(void) +{ + return eeprom_size; +} + +uint8_t EepromRead(int const address) +{ + if (address < 0 || (size_t)address >= eeprom_size) { return 0; } + if (!eeprom_data) { return 0; } + + return eeprom_data[address]; +} + +// Prototype needed for Arduino IDE - https://forum.arduino.cc/index.php?topic=406509.0 +template T EepromGet(int const address, T &t); +template T EepromGet(int const address, T &t) +{ + if (address < 0 || address + sizeof(T) > eeprom_size) { return t; } + if (!eeprom_data) { return 0; } + + memcpy((uint8_t*) &t, eeprom_data + address, sizeof(T)); + return t; +} + +void EepromWrite(int const address, uint8_t const value) +{ + if (address < 0 || (size_t)address >= eeprom_size) { return; } + if (!eeprom_data) { return; } + + // Optimise eeprom_dirty. Only flagged if data written is different. + uint8_t* pData = &eeprom_data[address]; + if (*pData != value) { + *pData = value; + eeprom_dirty = true; + } +} + +// Prototype needed for Arduino IDE - https://forum.arduino.cc/index.php?topic=406509.0 +template void EepromPut(int const address, const T &t); +template void EepromPut(int const address, const T &t) +{ + if (address < 0 || address + sizeof(T) > eeprom_size) { return; } + if (!eeprom_data) { return; } + + // Optimise eeprom_dirty. Only flagged if data written is different. + if (memcmp(eeprom_data + address, (const uint8_t*)&t, sizeof(T)) != 0) { + eeprom_dirty = true; + memcpy(eeprom_data + address, (const uint8_t*)&t, sizeof(T)); + } +} + +bool EepromCommit(void) +{ + bool ret = false; + if (!eeprom_size) { return false; } + if (!eeprom_dirty) { return true; } + if (!eeprom_data) { return false; } + + size_t flash_offset = SPI_FLASH_SEC_SIZE - eeprom_size; + uint8_t* flash_buffer; + flash_buffer = new uint8_t[SPI_FLASH_SEC_SIZE]; + noInterrupts(); + spi_flash_read(eeprom_sector * SPI_FLASH_SEC_SIZE, reinterpret_cast(flash_buffer), SPI_FLASH_SEC_SIZE); + memcpy(flash_buffer + flash_offset, eeprom_data, eeprom_size); + if (spi_flash_erase_sector(eeprom_sector) == SPI_FLASH_RESULT_OK) { + if (spi_flash_write(eeprom_sector * SPI_FLASH_SEC_SIZE, reinterpret_cast(flash_buffer), SPI_FLASH_SEC_SIZE) == SPI_FLASH_RESULT_OK) { + eeprom_dirty = false; + ret = true; + } + } + interrupts(); + delete[] flash_buffer; + + return ret; +} + +uint8_t * EepromGetDataPtr() +{ + eeprom_dirty = true; + return &eeprom_data[0]; +} + +void EepromEnd(void) +{ + if (!eeprom_size) { return; } + + EepromCommit(); + if (eeprom_data) { + delete[] eeprom_data; + } + eeprom_data = 0; + eeprom_size = 0; + eeprom_dirty = false; +} + +/********************************************************************************************/ + uint16_t settings_crc = 0; uint32_t settings_location = SETTINGS_LOCATION; uint8_t *settings_buffer = NULL; @@ -235,6 +368,7 @@ void SettingsSaveAll(void) Settings.power = 0; } XsnsCall(FUNC_SAVE_BEFORE_RESTART); + EepromCommit(); SettingsSave(0); } @@ -247,7 +381,7 @@ uint32_t GetSettingsAddress(void) return settings_location * SPI_FLASH_SEC_SIZE; } -void SettingsSave(byte rotate) +void SettingsSave(uint8_t rotate) { /* Save configuration in eeprom or one of 7 slots below * @@ -257,7 +391,7 @@ void SettingsSave(byte rotate) * stop_flash_rotate 0 = Allow flash slot rotation (SetOption12 0) * stop_flash_rotate 1 = Allow only eeprom flash slot use (SetOption12 1) */ -#ifndef BE_MINIMAL +#ifndef FIRMWARE_MINIMAL if ((GetSettingsCrc() != settings_crc) || rotate) { if (1 == rotate) { // Use eeprom flash slot only and disable flash rotate from now on (upgrade) stop_flash_rotate = 1; @@ -276,53 +410,86 @@ void SettingsSave(byte rotate) Settings.save_flag++; Settings.cfg_size = sizeof(SYSCFG); Settings.cfg_crc = GetSettingsCrc(); - ESP.flashEraseSector(settings_location); - ESP.flashWrite(settings_location * SPI_FLASH_SEC_SIZE, (uint32*)&Settings, sizeof(SYSCFG)); + + if (SPIFFS_END == settings_location) { + uint8_t* flash_buffer; + flash_buffer = new uint8_t[SPI_FLASH_SEC_SIZE]; + if (eeprom_data && eeprom_size) { + size_t flash_offset = SPI_FLASH_SEC_SIZE - eeprom_size; + memcpy(flash_buffer + flash_offset, eeprom_data, eeprom_size); // Write dirty EEPROM data + } else { + ESP.flashRead(settings_location * SPI_FLASH_SEC_SIZE, (uint32*)flash_buffer, SPI_FLASH_SEC_SIZE); // Read EEPROM area + } + memcpy(flash_buffer, &Settings, sizeof(Settings)); + ESP.flashEraseSector(settings_location); + ESP.flashWrite(settings_location * SPI_FLASH_SEC_SIZE, (uint32*)flash_buffer, SPI_FLASH_SEC_SIZE); + delete[] flash_buffer; + } else { + ESP.flashEraseSector(settings_location); + ESP.flashWrite(settings_location * SPI_FLASH_SEC_SIZE, (uint32*)&Settings, sizeof(SYSCFG)); + } + if (!stop_flash_rotate && rotate) { - for (byte i = 1; i < CFG_ROTATES; i++) { + for (uint8_t i = 1; i < CFG_ROTATES; i++) { ESP.flashEraseSector(settings_location -i); // Delete previous configurations by resetting to 0xFF delay(1); } } - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_CONFIG D_SAVED_TO_FLASH_AT " %X, " D_COUNT " %d, " D_BYTES " %d"), - settings_location, Settings.save_flag, sizeof(SYSCFG)); - AddLog(LOG_LEVEL_DEBUG); + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_CONFIG D_SAVED_TO_FLASH_AT " %X, " D_COUNT " %d, " D_BYTES " %d"), settings_location, Settings.save_flag, sizeof(SYSCFG)); settings_crc = Settings.cfg_crc; } -#endif // BE_MINIMAL +#endif // FIRMWARE_MINIMAL RtcSettingsSave(); } void SettingsLoad(void) { -/* Load configuration from eeprom or one of 7 slots below if first load does not stop_flash_rotate - */ + // Load configuration from eeprom or one of 7 slots below if first valid load does not stop_flash_rotate struct SYSCFGH { uint16_t cfg_holder; // 000 uint16_t cfg_size; // 002 unsigned long save_flag; // 004 } _SettingsH; + unsigned long save_flag = 0; - bool bad_crc = false; - settings_location = SETTINGS_LOCATION +1; - for (byte i = 0; i < CFG_ROTATES; i++) { - settings_location--; - ESP.flashRead(settings_location * SPI_FLASH_SEC_SIZE, (uint32*)&Settings, sizeof(SYSCFG)); - ESP.flashRead((settings_location -1) * SPI_FLASH_SEC_SIZE, (uint32*)&_SettingsH, sizeof(SYSCFGH)); - if (Settings.version > 0x06000000) { bad_crc = (Settings.cfg_crc != GetSettingsCrc()); } - if (Settings.flag.stop_flash_rotate || bad_crc || (Settings.cfg_holder != _SettingsH.cfg_holder) || (Settings.save_flag > _SettingsH.save_flag)) { - break; + settings_location = 0; + uint32_t flash_location = SETTINGS_LOCATION +1; + for (uint8_t i = 0; i < CFG_ROTATES; i++) { + flash_location--; + ESP.flashRead(flash_location * SPI_FLASH_SEC_SIZE, (uint32*)&Settings, sizeof(SYSCFG)); + + bool valid = false; + if (Settings.version > 0x06000000) { + valid = (Settings.cfg_crc == GetSettingsCrc()); + } else { + ESP.flashRead((flash_location -1) * SPI_FLASH_SEC_SIZE, (uint32*)&_SettingsH, sizeof(SYSCFGH)); + valid = (Settings.cfg_holder == _SettingsH.cfg_holder); } + if (valid) { + if (Settings.save_flag > save_flag) { + save_flag = Settings.save_flag; + settings_location = flash_location; + if (Settings.flag.stop_flash_rotate && (0 == i)) { // Stop only if eeprom area should be used and it is valid + break; + } + } + } + delay(1); } - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_CONFIG D_LOADED_FROM_FLASH_AT " %X, " D_COUNT " %d"), settings_location, Settings.save_flag); - AddLog(LOG_LEVEL_DEBUG); + if (settings_location > 0) { + ESP.flashRead(settings_location * SPI_FLASH_SEC_SIZE, (uint32*)&Settings, sizeof(SYSCFG)); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_CONFIG D_LOADED_FROM_FLASH_AT " %X, " D_COUNT " %d"), settings_location, Settings.save_flag); + } -#ifndef BE_MINIMAL - if (bad_crc || (Settings.cfg_holder != (uint16_t)CFG_HOLDER)) { SettingsDefault(); } +#ifndef FIRMWARE_MINIMAL + if (!settings_location || (Settings.cfg_holder != (uint16_t)CFG_HOLDER)) { // Init defaults if cfg_holder differs from user settings in my_user_config.h + SettingsDefault(); + } settings_crc = GetSettingsCrc(); -#endif // BE_MINIMAL +#endif // FIRMWARE_MINIMAL RtcSettingsLoad(); } @@ -334,7 +501,7 @@ void SettingsErase(uint8_t type) 1 = Erase SDK parameter area at end of linker memory model (0x0FDxxx - 0x0FFFFF) solving possible wifi errors */ -#ifndef BE_MINIMAL +#ifndef FIRMWARE_MINIMAL bool result; uint32_t _sectorStart = (ESP.getSketchSize() / SPI_FLASH_SEC_SIZE) + 1; @@ -344,10 +511,9 @@ void SettingsErase(uint8_t type) _sectorEnd = SETTINGS_LOCATION +5; } - boolean _serialoutput = (LOG_LEVEL_DEBUG_MORE <= seriallog_level); + bool _serialoutput = (LOG_LEVEL_DEBUG_MORE <= seriallog_level); - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_APPLICATION D_ERASE " %d " D_UNIT_SECTORS), _sectorEnd - _sectorStart); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_ERASE " %d " D_UNIT_SECTORS), _sectorEnd - _sectorStart); for (uint32_t _sector = _sectorStart; _sector < _sectorEnd; _sector++) { result = ESP.flashEraseSector(_sector); @@ -363,7 +529,7 @@ void SettingsErase(uint8_t type) } OsWatchLoop(); } -#endif // BE_MINIMAL +#endif // FIRMWARE_MINIMAL } // Copied from 2.4.0 as 2.3.0 is incomplete @@ -416,6 +582,8 @@ void SettingsDefaultSet2(void) // Settings.flag.value_units = 0; // Settings.flag.stop_flash_rotate = 0; Settings.save_data = SAVE_DATA; + Settings.param[P_BOOT_LOOP_OFFSET] = BOOT_LOOP_OFFSET; + Settings.param[P_RGB_REMAP] = RGB_REMAP_RGBW; Settings.sleep = APP_SLEEP; if (Settings.sleep < 50) { Settings.sleep = 50; // Default to 50 for sleep, for now @@ -423,8 +591,10 @@ void SettingsDefaultSet2(void) // Module // Settings.flag.interlock = 0; + Settings.interlock[0] = 0xFF; // Legacy support using all relays in one interlock group Settings.module = MODULE; -// for (byte i = 0; i < MAX_GPIO_PIN; i++) { Settings.my_gp.io[i] = 0; } + ModuleDefault(WEMOS); +// for (uint8_t i = 0; i < sizeof(Settings.my_gp); i++) { Settings.my_gp.io[i] = GPIO_NONE; } strlcpy(Settings.friendlyname[0], FRIENDLY_NAME, sizeof(Settings.friendlyname[0])); strlcpy(Settings.friendlyname[1], FRIENDLY_NAME"2", sizeof(Settings.friendlyname[1])); strlcpy(Settings.friendlyname[2], FRIENDLY_NAME"3", sizeof(Settings.friendlyname[2])); @@ -439,7 +609,7 @@ void SettingsDefaultSet2(void) Settings.blinkcount = APP_BLINKCOUNT; Settings.ledstate = APP_LEDSTATE; Settings.pulse_timer[0] = APP_PULSETIME; -// for (byte i = 1; i < MAX_PULSETIMERS; i++) { Settings.pulse_timer[i] = 0; } +// for (uint8_t i = 1; i < MAX_PULSETIMERS; i++) { Settings.pulse_timer[i] = 0; } // Serial Settings.baudrate = APP_BAUDRATE / 1200; @@ -470,6 +640,7 @@ void SettingsDefaultSet2(void) Settings.webserver = WEB_SERVER; Settings.weblog_level = WEB_LOG_LEVEL; strlcpy(Settings.web_password, WEB_PASSWORD, sizeof(Settings.web_password)); + Settings.flag3.mdns_enabled = MDNS_ENABLED; // Button // Settings.flag.button_restrict = 0; @@ -478,7 +649,7 @@ void SettingsDefaultSet2(void) Settings.param[P_HOLD_TIME] = KEY_HOLD_TIME; // Default 4 seconds hold time // Switch - for (byte i = 0; i < MAX_SWITCHES; i++) { Settings.switchmode[i] = SWITCH_MODE; } + for (uint8_t i = 0; i < MAX_SWITCHES; i++) { Settings.switchmode[i] = SWITCH_MODE; } // MQTT Settings.flag.mqtt_enabled = MQTT_USE; @@ -487,6 +658,7 @@ void SettingsDefaultSet2(void) Settings.flag.mqtt_button_retain = MQTT_BUTTON_RETAIN; Settings.flag.mqtt_switch_retain = MQTT_SWITCH_RETAIN; Settings.flag3.button_switch_force_local = MQTT_BUTTON_SWITCH_FORCE_LOCAL; + Settings.flag3.hass_tele_on_power = TELE_ON_POWER; // Settings.flag.mqtt_sensor_retain = 0; // Settings.flag.mqtt_offline = 0; // Settings.flag.mqtt_serial = 0; @@ -512,12 +684,12 @@ void SettingsDefaultSet2(void) char fingerprint[60]; strlcpy(fingerprint, MQTT_FINGERPRINT1, sizeof(fingerprint)); char *p = fingerprint; - for (byte i = 0; i < 20; i++) { + for (uint8_t i = 0; i < 20; i++) { Settings.mqtt_fingerprint[0][i] = strtol(p, &p, 16); } strlcpy(fingerprint, MQTT_FINGERPRINT2, sizeof(fingerprint)); p = fingerprint; - for (byte i = 0; i < 20; i++) { + for (uint8_t i = 0; i < 20; i++) { Settings.mqtt_fingerprint[1][i] = strtol(p, &p, 16); } Settings.tele_period = TELE_PERIOD; @@ -553,17 +725,17 @@ void SettingsDefaultSet2(void) RtcSettings.energy_kWhtotal = 0; // RF Bridge -// for (byte i = 0; i < 17; i++) { Settings.rf_code[i][0] = 0; } +// for (uint8_t i = 0; i < 17; i++) { Settings.rf_code[i][0] = 0; } memcpy_P(Settings.rf_code[0], kDefaultRfCode, 9); // Domoticz Settings.domoticz_update_timer = DOMOTICZ_UPDATE_TIMER; -// for (byte i = 0; i < MAX_DOMOTICZ_IDX; i++) { +// for (uint8_t i = 0; i < MAX_DOMOTICZ_IDX; i++) { // Settings.domoticz_relay_idx[i] = 0; // Settings.domoticz_key_idx[i] = 0; // Settings.domoticz_switch_idx[i] = 0; // } -// for (byte i = 0; i < MAX_DOMOTICZ_SNS_IDX; i++) { +// for (uint8_t i = 0; i < MAX_DOMOTICZ_SNS_IDX; i++) { // Settings.domoticz_sensor_idx[i] = 0; // } @@ -578,7 +750,7 @@ void SettingsDefaultSet2(void) // Rules // Settings.rule_enabled = 0; // Settings.rule_once = 0; -// for (byte i = 1; i < MAX_RULE_SETS; i++) { Settings.rules[i][0] = '\0'; } +// for (uint8_t i = 1; i < MAX_RULE_SETS; i++) { Settings.rules[i][0] = '\0'; } Settings.flag2.calc_resolution = CALC_RESOLUTION; // Home Assistant @@ -596,7 +768,7 @@ void SettingsDefaultSet2(void) //Settings.flag.decimal_text = 0; Settings.pwm_frequency = PWM_FREQ; Settings.pwm_range = PWM_RANGE; - for (byte i = 0; i < MAX_PWMS; i++) { + for (uint8_t i = 0; i < MAX_PWMS; i++) { Settings.light_color[i] = 255; // Settings.pwm_value[i] = 0; } @@ -625,8 +797,8 @@ void SettingsDefaultSet2(void) strlcpy(Settings.ntp_server[0], NTP_SERVER1, sizeof(Settings.ntp_server[0])); strlcpy(Settings.ntp_server[1], NTP_SERVER2, sizeof(Settings.ntp_server[1])); strlcpy(Settings.ntp_server[2], NTP_SERVER3, sizeof(Settings.ntp_server[2])); - for (byte j = 0; j < 3; j++) { - for (byte i = 0; i < strlen(Settings.ntp_server[j]); i++) { + for (uint8_t j = 0; j < 3; j++) { + for (uint8_t i = 0; i < strlen(Settings.ntp_server[j]); i++) { if (Settings.ntp_server[j][i] == ',') { Settings.ntp_server[j][i] = '.'; } @@ -639,7 +811,7 @@ void SettingsDefaultSet2(void) Settings.button_debounce = KEY_DEBOUNCE_TIME; Settings.switch_debounce = SWITCH_DEBOUNCE_TIME; - for (byte j = 0; j < 5; j++) { + for (uint8_t j = 0; j < 5; j++) { Settings.rgbwwTable[j] = 255; } @@ -720,7 +892,7 @@ void SettingsDelta(void) if (Settings.version != VERSION) { // Fix version dependent changes if (Settings.version < 0x05050000) { - for (byte i = 0; i < 17; i++) { Settings.rf_code[i][0] = 0; } + for (uint8_t i = 0; i < 17; i++) { Settings.rf_code[i][0] = 0; } memcpy_P(Settings.rf_code[0], kDefaultRfCode, 9); } if (Settings.version < 0x05080000) { @@ -742,12 +914,12 @@ void SettingsDelta(void) Settings.altitude = 0; } if (Settings.version < 0x0508000B) { - for (byte i = 0; i < MAX_GPIO_PIN; i++) { // Move GPIO_LEDs + for (uint8_t i = 0; i < sizeof(Settings.my_gp); i++) { // Move GPIO_LEDs if ((Settings.my_gp.io[i] >= 25) && (Settings.my_gp.io[i] <= 32)) { // Was GPIO_LED1 Settings.my_gp.io[i] += 23; // Move GPIO_LED1 } } - for (byte i = 0; i < MAX_PWMS; i++) { // Move pwm_value and reset additional pulse_timerrs + for (uint8_t i = 0; i < MAX_PWMS; i++) { // Move pwm_value and reset additional pulse_timerrs Settings.pwm_value[i] = Settings.pulse_timer[4 +i]; Settings.pulse_timer[4 +i] = 0; } @@ -778,7 +950,7 @@ void SettingsDelta(void) char fingerprint[60]; memcpy(fingerprint, Settings.mqtt_fingerprint, sizeof(fingerprint)); char *p = fingerprint; - for (byte i = 0; i < 20; i++) { + for (uint8_t i = 0; i < 20; i++) { Settings.mqtt_fingerprint[0][i] = strtol(p, &p, 16); Settings.mqtt_fingerprint[1][i] = Settings.mqtt_fingerprint[0][i]; } @@ -813,7 +985,7 @@ void SettingsDelta(void) SettingsDefaultSet_5_13_1c(); } if (Settings.version < 0x050E0002) { - for (byte i = 1; i < MAX_RULE_SETS; i++) { Settings.rules[i][0] = '\0'; } + for (uint8_t i = 1; i < MAX_RULE_SETS; i++) { Settings.rules[i][0] = '\0'; } Settings.rule_enabled = Settings.flag.mqtt_serial_raw; // Was rules_enabled until 5.14.0b Settings.rule_once = Settings.flag.pressure_conversion; // Was rules_once until 5.14.0b } @@ -822,14 +994,14 @@ void SettingsDelta(void) Settings.cfg_crc = GetSettingsCrc(); } if (Settings.version < 0x06000002) { - for (byte i = 0; i < MAX_SWITCHES; i++) { + for (uint8_t i = 0; i < MAX_SWITCHES; i++) { if (i < 4) { - Settings.switchmode[i] = Settings.ex_switchmode[i]; + Settings.switchmode[i] = Settings.interlock[i]; } else { Settings.switchmode[i] = SWITCH_MODE; } } - for (byte i = 0; i < MAX_GPIO_PIN; i++) { + for (uint8_t i = 0; i < sizeof(Settings.my_gp); i++) { if (Settings.my_gp.io[i] >= GPIO_SWT5) { // Move up from GPIO_SWT5 to GPIO_KEY1 Settings.my_gp.io[i] += 4; } @@ -848,7 +1020,7 @@ void SettingsDelta(void) Settings.switch_debounce = SWITCH_DEBOUNCE_TIME; } if (Settings.version < 0x0602010A) { - for (byte j = 0; j < 5; j++) { + for (uint8_t j = 0; j < 5; j++) { Settings.rgbwwTable[j] = 255; } } @@ -866,6 +1038,23 @@ void SettingsDelta(void) Settings.sleep = 50; // Default to 50 for sleep, for now } } + if (Settings.version < 0x06040105) { + Settings.flag3.mdns_enabled = MDNS_ENABLED; + Settings.param[P_MDNS_DELAYED_START] = 0; + } + if (Settings.version < 0x0604010B) { + Settings.interlock[0] = 0xFF; // Legacy support using all relays in one interlock group + for (uint8_t i = 1; i < MAX_INTERLOCKS; i++) { Settings.interlock[i] = 0; } + } + if (Settings.version < 0x0604010D) { + Settings.param[P_BOOT_LOOP_OFFSET] = BOOT_LOOP_OFFSET; + } + if (Settings.version < 0x06040110) { + ModuleDefault(WEMOS); + } + if (Settings.version < 0x06040113) { + Settings.param[P_RGB_REMAP] = RGB_REMAP_RGBW; + } Settings.version = VERSION; SettingsSave(1); diff --git a/sonoff/sonoff.h b/sonoff/sonoff.h index 7c2de21e6..6c44f54fd 100644 --- a/sonoff/sonoff.h +++ b/sonoff/sonoff.h @@ -1,7 +1,7 @@ /* sonoff.h - Master header file for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends + Copyright (C) 2019 Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -51,6 +51,7 @@ typedef unsigned long power_t; // Power (Relay) type // Changes to the following MAX_ defines will impact settings layout #define MAX_SWITCHES 8 // Max number of switches #define MAX_RELAYS 8 // Max number of relays +#define MAX_INTERLOCKS 4 // Max number of interlock groups (MAX_RELAYS / 2) #define MAX_LEDS 4 // Max number of leds #define MAX_KEYS 4 // Max number of keys or buttons #define MAX_PWMS 5 // Max number of PWM channels @@ -103,6 +104,7 @@ typedef unsigned long power_t; // Power (Relay) type #define PWM_MIN 100 // [PWM_MIN] Minimum frequency - Default: 100 // For Dimmers use double of your mains AC frequecy (100 for 50Hz and 120 for 60Hz) // For Controlling Servos use 50 and also set PWM_FREQ as 50 (DO NOT USE THESE VALUES FOR DIMMERS) +//#define PWM_LIGHTSCHEME0_IGNORE_SLEEP // Do not change sleep value for LightAnimate() scheme 0 #define DEFAULT_POWER_DELTA 80 // Power change percentage #define MAX_POWER_HOLD 10 // Time in SECONDS to allow max agreed power @@ -167,6 +169,15 @@ typedef unsigned long power_t; // Power (Relay) type #define NEO_RGBW 5 // Neopixel RGBW leds #define NEO_GRBW 6 // Neopixel GRBW leds +#define LT_SM16716 16 // Lights that use SM16716 will have this bit set in light_type + +#define RGB_REMAP_RGBW 0 +#define RGB_REMAP_RBGW 6 +#define RGB_REMAP_GRBW 24 +#define RGB_REMAP_GBRW 30 +#define RGB_REMAP_BRGW 48 +#define RGB_REMAP_BGRW 54 + #define MQTT_PUBSUBCLIENT 1 // Mqtt PubSubClient library #define MQTT_TASMOTAMQTT 2 // Mqtt TasmotaMqtt library based on esp-mqtt-arduino - soon obsolete #define MQTT_ESPMQTTARDUINO 3 // Mqtt esp-mqtt-arduino library by Ingo Randolf - obsolete but define is present for debugging purposes @@ -208,7 +219,7 @@ enum GetDateAndTimeOptions { DT_LOCAL, DT_UTC, DT_RESTART, DT_ENERGY }; enum LoggingLevels {LOG_LEVEL_NONE, LOG_LEVEL_ERROR, LOG_LEVEL_INFO, LOG_LEVEL_DEBUG, LOG_LEVEL_DEBUG_MORE, LOG_LEVEL_ALL}; -enum WifiConfigOptions {WIFI_RESTART, WIFI_SMARTCONFIG, WIFI_MANAGER, WIFI_WPSCONFIG, WIFI_RETRY, WIFI_WAIT, WIFI_SERIAL, MAX_WIFI_OPTION}; +enum WifiConfigOptions {WIFI_RESTART, WIFI_SMARTCONFIG, WIFI_MANAGER, WIFI_WPSCONFIG, WIFI_RETRY, WIFI_WAIT, WIFI_SERIAL, WIFI_MANAGER_RESET_ONLY, MAX_WIFI_OPTION}; enum SwitchModeOptions {TOGGLE, FOLLOW, FOLLOW_INV, PUSHBUTTON, PUSHBUTTON_INV, PUSHBUTTONHOLD, PUSHBUTTONHOLD_INV, PUSHBUTTON_TOGGLE, MAX_SWITCH_OPTION}; @@ -226,7 +237,7 @@ enum ButtonStates { PRESSED, NOT_PRESSED }; enum Shortcuts { SC_CLEAR, SC_DEFAULT, SC_USER }; -enum SettingsParmaIndex {P_HOLD_TIME, P_MAX_POWER_RETRY, P_TUYA_DIMMER_ID, P_MDNS_DELAYED_START, P_MAX_PARAM8}; // Max is PARAM8_SIZE (18) - SetOption32 until SetOption49 +enum SettingsParmaIndex {P_HOLD_TIME, P_MAX_POWER_RETRY, P_TUYA_DIMMER_ID, P_MDNS_DELAYED_START, P_BOOT_LOOP_OFFSET, P_RGB_REMAP, P_MAX_PARAM8}; // Max is PARAM8_SIZE (18) - SetOption32 until SetOption49 enum DomoticzSensors {DZ_TEMP, DZ_TEMP_HUM, DZ_TEMP_HUM_BARO, DZ_POWER_ENERGY, DZ_ILLUMINANCE, DZ_COUNT, DZ_VOLTAGE, DZ_CURRENT, DZ_AIRQUALITY, DZ_MAX_SENSORS}; @@ -241,7 +252,7 @@ enum LightSchemes {LS_POWER, LS_WAKEUP, LS_CYCLEUP, LS_CYCLEDN, LS_RANDOM, LS_MA enum XsnsFunctions {FUNC_SETTINGS_OVERRIDE, FUNC_MODULE_INIT, FUNC_PRE_INIT, FUNC_INIT, FUNC_LOOP, FUNC_EVERY_50_MSECOND, FUNC_EVERY_100_MSECOND, FUNC_EVERY_200_MSECOND, FUNC_EVERY_250_MSECOND, FUNC_EVERY_SECOND, - FUNC_PREP_BEFORE_TELEPERIOD, FUNC_JSON_APPEND, FUNC_WEB_APPEND, FUNC_SAVE_BEFORE_RESTART, FUNC_COMMAND, + FUNC_PREP_BEFORE_TELEPERIOD, FUNC_JSON_APPEND, FUNC_WEB_APPEND, FUNC_SAVE_BEFORE_RESTART, FUNC_COMMAND, FUNC_COMMAND_SENSOR, FUNC_COMMAND_DRIVER, FUNC_MQTT_SUBSCRIBE, FUNC_MQTT_INIT, FUNC_MQTT_DATA, FUNC_SET_POWER, FUNC_SET_DEVICE_POWER, FUNC_SHOW_SENSOR, FUNC_RULES_PROCESS, FUNC_SERIAL, FUNC_FREE_MEM, FUNC_BUTTON_PRESSED, @@ -260,5 +271,7 @@ const uint8_t kIFan02Speed[MAX_FAN_SPEED][3] = {{6,6,6}, {7,6,6}, {7,7,6}, {7,6, \*********************************************************************************************/ extern uint8_t light_device; // Light device number +extern uint8_t light_power; // Light power +extern uint8_t rotary_changed; // Rotary switch changed #endif // _SONOFF_H_ diff --git a/sonoff/sonoff.ino b/sonoff/sonoff.ino index be5964a94..82c306c90 100755 --- a/sonoff/sonoff.ino +++ b/sonoff/sonoff.ino @@ -1,7 +1,7 @@ /* sonoff.ino - Sonoff-Tasmota firmware for iTead Sonoff, Wemos and NodeMCU hardware - Copyright (C) 2018 Theo Arends + Copyright (C) 2019 Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -75,7 +75,7 @@ enum TasmotaCommands { CMND_MODULE, CMND_MODULES, CMND_GPIO, CMND_GPIOS, CMND_PWM, CMND_PWMFREQUENCY, CMND_PWMRANGE, CMND_COUNTER, CMND_COUNTERTYPE, CMND_COUNTERDEBOUNCE, CMND_BUTTONDEBOUNCE, CMND_SWITCHDEBOUNCE, CMND_SLEEP, CMND_UPGRADE, CMND_UPLOAD, CMND_OTAURL, CMND_SERIALLOG, CMND_SYSLOG, CMND_LOGHOST, CMND_LOGPORT, CMND_IPADDRESS, CMND_NTPSERVER, CMND_AP, CMND_SSID, CMND_PASSWORD, CMND_HOSTNAME, - CMND_WIFICONFIG, CMND_FRIENDLYNAME, CMND_SWITCHMODE, + CMND_WIFICONFIG, CMND_FRIENDLYNAME, CMND_SWITCHMODE, CMND_INTERLOCK, CMND_TEMPLATE, CMND_TELEPERIOD, CMND_RESTART, CMND_RESET, CMND_TIMEZONE, CMND_TIMESTD, CMND_TIMEDST, CMND_ALTITUDE, CMND_LEDPOWER, CMND_LEDSTATE, CMND_I2CSCAN, CMND_SERIALSEND, CMND_BAUDRATE, CMND_SERIALDELIMITER, CMND_DRIVER }; const char kTasmotaCommands[] PROGMEM = @@ -85,7 +85,7 @@ const char kTasmotaCommands[] PROGMEM = D_CMND_MODULE "|" D_CMND_MODULES "|" D_CMND_GPIO "|" D_CMND_GPIOS "|" D_CMND_PWM "|" D_CMND_PWMFREQUENCY "|" D_CMND_PWMRANGE "|" D_CMND_COUNTER "|" D_CMND_COUNTERTYPE "|" D_CMND_COUNTERDEBOUNCE "|" D_CMND_BUTTONDEBOUNCE "|" D_CMND_SWITCHDEBOUNCE "|" D_CMND_SLEEP "|" D_CMND_UPGRADE "|" D_CMND_UPLOAD "|" D_CMND_OTAURL "|" D_CMND_SERIALLOG "|" D_CMND_SYSLOG "|" D_CMND_LOGHOST "|" D_CMND_LOGPORT "|" D_CMND_IPADDRESS "|" D_CMND_NTPSERVER "|" D_CMND_AP "|" D_CMND_SSID "|" D_CMND_PASSWORD "|" D_CMND_HOSTNAME "|" - D_CMND_WIFICONFIG "|" D_CMND_FRIENDLYNAME "|" D_CMND_SWITCHMODE "|" + D_CMND_WIFICONFIG "|" D_CMND_FRIENDLYNAME "|" D_CMND_SWITCHMODE "|" D_CMND_INTERLOCK "|" D_CMND_TEMPLATE "|" D_CMND_TELEPERIOD "|" D_CMND_RESTART "|" D_CMND_RESET "|" D_CMND_TIMEZONE "|" D_CMND_TIMESTD "|" D_CMND_TIMEDST "|" D_CMND_ALTITUDE "|" D_CMND_LEDPOWER "|" D_CMND_LEDSTATE "|" D_CMND_I2CSCAN "|" D_CMND_SERIALSEND "|" D_CMND_BAUDRATE "|" D_CMND_SERIALDELIMITER "|" D_CMND_DRIVER; @@ -108,8 +108,6 @@ unsigned long state_250msecond = 0; // State 250msecond timer unsigned long pulse_timer[MAX_PULSETIMERS] = { 0 }; // Power off timer unsigned long blink_timer = 0; // Power cycle timer unsigned long backlog_delay = 0; // Command backlog delay -unsigned long button_debounce = 0; // Button debounce timer -unsigned long switch_debounce = 0; // Switch debounce timer power_t power = 0; // Current copy of Settings.power power_t blink_power; // Blink power state power_t blink_mask = 0; // Blink relay active mask @@ -130,57 +128,48 @@ uint32_t global_update = 0; // Timestamp of last global temperat float global_temperature = 0; // Provide a global temperature to be used by some sensors float global_humidity = 0; // Provide a global humidity to be used by some sensors char *ota_url; // OTA url string pointer -uint16_t dual_button_code = 0; // Sonoff dual received code uint16_t mqtt_cmnd_publish = 0; // ignore flag for publish command uint16_t blink_counter = 0; // Number of blink cycles uint16_t seriallog_timer = 0; // Timer to disable Seriallog uint16_t syslog_timer = 0; // Timer to re-enable syslog_level -uint16_t holdbutton[MAX_KEYS] = { 0 }; // Timer for button hold -uint16_t switch_no_pullup = 0; // Switch pull-up bitmask flags int16_t save_data_counter; // Counter and flag for config save to Flash RulesBitfield rules_flag; // Rule state flags (16 bits) -uint8_t serial_local = 0; // Handle serial locally; -uint8_t fallback_topic_flag = 0; // Use Topic or FallbackTopic uint8_t state_250mS = 0; // State 250msecond per second flag uint8_t latching_relay_pulse = 0; // Latching relay pulse timer uint8_t backlog_index = 0; // Command backlog index uint8_t backlog_pointer = 0; // Command backlog pointer -uint8_t backlog_mutex = 0; // Command backlog pending -uint8_t interlock_mutex = 0; // Interlock power command pending uint8_t sleep; // Current copy of Settings.sleep -uint8_t stop_flash_rotate = 0; // Allow flash configuration rotation -uint8_t blinkstate = 0; // LED state uint8_t blinkspeed = 1; // LED blink rate -uint8_t lastbutton[MAX_KEYS] = { NOT_PRESSED, NOT_PRESSED, NOT_PRESSED, NOT_PRESSED }; // Last button states -uint8_t multiwindow[MAX_KEYS] = { 0 }; // Max time between button presses to record press count -uint8_t multipress[MAX_KEYS] = { 0 }; // Number of button presses within multiwindow -uint8_t lastwallswitch[MAX_SWITCHES]; // Last wall switch states -uint8_t holdwallswitch[MAX_SWITCHES] = { 0 }; // Timer for wallswitch push button hold -uint8_t virtualswitch[MAX_SWITCHES]; // Virtual switch states uint8_t pin[GPIO_MAX]; // Possible pin configurations uint8_t led_inverted = 0; // LED inverted flag (1 = (0 = On, 1 = Off)) uint8_t pwm_inverted = 0; // PWM inverted flag (1 = inverted) uint8_t counter_no_pullup = 0; // Counter input pullup flag (1 = No pullup) -uint8_t dht_flg = 0; // DHT configured uint8_t energy_flg = 0; // Energy monitor configured -uint8_t i2c_flg = 0; // I2C configured -uint8_t spi_flg = 0; // SPI configured -uint8_t soft_spi_flg = 0; // Software SPI configured uint8_t light_type = 0; // Light types -uint8_t ntp_force_sync = 0; // Force NTP sync -byte serial_in_byte; // Received byte -byte dual_hex_code = 0; // Sonoff dual input flag -byte ota_retry_counter = OTA_ATTEMPTS; // OTA retry counter -byte web_log_index = 1; // Index in Web log buffer (should never be 0) -byte reset_web_log_flag = 0; // Reset web console log -byte devices_present = 0; // Max number of devices supported -byte seriallog_level; // Current copy of Settings.seriallog_level -byte syslog_level; // Current copy of Settings.syslog_level -byte mdns_delayed_start = 0; // mDNS delayed start -boolean latest_uptime_flag = true; // Signal latest uptime -boolean pwm_present = false; // Any PWM channel configured with SetOption15 0 -boolean mdns_begun = false; // mDNS active -mytmplt my_module; // Active copy of Module name and GPIOs (23 x 8 bits) +uint8_t serial_in_byte; // Received byte +uint8_t ota_retry_counter = OTA_ATTEMPTS; // OTA retry counter +uint8_t web_log_index = 1; // Index in Web log buffer (should never be 0) +uint8_t devices_present = 0; // Max number of devices supported +uint8_t seriallog_level; // Current copy of Settings.seriallog_level +uint8_t syslog_level; // Current copy of Settings.syslog_level +uint8_t my_module_type; // Current copy of Settings.module or user template type +//uint8_t mdns_delayed_start = 0; // mDNS delayed start +bool serial_local = false; // Handle serial locally; +bool fallback_topic_flag = false; // Use Topic or FallbackTopic +bool backlog_mutex = false; // Command backlog pending +bool interlock_mutex = false; // Interlock power command pending +bool stop_flash_rotate = false; // Allow flash configuration rotation +bool blinkstate = false; // LED state +bool latest_uptime_flag = true; // Signal latest uptime +bool pwm_present = false; // Any PWM channel configured with SetOption15 0 +bool dht_flg = false; // DHT configured +bool i2c_flg = false; // I2C configured +bool spi_flg = false; // SPI configured +bool soft_spi_flg = false; // Software SPI configured +bool ntp_force_sync = false; // Force NTP sync +bool reset_web_log_flag = false; // Reset web console log +myio my_module; // Active copy of Module GPIOs (17 x 8 bits) +gpio_flag my_module_flag; // Active copy of Module GPIO flags StateBitfield global_state; // Global states (currently Wifi and Mqtt) (8 bits) char my_version[33]; // Composed version string char my_image[33]; // Code image and/or commit @@ -193,7 +182,6 @@ char log_data[LOGSZ]; // Logging char web_log[WEB_LOG_SIZE] = {'\0'}; // Web log buffer String backlog[MAX_BACKLOG]; // Command backlog - /********************************************************************************************/ char* Format(char* output, const char* input, int size) @@ -212,12 +200,13 @@ char* Format(char* output, const char* input, int size) if (token != NULL) { digits = atoi(token); if (digits) { + char tmp[size]; if (strchr(token, 'd')) { - snprintf_P(output, size, PSTR("%s%c0%dd"), output, '%', digits); - snprintf_P(output, size, output, ESP.getChipId() & 0x1fff); // %04d - short chip ID in dec, like in hostname + snprintf_P(tmp, size, PSTR("%s%c0%dd"), output, '%', digits); + snprintf_P(output, size, tmp, ESP.getChipId() & 0x1fff); // %04d - short chip ID in dec, like in hostname } else { - snprintf_P(output, size, PSTR("%s%c0%dX"), output, '%', digits); - snprintf_P(output, size, output, ESP.getChipId()); // %06X - full chip ID in hex + snprintf_P(tmp, size, PSTR("%s%c0%dX"), output, '%', digits); + snprintf_P(output, size, tmp, ESP.getChipId()); // %06X - full chip ID in hex } } else { if (strchr(token, 'd')) { @@ -227,7 +216,7 @@ char* Format(char* output, const char* input, int size) } } } - if (!digits) strlcpy(output, input, size); + if (!digits) { strlcpy(output, input, size); } return output; } @@ -240,12 +229,12 @@ char* GetOtaUrl(char *otaurl, size_t otaurl_size) snprintf_P(otaurl, otaurl_size, Settings.ota_url, ESP.getChipId()); } else { - snprintf(otaurl, otaurl_size, Settings.ota_url); + strlcpy(otaurl, Settings.ota_url, otaurl_size); } return otaurl; } -char* GetTopic_P(char *stopic, byte prefix, char *topic, const char* subtopic) +char* GetTopic_P(char *stopic, uint8_t prefix, char *topic, const char* subtopic) { /* prefix 0 = Cmnd prefix 1 = Stat @@ -269,7 +258,7 @@ char* GetTopic_P(char *stopic, byte prefix, char *topic, const char* subtopic) if ((0 == prefix) && (-1 == fulltopic.indexOf(F(MQTT_TOKEN_PREFIX)))) { fulltopic += F("/" MQTT_TOKEN_PREFIX); // Need prefix for commands to handle mqtt topic loops } - for (byte i = 0; i < 3; i++) { + for (uint8_t i = 0; i < 3; i++) { if ('\0' == Settings.mqtt_prefix[i][0]) { snprintf_P(Settings.mqtt_prefix[i], sizeof(Settings.mqtt_prefix[i]), kPrefixes[i]); } @@ -288,14 +277,14 @@ char* GetTopic_P(char *stopic, byte prefix, char *topic, const char* subtopic) return stopic; } -char* GetFallbackTopic_P(char *stopic, byte prefix, const char* subtopic) +char* GetFallbackTopic_P(char *stopic, uint8_t prefix, const char* subtopic) { return GetTopic_P(stopic, prefix +4, NULL, subtopic); } -char* GetStateText(byte state) +char* GetStateText(uint8_t state) { - if (state > 3) state = 1; + if (state > 3) { state = 1; } return Settings.state_text[state]; } @@ -313,7 +302,7 @@ void SetLatchingRelay(power_t lpower, uint8_t state) latching_relay_pulse = 2; // max 200mS (initiated by stateloop()) } - for (byte i = 0; i < devices_present; i++) { + for (uint8_t i = 0; i < devices_present; i++) { uint8_t port = (i << 1) + ((latching_power >> i) &1); if (pin[GPIO_REL1 +port] < 99) { digitalWrite(pin[GPIO_REL1 +port], bitRead(rel_inverted, port) ? !state : state); @@ -331,16 +320,20 @@ void SetDevicePower(power_t rpower, int source) power = (1 << devices_present) -1; rpower = power; } - if (Settings.flag.interlock) { // Allow only one or no relay set - power_t mask = 1; - uint8_t count = 0; - for (byte i = 0; i < devices_present; i++) { - if (rpower & mask) count++; - mask <<= 1; - } - if (count > 1) { - power = 0; - rpower = 0; + + if (Settings.flag.interlock) { // Allow only one or no relay set + for (uint8_t i = 0; i < MAX_INTERLOCKS; i++) { + power_t mask = 1; + uint8_t count = 0; + for (uint8_t j = 0; j < devices_present; j++) { + if ((Settings.interlock[i] & mask) && (rpower & mask)) { count++; } + mask <<= 1; + } + if (count > 1) { + mask = ~Settings.interlock[i]; // Turn interlocked group off as there would be multiple relays on + power &= mask; + rpower &= mask; + } } } @@ -352,7 +345,7 @@ void SetDevicePower(power_t rpower, int source) if (XdrvCall(FUNC_SET_DEVICE_POWER)) { // Set power state and stop if serviced // Serviced } - else if ((SONOFF_DUAL == Settings.module) || (CH4 == Settings.module)) { + else if ((SONOFF_DUAL == my_module_type) || (CH4 == my_module_type)) { Serial.write(0xA0); Serial.write(0x04); Serial.write(rpower &0xFF); @@ -360,11 +353,11 @@ void SetDevicePower(power_t rpower, int source) Serial.write('\n'); Serial.flush(); } - else if (EXS_RELAY == Settings.module) { + else if (EXS_RELAY == my_module_type) { SetLatchingRelay(rpower, 1); } else { - for (byte i = 0; i < devices_present; i++) { + for (uint8_t i = 0; i < devices_present; i++) { state = rpower &1; if ((i < MAX_RELAYS) && (pin[GPIO_REL1 +i] < 99)) { digitalWrite(pin[GPIO_REL1 +i], bitRead(rel_inverted, i) ? !state : state); @@ -376,7 +369,16 @@ void SetDevicePower(power_t rpower, int source) void SetLedPower(uint8_t state) { - if (state) state = 1; + if (state) { state = 1; } + + uint8_t led_pin = 0; + if (pin[GPIO_LED2] < 99) { led_pin = 1; } + digitalWrite(pin[GPIO_LED1 + led_pin], (bitRead(led_inverted, led_pin)) ? !state : state); +} + +void SetLedLink(uint8_t state) +{ + if (state) { state = 1; } digitalWrite(pin[GPIO_LED1], (bitRead(led_inverted, 0)) ? !state : state); } @@ -384,7 +386,7 @@ uint8_t GetFanspeed(void) { uint8_t fanspeed = 0; -// if (SONOFF_IFAN02 == Settings.module) { +// if (SONOFF_IFAN02 == my_module_type) { /* Fanspeed is controlled by relay 2, 3 and 4 as in Sonoff 4CH. 000x = 0 001x = 1 @@ -399,7 +401,7 @@ uint8_t GetFanspeed(void) void SetFanspeed(uint8_t fanspeed) { - for (byte i = 0; i < MAX_FAN_SPEED -1; i++) { + for (uint8_t i = 0; i < MAX_FAN_SPEED -1; i++) { uint8_t state = kIFan02Speed[fanspeed][i]; // uint8_t state = pgm_read_byte(kIFan02Speed +(speed *3) +i); ExecuteCommandPower(i +2, state, SRC_IGNORE); // Use relay 2, 3 and 4 @@ -428,8 +430,10 @@ uint16_t GetPulseTimer(uint8_t index) /********************************************************************************************/ -void MqttDataHandler(char* topic, byte* data, unsigned int data_len) +void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) { + if (data_len > MQTT_MAX_PACKET_SIZE) { return; } // Do not allow more data than would be feasable within stack space + char *str; if (!strcmp(Settings.mqtt_prefix[0],Settings.mqtt_prefix[1])) { @@ -450,19 +454,21 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) char stemp1[TOPSZ]; char *p; char *type = NULL; - byte jsflg = 0; - byte lines = 1; - uint8_t grpflg = 0; -// uint8_t user_append_index = 0; + uint8_t lines = 1; + bool jsflg = false; + bool grpflg = false; +// bool user_append_index = false; uint16_t i = 0; uint16_t index; uint32_t address; +#ifdef USE_DEBUG_DRIVER ShowFreeMem(PSTR("MqttDataHandler")); +#endif strlcpy(topicBuf, topic, sizeof(topicBuf)); for (i = 0; i < data_len; i++) { - if (!isspace(data[i])) break; + if (!isspace(data[i])) { break; } } data_len -= i; memcpy(dataBuf, data +i, sizeof(dataBuf)); @@ -470,12 +476,10 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) if (topicBuf[0] != '/') { ShowSource(SRC_MQTT); } - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_RESULT D_RECEIVED_TOPIC " %s, " D_DATA_SIZE " %d, " D_DATA " %s"), - topicBuf, data_len, dataBuf); - AddLog(LOG_LEVEL_DEBUG_MORE); -// if (LOG_LEVEL_DEBUG_MORE <= seriallog_level) Serial.println(dataBuf); + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_RESULT D_RECEIVED_TOPIC " %s, " D_DATA_SIZE " %d, " D_DATA " %s"), topicBuf, data_len, dataBuf); +// if (LOG_LEVEL_DEBUG_MORE <= seriallog_level) { Serial.println(dataBuf); } - if (XdrvMqttData(topicBuf, sizeof(topicBuf), dataBuf, sizeof(dataBuf))) return; + if (XdrvMqttData(topicBuf, sizeof(topicBuf), dataBuf, sizeof(dataBuf))) { return; } grpflg = (strstr(topicBuf, Settings.mqtt_grptopic) != NULL); @@ -495,20 +499,18 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) } if (i < strlen(type)) { index = atoi(type +i); -// user_append_index = 1; +// user_append_index = true; } type[i] = '\0'; } - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_RESULT D_GROUP " %d, " D_INDEX " %d, " D_COMMAND " %s, " D_DATA " %s"), - grpflg, index, type, dataBuf); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_RESULT D_GROUP " %d, " D_INDEX " %d, " D_COMMAND " %s, " D_DATA " %s"), grpflg, index, type, dataBuf); if (type != NULL) { snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_COMMAND "\":\"" D_JSON_ERROR "\"}")); - if (Settings.ledstate &0x02) blinks++; + if (Settings.ledstate &0x02) { blinks++; } - if (!strcmp(dataBuf,"?")) data_len = 0; + if (!strcmp(dataBuf,"?")) { data_len = 0; } int16_t payload = -99; // No payload uint16_t payload16 = 0; long payload32 = strtol(dataBuf, &p, 10); @@ -523,13 +525,22 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) int temp_payload = GetStateNumber(dataBuf); if (temp_payload > -1) { payload = temp_payload; } -// snprintf_P(log_data, sizeof(log_data), PSTR("RSLT: Payload %d, Payload16 %d"), payload, payload16); -// AddLog(LOG_LEVEL_DEBUG); +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RSLT: Payload %d, Payload16 %d"), payload, payload16); int command_code = GetCommandCode(command, sizeof(command), type, kTasmotaCommands); if (-1 == command_code) { - if (!XdrvCommand(grpflg, type, index, dataBuf, data_len, payload, payload16)) { - type = NULL; // Unknown command +// XdrvMailbox.valid = 1; + XdrvMailbox.index = index; + XdrvMailbox.data_len = data_len; + XdrvMailbox.payload16 = payload16; + XdrvMailbox.payload = payload; + XdrvMailbox.grpflg = grpflg; + XdrvMailbox.topic = type; + XdrvMailbox.data = dataBuf; + if (!XdrvCall(FUNC_COMMAND)) { + if (!XsnsCall(FUNC_COMMAND)) { + type = NULL; // Unknown command + } } } else if (CMND_BACKLOG == command_code) { @@ -571,13 +582,13 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, bl_delay); } else if ((CMND_POWER == command_code) && (index > 0) && (index <= devices_present)) { - if ((payload < 0) || (payload > 4)) payload = 9; + if ((payload < 0) || (payload > 4)) { payload = 9; } // Settings.flag.device_index_enable = user_append_index; ExecuteCommandPower(index, payload, SRC_IGNORE); - fallback_topic_flag = 0; + fallback_topic_flag = false; return; } - else if ((CMND_FANSPEED == command_code) && (SONOFF_IFAN02 == Settings.module)) { + else if ((CMND_FANSPEED == command_code) && (SONOFF_IFAN02 == my_module_type)) { if (data_len > 0) { if ('-' == dataBuf[0]) { payload = (int16_t)GetFanspeed() -1; @@ -596,7 +607,7 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) else if (CMND_STATUS == command_code) { if ((payload < 0) || (payload > MAX_STATUS)) payload = 99; PublishStatus(payload); - fallback_topic_flag = 0; + fallback_topic_flag = false; return; } else if (CMND_STATE == command_code) { @@ -621,9 +632,9 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) // We also need at least 3 chars to make a valid version number string. if (((1 == data_len) && (1 == payload)) || ((data_len >= 3) && NewerVersion(dataBuf))) { ota_state_flag = 3; - snprintf_P(mqtt_data, sizeof(mqtt_data), "{\"%s\":\"" D_JSON_VERSION " %s " D_JSON_FROM " %s\"}", command, my_version, GetOtaUrl(stemp1, sizeof(stemp1))); + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"%s\":\"" D_JSON_VERSION " %s " D_JSON_FROM " %s\"}"), command, my_version, GetOtaUrl(stemp1, sizeof(stemp1))); } else { - snprintf_P(mqtt_data, sizeof(mqtt_data), "{\"%s\":\"" D_JSON_ONE_OR_GT "\"}", command, my_version); + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"%s\":\"" D_JSON_ONE_OR_GT "\"}"), command, my_version); } } else if (CMND_OTAURL == command_code) { @@ -653,7 +664,7 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, D_JSON_ONE_TO_RESTART); } } - else if ((CMND_POWERONSTATE == command_code) && (Settings.module != MOTOR)) { + else if ((CMND_POWERONSTATE == command_code) && (my_module_type != MOTOR)) { /* 0 = Keep relays off after power on * 1 = Turn relays on after power on, if PulseTime set wait for PulseTime seconds, and turn relays off * 2 = Toggle relays after power on @@ -664,7 +675,7 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) if ((payload >= POWER_ALL_OFF) && (payload <= POWER_ALL_OFF_PULSETIME_ON)) { Settings.poweronstate = payload; if (POWER_ALL_ALWAYS_ON == Settings.poweronstate) { - for (byte i = 1; i <= devices_present; i++) { + for (uint8_t i = 1; i <= devices_present; i++) { ExecuteCommandPower(i, POWER_ON, SRC_IGNORE); } } @@ -712,14 +723,14 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) XdrvMailbox.topic = command; XdrvMailbox.data = dataBuf; if (CMND_SENSOR == command_code) { - XsnsCall(FUNC_COMMAND); + XsnsCall(FUNC_COMMAND_SENSOR); } else { - XdrvCall(FUNC_COMMAND); + XdrvCall(FUNC_COMMAND_DRIVER); } } else if ((CMND_SETOPTION == command_code) && (index < 82)) { - byte ptype; - byte pindex; + uint8_t ptype; + uint8_t pindex; if (index <= 31) { // SetOption0 .. 31 = Settings.flag ptype = 0; pindex = index; // 0 .. 31 @@ -740,6 +751,7 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) case 6: // mqtt_button_retain (CMND_BUTTONRETAIN) case 7: // mqtt_switch_retain (CMND_SWITCHRETAIN) case 9: // mqtt_sensor_retain (CMND_SENSORRETAIN) + case 14: // interlock (CMND_INTERLOCK) case 22: // mqtt_serial (SerialSend and SerialLog) case 23: // mqtt_serial_raw (SerialSend) case 25: // knx_enabled (Web config) @@ -758,7 +770,7 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) } #ifdef USE_HOME_ASSISTANT if ((19 == pindex) || (30 == pindex)) { - HAssDiscovery(1); // hass_discovery or hass_light + HAssDiscover(); // Delayed execution to provide enough resources during hass_discovery or hass_light } #endif // USE_HOME_ASSISTANT } @@ -766,13 +778,14 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) else if (1 == ptype) { // SetOption50 .. 81 if (payload <= 1) { bitWrite(Settings.flag3.data, pindex, payload); - if (60 == ptype) { // SetOption60 enable or disable traditional sleep - if (payload == 0) { // Dynamic Sleep - WiFiSetSleepMode(); // Update WiFi sleep mode accordingly - } else { // Traditional Sleep //AT - WiFiSetSleepMode(); // Update WiFi sleep mode accordingly + if (5 == pindex) { // SetOption55 + if (0 == payload) { + restart_flag = 2; // Disable mDNS needs restart } } + if (10 == pindex) { // SetOption60 enable or disable traditional sleep + WiFiSetSleepMode(); // Update WiFi sleep mode accordingly + } } } else { // SetOption32 .. 49 @@ -787,6 +800,11 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) } if ((payload >= param_low) && (payload <= param_high)) { Settings.param[pindex] = payload; + switch (pindex) { + case P_RGB_REMAP: + LightUpdateColorMapping(); + break; + } } } } @@ -850,52 +868,54 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.flag2.weight_resolution); } else if (CMND_MODULE == command_code) { - if ((payload > 0) && (payload <= MAXMODULE)) { + if ((payload >= 0) && (payload <= MAXMODULE)) { + if (0 == payload) { payload = 256; } payload--; Settings.last_module = Settings.module; Settings.module = payload; + SetModuleType(); if (Settings.last_module != payload) { - for (byte i = 0; i < MAX_GPIO_PIN; i++) { - Settings.my_gp.io[i] = 0; + for (uint8_t i = 0; i < sizeof(Settings.my_gp); i++) { + Settings.my_gp.io[i] = GPIO_NONE; } } restart_flag = 2; } - snprintf_P(stemp1, sizeof(stemp1), kModules[Settings.module].name); - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE_SVALUE, command, Settings.module +1, stemp1); + snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE_SVALUE, command, ModuleNr(), ModuleName().c_str()); } else if (CMND_MODULES == command_code) { - for (byte i = 0; i < MAXMODULE; i++) { + for (uint8_t i = 0; i <= MAXMODULE; i++) { if (!jsflg) { snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_MODULES "%d\":["), lines); } else { snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,"), mqtt_data); } - jsflg = 1; - snprintf_P(stemp1, sizeof(stemp1), kModules[i].name); - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s\"%d (%s)\""), mqtt_data, i +1, stemp1); - if ((strlen(mqtt_data) > (LOGSZ - TOPSZ)) || (i == MAXMODULE -1)) { + jsflg = true; + uint8_t j = i; + if (0 == i) { j = USER_MODULE; } else { j--; } + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s\"%d (%s)\""), mqtt_data, i, AnyModuleName(j).c_str()); + if ((strlen(mqtt_data) > (LOGSZ - TOPSZ)) || (i == MAXMODULE)) { snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s]}"), mqtt_data); MqttPublishPrefixTopic_P(RESULT_OR_STAT, type); - jsflg = 0; + jsflg = false; lines++; } } mqtt_data[0] = '\0'; } - else if ((CMND_GPIO == command_code) && (index < MAX_GPIO_PIN)) { - mytmplt cmodule; - memcpy_P(&cmodule, &kModules[Settings.module], sizeof(cmodule)); - if ((GPIO_USER == ValidGPIO(index, cmodule.gp.io[index])) && (payload >= 0) && (payload < GPIO_SENSOR_END)) { + else if ((CMND_GPIO == command_code) && (index < sizeof(Settings.my_gp))) { + myio cmodule; + ModuleGpios(&cmodule); + if (ValidGPIO(index, cmodule.io[index]) && (payload >= 0) && (payload < GPIO_SENSOR_END)) { bool present = false; - for (byte i = 0; i < sizeof(kGpioNiceList); i++) { + for (uint8_t i = 0; i < sizeof(kGpioNiceList); i++) { uint8_t midx = pgm_read_byte(kGpioNiceList + i); if (midx == payload) { present = true; } } if (present) { - for (byte i = 0; i < MAX_GPIO_PIN; i++) { - if ((GPIO_USER == ValidGPIO(i, cmodule.gp.io[i])) && (Settings.my_gp.io[i] == payload)) { - Settings.my_gp.io[i] = 0; + for (uint8_t i = 0; i < sizeof(Settings.my_gp); i++) { + if (ValidGPIO(i, cmodule.io[i]) && (Settings.my_gp.io[i] == payload)) { + Settings.my_gp.io[i] = GPIO_NONE; } } Settings.my_gp.io[index] = payload; @@ -903,10 +923,10 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) } } snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{")); - for (byte i = 0; i < MAX_GPIO_PIN; i++) { - if (GPIO_USER == ValidGPIO(i, cmodule.gp.io[i])) { + for (uint8_t i = 0; i < sizeof(Settings.my_gp); i++) { + if (ValidGPIO(i, cmodule.io[i])) { if (jsflg) snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,"), mqtt_data); - jsflg = 1; + jsflg = true; snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s\"" D_CMND_GPIO "%d\":\"%d (%s)\""), mqtt_data, i, Settings.my_gp.io[i], GetTextIndexed(stemp1, sizeof(stemp1), Settings.my_gp.io[i], kSensorNames)); } @@ -918,31 +938,69 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) } } else if (CMND_GPIOS == command_code) { - mytmplt cmodule; - memcpy_P(&cmodule, &kModules[Settings.module], sizeof(cmodule)); + myio cmodule; + ModuleGpios(&cmodule); uint8_t midx; - for (byte i = 0; i < sizeof(kGpioNiceList); i++) { + for (uint8_t i = 0; i < sizeof(kGpioNiceList); i++) { midx = pgm_read_byte(kGpioNiceList + i); - if (!GetUsedInModule(midx, cmodule.gp.io)) { - + if (!GetUsedInModule(midx, cmodule.io)) { if (!jsflg) { snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_GPIOS "%d\":["), lines); } else { snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,"), mqtt_data); } - jsflg = 1; + jsflg = true; snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s\"%d (%s)\""), mqtt_data, midx, GetTextIndexed(stemp1, sizeof(stemp1), midx, kSensorNames)); if ((strlen(mqtt_data) > (LOGSZ - TOPSZ)) || (i == sizeof(kGpioNiceList) -1)) { snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s]}"), mqtt_data); MqttPublishPrefixTopic_P(RESULT_OR_STAT, type); - jsflg = 0; + jsflg = false; lines++; } } } - mqtt_data[0] = '\0'; } + else if (CMND_TEMPLATE == command_code) { + // {"NAME":"Generic","GPIO":[17,254,29,254,7,254,254,254,138,254,139,254,254],"FLAG":1,"BASE":255} + bool error = false; + + if (!strstr(dataBuf, "{")) { // If no JSON it must be parameter + if ((payload > 0) && (payload <= MAXMODULE)) { + ModuleDefault(payload -1); // Copy template module + if (USER_MODULE == Settings.module) { restart_flag = 2; } + } + else if (0 == payload) { // Copy current template to user template + if (Settings.module != USER_MODULE) { + ModuleDefault(Settings.module); + } + } + else if (255 == payload) { // Copy current module with user configured GPIO + if (Settings.module != USER_MODULE) { + ModuleDefault(Settings.module); + } + snprintf_P(Settings.user_template.name, sizeof(Settings.user_template.name), PSTR("Merged")); + uint8_t j = 0; + for (uint8_t i = 0; i < sizeof(mycfgio); i++) { + if (6 == i) { j = 9; } + if (8 == i) { j = 12; } + if (my_module.io[j] > GPIO_NONE) { + Settings.user_template.gp.io[i] = my_module.io[j]; + } + j++; + } + } + } + else if (data_len > 9) { // Workaround exception if empty JSON like {} - Needs checks + if (JsonTemplate(dataBuf)) { // Free 336 bytes StaticJsonBuffer stack space by moving code to function + if (USER_MODULE == Settings.module) { restart_flag = 2; } + } else { + snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, D_JSON_INVALID_JSON); + error = true; + } + } + if (!error) { TemplateJson(); } + } else if ((CMND_PWM == command_code) && pwm_present && (index > 0) && (index <= MAX_PWMS)) { if ((payload >= 0) && (payload <= Settings.pwm_range) && (pin[GPIO_PWM1 + index -1] < 99)) { Settings.pwm_value[index -1] = payload; @@ -962,7 +1020,7 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) else if (CMND_PWMRANGE == command_code) { if ((1 == payload) || ((payload > 254) && (payload < 1024))) { Settings.pwm_range = (1 == payload) ? PWM_RANGE : payload; - for (byte i = 0; i < MAX_PWMS; i++) { + for (uint8_t i = 0; i < MAX_PWMS; i++) { if (Settings.pwm_value[i] > Settings.pwm_range) { Settings.pwm_value[i] = Settings.pwm_range; } @@ -1010,7 +1068,7 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.switch_debounce); } else if (CMND_BAUDRATE == command_code) { - if (payload32 > 0) { + if (payload32 > 1200) { payload32 /= 1200; // Make it a valid baudrate baudrate = (1 == payload) ? APP_BAUDRATE : payload32 * 1200; SetSerialBaudrate(baudrate); @@ -1026,7 +1084,7 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) Serial.printf("%s\n", dataBuf); // "Hello Tiger\n" } else if (2 == index || 4 == index) { - for (int i = 0; i < data_len; i++) { + for (uint16_t i = 0; i < data_len; i++) { Serial.write(dataBuf[i]); // "Hello Tiger" or "A0" } } @@ -1035,7 +1093,7 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) Serial.printf("%s", Unescape(dataBuf, &dat_len)); // "Hello\f" } else if (5 == index) { - SerialSendRaw(RemoveSpace(dataBuf)); // "AA004566" + SerialSendRaw(RemoveSpace(dataBuf)); // "AA004566" as hex values } snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, D_JSON_DONE); } @@ -1087,7 +1145,7 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) if (Settings.ntp_server[index -1][i] == ',') Settings.ntp_server[index -1][i] = '.'; } // restart_flag = 2; // Issue #3890 - ntp_force_sync = 1; + ntp_force_sync = true; } snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, command, index, Settings.ntp_server[index -1]); } @@ -1114,7 +1172,7 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, command, index, Settings.sta_ssid[index -1]); } else if ((CMND_PASSWORD == command_code) && (index > 0) && (index <= 2)) { - if ((data_len > 0) && (data_len < sizeof(Settings.sta_pwd[0]))) { + if ((data_len > 4 || SC_CLEAR == Shortcut(dataBuf) || SC_DEFAULT == Shortcut(dataBuf)) && (data_len < sizeof(Settings.sta_pwd[0]))) { strlcpy(Settings.sta_pwd[index -1], (SC_CLEAR == Shortcut(dataBuf)) ? "" : (SC_DEFAULT == Shortcut(dataBuf)) ? (1 == index) ? STA_PASS1 : STA_PASS2 : dataBuf, sizeof(Settings.sta_pwd[0])); Settings.sta_active = index -1; restart_flag = 2; @@ -1123,8 +1181,8 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_ASTERIX, command, index); } } - else if ((CMND_HOSTNAME == command_code) && !grpflg) { - if ((data_len > 0) && (data_len < sizeof(Settings.hostname))) { + else if (CMND_HOSTNAME == command_code) { + if (!grpflg && (data_len > 0) && (data_len < sizeof(Settings.hostname))) { strlcpy(Settings.hostname, (SC_DEFAULT == Shortcut(dataBuf)) ? WIFI_HOSTNAME : dataBuf, sizeof(Settings.hostname)); if (strstr(Settings.hostname,"%")) { strlcpy(Settings.hostname, WIFI_HOSTNAME, sizeof(Settings.hostname)); @@ -1162,10 +1220,77 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) else if ((CMND_SWITCHMODE == command_code) && (index > 0) && (index <= MAX_SWITCHES)) { if ((payload >= 0) && (payload < MAX_SWITCH_OPTION)) { Settings.switchmode[index -1] = payload; - GpioSwitchPinMode(index -1); } snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_NVALUE, command, index, Settings.switchmode[index-1]); } + else if (CMND_INTERLOCK == command_code) { // Interlock 0 - Off, Interlock 1 - On, Interlock 1,2 3,4 5,6,7 + uint8_t max_relays = devices_present; + if (light_type) { max_relays--; } + if (max_relays > sizeof(Settings.interlock[0]) * 8) { max_relays = sizeof(Settings.interlock[0]) * 8; } + if (max_relays > 1) { // Only interlock with more than 1 relay + if (data_len > 0) { + if (strstr(dataBuf, ",")) { // Interlock entry + for (uint8_t i = 0; i < MAX_INTERLOCKS; i++) { Settings.interlock[i] = 0; } // Reset current interlocks + char *group; + char *q; + uint8_t group_index = 0; + power_t relay_mask = 0; + for (group = strtok_r(dataBuf, " ", &q); group && group_index < MAX_INTERLOCKS; group = strtok_r(NULL, " ", &q)) { + char *str; + for (str = strtok_r(group, ",", &p); str; str = strtok_r(NULL, ",", &p)) { + int pbit = atoi(str); + if ((pbit > 0) && (pbit <= max_relays)) { // Only valid relays + pbit--; + if (!bitRead(relay_mask, pbit)) { // Only relay once + bitSet(relay_mask, pbit); + bitSet(Settings.interlock[group_index], pbit); + } + } + } + group_index++; + } + for (uint8_t i = 0; i < group_index; i++) { + uint8_t minimal_bits = 0; + for (uint8_t j = 0; j < max_relays; j++) { + if (bitRead(Settings.interlock[i], j)) { minimal_bits++; } + } + if (minimal_bits < 2) { Settings.interlock[i] = 0; } // Discard single relay as interlock + } + } else { + Settings.flag.interlock = payload &1; // Enable/disable interlock + if (Settings.flag.interlock) { + SetDevicePower(power, SRC_IGNORE); // Remove multiple relays if set + } + } + } + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_INTERLOCK "\":\"%s\",\"" D_JSON_GROUPS "\":\""), GetStateText(Settings.flag.interlock)); + uint8_t anygroup = 0; + for (uint8_t i = 0; i < MAX_INTERLOCKS; i++) { + if (Settings.interlock[i]) { + anygroup++; + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s%s"), mqtt_data, (anygroup > 1) ? " " : ""); + uint8_t anybit = 0; + power_t mask = 1; + for (uint8_t j = 0; j < max_relays; j++) { + if (Settings.interlock[i] & mask) { + anybit++; + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s%s%d"), mqtt_data, (anybit > 1) ? "," : "", j +1); + } + mask <<= 1; + } + } + } + if (!anygroup) { + for (uint8_t j = 1; j <= max_relays; j++) { + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s%s%d"), mqtt_data, (j > 1) ? "," : "", j); + } + } + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s\"}"), mqtt_data); + } else { + Settings.flag.interlock = 0; + snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, GetStateText(Settings.flag.interlock)); + } + } else if (CMND_TELEPERIOD == command_code) { if ((payload >= 0) && (payload < 3601)) { Settings.tele_period = (1 == payload) ? TELE_PERIOD : payload; @@ -1180,10 +1305,7 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) restart_flag = 211; snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command , D_JSON_RESET_AND_RESTARTING); break; - case 2: - case 3: - case 4: - case 5: + case 2 ... 6: restart_flag = 210 + payload; snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_RESET "\":\"" D_JSON_ERASE ", " D_JSON_RESET_AND_RESTARTING "\"}")); break; @@ -1207,7 +1329,7 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) } else { Settings.timezone = 99; } - ntp_force_sync = 1; + ntp_force_sync = true; } if (99 == Settings.timezone) { snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.timezone); @@ -1242,7 +1364,7 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) value = strtol(p, &p, 10); tpos++; // Next parameter } - ntp_force_sync = 1; + ntp_force_sync = true; } else { if (0 == payload) { if (0 == ts) { @@ -1251,7 +1373,7 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) SettingsResetDst(); } } - ntp_force_sync = 1; + ntp_force_sync = true; } } snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"%s\":{\"Hemisphere\":%d,\"Week\":%d,\"Month\":%d,\"Day\":%d,\"Hour\":%d,\"Offset\":%d}}"), @@ -1283,7 +1405,10 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) else if (CMND_LEDSTATE == command_code) { if ((payload >= 0) && (payload < MAX_LED_OPTION)) { Settings.ledstate = payload; - if (!Settings.ledstate) SetLedPower(0); + if (!Settings.ledstate) { + SetLedPower(0); + SetLedLink(0); + } } snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.ledstate); } @@ -1301,12 +1426,12 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) type = (char*)topicBuf; } if (mqtt_data[0] != '\0') MqttPublishPrefixTopic_P(RESULT_OR_STAT, type); - fallback_topic_flag = 0; + fallback_topic_flag = false; } /********************************************************************************************/ -boolean SendKey(byte key, byte device, byte state) +bool SendKey(uint8_t key, uint8_t device, uint8_t state) { // key 0 = button_topic // key 1 = switch_topic @@ -1319,7 +1444,7 @@ boolean SendKey(byte key, byte device, byte state) char stopic[TOPSZ]; char scommand[CMDSZ]; char key_topic[sizeof(Settings.button_topic)]; - boolean result = false; + bool result = false; char *tmp = (key) ? Settings.switch_topic : Settings.button_topic; Format(key_topic, tmp, sizeof(key_topic)); @@ -1337,10 +1462,10 @@ boolean SendKey(byte key, byte device, byte state) } #ifdef USE_DOMOTICZ if (!(DomoticzSendKey(key, device, state, strlen(mqtt_data)))) { - MqttPublishDirect(stopic, (key) ? Settings.flag.mqtt_switch_retain : Settings.flag.mqtt_button_retain); + MqttPublishDirect(stopic, ((key) ? Settings.flag.mqtt_switch_retain : Settings.flag.mqtt_button_retain) && (state != 3 || !Settings.flag3.no_hold_retain)); } #else - MqttPublishDirect(stopic, (key) ? Settings.flag.mqtt_switch_retain : Settings.flag.mqtt_button_retain); + MqttPublishDirect(stopic, ((key) ? Settings.flag.mqtt_switch_retain : Settings.flag.mqtt_button_retain) && (state != 3 || !Settings.flag3.no_hold_retain)); #endif // USE_DOMOTICZ result = !Settings.flag3.button_switch_force_local; } else { @@ -1353,7 +1478,7 @@ boolean SendKey(byte key, byte device, byte state) return result; } -void ExecuteCommandPower(byte device, byte state, int source) +void ExecuteCommandPower(uint8_t device, uint8_t state, int source) { // device = Relay number 1 and up // state 0 = Relay Off @@ -1367,7 +1492,7 @@ void ExecuteCommandPower(byte device, byte state, int source) // ShowSource(source); - if (SONOFF_IFAN02 == Settings.module) { + if (SONOFF_IFAN02 == my_module_type) { blink_mask &= 1; // No blinking on the fan relays Settings.flag.interlock = 0; // No interlock mode as it is already done by the microcontroller Settings.pulse_timer[1] = 0; // No pulsetimers on the fan relays @@ -1382,20 +1507,30 @@ void ExecuteCommandPower(byte device, byte state, int source) } if ((device < 1) || (device > devices_present)) device = 1; if (device <= MAX_PULSETIMERS) { SetPulseTimer(device -1, 0); } - power_t mask = 1 << (device -1); + power_t mask = 1 << (device -1); // Device to control if (state <= POWER_TOGGLE) { if ((blink_mask & mask)) { blink_mask &= (POWER_MASK ^ mask); // Clear device mask MqttPublishPowerBlinkState(device); } - if (Settings.flag.interlock && !interlock_mutex) { // Clear all but masked relay - interlock_mutex = 1; - for (byte i = 0; i < devices_present; i++) { - power_t imask = 1 << i; - if ((power & imask) && (mask != imask)) ExecuteCommandPower(i +1, POWER_OFF, SRC_IGNORE); + + if (Settings.flag.interlock && !interlock_mutex) { // Clear all but masked relay in interlock group + interlock_mutex = true; + for (uint8_t i = 0; i < MAX_INTERLOCKS; i++) { + if (Settings.interlock[i] & mask) { // Find interlock group + for (uint8_t j = 0; j < devices_present; j++) { + power_t imask = 1 << j; + if ((Settings.interlock[i] & imask) && (power & imask) && (mask != imask)) { + ExecuteCommandPower(j +1, POWER_OFF, SRC_IGNORE); + delay(50); // Add some delay to make sure never have more than one relay on + } + } + break; // An interlocked relay is only present in one group so quit + } } - interlock_mutex = 0; + interlock_mutex = false; } + switch (state) { case POWER_OFF: { power &= (POWER_MASK ^ mask); @@ -1434,7 +1569,7 @@ void ExecuteCommandPower(byte device, byte state, int source) return; } else if (POWER_BLINK_STOP == state) { - byte flag = (blink_mask & mask); + uint8_t flag = (blink_mask & mask); blink_mask &= (POWER_MASK ^ mask); // Clear device mask MqttPublishPowerBlinkState(device); if (flag) ExecuteCommandPower(device, (blink_powersave >> (device -1))&1, SRC_IGNORE); // Restore state @@ -1447,7 +1582,7 @@ void StopAllPowerBlink(void) { power_t mask; - for (byte i = 1; i <= devices_present; i++) { + for (uint8_t i = 1; i <= devices_present; i++) { mask = 1 << (i -1); if (blink_mask & mask) { blink_mask &= (POWER_MASK ^ mask); // Clear device mask @@ -1459,57 +1594,61 @@ void StopAllPowerBlink(void) void ExecuteCommand(char *cmnd, int source) { - char stopic[CMDSZ]; - char svalue[INPUT_BUFFER_SIZE]; char *start; char *token; +#ifdef USE_DEBUG_DRIVER ShowFreeMem(PSTR("ExecuteCommand")); +#endif ShowSource(source); token = strtok(cmnd, " "); if (token != NULL) { start = strrchr(token, '/'); // Skip possible cmnd/sonoff/ preamble - if (start) token = start +1; + if (start) { token = start +1; } } + uint16_t size = (token != NULL) ? strlen(token) : 0; + char stopic[size +2]; // / + \0 snprintf_P(stopic, sizeof(stopic), PSTR("/%s"), (token == NULL) ? "" : token); + token = strtok(NULL, ""); -// snprintf_P(svalue, sizeof(svalue), (token == NULL) ? "" : token); // Fails with command FullTopic home/%prefix%/%topic% as it processes %p of %prefix% + size = (token != NULL) ? strlen(token) : 0; + char svalue[size +1]; strlcpy(svalue, (token == NULL) ? "" : token, sizeof(svalue)); // Fixed 5.8.0b - MqttDataHandler(stopic, (byte*)svalue, strlen(svalue)); + MqttDataHandler(stopic, (uint8_t*)svalue, strlen(svalue)); } void PublishStatus(uint8_t payload) { uint8_t option = STAT; char stemp[MAX_FRIENDLYNAMES * (sizeof(Settings.friendlyname[0]) +MAX_FRIENDLYNAMES)]; - char stemp2[MAX_SWITCHES * 3]; + char stemp2[64]; // Workaround MQTT - TCP/IP stack queueing when SUB_PREFIX = PUB_PREFIX - if (!strcmp(Settings.mqtt_prefix[0],Settings.mqtt_prefix[1]) && (!payload)) option++; // TELE + if (!strcmp(Settings.mqtt_prefix[0],Settings.mqtt_prefix[1]) && (!payload)) { option++; } // TELE - if ((!Settings.flag.mqtt_enabled) && (6 == payload)) payload = 99; - if (!energy_flg && (9 == payload)) payload = 99; + if ((!Settings.flag.mqtt_enabled) && (6 == payload)) { payload = 99; } + if (!energy_flg && (9 == payload)) { payload = 99; } if ((0 == payload) || (99 == payload)) { uint8_t maxfn = (devices_present > MAX_FRIENDLYNAMES) ? MAX_FRIENDLYNAMES : (!devices_present) ? 1 : devices_present; - if (SONOFF_IFAN02 == Settings.module) { maxfn = 1; } + if (SONOFF_IFAN02 == my_module_type) { maxfn = 1; } stemp[0] = '\0'; - for (byte i = 0; i < maxfn; i++) { + for (uint8_t i = 0; i < maxfn; i++) { snprintf_P(stemp, sizeof(stemp), PSTR("%s%s\"%s\"" ), stemp, (i > 0 ? "," : ""), Settings.friendlyname[i]); } stemp2[0] = '\0'; - for (byte i = 0; i < MAX_SWITCHES; i++) { + for (uint8_t i = 0; i < MAX_SWITCHES; i++) { snprintf_P(stemp2, sizeof(stemp2), PSTR("%s%s%d" ), stemp2, (i > 0 ? "," : ""), Settings.switchmode[i]); } snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_STATUS "\":{\"" D_CMND_MODULE "\":%d,\"" D_CMND_FRIENDLYNAME "\":[%s],\"" D_CMND_TOPIC "\":\"%s\",\"" D_CMND_BUTTONTOPIC "\":\"%s\",\"" D_CMND_POWER "\":%d,\"" D_CMND_POWERONSTATE "\":%d,\"" D_CMND_LEDSTATE "\":%d,\"" D_CMND_SAVEDATA "\":%d,\"" D_JSON_SAVESTATE "\":%d,\"" D_CMND_SWITCHTOPIC "\":\"%s\",\"" D_CMND_SWITCHMODE "\":[%s],\"" D_CMND_BUTTONRETAIN "\":%d,\"" D_CMND_SWITCHRETAIN "\":%d,\"" D_CMND_SENSORRETAIN "\":%d,\"" D_CMND_POWERRETAIN "\":%d}}"), - Settings.module +1, stemp, mqtt_topic, Settings.button_topic, power, Settings.poweronstate, Settings.ledstate, Settings.save_data, Settings.flag.save_state, Settings.switch_topic, stemp2, Settings.flag.mqtt_button_retain, Settings.flag.mqtt_switch_retain, Settings.flag.mqtt_sensor_retain, Settings.flag.mqtt_power_retain); + ModuleNr(), stemp, mqtt_topic, Settings.button_topic, power, Settings.poweronstate, Settings.ledstate, Settings.save_data, Settings.flag.save_state, Settings.switch_topic, stemp2, Settings.flag.mqtt_button_retain, Settings.flag.mqtt_switch_retain, Settings.flag.mqtt_sensor_retain, Settings.flag.mqtt_power_retain); MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS)); } if ((0 == payload) || (1 == payload)) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_STATUS D_STATUS1_PARAMETER "\":{\"" D_JSON_BAUDRATE "\":%d,\"" D_CMND_GROUPTOPIC "\":\"%s\",\"" D_CMND_OTAURL "\":\"%s\",\"" D_JSON_RESTARTREASON "\":\"%s\",\"" D_JSON_UPTIME "\":\"%s\",\"" D_JSON_STARTUPUTC "\":\"%s\",\"" D_CMND_SLEEP "\":%d,\"" D_JSON_BOOTCOUNT "\":%d,\"" D_JSON_SAVECOUNT "\":%d,\"" D_JSON_SAVEADDRESS "\":\"%X\"}}"), - baudrate, Settings.mqtt_grptopic, Settings.ota_url, GetResetReason().c_str(), GetUptime().c_str(), GetDateAndTime(DT_RESTART).c_str(), Settings.sleep, Settings.bootcount, Settings.save_flag, GetSettingsAddress()); + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_STATUS D_STATUS1_PARAMETER "\":{\"" D_JSON_BAUDRATE "\":%d,\"" D_CMND_GROUPTOPIC "\":\"%s\",\"" D_CMND_OTAURL "\":\"%s\",\"" D_JSON_RESTARTREASON "\":\"%s\",\"" D_JSON_UPTIME "\":\"%s\",\"" D_JSON_STARTUPUTC "\":\"%s\",\"" D_CMND_SLEEP "\":%d,\"" D_JSON_CONFIG_HOLDER "\":%d,\"" D_JSON_BOOTCOUNT "\":%d,\"" D_JSON_SAVECOUNT "\":%d,\"" D_JSON_SAVEADDRESS "\":\"%X\"}}"), + baudrate, Settings.mqtt_grptopic, Settings.ota_url, GetResetReason().c_str(), GetUptime().c_str(), GetDateAndTime(DT_RESTART).c_str(), Settings.sleep, Settings.cfg_holder, Settings.bootcount, Settings.save_flag, GetSettingsAddress()); MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "1")); } @@ -1520,8 +1659,12 @@ void PublishStatus(uint8_t payload) } if ((0 == payload) || (3 == payload)) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_STATUS D_STATUS3_LOGGING "\":{\"" D_CMND_SERIALLOG "\":%d,\"" D_CMND_WEBLOG "\":%d,\"" D_CMND_SYSLOG "\":%d,\"" D_CMND_LOGHOST "\":\"%s\",\"" D_CMND_LOGPORT "\":%d,\"" D_CMND_SSID "\":[\"%s\",\"%s\"],\"" D_CMND_TELEPERIOD "\":%d,\"" D_CMND_SETOPTION "\":[\"%08X\",\"%08X\",\"%08X\"]}}"), - Settings.seriallog_level, Settings.weblog_level, Settings.syslog_level, Settings.syslog_host, Settings.syslog_port, Settings.sta_ssid[0], Settings.sta_ssid[1], Settings.tele_period, Settings.flag.data, Settings.flag2.data, Settings.flag3.data); + stemp2[0] = '\0'; + for (int8_t i = 0; i < PARAM8_SIZE; i++) { + snprintf_P(stemp2, sizeof(stemp2), PSTR("%s%02X"), stemp2, Settings.param[i]); + } + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_STATUS D_STATUS3_LOGGING "\":{\"" D_CMND_SERIALLOG "\":%d,\"" D_CMND_WEBLOG "\":%d,\"" D_CMND_SYSLOG "\":%d,\"" D_CMND_LOGHOST "\":\"%s\",\"" D_CMND_LOGPORT "\":%d,\"" D_CMND_SSID "\":[\"%s\",\"%s\"],\"" D_CMND_TELEPERIOD "\":%d,\"" D_JSON_RESOLUTION "\":\"%08X\",\"" D_CMND_SETOPTION "\":[\"%08X\",\"%s\",\"%08X\"]}}"), + Settings.seriallog_level, Settings.weblog_level, Settings.syslog_level, Settings.syslog_host, Settings.syslog_port, Settings.sta_ssid[0], Settings.sta_ssid[1], Settings.tele_period, Settings.flag2.data, Settings.flag.data, stemp2, Settings.flag3.data); MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "3")); } @@ -1539,8 +1682,8 @@ void PublishStatus(uint8_t payload) } if (((0 == payload) || (6 == payload)) && Settings.flag.mqtt_enabled) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_STATUS D_STATUS6_MQTT "\":{\"" D_CMND_MQTTHOST "\":\"%s\",\"" D_CMND_MQTTPORT "\":%d,\"" D_CMND_MQTTCLIENT D_JSON_MASK "\":\"%s\",\"" D_CMND_MQTTCLIENT "\":\"%s\",\"" D_CMND_MQTTUSER "\":\"%s\",\"MqttType\":%d,\"MAX_PACKET_SIZE\":%d,\"KEEPALIVE\":%d}}"), - Settings.mqtt_host, Settings.mqtt_port, Settings.mqtt_client, mqtt_client, Settings.mqtt_user, MqttLibraryType(), MQTT_MAX_PACKET_SIZE, MQTT_KEEPALIVE); + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_STATUS D_STATUS6_MQTT "\":{\"" D_CMND_MQTTHOST "\":\"%s\",\"" D_CMND_MQTTPORT "\":%d,\"" D_CMND_MQTTCLIENT D_JSON_MASK "\":\"%s\",\"" D_CMND_MQTTCLIENT "\":\"%s\",\"" D_CMND_MQTTUSER "\":\"%s\",\"MqttType\":%d,\"" D_JSON_MQTT_COUNT "\":%d,\"MAX_PACKET_SIZE\":%d,\"KEEPALIVE\":%d}}"), + Settings.mqtt_host, Settings.mqtt_port, Settings.mqtt_client, mqtt_client, Settings.mqtt_user, MqttLibraryType(), MqttConnectCount(), MQTT_MAX_PACKET_SIZE, MQTT_KEEPALIVE); MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "6")); } @@ -1592,7 +1735,7 @@ void MqttShowPWMState(void) { snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s\"" D_CMND_PWM "\":{"), mqtt_data); bool first = true; - for (byte i = 0; i < MAX_PWMS; i++) { + for (uint8_t i = 0; i < MAX_PWMS; i++) { if (pin[GPIO_PWM1 + i] < 99) { snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s%s\"" D_CMND_PWM "%d\":%d"), mqtt_data, first ? "" : ",", i+1, Settings.pwm_value[i]); first = false; @@ -1615,12 +1758,12 @@ void MqttShowState(void) snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"SleepMode\":\"%s\",\"Sleep\":%u,\"LoadAvg\":%u"), mqtt_data, GetTextIndexed(stemp1, sizeof(stemp1), Settings.flag3.sleep_normal, kSleepMode), sleep, loop_load_avg); - for (byte i = 0; i < devices_present; i++) { + for (uint8_t i = 0; i < devices_present; i++) { if (i == light_device -1) { LightState(1); } else { snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"%s\":\"%s\""), mqtt_data, GetPowerDevice(stemp1, i +1, sizeof(stemp1), Settings.flag.device_index_enable), GetStateText(bitRead(power, i))); - if (SONOFF_IFAN02 == Settings.module) { + if (SONOFF_IFAN02 == my_module_type) { snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"" D_CMND_FANSPEED "\":%d"), mqtt_data, GetFanspeed()); break; } @@ -1632,26 +1775,26 @@ void MqttShowState(void) MqttShowPWMState(); } - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"" D_JSON_WIFI "\":{\"" D_JSON_AP "\":%d,\"" D_JSON_SSID "\":\"%s\",\"" D_JSON_BSSID "\":\"%s\",\"" D_JSON_CHANNEL "\":%d,\"" D_JSON_RSSI "\":%d}}"), - mqtt_data, Settings.sta_active +1, Settings.sta_ssid[Settings.sta_active], WiFi.BSSIDstr().c_str(), WiFi.channel(), WifiGetRssiAsQuality(WiFi.RSSI())); + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"" D_JSON_WIFI "\":{\"" D_JSON_AP "\":%d,\"" D_JSON_SSID "\":\"%s\",\"" D_JSON_BSSID "\":\"%s\",\"" D_JSON_CHANNEL "\":%d,\"" D_JSON_RSSI "\":%d,\"" D_JSON_LINK_COUNT "\":%d,\"" D_JSON_DOWNTIME "\":\"%s\"}}"), + mqtt_data, Settings.sta_active +1, Settings.sta_ssid[Settings.sta_active], WiFi.BSSIDstr().c_str(), WiFi.channel(), WifiGetRssiAsQuality(WiFi.RSSI()), WifiLinkCount(), WifiDowntime().c_str()); } -boolean MqttShowSensor(void) +bool MqttShowSensor(void) { snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s{\"" D_JSON_TIME "\":\"%s\""), mqtt_data, GetDateAndTime(DT_LOCAL).c_str()); int json_data_start = strlen(mqtt_data); - for (byte i = 0; i < MAX_SWITCHES; i++) { + for (uint8_t i = 0; i < MAX_SWITCHES; i++) { #ifdef USE_TM1638 if ((pin[GPIO_SWT1 +i] < 99) || ((pin[GPIO_TM16CLK] < 99) && (pin[GPIO_TM16DIO] < 99) && (pin[GPIO_TM16STB] < 99))) { #else if (pin[GPIO_SWT1 +i] < 99) { #endif // USE_TM1638 - boolean swm = ((FOLLOW_INV == Settings.switchmode[i]) || (PUSHBUTTON_INV == Settings.switchmode[i]) || (PUSHBUTTONHOLD_INV == Settings.switchmode[i])); - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"" D_JSON_SWITCH "%d\":\"%s\""), mqtt_data, i +1, GetStateText(swm ^ lastwallswitch[i])); + bool swm = ((FOLLOW_INV == Settings.switchmode[i]) || (PUSHBUTTON_INV == Settings.switchmode[i]) || (PUSHBUTTONHOLD_INV == Settings.switchmode[i])); + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"" D_JSON_SWITCH "%d\":\"%s\""), mqtt_data, i +1, GetStateText(swm ^ SwitchLastState(i))); } } XsnsCall(FUNC_JSON_APPEND); - boolean json_data_available = (strlen(mqtt_data) - json_data_start); + bool json_data_available = (strlen(mqtt_data) - json_data_start); if (strstr_P(mqtt_data, PSTR(D_JSON_PRESSURE))) { snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"" D_JSON_PRESSURE_UNIT "\":\"%s\""), mqtt_data, PressureUnit().c_str()); } @@ -1675,11 +1818,10 @@ void PerformEverySecond(void) RtcRebootSave(); Settings.bootcount++; // Moved to here to stop flash writes during start-up - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_APPLICATION D_BOOT_COUNT " %d"), Settings.bootcount); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BOOT_COUNT " %d"), Settings.bootcount); } - if ((4 == uptime) && (SONOFF_IFAN02 == Settings.module)) { // Microcontroller needs 3 seconds before accepting commands + if ((4 == uptime) && (SONOFF_IFAN02 == my_module_type)) { // Microcontroller needs 3 seconds before accepting commands SetDevicePower(1, SRC_RETRY); // Sync with default power on state microcontroller being Light ON and Fan OFF SetDevicePower(power, SRC_RETRY); // Set required power on state } @@ -1739,241 +1881,6 @@ void PerformEverySecond(void) if ((3 == RtcTime.minute) && !latest_uptime_flag) latest_uptime_flag = true; } -/*********************************************************************************************\ - * Button handler with single press only or multi-press and hold on all buttons -\*********************************************************************************************/ - -void ButtonHandler(void) -{ - uint8_t button = NOT_PRESSED; - uint8_t button_present = 0; - uint8_t hold_time_extent = IMMINENT_RESET_FACTOR; // Extent hold time factor in case of iminnent Reset command - uint16_t loops_per_second = 1000 / Settings.button_debounce; - char scmnd[20]; - - uint8_t maxdev = (devices_present > MAX_KEYS) ? MAX_KEYS : devices_present; - for (byte button_index = 0; button_index < maxdev; button_index++) { - button = NOT_PRESSED; - button_present = 0; - - if (!button_index && ((SONOFF_DUAL == Settings.module) || (CH4 == Settings.module))) { - button_present = 1; - if (dual_button_code) { - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_APPLICATION D_BUTTON " " D_CODE " %04X"), dual_button_code); - AddLog(LOG_LEVEL_DEBUG); - button = PRESSED; - if (0xF500 == dual_button_code) { // Button hold - holdbutton[button_index] = (loops_per_second * Settings.param[P_HOLD_TIME] / 10) -1; - hold_time_extent = 1; - } - dual_button_code = 0; - } - } else { - if (pin[GPIO_KEY1 +button_index] < 99) { - if (!((uptime < 4) && (0 == pin[GPIO_KEY1 +button_index]))) { // Block GPIO0 for 4 seconds after poweron to workaround Wemos D1 RTS circuit - button_present = 1; - button = digitalRead(pin[GPIO_KEY1 +button_index]); - } - } - } - - if (button_present) { - XdrvMailbox.index = button_index; - XdrvMailbox.payload = button; - if (XdrvCall(FUNC_BUTTON_PRESSED)) { - // Serviced - } - else if (SONOFF_4CHPRO == Settings.module) { - if (holdbutton[button_index]) { holdbutton[button_index]--; } - - boolean button_pressed = false; - if ((PRESSED == button) && (NOT_PRESSED == lastbutton[button_index])) { - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_APPLICATION D_BUTTON "%d " D_LEVEL_10), button_index +1); - AddLog(LOG_LEVEL_DEBUG); - holdbutton[button_index] = loops_per_second; - button_pressed = true; - } - if ((NOT_PRESSED == button) && (PRESSED == lastbutton[button_index])) { - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_APPLICATION D_BUTTON "%d " D_LEVEL_01), button_index +1); - AddLog(LOG_LEVEL_DEBUG); - if (!holdbutton[button_index]) { button_pressed = true; } // Do not allow within 1 second - } - if (button_pressed) { - if (!SendKey(0, button_index +1, POWER_TOGGLE)) { // Execute Toggle command via MQTT if ButtonTopic is set - ExecuteCommandPower(button_index +1, POWER_TOGGLE, SRC_BUTTON); // Execute Toggle command internally - } - } - } - else { - if ((PRESSED == button) && (NOT_PRESSED == lastbutton[button_index])) { - if (Settings.flag.button_single) { // Allow only single button press for immediate action - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_APPLICATION D_BUTTON "%d " D_IMMEDIATE), button_index +1); - AddLog(LOG_LEVEL_DEBUG); - if (!SendKey(0, button_index +1, POWER_TOGGLE)) { // Execute Toggle command via MQTT if ButtonTopic is set - ExecuteCommandPower(button_index +1, POWER_TOGGLE, SRC_BUTTON); // Execute Toggle command internally - } - } else { - multipress[button_index] = (multiwindow[button_index]) ? multipress[button_index] +1 : 1; - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_APPLICATION D_BUTTON "%d " D_MULTI_PRESS " %d"), button_index +1, multipress[button_index]); - AddLog(LOG_LEVEL_DEBUG); - multiwindow[button_index] = loops_per_second / 2; // 0.5 second multi press window - } - blinks = 201; - } - - if (NOT_PRESSED == button) { - holdbutton[button_index] = 0; - } else { - holdbutton[button_index]++; - if (Settings.flag.button_single) { // Allow only single button press for immediate action - if (holdbutton[button_index] == loops_per_second * hold_time_extent * Settings.param[P_HOLD_TIME] / 10) { // Button held for factor times longer -// Settings.flag.button_single = 0; - snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_SETOPTION "13 0")); // Disable single press only - ExecuteCommand(scmnd, SRC_BUTTON); - } - } else { - if (Settings.flag.button_restrict) { // Button restriction - if (holdbutton[button_index] == loops_per_second * Settings.param[P_HOLD_TIME] / 10) { // Button hold - multipress[button_index] = 0; - SendKey(0, button_index +1, 3); // Execute Hold command via MQTT if ButtonTopic is set - } - } else { - if (holdbutton[button_index] == loops_per_second * hold_time_extent * Settings.param[P_HOLD_TIME] / 10) { // Button held for factor times longer - multipress[button_index] = 0; - snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_RESET " 1")); - ExecuteCommand(scmnd, SRC_BUTTON); - } - } - } - } - - if (!Settings.flag.button_single) { // Allow multi-press - if (multiwindow[button_index]) { - multiwindow[button_index]--; - } else { - if (!restart_flag && !holdbutton[button_index] && (multipress[button_index] > 0) && (multipress[button_index] < MAX_BUTTON_COMMANDS +3)) { - boolean single_press = false; - if (multipress[button_index] < 3) { // Single or Double press - if ((SONOFF_DUAL_R2 == Settings.module) || (SONOFF_DUAL == Settings.module) || (CH4 == Settings.module)) { - single_press = true; - } else { - single_press = (Settings.flag.button_swap +1 == multipress[button_index]); - multipress[button_index] = 1; - } - } - if (single_press && SendKey(0, button_index + multipress[button_index], POWER_TOGGLE)) { // Execute Toggle command via MQTT if ButtonTopic is set - // Success - } else { - if (multipress[button_index] < 3) { // Single or Double press - if (WifiState() > WIFI_RESTART) { // WPSconfig, Smartconfig or Wifimanager active - restart_flag = 1; - } else { - ExecuteCommandPower(button_index + multipress[button_index], POWER_TOGGLE, SRC_BUTTON); // Execute Toggle command internally - } - } else { // 3 - 7 press - if (!Settings.flag.button_restrict) { - snprintf_P(scmnd, sizeof(scmnd), kCommands[multipress[button_index] -3]); - ExecuteCommand(scmnd, SRC_BUTTON); - } - } - } - multipress[button_index] = 0; - } - } - } - } - } - lastbutton[button_index] = button; - } -} - -/*********************************************************************************************\ - * Switch handler -\*********************************************************************************************/ - -void SwitchHandler(byte mode) -{ - uint8_t button = NOT_PRESSED; - uint8_t switchflag; - uint16_t loops_per_second = 1000 / Settings.switch_debounce; - - for (byte i = 0; i < MAX_SWITCHES; i++) { - if ((pin[GPIO_SWT1 +i] < 99) || (mode)) { - - if (holdwallswitch[i]) { - holdwallswitch[i]--; - if (0 == holdwallswitch[i]) { - SendKey(1, i +1, 3); // Execute command via MQTT - } - } - - if (mode) { - button = virtualswitch[i]; - } else { - if (!((uptime < 4) && (0 == pin[GPIO_SWT1 +i]))) { // Block GPIO0 for 4 seconds after poweron to workaround Wemos D1 RTS circuit - button = digitalRead(pin[GPIO_SWT1 +i]); - } - } - - if (button != lastwallswitch[i]) { - switchflag = 3; - switch (Settings.switchmode[i]) { - case TOGGLE: - switchflag = 2; // Toggle - break; - case FOLLOW: - switchflag = button &1; // Follow wall switch state - break; - case FOLLOW_INV: - switchflag = ~button &1; // Follow inverted wall switch state - break; - case PUSHBUTTON: - if ((PRESSED == button) && (NOT_PRESSED == lastwallswitch[i])) { - switchflag = 2; // Toggle with pushbutton to Gnd - } - break; - case PUSHBUTTON_INV: - if ((NOT_PRESSED == button) && (PRESSED == lastwallswitch[i])) { - switchflag = 2; // Toggle with releasing pushbutton from Gnd - } - break; - case PUSHBUTTON_TOGGLE: - if (button != lastwallswitch[i]) { - switchflag = 2; // Toggle with any pushbutton change - } - break; - case PUSHBUTTONHOLD: - if ((PRESSED == button) && (NOT_PRESSED == lastwallswitch[i])) { - holdwallswitch[i] = loops_per_second * Settings.param[P_HOLD_TIME] / 10; - } - if ((NOT_PRESSED == button) && (PRESSED == lastwallswitch[i]) && (holdwallswitch[i])) { - holdwallswitch[i] = 0; - switchflag = 2; // Toggle with pushbutton to Gnd - } - break; - case PUSHBUTTONHOLD_INV: - if ((NOT_PRESSED == button) && (PRESSED == lastwallswitch[i])) { - holdwallswitch[i] = loops_per_second * Settings.param[P_HOLD_TIME] / 10; - } - if ((PRESSED == button) && (NOT_PRESSED == lastwallswitch[i]) && (holdwallswitch[i])) { - holdwallswitch[i] = 0; - switchflag = 2; // Toggle with pushbutton to Gnd - } - break; - } - - if (switchflag < 3) { - if (!SendKey(1, i +1, switchflag)) { // Execute command via MQTT - ExecuteCommandPower(i +1, switchflag, SRC_SWITCH); // Execute command internally (if i < devices_present) - } - } - - lastwallswitch[i] = button; - } - } - } -} - /*********************************************************************************************\ * State loops \*********************************************************************************************/ @@ -1991,7 +1898,7 @@ void Every100mSeconds(void) if (!latching_relay_pulse) SetLatchingRelay(0, 0); } - for (byte i = 0; i < MAX_PULSETIMERS; i++) { + for (uint8_t i = 0; i < MAX_PULSETIMERS; i++) { if (pulse_timer[i] != 0L) { // Timer active? if (TimeReached(pulse_timer[i])) { // Timer finished? pulse_timer[i] = 0L; // Turn off this timer @@ -2017,9 +1924,9 @@ void Every100mSeconds(void) // Backlog if (TimeReached(backlog_delay)) { if ((backlog_pointer != backlog_index) && !backlog_mutex) { - backlog_mutex = 1; + backlog_mutex = true; ExecuteCommand((char*)backlog[backlog_pointer].c_str(), SRC_BACKLOG); - backlog_mutex = 0; + backlog_mutex = false; backlog_pointer++; if (backlog_pointer >= MAX_BACKLOG) { backlog_pointer = 0; } } @@ -2050,7 +1957,7 @@ void Every250mSeconds(void) } if (blinks || restart_flag || ota_state_flag) { if (restart_flag || ota_state_flag) { // Overrule blinks and keep led lit - blinkstate = 1; // Stay lit + blinkstate = true; // Stay lit } else { blinkspeed--; if (!blinkspeed) { @@ -2060,7 +1967,8 @@ void Every250mSeconds(void) } if ((!(Settings.ledstate &0x08)) && ((Settings.ledstate &0x06) || (blinks > 200) || (blinkstate))) { // if ( (!Settings.flag.global_state && global_state.data) || ((!(Settings.ledstate &0x08)) && ((Settings.ledstate &0x06) || (blinks > 200) || (blinkstate))) ) { - SetLedPower(blinkstate); // Set led on or off +// SetLedPower(blinkstate); // Set led on or off + SetLedLink(blinkstate); // Set led on or off } if (!blinkstate) { blinks--; @@ -2068,8 +1976,8 @@ void Every250mSeconds(void) } } else if (Settings.ledstate &1) { - boolean tstate = power; - if ((SONOFF_TOUCH == Settings.module) || (SONOFF_T11 == Settings.module) || (SONOFF_T12 == Settings.module) || (SONOFF_T13 == Settings.module)) { + bool tstate = power; + if ((SONOFF_TOUCH == my_module_type) || (SONOFF_T11 == my_module_type) || (SONOFF_T12 == my_module_type) || (SONOFF_T13 == my_module_type)) { tstate = (!power) ? 1 : 0; // As requested invert signal for Touch devices to find them in the dark } SetLedPower(tstate); @@ -2104,21 +2012,20 @@ void Every250mSeconds(void) ota_retry_counter--; if (ota_retry_counter) { strlcpy(mqtt_data, GetOtaUrl(log_data, sizeof(log_data)), sizeof(mqtt_data)); -#ifndef BE_MINIMAL +#ifndef FIRMWARE_MINIMAL if (RtcSettings.ota_loader) { char *bch = strrchr(mqtt_data, '/'); // Only consider filename after last backslash prevent change of urls having "-" in it char *pch = strrchr((bch != NULL) ? bch : mqtt_data, '-'); // Change from filename-DE.bin into filename-minimal.bin char *ech = strrchr((bch != NULL) ? bch : mqtt_data, '.'); // Change from filename.bin into filename-minimal.bin - if (!pch) pch = ech; + if (!pch) { pch = ech; } if (pch) { mqtt_data[pch - mqtt_data] = '\0'; char *ech = strrchr(Settings.ota_url, '.'); // Change from filename.bin into filename-minimal.bin snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s-" D_JSON_MINIMAL "%s"), mqtt_data, ech); // Minimal filename must be filename-minimal } } -#endif // BE_MINIMAL - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_UPLOAD "%s"), mqtt_data); - AddLog(LOG_LEVEL_DEBUG); +#endif // FIRMWARE_MINIMAL + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPLOAD "%s"), mqtt_data); #if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_1) || defined(ARDUINO_ESP8266_RELEASE_2_4_2) ota_result = (HTTP_UPDATE_FAILED != ESPhttpUpdate.update(mqtt_data)); #else @@ -2127,14 +2034,13 @@ void Every250mSeconds(void) ota_result = (HTTP_UPDATE_FAILED != ESPhttpUpdate.update(OTAclient, mqtt_data)); #endif if (!ota_result) { -#ifndef BE_MINIMAL +#ifndef FIRMWARE_MINIMAL int ota_error = ESPhttpUpdate.getLastError(); -// snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_UPLOAD "Ota error %d"), ota_error); -// AddLog(LOG_LEVEL_DEBUG); +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPLOAD "Ota error %d"), ota_error); if ((HTTP_UE_TOO_LESS_SPACE == ota_error) || (HTTP_UE_BIN_FOR_WRONG_FLASH == ota_error)) { RtcSettings.ota_loader = 1; // Try minimal image next } -#endif // BE_MINIMAL +#endif // FIRMWARE_MINIMAL ota_state_flag = 2; // Upgrade failed - retry } } @@ -2159,7 +2065,7 @@ void Every250mSeconds(void) if (save_data_counter <= 0) { if (Settings.flag.save_state) { power_t mask = POWER_MASK; - for (byte i = 0; i < MAX_PULSETIMERS; i++) { + for (uint8_t i = 0; i < MAX_PULSETIMERS; i++) { if ((Settings.pulse_timer[i] > 0) && (Settings.pulse_timer[i] < 30)) { // 3 seconds mask &= ~(1 << i); } @@ -2175,14 +2081,33 @@ void Every250mSeconds(void) } } if (restart_flag && (backlog_pointer == backlog_index)) { - if ((214 == restart_flag) || (215 == restart_flag)) { + if ((214 == restart_flag) || (215 == restart_flag) || (216 == restart_flag)) { char storage[sizeof(Settings.sta_ssid) + sizeof(Settings.sta_pwd)]; + char storage_mqtt_host[sizeof(Settings.mqtt_host)]; + uint16_t storage_mqtt_port; + char storage_mqtt_user[sizeof(Settings.mqtt_user)]; + char storage_mqtt_pwd[sizeof(Settings.mqtt_pwd)]; + char storage_mqtt_topic[sizeof(Settings.mqtt_topic)]; memcpy(storage, Settings.sta_ssid, sizeof(storage)); // Backup current SSIDs and Passwords - if (215 == restart_flag) { + if (216 == restart_flag) { + memcpy(storage_mqtt_host, Settings.mqtt_host, sizeof(Settings.mqtt_host)); + storage_mqtt_port = Settings.mqtt_port; + memcpy(storage_mqtt_user, Settings.mqtt_user, sizeof(Settings.mqtt_user)); + memcpy(storage_mqtt_pwd, Settings.mqtt_pwd, sizeof(Settings.mqtt_pwd)); + memcpy(storage_mqtt_topic, Settings.mqtt_topic, sizeof(Settings.mqtt_topic)); + } + if ((215 == restart_flag) || (216 == restart_flag)) { SettingsErase(0); // Erase all flash from program end to end of physical flash } SettingsDefault(); memcpy(Settings.sta_ssid, storage, sizeof(storage)); // Restore current SSIDs and Passwords + if (216 == restart_flag) { // Restore the mqtt host, port, username and password + memcpy(Settings.mqtt_host, storage_mqtt_host, sizeof(Settings.mqtt_host)); + Settings.mqtt_port = storage_mqtt_port; + memcpy(Settings.mqtt_user, storage_mqtt_user, sizeof(Settings.mqtt_user)); + memcpy(Settings.mqtt_pwd, storage_mqtt_pwd, sizeof(Settings.mqtt_pwd)); + memcpy(Settings.mqtt_topic, storage_mqtt_topic, sizeof(Settings.mqtt_topic)); + } restart_flag = 2; } else if (213 == restart_flag) { @@ -2230,20 +2155,19 @@ void ArduinoOTAInit(void) { ArduinoOTA.setPort(8266); ArduinoOTA.setHostname(my_hostname); - if (Settings.web_password[0] !=0) ArduinoOTA.setPassword(Settings.web_password); + if (Settings.web_password[0] !=0) { ArduinoOTA.setPassword(Settings.web_password); } ArduinoOTA.onStart([]() { SettingsSave(1); // Free flash for OTA update #ifdef USE_WEBSERVER - if (Settings.webserver) StopWebserver(); + if (Settings.webserver) { StopWebserver(); } #endif // USE_WEBSERVER #ifdef USE_ARILUX_RF AriluxRfDisable(); // Prevent restart exception on Arilux Interrupt routine #endif // USE_ARILUX_RF - if (Settings.flag.mqtt_enabled) MqttDisconnect(); - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_UPLOAD "Arduino OTA " D_UPLOAD_STARTED)); - AddLog(LOG_LEVEL_INFO); + if (Settings.flag.mqtt_enabled) { MqttDisconnect(); } + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_UPLOAD "Arduino OTA " D_UPLOAD_STARTED)); arduino_ota_triggered = true; arduino_ota_progress_dot_count = 0; delay(100); // Allow time for message xfer @@ -2254,7 +2178,7 @@ void ArduinoOTAInit(void) if ((LOG_LEVEL_DEBUG <= seriallog_level)) { arduino_ota_progress_dot_count++; Serial.printf("."); - if (!(arduino_ota_progress_dot_count % 80)) Serial.println(); + if (!(arduino_ota_progress_dot_count % 80)) { Serial.println(); } } }); @@ -2266,7 +2190,7 @@ void ArduinoOTAInit(void) */ char error_str[100]; - if ((LOG_LEVEL_DEBUG <= seriallog_level) && arduino_ota_progress_dot_count) Serial.println(); + if ((LOG_LEVEL_DEBUG <= seriallog_level) && arduino_ota_progress_dot_count) { Serial.println(); } switch (error) { case OTA_BEGIN_ERROR: strncpy_P(error_str, PSTR(D_UPLOAD_ERR_2), sizeof(error_str)); break; case OTA_RECEIVE_ERROR: strncpy_P(error_str, PSTR(D_UPLOAD_ERR_5), sizeof(error_str)); break; @@ -2274,22 +2198,19 @@ void ArduinoOTAInit(void) default: snprintf_P(error_str, sizeof(error_str), PSTR(D_UPLOAD_ERROR_CODE " %d"), error); } - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_UPLOAD "Arduino OTA %s. " D_RESTARTING), error_str); - AddLog(LOG_LEVEL_INFO); + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_UPLOAD "Arduino OTA %s. " D_RESTARTING), error_str); EspRestart(); }); ArduinoOTA.onEnd([]() { - if ((LOG_LEVEL_DEBUG <= seriallog_level)) Serial.println(); - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_UPLOAD "Arduino OTA " D_SUCCESSFUL ". " D_RESTARTING)); - AddLog(LOG_LEVEL_INFO); + if ((LOG_LEVEL_DEBUG <= seriallog_level)) { Serial.println(); } + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_UPLOAD "Arduino OTA " D_SUCCESSFUL ". " D_RESTARTING)); EspRestart(); }); ArduinoOTA.begin(); - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_UPLOAD "Arduino OTA " D_ENABLED " " D_PORT " 8266")); - AddLog(LOG_LEVEL_INFO); + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_UPLOAD "Arduino OTA " D_ENABLED " " D_PORT " 8266")); } #endif // USE_ARDUINO_OTA @@ -2298,29 +2219,15 @@ void ArduinoOTAInit(void) void SerialInput(void) { while (Serial.available()) { - yield(); +// yield(); + delay(0); serial_in_byte = Serial.read(); /*-------------------------------------------------------------------------------------------*\ * Sonoff dual and ch4 19200 baud serial interface \*-------------------------------------------------------------------------------------------*/ - if ((SONOFF_DUAL == Settings.module) || (CH4 == Settings.module)) { - if (dual_hex_code) { - dual_hex_code--; - if (dual_hex_code) { - dual_button_code = (dual_button_code << 8) | serial_in_byte; - serial_in_byte = 0; - } else { - if (serial_in_byte != 0xA1) { - dual_button_code = 0; // 0xA1 - End of Sonoff dual button code - } - } - } - if (0xA0 == serial_in_byte) { // 0xA0 - Start of Sonoff dual button code - serial_in_byte = 0; - dual_button_code = 0; - dual_hex_code = 3; - } + if ((SONOFF_DUAL == my_module_type) || (CH4 == my_module_type)) { + serial_in_byte = ButtonSerial(serial_in_byte); } /*-------------------------------------------------------------------------------------------*/ @@ -2333,27 +2240,29 @@ void SerialInput(void) /*-------------------------------------------------------------------------------------------*/ - if (serial_in_byte > 127 && !Settings.flag.mqtt_serial_raw) { // binary data... + if (serial_in_byte > 127 && !Settings.flag.mqtt_serial_raw) { // Discard binary data above 127 if no raw reception allowed serial_in_byte_counter = 0; Serial.flush(); return; } - if (!Settings.flag.mqtt_serial) { - if (isprint(serial_in_byte)) { - if (serial_in_byte_counter < INPUT_BUFFER_SIZE -1) { // add char to string if it still fits + if (!Settings.flag.mqtt_serial) { // SerialSend active + if (isprint(serial_in_byte)) { // Any char between 32 and 127 + if (serial_in_byte_counter < INPUT_BUFFER_SIZE -1) { // Add char to string if it still fits serial_in_buffer[serial_in_byte_counter++] = serial_in_byte; } else { serial_in_byte_counter = 0; } } } else { - if (serial_in_byte || Settings.flag.mqtt_serial_raw) { - if ((serial_in_byte_counter < INPUT_BUFFER_SIZE -1) && - ((serial_in_byte != Settings.serial_delimiter) || Settings.flag.mqtt_serial_raw)) { // add char to string if it still fits + if (serial_in_byte || Settings.flag.mqtt_serial_raw) { // Any char between 1 and 127 or any char (0 - 255) + if ((serial_in_byte_counter < INPUT_BUFFER_SIZE -1) && // Add char to string if it still fits and ... + ((isprint(serial_in_byte) && (128 == Settings.serial_delimiter)) || // Any char between 32 and 127 + (serial_in_byte != Settings.serial_delimiter) || // Any char between 1 and 127 and not being delimiter + Settings.flag.mqtt_serial_raw)) { // Any char between 0 and 255 serial_in_buffer[serial_in_byte_counter++] = serial_in_byte; serial_polling_window = millis(); } else { - serial_polling_window = 0; + serial_polling_window = 0; // Reception done - send mqtt break; } } @@ -2362,9 +2271,9 @@ void SerialInput(void) /*-------------------------------------------------------------------------------------------*\ * Sonoff SC 19200 baud serial interface \*-------------------------------------------------------------------------------------------*/ - if (SONOFF_SC == Settings.module) { - if (serial_in_byte == '\x1B') { // Sonoff SC status from ATMEGA328P - serial_in_buffer[serial_in_byte_counter] = 0; // serial data completed + if (SONOFF_SC == my_module_type) { + if (serial_in_byte == '\x1B') { // Sonoff SC status from ATMEGA328P + serial_in_buffer[serial_in_byte_counter] = 0; // Serial data completed SonoffScSerialInput(serial_in_buffer); serial_in_byte_counter = 0; Serial.flush(); @@ -2375,10 +2284,9 @@ void SerialInput(void) /*-------------------------------------------------------------------------------------------*/ else if (!Settings.flag.mqtt_serial && (serial_in_byte == '\n')) { - serial_in_buffer[serial_in_byte_counter] = 0; // serial data completed - seriallog_level = (Settings.seriallog_level < LOG_LEVEL_INFO) ? (byte)LOG_LEVEL_INFO : Settings.seriallog_level; - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_COMMAND "%s"), serial_in_buffer); - AddLog(LOG_LEVEL_INFO); + serial_in_buffer[serial_in_byte_counter] = 0; // Serial data completed + seriallog_level = (Settings.seriallog_level < LOG_LEVEL_INFO) ? (uint8_t)LOG_LEVEL_INFO : Settings.seriallog_level; + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_COMMAND "%s"), serial_in_buffer); ExecuteCommand(serial_in_buffer, SRC_SERIAL); serial_in_byte_counter = 0; serial_polling_window = 0; @@ -2388,7 +2296,7 @@ void SerialInput(void) } if (Settings.flag.mqtt_serial && serial_in_byte_counter && (millis() > (serial_polling_window + SERIAL_POLLING))) { - serial_in_buffer[serial_in_byte_counter] = 0; // serial data completed + serial_in_buffer[serial_in_byte_counter] = 0; // Serial data completed if (!Settings.flag.mqtt_serial_raw) { snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_SERIALRECEIVED "\":\"%s\"}"), serial_in_buffer); } else { @@ -2403,69 +2311,70 @@ void SerialInput(void) serial_in_byte_counter = 0; } } -/********************************************************************************************/ -void GpioSwitchPinMode(uint8_t index) -{ - if (pin[GPIO_SWT1 +index] < 99) { - pinMode(pin[GPIO_SWT1 +index], (16 == pin[GPIO_SWT1 +index]) ? INPUT_PULLDOWN_16 : bitRead(switch_no_pullup, index) ? INPUT : INPUT_PULLUP); -/* - // Re-enable pull-up on Shelly2 as of 20181110 (#4255) - uint8_t no_pullup = bitRead(switch_no_pullup, index); // 0 = INPUT_PULLUP, 1 = INPUT - if (no_pullup) { - if (SHELLY2 == Settings.module) { - // Switchmodes : TOGGLE, FOLLOW, FOLLOW_INV, PUSHBUTTON, PUSHBUTTON_INV, PUSHBUTTONHOLD, PUSHBUTTONHOLD_INV, PUSHBUTTON_TOGGLE, MAX_SWITCH_OPTION - no_pullup = (Settings.switchmode[index] < PUSHBUTTON); // INPUT on TOGGLE, FOLLOW and FOLLOW_INV. INPUT_PULLUP on anything else - } - } - pinMode(pin[GPIO_SWT1 +index], (16 == pin[GPIO_SWT1 +index]) ? INPUT_PULLDOWN_16 : (no_pullup) ? INPUT : INPUT_PULLUP); -*/ - } -} +/********************************************************************************************/ void GpioInit(void) { uint8_t mpin; - uint8_t key_no_pullup = 0; - mytmplt def_module; - if (Settings.module >= MAXMODULE) { + if ((Settings.module >= MAXMODULE) && (Settings.module < USER_MODULE)) { Settings.module = MODULE; Settings.last_module = MODULE; } + SetModuleType(); + if (Settings.module != Settings.last_module) { baudrate = APP_BAUDRATE; } - memcpy_P(&def_module, &kModules[Settings.module], sizeof(def_module)); - strlcpy(my_module.name, def_module.name, sizeof(my_module.name)); - for (byte i = 0; i < MAX_GPIO_PIN; i++) { - if (Settings.my_gp.io[i] > GPIO_NONE) { - my_module.gp.io[i] = Settings.my_gp.io[i]; - } - if ((def_module.gp.io[i] > GPIO_NONE) && (def_module.gp.io[i] < GPIO_USER)) { - my_module.gp.io[i] = def_module.gp.io[i]; + for (uint8_t i = 0; i < sizeof(Settings.user_template.gp); i++) { + if ((Settings.user_template.gp.io[i] >= GPIO_SENSOR_END) && (Settings.user_template.gp.io[i] < GPIO_USER)) { + Settings.user_template.gp.io[i] = GPIO_USER; // Fix not supported sensor ids in template } } - for (byte i = 0; i < GPIO_MAX; i++) { + myio def_gp; + ModuleGpios(&def_gp); + for (uint8_t i = 0; i < sizeof(Settings.my_gp); i++) { + if ((Settings.my_gp.io[i] >= GPIO_SENSOR_END) && (Settings.my_gp.io[i] < GPIO_USER)) { + Settings.my_gp.io[i] = GPIO_NONE; // Fix not supported sensor ids in module + } + else if (Settings.my_gp.io[i] > GPIO_NONE) { + my_module.io[i] = Settings.my_gp.io[i]; + } + if ((def_gp.io[i] > GPIO_NONE) && (def_gp.io[i] < GPIO_USER)) { + my_module.io[i] = def_gp.io[i]; + } + } + my_module_flag = ModuleFlag(); + + for (uint16_t i = 0; i < GPIO_MAX; i++) { pin[i] = 99; } - for (byte i = 0; i < MAX_GPIO_PIN; i++) { - mpin = ValidGPIO(i, my_module.gp.io[i]); + for (uint8_t i = 0; i < sizeof(my_module.io); i++) { + mpin = ValidPin(i, my_module.io[i]); -// snprintf_P(log_data, sizeof(log_data), PSTR("DBG: gpio pin %d, mpin %d"), i, mpin); -// AddLog(LOG_LEVEL_DEBUG); +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DBG: gpio pin %d, mpin %d"), i, mpin); if (mpin) { if ((mpin >= GPIO_SWT1_NP) && (mpin < (GPIO_SWT1_NP + MAX_SWITCHES))) { - bitSet(switch_no_pullup, mpin - GPIO_SWT1_NP); + SwitchPullupFlag(mpin - GPIO_SWT1_NP); mpin -= (GPIO_SWT1_NP - GPIO_SWT1); } else if ((mpin >= GPIO_KEY1_NP) && (mpin < (GPIO_KEY1_NP + MAX_KEYS))) { - bitSet(key_no_pullup, mpin - GPIO_KEY1_NP); + ButtonPullupFlag(mpin - GPIO_KEY1_NP); // 0 .. 3 mpin -= (GPIO_KEY1_NP - GPIO_KEY1); } + else if ((mpin >= GPIO_KEY1_INV) && (mpin < (GPIO_KEY1_INV + MAX_KEYS))) { + ButtonInvertFlag(mpin - GPIO_KEY1_INV); // 0 .. 3 + mpin -= (GPIO_KEY1_INV - GPIO_KEY1); + } + else if ((mpin >= GPIO_KEY1_INV_NP) && (mpin < (GPIO_KEY1_INV_NP + MAX_KEYS))) { + ButtonPullupFlag(mpin - GPIO_KEY1_INV_NP); // 0 .. 3 + ButtonInvertFlag(mpin - GPIO_KEY1_INV_NP); // 0 .. 3 + mpin -= (GPIO_KEY1_INV_NP - GPIO_KEY1); + } else if ((mpin >= GPIO_REL1_INV) && (mpin < (GPIO_REL1_INV + MAX_RELAYS))) { bitSet(rel_inverted, mpin - GPIO_REL1_INV); mpin -= (GPIO_REL1_INV - GPIO_REL1); @@ -2485,7 +2394,7 @@ void GpioInit(void) #ifdef USE_DHT else if ((mpin >= GPIO_DHT11) && (mpin <= GPIO_SI7021)) { if (DhtSetup(i, mpin)) { - dht_flg = 1; + dht_flg = true; mpin = GPIO_DHT11; } else { mpin = 0; @@ -2496,7 +2405,7 @@ void GpioInit(void) if (mpin) pin[mpin] = i; } - if ((2 == pin[GPIO_TXD]) || (H801 == Settings.module)) { Serial.set_tx(2); } + if ((2 == pin[GPIO_TXD]) || (H801 == my_module_type)) { Serial.set_tx(2); } analogWriteRange(Settings.pwm_range); // Default is 1023 (Arduino.h) analogWriteFreq(Settings.pwm_frequency); // Default is 1000 (core_esp8266_wiring_pwm.c) @@ -2504,14 +2413,14 @@ void GpioInit(void) #ifdef USE_SPI spi_flg = ((((pin[GPIO_SPI_CS] < 99) && (pin[GPIO_SPI_CS] > 14)) || (pin[GPIO_SPI_CS] < 12)) || (((pin[GPIO_SPI_DC] < 99) && (pin[GPIO_SPI_DC] > 14)) || (pin[GPIO_SPI_DC] < 12))); if (spi_flg) { - for (byte i = 0; i < GPIO_MAX; i++) { + for (uint16_t i = 0; i < GPIO_MAX; i++) { if ((pin[i] >= 12) && (pin[i] <=14)) pin[i] = 99; } - my_module.gp.io[12] = GPIO_SPI_MISO; + my_module.io[12] = GPIO_SPI_MISO; pin[GPIO_SPI_MISO] = 12; - my_module.gp.io[13] = GPIO_SPI_MOSI; + my_module.io[13] = GPIO_SPI_MOSI; pin[GPIO_SPI_MOSI] = 13; - my_module.gp.io[14] = GPIO_SPI_CLK; + my_module.io[14] = GPIO_SPI_CLK; pin[GPIO_SPI_CLK] = 14; } soft_spi_flg = ((pin[GPIO_SSPI_CS] < 99) && (pin[GPIO_SSPI_SCLK] < 99) && ((pin[GPIO_SSPI_MOSI] < 99) || (pin[GPIO_SSPI_MOSI] < 99))); @@ -2519,19 +2428,19 @@ void GpioInit(void) #ifdef USE_I2C i2c_flg = ((pin[GPIO_I2C_SCL] < 99) && (pin[GPIO_I2C_SDA] < 99)); - if (i2c_flg) Wire.begin(pin[GPIO_I2C_SDA], pin[GPIO_I2C_SCL]); + if (i2c_flg) { Wire.begin(pin[GPIO_I2C_SDA], pin[GPIO_I2C_SCL]); } #endif // USE_I2C devices_present = 1; light_type = LT_BASIC; // Use basic PWM control if SetOption15 = 0 if (Settings.flag.pwm_control) { - for (byte i = 0; i < MAX_PWMS; i++) { - if (pin[GPIO_PWM1 +i] < 99) light_type++; // Use Dimmer/Color control for all PWM as SetOption15 = 1 + for (uint8_t i = 0; i < MAX_PWMS; i++) { + if (pin[GPIO_PWM1 +i] < 99) { light_type++; } // Use Dimmer/Color control for all PWM as SetOption15 = 1 } } - if (SONOFF_BRIDGE == Settings.module) { + if (SONOFF_BRIDGE == my_module_type) { Settings.flag.mqtt_serial = 0; baudrate = 19200; } @@ -2539,40 +2448,43 @@ void GpioInit(void) if (XdrvCall(FUNC_MODULE_INIT)) { // Serviced } - else if (SONOFF_DUAL == Settings.module) { + else if (YTF_IR_BRIDGE == my_module_type) { + ClaimSerial(); // Stop serial loopback mode + } + else if (SONOFF_DUAL == my_module_type) { Settings.flag.mqtt_serial = 0; devices_present = 2; baudrate = 19200; } - else if (CH4 == Settings.module) { + else if (CH4 == my_module_type) { Settings.flag.mqtt_serial = 0; devices_present = 4; baudrate = 19200; } - else if (SONOFF_SC == Settings.module) { + else if (SONOFF_SC == my_module_type) { Settings.flag.mqtt_serial = 0; devices_present = 0; baudrate = 19200; } - else if (SONOFF_BN == Settings.module) { // PWM Single color led (White) + else if (SONOFF_BN == my_module_type) { // PWM Single color led (White) light_type = LT_PWM1; } - else if (SONOFF_LED == Settings.module) { // PWM Dual color led (White warm and cold) + else if (SONOFF_LED == my_module_type) { // PWM Dual color led (White warm and cold) light_type = LT_PWM2; } - else if (AILIGHT == Settings.module) { // RGBW led + else if (AILIGHT == my_module_type) { // RGBW led light_type = LT_RGBW; } - else if (SONOFF_B1 == Settings.module) { // RGBWC led + else if (SONOFF_B1 == my_module_type) { // RGBWC led light_type = LT_RGBWC; } else { - if (!light_type) devices_present = 0; - for (byte i = 0; i < MAX_RELAYS; i++) { + if (!light_type) { devices_present = 0; } + for (uint8_t i = 0; i < MAX_RELAYS; i++) { if (pin[GPIO_REL1 +i] < 99) { pinMode(pin[GPIO_REL1 +i], OUTPUT); devices_present++; - if (EXS_RELAY == Settings.module) { + if (EXS_RELAY == my_module_type) { digitalWrite(pin[GPIO_REL1 +i], bitRead(rel_inverted, i) ? 1 : 0); if (i &1) { devices_present--; } } @@ -2580,25 +2492,16 @@ void GpioInit(void) } } - for (byte i = 0; i < MAX_KEYS; i++) { - if (pin[GPIO_KEY1 +i] < 99) { - pinMode(pin[GPIO_KEY1 +i], (16 == pin[GPIO_KEY1 +i]) ? INPUT_PULLDOWN_16 : bitRead(key_no_pullup, i) ? INPUT : INPUT_PULLUP); - } - } - for (byte i = 0; i < MAX_LEDS; i++) { + for (uint8_t i = 0; i < MAX_LEDS; i++) { if (pin[GPIO_LED1 +i] < 99) { pinMode(pin[GPIO_LED1 +i], OUTPUT); digitalWrite(pin[GPIO_LED1 +i], bitRead(led_inverted, i)); } } - for (byte i = 0; i < MAX_SWITCHES; i++) { - lastwallswitch[i] = 1; // Init global to virtual switch state; - if (pin[GPIO_SWT1 +i] < 99) { - GpioSwitchPinMode(i); - lastwallswitch[i] = digitalRead(pin[GPIO_SWT1 +i]); // Set global now so doesn't change the saved power state on first switch check - } - virtualswitch[i] = lastwallswitch[i]; - } + + ButtonInit(); + SwitchInit(); + RotaryInit(); #ifdef USE_WS2812 if (!light_type && (pin[GPIO_WS2812] < 99)) { // RGB led @@ -2606,8 +2509,14 @@ void GpioInit(void) light_type = LT_WS2812; } #endif // USE_WS2812 +#ifdef USE_SM16716 + if (SM16716_ModuleSelected()) { + light_type += 3; + light_type |= LT_SM16716; + } +#endif // ifdef USE_SM16716 if (!light_type) { - for (byte i = 0; i < MAX_PWMS; i++) { // Basic PWM control only + for (uint8_t i = 0; i < MAX_PWMS; i++) { // Basic PWM control only if (pin[GPIO_PWM1 +i] < 99) { pwm_present = true; pinMode(pin[GPIO_PWM1 +i], OUTPUT); @@ -2617,6 +2526,7 @@ void GpioInit(void) } SetLedPower(Settings.ledstate &8); + SetLedLink(Settings.ledstate &8); XdrvCall(FUNC_PRE_INIT); } @@ -2627,8 +2537,6 @@ extern struct rst_info resetInfo; void setup(void) { - byte idx; - RtcRebootLoad(); if (!RtcRebootValid()) { RtcReboot.fast_reboot_count = 0; } RtcReboot.fast_reboot_count++; @@ -2658,7 +2566,7 @@ void setup(void) } baudrate = Settings.baudrate * 1200; - mdns_delayed_start = Settings.param[P_MDNS_DELAYED_START]; +// mdns_delayed_start = Settings.param[P_MDNS_DELAYED_START]; seriallog_level = Settings.seriallog_level; seriallog_timer = SERIALLOG_TIMER; syslog_level = Settings.syslog_level; @@ -2669,28 +2577,31 @@ void setup(void) Settings.flag2.emulation = 0; #endif // USE_EMULATION - // Disable functionality as possible cause of fast restart within BOOT_LOOP_TIME seconds (Exception, WDT or restarts) - if (RtcReboot.fast_reboot_count > 1) { // Restart twice - Settings.flag3.user_esp8285_enable = 0; // Disable ESP8285 Generic GPIOs interfering with flash SPI - if (RtcReboot.fast_reboot_count > 2) { // Restart 3 times - for (byte i = 0; i < MAX_RULE_SETS; i++) { - if (bitRead(Settings.rule_stop, i)) { - bitWrite(Settings.rule_enabled, i, 0); // Disable rules causing boot loop + if (Settings.param[P_BOOT_LOOP_OFFSET]) { + // Disable functionality as possible cause of fast restart within BOOT_LOOP_TIME seconds (Exception, WDT or restarts) + if (RtcReboot.fast_reboot_count > Settings.param[P_BOOT_LOOP_OFFSET]) { // Restart twice + Settings.flag3.user_esp8285_enable = 0; // Disable ESP8285 Generic GPIOs interfering with flash SPI + if (RtcReboot.fast_reboot_count > Settings.param[P_BOOT_LOOP_OFFSET] +1) { // Restart 3 times + for (uint8_t i = 0; i < MAX_RULE_SETS; i++) { + if (bitRead(Settings.rule_stop, i)) { + bitWrite(Settings.rule_enabled, i, 0); // Disable rules causing boot loop + } } } - } - if (RtcReboot.fast_reboot_count > 3) { // Restarted 4 times - Settings.rule_enabled = 0; // Disable all rules - } - if (RtcReboot.fast_reboot_count > 4) { // Restarted 5 times - Settings.module = SONOFF_BASIC; // Reset module to Sonoff Basic -// Settings.last_module = SONOFF_BASIC; - for (byte i = 0; i < MAX_GPIO_PIN; i++) { - Settings.my_gp.io[i] = GPIO_NONE; // Reset user defined GPIO disabling sensors + if (RtcReboot.fast_reboot_count > Settings.param[P_BOOT_LOOP_OFFSET] +2) { // Restarted 4 times + Settings.rule_enabled = 0; // Disable all rules } + if (RtcReboot.fast_reboot_count > Settings.param[P_BOOT_LOOP_OFFSET] +3) { // Restarted 5 times + for (uint8_t i = 0; i < sizeof(Settings.my_gp); i++) { + Settings.my_gp.io[i] = GPIO_NONE; // Reset user defined GPIO disabling sensors + } + } + if (RtcReboot.fast_reboot_count > Settings.param[P_BOOT_LOOP_OFFSET] +4) { // Restarted 6 times + Settings.module = SONOFF_BASIC; // Reset module to Sonoff Basic + // Settings.last_module = SONOFF_BASIC; + } + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_LOG_SOME_SETTINGS_RESET " (%d)"), RtcReboot.fast_reboot_count); } - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_APPLICATION D_LOG_SOME_SETTINGS_RESET " (%d)"), RtcReboot.fast_reboot_count); - AddLog(LOG_LEVEL_DEBUG); } Format(mqtt_client, Settings.mqtt_client, sizeof(mqtt_client)); @@ -2708,7 +2619,7 @@ void setup(void) WifiConnect(); - if (MOTOR == Settings.module) Settings.poweronstate = POWER_ALL_ON; // Needs always on else in limbo! + if (MOTOR == my_module_type) { Settings.poweronstate = POWER_ALL_ON; } // Needs always on else in limbo! if (POWER_ALL_ALWAYS_ON == Settings.poweronstate) { SetDevicePower(1, SRC_RESTART); } else { @@ -2745,7 +2656,7 @@ void setup(void) } // Issue #526 and #909 - for (byte i = 0; i < devices_present; i++) { + for (uint8_t i = 0; i < devices_present; i++) { if ((i < MAX_RELAYS) && (pin[GPIO_REL1 +i] < 99)) { bitWrite(power, i, digitalRead(pin[GPIO_REL1 +i]) ^ bitRead(rel_inverted, i)); } @@ -2755,14 +2666,10 @@ void setup(void) } blink_powersave = power; - char stopic[TOPSZ]; - snprintf_P(log_data, sizeof(log_data), PSTR(D_PROJECT " %s %s " D_VERSION " %s%s-" ARDUINO_ESP8266_RELEASE), - PROJECT, Settings.friendlyname[0], my_version, my_image); - AddLog(LOG_LEVEL_INFO); -#ifdef BE_MINIMAL - snprintf_P(log_data, sizeof(log_data), PSTR(D_WARNING_MINIMAL_VERSION)); - AddLog(LOG_LEVEL_INFO); -#endif // BE_MINIMAL + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_PROJECT " %s %s " D_VERSION " %s%s-" ARDUINO_ESP8266_RELEASE), PROJECT, Settings.friendlyname[0], my_version, my_image); +#ifdef FIRMWARE_MINIMAL + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_WARNING_MINIMAL_VERSION)); +#endif // FIRMWARE_MINIMAL RtcInit(); @@ -2785,14 +2692,10 @@ void loop(void) OsWatchLoop(); - if (TimeReached(button_debounce)) { - SetNextTimeInterval(button_debounce, Settings.button_debounce); - ButtonHandler(); - } - if (TimeReached(switch_debounce)) { - SetNextTimeInterval(switch_debounce, Settings.switch_debounce); - SwitchHandler(0); - } + ButtonLoop(); + SwitchLoop(); + RotaryLoop(); + if (TimeReached(state_50msecond)) { SetNextTimeInterval(state_50msecond, 50); XdrvCall(FUNC_EVERY_50_MSECOND); @@ -2811,7 +2714,7 @@ void loop(void) XsnsCall(FUNC_EVERY_250_MSECOND); } - if (!serial_local) SerialInput(); + if (!serial_local) { SerialInput(); } #ifdef USE_ARDUINO_OTA MDNS.update(); diff --git a/sonoff/sonoff_letsencrypt.h b/sonoff/sonoff_letsencrypt.h index fa00c9f29..24735b2bd 100644 --- a/sonoff/sonoff_letsencrypt.h +++ b/sonoff/sonoff_letsencrypt.h @@ -1,7 +1,7 @@ /* sonoff_letsencrypt.h - TLS Lets Encrypt certificate for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends + Copyright (C) 2019 Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/sonoff/sonoff_post.h b/sonoff/sonoff_post.h index 12a68fe47..fe5c5c08d 100755 --- a/sonoff/sonoff_post.h +++ b/sonoff/sonoff_post.h @@ -1,7 +1,7 @@ /* sonoff_post.h - Post header file for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends + Copyright (C) 2019 Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -55,7 +55,7 @@ void KNX_CB_Action(message_t const &msg, void *arg); * Provide an image with useful supported sensors enabled \*********************************************************************************************/ -#ifdef USE_SENSORS +#ifdef FIRMWARE_SENSORS #undef CODE_IMAGE #define CODE_IMAGE 3 @@ -92,6 +92,8 @@ void KNX_CB_Action(message_t const &msg, void *arg); //#define USE_MPU6050 // Enable MPU6050 sensor (I2C address 0x68 AD0 low or 0x69 AD0 high) (+2k6 code) //#define USE_DS3231 // Enable DS3231 external RTC in case no Wifi is avaliable. See docs in the source file (+1k2 code) //#define USE_MGC3130 // Enable MGC3130 Electric Field Effect Sensor (I2C address 0x42) (+2k7 code, 0k3 mem) +//#define USE_MAX44009 // Enable MAX44009 Ambient Light sensor (I2C addresses 0x4A and 0x4B) (+0k8 code) +#define USE_SCD30 // Enable Sensiron SCd30 CO2 sensor (I2C address 0x61) (+3k3 code) #define USE_MHZ19 // Add support for MH-Z19 CO2 sensor (+2k code) #define USE_SENSEAIR // Add support for SenseAir K30, K70 and S8 CO2 sensor (+2k3 code) #ifndef CO2_LOW @@ -112,10 +114,13 @@ void KNX_CB_Action(message_t const &msg, void *arg); #define TUYA_DIMMER_ID 0 // Default dimmer Id #endif #define USE_PS_16_DZ // Add support for PS-16-DZ Dimmer +//#define USE_AZ7798 // Add support for AZ-Instrument 7798 CO2 datalogger +#define USE_PN532_HSU // Add support for PN532 using HSU (Serial) interface (+1k8 code, 140 bytes mem) #define USE_PZEM004T // Add support for PZEM004T Energy monitor (+2k code) #define USE_PZEM_AC // Add support for PZEM014,016 Energy monitor (+1k1 code) #define USE_PZEM_DC // Add support for PZEM003,017 Energy monitor (+1k1 code) #define USE_MCP39F501 // Add support for MCP39F501 Energy monitor as used in Shelly 2 (+3k1 code) +#define USE_MAX31855 // Add support for MAX31855 K-Type thermocouple sensor using softSPI #define USE_IR_REMOTE // Send IR remote commands using library IRremoteESP8266 and ArduinoJson (+4k code, 0k3 mem, 48 iram) #define USE_IR_HVAC // Support for HVAC system using IR (+2k code) #define USE_IR_RECEIVE // Support for IR receiver (+5k5 code, 264 iram) @@ -135,15 +140,14 @@ void KNX_CB_Action(message_t const &msg, void *arg); #define USE_RF_SENSOR // Add support for RF sensor receiver (434MHz or 868MHz) (+0k8 code) // #define USE_THEO_V2 // Add support for decoding Theo V2 sensors as documented on https://sidweb.nl using 434MHz RF sensor receiver (+1k4 code) #define USE_ALECTO_V2 // Add support for decoding Alecto V2 sensors like ACH2010, WS3000 and DKW2012 using 868MHz RF sensor receiver (+1k7 code) -//#define USE_AZ7798 // Add support for AZ-Instrument 7798 CO2 datalogger -#endif // USE_SENSORS +#endif // FIRMWARE_SENSORS /*********************************************************************************************\ * [sonoff-classic.bin] * Provide an image close to version 5.12.0 but still within 499k program space to allow one time OTA \*********************************************************************************************/ -#ifdef USE_CLASSIC +#ifdef FIRMWARE_CLASSIC #undef CODE_IMAGE #define CODE_IMAGE 2 @@ -171,10 +175,6 @@ void KNX_CB_Action(message_t const &msg, void *arg); #undef USE_SENSEAIR // Disable support for SenseAir K30, K70 and S8 CO2 sensor #undef USE_PMS5003 // Disable support for PMS5003 and PMS7003 particle concentration sensor #undef USE_NOVA_SDS // Disable support for SDS011 and SDS021 particle concentration sensor -#undef USE_PZEM004T // Disable PZEM004T energy sensor -#undef USE_PZEM_AC // Disable PZEM014,016 Energy monitor -#undef USE_PZEM_DC // Disable PZEM003,017 Energy monitor -#undef USE_MCP39F501 // Disable support for MCP39F501 Energy monitor as used in Shelly 2 (+3k1 code) #undef USE_SERIAL_BRIDGE // Disable support for software Serial Bridge #undef USE_SDM120 // Disable support for Eastron SDM120-Modbus energy meter #undef USE_SDM630 // Disable support for Eastron SDM630-Modbus energy meter @@ -182,6 +182,13 @@ void KNX_CB_Action(message_t const &msg, void *arg); #undef USE_TUYA_DIMMER // Disable support for Tuya Serial Dimmer #undef USE_ARMTRONIX_DIMMERS // Disable support for Armtronix Dimmers (+1k4 code) #undef USE_PS_16_DZ // Disable support for PS-16-DZ Dimmer +#undef USE_AZ7798 // Disable support for AZ-Instrument 7798 CO2 datalogger +#undef USE_PN532_HSU // Disable support for PN532 using HSU (Serial) interface (+1k8 code, 140 bytes mem) +#undef USE_PZEM004T // Disable PZEM004T energy sensor +#undef USE_PZEM_AC // Disable PZEM014,016 Energy monitor +#undef USE_PZEM_DC // Disable PZEM003,017 Energy monitor +#undef USE_MCP39F501 // Disable support for MCP39F501 Energy monitor as used in Shelly 2 (+3k1 code) +#undef USE_MAX31855 // Disable MAX31855 K-Type thermocouple sensor using softSPI #undef USE_IR_REMOTE // Disable IR remote commands using library IRremoteESP8266 and ArduinoJson #undef USE_IR_RECEIVE // Disable support for IR receiver #undef USE_ARILUX_RF // Disable support for Arilux RF remote controller @@ -194,15 +201,14 @@ void KNX_CB_Action(message_t const &msg, void *arg); #undef USE_RF_SENSOR // Disable support for RF sensor receiver (434MHz or 868MHz) (+0k8 code) #undef DEBUG_THEO // Disable debug code #undef USE_DEBUG_DRIVER // Disable debug code -#undef USE_AZ7798 // Disable support for AZ-Instrument 7798 CO2 datalogger -#endif // USE_CLASSIC +#endif // FIRMWARE_CLASSIC /*********************************************************************************************\ * [sonoff-knx.bin] * Provide a dedicated KNX image allowing enough code and memory space \*********************************************************************************************/ -#ifdef USE_KNX_NO_EMULATION +#ifdef FIRMWARE_KNX_NO_EMULATION #undef CODE_IMAGE #define CODE_IMAGE 4 @@ -211,19 +217,23 @@ void KNX_CB_Action(message_t const &msg, void *arg); #define USE_KNX // Enable KNX IP Protocol Support (+23k code, +3k3 mem) #endif #undef USE_EMULATION // Disable Belkin WeMo and Hue Bridge emulation for Alexa (-16k code, -2k mem) -#endif // USE_KNX_NO_EMULATION +#endif // FIRMWARE_KNX_NO_EMULATION /*********************************************************************************************\ * [sonoff-display.bin] * Provide an image with display drivers enabled \*********************************************************************************************/ -#ifdef USE_DISPLAYS +#ifdef FIRMWARE_DISPLAYS #undef CODE_IMAGE #define CODE_IMAGE 6 #undef USE_ENERGY_SENSOR // Disable energy sensors (-14k code) + #undef USE_PZEM004T // Disable PZEM004T energy sensor + #undef USE_PZEM_AC // Disable PZEM014,016 Energy monitor + #undef USE_PZEM_DC // Disable PZEM003,017 Energy monitor + #undef USE_MCP39F501 // Disable MCP39F501 Energy monitor as used in Shelly 2 #undef USE_EMULATION // Disable Belkin WeMo and Hue Bridge emulation for Alexa (-16k code, -2k mem) #undef USE_DOMOTICZ // Disable Domoticz #undef USE_HOME_ASSISTANT // Disable Home Assistant @@ -241,7 +251,7 @@ void KNX_CB_Action(message_t const &msg, void *arg); #undef USE_ARILUX_RF // Remove support for Arilux RF remote controller (-0k8 code, 252 iram (non 2.3.0)) #undef USE_RF_FLASH // Remove support for flashing the EFM8BB1 chip on the Sonoff RF Bridge. C2CK must be connected to GPIO4, C2D to GPIO5 on the PCB (-3k code) -#endif // USE_DISPLAYS +#endif // FIRMWARE_DISPLAYS /*********************************************************************************************\ * Mandatory define for DS18x20 if changed by above image selections @@ -257,13 +267,13 @@ void KNX_CB_Action(message_t const &msg, void *arg); * Provide an image without sensors \*********************************************************************************************/ -#ifdef USE_BASIC +#ifdef FIRMWARE_BASIC #undef CODE_IMAGE #define CODE_IMAGE 5 #undef APP_SLEEP -#define APP_SLEEP 1 // Default to sleep = 1 for USE_BASIC +#define APP_SLEEP 1 // Default to sleep = 1 for FIRMWARE_BASIC //#undef USE_ENERGY_SENSOR // Disable energy sensors #undef USE_ARDUINO_OTA // Disable support for Arduino OTA @@ -299,10 +309,13 @@ void KNX_CB_Action(message_t const &msg, void *arg); //#undef USE_TUYA_DIMMER // Disable support for Tuya Serial Dimmer #undef USE_ARMTRONIX_DIMMERS // Disable support for Armtronix Dimmers (+1k4 code) #undef USE_PS_16_DZ // Disable support for PS-16-DZ Dimmer +#undef USE_AZ7798 // Disable support for AZ-Instrument 7798 CO2 datalogger +#undef USE_PN532_HSU // Disable support for PN532 using HSU (Serial) interface (+1k8 code, 140 bytes mem) #undef USE_PZEM004T // Disable PZEM004T energy sensor #undef USE_PZEM_AC // Disable PZEM014,016 Energy monitor #undef USE_PZEM_DC // Disable PZEM003,017 Energy monitor //#undef USE_MCP39F501 // Disable MCP39F501 Energy monitor as used in Shelly 2 +#undef USE_MAX31855 // Disable MAX31855 K-Type thermocouple sensor using softSPI #undef USE_IR_REMOTE // Disable IR driver #undef USE_WS2812 // Disable WS2812 Led string #undef USE_ARILUX_RF // Disable support for Arilux RF remote controller @@ -315,15 +328,14 @@ void KNX_CB_Action(message_t const &msg, void *arg); #undef USE_RF_SENSOR // Disable support for RF sensor receiver (434MHz or 868MHz) (+0k8 code) #undef DEBUG_THEO // Disable debug code #undef USE_DEBUG_DRIVER // Disable debug code -#undef USE_AZ7798 // Disable support for AZ-Instrument 7798 CO2 datalogger -#endif // USE_BASIC +#endif // FIRMWARE_BASIC /*********************************************************************************************\ * [sonoff-minimal.bin] * Provide the smallest image possible while still enabling a webserver for intermediate image load \*********************************************************************************************/ -#ifdef BE_MINIMAL +#ifdef FIRMWARE_MINIMAL #undef CODE_IMAGE #define CODE_IMAGE 1 @@ -362,10 +374,13 @@ void KNX_CB_Action(message_t const &msg, void *arg); #undef USE_TUYA_DIMMER // Disable support for Tuya Serial Dimmer #undef USE_ARMTRONIX_DIMMERS // Disable support for Armtronix Dimmers (+1k4 code) #undef USE_PS_16_DZ // Disable support for PS-16-DZ Dimmer +#undef USE_AZ7798 // Disable support for AZ-Instrument 7798 CO2 datalogger +#undef USE_PN532_HSU // Disable support for PN532 using HSU (Serial) interface (+1k8 code, 140 bytes mem) #undef USE_PZEM004T // Disable PZEM004T energy sensor #undef USE_PZEM_AC // Disable PZEM014,016 Energy monitor #undef USE_PZEM_DC // Disable PZEM003,017 Energy monitor #undef USE_MCP39F501 // Disable MCP39F501 Energy monitor as used in Shelly 2 +#undef USE_MAX31855 // DIsable MAX31855 K-Type thermocouple sensor using softSPI #undef USE_IR_REMOTE // Disable IR driver #undef USE_WS2812 // Disable WS2812 Led string #undef USE_ARILUX_RF // Disable support for Arilux RF remote controller @@ -378,8 +393,7 @@ void KNX_CB_Action(message_t const &msg, void *arg); #undef USE_RF_SENSOR // Disable support for RF sensor receiver (434MHz or 868MHz) (+0k8 code) #undef DEBUG_THEO // Disable debug code #undef USE_DEBUG_DRIVER // Disable debug code -#undef USE_AZ7798 // Disable support for AZ-Instrument 7798 CO2 datalogger -#endif // BE_MINIMAL +#endif // FIRMWARE_MINIMAL /*********************************************************************************************\ * Mandatory defines satisfying possible disabled defines diff --git a/sonoff/sonoff_template.h b/sonoff/sonoff_template.h index 48af9cf4b..c839d2a7e 100644 --- a/sonoff/sonoff_template.h +++ b/sonoff/sonoff_template.h @@ -1,7 +1,7 @@ /* sonoff_template.h - template settings for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends + Copyright (C) 2019 Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -143,25 +143,50 @@ enum UserSelectablePins { GPIO_RF_SENSOR, // Rf receiver with sensor decoding GPIO_AZ_TXD, // AZ-Instrument 7798 Serial interface GPIO_AZ_RXD, // AZ-Instrument 7798 Serial interface - GPIO_SENSOR_END }; - -// Programmer selectable GPIO functionality offset by user selectable GPIOs -enum ProgramSelectablePins { - GPIO_RXD = GPIO_SENSOR_END, // Serial interface - GPIO_TXD, // Serial interface - GPIO_SPI_MISO, // SPI MISO library fixed pin GPIO12 - GPIO_SPI_MOSI, // SPI MOSI library fixed pin GPIO13 - GPIO_SPI_CLK, // SPI Clk library fixed pin GPIO14 + GPIO_MAX31855CS, // MAX31855 Serial interface + GPIO_MAX31855CLK, // MAX31855 Serial interface + GPIO_MAX31855DO, // MAX31855 Serial interface + GPIO_KEY1_INV, // Inverted buttons + GPIO_KEY2_INV, + GPIO_KEY3_INV, + GPIO_KEY4_INV, + GPIO_KEY1_INV_NP, // Inverted buttons without pull-up + GPIO_KEY2_INV_NP, + GPIO_KEY3_INV_NP, + GPIO_KEY4_INV_NP, GPIO_NRG_SEL, // HLW8012/HLJ-01 Sel output (1 = Voltage) GPIO_NRG_SEL_INV, // HLW8012/HLJ-01 Sel output (0 = Voltage) GPIO_NRG_CF1, // HLW8012/HLJ-01 CF1 voltage / current GPIO_HLW_CF, // HLW8012 CF power GPIO_HJL_CF, // HJL-01/BL0937 CF power - GPIO_ADC0, // ADC + GPIO_MCP39F5_TX, // MCP39F501 Serial interface (Shelly2) + GPIO_MCP39F5_RX, // MCP39F501 Serial interface (Shelly2) + GPIO_MCP39F5_RST, // MCP39F501 Reset (Shelly2) + GPIO_PN532_TXD, // PN532 NFC Serial Tx + GPIO_PN532_RXD, // PN532 NFC Serial Rx + GPIO_SM16716_CLK, // SM16716 CLOCK + GPIO_SM16716_DAT, // SM16716 DATA + GPIO_SM16716_SEL, // SM16716 SELECT GPIO_DI, // my92x1 PWM input GPIO_DCKI, // my92x1 CLK input + GPIO_CSE7766_TX, // CSE7766 Serial interface (S31 and Pow R2) + GPIO_CSE7766_RX, // CSE7766 Serial interface (S31 and Pow R2) GPIO_ARIRFRCV, // AliLux RF Receive input - GPIO_USER, // User configurable + GPIO_TXD, // Serial interface + GPIO_RXD, // Serial interface + GPIO_ROT1A, // Rotary switch1 A Pin + GPIO_ROT1B, // Rotary switch1 B Pin + GPIO_ROT2A, // Rotary switch2 A Pin + GPIO_ROT2B, // Rotary switch2 B Pin + GPIO_SENSOR_END }; + +// Programmer selectable GPIO functionality +enum ProgramSelectablePins { + GPIO_FIX_START = 251, + GPIO_SPI_MISO, // SPI MISO library fixed pin GPIO12 + GPIO_SPI_MOSI, // SPI MOSI library fixed pin GPIO13 + GPIO_SPI_CLK, // SPI Clk library fixed pin GPIO14 + GPIO_USER, // User configurable needs to be 255 GPIO_MAX }; // Text in webpage Module Parameters and commands GPIOS and GPIO @@ -204,7 +229,19 @@ const char kSensorNames[] PROGMEM = D_SENSOR_MGC3130_XFER "|" D_SENSOR_MGC3130_RESET "|" D_SENSOR_SSPI_MISO "|" D_SENSOR_SSPI_MOSI "|" D_SENSOR_SSPI_SCLK "|" D_SENSOR_SSPI_CS "|" D_SENSOR_SSPI_DC "|" D_SENSOR_RF_SENSOR "|" - D_SENSOR_AZ_TX "|" D_SENSOR_AZ_RX; + D_SENSOR_AZ_TX "|" D_SENSOR_AZ_RX "|" + D_SENSOR_MAX31855_CS "|" D_SENSOR_MAX31855_CLK "|" D_SENSOR_MAX31855_DO "|" + D_SENSOR_BUTTON "1i|" D_SENSOR_BUTTON "2i|" D_SENSOR_BUTTON "3i|" D_SENSOR_BUTTON "4i|" + D_SENSOR_BUTTON "1in|" D_SENSOR_BUTTON "2in|" D_SENSOR_BUTTON "3in|" D_SENSOR_BUTTON "4in|" + D_SENSOR_NRG_SEL "|" D_SENSOR_NRG_SEL "i|" D_SENSOR_NRG_CF1 "|" D_SENSOR_HLW_CF "|" D_SENSOR_HJL_CF "|" + D_SENSOR_MCP39F5_TX "|" D_SENSOR_MCP39F5_RX "|" D_SENSOR_MCP39F5_RST "|" + D_SENSOR_PN532_TX "|" D_SENSOR_PN532_RX "|" + D_SENSOR_SM16716_CLK "|" D_SENSOR_SM16716_DAT "|" D_SENSOR_SM16716_POWER "|" + D_SENSOR_MY92X1_DI "|" D_SENSOR_MY92X1_DCKI "|" + D_SENSOR_CSE7766_TX "|" D_SENSOR_CSE7766_RX "|" + D_SENSOR_ARIRFRCV "|" D_SENSOR_TXD "|" D_SENSOR_RXD "|" + D_SENSOR_ROTARY "1a|" D_SENSOR_ROTARY "1b|" D_SENSOR_ROTARY "2a|" D_SENSOR_ROTARY "2b|" + ; /********************************************************************************************/ @@ -270,11 +307,23 @@ enum SupportedModules { PS_16_DZ, TECKIN_US, MANZOKU_EU_4, + OBI2, + YTF_IR_BRIDGE, + DIGOO, + KA10, + ZX2820, + MI_DESK_LAMP, + SP10, + WAGA, + SYF05, MAXMODULE }; +#define USER_MODULE 255 + /********************************************************************************************/ -#define MAX_GPIO_PIN 18 // Number of supported GPIO +#define MAX_GPIO_PIN 17 // Number of supported GPIO +#define MIN_FLASH_PINS 4 // Number of flash chip pins unusable for configuration (GPIO6, 7, 8 and 11) const char PINS_WEMOS[] PROGMEM = "D3TXD4RXD2D1flashcFLFLolD6D7D5D8D0A0"; @@ -282,21 +331,59 @@ typedef struct MYIO { uint8_t io[MAX_GPIO_PIN]; } myio; +typedef struct MYCFGIO { + uint8_t io[MAX_GPIO_PIN - MIN_FLASH_PINS]; +} mycfgio; + +#define GPIO_FLAG_USED 1 // Currently only one flag used + +#define GPIO_FLAG_ADC0 1 // Allow ADC0 when define USE_ADC_VCC is disabled +#define GPIO_FLAG_SPARE01 2 // Allow input pull-up control using SetOption62 - Superseded by user template editing +#define GPIO_FLAG_SPARE02 4 +#define GPIO_FLAG_SPARE03 8 +#define GPIO_FLAG_SPARE04 16 +#define GPIO_FLAG_SPARE05 32 +#define GPIO_FLAG_SPARE06 64 +#define GPIO_FLAG_SPARE07 128 + +typedef union { + uint8_t data; + struct { + uint8_t adc0 : 1; // Allow ADC0 when define USE_ADC_VCC is disabled + uint8_t spare01 : 1; + uint8_t spare02 : 1; + uint8_t spare03 : 1; + uint8_t spare04 : 1; + uint8_t spare05 : 1; + uint8_t spare06 : 1; + uint8_t spare07 : 1; + }; +} gpio_flag; + typedef struct MYTMPLT { char name[15]; - myio gp; + mycfgio gp; + gpio_flag flag; } mytmplt; const uint8_t kGpioNiceList[] PROGMEM = { GPIO_NONE, // Not used GPIO_KEY1, // Buttons GPIO_KEY1_NP, + GPIO_KEY1_INV, + GPIO_KEY1_INV_NP, GPIO_KEY2, GPIO_KEY2_NP, + GPIO_KEY2_INV, + GPIO_KEY2_INV_NP, GPIO_KEY3, GPIO_KEY3_NP, + GPIO_KEY3_INV, + GPIO_KEY3_INV_NP, GPIO_KEY4, GPIO_KEY4_NP, + GPIO_KEY4_INV, + GPIO_KEY4_INV_NP, GPIO_SWT1, // User connected external switches GPIO_SWT1_NP, GPIO_SWT2, @@ -355,6 +442,8 @@ const uint8_t kGpioNiceList[] PROGMEM = { GPIO_CNTR3_NP, GPIO_CNTR4, GPIO_CNTR4_NP, + GPIO_TXD, // Serial interface + GPIO_RXD, // Serial interface #ifdef USE_I2C GPIO_I2C_SCL, // I2C SCL GPIO_I2C_SDA, // I2C SDA @@ -404,21 +493,19 @@ const uint8_t kGpioNiceList[] PROGMEM = { GPIO_HX711_SCK, // HX711 Load Cell clock GPIO_HX711_DAT, // HX711 Load Cell data #endif -#ifdef USE_SERIAL_BRIDGE - GPIO_SBR_TX, // Serial Bridge Serial interface - GPIO_SBR_RX, // Serial Bridge Serial interface +#if defined(USE_ENERGY_SENSOR) && defined(USE_HLW8012) + GPIO_NRG_SEL, // HLW8012/HLJ-01 Sel output (1 = Voltage) + GPIO_NRG_SEL_INV, // HLW8012/HLJ-01 Sel output (0 = Voltage) + GPIO_NRG_CF1, // HLW8012/HLJ-01 CF1 voltage / current + GPIO_HLW_CF, // HLW8012 CF power + GPIO_HJL_CF, // HJL-01/BL0937 CF power #endif -#ifdef USE_MHZ19 - GPIO_MHZ_TXD, // MH-Z19 Serial interface - GPIO_MHZ_RXD, // MH-Z19 Serial interface -#endif -#ifdef USE_SENSEAIR - GPIO_SAIR_TX, // SenseAir Serial interface - GPIO_SAIR_RX, // SenseAir Serial interface -#endif -#ifdef USE_NOVA_SDS - GPIO_SDS0X1_TX, // Nova Fitness SDS011 Serial interface - GPIO_SDS0X1_RX, // Nova Fitness SDS011 Serial interface + GPIO_CSE7766_TX, // CSE7766 Serial interface (S31 and Pow R2) + GPIO_CSE7766_RX, // CSE7766 Serial interface (S31 and Pow R2) +#if defined(USE_ENERGY_SENSOR) && defined(USE_MCP39F501) + GPIO_MCP39F5_TX, // MCP39F501 Serial interface (Shelly2) + GPIO_MCP39F5_RX, // MCP39F501 Serial interface (Shelly2) + GPIO_MCP39F5_RST, // MCP39F501 Reset (Shelly2) #endif #if defined(USE_PZEM004T) || defined(USE_PZEM_AC) || defined(USE_PZEM_DC) GPIO_PZEM0XX_TX, // PZEM0XX Serial interface @@ -440,6 +527,22 @@ const uint8_t kGpioNiceList[] PROGMEM = { GPIO_SDM630_TX, // SDM630 Serial interface GPIO_SDM630_RX, // SDM630 Serial interface #endif +#ifdef USE_SERIAL_BRIDGE + GPIO_SBR_TX, // Serial Bridge Serial interface + GPIO_SBR_RX, // Serial Bridge Serial interface +#endif +#ifdef USE_MHZ19 + GPIO_MHZ_TXD, // MH-Z19 Serial interface + GPIO_MHZ_RXD, // MH-Z19 Serial interface +#endif +#ifdef USE_SENSEAIR + GPIO_SAIR_TX, // SenseAir Serial interface + GPIO_SAIR_RX, // SenseAir Serial interface +#endif +#ifdef USE_NOVA_SDS + GPIO_SDS0X1_TX, // Nova Fitness SDS011 Serial interface + GPIO_SDS0X1_RX, // Nova Fitness SDS011 Serial interface +#endif #ifdef USE_PMS5003 GPIO_PMS5003, // Plantower PMS5003 Serial interface #endif @@ -451,16 +554,37 @@ const uint8_t kGpioNiceList[] PROGMEM = { #endif #ifdef USE_TUYA_DIMMER GPIO_TUYA_TX, // Tuya Serial interface - GPIO_TUYA_RX, // Tuya Serial interface -#endif -#ifdef USE_MGC3130 - GPIO_MGC3130_XFER, - GPIO_MGC3130_RESET + GPIO_TUYA_RX, // Tuya Serial interface #endif #ifdef USE_AZ7798 GPIO_AZ_TXD, // AZ-Instrument 7798 CO2 datalogger Serial interface - GPIO_AZ_RXD // AZ-Instrument 7798 CO2 datalogger Serial interface + GPIO_AZ_RXD, // AZ-Instrument 7798 CO2 datalogger Serial interface #endif +#ifdef USE_PN532_HSU + GPIO_PN532_TXD, // PN532 HSU Tx + GPIO_PN532_RXD, // PN532 HSU Rx +#endif +#ifdef USE_MGC3130 + GPIO_MGC3130_XFER, + GPIO_MGC3130_RESET, +#endif +#ifdef USE_MAX31855 + GPIO_MAX31855CS, // MAX31855 Serial interface + GPIO_MAX31855CLK, // MAX31855 Serial interface + GPIO_MAX31855DO, // MAX31855 Serial interface +#endif + GPIO_DI, // my92x1 PWM input + GPIO_DCKI, // my92x1 CLK input +#ifdef USE_SM16716 + GPIO_SM16716_CLK, // SM16716 CLOCK + GPIO_SM16716_DAT, // SM16716 DATA + GPIO_SM16716_SEL, // SM16716 SELECT +#endif // USE_SM16716 + GPIO_ROT1A, // Rotary switch1 A Pin + GPIO_ROT1B, // Rotary switch1 B Pin + GPIO_ROT2A, // Rotary switch2 A Pin + GPIO_ROT2B, // Rotary switch2 B Pin + GPIO_ARIRFRCV // AliLux RF Receive input }; const uint8_t kModuleNiceList[MAXMODULE] PROGMEM = { @@ -499,30 +623,39 @@ const uint8_t kModuleNiceList[MAXMODULE] PROGMEM = { WION, SHELLY1, SHELLY2, - BLITZWOLF_BWSHP, // Socket Relay Devices with Energy Monitoring + BLITZWOLF_BWSHP, // Socket Relay Devices with Energy Monitoring TECKIN, TECKIN_US, APLIC_WDP303075, GOSUND, + ZX2820, SK03_TUYA, - NEO_COOLCAM, // Socket Relay Devices + DIGOO, + KA10, + SP10, + WAGA, + NEO_COOLCAM, // Socket Relay Devices OBI, + OBI2, MANZOKU_EU_4, - ESP_SWITCH, // Switch Devices - TUYA_DIMMER, // Dimmer Devices + ESP_SWITCH, // Switch Devices + TUYA_DIMMER, // Dimmer Devices ARMTRONIX_DIMMERS, PS_16_DZ, - H801, // Light Devices + H801, // Light Devices MAGICHOME, ARILUX_LC01, ARILUX_LC06, ARILUX_LC11, ZENGGE_ZF_WF017, HUAFAN_SS, + MI_DESK_LAMP, KMC_70011, - AILIGHT, // Light Bulbs + AILIGHT, // Light Bulbs PHILIPS, - WITTY, // Development Devices + SYF05, + YTF_IR_BRIDGE, + WITTY, // Development Devices WEMOS }; @@ -535,14 +668,14 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, // GPIO03 Serial TXD and Optional sensor GPIO_USER, // GPIO04 Optional sensor 0, // GPIO05 - 0, // GPIO06 (SD_CLK Flash) - 0, // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - 0, // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO) - 0, // GPIO10 (SD_DATA3 Flash QIO) - 0, // GPIO11 (SD_CMD Flash) + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) - GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) + GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) - Link and Power status GPIO_USER, // GPIO14 Optional sensor 0, // GPIO15 0, // GPIO16 @@ -551,13 +684,18 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { { "Sonoff RF", // Sonoff RF (ESP8266) GPIO_KEY1, // GPIO00 Button GPIO_USER, // GPIO01 Serial RXD and Optional sensor - 0, + GPIO_USER, // GPIO02 Optional sensor GPIO_USER, // GPIO03 Serial TXD and Optional sensor GPIO_USER, // GPIO04 Optional sensor 0, - 0, 0, 0, 0, 0, 0, // Flash connection + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) - GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) + GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) - Link and Power status GPIO_USER, // GPIO14 Optional sensor 0, 0, 0 }, @@ -568,12 +706,17 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, // GPIO03 Serial TXD and Optional sensor GPIO_USER, // GPIO04 Optional sensor GPIO_USER, // GPIO05 Optional sensor - 0, 0, 0, 0, 0, 0, // Flash connection + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) - GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) + GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) - Link and Power status GPIO_USER, // GPIO14 Optional sensor 0, 0, - GPIO_ADC0 // ADC0 Analog input + GPIO_FLAG_ADC0 // ADC0 Analog input }, { "Sonoff TH", // Sonoff TH10/16 (ESP8266) GPIO_KEY1, // GPIO00 Button @@ -582,9 +725,14 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, // GPIO03 Serial TXD and Optional sensor GPIO_USER, // GPIO04 Optional sensor 0, - 0, 0, 0, 0, 0, 0, // Flash connection + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) - GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) + GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) - Link and Power status GPIO_USER, // GPIO14 Optional sensor 0, 0, 0 }, @@ -595,9 +743,14 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_RXD, // GPIO03 Relay control GPIO_USER, // GPIO04 Optional sensor 0, - 0, 0, 0, 0, 0, 0, // Flash connection + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) 0, - GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) + GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status GPIO_USER, // GPIO14 Optional sensor 0, 0, 0 }, @@ -605,11 +758,16 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_KEY1, // GPIO00 Button 0, 0, 0, 0, GPIO_NRG_SEL, // GPIO05 HLW8012 Sel output (1 = Voltage) - 0, 0, 0, 0, 0, 0, // Flash connection + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) GPIO_NRG_CF1, // GPIO13 HLW8012 CF1 voltage / current GPIO_HLW_CF, // GPIO14 HLW8012 CF power - GPIO_LED1, // GPIO15 Blue Led (0 = On, 1 = Off) + GPIO_LED1, // GPIO15 Blue Led (0 = On, 1 = Off) - Link and Power status 0, 0 }, { "Sonoff 4CH", // Sonoff 4CH (ESP8285) @@ -619,11 +777,13 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, // GPIO03 Serial TXD and Optional sensor 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, 0, 0, // Flash connection + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) GPIO_KEY2, // GPIO09 Button 2 GPIO_KEY3, // GPIO10 Button 3 - 0, // Flash connection - GPIO_REL1, // GPIO12 Red Led and Relay 1 (0 = Off, 1 = On) + // GPIO11 (SD_CMD Flash) + GPIO_REL1, // GPIO12 Red Led and Relay 1 (0 = Off, 1 = On) - Link and Power status GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) GPIO_KEY4, // GPIO14 Button 4 GPIO_REL4, // GPIO15 Red Led and Relay 4 (0 = Off, 1 = On) @@ -635,8 +795,13 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, // GPIO02 Optional sensor GPIO_USER, // GPIO03 Serial TXD and Optional sensor 0, 0, - 0, 0, 0, 0, 0, 0, // Flash connection - GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) - Link and Power status GPIO_LED1_INV, // GPIO13 Green/Blue Led (0 = On, 1 = Off) 0, 0, 0, 0 }, @@ -646,9 +811,14 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { 0, GPIO_USER, // GPIO03 Serial TXD and Optional sensor 0, 0, - 0, 0, 0, 0, 0, 0, // Flash connection + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) - GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) + GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status 0, 0, 0, 0 }, { "Sonoff Touch", // Sonoff Touch (ESP8285) @@ -657,10 +827,13 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { 0, GPIO_USER, // GPIO03 Serial TXD and Optional sensor 0, 0, - 0, 0, 0, // Flash connection - 0, 0, - 0, // Flash connection - GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 + 0, // GPIO10 + // GPIO11 (SD_CMD Flash) + GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) - Link and Power status GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) 0, 0, 0, 0 }, @@ -669,9 +842,14 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { 0, 0, 0, GPIO_USER, // GPIO04 Optional sensor (PWM3 Green) GPIO_USER, // GPIO05 Optional sensor (PWM2 Red) - 0, 0, 0, 0, 0, 0, // Flash connection + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) GPIO_PWM1, // GPIO12 Cold light (PWM0 Cold) - GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) + GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status GPIO_PWM2, // GPIO14 Warm light (PWM1 Warm) GPIO_USER, // GPIO15 Optional sensor (PWM4 Blue) 0, 0 @@ -679,9 +857,14 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { { "1 Channel", // 1 Channel Inching/Latching Relay using (PSA-B01 - ESP8266 and PSF-B01 - ESP8285) GPIO_KEY1, // GPIO00 Button 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, // Flash connection + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) - GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) + GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) - Link and Power status 0, 0, 0, 0 }, { "4 Channel", // 4 Channel Inching/Latching Relays (ESP8266) @@ -690,17 +873,27 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { 0, GPIO_RXD, // GPIO03 Relay control 0, 0, - 0, 0, 0, 0, 0, 0, // Flash connection + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) 0, - GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) + GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status 0, 0, 0, 0 }, { "Motor C/AC", // Motor Clockwise / Anti clockwise (PSA-B01 - ESP8266) GPIO_KEY1, // GPIO00 Button 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, // Flash connection + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) - GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) + GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) - Link and Power status 0, 0, 0, 0 }, { "ElectroDragon", // ElectroDragon IoT Relay Board (ESP8266) @@ -710,13 +903,18 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, // GPIO03 Serial TXD and Optional sensor GPIO_USER, // GPIO04 Optional sensor GPIO_USER, // GPIO05 Optional sensor - 0, 0, 0, 0, 0, 0, // Flash connection + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) 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 GPIO_USER, // GPIO15 Optional sensor - GPIO_LED1, // GPIO16 Green/Blue Led (1 = On, 0 = Off) - GPIO_ADC0 // ADC0 A0 Analog input + GPIO_LED1, // GPIO16 Green/Blue Led (1 = On, 0 = Off) - Link and Power status + GPIO_FLAG_ADC0 // ADC0 A0 Analog input }, { "EXS Relay(s)", // ES-Store Latching relay(s) (ESP8266) // https://ex-store.de/ESP8266-WiFi-Relay-V31 @@ -728,11 +926,16 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, // GPIO03 UART0_RXD V3.1 Module Pin 3 GPIO_USER, // GPIO04 V3.1 Module Pin 10 - V5.0 Module Pin 2 GPIO_USER, // GPIO05 V3.1 Module Pin 9 - V5.0 Module Pin 1 - 0, 0, 0, 0, 0, 0, // Flash connection + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) GPIO_REL1, // GPIO12 Relay1 ( 1 = Off) GPIO_REL2, // GPIO13 Relay1 ( 1 = On) GPIO_USER, // GPIO14 V3.1 Module Pin 5 - V5.0 GPIO_REL3_INV Relay2 ( 1 = Off) - GPIO_LED1, // GPIO15 V5.0 LED1 + GPIO_LED1, // GPIO15 V5.0 LED1 - Link and Power status GPIO_USER, // GPIO16 V3.1 Module Pin 4 - V5.0 GPIO_REL4_INV Relay2 ( 1 = On) 0 }, @@ -740,9 +943,14 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { // https://www.amazon.com/gp/product/B00ZYLUBJU/ref=s9_acsd_al_bw_c_x_3_w GPIO_USER, // GPIO00 Optional sensor (pm clock) 0, - GPIO_LED1, // GPIO02 Green Led (1 = On, 0 = Off) + GPIO_LED1, // GPIO02 Green Led (1 = On, 0 = Off) - Link and Power status 0, 0, 0, - 0, 0, 0, 0, 0, 0, // Flash connection + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) GPIO_USER, // GPIO12 Optional sensor (pm data) GPIO_KEY1, // GPIO13 Button 0, @@ -756,16 +964,18 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, // GPIO03 RX Serial TXD and Optional sensor GPIO_USER, // GPIO04 D2 Wemos I2C SDA GPIO_USER, // GPIO05 D1 Wemos I2C SCL / Wemos Relay Shield (0 = Off, 1 = On) / Wemos WS2812B RGB led Shield - 0, 0, 0, // Flash connection - GPIO_USER, // Flash connection or GPIO09 on ESP8285 only! - GPIO_USER, // Flash connection or GPIO10 on ESP8285 only! - 0, // Flash connection + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + GPIO_USER, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + GPIO_USER, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) GPIO_USER, // GPIO12 D6 GPIO_USER, // GPIO13 D7 GPIO_USER, // GPIO14 D5 GPIO_USER, // GPIO15 D8 GPIO_USER, // GPIO16 D0 Wemos Wake - GPIO_ADC0 // ADC0 A0 Analog input + GPIO_FLAG_ADC0 // ADC0 A0 Analog input }, { "Sonoff Dev", // Sonoff Dev (ESP8266) GPIO_KEY1, // GPIO00 E-FW Button @@ -774,22 +984,32 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, // GPIO03 RX Serial TXD and Optional sensor GPIO_USER, // GPIO04 Optional sensor GPIO_USER, // GPIO05 Optional sensor - 0, 0, 0, 0, 0, 0, // Flash connection + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) GPIO_USER, // GPIO12 GPIO_USER, // GPIO13 BLUE LED GPIO_USER, // GPIO14 Optional sensor 0, // GPIO15 0, // GPIO16 - GPIO_ADC0 // ADC0 A0 Analog input + GPIO_FLAG_ADC0 // ADC0 A0 Analog input }, { "H801", // Lixada H801 Wifi (ESP8266) GPIO_USER, // GPIO00 E-FW Button - GPIO_LED1, // GPIO01 Green LED + GPIO_LED1, // GPIO01 Green LED - Link and Power status GPIO_USER, // GPIO02 TX and Optional sensor - Pin next to TX on the PCB GPIO_USER, // GPIO03 RX and Optional sensor - Pin next to GND on the PCB GPIO_PWM5, // GPIO04 W2 - PWM5 GPIO_LED2_INV, // GPIO05 Red LED - 0, 0, 0, 0, 0, 0, // Flash connection + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) GPIO_PWM3, // GPIO12 Blue GPIO_PWM2, // GPIO13 Green GPIO_PWM4, // GPIO14 W1 - PWM4 @@ -802,20 +1022,27 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, // GPIO02 Optional sensor GPIO_RXD, // GPIO03 TXD to ATMEGA328P 0, 0, - 0, 0, 0, 0, 0, 0, // Flash connection + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) 0, - GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) + GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) - Link and Power status 0, 0, 0, 0 }, { "Sonoff BN-SZ", // Sonoff BN-SZ01 Ceiling led (ESP8285) 0, 0, 0, 0, 0, 0, - 0, 0, 0, // Flash connection - 0, 0, - 0, // Flash connection + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 + 0, // GPIO10 + // GPIO11 (SD_CMD Flash) GPIO_PWM1, // GPIO12 Light - GPIO_LED1_INV, // GPIO13 Red Led (0 = On, 1 = Off) - 0, 0, - 0, 0 + GPIO_LED1_INV, // GPIO13 Red Led (0 = On, 1 = Off) - Link and Power status + 0, 0, 0, 0 }, { "Sonoff 4CH Pro", // Sonoff 4CH Pro (ESP8285) GPIO_KEY1, // GPIO00 Button 1 @@ -824,23 +1051,30 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, // GPIO03 Serial TXD and Optional sensor 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, 0, 0, // Flash connection + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) GPIO_KEY2, // GPIO09 Button 2 GPIO_KEY3, // GPIO10 Button 3 - 0, // Flash connection + // GPIO11 (SD_CMD Flash) GPIO_REL1, // GPIO12 Red Led and Relay 1 (0 = Off, 1 = On) - GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) + GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status GPIO_KEY4, // GPIO14 Button 4 GPIO_REL4, // GPIO15 Red Led and Relay 4 (0 = Off, 1 = On) 0, 0 }, { "Huafan SS", // Hua Fan Smart Socket (ESP8266) - like Sonoff Pow - GPIO_LED1_INV, // GPIO0 Blue Led (0 = On, 1 = Off) + GPIO_LED1_INV, // GPIO00 Blue Led (0 = On, 1 = Off) - Link status 0, 0, - GPIO_LED2_INV, // GPIO3 Red Led (0 = On, 1 = Off) - GPIO_KEY1, // GPIO4 Button - GPIO_REL1_INV, // GPIO5 Relay (0 = On, 1 = Off) - 0, 0, 0, 0, 0, 0, // Flash connection + GPIO_LED2_INV, // GPIO03 Red Led (0 = On, 1 = Off) - Power status + GPIO_KEY1, // GPIO04 Button + GPIO_REL1_INV, // GPIO05 Relay (0 = On, 1 = Off) + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) GPIO_NRG_CF1, // GPIO12 HLW8012 CF1 voltage / current GPIO_NRG_SEL, // GPIO13 HLW8012 Sel output (1 = Voltage) GPIO_HLW_CF, // GPIO14 HLW8012 CF power @@ -853,12 +1087,16 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_RXD, // GPIO03 RF bridge control GPIO_USER, // GPIO04 Optional sensor GPIO_USER, // GPIO05 Optional sensor - 0, 0, 0, // Flash connection - 0, 0, - 0, // Flash connection - 0, - GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - 0, 0, 0, 0 + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 + 0, // GPIO10 + // GPIO11 (SD_CMD Flash) + GPIO_USER, // GPIO12 Optional sensor + GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status + GPIO_USER, // GPIO14 Optional sensor + 0, 0, 0 }, { "Sonoff B1", // Sonoff B1 (ESP8285 - my9231) GPIO_KEY1, // GPIO00 Pad @@ -866,9 +1104,12 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, // GPIO02 Optional sensor SDA pad GPIO_USER, // GPIO03 Serial TXD and Optional sensor pad 0, 0, - 0, 0, 0, // Flash connection - 0, 0, - 0, // Flash connection + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 + 0, // GPIO10 + // GPIO11 (SD_CMD Flash) GPIO_DI, // GPIO12 my9231 DI 0, GPIO_DCKI, // GPIO14 my9231 DCKI @@ -880,9 +1121,12 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, // GPIO02 Optional sensor SDA pad GPIO_USER, // GPIO03 Serial TXD and Optional sensor pad 0, 0, - 0, 0, 0, // Flash connection - 0, 0, - 0, // Flash connection + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) 0, GPIO_DI, // GPIO13 my9291 DI 0, @@ -895,11 +1139,14 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, // GPIO02 Optional Sensor (J3 Pin 5) GPIO_USER, // GPIO03 Serial TXD and Optional sensor 0, 0, - 0, 0, 0, // Flash connection - 0, 0, - 0, // Flash connection + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 + 0, // GPIO10 + // GPIO11 (SD_CMD Flash) GPIO_REL1, // GPIO12 Blue Led and Relay 1 (0 = Off, 1 = On) - GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) + GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status 0, 0, 0, 0 }, { "Sonoff T1 2CH", // Sonoff T1 2CH (ESP8285) @@ -909,12 +1156,14 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, // GPIO03 Serial TXD and Optional sensor 0, GPIO_REL2, // GPIO05 Blue Led and Relay 2 (0 = Off, 1 = On) - 0, 0, 0, // Flash connection + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) GPIO_KEY2, // GPIO09 Button 2 - 0, - 0, // Flash connection + 0, // GPIO10 + // GPIO11 (SD_CMD Flash) GPIO_REL1, // GPIO12 Blue Led and Relay 1 (0 = Off, 1 = On) - GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) + GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status 0, 0, 0, 0 }, { "Sonoff T1 3CH", // Sonoff T1 3CH (ESP8285) @@ -924,12 +1173,14 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, // GPIO03 Serial TXD and Optional sensor GPIO_REL3, // GPIO04 Blue Led and Relay 3 (0 = Off, 1 = On) GPIO_REL2, // GPIO05 Blue Led and Relay 2 (0 = Off, 1 = On) - 0, 0, 0, // Flash connection + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) GPIO_KEY2, // GPIO09 Button 2 GPIO_KEY3, // GPIO10 Button 3 - 0, // Flash connection + // GPIO11 (SD_CMD Flash) GPIO_REL1, // GPIO12 Blue Led and Relay 1 (0 = Off, 1 = On) - GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) + GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status 0, 0, 0, 0 }, { "Supla Espablo", // Supla Espablo (ESP8266) @@ -940,55 +1191,75 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, // GPIO03 Serial TXD and Optional sensor GPIO_KEY1, // GPIO04 Button 1 GPIO_REL1, // GPIO05 Relay 1 (0 = Off, 1 = On) - 0, 0, 0, 0, 0, 0, // Flash connection + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) GPIO_USER, // GPIO12 Optional sensor GPIO_REL2, // GPIO13 Relay 2 (0 = Off, 1 = On) GPIO_USER, // GPIO14 Optional sensor 0, - GPIO_LED1, // GPIO16 Led (1 = On, 0 = Off) - GPIO_ADC0 // ADC0 A0 Analog input + GPIO_LED1, // GPIO16 Led (1 = On, 0 = Off) - Link and Power status + GPIO_FLAG_ADC0 // ADC0 A0 Analog input }, { "Witty Cloud", // Witty Cloud Dev Board (ESP8266) // https://www.aliexpress.com/item/ESP8266-serial-WIFI-Witty-cloud-Development-Board-ESP-12F-module-MINI-nodemcu/32643464555.html GPIO_USER, // GPIO00 D3 flash push button on interface board GPIO_USER, // GPIO01 Serial RXD and Optional sensor - GPIO_LED1_INV, // GPIO02 D4 Blue Led (0 = On, 1 = Off) on ESP-12F + GPIO_LED1_INV, // GPIO02 D4 Blue Led (0 = On, 1 = Off) on ESP-12F - Link and Power status GPIO_USER, // GPIO03 Serial TXD and Optional sensor GPIO_KEY1, // GPIO04 D2 push button on ESP-12F board GPIO_USER, // GPIO05 D1 optional sensor - 0, 0, 0, 0, 0, 0, // Flash connection + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) GPIO_PWM2, // GPIO12 D6 RGB LED Green GPIO_PWM3, // GPIO13 D7 RGB LED Blue GPIO_USER, // GPIO14 D5 optional sensor GPIO_PWM1, // GPIO15 D8 RGB LED Red GPIO_USER, // GPIO16 D0 optional sensor - GPIO_ADC0 // ADC0 A0 Light sensor / Requires USE_ADC_VCC in user_config.h to be disabled + GPIO_FLAG_ADC0 // ADC0 A0 Light sensor / Requires USE_ADC_VCC in user_config.h to be disabled }, { "Yunshan Relay", // Yunshan Wifi Relay (ESP8266) // https://www.ebay.com/p/Esp8266-220v-10a-Network-Relay-WiFi-Module/1369583381 // Schematics and Info https://ucexperiment.wordpress.com/2016/12/18/yunshan-esp8266-250v-15a-acdc-network-wifi-relay-module/ 0, // GPIO00 Flash jumper - Module Pin 8 GPIO_USER, // GPIO01 Serial RXD and Optional sensor - Module Pin 2 - GPIO_LED1_INV, // GPIO02 Blue Led (0 = On, 1 = Off) on ESP-12F - Module Pin 7 + GPIO_LED1_INV, // GPIO02 Blue Led (0 = On, 1 = Off) on ESP-12F - Module Pin 7 - Link and Power status GPIO_USER, // GPIO03 Serial TXD and Optional sensor - Module Pin 3 GPIO_REL1, // GPIO04 Red Led and Relay (0 = Off, 1 = On) - Module Pin 10 GPIO_KEY1, // GPIO05 Blue Led and OptoCoupler input - Module Pin 9 - 0, 0, 0, 0, 0, 0, // Flash connection - 0, 0, 0, 0, 0 + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + 0, 0, 0, 0, 0, 0 }, { "MagicHome", // Magic Home (aka Flux-light) (ESP8266) and Arilux LC10 (ESP8285) // https://www.aliexpress.com/item/Magic-Home-Mini-RGB-RGBW-Wifi-Controller-For-Led-Strip-Panel-light-Timing-Function-16million-colors/32686853650.html 0, GPIO_USER, // GPIO01 Serial RXD and Optional sensor - GPIO_LED1_INV, // GPIO02 Blue onboard LED + GPIO_LED1_INV, // GPIO02 Blue onboard LED - Link and Power status GPIO_USER, // GPIO03 Serial TXD and Optional sensor GPIO_ARIRFRCV, // GPIO04 IR or RF receiver (optional) (Arilux LC10) GPIO_PWM2, // GPIO05 RGB LED Green - 0, 0, 0, 0, 0, 0, // Flash connection + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) GPIO_PWM3, // GPIO12 RGB LED Blue GPIO_USER, // GPIO13 RGBW LED White (optional - set to PWM4 for Cold White or Warm White as used on Arilux LC10) GPIO_PWM1, // GPIO14 RGB LED Red - GPIO_LED2_INV, // GPIO15 RF receiver control (Arilux LC10) + GPIO_LED4_INV, // GPIO15 RF receiver control (Arilux LC10) 0, 0 }, { "Luani HVIO", // ESP8266_HVIO @@ -999,13 +1270,18 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, // GPIO03 Serial TXD and Optional sensor GPIO_REL1, // GPIO04 Relay 1 (0 = Off, 1 = On) GPIO_REL2, // GPIO05 Relay 2 (0 = Off, 1 = On) - 0, 0, 0, 0, 0, 0, // Flash connection + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) GPIO_SWT1, // GPIO12 External input 1 (0 = On, 1 = Off) GPIO_SWT2, // GPIO13 External input 2 (0 = On, 1 = Off) GPIO_USER, // GPIO14 Optional sensor / I2C SCL pad - GPIO_LED1, // GPIO15 Led (1 = On, 0 = Off) + GPIO_LED1, // GPIO15 Led (1 = On, 0 = Off) - Link and Power status 0, - GPIO_ADC0 // ADC0 A0 Analog input + GPIO_FLAG_ADC0 // ADC0 A0 Analog input }, { "KMC 70011", // KMC 70011 // https://www.amazon.com/KMC-Timing-Monitoring-Network-125V-240V/dp/B06XRX2GTQ @@ -1013,9 +1289,14 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { 0, 0, 0, GPIO_HLW_CF, // GPIO04 HLW8012 CF power GPIO_NRG_CF1, // GPIO05 HLW8012 CF1 voltage / current - 0, 0, 0, 0, 0, 0, // Flash connection + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) GPIO_NRG_SEL, // GPIO12 HLW8012 SEL (1 = Voltage) - GPIO_LED1_INV, // GPIO13 Green Led + GPIO_LED1_INV, // GPIO13 Green Led - Link and Power status GPIO_REL1, // GPIO14 Relay 0, 0, 0 }, @@ -1024,11 +1305,16 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { // (PwmFrequency 1111Hz) GPIO_KEY1, // GPIO00 Optional Button GPIO_USER, // GPIO01 Serial RXD and Optional sensor - GPIO_LED2_INV, // GPIO02 RF receiver control + GPIO_LED4_INV, // GPIO02 RF receiver control GPIO_USER, // GPIO03 Serial TXD and Optional sensor GPIO_ARIRFRCV, // GPIO04 IR or RF receiver (optional) GPIO_PWM1, // GPIO05 RGB LED Red - 0, 0, 0, 0, 0, 0, // Flash connection + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) GPIO_PWM2, // GPIO12 RGB LED Green GPIO_PWM3, // GPIO13 RGB LED Blue GPIO_USER, // GPIO14 RGBW LED White (optional - set to PWM4 for Cold White or Warm White) @@ -1039,11 +1325,16 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { // (PwmFrequency 540Hz) GPIO_KEY1, // GPIO00 Optional Button GPIO_USER, // GPIO01 Serial RXD and Optional sensor - GPIO_LED2_INV, // GPIO02 RF receiver control + GPIO_LED4_INV, // GPIO02 RF receiver control GPIO_USER, // GPIO03 Serial TXD and Optional sensor GPIO_PWM2, // GPIO04 RGB LED Green GPIO_PWM1, // GPIO05 RGB LED Red - 0, 0, 0, 0, 0, 0, // Flash connection + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) GPIO_PWM5, // GPIO12 RGBCW LED Warm GPIO_PWM4, // GPIO13 RGBW LED Cold GPIO_PWM3, // GPIO14 RGB LED Blue @@ -1057,12 +1348,14 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, // GPIO03 Serial TXD and Optional sensor 0, GPIO_REL2, // GPIO05 Relay 2 (0 = Off, 1 = On) - 0, 0, 0, // Flash connection + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) GPIO_USER, // GPIO09 Button 1 on header (0 = On, 1 = Off) GPIO_KEY1, // GPIO10 Button on casing - 0, // Flash connection + // GPIO11 (SD_CMD Flash) GPIO_REL1, // GPIO12 Relay 1 (0 = Off, 1 = On) - GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) + GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status 0, 0, 0, 0 }, { "Arilux LC06", // Arilux AL-LC06 (ESP8285) @@ -1073,7 +1366,12 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, // GPIO03 Serial TXD and Optional sensor GPIO_USER, // GPIO04 W2 - PWM5 0, - 0, 0, 0, 0, 0, 0, // Flash connection + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) GPIO_PWM2, // GPIO12 RGB LED Green GPIO_PWM3, // GPIO13 RGB LED Blue GPIO_PWM1, // GPIO14 RGB LED Red @@ -1082,13 +1380,18 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { }, { "Sonoff S31", // Sonoff S31 (ESP8266 - CSE7766) GPIO_KEY1, // GPIO00 Button - 0, // GPIO01 Serial RXD 4800 baud 8E1 CSE7766 energy sensor + GPIO_CSE7766_TX, // GPIO01 Serial RXD 4800 baud 8E1 CSE7766 energy sensor 0, - 0, // GPIO03 Serial TXD + GPIO_CSE7766_RX, // GPIO03 Serial TXD 0, 0, - 0, 0, 0, 0, 0, 0, // Flash connection + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) - GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) + GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) - Link and Power status 0, 0, 0, 0 }, { "Zengge WF017", // Zenggee ZJ-WF017-A (ESP12S)) @@ -1099,7 +1402,12 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { 0, GPIO_USER, // GPIO04 W2 - PWM5 0, - 0, 0, 0, 0, 0, 0, // Flash connection + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) GPIO_PWM2, // GPIO12 RGB LED Green GPIO_PWM1, // GPIO13 RGB LED Red GPIO_PWM3, // GPIO14 RGB LED Blue @@ -1107,13 +1415,18 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { }, { "Sonoff Pow R2", // Sonoff Pow R2 (ESP8285 - CSE7766) GPIO_KEY1, // GPIO00 Button - 0, // GPIO01 Serial RXD 4800 baud 8E1 CSE7766 energy sensor + GPIO_CSE7766_TX, // GPIO01 Serial RXD 4800 baud 8E1 CSE7766 energy sensor 0, - 0, // GPIO03 Serial TXD + GPIO_CSE7766_RX, // GPIO03 Serial TXD 0, 0, - 0, 0, 0, 0, 0, 0, // Flash connection + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) - GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) + GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status 0, 0, 0, 0 }, { "Sonoff iFan02", // Sonoff iFan02 (ESP8285) @@ -1123,12 +1436,14 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, // GPIO03 ESP_RXD Serial TXD and Optional sensor GPIO_REL3, // GPIO04 WIFI_O2 Relay 3 (0 = Off, 1 = On) controlling the fan GPIO_REL2, // GPIO05 WIFI_O1 Relay 2 (0 = Off, 1 = On) controlling the fan - 0, 0, 0, // Flash connection + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) GPIO_KEY2, // GPIO09 WIFI_KEY1 Virtual button 2 as feedback from RC GPIO_KEY3, // GPIO10 WIFI_KEY2 Virtual button 3 as feedback from RC - 0, // Flash connection + // GPIO11 (SD_CMD Flash) GPIO_REL1, // GPIO12 WIFI_O0 Relay 1 (0 = Off, 1 = On) controlling the light - GPIO_LED1_INV, // GPIO13 WIFI_CHK Blue Led on PCA (0 = On, 1 = Off) + GPIO_LED1_INV, // GPIO13 WIFI_CHK Blue Led on PCA (0 = On, 1 = Off) - Link and Power status GPIO_KEY4, // GPIO14 WIFI_KEY3 Virtual button 4 as feedback from RC GPIO_REL4, // GPIO15 WIFI_O3 Relay 4 (0 = Off, 1 = On) controlling the fan 0, 0 @@ -1138,43 +1453,67 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { // https://www.amazon.de/Steckdose-Homecube-intelligente-Verbrauchsanzeige-funktioniert/dp/B076Q2LKHG/ref=sr_1_fkmr0_1 // https://www.amazon.de/Intelligente-Stromverbrauch-Fernsteurung-Schaltbare-Energieklasse/dp/B076WZQS4S/ref=sr_1_1 // https://www.aliexpress.com/store/product/BlitzWolf-BW-SHP6-EU-Plug-Metering-Version-WIFI-Smart-Socket-220V-240V-10A-Work-with-Amazon/1965360_32945504669.html - GPIO_LED2_INV, // GPIO00 Red Led (1 = On, 0 = Off) + GPIO_LED2_INV, // GPIO00 Red Led (1 = On, 0 = Off) - Power status GPIO_USER, // GPIO01 Serial RXD and Optional sensor - GPIO_LED1_INV, // GPIO02 Blue Led (1 = On, 0 = Off) + GPIO_LED1_INV, // GPIO02 Blue Led (1 = On, 0 = Off) - Link status GPIO_USER, // GPIO03 Serial TXD and Optional sensor 0, GPIO_HJL_CF, // GPIO05 BL0937 or HJL-01 CF power - 0, 0, 0, 0, 0, 0, // Flash connection + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) GPIO_NRG_SEL_INV, // GPIO12 BL0937 or HJL-01 Sel output (0 = Voltage) GPIO_KEY1, // GPIO13 Button GPIO_NRG_CF1, // GPIO14 BL0937 or HJL-01 CF1 current / voltage GPIO_REL1, // GPIO15 Relay (0 = Off, 1 = On) 0, 0 }, - { "Shelly 1", // Shelly1 Open Source (ESP8266 - 2MB) - https://shelly.cloud/shelly1-open-source/ - 0, 0, 0, 0, - GPIO_REL1, // GPIO04 Relay (0 = Off, 1 = On) - GPIO_SWT1_NP, // GPIO05 SW pin - 0, 0, 0, 0, 0, 0, // Flash connection + { "Shelly 1", // Shelly1 Open Source (ESP8266 - 2MB) - https://shelly.cloud/shelly1-open-source/ + GPIO_USER, // GPIO00 - Only to be used when Shelly is connected to 12V DC + GPIO_USER, // GPIO01 Serial RXD - Only to be used when Shelly is connected to 12V DC + 0, + GPIO_USER, // GPIO03 Serial TXD - Only to be used when Shelly is connected to 12V DC + GPIO_REL1, // GPIO04 Relay (0 = Off, 1 = On) + GPIO_SWT1_NP, // GPIO05 SW pin + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) 0, 0, 0, 0, 0, 0 }, - { "Shelly 2", // Shelly2 (ESP8266 - 2MB) - https://shelly.cloud/shelly2/ + { "Shelly 2", // Shelly2 (ESP8266 - 2MB) - https://shelly.cloud/shelly2/ 0, - GPIO_TXD, // GPIO01 MCP39F501 Serial input + GPIO_MCP39F5_TX, // GPIO01 MCP39F501 Serial input 0, - GPIO_RXD, // GPIO03 MCP39F501 Serial output - GPIO_REL1, // GPIO04 - GPIO_REL2, // GPIO05 - 0, 0, 0, 0, 0, 0, // Flash connection - GPIO_SWT1, // GPIO12 + GPIO_MCP39F5_RX, // GPIO03 MCP39F501 Serial output + GPIO_REL1, // GPIO04 + GPIO_REL2, // GPIO05 + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_SWT1, // GPIO12 0, - GPIO_SWT2, // GPIO14 - 0, // GPIO15 MCP39F501 Reset - 0, 0 + GPIO_SWT2, // GPIO14 + GPIO_MCP39F5_RST, // GPIO15 MCP39F501 Reset + 0, + 0 }, { "Xiaomi Philips", // Xiaomi Philips bulb (ESP8266) 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) GPIO_PWM2, // GPIO12 cold/warm light 0, 0, GPIO_PWM1, // GPIO15 light intensity @@ -1183,9 +1522,14 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { { "Neo Coolcam", // Neo Coolcam (ESP8266) // https://www.banggood.com/NEO-COOLCAM-WiFi-Mini-Smart-Plug-APP-Remote-Control-Timing-Smart-Socket-EU-Plug-p-1288562.html?cur_warehouse=CN 0, 0, 0, 0, - GPIO_LED1_INV, // GPIO04 Red Led (0 = On, 1 = Off) + GPIO_LED1_INV, // GPIO04 Red Led (0 = On, 1 = Off) - Link and Power status 0, - 0, 0, 0, 0, 0, 0, // Flash connection + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) GPIO_KEY1, // GPIO13 Button 0, 0, 0, 0 @@ -1198,38 +1542,54 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, // GPIO03 Serial TXD and Optional sensor GPIO_KEY1, // GPIO04 Button 1 GPIO_REL2_INV, // GPIO05 Red Led 2 (0 = On, 1 = Off) - 0, 0, 0, 0, 0, 0, // Flash connection + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) GPIO_REL4_INV, // GPIO12 Blue Led 4 (0 = On, 1 = Off) GPIO_KEY4, // GPIO13 Button 4 GPIO_KEY3, // GPIO14 Button 3 GPIO_LED1, // GPIO15 Optional sensor GPIO_REL1_INV, // GPIO16 Green Led 1 (0 = On, 1 = Off) + 0 }, { "OBI Socket", // OBI socket (ESP8266) - https://www.obi.de/hausfunksteuerung/wifi-stecker-schuko/p/2291706 GPIO_USER, // GPIO00 GPIO_USER, // GPIO01 Serial RXD 0, GPIO_USER, // GPIO03 Serial TXD - GPIO_LED1, // GPIO04 Blue LED + GPIO_LED1, // GPIO04 Blue LED - Link and Power status GPIO_REL1, // GPIO05 (Relay OFF, but used as Relay Switch) - 0, 0, 0, 0, 0, 0, // Flash connection - GPIO_LED2, // GPIO12 (Relay ON, but set to LOW, so we can switch with GPIO05) + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_LED3, // GPIO12 (Relay ON, but set to LOW, so we can switch with GPIO05) GPIO_USER, // GPIO13 GPIO_KEY1, // GPIO14 Button 0, GPIO_USER, // GPIO16 - GPIO_ADC0 // ADC0 A0 Analog input + GPIO_FLAG_ADC0 // ADC0 A0 Analog input }, { "Teckin", // https://www.amazon.de/gp/product/B07D5V139R 0, GPIO_KEY1, // GPIO01 Serial TXD and Button 0, - GPIO_LED2_INV, // GPIO03 Serial RXD and Red Led (0 = On, 1 = Off) + GPIO_LED2_INV, // GPIO03 Serial RXD and Red Led (0 = On, 1 = Off) - Power status GPIO_HJL_CF, // GPIO04 BL0937 or HJL-01 CF power GPIO_NRG_CF1, // GPIO05 BL0937 or HJL-01 CF1 current / voltage - 0, 0, 0, 0, 0, 0, // Flash connection + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) GPIO_NRG_SEL_INV, // GPIO12 BL0937 or HJL-01 Sel output (0 = Voltage) - GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) + GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link status GPIO_REL1, // GPIO14 Relay (0 = Off, 1 = On) 0, 0, 0 }, @@ -1239,9 +1599,14 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_KEY1, // GPIO03 Button GPIO_HLW_CF, // GPIO04 HLW8012 CF power GPIO_NRG_CF1, // GPIO05 HLW8012 CF1 current / voltage - 0, 0, 0, 0, 0, 0, // Flash connection + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) GPIO_NRG_SEL_INV, // GPIO12 HLW8012 CF Sel output (0 = Voltage) - GPIO_LED1_INV, // GPIO13 LED (0 = On, 1 = Off) + GPIO_LED1_INV, // GPIO13 LED (0 = On, 1 = Off) - Link and Power status GPIO_REL1, // GPIO14 Relay SRU 5VDC SDA (0 = Off, 1 = On ) 0, 0, 0 }, @@ -1253,7 +1618,12 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, // GPIO03 MCU serial control GPIO_USER, GPIO_USER, - 0, 0, 0, 0, 0, 0, // Flash connection + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) GPIO_USER, GPIO_USER, GPIO_USER, // GPIO14 Green Led @@ -1263,27 +1633,37 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { }, { "Gosund SP1 v23", // https://www.amazon.de/gp/product/B0777BWS1P 0, - GPIO_LED1_INV, // GPIO01 Serial RXD and LED1 (blue) inv + GPIO_LED1_INV, // GPIO01 Serial RXD and LED1 (blue) inv - Link status 0, GPIO_KEY1, // GPIO03 Serial TXD and Button GPIO_HJL_CF, // GPIO04 BL0937 or HJL-01 CF power GPIO_NRG_CF1, // GPIO05 BL0937 or HJL-01 CF1 current / voltage - 0, 0, 0, 0, 0, 0, // Flash connection + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) GPIO_NRG_SEL_INV, // GPIO12 BL0937 or HJL-01 Sel output (0 = Voltage) - GPIO_LED2_INV, // GPIO13 LED2 (red) inv + GPIO_LED2_INV, // GPIO13 LED2 (red) inv - Power status GPIO_REL1, // GPIO14 Relay (0 = Off, 1 = On) 0, 0, 0 }, - { "ARMTR Dimmer", // ARMTRONIX Dimmer, one or two channel (ESP8266 w/ separate MCU dimmer) - // https://www.tindie.com/products/Armtronix/wifi-ac-dimmer-two-triac-board/ - // https://www.tindie.com/products/Armtronix/wifi-ac-dimmer-esp8266-one-triac-board-alexaecho/ + { "ARMTR Dimmer", // ARMTRONIX Dimmer, one or two channel (ESP8266 w/ separate MCU dimmer) + // https://www.tindie.com/products/Armtronix/wifi-ac-dimmer-two-triac-board/ + // https://www.tindie.com/products/Armtronix/wifi-ac-dimmer-esp8266-one-triac-board-alexaecho/ GPIO_USER, - GPIO_TXD, // GPIO01 MCU serial control + GPIO_TXD, // GPIO01 MCU serial control GPIO_USER, - GPIO_RXD, // GPIO03 MCU serial control + GPIO_RXD, // GPIO03 MCU serial control GPIO_USER, GPIO_USER, - 0, 0, 0, 0, 0, 0, // Flash connection + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) GPIO_USER, GPIO_USER, GPIO_USER, @@ -1296,10 +1676,15 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { 0, 0, 0, GPIO_HLW_CF, // GPIO04 HLW8012 CF power GPIO_NRG_CF1, // GPIO05 HLW8012 CF1 current / voltage - 0, 0, 0, 0, 0, 0, // Flash connection + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) GPIO_NRG_SEL_INV, // GPIO12 HLW8012 CF Sel output (0 = Voltage) - GPIO_LED2_INV, // GPIO13 Red Led (0 = On, 1 = Off) - GPIO_LED1_INV, // GPIO14 Blue Led (0 = On, 1 = Off) + GPIO_LED2_INV, // GPIO13 Red Led (0 = On, 1 = Off) - Power status + GPIO_LED1_INV, // GPIO14 Blue Led (0 = On, 1 = Off) - Link status GPIO_REL1, // GPIO15 Relay (0 = Off, 1 = On) 0, 0 }, @@ -1311,9 +1696,14 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_RXD, // GPIO03 MCU serial control GPIO_USER, GPIO_USER, - 0, 0, 0, 0, 0, 0, // Flash connection + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) GPIO_USER, - GPIO_LED1, // GPIO13 WiFi LED + GPIO_LED1, // GPIO13 WiFi LED - Link and Power status GPIO_USER, GPIO_USER, GPIO_USER, @@ -1322,13 +1712,18 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { { "Teckin US", // Teckin SP20 US with Energy Monitoring // https://www.amazon.com/Outlet-Compatible-Monitoring-Function-Required/dp/B079Q5W22B // https://www.amazon.com/Outlet-ZOOZEE-Monitoring-Function-Compatible/dp/B07J2LR5KN - GPIO_LED2_INV, // GPIO00 Red Led (1 = On, 0 = Off) + GPIO_LED2_INV, // GPIO00 Red Led (1 = On, 0 = Off) - Power status 0, - GPIO_LED1_INV, // GPIO02 Blue Led (1 = On, 0 = Off) + GPIO_LED1_INV, // GPIO02 Blue Led (1 = On, 0 = Off) - Link status 0, GPIO_REL1, // GPIO04 Relay (0 = Off, 1 = On) GPIO_HJL_CF, // GPIO05 BL0937 or HJL-01 CF power - 0, 0, 0, 0, 0, 0, // Flash connection + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) GPIO_NRG_SEL_INV, // GPIO12 BL0937 or HJL-01 Sel output (0 = Voltage) GPIO_KEY1, // GPIO13 Button GPIO_NRG_CF1, // GPIO14 BL0937 or HJL-01 CF1 current / voltage @@ -1343,33 +1738,274 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_KEY1, // GPIO03 Serial TXD + Button GPIO_REL2, // GPIO04 Relay 2 GPIO_REL1, // GPIO05 Relay 1 - 0, 0, 0, 0, 0, 0, // Flash connection + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) GPIO_REL3, // GPIO12 Relay 3 GPIO_REL4, // GPIO13 Relay 4 GPIO_USER, // GPIO14 0, GPIO_USER, // GPIO16 0 + }, + { "OBI Socket 2", // OBI socket (ESP8266) - https://www.obi.de/hausfunksteuerung/wifi-stecker-schuko-2-stueck-weiss/p/4077673 + 0, // GPIO00 + 0, // GPIO01 Serial RXD + 0, + 0, // GPIO03 Serial TXD + GPIO_REL1, // GPIO04 Relay 1 + GPIO_KEY1, // GPIO05 Button + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_LED1_INV, // GPIO12 Green LED - Link status + GPIO_LED2, // GPIO13 Red LED - Power status + 0, 0, 0, 0 + }, + { "YTF IR Bridge", // https://www.aliexpress.com/item/Tuya-universal-Smart-IR-Hub-remote-control-Voice-Control-AC-TV-Work-With-Alexa-Google-Home/32951202513.html + GPIO_USER, // GPIO00 + GPIO_USER, // GPIO01 Serial RXD + GPIO_USER, // GPIO02 + GPIO_USER, // GPIO03 Serial TXD + GPIO_LED1_INV, // GPIO04 Blue Led - Link status + GPIO_IRRECV, // GPIO05 IR Receiver + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + 0, // GPIO12 + GPIO_KEY1, // GPIO13 Button + GPIO_IRSEND, // GPIO14 IR Transmitter + 0, 0, 0 + }, + { "Digoo DG-SP202", // Digoo DG-SP202 + // https://www.banggood.com/DIGOO-DG-SP202-Dual-EU-Plug-Smart-WIFI-Socket-Individual-Controllable-Energy-Monitor-Remote-Control-Timing-Smart-Home-Outlet-let-p-1375323.html + GPIO_KEY1, // GPIO00 Button1 + 0, // GPIO01 Serial RXD + 0, // GPIO02 + 0, // GPIO03 Serial TXD + GPIO_HJL_CF, // GPIO04 BL0937 or HJL-01 CF power + GPIO_NRG_CF1, // GPIO05 BL0937 or HJL-01 CF1 current / voltage + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_NRG_SEL_INV, // GPIO12 BL0937 or HJL-01 Sel output (0 = Voltage) + GPIO_LED1, // GPIO13 Blue Leds - Link Status + GPIO_REL2, // GPIO14 Relay2 (0 = Off, 1 = On) and Red Led + GPIO_REL1, // GPIO15 Relay1 (0 = Off, 1 = On) and Red Led + GPIO_KEY2_NP, // GPIO16 Button2, externally pulled up + 0 + }, + { "KA10", // SMANERGY KA10 (ESP8285 - BL0937 Energy Monitoring) - https://www.amazon.es/dp/B07MBTCH2Y + 0, // GPIO00 + GPIO_LED1_INV, // GPIO01 Blue LED - Link status + 0, // GPIO02 + GPIO_KEY1, // GPIO03 Button + GPIO_HJL_CF, // GPIO04 BL0937 CF power + GPIO_NRG_CF1, // GPIO05 BL0937 CF1 voltage / current + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_NRG_SEL_INV, // GPIO12 BL0937 Sel output (1 = Voltage) + GPIO_LED2, // GPIO13 Red LED - Power status + GPIO_REL1, // GPIO14 Relay 1 + 0, 0, 0 + }, + { "Luminea ZX2820", + GPIO_KEY1, // GPIO00 Button + 0, 0, 0, + GPIO_HLW_CF, // GPIO04 HLW8012 CF power + GPIO_NRG_CF1, // GPIO05 HLW8012 CF1 voltage / current + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_NRG_SEL_INV, // GPIO12 HLW8012 SEL (0 = Voltage) + GPIO_LED1_INV, // GPIO13 Green Led - Link and Power status + GPIO_REL1, // GPIO14 Relay + 0, 0, 0 + }, + { "Mi Desk Lamp", // Mi LED Desk Lamp - https://www.mi.com/global/smartlamp/ + 0, 0, + GPIO_KEY1, // GPIO02 Button + 0, + GPIO_PWM1, // GPIO04 Cold White + GPIO_PWM2, // GPIO05 Warm White + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_ROT1A, // GPIO12 Rotary switch A pin + GPIO_ROT1B, // GPIO13 Rotary switch B pin + 0, 0, 0, 0 + }, + { "SP10", // Tuya SP10 (BL0937 Energy Monitoring) + // https://www.aliexpress.com/item/Smart-Mini-WiFi-Plug-Outlet-Switch-Work-With-ForEcho-Alexa-Google-Home-Remote-EU-Smart-Socket/32963670423.html + 0, // GPIO00 + GPIO_PWM1, // GPIO01 Nightlight + 0, // GPIO02 + GPIO_KEY1, // GPIO03 Button + GPIO_HJL_CF, // GPIO04 BL0937 CF power + GPIO_NRG_CF1, // GPIO05 BL0937 CF1 voltage / current + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_NRG_SEL_INV, // GPIO12 BL0937 Sel output (1 = Voltage) + GPIO_LED1, // GPIO13 Blue LED - Link status + GPIO_REL1, // GPIO14 Relay and red LED + 0, 0, 0 + }, + { "WAGA CHCZ02MB", // WAGA life CHCZ02MB (HJL-01 Energy Monitoring) + // https://www.ebay.com/itm/332595697006 + GPIO_LED2_INV, // GPIO00 Red LED + 0, // GPIO01 Serial RXD + 0, // GPIO02 + GPIO_NRG_SEL_INV, // GPIO03 HJL-01 Sel output (1 = Voltage) + 0, // GPIO04 + GPIO_HJL_CF, // GPIO05 HJL-01 CF power + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_REL1, // GPIO12 Relay + GPIO_KEY1, // GPIO13 Button + GPIO_NRG_CF1, // GPIO14 HJL-01 CF1 voltage / current + GPIO_LED1_INV, // GPIO15 Blue LED - Link status + 0, 0 + }, + { "SYF05", // Sunyesmart SYF05 (a.k.a. Fcmila) = TYWE3S + SM16726 + // Also works with Merkury 904 RGBW Bulbs with 13 set to GPIO_SM16716_SEL + // https://www.flipkart.com/fc-mila-bxav-xs-ad-smart-bulb/p/itmf85zgs45fzr7n + // https://docs.tuya.com/en/hardware/WiFi-module/wifi-e3s-module.html + // http://www.datasheet-pdf.com/PDF/SM16716-Datasheet-Sunmoon-932771 + GPIO_USER, // GPIO00 N.C. + 0, // GPIO01 Serial RXD + GPIO_USER, // GPIO02 N.C. + 0, // GPIO03 Serial TXD + GPIO_SM16716_CLK, // GPIO04 SM16716 Clock + GPIO_PWM1, // GPIO05 White + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_USER, // GPIO12 Alt. White on some devices + GPIO_USER, // GPIO13 SM16716 Enable on some devices + GPIO_SM16716_DAT, // GPIO14 SM16716 Data + 0, // GPIO15 wired to GND + GPIO_USER, // GPIO16 N.C. + GPIO_FLAG_ADC0 // ADC0 A0 Analog input } }; /* Optionals + { "RGB Smart Plug", // Tuya based smart plug with power monitoring and RGB light + // https://www.aliexpress.com/item/ET-Smart-Plug-Wifi-Socket-With-Switch-Phone-APP-Voice-Remote-Control-Monitor-Smart-Timing-Switch/32964036349.html?spm=a2g0s.9042311.0.0.439c4c4d4N8N2Q + // https://xiangshangcn.en.alibaba.com/product/60844251239-807590977/RGB_wifi_smart_plug_smart_socket_smart_outlet_EU_works_with_Amazon_alexa_google_home_mobile_app_tuya_solution_smart_life.html?spm=a2700.icbuShop.41413.24.4e996767oFAAmO + GPIO_PWM1, // GPIO00 Red + GPIO_USER, // GPIO01 Serial RXD and Optional sensor + GPIO_PWM3, // GPIO02 Blue + GPIO_USER, // GPIO03 Serial TXD and Optional sensor + GPIO_PWM2, // GPIO04 Green + GPIO_HJL_CF, // GPIO05 BL0937 or HJL-01 CF power + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_NRG_SEL_INV, // GPIO12 BL0937 or HJL-01 Sel output (0 = Voltage) + GPIO_KEY1, // GPIO13 Button + GPIO_NRG_CF1, // GPIO14 BL0937 or HJL-01 CF1 current / voltage + GPIO_REL1, // GPIO15 Relay (0 = Off, 1 = On) + 0, 0 + } + + { "ESP RGBWWC", // esp rgbww controller https://github.com/pljakobs/esp_rgbww_controller/tree/v2.3 + GPIO_KEY1, // GPIO00 Button + GPIO_USER, // GPIO01 Serial RXD and Optional sensor + 0, // GPIO02 + GPIO_USER, // GPIO03 Serial TXD and Optional sensor + GPIO_PWM5, // GPIO04 LED Warm White + GPIO_PWM4, // GPIO05 LED Cold White + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_PWM2, // GPIO12 LED Green + GPIO_PWM1, // GPIO13 LED Red + GPIO_PWM3, // GPIO14 LED Blue + 0, // GPIO15 + GPIO_KEY2, // GPIO16 Button + 0 + } + + { "N0DY Relay", // N0DY Wifi Dual Relay (ESP-07) + // https://www.n0dy.com/product/web-controlled-dual-relay/ + // https://www.amazon.com/dp/B072MKV8ZM + GPIO_KEY1, // GPIO00 PROG Button + GPIO_USER, // GPIO01 Serial RXD or Optional sensor on J2 RXD (if not using serial io) + 0, // GPIO02 + GPIO_USER, // GPIO03 Serial TXD or Optional sensor on J2 TXD (if not using serial io) + GPIO_REL2_INV, // GPIO04 Relay 2 (active low) + GPIO_REL1_INV, // GPIO05 Relay 1 (active low) + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + 0, 0, 0, 0, 0, 0 + } + { "MagicHome", // Magic Home (aka Flux-light) (ESP8266) // https://www.aliexpress.com/item/Magic-Home-Mini-RGB-RGBW-Wifi-Controller-For-Led-Strip-Panel-light-Timing-Function-16million-colors/32686853650.html 0, GPIO_USER, // GPIO01 Serial RXD and Optional sensor - GPIO_LED1_INV, // GPIO02 Blue onboard LED + GPIO_LED1_INV, // GPIO02 Blue onboard LED - Link and Power status GPIO_USER, // GPIO03 Serial TXD and Optional sensor GPIO_USER, // GPIO04 IR receiver (optional) GPIO_PWM2, // GPIO05 RGB LED Green - 0, 0, 0, 0, 0, 0, // Flash connection + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) GPIO_PWM3, // GPIO12 RGB LED Blue GPIO_USER, // GPIO13 RGBW LED White (optional - set to PWM4 for Cold White or Warm White) GPIO_PWM1, // GPIO14 RGB LED Red 0, 0, 0 - }, + } + { "Arilux LC10", // Arilux LC10 (ESP8285), RGBW + RF // https://github.com/arendst/Sonoff-Tasmota/wiki/MagicHome-with-ESP8285 // https://www.aliexpress.com/item/DC5-24V-Wireless-WIFI-LED-RGB-Controller-RGBW-Controller-IR-RF-Remote-Control-IOS-Android-for/32827253255.html @@ -1380,11 +2016,16 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, // GPIO03 Serial TXD and Optional sensor0 GPIO_ARIRFRCV, // GPIO04 RF receiver input GPIO_PWM2, // GPIO05 RGB LED Green - 0, 0, 0, 0, 0, 0, // Flash connection + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) GPIO_PWM3, // GPIO12 RGB LED Blue GPIO_PWM4, // GPIO13 RGBW LED White GPIO_PWM1, // GPIO14 RGB LED Red - GPIO_LED2_INV, // GPIO15 RF receiver control + GPIO_LED4_INV, // GPIO15 RF receiver control 0, 0 } @@ -1393,7 +2034,12 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_KEY2, // GPIO03 Serial TXD and Optional sensor GPIO_REL2, // GPIO04 Relay 2 GPIO_KEY3, // GPIO05 Input 2 - 0, 0, 0, 0, 0, 0, // Flash connection + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) GPIO_KEY1, // GPIO12 Key input GPIO_REL1, // GPIO13 Relay 1 0, @@ -1405,18 +2051,28 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { 0, 0, 0, 0, GPIO_REL1, // GPIO04 Relay (0 = Off, 1 = On) 0, - 0, 0, 0, 0, 0, 0, // Flash connection + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) GPIO_KEY1, // GPIO12 Button 0, 0, - GPIO_LED1, // GPIO15 Led (1 = On, 0 = Off) + GPIO_LED1, // GPIO15 Led (1 = On, 0 = Off) - Link and Power status 0, 0 } { "SMPW701E", // SM-PW701E WLAN Socket (#1190) 0, 0, 0, 0, - GPIO_LED1_INV, // GPIO04 Blue Led (0 = On, 1 = Off) + GPIO_LED1_INV, // GPIO04 Blue Led (0 = On, 1 = Off) - Link and Power status 0, // GPIO05 IR or RF receiver (optional) - 0, 0, 0, 0, 0, 0, // Flash connection + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) GPIO_REL1, // GPIO12 Relay and Red Led (0 = Off, 1 = On) GPIO_KEY1, // GPIO13 Button 0, 0, 0, 0 @@ -1427,9 +2083,14 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, // GPIO01 0, GPIO_USER, // GPIO03 - GPIO_LED1_INV, // GPIO04 Blue LED + GPIO_LED1_INV, // GPIO04 Blue LED - Link and Power status GPIO_REL1, // GPIO05 Red LED and relay - 0, 0, 0, 0, 0, 0, // Flash connection + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) 0, GPIO_KEY1, // GPIO13 Button (normally GPIO00) GPIO_USER, // GPIO14 @@ -1438,11 +2099,16 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { { "MagicHome v2.3", // Magic Home (aka Flux-light) (ESP8266) (#1353) 0, 0, - GPIO_LED1_INV, // GPIO02 Blue onboard LED + GPIO_LED1_INV, // GPIO02 Blue onboard LED - Link and Power status 0, GPIO_USER, // GPIO04 IR receiver (optional) GPIO_PWM2, // GPIO05 RGB LED Green - 0, 0, 0, 0, 0, 0, // Flash connection + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) GPIO_PWM1, // GPIO12 RGB LED Red GPIO_PWM3, // GPIO13 RGB LED Blue 0, @@ -1457,7 +2123,12 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, // GPIO03 (D8) Serial TXD GPIO_USER, // GPIO04 (D4) 4 x WS2812 Leds, (DOUT) Extents WS2812 string GPIO_USER, // GPIO05 (D5) Blue Led - 0, 0, 0, 0, 0, 0, // Flash connection + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) GPIO_USER, // GPIO12 (D12) GPIO_USER, // GPIO13 (D13) GPIO_USER, // GPIO14 (D14) @@ -1469,9 +2140,14 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { { "Delock 11826", // Delock 11826 (ESP8285) = Sonoff Basic GPIO_KEY1, // GPIO00 Button 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, // Flash connection + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) - GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) + GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) - Link and Power status 0, 0, 0, 0 } diff --git a/sonoff/sonoff_version.h b/sonoff/sonoff_version.h index 6e635a341..0e9c322ac 100644 --- a/sonoff/sonoff_version.h +++ b/sonoff/sonoff_version.h @@ -1,7 +1,7 @@ /* sonoff_version.h - Version header file for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends + Copyright (C) 2019 Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -20,7 +20,7 @@ #ifndef _SONOFF_VERSION_H_ #define _SONOFF_VERSION_H_ -#define VERSION 0x06040100 +#define VERSION 0x06050000 #define D_PROGRAMNAME "Sonoff-Tasmota" #define D_AUTHOR "Theo Arends" diff --git a/sonoff/support.ino b/sonoff/support.ino index 5c80651e9..fc2a3d853 100644 --- a/sonoff/support.ino +++ b/sonoff/support.ino @@ -1,7 +1,7 @@ /* support.ino - support for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends + Copyright (C) 2019 Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -31,7 +31,7 @@ Ticker tickerOSWatch; #define OSWATCH_RESET_TIME 120 static unsigned long oswatch_last_loop_time; -byte oswatch_blocked_loop = 0; +uint8_t oswatch_blocked_loop = 0; #ifndef USE_WS2812_DMA // Collides with Neopixelbus but solves exception //void OsWatchTicker() ICACHE_RAM_ATTR; @@ -47,8 +47,7 @@ void OsWatchTicker(void) unsigned long last_run = abs(t - oswatch_last_loop_time); #ifdef DEBUG_THEO - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_APPLICATION D_OSWATCH " FreeRam %d, rssi %d, last_run %d"), ESP.getFreeHeap(), WifiGetRssiAsQuality(WiFi.RSSI()), last_run); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_OSWATCH " FreeRam %d, rssi %d, last_run %d"), ESP.getFreeHeap(), WifiGetRssiAsQuality(WiFi.RSSI()), last_run); #endif // DEBUG_THEO if (last_run >= (OSWATCH_RESET_TIME * 1000)) { // AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_OSWATCH " " D_BLOCKED_LOOP ". " D_RESTARTING)); // Save iram space @@ -84,7 +83,7 @@ String GetResetReason(void) } } -boolean OsWatchBlockedLoop(void) +bool OsWatchBlockedLoop(void) { return oswatch_blocked_loop; } @@ -140,7 +139,7 @@ size_t strchrspn(const char *str1, int character) char* subStr(char* dest, char* str, const char *delim, int index) { char *act; - char *sub; + char *sub = NULL; char *ptr; int i; @@ -154,34 +153,39 @@ char* subStr(char* dest, char* str, const char *delim, int index) return sub; } -double CharToDouble(char *str) +double CharToDouble(const char *str) { // simple ascii to double, because atof or strtod are too large char strbuf[24]; strlcpy(strbuf, str, sizeof(strbuf)); - char *pt; - double left = atoi(strbuf); + char *pt = strbuf; + while ((*pt != '\0') && isblank(*pt)) { pt++; } // Trim leading spaces + + signed char sign = 1; + if (*pt == '-') { sign = -1; } + if (*pt == '-' || *pt=='+') { pt++; } // Skip any sign + + double left = 0; + if (*pt != '.') { + left = atoi(pt); // Get left part + while (isdigit(*pt)) { pt++; } // Skip number + } + double right = 0; - short len = 0; - pt = strtok (strbuf, "."); - if (pt) { - pt = strtok (NULL, "."); - if (pt) { - right = atoi(pt); - len = strlen(pt); - double fac = 1; - while (len) { - fac /= 10.0; - len--; - } - // pow is also very large - //double fac=pow(10,-len); - right *= fac; + if (*pt == '.') { + pt++; + right = atoi(pt); // Decimal part + while (isdigit(*pt)) { + pt++; + right /= 10.0; } } + double result = left + right; - if (left < 0) { result = left - right; } + if (sign < 0) { + return -result; // Add negative sign + } return result; } @@ -210,10 +214,12 @@ char* Unescape(char* buffer, uint16_t* size) { uint8_t* read = (uint8_t*)buffer; uint8_t* write = (uint8_t*)buffer; - uint16_t start_size = *size; - uint16_t end_size = *size; + int16_t start_size = *size; + int16_t end_size = *size; uint8_t che = 0; +// AddLogBuffer(LOG_LEVEL_DEBUG, (uint8_t*)buffer, *size); + while (start_size > 0) { uint8_t ch = *read++; start_size--; @@ -235,6 +241,14 @@ char* Unescape(char* buffer, uint16_t* size) case 's': che = ' '; break; // 20 Space case 't': che = '\t'; break; // 09 Horizontal tab case 'v': che = '\v'; break; // 0B Vertical tab + case 'x': { + uint8_t* start = read; + che = (uint8_t)strtol((const char*)read, (char**)&read, 16); + start_size -= (uint16_t)(read - start); + end_size -= (uint16_t)(read - start); + break; + } + case '"': che = '\"'; break; // 22 Quotation mark // case '?': che = '\?'; break; // 3F Question mark default : { che = chi; @@ -247,6 +261,9 @@ char* Unescape(char* buffer, uint16_t* size) } } *size = end_size; + +// AddLogBuffer(LOG_LEVEL_DEBUG, (uint8_t*)buffer, *size); + return buffer; } @@ -262,7 +279,7 @@ char* RemoveSpace(char* p) *write++ = ch; } } - *write = '\0'; +// *write = '\0'; // Removed 20190223 as it buffer overflows on no isspace found - no need either return p; } @@ -292,27 +309,6 @@ char* UpperCase_P(char* dest, const char* source) return dest; } -/* -char* LTrim(char* p) -{ - while ((*p != '\0') && (isblank(*p))) { - p++; // Trim leading spaces - } - return p; -} - -char* RTrim(char* p) -{ - char* q = p + strlen(p) -1; - while ((q >= p) && (isblank(*q))) { - q--; // Trim trailing spaces - } - q++; - *q = '\0'; - return p; -} -*/ - char* Trim(char* p) { while ((*p != '\0') && isblank(*p)) { p++; } // Trim leading spaces @@ -361,10 +357,10 @@ uint8_t Shortcut(const char* str) return result; } -boolean ParseIp(uint32_t* addr, const char* str) +bool ParseIp(uint32_t* addr, const char* str) { uint8_t *part = (uint8_t*)addr; - byte i; + uint8_t i; *addr = 0; for (i = 0; i < 4; i++) { @@ -378,7 +374,7 @@ boolean ParseIp(uint32_t* addr, const char* str) return (3 == i); } -void MakeValidMqtt(byte option, char* str) +void MakeValidMqtt(uint8_t option, char* str) { // option 0 = replace by underscore // option 1 = delete character @@ -643,7 +639,160 @@ int GetStateNumber(char *state_text) return state_number; } -boolean GetUsedInModule(byte val, uint8_t *arr) +void SetSerialBaudrate(int baudrate) +{ + Settings.baudrate = baudrate / 1200; + if (Serial.baudRate() != baudrate) { + if (seriallog_level) { + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_SET_BAUDRATE_TO " %d"), baudrate); + } + delay(100); + Serial.flush(); + Serial.begin(baudrate, serial_config); + delay(10); + Serial.println(); + } +} + +void ClaimSerial(void) +{ + serial_local = true; + AddLog_P(LOG_LEVEL_INFO, PSTR("SNS: Hardware Serial")); + SetSeriallog(LOG_LEVEL_NONE); + baudrate = Serial.baudRate(); + Settings.baudrate = baudrate / 1200; +} + +void SerialSendRaw(char *codes) +{ + char *p; + char stemp[3]; + uint8_t code; + + int size = strlen(codes); + + while (size > 0) { + strlcpy(stemp, codes, sizeof(stemp)); + code = strtol(stemp, &p, 16); + Serial.write(code); + size -= 2; + codes += 2; + } +} + +uint32_t GetHash(const char *buffer, size_t size) +{ + uint32_t hash = 0; + for (uint16_t i = 0; i <= size; i++) { + hash += (uint8_t)*buffer++ * (i +1); + } + return hash; +} + +void ShowSource(int source) +{ + if ((source > 0) && (source < SRC_MAX)) { + char stemp1[20]; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SRC: %s"), GetTextIndexed(stemp1, sizeof(stemp1), source, kCommandSource)); + } +} + +/*********************************************************************************************\ + * GPIO Module and Template management +\*********************************************************************************************/ + +uint8_t ModuleNr() +{ + // 0 = User module (255) + // 1 up = Template module 0 up + return (USER_MODULE == Settings.module) ? 0 : Settings.module +1; +} + +String AnyModuleName(uint8_t index) +{ + if (USER_MODULE == index) { + return String(Settings.user_template.name); + } else { + return FPSTR(kModules[index].name); + } +} + +String ModuleName() +{ + return AnyModuleName(Settings.module); +} + +void ModuleGpios(myio *gp) +{ + uint8_t *dest = (uint8_t *)gp; + memset(dest, GPIO_NONE, sizeof(myio)); + + uint8_t src[sizeof(mycfgio)]; + if (USER_MODULE == Settings.module) { + memcpy(&src, &Settings.user_template.gp, sizeof(mycfgio)); + } else { + memcpy_P(&src, &kModules[Settings.module].gp, sizeof(mycfgio)); + } + // 11 85 00 85 85 00 00 00 15 38 85 00 00 81 + +// AddLogBuffer(LOG_LEVEL_DEBUG, (uint8_t *)&src, sizeof(mycfgio)); + + uint8_t j = 0; + for (uint8_t i = 0; i < sizeof(mycfgio); i++) { + if (6 == i) { j = 9; } + if (8 == i) { j = 12; } + dest[j] = src[i]; + j++; + } + // 11 85 00 85 85 00 00 00 00 00 00 00 15 38 85 00 00 81 + +// AddLogBuffer(LOG_LEVEL_DEBUG, (uint8_t *)gp, sizeof(myio)); +} + +gpio_flag ModuleFlag() +{ + gpio_flag flag; + + if (USER_MODULE == Settings.module) { + flag = Settings.user_template.flag; + } else { + memcpy_P(&flag, &kModules[Settings.module].flag, sizeof(gpio_flag)); + } + + return flag; +} + +void ModuleDefault(uint8_t module) +{ + if (USER_MODULE == module) { module = WEMOS; } // Generic + Settings.user_template_base = module; + memcpy_P(&Settings.user_template, &kModules[module], sizeof(mytmplt)); +} + +void SetModuleType() +{ + my_module_type = (USER_MODULE == Settings.module) ? Settings.user_template_base : Settings.module; +} + +uint8_t ValidPin(uint8_t pin, uint8_t gpio) +{ + uint8_t result = gpio; + + if (((pin > 5) && (pin < 9)) || (11 == pin)) { + result = GPIO_NONE; // Disable flash pins GPIO6, GPIO7, GPIO8 and GPIO11 + } + if ((WEMOS == Settings.module) && (!Settings.flag3.user_esp8285_enable)) { + if ((pin == 9) || (pin == 10)) { result = GPIO_NONE; } // Disable possible flash GPIO9 and GPIO10 + } + return result; +} + +bool ValidGPIO(uint8_t pin, uint8_t gpio) +{ + return (GPIO_USER == ValidPin(pin, gpio)); // Only allow GPIO_USER pins +} + +bool GetUsedInModule(uint8_t val, uint8_t *arr) { int offset = 0; @@ -655,6 +804,12 @@ boolean GetUsedInModule(byte val, uint8_t *arr) if ((val >= GPIO_KEY1_NP) && (val < GPIO_KEY1_NP + MAX_KEYS)) { offset = -(GPIO_KEY1_NP - GPIO_KEY1); } + if ((val >= GPIO_KEY1_INV) && (val < GPIO_KEY1_INV + MAX_KEYS)) { + offset = -(GPIO_KEY1_INV - GPIO_KEY1); + } + if ((val >= GPIO_KEY1_INV_NP) && (val < GPIO_KEY1_INV_NP + MAX_KEYS)) { + offset = -(GPIO_KEY1_INV_NP - GPIO_KEY1); + } if ((val >= GPIO_SWT1) && (val < GPIO_SWT1 + MAX_SWITCHES)) { offset = (GPIO_SWT1_NP - GPIO_SWT1); @@ -691,80 +846,49 @@ boolean GetUsedInModule(byte val, uint8_t *arr) offset = -(GPIO_CNTR1_NP - GPIO_CNTR1); } - for (byte i = 0; i < MAX_GPIO_PIN; i++) { + for (uint8_t i = 0; i < MAX_GPIO_PIN; i++) { if (arr[i] == val) { return true; } if (arr[i] == val + offset) { return true; } } return false; } -void SetSerialBaudrate(int baudrate) +bool JsonTemplate(const char* dataBuf) { - Settings.baudrate = baudrate / 1200; - if (Serial.baudRate() != baudrate) { - if (seriallog_level) { - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_APPLICATION D_SET_BAUDRATE_TO " %d"), baudrate); - AddLog(LOG_LEVEL_INFO); + StaticJsonBuffer<350> jb; // 331 from https://arduinojson.org/v5/assistant/ + JsonObject& obj = jb.parseObject(dataBuf); + if (!obj.success()) { return false; } + + // All parameters are optional allowing for partial changes + const char* name = obj[D_JSON_NAME]; + if (name != nullptr) { + strlcpy(Settings.user_template.name, name, sizeof(Settings.user_template.name)); + } + if (obj[D_JSON_GPIO].success()) { + for (uint8_t i = 0; i < sizeof(mycfgio); i++) { + Settings.user_template.gp.io[i] = obj[D_JSON_GPIO][i] | 0; } - delay(100); - Serial.flush(); - Serial.begin(baudrate, serial_config); - delay(10); - Serial.println(); } + if (obj[D_JSON_FLAG].success()) { + uint8_t flag = obj[D_JSON_FLAG] | 0; + memcpy(&Settings.user_template.flag, &flag, sizeof(gpio_flag)); + } + if (obj[D_JSON_BASE].success()) { + uint8_t base = obj[D_JSON_BASE]; + if ((0 == base) || (base >= MAXMODULE)) { base = 17; } else { base--; } + Settings.user_template_base = base; // Default WEMOS + } + return true; } -void ClaimSerial(void) +void TemplateJson() { - serial_local = 1; - AddLog_P(LOG_LEVEL_INFO, PSTR("SNS: Hardware Serial")); - SetSeriallog(LOG_LEVEL_NONE); - baudrate = Serial.baudRate(); - Settings.baudrate = baudrate / 1200; -} - -void SerialSendRaw(char *codes) -{ - char *p; - char stemp[3]; - uint8_t code; - - int size = strlen(codes); - - while (size > 0) { - snprintf(stemp, sizeof(stemp), codes); - code = strtol(stemp, &p, 16); - Serial.write(code); - size -= 2; - codes += 2; + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_NAME "\":\"%s\",\"" D_JSON_GPIO "\":["), Settings.user_template.name); + for (uint8_t i = 0; i < sizeof(Settings.user_template.gp); i++) { + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s%s%d"), mqtt_data, (i>0)?",":"", Settings.user_template.gp.io[i]); } -} - -uint32_t GetHash(const char *buffer, size_t size) -{ - uint32_t hash = 0; - for (uint16_t i = 0; i <= size; i++) { - hash += (uint8_t)*buffer++ * (i +1); - } - return hash; -} - -void ShowSource(int source) -{ - if ((source > 0) && (source < SRC_MAX)) { - char stemp1[20]; - snprintf_P(log_data, sizeof(log_data), PSTR("SRC: %s"), GetTextIndexed(stemp1, sizeof(stemp1), source, kCommandSource)); - AddLog(LOG_LEVEL_DEBUG); - } -} - -uint8_t ValidGPIO(uint8_t pin, uint8_t gpio) -{ - uint8_t result = gpio; - if ((WEMOS == Settings.module) && (!Settings.flag3.user_esp8285_enable)) { - if ((pin == 9) || (pin == 10)) { result = GPIO_NONE; } // Disable possible flash GPIO9 and GPIO10 - } - return result; + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s],\"" D_JSON_FLAG "\":%d,\"" D_JSON_BASE "\":%d}"), + mqtt_data, Settings.user_template.flag, Settings.user_template_base +1); } /*********************************************************************************************\ @@ -841,7 +965,7 @@ uint32_t i2c_buffer = 0; bool I2cValidRead(uint8_t addr, uint8_t reg, uint8_t size) { - byte x = I2C_RETRY_COUNTER; + uint8_t x = I2C_RETRY_COUNTER; i2c_buffer = 0; do { @@ -850,7 +974,7 @@ bool I2cValidRead(uint8_t addr, uint8_t reg, uint8_t size) if (0 == Wire.endTransmission(false)) { // Try to become I2C Master, send data and collect bytes, keep master status for next request... Wire.requestFrom((int)addr, (int)size); // send data n-bytes read if (Wire.available() == size) { - for (byte i = 0; i < size; i++) { + for (uint8_t i = 0; i < size; i++) { i2c_buffer = i2c_buffer << 8 | Wire.read(); // receive DATA } } @@ -942,7 +1066,7 @@ int32_t I2cRead24(uint8_t addr, uint8_t reg) bool I2cWrite(uint8_t addr, uint8_t reg, uint32_t val, uint8_t size) { - byte x = I2C_RETRY_COUNTER; + uint8_t x = I2C_RETRY_COUNTER; do { Wire.beginTransmission((uint8_t)addr); // start transmission to device @@ -971,7 +1095,7 @@ int8_t I2cReadBuffer(uint8_t addr, uint8_t reg, uint8_t *reg_data, uint16_t len) Wire.beginTransmission((uint8_t)addr); Wire.write((uint8_t)reg); Wire.endTransmission(); - if (len != Wire.requestFrom((uint8_t)addr, (byte)len)) { + if (len != Wire.requestFrom((uint8_t)addr, (uint8_t)len)) { return 1; } while (len--) { @@ -1002,9 +1126,9 @@ void I2cScan(char *devs, unsigned int devs_len) // I2C_SDA_HELD_LOW 3 = I2C bus error. SDA line held low by slave/another_master after n bits // I2C_SDA_HELD_LOW_AFTER_INIT 4 = line busy. SDA again held low by another device. 2nd master? - byte error = 0; - byte address = 0; - byte any = 0; + uint8_t error = 0; + uint8_t address = 0; + uint8_t any = 0; snprintf_P(devs, devs_len, PSTR("{\"" D_CMND_I2CSCAN "\":\"" D_JSON_I2CSCAN_DEVICES_FOUND_AT)); for (address = 1; address <= 127; address++) { @@ -1028,9 +1152,9 @@ void I2cScan(char *devs, unsigned int devs_len) } } -boolean I2cDevice(byte addr) +bool I2cDevice(uint8_t addr) { - for (byte address = 1; address <= 127; address++) { + for (uint8_t address = 1; address <= 127; address++) { Wire.beginTransmission(address); if (!Wire.endTransmission() && (address == addr)) { return true; @@ -1044,12 +1168,11 @@ boolean I2cDevice(byte addr) * Syslog * * Example: - * snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_LOG "Any value %d"), value); - * AddLog(LOG_LEVEL_DEBUG); + * AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_LOG "Any value %d"), value); * \*********************************************************************************************/ -void SetSeriallog(byte loglevel) +void SetSeriallog(uint8_t loglevel) { Settings.seriallog_level = loglevel; seriallog_level = loglevel; @@ -1057,7 +1180,7 @@ void SetSeriallog(byte loglevel) } #ifdef USE_WEBSERVER -void GetLog(byte idx, char** entry_pp, size_t* len_p) +void GetLog(uint8_t idx, char** entry_pp, size_t* len_p) { char* entry_p = NULL; size_t len = 0; @@ -1065,7 +1188,7 @@ void GetLog(byte idx, char** entry_pp, size_t* len_p) if (idx) { char* it = web_log; do { - byte cur_idx = *it; + uint8_t cur_idx = *it; it++; size_t tmp = strchrspn(it, '\1'); tmp++; // Skip terminating '\1' @@ -1101,12 +1224,11 @@ void Syslog(void) } else { syslog_level = 0; syslog_timer = SYSLOG_TIMER; - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_APPLICATION D_SYSLOG_HOST_NOT_FOUND ". " D_RETRY_IN " %d " D_UNIT_SECOND), SYSLOG_TIMER); - AddLog(LOG_LEVEL_INFO); + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_SYSLOG_HOST_NOT_FOUND ". " D_RETRY_IN " %d " D_UNIT_SECOND), SYSLOG_TIMER); } } -void AddLog(byte loglevel) +void AddLog(uint8_t loglevel) { char mxtime[10]; // "13:45:21 " @@ -1136,13 +1258,13 @@ void AddLog(byte loglevel) if (!global_state.wifi_down && (loglevel <= syslog_level)) { Syslog(); } } -void AddLog_P(byte loglevel, const char *formatP) +void AddLog_P(uint8_t loglevel, const char *formatP) { snprintf_P(log_data, sizeof(log_data), formatP); AddLog(loglevel); } -void AddLog_P(byte loglevel, const char *formatP, const char *formatP2) +void AddLog_P(uint8_t loglevel, const char *formatP, const char *formatP2) { char message[100]; @@ -1152,22 +1274,31 @@ void AddLog_P(byte loglevel, const char *formatP, const char *formatP2) AddLog(loglevel); } -void AddLogSerial(byte loglevel, uint8_t *buffer, int count) +void AddLog_P2(uint8_t loglevel, PGM_P formatP, ...) { - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_SERIAL D_RECEIVED)); + va_list arg; + va_start(arg, formatP); + int len = vsnprintf_P(log_data, sizeof(log_data), formatP, arg); + va_end(arg); + + AddLog(loglevel); +} + +void AddLogBuffer(uint8_t loglevel, uint8_t *buffer, int count) +{ + snprintf_P(log_data, sizeof(log_data), PSTR("DMP:")); for (int i = 0; i < count; i++) { snprintf_P(log_data, sizeof(log_data), PSTR("%s %02X"), log_data, *(buffer++)); } AddLog(loglevel); } -void AddLogSerial(byte loglevel) +void AddLogSerial(uint8_t loglevel) { - AddLogSerial(loglevel, (uint8_t*)serial_in_buffer, serial_in_byte_counter); + AddLogBuffer(loglevel, (uint8_t*)serial_in_buffer, serial_in_byte_counter); } void AddLogMissed(char *sensor, uint8_t misses) { - snprintf_P(log_data, sizeof(log_data), PSTR("SNS: %s missed %d"), sensor, SENSOR_MAX_MISS - misses); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SNS: %s missed %d"), sensor, SENSOR_MAX_MISS - misses); } diff --git a/sonoff/support_button.ino b/sonoff/support_button.ino new file mode 100644 index 000000000..c0fe14bd2 --- /dev/null +++ b/sonoff/support_button.ino @@ -0,0 +1,242 @@ +/* + support_button.ino - button support for Sonoff-Tasmota + + Copyright (C) 2019 Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#define BUTTON_V1 +#ifdef BUTTON_V1 +/*********************************************************************************************\ + * Button support +\*********************************************************************************************/ + +unsigned long button_debounce = 0; // Button debounce timer +uint16_t holdbutton[MAX_KEYS] = { 0 }; // Timer for button hold +uint16_t dual_button_code = 0; // Sonoff dual received code + +uint8_t lastbutton[MAX_KEYS] = { NOT_PRESSED, NOT_PRESSED, NOT_PRESSED, NOT_PRESSED }; // Last button states +uint8_t multiwindow[MAX_KEYS] = { 0 }; // Max time between button presses to record press count +uint8_t multipress[MAX_KEYS] = { 0 }; // Number of button presses within multiwindow + +uint8_t dual_hex_code = 0; // Sonoff dual input flag +uint8_t key_no_pullup = 0; // key no pullup flag (1 = no pullup) +uint8_t key_inverted = 0; // Key inverted flag (1 = inverted) +uint8_t buttons_found = 0; // Number of buttons found flag + +/********************************************************************************************/ + +void ButtonPullupFlag(uint8 button_bit) +{ + bitSet(key_no_pullup, button_bit); +} + +void ButtonInvertFlag(uint8 button_bit) +{ + bitSet(key_inverted, button_bit); +} + +void ButtonInit(void) +{ + buttons_found = 0; + for (uint8_t i = 0; i < MAX_KEYS; i++) { + if (pin[GPIO_KEY1 +i] < 99) { + buttons_found++; + pinMode(pin[GPIO_KEY1 +i], bitRead(key_no_pullup, i) ? INPUT : ((16 == pin[GPIO_KEY1 +i]) ? INPUT_PULLDOWN_16 : INPUT_PULLUP)); + } + } +} + +uint8_t ButtonSerial(uint8_t serial_in_byte) +{ + if (dual_hex_code) { + dual_hex_code--; + if (dual_hex_code) { + dual_button_code = (dual_button_code << 8) | serial_in_byte; + serial_in_byte = 0; + } else { + if (serial_in_byte != 0xA1) { + dual_button_code = 0; // 0xA1 - End of Sonoff dual button code + } + } + } + if (0xA0 == serial_in_byte) { // 0xA0 - Start of Sonoff dual button code + serial_in_byte = 0; + dual_button_code = 0; + dual_hex_code = 3; + } + + return serial_in_byte; +} + +/*********************************************************************************************\ + * Button handler with single press only or multi-press and hold on all buttons +\*********************************************************************************************/ + +void ButtonHandler(void) +{ + if (uptime < 4) { return; } // Block GPIO for 4 seconds after poweron to workaround Wemos D1 / Obi RTS circuit + + uint8_t button = NOT_PRESSED; + uint8_t button_present = 0; + uint8_t hold_time_extent = IMMINENT_RESET_FACTOR; // Extent hold time factor in case of iminnent Reset command + uint16_t loops_per_second = 1000 / Settings.button_debounce; + char scmnd[20]; + +// uint8_t maxdev = (devices_present > MAX_KEYS) ? MAX_KEYS : devices_present; +// for (uint8_t button_index = 0; button_index < maxdev; button_index++) { + for (uint8_t button_index = 0; button_index < MAX_KEYS; button_index++) { + button = NOT_PRESSED; + button_present = 0; + + if (!button_index && ((SONOFF_DUAL == my_module_type) || (CH4 == my_module_type))) { + button_present = 1; + if (dual_button_code) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BUTTON " " D_CODE " %04X"), dual_button_code); + button = PRESSED; + if (0xF500 == dual_button_code) { // Button hold + holdbutton[button_index] = (loops_per_second * Settings.param[P_HOLD_TIME] / 10) -1; + hold_time_extent = 1; + } + dual_button_code = 0; + } + } else { + if (pin[GPIO_KEY1 +button_index] < 99) { + button_present = 1; + button = (digitalRead(pin[GPIO_KEY1 +button_index]) != bitRead(key_inverted, button_index)); + } + } + + if (button_present) { + XdrvMailbox.index = button_index; + XdrvMailbox.payload = button; + if (XdrvCall(FUNC_BUTTON_PRESSED)) { + // Serviced + } + else if (SONOFF_4CHPRO == my_module_type) { + if (holdbutton[button_index]) { holdbutton[button_index]--; } + + bool button_pressed = false; + if ((PRESSED == button) && (NOT_PRESSED == lastbutton[button_index])) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BUTTON "%d " D_LEVEL_10), button_index +1); + holdbutton[button_index] = loops_per_second; + button_pressed = true; + } + if ((NOT_PRESSED == button) && (PRESSED == lastbutton[button_index])) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BUTTON "%d " D_LEVEL_01), button_index +1); + if (!holdbutton[button_index]) { button_pressed = true; } // Do not allow within 1 second + } + if (button_pressed) { + if (!SendKey(0, button_index +1, POWER_TOGGLE)) { // Execute Toggle command via MQTT if ButtonTopic is set + ExecuteCommandPower(button_index +1, POWER_TOGGLE, SRC_BUTTON); // Execute Toggle command internally + } + } + } + else { + if ((PRESSED == button) && (NOT_PRESSED == lastbutton[button_index])) { + if (Settings.flag.button_single) { // Allow only single button press for immediate action + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BUTTON "%d " D_IMMEDIATE), button_index +1); + if (!SendKey(0, button_index +1, POWER_TOGGLE)) { // Execute Toggle command via MQTT if ButtonTopic is set + ExecuteCommandPower(button_index +1, POWER_TOGGLE, SRC_BUTTON); // Execute Toggle command internally + } + } else { + multipress[button_index] = (multiwindow[button_index]) ? multipress[button_index] +1 : 1; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BUTTON "%d " D_MULTI_PRESS " %d"), button_index +1, multipress[button_index]); + multiwindow[button_index] = loops_per_second / 2; // 0.5 second multi press window + } + blinks = 201; + } + + if (NOT_PRESSED == button) { + holdbutton[button_index] = 0; + } else { + holdbutton[button_index]++; + if (Settings.flag.button_single) { // Allow only single button press for immediate action + if (holdbutton[button_index] == loops_per_second * hold_time_extent * Settings.param[P_HOLD_TIME] / 10) { // Button held for factor times longer +// Settings.flag.button_single = 0; + snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_SETOPTION "13 0")); // Disable single press only + ExecuteCommand(scmnd, SRC_BUTTON); + } + } else { + if (Settings.flag.button_restrict) { // Button restriction + if (holdbutton[button_index] == loops_per_second * Settings.param[P_HOLD_TIME] / 10) { // Button hold + multipress[button_index] = 0; + SendKey(0, button_index +1, 3); // Execute Hold command via MQTT if ButtonTopic is set + } + } else { + if (holdbutton[button_index] == loops_per_second * hold_time_extent * Settings.param[P_HOLD_TIME] / 10) { // Button held for factor times longer + multipress[button_index] = 0; + snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_RESET " 1")); + ExecuteCommand(scmnd, SRC_BUTTON); + } + } + } + } + + if (!Settings.flag.button_single) { // Allow multi-press + if (multiwindow[button_index]) { + multiwindow[button_index]--; + } else { + if (!restart_flag && !holdbutton[button_index] && (multipress[button_index] > 0) && (multipress[button_index] < MAX_BUTTON_COMMANDS +3)) { + bool single_press = false; + if (multipress[button_index] < 3) { // Single or Double press + if ((SONOFF_DUAL_R2 == my_module_type) || (SONOFF_DUAL == my_module_type) || (CH4 == my_module_type)) { + single_press = true; + } else { + single_press = (Settings.flag.button_swap +1 == multipress[button_index]); + multipress[button_index] = 1; + } + } + if ((MI_DESK_LAMP == my_module_type) && (button_index == 0) && (rotary_changed) && (light_power)) { + rotary_changed = 0; // Color temp changed, no need to turn of the light + } else { + if (single_press && SendKey(0, button_index + multipress[button_index], POWER_TOGGLE)) { // Execute Toggle command via MQTT if ButtonTopic is set + // Success + } else { + if (multipress[button_index] < 3) { // Single or Double press + if (WifiState() > WIFI_RESTART) { // WPSconfig, Smartconfig or Wifimanager active + restart_flag = 1; + } else { + ExecuteCommandPower(button_index + multipress[button_index], POWER_TOGGLE, SRC_BUTTON); // Execute Toggle command internally + } + } else { // 3 - 7 press + if (!Settings.flag.button_restrict) { + snprintf_P(scmnd, sizeof(scmnd), kCommands[multipress[button_index] -3]); + ExecuteCommand(scmnd, SRC_BUTTON); + } + } + } + } + multipress[button_index] = 0; + } + } + } + } + } + lastbutton[button_index] = button; + } +} + +void ButtonLoop(void) +{ + if (buttons_found) { + if (TimeReached(button_debounce)) { + SetNextTimeInterval(button_debounce, Settings.button_debounce); + ButtonHandler(); + } + } +} + +#endif // BUTTON_V1 diff --git a/sonoff/support_features.ino b/sonoff/support_features.ino index 75ddaf327..c69b8e671 100644 --- a/sonoff/support_features.ino +++ b/sonoff/support_features.ino @@ -1,7 +1,7 @@ /* support_features.ino - feature support for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends + Copyright (C) 2019 Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -126,16 +126,16 @@ void GetFeatures(void) #ifdef USE_CONFIG_OVERRIDE feature_drv2 |= 0x00000001; // user_config(_override).h #endif -#ifdef BE_MINIMAL +#ifdef FIRMWARE_MINIMAL feature_drv2 |= 0x00000002; // user_config(_override).h #endif -#ifdef USE_SENSORS +#ifdef FIRMWARE_SENSORS feature_drv2 |= 0x00000004; // user_config(_override).h #endif -#ifdef USE_CLASSIC +#ifdef FIRMWARE_CLASSIC feature_drv2 |= 0x00000008; // user_config(_override).h #endif -#ifdef USE_KNX_NO_EMULATION +#ifdef FIRMWARE_KNX_NO_EMULATION feature_drv2 |= 0x00000010; // user_config(_override).h #endif #ifdef USE_DISPLAY_MODES1TO5 @@ -177,8 +177,10 @@ void GetFeatures(void) #ifdef USE_ARMTRONIX_DIMMERS feature_drv2 |= 0x00020000; // xdrv_18_armtronixdimmer.ino #endif +#ifdef USE_SM16716 + feature_drv2 |= 0x00040000; // xdrv_04_light.ino +#endif -// feature_drv2 |= 0x00040000; // feature_drv2 |= 0x00080000; // feature_drv2 |= 0x00100000; // feature_drv2 |= 0x00200000; @@ -365,18 +367,26 @@ void GetFeatures(void) feature_sns2 |= 0x00008000; // xsns_37_rfsensor.ino #endif #ifdef USE_THEO_V2 - feature_sns2 |= 0x00010000; + feature_sns2 |= 0x00010000; // xsns_37_rfsensor.ino #endif #ifdef USE_ALECTO_V2 - feature_sns2 |= 0x00020000; + feature_sns2 |= 0x00020000; // xsns_37_rfsensor.ino #endif #ifdef USE_AZ7798 - feature_sns2 |= 0x00040000; + feature_sns2 |= 0x00040000; // xsns_38_az7798.ino +#endif +#ifdef USE_MAX31855 + feature_sns2 |= 0x00080000; // xsns_39_max31855.ino +#endif +#ifdef USE_PN532_HSU + feature_sns2 |= 0x00100000; // xsns_40_pn532.ino +#endif +#ifdef USE_MAX44009 + feature_sns2 |= 0x00200000; +#endif +#ifdef USE_SCD30 + feature_sns2 |= 0x00400000; #endif -// feature_sns2 |= 0x00080000; -// feature_sns2 |= 0x00100000; -// feature_sns2 |= 0x00200000; -// feature_sns2 |= 0x00400000; // feature_sns2 |= 0x00800000; // feature_sns2 |= 0x01000000; // feature_sns2 |= 0x02000000; diff --git a/sonoff/support_rotary.ino b/sonoff/support_rotary.ino new file mode 100644 index 000000000..db88d8378 --- /dev/null +++ b/sonoff/support_rotary.ino @@ -0,0 +1,151 @@ +/* + support_rotary.ino - rotary switch support for Sonoff-Tasmota + + Copyright (C) 2019 Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#define ROTARY_V1 +#ifdef ROTARY_V1 +/*********************************************************************************************\ + * Rotary support +\*********************************************************************************************/ + +unsigned long rotary_debounce = 0; // Rotary debounce timer +uint8_t rotaries_found = 0; +uint8_t rotary_state = 0; +uint8_t rotary_position = 128; +uint8_t rotary_last_position = 128; +uint8_t interrupts_in_use = 0; +uint8_t rotary_changed = 0; + +/********************************************************************************************/ + +void update_position(void) +{ + uint8_t s; + + /* + * https://github.com/PaulStoffregen/Encoder/blob/master/Encoder.h + */ + + s = rotary_state & 3; + if (digitalRead(pin[GPIO_ROT1A])) s |= 4; + if (digitalRead(pin[GPIO_ROT1B])) s |= 8; + switch (s) { + case 0: case 5: case 10: case 15: + break; + case 1: case 7: case 8: case 14: + rotary_position++; break; + case 2: case 4: case 11: case 13: + rotary_position--; break; + case 3: case 12: + rotary_position = rotary_position + 2; break; + default: + rotary_position = rotary_position - 2; break; + } + rotary_state = (s >> 2); +} + +void update_rotary(void) +{ + if (MI_DESK_LAMP == my_module_type){ + if (light_power) { + update_position(); + } + } +} + +void RotaryInit(void) +{ + rotaries_found = 0; + if ((pin[GPIO_ROT1A] < 99) && (pin[GPIO_ROT1B] < 99)) { + rotaries_found++; + pinMode(pin[GPIO_ROT1A], INPUT_PULLUP); + pinMode(pin[GPIO_ROT1B], INPUT_PULLUP); + + // GPIO6-GPIO11 are typically used to interface with the flash memory IC on + // most esp8266 modules, so we should avoid adding interrupts to these pins. + + if ((pin[GPIO_ROT1A] < 6) || (pin[GPIO_ROT1A] > 11)) { + attachInterrupt(digitalPinToInterrupt(pin[GPIO_ROT1A]), update_rotary, CHANGE); + interrupts_in_use++; + } + if ((pin[GPIO_ROT1B] < 6) || (pin[GPIO_ROT1B] > 11)) { + attachInterrupt(digitalPinToInterrupt(pin[GPIO_ROT1B]), update_rotary, CHANGE); + interrupts_in_use++; + } + } +} + +/*********************************************************************************************\ + * Rotary handler +\*********************************************************************************************/ + +void RotaryHandler(void) +{ + if (interrupts_in_use < 2) { + noInterrupts(); + update_rotary(); + } else { + noInterrupts(); + } + if (rotary_last_position != rotary_position) { + if (MI_DESK_LAMP == my_module_type) { // Mi Desk lamp + if (holdbutton[0]) { + rotary_changed = 1; + // button1 is pressed: set color temperature + int16_t t = LightGetColorTemp(); + t = t + (rotary_position - rotary_last_position); + if (t < 153) { + t = 153; + } + if (t > 500) { + t = 500; + } + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_CMND_COLORTEMPERATURE " %d"), rotary_position - rotary_last_position); + LightSetColorTemp((uint16_t)t); + } else { + int8_t d = Settings.light_dimmer; + d = d + (rotary_position - rotary_last_position); + if (d < 1) { + d = 1; + } + if (d > 100) { + d = 100; + } + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_CMND_DIMMER " %d"), rotary_position - rotary_last_position); + + LightSetDimmer((uint8_t)d); + Settings.light_dimmer = d; + } + } + rotary_last_position = 128; + rotary_position = 128; + } + interrupts(); +} + +void RotaryLoop(void) +{ + if (rotaries_found) { + if (TimeReached(rotary_debounce)) { + SetNextTimeInterval(rotary_debounce, Settings.button_debounce); // Using button_debounce setting for this as well + RotaryHandler(); + } + } +} + +#endif // ROTARY_V1 diff --git a/sonoff/support_rtc.ino b/sonoff/support_rtc.ino index b09c593e2..9a36951f0 100644 --- a/sonoff/support_rtc.ino +++ b/sonoff/support_rtc.ino @@ -1,7 +1,7 @@ /* support_rtc.ino - Real Time Clock support for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends + Copyright (C) 2019 Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -43,7 +43,7 @@ uint32_t local_time = 0; uint32_t daylight_saving_time = 0; uint32_t standard_time = 0; uint32_t ntp_time = 0; -uint32_t midnight = 1451602800; +uint32_t midnight = 0; uint32_t restart_time = 0; int32_t time_timezone = 0; uint8_t midnight_now = 0; @@ -60,7 +60,7 @@ String GetBuildDateAndTime(void) int year = 0; // sscanf(mdate, "%s %d %d", bdt, &day, &year); // Not implemented in 2.3.0 and probably too much code - byte i = 0; + uint8_t i = 0; for (char *str = strtok_r(mdate, " ", &p); str && i < 3; str = strtok_r(NULL, " ", &p)) { switch (i++) { case 0: // Month @@ -87,6 +87,23 @@ String GetTimeZone(void) return String(tz); // -03:45 } +String GetDuration(uint32_t time) +{ + char dt[16]; + + TIME_T ut; + BreakTime(time, ut); + + // "P128DT14H35M44S" - ISO8601:2004 - https://en.wikipedia.org/wiki/ISO_8601 Durations +// snprintf_P(dt, sizeof(dt), PSTR("P%dDT%02dH%02dM%02dS"), ut.days, ut.hour, ut.minute, ut.second); + + // "128 14:35:44" - OpenVMS + // "128T14:35:44" - Tasmota + snprintf_P(dt, sizeof(dt), PSTR("%dT%02d:%02d:%02d"), ut.days, ut.hour, ut.minute, ut.second); + + return String(dt); // 128T14:35:44 +} + String GetDT(uint32_t time) { // "2017-03-07T11:08:02" - ISO8601:2004 @@ -112,7 +129,7 @@ String GetDT(uint32_t time) * "2017-03-07T11:08:02-07:00" - if DT_LOCAL and SetOption52 = 1 * "2017-03-07T11:08:02" - otherwise */ -String GetDateAndTime(byte time_type) +String GetDateAndTime(uint8_t time_type) { // "2017-03-07T11:08:02-07:00" - ISO8601:2004 uint32_t time = local_time; @@ -155,42 +172,26 @@ String GetTime(int type) return String(stime); // Thu Nov 01 11:41:02 2018 } +uint32_t UpTime(void) +{ + if (restart_time) { + return utc_time - restart_time; + } else { + return uptime; + } +} + +uint32_t MinutesUptime(void) +{ + return (UpTime() / 60); +} + String GetUptime(void) { - char dt[16]; - - TIME_T ut; - - if (restart_time) { - BreakTime(utc_time - restart_time, ut); - } else { - BreakTime(uptime, ut); - } - - // "P128DT14H35M44S" - ISO8601:2004 - https://en.wikipedia.org/wiki/ISO_8601 Durations -// snprintf_P(dt, sizeof(dt), PSTR("P%dDT%02dH%02dM%02dS"), ut.days, ut.hour, ut.minute, ut.second); - - // "128 14:35:44" - OpenVMS - // "128T14:35:44" - Tasmota - snprintf_P(dt, sizeof(dt), PSTR("%dT%02d:%02d:%02d"), ut.days, ut.hour, ut.minute, ut.second); - - return String(dt); // 128T14:35:44 + return GetDuration(UpTime()); } -uint32_t GetMinutesUptime(void) -{ - TIME_T ut; - - if (restart_time) { - BreakTime(utc_time - restart_time, ut); - } else { - BreakTime(uptime, ut); - } - - return (ut.days *1440) + (ut.hour *60) + ut.minute; -} - -uint32_t GetMinutesPastMidnight(void) +uint32_t MinutesPastMidnight(void) { uint32_t minutes = 0; @@ -322,6 +323,11 @@ uint32_t RuleToTime(TimeRule r, int yr) return t; } +uint32_t UtcTime(void) +{ + return utc_time; +} + uint32_t LocalTime(void) { return local_time; @@ -332,9 +338,9 @@ uint32_t Midnight(void) return midnight; } -boolean MidnightNow(void) +bool MidnightNow(void) { - boolean mnflg = midnight_now; + bool mnflg = midnight_now; if (mnflg) midnight_now = 0; return mnflg; } @@ -348,7 +354,7 @@ void RtcSecond(void) if (!global_state.wifi_down && (offset == RtcTime.second) && ((RtcTime.year < 2016) || (ntp_sync_minute == RtcTime.minute) || ntp_force_sync)) { ntp_time = sntp_get_current_timestamp(); if (ntp_time > 1451602800) { // Fix NTP bug in core 2.4.1/SDK 2.2.1 (returns Thu Jan 01 08:00:10 1970 after power on) - ntp_force_sync = 0; + ntp_force_sync = false; utc_time = ntp_time; ntp_sync_minute = 60; // Sync so block further requests if (restart_time == 0) { @@ -358,9 +364,7 @@ void RtcSecond(void) RtcTime.year = tmpTime.year + 1970; daylight_saving_time = RuleToTime(Settings.tflag[1], RtcTime.year); standard_time = RuleToTime(Settings.tflag[0], RtcTime.year); - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_APPLICATION "(" D_UTC_TIME ") %s, (" D_DST_TIME ") %s, (" D_STD_TIME ") %s"), - GetTime(0).c_str(), GetTime(2).c_str(), GetTime(3).c_str()); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION "(" D_UTC_TIME ") %s, (" D_DST_TIME ") %s, (" D_STD_TIME ") %s"), GetTime(0).c_str(), GetTime(2).c_str(), GetTime(3).c_str()); if (local_time < 1451602800) { // 2016-01-01 rules_flag.time_init = 1; } else { @@ -400,10 +404,17 @@ void RtcSecond(void) if (!Settings.energy_kWhtotal_time) { Settings.energy_kWhtotal_time = local_time; } } BreakTime(local_time, RtcTime); - if (!RtcTime.hour && !RtcTime.minute && !RtcTime.second && RtcTime.valid) { - midnight = local_time; - midnight_now = 1; + + if (RtcTime.valid) { + if (!midnight) { + midnight = local_time - (RtcTime.hour * 3600) - (RtcTime.minute * 60) - RtcTime.second; + } + if (!RtcTime.hour && !RtcTime.minute && !RtcTime.second) { + midnight = local_time; + midnight_now = 1; + } } + RtcTime.year += 1970; } diff --git a/sonoff/support_switch.ino b/sonoff/support_switch.ino new file mode 100644 index 000000000..7e84149ba --- /dev/null +++ b/sonoff/support_switch.ino @@ -0,0 +1,221 @@ +/* + support_switch.ino - switch support for Sonoff-Tasmota + + Copyright (C) 2019 Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#define SWITCH_V2 +#ifdef SWITCH_V2 +/*********************************************************************************************\ + * Switch support with input filter + * + * Inspired by (https://github.com/OLIMEX/olimex-iot-firmware-esp8266/blob/master/olimex/user/user_switch2.c) +\*********************************************************************************************/ + +#define SWITCH_PROBE_INTERVAL 10 // Time in milliseconds between switch input probe + +#include + +Ticker TickerSwitch; + +unsigned long switch_debounce = 0; // Switch debounce timer +uint16_t switch_no_pullup = 0; // Switch pull-up bitmask flags +uint8_t switch_state_buf[MAX_SWITCHES] = { 0 }; +uint8_t lastwallswitch[MAX_SWITCHES]; // Last wall switch states +uint8_t holdwallswitch[MAX_SWITCHES] = { 0 }; // Timer for wallswitch push button hold +uint8_t switch_virtual[MAX_SWITCHES]; // Virtual switch states +uint8_t switches_found = 0; + +/********************************************************************************************/ + +void SwitchPullupFlag(uint16 switch_bit) +{ + bitSet(switch_no_pullup, switch_bit); +} + +uint8_t SwitchLastState(uint8_t index) +{ + return lastwallswitch[index]; +} + +void SwitchSetVirtual(uint8_t index, uint8_t state) +{ + switch_virtual[index] = state; +} + +uint8_t SwitchGetVirtual(uint8_t index) +{ + return switch_virtual[index]; +} + +/*********************************************************************************************/ + +void SwitchProbe(void) +{ + if (uptime < 4) { return; } // Block GPIO for 4 seconds after poweron to workaround Wemos D1 / Obi RTS circuit + + uint8_t state_filter = Settings.switch_debounce / SWITCH_PROBE_INTERVAL; // 5, 10, 15 + uint8_t force_high = (Settings.switch_debounce % 50) &1; // 51, 101, 151 etc + uint8_t force_low = (Settings.switch_debounce % 50) &2; // 52, 102, 152 etc + + for (uint8_t i = 0; i < MAX_SWITCHES; i++) { + if (pin[GPIO_SWT1 +i] < 99) { + // Olimex user_switch2.c code to fix 50Hz induced pulses + if (1 == digitalRead(pin[GPIO_SWT1 +i])) { + + if (force_high) { // Enabled with SwitchDebounce x1 + if (1 == switch_virtual[i]) { + switch_state_buf[i] = state_filter; // With noisy input keep current state 1 unless constant 0 + } + } + + if (switch_state_buf[i] < state_filter) { + switch_state_buf[i]++; + if (state_filter == switch_state_buf[i]) { + switch_virtual[i] = 1; + } + } + } else { + + if (force_low) { // Enabled with SwitchDebounce x2 + if (0 == switch_virtual[i]) { + switch_state_buf[i] = 0; // With noisy input keep current state 0 unless constant 1 + } + } + + if (switch_state_buf[i] > 0) { + switch_state_buf[i]--; + if (0 == switch_state_buf[i]) { + switch_virtual[i] = 0; + } + } + } + } + } + TickerSwitch.attach_ms(SWITCH_PROBE_INTERVAL, SwitchProbe); // Re-arm as core 2.3.0 does only support ONCE mode +} + +void SwitchInit(void) +{ + switches_found = 0; + for (uint8_t i = 0; i < MAX_SWITCHES; i++) { + lastwallswitch[i] = 1; // Init global to virtual switch state; + if (pin[GPIO_SWT1 +i] < 99) { + switches_found++; + pinMode(pin[GPIO_SWT1 +i], bitRead(switch_no_pullup, i) ? INPUT : ((16 == pin[GPIO_SWT1 +i]) ? INPUT_PULLDOWN_16 : INPUT_PULLUP)); + lastwallswitch[i] = digitalRead(pin[GPIO_SWT1 +i]); // Set global now so doesn't change the saved power state on first switch check + } + switch_virtual[i] = lastwallswitch[i]; + } + if (switches_found) { TickerSwitch.attach_ms(SWITCH_PROBE_INTERVAL, SwitchProbe); } +} + +/*********************************************************************************************\ + * Switch handler +\*********************************************************************************************/ + +void SwitchHandler(uint8_t mode) +{ + if (uptime < 4) { return; } // Block GPIO for 4 seconds after poweron to workaround Wemos D1 / Obi RTS circuit + + uint8_t button = NOT_PRESSED; + uint8_t switchflag; + uint16_t loops_per_second = 1000 / Settings.switch_debounce; + + for (uint8_t i = 0; i < MAX_SWITCHES; i++) { + if ((pin[GPIO_SWT1 +i] < 99) || (mode)) { + + if (holdwallswitch[i]) { + holdwallswitch[i]--; + if (0 == holdwallswitch[i]) { + SendKey(1, i +1, 3); // Execute command via MQTT + } + } + + button = switch_virtual[i]; + +// enum SwitchModeOptions {TOGGLE, FOLLOW, FOLLOW_INV, PUSHBUTTON, PUSHBUTTON_INV, PUSHBUTTONHOLD, PUSHBUTTONHOLD_INV, PUSHBUTTON_TOGGLE, MAX_SWITCH_OPTION}; + + if (button != lastwallswitch[i]) { + switchflag = 3; + switch (Settings.switchmode[i]) { + case TOGGLE: + switchflag = 2; // Toggle + break; + case FOLLOW: + switchflag = button &1; // Follow wall switch state + break; + case FOLLOW_INV: + switchflag = ~button &1; // Follow inverted wall switch state + break; + case PUSHBUTTON: + if ((PRESSED == button) && (NOT_PRESSED == lastwallswitch[i])) { + switchflag = 2; // Toggle with pushbutton to Gnd + } + break; + case PUSHBUTTON_INV: + if ((NOT_PRESSED == button) && (PRESSED == lastwallswitch[i])) { + switchflag = 2; // Toggle with releasing pushbutton from Gnd + } + break; + case PUSHBUTTON_TOGGLE: + if (button != lastwallswitch[i]) { + switchflag = 2; // Toggle with any pushbutton change + } + break; + case PUSHBUTTONHOLD: + if ((PRESSED == button) && (NOT_PRESSED == lastwallswitch[i])) { + holdwallswitch[i] = loops_per_second * Settings.param[P_HOLD_TIME] / 10; + } + if ((NOT_PRESSED == button) && (PRESSED == lastwallswitch[i]) && (holdwallswitch[i])) { + holdwallswitch[i] = 0; + switchflag = 2; // Toggle with pushbutton to Gnd + } + break; + case PUSHBUTTONHOLD_INV: + if ((NOT_PRESSED == button) && (PRESSED == lastwallswitch[i])) { + holdwallswitch[i] = loops_per_second * Settings.param[P_HOLD_TIME] / 10; + } + if ((PRESSED == button) && (NOT_PRESSED == lastwallswitch[i]) && (holdwallswitch[i])) { + holdwallswitch[i] = 0; + switchflag = 2; // Toggle with pushbutton to Gnd + } + break; + } + + if (switchflag < 3) { + if (!SendKey(1, i +1, switchflag)) { // Execute command via MQTT + ExecuteCommandPower(i +1, switchflag, SRC_SWITCH); // Execute command internally (if i < devices_present) + } + } + + lastwallswitch[i] = button; + } + } + } +} + +void SwitchLoop(void) +{ + if (switches_found) { + if (TimeReached(switch_debounce)) { + SetNextTimeInterval(switch_debounce, Settings.switch_debounce); + SwitchHandler(0); + } + } +} + +#endif // SWITCH_V2 diff --git a/sonoff/support_wifi.ino b/sonoff/support_wifi.ino index 8e45f58ea..2c7299991 100644 --- a/sonoff/support_wifi.ino +++ b/sonoff/support_wifi.ino @@ -1,7 +1,7 @@ /* support_wifi.ino - wifi support for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends + Copyright (C) 2019 Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -32,8 +32,26 @@ #define WIFI_CHECK_SEC 20 // seconds #define WIFI_RETRY_OFFSET_SEC 20 // seconds +/* +// This worked for 2_5_0_BETA2 but fails since then. Waiting for a solution from core team (#4952) +#ifdef USE_MQTT_TLS +#if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_1) || defined(ARDUINO_ESP8266_RELEASE_2_4_2) +#else +#define USING_AXTLS +#include +// force use of AxTLS (BearSSL is now default) which uses less memory (#4952) +#include +using namespace axTLS; +#endif // ARDUINO_ESP8266_RELEASE prior to 2_5_0 +#else +#include // Wifi, MQTT, Ota, WifiManager +#endif // USE_MQTT_TLS +*/ #include // Wifi, MQTT, Ota, WifiManager +uint32_t wifi_last_event = 0; // Last wifi connection event +uint32_t wifi_downtime = 0; // Wifi down duration +uint16_t wifi_link_count = 0; // Number of wifi re-connect uint8_t wifi_counter; uint8_t wifi_retry_init; uint8_t wifi_retry; @@ -41,6 +59,7 @@ uint8_t wifi_status; uint8_t wps_result; uint8_t wifi_config_type = 0; uint8_t wifi_config_counter = 0; +uint8_t mdns_begun = 0; // mDNS active uint8_t wifi_scan_state; uint8_t wifi_bssid[6]; @@ -59,7 +78,7 @@ int WifiGetRssiAsQuality(int rssi) return quality; } -boolean WifiConfigCounter(void) +bool WifiConfigCounter(void) { if (wifi_config_counter) { wifi_config_counter = WIFI_CONFIG_SEC; @@ -88,18 +107,17 @@ void WifiWpsStatusCallback(wps_cb_status status) if (WPS_CB_ST_SUCCESS == wps_result) { wifi_wps_disable(); } else { - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_WIFI D_WPS_FAILED_WITH_STATUS " %d"), wps_result); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI D_WPS_FAILED_WITH_STATUS " %d"), wps_result); wifi_config_counter = 2; } } -boolean WifiWpsConfigDone(void) +bool WifiWpsConfigDone(void) { return (!wps_result); } -boolean WifiWpsConfigBegin(void) +bool WifiWpsConfigBegin(void) { wps_result = 99; if (!wifi_wps_disable()) { return false; } @@ -155,9 +173,9 @@ void WifiConfig(uint8_t type) } #endif // USE_WPS #ifdef USE_WEBSERVER - else if (WIFI_MANAGER == wifi_config_type) { + else if (WIFI_MANAGER == wifi_config_type || WIFI_MANAGER_RESET_ONLY == wifi_config_type) { AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_WCFG_2_WIFIMANAGER " " D_ACTIVE_FOR_3_MINUTES)); - WifiManagerBegin(); + WifiManagerBegin(WIFI_MANAGER_RESET_ONLY == wifi_config_type); } #endif // USE_WEBSERVER } @@ -206,7 +224,8 @@ void WifiBegin(uint8_t flag, uint8_t channel) delay(200); WiFi.mode(WIFI_STA); // Disable AP mode WiFiSetSleepMode(); -// if (WiFi.getPhyMode() != WIFI_PHY_MODE_11N) { WiFi.setPhyMode(WIFI_PHY_MODE_11N); } +// if (WiFi.getPhyMode() != WIFI_PHY_MODE_11N) { WiFi.setPhyMode(WIFI_PHY_MODE_11N); } // B/G/N +// if (WiFi.getPhyMode() != WIFI_PHY_MODE_11G) { WiFi.setPhyMode(WIFI_PHY_MODE_11G); } // B/G if (!WiFi.getAutoConnect()) { WiFi.setAutoConnect(true); } // WiFi.setAutoReconnect(true); switch (flag) { @@ -227,9 +246,8 @@ void WifiBegin(uint8_t flag, uint8_t channel) } else { WiFi.begin(Settings.sta_ssid[Settings.sta_active], Settings.sta_pwd[Settings.sta_active]); } - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_WIFI D_CONNECTING_TO_AP "%d %s " D_IN_MODE " 11%c " D_AS " %s..."), + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_CONNECTING_TO_AP "%d %s " D_IN_MODE " 11%c " D_AS " %s..."), Settings.sta_active +1, Settings.sta_ssid[Settings.sta_active], kWifiPhyMode[WiFi.getPhyMode() & 0x3], my_hostname); - AddLog(LOG_LEVEL_INFO); } void WifiBeginAfterScan() @@ -304,9 +322,8 @@ void WifiBeginAfterScan() break; } } - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_WIFI "Network %d, AP%c, SSId %s, Channel %d, BSSId %02X:%02X:%02X:%02X:%02X:%02X, RSSI %d, Encryption %d"), + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI "Network %d, AP%c, SSId %s, Channel %d, BSSId %02X:%02X:%02X:%02X:%02X:%02X, RSSI %d, Encryption %d"), i, (known) ? (j) ? '2' : '1' : '-', ssid_scan.c_str(), chan_scan, bssid_scan[0], bssid_scan[1], bssid_scan[2], bssid_scan[3], bssid_scan[4], bssid_scan[5], rssi_scan, (sec_scan == ENC_TYPE_NONE) ? 0 : 1); - AddLog(LOG_LEVEL_DEBUG); delay(0); } WiFi.scanDelete(); // Clean up Ram @@ -323,13 +340,26 @@ void WifiBeginAfterScan() } } +uint16_t WifiLinkCount() +{ + return wifi_link_count; +} + +String WifiDowntime() +{ + return GetDuration(wifi_downtime); +} + void WifiSetState(uint8_t state) { if (state == global_state.wifi_down) { if (state) { rules_flag.wifi_connected = 1; + wifi_link_count++; + wifi_downtime += UpTime() - wifi_last_event; } else { rules_flag.wifi_disconnected = 1; + wifi_last_event = UpTime(); } } global_state.wifi_down = state ^1; @@ -349,6 +379,14 @@ void WifiCheckIp(void) Settings.ip_address[3] = (uint32_t)WiFi.dnsIP(); } wifi_status = WL_CONNECTED; +#ifdef USE_DISCOVERY +#ifdef WEBSERVER_ADVERTISE + if (2 == mdns_begun) { + MDNS.update(); + AddLog_P(LOG_LEVEL_DEBUG_MORE, D_LOG_MDNS, "MDNS.update"); + } +#endif // USE_DISCOVERY +#endif // WEBSERVER_ADVERTISE } else { WifiSetState(0); uint8_t wifi_config_tool = Settings.sta_config; @@ -449,8 +487,7 @@ void WifiCheck(uint8_t param) strlcpy(Settings.sta_pwd[0], WiFi.psk().c_str(), sizeof(Settings.sta_pwd[0])); } Settings.sta_active = 0; - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_WIFI D_WCFG_1_SMARTCONFIG D_CMND_SSID "1 %s"), Settings.sta_ssid[0]); - AddLog(LOG_LEVEL_INFO); + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_WCFG_1_SMARTCONFIG D_CMND_SSID "1 %s"), Settings.sta_ssid[0]); } } if (!wifi_config_counter) { @@ -477,23 +514,24 @@ void WifiCheck(uint8_t param) } } -#ifdef BE_MINIMAL +#ifdef FIRMWARE_MINIMAL if (1 == RtcSettings.ota_loader) { RtcSettings.ota_loader = 0; ota_state_flag = 3; } -#endif // BE_MINIMAL +#endif // FIRMWARE_MINIMAL #ifdef USE_DISCOVERY - if (!mdns_begun) { - if (mdns_delayed_start) { - AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_MDNS D_ATTEMPTING_CONNECTION)); - mdns_delayed_start--; - } else { - mdns_delayed_start = Settings.param[P_MDNS_DELAYED_START]; - mdns_begun = MDNS.begin(my_hostname); - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_MDNS "%s"), (mdns_begun) ? D_INITIALIZED : D_FAILED); - AddLog(LOG_LEVEL_INFO); + if (Settings.flag3.mdns_enabled) { + if (!mdns_begun) { +// if (mdns_delayed_start) { +// AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_MDNS D_ATTEMPTING_CONNECTION)); +// mdns_delayed_start--; +// } else { +// mdns_delayed_start = Settings.param[P_MDNS_DELAYED_START]; + mdns_begun = (uint8_t)MDNS.begin(my_hostname); + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MDNS "%s"), (mdns_begun) ? D_INITIALIZED : D_FAILED); +// } } } #endif // USE_DISCOVERY @@ -503,7 +541,8 @@ void WifiCheck(uint8_t param) StartWebserver(Settings.webserver, WiFi.localIP()); #ifdef USE_DISCOVERY #ifdef WEBSERVER_ADVERTISE - if (mdns_begun) { + if (1 == mdns_begun) { + mdns_begun = 2; MDNS.addService("http", "tcp", WEB_PORT); } #endif // WEBSERVER_ADVERTISE @@ -528,7 +567,7 @@ void WifiCheck(uint8_t param) #if defined(USE_WEBSERVER) && defined(USE_EMULATION) UdpDisconnect(); #endif // USE_EMULATION - mdns_begun = false; + mdns_begun = 0; #ifdef USE_KNX knx_started = false; #endif // USE_KNX diff --git a/sonoff/user_config_override_sample.h b/sonoff/user_config_override_sample.h index 8d0abb22f..9a48d2d50 100644 --- a/sonoff/user_config_override_sample.h +++ b/sonoff/user_config_override_sample.h @@ -1,7 +1,7 @@ /* user_config_override.h - user configuration overrides my_user_config.h for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends + Copyright (C) 2019 Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -20,7 +20,7 @@ #ifndef _USER_CONFIG_OVERRIDE_H_ #define _USER_CONFIG_OVERRIDE_H_ -// force the compiler to show a warning to confirm that this file is inlcuded +// force the compiler to show a warning to confirm that this file is included #warning **** user_config_override.h: Using Settings from this File **** /*****************************************************************************************************\ @@ -74,7 +74,7 @@ Examples : #ifdef MY_IP #undef WIFI_IP_ADDRESS -#define WIFI_IP_ADDRESS MY_IP // Set to 0.0.0.0 for using DHCP or IP address +#define WIFI_IP_ADDRESS MY_IP // Set to 0.0.0.0 for using DHCP or enter a static IP address #endif #ifdef MY_GW @@ -93,4 +93,4 @@ Examples : -#endif // _USER_CONFIG_OVERRIDE_H_ \ No newline at end of file +#endif // _USER_CONFIG_OVERRIDE_H_ diff --git a/sonoff/xdrv_01_webserver.ino b/sonoff/xdrv_01_webserver.ino index 046181283..9d96c480c 100644 --- a/sonoff/xdrv_01_webserver.ino +++ b/sonoff/xdrv_01_webserver.ino @@ -1,7 +1,7 @@ /* xdrv_01_webserver.ino - webserver for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends + Copyright (C) 2019 Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -27,6 +27,8 @@ #define XDRV_01 1 +#define CHUNKED_BUFFER_SIZE 400 // Chunk buffer size + #ifndef WIFI_SOFT_AP_CHANNEL #define WIFI_SOFT_AP_CHANNEL 1 // Soft Access Point Channel number between 1 and 11 as used by SmartConfig web GUI #endif @@ -49,16 +51,16 @@ const char HTTP_HEAD[] PROGMEM = "" "" "" - "{h} - {v}" + "%s - %s" ""; + "window.onload=u;"; const char HTTP_SCRIPT_ROOT[] PROGMEM = "function la(p){" @@ -75,24 +77,25 @@ const char HTTP_SCRIPT_ROOT[] PROGMEM = "a=p;" "clearTimeout(lt);" "}" - "if(x!=null){x.abort();}" // Abort if no response within 2 seconds (happens on restart 1) + "if(x!=null){x.abort();}" // Abort if no response within 2 seconds (happens on restart 1) "x=new XMLHttpRequest();" "x.onreadystatechange=function(){" "if(x.readyState==4&&x.status==200){" - "var s=x.responseText.replace(/{t}/g,\"\").replace(/{s}/g,\"\").replace(/{c}/g,\"%'>
\").replace(/{s}/g,\"
\").replace(/{c}/g,\"%%'>
hasArg("m") "x.send();" - "lt=setTimeout(la,{a});" // Settings.web_refresh + "lt=setTimeout(la,%d);" // Settings.web_refresh "}" "function lb(p){" - "la('?d='+p);" // ?d related to WebGetArg("d", tmp, sizeof(tmp)); + "la('&d='+p);" // &d related to WebGetArg("d", tmp, sizeof(tmp)); "}" "function lc(p){" - "la('?t='+p);" // ?t related to WebGetArg("t", tmp, sizeof(tmp)); - "}"; + "la('&t='+p);" // &t related to WebGetArg("t", tmp, sizeof(tmp)); + "}" + "window.onload=la();"; const char HTTP_SCRIPT_WIFI[] PROGMEM = "function c(l){" @@ -101,18 +104,16 @@ const char HTTP_SCRIPT_WIFI[] PROGMEM = "}"; const char HTTP_SCRIPT_RELOAD[] PROGMEM = - "setTimeout(function(){location.href='.';}," STR(HTTP_RESTART_RECONNECT_TIME) ");" - ""; + "setTimeout(function(){location.href='.';}," STR(HTTP_RESTART_RECONNECT_TIME) ");"; // Local OTA upgrade requires more time to complete cp: before web ui should be reloaded const char HTTP_SCRIPT_RELOAD_OTA[] PROGMEM = - "setTimeout(function(){location.href='.';}," STR(HTTP_OTA_RESTART_RECONNECT_TIME) ");" - ""; + "setTimeout(function(){location.href='.';}," STR(HTTP_OTA_RESTART_RECONNECT_TIME) ");"; const char HTTP_SCRIPT_CONSOL[] PROGMEM = - "var sn=0;" // Scroll position - "var id=0;" // Get most of weblog initially - "function l(p){" // Console log and command service + "var sn=0;" // Scroll position + "var id=0;" // Get most of weblog initially + "function l(p){" // Console log and command service "var c,o,t;" "clearTimeout(lt);" "o='';" @@ -123,75 +124,131 @@ const char HTTP_SCRIPT_CONSOL[] PROGMEM = "c.value='';" "t.scrollTop=sn;" "}" - "if(t.scrollTop>=sn){" // User scrolled back so no updates - "if(x!=null){x.abort();}" // Abort if no response within 2 seconds (happens on restart 1) + "if(t.scrollTop>=sn){" // User scrolled back so no updates + "if(x!=null){x.abort();}" // Abort if no response within 2 seconds (happens on restart 1) "x=new XMLHttpRequest();" "x.onreadystatechange=function(){" "if(x.readyState==4&&x.status==200){" "var z,d;" - "d=x.responseXML;" - "id=d.getElementsByTagName('i')[0].childNodes[0].nodeValue;" - "if(d.getElementsByTagName('j')[0].childNodes[0].nodeValue==0){t.value='';}" - "z=d.getElementsByTagName('l')[0].childNodes;" - "if(z.length>0){t.value+=decodeURIComponent(z[0].nodeValue);}" + "d=x.responseText.split(/}1/);" // Field separator + "id=d.shift();" + "if(d.shift()==0){t.value='';}" + "z=d.shift();" + "if(z.length>0){t.value+=z;}" "t.scrollTop=99999;" "sn=t.scrollTop;" "}" "};" - "x.open('GET','ax?c2='+id+o,true);" + "x.open('GET','cs?c2='+id+o,true);" // Related to WebServer->hasArg("c2") and WebGetArg("c2", stmp, sizeof(stmp)) "x.send();" "}" - "lt=setTimeout(l,{a});" + "lt=setTimeout(l,%d);" "return false;" "}" - ""; + "window.onload=l;"; -const char HTTP_SCRIPT_MODULE1[] PROGMEM = +const char HTTP_MODULE_TEMPLATE_REPLACE[] PROGMEM = + "}2%d'>%s (%d}3"; // }2 and }3 are used in below os.replace + +const char HTTP_SCRIPT_MODULE_TEMPLATE[] PROGMEM = "var os;" - "function sk(s,g){" // s = value, g = id and name - "var o=os.replace(\"value='\"+s+\"'\",\"selected value='\"+s+\"'\");" + "function sk(s,g){" // s = value, g = id and name + "var o=os.replace(/}2/g,\"
"); - byte idx = 0; - for (byte i = 0; i < 4; i++) { - if (idx > 0) { page += F(""); } - for (byte j = 0; j < 4; j++) { - idx++; - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR(""), idx, idx); // ?k is related to WebGetArg("k", tmp, sizeof(tmp)); - page += mqtt_data; - } - } - page += F("
\").replace(/{m}/g,\"\").replace(/{e}/g,\"
\").replace(/{m}/g,\"\").replace(/{e}/g,\"
\").replace(/}2/g,\"\");" "eb('i').innerHTML=s;" "}" - ""; + "window.onload=i;"; -const char HTTP_HEAD_STYLE[] PROGMEM = +const char HTTP_HEAD_STYLE1[] PROGMEM = "" "" "" "" "
" -#ifdef BE_MINIMAL +#ifdef FIRMWARE_MINIMAL "

" D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "

" #endif "
" #ifdef LANGUAGE_MODULE_NAME - "

" D_MODULE " {ha

" + "

" D_MODULE " %s

" #else - "

{ha " D_MODULE "

" + "

%s " D_MODULE "

" #endif - "

{h}

{j}
"; + "

%s

%s
"; + const char HTTP_MSG_SLIDER1[] PROGMEM = "
" D_COLDLIGHT "" D_WARMLIGHT "
" "
"; @@ -223,78 +282,69 @@ const char HTTP_MSG_SLIDER2[] PROGMEM = "
"; const char HTTP_MSG_RSTRT[] PROGMEM = "
" D_DEVICE_WILL_RESTART "

"; -const char HTTP_BTN_MENU1[] PROGMEM = -#ifndef BE_MINIMAL - "
" - "
" -#endif - "
" - "
"; -const char HTTP_BTN_RSTRT[] PROGMEM = - "
"; -const char HTTP_BTN_MENU_MODULE[] PROGMEM = - "
" - "
"; -const char HTTP_BTN_MENU4[] PROGMEM = - "
" - "
" - "
" - "
" - "
" - "
"; -const char HTTP_BTN_MAIN[] PROGMEM = - "

"; + const char HTTP_FORM_LOGIN[] PROGMEM = + "
" "
" - "
" D_USER "

" - "
" D_PASSWORD "

" + "

" D_USER "

" + "

" D_PASSWORD "

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

"; + "" + "
"; + +const char HTTP_FORM_TEMPLATE[] PROGMEM = + "
 " D_TEMPLATE_PARAMETERS " " + "
" + "

" D_TEMPLATE_NAME "

" + "

" D_BASE_TYPE "

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

" // Keep close so do not use
+ "
 " D_TEMPLATE_FLAGS " 

" + "" D_ALLOW_ADC0 "
" + "

"; + const char HTTP_FORM_MODULE[] PROGMEM = - "
 " D_MODULE_PARAMETERS " " - "
" D_MODULE_TYPE " ({mt)

"; -const char HTTP_LNK_ITEM[] PROGMEM = - "
{v} ({w}) {i} {r}%
"; -const char HTTP_LNK_SCAN[] PROGMEM = - "
"; + "
 " D_MODULE_PARAMETERS " " + "" + "

" D_MODULE_TYPE " (%s)

" + "
"; + const char HTTP_FORM_WIFI[] PROGMEM = - "
 " D_WIFI_PARAMETERS " " - "
" D_AP1_SSID " (" STA_SSID1 ")

" - "
" D_AP1_PASSWORD "

" - "
" D_AP2_SSID " (" STA_SSID2 ")

" - "
" D_AP2_PASSWORD "

" - "
" D_HOSTNAME " (" WIFI_HOSTNAME ")

"; + "
 " D_WIFI_PARAMETERS " " + "" + "

" D_AP1_SSID " (" STA_SSID1 ")

" + "

" D_AP1_PASSWORD "

" + "

" D_AP2_SSID " (" STA_SSID2 ")

" + "

" D_AP2_PASSWORD "

" + "

" D_HOSTNAME " (%s)

"; + const char HTTP_FORM_LOG1[] PROGMEM = - "
 " D_LOGGING_PARAMETERS " "; + "
 " D_LOGGING_PARAMETERS " " + ""; const char HTTP_FORM_LOG2[] PROGMEM = - "
{b0 ({b1)

"; -const char HTTP_FORM_LOG3[] PROGMEM = - "
" D_SYSLOG_HOST " (" SYS_LOG_HOST ")

" - "
" D_SYSLOG_PORT " (" STR(SYS_LOG_PORT) ")

" - "
" D_TELEMETRY_PERIOD " (" STR(TELE_PERIOD) ")

"; + "

" D_SYSLOG_HOST " (" SYS_LOG_HOST ")

" + "

" D_SYSLOG_PORT " (" STR(SYS_LOG_PORT) ")

" + "

" D_TELEMETRY_PERIOD " (" STR(TELE_PERIOD) ")

"; + const char HTTP_FORM_OTHER[] PROGMEM = - "
 " D_OTHER_PARAMETERS " " -// "" - "
" D_WEB_ADMIN_PASSWORD "

" - "
" D_MQTT_ENABLE "
"; - const char HTTP_FORM_OTHER2[] PROGMEM = - "
" D_FRIENDLY_NAME " {1 ({2)

"; -#ifdef USE_EMULATION -const char HTTP_FORM_OTHER3a[] PROGMEM = - "
 " D_EMULATION " "; -const char HTTP_FORM_OTHER3b[] PROGMEM = - "
{3{4"; // Different id only used for labels -#endif // USE_EMULATION + "
 " D_OTHER_PARAMETERS " " + "" + "

" + "
 " D_TEMPLATE " " + "

" + "

" D_ACTIVATE "

" + "
" + "
" + "" D_WEB_ADMIN_PASSWORD "

" + "
" + "" D_MQTT_ENABLE "
" + "
"; + const char HTTP_FORM_END[] PROGMEM = - "
"; + "
" + "" + "
"; + const char HTTP_FORM_RST[] PROGMEM = "
" "
 " D_RESTORE_CONFIGURATION " "; @@ -302,51 +352,79 @@ const char HTTP_FORM_UPG[] PROGMEM = "
" "
 " D_UPGRADE_BY_WEBSERVER " " "
" - "
" D_OTA_URL "

" + "
" D_OTA_URL "

" "
" "


" "
 " D_UPGRADE_BY_FILE_UPLOAD " "; const char HTTP_FORM_RST_UPG[] PROGMEM = "
" "

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


" "
" "
" // "
" ""; + const char HTTP_TABLE100[] PROGMEM = - "
"; + "
"; + const char HTTP_COUNTER[] PROGMEM = "
"; + const char HTTP_END[] PROGMEM = "
" - "" + "" "" "" ""; -const char HTTP_DEVICE_CONTROL[] PROGMEM = ""; // ?o is related to WebGetArg("o", tmp, sizeof(tmp)); +const char HTTP_DEVICE_CONTROL[] PROGMEM = ""; // ?o is related to WebGetArg("o", tmp, sizeof(tmp)); const char HTTP_DEVICE_STATE[] PROGMEM = "%s"; // {c} = %'>
0) && (source < SRC_MAX)) { char stemp1[20]; - snprintf_P(log_data, sizeof(log_data), PSTR("SRC: %s from %s"), GetTextIndexed(stemp1, sizeof(stemp1), source, kCommandSource), WebServer->client().remoteIP().toString().c_str()); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SRC: %s from %s"), GetTextIndexed(stemp1, sizeof(stemp1), source, kCommandSource), WebServer->client().remoteIP().toString().c_str()); } } @@ -383,22 +464,21 @@ void StartWebserver(int type, IPAddress ipweb) if (!Settings.web_refresh) { Settings.web_refresh = HTTP_REFRESH_TIME; } if (!webserver_state) { if (!WebServer) { - WebServer = new ESP8266WebServer((HTTP_MANAGER==type) ? 80 : WEB_PORT); + WebServer = new ESP8266WebServer((HTTP_MANAGER == type || HTTP_MANAGER_RESET_ONLY == type) ? 80 : WEB_PORT); WebServer->on("/", HandleRoot); + WebServer->onNotFound(HandleNotFound); WebServer->on("/up", HandleUpgradeFirmware); WebServer->on("/u1", HandleUpgradeFirmwareStart); // OTA WebServer->on("/u2", HTTP_POST, HandleUploadDone, HandleUploadLoop); WebServer->on("/u2", HTTP_OPTIONS, HandlePreflightRequest); WebServer->on("/cs", HandleConsole); - WebServer->on("/ax", HandleAjaxConsoleRefresh); - WebServer->on("/ay", HandleAjaxStatusRefresh); WebServer->on("/cm", HandleHttpCommand); - WebServer->onNotFound(HandleNotFound); -#ifndef BE_MINIMAL +#ifndef FIRMWARE_MINIMAL WebServer->on("/cn", HandleConfiguration); WebServer->on("/md", HandleModuleConfiguration); WebServer->on("/wi", HandleWifiConfiguration); WebServer->on("/lg", HandleLoggingConfiguration); + WebServer->on("/tp", HandleTemplateConfiguration); WebServer->on("/co", HandleOtherConfiguration); WebServer->on("/dl", HandleBackupConfiguration); WebServer->on("/rs", HandleRestoreConfiguration); @@ -409,15 +489,13 @@ void StartWebserver(int type, IPAddress ipweb) #endif // USE_EMULATION XdrvCall(FUNC_WEB_ADD_HANDLER); XsnsCall(FUNC_WEB_ADD_HANDLER); -#endif // Not BE_MINIMAL +#endif // Not FIRMWARE_MINIMAL } - reset_web_log_flag = 0; + reset_web_log_flag = false; WebServer->begin(); // Web server start } if (webserver_state != type) { - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_HTTP D_WEBSERVER_ACTIVE_ON " %s%s " D_WITH_IP_ADDRESS " %s"), - my_hostname, (mdns_begun) ? ".local" : "", ipweb.toString().c_str()); - AddLog(LOG_LEVEL_INFO); + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_HTTP D_WEBSERVER_ACTIVE_ON " %s%s " D_WITH_IP_ADDRESS " %s"), my_hostname, (mdns_begun) ? ".local" : "", ipweb.toString().c_str()); } if (type) { webserver_state = type; } } @@ -431,7 +509,7 @@ void StopWebserver(void) } } -void WifiManagerBegin(void) +void WifiManagerBegin(bool reset_only) { // setup AP if (!global_state.wifi_down) { @@ -455,7 +533,7 @@ void WifiManagerBegin(void) DnsServer->setErrorReplyCode(DNSReplyCode::NoError); DnsServer->start(DNS_PORT, "*", WiFi.softAPIP()); - StartWebserver(HTTP_MANAGER, WiFi.softAPIP()); + StartWebserver((reset_only ? HTTP_MANAGER_RESET_ONLY : HTTP_MANAGER), WiFi.softAPIP()); } void PollDnsWebserver(void) @@ -466,35 +544,30 @@ void PollDnsWebserver(void) /*********************************************************************************************/ -void SetHeader(void) -{ - WebServer->sendHeader(F("Cache-Control"), F("no-cache, no-store, must-revalidate")); - WebServer->sendHeader(F("Pragma"), F("no-cache")); - WebServer->sendHeader(F("Expires"), F("-1")); -#ifndef ARDUINO_ESP8266_RELEASE_2_3_0 - WebServer->sendHeader(F("Access-Control-Allow-Origin"), F("*")); -#endif -} - bool WebAuthenticate(void) { - if (Settings.web_password[0] != 0) { + if (Settings.web_password[0] != 0 && HTTP_MANAGER_RESET_ONLY != webserver_state) { return WebServer->authenticate(WEB_USERNAME, Settings.web_password); } else { return true; } } -void ShowPage(String &page, bool auth) +bool HttpCheckPriviledgedAccess(bool autorequestauth = true) { - if (auth && (Settings.web_password[0] != 0) && !WebServer->authenticate(WEB_USERNAME, Settings.web_password)) { - return WebServer->requestAuthentication(); + if (HTTP_USER == webserver_state) { + HandleRoot(); + return false; } + if (autorequestauth && !WebAuthenticate()) { + WebServer->requestAuthentication(); + return false; + } + return true; +} - page.replace(F("{a}"), String(Settings.web_refresh)); - page.replace(F("{ha"), my_module.name); - page.replace(F("{h}"), Settings.friendlyname[0]); - +String WSNetworkInfo(void) +{ String info = ""; if (Settings.flag3.gui_hostname_ip) { uint8_t more_ips = 0; @@ -511,30 +584,170 @@ void ShowPage(String &page, bool auth) } info += F(")"); } - page.replace(F("{j}"), info); + return info; +} - if (HTTP_MANAGER == webserver_state) { +void WSHeaderSend(void) +{ + WebServer->sendHeader(F("Cache-Control"), F("no-cache, no-store, must-revalidate")); + WebServer->sendHeader(F("Pragma"), F("no-cache")); + WebServer->sendHeader(F("Expires"), F("-1")); +#ifndef ARDUINO_ESP8266_RELEASE_2_3_0 + WebServer->sendHeader(F("Access-Control-Allow-Origin"), F("*")); +#endif +} + +/********************************************************************************************** +* HTTP Content Page handler +**********************************************************************************************/ + +void WSSend(int code, int ctype, const String& content) +{ + char ct[25]; // strlen("application/octet-stream") +1 = Longest Content type string + WebServer->send(code, GetTextIndexed(ct, sizeof(ct), ctype, kContentTypes), content); +} + +/********************************************************************************************** +* HTTP Content Chunk handler +**********************************************************************************************/ + +void _WSContentSend(const String& content) // Low level sendContent for all core versions +{ + size_t len = content.length(); + +#ifdef ARDUINO_ESP8266_RELEASE_2_3_0 + const char * footer = "\r\n"; + char chunk_size[11]; + sprintf(chunk_size, "%x\r\n", len); + WebServer->sendContent(String() + chunk_size + content + footer); +#else + WebServer->sendContent(content); +#endif + +#ifdef USE_DEBUG_DRIVER + ShowFreeMem(PSTR("WSContentSend")); +#endif +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("HTP: Chunk size %d"), len); +} + +void WSContentFlush() +{ + if (chunk_buffer.length() > 0) { + _WSContentSend(chunk_buffer); // Flush chunk buffer + chunk_buffer = ""; + } +} + +void WSContentSend_P(const char* formatP, ...) // Content send snprintf_P char data +{ + // This uses char strings. Be aware of sending %% if % is needed + va_list arg; + va_start(arg, formatP); + int len = vsnprintf_P(mqtt_data, sizeof(mqtt_data), formatP, arg); + va_end(arg); + + if (0 == len) { // No content + return; + } + else if (len == sizeof(mqtt_data)) { + AddLog_P(LOG_LEVEL_INFO, PSTR("HTP: Content too large")); + } + else if (len < CHUNKED_BUFFER_SIZE) { // Append chunk buffer with small content + chunk_buffer += mqtt_data; + len = chunk_buffer.length(); + } + + if (len >= CHUNKED_BUFFER_SIZE) { // Either content or chunk buffer is oversize + WSContentFlush(); // Send chunk buffer before possible content oversize + } + if (strlen(mqtt_data) >= CHUNKED_BUFFER_SIZE) { // Content is oversize + _WSContentSend(mqtt_data); // Send content + } +} + +void WSContentStart_P(const char* title, bool auth) +{ + if (auth && (Settings.web_password[0] != 0) && !WebServer->authenticate(WEB_USERNAME, Settings.web_password)) { + return WebServer->requestAuthentication(); + } + + WebServer->client().flush(); + WebServer->setContentLength(CONTENT_LENGTH_UNKNOWN); + WSHeaderSend(); +#ifdef ARDUINO_ESP8266_RELEASE_2_3_0 + WebServer->sendHeader(F("Accept-Ranges"),F("none")); + WebServer->sendHeader(F("Transfer-Encoding"),F("chunked")); +#endif + WSSend(200, CT_HTML, ""); // Signal start of chunked content + chunk_buffer = ""; + + char ctitle[strlen_P(title) +1]; + strcpy_P(ctitle, title); // Get title from flash to RAM + WSContentSend_P(HTTP_HEAD, Settings.friendlyname[0], ctitle); +} + +void WSContentStart_P(const char* title) +{ + WSContentStart_P(title, true); +} + +void WSContentSendStyle_P(const char* style) +{ + if (WifiIsInManagerMode()) { if (WifiConfigCounter()) { - page.replace(F(""), FPSTR(HTTP_SCRIPT_COUNTER)); - page.replace(F(""), F("")); - page += FPSTR(HTTP_COUNTER); + WSContentSend_P(HTTP_SCRIPT_COUNTER); } } - page += FPSTR(HTTP_END); - page.replace(F("{mv"), my_version); - SetHeader(); - - ShowFreeMem(PSTR("ShowPage")); - - WebServer->send(200, FPSTR(HDR_CTYPE_HTML), page); + WSContentSend_P(HTTP_HEAD_STYLE1); + WSContentSend_P(HTTP_HEAD_STYLE2); + WSContentSend_P(style); + WSContentSend_P(HTTP_HEAD_STYLE3, ModuleName().c_str(), Settings.friendlyname[0], WSNetworkInfo().c_str()); } -void ShowPage(String &page) +void WSContentSendStyle(void) { - ShowPage(page, true); + WSContentSendStyle_P(PSTR("")); } -/*-------------------------------------------------------------------------------------------*/ +void WSContentButton(uint8_t title_index) +{ + char action[4]; + char title[32]; + + if (title_index <= BUTTON_RESET_CONFIGURATION) { + char confirm[64]; + WSContentSend_P(PSTR("

"), + GetTextIndexed(action, sizeof(action), title_index, kButtonAction), + GetTextIndexed(confirm, sizeof(confirm), title_index, kButtonConfirm), + (!title_index) ? "rst" : "non", + GetTextIndexed(title, sizeof(title), title_index, kButtonTitle)); + } else { + WSContentSend_P(PSTR("

"), + GetTextIndexed(action, sizeof(action), title_index, kButtonAction), + GetTextIndexed(title, sizeof(title), title_index, kButtonTitle)); + } +} + +void WSContentSpaceButton(uint8_t title_index) +{ + WSContentSend_P(PSTR("
")); // 5px padding + WSContentButton(title_index); +} + +void WSContentEnd(void) +{ + if (WifiIsInManagerMode()) { + if (WifiConfigCounter()) { + WSContentSend_P(HTTP_COUNTER); + } + } + WSContentSend_P(HTTP_END, my_version); + WSContentFlush(); // Flush chunk buffer + _WSContentSend(""); // Signal end of chunked content + WebServer->client().stop(); +} + +/*********************************************************************************************/ void WebRestart(uint8_t type) { @@ -543,29 +756,25 @@ void WebRestart(uint8_t type) // type 2 = restart after config change with possible ip address change too AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_RESTART); - String page = FPSTR(HTTP_HEAD); - page += FPSTR(HTTP_HEAD_STYLE); + bool reset_only = (HTTP_MANAGER_RESET_ONLY == webserver_state); + WSContentStart_P((type) ? S_SAVE_CONFIGURATION : S_RESTART, !reset_only); + WSContentSend_P(HTTP_SCRIPT_RELOAD); + WSContentSendStyle(); if (type) { - page.replace(F("{v}"), FPSTR(S_SAVE_CONFIGURATION)); - page += F("
" D_CONFIGURATION_SAVED "
"); + WSContentSend_P(PSTR("
" D_CONFIGURATION_SAVED "
")); if (2 == type) { - page += F("
" D_TRYING_TO_CONNECT "
"); + WSContentSend_P(PSTR("
" D_TRYING_TO_CONNECT "
")); } - page += F("
"); + WSContentSend_P(PSTR("
")); } - else { - page.replace(F("{v}"), FPSTR(S_RESTART)); - } - - page += FPSTR(HTTP_MSG_RSTRT); - if (HTTP_MANAGER == webserver_state) { + WSContentSend_P(HTTP_MSG_RSTRT); + if (HTTP_MANAGER == webserver_state || reset_only) { webserver_state = HTTP_ADMIN; } else { - page += FPSTR(HTTP_BTN_MAIN); + WSContentSpaceButton(BUTTON_MAIN); } - page.replace(F(""), FPSTR(HTTP_SCRIPT_RELOAD)); - ShowPage(page); + WSContentEnd(); ShowWebSource(SRC_WEBGUI); restart_flag = 2; @@ -575,124 +784,133 @@ void WebRestart(uint8_t type) void HandleWifiLogin(void) { - String page = FPSTR(HTTP_HEAD); - page.replace(F("{v}"), FPSTR( D_CONFIGURE_WIFI )); - page += FPSTR(HTTP_HEAD_STYLE); - page += FPSTR(HTTP_FORM_LOGIN); - ShowPage(page, false); // false means show page no matter if the client has or has not credentials + WSContentStart_P(S_CONFIGURE_WIFI, false); // false means show page no matter if the client has or has not credentials + WSContentSendStyle(); + WSContentSend_P(HTTP_FORM_LOGIN); + + if (HTTP_MANAGER_RESET_ONLY == webserver_state) { + WSContentSpaceButton(BUTTON_RESTART); +#ifndef FIRMWARE_MINIMAL + WSContentSpaceButton(BUTTON_RESET_CONFIGURATION); +#endif // FIRMWARE_MINIMAL + } + + WSContentEnd(); } void HandleRoot(void) { - AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_MAIN_MENU); - if (CaptivePortal()) { return; } // If captive portal redirect instead of displaying the page. - if ( WebServer->hasArg("rstrt") ) { + if (WebServer->hasArg("rst")) { WebRestart(0); return; } - if (HTTP_MANAGER == webserver_state) { -#ifndef BE_MINIMAL - if ((Settings.web_password[0] != 0) && !(WebServer->hasArg("USER1")) && !(WebServer->hasArg("PASS1"))) { + if (WifiIsInManagerMode()) { +#ifndef FIRMWARE_MINIMAL + if ((Settings.web_password[0] != 0) && !(WebServer->hasArg("USER1")) && !(WebServer->hasArg("PASS1")) && HTTP_MANAGER_RESET_ONLY != webserver_state) { HandleWifiLogin(); } else { -/* - char tmp1[100]; - WebGetArg("USER1", tmp1, sizeof(tmp1)); - char tmp2[100]; - WebGetArg("PASS1", tmp2, sizeof(tmp2)); - if (!(Settings.web_password[0] != 0) || (!(!strcmp(tmp1, WEB_USERNAME) && !strcmp(tmp2, Settings.web_password)))) { -*/ - if (!(Settings.web_password[0] != 0) || ((WebServer->arg("USER1") == WEB_USERNAME ) && (WebServer->arg("PASS1") == Settings.web_password ))) { + if (!(Settings.web_password[0] != 0) || ((WebServer->arg("USER1") == WEB_USERNAME ) && (WebServer->arg("PASS1") == Settings.web_password ) || HTTP_MANAGER_RESET_ONLY == webserver_state)) { HandleWifiConfiguration(); } else { // wrong user and pass HandleWifiLogin(); } } -#endif // Not BE_MINIMAL - } else { - char stemp[10]; - String page = FPSTR(HTTP_HEAD); - page.replace(F("{v}"), FPSTR(S_MAIN_MENU)); - page += FPSTR(HTTP_SCRIPT_ROOT); - page += FPSTR(HTTP_HEAD_STYLE); - page.replace(F(""), F("")); - - page += F("
"); - if (devices_present) { - if (light_type) { - if ((LST_COLDWARM == (light_type &7)) || (LST_RGBWC == (light_type &7))) { - snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_MSG_SLIDER1, LightGetColorTemp()); - page += mqtt_data; - } - snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_MSG_SLIDER2, Settings.light_dimmer); - page += mqtt_data; - } - page += FPSTR(HTTP_TABLE100); - page += F("
"); - if (SONOFF_IFAN02 == Settings.module) { - snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_DEVICE_CONTROL, 36, 1, D_BUTTON_TOGGLE, ""); - page += mqtt_data; - for (byte i = 0; i < MAX_FAN_SPEED; i++) { - snprintf_P(stemp, sizeof(stemp), PSTR("%d"), i); - snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_DEVICE_CONTROL, 16, i +2, stemp, ""); - page += mqtt_data; - } - } else { - for (byte idx = 1; idx <= devices_present; idx++) { - snprintf_P(stemp, sizeof(stemp), PSTR(" %d"), idx); - snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_DEVICE_CONTROL, - 100 / devices_present, idx, (devices_present < 5) ? D_BUTTON_TOGGLE : "", (devices_present > 1) ? stemp : ""); - page += mqtt_data; - } - } - page += F("
%s
"); - } - if (SONOFF_BRIDGE == Settings.module) { - page += FPSTR(HTTP_TABLE100); - page += F("
"); - } - -#ifndef BE_MINIMAL - mqtt_data[0] = '\0'; - XdrvCall(FUNC_WEB_ADD_MAIN_BUTTON); - XsnsCall(FUNC_WEB_ADD_MAIN_BUTTON); - page += String(mqtt_data); -#endif // Not BE_MINIMAL - - if (HTTP_ADMIN == webserver_state) { - page += FPSTR(HTTP_BTN_MENU1); - page += FPSTR(HTTP_BTN_RSTRT); - } - ShowPage(page); +#endif // Not FIRMWARE_MINIMAL + return; } + + if (HandleRootStatusRefresh()) { + return; + } + + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_MAIN_MENU); + + char stemp[5]; + + WSContentStart_P(S_MAIN_MENU); + WSContentSend_P(HTTP_SCRIPT_ROOT, Settings.web_refresh); + WSContentSendStyle(); + + WSContentSend_P(PSTR("
")); + if (devices_present) { + if (light_type) { + if ((LST_COLDWARM == (light_type &7)) || (LST_RGBWC == (light_type &7))) { + WSContentSend_P(HTTP_MSG_SLIDER1, LightGetColorTemp()); + } + WSContentSend_P(HTTP_MSG_SLIDER2, Settings.light_dimmer); + } + WSContentSend_P(HTTP_TABLE100); + WSContentSend_P(PSTR("")); + if (SONOFF_IFAN02 == my_module_type) { + WSContentSend_P(HTTP_DEVICE_CONTROL, 36, 1, D_BUTTON_TOGGLE, ""); + for (uint8_t i = 0; i < MAX_FAN_SPEED; i++) { + snprintf_P(stemp, sizeof(stemp), PSTR("%d"), i); + WSContentSend_P(HTTP_DEVICE_CONTROL, 16, i +2, stemp, ""); + } + } else { + for (uint8_t idx = 1; idx <= devices_present; idx++) { + snprintf_P(stemp, sizeof(stemp), PSTR(" %d"), idx); + WSContentSend_P(HTTP_DEVICE_CONTROL, 100 / devices_present, idx, (devices_present < 5) ? D_BUTTON_TOGGLE : "", (devices_present > 1) ? stemp : ""); + } + } + WSContentSend_P(PSTR("")); + } + if (SONOFF_BRIDGE == my_module_type) { + WSContentSend_P(HTTP_TABLE100); + WSContentSend_P(PSTR("")); + uint8_t idx = 0; + for (uint8_t i = 0; i < 4; i++) { + if (idx > 0) { WSContentSend_P(PSTR("")); } + for (uint8_t j = 0; j < 4; j++) { + idx++; + WSContentSend_P(PSTR(""), idx, idx); // &k is related to WebGetArg("k", tmp, sizeof(tmp)); + } + } + WSContentSend_P(PSTR("")); + } + +#ifndef FIRMWARE_MINIMAL + XdrvCall(FUNC_WEB_ADD_MAIN_BUTTON); + XsnsCall(FUNC_WEB_ADD_MAIN_BUTTON); +#endif // Not FIRMWARE_MINIMAL + + if (HTTP_ADMIN == webserver_state) { +#ifdef FIRMWARE_MINIMAL + WSContentSpaceButton(BUTTON_FIRMWARE_UPGRADE); +#else + WSContentSpaceButton(BUTTON_CONFIGURATION); + WSContentButton(BUTTON_INFORMATION); + WSContentButton(BUTTON_FIRMWARE_UPGRADE); +#endif // Not FIRMWARE_MINIMAL + WSContentButton(BUTTON_CONSOLE); + WSContentButton(BUTTON_RESTART); + } + WSContentEnd(); } -void HandleAjaxStatusRefresh(void) +bool HandleRootStatusRefresh(void) { - if (!WebAuthenticate()) { return WebServer->requestAuthentication(); } + if (!WebAuthenticate()) { + WebServer->requestAuthentication(); + return true; + } - char svalue[80]; - char tmp[100]; + if (!WebServer->hasArg("m")) { // Status refresh requested + return false; + } - WebGetArg("o", tmp, sizeof(tmp)); + char tmp[8]; // WebGetArg numbers only + char svalue[32]; // Command and number parameter + + WebGetArg("o", tmp, sizeof(tmp)); // 1 - 16 Device number for button Toggle or Fanspeed if (strlen(tmp)) { ShowWebSource(SRC_WEBGUI); uint8_t device = atoi(tmp); - if (SONOFF_IFAN02 == Settings.module) { + if (SONOFF_IFAN02 == my_module_type) { if (device < 2) { ExecuteCommandPower(1, POWER_TOGGLE, SRC_IGNORE); } else { @@ -703,17 +921,17 @@ void HandleAjaxStatusRefresh(void) ExecuteCommandPower(device, POWER_TOGGLE, SRC_IGNORE); } } - WebGetArg("d", tmp, sizeof(tmp)); + WebGetArg("d", tmp, sizeof(tmp)); // 0 - 100 Dimmer value if (strlen(tmp)) { snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_DIMMER " %s"), tmp); ExecuteWebCommand(svalue, SRC_WEBGUI); } - WebGetArg("t", tmp, sizeof(tmp)); + WebGetArg("t", tmp, sizeof(tmp)); // 153 - 500 Color temperature if (strlen(tmp)) { snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_COLORTEMPERATURE " %s"), tmp); ExecuteWebCommand(svalue, SRC_WEBGUI); } - WebGetArg("k", tmp, sizeof(tmp)); + WebGetArg("k", tmp, sizeof(tmp)); // 1 - 16 Pre defined RF keys if (strlen(tmp)) { snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_RFKEY "%s"), tmp); ExecuteWebCommand(svalue, SRC_WEBGUI); @@ -732,7 +950,7 @@ void HandleAjaxStatusRefresh(void) if (devices_present) { snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s{t}"), mqtt_data); uint8_t fsize = (devices_present < 5) ? 70 - (devices_present * 8) : 32; - if (SONOFF_IFAN02 == Settings.module) { + if (SONOFF_IFAN02 == my_module_type) { snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_DEVICE_STATE, mqtt_data, 36, (bitRead(power, 0)) ? "bold" : "normal", 54, GetStateText(bitRead(power, 0))); uint8_t fanspeed = GetFanspeed(); @@ -740,7 +958,7 @@ void HandleAjaxStatusRefresh(void) snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_DEVICE_STATE, mqtt_data, 64, (fanspeed) ? "bold" : "normal", 54, (fanspeed) ? svalue : GetStateText(0)); } else { - for (byte idx = 1; idx <= devices_present; idx++) { + for (uint8_t idx = 1; idx <= devices_present; idx++) { snprintf_P(svalue, sizeof(svalue), PSTR("%d"), bitRead(power, idx -1)); snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_DEVICE_STATE, mqtt_data, 100 / devices_present, (bitRead(power, idx -1)) ? "bold" : "normal", fsize, (devices_present < 5) ? GetStateText(bitRead(power, idx -1)) : svalue); @@ -748,47 +966,167 @@ void HandleAjaxStatusRefresh(void) } snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s"), mqtt_data); } - WebServer->send(200, FPSTR(HDR_CTYPE_HTML), mqtt_data); -} - -boolean HttpUser(void) -{ - boolean status = (HTTP_USER == webserver_state); - if (status) { HandleRoot(); } - return status; + WSHeaderSend(); + WSSend(200, CT_HTML, mqtt_data); + return true; } /*-------------------------------------------------------------------------------------------*/ -#ifndef BE_MINIMAL +#ifndef FIRMWARE_MINIMAL void HandleConfiguration(void) { - if (HttpUser()) { return; } - if (!WebAuthenticate()) { return WebServer->requestAuthentication(); } + if (!HttpCheckPriviledgedAccess()) { return; } + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURATION); - String page = FPSTR(HTTP_HEAD); - page.replace(F("{v}"), FPSTR(S_CONFIGURATION)); - page += FPSTR(HTTP_HEAD_STYLE); - page += FPSTR(HTTP_BTN_MENU_MODULE); + WSContentStart_P(S_CONFIGURATION); + WSContentSendStyle(); + + WSContentButton(BUTTON_MODULE); + WSContentButton(BUTTON_WIFI); - mqtt_data[0] = '\0'; XdrvCall(FUNC_WEB_ADD_BUTTON); XsnsCall(FUNC_WEB_ADD_BUTTON); - page += String(mqtt_data); - page += FPSTR(HTTP_BTN_MENU4); - page += FPSTR(HTTP_BTN_MAIN); - ShowPage(page); + WSContentButton(BUTTON_LOGGING); + WSContentButton(BUTTON_OTHER); + WSContentButton(BUTTON_TEMPLATE); + + WSContentSpaceButton(BUTTON_RESET_CONFIGURATION); + WSContentButton(BUTTON_BACKUP); + WSContentButton(BUTTON_RESTORE); + + WSContentSpaceButton(BUTTON_MAIN); + WSContentEnd(); +} + +/*-------------------------------------------------------------------------------------------*/ + +void HandleTemplateConfiguration(void) +{ + if (!HttpCheckPriviledgedAccess()) { return; } + + if (WebServer->hasArg("save")) { + TemplateSaveSettings(); + WebRestart(1); + return; + } + + char stemp[20]; // Template number and Sensor name + + if (WebServer->hasArg("m")) { + String page = ""; + for (uint8_t i = 0; i < MAXMODULE; i++) { // "}2'%d'>%s (%d)}3" - "}2'0'>Sonoff Basic (1)}3" + uint8_t midx = pgm_read_byte(kModuleNiceList + i); + snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_MODULE_TEMPLATE_REPLACE, midx, AnyModuleName(midx).c_str(), midx +1); + page += mqtt_data; + } + WSSend(200, CT_PLAIN, page); + return; + } + + WebGetArg("t", stemp, sizeof(stemp)); // 0 - 69 Template number + if (strlen(stemp)) { + uint8_t module = atoi(stemp); + uint8_t module_save = Settings.module; + Settings.module = module; + myio cmodule; + ModuleGpios(&cmodule); + gpio_flag flag = ModuleFlag(); + Settings.module = module_save; + + String page = AnyModuleName(module); // NAME: Generic + page += F("}1"); // Field separator + + for (uint8_t i = 0; i < sizeof(kGpioNiceList); i++) { // GPIO: }2'0'>None (0)}3}2'17'>Button1 (17)}3... + + if (1 == i) { + snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_MODULE_TEMPLATE_REPLACE, 255, D_SENSOR_USER, 255); // }2'255'>User (255)}3 + page += mqtt_data; + } + + uint8_t midx = pgm_read_byte(kGpioNiceList + i); + snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_MODULE_TEMPLATE_REPLACE, midx, GetTextIndexed(stemp, sizeof(stemp), midx, kSensorNames), midx); + page += mqtt_data; + } + + page += F("}1"); // Field separator + mqtt_data[0] = '\0'; + for (uint8_t i = 0; i < sizeof(cmodule); i++) { // 17,148,29,149,7,255,255,255,138,255,139,255,255 + if ((i < 6) || ((i > 8) && (i != 11))) { // Ignore flash pins GPIO06, 7, 8 and 11 + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s%s%d"), mqtt_data, (i>0)?",":"", cmodule.io[i]); + } + } + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s}1%d}1%d"), mqtt_data, flag, Settings.user_template_base); // FLAG: 1 BASE: 17 + page += mqtt_data; + + WSSend(200, CT_PLAIN, page); + return; + } + + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_TEMPLATE); + + WSContentStart_P(S_CONFIGURE_TEMPLATE); + WSContentSend_P(HTTP_SCRIPT_MODULE_TEMPLATE); + WSContentSend_P(HTTP_SCRIPT_TEMPLATE); + WSContentSendStyle(); + WSContentSend_P(HTTP_FORM_TEMPLATE); + + WSContentSend_P(PSTR("
")); + for (uint8_t i = 0; i < 17; i++) { + if ((i < 6) || ((i > 8) && (i != 11))) { // Ignore flash pins GPIO06, 7, 8 and 11 + WSContentSend_P(PSTR("" D_GPIO "%d"), + (0==i)?" style='width:74px'":"", i, ((9==i)||(10==i))? "ESP8285" :"", (0==i)?" style='width:176px'":"", i, i); + } + } + WSContentSend_P(PSTR("
%s
")); + + WSContentSend_P(HTTP_FORM_TEMPLATE_FLAG); + WSContentSend_P(HTTP_FORM_END); + WSContentSpaceButton(BUTTON_CONFIGURATION); + WSContentEnd(); +} + +void TemplateSaveSettings(void) +{ + char tmp[sizeof(Settings.user_template.name)]; // WebGetArg NAME and GPIO/BASE/FLAG byte value + char webindex[5]; // WebGetArg name + char svalue[128]; // Template command string + + WebGetArg("s1", tmp, sizeof(tmp)); // NAME + snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_TEMPLATE " {\"" D_JSON_NAME "\":\"%s\",\"" D_JSON_GPIO "\":["), tmp); + + uint8_t j = 0; + for (uint8_t i = 0; i < sizeof(Settings.user_template.gp); i++) { + if (6 == i) { j = 9; } + if (8 == i) { j = 12; } + snprintf_P(webindex, sizeof(webindex), PSTR("g%d"), j); + WebGetArg(webindex, tmp, sizeof(tmp)); // GPIO + uint8_t gpio = atoi(tmp); + snprintf_P(svalue, sizeof(svalue), PSTR("%s%s%d"), svalue, (i>0)?",":"", gpio); + j++; + } + + uint8_t flag = 0; + for (uint8_t i = 0; i < GPIO_FLAG_USED; i++) { + snprintf_P(webindex, sizeof(webindex), PSTR("c%d"), i); + uint8_t state = WebServer->hasArg(webindex) << i; // FLAG + flag += state; + } + WebGetArg("g99", tmp, sizeof(tmp)); // BASE + uint8_t base = atoi(tmp) +1; + + snprintf_P(svalue, sizeof(svalue), PSTR("%s],\"" D_JSON_FLAG "\":%d,\"" D_JSON_BASE "\":%d}"), svalue, flag, base); + ExecuteWebCommand(svalue, SRC_WEBGUI); } /*-------------------------------------------------------------------------------------------*/ void HandleModuleConfiguration(void) { - if (HttpUser()) { return; } - if (!WebAuthenticate()) { return WebServer->requestAuthentication(); } + if (!HttpCheckPriviledgedAccess()) { return; } if (WebServer->hasArg("save")) { ModuleSaveSettings(); @@ -796,213 +1134,213 @@ void HandleModuleConfiguration(void) return; } - char stemp[20]; + char stemp[20]; // Sensor name uint8_t midx; - mytmplt cmodule; - memcpy_P(&cmodule, &kModules[Settings.module], sizeof(cmodule)); + myio cmodule; + ModuleGpios(&cmodule); if (WebServer->hasArg("m")) { String page = ""; - for (byte i = 0; i < MAXMODULE; i++) { - midx = pgm_read_byte(kModuleNiceList + i); - snprintf_P(stemp, sizeof(stemp), kModules[midx].name); - snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SCRIPT_MODULE3, midx, stemp, midx +1); + uint8_t vidx = 0; + for (uint8_t i = 0; i <= MAXMODULE; i++) { // "}2'%d'>%s (%d)}3" - "}2'255'>UserTemplate (0)}3" - "}2'0'>Sonoff Basic (1)}3" + if (0 == i) { + midx = USER_MODULE; + vidx = 0; + } else { + midx = pgm_read_byte(kModuleNiceList + i -1); + vidx = midx +1; + } + snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_MODULE_TEMPLATE_REPLACE, midx, AnyModuleName(midx).c_str(), vidx); page += mqtt_data; } - page += "}3"; // String separator means do not use "}3" in Module name and Sensor name - for (byte j = 0; j < sizeof(kGpioNiceList); j++) { + WSSend(200, CT_PLAIN, page); + return; + } + + if (WebServer->hasArg("g")) { + String page = ""; + for (uint8_t j = 0; j < sizeof(kGpioNiceList); j++) { midx = pgm_read_byte(kGpioNiceList + j); - if (!GetUsedInModule(midx, cmodule.gp.io)) { - snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SCRIPT_MODULE3, midx, GetTextIndexed(stemp, sizeof(stemp), midx, kSensorNames), midx); + if (!GetUsedInModule(midx, cmodule.io)) { + snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_MODULE_TEMPLATE_REPLACE, midx, GetTextIndexed(stemp, sizeof(stemp), midx, kSensorNames), midx); page += mqtt_data; } } - WebServer->send(200, FPSTR(HDR_CTYPE_PLAIN), page); + WSSend(200, CT_PLAIN, page); return; } AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_MODULE); - String page = FPSTR(HTTP_HEAD); - page.replace(F("{v}"), FPSTR(S_CONFIGURE_MODULE)); - page += FPSTR(HTTP_SCRIPT_MODULE1); - page.replace(F("}4"), String(Settings.module)); - for (byte i = 0; i < MAX_GPIO_PIN; i++) { - if (GPIO_USER == ValidGPIO(i, cmodule.gp.io[i])) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("sk(%d,%d);"), my_module.gp.io[i], i); // g0 - g16 - page += mqtt_data; + WSContentStart_P(S_CONFIGURE_MODULE); + WSContentSend_P(HTTP_SCRIPT_MODULE_TEMPLATE); + WSContentSend_P(HTTP_SCRIPT_MODULE1, Settings.module); + for (uint8_t i = 0; i < sizeof(cmodule); i++) { + if (ValidGPIO(i, cmodule.io[i])) { + WSContentSend_P(PSTR("sk(%d,%d);"), my_module.io[i], i); // g0 - g16 } } - page += FPSTR(HTTP_SCRIPT_MODULE2); - page += FPSTR(HTTP_HEAD_STYLE); - page.replace(F(""), F("")); - page += FPSTR(HTTP_FORM_MODULE); - snprintf_P(stemp, sizeof(stemp), kModules[MODULE].name); - page.replace(F("{mt"), stemp); - page += F("
"); - for (byte i = 0; i < MAX_GPIO_PIN; i++) { - if (GPIO_USER == ValidGPIO(i, cmodule.gp.io[i])) { + WSContentSend_P(HTTP_SCRIPT_MODULE2); + WSContentSendStyle(); + WSContentSend_P(HTTP_FORM_MODULE, AnyModuleName(MODULE).c_str()); + for (uint8_t i = 0; i < sizeof(cmodule); i++) { + if (ValidGPIO(i, cmodule.io[i])) { snprintf_P(stemp, 3, PINS_WEMOS +i*2); - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR(""), - (WEMOS==Settings.module)?stemp:"", i, (0==i)? D_SENSOR_BUTTON "1":(1==i)? D_SERIAL_OUT :(3==i)? D_SERIAL_IN :(9==i)? "ESP8285" :(10==i)? "ESP8285" :(12==i)? D_SENSOR_RELAY "1":(13==i)? D_SENSOR_LED "1i":(14==i)? D_SENSOR :"", i, i); - page += mqtt_data; + WSContentSend_P(PSTR(""), + (WEMOS==my_module_type)?stemp:"", i, (0==i)? D_SENSOR_BUTTON "1":(1==i)? D_SERIAL_OUT :(3==i)? D_SERIAL_IN :((9==i)||(10==i))? "ESP8285" :(12==i)? D_SENSOR_RELAY "1":(13==i)? D_SENSOR_LED "1i":(14==i)? D_SENSOR :"", i, i); } } - page += F("
%s " D_GPIO "%d %s
%s " D_GPIO "%d %s
"); - page += FPSTR(HTTP_FORM_END); - page += FPSTR(HTTP_BTN_CONF); - ShowPage(page); + WSContentSend_P(PSTR("")); + WSContentSend_P(HTTP_FORM_END); + WSContentSpaceButton(BUTTON_CONFIGURATION); + WSContentEnd(); } void ModuleSaveSettings(void) { - char tmp[100]; - char stemp[TOPSZ]; + char tmp[8]; // WebGetArg numbers only + char webindex[5]; // WebGetArg name WebGetArg("g99", tmp, sizeof(tmp)); - byte new_module = (!strlen(tmp)) ? MODULE : atoi(tmp); + uint8_t new_module = (!strlen(tmp)) ? MODULE : atoi(tmp); Settings.last_module = Settings.module; Settings.module = new_module; - mytmplt cmodule; - memcpy_P(&cmodule, &kModules[Settings.module], sizeof(cmodule)); + SetModuleType(); + myio cmodule; + ModuleGpios(&cmodule); String gpios = ""; - for (byte i = 0; i < MAX_GPIO_PIN; i++) { + for (uint8_t i = 0; i < sizeof(cmodule); i++) { if (Settings.last_module != new_module) { - Settings.my_gp.io[i] = 0; + Settings.my_gp.io[i] = GPIO_NONE; } else { - if (GPIO_USER == ValidGPIO(i, cmodule.gp.io[i])) { - snprintf_P(stemp, sizeof(stemp), PSTR("g%d"), i); - WebGetArg(stemp, tmp, sizeof(tmp)); + if (ValidGPIO(i, cmodule.io[i])) { + snprintf_P(webindex, sizeof(webindex), PSTR("g%d"), i); + WebGetArg(webindex, tmp, sizeof(tmp)); Settings.my_gp.io[i] = (!strlen(tmp)) ? 0 : atoi(tmp); gpios += F(", " D_GPIO ); gpios += String(i); gpios += F(" "); gpios += String(Settings.my_gp.io[i]); } } } - snprintf_P(stemp, sizeof(stemp), kModules[Settings.module].name); - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_MODULE "%s " D_CMND_MODULE "%s"), stemp, gpios.c_str()); - AddLog(LOG_LEVEL_INFO); + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MODULE "%s " D_CMND_MODULE "%s"), ModuleName().c_str(), gpios.c_str()); } /*-------------------------------------------------------------------------------------------*/ String htmlEscape(String s) { - s.replace("&", "&"); - s.replace("<", "<"); - s.replace(">", ">"); - s.replace("\"", """); - s.replace("'", "'"); - s.replace("/", "/"); - return s; + s.replace("&", "&"); + s.replace("<", "<"); + s.replace(">", ">"); + s.replace("\"", """); + s.replace("'", "'"); + s.replace("/", "/"); + return s; } void HandleWifiConfiguration(void) { - if (HttpUser()) { return; } - if (!WebAuthenticate()) { return WebServer->requestAuthentication(); } + if (!HttpCheckPriviledgedAccess(!WifiIsInManagerMode())) { return; } AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_WIFI); - if (WebServer->hasArg("save")) { + if (WebServer->hasArg("save") && HTTP_MANAGER_RESET_ONLY != webserver_state) { WifiSaveSettings(); WebRestart(2); return; } - String page = FPSTR(HTTP_HEAD); - page.replace(F("{v}"), FPSTR(S_CONFIGURE_WIFI)); - page += FPSTR(HTTP_SCRIPT_WIFI); - page += FPSTR(HTTP_HEAD_STYLE); + WSContentStart_P(S_CONFIGURE_WIFI, !WifiIsInManagerMode()); + WSContentSend_P(HTTP_SCRIPT_WIFI); + WSContentSendStyle(); - if (WebServer->hasArg("scan")) { + if (HTTP_MANAGER_RESET_ONLY != webserver_state) { + if (WebServer->hasArg("scan")) { #ifdef USE_EMULATION - UdpDisconnect(); + UdpDisconnect(); #endif // USE_EMULATION - int n = WiFi.scanNetworks(); - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI D_SCAN_DONE)); + int n = WiFi.scanNetworks(); + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI D_SCAN_DONE)); - if (0 == n) { - AddLog_P(LOG_LEVEL_DEBUG, S_LOG_WIFI, S_NO_NETWORKS_FOUND); - page += FPSTR(S_NO_NETWORKS_FOUND); - page += F(". " D_REFRESH_TO_SCAN_AGAIN "."); - } else { - //sort networks - int indices[n]; - for (int i = 0; i < n; i++) { - indices[i] = i; - } - - // RSSI SORT - for (int i = 0; i < n; i++) { - for (int j = i + 1; j < n; j++) { - if (WiFi.RSSI(indices[j]) > WiFi.RSSI(indices[i])) { - std::swap(indices[i], indices[j]); - } - } - } - - // remove duplicates ( must be RSSI sorted ) - if (remove_duplicate_access_points) { - String cssid; + if (0 == n) { + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_WIFI, S_NO_NETWORKS_FOUND); + WSContentSend_P(S_NO_NETWORKS_FOUND); + WSContentSend_P(PSTR(". " D_REFRESH_TO_SCAN_AGAIN ".")); + } else { + //sort networks + int indices[n]; + for (int i = 0; i < n; i++) { + indices[i] = i; + } + + // RSSI SORT for (int i = 0; i < n; i++) { - if (-1 == indices[i]) { continue; } - cssid = WiFi.SSID(indices[i]); for (int j = i + 1; j < n; j++) { - if (cssid == WiFi.SSID(indices[j])) { - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_WIFI D_DUPLICATE_ACCESSPOINT " %s"), WiFi.SSID(indices[j]).c_str()); - AddLog(LOG_LEVEL_DEBUG); - indices[j] = -1; // set dup aps to index -1 + if (WiFi.RSSI(indices[j]) > WiFi.RSSI(indices[i])) { + std::swap(indices[i], indices[j]); } } } - } - //display networks in page - for (int i = 0; i < n; i++) { - if (-1 == indices[i]) { continue; } // skip dups - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_WIFI D_SSID " %s, " D_BSSID " %s, " D_CHANNEL " %d, " D_RSSI " %d"), WiFi.SSID(indices[i]).c_str(), WiFi.BSSIDstr(indices[i]).c_str(), WiFi.channel(indices[i]), WiFi.RSSI(indices[i])); - AddLog(LOG_LEVEL_DEBUG); - int quality = WifiGetRssiAsQuality(WiFi.RSSI(indices[i])); - - if (minimum_signal_quality == -1 || minimum_signal_quality < quality) { - String item = FPSTR(HTTP_LNK_ITEM); - String rssiQ; - rssiQ += quality; - item.replace(F("{v}"), htmlEscape(WiFi.SSID(indices[i]))); - item.replace(F("{w}"), String(WiFi.channel(indices[i]))); - item.replace(F("{r}"), rssiQ); - uint8_t auth = WiFi.encryptionType(indices[i]); - item.replace(F("{i}"), (ENC_TYPE_WEP == auth) ? F(D_WEP) : (ENC_TYPE_TKIP == auth) ? F(D_WPA_PSK) : (ENC_TYPE_CCMP == auth) ? F(D_WPA2_PSK) : (ENC_TYPE_AUTO == auth) ? F(D_AUTO) : F("")); - page += item; - delay(0); - } else { - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI D_SKIPPING_LOW_QUALITY)); + // remove duplicates ( must be RSSI sorted ) + if (remove_duplicate_access_points) { + String cssid; + for (int i = 0; i < n; i++) { + if (-1 == indices[i]) { continue; } + cssid = WiFi.SSID(indices[i]); + for (int j = i + 1; j < n; j++) { + if (cssid == WiFi.SSID(indices[j])) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI D_DUPLICATE_ACCESSPOINT " %s"), WiFi.SSID(indices[j]).c_str()); + indices[j] = -1; // set dup aps to index -1 + } + } + } } + //display networks in page + for (int i = 0; i < n; i++) { + if (-1 == indices[i]) { continue; } // skip dups + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI D_SSID " %s, " D_BSSID " %s, " D_CHANNEL " %d, " D_RSSI " %d"), WiFi.SSID(indices[i]).c_str(), WiFi.BSSIDstr(indices[i]).c_str(), WiFi.channel(indices[i]), WiFi.RSSI(indices[i])); + int quality = WifiGetRssiAsQuality(WiFi.RSSI(indices[i])); + + if (minimum_signal_quality == -1 || minimum_signal_quality < quality) { + uint8_t auth = WiFi.encryptionType(indices[i]); + WSContentSend_P(PSTR("
%s (%d) %s %d%%
"), + htmlEscape(WiFi.SSID(indices[i])).c_str(), + WiFi.channel(indices[i]), + (ENC_TYPE_WEP == auth) ? D_WEP : (ENC_TYPE_TKIP == auth) ? D_WPA_PSK : (ENC_TYPE_CCMP == auth) ? D_WPA2_PSK : (ENC_TYPE_AUTO == auth) ? D_AUTO : "", + quality + ); + delay(0); + } else { + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI D_SKIPPING_LOW_QUALITY)); + } + + } + WSContentSend_P(PSTR("
")); } - page += "
"; + } else { + WSContentSend_P(PSTR("
")); } - } else { - page += FPSTR(HTTP_LNK_SCAN); + + // As WIFI_HOSTNAME may contain %s-%04d it cannot be part of HTTP_FORM_WIFI where it will exception + WSContentSend_P(HTTP_FORM_WIFI, Settings.sta_ssid[0], Settings.sta_ssid[1], WIFI_HOSTNAME, WIFI_HOSTNAME, Settings.hostname); + WSContentSend_P(HTTP_FORM_END); } - page += FPSTR(HTTP_FORM_WIFI); - page.replace(F("{h1"), Settings.hostname); - page.replace(F("{s1"), Settings.sta_ssid[0]); - page.replace(F("{s2"), Settings.sta_ssid[1]); - page += FPSTR(HTTP_FORM_END); - if (HTTP_MANAGER == webserver_state) { - page += FPSTR(HTTP_BTN_RSTRT); + if (WifiIsInManagerMode()) { + WSContentSpaceButton(BUTTON_RESTART); +#ifndef FIRMWARE_MINIMAL + WSContentSpaceButton(BUTTON_RESET_CONFIGURATION); +#endif // FIRMWARE_MINIMAL } else { - page += FPSTR(HTTP_BTN_CONF); + WSContentSpaceButton(BUTTON_CONFIGURATION); } -// ShowPage(page); - ShowPage(page, !(HTTP_MANAGER == webserver_state)); + WSContentEnd(); } void WifiSaveSettings(void) { - char tmp[100]; + char tmp[sizeof(Settings.sta_pwd[0])]; // Max length is currently 65 WebGetArg("h", tmp, sizeof(tmp)); strlcpy(Settings.hostname, (!strlen(tmp)) ? WIFI_HOSTNAME : tmp, sizeof(Settings.hostname)); @@ -1014,20 +1352,18 @@ void WifiSaveSettings(void) WebGetArg("s2", tmp, sizeof(tmp)); strlcpy(Settings.sta_ssid[1], (!strlen(tmp)) ? STA_SSID2 : tmp, sizeof(Settings.sta_ssid[1])); WebGetArg("p1", tmp, sizeof(tmp)); - strlcpy(Settings.sta_pwd[0], (!strlen(tmp)) ? "" : (strchr(tmp,'*')) ? Settings.sta_pwd[0] : tmp, sizeof(Settings.sta_pwd[0])); + strlcpy(Settings.sta_pwd[0], (!strlen(tmp)) ? "" : (strlen(tmp) < 5) ? Settings.sta_pwd[0] : tmp, sizeof(Settings.sta_pwd[0])); WebGetArg("p2", tmp, sizeof(tmp)); - strlcpy(Settings.sta_pwd[1], (!strlen(tmp)) ? "" : (strchr(tmp,'*')) ? Settings.sta_pwd[1] : tmp, sizeof(Settings.sta_pwd[1])); - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_WIFI D_CMND_HOSTNAME " %s, " D_CMND_SSID "1 %s, " D_CMND_SSID "2 %s"), - Settings.hostname, Settings.sta_ssid[0], Settings.sta_ssid[1]); - AddLog(LOG_LEVEL_INFO); + strlcpy(Settings.sta_pwd[1], (!strlen(tmp)) ? "" : (strlen(tmp) < 5) ? Settings.sta_pwd[1] : tmp, sizeof(Settings.sta_pwd[1])); + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_CMND_HOSTNAME " %s, " D_CMND_SSID "1 %s, " D_CMND_SSID "2 %s"), Settings.hostname, Settings.sta_ssid[0], Settings.sta_ssid[1]); } /*-------------------------------------------------------------------------------------------*/ void HandleLoggingConfiguration(void) { - if (HttpUser()) { return; } - if (!WebAuthenticate()) { return WebServer->requestAuthentication(); } + if (!HttpCheckPriviledgedAccess()) { return; } + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_LOGGING); if (WebServer->hasArg("save")) { @@ -1036,58 +1372,40 @@ void HandleLoggingConfiguration(void) return; } - String page = FPSTR(HTTP_HEAD); - page.replace(F("{v}"), FPSTR(S_CONFIGURE_LOGGING)); - page += FPSTR(HTTP_HEAD_STYLE); - - page += FPSTR(HTTP_FORM_LOG1); - for (byte idx = 0; idx < 3; idx++) { - page += FPSTR(HTTP_FORM_LOG2); - switch (idx) { - case 0: - page.replace(F("{b0"), F(D_SERIAL_LOG_LEVEL)); - page.replace(F("{b1"), STR(SERIAL_LOG_LEVEL)); - page.replace(F("{b2"), F("ls")); - for (byte i = LOG_LEVEL_NONE; i < LOG_LEVEL_ALL; i++) { - page.replace("{a" + String(i), (i == Settings.seriallog_level) ? F(" selected ") : F(" ")); - } - break; - case 1: - page.replace(F("{b0"), F(D_WEB_LOG_LEVEL)); - page.replace(F("{b1"), STR(WEB_LOG_LEVEL)); - page.replace(F("{b2"), F("lw")); - for (byte i = LOG_LEVEL_NONE; i < LOG_LEVEL_ALL; i++) { - page.replace("{a" + String(i), (i == Settings.weblog_level) ? F(" selected ") : F(" ")); - } - break; - case 2: - page.replace(F("{b0"), F(D_SYS_LOG_LEVEL)); - page.replace(F("{b1"), STR(SYS_LOG_LEVEL)); - page.replace(F("{b2"), F("ll")); - for (byte i = LOG_LEVEL_NONE; i < LOG_LEVEL_ALL; i++) { - page.replace("{a" + String(i), (i == Settings.syslog_level) ? F(" selected ") : F(" ")); - } - break; + WSContentStart_P(S_CONFIGURE_LOGGING); + WSContentSendStyle(); + WSContentSend_P(HTTP_FORM_LOG1); + char stemp1[32]; + char stemp2[32]; + uint8_t dlevel[3] = { LOG_LEVEL_INFO, LOG_LEVEL_INFO, LOG_LEVEL_NONE }; + for (uint8_t idx = 0; idx < 3; idx++) { + uint8_t llevel = (0==idx)?Settings.seriallog_level:(1==idx)?Settings.weblog_level:Settings.syslog_level; + WSContentSend_P(PSTR("

%s (%s)

")); } - page += FPSTR(HTTP_FORM_LOG3); - page.replace(F("{l2"), Settings.syslog_host); - page.replace(F("{l3"), String(Settings.syslog_port)); - page.replace(F("{l4"), String(Settings.tele_period)); - page += FPSTR(HTTP_FORM_END); - page += FPSTR(HTTP_BTN_CONF); - ShowPage(page); + WSContentSend_P(HTTP_FORM_LOG2, Settings.syslog_host, Settings.syslog_port, Settings.tele_period); + WSContentSend_P(HTTP_FORM_END); + WSContentSpaceButton(BUTTON_CONFIGURATION); + WSContentEnd(); } void LoggingSaveSettings(void) { - char tmp[100]; + char tmp[sizeof(Settings.syslog_host)]; // Max length is currently 33 - WebGetArg("ls", tmp, sizeof(tmp)); + WebGetArg("l0", tmp, sizeof(tmp)); Settings.seriallog_level = (!strlen(tmp)) ? SERIAL_LOG_LEVEL : atoi(tmp); - WebGetArg("lw", tmp, sizeof(tmp)); + WebGetArg("l1", tmp, sizeof(tmp)); Settings.weblog_level = (!strlen(tmp)) ? WEB_LOG_LEVEL : atoi(tmp); - WebGetArg("ll", tmp, sizeof(tmp)); + WebGetArg("l2", tmp, sizeof(tmp)); Settings.syslog_level = (!strlen(tmp)) ? SYS_LOG_LEVEL : atoi(tmp); syslog_level = Settings.syslog_level; syslog_timer = 0; @@ -1100,17 +1418,16 @@ void LoggingSaveSettings(void) if ((Settings.tele_period > 0) && (Settings.tele_period < 10)) { Settings.tele_period = 10; // Do not allow periods < 10 seconds } - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_LOG D_CMND_SERIALLOG " %d, " D_CMND_WEBLOG " %d, " D_CMND_SYSLOG " %d, " D_CMND_LOGHOST " %s, " D_CMND_LOGPORT " %d, " D_CMND_TELEPERIOD " %d"), + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_LOG D_CMND_SERIALLOG " %d, " D_CMND_WEBLOG " %d, " D_CMND_SYSLOG " %d, " D_CMND_LOGHOST " %s, " D_CMND_LOGPORT " %d, " D_CMND_TELEPERIOD " %d"), Settings.seriallog_level, Settings.weblog_level, Settings.syslog_level, Settings.syslog_host, Settings.syslog_port, Settings.tele_period); - AddLog(LOG_LEVEL_INFO); } /*-------------------------------------------------------------------------------------------*/ void HandleOtherConfiguration(void) { - if (HttpUser()) { return; } - if (!WebAuthenticate()) { return WebServer->requestAuthentication(); } + if (!HttpCheckPriviledgedAccess()) { return; } + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_OTHER); if (WebServer->hasArg("save")) { @@ -1119,44 +1436,48 @@ void HandleOtherConfiguration(void) return; } - char stemp[40]; + WSContentStart_P(S_CONFIGURE_OTHER); + WSContentSendStyle(); + + TemplateJson(); + char stemp[strlen(mqtt_data) +1]; + strlcpy(stemp, mqtt_data, sizeof(stemp)); // Get JSON template + WSContentSend_P(HTTP_FORM_OTHER, stemp, (USER_MODULE == Settings.module) ? " checked disabled" : "", (Settings.flag.mqtt_enabled) ? " checked" : ""); - String page = FPSTR(HTTP_HEAD); - page.replace(F("{v}"), FPSTR(S_CONFIGURE_OTHER)); - page += FPSTR(HTTP_HEAD_STYLE); - page += FPSTR(HTTP_FORM_OTHER); - page.replace(F("{r1"), (Settings.flag.mqtt_enabled) ? F(" checked") : F("")); uint8_t maxfn = (devices_present > MAX_FRIENDLYNAMES) ? MAX_FRIENDLYNAMES : (!devices_present) ? 1 : devices_present; - if (SONOFF_IFAN02 == Settings.module) { maxfn = 1; } - for (byte i = 0; i < maxfn; i++) { - page += FPSTR(HTTP_FORM_OTHER2); - page.replace(F("{1"), String(i +1)); - snprintf_P(stemp, sizeof(stemp), PSTR(FRIENDLY_NAME"%d"), i +1); - page.replace(F("{2"), (i) ? stemp : FRIENDLY_NAME); - page.replace(F("{3"), Settings.friendlyname[i]); + if (SONOFF_IFAN02 == my_module_type) { maxfn = 1; } + for (uint8_t i = 0; i < maxfn; i++) { + snprintf_P(stemp, sizeof(stemp), PSTR("%d"), i +1); + WSContentSend_P(PSTR("" D_FRIENDLY_NAME " %d (" FRIENDLY_NAME "%s)

"), + i +1, + (i) ? stemp : "", + i, i, + (i) ? stemp : "", + Settings.friendlyname[i]); } + #ifdef USE_EMULATION - page += FPSTR(HTTP_FORM_OTHER3a); - for (byte i = 0; i < EMUL_MAX; i++) { - page += FPSTR(HTTP_FORM_OTHER3b); - page.replace(F("{1"), String(i)); - page.replace(F("{2"), (i == Settings.flag2.emulation) ? F(" checked") : F("")); - page.replace(F("{3"), (i == EMUL_NONE) ? F(D_NONE) : (i == EMUL_WEMO) ? F(D_BELKIN_WEMO) : F(D_HUE_BRIDGE)); - page.replace(F("{4"), (i == EMUL_NONE) ? F("") : (i == EMUL_WEMO) ? F(" " D_SINGLE_DEVICE) : F(" " D_MULTI_DEVICE)); + WSContentSend_P(PSTR("

 " D_EMULATION " 

")); // Keep close to Friendlynames so do not use
+ for (uint8_t i = 0; i < EMUL_MAX; i++) { + WSContentSend_P(PSTR("%s %s
"), // Different id only used for labels + i, i, + (i == Settings.flag2.emulation) ? " checked" : "", + GetTextIndexed(stemp, sizeof(stemp), i, kEmulationOptions), + (i == EMUL_NONE) ? "" : (i == EMUL_WEMO) ? D_SINGLE_DEVICE : D_MULTI_DEVICE); } - page += F("
"); - page += F("

"); + WSContentSend_P(PSTR("

")); #endif // USE_EMULATION - page += FPSTR(HTTP_FORM_END); - page += FPSTR(HTTP_BTN_CONF); - ShowPage(page); + + WSContentSend_P(HTTP_FORM_END); + WSContentSpaceButton(BUTTON_CONFIGURATION); + WSContentEnd(); } void OtherSaveSettings(void) { - char tmp[100]; - char stemp[TOPSZ]; - char stemp2[TOPSZ]; + char tmp[128]; + char webindex[5]; + char friendlyname[sizeof(Settings.friendlyname[0])]; WebGetArg("p1", tmp, sizeof(tmp)); strlcpy(Settings.web_password, (!strlen(tmp)) ? "" : (strchr(tmp,'*')) ? Settings.web_password : tmp, sizeof(Settings.web_password)); @@ -1166,22 +1487,34 @@ void OtherSaveSettings(void) Settings.flag2.emulation = (!strlen(tmp)) ? 0 : atoi(tmp); #endif // USE_EMULATION snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_OTHER D_MQTT_ENABLE " %s, " D_CMND_EMULATION " %d, " D_CMND_FRIENDLYNAME), GetStateText(Settings.flag.mqtt_enabled), Settings.flag2.emulation); - for (byte i = 0; i < MAX_FRIENDLYNAMES; i++) { - snprintf_P(stemp, sizeof(stemp), PSTR("a%d"), i +1); - WebGetArg(stemp, tmp, sizeof(tmp)); - snprintf_P(stemp2, sizeof(stemp2), PSTR(FRIENDLY_NAME"%d"), i +1); - strlcpy(Settings.friendlyname[i], (!strlen(tmp)) ? (i) ? stemp2 : FRIENDLY_NAME : tmp, sizeof(Settings.friendlyname[i])); + for (uint8_t i = 0; i < MAX_FRIENDLYNAMES; i++) { + snprintf_P(webindex, sizeof(webindex), PSTR("a%d"), i); + WebGetArg(webindex, tmp, sizeof(tmp)); + snprintf_P(friendlyname, sizeof(friendlyname), PSTR(FRIENDLY_NAME"%d"), i +1); + strlcpy(Settings.friendlyname[i], (!strlen(tmp)) ? (i) ? friendlyname : FRIENDLY_NAME : tmp, sizeof(Settings.friendlyname[i])); snprintf_P(log_data, sizeof(log_data), PSTR("%s%s %s"), log_data, (i) ? "," : "", Settings.friendlyname[i]); } AddLog(LOG_LEVEL_INFO); + WebGetArg("t1", tmp, sizeof(tmp)); + if (strlen(tmp)) { // {"NAME":"12345678901234","GPIO":[255,255,255,255,255,255,255,255,255,255,255,255,255],"FLAG":255,"BASE":255} + char svalue[128]; + snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_TEMPLATE " %s"), tmp); + ExecuteWebCommand(svalue, SRC_WEBGUI); + + if (WebServer->hasArg("t2")) { + snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_MODULE " 0")); + ExecuteWebCommand(svalue, SRC_WEBGUI); + } + + } } /*-------------------------------------------------------------------------------------------*/ void HandleBackupConfiguration(void) { - if (HttpUser()) { return; } - if (!WebAuthenticate()) { return WebServer->requestAuthentication(); } + if (!HttpCheckPriviledgedAccess()) { return; } + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_BACKUP_CONFIGURATION)); if (!SettingsBufferAlloc()) { return; } @@ -1194,7 +1527,7 @@ void HandleBackupConfiguration(void) snprintf_P(attachment, sizeof(attachment), PSTR("attachment; filename=Config_%s_%s.dmp"), NoAlNumToUnderscore(friendlyname, Settings.friendlyname[0]), my_version); WebServer->sendHeader(F("Content-Disposition"), attachment); - WebServer->send(200, FPSTR(HDR_CTYPE_STREAM), ""); + WSSend(200, CT_STREAM, ""); uint16_t cfg_crc = Settings.cfg_crc; Settings.cfg_crc = GetSettingsCrc(); // Calculate crc (again) as it might be wrong when savedata = 0 (#3918) @@ -1224,39 +1557,34 @@ void HandleBackupConfiguration(void) void HandleResetConfiguration(void) { - if (HttpUser()) { return; } - if (!WebAuthenticate()) { return WebServer->requestAuthentication(); } - - char svalue[33]; + if (!HttpCheckPriviledgedAccess(!WifiIsInManagerMode())) { return; } AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_RESET_CONFIGURATION); - String page = FPSTR(HTTP_HEAD); - page.replace(F("{v}"), FPSTR(S_RESET_CONFIGURATION)); - page += FPSTR(HTTP_HEAD_STYLE); - page += F("
" D_CONFIGURATION_RESET "
"); - page += FPSTR(HTTP_MSG_RSTRT); - page += FPSTR(HTTP_BTN_MAIN); - ShowPage(page); + WSContentStart_P(S_RESET_CONFIGURATION, !WifiIsInManagerMode()); + WSContentSendStyle(); + WSContentSend_P(PSTR("
" D_CONFIGURATION_RESET "
")); + WSContentSend_P(HTTP_MSG_RSTRT); + WSContentSpaceButton(BUTTON_MAIN); + WSContentEnd(); - snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_RESET " 1")); - ExecuteWebCommand(svalue, SRC_WEBGUI); + char command[CMDSZ]; + snprintf_P(command, sizeof(command), PSTR(D_CMND_RESET " 1")); + ExecuteWebCommand(command, SRC_WEBGUI); } void HandleRestoreConfiguration(void) { - if (HttpUser()) { return; } - if (!WebAuthenticate()) { return WebServer->requestAuthentication(); } + if (!HttpCheckPriviledgedAccess()) { return; } + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_RESTORE_CONFIGURATION); - String page = FPSTR(HTTP_HEAD); - page.replace(F("{v}"), FPSTR(S_RESTORE_CONFIGURATION)); - page += FPSTR(HTTP_HEAD_STYLE); - page += FPSTR(HTTP_FORM_RST); - page += FPSTR(HTTP_FORM_RST_UPG); - page.replace(F("{r1"), F(D_RESTORE)); - page += FPSTR(HTTP_BTN_CONF); - ShowPage(page); + WSContentStart_P(S_RESTORE_CONFIGURATION); + WSContentSendStyle(); + WSContentSend_P(HTTP_FORM_RST); + WSContentSend_P(HTTP_FORM_RST_UPG, D_RESTORE); + WSContentSpaceButton(BUTTON_CONFIGURATION); + WSContentEnd(); upload_error = 0; upload_file_type = UPL_SETTINGS; @@ -1266,139 +1594,116 @@ void HandleRestoreConfiguration(void) void HandleInformation(void) { - if (HttpUser()) { return; } - if (!WebAuthenticate()) { return WebServer->requestAuthentication(); } + if (!HttpCheckPriviledgedAccess()) { return; } + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_INFORMATION); char stopic[TOPSZ]; int freeMem = ESP.getFreeHeap(); - String page = FPSTR(HTTP_HEAD); - page.replace(F("{v}"), FPSTR(S_INFORMATION)); - page += FPSTR(HTTP_HEAD_STYLE); - // page += F("
 Information "); - - page += F(""); - page += F("
"); - + WSContentStart_P(S_INFORMATION); // Save 1k of code space replacing table html with javascript replace codes // }1 = // }2 = - String func = FPSTR(HTTP_SCRIPT_INFO_BEGIN); - func += F("
"); - func += F(D_PROGRAM_VERSION "}2"); func += my_version; func += my_image; - func += F("}1" D_BUILD_DATE_AND_TIME "}2"); func += GetBuildDateAndTime(); - func += F("}1" D_CORE_AND_SDK_VERSION "}2" ARDUINO_ESP8266_RELEASE "/"); func += String(ESP.getSdkVersion()); - func += F("}1" D_UPTIME "}2"); func += GetUptime(); - snprintf_P(stopic, sizeof(stopic), PSTR(" at 0x%X"), GetSettingsAddress()); - func += F("}1" D_FLASH_WRITE_COUNT "}2"); func += String(Settings.save_flag); func += stopic; - func += F("}1" D_BOOT_COUNT "}2"); func += String(Settings.bootcount); - func += F("}1" D_RESTART_REASON "}2"); func += GetResetReason(); + WSContentSend_P(HTTP_SCRIPT_INFO_BEGIN); + WSContentSend_P(PSTR("
")); + WSContentSend_P(PSTR(D_PROGRAM_VERSION "}2%s%s"), my_version, my_image); + WSContentSend_P(PSTR("}1" D_BUILD_DATE_AND_TIME "}2%s"), GetBuildDateAndTime().c_str()); + WSContentSend_P(PSTR("}1" D_CORE_AND_SDK_VERSION "}2" ARDUINO_ESP8266_RELEASE "/%s"), ESP.getSdkVersion()); + WSContentSend_P(PSTR("}1" D_UPTIME "}2%s"), GetUptime().c_str()); + WSContentSend_P(PSTR("}1" D_FLASH_WRITE_COUNT "}2%d at 0x%X"), Settings.save_flag, GetSettingsAddress()); + WSContentSend_P(PSTR("}1" D_BOOT_COUNT "}2%d"), Settings.bootcount); + WSContentSend_P(PSTR("}1" D_RESTART_REASON "}2%s"), GetResetReason().c_str()); uint8_t maxfn = (devices_present > MAX_FRIENDLYNAMES) ? MAX_FRIENDLYNAMES : devices_present; - if (SONOFF_IFAN02 == Settings.module) { maxfn = 1; } - for (byte i = 0; i < maxfn; i++) { - func += F("}1" D_FRIENDLY_NAME " "); func += i +1; func += F("}2"); func += Settings.friendlyname[i]; + if (SONOFF_IFAN02 == my_module_type) { maxfn = 1; } + for (uint8_t i = 0; i < maxfn; i++) { + WSContentSend_P(PSTR("}1" D_FRIENDLY_NAME " %d}2%s"), i +1, Settings.friendlyname[i]); } - - func += F("}1}2 "); // Empty line - func += F("}1" D_AP); func += String(Settings.sta_active +1); - func += F(" " D_SSID " (" D_RSSI ")}2"); func += Settings.sta_ssid[Settings.sta_active]; func += F(" ("); func += WifiGetRssiAsQuality(WiFi.RSSI()); func += F("%)"); - func += F("}1" D_HOSTNAME "}2"); func += my_hostname; - if (mdns_begun) { func += F(".local"); } + WSContentSend_P(PSTR("}1}2 ")); // Empty line + WSContentSend_P(PSTR("}1" D_AP "%d " D_SSID " (" D_RSSI ")}2%s (%d%%)"), Settings.sta_active +1, Settings.sta_ssid[Settings.sta_active], WifiGetRssiAsQuality(WiFi.RSSI())); + WSContentSend_P(PSTR("}1" D_HOSTNAME "}2%s%s"), my_hostname, (mdns_begun) ? ".local" : ""); if (static_cast(WiFi.localIP()) != 0) { - func += F("}1" D_IP_ADDRESS "}2"); func += WiFi.localIP().toString(); - func += F("}1" D_GATEWAY "}2"); func += IPAddress(Settings.ip_address[1]).toString(); - func += F("}1" D_SUBNET_MASK "}2"); func += IPAddress(Settings.ip_address[2]).toString(); - func += F("}1" D_DNS_SERVER "}2"); func += IPAddress(Settings.ip_address[3]).toString(); - func += F("}1" D_MAC_ADDRESS "}2"); func += WiFi.macAddress(); + WSContentSend_P(PSTR("}1" D_IP_ADDRESS "}2%s"), WiFi.localIP().toString().c_str()); + WSContentSend_P(PSTR("}1" D_GATEWAY "}2%s"), IPAddress(Settings.ip_address[1]).toString().c_str()); + WSContentSend_P(PSTR("}1" D_SUBNET_MASK "}2%s"), IPAddress(Settings.ip_address[2]).toString().c_str()); + WSContentSend_P(PSTR("}1" D_DNS_SERVER "}2%s"), IPAddress(Settings.ip_address[3]).toString().c_str()); + WSContentSend_P(PSTR("}1" D_MAC_ADDRESS "}2%s"), WiFi.macAddress().c_str()); } if (static_cast(WiFi.softAPIP()) != 0) { - func += F("}1" D_AP " " D_IP_ADDRESS "}2"); func += WiFi.softAPIP().toString(); - func += F("}1" D_AP " " D_GATEWAY "}2"); func += WiFi.softAPIP().toString(); - func += F("}1" D_AP " " D_MAC_ADDRESS "}2"); func += WiFi.softAPmacAddress(); + WSContentSend_P(PSTR("}1" D_IP_ADDRESS "}2%s"), WiFi.softAPIP().toString().c_str()); + WSContentSend_P(PSTR("}1" D_GATEWAY "}2%s"), WiFi.softAPIP().toString().c_str()); + WSContentSend_P(PSTR("}1" D_MAC_ADDRESS "}2%s"), WiFi.softAPmacAddress().c_str()); } - - func += F("}1}2 "); // Empty line + WSContentSend_P(PSTR("}1}2 ")); // Empty line if (Settings.flag.mqtt_enabled) { - func += F("}1" D_MQTT_HOST "}2"); func += Settings.mqtt_host; - func += F("}1" D_MQTT_PORT "}2"); func += String(Settings.mqtt_port); - func += F("}1" D_MQTT_USER "}2"); func += Settings.mqtt_user; - func += F("}1" D_MQTT_CLIENT "}2"); func += mqtt_client; - func += F("}1" D_MQTT_TOPIC "}2"); func += Settings.mqtt_topic; - func += F("}1" D_MQTT_GROUP_TOPIC "}2"); func += Settings.mqtt_grptopic; - func += F("}1" D_MQTT_FULL_TOPIC "}2"); func += GetTopic_P(stopic, CMND, mqtt_topic, ""); - func += F("}1" D_MQTT " " D_FALLBACK_TOPIC "}2"); func += GetFallbackTopic_P(stopic, CMND, ""); + WSContentSend_P(PSTR("}1" D_MQTT_HOST "}2%s"), Settings.mqtt_host); + WSContentSend_P(PSTR("}1" D_MQTT_PORT "}2%d"), Settings.mqtt_port); + WSContentSend_P(PSTR("}1" D_MQTT_USER "}2%s"), Settings.mqtt_user); + WSContentSend_P(PSTR("}1" D_MQTT_CLIENT "}2%s"), mqtt_client); + WSContentSend_P(PSTR("}1" D_MQTT_TOPIC "}2%s"), Settings.mqtt_topic); + WSContentSend_P(PSTR("}1" D_MQTT_GROUP_TOPIC "}2%s"), Settings.mqtt_grptopic); + WSContentSend_P(PSTR("}1" D_MQTT_FULL_TOPIC "}2%s"), GetTopic_P(stopic, CMND, mqtt_topic, "")); + WSContentSend_P(PSTR("}1" D_MQTT " " D_FALLBACK_TOPIC "}2%s"), GetFallbackTopic_P(stopic, CMND, "")); } else { - func += F("}1" D_MQTT "}2" D_DISABLED); + WSContentSend_P(PSTR("}1" D_MQTT "}2" D_DISABLED)); } + WSContentSend_P(PSTR("}1}2 ")); // Empty line - func += F("}1}2 "); // Empty line - func += F("}1" D_EMULATION "}2"); #ifdef USE_EMULATION - if (EMUL_WEMO == Settings.flag2.emulation) { - func += F(D_BELKIN_WEMO); - } - else if (EMUL_HUE == Settings.flag2.emulation) { - func += F(D_HUE_BRIDGE); - } - else { - func += F(D_NONE); - } + WSContentSend_P(PSTR("}1" D_EMULATION "}2%s"), GetTextIndexed(stopic, sizeof(stopic), Settings.flag2.emulation, kEmulationOptions)); #else - func += F(D_DISABLED); + WSContentSend_P(PSTR("}1" D_EMULATION "}2" D_DISABLED)); #endif // USE_EMULATION - func += F("}1" D_MDNS_DISCOVERY "}2"); #ifdef USE_DISCOVERY - func += F(D_ENABLED); - func += F("}1" D_MDNS_ADVERTISE "}2"); + WSContentSend_P(PSTR("}1" D_MDNS_DISCOVERY "}2%s"), (Settings.flag3.mdns_enabled) ? D_ENABLED : D_DISABLED); + if (Settings.flag3.mdns_enabled) { #ifdef WEBSERVER_ADVERTISE - func += F(D_WEB_SERVER); + WSContentSend_P(PSTR("}1" D_MDNS_ADVERTISE "}2" D_WEB_SERVER)); #else - func += F(D_DISABLED); + WSContentSend_P(PSTR("}1" D_MDNS_ADVERTISE "}2" D_DISABLED)); #endif // WEBSERVER_ADVERTISE + } #else - func += F(D_DISABLED); + WSContentSend_P(PSTR("}1" D_MDNS_DISCOVERY "}2" D_DISABLED)); #endif // USE_DISCOVERY - func += F("}1}2 "); // Empty line - func += F("}1" D_ESP_CHIP_ID "}2"); func += String(ESP.getChipId()); - snprintf_P(stopic, sizeof(stopic), PSTR("0x%06X"), ESP.getFlashChipId()); - func += F("}1" D_FLASH_CHIP_ID "}2"); func += stopic; - func += F("}1" D_FLASH_CHIP_SIZE "}2"); func += String(ESP.getFlashChipRealSize() / 1024); func += F("kB"); - func += F("}1" D_PROGRAM_FLASH_SIZE "}2"); func += String(ESP.getFlashChipSize() / 1024); func += F("kB"); - func += F("}1" D_PROGRAM_SIZE "}2"); func += String(ESP.getSketchSize() / 1024); func += F("kB"); - func += F("}1" D_FREE_PROGRAM_SPACE "}2"); func += String(ESP.getFreeSketchSpace() / 1024); func += F("kB"); - func += F("}1" D_FREE_MEMORY "}2"); func += String(freeMem / 1024); func += F("kB"); - func += F("
"); - func += FPSTR(HTTP_SCRIPT_INFO_END); - page.replace(F(""), func); - page.replace(F(""), F("")); + WSContentSend_P(PSTR("}1}2 ")); // Empty line + WSContentSend_P(PSTR("}1" D_ESP_CHIP_ID "}2%d"), ESP.getChipId()); + WSContentSend_P(PSTR("}1" D_FLASH_CHIP_ID "}20x%06X"), ESP.getFlashChipId()); + WSContentSend_P(PSTR("}1" D_FLASH_CHIP_SIZE "}2%dkB"), ESP.getFlashChipRealSize() / 1024); + WSContentSend_P(PSTR("}1" D_PROGRAM_FLASH_SIZE "}2%dkB"), ESP.getFlashChipSize() / 1024); + WSContentSend_P(PSTR("}1" D_PROGRAM_SIZE "}2%dkB"), ESP.getSketchSize() / 1024); + WSContentSend_P(PSTR("}1" D_FREE_PROGRAM_SPACE "}2%dkB"), ESP.getFreeSketchSpace() / 1024); + WSContentSend_P(PSTR("}1" D_FREE_MEMORY "}2%dkB"), freeMem / 1024); + WSContentSend_P(PSTR("
")); - // page += F("
"); - page += FPSTR(HTTP_BTN_MAIN); - ShowPage(page); + WSContentSend_P(HTTP_SCRIPT_INFO_END); + WSContentSendStyle(); + // WSContentSend_P(PSTR("
 Information ")); + WSContentSend_P(PSTR("" + "
")); + // WSContentSend_P(PSTR("
")); + WSContentSpaceButton(BUTTON_MAIN); + WSContentEnd(); } -#endif // Not BE_MINIMAL +#endif // Not FIRMWARE_MINIMAL /*-------------------------------------------------------------------------------------------*/ void HandleUpgradeFirmware(void) { - if (HttpUser()) { return; } - if (!WebAuthenticate()) { return WebServer->requestAuthentication(); } + if (!HttpCheckPriviledgedAccess()) { return; } + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_FIRMWARE_UPGRADE); - String page = FPSTR(HTTP_HEAD); - page.replace(F("{v}"), FPSTR(S_FIRMWARE_UPGRADE)); - page += FPSTR(HTTP_HEAD_STYLE); - page += FPSTR(HTTP_FORM_UPG); - page.replace(F("{o1"), Settings.ota_url); - page += FPSTR(HTTP_FORM_RST_UPG); - page.replace(F("{r1"), F(D_UPGRADE)); - page += FPSTR(HTTP_BTN_MAIN); - ShowPage(page); + WSContentStart_P(S_FIRMWARE_UPGRADE); + WSContentSendStyle(); + WSContentSend_P(HTTP_FORM_UPG, Settings.ota_url); + WSContentSend_P(HTTP_FORM_RST_UPG, D_UPGRADE); + WSContentSpaceButton(BUTTON_MAIN); + WSContentEnd(); upload_error = 0; upload_file_type = UPL_TASMOTA; @@ -1406,37 +1711,36 @@ void HandleUpgradeFirmware(void) void HandleUpgradeFirmwareStart(void) { - if (HttpUser()) { return; } - if (!WebAuthenticate()) { return WebServer->requestAuthentication(); } - char svalue[100]; + if (!HttpCheckPriviledgedAccess()) { return; } + + char command[sizeof(Settings.ota_url) + 10]; // OtaUrl AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_UPGRADE_STARTED)); WifiConfigCounter(); - char tmp[100]; - WebGetArg("o", tmp, sizeof(tmp)); - if (strlen(tmp)) { - snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_OTAURL " %s"), tmp); - ExecuteWebCommand(svalue, SRC_WEBGUI); + char otaurl[sizeof(Settings.ota_url)]; + WebGetArg("o", otaurl, sizeof(otaurl)); + if (strlen(otaurl)) { + snprintf_P(command, sizeof(command), PSTR(D_CMND_OTAURL " %s"), otaurl); + ExecuteWebCommand(command, SRC_WEBGUI); } - String page = FPSTR(HTTP_HEAD); - page.replace(F("{v}"), FPSTR(S_INFORMATION)); - page += FPSTR(HTTP_HEAD_STYLE); - page += F("
" D_UPGRADE_STARTED " ...
"); - page += FPSTR(HTTP_MSG_RSTRT); - page += FPSTR(HTTP_BTN_MAIN); - page.replace(F(""), FPSTR(HTTP_SCRIPT_RELOAD_OTA)); - ShowPage(page); + WSContentStart_P(S_INFORMATION); + WSContentSend_P(HTTP_SCRIPT_RELOAD_OTA); + WSContentSendStyle(); + WSContentSend_P(PSTR("
" D_UPGRADE_STARTED " ...
")); + WSContentSend_P(HTTP_MSG_RSTRT); + WSContentSpaceButton(BUTTON_MAIN); + WSContentEnd(); - snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_UPGRADE " 1")); - ExecuteWebCommand(svalue, SRC_WEBGUI); + snprintf_P(command, sizeof(command), PSTR(D_CMND_UPGRADE " 1")); + ExecuteWebCommand(command, SRC_WEBGUI); } void HandleUploadDone(void) { - if (HttpUser()) { return; } - if (!WebAuthenticate()) { return WebServer->requestAuthentication(); } + if (!HttpCheckPriviledgedAccess()) { return; } + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_UPLOAD_DONE)); char error[100]; @@ -1445,52 +1749,42 @@ void HandleUploadDone(void) restart_flag = 0; MqttRetryCounter(0); - String page = FPSTR(HTTP_HEAD); - page.replace(F("{v}"), FPSTR(S_INFORMATION)); - page += FPSTR(HTTP_HEAD_STYLE); - page += F("
" D_UPLOAD " " D_UPLOAD " " D_FAILED "

"); - switch (upload_error) { - case 1: strncpy_P(error, PSTR(D_UPLOAD_ERR_1), sizeof(error)); break; - case 2: strncpy_P(error, PSTR(D_UPLOAD_ERR_2), sizeof(error)); break; - case 3: strncpy_P(error, PSTR(D_UPLOAD_ERR_3), sizeof(error)); break; - case 4: strncpy_P(error, PSTR(D_UPLOAD_ERR_4), sizeof(error)); break; - case 5: strncpy_P(error, PSTR(D_UPLOAD_ERR_5), sizeof(error)); break; - case 6: strncpy_P(error, PSTR(D_UPLOAD_ERR_6), sizeof(error)); break; - case 7: strncpy_P(error, PSTR(D_UPLOAD_ERR_7), sizeof(error)); break; - case 8: strncpy_P(error, PSTR(D_UPLOAD_ERR_8), sizeof(error)); break; - case 9: strncpy_P(error, PSTR(D_UPLOAD_ERR_9), sizeof(error)); break; + WSContentSend_P(PSTR("red'>" D_FAILED "


")); #ifdef USE_RF_FLASH - case 10: strncpy_P(error, PSTR(D_UPLOAD_ERR_10), sizeof(error)); break; - case 11: strncpy_P(error, PSTR(D_UPLOAD_ERR_11), sizeof(error)); break; - case 12: strncpy_P(error, PSTR(D_UPLOAD_ERR_12), sizeof(error)); break; - case 13: strncpy_P(error, PSTR(D_UPLOAD_ERR_13), sizeof(error)); break; + if (upload_error < 14) { +#else + if (upload_error < 10) { #endif - default: - snprintf_P(error, sizeof(error), PSTR(D_UPLOAD_ERROR_CODE " %d"), upload_error); + GetTextIndexed(error, sizeof(error), upload_error -1, kUploadErrors); + } else { + snprintf_P(error, sizeof(error), PSTR(D_UPLOAD_ERROR_CODE " %d"), upload_error); } - page += error; - snprintf_P(log_data, sizeof(log_data), PSTR(D_UPLOAD ": %s"), error); - AddLog(LOG_LEVEL_DEBUG); + WSContentSend_P(error); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_UPLOAD ": %s"), error); stop_flash_rotate = Settings.flag.stop_flash_rotate; } else { - page += F("green'>" D_SUCCESSFUL "
"); - page += FPSTR(HTTP_MSG_RSTRT); - page.replace(F(""), FPSTR(HTTP_SCRIPT_RELOAD_OTA)); // Refesh main web ui after OTA upgrade + WSContentSend_P(PSTR("green'>" D_SUCCESSFUL "
")); + WSContentSend_P(HTTP_MSG_RSTRT); ShowWebSource(SRC_WEBGUI); restart_flag = 2; // Always restart to re-enable disabled features during update } SettingsBufferFree(); - page += F("

"); - page += FPSTR(HTTP_BTN_MAIN); - ShowPage(page); + WSContentSend_P(PSTR("
")); + WSContentSpaceButton(BUTTON_MAIN); + WSContentEnd(); } void HandleUploadLoop(void) { // Based on ESP8266HTTPUpdateServer.cpp uses ESP8266WebServer Parsing.cpp and Cores Updater.cpp (Update) - boolean _serialoutput = (LOG_LEVEL_DEBUG <= seriallog_level); + bool _serialoutput = (LOG_LEVEL_DEBUG <= seriallog_level); if (HTTP_USER == webserver_state) { return; } if (upload_error) { @@ -1507,8 +1801,7 @@ void HandleUploadLoop(void) return; } SettingsSave(1); // Free flash for upload - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_UPLOAD D_FILE " %s ..."), upload.filename.c_str()); - AddLog(LOG_LEVEL_INFO); + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_UPLOAD D_FILE " %s ..."), upload.filename.c_str()); if (UPL_SETTINGS == upload_file_type) { if (!SettingsBufferAlloc()) { upload_error = 2; // Not enough space @@ -1543,7 +1836,7 @@ void HandleUploadLoop(void) } else { #ifdef USE_RF_FLASH - if ((SONOFF_BRIDGE == Settings.module) && (upload.buf[0] == ':')) { // Check if this is a RF bridge FW file + if ((SONOFF_BRIDGE == my_module_type) && (upload.buf[0] == ':')) { // Check if this is a RF bridge FW file Update.end(); // End esp8266 update session upload_file_type = UPL_EFM8BB1; @@ -1591,7 +1884,7 @@ void HandleUploadLoop(void) upload_error = abs(result); return; } else if (result > 0) { - if (result > upload.currentSize) { + if ((size_t)result > upload.currentSize) { // Offset is larger than the buffer supplied, this should not happen upload_error = 9; // File too large - Failed to decode RF firmware return; @@ -1667,8 +1960,7 @@ void HandleUploadLoop(void) } } if (!upload_error) { - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_UPLOAD D_SUCCESSFUL " %u bytes. " D_RESTARTING), upload.totalSize); - AddLog(LOG_LEVEL_INFO); + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_UPLOAD D_SUCCESSFUL " %u bytes. " D_RESTARTING), upload.totalSize); } } else if (UPLOAD_FILE_ABORTED == upload.status) { restart_flag = 0; @@ -1686,37 +1978,34 @@ void HandlePreflightRequest(void) WebServer->sendHeader(F("Access-Control-Allow-Origin"), F("*")); WebServer->sendHeader(F("Access-Control-Allow-Methods"), F("GET, POST")); WebServer->sendHeader(F("Access-Control-Allow-Headers"), F("authorization")); - WebServer->send(200, FPSTR(HDR_CTYPE_HTML), ""); + WSSend(200, CT_HTML, ""); } /*-------------------------------------------------------------------------------------------*/ void HandleHttpCommand(void) { - if (HttpUser()) { return; } -// if (!WebAuthenticate()) { return WebServer->requestAuthentication(); } - char svalue[INPUT_BUFFER_SIZE]; // Large to serve Backlog + if (!HttpCheckPriviledgedAccess(false)) { return; } AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_COMMAND)); uint8_t valid = 1; if (Settings.web_password[0] != 0) { - char tmp1[100]; + char tmp1[sizeof(Settings.web_password)]; WebGetArg("user", tmp1, sizeof(tmp1)); - char tmp2[100]; + char tmp2[sizeof(Settings.web_password)]; WebGetArg("password", tmp2, sizeof(tmp2)); if (!(!strcmp(tmp1, WEB_USERNAME) && !strcmp(tmp2, Settings.web_password))) { valid = 0; } } String message = F("{\"" D_RSLT_WARNING "\":\""); if (valid) { - byte curridx = web_log_index; - WebGetArg("cmnd", svalue, sizeof(svalue)); - if (strlen(svalue)) { - ExecuteWebCommand(svalue, SRC_WEBCOMMAND); - + uint8_t curridx = web_log_index; + String svalue = WebServer->arg("cmnd"); + if (svalue.length() && (svalue.length() < INPUT_BUFFER_SIZE)) { + ExecuteWebCommand((char*)svalue.c_str(), SRC_WEBCOMMAND); if (web_log_index != curridx) { - byte counter = curridx; + uint8_t counter = curridx; message = F("{"); do { char* tmp; @@ -1728,6 +2017,7 @@ void HandleHttpCommand(void) if (JSON) { // Is it a JSON message (and not only [15:26:08 MQT: stat/wemos5/POWER = O]) if (message.length() > 1) { message += F(","); } size_t JSONlen = len - (JSON - tmp); + if (JSONlen > sizeof(mqtt_data)) { JSONlen = sizeof(mqtt_data); } strlcpy(mqtt_data, JSON +1, JSONlen -2); message += mqtt_data; } @@ -1745,56 +2035,58 @@ void HandleHttpCommand(void) } else { message += F(D_NEED_USER_AND_PASSWORD "\"}"); } - SetHeader(); - WebServer->send(200, FPSTR(HDR_CTYPE_JSON), message); + WSHeaderSend(); + WSSend(200, CT_JSON, message); } /*-------------------------------------------------------------------------------------------*/ void HandleConsole(void) { - if (HttpUser()) { return; } - if (!WebAuthenticate()) { return WebServer->requestAuthentication(); } - AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONSOLE); + if (!HttpCheckPriviledgedAccess()) { return; } - String page = FPSTR(HTTP_HEAD); - page.replace(F("{v}"), FPSTR(S_CONSOLE)); - page += FPSTR(HTTP_HEAD_STYLE); - page.replace(F(""), FPSTR(HTTP_SCRIPT_CONSOL)); - page.replace(F(""), F("")); - page += FPSTR(HTTP_FORM_CMND); - page += FPSTR(HTTP_BTN_MAIN); - ShowPage(page); -} - -void HandleAjaxConsoleRefresh(void) -{ - if (HttpUser()) { return; } - if (!WebAuthenticate()) { return WebServer->requestAuthentication(); } - char svalue[INPUT_BUFFER_SIZE]; // Large to serve Backlog - byte cflg = 1; - byte counter = 0; // Initial start, should never be 0 again - - WebGetArg("c1", svalue, sizeof(svalue)); - if (strlen(svalue)) { - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_COMMAND "%s"), svalue); - AddLog(LOG_LEVEL_INFO); - ExecuteWebCommand(svalue, SRC_WEBCONSOLE); + if (WebServer->hasArg("c2")) { // Console refresh requested + HandleConsoleRefresh(); + return; } - WebGetArg("c2", svalue, sizeof(svalue)); - if (strlen(svalue)) { counter = atoi(svalue); } + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONSOLE); - byte last_reset_web_log_flag = reset_web_log_flag; - String message = F("}9"); // Cannot load mqtt_data here as <> will be encoded by replacements below + WSContentStart_P(S_CONSOLE); + WSContentSend_P(HTTP_SCRIPT_CONSOL, Settings.web_refresh); + WSContentSendStyle(); + WSContentSend_P(HTTP_FORM_CMND); + WSContentSpaceButton(BUTTON_MAIN); + WSContentEnd(); +} + +void HandleConsoleRefresh(void) +{ + bool cflg = true; + uint8_t counter = 0; // Initial start, should never be 0 again + + String svalue = WebServer->arg("c1"); + if (svalue.length() && (svalue.length() < INPUT_BUFFER_SIZE)) { + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_COMMAND "%s"), svalue.c_str()); + ExecuteWebCommand((char*)svalue.c_str(), SRC_WEBCONSOLE); + } + + char stmp[8]; + WebGetArg("c2", stmp, sizeof(stmp)); + if (strlen(stmp)) { counter = atoi(stmp); } + + bool last_reset_web_log_flag = reset_web_log_flag; + // mqtt_data used as scratch space + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%d}1%d}1"), web_log_index, last_reset_web_log_flag); + String message = mqtt_data; if (!reset_web_log_flag) { counter = 0; - reset_web_log_flag = 1; + reset_web_log_flag = true; } if (counter != web_log_index) { if (!counter) { counter = web_log_index; - cflg = 0; + cflg = false; } do { char* tmp; @@ -1804,32 +2096,25 @@ void HandleAjaxConsoleRefresh(void) if (cflg) { message += F("\n"); } else { - cflg = 1; + cflg = true; } + if (len > sizeof(mqtt_data) -2) { len = sizeof(mqtt_data); } strlcpy(mqtt_data, tmp, len); - message += mqtt_data; + message += mqtt_data; // mqtt_data used as scratch space } counter++; - if (!counter) { counter++; } // Skip 0 as it is not allowed + if (!counter) { counter++; } // Skip log index 0 as it is not allowed } while (counter != web_log_index); - // XML encoding to fix blank console log in concert with javascript decodeURIComponent - message.replace(F("%"), F("%25")); // Needs to be done first as otherwise the % in %26 will also be converted - message.replace(F("&"), F("%26")); - message.replace(F("<"), F("%3C")); - message.replace(F(">"), F("%3E")); } - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%d%d"), web_log_index, last_reset_web_log_flag); - message.replace(F("}9"), mqtt_data); // Save to load here - message += F(""); - WebServer->send(200, FPSTR(HDR_CTYPE_XML), message); + message += F("}1"); + WSSend(200, CT_PLAIN, message); } /********************************************************************************************/ void HandleNotFound(void) { -// snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_HTTP "Not fount (%s)"), WebServer->uri().c_str()); -// AddLog(LOG_LEVEL_DEBUG); +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP "Not fount (%s)"), WebServer->uri().c_str()); if (CaptivePortal()) { return; } // If captive portal redirect instead of displaying the error page. @@ -1845,19 +2130,19 @@ void HandleNotFound(void) for (uint8_t i = 0; i < WebServer->args(); i++) { snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s %s: %s\n"), mqtt_data, WebServer->argName(i).c_str(), WebServer->arg(i).c_str()); } - SetHeader(); - WebServer->send(404, FPSTR(HDR_CTYPE_PLAIN), mqtt_data); + WSHeaderSend(); + WSSend(404, CT_PLAIN, mqtt_data); } } /* Redirect to captive portal if we got a request for another domain. Return true in that case so the page handler do not try to handle the request again. */ -boolean CaptivePortal(void) +bool CaptivePortal(void) { - if ((HTTP_MANAGER == webserver_state) && !ValidIpAddress(WebServer->hostHeader())) { + if ((WifiIsInManagerMode()) && !ValidIpAddress(WebServer->hostHeader())) { AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_REDIRECTED)); WebServer->sendHeader(F("Location"), String("http://") + WebServer->client().localIP().toString(), true); - WebServer->send(302, FPSTR(HDR_CTYPE_PLAIN), ""); // Empty content inhibits Content-length header so we have to close the socket ourselves. + WSSend(302, CT_PLAIN, ""); // Empty content inhibits Content-length header so we have to close the socket ourselves. WebServer->client().stop(); // Stop is needed because we sent no content length return true; } @@ -1865,7 +2150,7 @@ boolean CaptivePortal(void) } /** Is this an IP? */ -boolean ValidIpAddress(String str) +bool ValidIpAddress(String str) { for (uint16_t i = 0; i < str.length(); i++) { int c = str.charAt(i); @@ -1912,90 +2197,76 @@ String UrlEncode(const String& text) int WebSend(char *buffer) { - /* [sonoff] POWER1 ON --> Sends http://sonoff/cm?cmnd=POWER1 ON - * [192.168.178.86:80,admin:joker] POWER1 ON --> Sends http://hostname:80/cm?user=admin&password=joker&cmnd=POWER1 ON - * [sonoff] /any/link/starting/with/a/slash.php?log=123 --> Sends http://sonoff/any/link/starting/with/a/slash.php?log=123 - * [sonoff,admin:joker] /any/link/starting/with/a/slash.php?log=123 --> Sends http://sonoff/any/link/starting/with/a/slash.php?log=123 - */ + // [sonoff] POWER1 ON --> Sends http://sonoff/cm?cmnd=POWER1 ON + // [192.168.178.86:80,admin:joker] POWER1 ON --> Sends http://hostname:80/cm?user=admin&password=joker&cmnd=POWER1 ON + // [sonoff] /any/link/starting/with/a/slash.php?log=123 --> Sends http://sonoff/any/link/starting/with/a/slash.php?log=123 + // [sonoff,admin:joker] /any/link/starting/with/a/slash.php?log=123 --> Sends http://sonoff/any/link/starting/with/a/slash.php?log=123 char *host; - char *port; char *user; char *password; char *command; - uint16_t nport = 80; int status = 1; // Wrong parameters // buffer = | [ 192.168.178.86 : 80 , admin : joker ] POWER1 ON | host = strtok_r(buffer, "]", &command); // host = | [ 192.168.178.86 : 80 , admin : joker |, command = | POWER1 ON | if (host && command) { - host = Trim(host); // host = |[ 192.168.178.86 : 80 , admin : joker| - host++; // host = | 192.168.178.86 : 80 , admin : joker| - Skip [ - host = strtok_r(host, ",", &user); // host = | 192.168.178.86 : 80 |, user = | admin : joker| - host = strtok_r(host, ":", &port); // host = | 192.168.178.86 |, port = | 80 | - host = Trim(host); // host = |192.168.178.86| - if (port) { - port = Trim(port); // port = |80| - nport = atoi(port); - } - if (user) { - user = strtok_r(user, ":", &password); // user = | admin |, password = | joker| - user = Trim(user); // user = |admin| - if (password) { password = Trim(password); } // password = |joker| - } + RemoveSpace(host); // host = |[192.168.178.86:80,admin:joker| + host++; // host = |192.168.178.86:80,admin:joker| - Skip [ + host = strtok_r(host, ",", &user); // host = |192.168.178.86:80|, user = |admin:joker| + String url = F("http://"); // url = |http://| + url += host; // url = |http://192.168.178.86:80| + command = Trim(command); // command = |POWER1 ON| or |/any/link/starting/with/a/slash.php?log=123| - - String nuri = ""; if (command[0] != '/') { - nuri = "/cm?"; - if (user && password) { - nuri += F("user="); - nuri += user; - nuri += F("&password="); - nuri += password; - nuri += F("&"); - } - nuri += F("cmnd="); - } - nuri += command; - String uri = UrlEncode(nuri); - - IPAddress host_ip; - if (WiFi.hostByName(host, host_ip)) { - WiFiClient client; - - bool connected = false; - byte retry = 2; - while ((retry > 0) && !connected) { - --retry; - connected = client.connect(host_ip, nport); - if (connected) break; - } - - if (connected) { - String url = F("GET "); - url += uri; - url += F(" HTTP/1.1\r\nHost: "); -// url += IPAddress(host_ip).toString(); - url += host; // https://tools.ietf.org/html/rfc7230#section-5.4 (#4331) - if (port) { - url += F(":"); - url += port; + url += F("/cm?"); // url = |http://192.168.178.86/cm?| + if (user) { + user = strtok_r(user, ":", &password); // user = |admin|, password = |joker| + if (user && password) { + char userpass[128]; + snprintf_P(userpass, sizeof(userpass), PSTR("user=%s&password=%s&"), user, password); + url += userpass; // url = |http://192.168.178.86/cm?user=admin&password=joker&| } - url += F("\r\nConnection: close\r\n\r\n"); + } + url += F("cmnd="); // url = |http://192.168.178.86/cm?cmnd=| or |http://192.168.178.86/cm?user=admin&password=joker&cmnd=| + } + url += command; // url = |http://192.168.178.86/cm?cmnd=POWER1 ON| -//snprintf_P(log_data, sizeof(log_data), PSTR("DBG: Url |%s|"), url.c_str()); -//AddLog(LOG_LEVEL_DEBUG); +//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DBG: Uri |%s|"), url.c_str()); - client.print(url.c_str()); - client.flush(); - client.stop(); +#if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_1) || defined(ARDUINO_ESP8266_RELEASE_2_4_2) + HTTPClient http; + if (http.begin(UrlEncode(url))) { // UrlEncode(url) = |http://192.168.178.86/cm?cmnd=POWER1%20ON| +#else + WiFiClient http_client; + HTTPClient http; + if (http.begin(http_client, UrlEncode(url))) { // UrlEncode(url) = |http://192.168.178.86/cm?cmnd=POWER1%20ON| +#endif + int http_code = http.GET(); // Start connection and send HTTP header + if (http_code > 0) { // http_code will be negative on error + if (http_code == HTTP_CODE_OK || http_code == HTTP_CODE_MOVED_PERMANENTLY) { +/* + // Return received data to the user - Adds 900+ bytes to the code + String result = http.getString(); // File found at server - may need lot of ram or trigger out of memory! + uint16_t j = 0; + for (uint16_t i = 0; i < result.length(); i++) { + char text = result.charAt(i); + if (text > 31) { // Remove control characters like linefeed + mqtt_data[j++] = text; + if (j == sizeof(mqtt_data) -2) { break; } + } + } + mqtt_data[j] = '\0'; + MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_WEBSEND)); +*/ + } status = 0; // No error - Done } else { status = 2; // Connection failed } + http.end(); // Clean up connection data } else { - status = 3; // Host not found + status = 3; // Host not found or connection error } } return status; @@ -2066,9 +2337,9 @@ bool WebCommand(void) * Interface \*********************************************************************************************/ -boolean Xdrv01(byte function) +bool Xdrv01(uint8_t function) { - boolean result = false; + bool result = false; switch (function) { case FUNC_LOOP: diff --git a/sonoff/xdrv_02_mqtt.ino b/sonoff/xdrv_02_mqtt.ino index 7ba5cf47a..ba3dabc09 100644 --- a/sonoff/xdrv_02_mqtt.ino +++ b/sonoff/xdrv_02_mqtt.ino @@ -1,7 +1,7 @@ /* xdrv_02_mqtt.ino - mqtt support for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends + Copyright (C) 2019 Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -73,9 +73,11 @@ const char kMqttCommands[] PROGMEM = D_CMND_MQTTUSER "|" D_CMND_MQTTPASSWORD "|" D_CMND_FULLTOPIC "|" D_CMND_PREFIX "|" D_CMND_GROUPTOPIC "|" D_CMND_TOPIC "|" D_CMND_PUBLISH "|" D_CMND_BUTTONTOPIC "|" D_CMND_SWITCHTOPIC "|" D_CMND_BUTTONRETAIN "|" D_CMND_SWITCHRETAIN "|" D_CMND_POWERRETAIN "|" D_CMND_SENSORRETAIN ; +uint16_t mqtt_connect_count = 0; // MQTT re-connect count uint16_t mqtt_retry_counter = 1; // MQTT connection retry counter uint8_t mqtt_initial_connection_state = 2; // MQTT connection messages state bool mqtt_connected = false; // MQTT virtual connection status +bool mqtt_allowed = false; // MQTT enabled and parameters valid /*********************************************************************************************\ * MQTT driver specific code need to provide the following functions: @@ -83,7 +85,7 @@ bool mqtt_connected = false; // MQTT virtual connection status * bool MqttIsConnected() * void MqttDisconnect() * void MqttSubscribeLib(char *topic) - * bool MqttPublishLib(const char* topic, boolean retained) + * bool MqttPublishLib(const char* topic, bool retained) * void MqttLoop() \*********************************************************************************************/ @@ -108,13 +110,19 @@ void MqttDisconnect(void) MqttClient.disconnect(); } -void MqttSubscribeLib(char *topic) +void MqttSubscribeLib(const char *topic) { MqttClient.subscribe(topic); MqttClient.loop(); // Solve LmacRxBlk:1 messages } -bool MqttPublishLib(const char* topic, boolean retained) +void MqttUnsubscribeLib(const char *topic) +{ + MqttClient.unsubscribe(topic); + MqttClient.loop(); // Solve LmacRxBlk:1 messages +} + +bool MqttPublishLib(const char* topic, bool retained) { bool result = MqttClient.publish(topic, mqtt_data, retained); yield(); // #3313 @@ -146,12 +154,17 @@ void MqttDisconnectedCb(void) MqttDisconnected(MqttClient.State()); // status codes are documented in file mqtt.h as tConnState } -void MqttSubscribeLib(char *topic) +void MqttSubscribeLib(const char *topic) { MqttClient.Subscribe(topic, 0); } -bool MqttPublishLib(const char* topic, boolean retained) +void MqttUnsubscribeLib(const char *topic) +{ + MqttClient.Unsubscribe(topic, 0); +} + +bool MqttPublishLib(const char* topic, bool retained) { return MqttClient.Publish(topic, mqtt_data, strlen(mqtt_data), 0, retained); } @@ -179,21 +192,26 @@ void MqttDisconnect(void) void MqttMyDataCb(MQTTClient* client, char* topic, char* data, int data_len) //void MqttMyDataCb(MQTTClient *client, char topic[], char data[], int data_len) { -// MqttDataHandler((char*)topic, (byte*)data, data_len); +// MqttDataHandler((char*)topic, (uint8_t*)data, data_len); } */ void MqttMyDataCb(String &topic, String &data) { - MqttDataHandler((char*)topic.c_str(), (byte*)data.c_str(), data.length()); + MqttDataHandler((char*)topic.c_str(), (uint8_t*)data.c_str(), data.length()); } -void MqttSubscribeLib(char *topic) +void MqttSubscribeLib(const char *topic) { MqttClient.subscribe(topic, 0); } -bool MqttPublishLib(const char* topic, boolean retained) +void MqttUnsubscribeLib(const char *topic) +{ + MqttClient.unsubscribe(topic, 0); +} + +bool MqttPublishLib(const char* topic, bool retained) { return MqttClient.publish(topic, mqtt_data, strlen(mqtt_data), retained, 0); } @@ -210,26 +228,28 @@ void MqttLoop(void) #ifdef USE_DISCOVERY #ifdef MQTT_HOST_DISCOVERY -boolean MqttDiscoverServer(void) +void MqttDiscoverServer(void) { - if (!mdns_begun) { return false; } + if (!mdns_begun) { return; } int n = MDNS.queryService("mqtt", "tcp"); // Search for mqtt service - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_MDNS D_QUERY_DONE " %d"), n); - AddLog(LOG_LEVEL_INFO); + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MDNS D_QUERY_DONE " %d"), n); if (n > 0) { - // Note: current strategy is to get the first MQTT service (even when many are found) - snprintf_P(Settings.mqtt_host, sizeof(Settings.mqtt_host), MDNS.IP(0).toString().c_str()); - Settings.mqtt_port = MDNS.port(0); + uint8_t i = 0; // If the hostname isn't set, use the first record found. +#ifdef MDNS_HOSTNAME + for (i = n; i > 0; i--) { // Search from last to first and use first if not found + if (!strcmp(MDNS.hostname(i).c_str(), MDNS_HOSTNAME)) { + break; // Stop at matching record + } + } +#endif // MDNS_HOSTNAME + snprintf_P(Settings.mqtt_host, sizeof(Settings.mqtt_host), MDNS.IP(i).toString().c_str()); + Settings.mqtt_port = MDNS.port(i); - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_MDNS D_MQTT_SERVICE_FOUND " %s, " D_IP_ADDRESS " %s, " D_PORT " %d"), - MDNS.hostname(0).c_str(), Settings.mqtt_host, Settings.mqtt_port); - AddLog(LOG_LEVEL_INFO); + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MDNS D_MQTT_SERVICE_FOUND " %s, " D_IP_ADDRESS " %s, " D_PORT " %d"), MDNS.hostname(i).c_str(), Settings.mqtt_host, Settings.mqtt_port); } - - return n > 0; } #endif // MQTT_HOST_DISCOVERY #endif // USE_DISCOVERY @@ -244,19 +264,26 @@ void MqttRetryCounter(uint8_t value) mqtt_retry_counter = value; } -void MqttSubscribe(char *topic) +void MqttSubscribe(const char *topic) { - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_MQTT D_SUBSCRIBE_TO " %s"), topic); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_MQTT D_SUBSCRIBE_TO " %s"), topic); MqttSubscribeLib(topic); } -void MqttPublishDirect(const char* topic, boolean retained) +void MqttUnsubscribe(const char *topic) +{ + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_MQTT D_UNSUBSCRIBE_FROM " %s"), topic); + MqttUnsubscribeLib(topic); +} + +void MqttPublishDirect(const char* topic, bool retained) { char sretained[CMDSZ]; char slog_type[10]; +#ifdef USE_DEBUG_DRIVER ShowFreeMem(PSTR("MqttPublishDirect")); +#endif sretained[0] = '\0'; snprintf_P(slog_type, sizeof(slog_type), PSTR(D_LOG_RESULT)); @@ -278,14 +305,14 @@ void MqttPublishDirect(const char* topic, boolean retained) snprintf_P(log_data, sizeof(log_data), PSTR("%s ..."), log_data); } snprintf_P(log_data, sizeof(log_data), PSTR("%s%s"), log_data, sretained); - AddLog(LOG_LEVEL_INFO); + if (Settings.ledstate &0x04) { blinks++; } } -void MqttPublish(const char* topic, boolean retained) +void MqttPublish(const char* topic, bool retained) { char *me; @@ -303,7 +330,7 @@ void MqttPublish(const char* topic) MqttPublish(topic, false); } -void MqttPublishPrefixTopic_P(uint8_t prefix, const char* subtopic, boolean retained) +void MqttPublishPrefixTopic_P(uint8_t prefix, const char* subtopic, bool retained) { /* prefix 0 = cmnd using subtopic * prefix 1 = stat using subtopic @@ -316,7 +343,7 @@ void MqttPublishPrefixTopic_P(uint8_t prefix, const char* subtopic, boolean reta char stopic[TOPSZ]; snprintf_P(romram, sizeof(romram), ((prefix > 3) && !Settings.flag.mqtt_response) ? S_RSLT_RESULT : subtopic); - for (byte i = 0; i < strlen(romram); i++) { + for (uint8_t i = 0; i < strlen(romram); i++) { romram[i] = toupper(romram[i]); } prefix &= 3; @@ -329,14 +356,14 @@ void MqttPublishPrefixTopic_P(uint8_t prefix, const char* subtopic) MqttPublishPrefixTopic_P(prefix, subtopic, false); } -void MqttPublishPowerState(byte device) +void MqttPublishPowerState(uint8_t device) { char stopic[TOPSZ]; char scommand[33]; if ((device < 1) || (device > devices_present)) { device = 1; } - if ((SONOFF_IFAN02 == Settings.module) && (device > 1)) { + if ((SONOFF_IFAN02 == my_module_type) && (device > 1)) { if (GetFanspeed() < MAX_FAN_SPEED) { // 4 occurs when fanspeed is 3 and RC button 2 is pressed #ifdef USE_DOMOTICZ DomoticzUpdateFanState(); // RC Button feedback @@ -358,7 +385,7 @@ void MqttPublishPowerState(byte device) } } -void MqttPublishPowerBlinkState(byte device) +void MqttPublishPowerBlinkState(uint8_t device) { char scommand[33]; @@ -373,14 +400,17 @@ void MqttPublishPowerBlinkState(byte device) /*********************************************************************************************/ +uint16_t MqttConnectCount() +{ + return mqtt_connect_count; +} + void MqttDisconnected(int state) { mqtt_connected = false; mqtt_retry_counter = Settings.mqtt_retry; - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_MQTT D_CONNECT_FAILED_TO " %s:%d, rc %d. " D_RETRY_IN " %d " D_UNIT_SECOND), - Settings.mqtt_host, Settings.mqtt_port, state, mqtt_retry_counter); - AddLog(LOG_LEVEL_INFO); + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT D_CONNECT_FAILED_TO " %s:%d, rc %d. " D_RETRY_IN " %d " D_UNIT_SECOND), Settings.mqtt_host, Settings.mqtt_port, state, mqtt_retry_counter); rules_flag.mqtt_disconnected = 1; } @@ -388,10 +418,11 @@ void MqttConnected(void) { char stopic[TOPSZ]; - if (Settings.flag.mqtt_enabled) { + if (mqtt_allowed) { AddLog_P(LOG_LEVEL_INFO, S_LOG_MQTT, PSTR(D_CONNECTED)); mqtt_connected = true; mqtt_retry_counter = 0; + mqtt_connect_count++; GetTopic_P(stopic, TELE, mqtt_topic, S_LWT); snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR(D_ONLINE)); @@ -415,8 +446,7 @@ void MqttConnected(void) if (mqtt_initial_connection_state) { snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_MODULE "\":\"%s\",\"" D_JSON_VERSION "\":\"%s%s\",\"" D_JSON_FALLBACKTOPIC "\":\"%s\",\"" D_CMND_GROUPTOPIC "\":\"%s\"}"), - my_module.name, my_version, my_image, GetFallbackTopic_P(stopic, CMND, ""), Settings.mqtt_grptopic); -// my_module.name, my_version, my_image, mqtt_client, Settings.mqtt_grptopic); + ModuleName().c_str(), my_version, my_image, GetFallbackTopic_P(stopic, CMND, ""), Settings.mqtt_grptopic); MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_INFO "1")); #ifdef USE_WEBSERVER if (Settings.webserver) { @@ -428,9 +458,9 @@ void MqttConnected(void) snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_RESTARTREASON "\":\"%s\"}"), (GetResetReason() == "Exception") ? ESP.getResetInfo().c_str() : GetResetReason().c_str()); MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_INFO "3")); - for (byte i = 1; i <= devices_present; i++) { + for (uint8_t i = 1; i <= devices_present; i++) { MqttPublishPowerState(i); - if (SONOFF_IFAN02 == Settings.module) { break; } // Report status of light relay only + if (SONOFF_IFAN02 == my_module_type) { break; } // Report status of light relay only } if (Settings.tele_period) { tele_period = Settings.tele_period -9; } // Enable TelePeriod in 9 seconds rules_flag.system_boot = 1; @@ -445,15 +475,15 @@ void MqttConnected(void) } #ifdef USE_MQTT_TLS -boolean MqttCheckTls(void) +bool MqttCheckTls(void) { char fingerprint1[60]; char fingerprint2[60]; - boolean result = false; + bool result = false; fingerprint1[0] = '\0'; fingerprint2[0] = '\0'; - for (byte i = 0; i < sizeof(Settings.mqtt_fingerprint[0]); i++) { + for (uint8_t i = 0; i < sizeof(Settings.mqtt_fingerprint[0]); i++) { snprintf_P(fingerprint1, sizeof(fingerprint1), PSTR("%s%s%02X"), fingerprint1, (i) ? " " : "", Settings.mqtt_fingerprint[0][i]); snprintf_P(fingerprint2, sizeof(fingerprint2), PSTR("%s%s%02X"), fingerprint2, (i) ? " " : "", Settings.mqtt_fingerprint[1][i]); } @@ -465,9 +495,7 @@ boolean MqttCheckTls(void) //#endif if (!EspClient.connect(Settings.mqtt_host, Settings.mqtt_port)) { - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_MQTT D_TLS_CONNECT_FAILED_TO " %s:%d. " D_RETRY_IN " %d " D_UNIT_SECOND), - Settings.mqtt_host, Settings.mqtt_port, mqtt_retry_counter); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_MQTT D_TLS_CONNECT_FAILED_TO " %s:%d. " D_RETRY_IN " %d " D_UNIT_SECOND), Settings.mqtt_host, Settings.mqtt_port, mqtt_retry_counter); } else { #ifdef USE_MQTT_TLS_CA_CERT unsigned char tls_ca_cert[] = MQTT_TLS_CA_CERT; @@ -486,7 +514,19 @@ boolean MqttCheckTls(void) AddLog_P(LOG_LEVEL_INFO, S_LOG_MQTT, PSTR(D_VERIFIED "2")); result = true; } -#endif +#ifdef MDNS_HOSTNAME + // If the hostname is set, check that as well. + // This lets certs with the hostname for the CN be used. + else if (EspClient.verify(fingerprint1, MDNS_HOSTNAME)) { + AddLog_P(LOG_LEVEL_INFO, S_LOG_MQTT, PSTR(D_VERIFIED "1")); + result = true; + } + else if (EspClient.verify(fingerprint2, MDNS_HOSTNAME)) { + AddLog_P(LOG_LEVEL_INFO, S_LOG_MQTT, PSTR(D_VERIFIED "2")); + result = true; + } +#endif // MDNS_HOSTNAME +#endif // USE_MQTT_TLS_CA_CERT } if (!result) AddLog_P(LOG_LEVEL_INFO, S_LOG_MQTT, PSTR(D_FAILED)); EspClient.stop(); @@ -499,7 +539,18 @@ void MqttReconnect(void) { char stopic[TOPSZ]; - if (!Settings.flag.mqtt_enabled) { + mqtt_allowed = Settings.flag.mqtt_enabled; + if (mqtt_allowed) { +#ifdef USE_DISCOVERY +#ifdef MQTT_HOST_DISCOVERY + MqttDiscoverServer(); +#endif // MQTT_HOST_DISCOVERY +#endif // USE_DISCOVERY + if (!strlen(Settings.mqtt_host) || !Settings.mqtt_port) { + mqtt_allowed = false; + } + } + if (!mqtt_allowed) { MqttConnected(); return; } @@ -514,14 +565,6 @@ void MqttReconnect(void) mqtt_retry_counter = Settings.mqtt_retry; global_state.mqtt_down = 1; -#ifndef USE_MQTT_TLS -#ifdef USE_DISCOVERY -#ifdef MQTT_HOST_DISCOVERY - if (!strlen(Settings.mqtt_host) && !MqttDiscoverServer()) { return; } -#endif // MQTT_HOST_DISCOVERY -#endif // USE_DISCOVERY -#endif // USE_MQTT_TLS - char *mqtt_user = NULL; char *mqtt_pwd = NULL; if (strlen(Settings.mqtt_user) > 0) mqtt_user = Settings.mqtt_user; @@ -586,13 +629,11 @@ void MqttCheck(void) if (!MqttIsConnected()) { global_state.mqtt_down = 1; if (!mqtt_retry_counter) { -#ifndef USE_MQTT_TLS #ifdef USE_DISCOVERY #ifdef MQTT_HOST_DISCOVERY if (!strlen(Settings.mqtt_host) && !mdns_begun) { return; } #endif // MQTT_HOST_DISCOVERY #endif // USE_DISCOVERY -#endif // USE_MQTT_TLS MqttReconnect(); } else { mqtt_retry_counter--; @@ -620,7 +661,7 @@ bool MqttCommand(void) uint16_t data_len = XdrvMailbox.data_len; uint16_t payload16 = XdrvMailbox.payload16; int16_t payload = XdrvMailbox.payload; - uint8_t grpflg = XdrvMailbox.grpflg; + bool grpflg = XdrvMailbox.grpflg; char *type = XdrvMailbox.topic; char *dataBuf = XdrvMailbox.data; @@ -664,20 +705,20 @@ bool MqttCommand(void) if ((data_len > 0) && (data_len < sizeof(fingerprint))) { strlcpy(fingerprint, (SC_CLEAR == Shortcut(dataBuf)) ? "" : (SC_DEFAULT == Shortcut(dataBuf)) ? (1 == index) ? MQTT_FINGERPRINT1 : MQTT_FINGERPRINT2 : dataBuf, sizeof(fingerprint)); char *p = fingerprint; - for (byte i = 0; i < 20; i++) { + for (uint8_t i = 0; i < 20; i++) { Settings.mqtt_fingerprint[index -1][i] = strtol(p, &p, 16); } restart_flag = 2; } fingerprint[0] = '\0'; - for (byte i = 0; i < sizeof(Settings.mqtt_fingerprint[index -1]); i++) { + for (uint8_t i = 0; i < sizeof(Settings.mqtt_fingerprint[index -1]); i++) { snprintf_P(fingerprint, sizeof(fingerprint), PSTR("%s%s%02X"), fingerprint, (i) ? " " : "", Settings.mqtt_fingerprint[index -1][i]); } snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, command, index, fingerprint); } #endif - else if ((CMND_MQTTCLIENT == command_code) && !grpflg) { - if ((data_len > 0) && (data_len < sizeof(Settings.mqtt_client))) { + else if (CMND_MQTTCLIENT == command_code) { + if (!grpflg && (data_len > 0) && (data_len < sizeof(Settings.mqtt_client))) { strlcpy(Settings.mqtt_client, (SC_DEFAULT == Shortcut(dataBuf)) ? MQTT_CLIENT_ID : dataBuf, sizeof(Settings.mqtt_client)); restart_flag = 2; } @@ -726,10 +767,10 @@ bool MqttCommand(void) if (data_len > 0) { char *mqtt_part = strtok(dataBuf, " "); if (mqtt_part) { - snprintf(stemp1, sizeof(stemp1), mqtt_part); + strlcpy(stemp1, mqtt_part, sizeof(stemp1)); mqtt_part = strtok(NULL, " "); if (mqtt_part) { - snprintf(mqtt_data, sizeof(mqtt_data), mqtt_part); + strlcpy(mqtt_data, mqtt_part, sizeof(mqtt_data)); } else { mqtt_data[0] = '\0'; } @@ -748,8 +789,8 @@ bool MqttCommand(void) } snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, Settings.mqtt_grptopic); } - else if ((CMND_TOPIC == command_code) && !grpflg) { - if ((data_len > 0) && (data_len < sizeof(Settings.mqtt_topic))) { + else if (CMND_TOPIC == command_code) { + if (!grpflg && (data_len > 0) && (data_len < sizeof(Settings.mqtt_topic))) { MakeValidMqtt(0, dataBuf); if (!strcmp(dataBuf, mqtt_client)) SetShortcut(dataBuf, SC_DEFAULT); strlcpy(stemp1, (SC_DEFAULT == Shortcut(dataBuf)) ? MQTT_TOPIC : dataBuf, sizeof(stemp1)); @@ -762,8 +803,8 @@ bool MqttCommand(void) } snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, Settings.mqtt_topic); } - else if ((CMND_BUTTONTOPIC == command_code) && !grpflg) { - if ((data_len > 0) && (data_len < sizeof(Settings.button_topic))) { + else if (CMND_BUTTONTOPIC == command_code) { + if (!grpflg && (data_len > 0) && (data_len < sizeof(Settings.button_topic))) { MakeValidMqtt(0, dataBuf); if (!strcmp(dataBuf, mqtt_client)) SetShortcut(dataBuf, SC_DEFAULT); switch (Shortcut(dataBuf)) { @@ -850,22 +891,24 @@ bool MqttCommand(void) const char S_CONFIGURE_MQTT[] PROGMEM = D_CONFIGURE_MQTT; const char HTTP_BTN_MENU_MQTT[] PROGMEM = - "
"; + "

"; -const char HTTP_FORM_MQTT[] PROGMEM = - "
 " D_MQTT_PARAMETERS " 
" - "
" D_HOST " (" MQTT_HOST ")

" - "
" D_PORT " (" STR(MQTT_PORT) ")

" - "
" D_CLIENT " ({m0)

" - "
" D_USER " (" MQTT_USER ")

" - "
" D_PASSWORD "

" - "
" D_TOPIC " = %topic% (" MQTT_TOPIC ")

" - "
" D_FULL_TOPIC " (" MQTT_FULLTOPIC ")

"; +const char HTTP_FORM_MQTT1[] PROGMEM = + "
 " D_MQTT_PARAMETERS " " + "" + "

" D_HOST " (" MQTT_HOST ")

" + "

" D_PORT " (" STR(MQTT_PORT) ")

" + "

" D_CLIENT " (%s)

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

" D_USER " (" MQTT_USER ")

" + "

" D_PASSWORD "

" + "

" D_TOPIC " = %%topic%% (" MQTT_TOPIC ")

" + "

" D_FULL_TOPIC " (%s)

"; void HandleMqttConfiguration(void) { - if (HttpUser()) { return; } - if (!WebAuthenticate()) { return WebServer->requestAuthentication(); } + if (!HttpCheckPriviledgedAccess()) { return; } + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_MQTT); if (WebServer->hasArg("save")) { @@ -874,23 +917,24 @@ void HandleMqttConfiguration(void) return; } - String page = FPSTR(HTTP_HEAD); - page.replace(F("{v}"), FPSTR(S_CONFIGURE_MQTT)); - page += FPSTR(HTTP_HEAD_STYLE); - - page += FPSTR(HTTP_FORM_MQTT); char str[sizeof(Settings.mqtt_client)]; - page.replace(F("{m0"), Format(str, MQTT_CLIENT_ID, sizeof(Settings.mqtt_client))); - page.replace(F("{m1"), Settings.mqtt_host); - page.replace(F("{m2"), String(Settings.mqtt_port)); - page.replace(F("{m3"), Settings.mqtt_client); - page.replace(F("{m4"), (Settings.mqtt_user[0] == '\0')?"0":Settings.mqtt_user); - page.replace(F("{m6"), Settings.mqtt_topic); - page.replace(F("{m7"), Settings.mqtt_fulltopic); - page += FPSTR(HTTP_FORM_END); - page += FPSTR(HTTP_BTN_CONF); - ShowPage(page); + WSContentStart_P(S_CONFIGURE_MQTT); + WSContentSendStyle(); + WSContentSend_P(HTTP_FORM_MQTT1, + Settings.mqtt_host, + Settings.mqtt_port, + Format(str, MQTT_CLIENT_ID, sizeof(Settings.mqtt_client)), + MQTT_CLIENT_ID, + Settings.mqtt_client); + WSContentSend_P(HTTP_FORM_MQTT2, + (Settings.mqtt_user[0] == '\0') ? "0" : Settings.mqtt_user, + Settings.mqtt_topic, + MQTT_FULLTOPIC, MQTT_FULLTOPIC, + Settings.mqtt_fulltopic); + WSContentSend_P(HTTP_FORM_END); + WSContentSpaceButton(BUTTON_CONFIGURATION); + WSContentEnd(); } void MqttSaveSettings(void) @@ -920,10 +964,9 @@ void MqttSaveSettings(void) WebGetArg("mu", tmp, sizeof(tmp)); strlcpy(Settings.mqtt_user, (!strlen(tmp)) ? MQTT_USER : (!strcmp(tmp,"0")) ? "" : tmp, sizeof(Settings.mqtt_user)); WebGetArg("mp", tmp, sizeof(tmp)); - strlcpy(Settings.mqtt_pwd, (!strlen(tmp)) ? "" : (strchr(tmp,'*')) ? Settings.mqtt_pwd : tmp, sizeof(Settings.mqtt_pwd)); - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_MQTT D_CMND_MQTTHOST " %s, " D_CMND_MQTTPORT " %d, " D_CMND_MQTTCLIENT " %s, " D_CMND_MQTTUSER " %s, " D_CMND_TOPIC " %s, " D_CMND_FULLTOPIC " %s"), + strlcpy(Settings.mqtt_pwd, (!strlen(tmp)) ? "" : (!strcmp(tmp, D_ASTERISK_PWD)) ? Settings.mqtt_pwd : tmp, sizeof(Settings.mqtt_pwd)); + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT D_CMND_MQTTHOST " %s, " D_CMND_MQTTPORT " %d, " D_CMND_MQTTCLIENT " %s, " D_CMND_MQTTUSER " %s, " D_CMND_TOPIC " %s, " D_CMND_FULLTOPIC " %s"), Settings.mqtt_host, Settings.mqtt_port, Settings.mqtt_client, Settings.mqtt_user, Settings.mqtt_topic, Settings.mqtt_fulltopic); - AddLog(LOG_LEVEL_INFO); } #endif // USE_WEBSERVER @@ -931,15 +974,15 @@ void MqttSaveSettings(void) * Interface \*********************************************************************************************/ -boolean Xdrv02(byte function) +bool Xdrv02(uint8_t function) { - boolean result = false; + bool result = false; if (Settings.flag.mqtt_enabled) { switch (function) { #ifdef USE_WEBSERVER case FUNC_WEB_ADD_BUTTON: - strncat_P(mqtt_data, HTTP_BTN_MENU_MQTT, sizeof(mqtt_data) - strlen(mqtt_data) -1); + WSContentSend_P(HTTP_BTN_MENU_MQTT); break; case FUNC_WEB_ADD_HANDLER: WebServer->on("/" WEB_HANDLE_MQTT, HandleMqttConfiguration); diff --git a/sonoff/xdrv_03_energy.ino b/sonoff/xdrv_03_energy.ino index b97c53fc2..10814bd65 100644 --- a/sonoff/xdrv_03_energy.ino +++ b/sonoff/xdrv_03_energy.ino @@ -1,7 +1,7 @@ /* xdrv_03_energy.ino - Energy sensor support for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends + Copyright (C) 2019 Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -31,9 +31,14 @@ #include +#define D_CMND_POWERCAL "PowerCal" +#define D_CMND_VOLTAGECAL "VoltageCal" +#define D_CMND_CURRENTCAL "CurrentCal" + enum EnergyCommands { CMND_POWERDELTA, CMND_POWERLOW, CMND_POWERHIGH, CMND_VOLTAGELOW, CMND_VOLTAGEHIGH, CMND_CURRENTLOW, CMND_CURRENTHIGH, + CMND_POWERCAL, CMND_VOLTAGECAL, CMND_CURRENTCAL, CMND_POWERSET, CMND_VOLTAGESET, CMND_CURRENTSET, CMND_FREQUENCYSET, CMND_ENERGYRESET, CMND_MAXENERGY, CMND_MAXENERGYSTART, CMND_MAXPOWER, CMND_MAXPOWERHOLD, CMND_MAXPOWERWINDOW, @@ -41,6 +46,7 @@ enum EnergyCommands { const char kEnergyCommands[] PROGMEM = D_CMND_POWERDELTA "|" D_CMND_POWERLOW "|" D_CMND_POWERHIGH "|" D_CMND_VOLTAGELOW "|" D_CMND_VOLTAGEHIGH "|" D_CMND_CURRENTLOW "|" D_CMND_CURRENTHIGH "|" + D_CMND_POWERCAL "|" D_CMND_VOLTAGECAL "|" D_CMND_CURRENTCAL "|" D_CMND_POWERSET "|" D_CMND_VOLTAGESET "|" D_CMND_CURRENTSET "|" D_CMND_FREQUENCYSET "|" D_CMND_ENERGYRESET "|" D_CMND_MAXENERGY "|" D_CMND_MAXENERGYSTART "|" D_CMND_MAXPOWER "|" D_CMND_MAXPOWERHOLD "|" D_CMND_MAXPOWERWINDOW "|" @@ -66,24 +72,23 @@ uint8_t energy_power_delta = 0; bool energy_type_dc = false; bool energy_power_on = true; +bool energy_min_power_flag = false; +bool energy_max_power_flag = false; +bool energy_min_voltage_flag = false; +bool energy_max_voltage_flag = false; +bool energy_min_current_flag = false; +bool energy_max_current_flag = false; -byte energy_min_power_flag = 0; -byte energy_max_power_flag = 0; -byte energy_min_voltage_flag = 0; -byte energy_max_voltage_flag = 0; -byte energy_min_current_flag = 0; -byte energy_max_current_flag = 0; - -byte energy_power_steady_cntr = 8; // Allow for power on stabilization -byte energy_max_energy_state = 0; +uint8_t energy_power_steady_cntr = 8; // Allow for power on stabilization +uint8_t energy_max_energy_state = 0; #if FEATURE_POWER_LIMIT -byte energy_mplr_counter = 0; +uint8_t energy_mplr_counter = 0; uint16_t energy_mplh_counter = 0; uint16_t energy_mplw_counter = 0; #endif // FEATURE_POWER_LIMIT -byte energy_fifth_second = 0; +uint8_t energy_fifth_second = 0; Ticker ticker_energy; int energy_command_code = 0; @@ -141,9 +146,9 @@ void EnergySaveState(void) Settings.energy_kWhtotal = RtcSettings.energy_kWhtotal; } -boolean EnergyMargin(byte type, uint16_t margin, uint16_t value, byte &flag, byte &save_flag) +bool EnergyMargin(bool type, uint16_t margin, uint16_t value, bool &flag, bool &save_flag) { - byte change; + bool change; if (!margin) return false; change = save_flag; @@ -167,8 +172,8 @@ void EnergyMarginCheck(void) uint16_t energy_power_u = 0; uint16_t energy_voltage_u = 0; uint16_t energy_current_u = 0; - boolean flag; - boolean jsonflg; + bool flag; + bool jsonflg; if (energy_power_steady_cntr) { energy_power_steady_cntr--; @@ -194,34 +199,33 @@ void EnergyMarginCheck(void) energy_voltage_u = (uint16_t)(energy_voltage); energy_current_u = (uint16_t)(energy_current * 1000); -// snprintf_P(log_data, sizeof(log_data), PSTR("NRG: W %d, U %d, I %d"), energy_power_u, energy_voltage_u, energy_current_u); -// AddLog(LOG_LEVEL_DEBUG); +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("NRG: W %d, U %d, I %d"), energy_power_u, energy_voltage_u, energy_current_u); snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{")); - jsonflg = 0; - if (EnergyMargin(0, Settings.energy_min_power, energy_power_u, flag, energy_min_power_flag)) { + jsonflg = false; + if (EnergyMargin(false, Settings.energy_min_power, energy_power_u, flag, energy_min_power_flag)) { snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s%s\"" D_CMND_POWERLOW "\":\"%s\""), mqtt_data, (jsonflg)?",":"", GetStateText(flag)); - jsonflg = 1; + jsonflg = true; } - if (EnergyMargin(1, Settings.energy_max_power, energy_power_u, flag, energy_max_power_flag)) { + if (EnergyMargin(true, Settings.energy_max_power, energy_power_u, flag, energy_max_power_flag)) { snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s%s\"" D_CMND_POWERHIGH "\":\"%s\""), mqtt_data, (jsonflg)?",":"", GetStateText(flag)); - jsonflg = 1; + jsonflg = true; } - if (EnergyMargin(0, Settings.energy_min_voltage, energy_voltage_u, flag, energy_min_voltage_flag)) { + if (EnergyMargin(false, Settings.energy_min_voltage, energy_voltage_u, flag, energy_min_voltage_flag)) { snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s%s\"" D_CMND_VOLTAGELOW "\":\"%s\""), mqtt_data, (jsonflg)?",":"", GetStateText(flag)); - jsonflg = 1; + jsonflg = true; } - if (EnergyMargin(1, Settings.energy_max_voltage, energy_voltage_u, flag, energy_max_voltage_flag)) { + if (EnergyMargin(true, Settings.energy_max_voltage, energy_voltage_u, flag, energy_max_voltage_flag)) { snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s%s\"" D_CMND_VOLTAGEHIGH "\":\"%s\""), mqtt_data, (jsonflg)?",":"", GetStateText(flag)); - jsonflg = 1; + jsonflg = true; } - if (EnergyMargin(0, Settings.energy_min_current, energy_current_u, flag, energy_min_current_flag)) { + if (EnergyMargin(false, Settings.energy_min_current, energy_current_u, flag, energy_min_current_flag)) { snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s%s\"" D_CMND_CURRENTLOW "\":\"%s\""), mqtt_data, (jsonflg)?",":"", GetStateText(flag)); - jsonflg = 1; + jsonflg = true; } - if (EnergyMargin(1, Settings.energy_max_current, energy_current_u, flag, energy_max_current_flag)) { + if (EnergyMargin(true, Settings.energy_max_current, energy_current_u, flag, energy_max_current_flag)) { snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s%s\"" D_CMND_CURRENTHIGH "\":\"%s\""), mqtt_data, (jsonflg)?",":"", GetStateText(flag)); - jsonflg = 1; + jsonflg = true; } if (jsonflg) { snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s}"), mqtt_data); @@ -304,7 +308,7 @@ void EnergyMqttShow(void) snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_TIME "\":\"%s\""), GetDateAndTime(DT_LOCAL).c_str()); int tele_period_save = tele_period; tele_period = 2; - EnergyShow(1); + EnergyShow(true); tele_period = tele_period_save; snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s}"), mqtt_data); MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); @@ -315,12 +319,12 @@ void EnergyMqttShow(void) * Commands \*********************************************************************************************/ -boolean EnergyCommand(void) +bool EnergyCommand(void) { char command [CMDSZ]; char sunit[CMDSZ]; - boolean serviced = true; - uint8_t status_flag = 0; + bool serviced = true; + bool status_flag = false; uint8_t unit = 0; unsigned long nvalue = 0; @@ -390,6 +394,7 @@ boolean EnergyCommand(void) Settings.energy_kWhtoday = energy_kWhtoday; RtcSettings.energy_kWhtoday = energy_kWhtoday; energy_daily = (float)energy_kWhtoday / 100000; + if (!RtcSettings.energy_kWhtotal && !energy_kWhtoday) { Settings.energy_kWhtotal_time = LocalTime(); } break; case 2: Settings.energy_kWhyesterday = lnum *100; @@ -398,7 +403,7 @@ boolean EnergyCommand(void) RtcSettings.energy_kWhtotal = lnum *100; Settings.energy_kWhtotal = RtcSettings.energy_kWhtotal; energy_total = (float)(RtcSettings.energy_kWhtotal + energy_kWhtoday) / 100000; - if (!energy_total) { Settings.energy_kWhtotal_time = LocalTime(); } + Settings.energy_kWhtotal_time = (!energy_kWhtoday) ? LocalTime() : Midnight(); break; } } @@ -411,23 +416,38 @@ boolean EnergyCommand(void) snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"%s\":{\"" D_JSON_TOTAL "\":%s,\"" D_JSON_YESTERDAY "\":%s,\"" D_JSON_TODAY "\":%s}}"), command, energy_total_chr, energy_yesterday_chr, energy_daily_chr); - status_flag = 1; + status_flag = true; } - else if ((CMND_POWERSET == command_code) && XnrgCall(FUNC_COMMAND)) { // Watt + else if ((CMND_POWERCAL == command_code) && XnrgCall(FUNC_COMMAND)) { // microseconds + if ((XdrvMailbox.payload > 999) && (XdrvMailbox.payload < 32001)) { Settings.energy_power_calibration = XdrvMailbox.payload; } nvalue = Settings.energy_power_calibration; unit = UNIT_MICROSECOND; } - else if ((CMND_VOLTAGESET == command_code) && XnrgCall(FUNC_COMMAND)) { // Volt + else if ((CMND_VOLTAGECAL == command_code) && XnrgCall(FUNC_COMMAND)) { // microseconds + if ((XdrvMailbox.payload > 999) && (XdrvMailbox.payload < 32001)) { Settings.energy_voltage_calibration = XdrvMailbox.payload; } nvalue = Settings.energy_voltage_calibration; unit = UNIT_MICROSECOND; } - else if ((CMND_CURRENTSET == command_code) && XnrgCall(FUNC_COMMAND)) { // milliAmpere + else if ((CMND_CURRENTCAL == command_code) && XnrgCall(FUNC_COMMAND)) { // microseconds + if ((XdrvMailbox.payload > 999) && (XdrvMailbox.payload < 32001)) { Settings.energy_current_calibration = XdrvMailbox.payload; } nvalue = Settings.energy_current_calibration; unit = UNIT_MICROSECOND; } + else if ((CMND_POWERSET == command_code) && XnrgCall(FUNC_COMMAND)) { // Watt + nvalue = Settings.energy_power_calibration; + unit = UNIT_MILLISECOND; + } + else if ((CMND_VOLTAGESET == command_code) && XnrgCall(FUNC_COMMAND)) { // Volt + nvalue = Settings.energy_voltage_calibration; + unit = UNIT_MILLISECOND; + } + else if ((CMND_CURRENTSET == command_code) && XnrgCall(FUNC_COMMAND)) { // milliAmpere + nvalue = Settings.energy_current_calibration; + unit = UNIT_MILLISECOND; + } else if ((CMND_FREQUENCYSET == command_code) && XnrgCall(FUNC_COMMAND)) { // Hz nvalue = Settings.energy_frequency_calibration; - unit = UNIT_MICROSECOND; + unit = UNIT_MILLISECOND; } #if FEATURE_POWER_LIMIT @@ -493,8 +513,9 @@ boolean EnergyCommand(void) if (serviced && !status_flag) { - if (UNIT_MICROSECOND == unit) { + if (UNIT_MILLISECOND == unit) { snprintf_P(command, sizeof(command), PSTR("%sCal"), command); + unit = UNIT_MICROSECOND; } if (Settings.flag.value_units) { @@ -546,7 +567,7 @@ const char HTTP_ENERGY_SNS4[] PROGMEM = "%s" "{s}" D_ENERGY_TOTAL "{m}%s " D_UNIT_KILOWATTHOUR "{e}"; // {s} = , {m} = , {e} = #endif // USE_WEBSERVER -void EnergyShow(boolean json) +void EnergyShow(bool json) { char speriod[20]; char sfrequency[20]; @@ -659,9 +680,9 @@ void EnergyShow(boolean json) * Interface \*********************************************************************************************/ -boolean Xdrv03(byte function) +bool Xdrv03(uint8_t function) { - boolean result = false; + bool result = false; if (FUNC_PRE_INIT == function) { EnergyDrvInit(); @@ -674,6 +695,9 @@ boolean Xdrv03(byte function) case FUNC_SET_POWER: EnergySetPowerSteadyCounter(); break; + case FUNC_LOOP: + XnrgCall(FUNC_LOOP); + break; case FUNC_SERIAL: result = XnrgCall(FUNC_SERIAL); break; @@ -682,9 +706,9 @@ boolean Xdrv03(byte function) return result; } -boolean Xsns03(byte function) +bool Xsns03(uint8_t function) { - boolean result = false; + bool result = false; if (energy_flg) { switch (function) { @@ -695,11 +719,11 @@ boolean Xsns03(byte function) EnergyMarginCheck(); break; case FUNC_JSON_APPEND: - EnergyShow(1); + EnergyShow(true); break; #ifdef USE_WEBSERVER case FUNC_WEB_APPEND: - EnergyShow(0); + EnergyShow(false); break; #endif // USE_WEBSERVER case FUNC_SAVE_BEFORE_RESTART: diff --git a/sonoff/xdrv_04_light.ino b/sonoff/xdrv_04_light.ino index ae6644dda..1dc6e8630 100644 --- a/sonoff/xdrv_04_light.ino +++ b/sonoff/xdrv_04_light.ino @@ -1,7 +1,7 @@ /* xdrv_04_light.ino - PWM, WS2812 and sonoff led support for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends + Copyright (C) 2019 Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -32,6 +32,9 @@ * 11 +WS2812 RGB(W) no (One WS2812 RGB or RGBW ledstrip) * 12 AiLight RGBW no * 13 Sonoff B1 RGBCW yes + * 19 SM16716 RGB no + * 20 SM16716+W RGBW no + * 21 SM16716+CW RGBCW yes * * light_scheme WS2812 3+ Colors 1+2 Colors Effect * ------------ ------ --------- ---------- ----------------- @@ -106,11 +109,15 @@ uint8_t light_current_color[5]; uint8_t light_new_color[5]; uint8_t light_last_color[5]; uint8_t light_signal_color[5]; +uint8_t light_color_remap[5]; + +bool light_ct_rgb_linked; uint8_t light_wheel = 0; uint8_t light_subtype = 0; uint8_t light_device = 0; uint8_t light_power = 0; +uint8_t light_old_power = 1; uint8_t light_update = 1; uint8_t light_wakeup_active = 0; uint8_t light_wakeup_dimmer = 0; @@ -198,8 +205,7 @@ void AriluxRfHandler(void) } uint16_t stored_hostcode = Settings.rf_code[1][6] << 8 | Settings.rf_code[1][7]; - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_RFR D_HOST D_CODE " 0x%04X, " D_RECEIVED " 0x%06X"), stored_hostcode, arilux_rf_received_value); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_RFR D_HOST D_CODE " 0x%04X, " D_RECEIVED " 0x%06X"), stored_hostcode, arilux_rf_received_value); if (hostcode == stored_hostcode) { char command[33]; @@ -247,23 +253,23 @@ void AriluxRfHandler(void) void AriluxRfInit(void) { - if ((pin[GPIO_ARIRFRCV] < 99) && (pin[GPIO_LED2] < 99)) { + if ((pin[GPIO_ARIRFRCV] < 99) && (pin[GPIO_LED4] < 99)) { if (Settings.last_module != Settings.module) { Settings.rf_code[1][6] = 0; Settings.rf_code[1][7] = 0; Settings.last_module = Settings.module; } arilux_rf_received_value = 0; - digitalWrite(pin[GPIO_LED2], !bitRead(led_inverted, 1)); // Turn on RF + digitalWrite(pin[GPIO_LED4], !bitRead(led_inverted, 3)); // Turn on RF attachInterrupt(pin[GPIO_ARIRFRCV], AriluxRfInterrupt, CHANGE); } } void AriluxRfDisable(void) { - if ((pin[GPIO_ARIRFRCV] < 99) && (pin[GPIO_LED2] < 99)) { + if ((pin[GPIO_ARIRFRCV] < 99) && (pin[GPIO_LED4] < 99)) { detachInterrupt(pin[GPIO_ARIRFRCV]); - digitalWrite(pin[GPIO_LED2], bitRead(led_inverted, 1)); // Turn off RF + digitalWrite(pin[GPIO_LED4], bitRead(led_inverted, 3)); // Turn off RF } } #endif // USE_ARILUX_RF @@ -353,6 +359,109 @@ void LightMy92x1Duty(uint8_t duty_r, uint8_t duty_g, uint8_t duty_b, uint8_t dut os_delay_us(12); // TStop > 12us. } +#ifdef USE_SM16716 +/*********************************************************************************************\ + * SM16716 - Controlling RGB over a synchronous serial line + * Copyright (C) 2019 Gabor Simon + * + * Source: https://community.home-assistant.io/t/cheap-uk-wifi-bulbs-with-tasmota-teardown-help-tywe3s/40508/27 + * +\*********************************************************************************************/ + +// Enable this for debug logging +//#define D_LOG_SM16716 "SM16716: " + +uint8_t sm16716_pin_clk = 100; +uint8_t sm16716_pin_dat = 100; +uint8_t sm16716_pin_sel = 100; +uint8_t sm16716_enabled = 0; + +void SM16716_SendBit(uint8_t v) +{ + /* NOTE: + * According to the spec sheet, max freq is 30 MHz, that is 16.6 ns per high/low half of the + * clk square wave. That is less than the overhead of 'digitalWrite' at this clock rate, + * so no additional delays are needed yet. */ + + digitalWrite(sm16716_pin_dat, (v != 0) ? HIGH : LOW); + //delayMicroseconds(1); + digitalWrite(sm16716_pin_clk, HIGH); + //delayMicroseconds(1); + digitalWrite(sm16716_pin_clk, LOW); +} + +void SM16716_SendByte(uint8_t v) +{ + uint8_t mask; + + for (mask = 0x80; mask; mask >>= 1) { + SM16716_SendBit(v & mask); + } +} + +void SM16716_Update(uint8_t duty_r, uint8_t duty_g, uint8_t duty_b) +{ + if (sm16716_pin_sel < 99) { + uint8_t sm16716_should_enable = (duty_r | duty_g | duty_b); + if (!sm16716_enabled && sm16716_should_enable) { +#ifdef D_LOG_SM16716 + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_SM16716 "turning color on")); +#endif // D_LOG_SM16716 + sm16716_enabled = 1; + digitalWrite(sm16716_pin_sel, HIGH); + // in testing I found it takes a minimum of ~380us to wake up the chip + // tested on a Merkury RGBW with an SM726EB + delayMicroseconds(1000); + SM16716_Init(); + } + else if (sm16716_enabled && !sm16716_should_enable) { +#ifdef D_LOG_SM16716 + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_SM16716 "turning color off")); +#endif // D_LOG_SM16716 + sm16716_enabled = 0; + digitalWrite(sm16716_pin_sel, LOW); + } + } +#ifdef D_LOG_SM16716 + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_SM16716 "Update; rgb=%02x%02x%02x"), duty_r, duty_g, duty_b); +#endif // D_LOG_SM16716 + + // send start bit + SM16716_SendBit(1); + SM16716_SendByte(duty_r); + SM16716_SendByte(duty_g); + SM16716_SendByte(duty_b); + + // send a 'do it' pulse + // (if multiple chips are chained, each one processes the 1st '1rgb' 25-bit block and + // passes on the rest, right until the one starting with 0) + //SM16716_Init(); + SM16716_SendBit(0); + SM16716_SendByte(0); + SM16716_SendByte(0); + SM16716_SendByte(0); +} + +bool SM16716_ModuleSelected(void) +{ + sm16716_pin_clk = pin[GPIO_SM16716_CLK]; + sm16716_pin_dat = pin[GPIO_SM16716_DAT]; + sm16716_pin_sel = pin[GPIO_SM16716_SEL]; +#ifdef D_LOG_SM16716 + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_SM16716 "ModuleSelected; clk_pin=%d, dat_pin=%d)"), sm16716_pin_clk, sm16716_pin_dat); +#endif // D_LOG_SM16716 + return (sm16716_pin_clk < 99) && (sm16716_pin_dat < 99); +} + +void SM16716_Init(void) +{ + for (uint8_t t_init = 0; t_init < 50; ++t_init) { + SM16716_SendBit(0); + } +} + +#endif // ifdef USE_SM16716 + /********************************************************************************************/ void LightInit(void) @@ -366,29 +475,29 @@ void LightInit(void) Settings.light_color[0] = 255; // One channel only supports Dimmer but needs max color } if (light_type < LT_PWM6) { // PWM - for (byte i = 0; i < light_type; i++) { + for (uint8_t i = 0; i < light_type; i++) { Settings.pwm_value[i] = 0; // Disable direct PWM control if (pin[GPIO_PWM1 +i] < 99) { pinMode(pin[GPIO_PWM1 +i], OUTPUT); } } - if (SONOFF_LED == Settings.module) { // Fix Sonoff Led instabilities - if (!my_module.gp.io[4]) { + if (SONOFF_LED == my_module_type) { // Fix Sonoff Led instabilities + if (!my_module.io[4]) { pinMode(4, OUTPUT); // Stop floating outputs digitalWrite(4, LOW); } - if (!my_module.gp.io[5]) { + if (!my_module.io[5]) { pinMode(5, OUTPUT); // Stop floating outputs digitalWrite(5, LOW); } - if (!my_module.gp.io[14]) { + if (!my_module.io[14]) { pinMode(14, OUTPUT); // Stop floating outputs digitalWrite(14, LOW); } } if (pin[GPIO_ARIRFRCV] < 99) { - if (pin[GPIO_LED2] < 99) { - digitalWrite(pin[GPIO_LED2], bitRead(led_inverted, 1)); // Turn off RF + if (pin[GPIO_LED4] < 99) { + digitalWrite(pin[GPIO_LED4], bitRead(led_inverted, 3)); // Turn off RF } } } @@ -401,6 +510,32 @@ void LightInit(void) max_scheme = LS_MAX + WS2812_SCHEMES; } #endif // USE_WS2812 ************************************************************************ +#ifdef USE_SM16716 + else if (LT_SM16716 == light_type - light_subtype) { + // init PWM + for (uint8_t i = 0; i < light_subtype; i++) { + Settings.pwm_value[i] = 0; // Disable direct PWM control + if (pin[GPIO_PWM1 +i] < 99) { + pinMode(pin[GPIO_PWM1 +i], OUTPUT); + } + } + // init sm16716 + pinMode(sm16716_pin_clk, OUTPUT); + digitalWrite(sm16716_pin_clk, LOW); + + pinMode(sm16716_pin_dat, OUTPUT); + digitalWrite(sm16716_pin_dat, LOW); + + if (sm16716_pin_sel < 99) { + pinMode(sm16716_pin_sel, OUTPUT); + digitalWrite(sm16716_pin_sel, LOW); + // no need to call SM16716_Init here, it will be called after sel goes HIGH + } else { + // no sel pin means you have an 'always on' chip, so init right away + SM16716_Init(); + } + } +#endif // ifdef USE_SM16716 else { light_pdi_pin = pin[GPIO_DI]; light_pdcki_pin = pin[GPIO_DCKI]; @@ -422,6 +557,39 @@ void LightInit(void) light_power = 0; light_update = 1; light_wakeup_active = 0; + + LightUpdateColorMapping(); +} + +void LightUpdateColorMapping(void) +{ + uint8_t param = Settings.param[P_RGB_REMAP] & 127; + if(param > 119){ + param = 0; + } + uint8_t tmp[] = {0,1,2,3,4}; + light_color_remap[0] = tmp[param / 24]; + for (uint8_t i = param / 24; i<4; ++i){ + tmp[i] = tmp[i+1]; + } + param = param % 24; + light_color_remap[1] = tmp[(param / 6)]; + for (uint8_t i = param / 6; i<3; ++i){ + tmp[i] = tmp[i+1]; + } + param = param % 6; + light_color_remap[2] = tmp[(param / 2)]; + for (uint8_t i = param / 2; i<2; ++i){ + tmp[i] = tmp[i+1]; + } + param = param % 2; + light_color_remap[3] = tmp[param]; + light_color_remap[4] = tmp[1-param]; + + light_ct_rgb_linked = !(Settings.param[P_RGB_REMAP] & 128); + + light_update = 1; + //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%d colors: %d %d %d %d %d") ,Settings.param[P_RGB_REMAP], light_color_remap[0],light_color_remap[1],light_color_remap[2],light_color_remap[3],light_color_remap[4]); } void LightSetColorTemp(uint16_t ct) @@ -437,15 +605,17 @@ void LightSetColorTemp(uint16_t ct) } uint16_t icold = (100 * (347 - my_ct)) / 136; uint16_t iwarm = (100 * my_ct) / 136; - if (PHILIPS == Settings.module) { + if (PHILIPS == my_module_type) { // Xiaomi Philips bulbs follow a different scheme: // channel 0=intensity, channel2=temperature Settings.light_color[1] = (uint8_t)icold; } else if (LST_RGBWC == light_subtype) { - Settings.light_color[0] = 0; - Settings.light_color[1] = 0; - Settings.light_color[2] = 0; + if(light_ct_rgb_linked){ + Settings.light_color[0] = 0; + Settings.light_color[1] = 0; + Settings.light_color[2] = 0; + } Settings.light_color[3] = (uint8_t)icold; Settings.light_color[4] = (uint8_t)iwarm; } else { @@ -473,7 +643,7 @@ void LightSetDimmer(uint8_t myDimmer) { float temp; - if (PHILIPS == Settings.module) { + if (PHILIPS == my_module_type) { // Xiaomi Philips bulbs use two PWM channels with a different scheme: float dimmer = 100 / (float)myDimmer; temp = (float)Settings.light_color[0] / dimmer; // channel 1 is intensity @@ -486,7 +656,7 @@ void LightSetDimmer(uint8_t myDimmer) Settings.light_color[0] = 255; // One PWM channel only supports Dimmer but needs max color } float dimmer = 100 / (float)myDimmer; - for (byte i = 0; i < light_subtype; i++) { + for (uint8_t i = 0; i < light_subtype; i++) { if (Settings.flag.light_signal) { temp = (float)light_signal_color[i] / dimmer; } else { @@ -500,7 +670,7 @@ void LightSetColor(void) { uint8_t highest = 0; - for (byte i = 0; i < light_subtype; i++) { + for (uint8_t i = 0; i < light_subtype; i++) { if (highest < light_current_color[i]) { highest = light_current_color[i]; } @@ -508,7 +678,7 @@ void LightSetColor(void) float mDim = (float)highest / 2.55; Settings.light_dimmer = (uint8_t)mDim; float dimmer = 100 / mDim; - for (byte i = 0; i < light_subtype; i++) { + for (uint8_t i = 0; i < light_subtype; i++) { float temp = (float)light_current_color[i] * dimmer; Settings.light_color[i] = (uint8_t)temp; } @@ -527,8 +697,7 @@ void LightSetSignal(uint16_t lo, uint16_t hi, uint16_t value) signal = 255; } } -// snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_DEBUG "Light signal %d"), signal); -// AddLog(LOG_LEVEL_DEBUG); +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "Light signal %d"), signal); light_signal_color[0] = signal; light_signal_color[1] = 255 - signal; light_signal_color[2] = 0; @@ -546,7 +715,7 @@ char* LightGetColor(uint8_t type, char* scolor) { LightSetDimmer(Settings.light_dimmer); scolor[0] = '\0'; - for (byte i = 0; i < light_subtype; i++) { + for (uint8_t i = 0; i < light_subtype; i++) { if (!type && Settings.flag.decimal_text) { snprintf_P(scolor, 25, PSTR("%s%s%d"), scolor, (i > 0) ? "," : "", light_current_color[i]); } else { @@ -589,7 +758,7 @@ void LightState(uint8_t append) snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"" D_CMND_HSBCOLOR "\":\"%d,%d,%d\""), mqtt_data, h,s,b); // Add status for each channel snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"" D_CMND_CHANNEL "\":[" ), mqtt_data); - for (byte i = 0; i < light_subtype; i++) { + for (uint8_t i = 0; i < light_subtype; i++) { snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s%s%d" ), mqtt_data, (i > 0 ? "," : ""), light_current_color[i] * 100 / 255); } snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s]" ), mqtt_data); @@ -636,7 +805,7 @@ void LightPreparePower(void) void LightFade(void) { if (0 == Settings.light_fade) { - for (byte i = 0; i < light_subtype; i++) { + for (uint8_t i = 0; i < light_subtype; i++) { light_new_color[i] = light_current_color[i]; } } else { @@ -645,7 +814,7 @@ void LightFade(void) shift = (strip_timer_counter % (Settings.light_speed -6)) ? 0 : 8; } if (shift) { - for (byte i = 0; i < light_subtype; i++) { + for (uint8_t i = 0; i < light_subtype; i++) { if (light_new_color[i] != light_current_color[i]) { if (light_new_color[i] < light_current_color[i]) { light_new_color[i] += ((light_current_color[i] - light_new_color[i]) >> shift) +1; @@ -680,7 +849,7 @@ void LightWheel(uint8_t wheel_pos) light_entry_color[3] = 0; light_entry_color[4] = 0; float dimmer = 100 / (float)Settings.light_dimmer; - for (byte i = 0; i < LST_RGB; i++) { + for (uint8_t i = 0; i < LST_RGB; i++) { float temp = (float)light_entry_color[i] / dimmer; light_entry_color[i] = (uint8_t)temp; } @@ -699,7 +868,7 @@ void LightCycleColor(int8_t direction) void LightRandomColor(void) { uint8_t light_update = 0; - for (byte i = 0; i < LST_RGB; i++) { + for (uint8_t i = 0; i < LST_RGB; i++) { if (light_new_color[i] != light_current_color[i]) { light_update = 1; } @@ -715,11 +884,12 @@ void LightRandomColor(void) void LightSetPower(void) { // light_power = XdrvMailbox.index; + light_old_power = light_power; light_power = bitRead(XdrvMailbox.index, light_device -1); if (light_wakeup_active) { light_wakeup_active--; } - if (light_power) { + if (light_power && !light_old_power) { light_update = 1; } LightAnimate(); @@ -734,7 +904,7 @@ void LightAnimate(void) if (!light_power) { // Power Off sleep = Settings.sleep; strip_timer_counter = 0; - for (byte i = 0; i < light_subtype; i++) { + for (uint8_t i = 0; i < light_subtype; i++) { light_still_on += light_new_color[i]; } if (light_still_on && Settings.light_fade && (Settings.light_scheme < LS_MAX)) { @@ -742,19 +912,23 @@ void LightAnimate(void) if (speed > 6) { speed = 6; } - for (byte i = 0; i < light_subtype; i++) { + for (uint8_t i = 0; i < light_subtype; i++) { if (light_new_color[i] > 0) { light_new_color[i] -= (light_new_color[i] >> speed) +1; } } } else { - for (byte i = 0; i < light_subtype; i++) { + for (uint8_t i = 0; i < light_subtype; i++) { light_new_color[i] = 0; } } } else { +#ifdef PWM_LIGHTSCHEME0_IGNORE_SLEEP + sleep = (LS_POWER == Settings.light_scheme) ? Settings.sleep : 0; // If no animation then use sleep as is +#else sleep = 0; +#endif // PWM_LIGHTSCHEME0_IGNORE_SLEEP switch (Settings.light_scheme) { case LS_POWER: LightSetDimmer(Settings.light_dimmer); @@ -763,7 +937,7 @@ void LightAnimate(void) case LS_WAKEUP: if (2 == light_wakeup_active) { light_wakeup_active = 1; - for (byte i = 0; i < light_subtype; i++) { + for (uint8_t i = 0; i < light_subtype; i++) { light_new_color[i] = 0; } light_wakeup_counter = 0; @@ -775,7 +949,7 @@ void LightAnimate(void) light_wakeup_dimmer++; if (light_wakeup_dimmer <= Settings.light_dimmer) { LightSetDimmer(light_wakeup_dimmer); - for (byte i = 0; i < light_subtype; i++) { + for (uint8_t i = 0; i < light_subtype; i++) { light_new_color[i] = light_current_color[i]; } } else { @@ -805,30 +979,40 @@ void LightAnimate(void) } if ((Settings.light_scheme < LS_MAX) || !light_power) { - for (byte i = 0; i < light_subtype; i++) { - if (light_last_color[i] != light_new_color[i]) { + if (memcmp(light_last_color, light_new_color, light_subtype)) { light_update = 1; - } } if (light_update) { light_update = 0; - for (byte i = 0; i < light_subtype; i++) { + for (uint8_t i = 0; i < light_subtype; i++) { light_last_color[i] = light_new_color[i]; cur_col[i] = light_last_color[i]*Settings.rgbwwTable[i]/255; cur_col[i] = (Settings.light_correction) ? ledTable[cur_col[i]] : cur_col[i]; + } + + // color remapping + uint8_t orig_col[5]; + memcpy(orig_col, cur_col, sizeof(orig_col)); + for (uint8_t i = 0; i < 5; i++) { + cur_col[i] = orig_col[light_color_remap[i]]; + } + + for (uint8_t i = 0; i < light_subtype; i++) { if (light_type < LT_PWM6) { if (pin[GPIO_PWM1 +i] < 99) { if (cur_col[i] > 0xFC) { cur_col[i] = 0xFC; // Fix unwanted blinking and PWM watchdog errors for values close to pwm_range (H801, Arilux and BN-SZ01) } uint16_t curcol = cur_col[i] * (Settings.pwm_range / 255); -// snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_APPLICATION "Cur_Col%d %d, CurCol %d"), i, cur_col[i], curcol); -// AddLog(LOG_LEVEL_DEBUG); +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION "Cur_Col%d %d, CurCol %d"), i, cur_col[i], curcol); analogWrite(pin[GPIO_PWM1 +i], bitRead(pwm_inverted, i) ? Settings.pwm_range - curcol : curcol); } } } - XdrvMailbox.index = light_device; + + char *tmp_data = XdrvMailbox.data; + uint16_t tmp_data_len = XdrvMailbox.data_len; + XdrvMailbox.data = (char*)cur_col; XdrvMailbox.data_len = sizeof(cur_col); if (XdrvCall(FUNC_SET_CHANNELS)) { @@ -839,9 +1023,28 @@ void LightAnimate(void) Ws2812SetColor(0, cur_col[0], cur_col[1], cur_col[2], cur_col[3]); } #endif // USE_ES2812 ************************************************************************ +#ifdef USE_SM16716 + else if (LT_SM16716 == light_type - light_subtype) { + // handle any PWM pins, skipping the first 3 values for sm16716 + for (uint8_t i = 3; i < light_subtype; i++) { + if (pin[GPIO_PWM1 +i-3] < 99) { + if (cur_col[i] > 0xFC) { + cur_col[i] = 0xFC; // Fix unwanted blinking and PWM watchdog errors for values close to pwm_range (H801, Arilux and BN-SZ01) + } + uint16_t curcol = cur_col[i] * (Settings.pwm_range / 255); +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION "Cur_Col%d %d, CurCol %d"), i, cur_col[i], curcol); + analogWrite(pin[GPIO_PWM1 +i-3], bitRead(pwm_inverted, i-3) ? Settings.pwm_range - curcol : curcol); + } + } + // handle sm16716 update + SM16716_Update(cur_col[0], cur_col[1], cur_col[2]); + } +#endif // ifdef USE_SM16716 else if (light_type > LT_WS2812) { LightMy92x1Duty(cur_col[0], cur_col[1], cur_col[2], cur_col[3], cur_col[4]); } + XdrvMailbox.data = tmp_data; + XdrvMailbox.data_len = tmp_data_len; } } } @@ -947,8 +1150,10 @@ void LightHsbToRgb(void) light_current_color[0] = (uint8_t)(r * 255.0f); light_current_color[1] = (uint8_t)(g * 255.0f); light_current_color[2] = (uint8_t)(b * 255.0f); - light_current_color[3] = 0; - light_current_color[4] = 0; + if(light_ct_rgb_linked){ + light_current_color[3] = 0; + light_current_color[4] = 0; + } } /********************************************************************************************/ @@ -1005,7 +1210,7 @@ void LightSetHsb(float hue, float sat, float bri, uint16_t ct, bool gotct) * Commands \*********************************************************************************************/ -boolean LightColorEntry(char *buffer, uint8_t buffer_length) +bool LightColorEntry(char *buffer, uint8_t buffer_length) { char scolor[10]; char *p; @@ -1041,7 +1246,7 @@ boolean LightColorEntry(char *buffer, uint8_t buffer_length) entry_type = 2; // Decimal } else if (((2 * light_subtype) == buffer_length) || (buffer_length > 3)) { // Hexadecimal entry - for (byte i = 0; i < buffer_length / 2; i++) { + for (uint8_t i = 0; i < tmin((uint)(buffer_length / 2), sizeof(light_entry_color)); i++) { strlcpy(scolor, buffer + (i *2), 3); light_entry_color[i] = (uint8_t)strtol(scolor, &p, 16); } @@ -1074,12 +1279,12 @@ boolean LightColorEntry(char *buffer, uint8_t buffer_length) /********************************************************************************************/ -boolean LightCommand(void) +bool LightCommand(void) { char command [CMDSZ]; - boolean serviced = true; - boolean coldim = false; - boolean valid_entry = false; + bool serviced = true; + bool coldim = false; + bool valid_entry = false; char scolor[25]; char option = (1 == XdrvMailbox.data_len) ? XdrvMailbox.data[0] : '\0'; @@ -1111,7 +1316,7 @@ boolean LightCommand(void) Settings.light_scheme = 0; coldim = true; } else { // Color3, 4, 5 and 6 - for (byte i = 0; i < LST_RGB; i++) { + for (uint8_t i = 0; i < LST_RGB; i++) { Settings.ws_color[XdrvMailbox.index -3][i] = light_entry_color[i]; } } @@ -1122,7 +1327,7 @@ boolean LightCommand(void) } if (XdrvMailbox.index >= 3) { scolor[0] = '\0'; - for (byte i = 0; i < LST_RGB; i++) { + for (uint8_t i = 0; i < LST_RGB; i++) { if (Settings.flag.decimal_text) { snprintf_P(scolor, 25, PSTR("%s%s%d"), scolor, (i > 0) ? "," : "", Settings.ws_color[XdrvMailbox.index -3][i]); } else { @@ -1196,7 +1401,7 @@ boolean LightCommand(void) if (LightColorEntry(color, strlen(color))) { Ws2812SetColor(idx, light_entry_color[0], light_entry_color[1], light_entry_color[2], light_entry_color[3]); idx++; - if (idx >= Settings.light_pixels) break; + if (idx > Settings.light_pixels) break; } else { break; } @@ -1319,7 +1524,6 @@ boolean LightCommand(void) bool validtable = (XdrvMailbox.data_len > 0); char scolor[25]; if (validtable) { - uint16_t HSB[3]; if (strstr(XdrvMailbox.data, ",")) { // Command with up to 5 comma separated parameters for (int i = 0; i < LST_RGBWC; i++) { char *substr; @@ -1337,7 +1541,7 @@ boolean LightCommand(void) light_update = 1; } scolor[0] = '\0'; - for (byte i = 0; i < LST_RGBWC; i++) { + for (uint8_t i = 0; i < LST_RGBWC; i++) { snprintf_P(scolor, 25, PSTR("%s%s%d"), scolor, (i > 0) ? "," : "", Settings.rgbwwTable[i]); } snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, command, XdrvMailbox.index, scolor); @@ -1396,9 +1600,9 @@ boolean LightCommand(void) * Interface \*********************************************************************************************/ -boolean Xdrv04(byte function) +bool Xdrv04(uint8_t function) { - boolean result = false; + bool result = false; if (light_type) { switch (function) { diff --git a/sonoff/xdrv_05_irremote.ino b/sonoff/xdrv_05_irremote.ino index ef02e61ba..a72bc2764 100644 --- a/sonoff/xdrv_05_irremote.ino +++ b/sonoff/xdrv_05_irremote.ino @@ -1,7 +1,7 @@ /* xdrv_05_irremote.ino - infra red support for Sonoff-Tasmota - Copyright (C) 2018 Heiko Krupp, Lazar Obradovic and Theo Arends + Copyright (C) 2019 Heiko Krupp, Lazar Obradovic and Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -58,7 +58,7 @@ IRMitsubishiAC *mitsubir = NULL; const char kFanSpeedOptions[] = "A12345S"; const char kHvacModeOptions[] = "HDCA"; -#endif +#endif // USE_IR_HVAC /*********************************************************************************************\ * IR Send @@ -67,6 +67,7 @@ const char kHvacModeOptions[] = "HDCA"; #include IRsend *irsend = NULL; +bool irsend_active = false; void IrSendInit(void) { @@ -113,13 +114,12 @@ void IrReceiveCheck(void) if (irrecv->decode(&results)) { - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_IRR "RawLen %d, Overflow %d, Bits %d, Value %08X, Decode %d"), - results.rawlen, results.overflow, results.bits, results.value, results.decode_type); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_IRR "Echo %d, RawLen %d, Overflow %d, Bits %d, Value 0x%08X, Decode %d"), + irsend_active, results.rawlen, results.overflow, results.bits, results.value, results.decode_type); unsigned long now = millis(); // if ((now - ir_lasttime > IR_TIME_AVOID_DUPLICATE) && (UNKNOWN != results.decode_type) && (results.bits > 0)) { - if (now - ir_lasttime > IR_TIME_AVOID_DUPLICATE) { + if (!irsend_active && (now - ir_lasttime > IR_TIME_AVOID_DUPLICATE)) { ir_lasttime = now; iridx = results.decode_type; @@ -129,7 +129,7 @@ void IrReceiveCheck(void) if (Settings.flag.ir_receive_decimal) { snprintf_P(stemp, sizeof(stemp), PSTR("%u"), (uint32_t)results.value); } else { - snprintf_P(stemp, sizeof(stemp), PSTR("\"%lX\""), (uint32_t)results.value); + snprintf_P(stemp, sizeof(stemp), PSTR("\"0x%lX\""), (uint32_t)results.value); } snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_IRRECEIVED "\":{\"" D_JSON_IR_PROTOCOL "\":\"%s\",\"" D_JSON_IR_BITS "\":%d,\"" D_JSON_IR_DATA "\":%s"), GetTextIndexed(sirtype, sizeof(sirtype), iridx, kIrRemoteProtocols), results.bits, stemp); @@ -181,10 +181,10 @@ void IrReceiveCheck(void) TOSHIBA ********************/ -boolean IrHvacToshiba(const char *HVAC_Mode, const char *HVAC_FanMode, boolean HVAC_Power, int HVAC_Temp) +bool IrHvacToshiba(const char *HVAC_Mode, const char *HVAC_FanMode, bool HVAC_Power, int HVAC_Temp) { uint16_t rawdata[2 + 2 * 8 * HVAC_TOSHIBA_DATALEN + 2]; - byte data[HVAC_TOSHIBA_DATALEN] = {0xF2, 0x0D, 0x03, 0xFC, 0x01, 0x00, 0x00, 0x00, 0x00}; + uint8_t data[HVAC_TOSHIBA_DATALEN] = {0xF2, 0x0D, 0x03, 0xFC, 0x01, 0x00, 0x00, 0x00, 0x00}; char *p; uint8_t mode; @@ -201,7 +201,7 @@ boolean IrHvacToshiba(const char *HVAC_Mode, const char *HVAC_FanMode, boolean H data[6] = (p - kHvacModeOptions) ^ 0x03; // HOT = 0x03, DRY = 0x02, COOL = 0x01, AUTO = 0x00 if (!HVAC_Power) { - data[6] = (byte)0x07; // Turn OFF HVAC + data[6] = (uint8_t)0x07; // Turn OFF HVAC } if (HVAC_FanMode == NULL) { @@ -220,7 +220,7 @@ boolean IrHvacToshiba(const char *HVAC_Mode, const char *HVAC_FanMode, boolean H mode = mode << 5; // AUTO = 0x00, SPEED = 0x40, 0x60, 0x80, 0xA0, 0xC0, SILENT = 0x00 data[6] = data[6] | mode; - byte Temp; + uint8_t Temp; if (HVAC_Temp > 30) { Temp = 30; } @@ -230,15 +230,15 @@ boolean IrHvacToshiba(const char *HVAC_Mode, const char *HVAC_FanMode, boolean H else { Temp = HVAC_Temp; } - data[5] = (byte)(Temp - 17) << 4; + data[5] = (uint8_t)(Temp - 17) << 4; data[HVAC_TOSHIBA_DATALEN - 1] = 0; for (int x = 0; x < HVAC_TOSHIBA_DATALEN - 1; x++) { - data[HVAC_TOSHIBA_DATALEN - 1] = (byte)data[x] ^ data[HVAC_TOSHIBA_DATALEN - 1]; // CRC is a simple bits addition + data[HVAC_TOSHIBA_DATALEN - 1] = (uint8_t)data[x] ^ data[HVAC_TOSHIBA_DATALEN - 1]; // CRC is a simple bits addition } int i = 0; - byte mask = 1; + uint8_t mask = 1; //header rawdata[i++] = HVAC_TOSHIBA_HDR_MARK; @@ -262,10 +262,11 @@ boolean IrHvacToshiba(const char *HVAC_Mode, const char *HVAC_FanMode, boolean H rawdata[i++] = HVAC_TOSHIBA_RPT_MARK; rawdata[i++] = HVAC_TOSHIBA_RPT_SPACE; - noInterrupts(); +// noInterrupts(); + irsend_active = true; irsend->sendRaw(rawdata, i, 38); irsend->sendRaw(rawdata, i, 38); - interrupts(); +// interrupts(); return false; } @@ -275,7 +276,7 @@ boolean IrHvacToshiba(const char *HVAC_Mode, const char *HVAC_FanMode, boolean H MITSUBISHI ********************/ -boolean IrHvacMitsubishi(const char *HVAC_Mode, const char *HVAC_FanMode, boolean HVAC_Power, int HVAC_Temp) +bool IrHvacMitsubishi(const char *HVAC_Mode, const char *HVAC_FanMode, bool HVAC_Power, int HVAC_Temp) { char *p; uint8_t mode; @@ -312,9 +313,8 @@ boolean IrHvacMitsubishi(const char *HVAC_Mode, const char *HVAC_FanMode, boolea mitsubir->setVane(MITSUBISHI_AC_VANE_AUTO); mitsubir->send(); -// snprintf_P(log_data, sizeof(log_data), PSTR("IRHVAC: Mitsubishi Power %d, Mode %d, FanSpeed %d, Temp %d, VaneMode %d"), +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("IRHVAC: Mitsubishi Power %d, Mode %d, FanSpeed %d, Temp %d, VaneMode %d"), // mitsubir->getPower(), mitsubir->getMode(), mitsubir->getFan(), mitsubir->getTemp(), mitsubir->getVane()); -// AddLog(LOG_LEVEL_DEBUG); return false; } @@ -324,14 +324,14 @@ boolean IrHvacMitsubishi(const char *HVAC_Mode, const char *HVAC_FanMode, boolea LG ********************/ -boolean IrHvacLG(const char *HVAC_Mode, const char *HVAC_FanMode, boolean HVAC_Power, int HVAC_Temp) +bool IrHvacLG(const char *HVAC_Mode, const char *HVAC_FanMode, bool HVAC_Power, int HVAC_Temp) { uint32_t LG_Code; - byte data[HVAC_LG_DATALEN]; - static boolean hvacOn = false; + uint8_t data[HVAC_LG_DATALEN]; + static bool hvacOn = false; char *p; uint8_t mode; - byte Temp; + uint8_t Temp; // Constant data data[0] = 0x08; @@ -339,11 +339,11 @@ boolean IrHvacLG(const char *HVAC_Mode, const char *HVAC_FanMode, boolean HVAC_P data[2] = 0x00; if (!HVAC_Power) { - data[2] = (byte)0x0C; // Turn OFF HVAC, code 0x88C0051 - data[3] = (byte)0x00; - data[4] = (byte)0x00; - data[5] = (byte)0x05; - data[6] = (byte)0x01; + data[2] = (uint8_t)0x0C; // Turn OFF HVAC, code 0x88C0051 + data[3] = (uint8_t)0x00; + data[4] = (uint8_t)0x00; + data[5] = (uint8_t)0x05; + data[6] = (uint8_t)0x01; hvacOn = false; } @@ -379,8 +379,7 @@ boolean IrHvacLG(const char *HVAC_Mode, const char *HVAC_FanMode, boolean HVAC_P hvacOn = true; } -// snprintf_P(log_data, sizeof(log_data), PSTR("IRHVAC: HvacMode %s, ModeVal %d, Code %d"), p, mode, data[3]); -// AddLog(LOG_LEVEL_DEBUG); +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("IRHVAC: HvacMode %s, ModeVal %d, Code %d"), p, mode, data[3]); // Set code for HVAC temperature - data[4] if (HVAC_Temp > 30) { @@ -392,7 +391,7 @@ boolean IrHvacLG(const char *HVAC_Mode, const char *HVAC_FanMode, boolean HVAC_P else { Temp = HVAC_Temp; } - data[4] = (byte)(Temp - 15); + data[4] = (uint8_t)(Temp - 15); // Set code for HVAC fan mode - data[5] if (HVAC_FanMode == NULL) { @@ -412,8 +411,7 @@ boolean IrHvacLG(const char *HVAC_Mode, const char *HVAC_FanMode, boolean HVAC_P data[5] = (mode * 2) - 2; // Low = 0x00, Mid = 0x02, High = 0x04 } -// snprintf_P(log_data, sizeof(log_data), PSTR("IRHVAC: FanMode %s, ModeVal %d, Code %d"), p, mode, data[5]); -// AddLog(LOG_LEVEL_DEBUG); +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("IRHVAC: FanMode %s, ModeVal %d, Code %d"), p, mode, data[5]); // Set CRC code - data[6] data[6] = (data[3] + data[4] + data[5]) & 0x0f; // CRC @@ -426,13 +424,13 @@ boolean IrHvacLG(const char *HVAC_Mode, const char *HVAC_FanMode, boolean HVAC_P } LG_Code = LG_Code + data[6]; -// snprintf_P(log_data, sizeof(log_data), PSTR("IRHVAC: LG_Code %d"), LG_Code); -// AddLog(LOG_LEVEL_DEBUG); +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("IRHVAC: LG_Code %d"), LG_Code); // Send LG IR Code - noInterrupts(); +// noInterrupts(); + irsend_active = true; irsend->sendLG(LG_Code, 28); - interrupts(); +// interrupts(); return false; } @@ -442,23 +440,24 @@ boolean IrHvacLG(const char *HVAC_Mode, const char *HVAC_FanMode, boolean HVAC_P Fujitsu ********************/ -boolean IrHvacFujitsu(const char *HVAC_Mode, const char *HVAC_FanMode, boolean HVAC_Power, int HVAC_Temp) +bool IrHvacFujitsu(const char *HVAC_Mode, const char *HVAC_FanMode, bool HVAC_Power, int HVAC_Temp) { const char kFujitsuHvacModeOptions[] = "HDCAF"; -// snprintf_P(log_data, sizeof(log_data), PSTR("FUJITSU: mode:%s, fan:%s, power:%u, temp:%u"), HVAC_Mode, HVAC_FanMode, HVAC_Power, HVAC_Temp); -// AddLog(LOG_LEVEL_DEBUG); +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("FUJITSU: mode:%s, fan:%s, power:%u, temp:%u"), HVAC_Mode, HVAC_FanMode, HVAC_Power, HVAC_Temp); IRFujitsuAC ac(pin[GPIO_IRSEND]); + irsend_active = true; + if (0 == HVAC_Power) { ac.off(); ac.send(); return false; } - byte modes[5] = {FUJITSU_AC_MODE_HEAT, FUJITSU_AC_MODE_DRY, FUJITSU_AC_MODE_COOL, FUJITSU_AC_MODE_AUTO, FUJITSU_AC_MODE_FAN}; - byte fanModes[7] = {FUJITSU_AC_FAN_AUTO, FUJITSU_AC_FAN_LOW, FUJITSU_AC_FAN_MED, FUJITSU_AC_FAN_HIGH, FUJITSU_AC_FAN_HIGH, FUJITSU_AC_FAN_HIGH, FUJITSU_AC_FAN_QUIET}; + uint8_t modes[5] = {FUJITSU_AC_MODE_HEAT, FUJITSU_AC_MODE_DRY, FUJITSU_AC_MODE_COOL, FUJITSU_AC_MODE_AUTO, FUJITSU_AC_MODE_FAN}; + uint8_t fanModes[7] = {FUJITSU_AC_FAN_AUTO, FUJITSU_AC_FAN_LOW, FUJITSU_AC_FAN_MED, FUJITSU_AC_FAN_HIGH, FUJITSU_AC_FAN_HIGH, FUJITSU_AC_FAN_HIGH, FUJITSU_AC_FAN_QUIET}; ac.setCmd(FUJITSU_AC_CMD_TURN_ON); ac.setSwing(FUJITSU_AC_SWING_VERT); @@ -505,11 +504,11 @@ boolean IrHvacFujitsu(const char *HVAC_Mode, const char *HVAC_FanMode, boolean H { "Vendor": "", "Power": <0|1>, "Mode": "", "FanSpeed": "<1|2|3|4|5|Auto|Silence>", "Temp": <17..30> } */ -boolean IrSendCommand(void) +bool IrSendCommand(void) { char command [CMDSZ]; - boolean serviced = true; - boolean error = false; + bool serviced = true; + bool error = false; int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic, kIrRemoteCommands); if (-1 == command_code) { @@ -531,15 +530,14 @@ boolean IrSendCommand(void) if (count) { // At least two raw data values count++; uint16_t raw_array[count]; // It's safe to use stack for up to 240 packets (limited by mqtt_data length) - byte i = 0; + uint8_t i = 0; for (str = strtok_r(NULL, ", ", &p); str && i < count; str = strtok_r(NULL, ", ", &p)) { raw_array[i++] = strtoul(str, NULL, 0); // Allow decimal (5246996) and hexadecimal (0x501014) input } -// snprintf_P(log_data, sizeof(log_data), PSTR("IRS: Count %d, Freq %d, Arr[0] %d, Arr[count -1] %d"), -// count, freq, raw_array[0], raw_array[count -1]); -// AddLog(LOG_LEVEL_DEBUG); +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("IRS: Count %d, Freq %d, Arr[0] %d, Arr[count -1] %d"), count, freq, raw_array[0], raw_array[count -1]); + irsend_active = true; irsend->sendRaw(raw_array, count, freq); if (!count) { snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, D_JSON_FAILED); @@ -567,10 +565,10 @@ boolean IrSendCommand(void) char protocol_text[20]; int protocol_code = GetCommandCode(protocol_text, sizeof(protocol_text), protocol, kIrRemoteProtocols); - snprintf_P(log_data, sizeof(log_data), PSTR("IRS: protocol_text %s, protocol %s, bits %d, data %u (0x%lX), protocol_code %d"), + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("IRS: protocol_text %s, protocol %s, bits %d, data %u (0x%lX), protocol_code %d"), protocol_text, protocol, bits, data, data, protocol_code); - AddLog(LOG_LEVEL_DEBUG); + irsend_active = true; switch (protocol_code) { case NEC: irsend->sendNEC(data, (bits > NEC_BITS) ? NEC_BITS : bits); break; @@ -589,6 +587,7 @@ boolean IrSendCommand(void) case PANASONIC: irsend->sendPanasonic(bits, data); break; default: + irsend_active = false; snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, D_JSON_PROTOCOL_NOT_SUPPORTED); } } @@ -611,7 +610,7 @@ boolean IrSendCommand(void) const char *HVAC_FanMode; const char *HVAC_Vendor; int HVAC_Temp = 21; - boolean HVAC_Power = true; + bool HVAC_Power = true; if (XdrvMailbox.data_len) { char dataBufUc[XdrvMailbox.data_len]; @@ -629,9 +628,7 @@ boolean IrSendCommand(void) HVAC_FanMode = root[D_JSON_IRHVAC_FANSPEED]; HVAC_Temp = root[D_JSON_IRHVAC_TEMP]; -// snprintf_P(log_data, sizeof(log_data), PSTR("IRHVAC: Received Vendor %s, Power %d, Mode %s, FanSpeed %s, Temp %d"), -// HVAC_Vendor, HVAC_Power, HVAC_Mode, HVAC_FanMode, HVAC_Temp); -// AddLog(LOG_LEVEL_DEBUG); +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("IRHVAC: Received Vendor %s, Power %d, Mode %s, FanSpeed %s, Temp %d"), HVAC_Vendor, HVAC_Power, HVAC_Mode, HVAC_FanMode, HVAC_Temp); char vendor[20]; int vendor_code = GetCommandCode(vendor, sizeof(vendor), HVAC_Vendor, kIrHvacVendors); @@ -666,9 +663,9 @@ boolean IrSendCommand(void) * Interface \*********************************************************************************************/ -boolean Xdrv05(byte function) +bool Xdrv05(uint8_t function) { - boolean result = false; + bool result = false; if ((pin[GPIO_IRSEND] < 99) || (pin[GPIO_IRRECV] < 99)) { switch (function) { @@ -688,6 +685,7 @@ boolean Xdrv05(byte function) IrReceiveCheck(); // check if there's anything on IR side } #endif // USE_IR_RECEIVE + irsend_active = false; // re-enable IR reception break; case FUNC_COMMAND: if (pin[GPIO_IRSEND] < 99) { diff --git a/sonoff/xdrv_06_snfbridge.ino b/sonoff/xdrv_06_snfbridge.ino index 30bd6e4f0..c4b4d209b 100644 --- a/sonoff/xdrv_06_snfbridge.ino +++ b/sonoff/xdrv_06_snfbridge.ino @@ -1,7 +1,7 @@ /* xdrv_06_snfbridge.ino - sonoff RF bridge 433 support for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends and Erik Andrén Zachrisson (fw update) + Copyright (C) 2019 Theo Arends and Erik Andrén Zachrisson (fw update) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -56,7 +56,7 @@ unsigned long sonoff_bridge_last_learn_time = 0; ssize_t rf_find_hex_record_start(uint8_t *buf, size_t size) { - for (int i = 0; i < size; i++) { + for (size_t i = 0; i < size; i++) { if (buf[i] == ':') { return i; } @@ -66,7 +66,7 @@ ssize_t rf_find_hex_record_start(uint8_t *buf, size_t size) ssize_t rf_find_hex_record_end(uint8_t *buf, size_t size) { - for (ssize_t i = 0; i < size; i++) { + for (size_t i = 0; i < size; i++) { if (buf[i] == '\n') { return i; } @@ -251,7 +251,7 @@ void SonoffBridgeReceived(void) low_time = serial_in_buffer[3] << 8 | serial_in_buffer[4]; // Low time in uSec high_time = serial_in_buffer[5] << 8 | serial_in_buffer[6]; // High time in uSec if (low_time && high_time) { - for (byte i = 0; i < 9; i++) { + for (uint8_t i = 0; i < 9; i++) { Settings.rf_code[sonoff_bridge_learn_key][i] = serial_in_buffer[i +1]; } snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, D_CMND_RFKEY, sonoff_bridge_learn_key, D_JSON_LEARNED); @@ -274,7 +274,7 @@ void SonoffBridgeReceived(void) sonoff_bridge_last_received_id = received_id; sonoff_bridge_last_time = now; strncpy_P(rfkey, PSTR("\"" D_JSON_NONE "\""), sizeof(rfkey)); - for (byte i = 1; i <= 16; i++) { + for (uint8_t i = 1; i <= 16; i++) { if (Settings.rf_code[i][0]) { uint32_t send_id = Settings.rf_code[i][6] << 16 | Settings.rf_code[i][7] << 8 | Settings.rf_code[i][8]; if (send_id == received_id) { @@ -300,7 +300,7 @@ void SonoffBridgeReceived(void) } } -boolean SonoffBridgeSerialInput(void) +bool SonoffBridgeSerialInput(void) { // iTead Rf Universal Transceiver Module Serial Protocol Version 1.0 (20170420) static int8_t receive_len = 0; @@ -352,7 +352,7 @@ boolean SonoffBridgeSerialInput(void) return 0; } -void SonoffBridgeSendCommand(byte code) +void SonoffBridgeSendCommand(uint8_t code) { Serial.write(0xAA); // Start of Text Serial.write(code); // Command or Acknowledge @@ -370,7 +370,7 @@ void SonoffBridgeSendCode(uint32_t code) { Serial.write(0xAA); // Start of Text Serial.write(0xA5); // Send following code - for (byte i = 0; i < 6; i++) { + for (uint8_t i = 0; i < 6; i++) { Serial.write(Settings.rf_code[0][i]); } Serial.write((code >> 16) & 0xff); @@ -387,7 +387,7 @@ void SonoffBridgeSend(uint8_t idx, uint8_t key) key--; // Support 1 to 16 Serial.write(0xAA); // Start of Text Serial.write(0xA5); // Send following code - for (byte i = 0; i < 8; i++) { + for (uint8_t i = 0; i < 8; i++) { Serial.write(Settings.rf_code[idx][i]); } if (0 == idx) { @@ -418,10 +418,10 @@ void SonoffBridgeLearn(uint8_t key) * Commands \*********************************************************************************************/ -boolean SonoffBridgeCommand(void) +bool SonoffBridgeCommand(void) { char command [CMDSZ]; - boolean serviced = true; + bool serviced = true; int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic, kSonoffBridgeCommands); if (-1 == command_code) { @@ -485,7 +485,7 @@ boolean SonoffBridgeCommand(void) snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, command, XdrvMailbox.index, D_JSON_SET_TO_DEFAULT); } else if (4 == XdrvMailbox.payload) { // Save RF data provided by RFSync, RfLow, RfHigh and last RfCode - for (byte i = 0; i < 6; i++) { + for (uint8_t i = 0; i < 6; i++) { Settings.rf_code[XdrvMailbox.index][i] = Settings.rf_code[0][i]; } Settings.rf_code[XdrvMailbox.index][6] = (sonoff_bridge_last_send_code >> 16) & 0xff; @@ -566,11 +566,11 @@ void SonoffBridgeInit(void) * Interface \*********************************************************************************************/ -boolean Xdrv06(byte function) +bool Xdrv06(uint8_t function) { - boolean result = false; + bool result = false; - if (SONOFF_BRIDGE == Settings.module) { + if (SONOFF_BRIDGE == my_module_type) { switch (function) { case FUNC_INIT: SonoffBridgeInit(); diff --git a/sonoff/xdrv_07_domoticz.ino b/sonoff/xdrv_07_domoticz.ino index 3eb3fa275..0bd275d7c 100644 --- a/sonoff/xdrv_07_domoticz.ino +++ b/sonoff/xdrv_07_domoticz.ino @@ -1,7 +1,7 @@ /* xdrv_07_domoticz.ino - domoticz support for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends + Copyright (C) 2019 Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -41,8 +41,8 @@ const char S_JSON_DOMOTICZ_COMMAND_INDEX_LVALUE[] PROGMEM = "{\"" D_CMND_DOMOTIC char domoticz_in_topic[] = DOMOTICZ_IN_TOPIC; char domoticz_out_topic[] = DOMOTICZ_OUT_TOPIC; -boolean domoticz_subscribe = false; -byte domoticz_update_flag = 1; +bool domoticz_subscribe = false; +uint8_t domoticz_update_flag = 1; int domoticz_update_timer = 0; unsigned long fan_debounce = 0; // iFan02 state debounce timer @@ -80,7 +80,7 @@ void MqttPublishDomoticzFanState() int fan_speed = GetFanspeed(); snprintf_P(svalue, sizeof(svalue), PSTR("%d"), fan_speed * 10); snprintf_P(mqtt_data, sizeof(mqtt_data), DOMOTICZ_MESSAGE, - Settings.domoticz_relay_idx[1], (0 == fan_speed) ? 0 : 2, svalue, DomoticzBatteryQuality(), DomoticzRssiQuality()); + (int)Settings.domoticz_relay_idx[1], (0 == fan_speed) ? 0 : 2, svalue, DomoticzBatteryQuality(), DomoticzRssiQuality()); MqttPublish(domoticz_in_topic); fan_debounce = millis(); @@ -95,26 +95,26 @@ void DomoticzUpdateFanState() domoticz_update_flag = 1; } -void MqttPublishDomoticzPowerState(byte device) +void MqttPublishDomoticzPowerState(uint8_t device) { if (Settings.flag.mqtt_enabled) { if ((device < 1) || (device > devices_present)) { device = 1; } if (Settings.domoticz_relay_idx[device -1]) { - if ((SONOFF_IFAN02 == Settings.module) && (device > 1)) { + if ((SONOFF_IFAN02 == my_module_type) && (device > 1)) { // Fan handled by MqttPublishDomoticzFanState } else { char svalue[8]; // Dimmer value snprintf_P(svalue, sizeof(svalue), PSTR("%d"), Settings.light_dimmer); snprintf_P(mqtt_data, sizeof(mqtt_data), DOMOTICZ_MESSAGE, - Settings.domoticz_relay_idx[device -1], (power & (1 << (device -1))) ? 1 : 0, (light_type) ? svalue : "", DomoticzBatteryQuality(), DomoticzRssiQuality()); + (int)Settings.domoticz_relay_idx[device -1], (power & (1 << (device -1))) ? 1 : 0, (light_type) ? svalue : "", DomoticzBatteryQuality(), DomoticzRssiQuality()); MqttPublish(domoticz_in_topic); } } } } -void DomoticzUpdatePowerState(byte device) +void DomoticzUpdatePowerState(uint8_t device) { if (domoticz_update_flag) { MqttPublishDomoticzPowerState(device); @@ -128,8 +128,8 @@ void DomoticzMqttUpdate(void) domoticz_update_timer--; if (domoticz_update_timer <= 0) { domoticz_update_timer = Settings.domoticz_update_timer; - for (byte i = 1; i <= devices_present; i++) { - if ((SONOFF_IFAN02 == Settings.module) && (i > 1)) { + for (uint8_t i = 1; i <= devices_present; i++) { + if ((SONOFF_IFAN02 == my_module_type) && (i > 1)) { MqttPublishDomoticzFanState(); break; } else { @@ -143,7 +143,7 @@ void DomoticzMqttUpdate(void) void DomoticzMqttSubscribe(void) { uint8_t maxdev = (devices_present > MAX_DOMOTICZ_IDX) ? MAX_DOMOTICZ_IDX : devices_present; - for (byte i = 0; i < maxdev; i++) { + for (uint8_t i = 0; i < maxdev; i++) { if (Settings.domoticz_relay_idx[i]) { domoticz_subscribe = true; } @@ -181,7 +181,7 @@ void DomoticzMqttSubscribe(void) } */ -boolean DomoticzMqttData(void) +bool DomoticzMqttData(void) { char stemp1[10]; unsigned long idx = 0; @@ -206,16 +206,15 @@ boolean DomoticzMqttData(void) nvalue = domoticz["nvalue"]; } - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_DOMOTICZ "idx %d, nvalue %d"), idx, nvalue); - AddLog(LOG_LEVEL_DEBUG_MORE); + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_DOMOTICZ "idx %d, nvalue %d"), idx, nvalue); if ((idx > 0) && (nvalue >= 0) && (nvalue <= 15)) { uint8_t maxdev = (devices_present > MAX_DOMOTICZ_IDX) ? MAX_DOMOTICZ_IDX : devices_present; - for (byte i = 0; i < maxdev; i++) { + for (uint8_t i = 0; i < maxdev; i++) { if (idx == Settings.domoticz_relay_idx[i]) { bool iscolordimmer = strcmp_P(domoticz["dtype"],PSTR("Color Switch")) == 0; snprintf_P(stemp1, sizeof(stemp1), PSTR("%d"), i +1); - if ((SONOFF_IFAN02 == Settings.module) && (1 == i)) { // Idx 2 is fanspeed + if ((SONOFF_IFAN02 == my_module_type) && (1 == i)) { // Idx 2 is fanspeed uint8_t svalue = 0; if (domoticz.containsKey("svalue1")) { svalue = domoticz["svalue1"]; @@ -275,8 +274,7 @@ boolean DomoticzMqttData(void) return 1; } - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_DOMOTICZ D_RECEIVED_TOPIC " %s, " D_DATA " %s"), XdrvMailbox.topic, XdrvMailbox.data); - AddLog(LOG_LEVEL_DEBUG_MORE); + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_DOMOTICZ D_RECEIVED_TOPIC " %s, " D_DATA " %s"), XdrvMailbox.topic, XdrvMailbox.data); domoticz_update_flag = 0; } @@ -287,10 +285,10 @@ boolean DomoticzMqttData(void) * Commands \*********************************************************************************************/ -boolean DomoticzCommand(void) +bool DomoticzCommand(void) { char command [CMDSZ]; - boolean serviced = true; + bool serviced = true; uint8_t dmtcz_len = strlen(D_CMND_DOMOTICZ); // Prep for string length change if (!strncasecmp_P(XdrvMailbox.topic, PSTR(D_CMND_DOMOTICZ), dmtcz_len)) { // Prefix @@ -336,9 +334,9 @@ boolean DomoticzCommand(void) return serviced; } -boolean DomoticzSendKey(byte key, byte device, byte state, byte svalflg) +bool DomoticzSendKey(uint8_t key, uint8_t device, uint8_t state, uint8_t svalflg) { - boolean result = 0; + bool result = 0; if (device <= MAX_DOMOTICZ_IDX) { if ((Settings.domoticz_key_idx[device -1] || Settings.domoticz_switch_idx[device -1]) && (svalflg)) { @@ -374,7 +372,7 @@ uint8_t DomoticzHumidityState(char *hum) return (!h) ? 0 : (h < 40) ? 2 : (h > 70) ? 3 : 1; } -void DomoticzSensor(byte idx, char *data) +void DomoticzSensor(uint8_t idx, char *data) { if (Settings.domoticz_sensor_idx[idx]) { char dmess[90]; @@ -392,7 +390,7 @@ void DomoticzSensor(byte idx, char *data) } } -void DomoticzSensor(byte idx, uint32_t value) +void DomoticzSensor(uint8_t idx, uint32_t value) { char data[16]; snprintf_P(data, sizeof(data), PSTR("%d"), value); @@ -431,25 +429,27 @@ void DomoticzSensorPowerEnergy(int power, char *energy) const char S_CONFIGURE_DOMOTICZ[] PROGMEM = D_CONFIGURE_DOMOTICZ; const char HTTP_BTN_MENU_DOMOTICZ[] PROGMEM = - "
"; + "

"; const char HTTP_FORM_DOMOTICZ[] PROGMEM = - "
 " D_DOMOTICZ_PARAMETERS " 
" - "
"; + "
 " D_DOMOTICZ_PARAMETERS " " + "" + "
" + "
"; const char HTTP_FORM_DOMOTICZ_RELAY[] PROGMEM = - "" - ""; + "" + ""; const char HTTP_FORM_DOMOTICZ_SWITCH[] PROGMEM = - ""; + ""; const char HTTP_FORM_DOMOTICZ_SENSOR[] PROGMEM = - ""; + ""; const char HTTP_FORM_DOMOTICZ_TIMER[] PROGMEM = - ""; + ""; void HandleDomoticzConfiguration(void) { - if (HttpUser()) { return; } - if (!WebAuthenticate()) { return WebServer->requestAuthentication(); } + if (!HttpCheckPriviledgedAccess()) { return; } + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_DOMOTICZ); if (WebServer->hasArg("save")) { @@ -460,35 +460,30 @@ void HandleDomoticzConfiguration(void) char stemp[32]; - String page = FPSTR(HTTP_HEAD); - page.replace(F("{v}"), FPSTR(S_CONFIGURE_DOMOTICZ)); - page += FPSTR(HTTP_HEAD_STYLE); - page += FPSTR(HTTP_FORM_DOMOTICZ); + WSContentStart_P(S_CONFIGURE_DOMOTICZ); + WSContentSendStyle(); + WSContentSend_P(HTTP_FORM_DOMOTICZ); for (int i = 0; i < MAX_DOMOTICZ_IDX; i++) { if (i < devices_present) { - page += FPSTR(HTTP_FORM_DOMOTICZ_RELAY); - page.replace("{2", String((int)Settings.domoticz_relay_idx[i])); - page.replace("{3", String((int)Settings.domoticz_key_idx[i])); + WSContentSend_P(HTTP_FORM_DOMOTICZ_RELAY, + i +1, i, i, Settings.domoticz_relay_idx[i], + i +1, i, i, Settings.domoticz_key_idx[i]); } if (pin[GPIO_SWT1 +i] < 99) { - page += FPSTR(HTTP_FORM_DOMOTICZ_SWITCH); - page.replace("{4", String((int)Settings.domoticz_switch_idx[i])); + WSContentSend_P(HTTP_FORM_DOMOTICZ_SWITCH, + i +1, i, i, Settings.domoticz_switch_idx[i]); } - page.replace("{1", String(i +1)); - if ((SONOFF_IFAN02 == Settings.module) && (1 == i)) { break; } + if ((SONOFF_IFAN02 == my_module_type) && (1 == i)) { break; } } for (int i = 0; i < DZ_MAX_SENSORS; i++) { - page += FPSTR(HTTP_FORM_DOMOTICZ_SENSOR); - page.replace("{1", String(i +1)); - page.replace("{2", GetTextIndexed(stemp, sizeof(stemp), i, kDomoticzSensors)); - page.replace("{5", String((int)Settings.domoticz_sensor_idx[i])); + WSContentSend_P(HTTP_FORM_DOMOTICZ_SENSOR, + i +1, GetTextIndexed(stemp, sizeof(stemp), i, kDomoticzSensors), i, i, Settings.domoticz_sensor_idx[i]); } - page += FPSTR(HTTP_FORM_DOMOTICZ_TIMER); - page.replace("{6", String((int)Settings.domoticz_update_timer)); - page += F("
" D_DOMOTICZ_IDX " {1
" D_DOMOTICZ_KEY_IDX " {1
" D_DOMOTICZ_IDX " %d
" D_DOMOTICZ_KEY_IDX " %d
" D_DOMOTICZ_SWITCH_IDX " {1
" D_DOMOTICZ_SWITCH_IDX " %d
" D_DOMOTICZ_SENSOR_IDX " {1 {2
" D_DOMOTICZ_SENSOR_IDX " %d %s
" D_DOMOTICZ_UPDATE_TIMER " (" STR(DOMOTICZ_UPDATE_TIMER) ")
" D_DOMOTICZ_UPDATE_TIMER " (" STR(DOMOTICZ_UPDATE_TIMER) ")
"); - page += FPSTR(HTTP_FORM_END); - page += FPSTR(HTTP_BTN_CONF); - ShowPage(page); + WSContentSend_P(HTTP_FORM_DOMOTICZ_TIMER, Settings.domoticz_update_timer); + WSContentSend_P(PSTR("")); + WSContentSend_P(HTTP_FORM_END); + WSContentSpaceButton(BUTTON_CONFIGURATION); + WSContentEnd(); } void DomoticzSaveSettings(void) @@ -497,20 +492,20 @@ void DomoticzSaveSettings(void) char ssensor_indices[6 * MAX_DOMOTICZ_SNS_IDX]; char tmp[100]; - for (byte i = 0; i < MAX_DOMOTICZ_IDX; i++) { - snprintf_P(stemp, sizeof(stemp), PSTR("r%d"), i +1); + for (uint8_t i = 0; i < MAX_DOMOTICZ_IDX; i++) { + snprintf_P(stemp, sizeof(stemp), PSTR("r%d"), i); WebGetArg(stemp, tmp, sizeof(tmp)); Settings.domoticz_relay_idx[i] = (!strlen(tmp)) ? 0 : atoi(tmp); - snprintf_P(stemp, sizeof(stemp), PSTR("k%d"), i +1); + snprintf_P(stemp, sizeof(stemp), PSTR("k%d"), i); WebGetArg(stemp, tmp, sizeof(tmp)); Settings.domoticz_key_idx[i] = (!strlen(tmp)) ? 0 : atoi(tmp); - snprintf_P(stemp, sizeof(stemp), PSTR("s%d"), i +1); + snprintf_P(stemp, sizeof(stemp), PSTR("s%d"), i); WebGetArg(stemp, tmp, sizeof(tmp)); Settings.domoticz_switch_idx[i] = (!strlen(tmp)) ? 0 : atoi(tmp); } ssensor_indices[0] = '\0'; - for (byte i = 0; i < DZ_MAX_SENSORS; i++) { - snprintf_P(stemp, sizeof(stemp), PSTR("l%d"), i +1); + for (uint8_t i = 0; i < DZ_MAX_SENSORS; i++) { + snprintf_P(stemp, sizeof(stemp), PSTR("l%d"), i); WebGetArg(stemp, tmp, sizeof(tmp)); Settings.domoticz_sensor_idx[i] = (!strlen(tmp)) ? 0 : atoi(tmp); snprintf_P(ssensor_indices, sizeof(ssensor_indices), PSTR("%s%s%d"), ssensor_indices, (strlen(ssensor_indices)) ? "," : "", Settings.domoticz_sensor_idx[i]); @@ -518,12 +513,11 @@ void DomoticzSaveSettings(void) WebGetArg("ut", tmp, sizeof(tmp)); Settings.domoticz_update_timer = (!strlen(tmp)) ? DOMOTICZ_UPDATE_TIMER : atoi(tmp); - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_DOMOTICZ D_CMND_IDX " %d,%d,%d,%d, " D_CMND_KEYIDX " %d,%d,%d,%d, " D_CMND_SWITCHIDX " %d,%d,%d,%d, " D_CMND_SENSORIDX " %s, " D_CMND_UPDATETIMER " %d"), + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_DOMOTICZ D_CMND_IDX " %d,%d,%d,%d, " D_CMND_KEYIDX " %d,%d,%d,%d, " D_CMND_SWITCHIDX " %d,%d,%d,%d, " D_CMND_SENSORIDX " %s, " D_CMND_UPDATETIMER " %d"), Settings.domoticz_relay_idx[0], Settings.domoticz_relay_idx[1], Settings.domoticz_relay_idx[2], Settings.domoticz_relay_idx[3], Settings.domoticz_key_idx[0], Settings.domoticz_key_idx[1], Settings.domoticz_key_idx[2], Settings.domoticz_key_idx[3], Settings.domoticz_switch_idx[0], Settings.domoticz_switch_idx[1], Settings.domoticz_switch_idx[2], Settings.domoticz_switch_idx[3], ssensor_indices, Settings.domoticz_update_timer); - AddLog(LOG_LEVEL_INFO); } #endif // USE_WEBSERVER @@ -531,15 +525,15 @@ void DomoticzSaveSettings(void) * Interface \*********************************************************************************************/ -boolean Xdrv07(byte function) +bool Xdrv07(uint8_t function) { - boolean result = false; + bool result = false; if (Settings.flag.mqtt_enabled) { switch (function) { #ifdef USE_WEBSERVER case FUNC_WEB_ADD_BUTTON: - strncat_P(mqtt_data, HTTP_BTN_MENU_DOMOTICZ, sizeof(mqtt_data) - strlen(mqtt_data) -1); + WSContentSend_P(HTTP_BTN_MENU_DOMOTICZ); break; case FUNC_WEB_ADD_HANDLER: WebServer->on("/" WEB_HANDLE_DOMOTICZ, HandleDomoticzConfiguration); diff --git a/sonoff/xdrv_08_serial_bridge.ino b/sonoff/xdrv_08_serial_bridge.ino index e901e9958..d39e7a6eb 100644 --- a/sonoff/xdrv_08_serial_bridge.ino +++ b/sonoff/xdrv_08_serial_bridge.ino @@ -1,7 +1,7 @@ /* xdrv_08_serial_bridge.ino - serial bridge support for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends and Dániel Zoltán Tolnai + Copyright (C) 2019 Theo Arends and Dániel Zoltán Tolnai This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -31,12 +31,13 @@ enum SerialBridgeCommands { CMND_SSERIALSEND, CMND_SBAUDRATE }; const char kSerialBridgeCommands[] PROGMEM = D_CMND_SSERIALSEND "|" D_CMND_SBAUDRATE; -TasmotaSerial *SerialBridgeSerial; +TasmotaSerial *SerialBridgeSerial = NULL; -uint8_t serial_bridge_active = 1; -uint8_t serial_bridge_in_byte_counter = 0; unsigned long serial_bridge_polling_window = 0; char *serial_bridge_buffer = NULL; +int serial_bridge_in_byte_counter = 0; +bool serial_bridge_active = true; +bool serial_bridge_raw = false; void SerialBridgeInput(void) { @@ -44,25 +45,37 @@ void SerialBridgeInput(void) yield(); uint8_t serial_in_byte = SerialBridgeSerial->read(); - if (serial_in_byte > 127) { // binary data... + if ((serial_in_byte > 127) && !serial_bridge_raw) { // Discard binary data above 127 if no raw reception allowed serial_bridge_in_byte_counter = 0; SerialBridgeSerial->flush(); return; } - if (serial_in_byte) { - if ((serial_in_byte_counter < SERIAL_BRIDGE_BUFFER_SIZE -1) && (serial_in_byte != Settings.serial_delimiter)) { // add char to string if it still fits + if (serial_in_byte || serial_bridge_raw) { // Any char between 1 and 127 or any char (0 - 255) + + if ((serial_bridge_in_byte_counter < SERIAL_BRIDGE_BUFFER_SIZE -1) && // Add char to string if it still fits and ... + ((isprint(serial_in_byte) && (128 == Settings.serial_delimiter)) || // Any char between 32 and 127 + (serial_in_byte != Settings.serial_delimiter) || // Any char between 1 and 127 and not being delimiter + serial_bridge_raw)) { // Any char between 0 and 255 serial_bridge_buffer[serial_bridge_in_byte_counter++] = serial_in_byte; - serial_bridge_polling_window = millis(); // Wait for more data + serial_bridge_polling_window = millis(); // Wait for more data } else { - serial_bridge_polling_window = 0; // Publish now + serial_bridge_polling_window = 0; // Publish now break; } } } if (serial_bridge_in_byte_counter && (millis() > (serial_bridge_polling_window + SERIAL_POLLING))) { - serial_bridge_buffer[serial_bridge_in_byte_counter] = 0; // serial data completed - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_SSERIALRECEIVED "\":\"%s\"}"), serial_bridge_buffer); + serial_bridge_buffer[serial_bridge_in_byte_counter] = 0; // Serial data completed + if (!serial_bridge_raw) { + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_SSERIALRECEIVED "\":\"%s\"}"), serial_bridge_buffer); + } else { + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_SSERIALRECEIVED "\":\"")); + for (int i = 0; i < serial_bridge_in_byte_counter; i++) { + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s%02x"), mqtt_data, serial_bridge_buffer[i]); + } + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s\"}"), mqtt_data); + } MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_SSERIALRECEIVED)); // XdrvRulesProcess(); serial_bridge_in_byte_counter = 0; @@ -73,15 +86,18 @@ void SerialBridgeInput(void) void SerialBridgeInit(void) { - serial_bridge_active = 0; + serial_bridge_active = false; if ((pin[GPIO_SBR_RX] < 99) && (pin[GPIO_SBR_TX] < 99)) { - serial_bridge_buffer = (char*)(malloc(SERIAL_BRIDGE_BUFFER_SIZE)); - if (serial_bridge_buffer != NULL) { - SerialBridgeSerial = new TasmotaSerial(pin[GPIO_SBR_RX], pin[GPIO_SBR_TX]); - if (SerialBridgeSerial->begin(Settings.sbaudrate * 1200)) { // Baud rate is stored div 1200 so it fits into one byte - serial_bridge_active = 1; - SerialBridgeSerial->flush(); + SerialBridgeSerial = new TasmotaSerial(pin[GPIO_SBR_RX], pin[GPIO_SBR_TX]); + if (SerialBridgeSerial->begin(Settings.sbaudrate * 1200)) { // Baud rate is stored div 1200 so it fits into one byte + if (SerialBridgeSerial->hardwareSerial()) { + ClaimSerial(); + serial_bridge_buffer = serial_in_buffer; // Use idle serial buffer to save RAM + } else { + serial_bridge_buffer = (char*)(malloc(SERIAL_BRIDGE_BUFFER_SIZE)); } + serial_bridge_active = true; + SerialBridgeSerial->flush(); } } } @@ -90,39 +106,56 @@ void SerialBridgeInit(void) * Commands \*********************************************************************************************/ -boolean SerialBridgeCommand(void) +bool SerialBridgeCommand(void) { char command [CMDSZ]; - boolean serviced = true; + bool serviced = true; int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic, kSerialBridgeCommands); if (-1 == command_code) { serviced = false; // Unknown command } - else if ((CMND_SSERIALSEND == command_code) && (XdrvMailbox.index > 0) && (XdrvMailbox.index <= 3)) { + else if ((CMND_SSERIALSEND == command_code) && (XdrvMailbox.index > 0) && (XdrvMailbox.index <= 5)) { + serial_bridge_raw = (XdrvMailbox.index > 3); if (XdrvMailbox.data_len > 0) { if (1 == XdrvMailbox.index) { - SerialBridgeSerial->write(XdrvMailbox.data, XdrvMailbox.data_len); - SerialBridgeSerial->write("\n"); + SerialBridgeSerial->write(XdrvMailbox.data, XdrvMailbox.data_len); // "Hello Tiger" + SerialBridgeSerial->write("\n"); // "\n" } - else if (2 == XdrvMailbox.index) { - SerialBridgeSerial->write(XdrvMailbox.data, XdrvMailbox.data_len); + else if ((2 == XdrvMailbox.index) || (4 == XdrvMailbox.index)) { + SerialBridgeSerial->write(XdrvMailbox.data, XdrvMailbox.data_len); // "Hello Tiger" or "A0" } - else if (3 == XdrvMailbox.index) { + else if (3 == XdrvMailbox.index) { // "Hello\f" SerialBridgeSerial->write(Unescape(XdrvMailbox.data, &XdrvMailbox.data_len), XdrvMailbox.data_len); } + else if (5 == XdrvMailbox.index) { + char *p; + char stemp[3]; + uint8_t code; + + char *codes = RemoveSpace(XdrvMailbox.data); + int size = strlen(XdrvMailbox.data); + + while (size > 0) { + strlcpy(stemp, codes, sizeof(stemp)); + code = strtol(stemp, &p, 16); + SerialBridgeSerial->write(code); // "AA004566" as hex values + size -= 2; + codes += 2; + } + } snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, D_JSON_DONE); } } else if (CMND_SBAUDRATE == command_code) { char *p; int baud = strtol(XdrvMailbox.data, &p, 10); - if (baud > 0) { + if (baud >= 1200) { baud /= 1200; // Make it a valid baudrate Settings.sbaudrate = (1 == XdrvMailbox.payload) ? SOFT_BAUDRATE / 1200 : baud; SerialBridgeSerial->begin(Settings.sbaudrate * 1200); // Reinitialize serial port with new baud rate } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_LVALUE, command, Settings.sbaudrate * 1200); + snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.sbaudrate * 1200); } else serviced = false; // Unknown command @@ -133,9 +166,9 @@ boolean SerialBridgeCommand(void) * Interface \*********************************************************************************************/ -boolean Xdrv08(byte function) +bool Xdrv08(uint8_t function) { - boolean result = false; + bool result = false; if (serial_bridge_active) { switch (function) { @@ -143,7 +176,7 @@ boolean Xdrv08(byte function) SerialBridgeInit(); break; case FUNC_LOOP: - SerialBridgeInput(); + if (SerialBridgeSerial) { SerialBridgeInput(); } break; case FUNC_COMMAND: result = SerialBridgeCommand(); diff --git a/sonoff/xdrv_09_timers.ino b/sonoff/xdrv_09_timers.ino index 8273201d5..9a85ad3c4 100644 --- a/sonoff/xdrv_09_timers.ino +++ b/sonoff/xdrv_09_timers.ino @@ -1,7 +1,7 @@ /* xdrv_09_timers.ino - timer support for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends + Copyright (C) 2019 Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -135,7 +135,6 @@ void DuskTillDawn(uint8_t *hour_up,uint8_t *minute_up, uint8_t *hour_down, uint8 // double Zeitzone = 2.0; //Sommerzeit double Zeitzone = ((double)time_timezone) / 60; double Zeitgleichung = BerechneZeitgleichung(&DK, T); - double Minuten = Zeitgleichung * 60.0; double Zeitdifferenz = 12.0*acos((sin(h) - sin(B)*sin(DK)) / (cos(B)*cos(DK)))/pi; double AufgangOrtszeit = 12.0 - Zeitdifferenz - Zeitgleichung; double UntergangOrtszeit = 12.0 + Zeitdifferenz - Zeitgleichung; @@ -220,7 +219,7 @@ void ApplyTimerOffsets(Timer *duskdawn) duskdawn->time = timeBuffer; } -String GetSun(byte dawn) +String GetSun(uint8_t dawn) { char stime[6]; @@ -233,7 +232,7 @@ String GetSun(byte dawn) return String(stime); } -uint16_t GetSunMinutes(byte dawn) +uint16_t SunMinutes(uint8_t dawn) { uint8_t hour[2]; uint8_t minute[2]; @@ -247,7 +246,7 @@ uint16_t GetSunMinutes(byte dawn) /*******************************************************************************************/ -void TimerSetRandomWindow(byte index) +void TimerSetRandomWindow(uint8_t index) { timer_window[index] = 0; if (Settings.timer[index].window) { @@ -257,7 +256,7 @@ void TimerSetRandomWindow(byte index) void TimerSetRandomWindows(void) { - for (byte i = 0; i < MAX_TIMERS; i++) { TimerSetRandomWindow(i); } + for (uint8_t i = 0; i < MAX_TIMERS; i++) { TimerSetRandomWindow(i); } } void TimerEverySecond(void) @@ -269,7 +268,7 @@ void TimerEverySecond(void) int16_t time = (RtcTime.hour *60) + RtcTime.minute; uint8_t days = 1 << (RtcTime.day_of_week -1); - for (byte i = 0; i < MAX_TIMERS; i++) { + for (uint8_t i = 0; i < MAX_TIMERS; i++) { // if (Settings.timer[i].device >= devices_present) Settings.timer[i].data = 0; // Reset timer due to change in devices present Timer xtimer = Settings.timer[i]; uint16_t set_time = xtimer.time; @@ -309,7 +308,7 @@ void PrepShowTimer(uint8_t index) Timer xtimer = Settings.timer[index -1]; - for (byte i = 0; i < 7; i++) { + for (uint8_t i = 0; i < 7; i++) { uint8_t mask = 1 << i; snprintf(days, sizeof(days), "%s%d", days, ((xtimer.days & mask) > 0)); } @@ -338,11 +337,11 @@ void PrepShowTimer(uint8_t index) * Commands \*********************************************************************************************/ -boolean TimerCommand(void) +bool TimerCommand(void) { char command[CMDSZ]; char dataBufUc[XdrvMailbox.data_len]; - boolean serviced = true; + bool serviced = true; uint8_t index = XdrvMailbox.index; UpperCase(dataBufUc, XdrvMailbox.data); @@ -386,7 +385,7 @@ boolean TimerCommand(void) uint8_t sign = 0; char time_str[10]; - snprintf(time_str, sizeof(time_str), root[parm_uc]); + strlcpy(time_str, root[parm_uc], sizeof(time_str)); const char *substr = strtok(time_str, ":"); if (substr != NULL) { if (strchr(substr, '-')) { @@ -465,9 +464,9 @@ boolean TimerCommand(void) snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, GetStateText(Settings.flag3.timers_enable)); MqttPublishPrefixTopic_P(RESULT_OR_STAT, command); - byte jsflg = 0; - byte lines = 1; - for (byte i = 0; i < MAX_TIMERS; i++) { + uint8_t jsflg = 0; + uint8_t lines = 1; + for (uint8_t i = 0; i < MAX_TIMERS; i++) { if (!jsflg) { snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_TIMERS "%d\":{"), lines++); } else { @@ -518,9 +517,9 @@ boolean TimerCommand(void) const char S_CONFIGURE_TIMER[] PROGMEM = D_CONFIGURE_TIMER; const char HTTP_BTN_MENU_TIMER[] PROGMEM = - "
"; + "

"; -const char HTTP_TIMER_SCRIPT[] PROGMEM = +const char HTTP_TIMER_SCRIPT1[] PROGMEM = "var pt=[],ct=99;" "function qs(s){" // Alias to save code space "return document.querySelector(s);" @@ -529,8 +528,9 @@ const char HTTP_TIMER_SCRIPT[] PROGMEM = "var o=document.createElement('option');" "o.textContent=i;" "q.appendChild(o);" - "}" + "}"; #ifdef USE_SUNRISE +const char HTTP_TIMER_SCRIPT2[] PROGMEM = "function gt(){" // Set hours and minutes according to mode "var m,p,q;" "m=qs('input[name=\"rd\"]:checked').value;" // Get mode @@ -538,7 +538,7 @@ const char HTTP_TIMER_SCRIPT[] PROGMEM = "if(m==0){" // Time is set "so(0);" // Hide offset span and allow Hour 00..23 "q=Math.floor(p/60);if(q<10){q='0'+q;}qs('#ho').value=q;" // Set hours - "q=p%60;if(q<10){q='0'+q;}qs('#mi').value=q;" // Set minutes + "q=p%%60;if(q<10){q='0'+q;}qs('#mi').value=q;" // Set minutes "}" "if((m==1)||(m==2)){" // Sunrise or sunset is set "so(1);" // Show offset span and allow Hour 00..11 @@ -546,7 +546,7 @@ const char HTTP_TIMER_SCRIPT[] PROGMEM = "if(q>=12){q-=12;qs('#dr').selectedIndex=1;}" // Negative offset "else{qs('#dr').selectedIndex=0;}" "if(q<10){q='0'+q;}qs('#ho').value=q;" // Set offset hours - "q=p%60;if(q<10){q='0'+q;}qs('#mi').value=q;" // Set offset minutes + "q=p%%60;if(q<10){q='0'+q;}qs('#mi').value=q;" // Set offset minutes "}" "}" "function so(b){" // Hide or show offset items @@ -559,8 +559,9 @@ const char HTTP_TIMER_SCRIPT[] PROGMEM = "qs('#dr').disabled='disabled';" "if(e<23){for(i=12;i<=23;i++){ce(i,o);}}" // Create hours select options "}" - "}" + "}"; #endif +const char HTTP_TIMER_SCRIPT3[] PROGMEM = "function st(){" // Save parameters to hidden area "var i,l,m,n,p,s;" "m=0;s=0;" @@ -571,7 +572,7 @@ const char HTTP_TIMER_SCRIPT[] PROGMEM = "m=qs('input[name=\"rd\"]:checked').value;" // Check mode "s|=(qs('input[name=\"rd\"]:checked').value<<29);" // Get mode #endif - "if(}1>0){" + "if(%d>0){" "i=qs('#d1').selectedIndex;if(i>=0){s|=(i<<23);}" // Get output "s|=(qs('#p1').selectedIndex<<27);" // Get action "}else{" @@ -588,14 +589,15 @@ const char HTTP_TIMER_SCRIPT[] PROGMEM = "s|=((qs('#mw').selectedIndex)&0x0F)<<11;" // Get window minutes "pt[ct]=s;" "eb('t0').value=pt.join();" // Save parameters from array to hidden area - "}" + "}"; +const char HTTP_TIMER_SCRIPT4[] PROGMEM = "function ot(t,e){" // Select tab and update elements "var i,n,o,p,q,s;" "if(ct<99){st();}" // Save changes "ct=t;" "o=document.getElementsByClassName('tl');" // Restore style to all tabs/buttons - "for(i=0;i>29)&3;eb('b'+p).checked=1;" // Set mode @@ -603,23 +605,24 @@ const char HTTP_TIMER_SCRIPT[] PROGMEM = #else "p=s&0x7FF;" // Get time "q=Math.floor(p/60);if(q<10){q='0'+q;}qs('#ho').value=q;" // Set hours - "q=p%60;if(q<10){q='0'+q;}qs('#mi').value=q;" // Set minutes + "q=p%%60;if(q<10){q='0'+q;}qs('#mi').value=q;" // Set minutes #endif "q=(s>>11)&0xF;if(q<10){q='0'+q;}qs('#mw').value=q;" // Set window minutes "for(i=0;i<7;i++){p=(s>>(16+i))&1;eb('w'+i).checked=p;}" // Set weekdays - "if(}1>0){" + "if(%d>0){" "p=(s>>23)&0xF;qs('#d1').value=p+1;" // Set output "p=(s>>27)&3;qs('#p1').selectedIndex=p;" // Set action "}" "p=(s>>15)&1;eb('r0').checked=p;" // Set repeat "p=(s>>31)&1;eb('a0').checked=p;" // Set arm - "}" + "}"; +const char HTTP_TIMER_SCRIPT5[] PROGMEM = "function it(){" // Initialize elements and select first tab "var b,i,o,s;" "pt=eb('t0').value.split(',').map(Number);" // Get parameters from hidden area to array "s='';for(i=0;i<" STR(MAX_TIMERS) ";i++){b='';if(0==i){b=\" id='dP'\";}s+=\"\"}" "eb('bt').innerHTML=s;" // Create tabs - "if(}1>0){" // Create Output and Action drop down boxes + "if(%d>0){" // Create Output and Action drop down boxes "eb('oa').innerHTML=\"" D_TIMER_OUTPUT " " D_TIMER_ACTION " \";" "o=qs('#p1');ce('" D_OFF "',o);ce('" D_ON "',o);ce('" D_TOGGLE "',o);" // Create offset direction select options #ifdef USE_RULES @@ -629,50 +632,52 @@ const char HTTP_TIMER_SCRIPT[] PROGMEM = #endif "}else{" "eb('oa').innerHTML=\"" D_TIMER_ACTION " " D_RULE "\";" // No outputs but rule is allowed - "}" + "}"; +const char HTTP_TIMER_SCRIPT6[] PROGMEM = #ifdef USE_SUNRISE "o=qs('#dr');ce('+',o);ce('-',o);" // Create offset direction select options #endif "o=qs('#ho');for(i=0;i<=23;i++){ce((i<10)?('0'+i):i,o);}" // Create hours select options "o=qs('#mi');for(i=0;i<=59;i++){ce((i<10)?('0'+i):i,o);}" // Create minutes select options "o=qs('#mw');for(i=0;i<=15;i++){ce((i<10)?('0'+i):i,o);}" // Create window minutes select options - "o=qs('#d1');for(i=0;i<}1;i++){ce(i+1,o);}" // Create outputs + "o=qs('#d1');for(i=0;i<%d;i++){ce(i+1,o);}" // Create outputs "var a='" D_DAY3LIST "';" - "s='';for(i=0;i<7;i++){s+=\"\"+a.substring(i*3,(i*3)+3)+\"\"}" + "s='';for(i=0;i<7;i++){s+=\"\"+a.substring(i*3,(i*3)+3)+\" \"}" "eb('ds').innerHTML=s;" // Create weekdays "eb('dP').click();" // Get the element with id='dP' and click on it - "}"; + "}" + "window.onload=it;"; const char HTTP_TIMER_STYLE[] PROGMEM = - ".tl{float:left;border-radius:0;border:1px solid #fff;padding:1px;width:6.25%;}" -#ifdef USE_SUNRISE - "input[type='radio']{width:13px;height:24px;margin-top:-1px;margin-right:8px;vertical-align:middle;}" -#endif - ""; -const char HTTP_FORM_TIMER[] PROGMEM = + ".tl{float:left;border-radius:0;border:1px solid #f2f2f2;padding:1px;width:6.25%%;}"; // Border color needs to be the same as Fieldset background color from HTTP_HEAD_STYLE1 (transparent won't work) +const char HTTP_FORM_TIMER1[] PROGMEM = "
" " " D_TIMER_PARAMETERS " " "
" - "
" D_TIMER_ENABLE "


" + "
" D_TIMER_ENABLE "


" "



" "

" "
" - "" D_TIMER_ARM " " - "" D_TIMER_REPEAT "" + "" D_TIMER_ARM " " + "" D_TIMER_REPEAT "" "

" - "
" + "
"; #ifdef USE_SUNRISE - "
" +const char HTTP_FORM_TIMER3[] PROGMEM = + "
" "" D_TIMER_TIME "
" - "" D_SUNRISE " (}8)
" - "" D_SUNSET " (}9)
" + "" D_SUNRISE " (%s)
" + "" D_SUNSET " (%s)
" "
" + "

" "" - " " + " "; #else - "" D_TIMER_TIME " " +const char HTTP_FORM_TIMER3[] PROGMEM = + "" D_TIMER_TIME " "; #endif // USE_SUNRISE +const char HTTP_FORM_TIMER4[] PROGMEM = "" " " D_HOUR_MINUTE_SEPARATOR " " "" @@ -683,8 +688,8 @@ const char HTTP_FORM_TIMER1[] PROGMEM = void HandleTimerConfiguration(void) { - if (HttpUser()) { return; } - if (!WebAuthenticate()) { return WebServer->requestAuthentication(); } + if (!HttpCheckPriviledgedAccess()) { return; } + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_TIMER); if (WebServer->hasArg("save")) { @@ -693,28 +698,30 @@ void HandleTimerConfiguration(void) return; } - String page = FPSTR(HTTP_HEAD); - page.replace(F("{v}"), FPSTR(S_CONFIGURE_TIMER)); - page += FPSTR(HTTP_TIMER_SCRIPT); - page += FPSTR(HTTP_HEAD_STYLE); - page.replace(F(""), FPSTR(HTTP_TIMER_STYLE)); - page += FPSTR(HTTP_FORM_TIMER); - page.replace(F("{e0"), (Settings.flag3.timers_enable) ? F(" checked") : F("")); - for (byte i = 0; i < MAX_TIMERS; i++) { - if (i > 0) { page += F(","); } - page += String(Settings.timer[i].data); - } - page += FPSTR(HTTP_FORM_TIMER1); - page.replace(F("}1"), String(devices_present)); + WSContentStart_P(S_CONFIGURE_TIMER); + WSContentSend_P(HTTP_TIMER_SCRIPT1); #ifdef USE_SUNRISE - page.replace(F("}8"), GetSun(0)); // Add Sunrise - page.replace(F("}9"), GetSun(1)); // Add Sunset - page.replace(F("299"), String(100 + (strlen(D_SUNSET) *12))); // Fix string length to keep radios centered + WSContentSend_P(HTTP_TIMER_SCRIPT2); #endif // USE_SUNRISE - page += FPSTR(HTTP_FORM_END); - page += F(""); // Init elements and select first tab/button - page += FPSTR(HTTP_BTN_CONF); - ShowPage(page); + WSContentSend_P(HTTP_TIMER_SCRIPT3, devices_present); + WSContentSend_P(HTTP_TIMER_SCRIPT4, devices_present); + WSContentSend_P(HTTP_TIMER_SCRIPT5, devices_present); + WSContentSend_P(HTTP_TIMER_SCRIPT6, devices_present); + WSContentSendStyle_P(HTTP_TIMER_STYLE); + WSContentSend_P(HTTP_FORM_TIMER1, (Settings.flag3.timers_enable) ? " checked" : ""); + for (uint8_t i = 0; i < MAX_TIMERS; i++) { + WSContentSend_P(PSTR("%s%u"), (i > 0) ? "," : "", Settings.timer[i].data); + } + WSContentSend_P(HTTP_FORM_TIMER2); +#ifdef USE_SUNRISE + WSContentSend_P(HTTP_FORM_TIMER3, 100 + (strlen(D_SUNSET) *12), GetSun(0).c_str(), GetSun(1).c_str()); +#else + WSContentSend_P(HTTP_FORM_TIMER3); +#endif // USE_SUNRISE + WSContentSend_P(HTTP_FORM_TIMER4); + WSContentSend_P(HTTP_FORM_END); + WSContentSpaceButton(BUTTON_CONFIGURATION); + WSContentEnd(); } void TimerSaveSettings(void) @@ -726,7 +733,7 @@ void TimerSaveSettings(void) WebGetArg("t0", tmp, sizeof(tmp)); char *p = tmp; snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_MQTT D_CMND_TIMERS " %d"), Settings.flag3.timers_enable); - for (byte i = 0; i < MAX_TIMERS; i++) { + for (uint8_t i = 0; i < MAX_TIMERS; i++) { timer.data = strtol(p, &p, 10); p++; // Skip comma if (timer.time < 1440) { @@ -745,9 +752,9 @@ void TimerSaveSettings(void) * Interface \*********************************************************************************************/ -boolean Xdrv09(byte function) +bool Xdrv09(uint8_t function) { - boolean result = false; + bool result = false; switch (function) { case FUNC_PRE_INIT: @@ -757,9 +764,9 @@ boolean Xdrv09(byte function) #ifdef USE_TIMERS_WEB case FUNC_WEB_ADD_BUTTON: #ifdef USE_RULES - strncat_P(mqtt_data, HTTP_BTN_MENU_TIMER, sizeof(mqtt_data) - strlen(mqtt_data) -1); + WSContentSend_P(HTTP_BTN_MENU_TIMER); #else - if (devices_present) { strncat_P(mqtt_data, HTTP_BTN_MENU_TIMER, sizeof(mqtt_data) - strlen(mqtt_data) -1); } + if (devices_present) { WSContentSend_P(HTTP_BTN_MENU_TIMER); } #endif // USE_RULES break; case FUNC_WEB_ADD_HANDLER: diff --git a/sonoff/xdrv_10_rules.ino b/sonoff/xdrv_10_rules.ino index c9d880128..4d13bf6b9 100644 --- a/sonoff/xdrv_10_rules.ino +++ b/sonoff/xdrv_10_rules.ino @@ -1,7 +1,7 @@ /* xdrv_10_rules.ino - rule support for Sonoff-Tasmota - Copyright (C) 2018 ESP Easy Group and Theo Arends + Copyright (C) 2019 ESP Easy Group and Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -75,11 +75,50 @@ #define D_CMND_MULT "Mult" #define D_CMND_SCALE "Scale" #define D_CMND_CALC_RESOLUTION "CalcRes" +#define D_CMND_SUBSCRIBE "Subscribe" +#define D_CMND_UNSUBSCRIBE "Unsubscribe" #define D_JSON_INITIATED "Initiated" -enum RulesCommands { CMND_RULE, CMND_RULETIMER, CMND_EVENT, CMND_VAR, CMND_MEM, CMND_ADD, CMND_SUB, CMND_MULT, CMND_SCALE, CMND_CALC_RESOLUTION }; -const char kRulesCommands[] PROGMEM = D_CMND_RULE "|" D_CMND_RULETIMER "|" D_CMND_EVENT "|" D_CMND_VAR "|" D_CMND_MEM "|" D_CMND_ADD "|" D_CMND_SUB "|" D_CMND_MULT "|" D_CMND_SCALE "|" D_CMND_CALC_RESOLUTION ; +#define COMPARE_OPERATOR_NONE -1 +#define COMPARE_OPERATOR_EQUAL 0 +#define COMPARE_OPERATOR_BIGGER 1 +#define COMPARE_OPERATOR_SMALLER 2 +#define COMPARE_OPERATOR_EXACT_DIVISION 3 +#define COMPARE_OPERATOR_NUMBER_EQUAL 4 +#define COMPARE_OPERATOR_NOT_EQUAL 5 +#define COMPARE_OPERATOR_BIGGER_EQUAL 6 +#define COMPARE_OPERATOR_SMALLER_EQUAL 7 +#define MAXIMUM_COMPARE_OPERATOR COMPARE_OPERATOR_SMALLER_EQUAL +const char kCompareOperators[] PROGMEM = "=\0>\0<\0|\0==!=>=<="; + +#ifdef USE_EXPRESSION + #include // Import LinkedList library + + const char kExpressionOperators[] PROGMEM = "+-*/%^"; + #define EXPRESSION_OPERATOR_ADD 0 + #define EXPRESSION_OPERATOR_SUBTRACT 1 + #define EXPRESSION_OPERATOR_MULTIPLY 2 + #define EXPRESSION_OPERATOR_DIVIDEDBY 3 + #define EXPRESSION_OPERATOR_MODULO 4 + #define EXPRESSION_OPERATOR_POWER 5 + + const uint8_t kExpressionOperatorsPriorities[] PROGMEM = {1, 1, 2, 2, 3, 4}; + #define MAX_EXPRESSION_OPERATOR_PRIORITY 4 +#endif // USE_EXPRESSION + +enum RulesCommands { CMND_RULE, CMND_RULETIMER, CMND_EVENT, CMND_VAR, CMND_MEM, CMND_ADD, CMND_SUB, CMND_MULT, CMND_SCALE, CMND_CALC_RESOLUTION, CMND_SUBSCRIBE, CMND_UNSUBSCRIBE }; +const char kRulesCommands[] PROGMEM = D_CMND_RULE "|" D_CMND_RULETIMER "|" D_CMND_EVENT "|" D_CMND_VAR "|" D_CMND_MEM "|" D_CMND_ADD "|" D_CMND_SUB "|" D_CMND_MULT "|" D_CMND_SCALE "|" D_CMND_CALC_RESOLUTION "|" D_CMND_SUBSCRIBE "|" D_CMND_UNSUBSCRIBE ; + +#ifdef SUPPORT_MQTT_EVENT + #include // Import LinkedList library + typedef struct { + String Event; + String Topic; + String Key; + } MQTT_Subscription; + LinkedList subscriptions; +#endif //SUPPORT_MQTT_EVENT String rules_event_value; unsigned long rules_timer[MAX_RULE_TIMERS] = { 0 }; @@ -95,10 +134,18 @@ uint8_t rules_teleperiod = 0; char event_data[100]; char vars[MAX_RULE_VARS][33] = { 0 }; +#if (MAX_RULE_VARS>16) +#error MAX_RULE_VARS is bigger than 16 +#endif +#if (MAX_RULE_MEMS>5) +#error MAX_RULE_MEMS is bigger than 5 +#endif +uint16_t vars_event = 0; +uint8_t mems_event = 0; /*******************************************************************************************/ -bool RulesRuleMatch(byte rule_set, String &event, String &rule) +bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule) { // event = {"INA219":{"Voltage":4.494,"Current":0.020,"Power":0.089}} // event = {"System":{"Boot":1}} @@ -120,39 +167,28 @@ bool RulesRuleMatch(byte rule_set, String &event, String &rule) String rule_name = rule.substring(pos +1); // "CURRENT>0.100" or "BOOT" or "%var1%" or "MINUTE|5" - char compare = ' '; - pos = rule_name.indexOf(">"); - if (pos > 0) { - compare = '>'; - } else { - pos = rule_name.indexOf("<"); - if (pos > 0) { - compare = '<'; - } else { - pos = rule_name.indexOf("="); - if (pos > 0) { - compare = '='; - } else { - pos = rule_name.indexOf("|"); // Modulo, cannot use % easily as it is used for variable detection - if (pos > 0) { - compare = '%'; - } - } + char compare_operator[3]; + int8_t compare = COMPARE_OPERATOR_NONE; + for (int8_t i = MAXIMUM_COMPARE_OPERATOR; i >= 0; i--) { + snprintf_P(compare_operator, sizeof(compare_operator), kCompareOperators + (i *2)); + if ((pos = rule_name.indexOf(compare_operator)) > 0) { + compare = i; + break; } } char rule_svalue[CMDSZ] = { 0 }; double rule_value = 0; - if (pos > 0) { - String rule_param = rule_name.substring(pos + 1); - for (byte i = 0; i < MAX_RULE_VARS; i++) { + if (compare != COMPARE_OPERATOR_NONE) { + String rule_param = rule_name.substring(pos + strlen(compare_operator)); + for (uint8_t i = 0; i < MAX_RULE_VARS; i++) { snprintf_P(stemp, sizeof(stemp), PSTR("%%VAR%d%%"), i +1); if (rule_param.startsWith(stemp)) { rule_param = vars[i]; break; } } - for (byte i = 0; i < MAX_RULE_MEMS; i++) { + for (uint8_t i = 0; i < MAX_RULE_MEMS; i++) { snprintf_P(stemp, sizeof(stemp), PSTR("%%MEM%d%%"), i +1); if (rule_param.startsWith(stemp)) { rule_param = Settings.mems[i]; @@ -161,24 +197,28 @@ bool RulesRuleMatch(byte rule_set, String &event, String &rule) } snprintf_P(stemp, sizeof(stemp), PSTR("%%TIME%%")); if (rule_param.startsWith(stemp)) { - rule_param = String(GetMinutesPastMidnight()); + rule_param = String(MinutesPastMidnight()); } snprintf_P(stemp, sizeof(stemp), PSTR("%%UPTIME%%")); if (rule_param.startsWith(stemp)) { - rule_param = String(GetMinutesUptime()); + rule_param = String(MinutesUptime()); + } + snprintf_P(stemp, sizeof(stemp), PSTR("%%TIMESTAMP%%")); + if (rule_param.startsWith(stemp)) { + rule_param = GetDateAndTime(DT_LOCAL).c_str(); } #if defined(USE_TIMERS) && defined(USE_SUNRISE) snprintf_P(stemp, sizeof(stemp), PSTR("%%SUNRISE%%")); if (rule_param.startsWith(stemp)) { - rule_param = String(GetSunMinutes(0)); + rule_param = String(SunMinutes(0)); } snprintf_P(stemp, sizeof(stemp), PSTR("%%SUNSET%%")); if (rule_param.startsWith(stemp)) { - rule_param = String(GetSunMinutes(1)); + rule_param = String(SunMinutes(1)); } #endif // USE_TIMERS and USE_SUNRISE rule_param.toUpperCase(); - snprintf(rule_svalue, sizeof(rule_svalue), rule_param.c_str()); + strlcpy(rule_svalue, rule_param.c_str(), sizeof(rule_svalue)); int temp_value = GetStateNumber(rule_svalue); if (temp_value > -1) { @@ -197,9 +237,8 @@ bool RulesRuleMatch(byte rule_set, String &event, String &rule) double value = 0; const char* str_value = root[rule_task][rule_name]; -//snprintf_P(log_data, sizeof(log_data), PSTR("RUL: Task %s, Name %s, Value |%s|, TrigCnt %d, TrigSt %d, Source %s, Json %s"), +//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RUL: Task %s, Name %s, Value |%s|, TrigCnt %d, TrigSt %d, Source %s, Json %s"), // rule_task.c_str(), rule_name.c_str(), rule_svalue, rules_trigger_count[rule_set], bitRead(rules_triggers[rule_set], rules_trigger_count[rule_set]), event.c_str(), (str_value) ? str_value : "none"); -//AddLog(LOG_LEVEL_DEBUG); if (!root[rule_task][rule_name].success()) { return false; } // No value but rule_name is ok @@ -212,24 +251,32 @@ bool RulesRuleMatch(byte rule_set, String &event, String &rule) int int_value = int(value); int int_rule_value = int(rule_value); switch (compare) { - case '%': - if ((int_value > 0) && (int_rule_value > 0)) { - if ((int_value % int_rule_value) == 0) { match = true; } - } + case COMPARE_OPERATOR_EXACT_DIVISION: + match = (int_rule_value && (int_value % int_rule_value) == 0); break; - case '>': - if (value > rule_value) { match = true; } + case COMPARE_OPERATOR_EQUAL: + match = (!strcasecmp(str_value, rule_svalue)); // Compare strings - this also works for hexadecimals break; - case '<': - if (value < rule_value) { match = true; } + case COMPARE_OPERATOR_BIGGER: + match = (value > rule_value); break; - case '=': -// if (value == rule_value) { match = true; } // Compare values - only decimals or partly hexadecimals - if (!strcasecmp(str_value, rule_svalue)) { match = true; } // Compare strings - this also works for hexadecimals + case COMPARE_OPERATOR_SMALLER: + match = (value < rule_value); break; - case ' ': - match = true; // Json value but not needed + case COMPARE_OPERATOR_NUMBER_EQUAL: + match = (value == rule_value); break; + case COMPARE_OPERATOR_NOT_EQUAL: + match = (value != rule_value); + break; + case COMPARE_OPERATOR_BIGGER_EQUAL: + match = (value >= rule_value); + break; + case COMPARE_OPERATOR_SMALLER_EQUAL: + match = (value <= rule_value); + break; + default: + match = true; } } else match = true; @@ -250,15 +297,14 @@ bool RulesRuleMatch(byte rule_set, String &event, String &rule) /*******************************************************************************************/ -bool RuleSetProcess(byte rule_set, String &event_saved) +bool RuleSetProcess(uint8_t rule_set, String &event_saved) { bool serviced = false; char stemp[10]; delay(0); // Prohibit possible loop software watchdog -//snprintf_P(log_data, sizeof(log_data), PSTR("RUL: Event = %s, Rule = %s"), event_saved.c_str(), Settings.rules[rule_set]); -//AddLog(LOG_LEVEL_DEBUG); +//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RUL: Event = %s, Rule = %s"), event_saved.c_str(), Settings.rules[rule_set]); String rules = Settings.rules[rule_set]; @@ -293,8 +339,7 @@ bool RuleSetProcess(byte rule_set, String &event_saved) rules_event_value = ""; String event = event_saved; -//snprintf_P(log_data, sizeof(log_data), PSTR("RUL: Event |%s|, Rule |%s|, Command(s) |%s|"), event.c_str(), event_trigger.c_str(), commands.c_str()); -//AddLog(LOG_LEVEL_DEBUG); +//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RUL: Event |%s|, Rule |%s|, Command(s) |%s|"), event.c_str(), event_trigger.c_str(), commands.c_str()); if (RulesRuleMatch(rule_set, event, event_trigger)) { commands.trim(); @@ -303,26 +348,26 @@ bool RuleSetProcess(byte rule_set, String &event_saved) // if (!ucommand.startsWith("BACKLOG")) { commands = "backlog " + commands; } // Always use Backlog to prevent power race exception if (ucommand.indexOf("EVENT ") != -1) { commands = "backlog " + commands; } // Always use Backlog with event to prevent rule event loop exception commands.replace(F("%value%"), rules_event_value); - for (byte i = 0; i < MAX_RULE_VARS; i++) { + for (uint8_t i = 0; i < MAX_RULE_VARS; i++) { snprintf_P(stemp, sizeof(stemp), PSTR("%%var%d%%"), i +1); commands.replace(stemp, vars[i]); } - for (byte i = 0; i < MAX_RULE_MEMS; i++) { + for (uint8_t i = 0; i < MAX_RULE_MEMS; i++) { snprintf_P(stemp, sizeof(stemp), PSTR("%%mem%d%%"), i +1); commands.replace(stemp, Settings.mems[i]); } - commands.replace(F("%time%"), String(GetMinutesPastMidnight())); - commands.replace(F("%uptime%"), String(GetMinutesUptime())); + commands.replace(F("%time%"), String(MinutesPastMidnight())); + commands.replace(F("%uptime%"), String(MinutesUptime())); + commands.replace(F("%timestamp%"), GetDateAndTime(DT_LOCAL).c_str()); #if defined(USE_TIMERS) && defined(USE_SUNRISE) - commands.replace(F("%sunrise%"), String(GetSunMinutes(0))); - commands.replace(F("%sunset%"), String(GetSunMinutes(1))); + commands.replace(F("%sunrise%"), String(SunMinutes(0))); + commands.replace(F("%sunset%"), String(SunMinutes(1))); #endif // USE_TIMERS and USE_SUNRISE char command[commands.length() +1]; - snprintf(command, sizeof(command), commands.c_str()); + strlcpy(command, commands.c_str(), sizeof(command)); - snprintf_P(log_data, sizeof(log_data), PSTR("RUL: %s performs \"%s\""), event_trigger.c_str(), command); - AddLog(LOG_LEVEL_INFO); + AddLog_P2(LOG_LEVEL_INFO, PSTR("RUL: %s performs \"%s\""), event_trigger.c_str(), command); // snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, D_CMND_RULE, D_JSON_INITIATED); // MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_RULE)); @@ -342,15 +387,16 @@ bool RulesProcessEvent(char *json_event) { bool serviced = false; +#ifdef USE_DEBUG_DRIVER ShowFreeMem(PSTR("RulesProcessEvent")); +#endif String event_saved = json_event; event_saved.toUpperCase(); -//snprintf_P(log_data, sizeof(log_data), PSTR("RUL: Event %s"), event_saved.c_str()); -//AddLog(LOG_LEVEL_DEBUG); +//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RUL: Event %s"), event_saved.c_str()); - for (byte i = 0; i < MAX_RULE_SETS; i++) { + for (uint8_t i = 0; i < MAX_RULE_SETS; i++) { if (strlen(Settings.rules[i]) && bitRead(Settings.rule_enabled, i)) { if (RuleSetProcess(i, event_saved)) { serviced = true; } } @@ -366,7 +412,7 @@ bool RulesProcess(void) void RulesInit(void) { rules_flag.data = 0; - for (byte i = 0; i < MAX_RULE_SETS; i++) { + for (uint8_t i = 0; i < MAX_RULE_SETS; i++) { if (Settings.rules[i][0] == '\0') { bitWrite(Settings.rule_enabled, i, 0); bitWrite(Settings.rule_once, i, 0); @@ -383,7 +429,7 @@ void RulesEvery50ms(void) if (-1 == rules_new_power) { rules_new_power = power; } if (rules_new_power != rules_old_power) { if (rules_old_power != -1) { - for (byte i = 0; i < devices_present; i++) { + for (uint8_t i = 0; i < devices_present; i++) { uint8_t new_state = (rules_new_power >> i) &1; if (new_state != ((rules_old_power >> i) &1)) { snprintf_P(json_event, sizeof(json_event), PSTR("{\"Power%d\":{\"State\":%d}}"), i +1, new_state); @@ -392,20 +438,20 @@ void RulesEvery50ms(void) } } else { // Boot time POWER OUTPUTS (Relays) Status - for (byte i = 0; i < devices_present; i++) { + for (uint8_t i = 0; i < devices_present; i++) { uint8_t new_state = (rules_new_power >> i) &1; snprintf_P(json_event, sizeof(json_event), PSTR("{\"Power%d\":{\"Boot\":%d}}"), i +1, new_state); RulesProcessEvent(json_event); } // Boot time SWITCHES Status - for (byte i = 0; i < MAX_SWITCHES; i++) { + for (uint8_t i = 0; i < MAX_SWITCHES; i++) { #ifdef USE_TM1638 if ((pin[GPIO_SWT1 +i] < 99) || ((pin[GPIO_TM16CLK] < 99) && (pin[GPIO_TM16DIO] < 99) && (pin[GPIO_TM16STB] < 99))) { #else if (pin[GPIO_SWT1 +i] < 99) { #endif // USE_TM1638 - boolean swm = ((FOLLOW_INV == Settings.switchmode[i]) || (PUSHBUTTON_INV == Settings.switchmode[i]) || (PUSHBUTTONHOLD_INV == Settings.switchmode[i])); - snprintf_P(json_event, sizeof(json_event), PSTR("{\"" D_JSON_SWITCH "%d\":{\"Boot\":%d}}"), i +1, (swm ^ lastwallswitch[i])); + bool swm = ((FOLLOW_INV == Settings.switchmode[i]) || (PUSHBUTTON_INV == Settings.switchmode[i]) || (PUSHBUTTONHOLD_INV == Settings.switchmode[i])); + snprintf_P(json_event, sizeof(json_event), PSTR("{\"" D_JSON_SWITCH "%d\":{\"Boot\":%d}}"), i +1, (swm ^ SwitchLastState(i))); RulesProcessEvent(json_event); } } @@ -440,16 +486,36 @@ void RulesEvery50ms(void) event_data[0] ='\0'; } } + else if (vars_event) { + for (uint8_t i = 0; i < MAX_RULE_VARS-1; i++) { + if (bitRead(vars_event, i)) { + bitClear(vars_event, i); + snprintf_P(json_event, sizeof(json_event), PSTR("{\"Var%d\":{\"State\":%s}}"), i+1, vars[i]); + RulesProcessEvent(json_event); + break; + } + } + } + else if (mems_event) { + for (uint8_t i = 0; i < MAX_RULE_MEMS-1; i++) { + if (bitRead(mems_event, i)) { + bitClear(mems_event, i); + snprintf_P(json_event, sizeof(json_event), PSTR("{\"Mem%d\":{\"State\":%s}}"), i+1, Settings.mems[i]); + RulesProcessEvent(json_event); + break; + } + } + } else if (rules_flag.data) { uint16_t mask = 1; - for (byte i = 0; i < MAX_RULES_FLAG; i++) { + for (uint8_t i = 0; i < MAX_RULES_FLAG; i++) { if (rules_flag.data & mask) { rules_flag.data ^= mask; json_event[0] = '\0'; switch (i) { case 0: strncpy_P(json_event, PSTR("{\"System\":{\"Boot\":1}}"), sizeof(json_event)); break; - case 1: snprintf_P(json_event, sizeof(json_event), PSTR("{\"Time\":{\"Initialized\":%d}}"), GetMinutesPastMidnight()); break; - case 2: snprintf_P(json_event, sizeof(json_event), PSTR("{\"Time\":{\"Set\":%d}}"), GetMinutesPastMidnight()); break; + case 1: snprintf_P(json_event, sizeof(json_event), PSTR("{\"Time\":{\"Initialized\":%d}}"), MinutesPastMidnight()); break; + case 2: snprintf_P(json_event, sizeof(json_event), PSTR("{\"Time\":{\"Set\":%d}}"), MinutesPastMidnight()); break; case 3: strncpy_P(json_event, PSTR("{\"MQTT\":{\"Connected\":1}}"), sizeof(json_event)); break; case 4: strncpy_P(json_event, PSTR("{\"MQTT\":{\"Disconnected\":1}}"), sizeof(json_event)); break; case 5: strncpy_P(json_event, PSTR("{\"WIFI\":{\"Connected\":1}}"), sizeof(json_event)); break; @@ -492,11 +558,11 @@ void RulesEverySecond(void) if (RtcTime.valid) { if ((uptime > 60) && (RtcTime.minute != rules_last_minute)) { // Execute from one minute after restart every minute only once rules_last_minute = RtcTime.minute; - snprintf_P(json_event, sizeof(json_event), PSTR("{\"Time\":{\"Minute\":%d}}"), GetMinutesPastMidnight()); + snprintf_P(json_event, sizeof(json_event), PSTR("{\"Time\":{\"Minute\":%d}}"), MinutesPastMidnight()); RulesProcessEvent(json_event); } } - for (byte i = 0; i < MAX_RULE_TIMERS; i++) { + for (uint8_t i = 0; i < MAX_RULE_TIMERS; i++) { if (rules_timer[i] != 0L) { // Timer active? if (TimeReached(rules_timer[i])) { // Timer finished? rules_timer[i] = 0L; // Turn off this timer @@ -520,10 +586,496 @@ void RulesTeleperiod(void) rules_teleperiod = 0; } -boolean RulesCommand(void) +#ifdef SUPPORT_MQTT_EVENT +/********************************************************************************************/ +/* + * Rules: Process received MQTT message. + * If the message is in our subscription list, trigger an event with the value parsed from MQTT data + * Input: + * void - We are going to access XdrvMailbox data directly. + * Return: + * true - The message is consumed. + * false - The message is not in our list. + */ +bool RulesMqttData(void) +{ + bool serviced = false; + if (XdrvMailbox.data_len < 1 || XdrvMailbox.data_len > 128) { + return false; + } + String sTopic = XdrvMailbox.topic; + String sData = XdrvMailbox.data; + //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RUL: MQTT Topic %s, Event %s"), XdrvMailbox.topic, XdrvMailbox.data); + MQTT_Subscription event_item; + //Looking for matched topic + for (int index = 0; index < subscriptions.size(); index++) { + event_item = subscriptions.get(index); + + //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RUL: Match MQTT message Topic %s with subscription topic %s"), sTopic.c_str(), event_item.Topic.c_str()); + if (sTopic.startsWith(event_item.Topic)) { + //This topic is subscribed by us, so serve it + serviced = true; + String value; + if (event_item.Key.length() == 0) { //If did not specify Key + value = sData; + } else { //If specified Key, need to parse Key/Value from JSON data + StaticJsonBuffer<400> jsonBuf; + JsonObject& jsonData = jsonBuf.parseObject(sData); + String key1 = event_item.Key; + String key2; + if (!jsonData.success()) break; //Failed to parse JSON data, ignore this message. + int dot; + if ((dot = key1.indexOf('.')) > 0) { + key2 = key1.substring(dot+1); + key1 = key1.substring(0, dot); + if (!jsonData[key1][key2].success()) break; //Failed to get the key/value, ignore this message. + value = (const char *)jsonData[key1][key2]; + } else { + if (!jsonData[key1].success()) break; + value = (const char *)jsonData[key1]; + } + } + value.trim(); + //Create an new event. Cannot directly call RulesProcessEvent(). + snprintf_P(event_data, sizeof(event_data), PSTR("%s=%s"), event_item.Event.c_str(), value.c_str()); + } + } + return serviced; +} + +/********************************************************************************************/ +/* + * Subscribe a MQTT topic (with or without key) and assign an event name to it + * Command Subscribe format: + * Subscribe , [, ] + * This command will subscribe a and give it an event name . + * The optional parameter is for parse the specified key/value from MQTT message + * payload with JSON format. + * Subscribe + * Subscribe command without any parameter will list all topics currently subscribed. + * Input: + * data - A char buffer with all the parameters + * data_len - Length of the parameters + * Return: + * A string include subscribed event, topic and key. + */ +String RulesSubscribe(const char *data, int data_len) +{ + MQTT_Subscription subscription_item; + String events; + if (data_len > 0) { + char parameters[data_len+1]; + memcpy(parameters, data, data_len); + parameters[data_len] = '\0'; + String event_name, topic, key; + + char * pos = strtok(parameters, ","); + if (pos) { + event_name = Trim(pos); + pos = strtok(NULL, ","); + if (pos) { + topic = Trim(pos); + pos = strtok(NULL, ","); + if (pos) { + key = Trim(pos); + } + } + } + //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RUL: Subscribe command with parameters: %s, %s, %s."), event_name.c_str(), topic.c_str(), key.c_str()); + event_name.toUpperCase(); + if (event_name.length() > 0 && topic.length() > 0) { + //Search all subscriptions + for (int index=0; index < subscriptions.size(); index++) { + if (subscriptions.get(index).Event.equals(event_name)) { + //If find exists one, remove it. + String stopic = subscriptions.get(index).Topic + "/#"; + MqttUnsubscribe(stopic.c_str()); + subscriptions.remove(index); + break; + } + } + //Add "/#" to the topic + if (!topic.endsWith("#")) { + if (topic.endsWith("/")) { + topic.concat("#"); + } else { + topic.concat("/#"); + } + } + //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RUL: New topic: %s."), topic.c_str()); + //MQTT Subscribe + subscription_item.Event = event_name; + subscription_item.Topic = topic.substring(0, topic.length() - 2); //Remove "/#" so easy to match + subscription_item.Key = key; + subscriptions.add(subscription_item); + + MqttSubscribe(topic.c_str()); + events.concat(event_name + "," + topic + + (key.length()>0 ? "," : "") + + key); + } else { + events = D_JSON_WRONG_PARAMETERS; + } + } else { + //If did not specify the event name, list all subscribed event + for (int index=0; index < subscriptions.size(); index++) { + subscription_item = subscriptions.get(index); + events.concat(subscription_item.Event + "," + subscription_item.Topic + + (subscription_item.Key.length()>0 ? "," : "") + + subscription_item.Key + "; "); + } + } + return events; +} + +/********************************************************************************************/ +/* + * Unsubscribe specified MQTT event. If no event specified, Unsubscribe all. + * Command Unsubscribe format: + * Unsubscribe [] + * Input: + * data - Event name + * data_len - Length of the parameters + * Return: + * list all the events unsubscribed. + */ +String RulesUnsubscribe(const char * data, int data_len) +{ + MQTT_Subscription subscription_item; + String events; + if (data_len > 0) { + for (int index = 0; index < subscriptions.size(); index++) { + subscription_item = subscriptions.get(index); + if (subscription_item.Event.equalsIgnoreCase(data)) { + String stopic = subscription_item.Topic + "/#"; + MqttUnsubscribe(stopic.c_str()); + events = subscription_item.Event; + subscriptions.remove(index); + break; + } + } + } else { + //If did not specify the event name, unsubscribe all event + String stopic; + while (subscriptions.size() > 0) { + events.concat(subscriptions.get(0).Event + "; "); + stopic = subscriptions.get(0).Topic + "/#"; + MqttUnsubscribe(stopic.c_str()); + subscriptions.remove(0); + } + } + return events; +} +#endif // SUPPORT_MQTT_EVENT + +#ifdef USE_EXPRESSION +/********************************************************************************************/ +/* + * Parse a number value + * Input: + * pNumber - A char pointer point to a digit started string (guaranteed) + * value - Reference a double variable used to accept the result + * Output: + * pNumber - Pointer forward to next character after the number + * value - double type, the result value + * Return: + * true - succeed + * false - failed + */ +bool findNextNumber(char * &pNumber, double &value) +{ + bool bSucceed = false; + String sNumber = ""; + while (*pNumber) { + if (isdigit(*pNumber) || (*pNumber == '.')) { + sNumber += *pNumber; + pNumber++; + } else { + break; + } + } + if (sNumber.length() > 0) { + value = CharToDouble(sNumber.c_str()); + bSucceed = true; + } + return bSucceed; +} + +/********************************************************************************************/ +/* + * Parse a variable (like VAR1, MEM3) and get its value (double type) + * Input: + * pVarname - A char pointer point to a variable name string + * value - Reference a double variable used to accept the result + * Output: + * pVarname - Pointer forward to next character after the variable + * value - double type, the result value + * Return: + * true - succeed + * false - failed + */ +bool findNextVariableValue(char * &pVarname, double &value) +{ + bool succeed = true; + value = 0; + String sVarName = ""; + while (*pVarname) { + if (isalpha(*pVarname) || isdigit(*pVarname)) { + sVarName.concat(*pVarname); + pVarname++; + } else { + break; + } + } + sVarName.toUpperCase(); + if (sVarName.startsWith(F("VAR"))) { + int index = sVarName.substring(3).toInt(); + if (index > 0 && index <= MAX_RULE_VARS) { + value = CharToDouble(vars[index -1]); + } + } else if (sVarName.startsWith(F("MEM"))) { + int index = sVarName.substring(3).toInt(); + if (index > 0 && index <= MAX_RULE_MEMS) { + value = CharToDouble(Settings.mems[index -1]); + } + } else if (sVarName.equals(F("TIME"))) { + value = MinutesPastMidnight(); + } else if (sVarName.equals(F("UPTIME"))) { + value = MinutesUptime(); + } else if (sVarName.equals(F("UTCTIME"))) { + value = UtcTime(); + } else if (sVarName.equals(F("LOCALTIME"))) { + value = LocalTime(); +#if defined(USE_TIMERS) && defined(USE_SUNRISE) + } else if (sVarName.equals(F("SUNRISE"))) { + value = SunMinutes(0); + } else if (sVarName.equals(F("SUNSET"))) { + value = SunMinutes(1); +#endif + } else { + succeed = false; + } + + return succeed; +} + +/********************************************************************************************/ +/* + * Find next object in expression and evaluate it + * An object could be: + * - A float number start with a digit, like 0.787 + * - A variable name, like VAR1, MEM3 + * - An expression enclosed with a pair of round brackets, (.....) + * Input: + * pointer - A char pointer point to a place of the expression string + * value - Reference a double variable used to accept the result + * Output: + * pointer - Pointer forward to next character after next object + * value - double type, the result value + * Return: + * true - succeed + * false - failed + */ +bool findNextObjectValue(char * &pointer, double &value) +{ + bool bSucceed = false; + while (*pointer) + { + if (isspace(*pointer)) { //Skip leading spaces + pointer++; + continue; + } + if (isdigit(*pointer)) { //This object is a number + bSucceed = findNextNumber(pointer, value); + break; + } else if (isalpha(*pointer)) { //Should be a variable like VAR12, MEM1 + bSucceed = findNextVariableValue(pointer, value); + break; + } else if (*pointer == '(') { //It is a sub expression bracketed with () + pointer++; + char * sub_exp_start = pointer; //Find out the sub expression between a pair of parenthesis. "()" + unsigned int sub_exp_len = 0; + //Look for the matched closure parenthesis.")" + bool bFindClosures = false; + uint8_t matchClosures = 1; + while (*pointer) + { + if (*pointer == ')') { + matchClosures--; + if (matchClosures == 0) { + sub_exp_len = pointer - sub_exp_start; + bFindClosures = true; + break; + } + } else if (*pointer == '(') { + matchClosures++; + } + pointer++; + } + if (bFindClosures) { + value = evaluateExpression(sub_exp_start, sub_exp_len); + bSucceed = true; + } + break; + } else { //No number, no variable, no expression, then invalid object. + break; + } + } + return bSucceed; +} + +/********************************************************************************************/ +/* + * Find next operator in expression + * An operator could be: +, - , * , / , %, ^ + * Input: + * pointer - A char pointer point to a place of the expression string + * op - Reference to a variable used to accept the result + * Output: + * pointer - Pointer forward to next character after next operator + * op - The operator. 0, 1, 2, 3, 4, 5 + * Return: + * true - succeed + * false - failed + */ +bool findNextOperator(char * &pointer, int8_t &op) +{ + bool bSucceed = false; + while (*pointer) + { + if (isspace(*pointer)) { //Skip leading spaces + pointer++; + continue; + } + if (char *pch = strchr(kExpressionOperators, *pointer)) { //If it is an operator + op = (int8_t)(pch - kExpressionOperators); + pointer++; + bSucceed = true; + } + break; + } + return bSucceed; +} +/********************************************************************************************/ +/* + * Calculate a simple expression composed by 2 value and 1 operator, like 2 * 3 + * Input: + * pointer - A char pointer point to a place of the expression string + * value - Reference a double variable used to accept the result + * Output: + * pointer - Pointer forward to next character after next object + * value - double type, the result value + * Return: + * true - succeed + * false - failed + */ +double calculateTwoValues(double v1, double v2, uint8_t op) +{ + switch (op) + { + case EXPRESSION_OPERATOR_ADD: + return v1 + v2; + case EXPRESSION_OPERATOR_SUBTRACT: + return v1 - v2; + case EXPRESSION_OPERATOR_MULTIPLY: + return v1 * v2; + case EXPRESSION_OPERATOR_DIVIDEDBY: + return (0 == v2) ? 0 : (v1 / v2); + case EXPRESSION_OPERATOR_MODULO: + return (0 == v2) ? 0 : (int(v1) % int(v2)); + case EXPRESSION_OPERATOR_POWER: + return FastPrecisePow(v1, v2); + } + return 0; +} + +/********************************************************************************************/ +/* + * Parse and evaluate an expression. + * For example: "10 * ( MEM2 + 1) / 2" + * Right now, only support operators listed here: (order by priority) + * Priority 4: ^ (power) + * Priority 3: % (modulo, always get integer result) + * Priority 2: *, / + * Priority 1: +, - + * Input: + * expression - The expression to be evaluated + * len - Length of the expression + * Return: + * double - result. + * 0 - if the expression is invalid + * An example: + * MEM1 = 3, MEM2 = 6, VAR2 = 15, VAR10 = 80 + * At beginning, the expression might be complicated like: 3.14 * (MEM1 * (10 + VAR2 ^2) - 100) % 10 + VAR10 / (2 + MEM2) + * We are going to scan the whole expression, evaluate each object. + * Finally we will have a value list:. + * Order Object Value + * 0 3.14 3.14 + * 1 (MEM1 * (10 + VAR2 ^2) - 100) 605 + * 2 10 10 + * 3 VAR10 80 + * 4 (2 + MEM2) 8 + * And an operator list: + * Order Operator Priority + * 0 * 2 + * 1 % 3 + * 2 + 1 + * 3 / 2 + */ +double evaluateExpression(const char * expression, unsigned int len) +{ + char expbuf[len + 1]; + memcpy(expbuf, expression, len); + expbuf[len] = '\0'; + char * scan_pointer = expbuf; + + LinkedList object_values; + LinkedList operators; + int8_t op; + double va; + //Find and add the value of first object + if (findNextObjectValue(scan_pointer, va)) { + object_values.add(va); + } else { + return 0; + } + while (*scan_pointer) + { + if (findNextOperator(scan_pointer, op) + && *scan_pointer + && findNextObjectValue(scan_pointer, va)) + { + operators.add(op); + object_values.add(va); + } else { + //No operator followed or no more object after this operator, we done. + break; + } + } + + //Going to evaluate the whole expression + //Calculate by order of operator priorities. Looking for all operators with specified priority (from High to Low) + for (int8_t priority = MAX_EXPRESSION_OPERATOR_PRIORITY; priority>0; priority--) { + int index = 0; + while (index < operators.size()) { + if (priority == kExpressionOperatorsPriorities[(operators.get(index))]) { //need to calculate the operator first + //get current object value and remove the next object with current operator + va = calculateTwoValues(object_values.get(index), object_values.remove(index + 1), operators.remove(index)); + //Replace the current value with the result + object_values.set(index, va); + } else { + index++; + } + } + } + return object_values.get(0); +} +#endif //USE_EXPRESSION + +bool RulesCommand(void) { char command[CMDSZ]; - boolean serviced = true; + bool serviced = true; uint8_t index = XdrvMailbox.index; int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic, kRulesCommands); @@ -578,10 +1130,15 @@ boolean RulesCommand(void) } else if ((CMND_RULETIMER == command_code) && (index > 0) && (index <= MAX_RULE_TIMERS)) { if (XdrvMailbox.data_len > 0) { +#ifdef USE_EXPRESSION + double timer_set = evaluateExpression(XdrvMailbox.data, XdrvMailbox.data_len); + rules_timer[index -1] = (timer_set > 0) ? millis() + (1000 * timer_set) : 0; +#else rules_timer[index -1] = (XdrvMailbox.payload > 0) ? millis() + (1000 * XdrvMailbox.payload) : 0; +#endif //USE_EXPRESSION } mqtt_data[0] = '\0'; - for (byte i = 0; i < MAX_RULE_TIMERS; i++) { + for (uint8_t i = 0; i < MAX_RULE_TIMERS; i++) { snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s%c\"T%d\":%d"), mqtt_data, (i) ? ',' : '{', i +1, (rules_timer[i]) ? (rules_timer[i] - millis()) / 1000 : 0); } snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s}"), mqtt_data); @@ -594,13 +1151,23 @@ boolean RulesCommand(void) } else if ((CMND_VAR == command_code) && (index > 0) && (index <= MAX_RULE_VARS)) { if (XdrvMailbox.data_len > 0) { +#ifdef USE_EXPRESSION + dtostrfd(evaluateExpression(XdrvMailbox.data, XdrvMailbox.data_len), Settings.flag2.calc_resolution, vars[index -1]); +#else strlcpy(vars[index -1], ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data, sizeof(vars[index -1])); +#endif //USE_EXPRESSION + bitSet(vars_event, index -1); } snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, command, index, vars[index -1]); } else if ((CMND_MEM == command_code) && (index > 0) && (index <= MAX_RULE_MEMS)) { if (XdrvMailbox.data_len > 0) { +#ifdef USE_EXPRESSION + dtostrfd(evaluateExpression(XdrvMailbox.data, XdrvMailbox.data_len), Settings.flag2.calc_resolution, Settings.mems[index -1]); +#else strlcpy(Settings.mems[index -1], ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data, sizeof(Settings.mems[index -1])); +#endif //USE_EXPRESSION + bitSet(mems_event, index -1); } snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, command, index, Settings.mems[index -1]); } @@ -614,6 +1181,7 @@ boolean RulesCommand(void) if (XdrvMailbox.data_len > 0) { double tempvar = CharToDouble(vars[index -1]) + CharToDouble(XdrvMailbox.data); dtostrfd(tempvar, Settings.flag2.calc_resolution, vars[index -1]); + bitSet(vars_event, index -1); } snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, command, index, vars[index -1]); } @@ -621,6 +1189,7 @@ boolean RulesCommand(void) if (XdrvMailbox.data_len > 0) { double tempvar = CharToDouble(vars[index -1]) - CharToDouble(XdrvMailbox.data); dtostrfd(tempvar, Settings.flag2.calc_resolution, vars[index -1]); + bitSet(vars_event, index -1); } snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, command, index, vars[index -1]); } @@ -628,6 +1197,7 @@ boolean RulesCommand(void) if (XdrvMailbox.data_len > 0) { double tempvar = CharToDouble(vars[index -1]) * CharToDouble(XdrvMailbox.data); dtostrfd(tempvar, Settings.flag2.calc_resolution, vars[index -1]); + bitSet(vars_event, index -1); } snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, command, index, vars[index -1]); } @@ -643,9 +1213,18 @@ boolean RulesCommand(void) double toHigh = CharToDouble(subStr(sub_string, XdrvMailbox.data, ",", 5)); double value = map_double(valueIN, fromLow, fromHigh, toLow, toHigh); dtostrfd(value, Settings.flag2.calc_resolution, vars[index -1]); + bitSet(vars_event, index -1); } } snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, command, index, vars[index -1]); +#ifdef SUPPORT_MQTT_EVENT + } else if (CMND_SUBSCRIBE == command_code) { //MQTT Subscribe command. Subscribe , [, ] + String result = RulesSubscribe(XdrvMailbox.data, XdrvMailbox.data_len); + snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, result.c_str()); + } else if (CMND_UNSUBSCRIBE == command_code) { //MQTT Un-subscribe command. UnSubscribe + String result = RulesUnsubscribe(XdrvMailbox.data, XdrvMailbox.data_len); + snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, result.c_str()); +#endif //SUPPORT_MQTT_EVENT } else serviced = false; // Unknown command @@ -661,9 +1240,9 @@ double map_double(double x, double in_min, double in_max, double out_min, double * Interface \*********************************************************************************************/ -boolean Xdrv10(byte function) +bool Xdrv10(uint8_t function) { - boolean result = false; + bool result = false; switch (function) { case FUNC_PRE_INIT: @@ -687,6 +1266,11 @@ boolean Xdrv10(byte function) case FUNC_RULES_PROCESS: result = RulesProcess(); break; +#ifdef SUPPORT_MQTT_EVENT + case FUNC_MQTT_DATA: + result = RulesMqttData(); + break; +#endif //SUPPORT_MQTT_EVENT } return result; } diff --git a/sonoff/xdrv_11_knx.ino b/sonoff/xdrv_11_knx.ino index 9095b4efc..5ab0c7c88 100644 --- a/sonoff/xdrv_11_knx.ino +++ b/sonoff/xdrv_11_knx.ino @@ -1,7 +1,7 @@ /* xdrv_11_knx.ino - KNX IP Protocol support for Sonoff-Tasmota - Copyright (C) 2018 Adrian Scillato (https://github.com/ascillato) + Copyright (C) 2019 Adrian Scillato (https://github.com/ascillato) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -39,12 +39,12 @@ Variables in settings.h bool Settings.flag.knx_enabled Enable/Disable KNX Protocol uint16_t Settings.knx_physsical_addr Physical KNX address of this device -byte Settings.knx_GA_registered Number of group address to read -byte Settings.knx_CB_registered Number of group address to write +uint8_t Settings.knx_GA_registered Number of group address to read +uint8_t Settings.knx_CB_registered Number of group address to write uint16_t Settings.knx_GA_addr[MAX_KNX_GA] Group address to read uint16_t Settings.knx_CB_addr[MAX_KNX_CB] Group address to write -byte Settings.knx_GA_param[MAX_KNX_GA] Type of Input (relay changed, button pressed, sensor read) -byte Settings.knx_CB_param[MAX_KNX_CB] Type of Output (set relay, toggle relay, reply sensor value) +uint8_t Settings.knx_GA_param[MAX_KNX_GA] Type of Input (relay changed, button pressed, sensor read) +uint8_t Settings.knx_CB_param[MAX_KNX_CB] Type of Output (set relay, toggle relay, reply sensor value) \*********************************************************************************************/ @@ -61,11 +61,11 @@ address_t KNX_addr; // KNX Address converter variable float last_temp; float last_hum; -byte toggle_inhibit; +uint8_t toggle_inhibit; typedef struct __device_parameters { - byte type; // PARAMETER_ID. Used as type of GA = relay, button, sensor, etc, (INPUTS) + uint8_t type; // PARAMETER_ID. Used as type of GA = relay, button, sensor, etc, (INPUTS) // used when an action on device triggers a MSG to send on KNX // Needed because this is the value that the ESP_KNX_IP library will pass as parameter // to identify the action to perform when a MSG is received @@ -200,9 +200,9 @@ enum KnxCommands { CMND_KNXTXCMND, CMND_KNXTXVAL, CMND_KNX_ENABLED, CMND_KNX_ENH const char kKnxCommands[] PROGMEM = D_CMND_KNXTXCMND "|" D_CMND_KNXTXVAL "|" D_CMND_KNX_ENABLED "|" D_CMND_KNX_ENHANCED "|" D_CMND_KNX_PA "|" D_CMND_KNX_GA "|" D_CMND_KNX_CB ; -byte KNX_GA_Search( byte param, byte start = 0 ) +uint8_t KNX_GA_Search( uint8_t param, uint8_t start = 0 ) { - for (byte i = start; i < Settings.knx_GA_registered; ++i) + for (uint8_t i = start; i < Settings.knx_GA_registered; ++i) { if ( Settings.knx_GA_param[i] == param ) { @@ -216,9 +216,9 @@ byte KNX_GA_Search( byte param, byte start = 0 ) } -byte KNX_CB_Search( byte param, byte start = 0 ) +uint8_t KNX_CB_Search( uint8_t param, uint8_t start = 0 ) { - for (byte i = start; i < Settings.knx_CB_registered; ++i) + for (uint8_t i = start; i < Settings.knx_CB_registered; ++i) { if ( Settings.knx_CB_param[i] == param ) { @@ -232,7 +232,7 @@ byte KNX_CB_Search( byte param, byte start = 0 ) } -void KNX_ADD_GA( byte GAop, byte GA_FNUM, byte GA_AREA, byte GA_FDEF ) +void KNX_ADD_GA( uint8_t GAop, uint8_t GA_FNUM, uint8_t GA_AREA, uint8_t GA_FDEF ) { // Check if all GA were assigned. If yes-> return if ( Settings.knx_GA_registered >= MAX_KNX_GA ) { return; } @@ -247,20 +247,19 @@ void KNX_ADD_GA( byte GAop, byte GA_FNUM, byte GA_AREA, byte GA_FDEF ) Settings.knx_GA_registered++; - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_KNX D_ADD " GA #%d: %s " D_TO " %d/%d/%d"), + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_ADD " GA #%d: %s " D_TO " %d/%d/%d"), Settings.knx_GA_registered, device_param_ga[GAop-1], GA_FNUM, GA_AREA, GA_FDEF ); - AddLog(LOG_LEVEL_DEBUG); } -void KNX_DEL_GA( byte GAnum ) +void KNX_DEL_GA( uint8_t GAnum ) { - byte dest_offset = 0; - byte src_offset = 0; - byte len = 0; + uint8_t dest_offset = 0; + uint8_t src_offset = 0; + uint8_t len = 0; // Delete GA Settings.knx_GA_param[GAnum-1] = 0; @@ -291,19 +290,18 @@ void KNX_DEL_GA( byte GAnum ) if (len > 0) { - memmove(Settings.knx_GA_param + dest_offset, Settings.knx_GA_param + src_offset, len * sizeof(byte)); + memmove(Settings.knx_GA_param + dest_offset, Settings.knx_GA_param + src_offset, len * sizeof(uint8_t)); memmove(Settings.knx_GA_addr + dest_offset, Settings.knx_GA_addr + src_offset, len * sizeof(uint16_t)); } Settings.knx_GA_registered--; - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_KNX D_DELETE " GA #%d"), + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_DELETE " GA #%d"), GAnum ); - AddLog(LOG_LEVEL_DEBUG); } -void KNX_ADD_CB( byte CBop, byte CB_FNUM, byte CB_AREA, byte CB_FDEF ) +void KNX_ADD_CB( uint8_t CBop, uint8_t CB_FNUM, uint8_t CB_AREA, uint8_t CB_FDEF ) { // Check if all callbacks were assigned. If yes-> return if ( Settings.knx_CB_registered >= MAX_KNX_CB ) { return; } @@ -330,20 +328,19 @@ void KNX_ADD_CB( byte CBop, byte CB_FNUM, byte CB_AREA, byte CB_FDEF ) Settings.knx_CB_registered++; - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_KNX D_ADD " CB #%d: %d/%d/%d " D_TO " %s"), + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_ADD " CB #%d: %d/%d/%d " D_TO " %s"), Settings.knx_CB_registered, CB_FNUM, CB_AREA, CB_FDEF, device_param_cb[CBop-1] ); - AddLog(LOG_LEVEL_DEBUG); } -void KNX_DEL_CB( byte CBnum ) +void KNX_DEL_CB( uint8_t CBnum ) { - byte oldparam = Settings.knx_CB_param[CBnum-1]; - byte dest_offset = 0; - byte src_offset = 0; - byte len = 0; + uint8_t oldparam = Settings.knx_CB_param[CBnum-1]; + uint8_t dest_offset = 0; + uint8_t src_offset = 0; + uint8_t len = 0; // Delete assigment knx.callback_unassign(CBnum-1); @@ -375,7 +372,7 @@ void KNX_DEL_CB( byte CBnum ) if (len > 0) { - memmove(Settings.knx_CB_param + dest_offset, Settings.knx_CB_param + src_offset, len * sizeof(byte)); + memmove(Settings.knx_CB_param + dest_offset, Settings.knx_CB_param + src_offset, len * sizeof(uint8_t)); memmove(Settings.knx_CB_addr + dest_offset, Settings.knx_CB_addr + src_offset, len * sizeof(uint16_t)); } @@ -387,15 +384,14 @@ void KNX_DEL_CB( byte CBnum ) device_param[oldparam-1].CB_id = KNX_Empty; } - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_KNX D_DELETE " CB #%d"), CBnum ); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_DELETE " CB #%d"), CBnum ); } bool KNX_CONFIG_NOT_MATCH(void) { // Check for configured parameters that the device does not have (module changed) - for (byte i = 0; i < KNX_MAX_device_param; ++i) + for (uint8_t i = 0; i < KNX_MAX_device_param; ++i) { if ( !device_param[i].show ) { // device has this parameter ? // if not, search for all registered group address to this parameter for deletion @@ -417,7 +413,7 @@ bool KNX_CONFIG_NOT_MATCH(void) } // Check for invalid or erroneous configuration (tasmota flashed without clearing the memory) - for (byte i = 0; i < Settings.knx_GA_registered; ++i) + for (uint8_t i = 0; i < Settings.knx_GA_registered; ++i) { if ( Settings.knx_GA_param[i] != 0 ) // the GA[i] have a parameter defined? { @@ -427,7 +423,7 @@ bool KNX_CONFIG_NOT_MATCH(void) } } } - for (byte i = 0; i < Settings.knx_CB_registered; ++i) + for (uint8_t i = 0; i < Settings.knx_CB_registered; ++i) { if ( Settings.knx_CB_param[i] != 0 ) // the CB[i] have a parameter defined? { @@ -445,8 +441,7 @@ bool KNX_CONFIG_NOT_MATCH(void) void KNXStart(void) { knx.start(nullptr); - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_KNX D_START)); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_START)); } @@ -465,11 +460,11 @@ void KNX_INIT(void) // and activate options according to the hardware /*for (int i = GPIO_REL1; i < GPIO_REL8 + 1; ++i) { - if (GetUsedInModule(i, my_module.gp.io)) { device_param[i - GPIO_REL1].show = true; } + if (GetUsedInModule(i, my_module.io)) { device_param[i - GPIO_REL1].show = true; } } for (int i = GPIO_REL1_INV; i < GPIO_REL8_INV + 1; ++i) { - if (GetUsedInModule(i, my_module.gp.io)) { device_param[i - GPIO_REL1_INV].show = true; } + if (GetUsedInModule(i, my_module.io)) { device_param[i - GPIO_REL1_INV].show = true; } }*/ for (int i = 0; i < devices_present; ++i) { @@ -477,30 +472,30 @@ void KNX_INIT(void) } for (int i = GPIO_SWT1; i < GPIO_SWT4 + 1; ++i) { - if (GetUsedInModule(i, my_module.gp.io)) { device_param[i - GPIO_SWT1 + 8].show = true; } + if (GetUsedInModule(i, my_module.io)) { device_param[i - GPIO_SWT1 + 8].show = true; } } for (int i = GPIO_KEY1; i < GPIO_KEY4 + 1; ++i) { - if (GetUsedInModule(i, my_module.gp.io)) { device_param[i - GPIO_KEY1 + 8].show = true; } + if (GetUsedInModule(i, my_module.io)) { device_param[i - GPIO_KEY1 + 8].show = true; } } for (int i = GPIO_SWT1_NP; i < GPIO_SWT4_NP + 1; ++i) { - if (GetUsedInModule(i, my_module.gp.io)) { device_param[i - GPIO_SWT1_NP + 8].show = true; } + if (GetUsedInModule(i, my_module.io)) { device_param[i - GPIO_SWT1_NP + 8].show = true; } } for (int i = GPIO_KEY1_NP; i < GPIO_KEY4_NP + 1; ++i) { - if (GetUsedInModule(i, my_module.gp.io)) { device_param[i - GPIO_KEY1_NP + 8].show = true; } + if (GetUsedInModule(i, my_module.io)) { device_param[i - GPIO_KEY1_NP + 8].show = true; } } - if (GetUsedInModule(GPIO_DHT11, my_module.gp.io)) { device_param[KNX_TEMPERATURE-1].show = true; } - if (GetUsedInModule(GPIO_DHT22, my_module.gp.io)) { device_param[KNX_TEMPERATURE-1].show = true; } - if (GetUsedInModule(GPIO_SI7021, my_module.gp.io)) { device_param[KNX_TEMPERATURE-1].show = true; } - if (GetUsedInModule(GPIO_DSB, my_module.gp.io)) { device_param[KNX_TEMPERATURE-1].show = true; } - if (GetUsedInModule(GPIO_DHT11, my_module.gp.io)) { device_param[KNX_HUMIDITY-1].show = true; } - if (GetUsedInModule(GPIO_DHT22, my_module.gp.io)) { device_param[KNX_HUMIDITY-1].show = true; } - if (GetUsedInModule(GPIO_SI7021, my_module.gp.io)) { device_param[KNX_HUMIDITY-1].show = true; } + if (GetUsedInModule(GPIO_DHT11, my_module.io)) { device_param[KNX_TEMPERATURE-1].show = true; } + if (GetUsedInModule(GPIO_DHT22, my_module.io)) { device_param[KNX_TEMPERATURE-1].show = true; } + if (GetUsedInModule(GPIO_SI7021, my_module.io)) { device_param[KNX_TEMPERATURE-1].show = true; } + if (GetUsedInModule(GPIO_DSB, my_module.io)) { device_param[KNX_TEMPERATURE-1].show = true; } + if (GetUsedInModule(GPIO_DHT11, my_module.io)) { device_param[KNX_HUMIDITY-1].show = true; } + if (GetUsedInModule(GPIO_DHT22, my_module.io)) { device_param[KNX_HUMIDITY-1].show = true; } + if (GetUsedInModule(GPIO_SI7021, my_module.io)) { device_param[KNX_HUMIDITY-1].show = true; } - // Sonoff 31 or Sonoff Pow or any HLW8012 based device or Sonoff POW R2 or Any device with a Pzem004T - if ( ( SONOFF_S31 == Settings.module ) || ( SONOFF_POW_R2 == Settings.module ) || ( energy_flg != ENERGY_NONE ) ) { + // Any device with a Power Monitoring + if ( energy_flg != ENERGY_NONE ) { device_param[KNX_ENERGY_POWER-1].show = true; device_param[KNX_ENERGY_DAILY-1].show = true; device_param[KNX_ENERGY_START-1].show = true; @@ -522,15 +517,14 @@ void KNX_INIT(void) if (KNX_CONFIG_NOT_MATCH()) { Settings.knx_GA_registered = 0; Settings.knx_CB_registered = 0; - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_KNX D_DELETE " " D_KNX_PARAMETERS )); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_DELETE " " D_KNX_PARAMETERS)); } // Register Group Addresses to listen to // Search on the settings if there is a group address set for receive KNX messages for the type: device_param[j].type // If there is, register the group address on the KNX_IP Library to Receive data for Executing Callbacks - byte j; - for (byte i = 0; i < Settings.knx_CB_registered; ++i) + uint8_t j; + for (uint8_t i = 0; i < Settings.knx_CB_registered; ++i) { j = Settings.knx_CB_param[i]; if ( j > 0 ) @@ -555,22 +549,18 @@ void KNX_CB_Action(message_t const &msg, void *arg) if (msg.data_len == 1) { // COMMAND - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_KNX D_RECEIVED_FROM " %d.%d.%d " D_COMMAND " %s: %d " D_TO " %s"), - msg.received_on.ga.area, msg.received_on.ga.line, msg.received_on.ga.member, - (msg.ct == KNX_CT_WRITE) ? D_KNX_COMMAND_WRITE : (msg.ct == KNX_CT_READ) ? D_KNX_COMMAND_READ : D_KNX_COMMAND_OTHER, - msg.data[0], - device_param_cb[(chan->type)-1]); + tempchar[0] = msg.data[0]; + tempchar[1] = '\0'; } else { // VALUE float tempvar = knx.data_to_2byte_float(msg.data); dtostrfd(tempvar,2,tempchar); - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_KNX D_RECEIVED_FROM " %d.%d.%d " D_COMMAND " %s: %s " D_TO " %s"), - msg.received_on.ga.area, msg.received_on.ga.line, msg.received_on.ga.member, - (msg.ct == KNX_CT_WRITE) ? D_KNX_COMMAND_WRITE : (msg.ct == KNX_CT_READ) ? D_KNX_COMMAND_READ : D_KNX_COMMAND_OTHER, - tempchar, - device_param_cb[(chan->type)-1]); } - AddLog(LOG_LEVEL_INFO); + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_KNX D_RECEIVED_FROM " %d.%d.%d " D_COMMAND " %s: %s " D_TO " %s"), + msg.received_on.ga.area, msg.received_on.ga.line, msg.received_on.ga.member, + (msg.ct == KNX_CT_WRITE) ? D_KNX_COMMAND_WRITE : (msg.ct == KNX_CT_READ) ? D_KNX_COMMAND_READ : D_KNX_COMMAND_OTHER, + tempchar, + device_param_cb[(chan->type)-1]); switch (msg.ct) { @@ -652,14 +642,14 @@ void KNX_CB_Action(message_t const &msg, void *arg) } -void KnxUpdatePowerState(byte device, power_t state) +void KnxUpdatePowerState(uint8_t device, power_t state) { if (!(Settings.flag.knx_enabled)) { return; } device_param[device -1].last_state = bitRead(state, device -1); // power state (on/off) // Search all the registered GA that has that output (variable: device) as parameter - byte i = KNX_GA_Search(device); + uint8_t i = KNX_GA_Search(device); while ( i != KNX_Empty ) { KNX_addr.value = Settings.knx_GA_addr[i]; knx.write_1bit(KNX_addr, device_param[device -1].last_state); @@ -668,17 +658,16 @@ void KnxUpdatePowerState(byte device, power_t state) knx.write_1bit(KNX_addr, device_param[device -1].last_state); } - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_KNX "%s = %d " D_SENT_TO " %d.%d.%d"), + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s = %d " D_SENT_TO " %d.%d.%d"), device_param_ga[device -1], device_param[device -1].last_state, KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member); - AddLog(LOG_LEVEL_INFO); i = KNX_GA_Search(device, i + 1); } } -void KnxSendButtonPower(byte key, byte device, byte state) +void KnxSendButtonPower(uint8_t key, uint8_t device, uint8_t state) { // key 0 = button_topic // key 1 = switch_topic @@ -692,7 +681,7 @@ void KnxSendButtonPower(byte key, byte device, byte state) // { // Search all the registered GA that has that output (variable: device) as parameter - byte i = KNX_GA_Search(device + 8); + uint8_t i = KNX_GA_Search(device + 8); while ( i != KNX_Empty ) { KNX_addr.value = Settings.knx_GA_addr[i]; knx.write_1bit(KNX_addr, !(state == 0)); @@ -701,10 +690,9 @@ void KnxSendButtonPower(byte key, byte device, byte state) knx.write_1bit(KNX_addr, !(state == 0)); } - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_KNX "%s = %d " D_SENT_TO " %d.%d.%d"), + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s = %d " D_SENT_TO " %d.%d.%d"), device_param_ga[device + 7], !(state == 0), KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member); - AddLog(LOG_LEVEL_INFO); i = KNX_GA_Search(device + 8, i + 1); } @@ -712,7 +700,7 @@ void KnxSendButtonPower(byte key, byte device, byte state) } -void KnxSensor(byte sensor_type, float value) +void KnxSensor(uint8_t sensor_type, float value) { if (sensor_type == KNX_TEMPERATURE) { @@ -724,7 +712,7 @@ void KnxSensor(byte sensor_type, float value) if (!(Settings.flag.knx_enabled)) { return; } - byte i = KNX_GA_Search(sensor_type); + uint8_t i = KNX_GA_Search(sensor_type); while ( i != KNX_Empty ) { KNX_addr.value = Settings.knx_GA_addr[i]; knx.write_2byte_float(KNX_addr, value); @@ -733,10 +721,9 @@ void KnxSensor(byte sensor_type, float value) knx.write_2byte_float(KNX_addr, value); } - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_KNX "%s " D_SENT_TO " %d.%d.%d "), + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s " D_SENT_TO " %d.%d.%d "), device_param_ga[sensor_type -1], KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member); - AddLog(LOG_LEVEL_INFO); i = KNX_GA_Search(sensor_type, i+1); } @@ -752,20 +739,22 @@ void KnxSensor(byte sensor_type, float value) const char S_CONFIGURE_KNX[] PROGMEM = D_CONFIGURE_KNX; const char HTTP_BTN_MENU_KNX[] PROGMEM = - "
"; + "

"; const char HTTP_FORM_KNX[] PROGMEM = - "
 " D_KNX_PARAMETERS " 
" + "
" + " " D_KNX_PARAMETERS " " + "" "
" "" D_KNX_PHYSICAL_ADDRESS " " - " . " - " . " - "" + " . " + " . " + "" "

" D_KNX_PHYSICAL_ADDRESS_NOTE "

" - "" D_KNX_ENABLE " " D_KNX_ENABLE "

" @@ -773,23 +762,23 @@ const char HTTP_FORM_KNX2[] PROGMEM = "
" "" D_KNX_GROUP_ADDRESS_TO_WRITE "
" - ""; const char HTTP_FORM_KNX_OPT[] PROGMEM = - ""; + ""; const char HTTP_FORM_KNX_GA[] PROGMEM = - " / " - " / " - " "; + " / " + " / " + " "; const char HTTP_FORM_KNX_ADD_BTN[] PROGMEM = - "

" - ""; + "

" + "
"; const char HTTP_FORM_KNX_ADD_TABLE_ROW[] PROGMEM = - "" - ""; + "" + ""; const char HTTP_FORM_KNX3[] PROGMEM = "
{optex} -> GAfnum / GAarea / GAfdef
%s -> %d / %d / %d

" @@ -797,16 +786,16 @@ const char HTTP_FORM_KNX3[] PROGMEM = "" D_KNX_GROUP_ADDRESS_TO_READ "
"; const char HTTP_FORM_KNX4[] PROGMEM = - "-> "; const char HTTP_FORM_KNX_ADD_TABLE_ROW2[] PROGMEM = - "GAfnum / GAarea / GAfdef -> {optex}" - ""; + "%d / %d / %d -> %s" + ""; void HandleKNXConfiguration(void) { - if (HttpUser()) { return; } - if (!WebAuthenticate()) { return WebServer->requestAuthentication(); } + if (!HttpCheckPriviledgedAccess()) { return; } + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_KNX); char tmp[100]; @@ -822,13 +811,13 @@ void HandleKNXConfiguration(void) if ( WebServer->arg("btn_add") == "1" ) { stmp = WebServer->arg("GAop"); //option selected - byte GAop = stmp.toInt(); + uint8_t GAop = stmp.toInt(); stmp = WebServer->arg("GA_FNUM"); - byte GA_FNUM = stmp.toInt(); + uint8_t GA_FNUM = stmp.toInt(); stmp = WebServer->arg("GA_AREA"); - byte GA_AREA = stmp.toInt(); + uint8_t GA_AREA = stmp.toInt(); stmp = WebServer->arg("GA_FDEF"); - byte GA_FDEF = stmp.toInt(); + uint8_t GA_FDEF = stmp.toInt(); if (GAop) { KNX_ADD_GA( GAop, GA_FNUM, GA_AREA, GA_FDEF ); @@ -838,13 +827,13 @@ void HandleKNXConfiguration(void) { stmp = WebServer->arg("CBop"); //option selected - byte CBop = stmp.toInt(); + uint8_t CBop = stmp.toInt(); stmp = WebServer->arg("CB_FNUM"); - byte CB_FNUM = stmp.toInt(); + uint8_t CB_FNUM = stmp.toInt(); stmp = WebServer->arg("CB_AREA"); - byte CB_AREA = stmp.toInt(); + uint8_t CB_AREA = stmp.toInt(); stmp = WebServer->arg("CB_FDEF"); - byte CB_FDEF = stmp.toInt(); + uint8_t CB_FDEF = stmp.toInt(); if (CBop) { KNX_ADD_CB( CBop, CB_FNUM, CB_AREA, CB_FDEF ); @@ -855,7 +844,7 @@ void HandleKNXConfiguration(void) { stmp = WebServer->arg("btn_del_ga"); - byte GA_NUM = stmp.toInt(); + uint8_t GA_NUM = stmp.toInt(); KNX_DEL_GA(GA_NUM); @@ -864,139 +853,88 @@ void HandleKNXConfiguration(void) { stmp = WebServer->arg("btn_del_cb"); - byte CB_NUM = stmp.toInt(); + uint8_t CB_NUM = stmp.toInt(); KNX_DEL_CB(CB_NUM); } - String page = FPSTR(HTTP_HEAD); - page.replace(F("{v}"), FPSTR(S_CONFIGURE_KNX)); - page += FPSTR(HTTP_HEAD_STYLE); - page.replace(F("340px"), F("530px")); - page += FPSTR(HTTP_FORM_KNX); - KNX_physs_addr.value = Settings.knx_physsical_addr; - page.replace(F("{kna"), String(KNX_physs_addr.pa.area)); - page.replace(F("{knl"), String(KNX_physs_addr.pa.line)); - page.replace(F("{knm"), String(KNX_physs_addr.pa.member)); - if ( Settings.flag.knx_enabled ) { page += F(" checked"); } - page += FPSTR(HTTP_FORM_KNX1); - if ( Settings.flag.knx_enable_enhancement ) { page += F(" checked"); } + WSContentStart_P(S_CONFIGURE_KNX); + WSContentSend_P( + PSTR("function GAwarning()" + "{" + "var GA_FNUM=eb('GA_FNUM');" + "var GA_AREA=eb('GA_AREA');" + "var GA_FDEF=eb('GA_FDEF');" + "if(GA_FNUM!=null&&GA_FNUM.value=='0'&&GA_AREA.value=='0'&&GA_FDEF.value=='0'){" + "alert('" D_KNX_WARNING "');" + "}" + "}" + "function CBwarning()" + "{" + "var CB_FNUM=eb('CB_FNUM');" + "var CB_AREA=eb('CB_AREA');" + "var CB_FDEF=eb('CB_FDEF');" + "if(CB_FNUM!=null&&CB_FNUM.value=='0'&&CB_AREA.value=='0'&&CB_FDEF.value=='0'){" + "alert('" D_KNX_WARNING "');" + "}" + "}")); + WSContentSendStyle(); + WSContentSend_P(HTTP_FORM_KNX, KNX_physs_addr.pa.area, KNX_physs_addr.pa.line, KNX_physs_addr.pa.member); + if ( Settings.flag.knx_enabled ) { WSContentSend_P(PSTR(" checked")); } + WSContentSend_P(HTTP_FORM_KNX1); + if ( Settings.flag.knx_enable_enhancement ) { WSContentSend_P(PSTR(" checked")); } - page += FPSTR(HTTP_FORM_KNX2); - for (byte i = 0; i < KNX_MAX_device_param ; i++) + WSContentSend_P(HTTP_FORM_KNX2); + for (uint8_t i = 0; i < KNX_MAX_device_param ; i++) { if ( device_param[i].show ) { - page += FPSTR(HTTP_FORM_KNX_OPT); - page.replace(F("{vop}"), String(device_param[i].type)); - page.replace(F("{nop}"), String(device_param_ga[i])); + WSContentSend_P(HTTP_FORM_KNX_OPT, device_param[i].type, device_param_ga[i]); } } - page += F(" -> "); - page += FPSTR(HTTP_FORM_KNX_GA); - page.replace(F("GAfnum"), F("GA_FNUM")); - page.replace(F("GAarea"), F("GA_AREA")); - page.replace(F("GAfdef"), F("GA_FDEF")); - page.replace(F("GAfnum"), F("GA_FNUM")); - page.replace(F("GAarea"), F("GA_AREA")); - page.replace(F("GAfdef"), F("GA_FDEF")); - page += FPSTR(HTTP_FORM_KNX_ADD_BTN); - page.replace(F("{btnval}"), String(1)); - if (Settings.knx_GA_registered < MAX_KNX_GA) { - page.replace(F("btndis"), F(" ")); - } - else - { - page.replace(F("btndis"), F("disabled")); - } - page.replace(F("fncbtnadd"), F("GAwarning")); - for (byte i = 0; i < Settings.knx_GA_registered ; ++i) + WSContentSend_P(PSTR(" -> ")); + WSContentSend_P(HTTP_FORM_KNX_GA, "GA_FNUM", "GA_FNUM", "GA_AREA", "GA_AREA", "GA_FDEF", "GA_FDEF"); + WSContentSend_P(HTTP_FORM_KNX_ADD_BTN, "GAwarning", (Settings.knx_GA_registered < MAX_KNX_GA) ? "" : "disabled", 1); + for (uint8_t i = 0; i < Settings.knx_GA_registered ; ++i) { if ( Settings.knx_GA_param[i] ) { - page += FPSTR(HTTP_FORM_KNX_ADD_TABLE_ROW); - page.replace(F("{opval}"), String(i+1)); - page.replace(F("{optex}"), String(device_param_ga[Settings.knx_GA_param[i]-1])); KNX_addr.value = Settings.knx_GA_addr[i]; - page.replace(F("GAfnum"), String(KNX_addr.ga.area)); - page.replace(F("GAarea"), String(KNX_addr.ga.line)); - page.replace(F("GAfdef"), String(KNX_addr.ga.member)); + WSContentSend_P(HTTP_FORM_KNX_ADD_TABLE_ROW, device_param_ga[Settings.knx_GA_param[i]-1], KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member, i +1); } } - page += FPSTR(HTTP_FORM_KNX3); - page += FPSTR(HTTP_FORM_KNX_GA); - page.replace(F("GAfnum"), F("CB_FNUM")); - page.replace(F("GAarea"), F("CB_AREA")); - page.replace(F("GAfdef"), F("CB_FDEF")); - page.replace(F("GAfnum"), F("CB_FNUM")); - page.replace(F("GAarea"), F("CB_AREA")); - page.replace(F("GAfdef"), F("CB_FDEF")); - page += FPSTR(HTTP_FORM_KNX4); - byte j; - for (byte i = 0; i < KNX_MAX_device_param ; i++) + WSContentSend_P(HTTP_FORM_KNX3); + WSContentSend_P(HTTP_FORM_KNX_GA, "CB_FNUM", "CB_FNUM", "CB_AREA", "CB_AREA", "CB_FDEF", "CB_FDEF"); + WSContentSend_P(HTTP_FORM_KNX4); + + uint8_t j; + for (uint8_t i = 0; i < KNX_MAX_device_param ; i++) { // Check How many Relays are available and add: RelayX and TogleRelayX if ( (i > 8) && (i < 16) ) { j=i-8; } else { j=i; } if ( i == 8 ) { j = 0; } if ( device_param[j].show ) { - page += FPSTR(HTTP_FORM_KNX_OPT); - page.replace(F("{vop}"), String(device_param[i].type)); - page.replace(F("{nop}"), String(device_param_cb[i])); + WSContentSend_P(HTTP_FORM_KNX_OPT, device_param[i].type, device_param_cb[i]); } } - page += F(" "); - page += FPSTR(HTTP_FORM_KNX_ADD_BTN); - page.replace(F("{btnval}"), String(2)); - if (Settings.knx_CB_registered < MAX_KNX_CB) { - page.replace(F("btndis"), F(" ")); - } - else - { - page.replace(F("btndis"), F("disabled")); - } - page.replace(F("fncbtnadd"), F("CBwarning")); + WSContentSend_P(PSTR(" ")); + WSContentSend_P(HTTP_FORM_KNX_ADD_BTN, "CBwarning", (Settings.knx_CB_registered < MAX_KNX_CB) ? "" : "disabled", 2); - for (byte i = 0; i < Settings.knx_CB_registered ; ++i) + for (uint8_t i = 0; i < Settings.knx_CB_registered ; ++i) { if ( Settings.knx_CB_param[i] ) { - page += FPSTR(HTTP_FORM_KNX_ADD_TABLE_ROW2); - page.replace(F("{opval}"), String(i+1)); - page.replace(F("{optex}"), String(device_param_cb[Settings.knx_CB_param[i]-1])); KNX_addr.value = Settings.knx_CB_addr[i]; - page.replace(F("GAfnum"), String(KNX_addr.ga.area)); - page.replace(F("GAarea"), String(KNX_addr.ga.line)); - page.replace(F("GAfdef"), String(KNX_addr.ga.member)); + WSContentSend_P(HTTP_FORM_KNX_ADD_TABLE_ROW2, KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member, device_param_cb[Settings.knx_CB_param[i]-1], i +1); } } - page += F("
"); - page += F("
"); - page += FPSTR(HTTP_BTN_CONF); - - page.replace( F(""), - F("function GAwarning()" - "{" - "var GA_FNUM = document.getElementById('GA_FNUM');" - "var GA_AREA = document.getElementById('GA_AREA');" - "var GA_FDEF = document.getElementById('GA_FDEF');" - "if ( GA_FNUM != null && GA_FNUM.value == '0' && GA_AREA.value == '0' && GA_FDEF.value == '0' ) {" - "alert('" D_KNX_WARNING "');" - "}" - "}" - "function CBwarning()" - "{" - "var CB_FNUM = document.getElementById('CB_FNUM');" - "var CB_AREA = document.getElementById('CB_AREA');" - "var CB_FDEF = document.getElementById('CB_FDEF');" - "if ( CB_FNUM != null && CB_FNUM.value == '0' && CB_AREA.value == '0' && CB_FDEF.value == '0' ) {" - "alert('" D_KNX_WARNING "');" - "}" - "}" - "") ); - ShowPage(page); + WSContentSend_P(PSTR("
")); + WSContentSend_P(HTTP_FORM_END); + WSContentSpaceButton(BUTTON_CONFIGURATION); + WSContentEnd(); } } @@ -1009,9 +947,8 @@ void KNX_Save_Settings(void) Settings.flag.knx_enabled = WebServer->hasArg("b1"); Settings.flag.knx_enable_enhancement = WebServer->hasArg("b2"); - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_KNX D_ENABLED ": %d, " D_KNX_ENHANCEMENT ": %d"), + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_ENABLED ": %d, " D_KNX_ENHANCEMENT ": %d"), Settings.flag.knx_enabled, Settings.flag.knx_enable_enhancement ); - AddLog(LOG_LEVEL_DEBUG); stmp = WebServer->arg("area"); KNX_addr.pa.area = stmp.toInt(); @@ -1021,33 +958,29 @@ void KNX_Save_Settings(void) KNX_addr.pa.member = stmp.toInt(); Settings.knx_physsical_addr = KNX_addr.value; knx.physical_address_set( KNX_addr ); // Set Physical KNX Address of the device - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_KNX D_KNX_PHYSICAL_ADDRESS ": %d.%d.%d "), + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_KNX_PHYSICAL_ADDRESS ": %d.%d.%d "), KNX_addr.pa.area, KNX_addr.pa.line, KNX_addr.pa.member ); - AddLog(LOG_LEVEL_DEBUG); - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_KNX "GA: %d"), + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX "GA: %d"), Settings.knx_GA_registered ); - AddLog(LOG_LEVEL_DEBUG); - for (byte i = 0; i < Settings.knx_GA_registered ; ++i) + for (uint8_t i = 0; i < Settings.knx_GA_registered ; ++i) { KNX_addr.value = Settings.knx_GA_addr[i]; - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_KNX "GA #%d: %s " D_TO " %d/%d/%d"), + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX "GA #%d: %s " D_TO " %d/%d/%d"), i+1, device_param_ga[Settings.knx_GA_param[i]-1], KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member ); - AddLog(LOG_LEVEL_DEBUG); + } - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_KNX "CB: %d"), + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX "CB: %d"), Settings.knx_CB_registered ); - AddLog(LOG_LEVEL_DEBUG); - for (byte i = 0; i < Settings.knx_CB_registered ; ++i) + for (uint8_t i = 0; i < Settings.knx_CB_registered ; ++i) { KNX_addr.value = Settings.knx_CB_addr[i]; - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_KNX "CB #%d: %d/%d/%d " D_TO " %s"), + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX "CB #%d: %d/%d/%d " D_TO " %s"), i+1, KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member, device_param_cb[Settings.knx_CB_param[i]-1] ); - AddLog(LOG_LEVEL_DEBUG); } } @@ -1055,7 +988,7 @@ void KNX_Save_Settings(void) #endif // USE_WEBSERVER -boolean KnxCommand(void) +bool KnxCommand(void) { char command[CMDSZ]; uint8_t index = XdrvMailbox.index; @@ -1068,7 +1001,7 @@ boolean KnxCommand(void) // XdrvMailbox.payload <- data to send if (!(Settings.flag.knx_enabled)) { return false; } // Search all the registered GA that has that output (variable: KNX SLOTx) as parameter - byte i = KNX_GA_Search(index + KNX_SLOT1 -1); + uint8_t i = KNX_GA_Search(index + KNX_SLOT1 -1); while ( i != KNX_Empty ) { KNX_addr.value = Settings.knx_GA_addr[i]; knx.write_1bit(KNX_addr, !(XdrvMailbox.payload == 0)); @@ -1077,10 +1010,9 @@ boolean KnxCommand(void) knx.write_1bit(KNX_addr, !(XdrvMailbox.payload == 0)); } - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_KNX "%s = %d " D_SENT_TO " %d.%d.%d"), + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s = %d " D_SENT_TO " %d.%d.%d"), device_param_ga[index + KNX_SLOT1 -2], !(XdrvMailbox.payload == 0), KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member); - AddLog(LOG_LEVEL_INFO); i = KNX_GA_Search(index + KNX_SLOT1 -1, i + 1); } @@ -1093,7 +1025,7 @@ boolean KnxCommand(void) // XdrvMailbox.payload <- data to send if (!(Settings.flag.knx_enabled)) { return false; } // Search all the registered GA that has that output (variable: KNX SLOTx) as parameter - byte i = KNX_GA_Search(index + KNX_SLOT1 -1); + uint8_t i = KNX_GA_Search(index + KNX_SLOT1 -1); while ( i != KNX_Empty ) { KNX_addr.value = Settings.knx_GA_addr[i]; @@ -1106,10 +1038,9 @@ boolean KnxCommand(void) knx.write_2byte_float(KNX_addr, tempvar); } - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_KNX "%s = %s " D_SENT_TO " %d.%d.%d"), + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s = %s " D_SENT_TO " %d.%d.%d"), device_param_ga[index + KNX_SLOT1 -2], XdrvMailbox.data, KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member); - AddLog(LOG_LEVEL_INFO); i = KNX_GA_Search(index + KNX_SLOT1 -1, i + 1); } @@ -1288,9 +1219,9 @@ boolean KnxCommand(void) * Interface \*********************************************************************************************/ -boolean Xdrv11(byte function) +bool Xdrv11(uint8_t function) { - boolean result = false; + bool result = false; switch (function) { case FUNC_PRE_INIT: KNX_INIT(); @@ -1298,7 +1229,7 @@ boolean Xdrv11(byte function) #ifdef USE_WEBSERVER #ifdef USE_KNX_WEB_MENU case FUNC_WEB_ADD_BUTTON: - strncat_P(mqtt_data, HTTP_BTN_MENU_KNX, sizeof(mqtt_data) - strlen(mqtt_data) -1); + WSContentSend_P(HTTP_BTN_MENU_KNX); break; case FUNC_WEB_ADD_HANDLER: WebServer->on("/kn", HandleKNXConfiguration); diff --git a/sonoff/xdrv_12_home_assistant.ino b/sonoff/xdrv_12_home_assistant.ino index 350fe1ca6..5998b3585 100644 --- a/sonoff/xdrv_12_home_assistant.ino +++ b/sonoff/xdrv_12_home_assistant.ino @@ -1,7 +1,7 @@ /* xdrv_12_home_assistant.ino - home assistant support for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends + Copyright (C) 2019 Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -67,7 +67,7 @@ const char HASS_DISCOVER_LIGHT_WHITE[] PROGMEM = "%s,\"whit_val_cmd_t\":\"%s\"," // cmnd/led2/White "\"whit_val_stat_t\":\"%s\"," // stat/led2/RESULT "\"white_value_scale\":100," // (No abbreviation defined) - "\"whit_val_tpl\":\"{{ value_json.Channel[3] }}\""; + "\"whit_val_tpl\":\"{{value_json.Channel[3]}}\""; const char HASS_DISCOVER_LIGHT_CT[] PROGMEM = "%s,\"clr_temp_cmd_t\":\"%s\"," // cmnd/led2/CT @@ -122,17 +122,30 @@ const char HASS_DISCOVER_SENSOR_ANY[] PROGMEM = "%s,\"unit_of_meas\":\" \"," // " " As unit of measurement to get a value graph in Hass "\"val_tpl\":\"{{value_json['%s'].%s}}\""; // "COUNTER":{"C1":0} -> {{ value_json['COUNTER'].C1 }} +const char HASS_DISCOVER_SENSOR_HASS_STATUS[] PROGMEM = + "%s,\"json_attributes_topic\":\"%s\"," + "\"unit_of_meas\":\" \"," // " " As unit of measurement to get a value graph in Hass + "\"val_tpl\":\"{{value_json['" D_JSON_RSSI "']}}\"";// "COUNTER":{"C1":0} -> {{ value_json['COUNTER'].C1 }} + const char HASS_DISCOVER_DEVICE_INFO[] PROGMEM = "%s,\"uniq_id\":\"%s\"," "\"device\":{\"identifiers\":[\"%06X\"]," "\"name\":\"%s\"," "\"model\":\"%s\"," "\"sw_version\":\"%s%s\"," - "\"manufacturer\":\"%s\"}"; + "\"manufacturer\":\"Tasmota\"}"; + +const char HASS_DISCOVER_DEVICE_INFO_SHORT[] PROGMEM = + "%s,\"uniq_id\":\"%s\"," + "\"device\":{\"identifiers\":[\"%06X\"]}"; const char HASS_DISCOVER_TOPIC_PREFIX[] PROGMEM = "%s, \"~\":\"%s\""; +uint8_t hass_init_step = 0; +uint8_t hass_mode = 0; +int hass_tele_period = 0; + static void FindPrefix(char* s1, char* s2, char* out) { int prefixlen = 0; @@ -147,12 +160,31 @@ static void Shorten(char** s, char *prefix) { size_t len = strlen(*s); size_t prefixlen = strlen(prefix); - if (len > prefixlen && !strncmp(*s, prefix, prefixlen)) { + if (len > prefixlen && prefixlen != 0 && !strncmp(*s, prefix, prefixlen)) { *s += prefixlen-1; *s[0] = '~'; } } +int try_snprintf_P(char *s, size_t n, const char *format, ... ) +{ + va_list args; + va_start(args, format); + char dummy[2]; + int len = vsnprintf_P(dummy, 1, format, args); + if (len >= n) { + AddLog_P2(LOG_LEVEL_ERROR, PSTR("ERROR: MQTT discovery failed due to too long topic or friendly name. " + "Please shorten topic and friendly name. Failed to format(%u/%u):"), len, n); + va_start(args, format); + vsnprintf_P(log_data, sizeof(log_data), format, args); + AddLog(LOG_LEVEL_ERROR); + } else { + va_start(args, format); + vsnprintf_P(s, n, format, args); + } + va_end(args); +} + void HAssAnnounceRelayLight(void) { char stopic[TOPSZ]; @@ -169,16 +201,18 @@ void HAssAnnounceRelayLight(void) mqtt_data[0] = '\0'; // Clear retained message - // Clear "other" topic first in case the device has been reconfigured from ligth to switch or vice versa + // Clear "other" topic first in case the device has been reconfigured from light to switch or vice versa snprintf_P(unique_id, sizeof(unique_id), PSTR("%06X_%s_%d"), ESP.getChipId(), (is_topic_light) ? "RL" : "LI", i); - snprintf_P(stopic, sizeof(stopic), PSTR(HOME_ASSISTANT_DISCOVERY_PREFIX "/%s/%s/config"), (is_topic_light) ? "switch" : "light", unique_id); + snprintf_P(stopic, sizeof(stopic), PSTR(HOME_ASSISTANT_DISCOVERY_PREFIX "/%s/%s/config"), + (is_topic_light) ? "switch" : "light", unique_id); MqttPublish(stopic, true); // Clear or Set topic snprintf_P(unique_id, sizeof(unique_id), PSTR("%06X_%s_%d"), ESP.getChipId(), (is_topic_light) ? "LI" : "RL", i); - snprintf_P(stopic, sizeof(stopic), PSTR(HOME_ASSISTANT_DISCOVERY_PREFIX "/%s/%s/config"), (is_topic_light) ? "light" : "switch", unique_id); + snprintf_P(stopic, sizeof(stopic), PSTR(HOME_ASSISTANT_DISCOVERY_PREFIX "/%s/%s/config"), + (is_topic_light) ? "light" : "switch", unique_id); if (Settings.flag.hass_discovery && (i <= devices_present)) { - char name[33]; + char name[33+2]; // friendlyname(33) + " " + index char value_template[33]; char prefix[TOPSZ]; char *command_topic = stemp1; @@ -199,27 +233,31 @@ void HAssAnnounceRelayLight(void) Shorten(&command_topic, prefix); Shorten(&state_topic, prefix); Shorten(&availability_topic, prefix); - snprintf_P(mqtt_data, sizeof(mqtt_data), HASS_DISCOVER_RELAY, + + try_snprintf_P(mqtt_data, sizeof(mqtt_data)-1, HASS_DISCOVER_RELAY, name, command_topic, state_topic, value_template, Settings.state_text[0], Settings.state_text[1], availability_topic); + try_snprintf_P(mqtt_data, sizeof(mqtt_data)-1, HASS_DISCOVER_DEVICE_INFO_SHORT, mqtt_data, + unique_id, ESP.getChipId()); + try_snprintf_P(mqtt_data, sizeof(mqtt_data)-1, HASS_DISCOVER_TOPIC_PREFIX, mqtt_data, prefix); if (is_light) { char *brightness_command_topic = stemp1; GetTopic_P(brightness_command_topic, CMND, mqtt_topic, D_CMND_DIMMER); Shorten(&brightness_command_topic, prefix); - snprintf_P(mqtt_data, sizeof(mqtt_data), HASS_DISCOVER_LIGHT_DIMMER, mqtt_data, brightness_command_topic, state_topic); + try_snprintf_P(mqtt_data, sizeof(mqtt_data)-1, HASS_DISCOVER_LIGHT_DIMMER, mqtt_data, brightness_command_topic, state_topic); if (light_subtype >= LST_RGB) { char *rgb_command_topic = stemp1; GetTopic_P(rgb_command_topic, CMND, mqtt_topic, D_CMND_COLOR); Shorten(&rgb_command_topic, prefix); - snprintf_P(mqtt_data, sizeof(mqtt_data), HASS_DISCOVER_LIGHT_COLOR, mqtt_data, rgb_command_topic, state_topic); + try_snprintf_P(mqtt_data, sizeof(mqtt_data)-1, HASS_DISCOVER_LIGHT_COLOR, mqtt_data, rgb_command_topic, state_topic); char *effect_command_topic = stemp1; GetTopic_P(effect_command_topic, CMND, mqtt_topic, D_CMND_SCHEME); Shorten(&effect_command_topic, prefix); - snprintf_P(mqtt_data, sizeof(mqtt_data), HASS_DISCOVER_LIGHT_SCHEME, mqtt_data, effect_command_topic, state_topic); + try_snprintf_P(mqtt_data, sizeof(mqtt_data)-1, HASS_DISCOVER_LIGHT_SCHEME, mqtt_data, effect_command_topic, state_topic); } if (LST_RGBW == light_subtype) { @@ -227,28 +265,23 @@ void HAssAnnounceRelayLight(void) GetTopic_P(white_temp_command_topic, CMND, mqtt_topic, D_CMND_WHITE); Shorten(&white_temp_command_topic, prefix); - snprintf_P(mqtt_data, sizeof(mqtt_data), HASS_DISCOVER_LIGHT_WHITE, mqtt_data, white_temp_command_topic, state_topic); + try_snprintf_P(mqtt_data, sizeof(mqtt_data)-1, HASS_DISCOVER_LIGHT_WHITE, mqtt_data, white_temp_command_topic, state_topic); } if ((LST_COLDWARM == light_subtype) || (LST_RGBWC == light_subtype)) { char *color_temp_command_topic = stemp1; GetTopic_P(color_temp_command_topic, CMND, mqtt_topic, D_CMND_COLORTEMPERATURE); Shorten(&color_temp_command_topic, prefix); - snprintf_P(mqtt_data, sizeof(mqtt_data), HASS_DISCOVER_LIGHT_CT, mqtt_data, color_temp_command_topic, state_topic); + try_snprintf_P(mqtt_data, sizeof(mqtt_data)-1, HASS_DISCOVER_LIGHT_CT, mqtt_data, color_temp_command_topic, state_topic); } } - snprintf_P(stemp1, sizeof(stemp1), kModules[Settings.module].name); - snprintf_P(mqtt_data, sizeof(mqtt_data), HASS_DISCOVER_DEVICE_INFO, mqtt_data, - unique_id, ESP.getChipId(), - Settings.friendlyname[0], stemp1, my_version, my_image, "Tasmota"); - snprintf_P(mqtt_data, sizeof(mqtt_data), HASS_DISCOVER_TOPIC_PREFIX, mqtt_data, prefix); - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s}"), mqtt_data); + try_snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s}"), mqtt_data); } MqttPublish(stopic, true); } } -void HAssAnnounceButtonSwitch(byte device, char* topic, byte present, byte key, byte toggle) +void HAssAnnounceButtonSwitch(uint8_t device, char* topic, uint8_t present, uint8_t key, uint8_t toggle) { // key 0 = button // key 1 = switch @@ -264,7 +297,7 @@ void HAssAnnounceButtonSwitch(byte device, char* topic, byte present, byte key, snprintf_P(stopic, sizeof(stopic), PSTR(HOME_ASSISTANT_DISCOVERY_PREFIX "/binary_sensor/%s/config"), unique_id); if (Settings.flag.hass_discovery && present) { - char name[33]; + char name[33+6]; // friendlyname(33) + " " + "BTN" + " " + index char value_template[33]; char prefix[TOPSZ]; char *state_topic = stemp1; @@ -282,17 +315,15 @@ void HAssAnnounceButtonSwitch(byte device, char* topic, byte present, byte key, FindPrefix(state_topic, availability_topic, prefix); Shorten(&state_topic, prefix); Shorten(&availability_topic, prefix); - snprintf_P(mqtt_data, sizeof(mqtt_data), HASS_DISCOVER_BUTTON_SWITCH, + try_snprintf_P(mqtt_data, sizeof(mqtt_data)-1, HASS_DISCOVER_BUTTON_SWITCH, name, state_topic, Settings.state_text[toggle?2:1], availability_topic); - if (toggle) snprintf_P(mqtt_data, sizeof(mqtt_data), HASS_DISCOVER_BUTTON_SWITCH_TOGGLE, mqtt_data); - else snprintf_P(mqtt_data, sizeof(mqtt_data), HASS_DISCOVER_BUTTON_SWITCH_ONOFF, mqtt_data, Settings.state_text[0]); + try_snprintf_P(mqtt_data, sizeof(mqtt_data)-1, HASS_DISCOVER_DEVICE_INFO_SHORT, mqtt_data, + unique_id, ESP.getChipId()); + if (strlen(prefix) > 0 ) try_snprintf_P(mqtt_data, sizeof(mqtt_data)-1, HASS_DISCOVER_TOPIC_PREFIX, mqtt_data, prefix); + if (toggle) try_snprintf_P(mqtt_data, sizeof(mqtt_data)-1, HASS_DISCOVER_BUTTON_SWITCH_TOGGLE, mqtt_data); + else try_snprintf_P(mqtt_data, sizeof(mqtt_data)-1, HASS_DISCOVER_BUTTON_SWITCH_ONOFF, mqtt_data, Settings.state_text[0]); - snprintf_P(stemp1, sizeof(stemp1), kModules[Settings.module].name); - snprintf_P(mqtt_data, sizeof(mqtt_data), HASS_DISCOVER_DEVICE_INFO, mqtt_data, - unique_id, ESP.getChipId(), - Settings.friendlyname[0], stemp1, my_version, my_image, "Tasmota"); - snprintf_P(mqtt_data, sizeof(mqtt_data), HASS_DISCOVER_TOPIC_PREFIX, mqtt_data, prefix); - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s}"), mqtt_data); + try_snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s}"), mqtt_data); } MqttPublish(stopic, true); } @@ -305,11 +336,11 @@ void HAssAnnounceSwitches(void) char *tmp = Settings.switch_topic; Format(sw_topic, tmp, sizeof(sw_topic)); if ((strlen(sw_topic) != 0) && strcmp(sw_topic, "0")) { - for (byte switch_index = 0; switch_index < MAX_SWITCHES; switch_index++) { - byte switch_present = 0; - byte toggle = 1; + for (uint8_t switch_index = 0; switch_index < MAX_SWITCHES; switch_index++) { + uint8_t switch_present = 0; + uint8_t toggle = 1; - if ((pin[GPIO_SWT1 + switch_index] < 99) || (pin[GPIO_SWT1_NP + switch_index] < 99)) { + if (pin[GPIO_SWT1 + switch_index] < 99) { switch_present = 1; } @@ -334,14 +365,14 @@ void HAssAnnounceButtons(void) char *tmp = Settings.button_topic; Format(key_topic, tmp, sizeof(key_topic)); if ((strlen(key_topic) != 0) && strcmp(key_topic, "0")) { - for (byte button_index = 0; button_index < MAX_KEYS; button_index++) { - byte button_present = 0; - byte toggle = 1; + for (uint8_t button_index = 0; button_index < MAX_KEYS; button_index++) { + uint8_t button_present = 0; + uint8_t toggle = 1; - if (!button_index && ((SONOFF_DUAL == Settings.module) || (CH4 == Settings.module))) { + if (!button_index && ((SONOFF_DUAL == my_module_type) || (CH4 == my_module_type))) { button_present = 1; } else { - if ((pin[GPIO_KEY1 + button_index] < 99) || (pin[GPIO_KEY1_NP + button_index] < 99)) { + if (pin[GPIO_KEY1 + button_index] < 99) { button_present = 1; } } @@ -373,7 +404,7 @@ void HAssAnnounceSensor(const char* sensorname, const char* subsensortype) snprintf_P(stopic, sizeof(stopic), PSTR(HOME_ASSISTANT_DISCOVERY_PREFIX "/sensor/%s/config"), unique_id); if (Settings.flag.hass_discovery) { - char name[33]; + char name[33+42]; // friendlyname(33) + " " + sensorname(20?) + " " + sensortype(20?) char prefix[TOPSZ]; char *state_topic = stemp1; char *availability_topic = stemp2; @@ -385,40 +416,40 @@ void HAssAnnounceSensor(const char* sensorname, const char* subsensortype) Shorten(&state_topic, prefix); Shorten(&availability_topic, prefix); - snprintf_P(mqtt_data, sizeof(mqtt_data), HASS_DISCOVER_SENSOR, + try_snprintf_P(mqtt_data, sizeof(mqtt_data)-1, HASS_DISCOVER_SENSOR, name, state_topic, availability_topic); + try_snprintf_P(mqtt_data, sizeof(mqtt_data)-1, HASS_DISCOVER_DEVICE_INFO_SHORT, mqtt_data, + unique_id, ESP.getChipId()); + try_snprintf_P(mqtt_data, sizeof(mqtt_data)-1, HASS_DISCOVER_TOPIC_PREFIX, mqtt_data, prefix); if (!strcmp_P(subsensortype, PSTR(D_JSON_TEMPERATURE))) { - snprintf_P(mqtt_data, sizeof(mqtt_data), HASS_DISCOVER_SENSOR_TEMP, + try_snprintf_P(mqtt_data, sizeof(mqtt_data)-1, HASS_DISCOVER_SENSOR_TEMP, mqtt_data, TempUnit(), sensorname); } else if (!strcmp_P(subsensortype, PSTR(D_JSON_HUMIDITY))) { - snprintf_P(mqtt_data, sizeof(mqtt_data), HASS_DISCOVER_SENSOR_HUM, + try_snprintf_P(mqtt_data, sizeof(mqtt_data)-1, HASS_DISCOVER_SENSOR_HUM, mqtt_data, sensorname); } else if (!strcmp_P(subsensortype, PSTR(D_JSON_PRESSURE))) { - snprintf_P(mqtt_data, sizeof(mqtt_data), HASS_DISCOVER_SENSOR_PRESS, + try_snprintf_P(mqtt_data, sizeof(mqtt_data)-1, HASS_DISCOVER_SENSOR_PRESS, mqtt_data, PressureUnit().c_str(), sensorname); - } else if (!strcmp_P(subsensortype, PSTR(D_JSON_TOTAL)) || !strcmp_P(subsensortype, PSTR(D_JSON_TODAY)) || !strcmp_P(subsensortype, PSTR(D_JSON_YESTERDAY))){ - snprintf_P(mqtt_data, sizeof(mqtt_data), HASS_DISCOVER_SENSOR_KWH, + } else if (!strcmp_P(subsensortype, PSTR(D_JSON_TOTAL)) + || !strcmp_P(subsensortype, PSTR(D_JSON_TODAY)) + || !strcmp_P(subsensortype, PSTR(D_JSON_YESTERDAY))){ + try_snprintf_P(mqtt_data, sizeof(mqtt_data)-1, HASS_DISCOVER_SENSOR_KWH, mqtt_data, sensorname, subsensortype); } else if (!strcmp_P(subsensortype, PSTR(D_JSON_POWERUSAGE))){ - snprintf_P(mqtt_data, sizeof(mqtt_data), HASS_DISCOVER_SENSOR_WATT, + try_snprintf_P(mqtt_data, sizeof(mqtt_data)-1, HASS_DISCOVER_SENSOR_WATT, mqtt_data, sensorname, subsensortype); } else if (!strcmp_P(subsensortype, PSTR(D_JSON_VOLTAGE))){ - snprintf_P(mqtt_data, sizeof(mqtt_data), HASS_DISCOVER_SENSOR_VOLTAGE, + try_snprintf_P(mqtt_data, sizeof(mqtt_data)-1, HASS_DISCOVER_SENSOR_VOLTAGE, mqtt_data, sensorname, subsensortype); } else if (!strcmp_P(subsensortype, PSTR(D_JSON_CURRENT))){ - snprintf_P(mqtt_data, sizeof(mqtt_data), HASS_DISCOVER_SENSOR_AMPERE, + try_snprintf_P(mqtt_data, sizeof(mqtt_data)-1, HASS_DISCOVER_SENSOR_AMPERE, mqtt_data, sensorname, subsensortype); } else { - snprintf_P(mqtt_data, sizeof(mqtt_data), HASS_DISCOVER_SENSOR_ANY, + try_snprintf_P(mqtt_data, sizeof(mqtt_data)-1, HASS_DISCOVER_SENSOR_ANY, mqtt_data, sensorname, subsensortype); } - snprintf_P(stemp1, sizeof(stemp1), kModules[Settings.module].name); - snprintf_P(mqtt_data, sizeof(mqtt_data), HASS_DISCOVER_DEVICE_INFO, mqtt_data, - unique_id, ESP.getChipId(), - Settings.friendlyname[0], stemp1, my_version, my_image, "Tasmota"); - snprintf_P(mqtt_data, sizeof(mqtt_data), HASS_DISCOVER_TOPIC_PREFIX, mqtt_data, prefix); - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s}"), mqtt_data); + try_snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s}"), mqtt_data); } MqttPublish(stopic, true); } @@ -426,6 +457,7 @@ void HAssAnnounceSensor(const char* sensorname, const char* subsensortype) void HAssAnnounceSensors(void) { uint8_t hass_xsns_index = 0; + do { mqtt_data[0] = '\0'; int tele_period_save = tele_period; @@ -433,26 +465,28 @@ void HAssAnnounceSensors(void) XsnsNextCall(FUNC_JSON_APPEND, hass_xsns_index); // ,"INA219":{"Voltage":4.494,"Current":0.020,"Power":0.089} tele_period = tele_period_save; - char sensordata[256]; // Copy because we need to write to mqtt_data + char sensordata[256]; // Copy because we need to write to mqtt_data strlcpy(sensordata, mqtt_data, sizeof(sensordata)); if (strlen(sensordata)) { sensordata[0] = '{'; // {"INA219":{"Voltage":4.494,"Current":0.020,"Power":0.089} - snprintf_P(sensordata, sizeof(sensordata), PSTR("%s}"), sensordata); + snprintf_P(sensordata, sizeof(sensordata), PSTR("%s}"), sensordata); // {"INA219":{"Voltage":4.494,"Current":0.020,"Power":0.089}} - StaticJsonBuffer<256> jsonBuffer; + // JsonBuffer size calculation (https://arduinojson.org/v5/assistant/) + // 383 = {"MCP230XX":{"D0":0,"D1":0,"D2":0,"D3":0,"D4":0,"D5":0,"D6":0,"D7":0,"D8":0,"D9":0,"D10":0,"D11":0,"D12":0,"D13":0,"D14":0,"D15":0}} + // 381 = {"MPR121A":{"Button0":0,"Button1":0,"Button2":0,"Button3":0,"Button4":0,"Button5":0,"Button6":0,"Button7":0,"Button8":0,"Button9":0,"Button10":0,"Button11":0,"Button12":0}} + // 420 = {"ENERGY":{"TotalStartTime":"2018-10-30T17:09:47","Total":2.684,"Yesterday":0.020,"Today":0.006,"Period":0.04,"Power":0.49,"ApparentPower":4.71,"ReactivePower":4.70,"Factor":0.10,"Frequency":50.04,"Voltage":226.3,"Current":0.021}} + StaticJsonBuffer<500> jsonBuffer; JsonObject& root = jsonBuffer.parseObject(sensordata); if (!root.success()) { - snprintf_P(log_data, sizeof(log_data), PSTR("HASS: failed to parse '%s'"), sensordata); - AddLog(LOG_LEVEL_ERROR); + AddLog_P2(LOG_LEVEL_ERROR, PSTR("HASS: failed to parse '%s'"), sensordata); continue; } for (auto sensor : root) { const char* sensorname = sensor.key; JsonObject& sensors = sensor.value.as(); if (!sensors.success()) { - snprintf_P(log_data, sizeof(log_data), PSTR("HASS: failed to parse '%s'"), sensordata); - AddLog(LOG_LEVEL_ERROR); + AddLog_P2(LOG_LEVEL_ERROR, PSTR("HASS: failed to parse '%s'"), sensordata); continue; } for (auto subsensor : sensors) { @@ -460,32 +494,82 @@ void HAssAnnounceSensors(void) } } } + yield(); } while (hass_xsns_index != 0); } -static int string_ends_with(const char * str, const char * suffix) +void HAssAnnounceStatusSensor(void) { - int str_len = strlen(str); - int suffix_len = strlen(suffix); + char stopic[TOPSZ]; + char stemp1[TOPSZ]; + char stemp2[TOPSZ]; + char unique_id[30]; - return (str_len >= suffix_len) && (0 == strcmp(str + (str_len-suffix_len), suffix)); + // Announce sensor + mqtt_data[0] = '\0'; // Clear retained message + + // Clear or Set topic + snprintf_P(unique_id, sizeof(unique_id), PSTR("%06X_%s"), ESP.getChipId(), "status"); + snprintf_P(stopic, sizeof(stopic), PSTR(HOME_ASSISTANT_DISCOVERY_PREFIX "/sensor/%s/config"), unique_id); + + if (Settings.flag.hass_discovery) { + char name[33+7]; // friendlyname(33) + " " + "status" + char prefix[TOPSZ]; + char *state_topic = stemp1; + char *availability_topic = stemp2; + + snprintf_P(name, sizeof(name), PSTR("%s %s"), Settings.friendlyname[0], "status"); + GetTopic_P(state_topic, TELE, mqtt_topic, PSTR(D_RSLT_HASS_STATE)); + GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT); + FindPrefix(state_topic, availability_topic, prefix); + Shorten(&state_topic, prefix); + Shorten(&availability_topic, prefix); + + try_snprintf_P(mqtt_data, sizeof(mqtt_data)-1, HASS_DISCOVER_SENSOR, + name, state_topic, availability_topic); + try_snprintf_P(mqtt_data, sizeof(mqtt_data)-1, HASS_DISCOVER_SENSOR_HASS_STATUS, + mqtt_data, state_topic); + try_snprintf_P(mqtt_data, sizeof(mqtt_data)-1, HASS_DISCOVER_DEVICE_INFO, mqtt_data, + unique_id, ESP.getChipId(), + Settings.friendlyname[0], ModuleName().c_str(), my_version, my_image); + try_snprintf_P(mqtt_data, sizeof(mqtt_data)-1, HASS_DISCOVER_TOPIC_PREFIX, mqtt_data, prefix); + try_snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s}"), mqtt_data); + } + MqttPublish(stopic, true); } -void HAssDiscovery(uint8_t mode) +void HAssPublishStatus(void) +{ + snprintf_P(mqtt_data, sizeof(mqtt_data), + PSTR("{\"" D_JSON_VERSION "\":\"%s%s\",\"" D_JSON_BUILDDATETIME "\":\"%s\"," + "\"" D_JSON_COREVERSION "\":\"" ARDUINO_ESP8266_RELEASE "\",\"" D_JSON_SDKVERSION "\":\"%s\"," + "\"" D_CMND_MODULE "\":\"%s\",\"" D_JSON_RESTARTREASON "\":\"%s\",\"" D_JSON_UPTIME "\":\"%s\"," + "\"WiFi " D_JSON_LINK_COUNT "\":%d,\"WiFi " D_JSON_DOWNTIME "\":\"%s\",\"" D_JSON_MQTT_COUNT "\":%d," + "\"" D_JSON_BOOTCOUNT "\":%d,\"" D_JSON_SAVECOUNT "\":%d,\"" D_CMND_IPADDRESS "\":\"%s\"," + "\"" D_JSON_RSSI "\":\"%d\",\"LoadAvg\":%lu}"), + my_version, my_image, GetBuildDateAndTime().c_str(), ESP.getSdkVersion(), ModuleName().c_str(), + GetResetReason().c_str(), GetUptime().c_str(), WifiLinkCount(), WifiDowntime().c_str(), MqttConnectCount(), + Settings.bootcount, Settings.save_flag, WiFi.localIP().toString().c_str(), + WifiGetRssiAsQuality(WiFi.RSSI()), loop_load_avg); + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_HASS_STATE)); +} + +void HAssDiscovery(void) { // Configure Tasmota for default Home Assistant parameters to keep discovery message as short as possible if (Settings.flag.hass_discovery) { - Settings.flag.mqtt_response = 0; // Response always as RESULT and not as uppercase command - Settings.flag.decimal_text = 1; // Respond with decimal color values - Settings.flag3.hass_tele_on_power = 1; // send tele/STATE message as stat/RESULT -// Settings.light_scheme = 0; // To just control color it needs to be Scheme 0 - if (!string_ends_with(Settings.mqtt_fulltopic, "%prefix%/")) { + Settings.flag.mqtt_response = 0; // Response always as RESULT and not as uppercase command + Settings.flag.decimal_text = 1; // Respond with decimal color values + Settings.flag3.hass_tele_on_power = 1; // send tele/STATE message as stat/RESULT +// Settings.light_scheme = 0; // To just control color it needs to be Scheme 0 + if (strcmp_P(Settings.mqtt_fulltopic, PSTR("%topic%/%prefix%/"))) { strncpy_P(Settings.mqtt_fulltopic, PSTR("%topic%/%prefix%/"), sizeof(Settings.mqtt_fulltopic)); restart_flag = 2; + return; // As full topic has changed do restart first before sending discovery data } } - if (Settings.flag.hass_discovery || (1 == mode)) { + if (Settings.flag.hass_discovery || (1 == hass_mode)) { // Send info about relays and lights HAssAnnounceRelayLight(); @@ -497,71 +581,48 @@ void HAssDiscovery(uint8_t mode) // Send info about sensors HAssAnnounceSensors(); + + // Send info about status sensor + HAssAnnounceStatusSensor(); } } -/* -#define D_CMND_HASSDISCOVER "HassDiscover" - -enum HassCommands { CMND_HASSDISCOVER }; -const char kHassCommands[] PROGMEM = D_CMND_HASSDISCOVER ; - -boolean HassCommand(void) +void HAssDiscover(void) { - char command[CMDSZ]; - boolean serviced = true; - - int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic, kHassCommands); - if (-1 == command_code) { - serviced = false; // Unknown command - } - else if (CMND_HASSDISCOVER == command_code) { - if (XdrvMailbox.data_len > 0) { - switch (XdrvMailbox.payload) { - case 0: // Off - case 1: // On - Settings.flag.hass_discovery = XdrvMailbox.payload; - break; - case 2: // Toggle - Settings.flag.hass_discovery ^= 1; - break; - case 4: // Off - case 5: // On - Settings.flag.hass_light = XdrvMailbox.payload &1; - break; - case 6: // Toggle - Settings.flag.hass_light ^= 1; - break; - } - HAssDiscovery(1); - } - snprintf_P (mqtt_data, sizeof(mqtt_data), PSTR("{\"%s\":\"%s\",\"Force light\":\"%s\"}"), - command, GetStateText(Settings.flag.hass_discovery), GetStateText(Settings.flag.hass_light)); - } - else serviced = false; // Unknown command - - return serviced; + hass_mode = 1; // Force discovery + hass_init_step = 1; // Delayed discovery } -*/ /*********************************************************************************************\ * Interface \*********************************************************************************************/ -boolean Xdrv12(byte function) +bool Xdrv12(uint8_t function) { - boolean result = false; + bool result = false; if (Settings.flag.mqtt_enabled) { switch (function) { case FUNC_MQTT_INIT: - HAssDiscovery(0); + hass_mode = 0; // Discovery only if Settings.flag.hass_discovery is set + hass_init_step = 2; // Delayed discovery break; -/* - case FUNC_COMMAND: - result = HassCommand(); + case FUNC_EVERY_SECOND: + if (hass_init_step) { + hass_init_step--; + if (!hass_init_step) { + HAssDiscovery(); // Scheduled discovery using available resources + } + } else if (Settings.flag.hass_discovery && Settings.tele_period) { + hass_tele_period++; + if (hass_tele_period >= Settings.tele_period) { + hass_tele_period = 0; + + mqtt_data[0] = '\0'; + HAssPublishStatus(); + } + } break; -*/ } } return result; diff --git a/sonoff/xdrv_13_display.ino b/sonoff/xdrv_13_display.ino index f585f842c..f14680939 100644 --- a/sonoff/xdrv_13_display.ino +++ b/sonoff/xdrv_13_display.ino @@ -1,7 +1,7 @@ /* xdrv_13_display.ino - Display support for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends + Copyright (C) 2019 Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -427,13 +427,18 @@ void DisplayText(void) cp += var; DisplayDrawFilledRectangle(disp_xpos, disp_ypos, temp, temp1, color); break; - case 't': { + case 't': if (dp < (linebuf + DISPLAY_BUFFER_COLS) -5) { - snprintf_P(dp, 5, PSTR("%02d" D_HOUR_MINUTE_SEPARATOR "%02d"), RtcTime.hour, RtcTime.minute); + snprintf_P(dp, 6, PSTR("%02d" D_HOUR_MINUTE_SEPARATOR "%02d"), RtcTime.hour, RtcTime.minute); dp += 5; } break; - } + case 'T': + if (dp < (linebuf + DISPLAY_BUFFER_COLS) -8) { + snprintf_P(dp, 9, PSTR("%02d" D_MONTH_DAY_SEPARATOR "%02d" D_YEAR_MONTH_SEPARATOR "%02d"), RtcTime.day_of_month, RtcTime.month, RtcTime.year%2000); + dp += 8; + } + break; case 'd': // force draw grafics buffer DisplayDrawFrame(); @@ -490,7 +495,7 @@ void DisplayText(void) void DisplayClearScreenBuffer(void) { if (disp_screen_buffer_cols) { - for (byte i = 0; i < disp_screen_buffer_rows; i++) { + for (uint8_t i = 0; i < disp_screen_buffer_rows; i++) { memset(disp_screen_buffer[i], 0, disp_screen_buffer_cols); } } @@ -499,7 +504,7 @@ void DisplayClearScreenBuffer(void) void DisplayFreeScreenBuffer(void) { if (disp_screen_buffer != NULL) { - for (byte i = 0; i < disp_screen_buffer_rows; i++) { + for (uint8_t i = 0; i < disp_screen_buffer_rows; i++) { if (disp_screen_buffer[i] != NULL) { free(disp_screen_buffer[i]); } } free(disp_screen_buffer); @@ -514,7 +519,7 @@ void DisplayAllocScreenBuffer(void) disp_screen_buffer_rows = Settings.display_rows; disp_screen_buffer = (char**)malloc(sizeof(*disp_screen_buffer) * disp_screen_buffer_rows); if (disp_screen_buffer != NULL) { - for (byte i = 0; i < disp_screen_buffer_rows; i++) { + for (uint8_t i = 0; i < disp_screen_buffer_rows; i++) { disp_screen_buffer[i] = (char*)malloc(sizeof(*disp_screen_buffer[i]) * (Settings.display_cols[0] +1)); if (disp_screen_buffer[i] == NULL) { DisplayFreeScreenBuffer(); @@ -537,7 +542,7 @@ void DisplayReAllocScreenBuffer(void) void DisplayFillScreen(uint8_t line) { - byte len = disp_screen_buffer_cols - strlen(disp_screen_buffer[line]); + uint8_t len = disp_screen_buffer_cols - strlen(disp_screen_buffer[line]); if (len) { memset(disp_screen_buffer[line] + strlen(disp_screen_buffer[line]), 0x20, len); disp_screen_buffer[line][disp_screen_buffer_cols -1] = 0; @@ -549,7 +554,7 @@ void DisplayFillScreen(uint8_t line) void DisplayClearLogBuffer(void) { if (disp_log_buffer_cols) { - for (byte i = 0; i < DISPLAY_LOG_ROWS; i++) { + for (uint8_t i = 0; i < DISPLAY_LOG_ROWS; i++) { memset(disp_log_buffer[i], 0, disp_log_buffer_cols); } } @@ -558,7 +563,7 @@ void DisplayClearLogBuffer(void) void DisplayFreeLogBuffer(void) { if (disp_log_buffer != NULL) { - for (byte i = 0; i < DISPLAY_LOG_ROWS; i++) { + for (uint8_t i = 0; i < DISPLAY_LOG_ROWS; i++) { if (disp_log_buffer[i] != NULL) { free(disp_log_buffer[i]); } } free(disp_log_buffer); @@ -571,7 +576,7 @@ void DisplayAllocLogBuffer(void) if (!disp_log_buffer_cols) { disp_log_buffer = (char**)malloc(sizeof(*disp_log_buffer) * DISPLAY_LOG_ROWS); if (disp_log_buffer != NULL) { - for (byte i = 0; i < DISPLAY_LOG_ROWS; i++) { + for (uint8_t i = 0; i < DISPLAY_LOG_ROWS; i++) { disp_log_buffer[i] = (char*)malloc(sizeof(*disp_log_buffer[i]) * (Settings.display_cols[0] +1)); if (disp_log_buffer[i] == NULL) { DisplayFreeLogBuffer(); @@ -682,7 +687,7 @@ const char kSensorQuantity[] PROGMEM = D_JSON_CO2 "|" // ppm D_JSON_FREQUENCY ; // Hz -void DisplayJsonValue(const char *topic, const char* device, const char* mkey, const char* value) +void DisplayJsonValue(const char* topic, const char* device, const char* mkey, const char* value) { char quantity[TOPSZ]; char buffer[Settings.display_cols[0] +1]; @@ -690,11 +695,13 @@ void DisplayJsonValue(const char *topic, const char* device, const char* mkey, c char source[Settings.display_cols[0] - Settings.display_cols[1]]; char svalue[Settings.display_cols[1] +1]; +#ifdef USE_DEBUG_DRIVER ShowFreeMem(PSTR("DisplayJsonValue")); +#endif memset(spaces, 0x20, sizeof(spaces)); spaces[sizeof(spaces) -1] = '\0'; - snprintf_P(source, sizeof(source), PSTR("%s/%s%s"), topic, mkey, spaces); // pow1/Voltage + snprintf_P(source, sizeof(source), PSTR("%s%s%s%s"), topic, (strlen(topic))?"/":"", mkey, spaces); // pow1/Voltage or Voltage if topic is empty (local sensor) int quantity_code = GetCommandCode(quantity, sizeof(quantity), mkey, kSensorQuantity); if ((-1 == quantity_code) || !strcmp_P(mkey, S_RSLT_POWER)) { // Ok: Power, Not ok: POWER @@ -741,8 +748,7 @@ void DisplayJsonValue(const char *topic, const char* device, const char* mkey, c } snprintf_P(buffer, sizeof(buffer), PSTR("%s %s"), source, svalue); -// snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_DEBUG "mkey [%s], source [%s], value [%s], quantity_code %d, log_buffer [%s]"), mkey, source, value, quantity_code, buffer); -// AddLog(LOG_LEVEL_DEBUG); +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "mkey [%s], source [%s], value [%s], quantity_code %d, log_buffer [%s]"), mkey, source, value, quantity_code, buffer); DisplayLogBufferAdd(buffer); } @@ -772,8 +778,7 @@ void DisplayAnalyzeJson(char *topic, char *json) tempunit = root[D_JSON_TEMPERATURE_UNIT]; if (tempunit) { snprintf_P(disp_temp, sizeof(disp_temp), PSTR("%s"), tempunit); -// snprintf_P(log_data, sizeof(log_data), disp_temp); -// AddLog(LOG_LEVEL_DEBUG); +// AddLog_P2(LOG_LEVEL_DEBUG, disp_temp); } for (JsonObject::iterator it = root.begin(); it != root.end(); ++it) { @@ -785,14 +790,23 @@ void DisplayAnalyzeJson(char *topic, char *json) if (value2.is()) { JsonObject& Object3 = value2; for (JsonObject::iterator it3 = Object3.begin(); it3 != Object3.end(); ++it3) { - DisplayJsonValue(topic, it->key, it3->key, it3->value.as()); // Sensor 56% + const char* value = it3->value; + if (value != nullptr) { // "DHT11":{"Temperature":null,"Humidity":null} - ignore null as it will raise exception 28 + DisplayJsonValue(topic, it->key, it3->key, value); // Sensor 56% + } } } else { - DisplayJsonValue(topic, it->key, it2->key, it2->value.as()); // Sensor 56% + const char* value = it2->value; + if (value != nullptr) { + DisplayJsonValue(topic, it->key, it2->key, value); // Sensor 56% + } } } } else { - DisplayJsonValue(topic, it->key, it->key, it->value.as()); // Topic 56% + const char* value = it->value; + if (value != nullptr) { + DisplayJsonValue(topic, it->key, it->key, value); // Topic 56% + } } } } @@ -831,7 +845,7 @@ void DisplayMqttSubscribe(void) } } -boolean DisplayMqttData(void) +bool DisplayMqttData(void) { if (disp_subscribed) { char stopic[TOPSZ]; @@ -853,7 +867,9 @@ boolean DisplayMqttData(void) void DisplayLocalSensor(void) { if ((Settings.display_mode &0x02) && (0 == tele_period)) { - DisplayAnalyzeJson(mqtt_topic, mqtt_data); + char no_topic[1] = { 0 }; +// DisplayAnalyzeJson(mqtt_topic, mqtt_data); // Add local topic + DisplayAnalyzeJson(no_topic, mqtt_data); // Discard any topic } } @@ -867,8 +883,7 @@ void DisplayInitDriver(void) { XdspCall(FUNC_DISPLAY_INIT_DRIVER); -// snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_DEBUG "Display model %d"), Settings.display_model); -// AddLog(LOG_LEVEL_DEBUG); +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "Display model %d"), Settings.display_model); if (Settings.display_model) { devices_present++; @@ -894,10 +909,10 @@ void DisplaySetPower(void) * Commands \*********************************************************************************************/ -boolean DisplayCommand(void) +bool DisplayCommand(void) { char command [CMDSZ]; - boolean serviced = true; + bool serviced = true; uint8_t disp_len = strlen(D_CMND_DISPLAY); // Prep for string length change if (!strncasecmp_P(XdrvMailbox.topic, PSTR(D_CMND_DISPLAY), disp_len)) { // Prefix @@ -1062,9 +1077,9 @@ boolean DisplayCommand(void) * Interface \*********************************************************************************************/ -boolean Xdrv13(byte function) +bool Xdrv13(uint8_t function) { - boolean result = false; + bool result = false; if ((i2c_flg || spi_flg || soft_spi_flg) && XdspPresent()) { switch (function) { diff --git a/sonoff/xdrv_14_mp3.ino b/sonoff/xdrv_14_mp3.ino index 973c3fe5e..4e55cddc5 100644 --- a/sonoff/xdrv_14_mp3.ino +++ b/sonoff/xdrv_14_mp3.ino @@ -1,7 +1,7 @@ /* xdrv_14_mp3.ino - MP3 support for Sonoff-Tasmota - Copyright (C) 2018 gemu2015, mike2nl and Theo Arends + Copyright (C) 2019 gemu2015, mike2nl and Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -178,9 +178,9 @@ void MP3_CMD(uint8_t mp3cmd,uint16_t val) { * check the MP3 commands \*********************************************************************************************/ -boolean MP3PlayerCmd(void) { +bool MP3PlayerCmd(void) { char command[CMDSZ]; - boolean serviced = true; + bool serviced = true; uint8_t disp_len = strlen(D_CMND_MP3); if (!strncasecmp_P(XdrvMailbox.topic, PSTR(D_CMND_MP3), disp_len)) { // prefix @@ -226,9 +226,9 @@ boolean MP3PlayerCmd(void) { * Interface \*********************************************************************************************/ -boolean Xdrv14(byte function) +bool Xdrv14(uint8_t function) { - boolean result = false; + bool result = false; if (pin[GPIO_MP3_DFR562] < 99) { switch (function) { diff --git a/sonoff/xdrv_15_pca9685.ino b/sonoff/xdrv_15_pca9685.ino index b29e85a5b..de91a02b4 100644 --- a/sonoff/xdrv_15_pca9685.ino +++ b/sonoff/xdrv_15_pca9685.ino @@ -1,7 +1,7 @@ /* xdrv_15_pca9685.ino - Support for I2C PCA9685 12bit 16 pin hardware PWM driver - Copyright (C) 2018 Andre Thomas and Theo Arends + Copyright (C) 2019 Andre Thomas and Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -45,8 +45,7 @@ void PCA9685_Detect(void) if (I2cValidRead8(&buffer, USE_PCA9685_ADDR, PCA9685_REG_MODE1)) { if (0x20 == buffer) { pca9685_detected = 1; - snprintf_P(log_data, sizeof(log_data), S_LOG_I2C_FOUND_AT, "PCA9685", USE_PCA9685_ADDR); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, S_LOG_I2C_FOUND_AT, "PCA9685", USE_PCA9685_ADDR); PCA9685_Reset(); // Reset the controller } } @@ -101,10 +100,10 @@ void PCA9685_SetPWM(uint8_t pin, uint16_t pwm, bool inverted) { pca9685_pin_pwm_value[pin] = pwm; } -bool PCA9685_Command(void) +bool PCA9685_Command(void) { - boolean serviced = true; - boolean validpin = false; + bool serviced = true; + bool validpin = false; uint8_t paramcount = 0; if (XdrvMailbox.data_len > 0) { paramcount=1; @@ -178,9 +177,9 @@ void PCA9685_OutputTelemetry(bool telemetry) { } } -boolean Xdrv15(byte function) +bool Xdrv15(uint8_t function) { - boolean result = false; + bool result = false; if (i2c_flg) { switch (function) { diff --git a/sonoff/xdrv_16_tuyadimmer.ino b/sonoff/xdrv_16_tuyadimmer.ino index eb564a8e8..53c4984cc 100644 --- a/sonoff/xdrv_16_tuyadimmer.ino +++ b/sonoff/xdrv_16_tuyadimmer.ino @@ -1,7 +1,7 @@ /* xdrv_16_tuyadimmer.ino - Tuya dimmer support for Sonoff-Tasmota - Copyright (C) 2018 digiblur, Joel Stein and Theo Arends + Copyright (C) 2019 digiblur, Joel Stein and Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -47,7 +47,7 @@ TasmotaSerial *TuyaSerial = nullptr; uint8_t tuya_new_dim = 0; // Tuya dimmer value temp -boolean tuya_ignore_dim = false; // Flag to skip serial send to prevent looping when processing inbound states from the faceplate interaction +bool tuya_ignore_dim = false; // Flag to skip serial send to prevent looping when processing inbound states from the faceplate interaction uint8_t tuya_cmd_status = 0; // Current status of serial-read uint8_t tuya_cmd_checksum = 0; // Checksum of tuya command uint8_t tuya_data_len = 0; // Data lenght of command @@ -106,25 +106,24 @@ void TuyaSendState(uint8_t id, uint8_t type, uint8_t* value){ TuyaSendCmd(TUYA_CMD_SET_DP, payload_buffer, payload_len); } -void TuyaSendBool(uint8_t id, boolean value){ - TuyaSendState(id, TUYA_TYPE_BOOL, &value); +void TuyaSendBool(uint8_t id, bool value){ + TuyaSendState(id, TUYA_TYPE_BOOL, (uint8_t*)&value); } void TuyaSendValue(uint8_t id, uint32_t value){ TuyaSendState(id, TUYA_TYPE_VALUE, (uint8_t*)(&value)); } -boolean TuyaSetPower(void) +bool TuyaSetPower(void) { - boolean status = false; + bool status = false; uint8_t rpower = XdrvMailbox.index; int16_t source = XdrvMailbox.payload; if (source != SRC_SWITCH && TuyaSerial) { // ignore to prevent loop from pushing state from faceplate interaction - snprintf_P(log_data, sizeof(log_data), PSTR("TYA: SetDevicePower.rpower=%d"), rpower); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: SetDevicePower.rpower=%d"), rpower); TuyaSendBool(TUYA_POWER_ID, rpower); @@ -133,7 +132,7 @@ boolean TuyaSetPower(void) return status; } -boolean TuyaSetChannels(void) +bool TuyaSetChannels(void) { LightSerialDuty(((uint8_t*)XdrvMailbox.data)[0]); return true; @@ -146,16 +145,14 @@ void LightSerialDuty(uint8_t duty) duty = 25; // dimming acts odd below 25(10%) - this mirrors the threshold set on the faceplate itself } - snprintf_P(log_data, sizeof(log_data), PSTR( "TYA: Send Serial Packet Dim Value=%d (id=%d)"), duty, Settings.param[P_TUYA_DIMMER_ID]); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR( "TYA: Send Serial Packet Dim Value=%d (id=%d)"), duty, Settings.param[P_TUYA_DIMMER_ID]); TuyaSendValue(Settings.param[P_TUYA_DIMMER_ID], duty); } else { tuya_ignore_dim = false; // reset flag - snprintf_P(log_data, sizeof(log_data), PSTR( "TYA: Send Dim Level skipped due to 0 or already set. Value=%d"), duty); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR( "TYA: Send Dim Level skipped due to 0 or already set. Value=%d"), duty); } } @@ -164,8 +161,7 @@ void TuyaRequestState(void){ if(TuyaSerial) { // Get current status of MCU - snprintf_P(log_data, sizeof(log_data), "TYA: Request MCU state"); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Request MCU state")); TuyaSendCmd(TUYA_CMD_QUERY_STATE); } @@ -197,8 +193,7 @@ void TuyaPacketProcess(void) case TUYA_CMD_STATE: if (tuya_buffer[5] == 5) { // on/off packet - snprintf_P(log_data, sizeof(log_data),PSTR("TYA: RX - %s State"),tuya_buffer[10]?"On":"Off"); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: RX - %s State"),tuya_buffer[10]?"On":"Off"); if((power || Settings.light_dimmer > 0) && (power != tuya_buffer[10])) { ExecuteCommandPower(1, tuya_buffer[10], SRC_SWITCH); // send SRC_SWITCH? to use as flag to prevent loop from inbound states from faceplate interaction @@ -206,12 +201,10 @@ void TuyaPacketProcess(void) } else if (tuya_buffer[5] == 8) { // dim packet - snprintf_P(log_data, sizeof(log_data), PSTR("TYA: RX Dim State=%d"), tuya_buffer[13]); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: RX Dim State=%d"), tuya_buffer[13]); if (!Settings.param[P_TUYA_DIMMER_ID]) { - snprintf_P(log_data, sizeof(log_data), PSTR("TYA: Autoconfiguring Dimmer ID %d"), tuya_buffer[6]); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Autoconfiguring Dimmer ID %d"), tuya_buffer[6]); Settings.param[P_TUYA_DIMMER_ID] = tuya_buffer[6]; } @@ -220,8 +213,7 @@ void TuyaPacketProcess(void) snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_DIMMER " %d"), tuya_new_dim ); - snprintf_P(log_data, sizeof(log_data), PSTR("TYA: Send CMND_DIMMER_STR=%s"), scmnd ); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Send CMND_DIMMER_STR=%s"), scmnd ); tuya_ignore_dim = true; ExecuteCommand(scmnd, SRC_SWITCH); @@ -246,9 +238,9 @@ void TuyaPacketProcess(void) if (tuya_buffer[5] == 2) { uint8_t led1_gpio = tuya_buffer[6]; uint8_t key1_gpio = tuya_buffer[7]; - boolean key1_set = false; - boolean led1_set = false; - for (byte i = 0; i < MAX_GPIO_PIN; i++) { + bool key1_set = false; + bool led1_set = false; + for (uint8_t i = 0; i < sizeof(Settings.my_gp); i++) { if (Settings.my_gp.io[i] == GPIO_LED1) led1_set = true; else if (Settings.my_gp.io[i] == GPIO_KEY1) key1_set = true; } @@ -273,7 +265,7 @@ void TuyaPacketProcess(void) * API Functions \*********************************************************************************************/ -boolean TuyaModuleSelected(void) +bool TuyaModuleSelected(void) { if (!(pin[GPIO_TUYA_RX] < 99) || !(pin[GPIO_TUYA_TX] < 99)) { // fallback to hardware-serial if not explicitly selected pin[GPIO_TUYA_TX] = 1; @@ -297,8 +289,7 @@ void TuyaInit(void) if (TuyaSerial->begin(9600)) { if (TuyaSerial->hardwareSerial()) { ClaimSerial(); } // Get MCU Configuration - snprintf_P(log_data, sizeof(log_data), "TYA: Request MCU configuration"); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Request MCU configuration")); TuyaSendCmd(TUYA_CMD_MCU_CONF); } @@ -309,7 +300,7 @@ void TuyaSerialInput(void) { while (TuyaSerial->available()) { yield(); - byte serial_in_byte = TuyaSerial->read(); + uint8_t serial_in_byte = TuyaSerial->read(); if (serial_in_byte == 0x55) { // Start TUYA Packet tuya_cmd_status = 1; @@ -361,11 +352,10 @@ void TuyaSerialInput(void) } -boolean TuyaButtonPressed(void) +bool TuyaButtonPressed(void) { if (!XdrvMailbox.index && ((PRESSED == XdrvMailbox.payload) && (NOT_PRESSED == lastbutton[XdrvMailbox.index]))) { - snprintf_P(log_data, sizeof(log_data), PSTR("TYA: Reset GPIO triggered")); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Reset GPIO triggered")); TuyaResetWifi(); return true; // Reset GPIO served here } @@ -387,8 +377,7 @@ void TuyaSetWifiLed(void){ break; } - snprintf_P(log_data, sizeof(log_data), "TYA: Set WiFi LED to state %d (%d)", wifi_state, WifiState()); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Set WiFi LED to state %d (%d)"), wifi_state, WifiState()); TuyaSendCmd(TUYA_CMD_WIFI_STATE, &wifi_state, 1); } @@ -398,11 +387,11 @@ void TuyaSetWifiLed(void){ * Interface \*********************************************************************************************/ -boolean Xdrv16(byte function) +bool Xdrv16(uint8_t function) { - boolean result = false; + bool result = false; - if (TUYA_DIMMER == Settings.module) { + if (TUYA_DIMMER == my_module_type) { switch (function) { case FUNC_MODULE_INIT: result = TuyaModuleSelected(); diff --git a/sonoff/xdrv_17_rcswitch.ino b/sonoff/xdrv_17_rcswitch.ino index cb972e87d..300a156f5 100644 --- a/sonoff/xdrv_17_rcswitch.ino +++ b/sonoff/xdrv_17_rcswitch.ino @@ -1,7 +1,7 @@ /* xdrv_17_rcswitch.ino - RF transceiver using RcSwitch library for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends + Copyright (C) 2019 Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -49,8 +49,7 @@ void RfReceiveCheck(void) int protocol = mySwitch.getReceivedProtocol(); int delay = mySwitch.getReceivedDelay(); - snprintf_P(log_data, sizeof(log_data), PSTR("RFR: Data %lX (%u), Bits %d, Protocol %d, Delay %d"), data, data, bits, protocol, delay); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RFR: Data 0x%lX (%u), Bits %d, Protocol %d, Delay %d"), data, data, bits, protocol, delay); uint32_t now = millis(); if ((now - rf_lasttime > RF_TIME_AVOID_DUPLICATE) && (data > 0)) { @@ -60,7 +59,7 @@ void RfReceiveCheck(void) if (Settings.flag.rf_receive_decimal) { // SetOption28 (0 = hexadecimal, 1 = decimal) snprintf_P(stemp, sizeof(stemp), PSTR("%u"), (uint32_t)data); } else { - snprintf_P(stemp, sizeof(stemp), PSTR("\"%lX\""), (uint32_t)data); + snprintf_P(stemp, sizeof(stemp), PSTR("\"0x%lX\""), (uint32_t)data); } snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_RFRECEIVED "\":{\"" D_JSON_RF_DATA "\":%s,\"" D_JSON_RF_BITS "\":%d,\"" D_JSON_RF_PROTOCOL "\":%d,\"" D_JSON_RF_PULSE "\":%d}}"), stemp, bits, protocol, delay); @@ -88,10 +87,10 @@ void RfInit(void) * Commands \*********************************************************************************************/ -boolean RfSendCommand(void) +bool RfSendCommand(void) { - boolean serviced = true; - boolean error = false; + bool serviced = true; + bool error = false; if (!strcasecmp_P(XdrvMailbox.topic, PSTR(D_CMND_RFSEND))) { if (XdrvMailbox.data_len) { @@ -116,7 +115,7 @@ boolean RfSendCommand(void) } else { // RFsend data, bits, protocol, repeat, pulse char *p; - byte i = 0; + uint8_t i = 0; for (char *str = strtok_r(XdrvMailbox.data, ", ", &p); str && i < 5; str = strtok_r(NULL, ", ", &p)) { switch (i++) { case 0: @@ -166,9 +165,9 @@ boolean RfSendCommand(void) * Interface \*********************************************************************************************/ -boolean Xdrv17(byte function) +bool Xdrv17(uint8_t function) { - boolean result = false; + bool result = false; if ((pin[GPIO_RFSEND] < 99) || (pin[GPIO_RFRECV] < 99)) { switch (function) { diff --git a/sonoff/xdrv_18_armtronix_dimmers.ino b/sonoff/xdrv_18_armtronix_dimmers.ino index 795149bff..30af12ce7 100644 --- a/sonoff/xdrv_18_armtronix_dimmers.ino +++ b/sonoff/xdrv_18_armtronix_dimmers.ino @@ -1,7 +1,7 @@ /* xdrv_18_armtronix_dimmers.ino - Armtronix dimmers support for Sonoff-Tasmota - Copyright (C) 2018 wvdv2002 and Theo Arends + Copyright (C) 2019 wvdv2002 and Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -31,7 +31,7 @@ TasmotaSerial *ArmtronixSerial = nullptr; -boolean armtronix_ignore_dim = false; // Flag to skip serial send to prevent looping when processing inbound states from the faceplate interaction +bool armtronix_ignore_dim = false; // Flag to skip serial send to prevent looping when processing inbound states from the faceplate interaction int8_t armtronix_wifi_state = -2; // Keep MCU wifi-status in sync with WifiState() int8_t armtronix_dimState[2]; // Dimmer state values. int8_t armtronix_knobState[2]; // Dimmer state values. @@ -40,7 +40,7 @@ int8_t armtronix_knobState[2]; // Dimmer state values. * Internal Functions \*********************************************************************************************/ -boolean ArmtronixSetChannels(void) +bool ArmtronixSetChannels(void) { LightSerial2Duty(((uint8_t*)XdrvMailbox.data)[0], ((uint8_t*)XdrvMailbox.data)[1]); return true; @@ -58,13 +58,11 @@ void LightSerial2Duty(uint8_t duty1, uint8_t duty2) ArmtronixSerial->print("\nDimmer2:"); ArmtronixSerial->println(duty2); - snprintf_P(log_data, sizeof(log_data), PSTR( "ARM: Send Serial Packet Dim Values=%d,%d"), armtronix_dimState[0],armtronix_dimState[1]); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ARM: Send Serial Packet Dim Values=%d,%d"), armtronix_dimState[0],armtronix_dimState[1]); } else { armtronix_ignore_dim = false; - snprintf_P(log_data, sizeof(log_data), PSTR( "ARM: Send Dim Level skipped due to already set. Value=%d,%d"), armtronix_dimState[0],armtronix_dimState[1]); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ARM: Send Dim Level skipped due to already set. Value=%d,%d"), armtronix_dimState[0],armtronix_dimState[1]); } } @@ -73,8 +71,7 @@ void ArmtronixRequestState(void) { if (ArmtronixSerial) { // Get current status of MCU - snprintf_P(log_data, sizeof(log_data), "TYA: Request MCU state"); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P(LOG_LEVEL_DEBUG, PSTR("ARM: Request MCU state")); ArmtronixSerial->println("Status"); } @@ -84,7 +81,7 @@ void ArmtronixRequestState(void) * API Functions \*********************************************************************************************/ -boolean ArmtronixModuleSelected(void) +bool ArmtronixModuleSelected(void) { light_type = LT_SERIAL2; return true; @@ -123,8 +120,7 @@ void ArmtronixSerialInput(void) armtronix_ignore_dim = true; snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_CHANNEL "%d %d"),i+1, temp); ExecuteCommand(scmnd,SRC_SWITCH); - snprintf_P(log_data, sizeof(log_data), PSTR("ARM: Send CMND_CHANNEL=%s"), scmnd ); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ARM: Send CMND_CHANNEL=%s"), scmnd ); } commaIndex = answer.indexOf(',',commaIndex+1); } @@ -152,14 +148,13 @@ void ArmtronixSetWifiLed(void) break; } - snprintf_P(log_data, sizeof(log_data), "ARM: Set WiFi LED to state %d (%d)", wifi_state, WifiState()); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ARM: Set WiFi LED to state %d (%d)"), wifi_state, WifiState()); - char state = '0' + (wifi_state & 1 > 0); + char state = '0' + ((wifi_state & 1) > 0); ArmtronixSerial->print("Setled:"); ArmtronixSerial->write(state); ArmtronixSerial->write(','); - state = '0' + (wifi_state & 2 > 0); + state = '0' + ((wifi_state & 2) > 0); ArmtronixSerial->write(state); ArmtronixSerial->write(10); armtronix_wifi_state = WifiState(); @@ -169,11 +164,11 @@ void ArmtronixSetWifiLed(void) * Interface \*********************************************************************************************/ -boolean Xdrv18(byte function) +bool Xdrv18(uint8_t function) { - boolean result = false; + bool result = false; - if (ARMTRONIX_DIMMERS == Settings.module) { + if (ARMTRONIX_DIMMERS == my_module_type) { switch (function) { case FUNC_MODULE_INIT: result = ArmtronixModuleSelected(); diff --git a/sonoff/xdrv_19_ps16dz_dimmer.ino b/sonoff/xdrv_19_ps16dz_dimmer.ino index 7671249eb..e4b71bb18 100644 --- a/sonoff/xdrv_19_ps16dz_dimmer.ino +++ b/sonoff/xdrv_19_ps16dz_dimmer.ino @@ -1,7 +1,7 @@ /* xdrv_19_ps16dz_dimmer.ino - PS_16_DZ dimmer support for Sonoff-Tasmota - Copyright (C) 2018 Joel Stein and Theo Arends + Copyright (C) 2019 Joel Stein and Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -31,7 +31,7 @@ TasmotaSerial *PS16DZSerial = nullptr; -boolean ps16dz_ignore_dim = false; // Flag to skip serial send to prevent looping when processing inbound states from the faceplate interaction +bool ps16dz_ignore_dim = false; // Flag to skip serial send to prevent looping when processing inbound states from the faceplate interaction //uint64_t ps16dz_seq = 0; @@ -67,17 +67,16 @@ void PS16DZSendCommand(char type = 0, uint8_t value = 0) break; } - snprintf_P(log_data, sizeof(log_data), PSTR( "PSZ: Send serial command: %s"), ps16dz_tx_buffer ); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PSZ: Send serial command: %s"), ps16dz_tx_buffer ); PS16DZSerial->print(ps16dz_tx_buffer); PS16DZSerial->write(0x1B); PS16DZSerial->flush(); } -boolean PS16DZSetPower(void) +bool PS16DZSetPower(void) { - boolean status = false; + bool status = false; uint8_t rpower = XdrvMailbox.index; int16_t source = XdrvMailbox.payload; @@ -91,7 +90,7 @@ boolean PS16DZSetPower(void) return status; } -boolean PS16DZSetChannels(void) +bool PS16DZSetChannels(void) { PS16DZSerialDuty(((uint8_t*)XdrvMailbox.data)[0]); return true; @@ -109,8 +108,7 @@ void PS16DZSerialDuty(uint8_t duty) } else { ps16dz_ignore_dim = false; // reset flag - snprintf_P(log_data, sizeof(log_data), PSTR( "PSZ: Send Dim Level skipped due to 0 or already set. Value=%d"), duty); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PSZ: Send Dim Level skipped due to 0 or already set. Value=%d"), duty); } } @@ -128,7 +126,7 @@ void PS16DZResetWifi(void) * API Functions \*********************************************************************************************/ -boolean PS16DZModuleSelected(void) +bool PS16DZModuleSelected(void) { light_type = LT_SERIAL1; return true; @@ -153,7 +151,7 @@ void PS16DZSerialInput(void) char scmnd[20]; while (PS16DZSerial->available()) { yield(); - byte serial_in_byte = PS16DZSerial->read(); + uint8_t serial_in_byte = PS16DZSerial->read(); if (serial_in_byte != 0x1B){ if (ps16dz_byte_counter >= PS16DZ_BUFFER_SIZE - 1) { memset(ps16dz_rx_buffer, 0, PS16DZ_BUFFER_SIZE); @@ -165,8 +163,7 @@ void PS16DZSerialInput(void) } else { ps16dz_rx_buffer[ps16dz_byte_counter++] = 0x00; - snprintf_P(log_data, sizeof(log_data), PSTR("PSZ: command received: %s"), ps16dz_rx_buffer); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PSZ: command received: %s"), ps16dz_rx_buffer); if(!strncmp(ps16dz_rx_buffer+3, "UPDATE", 6) || !strncmp(ps16dz_rx_buffer+3, "RESULT", 6)) { char *end_str; char *string = ps16dz_rx_buffer+10; @@ -176,23 +173,20 @@ void PS16DZSerialInput(void) char* token2 = strtok_r(token, ":", &end_token); char* token3 = strtok_r(NULL, ":", &end_token); if(!strncmp(token2, "\"switch\"", 8)){ - boolean ps16dz_power = !strncmp(token3, "\"on\"", 4)?true:false; - snprintf_P(log_data, sizeof(log_data), PSTR("PSZ: power received: %s"), token3); - AddLog(LOG_LEVEL_DEBUG); + bool ps16dz_power = !strncmp(token3, "\"on\"", 4)?true:false; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PSZ: power received: %s"), token3); if((power || Settings.light_dimmer > 0) && (power !=ps16dz_power)) { ExecuteCommandPower(1, ps16dz_power, SRC_SWITCH); // send SRC_SWITCH? to use as flag to prevent loop from inbound states from faceplate interaction } } else if(!strncmp(token2, "\"bright\"", 8)){ uint8_t ps16dz_bright = atoi(token3); - snprintf_P(log_data, sizeof(log_data), PSTR("PSZ: brightness received: %d"), ps16dz_bright); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PSZ: brightness received: %d"), ps16dz_bright); if(power && ps16dz_bright > 0 && ps16dz_bright != Settings.light_dimmer) { snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_DIMMER " %d"), ps16dz_bright ); - snprintf_P(log_data, sizeof(log_data), PSTR("PSZ: Send CMND_DIMMER_STR=%s"), scmnd ); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PSZ: Send CMND_DIMMER_STR=%s"), scmnd ); ps16dz_ignore_dim = true; ExecuteCommand(scmnd, SRC_SWITCH); @@ -200,15 +194,13 @@ void PS16DZSerialInput(void) } else if(!strncmp(token2, "\"sequence\"", 10)){ //ps16dz_seq = strtoull(token3+1, NULL, 10); - snprintf_P(log_data, sizeof(log_data), PSTR("PSZ: sequence received: %s"), token3); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PSZ: sequence received: %s"), token3); } token = strtok_r(NULL, ",", &end_str); } } else if(!strncmp(ps16dz_rx_buffer+3, "SETTING", 7)) { - snprintf_P(log_data, sizeof(log_data), PSTR("PSZ: Reset")); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P(LOG_LEVEL_DEBUG, PSTR("PSZ: Reset")); PS16DZResetWifi(); } memset(ps16dz_rx_buffer, 0, PS16DZ_BUFFER_SIZE); @@ -225,11 +217,11 @@ void PS16DZSerialInput(void) * Interface \*********************************************************************************************/ -boolean Xdrv19(byte function) +bool Xdrv19(uint8_t function) { - boolean result = false; + bool result = false; - if (PS_16_DZ == Settings.module) { + if (PS_16_DZ == my_module_type) { switch (function) { case FUNC_MODULE_INIT: result = PS16DZModuleSelected(); diff --git a/sonoff/xdrv_99_debug.ino b/sonoff/xdrv_99_debug.ino new file mode 100644 index 000000000..e9da144b8 --- /dev/null +++ b/sonoff/xdrv_99_debug.ino @@ -0,0 +1,516 @@ +/* + xdrv_99_debug.ino - debug support for Sonoff-Tasmota + + Copyright (C) 2019 Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +//#define USE_DEBUG_DRIVER + +#ifdef DEBUG_THEO +#ifndef USE_DEBUG_DRIVER +#define USE_DEBUG_DRIVER +#endif // USE_DEBUG_DRIVER +#endif // DEBUG_THEO + +//#define USE_DEBUG_SETTING_NAMES + +#ifdef USE_DEBUG_DRIVER +/*********************************************************************************************\ + * Virtual debugging support - Part1 + * + * Needs file zzzz_debug.ino due to DEFINE processing +\*********************************************************************************************/ + +#define XDRV_99 99 + +#ifndef CPU_LOAD_CHECK +#define CPU_LOAD_CHECK 1 // Seconds between each CPU_LOAD log +#endif + +/*********************************************************************************************\ + * Debug commands +\*********************************************************************************************/ + +#define D_CMND_CFGDUMP "CfgDump" +#define D_CMND_CFGPOKE "CfgPoke" +#define D_CMND_CFGPEEK "CfgPeek" +#define D_CMND_CFGSHOW "CfgShow" +#define D_CMND_CFGXOR "CfgXor" +#define D_CMND_CPUCHECK "CpuChk" +#define D_CMND_EXCEPTION "Exception" +#define D_CMND_FREEMEM "FreeMem" +#define D_CMND_RTCDUMP "RtcDump" +#define D_CMND_HELP "Help" +#define D_CMND_SETSENSOR "SetSensor" +#define D_CMND_FLASHMODE "FlashMode" + +enum DebugCommands { + CMND_CFGDUMP, CMND_CFGPEEK, CMND_CFGPOKE, CMND_CFGSHOW, CMND_CFGXOR, + CMND_CPUCHECK, CMND_EXCEPTION, CMND_FREEMEM, CMND_RTCDUMP, CMND_SETSENSOR, CMND_FLASHMODE, CMND_HELP }; +const char kDebugCommands[] PROGMEM = + D_CMND_CFGDUMP "|" D_CMND_CFGPEEK "|" D_CMND_CFGPOKE "|" D_CMND_CFGSHOW "|" D_CMND_CFGXOR "|" + D_CMND_CPUCHECK "|" D_CMND_EXCEPTION "|" D_CMND_FREEMEM "|" D_CMND_RTCDUMP "|" D_CMND_SETSENSOR "|" D_CMND_FLASHMODE "|" D_CMND_HELP; + +uint32_t CPU_loops = 0; +uint32_t CPU_last_millis = 0; +uint32_t CPU_last_loop_time = 0; +uint8_t CPU_load_check = 0; +uint8_t CPU_show_freemem = 0; + +/*******************************************************************************************/ + +#ifdef DEBUG_THEO +void ExceptionTest(uint8_t type) +{ +/* +Exception (28): +epc1=0x4000bf64 epc2=0x00000000 epc3=0x00000000 excvaddr=0x00000007 depc=0x00000000 + +ctx: cont +sp: 3fff1f30 end: 3fff2840 offset: 01a0 + +>>>stack>>> +3fff20d0: 202c3573 756f7247 2c302070 646e4920 +3fff20e0: 40236a6e 7954202c 45206570 00454358 +3fff20f0: 00000010 00000007 00000000 3fff2180 +3fff2100: 3fff2190 40107bfc 3fff3e4c 3fff22c0 +3fff2110: 40261934 000000f0 3fff22c0 401004d8 +3fff2120: 40238fcf 00000050 3fff2100 4021fc10 +3fff2130: 3fff32bc 4021680c 3ffeade1 4021ff7d +3fff2140: 3fff2190 3fff2180 0000000c 7fffffff +3fff2150: 00000019 00000000 00000000 3fff21c0 +3fff2160: 3fff23f3 3ffe8e08 00000000 4021ffb4 +3fff2170: 3fff2190 3fff2180 0000000c 40201118 +3fff2180: 3fff21c0 0000003c 3ffef840 00000007 +3fff2190: 00000000 00000000 00000000 40201128 +3fff21a0: 3fff23f3 000000f1 3fff23ec 4020fafb +3fff21b0: 3fff23f3 3fff21c0 3fff21d0 3fff23f6 +3fff21c0: 00000000 3fff23fb 4022321b 00000000 + +Exception 28: LoadProhibited: A load referenced a page mapped with an attribute that does not permit loads +Decoding 14 results +0x40236a6e: ets_vsnprintf at ?? line ? +0x40107bfc: vsnprintf at C:\Data2\Arduino\arduino-1.8.1-esp-2.3.0\portable\packages\esp8266\hardware\esp8266\2.3.0\cores\esp8266/libc_replacements.c line 387 +0x40261934: bignum_exptmod at ?? line ? +0x401004d8: malloc at C:\Data2\Arduino\arduino-1.8.1-esp-2.3.0\portable\packages\esp8266\hardware\esp8266\2.3.0\cores\esp8266\umm_malloc/umm_malloc.c line 1664 +0x40238fcf: wifi_station_get_connect_status at ?? line ? +0x4021fc10: operator new[](unsigned int) at C:\Data2\Arduino\arduino-1.8.1-esp-2.3.0\portable\packages\esp8266\hardware\esp8266\2.3.0\cores\esp8266/abi.cpp line 57 +0x4021680c: ESP8266WiFiSTAClass::status() at C:\Data2\Arduino\arduino-1.8.1-esp-2.3.0\portable\packages\esp8266\hardware\esp8266\2.3.0\libraries\ESP8266WiFi\src/ESP8266WiFiSTA.cpp line 569 +0x4021ff7d: vsnprintf_P(char*, unsigned int, char const*, __va_list_tag) at C:\Data2\Arduino\arduino-1.8.1-esp-2.3.0\portable\packages\esp8266\hardware\esp8266\2.3.0\cores\esp8266/pgmspace.cpp line 146 +0x4021ffb4: snprintf_P(char*, unsigned int, char const*, ...) at C:\Data2\Arduino\arduino-1.8.1-esp-2.3.0\portable\packages\esp8266\hardware\esp8266\2.3.0\cores\esp8266/pgmspace.cpp line 146 +0x40201118: atol at C:\Data2\Arduino\arduino-1.8.1-esp-2.3.0\portable\packages\esp8266\hardware\esp8266\2.3.0\cores\esp8266/core_esp8266_noniso.c line 45 +0x40201128: atoi at C:\Data2\Arduino\arduino-1.8.1-esp-2.3.0\portable\packages\esp8266\hardware\esp8266\2.3.0\cores\esp8266/core_esp8266_noniso.c line 45 +0x4020fafb: MqttDataHandler(char*, unsigned char*, unsigned int) at R:\Arduino\Work-ESP8266\Theo\sonoff\sonoff-4\sonoff/sonoff.ino line 679 (discriminator 1) +0x4022321b: pp_attach at ?? line ? + +00:00:08 MQTT: tele/sonoff/INFO3 = {"Started":"Fatal exception:28 flag:2 (EXCEPTION) epc1:0x4000bf64 epc2:0x00000000 epc3:0x00000000 excvaddr:0x00000007 depc:0x00000000"} +*/ + if (1 == type) { + char svalue[10]; + snprintf_P(svalue, sizeof(svalue), PSTR("%s"), 7); // Exception 28 as number in string (7 in excvaddr) + } +/* +14:50:52 osWatch: FreeRam 25896, rssi 68, last_run 0 +14:51:02 osWatch: FreeRam 25896, rssi 58, last_run 0 +14:51:03 CMND: exception 2 +14:51:12 osWatch: FreeRam 25360, rssi 60, last_run 8771 +14:51:22 osWatch: FreeRam 25360, rssi 62, last_run 18771 +14:51:32 osWatch: FreeRam 25360, rssi 62, last_run 28771 +14:51:42 osWatch: FreeRam 25360, rssi 62, last_run 38771 +14:51:42 osWatch: Warning, loop blocked. Restart now +*/ + if (2 == type) { + while(1) delay(1000); // this will trigger the os watch + } +} + +#endif // DEBUG_THEO + +/*******************************************************************************************/ + +void CpuLoadLoop(void) +{ + CPU_last_loop_time = millis(); + if (CPU_load_check && CPU_last_millis) { + CPU_loops ++; + if ((CPU_last_millis + (CPU_load_check *1000)) <= CPU_last_loop_time) { +#if defined(F_CPU) && (F_CPU == 160000000L) + int CPU_load = 100 - ( (CPU_loops*(1 + 30*sleep)) / (CPU_load_check *800) ); + CPU_loops = CPU_loops / CPU_load_check; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "FreeRam %d, CPU %d%%(160MHz), Loops/sec %d"), ESP.getFreeHeap(), CPU_load, CPU_loops); +#else + int CPU_load = 100 - ( (CPU_loops*(1 + 30*sleep)) / (CPU_load_check *400) ); + CPU_loops = CPU_loops / CPU_load_check; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "FreeRam %d, CPU %d%%(80MHz), Loops/sec %d"), ESP.getFreeHeap(), CPU_load, CPU_loops); +#endif + CPU_last_millis = CPU_last_loop_time; + CPU_loops = 0; + } + } +} + +/*******************************************************************************************/ + +#if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_1) +// All version before core 2.4.2 +// https://github.com/esp8266/Arduino/issues/2557 + +extern "C" { +#include + extern cont_t g_cont; +} + +void DebugFreeMem(void) +{ + register uint32_t *sp asm("a1"); + +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "FreeRam %d, FreeStack %d, UnmodifiedStack %d (%s)"), ESP.getFreeHeap(), 4 * (sp - g_cont.stack), cont_get_free_stack(&g_cont), XdrvMailbox.data); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "FreeRam %d, FreeStack %d (%s)"), ESP.getFreeHeap(), 4 * (sp - g_cont.stack), XdrvMailbox.data); +} + +#else +// All version from core 2.4.2 +// https://github.com/esp8266/Arduino/pull/5018 +// https://github.com/esp8266/Arduino/pull/4553 + +extern "C" { +#include + extern cont_t* g_pcont; +} + +void DebugFreeMem(void) +{ + register uint32_t *sp asm("a1"); + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "FreeRam %d, FreeStack %d (%s)"), ESP.getFreeHeap(), 4 * (sp - g_pcont->stack), XdrvMailbox.data); +} + +#endif // ARDUINO_ESP8266_RELEASE_2_x_x + +/*******************************************************************************************/ + +void DebugRtcDump(char* parms) +{ + #define CFG_COLS 16 + + uint16_t idx; + uint16_t maxrow; + uint16_t row; + uint16_t col; + char *p; + + // |<--SDK data (256 bytes)-->|<--User data (512 bytes)-->| + // 000 - 0FF: SDK + // 000 - 01B: SDK rst_info + // 100 - 2FF: User + // 280 - 283: Tasmota RtcReboot (Offset 100 (x 4bytes) - sizeof(RTCRBT) (x 4bytes)) + // 290 - 2EB: Tasmota RtcSettings (Offset 100 (x 4bytes)) + + uint8_t buffer[768]; +// ESP.rtcUserMemoryRead(0, (uint32_t*)&buffer, sizeof(buffer)); + system_rtc_mem_read(0, (uint32_t*)&buffer, sizeof(buffer)); + + maxrow = ((sizeof(buffer)+CFG_COLS)/CFG_COLS); + + uint16_t srow = strtol(parms, &p, 16) / CFG_COLS; + uint16_t mrow = strtol(p, &p, 10); + +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Cnfg: Parms %s, Start row %d, rows %d"), parms, srow, mrow); + + if (0 == mrow) { // Default only 8 lines + mrow = 8; + } + if (srow > maxrow) { + srow = maxrow - mrow; + } + if (mrow < (maxrow - srow)) { + maxrow = srow + mrow; + } + + for (row = srow; row < maxrow; row++) { + idx = row * CFG_COLS; + snprintf_P(log_data, sizeof(log_data), PSTR("%03X:"), idx); + for (col = 0; col < CFG_COLS; col++) { + if (!(col%4)) { + snprintf_P(log_data, sizeof(log_data), PSTR("%s "), log_data); + } + snprintf_P(log_data, sizeof(log_data), PSTR("%s %02X"), log_data, buffer[idx + col]); + } + snprintf_P(log_data, sizeof(log_data), PSTR("%s |"), log_data); + for (col = 0; col < CFG_COLS; col++) { +// if (!(col%4)) { +// snprintf_P(log_data, sizeof(log_data), PSTR("%s "), log_data); +// } + snprintf_P(log_data, sizeof(log_data), PSTR("%s%c"), log_data, ((buffer[idx + col] > 0x20) && (buffer[idx + col] < 0x7F)) ? (char)buffer[idx + col] : ' '); + } + snprintf_P(log_data, sizeof(log_data), PSTR("%s|"), log_data); + AddLog(LOG_LEVEL_INFO); + } +} + +/*******************************************************************************************/ + +void DebugCfgDump(char* parms) +{ + #define CFG_COLS 16 + + uint16_t idx; + uint16_t maxrow; + uint16_t row; + uint16_t col; + char *p; + + uint8_t *buffer = (uint8_t *) &Settings; + maxrow = ((sizeof(SYSCFG)+CFG_COLS)/CFG_COLS); + + uint16_t srow = strtol(parms, &p, 16) / CFG_COLS; + uint16_t mrow = strtol(p, &p, 10); + +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Cnfg: Parms %s, Start row %d, rows %d"), parms, srow, mrow); + + if (0 == mrow) { // Default only 8 lines + mrow = 8; + } + if (srow > maxrow) { + srow = maxrow - mrow; + } + if (mrow < (maxrow - srow)) { + maxrow = srow + mrow; + } + + for (row = srow; row < maxrow; row++) { + idx = row * CFG_COLS; + snprintf_P(log_data, sizeof(log_data), PSTR("%03X:"), idx); + for (col = 0; col < CFG_COLS; col++) { + if (!(col%4)) { + snprintf_P(log_data, sizeof(log_data), PSTR("%s "), log_data); + } + snprintf_P(log_data, sizeof(log_data), PSTR("%s %02X"), log_data, buffer[idx + col]); + } + snprintf_P(log_data, sizeof(log_data), PSTR("%s |"), log_data); + for (col = 0; col < CFG_COLS; col++) { +// if (!(col%4)) { +// snprintf_P(log_data, sizeof(log_data), PSTR("%s "), log_data); +// } + snprintf_P(log_data, sizeof(log_data), PSTR("%s%c"), log_data, ((buffer[idx + col] > 0x20) && (buffer[idx + col] < 0x7F)) ? (char)buffer[idx + col] : ' '); + } + snprintf_P(log_data, sizeof(log_data), PSTR("%s|"), log_data); + AddLog(LOG_LEVEL_INFO); + delay(1); + } +} + +void DebugCfgPeek(char* parms) +{ + char *p; + + uint16_t address = strtol(parms, &p, 16); + if (address > sizeof(SYSCFG)) address = sizeof(SYSCFG) -4; + address = (address >> 2) << 2; + + uint8_t *buffer = (uint8_t *) &Settings; + uint8_t data8 = buffer[address]; + uint16_t data16 = (buffer[address +1] << 8) + buffer[address]; + uint32_t data32 = (buffer[address +3] << 24) + (buffer[address +2] << 16) + data16; + + snprintf_P(log_data, sizeof(log_data), PSTR("%03X:"), address); + for (uint8_t i = 0; i < 4; i++) { + snprintf_P(log_data, sizeof(log_data), PSTR("%s %02X"), log_data, buffer[address +i]); + } + snprintf_P(log_data, sizeof(log_data), PSTR("%s |"), log_data); + for (uint8_t i = 0; i < 4; i++) { + snprintf_P(log_data, sizeof(log_data), PSTR("%s%c"), log_data, ((buffer[address +i] > 0x20) && (buffer[address +i] < 0x7F)) ? (char)buffer[address +i] : ' '); + } + snprintf_P(log_data, sizeof(log_data), PSTR("%s| 0x%02X (%d), 0x%04X (%d), 0x%0LX (%lu)"), log_data, data8, data8, data16, data16, data32, data32); + AddLog(LOG_LEVEL_INFO); +} + +void DebugCfgPoke(char* parms) +{ + char *p; + + uint16_t address = strtol(parms, &p, 16); + if (address > sizeof(SYSCFG)) address = sizeof(SYSCFG) -4; + address = (address >> 2) << 2; + + uint32_t data = strtol(p, &p, 16); + + uint8_t *buffer = (uint8_t *) &Settings; + uint32_t data32 = (buffer[address +3] << 24) + (buffer[address +2] << 16) + (buffer[address +1] << 8) + buffer[address]; + + uint8_t *nbuffer = (uint8_t *) &data; + for (uint8_t i = 0; i < 4; i++) { buffer[address +i] = nbuffer[+i]; } + + uint32_t ndata32 = (buffer[address +3] << 24) + (buffer[address +2] << 16) + (buffer[address +1] << 8) + buffer[address]; + + AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: 0x%0LX (%lu) poked to 0x%0LX (%lu)"), address, data32, data32, ndata32, ndata32); +} + +#ifdef USE_DEBUG_SETTING_NAMES +void DebugCfgShow(uint8_t more) +{ + uint8_t *SetAddr; + SetAddr = (uint8_t *)&Settings; + + AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: Hostname (%d) [%s]"), (uint8_t *)&Settings.hostname - SetAddr, sizeof(Settings.hostname)-1, Settings.hostname); + AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: SSids (%d) [%s], [%s]"), (uint8_t *)&Settings.sta_ssid - SetAddr, sizeof(Settings.sta_ssid[0])-1, Settings.sta_ssid[0], Settings.sta_ssid[1]); + AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: Friendlynames (%d) [%s], [%s], [%s], [%s]"), (uint8_t *)&Settings.friendlyname - SetAddr, sizeof(Settings.friendlyname[0])-1, Settings.friendlyname[0], Settings.friendlyname[1], Settings.friendlyname[2], Settings.friendlyname[3]); + AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: OTA Url (%d) [%s]"), (uint8_t *)&Settings.ota_url - SetAddr, sizeof(Settings.ota_url)-1, Settings.ota_url); + AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: StateText (%d) [%s], [%s], [%s], [%s]"), (uint8_t *)&Settings.state_text - SetAddr, sizeof(Settings.state_text[0])-1, Settings.state_text[0], Settings.state_text[1], Settings.state_text[2], Settings.state_text[3]); + AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: Syslog Host (%d) [%s]"), (uint8_t *)&Settings.syslog_host - SetAddr, sizeof(Settings.syslog_host)-1, Settings.syslog_host); + AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: NTP Servers (%d) [%s], [%s], [%s]"), (uint8_t *)&Settings.ntp_server - SetAddr, sizeof(Settings.ntp_server[0])-1, Settings.ntp_server[0], Settings.ntp_server[1], Settings.ntp_server[2]); + AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: MQTT Host (%d) [%s]"), (uint8_t *)&Settings.mqtt_host - SetAddr, sizeof(Settings.mqtt_host)-1, Settings.mqtt_host); + AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: MQTT Client (%d) [%s]"), (uint8_t *)&Settings.mqtt_client - SetAddr, sizeof(Settings.mqtt_client)-1, Settings.mqtt_client); + AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: MQTT User (%d) [%s]"), (uint8_t *)&Settings.mqtt_user - SetAddr, sizeof(Settings.mqtt_user)-1, Settings.mqtt_user); + AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: MQTT FullTopic (%d) [%s]"), (uint8_t *)&Settings.mqtt_fulltopic - SetAddr, sizeof(Settings.mqtt_fulltopic)-1, Settings.mqtt_fulltopic); + AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: MQTT Topic (%d) [%s]"), (uint8_t *)&Settings.mqtt_topic - SetAddr, sizeof(Settings.mqtt_topic)-1, Settings.mqtt_topic); + AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: MQTT GroupTopic (%d) [%s]"), (uint8_t *)&Settings.mqtt_grptopic - SetAddr, sizeof(Settings.mqtt_grptopic)-1, Settings.mqtt_grptopic); + AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: MQTT ButtonTopic (%d) [%s]"), (uint8_t *)&Settings.button_topic - SetAddr, sizeof(Settings.button_topic)-1, Settings.button_topic); + AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: MQTT SwitchTopic (%d) [%s]"), (uint8_t *)&Settings.switch_topic - SetAddr, sizeof(Settings.switch_topic)-1, Settings.switch_topic); + AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: MQTT Prefixes (%d) [%s], [%s], [%s]"), (uint8_t *)&Settings.mqtt_prefix - SetAddr, sizeof(Settings.mqtt_prefix[0])-1, Settings.mqtt_prefix[0], Settings.mqtt_prefix[1], Settings.mqtt_prefix[2]); + if (17 == more) { + AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: AP Passwords (%d) [%s], [%s]"), (uint8_t *)&Settings.sta_pwd - SetAddr, sizeof(Settings.sta_pwd[0])-1, Settings.sta_pwd[0], Settings.sta_pwd[1]); + AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: MQTT Password (%d) [%s]"), (uint8_t *)&Settings.mqtt_pwd - SetAddr, sizeof(Settings.mqtt_pwd)-1, Settings.mqtt_pwd); + AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: Web Password (%d) [%s]"), (uint8_t *)&Settings.web_password - SetAddr, sizeof(Settings.web_password)-1, Settings.web_password); + } +} +#endif // USE_DEBUG_SETTING_NAMES + +void SetFlashMode(uint8_t mode) +{ + uint8_t *_buffer; + uint32_t address; + + address = 0; + _buffer = new uint8_t[FLASH_SECTOR_SIZE]; + + if (ESP.flashRead(address, (uint32_t*)_buffer, FLASH_SECTOR_SIZE)) { + if (_buffer[2] != mode) { // DOUT + _buffer[2] = mode; + if (ESP.flashEraseSector(address / FLASH_SECTOR_SIZE)) ESP.flashWrite(address, (uint32_t*)_buffer, FLASH_SECTOR_SIZE); + } + } + delete[] _buffer; +} + +/*******************************************************************************************/ + +bool DebugCommand(void) +{ + char command[CMDSZ]; + bool serviced = true; + + int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic, kDebugCommands); + if (-1 == command_code) { + serviced = false; // Unknown command + } + else if (CMND_HELP == command_code) { + AddLog_P(LOG_LEVEL_INFO, kDebugCommands); + snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, D_JSON_DONE); + } + else if (CMND_RTCDUMP == command_code) { + DebugRtcDump(XdrvMailbox.data); + snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, D_JSON_DONE); + } + else if (CMND_CFGDUMP == command_code) { + DebugCfgDump(XdrvMailbox.data); + snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, D_JSON_DONE); + } + else if (CMND_CFGPEEK == command_code) { + DebugCfgPeek(XdrvMailbox.data); + snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, D_JSON_DONE); + } + else if (CMND_CFGPOKE == command_code) { + DebugCfgPoke(XdrvMailbox.data); + snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, D_JSON_DONE); + } +#ifdef USE_DEBUG_SETTING_NAMES + else if (CMND_CFGSHOW == command_code) { + DebugCfgShow(XdrvMailbox.payload); + snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, D_JSON_DONE); + } +#endif // USE_DEBUG_SETTING_NAMES +#ifdef USE_WEBSERVER + else if (CMND_CFGXOR == command_code) { + if (XdrvMailbox.data_len > 0) { + config_xor_on_set = XdrvMailbox.payload; + } + snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, config_xor_on_set); + } +#endif // USE_WEBSERVER +#ifdef DEBUG_THEO + else if (CMND_EXCEPTION == command_code) { + if (XdrvMailbox.data_len > 0) ExceptionTest(XdrvMailbox.payload); + snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, D_JSON_DONE); + } +#endif // DEBUG_THEO + else if (CMND_CPUCHECK == command_code) { + if (XdrvMailbox.data_len > 0) { + CPU_load_check = XdrvMailbox.payload; + CPU_last_millis = CPU_last_loop_time; + } + snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, CPU_load_check); + } + else if (CMND_FREEMEM == command_code) { + if (XdrvMailbox.data_len > 0) { + CPU_show_freemem = XdrvMailbox.payload; + } + snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, CPU_show_freemem); + } + else if ((CMND_SETSENSOR == command_code) && (XdrvMailbox.index < MAX_XSNS_DRIVERS)) { + if ((XdrvMailbox.payload >= 0) && XsnsPresent(XdrvMailbox.index)) { + bitWrite(Settings.sensors[XdrvMailbox.index / 32], XdrvMailbox.index % 32, XdrvMailbox.payload &1); + if (1 == XdrvMailbox.payload) { restart_flag = 2; } // To safely re-enable a sensor currently most sensor need to follow complete restart init cycle + } + snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_XVALUE, command, XsnsGetSensors().c_str()); + } + else if (CMND_FLASHMODE == command_code) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) { + SetFlashMode(XdrvMailbox.payload); + } + snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, ESP.getFlashChipMode()); + } + else serviced = false; // Unknown command + + return serviced; +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xdrv99(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_PRE_INIT: + CPU_last_millis = millis(); + break; + case FUNC_LOOP: + CpuLoadLoop(); + break; + case FUNC_COMMAND: + result = DebugCommand(); + break; + case FUNC_FREE_MEM: + if (CPU_show_freemem) { DebugFreeMem(); } + break; + } + return result; +} + +#endif // USE_DEBUG_DRIVER \ No newline at end of file diff --git a/sonoff/xdrv_interface.ino b/sonoff/xdrv_interface.ino index 0ec3696d9..7de5e1bb5 100644 --- a/sonoff/xdrv_interface.ino +++ b/sonoff/xdrv_interface.ino @@ -1,7 +1,7 @@ /* xdrv_interface.ino - Driver interface support for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends inspired by ESPEasy + Copyright (C) 2019 Theo Arends inspired by ESPEasy This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -18,9 +18,9 @@ */ #ifdef XFUNC_PTR_IN_ROM -boolean (* const xdrv_func_ptr[])(byte) PROGMEM = { // Driver Function Pointers +bool (* const xdrv_func_ptr[])(uint8_t) PROGMEM = { // Driver Function Pointers #else -boolean (* const xdrv_func_ptr[])(byte) = { // Driver Function Pointers +bool (* const xdrv_func_ptr[])(uint8_t) = { // Driver Function Pointers #endif #ifdef XDRV_01 @@ -192,21 +192,7 @@ boolean (* const xdrv_func_ptr[])(byte) = { // Driver Function Pointers const uint8_t xdrv_present = sizeof(xdrv_func_ptr) / sizeof(xdrv_func_ptr[0]); // Number of drivers found -boolean XdrvCommand(uint8_t grpflg, char *type, uint16_t index, char *dataBuf, uint16_t data_len, int16_t payload, uint16_t payload16) -{ -// XdrvMailbox.valid = 1; - XdrvMailbox.index = index; - XdrvMailbox.data_len = data_len; - XdrvMailbox.payload16 = payload16; - XdrvMailbox.payload = payload; - XdrvMailbox.grpflg = grpflg; - XdrvMailbox.topic = type; - XdrvMailbox.data = dataBuf; - - return XdrvCall(FUNC_COMMAND); -} - -boolean XdrvMqttData(char *topicBuf, uint16_t stopicBuf, char *dataBuf, uint16_t sdataBuf) +bool XdrvMqttData(char *topicBuf, uint16_t stopicBuf, char *dataBuf, uint16_t sdataBuf) { XdrvMailbox.index = stopicBuf; XdrvMailbox.data_len = sdataBuf; @@ -216,11 +202,12 @@ boolean XdrvMqttData(char *topicBuf, uint16_t stopicBuf, char *dataBuf, uint16_t return XdrvCall(FUNC_MQTT_DATA); } -boolean XdrvRulesProcess(void) +bool XdrvRulesProcess(void) { return XdrvCall(FUNC_RULES_PROCESS); } +#ifdef USE_DEBUG_DRIVER void ShowFreeMem(const char *where) { char stemp[20]; @@ -228,19 +215,32 @@ void ShowFreeMem(const char *where) XdrvMailbox.data = stemp; XdrvCall(FUNC_FREE_MEM); } +#endif /*********************************************************************************************\ * Function call to all xdrv \*********************************************************************************************/ -boolean XdrvCall(byte Function) +bool XdrvCall(uint8_t Function) { - boolean result = false; + bool result = false; - for (byte x = 0; x < xdrv_present; x++) { + for (uint8_t x = 0; x < xdrv_present; x++) { // WifiAddDelayWhenDisconnected(); result = xdrv_func_ptr[x](Function); - if (result) break; + + if (result && ((FUNC_COMMAND == Function) || + (FUNC_COMMAND_DRIVER == Function) || + (FUNC_MQTT_DATA == Function) || + (FUNC_RULES_PROCESS == Function) || + (FUNC_BUTTON_PRESSED == Function) || + (FUNC_SERIAL == Function) || + (FUNC_MODULE_INIT == Function) || + (FUNC_SET_CHANNELS == Function) || + (FUNC_SET_DEVICE_POWER == Function) + )) { + break; + } } return result; diff --git a/sonoff/xdsp_01_lcd.ino b/sonoff/xdsp_01_lcd.ino index 60fe0bab6..28d045858 100644 --- a/sonoff/xdsp_01_lcd.ino +++ b/sonoff/xdsp_01_lcd.ino @@ -1,7 +1,7 @@ /* xdsp_01_lcd.ino - Display LCD support for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends and Adafruit + Copyright (C) 2019 Theo Arends and Adafruit This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -97,24 +97,29 @@ void LcdDisplayOnOff(uint8_t on) #ifdef USE_DISPLAY_MODES1TO5 -void LcdCenter(byte row, char* txt) +void LcdCenter(uint8_t row, char* txt) { - int offset; - int len; char line[Settings.display_cols[0] +2]; + int len = strlen(txt); + int offset = 0; + if (len >= Settings.display_cols[0]) { + len = Settings.display_cols[0]; + } else { + offset = (Settings.display_cols[0] - len) / 2; + } memset(line, 0x20, Settings.display_cols[0]); line[Settings.display_cols[0]] = 0; - len = strlen(txt); - offset = (len < Settings.display_cols[0]) ? offset = (Settings.display_cols[0] - len) / 2 : 0; - strlcpy(line +offset, txt, len); + for (uint8_t i = 0; i < len; i++) { + line[offset +i] = txt[i]; + } lcd->setCursor(0, row); lcd->print(line); } -boolean LcdPrintLog(void) +bool LcdPrintLog(void) { - boolean result = false; + bool result = false; disp_refresh--; if (!disp_refresh) { @@ -125,7 +130,7 @@ boolean LcdPrintLog(void) if (txt != NULL) { uint8_t last_row = Settings.display_rows -1; - for (byte i = 0; i < last_row; i++) { + for (uint8_t i = 0; i < last_row; i++) { strlcpy(disp_screen_buffer[i], disp_screen_buffer[i +1], disp_screen_buffer_cols); lcd->setCursor(0, i); // Col 0, Row i lcd->print(disp_screen_buffer[i +1]); @@ -133,8 +138,7 @@ boolean LcdPrintLog(void) strlcpy(disp_screen_buffer[last_row], txt, disp_screen_buffer_cols); DisplayFillScreen(last_row); - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_DEBUG "[%s]"), disp_screen_buffer[last_row]); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "[%s]"), disp_screen_buffer[last_row]); lcd->setCursor(0, last_row); lcd->print(disp_screen_buffer[last_row]); @@ -181,9 +185,9 @@ void LcdRefresh(void) // Every second * Interface \*********************************************************************************************/ -boolean Xdsp01(byte function) +bool Xdsp01(uint8_t function) { - boolean result = false; + bool result = false; if (i2c_flg) { if (FUNC_DISPLAY_INIT_DRIVER == function) { diff --git a/sonoff/xdsp_02_ssd1306.ino b/sonoff/xdsp_02_ssd1306.ino index 4ccdcd9c9..9697e4db0 100644 --- a/sonoff/xdsp_02_ssd1306.ino +++ b/sonoff/xdsp_02_ssd1306.ino @@ -1,7 +1,7 @@ /* xdsp_02_ssd1306.ino - Display Oled ssd1306 support for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends and Adafruit + Copyright (C) 2019 Theo Arends and Adafruit This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -147,15 +147,14 @@ void Ssd1306PrintLog(void) oled->clearDisplay(); oled->setTextSize(Settings.display_size); oled->setCursor(0,0); - for (byte i = 0; i < last_row; i++) { + for (uint8_t i = 0; i < last_row; i++) { strlcpy(disp_screen_buffer[i], disp_screen_buffer[i +1], disp_screen_buffer_cols); oled->println(disp_screen_buffer[i]); } strlcpy(disp_screen_buffer[last_row], txt, disp_screen_buffer_cols); DisplayFillScreen(last_row); - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_DEBUG "[%s]"), disp_screen_buffer[last_row]); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "[%s]"), disp_screen_buffer[last_row]); oled->println(disp_screen_buffer[last_row]); oled->display(); @@ -200,9 +199,9 @@ void Ssd1306Refresh(void) // Every second * Interface \*********************************************************************************************/ -boolean Xdsp02(byte function) +bool Xdsp02(uint8_t function) { - boolean result = false; + bool result = false; if (i2c_flg) { if (FUNC_DISPLAY_INIT_DRIVER == function) { diff --git a/sonoff/xdsp_03_matrix.ino b/sonoff/xdsp_03_matrix.ino index da458cb15..2535c92d9 100644 --- a/sonoff/xdsp_03_matrix.ino +++ b/sonoff/xdsp_03_matrix.ino @@ -1,7 +1,7 @@ /* xdsp_03_matrix.ino - Display 8x8 matrix support for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends and Adafruit + Copyright (C) 2019 Theo Arends and Adafruit This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -47,14 +47,14 @@ uint8_t mtx_done = 0; void MatrixWrite(void) { - for (byte i = 0; i < mtx_matrices; i++) { + for (uint8_t i = 0; i < mtx_matrices; i++) { matrix[i]->writeDisplay(); } } void MatrixClear(void) { - for (byte i = 0; i < mtx_matrices; i++) { + for (uint8_t i = 0; i < mtx_matrices; i++) { matrix[i]->clear(); } MatrixWrite(); @@ -62,7 +62,7 @@ void MatrixClear(void) void MatrixFixed(char* txt) { - for (byte i = 0; i < mtx_matrices; i++) { + for (uint8_t i = 0; i < mtx_matrices; i++) { matrix[i]->clear(); matrix[i]->setCursor(-i *8, 0); matrix[i]->print(txt); @@ -77,7 +77,7 @@ void MatrixCenter(char* txt) int len = strlen(txt); offset = (len < 8) ? offset = ((mtx_matrices *8) - (len *6)) / 2 : 0; - for (byte i = 0; i < mtx_matrices; i++) { + for (uint8_t i = 0; i < mtx_matrices; i++) { matrix[i]->clear(); matrix[i]->setCursor(-(i *8)+offset, 0); matrix[i]->print(txt); @@ -94,15 +94,14 @@ void MatrixScrollLeft(char* txt, int loop) // Horiz. position of text -- starts off right edge mtx_x = 8 * mtx_matrices; - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_DEBUG "[%s]"), txt); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "[%s]"), txt); disp_refresh = Settings.display_refresh; case 2: disp_refresh--; if (!disp_refresh) { disp_refresh = Settings.display_refresh; - for (byte i = 0; i < mtx_matrices; i++) { + for (uint8_t i = 0; i < mtx_matrices; i++) { matrix[i]->clear(); matrix[i]->setCursor(mtx_x - i *8, 0); matrix[i]->print(txt); @@ -145,9 +144,9 @@ void MatrixScrollUp(char* txt, int loop) words[wordcounter++] = p; p = strtok(NULL, separators); } - for (byte i = 0; i < mtx_matrices; i++) { + for (uint8_t i = 0; i < mtx_matrices; i++) { matrix[i]->clear(); - for (byte j = 0; j < wordcounter; j++) { + for (uint8_t j = 0; j < wordcounter; j++) { matrix[i]->setCursor(-i *8, mtx_y + (j *8)); matrix[i]->println(words[j]); } @@ -170,7 +169,7 @@ void MatrixScrollUp(char* txt, int loop) void MatrixInitMode(void) { - for (byte i = 0; i < mtx_matrices; i++) { + for (uint8_t i = 0; i < mtx_matrices; i++) { matrix[i]->setRotation(Settings.display_rotate); // 1 matrix[i]->setBrightness(Settings.display_dimmer); matrix[i]->blinkRate(0); // 0 - 3 @@ -227,7 +226,7 @@ void MatrixOnOff(void) void MatrixDrawStringAt(uint16_t x, uint16_t y, char *str, uint16_t color, uint8_t flag) { - snprintf(mtx_buffer, MTX_MAX_SCREEN_BUFFER, str); + strlcpy(mtx_buffer, str, MTX_MAX_SCREEN_BUFFER); mtx_mode = x &1; // Use x for selecting scroll up (0) or scroll left (1) mtx_loop = y &1; // Use y for selecting no loop (0) or loop (1) if (!mtx_state) { mtx_state = 1; } @@ -261,8 +260,7 @@ void MatrixPrintLog(uint8_t direction) i++; } - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_APPLICATION "[%s]"), mtx_buffer); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION "[%s]"), mtx_buffer); mtx_done = 1; } @@ -327,9 +325,9 @@ void MatrixRefresh(void) // Every second * Interface \*********************************************************************************************/ -boolean Xdsp03(byte function) +bool Xdsp03(uint8_t function) { - boolean result = false; + bool result = false; if (i2c_flg) { if (FUNC_DISPLAY_INIT_DRIVER == function) { diff --git a/sonoff/xdsp_04_ili9341.ino b/sonoff/xdsp_04_ili9341.ino index b4149035e..f50164e59 100644 --- a/sonoff/xdsp_04_ili9341.ino +++ b/sonoff/xdsp_04_ili9341.ino @@ -1,7 +1,7 @@ /* xdsp_04_ili9341.ino - Display Tft Ili9341 support for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends and Adafruit + Copyright (C) 2019 Theo Arends and Adafruit This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -148,7 +148,7 @@ void Ili9341PrintLog(void) char* txt = DisplayLogBuffer('\370'); if (txt != NULL) { - byte size = Settings.display_size; + uint8_t size = Settings.display_size; uint16_t theight = size * TFT_FONT_HEIGTH; tft->setTextSize(size); @@ -167,7 +167,7 @@ void Ili9341PrintLog(void) tft_scroll = theight; // Start below header tft->setCursor(0, tft_scroll); - for (byte i = 0; i < last_row; i++) { + for (uint8_t i = 0; i < last_row; i++) { strlcpy(disp_screen_buffer[i], disp_screen_buffer[i +1], disp_screen_buffer_cols); // tft->fillRect(0, tft_scroll, tft->width(), theight, ILI9341_BLACK); // Erase line tft->print(disp_screen_buffer[i]); @@ -178,8 +178,7 @@ void Ili9341PrintLog(void) DisplayFillScreen(last_row); tft->print(disp_screen_buffer[last_row]); } - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_APPLICATION "[%s]"), txt); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION "[%s]"), txt); } } } @@ -222,9 +221,9 @@ void Ili9341Refresh(void) // Every second * Interface \*********************************************************************************************/ -boolean Xdsp04(byte function) +bool Xdsp04(uint8_t function) { - boolean result = false; + bool result = false; if (spi_flg) { if (FUNC_DISPLAY_INIT_DRIVER == function) { diff --git a/sonoff/xdsp_05_epaper_29.ino b/sonoff/xdsp_05_epaper_29.ino index 24bb19655..6f4034ece 100644 --- a/sonoff/xdsp_05_epaper_29.ino +++ b/sonoff/xdsp_05_epaper_29.ino @@ -1,7 +1,7 @@ /* xdsp_05_epaper_29.ino - 2.9 Inch display e-paper support for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends, Gerhard Mutz and Waveshare + Copyright (C) 2019 Theo Arends, Gerhard Mutz and Waveshare This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -19,7 +19,7 @@ #ifdef USE_SPI #ifdef USE_DISPLAY -#ifdef USE_DISPLAY_EPAPER +#ifdef USE_DISPLAY_EPAPER_29 #define XDSP_05 5 @@ -123,12 +123,14 @@ void EpdInitDriver(void) epd.sclk_pin = pin[GPIO_SPI_CLK]; // 14 epd.mosi_pin = pin[GPIO_SPI_MOSI]; // 13 EpdInitMode(); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EPD: HardSPI CS %d, CLK %d, MOSI %d"), epd.cs_pin, epd.sclk_pin, epd.mosi_pin); } else if ((pin[GPIO_SSPI_CS] < 99) && (pin[GPIO_SSPI_SCLK] < 99) && (pin[GPIO_SSPI_MOSI] < 99)) { epd.cs_pin = pin[GPIO_SSPI_CS]; epd.sclk_pin = pin[GPIO_SSPI_SCLK]; epd.mosi_pin = pin[GPIO_SSPI_MOSI]; EpdInitMode(); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EPD: SoftSPI CS %d, CLK %d, MOSI %d"), epd.cs_pin, epd.sclk_pin, epd.mosi_pin); } } } @@ -161,6 +163,7 @@ void EpdDisplayFrame(void) { epd.SetFrameMemory(paint.GetImage(), 0, 0, paint.GetWidth(), paint.GetHeight()); epd.DisplayFrame(); + epd.Sleep(); } void EpdDrawStringAt(uint16_t x, uint16_t y, char *str, uint8_t color, uint8_t flag) @@ -198,7 +201,7 @@ void EpdPrintLog(void) char* txt = DisplayLogBuffer('\040'); if (txt != NULL) { - byte size = Settings.display_size; + uint8_t size = Settings.display_size; uint16_t theight = size * EPD_FONT_HEIGTH; EpdSetFont(size); @@ -206,7 +209,7 @@ void EpdPrintLog(void) // epd_scroll = theight; // Start below header epd_scroll = 0; // Start at top with no header - for (byte i = 0; i < last_row; i++) { + for (uint8_t i = 0; i < last_row; i++) { strlcpy(disp_screen_buffer[i], disp_screen_buffer[i +1], disp_screen_buffer_cols); EpdDrawStringAt(0, epd_scroll, disp_screen_buffer[i], COLORED, 0); epd_scroll += theight; @@ -216,8 +219,7 @@ void EpdPrintLog(void) EpdDrawStringAt(0, epd_scroll, disp_screen_buffer[last_row], COLORED, 0); // EpdDisplayFrame(); - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_APPLICATION "[%s]"), txt); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION "[%s]"), txt); } } } @@ -262,9 +264,9 @@ void EpdRefresh(void) // Every second * Interface \*********************************************************************************************/ -boolean Xdsp05(byte function) +bool Xdsp05(uint8_t function) { - boolean result = false; + bool result = false; if (spi_flg || soft_spi_flg) { if (FUNC_DISPLAY_INIT_DRIVER == function) { @@ -337,6 +339,6 @@ boolean Xdsp05(byte function) return result; } -#endif // USE_DISPLAY_EPAPER +#endif // USE_DISPLAY_EPAPER_29 #endif // USE_DISPLAY #endif // USE_SPI diff --git a/sonoff/xdsp_interface.ino b/sonoff/xdsp_interface.ino index e2847d85e..1f7becf88 100644 --- a/sonoff/xdsp_interface.ino +++ b/sonoff/xdsp_interface.ino @@ -1,7 +1,7 @@ /* xdsp_interface.ino - Display interface support for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends + Copyright (C) 2019 Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,10 +17,12 @@ along with this program. If not, see . */ +#ifdef USE_DISPLAY + #ifdef XFUNC_PTR_IN_ROM -boolean (* const xdsp_func_ptr[])(byte) PROGMEM = { // Display Function Pointers +bool (* const xdsp_func_ptr[])(uint8_t) PROGMEM = { // Display Function Pointers #else -boolean (* const xdsp_func_ptr[])(byte) = { // Display Function Pointers +bool (* const xdsp_func_ptr[])(uint8_t) = { // Display Function Pointers #endif #ifdef XDSP_01 @@ -117,14 +119,19 @@ uint8_t XdspPresent(void) return xdsp_present; } -boolean XdspCall(byte Function) +bool XdspCall(uint8_t Function) { - boolean result = false; + bool result = false; - for (byte x = 0; x < xdsp_present; x++) { + for (uint8_t x = 0; x < xdsp_present; x++) { result = xdsp_func_ptr[x](Function); - if (result) break; + + if (result && (FUNC_DISPLAY_MODEL == Function)) { + break; + } } return result; } + +#endif // USE_DISPLAY diff --git a/sonoff/xnrg_01_hlw8012.ino b/sonoff/xnrg_01_hlw8012.ino index 0a4419498..14c00d911 100644 --- a/sonoff/xnrg_01_hlw8012.ino +++ b/sonoff/xnrg_01_hlw8012.ino @@ -1,7 +1,7 @@ /* xnrg_01_hlw8012.ino - HLW8012 (Sonoff Pow) energy sensor support for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends + Copyright (C) 2019 Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -37,29 +37,36 @@ #define HJL_UREF 822 #define HJL_IREF 3300 -#define HLW_POWER_PROBE_TIME 10 // Number of seconds to probe for power before deciding none used +#define HLW_POWER_PROBE_TIME 10 // Number of seconds to probe for power before deciding none used (low power pulse can take up to 10 seconds) +#define HLW_SAMPLE_COUNT 10 // Max number of samples per cycle -byte hlw_select_ui_flag; -byte hlw_ui_flag = 1; -byte hlw_model_type = 0; -byte hlw_load_off; -byte hlw_cf1_timer; -unsigned long hlw_cf_pulse_length; -unsigned long hlw_cf_pulse_last_time; -unsigned long hlw_cf1_pulse_length; -unsigned long hlw_cf1_pulse_last_time; -unsigned long hlw_cf1_summed_pulse_length; -unsigned long hlw_cf1_pulse_counter; -unsigned long hlw_cf1_voltage_pulse_length; -unsigned long hlw_cf1_current_pulse_length; -unsigned long hlw_energy_period_counter; +//#define HLW_DEBUG +#ifdef HLW_DEBUG +unsigned long hlw_debug[HLW_SAMPLE_COUNT]; +#endif + +unsigned long hlw_cf_pulse_length = 0; +unsigned long hlw_cf_pulse_last_time = 0; +unsigned long hlw_cf_power_pulse_length = 0; + +unsigned long hlw_cf1_pulse_length = 0; +unsigned long hlw_cf1_pulse_last_time = 0; +unsigned long hlw_cf1_summed_pulse_length = 0; +unsigned long hlw_cf1_pulse_counter = 0; +unsigned long hlw_cf1_voltage_pulse_length = 0; +unsigned long hlw_cf1_current_pulse_length = 0; + +unsigned long hlw_energy_period_counter = 0; unsigned long hlw_power_ratio = 0; unsigned long hlw_voltage_ratio = 0; unsigned long hlw_current_ratio = 0; -unsigned long hlw_cf1_voltage_max_pulse_counter; -unsigned long hlw_cf1_current_max_pulse_counter; +uint8_t hlw_select_ui_flag = 0; +uint8_t hlw_ui_flag = 1; +uint8_t hlw_model_type = 0; +uint8_t hlw_load_off = 1; +uint8_t hlw_cf1_timer = 0; #ifndef USE_WS2812_DMA // Collides with Neopixelbus but solves exception void HlwCfInterrupt(void) ICACHE_RAM_ATTR; @@ -88,9 +95,12 @@ void HlwCf1Interrupt(void) // Service Voltage and Current hlw_cf1_pulse_last_time = us; 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_summed_pulse_length += hlw_cf1_pulse_length; +#ifdef HLW_DEBUG + hlw_debug[hlw_cf1_pulse_counter] = hlw_cf1_pulse_length; +#endif hlw_cf1_pulse_counter++; - if (10 == hlw_cf1_pulse_counter) { - hlw_cf1_timer = 8; // We need up to ten samples within 1 second (low current could take up to 0.3 second) + if (HLW_SAMPLE_COUNT == hlw_cf1_pulse_counter) { + hlw_cf1_timer = 8; // We need up to HLW_SAMPLE_COUNT samples within 1 second (low current could take up to 0.3 second) } } } @@ -99,6 +109,7 @@ void HlwCf1Interrupt(void) // Service Voltage and Current void HlwEvery200ms(void) { + unsigned long cf1_pulse_length = 0; unsigned long hlw_w = 0; unsigned long hlw_u = 0; unsigned long hlw_i = 0; @@ -107,9 +118,10 @@ void HlwEvery200ms(void) hlw_cf_pulse_length = 0; // No load for some time hlw_load_off = 1; } + hlw_cf_power_pulse_length = hlw_cf_pulse_length; - if (hlw_cf_pulse_length && energy_power_on && !hlw_load_off) { - hlw_w = (hlw_power_ratio * Settings.energy_power_calibration) / hlw_cf_pulse_length; + if (hlw_cf_power_pulse_length && energy_power_on && !hlw_load_off) { + hlw_w = (hlw_power_ratio * Settings.energy_power_calibration) / hlw_cf_power_pulse_length; // W *10 energy_active_power = (float)hlw_w / 10; } else { energy_active_power = 0; @@ -122,27 +134,43 @@ void HlwEvery200ms(void) digitalWrite(pin[GPIO_NRG_SEL], hlw_select_ui_flag); if (hlw_cf1_pulse_counter) { - hlw_cf1_pulse_length = hlw_cf1_summed_pulse_length / hlw_cf1_pulse_counter; - } else { - hlw_cf1_pulse_length = 0; + cf1_pulse_length = hlw_cf1_summed_pulse_length / hlw_cf1_pulse_counter; } + +#ifdef HLW_DEBUG + // Debugging for calculating mean and median + char stemp[100]; + stemp[0] = '\0'; + for (uint8_t i = 0; i < hlw_cf1_pulse_counter; i++) { + snprintf_P(stemp, sizeof(stemp), PSTR("%s %d"), stemp, hlw_debug[i]); + } + for (uint8_t i = 0; i < hlw_cf1_pulse_counter; i++) { + for (uint8_t j = i + 1; j < hlw_cf1_pulse_counter; j++) { + if (hlw_debug[i] > hlw_debug[j]) { // Sort ascending + std::swap(hlw_debug[i], hlw_debug[j]); + } + } + } + unsigned long median = hlw_debug[(hlw_cf1_pulse_counter +1) / 2]; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("NRG: power %d, ui %d, cnt %d, smpl%s, sum %d, mean %d, median %d"), + hlw_cf_power_pulse_length, hlw_select_ui_flag, hlw_cf1_pulse_counter, stemp, hlw_cf1_summed_pulse_length, cf1_pulse_length, median); +#endif + if (hlw_select_ui_flag == hlw_ui_flag) { - hlw_cf1_voltage_pulse_length = hlw_cf1_pulse_length; - hlw_cf1_voltage_max_pulse_counter = hlw_cf1_pulse_counter; + hlw_cf1_voltage_pulse_length = cf1_pulse_length; if (hlw_cf1_voltage_pulse_length && energy_power_on) { // If powered on always provide voltage - hlw_u = (hlw_voltage_ratio * Settings.energy_voltage_calibration) / hlw_cf1_voltage_pulse_length; + hlw_u = (hlw_voltage_ratio * Settings.energy_voltage_calibration) / hlw_cf1_voltage_pulse_length; // V *10 energy_voltage = (float)hlw_u / 10; } else { energy_voltage = 0; } } else { - hlw_cf1_current_pulse_length = hlw_cf1_pulse_length; - hlw_cf1_current_max_pulse_counter = hlw_cf1_pulse_counter; + hlw_cf1_current_pulse_length = cf1_pulse_length; if (hlw_cf1_current_pulse_length && energy_active_power) { // No current if no power being consumed - hlw_i = (hlw_current_ratio * Settings.energy_current_calibration) / hlw_cf1_current_pulse_length; + hlw_i = (hlw_current_ratio * Settings.energy_current_calibration) / hlw_cf1_current_pulse_length; // mA energy_current = (float)hlw_i / 1000; } else { energy_current = 0; @@ -186,45 +214,29 @@ void HlwSnsInit(void) hlw_current_ratio = HLW_IREF; } - hlw_cf_pulse_length = 0; - hlw_cf_pulse_last_time = 0; - hlw_cf1_pulse_length = 0; - hlw_cf1_pulse_last_time = 0; - hlw_cf1_voltage_pulse_length = 0; - hlw_cf1_current_pulse_length = 0; - hlw_cf1_voltage_max_pulse_counter = 0; - hlw_cf1_current_max_pulse_counter = 0; - - hlw_load_off = 1; - hlw_energy_period_counter = 0; - - hlw_select_ui_flag = 0; // Voltage; - pinMode(pin[GPIO_NRG_SEL], OUTPUT); digitalWrite(pin[GPIO_NRG_SEL], hlw_select_ui_flag); pinMode(pin[GPIO_NRG_CF1], INPUT_PULLUP); attachInterrupt(pin[GPIO_NRG_CF1], HlwCf1Interrupt, FALLING); pinMode(pin[GPIO_HLW_CF], INPUT_PULLUP); attachInterrupt(pin[GPIO_HLW_CF], HlwCfInterrupt, FALLING); - - hlw_cf1_timer = 0; } void HlwDrvInit(void) { if (!energy_flg) { - hlw_model_type = 0; + hlw_model_type = 0; // HLW8012 if (pin[GPIO_HJL_CF] < 99) { pin[GPIO_HLW_CF] = pin[GPIO_HJL_CF]; pin[GPIO_HJL_CF] = 99; - hlw_model_type = 1; + hlw_model_type = 1; // HJL-01/BL0937 } - hlw_ui_flag = 1; + hlw_ui_flag = 1; // Voltage on high if (pin[GPIO_NRG_SEL_INV] < 99) { pin[GPIO_NRG_SEL] = pin[GPIO_NRG_SEL_INV]; pin[GPIO_NRG_SEL_INV] = 99; - hlw_ui_flag = 0; + hlw_ui_flag = 0; // Voltage on low } if ((pin[GPIO_NRG_SEL] < 99) && (pin[GPIO_NRG_CF1] < 99) && (pin[GPIO_HLW_CF] < 99)) { // HLW8012 or HJL-01 based device @@ -233,13 +245,16 @@ void HlwDrvInit(void) } } -boolean HlwCommand(void) +bool HlwCommand(void) { - boolean serviced = true; + bool serviced = true; - if (CMND_POWERSET == energy_command_code) { - if (XdrvMailbox.data_len && hlw_cf_pulse_length) { - Settings.energy_power_calibration = ((unsigned long)(CharToDouble(XdrvMailbox.data) * 10) * hlw_cf_pulse_length) / hlw_power_ratio; + if ((CMND_POWERCAL == energy_command_code) || (CMND_VOLTAGECAL == energy_command_code) || (CMND_CURRENTCAL == energy_command_code)) { + // Service in xdrv_03_energy.ino + } + else if (CMND_POWERSET == energy_command_code) { + if (XdrvMailbox.data_len && hlw_cf_power_pulse_length) { + Settings.energy_power_calibration = ((unsigned long)(CharToDouble(XdrvMailbox.data) * 10) * hlw_cf_power_pulse_length) / hlw_power_ratio; } } else if (CMND_VOLTAGESET == energy_command_code) { @@ -261,7 +276,7 @@ boolean HlwCommand(void) * Interface \*********************************************************************************************/ -int Xnrg01(byte function) +int Xnrg01(uint8_t function) { int result = 0; diff --git a/sonoff/xnrg_02_cse7766.ino b/sonoff/xnrg_02_cse7766.ino index 318fb02a7..17fda1f44 100644 --- a/sonoff/xnrg_02_cse7766.ino +++ b/sonoff/xnrg_02_cse7766.ino @@ -1,7 +1,7 @@ /* xnrg_02_cse7766.ino - CSE7766 energy sensor support for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends + Copyright (C) 2019 Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -41,7 +41,7 @@ uint8_t cse_receive_flag = 0; long voltage_cycle = 0; long current_cycle = 0; long power_cycle = 0; -unsigned long power_cycle_first = 0; +long power_cycle_first = 0; long cf_pulses = 0; long cf_pulses_last_time = CSE_PULSES_NOT_INITIALIZED; uint8_t cse_power_invalid = CSE_MAX_INVALID_POWER; @@ -137,7 +137,7 @@ bool CseSerialInput(void) AddLogSerial(LOG_LEVEL_DEBUG_MORE); uint8_t checksum = 0; - for (byte i = 2; i < 23; i++) { checksum += serial_in_buffer[i]; } + for (uint8_t i = 2; i < 23; i++) { checksum += serial_in_buffer[i]; } if (checksum == serial_in_buffer[23]) { CseReceived(); cse_receive_flag = 0; @@ -191,7 +191,7 @@ void CseEverySecond(void) void CseDrvInit(void) { if (!energy_flg) { - if ((SONOFF_S31 == Settings.module) || (SONOFF_POW_R2 == Settings.module)) { // Sonoff S31 or Sonoff Pow R2 + if ((3 == pin[GPIO_CSE7766_RX]) && (1 == pin[GPIO_CSE7766_TX])) { // As it uses 8E1 currently only hardware serial is supported baudrate = 4800; serial_config = SERIAL_8E1; energy_flg = XNRG_02; @@ -199,23 +199,23 @@ void CseDrvInit(void) } } -boolean CseCommand(void) +bool CseCommand(void) { - boolean serviced = true; + bool serviced = true; if (CMND_POWERSET == energy_command_code) { if (XdrvMailbox.data_len && power_cycle) { - Settings.energy_power_calibration = ((unsigned long)CharToDouble(XdrvMailbox.data) * power_cycle) / CSE_PREF; + Settings.energy_power_calibration = (unsigned long)(CharToDouble(XdrvMailbox.data) * power_cycle) / CSE_PREF; } } else if (CMND_VOLTAGESET == energy_command_code) { if (XdrvMailbox.data_len && voltage_cycle) { - Settings.energy_voltage_calibration = ((unsigned long)CharToDouble(XdrvMailbox.data) * voltage_cycle) / CSE_UREF; + Settings.energy_voltage_calibration = (unsigned long)(CharToDouble(XdrvMailbox.data) * voltage_cycle) / CSE_UREF; } } else if (CMND_CURRENTSET == energy_command_code) { if (XdrvMailbox.data_len && current_cycle) { - Settings.energy_current_calibration = ((unsigned long)CharToDouble(XdrvMailbox.data) * current_cycle) / 1000; + Settings.energy_current_calibration = (unsigned long)(CharToDouble(XdrvMailbox.data) * current_cycle) / 1000; } } else serviced = false; // Unknown command @@ -227,7 +227,7 @@ boolean CseCommand(void) * Interface \*********************************************************************************************/ -int Xnrg02(byte function) +int Xnrg02(uint8_t function) { int result = 0; diff --git a/sonoff/xnrg_03_pzem004t.ino b/sonoff/xnrg_03_pzem004t.ino index 850131762..a15d61ba1 100644 --- a/sonoff/xnrg_03_pzem004t.ino +++ b/sonoff/xnrg_03_pzem004t.ino @@ -1,7 +1,7 @@ /* xnrg_03_pzem004t.ino - PZEM004T energy sensor support for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends + Copyright (C) 2019 Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -32,7 +32,7 @@ #include -TasmotaSerial *PzemSerial; +TasmotaSerial *PzemSerial = NULL; #define PZEM_VOLTAGE (uint8_t)0xB0 #define RESP_VOLTAGE (uint8_t)0xA0 @@ -122,7 +122,7 @@ bool PzemRecieve(uint8_t resp, float *data) } } - AddLogSerial(LOG_LEVEL_DEBUG_MORE, buffer, len); + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, len); if (len != sizeof(PZEMCommand)) { // AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "Pzem comms timeout")); @@ -226,7 +226,7 @@ void PzemDrvInit(void) * Interface \*********************************************************************************************/ -int Xnrg03(byte function) +int Xnrg03(uint8_t function) { int result = 0; @@ -239,7 +239,7 @@ int Xnrg03(byte function) PzemSnsInit(); break; case FUNC_EVERY_200_MSECOND: - PzemEvery200ms(); + if (PzemSerial) { PzemEvery200ms(); } break; } } diff --git a/sonoff/xnrg_04_mcp39f501.ino b/sonoff/xnrg_04_mcp39f501.ino index d962ef204..d878d9e8b 100644 --- a/sonoff/xnrg_04_mcp39f501.ino +++ b/sonoff/xnrg_04_mcp39f501.ino @@ -1,7 +1,7 @@ /* xnrg_04_mcp39f501.ino - MCP39F501 energy sensor support for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends + Copyright (C) 2019 Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -28,6 +28,7 @@ #define XNRG_04 4 +#define MCP_BAUDRATE 4800 #define MCP_TIMEOUT 4 #define MCP_CALIBRATION_TIMEOUT 2 @@ -63,6 +64,11 @@ #define MCP_FREQUENCY_GAIN_BASE 0x00AE #define MCP_FREQUENCY_LEN 4 +#define MCP_BUFFER_SIZE 60 + +#include +TasmotaSerial *McpSerial = NULL; + typedef struct mcp_cal_registers_type { uint16_t gain_current_rms; uint16_t gain_voltage_rms; @@ -86,6 +92,8 @@ typedef struct mcp_cal_registers_type { uint16_t accumulation_interval; } mcp_cal_registers_type; +char *mcp_buffer = NULL; +unsigned long mcp_window = 0; unsigned long mcp_kWhcounter = 0; uint32_t mcp_system_configuration = 0x03000000; uint32_t mcp_active_power; @@ -100,6 +108,7 @@ uint8_t mcp_calibration_active = 0; uint8_t mcp_init = 0; uint8_t mcp_timeout = 0; uint8_t mcp_calibrate = 0; +uint8_t mcp_byte_counter = 0; /*********************************************************************************************\ * Olimex tools @@ -112,7 +121,7 @@ uint8_t McpChecksum(uint8_t *data) uint8_t offset = 0; uint8_t len = data[1] -1; - for (byte i = offset; i < len; i++) { checksum += data[i]; } + for (uint8_t i = offset; i < len; i++) { checksum += data[i]; } return checksum; } @@ -121,7 +130,7 @@ unsigned long McpExtractInt(char *data, uint8_t offset, uint8_t size) unsigned long result = 0; unsigned long pow = 1; - for (byte i = 0; i < size; i++) { + for (uint8_t i = 0; i < size; i++) { result = result + (uint8_t)data[offset + i] * pow; pow = pow * 256; } @@ -130,7 +139,7 @@ unsigned long McpExtractInt(char *data, uint8_t offset, uint8_t size) void McpSetInt(unsigned long value, uint8_t *data, uint8_t offset, size_t size) { - for (byte i = 0; i < size; i++) { + for (uint8_t i = 0; i < size; i++) { data[offset + i] = ((value >> (i * 8)) & 0xFF); } } @@ -143,10 +152,10 @@ void McpSend(uint8_t *data) data[0] = MCP_START_FRAME; data[data[1] -1] = McpChecksum(data); -// AddLogSerial(LOG_LEVEL_DEBUG_MORE, data, data[1]); +// AddLogBuffer(LOG_LEVEL_DEBUG_MORE, data, data[1]); - for (byte i = 0; i < data[1]; i++) { - Serial.write(data[i]); + for (uint8_t i = 0; i < data[1]; i++) { + McpSerial->write(data[i]); } } @@ -162,7 +171,7 @@ void McpGetAddress(void) void McpAddressReceive(void) { // 06 05 004D 58 - mcp_address = serial_in_buffer[3]; + mcp_address = mcp_buffer[3]; } /********************************************************************************************/ @@ -183,26 +192,26 @@ void McpParseCalibration(void) mcp_cal_registers_type cal_registers; // 06 37 C882 B6AD 0781 9273 06000000 00000000 00000000 0000 D3FF 0300 00000003 9204 120C1300 204E0000 9808 E0AB0000 D9940000 0200 24 - cal_registers.gain_current_rms = McpExtractInt(serial_in_buffer, 2, 2); - cal_registers.gain_voltage_rms = McpExtractInt(serial_in_buffer, 4, 2); - cal_registers.gain_active_power = McpExtractInt(serial_in_buffer, 6, 2); - cal_registers.gain_reactive_power = McpExtractInt(serial_in_buffer, 8, 2); - cal_registers.offset_current_rms = McpExtractInt(serial_in_buffer, 10, 4); - cal_registers.offset_active_power = McpExtractInt(serial_in_buffer, 14, 4); - cal_registers.offset_reactive_power = McpExtractInt(serial_in_buffer, 18, 4); - cal_registers.dc_offset_current = McpExtractInt(serial_in_buffer, 22, 2); - cal_registers.phase_compensation = McpExtractInt(serial_in_buffer, 24, 2); - cal_registers.apparent_power_divisor = McpExtractInt(serial_in_buffer, 26, 2); + cal_registers.gain_current_rms = McpExtractInt(mcp_buffer, 2, 2); + cal_registers.gain_voltage_rms = McpExtractInt(mcp_buffer, 4, 2); + cal_registers.gain_active_power = McpExtractInt(mcp_buffer, 6, 2); + cal_registers.gain_reactive_power = McpExtractInt(mcp_buffer, 8, 2); + cal_registers.offset_current_rms = McpExtractInt(mcp_buffer, 10, 4); + cal_registers.offset_active_power = McpExtractInt(mcp_buffer, 14, 4); + cal_registers.offset_reactive_power = McpExtractInt(mcp_buffer, 18, 4); + cal_registers.dc_offset_current = McpExtractInt(mcp_buffer, 22, 2); + cal_registers.phase_compensation = McpExtractInt(mcp_buffer, 24, 2); + cal_registers.apparent_power_divisor = McpExtractInt(mcp_buffer, 26, 2); - cal_registers.system_configuration = McpExtractInt(serial_in_buffer, 28, 4); - cal_registers.dio_configuration = McpExtractInt(serial_in_buffer, 32, 2); - cal_registers.range = McpExtractInt(serial_in_buffer, 34, 4); + cal_registers.system_configuration = McpExtractInt(mcp_buffer, 28, 4); + cal_registers.dio_configuration = McpExtractInt(mcp_buffer, 32, 2); + cal_registers.range = McpExtractInt(mcp_buffer, 34, 4); - cal_registers.calibration_current = McpExtractInt(serial_in_buffer, 38, 4); - cal_registers.calibration_voltage = McpExtractInt(serial_in_buffer, 42, 2); - cal_registers.calibration_active_power = McpExtractInt(serial_in_buffer, 44, 4); - cal_registers.calibration_reactive_power = McpExtractInt(serial_in_buffer, 48, 4); - cal_registers.accumulation_interval = McpExtractInt(serial_in_buffer, 52, 2); + cal_registers.calibration_current = McpExtractInt(mcp_buffer, 38, 4); + cal_registers.calibration_voltage = McpExtractInt(mcp_buffer, 42, 2); + cal_registers.calibration_active_power = McpExtractInt(mcp_buffer, 44, 4); + cal_registers.calibration_reactive_power = McpExtractInt(mcp_buffer, 48, 4); + cal_registers.accumulation_interval = McpExtractInt(mcp_buffer, 52, 2); if (mcp_calibrate & MCP_CALIBRATE_POWER) { cal_registers.calibration_active_power = Settings.energy_power_calibration; @@ -373,8 +382,8 @@ void McpGetFrequency(void) void McpParseFrequency(void) { // 06 07 C350 8000 A0 - uint16_t line_frequency_ref = serial_in_buffer[2] * 256 + serial_in_buffer[3]; - uint16_t gain_line_frequency = serial_in_buffer[4] * 256 + serial_in_buffer[5]; + uint16_t line_frequency_ref = mcp_buffer[2] * 256 + mcp_buffer[3]; + uint16_t gain_line_frequency = mcp_buffer[4] * 256 + mcp_buffer[5]; if (mcp_calibrate & MCP_CALIBRATE_FREQUENCY) { line_frequency_ref = Settings.energy_frequency_calibration; @@ -438,12 +447,12 @@ void McpParseData(void) // 06 19 CE 18 00 00 F2 08 3A 38 00 00 66 00 00 00 93 38 00 00 36 7F 9A C6 B7 // Ak Ln Current---- Volt- ActivePower ReActivePow ApparentPow Factr Frequ Ck - mcp_current_rms = McpExtractInt(serial_in_buffer, 2, 4); - mcp_voltage_rms = McpExtractInt(serial_in_buffer, 6, 2); - mcp_active_power = McpExtractInt(serial_in_buffer, 8, 4); -// mcp_reactive_power = McpExtractInt(serial_in_buffer, 12, 4); -// mcp_power_factor = McpExtractInt(serial_in_buffer, 20, 2); - mcp_line_frequency = McpExtractInt(serial_in_buffer, 22, 2); + mcp_current_rms = McpExtractInt(mcp_buffer, 2, 4); + mcp_voltage_rms = McpExtractInt(mcp_buffer, 6, 2); + mcp_active_power = McpExtractInt(mcp_buffer, 8, 4); +// mcp_reactive_power = McpExtractInt(mcp_buffer, 12, 4); +// mcp_power_factor = McpExtractInt(mcp_buffer, 20, 2); + mcp_line_frequency = McpExtractInt(mcp_buffer, 22, 2); if (energy_power_on) { // Powered on energy_frequency = (float)mcp_line_frequency / 1000; @@ -464,49 +473,53 @@ void McpParseData(void) /********************************************************************************************/ -bool McpSerialInput(void) +void McpSerialInput(void) { - serial_in_buffer[serial_in_byte_counter++] = serial_in_byte; - unsigned long start = millis(); - while (millis() - start < 20) { + while ((McpSerial->available()) && (mcp_byte_counter < MCP_BUFFER_SIZE)) { yield(); - if (Serial.available()) { - serial_in_buffer[serial_in_byte_counter++] = Serial.read(); - start = millis(); - } + mcp_buffer[mcp_byte_counter++] = McpSerial->read(); + mcp_window = millis(); } - AddLogSerial(LOG_LEVEL_DEBUG_MORE); + // Ignore until non received after 2 chars (= 12 bits/char) time + if ((mcp_byte_counter) && (millis() - mcp_window > (24000 / MCP_BAUDRATE) +1)) { + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, (uint8_t*)mcp_buffer, mcp_byte_counter); - if (1 == serial_in_byte_counter) { - if (MCP_ERROR_CRC == serial_in_buffer[0]) { -// AddLog_P(LOG_LEVEL_DEBUG, PSTR("MCP: Send " D_CHECKSUM_FAILURE)); - mcp_timeout = 0; + if (MCP_BUFFER_SIZE == mcp_byte_counter) { +// AddLog_P(LOG_LEVEL_DEBUG, PSTR("MCP: Overflow")); } - else if (MCP_ERROR_NAK == serial_in_buffer[0]) { -// AddLog_P(LOG_LEVEL_DEBUG, PSTR("MCP: NAck")); - mcp_timeout = 0; - } - } - else if (MCP_ACK_FRAME == serial_in_buffer[0]) { - if (serial_in_byte_counter == serial_in_buffer[1]) { - - if (McpChecksum((uint8_t *)serial_in_buffer) != serial_in_buffer[serial_in_byte_counter -1]) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("MCP: " D_CHECKSUM_FAILURE)); - } else { - if (5 == serial_in_buffer[1]) { McpAddressReceive(); } - if (25 == serial_in_buffer[1]) { McpParseData(); } - if (MCP_CALIBRATION_LEN + 3 == serial_in_buffer[1]) { McpParseCalibration(); } - if (MCP_FREQUENCY_LEN + 3 == serial_in_buffer[1]) { McpParseFrequency(); } + else if (1 == mcp_byte_counter) { + if (MCP_ERROR_CRC == mcp_buffer[0]) { +// AddLog_P(LOG_LEVEL_DEBUG, PSTR("MCP: Send " D_CHECKSUM_FAILURE)); + mcp_timeout = 0; + } + else if (MCP_ERROR_NAK == mcp_buffer[0]) { +// AddLog_P(LOG_LEVEL_DEBUG, PSTR("MCP: NAck")); + mcp_timeout = 0; } - } - mcp_timeout = 0; + else if (MCP_ACK_FRAME == mcp_buffer[0]) { + if (mcp_byte_counter == mcp_buffer[1]) { + + if (McpChecksum((uint8_t *)mcp_buffer) != mcp_buffer[mcp_byte_counter -1]) { + AddLog_P(LOG_LEVEL_DEBUG, PSTR("MCP: " D_CHECKSUM_FAILURE)); + } else { + if (5 == mcp_buffer[1]) { McpAddressReceive(); } + if (25 == mcp_buffer[1]) { McpParseData(); } + if (MCP_CALIBRATION_LEN + 3 == mcp_buffer[1]) { McpParseCalibration(); } + if (MCP_FREQUENCY_LEN + 3 == mcp_buffer[1]) { McpParseFrequency(); } + } + + } + mcp_timeout = 0; + } + else if (MCP_SINGLE_WIRE == mcp_buffer[0]) { + mcp_timeout = 0; + } + + mcp_byte_counter = 0; + McpSerial->flush(); } - else if (MCP_SINGLE_WIRE == serial_in_buffer[0]) { - mcp_timeout = 0; - } - return 1; } /********************************************************************************************/ @@ -543,17 +556,31 @@ void McpEverySecond(void) void McpSnsInit(void) { - SetSeriallog(LOG_LEVEL_NONE); // Free serial interface from logging interference - digitalWrite(15, 1); // GPIO15 - MCP enable + // Software serial init needs to be done here as earlier (serial) interrupts may lead to Exceptions + McpSerial = new TasmotaSerial(pin[GPIO_MCP39F5_RX], pin[GPIO_MCP39F5_TX], 1); + if (McpSerial->begin(MCP_BAUDRATE)) { + if (McpSerial->hardwareSerial()) { + ClaimSerial(); + mcp_buffer = serial_in_buffer; // Use idle serial buffer to save RAM + } else { + mcp_buffer = (char*)(malloc(MCP_BUFFER_SIZE)); + } + if (pin[GPIO_MCP39F5_RST] < 99) { + digitalWrite(pin[GPIO_MCP39F5_RST], 1); // MCP enable + } + } else { + energy_flg = ENERGY_NONE; + } } void McpDrvInit(void) { if (!energy_flg) { - if (SHELLY2 == Settings.module) { - pinMode(15, OUTPUT); - digitalWrite(15, 0); // GPIO15 - MCP disable - Reset Delta Sigma ADC's - baudrate = 4800; + if ((pin[GPIO_MCP39F5_RX] < 99) && (pin[GPIO_MCP39F5_TX] < 99)) { + if (pin[GPIO_MCP39F5_RST] < 99) { + pinMode(pin[GPIO_MCP39F5_RST], OUTPUT); + digitalWrite(pin[GPIO_MCP39F5_RST], 0); // MCP disable - Reset Delta Sigma ADC's + } mcp_calibrate = 0; mcp_timeout = 2; // Initial wait mcp_init = 2; // Initial setup steps @@ -562,9 +589,9 @@ void McpDrvInit(void) } } -boolean McpCommand(void) +bool McpCommand(void) { - boolean serviced = true; + bool serviced = true; unsigned long value = 0; if (CMND_POWERSET == energy_command_code) { @@ -616,7 +643,7 @@ boolean McpCommand(void) * Interface \*********************************************************************************************/ -int Xnrg04(byte function) +int Xnrg04(uint8_t function) { int result = 0; @@ -628,15 +655,15 @@ int Xnrg04(byte function) case FUNC_INIT: McpSnsInit(); break; + case FUNC_LOOP: + if (McpSerial) { McpSerialInput(); } + break; case FUNC_EVERY_SECOND: - McpEverySecond(); + if (McpSerial) { McpEverySecond(); } break; case FUNC_COMMAND: result = McpCommand(); break; - case FUNC_SERIAL: - result = McpSerialInput(); - break; } } return result; diff --git a/sonoff/xnrg_05_pzem_ac.ino b/sonoff/xnrg_05_pzem_ac.ino index 7c145384a..b4ecea288 100644 --- a/sonoff/xnrg_05_pzem_ac.ino +++ b/sonoff/xnrg_05_pzem_ac.ino @@ -1,7 +1,7 @@ /* xnrg_05_pzem_ac.ino - PZEM-014,016 Modbus AC energy sensor support for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends + Copyright (C) 2019 Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -46,11 +46,10 @@ void PzemAcEverySecond(void) uint8_t buffer[26]; uint8_t error = PzemAcModbus->ReceiveBuffer(buffer, 10); - AddLogSerial(LOG_LEVEL_DEBUG_MORE, buffer, (buffer[2]) ? buffer[2] +5 : sizeof(buffer)); + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, (buffer[2]) ? buffer[2] +5 : sizeof(buffer)); if (error) { - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_DEBUG "PzemAc response error %d"), error); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "PzemAc response error %d"), error); } else { // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 // 01 04 14 08 D1 00 6C 00 00 00 F4 00 00 00 26 00 00 01 F4 00 64 00 00 51 34 @@ -104,7 +103,7 @@ void PzemAcDrvInit(void) * Interface \*********************************************************************************************/ -int Xnrg05(byte function) +int Xnrg05(uint8_t function) { int result = 0; diff --git a/sonoff/xnrg_06_pzem_dc.ino b/sonoff/xnrg_06_pzem_dc.ino index c12f401d4..2bb2bc4a2 100644 --- a/sonoff/xnrg_06_pzem_dc.ino +++ b/sonoff/xnrg_06_pzem_dc.ino @@ -1,7 +1,7 @@ /* xnrg_06_pzem_dc.ino - PZEM-003,017 Modbus DC energy sensor support for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends + Copyright (C) 2019 Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -46,11 +46,10 @@ void PzemDcEverySecond(void) uint8_t buffer[22]; uint8_t error = PzemDcModbus->ReceiveBuffer(buffer, 8); - AddLogSerial(LOG_LEVEL_DEBUG_MORE, buffer, (buffer[2]) ? buffer[2] +5 : sizeof(buffer)); + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, (buffer[2]) ? buffer[2] +5 : sizeof(buffer)); if (error) { - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_DEBUG "PzemDc response error %d"), error); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "PzemDc response error %d"), error); } else { // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 // 01 04 10 05 40 00 0A 00 0D 00 00 00 02 00 00 00 00 00 00 D6 29 @@ -103,7 +102,7 @@ void PzemDcDrvInit(void) * Interface \*********************************************************************************************/ -int Xnrg06(byte function) +int Xnrg06(uint8_t function) { int result = 0; diff --git a/sonoff/xnrg_interface.ino b/sonoff/xnrg_interface.ino index cea14e47c..61ff34e24 100644 --- a/sonoff/xnrg_interface.ino +++ b/sonoff/xnrg_interface.ino @@ -1,7 +1,7 @@ /* xnrg_interface.ino - Energy driver interface support for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends inspired by ESPEasy + Copyright (C) 2019 Theo Arends inspired by ESPEasy This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,10 +17,12 @@ along with this program. If not, see . */ +#ifdef USE_ENERGY_SENSOR + #ifdef XFUNC_PTR_IN_ROM -int (* const xnrg_func_ptr[])(byte) PROGMEM = { // Energy driver Function Pointers +int (* const xnrg_func_ptr[])(uint8_t) PROGMEM = { // Energy driver Function Pointers #else -int (* const xnrg_func_ptr[])(byte) = { // Energy driver Function Pointers +int (* const xnrg_func_ptr[])(uint8_t) = { // Energy driver Function Pointers #endif #ifdef XNRG_01 @@ -90,13 +92,20 @@ int (* const xnrg_func_ptr[])(byte) = { // Energy driver Function Pointers const uint8_t xnrg_present = sizeof(xnrg_func_ptr) / sizeof(xnrg_func_ptr[0]); // Number of drivers found -int XnrgCall(byte Function) +int XnrgCall(uint8_t Function) { int result = 0; - for (byte x = 0; x < xnrg_present; x++) { + for (uint8_t x = 0; x < xnrg_present; x++) { result = xnrg_func_ptr[x](Function); - if (result) break; + + if (result && ((FUNC_SERIAL == Function) || + (FUNC_COMMAND == Function) + )) { + break; + } } return result; } + +#endif // USE_ENERGY_SENSOR diff --git a/sonoff/xplg_wemohue.ino b/sonoff/xplg_wemohue.ino index 143d0e6ba..2a7b60b80 100644 --- a/sonoff/xplg_wemohue.ino +++ b/sonoff/xplg_wemohue.ino @@ -1,7 +1,7 @@ /* xplg_wemohue.ino - wemo and hue support for Sonoff-Tasmota - Copyright (C) 2018 Heiko Krupp and Theo Arends + Copyright (C) 2019 Heiko Krupp and Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -31,7 +31,7 @@ #include Ticker TickerMSearch; -boolean udp_connected = false; +bool udp_connected = false; char packet_buffer[UDP_BUFFER_SIZE]; // buffer to hold incoming UDP packet IPAddress ipMulticast(239,255,255,250); // Simple Service Discovery Protocol (SSDP) @@ -95,9 +95,8 @@ void WemoRespondToMSearch(int echo_type) } else { snprintf_P(message, sizeof(message), PSTR(D_FAILED_TO_SEND_RESPONSE)); } - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_UPNP D_WEMO " " D_JSON_TYPE " %d, %s " D_TO " %s:%d"), + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPNP D_WEMO " " D_JSON_TYPE " %d, %s " D_TO " %s:%d"), echo_type, message, udp_remote_ip.toString().c_str(), udp_remote_port); - AddLog(LOG_LEVEL_DEBUG); udp_response_mutex = false; } @@ -186,9 +185,8 @@ void HueRespondToMSearch(void) } else { snprintf_P(message, sizeof(message), PSTR(D_FAILED_TO_SEND_RESPONSE)); } - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_UPNP D_HUE " %s " D_TO " %s:%d"), + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPNP D_HUE " %s " D_TO " %s:%d"), message, udp_remote_ip.toString().c_str(), udp_remote_port); - AddLog(LOG_LEVEL_DEBUG); udp_response_mutex = false; } @@ -197,7 +195,7 @@ void HueRespondToMSearch(void) * Belkin WeMo and Philips Hue bridge UDP multicast support \*********************************************************************************************/ -boolean UdpDisconnect(void) +bool UdpDisconnect(void) { if (udp_connected) { WiFiUDP::stopAll(); @@ -207,7 +205,7 @@ boolean UdpDisconnect(void) return udp_connected; } -boolean UdpConnect(void) +bool UdpConnect(void) { if (!udp_connected) { if (PortUdp.beginMulticast(WiFi.localIP(), ipMulticast, port_multicast)) { @@ -302,7 +300,7 @@ const char WEMO_EVENTSERVICE_XML[] PROGMEM = "" "" "BinaryState" - "Boolean" + "bool" "0" "" "" @@ -405,21 +403,21 @@ void HandleUpnpEvent(void) state_xml.replace(F("Set"), F("Get")); } state_xml.replace("{x1", String(bitRead(power, devices_present -1))); - WebServer->send(200, FPSTR(HDR_CTYPE_XML), state_xml); + WSSend(200, CT_XML, state_xml); } void HandleUpnpService(void) { AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, PSTR(D_WEMO_EVENT_SERVICE)); - WebServer->send(200, FPSTR(HDR_CTYPE_PLAIN), FPSTR(WEMO_EVENTSERVICE_XML)); + WSSend(200, CT_PLAIN, FPSTR(WEMO_EVENTSERVICE_XML)); } void HandleUpnpMetaService(void) { AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, PSTR(D_WEMO_META_SERVICE)); - WebServer->send(200, FPSTR(HDR_CTYPE_PLAIN), FPSTR(WEMO_METASERVICE_XML)); + WSSend(200, CT_PLAIN, FPSTR(WEMO_METASERVICE_XML)); } void HandleUpnpSetupWemo(void) @@ -430,7 +428,7 @@ void HandleUpnpSetupWemo(void) setup_xml.replace("{x1", Settings.friendlyname[0]); setup_xml.replace("{x2", WemoUuid()); setup_xml.replace("{x3", WemoSerialnumber()); - WebServer->send(200, FPSTR(HDR_CTYPE_XML), setup_xml); + WSSend(200, CT_XML, setup_xml); } /*********************************************************************************************\ @@ -532,15 +530,14 @@ void HandleUpnpSetupHue(void) description_xml.replace("{x1", WiFi.localIP().toString()); description_xml.replace("{x2", HueUuid()); description_xml.replace("{x3", HueSerialnumber()); - WebServer->send(200, FPSTR(HDR_CTYPE_XML), description_xml); + WSSend(200, CT_XML, description_xml); } void HueNotImplemented(String *path) { - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_HTTP D_HUE_API_NOT_IMPLEMENTED " (%s)"), path->c_str()); - AddLog(LOG_LEVEL_DEBUG_MORE); + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE_API_NOT_IMPLEMENTED " (%s)"), path->c_str()); - WebServer->send(200, FPSTR(HDR_CTYPE_JSON), "{}"); + WSSend(200, CT_JSON, "{}"); } void HueConfigResponse(String *response) @@ -559,12 +556,12 @@ void HueConfig(String *path) { String response = ""; HueConfigResponse(&response); - WebServer->send(200, FPSTR(HDR_CTYPE_JSON), response); + WSSend(200, CT_JSON, response); } bool g_gotct = false; -void HueLightStatus1(byte device, String *response) +void HueLightStatus1(uint8_t device, String *response) { float hue = 0; float sat = 0; @@ -584,7 +581,7 @@ void HueLightStatus1(byte device, String *response) response->replace("{m}", g_gotct?"ct":"hs"); } -void HueLightStatus2(byte device, String *response) +void HueLightStatus2(uint8_t device, String *response) { *response += FPSTR(HUE_LIGHTS_STATUS_JSON2); response->replace("{j1", Settings.friendlyname[device-1]); @@ -610,7 +607,7 @@ void HueGlobalConfig(String *path) response += F("},\"groups\":{},\"schedules\":{},\"config\":"); HueConfigResponse(&response); response += "}"; - WebServer->send(200, FPSTR(HDR_CTYPE_JSON), response); + WSSend(200, CT_JSON, response); } void HueAuthentication(String *path) @@ -618,7 +615,7 @@ void HueAuthentication(String *path) char response[38]; snprintf_P(response, sizeof(response), PSTR("[{\"success\":{\"username\":\"%s\"}}]"), GetHueUserId().c_str()); - WebServer->send(200, FPSTR(HDR_CTYPE_JSON), response); + WSSend(200, CT_JSON, response); } void HueLights(String *path) @@ -627,15 +624,16 @@ void HueLights(String *path) * http://sonoff/api/username/lights/1/state?1={"on":true,"hue":56100,"sat":254,"bri":254,"alert":"none","transitiontime":40} */ String response; - uint8_t device = 1; - uint16_t tmp = 0; + int code = 200; float bri = 0; float hue = 0; float sat = 0; + uint16_t tmp = 0; uint16_t ct = 0; bool resp = false; bool on = false; bool change = false; + uint8_t device = 1; uint8_t maxhue = (devices_present > MAX_FRIENDLYNAMES) ? MAX_FRIENDLYNAMES : devices_present; path->remove(0,path->indexOf("/lights")); // Remove until /lights @@ -651,7 +649,6 @@ void HueLights(String *path) } } response += "}"; - WebServer->send(200, FPSTR(HDR_CTYPE_JSON), response); } else if (path->endsWith("/state")) { // Got ID/state path->remove(0,8); // Remove /lights/ @@ -761,8 +758,6 @@ void HueLights(String *path) else { response = FPSTR(HUE_ERROR_JSON); } - - WebServer->send(200, FPSTR(HDR_CTYPE_JSON), response); } else if(path->indexOf("/lights/") >= 0) { // Got /lights/ID path->remove(0,8); // Remove /lights/ @@ -773,11 +768,12 @@ void HueLights(String *path) response += F("{\"state\":"); HueLightStatus1(device, &response); HueLightStatus2(device, &response); - WebServer->send(200, FPSTR(HDR_CTYPE_JSON), response); } else { - WebServer->send(406, FPSTR(HDR_CTYPE_JSON), "{}"); + response = "{}"; + code = 406; } + WSSend(code, CT_JSON, response); } void HueGroups(String *path) @@ -799,7 +795,7 @@ void HueGroups(String *path) response += F("}"); } - WebServer->send(200, FPSTR(HDR_CTYPE_JSON), response); + WSSend(200, CT_JSON, response); } void HandleHueApi(String *path) @@ -820,12 +816,10 @@ void HandleHueApi(String *path) path->remove(0, 4); // remove /api uint16_t apilen = path->length(); - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_HTTP D_HUE_API " (%s)"), path->c_str()); - AddLog(LOG_LEVEL_DEBUG_MORE); // HTP: Hue API (//lights/1/state) + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE_API " (%s)"), path->c_str()); // HTP: Hue API (//lights/1/state for (args = 0; args < WebServer->args(); args++) { String json = WebServer->arg(args); - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_HTTP D_HUE_POST_ARGS " (%s)"), json.c_str()); - AddLog(LOG_LEVEL_DEBUG_MORE); // HTP: Hue POST args ({"on":false}) + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE_POST_ARGS " (%s)"), json.c_str()); // HTP: Hue POST args ({"on":false}) } if (path->endsWith("/invalid/")) {} // Just ignore diff --git a/sonoff/xplg_ws2812.ino b/sonoff/xplg_ws2812.ino index 423c834ff..d230b5a94 100644 --- a/sonoff/xplg_ws2812.ino +++ b/sonoff/xplg_ws2812.ino @@ -1,7 +1,7 @@ /* xplg_ws2812.ino - ws2812 led string support for Sonoff-Tasmota - Copyright (C) 2018 Heiko Krupp and Theo Arends + Copyright (C) 2019 Heiko Krupp and Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -24,35 +24,27 @@ #include +#if (USE_WS2812_CTYPE == NEO_GRB) + typedef NeoGrbFeature selectedNeoFeatureType; +#elif (USE_WS2812_CTYPE == NEO_BRG) + typedef NeoBrgFeature selectedNeoFeatureType; +#elif (USE_WS2812_CTYPE == NEO_RBG) + typedef NeoRbgFeature selectedNeoFeatureType; +#elif (USE_WS2812_CTYPE == NEO_RGBW) + typedef NeoRgbwFeature selectedNeoFeatureType; +#elif (USE_WS2812_CTYPE == NEO_GRBW) + typedef NeoGrbwFeature selectedNeoFeatureType; +#else // USE_WS2812_CTYPE + typedef NeoRgbFeature selectedNeoFeatureType; +#endif // USE_WS2812_CTYPE + + #ifdef USE_WS2812_DMA -#if (USE_WS2812_CTYPE == NEO_GRB) - NeoPixelBus *strip = NULL; -#elif (USE_WS2812_CTYPE == NEO_BRG) - NeoPixelBus *strip = NULL; -#elif (USE_WS2812_CTYPE == NEO_RBG) - NeoPixelBus *strip = NULL; -#elif (USE_WS2812_CTYPE == NEO_RGBW) - NeoPixelBus *strip = NULL; -#elif (USE_WS2812_CTYPE == NEO_GRBW) - NeoPixelBus *strip = NULL; -#else // USE_WS2812_CTYPE - NeoPixelBus *strip = NULL; -#endif // USE_WS2812_CTYPE + typedef Neo800KbpsMethod selectedNeoSpeedType; #else // USE_WS2812_DMA -#if (USE_WS2812_CTYPE == NEO_GRB) - NeoPixelBus *strip = NULL; -#elif (USE_WS2812_CTYPE == NEO_BRG) - NeoPixelBus *strip = NULL; -#elif (USE_WS2812_CTYPE == NEO_RBG) - NeoPixelBus *strip = NULL; -#elif (USE_WS2812_CTYPE == NEO_RGBW) - NeoPixelBus *strip = NULL; -#elif (USE_WS2812_CTYPE == NEO_GRBW) - NeoPixelBus *strip = NULL; -#else // USE_WS2812_CTYPE - NeoPixelBus *strip = NULL; -#endif // USE_WS2812_CTYPE + typedef NeoEsp8266BitBang800KbpsMethod selectedNeoSpeedType; #endif // USE_WS2812_DMA + NeoPixelBus *strip = NULL; struct WsColor { uint8_t red, green, blue; @@ -170,9 +162,9 @@ void Ws2812Clock(void) Ws2812UpdateHand((RtcTime.second * 1000) / clksize, WS_SECOND); Ws2812UpdateHand((RtcTime.minute * 1000) / clksize, WS_MINUTE); - Ws2812UpdateHand(((RtcTime.hour % 12) * (5000 / clksize)) + ((RtcTime.minute * 1000) / (12 * clksize)), WS_HOUR); + Ws2812UpdateHand((((RtcTime.hour % 12) * 5000) + ((RtcTime.minute * 1000) / 12 )) / clksize, WS_HOUR); if (Settings.ws_color[WS_MARKER][WS_RED] + Settings.ws_color[WS_MARKER][WS_GREEN] + Settings.ws_color[WS_MARKER][WS_BLUE]) { - for (byte i = 0; i < 12; i++) { + for (uint8_t i = 0; i < 12; i++) { Ws2812UpdateHand((i * 5000) / clksize, WS_MARKER); } } @@ -305,33 +297,9 @@ void Ws2812Bars(uint8_t schemenr) void Ws2812Init(void) { #ifdef USE_WS2812_DMA -#if (USE_WS2812_CTYPE == NEO_GRB) - strip = new NeoPixelBus(WS2812_MAX_LEDS); // For Esp8266, the Pin is omitted and it uses GPIO3 due to DMA hardware use. -#elif (USE_WS2812_CTYPE == NEO_BRG) - strip = new NeoPixelBus(WS2812_MAX_LEDS); // For Esp8266, the Pin is omitted and it uses GPIO3 due to DMA hardware use. -#elif (USE_WS2812_CTYPE == NEO_RBG) - strip = new NeoPixelBus(WS2812_MAX_LEDS); // For Esp8266, the Pin is omitted and it uses GPIO3 due to DMA hardware use. -#elif (USE_WS2812_CTYPE == NEO_RGBW) - strip = new NeoPixelBus(WS2812_MAX_LEDS); // For Esp8266, the Pin is omitted and it uses GPIO3 due to DMA hardware use. -#elif (USE_WS2812_CTYPE == NEO_GRBW) - strip = new NeoPixelBus(WS2812_MAX_LEDS); // For Esp8266, the Pin is omitted and it uses GPIO3 due to DMA hardware use. -#else // USE_WS2812_CTYPE - strip = new NeoPixelBus(WS2812_MAX_LEDS); // For Esp8266, the Pin is omitted and it uses GPIO3 due to DMA hardware use. -#endif // USE_WS2812_CTYPE + strip = new NeoPixelBus(WS2812_MAX_LEDS); // For Esp8266, the Pin is omitted and it uses GPIO3 due to DMA hardware use. #else // USE_WS2812_DMA -#if (USE_WS2812_CTYPE == NEO_GRB) - strip = new NeoPixelBus(WS2812_MAX_LEDS, pin[GPIO_WS2812]); -#elif (USE_WS2812_CTYPE == NEO_BRG) - strip = new NeoPixelBus(WS2812_MAX_LEDS, pin[GPIO_WS2812]); -#elif (USE_WS2812_CTYPE == NEO_RBG) - strip = new NeoPixelBus(WS2812_MAX_LEDS, pin[GPIO_WS2812]); -#elif (USE_WS2812_CTYPE == NEO_RGBW) - strip = new NeoPixelBus(WS2812_MAX_LEDS, pin[GPIO_WS2812]); -#elif (USE_WS2812_CTYPE == NEO_GRBW) - strip = new NeoPixelBus(WS2812_MAX_LEDS, pin[GPIO_WS2812]); -#else // USE_WS2812_CTYPE - strip = new NeoPixelBus(WS2812_MAX_LEDS, pin[GPIO_WS2812]); -#endif // USE_WS2812_CTYPE + strip = new NeoPixelBus(WS2812_MAX_LEDS, pin[GPIO_WS2812]); #endif // USE_WS2812_DMA strip->Begin(); Ws2812Clear(); @@ -395,7 +363,7 @@ char* Ws2812GetColor(uint16_t led, char* scolor) sl_ledcolor[1] = lcolor.G; sl_ledcolor[2] = lcolor.B; scolor[0] = '\0'; - for (byte i = 0; i < light_subtype; i++) { + for (uint8_t i = 0; i < light_subtype; i++) { if (Settings.flag.decimal_text) { snprintf_P(scolor, 25, PSTR("%s%s%d"), scolor, (i > 0) ? "," : "", sl_ledcolor[i]); } else { diff --git a/sonoff/xsns_01_counter.ino b/sonoff/xsns_01_counter.ino index 6b501d49f..cfa797030 100644 --- a/sonoff/xsns_01_counter.ino +++ b/sonoff/xsns_01_counter.ino @@ -1,7 +1,7 @@ /* xsns_01_counter.ino - Counter sensors (water meters, electricity meters etc.) sensor support for Sonoff-Tasmota - Copyright (C) 2018 Maarten Damen and Theo Arends + Copyright (C) 2019 Maarten Damen and Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -25,7 +25,7 @@ unsigned long last_counter_timer[MAX_COUNTERS]; // Last counter time in micro seconds -void CounterUpdate(byte index) +void CounterUpdate(uint8_t index) { unsigned long counter_debounce_time = micros() - last_counter_timer[index -1]; if (counter_debounce_time > Settings.pulse_counter_debounce * 1000) { @@ -36,8 +36,7 @@ void CounterUpdate(byte index) RtcSettings.pulse_counter[index -1]++; } -// snprintf_P(log_data, sizeof(log_data), PSTR("CNTR: Interrupt %d"), index); -// AddLog(LOG_LEVEL_DEBUG); +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CNTR: Interrupt %d"), index); } } @@ -65,7 +64,7 @@ void CounterUpdate4(void) void CounterSaveState(void) { - for (byte i = 0; i < MAX_COUNTERS; i++) { + for (uint8_t i = 0; i < MAX_COUNTERS; i++) { if (pin[GPIO_CNTR1 +i] < 99) { Settings.pulse_counter[i] = RtcSettings.pulse_counter[i]; } @@ -77,7 +76,7 @@ void CounterInit(void) typedef void (*function) () ; function counter_callbacks[] = { CounterUpdate1, CounterUpdate2, CounterUpdate3, CounterUpdate4 }; - for (byte i = 0; i < MAX_COUNTERS; i++) { + for (uint8_t i = 0; i < MAX_COUNTERS; i++) { if (pin[GPIO_CNTR1 +i] < 99) { pinMode(pin[GPIO_CNTR1 +i], bitRead(counter_no_pullup, i) ? INPUT : INPUT_PULLUP); attachInterrupt(pin[GPIO_CNTR1 +i], counter_callbacks[i], FALLING); @@ -90,20 +89,20 @@ const char HTTP_SNS_COUNTER[] PROGMEM = "%s{s}" D_COUNTER "%d{m}%s%s{e}"; // {s} = , {m} = , {e} = #endif // USE_WEBSERVER -void CounterShow(boolean json) +void CounterShow(bool json) { char stemp[10]; - byte dsxflg = 0; - byte header = 0; - for (byte i = 0; i < MAX_COUNTERS; i++) { + uint8_t dsxflg = 0; + uint8_t header = 0; + for (uint8_t i = 0; i < MAX_COUNTERS; i++) { if (pin[GPIO_CNTR1 +i] < 99) { char counter[33]; if (bitRead(Settings.pulse_counter_type, i)) { dtostrfd((double)RtcSettings.pulse_counter[i] / 1000000, 6, counter); } else { dsxflg++; - dtostrfd(RtcSettings.pulse_counter[i], 0, counter); + snprintf_P(counter, sizeof(counter), PSTR("%lu"), RtcSettings.pulse_counter[i]); } if (json) { @@ -141,9 +140,9 @@ void CounterShow(boolean json) * Interface \*********************************************************************************************/ -boolean Xsns01(byte function) +bool Xsns01(uint8_t function) { - boolean result = false; + bool result = false; switch (function) { case FUNC_INIT: diff --git a/sonoff/xsns_02_analog.ino b/sonoff/xsns_02_analog.ino index cb4e794f1..e75160697 100644 --- a/sonoff/xsns_02_analog.ino +++ b/sonoff/xsns_02_analog.ino @@ -1,7 +1,7 @@ /* xsns_02_analog.ino - ESP8266 ADC support for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends + Copyright (C) 2019 Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -29,7 +29,7 @@ uint16_t adc_last_value = 0; uint16_t AdcRead(void) { uint16_t analog = 0; - for (byte i = 0; i < 32; i++) { + for (uint8_t i = 0; i < 32; i++) { analog += analogRead(A0); delay(1); } @@ -50,7 +50,7 @@ void AdcEvery250ms(void) } #endif // USE_RULES -void AdcShow(boolean json) +void AdcShow(bool json) { uint16_t analog = AdcRead(); @@ -67,11 +67,11 @@ void AdcShow(boolean json) * Interface \*********************************************************************************************/ -boolean Xsns02(byte function) +bool Xsns02(uint8_t function) { - boolean result = false; + bool result = false; - if (pin[GPIO_ADC0] < 99) { + if (my_module_flag.adc0) { switch (function) { #ifdef USE_RULES case FUNC_EVERY_250_MSECOND: diff --git a/sonoff/xsns_04_snfsc.ino b/sonoff/xsns_04_snfsc.ino index d3329e3e5..f4880a308 100644 --- a/sonoff/xsns_04_snfsc.ino +++ b/sonoff/xsns_04_snfsc.ino @@ -1,7 +1,7 @@ /* xsns_04_snfsc.ino - sonoff SC support for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends + Copyright (C) 2019 Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -61,8 +61,7 @@ void SonoffScSend(const char *data) { Serial.write(data); Serial.write('\x1B'); - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_SERIAL D_TRANSMIT " %s"), data); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_SERIAL D_TRANSMIT " %s"), data); } void SonoffScInit(void) @@ -78,8 +77,7 @@ void SonoffScSerialInput(char *rcvstat) char *str; uint16_t value[5] = { 0 }; - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_SERIAL D_RECEIVED " %s"), rcvstat); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_SERIAL D_RECEIVED " %s"), rcvstat); if (!strncasecmp_P(rcvstat, PSTR("AT+UPDATE="), 10)) { int8_t i = -1; @@ -87,7 +85,7 @@ void SonoffScSerialInput(char *rcvstat) value[i++] = atoi(str); } if (value[0] > 0) { - for (byte i = 0; i < 5; i++) { + for (uint8_t i = 0; i < 5; i++) { sc_value[i] = value[i]; } sc_value[2] = (11 - sc_value[2]) * 10; // Invert light level @@ -110,7 +108,7 @@ const char HTTP_SNS_SCPLUS[] PROGMEM = "%s{s}" D_LIGHT "{m}%d%%{e}{s}" D_NOISE "{m}%d%%{e}{s}" D_AIR_QUALITY "{m}%d%%{e}"; // {s} = , {m} = , {e} = #endif // USE_WEBSERVER -void SonoffScShow(boolean json) +void SonoffScShow(bool json) { if (sc_value[0] > 0) { float t = ConvertTemp(sc_value[1]); @@ -154,11 +152,11 @@ void SonoffScShow(boolean json) * Interface \*********************************************************************************************/ -boolean Xsns04(byte function) +bool Xsns04(uint8_t function) { - boolean result = false; + bool result = false; - if (SONOFF_SC == Settings.module) { + if (SONOFF_SC == my_module_type) { switch (function) { case FUNC_INIT: SonoffScInit(); diff --git a/sonoff/xsns_05_ds18b20.ino b/sonoff/xsns_05_ds18b20.ino index dfd4394dd..f5d4bd03d 100644 --- a/sonoff/xsns_05_ds18b20.ino +++ b/sonoff/xsns_05_ds18b20.ino @@ -1,7 +1,7 @@ /* xsns_05_ds18b20.ino - DS18B20 temperature sensor support for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends + Copyright (C) 2019 Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -42,7 +42,11 @@ uint8_t OneWireReset(void) uint8_t retries = 125; //noInterrupts(); +#ifdef DS18B20_INTERNAL_PULLUP + pinMode(ds18x20_pin, INPUT_PULLUP); +#else pinMode(ds18x20_pin, INPUT); +#endif do { if (--retries == 0) { return 0; @@ -52,7 +56,11 @@ uint8_t OneWireReset(void) pinMode(ds18x20_pin, OUTPUT); digitalWrite(ds18x20_pin, LOW); delayMicroseconds(480); +#ifdef DS18B20_INTERNAL_PULLUP + pinMode(ds18x20_pin, INPUT_PULLUP); +#else pinMode(ds18x20_pin, INPUT); +#endif delayMicroseconds(70); uint8_t r = !digitalRead(ds18x20_pin); //interrupts(); @@ -81,7 +89,11 @@ uint8_t OneWireReadBit(void) pinMode(ds18x20_pin, OUTPUT); digitalWrite(ds18x20_pin, LOW); delayMicroseconds(3); +#ifdef DS18B20_INTERNAL_PULLUP + pinMode(ds18x20_pin, INPUT_PULLUP); +#else pinMode(ds18x20_pin, INPUT); +#endif delayMicroseconds(10); uint8_t r = digitalRead(ds18x20_pin); //interrupts(); @@ -108,7 +120,7 @@ uint8_t OneWireRead(void) return r; } -boolean OneWireCrc8(uint8_t *addr) +bool OneWireCrc8(uint8_t *addr) { uint8_t crc = 0; uint8_t len = 8; @@ -137,7 +149,7 @@ void Ds18b20Convert(void) // delay(750); // 750ms should be enough for 12bit conv } -boolean Ds18b20Read(void) +bool Ds18b20Read(void) { uint8_t data[9]; int8_t sign = 1; @@ -187,7 +199,7 @@ void Ds18b20EverySecond(void) } } -void Ds18b20Show(boolean json) +void Ds18b20Show(bool json) { if (ds18b20_valid) { // Check for valid temperature char temperature[33]; @@ -216,9 +228,9 @@ void Ds18b20Show(boolean json) * Interface \*********************************************************************************************/ -boolean Xsns05(byte function) +bool Xsns05(uint8_t function) { - boolean result = false; + bool result = false; if (pin[GPIO_DSB] < 99) { switch (function) { diff --git a/sonoff/xsns_05_ds18x20.ino b/sonoff/xsns_05_ds18x20.ino index 1061df86d..521fdeaa8 100644 --- a/sonoff/xsns_05_ds18x20.ino +++ b/sonoff/xsns_05_ds18x20.ino @@ -1,7 +1,7 @@ /* xsns_05_ds18x20.ino - DS18x20 temperature sensor support for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends + Copyright (C) 2019 Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -233,7 +233,7 @@ uint8_t OneWireSearch(uint8_t *newAddr) return search_result; } -boolean OneWireCrc8(uint8_t *addr) +bool OneWireCrc8(uint8_t *addr) { uint8_t crc = 0; uint8_t len = 8; @@ -261,7 +261,9 @@ void Ds18x20Init(void) ds18x20_pin = pin[GPIO_DSB]; OneWireResetSearch(); - for (ds18x20_sensors = 0; ds18x20_sensors < DS18X20_MAX_SENSORS; ds18x20_sensors) { + + ds18x20_sensors = 0; + while (ds18x20_sensors < DS18X20_MAX_SENSORS) { if (!OneWireSearch(ds18x20_sensor[ds18x20_sensors].address)) { break; } @@ -285,8 +287,7 @@ void Ds18x20Init(void) } } } - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_DSB D_SENSORS_FOUND " %d"), ds18x20_sensors); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DSB D_SENSORS_FOUND " %d"), ds18x20_sensors); } void Ds18x20Convert(void) @@ -308,9 +309,6 @@ bool Ds18x20Read(uint8_t sensor) { uint8_t data[9]; int8_t sign = 1; - uint16_t temp12 = 0; - int16_t temp14 = 0; - float temp9 = 0.0; uint8_t index = ds18x20_sensor[sensor].index; if (ds18x20_sensor[index].valid) { ds18x20_sensor[index].valid--; } @@ -323,48 +321,47 @@ bool Ds18x20Read(uint8_t sensor) } if (OneWireCrc8(data)) { switch(ds18x20_sensor[index].address[0]) { - case DS18S20_CHIPID: - if (data[1] > 0x80) { - data[0] = (~data[0]) +1; - sign = -1; // App-Note fix possible sign error + case DS18S20_CHIPID: { + if (data[1] > 0x80) { + data[0] = (~data[0]) +1; + sign = -1; // App-Note fix possible sign error + } + float temp9 = (float)(data[0] >> 1) * sign; + ds18x20_sensor[index].temperature = ConvertTemp((temp9 - 0.25) + ((16.0 - data[6]) / 16.0)); + ds18x20_sensor[index].valid = SENSOR_MAX_MISS; + return true; } - if (data[0] & 1) { - temp9 = ((data[0] >> 1) + 0.5) * sign; - } else { - temp9 = (data[0] >> 1) * sign; - } - ds18x20_sensor[index].temperature = ConvertTemp((temp9 - 0.25) + ((16.0 - data[6]) / 16.0)); - ds18x20_sensor[index].valid = SENSOR_MAX_MISS; - return true; - case DS1822_CHIPID: - case DS18B20_CHIPID: - if (data[4] != 0x7F) { - data[4] = 0x7F; // Set resolution to 12-bit - OneWireReset(); - OneWireSelect(ds18x20_sensor[index].address); - OneWireWrite(W1_WRITE_SCRATCHPAD); - OneWireWrite(data[2]); // Th Register - OneWireWrite(data[3]); // Tl Register - OneWireWrite(data[4]); // Configuration Register - OneWireSelect(ds18x20_sensor[index].address); - OneWireWrite(W1_WRITE_EEPROM); // Save scratchpad to EEPROM + case DS1822_CHIPID: + case DS18B20_CHIPID: { + if (data[4] != 0x7F) { + data[4] = 0x7F; // Set resolution to 12-bit + OneWireReset(); + OneWireSelect(ds18x20_sensor[index].address); + OneWireWrite(W1_WRITE_SCRATCHPAD); + OneWireWrite(data[2]); // Th Register + OneWireWrite(data[3]); // Tl Register + OneWireWrite(data[4]); // Configuration Register + OneWireSelect(ds18x20_sensor[index].address); + OneWireWrite(W1_WRITE_EEPROM); // Save scratchpad to EEPROM #ifdef W1_PARASITE_POWER - w1_power_until = millis() + 10; // 10ms specified duration for EEPROM write + w1_power_until = millis() + 10; // 10ms specified duration for EEPROM write #endif + } + uint16_t temp12 = (data[1] << 8) + data[0]; + if (temp12 > 2047) { + temp12 = (~temp12) +1; + sign = -1; + } + ds18x20_sensor[index].temperature = ConvertTemp(sign * temp12 * 0.0625); // Divide by 16 + ds18x20_sensor[index].valid = SENSOR_MAX_MISS; + return true; } - temp12 = (data[1] << 8) + data[0]; - if (temp12 > 2047) { - temp12 = (~temp12) +1; - sign = -1; + case MAX31850_CHIPID: { + int16_t temp14 = (data[1] << 8) + (data[0] & 0xFC); + ds18x20_sensor[index].temperature = ConvertTemp(temp14 * 0.0625); // Divide by 16 + ds18x20_sensor[index].valid = SENSOR_MAX_MISS; + return true; } - ds18x20_sensor[index].temperature = ConvertTemp(sign * temp12 * 0.0625); // Divide by 16 - ds18x20_sensor[index].valid = SENSOR_MAX_MISS; - return true; - case MAX31850_CHIPID: - temp14 = (data[1] << 8) + (data[0] & 0xFC); - ds18x20_sensor[index].temperature = ConvertTemp(temp14 * 0.0625); // Divide by 16 - ds18x20_sensor[index].valid = SENSOR_MAX_MISS; - return true; } } } @@ -422,7 +419,7 @@ void Ds18x20EverySecond(void) } } -void Ds18x20Show(boolean json) +void Ds18x20Show(bool json) { for (uint8_t i = 0; i < ds18x20_sensors; i++) { uint8_t index = ds18x20_sensor[i].index; @@ -438,7 +435,7 @@ void Ds18x20Show(boolean json) snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"%s\":{\"" D_JSON_TEMPERATURE "\":%s}"), mqtt_data, ds18x20_types, temperature); } else { char address[17]; - for (byte j = 0; j < 6; j++) { + for (uint8_t j = 0; j < 6; j++) { sprintf(address+2*j, "%02X", ds18x20_sensor[index].address[6-j]); // Skip sensor type and crc } snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"%s\":{\"" D_JSON_ID "\":\"%s\",\"" D_JSON_TEMPERATURE "\":%s}"), mqtt_data, ds18x20_types, address, temperature); @@ -466,9 +463,9 @@ void Ds18x20Show(boolean json) * Interface \*********************************************************************************************/ -boolean Xsns05(byte function) +bool Xsns05(uint8_t function) { - boolean result = false; + bool result = false; if (pin[GPIO_DSB] < 99) { switch (function) { diff --git a/sonoff/xsns_05_ds18x20_legacy.ino b/sonoff/xsns_05_ds18x20_legacy.ino index 5a89a0abe..cb07bf63c 100644 --- a/sonoff/xsns_05_ds18x20_legacy.ino +++ b/sonoff/xsns_05_ds18x20_legacy.ino @@ -1,7 +1,7 @@ /* xsns_05_ds18x20_legacy.ino - DS18x20 temperature sensor support for Sonoff-Tasmota - Copyright (C) 2018 Heiko Krupp and Theo Arends + Copyright (C) 2019 Heiko Krupp and Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -65,11 +65,11 @@ void Ds18x20Search(void) num_sensors++; } } - for (byte i = 0; i < num_sensors; i++) { + for (uint8_t i = 0; i < num_sensors; i++) { ds18x20_index[i] = i; } - for (byte i = 0; i < num_sensors; i++) { - for (byte j = i + 1; j < num_sensors; j++) { + for (uint8_t i = 0; i < num_sensors; i++) { + for (uint8_t j = i + 1; j < num_sensors; j++) { if (uint32_t(ds18x20_address[ds18x20_index[i]]) > uint32_t(ds18x20_address[ds18x20_index[j]])) { std::swap(ds18x20_index[i], ds18x20_index[j]); } @@ -87,7 +87,7 @@ String Ds18x20Addresses(uint8_t sensor) { char address[20]; - for (byte i = 0; i < 8; i++) { + for (uint8_t i = 0; i < 8; i++) { sprintf(address+2*i, "%02X", ds18x20_address[ds18x20_index[sensor]][i]); } return String(address); @@ -101,9 +101,9 @@ void Ds18x20Convert(void) // delay(750); // 750ms should be enough for 12bit conv } -boolean Ds18x20Read(uint8_t sensor, float &t) +bool Ds18x20Read(uint8_t sensor, float &t) { - byte data[12]; + uint8_t data[12]; int8_t sign = 1; uint16_t temp12 = 0; int16_t temp14 = 0; @@ -116,7 +116,7 @@ boolean Ds18x20Read(uint8_t sensor, float &t) ds->select(ds18x20_address[ds18x20_index[sensor]]); ds->write(W1_READ_SCRATCHPAD); // Read Scratchpad - for (byte i = 0; i < 9; i++) { + for (uint8_t i = 0; i < 9; i++) { data[i] = ds->read(); } if (OneWire::crc8(data, 8) == data[8]) { @@ -126,11 +126,7 @@ boolean Ds18x20Read(uint8_t sensor, float &t) data[0] = (~data[0]) +1; 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; - } + temp9 = (float)(data[0] >> 1) * sign; t = ConvertTemp((temp9 - 0.25) + ((16.0 - data[6]) / 16.0)); break; case DS18B20_CHIPID: @@ -168,13 +164,13 @@ void Ds18x20Type(uint8_t sensor) } } -void Ds18x20Show(boolean json) +void Ds18x20Show(bool json) { char stemp[10]; float t; - byte dsxflg = 0; - for (byte i = 0; i < Ds18x20Sensors(); i++) { + uint8_t dsxflg = 0; + for (uint8_t i = 0; i < Ds18x20Sensors(); i++) { if (Ds18x20Read(i, t)) { // Check if read failed Ds18x20Type(i); char temperature[33]; @@ -220,9 +216,9 @@ void Ds18x20Show(boolean json) * Interface \*********************************************************************************************/ -boolean Xsns05(byte function) +bool Xsns05(uint8_t function) { - boolean result = false; + bool result = false; if (pin[GPIO_DSB] < 99) { switch (function) { diff --git a/sonoff/xsns_06_dht.ino b/sonoff/xsns_06_dht.ino index 72f94edd7..e6d8d7328 100644 --- a/sonoff/xsns_06_dht.ino +++ b/sonoff/xsns_06_dht.ino @@ -1,7 +1,7 @@ /* xsns_06_dht.ino - DHTxx, AM23xx and SI7021 temperature and humidity sensor support for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends + Copyright (C) 2019 Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -33,11 +33,11 @@ uint32_t dht_max_cycles; uint8_t dht_data[5]; -byte dht_sensors = 0; +uint8_t dht_sensors = 0; struct DHTSTRUCT { - byte pin; - byte type; + uint8_t pin; + uint8_t type; char stype[12]; uint32_t lastreadtime; uint8_t lastresult; @@ -47,12 +47,12 @@ struct DHTSTRUCT { void DhtReadPrep(void) { - for (byte i = 0; i < dht_sensors; i++) { + for (uint8_t i = 0; i < dht_sensors; i++) { digitalWrite(Dht[i].pin, HIGH); } } -int32_t DhtExpectPulse(byte sensor, bool level) +int32_t DhtExpectPulse(uint8_t sensor, bool level) { int32_t count = 0; @@ -64,7 +64,7 @@ int32_t DhtExpectPulse(byte sensor, bool level) return count; } -boolean DhtRead(byte sensor) +bool DhtRead(uint8_t sensor) { int32_t cycles[80]; uint8_t error = 0; @@ -125,16 +125,15 @@ boolean DhtRead(byte sensor) uint8_t checksum = (dht_data[0] + dht_data[1] + dht_data[2] + dht_data[3]) & 0xFF; if (dht_data[4] != checksum) { - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_DHT D_CHECKSUM_FAILURE " %02X, %02X, %02X, %02X, %02X =? %02X"), + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT D_CHECKSUM_FAILURE " %02X, %02X, %02X, %02X, %02X =? %02X"), dht_data[0], dht_data[1], dht_data[2], dht_data[3], dht_data[4], checksum); - AddLog(LOG_LEVEL_DEBUG); return false; } return true; } -void DhtReadTempHum(byte sensor) +void DhtReadTempHum(uint8_t sensor) { if ((NAN == Dht[sensor].h) || (Dht[sensor].lastresult > DHT_MAX_RETRY)) { // Reset after 8 misses Dht[sensor].t = NAN; @@ -162,9 +161,9 @@ void DhtReadTempHum(byte sensor) } } -boolean DhtSetup(byte pin, byte type) +bool DhtSetup(uint8_t pin, uint8_t type) { - boolean success = false; + bool success = false; if (dht_sensors < DHT_MAX_SENSORS) { Dht[dht_sensors].pin = pin; @@ -181,7 +180,7 @@ void DhtInit(void) { dht_max_cycles = microsecondsToClockCycles(1000); // 1 millisecond timeout for reading pulses from DHT sensor. - for (byte i = 0; i < dht_sensors; i++) { + for (uint8_t i = 0; i < dht_sensors; i++) { pinMode(Dht[i].pin, INPUT_PULLUP); Dht[i].lastreadtime = 0; Dht[i].lastresult = 0; @@ -198,16 +197,16 @@ void DhtEverySecond(void) // <1mS DhtReadPrep(); } else { - for (byte i = 0; i < dht_sensors; i++) { + for (uint8_t i = 0; i < dht_sensors; i++) { // DHT11 and AM2301 25mS per sensor, SI7021 5mS per sensor DhtReadTempHum(i); } } } -void DhtShow(boolean json) +void DhtShow(bool json) { - for (byte i = 0; i < dht_sensors; i++) { + for (uint8_t i = 0; i < dht_sensors; i++) { char temperature[33]; dtostrfd(Dht[i].t, Settings.flag2.temperature_resolution, temperature); char humidity[33]; @@ -239,9 +238,9 @@ void DhtShow(boolean json) * Interface \*********************************************************************************************/ -boolean Xsns06(byte function) +bool Xsns06(uint8_t function) { - boolean result = false; + bool result = false; if (dht_flg) { switch (function) { diff --git a/sonoff/xsns_07_sht1x.ino b/sonoff/xsns_07_sht1x.ino index 0bf2d774c..51351bdf5 100644 --- a/sonoff/xsns_07_sht1x.ino +++ b/sonoff/xsns_07_sht1x.ino @@ -1,7 +1,7 @@ /* xsns_07_sht1x.ino - SHT1x temperature and sensor support for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends + Copyright (C) 2019 Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -44,21 +44,21 @@ uint8_t sht_valid = 0; float sht_temperature = 0; float sht_humidity = 0; -boolean ShtReset(void) +bool ShtReset(void) { pinMode(sht_sda_pin, INPUT_PULLUP); pinMode(sht_scl_pin, OUTPUT); delay(11); - for (byte i = 0; i < 9; i++) { + for (uint8_t i = 0; i < 9; i++) { digitalWrite(sht_scl_pin, HIGH); digitalWrite(sht_scl_pin, LOW); } - boolean success = ShtSendCommand(SHT1X_CMD_SOFT_RESET); + bool success = ShtSendCommand(SHT1X_CMD_SOFT_RESET); delay(11); return success; } -boolean ShtSendCommand(const byte cmd) +bool ShtSendCommand(const uint8_t cmd) { pinMode(sht_sda_pin, OUTPUT); // Transmission Start sequence @@ -72,7 +72,7 @@ boolean ShtSendCommand(const byte cmd) // Send the command (address must be 000b) shiftOut(sht_sda_pin, sht_scl_pin, MSBFIRST, cmd); // Wait for ACK - boolean ackerror = false; + bool ackerror = false; digitalWrite(sht_scl_pin, HIGH); pinMode(sht_sda_pin, INPUT_PULLUP); if (digitalRead(sht_sda_pin) != LOW) { @@ -90,10 +90,10 @@ boolean ShtSendCommand(const byte cmd) return (!ackerror); } -boolean ShtAwaitResult(void) +bool ShtAwaitResult(void) { // Maximum 320ms for 14 bit measurement - for (byte i = 0; i < 16; i++) { + for (uint8_t i = 0; i < 16; i++) { if (LOW == digitalRead(sht_sda_pin)) { return true; } @@ -125,7 +125,7 @@ int ShtReadData(void) return val; } -boolean ShtRead(void) +bool ShtRead(void) { if (sht_valid) { sht_valid--; } if (!ShtReset()) { return false; } @@ -185,7 +185,7 @@ void ShtEverySecond(void) } } -void ShtShow(boolean json) +void ShtShow(bool json) { if (sht_valid) { char temperature[33]; @@ -219,9 +219,9 @@ void ShtShow(boolean json) * Interface \*********************************************************************************************/ -boolean Xsns07(byte function) +bool Xsns07(uint8_t function) { - boolean result = false; + bool result = false; if (i2c_flg) { switch (function) { diff --git a/sonoff/xsns_08_htu21.ino b/sonoff/xsns_08_htu21.ino index 3e22c17f3..8ce9657ea 100644 --- a/sonoff/xsns_08_htu21.ino +++ b/sonoff/xsns_08_htu21.ino @@ -1,7 +1,7 @@ /* xsns_08_htu21.ino - HTU21 temperature and humidity sensor support for Sonoff-Tasmota - Copyright (C) 2018 Heiko Krupp and Theo Arends + Copyright (C) 2019 Heiko Krupp and Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -140,7 +140,7 @@ void HtuInit(void) HtuSetResolution(HTU21_RES_RH12_T14); } -boolean HtuRead(void) +bool HtuRead(void) { uint8_t checksum = 0; uint16_t sensorval = 0; @@ -224,8 +224,7 @@ void HtuDetect(void) htu_delay_humidity = 23; } GetTextIndexed(htu_types, sizeof(htu_types), index, kHtuTypes); - snprintf_P(log_data, sizeof(log_data), S_LOG_I2C_FOUND_AT, htu_types, htu_address); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, S_LOG_I2C_FOUND_AT, htu_types, htu_address); } } @@ -246,7 +245,7 @@ void HtuEverySecond(void) } } -void HtuShow(boolean json) +void HtuShow(bool json) { if (htu_valid) { char temperature[33]; @@ -280,9 +279,9 @@ void HtuShow(boolean json) * Interface \*********************************************************************************************/ -boolean Xsns08(byte function) +bool Xsns08(uint8_t function) { - boolean result = false; + bool result = false; if (i2c_flg) { switch (function) { diff --git a/sonoff/xsns_09_bmp.ino b/sonoff/xsns_09_bmp.ino index 6023ba5cc..488efd7ee 100755 --- a/sonoff/xsns_09_bmp.ino +++ b/sonoff/xsns_09_bmp.ino @@ -1,7 +1,7 @@ /* xsns_09_bmp.ino - BMP pressure, temperature, humidity and gas sensor support for Sonoff-Tasmota - Copyright (C) 2018 Heiko Krupp and Theo Arends + Copyright (C) 2019 Heiko Krupp and Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -101,7 +101,7 @@ typedef struct { bmp180_cal_data_t *bmp180_cal_data = NULL; -boolean Bmp180Calibration(uint8_t bmp_idx) +bool Bmp180Calibration(uint8_t bmp_idx) { if (!bmp180_cal_data) { bmp180_cal_data = (bmp180_cal_data_t*)malloc(BMP_MAX_SENSORS * sizeof(bmp180_cal_data_t)); @@ -246,7 +246,7 @@ typedef struct { Bme280CalibrationData_t *Bme280CalibrationData = NULL; -boolean Bmx280Calibrate(uint8_t bmp_idx) +bool Bmx280Calibrate(uint8_t bmp_idx) { // if (I2cRead8(bmp_address, BMP_REGISTER_CHIPID) != BME280_CHIPID) return false; @@ -351,7 +351,7 @@ static void BmeDelayMs(uint32_t ms) delay(ms); } -boolean Bme680Init(uint8_t bmp_idx) +bool Bme680Init(uint8_t bmp_idx) { if (!gas_sensor) { gas_sensor = (bme680_dev*)malloc(BMP_MAX_SENSORS * sizeof(bme680_dev)); @@ -455,14 +455,14 @@ void BmpDetect(void) if (!bmp_sensors) { return; } memset(bmp_sensors, 0, bmp_sensor_size); // Init defaults to 0 - for (byte i = 0; i < BMP_MAX_SENSORS; i++) { + for (uint8_t i = 0; i < BMP_MAX_SENSORS; i++) { uint8_t bmp_type = I2cRead8(bmp_addresses[i], BMP_REGISTER_CHIPID); if (bmp_type) { bmp_sensors[bmp_count].bmp_address = bmp_addresses[i]; bmp_sensors[bmp_count].bmp_type = bmp_type; bmp_sensors[bmp_count].bmp_model = 0; - boolean success = false; + bool success = false; switch (bmp_type) { case BMP180_CHIPID: success = Bmp180Calibration(bmp_count); @@ -482,8 +482,7 @@ void BmpDetect(void) } if (success) { GetTextIndexed(bmp_sensors[bmp_count].bmp_name, sizeof(bmp_sensors[bmp_count].bmp_name), bmp_sensors[bmp_count].bmp_model, kBmpTypes); - snprintf_P(log_data, sizeof(log_data), S_LOG_I2C_FOUND_AT, bmp_sensors[bmp_count].bmp_name, bmp_sensors[bmp_count].bmp_address); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, S_LOG_I2C_FOUND_AT, bmp_sensors[bmp_count].bmp_name, bmp_sensors[bmp_count].bmp_address); bmp_count++; } } @@ -494,7 +493,7 @@ void BmpRead(void) { if (!bmp_sensors) { return; } - for (byte bmp_idx = 0; bmp_idx < bmp_count; bmp_idx++) { + for (uint8_t bmp_idx = 0; bmp_idx < bmp_count; bmp_idx++) { switch (bmp_sensors[bmp_idx].bmp_type) { case BMP180_CHIPID: Bmp180Read(bmp_idx); @@ -525,11 +524,11 @@ void BmpEverySecond(void) } } -void BmpShow(boolean json) +void BmpShow(bool json) { if (!bmp_sensors) { return; } - for (byte bmp_idx = 0; bmp_idx < bmp_count; bmp_idx++) { + for (uint8_t bmp_idx = 0; bmp_idx < bmp_count; bmp_idx++) { if (bmp_sensors[bmp_idx].bmp_type) { float bmp_sealevel = 0.0; if (bmp_sensors[bmp_idx].bmp_pressure != 0.0) { @@ -540,7 +539,7 @@ void BmpShow(boolean json) float bmp_pressure = ConvertPressure(bmp_sensors[bmp_idx].bmp_pressure); char name[10]; - snprintf(name, sizeof(name), bmp_sensors[bmp_idx].bmp_name); + strlcpy(name, bmp_sensors[bmp_idx].bmp_name, sizeof(name)); if (bmp_count > 1) { snprintf_P(name, sizeof(name), PSTR("%s-%02X"), name, bmp_sensors[bmp_idx].bmp_address); // BMXXXX-XX } @@ -621,9 +620,9 @@ void BmpShow(boolean json) * Interface \*********************************************************************************************/ -boolean Xsns09(byte function) +bool Xsns09(uint8_t function) { - boolean result = false; + bool result = false; if (i2c_flg) { switch (function) { diff --git a/sonoff/xsns_10_bh1750.ino b/sonoff/xsns_10_bh1750.ino index af0d1aebb..95abc41c5 100644 --- a/sonoff/xsns_10_bh1750.ino +++ b/sonoff/xsns_10_bh1750.ino @@ -1,7 +1,7 @@ /* xsns_10_bh1750.ino - BH1750 ambient light sensor support for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends + Copyright (C) 2019 Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -44,8 +44,8 @@ bool Bh1750Read(void) if (bh1750_valid) { bh1750_valid--; } if (2 != Wire.requestFrom(bh1750_address, (uint8_t)2)) { return false; } - byte msb = Wire.read(); - byte lsb = Wire.read(); + uint8_t msb = Wire.read(); + uint8_t lsb = Wire.read(); bh1750_illuminance = ((msb << 8) | lsb) / 1.2; bh1750_valid = SENSOR_MAX_MISS; return true; @@ -59,14 +59,13 @@ void Bh1750Detect(void) return; } - for (byte i = 0; i < sizeof(bh1750_addresses); i++) { + for (uint8_t i = 0; i < sizeof(bh1750_addresses); i++) { bh1750_address = bh1750_addresses[i]; Wire.beginTransmission(bh1750_address); Wire.write(BH1750_CONTINUOUS_HIGH_RES_MODE); if (!Wire.endTransmission()) { bh1750_type = 1; - snprintf_P(log_data, sizeof(log_data), S_LOG_I2C_FOUND_AT, bh1750_types, bh1750_address); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, S_LOG_I2C_FOUND_AT, bh1750_types, bh1750_address); break; } } @@ -89,7 +88,7 @@ void Bh1750EverySecond(void) } } -void Bh1750Show(boolean json) +void Bh1750Show(bool json) { if (bh1750_valid) { if (json) { @@ -111,9 +110,9 @@ void Bh1750Show(boolean json) * Interface \*********************************************************************************************/ -boolean Xsns10(byte function) +bool Xsns10(uint8_t function) { - boolean result = false; + bool result = false; if (i2c_flg) { switch (function) { diff --git a/sonoff/xsns_11_veml6070.ino b/sonoff/xsns_11_veml6070.ino index 0177fcc63..c32fc090f 100644 --- a/sonoff/xsns_11_veml6070.ino +++ b/sonoff/xsns_11_veml6070.ino @@ -1,7 +1,7 @@ /* xsns_11_veml6070.ino - VEML6070 ultra violet light sensor support for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends + Copyright (C) 2019 Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -31,7 +31,7 @@ Version Date Action Description -------------------------------------------------------------------------------------------- - 1.0.0.3 20181006 fixed - missing "" around the UV Index text + 1.0.0.3 20181006 fixed - missing "" around the UV Index text - thanks to Lisa she had tested it on here mqtt system. -- 1.0.0.2 20180928 tests - same as in version 1.0.0.1 @@ -140,8 +140,7 @@ void Veml6070Detect(void) veml6070_type = 1; uint8_t veml_model = 0; GetTextIndexed(veml6070_name, sizeof(veml6070_name), veml_model, kVemlTypes); - snprintf_P(log_data, sizeof(log_data), S_LOG_I2C_FOUND_AT, "VEML6070", VEML6070_ADDR_L); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, S_LOG_I2C_FOUND_AT, "VEML6070", VEML6070_ADDR_L); } } @@ -156,13 +155,11 @@ void Veml6070UvTableInit(void) uv_risk_map[i] = ( (USE_VEML6070_RSET / VEML6070_TABLE_COEFFCIENT) / VEML6070_UV_MAX_DEFAULT ) * (i+1); } else { uv_risk_map[i] = ( (VEML6070_RSET_DEFAULT / VEML6070_TABLE_COEFFCIENT) / VEML6070_UV_MAX_DEFAULT ) * (i+1); - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_DEBUG "VEML6070 resistor error %d"), USE_VEML6070_RSET); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "VEML6070 resistor error %d"), USE_VEML6070_RSET); } #else uv_risk_map[i] = ( (VEML6070_RSET_DEFAULT / VEML6070_TABLE_COEFFCIENT) / VEML6070_UV_MAX_DEFAULT ) * (i+1); - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_DEBUG "VEML6070 resistor default used %d"), VEML6070_RSET_DEFAULT); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "VEML6070 resistor default used %d"), VEML6070_RSET_DEFAULT); #endif } } @@ -187,7 +184,7 @@ void Veml6070EverySecond(void) /********************************************************************************************/ -void Veml6070ModeCmd(boolean mode_cmd) +void Veml6070ModeCmd(bool mode_cmd) { // mode_cmd 1 = on = 1[ms] // mode_cmd 0 = off = 2[ms] @@ -196,8 +193,7 @@ void Veml6070ModeCmd(boolean mode_cmd) uint8_t status = Wire.endTransmission(); // action on status if (!status) { - snprintf_P(log_data, sizeof(log_data), S_LOG_I2C_FOUND_AT, "VEML6070 mode_cmd", VEML6070_ADDR_L); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, S_LOG_I2C_FOUND_AT, "VEML6070 mode_cmd", VEML6070_ADDR_L); } } @@ -241,8 +237,7 @@ double Veml6070UvRiskLevel(uint16_t uv_level) // out of range and much to high - it must be outerspace or sensor damaged snprintf_P(str_uvrisk_text, sizeof(str_uvrisk_text), D_UV_INDEX_7); return ( risk = 99 ); - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_DEBUG "VEML6070 out of range %d"), risk); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "VEML6070 out of range %d"), risk); } } @@ -270,7 +265,7 @@ double Veml6070UvPower(double uvrisk) /********************************************************************************************/ -void Veml6070Show(boolean json) +void Veml6070Show(bool json) { if (veml6070_type) { // convert double values to string @@ -307,9 +302,9 @@ void Veml6070Show(boolean json) * Interface \*********************************************************************************************/ -boolean Xsns11(byte function) +bool Xsns11(uint8_t function) { - boolean result = false; + bool result = false; if (i2c_flg) { switch (function) { diff --git a/sonoff/xsns_12_ads1115.ino b/sonoff/xsns_12_ads1115.ino index d07831725..a8f0d8531 100644 --- a/sonoff/xsns_12_ads1115.ino +++ b/sonoff/xsns_12_ads1115.ino @@ -1,7 +1,7 @@ /* xsns_12_ads1115_ada.ino - ADS1115 A/D Converter support for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends + Copyright (C) 2019 Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -119,7 +119,8 @@ CONFIG REGISTER uint8_t ads1115_type = 0; uint8_t ads1115_address; uint8_t ads1115_addresses[] = { ADS1115_ADDRESS_ADDR_GND, ADS1115_ADDRESS_ADDR_VDD, ADS1115_ADDRESS_ADDR_SDA, ADS1115_ADDRESS_ADDR_SCL }; - +uint8_t ads1115_found[] = {false,false,false,false}; +int16_t ads1115_values[4]; //Ads1115StartComparator(channel, ADS1115_REG_CONFIG_MODE_SINGLE); //Ads1115StartComparator(channel, ADS1115_REG_CONFIG_MODE_CONTIN); void Ads1115StartComparator(uint8_t channel, uint16_t mode) @@ -160,61 +161,90 @@ int16_t Ads1115GetConversion(uint8_t channel) void Ads1115Detect(void) { uint16_t buffer; - - if (ads1115_type) { - return; - } - - for (byte i = 0; i < sizeof(ads1115_addresses); i++) { - ads1115_address = ads1115_addresses[i]; - if (I2cValidRead16(&buffer, ads1115_address, ADS1115_REG_POINTER_CONVERT)) { - Ads1115StartComparator(i, ADS1115_REG_CONFIG_MODE_CONTIN); - ads1115_type = 1; - snprintf_P(log_data, sizeof(log_data), S_LOG_I2C_FOUND_AT, "ADS1115", ads1115_address); - AddLog(LOG_LEVEL_DEBUG); - break; + for (uint8_t i = 0; i < sizeof(ads1115_addresses); i++) { + if (!ads1115_found[i]) { + ads1115_address = ads1115_addresses[i]; + if (I2cValidRead16(&buffer, ads1115_address, ADS1115_REG_POINTER_CONVERT) && + I2cValidRead16(&buffer, ads1115_address, ADS1115_REG_POINTER_CONFIG)) { + Ads1115StartComparator(i, ADS1115_REG_CONFIG_MODE_CONTIN); + ads1115_type = 1; + ads1115_found[i] = 1; + AddLog_P2(LOG_LEVEL_DEBUG, S_LOG_I2C_FOUND_AT, "ADS1115", ads1115_address); + } } } } -void Ads1115Show(boolean json) +void Ads1115GetValues(uint8_t address) { - if (ads1115_type) { - char stemp[10]; + uint8_t old_address = ads1115_address; + ads1115_address = address; + for (uint8_t i = 0; i < 4; i++) { + ads1115_values[i] = Ads1115GetConversion(i); + //AddLog_P2(LOG_LEVEL_INFO, "Logging ADS1115 %02x (%i) = %i", address, i, ads1115_values[i] ); + } + ads1115_address = old_address; +} - byte dsxflg = 0; - for (byte i = 0; i < 4; i++) { - int16_t adc_value = Ads1115GetConversion(i); +void Ads1115toJSON(char *comma_j) +{ + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s%s{"), mqtt_data,comma_j); + char *comma = (char*)""; + for (uint8_t i = 0; i < 4; i++) { + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s%s\"A%d\":%d"), mqtt_data, comma, i, ads1115_values[i]); + comma = (char*)","; + } + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s}"), mqtt_data); +} +void Ads1115toString(uint8_t address) +{ + char label[15]; + snprintf_P(label, sizeof(label), "ADS1115(%02x)", address); + + for (uint8_t i = 0; i < 4; i++) { + snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_ANALOG, mqtt_data, label, i, ads1115_values[i]); + } +} + +void Ads1115Show(bool json) +{ + if (!ads1115_type) { return; } + + if (json) { + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"ADS1115\":["), mqtt_data); + } + + char *comma = (char*)""; + + for (uint8_t t = 0; t < sizeof(ads1115_addresses); t++) { + //AddLog_P2(LOG_LEVEL_INFO, "Logging ADS1115 %02x", ads1115_addresses[t]); + if (ads1115_found[t]) { + Ads1115GetValues(ads1115_addresses[t]); if (json) { - if (!dsxflg ) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"ADS1115\":{"), mqtt_data); - stemp[0] = '\0'; - } - dsxflg++; - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s%s\"A%d\":%d"), mqtt_data, stemp, i, adc_value); - strlcpy(stemp, ",", sizeof(stemp)); + Ads1115toJSON(comma); + comma = (char*)","; + } #ifdef USE_WEBSERVER - } else { - snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_ANALOG, mqtt_data, "ADS1115", i, adc_value); + else { + Ads1115toString(ads1115_addresses[t]); + } #endif // USE_WEBSERVER - } - } - if (json) { - if (dsxflg) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s}"), mqtt_data); - } } } + + if (json) { + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s]"), mqtt_data); + } } /*********************************************************************************************\ * Interface \*********************************************************************************************/ -boolean Xsns12(byte function) +bool Xsns12(uint8_t function) { - boolean result = false; + bool result = false; if (i2c_flg) { switch (function) { diff --git a/sonoff/xsns_12_ads1115_i2cdev.ino b/sonoff/xsns_12_ads1115_i2cdev.ino index 96c2f7006..f73581ec0 100644 --- a/sonoff/xsns_12_ads1115_i2cdev.ino +++ b/sonoff/xsns_12_ads1115_i2cdev.ino @@ -1,7 +1,7 @@ /* xsns_12_ads1115.ino - ADS1x15 A/D Converter support for Sonoff-Tasmota - Copyright (C) 2018 Stefan Bode and Theo Arends + Copyright (C) 2019 Stefan Bode and Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -55,7 +55,7 @@ uint8_t ads1115_addresses[] = { ADS1115_ADDRESS_ADDR_SCL // address pin tied to SCL pin }; -int16_t Ads1115GetConversion(byte channel) +int16_t Ads1115GetConversion(uint8_t channel) { switch (channel) { case 0: @@ -81,7 +81,7 @@ void Ads1115Detect(void) return; } - for (byte i = 0; i < sizeof(ads1115_addresses); i++) { + for (uint8_t i = 0; i < sizeof(ads1115_addresses); i++) { ads1115_address = ads1115_addresses[i]; ADS1115 adc0(ads1115_address); if (adc0.testConnection()) { @@ -90,20 +90,19 @@ void Ads1115Detect(void) adc0.setRate(ADS1115_RATE_860); adc0.setMode(ADS1115_MODE_CONTINUOUS); ads1115_type = 1; - snprintf_P(log_data, sizeof(log_data), S_LOG_I2C_FOUND_AT, "ADS1115", ads1115_address); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, S_LOG_I2C_FOUND_AT, "ADS1115", ads1115_address); break; } } } -void Ads1115Show(boolean json) +void Ads1115Show(bool json) { if (ads1115_type) { char stemp[10]; - byte dsxflg = 0; - for (byte i = 0; i < 4; i++) { + uint8_t dsxflg = 0; + for (uint8_t i = 0; i < 4; i++) { int16_t adc_value = Ads1115GetConversion(i); if (json) { @@ -132,9 +131,9 @@ void Ads1115Show(boolean json) * Interface \*********************************************************************************************/ -boolean Xsns12(byte function) +bool Xsns12(uint8_t function) { - boolean result = false; + bool result = false; if (i2c_flg) { switch (function) { diff --git a/sonoff/xsns_13_ina219.ino b/sonoff/xsns_13_ina219.ino index ae5999a7e..88f6ce240 100644 --- a/sonoff/xsns_13_ina219.ino +++ b/sonoff/xsns_13_ina219.ino @@ -1,7 +1,7 @@ /* xsns_13_ina219.ino - INA219 Current Sensor support for Sonoff-Tasmota - Copyright (C) 2018 Stefan Bode and Theo Arends + Copyright (C) 2019 Stefan Bode and Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -177,7 +177,7 @@ bool Ina219Read(void) bool Ina219CommandSensor(void) { - boolean serviced = true; + bool serviced = true; if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 2)) { Settings.ina219_mode = XdrvMailbox.payload; @@ -194,12 +194,11 @@ void Ina219Detect(void) { if (ina219_type) { return; } - for (byte i = 0; i < sizeof(ina219_addresses); i++) { + for (uint8_t i = 0; i < sizeof(ina219_addresses); i++) { ina219_address = ina219_addresses[i]; if (Ina219SetCalibration(Settings.ina219_mode)) { ina219_type = 1; - snprintf_P(log_data, sizeof(log_data), S_LOG_I2C_FOUND_AT, ina219_types, ina219_address); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, S_LOG_I2C_FOUND_AT, ina219_types, ina219_address); break; } } @@ -229,7 +228,7 @@ const char HTTP_SNS_INA219_DATA[] PROGMEM = "%s" "{s}INA219 " D_POWERUSAGE "{m}%s " D_UNIT_WATT "{e}"; #endif // USE_WEBSERVER -void Ina219Show(boolean json) +void Ina219Show(bool json) { if (ina219_valid) { float fpower = ina219_voltage * ina219_current; @@ -261,13 +260,13 @@ void Ina219Show(boolean json) * Interface \*********************************************************************************************/ -boolean Xsns13(byte function) +bool Xsns13(uint8_t function) { - boolean result = false; + bool result = false; if (i2c_flg) { switch (function) { - case FUNC_COMMAND: + case FUNC_COMMAND_SENSOR: if ((XSNS_13 == XdrvMailbox.index) && (ina219_type)) { result = Ina219CommandSensor(); } diff --git a/sonoff/xsns_14_sht3x.ino b/sonoff/xsns_14_sht3x.ino index 6738b866a..fcb7f4a22 100755 --- a/sonoff/xsns_14_sht3x.ino +++ b/sonoff/xsns_14_sht3x.ino @@ -1,7 +1,7 @@ /* xsns_14_sht3x.ino - SHT3X temperature and humidity sensor support for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends + Copyright (C) 2019 Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -82,24 +82,23 @@ void Sht3xDetect(void) float t; float h; - for (byte i = 0; i < SHT3X_MAX_SENSORS; i++) { + for (uint8_t i = 0; i < SHT3X_MAX_SENSORS; i++) { if (Sht3xRead(t, h, sht3x_addresses[i])) { sht3x_sensors[sht3x_count].address = sht3x_addresses[i]; GetTextIndexed(sht3x_sensors[sht3x_count].types, sizeof(sht3x_sensors[sht3x_count].types), i, kShtTypes); - snprintf_P(log_data, sizeof(log_data), S_LOG_I2C_FOUND_AT, sht3x_sensors[sht3x_count].types, sht3x_sensors[sht3x_count].address); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, S_LOG_I2C_FOUND_AT, sht3x_sensors[sht3x_count].types, sht3x_sensors[sht3x_count].address); sht3x_count++; } } } -void Sht3xShow(boolean json) +void Sht3xShow(bool json) { if (sht3x_count) { float t; float h; char types[11]; - for (byte i = 0; i < sht3x_count; i++) { + for (uint8_t i = 0; i < sht3x_count; i++) { if (Sht3xRead(t, h, sht3x_sensors[i].address)) { if (0 == i) { SetGlobalValues(t, h); } @@ -140,9 +139,9 @@ void Sht3xShow(boolean json) * Interface \*********************************************************************************************/ -boolean Xsns14(byte function) +bool Xsns14(uint8_t function) { - boolean result = false; + bool result = false; if (i2c_flg) { switch (function) { diff --git a/sonoff/xsns_15_mhz19.ino b/sonoff/xsns_15_mhz19.ino index 968c0d021..1e21aa6fb 100644 --- a/sonoff/xsns_15_mhz19.ino +++ b/sonoff/xsns_15_mhz19.ino @@ -1,7 +1,7 @@ /* xsns_15_mhz19.ino - MH-Z19(B) CO2 sensor support for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends + Copyright (C) 2019 Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -39,7 +39,10 @@ enum MhzFilterOptions {MHZ19_FILTER_OFF, MHZ19_FILTER_OFF_ALLSAMPLES, MHZ19_FILT /*********************************************************************************************\ * Source: http://www.winsen-sensor.com/d/files/infrared-gas-sensor/mh-z19b-co2-ver1_0.pdf * - * Automatic Baseline Correction (ABC logic function) + * Automatic Baseline Correction (ABC logic function) is enabled by default but may be disabled with command + * Sensor15 0 + * and enabled again with command + * Sensor15 1 * * ABC logic function refers to that sensor itself do zero point judgment and automatic calibration procedure * intelligently after a continuous operation period. The automatic calibration cycle is every 24 hours after powered on. @@ -52,10 +55,6 @@ enum MhzFilterOptions {MHZ19_FILTER_OFF, MHZ19_FILTER_OFF_ALLSAMPLES, MHZ19_FILT * Please do zero calibration timely, such as manual or commend calibration. \*********************************************************************************************/ -#define MHZ19_ABC_ENABLE 1 // Automatic Baseline Correction (0 = off, 1 = on (default)) - -/*********************************************************************************************/ - #include #ifndef CO2_LOW @@ -70,7 +69,10 @@ enum MhzFilterOptions {MHZ19_FILTER_OFF, MHZ19_FILTER_OFF_ALLSAMPLES, MHZ19_FILT TasmotaSerial *MhzSerial; -const char kMhzTypes[] PROGMEM = "MHZ19|MHZ19B"; +const char kMhzModels[] PROGMEM = "|B"; + +const char ABC_ENABLED[] PROGMEM = "ABC is Enabled"; +const char ABC_DISABLED[] PROGMEM = "ABC is Enabled"; enum MhzCommands { MHZ_CMND_READPPM, MHZ_CMND_ABCENABLE, MHZ_CMND_ABCDISABLE, MHZ_CMND_ZEROPOINT, MHZ_CMND_RESET, MHZ_CMND_RANGE_1000, MHZ_CMND_RANGE_2000, MHZ_CMND_RANGE_3000, MHZ_CMND_RANGE_5000 }; const uint8_t kMhzCommands[][4] PROGMEM = { @@ -88,9 +90,7 @@ const uint8_t kMhzCommands[][4] PROGMEM = { uint8_t mhz_type = 1; uint16_t mhz_last_ppm = 0; uint8_t mhz_filter = MHZ19_FILTER_OPTION; -bool mhz_abc_enable = MHZ19_ABC_ENABLE; bool mhz_abc_must_apply = false; -char mhz_types[7]; float mhz_temperature = 0; uint8_t mhz_retry = MHZ19_RETRY_COUNT; @@ -99,17 +99,17 @@ uint8_t mhz_state = 0; /*********************************************************************************************/ -byte MhzCalculateChecksum(byte *array) +uint8_t MhzCalculateChecksum(uint8_t *array) { - byte checksum = 0; - for (byte i = 1; i < 8; i++) { + uint8_t checksum = 0; + for (uint8_t i = 1; i < 8; i++) { checksum += array[i]; } checksum = 255 - checksum; return (checksum +1); } -size_t MhzSendCmd(byte command_id) +size_t MhzSendCmd(uint8_t command_id) { uint8_t mhz_send[9] = { 0 }; @@ -123,8 +123,7 @@ size_t MhzSendCmd(byte command_id) memcpy_P(&mhz_send[6], kMhzCommands[command_id] + sizeof(uint16_t), sizeof(uint16_t)); mhz_send[8] = MhzCalculateChecksum(mhz_send); - snprintf_P(log_data, sizeof(log_data), PSTR("Final MhzCommand: %x %x %x %x %x %x %x %x %x"),mhz_send[0],mhz_send[1],mhz_send[2],mhz_send[3],mhz_send[4],mhz_send[5],mhz_send[6],mhz_send[7],mhz_send[8]); - AddLog(LOG_LEVEL_DEBUG); +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Final MhzCommand: %x %x %x %x %x %x %x %x %x"),mhz_send[0],mhz_send[1],mhz_send[2],mhz_send[3],mhz_send[4],mhz_send[5],mhz_send[6],mhz_send[7],mhz_send[8]); return MhzSerial->write(mhz_send, sizeof(mhz_send)); } @@ -198,14 +197,14 @@ void MhzEverySecond(void) } } - AddLogSerial(LOG_LEVEL_DEBUG_MORE, mhz_response, counter); + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, mhz_response, counter); if (counter < 9) { // AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "MH-Z19 comms timeout")); return; } - byte crc = MhzCalculateChecksum(mhz_response); + uint8_t crc = MhzCalculateChecksum(mhz_response); if (mhz_response[8] != crc) { // AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "MH-Z19 crc error")); return; @@ -219,7 +218,7 @@ void MhzEverySecond(void) uint16_t u = (mhz_response[6] << 8) | mhz_response[7]; if (15000 == u) { // During (and only ever at) sensor boot, 'u' is reported as 15000 - if (!mhz_abc_enable) { + if (Settings.SensorBits1.mhz19b_abc_disable) { // After bootup of the sensor the ABC will be enabled. // Thus only actively disable after bootup. mhz_abc_must_apply = true; @@ -236,7 +235,7 @@ void MhzEverySecond(void) if (0 == s || 64 == s) { // Reading is stable. if (mhz_abc_must_apply) { mhz_abc_must_apply = false; - if (mhz_abc_enable) { + if (!Settings.SensorBits1.mhz19b_abc_disable) { MhzSendCmd(MHZ_CMND_ABCENABLE); } else { MhzSendCmd(MHZ_CMND_ABCDISABLE); @@ -253,8 +252,8 @@ void MhzEverySecond(void) /*********************************************************************************************\ * Command Sensor15 * - * 0 - (Not implemented) ABC Off - * 1 - (Not implemented) ABC On + * 0 - ABC Off + * 1 - ABC On (Default) * 2 - Manual start = ABC Off * 3 - (Not implemented) Optional filter settings * 9 - Reset @@ -271,9 +270,19 @@ void MhzEverySecond(void) bool MhzCommandSensor(void) { - boolean serviced = true; + bool serviced = true; switch (XdrvMailbox.payload) { + case 0: + Settings.SensorBits1.mhz19b_abc_disable = true; + MhzSendCmd(MHZ_CMND_ABCDISABLE); + snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_SENSOR_INDEX_SVALUE, XSNS_15, ABC_DISABLED); + break; + case 1: + Settings.SensorBits1.mhz19b_abc_disable = false; + MhzSendCmd(MHZ_CMND_ABCENABLE); + snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_SENSOR_INDEX_SVALUE, XSNS_15, ABC_ENABLED); + break; case 2: MhzSendCmd(MHZ_CMND_ZEROPOINT); snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_SENSOR_INDEX_SVALUE, XSNS_15, D_JSON_ZERO_POINT_CALIBRATION); @@ -299,7 +308,11 @@ bool MhzCommandSensor(void) snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_SENSOR_INDEX_SVALUE, XSNS_15, D_JSON_RANGE_5000); break; default: - serviced = false; + if (!Settings.SensorBits1.mhz19b_abc_disable) { + snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_SENSOR_INDEX_SVALUE, XSNS_15, ABC_ENABLED); + } else { + snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_SENSOR_INDEX_SVALUE, XSNS_15, ABC_DISABLED); + } } return serviced; @@ -320,21 +333,26 @@ void MhzInit(void) } } -void MhzShow(boolean json) +void MhzShow(bool json) { + char types[7] = "MHZ19B"; // MHZ19B for legacy reasons. Prefered is MHZ19 char temperature[33]; dtostrfd(mhz_temperature, Settings.flag2.temperature_resolution, temperature); - GetTextIndexed(mhz_types, sizeof(mhz_types), mhz_type -1, kMhzTypes); + char model[3]; + GetTextIndexed(model, sizeof(model), mhz_type -1, kMhzModels); if (json) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"%s\":{\"" D_JSON_CO2 "\":%d,\"" D_JSON_TEMPERATURE "\":%s}"), mqtt_data, mhz_types, mhz_last_ppm, temperature); + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"%s\":{\"" D_JSON_MODEL "\":\"%s\",\"" D_JSON_CO2 "\":%d,\"" D_JSON_TEMPERATURE "\":%s}"), mqtt_data, types, model, mhz_last_ppm, temperature); #ifdef USE_DOMOTICZ - if (0 == tele_period) DomoticzSensor(DZ_AIRQUALITY, mhz_last_ppm); + if (0 == tele_period) { + DomoticzSensor(DZ_AIRQUALITY, mhz_last_ppm); + DomoticzSensor(DZ_TEMP, temperature); + } #endif // USE_DOMOTICZ #ifdef USE_WEBSERVER } else { - snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_CO2, mqtt_data, mhz_types, mhz_last_ppm); - snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_TEMP, mqtt_data, mhz_types, temperature, TempUnit()); + snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_CO2, mqtt_data, types, mhz_last_ppm); + snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_TEMP, mqtt_data, types, temperature, TempUnit()); #endif // USE_WEBSERVER } } @@ -343,9 +361,9 @@ void MhzShow(boolean json) * Interface \*********************************************************************************************/ -boolean Xsns15(byte function) +bool Xsns15(uint8_t function) { - boolean result = false; + bool result = false; if (mhz_type) { switch (function) { @@ -355,7 +373,7 @@ boolean Xsns15(byte function) case FUNC_EVERY_SECOND: MhzEverySecond(); break; - case FUNC_COMMAND: + case FUNC_COMMAND_SENSOR: if (XSNS_15 == XdrvMailbox.index) { result = MhzCommandSensor(); } diff --git a/sonoff/xsns_16_tsl2561.ino b/sonoff/xsns_16_tsl2561.ino index 0285c55e0..688cdf419 100644 --- a/sonoff/xsns_16_tsl2561.ino +++ b/sonoff/xsns_16_tsl2561.ino @@ -1,7 +1,7 @@ /* xsns_16_tsl2561.ino - TSL2561 light sensor support for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends and Joachim Banzhaf + Copyright (C) 2019 Theo Arends and Joachim Banzhaf This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -48,7 +48,6 @@ bool Tsl2561Read(void) uint16_t scaledFull, scaledIr; uint32_t full, ir; - if (Tsl.available()) { if (Tsl.on()) { if (Tsl.id(id) && Tsl2561Util::autoGain(Tsl, gain, exposure, scaledFull, scaledIr) @@ -58,7 +57,6 @@ bool Tsl2561Read(void) tsl2561_milliLux = 0; } } - } tsl2561_valid = SENSOR_MAX_MISS; return true; } @@ -66,13 +64,14 @@ bool Tsl2561Read(void) void Tsl2561Detect(void) { if (tsl2561_type) { return; } + uint8_t id; - if (!Tsl.available()) { + if (I2cDevice(0x29) || I2cDevice(0x39) || I2cDevice(0x49)) { Tsl.begin(); - if (Tsl.available()) { + if (!Tsl.id(id)) return; + if (Tsl.on()) { tsl2561_type = 1; - snprintf_P(log_data, sizeof(log_data), S_LOG_I2C_FOUND_AT, tsl2561_types, Tsl.address()); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, S_LOG_I2C_FOUND_AT, tsl2561_types, Tsl.address(), id); } } } @@ -88,7 +87,7 @@ void Tsl2561EverySecond(void) if (tsl2561_type) { if (!Tsl2561Read()) { AddLogMissed(tsl2561_types, tsl2561_valid); -// if (!tsl2561_valid) { tsl2561_type = 0; } + if (!tsl2561_valid) { tsl2561_type = 0; } } } } @@ -99,7 +98,7 @@ const char HTTP_SNS_TSL2561[] PROGMEM = "%s{s}TSL2561 " D_ILLUMINANCE "{m}%u.%03u " D_UNIT_LUX "{e}"; // {s} = , {m} = , {e} = #endif // USE_WEBSERVER -void Tsl2561Show(boolean json) +void Tsl2561Show(bool json) { if (tsl2561_valid) { if (json) { @@ -120,9 +119,9 @@ void Tsl2561Show(boolean json) * Interface \*********************************************************************************************/ -boolean Xsns16(byte function) +bool Xsns16(uint8_t function) { - boolean result = false; + bool result = false; if (i2c_flg) { switch (function) { diff --git a/sonoff/xsns_17_senseair.ino b/sonoff/xsns_17_senseair.ino index 967563972..461b41145 100644 --- a/sonoff/xsns_17_senseair.ino +++ b/sonoff/xsns_17_senseair.ino @@ -1,7 +1,7 @@ /* xsns_17_senseair.ino - SenseAir CO2 sensor support for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends + Copyright (C) 2019 Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -70,19 +70,16 @@ void Senseair250ms(void) // Every 250 mSec if (data_ready) { uint8_t error = SenseairModbus->Receive16BitRegister(&value); if (error) { - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_DEBUG "SenseAir response error %d"), error); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "SenseAir response error %d"), error); } else { switch(senseair_read_state) { case 0: // 0x1A (26) READ_TYPE_LOW - S8: fe 04 02 01 77 ec 92 senseair_type = 2; - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_DEBUG "SenseAir type id low %04X"), value); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "SenseAir type id low %04X"), value); break; case 1: // 0x00 (0) READ_ERRORLOG - fe 04 02 00 00 ad 24 if (value) { - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_DEBUG "SenseAir error %04X"), value); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "SenseAir error %04X"), value); } break; case 2: // 0x03 (3) READ_CO2 - fe 04 02 06 2c af 59 @@ -98,13 +95,11 @@ void Senseair250ms(void) // Every 250 mSec case 5: // 0x1C (28) READ_RELAY_STATE - S8: fe 04 02 01 54 ad 4b - firmware version { bool relay_state = value >> 8 & 1; - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_DEBUG "SenseAir relay state %d"), relay_state); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "SenseAir relay state %d"), relay_state); break; } case 6: // 0x0A (10) READ_TEMP_ADJUSTMENT - S8: fe 84 02 f2 f1 - Illegal Data Address - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_DEBUG "SenseAir temp adjustment %d"), value); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "SenseAir temp adjustment %d"), value); break; } } @@ -145,7 +140,7 @@ void SenseairInit(void) } } -void SenseairShow(boolean json) +void SenseairShow(bool json) { char temperature[33]; dtostrfd(senseair_temperature, Settings.flag2.temperature_resolution, temperature); @@ -177,9 +172,9 @@ void SenseairShow(boolean json) * Interface \*********************************************************************************************/ -boolean Xsns17(byte function) +bool Xsns17(uint8_t function) { - boolean result = false; + bool result = false; if (senseair_type) { switch (function) { diff --git a/sonoff/xsns_18_pms5003.ino b/sonoff/xsns_18_pms5003.ino index 271b94301..82959ff88 100644 --- a/sonoff/xsns_18_pms5003.ino +++ b/sonoff/xsns_18_pms5003.ino @@ -1,7 +1,7 @@ /* xsns_18_pms5003.ino - PMS5003-7003 particle concentration sensor support for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends + Copyright (C) 2019 Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -45,7 +45,7 @@ struct pms5003data { /*********************************************************************************************/ -boolean PmsReadData(void) +bool PmsReadData(void) { if (! PmsSerial->available()) { return false; @@ -62,7 +62,7 @@ boolean PmsReadData(void) PmsSerial->readBytes(buffer, 32); PmsSerial->flush(); // Make room for another burst - AddLogSerial(LOG_LEVEL_DEBUG_MORE, buffer, 32); + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, 32); // get checksum ready for (uint8_t i = 0; i < 30; i++) { @@ -128,7 +128,7 @@ const char HTTP_PMS5003_SNS[] PROGMEM = "%s" "{s}PMS5003 " D_PARTICALS_BEYOND " 10 " D_UNIT_MICROMETER "{m}%d " D_UNIT_PARTS_PER_DECILITER "{e}"; // {s} = , {m} = , {e} = #endif // USE_WEBSERVER -void PmsShow(boolean json) +void PmsShow(bool json) { if (pms_valid) { if (json) { @@ -158,9 +158,9 @@ void PmsShow(boolean json) * Interface \*********************************************************************************************/ -boolean Xsns18(byte function) +bool Xsns18(uint8_t function) { - boolean result = false; + bool result = false; if (pms_type) { switch (function) { diff --git a/sonoff/xsns_19_mgs.ino b/sonoff/xsns_19_mgs.ino index 36ce969f7..dacd1430e 100644 --- a/sonoff/xsns_19_mgs.ino +++ b/sonoff/xsns_19_mgs.ino @@ -1,7 +1,7 @@ /* xsns_19_mgs.ino - Xadow and Grove Mutichannel Gas sensor support for Sonoff-Tasmota - Copyright (C) 2018 Palich2000 and Theo Arends + Copyright (C) 2019 Palich2000 and Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -38,12 +38,11 @@ void MGSInit(void) { gas.begin(MGS_SENSOR_ADDR); } -boolean MGSPrepare(void) +bool MGSPrepare(void) { gas.begin(MGS_SENSOR_ADDR); if (!gas.isError()) { - snprintf_P(log_data, sizeof(log_data), S_LOG_I2C_FOUND_AT, "MultiGasSensor", MGS_SENSOR_ADDR); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, S_LOG_I2C_FOUND_AT, "MultiGasSensor", MGS_SENSOR_ADDR); return true; } else { return false; @@ -61,7 +60,7 @@ char* measure_gas(int gas_type, char* buffer) const char HTTP_MGS_GAS[] PROGMEM = "%s{s}MGS %s{m}%s " D_UNIT_PARTS_PER_MILLION "{e}"; // {s} = , {m} = , {e} = #endif // USE_WEBSERVER -void MGSShow(boolean json) +void MGSShow(bool json) { char buffer[33]; if (json) { @@ -91,9 +90,9 @@ void MGSShow(boolean json) * Interface \*********************************************************************************************/ -boolean Xsns19(byte function) +bool Xsns19(uint8_t function) { - boolean result = false; + bool result = false; static int detected = false; if (i2c_flg) { diff --git a/sonoff/xsns_20_novasds.ino b/sonoff/xsns_20_novasds.ino index dfb08ac5a..d40301508 100644 --- a/sonoff/xsns_20_novasds.ino +++ b/sonoff/xsns_20_novasds.ino @@ -1,7 +1,7 @@ /* xsns_20_novasds.ino - Nova SDS011/SDS021 particle concentration sensor support for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends + Copyright (C) 2019 Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -21,7 +21,7 @@ /*********************************************************************************************\ * Nova Fitness SDS011 (and possibly SDS021) particle concentration sensor * For background information see http://aqicn.org/sensor/sds011/ - * For protocol specification see + * For protocol specification see * https://cdn.sparkfun.com/assets/parts/1/2/2/7/5/Laser_Dust_Sensor_Control_Protocol_V1.3.pdf * * Hardware Serial will be selected if GPIO3 = [SDS0X01] @@ -74,18 +74,17 @@ struct sds011data { #define NOVA_SDS_SLEEP 1 // Subcmnd "sleep mode" -bool NovaSdsCommand(uint8_t byte1, uint8_t byte2, uint8_t byte3, uint16_t sensorid, byte *buffer) +bool NovaSdsCommand(uint8_t byte1, uint8_t byte2, uint8_t byte3, uint16_t sensorid, uint8_t *buffer) { uint8_t novasds_cmnd[19] = {0xAA, 0xB4, byte1, byte2, byte3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, (uint8_t)(sensorid & 0xFF), (uint8_t)((sensorid>>8) & 0xFF), 0x00, 0xAB}; // calc crc - for (byte i = 2; i < 17; i++) { + for (uint8_t i = 2; i < 17; i++) { novasds_cmnd[17] += novasds_cmnd[i]; } - //~ snprintf_P(log_data, sizeof(log_data), PSTR("SDS: Send %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X"), + //~ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SDS: Send %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X"), //~ novasds_cmnd[0],novasds_cmnd[1],novasds_cmnd[2],novasds_cmnd[3],novasds_cmnd[4],novasds_cmnd[5],novasds_cmnd[6],novasds_cmnd[7],novasds_cmnd[8],novasds_cmnd[9], //~ novasds_cmnd[10],novasds_cmnd[11],novasds_cmnd[12],novasds_cmnd[13],novasds_cmnd[14],novasds_cmnd[15],novasds_cmnd[16],novasds_cmnd[17],novasds_cmnd[18]); - //~ AddLog(LOG_LEVEL_DEBUG); // send cmnd NovaSdsSerial->write(novasds_cmnd, sizeof(novasds_cmnd)); NovaSdsSerial->flush(); @@ -97,7 +96,7 @@ bool NovaSdsCommand(uint8_t byte1, uint8_t byte2, uint8_t byte3, uint16_t sensor // timeout return false; } - byte recbuf[10]; + uint8_t recbuf[10]; memset(recbuf, 0, sizeof(recbuf)); // sync to 0xAA header while ( (TimePassedSince(cmndtime) < NOVA_SDS_RECDATA_TIMEOUT) && ( NovaSdsSerial->available() > 0) && (0xAA != (recbuf[0] = NovaSdsSerial->read())) ); @@ -108,7 +107,7 @@ bool NovaSdsCommand(uint8_t byte1, uint8_t byte2, uint8_t byte3, uint16_t sensor // read rest (9 of 10 bytes) of message NovaSdsSerial->readBytes(&recbuf[1], 9); - AddLogSerial(LOG_LEVEL_DEBUG_MORE, recbuf, sizeof(recbuf)); + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, recbuf, sizeof(recbuf)); if ( NULL != buffer ) { // return data to buffer @@ -134,7 +133,7 @@ void NovaSdsSetWorkPeriod(void) bool NovaSdsReadData(void) { - byte d[10]; + uint8_t d[10]; if ( ! NovaSdsCommand(NOVA_SDS_QUERY_DATA, 0, 0, NOVA_SDS_DEVICE_ID, d) ) { return false; } @@ -186,7 +185,7 @@ const char HTTP_SDS0X1_SNS[] PROGMEM = "%s" "{s}SDS0X1 " D_ENVIRONMENTAL_CONCENTRATION " 10 " D_UNIT_MICROMETER "{m}%s " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}"; // {s} = , {m} = , {e} = #endif // USE_WEBSERVER -void NovaSdsShow(boolean json) +void NovaSdsShow(bool json) { if (novasds_valid) { float pm10f = (float)(novasds_data.pm100) / 10.0f; @@ -215,9 +214,9 @@ void NovaSdsShow(boolean json) * Interface \*********************************************************************************************/ -boolean Xsns20(byte function) +bool Xsns20(uint8_t function) { - boolean result = false; + bool result = false; if (novasds_type) { switch (function) { diff --git a/sonoff/xsns_21_sgp30.ino b/sonoff/xsns_21_sgp30.ino index 7c22da514..48fde6793 100644 --- a/sonoff/xsns_21_sgp30.ino +++ b/sonoff/xsns_21_sgp30.ino @@ -1,7 +1,7 @@ /* xsns_21_sgp30.ino - SGP30 gas and air quality sensor support for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends + Copyright (C) 2019 Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -44,10 +44,8 @@ void Sgp30Update(void) // Perform every second to ensure proper operation of th if (!sgp30_type) { if (sgp.begin()) { sgp30_type = 1; -// snprintf_P(log_data, sizeof(log_data), PSTR("SGP: Serialnumber 0x%04X-0x%04X-0x%04X"), sgp.serialnumber[0], sgp.serialnumber[1], sgp.serialnumber[2]); -// AddLog(LOG_LEVEL_DEBUG); - snprintf_P(log_data, sizeof(log_data), S_LOG_I2C_FOUND_AT, "SGP30", 0x58); - AddLog(LOG_LEVEL_DEBUG); +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SGP: Serialnumber 0x%04X-0x%04X-0x%04X"), sgp.serialnumber[0], sgp.serialnumber[1], sgp.serialnumber[2]); + AddLog_P2(LOG_LEVEL_DEBUG, S_LOG_I2C_FOUND_AT, "SGP30", 0x58); } } else { if (!sgp.IAQmeasure()) return; // Measurement failed @@ -59,8 +57,7 @@ void Sgp30Update(void) // Perform every second to ensure proper operation of th uint16_t eCO2_base; if (!sgp.getIAQBaseline(&eCO2_base, &TVOC_base)) return; // Failed to get baseline readings -// snprintf_P(log_data, sizeof(log_data), PSTR("SGP: Baseline values eCO2 0x%04X, TVOC 0x%04X"), eCO2_base, TVOC_base); -// AddLog(LOG_LEVEL_DEBUG); +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SGP: Baseline values eCO2 0x%04X, TVOC 0x%04X"), eCO2_base, TVOC_base); } sgp30_ready = 1; } @@ -70,7 +67,7 @@ const char HTTP_SNS_SGP30[] PROGMEM = "%s" "{s}SGP30 " D_ECO2 "{m}%d " D_UNIT_PARTS_PER_MILLION "{e}" // {s} = , {m} = , {e} = "{s}SGP30 " D_TVOC "{m}%d " D_UNIT_PARTS_PER_BILLION "{e}"; -void Sgp30Show(boolean json) +void Sgp30Show(bool json) { if (sgp30_ready) { if (json) { @@ -90,9 +87,9 @@ void Sgp30Show(boolean json) * Interface \*********************************************************************************************/ -boolean Xsns21(byte function) +bool Xsns21(uint8_t function) { - boolean result = false; + bool result = false; if (i2c_flg) { switch (function) { diff --git a/sonoff/xsns_22_sr04.ino b/sonoff/xsns_22_sr04.ino index 0278011e1..a52898728 100644 --- a/sonoff/xsns_22_sr04.ino +++ b/sonoff/xsns_22_sr04.ino @@ -1,7 +1,7 @@ /* xsns_22_sr04.ino - SR04 ultrasonic sensor support for Sonoff-Tasmota - Copyright (C) 2018 Nuno Ferreira and Theo Arends + Copyright (C) 2019 Nuno Ferreira and Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -28,6 +28,8 @@ * - https://www.dfrobot.com/wiki/index.php/Weather-proof_Ultrasonic_Sensor_SKU_:_SEN0207 \*********************************************************************************************/ +#define XSNS_22 22 + uint8_t sr04_echo_pin = 0; uint8_t sr04_trig_pin = 0; real64_t distance; @@ -46,7 +48,7 @@ const char HTTP_SNS_DISTANCE[] PROGMEM = "%s{s}SR04 " D_DISTANCE "{m}%s" D_UNIT_CENTIMETER "{e}"; // {s} = , {m} = , {e} = #endif // USE_WEBSERVER -void Sr04Show(boolean json) +void Sr04Show(bool json) { distance = (real64_t)(sonar->ping_median(5))/ US_ROUNDTRIP_CM; @@ -73,11 +75,9 @@ void Sr04Show(boolean json) * Interface \*********************************************************************************************/ -#define XSNS_22 - -boolean Xsns22(byte function) +bool Xsns22(uint8_t function) { - boolean result = false; + bool result = false; if ((pin[GPIO_SR04_ECHO] < 99) && (pin[GPIO_SR04_TRIG] < 99)) { switch (function) { diff --git a/sonoff/xsns_23_sdm120.ino b/sonoff/xsns_23_sdm120.ino index 9f27927e8..67971845a 100644 --- a/sonoff/xsns_23_sdm120.ino +++ b/sonoff/xsns_23_sdm120.ino @@ -1,7 +1,7 @@ /* xsns_23_sdm120.ino - Eastron SDM120-Modbus energy meter support for Sonoff-Tasmota - Copyright (C) 2018 Gennaro Tortone + Copyright (C) 2019 Gennaro Tortone This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -165,8 +165,7 @@ void SDM120250ms(void) // Every 250 mSec if (data_ready) { uint8_t error = SDM120_ModbusReceive(&value); if (error) { - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_DEBUG "SDM120 response error %d"), error); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "SDM120 response error %d"), error); } else { switch(sdm120_read_state) { case 0: @@ -281,7 +280,7 @@ const char HTTP_SNS_SDM120_DATA[] PROGMEM = "%s" ; #endif // USE_WEBSERVER -void SDM120Show(boolean json) +void SDM120Show(bool json) { char voltage[33]; dtostrfd(sdm120_voltage, Settings.flag2.voltage_resolution, voltage); @@ -343,9 +342,9 @@ void SDM120Show(boolean json) * Interface \*********************************************************************************************/ -boolean Xsns23(byte function) +bool Xsns23(uint8_t function) { - boolean result = false; + bool result = false; if (sdm120_type) { switch (function) { diff --git a/sonoff/xsns_24_si1145.ino b/sonoff/xsns_24_si1145.ino index 7b051ebe0..19855bf42 100644 --- a/sonoff/xsns_24_si1145.ino +++ b/sonoff/xsns_24_si1145.ino @@ -1,7 +1,7 @@ /* xsns_24_si1145.ino - SI1145/46/47 UV Index / IR / Visible light sensor support for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends + Copyright (C) 2019 Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -276,7 +276,7 @@ void Si1145DeInit(void) Si1145WriteByte(SI114X_COMMAND, SI114X_PSALS_AUTO); } -boolean Si1145Begin(void) +bool Si1145Begin(void) { if (!Si1145Present()) { return false; } @@ -310,8 +310,7 @@ void Si1145Update(void) if (!si1145_type) { if (Si1145Begin()) { si1145_type = 1; - snprintf_P(log_data, sizeof(log_data), S_LOG_I2C_FOUND_AT, "SI1145", SI114X_ADDR); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, S_LOG_I2C_FOUND_AT, "SI1145", SI114X_ADDR); } } } @@ -323,14 +322,14 @@ const char HTTP_SNS_SI1145[] PROGMEM = "%s" "{s}SI1145 " D_UV_INDEX "{m}%d.%d{e}"; #endif // USE_WEBSERVER -void Si1145Show(boolean json) +void Si1145Show(bool json) { if (si1145_type && Si1145Present()) { uint16_t visible = Si1145ReadVisible(); uint16_t infrared = Si1145ReadIR(); uint16_t uvindex = Si1145ReadUV(); if (json) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"SI1145\":{\"" D_JSON_ILLUMINANCE "\":%d,\"" D_JSON_INFRARED "\":%d,\"" D_JSON_UVINDEX "\":%d.%d}"), + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"SI1145\":{\"" D_JSON_ILLUMINANCE "\":%d,\"" D_JSON_INFRARED "\":%d,\"" D_JSON_UV_INDEX "\":%d.%d}"), mqtt_data, visible, infrared, uvindex /100, uvindex %100); #ifdef USE_DOMOTICZ if (0 == tele_period) DomoticzSensor(DZ_ILLUMINANCE, visible); @@ -349,9 +348,9 @@ void Si1145Show(boolean json) * Interface \*********************************************************************************************/ -boolean Xsns24(byte function) +bool Xsns24(uint8_t function) { - boolean result = false; + bool result = false; if (i2c_flg) { switch (function) { diff --git a/sonoff/xsns_25_sdm630.ino b/sonoff/xsns_25_sdm630.ino index 9060444b3..2f8e0acd8 100644 --- a/sonoff/xsns_25_sdm630.ino +++ b/sonoff/xsns_25_sdm630.ino @@ -1,7 +1,7 @@ /* xsns_25_sdm630.ino - Eastron SDM630-Modbus energy meter support for Sonoff-Tasmota - Copyright (C) 2018 Gennaro Tortone + Copyright (C) 2019 Gennaro Tortone This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -155,8 +155,7 @@ void SDM630250ms(void) // Every 250 mSec if (data_ready) { uint8_t error = SDM630_ModbusReceive(&value); if (error) { - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_DEBUG "SDM630 response error %d"), error); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "SDM630 response error %d"), error); } else { switch(sdm630_read_state) { case 0: @@ -267,7 +266,7 @@ const char HTTP_SNS_SDM630_DATA[] PROGMEM = "%s" "{s}SDM630 " D_ENERGY_TOTAL "{m}%s " D_UNIT_KILOWATTHOUR "{e}"; #endif // USE_WEBSERVER -void SDM630Show(boolean json) +void SDM630Show(bool json) { char voltage_l1[33]; dtostrfd(sdm630_voltage[0], Settings.flag2.voltage_resolution, voltage_l1); @@ -326,9 +325,9 @@ void SDM630Show(boolean json) * Interface \*********************************************************************************************/ -boolean Xsns25(byte function) +bool Xsns25(uint8_t function) { - boolean result = false; + bool result = false; if (sdm630_type) { switch (function) { diff --git a/sonoff/xsns_26_lm75ad.ino b/sonoff/xsns_26_lm75ad.ino index 3b548fa8f..8a48327bf 100644 --- a/sonoff/xsns_26_lm75ad.ino +++ b/sonoff/xsns_26_lm75ad.ino @@ -1,7 +1,7 @@ /* xsns_26_lm75ad.ino - Support for I2C LM75AD Temperature Sensor - Copyright (C) 2018 Andre Thomas and Theo Arends + Copyright (C) 2019 Andre Thomas and Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -53,13 +53,12 @@ void LM75ADDetect(void) if (lm75ad_type) { return; } uint16_t buffer; - for (byte i = 0; i < sizeof(lm75ad_addresses); i++) { + for (uint8_t i = 0; i < sizeof(lm75ad_addresses); i++) { lm75ad_address = lm75ad_addresses[i]; if (I2cValidRead16(&buffer, lm75ad_address, LM75_THYST_REGISTER)) { if (buffer == 0x4B00) { lm75ad_type = 1; - snprintf_P(log_data, sizeof(log_data), S_LOG_I2C_FOUND_AT, "LM75AD", lm75ad_address); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, S_LOG_I2C_FOUND_AT, "LM75AD", lm75ad_address); break; } } @@ -78,7 +77,7 @@ float LM75ADGetTemp(void) { return ConvertTemp(sign * t * 0.125); } -void LM75ADShow(boolean json) +void LM75ADShow(bool json) { if (lm75ad_type) { float t = LM75ADGetTemp(); @@ -102,9 +101,9 @@ void LM75ADShow(boolean json) * Interface \*********************************************************************************************/ -boolean Xsns26(byte function) +bool Xsns26(uint8_t function) { - boolean result = false; + bool result = false; if (i2c_flg) { switch (function) { diff --git a/sonoff/xsns_27_apds9960.ino b/sonoff/xsns_27_apds9960.ino index 33b98a5d2..765c89263 100644 --- a/sonoff/xsns_27_apds9960.ino +++ b/sonoff/xsns_27_apds9960.ino @@ -1,7 +1,7 @@ /* xsns_27_apds9960.ino - Support for I2C APDS9960 Proximity Sensor for Sonoff-Tasmota - Copyright (C) 2018 Shawn Hymel/Sparkfun and Theo Arends + Copyright (C) 2019 Shawn Hymel/Sparkfun and Theo Arends Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -1518,8 +1518,7 @@ int16_t readGesture(void) if (gesture_loop_counter == APDS9960_MAX_GESTURE_CYCLES){ // We will escape after a few loops disableGestureSensor(); // stop the sensor to prevent problems with power consumption/blocking and return to the main loop APDS9960_overload = true; // we report this as "long"-gesture - snprintf_P(log_data, sizeof(log_data), PSTR("Sensor overload")); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P(LOG_LEVEL_DEBUG, PSTR("Sensor overload")); } gesture_loop_counter += 1; /* Wait some time to collect next batch of FIFO data */ @@ -1794,33 +1793,32 @@ void handleGesture(void) { if (isGestureAvailable() ) { switch (readGesture()) { case DIR_UP: - snprintf_P(log_data, sizeof(log_data), PSTR("UP")); + AddLog_P(LOG_LEVEL_DEBUG, PSTR("UP")); snprintf_P(currentGesture, sizeof(currentGesture), PSTR("Up")); break; case DIR_DOWN: - snprintf_P(log_data, sizeof(log_data), PSTR("DOWN")); + AddLog_P(LOG_LEVEL_DEBUG, PSTR("DOWN")); snprintf_P(currentGesture, sizeof(currentGesture), PSTR("Down")); break; case DIR_LEFT: - snprintf_P(log_data, sizeof(log_data), PSTR("LEFT")); + AddLog_P(LOG_LEVEL_DEBUG, PSTR("LEFT")); snprintf_P(currentGesture, sizeof(currentGesture), PSTR("Left")); break; case DIR_RIGHT: - snprintf_P(log_data, sizeof(log_data), PSTR("RIGHT")); + AddLog_P(LOG_LEVEL_DEBUG, PSTR("RIGHT")); snprintf_P(currentGesture, sizeof(currentGesture), PSTR("Right")); break; default: if(APDS9960_overload) { - snprintf_P(log_data, sizeof(log_data), PSTR("LONG")); + AddLog_P(LOG_LEVEL_DEBUG, PSTR("LONG")); snprintf_P(currentGesture, sizeof(currentGesture), PSTR("Long")); } else{ - snprintf_P(log_data, sizeof(log_data), PSTR("NONE")); + AddLog_P(LOG_LEVEL_DEBUG, PSTR("NONE")); snprintf_P(currentGesture, sizeof(currentGesture), PSTR("None")); } } - AddLog(LOG_LEVEL_DEBUG); mqtt_data[0] = '\0'; if (MqttShowSensor()) { @@ -1903,13 +1901,12 @@ bool APDS9960_detect(void) return true; } - boolean success = false; + bool success = false; APDS9960type = I2cRead8(APDS9960_I2C_ADDR, APDS9960_ID); if (APDS9960type == APDS9960_CHIPID_1 || APDS9960type == APDS9960_CHIPID_2) { strcpy_P(APDS9960stype, PSTR("APDS9960")); - snprintf_P(log_data, sizeof(log_data), S_LOG_I2C_FOUND_AT, APDS9960stype, APDS9960_I2C_ADDR); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, S_LOG_I2C_FOUND_AT, APDS9960stype, APDS9960_I2C_ADDR); if (APDS9960_init()) { success = true; AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "APDS9960 initialized")); @@ -1919,12 +1916,10 @@ bool APDS9960_detect(void) } else { if (APDS9960type == APDS9930_CHIPID_1 || APDS9960type == APDS9930_CHIPID_2) { - snprintf_P(log_data, sizeof(log_data), PSTR("APDS9930 found at address 0x%x, unsupported chip"), APDS9960_I2C_ADDR); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("APDS9930 found at address 0x%x, unsupported chip"), APDS9960_I2C_ADDR); } else{ - snprintf_P(log_data, sizeof(log_data), PSTR("APDS9960 not found at address 0x%x"), APDS9960_I2C_ADDR); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("APDS9960 not found at address 0x%x"), APDS9960_I2C_ADDR); } } currentGesture[0] = '\0'; @@ -1935,7 +1930,7 @@ bool APDS9960_detect(void) * Presentation \*********************************************************************************************/ -void APDS9960_show(boolean json) +void APDS9960_show(bool json) { if (!APDS9960type) { return; @@ -1993,7 +1988,7 @@ void APDS9960_show(boolean json) bool APDS9960CommandSensor(void) { - boolean serviced = true; + bool serviced = true; switch (XdrvMailbox.payload) { case 0: // Off @@ -2039,9 +2034,9 @@ bool APDS9960CommandSensor(void) * Interface \*********************************************************************************************/ -boolean Xsns27(byte function) +bool Xsns27(uint8_t function) { - boolean result = false; + bool result = false; if (i2c_flg) { if (FUNC_INIT == function) { @@ -2051,7 +2046,7 @@ boolean Xsns27(byte function) case FUNC_EVERY_50_MSECOND: APDS9960_loop(); break; - case FUNC_COMMAND: + case FUNC_COMMAND_SENSOR: if (XSNS_27 == XdrvMailbox.index) { result = APDS9960CommandSensor(); } diff --git a/sonoff/xsns_28_tm1638.ino b/sonoff/xsns_28_tm1638.ino index 61852b40d..590432dfd 100644 --- a/sonoff/xsns_28_tm1638.ino +++ b/sonoff/xsns_28_tm1638.ino @@ -1,7 +1,7 @@ /* xsns_28_tm1638.ino - TM1638 8 switch, led and 7 segment unit support for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends + Copyright (C) 2019 Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -46,7 +46,7 @@ uint8_t tm1638_state = 0; * and from library https://github.com/MartyMacGyver/TM1638-demos-and-examples \*********************************************************************************************/ -void Tm16XXSend(byte data) +void Tm16XXSend(uint8_t data) { for (uint8_t i = 0; i < 8; i++) { digitalWrite(tm1638_data_pin, !!(data & (1 << i))); @@ -56,14 +56,14 @@ void Tm16XXSend(byte data) } } -void Tm16XXSendCommand(byte cmd) +void Tm16XXSendCommand(uint8_t cmd) { digitalWrite(tm1638_strobe_pin, LOW); Tm16XXSend(cmd); digitalWrite(tm1638_strobe_pin, HIGH); } -void TM16XXSendData(byte address, byte data) +void TM16XXSendData(uint8_t address, uint8_t data) { Tm16XXSendCommand(0x44); digitalWrite(tm1638_strobe_pin, LOW); @@ -72,9 +72,9 @@ void TM16XXSendData(byte address, byte data) digitalWrite(tm1638_strobe_pin, HIGH); } -byte Tm16XXReceive(void) +uint8_t Tm16XXReceive(void) { - byte temp = 0; + uint8_t temp = 0; // Pull-up on pinMode(tm1638_data_pin, INPUT); @@ -103,7 +103,7 @@ void Tm16XXClearDisplay(void) } } -void Tm1638SetLED(byte color, byte pos) +void Tm1638SetLED(uint8_t color, uint8_t pos) { TM16XXSendData((pos << 1) + 1, color); } @@ -111,7 +111,7 @@ void Tm1638SetLED(byte color, byte pos) void Tm1638SetLEDs(word leds) { for (int i = 0; i < tm1638_displays; i++) { - byte color = 0; + uint8_t color = 0; if ((leds & (1 << i)) != 0) { color |= TM1638_COLOR_RED; @@ -125,9 +125,9 @@ void Tm1638SetLEDs(word leds) } } -byte Tm1638GetButtons(void) +uint8_t Tm1638GetButtons(void) { - byte keys = 0; + uint8_t keys = 0; digitalWrite(tm1638_strobe_pin, LOW); Tm16XXSend(0x42); @@ -174,10 +174,10 @@ void TmInit(void) void TmLoop(void) { if (tm1638_state) { - byte buttons = Tm1638GetButtons(); - for (byte i = 0; i < MAX_SWITCHES; i++) { - virtualswitch[i] = (buttons &1) ^1; - byte color = (virtualswitch[i]) ? TM1638_COLOR_NONE : TM1638_COLOR_RED; + uint8_t buttons = Tm1638GetButtons(); + for (uint8_t i = 0; i < MAX_SWITCHES; i++) { + SwitchSetVirtual(i, (buttons &1) ^1); + uint8_t color = (SwitchGetVirtual(i)) ? TM1638_COLOR_NONE : TM1638_COLOR_RED; Tm1638SetLED(color, i); buttons >>= 1; } @@ -186,7 +186,7 @@ void TmLoop(void) } /* -void TmShow(boolean json) +void TmShow(bool json) { if (tm1638_type) { @@ -198,9 +198,9 @@ void TmShow(boolean json) * Interface \*********************************************************************************************/ -boolean Xsns28(byte function) +bool Xsns28(uint8_t function) { - boolean result = false; + bool result = false; if (tm1638_type) { switch (function) { diff --git a/sonoff/xsns_29_mcp230xx.ino b/sonoff/xsns_29_mcp230xx.ino index 64f3351a5..d7aaaf2ef 100644 --- a/sonoff/xsns_29_mcp230xx.ino +++ b/sonoff/xsns_29_mcp230xx.ino @@ -1,7 +1,7 @@ /* xsns_29_mcp230xx.ino - Support for I2C MCP23008/MCP23017 GPIO Expander - Copyright (C) 2018 Andre Thomas and Theo Arends + Copyright (C) 2019 Andre Thomas and Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -81,7 +81,7 @@ void MCP230xx_CheckForIntCounter(void) { } } } - + void MCP230xx_CheckForIntRetainer(void) { uint8_t en = 0; for (uint8_t ca=0;ca<16;ca++) { @@ -210,15 +210,13 @@ void MCP230xx_Detect(void) if (I2cValidRead8(&buffer, USE_MCP230xx_ADDR, MCP230xx_IOCON)) { if (0x00 == buffer) { mcp230xx_type = 1; // We have a MCP23008 - snprintf_P(log_data, sizeof(log_data), S_LOG_I2C_FOUND_AT, "MCP23008", USE_MCP230xx_ADDR); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, S_LOG_I2C_FOUND_AT, "MCP23008", USE_MCP230xx_ADDR); mcp230xx_pincount = 8; MCP230xx_ApplySettings(); } else { if (0x80 == buffer) { mcp230xx_type = 2; // We have a MCP23017 - snprintf_P(log_data, sizeof(log_data), S_LOG_I2C_FOUND_AT, "MCP23017", USE_MCP230xx_ADDR); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, S_LOG_I2C_FOUND_AT, "MCP23017", USE_MCP230xx_ADDR); mcp230xx_pincount = 16; // Reset bank mode to 0 I2cWrite8(USE_MCP230xx_ADDR, MCP230xx_IOCON, 0x00); @@ -325,7 +323,7 @@ void MCP230xx_CheckForInterrupt(void) { } } -void MCP230xx_Show(boolean json) +void MCP230xx_Show(bool json) { if (mcp230xx_type) { if (json) { @@ -426,8 +424,8 @@ void MCP230xx_Reset(uint8_t pinmode) { } bool MCP230xx_Command(void) { - boolean serviced = true; - boolean validpin = false; + bool serviced = true; + bool validpin = false; uint8_t paramcount = 0; if (XdrvMailbox.data_len > 0) { paramcount=1; @@ -498,8 +496,7 @@ bool MCP230xx_Command(void) { if (Settings.mcp230xx_config[pin].int_count_en) { Settings.mcp230xx_config[pin].int_count_en=0; MCP230xx_CheckForIntCounter(); - snprintf_P(log_data, sizeof(log_data), PSTR("*** WARNING *** - Disabled INTCNT for pin D%i"),pin); - AddLog(LOG_LEVEL_INFO); + AddLog_P2(LOG_LEVEL_INFO, PSTR("*** WARNING *** - Disabled INTCNT for pin D%i"),pin); } snprintf_P(mqtt_data, sizeof(mqtt_data), MCP230XX_INTCFG_RESPONSE,"DEF",pin,Settings.mcp230xx_config[pin].int_report_defer); // "{\"MCP230xx_INT%s\":{\"D_%i\":%i}}"; return serviced; @@ -537,17 +534,14 @@ bool MCP230xx_Command(void) { Settings.mcp230xx_config[pin].int_count_en=intcnt; if (Settings.mcp230xx_config[pin].int_report_defer) { Settings.mcp230xx_config[pin].int_report_defer=0; - snprintf_P(log_data, sizeof(log_data), PSTR("*** WARNING *** - Disabled INTDEF for pin D%i"),pin); - AddLog(LOG_LEVEL_INFO); + AddLog_P2(LOG_LEVEL_INFO, PSTR("*** WARNING *** - Disabled INTDEF for pin D%i"),pin); } if (Settings.mcp230xx_config[pin].int_report_mode < 3) { Settings.mcp230xx_config[pin].int_report_mode=3; - snprintf_P(log_data, sizeof(log_data), PSTR("*** WARNING *** - Disabled immediate interrupt/telemetry reporting for pin D%i"),pin); - AddLog(LOG_LEVEL_INFO); + AddLog_P2(LOG_LEVEL_INFO, PSTR("*** WARNING *** - Disabled immediate interrupt/telemetry reporting for pin D%i"),pin); } if ((Settings.mcp230xx_config[pin].int_count_en) && (!Settings.mcp230xx_int_timer)) { - snprintf_P(log_data, sizeof(log_data), PSTR("*** WARNING *** - INTCNT enabled for pin D%i but global INTTIMER is disabled!"),pin); - AddLog(LOG_LEVEL_INFO); + AddLog_P2(LOG_LEVEL_INFO, PSTR("*** WARNING *** - INTCNT enabled for pin D%i but global INTTIMER is disabled!"),pin); } MCP230xx_CheckForIntCounter(); // update register on whether or not we should be counting interrupts snprintf_P(mqtt_data, sizeof(mqtt_data), MCP230XX_INTCFG_RESPONSE,"CNT",pin,Settings.mcp230xx_config[pin].int_count_en); // "{\"MCP230xx_INT%s\":{\"D_%i\":%i}}"; @@ -605,7 +599,7 @@ bool MCP230xx_Command(void) { } uint8_t pin = atoi(subStr(sub_string, XdrvMailbox.data, ",", 1)); - + if (pin < mcp230xx_pincount) { if (0 == pin) { if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 1), "0")) validpin=true; @@ -767,7 +761,7 @@ void MCP230xx_Interrupt_Retain_Report(void) { uint16_t retainresult = 0; snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_TIME "\":\"%s\",\"MCP_INTRETAIN\": {"), GetDateAndTime(DT_LOCAL).c_str()); for (uint8_t pinx = 0;pinx < mcp230xx_pincount;pinx++) { - if (Settings.mcp230xx_config[pinx].int_retain_flag) { + if (Settings.mcp230xx_config[pinx].int_retain_flag) { snprintf_P(mqtt_data,sizeof(mqtt_data), PSTR("%s\"D%i\":%i,"),mqtt_data,pinx,mcp230xx_int_retainer[pinx]); retainresult |= (((mcp230xx_int_retainer[pinx])&1) << pinx); mcp230xx_int_retainer[pinx]=0; @@ -781,9 +775,9 @@ void MCP230xx_Interrupt_Retain_Report(void) { Interface \*********************************************************************************************/ -boolean Xsns29(byte function) +bool Xsns29(uint8_t function) { - boolean result = false; + bool result = false; if (i2c_flg) { switch (function) { @@ -820,7 +814,7 @@ boolean Xsns29(byte function) case FUNC_JSON_APPEND: MCP230xx_Show(1); break; - case FUNC_COMMAND: + case FUNC_COMMAND_SENSOR: if (XSNS_29 == XdrvMailbox.index) { result = MCP230xx_Command(); } diff --git a/sonoff/xsns_30_mpr121.ino b/sonoff/xsns_30_mpr121.ino index 4021247a6..01431faa6 100644 --- a/sonoff/xsns_30_mpr121.ino +++ b/sonoff/xsns_30_mpr121.ino @@ -220,8 +220,7 @@ void Mpr121Init(struct mpr121 *pS) if (pS->connected[i]) { // Log sensor found - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_I2C "MPR121(%c) " D_FOUND_AT " 0x%X"), pS->id[i], pS->i2c_addr[i]); - AddLog(LOG_LEVEL_INFO); + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_I2C "MPR121(%c) " D_FOUND_AT " 0x%X"), pS->id[i], pS->i2c_addr[i]); // Set thresholds for registers 0x41 - 0x5A (ExTTH and ExRTH) for (uint8_t j = 0; j < 13; j++) { @@ -283,12 +282,9 @@ void Mpr121Init(struct mpr121 *pS) // Check if sensor is running pS->running[i] = (0x00 != I2cRead8(pS->i2c_addr[i], MPR121_ECR_REG)); - if (pS->running[i]) { - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_I2C "MPR121%c: Running"), pS->id[i]); - } else { - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_I2C "MPR121%c: NOT Running"), pS->id[i]); - } - AddLog(LOG_LEVEL_INFO); + + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_I2C "MPR121%c: %sRunning"), pS->id[i], (pS->running[i]) ? "" : "NOT"); + } else { // Make sure running is false @@ -299,8 +295,7 @@ void Mpr121Init(struct mpr121 *pS) // Display no sensor found message if (!(pS->connected[0] || pS->connected[1] || pS->connected[2] || pS->connected[3])) { - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_I2C "MPR121: No sensors found")); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_I2C "MPR121: No sensors found")); } } // void Mpr121Init(struct mpr121 *s) @@ -318,7 +313,7 @@ void Mpr121Init(struct mpr121 *pS) * @post None. * */ -void Mpr121Show(struct mpr121 *pS, byte function) +void Mpr121Show(struct mpr121 *pS, uint8_t function) { // Loop through sensors @@ -329,8 +324,7 @@ void Mpr121Show(struct mpr121 *pS, byte function) // Read data if (!I2cValidRead16LE(&pS->current[i], pS->i2c_addr[i], MPR121_ELEX_REG)) { - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_I2C "MPR121%c: ERROR: Cannot read data!"), pS->id[i]); - AddLog(LOG_LEVEL_ERROR); + AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_I2C "MPR121%c: ERROR: Cannot read data!"), pS->id[i]); Mpr121Init(pS); return; } @@ -339,9 +333,7 @@ void Mpr121Show(struct mpr121 *pS, byte function) // Clear OVCF bit I2cWrite8(pS->i2c_addr[i], MPR121_ELEX_REG, 0x00); - snprintf_P(log_data, sizeof(log_data), - PSTR(D_LOG_I2C "MPR121%c: ERROR: Excess current detected! Fix circuits if it happens repeatedly! Soft-resetting MPR121 ..."), pS->id[i]); - AddLog(LOG_LEVEL_ERROR); + AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_I2C "MPR121%c: ERROR: Excess current detected! Fix circuits if it happens repeatedly! Soft-resetting MPR121 ..."), pS->id[i]); Mpr121Init(pS); return; } @@ -384,7 +376,7 @@ void Mpr121Show(struct mpr121 *pS, byte function) } } // if->running } // for-loop i -} // void Mpr121Show(byte function) +} // void Mpr121Show(uint8_t function) /*********************************************************************************************\ * Interface @@ -400,15 +392,15 @@ void Mpr121Show(struct mpr121 *pS, byte function) * FUNC_WEB_APPEND for displaying data in the Tasmota web-interface * * @param byte function Tasmota function ID. - * @return boolean ??? + * @return bool ??? * @pre None. * @post None. * */ -boolean Xsns30(byte function) +bool Xsns30(uint8_t function) { // ??? - boolean result = false; + bool result = false; // Sensor state/data struct static struct mpr121 mpr121; @@ -440,7 +432,7 @@ boolean Xsns30(byte function) #endif // USE_WEBSERVER } } - // Return boolean result + // Return bool result return result; } diff --git a/sonoff/xsns_31_ccs811.ino b/sonoff/xsns_31_ccs811.ino index 624dac34e..173cbe388 100644 --- a/sonoff/xsns_31_ccs811.ino +++ b/sonoff/xsns_31_ccs811.ino @@ -1,7 +1,7 @@ /* xsns_31_ccs811.ino - CCS811 gas and air quality sensor support for Sonoff-Tasmota - Copyright (C) 2018 Gerhard Mutz and Theo Arends + Copyright (C) 2019 Gerhard Mutz and Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -52,11 +52,9 @@ void CCS811Update(void) // Perform every n second sint8_t res = ccs.begin(CCS811_ADDRESS); if (!res) { CCS811_type = 1; - snprintf_P(log_data, sizeof(log_data), S_LOG_I2C_FOUND_AT, "CCS811", 0x5A); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, S_LOG_I2C_FOUND_AT, "CCS811", 0x5A); } else { - //snprintf_P(log_data, sizeof(log_data), "CCS811 init failed: %d",res); - //AddLog(LOG_LEVEL_DEBUG); + //AddLog_P2(LOG_LEVEL_DEBUG, "CCS811 init failed: %d",res); } } else { if (ccs.available()) { @@ -83,7 +81,7 @@ const char HTTP_SNS_CCS811[] PROGMEM = "%s" "{s}CCS811 " D_ECO2 "{m}%d " D_UNIT_PARTS_PER_MILLION "{e}" // {s} = , {m} = , {e} = "{s}CCS811 " D_TVOC "{m}%d " D_UNIT_PARTS_PER_BILLION "{e}"; -void CCS811Show(boolean json) +void CCS811Show(bool json) { if (CCS811_ready) { if (json) { @@ -103,9 +101,9 @@ void CCS811Show(boolean json) * Interface \*********************************************************************************************/ -boolean Xsns31(byte function) +bool Xsns31(uint8_t function) { - boolean result = false; + bool result = false; if (i2c_flg) { switch (function) { diff --git a/sonoff/xsns_32_mpu6050.ino b/sonoff/xsns_32_mpu6050.ino index 8fbdfc6f3..61156f9c1 100644 --- a/sonoff/xsns_32_mpu6050.ino +++ b/sonoff/xsns_32_mpu6050.ino @@ -1,7 +1,7 @@ /* xsns_32_mpu6050.ino - MPU6050 gyroscope and temperature sensor support for Sonoff-Tasmota - Copyright (C) 2018 Oliver Welter + Copyright (C) 2019 Oliver Welter This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -120,7 +120,7 @@ void MPU_6050Detect(void) return; } - for (byte i = 0; i < sizeof(MPU_6050_addresses); i++) + for (uint8_t i = 0; i < sizeof(MPU_6050_addresses); i++) { if(!I2cDevice(MPU_6050_addresses[i])) { @@ -134,12 +134,12 @@ void MPU_6050Detect(void) mpu6050.setXGyroOffset(220); mpu6050.setYGyroOffset(76); mpu6050.setZGyroOffset(-85); - mpu6050.setZAccelOffset(1788); + mpu6050.setZAccelOffset(1788); if (MPU6050_dmp.devStatus == 0) { mpu6050.setDMPEnabled(true); MPU6050_dmp.packetSize = mpu6050.dmpGetFIFOPacketSize(); MPU_6050_found = true; - } + } #else mpu6050.initialize(); MPU_6050_found = mpu6050.testConnection(); @@ -150,8 +150,7 @@ void MPU_6050Detect(void) if (MPU_6050_found) { - snprintf_P(log_data, sizeof(log_data), S_LOG_I2C_FOUND_AT, D_SENSOR_MPU6050, MPU_6050_address); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, S_LOG_I2C_FOUND_AT, D_SENSOR_MPU6050, MPU_6050_address); } } @@ -171,7 +170,7 @@ const char HTTP_SNS_GZ_AXIS[] PROGMEM = "%s{s}%s " D_GZ_AXIS "{m}%s{e}"; #define D_JSON_AXIS_GY "GyroYAxis" #define D_JSON_AXIS_GZ "GyroZAxis" -void MPU_6050Show(boolean json) +void MPU_6050Show(bool json) { if (MPU_6050_found) { MPU_6050PerformReading(); @@ -228,9 +227,9 @@ void MPU_6050Show(boolean json) * Interface \*********************************************************************************************/ -boolean Xsns32(byte function) +bool Xsns32(uint8_t function) { - boolean result = false; + bool result = false; if (i2c_flg) { switch (function) { diff --git a/sonoff/xsns_33_ds3231.ino b/sonoff/xsns_33_ds3231.ino index 48cb12553..98afa0ab0 100644 --- a/sonoff/xsns_33_ds3231.ino +++ b/sonoff/xsns_33_ds3231.ino @@ -1,7 +1,7 @@ /* xsns_33_ds3231.ino - ds3231 RTC chip, act like sensor support for Sonoff-Tasmota - Copyright (C) 2018 Guy Elgabsi (guy.elg AT gmail.com) + Copyright (C) 2019 Guy Elgabsi (guy.elg AT gmail.com) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -62,26 +62,21 @@ #define HR1224 6 //Hours register 12 or 24 hour mode (24 hour mode==0) #define CENTURY 7 //Century bit in Month register #define DYDT 6 //Day/Date flag bit in alarm Day/Date registers -boolean ds3231ReadStatus = false , ds3231WriteStatus = false; //flag, we want to wriet/write to DS3231 onlu once -boolean DS3231chipDetected; - +bool ds3231ReadStatus = false; +bool ds3231WriteStatus = false; //flag, we want to wriet/write to DS3231 onlu once +bool DS3231chipDetected = false; /*----------------------------------------------------------------------* Detect the DS3231 Chip ----------------------------------------------------------------------*/ -boolean DS3231Detect(void) +void DS3231Detect(void) { - if (I2cValidRead(USE_RTC_ADDR, RTC_STATUS, 1)) - { - snprintf_P(log_data, sizeof(log_data), S_LOG_I2C_FOUND_AT, "DS3231", USE_RTC_ADDR); - AddLog(LOG_LEVEL_INFO); - return true; - } - else - { - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_I2C "%s *NOT* " D_FOUND_AT " 0x%x"), "DS3231", USE_RTC_ADDR); - AddLog(LOG_LEVEL_INFO); - return false; + DS3231chipDetected = false; + if (I2cValidRead(USE_RTC_ADDR, RTC_STATUS, 1)) { + AddLog_P2(LOG_LEVEL_INFO, S_LOG_I2C_FOUND_AT, "DS3231", USE_RTC_ADDR); + DS3231chipDetected = true; + } else { + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_I2C "DS3231 NOT " D_FOUND_AT " 0x%x"), USE_RTC_ADDR); } } @@ -136,21 +131,19 @@ void SetDS3231Time (uint32_t epoch_time) { Interface \*********************************************************************************************/ -boolean Xsns33(byte function) +bool Xsns33(uint8_t function) { - boolean result = false; + bool result = false; if (i2c_flg) { switch (function) { case FUNC_INIT: - DS3231chipDetected = DS3231Detect(); - result = DS3231chipDetected; + DS3231Detect(); break; - case FUNC_EVERY_SECOND: TIME_T tmpTime; if (!ds3231ReadStatus && DS3231chipDetected && utc_time < 1451602800 ) { // We still did not sync with NTP (time not valid) , so, read time from DS3231 - ntp_force_sync = 1; //force to sync with ntp + ntp_force_sync = true; //force to sync with ntp utc_time = ReadFromDS3231(); //we read UTC TIME from DS3231 // from this line, we just copy the function from "void RtcSecond()" at the support.ino ,line 2143 and above // We need it to set rules etc. @@ -161,26 +154,20 @@ boolean Xsns33(byte function) RtcTime.year = tmpTime.year + 1970; daylight_saving_time = RuleToTime(Settings.tflag[1], RtcTime.year); standard_time = RuleToTime(Settings.tflag[0], RtcTime.year); - snprintf_P(log_data, sizeof(log_data), PSTR("Set time from DS3231 to RTC (" D_UTC_TIME ") %s, (" D_DST_TIME ") %s, (" D_STD_TIME ") %s"), + AddLog_P2(LOG_LEVEL_INFO, PSTR("Set time from DS3231 to RTC (" D_UTC_TIME ") %s, (" D_DST_TIME ") %s, (" D_STD_TIME ") %s"), GetTime(0).c_str(), GetTime(2).c_str(), GetTime(3).c_str()); - AddLog(LOG_LEVEL_INFO); if (local_time < 1451602800) { // 2016-01-01 rules_flag.time_init = 1; } else { rules_flag.time_set = 1; } - result = true; } else if (!ds3231WriteStatus && DS3231chipDetected && utc_time > 1451602800 && abs(utc_time - ReadFromDS3231()) > 60) {//if time is valid and is drift from RTC in more that 60 second - snprintf_P(log_data, sizeof(log_data), PSTR("Write Time TO DS3231 from NTP (" D_UTC_TIME ") %s, (" D_DST_TIME ") %s, (" D_STD_TIME ") %s"), + AddLog_P2(LOG_LEVEL_INFO, PSTR("Write Time TO DS3231 from NTP (" D_UTC_TIME ") %s, (" D_DST_TIME ") %s, (" D_STD_TIME ") %s"), GetTime(0).c_str(), GetTime(2).c_str(), GetTime(3).c_str()); - AddLog(LOG_LEVEL_INFO); SetDS3231Time (utc_time); //update the DS3231 time ds3231WriteStatus = true; } - else { - result = false; - } break; } } diff --git a/sonoff/xsns_34_hx711.ino b/sonoff/xsns_34_hx711.ino index c3f10ded9..2072dc496 100644 --- a/sonoff/xsns_34_hx711.ino +++ b/sonoff/xsns_34_hx711.ino @@ -1,7 +1,7 @@ /* xsns_34_hx711.ino - HX711 load cell support for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends + Copyright (C) 2019 Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -67,12 +67,12 @@ long hx_offset = 0; long hx_scale = 1; uint8_t hx_type = 1; uint8_t hx_sample_count = 0; -uint8_t hx_tare_flg = 0; uint8_t hx_calibrate_step = HX_CAL_END; uint8_t hx_calibrate_timer = 0; uint8_t hx_calibrate_msg = 0; uint8_t hx_pin_sck; uint8_t hx_pin_dout; +bool hx_tare_flg = false; /*********************************************************************************************/ @@ -118,7 +118,7 @@ long HxRead() void HxReset(void) { - hx_tare_flg = 1; + hx_tare_flg = true; hx_sum_weight = 0; hx_sample_count = 0; } @@ -155,7 +155,7 @@ bool HxCommand(void) bool show_parms = false; char sub_string[XdrvMailbox.data_len +1]; - for (byte ca = 0; ca < XdrvMailbox.data_len; ca++) { + for (uint8_t ca = 0; ca < XdrvMailbox.data_len; ca++) { if ((' ' == XdrvMailbox.data[ca]) || ('=' == XdrvMailbox.data[ca])) { XdrvMailbox.data[ca] = ','; } } @@ -257,7 +257,7 @@ void HxEvery100mSecond(void) if (hx_weight < 0) { hx_weight = 0; } if (hx_tare_flg) { - hx_tare_flg = 0; + hx_tare_flg = false; hx_offset = average; // grams } @@ -270,7 +270,7 @@ void HxEvery100mSecond(void) } else if (HX_CAL_RESET == hx_calibrate_step) { // Wait for stable reset if (hx_calibrate_timer) { - if (hx_weight < Settings.weight_reference) { + if (hx_weight < (long)Settings.weight_reference) { hx_calibrate_step--; hx_calibrate_timer = HX_CAL_TIMEOUT * (10 / HX_SAMPLES); HxCalibrationStateTextJson(2); @@ -281,7 +281,7 @@ void HxEvery100mSecond(void) } else if (HX_CAL_FIRST == hx_calibrate_step) { // Wait for first reference weight if (hx_calibrate_timer) { - if (hx_weight > Settings.weight_reference) { + if (hx_weight > (long)Settings.weight_reference) { hx_calibrate_step--; } } else { @@ -289,7 +289,7 @@ void HxEvery100mSecond(void) } } else if (HX_CAL_DONE == hx_calibrate_step) { // Second stable reference weight - if (hx_weight > Settings.weight_reference) { + if (hx_weight > (long)Settings.weight_reference) { hx_calibrate_step = HX_CAL_FINISH; // Calibration done Settings.weight_calibration = hx_weight / Settings.weight_reference; hx_weight = 0; // Reset calibration value @@ -301,7 +301,7 @@ void HxEvery100mSecond(void) if (HX_CAL_FAIL == hx_calibrate_step) { // Calibration failed hx_calibrate_step--; - hx_tare_flg = 1; // Perform a reset using old scale + hx_tare_flg = true; // Perform a reset using old scale HxCalibrationStateTextJson(0); } if (HX_CAL_FINISH == hx_calibrate_step) { // Calibration finished @@ -329,7 +329,7 @@ const char HTTP_HX711_CAL[] PROGMEM = "%s" "{s}HX711 %s{m}{e}"; #endif // USE_WEBSERVER -void HxShow(boolean json) +void HxShow(bool json) { char scount[30] = { 0 }; @@ -377,24 +377,24 @@ const char HTTP_BTN_MENU_MAIN_HX711[] PROGMEM = "
"; const char HTTP_BTN_MENU_HX711[] PROGMEM = - "
"; + "

"; const char HTTP_FORM_HX711[] PROGMEM = "
 " D_CALIBRATION " " "
" - "
" D_REFERENCE_WEIGHT " (" D_UNIT_KILOGRAM ")

" - "

" + "

" D_REFERENCE_WEIGHT " (" D_UNIT_KILOGRAM ")

" + "
" "
" "


" "
 " D_HX711_PARAMETERS " " "
" - "
" D_ITEM_WEIGHT " (" D_UNIT_KILOGRAM ")

"; + "

" D_ITEM_WEIGHT " (" D_UNIT_KILOGRAM ")

"; void HandleHxAction(void) { - if (HttpUser()) { return; } - if (!WebAuthenticate()) { return WebServer->requestAuthentication(); } + if (!HttpCheckPriviledgedAccess()) { return; } + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_HX711); if (WebServer->hasArg("save")) { @@ -403,41 +403,38 @@ void HandleHxAction(void) return; } - char tmp[100]; + char stemp1[20]; if (WebServer->hasArg("reset")) { - snprintf_P(tmp, sizeof(tmp), PSTR("Sensor34 1")); // Reset - ExecuteWebCommand(tmp, SRC_WEBGUI); + snprintf_P(stemp1, sizeof(stemp1), PSTR("Sensor34 1")); // Reset + ExecuteWebCommand(stemp1, SRC_WEBGUI); HandleRoot(); // Return to main screen return; } if (WebServer->hasArg("calibrate")) { - WebGetArg("p1", tmp, sizeof(tmp)); - Settings.weight_reference = (!strlen(tmp)) ? 0 : (unsigned long)(CharToDouble(tmp) * 1000); + WebGetArg("p1", stemp1, sizeof(stemp1)); + Settings.weight_reference = (!strlen(stemp1)) ? 0 : (unsigned long)(CharToDouble(stemp1) * 1000); HxLogUpdates(); - snprintf_P(tmp, sizeof(tmp), PSTR("Sensor34 2")); // Start calibration - ExecuteWebCommand(tmp, SRC_WEBGUI); + snprintf_P(stemp1, sizeof(stemp1), PSTR("Sensor34 2")); // Start calibration + ExecuteWebCommand(stemp1, SRC_WEBGUI); HandleRoot(); // Return to main screen return; } - String page = FPSTR(HTTP_HEAD); - page.replace(F("{v}"), FPSTR(D_CONFIGURE_HX711)); - page += FPSTR(HTTP_HEAD_STYLE); - page += FPSTR(HTTP_FORM_HX711); - dtostrfd((float)Settings.weight_reference / 1000, 3, tmp); - page.replace("{1", String(tmp)); - dtostrfd((float)Settings.weight_item / 10000, 4, tmp); - page.replace("{2", String(tmp)); - - page += FPSTR(HTTP_FORM_END); - page += FPSTR(HTTP_BTN_CONF); - ShowPage(page); + WSContentStart_P(S_CONFIGURE_HX711); + WSContentSendStyle(); + dtostrfd((float)Settings.weight_reference / 1000, 3, stemp1); + char stemp2[20]; + dtostrfd((float)Settings.weight_item / 10000, 4, stemp2); + WSContentSend_P(HTTP_FORM_HX711, stemp1, stemp2); + WSContentSend_P(HTTP_FORM_END); + WSContentSpaceButton(BUTTON_CONFIGURATION); + WSContentEnd(); } void HxSaveSettings(void) @@ -457,9 +454,7 @@ void HxLogUpdates(void) char weigth_item_chr[33]; dtostrfd((float)Settings.weight_item / 10000, 4, weigth_item_chr); - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_WIFI D_JSON_WEIGHT_REF " %s, " D_JSON_WEIGHT_ITEM " %s"), - weigth_ref_chr, weigth_item_chr); - AddLog(LOG_LEVEL_INFO); + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_JSON_WEIGHT_REF " %s, " D_JSON_WEIGHT_ITEM " %s"), weigth_ref_chr, weigth_item_chr); } #endif // USE_HX711_GUI @@ -469,9 +464,9 @@ void HxLogUpdates(void) * Interface \*********************************************************************************************/ -boolean Xsns34(byte function) +bool Xsns34(uint8_t function) { - boolean result = false; + bool result = false; if (hx_type) { switch (function) { @@ -481,7 +476,7 @@ boolean Xsns34(byte function) case FUNC_EVERY_100_MSECOND: HxEvery100mSecond(); break; - case FUNC_COMMAND: + case FUNC_COMMAND_SENSOR: if (XSNS_34 == XdrvMailbox.index) { result = HxCommand(); } @@ -495,10 +490,10 @@ boolean Xsns34(byte function) break; #ifdef USE_HX711_GUI case FUNC_WEB_ADD_MAIN_BUTTON: - strncat_P(mqtt_data, HTTP_BTN_MENU_MAIN_HX711, sizeof(mqtt_data) - strlen(mqtt_data) -1); + WSContentSend_P(HTTP_BTN_MENU_MAIN_HX711); break; case FUNC_WEB_ADD_BUTTON: - strncat_P(mqtt_data, HTTP_BTN_MENU_HX711, sizeof(mqtt_data) - strlen(mqtt_data) -1); + WSContentSend_P(HTTP_BTN_MENU_HX711); break; case FUNC_WEB_ADD_HANDLER: WebServer->on("/" WEB_HANDLE_HX711, HandleHxAction); diff --git a/sonoff/xsns_35_tx20.ino b/sonoff/xsns_35_tx20.ino index a198c766f..bae2e9532 100644 --- a/sonoff/xsns_35_tx20.ino +++ b/sonoff/xsns_35_tx20.ino @@ -1,7 +1,7 @@ /* xsns_35_Tx20.ino - La Crosse Tx20 wind sensor support for Sonoff-Tasmota - Copyright (C) 2018 Thomas Eckerstorfer and Theo Arends + Copyright (C) 2019 Thomas Eckerstorfer and Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -78,7 +78,7 @@ float tx20_wind_sum = 0; int tx20_count = 0; uint8_t tx20_wind_direction = 0; -boolean tx20_available = false; +bool tx20_available = false; void Tx20StartRead(void) { @@ -170,7 +170,7 @@ void Tx20Init(void) { attachInterrupt(pin[GPIO_TX20_TXD_BLACK], Tx20StartRead, RISING); } -void Tx20Show(boolean json) +void Tx20Show(bool json) { char wind_speed_string[33]; dtostrfd(tx20_wind_speed_kmh, 2, wind_speed_string); @@ -195,9 +195,9 @@ void Tx20Show(boolean json) * Interface \*********************************************************************************************/ -boolean Xsns35(byte function) +bool Xsns35(uint8_t function) { - boolean result = false; + bool result = false; if (pin[GPIO_TX20_TXD_BLACK] < 99) { switch (function) { diff --git a/sonoff/xsns_36_mgc3130.ino b/sonoff/xsns_36_mgc3130.ino index 81f4a72c6..5c1d2df48 100644 --- a/sonoff/xsns_36_mgc3130.ino +++ b/sonoff/xsns_36_mgc3130.ino @@ -1,7 +1,7 @@ /* xsns_36_MGC3130.ino - Support for I2C MGC3130 Electric Field Sensor for Sonoff-Tasmota - Copyright (C) 2018 Christian Baars & Theo Arends + Copyright (C) 2019 Christian Baars & Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -497,17 +497,15 @@ bool MGC3130_detect(void) digitalWrite(MGC3130_reset, HIGH); delay(50); - boolean success = false; + bool success = false; success = MGC3130_receiveMessage(); // This should read the firmware info if (success) { strcpy_P(MGC3130stype, PSTR("MGC3130")); - snprintf_P(log_data, sizeof(log_data), S_LOG_I2C_FOUND_AT, MGC3130stype, MGC3130_I2C_ADDR); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, S_LOG_I2C_FOUND_AT, MGC3130stype, MGC3130_I2C_ADDR); MGC3130_currentGesture[0] = '\0'; MGC3130_type = true; } else { - snprintf_P(log_data, sizeof(log_data), PSTR("MGC3130 did not respond at address 0x%x"), MGC3130_I2C_ADDR); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("MGC3130 did not respond at address 0x%x"), MGC3130_I2C_ADDR); } return success; } @@ -516,7 +514,7 @@ bool MGC3130_detect(void) * Presentation \*********************************************************************************************/ -void MGC3130_show(boolean json) +void MGC3130_show(bool json) { if (!MGC3130_type) { return; } @@ -576,7 +574,7 @@ void MGC3130_show(boolean json) bool MGC3130CommandSensor() { - boolean serviced = true; + bool serviced = true; switch (XdrvMailbox.payload) { case 0: // cycle through the modes @@ -602,9 +600,9 @@ bool MGC3130CommandSensor() * Interface \*********************************************************************************************/ -boolean Xsns36(byte function) +bool Xsns36(uint8_t function) { - boolean result = false; + bool result = false; if (i2c_flg) { if ((FUNC_INIT == function) && (pin[GPIO_MGC3130_XFER] < 99) && (pin[GPIO_MGC3130_RESET] < 99)) { @@ -615,7 +613,7 @@ boolean Xsns36(byte function) case FUNC_EVERY_50_MSECOND: MGC3130_loop(); break; - case FUNC_COMMAND: + case FUNC_COMMAND_SENSOR: if (XSNS_36 == XdrvMailbox.index) { result = MGC3130CommandSensor(); } diff --git a/sonoff/xsns_37_rfsensor.ino b/sonoff/xsns_37_rfsensor.ino index efb864924..efb6ded06 100644 --- a/sonoff/xsns_37_rfsensor.ino +++ b/sonoff/xsns_37_rfsensor.ino @@ -1,7 +1,7 @@ /* xsns_37_rfsensor.ino - RF sensor receiver for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends + Copyright (C) 2019 Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -49,10 +49,10 @@ typedef struct RawSignalStruct // Variabelen geplaatst in struct zodat deze later eenvoudig kunnen worden weggeschreven naar SDCard { int Number; // aantal bits, maal twee omdat iedere bit een mark en een space heeft. - byte Repeats; // Aantal maal dat de pulsreeks verzonden moet worden bij een zendactie. - byte Multiply; // Pulses[] * Multiply is de echte tijd van een puls in microseconden + uint8_t Repeats; // Aantal maal dat de pulsreeks verzonden moet worden bij een zendactie. + uint8_t Multiply; // Pulses[] * Multiply is de echte tijd van een puls in microseconden unsigned long Time; // Tijdstempel wanneer signaal is binnengekomen (millis()) - byte Pulses[RFSNS_RAW_BUFFER_SIZE+2]; // Tabel met de gemeten pulsen in microseconden gedeeld door rfsns_raw_signal->Multiply. Dit scheelt helft aan RAM geheugen. + uint8_t Pulses[RFSNS_RAW_BUFFER_SIZE+2]; // Tabel met de gemeten pulsen in microseconden gedeeld door rfsns_raw_signal->Multiply. Dit scheelt helft aan RAM geheugen. // Om legacy redenen zit de eerste puls in element 1. Element 0 wordt dus niet gebruikt. } raw_signal_t; @@ -65,7 +65,7 @@ uint8_t rfsns_any_sensor = 0; * Fetch signals from RF pin \*********************************************************************************************/ -bool RfSnsFetchSignal(byte DataPin, bool StateSignal) +bool RfSnsFetchSignal(uint8_t DataPin, bool StateSignal) { uint8_t Fbit = digitalPinToBitMask(DataPin); uint8_t Fport = digitalPinToPort(DataPin); @@ -179,17 +179,17 @@ void RfSnsAnalyzeTheov2(void) { if (rfsns_raw_signal->Number != RFSNS_THEOV2_PULSECOUNT) { return; } - byte Checksum; // 8 bits Checksum over following bytes - byte Channel; // 3 bits channel - byte Type; // 5 bits type - byte Voltage; // 8 bits Vcc like 45 = 4.5V, bit 8 is batt low + uint8_t Checksum; // 8 bits Checksum over following bytes + uint8_t Channel; // 3 bits channel + uint8_t Type; // 5 bits type + uint8_t Voltage; // 8 bits Vcc like 45 = 4.5V, bit 8 is batt low int Payload1; // 16 bits int Payload2; // 16 bits - byte b, bytes, bits, id; + uint8_t b, bytes, bits, id; - byte idx = 3; - byte chksum = 0; + uint8_t idx = 3; + uint8_t chksum = 0; for (bytes = 0; bytes < 7; bytes++) { b = 0; for (bits = 0; bits <= 7; bits++) @@ -251,9 +251,8 @@ void RfSnsAnalyzeTheov2(void) break; } - snprintf_P(log_data, sizeof(log_data), PSTR("RFS: TheoV2, ChkCalc %d, Chksum %d, id %d, Type %d, Ch %d, Volt %d, BattLo %d, Pld1 %d, Pld2 %d"), + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RFS: TheoV2, ChkCalc %d, Chksum %d, id %d, Type %d, Ch %d, Volt %d, BattLo %d, Pld1 %d, Pld2 %d"), chksum, Checksum, id, Type, Channel +1, Payload3, (Voltage & 0x80) >> 7, Payload1, Payload2); - AddLog(LOG_LEVEL_DEBUG); } void RfSnsTheoV2Show(bool json) @@ -438,23 +437,23 @@ void RfSnsAnalyzeAlectov2() if (!(((rfsns_raw_signal->Number >= RFSNS_ACH2010_MIN_PULSECOUNT) && (rfsns_raw_signal->Number <= RFSNS_ACH2010_MAX_PULSECOUNT)) || (rfsns_raw_signal->Number == RFSNS_DKW2012_PULSECOUNT))) { return; } - byte c = 0; - byte rfbit; - byte data[9] = { 0 }; - byte msgtype = 0; - byte rc = 0; + uint8_t c = 0; + uint8_t rfbit; + uint8_t data[9] = { 0 }; + uint8_t msgtype = 0; + uint8_t rc = 0; int temp; - byte checksum = 0; - byte checksumcalc = 0; - byte maxidx = 8; + uint8_t checksum = 0; + uint8_t checksumcalc = 0; + uint8_t maxidx = 8; unsigned long atime; float factor; char buf1[16]; if (rfsns_raw_signal->Number > RFSNS_ACH2010_MAX_PULSECOUNT) { maxidx = 9; } // Get message back to front as the header is almost never received complete for ACH2010 - byte idx = maxidx; - for (byte x = rfsns_raw_signal->Number; x > 0; x = x-2) { + uint8_t idx = maxidx; + for (uint8_t x = rfsns_raw_signal->Number; x > 0; x = x-2) { if (rfsns_raw_signal->Pulses[x-1] * rfsns_raw_signal->Multiply < 0x300) { rfbit = 0x80; } else { @@ -507,9 +506,8 @@ void RfSnsAnalyzeAlectov2() rfsns_alecto_v2->wdir = data[8] & 0xf; } - snprintf_P(log_data, sizeof(log_data), PSTR("RFS: " D_ALECTOV2 ", ChkCalc %d, Chksum %d, rc %d, Temp %d, Hum %d, Rain %d, Wind %d, Gust %d, Dir %d, Factor %s"), + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RFS: " D_ALECTOV2 ", ChkCalc %d, Chksum %d, rc %d, Temp %d, Hum %d, Rain %d, Wind %d, Gust %d, Dir %d, Factor %s"), checksumcalc, checksum, rc, ((data[1] & 0x3) * 256 + data[2]) - 400, data[3], (data[6] * 256) + data[7], data[4], data[5], data[8] & 0xf, dtostrfd(factor, 3, buf1)); - AddLog(LOG_LEVEL_DEBUG); } void RfSnsAlectoResetRain(void) @@ -627,8 +625,7 @@ void RfSnsInit(void) void RfSnsAnalyzeRawSignal(void) { - snprintf_P(log_data, sizeof(log_data), PSTR("RFS: Pulses %d"), (int)rfsns_raw_signal->Number); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RFS: Pulses %d"), (int)rfsns_raw_signal->Number); #ifdef USE_THEO_V2 RfSnsAnalyzeTheov2(); @@ -659,7 +656,7 @@ void RfSnsShow(bool json) * Interface \*********************************************************************************************/ -boolean Xsns37(byte function) +bool Xsns37(uint8_t function) { bool result = false; diff --git a/sonoff/xsns_38_az7798.ino b/sonoff/xsns_38_az7798.ino index f909bb061..3ec73085e 100644 --- a/sonoff/xsns_38_az7798.ino +++ b/sonoff/xsns_38_az7798.ino @@ -1,7 +1,7 @@ /* xsns_38_az7798.ino - AZ_Instrument 7798 CO2/temperature/humidity meter support for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends + Copyright (C) 2019 Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -107,9 +107,7 @@ * * : responds with : T19.9C:C2167ppm:H57.4% * This one gives the current readings. - ********************************************************************************************** - -/*********************************************************************************************/ +\*********************************************************************************************/ #include @@ -160,7 +158,7 @@ void AzEverySecond(void) } } while(((millis() - start) < AZ_READ_TIMEOUT) && (counter < sizeof(az_response)) && !az_received); - AddLogSerial(LOG_LEVEL_DEBUG_MORE, az_response, counter); + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, az_response, counter); if (!az_received) { AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 comms timeout")); @@ -252,7 +250,7 @@ void AzInit(void) } } -void AzShow(boolean json) +void AzShow(bool json) { char temperature[33]; dtostrfd(az_temperature, Settings.flag2.temperature_resolution, temperature); @@ -277,9 +275,9 @@ void AzShow(boolean json) * Interface \*********************************************************************************************/ -boolean Xsns38(byte function) +bool Xsns38(uint8_t function) { - boolean result = false; + bool result = false; if(az_type){ switch (function) { diff --git a/sonoff/xsns_39_max31855.ino b/sonoff/xsns_39_max31855.ino new file mode 100644 index 000000000..10630e171 --- /dev/null +++ b/sonoff/xsns_39_max31855.ino @@ -0,0 +1,176 @@ +/* + xsns_39_max31855.ino - MAX31855 thermocouple sensor support for Sonoff-Tasmota + + Copyright (C) 2019 Markus Past + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef USE_MAX31855 + +#define XSNS_39 39 + +bool initialized = false; + +struct MAX31855_ResultStruct{ + uint8_t ErrorCode; // Error Codes: 0 = No Error / 1 = TC open circuit / 2 = TC short to GND / 4 = TC short to VCC + float ProbeTemperature; // Measured temperature of the 'hot' TC junction (probe temp) + float ReferenceTemperature; // Measured temperature of the 'cold' TC junction (reference temp) +} MAX31855_Result; + +void MAX31855_Init(void){ + if(initialized) + return; + + // Set GPIO modes for SW-SPI + pinMode(pin[GPIO_MAX31855CS], OUTPUT); + pinMode(pin[GPIO_MAX31855CLK], OUTPUT); + pinMode(pin[GPIO_MAX31855DO], INPUT); + + // Chip not selected / Clock low + digitalWrite(pin[GPIO_MAX31855CS], HIGH); + digitalWrite(pin[GPIO_MAX31855CLK], LOW); + + initialized = true; +} + +/* +* MAX31855_GetResult(void) +* Acquires the raw data via SPI, checks for MAX31855 errors and fills result structure +*/ +void MAX31855_GetResult(void){ + int32_t RawData = MAX31855_ShiftIn(32); + uint8_t probeerror = RawData & 0x7; + + MAX31855_Result.ErrorCode = probeerror; + MAX31855_Result.ReferenceTemperature = MAX31855_GetReferenceTemperature(RawData); + if(probeerror) + MAX31855_Result.ProbeTemperature = NAN; // Return NaN if MAX31855 reports an error + else + MAX31855_Result.ProbeTemperature = MAX31855_GetProbeTemperature(RawData); +} + + +/* +* MAX31855_GetProbeTemperature(int32_t RawData) +* Decodes and returns the temperature of TCs 'hot' junction from RawData +*/ +float MAX31855_GetProbeTemperature(int32_t RawData){ + if(RawData & 0x80000000) + RawData = (RawData >> 18) | 0xFFFFC000; // Negative value - Drop lower 18 bits and extend to negative number + else + RawData >>= 18; // Positiv value - Drop lower 18 bits + + float result = (RawData * 0.25); // MAX31855 LSB resolution is 0.25°C for probe temperature + + return (Settings.flag.temperature_conversion) ? ConvertTemp(result) : result; // Check if we have to convert to Fahrenheit +} + +/* +* MAX31855_GetReferenceTemperature(int32_t RawData) +* Decodes and returns the temperature of TCs 'cold' junction from RawData +*/ +float MAX31855_GetReferenceTemperature(int32_t RawData){ + if(RawData & 0x8000) + RawData = (RawData >> 4) | 0xFFFFF000; // Negative value - Drop lower 4 bits and extend to negative number + else + RawData = (RawData >> 4) & 0x00000FFF; // Positiv value - Drop lower 4 bits and mask out remaining bits (probe temp, error bit, etc.) + + float result = (RawData * 0.0625); // MAX31855 LSB resolution is 0.0625°C for reference temperature + + return (Settings.flag.temperature_conversion) ? ConvertTemp(result) : result; // Check if we have to convert to Fahrenheit +} + +/* +* MAX31855_ShiftIn(uint8_t Length) +* Communicates with MAX31855 via SW-SPI and returns the raw data read from the chip +*/ +int32_t MAX31855_ShiftIn(uint8_t Length){ + int32_t dataIn = 0; + + digitalWrite(pin[GPIO_MAX31855CS], LOW); // CS = LOW -> Start SPI communication + delayMicroseconds(1); // CS fall to output enable = max. 100ns + + for(uint8_t i = 0; i < Length; i++) + { + digitalWrite(pin[GPIO_MAX31855CLK], LOW); + delayMicroseconds(1); // CLK pulse width low = min. 100ns / CLK fall to output valid = max. 40ns + dataIn <<= 1; + if(digitalRead(pin[GPIO_MAX31855DO])) + dataIn |= 1; + digitalWrite(pin[GPIO_MAX31855CLK], HIGH); + delayMicroseconds(1); // CLK pulse width high = min. 100ns + } + + digitalWrite(pin[GPIO_MAX31855CS], HIGH); // CS = HIGH -> End SPI communication + digitalWrite(pin[GPIO_MAX31855CLK], LOW); + return dataIn; +} + +void MAX31855_Show(bool Json){ + char probetemp[33]; + char referencetemp[33]; + dtostrfd(MAX31855_Result.ProbeTemperature, Settings.flag2.temperature_resolution, probetemp); + dtostrfd(MAX31855_Result.ReferenceTemperature, Settings.flag2.temperature_resolution, referencetemp); + + if(Json){ + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"%s\":{\"" D_JSON_PROBETEMPERATURE "\":%s,\"" D_JSON_REFERENCETEMPERATURE "\":%s,\"" D_JSON_ERROR "\":%d}"), \ + mqtt_data, "MAX31855", probetemp, referencetemp, MAX31855_Result.ErrorCode); +#ifdef USE_DOMOTICZ + if (0 == tele_period) { + DomoticzSensor(DZ_TEMP, probetemp); + } +#endif // USE_DOMOTICZ +#ifdef USE_KNX + if (0 == tele_period) { + KnxSensor(KNX_TEMPERATURE, MAX31855_Result.ProbeTemperature); + } +#endif // USE_KNX + } else { +#ifdef USE_WEBSERVER + snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_TEMP, mqtt_data, "MAX31855", probetemp, TempUnit()); +#endif // USE_WEBSERVER + } +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xsns39(uint8_t function) +{ + bool result = false; + if((pin[GPIO_MAX31855CS] < 99) && (pin[GPIO_MAX31855CLK] < 99) && (pin[GPIO_MAX31855DO] < 99)){ + + switch (function) { + case FUNC_INIT: + MAX31855_Init(); + break; + case FUNC_EVERY_SECOND: + MAX31855_GetResult(); + break; + case FUNC_JSON_APPEND: + MAX31855_Show(true); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_APPEND: + MAX31855_Show(false); + break; +#endif // USE_WEBSERVER + } + } + return result; +} + +#endif // USE_MAX31855 \ No newline at end of file diff --git a/sonoff/xsns_40_pn532.ino b/sonoff/xsns_40_pn532.ino new file mode 100644 index 000000000..0bfa8bda1 --- /dev/null +++ b/sonoff/xsns_40_pn532.ino @@ -0,0 +1,606 @@ +/* + xsns_40_pn532.ino - Support for PN532 (HSU) NFC Tag Reader + + Copyright (C) 2019 Andre Thomas and Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef USE_PN532_HSU + +#define XSNS_40 40 + +#include + +TasmotaSerial *PN532_Serial; + +#define PN532_INVALID_ACK -1 +#define PN532_TIMEOUT -2 +#define PN532_INVALID_FRAME -3 +#define PN532_NO_SPACE -4 + +#define PN532_PREAMBLE 0x00 +#define PN532_STARTCODE1 0x00 +#define PN532_STARTCODE2 0xFF +#define PN532_POSTAMBLE 0x00 + +#define PN532_HOSTTOPN532 0xD4 +#define PN532_PN532TOHOST 0xD5 + +#define PN532_ACK_WAIT_TIME 0x0A + +#define PN532_COMMAND_GETFIRMWAREVERSION 0x02 +#define PN532_COMMAND_SAMCONFIGURATION 0x14 +#define PN532_COMMAND_RFCONFIGURATION 0x32 +#define PN532_COMMAND_INDATAEXCHANGE 0x40 +#define PN532_COMMAND_INLISTPASSIVETARGET 0x4A + +#define PN532_MIFARE_ISO14443A 0x00 +#define MIFARE_CMD_READ 0x30 +#define MIFARE_CMD_AUTH_A 0x60 +#define MIFARE_CMD_AUTH_B 0x61 +#define MIFARE_CMD_WRITE 0xA0 + +uint8_t pn532_model = 0; // Used to maintain detection flag +uint8_t pn532_command = 0; // Used to carry command code between functions +uint8_t pn532_scantimer = 0; // Used to prevent multiple successful reads within 2 second window + +uint8_t pn532_packetbuffer[64]; // Global buffer used to store packet + +#ifdef USE_PN532_DATA_FUNCTION +uint8_t pn532_function = 0; +uint8_t pn532_newdata[16]; +uint8_t pn532_newdata_len = 0; +#endif // USE_PN532_DATA_FUNCTION + +void PN532_Init(void) +{ + if ((pin[GPIO_PN532_RXD] < 99) && (pin[GPIO_PN532_TXD] < 99)) { + PN532_Serial = new TasmotaSerial(pin[GPIO_PN532_RXD], pin[GPIO_PN532_TXD], 1); + if (PN532_Serial->begin(115200)) { + if (PN532_Serial->hardwareSerial()) { ClaimSerial(); } + PN532_wakeup(); + uint32_t ver = PN532_getFirmwareVersion(); + if (ver) { + PN532_setPassiveActivationRetries(0xFF); + PN532_SAMConfig(); + pn532_model = 1; + AddLog_P2(LOG_LEVEL_INFO,"NFC: PN532 NFC Reader detected (V%u.%u)",(ver>>16) & 0xFF, (ver>>8) & 0xFF); + } + } + } +} + +int8_t PN532_receive(uint8_t *buf, int len, uint16_t timeout) +{ + int read_bytes = 0; + int ret; + unsigned long start_millis; + while (read_bytes < len) { + start_millis = millis(); + do { + ret = PN532_Serial->read(); + if (ret >= 0) { + break; + } + } while((timeout == 0) || ((millis()- start_millis ) < timeout)); + + if (ret < 0) { + if (read_bytes) { + return read_bytes; + } else { + return PN532_TIMEOUT; + } + } + buf[read_bytes] = (uint8_t)ret; + read_bytes++; + } + return read_bytes; +} + +int8_t PN532_readAckFrame(void) +{ + const uint8_t PN532_ACK[] = {0, 0, 0xFF, 0, 0xFF, 0}; + uint8_t ackBuf[sizeof(PN532_ACK)]; + + if (PN532_receive(ackBuf, sizeof(PN532_ACK), PN532_ACK_WAIT_TIME) <= 0) { + return PN532_TIMEOUT; + } + + if (memcmp(&ackBuf, &PN532_ACK, sizeof(PN532_ACK))) { + return PN532_INVALID_ACK; + } + return 0; +} + +int8_t PN532_writeCommand(const uint8_t *header, uint8_t hlen, const uint8_t *body = 0, uint8_t blen = 0) +{ + // Clear the serial buffer just in case + PN532_Serial->flush(); + + pn532_command = header[0]; + PN532_Serial->write((uint8_t)PN532_PREAMBLE); + PN532_Serial->write((uint8_t)PN532_STARTCODE1); + PN532_Serial->write(PN532_STARTCODE2); + + uint8_t length = hlen + blen + 1; // length of data field: TFI + DATA + PN532_Serial->write(length); + PN532_Serial->write(~length + 1); // checksum of length + + PN532_Serial->write(PN532_HOSTTOPN532); + uint8_t sum = PN532_HOSTTOPN532; // sum of TFI + DATA + + PN532_Serial->write(header, hlen); + for (uint8_t i = 0; i < hlen; i++) { + sum += header[i]; + } + + PN532_Serial->write(body, blen); + for (uint8_t i = 0; i < blen; i++) { + sum += body[i]; + } + + uint8_t checksum = ~sum + 1; // checksum of TFI + DATA + PN532_Serial->write(checksum); + PN532_Serial->write((uint8_t)PN532_POSTAMBLE); + + return PN532_readAckFrame(); +} + +int16_t PN532_readResponse(uint8_t buf[], uint8_t len, uint16_t timeout = 50) +{ + uint8_t tmp[3]; + + // Read preamble and start code + if (PN532_receive(tmp, 3, timeout)<=0) { + return PN532_TIMEOUT; + } + if (0 != tmp[0] || 0!= tmp[1] || 0xFF != tmp[2]) { + return PN532_INVALID_FRAME; + } + + // Get length of data to be received + uint8_t length[2]; + if (PN532_receive(length, 2, timeout) <= 0) { + return PN532_TIMEOUT; + } + // Validate that frame is valid + if (0 != (uint8_t)(length[0] + length[1])) { + return PN532_INVALID_FRAME; + } + length[0] -= 2; + if (length[0] > len) { // If this happens, then pn532_packetbuffer is not large enough + return PN532_NO_SPACE; + } + + // Get the command byte + uint8_t cmd = pn532_command + 1; + if (PN532_receive(tmp, 2, timeout) <= 0) { // Time out while receiving + return PN532_TIMEOUT; + } + if (PN532_PN532TOHOST != tmp[0] || cmd != tmp[1]) { // Invalid frame received + return PN532_INVALID_FRAME; + } + + if (PN532_receive(buf, length[0], timeout) != length[0]) { // Timed out + return PN532_TIMEOUT; + } + + uint8_t sum = PN532_PN532TOHOST + cmd; + for (uint8_t i=0; i status) { + return 0; + } + + response = pn532_packetbuffer[0]; + response <<= 8; + response |= pn532_packetbuffer[1]; + response <<= 8; + response |= pn532_packetbuffer[2]; + response <<= 8; + response |= pn532_packetbuffer[3]; + + return response; +} + +void PN532_wakeup(void) +{ + uint8_t wakeup[5] = {0x55, 0x55, 0, 0, 0 }; + PN532_Serial->write(wakeup,sizeof(wakeup)); + + // Flush the serial buffer just in case there's garbage in there + PN532_Serial->flush(); +} + +bool PN532_readPassiveTargetID(uint8_t cardbaudrate, uint8_t *uid, uint8_t *uidLength, uint16_t timeout = 50) +{ + pn532_packetbuffer[0] = PN532_COMMAND_INLISTPASSIVETARGET; + pn532_packetbuffer[1] = 1; // max 1 cards at once (we can set this to 2 later) + pn532_packetbuffer[2] = cardbaudrate; + if (PN532_writeCommand(pn532_packetbuffer, 3)) { + return 0x0; // command failed + } + // read data packet + if (PN532_readResponse(pn532_packetbuffer, sizeof(pn532_packetbuffer), timeout) < 0) { + return 0x0; + } + + /* Check some basic stuff + b0 Tags Found + b1 Tag Number (only one used in this example) + b2..3 SENS_RES + b4 SEL_RES + b5 NFCID Length + b6..NFCIDLen NFCID + */ + + if (pn532_packetbuffer[0] != 1) { + return 0; + } + + uint16_t sens_res = pn532_packetbuffer[2]; + sens_res <<= 8; + sens_res |= pn532_packetbuffer[3]; + + /* Card appears to be Mifare Classic */ + *uidLength = pn532_packetbuffer[5]; + + for (uint8_t i = 0; i < pn532_packetbuffer[5]; i++) { + uid[i] = pn532_packetbuffer[6 + i]; + } + + return 1; +} + +bool PN532_setPassiveActivationRetries(uint8_t maxRetries) +{ + pn532_packetbuffer[0] = PN532_COMMAND_RFCONFIGURATION; + pn532_packetbuffer[1] = 5; // Config item 5 (MaxRetries) + pn532_packetbuffer[2] = 0xFF; // MxRtyATR (default = 0xFF) + pn532_packetbuffer[3] = 0x01; // MxRtyPSL (default = 0x01) + pn532_packetbuffer[4] = maxRetries; + if (PN532_writeCommand(pn532_packetbuffer, 5)) { + return 0; // no ACK + } + return (0 < PN532_readResponse(pn532_packetbuffer, sizeof(pn532_packetbuffer))); +} + +bool PN532_SAMConfig(void) +{ + pn532_packetbuffer[0] = PN532_COMMAND_SAMCONFIGURATION; + pn532_packetbuffer[1] = 0x01; // normal mode + pn532_packetbuffer[2] = 0x14; // timeout 50ms * 20 = 1 second + pn532_packetbuffer[3] = 0x00; // we don't need the external IRQ pin + if (PN532_writeCommand(pn532_packetbuffer, 4)) { + return false; + } + return (0 < PN532_readResponse(pn532_packetbuffer, sizeof(pn532_packetbuffer))); +} + +#ifdef USE_PN532_DATA_FUNCTION + +uint8_t mifareclassic_AuthenticateBlock (uint8_t *uid, uint8_t uidLen, uint32_t blockNumber, uint8_t keyNumber, uint8_t *keyData) +{ + uint8_t i; + uint8_t _key[6]; + uint8_t _uid[7]; + uint8_t _uidLen; + + // Hang on to the key and uid data + memcpy(&_key, keyData, 6); + memcpy(&_uid, uid, uidLen); + _uidLen = uidLen; + + // Prepare the authentication command // + pn532_packetbuffer[0] = PN532_COMMAND_INDATAEXCHANGE; /* Data Exchange Header */ + pn532_packetbuffer[1] = 1; /* Max card numbers */ + pn532_packetbuffer[2] = (keyNumber) ? MIFARE_CMD_AUTH_B : MIFARE_CMD_AUTH_A; + pn532_packetbuffer[3] = blockNumber; /* Block Number (1K = 0..63, 4K = 0..255 */ + memcpy(&pn532_packetbuffer[4], &_key, 6); + for (i = 0; i < _uidLen; i++) { + pn532_packetbuffer[10 + i] = _uid[i]; /* 4 bytes card ID */ + } + + if (PN532_writeCommand(pn532_packetbuffer, 10 + _uidLen)) { return 0; } + + // Read the response packet + PN532_readResponse(pn532_packetbuffer, sizeof(pn532_packetbuffer)); + + // Check if the response is valid and we are authenticated??? + // for an auth success it should be bytes 5-7: 0xD5 0x41 0x00 + // Mifare auth error is technically byte 7: 0x14 but anything other and 0x00 is not good + if (pn532_packetbuffer[0] != 0x00) { + // Authentification failed + return 0; + } + + return 1; +} + +uint8_t mifareclassic_ReadDataBlock (uint8_t blockNumber, uint8_t *data) +{ + /* Prepare the command */ + pn532_packetbuffer[0] = PN532_COMMAND_INDATAEXCHANGE; + pn532_packetbuffer[1] = 1; /* Card number */ + pn532_packetbuffer[2] = MIFARE_CMD_READ; /* Mifare Read command = 0x30 */ + pn532_packetbuffer[3] = blockNumber; /* Block Number (0..63 for 1K, 0..255 for 4K) */ + + /* Send the command */ + if (PN532_writeCommand(pn532_packetbuffer, 4)) { + return 0; + } + + /* Read the response packet */ + PN532_readResponse(pn532_packetbuffer, sizeof(pn532_packetbuffer)); + + /* If byte 8 isn't 0x00 we probably have an error */ + if (pn532_packetbuffer[0] != 0x00) { + return 0; + } + + /* Copy the 16 data bytes to the output buffer */ + /* Block content starts at byte 9 of a valid response */ + memcpy (data, &pn532_packetbuffer[1], 16); + + return 1; +} + +uint8_t mifareclassic_WriteDataBlock (uint8_t blockNumber, uint8_t *data) +{ + /* Prepare the first command */ + pn532_packetbuffer[0] = PN532_COMMAND_INDATAEXCHANGE; + pn532_packetbuffer[1] = 1; /* Card number */ + pn532_packetbuffer[2] = MIFARE_CMD_WRITE; /* Mifare Write command = 0xA0 */ + pn532_packetbuffer[3] = blockNumber; /* Block Number (0..63 for 1K, 0..255 for 4K) */ + memcpy(&pn532_packetbuffer[4], data, 16); /* Data Payload */ + + /* Send the command */ + if (PN532_writeCommand(pn532_packetbuffer, 20)) { + return 0; + } + + /* Read the response packet */ + return (0 < PN532_readResponse(pn532_packetbuffer, sizeof(pn532_packetbuffer))); +} + +#endif // USE_PN532_DATA_FUNCTION + +void PN532_ScanForTag(void) +{ + if (!pn532_model) { return; } + uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 }; + uint8_t uid_len = 0; + uint8_t card_data[16]; + bool erase_success = false; + bool set_success = false; + if (PN532_readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uid_len)) { + char uids[15]; + +#ifdef USE_PN532_DATA_FUNCTION + char card_datas[34]; +#endif // USE_PN532_DATA_FUNCTION + + sprintf(uids,""); + for (uint8_t i = 0;i < uid_len;i++) { + sprintf(uids,"%s%02X",uids,uid[i]); + } + +#ifdef USE_PN532_DATA_FUNCTION + if (uid_len == 4) { // Lets try to read block 1 of the mifare classic card for more information + uint8_t keyuniversal[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + if (mifareclassic_AuthenticateBlock (uid, uid_len, 1, 1, keyuniversal)) { + if (mifareclassic_ReadDataBlock(1, card_data)) { +#ifdef USE_PN532_DATA_RAW + memcpy(&card_datas,&card_data,sizeof(card_data)); +#else + for (uint8_t i = 0;i < sizeof(card_data);i++) { + if ((isalpha(card_data[i])) || ((isdigit(card_data[i])))) { + card_datas[i] = char(card_data[i]); + } else { + card_datas[i] = '\0'; + } + } +#endif // USE_PN532_DATA_RAW + } + if (pn532_function == 1) { // erase block 1 of card + for (uint8_t i = 0;i<16;i++) { + card_data[i] = 0x00; + } + if (mifareclassic_WriteDataBlock(1, card_data)) { + erase_success = true; + AddLog_P(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Erase success")); + memcpy(&card_datas,&card_data,sizeof(card_data)); // Cast block 1 to a string + } + } + if (pn532_function == 2) { +#ifdef USE_PN532_DATA_RAW + memcpy(&card_data,&pn532_newdata,sizeof(card_data)); + if (mifareclassic_WriteDataBlock(1, card_data)) { + set_success = true; + AddLog_P(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Data write successful")); + memcpy(&card_datas,&card_data,sizeof(card_data)); // Cast block 1 to a string + } +#else + bool IsAlphaNumeric = true; + for (uint8_t i = 0;i < pn532_newdata_len;i++) { + if ((!isalpha(pn532_newdata[i])) && (!isdigit(pn532_newdata[i]))) { + IsAlphaNumeric = false; + } + } + if (IsAlphaNumeric) { + memcpy(&card_data,&pn532_newdata,pn532_newdata_len); + card_data[pn532_newdata_len] = '\0'; // Enforce null termination + if (mifareclassic_WriteDataBlock(1, card_data)) { + set_success = true; + AddLog_P(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Data write successful")); + memcpy(&card_datas,&card_data,sizeof(card_data)); // Cast block 1 to a string + } + } else { + AddLog_P(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Data must be alphanumeric")); + } +#endif // USE_PN532_DATA_RAW + } + } else { + sprintf(card_datas,"AUTHFAIL"); + } + } + switch (pn532_function) { + case 0x01: + if (!erase_success) { + AddLog_P(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Erase fail - exiting erase mode")); + } + break; + case 0x02: + if (!set_success) { + AddLog_P(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Write failed - exiting set mode")); + } + default: + break; + } + pn532_function = 0; +#endif // USE_PN532_DATA_FUNCTION + + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_TIME "\":\"%s\""), GetDateAndTime(DT_LOCAL).c_str()); + +#ifdef USE_PN532_DATA_FUNCTION + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"PN532\":{\"UID\":\"%s\", \"DATA\":\"%s\"}}"), mqtt_data, uids, card_datas); +#else + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"PN532\":{\"UID\":\"%s\"}}"), mqtt_data, uids); +#endif // USE_PN532_DATA_FUNCTION + + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); + +#ifdef USE_PN532_CAUSE_EVENTS + + char command[71]; +#ifdef USE_PN532_DATA_FUNCTION + sprintf(command,"backlog event PN532_UID=%s;event PN532_DATA=%s",uids,card_datas); +#else + sprintf(command,"event PN532_UID=%s",uids); +#endif // USE_PN532_DATA_FUNCTION + ExecuteCommand(command, SRC_RULE); +#endif // USE_PN532_CAUSE_EVENTS + + pn532_scantimer = 7; // Ignore tags found for two seconds + } +} + +#ifdef USE_PN532_DATA_FUNCTION + +bool PN532_Command(void) +{ + bool serviced = true; + uint8_t paramcount = 0; + if (XdrvMailbox.data_len > 0) { + paramcount=1; + } else { + serviced = false; + return serviced; + } + char sub_string[XdrvMailbox.data_len]; + char sub_string_tmp[XdrvMailbox.data_len]; + for (uint8_t ca=0;ca 1) { + if (XdrvMailbox.data[XdrvMailbox.data_len-1] == ',') { + serviced = false; + return serviced; + } + sprintf(sub_string_tmp,subStr(sub_string, XdrvMailbox.data, ",", 2)); + pn532_newdata_len = strlen(sub_string_tmp); + if (pn532_newdata_len > 15) { pn532_newdata_len = 15; } + memcpy(&pn532_newdata,&sub_string_tmp,pn532_newdata_len); + pn532_newdata[pn532_newdata_len] = 0x00; // Null terminate the string + pn532_function = 2; + AddLog_P2(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Next scanned tag data block 1 will be set to '%s'"), pn532_newdata); + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_TIME "\":\"%s\""), GetDateAndTime(DT_LOCAL).c_str()); + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"PN532\":{\"COMMAND\":\"S\"\"}}"), mqtt_data); + return serviced; + } + } +} + +#endif // USE_PN532_DATA_FUNCTION + +bool Xsns40(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_INIT: + PN532_Init(); + result = true; + break; + case FUNC_EVERY_50_MSECOND: + break; + case FUNC_EVERY_100_MSECOND: + break; + case FUNC_EVERY_250_MSECOND: + if (pn532_scantimer > 0) { + pn532_scantimer--; + } else { + PN532_ScanForTag(); + } + break; + case FUNC_EVERY_SECOND: + break; +#ifdef USE_PN532_DATA_FUNCTION + case FUNC_COMMAND_SENSOR: + if (XSNS_40 == XdrvMailbox.index) { + result = PN532_Command(); + } + break; +#endif + } + return result; +} + +#endif // USE_PN532_HSU diff --git a/sonoff/xsns_41_max44009.ino b/sonoff/xsns_41_max44009.ino new file mode 100644 index 000000000..1cb0e97a7 --- /dev/null +++ b/sonoff/xsns_41_max44009.ino @@ -0,0 +1,179 @@ +/* + xsns_41_max44009.ino - MAX44009 ambient light sensor support for Sonoff-Tasmota + + Copyright (C) 2019 Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef USE_I2C +#ifdef USE_MAX44009 +/*********************************************************************************************\ + * MAX44009 - Ambient Light Intensity + * + * I2C Address: 0x4a or 0x4b +\*********************************************************************************************/ + +#define XSNS_41 41 + +#define MAX44009_ADDR1 0x4A +#define MAX44009_ADDR2 0x4B +#define MAX44009_NO_REGISTERS 8 +#define REG_CONFIG 0x02 +#define REG_LUMINANCE 0x03 +#define REG_LOWER_THRESHOLD 0x06 +#define REG_THRESHOLD_TIMER 0x07 + +#define MAX44009_CONTINUOUS_AUTO_MODE 0x80 // Start measurement in automatic, continous mode + +uint8_t max44009_address; +uint8_t max44009_addresses[] = { MAX44009_ADDR1, MAX44009_ADDR2, 0 }; //0 terminated list +uint8_t max44009_found = 0; +uint8_t max44009_valid = 0; +float max44009_illuminance = 0; +char max44009_types[] = "MAX44009"; + +bool Max4409Read_lum(void) +{ + max44009_valid = 0; + uint8_t regdata[2]; + + /* Read 2 bytes luminance */ + if (I2cValidRead16((uint16_t *)®data, max44009_address, REG_LUMINANCE)) { + int exponent = (regdata[0] & 0xF0) >> 4; + int mantissa = ((regdata[0] & 0x0F) << 4) | (regdata[1] & 0x0F); + max44009_illuminance = (float)(((0x00000001 << exponent) * (float)mantissa) * 0.045); + max44009_valid = 1; + return true; + } else { + return false; + } +} + +/********************************************************************************************/ + +void Max4409Detect(void) +{ + uint8_t reg[8]; + bool failed = false; + + if (max44009_found) { + return; + } + + uint8_t buffer1; + uint8_t buffer2; + for (uint8_t i = 0; 0 != max44009_addresses[i]; i++) { + + max44009_address = max44009_addresses[i]; + + if ((I2cValidRead8(&buffer1, max44009_address, REG_LOWER_THRESHOLD)) && + (I2cValidRead8(&buffer2, max44009_address, REG_THRESHOLD_TIMER))) { + //snprintf(log_data, sizeof(log_data), "MAX44009 %x: %x, %x", max44009_address, (int)buffer1, (int)buffer2); + //AddLog(LOG_LEVEL_DEBUG_MORE); + if ((0x00 == buffer1) && + (0xFF == buffer2)) { + + // looks like a MAX44009, try to initialize + + Wire.beginTransmission(max44009_address); + + // select configuration register and set mode + Wire.write(REG_CONFIG); + Wire.write(MAX44009_CONTINUOUS_AUTO_MODE); + if (0 == Wire.endTransmission()) { + max44009_found = 1; + AddLog_P2(LOG_LEVEL_DEBUG, S_LOG_I2C_FOUND_AT, max44009_types, max44009_address); + break; + } + } + } + } +} + +void Max4409EverySecond(void) +{ + if (max44009_found) { + Max4409Read_lum(); + } +} + +void Max4409Show(bool json) +{ + char illum_str[8]; + + if (max44009_valid) { + + /* convert illuminance to string with suitable accuracy */ + + uint8_t prec = 0; + if (10 > max44009_illuminance ) { + prec = 3; + } else if (100 > max44009_illuminance) { + prec = 2; + } else if (1000 > max44009_illuminance) { + prec = 1; + } + dtostrf(max44009_illuminance, sizeof(illum_str) -1, prec, illum_str); + + if (json) { + snprintf_P(mqtt_data, sizeof(mqtt_data), + PSTR("%s,\"%s\":{\"" D_JSON_ILLUMINANCE "\":%s}"), + mqtt_data, max44009_types, illum_str); +#ifdef USE_DOMOTICZ + if (0 == tele_period) { + DomoticzSensor(DZ_ILLUMINANCE, illum_str); + } +#endif // USE_DOMOTICZ +#ifdef USE_WEBSERVER + } else { + // show integer value for lx on web-server + snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_ILLUMINANCE, + mqtt_data, max44009_types, (int)max44009_illuminance); +#endif // USE_WEBSERVER + } + } +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xsns41(uint8_t function) +{ + bool result = false; + + if (i2c_flg) { + switch (function) { + case FUNC_INIT: + Max4409Detect(); + break; + case FUNC_EVERY_SECOND: + Max4409EverySecond(); + break; + case FUNC_JSON_APPEND: + Max4409Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_APPEND: + Max4409Show(0); + break; +#endif // USE_WEBSERVER + } + } + return result; +} + +#endif // USE_MAX44009 +#endif // USE_I2C diff --git a/sonoff/xsns_42_scd30.ino b/sonoff/xsns_42_scd30.ino new file mode 100644 index 000000000..c62df4d25 --- /dev/null +++ b/sonoff/xsns_42_scd30.ino @@ -0,0 +1,494 @@ +/* + xsns_42_scd30.ino - SC30 CO2 sensor support for Sonoff-Tasmota + + Copyright (C) 2019 Frogmore42 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +*/ +#ifdef USE_I2C +#ifdef USE_SCD30 + +#define XSNS_42 42 + +#define SCD30_MAX_MISSED_READS 3 +#define SONOFF_SCD30_STATE_NO_ERROR 0 +#define SONOFF_SCD30_STATE_ERROR_DATA_CRC 1 +#define SONOFF_SCD30_STATE_ERROR_READ_MEAS 2 +#define SONOFF_SCD30_STATE_ERROR_SOFT_RESET 3 +#define SONOFF_SCD30_STATE_ERROR_I2C_RESET 4 +#define SONOFF_SCD30_STATE_ERROR_UNKNOWN 5 + +#include "Arduino.h" +#include + +#define D_CMND_SCD30 "SCD30" + +const char S_JSON_SCD30_COMMAND_NVALUE[] PROGMEM = "{\"" D_CMND_SCD30 "%s\":%d}"; +const char S_JSON_SCD30_COMMAND_NFW_VALUE[] PROGMEM = "{\"" D_CMND_SCD30 "%s\":%d.%d}"; +const char S_JSON_SCD30_COMMAND[] PROGMEM = "{\"" D_CMND_SCD30 "%s\"}"; +const char kSCD30_Commands[] PROGMEM = "Alt|Auto|Cal|FW|Int|Pres|TOff"; + +/*********************************************************************************************\ + * enumerationsines +\*********************************************************************************************/ + +enum SCD30_Commands { // commands useable in console or rules + CMND_SCD30_ALTITUDE, + CMND_SCD30_AUTOMODE, + CMND_SCD30_CALIBRATE, + CMND_SCD30_FW, + CMND_SCD30_INTERVAL, + CMND_SCD30_PRESSURE, + CMND_SCD30_TEMPOFFSET +}; + + + +FrogmoreScd30 scd30; + +bool scd30Found = false; +bool scd30IsDataValid = false; +int scd30ErrorState = SONOFF_SCD30_STATE_NO_ERROR; +uint16_t scd30Interval_sec; +int scd30Loop_count = 0; +int scd30DataNotAvailable_count = 0; +int scd30GoodMeas_count = 0; +int scd30Reset_count = 0; +int scd30CrcError_count = 0; +int scd30Co2Zero_count = 0; +int i2cReset_count = 0; +uint16_t scd30_CO2 = 0; +uint16_t scd30_CO2EAvg = 0; +float scd30_Humid = 0.0; +float scd30_Temp = 0.0; + +bool Scd30Init() +{ + int error; + bool i2c_flg = ((pin[GPIO_I2C_SCL] < 99) && (pin[GPIO_I2C_SDA] < 99)); + if (i2c_flg) + { + uint8_t major = 0; + uint8_t minor = 0; + uint16_t interval_sec; + scd30.begin(); + error = scd30.getFirmwareVersion(&major, &minor); + if (error) + { +#ifdef SCD30_DEBUG + snprintf_P(log_data, sizeof(log_data), "SCD30: did not find an SCD30: 0x%lX", error); + AddLog(LOG_LEVEL_DEBUG); +#endif + return false; + } + else + { +#ifdef SCD30_DEBUG + snprintf_P(log_data, sizeof(log_data), "SCD30: found an SCD30: FW v%d.%d", major, minor); + AddLog(LOG_LEVEL_INFO); +#endif + } + + error = scd30.getMeasurementInterval(&scd30Interval_sec); + if (error) + { +#ifdef SCD30_DEBUG + snprintf_P(log_data, sizeof(log_data), "SCD30: error getMeasurementInterval: 0x%lX", error); + AddLog(LOG_LEVEL_ERROR); +#endif + return false; + } + + error = scd30.beginMeasuring(); + if (error) + { +#ifdef SCD30_DEBUG + snprintf_P(log_data, sizeof(log_data), "Error: Scd30BeginMeasuring: 0x%lX", error); + AddLog(LOG_LEVEL_ERROR); +#endif + return false; + } + + return true; + } +} + +// gets data from the sensor every 3 seconds or so to give the sensor time to gather new data +int Scd30Update() +{ + int error = 0; + int16_t delta = 0; + scd30Loop_count++; + + if (!scd30Found) + { + scd30Found = Scd30Init(); +#ifdef SCD30_DEBUG + snprintf_P(log_data, sizeof(log_data), "Scd30Update: found: %d ", scd30Found); + AddLog(LOG_LEVEL_INFO); +#endif + if (!scd30Found) + { +#ifdef SCD30_DEBUG + snprintf_P(log_data, sizeof(log_data), "Scd30Update: found: %d ", scd30Found); + AddLog(LOG_LEVEL_INFO); +#endif + return (ERROR_SCD30_NOT_FOUND_ERROR); + } + } + else + { + if (scd30Loop_count > (scd30Interval_sec - 1)) + { + switch (scd30ErrorState) + { + case SONOFF_SCD30_STATE_NO_ERROR: + { + error = scd30.readMeasurement(&scd30_CO2, &scd30_CO2EAvg, &scd30_Temp, &scd30_Humid); + switch (error) + { + case ERROR_SCD30_NO_ERROR: + scd30Loop_count = 0; + scd30IsDataValid = true; + scd30GoodMeas_count++; + break; + + case ERROR_SCD30_NO_DATA: + scd30DataNotAvailable_count++; + break; + + case ERROR_SCD30_CRC_ERROR: + scd30ErrorState = SONOFF_SCD30_STATE_ERROR_DATA_CRC; + scd30CrcError_count++; +#ifdef SCD30_DEBUG + snprintf_P(log_data, sizeof(log_data), "SCD30: CRC error, CRC error: %ld, CO2 zero: %ld, good: %ld, no data: %ld, sc30_reset: %ld, i2c_reset: %ld", scd30CrcError_count, scd30Co2Zero_count, scd30GoodMeas_count, scd30DataNotAvailable_count, scd30Reset_count, i2cReset_count); + AddLog(LOG_LEVEL_ERROR); +#endif + break; + + case ERROR_SCD30_CO2_ZERO: + scd30Co2Zero_count++; +#ifdef SCD30_DEBUG + snprintf_P(log_data, sizeof(log_data), "SCD30: CO2 zero, CRC error: %ld, CO2 zero: %ld, good: %ld, no data: %ld, sc30_reset: %ld, i2c_reset: %ld", scd30CrcError_count, scd30Co2Zero_count, scd30GoodMeas_count, scd30DataNotAvailable_count, scd30Reset_count, i2cReset_count); + AddLog(LOG_LEVEL_ERROR); +#endif + break; + + default: + { + scd30ErrorState = SONOFF_SCD30_STATE_ERROR_READ_MEAS; +#ifdef SCD30_DEBUG + snprintf_P(log_data, sizeof(log_data), "SCD30: Update: ReadMeasurement error: 0x%lX, counter: %ld", error, scd30Loop_count); + AddLog(LOG_LEVEL_ERROR); +#endif + return (error); + } + break; + } + } + break; + + case SONOFF_SCD30_STATE_ERROR_DATA_CRC: + { + //scd30IsDataValid = false; +#ifdef SCD30_DEBUG + snprintf_P(log_data, sizeof(log_data), "SCD30: in error state: %d, good: %ld, no data: %ld, sc30 reset: %ld, i2c reset: %ld", scd30ErrorState, scd30GoodMeas_count, scd30DataNotAvailable_count, scd30Reset_count, i2cReset_count); + AddLog(LOG_LEVEL_ERROR); + snprintf_P(log_data, sizeof(log_data), "SCD30: got CRC error, try again, counter: %ld", scd30Loop_count); + AddLog(LOG_LEVEL_ERROR); +#endif + scd30ErrorState = ERROR_SCD30_NO_ERROR; + } + break; + + case SONOFF_SCD30_STATE_ERROR_READ_MEAS: + { + //scd30IsDataValid = false; +#ifdef SCD30_DEBUG + snprintf_P(log_data, sizeof(log_data), "SCD30: in error state: %d, good: %ld, no data: %ld, sc30 reset: %ld, i2c reset: %ld", scd30ErrorState, scd30GoodMeas_count, scd30DataNotAvailable_count, scd30Reset_count, i2cReset_count); + AddLog(LOG_LEVEL_ERROR); + snprintf_P(log_data, sizeof(log_data), "SCD30: not answering, sending soft reset, counter: %ld", scd30Loop_count); + AddLog(LOG_LEVEL_ERROR); +#endif + scd30Reset_count++; + error = scd30.softReset(); + if (error) + { +#ifdef SCD30_DEBUG + snprintf_P(log_data, sizeof(log_data), "SCD30: resetting got error: 0x%lX", error); + AddLog(LOG_LEVEL_ERROR); +#endif + error >>= 8; + if (error == 4) + { + scd30ErrorState = SONOFF_SCD30_STATE_ERROR_SOFT_RESET; + } + else + { + scd30ErrorState = SONOFF_SCD30_STATE_ERROR_UNKNOWN; + } + } + else + { + scd30ErrorState = ERROR_SCD30_NO_ERROR; + } + } + break; + + case SONOFF_SCD30_STATE_ERROR_SOFT_RESET: + { + //scd30IsDataValid = false; +#ifdef SCD30_DEBUG + snprintf_P(log_data, sizeof(log_data), "SCD30: in error state: %d, good: %ld, no data: %ld, sc30 reset: %ld, i2c reset: %ld", scd30ErrorState, scd30GoodMeas_count, scd30DataNotAvailable_count, scd30Reset_count, i2cReset_count); + AddLog(LOG_LEVEL_ERROR); + snprintf_P(log_data, sizeof(log_data), "SCD30: clearing i2c bus"); + AddLog(LOG_LEVEL_ERROR); +#endif + i2cReset_count++; + error = scd30.clearI2CBus(); + if (error) + { + scd30ErrorState = SONOFF_SCD30_STATE_ERROR_I2C_RESET; +#ifdef SCD30_DEBUG + snprintf_P(log_data, sizeof(log_data), "SCD30: error clearing i2c bus: 0x%lX", error); + AddLog(LOG_LEVEL_ERROR); +#endif + } + else + { + scd30ErrorState = ERROR_SCD30_NO_ERROR; + } + } + break; + + default: + { + //scd30IsDataValid = false; +#ifdef SCD30_DEBUG + snprintf_P(log_data, sizeof(log_data), "SCD30: unknown error state: 0x%lX", scd30ErrorState); + AddLog(LOG_LEVEL_ERROR); +#endif + scd30ErrorState = SONOFF_SCD30_STATE_ERROR_SOFT_RESET; // try again + } + } + + if (scd30Loop_count > (SCD30_MAX_MISSED_READS * scd30Interval_sec)) + { + scd30IsDataValid = false; + } + } + } + return (ERROR_SCD30_NO_ERROR); +} + + +int Scd30GetCommand(int command_code, uint16_t *pvalue) +{ + switch (command_code) + { + case CMND_SCD30_ALTITUDE: + return scd30.getAltitudeCompensation(pvalue); + break; + + case CMND_SCD30_AUTOMODE: + return scd30.getCalibrationType(pvalue); + break; + + case CMND_SCD30_CALIBRATE: + return scd30.getForcedRecalibrationFactor(pvalue); + break; + + case CMND_SCD30_INTERVAL: + return scd30.getMeasurementInterval(pvalue); + break; + + case CMND_SCD30_PRESSURE: + return scd30.getAmbientPressure(pvalue); + break; + + case CMND_SCD30_TEMPOFFSET: + return scd30.getTemperatureOffset(pvalue); + break; + + default: + // else for Unknown command + break; + } +} + +int Scd30SetCommand(int command_code, uint16_t value) +{ + switch (command_code) + { + case CMND_SCD30_ALTITUDE: + return scd30.setAltitudeCompensation(value); + break; + + case CMND_SCD30_AUTOMODE: + return scd30.setCalibrationType(value); + break; + + case CMND_SCD30_CALIBRATE: + return scd30.setForcedRecalibrationFactor(value); + break; + + case CMND_SCD30_INTERVAL: + { + int error = scd30.setMeasurementInterval(value); + if (!error) + { + scd30Interval_sec = value; + } + + return error; + } + break; + + case CMND_SCD30_PRESSURE: + return scd30.setAmbientPressure(value); + break; + + case CMND_SCD30_TEMPOFFSET: + return scd30.setTemperatureOffset(value); + break; + + default: + // else for Unknown command + break; + } +} +/*********************************************************************************************\ + * Command Sensor92 +\*********************************************************************************************/ + +bool Scd30CommandSensor() +{ + char command[CMDSZ]; + bool serviced = true; + uint8_t prefix_len = strlen(D_CMND_SCD30); + + if (!strncasecmp_P(XdrvMailbox.topic, PSTR(D_CMND_SCD30), prefix_len)) { // prefix + int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic + prefix_len, kSCD30_Commands); + + switch (command_code) { + case CMND_SCD30_ALTITUDE: + case CMND_SCD30_AUTOMODE: + case CMND_SCD30_CALIBRATE: + case CMND_SCD30_INTERVAL: + case CMND_SCD30_PRESSURE: + case CMND_SCD30_TEMPOFFSET: + { + uint16_t value = 0; + if (XdrvMailbox.data_len > 0) + { + value = XdrvMailbox.payload16; + Scd30SetCommand(command_code, value); + } + else + { + Scd30GetCommand(command_code, &value); + } + + snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_SCD30_COMMAND_NVALUE, command, value); + } + break; + + case CMND_SCD30_FW: + { + uint8_t major = 0; + uint8_t minor = 0; + int error; + error = scd30.getFirmwareVersion(&major, &minor); + if (error) + { +#ifdef SCD30_DEBUG + snprintf_P(log_data, sizeof(log_data), "SCD30: error getting FW version: 0x%lX", error); + AddLog(LOG_LEVEL_ERROR); +#endif + serviced = false; + } + else + { + snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_SCD30_COMMAND_NFW_VALUE, command, major, minor); + } + } + break; + + default: + // else for Unknown command + serviced = false; + break; + } + } + return serviced; +} + +void Scd30Show(bool json) +{ + char humidity[10]; + char temperature[10]; + + if (scd30Found && scd30IsDataValid) + { + dtostrfd(scd30_Humid, Settings.flag2.humidity_resolution, humidity); + dtostrfd(ConvertTemp(scd30_Temp), Settings.flag2.temperature_resolution, temperature); + if (json) { + //snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"SCD30\":{\"" D_JSON_CO2 "\":%d,\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_HUMIDITY "\":%s}"), mqtt_data, scd30_CO2, temperature, humidity); + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"SCD30\":{\"" D_JSON_CO2 "\":%d,\"" D_JSON_ECO2 "\":%d,\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_HUMIDITY "\":%s}"), mqtt_data, scd30_CO2, scd30_CO2EAvg, temperature, humidity); +#ifdef USE_DOMOTICZ + if (0 == tele_period) DomoticzSensor(DZ_AIRQUALITY, scd30_CO2); +#endif // USE_DOMOTICZ +#ifdef USE_WEBSERVER + } else { + snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_CO2EAVG, mqtt_data, "SCD30", scd30_CO2EAvg); + snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_CO2, mqtt_data, "SCD30", scd30_CO2); + snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_TEMP, mqtt_data, "SCD30", temperature, TempUnit()); + snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_HUM, mqtt_data, "SCD30", humidity); +#endif // USE_WEBSERVER + } + } +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xsns42(byte function) +{ + bool result = false; + + if (i2c_flg) { + switch (function) { + case FUNC_EVERY_SECOND: + Scd30Update(); + break; + case FUNC_COMMAND: + result = Scd30CommandSensor(); + break; + case FUNC_JSON_APPEND: + Scd30Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_APPEND: + Scd30Show(0); + break; +#endif // USE_WEBSERVER + } + } + return result; +} + +#endif // USE_SCD30 +#endif // USE_I2C diff --git a/sonoff/xsns_interface.ino b/sonoff/xsns_interface.ino index d39995be9..fdb947bfb 100644 --- a/sonoff/xsns_interface.ino +++ b/sonoff/xsns_interface.ino @@ -1,7 +1,7 @@ /* xsns_interface.ino - Sensor interface support for Sonoff-Tasmota - Copyright (C) 2018 Theo Arends inspired by ESPEasy + Copyright (C) 2019 Theo Arends inspired by ESPEasy This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -18,9 +18,9 @@ */ #ifdef XFUNC_PTR_IN_ROM -boolean (* const xsns_func_ptr[])(byte) PROGMEM = { // Sensor Function Pointers for simple implementation of sensors +bool (* const xsns_func_ptr[])(uint8_t) PROGMEM = { // Sensor Function Pointers for simple implementation of sensors #else -boolean (* const xsns_func_ptr[])(byte) = { // Sensor Function Pointers for simple implementation of sensors +bool (* const xsns_func_ptr[])(uint8_t) = { // Sensor Function Pointers for simple implementation of sensors #endif #ifdef XSNS_01 @@ -268,7 +268,7 @@ const uint8_t xsns_present = sizeof(xsns_func_ptr) / sizeof(xsns_func_ptr[0]); * Function call to all xsns \*********************************************************************************************/ -boolean XsnsNextCall(byte Function, uint8_t &xsns_index) +bool XsnsNextCall(uint8_t Function, uint8_t &xsns_index) { xsns_index++; if (xsns_index == xsns_present) { xsns_index = 0; } @@ -282,15 +282,15 @@ boolean XsnsNextCall(byte Function, uint8_t &xsns_index) return xsns_func_ptr[xsns_index](Function); } -boolean XsnsCall(byte Function) +bool XsnsCall(uint8_t Function) { - boolean result = false; + bool result = false; #ifdef PROFILE_XSNS_EVERY_SECOND uint32_t profile_start_millis = millis(); #endif // PROFILE_XSNS_EVERY_SECOND - for (byte x = 0; x < xsns_present; x++) { + for (uint8_t x = 0; x < xsns_present; x++) { #ifdef USE_DEBUG_DRIVER if (XsnsEnabled(x)) { #endif @@ -305,13 +305,16 @@ boolean XsnsCall(byte Function) uint32_t profile_millis = millis() - profile_start_millis; if (profile_millis) { if (FUNC_EVERY_SECOND == Function) { - snprintf_P(log_data, sizeof(log_data), PSTR("PRF: At %08u XsnsCall %d to Sensor %d took %u mS"), uptime, Function, x, profile_millis); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PRF: At %08u XsnsCall %d to Sensor %d took %u mS"), uptime, Function, x, profile_millis); } } #endif // PROFILE_XSNS_SENSOR_EVERY_SECOND - if (result) break; + if (result && ((FUNC_COMMAND == Function) || + (FUNC_COMMAND_SENSOR == Function) + )) { + break; + } #ifdef USE_DEBUG_DRIVER } #endif @@ -321,8 +324,7 @@ boolean XsnsCall(byte Function) uint32_t profile_millis = millis() - profile_start_millis; if (profile_millis) { if (FUNC_EVERY_SECOND == Function) { - snprintf_P(log_data, sizeof(log_data), PSTR("PRF: At %08u XsnsCall %d took %u mS"), uptime, Function, profile_millis); - AddLog(LOG_LEVEL_DEBUG); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PRF: At %08u XsnsCall %d took %u mS"), uptime, Function, profile_millis); } } #endif // PROFILE_XSNS_EVERY_SECOND diff --git a/sonoff/zzzz_debug.ino b/sonoff/zzzz_debug.ino new file mode 100644 index 000000000..e0618b6ab --- /dev/null +++ b/sonoff/zzzz_debug.ino @@ -0,0 +1,309 @@ +/* + zzzz_debug.ino - debug support for Sonoff-Tasmota + + Copyright (C) 2019 Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef USE_DEBUG_DRIVER +/*********************************************************************************************\ + * Virtual debugging support - Part 2 + * + * Needs to be the last alphabetical file due to DEFINE compile order +\*********************************************************************************************/ + +/*********************************************************************************************\ + * Xsns available list +\*********************************************************************************************/ + +#ifdef XFUNC_PTR_IN_ROM +const uint8_t kXsnsList[] PROGMEM = { +#else +const uint8_t kXsnsList[] = { +#endif + +#ifdef XSNS_01 + XSNS_01, +#endif + +#ifdef XSNS_02 + XSNS_02, +#endif + +#ifdef XSNS_03 + XSNS_03, +#endif + +#ifdef XSNS_04 + XSNS_04, +#endif + +#ifdef XSNS_05 + XSNS_05, +#endif + +#ifdef XSNS_06 + XSNS_06, +#endif + +#ifdef XSNS_07 + XSNS_07, +#endif + +#ifdef XSNS_08 + XSNS_08, +#endif + +#ifdef XSNS_09 + XSNS_09, +#endif + +#ifdef XSNS_10 + XSNS_10, +#endif + +#ifdef XSNS_11 + XSNS_11, +#endif + +#ifdef XSNS_12 + XSNS_12, +#endif + +#ifdef XSNS_13 + XSNS_13, +#endif + +#ifdef XSNS_14 + XSNS_14, +#endif + +#ifdef XSNS_15 + XSNS_15, +#endif + +#ifdef XSNS_16 + XSNS_16, +#endif + +#ifdef XSNS_17 + XSNS_17, +#endif + +#ifdef XSNS_18 + XSNS_18, +#endif + +#ifdef XSNS_19 + XSNS_19, +#endif + +#ifdef XSNS_20 + XSNS_20, +#endif + +#ifdef XSNS_21 + XSNS_21, +#endif + +#ifdef XSNS_22 + XSNS_22, +#endif + +#ifdef XSNS_23 + XSNS_23, +#endif + +#ifdef XSNS_24 + XSNS_24, +#endif + +#ifdef XSNS_25 + XSNS_25, +#endif + +#ifdef XSNS_26 + XSNS_26, +#endif + +#ifdef XSNS_27 + XSNS_27, +#endif + +#ifdef XSNS_28 + XSNS_28, +#endif + +#ifdef XSNS_29 + XSNS_29, +#endif + +#ifdef XSNS_30 + XSNS_30, +#endif + +#ifdef XSNS_31 + XSNS_31, +#endif + +#ifdef XSNS_32 + XSNS_32, +#endif + +#ifdef XSNS_33 + XSNS_33, +#endif + +#ifdef XSNS_34 + XSNS_34, +#endif + +#ifdef XSNS_35 + XSNS_35, +#endif + +#ifdef XSNS_36 + XSNS_36, +#endif + +#ifdef XSNS_37 + XSNS_37, +#endif + +#ifdef XSNS_38 + XSNS_38, +#endif + +#ifdef XSNS_39 + XSNS_39, +#endif + +#ifdef XSNS_40 + XSNS_40, +#endif + +#ifdef XSNS_41 + XSNS_41, +#endif + +#ifdef XSNS_42 + XSNS_42, +#endif + +#ifdef XSNS_43 + XSNS_43, +#endif + +#ifdef XSNS_44 + XSNS_44, +#endif + +#ifdef XSNS_45 + XSNS_45, +#endif + +#ifdef XSNS_46 + XSNS_46, +#endif + +#ifdef XSNS_47 + XSNS_47, +#endif + +#ifdef XSNS_48 + XSNS_48, +#endif + +#ifdef XSNS_49 + XSNS_49, +#endif + +#ifdef XSNS_50 + XSNS_50, +#endif + +// Optional user defined sensors in range 91 - 99 + +#ifdef XSNS_91 + XSNS_91, +#endif + +#ifdef XSNS_92 + XSNS_92, +#endif + +#ifdef XSNS_93 + XSNS_93, +#endif + +#ifdef XSNS_94 + XSNS_94, +#endif + +#ifdef XSNS_95 + XSNS_95 +#endif +}; + +/*********************************************************************************************\ + * Xsns sensor control +\*********************************************************************************************/ + +bool XsnsEnabled(uint8_t sns_index) +{ + if (sns_index < sizeof(kXsnsList)) { +#ifdef XFUNC_PTR_IN_ROM + uint8_t index = pgm_read_byte(kXsnsList + sns_index); +#else + uint8_t index = kXsnsList[sns_index]; +#endif + return bitRead(Settings.sensors[index / 32], index % 32); + } + return true; +} + +bool XsnsPresent(uint8_t sns_index) +{ + uint8_t index = 0; + for (uint8_t i = 0; i < sizeof(kXsnsList); i++) { +#ifdef XFUNC_PTR_IN_ROM + index = pgm_read_byte(kXsnsList + i); +#else + index = kXsnsList[i]; +#endif + if (index == sns_index) { return true; } + } + return false; +} + +String XsnsGetSensors(void) +{ + char state[2] = { 0 }; + + String data = F("["); + for (uint8_t i = 0; i < MAX_XSNS_DRIVERS; i++) { + if (i && (!(i % 16))) { data += F(","); } + if (!(i % 16)) { data += F("\""); } + state[0] = '-'; + if (XsnsPresent(i)) { state[0] = bitRead(Settings.sensors[i / 32], i % 32) ? '1' : '0'; } + data += String(state); + if (i && (!((i +1) % 16))) { data += F("\""); } + } + data += F("]"); + + return data; +} + +#endif // USE_DEBUG_DRIVER \ No newline at end of file diff --git a/tools/decode-config.html b/tools/decode-config.html index dd24495b7..68968f00d 100644 --- a/tools/decode-config.html +++ b/tools/decode-config.html @@ -1,10 +1,10 @@

decode-config.py

decode-config.py is able to backup and restore Sonoff-Tasmota configuration.

-

In contrast to the Tasmota build-in "Backup/Restore Configuration" function,

+

In comparison with the Tasmota build-in "Backup/Restore Configuration" function decode-config.py

    -
  • decode-config.py uses human readable and editable JSON-format for backup/restore,
  • -
  • decode-config.py can restore previous backuped and changed JSON-format files,
  • -
  • decode-config.py is able to create Tasmota commands based on given configuration
  • +
  • uses human readable and editable JSON-format for backup/restore,
  • +
  • can restore previously backup and changed JSON-format files,
  • +
  • is able to create Tasmota compatible command list with related config parameter

Comparing backup files created by decode-config.py and *.dmp files created by Tasmota "Backup/Restore Configuration":

@@ -38,7 +38,7 @@
-

decode-config.py is able to handle Tasmota configurations for release version starting from 5.10.0 up to now.

+

decode-config.py is compatible with Tasmota version from v5.10.0 up to now.

Content

Prerequisite

    -
  • Python)
    This program is written in Python) so you need to install a python environment (for details see Python Setup and Usage)

    +
  • Python)
    This program is written in Python) so you need to install a working python environment (for details see Python Setup and Usage)

  • Sonoff-Tasmota Firmware with Web-Server enabled:

      -
    • To backup or restore configurations from/to a Sonoff-Tasmota device you need a firmare with enabled web-server in admin mode (command WebServer 2).
    • +
    • To backup or restore configurations from or to a Sonoff-Tasmota device you need a firmare with enabled web-server in admin mode (command WebServer 2). This is the Tasmota default.
    • If using your own compiled firmware be aware to enable the web-server (#define USE_WEBSERVER and #define WEB_SERVER 2).
  • @@ -89,19 +89,20 @@

    .dmp Format

    Configuration data as used by Tasmota "Backup/Restore Configuration" web interface.
    This format is binary and encrypted.

    .json Format

    -

    Configuration data in JSON-format.
    This format is decrypted, human readable and editable and can also be used for the --restore-file command.
    This file will becreated by decode-config.py using --backup-file with --backup-type json parameter (default).

    +

    Configuration data in JSON-format.
    This format is decrypted, human readable and editable and can also be used for the --restore-file parameter.
    This file will be created by decode-config.py using the --backup-file with --backup-type json parameter, this is the default.

    .bin Format

    -

    Configuration data in binary format.
    This format is binary decryptet, editable (e.g. using a hex editor) and can also be used for --restore-file command.
    It will be created by decode-config.py using --backup-file with --backup-type bin.
    Note:
    This file is 4 byte longer than an original .dmp file due to an prefix header at the beginning. The file data starting at address position 4 are containing the same as the struct SYSCFG from Tasmota settings.h in decrypted format.

    +

    Configuration data in binary format.
    This format is binary decryptet, editable (e.g. using a hex editor) and can also be used for --restore-file command.
    It will be created by decode-config.py using --backup-file with --backup-type bin.
    Note:
    The .bin file contains the same information as the original .dmp file from Tasmota "Backup/Restore Configuration" but it is decrpted and 4 byte longer than an original (it is a prefix header at the beginning). .bin file data starting at address 4 contains the same as the struct SYSCFG from Tasmota settings.h in decrypted format.

    File extensions

    -

    decode-config.py uses auto extension as default for backup filenames; you don't need to append extensions to your backup file, it will be selected based on --backup-type argument.
    If you want using your own extension use the --no-extension argument.

    +

    You don't need to append exensions for your file name as decode-config.py uses auto extension as default. The extension will be choose based on file contents and --backup-type parameter. +If you do not want using auto extensions use the --no-extension parameter.

    Usage

    -

    After download don't forget to set exec flag under linux with chmod +x decode-config.py or call the program using python decode-config.py....

    +

    After download don't forget to set the executable flag under linux with chmod +x decode-config.py or call the program using python decode-config.py....

    Basics

    At least pass a source where you want to read the configuration data from using -f <filename> or -d <host>:

    The source can be either

      -
    • a Tasmota device hostname or IP by passing it using the -d <host> arg
    • -
    • or a previously stored Tasmota *.dmp configuration file by passing the filename using -f <filename> arg
    • +
    • a Tasmota device hostname or IP using the -d <host> parameter
    • +
    • a Tasmota *.dmp configuration file using -f <filename> parameter

    Example:

    decode-config.py -d sonoff-4281
    @@ -119,22 +120,22 @@
       ]
     }
     

    Save backup file

    -

    To save the output as backup file --backup-file <filename>, you can use placeholder for Version, Friendlyname and Hostname:

    +

    To save the output as backup file use --backup-file <filename>, you can use placeholder for Version, Friendlyname and Hostname:

    decode-config.py -d sonoff-4281 --backup-file Config_@f_@v
     

    If you have setup a WebPassword within Tasmota, use

    decode-config.py -d sonoff-4281 -p <yourpassword> --backup-file Config_@f_@v
    -

    will create a file like Config_Sonoff_x.x.x.json. Because it is in JSON format, you can read and edit the file with any raw text editor.

    +

    will create a file like Config_Sonoff_6.4.0.json (the part Sonoff and 6.4.0 will choosen related to your device configuration). Because the default backup file format is JSON, you can read and change it with any raw text editor.

    Restore backup file

    -

    Reading back a saved (and possible changed) backup file use the --restore-file <filename> arg. This will read the (changed) configuration data from this file and send it back to the source device or filename.

    +

    Reading back a saved (and possible changed) backup file use the --restore-file <filename> parameter. This will read the (changed) configuration data from this file and send it back to the source device or filename.

    To restore the previously save backup file Config_Sonoff_6.2.1.json to device sonoff-4281 use:

    decode-config.py -d sonoff-4281 --restore-file Config_Sonoff_6.2.1.json
     

    with password set by WebPassword:

    decode-config.py -d sonoff-4281 -p <yourpassword> --restore-file Config_Sonoff_6.2.1.json
     

    Output to screen

    -

    Output to screen is default enabled when calling the program with a source arg but without a backup or restore arg.

    -

    --output arg will force screen output even if you use backup or restore arg.

    +

    To force screen output use the --output parameter.

    +

    Output to screen is default enabled when calling the program with a source parameter (-f or -d) but without any backup or restore parameter.

    JSON output

    -

    The default output format is JSON. You can force JSON output with --output-format json arg.

    +

    The default output format is JSON. You can force JSON output using the --output-format json parameter.

    Example:

    decode-config.py -d sonoff-4281 -c my.conf -x Wifi --output-format json
     
    diff --git a/tools/decode-config.md b/tools/decode-config.md
    index 97978114a..ca70d36e9 100644
    --- a/tools/decode-config.md
    +++ b/tools/decode-config.md
    @@ -1,10 +1,10 @@
     # decode-config.py
     _decode-config.py_ is able to backup and restore Sonoff-Tasmota configuration.
     
    -In contrast to the Tasmota build-in "Backup/Restore Configuration" function,
    -* _decode-config.py_ uses human readable and editable [JSON](http://www.json.org/)-format for backup/restore,
    -* _decode-config.py_ can restore previous backuped and changed [JSON](http://www.json.org/)-format files,
    -* _decode-config.py_ is able to create Tasmota commands based on given configuration
    +In comparison with the Tasmota build-in "Backup/Restore Configuration" function _decode-config.py_
    +* uses human readable and editable [JSON](http://www.json.org/)-format for backup/restore,
    +* can restore previously backup and changed [JSON](http://www.json.org/)-format files,
    +* is able to create Tasmota compatible command list with related config parameter
     
     Comparing backup files created by *decode-config.py* and *.dmp files created by Tasmota "Backup/Restore Configuration":  
     
    @@ -15,7 +15,7 @@ Comparing backup files created by *decode-config.py* and *.dmp files created by
     | Simply editable         |               Yes               |                  No                 |
     | Simply batch processing |               Yes               |                  No                 |
     
    -_decode-config.py_ is able to handle Tasmota configurations for release version starting from 5.10.0 up to now.
    +_decode-config.py_ is compatible with Tasmota version from v5.10.0 up to now.
     
     # Content
     * [Prerequisite](decode-config.md#prerequisite)
    @@ -42,10 +42,10 @@ _decode-config.py_ is able to handle Tasmota configurations for release version
     
     ## Prerequisite
     * [Python](https://en.wikipedia.org/wiki/Python_(programming_language))  
    -  This program is written in [Python](https://en.wikipedia.org/wiki/Python_(programming_language)) so you need to install a python environment (for details see [Python Setup and Usage](https://docs.python.org/2.7/using/index.html))
    +  This program is written in [Python](https://en.wikipedia.org/wiki/Python_(programming_language)) so you need to install a working python environment (for details see [Python Setup and Usage](https://docs.python.org/2.7/using/index.html))
     
     * [Sonoff-Tasmota](https://github.com/arendst/Sonoff-Tasmota) [Firmware](https://github.com/arendst/Sonoff-Tasmota/releases) with Web-Server enabled:
    -  * To backup or restore configurations from/to a Sonoff-Tasmota device you need a firmare with enabled web-server in admin mode (command [WebServer 2](https://github.com/arendst/Sonoff-Tasmota/wiki/Commands#wifi)).
    +  * To backup or restore configurations from or to a Sonoff-Tasmota device you need a firmare with enabled web-server in admin mode (command [WebServer 2](https://github.com/arendst/Sonoff-Tasmota/wiki/Commands#wifi)). This is the Tasmota default.
       * If using your own compiled firmware be aware to enable the web-server (`#define USE_WEBSERVER` and `#define WEB_SERVER 2`).
     
     ## File Types
    @@ -55,28 +55,28 @@ Configuration data as used by Tasmota "Backup/Restore Configuration" web interfa
     This format is binary and encrypted.
     ### .json Format
     Configuration data in [JSON](http://www.json.org/)-format.  
    -This format is decrypted, human readable and editable and can also be used for the `--restore-file` command.  
    -This file will becreated by _decode-config.py_ using `--backup-file` with `--backup-type json` parameter (default).
    +This format is decrypted, human readable and editable and can also be used for the `--restore-file` parameter.  
    +This file will be created by _decode-config.py_ using the `--backup-file` with `--backup-type json` parameter, this is the default.
     ### .bin Format
     Configuration data in binary format.  
     This format is binary decryptet, editable (e.g. using a hex editor) and can also be used for `--restore-file` command.  
     It will be created by _decode-config.py_ using `--backup-file` with `--backup-type bin`.  
     Note:  
    -This file is 4 byte longer than an original .dmp file due to an prefix header at the beginning. The file data starting at address position 4 are containing the same as the **struct SYSCFG** from Tasmota [settings.h](https://github.com/arendst/Sonoff-Tasmota/blob/master/sonoff/settings.h) in decrypted format.
    +The .bin file contains the same information as the original .dmp file from Tasmota "Backup/Restore Configuration" but it is decrpted and  4 byte longer than an original (it is a prefix header at the beginning). .bin file data starting at address 4 contains the same as the **struct SYSCFG** from Tasmota [settings.h](https://github.com/arendst/Sonoff-Tasmota/blob/master/sonoff/settings.h) in decrypted format.
     
     #### File extensions
    -_decode-config.py_ uses auto extension as default for backup filenames; you don't need to append extensions to your backup file, it will be selected based on `--backup-type` argument.  
    -If you want using your own extension use the `--no-extension` argument.
    +You don't need to append exensions for your file name as _decode-config.py_ uses auto extension as default. The extension will be choose based on file contents and `--backup-type` parameter.
    +If you do not want using auto extensions use the `--no-extension` parameter.
     
     ## Usage
    -After download don't forget to set exec flag under linux with `chmod +x decode-config.py` or call the program using `python decode-config.py...`.
    +After download don't forget to set the executable flag under linux with `chmod +x decode-config.py` or call the program using `python decode-config.py...`.
     
     ### Basics
     At least pass a source where you want to read the configuration data from using `-f ` or `-d `:
     
     The source can be either 
    -* a Tasmota device hostname or IP by passing it using the `-d ` arg
    -* or a previously stored Tasmota `*.dmp` configuration file by passing the filename using `-f ` arg
    +* a Tasmota device hostname or IP using the `-d ` parameter
    +* a Tasmota `*.dmp` configuration file using `-f ` parameter
     
     Example:  
     
    @@ -99,7 +99,7 @@ will output a human readable configuration in [JSON](http://www.json.org/)-forma
     
     
     ### Save backup file
    -To save the output as backup file `--backup-file `, you can use placeholder for Version, Friendlyname and Hostname:  
    +To save the output as backup file use `--backup-file `, you can use placeholder for Version, Friendlyname and Hostname:  
     
         decode-config.py -d sonoff-4281 --backup-file Config_@f_@v
         
    @@ -107,10 +107,10 @@ If you have setup a WebPassword within Tasmota, use
     
         decode-config.py -d sonoff-4281 -p  --backup-file Config_@f_@v
     
    -will create a file like `Config_Sonoff_x.x.x.json`. Because it is in JSON format, you can read and edit the file with any raw text editor.
    +will create a file like `Config_Sonoff_6.4.0.json` (the part `Sonoff` and `6.4.0` will choosen related to your device configuration). Because the default backup file format is JSON, you can read and change it with any raw text editor.
     
     ### Restore backup file
    -Reading back a saved (and possible changed) backup file use the `--restore-file ` arg. This will read the (changed) configuration data from this file and send it back to the source device or filename.
    +Reading back a saved (and possible changed) backup file use the `--restore-file ` parameter. This will read the (changed) configuration data from this file and send it back to the source device or filename.
     
     To restore the previously save backup file `Config_Sonoff_6.2.1.json` to device `sonoff-4281` use:  
     
    @@ -121,12 +121,12 @@ with password set by WebPassword:
         decode-config.py -d sonoff-4281 -p  --restore-file Config_Sonoff_6.2.1.json
     
     ### Output to screen
    -Output to screen is default enabled when calling the program with a source arg but without a backup or restore arg.
    +To force screen output use the `--output` parameter.
     
    -`--output` arg will force screen output even if you use backup or restore arg.
    +Output to screen is default enabled when calling the program with a source parameter (-f or -d) but without any backup or restore parameter.
     
     #### JSON output
    -The default output format is JSON. You can force JSON output with `--output-format json` arg.
    +The default output format is [JSON](decode-config.md#-json-format). You can force JSON output using the `--output-format json` parameter.
     
     Example:
     
    diff --git a/tools/decode-config.py b/tools/decode-config.py
    index d1daccf22..0b76ece85 100644
    --- a/tools/decode-config.py
    +++ b/tools/decode-config.py
    @@ -1,11 +1,11 @@
     #!/usr/bin/env python
     # -*- coding: utf-8 -*-
    -VER = '2.1.0015'
    +VER = '2.1.0021'
     
     """
         decode-config.py - Backup/Restore Sonoff-Tasmota configuration data
     
    -    Copyright (C) 2018 Norbert Richter 
    +    Copyright (C) 2019 Norbert Richter 
     
         This program is free software: you can redistribute it and/or modify
         it under the terms of the GNU General Public License as published by
    @@ -814,7 +814,73 @@ Setting_6_3_0_16['flag3'][0].update ({
     Setting_6_4_0_2 = copy.deepcopy(Setting_6_3_0_16)
     Setting_6_4_0_2['flag3'][0].pop('hass_short_discovery_msg',None)
     # ======================================================================
    +Setting_6_4_1_4 = copy.deepcopy(Setting_6_4_0_2)
    +Setting_6_4_1_4['flag3'][0].update ({
    +        'mdns_enabled':             (' 0 and len(obj["StatusLOG"]["SetOption"]) == 2: # old firmware: array consisted only of SetOptions 0..31 and resolution
    +                    break
     
    -                option = obj["StatusLOG"]["SetOption"][o]
    -                i_option = int(option,16)
    -                for i in range(len(a_setoption[f])):
    -                    if (a_setoption[f][i]):
    -                        state = (i_option >> i) & 1
    -                        options.append(str("{0:2d} ({1}) {2}".format(i + p, a_on_off[state], a_setoption[f][i])))
    +                if r == 1:
    +                    if len(register) == 8:            # pre 6.1.1.14: array consisted of SetOptions 0..31, resolution, and SetOptions 50..81
    +                        i += 18                       # adjust option index and skip 2nd register
    +                        continue
    +
    +                    elif len(register) == 36:         # 6.1.1.14: array consists of SetOptions 0..31, SetOptions 32..49, and SetOptions 50..81
    +                        split_register = [int(register[opt*2:opt*2+2],16) for opt in range(18)] # split register into 18 values
    +
    +                        for opt_idx, option in enumerate(opt_group):
    +                            options.append(str("{0:2d} ({1:3d}) {2}".format(i, split_register[opt_idx], option)))
    +                            i += 1
    +
    +                if r in (0, 2): #registers 1 and 3 hold binary values
    +                    for opt_idx, option in enumerate(opt_group):
    +                        i_register = int(register,16)
    +                        state = (i_register >> opt_idx) & 1
    +                        options.append(str("{0:2d} ({1}) {2}".format(i, a_on_off[state], option)))
    +                        i += 1
     
                 print("\nOptions")
    -            for i in range(len(options)):
    -                print("  {}".format(options[i]))
    +            for o in options:
    +                print("  {}".format(o))
     
    -    if ("StatusMEM" in obj):
    -        if ("Features" in obj["StatusMEM"]):
    +    if "StatusMEM" in obj:
    +        if "Features" in obj["StatusMEM"]:
                 features = []
                 for f in range(5):
                     feature = obj["StatusMEM"]["Features"][f]
                     i_feature = int(feature,16)
    -                if (f == 0):
    +                if f == 0:
                         features.append(str("Language LCID = {}".format(i_feature & 0xFFFF)))
                     else:
                         for i in range(len(a_features[f -1])):
    -                        if ((i_feature >> i) & 1):
    +                        if (i_feature >> i) & 1:
                                 features.append(a_features[f -1][i])
     
                 features.sort()
                 print("\nFeatures")
    -            for i in range(len(features)):
    -                print("  {}".format(features[i]))
    +            for f in features:
    +                print("  {}".format(f))
     
     if __name__ == "__main__":
         try:
    diff --git a/tools/fw_server/fw-server.py b/tools/fw_server/fw-server.py
    index 536c76785..4d70f0f5e 100644
    --- a/tools/fw_server/fw-server.py
    +++ b/tools/fw_server/fw-server.py
    @@ -3,7 +3,7 @@
     """
     fw-server.py - firmware server for Sonoff-Tasmota OTA upgrade
     
    -Copyright (C) 2018 Gennaro Tortone
    +Copyright (C) 2019 Gennaro Tortone
     
     This program is free software: you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
    @@ -24,7 +24,8 @@ Requirements:
        - pip install netifaces flask
     
     Instructions:
    -    Copy Sonoff-Tasmota firmware binary files in 'fw' directory.
    +    Copy Sonoff-Tasmota firmware binary files in 'fw' directory or
    +    specify a different directory with -f parameter.
         A set of prebuilt files can be downloaded by Sonoff-Tasmota release page:
             https://github.com/arendst/Sonoff-Tasmota/releases
     
    @@ -51,7 +52,6 @@ from sys import exit
     from flask import Flask, send_file
     import netifaces as ni
     
    -
     usage = "usage: fw-server {-d | -i} arg"
     
     parser = OptionParser(usage)
    @@ -59,6 +59,8 @@ parser.add_option("-d", "--dev", action="store", type="string",
                       dest="netdev", default="eth0", help="network interface (default: eth0)")
     parser.add_option("-i", "--ip", action="store", type="string",
                       dest="ip", help="IP address to bind")
    +parser.add_option("-f", "--fwdir", action="store", type="string",
    +                  dest="fwdir", help="firmware absolute path directory (default: fw/ directory)")
     (options, args) = parser.parse_args()
     
     netip = None
    @@ -72,14 +74,24 @@ if options.ip is None:
     else:
         netip = options.ip
     
    +if options.fwdir is None:
    +    fwdir = os.path.dirname(os.path.realpath(__file__)) + "/fw/"
    +else:
    +    if os.path.isdir(options.fwdir):
    +        fwdir = options.fwdir
    +    else:
    +        print("E: directory " + options.fwdir + " not available")
    +        exit(1)
    +
    +print(" * Directory: " + fwdir)
     
     app = Flask(__name__)
     
     
     @app.route('/')
     def fw(filename):
    -    if os.path.exists("fw/" + str(filename)):
    -        return send_file("fw/" + str(filename),
    +    if os.path.exists(fwdir + str(filename)):
    +        return send_file(fwdir + str(filename),
                              attachment_filename=filename,
                              mimetype='application/octet-stream')
     
    
    From 7a3e8a510816ef9d77e46f9e785bc7d410bf144c Mon Sep 17 00:00:00 2001
    From: Theo Arends <11044339+arendst@users.noreply.github.com>
    Date: Wed, 13 Mar 2019 18:45:54 +0100
    Subject: [PATCH 006/428] Prep for release 6.5
    
    Prep for release 6.5
    ---
     RELEASENOTES.md | 88 +++++++++++++++++++++++++++++++++++++++++++++++--
     platformio.ini  | 49 +++++++++++++++++----------
     2 files changed, 118 insertions(+), 19 deletions(-)
    
    diff --git a/RELEASENOTES.md b/RELEASENOTES.md
    index b33fd51f9..d8cec96f2 100644
    --- a/RELEASENOTES.md
    +++ b/RELEASENOTES.md
    @@ -82,7 +82,7 @@ Module            | Description
     58 PS-16-DZ       | PS-16-DZ  Wifi dimmer for Incandescent Lights and Led
     59 Teckin US      | Teckin US and ZooZee SA102 Wifi Smart Switch with Energy Monitoring
     60 Manzoku strip  | Manzoku Wifi Smart Power Strip with four Relays
    -61 OBI Socket 2   | OBI 2 Wifi Smart Socket 
    +61 OBI Socket 2   | OBI 2 Wifi Smart Socket
     62 YTF IR Bridge  | YTF Infra Red Wifi Bridge
     63 Digoo DG-SP202 | Digoo DG-SP202 Dual Wifi Smart Switch with Energy Monitoring
     64 KA10           | Smanergy KA10 Wifi Smart Wall Switch with Energy Monitoring
    @@ -197,4 +197,88 @@ The following binary downloads have been compiled with ESP8266/Arduino library c
     ## Changelog
     Version 6.5.0 20190315
     
    - * Tbd
    + * Remove command SetOption14 as it has been superseded by command Interlock
    + * Remove command SetOption63 as it has been superseded by command Interlock
    + * Remove command SetOption35 0-255 for mDNS start-up delay (#4793)
    + * Change webserver content handling from single String to small Chunks increasing RAM
    + * Change logging message handling
    + * Change code use of boolean to bool and byte to uint8_t
    + * Change code uint8_t flags to bool flags
    + * Change sonoff_template.h layout regarding optional module flags like ADC0
    + * Change button driver making it modular
    + * Change sonoff_template.h module lay-out by removing non-configurable GPIOs
    + * Change switch driver making it modular and introduce input filter (#4665, #4724)
    + * Change switch input detection by optimizing switch debounce (#4724)
    + * Change web authentication (#4865)
    + * Change image name BE_MINIMAL to FIRMWARE_MINIMAL (#5106)
    + * Change image names USE_xyz to FIRMWARE_xyz (#5106)
    + * Change GUI weblog solving possible empty screens (#5154)
    + * Change PN532 support from I2C to Serial for more stability (#5162)
    + * Change template update by removing possibility to add user module config keeping template as defined (#5222)
    + * Fix most compiler warnings
    + * Fix IR local echo
    + * Fix Display exception 28 when JSON value is NULL received
    + * Fix HLW8012, HJL01 and BL0937 based energy sensors low Power (below 10W) measurement regression from 6.4.1.6
    + * Fix epaper driver (#4785)
    + * Fix Home Assistant Sensor Discovery Software Watchdog restart (#4831, #4988)
    + * Fix allowable MAX_RULE_VARS to 16 (#4933)
    + * Fix mDNS addService (#4938, #4951)
    + * Fix Hass discovery of MHZ19(B) sensors (#4992)
    + * Fix some exceptions and watchdogs due to lack of stack space (#5215)
    + * Fix GUI wifi password acception starting with asteriks (*) (#5231, #5242)
    + * Fix command WebSend intermittent results (#5273, #5304)
    + * Fix additional characters in fallbacktopic, hostname and mqttclient on core 2.5.0 (#5359, #5417)
    + * Fix Energy TotalStartTime when commands EnergyReset0 and/or EnergyReset3 used (#5373)
    + * Fix DS18S20 temperature calculation (#5375)
    + * Fix float calculations in range from 0 to -1 (#5386)
    + * Fix exception on GUI Configure Logging and Configure Other (#5424)
    + * Add commands PowerCal, VoltageCal and CurrentCal for HLW8012, HJL01 and BL0937 based energy sensors
    + * Add Power status functionality to LED2 when configured leaving LED1 for Link status indication
    + * Add support for Smanergy KA10 Smart Wall Socket with Energy monitoring
    + * Add SerialBridge command SSerialSend5 \
    + * Add SetOption32 until SetOption49 diagnostic information to Status 3 report as replacement for second property value in SetOption property name
    + * Add Resolution property to Status 3 report providing previous SetOption second value property
    + * Add user configuration of HLW8012 and HJL-01/BL0937 Energy Monitoring as used in Sonoff S31, Pow Ra and many Tuya based devices
    + * Add user configuration of MCP39F501 Energy Monitoring as used in Shelly2
    + * Add property LinkCount to state and status 11 message representing number of Wifi Link re-connections
    + * Add property MqttCount to status 6 message representing number of Mqtt re-connections
    + * Add property Downtime to state and status 11 message representing the duration of wifi connection loss
    + * Add support for commands in sensor drivers
    + * Add (S)SerialSend3 escape sequence \x to allow hexadecimal byte value (#3560, #4947)
    + * Add command SetOption36 to control boot loop default restoration (#4645, #5063)
    + * Add define DS18B20_INTERNAL_PULLUP to select internal input pullup when only one DS18B20 sensor is connected eliminating external resistor (#4738)
    + * Add variable %timestamp% to rules (#4749)
    + * Add support for MAX31855 K-Type thermocouple sensor using softSPI (#4764)
    + * Add button control when no relay configured (#4682)
    + * Add support for Near Field Communication (NFC) controller PN532 using I2C (#4791)
    + * Add command SetOption55 0/1 to disable/enable mDNS (#4793)
    + * Add 4 seconds startup delay to button control (#4829)
    + * Add support for OBI Power Socket 2 (#4829)
    + * Add support for YTF IR Bridge (#4855)
    + * Add support for Mi LED Desk Lamp with rotary switch (#4887)
    + * Add support for Digoo DG-SP202 Smart Socket with Energy monitoring (#4891)
    + * Add support for MAX44009 Ambient Light sensor (#4907)
    + * Add split interlock (#4910)
    + * Add support for inverted buttons and inverted buttons without pullup (#4914)
    + * Add core version conditional compile options to provided PWM files (#4917)
    + * Add support for Luminea ZX2820 Smart Socket with Energy monitoring (#4921)
    + * Add define MDNS_ENABLE to control initial mDNS state (#4923)
    + * Add command Interlock 0 / 1 / 1,2 3,4 .. to control interlock ON/OFF and add up to 8 relays in 1 to 4 interlock groups (#5014)
    + * Add resiliency to saved Settings (#5065)
    + * Add support for multiple ADS1115 I2C devices (#5083)
    + * Add rule support for "==", "!=" ">=" and "<=" (#5122)
    + * Add MHZ19 Temperature as Domoticz Temperature selection (#5128)
    + * Add command SerialDelimiter 128 to filter reception of only characters between ASCII 32 and 127 (#5131)
    + * Add Hass status sensor (#5139)
    + * Add status message to former declined group commands (#5145)
    + * Add support for online template change using command Template or GUI Configure Other (#5177)
    + * Add parameter CFG_HOLDER to status 1 message (#5206)
    + * Add rule expression enabled by define USE_EXPRESSION in my_user_config.h (#5210)
    + * Add Configure Template menu option to GUI (#5222)
    + * Add option WifiConfig 7 to allow reset of device in AP mode without admin password (#5297)
    + * Add command SetOption62 0/1 to disable retain on Button or Swith hold messages (#5299)
    + * Add command SetOption37 for RGBCW color mapping (#5326)
    + * Add Korean language translations (#5344)
    + * Add command Template 255 to copy module configuration over to current active template and store as user template named Merged (#5371)
    + * Add 0x to IRRemote (SetOption29) and RCSwitch (SetOption28) received hexadecimal data (#5431)
    + * Add support for sensor SCD30 (#5434)
    diff --git a/platformio.ini b/platformio.ini
    index 2546e6864..b12b3ad00 100644
    --- a/platformio.ini
    +++ b/platformio.ini
    @@ -29,6 +29,7 @@ src_dir = sonoff
     ;env_default = sonoff-HE
     ;env_default = sonoff-HU
     ;env_default = sonoff-IT
    +;env_default = sonoff-KO
     ;env_default = sonoff-NL
     ;env_default = sonoff-PL
     ;env_default = sonoff-PT
    @@ -65,8 +66,8 @@ build_flags               = ${esp82xx_defaults.build_flags}
                                 -DVTABLES_IN_FLASH
     
     [core_2_5_0]
    -; *** Esp8266 core for Arduino version Core 2.5.0 beta tested for Tasmota
    -platform                  = https://github.com/Jason2866/platform-espressif8266.git#Tasmota
    +; *** Esp8266 core for Arduino version 2.5.0
    +platform                  = espressif8266@2.0.1
     build_flags               = ${esp82xx_defaults.build_flags}
                                 -Wl,-Teagle.flash.1m.ld
     ; lwIP 1.4 (Default)
    @@ -81,7 +82,7 @@ build_flags               = ${esp82xx_defaults.build_flags}
                                 -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH
                                 -DVTABLES_IN_FLASH
                                 -fno-exceptions
    -                            -lstdc++-nox
    +                            -lstdc++
     
     [core_stage]
     ; *** Esp8266 core for Arduino version latest beta
    @@ -107,10 +108,10 @@ build_flags               = ${esp82xx_defaults.build_flags}
     ; enable one option set -> No exception recommended
     ; No exception code in firmware
                                 -fno-exceptions
    -                            -lstdc++-nox
    +                            -lstdc++
     ; Exception code in firmware /needs much space! 90k
     ;                           -fexceptions
    -;                           -lstdc++
    +;                           -lstdc++-exc
     
     [core_active]
     ; Select one core set for platform and build_flags
    @@ -130,12 +131,12 @@ board_build.flash_mode    = dout
     
     platform                  = ${core_active.platform}
     build_flags               = ${core_active.build_flags}
    -;                            -DUSE_CLASSIC
    -;                            -DBE_MINIMAL
    -;                            -DUSE_SENSORS
    -;                            -DUSE_BASIC
    -;                            -DUSE_KNX_NO_EMULATION
    -;                            -DUSE_DISPLAYS
    +;                            -DFIRMWARE_CLASSIC
    +;                            -DFIRMWARE_MINIMAL
    +;                            -DFIRMWARE_SENSORS
    +;                            -DFIRMWARE_BASIC
    +;                            -DFIRMWARE_KNX_NO_EMULATION
    +;                            -DFIRMWARE_DISPLAYS
     ;                            -DUSE_CONFIG_OVERRIDE
     
     ; *** Fix espressif8266@1.7.0 induced undesired all warnings
    @@ -184,7 +185,7 @@ board = ${common.board}
     board_build.flash_mode = ${common.board_build.flash_mode}
     board_build.f_cpu = ${common.board_build.f_cpu}
     build_unflags = ${common.build_unflags}
    -build_flags = ${common.build_flags} -DBE_MINIMAL
    +build_flags = ${common.build_flags} -DFIRMWARE_MINIMAL
     monitor_speed = ${common.monitor_speed}
     upload_port = ${common.upload_port}
     upload_resetmethod = ${common.upload_resetmethod}
    @@ -198,7 +199,7 @@ board = ${common.board}
     board_build.flash_mode = ${common.board_build.flash_mode}
     board_build.f_cpu = ${common.board_build.f_cpu}
     build_unflags = ${common.build_unflags}
    -build_flags = ${common.build_flags} -DUSE_BASIC
    +build_flags = ${common.build_flags} -DFIRMWARE_BASIC
     monitor_speed = ${common.monitor_speed}
     upload_port = ${common.upload_port}
     upload_resetmethod = ${common.upload_resetmethod}
    @@ -212,7 +213,7 @@ board = ${common.board}
     board_build.flash_mode = ${common.board_build.flash_mode}
     board_build.f_cpu = ${common.board_build.f_cpu}
     build_unflags = ${common.build_unflags}
    -build_flags = ${common.build_flags} -DUSE_CLASSIC
    +build_flags = ${common.build_flags} -DFIRMWARE_CLASSIC
     monitor_speed = ${common.monitor_speed}
     upload_port = ${common.upload_port}
     upload_resetmethod = ${common.upload_resetmethod}
    @@ -226,7 +227,7 @@ board = ${common.board}
     board_build.flash_mode = ${common.board_build.flash_mode}
     board_build.f_cpu = ${common.board_build.f_cpu}
     build_unflags = ${common.build_unflags}
    -build_flags = ${common.build_flags} -DUSE_KNX_NO_EMULATION
    +build_flags = ${common.build_flags} -DFIRMWARE_KNX_NO_EMULATION
     monitor_speed = ${common.monitor_speed}
     upload_port = ${common.upload_port}
     upload_resetmethod = ${common.upload_resetmethod}
    @@ -240,7 +241,7 @@ board = ${common.board}
     board_build.flash_mode = ${common.board_build.flash_mode}
     board_build.f_cpu = ${common.board_build.f_cpu}
     build_unflags = ${common.build_unflags}
    -build_flags = ${common.build_flags} -DUSE_SENSORS
    +build_flags = ${common.build_flags} -DFIRMWARE_SENSORS
     monitor_speed = ${common.monitor_speed}
     upload_port = ${common.upload_port}
     upload_resetmethod = ${common.upload_resetmethod}
    @@ -254,7 +255,7 @@ board = ${common.board}
     board_build.flash_mode = ${common.board_build.flash_mode}
     board_build.f_cpu = ${common.board_build.f_cpu}
     build_unflags = ${common.build_unflags}
    -build_flags = ${common.build_flags} -DUSE_DISPLAYS
    +build_flags = ${common.build_flags} -DFIRMWARE_DISPLAYS
     monitor_speed = ${common.monitor_speed}
     upload_port = ${common.upload_port}
     upload_resetmethod = ${common.upload_resetmethod}
    @@ -415,6 +416,20 @@ upload_resetmethod = ${common.upload_resetmethod}
     upload_speed = ${common.upload_speed}
     extra_scripts = ${common.extra_scripts}
     
    +[env:sonoff-KO]
    +platform = ${common.platform}
    +framework = ${common.framework}
    +board = ${common.board}
    +board_build.flash_mode = ${common.board_build.flash_mode}
    +board_build.f_cpu = ${common.board_build.f_cpu}
    +build_unflags = ${common.build_unflags}
    +build_flags = ${common.build_flags} -DMY_LANGUAGE=ko-KO
    +monitor_speed = ${common.monitor_speed}
    +upload_port = ${common.upload_port}
    +upload_resetmethod = ${common.upload_resetmethod}
    +upload_speed = ${common.upload_speed}
    +extra_scripts = ${common.extra_scripts}
    +
     [env:sonoff-NL]
     platform = ${common.platform}
     framework = ${common.framework}
    
    From d9aa3102a71ecb54531d0b8c69cf6c185929f8f5 Mon Sep 17 00:00:00 2001
    From: Theo Arends <11044339+arendst@users.noreply.github.com>
    Date: Thu, 14 Mar 2019 17:21:45 +0100
    Subject: [PATCH 007/428] Prep for release 6.5
    
    Prep for release 6.5
    ---
     RELEASENOTES.md                   | 94 ++++++++++++++-----------------
     sonoff/_changelog.ino             |  3 +-
     sonoff/settings.h                 | 16 +++---
     sonoff/sonoff.ino                 | 33 +++++------
     sonoff/support.ino                |  2 +-
     sonoff/xdrv_01_webserver.ino      | 38 +++++--------
     sonoff/xdrv_12_home_assistant.ino |  2 +-
     7 files changed, 82 insertions(+), 106 deletions(-)
    
    diff --git a/RELEASENOTES.md b/RELEASENOTES.md
    index d8cec96f2..7089a956c 100644
    --- a/RELEASENOTES.md
    +++ b/RELEASENOTES.md
    @@ -195,35 +195,27 @@ The following binary downloads have been compiled with ESP8266/Arduino library c
     | USE_DISPLAY                    | - | - | - | - | - | - |
     
     ## Changelog
    -Version 6.5.0 20190315
    -
    - * Remove command SetOption14 as it has been superseded by command Interlock
    - * Remove command SetOption63 as it has been superseded by command Interlock
    +Version 6.5.0 20190319
    + * Remove commands SetOption14 and SetOption63 as it has been superseded by command Interlock
      * Remove command SetOption35 0-255 for mDNS start-up delay (#4793)
      * Change webserver content handling from single String to small Chunks increasing RAM
    - * Change logging message handling
      * Change code use of boolean to bool and byte to uint8_t
      * Change code uint8_t flags to bool flags
      * Change sonoff_template.h layout regarding optional module flags like ADC0
    - * Change button driver making it modular
      * Change sonoff_template.h module lay-out by removing non-configurable GPIOs
    + * Change button driver making it modular
      * Change switch driver making it modular and introduce input filter (#4665, #4724)
      * Change switch input detection by optimizing switch debounce (#4724)
      * Change web authentication (#4865)
    - * Change image name BE_MINIMAL to FIRMWARE_MINIMAL (#5106)
    - * Change image names USE_xyz to FIRMWARE_xyz (#5106)
    - * Change GUI weblog solving possible empty screens (#5154)
    - * Change PN532 support from I2C to Serial for more stability (#5162)
    - * Change template update by removing possibility to add user module config keeping template as defined (#5222)
    + * Change image name BE_MINIMAL to FIRMWARE_MINIMAL and USE_xyz to FIRMWARE_xyz (#5106)
    + * Change GUI weblog from XML to plain text solving possible empty screens (#5154)
      * Fix most compiler warnings
    - * Fix IR local echo
      * Fix Display exception 28 when JSON value is NULL received
    - * Fix HLW8012, HJL01 and BL0937 based energy sensors low Power (below 10W) measurement regression from 6.4.1.6
      * Fix epaper driver (#4785)
    - * Fix Home Assistant Sensor Discovery Software Watchdog restart (#4831, #4988)
    + * Fix HAss Sensor Discovery Software Watchdog restart (#4831, #4988)
      * Fix allowable MAX_RULE_VARS to 16 (#4933)
      * Fix mDNS addService (#4938, #4951)
    - * Fix Hass discovery of MHZ19(B) sensors (#4992)
    + * Fix HAss discovery of MHZ19(B) sensors (#4992)
      * Fix some exceptions and watchdogs due to lack of stack space (#5215)
      * Fix GUI wifi password acception starting with asteriks (*) (#5231, #5242)
      * Fix command WebSend intermittent results (#5273, #5304)
    @@ -233,52 +225,50 @@ Version 6.5.0 20190315
      * Fix float calculations in range from 0 to -1 (#5386)
      * Fix exception on GUI Configure Logging and Configure Other (#5424)
      * Add commands PowerCal, VoltageCal and CurrentCal for HLW8012, HJL01 and BL0937 based energy sensors
    - * Add Power status functionality to LED2 when configured leaving LED1 for Link status indication
    - * Add support for Smanergy KA10 Smart Wall Socket with Energy monitoring
    - * Add SerialBridge command SSerialSend5 \
    - * Add SetOption32 until SetOption49 diagnostic information to Status 3 report as replacement for second property value in SetOption property name
    - * Add Resolution property to Status 3 report providing previous SetOption second value property
    - * Add user configuration of HLW8012 and HJL-01/BL0937 Energy Monitoring as used in Sonoff S31, Pow Ra and many Tuya based devices
    - * Add user configuration of MCP39F501 Energy Monitoring as used in Shelly2
    - * Add property LinkCount to state and status 11 message representing number of Wifi Link re-connections
    - * Add property MqttCount to status 6 message representing number of Mqtt re-connections
    - * Add property Downtime to state and status 11 message representing the duration of wifi connection loss
    - * Add support for commands in sensor drivers
    - * Add (S)SerialSend3 escape sequence \x to allow hexadecimal byte value (#3560, #4947)
    + * Add command SerialDelimiter 128 to filter reception of only characters between ASCII 32 and 127 (#5131)
    + * Add command SSerialSend5 \ to SerialBridge
    + * Add command Interlock 0 / 1 / 1,2 3,4 .. to control interlock ON/OFF and add up to 8 relays in 1 to 4 interlock groups (#4910, #5014)
    + * Add command Template 255 to copy module configuration over to current active template and store as user template named Merged (#5371)
    + * Add command WifiConfig 7 to allow reset of device in AP mode without admin password (#5297)
      * Add command SetOption36 to control boot loop default restoration (#4645, #5063)
    - * Add define DS18B20_INTERNAL_PULLUP to select internal input pullup when only one DS18B20 sensor is connected eliminating external resistor (#4738)
    - * Add variable %timestamp% to rules (#4749)
    + * Add command SetOption37 for RGBCW color mapping (#5326)
    + * Add command SetOption55 0/1 and define MDNS_ENABLE to disable/enable mDNS (#4793, #4923)
    + * Add command SetOption62 0/1 to disable retain on Button or Switch hold messages (#5299)
    + * Add support for Smanergy KA10 Smart Wall Socket with Energy monitoring
    + * Add support for commands in sensor drivers
      * Add support for MAX31855 K-Type thermocouple sensor using softSPI (#4764)
    - * Add button control when no relay configured (#4682)
    - * Add support for Near Field Communication (NFC) controller PN532 using I2C (#4791)
    - * Add command SetOption55 0/1 to disable/enable mDNS (#4793)
    - * Add 4 seconds startup delay to button control (#4829)
    + * Add support for Near Field Communication (NFC) controller PN532 using Serial (#4791, #5162)
      * Add support for OBI Power Socket 2 (#4829)
      * Add support for YTF IR Bridge (#4855)
      * Add support for Mi LED Desk Lamp with rotary switch (#4887)
      * Add support for Digoo DG-SP202 Smart Socket with Energy monitoring (#4891)
      * Add support for MAX44009 Ambient Light sensor (#4907)
    - * Add split interlock (#4910)
      * Add support for inverted buttons and inverted buttons without pullup (#4914)
    - * Add core version conditional compile options to provided PWM files (#4917)
      * Add support for Luminea ZX2820 Smart Socket with Energy monitoring (#4921)
    - * Add define MDNS_ENABLE to control initial mDNS state (#4923)
    - * Add command Interlock 0 / 1 / 1,2 3,4 .. to control interlock ON/OFF and add up to 8 relays in 1 to 4 interlock groups (#5014)
    - * Add resiliency to saved Settings (#5065)
      * Add support for multiple ADS1115 I2C devices (#5083)
    - * Add rule support for "==", "!=" ">=" and "<=" (#5122)
    - * Add MHZ19 Temperature as Domoticz Temperature selection (#5128)
    - * Add command SerialDelimiter 128 to filter reception of only characters between ASCII 32 and 127 (#5131)
    - * Add Hass status sensor (#5139)
    - * Add status message to former declined group commands (#5145)
      * Add support for online template change using command Template or GUI Configure Other (#5177)
    - * Add parameter CFG_HOLDER to status 1 message (#5206)
    - * Add rule expression enabled by define USE_EXPRESSION in my_user_config.h (#5210)
    - * Add Configure Template menu option to GUI (#5222)
    - * Add option WifiConfig 7 to allow reset of device in AP mode without admin password (#5297)
    - * Add command SetOption62 0/1 to disable retain on Button or Swith hold messages (#5299)
    - * Add command SetOption37 for RGBCW color mapping (#5326)
    - * Add Korean language translations (#5344)
    - * Add command Template 255 to copy module configuration over to current active template and store as user template named Merged (#5371)
    - * Add 0x to IRRemote (SetOption29) and RCSwitch (SetOption28) received hexadecimal data (#5431)
    + * Add support for Korean language translations (#5344)
      * Add support for sensor SCD30 (#5434)
    + * Add parameter CFG_HOLDER to status 1 message (#5206)
    + * Add SetOption32 until SetOption49 diagnostic information to Status 3 report as replacement for second property value in SetOption property name
    + * Add Resolution property to Status 3 report providing previous SetOption second value property
    + * Add property MqttCount to status 6 message representing number of Mqtt re-connections
    + * Add property LinkCount to state and status 11 message representing number of Wifi Link re-connections
    + * Add property Downtime to state and status 11 message representing the duration of wifi connection loss
    + * Add variable %timestamp% to rules (#4749)
    + * Add rule support for "==", "!=" ">=" and "<=" (#5122)
    + * Add rule expression enabled by define USE_EXPRESSION in my_user_config.h (#5210)
    + * Add Power status functionality to LED2 when configured leaving LED1 for Link status indication
    + * Add user configuration of HLW8012 and HJL-01/BL0937 Energy Monitoring as used in Sonoff Pow and many Tuya based devices
    + * Add user configuration of MCP39F501 Energy Monitoring as used in Shelly2
    + * Add online template configuration using both commands and Configure Template menu option in GUI
    + * Add (S)SerialSend3 escape sequence \x to allow hexadecimal byte value (#3560, #4947)
    + * Add define DS18B20_INTERNAL_PULLUP to select internal input pullup when only one DS18B20 sensor is connected eliminating external resistor (#4738)
    + * Add button control when no relay configured (#4682)
    + * Add startup delay of 4 seconds to button control (#4829)
    + * Add core version conditional compile options to provided PWM files (#4917)
    + * Add resiliency to saved Settings (#5065)
    + * Add MHZ19 Temperature as Domoticz Temperature selection (#5128)
    + * Add HAss status sensor (#5139)
    + * Add status message to former declined group commands (#5145)
    + * Add 0x to IRRemote (SetOption29) and RCSwitch (SetOption28) received hexadecimal data (#5431)
    diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino
    index 0ef542e5e..58ead17cd 100644
    --- a/sonoff/_changelog.ino
    +++ b/sonoff/_changelog.ino
    @@ -1,4 +1,5 @@
    -/* 6.5.0 20190315
    +/* 6.5.0 20190319
    + * New Release
      *
      * 6.4.1.21 20190309
      * Fix exception on GUI Configure Logging and Configure Other (#5424)
    diff --git a/sonoff/settings.h b/sonoff/settings.h
    index 342ff1612..5a7827365 100644
    --- a/sonoff/settings.h
    +++ b/sonoff/settings.h
    @@ -200,8 +200,8 @@ struct SYSCFG {
       uint8_t       seriallog_level;           // 09E
       uint8_t       sta_config;                // 09F
       uint8_t       sta_active;                // 0A0
    -  char          sta_ssid[2][33];           // 0A1 - Keep together with sta_pwd as being copied as one chunck with reset 4/5
    -  char          sta_pwd[2][65];            // 0E3 - Keep together with sta_ssid as being copied as one chunck with reset 4/5
    +  char          sta_ssid[2][33];           // 0A1 - Keep together with sta_pwd as being copied as one chunck with reset 5
    +  char          sta_pwd[2][65];            // 0E3 - Keep together with sta_ssid as being copied as one chunck with reset 5
       char          hostname[33];              // 165
       char          syslog_host[33];           // 186
       uint8_t       rule_stop;                 // 1A7
    @@ -213,12 +213,12 @@ struct SYSCFG {
     
       uint8_t       free_1D5[20];              // 1D5  Free since 5.12.0e
     
    -  char          mqtt_host[33];             // 1E9
    -  uint16_t      mqtt_port;                 // 20A
    -  char          mqtt_client[33];           // 20C
    -  char          mqtt_user[33];             // 22D
    -  char          mqtt_pwd[33];              // 24E
    -  char          mqtt_topic[33];            // 26F
    +  char          mqtt_host[33];             // 1E9 - Keep together with below as being copied as one chunck with reset 6
    +  uint16_t      mqtt_port;                 // 20A - Keep together
    +  char          mqtt_client[33];           // 20C - Keep together
    +  char          mqtt_user[33];             // 22D - Keep together
    +  char          mqtt_pwd[33];              // 24E - Keep together
    +  char          mqtt_topic[33];            // 26F - Keep together with above items as being copied as one chunck with reset 6
       char          button_topic[33];          // 290
       char          mqtt_grptopic[33];         // 2B1
       uint8_t       display_model;             // 2D2
    diff --git a/sonoff/sonoff.ino b/sonoff/sonoff.ino
    index 82c306c90..39f180c0b 100755
    --- a/sonoff/sonoff.ino
    +++ b/sonoff/sonoff.ino
    @@ -2082,31 +2082,26 @@ void Every250mSeconds(void)
         }
         if (restart_flag && (backlog_pointer == backlog_index)) {
           if ((214 == restart_flag) || (215 == restart_flag) || (216 == restart_flag)) {
    -        char storage[sizeof(Settings.sta_ssid) + sizeof(Settings.sta_pwd)];
    -        char storage_mqtt_host[sizeof(Settings.mqtt_host)];
    -        uint16_t storage_mqtt_port;
    -        char storage_mqtt_user[sizeof(Settings.mqtt_user)];
    -        char storage_mqtt_pwd[sizeof(Settings.mqtt_pwd)];
    -        char storage_mqtt_topic[sizeof(Settings.mqtt_topic)];
    -        memcpy(storage, Settings.sta_ssid, sizeof(storage));  // Backup current SSIDs and Passwords
    +        char storage_wifi[sizeof(Settings.sta_ssid) +
    +                          sizeof(Settings.sta_pwd)];
    +        char storage_mqtt[sizeof(Settings.mqtt_host) +
    +                          sizeof(Settings.mqtt_port) +
    +                          sizeof(Settings.mqtt_client) +
    +                          sizeof(Settings.mqtt_user) +
    +                          sizeof(Settings.mqtt_pwd) +
    +                          sizeof(Settings.mqtt_topic)];
    +        memcpy(storage_wifi, Settings.sta_ssid, sizeof(storage_wifi));     // Backup current SSIDs and Passwords
             if (216 == restart_flag) {
    -          memcpy(storage_mqtt_host, Settings.mqtt_host, sizeof(Settings.mqtt_host));
    -          storage_mqtt_port = Settings.mqtt_port;
    -          memcpy(storage_mqtt_user, Settings.mqtt_user, sizeof(Settings.mqtt_user));
    -          memcpy(storage_mqtt_pwd, Settings.mqtt_pwd, sizeof(Settings.mqtt_pwd));
    -          memcpy(storage_mqtt_topic, Settings.mqtt_topic, sizeof(Settings.mqtt_topic));
    +          memcpy(storage_mqtt, Settings.mqtt_host, sizeof(storage_mqtt));  // Backup mqtt host, port, client, username and password
             }
             if ((215 == restart_flag) || (216 == restart_flag)) {
               SettingsErase(0);  // Erase all flash from program end to end of physical flash
             }
             SettingsDefault();
    -        memcpy(Settings.sta_ssid, storage, sizeof(storage));  // Restore current SSIDs and Passwords
    -        if (216 == restart_flag) {                            // Restore the mqtt host, port, username and password
    -          memcpy(Settings.mqtt_host, storage_mqtt_host, sizeof(Settings.mqtt_host));
    -          Settings.mqtt_port = storage_mqtt_port;
    -          memcpy(Settings.mqtt_user, storage_mqtt_user, sizeof(Settings.mqtt_user));
    -          memcpy(Settings.mqtt_pwd, storage_mqtt_pwd, sizeof(Settings.mqtt_pwd));
    -          memcpy(Settings.mqtt_topic, storage_mqtt_topic, sizeof(Settings.mqtt_topic));
    +        memcpy(Settings.sta_ssid, storage_wifi, sizeof(storage_wifi));     // Restore current SSIDs and Passwords
    +        if (216 == restart_flag) {
    +          memcpy(Settings.mqtt_host, storage_mqtt, sizeof(storage_mqtt));  // Restore the mqtt host, port, client, username and password
    +          strlcpy(Settings.mqtt_client, MQTT_CLIENT_ID, sizeof(Settings.mqtt_client));  // Set client to default
             }
             restart_flag = 2;
           }
    diff --git a/sonoff/support.ino b/sonoff/support.ino
    index fc2a3d853..e6068fb77 100644
    --- a/sonoff/support.ino
    +++ b/sonoff/support.ino
    @@ -1278,7 +1278,7 @@ void AddLog_P2(uint8_t loglevel, PGM_P formatP, ...)
     {
       va_list arg;
       va_start(arg, formatP);
    -  int len = vsnprintf_P(log_data, sizeof(log_data), formatP, arg);
    +  vsnprintf_P(log_data, sizeof(log_data), formatP, arg);
       va_end(arg);
     
       AddLog(loglevel);
    diff --git a/sonoff/xdrv_01_webserver.ino b/sonoff/xdrv_01_webserver.ino
    index 9d96c480c..f243b1b11 100644
    --- a/sonoff/xdrv_01_webserver.ino
    +++ b/sonoff/xdrv_01_webserver.ino
    @@ -272,7 +272,7 @@ const char HTTP_HEAD_STYLE3[] PROGMEM =
     #else
       "

    %s " D_MODULE "

    " #endif - "

    %s

    %s
"; + "

%s

"; const char HTTP_MSG_SLIDER1[] PROGMEM = "
" D_COLDLIGHT "" D_WARMLIGHT "
" @@ -566,27 +566,6 @@ bool HttpCheckPriviledgedAccess(bool autorequestauth = true) return true; } -String WSNetworkInfo(void) -{ - String info = ""; - if (Settings.flag3.gui_hostname_ip) { - uint8_t more_ips = 0; - info += F("

"); info += my_hostname; - if (mdns_begun) { info += F(".local"); } - info += F(" ("); - if (static_cast(WiFi.localIP()) != 0) { - info += WiFi.localIP().toString(); - more_ips++; - } - if (static_cast(WiFi.softAPIP()) != 0) { - if (more_ips) { info += F(", "); } - info += WiFi.softAPIP().toString(); - } - info += F(")

"); - } - return info; -} - void WSHeaderSend(void) { WebServer->sendHeader(F("Cache-Control"), F("no-cache, no-store, must-revalidate")); @@ -701,7 +680,18 @@ void WSContentSendStyle_P(const char* style) WSContentSend_P(HTTP_HEAD_STYLE1); WSContentSend_P(HTTP_HEAD_STYLE2); WSContentSend_P(style); - WSContentSend_P(HTTP_HEAD_STYLE3, ModuleName().c_str(), Settings.friendlyname[0], WSNetworkInfo().c_str()); + WSContentSend_P(HTTP_HEAD_STYLE3, ModuleName().c_str(), Settings.friendlyname[0]); + if (Settings.flag3.gui_hostname_ip) { + bool lip = (static_cast(WiFi.localIP()) != 0); + bool sip = (static_cast(WiFi.softAPIP()) != 0); + WSContentSend_P(PSTR("

%s%s (%s%s%s)

"), // sonoff.local (192.168.2.12,192.168.4.1) + my_hostname, + (mdns_begun) ? ".local" : "", + (lip) ? WiFi.localIP().toString().c_str() : "", + (lip && sip) ? "," : "", + (sip) ? WiFi.softAPIP().toString().c_str() : ""); + } + WSContentSend_P(PSTR("
")); } void WSContentSendStyle(void) @@ -812,7 +802,7 @@ void HandleRoot(void) if ((Settings.web_password[0] != 0) && !(WebServer->hasArg("USER1")) && !(WebServer->hasArg("PASS1")) && HTTP_MANAGER_RESET_ONLY != webserver_state) { HandleWifiLogin(); } else { - if (!(Settings.web_password[0] != 0) || ((WebServer->arg("USER1") == WEB_USERNAME ) && (WebServer->arg("PASS1") == Settings.web_password ) || HTTP_MANAGER_RESET_ONLY == webserver_state)) { + if (!(Settings.web_password[0] != 0) || (((WebServer->arg("USER1") == WEB_USERNAME ) && (WebServer->arg("PASS1") == Settings.web_password )) || HTTP_MANAGER_RESET_ONLY == webserver_state)) { HandleWifiConfiguration(); } else { // wrong user and pass diff --git a/sonoff/xdrv_12_home_assistant.ino b/sonoff/xdrv_12_home_assistant.ino index 5998b3585..639c74499 100644 --- a/sonoff/xdrv_12_home_assistant.ino +++ b/sonoff/xdrv_12_home_assistant.ino @@ -166,7 +166,7 @@ static void Shorten(char** s, char *prefix) } } -int try_snprintf_P(char *s, size_t n, const char *format, ... ) +void try_snprintf_P(char *s, int n, const char *format, ... ) { va_list args; va_start(args, format); From 640e28f7cfa0fcad0848f86adf26ed1f98785579 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Fri, 15 Mar 2019 11:43:14 +0100 Subject: [PATCH 008/428] Prep for release 6.5 Prep for release 6.5 --- RELEASENOTES.md | 11 +- .../src/internal/NeoEsp8266DmaMethod.h | 16 ++- platformio.ini | 4 + sonoff/language/he-HE.h | 124 +++++++++--------- tools/decode-config.py | 8 +- 5 files changed, 90 insertions(+), 73 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 7089a956c..c90d62e06 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -93,7 +93,7 @@ Module | Description 69 SYF05 | Sunyesmart SYF05 RGBWW Wifi Led Bulb ## Provided Binary Downloads -The following binary downloads have been compiled with ESP8266/Arduino library core version **2.4.2** patched with the Alexa fix. +The following binary downloads have been compiled with ESP8266/Arduino library core version **2.3.0**. - **sonoff-minimal.bin** = The Minimal version allows intermediate OTA uploads to support larger versions and does NOT change any persistent parameter. This version **should NOT be used for initial installation**. - **sonoff-classic.bin** = The Classic version allows **initial installation** using either WifiManager, Wps or SmartConfig. @@ -103,6 +103,8 @@ The following binary downloads have been compiled with ESP8266/Arduino library c - **sonoff-display.bin** = The Display version without Wps and SmartConfig configuration but adds display support. - **sonoff-knx.bin** = The Knx version without Wps and SmartConfig configuration and some other features but adds KNX support. +Core version **2.4.2** binaries can be found at http://thehackbox.org/tasmota/ + ### Available Features and Sensors | Feature or Sensor | minimal | basic | classic | sonoff | knx | sensors | Remarks @@ -125,6 +127,7 @@ The following binary downloads have been compiled with ESP8266/Arduino library c | USE_TIMERS_WEB | - | x | - | x | x | x | | USE_SUNRISE | - | x | - | x | x | x | | USE_RULES | - | x | - | x | x | x | +| USE_EXPRESSION | - | - | - | - | - | - | | | | | | | | | USE_ADC_VCC | x | x | x | x | x | - | | USE_DS18B20 | - | - | - | - | - | - | Single sensor @@ -146,7 +149,7 @@ The following binary downloads have been compiled with ESP8266/Arduino library c | USE_TSL2561 | - | - | - | - | - | x | | USE_MGS | - | - | - | - | - | x | | USE_SGP30 | - | - | - | x | x | x | -| USE_SI1145 | - | - | - | - | - | x | +| USE_SI1145 | - | - | - | - | - | - | | USE_LM75AD | - | - | - | x | x | x | | USE_APDS9960 | - | - | - | - | - | - | | USE_MCP230xx | - | - | - | - | - | - | @@ -157,7 +160,7 @@ The following binary downloads have been compiled with ESP8266/Arduino library c | USE_DS3231 | - | - | - | - | - | - | | USE_MGC3130 | - | - | - | - | - | - | | USE_MAX44009 | - | - | - | - | - | - | -| USE_SCD30 | - | - | - | - | - | - | +| USE_SCD30 | - | - | - | - | - | x | | | | | | | | | | Feature or Sensor | minimal | basic | classic | sonoff | knx | sensors | | USE_SPI | - | - | - | - | - | - | @@ -177,7 +180,7 @@ The following binary downloads have been compiled with ESP8266/Arduino library c | USE_ARMTRONIX_DIMMERS | - | x | - | x | x | x | | USE_PS_16_DZ | - | x | - | x | x | x | | USE_AZ7798 | - | - | - | - | - | - | -| USE_PN532_HSU | - | - | - | - | - | - | +| USE_PN532_HSU | - | - | - | - | - | x | | USE_IR_REMOTE | - | - | - | x | x | x | | USE_IR_HVAC | - | - | - | - | - | x | | USE_IR_RECEIVE | - | - | - | x | x | x | diff --git a/lib/NeoPixelBus-2.2.9/src/internal/NeoEsp8266DmaMethod.h b/lib/NeoPixelBus-2.2.9/src/internal/NeoEsp8266DmaMethod.h index af11e1a7a..dc4dd2723 100644 --- a/lib/NeoPixelBus-2.2.9/src/internal/NeoEsp8266DmaMethod.h +++ b/lib/NeoPixelBus-2.2.9/src/internal/NeoEsp8266DmaMethod.h @@ -48,7 +48,11 @@ extern "C" #include "ets_sys.h" #include "user_interface.h" +// Workaround STAGE compile error +#include +#if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_1) || defined(ARDUINO_ESP8266_RELEASE_2_4_2) || defined(ARDUINO_ESP8266_RELEASE_2_5_0) void rom_i2c_writeReg_Mask(uint32_t block, uint32_t host_id, uint32_t reg_add, uint32_t Msb, uint32_t Lsb, uint32_t indata); +#endif } struct slc_queue_item @@ -74,7 +78,7 @@ public: class NeoEsp8266DmaSpeed800Kbps { public: - const static uint32_t I2sClockDivisor = 3; + const static uint32_t I2sClockDivisor = 3; const static uint32_t I2sBaseClockDivisor = 16; const static uint32_t ResetTimeUs = 50; }; @@ -82,7 +86,7 @@ public: class NeoEsp8266DmaSpeed400Kbps { public: - const static uint32_t I2sClockDivisor = 6; + const static uint32_t I2sClockDivisor = 6; const static uint32_t I2sBaseClockDivisor = 16; const static uint32_t ResetTimeUs = 50; }; @@ -100,7 +104,7 @@ const uint8_t c_I2sPin = 3; // due to I2S hardware, the pin used is restricted t template class NeoEsp8266DmaMethodBase { public: - NeoEsp8266DmaMethodBase(uint16_t pixelCount, size_t elementSize) + NeoEsp8266DmaMethodBase(uint16_t pixelCount, size_t elementSize) { uint16_t dmaPixelSize = c_dmaBytesPerPixelBytes * elementSize; @@ -269,7 +273,7 @@ public: private: static NeoEsp8266DmaMethodBase* s_this; // for the ISR - size_t _pixelsSize; // Size of '_pixels' buffer + size_t _pixelsSize; // Size of '_pixels' buffer uint8_t* _pixels; // Holds LED color values uint32_t _i2sBufferSize; // total size of _i2sBuffer @@ -310,7 +314,7 @@ private: slc_queue_item* finished_item = (slc_queue_item*)SLCRXEDA; // data block has pending data waiting to send, prepare it - // point last state block to top + // point last state block to top (finished_item + 1)->next_link_ptr = (uint32_t)(s_this->_i2sBufDesc); s_this->_dmaState = NeoDmaState_Sending; @@ -367,7 +371,7 @@ private: } }; -template +template NeoEsp8266DmaMethodBase* NeoEsp8266DmaMethodBase::s_this; typedef NeoEsp8266DmaMethodBase NeoEsp8266DmaWs2813Method; diff --git a/platformio.ini b/platformio.ini index b12b3ad00..671a6f284 100644 --- a/platformio.ini +++ b/platformio.ini @@ -89,6 +89,10 @@ build_flags = ${esp82xx_defaults.build_flags} platform = https://github.com/platformio/platform-espressif8266.git#feature/stage build_flags = ${esp82xx_defaults.build_flags} -Wl,-Teagle.flash.1m.ld +; nonos-sdk 22x + -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x +; nonos-sdk-pre-v3 +; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK3 ; lwIP 1.4 (Default) ; -DPIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH ; lwIP 2 - Low Memory diff --git a/sonoff/language/he-HE.h b/sonoff/language/he-HE.h index 9f3458889..8ce1a3a78 100644 --- a/sonoff/language/he-HE.h +++ b/sonoff/language/he-HE.h @@ -55,20 +55,20 @@ #define D_AIR_QUALITY "איכות אוויר" #define D_AP "AP" // Access Point #define D_AS "as" -#define D_AUTO "AUTO" +#define D_AUTO "אוטומטי" #define D_BLINK "מהבהב" #define D_BLINKOFF "כיבוי היבהוב" #define D_BOOT_COUNT "מונה הפעלה מחדש" #define D_BRIGHTLIGHT "בהירות" #define D_BSSID "BSSId" #define D_BUTTON "לחצן" -#define D_BY "by" // Written by me +#define D_BY "ע" // Written by me #define D_BYTES "בייט" #define D_CELSIUS "צלזיוס" #define D_CHANNEL "ערוץ" #define D_CO2 "Carbon dioxide" #define D_CODE "קוד" // Button code -#define D_COLDLIGHT "קור" +#define D_COLDLIGHT "אור קר" #define D_COMMAND "פקודה" #define D_CONNECTED "מחובר" #define D_COUNT "סופר" @@ -148,7 +148,7 @@ #define D_STOP "עצירה" #define D_SUBNET_MASK "רשת מסכת משנה" #define D_SUBSCRIBE_TO "הרשם ל" -#define D_UNSUBSCRIBE_FROM "Unsubscribe from" +#define D_UNSUBSCRIBE_FROM "בטל רישום" #define D_SUCCESSFUL "הצליח" #define D_SUNRISE "זריחה" #define D_SUNSET "שקיעה" @@ -176,33 +176,33 @@ #define D_UV_POWER "UV Power" #define D_VERSION "גרסה" #define D_VOLTAGE "מתח" -#define D_WEIGHT "Weight" +#define D_WEIGHT "משקל" #define D_WARMLIGHT "חום" #define D_WEB_SERVER "Web שרת" // sonoff.ino -#define D_WARNING_MINIMAL_VERSION "WARNING This version does not support persistent settings" +#define D_WARNING_MINIMAL_VERSION "אזהרה גרסה זו אינה תומכת בהגדרות קבועות" #define D_LEVEL_10 "level 1-0" #define D_LEVEL_01 "level 0-1" -#define D_SERIAL_LOGGING_DISABLED "Serial logging disabled" -#define D_SYSLOG_LOGGING_REENABLED "Syslog logging re-enabled" +#define D_SERIAL_LOGGING_DISABLED "רישום טורי מושבת" +#define D_SYSLOG_LOGGING_REENABLED "הופעל מחדש Syslog רישום" -#define D_SET_BAUDRATE_TO "Set Baudrate to" -#define D_RECEIVED_TOPIC "Received Topic" -#define D_DATA_SIZE "Data Size" +#define D_SET_BAUDRATE_TO "הגדר קצב שידור ל" +#define D_RECEIVED_TOPIC "Topic התקבל" +#define D_DATA_SIZE "גודל נתונים" #define D_ANALOG_INPUT "אנלוגי" // support.ino #define D_OSWATCH "osWatch" -#define D_BLOCKED_LOOP "Blocked Loop" -#define D_WPS_FAILED_WITH_STATUS "WPSconfig FAILED with status" -#define D_ACTIVE_FOR_3_MINUTES "active for 3 minutes" -#define D_FAILED_TO_START "failed to start" -#define D_PATCH_ISSUE_2186 "Patch issue 2186" -#define D_CONNECTING_TO_AP "Connecting to AP" -#define D_IN_MODE "in mode" -#define D_CONNECT_FAILED_NO_IP_ADDRESS "Connect failed as no IP address received" -#define D_CONNECT_FAILED_AP_NOT_REACHED "Connect failed as AP cannot be reached" +#define D_BLOCKED_LOOP "לולאות חסומות" +#define D_WPS_FAILED_WITH_STATUS "נכשל עם הסטטוס WPSconfig" +#define D_ACTIVE_FOR_3_MINUTES "פעיל במשך 3 דקות" +#define D_FAILED_TO_START "נכשל בנסיון להתחיל" +#define D_PATCH_ISSUE_2186 "בעית תיקון 2186" +#define D_CONNECTING_TO_AP "AP -מתחבר ל" +#define D_IN_MODE "במצב" +#define D_CONNECT_FAILED_NO_IP_ADDRESS "IP החיבור נכשל מכיוון שלא התקבלה כתובת" +#define D_CONNECT_FAILED_AP_NOT_REACHED "זמין AP החיבור נכשל כיוון שאין" #define D_CONNECT_FAILED_WRONG_PASSWORD "Connect failed with AP incorrect password" #define D_CONNECT_FAILED_AP_TIMEOUT "Connect failed with AP timeout" #define D_ATTEMPTING_CONNECTION "Attempting connection..." @@ -213,19 +213,19 @@ #define D_SYSLOG_HOST_NOT_FOUND "Syslog Host not found" // settings.ino -#define D_SAVED_TO_FLASH_AT "Saved to flash at" -#define D_LOADED_FROM_FLASH_AT "Loaded from flash at" +#define D_SAVED_TO_FLASH_AT "נשמר לפלאש ב" +#define D_LOADED_FROM_FLASH_AT "נטען מהפלאש מ" #define D_USE_DEFAULTS "השתמש בהגדרות ברירת המחדל" #define D_ERASED_SECTOR "סקטור מחוק" // xdrv_02_webserver.ino #define D_NOSCRIPT "JavaScript - כדי להשתמש ב קושחה אסמוטה אנא הפעל" #define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "קושחה מינימלית - בבקשה אנא שדרג" -#define D_WEBSERVER_ACTIVE_ON "Web server active on" -#define D_WITH_IP_ADDRESS "with IP address" -#define D_WEBSERVER_STOPPED "Web server stopped" -#define D_FILE_NOT_FOUND "File Not Found" -#define D_REDIRECTED "Redirected to captive portal" +#define D_WEBSERVER_ACTIVE_ON "שרת ווב פעיל" +#define D_WITH_IP_ADDRESS "IP עם כתובת" +#define D_WEBSERVER_STOPPED "שרת ווב הופסק" +#define D_FILE_NOT_FOUND "קובץ לא נמצא" +#define D_REDIRECTED "הופנה מחדש לפורטל" #define D_WIFIMANAGER_SET_ACCESSPOINT_AND_STATION "Wifimanager set AccessPoint and keep Station" #define D_WIFIMANAGER_SET_ACCESSPOINT "Wifimanager set AccessPoint" #define D_TRYING_TO_CONNECT "מנסה לחבר את ההתקן לרשת" @@ -254,7 +254,7 @@ #define D_MODULE_PARAMETERS "מודול פרמטרים" #define D_MODULE_TYPE "סוג מודול" -#define D_PULLUP_ENABLE "No Button/Switch pull-up" +#define D_PULLUP_ENABLE "pull-up אין לחצן/מתג" #define D_GPIO " רגל " #define D_SERIAL_IN "כניסת סריאל" #define D_SERIAL_OUT "יציאת סריאל" @@ -299,13 +299,13 @@ #define D_SINGLE_DEVICE "התקן בודד" #define D_MULTI_DEVICE "התקנים" -#define D_CONFIGURE_TEMPLATE "Configure Template" -#define D_TEMPLATE_PARAMETERS "Template parameters" -#define D_TEMPLATE_NAME "Name" -#define D_BASE_TYPE "Based on" -#define D_TEMPLATE_FLAGS "Options" -#define D_ALLOW_ADC0 "ADC0 input" -#define D_ALLOW_PULLUP "User pull-up selection" +#define D_CONFIGURE_TEMPLATE "הגדר תבנית" +#define D_TEMPLATE_PARAMETERS "פרמטרים של תבנית" +#define D_TEMPLATE_NAME "שם" +#define D_BASE_TYPE "מבוסס על" +#define D_TEMPLATE_FLAGS "אפשריות" +#define D_ALLOW_ADC0 "ADC0 כניסת" +#define D_ALLOW_PULLUP "pull-up בחירת משתמש עבור" #define D_SAVE_CONFIGURATION "שמירת הגדרות" #define D_CONFIGURATION_SAVED "הגדרות נשמרו" @@ -357,12 +357,12 @@ #define D_NEED_USER_AND_PASSWORD "Need user=&password=" // xdrv_01_mqtt.ino -#define D_FINGERPRINT "Verify TLS fingerprint..." -#define D_TLS_CONNECT_FAILED_TO "TLS Connect failed to" -#define D_RETRY_IN "Retry in" -#define D_VERIFIED "Verified using Fingerprint" -#define D_INSECURE "Insecure connection due to invalid Fingerprint" -#define D_CONNECT_FAILED_TO "Connect failed to" +#define D_FINGERPRINT "...TLS אמת טביעת אצבע של" +#define D_TLS_CONNECT_FAILED_TO "נכשל TLS חיבור" +#define D_RETRY_IN "נסה שוב תוך" +#define D_VERIFIED "מאומת באמצעות טביעת אצבע" +#define D_INSECURE "חיבור לא מאובטח עקב טביעת אצבע לא חוקית" +#define D_CONNECT_FAILED_TO "חיבור נכשל ל" // xplg_wemohue.ino #define D_MULTICAST_DISABLED "Multicast disabled" @@ -441,16 +441,16 @@ #define D_ENERGY_TOTAL "צריכה כללית" // xsns_05_ds18b20.ino -#define D_SENSOR_BUSY "Sensor busy" -#define D_SENSOR_CRC_ERROR "Sensor CRC error" -#define D_SENSORS_FOUND "Sensors found" +#define D_SENSOR_BUSY "שרת עסוק" +#define D_SENSOR_CRC_ERROR "בחיישן CRC שגיאת" +#define D_SENSORS_FOUND "חיישנים לא נמצאו" // xsns_06_dht.ino -#define D_TIMEOUT_WAITING_FOR "Timeout waiting for" -#define D_START_SIGNAL_LOW "start signal low" -#define D_START_SIGNAL_HIGH "start signal high" -#define D_PULSE "pulse" -#define D_CHECKSUM_FAILURE "Checksum failure" +#define D_TIMEOUT_WAITING_FOR "הזמן הקצוב להמתנה" +#define D_START_SIGNAL_LOW "להתחיל אות נמוך" +#define D_START_SIGNAL_HIGH "להתחיל אות גבוה" +#define D_PULSE "פעימה" +#define D_CHECKSUM_FAILURE "בדיקת כשל נכשלה" // xsns_07_sht1x.ino #define D_SENSOR_DID_NOT_ACK_COMMAND "Sensor did not ACK command" @@ -470,17 +470,17 @@ #define D_GZ_AXIS "Gyro Z-Axis" // xsns_34_hx711.ino -#define D_HX_CAL_REMOVE "Remove weigth" -#define D_HX_CAL_REFERENCE "Load reference weigth" -#define D_HX_CAL_DONE "Calibrated" -#define D_HX_CAL_FAIL "Calibration failed" -#define D_RESET_HX711 "Reset Scale" -#define D_CONFIGURE_HX711 "Configure Scale" -#define D_HX711_PARAMETERS "Scale parameters" -#define D_ITEM_WEIGHT "Item weight" -#define D_REFERENCE_WEIGHT "Reference weigth" -#define D_CALIBRATE "Calibrate" -#define D_CALIBRATION "Calibration" +#define D_HX_CAL_REMOVE "הסר משקל" +#define D_HX_CAL_REFERENCE "טען משקל התייחסות" +#define D_HX_CAL_DONE "מכויל" +#define D_HX_CAL_FAIL "כיול נכשל" +#define D_RESET_HX711 "אפס את קנה המידה" +#define D_CONFIGURE_HX711 "הגדר קנה מידה" +#define D_HX711_PARAMETERS "פרמטרים של קנה מידה" +#define D_ITEM_WEIGHT "משקל פריט" +#define D_REFERENCE_WEIGHT "משקל הפניה" +#define D_CALIBRATE "כייל" +#define D_CALIBRATION "כיול" //xsns_35_tx20.ino #define D_TX20_WIND_DIRECTION "Wind Direction" @@ -494,7 +494,7 @@ // sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box #define D_SENSOR_NONE "None" -#define D_SENSOR_USER "User" +#define D_SENSOR_USER "משתמש" #define D_SENSOR_DHT11 "DHT11" #define D_SENSOR_AM2301 "AM2301" #define D_SENSOR_SI7021 "SI7021" @@ -502,7 +502,7 @@ #define D_SENSOR_I2C_SCL "I2C SCL" #define D_SENSOR_I2C_SDA "I2C SDA" #define D_SENSOR_WS2812 "WS2812" -#define D_SENSOR_DFR562 "MP3 Player" +#define D_SENSOR_DFR562 "נגן מוזיקה" #define D_SENSOR_IRSEND "IRsend" #define D_SENSOR_SWITCH "מתג" // Suffix "1" #define D_SENSOR_BUTTON "לחצן" // Suffix "1" diff --git a/tools/decode-config.py b/tools/decode-config.py index 0b76ece85..feec178cf 100644 --- a/tools/decode-config.py +++ b/tools/decode-config.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -VER = '2.1.0021' +VER = '2.1.0022' """ decode-config.py - Backup/Restore Sonoff-Tasmota configuration data @@ -873,7 +873,13 @@ Setting_6_4_1_16.update({ Setting_6_4_1_17 = copy.deepcopy(Setting_6_4_1_16) Setting_6_4_1_17['flag3'][0].pop('no_pullup',None) # ====================================================================== +Setting_6_4_1_18 = copy.deepcopy(Setting_6_4_1_17) +Setting_6_4_1_18['flag3'][0].update ({ + 'no_hold_retain': (' Date: Fri, 15 Mar 2019 11:46:41 +0100 Subject: [PATCH 009/428] Select core 2.3.0 as default Select core 2.3.0 as default --- platformio.ini | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/platformio.ini b/platformio.ini index 671a6f284..8d349d974 100644 --- a/platformio.ini +++ b/platformio.ini @@ -119,10 +119,10 @@ build_flags = ${esp82xx_defaults.build_flags} [core_active] ; Select one core set for platform and build_flags -;platform = ${core_2_3_0.platform} -;build_flags = ${core_2_3_0.build_flags} -platform = ${core_2_4_2.platform} -build_flags = ${core_2_4_2.build_flags} +platform = ${core_2_3_0.platform} +build_flags = ${core_2_3_0.build_flags} +;platform = ${core_2_4_2.platform} +;build_flags = ${core_2_4_2.build_flags} ;platform = ${core_2_5_0.platform} ;build_flags = ${core_2_5_0.build_flags} ;platform = ${core_stage.platform} From 39d81182d5f24468cd9808e74df67f45606b1aac Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Fri, 15 Mar 2019 14:53:19 +0100 Subject: [PATCH 010/428] Prep for 6.5 Prep for 6.5 --- RELEASENOTES.md | 184 ++++++++++++++++++----------------- sonoff/_changelog.ino | 3 + sonoff/my_user_config.h | 10 -- sonoff/sonoff.ino | 4 +- sonoff/sonoff_post.h | 2 - sonoff/sonoff_version.h | 2 +- sonoff/support_features.ino | 6 +- sonoff/xdrv_01_webserver.ino | 18 ++-- sonoff/xdrv_02_mqtt.ino | 160 ------------------------------ sonoff/xdrv_07_domoticz.ino | 1 - 10 files changed, 114 insertions(+), 276 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index c90d62e06..21885e970 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -107,100 +107,106 @@ Core version **2.4.2** binaries can be found at http://thehackbox.org/tasmota/ ### Available Features and Sensors -| Feature or Sensor | minimal | basic | classic | sonoff | knx | sensors | Remarks -|--------------------------------|---------|-------|---------|--------|------|---------|-------- -| MY_LANGUAGE en-GB | x | x | x | x | x | x | -| MQTT_LIBRARY_TYPE PUBSUBCLIENT | x | x | x | x | x | x | -| USE_WPS | - | - | x | - | - | - | WPS -| USE_SMARTCONFIG | - | - | x | - | - | - | SmartConfig -| USE_ARDUINO_OTA | - | - | - | - | - | - | -| USE_DOMOTICZ | - | - | x | x | x | x | -| USE_HOME_ASSISTANT | - | - | - | x | x | x | -| USE_MQTT_TLS | - | - | - | - | - | - | -| USE_KNX | - | - | - | - | x | - | -| USE_WEBSERVER | x | x | x | x | x | x | WifiManager -| USE_EMULATION | - | x | x | x | - | x | -| USE_DISCOVERY | - | - | x | x | x | x | -| WEBSERVER_ADVERTISE | - | - | x | x | x | x | -| MQTT_HOST_DISCOVERY | - | - | x | x | x | x | -| USE_TIMERS | - | x | - | x | x | x | -| USE_TIMERS_WEB | - | x | - | x | x | x | -| USE_SUNRISE | - | x | - | x | x | x | -| USE_RULES | - | x | - | x | x | x | -| USE_EXPRESSION | - | - | - | - | - | - | -| | | | | | | -| USE_ADC_VCC | x | x | x | x | x | - | -| USE_DS18B20 | - | - | - | - | - | - | Single sensor -| USE_DS18x20 | - | - | x | x | x | x | Multiple sensors -| USE_DS18x20_LEGACY | - | - | - | - | - | - | Multiple sensors -| | | | | | | | -| Feature or Sensor | minimal | basic | classic | sonoff | knx | sensors | -| USE_I2C | - | - | - | x | x | x | -| USE_SHT | - | - | - | x | x | x | -| USE_HTU | - | - | - | x | x | x | -| USE_BMP | - | - | - | x | x | x | -| USE_BME680 | - | - | - | - | - | x | -| USE_BH1750 | - | - | - | x | x | x | -| USE_VEML6070 | - | - | - | - | - | x | -| USE_ADS1115 | - | - | - | - | - | x | -| USE_ADS1115_I2CDEV | - | - | - | - | - | - | -| USE_INA219 | - | - | - | - | - | x | -| USE_SHT3X | - | - | - | x | x | x | -| USE_TSL2561 | - | - | - | - | - | x | -| USE_MGS | - | - | - | - | - | x | -| USE_SGP30 | - | - | - | x | x | x | -| USE_SI1145 | - | - | - | - | - | - | -| USE_LM75AD | - | - | - | x | x | x | -| USE_APDS9960 | - | - | - | - | - | - | -| USE_MCP230xx | - | - | - | - | - | - | -| USE_PCA9685 | - | - | - | - | - | - | -| USE_MPR121 | - | - | - | - | - | - | -| USE_CCS811 | - | - | - | - | - | - | -| USE_MPU6050 | - | - | - | - | - | - | -| USE_DS3231 | - | - | - | - | - | - | -| USE_MGC3130 | - | - | - | - | - | - | -| USE_MAX44009 | - | - | - | - | - | - | -| USE_SCD30 | - | - | - | - | - | x | -| | | | | | | | -| Feature or Sensor | minimal | basic | classic | sonoff | knx | sensors | -| USE_SPI | - | - | - | - | - | - | -| USE_MHZ19 | - | - | - | x | x | x | -| USE_SENSEAIR | - | - | - | x | x | x | -| USE_PMS5003 | - | - | - | x | x | x | -| USE_NOVA_SDS | - | - | - | x | x | x | -| USE_PZEM004T | - | - | - | x | x | x | -| USE_PZEM_AC | - | - | - | x | x | x | -| USE_PZEM_DC | - | - | - | x | x | x | -| USE_MCP39F501 | - | x | - | x | x | x | -| USE_SERIAL_BRIDGE | - | - | - | x | x | x | -| USE_SDM120 | - | - | - | - | - | x | -| USE_SDM630 | - | - | - | - | - | x | -| USE_MP3_PLAYER | - | - | - | - | - | x | -| USE_TUYA_DIMMER | - | x | - | x | x | x | -| USE_ARMTRONIX_DIMMERS | - | x | - | x | x | x | -| USE_PS_16_DZ | - | x | - | x | x | x | -| USE_AZ7798 | - | - | - | - | - | - | -| USE_PN532_HSU | - | - | - | - | - | x | -| USE_IR_REMOTE | - | - | - | x | x | x | -| USE_IR_HVAC | - | - | - | - | - | x | -| USE_IR_RECEIVE | - | - | - | x | x | x | -| USE_WS2812 | - | - | x | x | x | x | -| USE_WS2812_DMA | - | - | - | - | - | - | -| USE_ARILUX_RF | - | - | - | x | x | x | -| USE_SR04 | - | - | - | x | x | x | -| USE_TM1638 | - | - | - | - | - | x | -| USE_HX711 | - | - | - | x | x | x | -| USE_RF_FLASH | - | - | - | x | x | x | -| USE_TX20_WIND_SENSOR | - | - | - | x | x | x | -| USE_RC_SWITCH | - | - | - | x | x | x | -| USE_RF_SENSOR | - | - | - | - | - | x | AlectoV2 only -| USE_SM16716 | - | x | x | x | x | x | -| USE_DISPLAY | - | - | - | - | - | - | +| Feature or Sensor | minimal | basic | classic | sonoff | knx | sensors | display | Remarks +|-----------------------|---------|-------|---------|--------|------|---------|---------|-------- +| MY_LANGUAGE en-GB | x | x | x | x | x | x | x | +| USE_WPS | - | - | x | - | - | - | - | WPS +| USE_SMARTCONFIG | - | - | x | - | - | - | - | SmartConfig +| USE_ARDUINO_OTA | - | - | - | - | - | - | - | +| USE_DOMOTICZ | - | - | x | x | x | x | - | +| USE_HOME_ASSISTANT | - | - | - | x | x | x | - | +| USE_MQTT_TLS | - | - | - | - | - | - | - | +| USE_KNX | - | - | - | - | x | - | - | +| USE_WEBSERVER | x | x | x | x | x | x | x | WifiManager +| USE_EMULATION | - | x | x | x | - | x | - | +| USE_DISCOVERY | - | - | x | x | x | x | x | +| WEBSERVER_ADVERTISE | - | - | x | x | x | x | x | +| MQTT_HOST_DISCOVERY | - | - | x | x | x | x | x | +| USE_TIMERS | - | x | - | x | x | x | x | +| USE_TIMERS_WEB | - | x | - | x | x | x | x | +| USE_SUNRISE | - | x | - | x | x | x | x | +| USE_RULES | - | x | - | x | x | x | x | +| USE_EXPRESSION | - | - | - | - | - | - | - | +| | | | | | | | | +| USE_ADC_VCC | x | x | x | x | x | - | x | +| USE_DS18B20 | - | - | - | - | - | - | - | Single sensor +| USE_DS18x20 | - | - | x | x | x | x | x | Multiple sensors +| USE_DS18x20_LEGACY | - | - | - | - | - | - | - | Multiple sensors +| | | | | | | | | +| Feature or Sensor | minimal | basic | classic | sonoff | knx | sensors | display | Remarks +| USE_I2C | - | - | - | x | x | x | x | +| USE_SHT | - | - | - | x | x | x | x | +| USE_HTU | - | - | - | x | x | x | x | +| USE_BMP | - | - | - | x | x | x | x | +| USE_BME680 | - | - | - | - | - | x | - | +| USE_BH1750 | - | - | - | x | x | x | x | +| USE_VEML6070 | - | - | - | - | - | x | - | +| USE_ADS1115 | - | - | - | - | - | x | - | +| USE_ADS1115_I2CDEV | - | - | - | - | - | - | - | +| USE_INA219 | - | - | - | - | - | x | - | +| USE_SHT3X | - | - | - | x | x | x | x | +| USE_TSL2561 | - | - | - | - | - | x | - | +| USE_MGS | - | - | - | - | - | x | - | +| USE_SGP30 | - | - | - | x | x | x | x | +| USE_SI1145 | - | - | - | - | - | - | - | +| USE_LM75AD | - | - | - | x | x | x | x | +| USE_APDS9960 | - | - | - | - | - | - | - | +| USE_MCP230xx | - | - | - | - | - | - | - | +| USE_PCA9685 | - | - | - | - | - | - | - | +| USE_MPR121 | - | - | - | - | - | - | - | +| USE_CCS811 | - | - | - | - | - | - | - | +| USE_MPU6050 | - | - | - | - | - | - | - | +| USE_DS3231 | - | - | - | - | - | - | - | +| USE_MGC3130 | - | - | - | - | - | - | - | +| USE_MAX44009 | - | - | - | - | - | - | - | +| USE_SCD30 | - | - | - | - | - | x | - | +| | | | | | | | | +| Feature or Sensor | minimal | basic | classic | sonoff | knx | sensors | display | Remarks +| USE_SPI | - | - | - | - | - | - | x | +| USE_MHZ19 | - | - | - | x | x | x | x | +| USE_SENSEAIR | - | - | - | x | x | x | x | +| USE_PMS5003 | - | - | - | x | x | x | x | +| USE_NOVA_SDS | - | - | - | x | x | x | x | +| USE_ENERGY_SENSOR | - | x | x | x | x | x | - | +| USE_PZEM004T | - | - | - | x | x | x | - | +| USE_PZEM_AC | - | - | - | x | x | x | - | +| USE_PZEM_DC | - | - | - | x | x | x | - | +| USE_MCP39F501 | - | x | - | x | x | x | - | +| USE_SERIAL_BRIDGE | - | - | - | x | x | x | x | +| USE_SDM120 | - | - | - | - | - | x | - | +| USE_SDM630 | - | - | - | - | - | x | - | +| USE_MP3_PLAYER | - | - | - | - | - | x | - | +| USE_TUYA_DIMMER | - | x | - | x | x | x | x | +| USE_ARMTRONIX_DIMMERS | - | x | - | x | x | x | x | +| USE_PS_16_DZ | - | x | - | x | x | x | x | +| USE_AZ7798 | - | - | - | - | - | - | - | +| USE_PN532_HSU | - | - | - | - | - | x | - | +| USE_IR_REMOTE | - | - | - | x | x | x | x | +| USE_IR_HVAC | - | - | - | - | - | x | - | +| USE_IR_RECEIVE | - | - | - | x | x | x | x | +| USE_WS2812 | - | - | x | x | x | x | x | +| USE_WS2812_DMA | - | - | - | - | - | - | - | +| USE_ARILUX_RF | - | - | - | x | x | x | - | +| USE_SR04 | - | - | - | x | x | x | x | +| USE_TM1638 | - | - | - | - | - | x | - | +| USE_HX711 | - | - | - | x | x | x | x | +| USE_RF_FLASH | - | - | - | x | x | x | - | +| USE_TX20_WIND_SENSOR | - | - | - | x | x | x | x | +| USE_RC_SWITCH | - | - | - | x | x | x | x | +| USE_RF_SENSOR | - | - | - | - | - | x | - | AlectoV2 only +| USE_SM16716 | - | x | x | x | x | x | x | +| USE_DISPLAY | - | - | - | - | - | - | x | +| USE_DISPLAY_LCD | - | - | - | - | - | - | x | +| USE_DISPLAY_SSD1306 | - | - | - | - | - | - | x | +| USE_DISPLAY_MATRIX | - | - | - | - | - | - | x | +| USE_DISPLAY_ILI9341 | - | - | - | - | - | - | x | +| USE_DISPLAY_EPAPER_29 | - | - | - | - | - | - | x | ## Changelog Version 6.5.0 20190319 * Remove commands SetOption14 and SetOption63 as it has been superseded by command Interlock * Remove command SetOption35 0-255 for mDNS start-up delay (#4793) + * Remove support for MQTT_LIBRARY_TYPE, MQTT_ARDUINOMQTT and MQTT_TASMOTAMQTT (#5474) * Change webserver content handling from single String to small Chunks increasing RAM * Change code use of boolean to bool and byte to uint8_t * Change code uint8_t flags to bool flags diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index 58ead17cd..e0770b742 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -1,6 +1,9 @@ /* 6.5.0 20190319 * New Release * + * 6.4.2.22 20190315 + * Remove support for MQTT_LIBRARY_TYPE, MQTT_ARDUINOMQTT and MQTT_TASMOTAMQTT (#5474) + * * 6.4.1.21 20190309 * Fix exception on GUI Configure Logging and Configure Other (#5424) * Add support for sensor SCD30 (#5434) diff --git a/sonoff/my_user_config.h b/sonoff/my_user_config.h index 5214ad46c..b7e0846e4 100644 --- a/sonoff/my_user_config.h +++ b/sonoff/my_user_config.h @@ -230,16 +230,6 @@ // -- OTA ----------------------------------------- //#define USE_ARDUINO_OTA // Add optional support for Arduino OTA (+13k code) -/*-------------------------------------------------------------------------------------------*\ - * Select ONE of possible MQTT library types below -\*-------------------------------------------------------------------------------------------*/ - // Default MQTT driver for both non-TLS and TLS connections. Latest library version (20181016) does not block network if MQTT server is unavailable. -#define MQTT_LIBRARY_TYPE MQTT_PUBSUBCLIENT // Use PubSubClient library - // Alternative MQTT driver does not block network when MQTT server is unavailable. No TLS support -//#define MQTT_LIBRARY_TYPE MQTT_TASMOTAMQTT // Use TasmotaMqtt library (+4k4 (core 2.3.0), +14k4 (core 2.4.2 lwip2) code, +4k mem) - non-TLS only - // Alternative MQTT driver does not block network when MQTT server is unavailable. TLS should work but needs to be tested. -//#define MQTT_LIBRARY_TYPE MQTT_ARDUINOMQTT // Use arduino-mqtt (lwmqtt) library (+3k3 code, +2k mem) - // -- MQTT ---------------------------------------- #define MQTT_TELE_RETAIN 0 // Tele messages may send retain flag (0 = off, 1 = on) diff --git a/sonoff/sonoff.ino b/sonoff/sonoff.ino index 39f180c0b..bccd057cf 100755 --- a/sonoff/sonoff.ino +++ b/sonoff/sonoff.ino @@ -1682,8 +1682,8 @@ void PublishStatus(uint8_t payload) } if (((0 == payload) || (6 == payload)) && Settings.flag.mqtt_enabled) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_STATUS D_STATUS6_MQTT "\":{\"" D_CMND_MQTTHOST "\":\"%s\",\"" D_CMND_MQTTPORT "\":%d,\"" D_CMND_MQTTCLIENT D_JSON_MASK "\":\"%s\",\"" D_CMND_MQTTCLIENT "\":\"%s\",\"" D_CMND_MQTTUSER "\":\"%s\",\"MqttType\":%d,\"" D_JSON_MQTT_COUNT "\":%d,\"MAX_PACKET_SIZE\":%d,\"KEEPALIVE\":%d}}"), - Settings.mqtt_host, Settings.mqtt_port, Settings.mqtt_client, mqtt_client, Settings.mqtt_user, MqttLibraryType(), MqttConnectCount(), MQTT_MAX_PACKET_SIZE, MQTT_KEEPALIVE); + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_STATUS D_STATUS6_MQTT "\":{\"" D_CMND_MQTTHOST "\":\"%s\",\"" D_CMND_MQTTPORT "\":%d,\"" D_CMND_MQTTCLIENT D_JSON_MASK "\":\"%s\",\"" D_CMND_MQTTCLIENT "\":\"%s\",\"" D_CMND_MQTTUSER "\":\"%s\",\"" D_JSON_MQTT_COUNT "\":%d,\"MAX_PACKET_SIZE\":%d,\"KEEPALIVE\":%d}}"), + Settings.mqtt_host, Settings.mqtt_port, Settings.mqtt_client, mqtt_client, Settings.mqtt_user, MqttConnectCount(), MQTT_MAX_PACKET_SIZE, MQTT_KEEPALIVE); MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "6")); } diff --git a/sonoff/sonoff_post.h b/sonoff/sonoff_post.h index fe5c5c08d..bcbc8f2ad 100755 --- a/sonoff/sonoff_post.h +++ b/sonoff/sonoff_post.h @@ -158,8 +158,6 @@ void KNX_CB_Action(message_t const &msg, void *arg); #ifndef USE_SMARTCONFIG #define USE_SMARTCONFIG // Add support for Wifi SmartConfig as initial wifi configuration tool (+23k code, +0.6k mem) #endif -#undef MQTT_LIBRARY_TYPE -#define MQTT_LIBRARY_TYPE MQTT_PUBSUBCLIENT // Use PubSubClient library #undef USE_ARDUINO_OTA // Disable support for Arduino OTA //#undef USE_DOMOTICZ // Disable Domoticz #undef USE_HOME_ASSISTANT // Disable Home Assistant diff --git a/sonoff/sonoff_version.h b/sonoff/sonoff_version.h index 0e9c322ac..b9516367a 100644 --- a/sonoff/sonoff_version.h +++ b/sonoff/sonoff_version.h @@ -20,7 +20,7 @@ #ifndef _SONOFF_VERSION_H_ #define _SONOFF_VERSION_H_ -#define VERSION 0x06050000 +#define VERSION 0x06040116 #define D_PROGRAMNAME "Sonoff-Tasmota" #define D_AUTHOR "Theo Arends" diff --git a/sonoff/support_features.ino b/sonoff/support_features.ino index c69b8e671..f5229e81f 100644 --- a/sonoff/support_features.ino +++ b/sonoff/support_features.ino @@ -56,10 +56,10 @@ void GetFeatures(void) feature_drv1 |= 0x00000400; // xdrv_01_mqtt.ino #endif #if (MQTT_LIBRARY_TYPE == MQTT_TASMOTAMQTT) - feature_drv1 |= 0x00000800; // xdrv_01_mqtt.ino +// feature_drv1 |= 0x00000800; // xdrv_01_mqtt.ino #endif #if (MQTT_LIBRARY_TYPE == MQTT_ESPMQTTARDUINO) // Obsolete since 6.2.1.11 - feature_drv1 |= 0x00001000; // xdrv_01_mqtt.ino +// feature_drv1 |= 0x00001000; // xdrv_01_mqtt.ino #endif #ifdef MQTT_HOST_DISCOVERY feature_drv1 |= 0x00002000; // xdrv_01_mqtt.ino @@ -116,7 +116,7 @@ void GetFeatures(void) feature_drv1 |= 0x40000000; // support.ino #endif #if (MQTT_LIBRARY_TYPE == MQTT_ARDUINOMQTT) - feature_drv1 |= 0x80000000; // xdrv_01_mqtt.ino +// feature_drv1 |= 0x80000000; // xdrv_01_mqtt.ino #endif /*********************************************************************************************/ diff --git a/sonoff/xdrv_01_webserver.ino b/sonoff/xdrv_01_webserver.ino index f243b1b11..d8ab7b56e 100644 --- a/sonoff/xdrv_01_webserver.ino +++ b/sonoff/xdrv_01_webserver.ino @@ -294,9 +294,7 @@ const char HTTP_FORM_LOGIN[] PROGMEM = const char HTTP_FORM_TEMPLATE[] PROGMEM = "
 " D_TEMPLATE_PARAMETERS " " - "" - "

" D_TEMPLATE_NAME "

" - "

" D_BASE_TYPE "

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

" // Keep close so do not use
"
 " D_TEMPLATE_FLAGS " 

" @@ -1063,16 +1061,20 @@ void HandleTemplateConfiguration(void) WSContentSend_P(HTTP_SCRIPT_TEMPLATE); WSContentSendStyle(); WSContentSend_P(HTTP_FORM_TEMPLATE); - - WSContentSend_P(PSTR("
")); + WSContentSend_P(HTTP_TABLE100); + WSContentSend_P(PSTR("" + "" + "
" D_TEMPLATE_NAME "
" D_BASE_TYPE "
" + "


")); + WSContentSend_P(HTTP_TABLE100); for (uint8_t i = 0; i < 17; i++) { if ((i < 6) || ((i > 8) && (i != 11))) { // Ignore flash pins GPIO06, 7, 8 and 11 - WSContentSend_P(PSTR("" D_GPIO "%d%s"), - (0==i)?" style='width:74px'":"", i, ((9==i)||(10==i))? "ESP8285" :"", (0==i)?" style='width:176px'":"", i, i); + bool esp8285 = ((9==i)||(10==i)); + WSContentSend_P(PSTR("%s" D_GPIO "%d%s"), + (esp8285) ? "" : "", i, (esp8285) ? "" : "", (0==i) ? " style='width:200px'" : "", i, i); } } WSContentSend_P(PSTR("")); - WSContentSend_P(HTTP_FORM_TEMPLATE_FLAG); WSContentSend_P(HTTP_FORM_END); WSContentSpaceButton(BUTTON_CONFIGURATION); diff --git a/sonoff/xdrv_02_mqtt.ino b/sonoff/xdrv_02_mqtt.ino index ba3dabc09..6330259fd 100644 --- a/sonoff/xdrv_02_mqtt.ino +++ b/sonoff/xdrv_02_mqtt.ino @@ -19,42 +19,6 @@ #define XDRV_02 2 -/*********************************************************************************************\ - * Select ONE of possible MQTT library types below -\*********************************************************************************************/ -// Default MQTT driver for both non-TLS and TLS connections. Blocks network if MQTT server is unavailable. -//#define MQTT_LIBRARY_TYPE MQTT_PUBSUBCLIENT // Use PubSubClient library -// Alternative MQTT driver does not block network when MQTT server is unavailable. No TLS support -//#define MQTT_LIBRARY_TYPE MQTT_TASMOTAMQTT // Use TasmotaMqtt library (+4k4 (core 2.3.0), +14k4 (core 2.4.2 lwip2) code, +4k mem) - non-TLS only -// Alternative MQTT driver does not block network when MQTT server is unavailable. TLS should work but needs to be tested. -//#define MQTT_LIBRARY_TYPE MQTT_ARDUINOMQTT // Use arduino-mqtt (lwmqtt) library (+3k3 code, +2k mem) - -#if (MQTT_LIBRARY_TYPE == MQTT_ESPMQTTARDUINO) // Obsolete as of v6.2.1.11 -#undef MQTT_LIBRARY_TYPE -#define MQTT_LIBRARY_TYPE MQTT_ARDUINOMQTT -#endif - -/* -#if (MQTT_LIBRARY_TYPE == MQTT_TASMOTAMQTT) -#undef MQTT_LIBRARY_TYPE -#define MQTT_LIBRARY_TYPE MQTT_ARDUINOMQTT // Obsolete in near future -#endif -*/ - -#ifdef USE_MQTT_TLS - -#if (MQTT_LIBRARY_TYPE == MQTT_TASMOTAMQTT) -#undef MQTT_LIBRARY_TYPE -#endif - -#ifndef MQTT_LIBRARY_TYPE -#define MQTT_LIBRARY_TYPE MQTT_PUBSUBCLIENT // Use PubSubClient library as it only supports TLS -#endif - -#endif // USE_MQTT_TLS - -/*********************************************************************************************/ - #ifdef USE_MQTT_TLS #ifdef USE_MQTT_TLS_CA_CERT #include "sonoff_letsencrypt.h" // LetsEncrypt certificate @@ -89,8 +53,6 @@ bool mqtt_allowed = false; // MQTT enabled and parameters valid * void MqttLoop() \*********************************************************************************************/ -#if (MQTT_LIBRARY_TYPE == MQTT_PUBSUBCLIENT) /***********************************************/ - #include // Max message size calculated by PubSubClient is (MQTT_MAX_PACKET_SIZE < 5 + 2 + strlen(topic) + plength) @@ -134,96 +96,6 @@ void MqttLoop(void) MqttClient.loop(); } -#elif (MQTT_LIBRARY_TYPE == MQTT_TASMOTAMQTT) /**********************************************/ - -#include -TasmotaMqtt MqttClient; - -bool MqttIsConnected(void) -{ - return MqttClient.Connected(); -} - -void MqttDisconnect(void) -{ - MqttClient.Disconnect(); -} - -void MqttDisconnectedCb(void) -{ - MqttDisconnected(MqttClient.State()); // status codes are documented in file mqtt.h as tConnState -} - -void MqttSubscribeLib(const char *topic) -{ - MqttClient.Subscribe(topic, 0); -} - -void MqttUnsubscribeLib(const char *topic) -{ - MqttClient.Unsubscribe(topic, 0); -} - -bool MqttPublishLib(const char* topic, bool retained) -{ - return MqttClient.Publish(topic, mqtt_data, strlen(mqtt_data), 0, retained); -} - -void MqttLoop(void) -{ -} - -#elif (MQTT_LIBRARY_TYPE == MQTT_ARDUINOMQTT) /**********************************************/ - -#include -MQTTClient MqttClient(MQTT_MAX_PACKET_SIZE); - -bool MqttIsConnected(void) -{ - return MqttClient.connected(); -} - -void MqttDisconnect(void) -{ - MqttClient.disconnect(); -} - -/* -void MqttMyDataCb(MQTTClient* client, char* topic, char* data, int data_len) -//void MqttMyDataCb(MQTTClient *client, char topic[], char data[], int data_len) -{ -// MqttDataHandler((char*)topic, (uint8_t*)data, data_len); -} -*/ - -void MqttMyDataCb(String &topic, String &data) -{ - MqttDataHandler((char*)topic.c_str(), (uint8_t*)data.c_str(), data.length()); -} - -void MqttSubscribeLib(const char *topic) -{ - MqttClient.subscribe(topic, 0); -} - -void MqttUnsubscribeLib(const char *topic) -{ - MqttClient.unsubscribe(topic, 0); -} - -bool MqttPublishLib(const char* topic, bool retained) -{ - return MqttClient.publish(topic, mqtt_data, strlen(mqtt_data), retained, 0); -} - -void MqttLoop(void) -{ - MqttClient.loop(); -// delay(10); -} - -#endif // MQTT_LIBRARY_TYPE - /*********************************************************************************************/ #ifdef USE_DISCOVERY @@ -254,11 +126,6 @@ void MqttDiscoverServer(void) #endif // MQTT_HOST_DISCOVERY #endif // USE_DISCOVERY -int MqttLibraryType(void) -{ - return (int)MQTT_LIBRARY_TYPE; -} - void MqttRetryCounter(uint8_t value) { mqtt_retry_counter = value; @@ -573,38 +440,20 @@ void MqttReconnect(void) GetTopic_P(stopic, TELE, mqtt_topic, S_LWT); snprintf_P(mqtt_data, sizeof(mqtt_data), S_OFFLINE); -//#ifdef ARDUINO_ESP8266_RELEASE_2_4_1 #ifdef USE_MQTT_TLS EspClient = WiFiClientSecure(); // Wifi Secure Client reconnect issue 4497 (https://github.com/esp8266/Arduino/issues/4497) #else EspClient = WiFiClient(); // Wifi Client reconnect issue 4497 (https://github.com/esp8266/Arduino/issues/4497) #endif -//#endif if (2 == mqtt_initial_connection_state) { // Executed once just after power on and wifi is connected #ifdef USE_MQTT_TLS if (!MqttCheckTls()) return; #endif // USE_MQTT_TLS -#if (MQTT_LIBRARY_TYPE == MQTT_TASMOTAMQTT) - MqttClient.InitConnection(Settings.mqtt_host, Settings.mqtt_port); - MqttClient.InitClient(mqtt_client, mqtt_user, mqtt_pwd, MQTT_KEEPALIVE); - MqttClient.InitLWT(stopic, mqtt_data, 1, true); - MqttClient.OnConnected(MqttConnected); - MqttClient.OnDisconnected(MqttDisconnectedCb); - MqttClient.OnData(MqttDataHandler); -#elif (MQTT_LIBRARY_TYPE == MQTT_ARDUINOMQTT) - MqttClient.begin(Settings.mqtt_host, Settings.mqtt_port, EspClient); - MqttClient.setWill(stopic, mqtt_data, true, 1); - MqttClient.setOptions(MQTT_KEEPALIVE, true, MQTT_TIMEOUT); -// MqttClient.onMessageAdvanced(MqttMyDataCb); - MqttClient.onMessage(MqttMyDataCb); -#endif - mqtt_initial_connection_state = 1; } -#if (MQTT_LIBRARY_TYPE == MQTT_PUBSUBCLIENT) MqttClient.setCallback(MqttDataHandler); MqttClient.setServer(Settings.mqtt_host, Settings.mqtt_port); if (MqttClient.connect(mqtt_client, mqtt_user, mqtt_pwd, stopic, 1, true, mqtt_data)) { @@ -612,15 +461,6 @@ void MqttReconnect(void) } else { MqttDisconnected(MqttClient.state()); // status codes are documented here http://pubsubclient.knolleary.net/api.html#state } -#elif (MQTT_LIBRARY_TYPE == MQTT_TASMOTAMQTT) - MqttClient.Connect(); -#elif (MQTT_LIBRARY_TYPE == MQTT_ARDUINOMQTT) - if (MqttClient.connect(mqtt_client, mqtt_user, mqtt_pwd)) { - MqttConnected(); - } else { - MqttDisconnected(MqttClient.lastError()); // status codes are documented here https://github.com/256dpi/lwmqtt/blob/master/include/lwmqtt.h#L11 - } -#endif // MQTT_LIBRARY_TYPE } void MqttCheck(void) diff --git a/sonoff/xdrv_07_domoticz.ino b/sonoff/xdrv_07_domoticz.ino index 0bd275d7c..c2020176d 100644 --- a/sonoff/xdrv_07_domoticz.ino +++ b/sonoff/xdrv_07_domoticz.ino @@ -434,7 +434,6 @@ const char HTTP_BTN_MENU_DOMOTICZ[] PROGMEM = const char HTTP_FORM_DOMOTICZ[] PROGMEM = "
 " D_DOMOTICZ_PARAMETERS " " "" - "
" ""; const char HTTP_FORM_DOMOTICZ_RELAY[] PROGMEM = "" From 8b5cea4a9862f6bedb62f8b59a6158560c3c3bb2 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Fri, 15 Mar 2019 15:02:03 +0100 Subject: [PATCH 011/428] Prep for 6.5 Prep for 6.5 --- README.md | 1 - RELEASENOTES.md | 5 +++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b2cf7500c..1ec23cb11 100644 --- a/README.md +++ b/README.md @@ -120,7 +120,6 @@ Libraries used with Sonoff-Tasmota are: - [Adafruit SSD1306](https://github.com/adafruit/Adafruit_SSD1306) - [Adafruit GFX](https://github.com/adafruit/Adafruit-GFX-Library) - [ArduinoJson](https://arduinojson.org/) -- [arduino mqtt](https://github.com/256dpi/arduino-mqtt) - [Bosch BME680](https://github.com/BoschSensortec/BME680_driver) - [C2 Programmer](http://app.cear.ufpb.br/~lucas.hartmann/tag/efm8bb1/) - [esp-epaper-29-ws-20171230-gemu](https://github.com/gemu2015/Sonoff-Tasmota/tree/displays/lib) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 21885e970..10d124169 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -96,12 +96,13 @@ Module | Description The following binary downloads have been compiled with ESP8266/Arduino library core version **2.3.0**. - **sonoff-minimal.bin** = The Minimal version allows intermediate OTA uploads to support larger versions and does NOT change any persistent parameter. This version **should NOT be used for initial installation**. +- **sonoff-basic.bin** = The Basic version without Wps and SmartConfig configuration and most sensors. - **sonoff-classic.bin** = The Classic version allows **initial installation** using either WifiManager, Wps or SmartConfig. - **sonoff.bin** = The Sonoff version without Wps and SmartConfig configuration but adds more sensors. - **sonoff-BG.bin** to **sonoff-TW.bin** = The Sonoff version without Wps and SmartConfig configuration in different languages. -- **sonoff-sensors.bin** = The Sensors version without Wps and SmartConfig configuration but adds even more useful sensors. -- **sonoff-display.bin** = The Display version without Wps and SmartConfig configuration but adds display support. - **sonoff-knx.bin** = The Knx version without Wps and SmartConfig configuration and some other features but adds KNX support. +- **sonoff-sensors.bin** = The Sensors version without Wps and SmartConfig configuration but adds even more useful sensors. +- **sonoff-display.bin** = The Display version without Wps and SmartConfig configuration and Energy Monitoring but adds display support. Core version **2.4.2** binaries can be found at http://thehackbox.org/tasmota/ From 0c8c74bd4e915579af5211999708b4dcefa299a9 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Fri, 15 Mar 2019 15:08:38 +0100 Subject: [PATCH 012/428] Prep for 6.5 Prep for 6.5 --- sonoff/sonoff_version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sonoff/sonoff_version.h b/sonoff/sonoff_version.h index b9516367a..0e9c322ac 100644 --- a/sonoff/sonoff_version.h +++ b/sonoff/sonoff_version.h @@ -20,7 +20,7 @@ #ifndef _SONOFF_VERSION_H_ #define _SONOFF_VERSION_H_ -#define VERSION 0x06040116 +#define VERSION 0x06050000 #define D_PROGRAMNAME "Sonoff-Tasmota" #define D_AUTHOR "Theo Arends" From f4583fa6c54d3bad997e0f15c0dab062612b0608 Mon Sep 17 00:00:00 2001 From: blakadder Date: Mon, 18 Mar 2019 14:07:42 +0100 Subject: [PATCH 013/428] Update RELEASENOTES.md Changed order of release titles since the old order created confusion with new users who often installed minimal or classic bin then needed support cause they were unable to configure their device --- RELEASENOTES.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 10d124169..0a81de711 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -95,12 +95,12 @@ Module | Description ## Provided Binary Downloads The following binary downloads have been compiled with ESP8266/Arduino library core version **2.3.0**. -- **sonoff-minimal.bin** = The Minimal version allows intermediate OTA uploads to support larger versions and does NOT change any persistent parameter. This version **should NOT be used for initial installation**. +- **sonoff.bin** = The Sonoff version without Wps and SmartConfig configuration but adds more sensors. **RECOMMENDED RELEASE BINARY** - **sonoff-basic.bin** = The Basic version without Wps and SmartConfig configuration and most sensors. -- **sonoff-classic.bin** = The Classic version allows **initial installation** using either WifiManager, Wps or SmartConfig. -- **sonoff.bin** = The Sonoff version without Wps and SmartConfig configuration but adds more sensors. +- **sonoff-classic.bin** = The Classic version allows initial installation using either WifiManager, Wps or SmartConfig. - **sonoff-BG.bin** to **sonoff-TW.bin** = The Sonoff version without Wps and SmartConfig configuration in different languages. - **sonoff-knx.bin** = The Knx version without Wps and SmartConfig configuration and some other features but adds KNX support. +- **sonoff-minimal.bin** = The Minimal version allows intermediate OTA uploads to support larger versions and does NOT change any persistent parameter. This version **should NOT be used for initial installation**. - **sonoff-sensors.bin** = The Sensors version without Wps and SmartConfig configuration but adds even more useful sensors. - **sonoff-display.bin** = The Display version without Wps and SmartConfig configuration and Energy Monitoring but adds display support. From becfdbd0f0f9fe388b0b005cb7516ede3d6ba302 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Tue, 19 Mar 2019 11:05:53 +0100 Subject: [PATCH 014/428] Prep for 6.5 Prep for 6.5 --- sonoff/sonoff.ino | 1 - sonoff/xdrv_01_webserver.ino | 201 ++++++++++++++++++----------------- sonoff/xdrv_02_mqtt.ino | 2 +- sonoff/xdrv_07_domoticz.ino | 2 +- sonoff/xdrv_09_timers.ino | 2 +- sonoff/xdrv_11_knx.ino | 2 +- sonoff/xsns_34_hx711.ino | 2 +- 7 files changed, 107 insertions(+), 105 deletions(-) diff --git a/sonoff/sonoff.ino b/sonoff/sonoff.ino index bccd057cf..51b89031c 100755 --- a/sonoff/sonoff.ino +++ b/sonoff/sonoff.ino @@ -167,7 +167,6 @@ bool i2c_flg = false; // I2C configured bool spi_flg = false; // SPI configured bool soft_spi_flg = false; // Software SPI configured bool ntp_force_sync = false; // Force NTP sync -bool reset_web_log_flag = false; // Reset web console log myio my_module; // Active copy of Module GPIOs (17 x 8 bits) gpio_flag my_module_flag; // Active copy of Module GPIO flags StateBitfield global_state; // Global states (currently Wifi and Mqtt) (8 bits) diff --git a/sonoff/xdrv_01_webserver.ino b/sonoff/xdrv_01_webserver.ino index d8ab7b56e..415d14e53 100644 --- a/sonoff/xdrv_01_webserver.ino +++ b/sonoff/xdrv_01_webserver.ino @@ -383,7 +383,7 @@ const char HTTP_END[] PROGMEM = ""; const char HTTP_DEVICE_CONTROL[] PROGMEM = ""; // ?o is related to WebGetArg("o", tmp, sizeof(tmp)); -const char HTTP_DEVICE_STATE[] PROGMEM = "%s"; // {c} = %'>
%s
"; // {c} = %'>
"), mqtt_data); + char stemp[strlen(mqtt_data) +1]; + memcpy(stemp, mqtt_data, sizeof(stemp)); + + WSContentBegin(200, CT_HTML); + WSContentSend_P(PSTR("{t}%s
" D_DOMOTICZ_IDX " %d
%s
"), stemp); if (devices_present) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s{t}"), mqtt_data); + WSContentSend_P(PSTR("{t}")); uint8_t fsize = (devices_present < 5) ? 70 - (devices_present * 8) : 32; if (SONOFF_IFAN02 == my_module_type) { - snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_DEVICE_STATE, - mqtt_data, 36, (bitRead(power, 0)) ? "bold" : "normal", 54, GetStateText(bitRead(power, 0))); + WSContentSend_P(HTTP_DEVICE_STATE, 36, (bitRead(power, 0)) ? "bold" : "normal", 54, GetStateText(bitRead(power, 0))); uint8_t fanspeed = GetFanspeed(); snprintf_P(svalue, sizeof(svalue), PSTR("%d"), fanspeed); - snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_DEVICE_STATE, - mqtt_data, 64, (fanspeed) ? "bold" : "normal", 54, (fanspeed) ? svalue : GetStateText(0)); + WSContentSend_P(HTTP_DEVICE_STATE, 64, (fanspeed) ? "bold" : "normal", 54, (fanspeed) ? svalue : GetStateText(0)); } else { for (uint8_t idx = 1; idx <= devices_present; idx++) { snprintf_P(svalue, sizeof(svalue), PSTR("%d"), bitRead(power, idx -1)); - snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_DEVICE_STATE, - mqtt_data, 100 / devices_present, (bitRead(power, idx -1)) ? "bold" : "normal", fsize, (devices_present < 5) ? GetStateText(bitRead(power, idx -1)) : svalue); + WSContentSend_P(HTTP_DEVICE_STATE, 100 / devices_present, (bitRead(power, idx -1)) ? "bold" : "normal", fsize, (devices_present < 5) ? GetStateText(bitRead(power, idx -1)) : svalue); } } - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s"), mqtt_data); + WSContentSend_P(PSTR("")); } - WSHeaderSend(); - WSSend(200, CT_HTML, mqtt_data); + WSContentEnd(); + return true; } @@ -987,7 +1003,7 @@ void HandleConfiguration(void) WSContentButton(BUTTON_RESTORE); WSContentSpaceButton(BUTTON_MAIN); - WSContentEnd(); + WSContentStop(); } /*-------------------------------------------------------------------------------------------*/ @@ -1005,13 +1021,12 @@ void HandleTemplateConfiguration(void) char stemp[20]; // Template number and Sensor name if (WebServer->hasArg("m")) { - String page = ""; + WSContentBegin(200, CT_PLAIN); for (uint8_t i = 0; i < MAXMODULE; i++) { // "}2'%d'>%s (%d)}3" - "}2'0'>Sonoff Basic (1)}3" uint8_t midx = pgm_read_byte(kModuleNiceList + i); - snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_MODULE_TEMPLATE_REPLACE, midx, AnyModuleName(midx).c_str(), midx +1); - page += mqtt_data; + WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE, midx, AnyModuleName(midx).c_str(), midx +1); } - WSSend(200, CT_PLAIN, page); + WSContentEnd(); return; } @@ -1025,32 +1040,26 @@ void HandleTemplateConfiguration(void) gpio_flag flag = ModuleFlag(); Settings.module = module_save; - String page = AnyModuleName(module); // NAME: Generic - page += F("}1"); // Field separator - + WSContentBegin(200, CT_PLAIN); + WSContentSend_P(PSTR("%s}1"), AnyModuleName(module).c_str()); // NAME: Generic for (uint8_t i = 0; i < sizeof(kGpioNiceList); i++) { // GPIO: }2'0'>None (0)}3}2'17'>Button1 (17)}3... if (1 == i) { - snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_MODULE_TEMPLATE_REPLACE, 255, D_SENSOR_USER, 255); // }2'255'>User (255)}3 - page += mqtt_data; + WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE, 255, D_SENSOR_USER, 255); // }2'255'>User (255)}3 } uint8_t midx = pgm_read_byte(kGpioNiceList + i); - snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_MODULE_TEMPLATE_REPLACE, midx, GetTextIndexed(stemp, sizeof(stemp), midx, kSensorNames), midx); - page += mqtt_data; + WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE, midx, GetTextIndexed(stemp, sizeof(stemp), midx, kSensorNames), midx); } - page += F("}1"); // Field separator - mqtt_data[0] = '\0'; + WSContentSend_P(PSTR("}1")); // Field separator for (uint8_t i = 0; i < sizeof(cmodule); i++) { // 17,148,29,149,7,255,255,255,138,255,139,255,255 if ((i < 6) || ((i > 8) && (i != 11))) { // Ignore flash pins GPIO06, 7, 8 and 11 - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s%s%d"), mqtt_data, (i>0)?",":"", cmodule.io[i]); + WSContentSend_P(PSTR("%s%d"), (i>0)?",":"", cmodule.io[i]); } } - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s}1%d}1%d"), mqtt_data, flag, Settings.user_template_base); // FLAG: 1 BASE: 17 - page += mqtt_data; - - WSSend(200, CT_PLAIN, page); + WSContentSend_P(PSTR("}1%d}1%d"), flag, Settings.user_template_base); // FLAG: 1 BASE: 17 + WSContentEnd(); return; } @@ -1078,7 +1087,7 @@ void HandleTemplateConfiguration(void) WSContentSend_P(HTTP_FORM_TEMPLATE_FLAG); WSContentSend_P(HTTP_FORM_END); WSContentSpaceButton(BUTTON_CONFIGURATION); - WSContentEnd(); + WSContentStop(); } void TemplateSaveSettings(void) @@ -1132,7 +1141,7 @@ void HandleModuleConfiguration(void) ModuleGpios(&cmodule); if (WebServer->hasArg("m")) { - String page = ""; + WSContentBegin(200, CT_PLAIN); uint8_t vidx = 0; for (uint8_t i = 0; i <= MAXMODULE; i++) { // "}2'%d'>%s (%d)}3" - "}2'255'>UserTemplate (0)}3" - "}2'0'>Sonoff Basic (1)}3" if (0 == i) { @@ -1142,23 +1151,21 @@ void HandleModuleConfiguration(void) midx = pgm_read_byte(kModuleNiceList + i -1); vidx = midx +1; } - snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_MODULE_TEMPLATE_REPLACE, midx, AnyModuleName(midx).c_str(), vidx); - page += mqtt_data; + WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE, midx, AnyModuleName(midx).c_str(), vidx); } - WSSend(200, CT_PLAIN, page); + WSContentEnd(); return; } if (WebServer->hasArg("g")) { - String page = ""; + WSContentBegin(200, CT_PLAIN); for (uint8_t j = 0; j < sizeof(kGpioNiceList); j++) { midx = pgm_read_byte(kGpioNiceList + j); if (!GetUsedInModule(midx, cmodule.io)) { - snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_MODULE_TEMPLATE_REPLACE, midx, GetTextIndexed(stemp, sizeof(stemp), midx, kSensorNames), midx); - page += mqtt_data; + WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE, midx, GetTextIndexed(stemp, sizeof(stemp), midx, kSensorNames), midx); } } - WSSend(200, CT_PLAIN, page); + WSContentEnd(); return; } @@ -1185,7 +1192,7 @@ void HandleModuleConfiguration(void) WSContentSend_P(PSTR("")); WSContentSend_P(HTTP_FORM_END); WSContentSpaceButton(BUTTON_CONFIGURATION); - WSContentEnd(); + WSContentStop(); } void ModuleSaveSettings(void) @@ -1327,7 +1334,7 @@ void HandleWifiConfiguration(void) } else { WSContentSpaceButton(BUTTON_CONFIGURATION); } - WSContentEnd(); + WSContentStop(); } void WifiSaveSettings(void) @@ -1386,7 +1393,7 @@ void HandleLoggingConfiguration(void) WSContentSend_P(HTTP_FORM_LOG2, Settings.syslog_host, Settings.syslog_port, Settings.tele_period); WSContentSend_P(HTTP_FORM_END); WSContentSpaceButton(BUTTON_CONFIGURATION); - WSContentEnd(); + WSContentStop(); } void LoggingSaveSettings(void) @@ -1462,7 +1469,7 @@ void HandleOtherConfiguration(void) WSContentSend_P(HTTP_FORM_END); WSContentSpaceButton(BUTTON_CONFIGURATION); - WSContentEnd(); + WSContentStop(); } void OtherSaveSettings(void) @@ -1558,7 +1565,7 @@ void HandleResetConfiguration(void) WSContentSend_P(PSTR("
" D_CONFIGURATION_RESET "
")); WSContentSend_P(HTTP_MSG_RSTRT); WSContentSpaceButton(BUTTON_MAIN); - WSContentEnd(); + WSContentStop(); char command[CMDSZ]; snprintf_P(command, sizeof(command), PSTR(D_CMND_RESET " 1")); @@ -1576,7 +1583,7 @@ void HandleRestoreConfiguration(void) WSContentSend_P(HTTP_FORM_RST); WSContentSend_P(HTTP_FORM_RST_UPG, D_RESTORE); WSContentSpaceButton(BUTTON_CONFIGURATION); - WSContentEnd(); + WSContentStop(); upload_error = 0; upload_file_type = UPL_SETTINGS; @@ -1678,7 +1685,7 @@ void HandleInformation(void) "
")); // WSContentSend_P(PSTR("
")); WSContentSpaceButton(BUTTON_MAIN); - WSContentEnd(); + WSContentStop(); } #endif // Not FIRMWARE_MINIMAL @@ -1695,7 +1702,7 @@ void HandleUpgradeFirmware(void) WSContentSend_P(HTTP_FORM_UPG, Settings.ota_url); WSContentSend_P(HTTP_FORM_RST_UPG, D_UPGRADE); WSContentSpaceButton(BUTTON_MAIN); - WSContentEnd(); + WSContentStop(); upload_error = 0; upload_file_type = UPL_TASMOTA; @@ -1723,7 +1730,7 @@ void HandleUpgradeFirmwareStart(void) WSContentSend_P(PSTR("
" D_UPGRADE_STARTED " ...
")); WSContentSend_P(HTTP_MSG_RSTRT); WSContentSpaceButton(BUTTON_MAIN); - WSContentEnd(); + WSContentStop(); snprintf_P(command, sizeof(command), PSTR(D_CMND_UPGRADE " 1")); ExecuteWebCommand(command, SRC_WEBGUI); @@ -1770,7 +1777,7 @@ void HandleUploadDone(void) SettingsBufferFree(); WSContentSend_P(PSTR("
")); WSContentSpaceButton(BUTTON_MAIN); - WSContentEnd(); + WSContentStop(); } void HandleUploadLoop(void) @@ -1990,7 +1997,7 @@ void HandleHttpCommand(void) if (!(!strcmp(tmp1, WEB_USERNAME) && !strcmp(tmp2, Settings.web_password))) { valid = 0; } } - String message = F("{\"" D_RSLT_WARNING "\":\""); + WSContentBegin(200, CT_JSON); if (valid) { uint8_t curridx = web_log_index; String svalue = WebServer->arg("cmnd"); @@ -1998,7 +2005,8 @@ void HandleHttpCommand(void) ExecuteWebCommand((char*)svalue.c_str(), SRC_WEBCOMMAND); if (web_log_index != curridx) { uint8_t counter = curridx; - message = F("{"); + WSContentSend_P(PSTR("{")); + bool cflg = false; do { char* tmp; size_t len; @@ -2007,28 +2015,28 @@ void HandleHttpCommand(void) // [14:49:36 MQTT: stat/wemos5/RESULT = {"POWER":"OFF"}] > [{"POWER":"OFF"}] char* JSON = (char*)memchr(tmp, '{', len); if (JSON) { // Is it a JSON message (and not only [15:26:08 MQT: stat/wemos5/POWER = O]) - if (message.length() > 1) { message += F(","); } size_t JSONlen = len - (JSON - tmp); if (JSONlen > sizeof(mqtt_data)) { JSONlen = sizeof(mqtt_data); } - strlcpy(mqtt_data, JSON +1, JSONlen -2); - message += mqtt_data; + char stemp[JSONlen]; + strlcpy(stemp, JSON +1, JSONlen -2); + WSContentSend_P(PSTR("%s%s"), (cflg) ? "," : "", stemp); + cflg = true; } } counter++; if (!counter) counter++; // Skip 0 as it is not allowed } while (counter != web_log_index); - message += F("}"); + WSContentSend_P(PSTR("}")); } else { - message += F(D_ENABLE_WEBLOG_FOR_RESPONSE "\"}"); + WSContentSend_P(PSTR("{\"" D_RSLT_WARNING "\":\"" D_ENABLE_WEBLOG_FOR_RESPONSE "\"}")); } } else { - message += F(D_ENTER_COMMAND " cmnd=\"}"); + WSContentSend_P(PSTR("{\"" D_RSLT_WARNING "\":\"" D_ENTER_COMMAND " cmnd=\"}")); } } else { - message += F(D_NEED_USER_AND_PASSWORD "\"}"); + WSContentSend_P(PSTR("{\"" D_RSLT_WARNING "\":\"" D_NEED_USER_AND_PASSWORD "\"}")); } - WSHeaderSend(); - WSSend(200, CT_JSON, message); + WSContentEnd(); } /*-------------------------------------------------------------------------------------------*/ @@ -2049,7 +2057,7 @@ void HandleConsole(void) WSContentSendStyle(); WSContentSend_P(HTTP_FORM_CMND); WSContentSpaceButton(BUTTON_MAIN); - WSContentEnd(); + WSContentStop(); } void HandleConsoleRefresh(void) @@ -2067,10 +2075,8 @@ void HandleConsoleRefresh(void) WebGetArg("c2", stmp, sizeof(stmp)); if (strlen(stmp)) { counter = atoi(stmp); } - bool last_reset_web_log_flag = reset_web_log_flag; - // mqtt_data used as scratch space - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%d}1%d}1"), web_log_index, last_reset_web_log_flag); - String message = mqtt_data; + WSContentBegin(200, CT_PLAIN); + WSContentSend_P(PSTR("%d}1%d}1"), web_log_index, reset_web_log_flag); if (!reset_web_log_flag) { counter = 0; reset_web_log_flag = true; @@ -2085,21 +2091,18 @@ void HandleConsoleRefresh(void) size_t len; GetLog(counter, &tmp, &len); if (len) { - if (cflg) { - message += F("\n"); - } else { - cflg = true; - } if (len > sizeof(mqtt_data) -2) { len = sizeof(mqtt_data); } - strlcpy(mqtt_data, tmp, len); - message += mqtt_data; // mqtt_data used as scratch space + char stemp[len +1]; + strlcpy(stemp, tmp, len); + WSContentSend_P(PSTR("%s%s"), (cflg) ? "\n" : "", stemp); + cflg = true; } counter++; - if (!counter) { counter++; } // Skip log index 0 as it is not allowed + if (!counter) { counter++; } // Skip log index 0 as it is not allowed } while (counter != web_log_index); } - message += F("}1"); - WSSend(200, CT_PLAIN, message); + WSContentSend_P(PSTR("}1")); + WSContentEnd(); } /********************************************************************************************/ diff --git a/sonoff/xdrv_02_mqtt.ino b/sonoff/xdrv_02_mqtt.ino index 6330259fd..57086d46e 100644 --- a/sonoff/xdrv_02_mqtt.ino +++ b/sonoff/xdrv_02_mqtt.ino @@ -774,7 +774,7 @@ void HandleMqttConfiguration(void) Settings.mqtt_fulltopic); WSContentSend_P(HTTP_FORM_END); WSContentSpaceButton(BUTTON_CONFIGURATION); - WSContentEnd(); + WSContentStop(); } void MqttSaveSettings(void) diff --git a/sonoff/xdrv_07_domoticz.ino b/sonoff/xdrv_07_domoticz.ino index c2020176d..799b2d4b8 100644 --- a/sonoff/xdrv_07_domoticz.ino +++ b/sonoff/xdrv_07_domoticz.ino @@ -482,7 +482,7 @@ void HandleDomoticzConfiguration(void) WSContentSend_P(PSTR("")); WSContentSend_P(HTTP_FORM_END); WSContentSpaceButton(BUTTON_CONFIGURATION); - WSContentEnd(); + WSContentStop(); } void DomoticzSaveSettings(void) diff --git a/sonoff/xdrv_09_timers.ino b/sonoff/xdrv_09_timers.ino index 9a85ad3c4..a3ab51a1b 100644 --- a/sonoff/xdrv_09_timers.ino +++ b/sonoff/xdrv_09_timers.ino @@ -721,7 +721,7 @@ void HandleTimerConfiguration(void) WSContentSend_P(HTTP_FORM_TIMER4); WSContentSend_P(HTTP_FORM_END); WSContentSpaceButton(BUTTON_CONFIGURATION); - WSContentEnd(); + WSContentStop(); } void TimerSaveSettings(void) diff --git a/sonoff/xdrv_11_knx.ino b/sonoff/xdrv_11_knx.ino index 5ab0c7c88..45c9fe15f 100644 --- a/sonoff/xdrv_11_knx.ino +++ b/sonoff/xdrv_11_knx.ino @@ -934,7 +934,7 @@ void HandleKNXConfiguration(void) WSContentSend_P(PSTR("
")); WSContentSend_P(HTTP_FORM_END); WSContentSpaceButton(BUTTON_CONFIGURATION); - WSContentEnd(); + WSContentStop(); } } diff --git a/sonoff/xsns_34_hx711.ino b/sonoff/xsns_34_hx711.ino index 2072dc496..c287ef3f4 100644 --- a/sonoff/xsns_34_hx711.ino +++ b/sonoff/xsns_34_hx711.ino @@ -434,7 +434,7 @@ void HandleHxAction(void) WSContentSend_P(HTTP_FORM_HX711, stemp1, stemp2); WSContentSend_P(HTTP_FORM_END); WSContentSpaceButton(BUTTON_CONFIGURATION); - WSContentEnd(); + WSContentStop(); } void HxSaveSettings(void) From 54032d6c5404131d3e6813bc2361bd617c697b64 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Tue, 19 Mar 2019 11:14:39 +0100 Subject: [PATCH 015/428] Prep for 6.5 Prep for 6.5 --- RELEASENOTES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 0a81de711..e18a076a2 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -100,9 +100,9 @@ The following binary downloads have been compiled with ESP8266/Arduino library c - **sonoff-classic.bin** = The Classic version allows initial installation using either WifiManager, Wps or SmartConfig. - **sonoff-BG.bin** to **sonoff-TW.bin** = The Sonoff version without Wps and SmartConfig configuration in different languages. - **sonoff-knx.bin** = The Knx version without Wps and SmartConfig configuration and some other features but adds KNX support. -- **sonoff-minimal.bin** = The Minimal version allows intermediate OTA uploads to support larger versions and does NOT change any persistent parameter. This version **should NOT be used for initial installation**. - **sonoff-sensors.bin** = The Sensors version without Wps and SmartConfig configuration but adds even more useful sensors. - **sonoff-display.bin** = The Display version without Wps and SmartConfig configuration and Energy Monitoring but adds display support. +- **sonoff-minimal.bin** = The Minimal version allows intermediate OTA uploads to support larger versions and does NOT change any persistent parameter. This version **should NOT be used for initial installation**. Core version **2.4.2** binaries can be found at http://thehackbox.org/tasmota/ From 0aa220f33b4929382c819477a94174b4b1d8b140 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Tue, 19 Mar 2019 11:26:44 +0100 Subject: [PATCH 016/428] Prep for 6.5 Prep for 6.5 --- sonoff/_changelog.ino | 193 ++++++++++++++---------------------------- 1 file changed, 65 insertions(+), 128 deletions(-) diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index e0770b742..68d677454 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -1,144 +1,81 @@ /* 6.5.0 20190319 - * New Release - * - * 6.4.2.22 20190315 + * Remove commands SetOption14 and SetOption63 as it has been superseded by command Interlock + * Remove command SetOption35 0-255 for mDNS start-up delay (#4793) * Remove support for MQTT_LIBRARY_TYPE, MQTT_ARDUINOMQTT and MQTT_TASMOTAMQTT (#5474) - * - * 6.4.1.21 20190309 - * Fix exception on GUI Configure Logging and Configure Other (#5424) - * Add support for sensor SCD30 (#5434) - * Add support for commands in sensor drivers - * Add 0x to IRRemote (SetOption29) and RCSwitch (SetOption28) received hexadecimal data (#5431) - * Add button control when no relay configured (#4682) - * - * 6.4.1.20 20190304 - * Changed webserver content handling from single String to small Chunks increasing RAM - * Changed logging message handling + * Change webserver content handling from single String to small Chunks increasing RAM + * Change code use of boolean to bool and byte to uint8_t + * Change code uint8_t flags to bool flags + * Change sonoff_template.h layout regarding optional module flags like ADC0 + * Change sonoff_template.h module lay-out by removing non-configurable GPIOs + * Change button driver making it modular + * Change switch driver making it modular and introduce input filter (#4665, #4724) + * Change switch input detection by optimizing switch debounce (#4724) + * Change web authentication (#4865) + * Change image name BE_MINIMAL to FIRMWARE_MINIMAL and USE_xyz to FIRMWARE_xyz (#5106) + * Change GUI weblog from XML to plain text solving possible empty screens (#5154) + * Fix most compiler warnings + * Fix Display exception 28 when JSON value is NULL received + * Fix epaper driver (#4785) + * Fix HAss Sensor Discovery Software Watchdog restart (#4831, #4988) + * Fix allowable MAX_RULE_VARS to 16 (#4933) + * Fix mDNS addService (#4938, #4951) + * Fix HAss discovery of MHZ19(B) sensors (#4992) + * Fix some exceptions and watchdogs due to lack of stack space (#5215) + * Fix GUI wifi password acception starting with asteriks (*) (#5231, #5242) + * Fix command WebSend intermittent results (#5273, #5304) * Fix additional characters in fallbacktopic, hostname and mqttclient on core 2.5.0 (#5359, #5417) - * Add command Template 255 to copy module configuration over to current active template and store as user template named Merged (#5371) - * - * 6.4.1.19 20190222 - * Add command SetOption37 for RGBCW color mapping (#5326) - * Add Korean language translations (#5344) * Fix Energy TotalStartTime when commands EnergyReset0 and/or EnergyReset3 used (#5373) * Fix DS18S20 temperature calculation (#5375) * Fix float calculations in range from 0 to -1 (#5386) - * - * 6.4.1.18 20190221 - * Fix some exceptions and watchdogs due to lack of stack space - part 1 (#5215) - * Fix some exceptions and watchdogs due to lack of stack space - part 2 - * Add command SetOption62 0/1 to disable retain on Button or Swith hold messages (#5299) - * Add option WifiConfig 7 to allow reset of device in AP mode without admin password (#5297) - * Fix command WebSend when using a port number as regression from 6.4.1.17 (#5304) - * - * 6.4.1.17 20190214 - * Change template update by removing possibility to add user module config keeping template as defined (#5222) - * Fix regression from 6.4.1.16 where GPIO9 and GPIO10 connected devices did not work (#5197) - * Fix GUI wifi password acception starting with asteriks (*) (#5231, #5242) - * Add rule expression enabled by define USE_EXPRESSION in my_user_config.h (#5210) - * Add Configure Template menu option to GUI (#5222) - * Remove command SetOption62 as it's functionality is replaced by user changing the device template (#5255) - * Add property LinkCount to state and status 11 message representing number of Wifi Link re-connections - * Add property MqttCount to status 6 message representing number of Mqtt re-connections - * Add property Downtime to state and status 11 message representing the duration of wifi connection loss - * Fix command WebSend intermittent results (#5273) - * - * 6.4.1.16 20190211 - * Initial support for online template change using command Template or GUI Configure Other (#5177) - * Add parameter CFG_HOLDER to status 1 message (#5206) - * Update GUI - * - * 6.4.1.15 20190208 - * Change image name BE_MINIMAL to FIRMWARE_MINIMAL (#5106) - * Change image names USE_xyz to FIRMWARE_xyz (#5106) + * Fix exception on GUI Configure Logging and Configure Other (#5424) + * Add commands PowerCal, VoltageCal and CurrentCal for HLW8012, HJL01 and BL0937 based energy sensors * Add command SerialDelimiter 128 to filter reception of only characters between ASCII 32 and 127 (#5131) - * Add status message to former declined group commands (#5145) - * - * 6.4.1.14 20190203 - * Add SetOption32 until SetOption49 diagnostic information to Status 3 report as replacement for second property value in SetOption property name - * Add Resolution property to Status 3 report providing previous SetOption second value property - * Fix IR local echo - * Add user configuration of HLW8012 and HJL-01/BL0937 Energy Monitoring as used in Sonoff S31, Pow Ra and many Tuya based devices - * Add user configuration of MCP39F501 Energy Monitoring as used in Shelly2 - * Add support for multiple ADS1115 I2C devices (#5083) - * Add rule support for "==", "!=" ">=" and "<=" (#5122) - * Add Hass status sensor (#5139) - * Change GUI weblog solving possible empty screens (#5154) - * Change PN532 support from I2C to Serial for more stability (#5162) - * Add MHZ19 Temperature as Domoticz Temperature selection (#5128) - * - * 6.4.1.13 20190130 + * Add command SSerialSend5 \ to SerialBridge + * Add command Interlock 0 / 1 / 1,2 3,4 .. to control interlock ON/OFF and add up to 8 relays in 1 to 4 interlock groups (#4910, #5014) + * Add command Template 255 to copy module configuration over to current active template and store as user template named Merged (#5371) + * Add command WifiConfig 7 to allow reset of device in AP mode without admin password (#5297) * Add command SetOption36 to control boot loop default restoration (#4645, #5063) - * Add resiliency to saved Settings (#5065) - * - * 6.4.1.12 20190128 - * Change code use of boolean to bool and byte to uint8_t - * Change code uint8_t flags to bool flags - * - * 6.4.1.11 20190124 - * Remove command SetOption14 as it has been superseded by command Interlock - * Remove command SetOption63 as it has been superseded by command Interlock - * Add command Interlock 0 / 1 / 1,2 3,4 .. to control interlock ON/OFF and add up to 8 relays in 1 to 4 interlock groups (#5014) - * Add core version conditional compile options to provided PWM files (#4917) - * Add support for inverted buttons and inverted buttons without pullup (#4914) - * - * 6.4.1.10 20190121 - * Fix Hass discovery of MHZ19(B) sensors (#4992) - * Fix Hass Software Watchdog exception during discovery (#4988) - * Add support for MAX44009 Ambient Light sensor (#4907) - * - * 6.4.1.9 20190115 - * Add support for Mi LED Desk Lamp with rotary switch (#4887) - * Fix mDNS addService (#4938, #4951) - * Fix allowable MAX_RULE_VARS to 16 (#4933) - * Add (S)SerialSend3 escape sequence \x to allow hexadecimal byte value (#3560, #4947) - * Add SerialBridge command SSerialSend5 - * - * 6.4.1.8 20190107 - * Change sonoff_template.h layout regarding optional module flags like ADC0 - * Add command SetOption62 1 to force no Button/Switch pullup on dedicated modules. Currently only supported on Shelly2 (#4841) - * Fix Display exception 28 when JSON value is NULL received - * Fix Home Assistant Sensor Discovery Software Watchdog restart (#4831) + * Add command SetOption37 for RGBCW color mapping (#5326) + * Add command SetOption55 0/1 and define MDNS_ENABLE to disable/enable mDNS (#4793, #4923) + * Add command SetOption62 0/1 to disable retain on Button or Switch hold messages (#5299) + * Add support for Smanergy KA10 Smart Wall Socket with Energy monitoring + * Add support for commands in sensor drivers + * Add support for MAX31855 K-Type thermocouple sensor using softSPI (#4764) + * Add support for Near Field Communication (NFC) controller PN532 using Serial (#4791, #5162) * Add support for OBI Power Socket 2 (#4829) * Add support for YTF IR Bridge (#4855) - * Change web authentication (#4865) + * Add support for Mi LED Desk Lamp with rotary switch (#4887) * Add support for Digoo DG-SP202 Smart Socket with Energy monitoring (#4891) - * Add support for Smanergy KA10 Smart Wall Socket with Energy monitoring + * Add support for MAX44009 Ambient Light sensor (#4907) + * Add support for inverted buttons and inverted buttons without pullup (#4914) * Add support for Luminea ZX2820 Smart Socket with Energy monitoring (#4921) - * Add define MDNS_ENABLE to control initial mDNS state (#4923) - * Add split interlock part 1 (#4910) - * - * 6.4.1.7 20190106 - * Fix HLW8012, HJL01 and BL0937 based energy sensors low Power (below 10W) measurement regression from 6.4.1.6 - * Add Power status functionality to LED2 when configured leaving LED1 for Link status indication - * Add no pull-up control to Shelly 2 module (default is pull-up, change GPIO2 to Switch3n for no pull-up) (#4841) - * Add 4 seconds startup delay to button control (#4829) - * Change button driver making it modular - * - * 6.4.1.6 20190105 - * Add commands PowerCal, VoltageCal and CurrentCal for HLW8012, HJL01 and BL0937 based energy sensors - * - * 6.4.1.5 20190103 - * Remove command SetOption35 0-255 for mDNS start-up delay (#4793) - * Add command SetOption55 0/1 to disable/enable mDNS (#4793) - * - * 6.4.1.4 20190101 - * Update Copyright (C) 2019 - * Fix epaper driver (#4785) - * Add support for Near Field Communication (NFC) controller PN532 using I2C (#4791) - * - * 6.4.1.3 20181229 - * Change sonoff_template.h module lay-out by removing non-configurable GPIOs - * Add support for MAX31855 K-Type thermocouple sensor using softSPI (#4764) - * - * 6.4.1.2 20181228 - * Change switch driver making it modular and introduce input filter (#4665, #4724) - * Add define DS18B20_INTERNAL_PULLUP to select internal input pullup when only one DS18B20 sensor is connected eliminating external resistor (#4738) + * Add support for multiple ADS1115 I2C devices (#5083) + * Add support for online template change using command Template or GUI Configure Other (#5177) + * Add support for Korean language translations (#5344) + * Add support for sensor SCD30 (#5434) + * Add parameter CFG_HOLDER to status 1 message (#5206) + * Add SetOption32 until SetOption49 diagnostic information to Status 3 report as replacement for second property value in SetOption property name + * Add Resolution property to Status 3 report providing previous SetOption second value property + * Add property MqttCount to status 6 message representing number of Mqtt re-connections + * Add property LinkCount to state and status 11 message representing number of Wifi Link re-connections + * Add property Downtime to state and status 11 message representing the duration of wifi connection loss * Add variable %timestamp% to rules (#4749) - * - * 6.4.1.1 20181224 - * Fix most compiler warnings - * Change switch input detection by optimizing switch debounce (#4724) + * Add rule support for "==", "!=" ">=" and "<=" (#5122) + * Add rule expression enabled by define USE_EXPRESSION in my_user_config.h (#5210) + * Add Power status functionality to LED2 when configured leaving LED1 for Link status indication + * Add user configuration of HLW8012 and HJL-01/BL0937 Energy Monitoring as used in Sonoff Pow and many Tuya based devices + * Add user configuration of MCP39F501 Energy Monitoring as used in Shelly2 + * Add online template configuration using both commands and Configure Template menu option in GUI + * Add (S)SerialSend3 escape sequence \x to allow hexadecimal byte value (#3560, #4947) + * Add define DS18B20_INTERNAL_PULLUP to select internal input pullup when only one DS18B20 sensor is connected eliminating external resistor (#4738) + * Add button control when no relay configured (#4682) + * Add startup delay of 4 seconds to button control (#4829) + * Add core version conditional compile options to provided PWM files (#4917) + * Add resiliency to saved Settings (#5065) + * Add MHZ19 Temperature as Domoticz Temperature selection (#5128) + * Add HAss status sensor (#5139) + * Add status message to former declined group commands (#5145) + * Add 0x to IRRemote (SetOption29) and RCSwitch (SetOption28) received hexadecimal data (#5431) * * 6.4.1 20181224 * Change RAM usage BMP/BME I2C sensors From 3ee5a6db82e7fcd2cac9ecc09f56ab0e1526254f Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Tue, 19 Mar 2019 11:52:34 +0100 Subject: [PATCH 017/428] Prep for 6.5 Prep for 6.5 --- RELEASENOTES.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index e18a076a2..4f16b9070 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -7,16 +7,14 @@ See [wiki migration path](https://github.com/arendst/Sonoff-Tasmota/wiki/Upgrade 4. Migrate to **Sonoff-Tasmota 6.x** ## Release notes +### Core version 2.3.0 vs 2.4.2 +This release is based on ESP8266/Arduino library core 2.3.0 (again) as some people encountered wifi related issues on core 2.4.2. For others core 2.4.2 is working just fine. Both version are available from http://thehackbox.org/tasmota/ + ### Change in default initial configuration tool Firmware binary **sonoff-classic.bin** supports **WifiManager, Wps and SmartConfig** for initial configuration. The default tool is **Wps**. To save memory space all other binaries support **WifiManager only**. -See _changelog.ino how to enable them again. -- Define WIFI_CONFIG_TOOL now contains the default behaviour once a SSID has been configured. -- If no SSID is configured making a wifi connection impossible the new define WIFI_CONFIG_NO_SSID will be used. -- While define WIFI_CONFIG_NO_SSID is set to WIFI_WPSCONFIG in my_user_config.h the compiler will check for define USE_WPS and if not enabled WIFI_CONFIG_NO_SSID will default to WIFI_MANAGER using the webserver. If define USE_WEBSERVER is also not enabled WIFI_CONFIG_NO_SSID will default to WIFI_SMARTCONFIG. If define USE_SMARTCONFIG is also not enabled WIFI_CONFIG_NO_SSID will default to a new option WIFI_SERIAL allowing to enter wifi parameters to serial which is always possible. - ## Supported Modules The following hardware modules are supported. From e29f601c5e2fabac8b244b78f281fc30adf022e7 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Tue, 19 Mar 2019 12:21:58 +0100 Subject: [PATCH 018/428] Prep for 6.5 Prep for 6.5 --- sonoff/language/bg-BG.h | 2 +- sonoff/language/cs-CZ.h | 2 +- sonoff/language/de-DE.h | 2 +- sonoff/language/el-GR.h | 2 +- sonoff/language/en-GB.h | 2 +- sonoff/language/es-AR.h | 2 +- sonoff/language/fr-FR.h | 2 +- sonoff/language/he-HE.h | 2 +- sonoff/language/hu-HU.h | 2 +- sonoff/language/it-IT.h | 2 +- sonoff/language/ko-KO.h | 2 +- sonoff/language/nl-NL.h | 2 +- sonoff/language/pl-PL.h | 2 +- sonoff/language/pt-BR.h | 2 +- sonoff/language/pt-PT.h | 2 +- sonoff/language/ru-RU.h | 2 +- sonoff/language/sk-SK.h | 2 +- sonoff/language/sv-SE.h | 2 +- sonoff/language/tr-TR.h | 2 +- sonoff/language/uk-UK.h | 2 +- sonoff/language/zh-CN.h | 2 +- sonoff/language/zh-TW.h | 2 +- 22 files changed, 22 insertions(+), 22 deletions(-) diff --git a/sonoff/language/bg-BG.h b/sonoff/language/bg-BG.h index 6d44af3ff..2214634f9 100644 --- a/sonoff/language/bg-BG.h +++ b/sonoff/language/bg-BG.h @@ -220,7 +220,7 @@ // xdrv_02_webserver.ino #define D_NOSCRIPT "Разрешете JavaScript, за да използвате Tasmota" -#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "Минимален фърмуеър - моля надградете го" +#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "Минимален фърмуеър
моля надградете го" #define D_WEBSERVER_ACTIVE_ON "Уеб сървърът е активен на" #define D_WITH_IP_ADDRESS "с IP адрес" #define D_WEBSERVER_STOPPED "Уеб сървърът е спрян" diff --git a/sonoff/language/cs-CZ.h b/sonoff/language/cs-CZ.h index e4053ea75..41d4fc9de 100644 --- a/sonoff/language/cs-CZ.h +++ b/sonoff/language/cs-CZ.h @@ -220,7 +220,7 @@ // xdrv_02_webserver.ino #define D_NOSCRIPT "Pro používání prostředí Tasmota povolte JavaScript" -#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "Firmware MINIMÁLNÍ - prosím zaktualizujte" +#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "Firmware MINIMÁLNÍ
prosím zaktualizujte" #define D_WEBSERVER_ACTIVE_ON "Aktivní Web server" #define D_WITH_IP_ADDRESS "na IP adrese" #define D_WEBSERVER_STOPPED "Web server zastaven" diff --git a/sonoff/language/de-DE.h b/sonoff/language/de-DE.h index d93f77b89..0808c91c4 100644 --- a/sonoff/language/de-DE.h +++ b/sonoff/language/de-DE.h @@ -220,7 +220,7 @@ // xdrv_02_webserver.ino #define D_NOSCRIPT "JavaScript aktivieren um Tasmota benutzen zu können" -#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "MINIMUM-Firmware - bitte upgraden" +#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "MINIMUM-Firmware
bitte upgraden" #define D_WEBSERVER_ACTIVE_ON "Web-Server aktiv bei" #define D_WITH_IP_ADDRESS "mit IP-Adresse" #define D_WEBSERVER_STOPPED "Web-Server angehalten" diff --git a/sonoff/language/el-GR.h b/sonoff/language/el-GR.h index 8a3fe75a5..0e483759d 100644 --- a/sonoff/language/el-GR.h +++ b/sonoff/language/el-GR.h @@ -220,7 +220,7 @@ // webserver.ino #define D_NOSCRIPT "To use Tasmota, please enable JavaScript" -#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "MINIMAL firmware - παρακαλώ αναβαθμίστε" +#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "MINIMAL firmware
παρακαλώ αναβαθμίστε" #define D_WEBSERVER_ACTIVE_ON "Ενεργός διακομιστής Web στο" #define D_WITH_IP_ADDRESS "με διεύθυνση IP" #define D_WEBSERVER_STOPPED "Ο διακομιστής Web σταμάτησε" diff --git a/sonoff/language/en-GB.h b/sonoff/language/en-GB.h index e459cd02d..d5e792462 100644 --- a/sonoff/language/en-GB.h +++ b/sonoff/language/en-GB.h @@ -220,7 +220,7 @@ // xdrv_02_webserver.ino #define D_NOSCRIPT "To use Tasmota, please enable JavaScript" -#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "MINIMAL firmware - please upgrade" +#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "MINIMAL firmware
please upgrade" #define D_WEBSERVER_ACTIVE_ON "Web server active on" #define D_WITH_IP_ADDRESS "with IP address" #define D_WEBSERVER_STOPPED "Web server stopped" diff --git a/sonoff/language/es-AR.h b/sonoff/language/es-AR.h index 9f2187bbf..baf9838bf 100644 --- a/sonoff/language/es-AR.h +++ b/sonoff/language/es-AR.h @@ -220,7 +220,7 @@ // xdrv_02_webserver.ino #define D_NOSCRIPT "Habilitar JavaScript para usar Tasmota" -#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "Firmware MÍNIMO - actualice por favor" +#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "Firmware MÍNIMO
actualice por favor" #define D_WEBSERVER_ACTIVE_ON "Servidor web activo en" #define D_WITH_IP_ADDRESS "con dirección IP" #define D_WEBSERVER_STOPPED "Servidor web detenido" diff --git a/sonoff/language/fr-FR.h b/sonoff/language/fr-FR.h index 6b26a6212..0b7d29eb7 100644 --- a/sonoff/language/fr-FR.h +++ b/sonoff/language/fr-FR.h @@ -220,7 +220,7 @@ // xdrv_02_webserver.ino #define D_NOSCRIPT "Pour utiliser Tasmota, veuillez activer JavaScript" -#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "Firmware MINIMAL - merci de mettre à jour" +#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "Firmware MINIMAL
merci de mettre à jour" #define D_WEBSERVER_ACTIVE_ON "Serveur web actif sur" #define D_WITH_IP_ADDRESS "avec l'adresse IP" #define D_WEBSERVER_STOPPED "Serveur web éteint" diff --git a/sonoff/language/he-HE.h b/sonoff/language/he-HE.h index 8ce1a3a78..2a876d2b0 100644 --- a/sonoff/language/he-HE.h +++ b/sonoff/language/he-HE.h @@ -62,7 +62,7 @@ #define D_BRIGHTLIGHT "בהירות" #define D_BSSID "BSSId" #define D_BUTTON "לחצן" -#define D_BY "ע" // Written by me +#define D_BY "ע"י" // Written by me #define D_BYTES "בייט" #define D_CELSIUS "צלזיוס" #define D_CHANNEL "ערוץ" diff --git a/sonoff/language/hu-HU.h b/sonoff/language/hu-HU.h index 649796121..e8b818029 100644 --- a/sonoff/language/hu-HU.h +++ b/sonoff/language/hu-HU.h @@ -220,7 +220,7 @@ // xdrv_02_webserver.ino #define D_NOSCRIPT "A Tasmota használatához engedélyezd a Javascriptet!" -#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "MINIMÁLIS firmware - frissítsd!" +#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "MINIMÁLIS firmware
frissítsd!" #define D_WEBSERVER_ACTIVE_ON "Webszerver aktív:" #define D_WITH_IP_ADDRESS "IP cím:" #define D_WEBSERVER_STOPPED "Webszerver leállítva" diff --git a/sonoff/language/it-IT.h b/sonoff/language/it-IT.h index 4d735426f..db8e58475 100644 --- a/sonoff/language/it-IT.h +++ b/sonoff/language/it-IT.h @@ -220,7 +220,7 @@ // xdrv_02_webserver.ino #define D_NOSCRIPT "Abilitare JavaScript per utilizzare Tasmota" -#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "MINIMAL firmware - effettuare aggiornamento" +#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "MINIMAL firmware
effettuare aggiornamento" #define D_WEBSERVER_ACTIVE_ON "Web server attivo su" #define D_WITH_IP_ADDRESS "con indirizzo IP" #define D_WEBSERVER_STOPPED "Web server arrestato" diff --git a/sonoff/language/ko-KO.h b/sonoff/language/ko-KO.h index fb85e5ca2..851d24a8e 100644 --- a/sonoff/language/ko-KO.h +++ b/sonoff/language/ko-KO.h @@ -220,7 +220,7 @@ // xdrv_02_webserver.ino #define D_NOSCRIPT "Tasmota를 사용하려면 JavaScript를 활성화 하십시오." -#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "MINIMAL firmware - 업그레이드가 필요합니다" +#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "MINIMAL firmware
업그레이드가 필요합니다" #define D_WEBSERVER_ACTIVE_ON "Web 서버 작동 중" #define D_WITH_IP_ADDRESS "IP 주소" #define D_WEBSERVER_STOPPED "Web 서버 멈춤" diff --git a/sonoff/language/nl-NL.h b/sonoff/language/nl-NL.h index dd3fb1806..9e428165d 100644 --- a/sonoff/language/nl-NL.h +++ b/sonoff/language/nl-NL.h @@ -220,7 +220,7 @@ // xdrv_02_webserver.ino #define D_NOSCRIPT "Zet JavaScript aan voor Tasmota" -#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "MINIMAL firmware - opwaarderen" +#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "MINIMAL firmware
opwaarderen" #define D_WEBSERVER_ACTIVE_ON "Webserver actief op" #define D_WITH_IP_ADDRESS "met IP adres" #define D_WEBSERVER_STOPPED "Webserver gestopt" diff --git a/sonoff/language/pl-PL.h b/sonoff/language/pl-PL.h index 24e2f60ae..0f13d248a 100644 --- a/sonoff/language/pl-PL.h +++ b/sonoff/language/pl-PL.h @@ -220,7 +220,7 @@ // xdrv_02_webserver.ino #define D_NOSCRIPT "Aby korzystać z Tasmota, włącz obsługę JavaScript" -#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "Oprogramowanie MINIMAL - proszę uaktualnić" +#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "Oprogramowanie MINIMAL
proszę uaktualnić" #define D_WEBSERVER_ACTIVE_ON "Aktywny serwer Web" #define D_WITH_IP_ADDRESS "z adresem IP" #define D_WEBSERVER_STOPPED "Serwer Web zatrzymany" diff --git a/sonoff/language/pt-BR.h b/sonoff/language/pt-BR.h index 3d24e2dbc..63efc3f1e 100644 --- a/sonoff/language/pt-BR.h +++ b/sonoff/language/pt-BR.h @@ -220,7 +220,7 @@ // xdrv_02_webserver.ino #define D_NOSCRIPT "To use Tasmota, please enable JavaScript" -#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "Firmware mínimo - Atualizar por favor" +#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "Firmware mínimo
Atualizar por favor" #define D_WEBSERVER_ACTIVE_ON "Servidor WEB ativo em" #define D_WITH_IP_ADDRESS "com o endereço IP" #define D_WEBSERVER_STOPPED "Servidor WEB parou" diff --git a/sonoff/language/pt-PT.h b/sonoff/language/pt-PT.h index d675efe54..171f33d9b 100644 --- a/sonoff/language/pt-PT.h +++ b/sonoff/language/pt-PT.h @@ -220,7 +220,7 @@ // xdrv_02_webserver.ino #define D_NOSCRIPT "To use Tasmota, please enable JavaScript" -#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "MINIMO firmware - Atualizar Por favor" +#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "MINIMO firmware
Atualizar Por favor" #define D_WEBSERVER_ACTIVE_ON "Servidor WEB ativo em" #define D_WITH_IP_ADDRESS "com o endereço IP" #define D_WEBSERVER_STOPPED "Servitor WEB parou" diff --git a/sonoff/language/ru-RU.h b/sonoff/language/ru-RU.h index 50eabbc35..e4112953c 100644 --- a/sonoff/language/ru-RU.h +++ b/sonoff/language/ru-RU.h @@ -220,7 +220,7 @@ // xdrv_02_webserver.ino #define D_NOSCRIPT "To use Tasmota, please enable JavaScript" -#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "Прошивка MINIMAL - пожалуйста обновите" +#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "Прошивка MINIMAL
пожалуйста обновите" #define D_WEBSERVER_ACTIVE_ON "Веб-сервер активен" #define D_WITH_IP_ADDRESS "с IP-адресом" #define D_WEBSERVER_STOPPED "Веб-сервер остановлен" diff --git a/sonoff/language/sk-SK.h b/sonoff/language/sk-SK.h index 4f741ac51..cf99703c8 100644 --- a/sonoff/language/sk-SK.h +++ b/sonoff/language/sk-SK.h @@ -220,7 +220,7 @@ // xdrv_02_webserver.ino #define D_NOSCRIPT "Pre používanie prostredia Tasmota povoľte JavaScript" -#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "Firmware MINIMÁLNY - prosím aktualizujte" +#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "Firmware MINIMÁLNY
prosím aktualizujte" #define D_WEBSERVER_ACTIVE_ON "Aktívny Web server" #define D_WITH_IP_ADDRESS "na IP adrese" #define D_WEBSERVER_STOPPED "Web server zastavený" diff --git a/sonoff/language/sv-SE.h b/sonoff/language/sv-SE.h index dd6b31469..6a92c1bc1 100644 --- a/sonoff/language/sv-SE.h +++ b/sonoff/language/sv-SE.h @@ -220,7 +220,7 @@ // xdrv_02_webserver.ino #define D_NOSCRIPT "För att använda Tasmota, aktivera JavaScript" -#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "MINIMAL firmware - var god uppgradera" +#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "MINIMAL firmware
var god uppgradera" #define D_WEBSERVER_ACTIVE_ON "Webbserver aktiv på" #define D_WITH_IP_ADDRESS "med IP-adress" #define D_WEBSERVER_STOPPED "Webbserver stoppad" diff --git a/sonoff/language/tr-TR.h b/sonoff/language/tr-TR.h index f6f61ec56..b887c89c2 100755 --- a/sonoff/language/tr-TR.h +++ b/sonoff/language/tr-TR.h @@ -220,7 +220,7 @@ // xdrv_02_webserver.ino #define D_NOSCRIPT "To use Tasmota, please enable JavaScript" -#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "Donanım yazılımı çok düşük, lütfen yükseltin" +#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "Donanım yazılımı çok düşük
lütfen yükseltin" #define D_WEBSERVER_ACTIVE_ON "Web sunucusu aktif" #define D_WITH_IP_ADDRESS "IP adres ile" #define D_WEBSERVER_STOPPED "Web sunucusu durdu" diff --git a/sonoff/language/uk-UK.h b/sonoff/language/uk-UK.h index 71b7665c1..0efdd1c94 100644 --- a/sonoff/language/uk-UK.h +++ b/sonoff/language/uk-UK.h @@ -220,7 +220,7 @@ // xdrv_02_webserver.ino #define D_NOSCRIPT "To use Tasmota, please enable JavaScript" -#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "Прошивка MINIMAL - будь-ласка оновіть" +#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "Прошивка MINIMAL
будь-ласка оновіть" #define D_WEBSERVER_ACTIVE_ON "Веб-сервер активний" #define D_WITH_IP_ADDRESS "з IP-адресом" #define D_WEBSERVER_STOPPED "Веб-сервер зупинений" diff --git a/sonoff/language/zh-CN.h b/sonoff/language/zh-CN.h index 586591278..88767053b 100644 --- a/sonoff/language/zh-CN.h +++ b/sonoff/language/zh-CN.h @@ -220,7 +220,7 @@ // webserver.ino #define D_NOSCRIPT "Tasmota要求浏览器支持 JavaScript" -#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "当前是精简版固件 - 请升级" +#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "当前是精简版固件
请升级" #define D_WEBSERVER_ACTIVE_ON "Web 服务器地址:" #define D_WITH_IP_ADDRESS "IP 地址:" #define D_WEBSERVER_STOPPED "Web 服务已停止" diff --git a/sonoff/language/zh-TW.h b/sonoff/language/zh-TW.h index ae6d4df07..11f85eef2 100644 --- a/sonoff/language/zh-TW.h +++ b/sonoff/language/zh-TW.h @@ -220,7 +220,7 @@ // xdrv_02_webserver.ino #define D_NOSCRIPT "To use Tasmota, please enable JavaScript" -#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "固件版本過低 - 請升級" +#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "固件版本過低
請升級" #define D_WEBSERVER_ACTIVE_ON "Web服務器:" #define D_WITH_IP_ADDRESS "IP地址:" #define D_WEBSERVER_STOPPED "Web 服務器已停止" From 4441fca9da6f2108da5ee3fb68001c208e60fd2d Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Tue, 19 Mar 2019 13:05:00 +0100 Subject: [PATCH 019/428] Update he-HE.h Fix compile error --- sonoff/language/he-HE.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sonoff/language/he-HE.h b/sonoff/language/he-HE.h index 2a876d2b0..8ce1a3a78 100644 --- a/sonoff/language/he-HE.h +++ b/sonoff/language/he-HE.h @@ -62,7 +62,7 @@ #define D_BRIGHTLIGHT "בהירות" #define D_BSSID "BSSId" #define D_BUTTON "לחצן" -#define D_BY "ע"י" // Written by me +#define D_BY "ע" // Written by me #define D_BYTES "בייט" #define D_CELSIUS "צלזיוס" #define D_CHANNEL "ערוץ" From 6b19eb4dbd1e28303edcff5b9ca26c8cb5b6badb Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Tue, 19 Mar 2019 15:03:29 +0100 Subject: [PATCH 020/428] Prep for 6.5 Prep for 6.5 --- RELEASENOTES.md | 2 +- sonoff/language/he-HE.h | 104 ++++++++++++++++++++-------------------- sonoff/sonoff_post.h | 2 + 3 files changed, 55 insertions(+), 53 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 4f16b9070..e363ba9ac 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -199,7 +199,7 @@ Core version **2.4.2** binaries can be found at http://thehackbox.org/tasmota/ | USE_DISPLAY_SSD1306 | - | - | - | - | - | - | x | | USE_DISPLAY_MATRIX | - | - | - | - | - | - | x | | USE_DISPLAY_ILI9341 | - | - | - | - | - | - | x | -| USE_DISPLAY_EPAPER_29 | - | - | - | - | - | - | x | +| USE_DISPLAY_EPAPER_29 | - | - | - | - | - | - | x | Disabled for core 2.3.0 ## Changelog Version 6.5.0 20190319 diff --git a/sonoff/language/he-HE.h b/sonoff/language/he-HE.h index 8ce1a3a78..b26529cf2 100644 --- a/sonoff/language/he-HE.h +++ b/sonoff/language/he-HE.h @@ -54,7 +54,7 @@ #define D_ADMIN "מנהל" #define D_AIR_QUALITY "איכות אוויר" #define D_AP "AP" // Access Point -#define D_AS "as" +#define D_AS "-כ" #define D_AUTO "אוטומטי" #define D_BLINK "מהבהב" #define D_BLINKOFF "כיבוי היבהוב" @@ -62,11 +62,11 @@ #define D_BRIGHTLIGHT "בהירות" #define D_BSSID "BSSId" #define D_BUTTON "לחצן" -#define D_BY "ע" // Written by me +#define D_BY "על ידי" // Written by me #define D_BYTES "בייט" #define D_CELSIUS "צלזיוס" #define D_CHANNEL "ערוץ" -#define D_CO2 "Carbon dioxide" +#define D_CO2 "פחמן דו חמצני" #define D_CODE "קוד" // Button code #define D_COLDLIGHT "אור קר" #define D_COMMAND "פקודה" @@ -112,7 +112,7 @@ #define D_LWT "LWT" #define D_MODULE "מודול" #define D_MQTT "MQTT" -#define D_MULTI_PRESS "multi-press" +#define D_MULTI_PRESS "לחיצה מרובה" #define D_NOISE "רעש" #define D_NONE "כלום" #define D_OFF "כבוי" @@ -132,7 +132,7 @@ #define D_PROGRAM_FLASH_SIZE "גודל תוכנית פלאש" #define D_PROGRAM_SIZE "גודל תוכנית" #define D_PROJECT "פרויקט" -#define D_RAIN "Rain" +#define D_RAIN "גשם" #define D_RECEIVED "התקבל" #define D_RESTART "איתחול" #define D_RESTARTING "הפעלה מחדש" @@ -203,14 +203,14 @@ #define D_IN_MODE "במצב" #define D_CONNECT_FAILED_NO_IP_ADDRESS "IP החיבור נכשל מכיוון שלא התקבלה כתובת" #define D_CONNECT_FAILED_AP_NOT_REACHED "זמין AP החיבור נכשל כיוון שאין" -#define D_CONNECT_FAILED_WRONG_PASSWORD "Connect failed with AP incorrect password" -#define D_CONNECT_FAILED_AP_TIMEOUT "Connect failed with AP timeout" -#define D_ATTEMPTING_CONNECTION "Attempting connection..." -#define D_CHECKING_CONNECTION "Checking connection..." -#define D_QUERY_DONE "Query done. MQTT services found" -#define D_MQTT_SERVICE_FOUND "MQTT service found on" -#define D_FOUND_AT "found at" -#define D_SYSLOG_HOST_NOT_FOUND "Syslog Host not found" +#define D_CONNECT_FAILED_WRONG_PASSWORD "סיסמא שגויה , AP חיבור נכשל ל" +#define D_CONNECT_FAILED_AP_TIMEOUT "פג זמן המתנה , AP חיבור נכשל ל" +#define D_ATTEMPTING_CONNECTION "...מנסה להתחבר" +#define D_CHECKING_CONNECTION "...בודק חיבור" +#define D_QUERY_DONE "MQTT השאילתה נעשתה. נמצאו שירותי" +#define D_MQTT_SERVICE_FOUND "MQTT נמצאו שירותי" +#define D_FOUND_AT "נמצא ב" +#define D_SYSLOG_HOST_NOT_FOUND "לא נמצא Syslog מארח" // settings.ino #define D_SAVED_TO_FLASH_AT "נשמר לפלאש ב" @@ -219,7 +219,7 @@ #define D_ERASED_SECTOR "סקטור מחוק" // xdrv_02_webserver.ino -#define D_NOSCRIPT "JavaScript - כדי להשתמש ב קושחה אסמוטה אנא הפעל" +#define D_NOSCRIPT "JavaScript - כדי להשתמש בקושחת אסמוטה אנא הפעל" #define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "קושחה מינימלית - בבקשה אנא שדרג" #define D_WEBSERVER_ACTIVE_ON "שרת ווב פעיל" #define D_WITH_IP_ADDRESS "IP עם כתובת" @@ -230,7 +230,7 @@ #define D_WIFIMANAGER_SET_ACCESSPOINT "Wifimanager set AccessPoint" #define D_TRYING_TO_CONNECT "מנסה לחבר את ההתקן לרשת" -#define D_RESTART_IN "הפעלה מחדש תןך" +#define D_RESTART_IN "הפעלה מחדש תוך" #define D_SECONDS "שניות" #define D_DEVICE_WILL_RESTART "ההתקן יופעל מחדש בעוד מס' שניות" #define D_BUTTON_TOGGLE "מצב" @@ -286,12 +286,12 @@ #define D_MORE_DEBUG "מיפוי נוסף" #define D_SYSLOG_HOST "Syslog מארח" #define D_SYSLOG_PORT "Syslog פורט" -#define D_TELEMETRY_PERIOD "Telemetry period" +#define D_TELEMETRY_PERIOD "זמן שידור" #define D_OTHER_PARAMETERS "פרמטרים שונים" -#define D_TEMPLATE "Template" -#define D_ACTIVATE "Activate" -#define D_WEB_ADMIN_PASSWORD "סיסמת מנהל - אתר" +#define D_TEMPLATE "תבנית" +#define D_ACTIVATE "הפעל" +#define D_WEB_ADMIN_PASSWORD "סיסמת מנהל" #define D_MQTT_ENABLE "MQTT אפשר" #define D_FRIENDLY_NAME "שם ידידותי" #define D_BELKIN_WEMO "Belkin WeMo" @@ -374,38 +374,38 @@ #define D_WEMO_BASIC_EVENT "WeMo basic event" #define D_WEMO_EVENT_SERVICE "WeMo event service" #define D_WEMO_META_SERVICE "WeMo meta service" -#define D_WEMO_SETUP "WeMo setup" -#define D_RESPONSE_SENT "Response sent" +#define D_WEMO_SETUP "WeMo הגדרת" +#define D_RESPONSE_SENT "תגובה נשלחה" #define D_HUE "Hue" -#define D_HUE_BRIDGE_SETUP "Hue setup" +#define D_HUE_BRIDGE_SETUP "Hue הגדרת" #define D_HUE_API_NOT_IMPLEMENTED "Hue API not implemented" #define D_HUE_API "Hue API" #define D_HUE_POST_ARGS "Hue POST args" #define D_3_RESPONSE_PACKETS_SENT "3 response packets sent" // xdrv_07_domoticz.ino -#define D_DOMOTICZ_PARAMETERS "Domoticz parameters" +#define D_DOMOTICZ_PARAMETERS "Domoticz פרמטרי" #define D_DOMOTICZ_IDX "Idx" #define D_DOMOTICZ_KEY_IDX "Key idx" #define D_DOMOTICZ_SWITCH_IDX "Switch idx" #define D_DOMOTICZ_SENSOR_IDX "Sensor idx" - #define D_DOMOTICZ_TEMP "Temp" - #define D_DOMOTICZ_TEMP_HUM "Temp,Hum" - #define D_DOMOTICZ_TEMP_HUM_BARO "Temp,Hum,Baro" - #define D_DOMOTICZ_POWER_ENERGY "Power,Energy" - #define D_DOMOTICZ_ILLUMINANCE "Illuminance" + #define D_DOMOTICZ_TEMP "טמפרטורה" + #define D_DOMOTICZ_TEMP_HUM "טמפרטורה,לחות" + #define D_DOMOTICZ_TEMP_HUM_BARO "טמפרטורה,לחות,ברומטר" + #define D_DOMOTICZ_POWER_ENERGY "הספק,צריכה" + #define D_DOMOTICZ_ILLUMINANCE "עוצמת אור" #define D_DOMOTICZ_COUNT "Count/PM1" #define D_DOMOTICZ_VOLTAGE "Voltage/PM2.5" #define D_DOMOTICZ_CURRENT "Current/PM10" - #define D_DOMOTICZ_AIRQUALITY "AirQuality" -#define D_DOMOTICZ_UPDATE_TIMER "Update timer" + #define D_DOMOTICZ_AIRQUALITY "איכות אוויר" +#define D_DOMOTICZ_UPDATE_TIMER "עדכן טיימר" // xdrv_09_timers.ino #define D_CONFIGURE_TIMER "הגדרות תזמון" #define D_TIMER_PARAMETERS "פרמטרים עבור תזמון" #define D_TIMER_ENABLE "אפשר תזמון" -#define D_TIMER_ARM "חמש" +#define D_TIMER_ARM "טען" #define D_TIMER_TIME "זמן" #define D_TIMER_DAYS "ימים" #define D_TIMER_REPEAT "חזרות" @@ -413,25 +413,25 @@ #define D_TIMER_ACTION "פעולה" // xdrv_10_knx.ino -#define D_CONFIGURE_KNX "Configure KNX" -#define D_KNX_PARAMETERS "KNX Parameters" -#define D_KNX_GENERAL_CONFIG "General" -#define D_KNX_PHYSICAL_ADDRESS "Physical Address" +#define D_CONFIGURE_KNX "KNX הגדר" +#define D_KNX_PARAMETERS "KNX פרמטרי" +#define D_KNX_GENERAL_CONFIG "כללי" +#define D_KNX_PHYSICAL_ADDRESS "כתובת פיזית" #define D_KNX_PHYSICAL_ADDRESS_NOTE "( Must be unique on the KNX network )" -#define D_KNX_ENABLE "Enable KNX" -#define D_KNX_GROUP_ADDRESS_TO_WRITE "Data to Send to Group Addresses" -#define D_ADD "Add" -#define D_DELETE "Delete" -#define D_REPLY "Reply" -#define D_KNX_GROUP_ADDRESS_TO_READ "Group Addresses to Receive Data from" +#define D_KNX_ENABLE "KNX אפשר" +#define D_KNX_GROUP_ADDRESS_TO_WRITE "נתונים לשליחה אל כתובות קבוצתיות" +#define D_ADD "הוסף" +#define D_DELETE "מחק" +#define D_REPLY "השב" +#define D_KNX_GROUP_ADDRESS_TO_READ "כתובות קבוצתיות לקבלת נתונים מ" #define D_LOG_KNX "KNX: " -#define D_RECEIVED_FROM "Received from" -#define D_KNX_COMMAND_WRITE "Write" -#define D_KNX_COMMAND_READ "Read" -#define D_KNX_COMMAND_OTHER "Other" -#define D_SENT_TO "sent to" +#define D_RECEIVED_FROM "התקבל מאת" +#define D_KNX_COMMAND_WRITE "כתיבה" +#define D_KNX_COMMAND_READ "קריאה" +#define D_KNX_COMMAND_OTHER "אחר" +#define D_SENT_TO "נשלח ל" #define D_KNX_WARNING "The group address ( 0 / 0 / 0 ) is reserved and can not be used." -#define D_KNX_ENHANCEMENT "Communication Enhancement" +#define D_KNX_ENHANCEMENT "שיפור התקשורת" #define D_KNX_TX_SLOT "KNX TX" #define D_KNX_RX_SLOT "KNX RX" @@ -459,7 +459,7 @@ // xsns_18_pms5003.ino #define D_STANDARD_CONCENTRATION "CF-1 PM" // Standard Particle CF-1 Particle Matter #define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter -#define D_PARTICALS_BEYOND "Particals" +#define D_PARTICALS_BEYOND "חלקיקים" // xsns_32_mpu6050.ino #define D_AX_AXIS "Accel. X-Axis" @@ -483,10 +483,10 @@ #define D_CALIBRATION "כיול" //xsns_35_tx20.ino -#define D_TX20_WIND_DIRECTION "Wind Direction" -#define D_TX20_WIND_SPEED "Wind Speed" -#define D_TX20_WIND_SPEED_AVG "Wind Speed Avg" -#define D_TX20_WIND_SPEED_MAX "Wind Speed Max" +#define D_TX20_WIND_DIRECTION "כיוון הרוח" +#define D_TX20_WIND_SPEED "מהירות הרוח" +#define D_TX20_WIND_SPEED_AVG "מהירות הרוח ממוצעת" +#define D_TX20_WIND_SPEED_MAX "מהירות הרוח מקסימלית" #define D_TX20_NORTH "N" #define D_TX20_EAST "E" #define D_TX20_SOUTH "S" diff --git a/sonoff/sonoff_post.h b/sonoff/sonoff_post.h index bcbc8f2ad..095c7e2ac 100755 --- a/sonoff/sonoff_post.h +++ b/sonoff/sonoff_post.h @@ -245,7 +245,9 @@ void KNX_CB_Action(message_t const &msg, void *arg); #define USE_SPI // Hardware SPI using GPIO12(MISO), GPIO13(MOSI) and GPIO14(CLK) in addition to two user selectable GPIOs(CS and DC) #define USE_DISPLAY_ILI9341 // [DisplayModel 4] Enable ILI9341 Tft 480x320 display (+19k code) +#ifndef ARDUINO_ESP8266_RELEASE_2_3_0 // There is not enough spare RAM with core 2.3.0 to support the following #define USE_DISPLAY_EPAPER_29 // [DisplayModel 5] Enable e-paper 2.9 inch display (+19k code) +#endif #undef USE_ARILUX_RF // Remove support for Arilux RF remote controller (-0k8 code, 252 iram (non 2.3.0)) #undef USE_RF_FLASH // Remove support for flashing the EFM8BB1 chip on the Sonoff RF Bridge. C2CK must be connected to GPIO4, C2D to GPIO5 on the PCB (-3k code) From 4719b62b6e074bd6f27184b08e2a69518a99a798 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Tue, 19 Mar 2019 15:08:50 +0100 Subject: [PATCH 021/428] Prep for release 6.5 --- lib/TasmotaMqtt-1.1.1/.gitignore | 28 - lib/TasmotaMqtt-1.1.1/README.md | 8 - .../examples/mqtt_pub/mqtt_pub.ino | 102 -- .../examples/mqtt_sub/mqtt_sub.ino | 97 -- lib/TasmotaMqtt-1.1.1/keywords.txt | 39 - lib/TasmotaMqtt-1.1.1/library.properties | 9 - lib/TasmotaMqtt-1.1.1/src/TasmotaMqtt.cpp | 194 ---- lib/TasmotaMqtt-1.1.1/src/TasmotaMqtt.h | 88 -- lib/TasmotaMqtt-1.1.1/src/mqtt/debug.h | 19 - lib/TasmotaMqtt-1.1.1/src/mqtt/mqtt.c | 997 ------------------ lib/TasmotaMqtt-1.1.1/src/mqtt/mqtt.h | 148 --- lib/TasmotaMqtt-1.1.1/src/mqtt/mqtt_msg.c | 487 --------- lib/TasmotaMqtt-1.1.1/src/mqtt/mqtt_msg.h | 141 --- lib/TasmotaMqtt-1.1.1/src/mqtt/proto.c | 129 --- lib/TasmotaMqtt-1.1.1/src/mqtt/proto.h | 32 - lib/TasmotaMqtt-1.1.1/src/mqtt/queue.c | 75 -- lib/TasmotaMqtt-1.1.1/src/mqtt/queue.h | 44 - lib/TasmotaMqtt-1.1.1/src/mqtt/ringbuf.c | 67 -- lib/TasmotaMqtt-1.1.1/src/mqtt/ringbuf.h | 19 - lib/TasmotaMqtt-1.1.1/src/mqtt/typedef.h | 17 - lib/TasmotaMqtt-1.1.1/src/mqtt/user_config.h | 15 - lib/TasmotaMqtt-1.1.1/src/mqtt/utils.c | 149 --- lib/TasmotaMqtt-1.1.1/src/mqtt/utils.h | 9 - lib/arduino-mqtt-2.4.0/.editorconfig | 7 - lib/arduino-mqtt-2.4.0/.gitignore | 2 - lib/arduino-mqtt-2.4.0/.travis.yml | 46 - lib/arduino-mqtt-2.4.0/CMakeLists.txt | 38 - lib/arduino-mqtt-2.4.0/LICENSE.md | 21 - lib/arduino-mqtt-2.4.0/Makefile | 14 - lib/arduino-mqtt-2.4.0/README.md | 226 ---- .../AdafruitHuzzahESP8266.ino | 69 -- .../AdafruitHuzzahESP8266Secure.ino | 71 -- .../ArduinoEthernetShield.ino | 62 -- .../ArduinoMKRGSM1400/ArduinoMKRGSM1400.ino | 84 -- .../ArduinoMKRGSM1400Secure.ino | 86 -- .../ArduinoWiFi101/ArduinoWiFi101.ino | 70 -- .../ArduinoWiFi101Secure.ino | 75 -- .../ArduinoWiFiShield/ArduinoWiFiShield.ino | 68 -- .../examples/ArduinoYun/ArduinoYun.ino | 60 -- .../ArduinoYunSecure/ArduinoYunSecure.ino | 62 -- .../ESP32DevelopmentBoard.ino | 69 -- .../ESP32DevelopmentBoardSecure.ino | 71 -- lib/arduino-mqtt-2.4.0/library.properties | 9 - lib/arduino-mqtt-2.4.0/src/MQTT.h | 6 - lib/arduino-mqtt-2.4.0/src/MQTTClient.h | 491 --------- lib/arduino-mqtt-2.4.0/src/lwmqtt/client.c | 618 ----------- lib/arduino-mqtt-2.4.0/src/lwmqtt/helpers.c | 249 ----- lib/arduino-mqtt-2.4.0/src/lwmqtt/helpers.h | 137 --- lib/arduino-mqtt-2.4.0/src/lwmqtt/lwmqtt.h | 381 ------- lib/arduino-mqtt-2.4.0/src/lwmqtt/packet.c | 742 ------------- lib/arduino-mqtt-2.4.0/src/lwmqtt/packet.h | 185 ---- lib/arduino-mqtt-2.4.0/src/lwmqtt/string.c | 38 - 52 files changed, 6970 deletions(-) delete mode 100644 lib/TasmotaMqtt-1.1.1/.gitignore delete mode 100644 lib/TasmotaMqtt-1.1.1/README.md delete mode 100644 lib/TasmotaMqtt-1.1.1/examples/mqtt_pub/mqtt_pub.ino delete mode 100644 lib/TasmotaMqtt-1.1.1/examples/mqtt_sub/mqtt_sub.ino delete mode 100644 lib/TasmotaMqtt-1.1.1/keywords.txt delete mode 100644 lib/TasmotaMqtt-1.1.1/library.properties delete mode 100644 lib/TasmotaMqtt-1.1.1/src/TasmotaMqtt.cpp delete mode 100644 lib/TasmotaMqtt-1.1.1/src/TasmotaMqtt.h delete mode 100644 lib/TasmotaMqtt-1.1.1/src/mqtt/debug.h delete mode 100644 lib/TasmotaMqtt-1.1.1/src/mqtt/mqtt.c delete mode 100644 lib/TasmotaMqtt-1.1.1/src/mqtt/mqtt.h delete mode 100644 lib/TasmotaMqtt-1.1.1/src/mqtt/mqtt_msg.c delete mode 100644 lib/TasmotaMqtt-1.1.1/src/mqtt/mqtt_msg.h delete mode 100644 lib/TasmotaMqtt-1.1.1/src/mqtt/proto.c delete mode 100644 lib/TasmotaMqtt-1.1.1/src/mqtt/proto.h delete mode 100644 lib/TasmotaMqtt-1.1.1/src/mqtt/queue.c delete mode 100644 lib/TasmotaMqtt-1.1.1/src/mqtt/queue.h delete mode 100644 lib/TasmotaMqtt-1.1.1/src/mqtt/ringbuf.c delete mode 100644 lib/TasmotaMqtt-1.1.1/src/mqtt/ringbuf.h delete mode 100644 lib/TasmotaMqtt-1.1.1/src/mqtt/typedef.h delete mode 100644 lib/TasmotaMqtt-1.1.1/src/mqtt/user_config.h delete mode 100644 lib/TasmotaMqtt-1.1.1/src/mqtt/utils.c delete mode 100644 lib/TasmotaMqtt-1.1.1/src/mqtt/utils.h delete mode 100644 lib/arduino-mqtt-2.4.0/.editorconfig delete mode 100644 lib/arduino-mqtt-2.4.0/.gitignore delete mode 100644 lib/arduino-mqtt-2.4.0/.travis.yml delete mode 100644 lib/arduino-mqtt-2.4.0/CMakeLists.txt delete mode 100644 lib/arduino-mqtt-2.4.0/LICENSE.md delete mode 100644 lib/arduino-mqtt-2.4.0/Makefile delete mode 100644 lib/arduino-mqtt-2.4.0/README.md delete mode 100644 lib/arduino-mqtt-2.4.0/examples/AdafruitHuzzahESP8266/AdafruitHuzzahESP8266.ino delete mode 100644 lib/arduino-mqtt-2.4.0/examples/AdafruitHuzzahESP8266Secure/AdafruitHuzzahESP8266Secure.ino delete mode 100644 lib/arduino-mqtt-2.4.0/examples/ArduinoEthernetShield/ArduinoEthernetShield.ino delete mode 100644 lib/arduino-mqtt-2.4.0/examples/ArduinoMKRGSM1400/ArduinoMKRGSM1400.ino delete mode 100644 lib/arduino-mqtt-2.4.0/examples/ArduinoMKRGSM1400Secure/ArduinoMKRGSM1400Secure.ino delete mode 100644 lib/arduino-mqtt-2.4.0/examples/ArduinoWiFi101/ArduinoWiFi101.ino delete mode 100644 lib/arduino-mqtt-2.4.0/examples/ArduinoWiFi101Secure/ArduinoWiFi101Secure.ino delete mode 100644 lib/arduino-mqtt-2.4.0/examples/ArduinoWiFiShield/ArduinoWiFiShield.ino delete mode 100644 lib/arduino-mqtt-2.4.0/examples/ArduinoYun/ArduinoYun.ino delete mode 100644 lib/arduino-mqtt-2.4.0/examples/ArduinoYunSecure/ArduinoYunSecure.ino delete mode 100644 lib/arduino-mqtt-2.4.0/examples/ESP32DevelopmentBoard/ESP32DevelopmentBoard.ino delete mode 100644 lib/arduino-mqtt-2.4.0/examples/ESP32DevelopmentBoardSecure/ESP32DevelopmentBoardSecure.ino delete mode 100644 lib/arduino-mqtt-2.4.0/library.properties delete mode 100644 lib/arduino-mqtt-2.4.0/src/MQTT.h delete mode 100644 lib/arduino-mqtt-2.4.0/src/MQTTClient.h delete mode 100644 lib/arduino-mqtt-2.4.0/src/lwmqtt/client.c delete mode 100644 lib/arduino-mqtt-2.4.0/src/lwmqtt/helpers.c delete mode 100644 lib/arduino-mqtt-2.4.0/src/lwmqtt/helpers.h delete mode 100644 lib/arduino-mqtt-2.4.0/src/lwmqtt/lwmqtt.h delete mode 100644 lib/arduino-mqtt-2.4.0/src/lwmqtt/packet.c delete mode 100644 lib/arduino-mqtt-2.4.0/src/lwmqtt/packet.h delete mode 100644 lib/arduino-mqtt-2.4.0/src/lwmqtt/string.c diff --git a/lib/TasmotaMqtt-1.1.1/.gitignore b/lib/TasmotaMqtt-1.1.1/.gitignore deleted file mode 100644 index 2ee75414c..000000000 --- a/lib/TasmotaMqtt-1.1.1/.gitignore +++ /dev/null @@ -1,28 +0,0 @@ -# C++ objects and libs - -*.slo -*.lo -*.o -#*.a -*.la -*.lai -*.so -*.dll -*.dylib - -#Makefile -*-build-* -build-* -*.autosave - -# .log files (usually created by QtTest - thanks to VestniK) -*.log - - -# Editors temporary files -*~ - - -#OSX -.DS_Store -._* diff --git a/lib/TasmotaMqtt-1.1.1/README.md b/lib/TasmotaMqtt-1.1.1/README.md deleted file mode 100644 index db197299e..000000000 --- a/lib/TasmotaMqtt-1.1.1/README.md +++ /dev/null @@ -1,8 +0,0 @@ -MQTT -==== - -A Wrapper around mqtt for Arduino to be used with esp8266 modules. - -It wraps a slightly modified version of mqtt for esp8266 ported by Tuan PM. -Original code for esp: https://github.com/tuanpmt/esp_mqtt -Original code for contiki: https://github.com/esar/contiki-mqtt diff --git a/lib/TasmotaMqtt-1.1.1/examples/mqtt_pub/mqtt_pub.ino b/lib/TasmotaMqtt-1.1.1/examples/mqtt_pub/mqtt_pub.ino deleted file mode 100644 index 17b3be8db..000000000 --- a/lib/TasmotaMqtt-1.1.1/examples/mqtt_pub/mqtt_pub.ino +++ /dev/null @@ -1,102 +0,0 @@ -#include -#include - -void myDataCb(String& topic, String& data); -void myPublishedCb(); -void myDisconnectedCb(); -void myConnectedCb(); - -#define CLIENT_ID "client1" - -// create MQTT object -MQTT myMqtt(CLIENT_ID, "192.168.0.1", 1883); - -// -const char* ssid = "ssid"; -const char* password = "ssid_password"; - - -// -void setup() { - Serial.begin(115200); - delay(1000); - - Serial.println(); - Serial.println(); - Serial.print("Connecting to "); - Serial.println(ssid); - - WiFi.begin(ssid, password); - - while (WiFi.status() != WL_CONNECTED) { - delay(500); - Serial.print("."); - } - - Serial.println(""); - Serial.println("WiFi connected"); - Serial.println("IP address: "); - Serial.println(WiFi.localIP()); - - Serial.println("Connecting to MQTT server"); - - // setup callbacks - myMqtt.onConnected(myConnectedCb); - myMqtt.onDisconnected(myDisconnectedCb); - myMqtt.onPublished(myPublishedCb); - myMqtt.onData(myDataCb); - - Serial.println("connect mqtt..."); - myMqtt.connect(); - - delay(10); -} - -// -void loop() { - - int value = analogRead(A0); - - String topic("/"); - topic += CLIENT_ID; - topic += "/value"; - - String valueStr(value); - - // publish value to topic - boolean result = myMqtt.publish(topic, valueStr); - - delay(1000); -} - - -/* - * - */ -void myConnectedCb() -{ - Serial.println("connected to MQTT server"); -} - -void myDisconnectedCb() -{ - Serial.println("disconnected. try to reconnect..."); - delay(500); - myMqtt.connect(); -} - -void myPublishedCb() -{ - //Serial.println("published."); -} - -void myDataCb(String& topic, String& data) -{ - - Serial.print(topic); - Serial.print(": "); - Serial.println(data); -} - - - diff --git a/lib/TasmotaMqtt-1.1.1/examples/mqtt_sub/mqtt_sub.ino b/lib/TasmotaMqtt-1.1.1/examples/mqtt_sub/mqtt_sub.ino deleted file mode 100644 index e88e0a7bb..000000000 --- a/lib/TasmotaMqtt-1.1.1/examples/mqtt_sub/mqtt_sub.ino +++ /dev/null @@ -1,97 +0,0 @@ -#include -#include - -// This needs testing - -void myDataCb(char* topic, uint8_t* data, unsigned int data_len); -void myPublishedCb(); -void myDisconnectedCb(); -void myConnectedCb(); - -#define CLIENT_ID "client3" -#define TOPIC "/client1/value" - -// create MQTT -TasmotaMqtt myMqtt(); - -const char* ssid = "ssid"; -const char* password = "ssid_password"; - -// -void setup() { - Serial.begin(115200); - delay(1000); - - Serial.println(); - Serial.println(); - Serial.print("Connecting to "); - Serial.println(ssid); - - WiFi.begin(ssid, password); - - while (WiFi.status() != WL_CONNECTED) { - delay(500); - Serial.print("."); - } - - Serial.println(""); - Serial.println("WiFi connected"); - Serial.println("IP address: "); - Serial.println(WiFi.localIP()); - - - Serial.println("Connecting to MQTT server"); - - myMqtt.InitConnection("192.168.0.1", 1883); - myMqtt.InitClient(CLIENT_ID, "", ""); - myMqtt.InitLWT("/lwt", "offline"); - - // setup callbacks - myMqtt.OnConnected(myConnectedCb); - myMqtt.OnDisconnected(myDisconnectedCb); - myMqtt.OnPublished(myPublishedCb); - myMqtt.OnData(myDataCb); - - Serial.println("connect mqtt..."); - myMqtt.Connect(); - - Serial.println("subscribe to topic..."); - myMqtt.Subscribe(TOPIC); - - delay(10); -} - -// -void loop() { -} - - -/* - * - */ -void myConnectedCb() -{ - Serial.println("connected to MQTT server"); -} - -void myDisconnectedCb() -{ - Serial.println("disconnected. try to reconnect..."); - delay(500); - myMqtt.Connect(); -} - -void myPublishedCb() -{ - //Serial.println("published."); -} - -void myDataCb(char* topic, uint8_t* data, unsigned int data_len) -{ - Serial.print(topic); - Serial.print(": "); - Serial.println(data); -} - - - diff --git a/lib/TasmotaMqtt-1.1.1/keywords.txt b/lib/TasmotaMqtt-1.1.1/keywords.txt deleted file mode 100644 index 198919125..000000000 --- a/lib/TasmotaMqtt-1.1.1/keywords.txt +++ /dev/null @@ -1,39 +0,0 @@ -####################################### -# Syntax Coloring Map For Test -####################################### - -####################################### -# Datatypes (KEYWORD1) -####################################### - -TasmotaMqtt.h KEYWORD1 -TasmotaMqtt KEYWORD1 - -####################################### -# Methods and Functions (KEYWORD2) -####################################### - -InitConnection KEYWORD2 -InitClient KEYWORD2 -InitLWT KEYWORD2 - -Connect KEYWORD2 -Disconnect KEYWORD2 -Connected KEYWORD2 - -Publish KEYWORD2 -Subscribe KEYWORD2 - -#general -OnConnected KEYWORD2 -OnDisconnected KEYWORD2 -OnData KEYWORD2 - -####################################### -# Instances (KEYWORD2) -####################################### - -####################################### -# Constants (LITERAL1) -####################################### - diff --git a/lib/TasmotaMqtt-1.1.1/library.properties b/lib/TasmotaMqtt-1.1.1/library.properties deleted file mode 100644 index 5a3802f35..000000000 --- a/lib/TasmotaMqtt-1.1.1/library.properties +++ /dev/null @@ -1,9 +0,0 @@ -name=TasmotaMqtt -version=1.0.0 -author=Theo Arends -maintainer=Theo Arends -sentence=A Wrapper around mqtt for Arduino to be used with esp8266 modules. -paragraph=It wraps a slightly modified version of mqtt for esp8266 ported by Tuan PM. Original code for esp: https://github.com/tuanpmt/esp_mqtt Original code for contiki: https://github.com/esar/contiki-mqtt -category=Communication -url= -architectures=esp8266 \ No newline at end of file diff --git a/lib/TasmotaMqtt-1.1.1/src/TasmotaMqtt.cpp b/lib/TasmotaMqtt-1.1.1/src/TasmotaMqtt.cpp deleted file mode 100644 index 9beddd452..000000000 --- a/lib/TasmotaMqtt-1.1.1/src/TasmotaMqtt.cpp +++ /dev/null @@ -1,194 +0,0 @@ -/* - TasmotaMqtt.cpp - Wrapper for mqtt for esp8266 by Tuan PM for Tasmota - - Copyright (C) 2018 Theo Arends and Ingo Randolf - - This library is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#include "TasmotaMqtt.h" - -#include "user_interface.h" -#include "osapi.h" -#include "os_type.h" - -/*********************************************************************************************\ - * Prerequisite - * - * Copy .c and .h files from https://github.com/tuanpmt/esp_mqtt folder mqtt to folder mqtt - * - Replace BOOL with bool - * - Remove variables certificate and private_key from file mqtt.c - * - Add file user_config.h with default defines for - * MQTT_BUF_SIZE 256, MQTT_RECONNECT_TIMEOUT 5, QUEUE_BUFFER_SIZE 2048 and PROTOCOL_NAMEv311 -\*********************************************************************************************/ - -/*********************************************************************************************\ - * Mqtt internal callbacks -\*********************************************************************************************/ - -static void mqttConnectedCb(uint32_t *args) -{ - MQTT_Client* client = (MQTT_Client*)args; - TasmotaMqtt* _this = (TasmotaMqtt*)client->user_data; - if (_this && _this->onMqttConnectedCb) _this->onMqttConnectedCb(); -} - -static void mqttDisconnectedCb(uint32_t *args) -{ - MQTT_Client* client = (MQTT_Client*)args; - TasmotaMqtt* _this = (TasmotaMqtt*)client->user_data; - if (_this && _this->onMqttDisconnectedCb) _this->onMqttDisconnectedCb(); -} - -static void mqttPublishedCb(uint32_t *args) -{ - MQTT_Client* client = (MQTT_Client*)args; - TasmotaMqtt* _this = (TasmotaMqtt*)client->user_data; - if (_this && _this->onMqttPublishedCb) _this->onMqttPublishedCb(); -} - -static void mqttTimeoutCb(uint32_t *args) -{ - MQTT_Client* client = (MQTT_Client*)args; - TasmotaMqtt* _this = (TasmotaMqtt*)client->user_data; - if (_this && _this->onMqttTimeoutCb) _this->onMqttTimeoutCb(); -} - -static void mqttDataCb(uint32_t *args, const char* topic, uint32_t topic_len, const char *data, uint32_t data_len) -{ - MQTT_Client* client = (MQTT_Client*)args; - TasmotaMqtt* _this = (TasmotaMqtt*)client->user_data; - if (_this) _this->_onMqttDataCb(topic, topic_len, data, data_len); -} - -/*********************************************************************************************\ - * TasmotaMqtt class implementation -\*********************************************************************************************/ - -TasmotaMqtt::TasmotaMqtt() : - onMqttConnectedCb(0), - onMqttDisconnectedCb(0), - onMqttPublishedCb(0), - onMqttTimeoutCb(0), - onMqttDataCb(0) -{ -} - -TasmotaMqtt::~TasmotaMqtt() -{ - MQTT_DeleteClient(&mqttClient); -} - -void TasmotaMqtt::InitConnection(const char* host, uint32_t port, uint8_t security) -{ - MQTT_InitConnection(&mqttClient, (uint8_t*)host, port, security); - - // set user data - mqttClient.user_data = (void*)this; - - MQTT_OnConnected(&mqttClient, mqttConnectedCb); - MQTT_OnDisconnected(&mqttClient, mqttDisconnectedCb); - MQTT_OnPublished(&mqttClient, mqttPublishedCb); - MQTT_OnTimeout(&mqttClient, mqttTimeoutCb); - MQTT_OnData(&mqttClient, mqttDataCb); -} - -void TasmotaMqtt::InitClient(const char* client_id, const char* client_user, const char* client_pass, uint32_t keep_alive_time, uint8_t clean_session) -{ - MQTT_InitClient(&mqttClient, (uint8_t*)client_id, (uint8_t*)client_user, (uint8_t*)client_pass, keep_alive_time, clean_session); -} - -void TasmotaMqtt::DeleteClient() -{ - MQTT_DeleteClient(&mqttClient); -} - -void TasmotaMqtt::InitLWT(const char* will_topic, const char* will_msg, uint8_t will_qos, bool will_retain) -{ - MQTT_InitLWT(&mqttClient, (uint8_t*)will_topic, (uint8_t*)will_msg, will_qos, (uint8_t)will_retain); -} - -void TasmotaMqtt::OnConnected( void (*function)(void) ) -{ - onMqttConnectedCb = function; -} - -void TasmotaMqtt::OnDisconnected( void (*function)(void) ) -{ - onMqttDisconnectedCb = function; -} - -void TasmotaMqtt::OnPublished( void (*function)(void) ) -{ - onMqttPublishedCb = function; -} - -void TasmotaMqtt::OnTimeout( void (*function)(void) ) -{ - onMqttTimeoutCb = function; -} - -void TasmotaMqtt::OnData( void (*function)(char*, uint8_t*, unsigned int) ) -{ - onMqttDataCb = function; -} - -bool TasmotaMqtt::Subscribe(const char* topic, uint8_t qos) -{ - return MQTT_Subscribe(&mqttClient, (char*)topic, qos); -} - -bool TasmotaMqtt::Unsubscribe(const char* topic) -{ - return MQTT_UnSubscribe(&mqttClient, (char*)topic); -} - -void TasmotaMqtt::Connect() -{ - MQTT_Connect(&mqttClient); -} - -void TasmotaMqtt::Connect(const char* client_id, const char* client_user, const char* client_pass, const char* will_topic, const char* will_msg, uint8_t will_qos, bool will_retain) -{ - MQTT_InitClient(&mqttClient, (uint8_t*)client_id, (uint8_t*)client_user, (uint8_t*)client_pass, MQTT_KEEPALIVE, 1); - MQTT_InitLWT(&mqttClient, (uint8_t*)will_topic, (uint8_t*)will_msg, will_qos, (uint8_t)will_retain); - MQTT_Connect(&mqttClient); -} - -void TasmotaMqtt::Disconnect() -{ - MQTT_Disconnect(&mqttClient); -} - -bool TasmotaMqtt::Publish(const char* topic, const char* data, int data_length, int qos, bool retain) -{ - return MQTT_Publish(&mqttClient, topic, data, data_length, qos, (int)retain); -} - -bool TasmotaMqtt::Connected() -{ - return (mqttClient.connState > TCP_CONNECTED); -} - -/*********************************************************************************************/ - -void TasmotaMqtt::_onMqttDataCb(const char* topic, uint32_t topic_len, const char* data, uint32_t data_len) -{ - char topic_copy[topic_len +1]; - - memcpy(topic_copy, topic, topic_len); - topic_copy[topic_len] = 0; - if (0 == data_len) data = (const char*)&topic_copy + topic_len; - onMqttDataCb((char*)topic_copy, (byte*)data, data_len); -} diff --git a/lib/TasmotaMqtt-1.1.1/src/TasmotaMqtt.h b/lib/TasmotaMqtt-1.1.1/src/TasmotaMqtt.h deleted file mode 100644 index e512d8d5a..000000000 --- a/lib/TasmotaMqtt-1.1.1/src/TasmotaMqtt.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - TasmotaMqtt.h - Wrapper for mqtt for esp8266 by Tuan PM for Tasmota - - Copyright (C) 2018 Theo Arends and Ingo Randolf - - This library is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#ifndef TasmotaMqtt_h -#define TasmotaMqtt_h -/*********************************************************************************************\ - * TasmotaMqtt supports currently only non-TLS MQTT - * - * Adapted from esp-mqtt-arduino by Ingo Randolf (https://github.com/i-n-g-o/esp-mqtt-arduino) -\*********************************************************************************************/ - -#include - -#include -#include -#include -#include -#include - -extern "C" { - #include - #include "mqtt/mqtt.h" -} - -// MQTT_KEEPALIVE : keepAlive interval in Seconds -#ifndef MQTT_KEEPALIVE -#define MQTT_KEEPALIVE 15 -#endif - -class TasmotaMqtt { -public: - TasmotaMqtt(); - ~TasmotaMqtt(); - - void InitConnection(const char* host, uint32_t port, uint8_t security = 0); - void InitClient(const char* client_id, const char* client_user, const char* client_pass, uint32_t keep_alive_time = MQTT_KEEPALIVE, uint8_t clean_session = 1); - void DeleteClient(); - void InitLWT(const char* will_topic, const char* will_msg, uint8_t will_qos = 0, bool will_retain = false); - - void OnConnected( void (*)(void) ); - void OnDisconnected( void (*)(void) ); - void OnPublished( void (*)(void) ); - void OnTimeout( void (*)(void) ); - void OnData( void (*)(char*, uint8_t*, unsigned int) ); - - bool Subscribe(const char* topic, uint8_t qos = 0); - bool Unsubscribe(const char* topic); - - void Connect(); - void Connect(const char* client_id, const char* client_user, const char* client_pass, const char* will_topic, const char* will_msg, uint8_t will_qos = 0, bool will_retain = false); - void Disconnect(); - - bool Publish(const char* topic, const char* data, int data_length, int qos = 0, bool retain = false); - - bool Connected(); - - int State() { return mqttClient.connState; }; - - void (*onMqttConnectedCb)(void); - void (*onMqttDisconnectedCb)(void); - void (*onMqttPublishedCb)(void); - void (*onMqttTimeoutCb)(void); - void (*onMqttDataCb) (char*, uint8_t*, unsigned int); - - // internal callback - void _onMqttDataCb(const char*, uint32_t, const char*, uint32_t); - -private: - MQTT_Client mqttClient; -}; - -#endif // TasmotaMqtt_h \ No newline at end of file diff --git a/lib/TasmotaMqtt-1.1.1/src/mqtt/debug.h b/lib/TasmotaMqtt-1.1.1/src/mqtt/debug.h deleted file mode 100644 index f45dd6d8d..000000000 --- a/lib/TasmotaMqtt-1.1.1/src/mqtt/debug.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * debug.h - * - * Created on: Dec 4, 2014 - * Author: Minh - */ - -#ifndef USER_DEBUG_H_ -#define USER_DEBUG_H_ - - -#if defined(MQTT_DEBUG_ON) -#define MQTT_INFO( format, ... ) os_printf( format, ## __VA_ARGS__ ) -#else -#define MQTT_INFO( format, ... ) -#endif - - -#endif /* USER_DEBUG_H_ */ diff --git a/lib/TasmotaMqtt-1.1.1/src/mqtt/mqtt.c b/lib/TasmotaMqtt-1.1.1/src/mqtt/mqtt.c deleted file mode 100644 index 06609fe4a..000000000 --- a/lib/TasmotaMqtt-1.1.1/src/mqtt/mqtt.c +++ /dev/null @@ -1,997 +0,0 @@ -/* mqtt.c -* Protocol: http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html -* -* Copyright (c) 2014-2015, Tuan PM -* 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. -* * Neither the name of Redis nor the names of its contributors may be used -* to endorse or promote products derived from this software without -* specific prior written permission. -* -* 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 OWNER 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. -*/ - -#include "user_interface.h" -#include "osapi.h" -#include "espconn.h" -#include "os_type.h" -#include "mem.h" -#include "mqtt_msg.h" -#include "debug.h" -#include "user_config.h" -#include "mqtt.h" -#include "queue.h" - -#define MQTT_TASK_PRIO 2 -#define MQTT_TASK_QUEUE_SIZE 1 -#define MQTT_SEND_TIMOUT 5 - -#ifndef MQTT_SSL_SIZE -#define MQTT_SSL_SIZE 5120 -#endif - -#ifndef QUEUE_BUFFER_SIZE -#define QUEUE_BUFFER_SIZE 2048 -#endif - -/* -unsigned char *default_certificate; -unsigned int default_certificate_len = 0; -unsigned char *default_private_key; -unsigned int default_private_key_len = 0; -*/ - -os_event_t mqtt_procTaskQueue[MQTT_TASK_QUEUE_SIZE]; - -#ifdef PROTOCOL_NAMEv311 -LOCAL uint8_t zero_len_id[2] = { 0, 0 }; -#endif - -LOCAL void ICACHE_FLASH_ATTR -mqtt_dns_found(const char *name, ip_addr_t *ipaddr, void *arg) -{ - struct espconn *pConn = (struct espconn *)arg; - MQTT_Client* client = (MQTT_Client *)pConn->reverse; - - - if (ipaddr == NULL) - { - MQTT_INFO("DNS: Found, but got no ip, try to reconnect\r\n"); - client->connState = TCP_RECONNECT_REQ; - return; - } - - MQTT_INFO("DNS: found ip %d.%d.%d.%d\n", - *((uint8 *) &ipaddr->addr), - *((uint8 *) &ipaddr->addr + 1), - *((uint8 *) &ipaddr->addr + 2), - *((uint8 *) &ipaddr->addr + 3)); - - if (client->ip.addr == 0 && ipaddr->addr != 0) - { - os_memcpy(client->pCon->proto.tcp->remote_ip, &ipaddr->addr, 4); - if (client->security) { -#ifdef MQTT_SSL_ENABLE - espconn_secure_set_size(ESPCONN_CLIENT, MQTT_SSL_SIZE); - espconn_secure_connect(client->pCon); -#else - MQTT_INFO("TCP: Do not support SSL\r\n"); -#endif - } - else { - espconn_connect(client->pCon); - } - - client->connState = TCP_CONNECTING; - MQTT_INFO("TCP: connecting...\r\n"); - } - - system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); -} - - - -LOCAL void ICACHE_FLASH_ATTR -deliver_publish(MQTT_Client* client, uint8_t* message, int length) -{ - mqtt_event_data_t event_data; - - event_data.topic_length = length; - event_data.topic = mqtt_get_publish_topic(message, &event_data.topic_length); - event_data.data_length = length; - event_data.data = mqtt_get_publish_data(message, &event_data.data_length); - - if (client->dataCb) - client->dataCb((uint32_t*)client, event_data.topic, event_data.topic_length, event_data.data, event_data.data_length); - -} - -void ICACHE_FLASH_ATTR -mqtt_send_keepalive(MQTT_Client *client) -{ - MQTT_INFO("\r\nMQTT: Send keepalive packet to %s:%d!\r\n", client->host, client->port); - client->mqtt_state.outbound_message = mqtt_msg_pingreq(&client->mqtt_state.mqtt_connection); - client->mqtt_state.pending_msg_type = MQTT_MSG_TYPE_PINGREQ; - client->mqtt_state.pending_msg_type = mqtt_get_type(client->mqtt_state.outbound_message->data); - client->mqtt_state.pending_msg_id = mqtt_get_id(client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length); - - - client->sendTimeout = MQTT_SEND_TIMOUT; - MQTT_INFO("MQTT: Sending, type: %d, id: %04X\r\n", client->mqtt_state.pending_msg_type, client->mqtt_state.pending_msg_id); - err_t result = ESPCONN_OK; - if (client->security) { -#ifdef MQTT_SSL_ENABLE - result = espconn_secure_send(client->pCon, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length); -#else - MQTT_INFO("TCP: Do not support SSL\r\n"); -#endif - } - else { - result = espconn_send(client->pCon, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length); - } - - client->mqtt_state.outbound_message = NULL; - if (ESPCONN_OK == result) { - client->keepAliveTick = 0; - client->connState = MQTT_DATA; - system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); - } - else { - client->connState = TCP_RECONNECT_DISCONNECTING; - system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); - } -} - -/** - * @brief Delete tcp client and free all memory - * @param mqttClient: The mqtt client which contain TCP client - * @retval None - */ -void ICACHE_FLASH_ATTR -mqtt_tcpclient_delete(MQTT_Client *mqttClient) -{ - if (mqttClient->pCon != NULL) { - MQTT_INFO("TCP: Free memory\r\n"); - // Force abort connections - espconn_abort(mqttClient->pCon); - // Delete connections - espconn_delete(mqttClient->pCon); - - if (mqttClient->pCon->proto.tcp) { - os_free(mqttClient->pCon->proto.tcp); - mqttClient->pCon->proto.tcp = NULL; - } - os_free(mqttClient->pCon); - mqttClient->pCon = NULL; - } -} - -/** - * @brief Delete MQTT client and free all memory - * @param mqttClient: The mqtt client - * @retval None - */ -void ICACHE_FLASH_ATTR -mqtt_client_delete(MQTT_Client *mqttClient) -{ - if (mqttClient == NULL) - return; - - if (mqttClient->pCon != NULL) { - mqtt_tcpclient_delete(mqttClient); - } - - if (mqttClient->host != NULL) { - os_free(mqttClient->host); - mqttClient->host = NULL; - } - - if (mqttClient->user_data != NULL) { - os_free(mqttClient->user_data); - mqttClient->user_data = NULL; - } - - if (mqttClient->mqtt_state.in_buffer != NULL) { - os_free(mqttClient->mqtt_state.in_buffer); - mqttClient->mqtt_state.in_buffer = NULL; - } - - if (mqttClient->mqtt_state.out_buffer != NULL) { - os_free(mqttClient->mqtt_state.out_buffer); - mqttClient->mqtt_state.out_buffer = NULL; - } - - if (mqttClient->mqtt_state.outbound_message != NULL) { - if (mqttClient->mqtt_state.outbound_message->data != NULL) - { - os_free(mqttClient->mqtt_state.outbound_message->data); - mqttClient->mqtt_state.outbound_message->data = NULL; - } - } - - if (mqttClient->mqtt_state.mqtt_connection.buffer != NULL) { - // Already freed but not NULL - mqttClient->mqtt_state.mqtt_connection.buffer = NULL; - } - - if (mqttClient->connect_info.client_id != NULL) { -#ifdef PROTOCOL_NAMEv311 - /* Don't attempt to free if it's the zero_len array */ - if ( ((uint8_t*)mqttClient->connect_info.client_id) != zero_len_id ) - os_free(mqttClient->connect_info.client_id); -#else - os_free(mqttClient->connect_info.client_id); -#endif - mqttClient->connect_info.client_id = NULL; - } - - if (mqttClient->connect_info.username != NULL) { - os_free(mqttClient->connect_info.username); - mqttClient->connect_info.username = NULL; - } - - if (mqttClient->connect_info.password != NULL) { - os_free(mqttClient->connect_info.password); - mqttClient->connect_info.password = NULL; - } - - if (mqttClient->connect_info.will_topic != NULL) { - os_free(mqttClient->connect_info.will_topic); - mqttClient->connect_info.will_topic = NULL; - } - - if (mqttClient->connect_info.will_message != NULL) { - os_free(mqttClient->connect_info.will_message); - mqttClient->connect_info.will_message = NULL; - } - - if (mqttClient->msgQueue.buf != NULL) { - os_free(mqttClient->msgQueue.buf); - mqttClient->msgQueue.buf = NULL; - } - - // Initialize state - mqttClient->connState = WIFI_INIT; - // Clear callback functions to avoid abnormal callback - mqttClient->connectedCb = NULL; - mqttClient->disconnectedCb = NULL; - mqttClient->publishedCb = NULL; - mqttClient->timeoutCb = NULL; - mqttClient->dataCb = NULL; - - MQTT_INFO("MQTT: client already deleted\r\n"); -} - - -/** - * @brief Client received callback function. - * @param arg: contain the ip link information - * @param pdata: received data - * @param len: the lenght of received data - * @retval None - */ -void ICACHE_FLASH_ATTR -mqtt_tcpclient_recv(void *arg, char *pdata, unsigned short len) -{ - uint8_t msg_type; - uint8_t msg_qos; - uint16_t msg_id; - uint8_t msg_conn_ret; - - struct espconn *pCon = (struct espconn*)arg; - MQTT_Client *client = (MQTT_Client *)pCon->reverse; - -READPACKET: - MQTT_INFO("TCP: data received %d bytes\r\n", len); - // MQTT_INFO("STATE: %d\r\n", client->connState); - if (len < MQTT_BUF_SIZE && len > 0) { - os_memcpy(client->mqtt_state.in_buffer, pdata, len); - - msg_type = mqtt_get_type(client->mqtt_state.in_buffer); - msg_qos = mqtt_get_qos(client->mqtt_state.in_buffer); - msg_id = mqtt_get_id(client->mqtt_state.in_buffer, client->mqtt_state.in_buffer_length); - switch (client->connState) { - case MQTT_CONNECT_SENDING: - if (msg_type == MQTT_MSG_TYPE_CONNACK) { - if (client->mqtt_state.pending_msg_type != MQTT_MSG_TYPE_CONNECT) { - MQTT_INFO("MQTT: Invalid packet\r\n"); - if (client->security) { -#ifdef MQTT_SSL_ENABLE - espconn_secure_disconnect(client->pCon); -#else - MQTT_INFO("TCP: Do not support SSL\r\n"); -#endif - } - else { - espconn_disconnect(client->pCon); - } - } else { - msg_conn_ret = mqtt_get_connect_return_code(client->mqtt_state.in_buffer); - switch (msg_conn_ret) { - case CONNECTION_ACCEPTED: - MQTT_INFO("MQTT: Connected to %s:%d\r\n", client->host, client->port); - client->connState = MQTT_DATA; - if (client->connectedCb) - client->connectedCb((uint32_t*)client); - break; - case CONNECTION_REFUSE_PROTOCOL: - case CONNECTION_REFUSE_SERVER_UNAVAILABLE: - case CONNECTION_REFUSE_BAD_USERNAME: - case CONNECTION_REFUSE_NOT_AUTHORIZED: - MQTT_INFO("MQTT: Connection refuse, reason code: %d\r\n", msg_conn_ret); - default: - if (client->security) { -#ifdef MQTT_SSL_ENABLE - espconn_secure_disconnect(client->pCon); -#else - MQTT_INFO("TCP: Do not support SSL\r\n"); -#endif - } - else { - espconn_disconnect(client->pCon); - } - - } - - } - - } - break; - case MQTT_DATA: - case MQTT_KEEPALIVE_SEND: - client->mqtt_state.message_length_read = len; - client->mqtt_state.message_length = mqtt_get_total_length(client->mqtt_state.in_buffer, client->mqtt_state.message_length_read); - - - switch (msg_type) - { - - case MQTT_MSG_TYPE_SUBACK: - if (client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_SUBSCRIBE && client->mqtt_state.pending_msg_id == msg_id) - MQTT_INFO("MQTT: Subscribe successful\r\n"); - break; - case MQTT_MSG_TYPE_UNSUBACK: - if (client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_UNSUBSCRIBE && client->mqtt_state.pending_msg_id == msg_id) - MQTT_INFO("MQTT: UnSubscribe successful\r\n"); - break; - case MQTT_MSG_TYPE_PUBLISH: - if (msg_qos == 1) - client->mqtt_state.outbound_message = mqtt_msg_puback(&client->mqtt_state.mqtt_connection, msg_id); - else if (msg_qos == 2) - client->mqtt_state.outbound_message = mqtt_msg_pubrec(&client->mqtt_state.mqtt_connection, msg_id); - if (msg_qos == 1 || msg_qos == 2) { - MQTT_INFO("MQTT: Queue response QoS: %d\r\n", msg_qos); - if (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) { - MQTT_INFO("MQTT: Queue full\r\n"); - } - } - - deliver_publish(client, client->mqtt_state.in_buffer, client->mqtt_state.message_length_read); - break; - case MQTT_MSG_TYPE_PUBACK: - if (client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_PUBLISH && client->mqtt_state.pending_msg_id == msg_id) { - MQTT_INFO("MQTT: received MQTT_MSG_TYPE_PUBACK, finish QoS1 publish\r\n"); - } - - break; - case MQTT_MSG_TYPE_PUBREC: - client->mqtt_state.outbound_message = mqtt_msg_pubrel(&client->mqtt_state.mqtt_connection, msg_id); - if (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) { - MQTT_INFO("MQTT: Queue full\r\n"); - } - break; - case MQTT_MSG_TYPE_PUBREL: - client->mqtt_state.outbound_message = mqtt_msg_pubcomp(&client->mqtt_state.mqtt_connection, msg_id); - if (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) { - MQTT_INFO("MQTT: Queue full\r\n"); - } - break; - case MQTT_MSG_TYPE_PUBCOMP: - if (client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_PUBLISH && client->mqtt_state.pending_msg_id == msg_id) { - MQTT_INFO("MQTT: receive MQTT_MSG_TYPE_PUBCOMP, finish QoS2 publish\r\n"); - } - break; - case MQTT_MSG_TYPE_PINGREQ: - client->mqtt_state.outbound_message = mqtt_msg_pingresp(&client->mqtt_state.mqtt_connection); - if (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) { - MQTT_INFO("MQTT: Queue full\r\n"); - } - break; - case MQTT_MSG_TYPE_PINGRESP: - // Ignore - break; - } - // NOTE: this is done down here and not in the switch case above - // because the PSOCK_READBUF_LEN() won't work inside a switch - // statement due to the way protothreads resume. - if (msg_type == MQTT_MSG_TYPE_PUBLISH) - { - len = client->mqtt_state.message_length_read; - - if (client->mqtt_state.message_length < client->mqtt_state.message_length_read) - { - //client->connState = MQTT_PUBLISH_RECV; - //Not Implement yet - len -= client->mqtt_state.message_length; - pdata += client->mqtt_state.message_length; - - MQTT_INFO("Get another published message\r\n"); - goto READPACKET; - } - - } - break; - } - } else { - MQTT_INFO("ERROR: Message too long\r\n"); - } - system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); -} - -/** - * @brief Client send over callback function. - * @param arg: contain the ip link information - * @retval None - */ -void ICACHE_FLASH_ATTR -mqtt_tcpclient_sent_cb(void *arg) -{ - struct espconn *pCon = (struct espconn *)arg; - MQTT_Client* client = (MQTT_Client *)pCon->reverse; - MQTT_INFO("TCP: Sent\r\n"); - client->sendTimeout = 0; - client->keepAliveTick = 0; - - if ((client->connState == MQTT_DATA || client->connState == MQTT_KEEPALIVE_SEND) - && client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_PUBLISH) { - if (client->publishedCb) - client->publishedCb((uint32_t*)client); - } - system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); -} - -void ICACHE_FLASH_ATTR mqtt_timer(void *arg) -{ - MQTT_Client* client = (MQTT_Client*)arg; - - if (client->connState == MQTT_DATA) { - client->keepAliveTick ++; - if (client->keepAliveTick > (client->mqtt_state.connect_info->keepalive / 2)) { - client->connState = MQTT_KEEPALIVE_SEND; - system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); - } - - } else if (client->connState == TCP_RECONNECT_REQ) { - client->reconnectTick ++; - if (client->reconnectTick > MQTT_RECONNECT_TIMEOUT) { - client->reconnectTick = 0; - client->connState = TCP_RECONNECT; - system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); - if (client->timeoutCb) - client->timeoutCb((uint32_t*)client); - } - } - if (client->sendTimeout > 0) - client->sendTimeout --; -} - -void ICACHE_FLASH_ATTR -mqtt_tcpclient_discon_cb(void *arg) -{ - - struct espconn *pespconn = (struct espconn *)arg; - MQTT_Client* client = (MQTT_Client *)pespconn->reverse; - MQTT_INFO("TCP: Disconnected callback\r\n"); - if (TCP_DISCONNECTING == client->connState) { - client->connState = TCP_DISCONNECTED; - } - else if (MQTT_DELETING == client->connState) { - client->connState = MQTT_DELETED; - } - else { - client->connState = TCP_RECONNECT_REQ; - } - if (client->disconnectedCb) - client->disconnectedCb((uint32_t*)client); - - system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); -} - - - -/** - * @brief Tcp client connect success callback function. - * @param arg: contain the ip link information - * @retval None - */ -void ICACHE_FLASH_ATTR -mqtt_tcpclient_connect_cb(void *arg) -{ - struct espconn *pCon = (struct espconn *)arg; - MQTT_Client* client = (MQTT_Client *)pCon->reverse; - - espconn_regist_disconcb(client->pCon, mqtt_tcpclient_discon_cb); - espconn_regist_recvcb(client->pCon, mqtt_tcpclient_recv);//////// - espconn_regist_sentcb(client->pCon, mqtt_tcpclient_sent_cb);/////// - MQTT_INFO("MQTT: Connected to broker %s:%d\r\n", client->host, client->port); - - mqtt_msg_init(&client->mqtt_state.mqtt_connection, client->mqtt_state.out_buffer, client->mqtt_state.out_buffer_length); - client->mqtt_state.outbound_message = mqtt_msg_connect(&client->mqtt_state.mqtt_connection, client->mqtt_state.connect_info); - client->mqtt_state.pending_msg_type = mqtt_get_type(client->mqtt_state.outbound_message->data); - client->mqtt_state.pending_msg_id = mqtt_get_id(client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length); - - - client->sendTimeout = MQTT_SEND_TIMOUT; - MQTT_INFO("MQTT: Sending, type: %d, id: %04X\r\n", client->mqtt_state.pending_msg_type, client->mqtt_state.pending_msg_id); - if (client->security) { -#ifdef MQTT_SSL_ENABLE - espconn_secure_send(client->pCon, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length); -#else - MQTT_INFO("TCP: Do not support SSL\r\n"); -#endif - } - else { - espconn_send(client->pCon, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length); - } - - client->mqtt_state.outbound_message = NULL; - client->connState = MQTT_CONNECT_SENDING; - system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); -} - -/** - * @brief Tcp client connect repeat callback function. - * @param arg: contain the ip link information - * @retval None - */ -void ICACHE_FLASH_ATTR -mqtt_tcpclient_recon_cb(void *arg, sint8 errType) -{ - struct espconn *pCon = (struct espconn *)arg; - MQTT_Client* client = (MQTT_Client *)pCon->reverse; - - MQTT_INFO("TCP: Reconnect to %s:%d\r\n", client->host, client->port); - - client->connState = TCP_RECONNECT_REQ; - - system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); - -} - -/** - * @brief MQTT publish function. - * @param client: MQTT_Client reference - * @param topic: string topic will publish to - * @param data: buffer data send point to - * @param data_length: length of data - * @param qos: qos - * @param retain: retain - * @retval TRUE if success queue - */ -bool ICACHE_FLASH_ATTR -MQTT_Publish(MQTT_Client *client, const char* topic, const char* data, int data_length, int qos, int retain) -{ - uint8_t dataBuffer[MQTT_BUF_SIZE]; - uint16_t dataLen; - client->mqtt_state.outbound_message = mqtt_msg_publish(&client->mqtt_state.mqtt_connection, - topic, data, data_length, - qos, retain, - &client->mqtt_state.pending_msg_id); - if (client->mqtt_state.outbound_message->length == 0) { - MQTT_INFO("MQTT: Queuing publish failed\r\n"); - return FALSE; - } - MQTT_INFO("MQTT: queuing publish, length: %d, queue size(%d/%d)\r\n", client->mqtt_state.outbound_message->length, client->msgQueue.rb.fill_cnt, client->msgQueue.rb.size); - while (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) { - MQTT_INFO("MQTT: Queue full\r\n"); - if (QUEUE_Gets(&client->msgQueue, dataBuffer, &dataLen, MQTT_BUF_SIZE) == -1) { - MQTT_INFO("MQTT: Serious buffer error\r\n"); - return FALSE; - } - } - system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); - return TRUE; -} - -/** - * @brief MQTT subscibe function. - * @param client: MQTT_Client reference - * @param topic: string topic will subscribe - * @param qos: qos - * @retval TRUE if success queue - */ -bool ICACHE_FLASH_ATTR -MQTT_Subscribe(MQTT_Client *client, char* topic, uint8_t qos) -{ - uint8_t dataBuffer[MQTT_BUF_SIZE]; - uint16_t dataLen; - - client->mqtt_state.outbound_message = mqtt_msg_subscribe(&client->mqtt_state.mqtt_connection, - topic, qos, - &client->mqtt_state.pending_msg_id); - MQTT_INFO("MQTT: queue subscribe, topic\"%s\", id: %d\r\n", topic, client->mqtt_state.pending_msg_id); - while (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) { - MQTT_INFO("MQTT: Queue full\r\n"); - if (QUEUE_Gets(&client->msgQueue, dataBuffer, &dataLen, MQTT_BUF_SIZE) == -1) { - MQTT_INFO("MQTT: Serious buffer error\r\n"); - return FALSE; - } - } - system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); - - return TRUE; -} - -/** - * @brief MQTT un-subscibe function. - * @param client: MQTT_Client reference - * @param topic: String topic will un-subscribe - * @retval TRUE if success queue - */ -bool ICACHE_FLASH_ATTR -MQTT_UnSubscribe(MQTT_Client *client, char* topic) -{ - uint8_t dataBuffer[MQTT_BUF_SIZE]; - uint16_t dataLen; - client->mqtt_state.outbound_message = mqtt_msg_unsubscribe(&client->mqtt_state.mqtt_connection, - topic, - &client->mqtt_state.pending_msg_id); - MQTT_INFO("MQTT: queue un-subscribe, topic\"%s\", id: %d\r\n", topic, client->mqtt_state.pending_msg_id); - while (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) { - MQTT_INFO("MQTT: Queue full\r\n"); - if (QUEUE_Gets(&client->msgQueue, dataBuffer, &dataLen, MQTT_BUF_SIZE) == -1) { - MQTT_INFO("MQTT: Serious buffer error\r\n"); - return FALSE; - } - } - system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); - return TRUE; -} - -/** - * @brief MQTT ping function. - * @param client: MQTT_Client reference - * @retval TRUE if success queue - */ -bool ICACHE_FLASH_ATTR -MQTT_Ping(MQTT_Client *client) -{ - uint8_t dataBuffer[MQTT_BUF_SIZE]; - uint16_t dataLen; - client->mqtt_state.outbound_message = mqtt_msg_pingreq(&client->mqtt_state.mqtt_connection); - if (client->mqtt_state.outbound_message->length == 0) { - MQTT_INFO("MQTT: Queuing publish failed\r\n"); - return FALSE; - } - MQTT_INFO("MQTT: queuing publish, length: %d, queue size(%d/%d)\r\n", client->mqtt_state.outbound_message->length, client->msgQueue.rb.fill_cnt, client->msgQueue.rb.size); - while (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) { - MQTT_INFO("MQTT: Queue full\r\n"); - if (QUEUE_Gets(&client->msgQueue, dataBuffer, &dataLen, MQTT_BUF_SIZE) == -1) { - MQTT_INFO("MQTT: Serious buffer error\r\n"); - return FALSE; - } - } - system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); - return TRUE; -} - -void ICACHE_FLASH_ATTR -MQTT_Task(os_event_t *e) -{ - MQTT_Client* client = (MQTT_Client*)e->par; - uint8_t dataBuffer[MQTT_BUF_SIZE]; - uint16_t dataLen; - if (e->par == 0) - return; - switch (client->connState) { - - case TCP_RECONNECT_REQ: - break; - case TCP_RECONNECT: - mqtt_tcpclient_delete(client); - MQTT_Connect(client); - MQTT_INFO("TCP: Reconnect to: %s:%d\r\n", client->host, client->port); - client->connState = TCP_CONNECTING; - break; - case MQTT_DELETING: - case TCP_DISCONNECTING: - case TCP_RECONNECT_DISCONNECTING: - if (client->security) { -#ifdef MQTT_SSL_ENABLE - espconn_secure_disconnect(client->pCon); -#else - MQTT_INFO("TCP: Do not support SSL\r\n"); -#endif - } - else { - espconn_disconnect(client->pCon); - } - break; - case TCP_DISCONNECTED: - MQTT_INFO("MQTT: Disconnected\r\n"); - mqtt_tcpclient_delete(client); - break; - case MQTT_DELETED: - MQTT_INFO("MQTT: Deleted client\r\n"); - mqtt_client_delete(client); - break; - case MQTT_KEEPALIVE_SEND: - mqtt_send_keepalive(client); - break; - case MQTT_DATA: - if (QUEUE_IsEmpty(&client->msgQueue) || client->sendTimeout != 0) { - break; - } - if (QUEUE_Gets(&client->msgQueue, dataBuffer, &dataLen, MQTT_BUF_SIZE) == 0) { - client->mqtt_state.pending_msg_type = mqtt_get_type(dataBuffer); - client->mqtt_state.pending_msg_id = mqtt_get_id(dataBuffer, dataLen); - - - client->sendTimeout = MQTT_SEND_TIMOUT; - MQTT_INFO("MQTT: Sending, type: %d, id: %04X\r\n", client->mqtt_state.pending_msg_type, client->mqtt_state.pending_msg_id); - client->keepAliveTick = 0; - if (client->security) { -#ifdef MQTT_SSL_ENABLE - espconn_secure_send(client->pCon, dataBuffer, dataLen); -#else - MQTT_INFO("TCP: Do not support SSL\r\n"); -#endif - } - else { - espconn_send(client->pCon, dataBuffer, dataLen); - } - - client->mqtt_state.outbound_message = NULL; - break; - } - break; - } -} - -/** - * @brief MQTT initialization connection function - * @param client: MQTT_Client reference - * @param host: Domain or IP string - * @param port: Port to connect - * @param security: 1 for ssl, 0 for none - * @retval None - */ -void ICACHE_FLASH_ATTR -MQTT_InitConnection(MQTT_Client *mqttClient, uint8_t* host, uint32_t port, uint8_t security) -{ - uint32_t temp; - MQTT_INFO("MQTT:InitConnection\r\n"); - os_memset(mqttClient, 0, sizeof(MQTT_Client)); - temp = os_strlen(host); - mqttClient->host = (uint8_t*)os_zalloc(temp + 1); - os_strcpy(mqttClient->host, host); - mqttClient->host[temp] = 0; - mqttClient->port = port; - mqttClient->security = security; - -} - -/** - * @brief MQTT initialization mqtt client function - * @param client: MQTT_Client reference - * @param clientid: MQTT client id - * @param client_user:MQTT client user - * @param client_pass:MQTT client password - * @param client_pass:MQTT keep alive timer, in second - * @retval None - */ -bool ICACHE_FLASH_ATTR -MQTT_InitClient(MQTT_Client *mqttClient, uint8_t* client_id, uint8_t* client_user, uint8_t* client_pass, uint32_t keepAliveTime, uint8_t cleanSession) -{ - uint32_t temp; - MQTT_INFO("MQTT:InitClient\r\n"); - - os_memset(&mqttClient->connect_info, 0, sizeof(mqtt_connect_info_t)); - - if ( !client_id ) - { - /* Should be allowed by broker, but clean session flag must be set. */ - #ifdef PROTOCOL_NAMEv311 - if (cleanSession) - { - mqttClient->connect_info.client_id = zero_len_id; - } else { - MQTT_INFO("cleanSession must be set to use 0 length client_id\r\n"); - return false; - } - /* Not supported. Return. */ - #else - MQTT_INFO("Client ID required for MQTT < 3.1.1!\r\n"); - return false; - #endif - } - - /* If connect_info's client_id is still NULL and we get here, we can * - * assume the passed client_id is non-NULL. */ - if ( !(mqttClient->connect_info.client_id) ) - { - temp = os_strlen(client_id); - mqttClient->connect_info.client_id = (uint8_t*)os_zalloc(temp + 1); - os_strcpy(mqttClient->connect_info.client_id, client_id); - mqttClient->connect_info.client_id[temp] = 0; - } - - if (client_user) - { - temp = os_strlen(client_user); - mqttClient->connect_info.username = (uint8_t*)os_zalloc(temp + 1); - os_strcpy(mqttClient->connect_info.username, client_user); - mqttClient->connect_info.username[temp] = 0; - } - - if (client_pass) - { - temp = os_strlen(client_pass); - mqttClient->connect_info.password = (uint8_t*)os_zalloc(temp + 1); - os_strcpy(mqttClient->connect_info.password, client_pass); - mqttClient->connect_info.password[temp] = 0; - } - - - mqttClient->connect_info.keepalive = keepAliveTime; - mqttClient->connect_info.clean_session = cleanSession; - - mqttClient->mqtt_state.in_buffer = (uint8_t *)os_zalloc(MQTT_BUF_SIZE); - mqttClient->mqtt_state.in_buffer_length = MQTT_BUF_SIZE; - mqttClient->mqtt_state.out_buffer = (uint8_t *)os_zalloc(MQTT_BUF_SIZE); - mqttClient->mqtt_state.out_buffer_length = MQTT_BUF_SIZE; - mqttClient->mqtt_state.connect_info = &mqttClient->connect_info; - - mqtt_msg_init(&mqttClient->mqtt_state.mqtt_connection, mqttClient->mqtt_state.out_buffer, mqttClient->mqtt_state.out_buffer_length); - - QUEUE_Init(&mqttClient->msgQueue, QUEUE_BUFFER_SIZE); - - system_os_task(MQTT_Task, MQTT_TASK_PRIO, mqtt_procTaskQueue, MQTT_TASK_QUEUE_SIZE); - system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)mqttClient); - return true; -} -void ICACHE_FLASH_ATTR -MQTT_InitLWT(MQTT_Client *mqttClient, uint8_t* will_topic, uint8_t* will_msg, uint8_t will_qos, uint8_t will_retain) -{ - uint32_t temp; - temp = os_strlen(will_topic); - mqttClient->connect_info.will_topic = (uint8_t*)os_zalloc(temp + 1); - os_strcpy(mqttClient->connect_info.will_topic, will_topic); - mqttClient->connect_info.will_topic[temp] = 0; - - temp = os_strlen(will_msg); - mqttClient->connect_info.will_message = (uint8_t*)os_zalloc(temp + 1); - os_strcpy(mqttClient->connect_info.will_message, will_msg); - mqttClient->connect_info.will_message[temp] = 0; - - - mqttClient->connect_info.will_qos = will_qos; - mqttClient->connect_info.will_retain = will_retain; -} -/** - * @brief Begin connect to MQTT broker - * @param client: MQTT_Client reference - * @retval None - */ -void ICACHE_FLASH_ATTR -MQTT_Connect(MQTT_Client *mqttClient) -{ - if (mqttClient->pCon) { - // Clean up the old connection forcefully - using MQTT_Disconnect - // does not actually release the old connection until the - // disconnection callback is invoked. - mqtt_tcpclient_delete(mqttClient); - } - mqttClient->pCon = (struct espconn *)os_zalloc(sizeof(struct espconn)); - mqttClient->pCon->type = ESPCONN_TCP; - mqttClient->pCon->state = ESPCONN_NONE; - mqttClient->pCon->proto.tcp = (esp_tcp *)os_zalloc(sizeof(esp_tcp)); - mqttClient->pCon->proto.tcp->local_port = espconn_port(); - mqttClient->pCon->proto.tcp->remote_port = mqttClient->port; - mqttClient->pCon->reverse = mqttClient; - espconn_regist_connectcb(mqttClient->pCon, mqtt_tcpclient_connect_cb); - espconn_regist_reconcb(mqttClient->pCon, mqtt_tcpclient_recon_cb); - - mqttClient->keepAliveTick = 0; - mqttClient->reconnectTick = 0; - - - os_timer_disarm(&mqttClient->mqttTimer); - os_timer_setfn(&mqttClient->mqttTimer, (os_timer_func_t *)mqtt_timer, mqttClient); - os_timer_arm(&mqttClient->mqttTimer, 1000, 1); - - if (UTILS_StrToIP(mqttClient->host, &mqttClient->pCon->proto.tcp->remote_ip)) { - MQTT_INFO("TCP: Connect to ip %s:%d\r\n", mqttClient->host, mqttClient->port); - if (mqttClient->security) - { -#ifdef MQTT_SSL_ENABLE - espconn_secure_set_size(ESPCONN_CLIENT, MQTT_SSL_SIZE); - espconn_secure_connect(mqttClient->pCon); -#else - MQTT_INFO("TCP: Do not support SSL\r\n"); -#endif - } - else - { - espconn_connect(mqttClient->pCon); - } - } - else { - MQTT_INFO("TCP: Connect to domain %s:%d\r\n", mqttClient->host, mqttClient->port); - espconn_gethostbyname(mqttClient->pCon, mqttClient->host, &mqttClient->ip, mqtt_dns_found); - } - mqttClient->connState = TCP_CONNECTING; -} - -void ICACHE_FLASH_ATTR -MQTT_Disconnect(MQTT_Client *mqttClient) -{ - mqttClient->connState = TCP_DISCONNECTING; - system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)mqttClient); - os_timer_disarm(&mqttClient->mqttTimer); -} - -void ICACHE_FLASH_ATTR -MQTT_DeleteClient(MQTT_Client *mqttClient) -{ - if (NULL == mqttClient) - return; - - mqttClient->connState = MQTT_DELETED; - // if(TCP_DISCONNECTED == mqttClient->connState) { - // mqttClient->connState = MQTT_DELETED; - // } else if(MQTT_DELETED != mqttClient->connState) { - // mqttClient->connState = MQTT_DELETING; - // } - - system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)mqttClient); - os_timer_disarm(&mqttClient->mqttTimer); -} - -void ICACHE_FLASH_ATTR -MQTT_OnConnected(MQTT_Client *mqttClient, MqttCallback connectedCb) -{ - mqttClient->connectedCb = connectedCb; -} - -void ICACHE_FLASH_ATTR -MQTT_OnDisconnected(MQTT_Client *mqttClient, MqttCallback disconnectedCb) -{ - mqttClient->disconnectedCb = disconnectedCb; -} - -void ICACHE_FLASH_ATTR -MQTT_OnData(MQTT_Client *mqttClient, MqttDataCallback dataCb) -{ - mqttClient->dataCb = dataCb; -} - -void ICACHE_FLASH_ATTR -MQTT_OnPublished(MQTT_Client *mqttClient, MqttCallback publishedCb) -{ - mqttClient->publishedCb = publishedCb; -} - -void ICACHE_FLASH_ATTR -MQTT_OnTimeout(MQTT_Client *mqttClient, MqttCallback timeoutCb) -{ - mqttClient->timeoutCb = timeoutCb; -} diff --git a/lib/TasmotaMqtt-1.1.1/src/mqtt/mqtt.h b/lib/TasmotaMqtt-1.1.1/src/mqtt/mqtt.h deleted file mode 100644 index 96489107e..000000000 --- a/lib/TasmotaMqtt-1.1.1/src/mqtt/mqtt.h +++ /dev/null @@ -1,148 +0,0 @@ -/* mqtt.h -* -* Copyright (c) 2014-2015, Tuan PM -* 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. -* * Neither the name of Redis nor the names of its contributors may be used -* to endorse or promote products derived from this software without -* specific prior written permission. -* -* 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 OWNER 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. -*/ -#ifndef USER_AT_MQTT_H_ -#define USER_AT_MQTT_H_ -#include "user_config.h" -#include "mqtt_msg.h" -#include "user_interface.h" - -#include "queue.h" -typedef struct mqtt_event_data_t -{ - uint8_t type; - const char* topic; - const char* data; - uint16_t topic_length; - uint16_t data_length; - uint16_t data_offset; -} mqtt_event_data_t; - -typedef struct mqtt_state_t -{ - uint16_t port; - int auto_reconnect; - mqtt_connect_info_t* connect_info; - uint8_t* in_buffer; - uint8_t* out_buffer; - int in_buffer_length; - int out_buffer_length; - uint16_t message_length; - uint16_t message_length_read; - mqtt_message_t* outbound_message; - mqtt_connection_t mqtt_connection; - uint16_t pending_msg_id; - int pending_msg_type; - int pending_publish_qos; -} mqtt_state_t; - -typedef enum { - WIFI_INIT, - WIFI_CONNECTING, - WIFI_CONNECTING_ERROR, - WIFI_CONNECTED, - DNS_RESOLVE, - TCP_DISCONNECTING, - TCP_DISCONNECTED, - TCP_RECONNECT_DISCONNECTING, - TCP_RECONNECT_REQ, - TCP_RECONNECT, - TCP_CONNECTING, - TCP_CONNECTING_ERROR, - TCP_CONNECTED, - MQTT_CONNECT_SEND, - MQTT_CONNECT_SENDING, - MQTT_SUBSCIBE_SEND, - MQTT_SUBSCIBE_SENDING, - MQTT_DATA, - MQTT_KEEPALIVE_SEND, - MQTT_PUBLISH_RECV, - MQTT_PUBLISHING, - MQTT_DELETING, - MQTT_DELETED, -} tConnState; - -typedef void (*MqttCallback)(uint32_t *args); -typedef void (*MqttDataCallback)(uint32_t *args, const char* topic, uint32_t topic_len, const char *data, uint32_t lengh); - -typedef struct { - struct espconn *pCon; - uint8_t security; - uint8_t* host; - uint32_t port; - ip_addr_t ip; - mqtt_state_t mqtt_state; - mqtt_connect_info_t connect_info; - MqttCallback connectedCb; - MqttCallback disconnectedCb; - MqttCallback publishedCb; - MqttCallback timeoutCb; - MqttDataCallback dataCb; - ETSTimer mqttTimer; - uint32_t keepAliveTick; - uint32_t reconnectTick; - uint32_t sendTimeout; - tConnState connState; - QUEUE msgQueue; - void* user_data; -} MQTT_Client; - -#define SEC_NONSSL 0 -#define SEC_SSL 1 - -#define MQTT_FLAG_CONNECTED 1 -#define MQTT_FLAG_READY 2 -#define MQTT_FLAG_EXIT 4 - -#define MQTT_EVENT_TYPE_NONE 0 -#define MQTT_EVENT_TYPE_CONNECTED 1 -#define MQTT_EVENT_TYPE_DISCONNECTED 2 -#define MQTT_EVENT_TYPE_SUBSCRIBED 3 -#define MQTT_EVENT_TYPE_UNSUBSCRIBED 4 -#define MQTT_EVENT_TYPE_PUBLISH 5 -#define MQTT_EVENT_TYPE_PUBLISHED 6 -#define MQTT_EVENT_TYPE_EXITED 7 -#define MQTT_EVENT_TYPE_PUBLISH_CONTINUATION 8 - -void ICACHE_FLASH_ATTR MQTT_InitConnection(MQTT_Client *mqttClient, uint8_t* host, uint32_t port, uint8_t security); -bool ICACHE_FLASH_ATTR MQTT_InitClient(MQTT_Client *mqttClient, uint8_t* client_id, uint8_t* client_user, uint8_t* client_pass, uint32_t keepAliveTime, uint8_t cleanSession); -void ICACHE_FLASH_ATTR MQTT_DeleteClient(MQTT_Client *mqttClient); -void ICACHE_FLASH_ATTR MQTT_InitLWT(MQTT_Client *mqttClient, uint8_t* will_topic, uint8_t* will_msg, uint8_t will_qos, uint8_t will_retain); -void ICACHE_FLASH_ATTR MQTT_OnConnected(MQTT_Client *mqttClient, MqttCallback connectedCb); -void ICACHE_FLASH_ATTR MQTT_OnDisconnected(MQTT_Client *mqttClient, MqttCallback disconnectedCb); -void ICACHE_FLASH_ATTR MQTT_OnPublished(MQTT_Client *mqttClient, MqttCallback publishedCb); -void ICACHE_FLASH_ATTR MQTT_OnTimeout(MQTT_Client *mqttClient, MqttCallback timeoutCb); -void ICACHE_FLASH_ATTR MQTT_OnData(MQTT_Client *mqttClient, MqttDataCallback dataCb); -bool ICACHE_FLASH_ATTR MQTT_Subscribe(MQTT_Client *client, char* topic, uint8_t qos); -bool ICACHE_FLASH_ATTR MQTT_UnSubscribe(MQTT_Client *client, char* topic); -void ICACHE_FLASH_ATTR MQTT_Connect(MQTT_Client *mqttClient); -void ICACHE_FLASH_ATTR MQTT_Disconnect(MQTT_Client *mqttClient); -bool ICACHE_FLASH_ATTR MQTT_Publish(MQTT_Client *client, const char* topic, const char* data, int data_length, int qos, int retain); - -#endif /* USER_AT_MQTT_H_ */ diff --git a/lib/TasmotaMqtt-1.1.1/src/mqtt/mqtt_msg.c b/lib/TasmotaMqtt-1.1.1/src/mqtt/mqtt_msg.c deleted file mode 100644 index 57dcbac27..000000000 --- a/lib/TasmotaMqtt-1.1.1/src/mqtt/mqtt_msg.c +++ /dev/null @@ -1,487 +0,0 @@ -/* -* Copyright (c) 2014, Stephen Robinson -* All rights reserved. -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions -* are met: -* -* 1. Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* 2. 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. -* 3. Neither the name of the copyright holder nor the names of its -* contributors may be used to endorse or promote products derived -* from this software without specific prior written permission. -* -* 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. -* -*/ - -#include -#include "mqtt_msg.h" -#include "user_config.h" -#define MQTT_MAX_FIXED_HEADER_SIZE 3 - -enum mqtt_connect_flag -{ - MQTT_CONNECT_FLAG_USERNAME = 1 << 7, - MQTT_CONNECT_FLAG_PASSWORD = 1 << 6, - MQTT_CONNECT_FLAG_WILL_RETAIN = 1 << 5, - MQTT_CONNECT_FLAG_WILL = 1 << 2, - MQTT_CONNECT_FLAG_CLEAN_SESSION = 1 << 1 -}; - -struct __attribute((__packed__)) mqtt_connect_variable_header -{ - uint8_t lengthMsb; - uint8_t lengthLsb; -#if defined(PROTOCOL_NAMEv31) - uint8_t magic[6]; -#elif defined(PROTOCOL_NAMEv311) - uint8_t magic[4]; -#else -#error "Please define protocol name" -#endif - uint8_t version; - uint8_t flags; - uint8_t keepaliveMsb; - uint8_t keepaliveLsb; -}; - -static int ICACHE_FLASH_ATTR append_string(mqtt_connection_t* connection, const char* string, int len) -{ - if (connection->message.length + len + 2 > connection->buffer_length) - return -1; - - connection->buffer[connection->message.length++] = len >> 8; - connection->buffer[connection->message.length++] = len & 0xff; - memcpy(connection->buffer + connection->message.length, string, len); - connection->message.length += len; - - return len + 2; -} - -static uint16_t ICACHE_FLASH_ATTR append_message_id(mqtt_connection_t* connection, uint16_t message_id) -{ - // If message_id is zero then we should assign one, otherwise - // we'll use the one supplied by the caller - while (message_id == 0) - message_id = ++connection->message_id; - - if (connection->message.length + 2 > connection->buffer_length) - return 0; - - connection->buffer[connection->message.length++] = message_id >> 8; - connection->buffer[connection->message.length++] = message_id & 0xff; - - return message_id; -} - -static int ICACHE_FLASH_ATTR init_message(mqtt_connection_t* connection) -{ - connection->message.length = MQTT_MAX_FIXED_HEADER_SIZE; - return MQTT_MAX_FIXED_HEADER_SIZE; -} - -static mqtt_message_t* ICACHE_FLASH_ATTR fail_message(mqtt_connection_t* connection) -{ - connection->message.data = connection->buffer; - connection->message.length = 0; - return &connection->message; -} - -static mqtt_message_t* ICACHE_FLASH_ATTR fini_message(mqtt_connection_t* connection, int type, int dup, int qos, int retain) -{ - int remaining_length = connection->message.length - MQTT_MAX_FIXED_HEADER_SIZE; - - if (remaining_length > 127) - { - connection->buffer[0] = ((type & 0x0f) << 4) | ((dup & 1) << 3) | ((qos & 3) << 1) | (retain & 1); - connection->buffer[1] = 0x80 | (remaining_length % 128); - connection->buffer[2] = remaining_length / 128; - connection->message.length = remaining_length + 3; - connection->message.data = connection->buffer; - } - else - { - connection->buffer[1] = ((type & 0x0f) << 4) | ((dup & 1) << 3) | ((qos & 3) << 1) | (retain & 1); - connection->buffer[2] = remaining_length; - connection->message.length = remaining_length + 2; - connection->message.data = connection->buffer + 1; - } - - return &connection->message; -} - -void ICACHE_FLASH_ATTR mqtt_msg_init(mqtt_connection_t* connection, uint8_t* buffer, uint16_t buffer_length) -{ - memset(connection, 0, sizeof(mqtt_connection_t)); - connection->buffer = buffer; - connection->buffer_length = buffer_length; -} - -int ICACHE_FLASH_ATTR mqtt_get_total_length(uint8_t* buffer, uint16_t length) -{ - int i; - int totlen = 0; - - for (i = 1; i < length; ++i) - { - totlen += (buffer[i] & 0x7f) << (7 * (i - 1)); - if ((buffer[i] & 0x80) == 0) - { - ++i; - break; - } - } - totlen += i; - - return totlen; -} - -const char* ICACHE_FLASH_ATTR mqtt_get_publish_topic(uint8_t* buffer, uint16_t* length) -{ - int i; - int totlen = 0; - int topiclen; - - for (i = 1; i < *length; ++i) - { - totlen += (buffer[i] & 0x7f) << (7 * (i - 1)); - if ((buffer[i] & 0x80) == 0) - { - ++i; - break; - } - } - totlen += i; - - if (i + 2 >= *length) - return NULL; - topiclen = buffer[i++] << 8; - topiclen |= buffer[i++]; - - if (i + topiclen > *length) - return NULL; - - *length = topiclen; - return (const char*)(buffer + i); -} - -const char* ICACHE_FLASH_ATTR mqtt_get_publish_data(uint8_t* buffer, uint16_t* length) -{ - int i; - int totlen = 0; - int topiclen; - int blength = *length; - *length = 0; - - for (i = 1; i < blength; ++i) - { - totlen += (buffer[i] & 0x7f) << (7 * (i - 1)); - if ((buffer[i] & 0x80) == 0) - { - ++i; - break; - } - } - totlen += i; - - if (i + 2 >= blength) - return NULL; - topiclen = buffer[i++] << 8; - topiclen |= buffer[i++]; - - if (i + topiclen >= blength) - return NULL; - - i += topiclen; - - if (mqtt_get_qos(buffer) > 0) - { - if (i + 2 >= blength) - return NULL; - i += 2; - } - - if (totlen < i) - return NULL; - - if (totlen <= blength) - *length = totlen - i; - else - *length = blength - i; - return (const char*)(buffer + i); -} - -uint16_t ICACHE_FLASH_ATTR mqtt_get_id(uint8_t* buffer, uint16_t length) -{ - if (length < 1) - return 0; - - switch (mqtt_get_type(buffer)) - { - case MQTT_MSG_TYPE_PUBLISH: - { - int i; - int topiclen; - - for (i = 1; i < length; ++i) - { - if ((buffer[i] & 0x80) == 0) - { - ++i; - break; - } - } - - if (i + 2 >= length) - return 0; - topiclen = buffer[i++] << 8; - topiclen |= buffer[i++]; - - if (i + topiclen >= length) - return 0; - i += topiclen; - - if (mqtt_get_qos(buffer) > 0) - { - if (i + 2 >= length) - return 0; - //i += 2; - } else { - return 0; - } - - return (buffer[i] << 8) | buffer[i + 1]; - } - case MQTT_MSG_TYPE_PUBACK: - case MQTT_MSG_TYPE_PUBREC: - case MQTT_MSG_TYPE_PUBREL: - case MQTT_MSG_TYPE_PUBCOMP: - case MQTT_MSG_TYPE_SUBACK: - case MQTT_MSG_TYPE_UNSUBACK: - case MQTT_MSG_TYPE_SUBSCRIBE: - { - // This requires the remaining length to be encoded in 1 byte, - // which it should be. - if (length >= 4 && (buffer[1] & 0x80) == 0) - return (buffer[2] << 8) | buffer[3]; - else - return 0; - } - - default: - return 0; - } -} - -mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_connect(mqtt_connection_t* connection, mqtt_connect_info_t* info) -{ - struct mqtt_connect_variable_header* variable_header; - - init_message(connection); - - if (connection->message.length + sizeof(*variable_header) > connection->buffer_length) - return fail_message(connection); - variable_header = (void*)(connection->buffer + connection->message.length); - connection->message.length += sizeof(*variable_header); - - variable_header->lengthMsb = 0; -#if defined(PROTOCOL_NAMEv31) - variable_header->lengthLsb = 6; - memcpy(variable_header->magic, "MQIsdp", 6); - variable_header->version = 3; -#elif defined(PROTOCOL_NAMEv311) - variable_header->lengthLsb = 4; - memcpy(variable_header->magic, "MQTT", 4); - variable_header->version = 4; -#else -#error "Please define protocol name" -#endif - - variable_header->flags = 0; - variable_header->keepaliveMsb = info->keepalive >> 8; - variable_header->keepaliveLsb = info->keepalive & 0xff; - - if (info->clean_session) - variable_header->flags |= MQTT_CONNECT_FLAG_CLEAN_SESSION; - - if (info->client_id == NULL) - { - /* Never allowed */ - return fail_message(connection); - } - else if (info->client_id[0] == '\0') - { -#ifdef PROTOCOL_NAMEv311 - /* Allowed. Format 0 Length ID */ - append_string(connection, info->client_id, 2) ; -#else - /* 0 Length not allowed */ - return fail_message(connection); -#endif - } - else - { - /* No 0 data and at least 1 long. Good to go. */ - if(append_string(connection, info->client_id, strlen(info->client_id)) < 0) - return fail_message(connection); - } - - if (info->will_topic != NULL && info->will_topic[0] != '\0') - { - if (append_string(connection, info->will_topic, strlen(info->will_topic)) < 0) - return fail_message(connection); - - if (append_string(connection, info->will_message, strlen(info->will_message)) < 0) - return fail_message(connection); - - variable_header->flags |= MQTT_CONNECT_FLAG_WILL; - if (info->will_retain) - variable_header->flags |= MQTT_CONNECT_FLAG_WILL_RETAIN; - variable_header->flags |= (info->will_qos & 3) << 3; - } - - if (info->username != NULL && info->username[0] != '\0') - { - if (append_string(connection, info->username, strlen(info->username)) < 0) - return fail_message(connection); - - variable_header->flags |= MQTT_CONNECT_FLAG_USERNAME; - } - - if (info->password != NULL && info->password[0] != '\0') - { - if (append_string(connection, info->password, strlen(info->password)) < 0) - return fail_message(connection); - - variable_header->flags |= MQTT_CONNECT_FLAG_PASSWORD; - } - - return fini_message(connection, MQTT_MSG_TYPE_CONNECT, 0, 0, 0); -} - -mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_publish(mqtt_connection_t* connection, const char* topic, const char* data, int data_length, int qos, int retain, uint16_t* message_id) -{ - init_message(connection); - - if (topic == NULL || topic[0] == '\0') - return fail_message(connection); - - if (append_string(connection, topic, strlen(topic)) < 0) - return fail_message(connection); - - if (qos > 0) - { - if ((*message_id = append_message_id(connection, 0)) == 0) - return fail_message(connection); - } - else - *message_id = 0; - - if (connection->message.length + data_length > connection->buffer_length) - return fail_message(connection); - memcpy(connection->buffer + connection->message.length, data, data_length); - connection->message.length += data_length; - - return fini_message(connection, MQTT_MSG_TYPE_PUBLISH, 0, qos, retain); -} - -mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_puback(mqtt_connection_t* connection, uint16_t message_id) -{ - init_message(connection); - if (append_message_id(connection, message_id) == 0) - return fail_message(connection); - return fini_message(connection, MQTT_MSG_TYPE_PUBACK, 0, 0, 0); -} - -mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pubrec(mqtt_connection_t* connection, uint16_t message_id) -{ - init_message(connection); - if (append_message_id(connection, message_id) == 0) - return fail_message(connection); - return fini_message(connection, MQTT_MSG_TYPE_PUBREC, 0, 0, 0); -} - -mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pubrel(mqtt_connection_t* connection, uint16_t message_id) -{ - init_message(connection); - if (append_message_id(connection, message_id) == 0) - return fail_message(connection); - return fini_message(connection, MQTT_MSG_TYPE_PUBREL, 0, 1, 0); -} - -mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pubcomp(mqtt_connection_t* connection, uint16_t message_id) -{ - init_message(connection); - if (append_message_id(connection, message_id) == 0) - return fail_message(connection); - return fini_message(connection, MQTT_MSG_TYPE_PUBCOMP, 0, 0, 0); -} - -mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_subscribe(mqtt_connection_t* connection, const char* topic, int qos, uint16_t* message_id) -{ - init_message(connection); - - if (topic == NULL || topic[0] == '\0') - return fail_message(connection); - - if ((*message_id = append_message_id(connection, 0)) == 0) - return fail_message(connection); - - if (append_string(connection, topic, strlen(topic)) < 0) - return fail_message(connection); - - if (connection->message.length + 1 > connection->buffer_length) - return fail_message(connection); - connection->buffer[connection->message.length++] = qos; - - return fini_message(connection, MQTT_MSG_TYPE_SUBSCRIBE, 0, 1, 0); -} - -mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_unsubscribe(mqtt_connection_t* connection, const char* topic, uint16_t* message_id) -{ - init_message(connection); - - if (topic == NULL || topic[0] == '\0') - return fail_message(connection); - - if ((*message_id = append_message_id(connection, 0)) == 0) - return fail_message(connection); - - if (append_string(connection, topic, strlen(topic)) < 0) - return fail_message(connection); - - return fini_message(connection, MQTT_MSG_TYPE_UNSUBSCRIBE, 0, 1, 0); -} - -mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pingreq(mqtt_connection_t* connection) -{ - init_message(connection); - return fini_message(connection, MQTT_MSG_TYPE_PINGREQ, 0, 0, 0); -} - -mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pingresp(mqtt_connection_t* connection) -{ - init_message(connection); - return fini_message(connection, MQTT_MSG_TYPE_PINGRESP, 0, 0, 0); -} - -mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_disconnect(mqtt_connection_t* connection) -{ - init_message(connection); - return fini_message(connection, MQTT_MSG_TYPE_DISCONNECT, 0, 0, 0); -} diff --git a/lib/TasmotaMqtt-1.1.1/src/mqtt/mqtt_msg.h b/lib/TasmotaMqtt-1.1.1/src/mqtt/mqtt_msg.h deleted file mode 100644 index be3cc55cb..000000000 --- a/lib/TasmotaMqtt-1.1.1/src/mqtt/mqtt_msg.h +++ /dev/null @@ -1,141 +0,0 @@ -/* - * File: mqtt_msg.h - * Author: Minh Tuan - * - * Created on July 12, 2014, 1:05 PM - */ - -#ifndef MQTT_MSG_H -#define MQTT_MSG_H -#include "user_config.h" -#include "c_types.h" -#ifdef __cplusplus -extern "C" { -#endif - -/* -* Copyright (c) 2014, Stephen Robinson -* All rights reserved. -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions -* are met: -* -* 1. Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* 2. 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. -* 3. Neither the name of the copyright holder nor the names of its -* contributors may be used to endorse or promote products derived -* from this software without specific prior written permission. -* -* 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. -* -*/ -/* 7 6 5 4 3 2 1 0*/ -/*| --- Message Type---- | DUP Flag | QoS Level | Retain | -/* Remaining Length */ - - -enum mqtt_message_type -{ - MQTT_MSG_TYPE_CONNECT = 1, - MQTT_MSG_TYPE_CONNACK = 2, - MQTT_MSG_TYPE_PUBLISH = 3, - MQTT_MSG_TYPE_PUBACK = 4, - MQTT_MSG_TYPE_PUBREC = 5, - MQTT_MSG_TYPE_PUBREL = 6, - MQTT_MSG_TYPE_PUBCOMP = 7, - MQTT_MSG_TYPE_SUBSCRIBE = 8, - MQTT_MSG_TYPE_SUBACK = 9, - MQTT_MSG_TYPE_UNSUBSCRIBE = 10, - MQTT_MSG_TYPE_UNSUBACK = 11, - MQTT_MSG_TYPE_PINGREQ = 12, - MQTT_MSG_TYPE_PINGRESP = 13, - MQTT_MSG_TYPE_DISCONNECT = 14 -}; - -enum mqtt_connect_return_code -{ - CONNECTION_ACCEPTED = 0, - CONNECTION_REFUSE_PROTOCOL, - CONNECTION_REFUSE_ID_REJECTED, - CONNECTION_REFUSE_SERVER_UNAVAILABLE, - CONNECTION_REFUSE_BAD_USERNAME, - CONNECTION_REFUSE_NOT_AUTHORIZED -}; - -typedef struct mqtt_message -{ - uint8_t* data; - uint16_t length; - -} mqtt_message_t; - -typedef struct mqtt_connection -{ - mqtt_message_t message; - - uint16_t message_id; - uint8_t* buffer; - uint16_t buffer_length; - -} mqtt_connection_t; - -typedef struct mqtt_connect_info -{ - char* client_id; - char* username; - char* password; - char* will_topic; - char* will_message; - uint32_t keepalive; - int will_qos; - int will_retain; - int clean_session; - -} mqtt_connect_info_t; - - -static inline int ICACHE_FLASH_ATTR mqtt_get_type(uint8_t* buffer) { return (buffer[0] & 0xf0) >> 4; } -static inline int ICACHE_FLASH_ATTR mqtt_get_connect_return_code(uint8_t* buffer) { return buffer[3]; } -static inline int ICACHE_FLASH_ATTR mqtt_get_dup(uint8_t* buffer) { return (buffer[0] & 0x08) >> 3; } -static inline int ICACHE_FLASH_ATTR mqtt_get_qos(uint8_t* buffer) { return (buffer[0] & 0x06) >> 1; } -static inline int ICACHE_FLASH_ATTR mqtt_get_retain(uint8_t* buffer) { return (buffer[0] & 0x01); } - -void ICACHE_FLASH_ATTR mqtt_msg_init(mqtt_connection_t* connection, uint8_t* buffer, uint16_t buffer_length); -int ICACHE_FLASH_ATTR mqtt_get_total_length(uint8_t* buffer, uint16_t length); -const char* ICACHE_FLASH_ATTR mqtt_get_publish_topic(uint8_t* buffer, uint16_t* length); -const char* ICACHE_FLASH_ATTR mqtt_get_publish_data(uint8_t* buffer, uint16_t* length); -uint16_t ICACHE_FLASH_ATTR mqtt_get_id(uint8_t* buffer, uint16_t length); - -mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_connect(mqtt_connection_t* connection, mqtt_connect_info_t* info); -mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_publish(mqtt_connection_t* connection, const char* topic, const char* data, int data_length, int qos, int retain, uint16_t* message_id); -mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_puback(mqtt_connection_t* connection, uint16_t message_id); -mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pubrec(mqtt_connection_t* connection, uint16_t message_id); -mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pubrel(mqtt_connection_t* connection, uint16_t message_id); -mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pubcomp(mqtt_connection_t* connection, uint16_t message_id); -mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_subscribe(mqtt_connection_t* connection, const char* topic, int qos, uint16_t* message_id); -mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_unsubscribe(mqtt_connection_t* connection, const char* topic, uint16_t* message_id); -mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pingreq(mqtt_connection_t* connection); -mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pingresp(mqtt_connection_t* connection); -mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_disconnect(mqtt_connection_t* connection); - - -#ifdef __cplusplus -} -#endif - -#endif /* MQTT_MSG_H */ - diff --git a/lib/TasmotaMqtt-1.1.1/src/mqtt/proto.c b/lib/TasmotaMqtt-1.1.1/src/mqtt/proto.c deleted file mode 100644 index 84078b233..000000000 --- a/lib/TasmotaMqtt-1.1.1/src/mqtt/proto.c +++ /dev/null @@ -1,129 +0,0 @@ -#include "proto.h" -#include "ringbuf.h" -I8 ICACHE_FLASH_ATTR PROTO_Init(PROTO_PARSER *parser, PROTO_PARSE_CALLBACK *completeCallback, U8 *buf, U16 bufSize) -{ - parser->buf = buf; - parser->bufSize = bufSize; - parser->dataLen = 0; - parser->callback = completeCallback; - parser->isEsc = 0; - return 0; -} - -I8 ICACHE_FLASH_ATTR PROTO_ParseByte(PROTO_PARSER *parser, U8 value) -{ - switch (value) { - case 0x7D: - parser->isEsc = 1; - break; - - case 0x7E: - parser->dataLen = 0; - parser->isEsc = 0; - parser->isBegin = 1; - break; - - case 0x7F: - if (parser->callback != NULL) - parser->callback(); - parser->isBegin = 0; - return 0; - break; - - default: - if (parser->isBegin == 0) break; - - if (parser->isEsc) { - value ^= 0x20; - parser->isEsc = 0; - } - - if (parser->dataLen < parser->bufSize) - parser->buf[parser->dataLen++] = value; - - break; - } - return -1; -} - -I8 ICACHE_FLASH_ATTR PROTO_Parse(PROTO_PARSER *parser, U8 *buf, U16 len) -{ - while (len--) - PROTO_ParseByte(parser, *buf++); - - return 0; -} -I16 ICACHE_FLASH_ATTR PROTO_ParseRb(RINGBUF* rb, U8 *bufOut, U16* len, U16 maxBufLen) -{ - U8 c; - - PROTO_PARSER proto; - PROTO_Init(&proto, NULL, bufOut, maxBufLen); - while (RINGBUF_Get(rb, &c) == 0) { - if (PROTO_ParseByte(&proto, c) == 0) { - *len = proto.dataLen; - return 0; - } - } - return -1; -} -I16 ICACHE_FLASH_ATTR PROTO_Add(U8 *buf, const U8 *packet, I16 bufSize) -{ - U16 i = 2; - U16 len = *(U16*) packet; - - if (bufSize < 1) return -1; - - *buf++ = 0x7E; - bufSize--; - - while (len--) { - switch (*packet) { - case 0x7D: - case 0x7E: - case 0x7F: - if (bufSize < 2) return -1; - *buf++ = 0x7D; - *buf++ = *packet++ ^ 0x20; - i += 2; - bufSize -= 2; - break; - default: - if (bufSize < 1) return -1; - *buf++ = *packet++; - i++; - bufSize--; - break; - } - } - - if (bufSize < 1) return -1; - *buf++ = 0x7F; - - return i; -} - -I16 ICACHE_FLASH_ATTR PROTO_AddRb(RINGBUF *rb, const U8 *packet, I16 len) -{ - U16 i = 2; - if (RINGBUF_Put(rb, 0x7E) == -1) return -1; - while (len--) { - switch (*packet) { - case 0x7D: - case 0x7E: - case 0x7F: - if (RINGBUF_Put(rb, 0x7D) == -1) return -1; - if (RINGBUF_Put(rb, *packet++ ^ 0x20) == -1) return -1; - i += 2; - break; - default: - if (RINGBUF_Put(rb, *packet++) == -1) return -1; - i++; - break; - } - } - if (RINGBUF_Put(rb, 0x7F) == -1) return -1; - - return i; -} - diff --git a/lib/TasmotaMqtt-1.1.1/src/mqtt/proto.h b/lib/TasmotaMqtt-1.1.1/src/mqtt/proto.h deleted file mode 100644 index a405bcb95..000000000 --- a/lib/TasmotaMqtt-1.1.1/src/mqtt/proto.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * File: proto.h - * Author: ThuHien - * - * Created on November 23, 2012, 8:57 AM - */ - -#ifndef _PROTO_H_ -#define _PROTO_H_ -#include -#include "typedef.h" -#include "ringbuf.h" - -typedef void(PROTO_PARSE_CALLBACK)(); - -typedef struct { - U8 *buf; - U16 bufSize; - U16 dataLen; - U8 isEsc; - U8 isBegin; - PROTO_PARSE_CALLBACK* callback; -} PROTO_PARSER; - -I8 ICACHE_FLASH_ATTR PROTO_Init(PROTO_PARSER *parser, PROTO_PARSE_CALLBACK *completeCallback, U8 *buf, U16 bufSize); -I8 ICACHE_FLASH_ATTR PROTO_Parse(PROTO_PARSER *parser, U8 *buf, U16 len); -I16 ICACHE_FLASH_ATTR PROTO_Add(U8 *buf, const U8 *packet, I16 bufSize); -I16 ICACHE_FLASH_ATTR PROTO_AddRb(RINGBUF *rb, const U8 *packet, I16 len); -I8 ICACHE_FLASH_ATTR PROTO_ParseByte(PROTO_PARSER *parser, U8 value); -I16 ICACHE_FLASH_ATTR PROTO_ParseRb(RINGBUF *rb, U8 *bufOut, U16* len, U16 maxBufLen); -#endif - diff --git a/lib/TasmotaMqtt-1.1.1/src/mqtt/queue.c b/lib/TasmotaMqtt-1.1.1/src/mqtt/queue.c deleted file mode 100644 index 5e4216d0f..000000000 --- a/lib/TasmotaMqtt-1.1.1/src/mqtt/queue.c +++ /dev/null @@ -1,75 +0,0 @@ -/* str_queue.c -* -* Copyright (c) 2014-2015, Tuan PM -* 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. -* * Neither the name of Redis nor the names of its contributors may be used -* to endorse or promote products derived from this software without -* specific prior written permission. -* -* 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 OWNER 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. -*/ -#include "queue.h" - -#include "user_interface.h" -#include "osapi.h" -#include "os_type.h" -#include "mem.h" -#include "proto.h" - -uint8_t *last_rb_p_r; -uint8_t *last_rb_p_w; -uint32_t last_fill_cnt; - -void ICACHE_FLASH_ATTR QUEUE_Init(QUEUE *queue, int bufferSize) -{ - queue->buf = (uint8_t*)os_zalloc(bufferSize); - RINGBUF_Init(&queue->rb, queue->buf, bufferSize); -} -int32_t ICACHE_FLASH_ATTR QUEUE_Puts(QUEUE *queue, uint8_t* buffer, uint16_t len) -{ - uint32_t ret; - - last_rb_p_r = queue->rb.p_r; - last_rb_p_w = queue->rb.p_w; - last_fill_cnt = queue->rb.fill_cnt; - - ret = PROTO_AddRb(&queue->rb, buffer, len); - if (ret == -1) { - // rolling ring buffer back - queue->rb.p_r = last_rb_p_r; - queue->rb.p_w = last_rb_p_w; - queue->rb.fill_cnt = last_fill_cnt; - } - return ret; -} -int32_t ICACHE_FLASH_ATTR QUEUE_Gets(QUEUE *queue, uint8_t* buffer, uint16_t* len, uint16_t maxLen) -{ - - return PROTO_ParseRb(&queue->rb, buffer, len, maxLen); -} - -bool ICACHE_FLASH_ATTR QUEUE_IsEmpty(QUEUE *queue) -{ - if (queue->rb.fill_cnt <= 0) - return TRUE; - return FALSE; -} diff --git a/lib/TasmotaMqtt-1.1.1/src/mqtt/queue.h b/lib/TasmotaMqtt-1.1.1/src/mqtt/queue.h deleted file mode 100644 index 79107f2d5..000000000 --- a/lib/TasmotaMqtt-1.1.1/src/mqtt/queue.h +++ /dev/null @@ -1,44 +0,0 @@ -/* str_queue.h -- -* -* Copyright (c) 2014-2015, Tuan PM -* 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. -* * Neither the name of Redis nor the names of its contributors may be used -* to endorse or promote products derived from this software without -* specific prior written permission. -* -* 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 OWNER 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. -*/ - -#ifndef USER_QUEUE_H_ -#define USER_QUEUE_H_ -#include "os_type.h" -#include "ringbuf.h" -typedef struct { - uint8_t *buf; - RINGBUF rb; -} QUEUE; - -void ICACHE_FLASH_ATTR QUEUE_Init(QUEUE *queue, int bufferSize); -int32_t ICACHE_FLASH_ATTR QUEUE_Puts(QUEUE *queue, uint8_t* buffer, uint16_t len); -int32_t ICACHE_FLASH_ATTR QUEUE_Gets(QUEUE *queue, uint8_t* buffer, uint16_t* len, uint16_t maxLen); -bool ICACHE_FLASH_ATTR QUEUE_IsEmpty(QUEUE *queue); -#endif /* USER_QUEUE_H_ */ diff --git a/lib/TasmotaMqtt-1.1.1/src/mqtt/ringbuf.c b/lib/TasmotaMqtt-1.1.1/src/mqtt/ringbuf.c deleted file mode 100644 index fc882fd5c..000000000 --- a/lib/TasmotaMqtt-1.1.1/src/mqtt/ringbuf.c +++ /dev/null @@ -1,67 +0,0 @@ -/** -* \file -* Ring Buffer library -*/ - -#include "ringbuf.h" - - -/** -* \brief init a RINGBUF object -* \param r pointer to a RINGBUF object -* \param buf pointer to a byte array -* \param size size of buf -* \return 0 if successfull, otherwise failed -*/ -I16 ICACHE_FLASH_ATTR RINGBUF_Init(RINGBUF *r, U8* buf, I32 size) -{ - if (r == NULL || buf == NULL || size < 2) return -1; - - r->p_o = r->p_r = r->p_w = buf; - r->fill_cnt = 0; - r->size = size; - - return 0; -} -/** -* \brief put a character into ring buffer -* \param r pointer to a ringbuf object -* \param c character to be put -* \return 0 if successfull, otherwise failed -*/ -I16 ICACHE_FLASH_ATTR RINGBUF_Put(RINGBUF *r, U8 c) -{ - if (r->fill_cnt >= r->size)return -1; // ring buffer is full, this should be atomic operation - - - r->fill_cnt++; // increase filled slots count, this should be atomic operation - - - *r->p_w++ = c; // put character into buffer - - if (r->p_w >= r->p_o + r->size) // rollback if write pointer go pass - r->p_w = r->p_o; // the physical boundary - - return 0; -} -/** -* \brief get a character from ring buffer -* \param r pointer to a ringbuf object -* \param c read character -* \return 0 if successfull, otherwise failed -*/ -I16 ICACHE_FLASH_ATTR RINGBUF_Get(RINGBUF *r, U8* c) -{ - if (r->fill_cnt <= 0)return -1; // ring buffer is empty, this should be atomic operation - - - r->fill_cnt--; // decrease filled slots count - - - *c = *r->p_r++; // get the character out - - if (r->p_r >= r->p_o + r->size) // rollback if write pointer go pass - r->p_r = r->p_o; // the physical boundary - - return 0; -} diff --git a/lib/TasmotaMqtt-1.1.1/src/mqtt/ringbuf.h b/lib/TasmotaMqtt-1.1.1/src/mqtt/ringbuf.h deleted file mode 100644 index f1a4f7e8b..000000000 --- a/lib/TasmotaMqtt-1.1.1/src/mqtt/ringbuf.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef _RING_BUF_H_ -#define _RING_BUF_H_ - -#include -#include -#include "typedef.h" - -typedef struct { - U8* p_o; /**< Original pointer */ - U8* volatile p_r; /**< Read pointer */ - U8* volatile p_w; /**< Write pointer */ - volatile I32 fill_cnt; /**< Number of filled slots */ - I32 size; /**< Buffer size */ -} RINGBUF; - -I16 ICACHE_FLASH_ATTR RINGBUF_Init(RINGBUF *r, U8* buf, I32 size); -I16 ICACHE_FLASH_ATTR RINGBUF_Put(RINGBUF *r, U8 c); -I16 ICACHE_FLASH_ATTR RINGBUF_Get(RINGBUF *r, U8* c); -#endif diff --git a/lib/TasmotaMqtt-1.1.1/src/mqtt/typedef.h b/lib/TasmotaMqtt-1.1.1/src/mqtt/typedef.h deleted file mode 100644 index 887001ace..000000000 --- a/lib/TasmotaMqtt-1.1.1/src/mqtt/typedef.h +++ /dev/null @@ -1,17 +0,0 @@ -/** -* \file -* Standard Types definition -*/ - -#ifndef _TYPE_DEF_H_ -#define _TYPE_DEF_H_ - -typedef char I8; -typedef unsigned char U8; -typedef short I16; -typedef unsigned short U16; -typedef long I32; -typedef unsigned long U32; -typedef unsigned long long U64; - -#endif diff --git a/lib/TasmotaMqtt-1.1.1/src/mqtt/user_config.h b/lib/TasmotaMqtt-1.1.1/src/mqtt/user_config.h deleted file mode 100644 index 125353ed0..000000000 --- a/lib/TasmotaMqtt-1.1.1/src/mqtt/user_config.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef __MQTT_CONFIG_H__ -#define __MQTT_CONFIG_H__ - -//#define MQTT_SSL_ENABLE - -#define MQTT_RECONNECT_TIMEOUT 5 /*second*/ - -//#define MQTT_BUF_SIZE 1024 -#define MQTT_BUF_SIZE 512 -#define QUEUE_BUFFER_SIZE 2048 - -//#define PROTOCOL_NAMEv31 /*MQTT version 3.1 compatible with Mosquitto v0.15*/ -#define PROTOCOL_NAMEv311 /*MQTT version 3.11 compatible with https://eclipse.org/paho/clients/testing/*/ - -#endif // __MQTT_CONFIG_H__ \ No newline at end of file diff --git a/lib/TasmotaMqtt-1.1.1/src/mqtt/utils.c b/lib/TasmotaMqtt-1.1.1/src/mqtt/utils.c deleted file mode 100644 index ac4c9272b..000000000 --- a/lib/TasmotaMqtt-1.1.1/src/mqtt/utils.c +++ /dev/null @@ -1,149 +0,0 @@ -/* -* Copyright (c) 2014, Tuan PM -* Email: tuanpm@live.com -* -* All rights reserved. -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions -* are met: -* -* 1. Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* 2. 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. -* 3. Neither the name of the copyright holder nor the names of its -* contributors may be used to endorse or promote products derived -* from this software without specific prior written permission. -* -* 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. -* -*/ -#include -#include -#include -#include -#include -#include "utils.h" - - -uint8_t ICACHE_FLASH_ATTR UTILS_IsIPV4 (int8_t *str) -{ - uint8_t segs = 0; /* Segment count. */ - uint8_t chcnt = 0; /* Character count within segment. */ - uint8_t accum = 0; /* Accumulator for segment. */ - /* Catch NULL pointer. */ - if (str == 0) - return 0; - /* Process every character in string. */ - - while (*str != '\0') { - /* Segment changeover. */ - - if (*str == '.') { - /* Must have some digits in segment. */ - if (chcnt == 0) - return 0; - /* Limit number of segments. */ - if (++segs == 4) - return 0; - /* Reset segment values and restart loop. */ - chcnt = accum = 0; - str++; - continue; - } - - /* Check numeric. */ - if ((*str < '0') || (*str > '9')) - return 0; - - /* Accumulate and check segment. */ - - if ((accum = accum * 10 + *str - '0') > 255) - return 0; - /* Advance other segment specific stuff and continue loop. */ - - chcnt++; - str++; - } - - /* Check enough segments and enough characters in last segment. */ - - if (segs != 3) - return 0; - if (chcnt == 0) - return 0; - /* Address okay. */ - - return 1; -} -uint8_t ICACHE_FLASH_ATTR UTILS_StrToIP(const int8_t* str, void *ip) -{ - - /* The count of the number of bytes processed. */ - int i; - /* A pointer to the next digit to process. */ - const char * start; - - start = str; - for (i = 0; i < 4; i++) { - /* The digit being processed. */ - char c; - /* The value of this byte. */ - int n = 0; - while (1) { - c = * start; - start++; - if (c >= '0' && c <= '9') { - n *= 10; - n += c - '0'; - } - /* We insist on stopping at "." if we are still parsing - the first, second, or third numbers. If we have reached - the end of the numbers, we will allow any character. */ - else if ((i < 3 && c == '.') || i == 3) { - break; - } - else { - return 0; - } - } - if (n >= 256) { - return 0; - } - ((uint8_t*)ip)[i] = n; - } - return 1; - -} -uint32_t ICACHE_FLASH_ATTR UTILS_Atoh(const int8_t *s) -{ - uint32_t value = 0, digit; - int8_t c; - - while ((c = *s++)) { - if ('0' <= c && c <= '9') - digit = c - '0'; - else if ('A' <= c && c <= 'F') - digit = c - 'A' + 10; - else if ('a' <= c && c <= 'f') - digit = c - 'a' + 10; - else break; - - value = (value << 4) | digit; - } - - return value; -} - diff --git a/lib/TasmotaMqtt-1.1.1/src/mqtt/utils.h b/lib/TasmotaMqtt-1.1.1/src/mqtt/utils.h deleted file mode 100644 index fe2874803..000000000 --- a/lib/TasmotaMqtt-1.1.1/src/mqtt/utils.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef _UTILS_H_ -#define _UTILS_H_ - -#include "c_types.h" - -uint32_t ICACHE_FLASH_ATTR UTILS_Atoh(const int8_t *s); -uint8_t ICACHE_FLASH_ATTR UTILS_StrToIP(const int8_t* str, void *ip); -uint8_t ICACHE_FLASH_ATTR UTILS_IsIPV4 (int8_t *str); -#endif diff --git a/lib/arduino-mqtt-2.4.0/.editorconfig b/lib/arduino-mqtt-2.4.0/.editorconfig deleted file mode 100644 index 3edae7b53..000000000 --- a/lib/arduino-mqtt-2.4.0/.editorconfig +++ /dev/null @@ -1,7 +0,0 @@ -[Makefile] -indent_style = tab -indent_size = 4 - -[src/*.h,src/*.cpp,examples/**.ino] -indent_style = space -indent_size = 2 diff --git a/lib/arduino-mqtt-2.4.0/.gitignore b/lib/arduino-mqtt-2.4.0/.gitignore deleted file mode 100644 index 0c4fe4711..000000000 --- a/lib/arduino-mqtt-2.4.0/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -.DS_Store -cmake-build-debug/ diff --git a/lib/arduino-mqtt-2.4.0/.travis.yml b/lib/arduino-mqtt-2.4.0/.travis.yml deleted file mode 100644 index 6971b9fba..000000000 --- a/lib/arduino-mqtt-2.4.0/.travis.yml +++ /dev/null @@ -1,46 +0,0 @@ -language: generic -env: - global: - - IDE_VERSION=1.8.7 - matrix: - - EXAMPLE="AdafruitHuzzahESP8266" BOARD="esp8266:esp8266:huzzah:FlashSize=4M3M,CpuFrequency=80" - - EXAMPLE="AdafruitHuzzahESP8266Secure" BOARD="esp8266:esp8266:huzzah:FlashSize=4M3M,CpuFrequency=80" - - EXAMPLE="ArduinoEthernetShield" BOARD="arduino:avr:uno" - - EXAMPLE="ArduinoMKRGSM1400" BOARD="arduino:samd:mkrgsm1400" - - EXAMPLE="ArduinoMKRGSM1400Secure" BOARD="arduino:samd:mkrgsm1400" - - EXAMPLE="ArduinoWiFi101Secure" BOARD="arduino:avr:uno" - - EXAMPLE="ArduinoWiFiShield" BOARD="arduino:avr:uno" - - EXAMPLE="ArduinoYun" BOARD="arduino:avr:yun" - - EXAMPLE="ArduinoYunSecure" BOARD="arduino:avr:yun" - - EXAMPLE="ESP32DevelopmentBoard" BOARD="espressif:esp32:esp32:FlashFreq=80" - - EXAMPLE="ESP32DevelopmentBoardSecure" BOARD="espressif:esp32:esp32:FlashFreq=80" -before_install: - - /sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_1.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :1 -ac -screen 0 1280x1024x16 - - sleep 3 - - export DISPLAY=:1.0 - - wget http://downloads.arduino.cc/arduino-$IDE_VERSION-linux64.tar.xz - - tar xf arduino-$IDE_VERSION-linux64.tar.xz - - mv arduino-$IDE_VERSION ~/arduino-ide - - export PATH=$PATH:~/arduino-ide - - if [[ "$BOARD" =~ "esp8266:esp8266:" ]]; then - arduino --pref "boardsmanager.additional.urls=http://arduino.esp8266.com/stable/package_esp8266com_index.json" --install-boards esp8266:esp8266; - arduino --pref "boardsmanager.additional.urls=" --save-prefs; - fi - - if [[ "$BOARD" =~ "espressif:esp32:" ]]; then - mkdir -p ~/Arduino/hardware/espressif && - cd ~/Arduino/hardware/espressif && - git clone https://github.com/espressif/arduino-esp32.git esp32 && - cd esp32/tools/ && - python get.py && - cd $TRAVIS_BUILD_DIR; - fi - - if [[ "$BOARD" =~ "arduino:samd:mkrgsm1400" ]]; then - arduino --install-boards arduino:samd; - arduino --install-library MKRGSM; - fi - - arduino --install-library WiFi101 -install: - - mkdir -p ~/Arduino/libraries - - ln -s $PWD ~/Arduino/libraries/. -script: - - arduino --verbose-build --verify --board $BOARD $PWD/examples/$EXAMPLE/$EXAMPLE.ino; diff --git a/lib/arduino-mqtt-2.4.0/CMakeLists.txt b/lib/arduino-mqtt-2.4.0/CMakeLists.txt deleted file mode 100644 index 328ce0c87..000000000 --- a/lib/arduino-mqtt-2.4.0/CMakeLists.txt +++ /dev/null @@ -1,38 +0,0 @@ -# Uncompilable CMake File to enable project editing with CLion IDE - -cmake_minimum_required(VERSION 2.8.4) -project(arduino-mqtt) - -include_directories( - /Applications/Arduino.app/Contents/Java/hardware/arduino/avr/cores/arduino/ - /Users/256dpi/Development/Arduino/libraries/Ethernet/src - /Users/256dpi/Development/Arduino/libraries/WiFi101/src - /Users/256dpi/Development/Arduino/libraries/MKRGSM/src - /Applications/Arduino.app/Contents/Java/libraries/Bridge/src - /Users/256dpi/Library/Arduino15/packages/esp8266/hardware/esp8266/2.3.0/libraries/ESP8266WiFi/src - /Users/256dpi/Library/Arduino15/packages/esp32/libraries/WiFi/src - /Users/256dpi/Library/Arduino15/packages/esp32/libraries/WiFiClientSecure/src - src/) - -include_directories(src/) - -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") - -set(SOURCE_FILES - examples/AdafruitHuzzahESP8266/AdafruitHuzzahESP8266.ino - examples/AdafruitHuzzahESP8266Secure/AdafruitHuzzahESP8266Secure.ino - examples/ArduinoEthernetShield/ArduinoEthernetShield.ino - examples/ArduinoMKRGSM1400/ArduinoMKRGSM1400.ino - examples/ArduinoMKRGSM1400Secure/ArduinoMKRGSM1400Secure.ino - examples/ArduinoWiFi101/ArduinoWiFi101.ino - examples/ArduinoWiFi101Secure/ArduinoWiFi101Secure.ino - examples/ArduinoWiFiShield/ArduinoWiFiShield.ino - examples/ArduinoYun/ArduinoYun.ino - examples/ArduinoYunSecure/ArduinoYunSecure.ino - examples/ESP32DevelopmentBoard/ESP32DevelopmentBoard.ino - examples/ESP32DevelopmentBoardSecure/ESP32DevelopmentBoardSecure.ino - src/lwmqtt - src/MQTT.h - src/MQTTClient.h) - -add_executable(arduino-mqtt ${SOURCE_FILES}) diff --git a/lib/arduino-mqtt-2.4.0/LICENSE.md b/lib/arduino-mqtt-2.4.0/LICENSE.md deleted file mode 100644 index 325e07cff..000000000 --- a/lib/arduino-mqtt-2.4.0/LICENSE.md +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2015 Joël Gähwiler - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/lib/arduino-mqtt-2.4.0/Makefile b/lib/arduino-mqtt-2.4.0/Makefile deleted file mode 100644 index 9e734fcc3..000000000 --- a/lib/arduino-mqtt-2.4.0/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -all: fmt - -fmt: - clang-format -i src/*.h -style="{BasedOnStyle: Google, ColumnLimit: 120}" - -update: - rm -rf ./lwmqtt - git clone --branch v0.6.2 https://github.com/256dpi/lwmqtt.git ./lwmqtt - mkdir -p ./src/lwmqtt - cp -r ./lwmqtt/src/*.c ./src/lwmqtt/ - cp -r ./lwmqtt/src/*.h ./src/lwmqtt/ - cp -r ./lwmqtt/include/*.h ./src/lwmqtt/ - rm -rf ./lwmqtt - sed -i '' "s//\"lwmqtt.h\"/g" ./src/lwmqtt/* diff --git a/lib/arduino-mqtt-2.4.0/README.md b/lib/arduino-mqtt-2.4.0/README.md deleted file mode 100644 index 92e61ed6e..000000000 --- a/lib/arduino-mqtt-2.4.0/README.md +++ /dev/null @@ -1,226 +0,0 @@ -# arduino-mqtt - -[![Build Status](https://travis-ci.org/256dpi/arduino-mqtt.svg?branch=master)](https://travis-ci.org/256dpi/arduino-mqtt) -[![GitHub release](https://img.shields.io/github/release/256dpi/arduino-mqtt.svg)](https://github.com/256dpi/arduino-mqtt/releases) - -This library bundles the [lwmqtt](https://github.com/256dpi/lwmqtt) MQTT 3.1.1 client and adds a thin wrapper to get an Arduino like API. - -Download the latest version from the [release](https://github.com/256dpi/arduino-mqtt/releases) section. Or even better use the builtin Library Manager in the Arduino IDE and search for "MQTT". - -The library is also available on [PlatformIO](https://platformio.org/lib/show/617/MQTT). You can install it by running: `pio lib install "MQTT"`. - -## Compatibility - -The following examples show how you can use the library with various Arduino compatible hardware: - -- [Arduino Yun & Yun-Shield](https://github.com/256dpi/arduino-mqtt/blob/master/examples/ArduinoYun/ArduinoYun.ino) ([Secure](https://github.com/256dpi/arduino-mqtt/blob/master/examples/ArduinoYunSecure/ArduinoYunSecure.ino)) -- [Arduino Ethernet Shield](https://github.com/256dpi/arduino-mqtt/blob/master/examples/ArduinoEthernetShield/ArduinoEthernetShield.ino) -- [Arduino WiFi Shield](https://github.com/256dpi/arduino-mqtt/blob/master/examples/ArduinoWiFiShield/ArduinoWiFiShield.ino) -- [Adafruit HUZZAH ESP8266](https://github.com/256dpi/arduino-mqtt/blob/master/examples/AdafruitHuzzahESP8266/AdafruitHuzzahESP8266.ino) ([Secure](https://github.com/256dpi/arduino-mqtt/blob/master/examples/AdafruitHuzzahESP8266Secure/AdafruitHuzzahESP8266Secure.ino)) -- [Arduino/Genuino WiFi101 Shield](https://github.com/256dpi/arduino-mqtt/blob/master/examples/ArduinoWiFi101/ArduinoWiFi101.ino) ([Secure](https://github.com/256dpi/arduino-mqtt/blob/master/examples/ArduinoWiFi101Secure/ArduinoWiFi101Secure.ino)) -- [Arduino MKR GSM 1400](https://github.com/256dpi/arduino-mqtt/blob/master/examples/ArduinoMKRGSM1400/ArduinoMKRGSM1400.ino) ([Secure](https://github.com/256dpi/arduino-mqtt/blob/master/examples/ArduinoMKRGSM1400Secure/ArduinoMKRGSM1400Secure.ino)) -- [ESP32 Development Board](https://github.com/256dpi/arduino-mqtt/blob/master/examples/ESP32DevelopmentBoard/ESP32DevelopmentBoard.ino) ([Secure](https://github.com/256dpi/arduino-mqtt/blob/master/examples/ESP32DevelopmentBoardSecure/ESP32DevelopmentBoardSecure.ino)) - -Other shields and boards should also work if they provide a [Client](https://www.arduino.cc/en/Reference/ClientConstructor) based network implementation. - -## Notes - -- The maximum size for packets being published and received is set by default to 128 bytes. To change the buffer sizes, you need to use `MQTTClient client(256)` instead of just `MQTTClient client` on the top of your sketch. The passed value denotes the read and write buffer size. - -- On the ESP8266 it has been reported that an additional `delay(10);` after `client.loop();` fixes many stability issues with WiFi connections. - -- To use the library with shiftr.io, you need to provide the token key (username) and token secret (password) as the second and third argument to `client.connect(name, key, secret)`. - -## Example - -The following example uses an Arduino MKR1000 to connect to shiftr.io. You can check on your device after a successful connection here: https://shiftr.io/try. - -```c++ -#include -#include -#include - -const char ssid[] = "ssid"; -const char pass[] = "pass"; - -WiFiClient net; -MQTTClient client; - -unsigned long lastMillis = 0; - -void connect() { - Serial.print("checking wifi..."); - while (WiFi.status() != WL_CONNECTED) { - Serial.print("."); - delay(1000); - } - - Serial.print("\nconnecting..."); - while (!client.connect("arduino", "try", "try")) { - Serial.print("."); - delay(1000); - } - - Serial.println("\nconnected!"); - - client.subscribe("/hello"); - // client.unsubscribe("/hello"); -} - -void messageReceived(String &topic, String &payload) { - Serial.println("incoming: " + topic + " - " + payload); -} - -void setup() { - Serial.begin(115200); - WiFi.begin(ssid, pass); - - // Note: Local domain names (e.g. "Computer.local" on OSX) are not supported by Arduino. - // You need to set the IP address directly. - client.begin("broker.shiftr.io", net); - client.onMessage(messageReceived); - - connect(); -} - -void loop() { - client.loop(); - - if (!client.connected()) { - connect(); - } - - // publish a message roughly every second. - if (millis() - lastMillis > 1000) { - lastMillis = millis(); - client.publish("/hello", "world"); - } -} -``` - -## API - -Initialize the object using the hostname of the broker, the brokers port (default: `1883`) and the underlying Client class for network transport: - -```c++ -void begin(const char hostname[], Client &client); -void begin(const char hostname[], int port, Client &client); -``` - -- Specify port `8883` when using secure clients for encrypted connections. -- Local domain names (e.g. `Computer.local` on OSX) are not supported by Arduino. You need to set the IP address directly. - -The hostname and port can also be changed after calling `begin()`: - -```c++ -void setHost(const char hostname[]); -void setHost(const char hostname[], int port); -``` - -Set a will message (last testament) that gets registered on the broker after connecting. `setWill()` has to be called before calling `connect()`: - -```c++ -void setWill(const char topic[]); -void setWill(const char topic[], const char payload[]); -void setWill(const char topic[], const char payload[], bool retained, int qos); -void clearWill(); -``` - -Register a callback to receive messages: - -```c++ -void onMessage(MQTTClientCallbackSimple); -// Callback signature: void messageReceived(String &topic, String &payload) {} - -void onMessageAdvanced(MQTTClientCallbackAdvanced); -// Callback signature: void messageReceived(MQTTClient *client, char topic[], char payload[], int payload_length) {} -``` - -- The set callback is mostly called during a call to `loop()` but may also be called during a call to `subscribe()`, `unsubscribe()` or `publish() // QoS > 0` if messages have been received before receiving the required acknowledgement. Therefore, it is strongly recommended to not call `subscribe()`, `unsubscribe()` or `publish() // QoS > 0` directly in the callback. - -Set more advanced options: - -```c++ -void setOptions(int keepAlive, bool cleanSession, int timeout); -``` - -- The `keepAlive` option controls the keep alive interval in seconds (default: 10). -- The `cleanSession` option controls the session retention on the broker side (default: true). -- The `timeout` option controls the default timeout for all commands in milliseconds (default: 1000). - -Connect to broker using the supplied client id and an optional username and password: - -```c++ -bool connect(const char clientId[], bool skip = false); -bool connect(const char clientId[], const char username[], bool skip = false); -bool connect(const char clientId[], const char username[], const char password[], bool skip = false); -``` - -- If the `skip` option is set to true, the client will skip the network level connection and jump to the MQTT level connection. This option can be used in order to establish and verify TLS connections manually before giving control to the MQTT client. -- This functions returns a boolean that indicates if the connection has been established successfully. - -Publishes a message to the broker with an optional payload: - -```c++ -bool publish(const String &topic); -bool publish(const char topic[]); -bool publish(const String &topic, const String &payload); -bool publish(const String &topic, const String &payload, bool retained, int qos); -bool publish(const char topic[], const String &payload); -bool publish(const char topic[], const String &payload, bool retained, int qos); -bool publish(const char topic[], const char payload[]); -bool publish(const char topic[], const char payload[], bool retained, int qos); -bool publish(const char topic[], const char payload[], int length); -bool publish(const char topic[], const char payload[], int length, bool retained, int qos); -``` - -Subscribe to a topic: - -```c++ -bool subscribe(const String &topic); -bool subscribe(const String &topic, int qos); -bool subscribe(const char topic[]); -bool subscribe(const char topic[], int qos); -``` - -Unsubscribe from a topic: - -```c++ -bool unsubscribe(const String &topic); -bool unsubscribe(const char topic[]); -``` - -Sends and receives packets: - -```c++ -bool loop(); -``` - -- This function should be called in every `loop`. - -Check if the client is currently connected: - -```c++ -bool connected(); -``` - -Access low-level information for debugging: - -```c++ -lwmqtt_err_t lastError(); -lwmqtt_return_code_t returnCode(); -``` - -- The error codes can be found [here](https://github.com/256dpi/lwmqtt/blob/master/include/lwmqtt.h#L11). -- The return codes can be found [here](https://github.com/256dpi/lwmqtt/blob/master/include/lwmqtt.h#L243). - -Disconnect from the broker: - -```c++ -bool disconnect(); -``` - -## Release Management - -- Update version in `library.properties`. -- Create release on GitHub. diff --git a/lib/arduino-mqtt-2.4.0/examples/AdafruitHuzzahESP8266/AdafruitHuzzahESP8266.ino b/lib/arduino-mqtt-2.4.0/examples/AdafruitHuzzahESP8266/AdafruitHuzzahESP8266.ino deleted file mode 100644 index ca5a22607..000000000 --- a/lib/arduino-mqtt-2.4.0/examples/AdafruitHuzzahESP8266/AdafruitHuzzahESP8266.ino +++ /dev/null @@ -1,69 +0,0 @@ -// This example uses an Adafruit Huzzah ESP8266 -// to connect to shiftr.io. -// -// You can check on your device after a successful -// connection here: https://shiftr.io/try. -// -// by Joël Gähwiler -// https://github.com/256dpi/arduino-mqtt - -#include -#include - -const char ssid[] = "ssid"; -const char pass[] = "pass"; - -WiFiClient net; -MQTTClient client; - -unsigned long lastMillis = 0; - -void connect() { - Serial.print("checking wifi..."); - while (WiFi.status() != WL_CONNECTED) { - Serial.print("."); - delay(1000); - } - - Serial.print("\nconnecting..."); - while (!client.connect("arduino", "try", "try")) { - Serial.print("."); - delay(1000); - } - - Serial.println("\nconnected!"); - - client.subscribe("/hello"); - // client.unsubscribe("/hello"); -} - -void messageReceived(String &topic, String &payload) { - Serial.println("incoming: " + topic + " - " + payload); -} - -void setup() { - Serial.begin(115200); - WiFi.begin(ssid, pass); - - // Note: Local domain names (e.g. "Computer.local" on OSX) are not supported by Arduino. - // You need to set the IP address directly. - client.begin("broker.shiftr.io", net); - client.onMessage(messageReceived); - - connect(); -} - -void loop() { - client.loop(); - delay(10); // <- fixes some issues with WiFi stability - - if (!client.connected()) { - connect(); - } - - // publish a message roughly every second. - if (millis() - lastMillis > 1000) { - lastMillis = millis(); - client.publish("/hello", "world"); - } -} diff --git a/lib/arduino-mqtt-2.4.0/examples/AdafruitHuzzahESP8266Secure/AdafruitHuzzahESP8266Secure.ino b/lib/arduino-mqtt-2.4.0/examples/AdafruitHuzzahESP8266Secure/AdafruitHuzzahESP8266Secure.ino deleted file mode 100644 index 1def5678d..000000000 --- a/lib/arduino-mqtt-2.4.0/examples/AdafruitHuzzahESP8266Secure/AdafruitHuzzahESP8266Secure.ino +++ /dev/null @@ -1,71 +0,0 @@ -// This example uses an Adafruit Huzzah ESP8266 -// to connect to shiftr.io. -// -// You can check on your device after a successful -// connection here: https://shiftr.io/try. -// -// by Joël Gähwiler -// https://github.com/256dpi/arduino-mqtt - -#include -#include - -const char ssid[] = "ssid"; -const char pass[] = "pass"; - -WiFiClientSecure net; -MQTTClient client; - -unsigned long lastMillis = 0; - -void connect() { - Serial.print("checking wifi..."); - while (WiFi.status() != WL_CONNECTED) { - Serial.print("."); - delay(1000); - } - - Serial.print("\nconnecting..."); - while (!client.connect("arduino", "try", "try")) { - Serial.print("."); - delay(1000); - } - - Serial.println("\nconnected!"); - - client.subscribe("/hello"); - // client.unsubscribe("/hello"); -} - -void messageReceived(String &topic, String &payload) { - Serial.println("incoming: " + topic + " - " + payload); -} - -void setup() { - Serial.begin(115200); - WiFi.begin(ssid, pass); - - // Note: Local domain names (e.g. "Computer.local" on OSX) are not supported by Arduino. - // You need to set the IP address directly. - // - // MQTT brokers usually use port 8883 for secure connections. - client.begin("broker.shiftr.io", 8883, net); - client.onMessage(messageReceived); - - connect(); -} - -void loop() { - client.loop(); - delay(10); // <- fixes some issues with WiFi stability - - if (!client.connected()) { - connect(); - } - - // publish a message roughly every second. - if (millis() - lastMillis > 1000) { - lastMillis = millis(); - client.publish("/hello", "world"); - } -} diff --git a/lib/arduino-mqtt-2.4.0/examples/ArduinoEthernetShield/ArduinoEthernetShield.ino b/lib/arduino-mqtt-2.4.0/examples/ArduinoEthernetShield/ArduinoEthernetShield.ino deleted file mode 100644 index 8386c7788..000000000 --- a/lib/arduino-mqtt-2.4.0/examples/ArduinoEthernetShield/ArduinoEthernetShield.ino +++ /dev/null @@ -1,62 +0,0 @@ -// This example uses an Arduino Uno together with -// an Ethernet Shield to connect to shiftr.io. -// -// You can check on your device after a successful -// connection here: https://shiftr.io/try. -// -// by Joël Gähwiler -// https://github.com/256dpi/arduino-mqtt - -#include -#include - -byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; -byte ip[] = {192, 168, 1, 177}; // <- change to match your network - -EthernetClient net; -MQTTClient client; - -unsigned long lastMillis = 0; - -void connect() { - Serial.print("connecting..."); - while (!client.connect("arduino", "try", "try")) { - Serial.print("."); - delay(1000); - } - - Serial.println("\nconnected!"); - - client.subscribe("/hello"); - // client.unsubscribe("/hello"); -} - -void messageReceived(String &topic, String &payload) { - Serial.println("incoming: " + topic + " - " + payload); -} - -void setup() { - Serial.begin(115200); - Ethernet.begin(mac, ip); - - // Note: Local domain names (e.g. "Computer.local" on OSX) are not supported by Arduino. - // You need to set the IP address directly. - client.begin("broker.shiftr.io", net); - client.onMessage(messageReceived); - - connect(); -} - -void loop() { - client.loop(); - - if (!client.connected()) { - connect(); - } - - // publish a message roughly every second. - if (millis() - lastMillis > 1000) { - lastMillis = millis(); - client.publish("/hello", "world"); - } -} diff --git a/lib/arduino-mqtt-2.4.0/examples/ArduinoMKRGSM1400/ArduinoMKRGSM1400.ino b/lib/arduino-mqtt-2.4.0/examples/ArduinoMKRGSM1400/ArduinoMKRGSM1400.ino deleted file mode 100644 index dd56d5e21..000000000 --- a/lib/arduino-mqtt-2.4.0/examples/ArduinoMKRGSM1400/ArduinoMKRGSM1400.ino +++ /dev/null @@ -1,84 +0,0 @@ -// This example uses an Arduino MKR GSM 1400 board -// to connect to shiftr.io. -// -// IMPORTANT: This example uses the new MKRGSM library. -// -// You can check on your device after a successful -// connection here: https://shiftr.io/try. -// -// by Sandeep Mistry -// https://github.com/256dpi/arduino-mqtt - -#include -#include - -const char pin[] = ""; -const char apn[] = "apn"; -const char login[] = "login"; -const char password[] = "password"; - -GSMClient net; -GPRS gprs; -GSM gsmAccess; -MQTTClient client; - -unsigned long lastMillis = 0; - -void connect() { - // connection state - bool connected = false; - - Serial.print("connecting to cellular network ..."); - - // After starting the modem with gsmAccess.begin() - // attach to the GPRS network with the APN, login and password - while (!connected) { - if ((gsmAccess.begin(pin) == GSM_READY) && - (gprs.attachGPRS(apn, login, password) == GPRS_READY)) { - connected = true; - } else { - Serial.print("."); - delay(1000); - } - } - - Serial.print("\nconnecting..."); - while (!client.connect("arduino", "try", "try")) { - Serial.print("."); - delay(1000); - } - - Serial.println("\nconnected!"); - - client.subscribe("/hello"); - // client.unsubscribe("/hello"); -} - -void messageReceived(String &topic, String &payload) { - Serial.println("incoming: " + topic + " - " + payload); -} - -void setup() { - Serial.begin(115200); - - // Note: Local domain names (e.g. "Computer.local" on OSX) are not supported by Arduino. - // You need to set the IP address directly. - client.begin("broker.shiftr.io", net); - client.onMessage(messageReceived); - - connect(); -} - -void loop() { - client.loop(); - - if (!client.connected()) { - connect(); - } - - // publish a message roughly every second. - if (millis() - lastMillis > 1000) { - lastMillis = millis(); - client.publish("/hello", "world"); - } -} diff --git a/lib/arduino-mqtt-2.4.0/examples/ArduinoMKRGSM1400Secure/ArduinoMKRGSM1400Secure.ino b/lib/arduino-mqtt-2.4.0/examples/ArduinoMKRGSM1400Secure/ArduinoMKRGSM1400Secure.ino deleted file mode 100644 index 1b172ab6e..000000000 --- a/lib/arduino-mqtt-2.4.0/examples/ArduinoMKRGSM1400Secure/ArduinoMKRGSM1400Secure.ino +++ /dev/null @@ -1,86 +0,0 @@ -// This example uses an Arduino MKR GSM 1400 board -// to securely connect to shiftr.io. -// -// IMPORTANT: This example uses the new MKRGSM library. -// -// You can check on your device after a successful -// connection here: https://shiftr.io/try. -// -// by Sandeep Mistry -// https://github.com/256dpi/arduino-mqtt - -#include -#include - -const char pin[] = ""; -const char apn[] = "apn"; -const char login[] = "login"; -const char password[] = "password"; - -GSMSSLClient net; -GPRS gprs; -GSM gsmAccess; -MQTTClient client; - -unsigned long lastMillis = 0; - -void connect() { - // connection state - bool connected = false; - - Serial.print("connecting to cellular network ..."); - - // After starting the modem with gsmAccess.begin() - // attach to the GPRS network with the APN, login and password - while (!connected) { - if ((gsmAccess.begin(pin) == GSM_READY) && - (gprs.attachGPRS(apn, login, password) == GPRS_READY)) { - connected = true; - } else { - Serial.print("."); - delay(1000); - } - } - - Serial.print("\nconnecting..."); - while (!client.connect("arduino", "try", "try")) { - Serial.print("."); - delay(1000); - } - - Serial.println("\nconnected!"); - - client.subscribe("/hello"); - // client.unsubscribe("/hello"); -} - -void messageReceived(String &topic, String &payload) { - Serial.println("incoming: " + topic + " - " + payload); -} - -void setup() { - Serial.begin(115200); - - // Note: Local domain names (e.g. "Computer.local" on OSX) are not supported by Arduino. - // You need to set the IP address directly. - // - // MQTT brokers usually use port 8883 for secure connections. - client.begin("broker.shiftr.io", 8883, net); - client.onMessage(messageReceived); - - connect(); -} - -void loop() { - client.loop(); - - if (!client.connected()) { - connect(); - } - - // publish a message roughly every second. - if (millis() - lastMillis > 1000) { - lastMillis = millis(); - client.publish("/hello", "world"); - } -} diff --git a/lib/arduino-mqtt-2.4.0/examples/ArduinoWiFi101/ArduinoWiFi101.ino b/lib/arduino-mqtt-2.4.0/examples/ArduinoWiFi101/ArduinoWiFi101.ino deleted file mode 100644 index a36bd65aa..000000000 --- a/lib/arduino-mqtt-2.4.0/examples/ArduinoWiFi101/ArduinoWiFi101.ino +++ /dev/null @@ -1,70 +0,0 @@ -// This example uses an Arduino/Genuino Zero together with -// a WiFi101 Shield or a MKR1000 to connect to shiftr.io. -// -// IMPORTANT: This example uses the new WiFi101 library. -// -// You can check on your device after a successful -// connection here: https://shiftr.io/try. -// -// by Gilberto Conti -// https://github.com/256dpi/arduino-mqtt - -#include -#include - -const char ssid[] = "ssid"; -const char pass[] = "pass"; - -WiFiClient net; -MQTTClient client; - -unsigned long lastMillis = 0; - -void connect() { - Serial.print("checking wifi..."); - while (WiFi.status() != WL_CONNECTED) { - Serial.print("."); - delay(1000); - } - - Serial.print("\nconnecting..."); - while (!client.connect("arduino", "try", "try")) { - Serial.print("."); - delay(1000); - } - - Serial.println("\nconnected!"); - - client.subscribe("/hello"); - // client.unsubscribe("/hello"); -} - -void messageReceived(String &topic, String &payload) { - Serial.println("incoming: " + topic + " - " + payload); -} - -void setup() { - Serial.begin(115200); - WiFi.begin(ssid, pass); - - // Note: Local domain names (e.g. "Computer.local" on OSX) are not supported by Arduino. - // You need to set the IP address directly. - client.begin("broker.shiftr.io", net); - client.onMessage(messageReceived); - - connect(); -} - -void loop() { - client.loop(); - - if (!client.connected()) { - connect(); - } - - // publish a message roughly every second. - if (millis() - lastMillis > 1000) { - lastMillis = millis(); - client.publish("/hello", "world"); - } -} diff --git a/lib/arduino-mqtt-2.4.0/examples/ArduinoWiFi101Secure/ArduinoWiFi101Secure.ino b/lib/arduino-mqtt-2.4.0/examples/ArduinoWiFi101Secure/ArduinoWiFi101Secure.ino deleted file mode 100644 index c21e7ae99..000000000 --- a/lib/arduino-mqtt-2.4.0/examples/ArduinoWiFi101Secure/ArduinoWiFi101Secure.ino +++ /dev/null @@ -1,75 +0,0 @@ -// This example uses an Arduino/Genuino Zero together with -// a WiFi101 Shield or a MKR1000 to connect to shiftr.io. -// -// IMPORTANT: This example uses the new WiFi101 library. -// -// IMPORTANT: You need to install/update the SSL certificates first: -// https://github.com/arduino-libraries/WiFi101-FirmwareUpdater#to-update-ssl-certificates -// -// You can check on your device after a successful -// connection here: https://shiftr.io/try. -// -// by Gilberto Conti -// https://github.com/256dpi/arduino-mqtt - -#include -#include - -const char ssid[] = "ssid"; -const char pass[] = "pass"; - -WiFiSSLClient net; -MQTTClient client; - -unsigned long lastMillis = 0; - -void connect() { - Serial.print("checking wifi..."); - while (WiFi.status() != WL_CONNECTED) { - Serial.print("."); - delay(1000); - } - - Serial.print("\nconnecting..."); - while (!client.connect("arduino", "try", "try")) { - Serial.print("."); - delay(1000); - } - - Serial.println("\nconnected!"); - - client.subscribe("/hello"); - // client.unsubscribe("/hello"); -} - -void messageReceived(String &topic, String &payload) { - Serial.println("incoming: " + topic + " - " + payload); -} - -void setup() { - Serial.begin(115200); - WiFi.begin(ssid, pass); - - // Note: Local domain names (e.g. "Computer.local" on OSX) are not supported by Arduino. - // You need to set the IP address directly. - // - // MQTT brokers usually use port 8883 for secure connections. - client.begin("broker.shiftr.io", 8883, net); - client.onMessage(messageReceived); - - connect(); -} - -void loop() { - client.loop(); - - if (!client.connected()) { - connect(); - } - - // publish a message roughly every second. - if (millis() - lastMillis > 1000) { - lastMillis = millis(); - client.publish("/hello", "world"); - } -} diff --git a/lib/arduino-mqtt-2.4.0/examples/ArduinoWiFiShield/ArduinoWiFiShield.ino b/lib/arduino-mqtt-2.4.0/examples/ArduinoWiFiShield/ArduinoWiFiShield.ino deleted file mode 100644 index 4aff769f4..000000000 --- a/lib/arduino-mqtt-2.4.0/examples/ArduinoWiFiShield/ArduinoWiFiShield.ino +++ /dev/null @@ -1,68 +0,0 @@ -// This example uses an Arduino Uno together with -// a WiFi Shield to connect to shiftr.io. -// -// You can check on your device after a successful -// connection here: https://shiftr.io/try. -// -// by Joël Gähwiler -// https://github.com/256dpi/arduino-mqtt - -#include -#include - -const char ssid[] = "ssid"; -const char pass[] = "pass"; - -WiFiClient net; -MQTTClient client; - -unsigned long lastMillis = 0; - -void connect() { - Serial.print("checking wifi..."); - while (WiFi.status() != WL_CONNECTED) { - Serial.print("."); - delay(1000); - } - - Serial.print("\nconnecting..."); - while (!client.connect("arduino", "try", "try")) { - Serial.print("."); - delay(1000); - } - - Serial.println("\nconnected!"); - - client.subscribe("/hello"); - // client.unsubscribe("/hello"); -} - -void messageReceived(String &topic, String &payload) { - Serial.println("incoming: " + topic + " - " + payload); -} - -void setup() { - Serial.begin(115200); - WiFi.begin(ssid, pass); - - // Note: Local domain names (e.g. "Computer.local" on OSX) are not supported by Arduino. - // You need to set the IP address directly. - client.begin("broker.shiftr.io", net); - client.onMessage(messageReceived); - - connect(); -} - -void loop() { - client.loop(); - - if (!client.connected()) { - connect(); - } - - // publish a message roughly every second. - if (millis() - lastMillis > 1000) { - lastMillis = millis(); - client.publish("/hello", "world"); - } -} diff --git a/lib/arduino-mqtt-2.4.0/examples/ArduinoYun/ArduinoYun.ino b/lib/arduino-mqtt-2.4.0/examples/ArduinoYun/ArduinoYun.ino deleted file mode 100644 index 823bdff36..000000000 --- a/lib/arduino-mqtt-2.4.0/examples/ArduinoYun/ArduinoYun.ino +++ /dev/null @@ -1,60 +0,0 @@ -// This example uses an Arduino Yun or a Yun-Shield -// and the MQTTClient to connect to shiftr.io. -// -// You can check on your device after a successful -// connection here: https://shiftr.io/try. -// -// by Joël Gähwiler -// https://github.com/256dpi/arduino-mqtt - -#include -#include -#include - -BridgeClient net; -MQTTClient client; - -unsigned long lastMillis = 0; - -void connect() { - Serial.print("connecting..."); - while (!client.connect("arduino", "try", "try")) { - Serial.print("."); - delay(1000); - } - - Serial.println("\nconnected!"); - - client.subscribe("/hello"); - // client.unsubscribe("/hello"); -} - -void messageReceived(String &topic, String &payload) { - Serial.println("incoming: " + topic + " - " + payload); -} - -void setup() { - Bridge.begin(); - Serial.begin(115200); - - // Note: Local domain names (e.g. "Computer.local" on OSX) are not supported by Arduino. - // You need to set the IP address directly. - client.begin("broker.shiftr.io", net); - client.onMessage(messageReceived); - - connect(); -} - -void loop() { - client.loop(); - - if (!client.connected()) { - connect(); - } - - // publish a message roughly every second. - if (millis() - lastMillis > 1000) { - lastMillis = millis(); - client.publish("/hello", "world"); - } -} diff --git a/lib/arduino-mqtt-2.4.0/examples/ArduinoYunSecure/ArduinoYunSecure.ino b/lib/arduino-mqtt-2.4.0/examples/ArduinoYunSecure/ArduinoYunSecure.ino deleted file mode 100644 index 46c068ab2..000000000 --- a/lib/arduino-mqtt-2.4.0/examples/ArduinoYunSecure/ArduinoYunSecure.ino +++ /dev/null @@ -1,62 +0,0 @@ -// This example uses an Arduino Yun or a Yun-Shield -// and the MQTTClient to connect to shiftr.io. -// -// You can check on your device after a successful -// connection here: https://shiftr.io/try. -// -// by Joël Gähwiler -// https://github.com/256dpi/arduino-mqtt - -#include -#include -#include - -BridgeSSLClient net; -MQTTClient client; - -unsigned long lastMillis = 0; - -void connect() { - Serial.print("connecting..."); - while (!client.connect("arduino", "try", "try")) { - Serial.print("."); - delay(1000); - } - - Serial.println("\nconnected!"); - - client.subscribe("/hello"); - // client.unsubscribe("/hello"); -} - -void messageReceived(String &topic, String &payload) { - Serial.println("incoming: " + topic + " - " + payload); -} - -void setup() { - Bridge.begin(); - Serial.begin(115200); - - // Note: Local domain names (e.g. "Computer.local" on OSX) are not supported by Arduino. - // You need to set the IP address directly. - // - // MQTT brokers usually use port 8883 for secure connections. - client.begin("broker.shiftr.io", 8883, net); - client.onMessage(messageReceived); - - connect(); -} - -void loop() { - client.loop(); - - if (!client.connected()) { - connect(); - } - - // publish a message roughly every second. - if (millis() - lastMillis > 1000) { - lastMillis = millis(); - client.publish("/hello", "world"); - } -} diff --git a/lib/arduino-mqtt-2.4.0/examples/ESP32DevelopmentBoard/ESP32DevelopmentBoard.ino b/lib/arduino-mqtt-2.4.0/examples/ESP32DevelopmentBoard/ESP32DevelopmentBoard.ino deleted file mode 100644 index c6919280d..000000000 --- a/lib/arduino-mqtt-2.4.0/examples/ESP32DevelopmentBoard/ESP32DevelopmentBoard.ino +++ /dev/null @@ -1,69 +0,0 @@ -// This example uses an ESP32 Development Board -// to connect to shiftr.io. -// -// You can check on your device after a successful -// connection here: https://shiftr.io/try. -// -// by Joël Gähwiler -// https://github.com/256dpi/arduino-mqtt - -#include -#include - -const char ssid[] = "ssid"; -const char pass[] = "pass"; - -WiFiClient net; -MQTTClient client; - -unsigned long lastMillis = 0; - -void connect() { - Serial.print("checking wifi..."); - while (WiFi.status() != WL_CONNECTED) { - Serial.print("."); - delay(1000); - } - - Serial.print("\nconnecting..."); - while (!client.connect("arduino", "try", "try")) { - Serial.print("."); - delay(1000); - } - - Serial.println("\nconnected!"); - - client.subscribe("/hello"); - // client.unsubscribe("/hello"); -} - -void messageReceived(String &topic, String &payload) { - Serial.println("incoming: " + topic + " - " + payload); -} - -void setup() { - Serial.begin(115200); - WiFi.begin(ssid, pass); - - // Note: Local domain names (e.g. "Computer.local" on OSX) are not supported by Arduino. - // You need to set the IP address directly. - client.begin("broker.shiftr.io", net); - client.onMessage(messageReceived); - - connect(); -} - -void loop() { - client.loop(); - delay(10); // <- fixes some issues with WiFi stability - - if (!client.connected()) { - connect(); - } - - // publish a message roughly every second. - if (millis() - lastMillis > 1000) { - lastMillis = millis(); - client.publish("/hello", "world"); - } -} diff --git a/lib/arduino-mqtt-2.4.0/examples/ESP32DevelopmentBoardSecure/ESP32DevelopmentBoardSecure.ino b/lib/arduino-mqtt-2.4.0/examples/ESP32DevelopmentBoardSecure/ESP32DevelopmentBoardSecure.ino deleted file mode 100644 index cff75379e..000000000 --- a/lib/arduino-mqtt-2.4.0/examples/ESP32DevelopmentBoardSecure/ESP32DevelopmentBoardSecure.ino +++ /dev/null @@ -1,71 +0,0 @@ -// This example uses an ESP32 Development Board -// to connect to shiftr.io. -// -// You can check on your device after a successful -// connection here: https://shiftr.io/try. -// -// by Joël Gähwiler -// https://github.com/256dpi/arduino-mqtt - -#include -#include - -const char ssid[] = "ssid"; -const char pass[] = "pass"; - -WiFiClientSecure net; -MQTTClient client; - -unsigned long lastMillis = 0; - -void connect() { - Serial.print("checking wifi..."); - while (WiFi.status() != WL_CONNECTED) { - Serial.print("."); - delay(1000); - } - - Serial.print("\nconnecting..."); - while (!client.connect("arduino", "try", "try")) { - Serial.print("."); - delay(1000); - } - - Serial.println("\nconnected!"); - - client.subscribe("/hello"); - // client.unsubscribe("/hello"); -} - -void messageReceived(String &topic, String &payload) { - Serial.println("incoming: " + topic + " - " + payload); -} - -void setup() { - Serial.begin(115200); - WiFi.begin(ssid, pass); - - // Note: Local domain names (e.g. "Computer.local" on OSX) are not supported by Arduino. - // You need to set the IP address directly. - // - // MQTT brokers usually use port 8883 for secure connections. - client.begin("broker.shiftr.io", 8883, net); - client.onMessage(messageReceived); - - connect(); -} - -void loop() { - client.loop(); - delay(10); // <- fixes some issues with WiFi stability - - if (!client.connected()) { - connect(); - } - - // publish a message roughly every second. - if (millis() - lastMillis > 1000) { - lastMillis = millis(); - client.publish("/hello", "world"); - } -} diff --git a/lib/arduino-mqtt-2.4.0/library.properties b/lib/arduino-mqtt-2.4.0/library.properties deleted file mode 100644 index 0cf498e9b..000000000 --- a/lib/arduino-mqtt-2.4.0/library.properties +++ /dev/null @@ -1,9 +0,0 @@ -name=MQTT -version=2.4.0 -author=Joel Gaehwiler -maintainer=Joel Gaehwiler -sentence=MQTT library for Arduino -paragraph=This library bundles the lwmqtt client and adds a thin wrapper to get an Arduino like API. -category=Communication -url=https://github.com/256dpi/arduino-mqtt -architectures=* diff --git a/lib/arduino-mqtt-2.4.0/src/MQTT.h b/lib/arduino-mqtt-2.4.0/src/MQTT.h deleted file mode 100644 index 35652c45f..000000000 --- a/lib/arduino-mqtt-2.4.0/src/MQTT.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef MQTT_H -#define MQTT_H - -#include "MQTTClient.h" - -#endif diff --git a/lib/arduino-mqtt-2.4.0/src/MQTTClient.h b/lib/arduino-mqtt-2.4.0/src/MQTTClient.h deleted file mode 100644 index d87f62cd5..000000000 --- a/lib/arduino-mqtt-2.4.0/src/MQTTClient.h +++ /dev/null @@ -1,491 +0,0 @@ -#ifndef MQTT_CLIENT_H -#define MQTT_CLIENT_H - -#include -#include -#include - -extern "C" { -#include "lwmqtt/lwmqtt.h" -}; - -typedef struct { - uint32_t end; -} lwmqtt_arduino_timer_t; - -void lwmqtt_arduino_timer_set(void *ref, uint32_t timeout); - -int32_t lwmqtt_arduino_timer_get(void *ref); - -typedef struct { - Client *client; -} lwmqtt_arduino_network_t; - -void lwmqtt_arduino_timer_set(void *ref, uint32_t timeout) { - // cast timer reference - auto t = (lwmqtt_arduino_timer_t *)ref; - - // set future end time - t->end = (uint32_t)(millis() + timeout); -} - -int32_t lwmqtt_arduino_timer_get(void *ref) { - // cast timer reference - auto t = (lwmqtt_arduino_timer_t *)ref; - - // get difference to end time - return (int32_t)t->end - (int32_t)millis(); -} - -lwmqtt_err_t lwmqtt_arduino_network_read(void *ref, uint8_t *buffer, size_t len, size_t *read, uint32_t timeout) { - // cast network reference - auto n = (lwmqtt_arduino_network_t *)ref; - - // set timeout - n->client->setTimeout(timeout); - - // read bytes - *read = n->client->readBytes(buffer, len); - if (*read <= 0) { - return LWMQTT_NETWORK_FAILED_READ; - } - - return LWMQTT_SUCCESS; -} - -lwmqtt_err_t lwmqtt_arduino_network_write(void *ref, uint8_t *buffer, size_t len, size_t *sent, uint32_t /*timeout*/) { - // cast network reference - auto n = (lwmqtt_arduino_network_t *)ref; - - // write bytes - *sent = n->client->write(buffer, len); - if (*sent <= 0) { - return LWMQTT_NETWORK_FAILED_WRITE; - }; - - return LWMQTT_SUCCESS; -} - -class MQTTClient; - -typedef void (*MQTTClientCallbackSimple)(String &topic, String &payload); -typedef void (*MQTTClientCallbackAdvanced)(MQTTClient *client, char topic[], char bytes[], int length); - -typedef struct { - MQTTClient *client = nullptr; - MQTTClientCallbackSimple simple = nullptr; - MQTTClientCallbackAdvanced advanced = nullptr; -} MQTTClientCallback; - -static void MQTTClientHandler(lwmqtt_client_t * /*client*/, void *ref, lwmqtt_string_t topic, - lwmqtt_message_t message) { - // get callback - auto cb = (MQTTClientCallback *)ref; - - // null terminate topic - char terminated_topic[topic.len + 1]; - memcpy(terminated_topic, topic.data, topic.len); - terminated_topic[topic.len] = '\0'; - - // null terminate payload if available - if (message.payload != nullptr) { - message.payload[message.payload_len] = '\0'; - } - - // call the advanced callback and return if available - if (cb->advanced != nullptr) { - cb->advanced(cb->client, terminated_topic, (char *)message.payload, (int)message.payload_len); - return; - } - - // return if simple callback is not set - if (cb->simple == nullptr) { - return; - } - - // create topic string - String str_topic = String(terminated_topic); - - // create payload string - String str_payload; - if (message.payload != nullptr) { - str_payload = String((const char *)message.payload); - } - - // call simple callback - cb->simple(str_topic, str_payload); -} - -class MQTTClient { - private: - size_t bufSize = 0; - uint8_t *readBuf = nullptr; - uint8_t *writeBuf = nullptr; - - uint16_t keepAlive = 10; - bool cleanSession = true; - uint32_t timeout = 1000; - - Client *netClient = nullptr; - const char *hostname = nullptr; - int port = 0; - lwmqtt_will_t *will = nullptr; - MQTTClientCallback callback; - - lwmqtt_arduino_network_t network = {nullptr}; - lwmqtt_arduino_timer_t timer1 = {0}; - lwmqtt_arduino_timer_t timer2 = {0}; - lwmqtt_client_t client = {0}; - - bool _connected = false; - lwmqtt_return_code_t _returnCode = (lwmqtt_return_code_t)0; - lwmqtt_err_t _lastError = (lwmqtt_err_t)0; - - public: - explicit MQTTClient(int bufSize = 128) { - // reset client - memset(&this->client, 0, sizeof(lwmqtt_client_t)); - - // allocate buffers - this->bufSize = (size_t)bufSize; - this->readBuf = (uint8_t *)malloc((size_t)bufSize + 1); - this->writeBuf = (uint8_t *)malloc((size_t)bufSize); - } - - ~MQTTClient() { - // free will - this->clearWill(); - - // free hostname - if (this->hostname != nullptr) { - free((void *)this->hostname); - } - - // free buffers - free(this->readBuf); - free(this->writeBuf); - } - - void begin(const char hostname[], Client &client) { this->begin(hostname, 1883, client); } - - void begin(const char hostname[], int port, Client &client) { - // set hostname and port - this->setHost(hostname, port); - - // set client - this->netClient = &client; - - // initialize client - lwmqtt_init(&this->client, this->writeBuf, this->bufSize, this->readBuf, this->bufSize); - - // set timers - lwmqtt_set_timers(&this->client, &this->timer1, &this->timer2, lwmqtt_arduino_timer_set, lwmqtt_arduino_timer_get); - - // set network - lwmqtt_set_network(&this->client, &this->network, lwmqtt_arduino_network_read, lwmqtt_arduino_network_write); - - // set callback - lwmqtt_set_callback(&this->client, (void *)&this->callback, MQTTClientHandler); - } - - void onMessage(MQTTClientCallbackSimple cb) { - // set callback - this->callback.client = this; - this->callback.simple = cb; - this->callback.advanced = nullptr; - } - - void onMessageAdvanced(MQTTClientCallbackAdvanced cb) { - // set callback - this->callback.client = this; - this->callback.simple = nullptr; - this->callback.advanced = cb; - } - - void setHost(const char hostname[]) { this->setHost(hostname, 1883); } - - void setHost(const char hostname[], int port) { - // free hostname if set - if (this->hostname != nullptr) { - free((void *)this->hostname); - } - - // set hostname and port - this->hostname = strdup(hostname); - this->port = port; - } - - void setWill(const char topic[]) { this->setWill(topic, ""); } - - void setWill(const char topic[], const char payload[]) { this->setWill(topic, payload, false, 0); } - - void setWill(const char topic[], const char payload[], bool retained, int qos) { - // return if topic is missing - if (topic == nullptr || strlen(topic) == 0) { - return; - } - - // clear existing will - this->clearWill(); - - // allocate will - this->will = (lwmqtt_will_t *)malloc(sizeof(lwmqtt_will_t)); - memset(this->will, 0, sizeof(lwmqtt_will_t)); - - // set topic - this->will->topic = lwmqtt_string(strdup(topic)); - - // set payload if available - if (payload != nullptr && strlen(payload) > 0) { - this->will->payload = lwmqtt_string(strdup(payload)); - } - - // set flags - this->will->retained = retained; - this->will->qos = (lwmqtt_qos_t)qos; - } - - void clearWill() { - // return if not set - if (this->will == nullptr) { - return; - } - - // free payload if set - if (this->will->payload.len > 0) { - free(this->will->payload.data); - } - - // free topic if set - if (this->will->topic.len > 0) { - free(this->will->topic.data); - } - - // free will - free(this->will); - this->will = nullptr; - } - - void setOptions(int keepAlive, bool cleanSession, int timeout) { - // set new options - this->keepAlive = (uint16_t)keepAlive; - this->cleanSession = cleanSession; - this->timeout = (uint32_t)timeout; - } - - bool connect(const char clientId[], bool skip = false) { return this->connect(clientId, nullptr, nullptr); } - - bool connect(const char clientId[], const char username[], bool skip = false) { return this->connect(clientId, username, nullptr); } - - bool connect(const char clientId[], const char username[], const char password[], bool skip = false) { - // close left open connection if still connected - if (!skip && this->connected()) { - this->close(); - } - - // save client - this->network.client = this->netClient; - - // connect to hostg - if(!skip) { - int ret = this->netClient->connect(this->hostname, (uint16_t)this->port); - if (ret <= 0) { - return false; - } - } - - // prepare options - lwmqtt_options_t options = lwmqtt_default_options; - options.keep_alive = this->keepAlive; - options.clean_session = this->cleanSession; - options.client_id = lwmqtt_string(clientId); - - // set username and password if available - if (username != nullptr) { - options.username = lwmqtt_string(username); - - if (password != nullptr) { - options.password = lwmqtt_string(password); - } - } - - // connect to broker - this->_lastError = lwmqtt_connect(&this->client, options, this->will, &this->_returnCode, this->timeout); - if (this->_lastError != LWMQTT_SUCCESS) { - // close connection - this->close(); - - return false; - } - - // set flag - this->_connected = true; - - return true; - } - - bool publish(const String &topic) { return this->publish(topic.c_str(), ""); } - - bool publish(const char topic[]) { return this->publish(topic, ""); } - - bool publish(const String &topic, const String &payload) { return this->publish(topic.c_str(), payload.c_str()); } - - bool publish(const String &topic, const String &payload, bool retained, int qos) { - return this->publish(topic.c_str(), payload.c_str(), retained, qos); - } - - bool publish(const char topic[], const String &payload) { return this->publish(topic, payload.c_str()); } - - bool publish(const char topic[], const String &payload, bool retained, int qos) { - return this->publish(topic, payload.c_str(), retained, qos); - } - - bool publish(const char topic[], const char payload[]) { - return this->publish(topic, (char *)payload, (int)strlen(payload)); - } - - bool publish(const char topic[], const char payload[], bool retained, int qos) { - return this->publish(topic, (char *)payload, (int)strlen(payload), retained, qos); - } - - bool publish(const char topic[], const char payload[], int length) { - return this->publish(topic, payload, length, false, 0); - } - - bool publish(const char topic[], const char payload[], int length, bool retained, int qos) { - // return immediately if not connected - if (!this->connected()) { - return false; - } - - // prepare message - lwmqtt_message_t message = lwmqtt_default_message; - message.payload = (uint8_t *)payload; - message.payload_len = (size_t)length; - message.retained = retained; - message.qos = lwmqtt_qos_t(qos); - - // publish message - this->_lastError = lwmqtt_publish(&this->client, lwmqtt_string(topic), message, this->timeout); - if (this->_lastError != LWMQTT_SUCCESS) { - // close connection - this->close(); - - return false; - } - - return true; - } - - bool subscribe(const String &topic) { return this->subscribe(topic.c_str()); } - - bool subscribe(const String &topic, int qos) { return this->subscribe(topic.c_str(), qos); } - - bool subscribe(const char topic[]) { return this->subscribe(topic, 0); } - - bool subscribe(const char topic[], int qos) { - // return immediately if not connected - if (!this->connected()) { - return false; - } - - // subscribe to topic - this->_lastError = lwmqtt_subscribe_one(&this->client, lwmqtt_string(topic), (lwmqtt_qos_t)qos, this->timeout); - if (this->_lastError != LWMQTT_SUCCESS) { - // close connection - this->close(); - - return false; - } - - return true; - } - - bool unsubscribe(const String &topic) { return this->unsubscribe(topic.c_str()); } - - bool unsubscribe(const char topic[]) { - // return immediately if not connected - if (!this->connected()) { - return false; - } - - // unsubscribe from topic - this->_lastError = lwmqtt_unsubscribe_one(&this->client, lwmqtt_string(topic), this->timeout); - if (this->_lastError != LWMQTT_SUCCESS) { - // close connection - this->close(); - - return false; - } - - return true; - } - - bool loop() { - // return immediately if not connected - if (!this->connected()) { - return false; - } - - // get available bytes on the network - auto available = (size_t)this->netClient->available(); - - // yield if data is available - if (available > 0) { - this->_lastError = lwmqtt_yield(&this->client, available, this->timeout); - if (this->_lastError != LWMQTT_SUCCESS) { - // close connection - this->close(); - - return false; - } - } - - // keep the connection alive - this->_lastError = lwmqtt_keep_alive(&this->client, this->timeout); - if (this->_lastError != LWMQTT_SUCCESS) { - // close connection - this->close(); - - return false; - } - - return true; - } - - bool connected() { - // a client is connected if the network is connected, a client is available and - // the connection has been properly initiated - return this->netClient != nullptr && this->netClient->connected() == 1 && this->_connected; - } - - lwmqtt_err_t lastError() { return this->_lastError; } - - lwmqtt_return_code_t returnCode() { return this->_returnCode; } - - bool disconnect() { - // return immediately if not connected anymore - if (!this->connected()) { - return false; - } - - // cleanly disconnect - this->_lastError = lwmqtt_disconnect(&this->client, this->timeout); - - // close - this->close(); - - return this->_lastError == LWMQTT_SUCCESS; - } - - private: - void close() { - // set flag - this->_connected = false; - - // close network - this->netClient->stop(); - } -}; - -#endif diff --git a/lib/arduino-mqtt-2.4.0/src/lwmqtt/client.c b/lib/arduino-mqtt-2.4.0/src/lwmqtt/client.c deleted file mode 100644 index b1772b9f7..000000000 --- a/lib/arduino-mqtt-2.4.0/src/lwmqtt/client.c +++ /dev/null @@ -1,618 +0,0 @@ -#include "packet.h" - -void lwmqtt_init(lwmqtt_client_t *client, uint8_t *write_buf, size_t write_buf_size, uint8_t *read_buf, - size_t read_buf_size) { - client->last_packet_id = 1; - client->keep_alive_interval = 0; - client->pong_pending = false; - - client->write_buf = write_buf; - client->write_buf_size = write_buf_size; - client->read_buf = read_buf; - client->read_buf_size = read_buf_size; - - client->callback = NULL; - client->callback_ref = NULL; - - client->network = NULL; - client->network_read = NULL; - client->network_write = NULL; - - client->keep_alive_timer = NULL; - client->command_timer = NULL; - client->timer_set = NULL; - client->timer_get = NULL; -} - -void lwmqtt_set_network(lwmqtt_client_t *client, void *ref, lwmqtt_network_read_t read, lwmqtt_network_write_t write) { - client->network = ref; - client->network_read = read; - client->network_write = write; -} - -void lwmqtt_set_timers(lwmqtt_client_t *client, void *keep_alive_timer, void *command_timer, lwmqtt_timer_set_t set, - lwmqtt_timer_get_t get) { - client->keep_alive_timer = keep_alive_timer; - client->command_timer = command_timer; - client->timer_set = set; - client->timer_get = get; - - client->timer_set(client->keep_alive_timer, 0); - client->timer_set(client->command_timer, 0); -} - -void lwmqtt_set_callback(lwmqtt_client_t *client, void *ref, lwmqtt_callback_t cb) { - client->callback_ref = ref; - client->callback = cb; -} - -static uint16_t lwmqtt_get_next_packet_id(lwmqtt_client_t *client) { - // check overflow - if (client->last_packet_id == 65535) { - client->last_packet_id = 1; - return 1; - } - - // increment packet id - client->last_packet_id++; - - return client->last_packet_id; -} - -static lwmqtt_err_t lwmqtt_read_from_network(lwmqtt_client_t *client, size_t offset, size_t len) { - // check read buffer capacity - if (client->read_buf_size < offset + len) { - return LWMQTT_BUFFER_TOO_SHORT; - } - - // prepare counter - size_t read = 0; - - // read while data is missing - while (read < len) { - // check remaining time - int32_t remaining_time = client->timer_get(client->command_timer); - if (remaining_time <= 0) { - return LWMQTT_NETWORK_TIMEOUT; - } - - // read - size_t partial_read = 0; - lwmqtt_err_t err = client->network_read(client->network, client->read_buf + offset + read, len - read, - &partial_read, (uint32_t)remaining_time); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // increment counter - read += partial_read; - } - - return LWMQTT_SUCCESS; -} - -static lwmqtt_err_t lwmqtt_write_to_network(lwmqtt_client_t *client, size_t offset, size_t len) { - // prepare counter - size_t written = 0; - - // write while data is left - while (written < len) { - // check remaining time - int32_t remaining_time = client->timer_get(client->command_timer); - if (remaining_time <= 0) { - return LWMQTT_NETWORK_TIMEOUT; - } - - // write - size_t partial_write = 0; - lwmqtt_err_t err = client->network_write(client->network, client->write_buf + offset + written, len - written, - &partial_write, (uint32_t)remaining_time); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // increment counter - written += partial_write; - } - - return LWMQTT_SUCCESS; -} - -static lwmqtt_err_t lwmqtt_read_packet_in_buffer(lwmqtt_client_t *client, size_t *read, - lwmqtt_packet_type_t *packet_type) { - // preset packet type - *packet_type = LWMQTT_NO_PACKET; - - // read or wait for header byte - lwmqtt_err_t err = lwmqtt_read_from_network(client, 0, 1); - if (err == LWMQTT_NETWORK_TIMEOUT) { - // this is ok as no data has been read at all - return LWMQTT_SUCCESS; - } else if (err != LWMQTT_SUCCESS) { - return err; - } - - // detect packet type - err = lwmqtt_detect_packet_type(client->read_buf, 1, packet_type); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // prepare variables - size_t len = 0; - uint32_t rem_len = 0; - - do { - // adjust len - len++; - - // read next byte - err = lwmqtt_read_from_network(client, len, 1); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // attempt to detect remaining length - err = lwmqtt_detect_remaining_length(client->read_buf + 1, len, &rem_len); - } while (err == LWMQTT_BUFFER_TOO_SHORT); - - // check final error - if (err != LWMQTT_SUCCESS) { - return err; - } - - // read the rest of the buffer if needed - if (rem_len > 0) { - err = lwmqtt_read_from_network(client, 1 + len, rem_len); - if (err != LWMQTT_SUCCESS) { - return err; - } - } - - // adjust counter - *read += 1 + len + rem_len; - - return LWMQTT_SUCCESS; -} - -static lwmqtt_err_t lwmqtt_send_packet_in_buffer(lwmqtt_client_t *client, size_t length) { - // write to network - lwmqtt_err_t err = lwmqtt_write_to_network(client, 0, length); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // reset keep alive timer - client->timer_set(client->keep_alive_timer, client->keep_alive_interval); - - return LWMQTT_SUCCESS; -} - -static lwmqtt_err_t lwmqtt_cycle(lwmqtt_client_t *client, size_t *read, lwmqtt_packet_type_t *packet_type) { - // read next packet from the network - lwmqtt_err_t err = lwmqtt_read_packet_in_buffer(client, read, packet_type); - if (err != LWMQTT_SUCCESS) { - return err; - } else if (*packet_type == LWMQTT_NO_PACKET) { - return LWMQTT_SUCCESS; - } - - switch (*packet_type) { - // handle publish packets - case LWMQTT_PUBLISH_PACKET: { - // decode publish packet - bool dup; - uint16_t packet_id; - lwmqtt_string_t topic; - lwmqtt_message_t msg; - err = lwmqtt_decode_publish(client->read_buf, client->read_buf_size, &dup, &packet_id, &topic, &msg); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // call callback if set - if (client->callback != NULL) { - client->callback(client, client->callback_ref, topic, msg); - } - - // break early on qos zero - if (msg.qos == LWMQTT_QOS0) { - break; - } - - // define ack packet - lwmqtt_packet_type_t ack_type = LWMQTT_NO_PACKET; - if (msg.qos == LWMQTT_QOS1) { - ack_type = LWMQTT_PUBACK_PACKET; - } else if (msg.qos == LWMQTT_QOS2) { - ack_type = LWMQTT_PUBREC_PACKET; - } - - // encode ack packet - size_t len; - err = lwmqtt_encode_ack(client->write_buf, client->write_buf_size, &len, ack_type, false, packet_id); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // send ack packet - err = lwmqtt_send_packet_in_buffer(client, len); - if (err != LWMQTT_SUCCESS) { - return err; - } - - break; - } - - // handle pubrec packets - case LWMQTT_PUBREC_PACKET: { - // decode pubrec packet - bool dup; - uint16_t packet_id; - err = lwmqtt_decode_ack(client->read_buf, client->read_buf_size, LWMQTT_PUBREC_PACKET, &dup, &packet_id); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // encode pubrel packet - size_t len; - err = lwmqtt_encode_ack(client->write_buf, client->write_buf_size, &len, LWMQTT_PUBREL_PACKET, 0, packet_id); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // send pubrel packet - err = lwmqtt_send_packet_in_buffer(client, len); - if (err != LWMQTT_SUCCESS) { - return err; - } - - break; - } - - // handle pubrel packets - case LWMQTT_PUBREL_PACKET: { - // decode pubrec packet - bool dup; - uint16_t packet_id; - err = lwmqtt_decode_ack(client->read_buf, client->read_buf_size, LWMQTT_PUBREL_PACKET, &dup, &packet_id); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // encode pubcomp packet - size_t len; - err = lwmqtt_encode_ack(client->write_buf, client->write_buf_size, &len, LWMQTT_PUBCOMP_PACKET, 0, packet_id); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // send pubcomp packet - err = lwmqtt_send_packet_in_buffer(client, len); - if (err != LWMQTT_SUCCESS) { - return err; - } - - break; - } - - // handle pingresp packets - case LWMQTT_PINGRESP_PACKET: { - // set flag - client->pong_pending = false; - - break; - } - - // handle all other packets - default: { break; } - } - - return LWMQTT_SUCCESS; -} - -static lwmqtt_err_t lwmqtt_cycle_until(lwmqtt_client_t *client, lwmqtt_packet_type_t *packet_type, size_t available, - lwmqtt_packet_type_t needle) { - // prepare counter - size_t read = 0; - - // loop until timeout has been reached - do { - // do one cycle - lwmqtt_err_t err = lwmqtt_cycle(client, &read, packet_type); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // return when one packet has been successfully read when no availability has been given - if (needle == LWMQTT_NO_PACKET && available == 0) { - return LWMQTT_SUCCESS; - } - - // otherwise check if needle has been found - if (*packet_type == needle) { - return LWMQTT_SUCCESS; - } - } while (client->timer_get(client->command_timer) > 0 && (available == 0 || read < available)); - - return LWMQTT_SUCCESS; -} - -lwmqtt_err_t lwmqtt_yield(lwmqtt_client_t *client, size_t available, uint32_t timeout) { - // set command timer - client->timer_set(client->command_timer, timeout); - - // cycle until timeout has been reached - lwmqtt_packet_type_t packet_type = LWMQTT_NO_PACKET; - lwmqtt_err_t err = lwmqtt_cycle_until(client, &packet_type, available, LWMQTT_NO_PACKET); - if (err != LWMQTT_SUCCESS) { - return err; - } - - return LWMQTT_SUCCESS; -} - -lwmqtt_err_t lwmqtt_connect(lwmqtt_client_t *client, lwmqtt_options_t options, lwmqtt_will_t *will, - lwmqtt_return_code_t *return_code, uint32_t timeout) { - // set command timer - client->timer_set(client->command_timer, timeout); - - // save keep alive interval (take 75% to be a little earlier than actually needed) - client->keep_alive_interval = (uint32_t)(options.keep_alive * 750); - - // set keep alive timer - client->timer_set(client->keep_alive_timer, client->keep_alive_interval); - - // reset pong pending flag - client->pong_pending = false; - - // initialize return code - *return_code = LWMQTT_UNKNOWN_RETURN_CODE; - - // encode connect packet - size_t len; - lwmqtt_err_t err = lwmqtt_encode_connect(client->write_buf, client->write_buf_size, &len, options, will); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // send packet - err = lwmqtt_send_packet_in_buffer(client, len); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // wait for connack packet - lwmqtt_packet_type_t packet_type = LWMQTT_NO_PACKET; - err = lwmqtt_cycle_until(client, &packet_type, 0, LWMQTT_CONNACK_PACKET); - if (err != LWMQTT_SUCCESS) { - return err; - } else if (packet_type != LWMQTT_CONNACK_PACKET) { - return LWMQTT_MISSING_OR_WRONG_PACKET; - } - - // decode connack packet - bool session_present; - err = lwmqtt_decode_connack(client->read_buf, client->read_buf_size, &session_present, return_code); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // return error if connection was not accepted - if (*return_code != LWMQTT_CONNECTION_ACCEPTED) { - return LWMQTT_CONNECTION_DENIED; - } - - return LWMQTT_SUCCESS; -} - -lwmqtt_err_t lwmqtt_subscribe(lwmqtt_client_t *client, int count, lwmqtt_string_t *topic_filter, lwmqtt_qos_t *qos, - uint32_t timeout) { - // set command timer - client->timer_set(client->command_timer, timeout); - - // encode subscribe packet - size_t len; - lwmqtt_err_t err = lwmqtt_encode_subscribe(client->write_buf, client->write_buf_size, &len, - lwmqtt_get_next_packet_id(client), count, topic_filter, qos); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // send packet - err = lwmqtt_send_packet_in_buffer(client, len); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // wait for suback packet - lwmqtt_packet_type_t packet_type = LWMQTT_NO_PACKET; - err = lwmqtt_cycle_until(client, &packet_type, 0, LWMQTT_SUBACK_PACKET); - if (err != LWMQTT_SUCCESS) { - return err; - } else if (packet_type != LWMQTT_SUBACK_PACKET) { - return LWMQTT_MISSING_OR_WRONG_PACKET; - } - - // decode packet - int suback_count = 0; - lwmqtt_qos_t granted_qos[count]; - uint16_t packet_id; - err = lwmqtt_decode_suback(client->read_buf, client->read_buf_size, &packet_id, count, &suback_count, granted_qos); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // check suback codes - for (int i = 0; i < suback_count; i++) { - if (granted_qos[i] == LWMQTT_QOS_FAILURE) { - return LWMQTT_FAILED_SUBSCRIPTION; - } - } - - return LWMQTT_SUCCESS; -} - -lwmqtt_err_t lwmqtt_subscribe_one(lwmqtt_client_t *client, lwmqtt_string_t topic_filter, lwmqtt_qos_t qos, - uint32_t timeout) { - return lwmqtt_subscribe(client, 1, &topic_filter, &qos, timeout); -} - -lwmqtt_err_t lwmqtt_unsubscribe(lwmqtt_client_t *client, int count, lwmqtt_string_t *topic_filter, uint32_t timeout) { - // set command timer - client->timer_set(client->command_timer, timeout); - - // encode unsubscribe packet - size_t len; - lwmqtt_err_t err = lwmqtt_encode_unsubscribe(client->write_buf, client->write_buf_size, &len, - lwmqtt_get_next_packet_id(client), count, topic_filter); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // send unsubscribe packet - err = lwmqtt_send_packet_in_buffer(client, len); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // wait for unsuback packet - lwmqtt_packet_type_t packet_type = LWMQTT_NO_PACKET; - err = lwmqtt_cycle_until(client, &packet_type, 0, LWMQTT_UNSUBACK_PACKET); - if (err != LWMQTT_SUCCESS) { - return err; - } else if (packet_type != LWMQTT_UNSUBACK_PACKET) { - return LWMQTT_MISSING_OR_WRONG_PACKET; - } - - // decode unsuback packet - bool dup; - uint16_t packet_id; - err = lwmqtt_decode_ack(client->read_buf, client->read_buf_size, LWMQTT_UNSUBACK_PACKET, &dup, &packet_id); - if (err != LWMQTT_SUCCESS) { - return err; - } - - return LWMQTT_SUCCESS; -} - -lwmqtt_err_t lwmqtt_unsubscribe_one(lwmqtt_client_t *client, lwmqtt_string_t topic_filter, uint32_t timeout) { - return lwmqtt_unsubscribe(client, 1, &topic_filter, timeout); -} - -lwmqtt_err_t lwmqtt_publish(lwmqtt_client_t *client, lwmqtt_string_t topic, lwmqtt_message_t message, - uint32_t timeout) { - // set command timer - client->timer_set(client->command_timer, timeout); - - // add packet id if at least qos 1 - uint16_t packet_id = 0; - if (message.qos == LWMQTT_QOS1 || message.qos == LWMQTT_QOS2) { - packet_id = lwmqtt_get_next_packet_id(client); - } - - // encode publish packet - size_t len = 0; - lwmqtt_err_t err = - lwmqtt_encode_publish(client->write_buf, client->write_buf_size, &len, 0, packet_id, topic, message); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // send packet - err = lwmqtt_send_packet_in_buffer(client, len); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // immediately return on qos zero - if (message.qos == LWMQTT_QOS0) { - return LWMQTT_SUCCESS; - } - - // define ack packet - lwmqtt_packet_type_t ack_type = LWMQTT_NO_PACKET; - if (message.qos == LWMQTT_QOS1) { - ack_type = LWMQTT_PUBACK_PACKET; - } else if (message.qos == LWMQTT_QOS2) { - ack_type = LWMQTT_PUBCOMP_PACKET; - } - - // wait for ack packet - lwmqtt_packet_type_t packet_type = LWMQTT_NO_PACKET; - err = lwmqtt_cycle_until(client, &packet_type, 0, ack_type); - if (err != LWMQTT_SUCCESS) { - return err; - } else if (packet_type != ack_type) { - return LWMQTT_MISSING_OR_WRONG_PACKET; - } - - // decode ack packet - bool dup; - err = lwmqtt_decode_ack(client->read_buf, client->read_buf_size, ack_type, &dup, &packet_id); - if (err != LWMQTT_SUCCESS) { - return err; - } - - return LWMQTT_SUCCESS; -} - -lwmqtt_err_t lwmqtt_disconnect(lwmqtt_client_t *client, uint32_t timeout) { - // set command timer - client->timer_set(client->command_timer, timeout); - - // encode disconnect packet - size_t len; - lwmqtt_err_t err = lwmqtt_encode_zero(client->write_buf, client->write_buf_size, &len, LWMQTT_DISCONNECT_PACKET); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // send disconnected packet - err = lwmqtt_send_packet_in_buffer(client, len); - if (err != LWMQTT_SUCCESS) { - return err; - } - - return LWMQTT_SUCCESS; -} - -lwmqtt_err_t lwmqtt_keep_alive(lwmqtt_client_t *client, uint32_t timeout) { - // set command timer - client->timer_set(client->command_timer, timeout); - - // return immediately if keep alive interval is zero - if (client->keep_alive_interval == 0) { - return LWMQTT_SUCCESS; - } - - // return immediately if no ping is due - if (client->timer_get(client->keep_alive_timer) > 0) { - return LWMQTT_SUCCESS; - } - - // a ping is due - - // fail immediately if a pong is already pending - if (client->pong_pending) { - return LWMQTT_PONG_TIMEOUT; - } - - // encode pingreq packet - size_t len; - lwmqtt_err_t err = lwmqtt_encode_zero(client->write_buf, client->write_buf_size, &len, LWMQTT_PINGREQ_PACKET); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // send packet - err = lwmqtt_send_packet_in_buffer(client, len); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // set flag - client->pong_pending = true; - - return LWMQTT_SUCCESS; -} diff --git a/lib/arduino-mqtt-2.4.0/src/lwmqtt/helpers.c b/lib/arduino-mqtt-2.4.0/src/lwmqtt/helpers.c deleted file mode 100644 index 9c78f4eaf..000000000 --- a/lib/arduino-mqtt-2.4.0/src/lwmqtt/helpers.c +++ /dev/null @@ -1,249 +0,0 @@ -#include - -#include "helpers.h" - -uint8_t lwmqtt_read_bits(uint8_t byte, int pos, int num) { return (byte & (uint8_t)((~(0xFF << num)) << pos)) >> pos; } - -void lwmqtt_write_bits(uint8_t *byte, uint8_t value, int pos, int num) { - *byte = (*byte & ~(uint8_t)((~(0xFF << num)) << pos)) | (value << pos); -} - -lwmqtt_err_t lwmqtt_read_data(uint8_t **buf, const uint8_t *buf_end, uint8_t **data, size_t len) { - // check zero length - if (len == 0) { - *data = NULL; - return LWMQTT_SUCCESS; - } - - // check buffer size - if ((size_t)(buf_end - (*buf)) < len) { - return LWMQTT_BUFFER_TOO_SHORT; - } - - // read data - *data = *buf; - - // advance pointer - *buf += len; - - return LWMQTT_SUCCESS; -} - -lwmqtt_err_t lwmqtt_write_data(uint8_t **buf, const uint8_t *buf_end, uint8_t *data, size_t len) { - // check zero length - if (len == 0) { - return LWMQTT_SUCCESS; - } - - // check buffer size - if ((size_t)(buf_end - (*buf)) < len) { - return LWMQTT_BUFFER_TOO_SHORT; - } - - // write data - memcpy(*buf, data, len); - - // advance pointer - *buf += len; - - return LWMQTT_SUCCESS; -} - -lwmqtt_err_t lwmqtt_read_num(uint8_t **buf, const uint8_t *buf_end, uint16_t *num) { - // check buffer size - if ((size_t)(buf_end - (*buf)) < 2) { - *num = 0; - return LWMQTT_BUFFER_TOO_SHORT; - } - - // read two byte integer - *num = (uint16_t)256 * (*buf)[0] + (*buf)[1]; - - // adjust pointer - *buf += 2; - - return LWMQTT_SUCCESS; -} - -lwmqtt_err_t lwmqtt_write_num(uint8_t **buf, const uint8_t *buf_end, uint16_t num) { - // check buffer size - if ((size_t)(buf_end - (*buf)) < 2) { - return LWMQTT_BUFFER_TOO_SHORT; - } - - // write bytes - (*buf)[0] = (uint8_t)(num / 256); - (*buf)[1] = (uint8_t)(num % 256); - - // adjust pointer - *buf += 2; - - return LWMQTT_SUCCESS; -} - -lwmqtt_err_t lwmqtt_read_string(uint8_t **buf, const uint8_t *buf_end, lwmqtt_string_t *str) { - // read length - uint16_t len; - lwmqtt_err_t err = lwmqtt_read_num(buf, buf_end, &len); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // read data - err = lwmqtt_read_data(buf, buf_end, (uint8_t **)&str->data, len); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // set length - str->len = len; - - return LWMQTT_SUCCESS; -} - -lwmqtt_err_t lwmqtt_write_string(uint8_t **buf, const uint8_t *buf_end, lwmqtt_string_t str) { - // write string length - lwmqtt_err_t err = lwmqtt_write_num(buf, buf_end, str.len); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // write data - err = lwmqtt_write_data(buf, buf_end, (uint8_t *)str.data, str.len); - if (err != LWMQTT_SUCCESS) { - return err; - } - - return LWMQTT_SUCCESS; -} - -lwmqtt_err_t lwmqtt_read_byte(uint8_t **buf, const uint8_t *buf_end, uint8_t *byte) { - // check buffer size - if ((size_t)(buf_end - (*buf)) < 1) { - *byte = 0; - return LWMQTT_BUFFER_TOO_SHORT; - } - - // read byte - *byte = (*buf)[0]; - - // adjust pointer - *buf += 1; - - return LWMQTT_SUCCESS; -} - -lwmqtt_err_t lwmqtt_write_byte(uint8_t **buf, const uint8_t *buf_end, uint8_t byte) { - // check buffer size - if ((size_t)(buf_end - (*buf)) < 1) { - return LWMQTT_BUFFER_TOO_SHORT; - } - - // write byte - (*buf)[0] = byte; - - // adjust pointer - *buf += 1; - - return LWMQTT_SUCCESS; -} - -lwmqtt_err_t lwmqtt_varnum_length(uint32_t varnum, int *len) { - if (varnum < 128) { - *len = 1; - return LWMQTT_SUCCESS; - } else if (varnum < 16384) { - *len = 2; - return LWMQTT_SUCCESS; - } else if (varnum < 2097151) { - *len = 3; - return LWMQTT_SUCCESS; - } else if (varnum < 268435455) { - *len = 4; - return LWMQTT_SUCCESS; - } else { - *len = 0; - return LWMQTT_VARNUM_OVERFLOW; - } -} - -lwmqtt_err_t lwmqtt_read_varnum(uint8_t **buf, const uint8_t *buf_end, uint32_t *varnum) { - // prepare last byte - uint8_t byte; - - // prepare multiplier - uint32_t multiplier = 1; - - // prepare length - size_t len = 0; - - // initialize number - *varnum = 0; - - // decode variadic number - do { - // increment length - len++; - - // return error if buffer is to small - if ((size_t)(buf_end - (*buf)) < len) { - return LWMQTT_BUFFER_TOO_SHORT; - } - - // return error if the length has overflowed - if (len > 4) { - return LWMQTT_VARNUM_OVERFLOW; - } - - // read byte - byte = (*buf)[len - 1]; - - // add byte to number - *varnum += (byte & 127) * multiplier; - - // increase multiplier - multiplier *= 128; - } while ((byte & 128) != 0); - - // adjust pointer - *buf += len; - - return LWMQTT_SUCCESS; -} - -lwmqtt_err_t lwmqtt_write_varnum(uint8_t **buf, const uint8_t *buf_end, uint32_t varnum) { - // init len counter - size_t len = 0; - - // encode variadic number - do { - // check overflow - if (len == 4) { - return LWMQTT_VARNUM_OVERFLOW; - } - - // return error if buffer is to small - if ((size_t)(buf_end - (*buf)) < len + 1) { - return LWMQTT_BUFFER_TOO_SHORT; - } - - // calculate current byte - uint8_t byte = (uint8_t)(varnum % 128); - - // change remaining length - varnum /= 128; - - // set the top bit of this byte if there are more to encode - if (varnum > 0) { - byte |= 0x80; - } - - // write byte - (*buf)[len++] = byte; - } while (varnum > 0); - - // adjust pointer - *buf += len; - - return LWMQTT_SUCCESS; -} diff --git a/lib/arduino-mqtt-2.4.0/src/lwmqtt/helpers.h b/lib/arduino-mqtt-2.4.0/src/lwmqtt/helpers.h deleted file mode 100644 index 978eaf4a5..000000000 --- a/lib/arduino-mqtt-2.4.0/src/lwmqtt/helpers.h +++ /dev/null @@ -1,137 +0,0 @@ -#ifndef LWMQTT_HELPERS_H -#define LWMQTT_HELPERS_H - -#include "lwmqtt.h" - -/** - * Reads bits from a byte. - * - * @param byte - The byte to read from. - * @param pos - The position of the first bit. - * @param num - The number of bits to read. - * @return The read bits as a byte. - */ -uint8_t lwmqtt_read_bits(uint8_t byte, int pos, int num); - -/** - * Write bits to a byte. - * - * @param byte - The byte to write bits to. - * @param value - The bits to write as a byte. - * @param pos - The position of the first bit. - * @param num - The number of bits to write. - */ -void lwmqtt_write_bits(uint8_t *byte, uint8_t value, int pos, int num); - -/** - * Reads arbitrary data from the specified buffer. The pointer is incremented by bytes read. - * - * @param buf - Pointer to the buffer. - * @param buf_end - Pointer to the end of the buffer. - * @param data - Pointer to beginning of data. - * @param len - The amount of data to read. - * @return LWMQTT_SUCCESS or LWMQTT_BUFFER_TOO_SHORT. - */ -lwmqtt_err_t lwmqtt_read_data(uint8_t **buf, const uint8_t *buf_end, uint8_t **data, size_t len); - -/** - * Writes arbitrary data to the specified buffer. The pointer is incremented by the bytes written. - * - * @param buf - Pointer to the buffer. - * @param buf_end - Pointer to the end of the buffer. - * @param data - Pointer to the to be written data. - * @param len - The amount of data to write. - * @return LWMQTT_SUCCESS or LWMQTT_BUFFER_TOO_SHORT. - */ -lwmqtt_err_t lwmqtt_write_data(uint8_t **buf, const uint8_t *buf_end, uint8_t *data, size_t len); - -/** - * Reads two byte number from the specified buffer. The pointer is incremented by two. - * - * @param buf - Pointer to the buffer. - * @param buf_end - Pointer to the end of the buffer. - * @param num - The read number. - * @return LWMQTT_SUCCESS or LWMQTT_BUFFER_TOO_SHORT. - */ -lwmqtt_err_t lwmqtt_read_num(uint8_t **buf, const uint8_t *buf_end, uint16_t *num); - -/** - * Writes a two byte number to the specified buffer. The pointer is incremented by two. - * - * @param buf - Pointer to the buffer. - * @param buf_end - Pointer to the end of the buffer. - * @param num - The number to write. - * @return LWMQTT_SUCCESS or LWMQTT_BUFFER_TOO_SHORT. - */ -lwmqtt_err_t lwmqtt_write_num(uint8_t **buf, const uint8_t *buf_end, uint16_t num); - -/** - * Reads a string from the specified buffer into the passed object. The pointer is incremented by the bytes read. - * - * @param buf - Pointer to the buffer. - * @param buf_end - Pointer to the end of the buffer. - * @param str - The object into which the data is to be read. - * @return LWMQTT_SUCCESS or LWMQTT_BUFFER_TOO_SHORT. - */ -lwmqtt_err_t lwmqtt_read_string(uint8_t **buf, const uint8_t *buf_end, lwmqtt_string_t *str); - -/** - * Writes a string to the specified buffer. The pointer is incremented by the bytes written. - * - * @param buf - Pointer to the buffer. - * @param buf_end - Pointer to the end of the buffer. - * @param str - The string to write. - * @return LWMQTT_SUCCESS or LWMQTT_BUFFER_TOO_SHORT. - */ -lwmqtt_err_t lwmqtt_write_string(uint8_t **buf, const uint8_t *buf_end, lwmqtt_string_t str); - -/** - * Reads one byte from the buffer. The pointer is incremented by one. - * - * @param buf - Pointer to the buffer. - * @param buf_end - Pointer to the end of the buffer. - * @param byte - The read byte. - * @return LWMQTT_SUCCESS or LWMQTT_BUFFER_TOO_SHORT. - */ -lwmqtt_err_t lwmqtt_read_byte(uint8_t **buf, const uint8_t *buf_end, uint8_t *byte); - -/** - * Writes one byte to the specified buffer. The pointer is incremented by one. - * - * @param buf - Pointer to the buffer. - * @param buf_end - Pointer to the end of the buffer. - * @param byte - The byte to write. - * @return LWMQTT_SUCCESS or LWMQTT_BUFFER_TOO_SHORT. - */ -lwmqtt_err_t lwmqtt_write_byte(uint8_t **buf, const uint8_t *buf_end, uint8_t byte); - -/** - * Returns the amount of bytes required by the variable number. - * - * @param varnum - The number to check. - * @param len - The required length; - * @return LWMQTT_SUCCESS or LWMQTT_VARNUM_OVERFLOW. - */ -lwmqtt_err_t lwmqtt_varnum_length(uint32_t varnum, int *len); - -/** - * Reads a variable number from the specified buffer. The pointer is incremented by the bytes read. - * - * @param buf - Pointer to the buffer. - * @param buf_end - Pointer to the end of the buffer. - * @param varnum - The read varnum. - * @return LWMQTT_SUCCESS, LWMQTT_BUFFER_TOO_SHORT or LWMQTT_VARNUM_OVERFLOW. - */ -lwmqtt_err_t lwmqtt_read_varnum(uint8_t **buf, const uint8_t *buf_end, uint32_t *varnum); - -/** - * Writes a variable number to the specified buffer. The pointer is incremented by the bytes written. - * - * @param buf - Pointer to the buffer. - * @param buf_end - Pointer to the end of the buffer. - * @param varnum - The number to write. - * @return LWMQTT_SUCCESS, LWMQTT_BUFFER_TOO_SHORT or LWMQTT_VARNUM_OVERFLOW. - */ -lwmqtt_err_t lwmqtt_write_varnum(uint8_t **buf, const uint8_t *buf_end, uint32_t varnum); - -#endif diff --git a/lib/arduino-mqtt-2.4.0/src/lwmqtt/lwmqtt.h b/lib/arduino-mqtt-2.4.0/src/lwmqtt/lwmqtt.h deleted file mode 100644 index 7a7f142cc..000000000 --- a/lib/arduino-mqtt-2.4.0/src/lwmqtt/lwmqtt.h +++ /dev/null @@ -1,381 +0,0 @@ -#ifndef LWMQTT_H -#define LWMQTT_H - -#include -#include -#include - -/** - * The error type used by all exposed APIs. - * - * If a function returns an error that operates on a connected client (e.g publish, keep_alive, etc.) the caller should - * switch into a disconnected state, close and cleanup the current connection and start over by creating a new - * connection. - */ -typedef enum { - LWMQTT_SUCCESS = 0, - LWMQTT_BUFFER_TOO_SHORT = -1, - LWMQTT_VARNUM_OVERFLOW = -2, - LWMQTT_NETWORK_FAILED_CONNECT = -3, - LWMQTT_NETWORK_TIMEOUT = -4, - LWMQTT_NETWORK_FAILED_READ = -5, - LWMQTT_NETWORK_FAILED_WRITE = -6, - LWMQTT_REMAINING_LENGTH_OVERFLOW = -7, - LWMQTT_REMAINING_LENGTH_MISMATCH = -8, - LWMQTT_MISSING_OR_WRONG_PACKET = -9, - LWMQTT_CONNECTION_DENIED = -10, - LWMQTT_FAILED_SUBSCRIPTION = -11, - LWMQTT_SUBACK_ARRAY_OVERFLOW = -12, - LWMQTT_PONG_TIMEOUT = -13, -} lwmqtt_err_t; - -/** - * A common string object. - */ -typedef struct { - uint16_t len; - char *data; -} lwmqtt_string_t; - -/** - * The initializer for string objects. - */ -#define lwmqtt_default_string \ - { 0, NULL } - -/** - * Returns a string object for the passed C string. - * - * @param str - The C string. - * @return A string object. - */ -lwmqtt_string_t lwmqtt_string(const char *str); - -/** - * Compares a string object to a C string. - * - * @param a - The string object to compare. - * @param b - The C string to compare. - * @return Similarity e.g. strcmp(). - */ -int lwmqtt_strcmp(lwmqtt_string_t a, const char *b); - -/** - * The available QOS levels. - */ -typedef enum { LWMQTT_QOS0 = 0, LWMQTT_QOS1 = 1, LWMQTT_QOS2 = 2, LWMQTT_QOS_FAILURE = 128 } lwmqtt_qos_t; - -/** - * The message object used to publish and receive messages. - */ -typedef struct { - lwmqtt_qos_t qos; - bool retained; - uint8_t *payload; - size_t payload_len; -} lwmqtt_message_t; - -/** - * The initializer for message objects. - */ -#define lwmqtt_default_message \ - { LWMQTT_QOS0, false, NULL, 0 } - -/** - * Forward declaration of the client object. - */ -typedef struct lwmqtt_client_t lwmqtt_client_t; - -/** - * The callback used to read from a network object. - * - * The callbacks is expected to read up to the amount of bytes in to the passed buffer. It should block the specified - * timeout and wait for more incoming data. - * - * @param ref - A custom reference. - * @param buf - The buffer. - * @param len - The length of the buffer. - * @param read - Variable that must be set with the amount of read bytes. - * @param timeout - The timeout in milliseconds for the operation. - */ -typedef lwmqtt_err_t (*lwmqtt_network_read_t)(void *ref, uint8_t *buf, size_t len, size_t *read, uint32_t timeout); - -/** - * The callback used to write to a network object. - * - * The callback is expected to write up to the amount of bytes from the passed buffer. It should wait up to the - * specified timeout to write the specified data to the network. - * - * @param ref - A custom reference. - * @param buf - The buffer. - * @param len - The length of the buffer. - * @param sent - Variable that must be set with the amount of written bytes. - * @param timeout - The timeout in milliseconds for the operation. - */ -typedef lwmqtt_err_t (*lwmqtt_network_write_t)(void *ref, uint8_t *buf, size_t len, size_t *sent, uint32_t timeout); - -/** - * The callback used to set a timer. - * - * @param ref - A custom reference. - * @param timeout - The amount of milliseconds until the deadline. - */ -typedef void (*lwmqtt_timer_set_t)(void *ref, uint32_t timeout); - -/** - * The callback used to get a timers value. - * - * @param - A custom reference. - * @return The amount of milliseconds until the deadline. May return negative numbers if the deadline has been reached. - */ -typedef int32_t (*lwmqtt_timer_get_t)(void *ref); - -/** - * The callback used to forward incoming messages. - * - * Note: The callback is mostly executed because of a call to lwmqtt_yield() that processes incoming messages. However, - * it is possible that the callback is also executed during a call to lwmqtt_subscribe(), lwmqtt_publish() or - * lwmqtt_unsubscribe() if incoming messages are received between the required acknowledgements. It is therefore not - * recommended to call any further lwmqtt methods in the callback as this might result in weird call stacks. The - * callback should place the received messages in a queue and dispatch them after the caller has returned. - */ -typedef void (*lwmqtt_callback_t)(lwmqtt_client_t *client, void *ref, lwmqtt_string_t str, lwmqtt_message_t msg); - -/** - * The client object. - */ -struct lwmqtt_client_t { - uint16_t last_packet_id; - uint32_t keep_alive_interval; - bool pong_pending; - - size_t write_buf_size, read_buf_size; - uint8_t *write_buf, *read_buf; - - lwmqtt_callback_t callback; - void *callback_ref; - - void *network; - lwmqtt_network_read_t network_read; - lwmqtt_network_write_t network_write; - - void *keep_alive_timer; - void *command_timer; - lwmqtt_timer_set_t timer_set; - lwmqtt_timer_get_t timer_get; -}; - -/** - * Will initialize the specified client object. - * - * @param client - The client object. - * @param write_buf - The write buffer. - * @param write_buf_size - The write buffer size. - * @param read_buf - The read buffer. - * @param read_buf_size - The read buffer size. - */ -void lwmqtt_init(lwmqtt_client_t *client, uint8_t *write_buf, size_t write_buf_size, uint8_t *read_buf, - size_t read_buf_size); - -/** - * Will set the network reference and callbacks for this client object. - * - * @param client - The client object. - * @param ref - The reference to the network object. - * @param read - The read callback. - * @param write - The write callback. - */ -void lwmqtt_set_network(lwmqtt_client_t *client, void *ref, lwmqtt_network_read_t read, lwmqtt_network_write_t write); - -/** - * Will set the timer references and callbacks for this client object. - * - * @param client - The client object. - * @param keep_alive_timer - The reference to the keep alive timer. - * @param command_timer - The reference to the command timer. - * @param set - The set callback. - * @param get - The get callback. - */ -void lwmqtt_set_timers(lwmqtt_client_t *client, void *keep_alive_timer, void *command_timer, lwmqtt_timer_set_t set, - lwmqtt_timer_get_t get); - -/** - * Will set the callback used to receive incoming messages. - * - * @param client - The client object. - * @param ref - A custom reference that will passed to the callback. - * @param cb - The callback to be called. - */ -void lwmqtt_set_callback(lwmqtt_client_t *client, void *ref, lwmqtt_callback_t cb); - -/** - * The object defining the last will of a client. - */ -typedef struct { - lwmqtt_string_t topic; - lwmqtt_qos_t qos; - bool retained; - lwmqtt_string_t payload; -} lwmqtt_will_t; - -/** - * The default initializer for the will object. - */ -#define lwmqtt_default_will \ - { lwmqtt_default_string, LWMQTT_QOS0, false, lwmqtt_default_string } - -/** - * The object containing the connection options for a client. - */ -typedef struct { - lwmqtt_string_t client_id; - uint16_t keep_alive; - bool clean_session; - lwmqtt_string_t username; - lwmqtt_string_t password; -} lwmqtt_options_t; - -/** - * The default initializer for the options object. - */ -#define lwmqtt_default_options \ - { lwmqtt_default_string, 60, true, lwmqtt_default_string, lwmqtt_default_string } - -/** - * The available return codes transported by the connack packet. - */ -typedef enum { - LWMQTT_CONNECTION_ACCEPTED = 0, - LWMQTT_UNACCEPTABLE_PROTOCOL = 1, - LWMQTT_IDENTIFIER_REJECTED = 2, - LWMQTT_SERVER_UNAVAILABLE = 3, - LWMQTT_BAD_USERNAME_OR_PASSWORD = 4, - LWMQTT_NOT_AUTHORIZED = 5, - LWMQTT_UNKNOWN_RETURN_CODE = 6 -} lwmqtt_return_code_t; - -/** - * Will send a connect packet and wait for a connack response and set the return code. - * - * The network object must already be connected to the server. An error is returned if the broker rejects the - * connection. - * - * @param client - The client object. - * @param options - The options object. - * @param will - The will object. - * @param return_code - The variable that will receive the return code. - * @param timeout - The command timeout. - * @return An error value. - */ -lwmqtt_err_t lwmqtt_connect(lwmqtt_client_t *client, lwmqtt_options_t options, lwmqtt_will_t *will, - lwmqtt_return_code_t *return_code, uint32_t timeout); - -/** - * Will send a publish packet and wait for all acks to complete. - * - * Note: The message callback might be called with incoming messages as part of this call. - * - * @param client - The client object. - * @param topic - The topic. - * @param message - The message. - * @param timeout - The command timeout. - * @return An error value. - */ -lwmqtt_err_t lwmqtt_publish(lwmqtt_client_t *client, lwmqtt_string_t topic, lwmqtt_message_t msg, uint32_t timeout); - -/** - * Will send a subscribe packet with multiple topic filters plus QOS levels and wait for the suback to complete. - * - * Note: The message callback might be called with incoming messages as part of this call. - * - * @param client - The client object. - * @param count - The number of topic filters and QOS levels. - * @param topic_filter - The list of topic filters. - * @param qos - The list of QOS levels. - * @param timeout - The command timeout. - * @return An error value. - */ -lwmqtt_err_t lwmqtt_subscribe(lwmqtt_client_t *client, int count, lwmqtt_string_t *topic_filter, lwmqtt_qos_t *qos, - uint32_t timeout); - -/** - * Will send a subscribe packet with a single topic filter plus QOS level and wait for the suback to complete. - * - * Note: The message callback might be called with incoming messages as part of this call. - * - * @param client - The client object. - * @param topic_filter - The topic filter. - * @param qos - The QOS level. - * @param timeout - The command timeout. - * @return An error value. - */ -lwmqtt_err_t lwmqtt_subscribe_one(lwmqtt_client_t *client, lwmqtt_string_t topic_filter, lwmqtt_qos_t qos, - uint32_t timeout); - -/** - * Will send an unsubscribe packet with multiple topic filters and wait for the unsuback to complete. - * - * Note: The message callback might be called with incoming messages as part of this call. - * - * @param client - The client object. - * @param count - The number of topic filters. - * @param topic_filter - The topic filter. - * @param timeout - The command timeout. - * @return An error value. - */ -lwmqtt_err_t lwmqtt_unsubscribe(lwmqtt_client_t *client, int count, lwmqtt_string_t *topic_filter, uint32_t timeout); - -/** - * Will send an unsubscribe packet with a single topic filter and wait for the unsuback to complete. - * - * Note: The message callback might be called with incoming messages as part of this call. - * - * @param client - The client object. - * @param topic_filter - The topic filter. - * @param timeout - The command timeout. - * @return An error value. - */ -lwmqtt_err_t lwmqtt_unsubscribe_one(lwmqtt_client_t *client, lwmqtt_string_t topic_filter, uint32_t timeout); - -/** - * Will send a disconnect packet and finish the client. - * - * @param client - The client object. - * @param timeout - The command timeout. - * @return An error value. - */ -lwmqtt_err_t lwmqtt_disconnect(lwmqtt_client_t *client, uint32_t timeout); - -/** - * Will yield control to the client and receive incoming packets from the network. - * - * Single-threaded applications may peek on the network and assess if data is available to read before calling yield and - * potentially block until the timeout is reached. Multi-threaded applications may select on the socket and block until - * data is available and then yield to the client if data is available. All applications may specify the amount of bytes - * available to read in order to constrain the yield to only receive packets that are already in-flight. - * - * If no availability info is given the yield will return after one packet has been successfully read or the deadline - * has been reached but no single bytes has been received. - * - * Note: The message callback might be called with incoming messages as part of this call. - * - * @param client - The client object. - * @param available - The available bytes to read. - * @param timeout - The command timeout. - * @return An error value. - */ -lwmqtt_err_t lwmqtt_yield(lwmqtt_client_t *client, size_t available, uint32_t timeout); - -/** - * Will yield control to the client to keep the connection alive. - * - * This functions must be called at a rate slightly lower than 25% of the configured keep alive. If keep alive is zero, - * the function must not be called at all. - * - * @param client - The client object. - * @param timeout - The command timeout. - * @return An error value. - */ -lwmqtt_err_t lwmqtt_keep_alive(lwmqtt_client_t *client, uint32_t timeout); - -#endif // LWMQTT_H diff --git a/lib/arduino-mqtt-2.4.0/src/lwmqtt/packet.c b/lib/arduino-mqtt-2.4.0/src/lwmqtt/packet.c deleted file mode 100644 index 512b44d94..000000000 --- a/lib/arduino-mqtt-2.4.0/src/lwmqtt/packet.c +++ /dev/null @@ -1,742 +0,0 @@ -#include "packet.h" - -lwmqtt_err_t lwmqtt_detect_packet_type(uint8_t *buf, size_t buf_len, lwmqtt_packet_type_t *packet_type) { - // set default packet type - *packet_type = LWMQTT_NO_PACKET; - - // prepare pointer - uint8_t *buf_ptr = buf; - uint8_t *buf_end = buf + buf_len; - - // prepare header - uint8_t header; - - // read header - lwmqtt_err_t err = lwmqtt_read_byte(&buf_ptr, buf_end, &header); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // get packet type - *packet_type = (lwmqtt_packet_type_t)lwmqtt_read_bits(header, 4, 4); - - // check if packet type is correct and can be received - switch (*packet_type) { - case LWMQTT_CONNACK_PACKET: - case LWMQTT_PUBLISH_PACKET: - case LWMQTT_PUBACK_PACKET: - case LWMQTT_PUBREC_PACKET: - case LWMQTT_PUBREL_PACKET: - case LWMQTT_PUBCOMP_PACKET: - case LWMQTT_SUBACK_PACKET: - case LWMQTT_UNSUBACK_PACKET: - case LWMQTT_PINGRESP_PACKET: - return LWMQTT_SUCCESS; - default: - *packet_type = LWMQTT_NO_PACKET; - return LWMQTT_MISSING_OR_WRONG_PACKET; - } -} - -lwmqtt_err_t lwmqtt_detect_remaining_length(uint8_t *buf, size_t buf_len, uint32_t *rem_len) { - // prepare pointer - uint8_t *ptr = buf; - - // attempt to decode remaining length - lwmqtt_err_t err = lwmqtt_read_varnum(&ptr, buf + buf_len, rem_len); - if (err == LWMQTT_VARNUM_OVERFLOW) { - *rem_len = 0; - return LWMQTT_REMAINING_LENGTH_OVERFLOW; - } else if (err != LWMQTT_SUCCESS) { - *rem_len = 0; - return err; - } - - return LWMQTT_SUCCESS; -} - -lwmqtt_err_t lwmqtt_encode_connect(uint8_t *buf, size_t buf_len, size_t *len, lwmqtt_options_t options, - lwmqtt_will_t *will) { - // prepare pointers - uint8_t *buf_ptr = buf; - uint8_t *buf_end = buf + buf_len; - - // fixed header is 10 - uint32_t rem_len = 10; - - // add client id to remaining length - rem_len += options.client_id.len + 2; - - // add will if present to remaining length - if (will != NULL) { - rem_len += will->topic.len + 2 + will->payload.len + 2; - } - - // add username if present to remaining length - if (options.username.len > 0) { - rem_len += options.username.len + 2; - - // add password if present to remaining length - if (options.password.len > 0) { - rem_len += options.password.len + 2; - } - } - - // check remaining length length - int rem_len_len; - lwmqtt_err_t err = lwmqtt_varnum_length(rem_len, &rem_len_len); - if (err == LWMQTT_VARNUM_OVERFLOW) { - return LWMQTT_REMAINING_LENGTH_OVERFLOW; - } - - // prepare header - uint8_t header = 0; - lwmqtt_write_bits(&header, LWMQTT_CONNECT_PACKET, 4, 4); - - // write header - err = lwmqtt_write_byte(&buf_ptr, buf_end, header); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // write remaining length - err = lwmqtt_write_varnum(&buf_ptr, buf_end, rem_len); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // write version string - err = lwmqtt_write_string(&buf_ptr, buf_end, lwmqtt_string("MQTT")); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // write version number - err = lwmqtt_write_byte(&buf_ptr, buf_end, 4); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // prepare flags - uint8_t flags = 0; - - // set clean session - lwmqtt_write_bits(&flags, (uint8_t)(options.clean_session), 1, 1); - - // set will flags if present - if (will != NULL) { - lwmqtt_write_bits(&flags, 1, 2, 1); - lwmqtt_write_bits(&flags, will->qos, 3, 2); - lwmqtt_write_bits(&flags, (uint8_t)(will->retained), 5, 1); - } - - // set username flag if present - if (options.username.len > 0) { - lwmqtt_write_bits(&flags, 1, 7, 1); - - // set password flag if present - if (options.password.len > 0) { - lwmqtt_write_bits(&flags, 1, 6, 1); - } - } - - // write flags - err = lwmqtt_write_byte(&buf_ptr, buf_end, flags); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // write keep alive - err = lwmqtt_write_num(&buf_ptr, buf_end, options.keep_alive); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // write client id - err = lwmqtt_write_string(&buf_ptr, buf_end, options.client_id); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // write will if present - if (will != NULL) { - // write topic - err = lwmqtt_write_string(&buf_ptr, buf_end, will->topic); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // write payload length - err = lwmqtt_write_num(&buf_ptr, buf_end, (uint16_t)will->payload.len); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // write payload - err = lwmqtt_write_data(&buf_ptr, buf_end, (uint8_t *)will->payload.data, will->payload.len); - if (err != LWMQTT_SUCCESS) { - return err; - } - } - - // write username if present - if (options.username.len > 0) { - err = lwmqtt_write_string(&buf_ptr, buf_end, options.username); - if (err != LWMQTT_SUCCESS) { - return err; - } - } - - // write password if present - if (options.username.len > 0 && options.password.len > 0) { - err = lwmqtt_write_string(&buf_ptr, buf_end, options.password); - if (err != LWMQTT_SUCCESS) { - return err; - } - } - - // set written length - *len = buf_ptr - buf; - - return LWMQTT_SUCCESS; -} - -lwmqtt_err_t lwmqtt_decode_connack(uint8_t *buf, size_t buf_len, bool *session_present, - lwmqtt_return_code_t *return_code) { - // prepare pointers - uint8_t *buf_ptr = buf; - uint8_t *buf_end = buf + buf_len; - - // read header - uint8_t header; - lwmqtt_err_t err = lwmqtt_read_byte(&buf_ptr, buf_end, &header); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // check packet type - if (lwmqtt_read_bits(header, 4, 4) != LWMQTT_CONNACK_PACKET) { - return LWMQTT_MISSING_OR_WRONG_PACKET; - } - - // read remaining length - uint32_t rem_len; - err = lwmqtt_read_varnum(&buf_ptr, buf_end, &rem_len); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // check remaining length - if (rem_len != 2) { - return LWMQTT_REMAINING_LENGTH_MISMATCH; - } - - // read flags - uint8_t flags; - err = lwmqtt_read_byte(&buf_ptr, buf_end, &flags); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // read return code - uint8_t raw_return_code; - err = lwmqtt_read_byte(&buf_ptr, buf_end, &raw_return_code); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // get session present - *session_present = lwmqtt_read_bits(flags, 7, 1) == 1; - - // get return code - switch (raw_return_code) { - case 0: - *return_code = LWMQTT_CONNECTION_ACCEPTED; - break; - case 1: - *return_code = LWMQTT_UNACCEPTABLE_PROTOCOL; - break; - case 2: - *return_code = LWMQTT_IDENTIFIER_REJECTED; - break; - case 3: - *return_code = LWMQTT_SERVER_UNAVAILABLE; - break; - case 4: - *return_code = LWMQTT_BAD_USERNAME_OR_PASSWORD; - break; - case 5: - *return_code = LWMQTT_NOT_AUTHORIZED; - break; - default: - *return_code = LWMQTT_UNKNOWN_RETURN_CODE; - } - - return LWMQTT_SUCCESS; -} - -lwmqtt_err_t lwmqtt_encode_zero(uint8_t *buf, size_t buf_len, size_t *len, lwmqtt_packet_type_t packet_type) { - // prepare pointer - uint8_t *buf_ptr = buf; - uint8_t *buf_end = buf + buf_len; - - // write header - uint8_t header = 0; - lwmqtt_write_bits(&header, packet_type, 4, 4); - lwmqtt_err_t err = lwmqtt_write_byte(&buf_ptr, buf_end, header); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // write remaining length - err = lwmqtt_write_varnum(&buf_ptr, buf_end, 0); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // set length - *len = buf_ptr - buf; - - return LWMQTT_SUCCESS; -} - -lwmqtt_err_t lwmqtt_decode_ack(uint8_t *buf, size_t buf_len, lwmqtt_packet_type_t packet_type, bool *dup, - uint16_t *packet_id) { - // prepare pointer - uint8_t *buf_ptr = buf; - uint8_t *buf_end = buf + buf_len; - - // read header - uint8_t header = 0; - lwmqtt_err_t err = lwmqtt_read_byte(&buf_ptr, buf_end, &header); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // check packet type - if (lwmqtt_read_bits(header, 4, 4) != packet_type) { - return LWMQTT_MISSING_OR_WRONG_PACKET; - } - - // get dup - *dup = lwmqtt_read_bits(header, 3, 1) == 1; - - // read remaining length - uint32_t rem_len; - err = lwmqtt_read_varnum(&buf_ptr, buf + buf_len, &rem_len); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // check remaining length - if (rem_len != 2) { - return LWMQTT_REMAINING_LENGTH_MISMATCH; - } - - // read packet id - err = lwmqtt_read_num(&buf_ptr, buf_end, packet_id); - if (err != LWMQTT_SUCCESS) { - return err; - } - - return LWMQTT_SUCCESS; -} - -lwmqtt_err_t lwmqtt_encode_ack(uint8_t *buf, size_t buf_len, size_t *len, lwmqtt_packet_type_t packet_type, bool dup, - uint16_t packet_id) { - // prepare pointer - uint8_t *buf_ptr = buf; - uint8_t *buf_end = buf + buf_len; - - // prepare header - uint8_t header = 0; - - // set packet type - lwmqtt_write_bits(&header, packet_type, 4, 4); - - // set dup - lwmqtt_write_bits(&header, (uint8_t)(dup), 3, 1); - - // set qos - lwmqtt_write_bits(&header, (uint8_t)(packet_type == LWMQTT_PUBREL_PACKET ? LWMQTT_QOS1 : LWMQTT_QOS0), 1, 2); - - // write header - lwmqtt_err_t err = lwmqtt_write_byte(&buf_ptr, buf_end, header); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // write remaining length - err = lwmqtt_write_varnum(&buf_ptr, buf_end, 2); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // write packet id - err = lwmqtt_write_num(&buf_ptr, buf_end, packet_id); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // set written length - *len = buf_ptr - buf; - - return LWMQTT_SUCCESS; -} - -lwmqtt_err_t lwmqtt_decode_publish(uint8_t *buf, size_t buf_len, bool *dup, uint16_t *packet_id, lwmqtt_string_t *topic, - lwmqtt_message_t *msg) { - // prepare pointer - uint8_t *buf_ptr = buf; - uint8_t *buf_end = buf + buf_len; - - // read header - uint8_t header; - lwmqtt_err_t err = lwmqtt_read_byte(&buf_ptr, buf_end, &header); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // check packet type - if (lwmqtt_read_bits(header, 4, 4) != LWMQTT_PUBLISH_PACKET) { - return LWMQTT_MISSING_OR_WRONG_PACKET; - } - - // get dup - *dup = lwmqtt_read_bits(header, 3, 1) == 1; - - // get retained - msg->retained = lwmqtt_read_bits(header, 0, 1) == 1; - - // get qos - switch (lwmqtt_read_bits(header, 1, 2)) { - case 0: - msg->qos = LWMQTT_QOS0; - break; - case 1: - msg->qos = LWMQTT_QOS1; - break; - case 2: - msg->qos = LWMQTT_QOS2; - break; - default: - msg->qos = LWMQTT_QOS0; - break; - } - - // read remaining length - uint32_t rem_len; - err = lwmqtt_read_varnum(&buf_ptr, buf_end, &rem_len); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // check remaining length (topic length) - if (rem_len < 2) { - return LWMQTT_REMAINING_LENGTH_MISMATCH; - } - - // check buffer capacity - if ((uint32_t)(buf_end - buf_ptr) < rem_len) { - return LWMQTT_BUFFER_TOO_SHORT; - } - - // reset buf end - buf_end = buf_ptr + rem_len; - - // read topic - err = lwmqtt_read_string(&buf_ptr, buf_end, topic); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // read packet id if qos is at least 1 - if (msg->qos > 0) { - err = lwmqtt_read_num(&buf_ptr, buf_end, packet_id); - if (err != LWMQTT_SUCCESS) { - return err; - } - } else { - *packet_id = 0; - } - - // set payload length - msg->payload_len = buf_end - buf_ptr; - - // read payload - err = lwmqtt_read_data(&buf_ptr, buf_end, &msg->payload, buf_end - buf_ptr); - if (err != LWMQTT_SUCCESS) { - return err; - } - - return LWMQTT_SUCCESS; -} - -lwmqtt_err_t lwmqtt_encode_publish(uint8_t *buf, size_t buf_len, size_t *len, bool dup, uint16_t packet_id, - lwmqtt_string_t topic, lwmqtt_message_t msg) { - // prepare pointer - uint8_t *buf_ptr = buf; - uint8_t *buf_end = buf + buf_len; - - // calculate remaining length - uint32_t rem_len = 2 + topic.len + (uint32_t)msg.payload_len; - if (msg.qos > 0) { - rem_len += 2; - } - - // check remaining length length - int rem_len_len; - lwmqtt_err_t err = lwmqtt_varnum_length(rem_len, &rem_len_len); - if (err == LWMQTT_VARNUM_OVERFLOW) { - return LWMQTT_REMAINING_LENGTH_OVERFLOW; - } - - // prepare header - uint8_t header = 0; - - // set packet type - lwmqtt_write_bits(&header, LWMQTT_PUBLISH_PACKET, 4, 4); - - // set dup - lwmqtt_write_bits(&header, (uint8_t)(dup), 3, 1); - - // set qos - lwmqtt_write_bits(&header, msg.qos, 1, 2); - - // set retained - lwmqtt_write_bits(&header, (uint8_t)(msg.retained), 0, 1); - - // write header - err = lwmqtt_write_byte(&buf_ptr, buf_end, header); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // write remaining length - err = lwmqtt_write_varnum(&buf_ptr, buf_end, rem_len); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // write topic - err = lwmqtt_write_string(&buf_ptr, buf_end, topic); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // write packet id if qos is at least 1 - if (msg.qos > 0) { - err = lwmqtt_write_num(&buf_ptr, buf_end, packet_id); - if (err != LWMQTT_SUCCESS) { - return err; - } - } - - // write payload - err = lwmqtt_write_data(&buf_ptr, buf_end, msg.payload, msg.payload_len); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // set length - *len = buf_ptr - buf; - - return LWMQTT_SUCCESS; -} - -lwmqtt_err_t lwmqtt_encode_subscribe(uint8_t *buf, size_t buf_len, size_t *len, uint16_t packet_id, int count, - lwmqtt_string_t *topic_filters, lwmqtt_qos_t *qos_levels) { - // prepare pointer - uint8_t *buf_ptr = buf; - uint8_t *buf_end = buf + buf_len; - - // calculate remaining length - uint32_t rem_len = 2; - for (int i = 0; i < count; i++) { - rem_len += 2 + topic_filters[i].len + 1; - } - - // check remaining length length - int rem_len_len; - lwmqtt_err_t err = lwmqtt_varnum_length(rem_len, &rem_len_len); - if (err == LWMQTT_VARNUM_OVERFLOW) { - return LWMQTT_REMAINING_LENGTH_OVERFLOW; - } - - // prepare header - uint8_t header = 0; - - // set packet type - lwmqtt_write_bits(&header, LWMQTT_SUBSCRIBE_PACKET, 4, 4); - - // set qos - lwmqtt_write_bits(&header, LWMQTT_QOS1, 1, 2); - - // write header - err = lwmqtt_write_byte(&buf_ptr, buf_end, header); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // write remaining length - err = lwmqtt_write_varnum(&buf_ptr, buf_end, rem_len); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // write packet id - err = lwmqtt_write_num(&buf_ptr, buf_end, packet_id); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // write all subscriptions - for (int i = 0; i < count; i++) { - // write topic - err = lwmqtt_write_string(&buf_ptr, buf_end, topic_filters[i]); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // write qos level - err = lwmqtt_write_byte(&buf_ptr, buf_end, (uint8_t)qos_levels[i]); - if (err != LWMQTT_SUCCESS) { - return err; - } - } - - // set length - *len = buf_ptr - buf; - - return LWMQTT_SUCCESS; -} - -lwmqtt_err_t lwmqtt_decode_suback(uint8_t *buf, size_t buf_len, uint16_t *packet_id, int max_count, int *count, - lwmqtt_qos_t *granted_qos_levels) { - // prepare pointer - uint8_t *buf_ptr = buf; - uint8_t *buf_end = buf + buf_len; - - // read header - uint8_t header; - lwmqtt_err_t err = lwmqtt_read_byte(&buf_ptr, buf_end, &header); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // check packet type - if (lwmqtt_read_bits(header, 4, 4) != LWMQTT_SUBACK_PACKET) { - return LWMQTT_MISSING_OR_WRONG_PACKET; - } - - // read remaining length - uint32_t rem_len; - err = lwmqtt_read_varnum(&buf_ptr, buf_end, &rem_len); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // check remaining length (packet id + min. one suback code) - if (rem_len < 3) { - return LWMQTT_REMAINING_LENGTH_MISMATCH; - } - - // read packet id - err = lwmqtt_read_num(&buf_ptr, buf_end, packet_id); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // read all suback codes - for (*count = 0; *count < (int)rem_len - 2; (*count)++) { - // check max count - if (*count > max_count) { - return LWMQTT_SUBACK_ARRAY_OVERFLOW; - } - - // read qos level - uint8_t raw_qos_level; - err = lwmqtt_read_byte(&buf_ptr, buf_end, &raw_qos_level); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // set qos level - switch (raw_qos_level) { - case 0: - granted_qos_levels[*count] = LWMQTT_QOS0; - break; - case 1: - granted_qos_levels[*count] = LWMQTT_QOS1; - break; - case 2: - granted_qos_levels[*count] = LWMQTT_QOS2; - break; - default: - granted_qos_levels[*count] = LWMQTT_QOS_FAILURE; - break; - } - } - - return LWMQTT_SUCCESS; -} - -lwmqtt_err_t lwmqtt_encode_unsubscribe(uint8_t *buf, size_t buf_len, size_t *len, uint16_t packet_id, int count, - lwmqtt_string_t *topic_filters) { - // prepare pointer - uint8_t *buf_ptr = buf; - uint8_t *buf_end = buf + buf_len; - - // calculate remaining length - uint32_t rem_len = 2; - for (int i = 0; i < count; i++) { - rem_len += 2 + topic_filters[i].len; - } - - // check remaining length length - int rem_len_len; - lwmqtt_err_t err = lwmqtt_varnum_length(rem_len, &rem_len_len); - if (err == LWMQTT_VARNUM_OVERFLOW) { - return LWMQTT_REMAINING_LENGTH_OVERFLOW; - } - - // prepare header - uint8_t header = 0; - - // set packet type - lwmqtt_write_bits(&header, LWMQTT_UNSUBSCRIBE_PACKET, 4, 4); - - // set qos - lwmqtt_write_bits(&header, LWMQTT_QOS1, 1, 2); - - // write header - err = lwmqtt_write_byte(&buf_ptr, buf_end, header); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // write remaining length - err = lwmqtt_write_varnum(&buf_ptr, buf_end, rem_len); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // write packet id - err = lwmqtt_write_num(&buf_ptr, buf_end, packet_id); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // write topics - for (int i = 0; i < count; i++) { - err = lwmqtt_write_string(&buf_ptr, buf_end, topic_filters[i]); - if (err != LWMQTT_SUCCESS) { - return err; - } - } - - // set length - *len = buf_ptr - buf; - - return LWMQTT_SUCCESS; -} diff --git a/lib/arduino-mqtt-2.4.0/src/lwmqtt/packet.h b/lib/arduino-mqtt-2.4.0/src/lwmqtt/packet.h deleted file mode 100644 index 5fe9e50f1..000000000 --- a/lib/arduino-mqtt-2.4.0/src/lwmqtt/packet.h +++ /dev/null @@ -1,185 +0,0 @@ -#ifndef LWMQTT_PACKET_H -#define LWMQTT_PACKET_H - -#include "helpers.h" - -/** - * The available packet types. - */ -typedef enum { - LWMQTT_NO_PACKET = 0, - LWMQTT_CONNECT_PACKET = 1, - LWMQTT_CONNACK_PACKET, - LWMQTT_PUBLISH_PACKET, - LWMQTT_PUBACK_PACKET, - LWMQTT_PUBREC_PACKET, - LWMQTT_PUBREL_PACKET, - LWMQTT_PUBCOMP_PACKET, - LWMQTT_SUBSCRIBE_PACKET, - LWMQTT_SUBACK_PACKET, - LWMQTT_UNSUBSCRIBE_PACKET, - LWMQTT_UNSUBACK_PACKET, - LWMQTT_PINGREQ_PACKET, - LWMQTT_PINGRESP_PACKET, - LWMQTT_DISCONNECT_PACKET -} lwmqtt_packet_type_t; - -/** - * Will detect the packet type from the at least one byte long buffer. - * - * @param buf - The buffer from which the packet type will be detected. - * @param buf_len - The length of the specified buffer. - * @param packet_type - The packet type. - * @return An error value. - */ -lwmqtt_err_t lwmqtt_detect_packet_type(uint8_t *buf, size_t buf_len, lwmqtt_packet_type_t *packet_type); - -/** - * Will detect the remaining length form the at least on byte long buffer. - * - * It will return LWMQTT_BUFFER_TOO_SHORT if the buffer is to short and an additional byte should be read from the - * network. In case the remaining length is overflowed it will return LWMQTT_REMAINING_LENGTH_OVERFLOW. - * - * @param buf - The buffer from which the remaining length will be detected. - * @param buf_len - The length of the specified buffer. - * @param rem_len - The detected remaining length. - * @return An error value. - */ -lwmqtt_err_t lwmqtt_detect_remaining_length(uint8_t *buf, size_t buf_len, uint32_t *rem_len); - -/** - * Encodes a connect packet into the supplied buffer. - * - * @param buf - The buffer into which the packet will be encoded. - * @param buf_len - The length of the specified buffer. - * @param len - The encoded length of the packet. - * @param options - The options to be used to build the connect packet. - * @param will - The last will and testament. - * @return An error value. - */ -lwmqtt_err_t lwmqtt_encode_connect(uint8_t *buf, size_t buf_len, size_t *len, lwmqtt_options_t options, - lwmqtt_will_t *will); - -/** - * Decodes a connack packet from the supplied buffer. - * - * @param buf - The raw buffer data. - * @param buf_len - The length of the specified buffer. - * @param session_present - The session present flag. - * @param return_code - The return code. - * @return An error value. - */ -lwmqtt_err_t lwmqtt_decode_connack(uint8_t *buf, size_t buf_len, bool *session_present, - lwmqtt_return_code_t *return_code); - -/** - * Encodes a zero (disconnect, pingreq) packet into the supplied buffer. - * - * @param buf - The buffer into which the packet will be encoded. - * @param buf_len - The length of the specified buffer. - * @param len - The encoded length of the packet. - * @param packet_type - The packets type. - * @return An error value. - */ -lwmqtt_err_t lwmqtt_encode_zero(uint8_t *buf, size_t buf_len, size_t *len, lwmqtt_packet_type_t packet_type); - -/** - * Decodes an ack (puback, pubrec, pubrel, pubcomp, unsuback) packet from the supplied buffer. - * - * @param buf - The raw buffer data. - * @param buf_len - The length of the specified buffer. - * @param packet_type - The packet type. - * @param dup - The dup flag. - * @param packet_id - The packet id. - * @return An error value. - */ -lwmqtt_err_t lwmqtt_decode_ack(uint8_t *buf, size_t buf_len, lwmqtt_packet_type_t packet_type, bool *dup, - uint16_t *packet_id); - -/** - * Encodes an ack (puback, pubrec, pubrel, pubcomp) packet into the supplied buffer. - * - * @param buf - The buffer into which the packet will be encoded. - * @param buf_len - The length of the specified buffer. - * @param len - The encoded length of the packet. - * @param packet_type - The packets type. - * @param dup - The dup flag. - * @param packet_id - The packet id. - * @return An error value. - */ -lwmqtt_err_t lwmqtt_encode_ack(uint8_t *buf, size_t buf_len, size_t *len, lwmqtt_packet_type_t packet_type, bool dup, - uint16_t packet_id); - -/** - * Decodes a publish packet from the supplied buffer. - * - * @param buf - The raw buffer data. - * @param buf_len - The length of the specified buffer. - * @param dup - The dup flag. - * @param packet_id - The packet id. - * @param topic - The topic. - * @parma msg - The message. - * @return An error value. - */ -lwmqtt_err_t lwmqtt_decode_publish(uint8_t *buf, size_t buf_len, bool *dup, uint16_t *packet_id, lwmqtt_string_t *topic, - lwmqtt_message_t *msg); - -/** - * Encodes a publish packet into the supplied buffer. - * - * @param buf - The buffer into which the packet will be encoded. - * @param buf_len - The length of the specified buffer. - * @param len - The encoded length of the packet. - * @param dup - The dup flag. - * @param packet_id - The packet id. - * @param topic - The topic. - * @param msg - The message. - * @return An error value. - */ -lwmqtt_err_t lwmqtt_encode_publish(uint8_t *buf, size_t buf_len, size_t *len, bool dup, uint16_t packet_id, - lwmqtt_string_t topic, lwmqtt_message_t msg); - -/** - * Encodes a subscribe packet into the supplied buffer. - * - * @param buf - The buffer into which the packet will be encoded. - * @param buf_len - The length of the specified buffer. - * @param len - The encoded length of the packet. - * @param packet_id - The packet id. - * @param count - The number of members in the topic_filters and qos_levels array. - * @param topic_filters - The array of topic filter. - * @param qos_levels - The array of requested QoS levels. - * @return An error value. - */ -lwmqtt_err_t lwmqtt_encode_subscribe(uint8_t *buf, size_t buf_len, size_t *len, uint16_t packet_id, int count, - lwmqtt_string_t *topic_filters, lwmqtt_qos_t *qos_levels); - -/** - * Decodes a suback packet from the supplied buffer. - * - * @param buf - The raw buffer data. - * @param buf_len - The length of the specified buffer. - * @param packet_id - The packet id. - * @param max_count - The maximum number of members allowed in the granted_qos_levels array. - * @param count - The number of members in the granted_qos_levels array. - * @param granted_qos_levels - The granted QoS levels. - * @return An error value. - */ -lwmqtt_err_t lwmqtt_decode_suback(uint8_t *buf, size_t buf_len, uint16_t *packet_id, int max_count, int *count, - lwmqtt_qos_t *granted_qos_levels); - -/** - * Encodes the supplied unsubscribe data into the supplied buffer, ready for sending - * - * @param buf - The buffer into which the packet will be encoded. - * @param buf_len - The length of the specified buffer. - * @param len - The encoded length of the packet. - * @param packet_id - The packet id. - * @param count - The number of members in the topic_filters array. - * @param topic_filters - The array of topic filters. - * @return An error value. - */ -lwmqtt_err_t lwmqtt_encode_unsubscribe(uint8_t *buf, size_t buf_len, size_t *len, uint16_t packet_id, int count, - lwmqtt_string_t *topic_filters); - -#endif // LWMQTT_PACKET_H diff --git a/lib/arduino-mqtt-2.4.0/src/lwmqtt/string.c b/lib/arduino-mqtt-2.4.0/src/lwmqtt/string.c deleted file mode 100644 index c27dc94e3..000000000 --- a/lib/arduino-mqtt-2.4.0/src/lwmqtt/string.c +++ /dev/null @@ -1,38 +0,0 @@ -#include - -#include "lwmqtt.h" - -lwmqtt_string_t lwmqtt_string(const char *str) { - // check for null - if (str == NULL) { - return (lwmqtt_string_t){0, NULL}; - } - - // get length - uint16_t len = (uint16_t)strlen(str); - - // check zero length - if (len == 0) { - return (lwmqtt_string_t){0, NULL}; - } - - return (lwmqtt_string_t){len, (char *)str}; -} - -int lwmqtt_strcmp(lwmqtt_string_t a, const char *b) { - // get string of b - lwmqtt_string_t b_str = lwmqtt_string(b); - - // return if both are zero length - if (a.len == 0 && b_str.len == 0) { - return 0; - } - - // return if lengths are different - if (a.len != b_str.len) { - return -1; - } - - // compare memory of same length - return strncmp(a.data, b_str.data, a.len); -} From 9880ed84ceed5aeca690e9de6ec65be293e05e8c Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Tue, 19 Mar 2019 15:13:45 +0100 Subject: [PATCH 022/428] Update RELEASENOTES.md --- RELEASENOTES.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index e363ba9ac..4485d3083 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -8,7 +8,7 @@ See [wiki migration path](https://github.com/arendst/Sonoff-Tasmota/wiki/Upgrade ## Release notes ### Core version 2.3.0 vs 2.4.2 -This release is based on ESP8266/Arduino library core 2.3.0 (again) as some people encountered wifi related issues on core 2.4.2. For others core 2.4.2 is working just fine. Both version are available from http://thehackbox.org/tasmota/ +This release is based on ESP8266/Arduino library core 2.3.0 (again) as some people encountered wifi related issues on core 2.4.2. For others core 2.4.2 is working just fine. Both version are available from http://thehackbox.org/tasmota/release/ ### Change in default initial configuration tool Firmware binary **sonoff-classic.bin** supports **WifiManager, Wps and SmartConfig** for initial configuration. The default tool is **Wps**. @@ -102,7 +102,7 @@ The following binary downloads have been compiled with ESP8266/Arduino library c - **sonoff-display.bin** = The Display version without Wps and SmartConfig configuration and Energy Monitoring but adds display support. - **sonoff-minimal.bin** = The Minimal version allows intermediate OTA uploads to support larger versions and does NOT change any persistent parameter. This version **should NOT be used for initial installation**. -Core version **2.4.2** binaries can be found at http://thehackbox.org/tasmota/ +Core version **2.4.2** binaries can be found at http://thehackbox.org/tasmota/release/020402/ ### Available Features and Sensors From 4fdc4155713c7e7d961d7a35b436d0e3c5d27fe9 Mon Sep 17 00:00:00 2001 From: Jason2866 Date: Tue, 7 May 2019 23:05:31 +0200 Subject: [PATCH 023/428] Create .gitpod.yml To make it possible to compile master branch with Gitpod --- .gitpod.yml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .gitpod.yml diff --git a/.gitpod.yml b/.gitpod.yml new file mode 100644 index 000000000..38e7d7bfa --- /dev/null +++ b/.gitpod.yml @@ -0,0 +1,3 @@ +tasks: + - before: pip install -U platformio + command: platformio run -e sonoff From c702cb17e05b61787bbeeb7cc6bc8d9b946e0c86 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Fri, 5 Jul 2019 13:44:24 +0200 Subject: [PATCH 024/428] Release 6.6 Release 6.6 --- .gitignore | 5 + API.md | 5 +- CONTRIBUTING.md | 58 +- README.md | 70 +- REFERENCE.md | 5 +- RELEASENOTES.md | 16 +- SUPPORT.md | 13 +- TEMPLATE.md | 7 +- .../boards.txt | 421 +- .../platform.txt | 78 +- include/dummy.txt | 1 + .../.github/ISSUE_TEMPLATE.md | 0 .../.github/PULL_REQUEST_TEMPLATE.md | 0 .../.gitignore | 0 .../.travis.yml | 0 .../Adafruit_SGP30.cpp | 58 +- .../Adafruit_SGP30.h | 1 + .../README.md | 0 .../examples/sgp30test/sgp30test.ino | 16 + .../library.properties | 2 +- .../license.txt | 0 .../examples/IRMQTTServer/IRMQTTServer.ino | 1608 ------ .../examples/IRrecvDump/platformio.ini | 17 - .../examples/IRrecvDumpV2/platformio.ini | 17 - .../examples/IRsendDemo/platformio.ini | 17 - .../examples/IRsendProntoDemo/platformio.ini | 17 - .../JVCPanasonicSendDemo/platformio.ini | 17 - .../examples/LGACSend/platformio.ini | 17 - .../examples/TurnOnArgoAC/platformio.ini | 17 - .../examples/TurnOnDaikinAC/platformio.ini | 17 - .../examples/TurnOnFujitsuAC/platformio.ini | 17 - .../TurnOnKelvinatorAC/platformio.ini | 17 - .../TurnOnMitsubishiAC/platformio.ini | 17 - .../examples/TurnOnToshibaAC/platformio.ini | 17 - .../examples/TurnOnTrotecAC/platformio.ini | 17 - lib/IRremoteESP8266-2.5.2.03/src/IRutils.cpp | 470 -- .../src/ir_Daikin.cpp | 750 --- lib/IRremoteESP8266-2.5.2.03/src/ir_Daikin.h | 208 - .../src/ir_Trotec.cpp | 127 - .../src/ir_Whirlpool.cpp | 149 - .../test/IRsend_test.cpp | 290 -- .../test/ir_Daikin_test.cpp | 838 ---- .../test/ir_Whirlpool_test.cpp | 118 - .../.github/CONTRIBUTING.md | 0 .../.github/Contributors.md | 4 +- .../.github/issue_template.md | 0 .../.gitignore | 0 .../.gitmodules | 0 .../.style.yapf | 0 .../.travis.yml | 15 +- .../CPPLINT.cfg | 0 .../LICENSE.txt | 0 .../README.md | 5 +- .../ReleaseNotes.md | 102 + .../ControlSamsungAC/ControlSamsungAC.ino | 99 + .../examples/ControlSamsungAC}/platformio.ini | 2 + .../examples/IRGCSendDemo/IRGCSendDemo.ino | 0 .../examples/IRGCSendDemo}/platformio.ini | 2 + .../examples/IRGCTCPServer/IRGCTCPServer.ino | 0 .../examples/IRGCTCPServer}/platformio.ini | 2 + .../examples/IRMQTTServer/IRMQTTServer.h | 258 + .../examples/IRMQTTServer/IRMQTTServer.ino | 3034 ++++++++++++ .../examples/IRMQTTServer/platformio.ini | 16 +- .../examples/IRServer/IRServer.ino | 0 .../examples/IRServer/platformio.ini | 2 + .../examples/IRrecvDemo/IRrecvDemo.ino | 0 .../examples/IRrecvDemo/platformio.ini | 19 + .../examples/IRrecvDump/IRrecvDump.ino | 0 .../examples/IRrecvDump/platformio.ini | 19 + .../examples/IRrecvDumpV2/IRrecvDumpV2.ino | 62 +- .../examples/IRrecvDumpV2/platformio.ini | 19 + .../examples/IRsendDemo/IRsendDemo.ino | 19 +- .../examples/IRsendDemo/platformio.ini | 19 + .../IRsendProntoDemo/IRsendProntoDemo.ino | 0 .../examples/IRsendProntoDemo/platformio.ini | 19 + .../JVCPanasonicSendDemo.ino | 0 .../JVCPanasonicSendDemo/platformio.ini | 19 + .../examples/LGACSend/LGACSend.ino | 0 .../examples/LGACSend/platformio.ini | 19 + .../examples/TurnOnArgoAC/TurnOnArgoAC.ino | 0 .../examples/TurnOnArgoAC/platformio.ini | 19 + .../TurnOnDaikinAC/TurnOnDaikinAC.ino | 0 .../examples/TurnOnDaikinAC/platformio.ini | 19 + .../TurnOnFujitsuAC/TurnOnFujitsuAC.ino | 0 .../examples/TurnOnFujitsuAC/platformio.ini | 19 + .../TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino | 0 .../TurnOnKelvinatorAC/platformio.ini | 19 + .../TurnOnMitsubishiAC/TurnOnMitsubishiAC.ino | 0 .../TurnOnMitsubishiAC/platformio.ini | 19 + .../TurnOnMitsubishiHeavyAc.ino | 72 + .../TurnOnMitsubishiHeavyAc/platformio.ini | 19 + .../TurnOnPanasonicAC/TurnOnPanasonicAC.ino | 74 + .../examples/TurnOnPanasonicAC/platformio.ini | 19 + .../TurnOnToshibaAC/TurnOnToshibaAC.ino | 0 .../examples/TurnOnToshibaAC/platformio.ini | 19 + .../TurnOnTrotecAC/TurnOnTrotecAC.ino | 0 .../examples/TurnOnTrotecAC/platformio.ini | 19 + .../keywords.txt | 542 ++- .../library.json | 2 +- .../library.properties | 2 +- .../platformio.ini | 3 + .../pylintrc | 0 .../src/CPPLINT.cfg | 0 lib/IRremoteESP8266-2.6.0/src/IRac.cpp | 1125 +++++ lib/IRremoteESP8266-2.6.0/src/IRac.h | 248 + .../src/IRrecv.cpp | 34 + .../src/IRrecv.h | 38 +- .../src/IRremoteESP8266.h | 137 +- .../src/IRsend.cpp | 213 +- .../src/IRsend.h | 124 +- .../src/IRtimer.cpp | 33 +- .../src/IRtimer.h | 12 + lib/IRremoteESP8266-2.6.0/src/IRutils.cpp | 768 +++ .../src/IRutils.h | 13 +- .../src/ir_Aiwa.cpp | 0 .../src/ir_Argo.cpp | 38 +- .../src/ir_Argo.h | 16 +- .../src/ir_Carrier.cpp | 0 .../src/ir_Coolix.cpp | 216 +- .../src/ir_Coolix.h | 31 +- lib/IRremoteESP8266-2.6.0/src/ir_Daikin.cpp | 1712 +++++++ lib/IRremoteESP8266-2.6.0/src/ir_Daikin.h | 444 ++ .../src/ir_Denon.cpp | 0 .../src/ir_Dish.cpp | 0 .../src/ir_Electra.cpp | 21 +- .../src/ir_Fujitsu.cpp | 95 +- .../src/ir_Fujitsu.h | 13 +- .../src/ir_GICable.cpp | 0 .../src/ir_GlobalCache.cpp | 0 .../src/ir_Gree.cpp | 128 +- .../src/ir_Gree.h | 17 +- .../src/ir_Haier.cpp | 324 +- .../src/ir_Haier.h | 26 +- .../src/ir_Hitachi.cpp | 89 +- .../src/ir_Hitachi.h | 13 +- .../src/ir_JVC.cpp | 0 .../src/ir_Kelvinator.cpp | 94 +- .../src/ir_Kelvinator.h | 12 +- .../src/ir_LG.cpp | 6 +- .../src/ir_LG.h | 0 .../src/ir_Lasertag.cpp | 2 +- lib/IRremoteESP8266-2.6.0/src/ir_Lego.cpp | 128 + .../src/ir_Lutron.cpp | 0 .../src/ir_MWM.cpp | 4 +- .../src/ir_Magiquest.cpp | 0 .../src/ir_Magiquest.h | 4 +- .../src/ir_Midea.cpp | 81 +- .../src/ir_Midea.h | 11 +- .../src/ir_Mitsubishi.cpp | 111 +- .../src/ir_Mitsubishi.h | 14 +- .../src/ir_MitsubishiHeavy.cpp | 1014 ++++ .../src/ir_MitsubishiHeavy.h | 264 + .../src/ir_NEC.cpp | 0 .../src/ir_NEC.h | 0 .../src/ir_Nikai.cpp | 0 .../src/ir_Panasonic.cpp | 240 +- .../src/ir_Panasonic.h | 16 +- .../src/ir_Pioneer.cpp | 0 .../src/ir_Pronto.cpp | 0 .../src/ir_RC5_RC6.cpp | 0 .../src/ir_RCMM.cpp | 0 .../src/ir_Samsung.cpp | 293 +- .../src/ir_Samsung.h | 19 +- .../src/ir_Sanyo.cpp | 0 .../src/ir_Sharp.cpp | 0 .../src/ir_Sherwood.cpp | 0 .../src/ir_Sony.cpp | 0 lib/IRremoteESP8266-2.6.0/src/ir_Tcl.cpp | 420 ++ lib/IRremoteESP8266-2.6.0/src/ir_Tcl.h | 105 + lib/IRremoteESP8266-2.6.0/src/ir_Teco.cpp | 278 ++ lib/IRremoteESP8266-2.6.0/src/ir_Teco.h | 144 + .../src/ir_Toshiba.cpp | 66 +- .../src/ir_Toshiba.h | 11 +- lib/IRremoteESP8266-2.6.0/src/ir_Trotec.cpp | 162 + .../src/ir_Trotec.h | 37 +- lib/IRremoteESP8266-2.6.0/src/ir_Vestel.cpp | 583 +++ lib/IRremoteESP8266-2.6.0/src/ir_Vestel.h | 177 + .../src/ir_Whirlpool.cpp | 671 +++ lib/IRremoteESP8266-2.6.0/src/ir_Whirlpool.h | 167 + .../src/ir_Whynter.cpp | 0 lib/IRremoteESP8266-2.6.0/test/IRac_test.cpp | 865 ++++ .../test/IRrecv_test.cpp | 0 .../test/IRrecv_test.h | 0 .../test/IRsend_test.cpp | 686 +++ .../test/IRsend_test.h | 22 +- .../test/IRutils_test.cpp | 65 + .../test/Makefile | 186 +- .../test/ir_Aiwa_test.cpp | 6 + .../test/ir_Carrier_test.cpp | 4 + .../test/ir_Coolix_test.cpp | 164 +- .../test/ir_Daikin_test.cpp | 1880 +++++++ .../test/ir_Denon_test.cpp | 8 + .../test/ir_Dish_test.cpp | 8 + .../test/ir_Electra_test.cpp | 19 +- .../test/ir_Fujitsu_test.cpp | 5 + .../test/ir_GICable_test.cpp | 6 + .../test/ir_GlobalCache_test.cpp | 2 + .../test/ir_Gree_test.cpp | 21 +- .../test/ir_Haier_test.cpp | 93 +- .../test/ir_Hitachi_test.cpp | 5 + .../test/ir_JVC_test.cpp | 5 + .../test/ir_Kelvinator_test.cpp | 4 + .../test/ir_LG_test.cpp | 58 + .../test/ir_Lasertag_test.cpp | 9 +- .../test/ir_Lego_test.cpp | 196 + .../test/ir_Lutron_test.cpp | 9 +- .../test/ir_MWM_test.cpp | 2 + .../test/ir_Magiquest_test.cpp | 3 + .../test/ir_Midea_test.cpp | 7 + .../test/ir_MitsubishiHeavy_test.cpp | 851 ++++ .../test/ir_Mitsubishi_test.cpp | 17 + .../test/ir_NEC_test.cpp | 9 +- .../test/ir_Nikai_test.cpp | 3 + .../test/ir_Panasonic_test.cpp | 9 + .../test/ir_Pioneer_test.cpp | 3 + .../test/ir_Pronto_test.cpp | 20 +- .../test/ir_RC5_RC6_test.cpp | 26 + .../test/ir_RCMM_test.cpp | 5 + .../test/ir_Samsung_test.cpp | 289 +- .../test/ir_Sanyo_test.cpp | 2 + .../test/ir_Sharp_test.cpp | 8 + .../test/ir_Sherwood_test.cpp | 3 + .../test/ir_Sony_test.cpp | 7 + .../test/ir_Tcl_test.cpp | 384 ++ .../test/ir_Teco_test.cpp | 358 ++ .../test/ir_Toshiba_test.cpp | 7 + .../test/ir_Vestel_test.cpp | 513 ++ .../test/ir_Whirlpool_test.cpp | 585 +++ .../test/ir_Whynter_test.cpp | 8 + .../tools/Makefile | 55 +- .../tools/RawToGlobalCache.sh | 0 .../tools/auto_analyse_raw_data.py | 6 +- .../tools/auto_analyse_raw_data_test.py | 0 .../tools/gc_decode.cpp | 0 .../tools/mkkeywords | 0 .../tools/mode2_decode.cpp | 0 lib/Joba_Tsl2561-2.0.10/.gitignore | 8 + lib/Joba_Tsl2561-2.0.10/.hgignore | 4 + lib/Joba_Tsl2561-2.0.10/.travis.yml | 55 + .../COPYING | 0 .../COPYING.LESSER | 0 .../README | 4 +- .../examples/Autogain/Autogain.ino | 4 +- .../examples/Simple/Simple.ino | 6 +- .../examples/Testing/Testing.ino | 4 +- .../examples/Utility/Utility.ino | 6 +- .../examples}/platformio.ini | 31 +- .../examples/platformio.sh | 0 .../lib/readme.txt | 0 .../library.json | 4 +- .../library.properties | 4 +- .../platformio.ini | 26 +- .../src/Tsl2561.cpp | 0 .../src/Tsl2561.h | 8 + .../src/Tsl2561Util.cpp | 31 +- .../src/Tsl2561Util.h | 0 .../tests/testcases/mqtt_basic.py | 43 - .../testcases/mqtt_publish_in_callback.py | 64 - .../tests/testsuite.py | 179 - .../.gitignore | 0 .../.travis.yml | 0 .../CHANGES.txt | 10 +- .../LICENSE.txt | 0 .../README.md | 3 +- .../examples/mqtt_auth/mqtt_auth.ino | 0 .../examples/mqtt_basic/mqtt_basic.ino | 0 .../examples/mqtt_esp8266/mqtt_esp8266.ino | 28 +- .../mqtt_large_message/mqtt_large_message.ino | 179 + .../mqtt_publish_in_callback.ino | 0 .../mqtt_reconnect_nonblocking.ino | 0 .../examples/mqtt_stream/mqtt_stream.ino | 0 .../keywords.txt | 3 + .../library.json | 2 +- .../library.properties | 2 +- .../src/PubSubClient.cpp | 138 +- .../src/PubSubClient.h | 34 +- .../tests/.gitignore | 0 .../tests/Makefile | 0 .../tests/README.md | 0 .../tests/src/connect_spec.cpp | 46 + .../tests/src/keepalive_spec.cpp | 0 .../tests/src/lib/Arduino.h | 3 + .../tests/src/lib/BDDTest.cpp | 0 .../tests/src/lib/BDDTest.h | 0 .../tests/src/lib/Buffer.cpp | 4 + .../tests/src/lib/Buffer.h | 0 .../tests/src/lib/Client.h | 0 .../tests/src/lib/IPAddress.cpp | 0 .../tests/src/lib/IPAddress.h | 0 .../tests/src/lib/Print.h | 28 + .../tests/src/lib/ShimClient.cpp | 0 .../tests/src/lib/ShimClient.h | 0 .../tests/src/lib/Stream.cpp | 0 .../tests/src/lib/Stream.h | 0 .../tests/src/lib/trace.h | 0 .../tests/src/publish_spec.cpp | 0 .../tests/src/receive_spec.cpp | 30 + .../tests/src/subscribe_spec.cpp | 0 .../tests/testcases/__init__.py | 0 .../tests/testcases/mqtt_basic.py | 39 + .../testcases/mqtt_publish_in_callback.py | 59 + .../tests/testcases/settings.py | 0 .../tests/testsuite.py | 181 + .../README.md | 0 .../examples/swsertest/swsertest.ino | 0 .../keywords.txt | 0 .../library.json | 0 .../library.properties | 0 .../src/TasmotaSerial.cpp | 6 +- .../src/TasmotaSerial.h | 0 .../bearssl_esp8266-customized.txt | 13 + lib/bearssl-esp8266/conf/esp8266.mk | 21 + lib/bearssl-esp8266/library.properties | 9 + lib/bearssl-esp8266/src/aead/ccm.c | 346 ++ lib/bearssl-esp8266/src/aead/eax.c | 525 ++ lib/bearssl-esp8266/src/aead/gcm.c | 318 ++ lib/bearssl-esp8266/src/codec/ccopy.c | 44 + lib/bearssl-esp8266/src/codec/dec16be.c | 38 + lib/bearssl-esp8266/src/codec/dec16le.c | 38 + lib/bearssl-esp8266/src/codec/dec32be.c | 38 + lib/bearssl-esp8266/src/codec/dec32le.c | 38 + lib/bearssl-esp8266/src/codec/dec64be.c | 38 + lib/bearssl-esp8266/src/codec/dec64le.c | 38 + lib/bearssl-esp8266/src/codec/enc16be.c | 38 + lib/bearssl-esp8266/src/codec/enc16le.c | 38 + lib/bearssl-esp8266/src/codec/enc32be.c | 38 + lib/bearssl-esp8266/src/codec/enc32le.c | 38 + lib/bearssl-esp8266/src/codec/enc64be.c | 38 + lib/bearssl-esp8266/src/codec/enc64le.c | 38 + lib/bearssl-esp8266/src/codec/pemdec.c | 536 ++ lib/bearssl-esp8266/src/codec/pemenc.c | 173 + lib/bearssl-esp8266/src/ec/ec_all_m15.c | 121 + lib/bearssl-esp8266/src/ec/ec_c25519_i15.c | 398 ++ lib/bearssl-esp8266/src/ec/ec_curve25519.c | 46 + lib/bearssl-esp8266/src/ec/ec_default.c | 36 + lib/bearssl-esp8266/src/ec/ec_keygen.c | 86 + lib/bearssl-esp8266/src/ec/ec_p256_m15.c | 2117 ++++++++ lib/bearssl-esp8266/src/ec/ec_prime_i15.c | 822 ++++ lib/bearssl-esp8266/src/ec/ec_pubkey.c | 85 + lib/bearssl-esp8266/src/ec/ec_secp256r1.c | 51 + lib/bearssl-esp8266/src/ec/ec_secp384r1.c | 57 + lib/bearssl-esp8266/src/ec/ec_secp521r1.c | 64 + lib/bearssl-esp8266/src/ec/ecdsa_atr.c | 134 + .../src/ec/ecdsa_default_sign_asn1.c | 36 + .../src/ec/ecdsa_default_sign_raw.c | 36 + .../src/ec/ecdsa_default_vrfy_asn1.c | 36 + .../src/ec/ecdsa_default_vrfy_raw.c | 36 + lib/bearssl-esp8266/src/ec/ecdsa_i15_bits.c | 47 + .../src/ec/ecdsa_i15_sign_asn1.c | 45 + .../src/ec/ecdsa_i15_sign_raw.c | 174 + .../src/ec/ecdsa_i15_vrfy_asn1.c | 48 + .../src/ec/ecdsa_i15_vrfy_raw.c | 166 + lib/bearssl-esp8266/src/ec/ecdsa_rta.c | 121 + lib/bearssl-esp8266/src/hash/dig_oid.c | 84 + lib/bearssl-esp8266/src/hash/dig_size.c | 50 + lib/bearssl-esp8266/src/hash/ghash_ctmul.c | 345 ++ lib/bearssl-esp8266/src/hash/ghash_ctmul32.c | 251 + lib/bearssl-esp8266/src/hash/ghash_ctmul64.c | 154 + lib/bearssl-esp8266/src/hash/ghash_pclmul.c | 389 ++ lib/bearssl-esp8266/src/hash/md5.c | 210 + lib/bearssl-esp8266/src/hash/md5sha1.c | 141 + lib/bearssl-esp8266/src/hash/mgf1.c | 56 + lib/bearssl-esp8266/src/hash/multihash.c | 166 + lib/bearssl-esp8266/src/hash/sha1.c | 191 + lib/bearssl-esp8266/src/hash/sha2big.c | 285 ++ lib/bearssl-esp8266/src/hash/sha2small.c | 341 ++ lib/bearssl-esp8266/src/int/i15_add.c | 46 + lib/bearssl-esp8266/src/int/i15_bitlen.c | 44 + lib/bearssl-esp8266/src/int/i15_decmod.c | 124 + lib/bearssl-esp8266/src/int/i15_decode.c | 56 + lib/bearssl-esp8266/src/int/i15_decred.c | 100 + lib/bearssl-esp8266/src/int/i15_encode.c | 56 + lib/bearssl-esp8266/src/int/i15_fmont.c | 59 + lib/bearssl-esp8266/src/int/i15_iszero.c | 39 + lib/bearssl-esp8266/src/int/i15_moddiv.c | 465 ++ lib/bearssl-esp8266/src/int/i15_modpow.c | 50 + lib/bearssl-esp8266/src/int/i15_modpow2.c | 160 + lib/bearssl-esp8266/src/int/i15_montmul.c | 321 ++ lib/bearssl-esp8266/src/int/i15_mulacc.c | 61 + lib/bearssl-esp8266/src/int/i15_muladd.c | 173 + lib/bearssl-esp8266/src/int/i15_ninv15.c | 38 + lib/bearssl-esp8266/src/int/i15_reduce.c | 66 + lib/bearssl-esp8266/src/int/i15_rshift.c | 47 + lib/bearssl-esp8266/src/int/i15_sub.c | 46 + lib/bearssl-esp8266/src/int/i15_tmont.c | 36 + lib/bearssl-esp8266/src/kdf/hkdf.c | 107 + lib/bearssl-esp8266/src/kdf/shake.c | 590 +++ lib/bearssl-esp8266/src/mac/hmac.c | 122 + lib/bearssl-esp8266/src/mac/hmac_ct.c | 193 + lib/bearssl-esp8266/src/pgmspace_bearssl.h | 119 + lib/bearssl-esp8266/src/rand/aesctr_drbg.c | 206 + lib/bearssl-esp8266/src/rand/hmac_drbg.c | 157 + lib/bearssl-esp8266/src/rand/sysrng.c | 195 + .../src/rsa/rsa_default_keygen.c | 38 + .../src/rsa/rsa_default_modulus.c | 36 + .../src/rsa/rsa_default_oaep_decrypt.c | 38 + .../src/rsa/rsa_default_oaep_encrypt.c | 38 + .../src/rsa/rsa_default_pkcs1_sign.c | 38 + .../src/rsa/rsa_default_pkcs1_vrfy.c | 38 + .../src/rsa/rsa_default_priv.c | 38 + .../src/rsa/rsa_default_privexp.c | 36 + .../src/rsa/rsa_default_pss_sign.c | 38 + .../src/rsa/rsa_default_pss_vrfy.c | 38 + lib/bearssl-esp8266/src/rsa/rsa_default_pub.c | 38 + .../src/rsa/rsa_default_pubexp.c | 36 + lib/bearssl-esp8266/src/rsa/rsa_i15_keygen.c | 585 +++ lib/bearssl-esp8266/src/rsa/rsa_i15_modulus.c | 99 + .../src/rsa/rsa_i15_oaep_decrypt.c | 41 + .../src/rsa/rsa_i15_oaep_encrypt.c | 44 + .../src/rsa/rsa_i15_pkcs1_sign.c | 37 + .../src/rsa/rsa_i15_pkcs1_vrfy.c | 43 + lib/bearssl-esp8266/src/rsa/rsa_i15_priv.c | 215 + lib/bearssl-esp8266/src/rsa/rsa_i15_privexp.c | 320 ++ .../src/rsa/rsa_i15_pss_sign.c | 40 + .../src/rsa/rsa_i15_pss_vrfy.c | 44 + lib/bearssl-esp8266/src/rsa/rsa_i15_pub.c | 113 + lib/bearssl-esp8266/src/rsa/rsa_i15_pubexp.c | 152 + lib/bearssl-esp8266/src/rsa/rsa_oaep_pad.c | 112 + lib/bearssl-esp8266/src/rsa/rsa_oaep_unpad.c | 145 + .../src/rsa/rsa_pkcs1_sig_pad.c | 100 + .../src/rsa/rsa_pkcs1_sig_unpad.c | 121 + lib/bearssl-esp8266/src/rsa/rsa_pss_sig_pad.c | 106 + .../src/rsa/rsa_pss_sig_unpad.c | 121 + lib/bearssl-esp8266/src/rsa/rsa_ssl_decrypt.c | 52 + lib/bearssl-esp8266/src/settings.c | 313 ++ lib/bearssl-esp8266/src/ssl/prf.c | 73 + lib/bearssl-esp8266/src/ssl/prf_md5sha1.c | 43 + lib/bearssl-esp8266/src/ssl/prf_sha256.c | 36 + lib/bearssl-esp8266/src/ssl/prf_sha384.c | 36 + .../src/ssl/ssl_ccert_single_ec.c | 156 + .../src/ssl/ssl_ccert_single_rsa.c | 157 + lib/bearssl-esp8266/src/ssl/ssl_client.c | 78 + .../src/ssl/ssl_client_default_rsapub.c | 32 + lib/bearssl-esp8266/src/ssl/ssl_client_full.c | 179 + lib/bearssl-esp8266/src/ssl/ssl_engine.c | 1569 ++++++ .../src/ssl/ssl_engine_default_aescbc.c | 64 + .../src/ssl/ssl_engine_default_aesccm.c | 67 + .../src/ssl/ssl_engine_default_aesgcm.c | 89 + .../src/ssl/ssl_engine_default_chapol.c | 65 + .../src/ssl/ssl_engine_default_descbc.c | 37 + .../src/ssl/ssl_engine_default_ec.c | 36 + .../src/ssl/ssl_engine_default_ecdsa.c | 38 + .../src/ssl/ssl_engine_default_rsavrfy.c | 32 + lib/bearssl-esp8266/src/ssl/ssl_hashes.c | 46 + lib/bearssl-esp8266/src/ssl/ssl_hs_client.c | 1927 ++++++++ lib/bearssl-esp8266/src/ssl/ssl_hs_server.c | 1995 ++++++++ lib/bearssl-esp8266/src/ssl/ssl_io.c | 261 + lib/bearssl-esp8266/src/ssl/ssl_keyexport.c | 83 + lib/bearssl-esp8266/src/ssl/ssl_lru.c | 537 ++ lib/bearssl-esp8266/src/ssl/ssl_rec_cbc.c | 440 ++ lib/bearssl-esp8266/src/ssl/ssl_rec_ccm.c | 213 + lib/bearssl-esp8266/src/ssl/ssl_rec_chapol.c | 177 + lib/bearssl-esp8266/src/ssl/ssl_rec_gcm.c | 235 + .../src/ssl/ssl_scert_single_ec.c | 142 + .../src/ssl/ssl_scert_single_rsa.c | 162 + .../src/symcipher/aes_big_cbcdec.c | 69 + .../src/symcipher/aes_big_cbcenc.c | 67 + .../src/symcipher/aes_big_ctr.c | 84 + .../src/symcipher/aes_big_ctrcbc.c | 142 + .../src/symcipher/aes_big_dec.c | 254 + .../src/symcipher/aes_big_enc.c | 157 + .../src/symcipher/aes_common.c | 112 + lib/bearssl-esp8266/src/symcipher/aes_ct.c | 328 ++ lib/bearssl-esp8266/src/symcipher/aes_ct64.c | 398 ++ .../src/symcipher/aes_ct64_cbcdec.c | 104 + .../src/symcipher/aes_ct64_cbcenc.c | 81 + .../src/symcipher/aes_ct64_ctr.c | 114 + .../src/symcipher/aes_ct64_ctrcbc.c | 433 ++ .../src/symcipher/aes_ct64_dec.c | 159 + .../src/symcipher/aes_ct64_enc.c | 115 + .../src/symcipher/aes_ct_cbcdec.c | 111 + .../src/symcipher/aes_ct_cbcenc.c | 91 + .../src/symcipher/aes_ct_ctr.c | 116 + .../src/symcipher/aes_ct_ctrcbc.c | 422 ++ .../src/symcipher/aes_ct_dec.c | 170 + .../src/symcipher/aes_ct_enc.c | 112 + .../src/symcipher/aes_small_cbcdec.c | 69 + .../src/symcipher/aes_small_cbcenc.c | 67 + .../src/symcipher/aes_small_ctr.c | 84 + .../src/symcipher/aes_small_ctrcbc.c | 142 + .../src/symcipher/aes_small_dec.c | 176 + .../src/symcipher/aes_small_enc.c | 129 + .../src/symcipher/chacha20_ct.c | 106 + .../src/symcipher/chacha20_sse2.c | 237 + lib/bearssl-esp8266/src/symcipher/des_ct.c | 411 ++ .../src/symcipher/des_ct_cbcdec.c | 87 + .../src/symcipher/des_ct_cbcenc.c | 69 + .../src/symcipher/des_support.c | 166 + lib/bearssl-esp8266/src/symcipher/des_tab.c | 310 ++ .../src/symcipher/des_tab_cbcdec.c | 85 + .../src/symcipher/des_tab_cbcenc.c | 67 + .../src/symcipher/poly1305_ctmul.c | 260 + .../src/symcipher/poly1305_ctmul32.c | 297 ++ .../src/symcipher/poly1305_ctmulq.c | 475 ++ .../src/symcipher/poly1305_i15.c | 221 + lib/bearssl-esp8266/src/t_bearssl.h | 170 + lib/bearssl-esp8266/src/t_bearssl_aead.h | 1059 ++++ lib/bearssl-esp8266/src/t_bearssl_block.h | 2618 ++++++++++ lib/bearssl-esp8266/src/t_bearssl_ec.h | 967 ++++ lib/bearssl-esp8266/src/t_bearssl_hash.h | 1346 +++++ lib/bearssl-esp8266/src/t_bearssl_hmac.h | 241 + lib/bearssl-esp8266/src/t_bearssl_kdf.h | 284 ++ lib/bearssl-esp8266/src/t_bearssl_pem.h | 294 ++ lib/bearssl-esp8266/src/t_bearssl_prf.h | 150 + lib/bearssl-esp8266/src/t_bearssl_rand.h | 397 ++ lib/bearssl-esp8266/src/t_bearssl_rsa.h | 1655 +++++++ lib/bearssl-esp8266/src/t_bearssl_ssl.h | 4308 +++++++++++++++++ .../src/t_bearssl_tasmota_config.h | 30 + lib/bearssl-esp8266/src/t_bearssl_x509.h | 1592 ++++++ lib/bearssl-esp8266/src/t_config.h | 238 + lib/bearssl-esp8266/src/t_inner.h | 2590 ++++++++++ lib/bearssl-esp8266/src/x509/asn1enc.c | 93 + .../src/x509/encode_ec_pk8der.c | 110 + .../src/x509/encode_ec_rawder.c | 161 + .../src/x509/encode_rsa_pk8der.c | 97 + .../src/x509/encode_rsa_rawder.c | 96 + lib/bearssl-esp8266/src/x509/pkey_decoder.c | 587 +++ lib/bearssl-esp8266/src/x509/skey_decoder.c | 654 +++ lib/bearssl-esp8266/src/x509/x509_decoder.c | 790 +++ lib/bearssl-esp8266/src/x509/x509_knownkey.c | 105 + lib/bearssl-esp8266/src/x509/x509_minimal.c | 1776 +++++++ .../src/x509/x509_minimal_full.c | 59 + lib/vl53l0x-arduino-1.02/.travis.yml | 24 + lib/vl53l0x-arduino-1.02/LICENSE.txt | 71 + lib/vl53l0x-arduino-1.02/README.md | 164 + lib/vl53l0x-arduino-1.02/VL53L0X.cpp | 1036 ++++ lib/vl53l0x-arduino-1.02/VL53L0X.h | 176 + .../examples/Continuous/Continuous.ino | 33 + .../examples/Single/Single.ino | 65 + lib/vl53l0x-arduino-1.02/keywords.txt | 90 + lib/vl53l0x-arduino-1.02/library.properties | 9 + platformio.ini | 30 +- scripter.md | 728 +++ sonoff/Parsing.cpp | 6 +- sonoff/StackThunk_light.cpp | 144 + sonoff/StackThunk_light.h | 93 + sonoff/WiFiClientSecureLightBearSSL.cpp | 911 ++++ sonoff/WiFiClientSecureLightBearSSL.h | 221 + sonoff/_changelog.ino | 127 +- sonoff/core_esp8266_wiring_pwm.c | 5 + sonoff/i18n.h | 52 +- sonoff/language/bg-BG.h | 44 +- sonoff/language/cs-CZ.h | 41 +- sonoff/language/de-DE.h | 23 +- sonoff/language/el-GR.h | 93 +- sonoff/language/en-GB.h | 17 +- sonoff/language/{es-AR.h => es-ES.h} | 45 +- sonoff/language/fr-FR.h | 25 +- sonoff/language/he-HE.h | 17 +- sonoff/language/hu-HU.h | 17 +- sonoff/language/it-IT.h | 17 +- sonoff/language/ko-KO.h | 17 +- sonoff/language/nl-NL.h | 31 +- sonoff/language/pl-PL.h | 17 +- sonoff/language/pt-BR.h | 17 +- sonoff/language/pt-PT.h | 17 +- sonoff/language/ru-RU.h | 17 +- sonoff/language/sk-SK.h | 17 +- sonoff/language/sv-SE.h | 17 +- sonoff/language/tr-TR.h | 17 +- sonoff/language/uk-UK.h | 17 +- sonoff/language/zh-CN.h | 17 +- sonoff/language/zh-TW.h | 17 +- sonoff/my_user_config.h | 73 +- sonoff/settings.h | 41 +- sonoff/settings.ino | 249 +- sonoff/sonoff.h | 197 +- sonoff/sonoff.ino | 821 ++-- sonoff/sonoff_aws_iot.cpp | 152 + sonoff/sonoff_ca.ino | 159 + sonoff/sonoff_letsencrypt.h | 102 - sonoff/sonoff_post.h | 76 +- sonoff/sonoff_template.h | 191 +- sonoff/sonoff_version.h | 7 +- sonoff/support.ino | 363 +- sonoff/support_button.ino | 95 +- sonoff/support_features.ino | 61 +- sonoff/support_float.ino | 373 ++ sonoff/support_rotary.ino | 11 +- sonoff/support_rtc.ino | 24 +- sonoff/support_switch.ino | 8 +- sonoff/support_udp.ino | 136 + sonoff/support_wifi.ino | 23 +- sonoff/xdrv_01_webserver.ino | 651 ++- sonoff/xdrv_02_mqtt.ino | 415 +- sonoff/xdrv_03_energy.ino | 201 +- sonoff/xdrv_04_light.ino | 1626 +++++-- sonoff/xdrv_05_irremote.ino | 473 +- sonoff/xdrv_06_snfbridge.ino | 54 +- sonoff/xdrv_07_domoticz.ino | 60 +- sonoff/xdrv_08_serial_bridge.ino | 30 +- sonoff/xdrv_09_timers.ino | 232 +- sonoff/xdrv_10_rules.ino | 197 +- sonoff/xdrv_10_scripter.ino | 3065 ++++++++++++ sonoff/xdrv_11_knx.ino | 77 +- sonoff/xdrv_12_home_assistant.ino | 148 +- sonoff/xdrv_13_display.ino | 124 +- sonoff/xdrv_14_mp3.ino | 6 +- sonoff/xdrv_15_pca9685.ino | 30 +- sonoff/xdrv_16_tuyadimmer.ino | 167 +- sonoff/xdrv_17_rcswitch.ino | 12 +- sonoff/xdrv_18_armtronix_dimmers.ino | 10 +- sonoff/xdrv_19_ps16dz_dimmer.ino | 288 +- sonoff/{xplg_wemohue.ino => xdrv_20_hue.ino} | 662 +-- sonoff/xdrv_21_wemo.ino | 271 ++ sonoff/xdrv_99_debug.ino | 36 +- sonoff/xdrv_interface.ino | 4 +- sonoff/xdsp_01_lcd.ino | 6 +- sonoff/xdsp_02_ssd1306.ino | 4 +- sonoff/xdsp_03_matrix.ino | 26 +- sonoff/xdsp_04_ili9341.ino | 5 +- sonoff/xdsp_05_epaper_29.ino | 4 +- sonoff/xdsp_interface.ino | 2 +- sonoff/xnrg_01_hlw8012.ino | 165 +- sonoff/xnrg_02_cse7766.ino | 60 +- sonoff/xnrg_03_pzem004t.ino | 7 +- sonoff/xnrg_04_mcp39f501.ino | 36 +- sonoff/xnrg_05_pzem_ac.ino | 56 +- sonoff/xnrg_06_pzem_dc.ino | 6 +- sonoff/xnrg_07_ade7953.ino | 257 + sonoff/xnrg_interface.ino | 2 +- sonoff/xplg_ws2812.ino | 24 +- sonoff/xsns_01_counter.ino | 30 +- sonoff/xsns_02_analog.ino | 211 +- sonoff/xsns_04_snfsc.ino | 20 +- sonoff/xsns_05_ds18b20.ino | 12 +- sonoff/xsns_05_ds18x20.ino | 34 +- sonoff/xsns_05_ds18x20_legacy.ino | 28 +- sonoff/xsns_06_dht.ino | 23 +- sonoff/xsns_07_sht1x.ino | 15 +- sonoff/xsns_08_htu21.ino | 13 +- sonoff/xsns_09_bmp.ino | 39 +- sonoff/xsns_10_bh1750.ino | 8 +- sonoff/xsns_11_veml6070.ino | 28 +- sonoff/xsns_12_ads1115.ino | 25 +- sonoff/xsns_12_ads1115_i2cdev.ino | 17 +- sonoff/xsns_13_ina219.ino | 14 +- sonoff/xsns_14_sht3x.ino | 21 +- sonoff/xsns_15_mhz19.ino | 34 +- sonoff/xsns_16_tsl2561.ino | 10 +- sonoff/xsns_17_senseair.ino | 16 +- sonoff/xsns_18_pms5003.ino | 12 +- sonoff/xsns_19_mgs.ino | 36 +- sonoff/xsns_20_novasds.ino | 40 +- sonoff/xsns_21_sgp30.ino | 114 +- sonoff/xsns_22_sr04.ino | 10 +- sonoff/xsns_23_sdm120.ino | 77 +- sonoff/xsns_24_si1145.ino | 10 +- sonoff/xsns_25_sdm630.ino | 25 +- sonoff/xsns_26_lm75ad.ino | 8 +- sonoff/xsns_27_apds9960.ino | 30 +- sonoff/xsns_28_tm1638.ino | 16 +- sonoff/xsns_29_mcp230xx.ino | 103 +- sonoff/xsns_30_mpr121.ino | 26 +- sonoff/xsns_31_ccs811.ino | 10 +- sonoff/xsns_32_mpu6050.ino | 30 +- sonoff/xsns_34_hx711.ino | 98 +- sonoff/xsns_35_tx20.ino | 16 +- sonoff/xsns_36_mgc3130.ino | 14 +- sonoff/xsns_37_rfsensor.ino | 67 +- sonoff/xsns_38_az7798.ino | 14 +- sonoff/xsns_39_max31855.ino | 14 +- sonoff/xsns_40_pn532.ino | 30 +- sonoff/xsns_41_max44009.ino | 11 +- sonoff/xsns_42_scd30.ino | 21 +- sonoff/xsns_43_hre.ino | 288 ++ sonoff/xsns_44_sps30.ino | 316 ++ sonoff/xsns_45_vl53l0x.ino | 162 + sonoff/xsns_46_MLX90614.ino | 143 + sonoff/xsns_interface.ino | 2 +- sonoff/zzzz_debug.ino | 6 +- tools/decode-config.html | 174 +- tools/decode-config.md | 4 +- tools/decode-config.py | 832 ++-- tools/decode-status.py | 18 +- tools/logo/TASMOTA_FullLogo_Vector.svg | 1 + tools/logo/TASMOTA_Symbol_Vector.svg | 1 + 677 files changed, 95371 insertions(+), 10573 deletions(-) rename arduino/{version 2.5.0 => version 2.5.2}/boards.txt (96%) rename arduino/{version 2.5.0 => version 2.5.2}/platform.txt (50%) create mode 100644 include/dummy.txt rename lib/{Adafruit_SGP30-1.0.0.13 => Adafruit_SGP30-1.0.3}/.github/ISSUE_TEMPLATE.md (100%) rename lib/{Adafruit_SGP30-1.0.0.13 => Adafruit_SGP30-1.0.3}/.github/PULL_REQUEST_TEMPLATE.md (100%) rename lib/{Adafruit_SGP30-1.0.0.13 => Adafruit_SGP30-1.0.3}/.gitignore (100%) rename lib/{Adafruit_SGP30-1.0.0.13 => Adafruit_SGP30-1.0.3}/.travis.yml (100%) rename lib/{Adafruit_SGP30-1.0.0.13 => Adafruit_SGP30-1.0.3}/Adafruit_SGP30.cpp (83%) rename lib/{Adafruit_SGP30-1.0.0.13 => Adafruit_SGP30-1.0.3}/Adafruit_SGP30.h (97%) rename lib/{Adafruit_SGP30-1.0.0.13 => Adafruit_SGP30-1.0.3}/README.md (100%) rename lib/{Adafruit_SGP30-1.0.0.13 => Adafruit_SGP30-1.0.3}/examples/sgp30test/sgp30test.ino (58%) rename lib/{Adafruit_SGP30-1.0.0.13 => Adafruit_SGP30-1.0.3}/library.properties (95%) rename lib/{Adafruit_SGP30-1.0.0.13 => Adafruit_SGP30-1.0.3}/license.txt (100%) delete mode 100644 lib/IRremoteESP8266-2.5.2.03/examples/IRMQTTServer/IRMQTTServer.ino delete mode 100644 lib/IRremoteESP8266-2.5.2.03/examples/IRrecvDump/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.5.2.03/examples/IRrecvDumpV2/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.5.2.03/examples/IRsendDemo/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.5.2.03/examples/IRsendProntoDemo/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.5.2.03/examples/JVCPanasonicSendDemo/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.5.2.03/examples/LGACSend/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.5.2.03/examples/TurnOnArgoAC/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.5.2.03/examples/TurnOnDaikinAC/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.5.2.03/examples/TurnOnFujitsuAC/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.5.2.03/examples/TurnOnKelvinatorAC/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.5.2.03/examples/TurnOnMitsubishiAC/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.5.2.03/examples/TurnOnToshibaAC/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.5.2.03/examples/TurnOnTrotecAC/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.5.2.03/src/IRutils.cpp delete mode 100644 lib/IRremoteESP8266-2.5.2.03/src/ir_Daikin.cpp delete mode 100644 lib/IRremoteESP8266-2.5.2.03/src/ir_Daikin.h delete mode 100644 lib/IRremoteESP8266-2.5.2.03/src/ir_Trotec.cpp delete mode 100644 lib/IRremoteESP8266-2.5.2.03/src/ir_Whirlpool.cpp delete mode 100644 lib/IRremoteESP8266-2.5.2.03/test/IRsend_test.cpp delete mode 100644 lib/IRremoteESP8266-2.5.2.03/test/ir_Daikin_test.cpp delete mode 100644 lib/IRremoteESP8266-2.5.2.03/test/ir_Whirlpool_test.cpp rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/.github/CONTRIBUTING.md (100%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/.github/Contributors.md (87%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/.github/issue_template.md (100%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/.gitignore (100%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/.gitmodules (100%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/.style.yapf (100%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/.travis.yml (81%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/CPPLINT.cfg (100%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/LICENSE.txt (100%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/README.md (95%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/ReleaseNotes.md (72%) create mode 100644 lib/IRremoteESP8266-2.6.0/examples/ControlSamsungAC/ControlSamsungAC.ino rename lib/{IRremoteESP8266-2.5.2.03/examples/IRGCSendDemo => IRremoteESP8266-2.6.0/examples/ControlSamsungAC}/platformio.ini (83%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/examples/IRGCSendDemo/IRGCSendDemo.ino (100%) rename lib/{IRremoteESP8266-2.5.2.03/examples/IRGCTCPServer => IRremoteESP8266-2.6.0/examples/IRGCSendDemo}/platformio.ini (83%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/examples/IRGCTCPServer/IRGCTCPServer.ino (100%) rename lib/{IRremoteESP8266-2.5.2.03/examples/IRrecvDemo => IRremoteESP8266-2.6.0/examples/IRGCTCPServer}/platformio.ini (83%) create mode 100644 lib/IRremoteESP8266-2.6.0/examples/IRMQTTServer/IRMQTTServer.h create mode 100644 lib/IRremoteESP8266-2.6.0/examples/IRMQTTServer/IRMQTTServer.ino rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/examples/IRMQTTServer/platformio.ini (55%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/examples/IRServer/IRServer.ino (100%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/examples/IRServer/platformio.ini (83%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/examples/IRrecvDemo/IRrecvDemo.ino (100%) create mode 100644 lib/IRremoteESP8266-2.6.0/examples/IRrecvDemo/platformio.ini rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/examples/IRrecvDump/IRrecvDump.ino (100%) create mode 100644 lib/IRremoteESP8266-2.6.0/examples/IRrecvDump/platformio.ini rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/examples/IRrecvDumpV2/IRrecvDumpV2.ino (84%) create mode 100644 lib/IRremoteESP8266-2.6.0/examples/IRrecvDumpV2/platformio.ini rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/examples/IRsendDemo/IRsendDemo.ino (85%) create mode 100644 lib/IRremoteESP8266-2.6.0/examples/IRsendDemo/platformio.ini rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/examples/IRsendProntoDemo/IRsendProntoDemo.ino (100%) create mode 100644 lib/IRremoteESP8266-2.6.0/examples/IRsendProntoDemo/platformio.ini rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/examples/JVCPanasonicSendDemo/JVCPanasonicSendDemo.ino (100%) create mode 100644 lib/IRremoteESP8266-2.6.0/examples/JVCPanasonicSendDemo/platformio.ini rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/examples/LGACSend/LGACSend.ino (100%) create mode 100644 lib/IRremoteESP8266-2.6.0/examples/LGACSend/platformio.ini rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/examples/TurnOnArgoAC/TurnOnArgoAC.ino (100%) create mode 100644 lib/IRremoteESP8266-2.6.0/examples/TurnOnArgoAC/platformio.ini rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/examples/TurnOnDaikinAC/TurnOnDaikinAC.ino (100%) create mode 100644 lib/IRremoteESP8266-2.6.0/examples/TurnOnDaikinAC/platformio.ini rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/examples/TurnOnFujitsuAC/TurnOnFujitsuAC.ino (100%) create mode 100644 lib/IRremoteESP8266-2.6.0/examples/TurnOnFujitsuAC/platformio.ini rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/examples/TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino (100%) create mode 100644 lib/IRremoteESP8266-2.6.0/examples/TurnOnKelvinatorAC/platformio.ini rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/examples/TurnOnMitsubishiAC/TurnOnMitsubishiAC.ino (100%) create mode 100644 lib/IRremoteESP8266-2.6.0/examples/TurnOnMitsubishiAC/platformio.ini create mode 100644 lib/IRremoteESP8266-2.6.0/examples/TurnOnMitsubishiHeavyAc/TurnOnMitsubishiHeavyAc.ino create mode 100644 lib/IRremoteESP8266-2.6.0/examples/TurnOnMitsubishiHeavyAc/platformio.ini create mode 100644 lib/IRremoteESP8266-2.6.0/examples/TurnOnPanasonicAC/TurnOnPanasonicAC.ino create mode 100644 lib/IRremoteESP8266-2.6.0/examples/TurnOnPanasonicAC/platformio.ini rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/examples/TurnOnToshibaAC/TurnOnToshibaAC.ino (100%) create mode 100644 lib/IRremoteESP8266-2.6.0/examples/TurnOnToshibaAC/platformio.ini rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/examples/TurnOnTrotecAC/TurnOnTrotecAC.ino (100%) create mode 100644 lib/IRremoteESP8266-2.6.0/examples/TurnOnTrotecAC/platformio.ini rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/keywords.txt (74%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/library.json (97%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/library.properties (96%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/platformio.ini (83%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/pylintrc (100%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/src/CPPLINT.cfg (100%) create mode 100644 lib/IRremoteESP8266-2.6.0/src/IRac.cpp create mode 100644 lib/IRremoteESP8266-2.6.0/src/IRac.h rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/src/IRrecv.cpp (95%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/src/IRrecv.h (90%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/src/IRremoteESP8266.h (82%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/src/IRsend.cpp (92%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/src/IRsend.h (77%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/src/IRtimer.cpp (53%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/src/IRtimer.h (64%) create mode 100644 lib/IRremoteESP8266-2.6.0/src/IRutils.cpp rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/src/IRutils.h (74%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/src/ir_Aiwa.cpp (100%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/src/ir_Argo.cpp (84%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/src/ir_Argo.h (91%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/src/ir_Carrier.cpp (100%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/src/ir_Coolix.cpp (72%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/src/ir_Coolix.h (82%) create mode 100644 lib/IRremoteESP8266-2.6.0/src/ir_Daikin.cpp create mode 100644 lib/IRremoteESP8266-2.6.0/src/ir_Daikin.h rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/src/ir_Denon.cpp (100%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/src/ir_Dish.cpp (100%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/src/ir_Electra.cpp (87%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/src/ir_Fujitsu.cpp (88%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/src/ir_Fujitsu.h (93%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/src/ir_GICable.cpp (100%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/src/ir_GlobalCache.cpp (100%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/src/ir_Gree.cpp (82%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/src/ir_Gree.h (89%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/src/ir_Haier.cpp (75%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/src/ir_Haier.h (93%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/src/ir_Hitachi.cpp (86%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/src/ir_Hitachi.h (87%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/src/ir_JVC.cpp (100%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/src/ir_Kelvinator.cpp (91%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/src/ir_Kelvinator.h (96%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/src/ir_LG.cpp (98%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/src/ir_LG.h (100%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/src/ir_Lasertag.cpp (98%) create mode 100644 lib/IRremoteESP8266-2.6.0/src/ir_Lego.cpp rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/src/ir_Lutron.cpp (100%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/src/ir_MWM.cpp (98%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/src/ir_Magiquest.cpp (100%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/src/ir_Magiquest.h (86%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/src/ir_Midea.cpp (87%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/src/ir_Midea.h (93%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/src/ir_Mitsubishi.cpp (90%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/src/ir_Mitsubishi.h (91%) create mode 100644 lib/IRremoteESP8266-2.6.0/src/ir_MitsubishiHeavy.cpp create mode 100644 lib/IRremoteESP8266-2.6.0/src/ir_MitsubishiHeavy.h rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/src/ir_NEC.cpp (100%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/src/ir_NEC.h (100%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/src/ir_Nikai.cpp (100%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/src/ir_Panasonic.cpp (83%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/src/ir_Panasonic.h (91%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/src/ir_Pioneer.cpp (100%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/src/ir_Pronto.cpp (100%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/src/ir_RC5_RC6.cpp (100%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/src/ir_RCMM.cpp (100%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/src/ir_Samsung.cpp (66%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/src/ir_Samsung.h (84%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/src/ir_Sanyo.cpp (100%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/src/ir_Sharp.cpp (100%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/src/ir_Sherwood.cpp (100%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/src/ir_Sony.cpp (100%) create mode 100644 lib/IRremoteESP8266-2.6.0/src/ir_Tcl.cpp create mode 100644 lib/IRremoteESP8266-2.6.0/src/ir_Tcl.h create mode 100644 lib/IRremoteESP8266-2.6.0/src/ir_Teco.cpp create mode 100644 lib/IRremoteESP8266-2.6.0/src/ir_Teco.h rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/src/ir_Toshiba.cpp (86%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/src/ir_Toshiba.h (90%) create mode 100644 lib/IRremoteESP8266-2.6.0/src/ir_Trotec.cpp rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/src/ir_Trotec.h (68%) create mode 100644 lib/IRremoteESP8266-2.6.0/src/ir_Vestel.cpp create mode 100644 lib/IRremoteESP8266-2.6.0/src/ir_Vestel.h create mode 100644 lib/IRremoteESP8266-2.6.0/src/ir_Whirlpool.cpp create mode 100644 lib/IRremoteESP8266-2.6.0/src/ir_Whirlpool.h rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/src/ir_Whynter.cpp (100%) create mode 100644 lib/IRremoteESP8266-2.6.0/test/IRac_test.cpp rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/test/IRrecv_test.cpp (100%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/test/IRrecv_test.h (100%) create mode 100644 lib/IRremoteESP8266-2.6.0/test/IRsend_test.cpp rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/test/IRsend_test.h (82%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/test/IRutils_test.cpp (85%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/test/Makefile (71%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/test/ir_Aiwa_test.cpp (99%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/test/ir_Carrier_test.cpp (99%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/test/ir_Coolix_test.cpp (70%) create mode 100644 lib/IRremoteESP8266-2.6.0/test/ir_Daikin_test.cpp rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/test/ir_Denon_test.cpp (98%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/test/ir_Dish_test.cpp (98%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/test/ir_Electra_test.cpp (84%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/test/ir_Fujitsu_test.cpp (99%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/test/ir_GICable_test.cpp (98%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/test/ir_GlobalCache_test.cpp (98%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/test/ir_Gree_test.cpp (98%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/test/ir_Haier_test.cpp (90%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/test/ir_Hitachi_test.cpp (99%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/test/ir_JVC_test.cpp (99%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/test/ir_Kelvinator_test.cpp (99%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/test/ir_LG_test.cpp (88%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/test/ir_Lasertag_test.cpp (98%) create mode 100644 lib/IRremoteESP8266-2.6.0/test/ir_Lego_test.cpp rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/test/ir_Lutron_test.cpp (95%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/test/ir_MWM_test.cpp (99%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/test/ir_Magiquest_test.cpp (99%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/test/ir_Midea_test.cpp (99%) create mode 100644 lib/IRremoteESP8266-2.6.0/test/ir_MitsubishiHeavy_test.cpp rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/test/ir_Mitsubishi_test.cpp (99%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/test/ir_NEC_test.cpp (98%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/test/ir_Nikai_test.cpp (99%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/test/ir_Panasonic_test.cpp (99%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/test/ir_Pioneer_test.cpp (99%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/test/ir_Pronto_test.cpp (96%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/test/ir_RC5_RC6_test.cpp (98%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/test/ir_RCMM_test.cpp (98%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/test/ir_Samsung_test.cpp (77%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/test/ir_Sanyo_test.cpp (99%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/test/ir_Sharp_test.cpp (98%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/test/ir_Sherwood_test.cpp (97%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/test/ir_Sony_test.cpp (99%) create mode 100644 lib/IRremoteESP8266-2.6.0/test/ir_Tcl_test.cpp create mode 100644 lib/IRremoteESP8266-2.6.0/test/ir_Teco_test.cpp rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/test/ir_Toshiba_test.cpp (99%) create mode 100644 lib/IRremoteESP8266-2.6.0/test/ir_Vestel_test.cpp create mode 100644 lib/IRremoteESP8266-2.6.0/test/ir_Whirlpool_test.cpp rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/test/ir_Whynter_test.cpp (98%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/tools/Makefile (76%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/tools/RawToGlobalCache.sh (100%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/tools/auto_analyse_raw_data.py (98%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/tools/auto_analyse_raw_data_test.py (100%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/tools/gc_decode.cpp (100%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/tools/mkkeywords (100%) rename lib/{IRremoteESP8266-2.5.2.03 => IRremoteESP8266-2.6.0}/tools/mode2_decode.cpp (100%) create mode 100644 lib/Joba_Tsl2561-2.0.10/.gitignore create mode 100644 lib/Joba_Tsl2561-2.0.10/.hgignore create mode 100644 lib/Joba_Tsl2561-2.0.10/.travis.yml rename lib/{Joba_Tsl2561-2.0.7 => Joba_Tsl2561-2.0.10}/COPYING (100%) rename lib/{Joba_Tsl2561-2.0.7 => Joba_Tsl2561-2.0.10}/COPYING.LESSER (100%) rename lib/{Joba_Tsl2561-2.0.7 => Joba_Tsl2561-2.0.10}/README (87%) rename lib/{Joba_Tsl2561-2.0.7 => Joba_Tsl2561-2.0.10}/examples/Autogain/Autogain.ino (96%) rename lib/{Joba_Tsl2561-2.0.7 => Joba_Tsl2561-2.0.10}/examples/Simple/Simple.ino (88%) rename lib/{Joba_Tsl2561-2.0.7 => Joba_Tsl2561-2.0.10}/examples/Testing/Testing.ino (97%) rename lib/{Joba_Tsl2561-2.0.7 => Joba_Tsl2561-2.0.10}/examples/Utility/Utility.ino (93%) rename lib/{Joba_Tsl2561-2.0.7 => Joba_Tsl2561-2.0.10/examples}/platformio.ini (67%) rename lib/{Joba_Tsl2561-2.0.7 => Joba_Tsl2561-2.0.10}/examples/platformio.sh (100%) rename lib/{Joba_Tsl2561-2.0.7 => Joba_Tsl2561-2.0.10}/lib/readme.txt (100%) rename lib/{Joba_Tsl2561-2.0.7 => Joba_Tsl2561-2.0.10}/library.json (62%) rename lib/{Joba_Tsl2561-2.0.7 => Joba_Tsl2561-2.0.10}/library.properties (67%) rename lib/{Joba_Tsl2561-2.0.7/examples => Joba_Tsl2561-2.0.10}/platformio.ini (75%) rename lib/{Joba_Tsl2561-2.0.7 => Joba_Tsl2561-2.0.10}/src/Tsl2561.cpp (100%) rename lib/{Joba_Tsl2561-2.0.7 => Joba_Tsl2561-2.0.10}/src/Tsl2561.h (94%) rename lib/{Joba_Tsl2561-2.0.7 => Joba_Tsl2561-2.0.10}/src/Tsl2561Util.cpp (83%) rename lib/{Joba_Tsl2561-2.0.7 => Joba_Tsl2561-2.0.10}/src/Tsl2561Util.h (100%) delete mode 100644 lib/PubSubClient-EspEasy-2.6.09/tests/testcases/mqtt_basic.py delete mode 100644 lib/PubSubClient-EspEasy-2.6.09/tests/testcases/mqtt_publish_in_callback.py delete mode 100644 lib/PubSubClient-EspEasy-2.6.09/tests/testsuite.py rename lib/{PubSubClient-EspEasy-2.6.09 => PubSubClient-EspEasy-2.7.12}/.gitignore (100%) rename lib/{PubSubClient-EspEasy-2.6.09 => PubSubClient-EspEasy-2.7.12}/.travis.yml (100%) rename lib/{PubSubClient-EspEasy-2.6.09 => PubSubClient-EspEasy-2.7.12}/CHANGES.txt (86%) rename lib/{PubSubClient-EspEasy-2.6.09 => PubSubClient-EspEasy-2.7.12}/LICENSE.txt (100%) rename lib/{PubSubClient-EspEasy-2.6.09 => PubSubClient-EspEasy-2.7.12}/README.md (95%) rename lib/{PubSubClient-EspEasy-2.6.09 => PubSubClient-EspEasy-2.7.12}/examples/mqtt_auth/mqtt_auth.ino (100%) rename lib/{PubSubClient-EspEasy-2.6.09 => PubSubClient-EspEasy-2.7.12}/examples/mqtt_basic/mqtt_basic.ino (100%) rename lib/{PubSubClient-EspEasy-2.6.09 => PubSubClient-EspEasy-2.7.12}/examples/mqtt_esp8266/mqtt_esp8266.ino (92%) create mode 100644 lib/PubSubClient-EspEasy-2.7.12/examples/mqtt_large_message/mqtt_large_message.ino rename lib/{PubSubClient-EspEasy-2.6.09 => PubSubClient-EspEasy-2.7.12}/examples/mqtt_publish_in_callback/mqtt_publish_in_callback.ino (100%) rename lib/{PubSubClient-EspEasy-2.6.09 => PubSubClient-EspEasy-2.7.12}/examples/mqtt_reconnect_nonblocking/mqtt_reconnect_nonblocking.ino (100%) rename lib/{PubSubClient-EspEasy-2.6.09 => PubSubClient-EspEasy-2.7.12}/examples/mqtt_stream/mqtt_stream.ino (100%) rename lib/{PubSubClient-EspEasy-2.6.09 => PubSubClient-EspEasy-2.7.12}/keywords.txt (91%) rename lib/{PubSubClient-EspEasy-2.6.09 => PubSubClient-EspEasy-2.7.12}/library.json (97%) rename lib/{PubSubClient-EspEasy-2.6.09 => PubSubClient-EspEasy-2.7.12}/library.properties (98%) rename lib/{PubSubClient-EspEasy-2.6.09 => PubSubClient-EspEasy-2.7.12}/src/PubSubClient.cpp (79%) rename lib/{PubSubClient-EspEasy-2.6.09 => PubSubClient-EspEasy-2.7.12}/src/PubSubClient.h (75%) rename lib/{PubSubClient-EspEasy-2.6.09 => PubSubClient-EspEasy-2.7.12}/tests/.gitignore (100%) rename lib/{PubSubClient-EspEasy-2.6.09 => PubSubClient-EspEasy-2.7.12}/tests/Makefile (100%) rename lib/{PubSubClient-EspEasy-2.6.09 => PubSubClient-EspEasy-2.7.12}/tests/README.md (100%) rename lib/{PubSubClient-EspEasy-2.6.09 => PubSubClient-EspEasy-2.7.12}/tests/src/connect_spec.cpp (83%) rename lib/{PubSubClient-EspEasy-2.6.09 => PubSubClient-EspEasy-2.7.12}/tests/src/keepalive_spec.cpp (100%) rename lib/{PubSubClient-EspEasy-2.6.09 => PubSubClient-EspEasy-2.7.12}/tests/src/lib/Arduino.h (90%) rename lib/{PubSubClient-EspEasy-2.6.09 => PubSubClient-EspEasy-2.7.12}/tests/src/lib/BDDTest.cpp (100%) rename lib/{PubSubClient-EspEasy-2.6.09 => PubSubClient-EspEasy-2.7.12}/tests/src/lib/BDDTest.h (100%) rename lib/{PubSubClient-EspEasy-2.6.09 => PubSubClient-EspEasy-2.7.12}/tests/src/lib/Buffer.cpp (86%) rename lib/{PubSubClient-EspEasy-2.6.09 => PubSubClient-EspEasy-2.7.12}/tests/src/lib/Buffer.h (100%) rename lib/{PubSubClient-EspEasy-2.6.09 => PubSubClient-EspEasy-2.7.12}/tests/src/lib/Client.h (100%) rename lib/{PubSubClient-EspEasy-2.6.09 => PubSubClient-EspEasy-2.7.12}/tests/src/lib/IPAddress.cpp (100%) rename lib/{PubSubClient-EspEasy-2.6.09 => PubSubClient-EspEasy-2.7.12}/tests/src/lib/IPAddress.h (100%) create mode 100644 lib/PubSubClient-EspEasy-2.7.12/tests/src/lib/Print.h rename lib/{PubSubClient-EspEasy-2.6.09 => PubSubClient-EspEasy-2.7.12}/tests/src/lib/ShimClient.cpp (100%) rename lib/{PubSubClient-EspEasy-2.6.09 => PubSubClient-EspEasy-2.7.12}/tests/src/lib/ShimClient.h (100%) rename lib/{PubSubClient-EspEasy-2.6.09 => PubSubClient-EspEasy-2.7.12}/tests/src/lib/Stream.cpp (100%) rename lib/{PubSubClient-EspEasy-2.6.09 => PubSubClient-EspEasy-2.7.12}/tests/src/lib/Stream.h (100%) rename lib/{PubSubClient-EspEasy-2.6.09 => PubSubClient-EspEasy-2.7.12}/tests/src/lib/trace.h (100%) rename lib/{PubSubClient-EspEasy-2.6.09 => PubSubClient-EspEasy-2.7.12}/tests/src/publish_spec.cpp (100%) rename lib/{PubSubClient-EspEasy-2.6.09 => PubSubClient-EspEasy-2.7.12}/tests/src/receive_spec.cpp (89%) rename lib/{PubSubClient-EspEasy-2.6.09 => PubSubClient-EspEasy-2.7.12}/tests/src/subscribe_spec.cpp (100%) rename lib/{PubSubClient-EspEasy-2.6.09 => PubSubClient-EspEasy-2.7.12}/tests/testcases/__init__.py (100%) create mode 100644 lib/PubSubClient-EspEasy-2.7.12/tests/testcases/mqtt_basic.py create mode 100644 lib/PubSubClient-EspEasy-2.7.12/tests/testcases/mqtt_publish_in_callback.py rename lib/{PubSubClient-EspEasy-2.6.09 => PubSubClient-EspEasy-2.7.12}/tests/testcases/settings.py (100%) create mode 100644 lib/PubSubClient-EspEasy-2.7.12/tests/testsuite.py rename lib/{TasmotaSerial-2.3.0 => TasmotaSerial-2.3.1}/README.md (100%) rename lib/{TasmotaSerial-2.3.0 => TasmotaSerial-2.3.1}/examples/swsertest/swsertest.ino (100%) rename lib/{TasmotaSerial-2.3.0 => TasmotaSerial-2.3.1}/keywords.txt (100%) rename lib/{TasmotaSerial-2.3.0 => TasmotaSerial-2.3.1}/library.json (100%) rename lib/{TasmotaSerial-2.3.0 => TasmotaSerial-2.3.1}/library.properties (100%) rename lib/{TasmotaSerial-2.3.0 => TasmotaSerial-2.3.1}/src/TasmotaSerial.cpp (97%) rename lib/{TasmotaSerial-2.3.0 => TasmotaSerial-2.3.1}/src/TasmotaSerial.h (100%) create mode 100644 lib/bearssl-esp8266/bearssl_esp8266-customized.txt create mode 100644 lib/bearssl-esp8266/conf/esp8266.mk create mode 100644 lib/bearssl-esp8266/library.properties create mode 100644 lib/bearssl-esp8266/src/aead/ccm.c create mode 100644 lib/bearssl-esp8266/src/aead/eax.c create mode 100644 lib/bearssl-esp8266/src/aead/gcm.c create mode 100644 lib/bearssl-esp8266/src/codec/ccopy.c create mode 100644 lib/bearssl-esp8266/src/codec/dec16be.c create mode 100644 lib/bearssl-esp8266/src/codec/dec16le.c create mode 100644 lib/bearssl-esp8266/src/codec/dec32be.c create mode 100644 lib/bearssl-esp8266/src/codec/dec32le.c create mode 100644 lib/bearssl-esp8266/src/codec/dec64be.c create mode 100644 lib/bearssl-esp8266/src/codec/dec64le.c create mode 100644 lib/bearssl-esp8266/src/codec/enc16be.c create mode 100644 lib/bearssl-esp8266/src/codec/enc16le.c create mode 100644 lib/bearssl-esp8266/src/codec/enc32be.c create mode 100644 lib/bearssl-esp8266/src/codec/enc32le.c create mode 100644 lib/bearssl-esp8266/src/codec/enc64be.c create mode 100644 lib/bearssl-esp8266/src/codec/enc64le.c create mode 100644 lib/bearssl-esp8266/src/codec/pemdec.c create mode 100644 lib/bearssl-esp8266/src/codec/pemenc.c create mode 100644 lib/bearssl-esp8266/src/ec/ec_all_m15.c create mode 100644 lib/bearssl-esp8266/src/ec/ec_c25519_i15.c create mode 100644 lib/bearssl-esp8266/src/ec/ec_curve25519.c create mode 100644 lib/bearssl-esp8266/src/ec/ec_default.c create mode 100644 lib/bearssl-esp8266/src/ec/ec_keygen.c create mode 100644 lib/bearssl-esp8266/src/ec/ec_p256_m15.c create mode 100644 lib/bearssl-esp8266/src/ec/ec_prime_i15.c create mode 100644 lib/bearssl-esp8266/src/ec/ec_pubkey.c create mode 100644 lib/bearssl-esp8266/src/ec/ec_secp256r1.c create mode 100644 lib/bearssl-esp8266/src/ec/ec_secp384r1.c create mode 100644 lib/bearssl-esp8266/src/ec/ec_secp521r1.c create mode 100644 lib/bearssl-esp8266/src/ec/ecdsa_atr.c create mode 100644 lib/bearssl-esp8266/src/ec/ecdsa_default_sign_asn1.c create mode 100644 lib/bearssl-esp8266/src/ec/ecdsa_default_sign_raw.c create mode 100644 lib/bearssl-esp8266/src/ec/ecdsa_default_vrfy_asn1.c create mode 100644 lib/bearssl-esp8266/src/ec/ecdsa_default_vrfy_raw.c create mode 100644 lib/bearssl-esp8266/src/ec/ecdsa_i15_bits.c create mode 100644 lib/bearssl-esp8266/src/ec/ecdsa_i15_sign_asn1.c create mode 100644 lib/bearssl-esp8266/src/ec/ecdsa_i15_sign_raw.c create mode 100644 lib/bearssl-esp8266/src/ec/ecdsa_i15_vrfy_asn1.c create mode 100644 lib/bearssl-esp8266/src/ec/ecdsa_i15_vrfy_raw.c create mode 100644 lib/bearssl-esp8266/src/ec/ecdsa_rta.c create mode 100644 lib/bearssl-esp8266/src/hash/dig_oid.c create mode 100644 lib/bearssl-esp8266/src/hash/dig_size.c create mode 100644 lib/bearssl-esp8266/src/hash/ghash_ctmul.c create mode 100644 lib/bearssl-esp8266/src/hash/ghash_ctmul32.c create mode 100644 lib/bearssl-esp8266/src/hash/ghash_ctmul64.c create mode 100644 lib/bearssl-esp8266/src/hash/ghash_pclmul.c create mode 100644 lib/bearssl-esp8266/src/hash/md5.c create mode 100644 lib/bearssl-esp8266/src/hash/md5sha1.c create mode 100644 lib/bearssl-esp8266/src/hash/mgf1.c create mode 100644 lib/bearssl-esp8266/src/hash/multihash.c create mode 100644 lib/bearssl-esp8266/src/hash/sha1.c create mode 100644 lib/bearssl-esp8266/src/hash/sha2big.c create mode 100644 lib/bearssl-esp8266/src/hash/sha2small.c create mode 100644 lib/bearssl-esp8266/src/int/i15_add.c create mode 100644 lib/bearssl-esp8266/src/int/i15_bitlen.c create mode 100644 lib/bearssl-esp8266/src/int/i15_decmod.c create mode 100644 lib/bearssl-esp8266/src/int/i15_decode.c create mode 100644 lib/bearssl-esp8266/src/int/i15_decred.c create mode 100644 lib/bearssl-esp8266/src/int/i15_encode.c create mode 100644 lib/bearssl-esp8266/src/int/i15_fmont.c create mode 100644 lib/bearssl-esp8266/src/int/i15_iszero.c create mode 100644 lib/bearssl-esp8266/src/int/i15_moddiv.c create mode 100644 lib/bearssl-esp8266/src/int/i15_modpow.c create mode 100644 lib/bearssl-esp8266/src/int/i15_modpow2.c create mode 100644 lib/bearssl-esp8266/src/int/i15_montmul.c create mode 100644 lib/bearssl-esp8266/src/int/i15_mulacc.c create mode 100644 lib/bearssl-esp8266/src/int/i15_muladd.c create mode 100644 lib/bearssl-esp8266/src/int/i15_ninv15.c create mode 100644 lib/bearssl-esp8266/src/int/i15_reduce.c create mode 100644 lib/bearssl-esp8266/src/int/i15_rshift.c create mode 100644 lib/bearssl-esp8266/src/int/i15_sub.c create mode 100644 lib/bearssl-esp8266/src/int/i15_tmont.c create mode 100644 lib/bearssl-esp8266/src/kdf/hkdf.c create mode 100644 lib/bearssl-esp8266/src/kdf/shake.c create mode 100644 lib/bearssl-esp8266/src/mac/hmac.c create mode 100644 lib/bearssl-esp8266/src/mac/hmac_ct.c create mode 100644 lib/bearssl-esp8266/src/pgmspace_bearssl.h create mode 100644 lib/bearssl-esp8266/src/rand/aesctr_drbg.c create mode 100644 lib/bearssl-esp8266/src/rand/hmac_drbg.c create mode 100644 lib/bearssl-esp8266/src/rand/sysrng.c create mode 100644 lib/bearssl-esp8266/src/rsa/rsa_default_keygen.c create mode 100644 lib/bearssl-esp8266/src/rsa/rsa_default_modulus.c create mode 100644 lib/bearssl-esp8266/src/rsa/rsa_default_oaep_decrypt.c create mode 100644 lib/bearssl-esp8266/src/rsa/rsa_default_oaep_encrypt.c create mode 100644 lib/bearssl-esp8266/src/rsa/rsa_default_pkcs1_sign.c create mode 100644 lib/bearssl-esp8266/src/rsa/rsa_default_pkcs1_vrfy.c create mode 100644 lib/bearssl-esp8266/src/rsa/rsa_default_priv.c create mode 100644 lib/bearssl-esp8266/src/rsa/rsa_default_privexp.c create mode 100644 lib/bearssl-esp8266/src/rsa/rsa_default_pss_sign.c create mode 100644 lib/bearssl-esp8266/src/rsa/rsa_default_pss_vrfy.c create mode 100644 lib/bearssl-esp8266/src/rsa/rsa_default_pub.c create mode 100644 lib/bearssl-esp8266/src/rsa/rsa_default_pubexp.c create mode 100644 lib/bearssl-esp8266/src/rsa/rsa_i15_keygen.c create mode 100644 lib/bearssl-esp8266/src/rsa/rsa_i15_modulus.c create mode 100644 lib/bearssl-esp8266/src/rsa/rsa_i15_oaep_decrypt.c create mode 100644 lib/bearssl-esp8266/src/rsa/rsa_i15_oaep_encrypt.c create mode 100644 lib/bearssl-esp8266/src/rsa/rsa_i15_pkcs1_sign.c create mode 100644 lib/bearssl-esp8266/src/rsa/rsa_i15_pkcs1_vrfy.c create mode 100644 lib/bearssl-esp8266/src/rsa/rsa_i15_priv.c create mode 100644 lib/bearssl-esp8266/src/rsa/rsa_i15_privexp.c create mode 100644 lib/bearssl-esp8266/src/rsa/rsa_i15_pss_sign.c create mode 100644 lib/bearssl-esp8266/src/rsa/rsa_i15_pss_vrfy.c create mode 100644 lib/bearssl-esp8266/src/rsa/rsa_i15_pub.c create mode 100644 lib/bearssl-esp8266/src/rsa/rsa_i15_pubexp.c create mode 100644 lib/bearssl-esp8266/src/rsa/rsa_oaep_pad.c create mode 100644 lib/bearssl-esp8266/src/rsa/rsa_oaep_unpad.c create mode 100644 lib/bearssl-esp8266/src/rsa/rsa_pkcs1_sig_pad.c create mode 100644 lib/bearssl-esp8266/src/rsa/rsa_pkcs1_sig_unpad.c create mode 100644 lib/bearssl-esp8266/src/rsa/rsa_pss_sig_pad.c create mode 100644 lib/bearssl-esp8266/src/rsa/rsa_pss_sig_unpad.c create mode 100644 lib/bearssl-esp8266/src/rsa/rsa_ssl_decrypt.c create mode 100644 lib/bearssl-esp8266/src/settings.c create mode 100644 lib/bearssl-esp8266/src/ssl/prf.c create mode 100644 lib/bearssl-esp8266/src/ssl/prf_md5sha1.c create mode 100644 lib/bearssl-esp8266/src/ssl/prf_sha256.c create mode 100644 lib/bearssl-esp8266/src/ssl/prf_sha384.c create mode 100644 lib/bearssl-esp8266/src/ssl/ssl_ccert_single_ec.c create mode 100644 lib/bearssl-esp8266/src/ssl/ssl_ccert_single_rsa.c create mode 100644 lib/bearssl-esp8266/src/ssl/ssl_client.c create mode 100644 lib/bearssl-esp8266/src/ssl/ssl_client_default_rsapub.c create mode 100644 lib/bearssl-esp8266/src/ssl/ssl_client_full.c create mode 100644 lib/bearssl-esp8266/src/ssl/ssl_engine.c create mode 100644 lib/bearssl-esp8266/src/ssl/ssl_engine_default_aescbc.c create mode 100644 lib/bearssl-esp8266/src/ssl/ssl_engine_default_aesccm.c create mode 100644 lib/bearssl-esp8266/src/ssl/ssl_engine_default_aesgcm.c create mode 100644 lib/bearssl-esp8266/src/ssl/ssl_engine_default_chapol.c create mode 100644 lib/bearssl-esp8266/src/ssl/ssl_engine_default_descbc.c create mode 100644 lib/bearssl-esp8266/src/ssl/ssl_engine_default_ec.c create mode 100644 lib/bearssl-esp8266/src/ssl/ssl_engine_default_ecdsa.c create mode 100644 lib/bearssl-esp8266/src/ssl/ssl_engine_default_rsavrfy.c create mode 100644 lib/bearssl-esp8266/src/ssl/ssl_hashes.c create mode 100644 lib/bearssl-esp8266/src/ssl/ssl_hs_client.c create mode 100644 lib/bearssl-esp8266/src/ssl/ssl_hs_server.c create mode 100644 lib/bearssl-esp8266/src/ssl/ssl_io.c create mode 100644 lib/bearssl-esp8266/src/ssl/ssl_keyexport.c create mode 100644 lib/bearssl-esp8266/src/ssl/ssl_lru.c create mode 100644 lib/bearssl-esp8266/src/ssl/ssl_rec_cbc.c create mode 100644 lib/bearssl-esp8266/src/ssl/ssl_rec_ccm.c create mode 100644 lib/bearssl-esp8266/src/ssl/ssl_rec_chapol.c create mode 100644 lib/bearssl-esp8266/src/ssl/ssl_rec_gcm.c create mode 100644 lib/bearssl-esp8266/src/ssl/ssl_scert_single_ec.c create mode 100644 lib/bearssl-esp8266/src/ssl/ssl_scert_single_rsa.c create mode 100644 lib/bearssl-esp8266/src/symcipher/aes_big_cbcdec.c create mode 100644 lib/bearssl-esp8266/src/symcipher/aes_big_cbcenc.c create mode 100644 lib/bearssl-esp8266/src/symcipher/aes_big_ctr.c create mode 100644 lib/bearssl-esp8266/src/symcipher/aes_big_ctrcbc.c create mode 100644 lib/bearssl-esp8266/src/symcipher/aes_big_dec.c create mode 100644 lib/bearssl-esp8266/src/symcipher/aes_big_enc.c create mode 100644 lib/bearssl-esp8266/src/symcipher/aes_common.c create mode 100644 lib/bearssl-esp8266/src/symcipher/aes_ct.c create mode 100644 lib/bearssl-esp8266/src/symcipher/aes_ct64.c create mode 100644 lib/bearssl-esp8266/src/symcipher/aes_ct64_cbcdec.c create mode 100644 lib/bearssl-esp8266/src/symcipher/aes_ct64_cbcenc.c create mode 100644 lib/bearssl-esp8266/src/symcipher/aes_ct64_ctr.c create mode 100644 lib/bearssl-esp8266/src/symcipher/aes_ct64_ctrcbc.c create mode 100644 lib/bearssl-esp8266/src/symcipher/aes_ct64_dec.c create mode 100644 lib/bearssl-esp8266/src/symcipher/aes_ct64_enc.c create mode 100644 lib/bearssl-esp8266/src/symcipher/aes_ct_cbcdec.c create mode 100644 lib/bearssl-esp8266/src/symcipher/aes_ct_cbcenc.c create mode 100644 lib/bearssl-esp8266/src/symcipher/aes_ct_ctr.c create mode 100644 lib/bearssl-esp8266/src/symcipher/aes_ct_ctrcbc.c create mode 100644 lib/bearssl-esp8266/src/symcipher/aes_ct_dec.c create mode 100644 lib/bearssl-esp8266/src/symcipher/aes_ct_enc.c create mode 100644 lib/bearssl-esp8266/src/symcipher/aes_small_cbcdec.c create mode 100644 lib/bearssl-esp8266/src/symcipher/aes_small_cbcenc.c create mode 100644 lib/bearssl-esp8266/src/symcipher/aes_small_ctr.c create mode 100644 lib/bearssl-esp8266/src/symcipher/aes_small_ctrcbc.c create mode 100644 lib/bearssl-esp8266/src/symcipher/aes_small_dec.c create mode 100644 lib/bearssl-esp8266/src/symcipher/aes_small_enc.c create mode 100644 lib/bearssl-esp8266/src/symcipher/chacha20_ct.c create mode 100644 lib/bearssl-esp8266/src/symcipher/chacha20_sse2.c create mode 100644 lib/bearssl-esp8266/src/symcipher/des_ct.c create mode 100644 lib/bearssl-esp8266/src/symcipher/des_ct_cbcdec.c create mode 100644 lib/bearssl-esp8266/src/symcipher/des_ct_cbcenc.c create mode 100644 lib/bearssl-esp8266/src/symcipher/des_support.c create mode 100644 lib/bearssl-esp8266/src/symcipher/des_tab.c create mode 100644 lib/bearssl-esp8266/src/symcipher/des_tab_cbcdec.c create mode 100644 lib/bearssl-esp8266/src/symcipher/des_tab_cbcenc.c create mode 100644 lib/bearssl-esp8266/src/symcipher/poly1305_ctmul.c create mode 100644 lib/bearssl-esp8266/src/symcipher/poly1305_ctmul32.c create mode 100644 lib/bearssl-esp8266/src/symcipher/poly1305_ctmulq.c create mode 100644 lib/bearssl-esp8266/src/symcipher/poly1305_i15.c create mode 100644 lib/bearssl-esp8266/src/t_bearssl.h create mode 100644 lib/bearssl-esp8266/src/t_bearssl_aead.h create mode 100644 lib/bearssl-esp8266/src/t_bearssl_block.h create mode 100644 lib/bearssl-esp8266/src/t_bearssl_ec.h create mode 100644 lib/bearssl-esp8266/src/t_bearssl_hash.h create mode 100644 lib/bearssl-esp8266/src/t_bearssl_hmac.h create mode 100644 lib/bearssl-esp8266/src/t_bearssl_kdf.h create mode 100644 lib/bearssl-esp8266/src/t_bearssl_pem.h create mode 100644 lib/bearssl-esp8266/src/t_bearssl_prf.h create mode 100644 lib/bearssl-esp8266/src/t_bearssl_rand.h create mode 100644 lib/bearssl-esp8266/src/t_bearssl_rsa.h create mode 100644 lib/bearssl-esp8266/src/t_bearssl_ssl.h create mode 100644 lib/bearssl-esp8266/src/t_bearssl_tasmota_config.h create mode 100644 lib/bearssl-esp8266/src/t_bearssl_x509.h create mode 100644 lib/bearssl-esp8266/src/t_config.h create mode 100644 lib/bearssl-esp8266/src/t_inner.h create mode 100644 lib/bearssl-esp8266/src/x509/asn1enc.c create mode 100644 lib/bearssl-esp8266/src/x509/encode_ec_pk8der.c create mode 100644 lib/bearssl-esp8266/src/x509/encode_ec_rawder.c create mode 100644 lib/bearssl-esp8266/src/x509/encode_rsa_pk8der.c create mode 100644 lib/bearssl-esp8266/src/x509/encode_rsa_rawder.c create mode 100644 lib/bearssl-esp8266/src/x509/pkey_decoder.c create mode 100644 lib/bearssl-esp8266/src/x509/skey_decoder.c create mode 100644 lib/bearssl-esp8266/src/x509/x509_decoder.c create mode 100644 lib/bearssl-esp8266/src/x509/x509_knownkey.c create mode 100644 lib/bearssl-esp8266/src/x509/x509_minimal.c create mode 100644 lib/bearssl-esp8266/src/x509/x509_minimal_full.c create mode 100644 lib/vl53l0x-arduino-1.02/.travis.yml create mode 100644 lib/vl53l0x-arduino-1.02/LICENSE.txt create mode 100644 lib/vl53l0x-arduino-1.02/README.md create mode 100644 lib/vl53l0x-arduino-1.02/VL53L0X.cpp create mode 100644 lib/vl53l0x-arduino-1.02/VL53L0X.h create mode 100644 lib/vl53l0x-arduino-1.02/examples/Continuous/Continuous.ino create mode 100644 lib/vl53l0x-arduino-1.02/examples/Single/Single.ino create mode 100644 lib/vl53l0x-arduino-1.02/keywords.txt create mode 100644 lib/vl53l0x-arduino-1.02/library.properties create mode 100644 scripter.md create mode 100644 sonoff/StackThunk_light.cpp create mode 100644 sonoff/StackThunk_light.h create mode 100644 sonoff/WiFiClientSecureLightBearSSL.cpp create mode 100644 sonoff/WiFiClientSecureLightBearSSL.h rename sonoff/language/{es-AR.h => es-ES.h} (95%) create mode 100644 sonoff/sonoff_aws_iot.cpp create mode 100644 sonoff/sonoff_ca.ino delete mode 100644 sonoff/sonoff_letsencrypt.h create mode 100644 sonoff/support_float.ino create mode 100644 sonoff/support_udp.ino create mode 100644 sonoff/xdrv_10_scripter.ino rename sonoff/{xplg_wemohue.ino => xdrv_20_hue.ino} (50%) create mode 100644 sonoff/xdrv_21_wemo.ino create mode 100644 sonoff/xnrg_07_ade7953.ino create mode 100644 sonoff/xsns_43_hre.ino create mode 100644 sonoff/xsns_44_sps30.ino create mode 100644 sonoff/xsns_45_vl53l0x.ino create mode 100644 sonoff/xsns_46_MLX90614.ino create mode 100644 tools/logo/TASMOTA_FullLogo_Vector.svg create mode 100644 tools/logo/TASMOTA_Symbol_Vector.svg diff --git a/.gitignore b/.gitignore index b6460347b..2c1ceae12 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,11 @@ .gcc-flags.json sonoff/user_config_override.h build +firmware.map ## Visual Studio Code specific ###### .vscode +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json +*.bak diff --git a/API.md b/API.md index caec908c6..dc3a7f6e3 100644 --- a/API.md +++ b/API.md @@ -1,4 +1,7 @@ -## Sonoff-Tasmota basic API information +Logo + +# Basic API information + Sonoff-Tasmota can easily be extended by developers using provided function pointers as callback Ids. This document lists the available callback function Ids. See the wiki (https://github.com/arendst/Sonoff-Tasmota/wiki/Sensor-API) for more information. Callback availability can be checked by searching for either XdrvCall, XsnsCall, XdspCall and XnrgCall. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7cfcdfadb..96e1c3173 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,4 +1,6 @@ -# Contributing to Sonoff-Tasmota +Logo + +# Contributing **Any contribution helps our team and makes Tasmota better for the entire community!** @@ -26,7 +28,7 @@ This document describes rules that are in effect for this repository, meant for 1. Any contributor to the project can participate in the triaging process, if he/she chooses to do so. 2. An issue that needs to be closed, either due to not complying with this policy, or for other reasons, should be closed by a contributor. 3. Issues that are accepted should be marked with appropriate labels. -4. Issues that could impact functionality for many users should be considered severe. +4. Issues that could impact functionality for many users should be considered severe. 5. Issues caused by the SDK or chip should not be marked severe, as there usually isn’t much to be done. Common sense should be applied when deciding. Such issues should be documented in the Wiki, for reference by users. 6. Issues with feature requests should be discussed for viability/desirability. 7. Feature requests or changes that are meant to address a very specific/limited use case, especially if at the expense of increased code complexity, may be denied, or may be required to be redesigned, generalized, or simplified. @@ -56,7 +58,57 @@ The process is straight-forward. 7. All pull requests should consider updates to the documentation. 8. Pull requests that address an outstanding issue, particularly an issue deemed to be severe, should be given priority. 9. If a PR is accepted, then it should undergo review and updated based on the feedback provided, then merged. -10. Pull requests that don't meet the above will be denied and closed. +10. By submitting a PR, it is needed to use the provided PR template and check all boxes, performing the required tasks and accepting the CLA. +11. Pull requests that don't meet the above will be denied and closed. + +-------------------------------------- + +## Contributor License Agreement (CLA) + +``` +By making a contribution to this project, I certify that: + +(a) The contribution was created in whole or in part by me and I + have the right to submit it under the GPL-3.0 license; or + +(b) The contribution is based upon previous work that, to the best + of my knowledge, is covered under an appropriate open source + license and I have the right under that license to submit that + work with modifications, whether created in whole or in part + by me, under the GPL-3.0 license; or + +(c) The contribution was provided directly to me by some other + person who certified (a), (b) or (c) and I have not modified + it. + +(d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including all + personal information I submit with it) is maintained indefinitely + and may be redistributed consistent with this project or the open + source license(s) involved. +``` + +This Contributor License Agreement (CLA) was adopted on April 1st, 2019. + +The text of this license is available under the [Creative Commons Attribution-ShareAlike 3.0 Unported License](http://creativecommons.org/licenses/by-sa/3.0/). It is based on the Linux [Developer Certificate Of Origin](http://elinux.org/Developer_Certificate_Of_Origin), but is modified to explicitly use the GPL-3.0 license and not mention sign-off (due to GitHub.com keeps an historial, with your user name, of PRs' commits and all editions on PR's comments). + +To accept the CLA it is required to put a x between [ ] on `[ ] I accept the CLA` in the PR template when submitting it. The [ ] is an opt-in box, so you have to manually accept it. + +**Why a CLA ?** + +_"A Contributor Licence Agreement (CLA) is strongly recommended when accepting third party contributions to an open development project, such as an open source software project. In order to redistribute contributions, it is necessary to ensure that the project has the necessary rights to do so. A Contributor Licence Agreement is a lightweight agreement, signed by the copyright holder, that grants the necessary rights for the contribution to be redistributed as part of the project."_ [OSS Watch](http://oss-watch.ac.uk/resources/cla) + +A CLA is a legal document in which you state _you are entitled to contribute the code/documentation/translation to the project_ you’re contributing to and that _you are willing to have it used in distributions and derivative works_. This means that should there be any kind of legal issue in the future as to the origins and ownership of any particular piece of code, then that project has the necessary forms on file from the contributor(s) saying they were permitted to make this contribution. + +CLA is a safety because it also ensures that once you have provided a contribution, you cannot try to withdraw permission for its use at a later date. People can therefore use that software, confident that they will not be asked to stop using pieces of the code at a later date. + +A __license__ grants "outbound" rights to the user of project. + +A __CLA__ enables a contributor to grant "inbound" rights to a project. + + + + diff --git a/README.md b/README.md index 1ec23cb11..578d1c8f7 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ## Sonoff-Tasmota -Alternative firmware for _ESP8266 based devices_ like [iTead](https://www.itead.cc/) _**Sonoff**_ with **web UI, rules and timers, OTA updates, custome device templates and sensors support**. Allows control over **MQTT**, **HTTP**, **Serial** and **KNX** for integrations with smart home systems. Written for Arduino IDE and PlatformIO. +LogoAlternative firmware for _ESP8266 based devices_ like [iTead](https://www.itead.cc/) _**Sonoff**_ with **web UI, rules and timers, OTA updates, custom device templates and sensor support**. Allows control over **MQTT**, **HTTP**, **Serial** and **KNX** for integrations with smart home systems. Written for Arduino IDE and PlatformIO. [![GitHub version](https://img.shields.io/github/release/arendst/Sonoff-Tasmota.svg)](https://github.com/arendst/Sonoff-Tasmota/releases/latest) [![GitHub download](https://img.shields.io/github/downloads/arendst/Sonoff-Tasmota/total.svg)](https://github.com/arendst/Sonoff-Tasmota/releases/latest) @@ -15,31 +15,31 @@ If you like **Sonoff-Tasmota**, give it a star, or fork it and contribute! See [RELEASENOTES.md](https://github.com/arendst/Sonoff-Tasmota/blob/master/RELEASENOTES.md) for release information. -In addition to the [release webpage](https://github.com/arendst/Sonoff-Tasmota/releases/latest) the binaries can also be OTA downloaded from http://thehackbox.org/tasmota/release/ +In addition to the [release webpage](https://github.com/arendst/Sonoff-Tasmota/releases/latest) the binaries can also be downloaded from http://thehackbox.org/tasmota/release/ -### Disclaimer +#### Disclaimer :warning: **DANGER OF ELECTROCUTION** :warning: -A Sonoff device is not a toy. It uses Mains AC so there is a danger of electrocution if not installed properly. If you don't know how to install it, please call an electrician. Remember: _**SAFETY FIRST**_. It is not worth to risk yourself, your family and your home if you don't know exactly what you are doing. Never try to flash a Sonoff device while it is connected to MAINS AC. +A Sonoff device is not a toy. It uses Mains AC so there is a danger of electrocution if not installed properly. If you don't know how to install it, please call an electrician. Remember: _**SAFETY FIRST**_. It is not worth risk to yourself, your family, and your home if you don't know exactly what you are doing. Never try to flash a Sonoff device while it is connected to MAINS AC. We don't take any responsibility nor liability for using this software nor for the installation or any tips, advice, videos, etc. given by any member of this site or any related site. ### Note -Please do not ask to add devices where you can't provide a basic working configuration (other than sonoff). Since there are thousands of them.. +Please do not ask to add new devices unless it requires additional code for new features. If the device is not listed as a module, try using [Templates](https://github.com/arendst/Sonoff-Tasmota/wiki/Templates) first. If it is not listed in the [Tasmota Device Templates Repository](http://blakadder.github.io/templates) create your own [Template](https://github.com/arendst/Sonoff-Tasmota/wiki/Templates#creating-your-template-). ### Quick Install -Download one of the released binaries from https://github.com/arendst/Sonoff-Tasmota/releases and flash it to your hardware as documented in the wiki. +Download one of the released binaries from https://github.com/arendst/Sonoff-Tasmota/releases and flash it to your hardware as [documented in the wiki](https://github.com/arendst/Sonoff-Tasmota/wiki/Flashing). ### Important User Compilation Information If you want to compile Sonoff-Tasmota yourself keep in mind the following: - Only Flash Mode **DOUT** is supported. Do not use Flash Mode DIO / QIO / QOUT as it might seem to brick your device. See [Wiki](https://github.com/arendst/Sonoff-Tasmota/wiki/Theo's-Tasmota-Tips) for background information. -- Sonoff-Tasmota uses a 1M linker script WITHOUT spiffs **1M (no SPIFFS)** for optimal code space. If you compile using ESP/Arduino library 2.3.0 then download the provided new linker script to your Arduino IDE or Platformio base folder. Later version of ESP/Arduino library already contain the correct linker script. See [Wiki > Prerequisite](https://github.com/arendst/Sonoff-Tasmota/wiki/Prerequisite). -- To make compile time changes to Sonoff-Tasmota it can use the ``user_config_override.h`` file. It assures keeping your settings when you download and compile a new version. To use ``user_config.override.h`` you will have to make a copy of the provided ``user_config.override_sample.h`` file and add your setting overrides. To enable the override file you will need to use a compile define as documented in the ``user_config_override_sample.h`` file. +- Sonoff-Tasmota uses a 1M linker script WITHOUT spiffs **1M (no SPIFFS)** for optimal code space. If you compile using ESP/Arduino library 2.3.0 then download the provided new linker script to your Arduino IDE or Platformio base folder. Later version of ESP/Arduino library already contain the correct linker script. See [Wiki > Prerequisites](https://github.com/arendst/Sonoff-Tasmota/wiki/Prerequisites). +- To make compile time changes to Sonoff-Tasmota it can use the ``user_config_override.h`` file. It assures keeping your settings when you download and compile a new version. To use ``user_config.override.h`` you will have to make a copy of the provided ``user_config_override_sample.h`` file and add your setting overrides. To enable the override file you will need to use a compile define as documented in the ``user_config_override_sample.h`` file. ### Version Information - Sonoff-Tasmota provides all (Sonoff) modules in one file and starts with module Sonoff Basic. -- Once uploaded select module using the configuration webpage or the commands ```Modules``` and ```Module```. +- Once uploaded, select [Module](https://github.com/arendst/Sonoff-Tasmota/wiki/Modules) using the configuration webpage, the commands ```Modules``` and ```Module``` or configure the [Template](https://github.com/arendst/Sonoff-Tasmota/wiki/Templates) for your device - After reboot select config menu again or use commands ```GPIOs``` and ```GPIO``` to change GPIO with desired sensor. ### Migration Information @@ -53,51 +53,11 @@ See [wiki migration path](https://github.com/arendst/Sonoff-Tasmota/wiki/Upgrade ### Support Information -See [Wiki](https://github.com/arendst/Sonoff-Tasmota/wiki) for more information.
-See [Community](https://groups.google.com/d/forum/sonoffusers) for forum.
-See [Chat](https://discord.gg/Ks2Kzd4) for more user experience. +For a database of supported devices see [Tasmota Device Templates Repository](https://blakadder.github.io/templates) -The following devices are supported: -- [iTead Sonoff Basic (R2)](https://www.itead.cc/smart-home/sonoff-wifi-wireless-switch-1.html) -- [iTead Sonoff RF](https://www.itead.cc/smart-home/sonoff-rf.html) -- [iTead Sonoff SV](https://www.itead.cc/smart-home/sonoff-sv.html) -- [iTead Sonoff TH10/TH16 with temperature sensor](https://www.itead.cc/smart-home/sonoff-th.html) -- [iTead Sonoff Dual (R2)](https://www.itead.cc/smart-home/sonoff-dual.html) -- [iTead Sonoff Pow with Energy Monitoring](https://www.itead.cc/smart-home/sonoff-pow.html) -- [iTead Sonoff Pow R2 with Energy Monitoring](https://www.itead.cc/sonoff-pow-r2.html) -- [iTead Sonoff 4CH (R2)](https://www.itead.cc/smart-home/sonoff-4ch.html) -- [iTead Sonoff 4CH Pro (R2)](https://www.itead.cc/smart-home/sonoff-4ch-pro.html) -- [iTead Sonoff S20 Smart Socket](https://www.itead.cc/smart-socket.html) -- [Sonoff S22 Smart Socket](https://github.com/arendst/Sonoff-Tasmota/issues/627) -- [iTead Sonoff S26 Smart Socket](https://www.itead.cc/sonoff-s26-wifi-smart-plug.html) -- [iTead Sonoff S31 Smart Socket with Energy Monitoring](https://www.itead.cc/sonoff-s31.html) -- [iTead Slampher](https://www.itead.cc/slampher.html) -- [iTead Sonoff Touch](https://www.itead.cc/sonoff-touch.html) -- [iTead Sonoff T1](https://www.itead.cc/sonoff-t1.html) -- [iTead Sonoff SC](https://www.itead.cc/sonoff-sc.html) -- [iTead Sonoff Led](https://www.itead.cc/sonoff-led.html) -- [iTead Sonoff BN-SZ01 Ceiling Led](https://www.itead.cc/bn-sz01.html) -- [iTead Sonoff B1](https://www.itead.cc/sonoff-b1.html) -- [iTead Sonoff iFan02](https://www.itead.cc/sonoff-ifan02-wifi-smart-ceiling-fan-with-light.html) -- [iTead Sonoff RF Bridge 433](https://www.itead.cc/sonoff-rf-bridge-433.html) -- [iTead Sonoff Dev](https://www.itead.cc/sonoff-dev.html) -- [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/) -- AI Light or any my9291 compatible RGBW LED bulb -- H801 PWM LED controller -- [MagicHome PWM LED controller](https://github.com/arendst/Sonoff-Tasmota/wiki/MagicHome-LED-strip-controller) -- AriLux AL-LC01, AL-LC06 and AL-LC11 PWM LED controller -- [Supla device - Espablo-inCan mod. for electrical Installation box](https://forum.supla.org/viewtopic.php?f=33&t=2188) -- [BlitzWolf BW-SHP2 Smart Socket with Energy Monitoring](https://www.banggood.com/BlitzWolf-BW-SHP2-Smart-WIFI-Socket-EU-Plug-220V-16A-Work-with-Amazon-Alexa-Google-Assistant-p-1292899.html) -- [Luani HVIO board](https://luani.de/projekte/esp8266-hvio/) -- [Wemos D1 mini](https://wiki.wemos.cc/products:d1:d1_mini) -- [HuaFan Smart Socket](https://github.com/arendst/Sonoff-Tasmota/wiki/HuaFan-Smart-Socket) -- [Hyleton-313 Smart Plug](https://github.com/arendst/Sonoff-Tasmota/wiki/Hyleton-313-Smart-Plug) -- [Allterco Shelly 1](https://shelly.cloud/shelly1-open-source/) -- [Allterco Shelly 2 with Energy Monitoring](https://shelly.cloud/shelly2/) -- NodeMcu and Ledunia -- [KS-602 based switches like GresaTek, Jesiya, NewRice, Lyasi etc](https://ucexperiment.wordpress.com/2017/11/14/reprogramming-a-lyasi-wifi-wall-switch-with-esp8285/) +See [Wiki](https://github.com/arendst/Sonoff-Tasmota/wiki) for use instructions and how-to's.
+See [Community](https://groups.google.com/d/forum/sonoffusers) for forum.
+Visit [Discord Chat](https://discord.gg/Ks2Kzd4) for discussions and troubleshooting. ### Contribute You can contribute to Sonoff-Tasmota by @@ -153,7 +113,7 @@ People helping to keep the show on the road: - Emontnemery for his HomeAssistant Discovery concept and many code tuning tips - Aidan Mountford for his HSB support - Daniel Ztolnai for his Serial Bridge implementation -- Gerhard Mutz for his SGP30, Sunrise/Sunset and display support drivers +- Gerhard Mutz for multiple sensor & display drivers, Sunrise/Sunset, and scripting - Nuno Ferreira for his HC-SR04 driver - Adrian Scillato for his (security)fixes and implementing and maintaining KNX - Gennaro Tortone for implementing and maintaining Eastron drivers @@ -163,6 +123,8 @@ People helping to keep the show on the road: - Joel Stein and digiblur for their Tuya research and driver - Frogmore42 and Jason2866 for providing many issue answers - Blakadder for editing the wiki and providing template management +- Stephan Hadinger for refactoring light driver and enhancing HueEmulation +- tmo for designing the official logo - Many more providing Tips, Wips, Pocs or PRs ### License diff --git a/REFERENCE.md b/REFERENCE.md index 22bd77899..6d2c08747 100644 --- a/REFERENCE.md +++ b/REFERENCE.md @@ -1,4 +1,7 @@ -## Tasmota Reference +Logo + +# Reference + Tasmota backgound information. ## Supported Smart Switch with Energy Monitoring GPIO usage diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 4485d3083..1209b76bf 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -1,3 +1,7 @@ +Logo + +# RELEASE NOTES + ## Migration Information See [wiki migration path](https://github.com/arendst/Sonoff-Tasmota/wiki/Upgrade#migration-path) for instructions how to migrate to a major version. Pay attention to the following version breaks due to dynamic settings updates: @@ -6,11 +10,10 @@ See [wiki migration path](https://github.com/arendst/Sonoff-Tasmota/wiki/Upgrade 3. Migrate to **Sonoff-Tasmota 5.14** 4. Migrate to **Sonoff-Tasmota 6.x** -## Release notes -### Core version 2.3.0 vs 2.4.2 +## Core version 2.3.0 vs 2.4.2 This release is based on ESP8266/Arduino library core 2.3.0 (again) as some people encountered wifi related issues on core 2.4.2. For others core 2.4.2 is working just fine. Both version are available from http://thehackbox.org/tasmota/release/ -### Change in default initial configuration tool +## Change in default initial configuration tool Firmware binary **sonoff-classic.bin** supports **WifiManager, Wps and SmartConfig** for initial configuration. The default tool is **Wps**. To save memory space all other binaries support **WifiManager only**. @@ -71,14 +74,14 @@ Module | Description 49 Neo Coolcam | Neo Coolcam Wifi Smart Socket 50 ESP Switch | ESP Switch 4-gang Wifi Switch with Leds 51 OBI Socket | OBI Wifi Smart Socket -52 Teckin | Teckin SP20 Wifi Smart Switch with Energy Monitoring +52 Teckin | Teckin SP22 Wifi Smart Switch with Energy Monitoring 53 AplicWDP303075 | Aplic WDP 303075 CSL Wifi Smart Switch with Energy Monitoring 54 Tuya Dimmer | MIUO (and other Tuya based) Wifi Dimmer for Incandescent Lights and Led 55 Gosund SP1 v23 | Gosund SP1 v2.3 Wifi Smart Switch with Energy Monitoring 56 ARMTR Dimmer | ARMtronix Wifi dimmer for Incandescent Lights and Led 57 SK03 Outdoor | SK03 Outdoor Wifi Smart Switch with Energy Monitoring 58 PS-16-DZ | PS-16-DZ Wifi dimmer for Incandescent Lights and Led -59 Teckin US | Teckin US and ZooZee SA102 Wifi Smart Switch with Energy Monitoring +59 Teckin US | Teckin SP20 and ZooZee SA102 Wifi Smart Switch with Energy Monitoring 60 Manzoku strip | Manzoku Wifi Smart Power Strip with four Relays 61 OBI Socket 2 | OBI 2 Wifi Smart Socket 62 YTF IR Bridge | YTF Infra Red Wifi Bridge @@ -104,7 +107,7 @@ The following binary downloads have been compiled with ESP8266/Arduino library c Core version **2.4.2** binaries can be found at http://thehackbox.org/tasmota/release/020402/ -### Available Features and Sensors +## Available Features and Sensors | Feature or Sensor | minimal | basic | classic | sonoff | knx | sensors | display | Remarks |-----------------------|---------|-------|---------|--------|------|---------|---------|-------- @@ -194,6 +197,7 @@ Core version **2.4.2** binaries can be found at http://thehackbox.org/tasmota/re | USE_RC_SWITCH | - | - | - | x | x | x | x | | USE_RF_SENSOR | - | - | - | - | - | x | - | AlectoV2 only | USE_SM16716 | - | x | x | x | x | x | x | +| USE_HRE | - | - | - | - | - | x | - | | USE_DISPLAY | - | - | - | - | - | - | x | | USE_DISPLAY_LCD | - | - | - | - | - | - | x | | USE_DISPLAY_SSD1306 | - | - | - | - | - | - | x | diff --git a/SUPPORT.md b/SUPPORT.md index 46fa03ebc..26c770b80 100644 --- a/SUPPORT.md +++ b/SUPPORT.md @@ -1,20 +1,23 @@ -# Sonoff-Tasmota Support +Logo + +# Support If you're looking for support on **Sonoff-Tasmota** there are some options available: -### Documentation: +## Documentation: * [Wiki Pages](https://github.com/arendst/Sonoff-Tasmota/wiki): For information on how to Flash Tasmota, configure and use it. -* [Troubleshooting Information](https://github.com/arendst/Sonoff-Tasmota/wiki/Troubleshooting): For information on common problems and solutions. +* [FAQ](https://github.com/arendst/Sonoff-Tasmota/wiki/FAQ): For information on common problems and solutions. +* [Troubleshooting Information](https://github.com/arendst/Sonoff-Tasmota/wiki/Troubleshooting): For ways to debug and troubleshoot. * [Commands Information](https://github.com/arendst/Sonoff-Tasmota/wiki/Commands): For information on all the commands supported by Tasmota. -### Support's Community: +## Support's Community: * [Tasmota Forum](https://groups.google.com/d/forum/sonoffusers): For usage and discussions. * [Tasmota Support Chat](https://discord.gg/Ks2Kzd4): For support, troubleshooting and general questions. You have better chances to get fast answers from members of the Tasmota Community. * [Search in Issues](https://github.com/arendst/Sonoff-Tasmota/issues): You might find an answer to your question by searching current or closed issues. -### Developers' Community: +## Developers' Community: * [Bug Report](https://github.com/arendst/Sonoff-Tasmota/issues/new?template=Bug_report.md): For reporting Bugs of Tasmota Software. * [Feature Request](https://github.com/arendst/Sonoff-Tasmota/issues/new?template=Feature_request.md): For requesting features/functions to Tasmota Software. diff --git a/TEMPLATE.md b/TEMPLATE.md index 67431f651..33465a0e9 100644 --- a/TEMPLATE.md +++ b/TEMPLATE.md @@ -1,4 +1,7 @@ -## Sonoff-Tasmota template information +Logo + +# Template information + Sonoff-Tasmota uses Device or Module information to control peripherals connected to GPIOs. This information is stored in the ``sonoff_template.h`` file as a device specific template. The template contains information about what GPIO should be connected to what peripheral and what GPIO may be configured online using the ``GPIO`` command or GUI Configure Module menu. In addition a device may need specific coding to process the data from these peripherals. The module number as provided by the ``Modules`` command is used to select this coding. Starting with version 6.4.1.16 Sonoff-Tasmota Modules can be extended by users online using a template. To provide easy processing by Sonoff-Tasmota a user template is written as JSON text and could look like this: @@ -83,4 +86,4 @@ The following command will update the flag of a stored template ``Template {"FLAG":1}`` The following command will update the base of a stored template to Generic -``Template {"BASE":0}`` \ No newline at end of file +``Template {"BASE":0}`` diff --git a/arduino/version 2.5.0/boards.txt b/arduino/version 2.5.2/boards.txt similarity index 96% rename from arduino/version 2.5.0/boards.txt rename to arduino/version 2.5.2/boards.txt index 300a608c4..af5bc1129 100644 --- a/arduino/version 2.5.0/boards.txt +++ b/arduino/version 2.5.2/boards.txt @@ -6,7 +6,9 @@ menu.BoardModel=Model menu.baud=Upload Speed + menu.UploadTool=Upload Using + menu.xtal=CPU Frequency menu.CrystalFreq=Crystal Frequency menu.eesz=Flash Size @@ -21,6 +23,8 @@ menu.vt=VTables menu.exception=Exceptions menu.led=Builtin Led menu.wipe=Erase Flash +menu.sdk=Espressif FW +menu.ssl=SSL Support ############################################################## generic.name=Generic ESP8266 Module @@ -28,7 +32,7 @@ generic.build.board=ESP8266_GENERIC generic.upload.tool=esptool generic.upload.maximum_data_size=81920 generic.upload.wait_for_upload_port=true -generic.upload.erase_cmd= +generic.upload.erase_cmd=version generic.serial.disableDTR=true generic.serial.disableRTS=true generic.build.mcu=esp8266 @@ -60,6 +64,10 @@ generic.menu.exception.disabled.build.stdcpp_lib=-lstdc++ generic.menu.exception.enabled=Enabled generic.menu.exception.enabled.build.exception_flags=-fexceptions generic.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +generic.menu.ssl.all=All SSL ciphers (most compatible) +generic.menu.ssl.all.build.sslflags= +generic.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +generic.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC generic.menu.ResetMethod.ck=ck generic.menu.ResetMethod.ck.upload.resetmethod=ck generic.menu.ResetMethod.nodemcu=nodemcu @@ -357,6 +365,12 @@ generic.menu.led.14=14 generic.menu.led.14.build.led=-DLED_BUILTIN=14 generic.menu.led.15=15 generic.menu.led.15.build.led=-DLED_BUILTIN=15 +generic.menu.sdk.nonosdk221=nonos-sdk 2.2.1 (legacy) +generic.menu.sdk.nonosdk221.build.sdk=NONOSDK221 +generic.menu.sdk.nonosdk222=nonos-sdk 2.2.2-190313 (testing) +generic.menu.sdk.nonosdk222.build.sdk=NONOSDK22x +generic.menu.sdk.nonosdk3v0=nonos-sdk pre-3 (known issues) +generic.menu.sdk.nonosdk3v0.build.sdk=NONOSDK3V0 generic.menu.ip.lm2f=v2 Lower Memory generic.menu.ip.lm2f.build.lwip_include=lwip2/include generic.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat @@ -445,11 +459,11 @@ generic.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOO generic.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG generic.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG generic.menu.wipe.none=Only Sketch -generic.menu.wipe.none.upload.erase_cmd= +generic.menu.wipe.none.upload.erase_cmd=version generic.menu.wipe.sdk=Sketch + WiFi Settings -generic.menu.wipe.sdk.upload.erase_cmd=-ca "{build.rfcal_addr}" -cz 0x4000 +generic.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 generic.menu.wipe.all=All Flash Contents -generic.menu.wipe.all.upload.erase_cmd=-ca 0x0 -cz "{build.flash_size_bytes}" +generic.menu.wipe.all.upload.erase_cmd=erase_flash generic.menu.baud.115200=115200 generic.menu.baud.115200.upload.speed=115200 generic.menu.baud.9600=9600 @@ -476,7 +490,7 @@ esp8285.build.variant=esp8285 esp8285.upload.tool=esptool esp8285.upload.maximum_data_size=81920 esp8285.upload.wait_for_upload_port=true -esp8285.upload.erase_cmd= +esp8285.upload.erase_cmd=version esp8285.serial.disableDTR=true esp8285.serial.disableRTS=true esp8285.build.mcu=esp8266 @@ -500,6 +514,10 @@ esp8285.menu.exception.disabled.build.stdcpp_lib=-lstdc++ esp8285.menu.exception.enabled=Enabled esp8285.menu.exception.enabled.build.exception_flags=-fexceptions esp8285.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +esp8285.menu.ssl.all=All SSL ciphers (most compatible) +esp8285.menu.ssl.all.build.sslflags= +esp8285.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +esp8285.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC esp8285.menu.ResetMethod.ck=ck esp8285.menu.ResetMethod.ck.upload.resetmethod=ck esp8285.menu.ResetMethod.nodemcu=nodemcu @@ -711,11 +729,11 @@ esp8285.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOO esp8285.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG esp8285.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG esp8285.menu.wipe.none=Only Sketch -esp8285.menu.wipe.none.upload.erase_cmd= +esp8285.menu.wipe.none.upload.erase_cmd=version esp8285.menu.wipe.sdk=Sketch + WiFi Settings -esp8285.menu.wipe.sdk.upload.erase_cmd=-ca "{build.rfcal_addr}" -cz 0x4000 +esp8285.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 esp8285.menu.wipe.all=All Flash Contents -esp8285.menu.wipe.all.upload.erase_cmd=-ca 0x0 -cz "{build.flash_size_bytes}" +esp8285.menu.wipe.all.upload.erase_cmd=erase_flash esp8285.menu.baud.115200=115200 esp8285.menu.baud.115200.upload.speed=115200 esp8285.menu.baud.9600=9600 @@ -751,7 +769,7 @@ espduino.menu.UploadTool.espota.upload.tool=espota espduino.upload.tool=esptool espduino.upload.maximum_data_size=81920 espduino.upload.wait_for_upload_port=true -espduino.upload.erase_cmd= +espduino.upload.erase_cmd=version espduino.serial.disableDTR=true espduino.serial.disableRTS=true espduino.build.mcu=esp8266 @@ -775,6 +793,10 @@ espduino.menu.exception.disabled.build.stdcpp_lib=-lstdc++ espduino.menu.exception.enabled=Enabled espduino.menu.exception.enabled.build.exception_flags=-fexceptions espduino.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +espduino.menu.ssl.all=All SSL ciphers (most compatible) +espduino.menu.ssl.all.build.sslflags= +espduino.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +espduino.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC espduino.build.flash_mode=dio espduino.build.flash_flags=-DFLASHMODE_DIO espduino.build.flash_freq=40 @@ -903,11 +925,11 @@ espduino.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAO espduino.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG espduino.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG espduino.menu.wipe.none=Only Sketch -espduino.menu.wipe.none.upload.erase_cmd= +espduino.menu.wipe.none.upload.erase_cmd=version espduino.menu.wipe.sdk=Sketch + WiFi Settings -espduino.menu.wipe.sdk.upload.erase_cmd=-ca "{build.rfcal_addr}" -cz 0x4000 +espduino.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 espduino.menu.wipe.all=All Flash Contents -espduino.menu.wipe.all.upload.erase_cmd=-ca 0x0 -cz "{build.flash_size_bytes}" +espduino.menu.wipe.all.upload.erase_cmd=erase_flash espduino.menu.baud.115200=115200 espduino.menu.baud.115200.upload.speed=115200 espduino.menu.baud.9600=9600 @@ -934,7 +956,7 @@ huzzah.build.variant=adafruit huzzah.upload.tool=esptool huzzah.upload.maximum_data_size=81920 huzzah.upload.wait_for_upload_port=true -huzzah.upload.erase_cmd= +huzzah.upload.erase_cmd=version huzzah.serial.disableDTR=true huzzah.serial.disableRTS=true huzzah.build.mcu=esp8266 @@ -958,6 +980,10 @@ huzzah.menu.exception.disabled.build.stdcpp_lib=-lstdc++ huzzah.menu.exception.enabled=Enabled huzzah.menu.exception.enabled.build.exception_flags=-fexceptions huzzah.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +huzzah.menu.ssl.all=All SSL ciphers (most compatible) +huzzah.menu.ssl.all.build.sslflags= +huzzah.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +huzzah.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC huzzah.upload.resetmethod=nodemcu huzzah.build.flash_mode=qio huzzah.build.flash_flags=-DFLASHMODE_QIO @@ -1087,11 +1113,11 @@ huzzah.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM huzzah.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG huzzah.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG huzzah.menu.wipe.none=Only Sketch -huzzah.menu.wipe.none.upload.erase_cmd= +huzzah.menu.wipe.none.upload.erase_cmd=version huzzah.menu.wipe.sdk=Sketch + WiFi Settings -huzzah.menu.wipe.sdk.upload.erase_cmd=-ca "{build.rfcal_addr}" -cz 0x4000 +huzzah.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 huzzah.menu.wipe.all=All Flash Contents -huzzah.menu.wipe.all.upload.erase_cmd=-ca 0x0 -cz "{build.flash_size_bytes}" +huzzah.menu.wipe.all.upload.erase_cmd=erase_flash huzzah.menu.baud.115200=115200 huzzah.menu.baud.115200.upload.speed=115200 huzzah.menu.baud.9600=9600 @@ -1118,7 +1144,7 @@ inventone.build.variant=inventone inventone.upload.tool=esptool inventone.upload.maximum_data_size=81920 inventone.upload.wait_for_upload_port=true -inventone.upload.erase_cmd= +inventone.upload.erase_cmd=version inventone.serial.disableDTR=true inventone.serial.disableRTS=true inventone.build.mcu=esp8266 @@ -1142,6 +1168,10 @@ inventone.menu.exception.disabled.build.stdcpp_lib=-lstdc++ inventone.menu.exception.enabled=Enabled inventone.menu.exception.enabled.build.exception_flags=-fexceptions inventone.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +inventone.menu.ssl.all=All SSL ciphers (most compatible) +inventone.menu.ssl.all.build.sslflags= +inventone.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +inventone.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC inventone.upload.resetmethod=nodemcu inventone.build.flash_mode=dio inventone.build.flash_flags=-DFLASHMODE_DIO @@ -1271,11 +1301,11 @@ inventone.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTA inventone.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG inventone.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG inventone.menu.wipe.none=Only Sketch -inventone.menu.wipe.none.upload.erase_cmd= +inventone.menu.wipe.none.upload.erase_cmd=version inventone.menu.wipe.sdk=Sketch + WiFi Settings -inventone.menu.wipe.sdk.upload.erase_cmd=-ca "{build.rfcal_addr}" -cz 0x4000 +inventone.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 inventone.menu.wipe.all=All Flash Contents -inventone.menu.wipe.all.upload.erase_cmd=-ca 0x0 -cz "{build.flash_size_bytes}" +inventone.menu.wipe.all.upload.erase_cmd=erase_flash inventone.menu.baud.115200=115200 inventone.menu.baud.115200.upload.speed=115200 inventone.menu.baud.9600=9600 @@ -1302,7 +1332,7 @@ cw01.build.variant=xinabox cw01.upload.tool=esptool cw01.upload.maximum_data_size=81920 cw01.upload.wait_for_upload_port=true -cw01.upload.erase_cmd= +cw01.upload.erase_cmd=version cw01.serial.disableDTR=true cw01.serial.disableRTS=true cw01.build.mcu=esp8266 @@ -1326,6 +1356,10 @@ cw01.menu.exception.disabled.build.stdcpp_lib=-lstdc++ cw01.menu.exception.enabled=Enabled cw01.menu.exception.enabled.build.exception_flags=-fexceptions cw01.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +cw01.menu.ssl.all=All SSL ciphers (most compatible) +cw01.menu.ssl.all.build.sslflags= +cw01.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +cw01.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC cw01.upload.resetmethod=nodemcu cw01.menu.CrystalFreq.26=26 MHz cw01.menu.CrystalFreq.40=40 MHz @@ -1458,11 +1492,11 @@ cw01.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM.b cw01.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG cw01.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG cw01.menu.wipe.none=Only Sketch -cw01.menu.wipe.none.upload.erase_cmd= +cw01.menu.wipe.none.upload.erase_cmd=version cw01.menu.wipe.sdk=Sketch + WiFi Settings -cw01.menu.wipe.sdk.upload.erase_cmd=-ca "{build.rfcal_addr}" -cz 0x4000 +cw01.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 cw01.menu.wipe.all=All Flash Contents -cw01.menu.wipe.all.upload.erase_cmd=-ca 0x0 -cz "{build.flash_size_bytes}" +cw01.menu.wipe.all.upload.erase_cmd=erase_flash cw01.menu.baud.115200=115200 cw01.menu.baud.115200.upload.speed=115200 cw01.menu.baud.9600=9600 @@ -1489,7 +1523,7 @@ espresso_lite_v1.build.variant=espresso_lite_v1 espresso_lite_v1.upload.tool=esptool espresso_lite_v1.upload.maximum_data_size=81920 espresso_lite_v1.upload.wait_for_upload_port=true -espresso_lite_v1.upload.erase_cmd= +espresso_lite_v1.upload.erase_cmd=version espresso_lite_v1.serial.disableDTR=true espresso_lite_v1.serial.disableRTS=true espresso_lite_v1.build.mcu=esp8266 @@ -1513,6 +1547,10 @@ espresso_lite_v1.menu.exception.disabled.build.stdcpp_lib=-lstdc++ espresso_lite_v1.menu.exception.enabled=Enabled espresso_lite_v1.menu.exception.enabled.build.exception_flags=-fexceptions espresso_lite_v1.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +espresso_lite_v1.menu.ssl.all=All SSL ciphers (most compatible) +espresso_lite_v1.menu.ssl.all.build.sslflags= +espresso_lite_v1.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +espresso_lite_v1.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC espresso_lite_v1.build.flash_mode=dio espresso_lite_v1.build.flash_flags=-DFLASHMODE_DIO espresso_lite_v1.build.flash_freq=40 @@ -1645,11 +1683,11 @@ espresso_lite_v1.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPD espresso_lite_v1.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG espresso_lite_v1.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG espresso_lite_v1.menu.wipe.none=Only Sketch -espresso_lite_v1.menu.wipe.none.upload.erase_cmd= +espresso_lite_v1.menu.wipe.none.upload.erase_cmd=version espresso_lite_v1.menu.wipe.sdk=Sketch + WiFi Settings -espresso_lite_v1.menu.wipe.sdk.upload.erase_cmd=-ca "{build.rfcal_addr}" -cz 0x4000 +espresso_lite_v1.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 espresso_lite_v1.menu.wipe.all=All Flash Contents -espresso_lite_v1.menu.wipe.all.upload.erase_cmd=-ca 0x0 -cz "{build.flash_size_bytes}" +espresso_lite_v1.menu.wipe.all.upload.erase_cmd=erase_flash espresso_lite_v1.menu.baud.115200=115200 espresso_lite_v1.menu.baud.115200.upload.speed=115200 espresso_lite_v1.menu.baud.9600=9600 @@ -1676,7 +1714,7 @@ espresso_lite_v2.build.variant=espresso_lite_v2 espresso_lite_v2.upload.tool=esptool espresso_lite_v2.upload.maximum_data_size=81920 espresso_lite_v2.upload.wait_for_upload_port=true -espresso_lite_v2.upload.erase_cmd= +espresso_lite_v2.upload.erase_cmd=version espresso_lite_v2.serial.disableDTR=true espresso_lite_v2.serial.disableRTS=true espresso_lite_v2.build.mcu=esp8266 @@ -1700,6 +1738,10 @@ espresso_lite_v2.menu.exception.disabled.build.stdcpp_lib=-lstdc++ espresso_lite_v2.menu.exception.enabled=Enabled espresso_lite_v2.menu.exception.enabled.build.exception_flags=-fexceptions espresso_lite_v2.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +espresso_lite_v2.menu.ssl.all=All SSL ciphers (most compatible) +espresso_lite_v2.menu.ssl.all.build.sslflags= +espresso_lite_v2.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +espresso_lite_v2.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC espresso_lite_v2.build.flash_mode=dio espresso_lite_v2.build.flash_flags=-DFLASHMODE_DIO espresso_lite_v2.build.flash_freq=40 @@ -1832,11 +1874,11 @@ espresso_lite_v2.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPD espresso_lite_v2.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG espresso_lite_v2.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG espresso_lite_v2.menu.wipe.none=Only Sketch -espresso_lite_v2.menu.wipe.none.upload.erase_cmd= +espresso_lite_v2.menu.wipe.none.upload.erase_cmd=version espresso_lite_v2.menu.wipe.sdk=Sketch + WiFi Settings -espresso_lite_v2.menu.wipe.sdk.upload.erase_cmd=-ca "{build.rfcal_addr}" -cz 0x4000 +espresso_lite_v2.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 espresso_lite_v2.menu.wipe.all=All Flash Contents -espresso_lite_v2.menu.wipe.all.upload.erase_cmd=-ca 0x0 -cz "{build.flash_size_bytes}" +espresso_lite_v2.menu.wipe.all.upload.erase_cmd=erase_flash espresso_lite_v2.menu.baud.115200=115200 espresso_lite_v2.menu.baud.115200.upload.speed=115200 espresso_lite_v2.menu.baud.9600=9600 @@ -1863,7 +1905,7 @@ phoenix_v1.build.variant=phoenix_v1 phoenix_v1.upload.tool=esptool phoenix_v1.upload.maximum_data_size=81920 phoenix_v1.upload.wait_for_upload_port=true -phoenix_v1.upload.erase_cmd= +phoenix_v1.upload.erase_cmd=version phoenix_v1.serial.disableDTR=true phoenix_v1.serial.disableRTS=true phoenix_v1.build.mcu=esp8266 @@ -1887,6 +1929,10 @@ phoenix_v1.menu.exception.disabled.build.stdcpp_lib=-lstdc++ phoenix_v1.menu.exception.enabled=Enabled phoenix_v1.menu.exception.enabled.build.exception_flags=-fexceptions phoenix_v1.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +phoenix_v1.menu.ssl.all=All SSL ciphers (most compatible) +phoenix_v1.menu.ssl.all.build.sslflags= +phoenix_v1.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +phoenix_v1.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC phoenix_v1.build.flash_mode=dio phoenix_v1.build.flash_flags=-DFLASHMODE_DIO phoenix_v1.build.flash_freq=40 @@ -2019,11 +2065,11 @@ phoenix_v1.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROT phoenix_v1.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG phoenix_v1.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG phoenix_v1.menu.wipe.none=Only Sketch -phoenix_v1.menu.wipe.none.upload.erase_cmd= +phoenix_v1.menu.wipe.none.upload.erase_cmd=version phoenix_v1.menu.wipe.sdk=Sketch + WiFi Settings -phoenix_v1.menu.wipe.sdk.upload.erase_cmd=-ca "{build.rfcal_addr}" -cz 0x4000 +phoenix_v1.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 phoenix_v1.menu.wipe.all=All Flash Contents -phoenix_v1.menu.wipe.all.upload.erase_cmd=-ca 0x0 -cz "{build.flash_size_bytes}" +phoenix_v1.menu.wipe.all.upload.erase_cmd=erase_flash phoenix_v1.menu.baud.115200=115200 phoenix_v1.menu.baud.115200.upload.speed=115200 phoenix_v1.menu.baud.9600=9600 @@ -2050,7 +2096,7 @@ phoenix_v2.build.variant=phoenix_v2 phoenix_v2.upload.tool=esptool phoenix_v2.upload.maximum_data_size=81920 phoenix_v2.upload.wait_for_upload_port=true -phoenix_v2.upload.erase_cmd= +phoenix_v2.upload.erase_cmd=version phoenix_v2.serial.disableDTR=true phoenix_v2.serial.disableRTS=true phoenix_v2.build.mcu=esp8266 @@ -2074,6 +2120,10 @@ phoenix_v2.menu.exception.disabled.build.stdcpp_lib=-lstdc++ phoenix_v2.menu.exception.enabled=Enabled phoenix_v2.menu.exception.enabled.build.exception_flags=-fexceptions phoenix_v2.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +phoenix_v2.menu.ssl.all=All SSL ciphers (most compatible) +phoenix_v2.menu.ssl.all.build.sslflags= +phoenix_v2.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +phoenix_v2.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC phoenix_v2.build.flash_mode=dio phoenix_v2.build.flash_flags=-DFLASHMODE_DIO phoenix_v2.build.flash_freq=40 @@ -2206,11 +2256,11 @@ phoenix_v2.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROT phoenix_v2.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG phoenix_v2.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG phoenix_v2.menu.wipe.none=Only Sketch -phoenix_v2.menu.wipe.none.upload.erase_cmd= +phoenix_v2.menu.wipe.none.upload.erase_cmd=version phoenix_v2.menu.wipe.sdk=Sketch + WiFi Settings -phoenix_v2.menu.wipe.sdk.upload.erase_cmd=-ca "{build.rfcal_addr}" -cz 0x4000 +phoenix_v2.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 phoenix_v2.menu.wipe.all=All Flash Contents -phoenix_v2.menu.wipe.all.upload.erase_cmd=-ca 0x0 -cz "{build.flash_size_bytes}" +phoenix_v2.menu.wipe.all.upload.erase_cmd=erase_flash phoenix_v2.menu.baud.115200=115200 phoenix_v2.menu.baud.115200.upload.speed=115200 phoenix_v2.menu.baud.9600=9600 @@ -2237,7 +2287,7 @@ nodemcu.build.variant=nodemcu nodemcu.upload.tool=esptool nodemcu.upload.maximum_data_size=81920 nodemcu.upload.wait_for_upload_port=true -nodemcu.upload.erase_cmd= +nodemcu.upload.erase_cmd=version nodemcu.serial.disableDTR=true nodemcu.serial.disableRTS=true nodemcu.build.mcu=esp8266 @@ -2261,6 +2311,10 @@ nodemcu.menu.exception.disabled.build.stdcpp_lib=-lstdc++ nodemcu.menu.exception.enabled=Enabled nodemcu.menu.exception.enabled.build.exception_flags=-fexceptions nodemcu.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +nodemcu.menu.ssl.all=All SSL ciphers (most compatible) +nodemcu.menu.ssl.all.build.sslflags= +nodemcu.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +nodemcu.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC nodemcu.upload.resetmethod=nodemcu nodemcu.build.flash_mode=qio nodemcu.build.flash_flags=-DFLASHMODE_QIO @@ -2390,11 +2444,11 @@ nodemcu.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOO nodemcu.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG nodemcu.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG nodemcu.menu.wipe.none=Only Sketch -nodemcu.menu.wipe.none.upload.erase_cmd= +nodemcu.menu.wipe.none.upload.erase_cmd=version nodemcu.menu.wipe.sdk=Sketch + WiFi Settings -nodemcu.menu.wipe.sdk.upload.erase_cmd=-ca "{build.rfcal_addr}" -cz 0x4000 +nodemcu.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 nodemcu.menu.wipe.all=All Flash Contents -nodemcu.menu.wipe.all.upload.erase_cmd=-ca 0x0 -cz "{build.flash_size_bytes}" +nodemcu.menu.wipe.all.upload.erase_cmd=erase_flash nodemcu.menu.baud.115200=115200 nodemcu.menu.baud.115200.upload.speed=115200 nodemcu.menu.baud.9600=9600 @@ -2421,7 +2475,7 @@ nodemcuv2.build.variant=nodemcu nodemcuv2.upload.tool=esptool nodemcuv2.upload.maximum_data_size=81920 nodemcuv2.upload.wait_for_upload_port=true -nodemcuv2.upload.erase_cmd= +nodemcuv2.upload.erase_cmd=version nodemcuv2.serial.disableDTR=true nodemcuv2.serial.disableRTS=true nodemcuv2.build.mcu=esp8266 @@ -2445,6 +2499,10 @@ nodemcuv2.menu.exception.disabled.build.stdcpp_lib=-lstdc++ nodemcuv2.menu.exception.enabled=Enabled nodemcuv2.menu.exception.enabled.build.exception_flags=-fexceptions nodemcuv2.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +nodemcuv2.menu.ssl.all=All SSL ciphers (most compatible) +nodemcuv2.menu.ssl.all.build.sslflags= +nodemcuv2.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +nodemcuv2.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC nodemcuv2.upload.resetmethod=nodemcu nodemcuv2.build.flash_mode=dio nodemcuv2.build.flash_flags=-DFLASHMODE_DIO @@ -2574,11 +2632,11 @@ nodemcuv2.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTA nodemcuv2.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG nodemcuv2.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG nodemcuv2.menu.wipe.none=Only Sketch -nodemcuv2.menu.wipe.none.upload.erase_cmd= +nodemcuv2.menu.wipe.none.upload.erase_cmd=version nodemcuv2.menu.wipe.sdk=Sketch + WiFi Settings -nodemcuv2.menu.wipe.sdk.upload.erase_cmd=-ca "{build.rfcal_addr}" -cz 0x4000 +nodemcuv2.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 nodemcuv2.menu.wipe.all=All Flash Contents -nodemcuv2.menu.wipe.all.upload.erase_cmd=-ca 0x0 -cz "{build.flash_size_bytes}" +nodemcuv2.menu.wipe.all.upload.erase_cmd=erase_flash nodemcuv2.menu.baud.115200=115200 nodemcuv2.menu.baud.115200.upload.speed=115200 nodemcuv2.menu.baud.9600=9600 @@ -2605,7 +2663,7 @@ modwifi.build.variant=modwifi modwifi.upload.tool=esptool modwifi.upload.maximum_data_size=81920 modwifi.upload.wait_for_upload_port=true -modwifi.upload.erase_cmd= +modwifi.upload.erase_cmd=version modwifi.serial.disableDTR=true modwifi.serial.disableRTS=true modwifi.build.mcu=esp8266 @@ -2629,6 +2687,10 @@ modwifi.menu.exception.disabled.build.stdcpp_lib=-lstdc++ modwifi.menu.exception.enabled=Enabled modwifi.menu.exception.enabled.build.exception_flags=-fexceptions modwifi.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +modwifi.menu.ssl.all=All SSL ciphers (most compatible) +modwifi.menu.ssl.all.build.sslflags= +modwifi.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +modwifi.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC modwifi.upload.resetmethod=ck modwifi.build.flash_mode=qio modwifi.build.flash_flags=-DFLASHMODE_QIO @@ -2768,11 +2830,11 @@ modwifi.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOO modwifi.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG modwifi.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG modwifi.menu.wipe.none=Only Sketch -modwifi.menu.wipe.none.upload.erase_cmd= +modwifi.menu.wipe.none.upload.erase_cmd=version modwifi.menu.wipe.sdk=Sketch + WiFi Settings -modwifi.menu.wipe.sdk.upload.erase_cmd=-ca "{build.rfcal_addr}" -cz 0x4000 +modwifi.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 modwifi.menu.wipe.all=All Flash Contents -modwifi.menu.wipe.all.upload.erase_cmd=-ca 0x0 -cz "{build.flash_size_bytes}" +modwifi.menu.wipe.all.upload.erase_cmd=erase_flash modwifi.menu.baud.115200=115200 modwifi.menu.baud.115200.upload.speed=115200 modwifi.menu.baud.9600=9600 @@ -2799,7 +2861,7 @@ thing.build.variant=thing thing.upload.tool=esptool thing.upload.maximum_data_size=81920 thing.upload.wait_for_upload_port=true -thing.upload.erase_cmd= +thing.upload.erase_cmd=version thing.serial.disableDTR=true thing.serial.disableRTS=true thing.build.mcu=esp8266 @@ -2823,6 +2885,10 @@ thing.menu.exception.disabled.build.stdcpp_lib=-lstdc++ thing.menu.exception.enabled=Enabled thing.menu.exception.enabled.build.exception_flags=-fexceptions thing.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +thing.menu.ssl.all=All SSL ciphers (most compatible) +thing.menu.ssl.all.build.sslflags= +thing.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +thing.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC thing.upload.resetmethod=ck thing.build.flash_mode=qio thing.build.flash_flags=-DFLASHMODE_QIO @@ -2952,11 +3018,11 @@ thing.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM. thing.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG thing.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG thing.menu.wipe.none=Only Sketch -thing.menu.wipe.none.upload.erase_cmd= +thing.menu.wipe.none.upload.erase_cmd=version thing.menu.wipe.sdk=Sketch + WiFi Settings -thing.menu.wipe.sdk.upload.erase_cmd=-ca "{build.rfcal_addr}" -cz 0x4000 +thing.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 thing.menu.wipe.all=All Flash Contents -thing.menu.wipe.all.upload.erase_cmd=-ca 0x0 -cz "{build.flash_size_bytes}" +thing.menu.wipe.all.upload.erase_cmd=erase_flash thing.menu.baud.115200=115200 thing.menu.baud.115200.upload.speed=115200 thing.menu.baud.9600=9600 @@ -2983,7 +3049,7 @@ thingdev.build.variant=thing thingdev.upload.tool=esptool thingdev.upload.maximum_data_size=81920 thingdev.upload.wait_for_upload_port=true -thingdev.upload.erase_cmd= +thingdev.upload.erase_cmd=version thingdev.serial.disableDTR=true thingdev.serial.disableRTS=true thingdev.build.mcu=esp8266 @@ -3007,6 +3073,10 @@ thingdev.menu.exception.disabled.build.stdcpp_lib=-lstdc++ thingdev.menu.exception.enabled=Enabled thingdev.menu.exception.enabled.build.exception_flags=-fexceptions thingdev.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +thingdev.menu.ssl.all=All SSL ciphers (most compatible) +thingdev.menu.ssl.all.build.sslflags= +thingdev.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +thingdev.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC thingdev.upload.resetmethod=nodemcu thingdev.build.flash_mode=dio thingdev.build.flash_flags=-DFLASHMODE_DIO @@ -3136,11 +3206,11 @@ thingdev.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAO thingdev.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG thingdev.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG thingdev.menu.wipe.none=Only Sketch -thingdev.menu.wipe.none.upload.erase_cmd= +thingdev.menu.wipe.none.upload.erase_cmd=version thingdev.menu.wipe.sdk=Sketch + WiFi Settings -thingdev.menu.wipe.sdk.upload.erase_cmd=-ca "{build.rfcal_addr}" -cz 0x4000 +thingdev.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 thingdev.menu.wipe.all=All Flash Contents -thingdev.menu.wipe.all.upload.erase_cmd=-ca 0x0 -cz "{build.flash_size_bytes}" +thingdev.menu.wipe.all.upload.erase_cmd=erase_flash thingdev.menu.baud.115200=115200 thingdev.menu.baud.115200.upload.speed=115200 thingdev.menu.baud.9600=9600 @@ -3166,7 +3236,7 @@ esp210.build.board=ESP8266_ESP210 esp210.upload.tool=esptool esp210.upload.maximum_data_size=81920 esp210.upload.wait_for_upload_port=true -esp210.upload.erase_cmd= +esp210.upload.erase_cmd=version esp210.serial.disableDTR=true esp210.serial.disableRTS=true esp210.build.mcu=esp8266 @@ -3191,6 +3261,10 @@ esp210.menu.exception.disabled.build.stdcpp_lib=-lstdc++ esp210.menu.exception.enabled=Enabled esp210.menu.exception.enabled.build.exception_flags=-fexceptions esp210.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +esp210.menu.ssl.all=All SSL ciphers (most compatible) +esp210.menu.ssl.all.build.sslflags= +esp210.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +esp210.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC esp210.upload.resetmethod=ck esp210.build.flash_mode=qio esp210.build.flash_flags=-DFLASHMODE_QIO @@ -3320,11 +3394,11 @@ esp210.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM esp210.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG esp210.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG esp210.menu.wipe.none=Only Sketch -esp210.menu.wipe.none.upload.erase_cmd= +esp210.menu.wipe.none.upload.erase_cmd=version esp210.menu.wipe.sdk=Sketch + WiFi Settings -esp210.menu.wipe.sdk.upload.erase_cmd=-ca "{build.rfcal_addr}" -cz 0x4000 +esp210.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 esp210.menu.wipe.all=All Flash Contents -esp210.menu.wipe.all.upload.erase_cmd=-ca 0x0 -cz "{build.flash_size_bytes}" +esp210.menu.wipe.all.upload.erase_cmd=erase_flash esp210.menu.baud.57600=57600 esp210.menu.baud.57600.upload.speed=57600 esp210.menu.baud.9600=9600 @@ -3351,7 +3425,7 @@ d1_mini.build.variant=d1_mini d1_mini.upload.tool=esptool d1_mini.upload.maximum_data_size=81920 d1_mini.upload.wait_for_upload_port=true -d1_mini.upload.erase_cmd= +d1_mini.upload.erase_cmd=version d1_mini.serial.disableDTR=true d1_mini.serial.disableRTS=true d1_mini.build.mcu=esp8266 @@ -3375,6 +3449,10 @@ d1_mini.menu.exception.disabled.build.stdcpp_lib=-lstdc++ d1_mini.menu.exception.enabled=Enabled d1_mini.menu.exception.enabled.build.exception_flags=-fexceptions d1_mini.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +d1_mini.menu.ssl.all=All SSL ciphers (most compatible) +d1_mini.menu.ssl.all.build.sslflags= +d1_mini.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +d1_mini.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC d1_mini.upload.resetmethod=nodemcu d1_mini.build.flash_mode=dio d1_mini.build.flash_flags=-DFLASHMODE_DIO @@ -3504,11 +3582,11 @@ d1_mini.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOO d1_mini.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG d1_mini.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG d1_mini.menu.wipe.none=Only Sketch -d1_mini.menu.wipe.none.upload.erase_cmd= +d1_mini.menu.wipe.none.upload.erase_cmd=version d1_mini.menu.wipe.sdk=Sketch + WiFi Settings -d1_mini.menu.wipe.sdk.upload.erase_cmd=-ca "{build.rfcal_addr}" -cz 0x4000 +d1_mini.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 d1_mini.menu.wipe.all=All Flash Contents -d1_mini.menu.wipe.all.upload.erase_cmd=-ca 0x0 -cz "{build.flash_size_bytes}" +d1_mini.menu.wipe.all.upload.erase_cmd=erase_flash d1_mini.menu.baud.921600=921600 d1_mini.menu.baud.921600.upload.speed=921600 d1_mini.menu.baud.9600=9600 @@ -3535,7 +3613,7 @@ d1_mini_pro.build.variant=d1_mini d1_mini_pro.upload.tool=esptool d1_mini_pro.upload.maximum_data_size=81920 d1_mini_pro.upload.wait_for_upload_port=true -d1_mini_pro.upload.erase_cmd= +d1_mini_pro.upload.erase_cmd=version d1_mini_pro.serial.disableDTR=true d1_mini_pro.serial.disableRTS=true d1_mini_pro.build.mcu=esp8266 @@ -3559,6 +3637,10 @@ d1_mini_pro.menu.exception.disabled.build.stdcpp_lib=-lstdc++ d1_mini_pro.menu.exception.enabled=Enabled d1_mini_pro.menu.exception.enabled.build.exception_flags=-fexceptions d1_mini_pro.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +d1_mini_pro.menu.ssl.all=All SSL ciphers (most compatible) +d1_mini_pro.menu.ssl.all.build.sslflags= +d1_mini_pro.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +d1_mini_pro.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC d1_mini_pro.upload.resetmethod=nodemcu d1_mini_pro.build.flash_mode=dio d1_mini_pro.build.flash_flags=-DFLASHMODE_DIO @@ -3671,11 +3753,11 @@ d1_mini_pro.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATERO d1_mini_pro.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG d1_mini_pro.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG d1_mini_pro.menu.wipe.none=Only Sketch -d1_mini_pro.menu.wipe.none.upload.erase_cmd= +d1_mini_pro.menu.wipe.none.upload.erase_cmd=version d1_mini_pro.menu.wipe.sdk=Sketch + WiFi Settings -d1_mini_pro.menu.wipe.sdk.upload.erase_cmd=-ca "{build.rfcal_addr}" -cz 0x4000 +d1_mini_pro.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 d1_mini_pro.menu.wipe.all=All Flash Contents -d1_mini_pro.menu.wipe.all.upload.erase_cmd=-ca 0x0 -cz "{build.flash_size_bytes}" +d1_mini_pro.menu.wipe.all.upload.erase_cmd=erase_flash d1_mini_pro.menu.baud.921600=921600 d1_mini_pro.menu.baud.921600.upload.speed=921600 d1_mini_pro.menu.baud.9600=9600 @@ -3702,7 +3784,7 @@ d1_mini_lite.build.variant=d1_mini d1_mini_lite.upload.tool=esptool d1_mini_lite.upload.maximum_data_size=81920 d1_mini_lite.upload.wait_for_upload_port=true -d1_mini_lite.upload.erase_cmd= +d1_mini_lite.upload.erase_cmd=version d1_mini_lite.serial.disableDTR=true d1_mini_lite.serial.disableRTS=true d1_mini_lite.build.mcu=esp8266 @@ -3726,6 +3808,10 @@ d1_mini_lite.menu.exception.disabled.build.stdcpp_lib=-lstdc++ d1_mini_lite.menu.exception.enabled=Enabled d1_mini_lite.menu.exception.enabled.build.exception_flags=-fexceptions d1_mini_lite.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +d1_mini_lite.menu.ssl.all=All SSL ciphers (most compatible) +d1_mini_lite.menu.ssl.all.build.sslflags= +d1_mini_lite.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +d1_mini_lite.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC d1_mini_lite.upload.resetmethod=nodemcu d1_mini_lite.build.flash_mode=dout d1_mini_lite.build.flash_flags=-DFLASHMODE_DOUT @@ -3895,11 +3981,11 @@ d1_mini_lite.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATER d1_mini_lite.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG d1_mini_lite.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG d1_mini_lite.menu.wipe.none=Only Sketch -d1_mini_lite.menu.wipe.none.upload.erase_cmd= +d1_mini_lite.menu.wipe.none.upload.erase_cmd=version d1_mini_lite.menu.wipe.sdk=Sketch + WiFi Settings -d1_mini_lite.menu.wipe.sdk.upload.erase_cmd=-ca "{build.rfcal_addr}" -cz 0x4000 +d1_mini_lite.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 d1_mini_lite.menu.wipe.all=All Flash Contents -d1_mini_lite.menu.wipe.all.upload.erase_cmd=-ca 0x0 -cz "{build.flash_size_bytes}" +d1_mini_lite.menu.wipe.all.upload.erase_cmd=erase_flash d1_mini_lite.menu.baud.921600=921600 d1_mini_lite.menu.baud.921600.upload.speed=921600 d1_mini_lite.menu.baud.9600=9600 @@ -3926,7 +4012,7 @@ d1.build.variant=d1 d1.upload.tool=esptool d1.upload.maximum_data_size=81920 d1.upload.wait_for_upload_port=true -d1.upload.erase_cmd= +d1.upload.erase_cmd=version d1.serial.disableDTR=true d1.serial.disableRTS=true d1.build.mcu=esp8266 @@ -3950,6 +4036,10 @@ d1.menu.exception.disabled.build.stdcpp_lib=-lstdc++ d1.menu.exception.enabled=Enabled d1.menu.exception.enabled.build.exception_flags=-fexceptions d1.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +d1.menu.ssl.all=All SSL ciphers (most compatible) +d1.menu.ssl.all.build.sslflags= +d1.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +d1.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC d1.upload.resetmethod=nodemcu d1.build.flash_mode=dio d1.build.flash_flags=-DFLASHMODE_DIO @@ -4079,11 +4169,11 @@ d1.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM.bui d1.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG d1.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG d1.menu.wipe.none=Only Sketch -d1.menu.wipe.none.upload.erase_cmd= +d1.menu.wipe.none.upload.erase_cmd=version d1.menu.wipe.sdk=Sketch + WiFi Settings -d1.menu.wipe.sdk.upload.erase_cmd=-ca "{build.rfcal_addr}" -cz 0x4000 +d1.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 d1.menu.wipe.all=All Flash Contents -d1.menu.wipe.all.upload.erase_cmd=-ca 0x0 -cz "{build.flash_size_bytes}" +d1.menu.wipe.all.upload.erase_cmd=erase_flash d1.menu.baud.921600=921600 d1.menu.baud.921600.upload.speed=921600 d1.menu.baud.9600=9600 @@ -4110,7 +4200,7 @@ espino.build.variant=espino espino.upload.tool=esptool espino.upload.maximum_data_size=81920 espino.upload.wait_for_upload_port=true -espino.upload.erase_cmd= +espino.upload.erase_cmd=version espino.serial.disableDTR=true espino.serial.disableRTS=true espino.build.mcu=esp8266 @@ -4134,6 +4224,10 @@ espino.menu.exception.disabled.build.stdcpp_lib=-lstdc++ espino.menu.exception.enabled=Enabled espino.menu.exception.enabled.build.exception_flags=-fexceptions espino.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +espino.menu.ssl.all=All SSL ciphers (most compatible) +espino.menu.ssl.all.build.sslflags= +espino.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +espino.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC espino.menu.ResetMethod.ck=ck espino.menu.ResetMethod.ck.upload.resetmethod=ck espino.menu.ResetMethod.nodemcu=nodemcu @@ -4266,11 +4360,11 @@ espino.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM espino.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG espino.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG espino.menu.wipe.none=Only Sketch -espino.menu.wipe.none.upload.erase_cmd= +espino.menu.wipe.none.upload.erase_cmd=version espino.menu.wipe.sdk=Sketch + WiFi Settings -espino.menu.wipe.sdk.upload.erase_cmd=-ca "{build.rfcal_addr}" -cz 0x4000 +espino.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 espino.menu.wipe.all=All Flash Contents -espino.menu.wipe.all.upload.erase_cmd=-ca 0x0 -cz "{build.flash_size_bytes}" +espino.menu.wipe.all.upload.erase_cmd=erase_flash espino.menu.baud.115200=115200 espino.menu.baud.115200.upload.speed=115200 espino.menu.baud.9600=9600 @@ -4297,7 +4391,7 @@ espinotee.build.variant=espinotee espinotee.upload.tool=esptool espinotee.upload.maximum_data_size=81920 espinotee.upload.wait_for_upload_port=true -espinotee.upload.erase_cmd= +espinotee.upload.erase_cmd=version espinotee.serial.disableDTR=true espinotee.serial.disableRTS=true espinotee.build.mcu=esp8266 @@ -4321,6 +4415,10 @@ espinotee.menu.exception.disabled.build.stdcpp_lib=-lstdc++ espinotee.menu.exception.enabled=Enabled espinotee.menu.exception.enabled.build.exception_flags=-fexceptions espinotee.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +espinotee.menu.ssl.all=All SSL ciphers (most compatible) +espinotee.menu.ssl.all.build.sslflags= +espinotee.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +espinotee.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC espinotee.upload.resetmethod=nodemcu espinotee.build.flash_mode=qio espinotee.build.flash_flags=-DFLASHMODE_QIO @@ -4450,11 +4548,11 @@ espinotee.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTA espinotee.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG espinotee.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG espinotee.menu.wipe.none=Only Sketch -espinotee.menu.wipe.none.upload.erase_cmd= +espinotee.menu.wipe.none.upload.erase_cmd=version espinotee.menu.wipe.sdk=Sketch + WiFi Settings -espinotee.menu.wipe.sdk.upload.erase_cmd=-ca "{build.rfcal_addr}" -cz 0x4000 +espinotee.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 espinotee.menu.wipe.all=All Flash Contents -espinotee.menu.wipe.all.upload.erase_cmd=-ca 0x0 -cz "{build.flash_size_bytes}" +espinotee.menu.wipe.all.upload.erase_cmd=erase_flash espinotee.menu.baud.115200=115200 espinotee.menu.baud.115200.upload.speed=115200 espinotee.menu.baud.9600=9600 @@ -4476,29 +4574,29 @@ espinotee.menu.baud.921600.upload.speed=921600 ############################################################## wifinfo.name=WifInfo -wifinfo.menu.ESPModule.ESP12.build.board=ESP8266_ESP12 -wifinfo.menu.ESPModule.ESP12.upload.maximum_size=1044464 -wifinfo.menu.ESPModule.ESP12.build.spiffs_pagesize=256 -wifinfo.menu.ESPModule.ESP12.build.flash_ld=eagle.flash.4m1m.ld -wifinfo.menu.ESPModule.ESP07192.build.spiffs_blocksize=4096 -wifinfo.menu.ESPModule.ESP07192.build.spiffs_end=0xFB000 -wifinfo.menu.ESPModule.ESP12=ESP12 (4M/1M SPIFFS) -wifinfo.menu.ESPModule.ESP12.build.spiffs_start=0x300000 -wifinfo.menu.ESPModule.ESP12.build.spiffs_end=0x3FB000 -wifinfo.menu.ESPModule.ESP07192.build.spiffs_start=0xCB000 -wifinfo.menu.ESPModule.ESP07192.build.board=ESP8266_ESP07 -wifinfo.menu.ESPModule.ESP12.build.spiffs_blocksize=8192 -wifinfo.menu.ESPModule.ESP12.build.flash_size=4M wifinfo.build.board=WIFINFO wifinfo.build.variant=wifinfo -wifinfo.menu.ESPModule.ESP07192.build.flash_ld=eagle.flash.1m192.ld -wifinfo.menu.ESPModule.ESP07192.build.flash_size=1M wifinfo.menu.ESPModule.ESP07192=ESP07 (1M/192K SPIFFS) +wifinfo.menu.ESPModule.ESP07192.build.board=ESP8266_ESP07 +wifinfo.menu.ESPModule.ESP07192.build.flash_size=1M +wifinfo.menu.ESPModule.ESP07192.build.flash_ld=eagle.flash.1m192.ld +wifinfo.menu.ESPModule.ESP07192.build.spiffs_start=0xCB000 +wifinfo.menu.ESPModule.ESP07192.build.spiffs_end=0xFB000 +wifinfo.menu.ESPModule.ESP07192.build.spiffs_blocksize=4096 wifinfo.menu.ESPModule.ESP07192.upload.maximum_size=827376 +wifinfo.menu.ESPModule.ESP12=ESP12 (4M/1M SPIFFS) +wifinfo.menu.ESPModule.ESP12.build.board=ESP8266_ESP12 +wifinfo.menu.ESPModule.ESP12.build.flash_size=4M +wifinfo.menu.ESPModule.ESP12.build.flash_ld=eagle.flash.4m1m.ld +wifinfo.menu.ESPModule.ESP12.build.spiffs_start=0x300000 +wifinfo.menu.ESPModule.ESP12.build.spiffs_end=0x3FB000 +wifinfo.menu.ESPModule.ESP12.build.spiffs_blocksize=8192 +wifinfo.menu.ESPModule.ESP12.build.spiffs_pagesize=256 +wifinfo.menu.ESPModule.ESP12.upload.maximum_size=1044464 wifinfo.upload.tool=esptool wifinfo.upload.maximum_data_size=81920 wifinfo.upload.wait_for_upload_port=true -wifinfo.upload.erase_cmd= +wifinfo.upload.erase_cmd=version wifinfo.serial.disableDTR=true wifinfo.serial.disableRTS=true wifinfo.build.mcu=esp8266 @@ -4522,6 +4620,10 @@ wifinfo.menu.exception.disabled.build.stdcpp_lib=-lstdc++ wifinfo.menu.exception.enabled=Enabled wifinfo.menu.exception.enabled.build.exception_flags=-fexceptions wifinfo.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +wifinfo.menu.ssl.all=All SSL ciphers (most compatible) +wifinfo.menu.ssl.all.build.sslflags= +wifinfo.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +wifinfo.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC wifinfo.upload.resetmethod=nodemcu wifinfo.build.flash_mode=qio wifinfo.build.flash_flags=-DFLASHMODE_QIO @@ -4694,11 +4796,11 @@ wifinfo.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOO wifinfo.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG wifinfo.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG wifinfo.menu.wipe.none=Only Sketch -wifinfo.menu.wipe.none.upload.erase_cmd= +wifinfo.menu.wipe.none.upload.erase_cmd=version wifinfo.menu.wipe.sdk=Sketch + WiFi Settings -wifinfo.menu.wipe.sdk.upload.erase_cmd=-ca "{build.rfcal_addr}" -cz 0x4000 +wifinfo.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 wifinfo.menu.wipe.all=All Flash Contents -wifinfo.menu.wipe.all.upload.erase_cmd=-ca 0x0 -cz "{build.flash_size_bytes}" +wifinfo.menu.wipe.all.upload.erase_cmd=erase_flash wifinfo.menu.baud.115200=115200 wifinfo.menu.baud.115200.upload.speed=115200 wifinfo.menu.baud.9600=9600 @@ -4720,23 +4822,23 @@ wifinfo.menu.baud.921600.upload.speed=921600 ############################################################## arduino-esp8266.name=Arduino -arduino-esp8266.menu.BoardModel.starottodeved.build.board=ESP8266_ARDUINO_STAR_OTTO -arduino-esp8266.menu.BoardModel.primo.build.extra_flags=-DF_CRYSTAL=40000000 -DESP8266 -arduino-esp8266.menu.BoardModel.starottodeved.build.extra_flags=-DF_CRYSTAL=40000000 -DESP8266 -arduino-esp8266.menu.BoardModel.starottodeved.build.variant=arduino_uart -arduino-esp8266.menu.BoardModel.unowifideved.build.board=ESP8266_ARDUINO_UNOWIFI -arduino-esp8266.menu.BoardModel.unowifideved.build.extra_flags=-DF_CRYSTAL=40000000 -DESP8266 -arduino-esp8266.menu.BoardModel.primo=Primo -arduino-esp8266.menu.BoardModel.unowifideved.build.variant=arduino_uart -arduino-esp8266.menu.BoardModel.primo.build.variant=arduino_spi -arduino-esp8266.menu.BoardModel.starottodeved=Star OTTO arduino-esp8266.build.board=ESP8266_ARDUINO +arduino-esp8266.menu.BoardModel.primo=Primo arduino-esp8266.menu.BoardModel.primo.build.board=ESP8266_ARDUINO_PRIMO +arduino-esp8266.menu.BoardModel.primo.build.variant=arduino_spi +arduino-esp8266.menu.BoardModel.primo.build.extra_flags=-DF_CRYSTAL=40000000 -DESP8266 arduino-esp8266.menu.BoardModel.unowifideved=Uno WiFi +arduino-esp8266.menu.BoardModel.unowifideved.build.board=ESP8266_ARDUINO_UNOWIFI +arduino-esp8266.menu.BoardModel.unowifideved.build.variant=arduino_uart +arduino-esp8266.menu.BoardModel.unowifideved.build.extra_flags=-DF_CRYSTAL=40000000 -DESP8266 +arduino-esp8266.menu.BoardModel.starottodeved=Star OTTO +arduino-esp8266.menu.BoardModel.starottodeved.build.variant=arduino_uart +arduino-esp8266.menu.BoardModel.starottodeved.build.board=ESP8266_ARDUINO_STAR_OTTO +arduino-esp8266.menu.BoardModel.starottodeved.build.extra_flags=-DF_CRYSTAL=40000000 -DESP8266 arduino-esp8266.upload.tool=esptool arduino-esp8266.upload.maximum_data_size=81920 arduino-esp8266.upload.wait_for_upload_port=true -arduino-esp8266.upload.erase_cmd= +arduino-esp8266.upload.erase_cmd=version arduino-esp8266.serial.disableDTR=true arduino-esp8266.serial.disableRTS=true arduino-esp8266.build.mcu=esp8266 @@ -4761,6 +4863,10 @@ arduino-esp8266.menu.exception.disabled.build.stdcpp_lib=-lstdc++ arduino-esp8266.menu.exception.enabled=Enabled arduino-esp8266.menu.exception.enabled.build.exception_flags=-fexceptions arduino-esp8266.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +arduino-esp8266.menu.ssl.all=All SSL ciphers (most compatible) +arduino-esp8266.menu.ssl.all.build.sslflags= +arduino-esp8266.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +arduino-esp8266.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC arduino-esp8266.upload.resetmethod=ck arduino-esp8266.build.flash_mode=qio arduino-esp8266.build.flash_flags=-DFLASHMODE_QIO @@ -4890,11 +4996,11 @@ arduino-esp8266.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDA arduino-esp8266.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG arduino-esp8266.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG arduino-esp8266.menu.wipe.none=Only Sketch -arduino-esp8266.menu.wipe.none.upload.erase_cmd= +arduino-esp8266.menu.wipe.none.upload.erase_cmd=version arduino-esp8266.menu.wipe.sdk=Sketch + WiFi Settings -arduino-esp8266.menu.wipe.sdk.upload.erase_cmd=-ca "{build.rfcal_addr}" -cz 0x4000 +arduino-esp8266.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 arduino-esp8266.menu.wipe.all=All Flash Contents -arduino-esp8266.menu.wipe.all.upload.erase_cmd=-ca 0x0 -cz "{build.flash_size_bytes}" +arduino-esp8266.menu.wipe.all.upload.erase_cmd=erase_flash arduino-esp8266.menu.baud.115200=115200 arduino-esp8266.menu.baud.115200.upload.speed=115200 arduino-esp8266.menu.baud.9600=9600 @@ -4922,7 +5028,7 @@ gen4iod.build.variant=generic gen4iod.upload.tool=esptool gen4iod.upload.maximum_data_size=81920 gen4iod.upload.wait_for_upload_port=true -gen4iod.upload.erase_cmd= +gen4iod.upload.erase_cmd=version gen4iod.serial.disableDTR=true gen4iod.serial.disableRTS=true gen4iod.build.mcu=esp8266 @@ -4946,6 +5052,10 @@ gen4iod.menu.exception.disabled.build.stdcpp_lib=-lstdc++ gen4iod.menu.exception.enabled=Enabled gen4iod.menu.exception.enabled.build.exception_flags=-fexceptions gen4iod.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +gen4iod.menu.ssl.all=All SSL ciphers (most compatible) +gen4iod.menu.ssl.all.build.sslflags= +gen4iod.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +gen4iod.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC gen4iod.upload.resetmethod=nodemcu gen4iod.build.flash_mode=dio gen4iod.build.flash_flags=-DFLASHMODE_DIO @@ -5075,11 +5185,11 @@ gen4iod.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOO gen4iod.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG gen4iod.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG gen4iod.menu.wipe.none=Only Sketch -gen4iod.menu.wipe.none.upload.erase_cmd= +gen4iod.menu.wipe.none.upload.erase_cmd=version gen4iod.menu.wipe.sdk=Sketch + WiFi Settings -gen4iod.menu.wipe.sdk.upload.erase_cmd=-ca "{build.rfcal_addr}" -cz 0x4000 +gen4iod.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 gen4iod.menu.wipe.all=All Flash Contents -gen4iod.menu.wipe.all.upload.erase_cmd=-ca 0x0 -cz "{build.flash_size_bytes}" +gen4iod.menu.wipe.all.upload.erase_cmd=erase_flash gen4iod.menu.baud.115200=115200 gen4iod.menu.baud.115200.upload.speed=115200 gen4iod.menu.baud.9600=9600 @@ -5107,7 +5217,7 @@ oak.upload.maximum_size=1040368 oak.upload.tool=esptool oak.upload.maximum_data_size=81920 oak.upload.wait_for_upload_port=true -oak.upload.erase_cmd= +oak.upload.erase_cmd=version oak.serial.disableDTR=true oak.serial.disableRTS=true oak.build.mcu=esp8266 @@ -5131,6 +5241,10 @@ oak.menu.exception.disabled.build.stdcpp_lib=-lstdc++ oak.menu.exception.enabled=Enabled oak.menu.exception.enabled.build.exception_flags=-fexceptions oak.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +oak.menu.ssl.all=All SSL ciphers (most compatible) +oak.menu.ssl.all.build.sslflags= +oak.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +oak.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC oak.upload.resetmethod=none oak.build.flash_mode=dio oak.build.flash_flags=-DFLASHMODE_DIO @@ -5260,11 +5374,11 @@ oak.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOM.bu oak.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG oak.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG oak.menu.wipe.none=Only Sketch -oak.menu.wipe.none.upload.erase_cmd= +oak.menu.wipe.none.upload.erase_cmd=version oak.menu.wipe.sdk=Sketch + WiFi Settings -oak.menu.wipe.sdk.upload.erase_cmd=-ca "{build.rfcal_addr}" -cz 0x4000 +oak.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 oak.menu.wipe.all=All Flash Contents -oak.menu.wipe.all.upload.erase_cmd=-ca 0x0 -cz "{build.flash_size_bytes}" +oak.menu.wipe.all.upload.erase_cmd=erase_flash oak.menu.baud.921600=921600 oak.menu.baud.921600.upload.speed=921600 oak.menu.baud.9600=9600 @@ -5291,7 +5405,7 @@ wifiduino.build.variant=wifiduino wifiduino.upload.tool=esptool wifiduino.upload.maximum_data_size=81920 wifiduino.upload.wait_for_upload_port=true -wifiduino.upload.erase_cmd= +wifiduino.upload.erase_cmd=version wifiduino.serial.disableDTR=true wifiduino.serial.disableRTS=true wifiduino.build.mcu=esp8266 @@ -5315,6 +5429,10 @@ wifiduino.menu.exception.disabled.build.stdcpp_lib=-lstdc++ wifiduino.menu.exception.enabled=Enabled wifiduino.menu.exception.enabled.build.exception_flags=-fexceptions wifiduino.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +wifiduino.menu.ssl.all=All SSL ciphers (most compatible) +wifiduino.menu.ssl.all.build.sslflags= +wifiduino.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +wifiduino.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC wifiduino.upload.resetmethod=nodemcu wifiduino.build.flash_mode=dio wifiduino.build.flash_flags=-DFLASHMODE_DIO @@ -5444,11 +5562,11 @@ wifiduino.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTA wifiduino.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG wifiduino.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG wifiduino.menu.wipe.none=Only Sketch -wifiduino.menu.wipe.none.upload.erase_cmd= +wifiduino.menu.wipe.none.upload.erase_cmd=version wifiduino.menu.wipe.sdk=Sketch + WiFi Settings -wifiduino.menu.wipe.sdk.upload.erase_cmd=-ca "{build.rfcal_addr}" -cz 0x4000 +wifiduino.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 wifiduino.menu.wipe.all=All Flash Contents -wifiduino.menu.wipe.all.upload.erase_cmd=-ca 0x0 -cz "{build.flash_size_bytes}" +wifiduino.menu.wipe.all.upload.erase_cmd=erase_flash wifiduino.menu.baud.921600=921600 wifiduino.menu.baud.921600.upload.speed=921600 wifiduino.menu.baud.9600=9600 @@ -5475,7 +5593,7 @@ wifi_slot.build.variant=wifi_slot wifi_slot.upload.tool=esptool wifi_slot.upload.maximum_data_size=81920 wifi_slot.upload.wait_for_upload_port=true -wifi_slot.upload.erase_cmd= +wifi_slot.upload.erase_cmd=version wifi_slot.serial.disableDTR=true wifi_slot.serial.disableRTS=true wifi_slot.build.mcu=esp8266 @@ -5499,6 +5617,10 @@ wifi_slot.menu.exception.disabled.build.stdcpp_lib=-lstdc++ wifi_slot.menu.exception.enabled=Enabled wifi_slot.menu.exception.enabled.build.exception_flags=-fexceptions wifi_slot.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +wifi_slot.menu.ssl.all=All SSL ciphers (most compatible) +wifi_slot.menu.ssl.all.build.sslflags= +wifi_slot.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +wifi_slot.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC wifi_slot.upload.resetmethod=nodemcu wifi_slot.menu.FlashFreq.40=40MHz wifi_slot.menu.FlashFreq.40.build.flash_freq=40 @@ -5728,11 +5850,11 @@ wifi_slot.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTA wifi_slot.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG wifi_slot.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG wifi_slot.menu.wipe.none=Only Sketch -wifi_slot.menu.wipe.none.upload.erase_cmd= +wifi_slot.menu.wipe.none.upload.erase_cmd=version wifi_slot.menu.wipe.sdk=Sketch + WiFi Settings -wifi_slot.menu.wipe.sdk.upload.erase_cmd=-ca "{build.rfcal_addr}" -cz 0x4000 +wifi_slot.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 wifi_slot.menu.wipe.all=All Flash Contents -wifi_slot.menu.wipe.all.upload.erase_cmd=-ca 0x0 -cz "{build.flash_size_bytes}" +wifi_slot.menu.wipe.all.upload.erase_cmd=erase_flash wifi_slot.menu.baud.115200=115200 wifi_slot.menu.baud.115200.upload.speed=115200 wifi_slot.menu.baud.9600=9600 @@ -5759,7 +5881,7 @@ wiolink.build.variant=wiolink wiolink.upload.tool=esptool wiolink.upload.maximum_data_size=81920 wiolink.upload.wait_for_upload_port=true -wiolink.upload.erase_cmd= +wiolink.upload.erase_cmd=version wiolink.serial.disableDTR=true wiolink.serial.disableRTS=true wiolink.build.mcu=esp8266 @@ -5783,6 +5905,10 @@ wiolink.menu.exception.disabled.build.stdcpp_lib=-lstdc++ wiolink.menu.exception.enabled=Enabled wiolink.menu.exception.enabled.build.exception_flags=-fexceptions wiolink.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +wiolink.menu.ssl.all=All SSL ciphers (most compatible) +wiolink.menu.ssl.all.build.sslflags= +wiolink.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +wiolink.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC wiolink.upload.resetmethod=nodemcu wiolink.build.flash_mode=qio wiolink.build.flash_flags=-DFLASHMODE_QIO @@ -5912,11 +6038,11 @@ wiolink.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOO wiolink.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG wiolink.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG wiolink.menu.wipe.none=Only Sketch -wiolink.menu.wipe.none.upload.erase_cmd= +wiolink.menu.wipe.none.upload.erase_cmd=version wiolink.menu.wipe.sdk=Sketch + WiFi Settings -wiolink.menu.wipe.sdk.upload.erase_cmd=-ca "{build.rfcal_addr}" -cz 0x4000 +wiolink.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 wiolink.menu.wipe.all=All Flash Contents -wiolink.menu.wipe.all.upload.erase_cmd=-ca 0x0 -cz "{build.flash_size_bytes}" +wiolink.menu.wipe.all.upload.erase_cmd=erase_flash wiolink.menu.baud.115200=115200 wiolink.menu.baud.115200.upload.speed=115200 wiolink.menu.baud.9600=9600 @@ -5943,7 +6069,7 @@ espectro.build.variant=espectro espectro.upload.tool=esptool espectro.upload.maximum_data_size=81920 espectro.upload.wait_for_upload_port=true -espectro.upload.erase_cmd= +espectro.upload.erase_cmd=version espectro.serial.disableDTR=true espectro.serial.disableRTS=true espectro.build.mcu=esp8266 @@ -5967,6 +6093,10 @@ espectro.menu.exception.disabled.build.stdcpp_lib=-lstdc++ espectro.menu.exception.enabled=Enabled espectro.menu.exception.enabled.build.exception_flags=-fexceptions espectro.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +espectro.menu.ssl.all=All SSL ciphers (most compatible) +espectro.menu.ssl.all.build.sslflags= +espectro.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +espectro.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC espectro.upload.resetmethod=nodemcu espectro.build.flash_mode=dio espectro.build.flash_flags=-DFLASHMODE_DIO @@ -6096,11 +6226,11 @@ espectro.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAO espectro.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG espectro.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG espectro.menu.wipe.none=Only Sketch -espectro.menu.wipe.none.upload.erase_cmd= +espectro.menu.wipe.none.upload.erase_cmd=version espectro.menu.wipe.sdk=Sketch + WiFi Settings -espectro.menu.wipe.sdk.upload.erase_cmd=-ca "{build.rfcal_addr}" -cz 0x4000 +espectro.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 espectro.menu.wipe.all=All Flash Contents -espectro.menu.wipe.all.upload.erase_cmd=-ca 0x0 -cz "{build.flash_size_bytes}" +espectro.menu.wipe.all.upload.erase_cmd=erase_flash espectro.menu.baud.115200=115200 espectro.menu.baud.115200.upload.speed=115200 espectro.menu.baud.9600=9600 @@ -6119,3 +6249,4 @@ espectro.menu.baud.512000.windows=512000 espectro.menu.baud.512000.upload.speed=512000 espectro.menu.baud.921600=921600 espectro.menu.baud.921600.upload.speed=921600 + diff --git a/arduino/version 2.5.0/platform.txt b/arduino/version 2.5.2/platform.txt similarity index 50% rename from arduino/version 2.5.0/platform.txt rename to arduino/version 2.5.2/platform.txt index 8e9d8b51c..ed4f2a1ba 100644 --- a/arduino/version 2.5.0/platform.txt +++ b/arduino/version 2.5.2/platform.txt @@ -3,14 +3,20 @@ # ------------------------------ # For more info: -# https://github.com/arduino/Arduino/wiki/Arduino-IDE-1.5---3rd-party-Hardware-specification +# https://github.com/arduino/Arduino/wiki/Arduino-IDE-1.5-3rd-party-Hardware-specification + +name=ESP8266 Boards (2.5.2) +version=2.5.2 + +# These will be removed by the packager script when doing a JSON release + + -name=ESP8266 Boards (2.5.0) -version=2.5.0 -runtime.tools.xtensa-lx106-elf-gcc.path={runtime.platform.path}/tools/xtensa-lx106-elf -runtime.tools.esptool.path={runtime.platform.path}/tools/esptool runtime.tools.signing={runtime.platform.path}/tools/signing.py +runtime.tools.elf2bin={runtime.platform.path}/tools/elf2bin.py +runtime.tools.makecorever={runtime.platform.path}/tools/makecorever.py +runtime.tools.eboot={runtime.platform.path}/bootloaders/eboot/eboot.elf compiler.warning_flags=-w compiler.warning_flags.none=-w @@ -24,31 +30,35 @@ build.lwip_flags=-DLWIP_OPEN_SRC build.vtable_flags=-DVTABLES_IN_FLASH +build.sslflags= + build.exception_flags=-fno-exceptions build.stdcpp_lib=-lstdc++ #build.float=-u _printf_float -u _scanf_float build.float= build.led= +build.sdk=NONOSDK221 compiler.path={runtime.tools.xtensa-lx106-elf-gcc.path}/bin/ compiler.sdk.path={runtime.platform.path}/tools/sdk + compiler.libc.path={runtime.platform.path}/tools/sdk/libc/xtensa-lx106-elf compiler.cpreprocessor.flags=-D__ets__ -DICACHE_FLASH -U__STRICT_ANSI__ "-I{compiler.sdk.path}/include" "-I{compiler.sdk.path}/{build.lwip_include}" "-I{compiler.libc.path}/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 {build.exception_flags} +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 {build.exception_flags} {build.sslflags} 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 app_entry {build.float} -Wl,-static "-L{compiler.sdk.path}/lib" "-L{compiler.sdk.path}/ld" "-L{compiler.libc.path}/lib" "-T{build.flash_ld}" -Wl,--gc-sections -Wl,-wrap,system_restart_local -Wl,-wrap,spi_flash_read +compiler.c.elf.flags=-g {compiler.warning_flags} -Os -nostdlib -Wl,--no-check-sections -u app_entry {build.float} -Wl,-static "-L{compiler.sdk.path}/lib" "-L{compiler.sdk.path}/lib/{build.sdk}" "-L{compiler.sdk.path}/ld" "-L{compiler.libc.path}/lib" "-T{build.flash_ld}" -Wl,--gc-sections -Wl,-wrap,system_restart_local -Wl,-wrap,spi_flash_read compiler.c.elf.cmd=xtensa-lx106-elf-gcc compiler.c.elf.libs=-lhal -lphy -lpp -lnet80211 {build.lwip_lib} -lwpa -lcrypto -lmain -lwps -lbearssl -laxtls -lespnow -lsmartconfig -lairkiss -lwpa2 {build.stdcpp_lib} -lm -lc -lgcc compiler.cpp.cmd=xtensa-lx106-elf-g++ -compiler.cpp.flags=-c {compiler.warning_flags} -Os -g -mlongcalls -mtext-section-literals -fno-rtti -falign-functions=4 -std=c++11 -MMD -ffunction-sections -fdata-sections {build.exception_flags} +compiler.cpp.flags=-c {compiler.warning_flags} -Os -g -mlongcalls -mtext-section-literals -fno-rtti -falign-functions=4 -std=c++11 -MMD -ffunction-sections -fdata-sections {build.exception_flags} {build.sslflags} compiler.as.cmd=xtensa-lx106-elf-as @@ -60,9 +70,6 @@ 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 @@ -77,26 +84,20 @@ compiler.elf2hex.extra_flags= ## generate file with git version number ## needs bash, git, and echo -recipe.hooks.core.prebuild.1.pattern=python "{runtime.tools.signing}" --mode header --publickey "{build.source.path}/public.key" --out "{build.path}/core/Updater_Signing.h" -recipe.hooks.core.prebuild.2.pattern=bash -c "mkdir -p {build.path}/core && echo \#define ARDUINO_ESP8266_GIT_VER 0x`git --git-dir {runtime.platform.path}/.git rev-parse --short=8 HEAD 2>/dev/null || echo ffffffff` >{build.path}/core/core_version.h" -recipe.hooks.core.prebuild.3.pattern=bash -c "mkdir -p {build.path}/core && echo \#define ARDUINO_ESP8266_GIT_DESC `cd "{runtime.platform.path}"; git describe --tags 2>/dev/null || echo unix-{version}` >>{build.path}/core/core_version.h" +recipe.hooks.core.prebuild.1.pattern="{runtime.tools.python.path}/python" "{runtime.tools.signing}" --mode header --publickey "{build.source.path}/public.key" --out "{build.path}/core/Updater_Signing.h" -## windows-compatible version without git -recipe.hooks.core.prebuild.1.pattern.windows=cmd.exe /c rem cannot sign on windows -recipe.hooks.core.prebuild.2.pattern.windows=cmd.exe /c mkdir {build.path}\core & (echo #define ARDUINO_ESP8266_GIT_VER 0x00000000 & echo #define ARDUINO_ESP8266_GIT_DESC win-{version} ) > {build.path}\core\core_version.h -recipe.hooks.core.prebuild.3.pattern.windows=cmd.exe /c if exist {build.source.path}\public.key echo #error Cannot automatically build signed binaries on Windows > {build.path}\core\Updater_Signing.h ## Build the app.ld linker file recipe.hooks.linking.prelink.1.pattern="{compiler.path}{compiler.c.cmd}" -CC -E -P {build.vtable_flags} "{runtime.platform.path}/tools/sdk/ld/eagle.app.v6.common.ld.h" -o "{build.path}/local.eagle.app.v6.common.ld" ## 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}" {build.led} {build.flash_flags} {compiler.c.extra_flags} {build.extra_flags} {includes} "{source_file}" -o "{object_file}" +recipe.c.o.pattern="{compiler.path}{compiler.c.cmd}" {compiler.cpreprocessor.flags} {compiler.c.flags} -D{build.sdk}=1 -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}" {build.led} {build.flash_flags} {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}" {build.led} {build.flash_flags} {compiler.cpp.extra_flags} {build.extra_flags} {includes} "{source_file}" -o "{object_file}" +recipe.cpp.o.pattern="{compiler.path}{compiler.cpp.cmd}" {compiler.cpreprocessor.flags} {compiler.cpp.flags} -D{build.sdk}=1 -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}" {build.led} {build.flash_flags} {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}" {build.led} {build.flash_flags} {compiler.c.extra_flags} {build.extra_flags} {includes} "{source_file}" -o "{object_file}" +recipe.S.o.pattern="{compiler.path}{compiler.c.cmd}" {compiler.cpreprocessor.flags} {compiler.S.flags} -D{build.sdk}=1 -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}" {build.led} {build.flash_flags} {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} "{archive_file_path}" "{object_file}" @@ -108,14 +109,8 @@ recipe.c.combine.pattern="{compiler.path}{compiler.c.elf.cmd}" {build.exception_ 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.1.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 -recipe.objcopy.hex.2.pattern=python "{runtime.tools.signing}" --mode sign --privatekey "{build.source.path}/private.key" --bin "{build.path}/{build.project_name}.bin" --out "{build.path}/{build.project_name}.bin.signed" - -# No signing on Windows -recipe.objcopy.hex.1.pattern.windows="{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 -recipe.objcopy.hex.2.pattern.windows= +recipe.objcopy.hex.1.pattern="{runtime.tools.python.path}/python" "{runtime.tools.elf2bin}" --eboot "{runtime.tools.eboot}" --app "{build.path}/{build.project_name}.elf" --flash_mode {build.flash_mode} --flash_freq {build.flash_freq} --flash_size {build.flash_size} --path "{runtime.tools.xtensa-lx106-elf-gcc.path}/bin" --out "{build.path}/{build.project_name}.bin" +recipe.objcopy.hex.2.pattern="{runtime.tools.python.path}/python" "{runtime.tools.signing}" --mode sign --privatekey "{build.source.path}/private.key" --bin "{build.path}/{build.project_name}.bin" --out "{build.path}/{build.project_name}.bin.signed" ## Save hex recipe.output.tmp_file={build.project_name}.bin @@ -123,22 +118,30 @@ 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=^(?:\.irom0\.text|\.text|\.text1|\.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.path= +# Because the variable expansion doesn't allow one tool to find another, the following lines +# will point to "{runtime.platform.path}/tools/python/python" in GIT and +# "{runtime.tools.python.path}/python" for JSON board manager releases. +tools.esptool.cmd={runtime.tools.python.path}/python +tools.esptool.network_cmd={runtime.tools.python.path}/python + + tools.esptool.upload.protocol=esp -tools.esptool.upload.params.verbose=-vv +tools.esptool.upload.params.verbose=--trace tools.esptool.upload.params.quiet= -tools.esptool.upload.pattern="{path}/{cmd}" {upload.verbose} -cd {upload.resetmethod} -cb {upload.speed} -cp "{serial.port}" {upload.erase_cmd} -ca 0x00000 -cf "{build.path}/{build.project_name}.bin" + +# First, potentially perform an erase or nothing +# Next, do the binary upload +# Combined in one rule because Arduino doesn't suport upload.1.pattern/upload.3.pattern +tools.esptool.upload.pattern="{cmd}" "{runtime.platform.path}/tools/upload.py" --chip esp8266 --port "{serial.port}" --baud "{upload.speed}" "{upload.verbose}" {upload.erase_cmd} --end --chip esp8266 --port "{serial.port}" --baud "{upload.speed}" "{upload.verbose}" write_flash 0x0 "{build.path}/{build.project_name}.bin" --end + 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 @@ -152,4 +155,3 @@ tools.espupload.upload.protocol=espupload tools.espupload.upload.params.verbose= tools.espupload.upload.params.quiet= tools.espupload.upload.pattern="{cmd}" "{path}/espupload.py" -f "{build.path}/{build.project_name}.bin" - diff --git a/include/dummy.txt b/include/dummy.txt new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/include/dummy.txt @@ -0,0 +1 @@ + diff --git a/lib/Adafruit_SGP30-1.0.0.13/.github/ISSUE_TEMPLATE.md b/lib/Adafruit_SGP30-1.0.3/.github/ISSUE_TEMPLATE.md similarity index 100% rename from lib/Adafruit_SGP30-1.0.0.13/.github/ISSUE_TEMPLATE.md rename to lib/Adafruit_SGP30-1.0.3/.github/ISSUE_TEMPLATE.md diff --git a/lib/Adafruit_SGP30-1.0.0.13/.github/PULL_REQUEST_TEMPLATE.md b/lib/Adafruit_SGP30-1.0.3/.github/PULL_REQUEST_TEMPLATE.md similarity index 100% rename from lib/Adafruit_SGP30-1.0.0.13/.github/PULL_REQUEST_TEMPLATE.md rename to lib/Adafruit_SGP30-1.0.3/.github/PULL_REQUEST_TEMPLATE.md diff --git a/lib/Adafruit_SGP30-1.0.0.13/.gitignore b/lib/Adafruit_SGP30-1.0.3/.gitignore similarity index 100% rename from lib/Adafruit_SGP30-1.0.0.13/.gitignore rename to lib/Adafruit_SGP30-1.0.3/.gitignore diff --git a/lib/Adafruit_SGP30-1.0.0.13/.travis.yml b/lib/Adafruit_SGP30-1.0.3/.travis.yml similarity index 100% rename from lib/Adafruit_SGP30-1.0.0.13/.travis.yml rename to lib/Adafruit_SGP30-1.0.3/.travis.yml diff --git a/lib/Adafruit_SGP30-1.0.0.13/Adafruit_SGP30.cpp b/lib/Adafruit_SGP30-1.0.3/Adafruit_SGP30.cpp similarity index 83% rename from lib/Adafruit_SGP30-1.0.0.13/Adafruit_SGP30.cpp rename to lib/Adafruit_SGP30-1.0.3/Adafruit_SGP30.cpp index b2ccbe8da..ce6116863 100644 --- a/lib/Adafruit_SGP30-1.0.0.13/Adafruit_SGP30.cpp +++ b/lib/Adafruit_SGP30-1.0.3/Adafruit_SGP30.cpp @@ -37,7 +37,7 @@ //#define I2C_DEBUG /**************************************************************************/ -/*! +/*! @brief Instantiates a new SGP30 class */ /**************************************************************************/ @@ -45,7 +45,7 @@ Adafruit_SGP30::Adafruit_SGP30() { } /**************************************************************************/ -/*! +/*! @brief Setups the hardware and detects a valid SGP30. Initializes I2C then reads the serialnumber and checks that we are talking to an SGP30 @param theWire Optional pointer to I2C interface, otherwise use Wire @@ -60,31 +60,32 @@ boolean Adafruit_SGP30::begin(TwoWire *theWire) { _i2c = theWire; } - _i2c->begin(); +// assume i2c initialized already to avoid resetting clock stretching +// _i2c->begin(); + - uint8_t command[2]; command[0] = 0x36; command[1] = 0x82; - if (! readWordFromCommand(command, 2, 10, serialnumber, 3)) + if (! readWordFromCommand(command, 2, 10, serialnumber, 3)) return false; uint16_t featureset; command[0] = 0x20; command[1] = 0x2F; - if (! readWordFromCommand(command, 2, 10, &featureset, 1)) + if (! readWordFromCommand(command, 2, 10, &featureset, 1)) return false; //Serial.print("Featureset 0x"); Serial.println(featureset, HEX); - if (featureset != SGP30_FEATURESET) + if (featureset != SGP30_FEATURESET) return false; - if (! IAQinit()) + if (! IAQinit()) return false; return true; } /**************************************************************************/ -/*! +/*! @brief Commands the sensor to begin the IAQ algorithm. Must be called after startup. @returns True if command completed successfully, false if something went wrong! */ @@ -97,7 +98,7 @@ boolean Adafruit_SGP30::IAQinit(void) { } /**************************************************************************/ -/*! +/*! @brief Commands the sensor to take a single eCO2/VOC measurement. Places results in {@link TVOC} and {@link eCO2} @returns True if command completed successfully, false if something went wrong! */ @@ -113,9 +114,9 @@ boolean Adafruit_SGP30::IAQmeasure(void) { eCO2 = reply[0]; return true; } - + /**************************************************************************/ -/*! +/*! @brief Request baseline calibration values for both CO2 and TVOC IAQ calculations. Places results in parameter memory locaitons. @param eco2_base A pointer to a uint16_t which we will save the calibration value to @param tvoc_base A pointer to a uint16_t which we will save the calibration value to @@ -135,7 +136,7 @@ boolean Adafruit_SGP30::getIAQBaseline(uint16_t *eco2_base, uint16_t *tvoc_base) } /**************************************************************************/ -/*! +/*! @brief Assign baseline calibration values for both CO2 and TVOC IAQ calculations. @param eco2_base A uint16_t which we will save the calibration value from @param tvoc_base A uint16_t which we will save the calibration value from @@ -157,7 +158,30 @@ boolean Adafruit_SGP30::setIAQBaseline(uint16_t eco2_base, uint16_t tvoc_base) { } /**************************************************************************/ -/*! +/*! + @brief Set the absolute humidity value [mg/m^3] for compensation to increase precision of TVOC and eCO2. + @param absolute_humidity A uint32_t [mg/m^3] which we will be used for compensation. If the absolute humidity is set to zero, humidity compensation will be disabled. + @returns True if command completed successfully, false if something went wrong! +*/ +/**************************************************************************/ +boolean Adafruit_SGP30::setHumidity(uint32_t absolute_humidity) { + if (absolute_humidity > 256000) { + return false; + } + + uint16_t ah_scaled = (uint16_t)(((uint64_t)absolute_humidity * 256 * 16777) >> 24); + uint8_t command[5]; + command[0] = 0x20; + command[1] = 0x61; + command[2] = ah_scaled >> 8; + command[3] = ah_scaled & 0xFF; + command[4] = generateCRC(command+2, 2); + + return readWordFromCommand(command, 5, 10); +} + +/**************************************************************************/ +/*! @brief I2C low level interfacing */ /**************************************************************************/ @@ -186,16 +210,16 @@ boolean Adafruit_SGP30::readWordFromCommand(uint8_t command[], uint8_t commandLe delay(delayms); - if (readlen == 0) + if (readlen == 0) return true; uint8_t replylen = readlen * (SGP30_WORD_LEN +1); - if (_i2c->requestFrom(_i2caddr, replylen) != replylen) + if (_i2c->requestFrom(_i2caddr, replylen) != replylen) return false; uint8_t replybuffer[replylen]; #ifdef I2C_DEBUG Serial.print("\t\t<- "); -#endif +#endif for (uint8_t i=0; iread(); #ifdef I2C_DEBUG diff --git a/lib/Adafruit_SGP30-1.0.0.13/Adafruit_SGP30.h b/lib/Adafruit_SGP30-1.0.3/Adafruit_SGP30.h similarity index 97% rename from lib/Adafruit_SGP30-1.0.0.13/Adafruit_SGP30.h rename to lib/Adafruit_SGP30-1.0.3/Adafruit_SGP30.h index cc95fa54b..6f27aad04 100644 --- a/lib/Adafruit_SGP30-1.0.0.13/Adafruit_SGP30.h +++ b/lib/Adafruit_SGP30-1.0.3/Adafruit_SGP30.h @@ -42,6 +42,7 @@ class Adafruit_SGP30 { boolean getIAQBaseline(uint16_t *eco2_base, uint16_t *tvoc_base); boolean setIAQBaseline(uint16_t eco2_base, uint16_t tvoc_base); + boolean setHumidity(uint32_t absolute_humidity); /** * The last measurement of the IAQ-calculated Total Volatile Organic Compounds in ppb. This value is set when you call {@link IAQmeasure()} diff --git a/lib/Adafruit_SGP30-1.0.0.13/README.md b/lib/Adafruit_SGP30-1.0.3/README.md similarity index 100% rename from lib/Adafruit_SGP30-1.0.0.13/README.md rename to lib/Adafruit_SGP30-1.0.3/README.md diff --git a/lib/Adafruit_SGP30-1.0.0.13/examples/sgp30test/sgp30test.ino b/lib/Adafruit_SGP30-1.0.3/examples/sgp30test/sgp30test.ino similarity index 58% rename from lib/Adafruit_SGP30-1.0.0.13/examples/sgp30test/sgp30test.ino rename to lib/Adafruit_SGP30-1.0.3/examples/sgp30test/sgp30test.ino index 6b9b3ea05..b7ff8a70c 100644 --- a/lib/Adafruit_SGP30-1.0.0.13/examples/sgp30test/sgp30test.ino +++ b/lib/Adafruit_SGP30-1.0.3/examples/sgp30test/sgp30test.ino @@ -3,6 +3,17 @@ Adafruit_SGP30 sgp; +/* return absolute humidity [mg/m^3] with approximation formula +* @param temperature [°C] +* @param humidity [%RH] +*/ +uint32_t getAbsoluteHumidity(float temperature, float humidity) { + // approximation formula from Sensirion SGP30 Driver Integration chapter 3.15 + const float absoluteHumidity = 216.7f * ((humidity / 100.0f) * 6.112f * exp((17.62f * temperature) / (243.12f + temperature)) / (273.15f + temperature)); // [g/m^3] + const uint32_t absoluteHumidityScaled = static_cast(1000.0f * absoluteHumidity); // [mg/m^3] + return absoluteHumidityScaled; +} + void setup() { Serial.begin(9600); Serial.println("SGP30 test"); @@ -22,6 +33,11 @@ void setup() { int counter = 0; void loop() { + // If you have a temperature / humidity sensor, you can set the absolute humidity to enable the humditiy compensation for the air quality signals + //float temperature = 22.1; // [°C] + //float humidity = 45.2; // [%RH] + //sgp.setHumidity(getAbsoluteHumidity(temperature, humidity)); + if (! sgp.IAQmeasure()) { Serial.println("Measurement failed"); return; diff --git a/lib/Adafruit_SGP30-1.0.0.13/library.properties b/lib/Adafruit_SGP30-1.0.3/library.properties similarity index 95% rename from lib/Adafruit_SGP30-1.0.0.13/library.properties rename to lib/Adafruit_SGP30-1.0.3/library.properties index 3520b4d38..6c86464d1 100644 --- a/lib/Adafruit_SGP30-1.0.0.13/library.properties +++ b/lib/Adafruit_SGP30-1.0.3/library.properties @@ -1,5 +1,5 @@ name=Adafruit SGP30 Sensor -version=1.0.2 +version=1.0.3 author=Adafruit maintainer=Adafruit sentence=This is an Arduino library for the Adafruit SGP30 Gas / Air Quality Sensor diff --git a/lib/Adafruit_SGP30-1.0.0.13/license.txt b/lib/Adafruit_SGP30-1.0.3/license.txt similarity index 100% rename from lib/Adafruit_SGP30-1.0.0.13/license.txt rename to lib/Adafruit_SGP30-1.0.3/license.txt diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/IRMQTTServer/IRMQTTServer.ino b/lib/IRremoteESP8266-2.5.2.03/examples/IRMQTTServer/IRMQTTServer.ino deleted file mode 100644 index 7851cf5dc..000000000 --- a/lib/IRremoteESP8266-2.5.2.03/examples/IRMQTTServer/IRMQTTServer.ino +++ /dev/null @@ -1,1608 +0,0 @@ -/* - * Send & receive arbitrary IR codes via a web server or MQTT. - * Copyright David Conran 2016, 2017, 2018 - * - * NOTE: An IR LED circuit *MUST* be connected to ESP8266 GPIO4 (D2) if - * you want to send IR messages. See IR_LED below. - * A compatible IR RX modules *MUST* be connected to ESP8266 GPIO14 (D5) - * if you want to capture & decode IR nessages. See IR_RX below. - * - * WARN: This is very advanced & complicated example code. Not for beginners. - * You are strongly suggested to try & look at other example code first. - * - * # Instructions - * - * ## Before First Boot (i.e. Compile time) - * - Either: - * o Set the MQTT_SERVER define below to the address of your MQTT server. - * or - * o Disable MQTT by commenting out the line "#define MQTT_ENABLE" down below. - * - * - Arduino IDE: - * o Install the following libraries via Library Manager - * - WiFiManager (https://github.com/tzapu/WiFiManager) (Version >= 0.14) - * - PubSubClient (https://pubsubclient.knolleary.net/) - * o You MUST change to have the following (or larger) value: - * #define MQTT_MAX_PACKET_SIZE 512 - * - PlatformIO IDE: - * If you are using PlatformIO, this should already been done for you in - * the accompanying platformio.ini file. - * - * ## First Boot (Initial setup) - * The ESP8266 board will boot into the WiFiManager's AP mode. - * i.e. It will create a WiFi Access Point with a SSID like: "ESP123456" etc. - * Connect to that SSID. Then point your browser to http://192.168.4.1/ and - * configure the ESP8266 to connect to your desired WiFi network. - * It will remember the new WiFi connection details on next boot. - * More information can be found here: - * https://github.com/tzapu/WiFiManager#how-it-works - * - * If you need to reset the WiFi settings, visit: - * http:///reset - * - * ## Normal Use (After setup) - * Enter 'http:///ir?type=7&code=E0E09966 - * http:///ir?type=4&code=0xf50&bits=12 - * http:///ir?code=C1A2E21D&repeats=8&type=19 - * http:///ir?type=31&code=40000,1,1,96,24,24,24,48,24,24,24,24,24,48,24,24,24,24,24,48,24,24,24,24,24,24,24,24,1058 - * http:///ir?type=18&code=190B8050000000E0190B8070000010f0 - * http:///ir?repeats=1&type=25&code=0000,006E,0022,0002,0155,00AA,0015,0040,0015,0040,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0040,0015,0040,0015,0015,0015,0040,0015,0015,0015,0015,0015,0015,0015,0040,0015,0015,0015,0015,0015,0040,0015,0040,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0040,0015,0015,0015,0015,0015,0040,0015,0040,0015,0040,0015,0040,0015,0040,0015,0640,0155,0055,0015,0E40 - * - * or - * - * Send a MQTT message to the topic 'ir_server/send' using the following - * format (Order is important): - * protocol_num,hexcode e.g. 7,E0E09966 which is Samsung(7), Power On code, - * default bit size, default nr. of repeats. - * protocol_num,hexcode,bits e.g. 4,f50,12 which is Sony(4), Power Off code, - * 12 bits & default nr. of repeats. - * protocol_num,hexcode,bits,repeats e.g. 19,C1A2E21D,0,8 which is - * Sherwood(19), Vol Up, default bit size & - * repeated 8 times. - * 30,frequency,raw_string e.g. 30,38000,9000,4500,500,1500,500,750,500,750 - * Raw (30) @ 38kHz with a raw code of "9000,4500,500,1500,500,750,500,750" - * 31,code_string e.g. 31,40000,1,1,96,24,24,24,48,24,24,24,24,24,48,24,24,24,24,24,48,24,24,24,24,24,24,24,24,1058 - * GlobalCache (31) & "40000,1,1,96,..." (Sony Vol Up) - * 25,Rrepeats,hex_code_string e.g. 25,R1,0000,006E,0022,0002,0155,00AA,0015,0040,0015,0040,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0040,0015,0040,0015,0015,0015,0040,0015,0015,0015,0015,0015,0015,0015,0040,0015,0015,0015,0015,0015,0040,0015,0040,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0040,0015,0015,0015,0015,0015,0040,0015,0040,0015,0040,0015,0040,0015,0040,0015,0640,0155,0055,0015,0E40 - * Pronto (25), 1 repeat, & "0000 006E 0022 0002 ..." (Sherwood Amp Tape Input) - * ac_protocol_num,really_long_hexcode e.g. 18,190B8050000000E0190B8070000010F0 - * Kelvinator (18) Air Con on, Low Fan, 25 deg etc. - * NOTE: Ensure you zero-pad to the correct number of - * digits for the bit/byte size you want to send - * as some A/C units have units have different - * sized messages. e.g. Fujitsu A/C units. - * In short: - * No spaces after/before commas. - * Values are comma separated. - * The first value is always in Decimal. - * For simple protocols, the next value (hexcode) is always hexadecimal. - * The optional bit size is in decimal. - * - * Unix command line usage example: - * # Install a MQTT client - * $ sudo apt install mosquitto-clients - * # Send a 32-bit NEC code of 0x1234abcd via MQTT. - * $ mosquitto_pub -h 10.20.0.253 -t ir_server/send -m '3,1234abcd,32' - * - * This server will send (back) what ever IR message it just transmitted to - * the MQTT topic 'ir_server/sent' to confirm it has been performed. This works - * for messages requested via MQTT or via HTTP. - * Note: Other status messages are also sent to 'ir_server/sent' from time to - * time. - * Unix command line usage example: - * # Listen to MQTT acknowledgements. - * $ mosquitto_sub -h 10.20.0.253 -t ir_server/sent - * - * Incoming IR messages (from an IR remote control) will be transmitted to - * the MQTT topic 'ir_server/received'. The MQTT message will be formatted - * similar to what is required to for the 'sent' topic. - * e.g. "3,C1A2F00F,32" (Protocol,Value,Bits) for simple codes - * or "18,110B805000000060110B807000001070" (Protocol,Value) for complex codes - * Note: If the protocol is listed as -1, then that is an UNKNOWN IR protocol. - * You can't use that to recreate/resend an IR message. It's only for - * matching purposes and shouldn't be trusted. - * - * Unix command line usage example: - * # Listen via MQTT for IR messages captured by this server. - * $ mosquitto_sub -h 10.20.0.253 -t ir_server/received - * - * If DEBUG is turned on, there is additional information printed on the Serial - * Port. - * - * ## Updates - * You can upload new firmware over the air (OTA) via the form on the device's - * main page. No need to connect to the device again via USB. \o/ - * Your WiFi settings should be remembered between updates. \o/ \o/ - * - * Copyright Notice: - * Code for this has been borrowed from lots of other OpenSource projects & - * resources. I'm *NOT* claiming complete Copyright ownership of all the code. - * Likewise, feel free to borrow from this as much as you want. - */ - -#define MQTT_ENABLE // Comment this out if you don't want to use MQTT at all. - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef MQTT_ENABLE -// -------------------------------------------------------------------- -// * * * IMPORTANT * * * -// You must change to have the following value. -// #define MQTT_MAX_PACKET_SIZE 512 -// -------------------------------------------------------------------- -#include -#endif // MQTT_ENABLE -#include -#include - -// Configuration parameters -// GPIO the IR LED is connected to/controlled by. GPIO 4 = D2. -#define IR_LED 4 -// define IR_LED 3 // For an ESP-01 we suggest you use RX/GPIO3/Pin 7. -// -// GPIO the IR RX module is connected to/controlled by. GPIO 14 = D5. -// Comment this out to disable receiving/decoding IR messages entirely. -#define IR_RX 14 -const uint16_t kHttpPort = 80; // The TCP port the HTTP server is listening on. -// Name of the device you want in mDNS. -// NOTE: Changing this will change the MQTT path too unless you override it -// via MQTTprefix below. -#define HOSTNAME "ir_server" - -// We obtain our network config via DHCP by default but allow an easy way to -// use a static IP config. -#define USE_STATIC_IP false // Change to 'true' if you don't want to use DHCP. -#if USE_STATIC_IP -const IPAddress kIPAddress = IPAddress(10, 0, 1, 78); -const IPAddress kGateway = IPAddress(10, 0, 1, 1); -const IPAddress kSubnetMask = IPAddress(255, 255, 255, 0); -#endif // USE_STATIC_IP - -#ifdef MQTT_ENABLE -// Address of your MQTT server. -#define MQTT_SERVER "10.20.0.253" // <=- CHANGE ME -const uint16_t kMqttPort = 1883; // Default port used by MQTT servers. -// Set if your MQTT server requires a Username & Password to connect. -const char* mqtt_user = ""; -const char* mqtt_password = ""; -const uint32_t kMqttReconnectTime = 5000; // Delay(ms) between reconnect tries. - -#define MQTTprefix HOSTNAME // Change this if you want the MQTT topic to be - // independent of the hostname. -#define MQTTack MQTTprefix "/sent" // Topic we send back acknowledgements on -#define MQTTcommand MQTTprefix "/send" // Topic we get new commands from. -#define MQTTrecv MQTTprefix "/received" // Topic we send received IRs to. -#endif // MQTT_ENABLE - -// HTML arguments we will parse for IR code information. -#define argType "type" -#define argData "code" -#define argBits "bits" -#define argRepeat "repeats" -// Let's use a larger than normal buffer so we can handle AirCon remote codes. -const uint16_t kCaptureBufferSize = 1024; -#if DECODE_AC -// Some A/C units have gaps in their protocols of ~40ms. e.g. Kelvinator -// A value this large may swallow repeats of some protocols -const uint8_t kCaptureTimeout = 50; -#else // DECODE_AC -// Suits most messages, while not swallowing many repeats. -const uint8_t kCaptureTimeout = 15; -#endif // DECODE_AC -// Ignore unknown messages with <10 pulses -const uint16_t kMinUnknownSize = 20; - -#define _MY_VERSION_ "v0.7.0" - -// Disable debug output if any of the IR pins are on the TX (D1) pin. -#if (IR_LED != 1 && IR_RX != 1) -#undef DEBUG -#define DEBUG true // Change to 'false' to disable all serial output. -#else -#undef DEBUG -#define DEBUG false -#endif -// NOTE: Make sure you set your Serial Monitor to the same speed. -#define BAUD_RATE 115200 // Serial port Baud rate. - -// Globals -ESP8266WebServer server(kHttpPort); -IRsend irsend = IRsend(IR_LED); -#ifdef IR_RX -IRrecv irrecv(IR_RX, kCaptureBufferSize, kCaptureTimeout, true); -decode_results capture; // Somewhere to store inbound IR messages. -#endif // IR_RX -MDNSResponder mdns; -WiFiClient espClient; -WiFiManager wifiManager; - -uint16_t *codeArray; -uint32_t lastReconnectAttempt = 0; // MQTT last attempt reconnection number -bool boot = true; -bool ir_lock = false; // Primitive locking for gating the IR LED. -uint32_t sendReqCounter = 0; -bool lastSendSucceeded = false; // Store the success status of the last send. -uint32_t lastSendTime = 0; -int8_t offset; // The calculated period offset for this chip and library. - -#ifdef MQTT_ENABLE -String lastMqttCmd = "None"; -uint32_t lastMqttCmdTime = 0; -uint32_t lastConnectedTime = 0; -uint32_t lastDisconnectedTime = 0; -uint32_t mqttDisconnectCounter = 0; -bool wasConnected = true; -#ifdef IR_RX -String lastIrReceived = "None"; -uint32_t lastIrReceivedTime = 0; -uint32_t irRecvCounter = 0; -#endif // IR_RX - - -// MQTT client parameters -void callback(char* topic, byte* payload, unsigned int length); -PubSubClient mqtt_client(MQTT_SERVER, kMqttPort, callback, espClient); -// Create a unique MQTT client id. -String mqtt_clientid = MQTTprefix + String(ESP.getChipId(), HEX); -#endif // MQTT_ENABLE - -// Debug messages get sent to the serial port. -void debug(String str) { -#ifdef DEBUG - uint32_t now = millis(); - Serial.printf("%07u.%03u: %s\n", now / 1000, now % 1000, str.c_str()); -#endif // DEBUG -} - -String timeSince(uint32_t const start) { - if (start == 0) - return "Never"; - uint32_t diff = 0; - uint32_t now = millis(); - if (start < now) - diff = now - start; - else - diff = UINT32_MAX - start + now; - diff /= 1000; // Convert to seconds. - if (diff == 0) return "Now"; - - // Note: millis() can only count up to 45 days, so uint8_t is safe. - uint8_t days = diff / (60 * 60 * 24); - uint8_t hours = (diff / (60 * 60)) % 24; - uint8_t minutes = (diff / 60) % 60; - uint8_t seconds = diff % 60; - - String result = ""; - if (days) - result += String(days) + " day"; - if (days > 1) result += "s"; - if (hours) - result += " " + String(hours) + " hour"; - if (hours > 1) result += "s"; - if (minutes) - result += " " + String(minutes) + " minute"; - if (minutes > 1) result += "s"; - if (seconds) - result += " " + String(seconds) + " second"; - if (seconds > 1) result += "s"; - result.trim(); - return result + " ago"; -} - -// Quick and dirty check for any unsafe chars in a string -// that may cause HTML shenanigans. e.g. An XSS. -bool hasUnsafeHTMLChars(String input) { - static char unsafe[] = "';!-\"<>=&{}()"; - for (uint8_t i = 0; unsafe[i]; i++) - if (input.indexOf(unsafe[i]) != -1) return true; - return false; -} - -// Root web page with example usage etc. -void handleRoot() { - server.send(200, "text/html", - "IR MQTT server" - "" - "

ESP8266 IR MQTT Server

" - "

" - "

Information

" - "

IP address: " + WiFi.localIP().toString() + "
" - "Booted: " + timeSince(1) + "
" + - "Version: " _MY_VERSION_ "
" - "Period Offset: " + String(offset) + "us
" - "IR Lib Version: " _IRREMOTEESP8266_VERSION_ "
" - "ESP8266 Core Version: " + ESP.getCoreVersion() + "
" - "IR Send GPIO: " + String(IR_LED) + "
" - "Total send requests: " + String(sendReqCounter) + "
" - "Last message sent: " + String(lastSendSucceeded ? "Ok" : "FAILED") + - " (" + timeSince(lastSendTime) + ")
" -#ifdef IR_RX - "IR Recv GPIO: " + String(IR_RX) + "
" - "Total IR Received: " + String(irRecvCounter) + "
" - "Last IR Received: " + lastIrReceived + - " (" + timeSince(lastIrReceivedTime) + ")
" -#endif // IR_RX - "

" -#ifdef MQTT_ENABLE - "

MQTT Information

" - "

Server: " MQTT_SERVER ":" + String(kMqttPort) + " (" + - (mqtt_client.connected() ? "Connected " + timeSince(lastDisconnectedTime) - : "Disconnected " + timeSince(lastConnectedTime)) + - ")
" - "Disconnections: " + String(mqttDisconnectCounter - 1) + "
" - "Client id: " + mqtt_clientid + "
" - "Command topic: " MQTTcommand "
" - "Acknowledgements topic: " MQTTack "
" -#ifdef IR_RX - "IR Received topic: " MQTTrecv "
" -#endif // IR_RX - "Last MQTT command seen: " + - // lastMqttCmd is unescaped untrusted input. - // Avoid any possible HTML/XSS when displaying it. - (hasUnsafeHTMLChars(lastMqttCmd) ? - "Contains unsafe HTML characters" : lastMqttCmd) + - " (" + timeSince(lastMqttCmdTime) + ")

" -#endif // MQTT_ENABLE - "

" - "

Hardcoded examples

" - "

" - "Sherwood Amp On (GlobalCache)

" - "

" - "Sherwood Amp Off (Raw)

" - "

" - "Sherwood Amp Input TAPE (Pronto)

" - "

TV on (Samsung)

" - "

Power Off (Sony 12bit)

" - "

" - "

Send a simple IR message

" - "" - "Type: " - "" - " Code: 0x" - " Bit size: " - "" - " Repeats: " - " " - "" - "


" - "

Send an IRremote Raw IR message

" - "

" - "" - "String: (freq,array data) " - " " - "
" - "

" - "

Send a GlobalCache" - " IR message

" - "

" - "" - "String: 1:1,1," - " " - "
" - "

" - "

Send a Pronto code IR message

" - "

" - "" - "String (comma separated): " - " Repeats: " - " " - "
" - "

" - "

Send an Air Conditioner IR message

" - "

" - "Type: " - "" - " State code: 0x" - "" - " " - "
" - "

" - "

Update IR Server firmware

" - "Warning:
" - "Updating your firmware may screw up your access to the device. " - "If you are going to use this, know what you are doing first " - "(and you probably do).
" - "

" - "Firmware to upload: " - "" - "
" - ""); -} - -// Reset web page -void handleReset() { - server.send(200, "text/html", - "Reset Config" - "" - "

Resetting the WiFiManager config back to defaults.

" - "

Device restarting. Try connecting in a few seconds.

" - ""); - // Do the reset. - wifiManager.resetSettings(); - delay(10); - ESP.restart(); - delay(1000); -} - -// Parse an Air Conditioner A/C Hex String/code and send it. -// Args: -// irType: Nr. of the protocol we need to send. -// str: A hexadecimal string containing the state to be sent. -// Returns: -// bool: Successfully sent or not. -bool parseStringAndSendAirCon(const uint16_t irType, const String str) { - uint8_t strOffset = 0; - uint8_t state[kStateSizeMax] = {0}; // All array elements are set to 0. - uint16_t stateSize = 0; - - if (str.startsWith("0x") || str.startsWith("0X")) - strOffset = 2; - // Calculate how many hexadecimal characters there are. - uint16_t inputLength = str.length() - strOffset; - if (inputLength == 0) { - debug("Zero length AirCon code encountered. Ignored."); - return false; // No input. Abort. - } - - switch (irType) { // Get the correct state size for the protocol. - case KELVINATOR: - stateSize = kKelvinatorStateLength; - break; - case TOSHIBA_AC: - stateSize = kToshibaACStateLength; - break; - case DAIKIN: - stateSize = kDaikinStateLength; - break; - case ELECTRA_AC: - stateSize = kElectraAcStateLength; - break; - case MITSUBISHI_AC: - stateSize = kMitsubishiACStateLength; - break; - case PANASONIC_AC: - stateSize = kPanasonicAcStateLength; - break; - case TROTEC: - stateSize = kTrotecStateLength; - break; - case ARGO: - stateSize = kArgoStateLength; - break; - case GREE: - stateSize = kGreeStateLength; - break; - case FUJITSU_AC: - // Fujitsu has four distinct & different size states, so make a best guess - // which one we are being presented with based on the number of - // hexadecimal digits provided. i.e. Zero-pad if you need to to get - // the correct length/byte size. - stateSize = inputLength / 2; // Every two hex chars is a byte. - // Use at least the minimum size. - stateSize = std::max(stateSize, - (uint16_t) (kFujitsuAcStateLengthShort - 1)); - // If we think it isn't a "short" message. - if (stateSize > kFujitsuAcStateLengthShort) - // Then it has to be at least the smaller version of the "normal" size. - stateSize = std::max(stateSize, (uint16_t) (kFujitsuAcStateLength - 1)); - // Lastly, it should never exceed the maximum "normal" size. - stateSize = std::min(stateSize, kFujitsuAcStateLength); - break; - case HAIER_AC: - stateSize = kHaierACStateLength; - break; - case HAIER_AC_YRW02: - stateSize = kHaierACYRW02StateLength; - break; - case HITACHI_AC: - stateSize = kHitachiAcStateLength; - break; - case HITACHI_AC1: - stateSize = kHitachiAc1StateLength; - break; - case HITACHI_AC2: - stateSize = kHitachiAc2StateLength; - break; - case WHIRLPOOL_AC: - stateSize = kWhirlpoolAcStateLength; - break; - case SAMSUNG_AC: - // Samsung has two distinct & different size states, so make a best guess - // which one we are being presented with based on the number of - // hexadecimal digits provided. i.e. Zero-pad if you need to to get - // the correct length/byte size. - stateSize = inputLength / 2; // Every two hex chars is a byte. - // Use at least the minimum size. - stateSize = std::max(stateSize, (uint16_t) (kSamsungAcStateLength)); - // If we think it isn't a "normal" message. - if (stateSize > kSamsungAcStateLength) - // Then it probably the extended size. - stateSize = std::max(stateSize, - (uint16_t) (kSamsungAcExtendedStateLength)); - // Lastly, it should never exceed the maximum "extended" size. - stateSize = std::min(stateSize, kSamsungAcExtendedStateLength); - break; - case MWM: - // MWM has variable size states, so make a best guess - // which one we are being presented with based on the number of - // hexadecimal digits provided. i.e. Zero-pad if you need to to get - // the correct length/byte size. - stateSize = inputLength / 2; // Every two hex chars is a byte. - // Use at least the minimum size. - stateSize = std::max(stateSize, (uint16_t) 3); - // Cap the maximum size. - stateSize = std::min(stateSize, kStateSizeMax); - break; - default: // Not a protocol we expected. Abort. - debug("Unexpected AirCon protocol detected. Ignoring."); - return false; - } - if (inputLength > stateSize * 2) { - debug("AirCon code to large for the given protocol."); - return false; - } - - // Ptr to the least significant byte of the resulting state for this protocol. - uint8_t *statePtr = &state[stateSize - 1]; - - // Convert the string into a state array of the correct length. - for (uint16_t i = 0; i < inputLength; i++) { - // Grab the next least sigificant hexadecimal digit from the string. - uint8_t c = tolower(str[inputLength + strOffset - i - 1]); - if (isxdigit(c)) { - if (isdigit(c)) - c -= '0'; - else - c = c - 'a' + 10; - } else { - debug("Aborting! Non-hexadecimal char found in AirCon state: " + str); - return false; - } - if (i % 2 == 1) { // Odd: Upper half of the byte. - *statePtr += (c << 4); - statePtr--; // Advance up to the next least significant byte of state. - } else { // Even: Lower half of the byte. - *statePtr = c; - } - } - - // Make the appropriate call for the protocol type. - switch (irType) { -#if SEND_KELVINATOR - case KELVINATOR: - irsend.sendKelvinator(reinterpret_cast(state)); - break; -#endif -#if SEND_TOSHIBA_AC - case TOSHIBA_AC: - irsend.sendToshibaAC(reinterpret_cast(state)); - break; -#endif -#if SEND_DAIKIN - case DAIKIN: - irsend.sendDaikin(reinterpret_cast(state)); - break; -#endif -#if MITSUBISHI_AC - case MITSUBISHI_AC: - irsend.sendMitsubishiAC(reinterpret_cast(state)); - break; -#endif -#if SEND_TROTEC - case TROTEC: - irsend.sendTrotec(reinterpret_cast(state)); - break; -#endif -#if SEND_ARGO - case ARGO: - irsend.sendArgo(reinterpret_cast(state)); - break; -#endif -#if SEND_GREE - case GREE: - irsend.sendGree(reinterpret_cast(state)); - break; -#endif -#if SEND_FUJITSU_AC - case FUJITSU_AC: - irsend.sendFujitsuAC(reinterpret_cast(state), stateSize); - break; -#endif -#if SEND_HAIER_AC - case HAIER_AC: - irsend.sendHaierAC(reinterpret_cast(state)); - break; -#endif -#if SEND_HAIER_AC_YRW02 - case HAIER_AC_YRW02: - irsend.sendHaierACYRW02(reinterpret_cast(state)); - break; -#endif -#if SEND_HITACHI_AC - case HITACHI_AC: - irsend.sendHitachiAC(reinterpret_cast(state)); - break; -#endif -#if SEND_HITACHI_AC1 - case HITACHI_AC1: - irsend.sendHitachiAC1(reinterpret_cast(state)); - break; -#endif -#if SEND_HITACHI_AC2 - case HITACHI_AC2: - irsend.sendHitachiAC2(reinterpret_cast(state)); - break; -#endif -#if SEND_WHIRLPOOL_AC - case WHIRLPOOL_AC: - irsend.sendWhirlpoolAC(reinterpret_cast(state)); - break; -#endif -#if SEND_SAMSUNG_AC - case SAMSUNG_AC: - irsend.sendSamsungAC(reinterpret_cast(state), stateSize); - break; -#endif -#if SEND_ELECTRA_AC - case ELECTRA_AC: - irsend.sendElectraAC(reinterpret_cast(state)); - break; -#endif -#if SEND_PANASONIC_AC - case PANASONIC_AC: - irsend.sendPanasonicAC(reinterpret_cast(state)); - break; -#endif -#if SEND_MWM_ - case MWM: - irsend.sendMWM(reinterpret_cast(state), stateSize); - break; -#endif - default: - debug("Unexpected AirCon type in send request. Not sent."); - return false; - } - return true; // We were successful as far as we can tell. -} - -// Count how many values are in the String. -// Args: -// str: String containing the values. -// sep: Character that separates the values. -// Returns: -// The number of values found in the String. -uint16_t countValuesInStr(const String str, char sep) { - int16_t index = -1; - uint16_t count = 1; - do { - index = str.indexOf(sep, index + 1); - count++; - } while (index != -1); - return count; -} - -// Dynamically allocate an array of uint16_t's. -// Args: -// size: Nr. of uint16_t's need to be in the new array. -// Returns: -// A Ptr to the new array. Restarts the ESP8266 if it fails. -uint16_t * newCodeArray(const uint16_t size) { - uint16_t *result; - - result = reinterpret_cast(malloc(size * sizeof(uint16_t))); - // Check we malloc'ed successfully. - if (result == NULL) { // malloc failed, so give up. - Serial.printf("\nCan't allocate %d bytes. (%d bytes free)\n", - size * sizeof(uint16_t), ESP.getFreeHeap()); - Serial.println("Giving up & forcing a reboot."); - ESP.restart(); // Reboot. - delay(500); // Wait for the restart to happen. - return result; // Should never get here, but just in case. - } - return result; -} - -#if SEND_GLOBALCACHE -// Parse a GlobalCache String/code and send it. -// Args: -// str: A GlobalCache formatted String of comma separated numbers. -// e.g. "38000,1,1,170,170,20,63,20,63,20,63,20,20,20,20,20,20,20,20,20, -// 20,20,63,20,63,20,63,20,20,20,20,20,20,20,20,20,20,20,20,20,63, -// 20,20,20,20,20,20,20,20,20,20,20,20,20,63,20,20,20,63,20,63,20, -// 63,20,63,20,63,20,63,20,1798" -// Note: The leading "1:1,1," of normal GC codes should be removed. -// Returns: -// bool: Successfully sent or not. -bool parseStringAndSendGC(const String str) { - uint16_t count; - uint16_t *code_array; - String tmp_str; - - // Remove the leading "1:1,1," if present. - if (str.startsWith("1:1,1,")) - tmp_str = str.substring(6); - else - tmp_str = str; - - // Find out how many items there are in the string. - count = countValuesInStr(tmp_str, ','); - - // Now we know how many there are, allocate the memory to store them all. - code_array = newCodeArray(count); - - // Now convert the strings to integers and place them in code_array. - count = 0; - uint16_t start_from = 0; - int16_t index = -1; - do { - index = tmp_str.indexOf(',', start_from); - code_array[count] = tmp_str.substring(start_from, index).toInt(); - start_from = index + 1; - count++; - } while (index != -1); - - irsend.sendGC(code_array, count); // All done. Send it. - free(code_array); // Free up the memory allocated. - if (count > 0) - return true; // We sent something. - return false; // We probably didn't. -} -#endif // SEND_GLOBALCACHE - -#if SEND_PRONTO -// Parse a Pronto Hex String/code and send it. -// Args: -// str: A comma-separated String of nr. of repeats, then hexadecimal numbers. -// e.g. "R1,0000,0067,0000,0015,0060,0018,0018,0018,0030,0018,0030,0018, -// 0030,0018,0018,0018,0030,0018,0018,0018,0018,0018,0030,0018, -// 0018,0018,0030,0018,0030,0018,0030,0018,0018,0018,0018,0018, -// 0030,0018,0018,0018,0018,0018,0030,0018,0018,03f6" -// or -// "0000,0067,0000,0015,0060,0018". i.e. without the Repeat value -// Requires at least kProntoMinLength comma-separated values. -// sendPronto() only supports raw pronto code types, thus so does this. -// repeats: Nr. of times the message is to be repeated. -// This value is ignored if an embeddd repeat is found in str. -// Returns: -// bool: Successfully sent or not. -bool parseStringAndSendPronto(const String str, uint16_t repeats) { - uint16_t count; - uint16_t *code_array; - int16_t index = -1; - uint16_t start_from = 0; - - // Find out how many items there are in the string. - count = countValuesInStr(str, ','); - - // Check if we have the optional embedded repeats value in the code string. - if (str.startsWith("R") || str.startsWith("r")) { - // Grab the first value from the string, as it is the nr. of repeats. - index = str.indexOf(',', start_from); - repeats = str.substring(start_from + 1, index).toInt(); // Skip the 'R'. - start_from = index + 1; - count--; // We don't count the repeats value as part of the code array. - } - - // We need at least kProntoMinLength values for the code part. - if (count < kProntoMinLength) return false; - - // Now we know how many there are, allocate the memory to store them all. - code_array = newCodeArray(count); - - // Rest of the string are values for the code array. - // Now convert the hex strings to integers and place them in code_array. - count = 0; - do { - index = str.indexOf(',', start_from); - // Convert the hexadecimal value string to an unsigned integer. - code_array[count] = strtoul(str.substring(start_from, index).c_str(), - NULL, 16); - start_from = index + 1; - count++; - } while (index != -1); - - irsend.sendPronto(code_array, count, repeats); // All done. Send it. - free(code_array); // Free up the memory allocated. - if (count > 0) - return true; // We sent something. - return false; // We probably didn't. -} -#endif // SEND_PRONTO - -#if SEND_RAW -// Parse an IRremote Raw Hex String/code and send it. -// Args: -// str: A comma-separated String containing the freq and raw IR data. -// e.g. "38000,9000,4500,600,1450,600,900,650,1500,..." -// Requires at least two comma-separated values. -// First value is the transmission frequency in Hz or kHz. -// Returns: -// bool: Successfully sent or not. -bool parseStringAndSendRaw(const String str) { - uint16_t count; - uint16_t freq = 38000; // Default to 38kHz. - uint16_t *raw_array; - - // Find out how many items there are in the string. - count = countValuesInStr(str, ','); - - // We expect the frequency as the first comma separated value, so we need at - // least two values. If not, bail out. - if (count < 2) return false; - count--; // We don't count the frequency value as part of the raw array. - - // Now we know how many there are, allocate the memory to store them all. - raw_array = newCodeArray(count); - - // Grab the first value from the string, as it is the frequency. - int16_t index = str.indexOf(',', 0); - freq = str.substring(0, index).toInt(); - uint16_t start_from = index + 1; - // Rest of the string are values for the raw array. - // Now convert the strings to integers and place them in raw_array. - count = 0; - do { - index = str.indexOf(',', start_from); - raw_array[count] = str.substring(start_from, index).toInt(); - start_from = index + 1; - count++; - } while (index != -1); - - irsend.sendRaw(raw_array, count, freq); // All done. Send it. - free(raw_array); // Free up the memory allocated. - if (count > 0) - return true; // We sent something. - return false; // We probably didn't. -} -#endif // SEND_RAW - -// Parse the URL args to find the IR code. -void handleIr() { - uint64_t data = 0; - String data_str = ""; - int ir_type = 3; // Default to NEC codes. - uint16_t nbits = 0; - uint16_t repeat = 0; - - for (uint16_t i = 0; i < server.args(); i++) { - if (server.argName(i) == argType) - ir_type = atoi(server.arg(i).c_str()); - if (server.argName(i) == argData) { - data = getUInt64fromHex(server.arg(i).c_str()); - data_str = server.arg(i); - } - if (server.argName(i) == argBits) - nbits = atoi(server.arg(i).c_str()); - if (server.argName(i) == argRepeat) - repeat = atoi(server.arg(i).c_str()); - } - debug("New code received via HTTP"); - lastSendSucceeded = sendIRCode(ir_type, data, data_str.c_str(), nbits, - repeat); - handleRoot(); -} - -void handleNotFound() { - String message = "File Not Found\n\n"; - message += "URI: "; - message += server.uri(); - message += "\nMethod: "; - message += (server.method() == HTTP_GET)?"GET":"POST"; - message += "\nArguments: "; - message += server.args(); - message += "\n"; - for (uint8_t i=0; i < server.args(); i++) - message += " " + server.argName(i) + ": " + server.arg(i) + "\n"; - server.send(404, "text/plain", message); -} - -void setup_wifi() { - delay(10); - // We start by connecting to a WiFi network - - wifiManager.setTimeout(300); // Time out after 5 mins. -#if USE_STATIC_IP - // Use a static IP config rather than the one supplied via DHCP. - wifiManager.setSTAStaticIPConfig(kIPAddress, kGateway, kSubnetMask); -#endif // USE_STATIC_IP - if (!wifiManager.autoConnect()) { - debug("Wifi failed to connect and hit timeout."); - delay(3000); - // Reboot. A.k.a. "Have you tried turning it Off and On again?" - ESP.reset(); - delay(5000); - } - - debug("WiFi connected. IP address: " + WiFi.localIP().toString()); -} - -void setup(void) { - irsend.begin(); - offset = irsend.calibrate(); -#if IR_RX -#if DECODE_HASH - // Ignore messages with less than minimum on or off pulses. - irrecv.setUnknownThreshold(kMinUnknownSize); -#endif // DECODE_HASH - irrecv.enableIRIn(); // Start the receiver -#endif // IR_RX - - #ifdef DEBUG - // Use SERIAL_TX_ONLY so that the RX pin can be freed up for GPIO/IR use. - Serial.begin(BAUD_RATE, SERIAL_8N1, SERIAL_TX_ONLY); - while (!Serial) // Wait for the serial connection to be establised. - delay(50); - Serial.println(); - debug("IRMQTTServer " _MY_VERSION_" has booted."); - #endif // DEBUG - - setup_wifi(); - - // Wait a bit for things to settle. - delay(1500); - - lastReconnectAttempt = 0; - - if (mdns.begin(HOSTNAME, WiFi.localIP())) { - debug("MDNS responder started"); - } - - // Setup the root web page. - server.on("/", handleRoot); - // Setup the page to handle web-based IR codes. - server.on("/ir", handleIr); - // Setup a reset page to cause WiFiManager information to be reset. - server.on("/reset", handleReset); - - // Setup the URL to allow Over-The-Air (OTA) firmware updates. - server.on("/update", HTTP_POST, [](){ - server.sendHeader("Connection", "close"); - server.send(200, "text/plain", (Update.hasError())?"FAIL":"OK"); - ESP.restart(); - }, [](){ - HTTPUpload& upload = server.upload(); - if (upload.status == UPLOAD_FILE_START) { - WiFiUDP::stopAll(); - debug("Update: " + upload.filename); - uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & - 0xFFFFF000; - if (!Update.begin(maxSketchSpace)) { // start with max available size -#ifdef DEBUG - Update.printError(Serial); -#endif // DEBUG - } - } else if (upload.status == UPLOAD_FILE_WRITE) { - if (Update.write(upload.buf, upload.currentSize) != - upload.currentSize) { -#ifdef DEBUG - Update.printError(Serial); -#endif // DEBUG - } - } else if (upload.status == UPLOAD_FILE_END) { - if (Update.end(true)) { // true to set the size to the current progress - debug("Update Success: " + (String) upload.totalSize + - "\nRebooting..."); - } - } - yield(); - }); - - // Set up an error page. - server.onNotFound(handleNotFound); - - server.begin(); - debug("HTTP server started"); -} - -#ifdef MQTT_ENABLE -// MQTT subscribing to topic -void subscribing(const String topic_name) { - // subscription to topic for receiving data - if (mqtt_client.subscribe(topic_name.c_str())) { - debug("Subscription OK to " + topic_name); - } -} - -bool reconnect() { - // Loop a few times or until we're reconnected - uint16_t tries = 1; - while (!mqtt_client.connected() && tries <= 3) { - int connected = false; - // Attempt to connect - debug("Attempting MQTT connection to " MQTT_SERVER ":" + String(kMqttPort) + - "... "); - if (mqtt_user && mqtt_password) - connected = mqtt_client.connect(mqtt_clientid.c_str(), mqtt_user, - mqtt_password); - else - connected = mqtt_client.connect(mqtt_clientid.c_str()); - if (connected) { - // Once connected, publish an announcement... - mqtt_client.publish(MQTTack, "Connected"); - debug("connected."); - // Subscribing to topic(s) - subscribing(MQTTcommand); - } else { - debug("failed, rc=" + String(mqtt_client.state()) + - " Try again in a bit."); - // Wait for a bit before retrying - delay(tries << 7); // Linear increasing back-off (x128) - } - tries++; - } - return mqtt_client.connected(); -} -#endif // MQTT_ENABLE - -void loop(void) { - server.handleClient(); // Handle any web activity - -#ifdef MQTT_ENABLE - uint32_t now = millis(); - // MQTT client connection management - if (!mqtt_client.connected()) { - if (wasConnected) { - lastDisconnectedTime = now; - wasConnected = false; - mqttDisconnectCounter++; - } - // Reconnect if it's longer than kMqttReconnectTime since we last tried. - if (now - lastReconnectAttempt > kMqttReconnectTime) { - lastReconnectAttempt = now; - debug("client mqtt not connected, trying to connect"); - // Attempt to reconnect - if (reconnect()) { - lastReconnectAttempt = 0; - wasConnected = true; - if (boot) { - mqtt_client.publish(MQTTack, "IR Server just booted"); - boot = false; - } else { - String text = "IR Server just (re)connected to MQTT. " - "Lost connection about " + timeSince(lastConnectedTime); - mqtt_client.publish(MQTTack, text.c_str()); - } - lastConnectedTime = now; - debug("successful client mqtt connection"); - } - } - } else { - lastConnectedTime = now; - // MQTT loop - mqtt_client.loop(); - } -#endif // MQTT_ENABLE -#ifdef IR_RX - // Check if an IR code has been received via the IR RX module. - if (irrecv.decode(&capture)) { - lastIrReceivedTime = millis(); - lastIrReceived = String(capture.decode_type) + "," + - resultToHexidecimal(&capture); - // If it isn't an AC code, add the bits. - if (!hasACState(capture.decode_type)) - lastIrReceived += "," + String(capture.bits); - mqtt_client.publish(MQTTrecv, lastIrReceived.c_str()); - irRecvCounter++; - debug("Incoming IR message sent to MQTT: " + lastIrReceived); - } -#endif // IR_RX - delay(100); -} - -// Arduino framework doesn't support strtoull(), so make our own one. -uint64_t getUInt64fromHex(char const *str) { - uint64_t result = 0; - uint16_t offset = 0; - // Skip any leading '0x' or '0X' prefix. - if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) - offset = 2; - for (; isxdigit((unsigned char)str[offset]); offset++) { - char c = str[offset]; - result *= 16; - if (isdigit(c)) /* '0' .. '9' */ - result += c - '0'; - else if (isupper(c)) /* 'A' .. 'F' */ - result += c - 'A' + 10; - else /* 'a' .. 'f'*/ - result += c - 'a' + 10; - } - return result; -} - -// Transmit the given IR message. -// -// Args: -// ir_type: enum of the protocol to be sent. -// code: Numeric payload of the IR message. Most protocols use this. -// code_str: The unparsed code to be sent. Used by complex protocol encodings. -// bits: Nr. of bits in the protocol. 0 means use the protocol's default. -// repeat: Nr. of times the message is to be repeated. (Not all protcols.) -// Returns: -// bool: Successfully sent or not. -bool sendIRCode(int const ir_type, uint64_t const code, char const * code_str, - uint16_t bits, uint16_t repeat) { - // Create a pseudo-lock so we don't try to send two codes at the same time. - while (ir_lock) - delay(20); - ir_lock = true; - - bool success = true; // Assume success. - - // send the IR message. - switch (ir_type) { -#if SEND_RC5 - case RC5: // 1 - if (bits == 0) - bits = kRC5Bits; - irsend.sendRC5(code, bits, repeat); - break; -#endif -#if SEND_RC6 - case RC6: // 2 - if (bits == 0) - bits = kRC6Mode0Bits; - irsend.sendRC6(code, bits, repeat); - break; -#endif -#if SEND_NEC - case NEC: // 3 - if (bits == 0) - bits = kNECBits; - irsend.sendNEC(code, bits, repeat); - break; -#endif -#if SEND_SONY - case SONY: // 4 - if (bits == 0) - bits = kSony12Bits; - repeat = std::max(repeat, kSonyMinRepeat); - irsend.sendSony(code, bits, repeat); - break; -#endif -#if SEND_PANASONIC - case PANASONIC: // 5 - if (bits == 0) - bits = kPanasonicBits; - irsend.sendPanasonic64(code, bits, repeat); - break; -#endif -#if SEND_JVC - case JVC: // 6 - if (bits == 0) - bits = kJvcBits; - irsend.sendJVC(code, bits, repeat); - break; -#endif -#if SEND_SAMSUNG - case SAMSUNG: // 7 - if (bits == 0) - bits = kSamsungBits; - irsend.sendSAMSUNG(code, bits, repeat); - break; -#endif -#if SEND_WHYNTER - case WHYNTER: // 8 - if (bits == 0) - bits = kWhynterBits; - irsend.sendWhynter(code, bits, repeat); - break; -#endif -#if SEND_AIWA_RC_T501 - case AIWA_RC_T501: // 9 - if (bits == 0) - bits = kAiwaRcT501Bits; - repeat = std::max(repeat, kAiwaRcT501MinRepeats); - irsend.sendAiwaRCT501(code, bits, repeat); - break; -#endif -#if SEND_LG - case LG: // 10 - if (bits == 0) - bits = kLgBits; - irsend.sendLG(code, bits, repeat); - break; -#endif -#if SEND_MITSUBISHI - case MITSUBISHI: // 12 - if (bits == 0) - bits = kMitsubishiBits; - repeat = std::max(repeat, kMitsubishiMinRepeat); - irsend.sendMitsubishi(code, bits, repeat); - break; -#endif -#if SEND_DISH - case DISH: // 13 - if (bits == 0) - bits = kDishBits; - repeat = std::max(repeat, kDishMinRepeat); - irsend.sendDISH(code, bits, repeat); - break; -#endif -#if SEND_SHARP - case SHARP: // 14 - if (bits == 0) - bits = kSharpBits; - irsend.sendSharpRaw(code, bits, repeat); - break; -#endif -#if SEND_COOLIX - case COOLIX: // 15 - if (bits == 0) - bits = kCoolixBits; - irsend.sendCOOLIX(code, bits, repeat); - break; -#endif - case DAIKIN: // 16 - case KELVINATOR: // 18 - case MITSUBISHI_AC: // 20 - case GREE: // 24 - case ARGO: // 27 - case TROTEC: // 28 - case TOSHIBA_AC: // 32 - case FUJITSU_AC: // 33 - case HAIER_AC: // 38 - case HAIER_AC_YRW02: // 44 - case HITACHI_AC: // 40 - case HITACHI_AC1: // 41 - case HITACHI_AC2: // 42 - case WHIRLPOOL_AC: // 45 - case SAMSUNG_AC: // 46 - case ELECTRA_AC: // 48 - case PANASONIC_AC: // 49 - case MWM: // 52 - success = parseStringAndSendAirCon(ir_type, code_str); - break; -#if SEND_DENON - case DENON: // 17 - if (bits == 0) - bits = DENON_BITS; - irsend.sendDenon(code, bits, repeat); - break; -#endif -#if SEND_SHERWOOD - case SHERWOOD: // 19 - if (bits == 0) - bits = kSherwoodBits; - repeat = std::max(repeat, kSherwoodMinRepeat); - irsend.sendSherwood(code, bits, repeat); - break; -#endif -#if SEND_RCMM - case RCMM: // 21 - if (bits == 0) - bits = kRCMMBits; - irsend.sendRCMM(code, bits, repeat); - break; -#endif -#if SEND_SANYO - case SANYO_LC7461: // 22 - if (bits == 0) - bits = kSanyoLC7461Bits; - irsend.sendSanyoLC7461(code, bits, repeat); - break; -#endif -#if SEND_RC5 - case RC5X: // 23 - if (bits == 0) - bits = kRC5XBits; - irsend.sendRC5(code, bits, repeat); - break; -#endif -#if SEND_PRONTO - case PRONTO: // 25 - success = parseStringAndSendPronto(code_str, repeat); - break; -#endif -#if SEND_NIKAI - case NIKAI: // 29 - if (bits == 0) - bits = kNikaiBits; - irsend.sendNikai(code, bits, repeat); - break; -#endif -#if SEND_RAW - case RAW: // 30 - success = parseStringAndSendRaw(code_str); - break; -#endif -#if SEND_GLOBALCACHE - case GLOBALCACHE: // 31 - success = parseStringAndSendGC(code_str); - break; -#endif -#if SEND_MIDEA - case MIDEA: // 34 - if (bits == 0) - bits = kMideaBits; - irsend.sendMidea(code, bits, repeat); - break; -#endif -#if SEND_MAGIQUEST - case MAGIQUEST: // 35 - if (bits == 0) - bits = kMagiquestBits; - irsend.sendMagiQuest(code, bits, repeat); - break; -#endif -#if SEND_LASERTAG - case LASERTAG: // 36 - if (bits == 0) - bits = kLasertagBits; - irsend.sendLasertag(code, bits, repeat); - break; -#endif -#if SEND_CARRIER_AC - case CARRIER_AC: // 37 - if (bits == 0) - bits = kCarrierAcBits; - irsend.sendCarrierAC(code, bits, repeat); - break; -#endif -#if SEND_MITSUBISHI2 - case MITSUBISHI2: // 39 - if (bits == 0) - bits = kMitsubishiBits; - repeat = std::max(repeat, kMitsubishiMinRepeat); - irsend.sendMitsubishi2(code, bits, repeat); - break; -#endif -#if SEND_GICABLE - case GICABLE: // 43 - if (bits == 0) - bits = kGicableBits; - repeat = std::max(repeat, kGicableMinRepeat); - irsend.sendGICable(code, bits, repeat); - break; -#endif -#if SEND_LUTRON - case LUTRON: // 47 - if (bits == 0) - bits = kLutronBits; - irsend.sendLutron(code, bits, repeat); - break; -#endif -#if SEND_PIONEER - case PIONEER: // 50 - if (bits == 0) - bits = kPioneerBits; - irsend.sendPioneer(code, bits, repeat); - break; -#endif - -#if SEND_LG - case LG2: // 51 - if (bits == 0) - bits = kLgBits; - irsend.sendLG2(code, bits, repeat); - break; -#endif - default: - // If we got here, we didn't know how to send it. - success = false; - } - lastSendTime = millis(); - // Release the lock. - ir_lock = false; - - // Indicate that we sent the message or not. - if (success) { - sendReqCounter++; - debug("Sent the IR message:"); - } else { - debug("Failed to send IR Message:"); - } - debug("Type: " + String(ir_type)); - // For "long" codes we basically repeat what we got. - if (hasACState((decode_type_t) ir_type) || - ir_type == PRONTO || - ir_type == RAW || - ir_type == GLOBALCACHE) { - debug("Code: "); - debug(code_str); - // Confirm what we were asked to send was sent. -#ifdef MQTT_ENABLE - if (success) { - if (ir_type == PRONTO && repeat > 0) - mqtt_client.publish(MQTTack, (String(ir_type) + ",R" + - String(repeat) + "," + - String(code_str)).c_str()); - else - mqtt_client.publish(MQTTack, (String(ir_type) + "," + - String(code_str)).c_str()); - } -#endif // MQTT_ENABLE - } else { // For "short" codes, we break it down a bit more before we report. - debug("Code: 0x" + uint64ToString(code, 16)); - debug("Bits: " + String(bits)); - debug("Repeats: " + String(repeat)); -#ifdef MQTT_ENABLE - if (success) - mqtt_client.publish(MQTTack, (String(ir_type) + "," + - uint64ToString(code, 16) - + "," + String(bits) + "," + - String(repeat)).c_str()); -#endif // MQTT_ENABLE - } - return success; -} - -#ifdef MQTT_ENABLE -void receivingMQTT(String const topic_name, String const callback_str) { - char* tok_ptr; - uint64_t code = 0; - uint16_t nbits = 0; - uint16_t repeat = 0; - - debug("Receiving data by MQTT topic " + topic_name); - - // Make a copy of the callback string as strtok destroys it. - char* callback_c_str = strdup(callback_str.c_str()); - debug("MQTT Payload (raw): " + callback_str); - // Save the message as the last command seen (global). - lastMqttCmd = callback_str; - lastMqttCmdTime = millis(); - - // Get the numeric protocol type. - int ir_type = strtoul(strtok_r(callback_c_str, ",", &tok_ptr), NULL, 10); - char* next = strtok_r(NULL, ",", &tok_ptr); - // If there is unparsed string left, try to convert it assuming it's hex. - if (next != NULL) { - code = getUInt64fromHex(next); - next = strtok_r(NULL, ",", &tok_ptr); - } else { - // We require at least two value in the string. Give up. - return; - } - // If there is still string left, assume it is the bit size. - if (next != NULL) { - nbits = atoi(next); - next = strtok_r(NULL, ",", &tok_ptr); - } - // If there is still string left, assume it is the repeat count. - if (next != NULL) - repeat = atoi(next); - - free(callback_c_str); - - - // send received MQTT value by IR signal - lastSendSucceeded = sendIRCode( - ir_type, code, - callback_str.substring(callback_str.indexOf(",") + 1).c_str(), - nbits, repeat); -} - -// Callback function, when the gateway receive an MQTT value on the topics -// subscribed this function is called -void callback(char* topic, byte* payload, unsigned int length) { - // In order to republish this payload, a copy must be made - // as the orignal payload buffer will be overwritten whilst - // constructing the PUBLISH packet. - // Allocate the correct amount of memory for the payload copy - byte* payload_copy = reinterpret_cast(malloc(length + 1)); - // Copy the payload to the new buffer - memcpy(payload_copy, payload, length); - - // Conversion to a printable string - payload_copy[length] = '\0'; - String callback_string = String(reinterpret_cast(payload_copy)); - String topic_name = String(reinterpret_cast(topic)); - - // launch the function to treat received data - receivingMQTT(topic_name, callback_string); - - // Free the memory - free(payload_copy); -} -#endif // MQTT_ENABLE diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/IRrecvDump/platformio.ini b/lib/IRremoteESP8266-2.5.2.03/examples/IRrecvDump/platformio.ini deleted file mode 100644 index eeb8d1f2e..000000000 --- a/lib/IRremoteESP8266-2.5.2.03/examples/IRrecvDump/platformio.ini +++ /dev/null @@ -1,17 +0,0 @@ -[platformio] -lib_extra_dirs = ../../ -src_dir=. - -[common] -build_flags = -lib_deps_builtin = -lib_deps_external = - -[env:nodemcuv2] -platform = espressif8266 -framework = arduino -board = nodemcuv2 -build_flags = ${common.build_flags} -lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/IRrecvDumpV2/platformio.ini b/lib/IRremoteESP8266-2.5.2.03/examples/IRrecvDumpV2/platformio.ini deleted file mode 100644 index eeb8d1f2e..000000000 --- a/lib/IRremoteESP8266-2.5.2.03/examples/IRrecvDumpV2/platformio.ini +++ /dev/null @@ -1,17 +0,0 @@ -[platformio] -lib_extra_dirs = ../../ -src_dir=. - -[common] -build_flags = -lib_deps_builtin = -lib_deps_external = - -[env:nodemcuv2] -platform = espressif8266 -framework = arduino -board = nodemcuv2 -build_flags = ${common.build_flags} -lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/IRsendDemo/platformio.ini b/lib/IRremoteESP8266-2.5.2.03/examples/IRsendDemo/platformio.ini deleted file mode 100644 index eeb8d1f2e..000000000 --- a/lib/IRremoteESP8266-2.5.2.03/examples/IRsendDemo/platformio.ini +++ /dev/null @@ -1,17 +0,0 @@ -[platformio] -lib_extra_dirs = ../../ -src_dir=. - -[common] -build_flags = -lib_deps_builtin = -lib_deps_external = - -[env:nodemcuv2] -platform = espressif8266 -framework = arduino -board = nodemcuv2 -build_flags = ${common.build_flags} -lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/IRsendProntoDemo/platformio.ini b/lib/IRremoteESP8266-2.5.2.03/examples/IRsendProntoDemo/platformio.ini deleted file mode 100644 index eeb8d1f2e..000000000 --- a/lib/IRremoteESP8266-2.5.2.03/examples/IRsendProntoDemo/platformio.ini +++ /dev/null @@ -1,17 +0,0 @@ -[platformio] -lib_extra_dirs = ../../ -src_dir=. - -[common] -build_flags = -lib_deps_builtin = -lib_deps_external = - -[env:nodemcuv2] -platform = espressif8266 -framework = arduino -board = nodemcuv2 -build_flags = ${common.build_flags} -lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/JVCPanasonicSendDemo/platformio.ini b/lib/IRremoteESP8266-2.5.2.03/examples/JVCPanasonicSendDemo/platformio.ini deleted file mode 100644 index eeb8d1f2e..000000000 --- a/lib/IRremoteESP8266-2.5.2.03/examples/JVCPanasonicSendDemo/platformio.ini +++ /dev/null @@ -1,17 +0,0 @@ -[platformio] -lib_extra_dirs = ../../ -src_dir=. - -[common] -build_flags = -lib_deps_builtin = -lib_deps_external = - -[env:nodemcuv2] -platform = espressif8266 -framework = arduino -board = nodemcuv2 -build_flags = ${common.build_flags} -lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/LGACSend/platformio.ini b/lib/IRremoteESP8266-2.5.2.03/examples/LGACSend/platformio.ini deleted file mode 100644 index eeb8d1f2e..000000000 --- a/lib/IRremoteESP8266-2.5.2.03/examples/LGACSend/platformio.ini +++ /dev/null @@ -1,17 +0,0 @@ -[platformio] -lib_extra_dirs = ../../ -src_dir=. - -[common] -build_flags = -lib_deps_builtin = -lib_deps_external = - -[env:nodemcuv2] -platform = espressif8266 -framework = arduino -board = nodemcuv2 -build_flags = ${common.build_flags} -lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/TurnOnArgoAC/platformio.ini b/lib/IRremoteESP8266-2.5.2.03/examples/TurnOnArgoAC/platformio.ini deleted file mode 100644 index eeb8d1f2e..000000000 --- a/lib/IRremoteESP8266-2.5.2.03/examples/TurnOnArgoAC/platformio.ini +++ /dev/null @@ -1,17 +0,0 @@ -[platformio] -lib_extra_dirs = ../../ -src_dir=. - -[common] -build_flags = -lib_deps_builtin = -lib_deps_external = - -[env:nodemcuv2] -platform = espressif8266 -framework = arduino -board = nodemcuv2 -build_flags = ${common.build_flags} -lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/TurnOnDaikinAC/platformio.ini b/lib/IRremoteESP8266-2.5.2.03/examples/TurnOnDaikinAC/platformio.ini deleted file mode 100644 index eeb8d1f2e..000000000 --- a/lib/IRremoteESP8266-2.5.2.03/examples/TurnOnDaikinAC/platformio.ini +++ /dev/null @@ -1,17 +0,0 @@ -[platformio] -lib_extra_dirs = ../../ -src_dir=. - -[common] -build_flags = -lib_deps_builtin = -lib_deps_external = - -[env:nodemcuv2] -platform = espressif8266 -framework = arduino -board = nodemcuv2 -build_flags = ${common.build_flags} -lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/TurnOnFujitsuAC/platformio.ini b/lib/IRremoteESP8266-2.5.2.03/examples/TurnOnFujitsuAC/platformio.ini deleted file mode 100644 index eeb8d1f2e..000000000 --- a/lib/IRremoteESP8266-2.5.2.03/examples/TurnOnFujitsuAC/platformio.ini +++ /dev/null @@ -1,17 +0,0 @@ -[platformio] -lib_extra_dirs = ../../ -src_dir=. - -[common] -build_flags = -lib_deps_builtin = -lib_deps_external = - -[env:nodemcuv2] -platform = espressif8266 -framework = arduino -board = nodemcuv2 -build_flags = ${common.build_flags} -lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/TurnOnKelvinatorAC/platformio.ini b/lib/IRremoteESP8266-2.5.2.03/examples/TurnOnKelvinatorAC/platformio.ini deleted file mode 100644 index eeb8d1f2e..000000000 --- a/lib/IRremoteESP8266-2.5.2.03/examples/TurnOnKelvinatorAC/platformio.ini +++ /dev/null @@ -1,17 +0,0 @@ -[platformio] -lib_extra_dirs = ../../ -src_dir=. - -[common] -build_flags = -lib_deps_builtin = -lib_deps_external = - -[env:nodemcuv2] -platform = espressif8266 -framework = arduino -board = nodemcuv2 -build_flags = ${common.build_flags} -lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/TurnOnMitsubishiAC/platformio.ini b/lib/IRremoteESP8266-2.5.2.03/examples/TurnOnMitsubishiAC/platformio.ini deleted file mode 100644 index eeb8d1f2e..000000000 --- a/lib/IRremoteESP8266-2.5.2.03/examples/TurnOnMitsubishiAC/platformio.ini +++ /dev/null @@ -1,17 +0,0 @@ -[platformio] -lib_extra_dirs = ../../ -src_dir=. - -[common] -build_flags = -lib_deps_builtin = -lib_deps_external = - -[env:nodemcuv2] -platform = espressif8266 -framework = arduino -board = nodemcuv2 -build_flags = ${common.build_flags} -lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/TurnOnToshibaAC/platformio.ini b/lib/IRremoteESP8266-2.5.2.03/examples/TurnOnToshibaAC/platformio.ini deleted file mode 100644 index eeb8d1f2e..000000000 --- a/lib/IRremoteESP8266-2.5.2.03/examples/TurnOnToshibaAC/platformio.ini +++ /dev/null @@ -1,17 +0,0 @@ -[platformio] -lib_extra_dirs = ../../ -src_dir=. - -[common] -build_flags = -lib_deps_builtin = -lib_deps_external = - -[env:nodemcuv2] -platform = espressif8266 -framework = arduino -board = nodemcuv2 -build_flags = ${common.build_flags} -lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/TurnOnTrotecAC/platformio.ini b/lib/IRremoteESP8266-2.5.2.03/examples/TurnOnTrotecAC/platformio.ini deleted file mode 100644 index eeb8d1f2e..000000000 --- a/lib/IRremoteESP8266-2.5.2.03/examples/TurnOnTrotecAC/platformio.ini +++ /dev/null @@ -1,17 +0,0 @@ -[platformio] -lib_extra_dirs = ../../ -src_dir=. - -[common] -build_flags = -lib_deps_builtin = -lib_deps_external = - -[env:nodemcuv2] -platform = espressif8266 -framework = arduino -board = nodemcuv2 -build_flags = ${common.build_flags} -lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.5.2.03/src/IRutils.cpp b/lib/IRremoteESP8266-2.5.2.03/src/IRutils.cpp deleted file mode 100644 index 7864625a5..000000000 --- a/lib/IRremoteESP8266-2.5.2.03/src/IRutils.cpp +++ /dev/null @@ -1,470 +0,0 @@ -// Copyright 2017 David Conran - -#include "IRutils.h" -#ifndef UNIT_TEST -#include -#endif - -#define __STDC_LIMIT_MACROS -#include -#include -#ifndef ARDUINO -#include -#endif -#include "IRrecv.h" -#include "IRremoteESP8266.h" - -// Reverse the order of the requested least significant nr. of bits. -// Args: -// input: Bit pattern/integer to reverse. -// nbits: Nr. of bits to reverse. -// Returns: -// The reversed bit pattern. -uint64_t reverseBits(uint64_t input, uint16_t nbits) { - if (nbits <= 1) return input; // Reversing <= 1 bits makes no change at all. - // Cap the nr. of bits to rotate to the max nr. of bits in the input. - nbits = std::min(nbits, (uint16_t)(sizeof(input) * 8)); - uint64_t output = 0; - for (uint16_t i = 0; i < nbits; i++) { - output <<= 1; - output |= (input & 1); - input >>= 1; - } - // Merge any remaining unreversed bits back to the top of the reversed bits. - return (input << nbits) | output; -} - -// Convert a uint64_t (unsigned long long) to a string. -// Arduino String/toInt/Serial.print() can't handle printing 64 bit values. -// -// Args: -// input: The value to print -// base: The output base. -// Returns: -// A string representation of the integer. -// Note: Based on Arduino's Print::printNumber() -#ifdef ARDUINO // Arduino's & C++'s string implementations can't co-exist. -String uint64ToString(uint64_t input, uint8_t base) { - String result = ""; -#else -std::string uint64ToString(uint64_t input, uint8_t base) { - std::string result = ""; -#endif - // prevent issues if called with base <= 1 - if (base < 2) base = 10; - // Check we have a base that we can actually print. - // i.e. [0-9A-Z] == 36 - if (base > 36) base = 10; - - do { - char c = input % base; - input /= base; - - if (c < 10) - c += '0'; - else - c += 'A' - 10; - result = c + result; - } while (input); - return result; -} - -#ifdef ARDUINO -// Print a uint64_t/unsigned long long to the Serial port -// Serial.print() can't handle printing long longs. (uint64_t) -// -// Args: -// input: The value to print -// base: The output base. -void serialPrintUint64(uint64_t input, uint8_t base) { - Serial.print(uint64ToString(input, base)); -} -#endif - -// Convert a protocol type (enum etc) to a human readable string. -// Args: -// protocol: Nr. (enum) of the protocol. -// isRepeat: A flag indicating if it is a repeat message of the protocol. -// Returns: -// A string containing the protocol name. -#ifdef ARDUINO // Arduino's & C++'s string implementations can't co-exist. -String typeToString(const decode_type_t protocol, const bool isRepeat) { - String result = ""; -#else -std::string typeToString(const decode_type_t protocol, const bool isRepeat) { - std::string result = ""; -#endif - switch (protocol) { - default: - case UNKNOWN: - result = "UNKNOWN"; - break; - case UNUSED: - result = "UNUSED"; - break; - case AIWA_RC_T501: - result = "AIWA_RC_T501"; - break; - case ARGO: - result = "ARGO"; - break; - case CARRIER_AC: - result = "CARRIER_AC"; - break; - case COOLIX: - result = "COOLIX"; - break; - case DAIKIN: - result = "DAIKIN"; - break; - case DENON: - result = "DENON"; - break; - case DISH: - result = "DISH"; - break; - case ELECTRA_AC: - result = "ELECTRA_AC"; - break; - case FUJITSU_AC: - result = "FUJITSU_AC"; - break; - case GICABLE: - result = "GICABLE"; - break; - case GLOBALCACHE: - result = "GLOBALCACHE"; - break; - case GREE: - result = "GREE"; - break; - case HAIER_AC: - result = "HAIER_AC"; - break; - case HAIER_AC_YRW02: - result = "HAIER_AC_YRW02"; - break; - case HITACHI_AC: - result = "HITACHI_AC"; - break; - case HITACHI_AC1: - result = "HITACHI_AC1"; - break; - case HITACHI_AC2: - result = "HITACHI_AC2"; - break; - case JVC: - result = "JVC"; - break; - case KELVINATOR: - result = "KELVINATOR"; - break; - case LG: - result = "LG"; - break; - case LG2: - result = "LG2"; - break; - case LASERTAG: - result = "LASERTAG"; - break; - case LUTRON: - result = "LUTRON"; - break; - case MAGIQUEST: - result = "MAGIQUEST"; - break; - case MIDEA: - result = "MIDEA"; - break; - case MITSUBISHI: - result = "MITSUBISHI"; - break; - case MITSUBISHI2: - result = "MITSUBISHI2"; - break; - case MITSUBISHI_AC: - result = "MITSUBISHI_AC"; - break; - case MWM: - result = "MWM"; - break; - case NEC: - result = "NEC"; - break; - case NEC_LIKE: - result = "NEC (non-strict)"; - break; - case NIKAI: - result = "NIKAI"; - break; - case PANASONIC: - result = "PANASONIC"; - break; - case PANASONIC_AC: - result = "PANASONIC_AC"; - break; - case PIONEER: - result = "PIONEER"; - break; - case PRONTO: - result = "PRONTO"; - break; - case RAW: - result = "RAW"; - break; - case RC5: - result = "RC5"; - break; - case RC5X: - result = "RC5X"; - break; - case RC6: - result = "RC6"; - break; - case RCMM: - result = "RCMM"; - break; - case SAMSUNG: - result = "SAMSUNG"; - break; - case SAMSUNG_AC: - result = "SAMSUNG_AC"; - break; - case SANYO: - result = "SANYO"; - break; - case SANYO_LC7461: - result = "SANYO_LC7461"; - break; - case SHARP: - result = "SHARP"; - break; - case SHERWOOD: - result = "SHERWOOD"; - break; - case SONY: - result = "SONY"; - break; - case TOSHIBA_AC: - result = "TOSHIBA_AC"; - break; - case TROTEC: - result = "TROTEC"; - break; - case WHIRLPOOL_AC: - result = "WHIRLPOOL_AC"; - break; - case WHYNTER: - result = "WHYNTER"; - break; - } - if (isRepeat) result += " (Repeat)"; - return result; -} - -// Does the given protocol use a complex state as part of the decode? -bool hasACState(const decode_type_t protocol) { - switch (protocol) { - case DAIKIN: - case ELECTRA_AC: - case FUJITSU_AC: - case GREE: - case HAIER_AC: - case HAIER_AC_YRW02: - case HITACHI_AC: - case HITACHI_AC1: - case HITACHI_AC2: - case KELVINATOR: - case MITSUBISHI_AC: - case MWM: - case PANASONIC_AC: - case SAMSUNG_AC: - case TOSHIBA_AC: - case WHIRLPOOL_AC: - return true; - default: - return false; - } -} - -// Return the corrected length of a 'raw' format array structure -// after over-large values are converted into multiple entries. -// Args: -// results: A ptr to a decode result. -// Returns: -// A uint16_t containing the length. -uint16_t getCorrectedRawLength(const decode_results *results) { - uint16_t extended_length = results->rawlen - 1; - for (uint16_t i = 0; i < results->rawlen - 1; i++) { - uint32_t usecs = results->rawbuf[i] * kRawTick; - // Add two extra entries for multiple larger than UINT16_MAX it is. - extended_length += (usecs / (UINT16_MAX + 1)) * 2; - } - return extended_length; -} - -// Return a string containing the key values of a decode_results structure -// in a C/C++ code style format. -#ifdef ARDUINO -String resultToSourceCode(const decode_results *results) { - String output = ""; -#else -std::string resultToSourceCode(const decode_results *results) { - std::string output = ""; -#endif - // Start declaration - output += "uint16_t "; // variable type - output += "rawData["; // array name - output += uint64ToString(getCorrectedRawLength(results), 10); - // array size - output += "] = {"; // Start declaration - - // Dump data - for (uint16_t i = 1; i < results->rawlen; i++) { - uint32_t usecs; - for (usecs = results->rawbuf[i] * kRawTick; usecs > UINT16_MAX; - usecs -= UINT16_MAX) { - output += uint64ToString(UINT16_MAX); - if (i % 2) - output += ", 0, "; - else - output += ", 0, "; - } - output += uint64ToString(usecs, 10); - if (i < results->rawlen - 1) - output += ", "; // ',' not needed on the last one - if (i % 2 == 0) output += " "; // Extra if it was even. - } - - // End declaration - output += "};"; - - // Comment - output += " // " + typeToString(results->decode_type, results->repeat); - // Only display the value if the decode type doesn't have an A/C state. - if (!hasACState(results->decode_type)) - output += " " + uint64ToString(results->value, 16); - output += "\n"; - - // Now dump "known" codes - if (results->decode_type != UNKNOWN) { - if (hasACState(results->decode_type)) { -#if DECODE_AC - uint16_t nbytes = results->bits / 8; - output += "uint8_t state[" + uint64ToString(nbytes) + "] = {"; - for (uint16_t i = 0; i < nbytes; i++) { - output += "0x"; - if (results->state[i] < 0x10) output += "0"; - output += uint64ToString(results->state[i], 16); - if (i < nbytes - 1) output += ", "; - } - output += "};\n"; -#endif // DECODE_AC - } else { - // Simple protocols - // Some protocols have an address &/or command. - // NOTE: It will ignore the atypical case when a message has been - // decoded but the address & the command are both 0. - if (results->address > 0 || results->command > 0) { - output += "uint32_t address = 0x" + - uint64ToString(results->address, 16) + ";\n"; - output += "uint32_t command = 0x" + - uint64ToString(results->command, 16) + ";\n"; - } - // Most protocols have data - output += - "uint64_t data = 0x" + uint64ToString(results->value, 16) + ";\n"; - } - } - return output; -} - -// Dump out the decode_results structure. -// -#ifdef ARDUINO -String resultToTimingInfo(const decode_results *results) { - String output = ""; - String value = ""; -#else -std::string resultToTimingInfo(const decode_results *results) { - std::string output = ""; - std::string value = ""; -#endif - output += "Raw Timing[" + uint64ToString(results->rawlen - 1, 10) + "]:\n"; - - for (uint16_t i = 1; i < results->rawlen; i++) { - if (i % 2 == 0) - output += "-"; // even - else - output += " +"; // odd - value = uint64ToString(results->rawbuf[i] * kRawTick); - // Space pad the value till it is at least 6 chars long. - while (value.length() < 6) value = " " + value; - output += value; - if (i < results->rawlen - 1) output += ", "; // ',' not needed for last one - if (!(i % 8)) output += "\n"; // Newline every 8 entries. - } - output += "\n"; - return output; -} - -// Convert the decode_results structure's value/state to simple hexadecimal. -// -#ifdef ARDUINO -String resultToHexidecimal(const decode_results *result) { - String output = ""; -#else -std::string resultToHexidecimal(const decode_results *result) { - std::string output = ""; -#endif - if (hasACState(result->decode_type)) { -#if DECODE_AC - for (uint16_t i = 0; result->bits > i * 8; i++) { - if (result->state[i] < 0x10) output += "0"; // Zero pad - output += uint64ToString(result->state[i], 16); - } -#endif // DECODE_AC - } else { - output += uint64ToString(result->value, 16); - } - return output; -} - -// Dump out the decode_results structure. -// -#ifdef ARDUINO -String resultToHumanReadableBasic(const decode_results *results) { - String output = ""; -#else -std::string resultToHumanReadableBasic(const decode_results *results) { - std::string output = ""; -#endif - // Show Encoding standard - output += - "Encoding : " + typeToString(results->decode_type, results->repeat) + - "\n"; - - // Show Code & length - output += "Code : "; - output += resultToHexidecimal(results); - output += " (" + uint64ToString(results->bits) + " bits)\n"; - return output; -} - -uint8_t sumBytes(uint8_t *start, const uint16_t length, const uint8_t init) { - uint8_t checksum = init; - uint8_t *ptr; - for (ptr = start; ptr - start < length; ptr++) checksum += *ptr; - return checksum; -} - -uint64_t invertBits(const uint64_t data, const uint16_t nbits) { - // No change if we are asked to invert no bits. - if (nbits == 0) return data; - uint64_t result = ~data; - // If we are asked to invert all the bits or more than we have, it's simple. - if (nbits >= sizeof(data) * 8) return result; - // Mask off any unwanted bits and return the result. - return (result & ((1ULL << nbits) - 1)); -} diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Daikin.cpp b/lib/IRremoteESP8266-2.5.2.03/src/ir_Daikin.cpp deleted file mode 100644 index b94b4a63a..000000000 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Daikin.cpp +++ /dev/null @@ -1,750 +0,0 @@ -/* -An Arduino sketch to emulate IR Daikin ARC433** remote control unit -Read more at: -http://harizanov.com/2012/02/control-daikin-air-conditioner-over-the-internet/ - -Copyright 2016 sillyfrog -Copyright 2017 sillyfrog, crankyoldgit -*/ - -#include "ir_Daikin.h" -#include -#ifndef ARDUINO -#include -#endif -#include "IRrecv.h" -#include "IRremoteESP8266.h" -#include "IRsend.h" -#include "IRutils.h" - -// DDDDD AAA IIIII KK KK IIIII NN NN -// DD DD AAAAA III KK KK III NNN NN -// DD DD AA AA III KKKK III NN N NN -// DD DD AAAAAAA III KK KK III NN NNN -// DDDDDD AA AA IIIII KK KK IIIII NN NN - -// Constants -// Ref: -// https://github.com/mharizanov/Daikin-AC-remote-control-over-the-Internet/tree/master/IRremote -// http://rdlab.cdmt.vn/project-2013/daikin-ir-protocol - -#if SEND_DAIKIN -// Original header -// static uint8_t header1[DAIKIN_HEADER1_LENGTH]; -// header1[0] = 0b00010001; -// header1[1] = 0b11011010; -// header1[2] = 0b00100111; -// header1[3] = 0b00000000; -// header1[4] = 0b11000101; -// header1[5] = 0b00000000; -// header1[6] = 0b00000000; -// header1[7] = 0b11010111; - -// Send a Daikin A/C message. -// -// Args: -// data: An array of kDaikinStateLength bytes containing the IR command. -// -// Status: STABLE -// -// Ref: -// IRDaikinESP.cpp -// https://github.com/mharizanov/Daikin-AC-remote-control-over-the-Internet/tree/master/IRremote -void IRsend::sendDaikin(unsigned char data[], uint16_t nbytes, - uint16_t repeat) { - if (nbytes < kDaikinStateLength) - return; // Not enough bytes to send a proper message. - - for (uint16_t r = 0; r <= repeat; r++) { - // Send the header, 0b00000 - sendGeneric(0, 0, // No header for the header - kDaikinBitMark, kDaikinOneSpace, kDaikinBitMark, - kDaikinZeroSpace, kDaikinBitMark, kDaikinZeroSpace + kDaikinGap, - (uint64_t)0b00000, 5, 38, false, 0, 50); - // Leading header - // Do this as a constant to save RAM and keep in flash memory - sendGeneric(kDaikinHdrMark, kDaikinHdrSpace, kDaikinBitMark, - kDaikinOneSpace, kDaikinBitMark, kDaikinZeroSpace, - kDaikinBitMark, kDaikinZeroSpace + kDaikinGap, - kDaikinFirstHeader64, 64, 38, false, 0, 50); - // Data #1 - sendGeneric(kDaikinHdrMark, kDaikinHdrSpace, kDaikinBitMark, - kDaikinOneSpace, kDaikinBitMark, kDaikinZeroSpace, - kDaikinBitMark, kDaikinZeroSpace + kDaikinGap, data, 8, 38, - false, 0, 50); - // Data #2 - sendGeneric(kDaikinHdrMark, kDaikinHdrSpace, kDaikinBitMark, - kDaikinOneSpace, kDaikinBitMark, kDaikinZeroSpace, - kDaikinBitMark, kDaikinZeroSpace + kDaikinGap, data + 8, - nbytes - 8, 38, false, 0, 50); - } -} -#endif // SEND_DAIKIN - -IRDaikinESP::IRDaikinESP(uint16_t pin) : _irsend(pin) { stateReset(); } - -void IRDaikinESP::begin() { _irsend.begin(); } - -#if SEND_DAIKIN -void IRDaikinESP::send() { - checksum(); - _irsend.sendDaikin(daikin); -} -#endif // SEND_DAIKIN - -// Calculate the checksum for a given data block. -// Args: -// block: Ptr to the start of the data block. -// length: Nr. of bytes to checksum. -// Returns: -// A byte containing the calculated checksum. -uint8_t IRDaikinESP::calcBlockChecksum(const uint8_t *block, - const uint16_t length) { - uint8_t sum = 0; - // Daikin checksum is just the addition of all the data bytes - // in the block but capped to 8 bits. - for (uint16_t i = 0; i < length; i++, block++) sum += *block; - return sum & 0xFFU; -} - -// Verify the checksum is valid for a given state. -// Args: -// state: The array to verify the checksum of. -// length: The size of the state. -// Returns: -// A boolean. -bool IRDaikinESP::validChecksum(const uint8_t state[], const uint16_t length) { - if (length < 8 || state[7] != calcBlockChecksum(state, 7)) return false; - if (length < 10 || - state[length - 1] != calcBlockChecksum(state + 8, length - 9)) - return false; - return true; -} - -// Calculate and set the checksum values for the internal state. -void IRDaikinESP::checksum() { - daikin[7] = calcBlockChecksum(daikin, 7); - daikin[26] = calcBlockChecksum(daikin + 8, 17); -} - -void IRDaikinESP::stateReset() { - for (uint8_t i = 0; i < kDaikinStateLength; i++) daikin[i] = 0x0; - - daikin[0] = 0x11; - daikin[1] = 0xDA; - daikin[2] = 0x27; - daikin[4] = 0x42; - // daikin[7] is a checksum byte, it will be set by checksum(). - daikin[8] = 0x11; - daikin[9] = 0xDA; - daikin[10] = 0x27; - daikin[13] = 0x49; - daikin[14] = 0x1E; - daikin[16] = 0xB0; - daikin[19] = 0x06; - daikin[20] = 0x60; - daikin[23] = 0xC0; - // daikin[26] is a checksum byte, it will be set by checksum(). - checksum(); -} - -uint8_t *IRDaikinESP::getRaw() { - checksum(); // Ensure correct settings before sending. - return daikin; -} - -void IRDaikinESP::setRaw(uint8_t new_code[]) { - for (uint8_t i = 0; i < kDaikinStateLength; i++) daikin[i] = new_code[i]; -} - -void IRDaikinESP::on() { - // state = ON; - setBit(kDaikinBytePower, kDaikinBitPower); -} - -void IRDaikinESP::off() { - // state = OFF; - clearBit(kDaikinBytePower, kDaikinBitPower); -} - -void IRDaikinESP::setPower(bool state) { - if (state) - on(); - else - off(); -} - -bool IRDaikinESP::getPower() { - return (getBit(kDaikinBytePower, kDaikinBitPower) > 0); -} - -// Set the temp in deg C -void IRDaikinESP::setTemp(uint8_t temp) { - if (temp < kDaikinMinTemp) - temp = kDaikinMinTemp; - else if (temp > kDaikinMaxTemp) - temp = kDaikinMaxTemp; - daikin[14] = temp * 2; -} - -uint8_t IRDaikinESP::getTemp() { return daikin[14] / 2; } - -// Set the speed of the fan, 1-5 or kDaikinFanAuto or kDaikinFanQuiet -void IRDaikinESP::setFan(uint8_t fan) { - // Set the fan speed bits, leave low 4 bits alone - uint8_t fanset; - if (fan == kDaikinFanQuiet || fan == kDaikinFanAuto) - fanset = fan; - else if (fan < kDaikinFanMin || fan > kDaikinFanMax) - fanset = kDaikinFanAuto; - else - fanset = 2 + fan; - daikin[16] &= 0x0F; - daikin[16] |= (fanset << 4); -} - -uint8_t IRDaikinESP::getFan() { - uint8_t fan = daikin[16] >> 4; - if (fan != kDaikinFanQuiet && fan != kDaikinFanAuto) fan -= 2; - return fan; -} - -uint8_t IRDaikinESP::getMode() { - /* - kDaikinCool - kDaikinHeat - kDaikinFan - kDaikinAuto - kDaikinDry - */ - return daikin[13] >> 4; -} - -void IRDaikinESP::setMode(uint8_t mode) { - switch (mode) { - case kDaikinCool: - case kDaikinHeat: - case kDaikinFan: - case kDaikinDry: - break; - default: - mode = kDaikinAuto; - } - mode <<= 4; - daikin[13] &= 0b10001111; - daikin[13] |= mode; -} - -void IRDaikinESP::setSwingVertical(bool state) { - if (state) - daikin[16] |= 0x0F; - else - daikin[16] &= 0xF0; -} - -bool IRDaikinESP::getSwingVertical() { return daikin[16] & 0x01; } - -void IRDaikinESP::setSwingHorizontal(bool state) { - if (state) - daikin[17] |= 0x0F; - else - daikin[17] &= 0xF0; -} - -bool IRDaikinESP::getSwingHorizontal() { return daikin[17] & 0x01; } - -void IRDaikinESP::setQuiet(bool state) { - if (state) { - setBit(kDaikinByteSilent, kDaikinBitSilent); - // Powerful & Quiet mode being on are mutually exclusive. - setPowerful(false); - } else { - clearBit(kDaikinByteSilent, kDaikinBitSilent); - } -} - -bool IRDaikinESP::getQuiet() { - return (getBit(kDaikinByteSilent, kDaikinBitSilent) > 0); -} - -void IRDaikinESP::setPowerful(bool state) { - if (state) { - setBit(kDaikinBytePowerful, kDaikinBitPowerful); - // Powerful, Quiet, & Econo mode being on are mutually exclusive. - setQuiet(false); - setEcono(false); - } else { - clearBit(kDaikinBytePowerful, kDaikinBitPowerful); - } -} - -bool IRDaikinESP::getPowerful() { - return (getBit(kDaikinBytePowerful, kDaikinBitPowerful) > 0); -} - -void IRDaikinESP::setSensor(bool state) { - if (state) - setBit(kDaikinByteSensor, kDaikinBitSensor); - else - clearBit(kDaikinByteSensor, kDaikinBitSensor); -} - -bool IRDaikinESP::getSensor() { - return (getBit(kDaikinByteSensor, kDaikinBitSensor) > 0); -} - -void IRDaikinESP::setEcono(bool state) { - if (state) { - setBit(kDaikinByteEcono, kDaikinBitEcono); - // Powerful & Econo mode being on are mutually exclusive. - setPowerful(false); - } else { - clearBit(kDaikinByteEcono, kDaikinBitEcono); - } -} - -bool IRDaikinESP::getEcono() { - return (getBit(kDaikinByteEcono, kDaikinBitEcono) > 0); -} - -void IRDaikinESP::setEye(bool state) { - if (state) - setBit(kDaikinByteEye, kDaikinBitEye); - else - clearBit(kDaikinByteEye, kDaikinBitEye); -} - -bool IRDaikinESP::getEye() { - return (getBit(kDaikinByteEye, kDaikinBitEye) > 0); -} - -void IRDaikinESP::setMold(bool state) { - if (state) - setBit(kDaikinByteMold, kDaikinBitMold); - else - clearBit(kDaikinByteMold, kDaikinBitMold); -} - -bool IRDaikinESP::getMold() { - return (getBit(kDaikinByteMold, kDaikinBitMold) > 0); -} - -void IRDaikinESP::setBit(uint8_t byte, uint8_t bitmask) { - daikin[byte] |= bitmask; -} - -void IRDaikinESP::clearBit(uint8_t byte, uint8_t bitmask) { - bitmask = ~bitmask; - daikin[byte] &= bitmask; -} - -uint8_t IRDaikinESP::getBit(uint8_t byte, uint8_t bitmask) { - return daikin[byte] & bitmask; -} - -// starttime: Number of minutes after midnight, in 10 minutes increments -void IRDaikinESP::enableOnTimer(uint16_t starttime) { - setBit(kDaikinByteOnTimer, kDaikinBitOnTimer); - daikin[18] = (uint8_t)(starttime & 0x00FF); - // only keep 4 bits - daikin[19] &= 0xF0; - daikin[19] |= (uint8_t)((starttime >> 8) & 0x0F); -} - -void IRDaikinESP::disableOnTimer() { - enableOnTimer(0x600); - clearBit(kDaikinByteOnTimer, kDaikinBitOnTimer); -} - -uint16_t IRDaikinESP::getOnTime() { - uint16_t ret; - ret = daikin[19] & 0x0F; - ret = ret << 8; - ret += daikin[18]; - return ret; -} - -bool IRDaikinESP::getOnTimerEnabled() { - return getBit(kDaikinByteOnTimer, kDaikinBitOnTimer); -} - -// endtime: Number of minutes after midnight, in 10 minutes increments -void IRDaikinESP::enableOffTimer(uint16_t endtime) { - setBit(kDaikinByteOffTimer, kDaikinBitOffTimer); - daikin[20] = (uint8_t)((endtime >> 4) & 0xFF); - daikin[19] &= 0x0F; - daikin[19] |= (uint8_t)((endtime & 0x000F) << 4); -} - -void IRDaikinESP::disableOffTimer() { - enableOffTimer(0x600); - clearBit(kDaikinByteOffTimer, kDaikinBitOffTimer); -} - -uint16_t IRDaikinESP::getOffTime() { - uint16_t ret, tmp; - ret = daikin[20]; - ret <<= 4; - tmp = daikin[19] & 0xF0; - tmp >>= 4; - ret += tmp; - return ret; -} - -bool IRDaikinESP::getOffTimerEnabled() { - return getBit(kDaikinByteOffTimer, kDaikinBitOffTimer); -} - -void IRDaikinESP::setCurrentTime(uint16_t numMins) { - if (numMins > 24 * 60) numMins = 0; // If > 23:59, set to 00:00 - daikin[5] = (uint8_t)(numMins & 0x00FF); - // only keep 4 bits - daikin[6] &= 0xF0; - daikin[6] |= (uint8_t)((numMins >> 8) & 0x0F); -} - -uint16_t IRDaikinESP::getCurrentTime() { - uint16_t ret; - ret = daikin[6] & 0x0F; - ret <<= 8; - ret += daikin[5]; - return ret; -} - -#ifdef ARDUINO -String IRDaikinESP::renderTime(uint16_t timemins) { - String ret; -#else // ARDUINO -std::string IRDaikinESP::renderTime(uint16_t timemins) { - std::string ret; -#endif // ARDUINO - uint16_t hours, mins; - hours = timemins / 60; - ret = uint64ToString(hours) + ":"; - mins = timemins - (hours * 60); - if (mins < 10) ret += "0"; - ret += uint64ToString(mins); - return ret; -} - -// Convert the internal state into a human readable string. -#ifdef ARDUINO -String IRDaikinESP::toString() { - String result = ""; -#else // ARDUINO -std::string IRDaikinESP::toString() { - std::string result = ""; -#endif // ARDUINO - result += "Power: "; - if (getPower()) - result += "On"; - else - result += "Off"; - result += ", Mode: " + uint64ToString(getMode()); - switch (getMode()) { - case kDaikinAuto: - result += " (AUTO)"; - break; - case kDaikinCool: - result += " (COOL)"; - break; - case kDaikinHeat: - result += " (HEAT)"; - break; - case kDaikinDry: - result += " (DRY)"; - break; - case kDaikinFan: - result += " (FAN)"; - break; - default: - result += " (UNKNOWN)"; - } - result += ", Temp: " + uint64ToString(getTemp()) + "C"; - result += ", Fan: " + uint64ToString(getFan()); - switch (getFan()) { - case kDaikinFanAuto: - result += " (AUTO)"; - break; - case kDaikinFanQuiet: - result += " (QUIET)"; - break; - case kDaikinFanMin: - result += " (MIN)"; - break; - case kDaikinFanMax: - result += " (MAX)"; - break; - } - result += ", Powerful: "; - if (getPowerful()) - result += "On"; - else - result += "Off"; - result += ", Quiet: "; - if (getQuiet()) - result += "On"; - else - result += "Off"; - result += ", Sensor: "; - if (getSensor()) - result += "On"; - else - result += "Off"; - result += ", Eye: "; - if (getEye()) - result += "On"; - else - result += "Off"; - result += ", Mold: "; - if (getMold()) - result += "On"; - else - result += "Off"; - result += ", Swing (Horizontal): "; - if (getSwingHorizontal()) - result += "On"; - else - result += "Off"; - result += ", Swing (Vertical): "; - if (getSwingVertical()) - result += "On"; - else - result += "Off"; - result += ", Current Time: " + renderTime(getCurrentTime()); - result += ", On Time: "; - if (getOnTimerEnabled()) - result += renderTime(getOnTime()); - else - result += "Off"; - result += ", Off Time: "; - if (getOffTimerEnabled()) - result += renderTime(getOffTime()); - else - result += "Off"; - - return result; -} - -#if DAIKIN_DEBUG -// Print what we have -void IRDaikinESP::printState() { -#ifdef ARDUINO - String strbits; -#else // ARDUINO - std::string strbits; -#endif // ARDUINO - DPRINTLN("Raw Bits:"); - for (uint8_t i = 0; i < kDaikinStateLength; i++) { - strbits = uint64ToString(daikin[i], BIN); - while (strbits.length() < 8) strbits = "0" + strbits; - DPRINT(strbits); - DPRINT(" "); - } - DPRINTLN(""); - DPRINTLN(toString()); -} -#endif // DAIKIN_DEBUG - -/* - * Return most important bits to allow replay - * layout is: - * 0: Power - * 1-3: Mode - * 4-7: Fan speed/mode - * 8-14: Target Temperature - * 15: Econo - * 16: Powerful - * 17: Quiet - * 18: Sensor - * 19: Swing Vertical - * 20-31: Current time (mins since midnight) - * */ -uint32_t IRDaikinESP::getCommand() { - uint32_t ret = 0; - uint32_t tmp = 0; - if (getPower()) ret |= 0b00000000000000000000000000000001; - tmp = getMode(); - tmp = tmp << 1; - ret |= tmp; - - tmp = getFan(); - tmp <<= 4; - ret |= tmp; - - tmp = getTemp(); - tmp <<= 8; - ret |= tmp; - - if (getEcono()) ret |= 0b00000000000000001000000000000000; - if (getPowerful()) ret |= 0b00000000000000010000000000000000; - if (getQuiet()) ret |= 0b00000000000000100000000000000000; - if (getSensor()) ret |= 0b00000000000001000000000000000000; - if (getSwingVertical()) ret |= 0b00000000000010000000000000000000; - ret |= (getCurrentTime() << 20); - return ret; -} - -void IRDaikinESP::setCommand(uint32_t value) { - uint32_t tmp = 0; - if (value & 0b00000000000000000000000000000001) setPower(true); - tmp = value & 0b00000000000000000000000000001110; - tmp >>= 1; - setMode(tmp); - - tmp = value & 0b00000000000000000000000011110000; - tmp >>= 4; - setFan(tmp); - - tmp = value & 0b00000000000000000111111100000000; - tmp >>= 8; - setTemp(tmp); - - if (value & 0b00000000000000001000000000000000) setEcono(true); - if (value & 0b00000000000000010000000000000000) setPowerful(true); - if (value & 0b00000000000000100000000000000000) setQuiet(true); - if (value & 0b00000000000001000000000000000000) setSensor(true); - if (value & 0b00000000000010000000000000000000) setSwingVertical(true); - - value >>= 20; - setCurrentTime(value); -} - -#if DECODE_DAIKIN - -void addbit(bool val, unsigned char data[]) { - uint8_t curbit = data[kDaikinCurBit]; - uint8_t curindex = data[kDaikinCurIndex]; - if (val) { - unsigned char bit = 1; - bit = bit << curbit; - data[curindex] |= bit; - } - curbit++; - if (curbit == 8) { - curbit = 0; - curindex++; - } - data[kDaikinCurBit] = curbit; - data[kDaikinCurIndex] = curindex; -} - -bool checkheader(decode_results *results, uint16_t *offset) { - if (!IRrecv::matchMark(results->rawbuf[(*offset)++], kDaikinBitMark, - kDaikinTolerance, kDaikinMarkExcess)) - return false; - if (!IRrecv::matchSpace(results->rawbuf[(*offset)++], - kDaikinZeroSpace + kDaikinGap, kDaikinTolerance, - kDaikinMarkExcess)) - return false; - if (!IRrecv::matchMark(results->rawbuf[(*offset)++], kDaikinHdrMark, - kDaikinTolerance, kDaikinMarkExcess)) - return false; - if (!IRrecv::matchSpace(results->rawbuf[(*offset)++], kDaikinHdrSpace, - kDaikinTolerance, kDaikinMarkExcess)) - return false; - - return true; -} - -bool readbits(decode_results *results, uint16_t *offset, - unsigned char daikin_code[], uint16_t countbits) { - for (uint16_t i = 0; i < countbits && *offset < results->rawlen - 1; - i++, (*offset)++) { - if (!IRrecv::matchMark(results->rawbuf[(*offset)++], kDaikinBitMark, - kDaikinTolerance, kDaikinMarkExcess)) - return false; - if (IRrecv::matchSpace(results->rawbuf[*offset], kDaikinOneSpace, - kDaikinTolerance, kDaikinMarkExcess)) - addbit(1, daikin_code); - else if (IRrecv::matchSpace(results->rawbuf[*offset], kDaikinZeroSpace, - kDaikinTolerance, kDaikinMarkExcess)) - addbit(0, daikin_code); - else - return false; - } - return true; -} - -// Decode the supplied Daikin A/C message. -// Args: -// results: Ptr to the data to decode and where to store the decode result. -// nbits: Nr. of bits to expect in the data portion. (kDaikinRawBits) -// strict: Flag to indicate if we strictly adhere to the specification. -// Returns: -// boolean: True if it can decode it, false if it can't. -// -// Status: BETA / Should be working. -// -// Notes: -// If DAIKIN_DEBUG enabled, will print all the set options and values. -// -// Ref: -// https://github.com/mharizanov/Daikin-AC-remote-control-over-the-Internet/tree/master/IRremote -bool IRrecv::decodeDaikin(decode_results *results, uint16_t nbits, - bool strict) { - if (results->rawlen < kDaikinRawBits) return false; - - // Compliance - if (strict && nbits != kDaikinRawBits) return false; - - uint16_t offset = kStartOffset; - unsigned char daikin_code[kDaikinStateLength + 2]; - for (uint8_t i = 0; i < kDaikinStateLength + 2; i++) daikin_code[i] = 0; - - // Header (#1) - for (uint8_t i = 0; i < 10; i++) { - if (!matchMark(results->rawbuf[offset++], kDaikinBitMark)) return false; - } - if (!checkheader(results, &offset)) return false; - - // Data (#1) - if (!readbits(results, &offset, daikin_code, 8 * 8)) return false; - - // Ignore everything that has just been captured as it is not needed. - // Some remotes may not send this portion, my remote did, but it's not - // required. - for (uint8_t i = 0; i < kDaikinStateLength + 2; i++) daikin_code[i] = 0; - - // Header (#2) - if (!checkheader(results, &offset)) return false; - - // Data (#2) - if (!readbits(results, &offset, daikin_code, 8 * 8)) return false; - - // Header (#3) - if (!checkheader(results, &offset)) return false; - - // Data (#3), read up everything else - if (!readbits(results, &offset, daikin_code, kDaikinBits - (8 * 8))) - return false; - - // Footer - if (!matchMark(results->rawbuf[offset++], kDaikinBitMark)) return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset], kDaikinGap)) - return false; - - // Compliance - if (strict) { - if (!IRDaikinESP::validChecksum(daikin_code)) return false; - } - - // Success -#if DAIKIN_DEBUG - IRDaikinESP dako = IRDaikinESP(0); - dako.setRaw(daikin_code); -#ifdef ARDUINO - yield(); -#endif // ARDUINO - dako.printState(); -#endif // DAIKIN_DEBUG - - // Copy across the bits to state - for (uint8_t i = 0; i < kDaikinStateLength; i++) - results->state[i] = daikin_code[i]; - results->bits = kDaikinStateLength * 8; - results->decode_type = DAIKIN; - return true; -} -#endif // DECODE_DAIKIN diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Daikin.h b/lib/IRremoteESP8266-2.5.2.03/src/ir_Daikin.h deleted file mode 100644 index 7094990d8..000000000 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Daikin.h +++ /dev/null @@ -1,208 +0,0 @@ -// Copyright 2016 sillyfrog -// Copyright 2017 sillyfrog, crankyoldgit -#ifndef IR_DAIKIN_H_ -#define IR_DAIKIN_H_ - -#ifndef UNIT_TEST -#include -#else -#include -#endif -#include "IRrecv.h" -#include "IRremoteESP8266.h" -#include "IRsend.h" - -// Option to disable the additional Daikin debug info to conserve memory -#define DAIKIN_DEBUG false - -// DDDDD AAA IIIII KK KK IIIII NN NN -// DD DD AAAAA III KK KK III NNN NN -// DD DD AA AA III KKKK III NN N NN -// DD DD AAAAAAA III KK KK III NN NNN -// DDDDDD AA AA IIIII KK KK IIIII NN NN - -/* - Daikin AC map - byte 5=Current time, mins past midnight, low bits - byte 6 - b0-b3=Current time, mins past midnight, high bits - byte 7= checksum of the first part (and last byte before a 29ms pause) - byte 13=mode - b7 = 0 - b6+b5+b4 = Mode - Modes: b6+b5+b4 - 011 = Cool - 100 = Heat (temp 23) - 110 = FAN (temp not shown, but 25) - 000 = Fully Automatic (temp 25) - 010 = DRY (temp 0xc0 = 96 degrees c) - b3 = 1 - b2 = OFF timer set - b1 = ON timer set - b0 = Air Conditioner ON - byte 14=temp*2 (Temp should be between 10 - 32) - byte 16=Fan - FAN control - b7+b6+b5+b4 = Fan speed - Fan: b7+b6+b5+b4 - 0×3 = 1 bar - 0×4 = 2 bar - 0×5 = 3 bar - 0×6 = 4 bar - 0×7 = 5 bar - 0xa = Auto - 0xb = Quite - b3+b2+b1+b0 = Swing control up/down - Swing control up/down: - 0000 = Swing up/down off - 1111 = Swing up/down on - byte 17 - Swing control left/right: - 0000 = Swing left/right off - 1111 = Swing left/right on - byte 18=On timer mins past midnight, low bits - byte 19 - b0-b3=On timer mins past midnight, high bits - b4-b7=Off timer mins past midnight, low bits - byte 20=Off timer mins past midnight, high bits - byte 21=Aux -> Powerful (bit 1), Silent (bit 5) - byte 24=Aux2 - b1: Sensor - b2: Econo mode - b7: Intelligent eye on - byte 25=Aux3 - b1: Mold Proof - byte 26= checksum of the second part -*/ - -// Constants -const uint8_t kDaikinAuto = 0b000; -const uint8_t kDaikinDry = 0b010; -const uint8_t kDaikinCool = 0b011; -const uint8_t kDaikinHeat = 0b100; -const uint8_t kDaikinFan = 0b110; -const uint8_t kDaikinMinTemp = 10; // Celsius -const uint8_t kDaikinMaxTemp = 32; // Celsius -const uint8_t kDaikinFanMin = 1; -const uint8_t kDaikinFanMax = 5; -const uint8_t kDaikinFanAuto = 0b1010; -const uint8_t kDaikinFanQuiet = 0b1011; -const uint8_t kDaikinBytePower = 13; -const uint8_t kDaikinBitPower = 0b00000001; -const uint8_t kDaikinBytePowerful = 21; -const uint8_t kDaikinBitPowerful = 0b00000001; -const uint8_t kDaikinByteSilent = 21; -const uint8_t kDaikinBitSilent = 0b00100000; -const uint8_t kDaikinByteSensor = 24; -const uint8_t kDaikinBitSensor = 0b00000010; -const uint8_t kDaikinByteEcono = 24; -const uint8_t kDaikinBitEcono = 0b00000100; -const uint8_t kDaikinByteEye = 24; -const uint8_t kDaikinBitEye = 0b10000000; -const uint8_t kDaikinByteMold = 25; -const uint8_t kDaikinBitMold = 0b00000010; -const uint8_t kDaikinByteOffTimer = 13; -const uint8_t kDaikinBitOffTimer = 0b00000100; -const uint8_t kDaikinByteOnTimer = 13; -const uint8_t kDaikinBitOnTimer = 0b00000010; -const uint8_t kDaikinCurBit = kDaikinStateLength; -const uint8_t kDaikinCurIndex = kDaikinStateLength + 1; -const uint8_t kDaikinTolerance = 35; -const uint16_t kDaikinMarkExcess = kMarkExcess; -const uint16_t kDaikinHdrMark = 3650; // kDaikinBitMark * 8 -const uint16_t kDaikinHdrSpace = 1623; // kDaikinBitMark * 4 -const uint16_t kDaikinBitMark = 428; -const uint16_t kDaikinZeroSpace = 428; -const uint16_t kDaikinOneSpace = 1280; -const uint16_t kDaikinGap = 29000; -// Note bits in each octet swapped so can be sent as a single value -const uint64_t kDaikinFirstHeader64 = - 0b1101011100000000000000001100010100000000001001111101101000010001; - -// Legacy defines. -#define DAIKIN_COOL kDaikinCool -#define DAIKIN_HEAT kDaikinHeat -#define DAIKIN_FAN kDaikinFan -#define DAIKIN_AUTO kDaikinAuto -#define DAIKIN_DRY kDaikinDry -#define DAIKIN_MIN_TEMP kDaikinMinTemp -#define DAIKIN_MAX_TEMP kDaikinMaxTemp -#define DAIKIN_FAN_MIN kDaikinFanMin -#define DAIKIN_FAN_MAX kDaikinFanMax -#define DAIKIN_FAN_AUTO kDaikinFanAuto -#define DAIKIN_FAN_QUIET kDaikinFanQuiet - -class IRDaikinESP { - public: - explicit IRDaikinESP(uint16_t pin); - -#if SEND_DAIKIN - void send(); -#endif - void begin(); - void on(); - void off(); - void setPower(bool state); - bool getPower(); - void setTemp(uint8_t temp); - uint8_t getTemp(); - void setFan(uint8_t fan); - uint8_t getFan(); - uint8_t getMode(); - void setMode(uint8_t mode); - void setSwingVertical(bool state); - bool getSwingVertical(); - void setSwingHorizontal(bool state); - bool getSwingHorizontal(); - bool getQuiet(); - void setQuiet(bool state); - bool getPowerful(); - void setPowerful(bool state); - void setSensor(bool state); - bool getSensor(); - void setEcono(bool state); - bool getEcono(); - void setEye(bool state); - bool getEye(); - void setMold(bool state); - bool getMold(); - void enableOnTimer(uint16_t starttime); - void disableOnTimer(); - uint16_t getOnTime(); - bool getOnTimerEnabled(); - void enableOffTimer(uint16_t endtime); - void disableOffTimer(); - uint16_t getOffTime(); - bool getOffTimerEnabled(); - void setCurrentTime(uint16_t time); - uint16_t getCurrentTime(); - uint8_t* getRaw(); - void setRaw(uint8_t new_code[]); -#if DAIKIN_DEBUG - void printState(); -#endif // DAIKIN_DEBUG - uint32_t getCommand(); - void setCommand(uint32_t value); - static bool validChecksum(const uint8_t state[], - const uint16_t length = kDaikinStateLength); -#ifdef ARDUINO - String toString(); - static String renderTime(uint16_t timemins); -#else - std::string toString(); - static std::string renderTime(uint16_t timemins); -#endif - - private: - // # of bytes per command - uint8_t daikin[kDaikinStateLength]; - void stateReset(); - static uint8_t calcBlockChecksum(const uint8_t* block, const uint16_t length); - void checksum(); - void setBit(uint8_t byte, uint8_t bitmask); - void clearBit(uint8_t byte, uint8_t bitmask); - uint8_t getBit(uint8_t byte, uint8_t bitmask); - IRsend _irsend; -}; - -#endif // IR_DAIKIN_H_ diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Trotec.cpp b/lib/IRremoteESP8266-2.5.2.03/src/ir_Trotec.cpp deleted file mode 100644 index 0bece2664..000000000 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Trotec.cpp +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright 2017 stufisher - -#include "ir_Trotec.h" -#include "IRremoteESP8266.h" -#include "IRutils.h" - -// Constants -const uint16_t kTrotecHdrMark = 5952; -const uint16_t kTrotecHdrSpace = 7364; -const uint16_t kTrotecOneMark = 592; -const uint16_t kTrotecOneSpace = 1560; -const uint16_t kTrotecZeroMark = 592; -const uint16_t kTrotecZeroSpace = 592; -const uint16_t kTrotecGap = 6184; -const uint16_t kTrotecGapEnd = 1500; // made up value - -#if SEND_TROTEC - -void IRsend::sendTrotec(unsigned char data[], uint16_t nbytes, - uint16_t repeat) { - if (nbytes < kTrotecStateLength) return; - - for (uint16_t r = 0; r <= repeat; r++) { - sendGeneric(kTrotecHdrMark, kTrotecHdrSpace, kTrotecOneMark, - kTrotecOneSpace, kTrotecZeroMark, kTrotecZeroSpace, - kTrotecOneMark, kTrotecGap, data, nbytes, 36, false, - 0, // Repeats handled elsewhere - 50); - // More footer - enableIROut(36); - mark(kTrotecOneMark); - space(kTrotecGapEnd); - } -} -#endif // SEND_TROTEC - -IRTrotecESP::IRTrotecESP(uint16_t pin) : _irsend(pin) { stateReset(); } - -void IRTrotecESP::begin() { _irsend.begin(); } - -#if SEND_TROTEC -void IRTrotecESP::send() { - checksum(); - _irsend.sendTrotec(trotec); -} -#endif // SEND_TROTEC - -void IRTrotecESP::checksum() { - uint8_t sum = 0; - uint8_t i; - - for (i = 2; i < 8; i++) sum += trotec[i]; - - trotec[8] = sum & 0xFF; -} - -void IRTrotecESP::stateReset() { - for (uint8_t i = 2; i < kTrotecStateLength; i++) trotec[i] = 0x0; - - trotec[0] = kTrotecIntro1; - trotec[1] = kTrotecIntro2; - - setPower(false); - setTemp(kTrotecDefTemp); - setSpeed(kTrotecFanMed); - setMode(kTrotecAuto); -} - -uint8_t* IRTrotecESP::getRaw() { - checksum(); - return trotec; -} - -void IRTrotecESP::setPower(bool state) { - if (state) - trotec[2] |= (kTrotecOn << 3); - else - trotec[2] &= ~(kTrotecOn << 3); -} - -uint8_t IRTrotecESP::getPower() { return trotec[2] & (kTrotecOn << 3); } - -void IRTrotecESP::setSpeed(uint8_t speed) { - trotec[2] = (trotec[2] & 0xcf) | (speed << 4); -} - -uint8_t IRTrotecESP::getSpeed() { return trotec[2] & 0x30; } - -void IRTrotecESP::setMode(uint8_t mode) { - trotec[2] = (trotec[2] & 0xfc) | mode; -} - -uint8_t IRTrotecESP::getMode() { return trotec[2] & 0x03; } - -void IRTrotecESP::setTemp(uint8_t temp) { - if (temp < kTrotecMinTemp) - temp = kTrotecMinTemp; - else if (temp > kTrotecMaxTemp) - temp = kTrotecMaxTemp; - - trotec[3] = (trotec[3] & 0x80) | (temp - kTrotecMinTemp); -} - -uint8_t IRTrotecESP::getTemp() { return trotec[3] & 0x7f; } - -void IRTrotecESP::setSleep(bool sleep) { - if (sleep) - trotec[3] |= (kTrotecSleepOn << 7); - else - trotec[3] &= ~(kTrotecSleepOn << 7); -} - -bool IRTrotecESP::getSleep(void) { return trotec[3] & (kTrotecSleepOn << 7); } - -void IRTrotecESP::setTimer(uint8_t timer) { - if (timer > kTrotecMaxTimer) timer = kTrotecMaxTimer; - - if (timer) { - trotec[5] |= (kTrotecTimerOn << 6); - trotec[6] = timer; - } else { - trotec[5] &= ~(kTrotecTimerOn << 6); - trotec[6] = 0; - } -} - -uint8_t IRTrotecESP::getTimer() { return trotec[6]; } diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Whirlpool.cpp b/lib/IRremoteESP8266-2.5.2.03/src/ir_Whirlpool.cpp deleted file mode 100644 index 671513991..000000000 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Whirlpool.cpp +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright 2018 David Conran -// -// Code to emulate Whirlpool protocol compatible devices. -// Should be compatible with: -// * SPIS409L, SPIS412L, SPIW409L, SPIW412L, SPIW418L -// - -#include -#ifndef ARDUINO -#include -#endif -#include "IRrecv.h" -#include "IRremoteESP8266.h" -#include "IRsend.h" -#include "IRutils.h" - -// WW WW HH HH IIIII RRRRRR LL PPPPPP OOOOO OOOOO LL -// WW WW HH HH III RR RR LL PP PP OO OO OO OO LL -// WW W WW HHHHHHH III RRRRRR LL PPPPPP OO OO OO OO LL -// WW WWW WW HH HH III RR RR LL PP OO OO OO OO LL -// WW WW HH HH IIIII RR RR LLLLLLL PP OOOO0 OOOO0 LLLLLLL - -// Constants -// Ref: https://github.com/markszabo/IRremoteESP8266/issues/509 -const uint16_t kWhirlpoolAcHdrMark = 8950; -const uint16_t kWhirlpoolAcHdrSpace = 4484; -const uint16_t kWhirlpoolAcBitMark = 597; -const uint16_t kWhirlpoolAcOneSpace = 1649; -const uint16_t kWhirlpoolAcZeroSpace = 533; -const uint16_t kWhirlpoolAcGap = 7920; -const uint32_t kWhirlpoolAcMinGap = 100000; // Completely made up value. -const uint8_t kWhirlpoolAcSections = 3; - -#if SEND_WHIRLPOOL_AC -// Send a Whirlpool A/C message. -// -// Args: -// data: An array of bytes containing the IR command. -// nbytes: Nr. of bytes of data in the array. (>=kWhirlpoolAcStateLength) -// repeat: Nr. of times the message is to be repeated. (Default = 0). -// -// Status: ALPHA / Untested. -// -// Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/509 -void IRsend::sendWhirlpoolAC(unsigned char data[], uint16_t nbytes, - uint16_t repeat) { - if (nbytes < kWhirlpoolAcStateLength) - return; // Not enough bytes to send a proper message. - for (uint16_t r = 0; r <= repeat; r++) { - // Section 1 - sendGeneric(kWhirlpoolAcHdrMark, kWhirlpoolAcHdrSpace, kWhirlpoolAcBitMark, - kWhirlpoolAcOneSpace, kWhirlpoolAcBitMark, - kWhirlpoolAcZeroSpace, kWhirlpoolAcBitMark, kWhirlpoolAcGap, - data, 6, // 6 bytes == 48 bits - 38000, // Complete guess of the modulation frequency. - false, 0, 50); - // Section 2 - sendGeneric(0, 0, kWhirlpoolAcBitMark, kWhirlpoolAcOneSpace, - kWhirlpoolAcBitMark, kWhirlpoolAcZeroSpace, kWhirlpoolAcBitMark, - kWhirlpoolAcGap, data + 6, 8, // 8 bytes == 64 bits - 38000, // Complete guess of the modulation frequency. - false, 0, 50); - // Section 3 - sendGeneric(0, 0, kWhirlpoolAcBitMark, kWhirlpoolAcOneSpace, - kWhirlpoolAcBitMark, kWhirlpoolAcZeroSpace, kWhirlpoolAcBitMark, - kWhirlpoolAcMinGap, data + 14, 7, // 7 bytes == 56 bits - 38000, // Complete guess of the modulation frequency. - false, 0, 50); - } -} -#endif // SEND_WHIRLPOOL_AC - -#if DECODE_WHIRLPOOL_AC -// Decode the supplied Whirlpool A/C message. -// -// Args: -// results: Ptr to the data to decode and where to store the decode result. -// nbits: The number of data bits to expect. Typically kWhirlpoolAcBits -// strict: Flag indicating if we should perform strict matching. -// Returns: -// boolean: True if it can decode it, false if it can't. -// -// Status: ALPHA / Untested. -// -// -// Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/509 -bool IRrecv::decodeWhirlpoolAC(decode_results *results, uint16_t nbits, - bool strict) { - if (results->rawlen < 2 * nbits + 4 + kHeader + kFooter - 1) - return false; // Can't possibly be a valid Whirlpool A/C message. - if (strict) { - if (nbits != kWhirlpoolAcBits) return false; - } - - uint16_t offset = kStartOffset; - uint16_t dataBitsSoFar = 0; - uint16_t i = 0; - match_result_t data_result; - uint8_t sectionSize[kWhirlpoolAcSections] = {6, 8, 7}; - - // Header - if (!matchMark(results->rawbuf[offset++], kWhirlpoolAcHdrMark)) return false; - if (!matchSpace(results->rawbuf[offset++], kWhirlpoolAcHdrSpace)) - return false; - - // Data Section - // Keep reading bytes until we either run out of section or state to fill. - for (uint8_t section = 0, pos = 0; section < kWhirlpoolAcSections; - section++) { - pos += sectionSize[section]; - for (; offset <= results->rawlen - 16 && i < pos; - i++, dataBitsSoFar += 8, offset += data_result.used) { - data_result = - matchData(&(results->rawbuf[offset]), 8, kWhirlpoolAcBitMark, - kWhirlpoolAcOneSpace, kWhirlpoolAcBitMark, - kWhirlpoolAcZeroSpace, kTolerance, kMarkExcess, false); - if (data_result.success == false) break; // Fail - // Data is in LSB order. We need to reverse it. - results->state[i] = (uint8_t)data_result.data; - } - // Section Footer - if (!matchMark(results->rawbuf[offset++], kWhirlpoolAcBitMark)) - return false; - if (section < kWhirlpoolAcSections - 1) { // Inter-section gaps. - if (!matchSpace(results->rawbuf[offset++], kWhirlpoolAcGap)) return false; - } else { // Last section / End of message gap. - if (offset <= results->rawlen && - !matchAtLeast(results->rawbuf[offset++], kWhirlpoolAcGap)) - return false; - } - } - - // Compliance - if (strict) { - // Re-check we got the correct size/length due to the way we read the data. - if (dataBitsSoFar != kWhirlpoolAcBits) return false; - } - - // Success - results->decode_type = WHIRLPOOL_AC; - results->bits = dataBitsSoFar; - // No need to record the state as we stored it as we decoded it. - // As we use result->state, we don't record value, address, or command as it - // is a union data type. - return true; -} -#endif // WHIRLPOOL_AC diff --git a/lib/IRremoteESP8266-2.5.2.03/test/IRsend_test.cpp b/lib/IRremoteESP8266-2.5.2.03/test/IRsend_test.cpp deleted file mode 100644 index 353639918..000000000 --- a/lib/IRremoteESP8266-2.5.2.03/test/IRsend_test.cpp +++ /dev/null @@ -1,290 +0,0 @@ -// Copyright 2017 David Conran - -#include "IRsend_test.h" -#include "IRsend.h" -#include "gtest/gtest.h" - -// Tests sendData(). - -// Test sending zero bits. -TEST(TestSendData, SendZeroBits) { - IRsendTest irsend(4); - irsend.begin(); - irsend.sendData(1, 2, 3, 4, 0b1, 0, true); - EXPECT_EQ("", irsend.outputStr()); -} - -// Test sending zero and one. -TEST(TestSendData, SendSingleBit) { - IRsendTest irsend(4); - irsend.begin(); - irsend.sendData(1, 2, 3, 4, 0b1, 1, true); - EXPECT_EQ("m1s2", irsend.outputStr()); - irsend.sendData(1, 2, 3, 4, 0b0, 1, true); - EXPECT_EQ("m3s4", irsend.outputStr()); -} - -// Test sending bit order. -TEST(TestSendData, TestingBitSendOrder) { - IRsendTest irsend(4); - irsend.begin(); - irsend.sendData(1, 2, 3, 4, 0b10, 2, true); - EXPECT_EQ("m1s2m3s4", irsend.outputStr()); - irsend.sendData(1, 2, 3, 4, 0b10, 2, false); - EXPECT_EQ("m3s4m1s2", irsend.outputStr()); - irsend.sendData(1, 2, 3, 4, 0b0001, 4, false); - EXPECT_EQ("m1s2m3s4m3s4m3s4", irsend.outputStr()); -} - -// Test sending typical data. -TEST(TestSendData, SendTypicalData) { - IRsendTest irsend(4); - irsend.begin(); - irsend.sendData(1, 2, 3, 4, 0b1010110011110000, 16, true); - EXPECT_EQ("m1s2m3s4m1s2m3s4m1s2m1s2m3s4m3s4m1s2m1s2m1s2m1s2m3s4m3s4m3s4m3s4", - irsend.outputStr()); - irsend.sendData(1, 2, 3, 4, 0x1234567890ABCDEF, 64, true); - EXPECT_EQ( - "m3s4m3s4m3s4m1s2m3s4m3s4m1s2m3s4m3s4m3s4m1s2m1s2m3s4m1s2m3s4m3s4" - "m3s4m1s2m3s4m1s2m3s4m1s2m1s2m3s4m3s4m1s2m1s2m1s2m1s2m3s4m3s4m3s4" - "m1s2m3s4m3s4m1s2m3s4m3s4m3s4m3s4m1s2m3s4m1s2m3s4m1s2m3s4m1s2m1s2" - "m1s2m1s2m3s4m3s4m1s2m1s2m3s4m1s2m1s2m1s2m1s2m3s4m1s2m1s2m1s2m1s2", - irsend.outputStr()); -} - -// Test sending more than expected bits. -TEST(TestSendData, SendOverLargeData) { - IRsendTest irsend(4); - irsend.begin(); - irsend.sendData(1, 2, 3, 4, 0xFFFFFFFFFFFFFFFF, 70, true); - EXPECT_EQ( - "m3s4m3s4m3s4m3s4m3s4m3s4" - "m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2" - "m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2" - "m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2" - "m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2", - irsend.outputStr()); -} - -// Test inverting the output. -TEST(TestIRSend, InvertedOutput) { - IRsendTest irsend(4, true); - irsend.begin(); - irsend.sendData(1, 2, 3, 4, 0b1, 1, true); - EXPECT_EQ("s1m2", irsend.outputStr()); - irsend.sendData(1, 2, 3, 4, 0b0, 1, true); - EXPECT_EQ("s3m4", irsend.outputStr()); -} - -// Test typical use of sendRaw(). -TEST(TestSendRaw, GeneralUse) { - IRsendTest irsend(4); - IRrecv irrecv(0); - - irsend.begin(); - // NEC C3E0E0E8 as measured in #204 - uint16_t rawData[67] = { - 8950, 4500, 550, 1650, 600, 1650, 550, 550, 600, 500, 600, 550, - 550, 550, 600, 1650, 550, 1650, 600, 1650, 600, 1650, 550, 1700, - 550, 550, 600, 550, 550, 550, 600, 500, 600, 550, 550, 1650, - 600, 1650, 600, 1650, 550, 550, 600, 500, 600, 500, 600, 550, - 550, 550, 600, 1650, 550, 1650, 600, 1650, 600, 500, 650, 1600, - 600, 500, 600, 550, 550, 550, 600}; - - irsend.sendRaw(rawData, 67, 38); - EXPECT_EQ( - "m8950s4500" - "m550s1650m600s1650m550s550m600s500m600s550m550s550m600s1650m550s1650" - "m600s1650m600s1650m550s1700m550s550m600s550m550s550m600s500m600s550" - "m550s1650m600s1650m600s1650m550s550m600s500m600s500m600s550m550s550" - "m600s1650m550s1650m600s1650m600s500m650s1600m600s500m600s550m550s550" - "m600", - irsend.outputStr()); - - irsend.reset(); - irsend.sendRaw(rawData, 67, 38); - irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decodeNEC(&irsend.capture, kNECBits, false)); - EXPECT_EQ(NEC, irsend.capture.decode_type); - EXPECT_EQ(32, irsend.capture.bits); - EXPECT_EQ(0xC3E0E0E8, irsend.capture.value); - EXPECT_EQ( - "m8950s4500" - "m550s1650m600s1650m550s550m600s500m600s550m550s550m600s1650m550s1650" - "m600s1650m600s1650m550s1700m550s550m600s550m550s550m600s500m600s550" - "m550s1650m600s1650m600s1650m550s550m600s500m600s500m600s550m550s550" - "m600s1650m550s1650m600s1650m600s500m650s1600m600s500m600s550m550s550" - "m600", - irsend.outputStr()); -} - -// Incorrect handling of decodes from Raw. i.e. There is no gap recorded at -// the end of a command when using the interrupt code. sendRaw() best emulates -// this for unit testing purposes. sendGC() and sendXXX() will add the trailing -// gap. Users won't see this in normal use. -TEST(TestSendRaw, NoTrailingGap) { - IRsendTest irsend(4); - IRrecv irrecv(4); - irsend.begin(); - - irsend.reset(); - uint16_t rawData[67] = { - 9000, 4500, 650, 550, 650, 1650, 600, 550, 650, 550, 600, 1650, - 650, 550, 600, 1650, 650, 1650, 650, 1650, 600, 550, 650, 1650, - 650, 1650, 650, 550, 600, 1650, 650, 1650, 650, 550, 650, 550, - 650, 1650, 650, 550, 650, 550, 650, 550, 600, 550, 650, 550, - 650, 550, 650, 1650, 600, 550, 650, 1650, 650, 1650, 650, 1650, - 650, 1650, 650, 1650, 650, 1650, 600}; - irsend.sendRaw(rawData, 67, 38); - irsend.makeDecodeResult(); - EXPECT_TRUE(irrecv.decodeNEC(&irsend.capture)); - EXPECT_EQ(NEC, irsend.capture.decode_type); - EXPECT_EQ(kNECBits, irsend.capture.bits); -} - -TEST(TestLowLevelSend, MarkFrequencyModulationAt38kHz) { - IRsendLowLevelTest irsend(0); - - irsend.begin(); - - irsend.reset(); - irsend.enableIROut(38000, 50); - EXPECT_EQ(5, irsend.mark(100)); - EXPECT_EQ( - "[On]10usecs[Off]11usecs[On]10usecs[Off]11usecs[On]10usecs[Off]11usecs" - "[On]10usecs[Off]11usecs[On]10usecs[Off]6usecs", - irsend.low_level_sequence); - - irsend.reset(); - irsend.enableIROut(38000, 33); - EXPECT_EQ(5, irsend.mark(100)); - EXPECT_EQ( - "[On]6usecs[Off]15usecs[On]6usecs[Off]15usecs[On]6usecs[Off]15usecs" - "[On]6usecs[Off]15usecs[On]6usecs[Off]10usecs", - irsend.low_level_sequence); - - irsend.reset(); - irsend.enableIROut(38000, 100); - EXPECT_EQ(1, irsend.mark(1000)); - EXPECT_EQ("[On]1000usecs[Off]", irsend.low_level_sequence); -} - -TEST(TestLowLevelSend, MarkFrequencyModulationAt36_7kHz) { - IRsendLowLevelTest irsend(0); - - irsend.begin(); - - irsend.reset(); - irsend.enableIROut(36700, 50); - EXPECT_EQ(5, irsend.mark(100)); - EXPECT_EQ( - "[On]11usecs[Off]11usecs[On]11usecs[Off]11usecs[On]11usecs[Off]11usecs" - "[On]11usecs[Off]11usecs[On]11usecs[Off]1usecs", - irsend.low_level_sequence); - - irsend.reset(); - irsend.enableIROut(36700, 33); - EXPECT_EQ(5, irsend.mark(100)); - EXPECT_EQ( - "[On]7usecs[Off]15usecs[On]7usecs[Off]15usecs[On]7usecs[Off]15usecs" - "[On]7usecs[Off]15usecs[On]7usecs[Off]5usecs", - irsend.low_level_sequence); - - irsend.reset(); - irsend.enableIROut(36700, 100); - EXPECT_EQ(1, irsend.mark(1000)); - EXPECT_EQ("[On]1000usecs[Off]", irsend.low_level_sequence); -} - -TEST(TestLowLevelSend, MarkFrequencyModulationAt40kHz) { - IRsendLowLevelTest irsend(0); - - irsend.begin(); - - irsend.reset(); - irsend.enableIROut(40000, 50); - EXPECT_EQ(5, irsend.mark(100)); - EXPECT_EQ( - "[On]10usecs[Off]10usecs[On]10usecs[Off]10usecs[On]10usecs[Off]10usecs" - "[On]10usecs[Off]10usecs[On]10usecs[Off]10usecs", - irsend.low_level_sequence); - - irsend.reset(); - irsend.enableIROut(40000, 33); - EXPECT_EQ(5, irsend.mark(100)); - EXPECT_EQ( - "[On]6usecs[Off]14usecs[On]6usecs[Off]14usecs[On]6usecs[Off]14usecs" - "[On]6usecs[Off]14usecs[On]6usecs[Off]14usecs", - irsend.low_level_sequence); - - irsend.reset(); - irsend.enableIROut(40000, 100); - EXPECT_EQ(1, irsend.mark(1000)); - EXPECT_EQ("[On]1000usecs[Off]", irsend.low_level_sequence); -} - -TEST(TestLowLevelSend, MarkNoModulation) { - IRsendLowLevelTest irsend(0, false, false); - - irsend.begin(); - - irsend.reset(); - irsend.enableIROut(38000, 50); - EXPECT_EQ(1, irsend.mark(1000)); - EXPECT_EQ("[On]1000usecs[Off]", irsend.low_level_sequence); - - irsend.reset(); - irsend.enableIROut(36700, 25); - EXPECT_EQ(1, irsend.mark(1000)); - EXPECT_EQ("[On]1000usecs[Off]", irsend.low_level_sequence); - - irsend.reset(); - irsend.enableIROut(40000, 75); - EXPECT_EQ(1, irsend.mark(1000)); - EXPECT_EQ("[On]1000usecs[Off]", irsend.low_level_sequence); -} - -TEST(TestLowLevelSend, SpaceFrequencyModulation) { - IRsendLowLevelTest irsend(0); - - irsend.reset(); - irsend.enableIROut(38000); - irsend.space(1000); - EXPECT_EQ("[Off]1000usecs", irsend.low_level_sequence); - - irsend.reset(); - irsend.enableIROut(40000, 75); - irsend.space(1000); - EXPECT_EQ("[Off]1000usecs", irsend.low_level_sequence); - - irsend.reset(); - irsend.enableIROut(38000, 100); - irsend.space(1000); - EXPECT_EQ("[Off]1000usecs", irsend.low_level_sequence); - - irsend.reset(); - irsend.enableIROut(38000, 33); - irsend.space(1000); - EXPECT_EQ("[Off]1000usecs", irsend.low_level_sequence); -} - -TEST(TestLowLevelSend, SpaceNoModulation) { - IRsendLowLevelTest irsend(0, false, false); - - irsend.begin(); - - irsend.reset(); - irsend.enableIROut(38000, 50); - irsend.space(1000); - EXPECT_EQ("[Off]1000usecs", irsend.low_level_sequence); - - irsend.reset(); - irsend.enableIROut(36700, 25); - irsend.space(1000); - EXPECT_EQ("[Off]1000usecs", irsend.low_level_sequence); - - irsend.reset(); - irsend.enableIROut(40000, 75); - irsend.space(1000); - EXPECT_EQ("[Off]1000usecs", irsend.low_level_sequence); -} diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_Daikin_test.cpp b/lib/IRremoteESP8266-2.5.2.03/test/ir_Daikin_test.cpp deleted file mode 100644 index c8192fc82..000000000 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_Daikin_test.cpp +++ /dev/null @@ -1,838 +0,0 @@ -// Copyright 2017 David Conran -#include "ir_Daikin.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" -#include "gtest/gtest.h" - -// Tests for sendDaikin(). - -// Test sending typical data only. -TEST(TestSendDaikin, SendDataOnly) { - IRsendTest irsend(4); - irsend.begin(); - - uint8_t daikin_code[kDaikinStateLength] = { - 0x11, 0xDA, 0x27, 0xF0, 0x00, 0x00, 0x00, 0x20, 0x11, - 0xDA, 0x27, 0x00, 0x00, 0x41, 0x1E, 0x00, 0xB0, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xE3}; - - irsend.reset(); - irsend.sendDaikin(daikin_code); - EXPECT_EQ( - "m428s428m428s428m428s428m428s428m428s428" - "m428s29428m3650s1623" - "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" - "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" - "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s1280m428s428m428s1280m428s428m428s428m428s428m428s1280m428s1280" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s1280m428s1280m428s1280m428s428m428s1280m428s428m428s1280m428s1280" - "m428s29428m3650s1623" - "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" - "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" - "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s1280m428s1280m428s1280m428s1280" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s1280m428s428m428s428" - "m428s29428m3650s1623" - "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" - "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" - "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s1280m428s428m428s428m428s428m428s428m428s428m428s1280m428s428" - "m428s428m428s1280m428s1280m428s1280m428s1280m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s1280m428s1280m428s428m428s1280" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s1280m428s1280" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s1280m428s1280m428s428m428s428m428s428m428s1280m428s1280m428s1280" - "m428s29428", - irsend.outputStr()); -} - -// Test sending with repeats. -TEST(TestSendDaikin, SendWithRepeats) { - IRsendTest irsend(4); - irsend.begin(); - - irsend.reset(); - uint8_t daikin_code[kDaikinStateLength] = { - 0x11, 0xDA, 0x27, 0xF0, 0x00, 0x00, 0x00, 0x20, 0x11, - 0xDA, 0x27, 0x00, 0x00, 0x41, 0x1E, 0x00, 0xB0, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xE3}; - irsend.reset(); - - irsend.sendDaikin(daikin_code, kDaikinStateLength, 1); - EXPECT_EQ( - "m428s428m428s428m428s428m428s428m428s428" - "m428s29428m3650s1623" - "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" - "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" - "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s1280m428s428m428s1280m428s428m428s428m428s428m428s1280m428s1280" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s1280m428s1280m428s1280m428s428m428s1280m428s428m428s1280m428s1280" - "m428s29428m3650s1623" - "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" - "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" - "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s1280m428s1280m428s1280m428s1280" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s1280m428s428m428s428" - "m428s29428m3650s1623" - "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" - "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" - "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s1280m428s428m428s428m428s428m428s428m428s428m428s1280m428s428" - "m428s428m428s1280m428s1280m428s1280m428s1280m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s1280m428s1280m428s428m428s1280" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s1280m428s1280" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s1280m428s1280m428s428m428s428m428s428m428s1280m428s1280m428s1280" - "m428s29428" - "m428s428m428s428m428s428m428s428m428s428" - "m428s29428m3650s1623" - "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" - "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" - "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s1280m428s428m428s1280m428s428m428s428m428s428m428s1280m428s1280" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s1280m428s1280m428s1280m428s428m428s1280m428s428m428s1280m428s1280" - "m428s29428m3650s1623" - "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" - "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" - "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s1280m428s1280m428s1280m428s1280" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s1280m428s428m428s428" - "m428s29428m3650s1623" - "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" - "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" - "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s1280m428s428m428s428m428s428m428s428m428s428m428s1280m428s428" - "m428s428m428s1280m428s1280m428s1280m428s1280m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s1280m428s1280m428s428m428s1280" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s1280m428s1280" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s1280m428s1280m428s428m428s428m428s428m428s1280m428s1280m428s1280" - "m428s29428", - irsend.outputStr()); -} - -// Test sending atypical sizes. -TEST(TestSendDaikin, SendUnexpectedSizes) { - IRsendTest irsend(4); - irsend.begin(); - - uint8_t daikin_short_code[kDaikinStateLength - 1] = { - 0x11, 0xDA, 0x27, 0xF0, 0x00, 0x00, 0x00, 0x20, 0x11, - 0xDA, 0x27, 0x00, 0x00, 0x41, 0x1E, 0x00, 0xB0, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00}; - - irsend.reset(); - irsend.sendDaikin(daikin_short_code, kDaikinStateLength - 1); - ASSERT_EQ("", irsend.outputStr()); - - uint8_t daikin_long_code[kDaikinStateLength + 1] = { - 0x11, 0xDA, 0x27, 0xF0, 0x00, 0x00, 0x00, 0x20, 0x11, 0xDA, - 0x27, 0x00, 0x00, 0x41, 0x1E, 0x00, 0xB0, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xE3, 0x11}; - irsend.reset(); - irsend.sendDaikin(daikin_long_code, kDaikinStateLength + 1); - ASSERT_EQ( - "m428s428m428s428m428s428m428s428m428s428" - "m428s29428m3650s1623" - "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" - "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" - "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s1280m428s428m428s1280m428s428m428s428m428s428m428s1280m428s1280" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s1280m428s1280m428s1280m428s428m428s1280m428s428m428s1280m428s1280" - "m428s29428m3650s1623" - "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" - "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" - "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s1280m428s1280m428s1280m428s1280" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s1280m428s428m428s428" - "m428s29428m3650s1623" - "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" - "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" - "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s1280m428s428m428s428m428s428m428s428m428s428m428s1280m428s428" - "m428s428m428s1280m428s1280m428s1280m428s1280m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s1280m428s1280m428s428m428s1280" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s1280m428s1280" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s1280m428s1280m428s428m428s428m428s428m428s1280m428s1280m428s1280" - "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" - "m428s29428", - irsend.outputStr()); -} - -// Tests for IRDaikinESP class. - -TEST(TestDaikinClass, Power) { - IRDaikinESP irdaikin(0); - irdaikin.begin(); - - irdaikin.on(); - EXPECT_TRUE(irdaikin.getPower()); - - irdaikin.off(); - EXPECT_FALSE(irdaikin.getPower()); - - irdaikin.setPower(true); - EXPECT_TRUE(irdaikin.getPower()); - - irdaikin.setPower(false); - EXPECT_FALSE(irdaikin.getPower()); -} - -TEST(TestDaikinClass, Temperature) { - IRDaikinESP irdaikin(0); - irdaikin.begin(); - - irdaikin.setTemp(0); - EXPECT_EQ(kDaikinMinTemp, irdaikin.getTemp()); - - irdaikin.setTemp(255); - EXPECT_EQ(kDaikinMaxTemp, irdaikin.getTemp()); - - irdaikin.setTemp(kDaikinMinTemp); - EXPECT_EQ(kDaikinMinTemp, irdaikin.getTemp()); - - irdaikin.setTemp(kDaikinMaxTemp); - EXPECT_EQ(kDaikinMaxTemp, irdaikin.getTemp()); - - irdaikin.setTemp(kDaikinMinTemp - 1); - EXPECT_EQ(kDaikinMinTemp, irdaikin.getTemp()); - - irdaikin.setTemp(kDaikinMaxTemp + 1); - EXPECT_EQ(kDaikinMaxTemp, irdaikin.getTemp()); - - irdaikin.setTemp(kDaikinMinTemp + 1); - EXPECT_EQ(kDaikinMinTemp + 1, irdaikin.getTemp()); - - irdaikin.setTemp(21); - EXPECT_EQ(21, irdaikin.getTemp()); - - irdaikin.setTemp(25); - EXPECT_EQ(25, irdaikin.getTemp()); - - irdaikin.setTemp(29); - EXPECT_EQ(29, irdaikin.getTemp()); -} - -TEST(TestDaikinClass, OperatingMode) { - IRDaikinESP irdaikin(0); - irdaikin.begin(); - - irdaikin.setMode(kDaikinAuto); - EXPECT_EQ(kDaikinAuto, irdaikin.getMode()); - - irdaikin.setMode(kDaikinCool); - EXPECT_EQ(kDaikinCool, irdaikin.getMode()); - - irdaikin.setMode(kDaikinHeat); - EXPECT_EQ(kDaikinHeat, irdaikin.getMode()); - - irdaikin.setMode(kDaikinDry); - EXPECT_EQ(kDaikinDry, irdaikin.getMode()); - - irdaikin.setMode(kDaikinFan); - EXPECT_EQ(kDaikinFan, irdaikin.getMode()); - - irdaikin.setMode(kDaikinFan + 1); - EXPECT_EQ(kDaikinAuto, irdaikin.getMode()); - - irdaikin.setMode(kDaikinAuto + 1); - EXPECT_EQ(kDaikinAuto, irdaikin.getMode()); - - irdaikin.setMode(255); - EXPECT_EQ(kDaikinAuto, irdaikin.getMode()); -} - -TEST(TestDaikinClass, VaneSwing) { - IRDaikinESP irdaikin(0); - irdaikin.begin(); - - irdaikin.setSwingHorizontal(true); - irdaikin.setSwingVertical(false); - - irdaikin.setSwingHorizontal(true); - EXPECT_TRUE(irdaikin.getSwingHorizontal()); - EXPECT_FALSE(irdaikin.getSwingVertical()); - - irdaikin.setSwingVertical(true); - EXPECT_TRUE(irdaikin.getSwingHorizontal()); - EXPECT_TRUE(irdaikin.getSwingVertical()); - - irdaikin.setSwingHorizontal(false); - EXPECT_FALSE(irdaikin.getSwingHorizontal()); - EXPECT_TRUE(irdaikin.getSwingVertical()); - - irdaikin.setSwingVertical(false); - EXPECT_FALSE(irdaikin.getSwingHorizontal()); - EXPECT_FALSE(irdaikin.getSwingVertical()); -} - -TEST(TestDaikinClass, QuietMode) { - IRDaikinESP irdaikin(0); - irdaikin.begin(); - - irdaikin.setQuiet(true); - EXPECT_TRUE(irdaikin.getQuiet()); - - irdaikin.setQuiet(false); - EXPECT_FALSE(irdaikin.getQuiet()); - - irdaikin.setQuiet(true); - EXPECT_TRUE(irdaikin.getQuiet()); - - // Setting Econo mode should NOT change out of quiet mode. - irdaikin.setEcono(true); - EXPECT_TRUE(irdaikin.getQuiet()); - irdaikin.setEcono(false); - EXPECT_TRUE(irdaikin.getQuiet()); - - // But setting Powerful mode should exit out of quiet mode. - irdaikin.setPowerful(true); - EXPECT_FALSE(irdaikin.getQuiet()); -} - -TEST(TestDaikinClass, PowerfulMode) { - IRDaikinESP irdaikin(0); - irdaikin.begin(); - - irdaikin.setPowerful(true); - EXPECT_TRUE(irdaikin.getPowerful()); - - irdaikin.setPowerful(false); - EXPECT_FALSE(irdaikin.getPowerful()); - - irdaikin.setPowerful(true); - EXPECT_TRUE(irdaikin.getPowerful()); - - irdaikin.setQuiet(true); - EXPECT_FALSE(irdaikin.getPowerful()); - - irdaikin.setPowerful(true); - irdaikin.setEcono(true); - EXPECT_FALSE(irdaikin.getPowerful()); -} - -TEST(TestDaikinClass, EconoMode) { - IRDaikinESP irdaikin(0); - irdaikin.begin(); - - irdaikin.setEcono(true); - EXPECT_TRUE(irdaikin.getEcono()); - - irdaikin.setEcono(false); - EXPECT_FALSE(irdaikin.getEcono()); - - irdaikin.setEcono(true); - EXPECT_TRUE(irdaikin.getEcono()); - - // Setting Quiet mode should NOT change out of Econo mode. - irdaikin.setQuiet(true); - EXPECT_TRUE(irdaikin.getEcono()); - irdaikin.setQuiet(false); - EXPECT_TRUE(irdaikin.getEcono()); - - // But setting Powerful mode should exit out of Econo mode. - irdaikin.setPowerful(true); - EXPECT_FALSE(irdaikin.getEcono()); -} - -TEST(TestDaikinClass, FanSpeed) { - IRDaikinESP irdaikin(0); - irdaikin.begin(); - - // Unexpected value should default to Auto. - irdaikin.setFan(0); - EXPECT_EQ(kDaikinFanAuto, irdaikin.getFan()); - - // Unexpected value should default to Auto. - irdaikin.setFan(255); - EXPECT_EQ(kDaikinFanAuto, irdaikin.getFan()); - - irdaikin.setFan(kDaikinFanMax); - EXPECT_EQ(kDaikinFanMax, irdaikin.getFan()); - - // Beyond Max should default to Auto. - irdaikin.setFan(kDaikinFanMax + 1); - EXPECT_EQ(kDaikinFanAuto, irdaikin.getFan()); - - irdaikin.setFan(kDaikinFanMax - 1); - EXPECT_EQ(kDaikinFanMax - 1, irdaikin.getFan()); - - irdaikin.setFan(kDaikinFanMin); - EXPECT_EQ(kDaikinFanMin, irdaikin.getFan()); - - irdaikin.setFan(kDaikinFanMin + 1); - EXPECT_EQ(kDaikinFanMin + 1, irdaikin.getFan()); - - // Beyond Min should default to Auto. - irdaikin.setFan(kDaikinFanMin - 1); - EXPECT_EQ(kDaikinFanAuto, irdaikin.getFan()); - - irdaikin.setFan(3); - EXPECT_EQ(3, irdaikin.getFan()); - - irdaikin.setFan(kDaikinFanAuto); - EXPECT_EQ(kDaikinFanAuto, irdaikin.getFan()); - - irdaikin.setFan(kDaikinFanQuiet); - EXPECT_EQ(kDaikinFanQuiet, irdaikin.getFan()); -} - -TEST(TestDaikinClass, CurrentTime) { - IRDaikinESP irdaikin(0); - irdaikin.begin(); - - irdaikin.setCurrentTime(0); // 00:00 - EXPECT_EQ(0, irdaikin.getCurrentTime()); - - irdaikin.setCurrentTime(754); // 12:34 - EXPECT_EQ(754, irdaikin.getCurrentTime()); - - irdaikin.setCurrentTime(1439); // 23:59 - EXPECT_EQ(1439, irdaikin.getCurrentTime()); -} - -TEST(TestDaikinClass, OnOffTimers) { - IRDaikinESP irdaikin(0); - irdaikin.begin(); - - // Both timers turned off. - irdaikin.disableOnTimer(); - irdaikin.disableOffTimer(); - EXPECT_FALSE(irdaikin.getOnTimerEnabled()); - EXPECT_EQ(0x600, irdaikin.getOnTime()); - EXPECT_FALSE(irdaikin.getOffTimerEnabled()); - EXPECT_EQ(0x600, irdaikin.getOffTime()); - - // Turn on just the On Timer. - irdaikin.enableOnTimer(123); - EXPECT_TRUE(irdaikin.getOnTimerEnabled()); - EXPECT_EQ(123, irdaikin.getOnTime()); - EXPECT_FALSE(irdaikin.getOffTimerEnabled()); - EXPECT_EQ(0x600, irdaikin.getOffTime()); - - // Now turn on the Off Timer. - irdaikin.enableOffTimer(754); - EXPECT_TRUE(irdaikin.getOffTimerEnabled()); - EXPECT_EQ(754, irdaikin.getOffTime()); - EXPECT_TRUE(irdaikin.getOnTimerEnabled()); - EXPECT_EQ(123, irdaikin.getOnTime()); - - // Turn off the just the On Timer. - irdaikin.disableOnTimer(); - EXPECT_FALSE(irdaikin.getOnTimerEnabled()); - EXPECT_EQ(0x600, irdaikin.getOnTime()); - EXPECT_TRUE(irdaikin.getOffTimerEnabled()); - EXPECT_EQ(754, irdaikin.getOffTime()); - - // Now turn off the Off Timer. - irdaikin.disableOffTimer(); - EXPECT_FALSE(irdaikin.getOffTimerEnabled()); - EXPECT_EQ(0x600, irdaikin.getOffTime()); - EXPECT_FALSE(irdaikin.getOnTimerEnabled()); - EXPECT_EQ(0x600, irdaikin.getOnTime()); - - // Use some canary values around the timers to ensure no accidental - // bit flips happen. i.e. Neighbouring bytes in the state. - // (Found some during testing on systems with different endian-ness) - // Tests here to make sure it never happens again. - irdaikin.setSwingHorizontal(true); - irdaikin.setPowerful(true); - irdaikin.disableOffTimer(); - irdaikin.disableOnTimer(); - ASSERT_TRUE(irdaikin.getSwingHorizontal()); - ASSERT_TRUE(irdaikin.getPowerful()); - irdaikin.enableOnTimer(123); - irdaikin.enableOffTimer(456); - ASSERT_TRUE(irdaikin.getSwingHorizontal()); - ASSERT_TRUE(irdaikin.getPowerful()); - irdaikin.disableOffTimer(); - irdaikin.disableOnTimer(); - ASSERT_TRUE(irdaikin.getSwingHorizontal()); - ASSERT_TRUE(irdaikin.getPowerful()); - - irdaikin.setSwingHorizontal(false); - irdaikin.setPowerful(false); - irdaikin.disableOffTimer(); - irdaikin.disableOnTimer(); - ASSERT_FALSE(irdaikin.getSwingHorizontal()); - ASSERT_FALSE(irdaikin.getPowerful()); - irdaikin.enableOnTimer(123); - irdaikin.enableOffTimer(456); - ASSERT_FALSE(irdaikin.getSwingHorizontal()); - ASSERT_FALSE(irdaikin.getPowerful()); - irdaikin.disableOffTimer(); - irdaikin.disableOnTimer(); - ASSERT_FALSE(irdaikin.getSwingHorizontal()); - ASSERT_FALSE(irdaikin.getPowerful()); -} - -// Test Eye mode. -TEST(TestDaikinClass, EyeSetting) { - IRDaikinESP irdaikin(0); - irdaikin.begin(); - - // The Eye setting is stored in the same byte as Econo mode. - // Econo mode tests are there to make sure it isn't harmed and vice-versa. - irdaikin.setEcono(false); - irdaikin.setEye(false); - ASSERT_FALSE(irdaikin.getEye()); - EXPECT_FALSE(irdaikin.getEcono()); - - irdaikin.setEye(true); - ASSERT_TRUE(irdaikin.getEye()); - EXPECT_FALSE(irdaikin.getEcono()); - - irdaikin.setEcono(false); - ASSERT_TRUE(irdaikin.getEye()); - EXPECT_FALSE(irdaikin.getEcono()); - - irdaikin.setEcono(true); - ASSERT_TRUE(irdaikin.getEye()); - EXPECT_TRUE(irdaikin.getEcono()); - - irdaikin.setEye(false); - ASSERT_FALSE(irdaikin.getEye()); - EXPECT_TRUE(irdaikin.getEcono()); -} - -// Test Mold mode. -TEST(TestDaikinClass, MoldSetting) { - IRDaikinESP irdaikin(0); - irdaikin.begin(); - - irdaikin.setMold(false); - ASSERT_FALSE(irdaikin.getMold()); - - irdaikin.setMold(true); - ASSERT_TRUE(irdaikin.getMold()); - - irdaikin.setMold(false); - ASSERT_FALSE(irdaikin.getMold()); -} - -// Test Sensor mode. -TEST(TestDaikinClass, SensorSetting) { - IRDaikinESP irdaikin(0); - irdaikin.begin(); - - irdaikin.setSensor(false); - ASSERT_FALSE(irdaikin.getSensor()); - - irdaikin.setSensor(true); - ASSERT_TRUE(irdaikin.getSensor()); - - irdaikin.setSensor(false); - ASSERT_FALSE(irdaikin.getSensor()); -} - -TEST(TestDaikinClass, RenderTime) { - EXPECT_EQ("0:00", IRDaikinESP::renderTime(0)); - EXPECT_EQ("0:10", IRDaikinESP::renderTime(10)); - EXPECT_EQ("1:00", IRDaikinESP::renderTime(1 * 60 + 0)); - EXPECT_EQ("23:59", IRDaikinESP::renderTime(23 * 60 + 59)); -} - -TEST(TestDaikinClass, SetAndGetRaw) { - IRDaikinESP irdaikin(0); - uint8_t initialState[kDaikinStateLength] = { - 0x11, 0xDA, 0x27, 0x00, 0x42, 0x00, 0x00, 0x54, 0x11, - 0xDA, 0x27, 0x00, 0x00, 0x49, 0x1E, 0x00, 0xB0, 0x00, - 0x00, 0x06, 0x60, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x4F}; - uint8_t expectedState[kDaikinStateLength] = { - 0x11, 0xDA, 0x27, 0x00, 0x42, 0x00, 0x00, 0x54, 0x11, - 0xDA, 0x27, 0x00, 0x00, 0x48, 0x2A, 0x00, 0xB0, 0x00, - 0x00, 0x06, 0x60, 0x00, 0x00, 0xC0, 0x00, 0x02, 0x5A}; - - EXPECT_STATE_EQ(initialState, irdaikin.getRaw(), kDaikinBits); - // toggle the power state. - irdaikin.setPower(!irdaikin.getPower()); - irdaikin.setTemp(21); - irdaikin.setMold(true); - EXPECT_STATE_EQ(expectedState, irdaikin.getRaw(), kDaikinBits); - irdaikin.setRaw(initialState); - EXPECT_STATE_EQ(initialState, irdaikin.getRaw(), kDaikinBits); -} - -TEST(TestDaikinClass, ChecksumValidation) { - uint8_t daikin_code[kDaikinStateLength] = { - 0x11, 0xDA, 0x27, 0xF0, 0x00, 0x00, 0x00, 0x02, 0x11, - 0xDA, 0x27, 0x00, 0x00, 0x41, 0x1E, 0x00, 0xB0, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xE1}; - - EXPECT_TRUE(IRDaikinESP::validChecksum(daikin_code)); - // Change the array so the checksum is invalid. - daikin_code[0] ^= 0xFF; - EXPECT_FALSE(IRDaikinESP::validChecksum(daikin_code)); - // Restore the previous change, and change another byte. - daikin_code[0] ^= 0xFF; - daikin_code[4] ^= 0xFF; - EXPECT_FALSE(IRDaikinESP::validChecksum(daikin_code)); - daikin_code[4] ^= 0xFF; - // Change something in the 2nd block. - daikin_code[10] ^= 0xFF; - EXPECT_FALSE(IRDaikinESP::validChecksum(daikin_code)); - daikin_code[10] ^= 0xFF; - EXPECT_TRUE(IRDaikinESP::validChecksum(daikin_code)); -} - -// Test human readable output. -TEST(TestDaikinClass, HumanReadable) { - IRDaikinESP irdaikin(0); - - EXPECT_EQ( - "Power: On, Mode: 4 (HEAT), Temp: 15C, Fan: 11 (QUIET), " - "Powerful: Off, Quiet: Off, Sensor: Off, Eye: Off, Mold: Off, " - "Swing (Horizontal): Off, Swing (Vertical): Off, " - "Current Time: 0:00, On Time: Off, Off Time: Off", - irdaikin.toString()); - irdaikin.setMode(kDaikinAuto); - irdaikin.setTemp(25); - irdaikin.setFan(kDaikinFanAuto); - irdaikin.setQuiet(true); - irdaikin.setSensor(true); - irdaikin.setEye(true); - irdaikin.setMold(true); - irdaikin.setSwingVertical(true); - irdaikin.setSwingHorizontal(true); - irdaikin.setCurrentTime(9 * 60 + 15); - irdaikin.enableOnTimer(8 * 60 + 0); - irdaikin.enableOffTimer(17 * 60 + 30); - irdaikin.off(); - EXPECT_EQ( - "Power: Off, Mode: 0 (AUTO), Temp: 25C, Fan: 10 (AUTO), " - "Powerful: Off, Quiet: On, Sensor: On, Eye: On, Mold: On, " - "Swing (Horizontal): On, Swing (Vertical): On, " - "Current Time: 9:15, On Time: 8:00, Off Time: 17:30", - irdaikin.toString()); -} - -// Test general message construction after tweaking some settings. -TEST(TestDaikinClass, MessageConstuction) { - IRDaikinESP irdaikin(0); - IRsendTest irsend(4); - irdaikin.begin(); - irsend.begin(); - - irdaikin.setFan(kDaikinFanMin); - irdaikin.setMode(kDaikinCool); - irdaikin.setTemp(27); - irdaikin.setSwingVertical(false); - irdaikin.setSwingHorizontal(true); - irdaikin.setQuiet(false); - irdaikin.setPower(true); - - // Check everything for kicks. - EXPECT_EQ(kDaikinFanMin, irdaikin.getFan()); - EXPECT_EQ(kDaikinCool, irdaikin.getMode()); - EXPECT_EQ(27, irdaikin.getTemp()); - EXPECT_FALSE(irdaikin.getSwingVertical()); - EXPECT_TRUE(irdaikin.getSwingHorizontal()); - EXPECT_FALSE(irdaikin.getQuiet()); - EXPECT_TRUE(irdaikin.getPower()); - - irsend.reset(); - irsend.sendDaikin(irdaikin.getRaw()); - EXPECT_EQ( - "m428s428m428s428m428s428m428s428m428s428" - "m428s29428m3650s1623" - "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" - "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" - "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s1280m428s428m428s1280m428s428m428s428m428s428m428s1280m428s1280" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s1280m428s1280m428s1280m428s428m428s1280m428s428m428s1280m428s1280" - "m428s29428m3650s1623" - "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" - "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" - "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s1280m428s428m428s428m428s428m428s428m428s1280m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s1280m428s428m428s1280m428s428m428s1280m428s428" - "m428s29428m3650s1623" - "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" - "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" - "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s1280m428s428m428s428m428s1280m428s1280m428s1280m428s428m428s428" - "m428s428m428s1280m428s1280m428s428m428s1280m428s1280m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s1280m428s1280m428s428m428s428" - "m428s1280m428s1280m428s1280m428s1280m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s1280m428s1280m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s1280m428s1280m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s1280m428s1280" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s1280m428s1280m428s428m428s428m428s1280m428s1280m428s1280" - "m428s29428", - irsend.outputStr()); -} - -// Tests for decodeDaikin(). - -// Test decoding a message captured from a real IR remote. -TEST(TestDecodeDaikin, RealExample) { - IRDaikinESP irdaikin(0); - IRsendTest irsend(4); - IRrecv irrecv(4); - irsend.begin(); - - uint8_t expectedState[kDaikinStateLength] = { - 0x11, 0xDA, 0x27, 0x00, 0x42, 0x3A, 0x05, 0x93, 0x11, - 0xDA, 0x27, 0x00, 0x00, 0x3F, 0x3A, 0x00, 0xA0, 0x00, - 0x0A, 0x25, 0x17, 0x01, 0x00, 0xC0, 0x00, 0x00, 0x32}; - uint16_t rawData[kDaikinRawBits] = { - 416, 446, 416, 446, 416, 446, 418, 446, 416, 446, 416, 25434, - 3436, 1768, 390, 1336, 390, 446, 416, 446, 416, 446, 416, 1336, - 390, 446, 416, 446, 416, 446, 416, 446, 416, 1336, 390, 448, - 416, 1336, 390, 1336, 390, 448, 416, 1336, 390, 1336, 390, 1338, - 388, 1338, 390, 1336, 390, 446, 416, 446, 416, 1336, 390, 446, - 416, 446, 416, 446, 416, 446, 416, 446, 416, 446, 416, 448, - 416, 446, 416, 446, 416, 446, 416, 1336, 390, 446, 416, 1336, - 390, 448, 416, 446, 416, 446, 416, 1336, 390, 1336, 390, 446, - 416, 446, 416, 446, 416, 446, 416, 446, 416, 446, 416, 446, - 416, 446, 416, 446, 416, 448, 416, 446, 416, 446, 416, 446, - 416, 448, 414, 448, 416, 448, 416, 1336, 390, 1336, 390, 1336, - 390, 446, 414, 1336, 390, 448, 414, 1336, 390, 1336, 390, 34878, - 3436, 1768, 390, 1336, 390, 446, 416, 448, 416, 446, 416, 1336, - 390, 446, 416, 448, 416, 446, 416, 446, 416, 1336, 390, 446, - 416, 1336, 390, 1336, 390, 446, 416, 1336, 390, 1336, 390, 1336, - 390, 1336, 390, 1336, 392, 446, 414, 448, 416, 1336, 390, 446, - 416, 446, 416, 446, 416, 446, 414, 448, 416, 446, 416, 448, - 414, 448, 416, 446, 416, 446, 416, 446, 414, 1336, 390, 448, - 416, 446, 416, 446, 416, 448, 416, 1336, 390, 446, 416, 446, - 416, 1336, 390, 446, 416, 1336, 390, 1336, 390, 1336, 390, 446, - 416, 446, 414, 1338, 390, 446, 416, 1336, 390, 446, 416, 446, - 416, 446, 416, 446, 416, 446, 416, 1336, 390, 1336, 390, 446, - 416, 446, 416, 1336, 390, 446, 416, 446, 416, 1336, 390, 34876, - 3436, 1768, 388, 1336, 390, 446, 416, 446, 416, 448, 416, 1336, - 390, 446, 416, 446, 416, 446, 416, 448, 416, 1336, 390, 448, - 414, 1336, 390, 1336, 390, 446, 416, 1336, 388, 1338, 388, 1336, - 390, 1336, 390, 1336, 390, 446, 416, 446, 416, 1336, 390, 446, - 420, 442, 416, 446, 416, 446, 416, 446, 416, 446, 416, 446, - 416, 446, 416, 446, 416, 446, 416, 446, 416, 446, 416, 448, - 416, 446, 416, 448, 416, 446, 416, 448, 416, 446, 416, 1336, - 390, 1336, 390, 1336, 388, 1338, 390, 1336, 390, 1336, 392, 446, - 416, 446, 416, 448, 416, 1334, 390, 446, 416, 1338, 388, 1336, - 390, 1336, 390, 446, 416, 446, 416, 448, 414, 446, 416, 446, - 416, 446, 416, 448, 416, 446, 416, 446, 416, 446, 416, 446, - 416, 446, 416, 446, 416, 446, 416, 446, 416, 1336, 390, 446, - 416, 1336, 390, 446, 414, 448, 416, 446, 416, 446, 416, 446, - 416, 448, 416, 446, 416, 446, 416, 446, 416, 1336, 390, 446, - 416, 1336, 390, 446, 416, 446, 416, 446, 416, 448, 416, 1338, - 390, 444, 418, 1336, 390, 448, 416, 446, 416, 1336, 390, 446, - 416, 446, 416, 1336, 390, 1336, 388, 1336, 390, 446, 416, 1336, - 390, 448, 414, 448, 414, 448, 416, 1334, 390, 446, 416, 446, - 416, 446, 416, 448, 416, 446, 416, 446, 416, 448, 416, 446, - 416, 446, 416, 446, 416, 446, 416, 446, 416, 446, 416, 446, - 416, 446, 416, 446, 416, 446, 416, 446, 416, 446, 416, 446, - 416, 448, 416, 1336, 390, 1336, 390, 446, 416, 446, 416, 446, - 416, 446, 414, 446, 416, 448, 416, 446, 416, 448, 414, 446, - 418, 446, 416, 446, 416, 448, 416, 446, 416, 448, 416, 446, - 416, 448, 416, 446, 416, 1336, 390, 446, 416, 446, 416, 1338, - 390, 1336, 390, 446, 416, 446, 416}; // Captured by @sillyfrog - - irsend.reset(); - irsend.sendRaw(rawData, kDaikinRawBits, 38000); - irsend.makeDecodeResult(); - EXPECT_TRUE(irrecv.decode(&irsend.capture)); - ASSERT_EQ(DAIKIN, irsend.capture.decode_type); - ASSERT_EQ(kDaikinBits, irsend.capture.bits); - EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); -} - -// Decoding a message we entirely constructed based solely on a given state. -TEST(TestDecodeDaikin, SyntheticExample) { - IRDaikinESP irdaikin(0); - IRsendTest irsend(4); - IRrecv irrecv(4); - irsend.begin(); - - uint8_t expectedState[kDaikinStateLength] = { - 0x11, 0xDA, 0x27, 0x00, 0x42, 0x3A, 0x05, 0x93, 0x11, - 0xDA, 0x27, 0x00, 0x00, 0x3F, 0x3A, 0x00, 0xA0, 0x00, - 0x0A, 0x25, 0x17, 0x01, 0x00, 0xC0, 0x00, 0x00, 0x32}; - - irsend.reset(); - irsend.sendDaikin(expectedState); - irsend.makeDecodeResult(); - EXPECT_TRUE(irrecv.decode(&irsend.capture)); - ASSERT_EQ(DAIKIN, irsend.capture.decode_type); - ASSERT_EQ(kDaikinBits, irsend.capture.bits); - EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); -} diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_Whirlpool_test.cpp b/lib/IRremoteESP8266-2.5.2.03/test/ir_Whirlpool_test.cpp deleted file mode 100644 index c30cb21d3..000000000 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_Whirlpool_test.cpp +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright 2018 David Conran - -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" -#include "gtest/gtest.h" - -// Tests for sendWhirlpoolAC(). - -// Test sending typical data only. -TEST(TestSendWhirlpoolAC, SendDataOnly) { - IRsendTest irsend(0); - irsend.begin(); - uint8_t data[kWhirlpoolAcStateLength] = { - 0x83, 0x06, 0x10, 0x71, 0x00, 0x00, 0x91, 0x1F, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xEF, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x02}; - - irsend.sendWhirlpoolAC(data); - EXPECT_EQ( - "m8950s4484" - "m597s1649m597s1649m597s533m597s533m597s533m597s533m597s533m597s1649" - "m597s533m597s1649m597s1649m597s533m597s533m597s533m597s533m597s533" - "m597s533m597s533m597s533m597s533m597s1649m597s533m597s533m597s533" - "m597s1649m597s533m597s533m597s533m597s1649m597s1649m597s1649m597s533" - "m597s533m597s533m597s533m597s533m597s533m597s533m597s533m597s533" - "m597s533m597s533m597s533m597s533m597s533m597s533m597s533m597s533" - "m597s7920" - "m597s1649m597s533m597s533m597s533m597s1649m597s533m597s533m597s1649" - "m597s1649m597s1649m597s1649m597s1649m597s1649m597s533m597s533m597s533" - "m597s533m597s533m597s533m597s533m597s533m597s533m597s533m597s533" - "m597s533m597s533m597s533m597s533m597s533m597s533m597s533m597s533" - "m597s533m597s533m597s533m597s533m597s533m597s533m597s533m597s533" - "m597s533m597s533m597s533m597s533m597s533m597s533m597s533m597s533" - "m597s533m597s533m597s533m597s533m597s533m597s533m597s533m597s533" - "m597s1649m597s1649m597s1649m597s1649m597s533m597s1649m597s1649m597s1649" - "m597s7920" - "m597s533m597s533m597s533m597s533m597s533m597s533m597s533m597s533" - "m597s533m597s1649m597s533m597s533m597s533m597s533m597s533m597s533" - "m597s533m597s533m597s533m597s533m597s533m597s533m597s533m597s533" - "m597s533m597s533m597s533m597s533m597s533m597s533m597s533m597s533" - "m597s533m597s533m597s533m597s533m597s533m597s533m597s533m597s533" - "m597s533m597s533m597s533m597s533m597s533m597s533m597s533m597s533" - "m597s533m597s1649m597s533m597s533m597s533m597s533m597s533m597s533" - "m597s100000", - irsend.outputStr()); -} - -// Tests for decodeWhirlpoolAC(). -// Decode normal WhirlpoolAC messages. -TEST(TestDecodeWhirlpoolAC, SyntheticDecode) { - IRsendTest irsend(0); - IRrecv irrecv(0); - irsend.begin(); - - // Synthesised Normal WhirlpoolAC message. - irsend.reset(); - uint8_t expectedState[kWhirlpoolAcStateLength] = { - 0x83, 0x06, 0x10, 0x71, 0x00, 0x00, 0x91, 0x1F, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xEF, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x02}; - irsend.sendWhirlpoolAC(expectedState); - irsend.makeDecodeResult(); - EXPECT_TRUE(irrecv.decode(&irsend.capture)); - EXPECT_EQ(WHIRLPOOL_AC, irsend.capture.decode_type); - EXPECT_EQ(kWhirlpoolAcBits, irsend.capture.bits); - EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); -} - -// Decode a recorded example -TEST(TestDecodeWhirlpoolAC, RealExampleDecode) { - IRsendTest irsend(0); - IRrecv irrecv(0); - irsend.begin(); - - // Real WhirlpoolAC message. - // Ref: https://github.com/markszabo/IRremoteESP8266/issues/509 - uint16_t rawData[343] = { - 8950, 4484, 598, 1642, 598, 1646, 594, 534, 594, 538, 602, 532, - 598, 540, 600, 542, 598, 1650, 600, 522, 598, 1644, 596, 1650, - 600, 532, 598, 538, 602, 536, 594, 548, 592, 538, 602, 518, - 600, 524, 596, 532, 598, 532, 598, 1654, 596, 544, 596, 544, - 596, 536, 594, 1644, 596, 528, 600, 528, 592, 538, 602, 1648, - 602, 1654, 596, 1664, 598, 534, 594, 526, 594, 530, 598, 528, - 602, 530, 600, 534, 596, 542, 598, 542, 598, 534, 596, 526, - 594, 530, 600, 528, 602, 530, 600, 534, 596, 542, 598, 544, - 596, 518, 602, 7916, 598, 1642, 598, 528, 600, 528, 602, 530, - 600, 1652, 598, 542, 598, 544, 596, 1654, 596, 1644, 596, 1648, - 602, 1644, 596, 1654, 596, 1656, 604, 536, 594, 548, 602, 528, - 600, 520, 600, 524, 596, 532, 598, 532, 596, 538, 602, 536, - 594, 546, 594, 538, 602, 518, 600, 524, 596, 532, 598, 532, - 598, 536, 594, 544, 596, 544, 596, 536, 594, 526, 592, 530, - 600, 528, 600, 530, 602, 532, 596, 542, 598, 542, 598, 534, - 596, 524, 596, 528, 600, 526, 592, 538, 592, 542, 598, 540, - 600, 540, 600, 530, 598, 522, 598, 526, 594, 534, 596, 534, - 594, 540, 602, 536, 592, 548, 592, 538, 600, 1636, 594, 1648, - 602, 1642, 598, 1652, 598, 538, 602, 1680, 570, 1662, 598, 1634, - 596, 7924, 600, 520, 598, 526, 592, 534, 596, 534, 596, 540, - 600, 536, 604, 538, 602, 530, 600, 520, 598, 1640, 600, 528, - 600, 530, 600, 534, 594, 544, 596, 544, 596, 534, 596, 526, - 594, 528, 600, 526, 594, 536, 592, 542, 598, 538, 602, 538, - 602, 528, 600, 520, 600, 524, 596, 530, 600, 532, 598, 534, - 596, 542, 598, 542, 598, 532, 598, 524, 596, 528, 602, 526, - 594, 536, 594, 540, 600, 536, 594, 548, 592, 538, 602, 518, - 602, 522, 596, 530, 600, 530, 600, 534, 596, 542, 598, 544, - 596, 534, 596, 524, 594, 1644, 596, 532, 596, 534, 596, 538, - 602, 536, 594, 546, 594, 520, 600}; - uint8_t expectedState[kWhirlpoolAcStateLength] = { - 0x83, 0x06, 0x10, 0x71, 0x00, 0x00, 0x91, 0x1F, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xEF, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x02}; - - irsend.reset(); - irsend.sendRaw(rawData, 343, 38000); - irsend.makeDecodeResult(); - EXPECT_TRUE(irrecv.decode(&irsend.capture)); - EXPECT_EQ(WHIRLPOOL_AC, irsend.capture.decode_type); - EXPECT_EQ(kWhirlpoolAcBits, irsend.capture.bits); - EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); -} diff --git a/lib/IRremoteESP8266-2.5.2.03/.github/CONTRIBUTING.md b/lib/IRremoteESP8266-2.6.0/.github/CONTRIBUTING.md similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/.github/CONTRIBUTING.md rename to lib/IRremoteESP8266-2.6.0/.github/CONTRIBUTING.md diff --git a/lib/IRremoteESP8266-2.5.2.03/.github/Contributors.md b/lib/IRremoteESP8266-2.6.0/.github/Contributors.md similarity index 87% rename from lib/IRremoteESP8266-2.5.2.03/.github/Contributors.md rename to lib/IRremoteESP8266-2.6.0/.github/Contributors.md index 5f75ea3b4..af9734d69 100644 --- a/lib/IRremoteESP8266-2.5.2.03/.github/Contributors.md +++ b/lib/IRremoteESP8266-2.6.0/.github/Contributors.md @@ -12,7 +12,9 @@ - [Jorge Cisneros](https://github.com/jorgecis/) - [Denes Varga](https://github.com/denxhun/) - [Brett T. Warden](https://github.com/bwarden/) +- [Fabien Valthier](https://github.com/hcoohb) +- [Ajay Pala](https://github.com/ajaypala/) -All contributors can be found on the [contributors site](https://github.com/markszabo/IRremoteESP8266/graphs/contributors). +All contributors can be found on the [contributors site](https://github.com/markszabo/IRremoteESP8266/graphs/contributors). ### Contributors of the [original project](https://github.com/z3t0/Arduino-IRremote) can be found on the [original project's contributors page](https://github.com/z3t0/Arduino-IRremote/blob/master/Contributors.md) diff --git a/lib/IRremoteESP8266-2.5.2.03/.github/issue_template.md b/lib/IRremoteESP8266-2.6.0/.github/issue_template.md similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/.github/issue_template.md rename to lib/IRremoteESP8266-2.6.0/.github/issue_template.md diff --git a/lib/IRremoteESP8266-2.5.2.03/.gitignore b/lib/IRremoteESP8266-2.6.0/.gitignore similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/.gitignore rename to lib/IRremoteESP8266-2.6.0/.gitignore diff --git a/lib/IRremoteESP8266-2.5.2.03/.gitmodules b/lib/IRremoteESP8266-2.6.0/.gitmodules similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/.gitmodules rename to lib/IRremoteESP8266-2.6.0/.gitmodules diff --git a/lib/IRremoteESP8266-2.5.2.03/.style.yapf b/lib/IRremoteESP8266-2.6.0/.style.yapf similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/.style.yapf rename to lib/IRremoteESP8266-2.6.0/.style.yapf diff --git a/lib/IRremoteESP8266-2.5.2.03/.travis.yml b/lib/IRremoteESP8266-2.6.0/.travis.yml similarity index 81% rename from lib/IRremoteESP8266-2.5.2.03/.travis.yml rename to lib/IRremoteESP8266-2.6.0/.travis.yml index 4331425e9..ae2d9fe3c 100644 --- a/lib/IRremoteESP8266-2.5.2.03/.travis.yml +++ b/lib/IRremoteESP8266-2.6.0/.travis.yml @@ -1,20 +1,21 @@ language: c env: - - BD=esp8266:esp8266:nodemcuv2:CpuFrequency=80,FlashSize=4M3M - - BD=esp8266:esp8266:d1_mini:CpuFrequency=80,FlashSize=4M3M + - BD=esp8266:esp8266:nodemcuv2:xtal=80,eesz=4M3M,ip=lm2f,exception=disabled + - BD=esp8266:esp8266:d1_mini:xtal=80,eesz=4M3M,ip=lm2f,exception=disabled before_install: - "/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_1.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :1 -ac -screen 0 1280x1024x16" - sleep 3 - export DISPLAY=:1.0 - - wget http://downloads.arduino.cc/arduino-1.8.2-linux64.tar.xz - - tar xf arduino-1.8.2-linux64.tar.xz - - sudo mv arduino-1.8.2 /usr/local/share/arduino + - wget http://downloads.arduino.cc/arduino-1.8.8-linux64.tar.xz + - tar xf arduino-1.8.8-linux64.tar.xz + - sudo mv arduino-1.8.8 /usr/local/share/arduino - sudo ln -s /usr/local/share/arduino/arduino /usr/local/bin/arduino - wget https://raw.githubusercontent.com/google/styleguide/gh-pages/cpplint/cpplint.py install: - ln -s $PWD /usr/local/share/arduino/libraries/ - git clone https://github.com/tzapu/WiFiManager.git /usr/local/share/arduino/libraries/WiFiManager - git clone https://github.com/knolleary/pubsubclient.git /usr/local/share/arduino/libraries/PubSubClient + - git clone https://github.com/bblanchon/ArduinoJson.git --branch 5.x /usr/local/share/arduino/libraries/ArduinoJson - arduino --pref "boardsmanager.additional.urls=http://arduino.esp8266.com/stable/package_esp8266com_index.json" --save-prefs - arduino --install-boards esp8266:esp8266 - arduino --board $BD --save-prefs @@ -40,6 +41,10 @@ script: - arduino --verify --board $BD $PWD/examples/TurnOnArgoAC/TurnOnArgoAC.ino - arduino --verify --board $BD $PWD/examples/IRMQTTServer/IRMQTTServer.ino - arduino --verify --board $BD $PWD/examples/TurnOnToshibaAC/TurnOnToshibaAC.ino + - arduino --verify --board $BD $PWD/examples/ControlSamsungAC/ControlSamsungAC.ino + - arduino --verify --board $BD $PWD/examples/TurnOnPanasonicAC/TurnOnPanasonicAC.ino + - arduino --verify --board $BD $PWD/examples/TurnOnMitsubishiHeavyAc/TurnOnMitsubishiHeavyAc.ino + # Also check the tools programs compile. - (cd tools; make all) # Check for lint issues. diff --git a/lib/IRremoteESP8266-2.5.2.03/CPPLINT.cfg b/lib/IRremoteESP8266-2.6.0/CPPLINT.cfg similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/CPPLINT.cfg rename to lib/IRremoteESP8266-2.6.0/CPPLINT.cfg diff --git a/lib/IRremoteESP8266-2.5.2.03/LICENSE.txt b/lib/IRremoteESP8266-2.6.0/LICENSE.txt similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/LICENSE.txt rename to lib/IRremoteESP8266-2.6.0/LICENSE.txt diff --git a/lib/IRremoteESP8266-2.5.2.03/README.md b/lib/IRremoteESP8266-2.6.0/README.md similarity index 95% rename from lib/IRremoteESP8266-2.5.2.03/README.md rename to lib/IRremoteESP8266-2.6.0/README.md index bb9d5a9d8..1eaaa21b4 100644 --- a/lib/IRremoteESP8266-2.5.2.03/README.md +++ b/lib/IRremoteESP8266-2.6.0/README.md @@ -1,14 +1,15 @@ # IRremote ESP8266 Library [![Build Status](https://travis-ci.org/markszabo/IRremoteESP8266.svg?branch=master)](https://travis-ci.org/markszabo/IRremoteESP8266) +[![arduino-library-badge](https://www.ardu-badge.com/badge/IRremoteESP8266.svg?)](https://www.ardu-badge.com/IRremoteESP8266) [![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/markszabo/IRremoteESP8266.svg)](http://isitmaintained.com/project/markszabo/IRremoteESP8266 "Average time to resolve an issue") [![Percentage of issues still open](http://isitmaintained.com/badge/open/markszabo/IRremoteESP8266.svg)](http://isitmaintained.com/project/markszabo/IRremoteESP8266 "Percentage of issues still open") [![GitLicense](https://gitlicense.com/badge/markszabo/IRremoteESP8266)](https://gitlicense.com/license/markszabo/IRremoteESP8266) This library enables you to **send _and_ receive** infra-red signals on an [ESP8266 using the Arduino framework](https://github.com/esp8266/Arduino) using common 940nm IR LEDs and common IR receiver modules. e.g. TSOP{17,22,24,36,38,44,48}* etc. -## v2.5.2 Now Available -Version 2.5.2 of the library is now [available](https://github.com/markszabo/IRremoteESP8266/releases/latest). You can view the [Release Notes](ReleaseNotes.md) for all the significant changes. +## v2.6.0 Now Available +Version 2.6.0 of the library is now [available](https://github.com/markszabo/IRremoteESP8266/releases/latest). You can view the [Release Notes](ReleaseNotes.md) for all the significant changes. #### Upgrading from pre-v2.0 Usage of the library has been slightly changed in v2.0. You will need to change your usage to work with v2.0 and beyond. You can read more about the changes required on our [Upgrade to v2.0](https://github.com/markszabo/IRremoteESP8266/wiki/Upgrading-to-v2.0) page. diff --git a/lib/IRremoteESP8266-2.5.2.03/ReleaseNotes.md b/lib/IRremoteESP8266-2.6.0/ReleaseNotes.md similarity index 72% rename from lib/IRremoteESP8266-2.5.2.03/ReleaseNotes.md rename to lib/IRremoteESP8266-2.6.0/ReleaseNotes.md index 56e84dd89..98416a12a 100644 --- a/lib/IRremoteESP8266-2.5.2.03/ReleaseNotes.md +++ b/lib/IRremoteESP8266-2.6.0/ReleaseNotes.md @@ -1,5 +1,107 @@ # Release Notes +## _v2.6.0 (20190430)_ + +**[Bug Fixes]** +- Fixed problem where LG protocol used wrong duty cycle for repeat. (#687) +- Fix checksum calculation for Daikin protocols. (#678) +- Fix the byte array version of sendGree() (#684, #685) +- Fix artificial vs. real state creation on HaierAC. (#668, #671) +- Fix issues caused by having `MQTT_ENABLE` set to false. (#677) +- Fix compile problem when DEBUG is defined. (#673, #674) +- Fix Minor bug with MQTT_ENABLE False condition (#654) + +**[Features]** +- Experimental support for DAIKIN216 (ARC433B69) (#690) +- Experimental support for Mitsubishi Heavy Industries A/Cs. (#660, #665, #667) +- Support more features of TCL A/C (#656) +- Add LEGO(TM) Power Functions IR protocol. (#655) +- Add Panasonic AC RKR model & Example (#649) +- DAIKIN/IRDaikinESP overhaul and add Comfort mode support. (#678) + **WARNING**: Previous `sendDaikin()` calls may not work. + Please recapture codes or use `kDaikinStateLengthShort` for + `nbytes` in those calls. +- IRMQTTServer: Move MQTT server and other parameters to WifiManager. (#680) + **WARNING**: Previous users may need to fully wipe/reset the + SPIFFS/WifiManager settings by visiting + `http:///reset` prior to or + after update. +- Add Wifi filtering options to IRMQTTServer. (#679) +- Add advanced aircon/climate functionality to IRMQTTServer (#677) +- Initial prototype of a common interface for all A/Cs. (#664) +- Improve MQTT topic usage for feedback messages. (#663) +- Add multiple independent GPIO sending support via MQTT. (#661) + +**[Misc]** +- Adjust kGreeHdrSpace to 4500 (#684, #686) +- Add Home Assistant mqtt climate instructions. (#682) +- Implement htmlEscape() to prevent XSS etc. (#681) +- Add F() Macros (#670) +- Update Daikin2's Cool mode min temp to 18C (#658) +- Change per byte bit-order in Electra protocol. (#648) +- Improve Daikin2 power on/off. (#647) + + +## _v2.5.6 (20190316)_ + +**[Bug Fixes]** +- Fix Coolix A/C Class to handle special states better. (#633, #624) + +**[Features]** +- Fix case style for recent A/C protocols. (#631) +- Update `IRsend::send()` to include all simple protocols. (#629, #628) +- Experimental basic support for 112 bit TCL AC messages (#627, #619) +- Add support for TECO AC (#622) +- Experimental support for Samsung 36 bit protocol (#625, #621) + +**[Misc]** +- Set Coolix to default to 1 repeat. (#637, #636, #624, #439) +- Set Daikin2 modulation to 36.7kHz. (#635) +- Refactor IRVestelAC class to use portable code. (#617) +- Adjust Daikin2 timings and tolerance. (#616, #582) + + +## _v2.5.5 (20190207)_ + +**[Bug Fixes]** +- Fix decoding of Samsung A/C Extended messages. (#610) +- Fix IRMQTTServer example to work with GPIO0 as IR_RX (#608) +- Fix incorrect #define usage. (#597, #596) + +**[Features]** +- Add deep decoding/construction of Daikin2 messages (#600) +- Added Old Vestel A/C support (56 Bits) with full functions. (#607) + +**[Misc]** +- Add ControlSamsungAC example code. (#599) +- Add how to send a state/air-con to IRsendDemo.ino (#594) + + +## _v2.5.4 (20190102)_ + +**[Features]** +- Experimental basic support for 39 Byte Daikin A/C (#583) +- Handle send() repeats in A/C classes. Improve Coolix support. (#580) +- Add optional RX pin pullup and dump raw messages in IRMQTTServer.ino (#589) + +**[Misc]** +- Make auto_analyse_raw_data.py work with Python3 (#581) +- Update CI/travis config due to esp8266 core 2.5.0 changes (#591) + + +## _v2.5.3 (20181123)_ + +**[Features]** +- Add deep support for the Hitachi 28-Byte A/C Protocol (#563) +- Deep decoding for Whirlpool A/C (#572) +- Improve security options for IRMQTTServer example. (#575) +- Require a changed firmware password before upload. (#576) + +**[Misc]** +- Add missing '}' in output of Auto analyse. (#562) +- Make A/C example code a bit more simple. (#571) + + ## _v2.5.2 (20181021)_ **[Bug Fixes]** diff --git a/lib/IRremoteESP8266-2.6.0/examples/ControlSamsungAC/ControlSamsungAC.ino b/lib/IRremoteESP8266-2.6.0/examples/ControlSamsungAC/ControlSamsungAC.ino new file mode 100644 index 000000000..df910fe87 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/examples/ControlSamsungAC/ControlSamsungAC.ino @@ -0,0 +1,99 @@ +/* Copyright 2019 David Conran +* +* An IR LED circuit *MUST* be connected to the ESP8266 on a pin +* as specified by kIrLed below. +* +* TL;DR: The IR LED needs to be driven by a transistor for a good result. +* +* Suggested circuit: +* https://github.com/markszabo/IRremoteESP8266/wiki#ir-sending +* +* Common mistakes & tips: +* * Don't just connect the IR LED directly to the pin, it won't +* have enough current to drive the IR LED effectively. +* * Make sure you have the IR LED polarity correct. +* See: https://learn.sparkfun.com/tutorials/polarity/diode-and-led-polarity +* * Typical digital camera/phones can be used to see if the IR LED is flashed. +* Replace the IR LED with a normal LED if you don't have a digital camera +* when debugging. +* * Avoid using the following pins unless you really know what you are doing: +* * Pin 0/D3: Can interfere with the boot/program mode & support circuits. +* * Pin 1/TX/TXD0: Any serial transmissions from the ESP8266 will interfere. +* * Pin 3/RX/RXD0: Any serial transmissions to the ESP8266 will interfere. +* * ESP-01 modules are tricky. We suggest you use a module with more GPIOs +* for your first time. e.g. ESP-12 etc. +*/ +#ifndef UNIT_TEST +#include +#endif +#include +#include +#include + +const uint16_t kIrLed = 4; // ESP8266 GPIO pin to use. Recommended: 4 (D2). +IRSamsungAc ac(kIrLed); // Set the GPIO used for sending messages. + +void printState() { + // Display the settings. + Serial.println("Samsung A/C remote is in the following state:"); + Serial.printf(" %s\n", ac.toString().c_str()); +} + +void setup() { + ac.begin(); + Serial.begin(115200); + delay(200); + + // Set up what we want to send. See ir_Samsung.cpp for all the options. + Serial.println("Default state of the remote."); + printState(); + Serial.println("Setting initial state for A/C."); + ac.off(); + ac.setFan(kSamsungAcFanLow); + ac.setMode(kSamsungAcCool); + ac.setTemp(25); + ac.setSwing(false); + printState(); +} + +void loop() { + // Turn the A/C unit on and set to cooling mode. + // Power changes require we send an extended message. + Serial.println("Sending an extended IR command to A/C ..."); + ac.on(); + ac.setMode(kSamsungAcCool); + ac.sendExtended(); + printState(); + delay(15000); // wait 15 seconds + + // Increase the fan speed. + Serial.println("Sending a normal IR command to A/C ..."); + ac.setFan(kSamsungAcFanHigh); + ac.send(); + printState(); + delay(15000); + + // Change to swing the fan. + Serial.println("Sending a normal IR command to A/C ..."); + ac.setSwing(true); + ac.send(); + printState(); + delay(15000); + + // Change to Fan mode, lower the speed, and stop the swing. + Serial.println("Sending a normal IR command to A/C ..."); + ac.setSwing(false); + ac.setMode(kSamsungAcFan); + ac.setFan(kSamsungAcFanLow); + ac.send(); + printState(); + delay(15000); + + // Turn the A/C unit off. + // Power changes require we send an extended message. + Serial.println("Sending an extended IR command to A/C ..."); + ac.off(); + ac.sendExtended(); + printState(); + delay(15000); // wait 15 seconds +} diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/IRGCSendDemo/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/ControlSamsungAC/platformio.ini similarity index 83% rename from lib/IRremoteESP8266-2.5.2.03/examples/IRGCSendDemo/platformio.ini rename to lib/IRremoteESP8266-2.6.0/examples/ControlSamsungAC/platformio.ini index eeb8d1f2e..ec84f22f3 100644 --- a/lib/IRremoteESP8266-2.5.2.03/examples/IRGCSendDemo/platformio.ini +++ b/lib/IRremoteESP8266-2.6.0/examples/ControlSamsungAC/platformio.ini @@ -6,11 +6,13 @@ src_dir=. build_flags = lib_deps_builtin = lib_deps_external = +lib_ldf_mode = chain+ [env:nodemcuv2] platform = espressif8266 framework = arduino board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} build_flags = ${common.build_flags} lib_deps = ${common.lib_deps_builtin} diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/IRGCSendDemo/IRGCSendDemo.ino b/lib/IRremoteESP8266-2.6.0/examples/IRGCSendDemo/IRGCSendDemo.ino similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/examples/IRGCSendDemo/IRGCSendDemo.ino rename to lib/IRremoteESP8266-2.6.0/examples/IRGCSendDemo/IRGCSendDemo.ino diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/IRGCTCPServer/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/IRGCSendDemo/platformio.ini similarity index 83% rename from lib/IRremoteESP8266-2.5.2.03/examples/IRGCTCPServer/platformio.ini rename to lib/IRremoteESP8266-2.6.0/examples/IRGCSendDemo/platformio.ini index eeb8d1f2e..ec84f22f3 100644 --- a/lib/IRremoteESP8266-2.5.2.03/examples/IRGCTCPServer/platformio.ini +++ b/lib/IRremoteESP8266-2.6.0/examples/IRGCSendDemo/platformio.ini @@ -6,11 +6,13 @@ src_dir=. build_flags = lib_deps_builtin = lib_deps_external = +lib_ldf_mode = chain+ [env:nodemcuv2] platform = espressif8266 framework = arduino board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} build_flags = ${common.build_flags} lib_deps = ${common.lib_deps_builtin} diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/IRGCTCPServer/IRGCTCPServer.ino b/lib/IRremoteESP8266-2.6.0/examples/IRGCTCPServer/IRGCTCPServer.ino similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/examples/IRGCTCPServer/IRGCTCPServer.ino rename to lib/IRremoteESP8266-2.6.0/examples/IRGCTCPServer/IRGCTCPServer.ino diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/IRrecvDemo/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/IRGCTCPServer/platformio.ini similarity index 83% rename from lib/IRremoteESP8266-2.5.2.03/examples/IRrecvDemo/platformio.ini rename to lib/IRremoteESP8266-2.6.0/examples/IRGCTCPServer/platformio.ini index eeb8d1f2e..ec84f22f3 100644 --- a/lib/IRremoteESP8266-2.5.2.03/examples/IRrecvDemo/platformio.ini +++ b/lib/IRremoteESP8266-2.6.0/examples/IRGCTCPServer/platformio.ini @@ -6,11 +6,13 @@ src_dir=. build_flags = lib_deps_builtin = lib_deps_external = +lib_ldf_mode = chain+ [env:nodemcuv2] platform = espressif8266 framework = arduino board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} build_flags = ${common.build_flags} lib_deps = ${common.lib_deps_builtin} diff --git a/lib/IRremoteESP8266-2.6.0/examples/IRMQTTServer/IRMQTTServer.h b/lib/IRremoteESP8266-2.6.0/examples/IRMQTTServer/IRMQTTServer.h new file mode 100644 index 000000000..9494dbe2b --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/examples/IRMQTTServer/IRMQTTServer.h @@ -0,0 +1,258 @@ +/* + * Send & receive arbitrary IR codes via a web server or MQTT. + * Copyright David Conran 2016, 2017, 2018, 2019 + */ +#ifndef EXAMPLES_IRMQTTSERVER_IRMQTTSERVER_H_ +#define EXAMPLES_IRMQTTSERVER_IRMQTTSERVER_H_ + +#include +#include +#include +#include +#include +#include + +// ---------------- Start of User Configuration Section ------------------------ + +#ifndef MQTT_ENABLE +#define MQTT_ENABLE true // Whether or not MQTT is used at all. +#endif // MQTT_ENABLE + +// ---------------------- Board Related Settings ------------------------------- +// NOTE: Make sure you set your Serial Monitor to the same speed. +#define BAUD_RATE 115200 // Serial port Baud rate. + +// GPIO the IR LED is connected to/controlled by. GPIO 4 = D2. +#define IR_LED 4 // <=- CHANGE_ME (optional) +// define IR_LED 3 // For an ESP-01 we suggest you use RX/GPIO3/Pin 7. + +// GPIO the IR RX module is connected to/controlled by. e.g. GPIO 14 = D5. +// Comment this out to disable receiving/decoding IR messages entirely. +#define IR_RX 14 // <=- CHANGE_ME (optional) +#define IR_RX_PULLUP false + +// --------------------- Network Related Settings ------------------------------ +const uint16_t kHttpPort = 80; // The TCP port the HTTP server is listening on. +// Change to 'true'/'false' if you do/don't want these features or functions. +#define USE_STATIC_IP false // Change to 'true' if you don't want to use DHCP. +// We obtain our network config via DHCP by default but allow an easy way to +// use a static IP config. +#if USE_STATIC_IP +const IPAddress kIPAddress = IPAddress(10, 0, 1, 78); +const IPAddress kGateway = IPAddress(10, 0, 1, 1); +const IPAddress kSubnetMask = IPAddress(255, 255, 255, 0); +#endif // USE_STATIC_IP + +// See: https://github.com/tzapu/WiFiManager#filter-networks for these settings. +#define HIDE_DUPLIATE_NETWORKS false // Should WifiManager hide duplicate SSIDs +// #define MIN_SIGNAL_STRENGTH 20 // Minimum WiFi signal stength (percentage) + // before we will connect. + // The unset default is 8%. + // (Uncomment to enable) + +// ----------------------- HTTP Related Settings ------------------------------- +#define FIRMWARE_OTA true // Allow remote update of the firmware via http. + // Less secure if enabled. + // Note: Firmware OTA is also disabled until + // a password is set. +#define HTML_PASSWORD_ENABLE false // Protect access to the HTML interface. + // Note: OTA update is always passworded. +// If you do not set a password, Firmware OTA updates will be blocked. + +// ----------------------- MQTT Related Settings ------------------------------- +#if MQTT_ENABLE +const uint32_t kMqttReconnectTime = 5000; // Delay(ms) between reconnect tries. + +#define MQTT_ACK "sent" // Sub-topic we send back acknowledgements on. +#define MQTT_SEND "send" // Sub-topic we get new commands from. +#define MQTT_RECV "received" // Topic we send received IRs to. +#define MQTT_LOG "log" // Topic we send log messages to. +#define MQTT_LWT "status" // Topic for the Last Will & Testament. +#define MQTT_CLIMATE "ac" // Sub-topic for the climate topics. +#define MQTT_CLIMATE_CMND "cmnd" // Sub-topic for the climate command topics. +#define MQTT_CLIMATE_STAT "stat" // Sub-topic for the climate stat topics. +#define MQTTbroadcastInterval 10 * 60 // Seconds between rebroadcasts + +#define QOS 1 // MQTT broker should queue up any unreceived messages for us +// #define QOS 0 // MQTT broker WON'T queue up messages for us. Fire & Forget. +#endif // MQTT_ENABLE + +// ------------------------ IR Capture Settings -------------------------------- +// Let's use a larger than normal buffer so we can handle AirCon remote codes. +const uint16_t kCaptureBufferSize = 1024; +#if DECODE_AC +// Some A/C units have gaps in their protocols of ~40ms. e.g. Kelvinator +// A value this large may swallow repeats of some protocols +const uint8_t kCaptureTimeout = 50; // Milliseconds +#else // DECODE_AC +// Suits most messages, while not swallowing many repeats. +const uint8_t kCaptureTimeout = 15; // Milliseconds +#endif // DECODE_AC +// Ignore unknown messages with <10 pulses (see also REPORT_UNKNOWNS) +const uint16_t kMinUnknownSize = 2 * 10; +#define REPORT_UNKNOWNS false // Report inbound IR messages that we don't know. +#define REPORT_RAW_UNKNOWNS false // Report the whole buffer, recommended: + // MQTT_MAX_PACKET_SIZE of 1024 or more + +// ------------------------ Advanced Usage Only -------------------------------- +// Change if you need multiple independent send gpio/topics. +const uint8_t gpioTable[] = { + IR_LED, // Default GPIO. e.g. ir_server/send or ir_server/send_0 + // Uncomment the following as needed. + // NOTE: Remember to disable DEBUG if you are using one of the serial pins. + // 5, // GPIO 5 / D1 e.g. ir_server/send_1 + // 14, // GPIO 14 / D5 e.g. ir_server/send_2 + // 16, // GPIO 16 / D0 e.g. ir_server/send_3 +}; + +#define KEY_PROTOCOL "protocol" +#define KEY_MODEL "model" +#define KEY_POWER "power" +#define KEY_MODE "mode" +#define KEY_TEMP "temp" +#define KEY_FANSPEED "fanspeed" +#define KEY_SWINGV "swingv" +#define KEY_SWINGH "swingh" +#define KEY_QUIET "quiet" +#define KEY_TURBO "turbo" +#define KEY_LIGHT "light" +#define KEY_BEEP "beep" +#define KEY_ECONO "econo" +#define KEY_SLEEP "sleep" +#define KEY_CLOCK "clock" +#define KEY_FILTER "filter" +#define KEY_CLEAN "clean" +#define KEY_CELSIUS "use_celsius" + +// HTML arguments we will parse for IR code information. +#define KEY_TYPE "type" // KEY_PROTOCOL is also checked too. +#define KEY_CODE "code" +#define KEY_BITS "bits" +#define KEY_REPEAT "repeats" + +// Text for Last Will & Testament status messages. +const char* kLwtOnline = "Online"; +const char* kLwtOffline = "Offline"; + +const uint8_t kHostnameLength = 30; +const uint8_t kPortLength = 5; // Largest value of uint16_t is "65535". +const uint8_t kUsernameLength = 15; +const uint8_t kPasswordLength = 20; + +// -------------------------- Debug Settings ----------------------------------- +// Disable debug output if any of the IR pins are on the TX (D1) pin. +// Note: This is a crude method to catch the common use cases. +// See `isSerialGpioUsedByIr()` for the better method. +#if (IR_LED != 1 && IR_RX != 1) +#ifndef DEBUG +#define DEBUG true // Change to 'false' to disable all serial output. +#endif // DEBUG +#else // (IR_LED != 1 && IR_RX != 1) +#undef DEBUG +#define DEBUG false +#endif + +// ----------------- End of User Configuration Section ------------------------- + +// Constants +#define _MY_VERSION_ "v1.0.0" + +const uint8_t kSendTableSize = sizeof(gpioTable); +// JSON stuff +// Name of the json config file in SPIFFS. +const char* kConfigFile = "/config.json"; +const char* kMqttServerKey = "mqtt_server"; +const char* kMqttPortKey = "mqtt_port"; +const char* kMqttUserKey = "mqtt_user"; +const char* kMqttPassKey = "mqtt_pass"; +const char* kMqttPrefixKey = "mqtt_prefix"; +const char* kHostnameKey = "hostname"; +const char* kHttpUserKey = "http_user"; +const char* kHttpPassKey = "http_pass"; + +#if MQTT_ENABLE +const uint32_t kBroadcastPeriodMs = MQTTbroadcastInterval * 1000; // mSeconds. +const uint32_t kStatListenPeriodMs = 5 * 1000; // mSeconds + +void mqttCallback(char* topic, byte* payload, unsigned int length); +String listOfCommandTopics(void); +void handleSendMqttDiscovery(void); +void subscribing(const String topic_name); +void unsubscribing(const String topic_name); +void mqttLog(const String mesg); +bool reconnect(void); +void receivingMQTT(String const topic_name, String const callback_str); +void callback(char* topic, byte* payload, unsigned int length); +void sendMQTTDiscovery(const char *topic); +void doBroadcast(TimerMs *timer, const uint32_t interval, + const commonAcState_t state, const bool retain, + const bool force); +#endif // MQTT_ENABLE +bool isSerialGpioUsedByIr(void); +void debug(const char *str); +void saveWifiConfigCallback(void); +void saveWifiConfig(void); +void loadWifiConfigFile(void); +String msToHumanString(uint32_t const msecs); +String timeElapsed(uint32_t const msec); +String timeSince(uint32_t const start); +String listOfSendGpios(void); +bool hasUnsafeHTMLChars(String input); +String htmlMenu(void); +void handleRoot(void); +String addJsReloadUrl(const String url, const uint16_t timeout_s, + const bool notify); +void handleExamples(void); +String boolToString(const bool value); +String opmodeToString(const stdAc::opmode_t mode); +String fanspeedToString(const stdAc::fanspeed_t speed); +String swingvToString(const stdAc::swingv_t swingv); +String swinghToString(const stdAc::swingh_t swingh); +String htmlSelectBool(const String name, const bool def); +String htmlSelectProtocol(const String name, const decode_type_t def); +String htmlSelectModel(const String name, const int16_t def); +String htmlSelectMode(const String name, const stdAc::opmode_t def); +String htmlSelectFanspeed(const String name, const stdAc::fanspeed_t def); +String htmlSelectSwingv(const String name, const stdAc::swingv_t def); +String htmlSelectSwingh(const String name, const stdAc::swingh_t def); +void handleAirCon(void); +void handleAirConSet(void); +void handleAdmin(void); +void handleInfo(void); +void handleReset(void); +void handleReboot(void); +bool parseStringAndSendAirCon(IRsend *irsend, const uint16_t irType, + const String str); +uint16_t countValuesInStr(const String str, char sep); +uint16_t * newCodeArray(const uint16_t size); +#if SEND_GLOBALCACHE +bool parseStringAndSendGC(IRsend *irsend, const String str); +#endif // SEND_GLOBALCACHE +#if SEND_PRONTO +bool parseStringAndSendPronto(IRsend *irsend, const String str, + uint16_t repeats); +#endif // SEND_PRONTO +#if SEND_RAW +bool parseStringAndSendRaw(IRsend *irsend, const String str); +#endif // SEND_RAW +void handleIr(void); +void handleNotFound(void); +void setup_wifi(void); +void init_vars(void); +void setup(void); +void loop(void); +uint64_t getUInt64fromHex(char const *str); +bool sendIRCode(IRsend *irsend, int const ir_type, + uint64_t const code, char const * code_str, uint16_t bits, + uint16_t repeat); +bool sendInt(const String topic, const int32_t num, const bool retain); +bool sendBool(const String topic, const bool on, const bool retain); +bool sendString(const String topic, const String str, const bool retain); +bool sendFloat(const String topic, const float_t temp, const bool retain); +commonAcState_t updateClimate(commonAcState_t current, const String str, + const String prefix, const String payload); +bool cmpClimate(const commonAcState_t a, const commonAcState_t b); +bool sendClimate(const commonAcState_t prev, const commonAcState_t next, + const String topic_prefix, const bool retain, + const bool forceMQTT, const bool forceIR); +#endif // EXAMPLES_IRMQTTSERVER_IRMQTTSERVER_H_ diff --git a/lib/IRremoteESP8266-2.6.0/examples/IRMQTTServer/IRMQTTServer.ino b/lib/IRremoteESP8266-2.6.0/examples/IRMQTTServer/IRMQTTServer.ino new file mode 100644 index 000000000..31e40432d --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/examples/IRMQTTServer/IRMQTTServer.ino @@ -0,0 +1,3034 @@ +/* + * Send & receive arbitrary IR codes via a web server or MQTT. + * Copyright David Conran 2016, 2017, 2018, 2019 + * + * Copyright: + * Code for this has been borrowed from lots of other OpenSource projects & + * resources. I'm *NOT* claiming complete Copyright ownership of all the code. + * Likewise, feel free to borrow from this as much as you want. + * + * NOTE: An IR LED circuit SHOULD be connected to ESP8266 GPIO4 (D2) if + * you want to send IR messages. + * A compatible IR RX modules SHOULD be connected to ESP8266 GPIO14 (D5) + * if you want to capture & decode IR nessages. + * See 'IR_LED' & 'IR_RX' in IRMQTTServer.h. + * + * WARN: This is *very* advanced & complicated example code. Not for beginners. + * You are strongly suggested to try & look at other example code first + * to understand how this library works. + * + * # Instructions + * + * ## Before First Boot (i.e. Compile time) + * - Disable MQTT if desired. (see '#define MQTT_ENABLE' in IRMQTTServer.h). + * + * - Site specific settings: + * o Search for 'CHANGE_ME' in IRMQTTServer.h for the things you probably + * need to change for your particular situation. + * o All user changable settings are in the file IRMQTTServer.h. + * + * - Arduino IDE: + * o Install the following libraries via Library Manager + * - ArduinoJson (https://arduinojson.org/) (Version >= 5.x and < 6) + * - PubSubClient (https://pubsubclient.knolleary.net/) + * - WiFiManager (https://github.com/tzapu/WiFiManager) (Version >= 0.14) + * o You MUST change to have the following (or larger) value: + * (with REPORT_RAW_UNKNOWNS 1024 or more is recommended) + * #define MQTT_MAX_PACKET_SIZE 768 + * - PlatformIO IDE: + * If you are using PlatformIO, this should already been done for you in + * the accompanying platformio.ini file. + * + * ## First Boot (Initial setup) + * The ESP8266 board will boot into the WiFiManager's AP mode. + * i.e. It will create a WiFi Access Point with a SSID like: "ESP123456" etc. + * Connect to that SSID. Then point your browser to http://192.168.4.1/ and + * configure the ESP8266 to connect to your desired WiFi network and associated + * required settings. It will remember these details on next boot if the device + * connects successfully. + * More information can be found here: + * https://github.com/tzapu/WiFiManager#how-it-works + * + * If you need to reset the WiFi and saved settings to go back to "First Boot", + * visit: http:///reset + * + * ## Normal Use (After initial setup) + * Enter 'http:///ir?type=7&code=E0E09966 + * http:///ir?type=4&code=0xf50&bits=12 + * http:///ir?code=C1A2E21D&repeats=8&type=19 + * http:///ir?type=31&code=40000,1,1,96,24,24,24,48,24,24,24,24,24,48,24,24,24,24,24,48,24,24,24,24,24,24,24,24,1058 + * http:///ir?type=18&code=190B8050000000E0190B8070000010f0 + * http:///ir?repeats=1&type=25&code=0000,006E,0022,0002,0155,00AA,0015,0040,0015,0040,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0040,0015,0040,0015,0015,0015,0040,0015,0015,0015,0015,0015,0015,0015,0040,0015,0015,0015,0015,0015,0040,0015,0040,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0040,0015,0015,0015,0015,0015,0040,0015,0040,0015,0040,0015,0040,0015,0040,0015,0640,0155,0055,0015,0E40 + * + * or + * + * Send a MQTT message to the topic 'ir_server/send' (or 'ir_server/send_0' etc) + * using the following format (Order is important): + * protocol_num,hexcode + * e.g. 7,E0E09966 + * which is: Samsung(7), Power On code, default bit size, + * default nr. of repeats. + * + * protocol_num,hexcode,bits + * e.g. 4,f50,12 + * which is: Sony(4), Power Off code, 12 bits & default nr. of repeats. + * + * protocol_num,hexcode,bits,repeats + * e.g. 19,C1A2E21D,0,8 + * which is: Sherwood(19), Vol Up, default bit size & repeated 8 times. + * + * 30,frequency,raw_string + * e.g. 30,38000,9000,4500,500,1500,500,750,500,750 + * which is: Raw (30) @ 38kHz with a raw code of + * "9000,4500,500,1500,500,750,500,750" + * + * 31,code_string + * e.g. 31,40000,1,1,96,24,24,24,48,24,24,24,24,24,48,24,24,24,24,24,48,24,24,24,24,24,24,24,24,1058 + * which is: GlobalCache (31) & "40000,1,1,96,..." (Sony Vol Up) + * + * 25,Rrepeats,hex_code_string + * e.g. 25,R1,0000,006E,0022,0002,0155,00AA,0015,0040,0015,0040,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0040,0015,0040,0015,0015,0015,0040,0015,0015,0015,0015,0015,0015,0015,0040,0015,0015,0015,0015,0015,0040,0015,0040,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0040,0015,0015,0015,0015,0015,0040,0015,0040,0015,0040,0015,0040,0015,0040,0015,0640,0155,0055,0015,0E40 + * which is: Pronto (25), 1 repeat, & "0000 006E 0022 0002 ..." + * aka a "Sherwood Amp Tape Input" message. + * + * ac_protocol_num,really_long_hexcode + * e.g. 18,190B8050000000E0190B8070000010F0 + * which is: Kelvinator (18) Air Con on, Low Fan, 25 deg etc. + * NOTE: Ensure you zero-pad to the correct number of digits for the + * bit/byte size you want to send as some A/C units have units + * have different sized messages. e.g. Fujitsu A/C units. + * + * In short: + * No spaces after/before commas. + * Values are comma separated. + * The first value is always in Decimal. + * For simple protocols, the next value (hexcode) is always hexadecimal. + * The optional bit size is in decimal. + * CAUTION: Some AC protocols DO NOT use the really_long_hexcode method. + * e.g. < 64bit AC protocols. + * + * Unix command line usage example: + * # Install a MQTT client + * $ sudo apt install mosquitto-clients + * # Send a 32-bit NEC code of 0x1234abcd via MQTT. + * $ mosquitto_pub -h 10.0.0.4 -t ir_server/send -m '3,1234abcd,32' + * + * This server will send (back) what ever IR message it just transmitted to + * the MQTT topic 'ir_server/sent' to confirm it has been performed. This works + * for messages requested via MQTT or via HTTP. + * + * Unix command line usage example: + * # Listen to MQTT acknowledgements. + * $ mosquitto_sub -h 10.0.0.4 -t ir_server/sent + * + * Incoming IR messages (from an IR remote control) will be transmitted to + * the MQTT topic 'ir_server/received'. The MQTT message will be formatted + * similar to what is required to for the 'sent' topic. + * e.g. "3,C1A2F00F,32" (Protocol,Value,Bits) for simple codes + * or "18,110B805000000060110B807000001070" (Protocol,Value) for complex codes + * Note: If the protocol is listed as -1, then that is an UNKNOWN IR protocol. + * You can't use that to recreate/resend an IR message. It's only for + * matching purposes and shouldn't be trusted. + * + * Unix command line usage example: + * # Listen via MQTT for IR messages captured by this server. + * $ mosquitto_sub -h 10.0.0.4 -t ir_server/received + * + * Note: General logging messages are also sent to 'ir_server/log' from + * time to time. + * + * ## Climate (AirCon) interface. (Advanced use) + * You can now control Air Conditioner devices that have full/detailed support + * from the IRremoteESP8266 library. See the "Aircon" page for list of supported + * devices. You can do this via HTTP/HTML or via MQTT. + * + * NOTE: It will only change the attributes you change/set. It's up to you to + * maintain a consistent set of attributes for your particular aircon. + * + * TIP: Use "-1" for 'model' if your A/C doesn't have a specific `setModel()` + * or IR class attribute. Most don't. Some do. + * e.g. PANASONIC_AC, FUJITSU_AC, WHIRLPOOL_AC + * + * ### via MQTT: + * The code listen for commands (via wildcard) on the MQTT topics at the + * `ir_server/ac/cmnd/+` level, such as: + * i.e. protocol, model, power, mode, temp, fanspeed, swingv, swingh, quiet, + * turbo, light, beep, econo, sleep, filter, clean, use_celsius + * e.g. ir_server/ac/cmnd/power, ir_server/ac/cmnd/temp, etc. + * It will process them, and if successful and it caused a change, it will + * acknowledge this via the relevant state topic for that command. + * e.g. If the aircon/climate changes from power off to power on, it will + * send an "on" payload to "ir_server/ac/stat/power" + * NOTE: These "stat" messages have the MQTT retain flag set to on. Thus the + * MQTT broker will remember them until reset/restarted etc. + * + * The code will also periodically broadcast all possible aircon/climate state + * attributes to their corresponding "ir_server/ac/stat" topics. This ensures + * any updates to the ESP's knowledge that may have been lost in transmission + * are re-communicated. e.g. The MQTT broker being offline. + * This also helps with Home Assistant MQTT discovery. + * + * The program on boot & first successful connection to the MQTT broker, will + * try to re-acquire any previous aircon/climate state information and act + * accordingly. This will typically result in A/C IR message being sent as and + * saved state will probably be different from the defaults. + * + * NOTE: Command attributes are processed sequentially. + * e.g. Going from "25C, cool, fan low" to "27C, heat, fan high" may go + * via "27C, cool, fan low" & "27C, heat, fan low" depending on the order + * of arrival & processing of the MQTT commands. + * + * ### Home Assistant (HA) MQTT climate integration + * After you have set the Protocol (required) & Model (if needed) and any of + * the other misc aircon settings you desire, you can then add the following to + * your Home Assistant configuration, and it should allow you to + * control most of the important settings. Google Home/Assistant (via HA) + * can also control the device, but you will need to configure Home Assistant + * via it's documentation for that. It has even more limited control. + * It's far beyond the scope of these instructions to guide you through setting + * up HA and Google Home integration. See https://www.home-assistant.io/ + * + * In HA's configuration.yaml, add: + * + * climate: + * platform: mqtt + * name: Living Room Aircon + * modes: + * - "off" + * - "auto" + * - "cool" + * - "heat" + * - "dry" + * - "fan_only" + * fan_modes: + * - "auto" + * - "min" + * - "low" + * - "medium" + * - "high" + * - "max" + * swing_modes: + * - "off" + * - "auto" + * - "highest" + * - "high" + * - "middle" + * - "low" + * - "lowest" + * power_command_topic: "ir_server/ac/cmnd/power" + * mode_command_topic: "ir_server/ac/cmnd/mode" + * mode_state_topic: "ir_server/ac/stat/mode" + * temperature_command_topic: "ir_server/ac/cmnd/temp" + * temperature_state_topic: "ir_server/ac/stat/temp" + * fan_mode_command_topic: "ir_server/ac/cmnd/fanspeed" + * fan_mode_state_topic: "ir_server/ac/stat/fanspeed" + * swing_mode_command_topic: "ir_server/ac/cmnd/swingv" + * swing_mode_state_topic: "ir_server/ac/stat/swingv" + * min_temp: 16 + * max_temp: 32 + * temp_step: 1 + * retain: false + * + * ### via HTTP: + * Use the "http:///aircon/set" URL and pass on + * the arguments as needed to control your device. See the `KEY_*` #defines + * in the code for all the parameters. + * i.e. protocol, model, power, mode, temp, fanspeed, swingv, swingh, quiet, + * turbo, light, beep, econo, sleep, filter, clean, use_celsius + * Example: + * http:///aircon/set?protocol=PANASONIC_AC&model=LKE&power=on&mode=auto&fanspeed=min&temp=23 + * + * ## Debugging & Logging + * If DEBUG is turned on, there is additional information printed on the Serial + * Port. Serial Port output may be disabled if the GPIO is used for IR. + * + * If MQTT is enabled, some information/logging is sent to the MQTT topic: + * `ir_server/log` + * + * ## Updates + * You can upload new firmware over the air (OTA) via the form on the device's + * main page. No need to connect to the device again via USB. \o/ + * Your WiFi settings should be remembered between updates. \o/ \o/ + * + * ## Security + * + * There is NO authentication set on the HTTP/HTML interface by default (see + * `HTML_PASSWORD_ENABLE` to change that), and there is NO SSL/TLS (encryption) + * used by this example code. + * i.e. All usernames & passwords are sent in clear text. + * All communication to the MQTT server is in clear text. + * e.g. This on/using the public Internet is a 'Really Bad Idea'! + * You should NOT have or use this code or device exposed on an untrusted and/or + * unprotected network. + * If you allow access to OTA firmware updates, then a 'Bad Guy' could + * potentially compromise your network. OTA updates are password protected by + * default. If you are sufficiently paranoid, you SHOULD disable uploading + * firmware via OTA. (see 'FIRMWARE_OTA') + * You SHOULD also set/change all usernames & passwords. + * For extra bonus points: Use a separate untrusted SSID/vlan/network/ segment + * for your IoT stuff, including this device. + * Caveat Emptor. You have now been suitably warned. + * + */ + +#include "IRMQTTServer.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if MQTT_ENABLE +// -------------------------------------------------------------------- +// * * * IMPORTANT * * * +// You must change to have the following value. +// #define MQTT_MAX_PACKET_SIZE 768 +// -------------------------------------------------------------------- +#include +#endif // MQTT_ENABLE +#include // NOLINT(build/include) +#include +#include + +// Globals +ESP8266WebServer server(kHttpPort); +#ifdef IR_RX +IRrecv irrecv(IR_RX, kCaptureBufferSize, kCaptureTimeout, true); +decode_results capture; // Somewhere to store inbound IR messages. +#endif // IR_RX +MDNSResponder mdns; +WiFiClient espClient; +WiFiManager wifiManager; +bool flagSaveWifiConfig = false; +char HttpUsername[kUsernameLength + 1] = "admin"; // Default HTT username. +char HttpPassword[kPasswordLength + 1] = ""; // No HTTP password by default. +char Hostname[kHostnameLength + 1] = "ir_server"; // Default hostname. +uint16_t *codeArray; +uint32_t lastReconnectAttempt = 0; // MQTT last attempt reconnection number +bool boot = true; +bool lockIr = false; // Primitive locking for gating the IR LED. +uint32_t sendReqCounter = 0; +bool lastSendSucceeded = false; // Store the success status of the last send. +uint32_t lastSendTime = 0; +int8_t offset; // The calculated period offset for this chip and library. +IRsend *IrSendTable[kSendTableSize]; + +#ifdef IR_RX +String lastIrReceived = "None"; +uint32_t lastIrReceivedTime = 0; +uint32_t irRecvCounter = 0; +#endif // IR_RX + +// Climate stuff +commonAcState_t climate; +commonAcState_t climate_prev; +IRac commonAc(gpioTable[0]); +TimerMs lastClimateIr = TimerMs(); // When we last sent the IR Climate mesg. +uint32_t irClimateCounter = 0; // How many have we sent? +// Store the success status of the last climate send. +bool lastClimateSucceeded = false; +bool hasClimateBeenSent = false; // Has the Climate ever been sent? + +#if MQTT_ENABLE +PubSubClient mqtt_client(espClient); +String lastMqttCmd = "None"; +String lastMqttCmdTopic = "None"; +uint32_t lastMqttCmdTime = 0; +uint32_t lastConnectedTime = 0; +uint32_t lastDisconnectedTime = 0; +uint32_t mqttDisconnectCounter = 0; +uint32_t mqttSentCounter = 0; +uint32_t mqttRecvCounter = 0; +bool wasConnected = true; + +char MqttServer[kHostnameLength + 1] = "10.0.0.4"; +char MqttPort[kPortLength + 1] = "1883"; +char MqttUsername[kUsernameLength + 1] = ""; +char MqttPassword[kPasswordLength + 1] = ""; +char MqttPrefix[kHostnameLength + 1] = ""; + +String MqttAck; // Sub-topic we send back acknowledgements on. +String MqttSend; // Sub-topic we get new commands from. +String MqttRecv; // Topic we send received IRs to. +String MqttLog; // Topic we send log messages to. +String MqttLwt; // Topic for the Last Will & Testament. +String MqttClimate; // Sub-topic for the climate topics. +String MqttClimateCmnd; // Sub-topic for the climate command topics. +String MqttClimateStat; // Sub-topic for the climate stat topics. +String MqttDiscovery; +String MqttHAName; +String MqttClientId; + +// Primative lock file for gating MQTT state broadcasts. +bool lockMqttBroadcast = true; +TimerMs lastBroadcast = TimerMs(); // When we last sent a broadcast. +bool hasBroadcastBeenSent = false; +TimerMs lastDiscovery = TimerMs(); // When we last sent a Discovery. +bool hasDiscoveryBeenSent = false; +TimerMs statListenTime = TimerMs(); // How long we've been listening for. +#endif // MQTT_ENABLE + +bool isSerialGpioUsedByIr(void) { + const uint8_t kSerialTxGpio = 1; // The GPIO serial output is sent too. + // Note: *DOES NOT* control Serial output. + // Ensure we are not trodding on anything IR related. +#ifdef IR_RX + if (IR_RX == kSerialTxGpio) + return true; // Serial port is in use by IR capture. Abort. +#endif // IR_RX + for (uint8_t i = 0; i < kSendTableSize; i++) + if (gpioTable[i] == kSerialTxGpio) + return true; // Serial port is in use for IR sending. Abort. + return false; // Not in use as far as we can tell. +} + +// Debug messages get sent to the serial port. +void debug(const char *str) { +#if DEBUG + if (isSerialGpioUsedByIr()) return; // Abort. + uint32_t now = millis(); + Serial.printf("%07u.%03u: %s\n", now / 1000, now % 1000, str); +#endif // DEBUG +} + +// callback notifying us of the need to save the wifi config +void saveWifiConfigCallback(void) { + debug("saveWifiConfigCallback called."); + flagSaveWifiConfig = true; +} + +void saveWifiConfig(void) { + debug("Saving the wifi config."); + DynamicJsonBuffer jsonBuffer; + JsonObject& json = jsonBuffer.createObject(); +#if MQTT_ENABLE + json[kMqttServerKey] = MqttServer; + json[kMqttPortKey] = MqttPort; + json[kMqttUserKey] = MqttUsername; + json[kMqttPassKey] = MqttPassword; + json[kMqttPrefixKey] = MqttPrefix; +#endif // MQTT_ENABLE + json[kHostnameKey] = Hostname; + json[kHttpUserKey] = HttpUsername; + json[kHttpPassKey] = HttpPassword; + + if (SPIFFS.begin()) { + File configFile = SPIFFS.open(kConfigFile, "w"); + if (!configFile) { + debug("Failed to open config file for writing."); + } else { + debug("Writing out the config file."); + json.printTo(configFile); + configFile.close(); + debug("Finished writing config file."); + } + SPIFFS.end(); + } +} + +void loadWifiConfigFile(void) { + debug("Trying to mount SPIFFS"); + if (SPIFFS.begin()) { + debug("mounted file system"); + if (SPIFFS.exists(kConfigFile)) { + debug("config file exists"); + + File configFile = SPIFFS.open(kConfigFile, "r"); + if (configFile) { + debug("Opened config file"); + size_t size = configFile.size(); + // Allocate a buffer to store contents of the file. + std::unique_ptr buf(new char[size]); + + configFile.readBytes(buf.get(), size); + DynamicJsonBuffer jsonBuffer; + JsonObject& json = jsonBuffer.parseObject(buf.get()); + if (json.success()) { + debug("Json config file parsed ok."); +#if MQTT_ENABLE + strncpy(MqttServer, json[kMqttServerKey] | "", kHostnameLength); + strncpy(MqttPort, json[kMqttPortKey] | "1883", kPortLength); + strncpy(MqttUsername, json[kMqttUserKey] | "", kUsernameLength); + strncpy(MqttPassword, json[kMqttPassKey] | "", kPasswordLength); + strncpy(MqttPrefix, json[kMqttPrefixKey] | "", kHostnameLength); +#endif // MQTT_ENABLE + strncpy(Hostname, json[kHostnameKey] | "", kHostnameLength); + strncpy(HttpUsername, json[kHttpUserKey] | "", kUsernameLength); + strncpy(HttpPassword, json[kHttpPassKey] | "", kPasswordLength); + debug("Recovered Json fields."); + } else { + debug("Failed to load json config"); + } + debug("Closing the config file."); + configFile.close(); + } + } else { + debug("Config file doesn't exist!"); + } + debug("Unmounting SPIFFS."); + SPIFFS.end(); + } else { + debug("Failed to mount SPIFFS"); + } +} + +String msToHumanString(uint32_t const msecs) { + uint32_t totalseconds = msecs / 1000; + if (totalseconds == 0) return "Now"; + + // Note: millis() can only count up to 45 days, so uint8_t is safe. + uint8_t days = totalseconds / (60 * 60 * 24); + uint8_t hours = (totalseconds / (60 * 60)) % 24; + uint8_t minutes = (totalseconds / 60) % 60; + uint8_t seconds = totalseconds % 60; + + String result = ""; + if (days) result += String(days) + " day"; + if (days > 1) result += 's'; + if (hours) result += ' ' + String(hours) + " hour"; + if (hours > 1) result += 's'; + if (minutes) result += ' ' + String(minutes) + " minute"; + if (minutes > 1) result += 's'; + if (seconds) result += ' ' + String(seconds) + " second"; + if (seconds > 1) result += 's'; + result.trim(); + return result; +} + +String timeElapsed(uint32_t const msec) { + String result = msToHumanString(msec); + if (result.equalsIgnoreCase("Now")) + return result; + else + return result + " ago"; +} + +String timeSince(uint32_t const start) { + if (start == 0) + return "Never"; + uint32_t diff = 0; + uint32_t now = millis(); + if (start < now) + diff = now - start; + else + diff = UINT32_MAX - start + now; + return msToHumanString(diff) + " ago"; +} + +// Return a string containing the comma separated list of sending gpios. +String listOfSendGpios(void) { + String result = String(gpioTable[0]); + if (kSendTableSize > 1) result += " (default)"; + for (uint8_t i = 1; i < kSendTableSize; i++) { + result += ", " + String(gpioTable[i]); + } + return result; +} + +String htmlMenu(void) { + return F( + "
" + "" + "" + "" + "" + "" + "
" + "
"); +} + +// Root web page with example usage etc. +void handleRoot(void) { +#if HTML_PASSWORD_ENABLE + if (!server.authenticate(HttpUsername, HttpPassword)) { + debug("Basic HTTP authentication failure for /."); + return server.requestAuthentication(); + } +#endif + String html = F( + "IR MQTT server" + "" + "

ESP8266 IR MQTT Server

" + "
" _MY_VERSION_ "
"); + html += htmlMenu(); + html += F( + "

Send a simple IR message

" + "

" + "Type: " + "" + " Code: 0x" + " Bit size: " + "" + " Repeats: " + " " + "
" + "

" + "

Send a complex (Air Conditioner) IR message

" + "

" + "Type: " + "" + " State code: 0x" + "" + " " + "
" + "

" + "

Send an IRremote Raw IR message

" + "

" + "" + "String: (freq,array data) " + " " + "
" + "

" + "

Send a GlobalCache" + " IR message

" + "

" + "" + "String: 1:1,1," + " " + "
" + "

" + "

Send a Pronto code IR message

" + "

" + "" + "String (comma separated): " + " Repeats: " + " " + "
" + "
"); + server.send(200, "text/html", html); +} + +String addJsReloadUrl(const String url, const uint16_t timeout_s, + const bool notify) { + String html = F( + "\n"); + return html; +} + +// Web page with hardcoded example usage etc. +void handleExamples(void) { +#if HTML_PASSWORD_ENABLE + if (!server.authenticate(HttpUsername, HttpPassword)) { + debug("Basic HTTP authentication failure for /examples."); + return server.requestAuthentication(); + } +#endif + String html = F( + "IR MQTT examples" + "" + "

ESP8266 IR MQTT Server

" + "
" _MY_VERSION_ "
"); + html += htmlMenu(); + html += F( + "

Hardcoded examples

" + "

" + "Sherwood Amp On (GlobalCache)

" + "

" + "Sherwood Amp Off (Raw)

" + "

" + "Sherwood Amp Input TAPE (Pronto)

" + "

TV on (Samsung)

" + "

Power Off (Sony 12bit)

" + "

" + "Panasonic A/C LKE model, On, Auto mode, Min fan, 23C" + " (via HTTP aircon interface)

" + "

" + "Change just the temp to 27C (via HTTP aircon interface)

" + "

" + "Turn OFF the current A/C (via HTTP aircon interface)

" + "

"); + server.send(200, "text/html", html); +} + +String boolToString(const bool value) { + return value ? F("on") : F("off"); +} + + +String opmodeToString(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kOff: + return F("off"); + case stdAc::opmode_t::kAuto: + return F("auto"); + case stdAc::opmode_t::kCool: + return F("cool"); + case stdAc::opmode_t::kHeat: + return F("heat"); + case stdAc::opmode_t::kDry: + return F("dry"); + case stdAc::opmode_t::kFan: + return F("fan_only"); + default: + return F("unknown"); + } +} + +String fanspeedToString(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kAuto: + return F("auto"); + case stdAc::fanspeed_t::kMax: + return F("max"); + case stdAc::fanspeed_t::kHigh: + return F("high"); + case stdAc::fanspeed_t::kMedium: + return F("medium"); + case stdAc::fanspeed_t::kLow: + return F("low"); + case stdAc::fanspeed_t::kMin: + return F("min"); + default: + return F("unknown"); + } +} + +String swingvToString(const stdAc::swingv_t swingv) { + switch (swingv) { + case stdAc::swingv_t::kOff: + return F("off"); + case stdAc::swingv_t::kAuto: + return F("auto"); + case stdAc::swingv_t::kHighest: + return F("highest"); + case stdAc::swingv_t::kHigh: + return F("high"); + case stdAc::swingv_t::kMiddle: + return F("middle"); + case stdAc::swingv_t::kLow: + return F("low"); + case stdAc::swingv_t::kLowest: + return F("lowest"); + default: + return F("unknown"); + } +} + +String swinghToString(const stdAc::swingh_t swingh) { + switch (swingh) { + case stdAc::swingh_t::kOff: + return F("off"); + case stdAc::swingh_t::kAuto: + return F("auto"); + case stdAc::swingh_t::kLeftMax: + return F("leftmax"); + case stdAc::swingh_t::kLeft: + return F("left"); + case stdAc::swingh_t::kMiddle: + return F("middle"); + case stdAc::swingh_t::kRight: + return F("right"); + case stdAc::swingh_t::kRightMax: + return F("rightmax"); + default: + return F("unknown"); + } +} + +String htmlSelectBool(const String name, const bool def) { + String html = ""); + return html; +} + +String htmlSelectProtocol(const String name, const decode_type_t def) { + String html = ""); + return html; +} + +String htmlSelectModel(const String name, const int16_t def) { + String html = ""); + return html; +} + +String htmlSelectMode(const String name, const stdAc::opmode_t def) { + String html = ""); + return html; +} + +String htmlSelectFanspeed(const String name, const stdAc::fanspeed_t def) { + String html = ""); + return html; +} + +String htmlSelectSwingv(const String name, const stdAc::swingv_t def) { + String html = ""); + return html; +} + +String htmlSelectSwingh(const String name, const stdAc::swingh_t def) { + String html = ""); + return html; +} + +// Admin web page +void handleAirCon(void) { + String html = F( + "AirCon control" + "" + "

Air Conditioner Control

"); + html += htmlMenu(); + html += "

Current Settings

" + "
" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "
Protocol" + + htmlSelectProtocol(KEY_PROTOCOL, climate.protocol) + "
Model" + htmlSelectModel(KEY_MODEL, climate.model) + + "
Power" + htmlSelectBool(KEY_POWER, climate.power) + + "
Mode" + htmlSelectMode(KEY_MODE, climate.mode) + + "
Temp" + "" + "
Fan Speed" + + htmlSelectFanspeed(KEY_FANSPEED, climate.fanspeed) + "
Swing (V)" + + htmlSelectSwingv(KEY_SWINGV, climate.swingv) + "
Swing (H)" + + htmlSelectSwingh(KEY_SWINGH, climate.swingh) + "
Quiet" + htmlSelectBool(KEY_QUIET, climate.quiet) + + "
Turbo" + htmlSelectBool(KEY_TURBO, climate.turbo) + + "
Econo" + htmlSelectBool(KEY_ECONO, climate.econo) + + "
Light" + htmlSelectBool(KEY_LIGHT, climate.light) + + "
Filter" + htmlSelectBool(KEY_FILTER, climate.filter) + + "
Clean" + htmlSelectBool(KEY_CLEAN, climate.clean) + + "
Beep" + htmlSelectBool(KEY_BEEP, climate.beep) + + "
" + "" + "
"; + // Display the current settings. + html += F(""); + server.send(200, "text/html", html); +} + +// Parse the URL args to find the Common A/C arguments. +void handleAirConSet(void) { +#if HTML_PASSWORD_ENABLE + if (!server.authenticate(HttpUsername, HttpPassword)) { + debug("Basic HTTP authentication failure for /aircon/set."); + return server.requestAuthentication(); + } +#endif + commonAcState_t result = climate; + debug("New common a/c received via HTTP"); + for (uint16_t i = 0; i < server.args(); i++) + result = updateClimate(result, server.argName(i), "", server.arg(i)); + +#if MQTT_ENABLE + sendClimate(climate, result, MqttClimateStat, + true, false, false); +#else // MQTT_ENABLE + sendClimate(climate, result, "", false, false, false); +#endif // MQTT_ENABLE + // Update the old climate state with the new one. + climate = result; + // Redirect back to the aircon page. + String html = F( + "Update Aircon" + "" + "

Aircon updated!

"); + html += addJsReloadUrl("/aircon", 2, false); + html += F(""); + server.send(200, "text/html", html); +} + +// Admin web page +void handleAdmin(void) { + String html = F( + "IR MQTT server admin" + "" + "

Administration

"); + html += htmlMenu(); + html += F( + "

Special commands

" +#if MQTT_ENABLE + " " + "Send a Climate MQTT discovery message to Home Assistant.

" +#endif // MQTT_ENABLE + " A simple reboot of the ESP8266. " + "ie. No changes

" + " Warning: " + "Resets the device back to original settings. " + "ie. Goes back to AP/Setup mode.
"); +#if FIRMWARE_OTA + html += F("

Update firmware

" + "Warning:
"); + if (!strlen(HttpPassword)) // Deny if password not set + html += F("OTA firmware is disabled until you set a password. " + "You will need to wipe & reset to set one." + "

"); + else // default password has been changed, so allow it. + html += F( + "Updating your firmware may screw up your access to the device. " + "If you are going to use this, know what you are doing first " + "(and you probably do).
" + "

" + "Firmware to upload: " + "" + "
"); +#endif // FIRMWARE_OTA + html += F(""); + server.send(200, "text/html", html); +} + +// Info web page +void handleInfo(void) { + String html = + "IR MQTT server info" + "" + "

Information

"; + html += htmlMenu(); + html += + "

General

" + "

Hostname: " + String(Hostname) + "
" + "IP address: " + WiFi.localIP().toString() + "
" + "Booted: " + timeSince(1) + "
" + + "Version: " _MY_VERSION_ "
" + "Built: " __DATE__ + " " __TIME__ "
" + "Period Offset: " + String(offset) + "us
" + "IR Lib Version: " _IRREMOTEESP8266_VERSION_ "
" + "ESP8266 Core Version: " + ESP.getCoreVersion() + "
" + "IR Send GPIO(s): " + listOfSendGpios() + "
" + "Total send requests: " + String(sendReqCounter) + "
" + "Last message sent: " + String(lastSendSucceeded ? "Ok" : "FAILED") + + " (" + timeSince(lastSendTime) + ")
" +#ifdef IR_RX + "IR Recv GPIO: " + String(IR_RX) + +#if IR_RX_PULLUP + " (pullup)" +#endif // IR_RX_PULLUP + "
" + "Total IR Received: " + String(irRecvCounter) + "
" + "Last IR Received: " + lastIrReceived + + " (" + timeSince(lastIrReceivedTime) + ")
" +#endif // IR_RX + "Duplicate Wifi networks: " + + String(HIDE_DUPLIATE_NETWORKS ? "Hide" : "Show") + "
" + "Min Wifi signal required: " +#ifdef MIN_SIGNAL_STRENGTH + + String(static_cast(MIN_SIGNAL_STRENGTH)) + +#else // MIN_SIGNAL_STRENGTH + "8" +#endif // MIN_SIGNAL_STRENGTH + "%
" + "Serial debugging: " +#if DEBUG + + String(isSerialGpioUsedByIr() ? "Off" : "On") + +#else // DEBUG + "Off" +#endif // DEBUG + "
" + "

" +#if MQTT_ENABLE + "

MQTT Information

" + "

Server: " + String(MqttServer) + ":" + String(MqttPort) + " (" + + (mqtt_client.connected() ? "Connected " + timeSince(lastDisconnectedTime) + : "Disconnected " + timeSince(lastConnectedTime)) + + ")
" + "Disconnections: " + String(mqttDisconnectCounter - 1) + "
" + "Client id: " + MqttClientId + "
" + "Command topic(s): " + listOfCommandTopics() + "
" + "Acknowledgements topic: " + MqttAck + "
" +#ifdef IR_RX + "IR Received topic: " + MqttRecv + "
" +#endif // IR_RX + "Log topic: " + MqttLog + "
" + "LWT topic: " + MqttLwt + "
" + "QoS: " + String(QOS) + "
" + // lastMqttCmd* is unescaped untrusted input. + // Avoid any possible HTML/XSS when displaying it. + "Last MQTT command seen: (topic) '" + htmlEscape(lastMqttCmdTopic) + + "' (payload) '" + htmlEscape(lastMqttCmd) + "' (" + + timeSince(lastMqttCmdTime) + ")
" + "Total published: " + String(mqttSentCounter) + "
" + "Total received: " + String(mqttRecvCounter) + "
" + "

" +#endif // MQTT_ENABLE + "

Climate Information

" + "

" + "IR Send GPIO: " + String(gpioTable[0]) + "
" + "Total sent: " + String(irClimateCounter) + "
" + "Last send: " + String(hasClimateBeenSent ? + (String(lastClimateSucceeded ? "Ok" : "FAILED") + + " (" + timeElapsed(lastClimateIr.elapsed()) + ")") : + "Never") + "
" +#if MQTT_ENABLE + "State listen period: " + msToHumanString(kStatListenPeriodMs) + "
" + "State broadcast period: " + msToHumanString(kBroadcastPeriodMs) + "
" + "Last state broadcast: " + (hasBroadcastBeenSent ? + timeElapsed(lastBroadcast.elapsed()) : + String("Never")) + "
" + "Last discovery sent: " + (lockMqttBroadcast ? + String("Locked") : + (hasDiscoveryBeenSent ? + timeElapsed(lastDiscovery.elapsed()) : + String("Never"))) + + "
" + "Command topics: " + MqttClimateCmnd + + "(" KEY_PROTOCOL "|" KEY_MODEL "|" KEY_POWER "|" KEY_MODE "|" KEY_TEMP "|" + KEY_FANSPEED "|" KEY_SWINGV "|" KEY_SWINGH "|" KEY_QUIET "|" + KEY_TURBO "|" KEY_LIGHT "|" KEY_BEEP "|" KEY_ECONO "|" KEY_SLEEP "|" + KEY_CLOCK "|" KEY_FILTER "|" KEY_CLEAN "|" KEY_CELSIUS ")
" + "State topics: " + MqttClimateStat + + "(" KEY_PROTOCOL "|" KEY_MODEL "|" KEY_POWER "|" KEY_MODE "|" KEY_TEMP "|" + KEY_FANSPEED "|" KEY_SWINGV "|" KEY_SWINGH "|" KEY_QUIET "|" + KEY_TURBO "|" KEY_LIGHT "|" KEY_BEEP "|" KEY_ECONO "|" KEY_SLEEP "|" + KEY_CLOCK "|" KEY_FILTER "|" KEY_CLEAN "|" KEY_CELSIUS ")
" +#endif // MQTT_ENABLE + "

" + // Page footer + "

" + "(Note: Page will refresh every 60 seconds.)" + "

"; + html += addJsReloadUrl("/info", 60, false); + html += ""; + server.send(200, "text/html", html); +} +// Reset web page +void handleReset(void) { +#if HTML_PASSWORD_ENABLE + if (!server.authenticate(HttpUsername, HttpPassword)) { + debug("Basic HTTP authentication failure for /reset."); + return server.requestAuthentication(); + } +#endif + server.send(200, "text/html", + "Reset WiFi Config" + "" + "

Resetting the WiFiManager config back to defaults.

" + "

Device restarting. Try connecting in a few seconds.

" + + addJsReloadUrl("/", 10, true) + + ""); + // Do the reset. +#if MQTT_ENABLE + mqttLog("Wiping all saved config settings."); +#endif // MQTT_ENABLE + debug("Trying to mount SPIFFS"); + if (SPIFFS.begin()) { + debug("Removing JSON config file"); + SPIFFS.remove(kConfigFile); + SPIFFS.end(); + } + delay(1000); + debug("Reseting wifiManager's settings."); + wifiManager.resetSettings(); + delay(1000); + debug("rebooting..."); + ESP.restart(); + delay(1000); +} + +// Reboot web page +void handleReboot() { +#if HTML_PASSWORD_ENABLE + if (!server.authenticate(HttpUsername, HttpPassword)) { + debug("Basic HTTP authentication failure for /quitquitquit."); + return server.requestAuthentication(); + } +#endif + server.send(200, "text/html", + "Rebooting" + "" + "

Device restarting.

" + "

Try connecting in a few seconds.

" + + addJsReloadUrl("/", 15, true) + + ""); +#if MQTT_ENABLE + mqttLog("Reboot requested"); +#endif // MQTT_ENABLE + // Do the reset. + delay(1000); + ESP.restart(); + delay(1000); +} + +// Parse an Air Conditioner A/C Hex String/code and send it. +// Args: +// irsend: A Ptr to the IRsend object to transmit via. +// irType: Nr. of the protocol we need to send. +// str: A hexadecimal string containing the state to be sent. +// Returns: +// bool: Successfully sent or not. +bool parseStringAndSendAirCon(IRsend *irsend, const uint16_t irType, + const String str) { + uint8_t strOffset = 0; + uint8_t state[kStateSizeMax] = {0}; // All array elements are set to 0. + uint16_t stateSize = 0; + + if (str.startsWith("0x") || str.startsWith("0X")) + strOffset = 2; + // Calculate how many hexadecimal characters there are. + uint16_t inputLength = str.length() - strOffset; + if (inputLength == 0) { + debug("Zero length AirCon code encountered. Ignored."); + return false; // No input. Abort. + } + + switch (irType) { // Get the correct state size for the protocol. + case KELVINATOR: + stateSize = kKelvinatorStateLength; + break; + case TOSHIBA_AC: + stateSize = kToshibaACStateLength; + break; + case DAIKIN: + // Daikin has 2 different possible size states. + // (The correct size, and a legacy shorter size.) + // Guess which one we are being presented with based on the number of + // hexadecimal digits provided. i.e. Zero-pad if you need to to get + // the correct length/byte size. + // This should provide backward compatiblity with legacy messages. + stateSize = inputLength / 2; // Every two hex chars is a byte. + // Use at least the minimum size. + stateSize = std::max(stateSize, kDaikinStateLengthShort); + // If we think it isn't a "short" message. + if (stateSize > kDaikinStateLengthShort) + // Then it has to be at least the version of the "normal" size. + stateSize = std::max(stateSize, kDaikinStateLength); + // Lastly, it should never exceed the "normal" size. + stateSize = std::min(stateSize, kDaikinStateLength); + break; + case DAIKIN2: + stateSize = kDaikin2StateLength; + break; + case DAIKIN216: + stateSize = kDaikin216StateLength; + break; + case ELECTRA_AC: + stateSize = kElectraAcStateLength; + break; + case MITSUBISHI_AC: + stateSize = kMitsubishiACStateLength; + break; + case MITSUBISHI_HEAVY_88: + stateSize = kMitsubishiHeavy88StateLength; + break; + case MITSUBISHI_HEAVY_152: + stateSize = kMitsubishiHeavy152StateLength; + break; + case PANASONIC_AC: + stateSize = kPanasonicAcStateLength; + break; + case TROTEC: + stateSize = kTrotecStateLength; + break; + case ARGO: + stateSize = kArgoStateLength; + break; + case GREE: + stateSize = kGreeStateLength; + break; + case FUJITSU_AC: + // Fujitsu has four distinct & different size states, so make a best guess + // which one we are being presented with based on the number of + // hexadecimal digits provided. i.e. Zero-pad if you need to to get + // the correct length/byte size. + stateSize = inputLength / 2; // Every two hex chars is a byte. + // Use at least the minimum size. + stateSize = std::max(stateSize, + (uint16_t) (kFujitsuAcStateLengthShort - 1)); + // If we think it isn't a "short" message. + if (stateSize > kFujitsuAcStateLengthShort) + // Then it has to be at least the smaller version of the "normal" size. + stateSize = std::max(stateSize, (uint16_t) (kFujitsuAcStateLength - 1)); + // Lastly, it should never exceed the maximum "normal" size. + stateSize = std::min(stateSize, kFujitsuAcStateLength); + break; + case HAIER_AC: + stateSize = kHaierACStateLength; + break; + case HAIER_AC_YRW02: + stateSize = kHaierACYRW02StateLength; + break; + case HITACHI_AC: + stateSize = kHitachiAcStateLength; + break; + case HITACHI_AC1: + stateSize = kHitachiAc1StateLength; + break; + case HITACHI_AC2: + stateSize = kHitachiAc2StateLength; + break; + case WHIRLPOOL_AC: + stateSize = kWhirlpoolAcStateLength; + break; + case SAMSUNG_AC: + // Samsung has two distinct & different size states, so make a best guess + // which one we are being presented with based on the number of + // hexadecimal digits provided. i.e. Zero-pad if you need to to get + // the correct length/byte size. + stateSize = inputLength / 2; // Every two hex chars is a byte. + // Use at least the minimum size. + stateSize = std::max(stateSize, (uint16_t) (kSamsungAcStateLength)); + // If we think it isn't a "normal" message. + if (stateSize > kSamsungAcStateLength) + // Then it probably the extended size. + stateSize = std::max(stateSize, + (uint16_t) (kSamsungAcExtendedStateLength)); + // Lastly, it should never exceed the maximum "extended" size. + stateSize = std::min(stateSize, kSamsungAcExtendedStateLength); + break; + case MWM: + // MWM has variable size states, so make a best guess + // which one we are being presented with based on the number of + // hexadecimal digits provided. i.e. Zero-pad if you need to to get + // the correct length/byte size. + stateSize = inputLength / 2; // Every two hex chars is a byte. + // Use at least the minimum size. + stateSize = std::max(stateSize, (uint16_t) 3); + // Cap the maximum size. + stateSize = std::min(stateSize, kStateSizeMax); + break; + case TCL112AC: + stateSize = kTcl112AcStateLength; + break; + default: // Not a protocol we expected. Abort. + debug("Unexpected AirCon protocol detected. Ignoring."); + return false; + } + if (inputLength > stateSize * 2) { + debug("AirCon code to large for the given protocol."); + return false; + } + + // Ptr to the least significant byte of the resulting state for this protocol. + uint8_t *statePtr = &state[stateSize - 1]; + + // Convert the string into a state array of the correct length. + for (uint16_t i = 0; i < inputLength; i++) { + // Grab the next least sigificant hexadecimal digit from the string. + uint8_t c = tolower(str[inputLength + strOffset - i - 1]); + if (isxdigit(c)) { + if (isdigit(c)) + c -= '0'; + else + c = c - 'a' + 10; + } else { + debug("Aborting! Non-hexadecimal char found in AirCon state:"); + debug(str.c_str()); + return false; + } + if (i % 2 == 1) { // Odd: Upper half of the byte. + *statePtr += (c << 4); + statePtr--; // Advance up to the next least significant byte of state. + } else { // Even: Lower half of the byte. + *statePtr = c; + } + } + + // Make the appropriate call for the protocol type. + switch (irType) { +#if SEND_KELVINATOR + case KELVINATOR: + irsend->sendKelvinator(reinterpret_cast(state)); + break; +#endif +#if SEND_TOSHIBA_AC + case TOSHIBA_AC: + irsend->sendToshibaAC(reinterpret_cast(state)); + break; +#endif +#if SEND_DAIKIN + case DAIKIN: + irsend->sendDaikin(reinterpret_cast(state)); + break; +#endif +#if SEND_DAIKIN2 + case DAIKIN2: + irsend->sendDaikin2(reinterpret_cast(state)); + break; +#endif +#if SEND_DAIKIN216 + case DAIKIN216: + irsend->sendDaikin216(reinterpret_cast(state)); + break; +#endif // SEND_DAIKIN216 +#if SEND_MITSUBISHI_AC + case MITSUBISHI_AC: + irsend->sendMitsubishiAC(reinterpret_cast(state)); + break; +#endif +#if SEND_MITSUBISHIHEAVY + case MITSUBISHI_HEAVY_88: // 59 + irsend->sendMitsubishiHeavy88(reinterpret_cast(state)); + break; + case MITSUBISHI_HEAVY_152: // 60 + irsend->sendMitsubishiHeavy152(reinterpret_cast(state)); + break; +#endif // SEND_MITSUBISHIHEAVY +#if SEND_TROTEC + case TROTEC: + irsend->sendTrotec(reinterpret_cast(state)); + break; +#endif +#if SEND_ARGO + case ARGO: + irsend->sendArgo(reinterpret_cast(state)); + break; +#endif +#if SEND_GREE + case GREE: + irsend->sendGree(reinterpret_cast(state)); + break; +#endif +#if SEND_FUJITSU_AC + case FUJITSU_AC: + irsend->sendFujitsuAC(reinterpret_cast(state), stateSize); + break; +#endif +#if SEND_HAIER_AC + case HAIER_AC: + irsend->sendHaierAC(reinterpret_cast(state)); + break; +#endif +#if SEND_HAIER_AC_YRW02 + case HAIER_AC_YRW02: + irsend->sendHaierACYRW02(reinterpret_cast(state)); + break; +#endif +#if SEND_HITACHI_AC + case HITACHI_AC: + irsend->sendHitachiAC(reinterpret_cast(state)); + break; +#endif +#if SEND_HITACHI_AC1 + case HITACHI_AC1: + irsend->sendHitachiAC1(reinterpret_cast(state)); + break; +#endif +#if SEND_HITACHI_AC2 + case HITACHI_AC2: + irsend->sendHitachiAC2(reinterpret_cast(state)); + break; +#endif +#if SEND_WHIRLPOOL_AC + case WHIRLPOOL_AC: + irsend->sendWhirlpoolAC(reinterpret_cast(state)); + break; +#endif +#if SEND_SAMSUNG_AC + case SAMSUNG_AC: + irsend->sendSamsungAC(reinterpret_cast(state), stateSize); + break; +#endif +#if SEND_ELECTRA_AC + case ELECTRA_AC: + irsend->sendElectraAC(reinterpret_cast(state)); + break; +#endif +#if SEND_PANASONIC_AC + case PANASONIC_AC: + irsend->sendPanasonicAC(reinterpret_cast(state)); + break; +#endif +#if SEND_MWM + case MWM: + irsend->sendMWM(reinterpret_cast(state), stateSize); + break; +#endif +#if SEND_TCL112AC + case TCL112AC: + irsend->sendTcl112Ac(reinterpret_cast(state)); + break; +#endif + default: + debug("Unexpected AirCon type in send request. Not sent."); + return false; + } + return true; // We were successful as far as we can tell. +} + +// Count how many values are in the String. +// Args: +// str: String containing the values. +// sep: Character that separates the values. +// Returns: +// The number of values found in the String. +uint16_t countValuesInStr(const String str, char sep) { + int16_t index = -1; + uint16_t count = 1; + do { + index = str.indexOf(sep, index + 1); + count++; + } while (index != -1); + return count; +} + +// Dynamically allocate an array of uint16_t's. +// Args: +// size: Nr. of uint16_t's need to be in the new array. +// Returns: +// A Ptr to the new array. Restarts the ESP8266 if it fails. +uint16_t * newCodeArray(const uint16_t size) { + uint16_t *result; + + result = reinterpret_cast(malloc(size * sizeof(uint16_t))); + // Check we malloc'ed successfully. + if (result == NULL) { // malloc failed, so give up. + Serial.printf("\nCan't allocate %d bytes. (%d bytes free)\n", + size * sizeof(uint16_t), ESP.getFreeHeap()); + Serial.println("Giving up & forcing a reboot."); + ESP.restart(); // Reboot. + delay(500); // Wait for the restart to happen. + return result; // Should never get here, but just in case. + } + return result; +} + +#if SEND_GLOBALCACHE +// Parse a GlobalCache String/code and send it. +// Args: +// irsend: A ptr to the IRsend object to transmit via. +// str: A GlobalCache formatted String of comma separated numbers. +// e.g. "38000,1,1,170,170,20,63,20,63,20,63,20,20,20,20,20,20,20,20,20, +// 20,20,63,20,63,20,63,20,20,20,20,20,20,20,20,20,20,20,20,20,63, +// 20,20,20,20,20,20,20,20,20,20,20,20,20,63,20,20,20,63,20,63,20, +// 63,20,63,20,63,20,63,20,1798" +// Note: The leading "1:1,1," of normal GC codes should be removed. +// Returns: +// bool: Successfully sent or not. +bool parseStringAndSendGC(IRsend *irsend, const String str) { + uint16_t count; + uint16_t *code_array; + String tmp_str; + + // Remove the leading "1:1,1," if present. + if (str.startsWith("1:1,1,")) + tmp_str = str.substring(6); + else + tmp_str = str; + + // Find out how many items there are in the string. + count = countValuesInStr(tmp_str, ','); + + // Now we know how many there are, allocate the memory to store them all. + code_array = newCodeArray(count); + + // Now convert the strings to integers and place them in code_array. + count = 0; + uint16_t start_from = 0; + int16_t index = -1; + do { + index = tmp_str.indexOf(',', start_from); + code_array[count] = tmp_str.substring(start_from, index).toInt(); + start_from = index + 1; + count++; + } while (index != -1); + + irsend->sendGC(code_array, count); // All done. Send it. + free(code_array); // Free up the memory allocated. + if (count > 0) + return true; // We sent something. + return false; // We probably didn't. +} +#endif // SEND_GLOBALCACHE + +#if SEND_PRONTO +// Parse a Pronto Hex String/code and send it. +// Args: +// irsend: A ptr to the IRsend object to transmit via. +// str: A comma-separated String of nr. of repeats, then hexadecimal numbers. +// e.g. "R1,0000,0067,0000,0015,0060,0018,0018,0018,0030,0018,0030,0018, +// 0030,0018,0018,0018,0030,0018,0018,0018,0018,0018,0030,0018, +// 0018,0018,0030,0018,0030,0018,0030,0018,0018,0018,0018,0018, +// 0030,0018,0018,0018,0018,0018,0030,0018,0018,03f6" +// or +// "0000,0067,0000,0015,0060,0018". i.e. without the Repeat value +// Requires at least kProntoMinLength comma-separated values. +// sendPronto() only supports raw pronto code types, thus so does this. +// repeats: Nr. of times the message is to be repeated. +// This value is ignored if an embeddd repeat is found in str. +// Returns: +// bool: Successfully sent or not. +bool parseStringAndSendPronto(IRsend *irsend, const String str, + uint16_t repeats) { + uint16_t count; + uint16_t *code_array; + int16_t index = -1; + uint16_t start_from = 0; + + // Find out how many items there are in the string. + count = countValuesInStr(str, ','); + + // Check if we have the optional embedded repeats value in the code string. + if (str.startsWith("R") || str.startsWith("r")) { + // Grab the first value from the string, as it is the nr. of repeats. + index = str.indexOf(',', start_from); + repeats = str.substring(start_from + 1, index).toInt(); // Skip the 'R'. + start_from = index + 1; + count--; // We don't count the repeats value as part of the code array. + } + + // We need at least kProntoMinLength values for the code part. + if (count < kProntoMinLength) return false; + + // Now we know how many there are, allocate the memory to store them all. + code_array = newCodeArray(count); + + // Rest of the string are values for the code array. + // Now convert the hex strings to integers and place them in code_array. + count = 0; + do { + index = str.indexOf(',', start_from); + // Convert the hexadecimal value string to an unsigned integer. + code_array[count] = strtoul(str.substring(start_from, index).c_str(), + NULL, 16); + start_from = index + 1; + count++; + } while (index != -1); + + irsend->sendPronto(code_array, count, repeats); // All done. Send it. + free(code_array); // Free up the memory allocated. + if (count > 0) + return true; // We sent something. + return false; // We probably didn't. +} +#endif // SEND_PRONTO + +#if SEND_RAW +// Parse an IRremote Raw Hex String/code and send it. +// Args: +// irsend: A ptr to the IRsend object to transmit via. +// str: A comma-separated String containing the freq and raw IR data. +// e.g. "38000,9000,4500,600,1450,600,900,650,1500,..." +// Requires at least two comma-separated values. +// First value is the transmission frequency in Hz or kHz. +// Returns: +// bool: Successfully sent or not. +bool parseStringAndSendRaw(IRsend *irsend, const String str) { + uint16_t count; + uint16_t freq = 38000; // Default to 38kHz. + uint16_t *raw_array; + + // Find out how many items there are in the string. + count = countValuesInStr(str, ','); + + // We expect the frequency as the first comma separated value, so we need at + // least two values. If not, bail out. + if (count < 2) return false; + count--; // We don't count the frequency value as part of the raw array. + + // Now we know how many there are, allocate the memory to store them all. + raw_array = newCodeArray(count); + + // Grab the first value from the string, as it is the frequency. + int16_t index = str.indexOf(',', 0); + freq = str.substring(0, index).toInt(); + uint16_t start_from = index + 1; + // Rest of the string are values for the raw array. + // Now convert the strings to integers and place them in raw_array. + count = 0; + do { + index = str.indexOf(',', start_from); + raw_array[count] = str.substring(start_from, index).toInt(); + start_from = index + 1; + count++; + } while (index != -1); + + irsend->sendRaw(raw_array, count, freq); // All done. Send it. + free(raw_array); // Free up the memory allocated. + if (count > 0) + return true; // We sent something. + return false; // We probably didn't. +} +#endif // SEND_RAW + +// Parse the URL args to find the IR code. +void handleIr(void) { +#if HTML_PASSWORD_ENABLE + if (!server.authenticate(HttpUsername, HttpPassword)) { + debug("Basic HTTP authentication failure for /ir."); + return server.requestAuthentication(); + } +#endif + uint64_t data = 0; + String data_str = ""; + int16_t ir_type = decode_type_t::NEC; // Default to NEC codes. + uint16_t nbits = 0; + uint16_t repeat = 0; + + for (uint16_t i = 0; i < server.args(); i++) { + if (server.argName(i).equals(KEY_TYPE) || + server.argName(i).equals(KEY_PROTOCOL)) { + ir_type = strToDecodeType(server.arg(i).c_str()); + } else if (server.argName(i).equals(KEY_CODE)) { + data = getUInt64fromHex(server.arg(i).c_str()); + data_str = server.arg(i); + } else if (server.argName(i).equals(KEY_BITS)) { + nbits = server.arg(i).toInt(); + } else if (server.argName(i).equals(KEY_REPEAT)) { + repeat = server.arg(i).toInt(); + } + } + debug("New code received via HTTP"); + lastSendSucceeded = sendIRCode(IrSendTable[0], ir_type, data, + data_str.c_str(), nbits, repeat); + String html = F( + "Send IR command" + "" + "

IR command sent!

"); + html += addJsReloadUrl("/", 2, true); + html += F(""); + server.send(200, "text/html", html); +} + +void handleNotFound(void) { + String message = "File Not Found\n\n"; + message += "URI: "; + message += server.uri(); + message += "\nMethod: "; + message += (server.method() == HTTP_GET)?"GET":"POST"; + message += "\nArguments: "; + message += server.args(); + message += "\n"; + for (uint8_t i=0; i < server.args(); i++) + message += " " + server.argName(i) + ": " + server.arg(i) + "\n"; + server.send(404, "text/plain", message); +} + +void setup_wifi(void) { + delay(10); + loadWifiConfigFile(); + // We start by connecting to a WiFi network + wifiManager.setTimeout(300); // Time out after 5 mins. + // Set up additional parameters for WiFiManager config menu page. + wifiManager.setSaveConfigCallback(saveWifiConfigCallback); + WiFiManagerParameter custom_hostname_text( + "
Hostname
"); + wifiManager.addParameter(&custom_hostname_text); + WiFiManagerParameter custom_hostname( + kHostnameKey, kHostnameKey, Hostname, kHostnameLength); + wifiManager.addParameter(&custom_hostname); + WiFiManagerParameter custom_authentication_text( + "

Web/OTA authentication
"); + wifiManager.addParameter(&custom_authentication_text); + WiFiManagerParameter custom_http_username( + kHttpUserKey, "username", HttpUsername, kUsernameLength); + wifiManager.addParameter(&custom_http_username); + WiFiManagerParameter custom_http_password( + kHttpPassKey, "password (No OTA if blank)", HttpPassword, kPasswordLength, + " type='password'"); + wifiManager.addParameter(&custom_http_password); +#if MQTT_ENABLE + WiFiManagerParameter custom_mqtt_text( + "

MQTT Broker details
"); + wifiManager.addParameter(&custom_mqtt_text); + WiFiManagerParameter custom_mqtt_server( + kMqttServerKey, "mqtt server", MqttServer, kHostnameLength); + wifiManager.addParameter(&custom_mqtt_server); + WiFiManagerParameter custom_mqtt_port( + kMqttPortKey, "mqtt port", MqttPort, kPortLength, + " input type='number' min='1' max='65535'"); + wifiManager.addParameter(&custom_mqtt_port); + WiFiManagerParameter custom_mqtt_user( + kMqttUserKey, "mqtt username", MqttUsername, kUsernameLength); + wifiManager.addParameter(&custom_mqtt_user); + WiFiManagerParameter custom_mqtt_pass( + kMqttPassKey, "mqtt password", MqttPassword, kPasswordLength, + " type='password'"); + wifiManager.addParameter(&custom_mqtt_pass); + WiFiManagerParameter custom_prefix_text( + "

MQTT Prefix
"); + wifiManager.addParameter(&custom_prefix_text); + WiFiManagerParameter custom_mqtt_prefix( + kMqttPrefixKey, "Leave empty to use Hostname", MqttPrefix, + kHostnameLength); + wifiManager.addParameter(&custom_mqtt_prefix); + #endif // MQTT_ENABLE +#if USE_STATIC_IP + // Use a static IP config rather than the one supplied via DHCP. + wifiManager.setSTAStaticIPConfig(kIPAddress, kGateway, kSubnetMask); +#endif // USE_STATIC_IP +#if MIN_SIGNAL_STRENGTH + wifiManager.setMinimumSignalQuality(MIN_SIGNAL_STRENGTH); +#endif // MIN_SIGNAL_STRENGTH + wifiManager.setRemoveDuplicateAPs(HIDE_DUPLIATE_NETWORKS); + + if (!wifiManager.autoConnect()) { + debug("Wifi failed to connect and hit timeout. Rebooting..."); + delay(3000); + // Reboot. A.k.a. "Have you tried turning it Off and On again?" + ESP.reset(); + delay(5000); + } + +#if MQTT_ENABLE + strncpy(MqttServer, custom_mqtt_server.getValue(), kHostnameLength); + strncpy(MqttPort, custom_mqtt_port.getValue(), kPortLength); + strncpy(MqttUsername, custom_mqtt_user.getValue(), kUsernameLength); + strncpy(MqttPassword, custom_mqtt_pass.getValue(), kPasswordLength); + strncpy(MqttPrefix, custom_mqtt_prefix.getValue(), kHostnameLength); +#endif // MQTT_ENABLE + strncpy(Hostname, custom_hostname.getValue(), kHostnameLength); + strncpy(HttpUsername, custom_http_username.getValue(), kUsernameLength); + strncpy(HttpPassword, custom_http_password.getValue(), kPasswordLength); + if (flagSaveWifiConfig) { + saveWifiConfig(); + } + debug("WiFi connected. IP address:"); + debug(WiFi.localIP().toString().c_str()); +} + +void init_vars(void) { +#if MQTT_ENABLE + // If we have a prefix already, use it. Otherwise use the hostname. + if (!strlen(MqttPrefix)) strncpy(MqttPrefix, Hostname, kHostnameLength); + // Topic we send back acknowledgements on. + MqttAck = String(MqttPrefix) + '/' + MQTT_ACK; + // Sub-topic we get new commands from. + MqttSend = String(MqttPrefix) + '/' + MQTT_SEND; + // Topic we send received IRs to. + MqttRecv = String(MqttPrefix) + '/' + MQTT_RECV; + // Topic we send log messages to. + MqttLog = String(MqttPrefix) + '/' + MQTT_LOG; + // Topic for the Last Will & Testament. + MqttLwt = String(MqttPrefix) + '/' + MQTT_LWT; + // Sub-topic for the climate topics. + MqttClimate = String(MqttPrefix) + '/' + MQTT_CLIMATE; + // Sub-topic for the climate command topics. + MqttClimateCmnd = MqttClimate + '/' + MQTT_CLIMATE_CMND + '/'; + // Sub-topic for the climate stat topics. + MqttClimateStat = MqttClimate + '/' + MQTT_CLIMATE_STAT + '/'; + MqttDiscovery = "homeassistant/climate/" + String(Hostname) + "/config"; + MqttHAName = String(Hostname) + "_aircon"; + // Create a unique MQTT client id. + MqttClientId = String(Hostname) + String(ESP.getChipId(), HEX); +#endif // MQTT_ENABLE +} + +void setup(void) { + // Set the default climate settings. + climate.protocol = decode_type_t::UNKNOWN; + climate.model = -1; // Unknown. + climate.power = false; + climate.mode = stdAc::opmode_t::kAuto; + climate.celsius = true; + climate.degrees = 25; // 25C + climate.fanspeed = stdAc::fanspeed_t::kAuto; + climate.swingv = stdAc::swingv_t::kAuto; + climate.swingh = stdAc::swingh_t::kAuto; + climate.quiet = false; + climate.turbo = false; + climate.econo = false; + climate.light = false; + climate.filter = false; + climate.clean = false; + climate.beep = false; + climate.sleep = -1; // Off + climate.clock = -1; // Don't set. + climate_prev = climate; + + // Initialise all the IR transmitters. + for (uint8_t i = 0; i < kSendTableSize; i++) { + IrSendTable[i] = new IRsend(gpioTable[i]); + IrSendTable[i]->begin(); + offset = IrSendTable[i]->calibrate(); + } +#ifdef IR_RX +#if IR_RX_PULLUP + pinMode(IR_RX, INPUT_PULLUP); +#endif // IR_RX_PULLUP +#if DECODE_HASH + // Ignore messages with less than minimum on or off pulses. + irrecv.setUnknownThreshold(kMinUnknownSize); +#endif // DECODE_HASH + irrecv.enableIRIn(); // Start the receiver +#endif // IR_RX + +#if DEBUG + if (!isSerialGpioUsedByIr()) { + // Use SERIAL_TX_ONLY so that the RX pin can be freed up for GPIO/IR use. + Serial.begin(BAUD_RATE, SERIAL_8N1, SERIAL_TX_ONLY); + while (!Serial) // Wait for the serial connection to be establised. + delay(50); + Serial.println(); + debug("IRMQTTServer " _MY_VERSION_" has booted."); + } +#endif // DEBUG + + setup_wifi(); + + // Wait a bit for things to settle. + delay(500); + + lastReconnectAttempt = 0; + + if (mdns.begin(Hostname, WiFi.localIP())) { + debug("MDNS responder started"); + } + + // Setup the root web page. + server.on("/", handleRoot); + // Setup the examples web page. + server.on("/examples", handleExamples); + // Setup the page to handle web-based IR codes. + server.on("/ir", handleIr); + // Setup the aircon page. + server.on("/aircon", handleAirCon); + // Setup the aircon update page. + server.on("/aircon/set", handleAirConSet); + // Setup the info page. + server.on("/info", handleInfo); + // Setup the admin page. + server.on("/admin", handleAdmin); + // Setup a reset page to cause WiFiManager information to be reset. + server.on("/reset", handleReset); + // Reboot url + server.on("/quitquitquit", handleReboot); +#if MQTT_ENABLE + // MQTT Discovery url + server.on("/send_discovery", handleSendMqttDiscovery); + // Finish setup of the mqtt clent object. + mqtt_client.setServer(MqttServer, atoi(MqttPort)); + mqtt_client.setCallback(mqttCallback); + // Set various variables + init_vars(); +#endif // MQTT_ENABLE + +#if FIRMWARE_OTA + // Setup the URL to allow Over-The-Air (OTA) firmware updates. + if (strlen(HttpPassword)) { // Allow if password is set. + server.on("/update", HTTP_POST, [](){ +#if MQTT_ENABLE + mqttLog("Attempting firmware update & reboot"); + delay(1000); +#endif // MQTT_ENABLE + server.send(200, "text/html", + "Updating firmware." + "" + "

Updating firmware

" + "
" + "

Warning! Don't power off the device for 60 seconds!

" + "

The firmware is uploading and will try to flash itself. " + "It is important to not interrupt the process.

" + "

The firmware upload seems to have " + + String(Update.hasError() ? "FAILED!" : "SUCCEEDED!") + + " Rebooting!

" + + addJsReloadUrl("/", 20, true) + + ""); + delay(1000); + ESP.restart(); + delay(1000); + }, [](){ + if (!server.authenticate(HttpUsername, HttpPassword)) { + debug("Basic HTTP authentication failure for /update."); + return server.requestAuthentication(); + } + HTTPUpload& upload = server.upload(); + if (upload.status == UPLOAD_FILE_START) { + WiFiUDP::stopAll(); + debug("Update:"); + debug(upload.filename.c_str()); + uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & + 0xFFFFF000; + if (!Update.begin(maxSketchSpace)) { // start with max available size +#if DEBUG + if (!isSerialGpioUsedByIr()) + Update.printError(Serial); +#endif // DEBUG + } + } else if (upload.status == UPLOAD_FILE_WRITE) { + if (Update.write(upload.buf, upload.currentSize) != + upload.currentSize) { +#if DEBUG + if (!isSerialGpioUsedByIr()) + Update.printError(Serial); +#endif // DEBUG + } + } else if (upload.status == UPLOAD_FILE_END) { + // true to set the size to the current progress + if (Update.end(true)) { + debug("Update Success:"); + debug(String(upload.totalSize).c_str()); + debug("Rebooting..."); + } + } + yield(); + }); + } +#endif // FIRMWARE_OTA + + // Set up an error page. + server.onNotFound(handleNotFound); + + server.begin(); + debug("HTTP server started"); +} + +#if MQTT_ENABLE +// MQTT subscribing to topic +void subscribing(const String topic_name) { + // subscription to topic for receiving data with QoS. + if (mqtt_client.subscribe(topic_name.c_str(), QOS)) + debug("Subscription OK to:"); + else + debug("Subscription FAILED to:"); + debug(topic_name.c_str()); +} + +// Un-subscribe from a MQTT topic +void unsubscribing(const String topic_name) { + // subscription to topic for receiving data with QoS. + if (mqtt_client.unsubscribe(topic_name.c_str())) + debug("Unsubscribed OK from:"); + else + debug("FAILED to unsubscribe from:"); + debug(topic_name.c_str()); +} + +void mqttLog(const String mesg) { + debug(mesg.c_str()); + mqtt_client.publish(MqttLog.c_str(), mesg.c_str()); + mqttSentCounter++; +} + +bool reconnect(void) { + // Loop a few times or until we're reconnected + uint16_t tries = 1; + while (!mqtt_client.connected() && tries <= 3) { + int connected = false; + // Attempt to connect + debug(("Attempting MQTT connection to " + String(MqttServer) + ":" + + String(MqttPort) + "... ").c_str()); + if (strcmp(MqttUsername, "") && strcmp(MqttPassword, "")) { + debug("Using mqtt username/password to connect."); + connected = mqtt_client.connect(MqttClientId.c_str(), + MqttUsername, MqttPassword, + MqttLwt.c_str(), + QOS, true, kLwtOffline); + + } else { + debug("Using password-less mqtt connection."); + connected = mqtt_client.connect(MqttClientId.c_str(), MqttLwt.c_str(), + QOS, true, kLwtOffline); + } + if (connected) { + // Once connected, publish an announcement... + mqttLog("(Re)Connected."); + + // Update Last Will & Testament to say we are back online. + mqtt_client.publish(MqttLwt.c_str(), kLwtOnline, true); + mqttSentCounter++; + + // Subscribing to topic(s) + subscribing(MqttSend); + for (uint8_t i = 0; i < kSendTableSize; i++) { + subscribing(MqttSend + '_' + String(static_cast(i))); + } + // Climate command topics. + subscribing(MqttClimateCmnd + '+'); + } else { + debug(("failed, rc=" + String(mqtt_client.state()) + + " Try again in a bit.").c_str()); + // Wait for a bit before retrying + delay(tries << 7); // Linear increasing back-off (x128) + } + tries++; + } + return mqtt_client.connected(); +} + +// Return a string containing the comma separated list of MQTT command topics. +String listOfCommandTopics(void) { + String result = MqttSend; + for (uint16_t i = 0; i < kSendTableSize; i++) { + result += ", " + MqttSend + '_' + String(i); + } + return result; +} + +// MQTT Discovery web page +void handleSendMqttDiscovery(void) { +#if HTML_PASSWORD_ENABLE + if (!server.authenticate(HttpUsername, HttpPassword)) { + debug("Basic HTTP authentication failure for /send_discovery."); + return server.requestAuthentication(); + } +#endif // HTML_PASSWORD_ENABLE + server.send(200, "text/html", + "Sending MQTT Discovery message" + "" + "

Sending MQTT Discovery message.

" + + htmlMenu() + + "

The Home Assistant MQTT Discovery message is being sent to topic: " + + MqttDiscovery + ". It will show up in Home Assistant in a few seconds." + "

" + "

Warning!

" + "

Home Assistant's config for this device is reset each time this is " + " is sent.

" + + addJsReloadUrl("/", 15, true) + + ""); + sendMQTTDiscovery(MqttDiscovery.c_str()); +} + +void doBroadcast(TimerMs *timer, const uint32_t interval, + const commonAcState_t state, const bool retain, + const bool force) { + if (force || (!lockMqttBroadcast && timer->elapsed() > interval)) { + debug("Sending MQTT stat update broadcast."); + sendClimate(state, state, MqttClimateStat, + retain, true, false); + timer->reset(); // It's been sent, so reset the timer. + hasBroadcastBeenSent = true; + } +} + +void receivingMQTT(String const topic_name, String const callback_str) { + char* tok_ptr; + uint64_t code = 0; + uint16_t nbits = 0; + uint16_t repeat = 0; + uint8_t channel = 0; // Default to the first channel. e.g. "*_0" + + debug("Receiving data by MQTT topic:"); + debug(topic_name.c_str()); + debug("with payload:"); + debug(callback_str.c_str()); + // Save the message as the last command seen (global). + lastMqttCmdTopic = topic_name; + lastMqttCmd = callback_str; + lastMqttCmdTime = millis(); + mqttRecvCounter++; + + if (topic_name.startsWith(MqttClimate)) { + if (topic_name.startsWith(MqttClimateCmnd)) { + debug("It's a climate command topic"); + commonAcState_t updated = updateClimate( + climate, topic_name, MqttClimateCmnd, callback_str); + sendClimate(climate, updated, MqttClimateStat, + true, false, false); + climate = updated; + } else if (topic_name.startsWith(MqttClimateStat)) { + debug("It's a climate state topic. Update internal state and DON'T send"); + climate = updateClimate( + climate, topic_name, MqttClimateStat, callback_str); + } + return; // We are done for now. + } + // Check if a specific channel was requested by looking for a "*_[0-9]" suffix + for (uint8_t i = 0; i < kSendTableSize; i++) { + debug(("Checking if " + topic_name + " ends with _" + String(i)).c_str()); + if (topic_name.endsWith("_" + String(i))) { + channel = i; + debug("It does!"); + break; + } + } + + debug(("Using transmit channel " + String(static_cast(channel)) + + " / GPIO " + String(static_cast(gpioTable[channel]))).c_str()); + // Make a copy of the callback string as strtok destroys it. + char* callback_c_str = strdup(callback_str.c_str()); + debug("MQTT Payload (raw):"); + debug(callback_c_str); + + // Get the numeric protocol type. + int ir_type = strtoul(strtok_r(callback_c_str, ",", &tok_ptr), NULL, 10); + char* next = strtok_r(NULL, ",", &tok_ptr); + // If there is unparsed string left, try to convert it assuming it's hex. + if (next != NULL) { + code = getUInt64fromHex(next); + next = strtok_r(NULL, ",", &tok_ptr); + } else { + // We require at least two value in the string. Give up. + return; + } + // If there is still string left, assume it is the bit size. + if (next != NULL) { + nbits = atoi(next); + next = strtok_r(NULL, ",", &tok_ptr); + } + // If there is still string left, assume it is the repeat count. + if (next != NULL) + repeat = atoi(next); + + free(callback_c_str); + + // send received MQTT value by IR signal + lastSendSucceeded = sendIRCode( + IrSendTable[channel], ir_type, code, + callback_str.substring(callback_str.indexOf(",") + 1).c_str(), + nbits, repeat); +} + +// Callback function, when we receive an MQTT value on the topics +// subscribed this function is called +void mqttCallback(char* topic, byte* payload, unsigned int length) { + // In order to republish this payload, a copy must be made + // as the orignal payload buffer will be overwritten whilst + // constructing the PUBLISH packet. + // Allocate the correct amount of memory for the payload copy + byte* payload_copy = reinterpret_cast(malloc(length + 1)); + // Copy the payload to the new buffer + memcpy(payload_copy, payload, length); + + // Conversion to a printable string + payload_copy[length] = '\0'; + String callback_string = String(reinterpret_cast(payload_copy)); + String topic_name = String(reinterpret_cast(topic)); + + // launch the function to treat received data + receivingMQTT(topic_name, callback_string); + + // Free the memory + free(payload_copy); +} + +void sendMQTTDiscovery(const char *topic) { + if (mqtt_client.publish( + topic, String( + "{" + "\"~\":\"" + MqttClimate + "\"," + "\"name\":\"" + MqttHAName + "\"," + "\"pow_cmd_t\":\"~" MQTT_CLIMATE "/" MQTT_CLIMATE_CMND "/" KEY_POWER "\"," + "\"mode_cmd_t\":\"~" MQTT_CLIMATE "/" MQTT_CLIMATE_CMND "/" KEY_MODE "\"," + "\"mode_stat_t\":\"~" MQTT_CLIMATE "/" MQTT_CLIMATE_STAT "/" KEY_MODE + "\"," + "\"modes\":[\"off\",\"auto\",\"cool\",\"heat\",\"dry\",\"fan_only\"]," + "\"temp_cmd_t\":\"~" MQTT_CLIMATE "/" MQTT_CLIMATE_CMND "/" KEY_TEMP "\"," + "\"temp_stat_t\":\"~" MQTT_CLIMATE "/" MQTT_CLIMATE_STAT "/" KEY_TEMP + "\"," + "\"min_temp\":\"16\"," + "\"max_temp\":\"30\"," + "\"temp_step\":\"1\"," + "\"fan_mode_cmd_t\":\"~" MQTT_CLIMATE "/" MQTT_CLIMATE_CMND "/" + KEY_FANSPEED "\"," + "\"fan_mode_stat_t\":\"~" MQTT_CLIMATE "/" MQTT_CLIMATE_STAT "/" + KEY_FANSPEED "\"," + "\"fan_modes\":[\"auto\",\"min\",\"low\",\"medium\",\"high\",\"max\"]," + "\"swing_mode_cmd_t\":\"~" MQTT_CLIMATE "/" MQTT_CLIMATE_CMND "/" + KEY_SWINGV "\"," + "\"swing_mode_stat_t\":\"~" MQTT_CLIMATE "/" MQTT_CLIMATE_STAT "/" + KEY_SWINGV "\"," + "\"swing_modes\":[" + "\"off\",\"auto\",\"highest\",\"high\",\"middle\",\"low\",\"lowest\"" + "]" + "}").c_str())) { + mqttLog("MQTT climate discovery successful sent."); + hasDiscoveryBeenSent = true; + lastDiscovery.reset(); + mqttSentCounter++; + } else { + mqttLog("MQTT climate discovery FAILED to send."); + } +} +#endif // MQTT_ENABLE + +void loop(void) { + server.handleClient(); // Handle any web activity + +#if MQTT_ENABLE + uint32_t now = millis(); + // MQTT client connection management + if (!mqtt_client.connected()) { + if (wasConnected) { + lastDisconnectedTime = now; + wasConnected = false; + mqttDisconnectCounter++; + } + // Reconnect if it's longer than kMqttReconnectTime since we last tried. + if (now - lastReconnectAttempt > kMqttReconnectTime) { + lastReconnectAttempt = now; + debug("client mqtt not connected, trying to connect"); + // Attempt to reconnect + if (reconnect()) { + lastReconnectAttempt = 0; + wasConnected = true; + if (boot) { + mqttLog("IR Server just booted"); + boot = false; + } else { + mqttLog("IR Server just (re)connected to MQTT. " + "Lost connection about " + timeSince(lastConnectedTime)); + } + lastConnectedTime = now; + debug("successful client mqtt connection"); + if (lockMqttBroadcast) { + // Attempt to fetch back any Climate state stored in MQTT retained + // messages on the MQTT broker. + mqttLog("Started listening for previous state."); + climate_prev = climate; // Make a copy so we can compare afterwards. + subscribing(MqttClimateStat + '+'); + statListenTime.reset(); + } + } + } + } else { + // MQTT loop + lastConnectedTime = now; + mqtt_client.loop(); + if (lockMqttBroadcast && statListenTime.elapsed() > kStatListenPeriodMs) { + unsubscribing(MqttClimateStat + '+'); + mqttLog("Finished listening for previous state."); + if (cmpClimate(climate, climate_prev)) { // Something changed. + mqttLog("The state was recovered from MQTT broker. Updating."); + sendClimate(climate_prev, climate, MqttClimateStat, + true, false, false); + } + lockMqttBroadcast = false; // Release the lock so we can broadcast again. + } + // Periodically send all of the climate state via MQTT. + doBroadcast(&lastBroadcast, kBroadcastPeriodMs, climate, false, false); + } +#endif // MQTT_ENABLE +#ifdef IR_RX + // Check if an IR code has been received via the IR RX module. +#if REPORT_UNKNOWNS + if (irrecv.decode(&capture)) { +#else // REPORT_UNKNOWNS + if (irrecv.decode(&capture) && capture.decode_type != UNKNOWN) { +#endif // REPORT_UNKNOWNS + lastIrReceivedTime = millis(); + lastIrReceived = String(capture.decode_type) + "," + + resultToHexidecimal(&capture); +#if REPORT_RAW_UNKNOWNS + if (capture.decode_type == UNKNOWN) { + lastIrReceived += ";"; + for (uint16_t i = 1; i < capture.rawlen; i++) { + uint32_t usecs; + for (usecs = capture.rawbuf[i] * kRawTick; usecs > UINT16_MAX; + usecs -= UINT16_MAX) { + lastIrReceived += uint64ToString(UINT16_MAX); + lastIrReceived += ",0,"; + } + lastIrReceived += uint64ToString(usecs, 10); + if (i < capture.rawlen - 1) + lastIrReceived += ","; + } + } +#endif // REPORT_RAW_UNKNOWNS + // If it isn't an AC code, add the bits. + if (!hasACState(capture.decode_type)) + lastIrReceived += "," + String(capture.bits); +#if MQTT_ENABLE + mqtt_client.publish(MqttRecv.c_str(), lastIrReceived.c_str()); + mqttSentCounter++; +#endif // MQTT_ENABLE + irRecvCounter++; + debug("Incoming IR message sent to MQTT:"); + debug(lastIrReceived.c_str()); + } +#endif // IR_RX + delay(100); +} + +// Arduino framework doesn't support strtoull(), so make our own one. +uint64_t getUInt64fromHex(char const *str) { + uint64_t result = 0; + uint16_t offset = 0; + // Skip any leading '0x' or '0X' prefix. + if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) + offset = 2; + for (; isxdigit((unsigned char)str[offset]); offset++) { + char c = str[offset]; + result *= 16; + if (isdigit(c)) /* '0' .. '9' */ + result += c - '0'; + else if (isupper(c)) /* 'A' .. 'F' */ + result += c - 'A' + 10; + else /* 'a' .. 'f'*/ + result += c - 'a' + 10; + } + return result; +} + +// Transmit the given IR message. +// +// Args: +// irsend: A pointer to a IRsend object to transmit via. +// ir_type: enum of the protocol to be sent. +// code: Numeric payload of the IR message. Most protocols use this. +// code_str: The unparsed code to be sent. Used by complex protocol encodings. +// bits: Nr. of bits in the protocol. 0 means use the protocol's default. +// repeat: Nr. of times the message is to be repeated. (Not all protcols.) +// Returns: +// bool: Successfully sent or not. +bool sendIRCode(IRsend *irsend, int const ir_type, + uint64_t const code, char const * code_str, uint16_t bits, + uint16_t repeat) { + // Create a pseudo-lock so we don't try to send two codes at the same time. + while (lockIr) + delay(20); + lockIr = true; + + bool success = true; // Assume success. + + // send the IR message. + switch (ir_type) { +#if SEND_RC5 + case RC5: // 1 + if (bits == 0) + bits = kRC5Bits; + irsend->sendRC5(code, bits, repeat); + break; +#endif +#if SEND_RC6 + case RC6: // 2 + if (bits == 0) + bits = kRC6Mode0Bits; + irsend->sendRC6(code, bits, repeat); + break; +#endif +#if SEND_NEC + case NEC: // 3 + if (bits == 0) + bits = kNECBits; + irsend->sendNEC(code, bits, repeat); + break; +#endif +#if SEND_SONY + case SONY: // 4 + if (bits == 0) + bits = kSony12Bits; + repeat = std::max(repeat, kSonyMinRepeat); + irsend->sendSony(code, bits, repeat); + break; +#endif +#if SEND_PANASONIC + case PANASONIC: // 5 + if (bits == 0) + bits = kPanasonicBits; + irsend->sendPanasonic64(code, bits, repeat); + break; +#endif +#if SEND_JVC + case JVC: // 6 + if (bits == 0) + bits = kJvcBits; + irsend->sendJVC(code, bits, repeat); + break; +#endif +#if SEND_SAMSUNG + case SAMSUNG: // 7 + if (bits == 0) + bits = kSamsungBits; + irsend->sendSAMSUNG(code, bits, repeat); + break; +#endif +#if SEND_SAMSUNG36 + case SAMSUNG36: // 56 + if (bits == 0) + bits = kSamsung36Bits; + irsend->sendSamsung36(code, bits, repeat); + break; +#endif +#if SEND_WHYNTER + case WHYNTER: // 8 + if (bits == 0) + bits = kWhynterBits; + irsend->sendWhynter(code, bits, repeat); + break; +#endif +#if SEND_AIWA_RC_T501 + case AIWA_RC_T501: // 9 + if (bits == 0) + bits = kAiwaRcT501Bits; + repeat = std::max(repeat, kAiwaRcT501MinRepeats); + irsend->sendAiwaRCT501(code, bits, repeat); + break; +#endif +#if SEND_LG + case LG: // 10 + if (bits == 0) + bits = kLgBits; + irsend->sendLG(code, bits, repeat); + break; +#endif +#if SEND_MITSUBISHI + case MITSUBISHI: // 12 + if (bits == 0) + bits = kMitsubishiBits; + repeat = std::max(repeat, kMitsubishiMinRepeat); + irsend->sendMitsubishi(code, bits, repeat); + break; +#endif +#if SEND_DISH + case DISH: // 13 + if (bits == 0) + bits = kDishBits; + repeat = std::max(repeat, kDishMinRepeat); + irsend->sendDISH(code, bits, repeat); + break; +#endif +#if SEND_SHARP + case SHARP: // 14 + if (bits == 0) + bits = kSharpBits; + irsend->sendSharpRaw(code, bits, repeat); + break; +#endif +#if SEND_COOLIX + case COOLIX: // 15 + if (bits == 0) + bits = kCoolixBits; + irsend->sendCOOLIX(code, bits, repeat); + break; +#endif + case DAIKIN: // 16 + case DAIKIN2: // 53 + case DAIKIN216: // 61 + case KELVINATOR: // 18 + case MITSUBISHI_AC: // 20 + case GREE: // 24 + case ARGO: // 27 + case TROTEC: // 28 + case TOSHIBA_AC: // 32 + case FUJITSU_AC: // 33 + case HAIER_AC: // 38 + case HAIER_AC_YRW02: // 44 + case HITACHI_AC: // 40 + case HITACHI_AC1: // 41 + case HITACHI_AC2: // 42 + case WHIRLPOOL_AC: // 45 + case SAMSUNG_AC: // 46 + case ELECTRA_AC: // 48 + case PANASONIC_AC: // 49 + case MWM: // 52 + success = parseStringAndSendAirCon(irsend, ir_type, code_str); + break; +#if SEND_DENON + case DENON: // 17 + if (bits == 0) + bits = DENON_BITS; + irsend->sendDenon(code, bits, repeat); + break; +#endif +#if SEND_SHERWOOD + case SHERWOOD: // 19 + if (bits == 0) + bits = kSherwoodBits; + repeat = std::max(repeat, kSherwoodMinRepeat); + irsend->sendSherwood(code, bits, repeat); + break; +#endif +#if SEND_RCMM + case RCMM: // 21 + if (bits == 0) + bits = kRCMMBits; + irsend->sendRCMM(code, bits, repeat); + break; +#endif +#if SEND_SANYO + case SANYO_LC7461: // 22 + if (bits == 0) + bits = kSanyoLC7461Bits; + irsend->sendSanyoLC7461(code, bits, repeat); + break; +#endif +#if SEND_RC5 + case RC5X: // 23 + if (bits == 0) + bits = kRC5XBits; + irsend->sendRC5(code, bits, repeat); + break; +#endif +#if SEND_PRONTO + case PRONTO: // 25 + success = parseStringAndSendPronto(irsend, code_str, repeat); + break; +#endif +#if SEND_NIKAI + case NIKAI: // 29 + if (bits == 0) + bits = kNikaiBits; + irsend->sendNikai(code, bits, repeat); + break; +#endif +#if SEND_RAW + case RAW: // 30 + success = parseStringAndSendRaw(irsend, code_str); + break; +#endif +#if SEND_GLOBALCACHE + case GLOBALCACHE: // 31 + success = parseStringAndSendGC(irsend, code_str); + break; +#endif +#if SEND_MIDEA + case MIDEA: // 34 + if (bits == 0) + bits = kMideaBits; + irsend->sendMidea(code, bits, repeat); + break; +#endif +#if SEND_MAGIQUEST + case MAGIQUEST: // 35 + if (bits == 0) + bits = kMagiquestBits; + irsend->sendMagiQuest(code, bits, repeat); + break; +#endif +#if SEND_LASERTAG + case LASERTAG: // 36 + if (bits == 0) + bits = kLasertagBits; + irsend->sendLasertag(code, bits, repeat); + break; +#endif +#if SEND_CARRIER_AC + case CARRIER_AC: // 37 + if (bits == 0) + bits = kCarrierAcBits; + irsend->sendCarrierAC(code, bits, repeat); + break; +#endif +#if SEND_MITSUBISHI2 + case MITSUBISHI2: // 39 + if (bits == 0) + bits = kMitsubishiBits; + repeat = std::max(repeat, kMitsubishiMinRepeat); + irsend->sendMitsubishi2(code, bits, repeat); + break; +#endif +#if SEND_GICABLE + case GICABLE: // 43 + if (bits == 0) + bits = kGicableBits; + repeat = std::max(repeat, kGicableMinRepeat); + irsend->sendGICable(code, bits, repeat); + break; +#endif +#if SEND_LUTRON + case LUTRON: // 47 + if (bits == 0) + bits = kLutronBits; + irsend->sendLutron(code, bits, repeat); + break; +#endif +#if SEND_PIONEER + case PIONEER: // 50 + if (bits == 0) + bits = kPioneerBits; + irsend->sendPioneer(code, bits, repeat); + break; +#endif +#if SEND_LG + case LG2: // 51 + if (bits == 0) + bits = kLgBits; + irsend->sendLG2(code, bits, repeat); + break; +#endif +#if SEND_VESTEL_AC + case VESTEL_AC: // 54 + if (bits == 0) + bits = kVestelAcBits; + irsend->sendVestelAc(code, bits, repeat); + break; +#endif +#if SEND_TECO + case TECO: // 55 + if (bits == 0) + bits = kTecoBits; + irsend->sendTeco(code, bits, repeat); + break; +#endif +#if SEND_LEGOPF + case LEGOPF: // 58 + if (bits == 0) + bits = kLegoPfBits; + irsend->sendLegoPf(code, bits, repeat); + break; +#endif + default: + // If we got here, we didn't know how to send it. + success = false; + } + lastSendTime = millis(); + // Release the lock. + lockIr = false; + + // Indicate that we sent the message or not. + if (success) { + sendReqCounter++; + debug("Sent the IR message:"); + } else { + debug("Failed to send IR Message:"); + } + debug("Type:"); + debug(String(ir_type).c_str()); + // For "long" codes we basically repeat what we got. + if (hasACState((decode_type_t) ir_type) || + ir_type == PRONTO || + ir_type == RAW || + ir_type == GLOBALCACHE) { + debug("Code: "); + debug(code_str); + // Confirm what we were asked to send was sent. +#if MQTT_ENABLE + if (success) { + if (ir_type == PRONTO && repeat > 0) + mqtt_client.publish(MqttAck.c_str(), (String(ir_type) + ",R" + + String(repeat) + "," + + String(code_str)).c_str()); + else + mqtt_client.publish(MqttAck.c_str(), (String(ir_type) + "," + + String(code_str)).c_str()); + mqttSentCounter++; + } +#endif // MQTT_ENABLE + } else { // For "short" codes, we break it down a bit more before we report. + debug(("Code: 0x" + uint64ToString(code, 16)).c_str()); + debug(("Bits: " + String(bits)).c_str()); + debug(("Repeats: " + String(repeat)).c_str()); +#if MQTT_ENABLE + if (success) { + mqtt_client.publish(MqttAck.c_str(), (String(ir_type) + "," + + uint64ToString(code, 16) + + "," + String(bits) + "," + + String(repeat)).c_str()); + mqttSentCounter++; + } +#endif // MQTT_ENABLE + } + return success; +} + +bool sendInt(const String topic, const int32_t num, const bool retain) { +#if MQTT_ENABLE + mqttSentCounter++; + return mqtt_client.publish(topic.c_str(), String(num).c_str(), retain); +#else // MQTT_ENABLE + return true; +#endif // MQTT_ENABLE +} + +bool sendBool(const String topic, const bool on, const bool retain) { +#if MQTT_ENABLE + mqttSentCounter++; + return mqtt_client.publish(topic.c_str(), (on ? "on" : "off"), retain); +#else // MQTT_ENABLE + return true; +#endif // MQTT_ENABLE +} + +bool sendString(const String topic, const String str, const bool retain) { +#if MQTT_ENABLE + mqttSentCounter++; + return mqtt_client.publish(topic.c_str(), str.c_str(), retain); +#else // MQTT_ENABLE + return true; +#endif // MQTT_ENABLE +} + +bool sendFloat(const String topic, const float_t temp, const bool retain) { +#if MQTT_ENABLE + mqttSentCounter++; + return mqtt_client.publish(topic.c_str(), String(temp, 1).c_str(), retain); +#else // MQTT_ENABLE + return true; +#endif // MQTT_ENABLE +} + +commonAcState_t updateClimate(commonAcState_t current, const String str, + const String prefix, const String payload) { + commonAcState_t result = current; + String value = payload; + value.toUpperCase(); + if (str.equals(prefix + KEY_PROTOCOL)) + result.protocol = strToDecodeType(value.c_str()); + else if (str.equals(prefix + KEY_MODEL)) + result.model = IRac::strToModel(value.c_str()); + else if (str.equals(prefix + KEY_POWER)) + result.power = IRac::strToBool(value.c_str()); + else if (str.equals(prefix + KEY_MODE)) + result.mode = IRac::strToOpmode(value.c_str()); + else if (str.equals(prefix + KEY_TEMP)) + result.degrees = value.toFloat(); + else if (str.equals(prefix + KEY_FANSPEED)) + result.fanspeed = IRac::strToFanspeed(value.c_str()); + else if (str.equals(prefix + KEY_SWINGV)) + result.swingv = IRac::strToSwingV(value.c_str()); + else if (str.equals(prefix + KEY_SWINGH)) + result.swingh = IRac::strToSwingH(value.c_str()); + else if (str.equals(prefix + KEY_QUIET)) + result.quiet = IRac::strToBool(value.c_str()); + else if (str.equals(prefix + KEY_TURBO)) + result.turbo = IRac::strToBool(value.c_str()); + else if (str.equals(prefix + KEY_ECONO)) + result.econo = IRac::strToBool(value.c_str()); + else if (str.equals(prefix + KEY_LIGHT)) + result.light = IRac::strToBool(value.c_str()); + else if (str.equals(prefix + KEY_BEEP)) + result.beep = IRac::strToBool(value.c_str()); + else if (str.equals(prefix + KEY_FILTER)) + result.filter = IRac::strToBool(value.c_str()); + else if (str.equals(prefix + KEY_CLEAN)) + result.clean = IRac::strToBool(value.c_str()); + else if (str.equals(prefix + KEY_SLEEP)) + result.sleep = value.toInt(); + else if (str.equals(prefix + KEY_CLOCK)) + result.clock = value.toInt(); + return result; +} + +// Compare two AirCon states (climates). +// Returns: True if they differ, False if they don't. +bool cmpClimate(const commonAcState_t a, const commonAcState_t b) { + return a.protocol != b.protocol || a.model != b.model || a.power != b.power || + a.mode != b.mode || a.degrees != b.degrees || a.celsius != b.celsius || + a.fanspeed != b.fanspeed || a.swingv != b.swingv || + a.swingh != b.swingh || a.quiet != b.quiet || a.turbo != b.turbo || + a.econo != b.econo || a.light != b.light || a.filter != b.filter || + a.clean != b.clean || a.beep != b.beep || a.sleep != b.sleep; +} + +bool sendClimate(const commonAcState_t prev, const commonAcState_t next, + const String topic_prefix, const bool retain, + const bool forceMQTT, const bool forceIR) { + bool diff = false; + bool success = true; + + if (prev.protocol != next.protocol || forceMQTT) { + diff = true; + success &= sendString(topic_prefix + KEY_PROTOCOL, + typeToString(next.protocol), retain); + } + if (prev.model != next.model || forceMQTT) { + diff = true; + success &= sendInt(topic_prefix + KEY_MODEL, next.model, retain); + } + if (prev.power != next.power || prev.mode != next.mode || forceMQTT) { + diff = true; + success &= sendBool(topic_prefix + KEY_POWER, next.power, retain); + success &= sendString(topic_prefix + KEY_MODE, + (next.power ? opmodeToString(next.mode) : F("off")), + retain); + } + if (prev.degrees != next.degrees || forceMQTT) { + diff = true; + success &= sendFloat(topic_prefix + KEY_TEMP, next.degrees, retain); + } + if (prev.celsius != next.celsius || forceMQTT) { + diff = true; + success &= sendBool(topic_prefix + KEY_CELSIUS, next.celsius, retain); + } + if (prev.fanspeed != next.fanspeed || forceMQTT) { + diff = true; + success &= sendString(topic_prefix + KEY_FANSPEED, + fanspeedToString(next.fanspeed), retain); + } + if (prev.swingv != next.swingv || forceMQTT) { + diff = true; + success &= sendString(topic_prefix + KEY_SWINGV, + swingvToString(next.swingv), retain); + } + if (prev.swingh != next.swingh || forceMQTT) { + diff = true; + success &= sendString(topic_prefix + KEY_SWINGH, + swinghToString(next.swingh), retain); + } + if (prev.quiet != next.quiet || forceMQTT) { + diff = true; + success &= sendBool(topic_prefix + KEY_QUIET, next.quiet, retain); + } + if (prev.turbo != next.turbo || forceMQTT) { + diff = true; + success &= sendBool(topic_prefix + KEY_TURBO, next.turbo, retain); + } + if (prev.econo != next.econo || forceMQTT) { + diff = true; + success &= sendBool(topic_prefix + KEY_ECONO, next.econo, retain); + } + if (prev.light != next.light || forceMQTT) { + diff = true; + success &= sendBool(topic_prefix + KEY_LIGHT, next.light, retain); + } + if (prev.filter != next.filter || forceMQTT) { + diff = true; + success &= sendBool(topic_prefix + KEY_FILTER, next.filter, retain); + } + if (prev.clean != next.clean || forceMQTT) { + diff = true; + success &= sendBool(topic_prefix + KEY_CLEAN, next.clean, retain); + } + if (prev.beep != next.beep || forceMQTT) { + diff = true; + success &= sendBool(topic_prefix + KEY_BEEP, next.beep, retain); + } + if (prev.sleep != next.sleep || forceMQTT) { + diff = true; + success &= sendInt(topic_prefix + KEY_SLEEP, next.sleep, retain); + } + if (diff && !forceMQTT) + debug("Difference in common A/C state detected."); + else + debug("NO difference in common A/C state detected."); + // Only send an IR message if we need to. + if ((diff && !forceMQTT) || forceIR) { + debug("Sending common A/C state via IR."); + lastClimateSucceeded = commonAc.sendAc( + next.protocol, next.model, next.power, next.mode, + next.degrees, next.celsius, next.fanspeed, next.swingv, next.swingh, + next.quiet, next.turbo, next.econo, next.light, next.filter, next.clean, + next.beep, next.sleep, -1); + if (lastClimateSucceeded) hasClimateBeenSent = true; + success &= lastClimateSucceeded; + lastClimateIr.reset(); + irClimateCounter++; + sendReqCounter++; + } + return success; +} diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/IRMQTTServer/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/IRMQTTServer/platformio.ini similarity index 55% rename from lib/IRremoteESP8266-2.5.2.03/examples/IRMQTTServer/platformio.ini rename to lib/IRremoteESP8266-2.6.0/examples/IRMQTTServer/platformio.ini index 27b44ddca..243b36a99 100644 --- a/lib/IRremoteESP8266-2.5.2.03/examples/IRMQTTServer/platformio.ini +++ b/lib/IRremoteESP8266-2.6.0/examples/IRMQTTServer/platformio.ini @@ -3,16 +3,19 @@ lib_extra_dirs = ../../ src_dir=. [common] -build_flags = -DMQTT_MAX_PACKET_SIZE=512 +build_flags = -DMQTT_MAX_PACKET_SIZE=768 +lib_ldf_mode = chain+ lib_deps_builtin = lib_deps_external = PubSubClient WifiManager@0.14 + ArduinoJson [env:nodemcuv2] platform = espressif8266 framework = arduino board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} build_flags = ${common.build_flags} lib_deps = ${common.lib_deps_builtin} @@ -22,7 +25,18 @@ lib_deps = platform=espressif8266 framework=arduino board=d1_mini +lib_ldf_mode = ${common.lib_ldf_mode} build_flags = ${common.build_flags} lib_deps = ${common.lib_deps_builtin} ${common.lib_deps_external} + +[env:d1_mini_no_mqtt] +platform=espressif8266 +framework=arduino +board=d1_mini +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} -DMQTT_ENABLE=false +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/IRServer/IRServer.ino b/lib/IRremoteESP8266-2.6.0/examples/IRServer/IRServer.ino similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/examples/IRServer/IRServer.ino rename to lib/IRremoteESP8266-2.6.0/examples/IRServer/IRServer.ino diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/IRServer/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/IRServer/platformio.ini similarity index 83% rename from lib/IRremoteESP8266-2.5.2.03/examples/IRServer/platformio.ini rename to lib/IRremoteESP8266-2.6.0/examples/IRServer/platformio.ini index eeb8d1f2e..ec84f22f3 100644 --- a/lib/IRremoteESP8266-2.5.2.03/examples/IRServer/platformio.ini +++ b/lib/IRremoteESP8266-2.6.0/examples/IRServer/platformio.ini @@ -6,11 +6,13 @@ src_dir=. build_flags = lib_deps_builtin = lib_deps_external = +lib_ldf_mode = chain+ [env:nodemcuv2] platform = espressif8266 framework = arduino board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} build_flags = ${common.build_flags} lib_deps = ${common.lib_deps_builtin} diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/IRrecvDemo/IRrecvDemo.ino b/lib/IRremoteESP8266-2.6.0/examples/IRrecvDemo/IRrecvDemo.ino similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/examples/IRrecvDemo/IRrecvDemo.ino rename to lib/IRremoteESP8266-2.6.0/examples/IRrecvDemo/IRrecvDemo.ino diff --git a/lib/IRremoteESP8266-2.6.0/examples/IRrecvDemo/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/IRrecvDemo/platformio.ini new file mode 100644 index 000000000..ec84f22f3 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/examples/IRrecvDemo/platformio.ini @@ -0,0 +1,19 @@ +[platformio] +lib_extra_dirs = ../../ +src_dir=. + +[common] +build_flags = +lib_deps_builtin = +lib_deps_external = +lib_ldf_mode = chain+ + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/IRrecvDump/IRrecvDump.ino b/lib/IRremoteESP8266-2.6.0/examples/IRrecvDump/IRrecvDump.ino similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/examples/IRrecvDump/IRrecvDump.ino rename to lib/IRremoteESP8266-2.6.0/examples/IRrecvDump/IRrecvDump.ino diff --git a/lib/IRremoteESP8266-2.6.0/examples/IRrecvDump/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/IRrecvDump/platformio.ini new file mode 100644 index 000000000..ec84f22f3 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/examples/IRrecvDump/platformio.ini @@ -0,0 +1,19 @@ +[platformio] +lib_extra_dirs = ../../ +src_dir=. + +[common] +build_flags = +lib_deps_builtin = +lib_deps_external = +lib_ldf_mode = chain+ + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/IRrecvDumpV2/IRrecvDumpV2.ino b/lib/IRremoteESP8266-2.6.0/examples/IRrecvDumpV2/IRrecvDumpV2.ino similarity index 84% rename from lib/IRremoteESP8266-2.5.2.03/examples/IRrecvDumpV2/IRrecvDumpV2.ino rename to lib/IRremoteESP8266-2.6.0/examples/IRrecvDumpV2/IRrecvDumpV2.ino index d72e0814c..2dee0597c 100644 --- a/lib/IRremoteESP8266-2.5.2.03/examples/IRrecvDumpV2/IRrecvDumpV2.ino +++ b/lib/IRremoteESP8266-2.6.0/examples/IRrecvDumpV2/IRrecvDumpV2.ino @@ -35,9 +35,15 @@ #include #include #include +#include #include #include +#include +#include #include +#include +#include + // ==================== start of TUNEABLE PARAMETERS ==================== // An IR detector/demodulator is connected to GPIO pin 14 @@ -121,6 +127,20 @@ void dumpACInfo(decode_results *results) { description = ac.toString(); } #endif // DECODE_DAIKIN +#if DECODE_DAIKIN2 + if (results->decode_type == DAIKIN2) { + IRDaikin2 ac(0); + ac.setRaw(results->state); + description = ac.toString(); + } +#endif // DECODE_DAIKIN2 +#if DECODE_DAIKIN216 + if (results->decode_type == DAIKIN216) { + IRDaikin216 ac(0); + ac.setRaw(results->state); + description = ac.toString(); + } +#endif // DECODE_DAIKIN216 #if DECODE_FUJITSU_AC if (results->decode_type == FUJITSU_AC) { IRFujitsuAC ac(0); @@ -142,6 +162,18 @@ void dumpACInfo(decode_results *results) { description = ac.toString(); } #endif // DECODE_MITSUBISHI_AC +#if DECODE_MITSUBISHIHEAVY + if (results->decode_type == MITSUBISHI_HEAVY_88) { + IRMitsubishiHeavy88Ac ac(0); + ac.setRaw(results->state); + description = ac.toString(); + } + if (results->decode_type == MITSUBISHI_HEAVY_152) { + IRMitsubishiHeavy152Ac ac(0); + ac.setRaw(results->state); + description = ac.toString(); + } +#endif // DECODE_MITSUBISHIHEAVY #if DECODE_TOSHIBA_AC if (results->decode_type == TOSHIBA_AC) { IRToshibaAC ac(0); @@ -180,7 +212,7 @@ void dumpACInfo(decode_results *results) { #if DECODE_SAMSUNG_AC if (results->decode_type == SAMSUNG_AC) { IRSamsungAc ac(0); - ac.setRaw(results->state); + ac.setRaw(results->state, results->bits / 8); description = ac.toString(); } #endif // DECODE_SAMSUNG_AC @@ -206,6 +238,34 @@ void dumpACInfo(decode_results *results) { description = ac.toString(); } #endif // DECODE_HITACHI_AC +#if DECODE_WHIRLPOOL_AC + if (results->decode_type == WHIRLPOOL_AC) { + IRWhirlpoolAc ac(0); + ac.setRaw(results->state); + description = ac.toString(); + } +#endif // DECODE_WHIRLPOOL_AC +#if DECODE_VESTEL_AC + if (results->decode_type == VESTEL_AC) { + IRVestelAc ac(0); + ac.setRaw(results->value); // Like Coolix, use value instead of state. + description = ac.toString(); + } +#endif // DECODE_VESTEL_AC +#if DECODE_TECO + if (results->decode_type == TECO) { + IRTecoAc ac(0); + ac.setRaw(results->value); // Like Coolix, use value instead of state. + description = ac.toString(); + } +#endif // DECODE_TECO +#if DECODE_TCL112AC + if (results->decode_type == TCL112AC) { + IRTcl112Ac ac(0); + ac.setRaw(results->state); + description = ac.toString(); + } +#endif // DECODE_TCL112AC // If we got a human-readable description of the message, display it. if (description != "") Serial.println("Mesg Desc.: " + description); } diff --git a/lib/IRremoteESP8266-2.6.0/examples/IRrecvDumpV2/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/IRrecvDumpV2/platformio.ini new file mode 100644 index 000000000..ec84f22f3 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/examples/IRrecvDumpV2/platformio.ini @@ -0,0 +1,19 @@ +[platformio] +lib_extra_dirs = ../../ +src_dir=. + +[common] +build_flags = +lib_deps_builtin = +lib_deps_external = +lib_ldf_mode = chain+ + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/IRsendDemo/IRsendDemo.ino b/lib/IRremoteESP8266-2.6.0/examples/IRsendDemo/IRsendDemo.ino similarity index 85% rename from lib/IRremoteESP8266-2.5.2.03/examples/IRsendDemo/IRsendDemo.ino rename to lib/IRremoteESP8266-2.6.0/examples/IRsendDemo/IRsendDemo.ino index 892016b3e..19f118671 100644 --- a/lib/IRremoteESP8266-2.5.2.03/examples/IRsendDemo/IRsendDemo.ino +++ b/lib/IRremoteESP8266-2.6.0/examples/IRsendDemo/IRsendDemo.ino @@ -1,6 +1,6 @@ /* IRremoteESP8266: IRsendDemo - demonstrates sending IR codes with IRsend. * - * Version 1.0 April, 2017 + * Version 1.1 January, 2019 * Based on Ken Shirriff's IrsendDemo Version 0.1 July, 2009, * Copyright 2009 Ken Shirriff, http://arcfn.com * @@ -46,6 +46,10 @@ uint16_t rawData[67] = {9000, 4500, 650, 550, 650, 1650, 600, 550, 650, 550, 650, 550, 650, 550, 600, 550, 650, 550, 650, 550, 650, 1650, 600, 550, 650, 1650, 650, 1650, 650, 1650, 650, 1650, 650, 1650, 650, 1650, 600}; +// Example Samsung A/C state captured from IRrecvDumpV2.ino +uint8_t samsungState[kSamsungAcStateLength] = { + 0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0, + 0x01, 0xE2, 0xFE, 0x71, 0x40, 0x11, 0xF0}; void setup() { irsend.begin(); @@ -53,19 +57,16 @@ void setup() { } void loop() { -#if SEND_NEC Serial.println("NEC"); - irsend.sendNEC(0x00FFE01FUL, 32); -#endif // SEND_NEC + irsend.sendNEC(0x00FFE01FUL); delay(2000); -#if SEND_SONY Serial.println("Sony"); - irsend.sendSony(0xa90, 12, 2); -#endif // SEND_SONY + irsend.sendSony(0xa90, 12, 2); // 12 bits & 2 repeats delay(2000); -#if SEND_RAW Serial.println("a rawData capture from IRrecvDumpV2"); irsend.sendRaw(rawData, 67, 38); // Send a raw data capture at 38kHz. -#endif // SEND_RAW + delay(2000); + Serial.println("a Samsung A/C state from IRrecvDumpV2"); + irsend.sendSamsungAC(samsungState); delay(2000); } diff --git a/lib/IRremoteESP8266-2.6.0/examples/IRsendDemo/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/IRsendDemo/platformio.ini new file mode 100644 index 000000000..ec84f22f3 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/examples/IRsendDemo/platformio.ini @@ -0,0 +1,19 @@ +[platformio] +lib_extra_dirs = ../../ +src_dir=. + +[common] +build_flags = +lib_deps_builtin = +lib_deps_external = +lib_ldf_mode = chain+ + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/IRsendProntoDemo/IRsendProntoDemo.ino b/lib/IRremoteESP8266-2.6.0/examples/IRsendProntoDemo/IRsendProntoDemo.ino similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/examples/IRsendProntoDemo/IRsendProntoDemo.ino rename to lib/IRremoteESP8266-2.6.0/examples/IRsendProntoDemo/IRsendProntoDemo.ino diff --git a/lib/IRremoteESP8266-2.6.0/examples/IRsendProntoDemo/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/IRsendProntoDemo/platformio.ini new file mode 100644 index 000000000..ec84f22f3 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/examples/IRsendProntoDemo/platformio.ini @@ -0,0 +1,19 @@ +[platformio] +lib_extra_dirs = ../../ +src_dir=. + +[common] +build_flags = +lib_deps_builtin = +lib_deps_external = +lib_ldf_mode = chain+ + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/JVCPanasonicSendDemo/JVCPanasonicSendDemo.ino b/lib/IRremoteESP8266-2.6.0/examples/JVCPanasonicSendDemo/JVCPanasonicSendDemo.ino similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/examples/JVCPanasonicSendDemo/JVCPanasonicSendDemo.ino rename to lib/IRremoteESP8266-2.6.0/examples/JVCPanasonicSendDemo/JVCPanasonicSendDemo.ino diff --git a/lib/IRremoteESP8266-2.6.0/examples/JVCPanasonicSendDemo/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/JVCPanasonicSendDemo/platformio.ini new file mode 100644 index 000000000..ec84f22f3 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/examples/JVCPanasonicSendDemo/platformio.ini @@ -0,0 +1,19 @@ +[platformio] +lib_extra_dirs = ../../ +src_dir=. + +[common] +build_flags = +lib_deps_builtin = +lib_deps_external = +lib_ldf_mode = chain+ + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/LGACSend/LGACSend.ino b/lib/IRremoteESP8266-2.6.0/examples/LGACSend/LGACSend.ino similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/examples/LGACSend/LGACSend.ino rename to lib/IRremoteESP8266-2.6.0/examples/LGACSend/LGACSend.ino diff --git a/lib/IRremoteESP8266-2.6.0/examples/LGACSend/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/LGACSend/platformio.ini new file mode 100644 index 000000000..ec84f22f3 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/examples/LGACSend/platformio.ini @@ -0,0 +1,19 @@ +[platformio] +lib_extra_dirs = ../../ +src_dir=. + +[common] +build_flags = +lib_deps_builtin = +lib_deps_external = +lib_ldf_mode = chain+ + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/TurnOnArgoAC/TurnOnArgoAC.ino b/lib/IRremoteESP8266-2.6.0/examples/TurnOnArgoAC/TurnOnArgoAC.ino similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/examples/TurnOnArgoAC/TurnOnArgoAC.ino rename to lib/IRremoteESP8266-2.6.0/examples/TurnOnArgoAC/TurnOnArgoAC.ino diff --git a/lib/IRremoteESP8266-2.6.0/examples/TurnOnArgoAC/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/TurnOnArgoAC/platformio.ini new file mode 100644 index 000000000..ec84f22f3 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/examples/TurnOnArgoAC/platformio.ini @@ -0,0 +1,19 @@ +[platformio] +lib_extra_dirs = ../../ +src_dir=. + +[common] +build_flags = +lib_deps_builtin = +lib_deps_external = +lib_ldf_mode = chain+ + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/TurnOnDaikinAC/TurnOnDaikinAC.ino b/lib/IRremoteESP8266-2.6.0/examples/TurnOnDaikinAC/TurnOnDaikinAC.ino similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/examples/TurnOnDaikinAC/TurnOnDaikinAC.ino rename to lib/IRremoteESP8266-2.6.0/examples/TurnOnDaikinAC/TurnOnDaikinAC.ino diff --git a/lib/IRremoteESP8266-2.6.0/examples/TurnOnDaikinAC/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/TurnOnDaikinAC/platformio.ini new file mode 100644 index 000000000..ec84f22f3 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/examples/TurnOnDaikinAC/platformio.ini @@ -0,0 +1,19 @@ +[platformio] +lib_extra_dirs = ../../ +src_dir=. + +[common] +build_flags = +lib_deps_builtin = +lib_deps_external = +lib_ldf_mode = chain+ + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/TurnOnFujitsuAC/TurnOnFujitsuAC.ino b/lib/IRremoteESP8266-2.6.0/examples/TurnOnFujitsuAC/TurnOnFujitsuAC.ino similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/examples/TurnOnFujitsuAC/TurnOnFujitsuAC.ino rename to lib/IRremoteESP8266-2.6.0/examples/TurnOnFujitsuAC/TurnOnFujitsuAC.ino diff --git a/lib/IRremoteESP8266-2.6.0/examples/TurnOnFujitsuAC/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/TurnOnFujitsuAC/platformio.ini new file mode 100644 index 000000000..ec84f22f3 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/examples/TurnOnFujitsuAC/platformio.ini @@ -0,0 +1,19 @@ +[platformio] +lib_extra_dirs = ../../ +src_dir=. + +[common] +build_flags = +lib_deps_builtin = +lib_deps_external = +lib_ldf_mode = chain+ + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino b/lib/IRremoteESP8266-2.6.0/examples/TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/examples/TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino rename to lib/IRremoteESP8266-2.6.0/examples/TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino diff --git a/lib/IRremoteESP8266-2.6.0/examples/TurnOnKelvinatorAC/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/TurnOnKelvinatorAC/platformio.ini new file mode 100644 index 000000000..ec84f22f3 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/examples/TurnOnKelvinatorAC/platformio.ini @@ -0,0 +1,19 @@ +[platformio] +lib_extra_dirs = ../../ +src_dir=. + +[common] +build_flags = +lib_deps_builtin = +lib_deps_external = +lib_ldf_mode = chain+ + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/TurnOnMitsubishiAC/TurnOnMitsubishiAC.ino b/lib/IRremoteESP8266-2.6.0/examples/TurnOnMitsubishiAC/TurnOnMitsubishiAC.ino similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/examples/TurnOnMitsubishiAC/TurnOnMitsubishiAC.ino rename to lib/IRremoteESP8266-2.6.0/examples/TurnOnMitsubishiAC/TurnOnMitsubishiAC.ino diff --git a/lib/IRremoteESP8266-2.6.0/examples/TurnOnMitsubishiAC/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/TurnOnMitsubishiAC/platformio.ini new file mode 100644 index 000000000..ec84f22f3 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/examples/TurnOnMitsubishiAC/platformio.ini @@ -0,0 +1,19 @@ +[platformio] +lib_extra_dirs = ../../ +src_dir=. + +[common] +build_flags = +lib_deps_builtin = +lib_deps_external = +lib_ldf_mode = chain+ + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.6.0/examples/TurnOnMitsubishiHeavyAc/TurnOnMitsubishiHeavyAc.ino b/lib/IRremoteESP8266-2.6.0/examples/TurnOnMitsubishiHeavyAc/TurnOnMitsubishiHeavyAc.ino new file mode 100644 index 000000000..2ad2d7bc3 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/examples/TurnOnMitsubishiHeavyAc/TurnOnMitsubishiHeavyAc.ino @@ -0,0 +1,72 @@ +/* Copyright 2019 David Conran +* +* An IR LED circuit *MUST* be connected to the ESP8266 on a pin +* as specified by kIrLed below. +* +* TL;DR: The IR LED needs to be driven by a transistor for a good result. +* +* Suggested circuit: +* https://github.com/markszabo/IRremoteESP8266/wiki#ir-sending +* +* Common mistakes & tips: +* * Don't just connect the IR LED directly to the pin, it won't +* have enough current to drive the IR LED effectively. +* * Make sure you have the IR LED polarity correct. +* See: https://learn.sparkfun.com/tutorials/polarity/diode-and-led-polarity +* * Typical digital camera/phones can be used to see if the IR LED is flashed. +* Replace the IR LED with a normal LED if you don't have a digital camera +* when debugging. +* * Avoid using the following pins unless you really know what you are doing: +* * Pin 0/D3: Can interfere with the boot/program mode & support circuits. +* * Pin 1/TX/TXD0: Any serial transmissions from the ESP8266 will interfere. +* * Pin 3/RX/RXD0: Any serial transmissions to the ESP8266 will interfere. +* * ESP-01 modules are tricky. We suggest you use a module with more GPIOs +* for your first time. e.g. ESP-12 etc. +*/ +#ifndef UNIT_TEST +#include +#endif +#include +#include +#include + +const uint16_t kIrLed = 4; // ESP8266 GPIO pin to use. Recommended: 4 (D2). +IRMitsubishiHeavy152Ac ac(kIrLed); // Set the GPIO used for sending messages. + +void printState() { + // Display the settings. + Serial.println("Mitsubishi Heavy A/C remote is in the following state:"); + Serial.printf(" %s\n", ac.toString().c_str()); + // Display the encoded IR sequence. + unsigned char* ir_code = ac.getRaw(); + Serial.print("IR Code: 0x"); + for (uint8_t i = 0; i < kMitsubishiHeavy152StateLength; i++) + Serial.printf("%02X", ir_code[i]); + Serial.println(); +} + +void setup() { + ac.begin(); + Serial.begin(115200); + delay(200); + + // Set up what we want to send. See ir_MitsubishiHeavy.(cpp|h) for all the + // options. + Serial.println("Default state of the remote."); + printState(); + Serial.println("Setting desired state for A/C."); + ac.setPower(true); // Turn it on. + ac.setFan(kMitsubishiHeavy152FanMed); // Medium Fan + ac.setMode(kMitsubishiHeavyCool); // Cool mode + ac.setTemp(26); // Celsius + ac.setSwingVertical(kMitsubishiHeavy152SwingVAuto); // Swing vertically + ac.setSwingHorizontal(kMitsubishiHeavy152SwingHMiddle); // Swing Horizontally +} + +void loop() { + // Now send the IR signal. + Serial.println("Sending IR command to A/C ..."); + ac.send(); + printState(); + delay(5000); +} diff --git a/lib/IRremoteESP8266-2.6.0/examples/TurnOnMitsubishiHeavyAc/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/TurnOnMitsubishiHeavyAc/platformio.ini new file mode 100644 index 000000000..ec84f22f3 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/examples/TurnOnMitsubishiHeavyAc/platformio.ini @@ -0,0 +1,19 @@ +[platformio] +lib_extra_dirs = ../../ +src_dir=. + +[common] +build_flags = +lib_deps_builtin = +lib_deps_external = +lib_ldf_mode = chain+ + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.6.0/examples/TurnOnPanasonicAC/TurnOnPanasonicAC.ino b/lib/IRremoteESP8266-2.6.0/examples/TurnOnPanasonicAC/TurnOnPanasonicAC.ino new file mode 100644 index 000000000..ea39ac5e2 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/examples/TurnOnPanasonicAC/TurnOnPanasonicAC.ino @@ -0,0 +1,74 @@ +/* Copyright 2017, 2018 David Conran +* +* An IR LED circuit *MUST* be connected to the ESP8266 on a pin +* as specified by kIrLed below. +* +* TL;DR: The IR LED needs to be driven by a transistor for a good result. +* +* Suggested circuit: +* https://github.com/markszabo/IRremoteESP8266/wiki#ir-sending +* +* Common mistakes & tips: +* * Don't just connect the IR LED directly to the pin, it won't +* have enough current to drive the IR LED effectively. +* * Make sure you have the IR LED polarity correct. +* See: https://learn.sparkfun.com/tutorials/polarity/diode-and-led-polarity +* * Typical digital camera/phones can be used to see if the IR LED is flashed. +* Replace the IR LED with a normal LED if you don't have a digital camera +* when debugging. +* * Avoid using the following pins unless you really know what you are doing: +* * Pin 0/D3: Can interfere with the boot/program mode & support circuits. +* * Pin 1/TX/TXD0: Any serial transmissions from the ESP8266 will interfere. +* * Pin 3/RX/RXD0: Any serial transmissions to the ESP8266 will interfere. +* * ESP-01 modules are tricky. We suggest you use a module with more GPIOs +* for your first time. e.g. ESP-12 etc. +*/ +#ifndef UNIT_TEST +#include +#endif +#include +#include +#include + +const uint16_t kIrLed = 4; // ESP8266 GPIO pin to use. Recommended: 4 (D2). +IRPanasonicAc ac(kIrLed); // Set the GPIO used for sending messages. + +void printState() { + // Display the settings. + Serial.println("Panasonic A/C remote is in the following state:"); + Serial.printf(" %s\n", ac.toString().c_str()); + // Display the encoded IR sequence. + unsigned char* ir_code = ac.getRaw(); + Serial.print("IR Code: 0x"); + for (uint8_t i = 0; i < kPanasonicAcStateLength; i++) + Serial.printf("%02X", ir_code[i]); + Serial.println(); +} + +void setup() { + ac.begin(); + Serial.begin(115200); + delay(200); + + // Set up what we want to send. See ir_Panasonic.cpp for all the options. + Serial.println("Default state of the remote."); + printState(); + Serial.println("Setting desired state for A/C."); + ac.setModel(kPanasonicRkr); + ac.on(); + ac.setFan(kPanasonicAcFanAuto); + ac.setMode(kPanasonicAcCool); + ac.setTemp(26); + ac.setSwingVertical(kPanasonicAcSwingVAuto); + ac.setSwingHorizontal(kPanasonicAcSwingHAuto); +} + +void loop() { + // Now send the IR signal. +#if SEND_PANASONIC_AC + Serial.println("Sending IR command to A/C ..."); + ac.send(); +#endif // SEND_PANASONIC_AC + printState(); + delay(5000); +} diff --git a/lib/IRremoteESP8266-2.6.0/examples/TurnOnPanasonicAC/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/TurnOnPanasonicAC/platformio.ini new file mode 100644 index 000000000..ec84f22f3 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/examples/TurnOnPanasonicAC/platformio.ini @@ -0,0 +1,19 @@ +[platformio] +lib_extra_dirs = ../../ +src_dir=. + +[common] +build_flags = +lib_deps_builtin = +lib_deps_external = +lib_ldf_mode = chain+ + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/TurnOnToshibaAC/TurnOnToshibaAC.ino b/lib/IRremoteESP8266-2.6.0/examples/TurnOnToshibaAC/TurnOnToshibaAC.ino similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/examples/TurnOnToshibaAC/TurnOnToshibaAC.ino rename to lib/IRremoteESP8266-2.6.0/examples/TurnOnToshibaAC/TurnOnToshibaAC.ino diff --git a/lib/IRremoteESP8266-2.6.0/examples/TurnOnToshibaAC/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/TurnOnToshibaAC/platformio.ini new file mode 100644 index 000000000..ec84f22f3 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/examples/TurnOnToshibaAC/platformio.ini @@ -0,0 +1,19 @@ +[platformio] +lib_extra_dirs = ../../ +src_dir=. + +[common] +build_flags = +lib_deps_builtin = +lib_deps_external = +lib_ldf_mode = chain+ + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/TurnOnTrotecAC/TurnOnTrotecAC.ino b/lib/IRremoteESP8266-2.6.0/examples/TurnOnTrotecAC/TurnOnTrotecAC.ino similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/examples/TurnOnTrotecAC/TurnOnTrotecAC.ino rename to lib/IRremoteESP8266-2.6.0/examples/TurnOnTrotecAC/TurnOnTrotecAC.ino diff --git a/lib/IRremoteESP8266-2.6.0/examples/TurnOnTrotecAC/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/TurnOnTrotecAC/platformio.ini new file mode 100644 index 000000000..ec84f22f3 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/examples/TurnOnTrotecAC/platformio.ini @@ -0,0 +1,19 @@ +[platformio] +lib_extra_dirs = ../../ +src_dir=. + +[common] +build_flags = +lib_deps_builtin = +lib_deps_external = +lib_ldf_mode = chain+ + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.5.2.03/keywords.txt b/lib/IRremoteESP8266-2.6.0/keywords.txt similarity index 74% rename from lib/IRremoteESP8266-2.5.2.03/keywords.txt rename to lib/IRremoteESP8266-2.6.0/keywords.txt index ac3f43fe1..a498c5d61 100644 --- a/lib/IRremoteESP8266-2.5.2.03/keywords.txt +++ b/lib/IRremoteESP8266-2.6.0/keywords.txt @@ -22,21 +22,32 @@ IRArgoAC KEYWORD1 IRCoolixAC KEYWORD1 +IRDaikin2 KEYWORD1 +IRDaikin216 KEYWORD1 IRDaikinESP KEYWORD1 IRFujitsuAC KEYWORD1 IRGreeAC KEYWORD1 IRHaierAC KEYWORD1 IRHaierACYRW02 KEYWORD1 +IRHitachiAc KEYWORD1 IRKelvinatorAC KEYWORD1 IRMideaAC KEYWORD1 IRMitsubishiAC KEYWORD1 +IRMitsubishiHeavy152Ac KEYWORD1 +IRMitsubishiHeavy88Ac KEYWORD1 IRPanasonicAc KEYWORD1 IRSamsungAc KEYWORD1 +IRTcl112Ac KEYWORD1 +IRTecoAc KEYWORD1 IRToshibaAC KEYWORD1 IRTrotecESP KEYWORD1 +IRVestelAc KEYWORD1 +IRWhirlpoolAc KEYWORD1 +IRac KEYWORD1 IRrecv KEYWORD1 IRsend KEYWORD1 IRtimer KEYWORD1 +TimerMs KEYWORD1 decode_results KEYWORD1 ir_params_t KEYWORD1 match_result_t KEYWORD1 @@ -46,8 +57,10 @@ match_result_t KEYWORD1 ####################################### _delayMicroseconds KEYWORD2 +_setMode KEYWORD2 +_setTemp KEYWORD2 add KEYWORD2 -addbit KEYWORD2 +argo KEYWORD2 begin KEYWORD2 buildFromState KEYWORD2 buildState KEYWORD2 @@ -60,18 +73,27 @@ calibrate KEYWORD2 cancelOffTimer KEYWORD2 cancelOnTimer KEYWORD2 cancelTimers KEYWORD2 -checkheader KEYWORD2 +checkZjsSig KEYWORD2 +checkZmsSig KEYWORD2 checksum KEYWORD2 -clearBit KEYWORD2 +clearOnTimerFlag KEYWORD2 clearSensorTemp KEYWORD2 +clearSleepTimerFlag KEYWORD2 compare KEYWORD2 +coolix KEYWORD2 copyIrParams KEYWORD2 +countBits KEYWORD2 +daikin KEYWORD2 +daikin2 KEYWORD2 +daikin216 KEYWORD2 decode KEYWORD2 decodeAiwaRCT501 KEYWORD2 decodeCOOLIX KEYWORD2 decodeCarrierAC KEYWORD2 decodeDISH KEYWORD2 decodeDaikin KEYWORD2 +decodeDaikin2 KEYWORD2 +decodeDaikin216 KEYWORD2 decodeDenon KEYWORD2 decodeElectraAC KEYWORD2 decodeFujitsuAC KEYWORD2 @@ -85,6 +107,7 @@ decodeJVC KEYWORD2 decodeKelvinator KEYWORD2 decodeLG KEYWORD2 decodeLasertag KEYWORD2 +decodeLegoPf KEYWORD2 decodeLutron KEYWORD2 decodeMWM KEYWORD2 decodeMagiQuest KEYWORD2 @@ -92,6 +115,7 @@ decodeMidea KEYWORD2 decodeMitsubishi KEYWORD2 decodeMitsubishi2 KEYWORD2 decodeMitsubishiAC KEYWORD2 +decodeMitsubishiHeavy KEYWORD2 decodeNEC KEYWORD2 decodeNikai KEYWORD2 decodePanasonic KEYWORD2 @@ -101,22 +125,29 @@ decodeRC5 KEYWORD2 decodeRC6 KEYWORD2 decodeRCMM KEYWORD2 decodeSAMSUNG KEYWORD2 +decodeSamsung36 KEYWORD2 decodeSamsungAC KEYWORD2 decodeSanyo KEYWORD2 decodeSanyoLC7461 KEYWORD2 decodeSharp KEYWORD2 decodeSony KEYWORD2 +decodeTcl112Ac KEYWORD2 +decodeTeco KEYWORD2 decodeToshibaAC KEYWORD2 +decodeVestelAc KEYWORD2 decodeWhirlpoolAC KEYWORD2 decodeWhynter KEYWORD2 disableIRIn KEYWORD2 disableOffTimer KEYWORD2 disableOnTimer KEYWORD2 +disableSleepTimer KEYWORD2 elapsed KEYWORD2 enableIRIn KEYWORD2 enableIROut KEYWORD2 enableOffTimer KEYWORD2 enableOnTimer KEYWORD2 +enableSleepTimer KEYWORD2 +enableTimer KEYWORD2 encodeJVC KEYWORD2 encodeLG KEYWORD2 encodeMagiQuest KEYWORD2 @@ -131,15 +162,18 @@ encodeSanyoLC7461 KEYWORD2 encodeSharp KEYWORD2 encodeSony KEYWORD2 encodeTime KEYWORD2 +fanspeed_t KEYWORD2 fixChecksum KEYWORD2 fixup KEYWORD2 +fujitsu KEYWORD2 +get3D KEYWORD2 getBeep KEYWORD2 -getBit KEYWORD2 getBufSize KEYWORD2 getButton KEYWORD2 getClean KEYWORD2 getClock KEYWORD2 getCmd KEYWORD2 +getComfort KEYWORD2 getCommand KEYWORD2 getCoolMode KEYWORD2 getCorrectedRawLength KEYWORD2 @@ -147,11 +181,16 @@ getCurrTime KEYWORD2 getCurrentTime KEYWORD2 getEcono KEYWORD2 getEye KEYWORD2 +getEyeAuto KEYWORD2 getFan KEYWORD2 getFanSpeed KEYWORD2 +getFilter KEYWORD2 getFlap KEYWORD2 +getFreshAir KEYWORD2 +getFreshAirHigh KEYWORD2 getHealth KEYWORD2 getHeatMode KEYWORD2 +getIon KEYWORD2 getIonFilter KEYWORD2 getLed KEYWORD2 getLight KEYWORD2 @@ -159,6 +198,7 @@ getMax KEYWORD2 getMode KEYWORD2 getMold KEYWORD2 getNight KEYWORD2 +getNormalState KEYWORD2 getOffTime KEYWORD2 getOffTimer KEYWORD2 getOffTimerEnabled KEYWORD2 @@ -166,23 +206,30 @@ getOnTime KEYWORD2 getOnTimer KEYWORD2 getOnTimerEnabled KEYWORD2 getPower KEYWORD2 +getPowerToggle KEYWORD2 getPowerful KEYWORD2 +getPurify KEYWORD2 getQuiet KEYWORD2 getRClevel KEYWORD2 getRaw KEYWORD2 getSensor KEYWORD2 getSensorTemp KEYWORD2 +getSilent KEYWORD2 getSleep KEYWORD2 +getSleepTime KEYWORD2 +getSleepTimerEnabled KEYWORD2 getSpeed KEYWORD2 getStartClock KEYWORD2 getStateLength KEYWORD2 getStopClock KEYWORD2 +getSuper KEYWORD2 getSwing KEYWORD2 getSwingHorizontal KEYWORD2 getSwingVertical KEYWORD2 getSwingVerticalAuto KEYWORD2 getSwingVerticalPosition KEYWORD2 getTemp KEYWORD2 +getTempOffset KEYWORD2 getTempRaw KEYWORD2 getTime KEYWORD2 getTimer KEYWORD2 @@ -191,10 +238,23 @@ getVane KEYWORD2 getXFan KEYWORD2 getZoneFollow KEYWORD2 getiFeel KEYWORD2 +gree KEYWORD2 +haier KEYWORD2 +haierYrwo2 KEYWORD2 hasACState KEYWORD2 +hitachi KEYWORD2 +htmlEscape KEYWORD2 invertBits KEYWORD2 +isOffTimerActive KEYWORD2 isOffTimerEnabled KEYWORD2 +isOnTimerActive KEYWORD2 isOnTimerEnabled KEYWORD2 +isProtocolSupported KEYWORD2 +isSpecialState KEYWORD2 +isTimeCommand KEYWORD2 +isTimerActive KEYWORD2 +isTimerEnabled KEYWORD2 +kelvinator KEYWORD2 ledOff KEYWORD2 ledOn KEYWORD2 mark KEYWORD2 @@ -203,10 +263,17 @@ matchAtLeast KEYWORD2 matchData KEYWORD2 matchMark KEYWORD2 matchSpace KEYWORD2 +midea KEYWORD2 +mitsubishi KEYWORD2 +mitsubishiHeavy152 KEYWORD2 +mitsubishiHeavy88 KEYWORD2 +mode) KEYWORD2 off KEYWORD2 on KEYWORD2 -printState KEYWORD2 -readbits KEYWORD2 +opmode_t KEYWORD2 +panasonic KEYWORD2 +position) KEYWORD2 +recoverSavedState KEYWORD2 renderTime KEYWORD2 reset KEYWORD2 resultToHexidecimal KEYWORD2 @@ -215,13 +282,17 @@ resultToSourceCode KEYWORD2 resultToTimingInfo KEYWORD2 resume KEYWORD2 reverseBits KEYWORD2 +samsung KEYWORD2 send KEYWORD2 +sendAc KEYWORD2 sendAiwaRCT501 KEYWORD2 sendArgo KEYWORD2 sendCOOLIX KEYWORD2 sendCarrierAC KEYWORD2 sendDISH KEYWORD2 sendDaikin KEYWORD2 +sendDaikin2 KEYWORD2 +sendDaikin216 KEYWORD2 sendData KEYWORD2 sendDenon KEYWORD2 sendElectraAC KEYWORD2 @@ -241,6 +312,7 @@ sendKelvinator KEYWORD2 sendLG KEYWORD2 sendLG2 KEYWORD2 sendLasertag KEYWORD2 +sendLegoPf KEYWORD2 sendLutron KEYWORD2 sendMWM KEYWORD2 sendMagiQuest KEYWORD2 @@ -248,8 +320,12 @@ sendMidea KEYWORD2 sendMitsubishi KEYWORD2 sendMitsubishi2 KEYWORD2 sendMitsubishiAC KEYWORD2 +sendMitsubishiHeavy152 KEYWORD2 +sendMitsubishiHeavy88 KEYWORD2 sendNEC KEYWORD2 sendNikai KEYWORD2 +sendOff KEYWORD2 +sendOn KEYWORD2 sendPanasonic KEYWORD2 sendPanasonic64 KEYWORD2 sendPanasonicAC KEYWORD2 @@ -260,34 +336,45 @@ sendRC6 KEYWORD2 sendRCMM KEYWORD2 sendRaw KEYWORD2 sendSAMSUNG KEYWORD2 +sendSamsung36 KEYWORD2 sendSamsungAC KEYWORD2 sendSanyoLC7461 KEYWORD2 sendSharp KEYWORD2 sendSharpRaw KEYWORD2 sendSherwood KEYWORD2 sendSony KEYWORD2 +sendTcl112Ac KEYWORD2 +sendTeco KEYWORD2 sendToshibaAC KEYWORD2 sendTrotec KEYWORD2 +sendVestelAc KEYWORD2 sendWhirlpoolAC KEYWORD2 sendWhynter KEYWORD2 serialPrintUint64 KEYWORD2 +set3D KEYWORD2 +setAuto KEYWORD2 setBeep KEYWORD2 -setBit KEYWORD2 setButton KEYWORD2 setClean KEYWORD2 setClock KEYWORD2 setCmd KEYWORD2 +setComfort KEYWORD2 setCommand KEYWORD2 setCoolMode KEYWORD2 setCurrTime KEYWORD2 setCurrentTime KEYWORD2 setEcono KEYWORD2 setEye KEYWORD2 +setEyeAuto KEYWORD2 setFan KEYWORD2 setFanSpeed KEYWORD2 +setFilter KEYWORD2 setFlap KEYWORD2 +setFreshAir KEYWORD2 +setFreshAirHigh KEYWORD2 setHealth KEYWORD2 setHeatMode KEYWORD2 +setIon KEYWORD2 setIonFilter KEYWORD2 setLed KEYWORD2 setLight KEYWORD2 @@ -297,19 +384,25 @@ setModel KEYWORD2 setMold KEYWORD2 setNight KEYWORD2 setOffTimer KEYWORD2 +setOffTimerActive KEYWORD2 setOnTimer KEYWORD2 +setOnTimerActive KEYWORD2 setPower KEYWORD2 +setPowerToggle KEYWORD2 setPowerful KEYWORD2 +setPurify KEYWORD2 setQuiet KEYWORD2 setRaw KEYWORD2 setRoomTemp KEYWORD2 setSensor KEYWORD2 setSensorTemp KEYWORD2 setSensorTempRaw KEYWORD2 +setSilent KEYWORD2 setSleep KEYWORD2 setSpeed KEYWORD2 setStartClock KEYWORD2 setStopClock KEYWORD2 +setSuper KEYWORD2 setSwing KEYWORD2 setSwingHorizontal KEYWORD2 setSwingVertical KEYWORD2 @@ -317,6 +410,7 @@ setTemp KEYWORD2 setTempRaw KEYWORD2 setTime KEYWORD2 setTimer KEYWORD2 +setTimerActive KEYWORD2 setTurbo KEYWORD2 setUnknownThreshold KEYWORD2 setVane KEYWORD2 @@ -324,19 +418,33 @@ setXFan KEYWORD2 setZoneFollow KEYWORD2 setiFeel KEYWORD2 space KEYWORD2 +speed) KEYWORD2 stateReset KEYWORD2 stepHoriz KEYWORD2 stepVert KEYWORD2 +strToBool KEYWORD2 +strToModel KEYWORD2 sumBytes KEYWORD2 +swingh_t KEYWORD2 +swingv) KEYWORD2 +swingv_t KEYWORD2 +tcl112 KEYWORD2 +teco KEYWORD2 ticksHigh KEYWORD2 ticksLow KEYWORD2 timeToString KEYWORD2 toString KEYWORD2 toggleRC5 KEYWORD2 toggleRC6 KEYWORD2 +toshiba KEYWORD2 +trotec KEYWORD2 typeToString KEYWORD2 uint64ToString KEYWORD2 +updateSavedState KEYWORD2 validChecksum KEYWORD2 +vestel KEYWORD2 +whirlpool KEYWORD2 +xorBytes KEYWORD2 ####################################### # Constants (LITERAL1) @@ -349,9 +457,9 @@ ARDB1 LITERAL1 ARGO LITERAL1 ARGO_COMMAND_LENGTH LITERAL1 ARGO_COOL_AUTO LITERAL1 +ARGO_COOL_HUM LITERAL1 ARGO_COOL_OFF LITERAL1 ARGO_COOL_ON LITERAL1 -ARGO_COOl_HUM LITERAL1 ARGO_FAN_1 LITERAL1 ARGO_FAN_2 LITERAL1 ARGO_FAN_3 LITERAL1 @@ -375,10 +483,11 @@ CARRIER_AC_BITS LITERAL1 COOLIX LITERAL1 COOLIX_BITS LITERAL1 DAIKIN LITERAL1 +DAIKIN2 LITERAL1 +DAIKIN216 LITERAL1 DAIKIN_AUTO LITERAL1 DAIKIN_COMMAND_LENGTH LITERAL1 DAIKIN_COOL LITERAL1 -DAIKIN_DEBUG LITERAL1 DAIKIN_DRY LITERAL1 DAIKIN_FAN LITERAL1 DAIKIN_FAN_AUTO LITERAL1 @@ -394,6 +503,8 @@ DECODE_ARGO LITERAL1 DECODE_CARRIER_AC LITERAL1 DECODE_COOLIX LITERAL1 DECODE_DAIKIN LITERAL1 +DECODE_DAIKIN2 LITERAL1 +DECODE_DAIKIN216 LITERAL1 DECODE_DENON LITERAL1 DECODE_DISH LITERAL1 DECODE_ELECTRA_AC LITERAL1 @@ -410,12 +521,14 @@ DECODE_HITACHI_AC2 LITERAL1 DECODE_JVC LITERAL1 DECODE_KELVINATOR LITERAL1 DECODE_LASERTAG LITERAL1 +DECODE_LEGOPF LITERAL1 DECODE_LG LITERAL1 DECODE_LUTRON LITERAL1 DECODE_MAGIQUEST LITERAL1 DECODE_MIDEA LITERAL1 DECODE_MITSUBISHI LITERAL1 DECODE_MITSUBISHI2 LITERAL1 +DECODE_MITSUBISHIHEAVY LITERAL1 DECODE_MITSUBISHI_AC LITERAL1 DECODE_MWM LITERAL1 DECODE_NEC LITERAL1 @@ -428,19 +541,25 @@ DECODE_RC5 LITERAL1 DECODE_RC6 LITERAL1 DECODE_RCMM LITERAL1 DECODE_SAMSUNG LITERAL1 +DECODE_SAMSUNG36 LITERAL1 DECODE_SAMSUNG_AC LITERAL1 DECODE_SANYO LITERAL1 DECODE_SHARP LITERAL1 DECODE_SHERWOOD LITERAL1 DECODE_SONY LITERAL1 +DECODE_TCL112AC LITERAL1 +DECODE_TECO LITERAL1 DECODE_TOSHIBA_AC LITERAL1 DECODE_TROTEC LITERAL1 +DECODE_VESTEL_AC LITERAL1 DECODE_WHIRLPOOL_AC LITERAL1 DECODE_WHYNTER LITERAL1 DENON LITERAL1 DENON_48_BITS LITERAL1 DENON_BITS LITERAL1 DENON_LEGACY_BITS LITERAL1 +DG11J13A LITERAL1 +DG11J191 LITERAL1 DISH LITERAL1 DISH_BITS LITERAL1 ELECTRA_AC LITERAL1 @@ -580,6 +699,7 @@ KELVINATOR_MIN_TEMP LITERAL1 KELVINATOR_STATE_LENGTH LITERAL1 LASERTAG LITERAL1 LASERTAG_BITS LITERAL1 +LEGOPF LITERAL1 LG LITERAL1 LG2 LITERAL1 LG32_BITS LITERAL1 @@ -623,6 +743,8 @@ MITSUBISHI_AC_STATE_LENGTH LITERAL1 MITSUBISHI_AC_VANE_AUTO LITERAL1 MITSUBISHI_AC_VANE_AUTO_MOVE LITERAL1 MITSUBISHI_BITS LITERAL1 +MITSUBISHI_HEAVY_152 LITERAL1 +MITSUBISHI_HEAVY_88 LITERAL1 MWM LITERAL1 NEC LITERAL1 NEC_BITS LITERAL1 @@ -647,6 +769,7 @@ RC6_MODE0_BITS LITERAL1 RCMM LITERAL1 RCMM_BITS LITERAL1 SAMSUNG LITERAL1 +SAMSUNG36 LITERAL1 SAMSUNG_AC LITERAL1 SAMSUNG_BITS LITERAL1 SANYO LITERAL1 @@ -658,6 +781,8 @@ SEND_ARGO LITERAL1 SEND_CARRIER_AC LITERAL1 SEND_COOLIX LITERAL1 SEND_DAIKIN LITERAL1 +SEND_DAIKIN2 LITERAL1 +SEND_DAIKIN216 LITERAL1 SEND_DENON LITERAL1 SEND_DISH LITERAL1 SEND_ELECTRA_AC LITERAL1 @@ -673,12 +798,14 @@ SEND_HITACHI_AC2 LITERAL1 SEND_JVC LITERAL1 SEND_KELVINATOR LITERAL1 SEND_LASERTAG LITERAL1 +SEND_LEGOPF LITERAL1 SEND_LG LITERAL1 SEND_LUTRON LITERAL1 SEND_MAGIQUEST LITERAL1 SEND_MIDEA LITERAL1 SEND_MITSUBISHI LITERAL1 SEND_MITSUBISHI2 LITERAL1 +SEND_MITSUBISHIHEAVY LITERAL1 SEND_MITSUBISHI_AC LITERAL1 SEND_MWM LITERAL1 SEND_NEC LITERAL1 @@ -692,13 +819,17 @@ SEND_RC5 LITERAL1 SEND_RC6 LITERAL1 SEND_RCMM LITERAL1 SEND_SAMSUNG LITERAL1 +SEND_SAMSUNG36 LITERAL1 SEND_SAMSUNG_AC LITERAL1 SEND_SANYO LITERAL1 SEND_SHARP LITERAL1 SEND_SHERWOOD LITERAL1 SEND_SONY LITERAL1 +SEND_TCL112AC LITERAL1 +SEND_TECO LITERAL1 SEND_TOSHIBA_AC LITERAL1 SEND_TROTEC LITERAL1 +SEND_VESTEL_AC LITERAL1 SEND_WHIRLPOOL_AC LITERAL1 SEND_WHYNTER LITERAL1 SHARP LITERAL1 @@ -709,6 +840,8 @@ SONY LITERAL1 SONY_12_BITS LITERAL1 SONY_15_BITS LITERAL1 SONY_20_BITS LITERAL1 +TCL112AC LITERAL1 +TECO LITERAL1 TIMEOUT_MS LITERAL1 TOSHIBA_AC LITERAL1 TOSHIBA_AC_AUTO LITERAL1 @@ -733,9 +866,9 @@ TROTEC_FAN_MED LITERAL1 TROTEC_MAX_TEMP LITERAL1 TROTEC_MAX_TIMER LITERAL1 TROTEC_MIN_TEMP LITERAL1 -TROTEC_MIN_TIMER LITERAL1 UNKNOWN LITERAL1 UNUSED LITERAL1 +VESTEL_AC LITERAL1 WHIRLPOOL_AC LITERAL1 WHYNTER LITERAL1 WHYNTER_BITS LITERAL1 @@ -750,6 +883,7 @@ kArgoCoolAuto LITERAL1 kArgoCoolHum LITERAL1 kArgoCoolOff LITERAL1 kArgoCoolOn LITERAL1 +kArgoDefaultRepeat LITERAL1 kArgoFan1 LITERAL1 kArgoFan2 LITERAL1 kArgoFan3 LITERAL1 @@ -772,6 +906,7 @@ kArgoMinTemp LITERAL1 kArgoOneSpace LITERAL1 kArgoStateLength LITERAL1 kArgoZeroSpace LITERAL1 +kAuto LITERAL1 kCarrierAcBitMark LITERAL1 kCarrierAcBits LITERAL1 kCarrierAcGap LITERAL1 @@ -780,16 +915,19 @@ kCarrierAcHdrSpace LITERAL1 kCarrierAcMinRepeat LITERAL1 kCarrierAcOneSpace LITERAL1 kCarrierAcZeroSpace LITERAL1 +kCool LITERAL1 kCoolixAuto LITERAL1 kCoolixBitMark LITERAL1 kCoolixBitMarkTicks LITERAL1 kCoolixBits LITERAL1 kCoolixClean LITERAL1 kCoolixCool LITERAL1 +kCoolixDefaultRepeat LITERAL1 kCoolixDefaultState LITERAL1 kCoolixDry LITERAL1 kCoolixFan LITERAL1 kCoolixFanAuto LITERAL1 +kCoolixFanAuto0 LITERAL1 kCoolixFanFixed LITERAL1 kCoolixFanMask LITERAL1 kCoolixFanMax LITERAL1 @@ -827,7 +965,70 @@ kCoolixUnknown LITERAL1 kCoolixZeroSpace LITERAL1 kCoolixZeroSpaceTicks LITERAL1 kCoolixZoneFollowMask LITERAL1 +kDaikin216BitMark LITERAL1 +kDaikin216Bits LITERAL1 +kDaikin216ByteFan LITERAL1 +kDaikin216ByteMode LITERAL1 +kDaikin216BytePower LITERAL1 +kDaikin216ByteSwingH LITERAL1 +kDaikin216ByteSwingV LITERAL1 +kDaikin216ByteTemp LITERAL1 +kDaikin216DefaultRepeat LITERAL1 +kDaikin216Freq LITERAL1 +kDaikin216Gap LITERAL1 +kDaikin216HdrMark LITERAL1 +kDaikin216HdrSpace LITERAL1 +kDaikin216MaskFan LITERAL1 +kDaikin216MaskMode LITERAL1 +kDaikin216MaskSwingH LITERAL1 +kDaikin216MaskSwingV LITERAL1 +kDaikin216MaskTemp LITERAL1 +kDaikin216OneSpace LITERAL1 +kDaikin216Section1Length LITERAL1 +kDaikin216Section2Length LITERAL1 +kDaikin216Sections LITERAL1 +kDaikin216StateLength LITERAL1 +kDaikin216ZeroSpace LITERAL1 +kDaikin2BeepMask LITERAL1 +kDaikin2BitClean LITERAL1 +kDaikin2BitEye LITERAL1 +kDaikin2BitEyeAuto LITERAL1 +kDaikin2BitFreshAir LITERAL1 +kDaikin2BitFreshAirHigh LITERAL1 +kDaikin2BitMark LITERAL1 +kDaikin2BitMold LITERAL1 +kDaikin2BitPower LITERAL1 +kDaikin2BitPurify LITERAL1 +kDaikin2BitSleepTimer LITERAL1 +kDaikin2Bits LITERAL1 +kDaikin2DefaultRepeat LITERAL1 +kDaikin2Freq LITERAL1 +kDaikin2Gap LITERAL1 +kDaikin2HdrMark LITERAL1 +kDaikin2HdrSpace LITERAL1 +kDaikin2LeaderMark LITERAL1 +kDaikin2LeaderSpace LITERAL1 +kDaikin2LightMask LITERAL1 +kDaikin2MinCoolTemp LITERAL1 +kDaikin2OneSpace LITERAL1 +kDaikin2Section1Length LITERAL1 +kDaikin2Section2Length LITERAL1 +kDaikin2Sections LITERAL1 +kDaikin2StateLength LITERAL1 +kDaikin2SwingHAuto LITERAL1 +kDaikin2SwingHSwing LITERAL1 +kDaikin2SwingVAuto LITERAL1 +kDaikin2SwingVBreeze LITERAL1 +kDaikin2SwingVCirculate LITERAL1 +kDaikin2SwingVHigh LITERAL1 +kDaikin2SwingVLow LITERAL1 +kDaikin2Tolerance LITERAL1 +kDaikin2ZeroSpace LITERAL1 kDaikinAuto LITERAL1 +kDaikinBeepLoud LITERAL1 +kDaikinBeepOff LITERAL1 +kDaikinBeepQuiet LITERAL1 +kDaikinBitComfort LITERAL1 kDaikinBitEcono LITERAL1 kDaikinBitEye LITERAL1 kDaikinBitMark LITERAL1 @@ -839,18 +1040,33 @@ kDaikinBitPowerful LITERAL1 kDaikinBitSensor LITERAL1 kDaikinBitSilent LITERAL1 kDaikinBits LITERAL1 +kDaikinBitsShort LITERAL1 +kDaikinByteChecksum1 LITERAL1 +kDaikinByteChecksum2 LITERAL1 +kDaikinByteChecksum3 LITERAL1 +kDaikinByteClockMinsHigh LITERAL1 +kDaikinByteClockMinsLow LITERAL1 +kDaikinByteComfort LITERAL1 kDaikinByteEcono LITERAL1 kDaikinByteEye LITERAL1 +kDaikinByteFan LITERAL1 kDaikinByteMold LITERAL1 kDaikinByteOffTimer LITERAL1 +kDaikinByteOffTimerMinsHigh LITERAL1 +kDaikinByteOffTimerMinsLow LITERAL1 kDaikinByteOnTimer LITERAL1 +kDaikinByteOnTimerMinsHigh LITERAL1 +kDaikinByteOnTimerMinsLow LITERAL1 kDaikinBytePower LITERAL1 kDaikinBytePowerful LITERAL1 kDaikinByteSensor LITERAL1 kDaikinByteSilent LITERAL1 +kDaikinByteSwingH LITERAL1 +kDaikinByteTemp LITERAL1 kDaikinCool LITERAL1 kDaikinCurBit LITERAL1 kDaikinCurIndex LITERAL1 +kDaikinDefaultRepeat LITERAL1 kDaikinDry LITERAL1 kDaikinFan LITERAL1 kDaikinFanAuto LITERAL1 @@ -861,15 +1077,25 @@ kDaikinFirstHeader64 LITERAL1 kDaikinGap LITERAL1 kDaikinHdrMark LITERAL1 kDaikinHdrSpace LITERAL1 +kDaikinHeaderLength LITERAL1 kDaikinHeat LITERAL1 +kDaikinLightBright LITERAL1 +kDaikinLightDim LITERAL1 +kDaikinLightOff LITERAL1 kDaikinMarkExcess LITERAL1 kDaikinMaxTemp LITERAL1 kDaikinMinTemp LITERAL1 kDaikinOneSpace LITERAL1 -kDaikinRawBits LITERAL1 +kDaikinSection1Length LITERAL1 +kDaikinSection2Length LITERAL1 +kDaikinSection3Length LITERAL1 +kDaikinSections LITERAL1 kDaikinStateLength LITERAL1 +kDaikinStateLengthShort LITERAL1 kDaikinTolerance LITERAL1 +kDaikinUnusedTime LITERAL1 kDaikinZeroSpace LITERAL1 +kDefaultMessageGap LITERAL1 kDenonBitMark LITERAL1 kDenonBitMarkTicks LITERAL1 kDenonBits LITERAL1 @@ -902,6 +1128,7 @@ kDishRptSpaceTicks LITERAL1 kDishTick LITERAL1 kDishZeroSpace LITERAL1 kDishZeroSpaceTicks LITERAL1 +kDry LITERAL1 kDutyDefault LITERAL1 kDutyMax LITERAL1 kElectraAcBitMark LITERAL1 @@ -912,6 +1139,7 @@ kElectraAcMessageGap LITERAL1 kElectraAcOneSpace LITERAL1 kElectraAcStateLength LITERAL1 kElectraAcZeroSpace LITERAL1 +kFan LITERAL1 kFnvBasis32 LITERAL1 kFnvPrime32 LITERAL1 kFooter LITERAL1 @@ -969,10 +1197,13 @@ kGreeBits LITERAL1 kGreeBlockFooter LITERAL1 kGreeBlockFooterBits LITERAL1 kGreeCool LITERAL1 +kGreeDefaultRepeat LITERAL1 kGreeDry LITERAL1 kGreeFan LITERAL1 +kGreeFanAuto LITERAL1 kGreeFanMask LITERAL1 kGreeFanMax LITERAL1 +kGreeFanMin LITERAL1 kGreeHdrMark LITERAL1 kGreeHdrSpace LITERAL1 kGreeHeat LITERAL1 @@ -1020,6 +1251,7 @@ kHaierAcCmdTimerCancel LITERAL1 kHaierAcCmdTimerSet LITERAL1 kHaierAcCool LITERAL1 kHaierAcDefTemp LITERAL1 +kHaierAcDefaultRepeat LITERAL1 kHaierAcDry LITERAL1 kHaierAcFan LITERAL1 kHaierAcFanAuto LITERAL1 @@ -1050,6 +1282,7 @@ kHaierAcYrw02ButtonTempDown LITERAL1 kHaierAcYrw02ButtonTempUp LITERAL1 kHaierAcYrw02ButtonTurbo LITERAL1 kHaierAcYrw02Cool LITERAL1 +kHaierAcYrw02DefaultRepeat LITERAL1 kHaierAcYrw02Dry LITERAL1 kHaierAcYrw02Fan LITERAL1 kHaierAcYrw02FanAuto LITERAL1 @@ -1071,17 +1304,32 @@ kHaierAcYrw02TurboLow LITERAL1 kHaierAcYrw02TurboOff LITERAL1 kHaierAcZeroSpace LITERAL1 kHeader LITERAL1 +kHeat LITERAL1 +kHigh LITERAL1 +kHighest LITERAL1 kHitachiAc1Bits LITERAL1 kHitachiAc1HdrMark LITERAL1 kHitachiAc1HdrSpace LITERAL1 kHitachiAc1StateLength LITERAL1 kHitachiAc2Bits LITERAL1 kHitachiAc2StateLength LITERAL1 +kHitachiAcAuto LITERAL1 +kHitachiAcAutoTemp LITERAL1 kHitachiAcBitMark LITERAL1 kHitachiAcBits LITERAL1 +kHitachiAcCool LITERAL1 +kHitachiAcDefaultRepeat LITERAL1 +kHitachiAcDry LITERAL1 +kHitachiAcFan LITERAL1 +kHitachiAcFanAuto LITERAL1 +kHitachiAcFanHigh LITERAL1 +kHitachiAcFanLow LITERAL1 kHitachiAcHdrMark LITERAL1 kHitachiAcHdrSpace LITERAL1 +kHitachiAcHeat LITERAL1 +kHitachiAcMaxTemp LITERAL1 kHitachiAcMinGap LITERAL1 +kHitachiAcMinTemp LITERAL1 kHitachiAcOneSpace LITERAL1 kHitachiAcStateLength LITERAL1 kHitachiAcZeroSpace LITERAL1 @@ -1113,6 +1361,7 @@ kKelvinatorChecksumStart LITERAL1 kKelvinatorCmdFooter LITERAL1 kKelvinatorCmdFooterBits LITERAL1 kKelvinatorCool LITERAL1 +kKelvinatorDefaultRepeat LITERAL1 kKelvinatorDry LITERAL1 kKelvinatorFan LITERAL1 kKelvinatorFanAuto LITERAL1 @@ -1159,6 +1408,16 @@ kLasertagMinRepeat LITERAL1 kLasertagMinSamples LITERAL1 kLasertagTick LITERAL1 kLasertagTolerance LITERAL1 +kLastDecodeType LITERAL1 +kLeft LITERAL1 +kLeftMax LITERAL1 +kLegoPfBitMark LITERAL1 +kLegoPfBits LITERAL1 +kLegoPfHdrSpace LITERAL1 +kLegoPfMinCommandLength LITERAL1 +kLegoPfMinRepeat LITERAL1 +kLegoPfOneSpace LITERAL1 +kLegoPfZeroSpace LITERAL1 kLg2BitMark LITERAL1 kLg2BitMarkTicks LITERAL1 kLg2HdrMark LITERAL1 @@ -1190,6 +1449,8 @@ kLgRptSpaceTicks LITERAL1 kLgTick LITERAL1 kLgZeroSpace LITERAL1 kLgZeroSpaceTicks LITERAL1 +kLow LITERAL1 +kLowest LITERAL1 kLutronBits LITERAL1 kLutronDelta LITERAL1 kLutronGap LITERAL1 @@ -1213,8 +1474,11 @@ kMagiquestBits LITERAL1 kMark LITERAL1 kMarkExcess LITERAL1 kMarkState LITERAL1 +kMax LITERAL1 kMaxAccurateUsecDelay LITERAL1 kMaxTimeoutMs LITERAL1 +kMedium LITERAL1 +kMiddle LITERAL1 kMideaACAuto LITERAL1 kMideaACChecksumMask LITERAL1 kMideaACCool LITERAL1 @@ -1251,6 +1515,7 @@ kMideaTick LITERAL1 kMideaTolerance LITERAL1 kMideaZeroSpace LITERAL1 kMideaZeroSpaceTicks LITERAL1 +kMin LITERAL1 kMitsubishi2BitMark LITERAL1 kMitsubishi2HdrMark LITERAL1 kMitsubishi2HdrSpace LITERAL1 @@ -1287,6 +1552,91 @@ kMitsubishiAcZeroSpace LITERAL1 kMitsubishiBitMark LITERAL1 kMitsubishiBitMarkTicks LITERAL1 kMitsubishiBits LITERAL1 +kMitsubishiHeavy152Bits LITERAL1 +kMitsubishiHeavy152FanAuto LITERAL1 +kMitsubishiHeavy152FanEcono LITERAL1 +kMitsubishiHeavy152FanHigh LITERAL1 +kMitsubishiHeavy152FanLow LITERAL1 +kMitsubishiHeavy152FanMax LITERAL1 +kMitsubishiHeavy152FanMed LITERAL1 +kMitsubishiHeavy152FanTurbo LITERAL1 +kMitsubishiHeavy152MinRepeat LITERAL1 +kMitsubishiHeavy152StateLength LITERAL1 +kMitsubishiHeavy152SwingHAuto LITERAL1 +kMitsubishiHeavy152SwingHLeft LITERAL1 +kMitsubishiHeavy152SwingHLeftMax LITERAL1 +kMitsubishiHeavy152SwingHLeftRight LITERAL1 +kMitsubishiHeavy152SwingHMask LITERAL1 +kMitsubishiHeavy152SwingHMiddle LITERAL1 +kMitsubishiHeavy152SwingHOff LITERAL1 +kMitsubishiHeavy152SwingHRight LITERAL1 +kMitsubishiHeavy152SwingHRightLeft LITERAL1 +kMitsubishiHeavy152SwingHRightMax LITERAL1 +kMitsubishiHeavy152SwingVAuto LITERAL1 +kMitsubishiHeavy152SwingVHigh LITERAL1 +kMitsubishiHeavy152SwingVHighest LITERAL1 +kMitsubishiHeavy152SwingVLow LITERAL1 +kMitsubishiHeavy152SwingVLowest LITERAL1 +kMitsubishiHeavy152SwingVMask LITERAL1 +kMitsubishiHeavy152SwingVMiddle LITERAL1 +kMitsubishiHeavy152SwingVOff LITERAL1 +kMitsubishiHeavy3DMask LITERAL1 +kMitsubishiHeavy88Bits LITERAL1 +kMitsubishiHeavy88CleanBit LITERAL1 +kMitsubishiHeavy88FanAuto LITERAL1 +kMitsubishiHeavy88FanEcono LITERAL1 +kMitsubishiHeavy88FanHigh LITERAL1 +kMitsubishiHeavy88FanLow LITERAL1 +kMitsubishiHeavy88FanMask LITERAL1 +kMitsubishiHeavy88FanMed LITERAL1 +kMitsubishiHeavy88FanTurbo LITERAL1 +kMitsubishiHeavy88MinRepeat LITERAL1 +kMitsubishiHeavy88StateLength LITERAL1 +kMitsubishiHeavy88SwingH3D LITERAL1 +kMitsubishiHeavy88SwingHAuto LITERAL1 +kMitsubishiHeavy88SwingHLeft LITERAL1 +kMitsubishiHeavy88SwingHLeftMax LITERAL1 +kMitsubishiHeavy88SwingHLeftRight LITERAL1 +kMitsubishiHeavy88SwingHMask LITERAL1 +kMitsubishiHeavy88SwingHMiddle LITERAL1 +kMitsubishiHeavy88SwingHOff LITERAL1 +kMitsubishiHeavy88SwingHRight LITERAL1 +kMitsubishiHeavy88SwingHRightLeft LITERAL1 +kMitsubishiHeavy88SwingHRightMax LITERAL1 +kMitsubishiHeavy88SwingVAuto LITERAL1 +kMitsubishiHeavy88SwingVHigh LITERAL1 +kMitsubishiHeavy88SwingVHighest LITERAL1 +kMitsubishiHeavy88SwingVLow LITERAL1 +kMitsubishiHeavy88SwingVLowest LITERAL1 +kMitsubishiHeavy88SwingVMask LITERAL1 +kMitsubishiHeavy88SwingVMaskByte5 LITERAL1 +kMitsubishiHeavy88SwingVMaskByte7 LITERAL1 +kMitsubishiHeavy88SwingVMiddle LITERAL1 +kMitsubishiHeavy88SwingVOff LITERAL1 +kMitsubishiHeavyAuto LITERAL1 +kMitsubishiHeavyBitMark LITERAL1 +kMitsubishiHeavyCleanBit LITERAL1 +kMitsubishiHeavyCool LITERAL1 +kMitsubishiHeavyDry LITERAL1 +kMitsubishiHeavyFan LITERAL1 +kMitsubishiHeavyFanMask LITERAL1 +kMitsubishiHeavyFilterBit LITERAL1 +kMitsubishiHeavyGap LITERAL1 +kMitsubishiHeavyHdrMark LITERAL1 +kMitsubishiHeavyHdrSpace LITERAL1 +kMitsubishiHeavyHeat LITERAL1 +kMitsubishiHeavyMaxTemp LITERAL1 +kMitsubishiHeavyMinTemp LITERAL1 +kMitsubishiHeavyModeMask LITERAL1 +kMitsubishiHeavyNightBit LITERAL1 +kMitsubishiHeavyOneSpace LITERAL1 +kMitsubishiHeavyPowerBit LITERAL1 +kMitsubishiHeavySigLength LITERAL1 +kMitsubishiHeavySilentBit LITERAL1 +kMitsubishiHeavyTempMask LITERAL1 +kMitsubishiHeavyZeroSpace LITERAL1 +kMitsubishiHeavyZjsSig LITERAL1 +kMitsubishiHeavyZmsSig LITERAL1 kMitsubishiMinCommandLength LITERAL1 kMitsubishiMinCommandLengthTicks LITERAL1 kMitsubishiMinGap LITERAL1 @@ -1331,10 +1681,12 @@ kNikaiTick LITERAL1 kNikaiZeroSpace LITERAL1 kNikaiZeroSpaceTicks LITERAL1 kNoRepeat LITERAL1 +kOff LITERAL1 kPanasonicAcAuto LITERAL1 kPanasonicAcBits LITERAL1 kPanasonicAcChecksumInit LITERAL1 kPanasonicAcCool LITERAL1 +kPanasonicAcDefaultRepeat LITERAL1 kPanasonicAcDry LITERAL1 kPanasonicAcExcess LITERAL1 kPanasonicAcFan LITERAL1 @@ -1393,6 +1745,7 @@ kPanasonicMinGapTicks LITERAL1 kPanasonicNke LITERAL1 kPanasonicOneSpace LITERAL1 kPanasonicOneSpaceTicks LITERAL1 +kPanasonicRkr LITERAL1 kPanasonicTick LITERAL1 kPanasonicUnknown LITERAL1 kPanasonicZeroSpace LITERAL1 @@ -1450,6 +1803,9 @@ kRcmmRptLengthTicks LITERAL1 kRcmmTick LITERAL1 kRcmmTolerance LITERAL1 kRepeat LITERAL1 +kRight LITERAL1 +kRightMax LITERAL1 +kSamsung36Bits LITERAL1 kSamsungACSectionLength LITERAL1 kSamsungAcAuto LITERAL1 kSamsungAcAutoTemp LITERAL1 @@ -1459,6 +1815,7 @@ kSamsungAcBits LITERAL1 kSamsungAcCleanMask10 LITERAL1 kSamsungAcCleanMask11 LITERAL1 kSamsungAcCool LITERAL1 +kSamsungAcDefaultRepeat LITERAL1 kSamsungAcDry LITERAL1 kSamsungAcExtendedBits LITERAL1 kSamsungAcExtendedStateLength LITERAL1 @@ -1569,6 +1926,65 @@ kSpaceState LITERAL1 kStartOffset LITERAL1 kStateSizeMax LITERAL1 kStopState LITERAL1 +kTcl112AcAuto LITERAL1 +kTcl112AcBitEcono LITERAL1 +kTcl112AcBitHealth LITERAL1 +kTcl112AcBitLight LITERAL1 +kTcl112AcBitMark LITERAL1 +kTcl112AcBitSwingH LITERAL1 +kTcl112AcBitSwingV LITERAL1 +kTcl112AcBitTurbo LITERAL1 +kTcl112AcBits LITERAL1 +kTcl112AcCool LITERAL1 +kTcl112AcDefaultRepeat LITERAL1 +kTcl112AcDry LITERAL1 +kTcl112AcFan LITERAL1 +kTcl112AcFanAuto LITERAL1 +kTcl112AcFanHigh LITERAL1 +kTcl112AcFanLow LITERAL1 +kTcl112AcFanMask LITERAL1 +kTcl112AcFanMed LITERAL1 +kTcl112AcGap LITERAL1 +kTcl112AcHalfDegree LITERAL1 +kTcl112AcHdrMark LITERAL1 +kTcl112AcHdrSpace LITERAL1 +kTcl112AcHeat LITERAL1 +kTcl112AcOneSpace LITERAL1 +kTcl112AcPowerMask LITERAL1 +kTcl112AcStateLength LITERAL1 +kTcl112AcTempMax LITERAL1 +kTcl112AcTempMin LITERAL1 +kTcl112AcZeroSpace LITERAL1 +kTecoAuto LITERAL1 +kTecoBitMark LITERAL1 +kTecoBits LITERAL1 +kTecoCool LITERAL1 +kTecoDefaultRepeat LITERAL1 +kTecoDry LITERAL1 +kTecoFan LITERAL1 +kTecoFanAuto LITERAL1 +kTecoFanHigh LITERAL1 +kTecoFanLow LITERAL1 +kTecoFanMask LITERAL1 +kTecoFanMed LITERAL1 +kTecoGap LITERAL1 +kTecoHdrMark LITERAL1 +kTecoHdrSpace LITERAL1 +kTecoHeat LITERAL1 +kTecoMaxTemp LITERAL1 +kTecoMinTemp LITERAL1 +kTecoModeMask LITERAL1 +kTecoOneSpace LITERAL1 +kTecoPower LITERAL1 +kTecoReset LITERAL1 +kTecoSleep LITERAL1 +kTecoSwing LITERAL1 +kTecoTempMask LITERAL1 +kTecoTimerHalfH LITERAL1 +kTecoTimerOn LITERAL1 +kTecoTimerTenHr LITERAL1 +kTecoTimerUniHr LITERAL1 +kTecoZeroSpace LITERAL1 kTimeoutMs LITERAL1 kTolerance LITERAL1 kToshibaACBits LITERAL1 @@ -1592,6 +2008,7 @@ kToshibaAcZeroSpace LITERAL1 kTrotecAuto LITERAL1 kTrotecCool LITERAL1 kTrotecDefTemp LITERAL1 +kTrotecDefaultRepeat LITERAL1 kTrotecDry LITERAL1 kTrotecFan LITERAL1 kTrotecFanHigh LITERAL1 @@ -1606,26 +2023,119 @@ kTrotecIntro2 LITERAL1 kTrotecMaxTemp LITERAL1 kTrotecMaxTimer LITERAL1 kTrotecMinTemp LITERAL1 -kTrotecMinTimer LITERAL1 -kTrotecOff LITERAL1 -kTrotecOn LITERAL1 kTrotecOneMark LITERAL1 kTrotecOneSpace LITERAL1 -kTrotecSleepOn LITERAL1 +kTrotecPowerBit LITERAL1 +kTrotecSleepBit LITERAL1 kTrotecStateLength LITERAL1 -kTrotecTimerOn LITERAL1 +kTrotecTimerBit LITERAL1 kTrotecZeroMark LITERAL1 kTrotecZeroSpace LITERAL1 kUnknownThreshold LITERAL1 +kVestelAcAuto LITERAL1 +kVestelAcBitMark LITERAL1 +kVestelAcBits LITERAL1 +kVestelAcCRCMask LITERAL1 +kVestelAcChecksumOffset LITERAL1 +kVestelAcCool LITERAL1 +kVestelAcDry LITERAL1 +kVestelAcFan LITERAL1 +kVestelAcFanAuto LITERAL1 +kVestelAcFanAutoCool LITERAL1 +kVestelAcFanAutoHot LITERAL1 +kVestelAcFanHigh LITERAL1 +kVestelAcFanLow LITERAL1 +kVestelAcFanMed LITERAL1 +kVestelAcFanOffset LITERAL1 +kVestelAcHdrMark LITERAL1 +kVestelAcHdrSpace LITERAL1 +kVestelAcHeat LITERAL1 +kVestelAcHourOffset LITERAL1 +kVestelAcIon LITERAL1 +kVestelAcIonOffset LITERAL1 +kVestelAcMaxTemp LITERAL1 +kVestelAcMinTempC LITERAL1 +kVestelAcMinTempH LITERAL1 +kVestelAcMinuteOffset LITERAL1 +kVestelAcModeOffset LITERAL1 +kVestelAcNormal LITERAL1 +kVestelAcOffTimeOffset LITERAL1 +kVestelAcOffTimerFlagOffset LITERAL1 +kVestelAcOnTimeOffset LITERAL1 +kVestelAcOnTimerFlagOffset LITERAL1 +kVestelAcOneSpace LITERAL1 +kVestelAcPowerOffset LITERAL1 +kVestelAcSleep LITERAL1 +kVestelAcStateDefault LITERAL1 +kVestelAcSwing LITERAL1 +kVestelAcSwingOffset LITERAL1 +kVestelAcTempOffset LITERAL1 +kVestelAcTimeStateDefault LITERAL1 +kVestelAcTimerFlagOffset LITERAL1 +kVestelAcTolerance LITERAL1 +kVestelAcTurbo LITERAL1 +kVestelAcTurboSleepOffset LITERAL1 +kVestelAcZeroSpace LITERAL1 +kWhirlpoolAcAltTempMask LITERAL1 +kWhirlpoolAcAltTempPos LITERAL1 +kWhirlpoolAcAuto LITERAL1 +kWhirlpoolAcAutoTemp LITERAL1 kWhirlpoolAcBitMark LITERAL1 kWhirlpoolAcBits LITERAL1 +kWhirlpoolAcChecksumByte1 LITERAL1 +kWhirlpoolAcChecksumByte2 LITERAL1 +kWhirlpoolAcClockPos LITERAL1 +kWhirlpoolAcCommand6thSense LITERAL1 +kWhirlpoolAcCommandFanSpeed LITERAL1 +kWhirlpoolAcCommandIFeel LITERAL1 +kWhirlpoolAcCommandLight LITERAL1 +kWhirlpoolAcCommandMode LITERAL1 +kWhirlpoolAcCommandOffTimer LITERAL1 +kWhirlpoolAcCommandOnTimer LITERAL1 +kWhirlpoolAcCommandPos LITERAL1 +kWhirlpoolAcCommandPower LITERAL1 +kWhirlpoolAcCommandSleep LITERAL1 +kWhirlpoolAcCommandSuper LITERAL1 +kWhirlpoolAcCommandSwing LITERAL1 +kWhirlpoolAcCommandTemp LITERAL1 +kWhirlpoolAcCool LITERAL1 +kWhirlpoolAcDefaultRepeat LITERAL1 +kWhirlpoolAcDry LITERAL1 +kWhirlpoolAcFan LITERAL1 +kWhirlpoolAcFanAuto LITERAL1 +kWhirlpoolAcFanHigh LITERAL1 +kWhirlpoolAcFanLow LITERAL1 +kWhirlpoolAcFanMask LITERAL1 +kWhirlpoolAcFanMedium LITERAL1 +kWhirlpoolAcFanPos LITERAL1 kWhirlpoolAcGap LITERAL1 kWhirlpoolAcHdrMark LITERAL1 kWhirlpoolAcHdrSpace LITERAL1 +kWhirlpoolAcHeat LITERAL1 +kWhirlpoolAcHourMask LITERAL1 +kWhirlpoolAcLightMask LITERAL1 +kWhirlpoolAcMaxTemp LITERAL1 kWhirlpoolAcMinGap LITERAL1 +kWhirlpoolAcMinTemp LITERAL1 +kWhirlpoolAcMinuteMask LITERAL1 +kWhirlpoolAcModeMask LITERAL1 +kWhirlpoolAcModePos LITERAL1 +kWhirlpoolAcOffTimerPos LITERAL1 +kWhirlpoolAcOnTimerPos LITERAL1 kWhirlpoolAcOneSpace LITERAL1 +kWhirlpoolAcPowerToggleMask LITERAL1 +kWhirlpoolAcPowerTogglePos LITERAL1 kWhirlpoolAcSections LITERAL1 +kWhirlpoolAcSleepMask LITERAL1 +kWhirlpoolAcSleepPos LITERAL1 kWhirlpoolAcStateLength LITERAL1 +kWhirlpoolAcSuperMask LITERAL1 +kWhirlpoolAcSuperPos LITERAL1 +kWhirlpoolAcSwing1Mask LITERAL1 +kWhirlpoolAcSwing2Mask LITERAL1 +kWhirlpoolAcTempMask LITERAL1 +kWhirlpoolAcTempPos LITERAL1 +kWhirlpoolAcTimerEnableMask LITERAL1 kWhirlpoolAcZeroSpace LITERAL1 kWhynterBitMark LITERAL1 kWhynterBitMarkTicks LITERAL1 diff --git a/lib/IRremoteESP8266-2.5.2.03/library.json b/lib/IRremoteESP8266-2.6.0/library.json similarity index 97% rename from lib/IRremoteESP8266-2.5.2.03/library.json rename to lib/IRremoteESP8266-2.6.0/library.json index 3fc14f027..95867de1d 100644 --- a/lib/IRremoteESP8266-2.5.2.03/library.json +++ b/lib/IRremoteESP8266-2.6.0/library.json @@ -1,6 +1,6 @@ { "name": "IRremoteESP8266", - "version": "2.5.2", + "version": "2.6.0", "keywords": "infrared, ir, remote, esp8266", "description": "Send and receive infrared signals with multiple protocols (ESP8266)", "repository": diff --git a/lib/IRremoteESP8266-2.5.2.03/library.properties b/lib/IRremoteESP8266-2.6.0/library.properties similarity index 96% rename from lib/IRremoteESP8266-2.5.2.03/library.properties rename to lib/IRremoteESP8266-2.6.0/library.properties index e71dc4154..f122067c5 100644 --- a/lib/IRremoteESP8266-2.5.2.03/library.properties +++ b/lib/IRremoteESP8266-2.6.0/library.properties @@ -1,5 +1,5 @@ name=IRremoteESP8266 -version=2.5.2 +version=2.6.0 author=Sebastien Warin, Mark Szabo, Ken Shirriff, David Conran maintainer=Mark Szabo, David Conran, Sebastien Warin, Roi Dayan, Massimiliano Pinto sentence=Send and receive infrared signals with multiple protocols (ESP8266) diff --git a/lib/IRremoteESP8266-2.5.2.03/platformio.ini b/lib/IRremoteESP8266-2.6.0/platformio.ini similarity index 83% rename from lib/IRremoteESP8266-2.5.2.03/platformio.ini rename to lib/IRremoteESP8266-2.6.0/platformio.ini index 63c3781e1..b6020c165 100644 --- a/lib/IRremoteESP8266-2.5.2.03/platformio.ini +++ b/lib/IRremoteESP8266-2.6.0/platformio.ini @@ -6,11 +6,13 @@ src_dir = examples/IRrecvDumpV2 build_flags = lib_deps_builtin = lib_deps_external = +lib_ldf_mode = chain+ [env:nodemcuv2] platform = espressif8266 framework = arduino board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} build_flags = ${common.build_flags} lib_deps = ${common.lib_deps_builtin} @@ -20,6 +22,7 @@ lib_deps = platform = espressif8266 framework = arduino board = d1_mini +lib_ldf_mode = ${common.lib_ldf_mode} build_flags = ${common.build_flags} lib_deps = ${common.lib_deps_builtin} diff --git a/lib/IRremoteESP8266-2.5.2.03/pylintrc b/lib/IRremoteESP8266-2.6.0/pylintrc similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/pylintrc rename to lib/IRremoteESP8266-2.6.0/pylintrc diff --git a/lib/IRremoteESP8266-2.5.2.03/src/CPPLINT.cfg b/lib/IRremoteESP8266-2.6.0/src/CPPLINT.cfg similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/src/CPPLINT.cfg rename to lib/IRremoteESP8266-2.6.0/src/CPPLINT.cfg diff --git a/lib/IRremoteESP8266-2.6.0/src/IRac.cpp b/lib/IRremoteESP8266-2.6.0/src/IRac.cpp new file mode 100644 index 000000000..782c147c2 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/src/IRac.cpp @@ -0,0 +1,1125 @@ +// Copyright 2019 David Conran + +// Provide a universal/standard interface for sending A/C nessages. +// It does not provide complete and maximum granular control but tries +// to off most common functionallity across all supported devices. + +#include "IRac.h" +#ifndef UNIT_TEST +#include +#endif + +#include +#ifndef ARDUINO +#include +#endif +#include "IRsend.h" +#include "IRremoteESP8266.h" +#include "ir_Argo.h" +#include "ir_Coolix.h" +#include "ir_Daikin.h" +#include "ir_Fujitsu.h" +#include "ir_Haier.h" +#include "ir_Hitachi.h" +#include "ir_Kelvinator.h" +#include "ir_Midea.h" +#include "ir_Mitsubishi.h" +#include "ir_MitsubishiHeavy.h" +#include "ir_Panasonic.h" +#include "ir_Samsung.h" +#include "ir_Tcl.h" +#include "ir_Teco.h" +#include "ir_Toshiba.h" +#include "ir_Trotec.h" +#include "ir_Vestel.h" +#include "ir_Whirlpool.h" + +IRac::IRac(uint8_t pin) { _pin = pin; } + +// Is the given protocol supported by the IRac class? +bool IRac::isProtocolSupported(const decode_type_t protocol) { + switch (protocol) { +#if SEND_ARGO + case decode_type_t::ARGO: +#endif +#if SEND_COOLIX + case decode_type_t::COOLIX: +#endif +#if SEND_DAIKIN + case decode_type_t::DAIKIN: +#endif +#if SEND_DAIKIN2 + case decode_type_t::DAIKIN2: +#endif +#if SEND_DAIKIN216 + case decode_type_t::DAIKIN216: +#endif +#if SEND_FUJITSU_AC + case decode_type_t::FUJITSU_AC: +#endif +#if SEND_GREE + case decode_type_t::GREE: +#endif +#if SEND_HAIER_AC + case decode_type_t::HAIER_AC: +#endif +#if SEND_HAIER_AC_YRW02 + case decode_type_t::HAIER_AC_YRW02: +#endif +#if SEND_HITACHI_AC + case decode_type_t::HITACHI_AC: +#endif +#if SEND_KELVINATOR + case decode_type_t::KELVINATOR: +#endif +#if SEND_MIDEA + case decode_type_t::MIDEA: +#endif +#if SEND_MITSUBISHI_AC + case decode_type_t::MITSUBISHI_AC: +#endif +#if SEND_MITSUBISHIHEAVY + case decode_type_t::MITSUBISHI_HEAVY_88: + case decode_type_t::MITSUBISHI_HEAVY_152: +#endif +#if SEND_PANASONIC_AC + case decode_type_t::PANASONIC_AC: +#endif +#if SEND_SAMSUNG_AC + case decode_type_t::SAMSUNG_AC: +#endif +#if SEND_TCL112AC + case decode_type_t::TCL112AC: +#endif +#if SEND_TECO + case decode_type_t::TECO: +#endif +#if SEND_TOSHIBA_AC + case decode_type_t::TOSHIBA_AC: +#endif +#if SEND_TROTEC + case decode_type_t::TROTEC: +#endif +#if SEND_VESTEL_AC + case decode_type_t::VESTEL_AC: +#endif +#if SEND_WHIRLPOOL_AC + case decode_type_t::WHIRLPOOL_AC: +#endif + return true; + default: + return false; + } +} + +#if SEND_ARGO +void IRac::argo(IRArgoAC *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const bool turbo, const int16_t sleep) { + ac->setPower(on); + switch (mode) { + case stdAc::opmode_t::kCool: + ac->setCoolMode(kArgoCoolOn); + break; + case stdAc::opmode_t::kHeat: + ac->setHeatMode(kArgoHeatOn); + break; + case stdAc::opmode_t::kDry: + ac->setCoolMode(kArgoCoolHum); + break; + default: // No idea how to set Fan mode. + ac->setCoolMode(kArgoCoolAuto); + } + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setFlap(ac->convertSwingV(swingv)); + // No Quiet setting available. + // No Light setting available. + // No Filter setting available. + ac->setMax(turbo); + // No Economy setting available. + // No Clean setting available. + // No Beep setting available. + ac->setNight(sleep >= 0); // Convert to a boolean. + ac->send(); +} +#endif // SEND_ARGO + +#if SEND_COOLIX +void IRac::coolix(IRCoolixAC *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool turbo, const bool light, const bool clean, + const int16_t sleep) { + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + // No Filter setting available. + // No Beep setting available. + // No Clock setting available. + // No Econo setting available. + // No Quiet setting available. + if (swingv != stdAc::swingv_t::kOff || swingh != stdAc::swingh_t::kOff) { + // Swing has a special command that needs to be sent independently. + ac->setSwing(); + ac->send(); + } + if (turbo) { + // Turbo has a special command that needs to be sent independently. + ac->setTurbo(); + ac->send(); + } + if (sleep > 0) { + // Sleep has a special command that needs to be sent independently. + ac->setSleep(); + ac->send(); + } + if (light) { + // Light has a special command that needs to be sent independently. + ac->setLed(); + ac->send(); + } + if (clean) { + // Clean has a special command that needs to be sent independently. + ac->setClean(); + ac->send(); + } + // Power gets done last, as off has a special command. + ac->setPower(on); + ac->send(); +} +#endif // SEND_COOLIX + +#if SEND_DAIKIN +void IRac::daikin(IRDaikinESP *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const bool econo, + const bool clean) { + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical((int8_t)swingv >= 0); + ac->setSwingHorizontal((int8_t)swingh >= 0); + ac->setQuiet(quiet); + // No Light setting available. + // No Filter setting available. + ac->setPowerful(turbo); + ac->setEcono(econo); + ac->setMold(clean); + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_DAIKIN + +#if SEND_DAIKIN2 +void IRac::daikin2(IRDaikin2 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const bool light, + const bool econo, const bool filter, const bool clean, + const bool beep, const int16_t sleep, const int16_t clock) { + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical(ac->convertSwingV(swingv)); + ac->setSwingHorizontal((int8_t)swingh >= 0); + ac->setQuiet(quiet); + ac->setLight(light); + ac->setPowerful(turbo); + ac->setEcono(econo); + ac->setPurify(filter); + ac->setMold(clean); + ac->setBeep(beep); + if (sleep > 0) ac->enableSleepTimer(sleep); + if (clock >= 0) ac->setCurrentTime(clock); + ac->send(); +} +#endif // SEND_DAIKIN2 + +#if SEND_DAIKIN216 +void IRac::daikin216(IRDaikin216 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet) { + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical((int8_t)swingv >= 0); + ac->setSwingHorizontal((int8_t)swingh >= 0); + ac->setQuiet(quiet); + ac->send(); +} +#endif // SEND_DAIKIN216 + +#if SEND_FUJITSU_AC +void IRac::fujitsu(IRFujitsuAC *ac, const fujitsu_ac_remote_model_t model, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet) { + ac->setModel(model); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFanSpeed(ac->convertFan(fan)); + uint8_t swing = kFujitsuAcSwingOff; + if (swingv > stdAc::swingv_t::kOff) swing |= kFujitsuAcSwingVert; + if (swingh > stdAc::swingh_t::kOff) swing |= kFujitsuAcSwingHoriz; + ac->setSwing(swing); + if (quiet) ac->setFanSpeed(kFujitsuAcFanQuiet); + // No Turbo setting available. + // No Light setting available. + // No Econo setting available. + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + if (!on) ac->off(); + ac->send(); +} +#endif // SEND_FUJITSU_AC + +#if SEND_GREE +void IRac::gree(IRGreeAC *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const bool turbo, const bool light, const bool clean, + const int16_t sleep) { + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical(swingv == stdAc::swingv_t::kAuto, // Set auto flag. + ac->convertSwingV(swingv)); + ac->setLight(light); + ac->setTurbo(turbo); + ac->setXFan(clean); + ac->setSleep(sleep >= 0); // Sleep on this A/C is either on or off. + // No Horizontal Swing setting available. + // No Filter setting available. + // No Beep setting available. + // No Quiet setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_GREE + +#if SEND_HAIER_AC +void IRac::haier(IRHaierAC *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const bool filter, const int16_t sleep, const int16_t clock) { + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwing(ac->convertSwingV(swingv)); + // No Horizontal Swing setting available. + // No Quiet setting available. + // No Turbo setting available. + // No Light setting available. + ac->setHealth(filter); + // No Clean setting available. + // No Beep setting available. + ac->setSleep(sleep >= 0); // Sleep on this A/C is either on or off. + if (clock >=0) ac->setCurrTime(clock); + if (on) + ac->setCommand(kHaierAcCmdOn); + else + ac->setCommand(kHaierAcCmdOff); + ac->send(); +} +#endif // SEND_HAIER_AC + +#if SEND_HAIER_AC_YRW02 +void IRac::haierYrwo2(IRHaierACYRW02 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const bool turbo, + const bool filter, const int16_t sleep) { + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwing(ac->convertSwingV(swingv)); + // No Horizontal Swing setting available. + // No Quiet setting available. + ac->setTurbo(turbo); + // No Light setting available. + ac->setHealth(filter); + // No Clean setting available. + // No Beep setting available. + ac->setSleep(sleep >= 0); // Sleep on this A/C is either on or off. + ac->setPower(on); + ac->send(); +} +#endif // SEND_HAIER_AC_YRW02 + +#if SEND_HITACHI_AC +void IRac::hitachi(IRHitachiAc *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh) { + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical(swingv != stdAc::swingv_t::kOff); + ac->setSwingHorizontal(swingh != stdAc::swingh_t::kOff); + // No Quiet setting available. + // No Turbo setting available. + // No Light setting available. + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_HITACHI_AC + +#if SEND_KELVINATOR +void IRac::kelvinator(IRKelvinatorAC *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const bool light, + const bool filter, const bool clean) { + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan((uint8_t)fan); // No conversion needed. + ac->setSwingVertical((int8_t)swingv >= 0); + ac->setSwingHorizontal((int8_t)swingh >= 0); + ac->setQuiet(quiet); + ac->setTurbo(turbo); + ac->setLight(light); + ac->setIonFilter(filter); + ac->setXFan(clean); + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_KELVINATOR + +#if SEND_MIDEA +void IRac::midea(IRMideaAC *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, const int16_t sleep) { + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees, true); // true means use Celsius. + ac->setFan(ac->convertFan(fan)); + // No Vertical swing setting available. + // No Horizontal swing setting available. + // No Quiet setting available. + // No Turbo setting available. + // No Light setting available. + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + ac->setSleep(sleep >= 0); // Sleep on this A/C is either on or off. + // No Clock setting available. + ac->send(); +} +#endif // SEND_MIDEA + +#if SEND_MITSUBISHI_AC +void IRac::mitsubishi(IRMitsubishiAC *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const bool quiet, const int16_t clock) { + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setVane(ac->convertSwingV(swingv)); + // No Horizontal swing setting available. + if (quiet) ac->setFan(kMitsubishiAcFanSilent); + // No Turbo setting available. + // No Light setting available. + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + if (clock >= 0) ac->setClock(clock / 10); // Clock is in 10 min increments. + ac->send(); +} +#endif // SEND_MITSUBISHI_AC + +#if SEND_MITSUBISHIHEAVY +void IRac::mitsubishiHeavy88(IRMitsubishiHeavy88Ac *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const stdAc::swingh_t swingh, + const bool turbo, const bool econo, + const bool clean) { + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical(ac->convertSwingV(swingv)); + ac->setSwingHorizontal(ac->convertSwingH(swingh)); + // No Quiet setting available. + ac->setTurbo(turbo); + // No Light setting available. + ac->setEcono(econo); + // No Filter setting available. + ac->setClean(clean); + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + ac->send(); +} + +void IRac::mitsubishiHeavy152(IRMitsubishiHeavy152Ac *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, + const bool econo, const bool filter, + const bool clean, const int16_t sleep) { + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical(ac->convertSwingV(swingv)); + ac->setSwingHorizontal(ac->convertSwingH(swingh)); + ac->setSilent(quiet); + ac->setTurbo(turbo); + // No Light setting available. + ac->setEcono(econo); + ac->setClean(clean); + ac->setFilter(filter); + // No Beep setting available. + ac->setNight(sleep >= 0); // Sleep is either on/off, so convert to boolean. + // No Clock setting available. + ac->send(); +} +#endif // SEND_MITSUBISHIHEAVY + +#if SEND_PANASONIC_AC +void IRac::panasonic(IRPanasonicAc *ac, const panasonic_ac_remote_model_t model, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const int16_t clock) { + ac->setModel(model); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical(ac->convertSwingV(swingv)); + ac->setSwingHorizontal(ac->convertSwingH(swingh)); + ac->setQuiet(quiet); + ac->setPowerful(turbo); + // No Light setting available. + // No Econo setting available. + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + if (clock >= 0) ac->setClock(clock); + ac->send(); +} +#endif // SEND_PANASONIC_AC + +#if SEND_SAMSUNG_AC +void IRac::samsung(IRSamsungAc *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const bool quiet, const bool turbo, const bool clean, + const bool beep, const bool sendOnOffHack) { + if (sendOnOffHack) { + // Use a hack to for the unit on or off. + // See: https://github.com/markszabo/IRremoteESP8266/issues/604#issuecomment-475020036 + if (on) + ac->sendOn(); + else + ac->sendOff(); + } + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwing(swingv != stdAc::swingv_t::kOff); + // No Horizontal swing setting available. + ac->setQuiet(quiet); + if (turbo) ac->setFan(kSamsungAcFanTurbo); + // No Light setting available. + // No Econo setting available. + // No Filter setting available. + ac->setClean(clean); + ac->setBeep(beep); + // No Sleep setting available. + // No Clock setting available. + // Do setMode() again as it can affect fan speed. + ac->setMode(ac->convertMode(mode)); + ac->send(); +} +#endif // SEND_SAMSUNG_AC + +#if SEND_TCL112AC +void IRac::tcl112(IRTcl112Ac *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool turbo, const bool light, const bool econo, + const bool filter) { + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical(swingv != stdAc::swingv_t::kOff); + ac->setSwingHorizontal(swingh != stdAc::swingh_t::kOff); + // No Quiet setting available. + ac->setTurbo(turbo); + ac->setLight(light); + ac->setEcono(econo); + ac->setHealth(filter); + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_TCL112AC + +#if SEND_TECO +void IRac::teco(IRTecoAc *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const int16_t sleep) { + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwing(swingv != stdAc::swingv_t::kOff); + // No Horizontal swing setting available. + // No Quiet setting available. + // No Turbo setting available. + // No Light setting available. + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. + // No Clock setting available. + ac->send(); +} +#endif // SEND_TECO + +#if SEND_TOSHIBA_AC +void IRac::toshiba(IRToshibaAC *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan) { + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + // No Vertical swing setting available. + // No Horizontal swing setting available. + // No Quiet setting available. + // No Turbo setting available. + // No Light setting available. + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_TOSHIBA_AC + +#if SEND_TROTEC +void IRac::trotec(IRTrotecESP *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const int16_t sleep) { + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setSpeed(ac->convertFan(fan)); + // No Vertical swing setting available. + // No Horizontal swing setting available. + // No Quiet setting available. + // No Turbo setting available. + // No Light setting available. + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. + // No Clock setting available. + ac->send(); +} +#endif // SEND_TROTEC + +#if SEND_VESTEL_AC +void IRac::vestel(IRVestelAc *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const bool turbo, const bool filter, const int16_t sleep, + const int16_t clock, const bool sendNormal) { + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwing(swingv != stdAc::swingv_t::kOff); + // No Horizontal swing setting available. + // No Quiet setting available. + ac->setTurbo(turbo); + // No Light setting available. + ac->setIon(filter); + // No Clean setting available. + // No Beep setting available. + ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. + if (sendNormal) ac->send(); // Send the normal message. + if (clock >= 0) { + ac->setTime(clock); + ac->send(); // Setting the clock requires a different "timer" message. + } +} +#endif // SEND_VESTEL_AC + +#if SEND_WHIRLPOOL_AC +void IRac::whirlpool(IRWhirlpoolAc *ac, const whirlpool_ac_remote_model_t model, + const bool on, const stdAc::opmode_t mode, + const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const bool turbo, const bool light, + const int16_t sleep, const int16_t clock) { + ac->setModel(model); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwing(swingv != stdAc::swingv_t::kOff); + // No Horizontal swing setting available. + // No Quiet setting available. + ac->setSuper(turbo); + ac->setLight(light); + // No Filter setting available + // No Clean setting available. + // No Beep setting available. + ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. + if (clock >= 0) ac->setClock(clock); + ac->setPowerToggle(on); + ac->send(); +} +#endif // SEND_WHIRLPOOL_AC + +// Send A/C message for a given device using common A/C settings. +// Args: +// vendor: The type of A/C protocol to use. +// model: The specific model of A/C if supported/applicable. +// on: Should the unit be powered on? (or in some cases, toggled) +// mode: What operating mode should the unit perform? e.g. Cool, Heat etc. +// degrees: What temperature should the unit be set to? +// celsius: Use degreees Celsius, otherwise Fahrenheit. +// fan: Fan speed. +// The following args are all "if supported" by the underlying A/C classes. +// swingv: Control the vertical swing of the vanes. +// swingh: Control the horizontal swing of the vanes. +// quiet: Set the unit to quiet (fan) operation mode. +// turbo: Set the unit to turbo operating mode. e.g. Max fan & cooling etc. +// econo: Set the unit to economical operating mode. +// light: Turn on the display/LEDs etc. +// filter: Turn on any particle/ion/allergy filter etc. +// clean: Turn on any settings to reduce mold etc. (Not self-clean mode.) +// beep: Control if the unit beeps upon receiving commands. +// sleep: Nr. of mins of sleep mode, or use sleep mode. (< 0 means off.) +// clock: Nr. of mins past midnight to set the clock to. (< 0 means off.) +// Returns: +// boolean: True, if accepted/converted/attempted. False, if unsupported. +bool IRac::sendAc(const decode_type_t vendor, const int16_t model, + const bool power, const stdAc::opmode_t mode, + const float degrees, const bool celsius, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const bool econo, + const bool light, const bool filter, const bool clean, + const bool beep, const int16_t sleep, const int16_t clock) { + // Convert the temperature to Celsius. + float degC; + bool on = power; + if (celsius) + degC = degrees; + else + degC = (degrees - 32.0) * (5.0 / 9.0); + // A hack for Home Assistant, it appears to need/want an Off opmode. + if (mode == stdAc::opmode_t::kOff) on = false; + // Per vendor settings & setup. + switch (vendor) { +#if SEND_ARGO + case ARGO: + { + IRArgoAC ac(_pin); + argo(&ac, on, mode, degC, fan, swingv, turbo, sleep); + break; + } +#endif // SEND_DAIKIN +#if SEND_COOLIX + case COOLIX: + { + IRCoolixAC ac(_pin); + coolix(&ac, on, mode, degC, fan, swingv, swingh, + quiet, turbo, econo, clean); + break; + } +#endif // SEND_DAIKIN +#if SEND_DAIKIN + case DAIKIN: + { + IRDaikinESP ac(_pin); + daikin(&ac, on, mode, degC, fan, swingv, swingh, + quiet, turbo, econo, clean); + break; + } +#endif // SEND_DAIKIN +#if SEND_DAIKIN2 + case DAIKIN2: + { + IRDaikin2 ac(_pin); + daikin2(&ac, on, mode, degC, fan, swingv, swingh, quiet, turbo, + light, econo, filter, clean, beep, sleep, clock); + break; + } +#endif // SEND_DAIKIN216 +#if SEND_DAIKIN216 + case DAIKIN216: + { + IRDaikin216 ac(_pin); + daikin216(&ac, on, mode, degC, fan, swingv, swingh, quiet); + break; + } +#endif // SEND_DAIKIN216 +#if SEND_FUJITSU_AC + case FUJITSU_AC: + { + IRFujitsuAC ac(_pin); + ac.begin(); + fujitsu(&ac, (fujitsu_ac_remote_model_t)model, on, mode, degC, fan, + swingv, swingh, quiet); + break; + } +#endif // SEND_FUJITSU_AC +#if SEND_GREE + case GREE: + { + IRGreeAC ac(_pin); + ac.begin(); + gree(&ac, on, mode, degC, fan, swingv, light, turbo, clean, sleep); + break; + } +#endif // SEND_GREE +#if SEND_HAIER_AC + case HAIER_AC: + { + IRHaierAC ac(_pin); + ac.begin(); + haier(&ac, on, mode, degC, fan, swingv, filter, sleep, clock); + break; + } +#endif // SEND_HAIER_AC +#if SEND_HAIER_AC_YRW02 + case HAIER_AC_YRW02: + { + IRHaierACYRW02 ac(_pin); + ac.begin(); + haierYrwo2(&ac, on, mode, degC, fan, swingv, turbo, filter, sleep); + break; + } +#endif // SEND_HAIER_AC_YRW02 +#if SEND_HITACHI_AC + case HITACHI_AC: + { + IRHitachiAc ac(_pin); + ac.begin(); + hitachi(&ac, on, mode, degC, fan, swingv, swingh); + break; + } +#endif // SEND_HITACHI_AC +#if SEND_KELVINATOR + case KELVINATOR: + { + IRKelvinatorAC ac(_pin); + ac.begin(); + kelvinator(&ac, on, mode, degC, fan, swingv, swingh, quiet, turbo, + light, filter, clean); + break; + } +#endif // SEND_KELVINATOR +#if SEND_MIDEA + case MIDEA: + { + IRMideaAC ac(_pin); + ac.begin(); + midea(&ac, on, mode, degC, fan, sleep); + break; + } +#endif // SEND_MIDEA +#if SEND_MITSUBISHI_AC + case MITSUBISHI_AC: + { + IRMitsubishiAC ac(_pin); + ac.begin(); + mitsubishi(&ac, on, mode, degC, fan, swingv, quiet, clock); + break; + } +#endif // SEND_MITSUBISHI_AC +#if SEND_MITSUBISHIHEAVY + case MITSUBISHI_HEAVY_88: + { + IRMitsubishiHeavy88Ac ac(_pin); + ac.begin(); + mitsubishiHeavy88(&ac, on, mode, degC, fan, swingv, swingh, + turbo, econo, clean); + break; + } + case MITSUBISHI_HEAVY_152: + { + IRMitsubishiHeavy152Ac ac(_pin); + ac.begin(); + mitsubishiHeavy152(&ac, on, mode, degC, fan, swingv, swingh, + quiet, turbo, econo, filter, clean, sleep); + break; + } +#endif // SEND_MITSUBISHIHEAVY +#if SEND_PANASONIC_AC + case PANASONIC_AC: + { + IRPanasonicAc ac(_pin); + ac.begin(); + panasonic(&ac, (panasonic_ac_remote_model_t)model, on, mode, degC, fan, + swingv, swingh, quiet, turbo, clock); + break; + } +#endif // SEND_PANASONIC_AC +#if SEND_SAMSUNG_AC + case SAMSUNG_AC: + { + IRSamsungAc ac(_pin); + ac.begin(); + samsung(&ac, on, mode, degC, fan, swingv, quiet, turbo, clean, beep); + break; + } +#endif // SEND_SAMSUNG_AC +#if SEND_TCL112AC + case TCL112AC: + { + IRTcl112Ac ac(_pin); + ac.begin(); + tcl112(&ac, on, mode, degC, fan, swingv, swingh, turbo, light, econo, + filter); + break; + } +#endif // SEND_TCL112AC +#if SEND_TECO + case TECO: + { + IRTecoAc ac(_pin); + ac.begin(); + teco(&ac, on, mode, degC, fan, swingv, sleep); + break; + } +#endif // SEND_TECO +#if SEND_TOSHIBA_AC + case TOSHIBA_AC: + { + IRToshibaAC ac(_pin); + ac.begin(); + toshiba(&ac, on, mode, degC, fan); + break; + } +#endif // SEND_TOSHIBA_AC +#if SEND_TROTEC + case TROTEC: + { + IRTrotecESP ac(_pin); + ac.begin(); + trotec(&ac, on, mode, degC, fan, sleep); + break; + } +#endif // SEND_TROTEC +#if SEND_VESTEL_AC + case VESTEL_AC: + { + IRVestelAc ac(_pin); + ac.begin(); + vestel(&ac, on, mode, degC, fan, swingv, turbo, filter, sleep, clock); + break; + } +#endif // SEND_VESTEL_AC +#if SEND_WHIRLPOOL_AC + case WHIRLPOOL_AC: + { + IRWhirlpoolAc ac(_pin); + ac.begin(); + whirlpool(&ac, (whirlpool_ac_remote_model_t)model, on, mode, degC, fan, + swingv, turbo, light, sleep, clock); + break; + } +#endif // SEND_WHIRLPOOL_AC + default: + return false; // Fail, didn't match anything. + } + return true; // Success. +} + +stdAc::opmode_t IRac::strToOpmode(const char *str, + const stdAc::opmode_t def) { + if (!strcmp(str, "AUTO") || !strcmp(str, "AUTOMATIC")) + return stdAc::opmode_t::kAuto; + else if (!strcmp(str, "OFF") || !strcmp(str, "STOP")) + return stdAc::opmode_t::kOff; + else if (!strcmp(str, "COOL") || !strcmp(str, "COOLING")) + return stdAc::opmode_t::kCool; + else if (!strcmp(str, "HEAT") || !strcmp(str, "HEATING")) + return stdAc::opmode_t::kHeat; + else if (!strcmp(str, "DRY") || !strcmp(str, "DRYING") || + !strcmp(str, "DEHUMIDIFY")) + return stdAc::opmode_t::kDry; + else if (!strcmp(str, "FAN") || !strcmp(str, "FANONLY") || + !strcmp(str, "FAN_ONLY")) + return stdAc::opmode_t::kFan; + else + return def; +} + +stdAc::fanspeed_t IRac::strToFanspeed(const char *str, + const stdAc::fanspeed_t def) { + if (!strcmp(str, "AUTO") || !strcmp(str, "AUTOMATIC")) + return stdAc::fanspeed_t::kAuto; + else if (!strcmp(str, "MIN") || !strcmp(str, "MINIMUM") || + !strcmp(str, "LOWEST")) + return stdAc::fanspeed_t::kMin; + else if (!strcmp(str, "LOW")) + return stdAc::fanspeed_t::kLow; + else if (!strcmp(str, "MED") || !strcmp(str, "MEDIUM") || + !strcmp(str, "MID")) + return stdAc::fanspeed_t::kMedium; + else if (!strcmp(str, "HIGH") || !strcmp(str, "HI")) + return stdAc::fanspeed_t::kHigh; + else if (!strcmp(str, "MAX") || !strcmp(str, "MAXIMUM") || + !strcmp(str, "HIGHEST")) + return stdAc::fanspeed_t::kMax; + else + return def; +} + +stdAc::swingv_t IRac::strToSwingV(const char *str, + const stdAc::swingv_t def) { + if (!strcmp(str, "AUTO") || !strcmp(str, "AUTOMATIC") || + !strcmp(str, "ON") || !strcmp(str, "SWING")) + return stdAc::swingv_t::kAuto; + else if (!strcmp(str, "OFF") || !strcmp(str, "STOP")) + return stdAc::swingv_t::kOff; + else if (!strcmp(str, "MIN") || !strcmp(str, "MINIMUM") || + !strcmp(str, "LOWEST") || !strcmp(str, "BOTTOM") || + !strcmp(str, "DOWN")) + return stdAc::swingv_t::kLowest; + else if (!strcmp(str, "LOW")) + return stdAc::swingv_t::kLow; + else if (!strcmp(str, "MID") || !strcmp(str, "MIDDLE") || + !strcmp(str, "MED") || !strcmp(str, "MEDIUM") || + !strcmp(str, "CENTRE") || !strcmp(str, "CENTER")) + return stdAc::swingv_t::kMiddle; + else if (!strcmp(str, "HIGH") || !strcmp(str, "HI")) + return stdAc::swingv_t::kHigh; + else if (!strcmp(str, "HIGHEST") || !strcmp(str, "MAX") || + !strcmp(str, "MAXIMUM") || !strcmp(str, "TOP") || + !strcmp(str, "UP")) + return stdAc::swingv_t::kHighest; + else + return def; +} + +stdAc::swingh_t IRac::strToSwingH(const char *str, + const stdAc::swingh_t def) { + if (!strcmp(str, "AUTO") || !strcmp(str, "AUTOMATIC") || + !strcmp(str, "ON") || !strcmp(str, "SWING")) + return stdAc::swingh_t::kAuto; + else if (!strcmp(str, "OFF") || !strcmp(str, "STOP")) + return stdAc::swingh_t::kOff; + else if (!strcmp(str, "LEFTMAX") || !strcmp(str, "LEFT MAX") || + !strcmp(str, "MAXLEFT") || !strcmp(str, "MAX LEFT") || + !strcmp(str, "FARLEFT") || !strcmp(str, "FAR LEFT")) + return stdAc::swingh_t::kLeftMax; + else if (!strcmp(str, "LEFT")) + return stdAc::swingh_t::kLeft; + else if (!strcmp(str, "MID") || !strcmp(str, "MIDDLE") || + !strcmp(str, "MED") || !strcmp(str, "MEDIUM") || + !strcmp(str, "CENTRE") || !strcmp(str, "CENTER")) + return stdAc::swingh_t::kMiddle; + else if (!strcmp(str, "RIGHT")) + return stdAc::swingh_t::kRight; + else if (!strcmp(str, "RIGHTMAX") || !strcmp(str, "RIGHT MAX") || + !strcmp(str, "MAXRIGHT") || !strcmp(str, "MAX RIGHT") || + !strcmp(str, "FARRIGHT") || !strcmp(str, "FAR RIGHT")) + return stdAc::swingh_t::kRightMax; + else + return def; +} + +// Assumes str is upper case or an integer >= 1. +int16_t IRac::strToModel(const char *str, const int16_t def) { + // Fujitsu A/C models + if (!strcmp(str, "ARRAH2E")) { + return fujitsu_ac_remote_model_t::ARRAH2E; + } else if (!strcmp(str, "ARDB1")) { + return fujitsu_ac_remote_model_t::ARDB1; + // Panasonic A/C families + } else if (!strcmp(str, "LKE") || !strcmp(str, "PANASONICLKE")) { + return panasonic_ac_remote_model_t::kPanasonicLke; + } else if (!strcmp(str, "NKE") || !strcmp(str, "PANASONICNKE")) { + return panasonic_ac_remote_model_t::kPanasonicNke; + } else if (!strcmp(str, "DKE") || !strcmp(str, "PANASONICDKE")) { + return panasonic_ac_remote_model_t::kPanasonicDke; + } else if (!strcmp(str, "JKE") || !strcmp(str, "PANASONICJKE")) { + return panasonic_ac_remote_model_t::kPanasonicJke; + } else if (!strcmp(str, "CKP") || !strcmp(str, "PANASONICCKP")) { + return panasonic_ac_remote_model_t::kPanasonicCkp; + } else if (!strcmp(str, "RKR") || !strcmp(str, "PANASONICRKR")) { + return panasonic_ac_remote_model_t::kPanasonicRkr; + // Whirlpool A/C models + } else if (!strcmp(str, "DG11J13A") || !strcmp(str, "DG11J104") || + !strcmp(str, "DG11J1-04")) { + return whirlpool_ac_remote_model_t::DG11J13A; + } else if (!strcmp(str, "DG11J191")) { + return whirlpool_ac_remote_model_t::DG11J191; + } else { + int16_t number = atoi(str); + if (number > 0) + return number; + else + return def; + } +} + +// Assumes str is upper case. +bool IRac::strToBool(const char *str, const bool def) { + if (!strcmp(str, "ON") || !strcmp(str, "1") || !strcmp(str, "YES") || + !strcmp(str, "TRUE")) + return true; + else if (!strcmp(str, "OFF") || !strcmp(str, "0") || + !strcmp(str, "NO") || !strcmp(str, "FALSE")) + return false; + else + return def; +} diff --git a/lib/IRremoteESP8266-2.6.0/src/IRac.h b/lib/IRremoteESP8266-2.6.0/src/IRac.h new file mode 100644 index 000000000..ce8d50507 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/src/IRac.h @@ -0,0 +1,248 @@ +#ifndef IRAC_H_ +#define IRAC_H_ + +// Copyright 2019 David Conran + +#ifndef UNIT_TEST +#include +#endif +#ifndef ARDUINO +#include +#endif +#include "IRremoteESP8266.h" +#include "ir_Argo.h" +#include "ir_Coolix.h" +#include "ir_Daikin.h" +#include "ir_Fujitsu.h" +#include "ir_Gree.h" +#include "ir_Haier.h" +#include "ir_Hitachi.h" +#include "ir_Kelvinator.h" +#include "ir_Midea.h" +#include "ir_Mitsubishi.h" +#include "ir_MitsubishiHeavy.h" +#include "ir_Panasonic.h" +#include "ir_Samsung.h" +#include "ir_Tcl.h" +#include "ir_Teco.h" +#include "ir_Toshiba.h" +#include "ir_Trotec.h" +#include "ir_Vestel.h" +#include "ir_Whirlpool.h" + +class IRac { + public: + explicit IRac(uint8_t pin); + static bool isProtocolSupported(const decode_type_t protocol); + bool sendAc(const decode_type_t vendor, const int16_t model, + const bool power, const stdAc::opmode_t mode, const float degrees, + const bool celsius, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const bool econo, + const bool light, const bool filter, const bool clean, + const bool beep, const int16_t sleep = -1, + const int16_t clock = -1); + + static bool strToBool(const char *str, const bool def = false); + static int16_t strToModel(const char *str, const int16_t def = -1); + static stdAc::opmode_t strToOpmode( + const char *str, const stdAc::opmode_t def = stdAc::opmode_t::kAuto); + static stdAc::fanspeed_t strToFanspeed( + const char *str, + const stdAc::fanspeed_t def = stdAc::fanspeed_t::kAuto); + static stdAc::swingv_t strToSwingV( + const char *str, const stdAc::swingv_t def = stdAc::swingv_t::kOff); + static stdAc::swingh_t strToSwingH( + const char *str, const stdAc::swingh_t def = stdAc::swingh_t::kOff); +#ifndef UNIT_TEST + + private: +#endif + uint8_t _pin; +#if SEND_ARGO + void argo(IRArgoAC *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const bool turbo, const int16_t sleep = -1); +#endif // SEND_ARGO +#if SEND_COOLIX + void coolix(IRCoolixAC *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool turbo, const bool light, const bool clean, + const int16_t sleep = -1); +#endif // SEND_COOLIX +#if SEND_DAIKIN + void daikin(IRDaikinESP *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const bool econo, + const bool clean); +#endif // SEND_DAIKIN +#if SEND_DAIKIN2 + void daikin2(IRDaikin2 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const bool light, + const bool econo, const bool filter, const bool clean, + const bool beep, const int16_t sleep = -1, + const int16_t clock = -1); +#endif // SEND_DAIKIN2 +#if SEND_DAIKIN216 +void daikin216(IRDaikin216 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet); +#endif // SEND_DAIKIN216 +#if SEND_FUJITSU_AC + void fujitsu(IRFujitsuAC *ac, const fujitsu_ac_remote_model_t model, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet); +#endif // SEND_FUJITSU_AC +#if SEND_GREE + void gree(IRGreeAC *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const bool turbo, const bool light, const bool clean, + const int16_t sleep = -1); +#endif // SEND_GREE +#if SEND_HAIER_AC + void haier(IRHaierAC *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const bool filter, const int16_t sleep = -1, + const int16_t clock = -1); +#endif // SEND_HAIER_AC +#if SEND_HAIER_AC_YRW02 + void haierYrwo2(IRHaierACYRW02 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const bool turbo, const bool filter, + const int16_t sleep = -1); +#endif // SEND_HAIER_AC_YRW02 +#if SEND_HITACHI_AC + void hitachi(IRHitachiAc *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh); +#endif // SEND_HITACHI_AC +#if SEND_KELVINATOR + void kelvinator(IRKelvinatorAC *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const bool light, + const bool filter, const bool clean); +#endif // SEND_KELVINATOR +#if SEND_MIDEA + void midea(IRMideaAC *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, const int16_t sleep = -1); +#endif // SEND_MIDEA +#if SEND_MITSUBISHI_AC + void mitsubishi(IRMitsubishiAC *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const bool quiet, const int16_t clock = -1); +#endif // SEND_MITSUBISHI_AC +#if SEND_MITSUBISHIHEAVY + void mitsubishiHeavy88(IRMitsubishiHeavy88Ac *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const stdAc::swingh_t swingh, + const bool turbo, const bool econo, const bool clean); + void mitsubishiHeavy152(IRMitsubishiHeavy152Ac *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const bool econo, + const bool filter, const bool clean, + const int16_t sleep = -1); +#endif // SEND_MITSUBISHIHEAVY +#if SEND_PANASONIC_AC + void panasonic(IRPanasonicAc *ac, const panasonic_ac_remote_model_t model, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const int16_t clock = -1); +#endif // SEND_PANASONIC_AC +#if SEND_SAMSUNG_AC + void samsung(IRSamsungAc *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const bool quiet, const bool turbo, const bool clean, + const bool beep, const bool sendOnOffHack = true); +#endif // SEND_SAMSUNG_AC +#if SEND_TCL112AC + void tcl112(IRTcl112Ac *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool turbo, const bool light, const bool econo, + const bool filter); +#endif // SEND_TCL112AC +#if SEND_TECO + void teco(IRTecoAc *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const int16_t sleep = -1); +#endif // SEND_TECO +#if SEND_TOSHIBA_AC + void toshiba(IRToshibaAC *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan); +#endif // SEND_TOSHIBA_AC +#if SEND_TROTEC + void trotec(IRTrotecESP *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, const int16_t sleep = -1); +#endif // SEND_TROTEC +#if SEND_VESTEL_AC + void vestel(IRVestelAc *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const bool turbo, const bool filter, + const int16_t sleep = -1, const int16_t clock = -1, + const bool sendNormal = true); +#endif // SEND_VESTEL_AC +#if SEND_WHIRLPOOL_AC + void whirlpool(IRWhirlpoolAc *ac, const whirlpool_ac_remote_model_t model, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const bool turbo, const bool light, + const int16_t sleep = -1, const int16_t clock = -1); +#endif // SEND_WHIRLPOOL_AC +}; // IRac class + +// Structure to hold a common A/C state. +typedef struct { + decode_type_t protocol; + int16_t model; + bool power; + stdAc::opmode_t mode; + float degrees; + bool celsius; + stdAc::fanspeed_t fanspeed; + stdAc::swingv_t swingv; + stdAc::swingh_t swingh; + bool quiet; + bool turbo; + bool econo; + bool light; + bool filter; + bool clean; + bool beep; + int16_t sleep; + int16_t clock; +} commonAcState_t; +#endif // IRAC_H_ diff --git a/lib/IRremoteESP8266-2.5.2.03/src/IRrecv.cpp b/lib/IRremoteESP8266-2.6.0/src/IRrecv.cpp similarity index 95% rename from lib/IRremoteESP8266-2.5.2.03/src/IRrecv.cpp rename to lib/IRremoteESP8266-2.6.0/src/IRrecv.cpp index b2c984396..eac868084 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/IRrecv.cpp +++ b/lib/IRremoteESP8266-2.6.0/src/IRrecv.cpp @@ -364,6 +364,10 @@ bool IRrecv::decode(decode_results *results, irparams_t *save) { DPRINTLN("Attempting SAMSUNG decode"); if (decodeSAMSUNG(results)) return true; #endif +#if DECODE_SAMSUNG36 + DPRINTLN("Attempting Samsung36 decode"); + if (decodeSamsung36(results)) return true; +#endif #if DECODE_WHYNTER DPRINTLN("Attempting Whynter decode"); if (decodeWhynter(results)) return true; @@ -394,6 +398,14 @@ bool IRrecv::decode(decode_results *results, irparams_t *save) { DPRINTLN("Attempting Daikin decode"); if (decodeDaikin(results)) return true; #endif +#if DECODE_DAIKIN2 + DPRINTLN("Attempting Daikin2 decode"); + if (decodeDaikin2(results)) return true; +#endif +#if DECODE_DAIKIN216 + DPRINTLN("Attempting Daikin216 decode"); + if (decodeDaikin216(results)) return true; +#endif #if DECODE_TOSHIBA_AC DPRINTLN("Attempting Toshiba AC decode"); if (decodeToshibaAC(results)) return true; @@ -489,6 +501,28 @@ bool IRrecv::decode(decode_results *results, irparams_t *save) { DPRINTLN("Attempting MWM decode"); if (decodeMWM(results)) return true; #endif +#if DECODE_VESTEL_AC + DPRINTLN("Attempting Vestel AC decode"); + if (decodeVestelAc(results)) return true; +#endif +#if DECODE_TCL112AC + DPRINTLN("Attempting TCL112AC decode"); + if (decodeTcl112Ac(results)) return true; +#endif +#if DECODE_TECO + DPRINTLN("Attempting Teco decode"); + if (decodeTeco(results)) return true; +#endif +#if DECODE_LEGOPF + DPRINTLN("Attempting LEGOPF decode"); + if (decodeLegoPf(results)) return true; +#endif +#if DECODE_MITSUBISHIHEAVY + DPRINTLN("Attempting MITSUBISHIHEAVY (152 bit) decode"); + if (decodeMitsubishiHeavy(results, kMitsubishiHeavy152Bits)) return true; + DPRINTLN("Attempting MITSUBISHIHEAVY (88 bit) decode"); + if (decodeMitsubishiHeavy(results, kMitsubishiHeavy88Bits)) return true; +#endif #if DECODE_HASH // decodeHash returns a hash on any input. // Thus, it needs to be last in the list. diff --git a/lib/IRremoteESP8266-2.5.2.03/src/IRrecv.h b/lib/IRremoteESP8266-2.6.0/src/IRrecv.h similarity index 90% rename from lib/IRremoteESP8266-2.5.2.03/src/IRrecv.h rename to lib/IRremoteESP8266-2.6.0/src/IRrecv.h index c0f5e781a..0659f093e 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/IRrecv.h +++ b/lib/IRremoteESP8266-2.6.0/src/IRrecv.h @@ -181,6 +181,10 @@ class IRrecv { uint16_t nbits = kMitsubishiACBits, bool strict = false); #endif +#if DECODE_MITSUBISHIHEAVY + bool decodeMitsubishiHeavy(decode_results *results, const uint16_t nbits, + const bool strict = true); +#endif #if (DECODE_RC5 || DECODE_R6 || DECODE_LASERTAG || DECODE_MWM) int16_t getRClevel(decode_results *results, uint16_t *offset, uint16_t *used, uint16_t bitTime, uint8_t tolerance = kTolerance, @@ -216,6 +220,11 @@ class IRrecv { bool decodeSAMSUNG(decode_results *results, uint16_t nbits = kSamsungBits, bool strict = true); #endif +#if DECODE_SAMSUNG + bool decodeSamsung36(decode_results *results, + const uint16_t nbits = kSamsung36Bits, + const bool strict = true); +#endif #if DECODE_SAMSUNG_AC bool decodeSamsungAC(decode_results *results, uint16_t nbits = kSamsungAcBits, bool strict = true); @@ -257,8 +266,17 @@ class IRrecv { uint16_t nbits = kKelvinatorBits, bool strict = true); #endif #if DECODE_DAIKIN - bool decodeDaikin(decode_results *results, uint16_t nbits = kDaikinRawBits, - bool strict = true); + bool decodeDaikin(decode_results *results, const uint16_t nbits = kDaikinBits, + const bool strict = true); +#endif +#if DECODE_DAIKIN2 + bool decodeDaikin2(decode_results *results, uint16_t nbits = kDaikin2Bits, + bool strict = true); +#endif +#if DECODE_DAIKIN216 + bool decodeDaikin216(decode_results *results, + const uint16_t nbits = kDaikin216Bits, + const bool strict = true); #endif #if DECODE_TOSHIBA_AC bool decodeToshibaAC(decode_results *results, @@ -330,6 +348,22 @@ class IRrecv { bool decodeMWM(decode_results *results, uint16_t nbits = 24, bool strict = true); #endif +#if DECODE_VESTEL_AC + bool decodeVestelAc(decode_results *results, uint16_t nbits = kVestelAcBits, + bool strict = true); +#endif +#if DECODE_TCL112AC + bool decodeTcl112Ac(decode_results *results, uint16_t nbits = kTcl112AcBits, + bool strict = true); +#endif +#if DECODE_TECO + bool decodeTeco(decode_results *results, uint16_t nbits = kTecoBits, + bool strict = false); +#endif +#if DECODE_LEGOPF + bool decodeLegoPf(decode_results *results, const uint16_t nbits = kLegoPfBits, + const bool strict = true); +#endif }; #endif // IRRECV_H_ diff --git a/lib/IRremoteESP8266-2.5.2.03/src/IRremoteESP8266.h b/lib/IRremoteESP8266-2.6.0/src/IRremoteESP8266.h similarity index 82% rename from lib/IRremoteESP8266-2.5.2.03/src/IRremoteESP8266.h rename to lib/IRremoteESP8266-2.6.0/src/IRremoteESP8266.h index e228cbcb0..b532cb1c0 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/IRremoteESP8266.h +++ b/lib/IRremoteESP8266-2.6.0/src/IRremoteESP8266.h @@ -34,6 +34,8 @@ * Fujitsu A/C code added by jonnygraham * Trotec AC code by stufisher * Carrier & Haier AC code by crankyoldgit + * Vestel AC code by Erdem U. Altınyurt + * Teco AC code by Fabien Valthier (hcoohb) * * GPL license, all text above must be included in any redistribution ****************************************************/ @@ -48,7 +50,7 @@ #endif // Library Version -#define _IRREMOTEESP8266_VERSION_ "2.5.2" +#define _IRREMOTEESP8266_VERSION_ "2.6.0" // Supported IR protocols // Each protocol you include costs memory and, during decode, costs time // Disable (set to false) all the protocols you do not need/want! @@ -86,6 +88,9 @@ #define DECODE_SAMSUNG true #define SEND_SAMSUNG true +#define DECODE_SAMSUNG36 true +#define SEND_SAMSUNG36 true + #define DECODE_SAMSUNG_AC true #define SEND_SAMSUNG_AC true @@ -199,6 +204,27 @@ #define DECODE_PIONEER true #define SEND_PIONEER true + +#define DECODE_DAIKIN2 true +#define SEND_DAIKIN2 true + +#define DECODE_VESTEL_AC true +#define SEND_VESTEL_AC true + +#define DECODE_TECO true +#define SEND_TECO true + +#define DECODE_TCL112AC true +#define SEND_TCL112AC true + +#define DECODE_LEGOPF true +#define SEND_LEGOPF true + +#define DECODE_MITSUBISHIHEAVY true +#define SEND_MITSUBISHIHEAVY true + +#define DECODE_DAIKIN216 true +#define SEND_DAIKIN216 true */ // Tasmota supported protocols (less protocols is less code size) @@ -233,6 +259,9 @@ #define DECODE_SAMSUNG true #define SEND_SAMSUNG true +#define DECODE_SAMSUNG36 false +#define SEND_SAMSUNG36 false + #define DECODE_SAMSUNG_AC false #define SEND_SAMSUNG_AC false @@ -347,12 +376,35 @@ #define DECODE_PIONEER false #define SEND_PIONEER false +#define DECODE_DAIKIN2 false +#define SEND_DAIKIN2 false + +#define DECODE_VESTEL_AC false +#define SEND_VESTEL_AC false + +#define DECODE_TECO false +#define SEND_TECO false + +#define DECODE_TCL112AC false +#define SEND_TCL112AC false + +#define DECODE_LEGOPF false +#define SEND_LEGOPF false + +#define DECODE_MITSUBISHIHEAVY false +#define SEND_MITSUBISHIHEAVY false + +#define DECODE_DAIKIN216 false +#define SEND_DAIKIN216 false + #if (DECODE_ARGO || DECODE_DAIKIN || DECODE_FUJITSU_AC || DECODE_GREE || \ DECODE_KELVINATOR || DECODE_MITSUBISHI_AC || DECODE_TOSHIBA_AC || \ DECODE_TROTEC || DECODE_HAIER_AC || DECODE_HITACHI_AC || \ DECODE_HITACHI_AC1 || DECODE_HITACHI_AC2 || DECODE_HAIER_AC_YRW02 || \ DECODE_WHIRLPOOL_AC || DECODE_SAMSUNG_AC || DECODE_ELECTRA_AC || \ - DECODE_PANASONIC_AC || DECODE_MWM) + DECODE_PANASONIC_AC || DECODE_MWM || DECODE_DAIKIN2 || \ + DECODE_VESTEL_AC || DECODE_TCL112AC || DECODE_MITSUBISHIHEAVY || \ + DECODE_DAIKIN216) #define DECODE_AC true // We need some common infrastructure for decoding A/Cs. #else #define DECODE_AC false // We don't need that infrastructure. @@ -376,54 +428,65 @@ enum decode_type_t { RC6, NEC, SONY, - PANASONIC, + PANASONIC, // (5) JVC, SAMSUNG, WHYNTER, AIWA_RC_T501, - LG, + LG, // (10) SANYO, MITSUBISHI, DISH, SHARP, - COOLIX, + COOLIX, // (15) DAIKIN, DENON, KELVINATOR, SHERWOOD, - MITSUBISHI_AC, + MITSUBISHI_AC, // (20) RCMM, SANYO_LC7461, RC5X, GREE, - PRONTO, // Technically not a protocol, but an encoding. + PRONTO, // Technically not a protocol, but an encoding. (25) NEC_LIKE, ARGO, TROTEC, NIKAI, - RAW, // Technically not a protocol, but an encoding. + RAW, // Technically not a protocol, but an encoding. (30) GLOBALCACHE, // Technically not a protocol, but an encoding. TOSHIBA_AC, FUJITSU_AC, MIDEA, - MAGIQUEST, + MAGIQUEST, // (35) LASERTAG, CARRIER_AC, HAIER_AC, MITSUBISHI2, - HITACHI_AC, + HITACHI_AC, // (40) HITACHI_AC1, HITACHI_AC2, GICABLE, HAIER_AC_YRW02, - WHIRLPOOL_AC, + WHIRLPOOL_AC, // (45) SAMSUNG_AC, LUTRON, ELECTRA_AC, PANASONIC_AC, - PIONEER, + PIONEER, // (50) LG2, MWM, + DAIKIN2, + VESTEL_AC, + TECO, // (55) + SAMSUNG36, + TCL112AC, + LEGOPF, + MITSUBISHI_HEAVY_88, + MITSUBISHI_HEAVY_152, // 60 + DAIKIN216, + // Add new entries before this one, and update it to point to the last entry. + kLastDecodeType = DAIKIN216, }; // Message lengths & required repeat values @@ -433,13 +496,22 @@ const uint16_t kSingleRepeat = 1; const uint16_t kAiwaRcT501Bits = 15; const uint16_t kAiwaRcT501MinRepeats = kSingleRepeat; const uint16_t kArgoStateLength = 12; +const uint16_t kArgoDefaultRepeat = kNoRepeat; const uint16_t kCoolixBits = 24; +const uint16_t kCoolixDefaultRepeat = 1; const uint16_t kCarrierAcBits = 32; const uint16_t kCarrierAcMinRepeat = kNoRepeat; -// Daikin has a lot of static stuff that is discarded -const uint16_t kDaikinRawBits = 583; -const uint16_t kDaikinStateLength = 27; +const uint16_t kDaikinStateLength = 35; const uint16_t kDaikinBits = kDaikinStateLength * 8; +const uint16_t kDaikinStateLengthShort = kDaikinStateLength - 8; +const uint16_t kDaikinBitsShort = kDaikinStateLengthShort * 8; +const uint16_t kDaikinDefaultRepeat = kNoRepeat; +const uint16_t kDaikin2StateLength = 39; +const uint16_t kDaikin2Bits = kDaikin2StateLength * 8; +const uint16_t kDaikin2DefaultRepeat = kNoRepeat; +const uint16_t kDaikin216StateLength = 27; +const uint16_t kDaikin216Bits = kDaikin216StateLength * 8; +const uint16_t kDaikin216DefaultRepeat = kNoRepeat; const uint16_t kDenonBits = 15; const uint16_t kDenonLegacyBits = 14; const uint16_t kDishBits = 16; @@ -455,12 +527,16 @@ const uint16_t kGicableBits = 16; const uint16_t kGicableMinRepeat = kSingleRepeat; const uint16_t kGreeStateLength = 8; const uint16_t kGreeBits = kGreeStateLength * 8; +const uint16_t kGreeDefaultRepeat = kNoRepeat; const uint16_t kHaierACStateLength = 9; const uint16_t kHaierACBits = kHaierACStateLength * 8; +const uint16_t kHaierAcDefaultRepeat = kNoRepeat; const uint16_t kHaierACYRW02StateLength = 14; const uint16_t kHaierACYRW02Bits = kHaierACYRW02StateLength * 8; +const uint16_t kHaierAcYrw02DefaultRepeat = kNoRepeat; const uint16_t kHitachiAcStateLength = 28; const uint16_t kHitachiAcBits = kHitachiAcStateLength * 8; +const uint16_t kHitachiAcDefaultRepeat = kNoRepeat; const uint16_t kHitachiAc1StateLength = 13; const uint16_t kHitachiAc1Bits = kHitachiAc1StateLength * 8; const uint16_t kHitachiAc2StateLength = 53; @@ -468,8 +544,11 @@ const uint16_t kHitachiAc2Bits = kHitachiAc2StateLength * 8; const uint16_t kJvcBits = 16; const uint16_t kKelvinatorStateLength = 16; const uint16_t kKelvinatorBits = kKelvinatorStateLength * 8; +const uint16_t kKelvinatorDefaultRepeat = kNoRepeat; const uint16_t kLasertagBits = 13; const uint16_t kLasertagMinRepeat = kNoRepeat; +const uint16_t kLegoPfBits = 16; +const uint16_t kLegoPfMinRepeat = kNoRepeat; const uint16_t kLgBits = 28; const uint16_t kLg32Bits = 32; const uint16_t kLutronBits = 35; @@ -483,6 +562,12 @@ const uint16_t kMitsubishiMinRepeat = kSingleRepeat; const uint16_t kMitsubishiACStateLength = 18; const uint16_t kMitsubishiACBits = kMitsubishiACStateLength * 8; const uint16_t kMitsubishiACMinRepeat = kSingleRepeat; +const uint16_t kMitsubishiHeavy88StateLength = 11; +const uint16_t kMitsubishiHeavy88Bits = kMitsubishiHeavy88StateLength * 8; +const uint16_t kMitsubishiHeavy88MinRepeat = kNoRepeat; +const uint16_t kMitsubishiHeavy152StateLength = 19; +const uint16_t kMitsubishiHeavy152Bits = kMitsubishiHeavy152StateLength * 8; +const uint16_t kMitsubishiHeavy152MinRepeat = kNoRepeat; const uint16_t kNikaiBits = 24; const uint16_t kNECBits = 32; const uint16_t kPanasonicBits = 48; @@ -491,6 +576,7 @@ const uint16_t kPanasonicAcStateLength = 27; const uint16_t kPanasonicAcStateShortLength = 16; const uint16_t kPanasonicAcBits = kPanasonicAcStateLength * 8; const uint16_t kPanasonicAcShortBits = kPanasonicAcStateShortLength * 8; +const uint16_t kPanasonicAcDefaultRepeat = kNoRepeat; const uint16_t kPioneerBits = 64; const uint16_t kProntoMinLength = 6; const uint16_t kRC5RawBits = 14; @@ -500,10 +586,12 @@ const uint16_t kRC6Mode0Bits = 20; // Excludes the 'start' bit. const uint16_t kRC6_36Bits = 36; // Excludes the 'start' bit. const uint16_t kRCMMBits = 24; const uint16_t kSamsungBits = 32; +const uint16_t kSamsung36Bits = 36; const uint16_t kSamsungAcStateLength = 14; const uint16_t kSamsungAcBits = kSamsungAcStateLength * 8; const uint16_t kSamsungAcExtendedStateLength = 21; const uint16_t kSamsungAcExtendedBits = kSamsungAcExtendedStateLength * 8; +const uint16_t kSamsungAcDefaultRepeat = kNoRepeat; const uint16_t kSanyoSA8650BBits = 12; const uint16_t kSanyoLC7461AddressBits = 13; const uint16_t kSanyoLC7461CommandBits = 8; @@ -519,13 +607,22 @@ const uint16_t kSony15Bits = 15; const uint16_t kSony20Bits = 20; const uint16_t kSonyMinBits = 12; const uint16_t kSonyMinRepeat = 2; +const uint16_t kTcl112AcStateLength = 14; +const uint16_t kTcl112AcBits = kTcl112AcStateLength * 8; +const uint16_t kTcl112AcDefaultRepeat = kNoRepeat; +const uint16_t kTecoBits = 35; +const uint16_t kTecoDefaultRepeat = kNoRepeat; const uint16_t kToshibaACStateLength = 9; const uint16_t kToshibaACBits = kToshibaACStateLength * 8; const uint16_t kToshibaACMinRepeat = kSingleRepeat; const uint16_t kTrotecStateLength = 9; +const uint16_t kTrotecDefaultRepeat = kNoRepeat; const uint16_t kWhirlpoolAcStateLength = 21; const uint16_t kWhirlpoolAcBits = kWhirlpoolAcStateLength * 8; +const uint16_t kWhirlpoolAcDefaultRepeat = kNoRepeat; const uint16_t kWhynterBits = 32; +const uint8_t kVestelAcBits = 56; + // Legacy defines. (Deprecated) #define AIWA_RC_T501_BITS kAiwaRcT501Bits @@ -598,4 +695,14 @@ const uint16_t kWhynterBits = 32; #define DPRINTLN(x) #endif // DEBUG +#ifdef UNIT_TEST +#ifndef F +// Create a no-op F() macro so the code base still compiles outside of the +// Arduino framework. Thus we can safely use the Arduino 'F()' macro through-out +// the code base. That macro stores constants in Flash (PROGMEM) memory. +// See: https://github.com/markszabo/IRremoteESP8266/issues/667 +#define F(x) x +#endif // F +#endif // UNIT_TEST + #endif // IRREMOTEESP8266_H_ diff --git a/lib/IRremoteESP8266-2.5.2.03/src/IRsend.cpp b/lib/IRremoteESP8266-2.6.0/src/IRsend.cpp similarity index 92% rename from lib/IRremoteESP8266-2.5.2.03/src/IRsend.cpp rename to lib/IRremoteESP8266-2.6.0/src/IRsend.cpp index 96f95172d..22c0c874b 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/IRsend.cpp +++ b/lib/IRremoteESP8266-2.6.0/src/IRsend.cpp @@ -1,6 +1,6 @@ // Copyright 2009 Ken Shirriff // Copyright 2015 Mark Szabo -// Copyright 2017 David Conran +// Copyright 2017,2019 David Conran #include "IRsend.h" #ifndef UNIT_TEST @@ -110,6 +110,9 @@ void IRsend::enableIROut(uint32_t freq, uint8_t duty) { } if (freq < 1000) // Were we given kHz? Supports the old call usage. freq *= 1000; +#ifdef UNIT_TEST + _freq_unittest = freq; +#endif // UNIT_TEST uint32_t period = calcUSecPeriod(freq); // Nr. of uSeconds the LED will be on per pulse. onTimePeriod = (period * _dutycycle) / kDutyMax; @@ -488,57 +491,24 @@ void IRsend::sendRaw(uint16_t buf[], uint16_t len, uint16_t hz) { } #endif // SEND_RAW -#ifndef UNIT_TEST -void IRsend::send(uint16_t type, uint64_t data, uint16_t nbits) { +// Send a simple (up to 64 bits) IR message of a given type. +// An unknown/unsupported type will do nothing. +// Args: +// type: Protocol number/type of the message you want to send. +// data: The data you want to send (up to 64 bits). +// nbits: How many bits long the message is to be. +// Returns: +// bool: True if it is a type we can attempt to send, false if not. +bool IRsend::send(decode_type_t type, uint64_t data, uint16_t nbits) { switch (type) { -#if SEND_NEC - case NEC: - sendNEC(data, nbits); +#if SEND_AIWA_RC_T501 + case AIWA_RC_T501: + sendAiwaRCT501(data, nbits); break; #endif -#if SEND_SONY - case SONY: - sendSony(data, nbits); - break; -#endif -#if SEND_RC5 - case RC5: - sendRC5(data, nbits); - break; -#endif -#if SEND_RC6 - case RC6: - sendRC6(data, nbits); - break; -#endif -#if SEND_DISH - case DISH: - sendDISH(data, nbits); - break; -#endif -#if SEND_JVC - case JVC: - sendJVC(data, nbits); - break; -#endif -#if SEND_SAMSUNG - case SAMSUNG: - sendSAMSUNG(data, nbits); - break; -#endif -#if SEND_LG - case LG: - sendLG(data, nbits); - break; -#endif -#if SEND_LG - case LG2: - sendLG2(data, nbits); - break; -#endif -#if SEND_WHYNTER - case WHYNTER: - sendWhynter(data, nbits); +#if SEND_CARRIER_AC + case CARRIER_AC: + sendCarrierAC(data, nbits); break; #endif #if SEND_COOLIX @@ -551,14 +521,57 @@ void IRsend::send(uint16_t type, uint64_t data, uint16_t nbits) { sendDenon(data, nbits); break; #endif -#if SEND_SHERWOOD - case SHERWOOD: - sendSherwood(data, nbits); +#if SEND_DISH + case DISH: + sendDISH(data, nbits); break; #endif -#if SEND_RCMM - case RCMM: - sendRCMM(data, nbits); +#if SEND_GICABLE + case GICABLE: + sendGICable(data, nbits); + break; +#endif +#if SEND_GREE + case GREE: + sendGree(data, nbits); + break; +#endif +#if SEND_JVC + case JVC: + sendJVC(data, nbits); + break; +#endif +#if SEND_LASERTAG + case LASERTAG: + sendLasertag(data, nbits); + break; +#endif +#if SEND_LEGOPF + case LEGOPF: + sendLegoPf(data, nbits); + break; +#endif +#if SEND_LG + case LG: + sendLG(data, nbits); + break; + case LG2: + sendLG2(data, nbits); + break; +#endif +#if SEND_LUTRON + case LUTRON: + sendLutron(data, nbits); + break; +#endif +#if SEND_MAGIQUEST + case MAGIQUEST: + sendMagiQuest(data, nbits); + break; +#endif +#if SEND_MIDEA + case MIDEA: + sendMidea(data, nbits); break; #endif #if SEND_MITSUBISHI @@ -571,24 +584,20 @@ void IRsend::send(uint16_t type, uint64_t data, uint16_t nbits) { sendMitsubishi2(data, nbits); break; #endif -#if SEND_SHARP - case SHARP: - sendSharpRaw(data, nbits); +#if SEND_NIKAI + case NIKAI: + sendNikai(data, nbits); break; #endif -#if SEND_AIWA_RC_T501 - case AIWA_RC_T501: - sendAiwaRCT501(data, nbits); +#if SEND_NEC + case NEC: + case NEC_LIKE: + sendNEC(data, nbits); break; #endif -#if SEND_MIDEA - case MIDEA: - sendMidea(data, nbits); - break; -#endif -#if SEND_GICABLE - case GICABLE: - sendGICable(data, nbits); +#if SEND_PANASONIC + case PANASONIC: + sendPanasonic64(data, nbits); break; #endif #if SEND_PIONEER @@ -596,6 +605,68 @@ void IRsend::send(uint16_t type, uint64_t data, uint16_t nbits) { sendPioneer(data, nbits); break; #endif - } -} +#if SEND_RC5 + case RC5: + sendRC5(data, nbits); + break; #endif +#if SEND_RC6 + case RC6: + sendRC6(data, nbits); + break; +#endif +#if SEND_RCMM + case RCMM: + sendRCMM(data, nbits); + break; +#endif +#if SEND_SAMSUNG + case SAMSUNG: + sendSAMSUNG(data, nbits); + break; +#endif +#if SEND_SAMSUNG36 + case SAMSUNG36: + sendSamsung36(data, nbits); + break; +#endif +#if SEND_SANYO + case SANYO_LC7461: + sendSanyoLC7461(data, nbits); + break; +#endif +#if SEND_SHARP + case SHARP: + sendSharpRaw(data, nbits); + break; +#endif +#if SEND_SHERWOOD + case SHERWOOD: + sendSherwood(data, nbits); + break; +#endif +#if SEND_SONY + case SONY: + sendSony(data, nbits); + break; +#endif +#if SEND_TECO + case TECO: + sendTeco(data, nbits); + break; +#endif +#if SEND_VESTEL_AC + case VESTEL_AC: + sendVestelAc(data, nbits); + break; +#endif +#if SEND_WHYNTER + case WHYNTER: + sendWhynter(data, nbits); + break; +#endif + default: + return false; + } + return true; +} diff --git a/lib/IRremoteESP8266-2.5.2.03/src/IRsend.h b/lib/IRremoteESP8266-2.6.0/src/IRsend.h similarity index 77% rename from lib/IRremoteESP8266-2.5.2.03/src/IRsend.h rename to lib/IRremoteESP8266-2.6.0/src/IRsend.h index 8e2dc248e..b065f6582 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/IRsend.h +++ b/lib/IRremoteESP8266-2.6.0/src/IRsend.h @@ -28,6 +28,49 @@ const uint8_t kDutyMax = 100; // Percentage // delayMicroseconds() is only accurate to 16383us. // Ref: https://www.arduino.cc/en/Reference/delayMicroseconds const uint16_t kMaxAccurateUsecDelay = 16383; +// Usecs to wait between messages we don't know the proper gap time. +const uint32_t kDefaultMessageGap = 100000; + + +namespace stdAc { + enum class opmode_t { + kOff = -1, + kAuto = 0, + kCool = 1, + kHeat = 2, + kDry = 3, + kFan = 4, + }; + + enum class fanspeed_t { + kAuto = 0, + kMin = 1, + kLow = 2, + kMedium = 3, + kHigh = 4, + kMax = 5, + }; + + enum class swingv_t { + kOff = -1, + kAuto = 0, + kHighest = 1, + kHigh = 2, + kMiddle = 3, + kLow = 4, + kLowest = 5, + }; + + enum class swingh_t { + kOff = -1, + kAuto = 0, // a.k.a. On. + kLeftMax = 1, + kLeft = 2, + kMiddle = 3, + kRight = 4, + kRightMax = 5, + }; +}; // namespace stdAc // Classes class IRsend { @@ -66,7 +109,7 @@ class IRsend { const uint8_t *dataptr, const uint16_t nbytes, const uint16_t frequency, const bool MSBfirst, const uint16_t repeat, const uint8_t dutycycle); - void send(uint16_t type, uint64_t data, uint16_t nbits); + bool send(decode_type_t type, uint64_t data, uint16_t nbits); #if (SEND_NEC || SEND_SHERWOOD || SEND_AIWA_RC_T501 || SEND_SANYO) void sendNEC(uint64_t data, uint16_t nbits = kNECBits, uint16_t repeat = kNoRepeat); @@ -92,10 +135,14 @@ class IRsend { uint16_t repeat = kNoRepeat); uint32_t encodeSAMSUNG(uint8_t customer, uint8_t command); #endif +#if SEND_SAMSUNG36 + void sendSamsung36(const uint64_t data, const uint16_t nbits = kSamsung36Bits, + const uint16_t repeat = kNoRepeat); +#endif #if SEND_SAMSUNG_AC - void sendSamsungAC(unsigned char data[], - uint16_t nbytes = kSamsungAcStateLength, - uint16_t repeat = kNoRepeat); + void sendSamsungAC(const unsigned char data[], + const uint16_t nbytes = kSamsungAcStateLength, + const uint16_t repeat = kSamsungAcDefaultRepeat); #endif #if SEND_LG void sendLG(uint64_t data, uint16_t nbits = kLgBits, @@ -166,7 +213,7 @@ class IRsend { #endif #if SEND_COOLIX void sendCOOLIX(uint64_t data, uint16_t nbits = kCoolixBits, - uint16_t repeat = kNoRepeat); + uint16_t repeat = kCoolixDefaultRepeat); #endif #if SEND_WHYNTER void sendWhynter(uint64_t data, uint16_t nbits = kWhynterBits, @@ -185,6 +232,16 @@ class IRsend { uint16_t nbytes = kMitsubishiACStateLength, uint16_t repeat = kMitsubishiACMinRepeat); #endif +#if SEND_MITSUBISHIHEAVY + void sendMitsubishiHeavy88( + const unsigned char data[], + const uint16_t nbytes = kMitsubishiHeavy88StateLength, + const uint16_t repeat = kMitsubishiHeavy88MinRepeat); + void sendMitsubishiHeavy152( + const unsigned char data[], + const uint16_t nbytes = kMitsubishiHeavy152StateLength, + const uint16_t repeat = kMitsubishiHeavy152MinRepeat); +#endif #if SEND_FUJITSU_AC void sendFujitsuAC(unsigned char data[], uint16_t nbytes, uint16_t repeat = kFujitsuAcMinRepeat); @@ -195,12 +252,21 @@ class IRsend { #if SEND_KELVINATOR void sendKelvinator(unsigned char data[], uint16_t nbytes = kKelvinatorStateLength, - uint16_t repeat = kNoRepeat); + uint16_t repeat = kKelvinatorDefaultRepeat); #endif #if SEND_DAIKIN - void sendDaikin(unsigned char data[], uint16_t nbytes = kDaikinStateLength, - uint16_t repeat = kNoRepeat); - void sendDaikinGapHeader(); + void sendDaikin(const unsigned char data[], + const uint16_t nbytes = kDaikinStateLength, + const uint16_t repeat = kDaikinDefaultRepeat); +#endif +#if SEND_DAIKIN2 + void sendDaikin2(unsigned char data[], uint16_t nbytes = kDaikin2StateLength, + uint16_t repeat = kDaikin2DefaultRepeat); +#endif +#if SEND_DAIKIN216 + void sendDaikin216(const unsigned char data[], + const uint16_t nbytes = kDaikin216StateLength, + const uint16_t repeat = kDaikin216DefaultRepeat); #endif #if SEND_AIWA_RC_T501 void sendAiwaRCT501(uint64_t data, uint16_t nbits = kAiwaRcT501Bits, @@ -208,20 +274,20 @@ class IRsend { #endif #if SEND_GREE void sendGree(uint64_t data, uint16_t nbits = kGreeBits, - uint16_t repeat = kNoRepeat); + uint16_t repeat = kGreeDefaultRepeat); void sendGree(uint8_t data[], uint16_t nbytes = kGreeStateLength, - uint16_t repeat = kNoRepeat); + uint16_t repeat = kGreeDefaultRepeat); #endif #if SEND_PRONTO void sendPronto(uint16_t data[], uint16_t len, uint16_t repeat = kNoRepeat); #endif #if SEND_ARGO void sendArgo(unsigned char data[], uint16_t nbytes = kArgoStateLength, - uint16_t repeat = kNoRepeat); + uint16_t repeat = kArgoDefaultRepeat); #endif #if SEND_TROTEC void sendTrotec(unsigned char data[], uint16_t nbytes = kTrotecStateLength, - uint16_t repeat = kNoRepeat); + uint16_t repeat = kTrotecDefaultRepeat); #endif #if SEND_NIKAI void sendNikai(uint64_t data, uint16_t nbits = kNikaiBits, @@ -251,17 +317,17 @@ class IRsend { #endif #if (SEND_HAIER_AC || SEND_HAIER_AC_YRW02) void sendHaierAC(unsigned char data[], uint16_t nbytes = kHaierACStateLength, - uint16_t repeat = kNoRepeat); + uint16_t repeat = kHaierAcDefaultRepeat); #endif #if SEND_HAIER_AC_YRW02 void sendHaierACYRW02(unsigned char data[], uint16_t nbytes = kHaierACYRW02StateLength, - uint16_t repeat = kNoRepeat); + uint16_t repeat = kHaierAcYrw02DefaultRepeat); #endif #if SEND_HITACHI_AC void sendHitachiAC(unsigned char data[], uint16_t nbytes = kHitachiAcStateLength, - uint16_t repeat = kNoRepeat); + uint16_t repeat = kHitachiAcDefaultRepeat); #endif #if SEND_HITACHI_AC1 void sendHitachiAC1(unsigned char data[], @@ -280,7 +346,7 @@ class IRsend { #if SEND_WHIRLPOOL_AC void sendWhirlpoolAC(unsigned char data[], uint16_t nbytes = kWhirlpoolAcStateLength, - uint16_t repeat = kNoRepeat); + uint16_t repeat = kWhirlpoolAcDefaultRepeat); #endif #if SEND_LUTRON void sendLutron(uint64_t data, uint16_t nbits = kLutronBits, @@ -294,7 +360,7 @@ class IRsend { #if SEND_PANASONIC_AC void sendPanasonicAC(unsigned char data[], uint16_t nbytes = kPanasonicAcStateLength, - uint16_t repeat = kNoRepeat); + uint16_t repeat = kPanasonicAcDefaultRepeat); #endif #if SEND_PIONEER void sendPioneer(const uint64_t data, const uint16_t nbits = kPioneerBits, @@ -305,6 +371,24 @@ class IRsend { void sendMWM(unsigned char data[], uint16_t nbytes, uint16_t repeat = kNoRepeat); #endif +#if SEND_VESTEL_AC + void sendVestelAc(const uint64_t data, const uint16_t nbits = kVestelAcBits, + const uint16_t repeat = kNoRepeat); +#endif +#if SEND_TCL112AC + void sendTcl112Ac(const unsigned char data[], + const uint16_t nbytes = kTcl112AcStateLength, + const uint16_t repeat = kTcl112AcDefaultRepeat); +#endif +#if SEND_TECO + void sendTeco(uint64_t data, uint16_t nbits = kTecoBits, + uint16_t repeat = kNoRepeat); +#endif +#if SEND_LEGOPF + void sendLegoPf(const uint64_t data, const uint16_t nbits = kLegoPfBits, + const uint16_t repeat = kLegoPfMinRepeat); +#endif + protected: #ifdef UNIT_TEST @@ -319,8 +403,12 @@ class IRsend { uint8_t outputOff; VIRTUAL void ledOff(); VIRTUAL void ledOn(); +#ifndef UNIT_TEST private: +#else + uint32_t _freq_unittest; +#endif // UNIT_TEST uint16_t onTimePeriod; uint16_t offTimePeriod; uint16_t IRpin; diff --git a/lib/IRremoteESP8266-2.5.2.03/src/IRtimer.cpp b/lib/IRremoteESP8266-2.6.0/src/IRtimer.cpp similarity index 53% rename from lib/IRremoteESP8266-2.5.2.03/src/IRtimer.cpp rename to lib/IRremoteESP8266-2.6.0/src/IRtimer.cpp index 029637cbb..4173d763b 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/IRtimer.cpp +++ b/lib/IRremoteESP8266-2.6.0/src/IRtimer.cpp @@ -7,12 +7,12 @@ #ifdef UNIT_TEST // Used to help simulate elapsed time in unit tests. -extern uint32_t _IRtimer_unittest_now; +uint32_t _IRtimer_unittest_now = 0; +uint32_t _TimerMs_unittest_now = 0; #endif // UNIT_TEST // This class performs a simple time in useconds since instantiated. // Handles when the system timer wraps around (once). - IRtimer::IRtimer() { reset(); } void IRtimer::reset() { @@ -39,3 +39,32 @@ uint32_t IRtimer::elapsed() { #ifdef UNIT_TEST void IRtimer::add(uint32_t usecs) { _IRtimer_unittest_now += usecs; } #endif // UNIT_TEST + +// This class performs a simple time in milli-seoncds since instantiated. +// Handles when the system timer wraps around (once). +TimerMs::TimerMs() { reset(); } + +void TimerMs::reset() { +#ifndef UNIT_TEST + start = millis(); +#else + start = _TimerMs_unittest_now; +#endif +} + +uint32_t TimerMs::elapsed() { +#ifndef UNIT_TEST + uint32_t now = millis(); +#else + uint32_t now = _TimerMs_unittest_now; +#endif + if (start <= now) // Check if the system timer has wrapped. + return now - start; // No wrap. + else + return UINT32_MAX - start + now; // Has wrapped. +} + +// Only used in unit testing. +#ifdef UNIT_TEST +void TimerMs::add(uint32_t msecs) { _IRtimer_unittest_now += msecs; } +#endif // UNIT_TEST diff --git a/lib/IRremoteESP8266-2.5.2.03/src/IRtimer.h b/lib/IRremoteESP8266-2.6.0/src/IRtimer.h similarity index 64% rename from lib/IRremoteESP8266-2.5.2.03/src/IRtimer.h rename to lib/IRremoteESP8266-2.6.0/src/IRtimer.h index baca1cf74..d00e1d0fa 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/IRtimer.h +++ b/lib/IRremoteESP8266-2.6.0/src/IRtimer.h @@ -20,4 +20,16 @@ class IRtimer { uint32_t start; }; +class TimerMs { + public: + TimerMs(); + void reset(); + uint32_t elapsed(); +#ifdef UNIT_TEST + static void add(uint32_t msecs); +#endif // UNIT_TEST + + private: + uint32_t start; +}; #endif // IRTIMER_H_ diff --git a/lib/IRremoteESP8266-2.6.0/src/IRutils.cpp b/lib/IRremoteESP8266-2.6.0/src/IRutils.cpp new file mode 100644 index 000000000..d90925241 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/src/IRutils.cpp @@ -0,0 +1,768 @@ +// Copyright 2017 David Conran + +#include "IRutils.h" +#ifndef UNIT_TEST +#include +#endif + +#define __STDC_LIMIT_MACROS +#include +#include +#include +#ifndef ARDUINO +#include +#endif +#include "IRrecv.h" +#include "IRremoteESP8266.h" + +// Reverse the order of the requested least significant nr. of bits. +// Args: +// input: Bit pattern/integer to reverse. +// nbits: Nr. of bits to reverse. +// Returns: +// The reversed bit pattern. +uint64_t reverseBits(uint64_t input, uint16_t nbits) { + if (nbits <= 1) return input; // Reversing <= 1 bits makes no change at all. + // Cap the nr. of bits to rotate to the max nr. of bits in the input. + nbits = std::min(nbits, (uint16_t)(sizeof(input) * 8)); + uint64_t output = 0; + for (uint16_t i = 0; i < nbits; i++) { + output <<= 1; + output |= (input & 1); + input >>= 1; + } + // Merge any remaining unreversed bits back to the top of the reversed bits. + return (input << nbits) | output; +} + +// Convert a uint64_t (unsigned long long) to a string. +// Arduino String/toInt/Serial.print() can't handle printing 64 bit values. +// +// Args: +// input: The value to print +// base: The output base. +// Returns: +// A string representation of the integer. +// Note: Based on Arduino's Print::printNumber() +#ifdef ARDUINO // Arduino's & C++'s string implementations can't co-exist. +String uint64ToString(uint64_t input, uint8_t base) { + String result = ""; +#else +std::string uint64ToString(uint64_t input, uint8_t base) { + std::string result = ""; +#endif + // prevent issues if called with base <= 1 + if (base < 2) base = 10; + // Check we have a base that we can actually print. + // i.e. [0-9A-Z] == 36 + if (base > 36) base = 10; + + do { + char c = input % base; + input /= base; + + if (c < 10) + c += '0'; + else + c += 'A' - 10; + result = c + result; + } while (input); + return result; +} + +#ifdef ARDUINO +// Print a uint64_t/unsigned long long to the Serial port +// Serial.print() can't handle printing long longs. (uint64_t) +// +// Args: +// input: The value to print +// base: The output base. +void serialPrintUint64(uint64_t input, uint8_t base) { + Serial.print(uint64ToString(input, base)); +} +#endif + +// Convert a c-style str to a decode_type_t +// Note: Assumes str is upper case. +// +// Args: +// str: An upper-case C-style string. +// Returns: +// A decode_type_t enum. +decode_type_t strToDecodeType(const char *str) { + if (!strcmp(str, "UNKNOWN")) + return decode_type_t::UNKNOWN; + else if (!strcmp(str, "UNUSED")) + return decode_type_t::UNUSED; + else if (!strcmp(str, "AIWA_RC_T501")) + return decode_type_t::AIWA_RC_T501; + else if (!strcmp(str, "ARGO")) + return decode_type_t::ARGO; + else if (!strcmp(str, "CARRIER_AC")) + return decode_type_t::CARRIER_AC; + else if (!strcmp(str, "COOLIX")) + return decode_type_t::COOLIX; + else if (!strcmp(str, "DAIKIN")) + return decode_type_t::DAIKIN; + else if (!strcmp(str, "DAIKIN2")) + return decode_type_t::DAIKIN2; + else if (!strcmp(str, "DAIKIN216")) + return decode_type_t::DAIKIN216; + else if (!strcmp(str, "DENON")) + return decode_type_t::DENON; + else if (!strcmp(str, "DISH")) + return decode_type_t::DISH; + else if (!strcmp(str, "ELECTRA_AC")) + return decode_type_t::ELECTRA_AC; + else if (!strcmp(str, "FUJITSU_AC")) + return decode_type_t::FUJITSU_AC; + else if (!strcmp(str, "GICABLE")) + return decode_type_t::GICABLE; + else if (!strcmp(str, "GLOBALCACHE")) + return decode_type_t::GLOBALCACHE; + else if (!strcmp(str, "GREE")) + return decode_type_t::GREE; + else if (!strcmp(str, "HAIER_AC")) + return decode_type_t::HAIER_AC; + else if (!strcmp(str, "HAIER_AC_YRW02")) + return decode_type_t::HAIER_AC_YRW02; + else if (!strcmp(str, "HITACHI_AC")) + return decode_type_t::HITACHI_AC; + else if (!strcmp(str, "HITACHI_AC1")) + return decode_type_t::HITACHI_AC1; + else if (!strcmp(str, "HITACHI_AC2")) + return decode_type_t::HITACHI_AC2; + else if (!strcmp(str, "JVC")) + return decode_type_t::JVC; + else if (!strcmp(str, "KELVINATOR")) + return decode_type_t::KELVINATOR; + else if (!strcmp(str, "LEGOPF")) + return decode_type_t::LEGOPF; + else if (!strcmp(str, "LG")) + return decode_type_t::LG; + else if (!strcmp(str, "LG2")) + return decode_type_t::LG2; + else if (!strcmp(str, "LASERTAG")) + return decode_type_t::LASERTAG; + else if (!strcmp(str, "LUTRON")) + return decode_type_t::LUTRON; + else if (!strcmp(str, "MAGIQUEST")) + return decode_type_t::MAGIQUEST; + else if (!strcmp(str, "MIDEA")) + return decode_type_t::MIDEA; + else if (!strcmp(str, "MITSUBISHI")) + return decode_type_t::MITSUBISHI; + else if (!strcmp(str, "MITSUBISHI2")) + return decode_type_t::MITSUBISHI2; + else if (!strcmp(str, "MITSUBISHI_AC")) + return decode_type_t::MITSUBISHI_AC; + else if (!strcmp(str, "MWM")) + return decode_type_t::MWM; + else if (!strcmp(str, "NEC") || !strcmp(str, "NEC (NON-STRICT")) + return decode_type_t::NEC; + else if (!strcmp(str, "NIKAI")) + return decode_type_t::NIKAI; + else if (!strcmp(str, "PANASONIC")) + return decode_type_t::PANASONIC; + else if (!strcmp(str, "PANASONIC_AC")) + return decode_type_t::PANASONIC_AC; + else if (!strcmp(str, "PIONEER")) + return decode_type_t::PIONEER; + else if (!strcmp(str, "PRONTO")) + return decode_type_t::PRONTO; + else if (!strcmp(str, "RAW")) + return decode_type_t::RAW; + else if (!strcmp(str, "RC5")) + return decode_type_t::RC5; + else if (!strcmp(str, "RC5X")) + return decode_type_t::RC5X; + else if (!strcmp(str, "RC6")) + return decode_type_t::RC6; + else if (!strcmp(str, "RCMM")) + return decode_type_t::RCMM; + else if (!strcmp(str, "SAMSUNG")) + return decode_type_t::SAMSUNG; + else if (!strcmp(str, "SAMSUNG36")) + return decode_type_t::SAMSUNG36; + else if (!strcmp(str, "SAMSUNG_AC")) + return decode_type_t::SAMSUNG_AC; + else if (!strcmp(str, "SANYO")) + return decode_type_t::SANYO; + else if (!strcmp(str, "SANYO_LC7461")) + return decode_type_t::SANYO_LC7461; + else if (!strcmp(str, "SHARP")) + return decode_type_t::SHARP; + else if (!strcmp(str, "SHERWOOD")) + return decode_type_t::SHERWOOD; + else if (!strcmp(str, "SONY")) + return decode_type_t::SONY; + else if (!strcmp(str, "TCL112AC")) + return decode_type_t::TCL112AC; + else if (!strcmp(str, "TECO")) + return decode_type_t::TECO; + else if (!strcmp(str, "TOSHIBA_AC")) + return decode_type_t::TOSHIBA_AC; + else if (!strcmp(str, "TROTEC")) + return decode_type_t::TROTEC; + else if (!strcmp(str, "VESTEL_AC")) + return decode_type_t::VESTEL_AC; + else if (!strcmp(str, "WHIRLPOOL_AC")) + return decode_type_t::WHIRLPOOL_AC; + else if (!strcmp(str, "WHYNTER")) + return decode_type_t::WHYNTER; + // Handle integer values of the type by converting to a string and back again. + decode_type_t result = strToDecodeType( + typeToString((decode_type_t)atoi(str)).c_str()); + if (result > 0) + return result; + else + return decode_type_t::UNKNOWN; +} + +// Escape any special HTML (unsafe) characters in a string. e.g. anti-XSS. +// Args: +// unescaped: A string containing text to make HTML safe. +// Returns: +// A string that is HTML safe. +#ifdef ARDUINO // Arduino's & C++'s string implementations can't co-exist. +String htmlEscape(const String unescaped) { + String result = ""; +#else +std::string htmlEscape(const std::string unescaped) { + std::string result = ""; +#endif + uint16_t ulen = unescaped.length(); + result.reserve(ulen); // The result will be at least the size of input. + for (size_t i = 0; i < ulen; i++) { + char c = unescaped[i]; + switch (c) { + // ';!-"<>=&#{}() are all unsafe. + case '\'': + result += F("'"); + break; + case ';': + result += F(";"); + break; + case '!': + result += F("!"); + break; + case '-': + result += F("‐"); + break; + case '\"': + result += F("""); + break; + case '<': + result += F("<"); + break; + case '>': + result += F(">"); + break; + case '=': + result += F("&#equals;"); + break; + case '&': + result += F("&"); + break; + case '#': + result += F("#"); + break; + case '{': + result += F("{"); + break; + case '}': + result += F("}"); + break; + case '(': + result += F("("); + break; + case ')': + result += F(")"); + break; + default: + result += c; + } + } + return result; +} + +// Convert a protocol type (enum etc) to a human readable string. +// Args: +// protocol: Nr. (enum) of the protocol. +// isRepeat: A flag indicating if it is a repeat message of the protocol. +// Returns: +// A string containing the protocol name. +#ifdef ARDUINO // Arduino's & C++'s string implementations can't co-exist. +String typeToString(const decode_type_t protocol, const bool isRepeat) { + String result = ""; +#else +std::string typeToString(const decode_type_t protocol, const bool isRepeat) { + std::string result = ""; +#endif + switch (protocol) { + case UNUSED: + result = F("UNUSED"); + break; + case AIWA_RC_T501: + result = F("AIWA_RC_T501"); + break; + case ARGO: + result = F("ARGO"); + break; + case CARRIER_AC: + result = F("CARRIER_AC"); + break; + case COOLIX: + result = F("COOLIX"); + break; + case DAIKIN: + result = F("DAIKIN"); + break; + case DAIKIN2: + result = F("DAIKIN2"); + break; + case DAIKIN216: + result = F("DAIKIN216"); + break; + case DENON: + result = F("DENON"); + break; + case DISH: + result = F("DISH"); + break; + case ELECTRA_AC: + result = F("ELECTRA_AC"); + break; + case FUJITSU_AC: + result = F("FUJITSU_AC"); + break; + case GICABLE: + result = F("GICABLE"); + break; + case GLOBALCACHE: + result = F("GLOBALCACHE"); + break; + case GREE: + result = F("GREE"); + break; + case HAIER_AC: + result = F("HAIER_AC"); + break; + case HAIER_AC_YRW02: + result = F("HAIER_AC_YRW02"); + break; + case HITACHI_AC: + result = F("HITACHI_AC"); + break; + case HITACHI_AC1: + result = F("HITACHI_AC1"); + break; + case HITACHI_AC2: + result = F("HITACHI_AC2"); + break; + case JVC: + result = F("JVC"); + break; + case KELVINATOR: + result = F("KELVINATOR"); + break; + case LEGOPF: + result = F("LEGOPF"); + break; + case LG: + result = F("LG"); + break; + case LG2: + result = F("LG2"); + break; + case LASERTAG: + result = F("LASERTAG"); + break; + case LUTRON: + result = F("LUTRON"); + break; + case MAGIQUEST: + result = F("MAGIQUEST"); + break; + case MIDEA: + result = F("MIDEA"); + break; + case MITSUBISHI: + result = F("MITSUBISHI"); + break; + case MITSUBISHI2: + result = F("MITSUBISHI2"); + break; + case MITSUBISHI_AC: + result = F("MITSUBISHI_AC"); + break; + case MITSUBISHI_HEAVY_88: + result = F("MITSUBISHI_HEAVY_88"); + break; + case MITSUBISHI_HEAVY_152: + result = F("MITSUBISHI_HEAVY_152"); + break; + case MWM: + result = F("MWM"); + break; + case NEC: + result = F("NEC"); + break; + case NEC_LIKE: + result = F("NEC (non-strict)"); + break; + case NIKAI: + result = F("NIKAI"); + break; + case PANASONIC: + result = F("PANASONIC"); + break; + case PANASONIC_AC: + result = F("PANASONIC_AC"); + break; + case PIONEER: + result = F("PIONEER"); + break; + case PRONTO: + result = F("PRONTO"); + break; + case RAW: + result = F("RAW"); + break; + case RC5: + result = F("RC5"); + break; + case RC5X: + result = F("RC5X"); + break; + case RC6: + result = F("RC6"); + break; + case RCMM: + result = F("RCMM"); + break; + case SAMSUNG: + result = F("SAMSUNG"); + break; + case SAMSUNG36: + result = F("SAMSUNG36"); + break; + case SAMSUNG_AC: + result = F("SAMSUNG_AC"); + break; + case SANYO: + result = F("SANYO"); + break; + case SANYO_LC7461: + result = F("SANYO_LC7461"); + break; + case SHARP: + result = F("SHARP"); + break; + case SHERWOOD: + result = F("SHERWOOD"); + break; + case SONY: + result = F("SONY"); + break; + case TCL112AC: + result = F("TCL112AC"); + break; + case TECO: + result = F("TECO"); + break; + case TOSHIBA_AC: + result = F("TOSHIBA_AC"); + break; + case TROTEC: + result = F("TROTEC"); + break; + case VESTEL_AC: + result = F("VESTEL_AC"); + break; + case WHIRLPOOL_AC: + result = F("WHIRLPOOL_AC"); + break; + case WHYNTER: + result = F("WHYNTER"); + break; + case UNKNOWN: + default: + result = F("UNKNOWN"); + break; + } + if (isRepeat) result += F(" (Repeat)"); + return result; +} + +// Does the given protocol use a complex state as part of the decode? +bool hasACState(const decode_type_t protocol) { + switch (protocol) { + case DAIKIN: + case DAIKIN2: + case DAIKIN216: + case ELECTRA_AC: + case FUJITSU_AC: + case GREE: + case HAIER_AC: + case HAIER_AC_YRW02: + case HITACHI_AC: + case HITACHI_AC1: + case HITACHI_AC2: + case KELVINATOR: + case MITSUBISHI_AC: + case MITSUBISHI_HEAVY_88: + case MITSUBISHI_HEAVY_152: + case MWM: + case PANASONIC_AC: + case SAMSUNG_AC: + case TCL112AC: + case TOSHIBA_AC: + case WHIRLPOOL_AC: + return true; + default: + return false; + } +} + +// Return the corrected length of a 'raw' format array structure +// after over-large values are converted into multiple entries. +// Args: +// results: A ptr to a decode result. +// Returns: +// A uint16_t containing the length. +uint16_t getCorrectedRawLength(const decode_results *results) { + uint16_t extended_length = results->rawlen - 1; + for (uint16_t i = 0; i < results->rawlen - 1; i++) { + uint32_t usecs = results->rawbuf[i] * kRawTick; + // Add two extra entries for multiple larger than UINT16_MAX it is. + extended_length += (usecs / (UINT16_MAX + 1)) * 2; + } + return extended_length; +} + +// Return a string containing the key values of a decode_results structure +// in a C/C++ code style format. +#ifdef ARDUINO +String resultToSourceCode(const decode_results *results) { + String output = ""; +#else +std::string resultToSourceCode(const decode_results *results) { + std::string output = ""; +#endif + // Start declaration + output += F("uint16_t "); // variable type + output += F("rawData["); // array name + output += uint64ToString(getCorrectedRawLength(results), 10); + // array size + output += F("] = {"); // Start declaration + + // Dump data + for (uint16_t i = 1; i < results->rawlen; i++) { + uint32_t usecs; + for (usecs = results->rawbuf[i] * kRawTick; usecs > UINT16_MAX; + usecs -= UINT16_MAX) { + output += uint64ToString(UINT16_MAX); + if (i % 2) + output += F(", 0, "); + else + output += F(", 0, "); + } + output += uint64ToString(usecs, 10); + if (i < results->rawlen - 1) + output += F(", "); // ',' not needed on the last one + if (i % 2 == 0) output += ' '; // Extra if it was even. + } + + // End declaration + output += F("};"); + + // Comment + output += F(" // "); + output += typeToString(results->decode_type, results->repeat); + // Only display the value if the decode type doesn't have an A/C state. + if (!hasACState(results->decode_type)) + output += ' ' + uint64ToString(results->value, 16); + output += F("\n"); + + // Now dump "known" codes + if (results->decode_type != UNKNOWN) { + if (hasACState(results->decode_type)) { +#if DECODE_AC + uint16_t nbytes = results->bits / 8; + output += F("uint8_t state["); + output += uint64ToString(nbytes); + output += F("] = {"); + for (uint16_t i = 0; i < nbytes; i++) { + output += F("0x"); + if (results->state[i] < 0x10) output += '0'; + output += uint64ToString(results->state[i], 16); + if (i < nbytes - 1) output += F(", "); + } + output += F("};\n"); +#endif // DECODE_AC + } else { + // Simple protocols + // Some protocols have an address &/or command. + // NOTE: It will ignore the atypical case when a message has been + // decoded but the address & the command are both 0. + if (results->address > 0 || results->command > 0) { + output += F("uint32_t address = 0x"); + output += uint64ToString(results->address, 16); + output += F(";\n"); + output += F("uint32_t command = 0x"); + output += uint64ToString(results->command, 16); + output += F(";\n"); + } + // Most protocols have data + output += F("uint64_t data = 0x"); + output += uint64ToString(results->value, 16); + output += F(";\n"); + } + } + return output; +} + +// Dump out the decode_results structure. +// +#ifdef ARDUINO +String resultToTimingInfo(const decode_results *results) { + String output = ""; + String value = ""; +#else +std::string resultToTimingInfo(const decode_results *results) { + std::string output = ""; + std::string value = ""; +#endif + output += F("Raw Timing["); + output += uint64ToString(results->rawlen - 1, 10); + output += F("]:\n"); + + for (uint16_t i = 1; i < results->rawlen; i++) { + if (i % 2 == 0) + output += '-'; // even + else + output += F(" +"); // odd + value = uint64ToString(results->rawbuf[i] * kRawTick); + // Space pad the value till it is at least 6 chars long. + while (value.length() < 6) value = ' ' + value; + output += value; + if (i < results->rawlen - 1) + output += F(", "); // ',' not needed for last one + if (!(i % 8)) output += '\n'; // Newline every 8 entries. + } + output += '\n'; + return output; +} + +// Convert the decode_results structure's value/state to simple hexadecimal. +// +#ifdef ARDUINO +String resultToHexidecimal(const decode_results *result) { + String output = ""; +#else +std::string resultToHexidecimal(const decode_results *result) { + std::string output = ""; +#endif + if (hasACState(result->decode_type)) { +#if DECODE_AC + for (uint16_t i = 0; result->bits > i * 8; i++) { + if (result->state[i] < 0x10) output += '0'; // Zero pad + output += uint64ToString(result->state[i], 16); + } +#endif // DECODE_AC + } else { + output += uint64ToString(result->value, 16); + } + return output; +} + +// Dump out the decode_results structure. +// +#ifdef ARDUINO +String resultToHumanReadableBasic(const decode_results *results) { + String output = ""; +#else +std::string resultToHumanReadableBasic(const decode_results *results) { + std::string output = ""; +#endif + // Show Encoding standard + output += F("Encoding : "); + output += typeToString(results->decode_type, results->repeat); + output += '\n'; + + // Show Code & length + output += F("Code : "); + output += resultToHexidecimal(results); + output += F(" ("); + output += uint64ToString(results->bits); + output += F(" bits)\n"); + return output; +} + +uint8_t sumBytes(uint8_t *start, const uint16_t length, const uint8_t init) { + uint8_t checksum = init; + uint8_t *ptr; + for (ptr = start; ptr - start < length; ptr++) checksum += *ptr; + return checksum; +} + +uint8_t xorBytes(uint8_t *start, const uint16_t length, const uint8_t init) { + uint8_t checksum = init; + uint8_t *ptr; + for (ptr = start; ptr - start < length; ptr++) checksum ^= *ptr; + return checksum; +} + +// Count the number of bits of a certain type. +// Args: +// start: Ptr to the start of data to count bits in. +// length: How many bytes to count. +// ones: Count the binary 1 bits. False for counting the 0 bits. +// init: Start the counting from this value. +// Returns: +// Nr. of bits found. +uint16_t countBits(const uint8_t *start, const uint16_t length, const bool ones, + const uint16_t init) { + uint16_t count = init; + for (uint16_t offset = 0; offset < length; offset++) + for (uint8_t currentbyte = *(start + offset); + currentbyte; + currentbyte >>= 1) + if (currentbyte & 1) count++; + if (ones || length == 0) + return count; + else + return (length * 8) - count; +} + +// Count the number of bits of a certain type. +// Args: +// data: The value you want bits counted for, starting from the LSB. +// length: How many bits to count. +// ones: Count the binary 1 bits. False for counting the 0 bits. +// init: Start the counting from this value. +// Returns: +// Nr. of bits found. +uint16_t countBits(const uint64_t data, const uint8_t length, const bool ones, + const uint16_t init) { + uint16_t count = init; + uint8_t bitsSoFar = length; + for (uint64_t remainder = data; remainder && bitsSoFar; + remainder >>= 1, bitsSoFar--) + if (remainder & 1) count++; + if (ones || length == 0) + return count; + else + return length - count; +} + +uint64_t invertBits(const uint64_t data, const uint16_t nbits) { + // No change if we are asked to invert no bits. + if (nbits == 0) return data; + uint64_t result = ~data; + // If we are asked to invert all the bits or more than we have, it's simple. + if (nbits >= sizeof(data) * 8) return result; + // Mask off any unwanted bits and return the result. + return (result & ((1ULL << nbits) - 1)); +} diff --git a/lib/IRremoteESP8266-2.5.2.03/src/IRutils.h b/lib/IRremoteESP8266-2.6.0/src/IRutils.h similarity index 74% rename from lib/IRremoteESP8266-2.5.2.03/src/IRutils.h rename to lib/IRremoteESP8266-2.6.0/src/IRutils.h index c17375d98..0d0b677b5 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/IRutils.h +++ b/lib/IRremoteESP8266-2.6.0/src/IRutils.h @@ -24,7 +24,8 @@ String resultToSourceCode(const decode_results *results); String resultToTimingInfo(const decode_results *results); String resultToHumanReadableBasic(const decode_results *results); String resultToHexidecimal(const decode_results *result); -#else +String htmlEscape(const String unescaped); +#else // ARDUINO std::string uint64ToString(uint64_t input, uint8_t base = 10); std::string typeToString(const decode_type_t protocol, const bool isRepeat = false); @@ -32,10 +33,16 @@ std::string resultToSourceCode(const decode_results *results); std::string resultToTimingInfo(const decode_results *results); std::string resultToHumanReadableBasic(const decode_results *results); std::string resultToHexidecimal(const decode_results *result); -#endif +std::string htmlEscape(const std::string unescaped); +#endif // ARDUINO bool hasACState(const decode_type_t protocol); uint16_t getCorrectedRawLength(const decode_results *results); uint8_t sumBytes(uint8_t *start, const uint16_t length, const uint8_t init = 0); +uint8_t xorBytes(uint8_t *start, const uint16_t length, const uint8_t init = 0); +uint16_t countBits(const uint8_t *start, const uint16_t length, + const bool ones = true, const uint16_t init = 0); +uint16_t countBits(const uint64_t data, const uint8_t length, + const bool ones = true, const uint16_t init = 0); uint64_t invertBits(const uint64_t data, const uint16_t nbits); - +decode_type_t strToDecodeType(const char *str); #endif // IRUTILS_H_ diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Aiwa.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Aiwa.cpp similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Aiwa.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_Aiwa.cpp diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Argo.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Argo.cpp similarity index 84% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Argo.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_Argo.cpp index 8a3e69f72..d6711acd3 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Argo.cpp +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Argo.cpp @@ -40,9 +40,9 @@ IRArgoAC::IRArgoAC(uint16_t pin) : _irsend(pin) { stateReset(); } void IRArgoAC::begin() { _irsend.begin(); } #if SEND_ARGO -void IRArgoAC::send() { +void IRArgoAC::send(const uint16_t repeat) { checksum(); // Create valid checksum before sending - _irsend.sendArgo(argo); + _irsend.sendArgo(argo, kArgoStateLength, repeat); } #endif // SEND_ARGO @@ -228,3 +228,37 @@ void IRArgoAC::setRoomTemp(uint8_t temp) { argo[3] += temp << 5; // Append to bit 5,6,7 argo[4] += temp >> 3; // Remove lowest 3 bits and append in 0,1 } + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRArgoAC::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: + return kArgoFan1; + case stdAc::fanspeed_t::kMedium: + return kArgoFan2; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: + return kArgoFan3; + default: + return kArgoFanAuto; + } +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRArgoAC::convertSwingV(const stdAc::swingv_t position) { + switch (position) { + case stdAc::swingv_t::kHighest: + return kArgoFlapFull; + case stdAc::swingv_t::kHigh: + return kArgoFlap5; + case stdAc::swingv_t::kMiddle: + return kArgoFlap4; + case stdAc::swingv_t::kLow: + return kArgoFlap3; + case stdAc::swingv_t::kLowest: + return kArgoFlap1; + default: + return kArgoFlapAuto; + } +} diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Argo.h b/lib/IRremoteESP8266-2.6.0/src/ir_Argo.h similarity index 91% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Argo.h rename to lib/IRremoteESP8266-2.6.0/src/ir_Argo.h index b49fc3517..883c2ddfd 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Argo.h +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Argo.h @@ -6,6 +6,10 @@ #include "IRremoteESP8266.h" #include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif + // ARGO Ulisse DCI @@ -55,7 +59,7 @@ const uint8_t kArgoFlapFull = 7; // 0b111 #define ARGO_COOL_ON kArgoCoolOn #define ARGO_COOL_OFF kArgoCoolOff #define ARGO_COOL_AUTO kArgoCoolAuto -#define ARGO_COOl_HUM kArgoCoolHum +#define ARGO_COOL_HUM kArgoCoolHum #define ARGO_HEAT_ON kArgoHeatOn #define ARGO_HEAT_AUTO kArgoHeatAuto #define ARGO_HEAT_BLINK kArgoHeatBlink @@ -80,7 +84,7 @@ class IRArgoAC { explicit IRArgoAC(uint16_t pin); #if SEND_ARGO - void send(); + void send(const uint16_t repeat = kArgoDefaultRepeat); #endif // SEND_ARGO void begin(); void on(); @@ -118,13 +122,19 @@ class IRArgoAC { void setRoomTemp(uint8_t temp); uint8_t* getRaw(); + uint8_t convertFan(const stdAc::fanspeed_t speed); + uint8_t convertSwingV(const stdAc::swingv_t position); +#ifndef UNIT_TEST private: + IRsend _irsend; // instance of the IR send class +#else + IRsendTest _irsend; // instance of the testing IR send class +#endif // # of bytes per command uint8_t argo[kArgoStateLength]; // Defined in IRremoteESP8266.h void stateReset(); void checksum(); - IRsend _irsend; // instance of the IR send class // Attributes uint8_t set_temp; diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Carrier.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Carrier.cpp similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Carrier.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_Carrier.cpp diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Coolix.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Coolix.cpp similarity index 72% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Coolix.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_Coolix.cpp index ee539af25..2659a1d88 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Coolix.cpp +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Coolix.cpp @@ -93,25 +93,62 @@ void IRsend::sendCOOLIX(uint64_t data, uint16_t nbits, uint16_t repeat) { // https://github.com/markszabo/IRremoteESP8266/issues/484 IRCoolixAC::IRCoolixAC(uint16_t pin) : _irsend(pin) { stateReset(); } -void IRCoolixAC::stateReset() { remote_state = kCoolixDefaultState; } +void IRCoolixAC::stateReset() { setRaw(kCoolixDefaultState); } void IRCoolixAC::begin() { _irsend.begin(); } #if SEND_COOLIX -void IRCoolixAC::send() { _irsend.sendCOOLIX(remote_state); } +void IRCoolixAC::send(const uint16_t repeat) { + _irsend.sendCOOLIX(remote_state, kCoolixBits, repeat); +} #endif // SEND_COOLIX uint32_t IRCoolixAC::getRaw() { return remote_state; } -void IRCoolixAC::setRaw(const uint32_t new_code) { remote_state = new_code; } +void IRCoolixAC::setRaw(const uint32_t new_code) { + remote_state = new_code; + saved_state = new_code; +} + +// Return true if the current state is a special state. +bool IRCoolixAC::isSpecialState(void) { + switch (remote_state) { + case kCoolixClean: + case kCoolixLed: + case kCoolixOff: + case kCoolixSwing: + case kCoolixSleep: + case kCoolixTurbo: + return true; + default: + return false; + } +} + +void IRCoolixAC::updateSavedState(void) { + if (!isSpecialState()) saved_state = remote_state; +} + +void IRCoolixAC::recoverSavedState(void) { + // If the current state is a special one, last known normal one. + if (isSpecialState()) remote_state = saved_state; + // If the saved_state was also a special state, reset as we expect a normal + // state out of all this. + if (isSpecialState()) stateReset(); +} + +uint32_t IRCoolixAC::getNormalState(void) { + return isSpecialState() ? saved_state : remote_state; +} void IRCoolixAC::setTempRaw(const uint8_t code) { + recoverSavedState(); remote_state &= ~kCoolixTempMask; // Clear the old temp. remote_state |= (code << 4); } uint8_t IRCoolixAC::getTempRaw() { - return (remote_state & kCoolixTempMask) >> 4; + return (getNormalState() & kCoolixTempMask) >> 4; } void IRCoolixAC::setTemp(const uint8_t desired) { @@ -130,6 +167,7 @@ uint8_t IRCoolixAC::getTemp() { } void IRCoolixAC::setSensorTempRaw(const uint8_t code) { + recoverSavedState(); remote_state &= ~kCoolixSensorTempMask; // Clear previous sensor temp. remote_state |= ((code & 0xF) << 8); } @@ -143,7 +181,8 @@ void IRCoolixAC::setSensorTemp(const uint8_t desired) { } uint8_t IRCoolixAC::getSensorTemp() { - return ((remote_state & kCoolixSensorTempMask) >> 8) + kCoolixSensorTempMin; + return ((getNormalState() & kCoolixSensorTempMask) >> 8) + + kCoolixSensorTempMin; } bool IRCoolixAC::getPower() { @@ -152,25 +191,35 @@ bool IRCoolixAC::getPower() { } void IRCoolixAC::setPower(const bool power) { - if (!power) remote_state = kCoolixOff; - // There really is no distinct "on" setting, so do nothing. + if (power) { + // There really is no distinct "on" setting, just ensure it a normal state. + recoverSavedState(); + } else { + updateSavedState(); + remote_state = kCoolixOff; + } } bool IRCoolixAC::getSwing() { return remote_state == kCoolixSwing; } void IRCoolixAC::setSwing() { // Assumes that repeated sending "swing" toggles the action on the device. + updateSavedState(); remote_state = kCoolixSwing; } bool IRCoolixAC::getSleep() { return remote_state == kCoolixSleep; } -void IRCoolixAC::setSleep() { remote_state = kCoolixSleep; } +void IRCoolixAC::setSleep() { + updateSavedState(); + remote_state = kCoolixSleep; +} bool IRCoolixAC::getTurbo() { return remote_state == kCoolixTurbo; } void IRCoolixAC::setTurbo() { // Assumes that repeated sending "turbo" toggles the action on the device. + updateSavedState(); remote_state = kCoolixTurbo; } @@ -178,19 +227,24 @@ bool IRCoolixAC::getLed() { return remote_state == kCoolixLed; } void IRCoolixAC::setLed() { // Assumes that repeated sending "Led" toggles the action on the device. + updateSavedState(); remote_state = kCoolixLed; } bool IRCoolixAC::getClean() { return remote_state == kCoolixClean; } -void IRCoolixAC::setClean() { remote_state = kCoolixClean; } +void IRCoolixAC::setClean() { + updateSavedState(); + remote_state = kCoolixClean; +} bool IRCoolixAC::getZoneFollow() { - return remote_state & kCoolixZoneFollowMask; + return getNormalState() & kCoolixZoneFollowMask; } // Internal use only. void IRCoolixAC::setZoneFollow(bool state) { + recoverSavedState(); if (state) { remote_state |= kCoolixZoneFollowMask; } else { @@ -199,6 +253,7 @@ void IRCoolixAC::setZoneFollow(bool state) { } void IRCoolixAC::clearSensorTemp() { + recoverSavedState(); setZoneFollow(false); setSensorTempRaw(kCoolixSensorTempIgnoreCode); } @@ -212,6 +267,7 @@ void IRCoolixAC::setMode(const uint8_t mode) { case kCoolixAuto: case kCoolixHeat: case kCoolixDry: + recoverSavedState(); remote_state = (remote_state & ~kCoolixModeMask) | (actualmode << 2); // Force the temp into a known-good state. setTemp(getTemp()); @@ -220,21 +276,25 @@ void IRCoolixAC::setMode(const uint8_t mode) { } uint8_t IRCoolixAC::getMode() { - uint8_t mode = (remote_state & kCoolixModeMask) >> 2; + uint8_t mode = (getNormalState() & kCoolixModeMask) >> 2; if (mode == kCoolixDry) if (getTempRaw() == kCoolixFanTempCode) return kCoolixFan; return mode; } -uint8_t IRCoolixAC::getFan() { return (remote_state & kCoolixFanMask) >> 13; } +uint8_t IRCoolixAC::getFan() { + return (getNormalState() & kCoolixFanMask) >> 13; +} void IRCoolixAC::setFan(const uint8_t speed) { + recoverSavedState(); uint8_t newspeed = speed; switch (speed) { case kCoolixFanMin: case kCoolixFanMed: case kCoolixFanMax: case kCoolixFanAuto: + case kCoolixFanAuto0: case kCoolixFanZoneFollow: case kCoolixFanFixed: break; @@ -245,6 +305,38 @@ void IRCoolixAC::setFan(const uint8_t speed) { remote_state |= ((newspeed << 13) & kCoolixFanMask); } +// Convert a standard A/C mode into its native mode. +uint8_t IRCoolixAC::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kCoolixCool; + case stdAc::opmode_t::kHeat: + return kCoolixHeat; + case stdAc::opmode_t::kDry: + return kCoolixDry; + case stdAc::opmode_t::kFan: + return kCoolixFan; + default: + return kCoolixAuto; + } +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRCoolixAC::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: + return kCoolixFanMin; + case stdAc::fanspeed_t::kMedium: + return kCoolixFanMed; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: + return kCoolixFanMax; + default: + return kCoolixFanAuto; + } +} + // Convert the internal state into a human readable string. #ifdef ARDUINO String IRCoolixAC::toString() { @@ -253,89 +345,97 @@ String IRCoolixAC::toString() { std::string IRCoolixAC::toString() { std::string result = ""; #endif // ARDUINO - result += "Power: "; + result += F("Power: "); if (getPower()) { - result += "On"; + result += F("On"); } else { - result += "Off"; + result += F("Off"); return result; // If it's off, there is no other info. } - result += ", Fan: " + uint64ToString(getFan()); - switch (getFan()) { - case kCoolixFanAuto: - result += " (AUTO)"; - break; - case kCoolixFanMax: - result += " (MAX)"; - break; - case kCoolixFanMin: - result += " (MIN)"; - break; - case kCoolixFanMed: - result += " (MED)"; - break; - case kCoolixFanZoneFollow: - result += " (ZONEFOLLOW)"; - break; - case kCoolixFanFixed: - result += " (FIXED)"; - break; - default: - result += " (UNKNOWN)"; - } // Special modes. if (getSwing()) { - result += ", Swing: Toggle"; + result += F(", Swing: Toggle"); return result; } if (getSleep()) { - result += ", Sleep: Toggle"; + result += F(", Sleep: Toggle"); return result; } if (getTurbo()) { - result += ", Turbo: Toggle"; + result += F(", Turbo: Toggle"); return result; } if (getLed()) { - result += ", Led: Toggle"; + result += F(", Led: Toggle"); return result; } if (getClean()) { - result += ", Mode: Self clean"; + result += F(", Clean: Toggle"); return result; } - result += ", Mode: " + uint64ToString(getMode()); + result += F(", Mode: "); + result += uint64ToString(getMode()); switch (getMode()) { case kCoolixAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kCoolixCool: - result += " (COOL)"; + result += F(" (COOL)"); break; case kCoolixHeat: - result += " (HEAT)"; + result += F(" (HEAT)"); break; case kCoolixDry: - result += " (DRY)"; + result += F(" (DRY)"); break; case kCoolixFan: - result += " (FAN)"; + result += F(" (FAN)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); } - if (getMode() != kCoolixFan) // Fan mode doesn't have a temperature. - result += ", Temp: " + uint64ToString(getTemp()) + "C"; - result += ", Zone Follow: "; + result += F(", Fan: "); + result += uint64ToString(getFan()); + switch (getFan()) { + case kCoolixFanAuto: + result += F(" (AUTO)"); + break; + case kCoolixFanAuto0: + result += F(" (AUTO0)"); + break; + case kCoolixFanMax: + result += F(" (MAX)"); + break; + case kCoolixFanMin: + result += F(" (MIN)"); + break; + case kCoolixFanMed: + result += F(" (MED)"); + break; + case kCoolixFanZoneFollow: + result += F(" (ZONEFOLLOW)"); + break; + case kCoolixFanFixed: + result += F(" (FIXED)"); + break; + default: + result += F(" (UNKNOWN)"); + } + if (getMode() != kCoolixFan) { // Fan mode doesn't have a temperature. + result += F(", Temp: "); + result += uint64ToString(getTemp()); + result += 'C'; + } + result += F(", Zone Follow: "); if (getZoneFollow()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Sensor Temp: "; + result += F("Off"); + result += F(", Sensor Temp: "); if (getSensorTemp() > kCoolixSensorTempMax) - result += "Ignored"; + result += F("Ignored"); else - result += uint64ToString(getSensorTemp()) + "C"; + result += uint64ToString(getSensorTemp()) + F("C"); return result; } diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Coolix.h b/lib/IRremoteESP8266-2.6.0/src/ir_Coolix.h similarity index 82% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Coolix.h rename to lib/IRremoteESP8266-2.6.0/src/ir_Coolix.h index ee4552074..d85db98d7 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Coolix.h +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Coolix.h @@ -14,6 +14,9 @@ #endif #include "IRremoteESP8266.h" #include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif // CCCCC OOOOO OOOOO LL IIIII XX XX // CC C OO OO OO OO LL III XX XX @@ -30,11 +33,11 @@ // Constants // Modes -const uint8_t kCoolixCool = 0b00; -const uint8_t kCoolixDry = 0b01; -const uint8_t kCoolixAuto = 0b10; -const uint8_t kCoolixHeat = 0b11; -const uint8_t kCoolixFan = 4; // Synthetic. +const uint8_t kCoolixCool = 0b000; +const uint8_t kCoolixDry = 0b001; +const uint8_t kCoolixAuto = 0b010; +const uint8_t kCoolixHeat = 0b011; +const uint8_t kCoolixFan = 0b100; // Synthetic. const uint32_t kCoolixModeMask = 0b000000000000000000001100; // 0xC const uint32_t kCoolixZoneFollowMask = 0b000010000000000000000000; // 0x80000 // Fan Control @@ -42,6 +45,7 @@ const uint8_t kCoolixFanMin = 0b100; const uint8_t kCoolixFanMed = 0b010; const uint8_t kCoolixFanMax = 0b001; const uint8_t kCoolixFanAuto = 0b101; +const uint8_t kCoolixFanAuto0 = 0b000; const uint8_t kCoolixFanZoneFollow = 0b110; const uint8_t kCoolixFanFixed = 0b111; const uint32_t kCoolixFanMask = 0b000000001110000000000000; // 0x00E000 @@ -90,7 +94,7 @@ class IRCoolixAC { void stateReset(); #if SEND_COOLIX - void send(); + void send(const uint16_t repeat = kCoolixDefaultRepeat); #endif // SEND_COOLIX void begin(); void on(); @@ -119,21 +123,30 @@ class IRCoolixAC { bool getZoneFollow(); uint32_t getRaw(); void setRaw(const uint32_t new_code); - + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); #ifdef ARDUINO String toString(); #else std::string toString(); #endif +#ifndef UNIT_TEST private: - // The state of the IR remote in IR code form. - uint32_t remote_state; IRsend _irsend; +#else + IRsendTest _irsend; +#endif + uint32_t remote_state; // The state of the IR remote in IR code form. + uint32_t saved_state; // Copy of the state if we required a special mode. void setTempRaw(const uint8_t code); uint8_t getTempRaw(); void setSensorTempRaw(const uint8_t code); void setZoneFollow(const bool state); + bool isSpecialState(void); + void updateSavedState(void); + void recoverSavedState(void); + uint32_t getNormalState(void); }; #endif // IR_COOLIX_H_ diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Daikin.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Daikin.cpp new file mode 100644 index 000000000..358dbd603 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Daikin.cpp @@ -0,0 +1,1712 @@ +/* +An Arduino sketch to emulate IR Daikin ARC433** & ARC477A1 remote control unit +Read more at: +http://harizanov.com/2012/02/control-daikin-air-conditioner-over-the-internet/ + +Copyright 2016 sillyfrog +Copyright 2017 sillyfrog, crankyoldgit +Copyright 2018-2019 crankyoldgit +*/ + +#include "ir_Daikin.h" +#include +#ifndef ARDUINO +#include +#endif +#include "IRrecv.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif +#include "IRutils.h" + +// DDDDD AAA IIIII KK KK IIIII NN NN +// DD DD AAAAA III KK KK III NNN NN +// DD DD AA AA III KKKK III NN N NN +// DD DD AAAAAAA III KK KK III NN NNN +// DDDDDD AA AA IIIII KK KK IIIII NN NN + +// Constants +// Ref: +// https://github.com/mharizanov/Daikin-AC-remote-control-over-the-Internet/tree/master/IRremote +// http://rdlab.cdmt.vn/project-2013/daikin-ir-protocol +// https://github.com/markszabo/IRremoteESP8266/issues/582 + +#if SEND_DAIKIN +// Send a Daikin A/C message. +// +// Args: +// data: An array of kDaikinStateLength bytes containing the IR command. +// +// Status: STABLE +// +// Ref: +// IRDaikinESP.cpp +// https://github.com/mharizanov/Daikin-AC-remote-control-over-the-Internet/tree/master/IRremote +// https://github.com/blafois/Daikin-IR-Reverse +void IRsend::sendDaikin(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes < kDaikinStateLengthShort) + return; // Not enough bytes to send a proper message. + + for (uint16_t r = 0; r <= repeat; r++) { + uint16_t offset = 0; + // Send the header, 0b00000 + sendGeneric(0, 0, // No header for the header + kDaikinBitMark, kDaikinOneSpace, kDaikinBitMark, + kDaikinZeroSpace, kDaikinBitMark, kDaikinZeroSpace + kDaikinGap, + (uint64_t)0b00000, kDaikinHeaderLength, 38, false, 0, 50); + // Data #1 + if (nbytes < kDaikinStateLength) { // Are we using the legacy size? + // Do this as a constant to save RAM and keep in flash memory + sendGeneric(kDaikinHdrMark, kDaikinHdrSpace, kDaikinBitMark, + kDaikinOneSpace, kDaikinBitMark, kDaikinZeroSpace, + kDaikinBitMark, kDaikinZeroSpace + kDaikinGap, + kDaikinFirstHeader64, 64, 38, false, 0, 50); + } else { // We are using the newer/more correct size. + sendGeneric(kDaikinHdrMark, kDaikinHdrSpace, kDaikinBitMark, + kDaikinOneSpace, kDaikinBitMark, kDaikinZeroSpace, + kDaikinBitMark, kDaikinZeroSpace + kDaikinGap, + data, kDaikinSection1Length, 38, false, 0, 50); + offset += kDaikinSection1Length; + } + // Data #2 + sendGeneric(kDaikinHdrMark, kDaikinHdrSpace, kDaikinBitMark, + kDaikinOneSpace, kDaikinBitMark, kDaikinZeroSpace, + kDaikinBitMark, kDaikinZeroSpace + kDaikinGap, + data + offset, kDaikinSection2Length, 38, false, 0, 50); + offset += kDaikinSection2Length; + // Data #3 + sendGeneric(kDaikinHdrMark, kDaikinHdrSpace, kDaikinBitMark, + kDaikinOneSpace, kDaikinBitMark, kDaikinZeroSpace, + kDaikinBitMark, kDaikinZeroSpace + kDaikinGap, + data + offset, nbytes - offset, 38, false, 0, 50); + } +} +#endif // SEND_DAIKIN + +IRDaikinESP::IRDaikinESP(uint16_t pin) : _irsend(pin) { stateReset(); } + +void IRDaikinESP::begin(void) { _irsend.begin(); } + +#if SEND_DAIKIN +void IRDaikinESP::send(const uint16_t repeat) { + this->checksum(); + _irsend.sendDaikin(remote, kDaikinStateLength, repeat); +} +#endif // SEND_DAIKIN + +// Verify the checksums are valid for a given state. +// Args: +// state: The array to verify the checksums of. +// length: The size of the state. +// Returns: +// A boolean. +bool IRDaikinESP::validChecksum(uint8_t state[], const uint16_t length) { + // Data #1 + if (length < kDaikinSection1Length || + state[kDaikinByteChecksum1] != sumBytes(state, kDaikinSection1Length - 1)) + return false; + // Data #2 + if (length < kDaikinSection1Length + kDaikinSection2Length || + state[kDaikinByteChecksum2] != sumBytes(state + kDaikinSection1Length, + kDaikinSection2Length - 1)) + return false; + // Data #3 + if (length < kDaikinSection1Length + kDaikinSection2Length + 2 || + state[length - 1] != sumBytes(state + kDaikinSection1Length + + kDaikinSection2Length, + length - (kDaikinSection1Length + + kDaikinSection2Length) - 1)) + return false; + return true; +} + +// Calculate and set the checksum values for the internal state. +void IRDaikinESP::checksum(void) { + remote[kDaikinByteChecksum1] = sumBytes(remote, kDaikinSection1Length - 1); + remote[kDaikinByteChecksum2] = sumBytes(remote + kDaikinSection1Length, + kDaikinSection2Length - 1); + remote[kDaikinByteChecksum3] = sumBytes(remote + kDaikinSection1Length + + kDaikinSection2Length, + kDaikinSection3Length - 1); +} + +void IRDaikinESP::stateReset(void) { + for (uint8_t i = 0; i < kDaikinStateLength; i++) remote[i] = 0x0; + + remote[0] = 0x11; + remote[1] = 0xDA; + remote[2] = 0x27; + remote[4] = 0xC5; + // remote[7] is a checksum byte, it will be set by checksum(). + + remote[8] = 0x11; + remote[9] = 0xDA; + remote[10] = 0x27; + remote[12] = 0x42; + // remote[15] is a checksum byte, it will be set by checksum(). + remote[16] = 0x11; + remote[17] = 0xDA; + remote[18] = 0x27; + remote[21] = 0x49; + remote[22] = 0x1E; + remote[24] = 0xB0; + remote[27] = 0x06; + remote[28] = 0x60; + remote[31] = 0xC0; + // remote[34] is a checksum byte, it will be set by checksum(). + this->checksum(); +} + +uint8_t *IRDaikinESP::getRaw(void) { + this->checksum(); // Ensure correct settings before sending. + return remote; +} + +void IRDaikinESP::setRaw(const uint8_t new_code[], const uint16_t length) { + uint8_t offset = 0; + if (length == kDaikinStateLengthShort) { // Handle the "short" length case. + offset = kDaikinStateLength - kDaikinStateLengthShort; + this->stateReset(); + } + for (uint8_t i = 0; i < length && i < kDaikinStateLength; i++) + remote[i + offset] = new_code[i]; +} + +void IRDaikinESP::on(void) { remote[kDaikinBytePower] |= kDaikinBitPower; } + +void IRDaikinESP::off(void) { remote[kDaikinBytePower] &= ~kDaikinBitPower; } + +void IRDaikinESP::setPower(const bool on) { + if (on) + this->on(); + else + this->off(); +} + +bool IRDaikinESP::getPower(void) { + return remote[kDaikinBytePower] & kDaikinBitPower; +} + +// Set the temp in deg C +void IRDaikinESP::setTemp(const uint8_t temp) { + uint8_t degrees = std::max(temp, kDaikinMinTemp); + degrees = std::min(degrees, kDaikinMaxTemp); + remote[kDaikinByteTemp] = degrees << 1; +} + +uint8_t IRDaikinESP::getTemp(void) { return remote[kDaikinByteTemp] >> 1; } + +// Set the speed of the fan, 1-5 or kDaikinFanAuto or kDaikinFanQuiet +void IRDaikinESP::setFan(const uint8_t fan) { + // Set the fan speed bits, leave low 4 bits alone + uint8_t fanset; + if (fan == kDaikinFanQuiet || fan == kDaikinFanAuto) + fanset = fan; + else if (fan < kDaikinFanMin || fan > kDaikinFanMax) + fanset = kDaikinFanAuto; + else + fanset = 2 + fan; + remote[kDaikinByteFan] &= 0x0F; + remote[kDaikinByteFan] |= (fanset << 4); +} + +uint8_t IRDaikinESP::getFan(void) { + uint8_t fan = remote[kDaikinByteFan] >> 4; + if (fan != kDaikinFanQuiet && fan != kDaikinFanAuto) fan -= 2; + return fan; +} + +uint8_t IRDaikinESP::getMode(void) { return remote[kDaikinBytePower] >> 4; } + +void IRDaikinESP::setMode(const uint8_t mode) { + switch (mode) { + case kDaikinAuto: + case kDaikinCool: + case kDaikinHeat: + case kDaikinFan: + case kDaikinDry: + remote[kDaikinBytePower] &= 0b10001111; + remote[kDaikinBytePower] |= (mode << 4); + break; + default: + this->setMode(kDaikinAuto); + } +} + +void IRDaikinESP::setSwingVertical(const bool on) { + if (on) + remote[kDaikinByteFan] |= 0x0F; + else + remote[kDaikinByteFan] &= 0xF0; +} + +bool IRDaikinESP::getSwingVertical(void) { + return remote[kDaikinByteFan] & 0x0F; +} + +void IRDaikinESP::setSwingHorizontal(const bool on) { + if (on) + remote[kDaikinByteSwingH] |= 0x0F; + else + remote[kDaikinByteSwingH] &= 0xF0; +} + +bool IRDaikinESP::getSwingHorizontal(void) { + return remote[kDaikinByteSwingH] & 0x0F; +} + +void IRDaikinESP::setQuiet(const bool on) { + if (on) { + remote[kDaikinByteSilent] |= kDaikinBitSilent; + // Powerful & Quiet mode being on are mutually exclusive. + this->setPowerful(false); + } else { + remote[kDaikinByteSilent] &= ~kDaikinBitSilent; + } +} + +bool IRDaikinESP::getQuiet(void) { + return remote[kDaikinByteSilent] & kDaikinBitSilent; +} + +void IRDaikinESP::setPowerful(const bool on) { + if (on) { + remote[kDaikinBytePowerful] |= kDaikinBitPowerful; + // Powerful, Quiet, & Econo mode being on are mutually exclusive. + this->setQuiet(false); + this->setEcono(false); + } else { + remote[kDaikinBytePowerful] &= ~kDaikinBitPowerful; + } +} + +bool IRDaikinESP::getPowerful(void) { + return remote[kDaikinBytePowerful] & kDaikinBitPowerful; +} + +void IRDaikinESP::setSensor(const bool on) { + if (on) + remote[kDaikinByteSensor] |= kDaikinBitSensor; + else + remote[kDaikinByteSensor] &= ~kDaikinBitSensor; +} + +bool IRDaikinESP::getSensor(void) { + return remote[kDaikinByteSensor] & kDaikinBitSensor; +} + +void IRDaikinESP::setEcono(const bool on) { + if (on) { + remote[kDaikinByteEcono] |= kDaikinBitEcono; + // Powerful & Econo mode being on are mutually exclusive. + this->setPowerful(false); + } else { + remote[kDaikinByteEcono] &= ~kDaikinBitEcono; + } +} + +bool IRDaikinESP::getEcono(void) { + return remote[kDaikinByteEcono] & kDaikinBitEcono; +} + +void IRDaikinESP::setEye(const bool on) { + if (on) + remote[kDaikinByteEye] |= kDaikinBitEye; + else + remote[kDaikinByteEye] &= ~kDaikinBitEye; +} + +bool IRDaikinESP::getEye(void) { + return remote[kDaikinByteEye] & kDaikinBitEye; +} + +void IRDaikinESP::setMold(const bool on) { + if (on) + remote[kDaikinByteMold] |= kDaikinBitMold; + else + remote[kDaikinByteMold] &= ~kDaikinBitMold; +} + +bool IRDaikinESP::getMold(void) { + return remote[kDaikinByteMold] & kDaikinBitMold; +} + +void IRDaikinESP::setComfort(const bool on) { + if (on) + remote[kDaikinByteComfort] |= kDaikinBitComfort; + else + remote[kDaikinByteComfort] &= ~kDaikinBitComfort; +} + +bool IRDaikinESP::getComfort(void) { + return remote[kDaikinByteComfort] & kDaikinBitComfort; +} + +// starttime: Number of minutes after midnight. +void IRDaikinESP::enableOnTimer(const uint16_t starttime) { + remote[kDaikinByteOnTimer] |= kDaikinBitOnTimer; + remote[kDaikinByteOnTimerMinsLow] = starttime; + // only keep 4 bits + remote[kDaikinByteOnTimerMinsHigh] &= 0xF0; + remote[kDaikinByteOnTimerMinsHigh] |= ((starttime >> 8) & 0x0F); +} + +void IRDaikinESP::disableOnTimer(void) { + this->enableOnTimer(kDaikinUnusedTime); + remote[kDaikinByteOnTimer] &= ~kDaikinBitOnTimer; +} + +uint16_t IRDaikinESP::getOnTime(void) { + return ((remote[kDaikinByteOnTimerMinsHigh] & 0x0F) << 8) + + remote[kDaikinByteOnTimerMinsLow]; +} + +bool IRDaikinESP::getOnTimerEnabled(void) { + return remote[kDaikinByteOnTimer] & kDaikinBitOnTimer; +} + +// endtime: Number of minutes after midnight. +void IRDaikinESP::enableOffTimer(const uint16_t endtime) { + remote[kDaikinByteOffTimer] |= kDaikinBitOffTimer; + remote[kDaikinByteOffTimerMinsHigh] = endtime >> 4; + remote[kDaikinByteOffTimerMinsLow] &= 0x0F; + remote[kDaikinByteOffTimerMinsLow] |= ((endtime & 0x0F) << 4); +} + +void IRDaikinESP::disableOffTimer(void) { + this->enableOffTimer(kDaikinUnusedTime); + remote[kDaikinByteOffTimer] &= ~kDaikinBitOffTimer; +} + +uint16_t IRDaikinESP::getOffTime(void) { + return (remote[kDaikinByteOffTimerMinsHigh] << 4) + + ((remote[kDaikinByteOffTimerMinsLow] & 0xF0) >> 4); +} + +bool IRDaikinESP::getOffTimerEnabled(void) { + return remote[kDaikinByteOffTimer] & kDaikinBitOffTimer; +} + +void IRDaikinESP::setCurrentTime(const uint16_t mins_since_midnight) { + uint16_t mins = mins_since_midnight; + if (mins > 24 * 60) mins = 0; // If > 23:59, set to 00:00 + remote[kDaikinByteClockMinsLow] = mins; + // only keep 4 bits + remote[kDaikinByteClockMinsHigh] &= 0xF0; + remote[kDaikinByteClockMinsHigh] |= ((mins >> 8) & 0x0F); +} + +uint16_t IRDaikinESP::getCurrentTime(void) { + return ((remote[kDaikinByteClockMinsHigh] & 0x0F) << 8) + + remote[kDaikinByteClockMinsLow]; +} + +#ifdef ARDUINO +String IRDaikinESP::renderTime(const uint16_t timemins) { + String ret; +#else // ARDUINO +std::string IRDaikinESP::renderTime(const uint16_t timemins) { + std::string ret; +#endif // ARDUINO + ret = uint64ToString(timemins / 60) + ':'; + uint8_t mins = timemins % 60; + if (mins < 10) ret += '0'; + ret += uint64ToString(mins); + return ret; +} + +// Convert the internal state into a human readable string. +#ifdef ARDUINO +String IRDaikinESP::toString(void) { + String result = ""; +#else // ARDUINO +std::string IRDaikinESP::toString(void) { + std::string result = ""; +#endif // ARDUINO + result += F("Power: "); + result += this->getPower() ? F("On") : F("Off"); + result += F(", Mode: "); + result += uint64ToString(this->getMode()); + switch (this->getMode()) { + case kDaikinAuto: + result += F(" (AUTO)"); + break; + case kDaikinCool: + result += F(" (COOL)"); + break; + case kDaikinHeat: + result += F(" (HEAT)"); + break; + case kDaikinDry: + result += F(" (DRY)"); + break; + case kDaikinFan: + result += F(" (FAN)"); + break; + default: + result += F(" (UNKNOWN)"); + } + result += F(", Temp: "); + result += uint64ToString(this->getTemp()); + result += F("C, Fan: "); + result += uint64ToString(this->getFan()); + switch (this->getFan()) { + case kDaikinFanAuto: + result += F(" (AUTO)"); + break; + case kDaikinFanQuiet: + result += F(" (QUIET)"); + break; + case kDaikinFanMin: + result += F(" (MIN)"); + break; + case kDaikinFanMax: + result += F(" (MAX)"); + break; + } + result += F(", Powerful: "); + result += this->getPowerful() ? F("On") : F("Off"); + result += F(", Quiet: "); + result += this->getQuiet() ? F("On") : F("Off"); + result += F(", Sensor: "); + result += this->getSensor() ? F("On") : F("Off"); + result += F(", Eye: "); + result += this->getEye() ? F("On") : F("Off"); + result += F(", Mold: "); + result += this->getMold() ? F("On") : F("Off"); + result += F(", Comfort: "); + result += this->getComfort() ? F("On") : F("Off"); + result += F(", Swing (Horizontal): "); + result += this->getSwingHorizontal() ? F("On") : F("Off"); + result += F(", Swing (Vertical): "); + result += this->getSwingVertical() ? F("On") : F("Off"); + result += F(", Current Time: "); + result += this->renderTime(this->getCurrentTime()); + result += F(", On Time: "); + if (this->getOnTimerEnabled()) + result += this->renderTime(this->getOnTime()); + else + result += F("Off"); + result += F(", Off Time: "); + if (this->getOffTimerEnabled()) + result += this->renderTime(this->getOffTime()); + else + result += F("Off"); + return result; +} + +// Convert a standard A/C mode into its native mode. +uint8_t IRDaikinESP::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kDaikinCool; + case stdAc::opmode_t::kHeat: + return kDaikinHeat; + case stdAc::opmode_t::kDry: + return kDaikinDry; + case stdAc::opmode_t::kFan: + return kDaikinFan; + default: + return kDaikinAuto; + } +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRDaikinESP::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + return kDaikinFanQuiet; + case stdAc::fanspeed_t::kLow: + return kDaikinFanMin; + case stdAc::fanspeed_t::kMedium: + return kDaikinFanMin + 1; + case stdAc::fanspeed_t::kHigh: + return kDaikinFanMax - 1; + case stdAc::fanspeed_t::kMax: + return kDaikinFanMax; + default: + return kDaikinFanAuto; + } +} + +#if DECODE_DAIKIN +// Decode the supplied Daikin A/C message. +// Args: +// results: Ptr to the data to decode and where to store the decode result. +// nbits: Nr. of bits to expect in the data portion. (kDaikinBits) +// strict: Flag to indicate if we strictly adhere to the specification. +// Returns: +// boolean: True if it can decode it, false if it can't. +// +// Status: BETA / Should be working. +// +// Ref: +// https://github.com/mharizanov/Daikin-AC-remote-control-over-the-Internet/tree/master/IRremote +bool IRrecv::decodeDaikin(decode_results *results, const uint16_t nbits, + const bool strict) { + // Is there enough data to match successfully? + if (results->rawlen < (2 * (nbits + kDaikinHeaderLength) + + kDaikinSections * (kHeader + kFooter) + kFooter - 1)) + return false; + + // Compliance + if (strict && nbits != kDaikinBits) return false; + + uint16_t offset = kStartOffset; + match_result_t data_result; + uint16_t dataBitsSoFar = 0; + uint16_t i = 0; + + // Header #1 - Doesn't count as data. + data_result = matchData(&(results->rawbuf[offset]), kDaikinHeaderLength, + kDaikinBitMark, kDaikinOneSpace, + kDaikinBitMark, kDaikinZeroSpace, + kDaikinTolerance, kDaikinMarkExcess, false); + offset += data_result.used; + if (data_result.success == false) return false; // Fail + if (data_result.data) return false; // The header bits should be zero. + + // Read the Data sections. + // Keep reading bytes until we either run out of section or state to fill. + const uint8_t kSectionSize[kDaikinSections] = { + kDaikinSection1Length, kDaikinSection2Length, kDaikinSection3Length}; + for (uint8_t section = 0, pos = 0; section < kDaikinSections; + section++) { + pos += kSectionSize[section]; + // Section Footer + if (!matchMark(results->rawbuf[offset++], kDaikinBitMark, + kDaikinTolerance, kDaikinMarkExcess)) return false; + if (!matchSpace(results->rawbuf[offset++], kDaikinZeroSpace + kDaikinGap, + kDaikinTolerance, kDaikinMarkExcess)) return false; + // Section Header + if (!matchMark(results->rawbuf[offset++], kDaikinHdrMark, + kDaikinTolerance, kDaikinMarkExcess)) return false; + if (!matchSpace(results->rawbuf[offset++], kDaikinHdrSpace, + kDaikinTolerance, kDaikinMarkExcess)) return false; + + // Section Data + for (; offset <= results->rawlen - 16 && i < pos; + i++, dataBitsSoFar += 8, offset += data_result.used) { + // Read in a byte at a time. + data_result = + matchData(&(results->rawbuf[offset]), 8, + kDaikinBitMark, kDaikinOneSpace, + kDaikinBitMark, kDaikinZeroSpace, + kDaikinTolerance, kDaikinMarkExcess, false); + if (data_result.success == false) break; // Fail + results->state[i] = (uint8_t)data_result.data; + } + } + + // Footer + if (!matchMark(results->rawbuf[offset++], kDaikinBitMark)) return false; + if (offset < results->rawlen && + !matchAtLeast(results->rawbuf[offset], kDaikinGap)) + return false; + + // Compliance + if (strict) { + // Re-check we got the correct size/length due to the way we read the data. + if (dataBitsSoFar != kDaikinBits) return false; + // Validate the checksum. + if (!IRDaikinESP::validChecksum(results->state)) return false; + } + + // Success + results->decode_type = DAIKIN; + results->bits = dataBitsSoFar; + // No need to record the state as we stored it as we decoded it. + // As we use result->state, we don't record value, address, or command as it + // is a union data type. + return true; +} +#endif // DECODE_DAIKIN + +#if SEND_DAIKIN2 +// Send a Daikin2 A/C message. +// +// Args: +// data: An array of kDaikin2StateLength bytes containing the IR command. +// +// Status: BETA/Appears to work. +// +// Ref: +// https://github.com/markszabo/IRremoteESP8266/issues/582 +void IRsend::sendDaikin2(unsigned char data[], uint16_t nbytes, + uint16_t repeat) { + if (nbytes < kDaikin2Section1Length) + return; // Not enough bytes to send a partial message. + + for (uint16_t r = 0; r <= repeat; r++) { + // Leader + sendGeneric(kDaikin2LeaderMark, kDaikin2LeaderSpace, + 0, 0, 0, 0, 0, 0, (uint64_t) 0, // No data payload. + 0, kDaikin2Freq, false, 0, 50); + // Section #1 + sendGeneric(kDaikin2HdrMark, kDaikin2HdrSpace, kDaikin2BitMark, + kDaikin2OneSpace, kDaikin2BitMark, kDaikin2ZeroSpace, + kDaikin2BitMark, kDaikin2Gap, data, kDaikin2Section1Length, + kDaikin2Freq, false, 0, 50); + // Section #2 + sendGeneric(kDaikin2HdrMark, kDaikin2HdrSpace, kDaikin2BitMark, + kDaikin2OneSpace, kDaikin2BitMark, kDaikin2ZeroSpace, + kDaikin2BitMark, kDaikin2Gap, data + kDaikin2Section1Length, + nbytes - kDaikin2Section1Length, + kDaikin2Freq, false, 0, 50); + } +} +#endif // SEND_DAIKIN2 + +// Class for handling Daikin2 A/C messages. +// +// Code by crankyoldgit, Reverse engineering analysis by sheppy99 +// +// Supported Remotes: Daikin ARC477A1 remote +// +// Ref: +// https://github.com/markszabo/IRremoteESP8266/issues/582 +// https://docs.google.com/spreadsheets/d/1f8EGfIbBUo2B-CzUFdrgKQprWakoYNKM80IKZN4KXQE/edit?usp=sharing +// https://www.daikin.co.nz/sites/default/files/daikin-split-system-US7-FTXZ25-50NV1B.pdf +IRDaikin2::IRDaikin2(uint16_t pin) : _irsend(pin) { stateReset(); } + +void IRDaikin2::begin() { _irsend.begin(); } + +#if SEND_DAIKIN2 +void IRDaikin2::send(const uint16_t repeat) { + checksum(); + _irsend.sendDaikin2(remote_state, kDaikin2StateLength, repeat); +} +#endif // SEND_DAIKIN2 + +// Verify the checksum is valid for a given state. +// Args: +// state: The array to verify the checksum of. +// length: The size of the state. +// Returns: +// A boolean. +bool IRDaikin2::validChecksum(uint8_t state[], const uint16_t length) { + // Validate the checksum of section #1. + if (length <= kDaikin2Section1Length - 1 || + state[kDaikin2Section1Length - 1] != sumBytes(state, + kDaikin2Section1Length - 1)) + return false; + // Validate the checksum of section #2 (a.k.a. the rest) + if (length <= kDaikin2Section1Length + 1 || + state[length - 1] != sumBytes(state + kDaikin2Section1Length, + length - kDaikin2Section1Length - 1)) + return false; + return true; +} + +// Calculate and set the checksum values for the internal state. +void IRDaikin2::checksum() { + remote_state[kDaikin2Section1Length - 1] = sumBytes( + remote_state, kDaikin2Section1Length - 1); + remote_state[kDaikin2StateLength -1 ] = sumBytes( + remote_state + kDaikin2Section1Length, kDaikin2Section2Length - 1); +} + +void IRDaikin2::stateReset() { + for (uint8_t i = 0; i < kDaikin2StateLength; i++) remote_state[i] = 0x0; + + remote_state[0] = 0x11; + remote_state[1] = 0xDA; + remote_state[2] = 0x27; + remote_state[4] = 0x01; + remote_state[6] = 0xC0; + remote_state[7] = 0x70; + remote_state[8] = 0x08; + remote_state[9] = 0x0C; + remote_state[10] = 0x80; + remote_state[11] = 0x04; + remote_state[12] = 0xB0; + remote_state[13] = 0x16; + remote_state[14] = 0x24; + remote_state[17] = 0xBE; + remote_state[18] = 0xD0; + // remote_state[19] is a checksum byte, it will be set by checksum(). + remote_state[20] = 0x11; + remote_state[21] = 0xDA; + remote_state[22] = 0x27; + remote_state[25] = 0x08; + remote_state[28] = 0xA0; + remote_state[35] = 0xC1; + remote_state[36] = 0x80; + remote_state[37] = 0x60; + // remote_state[38] is a checksum byte, it will be set by checksum(). + disableOnTimer(); + disableOffTimer(); + disableSleepTimer(); + checksum(); +} + +uint8_t *IRDaikin2::getRaw() { + checksum(); // Ensure correct settings before sending. + return remote_state; +} + +void IRDaikin2::setRaw(const uint8_t new_code[]) { + for (uint8_t i = 0; i < kDaikin2StateLength; i++) + remote_state[i] = new_code[i]; +} + +void IRDaikin2::on() { + remote_state[25] |= kDaikinBitPower; + remote_state[6] &= ~kDaikin2BitPower; +} + +void IRDaikin2::off() { + remote_state[25] &= ~kDaikinBitPower; + remote_state[6] |= kDaikin2BitPower; +} + +void IRDaikin2::setPower(const bool state) { + if (state) + on(); + else + off(); +} + +bool IRDaikin2::getPower() { + return (remote_state[25] & kDaikinBitPower) && + !(remote_state[6] & kDaikin2BitPower); +} + +uint8_t IRDaikin2::getMode() { return remote_state[25] >> 4; } + +void IRDaikin2::setMode(const uint8_t desired_mode) { + uint8_t mode = desired_mode; + switch (mode) { + case kDaikinCool: + case kDaikinHeat: + case kDaikinFan: + case kDaikinDry: + break; + default: + mode = kDaikinAuto; + } + remote_state[25] &= 0b10001111; + remote_state[25] |= (mode << 4); + // Redo the temp setting as Cool mode has a different min temp. + if (mode == kDaikinCool) this->setTemp(this->getTemp()); +} + +// Set the temp in deg C +void IRDaikin2::setTemp(const uint8_t desired) { + // The A/C has a different min temp if in cool mode. + uint8_t temp = std::max( + (this->getMode() == kDaikinCool) ? kDaikin2MinCoolTemp : kDaikinMinTemp, + desired); + temp = std::min(kDaikinMaxTemp, temp); + remote_state[26] = temp * 2; +} + +// Set the speed of the fan, 1-5 or kDaikinFanAuto or kDaikinFanQuiet +void IRDaikin2::setFan(const uint8_t fan) { + // Set the fan speed bits, leave low 4 bits alone + uint8_t fanset; + if (fan == kDaikinFanQuiet || fan == kDaikinFanAuto) + fanset = fan; + else if (fan < kDaikinFanMin || fan > kDaikinFanMax) + fanset = kDaikinFanAuto; + else + fanset = 2 + fan; + remote_state[28] &= 0x0F; + remote_state[28] |= (fanset << 4); +} + +uint8_t IRDaikin2::getFan() { + uint8_t fan = remote_state[28] >> 4; + if (fan != kDaikinFanQuiet && fan != kDaikinFanAuto) fan -= 2; + return fan; +} + +uint8_t IRDaikin2::getTemp() { return remote_state[26] / 2; } + +void IRDaikin2::setSwingVertical(const uint8_t position) { + switch (position) { + case kDaikin2SwingVHigh: + case 2: + case 3: + case 4: + case 5: + case kDaikin2SwingVLow: + case kDaikin2SwingVBreeze: + case kDaikin2SwingVCirculate: + case kDaikin2SwingVAuto: + remote_state[18] &= 0xF0; + remote_state[18] |= (position & 0x0F); + } +} + +uint8_t IRDaikin2::getSwingVertical() { return remote_state[18] & 0x0F; } + +void IRDaikin2::setSwingHorizontal(const uint8_t position) { + remote_state[17] = position; +} + +uint8_t IRDaikin2::getSwingHorizontal() { return remote_state[17]; } + +void IRDaikin2::setCurrentTime(const uint16_t numMins) { + uint16_t mins = numMins; + if (numMins > 24 * 60) mins = 0; // If > 23:59, set to 00:00 + remote_state[5] = (uint8_t)(mins & 0xFF); + // only keep 4 bits + remote_state[6] &= 0xF0; + remote_state[6] |= (uint8_t)((mins >> 8) & 0x0F); +} + +uint16_t IRDaikin2::getCurrentTime() { + return ((remote_state[6] & 0x0F) << 8) + remote_state[5]; +} + +// starttime: Number of minutes after midnight. +// Note: Timer location is shared with sleep timer. +void IRDaikin2::enableOnTimer(const uint16_t starttime) { + clearSleepTimerFlag(); + remote_state[25] |= kDaikinBitOnTimer; // Set the On Timer flag. + remote_state[30] = (uint8_t)(starttime & 0xFF); + // only keep 4 bits + remote_state[31] &= 0xF0; + remote_state[31] |= (uint8_t)((starttime >> 8) & 0x0F); +} + +void IRDaikin2::clearOnTimerFlag() { + remote_state[25] &= ~kDaikinBitOnTimer; +} + +void IRDaikin2::disableOnTimer() { + enableOnTimer(kDaikinUnusedTime); + clearOnTimerFlag(); + clearSleepTimerFlag(); +} + +uint16_t IRDaikin2::getOnTime() { + return ((remote_state[31] & 0x0F) << 8) + remote_state[30]; +} + +bool IRDaikin2::getOnTimerEnabled() { + return remote_state[25] & kDaikinBitOnTimer; +} + +// endtime: Number of minutes after midnight. +void IRDaikin2::enableOffTimer(const uint16_t endtime) { + remote_state[25] |= kDaikinBitOffTimer; // Set the Off Timer flag. + remote_state[32] = (uint8_t)((endtime >> 4) & 0xFF); + remote_state[31] &= 0x0F; + remote_state[31] |= (uint8_t)((endtime & 0xF) << 4); +} + +void IRDaikin2::disableOffTimer() { + enableOffTimer(kDaikinUnusedTime); + remote_state[25] &= ~kDaikinBitOffTimer; // Clear the Off Timer flag. +} + +uint16_t IRDaikin2::getOffTime() { + return (remote_state[32] << 4) + (remote_state[31] >> 4); +} + +bool IRDaikin2::getOffTimerEnabled() { + return remote_state[25] & kDaikinBitOffTimer; +} + +uint8_t IRDaikin2::getBeep() { + return remote_state[7] >> 6; +} + +void IRDaikin2::setBeep(const uint8_t beep) { + remote_state[7] &= ~kDaikin2BeepMask; + remote_state[7] |= ((beep << 6) & kDaikin2BeepMask); +} + +uint8_t IRDaikin2::getLight() { + return (remote_state[7] & kDaikin2LightMask) >> 4; +} + +void IRDaikin2::setLight(const uint8_t light) { + remote_state[7] &= ~kDaikin2LightMask; + remote_state[7] |= ((light << 4) & kDaikin2LightMask); +} + +void IRDaikin2::setMold(const bool on) { + if (on) + remote_state[8] |= kDaikin2BitMold; + else + remote_state[8] &= ~kDaikin2BitMold; +} + +bool IRDaikin2::getMold() { + return remote_state[8] & kDaikin2BitMold; +} + +// Auto clean setting. +void IRDaikin2::setClean(const bool on) { + if (on) + remote_state[8] |= kDaikin2BitClean; + else + remote_state[8] &= ~kDaikin2BitClean; +} + +bool IRDaikin2::getClean() { + return remote_state[8] & kDaikin2BitClean; +} + +// Fresh Air settings. +void IRDaikin2::setFreshAir(const bool on) { + if (on) + remote_state[8] |= kDaikin2BitFreshAir; + else + remote_state[8] &= ~kDaikin2BitFreshAir; +} + +bool IRDaikin2::getFreshAir() { + return remote_state[8] & kDaikin2BitFreshAir; +} + +void IRDaikin2::setFreshAirHigh(const bool on) { + if (on) + remote_state[8] |= kDaikin2BitFreshAirHigh; + else + remote_state[8] &= ~kDaikin2BitFreshAirHigh; +} + +bool IRDaikin2::getFreshAirHigh() { + return remote_state[8] & kDaikin2BitFreshAirHigh; +} + +void IRDaikin2::setEyeAuto(bool on) { + if (on) + remote_state[13] |= kDaikin2BitEyeAuto; + else + remote_state[13] &= ~kDaikin2BitEyeAuto; +} + +bool IRDaikin2::getEyeAuto() { + return remote_state[13] & kDaikin2BitEyeAuto; +} + +void IRDaikin2::setEye(bool on) { + if (on) + remote_state[36] |= kDaikin2BitEye; + else + remote_state[36] &= ~kDaikin2BitEye; +} + +bool IRDaikin2::getEye() { + return remote_state[36] & kDaikin2BitEye; +} + +void IRDaikin2::setEcono(bool on) { + if (on) + remote_state[36] |= kDaikinBitEcono; + else + remote_state[36] &= ~kDaikinBitEcono; +} + +bool IRDaikin2::getEcono() { + return remote_state[36] & kDaikinBitEcono; +} + +// sleeptime: Number of minutes. +// Note: Timer location is shared with On Timer. +void IRDaikin2::enableSleepTimer(const uint16_t sleeptime) { + enableOnTimer(sleeptime); + clearOnTimerFlag(); + remote_state[36] |= kDaikin2BitSleepTimer; // Set the Sleep Timer flag. +} + +void IRDaikin2::clearSleepTimerFlag() { + remote_state[36] &= ~kDaikin2BitSleepTimer; +} + +void IRDaikin2::disableSleepTimer() { + disableOnTimer(); +} + +uint16_t IRDaikin2::getSleepTime() { + return getOnTime(); +} + +bool IRDaikin2::getSleepTimerEnabled() { + return remote_state[36] & kDaikin2BitSleepTimer; +} + +void IRDaikin2::setQuiet(const bool on) { + if (on) { + remote_state[33] |= kDaikinBitSilent; + // Powerful & Quiet mode being on are mutually exclusive. + setPowerful(false); + } else { + remote_state[33] &= ~kDaikinBitSilent; + } +} + +bool IRDaikin2::getQuiet() { return remote_state[33] & kDaikinBitSilent; } + +void IRDaikin2::setPowerful(const bool on) { + if (on) { + remote_state[33] |= kDaikinBitPowerful; + // Powerful & Quiet mode being on are mutually exclusive. + setQuiet(false); + } else { + remote_state[33] &= ~kDaikinBitPowerful; + } +} + +bool IRDaikin2::getPowerful() { return remote_state[33] & kDaikinBitPowerful; } + +void IRDaikin2::setPurify(const bool on) { + if (on) + remote_state[36] |= kDaikin2BitPurify; + else + remote_state[36] &= ~kDaikin2BitPurify; +} + +bool IRDaikin2::getPurify() { return remote_state[36] & kDaikin2BitPurify; } + +// Convert a standard A/C mode into its native mode. +uint8_t IRDaikin2::convertMode(const stdAc::opmode_t mode) { + return IRDaikinESP::convertMode(mode); +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRDaikin2::convertFan(const stdAc::fanspeed_t speed) { + return IRDaikinESP::convertFan(speed); +} + +// Convert a standard A/C vertical swing into its native version. +uint8_t IRDaikin2::convertSwingV(const stdAc::swingv_t position) { + switch (position) { + case stdAc::swingv_t::kHighest: + case stdAc::swingv_t::kHigh: + case stdAc::swingv_t::kMiddle: + case stdAc::swingv_t::kLow: + case stdAc::swingv_t::kLowest: + return (uint8_t)position + kDaikin2SwingVHigh; + default: + return kDaikin2SwingVAuto; + } +} + +// Convert the internal state into a human readable string. +#ifdef ARDUINO +String IRDaikin2::toString() { + String result = ""; +#else // ARDUINO +std::string IRDaikin2::toString() { + std::string result = ""; +#endif // ARDUINO + result += F("Power: "); + if (getPower()) + result += F("On"); + else + result += F("Off"); + result += F(", Mode: "); + result += uint64ToString(getMode()); + switch (getMode()) { + case kDaikinAuto: + result += F(" (AUTO)"); + break; + case kDaikinCool: + result += F(" (COOL)"); + break; + case kDaikinHeat: + result += F(" (HEAT)"); + break; + case kDaikinDry: + result += F(" (DRY)"); + break; + case kDaikinFan: + result += F(" (FAN)"); + break; + default: + result += F(" (UNKNOWN)"); + } + result += F(", Temp: "); + result += uint64ToString(getTemp()); + result += F("C, Fan: "); + result += uint64ToString(getFan()); + switch (getFan()) { + case kDaikinFanAuto: + result += F(" (Auto)"); + break; + case kDaikinFanQuiet: + result += F(" (Quiet)"); + break; + case kDaikinFanMin: + result += F(" (Min)"); + break; + case kDaikinFanMax: + result += F(" (Max)"); + break; + } + result += F(", Swing (V): "); + result += uint64ToString(getSwingVertical()); + switch (getSwingVertical()) { + case kDaikin2SwingVHigh: + result += F(" (Highest)"); + break; + case 2: + case 3: + case 4: + case 5: + break; + case kDaikin2SwingVLow: + result += F(" (Lowest)"); + break; + case kDaikin2SwingVBreeze: + result += F(" (Breeze)"); + break; + case kDaikin2SwingVCirculate: + result += F(" (Circulate)"); + break; + case kDaikin2SwingVAuto: + result += F(" (Auto)"); + break; + default: + result += F(" (Unknown)"); + } + result += F(", Swing (H): "); + result += uint64ToString(getSwingHorizontal()); + switch (getSwingHorizontal()) { + case kDaikin2SwingHAuto: + result += F(" (Auto)"); + break; + case kDaikin2SwingHSwing: + result += F(" (Swing)"); + break; + } + result += F(", Clock: "); + result += IRDaikinESP::renderTime(getCurrentTime()); + result += F(", On Time: "); + if (getOnTimerEnabled()) + result += IRDaikinESP::renderTime(getOnTime()); + else + result += F("Off"); + result += F(", Off Time: "); + if (getOffTimerEnabled()) + result += IRDaikinESP::renderTime(getOffTime()); + else + result += F("Off"); + result += F(", Sleep Time: "); + if (getSleepTimerEnabled()) + result += IRDaikinESP::renderTime(getSleepTime()); + else + result += F("Off"); + result += F(", Beep: "); + result += uint64ToString(getBeep()); + switch (getBeep()) { + case kDaikinBeepLoud: + result += F(" (Loud)"); + break; + case kDaikinBeepQuiet: + result += F(" (Quiet)"); + break; + case kDaikinBeepOff: + result += F(" (Off)"); + break; + default: + result += F(" (UNKNOWN)"); + } + result += F(", Light: "); + result += uint64ToString(getLight()); + switch (getLight()) { + case kDaikinLightBright: + result += F(" (Bright)"); + break; + case kDaikinLightDim: + result += F(" (Dim)"); + break; + case kDaikinLightOff: + result += F(" (Off)"); + break; + default: + result += F(" (UNKNOWN)"); + } + result += F(", Mold: "); + result += (getMold() ? F("On") : F("Off")); + result += F(", Clean: "); + result += (getClean() ? F("On") : F("Off")); + result += F(", Fresh Air: "); + if (getFreshAir()) + result += (getFreshAirHigh() ? "High" : "On"); + else + result += F("Off"); + result += F(", Eye: "); + result += (getEye() ? F("On") : F("Off")); + result += F(", Eye Auto: "); + result += (getEyeAuto() ? F("On") : F("Off")); + result += F(", Quiet: "); + result += (getQuiet() ? F("On") : F("Off")); + result += F(", Powerful: "); + result += (getPowerful() ? F("On") : F("Off")); + result += ", Purify: "; + result += (getPurify() ? F("On") : F("Off")); + result += F(", Econo: "); + result += (getEcono() ? F("On") : F("Off")); + return result; +} + +#if DECODE_DAIKIN2 +// Decode the supplied Daikin2 A/C message. +// Args: +// results: Ptr to the data to decode and where to store the decode result. +// nbits: Nr. of bits to expect in the data portion. (kDaikin2Bits) +// strict: Flag to indicate if we strictly adhere to the specification. +// Returns: +// boolean: True if it can decode it, false if it can't. +// +// Supported devices: +// - Daikin FTXZ25NV1B, FTXZ35NV1B, FTXZ50NV1B Aircon +// - Daikin ARC477A1 remote +// +// Status: BETA / Work as expected. +// +// Ref: +// https://github.com/mharizanov/Daikin-AC-remote-control-over-the-Internet/tree/master/IRremote +bool IRrecv::decodeDaikin2(decode_results *results, uint16_t nbits, + bool strict) { + if (results->rawlen < 2 * (nbits + kHeader + kFooter) + kHeader - 1) + return false; + + // Compliance + if (strict && nbits != kDaikin2Bits) return false; + + uint16_t offset = kStartOffset; + uint16_t dataBitsSoFar = 0; + uint16_t i = 0; + match_result_t data_result; + uint8_t sectionSize[kDaikin2Sections] = {kDaikin2Section1Length, + kDaikin2Section2Length}; + + // Leader + if (!matchMark(results->rawbuf[offset++], kDaikin2LeaderMark, + kDaikin2Tolerance)) return false; + if (!matchSpace(results->rawbuf[offset++], kDaikin2LeaderSpace, + kDaikin2Tolerance)) return false; + + // Sections + // Keep reading bytes until we either run out of section or state to fill. + for (uint8_t section = 0, pos = 0; section < kDaikin2Sections; + section++) { + pos += sectionSize[section]; + + // Section Header + if (!matchMark(results->rawbuf[offset++], kDaikin2HdrMark, + kDaikin2Tolerance)) return false; + if (!matchSpace(results->rawbuf[offset++], kDaikin2HdrSpace, + kDaikin2Tolerance)) return false; + + // Section Data + for (; offset <= results->rawlen - 16 && i < pos; + i++, dataBitsSoFar += 8, offset += data_result.used) { + // Read in a byte at a time. + data_result = + matchData(&(results->rawbuf[offset]), 8, kDaikin2BitMark, + kDaikin2OneSpace, kDaikin2BitMark, + kDaikin2ZeroSpace, kDaikin2Tolerance, kMarkExcess, false); + if (data_result.success == false) break; // Fail + results->state[i] = (uint8_t)data_result.data; + } + + // Section Footer + if (!matchMark(results->rawbuf[offset++], kDaikin2BitMark, + kDaikin2Tolerance)) return false; + if (section < kDaikin2Sections - 1) { // Inter-section gaps. + if (!matchSpace(results->rawbuf[offset++], kDaikin2Gap, + kDaikin2Tolerance)) return false; + } else { // Last section / End of message gap. + if (offset <= results->rawlen && + !matchAtLeast(results->rawbuf[offset++], kDaikin2Gap, + kDaikin2Tolerance)) return false; + } + } + + // Compliance + if (strict) { + // Re-check we got the correct size/length due to the way we read the data. + if (dataBitsSoFar != kDaikin2Bits) return false; + // Validate the checksum. + if (!IRDaikin2::validChecksum(results->state)) return false; + } + + // Success + results->decode_type = DAIKIN2; + results->bits = dataBitsSoFar; + // No need to record the state as we stored it as we decoded it. + // As we use result->state, we don't record value, address, or command as it + // is a union data type. + return true; +} +#endif // DECODE_DAIKIN2 + +#if SEND_DAIKIN216 +// Send a Daikin 216 bit A/C message. +// +// Args: +// data: An array of kDaikin216StateLength bytes containing the IR command. +// +// Status: Alpha/Untested on a real device. +// +// Supported devices: +// - Daikin ARC433B69 remote. +// +// Ref: +// https://github.com/markszabo/IRremoteESP8266/issues/689 +// https://github.com/danny-source/Arduino_DY_IRDaikin +void IRsend::sendDaikin216(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes < kDaikin216Section1Length) + return; // Not enough bytes to send a partial message. + + for (uint16_t r = 0; r <= repeat; r++) { + // Section #1 + sendGeneric(kDaikin216HdrMark, kDaikin216HdrSpace, kDaikin216BitMark, + kDaikin216OneSpace, kDaikin216BitMark, kDaikin216ZeroSpace, + kDaikin216BitMark, kDaikin216Gap, data, + kDaikin216Section1Length, + kDaikin216Freq, false, 0, kDutyDefault); + // Section #2 + sendGeneric(kDaikin216HdrMark, kDaikin216HdrSpace, kDaikin216BitMark, + kDaikin216OneSpace, kDaikin216BitMark, kDaikin216ZeroSpace, + kDaikin216BitMark, kDaikin216Gap, + data + kDaikin216Section1Length, + nbytes - kDaikin216Section1Length, + kDaikin216Freq, false, 0, kDutyDefault); + } +} +#endif // SEND_DAIKIN216 + +// Class for handling Daikin 216 bit / 27 byte A/C messages. +// +// Code by crankyoldgit. +// +// Supported Remotes: Daikin ARC433B69 remote +// +// Ref: +// https://github.com/markszabo/IRremoteESP8266/issues/689 +// https://github.com/danny-source/Arduino_DY_IRDaikin +IRDaikin216::IRDaikin216(uint16_t pin) : _irsend(pin) { stateReset(); } + +void IRDaikin216::begin() { _irsend.begin(); } + +#if SEND_DAIKIN216 +void IRDaikin216::send(const uint16_t repeat) { + checksum(); + _irsend.sendDaikin216(remote_state, kDaikin216StateLength, repeat); +} +#endif // SEND_DAIKIN216 + +// Verify the checksum is valid for a given state. +// Args: +// state: The array to verify the checksum of. +// length: The size of the state. +// Returns: +// A boolean. +bool IRDaikin216::validChecksum(uint8_t state[], const uint16_t length) { + // Validate the checksum of section #1. + if (length <= kDaikin216Section1Length - 1 || + state[kDaikin216Section1Length - 1] != sumBytes( + state, kDaikin216Section1Length - 1)) + return false; + // Validate the checksum of section #2 (a.k.a. the rest) + if (length <= kDaikin216Section1Length + 1 || + state[length - 1] != sumBytes(state + kDaikin216Section1Length, + length - kDaikin216Section1Length - 1)) + return false; + return true; +} + +// Calculate and set the checksum values for the internal state. +void IRDaikin216::checksum() { + remote_state[kDaikin216Section1Length - 1] = sumBytes( + remote_state, kDaikin216Section1Length - 1); + remote_state[kDaikin216StateLength - 1] = sumBytes( + remote_state + kDaikin216Section1Length, kDaikin216Section2Length - 1); +} + +void IRDaikin216::stateReset() { + for (uint8_t i = 0; i < kDaikin216StateLength; i++) remote_state[i] = 0x00; + remote_state[0] = 0x11; + remote_state[1] = 0xDA; + remote_state[2] = 0x27; + remote_state[3] = 0xF0; + // remote_state[7] is a checksum byte, it will be set by checksum(). + remote_state[8] = 0x11; + remote_state[9] = 0xDA; + remote_state[10] = 0x27; + remote_state[23] = 0xC0; + // remote_state[26] is a checksum byte, it will be set by checksum(). +} + +uint8_t *IRDaikin216::getRaw() { + checksum(); // Ensure correct settings before sending. + return remote_state; +} + +void IRDaikin216::setRaw(const uint8_t new_code[]) { + for (uint8_t i = 0; i < kDaikin216StateLength; i++) + remote_state[i] = new_code[i]; +} + + +void IRDaikin216::on() { + remote_state[kDaikin216BytePower] |= kDaikinBitPower; +} + +void IRDaikin216::off() { + remote_state[kDaikin216BytePower] &= ~kDaikinBitPower; +} + +void IRDaikin216::setPower(const bool state) { + if (state) + on(); + else + off(); +} + +bool IRDaikin216::getPower() { + return remote_state[kDaikin216BytePower] & kDaikinBitPower; +} + +uint8_t IRDaikin216::getMode() { + return (remote_state[kDaikin216ByteMode] & kDaikin216MaskMode) >> 4; +} + +void IRDaikin216::setMode(const uint8_t mode) { + switch (mode) { + case kDaikinAuto: + case kDaikinCool: + case kDaikinHeat: + case kDaikinFan: + case kDaikinDry: + remote_state[kDaikin216ByteMode] &= ~kDaikin216MaskMode; + remote_state[kDaikin216ByteMode] |= (mode << 4); + break; + default: + this->setMode(kDaikinAuto); + } +} + +// Convert a standard A/C mode into its native mode. +uint8_t IRDaikin216::convertMode(const stdAc::opmode_t mode) { + return IRDaikinESP::convertMode(mode); +} + +// Set the temp in deg C +void IRDaikin216::setTemp(const uint8_t temp) { + uint8_t degrees = std::max(temp, kDaikinMinTemp); + degrees = std::min(degrees, kDaikinMaxTemp); + remote_state[kDaikin216ByteTemp] &= ~kDaikin216MaskTemp; + remote_state[kDaikin216ByteTemp] |= (degrees << 1); +} + +uint8_t IRDaikin216::getTemp(void) { + return (remote_state[kDaikin216ByteTemp] & kDaikin216MaskTemp) >> 1; +} + +// Set the speed of the fan, 1-5 or kDaikinFanAuto or kDaikinFanQuiet +void IRDaikin216::setFan(const uint8_t fan) { + // Set the fan speed bits, leave low 4 bits alone + uint8_t fanset; + if (fan == kDaikinFanQuiet || fan == kDaikinFanAuto) + fanset = fan; + else if (fan < kDaikinFanMin || fan > kDaikinFanMax) + fanset = kDaikinFanAuto; + else + fanset = 2 + fan; + remote_state[kDaikin216ByteFan] &= ~kDaikin216MaskFan; + remote_state[kDaikin216ByteFan] |= (fanset << 4); +} + +uint8_t IRDaikin216::getFan() { + uint8_t fan = remote_state[kDaikin216ByteFan] >> 4; + if (fan != kDaikinFanQuiet && fan != kDaikinFanAuto) fan -= 2; + return fan; +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRDaikin216::convertFan(const stdAc::fanspeed_t speed) { + return IRDaikinESP::convertFan(speed); +} + +void IRDaikin216::setSwingVertical(const bool on) { + if (on) + remote_state[kDaikin216ByteSwingV] |= kDaikin216MaskSwingV; + else + remote_state[kDaikin216ByteSwingV] &= ~kDaikin216MaskSwingV; +} + +bool IRDaikin216::getSwingVertical(void) { + return remote_state[kDaikin216ByteSwingV] & kDaikin216MaskSwingV; +} + +void IRDaikin216::setSwingHorizontal(const bool on) { + if (on) + remote_state[kDaikin216ByteSwingH] |= kDaikin216MaskSwingH; + else + remote_state[kDaikin216ByteSwingH] &= ~kDaikin216MaskSwingH; +} + +bool IRDaikin216::getSwingHorizontal(void) { + return remote_state[kDaikin216ByteSwingH] & kDaikin216MaskSwingH; +} + +// This is a horrible hack till someone works out the quiet mode bit. +void IRDaikin216::setQuiet(const bool on) { + if (on) + this->setFan(kDaikinFanQuiet); + else if (this->getFan() == kDaikinFanQuiet) + this->setFan(kDaikinFanAuto); +} + +// This is a horrible hack till someone works out the quiet mode bit. +bool IRDaikin216::getQuiet(void) { + return this->getFan() == kDaikinFanQuiet; +} + +// Convert the internal state into a human readable string. +#ifdef ARDUINO +String IRDaikin216::toString() { + String result = ""; +#else // ARDUINO +std::string IRDaikin216::toString() { + std::string result = ""; +#endif // ARDUINO + result += F("Power: "); + if (this->getPower()) + result += F("On"); + else + result += F("Off"); + result += F(", Mode: "); + result += uint64ToString(this->getMode()); + switch (getMode()) { + case kDaikinAuto: + result += F(" (AUTO)"); + break; + case kDaikinCool: + result += F(" (COOL)"); + break; + case kDaikinHeat: + result += F(" (HEAT)"); + break; + case kDaikinDry: + result += F(" (DRY)"); + break; + case kDaikinFan: + result += F(" (FAN)"); + break; + default: + result += F(" (UNKNOWN)"); + } + result += F(", Temp: "); + result += uint64ToString(this->getTemp()); + result += F("C, Fan: "); + result += uint64ToString(this->getFan()); + switch (this->getFan()) { + case kDaikinFanAuto: + result += F(" (AUTO)"); + break; + case kDaikinFanQuiet: + result += F(" (QUIET)"); + break; + case kDaikinFanMin: + result += F(" (MIN)"); + break; + case kDaikinFanMax: + result += F(" (MAX)"); + break; + } + result += F(", Swing (Horizontal): "); + result += this->getSwingHorizontal() ? F("On") : F("Off"); + result += F(", Swing (Vertical): "); + result += this->getSwingVertical() ? F("On") : F("Off"); + result += F(", Quiet: "); + result += (getQuiet() ? F("On") : F("Off")); + return result; +} + +#if DECODE_DAIKIN216 +// Decode the supplied Daikin 216 bit A/C message. +// Args: +// results: Ptr to the data to decode and where to store the decode result. +// nbits: Nr. of bits to expect in the data portion. (kDaikin216Bits) +// strict: Flag to indicate if we strictly adhere to the specification. +// Returns: +// boolean: True if it can decode it, false if it can't. +// +// Supported devices: +// - Daikin ARC433B69 remote. +// +// Status: BETA / Should be working. +// +// Ref: +// https://github.com/markszabo/IRremoteESP8266/issues/689 +// https://github.com/danny-source/Arduino_DY_IRDaikin +bool IRrecv::decodeDaikin216(decode_results *results, const uint16_t nbits, + const bool strict) { + if (results->rawlen < 2 * (nbits + kHeader + kFooter) - 1) + return false; + + // Compliance + if (strict && nbits != kDaikin216Bits) return false; + + uint16_t offset = kStartOffset; + uint16_t dataBitsSoFar = 0; + uint16_t i = 0; + match_result_t data_result; + uint8_t sectionSize[kDaikin216Sections] = {kDaikin216Section1Length, + kDaikin216Section2Length}; + + // Sections + // Keep reading bytes until we either run out of section or state to fill. + for (uint8_t section = 0, pos = 0; section < kDaikin216Sections; + section++) { + pos += sectionSize[section]; + + // Section Header + if (!matchMark(results->rawbuf[offset++], kDaikin216HdrMark)) return false; + if (!matchSpace(results->rawbuf[offset++], kDaikin2HdrSpace)) return false; + + // Section Data + for (; offset <= results->rawlen - 16 && i < pos; + i++, dataBitsSoFar += 8, offset += data_result.used) { + // Read in a byte at a time. + data_result = + matchData(&(results->rawbuf[offset]), 8, kDaikin216BitMark, + kDaikin216OneSpace, kDaikin216BitMark, + kDaikin216ZeroSpace, kTolerance, kMarkExcess, false); + if (data_result.success == false) break; // Fail + results->state[i] = (uint8_t)data_result.data; + } + + // Section Footer + if (!matchMark(results->rawbuf[offset++], kDaikin216BitMark)) return false; + if (section < kDaikin216Sections - 1) { // Inter-section gaps. + if (!matchSpace(results->rawbuf[offset++], kDaikin216Gap)) return false; + } else { // Last section / End of message gap. + if (offset <= results->rawlen && + !matchAtLeast(results->rawbuf[offset++], kDaikin216Gap)) return false; + } + } + + // Compliance + if (strict) { + // Re-check we got the correct size/length due to the way we read the data. + if (dataBitsSoFar != kDaikin216Bits) return false; + // Validate the checksum. + if (!IRDaikin216::validChecksum(results->state)) return false; + } + + // Success + results->decode_type = decode_type_t::DAIKIN216; + results->bits = dataBitsSoFar; + // No need to record the state as we stored it as we decoded it. + // As we use result->state, we don't record value, address, or command as it + // is a union data type. + return true; +} +#endif // DECODE_DAIKIN216 diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Daikin.h b/lib/IRremoteESP8266-2.6.0/src/ir_Daikin.h new file mode 100644 index 000000000..038e8edd9 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Daikin.h @@ -0,0 +1,444 @@ +// Copyright 2016 sillyfrog +// Copyright 2017 sillyfrog, crankyoldgit +// Copyright 2018-2019 crankyoldgit +#ifndef IR_DAIKIN_H_ +#define IR_DAIKIN_H_ + +#ifndef UNIT_TEST +#include +#else +#include +#endif +#include "IRrecv.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif + +// DDDDD AAA IIIII KK KK IIIII NN NN +// DD DD AAAAA III KK KK III NNN NN +// DD DD AA AA III KKKK III NN N NN +// DD DD AAAAAAA III KK KK III NN NNN +// DDDDDD AA AA IIIII KK KK IIIII NN NN + +/* + Daikin AC map + byte 6= + b4:Comfort + byte 7= checksum of the first part (and last byte before a 29ms pause) + byte 13=Current time, mins past midnight, low bits + byte 14 + b0-b3=Current time, mins past midnight, high bits + byte 15= checksum of the second part (and last byte before a 29ms pause) + byte 21=mode + b7 = 0 + b6+b5+b4 = Mode + Modes: b6+b5+b4 + 011 = Cool + 100 = Heat (temp 23) + 110 = FAN (temp not shown, but 25) + 000 = Fully Automatic (temp 25) + 010 = DRY (temp 0xc0 = 96 degrees c) + b3 = 1 + b2 = OFF timer set + b1 = ON timer set + b0 = Air Conditioner ON + byte 22=temp*2 (Temp should be between 10 - 32) + byte 24=Fan + FAN control + b7+b6+b5+b4 = Fan speed + Fan: b7+b6+b5+b4 + 0×3 = 1 bar + 0×4 = 2 bar + 0×5 = 3 bar + 0×6 = 4 bar + 0×7 = 5 bar + 0xa = Auto + 0xb = Quite + b3+b2+b1+b0 = Swing control up/down + Swing control up/down: + 0000 = Swing up/down off + 1111 = Swing up/down on + byte 25 + Swing control left/right: + 0000 = Swing left/right off + 1111 = Swing left/right on + byte 26=On timer mins past midnight, low bits + byte 27 + b0-b3=On timer mins past midnight, high bits + b4-b7=Off timer mins past midnight, low bits + byte 28=Off timer mins past midnight, high bits + byte 29=Aux -> Powerful (bit 1), Silent (bit 5) + byte 32=Aux2 + b1: Sensor + b2: Econo mode + b7: Intelligent eye on + byte 33=Aux3 + b1: Mold Proof + byte 34= checksum of the third part +*/ + +// Constants +const uint8_t kDaikinAuto = 0b000; +const uint8_t kDaikinDry = 0b010; +const uint8_t kDaikinCool = 0b011; +const uint8_t kDaikinHeat = 0b100; +const uint8_t kDaikinFan = 0b110; +const uint8_t kDaikinMinTemp = 10; // Celsius +const uint8_t kDaikinMaxTemp = 32; // Celsius +const uint8_t kDaikinFanMin = 1; +const uint8_t kDaikinFanMax = 5; +const uint8_t kDaikinFanAuto = 0b1010; +const uint8_t kDaikinFanQuiet = 0b1011; +const uint16_t kDaikinHeaderLength = 5; +const uint8_t kDaikinSections = 3; +const uint8_t kDaikinSection1Length = 8; +const uint8_t kDaikinSection2Length = 8; +const uint8_t kDaikinSection3Length = + kDaikinStateLength - kDaikinSection1Length - kDaikinSection2Length; +const uint8_t kDaikinByteComfort = 6; +const uint8_t kDaikinByteChecksum1 = 7; +const uint8_t kDaikinBitComfort = 0b00010000; +const uint8_t kDaikinByteClockMinsLow = 13; +const uint8_t kDaikinByteClockMinsHigh = 14; +const uint8_t kDaikinByteChecksum2 = 15; +const uint8_t kDaikinBytePower = 21; +const uint8_t kDaikinBitPower = 0b00000001; +const uint8_t kDaikinByteTemp = 22; +const uint8_t kDaikinByteFan = 24; +const uint8_t kDaikinByteSwingH = 25; +const uint8_t kDaikinByteOnTimerMinsLow = 26; +const uint8_t kDaikinByteOnTimerMinsHigh = 27; +const uint8_t kDaikinByteOffTimerMinsLow = kDaikinByteOnTimerMinsHigh; +const uint8_t kDaikinByteOffTimerMinsHigh = 28; +const uint8_t kDaikinBytePowerful = 29; +const uint8_t kDaikinBitPowerful = 0b00000001; +const uint8_t kDaikinByteSilent = kDaikinBytePowerful; +const uint8_t kDaikinBitSilent = 0b00100000; +const uint8_t kDaikinByteSensor = 32; +const uint8_t kDaikinBitSensor = 0b00000010; +const uint8_t kDaikinByteEcono = kDaikinByteSensor; +const uint8_t kDaikinBitEcono = 0b00000100; +const uint8_t kDaikinByteEye = kDaikinByteSensor; +const uint8_t kDaikinBitEye = 0b10000000; +const uint8_t kDaikinByteMold = 33; +const uint8_t kDaikinBitMold = 0b00000010; +const uint8_t kDaikinByteOffTimer = kDaikinBytePower; +const uint8_t kDaikinBitOffTimer = 0b00000100; +const uint8_t kDaikinByteOnTimer = kDaikinByteOffTimer; +const uint8_t kDaikinBitOnTimer = 0b00000010; +const uint8_t kDaikinByteChecksum3 = kDaikinStateLength - 1; +const uint16_t kDaikinUnusedTime = 0x600; +const uint8_t kDaikinBeepQuiet = 1; +const uint8_t kDaikinBeepLoud = 2; +const uint8_t kDaikinBeepOff = 3; +const uint8_t kDaikinLightBright = 1; +const uint8_t kDaikinLightDim = 2; +const uint8_t kDaikinLightOff = 3; +const uint8_t kDaikinCurBit = kDaikinStateLength; +const uint8_t kDaikinCurIndex = kDaikinStateLength + 1; +const uint8_t kDaikinTolerance = 35; +const uint16_t kDaikinMarkExcess = kMarkExcess; +const uint16_t kDaikinHdrMark = 3650; // kDaikinBitMark * 8 +const uint16_t kDaikinHdrSpace = 1623; // kDaikinBitMark * 4 +const uint16_t kDaikinBitMark = 428; +const uint16_t kDaikinZeroSpace = 428; +const uint16_t kDaikinOneSpace = 1280; +const uint16_t kDaikinGap = 29000; +// Note bits in each octet swapped so can be sent as a single value +const uint64_t kDaikinFirstHeader64 = + 0b1101011100000000000000001100010100000000001001111101101000010001; + +// Another variant of the protocol for the Daikin ARC477A1 remote. +const uint16_t kDaikin2Freq = 36700; // Modulation Frequency in Hz. +const uint16_t kDaikin2LeaderMark = 10024; +const uint16_t kDaikin2LeaderSpace = 25180; +const uint16_t kDaikin2Gap = kDaikin2LeaderMark + kDaikin2LeaderSpace; +const uint16_t kDaikin2HdrMark = 3500; +const uint16_t kDaikin2HdrSpace = 1728; +const uint16_t kDaikin2BitMark = 460; +const uint16_t kDaikin2OneSpace = 1270; +const uint16_t kDaikin2ZeroSpace = 420; +const uint16_t kDaikin2Sections = 2; +const uint16_t kDaikin2Section1Length = 20; +const uint16_t kDaikin2Section2Length = 19; +const uint8_t kDaikin2Tolerance = kTolerance + 5; + +const uint8_t kDaikin2BitSleepTimer = 0b00100000; +const uint8_t kDaikin2BitPurify = 0b00010000; +const uint8_t kDaikin2BitEye = 0b00000010; +const uint8_t kDaikin2BitEyeAuto = 0b10000000; +const uint8_t kDaikin2BitMold = 0b00001000; +const uint8_t kDaikin2BitClean = 0b00100000; +const uint8_t kDaikin2BitFreshAir = 0b00000001; +const uint8_t kDaikin2BitFreshAirHigh = 0b10000000; +const uint8_t kDaikin2BitPower = 0b10000000; +const uint8_t kDaikin2LightMask = 0b00110000; +const uint8_t kDaikin2BeepMask = 0b11000000; +const uint8_t kDaikin2SwingVHigh = 0x1; +const uint8_t kDaikin2SwingVLow = 0x6; +const uint8_t kDaikin2SwingVBreeze = 0xC; +const uint8_t kDaikin2SwingVCirculate = 0xD; +const uint8_t kDaikin2SwingVAuto = 0xE; +const uint8_t kDaikin2SwingHAuto = 0xBE; +const uint8_t kDaikin2SwingHSwing = 0xBF; +const uint8_t kDaikin2MinCoolTemp = 18; // Min temp (in C) when in Cool mode. + +// Another variant of the protocol for the Daikin ARC433B69 remote. +const uint16_t kDaikin216Freq = 38000; // Modulation Frequency in Hz. +const uint16_t kDaikin216HdrMark = 3400; +const uint16_t kDaikin216HdrSpace = 1800; +const uint16_t kDaikin216BitMark = 380; +const uint16_t kDaikin216OneSpace = 1350; +const uint16_t kDaikin216ZeroSpace = 480; +const uint16_t kDaikin216Gap = 29650; +const uint16_t kDaikin216Sections = 2; +const uint16_t kDaikin216Section1Length = 8; +const uint16_t kDaikin216Section2Length = kDaikin216StateLength - + kDaikin216Section1Length; +const uint8_t kDaikin216BytePower = 13; +const uint8_t kDaikin216ByteMode = kDaikin216BytePower; +const uint8_t kDaikin216MaskMode = 0b01110000; +const uint8_t kDaikin216ByteTemp = 14; +const uint8_t kDaikin216MaskTemp = 0b01111110; +const uint8_t kDaikin216ByteFan = 16; +const uint8_t kDaikin216MaskFan = 0b11110000; +const uint8_t kDaikin216ByteSwingV = 16; +const uint8_t kDaikin216MaskSwingV = 0b00001111; +const uint8_t kDaikin216ByteSwingH = 17; +const uint8_t kDaikin216MaskSwingH = kDaikin216MaskSwingV; + + +// Legacy defines. +#define DAIKIN_COOL kDaikinCool +#define DAIKIN_HEAT kDaikinHeat +#define DAIKIN_FAN kDaikinFan +#define DAIKIN_AUTO kDaikinAuto +#define DAIKIN_DRY kDaikinDry +#define DAIKIN_MIN_TEMP kDaikinMinTemp +#define DAIKIN_MAX_TEMP kDaikinMaxTemp +#define DAIKIN_FAN_MIN kDaikinFanMin +#define DAIKIN_FAN_MAX kDaikinFanMax +#define DAIKIN_FAN_AUTO kDaikinFanAuto +#define DAIKIN_FAN_QUIET kDaikinFanQuiet + +class IRDaikinESP { + public: + explicit IRDaikinESP(uint16_t pin); + +#if SEND_DAIKIN + void send(const uint16_t repeat = kDaikinDefaultRepeat); +#endif + void begin(void); + void on(void); + void off(void); + void setPower(const bool on); + bool getPower(void); + void setTemp(const uint8_t temp); + uint8_t getTemp(); + void setFan(const uint8_t fan); + uint8_t getFan(void); + void setMode(const uint8_t mode); + uint8_t getMode(void); + void setSwingVertical(const bool on); + bool getSwingVertical(void); + void setSwingHorizontal(const bool on); + bool getSwingHorizontal(void); + bool getQuiet(void); + void setQuiet(const bool on); + bool getPowerful(void); + void setPowerful(const bool on); + void setSensor(const bool on); + bool getSensor(void); + void setEcono(const bool on); + bool getEcono(void); + void setEye(const bool on); + bool getEye(void); + void setMold(const bool on); + bool getMold(void); + void setComfort(const bool on); + bool getComfort(void); + void enableOnTimer(const uint16_t starttime); + void disableOnTimer(void); + uint16_t getOnTime(void); + bool getOnTimerEnabled(); + void enableOffTimer(const uint16_t endtime); + void disableOffTimer(void); + uint16_t getOffTime(void); + bool getOffTimerEnabled(void); + void setCurrentTime(const uint16_t mins_since_midnight); + uint16_t getCurrentTime(void); + uint8_t* getRaw(void); + void setRaw(const uint8_t new_code[], + const uint16_t length = kDaikinStateLength); + static bool validChecksum(uint8_t state[], + const uint16_t length = kDaikinStateLength); + static uint8_t convertMode(const stdAc::opmode_t mode); + static uint8_t convertFan(const stdAc::fanspeed_t speed); +#ifdef ARDUINO + String toString(void); + static String renderTime(const uint16_t timemins); +#else + std::string toString(void); + static std::string renderTime(const uint16_t timemins); +#endif +#ifndef UNIT_TEST + + private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif + // # of bytes per command + uint8_t remote[kDaikinStateLength]; + void stateReset(void); + void checksum(void); +}; + +// Class to emulate a Daikin ARC477A1 remote. +class IRDaikin2 { + public: + explicit IRDaikin2(uint16_t pin); + +#if SEND_DAIKIN2 + void send(const uint16_t repeat = kDaikin2DefaultRepeat); +#endif + void begin(); + void on(); + void off(); + void setPower(const bool state); + bool getPower(); + void setTemp(const uint8_t temp); + uint8_t getTemp(); + void setFan(const uint8_t fan); + uint8_t getFan(); + uint8_t getMode(); + void setMode(const uint8_t mode); + void setSwingVertical(const uint8_t position); + uint8_t getSwingVertical(); + void setSwingHorizontal(const uint8_t position); + uint8_t getSwingHorizontal(); + bool getQuiet(); + void setQuiet(const bool on); + bool getPowerful(); + void setPowerful(const bool on); + void setSensor(const bool on); + bool getSensor(); + void setEcono(const bool on); + bool getEcono(); + void setEye(const bool on); + bool getEye(); + void setEyeAuto(const bool on); + bool getEyeAuto(); + void setPurify(const bool on); + bool getPurify(); + void setMold(const bool on); + bool getMold(); + void enableOnTimer(const uint16_t starttime); + void disableOnTimer(); + uint16_t getOnTime(); + bool getOnTimerEnabled(); + void enableSleepTimer(const uint16_t sleeptime); + void disableSleepTimer(); + uint16_t getSleepTime(); + bool getSleepTimerEnabled(); + void enableOffTimer(const uint16_t endtime); + void disableOffTimer(); + uint16_t getOffTime(); + bool getOffTimerEnabled(); + void setCurrentTime(const uint16_t time); + uint16_t getCurrentTime(); + void setBeep(const uint8_t beep); + uint8_t getBeep(); + void setLight(const uint8_t light); + uint8_t getLight(); + void setClean(const bool on); + bool getClean(); + void setFreshAir(const bool on); + bool getFreshAir(); + void setFreshAirHigh(const bool on); + bool getFreshAirHigh(); + uint8_t* getRaw(); + void setRaw(const uint8_t new_code[]); + uint32_t getCommand(); + void setCommand(uint32_t value); + static bool validChecksum(uint8_t state[], + const uint16_t length = kDaikin2StateLength); + static uint8_t convertMode(const stdAc::opmode_t mode); + static uint8_t convertFan(const stdAc::fanspeed_t speed); + uint8_t convertSwingV(const stdAc::swingv_t position); +#ifdef ARDUINO + String toString(); + static String renderTime(uint16_t timemins); +#else + std::string toString(); + static std::string renderTime(uint16_t timemins); +#endif +#ifndef UNIT_TEST + + private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif + // # of bytes per command + uint8_t remote_state[kDaikin2StateLength]; + void stateReset(); + void checksum(); + void clearOnTimerFlag(); + void clearSleepTimerFlag(); +}; + +// Class to emulate a Daikin ARC433B69 remote. +class IRDaikin216 { + public: + explicit IRDaikin216(uint16_t pin); + +#if SEND_DAIKIN216 + void send(const uint16_t repeat = kDaikin216DefaultRepeat); +#endif + void begin(); + uint8_t* getRaw(); + void setRaw(const uint8_t new_code[]); + static bool validChecksum(uint8_t state[], + const uint16_t length = kDaikin216StateLength); + void on(void); + void off(void); + void setPower(const bool on); + bool getPower(void); + void setTemp(const uint8_t temp); + uint8_t getTemp(); + void setMode(const uint8_t mode); + uint8_t getMode(void); + static uint8_t convertMode(const stdAc::opmode_t mode); + void setFan(const uint8_t fan); + uint8_t getFan(void); + static uint8_t convertFan(const stdAc::fanspeed_t speed); + void setSwingVertical(const bool on); + bool getSwingVertical(void); + void setSwingHorizontal(const bool on); + bool getSwingHorizontal(void); + void setQuiet(const bool on); + bool getQuiet(void); +#ifdef ARDUINO + String toString(void); + static String renderTime(const uint16_t timemins); +#else + std::string toString(void); + static std::string renderTime(const uint16_t timemins); +#endif +#ifndef UNIT_TEST + + private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif + // # of bytes per command + uint8_t remote_state[kDaikin216StateLength]; + void stateReset(); + void checksum(); +}; + +#endif // IR_DAIKIN_H_ diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Denon.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Denon.cpp similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Denon.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_Denon.cpp diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Dish.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Dish.cpp similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Dish.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_Dish.cpp diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Electra.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Electra.cpp similarity index 87% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Electra.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_Electra.cpp index df69be748..0700ab698 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Electra.cpp +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Electra.cpp @@ -1,4 +1,4 @@ -// Copyright 2018 David Conran +// Copyright 2018, 2019 David Conran #include "IRrecv.h" #include "IRsend.h" @@ -17,6 +17,7 @@ // Ref: // https://github.com/markszabo/IRremoteESP8266/issues/527 +// https://github.com/markszabo/IRremoteESP8266/issues/642 // Constants const uint16_t kElectraAcHdrMark = 9166; @@ -24,7 +25,7 @@ const uint16_t kElectraAcBitMark = 646; const uint16_t kElectraAcHdrSpace = 4470; const uint16_t kElectraAcOneSpace = 1647; const uint16_t kElectraAcZeroSpace = 547; -const uint32_t kElectraAcMessageGap = 100000; // Completely made-up guess. +const uint32_t kElectraAcMessageGap = kDefaultMessageGap; // Just a guess. #if SEND_ELECTRA_AC // Send a Electra message @@ -42,7 +43,8 @@ void IRsend::sendElectraAC(uint8_t data[], uint16_t nbytes, uint16_t repeat) { kElectraAcOneSpace, kElectraAcBitMark, kElectraAcZeroSpace, kElectraAcBitMark, kElectraAcMessageGap, data, nbytes, 38000, // Complete guess of the modulation frequency. - true, 0, 50); + false, // Send data in LSB order per byte + 0, 50); } #endif @@ -56,7 +58,7 @@ void IRsend::sendElectraAC(uint8_t data[], uint16_t nbytes, uint16_t repeat) { // Returns: // boolean: True if it can decode it, false if it can't. // -// Status: Alpha / Needs testing against a real device. +// Status: Beta / Probably works. // bool IRrecv::decodeElectraAC(decode_results *results, uint16_t nbits, bool strict) { @@ -68,8 +70,6 @@ bool IRrecv::decodeElectraAC(decode_results *results, uint16_t nbits, return false; // Not strictly a ELECTRA_AC message. } - // The protocol sends the data normal + inverted, alternating on - // each byte. Hence twice the number of expected data bits. if (results->rawlen < 2 * nbits + kHeader + kFooter - 1) return false; // Can't possibly be a valid ELECTRA_AC message. @@ -87,7 +87,7 @@ bool IRrecv::decodeElectraAC(decode_results *results, uint16_t nbits, i++, dataBitsSoFar += 8, offset += data_result.used) { data_result = matchData(&(results->rawbuf[offset]), 8, kElectraAcBitMark, kElectraAcOneSpace, kElectraAcBitMark, - kElectraAcZeroSpace, kTolerance, 0, true); + kElectraAcZeroSpace, kTolerance, 0, false); if (data_result.success == false) return false; // Fail results->state[i] = data_result.data; } @@ -99,7 +99,12 @@ bool IRrecv::decodeElectraAC(decode_results *results, uint16_t nbits, return false; // Compliance - if (strict && dataBitsSoFar != nbits) return false; + if (strict) { + if (dataBitsSoFar != nbits) return false; + // Verify the checksum. + if (sumBytes(results->state, (dataBitsSoFar / 8) - 1) != + results->state[(dataBitsSoFar / 8) - 1]) return false; + } // Success results->decode_type = ELECTRA_AC; diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Fujitsu.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Fujitsu.cpp similarity index 88% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Fujitsu.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_Fujitsu.cpp index 7c1b99834..de1b97e87 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Fujitsu.cpp +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Fujitsu.cpp @@ -84,9 +84,9 @@ void IRFujitsuAC::begin() { _irsend.begin(); } #if SEND_FUJITSU_AC // Send the current desired state to the IR LED. -void IRFujitsuAC::send() { +void IRFujitsuAC::send(const uint16_t repeat) { getRaw(); - _irsend.sendFujitsuAC(remote_state, getStateLength()); + _irsend.sendFujitsuAC(remote_state, getStateLength(), repeat); } #endif // SEND_FUJITSU_AC @@ -281,6 +281,7 @@ void IRFujitsuAC::setMode(uint8_t mode) { } uint8_t IRFujitsuAC::getMode() { return _mode; } + // Set the requested swing operation mode of the a/c unit. void IRFujitsuAC::setSwing(uint8_t swingMode) { switch (_model) { @@ -319,6 +320,39 @@ bool IRFujitsuAC::validChecksum(uint8_t state[], uint16_t length) { return checksum == (uint8_t)(sum_complement - sum); // Does it match? } +// Convert a standard A/C mode into its native mode. +uint8_t IRFujitsuAC::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kFujitsuAcModeCool; + case stdAc::opmode_t::kHeat: + return kFujitsuAcModeHeat; + case stdAc::opmode_t::kDry: + return kFujitsuAcModeDry; + case stdAc::opmode_t::kFan: + return kFujitsuAcModeFan; + default: + return kFujitsuAcModeAuto; + } +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRFujitsuAC::convertFan(stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + return kFujitsuAcFanQuiet; + case stdAc::fanspeed_t::kLow: + return kFujitsuAcFanLow; + case stdAc::fanspeed_t::kMedium: + return kFujitsuAcFanMed; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: + return kFujitsuAcFanHigh; + default: + return kFujitsuAcFanAuto; + } +} + // Convert the internal state into a human readable string. #ifdef ARDUINO String IRFujitsuAC::toString() { @@ -327,77 +361,80 @@ String IRFujitsuAC::toString() { std::string IRFujitsuAC::toString() { std::string result = ""; #endif // ARDUINO - result += "Power: "; + result += F("Power: "); if (getPower()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Mode: " + uint64ToString(getMode()); + result += F("Off"); + result += F(", Mode: "); + result += uint64ToString(getMode()); switch (getMode()) { case kFujitsuAcModeAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kFujitsuAcModeCool: - result += " (COOL)"; + result += F(" (COOL)"); break; case kFujitsuAcModeHeat: - result += " (HEAT)"; + result += F(" (HEAT)"); break; case kFujitsuAcModeDry: - result += " (DRY)"; + result += F(" (DRY)"); break; case kFujitsuAcModeFan: - result += " (FAN)"; + result += F(" (FAN)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); } - result += ", Temp: " + uint64ToString(getTemp()) + "C"; - result += ", Fan: " + uint64ToString(getFanSpeed()); + result += F(", Temp: "); + result += uint64ToString(getTemp()); + result += F("C, Fan: "); + result += uint64ToString(getFanSpeed()); switch (getFanSpeed()) { case kFujitsuAcFanAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kFujitsuAcFanHigh: - result += " (HIGH)"; + result += F(" (HIGH)"); break; case kFujitsuAcFanMed: - result += " (MED)"; + result += F(" (MED)"); break; case kFujitsuAcFanLow: - result += " (LOW)"; + result += F(" (LOW)"); break; case kFujitsuAcFanQuiet: - result += " (QUIET)"; + result += F(" (QUIET)"); break; } - result += ", Swing: "; + result += F(", Swing: "); switch (getSwing()) { case kFujitsuAcSwingOff: - result += "Off"; + result += F("Off"); break; case kFujitsuAcSwingVert: - result += "Vert"; + result += F("Vert"); break; case kFujitsuAcSwingHoriz: - result += "Horiz"; + result += F("Horiz"); break; case kFujitsuAcSwingBoth: - result += "Vert + Horiz"; + result += F("Vert + Horiz"); break; default: - result += "UNKNOWN"; + result += F("UNKNOWN"); } - result += ", Command: "; + result += F(", Command: "); switch (getCmd()) { case kFujitsuAcCmdStepHoriz: - result += "Step vane horizontally"; + result += F("Step vane horizontally"); break; case kFujitsuAcCmdStepVert: - result += "Step vane vertically"; + result += F("Step vane vertically"); break; default: - result += "N/A"; + result += F("N/A"); } return result; } diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Fujitsu.h b/lib/IRremoteESP8266-2.6.0/src/ir_Fujitsu.h similarity index 93% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Fujitsu.h rename to lib/IRremoteESP8266-2.6.0/src/ir_Fujitsu.h index bba634be6..78a4f8951 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Fujitsu.h +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Fujitsu.h @@ -13,6 +13,9 @@ #include "IRrecv.h" #include "IRremoteESP8266.h" #include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif // FUJITSU A/C support added by Jonny Graham @@ -78,7 +81,7 @@ class IRFujitsuAC { void setModel(fujitsu_ac_remote_model_t model); void stateReset(); #if SEND_FUJITSU_AC - void send(); + void send(const uint16_t repeat = kFujitsuAcMinRepeat); #endif // SEND_FUJITSU_AC void begin(); void off(); @@ -99,15 +102,21 @@ class IRFujitsuAC { uint8_t getStateLength(); static bool validChecksum(uint8_t* state, uint16_t length); bool getPower(); + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(stdAc::fanspeed_t speed); #ifdef ARDUINO String toString(); #else std::string toString(); #endif +#ifndef UNIT_TEST private: - uint8_t remote_state[kFujitsuAcStateLength]; IRsend _irsend; +#else + IRsendTest _irsend; +#endif + uint8_t remote_state[kFujitsuAcStateLength]; uint8_t _temp; uint8_t _fanSpeed; uint8_t _mode; diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_GICable.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_GICable.cpp similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_GICable.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_GICable.cpp diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_GlobalCache.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_GlobalCache.cpp similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_GlobalCache.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_GlobalCache.cpp diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Gree.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Gree.cpp similarity index 82% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Gree.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_Gree.cpp index df8afada6..756f008d4 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Gree.cpp +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Gree.cpp @@ -27,7 +27,7 @@ // Constants // Ref: https://github.com/ToniA/arduino-heatpumpir/blob/master/GreeHeatpumpIR.h const uint16_t kGreeHdrMark = 9000; -const uint16_t kGreeHdrSpace = 4000; +const uint16_t kGreeHdrSpace = 4500; // See #684 and real example in unit tests const uint16_t kGreeBitMark = 620; const uint16_t kGreeOneSpace = 1600; const uint16_t kGreeZeroSpace = 540; @@ -59,7 +59,7 @@ void IRsend::sendGree(unsigned char data[], uint16_t nbytes, uint16_t repeat) { // Footer #1 sendGeneric(0, 0, // No Header kGreeBitMark, kGreeOneSpace, kGreeBitMark, kGreeZeroSpace, - kGreeBitMark, kGreeMsgSpace, 0b010, 3, 38, true, 0, false); + kGreeBitMark, kGreeMsgSpace, 0b010, 3, 38, false, 0, 50); // Block #2 sendGeneric(0, 0, // No Header for Block #2 @@ -129,9 +129,9 @@ void IRGreeAC::fixup() { void IRGreeAC::begin() { _irsend.begin(); } #if SEND_GREE -void IRGreeAC::send() { +void IRGreeAC::send(const uint16_t repeat) { fixup(); // Ensure correct settings before sending. - _irsend.sendGree(remote_state); + _irsend.sendGree(remote_state, kGreeStateLength, repeat); } #endif // SEND_GREE @@ -305,6 +305,57 @@ uint8_t IRGreeAC::getSwingVerticalPosition() { return remote_state[4] & kGreeSwingPosMask; } + +// Convert a standard A/C mode into its native mode. +uint8_t IRGreeAC::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kGreeCool; + case stdAc::opmode_t::kHeat: + return kGreeHeat; + case stdAc::opmode_t::kDry: + return kGreeDry; + case stdAc::opmode_t::kFan: + return kGreeFan; + default: + return kGreeAuto; + } +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRGreeAC::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + return kGreeFanMin; + case stdAc::fanspeed_t::kLow: + case stdAc::fanspeed_t::kMedium: + return kGreeFanMax - 1; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: + return kGreeFanMax; + default: + return kGreeFanAuto; + } +} + +// Convert a standard A/C Vertical Swing into its native version. +uint8_t IRGreeAC::convertSwingV(const stdAc::swingv_t swingv) { + switch (swingv) { + case stdAc::swingv_t::kHighest: + return kGreeSwingUp; + case stdAc::swingv_t::kHigh: + return kGreeSwingMiddleUp; + case stdAc::swingv_t::kMiddle: + return kGreeSwingMiddle; + case stdAc::swingv_t::kLow: + return kGreeSwingMiddleDown; + case stdAc::swingv_t::kLowest: + return kGreeSwingDown; + default: + return kGreeSwingAuto; + } +} + // Convert the internal state into a human readable string. #ifdef ARDUINO String IRGreeAC::toString() { @@ -313,74 +364,77 @@ String IRGreeAC::toString() { std::string IRGreeAC::toString() { std::string result = ""; #endif // ARDUINO - result += "Power: "; + result += F("Power: "); if (getPower()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Mode: " + uint64ToString(getMode()); + result += F("Off"); + result += F(", Mode: "); + result += uint64ToString(getMode()); switch (getMode()) { case kGreeAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kGreeCool: - result += " (COOL)"; + result += F(" (COOL)"); break; case kGreeHeat: - result += " (HEAT)"; + result += F(" (HEAT)"); break; case kGreeDry: - result += " (DRY)"; + result += F(" (DRY)"); break; case kGreeFan: - result += " (FAN)"; + result += F(" (FAN)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); } - result += ", Temp: " + uint64ToString(getTemp()) + "C"; - result += ", Fan: " + uint64ToString(getFan()); + result += F(", Temp: "); + result += uint64ToString(getTemp()); + result += F("C, Fan: "); + result += uint64ToString(getFan()); switch (getFan()) { case 0: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kGreeFanMax: - result += " (MAX)"; + result += F(" (MAX)"); break; } - result += ", Turbo: "; + result += F(", Turbo: "); if (getTurbo()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", XFan: "; + result += F("Off"); + result += F(", XFan: "); if (getXFan()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Light: "; + result += F("Off"); + result += F(", Light: "); if (getLight()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Sleep: "; + result += F("Off"); + result += F(", Sleep: "); if (getSleep()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Swing Vertical Mode: "; + result += F("Off"); + result += F(", Swing Vertical Mode: "); if (getSwingVerticalAuto()) - result += "Auto"; + result += F("Auto"); else - result += "Manual"; - result += - ", Swing Vertical Pos: " + uint64ToString(getSwingVerticalPosition()); + result += F("Manual"); + result += F(", Swing Vertical Pos: "); + result += uint64ToString(getSwingVerticalPosition()); switch (getSwingVerticalPosition()) { case kGreeSwingLastPos: - result += " (Last Pos)"; + result += F(" (Last Pos)"); break; case kGreeSwingAuto: - result += " (Auto)"; + result += F(" (Auto)"); break; } return result; diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Gree.h b/lib/IRremoteESP8266-2.6.0/src/ir_Gree.h similarity index 89% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Gree.h rename to lib/IRremoteESP8266-2.6.0/src/ir_Gree.h index 73f69eb31..c3c5916dc 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Gree.h +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Gree.h @@ -14,6 +14,9 @@ #endif #include "IRremoteESP8266.h" #include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif // GGGG RRRRRR EEEEEEE EEEEEEE // GG GG RR RR EE EE @@ -44,6 +47,8 @@ const uint8_t kGreeSwingPosMask = 0b00001111; const uint8_t kGreeMinTemp = 16; // Celsius const uint8_t kGreeMaxTemp = 30; // Celsius +const uint8_t kGreeFanAuto = 0; +const uint8_t kGreeFanMin = 1; const uint8_t kGreeFanMax = 3; const uint8_t kGreeSwingLastPos = 0b00000000; @@ -84,7 +89,7 @@ class IRGreeAC { void stateReset(); #if SEND_GREE - void send(); + void send(const uint16_t repeat = kGreeDefaultRepeat); #endif // SEND_GREE void begin(); void on(); @@ -108,7 +113,9 @@ class IRGreeAC { void setSwingVertical(const bool automatic, const uint8_t position); bool getSwingVerticalAuto(); uint8_t getSwingVerticalPosition(); - + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); + uint8_t convertSwingV(const stdAc::swingv_t swingv); uint8_t* getRaw(); void setRaw(uint8_t new_code[]); static bool validChecksum(const uint8_t state[], @@ -118,13 +125,17 @@ class IRGreeAC { #else std::string toString(); #endif +#ifndef UNIT_TEST private: + IRsend _irsend; +#else // UNIT_TEST + IRsendTest _irsend; +#endif // UNIT_TEST // The state of the IR remote in IR code form. uint8_t remote_state[kGreeStateLength]; void checksum(const uint16_t length = kGreeStateLength); void fixup(); - IRsend _irsend; }; #endif // IR_GREE_H_ diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Haier.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Haier.cpp similarity index 75% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Haier.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_Haier.cpp index 2c47e4eac..f76bb3447 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Haier.cpp +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Haier.cpp @@ -45,7 +45,7 @@ const uint32_t kHaierAcMinGap = 150000; // Completely made up value. // nbytes: Nr. of bytes of data in the array. (>=kHaierACStateLength) // repeat: Nr. of times the message is to be repeated. (Default = 0). // -// Status: Beta / Probably working. +// Status: STABLE / Known to be working. // void IRsend::sendHaierAC(unsigned char data[], uint16_t nbytes, uint16_t repeat) { @@ -86,9 +86,9 @@ IRHaierAC::IRHaierAC(uint16_t pin) : _irsend(pin) { stateReset(); } void IRHaierAC::begin() { _irsend.begin(); } #if SEND_HAIER_AC -void IRHaierAC::send() { +void IRHaierAC::send(const uint16_t repeat) { checksum(); - _irsend.sendHaierAC(remote_state); + _irsend.sendHaierAC(remote_state, kHaierACStateLength, repeat); } #endif // SEND_HAIER_AC @@ -104,7 +104,10 @@ bool IRHaierAC::validChecksum(uint8_t state[], const uint16_t length) { void IRHaierAC::stateReset() { for (uint8_t i = 1; i < kHaierACStateLength; i++) remote_state[i] = 0x0; remote_state[0] = kHaierAcPrefix; - remote_state[2] = 0b00100000; + remote_state[2] = 0x20; + remote_state[4] = 0x0C; + remote_state[5] = 0xC0; + remote_state[6] = 0x20; setTemp(kHaierAcDefTemp); setFan(kHaierAcFanAuto); @@ -304,14 +307,63 @@ std::string IRHaierAC::timeToString(const uint16_t nr_mins) { std::string result = ""; #endif // ARDUINO - if (nr_mins / 24 < 10) result += "0"; // Zero pad. + if (nr_mins / 24 < 10) result += '0'; // Zero pad. result += uint64ToString(nr_mins / 60); - result += ":"; - if (nr_mins % 60 < 10) result += "0"; // Zero pad. + result += ':'; + if (nr_mins % 60 < 10) result += '0'; // Zero pad. result += uint64ToString(nr_mins % 60); return result; } +// Convert a standard A/C mode into its native mode. +uint8_t IRHaierAC::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kHaierAcCool; + case stdAc::opmode_t::kHeat: + return kHaierAcHeat; + case stdAc::opmode_t::kDry: + return kHaierAcDry; + case stdAc::opmode_t::kFan: + return kHaierAcFan; + default: + return kHaierAcAuto; + } +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRHaierAC::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: + return kHaierAcFanLow; + case stdAc::fanspeed_t::kMedium: + return kHaierAcFanMed; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: + return kHaierAcFanHigh; + default: + return kHaierAcFanAuto; + } +} + +// Convert a standard A/C vertical swing into its native setting. +uint8_t IRHaierAC::convertSwingV(const stdAc::swingv_t position) { + switch (position) { + case stdAc::swingv_t::kHighest: + case stdAc::swingv_t::kHigh: + case stdAc::swingv_t::kMiddle: + return kHaierAcSwingUp; + case stdAc::swingv_t::kLow: + case stdAc::swingv_t::kLowest: + return kHaierAcSwingDown; + case stdAc::swingv_t::kOff: + return kHaierAcSwingOff; + default: + return kHaierAcSwingChg; + } +} + // Convert the internal state into a human readable string. #ifdef ARDUINO String IRHaierAC::toString() { @@ -321,114 +373,122 @@ std::string IRHaierAC::toString() { std::string result = ""; #endif // ARDUINO uint8_t cmd = getCommand(); - result += "Command: " + uint64ToString(cmd) + " ("; + result += F("Command: "); + result += uint64ToString(cmd); + result += F(" ("); switch (cmd) { case kHaierAcCmdOff: - result += "Off"; + result += F("Off"); break; case kHaierAcCmdOn: - result += "On"; + result += F("On"); break; case kHaierAcCmdMode: - result += "Mode"; + result += F("Mode"); break; case kHaierAcCmdFan: - result += "Fan"; + result += F("Fan"); break; case kHaierAcCmdTempUp: - result += "Temp Up"; + result += F("Temp Up"); break; case kHaierAcCmdTempDown: - result += "Temp Down"; + result += F("Temp Down"); break; case kHaierAcCmdSleep: - result += "Sleep"; + result += F("Sleep"); break; case kHaierAcCmdTimerSet: - result += "Timer Set"; + result += F("Timer Set"); break; case kHaierAcCmdTimerCancel: - result += "Timer Cancel"; + result += F("Timer Cancel"); break; case kHaierAcCmdHealth: - result += "Health"; + result += F("Health"); break; case kHaierAcCmdSwing: - result += "Swing"; + result += F("Swing"); break; default: - result += "Unknown"; + result += F("Unknown"); } - result += ")"; - result += ", Mode: " + uint64ToString(getMode()); + result += ')'; + result += F(", Mode: "); + result += uint64ToString(getMode()); switch (getMode()) { case kHaierAcAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kHaierAcCool: - result += " (COOL)"; + result += F(" (COOL)"); break; case kHaierAcHeat: - result += " (HEAT)"; + result += F(" (HEAT)"); break; case kHaierAcDry: - result += " (DRY)"; + result += F(" (DRY)"); break; case kHaierAcFan: - result += " (FAN)"; + result += F(" (FAN)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); } - result += ", Temp: " + uint64ToString(getTemp()) + "C"; - result += ", Fan: " + uint64ToString(getFan()); + result += F(", Temp: "); + result += uint64ToString(getTemp()); + result += F("C, Fan: "); + result += uint64ToString(getFan()); switch (getFan()) { case kHaierAcFanAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kHaierAcFanHigh: - result += " (MAX)"; + result += F(" (MAX)"); break; } - result += ", Swing: " + uint64ToString(getSwing()) + " ("; + result += F(", Swing: "); + result += uint64ToString(getSwing()); + result += F(" ("); switch (getSwing()) { case kHaierAcSwingOff: - result += "Off"; + result += F("Off"); break; case kHaierAcSwingUp: - result += "Up"; + result += F("Up"); break; case kHaierAcSwingDown: - result += "Down"; + result += F("Down"); break; case kHaierAcSwingChg: - result += "Chg"; + result += F("Chg"); break; default: - result += "Unknown"; + result += F("Unknown"); } - result += ")"; - result += ", Sleep: "; + result += ')'; + result += F(", Sleep: "); if (getSleep()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Health: "; + result += F("Off"); + result += F(", Health: "); if (getHealth()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Current Time: " + timeToString(getCurrTime()); - result += ", On Timer: "; + result += F("Off"); + result += F(", Current Time: "); + result += timeToString(getCurrTime()); + result += F(", On Timer: "); if (getOnTimer() >= 0) result += timeToString(getOnTimer()); else - result += "Off"; - result += ", Off Timer: "; + result += F("Off"); + result += F(", Off Timer: "); if (getOffTimer() >= 0) result += timeToString(getOffTimer()); else - result += "Off"; + result += F("Off"); return result; } @@ -440,9 +500,9 @@ IRHaierACYRW02::IRHaierACYRW02(uint16_t pin) : _irsend(pin) { stateReset(); } void IRHaierACYRW02::begin() { _irsend.begin(); } #if SEND_HAIER_AC_YRW02 -void IRHaierACYRW02::send() { +void IRHaierACYRW02::send(const uint16_t repeat) { checksum(); - _irsend.sendHaierACYRW02(remote_state); + _irsend.sendHaierACYRW02(remote_state, kHaierACYRW02StateLength, repeat); } #endif // SEND_HAIER_AC_YRW02 @@ -628,6 +688,57 @@ void IRHaierACYRW02::setSwing(uint8_t state) { remote_state[1] |= newstate; } +// Convert a standard A/C mode into its native mode. +uint8_t IRHaierACYRW02::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kHaierAcYrw02Cool; + case stdAc::opmode_t::kHeat: + return kHaierAcYrw02Heat; + case stdAc::opmode_t::kDry: + return kHaierAcYrw02Dry; + case stdAc::opmode_t::kFan: + return kHaierAcYrw02Fan; + default: + return kHaierAcYrw02Auto; + } +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRHaierACYRW02::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: + return kHaierAcYrw02FanLow; + case stdAc::fanspeed_t::kMedium: + return kHaierAcYrw02FanMed; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: + return kHaierAcYrw02FanHigh; + default: + return kHaierAcYrw02FanAuto; + } +} + +// Convert a standard A/C vertical swing into its native setting. +uint8_t IRHaierACYRW02::convertSwingV(const stdAc::swingv_t position) { + switch (position) { + case stdAc::swingv_t::kHighest: + case stdAc::swingv_t::kHigh: + return kHaierAcYrw02SwingTop; + case stdAc::swingv_t::kMiddle: + return kHaierAcYrw02SwingMiddle; + case stdAc::swingv_t::kLow: + return kHaierAcYrw02SwingDown; + case stdAc::swingv_t::kLowest: + return kHaierAcYrw02SwingBottom; + case stdAc::swingv_t::kOff: + return kHaierAcYrw02SwingOff; + default: + return kHaierAcYrw02SwingAuto; + } +} + // Convert the internal state into a human readable string. #ifdef ARDUINO String IRHaierACYRW02::toString() { @@ -636,132 +747,141 @@ String IRHaierACYRW02::toString() { std::string IRHaierACYRW02::toString() { std::string result = ""; #endif // ARDUINO - result += "Power: "; + result += F("Power: "); if (getPower()) - result += "On"; + result += F("On"); else - result += "Off"; + result += F("Off"); uint8_t cmd = getButton(); - result += ", Button: " + uint64ToString(cmd) + " ("; + result += F(", Button: "); + result += uint64ToString(cmd); + result += F(" ("); switch (cmd) { case kHaierAcYrw02ButtonPower: - result += "Power"; + result += F("Power"); break; case kHaierAcYrw02ButtonMode: - result += "Mode"; + result += F("Mode"); break; case kHaierAcYrw02ButtonFan: - result += "Fan"; + result += F("Fan"); break; case kHaierAcYrw02ButtonTempUp: - result += "Temp Up"; + result += F("Temp Up"); break; case kHaierAcYrw02ButtonTempDown: - result += "Temp Down"; + result += F("Temp Down"); break; case kHaierAcYrw02ButtonSleep: - result += "Sleep"; + result += F("Sleep"); break; case kHaierAcYrw02ButtonHealth: result += "Health"; break; case kHaierAcYrw02ButtonSwing: - result += "Swing"; + result += F("Swing"); break; case kHaierAcYrw02ButtonTurbo: - result += "Turbo"; + result += F("Turbo"); break; default: - result += "Unknown"; + result += F("Unknown"); } - result += ")"; - result += ", Mode: " + uint64ToString(getMode()); + result += ')'; + result += F(", Mode: "); + result += uint64ToString(getMode()); switch (getMode()) { case kHaierAcYrw02Auto: - result += " (Auto)"; + result += F(" (Auto)"); break; case kHaierAcYrw02Cool: - result += " (Cool)"; + result += F(" (Cool)"); break; case kHaierAcYrw02Heat: - result += " (Heat)"; + result += F(" (Heat)"); break; case kHaierAcYrw02Dry: - result += " (Dry)"; + result += F(" (Dry)"); break; case kHaierAcYrw02Fan: - result += " (Fan)"; + result += F(" (Fan)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); } - result += ", Temp: " + uint64ToString(getTemp()) + "C"; - result += ", Fan: " + uint64ToString(getFan()); + result += F(", Temp: "); + result += uint64ToString(getTemp()); + result += F("C, Fan: "); + result += uint64ToString(getFan()); switch (getFan()) { case kHaierAcYrw02FanAuto: - result += " (Auto)"; + result += F(" (Auto)"); break; case kHaierAcYrw02FanHigh: - result += " (High)"; + result += F(" (High)"); break; case kHaierAcYrw02FanLow: - result += " (Low)"; + result += F(" (Low)"); break; case kHaierAcYrw02FanMed: - result += " (Med)"; + result += F(" (Med)"); break; default: - result += " (Unknown)"; + result += F(" (Unknown)"); } - result += ", Turbo: " + uint64ToString(getTurbo()) + " ("; + result += F(", Turbo: "); + result += uint64ToString(getTurbo()); + result += F(" ("); switch (getTurbo()) { case kHaierAcYrw02TurboOff: - result += "Off"; + result += F("Off"); break; case kHaierAcYrw02TurboLow: - result += "Low"; + result += F("Low"); break; case kHaierAcYrw02TurboHigh: - result += "High"; + result += F("High"); break; default: - result += "Unknown"; + result += F("Unknown"); } - result += ")"; - result += ", Swing: " + uint64ToString(getSwing()) + " ("; + result += ')'; + result += F(", Swing: "); + result += uint64ToString(getSwing()); + result += F(" ("); switch (getSwing()) { case kHaierAcYrw02SwingOff: - result += "Off"; + result += F("Off"); break; case kHaierAcYrw02SwingAuto: - result += "Auto"; + result += F("Auto"); break; case kHaierAcYrw02SwingBottom: - result += "Bottom"; + result += F("Bottom"); break; case kHaierAcYrw02SwingDown: - result += "Down"; + result += F("Down"); break; case kHaierAcYrw02SwingTop: - result += "Top"; + result += F("Top"); break; case kHaierAcYrw02SwingMiddle: - result += "Middle"; + result += F("Middle"); break; default: - result += "Unknown"; + result += F("Unknown"); } - result += ")"; - result += ", Sleep: "; + result += ')'; + result += F(", Sleep: "); if (getSleep()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Health: "; + result += F("Off"); + result += F(", Health: "); if (getHealth()) - result += "On"; + result += F("On"); else - result += "Off"; + result += F("Off"); return result; } @@ -777,7 +897,7 @@ std::string IRHaierACYRW02::toString() { // Returns: // boolean: True if it can decode it, false if it can't. // -// Status: BETA / Appears to be working. +// Status: STABLE / Known to be working. // bool IRrecv::decodeHaierAC(decode_results* results, uint16_t nbits, bool strict) { diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Haier.h b/lib/IRremoteESP8266-2.6.0/src/ir_Haier.h similarity index 93% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Haier.h rename to lib/IRremoteESP8266-2.6.0/src/ir_Haier.h index fdc15a3a8..8f7b35196 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Haier.h +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Haier.h @@ -11,6 +11,9 @@ #endif #include "IRremoteESP8266.h" #include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif // HH HH AAA IIIII EEEEEEE RRRRRR // HH HH AAAAA III EE RR RR @@ -186,7 +189,7 @@ class IRHaierAC { explicit IRHaierAC(uint16_t pin); #if SEND_HAIER_AC - void send(); + void send(const uint16_t repeat = kHaierAcDefaultRepeat); #endif // SEND_HAIER_AC void begin(); @@ -223,6 +226,10 @@ class IRHaierAC { void setRaw(uint8_t new_code[]); static bool validChecksum(uint8_t state[], const uint16_t length = kHaierACStateLength); + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); + uint8_t convertSwingV(const stdAc::swingv_t position); + #ifdef ARDUINO String toString(); static String timeToString(const uint16_t nr_mins); @@ -230,14 +237,18 @@ class IRHaierAC { std::string toString(); static std::string timeToString(const uint16_t nr_mins); #endif +#ifndef UNIT_TEST private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif uint8_t remote_state[kHaierACStateLength]; void stateReset(); void checksum(); static uint16_t getTime(const uint8_t ptr[]); static void setTime(uint8_t ptr[], const uint16_t nr_mins); - IRsend _irsend; }; class IRHaierACYRW02 { @@ -245,7 +256,7 @@ class IRHaierACYRW02 { explicit IRHaierACYRW02(uint16_t pin); #if SEND_HAIER_AC_YRW02 - void send(); + void send(const uint16_t repeat = kHaierAcYrw02DefaultRepeat); #endif // SEND_HAIER_AC_YRW02 void begin(); @@ -281,17 +292,24 @@ class IRHaierACYRW02 { void setRaw(uint8_t new_code[]); static bool validChecksum(uint8_t state[], const uint16_t length = kHaierACYRW02StateLength); + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); + uint8_t convertSwingV(const stdAc::swingv_t position); #ifdef ARDUINO String toString(); #else std::string toString(); #endif +#ifndef UNIT_TEST private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif uint8_t remote_state[kHaierACYRW02StateLength]; void stateReset(); void checksum(); - IRsend _irsend; }; #endif // IR_HAIER_H_ diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Hitachi.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Hitachi.cpp similarity index 86% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Hitachi.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_Hitachi.cpp index 111051974..b88189f4a 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Hitachi.cpp +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Hitachi.cpp @@ -30,7 +30,7 @@ const uint16_t kHitachiAc1HdrSpace = 3400; const uint16_t kHitachiAcBitMark = 400; const uint16_t kHitachiAcOneSpace = 1250; const uint16_t kHitachiAcZeroSpace = 500; -const uint32_t kHitachiAcMinGap = 100000; // Completely made up value. +const uint32_t kHitachiAcMinGap = kDefaultMessageGap; // Just a guess. #if (SEND_HITACHI_AC || SEND_HITACHI_AC2) // Send a Hitachi A/C message. @@ -106,7 +106,7 @@ void IRsend::sendHitachiAC2(unsigned char data[], uint16_t nbytes, } #endif // SEND_HITACHI_AC2 -// Class for handling the remote control oh a Hitachi 28 byte A/C message. +// Class for handling the remote control on a Hitachi 28 byte A/C message. // Inspired by: // https://github.com/ToniA/arduino-heatpumpir/blob/master/HitachiHeatpumpIR.cpp @@ -159,9 +159,9 @@ void IRHitachiAc::setRaw(const uint8_t new_code[], const uint16_t length) { } #if SEND_HITACHI_AC -void IRHitachiAc::send() { +void IRHitachiAc::send(const uint16_t repeat) { checksum(); - _irsend.sendHitachiAC(remote_state); + _irsend.sendHitachiAC(remote_state, kHitachiAcStateLength, repeat); } #endif // SEND_HITACHI_AC @@ -257,6 +257,40 @@ void IRHitachiAc::setSwingHorizontal(const bool on) { remote_state[15] &= 0x7F; } + +// Convert a standard A/C mode into its native mode. +uint8_t IRHitachiAc::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kHitachiAcCool; + case stdAc::opmode_t::kHeat: + return kHitachiAcHeat; + case stdAc::opmode_t::kDry: + return kHitachiAcDry; + case stdAc::opmode_t::kFan: + return kHitachiAcFan; + default: + return kHitachiAcAuto; + } +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRHitachiAc::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: + return kHitachiAcFanLow; + case stdAc::fanspeed_t::kMedium: + return kHitachiAcFanLow + 1; + case stdAc::fanspeed_t::kHigh: + return kHitachiAcFanHigh - 1; + case stdAc::fanspeed_t::kMax: + return kHitachiAcFanHigh; + default: + return kHitachiAcFanAuto; + } +} + // Convert the internal state into a human readable string. #ifdef ARDUINO String IRHitachiAc::toString() { @@ -265,57 +299,60 @@ String IRHitachiAc::toString() { std::string IRHitachiAc::toString() { std::string result = ""; #endif // ARDUINO - result += "Power: "; + result += F("Power: "); if (getPower()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Mode: " + uint64ToString(getMode()); + result += F("Off"); + result += F(", Mode: "); + result += uint64ToString(getMode()); switch (getMode()) { case kHitachiAcAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kHitachiAcCool: - result += " (COOL)"; + result += F(" (COOL)"); break; case kHitachiAcHeat: - result += " (HEAT)"; + result += F(" (HEAT)"); break; case kHitachiAcDry: - result += " (DRY)"; + result += F(" (DRY)"); break; case kHitachiAcFan: - result += " (FAN)"; + result += F(" (FAN)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); } - result += ", Temp: " + uint64ToString(getTemp()) + "C"; - result += ", Fan: " + uint64ToString(getFan()); + result += F(", Temp: "); + result += uint64ToString(getTemp()); + result += F("C, Fan: "); + result += uint64ToString(getFan()); switch (getFan()) { case kHitachiAcFanAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kHitachiAcFanLow: - result += " (LOW)"; + result += F(" (LOW)"); break; case kHitachiAcFanHigh: - result += " (HIGH)"; + result += F(" (HIGH)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); break; } - result += ", Swing (Vertical): "; + result += F(", Swing (Vertical): "); if (getSwingVertical()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Swing (Horizontal): "; + result += F("Off"); + result += F(", Swing (Horizontal): "); if (getSwingHorizontal()) - result += "On"; + result += F("On"); else - result += "Off"; + result += F("Off"); return result; } diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Hitachi.h b/lib/IRremoteESP8266-2.6.0/src/ir_Hitachi.h similarity index 87% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Hitachi.h rename to lib/IRremoteESP8266-2.6.0/src/ir_Hitachi.h index eddab59e4..532717447 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Hitachi.h +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Hitachi.h @@ -14,6 +14,9 @@ #endif #include "IRremoteESP8266.h" #include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif // Constants const uint8_t kHitachiAcAuto = 2; @@ -35,7 +38,7 @@ class IRHitachiAc { void stateReset(); #if SEND_HITACHI_AC - void send(); + void send(const uint16_t repeat = kHitachiAcDefaultRepeat); #endif // SEND_HITACHI_AC void begin(); void on(); @@ -59,17 +62,23 @@ class IRHitachiAc { const uint16_t length = kHitachiAcStateLength); static uint8_t calcChecksum(const uint8_t state[], const uint16_t length = kHitachiAcStateLength); + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); #ifdef ARDUINO String toString(); #else std::string toString(); #endif +#ifndef UNIT_TEST private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif // The state of the IR remote in IR code form. uint8_t remote_state[kHitachiAcStateLength]; void checksum(const uint16_t length = kHitachiAcStateLength); - IRsend _irsend; uint8_t _previoustemp; }; diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_JVC.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_JVC.cpp similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_JVC.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_JVC.cpp diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Kelvinator.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Kelvinator.cpp similarity index 91% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Kelvinator.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_Kelvinator.cpp index ddf61b097..c69f4cb8a 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Kelvinator.cpp +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Kelvinator.cpp @@ -19,6 +19,7 @@ #ifndef ARDUINO #include #endif +#include "IRac.h" #include "IRrecv.h" #include "IRsend.h" #include "IRutils.h" @@ -141,9 +142,9 @@ void IRKelvinatorAC::fixup() { } #if SEND_KELVINATOR -void IRKelvinatorAC::send() { +void IRKelvinatorAC::send(const uint16_t repeat) { fixup(); // Ensure correct settings before sending. - _irsend.sendKelvinator(remote_state); + _irsend.sendKelvinator(remote_state, kKelvinatorStateLength, repeat); } #endif // SEND_KELVINATOR @@ -347,6 +348,22 @@ bool IRKelvinatorAC::getTurbo() { return ((remote_state[2] & kKelvinatorTurbo) != 0); } +// Convert a standard A/C mode into its native mode. +uint8_t IRKelvinatorAC::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kKelvinatorCool; + case stdAc::opmode_t::kHeat: + return kKelvinatorHeat; + case stdAc::opmode_t::kDry: + return kKelvinatorDry; + case stdAc::opmode_t::kFan: + return kKelvinatorFan; + default: + return kKelvinatorAuto; + } +} + // Convert the internal state into a human readable string. #ifdef ARDUINO String IRKelvinatorAC::toString() { @@ -355,76 +372,79 @@ String IRKelvinatorAC::toString() { std::string IRKelvinatorAC::toString() { std::string result = ""; #endif // ARDUINO - result += "Power: "; + result += F("Power: "); if (getPower()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Mode: " + uint64ToString(getMode()); + result += F("Off"); + result += F(", Mode: "); + result += uint64ToString(getMode()); switch (getMode()) { case kKelvinatorAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kKelvinatorCool: - result += " (COOL)"; + result += F(" (COOL)"); break; case kKelvinatorHeat: - result += " (HEAT)"; + result += F(" (HEAT)"); break; case kKelvinatorDry: - result += " (DRY)"; + result += F(" (DRY)"); break; case kKelvinatorFan: - result += " (FAN)"; + result += F(" (FAN)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); } - result += ", Temp: " + uint64ToString(getTemp()) + "C"; - result += ", Fan: " + uint64ToString(getFan()); + result += F(", Temp: "); + result += uint64ToString(getTemp()); + result += F("C, Fan: "); + result += uint64ToString(getFan()); switch (getFan()) { case kKelvinatorFanAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kKelvinatorFanMax: - result += " (MAX)"; + result += F(" (MAX)"); break; } - result += ", Turbo: "; + result += F(", Turbo: "); if (getTurbo()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Quiet: "; + result += F("Off"); + result += F(", Quiet: "); if (getQuiet()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", XFan: "; + result += F("Off"); + result += F(", XFan: "); if (getXFan()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", IonFilter: "; + result += F("Off"); + result += F(", IonFilter: "); if (getIonFilter()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Light: "; + result += F("Off"); + result += F(", Light: "); if (getLight()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Swing (Horizontal): "; + result += F("Off"); + result += F(", Swing (Horizontal): "); if (getSwingHorizontal()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Swing (Vertical): "; + result += F("Off"); + result += F(", Swing (Vertical): "); if (getSwingVertical()) - result += "On"; + result += F("On"); else - result += "Off"; + result += F("Off"); return result; } diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Kelvinator.h b/lib/IRremoteESP8266-2.6.0/src/ir_Kelvinator.h similarity index 96% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Kelvinator.h rename to lib/IRremoteESP8266-2.6.0/src/ir_Kelvinator.h index 1508d6cdc..ce830c70a 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Kelvinator.h +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Kelvinator.h @@ -14,6 +14,9 @@ #endif #include "IRremoteESP8266.h" #include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif // KK KK EEEEEEE LL VV VV IIIII NN NN AAA TTTTTTT OOOOO RRRRRR // KK KK EE LL VV VV III NNN NN AAAAA TTT OO OO RR RR @@ -130,7 +133,7 @@ class IRKelvinatorAC { void stateReset(); #if SEND_KELVINATOR - void send(); + void send(const uint16_t repeat = kKelvinatorDefaultRepeat); #endif // SEND_KELVINATOR void begin(); void on(); @@ -163,18 +166,23 @@ class IRKelvinatorAC { const uint8_t* block, const uint16_t length = kKelvinatorStateLength / 2); static bool validChecksum(const uint8_t state[], const uint16_t length = kKelvinatorStateLength); + uint8_t convertMode(const stdAc::opmode_t mode); #ifdef ARDUINO String toString(); #else std::string toString(); #endif +#ifndef UNIT_TEST private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif // The state of the IR remote in IR code form. uint8_t remote_state[kKelvinatorStateLength]; void checksum(const uint16_t length = kKelvinatorStateLength); void fixup(); - IRsend _irsend; }; #endif // IR_KELVINATOR_H_ diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_LG.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_LG.cpp similarity index 98% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_LG.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_LG.cpp index f9d922fc7..36c85ff15 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_LG.cpp +++ b/lib/IRremoteESP8266-2.6.0/src/ir_LG.cpp @@ -85,11 +85,13 @@ uint8_t calcLGChecksum(uint16_t data) { // IR Remote models: 6711A20083V void IRsend::sendLG(uint64_t data, uint16_t nbits, uint16_t repeat) { uint16_t repeatHeaderMark = 0; + uint8_t duty = kDutyDefault; if (nbits >= kLg32Bits) { // LG 32bit protocol is near identical to Samsung except for repeats. sendSAMSUNG(data, nbits, 0); // Send it as a single Samsung message. repeatHeaderMark = kLg32RptHdrMark; + duty = 33; repeat++; } else { // LG (28-bit) protocol. @@ -97,7 +99,7 @@ void IRsend::sendLG(uint64_t data, uint16_t nbits, uint16_t repeat) { sendGeneric(kLgHdrMark, kLgHdrSpace, kLgBitMark, kLgOneSpace, kLgBitMark, kLgZeroSpace, kLgBitMark, kLgMinGap, kLgMinMessageLength, data, nbits, 38, true, 0, // Repeats are handled later. - 50); + duty); } // Repeat @@ -105,7 +107,7 @@ void IRsend::sendLG(uint64_t data, uint16_t nbits, uint16_t repeat) { if (repeat) sendGeneric(repeatHeaderMark, kLgRptSpace, 0, 0, 0, 0, // No data is sent. kLgBitMark, kLgMinGap, kLgMinMessageLength, 0, 0, // No data. - 38, true, repeat - 1, 50); + 38, true, repeat - 1, duty); } // Send an LG Variant-2 formatted message. diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_LG.h b/lib/IRremoteESP8266-2.6.0/src/ir_LG.h similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_LG.h rename to lib/IRremoteESP8266-2.6.0/src/ir_LG.h diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Lasertag.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Lasertag.cpp similarity index 98% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Lasertag.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_Lasertag.cpp index 7f0b89ae9..b1cbdc9b1 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Lasertag.cpp +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Lasertag.cpp @@ -14,7 +14,7 @@ // Constants const uint16_t kLasertagMinSamples = 13; const uint16_t kLasertagTick = 333; -const uint32_t kLasertagMinGap = 100000; // Completely made up amount. +const uint32_t kLasertagMinGap = kDefaultMessageGap; // Just a guess. const uint8_t kLasertagTolerance = 0; // Percentage error margin. const uint16_t kLasertagExcess = 0; // See kMarkExcess. const uint16_t kLasertagDelta = 150; // Use instead of Excess and Tolerance. diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Lego.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Lego.cpp new file mode 100644 index 000000000..b051aba51 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Lego.cpp @@ -0,0 +1,128 @@ +// Copyright 2019 David Conran + +#include +#include "IRrecv.h" +#include "IRsend.h" +#include "IRutils.h" + +// LEGO +// (LEGO is a Registrated Trademark of the Lego Group.) +// +// Supported Devices: +// - LEGO Power Functions IR Receiver +// +// Ref: +// - https://github.com/markszabo/IRremoteESP8266/issues/641 +// - https://github.com/markszabo/IRremoteESP8266/files/2974525/LEGO_Power_Functions_RC_v120.pdf + +// Constants +const uint16_t kLegoPfBitMark = 158; +const uint16_t kLegoPfHdrSpace = 1026; +const uint16_t kLegoPfZeroSpace = 263; +const uint16_t kLegoPfOneSpace = 553; +const uint32_t kLegoPfMinCommandLength = 16000; // 16ms + + +#if SEND_LEGOPF +// Send a LEGO Power Functions message. +// +// Args: +// data: Contents of the message to be sent. +// nbits: Nr. of bits of data to be sent. Typically kLegoPfBits. +// repeat: Nr. of additional times the message is to be sent. +// Note: Non-zero repeats results in at least 5 messages per spec. +// +// Status: Beta / Should work. +void IRsend::sendLegoPf(const uint64_t data, const uint16_t nbits, + const uint16_t repeat) { + uint8_t channelid = ((data >> (nbits - 4)) & 0b11) + 1; + if (repeat) { + // We are in repeat mode. + // Spec says a pause before transmittion. + if (channelid < 4) space((4 - channelid) * kLegoPfMinCommandLength); + // Spec says there are a minimum of 5 message repeats. + for (uint16_t r = 0; r < std::max(repeat, (uint16_t)5); r++) { + // Lego has a special repeat mode which repeats a message with varying + // start to start times. + sendGeneric(kLegoPfBitMark, kLegoPfHdrSpace, + kLegoPfBitMark, kLegoPfOneSpace, + kLegoPfBitMark, kLegoPfZeroSpace, + kLegoPfBitMark, kLegoPfHdrSpace, + ((r < 2) ? 5 : (6 + 2 * channelid)) * kLegoPfMinCommandLength, + data, nbits, 38000, true, 0, kDutyDefault); + } + } else { // No repeat, just a simple message. + sendGeneric(kLegoPfBitMark, kLegoPfHdrSpace, + kLegoPfBitMark, kLegoPfOneSpace, + kLegoPfBitMark, kLegoPfZeroSpace, + kLegoPfBitMark, kLegoPfHdrSpace, + kLegoPfMinCommandLength * 5, + data, nbits, 38000, true, 0, kDutyDefault); + } +} +#endif // SEND_LEGO + +#if DECODE_LEGOPF +// Decode the supplied LEGO Power Functions message. +// +// Args: +// results: Ptr to the data to decode and where to store the decode result. +// nbits: The number of data bits to expect. Typically kLegoPfBits. +// strict: Flag indicating if we should perform strict matching. +// Returns: +// boolean: True if it can decode it, false if it can't. +// +// Status: Alpha / Untested. +bool IRrecv::decodeLegoPf(decode_results* results, + const uint16_t nbits, const bool strict) { + // Check if can possibly be a valid LEGO message. + if (results->rawlen < 2 * nbits + kHeader + kFooter - 1) return false; + if (strict && nbits != kLegoPfBits) return false; // Not what is expected + + uint64_t data = 0; + uint16_t offset = kStartOffset; + match_result_t data_result; + + // Header + if (!matchMark(results->rawbuf[offset++], kLegoPfBitMark)) return false; + if (!matchSpace(results->rawbuf[offset++], kLegoPfHdrSpace)) return false; + // Data (Typically 16 bits) + data_result = + matchData(&(results->rawbuf[offset]), nbits, + kLegoPfBitMark, kLegoPfOneSpace, + kLegoPfBitMark, kLegoPfZeroSpace, + kTolerance, kMarkExcess, true); + if (data_result.success == false) return false; + data = data_result.data; + offset += data_result.used; + uint16_t actualBits = data_result.used / 2; + + // Footer. + if (!matchMark(results->rawbuf[offset++], kLegoPfBitMark)) return false; + if (offset < results->rawlen && + !matchAtLeast(results->rawbuf[offset], kLegoPfMinCommandLength)) + return false; + + // Compliance + if (actualBits < nbits) return false; + if (strict) { + if (actualBits != nbits) return false; // Not as we expected. + // Verify the Longitudinal Redundancy Check (LRC) + uint16_t lrc_data = data; + uint8_t lrc = 0xF; + for (uint8_t i = 0; i < 4; i++) { + lrc ^= (lrc_data & 0xF); + lrc_data >>= 4; + } + if (lrc) return false; + } + + // Success + results->decode_type = LEGOPF; + results->bits = actualBits; + results->value = data; + results->address = ((data >> (nbits - 4)) & 0b11) + 1; // Channel Id + results->command = (data >> 4) & 0xFF; // Stuff between Channel Id and LRC. + return true; +} +#endif // DECODE_LEGOPF diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Lutron.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Lutron.cpp similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Lutron.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_Lutron.cpp diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_MWM.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_MWM.cpp similarity index 98% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_MWM.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_MWM.cpp index a75e99e3a..61eac49e2 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_MWM.cpp +++ b/lib/IRremoteESP8266-2.6.0/src/ir_MWM.cpp @@ -105,7 +105,7 @@ bool IRrecv::decodeMWM(decode_results *results, uint16_t nbits, bool strict) { for (; offset < results->rawlen && results->bits < 8 * kStateSizeMax; frame_bits++) { DPRINT("DEBUG: decodeMWM: offset = "); - DPRINTLN(uint64ToString(offset)); + DPRINTLN(offset); int16_t level = getRClevel(results, &offset, &used, kMWMTick, kMWMTolerance, kMWMExcess, kMWMDelta, kMWMMaxWidth); if (level < 0) { @@ -129,7 +129,7 @@ bool IRrecv::decodeMWM(decode_results *results, uint16_t nbits, bool strict) { DPRINT("DEBUG: decodeMWM: data_bits = "); DPRINTLN(data_bits); DPRINT("DEBUG: decodeMWM: Finished byte: "); - DPRINTLN(data); + DPRINTLN(uint64ToString(data)); results->state[data_bits / 8 - 1] = data & 0xFF; results->bits = data_bits; data = 0; diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Magiquest.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Magiquest.cpp similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Magiquest.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_Magiquest.cpp diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Magiquest.h b/lib/IRremoteESP8266-2.6.0/src/ir_Magiquest.h similarity index 86% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Magiquest.h rename to lib/IRremoteESP8266-2.6.0/src/ir_Magiquest.h index d2d82d152..81fff53ad 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Magiquest.h +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Magiquest.h @@ -31,5 +31,5 @@ const uint16_t kMagiQuestMarkZero = 280; const uint16_t kMagiQuestSpaceZero = 850; const uint16_t kMagiQuestMarkOne = 580; const uint16_t kMagiQuestSpaceOne = 600; -const uint32_t kMagiQuestGap = 100000; // A guess of the gap between messages -#endif // IR_MAGIQUEST_H_ +const uint32_t kMagiQuestGap = kDefaultMessageGap; // Just a guess. +#endif // IR_MAGIQUEST_H_ diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Midea.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Midea.cpp similarity index 87% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Midea.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_Midea.cpp index 8e55c7d22..8d5d9494f 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Midea.cpp +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Midea.cpp @@ -104,9 +104,9 @@ void IRMideaAC::begin() { _irsend.begin(); } #if SEND_MIDEA // Send the current desired state to the IR LED. -void IRMideaAC::send() { +void IRMideaAC::send(const uint16_t repeat) { checksum(); // Ensure correct checksum before sending. - _irsend.sendMidea(remote_state); + _irsend.sendMidea(remote_state, kMideaBits, repeat); } #endif // SEND_MIDEA @@ -257,6 +257,39 @@ void IRMideaAC::checksum() { remote_state |= calcChecksum(remote_state); } + +// Convert a standard A/C mode into its native mode. +uint8_t IRMideaAC::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kMideaACCool; + case stdAc::opmode_t::kHeat: + return kMideaACHeat; + case stdAc::opmode_t::kDry: + return kMideaACDry; + case stdAc::opmode_t::kFan: + return kMideaACFan; + default: + return kMideaACAuto; + } +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRMideaAC::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: + return kMideaACFanLow; + case stdAc::fanspeed_t::kMedium: + return kMideaACFanMed; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: + return kMideaACFanHigh; + default: + return kMideaACFanAuto; + } +} + // Convert the internal state into a human readable string. #ifdef ARDUINO String IRMideaAC::toString() { @@ -265,53 +298,57 @@ String IRMideaAC::toString() { std::string IRMideaAC::toString() { std::string result = ""; #endif // ARDUINO - result += "Power: "; + result += F("Power: "); if (getPower()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Mode: " + uint64ToString(getMode()); + result += F("Off"); + result += F(", Mode: "); + result += uint64ToString(getMode()); switch (getMode()) { case kMideaACAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kMideaACCool: - result += " (COOL)"; + result += F(" (COOL)"); break; case kMideaACHeat: - result += " (HEAT)"; + result += F(" (HEAT)"); break; case kMideaACDry: - result += " (DRY)"; + result += F(" (DRY)"); break; case kMideaACFan: - result += " (FAN)"; + result += F(" (FAN)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); } - result += ", Temp: " + uint64ToString(getTemp(true)) + "C/" + - uint64ToString(getTemp(false)) + "F"; - result += ", Fan: " + uint64ToString(getFan()); + result += F(", Temp: "); + result += uint64ToString(getTemp(true)); + result += F("C/"); + result += uint64ToString(getTemp(false)); + result += F("F, Fan: "); + result += uint64ToString(getFan()); switch (getFan()) { case kMideaACFanAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kMideaACFanLow: - result += " (LOW)"; + result += F(" (LOW)"); break; case kMideaACFanMed: - result += " (MED)"; + result += F(" (MED)"); break; case kMideaACFanHigh: - result += " (HI)"; + result += F(" (HI)"); break; } - result += ", Sleep: "; + result += F(", Sleep: "); if (getSleep()) - result += "On"; + result += F("On"); else - result += "Off"; + result += F("Off"); return result; } diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Midea.h b/lib/IRremoteESP8266-2.6.0/src/ir_Midea.h similarity index 93% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Midea.h rename to lib/IRremoteESP8266-2.6.0/src/ir_Midea.h index aa9f94a92..ab14eb252 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Midea.h +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Midea.h @@ -11,6 +11,9 @@ #endif #include "IRremoteESP8266.h" #include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif // MM MM IIIII DDDDD EEEEEEE AAA // MMM MMM III DD DD EE AAAAA @@ -67,7 +70,7 @@ class IRMideaAC { void stateReset(); #if SEND_MIDEA - void send(); + void send(const uint16_t repeat = kMideaMinRepeat); #endif // SEND_MIDEA void begin(); void on(); @@ -85,6 +88,8 @@ class IRMideaAC { static bool validChecksum(const uint64_t state); void setSleep(const bool state); bool getSleep(); + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); #ifdef ARDUINO String toString(); #else @@ -93,11 +98,13 @@ class IRMideaAC { #ifndef UNIT_TEST private: + IRsend _irsend; +#else + IRsendTest _irsend; #endif uint64_t remote_state; void checksum(); static uint8_t calcChecksum(const uint64_t state); - IRsend _irsend; }; #endif // IR_MIDEA_H_ diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Mitsubishi.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Mitsubishi.cpp similarity index 90% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Mitsubishi.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_Mitsubishi.cpp index b092c27b9..ca9bef5d9 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Mitsubishi.cpp +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Mitsubishi.cpp @@ -165,7 +165,7 @@ bool IRrecv::decodeMitsubishi(decode_results *results, uint16_t nbits, // This protocol appears to have a manditory in-protocol repeat. // That is in *addition* to the entire message needing to be sent twice // for the device to accept the command. That is separate from the repeat. -// i.e. Allegedly, the real remote requires the "OFF" button pressed twice. +// i.e. Allegedly, the real remote requires the "Off" button pressed twice. // You will need to add a suitable gap yourself. // Ref: // https://github.com/markszabo/IRremoteESP8266/issues/441 @@ -453,9 +453,9 @@ void IRMitsubishiAC::begin() { _irsend.begin(); } #if SEND_MITSUBISHI_AC // Send the current desired state to the IR LED. -void IRMitsubishiAC::send() { +void IRMitsubishiAC::send(const uint16_t repeat) { checksum(); // Ensure correct checksum before sending. - _irsend.sendMitsubishiAC(remote_state); + _irsend.sendMitsubishiAC(remote_state, kMitsubishiACStateLength, repeat); } #endif // SEND_MITSUBISHI_AC @@ -615,6 +615,52 @@ void IRMitsubishiAC::setTimer(uint8_t timer) { remote_state[13] = timer & 0b111; } +// Convert a standard A/C mode into its native mode. +uint8_t IRMitsubishiAC::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kMitsubishiAcCool; + case stdAc::opmode_t::kHeat: + return kMitsubishiAcHeat; + case stdAc::opmode_t::kDry: + return kMitsubishiAcDry; + default: + return kMitsubishiAcAuto; + } +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRMitsubishiAC::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + return kMitsubishiAcFanSilent; + case stdAc::fanspeed_t::kLow: + return kMitsubishiAcFanRealMax - 3; + case stdAc::fanspeed_t::kMedium: + return kMitsubishiAcFanRealMax - 2; + case stdAc::fanspeed_t::kHigh: + return kMitsubishiAcFanRealMax - 1; + case stdAc::fanspeed_t::kMax: + return kMitsubishiAcFanRealMax; + default: + return kMitsubishiAcFanAuto; + } +} + +// Convert a standard A/C vertical swing into its native setting. +uint8_t IRMitsubishiAC::convertSwingV(const stdAc::swingv_t position) { + switch (position) { + case stdAc::swingv_t::kHighest: + case stdAc::swingv_t::kHigh: + case stdAc::swingv_t::kMiddle: + case stdAc::swingv_t::kLow: + case stdAc::swingv_t::kLowest: + return kMitsubishiAcVaneAutoMove; + default: + return kMitsubishiAcVaneAuto; + } +} + #ifdef ARDUINO String IRMitsubishiAC::timeToString(uint64_t time) { String result = ""; @@ -622,10 +668,10 @@ String IRMitsubishiAC::timeToString(uint64_t time) { std::string IRMitsubishiAC::timeToString(uint64_t time) { std::string result = ""; #endif // ARDUINO - if (time / 6 < 10) result += "0"; + if (time / 6 < 10) result += '0'; result += uint64ToString(time / 6); - result += ":"; - if (time * 10 % 60 < 10) result += "0"; + result += ':'; + if (time * 10 % 60 < 10) result += '0'; result += uint64ToString(time * 10 % 60); return result; } @@ -638,77 +684,78 @@ String IRMitsubishiAC::toString() { std::string IRMitsubishiAC::toString() { std::string result = ""; #endif // ARDUINO - result += "Power: "; + result += F("Power: "); if (getPower()) - result += "On"; + result += F("On"); else - result += "Off"; + result += F("Off"); switch (getMode()) { case MITSUBISHI_AC_AUTO: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case MITSUBISHI_AC_COOL: - result += " (COOL)"; + result += F(" (COOL)"); break; case MITSUBISHI_AC_DRY: - result += " (DRY)"; + result += F(" (DRY)"); break; case MITSUBISHI_AC_HEAT: - result += " (HEAT)"; + result += F(" (HEAT)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); } - result += ", Temp: " + uint64ToString(getTemp()) + "C"; - result += ", FAN: "; + result += F(", Temp: "); + result += uint64ToString(getTemp()); + result += F("C, FAN: "); switch (getFan()) { case MITSUBISHI_AC_FAN_AUTO: - result += "AUTO"; + result += F("AUTO"); break; case MITSUBISHI_AC_FAN_MAX: - result += "MAX"; + result += F("MAX"); break; case MITSUBISHI_AC_FAN_SILENT: - result += "SILENT"; + result += F("SILENT"); break; default: result += uint64ToString(getFan()); } - result += ", VANE: "; + result += F(", VANE: "); switch (getVane()) { case MITSUBISHI_AC_VANE_AUTO: - result += "AUTO"; + result += F("AUTO"); break; case MITSUBISHI_AC_VANE_AUTO_MOVE: - result += "AUTO MOVE"; + result += F("AUTO MOVE"); break; default: result += uint64ToString(getVane()); } - result += ", Time: "; + result += F(", Time: "); result += timeToString(getClock()); - result += ", On timer: "; + result += F(", On timer: "); result += timeToString(getStartClock()); - result += ", Off timer: "; + result += F(", Off timer: "); result += timeToString(getStopClock()); - result += ", Timer: "; + result += F(", Timer: "); switch (getTimer()) { case kMitsubishiAcNoTimer: - result += "-"; + result += '-'; break; case kMitsubishiAcStartTimer: - result += "Start"; + result += F("Start"); break; case kMitsubishiAcStopTimer: - result += "Stop"; + result += F("Stop"); break; case kMitsubishiAcStartStopTimer: - result += "Start+Stop"; + result += F("Start+Stop"); break; default: - result += "? ("; + result += F("? ("); result += getTimer(); - result += ")\n"; + result += F(")\n"); } return result; } diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Mitsubishi.h b/lib/IRremoteESP8266-2.6.0/src/ir_Mitsubishi.h similarity index 91% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Mitsubishi.h rename to lib/IRremoteESP8266-2.6.0/src/ir_Mitsubishi.h index 7b03efce6..c8dca5dbc 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Mitsubishi.h +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Mitsubishi.h @@ -12,6 +12,9 @@ #endif #include "IRremoteESP8266.h" #include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif // MMMMM IIIII TTTTT SSSS U U BBBB IIIII SSSS H H IIIII // M M M I T S U U B B I S H H I @@ -64,7 +67,7 @@ class IRMitsubishiAC { void stateReset(); #if SEND_MITSUBISHI_AC - void send(); + void send(const uint16_t repeat = kMitsubishiACMinRepeat); #endif // SEND_MITSUBISHI_AC void begin(); void on(); @@ -89,13 +92,21 @@ class IRMitsubishiAC { void setStopClock(uint8_t clock); uint8_t getTimer(); void setTimer(uint8_t timer); + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); + uint8_t convertSwingV(const stdAc::swingv_t position); #ifdef ARDUINO String toString(); #else std::string toString(); #endif +#ifndef UNIT_TEST private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif #ifdef ARDUINO String timeToString(uint64_t time); #else @@ -103,7 +114,6 @@ class IRMitsubishiAC { #endif uint8_t remote_state[kMitsubishiACStateLength]; void checksum(); - IRsend _irsend; }; #endif // IR_MITSUBISHI_H_ diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_MitsubishiHeavy.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_MitsubishiHeavy.cpp new file mode 100644 index 000000000..9048124d4 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/src/ir_MitsubishiHeavy.cpp @@ -0,0 +1,1014 @@ +// Copyright 2019 David Conran +// Mitsubishi Heavy Industries A/C remote emulation. + +// Code to emulate Mitsubishi Heavy Industries A/C IR remote control units, +// which should control at least the following A/C units: +// Remote Control RLA502A700B: +// Model SRKxxZM-S +// Model SRKxxZMXA-S +// Remote Control RKX502A001C: +// Model SRKxxZJ-S + +// Note: This code was *heavily* influenced by @ToniA's great work & code, +// but it has been written from scratch. +// Nothing was copied other than constants and message analysis. + +#include "ir_MitsubishiHeavy.h" +#include +#include "IRremoteESP8266.h" +#include "IRutils.h" +#ifndef ARDUINO +#include +#endif + +// Ref: +// https://github.com/markszabo/IRremoteESP8266/issues/660 +// https://github.com/ToniA/Raw-IR-decoder-for-Arduino/blob/master/MitsubishiHeavy.cpp +// https://github.com/ToniA/arduino-heatpumpir/blob/master/MitsubishiHeavyHeatpumpIR.cpp + +// Constants +const uint16_t kMitsubishiHeavyHdrMark = 3140; +const uint16_t kMitsubishiHeavyHdrSpace = 1630; +const uint16_t kMitsubishiHeavyBitMark = 370; +const uint16_t kMitsubishiHeavyOneSpace = 420; +const uint16_t kMitsubishiHeavyZeroSpace = 1220; +const uint32_t kMitsubishiHeavyGap = kDefaultMessageGap; // Just a guess. + +#if SEND_MITSUBISHIHEAVY +// Send a MitsubishiHeavy 88 bit A/C message. +// +// Args: +// data: Contents of the message to be sent. +// nbits: Nr. of bits of data to be sent. Typically kMitsubishiHeavy88Bits. +// repeat: Nr. of additional times the message is to be sent. +// +// Status: BETA / Appears to be working. Needs testing against a real device. +void IRsend::sendMitsubishiHeavy88(const unsigned char data[], + const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes < kMitsubishiHeavy88StateLength) + return; // Not enough bytes to send a proper message. + sendGeneric(kMitsubishiHeavyHdrMark, kMitsubishiHeavyHdrSpace, + kMitsubishiHeavyBitMark, kMitsubishiHeavyOneSpace, + kMitsubishiHeavyBitMark, kMitsubishiHeavyZeroSpace, + kMitsubishiHeavyBitMark, kMitsubishiHeavyGap, + data, nbytes, 38000, false, repeat, kDutyDefault); +} + +// Send a MitsubishiHeavy 152 bit A/C message. +// +// Args: +// data: Contents of the message to be sent. +// nbits: Nr. of bits of data to be sent. Typically kMitsubishiHeavy152Bits. +// repeat: Nr. of additional times the message is to be sent. +// +// Status: BETA / Appears to be working. Needs testing against a real device. +void IRsend::sendMitsubishiHeavy152(const unsigned char data[], + const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes < kMitsubishiHeavy152StateLength) + return; // Not enough bytes to send a proper message. + sendMitsubishiHeavy88(data, nbytes, repeat); +} +#endif // SEND_MITSUBISHIHEAVY + +// Class for decoding and constructing MitsubishiHeavy152 AC messages. +IRMitsubishiHeavy152Ac::IRMitsubishiHeavy152Ac( + const uint16_t pin) : _irsend(pin) { stateReset(); } + +void IRMitsubishiHeavy152Ac::begin() { _irsend.begin(); } + +#if SEND_MITSUBISHIHEAVY +void IRMitsubishiHeavy152Ac::send(const uint16_t repeat) { + _irsend.sendMitsubishiHeavy152(this->getRaw(), kMitsubishiHeavy152StateLength, + repeat); +} +#endif // SEND_MITSUBISHIHEAVY + +void IRMitsubishiHeavy152Ac::stateReset(void) { + uint8_t i = 0; + for (; i < kMitsubishiHeavySigLength; i++) + remote_state[i] = kMitsubishiHeavyZmsSig[i]; + for (; i < kMitsubishiHeavy152StateLength - 3; i += 2) remote_state[i] = 0; + remote_state[17] = 0x80; +} + +uint8_t *IRMitsubishiHeavy152Ac::getRaw(void) { + checksum(); + return remote_state; +} + +void IRMitsubishiHeavy152Ac::setRaw(const uint8_t *data) { + for (uint8_t i = 0; i < kMitsubishiHeavy152StateLength; i++) + remote_state[i] = data[i]; +} + +void IRMitsubishiHeavy152Ac::on(void) { + remote_state[5] |= kMitsubishiHeavyPowerBit; +} + +void IRMitsubishiHeavy152Ac::off(void) { + remote_state[5] &= ~kMitsubishiHeavyPowerBit; +} + +void IRMitsubishiHeavy152Ac::setPower(const bool on) { + if (on) + this->on(); + else + this->off(); +} + +bool IRMitsubishiHeavy152Ac::getPower(void) { + return remote_state[5] & kMitsubishiHeavyPowerBit; +} + +void IRMitsubishiHeavy152Ac::setTemp(const uint8_t temp) { + uint8_t newtemp = temp; + newtemp = std::min(newtemp, kMitsubishiHeavyMaxTemp); + newtemp = std::max(newtemp, kMitsubishiHeavyMinTemp); + + remote_state[7] &= ~kMitsubishiHeavyTempMask; + remote_state[7] |= newtemp - kMitsubishiHeavyMinTemp; +} + +uint8_t IRMitsubishiHeavy152Ac::getTemp(void) { + return (remote_state[7] & kMitsubishiHeavyTempMask) + kMitsubishiHeavyMinTemp; +} + +// Set the speed of the fan +void IRMitsubishiHeavy152Ac::setFan(const uint8_t speed) { + uint8_t newspeed = speed; + switch (speed) { + case kMitsubishiHeavy152FanLow: + case kMitsubishiHeavy152FanMed: + case kMitsubishiHeavy152FanHigh: + case kMitsubishiHeavy152FanMax: + case kMitsubishiHeavy152FanEcono: + case kMitsubishiHeavy152FanTurbo: + break; + default: + newspeed = kMitsubishiHeavy152FanAuto; + } + remote_state[9] &= ~kMitsubishiHeavyFanMask; + remote_state[9] |= newspeed; +} + +uint8_t IRMitsubishiHeavy152Ac::getFan(void) { + return remote_state[9] & kMitsubishiHeavyFanMask; +} + +void IRMitsubishiHeavy152Ac::setMode(const uint8_t mode) { + uint8_t newmode = mode; + switch (mode) { + case kMitsubishiHeavyCool: + case kMitsubishiHeavyDry: + case kMitsubishiHeavyFan: + case kMitsubishiHeavyHeat: + break; + default: + newmode = kMitsubishiHeavyAuto; + } + remote_state[5] &= ~kMitsubishiHeavyModeMask; + remote_state[5] |= newmode; +} + +uint8_t IRMitsubishiHeavy152Ac::getMode(void) { + return remote_state[5] & kMitsubishiHeavyModeMask; +} + +void IRMitsubishiHeavy152Ac::setSwingVertical(const uint8_t pos) { + uint8_t newpos = std::min(pos, kMitsubishiHeavy152SwingVOff); + remote_state[11] &= ~kMitsubishiHeavy152SwingVMask; + remote_state[11] |= (newpos << 5); +} + +uint8_t IRMitsubishiHeavy152Ac::getSwingVertical(void) { + return remote_state[11] >> 5; +} + +void IRMitsubishiHeavy152Ac::setSwingHorizontal(const uint8_t pos) { + uint8_t newpos = std::min(pos, kMitsubishiHeavy152SwingHOff); + remote_state[13] &= ~kMitsubishiHeavy152SwingHMask; + remote_state[13] |= (newpos & kMitsubishiHeavy152SwingHMask); +} + +uint8_t IRMitsubishiHeavy152Ac::getSwingHorizontal(void) { + return remote_state[13] & kMitsubishiHeavy152SwingHMask; +} + +void IRMitsubishiHeavy152Ac::setNight(const bool on) { + if (on) + remote_state[15] |= kMitsubishiHeavyNightBit; + else + remote_state[15] &= ~kMitsubishiHeavyNightBit; +} + +bool IRMitsubishiHeavy152Ac::getNight(void) { + return remote_state[15] & kMitsubishiHeavyNightBit; +} + +void IRMitsubishiHeavy152Ac::set3D(const bool on) { + if (on) + remote_state[11] |= kMitsubishiHeavy3DMask; + else + remote_state[11] &= ~kMitsubishiHeavy3DMask; +} + +bool IRMitsubishiHeavy152Ac::get3D(void) { + return (remote_state[11] & kMitsubishiHeavy3DMask) == kMitsubishiHeavy3DMask; +} + +void IRMitsubishiHeavy152Ac::setSilent(const bool on) { + if (on) + remote_state[15] |= kMitsubishiHeavySilentBit; + else + remote_state[15] &= ~kMitsubishiHeavySilentBit; +} + +bool IRMitsubishiHeavy152Ac::getSilent(void) { + return remote_state[15] & kMitsubishiHeavySilentBit; +} + +void IRMitsubishiHeavy152Ac::setFilter(const bool on) { + if (on) + remote_state[5] |= kMitsubishiHeavyFilterBit; + else + remote_state[5] &= ~kMitsubishiHeavyFilterBit; +} + +bool IRMitsubishiHeavy152Ac::getFilter(void) { + return remote_state[5] & kMitsubishiHeavyFilterBit; +} + +void IRMitsubishiHeavy152Ac::setClean(const bool on) { + this->setFilter(on); + if (on) + remote_state[5] |= kMitsubishiHeavyCleanBit; + else + remote_state[5] &= ~kMitsubishiHeavyCleanBit; +} + +bool IRMitsubishiHeavy152Ac::getClean(void) { + return remote_state[5] & kMitsubishiHeavyCleanBit && this->getFilter(); +} + +void IRMitsubishiHeavy152Ac::setTurbo(const bool on) { + if (on) + this->setFan(kMitsubishiHeavy152FanTurbo); + else if (this->getTurbo()) this->setFan(kMitsubishiHeavy152FanAuto); +} + +bool IRMitsubishiHeavy152Ac::getTurbo(void) { + return this->getFan() == kMitsubishiHeavy152FanTurbo; +} + +void IRMitsubishiHeavy152Ac::setEcono(const bool on) { + if (on) + this->setFan(kMitsubishiHeavy152FanEcono); + else if (this->getEcono()) this->setFan(kMitsubishiHeavy152FanAuto); +} + +bool IRMitsubishiHeavy152Ac::getEcono(void) { + return this->getFan() == kMitsubishiHeavy152FanEcono; +} + +// Verify the given state has a ZM-S signature. +bool IRMitsubishiHeavy152Ac::checkZmsSig(const uint8_t *state) { + for (uint8_t i = 0; i < kMitsubishiHeavySigLength; i++) + if (state[i] != kMitsubishiHeavyZmsSig[i]) return false; + return true; +} + +// Protocol technically has no checksum, but does has inverted byte pairs. +void IRMitsubishiHeavy152Ac::checksum(void) { + for (uint8_t i = kMitsubishiHeavySigLength - 2; + i < kMitsubishiHeavy152StateLength; + i += 2) { + remote_state[i + 1] = ~remote_state[i]; + } +} + +// Protocol technically has no checksum, but does has inverted byte pairs. +bool IRMitsubishiHeavy152Ac::validChecksum(const uint8_t *state, + const uint16_t length) { + // Assume anything too short is fine. + if (length < kMitsubishiHeavySigLength) return true; + // Check all the byte pairs. + for (uint16_t i = kMitsubishiHeavySigLength - 2; + i < length; + i += 2) { + // XOR of a byte and it's self inverted should be 0xFF; + if ((state[i] ^ state[i + 1]) != 0xFF) return false; + } + return true; +} + + +// Convert a standard A/C mode into its native mode. +uint8_t IRMitsubishiHeavy152Ac::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kMitsubishiHeavyCool; + case stdAc::opmode_t::kHeat: + return kMitsubishiHeavyHeat; + case stdAc::opmode_t::kDry: + return kMitsubishiHeavyDry; + case stdAc::opmode_t::kFan: + return kMitsubishiHeavyFan; + default: + return kMitsubishiHeavyAuto; + } +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRMitsubishiHeavy152Ac::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + return kMitsubishiHeavy152FanEcono; // Assumes Econo is slower than Low. + case stdAc::fanspeed_t::kLow: + return kMitsubishiHeavy152FanLow; + case stdAc::fanspeed_t::kMedium: + return kMitsubishiHeavy152FanMed; + case stdAc::fanspeed_t::kHigh: + return kMitsubishiHeavy152FanHigh; + case stdAc::fanspeed_t::kMax: + return kMitsubishiHeavy152FanMax; + default: + return kMitsubishiHeavy152FanAuto; + } +} + +// Convert a standard A/C vertical swing into its native setting. +uint8_t IRMitsubishiHeavy152Ac::convertSwingV(const stdAc::swingv_t position) { + switch (position) { + case stdAc::swingv_t::kAuto: + return kMitsubishiHeavy152SwingVAuto; + case stdAc::swingv_t::kHighest: + return kMitsubishiHeavy152SwingVHighest; + case stdAc::swingv_t::kHigh: + return kMitsubishiHeavy152SwingVHigh; + case stdAc::swingv_t::kMiddle: + return kMitsubishiHeavy152SwingVMiddle; + case stdAc::swingv_t::kLow: + return kMitsubishiHeavy152SwingVLow; + case stdAc::swingv_t::kLowest: + return kMitsubishiHeavy152SwingVLowest; + default: + return kMitsubishiHeavy152SwingVOff; + } +} + +// Convert a standard A/C horizontal swing into its native setting. +uint8_t IRMitsubishiHeavy152Ac::convertSwingH(const stdAc::swingh_t position) { + switch (position) { + case stdAc::swingh_t::kAuto: + return kMitsubishiHeavy152SwingHAuto; + case stdAc::swingh_t::kLeftMax: + return kMitsubishiHeavy152SwingHLeftMax; + case stdAc::swingh_t::kLeft: + return kMitsubishiHeavy152SwingHLeft; + case stdAc::swingh_t::kMiddle: + return kMitsubishiHeavy152SwingHMiddle; + case stdAc::swingh_t::kRight: + return kMitsubishiHeavy152SwingHRight; + case stdAc::swingh_t::kRightMax: + return kMitsubishiHeavy152SwingHRightMax; + default: + return kMitsubishiHeavy152SwingHOff; + } +} + +// Convert the internal state into a human readable string. +#ifdef ARDUINO +String IRMitsubishiHeavy152Ac::toString(void) { + String result = ""; +#else +std::string IRMitsubishiHeavy152Ac::toString(void) { + std::string result = ""; +#endif // ARDUINO + result += F("Power: "); + result += (this->getPower() ? F("On") : F("Off")); + result += F(", Mode: "); + result += uint64ToString(this->getMode()); + switch (this->getMode()) { + case kMitsubishiHeavyAuto: + result += F(" (Auto)"); + break; + case kMitsubishiHeavyCool: + result += F(" (Cool)"); + break; + case kMitsubishiHeavyHeat: + result += F(" (Heat)"); + break; + case kMitsubishiHeavyDry: + result += F(" (Dry)"); + break; + case kMitsubishiHeavyFan: + result += F(" (Fan)"); + break; + default: + result += F(" (UNKNOWN)"); + } + result += F(", Temp: "); + result += uint64ToString(this->getTemp()) + 'C'; + result += F(", Fan: "); + result += uint64ToString(this->getFan()); + switch (this->getFan()) { + case kMitsubishiHeavy152FanAuto: + result += F(" (Auto)"); + break; + case kMitsubishiHeavy152FanHigh: + result += F(" (High)"); + break; + case kMitsubishiHeavy152FanLow: + result += F(" (Low)"); + break; + case kMitsubishiHeavy152FanMed: + result += F(" (Med)"); + break; + case kMitsubishiHeavy152FanMax: + result += F(" (Max)"); + break; + case kMitsubishiHeavy152FanEcono: + result += F(" (Econo)"); + break; + case kMitsubishiHeavy152FanTurbo: + result += F(" (Turbo)"); + break; + default: + result += F(" (UNKNOWN)"); + } + result += F(", Swing (V): "); + result += uint64ToString(this->getSwingVertical()); + switch (this->getSwingVertical()) { + case kMitsubishiHeavy152SwingVAuto: + result += F(" (Auto)"); + break; + case kMitsubishiHeavy152SwingVHighest: + result += F(" (Highest)"); + break; + case kMitsubishiHeavy152SwingVHigh: + result += F(" (High)"); + break; + case kMitsubishiHeavy152SwingVMiddle: + result += F(" (Middle)"); + break; + case kMitsubishiHeavy152SwingVLow: + result += F(" (Low)"); + break; + case kMitsubishiHeavy152SwingVLowest: + result += F(" (Lowest)"); + break; + case kMitsubishiHeavy152SwingVOff: + result += F(" (Off)"); + break; + default: + result += F(" (UNKNOWN)"); + } + result += F(", Swing (H): "); + result += uint64ToString(this->getSwingHorizontal()); + switch (this->getSwingHorizontal()) { + case kMitsubishiHeavy152SwingHAuto: + result += F(" (Auto)"); + break; + case kMitsubishiHeavy152SwingHLeftMax: + result += F(" (Max Left)"); + break; + case kMitsubishiHeavy152SwingHLeft: + result += F(" (Left)"); + break; + case kMitsubishiHeavy152SwingHMiddle: + result += F(" (Middle)"); + break; + case kMitsubishiHeavy152SwingHRight: + result += F(" (Right)"); + break; + case kMitsubishiHeavy152SwingHRightMax: + result += F(" (Max Right)"); + break; + case kMitsubishiHeavy152SwingHLeftRight: + result += F(" (Left Right)"); + break; + case kMitsubishiHeavy152SwingHRightLeft: + result += F(" (Right Left)"); + break; + case kMitsubishiHeavy152SwingHOff: + result += F(" (Off)"); + break; + default: + result += F(" (UNKNOWN)"); + } + result += F(", Silent: "); + result += (this->getSilent() ? F("On") : F("Off")); + result += F(", Turbo: "); + result += (this->getTurbo() ? F("On") : F("Off")); + result += F(", Econo: "); + result += (this->getEcono() ? F("On") : F("Off")); + result += F(", Night: "); + result += (this->getNight() ? F("On") : F("Off")); + result += F(", Filter: "); + result += (this->getFilter() ? F("On") : F("Off")); + result += F(", 3D: "); + result += (this->get3D() ? F("On") : F("Off")); + result += F(", Clean: "); + result += (this->getClean() ? F("On") : F("Off")); + return result; +} + + +// Class for decoding and constructing MitsubishiHeavy88 AC messages. +IRMitsubishiHeavy88Ac::IRMitsubishiHeavy88Ac( + const uint16_t pin) : _irsend(pin) { stateReset(); } + +void IRMitsubishiHeavy88Ac::begin() { _irsend.begin(); } + +#if SEND_MITSUBISHIHEAVY +void IRMitsubishiHeavy88Ac::send(const uint16_t repeat) { + _irsend.sendMitsubishiHeavy88(this->getRaw(), kMitsubishiHeavy88StateLength, + repeat); +} +#endif // SEND_MITSUBISHIHEAVY + +void IRMitsubishiHeavy88Ac::stateReset(void) { + uint8_t i = 0; + for (; i < kMitsubishiHeavySigLength; i++) + remote_state[i] = kMitsubishiHeavyZjsSig[i]; + for (; i < kMitsubishiHeavy88StateLength; i++) remote_state[i] = 0; +} + +uint8_t *IRMitsubishiHeavy88Ac::getRaw(void) { + checksum(); + return remote_state; +} + +void IRMitsubishiHeavy88Ac::setRaw(const uint8_t *data) { + for (uint8_t i = 0; i < kMitsubishiHeavy88StateLength; i++) + remote_state[i] = data[i]; +} + +void IRMitsubishiHeavy88Ac::on(void) { + remote_state[9] |= kMitsubishiHeavyPowerBit; +} + +void IRMitsubishiHeavy88Ac::off(void) { + remote_state[9] &= ~kMitsubishiHeavyPowerBit; +} + +void IRMitsubishiHeavy88Ac::setPower(const bool on) { + if (on) + this->on(); + else + this->off(); +} + +bool IRMitsubishiHeavy88Ac::getPower(void) { + return remote_state[9] & kMitsubishiHeavyPowerBit; +} + +void IRMitsubishiHeavy88Ac::setTemp(const uint8_t temp) { + uint8_t newtemp = temp; + newtemp = std::min(newtemp, kMitsubishiHeavyMaxTemp); + newtemp = std::max(newtemp, kMitsubishiHeavyMinTemp); + + remote_state[9] &= kMitsubishiHeavyTempMask; + remote_state[9] |= ((newtemp - kMitsubishiHeavyMinTemp) << 4); +} + +uint8_t IRMitsubishiHeavy88Ac::getTemp(void) { + return (remote_state[9] >> 4) + kMitsubishiHeavyMinTemp; +} + +// Set the speed of the fan +void IRMitsubishiHeavy88Ac::setFan(const uint8_t speed) { + uint8_t newspeed = speed; + switch (speed) { + case kMitsubishiHeavy88FanLow: + case kMitsubishiHeavy88FanMed: + case kMitsubishiHeavy88FanHigh: + case kMitsubishiHeavy88FanTurbo: + case kMitsubishiHeavy88FanEcono: + break; + default: + newspeed = kMitsubishiHeavy88FanAuto; + } + remote_state[7] &= ~kMitsubishiHeavy88FanMask; + remote_state[7] |= (newspeed << 5); +} + +uint8_t IRMitsubishiHeavy88Ac::getFan(void) { + return remote_state[7] >> 5; +} + +void IRMitsubishiHeavy88Ac::setMode(const uint8_t mode) { + uint8_t newmode = mode; + switch (mode) { + case kMitsubishiHeavyCool: + case kMitsubishiHeavyDry: + case kMitsubishiHeavyFan: + case kMitsubishiHeavyHeat: + break; + default: + newmode = kMitsubishiHeavyAuto; + } + remote_state[9] &= ~kMitsubishiHeavyModeMask; + remote_state[9] |= newmode; +} + +uint8_t IRMitsubishiHeavy88Ac::getMode(void) { + return remote_state[9] & kMitsubishiHeavyModeMask; +} + +void IRMitsubishiHeavy88Ac::setSwingVertical(const uint8_t pos) { + uint8_t newpos; + switch (pos) { + case kMitsubishiHeavy88SwingVAuto: + case kMitsubishiHeavy88SwingVHighest: + case kMitsubishiHeavy88SwingVHigh: + case kMitsubishiHeavy88SwingVMiddle: + case kMitsubishiHeavy88SwingVLow: + case kMitsubishiHeavy88SwingVLowest: + newpos = pos; + break; + default: + newpos = kMitsubishiHeavy88SwingVOff; + } + remote_state[5] &= ~kMitsubishiHeavy88SwingVMaskByte5; + remote_state[5] |= (newpos & kMitsubishiHeavy88SwingVMaskByte5); + remote_state[7] &= ~kMitsubishiHeavy88SwingVMaskByte7; + remote_state[7] |= (newpos & kMitsubishiHeavy88SwingVMaskByte7); +} + +uint8_t IRMitsubishiHeavy88Ac::getSwingVertical(void) { + return (remote_state[5] & kMitsubishiHeavy88SwingVMaskByte5) | + (remote_state[7] & kMitsubishiHeavy88SwingVMaskByte7); +} + +void IRMitsubishiHeavy88Ac::setSwingHorizontal(const uint8_t pos) { + uint8_t newpos; + switch (pos) { + case kMitsubishiHeavy88SwingHAuto: + case kMitsubishiHeavy88SwingHLeftMax: + case kMitsubishiHeavy88SwingHLeft: + case kMitsubishiHeavy88SwingHMiddle: + case kMitsubishiHeavy88SwingHRight: + case kMitsubishiHeavy88SwingHRightMax: + case kMitsubishiHeavy88SwingHLeftRight: + case kMitsubishiHeavy88SwingHRightLeft: + case kMitsubishiHeavy88SwingH3D: + newpos = pos; + break; + default: + newpos = kMitsubishiHeavy88SwingHOff; + } + remote_state[5] &= ~kMitsubishiHeavy88SwingHMask; + remote_state[5] |= newpos; +} + +uint8_t IRMitsubishiHeavy88Ac::getSwingHorizontal(void) { + return remote_state[5] & kMitsubishiHeavy88SwingHMask; +} + +void IRMitsubishiHeavy88Ac::setTurbo(const bool on) { + if (on) + this->setFan(kMitsubishiHeavy88FanTurbo); + else if (this->getTurbo()) this->setFan(kMitsubishiHeavy88FanAuto); +} + +bool IRMitsubishiHeavy88Ac::getTurbo(void) { + return this->getFan() == kMitsubishiHeavy88FanTurbo; +} + +void IRMitsubishiHeavy88Ac::setEcono(const bool on) { + if (on) + this->setFan(kMitsubishiHeavy88FanEcono); + else if (this->getEcono()) this->setFan(kMitsubishiHeavy88FanAuto); +} + +bool IRMitsubishiHeavy88Ac::getEcono(void) { + return this->getFan() == kMitsubishiHeavy88FanEcono; +} + +void IRMitsubishiHeavy88Ac::set3D(const bool on) { + if (on) + this->setSwingHorizontal(kMitsubishiHeavy88SwingH3D); + else if (this->get3D()) + this->setSwingHorizontal(kMitsubishiHeavy88SwingHOff); +} + +bool IRMitsubishiHeavy88Ac::get3D(void) { + return this->getSwingHorizontal() == kMitsubishiHeavy88SwingH3D; +} + +void IRMitsubishiHeavy88Ac::setClean(const bool on) { + if (on) + remote_state[5] |= kMitsubishiHeavy88CleanBit; + else + remote_state[5] &= ~kMitsubishiHeavy88CleanBit; +} + +bool IRMitsubishiHeavy88Ac::getClean(void) { + return remote_state[5] & kMitsubishiHeavy88CleanBit; +} + +// Verify the given state has a ZJ-S signature. +bool IRMitsubishiHeavy88Ac::checkZjsSig(const uint8_t *state) { + for (uint8_t i = 0; i < kMitsubishiHeavySigLength; i++) + if (state[i] != kMitsubishiHeavyZjsSig[i]) return false; + return true; +} + +// Protocol technically has no checksum, but does has inverted byte pairs. +void IRMitsubishiHeavy88Ac::checksum(void) { + for (uint8_t i = kMitsubishiHeavySigLength - 2; + i < kMitsubishiHeavy88StateLength; + i += 2) { + remote_state[i + 1] = ~remote_state[i]; + } +} + +// Protocol technically has no checksum, but does has inverted byte pairs. +bool IRMitsubishiHeavy88Ac::validChecksum(const uint8_t *state, + const uint16_t length) { + return IRMitsubishiHeavy152Ac::validChecksum(state, length); +} + +// Convert a standard A/C mode into its native mode. +uint8_t IRMitsubishiHeavy88Ac::convertMode(const stdAc::opmode_t mode) { + return IRMitsubishiHeavy152Ac::convertMode(mode); +} + + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRMitsubishiHeavy88Ac::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + return kMitsubishiHeavy88FanEcono; // Assumes Econo is slower than Low. + case stdAc::fanspeed_t::kLow: + return kMitsubishiHeavy88FanLow; + case stdAc::fanspeed_t::kMedium: + return kMitsubishiHeavy88FanMed; + case stdAc::fanspeed_t::kHigh: + return kMitsubishiHeavy88FanHigh; + case stdAc::fanspeed_t::kMax: + return kMitsubishiHeavy88FanTurbo; + default: + return kMitsubishiHeavy88FanAuto; + } +} + +// Convert a standard A/C vertical swing into its native setting. +uint8_t IRMitsubishiHeavy88Ac::convertSwingV(const stdAc::swingv_t position) { + switch (position) { + case stdAc::swingv_t::kAuto: + return kMitsubishiHeavy88SwingVAuto; + case stdAc::swingv_t::kHighest: + return kMitsubishiHeavy88SwingVHighest; + case stdAc::swingv_t::kHigh: + return kMitsubishiHeavy88SwingVHigh; + case stdAc::swingv_t::kMiddle: + return kMitsubishiHeavy88SwingVMiddle; + case stdAc::swingv_t::kLow: + return kMitsubishiHeavy88SwingVLow; + case stdAc::swingv_t::kLowest: + return kMitsubishiHeavy88SwingVLowest; + default: + return kMitsubishiHeavy88SwingVOff; + } +} + +// Convert a standard A/C horizontal swing into its native setting. +uint8_t IRMitsubishiHeavy88Ac::convertSwingH(const stdAc::swingh_t position) { + switch (position) { + case stdAc::swingh_t::kAuto: + return kMitsubishiHeavy88SwingHAuto; + case stdAc::swingh_t::kLeftMax: + return kMitsubishiHeavy88SwingHLeftMax; + case stdAc::swingh_t::kLeft: + return kMitsubishiHeavy88SwingHLeft; + case stdAc::swingh_t::kMiddle: + return kMitsubishiHeavy88SwingHMiddle; + case stdAc::swingh_t::kRight: + return kMitsubishiHeavy88SwingHRight; + case stdAc::swingh_t::kRightMax: + return kMitsubishiHeavy88SwingHRightMax; + default: + return kMitsubishiHeavy88SwingHOff; + } +} + +// Convert the internal state into a human readable string. +#ifdef ARDUINO +String IRMitsubishiHeavy88Ac::toString(void) { + String result = ""; +#else +std::string IRMitsubishiHeavy88Ac::toString(void) { + std::string result = ""; +#endif // ARDUINO + result += F("Power: "); + result += (this->getPower() ? F("On") : F("Off")); + result += F(", Mode: "); + result += uint64ToString(this->getMode()); + switch (this->getMode()) { + case kMitsubishiHeavyAuto: + result += F(" (Auto)"); + break; + case kMitsubishiHeavyCool: + result += F(" (Cool)"); + break; + case kMitsubishiHeavyHeat: + result += F(" (Heat)"); + break; + case kMitsubishiHeavyDry: + result += F(" (Dry)"); + break; + case kMitsubishiHeavyFan: + result += F(" (Fan)"); + break; + default: + result += F(" (UNKNOWN)"); + } + result += F(", Temp: "); + result += uint64ToString(this->getTemp()) + 'C'; + result += F(", Fan: "); + result += uint64ToString(this->getFan()); + switch (this->getFan()) { + case kMitsubishiHeavy88FanAuto: + result += F(" (Auto)"); + break; + case kMitsubishiHeavy88FanHigh: + result += F(" (High)"); + break; + case kMitsubishiHeavy88FanLow: + result += F(" (Low)"); + break; + case kMitsubishiHeavy88FanMed: + result += F(" (Med)"); + break; + case kMitsubishiHeavy88FanEcono: + result += F(" (Econo)"); + break; + case kMitsubishiHeavy88FanTurbo: + result += F(" (Turbo)"); + break; + default: + result += F(" (UNKNOWN)"); + } + result += F(", Swing (V): "); + result += uint64ToString(this->getSwingVertical()); + switch (this->getSwingVertical()) { + case kMitsubishiHeavy88SwingVAuto: + result += F(" (Auto)"); + break; + case kMitsubishiHeavy88SwingVHighest: + result += F(" (Highest)"); + break; + case kMitsubishiHeavy88SwingVHigh: + result += F(" (High)"); + break; + case kMitsubishiHeavy88SwingVMiddle: + result += F(" (Middle)"); + break; + case kMitsubishiHeavy88SwingVLow: + result += F(" (Low)"); + break; + case kMitsubishiHeavy88SwingVLowest: + result += F(" (Lowest)"); + break; + case kMitsubishiHeavy88SwingVOff: + result += F(" (Off)"); + break; + default: + result += F(" (UNKNOWN)"); + } + result += F(", Swing (H): "); + result += uint64ToString(this->getSwingHorizontal()); + switch (this->getSwingHorizontal()) { + case kMitsubishiHeavy88SwingHAuto: + result += F(" (Auto)"); + break; + case kMitsubishiHeavy88SwingHLeftMax: + result += F(" (Max Left)"); + break; + case kMitsubishiHeavy88SwingHLeft: + result += F(" (Left)"); + break; + case kMitsubishiHeavy88SwingHMiddle: + result += F(" (Middle)"); + break; + case kMitsubishiHeavy88SwingHRight: + result += F(" (Right)"); + break; + case kMitsubishiHeavy88SwingHRightMax: + result += F(" (Max Right)"); + break; + case kMitsubishiHeavy88SwingHLeftRight: + result += F(" (Left Right)"); + break; + case kMitsubishiHeavy88SwingHRightLeft: + result += F(" (Right Left)"); + break; + case kMitsubishiHeavy88SwingH3D: + result += F(" (3D)"); + break; + case kMitsubishiHeavy88SwingHOff: + result += F(" (Off)"); + break; + default: + result += F(" (UNKNOWN)"); + } + result += F(", Turbo: "); + result += (this->getTurbo() ? F("On") : F("Off")); + result += F(", Econo: "); + result += (this->getEcono() ? F("On") : F("Off")); + result += F(", 3D: "); + result += (this->get3D() ? F("On") : F("Off")); + result += F(", Clean: "); + result += (this->getClean() ? F("On") : F("Off")); + return result; +} + +#if DECODE_MITSUBISHIHEAVY +// Decode the supplied MitsubishiHeavy message. +// +// Args: +// results: Ptr to the data to decode and where to store the decode result. +// nbits: The number of data bits to expect. +// Typically kMitsubishiHeavy88Bits or kMitsubishiHeavy152Bits. +// strict: Flag indicating if we should perform strict matching. +// Returns: +// boolean: True if it can decode it, false if it can't. +// +// Status: BETA / Appears to be working. Needs testing against a real device. +bool IRrecv::decodeMitsubishiHeavy(decode_results* results, + const uint16_t nbits, const bool strict) { + // Check if can possibly be a valid MitsubishiHeavy message. + if (results->rawlen < 2 * nbits + kHeader + kFooter - 1) return false; + if (strict) { + switch (nbits) { + case kMitsubishiHeavy88Bits: + case kMitsubishiHeavy152Bits: + break; + default: + return false; // Not what is expected + } + } + + uint16_t actualBits = 0; + uint16_t offset = kStartOffset; + match_result_t data_result; + + // Header + if (!matchMark(results->rawbuf[offset++], kMitsubishiHeavyHdrMark)) + return false; + if (!matchSpace(results->rawbuf[offset++], kMitsubishiHeavyHdrSpace)) + return false; + // Data + // Keep reading bytes until we either run out of section or state to fill. + for (uint16_t i = 0; + offset <= results->rawlen - 16 && actualBits < nbits; + i++, actualBits += 8, offset += data_result.used) { + data_result = matchData(&(results->rawbuf[offset]), 8, + kMitsubishiHeavyBitMark, kMitsubishiHeavyOneSpace, + kMitsubishiHeavyBitMark, kMitsubishiHeavyZeroSpace, + kTolerance, 0, false); + if (data_result.success == false) { + DPRINT("DEBUG: offset = "); + DPRINTLN(offset + data_result.used); + return false; // Fail + } + results->state[i] = data_result.data; + } + // Footer. + if (!matchMark(results->rawbuf[offset++], kMitsubishiHeavyBitMark)) + return false; + if (offset < results->rawlen && + !matchAtLeast(results->rawbuf[offset], kMitsubishiHeavyGap)) return false; + + // Compliance + if (actualBits < nbits) return false; + if (strict && actualBits != nbits) return false; // Not as we expected. + switch (actualBits) { + case kMitsubishiHeavy88Bits: + if (strict && !(IRMitsubishiHeavy88Ac::checkZjsSig(results->state) && + IRMitsubishiHeavy88Ac::validChecksum(results->state))) + return false; + results->decode_type = MITSUBISHI_HEAVY_88; + break; + case kMitsubishiHeavy152Bits: + if (strict && !(IRMitsubishiHeavy152Ac::checkZmsSig(results->state) && + IRMitsubishiHeavy152Ac::validChecksum(results->state))) + return false; + results->decode_type = MITSUBISHI_HEAVY_152; + break; + default: + return false; + } + + // Success + results->bits = actualBits; + // No need to record the state as we stored it as we decoded it. + // As we use result->state, we don't record value, address, or command as it + // is a union data type. + return true; +} +#endif // DECODE_MITSUBISHIHEAVY diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_MitsubishiHeavy.h b/lib/IRremoteESP8266-2.6.0/src/ir_MitsubishiHeavy.h new file mode 100644 index 000000000..bcd85c6e0 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/src/ir_MitsubishiHeavy.h @@ -0,0 +1,264 @@ +// Copyright 2019 David Conran + +#ifndef IR_MITSUBISHIHEAVY_H_ +#define IR_MITSUBISHIHEAVY_H_ + +#ifndef UNIT_TEST +#include +#else +#include +#endif +#include "IRremoteESP8266.h" +#include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif + +// Ref: +// https://github.com/markszabo/IRremoteESP8266/issues/660 +// https://github.com/ToniA/Raw-IR-decoder-for-Arduino/blob/master/MitsubishiHeavy.cpp +// https://github.com/ToniA/arduino-heatpumpir/blob/master/MitsubishiHeavyHeatpumpIR.cpp + +// Constants. +const uint8_t kMitsubishiHeavySigLength = 5; + + +// ZMS (152 bit) +const uint8_t kMitsubishiHeavyZmsSig[kMitsubishiHeavySigLength] = { + 0xAD, 0x51, 0x3C, 0xE5, 0x1A}; +// Byte[5] +const uint8_t kMitsubishiHeavyFilterBit = 0b01000000; +const uint8_t kMitsubishiHeavyCleanBit = 0b00100000; +const uint8_t kMitsubishiHeavyPowerBit = 0b00001000; // Byte 9 on ZJS +const uint8_t kMitsubishiHeavyModeMask = 0b00000111; // Byte 9 on ZJS +const uint8_t kMitsubishiHeavyAuto = 0; // 0b000 +const uint8_t kMitsubishiHeavyCool = 1; // 0b001 +const uint8_t kMitsubishiHeavyDry = 2; // 0b010 +const uint8_t kMitsubishiHeavyFan = 3; // 0b011 +const uint8_t kMitsubishiHeavyHeat = 4; // 0b100 +// Byte[7] +const uint8_t kMitsubishiHeavyTempMask = 0b00001111; +const uint8_t kMitsubishiHeavyMinTemp = 17; // 17C +const uint8_t kMitsubishiHeavyMaxTemp = 31; // 31C +// Byte[9] +const uint8_t kMitsubishiHeavyFanMask = 0b00001111; // ~Byte 7 on ZJS. +const uint8_t kMitsubishiHeavy152FanAuto = 0x0; // 0b0000 +const uint8_t kMitsubishiHeavy152FanLow = 0x1; // 0b0001 +const uint8_t kMitsubishiHeavy152FanMed = 0x2; // 0b0010 +const uint8_t kMitsubishiHeavy152FanHigh = 0x3; // 0b0011 +const uint8_t kMitsubishiHeavy152FanMax = 0x4; // 0b0100 +const uint8_t kMitsubishiHeavy152FanEcono = 0x6; // 0b0110 +const uint8_t kMitsubishiHeavy152FanTurbo = 0x8; // 0b1000 +// Byte[11] +const uint8_t kMitsubishiHeavy3DMask = 0b00010010; +const uint8_t kMitsubishiHeavy152SwingVMask = 0b11100000; +const uint8_t kMitsubishiHeavy152SwingVAuto = 0; // 0b000 +const uint8_t kMitsubishiHeavy152SwingVHighest = 1; // 0b001 +const uint8_t kMitsubishiHeavy152SwingVHigh = 2; // 0b010 +const uint8_t kMitsubishiHeavy152SwingVMiddle = 3; // 0b011 +const uint8_t kMitsubishiHeavy152SwingVLow = 4; // 0b100 +const uint8_t kMitsubishiHeavy152SwingVLowest = 5; // 0b101 +const uint8_t kMitsubishiHeavy152SwingVOff = 6; // 0b110 +// Byte[13] +const uint8_t kMitsubishiHeavy152SwingHMask = 0b00001111; +const uint8_t kMitsubishiHeavy152SwingHAuto = 0; // 0b0000 +const uint8_t kMitsubishiHeavy152SwingHLeftMax = 1; // 0b0001 +const uint8_t kMitsubishiHeavy152SwingHLeft = 2; // 0b0010 +const uint8_t kMitsubishiHeavy152SwingHMiddle = 3; // 0b0011 +const uint8_t kMitsubishiHeavy152SwingHRight = 4; // 0b0100 +const uint8_t kMitsubishiHeavy152SwingHRightMax = 5; // 0b0101 +const uint8_t kMitsubishiHeavy152SwingHRightLeft = 6; // 0b0110 +const uint8_t kMitsubishiHeavy152SwingHLeftRight = 7; // 0b0111 +const uint8_t kMitsubishiHeavy152SwingHOff = 8; // 0b1000 +// Byte[15] +const uint8_t kMitsubishiHeavyNightBit = 0b01000000; +const uint8_t kMitsubishiHeavySilentBit = 0b10000000; + + +// ZJS (88 bit) +const uint8_t kMitsubishiHeavyZjsSig[kMitsubishiHeavySigLength] = { + 0xAD, 0x51, 0x3C, 0xD9, 0x26}; +// Byte [5] +const uint8_t kMitsubishiHeavy88CleanBit = 0b00100000; +const uint8_t kMitsubishiHeavy88SwingHMask = 0b11001100; +const uint8_t kMitsubishiHeavy88SwingHAuto = 0x80; // 0b10000000 +const uint8_t kMitsubishiHeavy88SwingHLeftMax = 0x04; // 0b00000100 +const uint8_t kMitsubishiHeavy88SwingHLeft = 0x44; // 0b01000100 +const uint8_t kMitsubishiHeavy88SwingHMiddle = 0x84; // 0b10000100 +const uint8_t kMitsubishiHeavy88SwingHRight = 0xC4; // 0b11000100 +const uint8_t kMitsubishiHeavy88SwingHRightMax = 0x08; // 0b00001000 +const uint8_t kMitsubishiHeavy88SwingHRightLeft = 0x88; // 0b10001000 +const uint8_t kMitsubishiHeavy88SwingHLeftRight = 0x48; // 0b01001000 +const uint8_t kMitsubishiHeavy88SwingHOff = 0x00; // 0b00000000 +const uint8_t kMitsubishiHeavy88SwingH3D = 0xC8; // 0b11001000 +// Byte[7] +const uint8_t kMitsubishiHeavy88FanMask = 0b11100000; +const uint8_t kMitsubishiHeavy88FanAuto = 0; // 0b000 +const uint8_t kMitsubishiHeavy88FanLow = 2; // 0b010 +const uint8_t kMitsubishiHeavy88FanMed = 3; // 0b011 +const uint8_t kMitsubishiHeavy88FanHigh = 4; // 0b100 +const uint8_t kMitsubishiHeavy88FanTurbo = 6; // 0b110 +const uint8_t kMitsubishiHeavy88FanEcono = 7; // 0b111 +const uint8_t kMitsubishiHeavy88SwingVMaskByte5 = 0b00000010; +const uint8_t kMitsubishiHeavy88SwingVMaskByte7 = 0b00011000; +const uint8_t kMitsubishiHeavy88SwingVMask = + kMitsubishiHeavy88SwingVMaskByte5 | kMitsubishiHeavy88SwingVMaskByte7; + // i.e. 0b00011010 +const uint8_t kMitsubishiHeavy88SwingVAuto = 0b00010000; // 0x10 +const uint8_t kMitsubishiHeavy88SwingVHighest = 0b00011000; // 0x18 +const uint8_t kMitsubishiHeavy88SwingVHigh = 0b00000010; // 0x02 +const uint8_t kMitsubishiHeavy88SwingVMiddle = 0b00001010; // 0x0A +const uint8_t kMitsubishiHeavy88SwingVLow = 0b00010010; // 0x12 +const uint8_t kMitsubishiHeavy88SwingVLowest = 0b00011010; // 0x1A +const uint8_t kMitsubishiHeavy88SwingVOff = 0b00000000; // 0x00 +// Byte[9] is Power & Mode & Temp. + + +// Classes +class IRMitsubishiHeavy152Ac { + public: + explicit IRMitsubishiHeavy152Ac(const uint16_t pin); + + void stateReset(void); +#if SEND_MITSUBISHIHEAVY + void send(const uint16_t repeat = kMitsubishiHeavy152MinRepeat); +#endif // SEND_MITSUBISHIHEAVY + void begin(void); + void on(void); + void off(void); + + void setPower(const bool on); + bool getPower(void); + + void setTemp(const uint8_t temp); + uint8_t getTemp(void); + + void setFan(const uint8_t fan); + uint8_t getFan(void); + + void setMode(const uint8_t mode); + uint8_t getMode(void); + + void setSwingVertical(const uint8_t pos); + uint8_t getSwingVertical(void); + void setSwingHorizontal(const uint8_t pos); + uint8_t getSwingHorizontal(void); + + void setNight(const bool on); + bool getNight(void); + + void set3D(const bool on); + bool get3D(void); + + void setSilent(const bool on); + bool getSilent(void); + + void setFilter(const bool on); + bool getFilter(void); + + void setClean(const bool on); + bool getClean(void); + + void setTurbo(const bool on); + bool getTurbo(void); + + void setEcono(const bool on); + bool getEcono(void); + + uint8_t* getRaw(void); + void setRaw(const uint8_t* data); + + static bool checkZmsSig(const uint8_t *state); + static bool validChecksum( + const uint8_t *state, + const uint16_t length = kMitsubishiHeavy152StateLength); + static uint8_t convertMode(const stdAc::opmode_t mode); + static uint8_t convertFan(const stdAc::fanspeed_t speed); + static uint8_t convertSwingV(const stdAc::swingv_t position); + static uint8_t convertSwingH(const stdAc::swingh_t position); +#ifdef ARDUINO + String toString(void); +#else // ARDUINO + std::string toString(void); +#endif // ARDUINO +#ifndef UNIT_TEST + + private: + IRsend _irsend; +#else // UNIT_TEST + IRsendTest _irsend; +#endif // UNIT_TEST + // The state of the IR remote in IR code form. + uint8_t remote_state[kMitsubishiHeavy152StateLength]; + void checksum(); +}; + +class IRMitsubishiHeavy88Ac { + public: + explicit IRMitsubishiHeavy88Ac(const uint16_t pin); + + void stateReset(void); +#if SEND_MITSUBISHIHEAVY + void send(const uint16_t repeat = kMitsubishiHeavy88MinRepeat); +#endif // SEND_MITSUBISHIHEAVY + void begin(void); + void on(void); + void off(void); + + void setPower(const bool on); + bool getPower(void); + + void setTemp(const uint8_t temp); + uint8_t getTemp(void); + + void setFan(const uint8_t fan); + uint8_t getFan(void); + + void setMode(const uint8_t mode); + uint8_t getMode(void); + + void setSwingVertical(const uint8_t pos); + uint8_t getSwingVertical(void); + void setSwingHorizontal(const uint8_t pos); + uint8_t getSwingHorizontal(void); + + void setTurbo(const bool on); + bool getTurbo(void); + + void setEcono(const bool on); + bool getEcono(void); + + void set3D(const bool on); + bool get3D(void); + + void setClean(const bool on); + bool getClean(void); + + uint8_t* getRaw(void); + void setRaw(const uint8_t* data); + + static bool checkZjsSig(const uint8_t *state); + static bool validChecksum( + const uint8_t *state, + const uint16_t length = kMitsubishiHeavy88StateLength); + static uint8_t convertMode(const stdAc::opmode_t mode); + static uint8_t convertFan(const stdAc::fanspeed_t speed); + static uint8_t convertSwingV(const stdAc::swingv_t position); + static uint8_t convertSwingH(const stdAc::swingh_t position); +#ifdef ARDUINO + String toString(void); +#else // ARDUINO + std::string toString(void); +#endif // ARDUINO +#ifndef UNIT_TEST + + private: + IRsend _irsend; +#else // UNIT_TEST + IRsendTest _irsend; +#endif // UNIT_TEST + // The state of the IR remote in IR code form. + uint8_t remote_state[kMitsubishiHeavy152StateLength]; + void checksum(); +}; +#endif // IR_MITSUBISHIHEAVY_H_ diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_NEC.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_NEC.cpp similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_NEC.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_NEC.cpp diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_NEC.h b/lib/IRremoteESP8266-2.6.0/src/ir_NEC.h similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_NEC.h rename to lib/IRremoteESP8266-2.6.0/src/ir_NEC.h diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Nikai.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Nikai.cpp similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Nikai.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_Nikai.cpp diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Panasonic.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Panasonic.cpp similarity index 83% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Panasonic.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_Panasonic.cpp index e79b136a5..47aa51c96 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Panasonic.cpp +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Panasonic.cpp @@ -27,8 +27,8 @@ // Code by crankyoldgit // Panasonic A/C models supported: // A/C Series/models: -// JKE, LKE, DKE, CKP, & NKE series. (In theory) -// CS-YW9MKD (confirmed) +// JKE, LKE, DKE, CKP, RKR, & NKE series. (In theory) +// CS-YW9MKD, CS-Z9RKR (confirmed) // CS-ME14CKPG / CS-ME12CKPG / CS-ME10CKPG // A/C Remotes: // A75C3747 (confirmed) @@ -63,7 +63,7 @@ const uint32_t kPanasonicMinGap = kPanasonicMinGapTicks * kPanasonicTick; const uint16_t kPanasonicAcSectionGap = 10000; const uint16_t kPanasonicAcSection1Length = 8; -const uint32_t kPanasonicAcMessageGap = 100000; // A complete guess. +const uint32_t kPanasonicAcMessageGap = kDefaultMessageGap; // Just a guess. #if (SEND_PANASONIC || SEND_DENON) // Send a Panasonic formatted message. @@ -211,7 +211,7 @@ bool IRrecv::decodePanasonic(decode_results *results, uint16_t nbits, //: // Panasonic A/C models supported: // A/C Series/models: -// JKE, LKE, DKE, & NKE series. +// JKE, LKE, DKE, CKP, RKR, & NKE series. // CS-YW9MKD // A/C Remotes: // A75C3747 @@ -268,9 +268,9 @@ void IRPanasonicAc::fixChecksum(const uint16_t length) { } #if SEND_PANASONIC_AC -void IRPanasonicAc::send() { +void IRPanasonicAc::send(const uint16_t repeat) { fixChecksum(); - _irsend.sendPanasonicAC(remote_state); + _irsend.sendPanasonicAC(remote_state, kPanasonicAcStateLength, repeat); } #endif // SEND_PANASONIC_AC @@ -281,6 +281,7 @@ void IRPanasonicAc::setModel(const panasonic_ac_remote_model_t model) { case kPanasonicLke: case kPanasonicNke: case kPanasonicCkp: + case kPanasonicRkr: break; default: // Only proceed if we know what to do. return; @@ -311,12 +312,17 @@ void IRPanasonicAc::setModel(const panasonic_ac_remote_model_t model) { case kPanasonicCkp: remote_state[21] |= 0x10; remote_state[23] = 0x01; + break; + case kPanasonicRkr: + remote_state[13] |= 0x08; + remote_state[23] = 0x89; default: break; } } panasonic_ac_remote_model_t IRPanasonicAc::getModel() { + if (remote_state[23] == 0x89) return kPanasonicRkr; if (remote_state[17] == 0x00) { if ((remote_state[21] & 0x10) && (remote_state[23] & 0x01)) return kPanasonicCkp; @@ -438,6 +444,7 @@ void IRPanasonicAc::setSwingHorizontal(const uint8_t desired_direction) { uint8_t direction = desired_direction; switch (getModel()) { case kPanasonicDke: + case kPanasonicRkr: break; case kPanasonicNke: case kPanasonicLke: @@ -460,18 +467,25 @@ uint8_t IRPanasonicAc::getFan() { } bool IRPanasonicAc::getQuiet() { - if (getModel() == kPanasonicCkp) - return remote_state[21] & kPanasonicAcQuietCkp; - else - return remote_state[21] & kPanasonicAcQuiet; + switch (getModel()) { + case kPanasonicRkr: + case kPanasonicCkp: + return remote_state[21] & kPanasonicAcQuietCkp; + default: + return remote_state[21] & kPanasonicAcQuiet; + } } void IRPanasonicAc::setQuiet(const bool state) { uint8_t quiet; - if (getModel() == kPanasonicCkp) - quiet = kPanasonicAcQuietCkp; - else - quiet = kPanasonicAcQuiet; + switch (getModel()) { + case kPanasonicRkr: + case kPanasonicCkp: + quiet = kPanasonicAcQuietCkp; + break; + default: + quiet = kPanasonicAcQuiet; + } if (state) { setPowerful(false); // Powerful is mutually exclusive. @@ -482,18 +496,25 @@ void IRPanasonicAc::setQuiet(const bool state) { } bool IRPanasonicAc::getPowerful() { - if (getModel() == kPanasonicCkp) - return remote_state[21] & kPanasonicAcPowerfulCkp; - else - return remote_state[21] & kPanasonicAcPowerful; + switch (getModel()) { + case kPanasonicRkr: + case kPanasonicCkp: + return remote_state[21] & kPanasonicAcPowerfulCkp; + default: + return remote_state[21] & kPanasonicAcPowerful; + } } void IRPanasonicAc::setPowerful(const bool state) { uint8_t powerful; - if (getModel() == kPanasonicCkp) - powerful = kPanasonicAcPowerfulCkp; - else - powerful = kPanasonicAcPowerful; + switch (getModel()) { + case kPanasonicRkr: + case kPanasonicCkp: + powerful = kPanasonicAcPowerfulCkp; + break; + default: + powerful = kPanasonicAcPowerful; + } if (state) { setQuiet(false); // Quiet is mutually exclusive. @@ -591,12 +612,79 @@ String IRPanasonicAc::timeToString(const uint16_t mins_since_midnight) { std::string IRPanasonicAc::timeToString(const uint16_t mins_since_midnight) { std::string result = ""; #endif // ARDUINO - result += uint64ToString(mins_since_midnight / 60) + ":"; + result += uint64ToString(mins_since_midnight / 60) + ':'; uint8_t mins = mins_since_midnight % 60; - if (mins < 10) result += "0"; // Zero pad the minutes. + if (mins < 10) result += '0'; // Zero pad the minutes. return result + uint64ToString(mins); } +// Convert a standard A/C mode into its native mode. +uint8_t IRPanasonicAc::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kPanasonicAcCool; + case stdAc::opmode_t::kHeat: + return kPanasonicAcHeat; + case stdAc::opmode_t::kDry: + return kPanasonicAcDry; + case stdAc::opmode_t::kFan: + return kPanasonicAcFan; + default: + return kPanasonicAcAuto; + } +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRPanasonicAc::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + return kPanasonicAcFanMin; + case stdAc::fanspeed_t::kLow: + return kPanasonicAcFanMin + 1; + case stdAc::fanspeed_t::kMedium: + return kPanasonicAcFanMin + 2; + case stdAc::fanspeed_t::kHigh: + return kPanasonicAcFanMin + 3; + case stdAc::fanspeed_t::kMax: + return kPanasonicAcFanMax; + default: + return kPanasonicAcFanAuto; + } +} + +// Convert a standard A/C vertical swing into its native setting. +uint8_t IRPanasonicAc::convertSwingV(const stdAc::swingv_t position) { + switch (position) { + case stdAc::swingv_t::kHighest: + case stdAc::swingv_t::kHigh: + case stdAc::swingv_t::kMiddle: + return kPanasonicAcSwingVUp; + case stdAc::swingv_t::kLow: + case stdAc::swingv_t::kLowest: + return kPanasonicAcSwingVDown; + default: + return kPanasonicAcSwingVAuto; + } +} + +// Convert a standard A/C horizontal swing into its native setting. +uint8_t IRPanasonicAc::convertSwingH(const stdAc::swingh_t position) { + switch (position) { + case stdAc::swingh_t::kLeftMax: + return kPanasonicAcSwingHFullLeft; + case stdAc::swingh_t::kLeft: + return kPanasonicAcSwingHLeft; + case stdAc::swingh_t::kMiddle: + return kPanasonicAcSwingHMiddle; + case stdAc::swingh_t::kRight: + return kPanasonicAcSwingHRight; + case stdAc::swingh_t::kRightMax: + return kPanasonicAcSwingHFullRight; + default: + return kPanasonicAcSwingHAuto; + } +} + // Convert the internal state into a human readable string. #ifdef ARDUINO String IRPanasonicAc::toString() { @@ -605,84 +693,92 @@ String IRPanasonicAc::toString() { std::string IRPanasonicAc::toString() { std::string result = ""; #endif // ARDUINO - result += "Model: " + uint64ToString(getModel()); + result += F("Model: "); + result += uint64ToString(getModel()); switch (getModel()) { case kPanasonicDke: - result += " (DKE)"; + result += F(" (DKE)"); break; case kPanasonicJke: - result += " (JKE)"; + result += F(" (JKE)"); break; case kPanasonicNke: - result += " (NKE)"; + result += F(" (NKE)"); break; case kPanasonicLke: - result += " (LKE)"; + result += F(" (LKE)"); break; case kPanasonicCkp: - result += " (CKP)"; + result += F(" (CKP)"); + break; + case kPanasonicRkr: + result += F(" (RKR)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); } - result += ", Power: "; + result += F(", Power: "); if (getPower()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Mode: " + uint64ToString(getMode()); + result += F("Off"); + result += F(", Mode: "); + result += uint64ToString(getMode()); switch (getMode()) { case kPanasonicAcAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kPanasonicAcCool: - result += " (COOL)"; + result += F(" (COOL)"); break; case kPanasonicAcHeat: - result += " (HEAT)"; + result += F(" (HEAT)"); break; case kPanasonicAcDry: - result += " (DRY)"; + result += F(" (DRY)"); break; case kPanasonicAcFan: - result += " (FAN)"; + result += F(" (FAN)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); } - result += ", Temp: " + uint64ToString(getTemp()) + "C"; - result += ", Fan: " + uint64ToString(getFan()); + result += F(", Temp: "); + result += uint64ToString(getTemp()); + result += F("C, Fan: "); + result += uint64ToString(getFan()); switch (getFan()) { case kPanasonicAcFanAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kPanasonicAcFanMax: - result += " (MAX)"; + result += F(" (MAX)"); break; case kPanasonicAcFanMin: - result += " (MIN)"; + result += F(" (MIN)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); break; } - result += ", Swing (Vertical): " + uint64ToString(getSwingVertical()); + result += F(", Swing (Vertical): "); + result += uint64ToString(getSwingVertical()); switch (getSwingVertical()) { case kPanasonicAcSwingVAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kPanasonicAcSwingVUp: - result += " (Full Up)"; + result += F(" (Full Up)"); break; case kPanasonicAcSwingVDown: - result += " (Full Down)"; + result += F(" (Full Down)"); break; case 2: case 3: case 4: break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); break; } switch (getModel()) { @@ -690,52 +786,54 @@ std::string IRPanasonicAc::toString() { case kPanasonicCkp: break; // No Horizontal Swing support. default: - result += ", Swing (Horizontal): " + uint64ToString(getSwingHorizontal()); + result += F(", Swing (Horizontal): "); + result += uint64ToString(getSwingHorizontal()); switch (getSwingHorizontal()) { case kPanasonicAcSwingHAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kPanasonicAcSwingHFullLeft: - result += " (Full Left)"; + result += F(" (Full Left)"); break; case kPanasonicAcSwingHLeft: - result += " (Left)"; + result += F(" (Left)"); break; case kPanasonicAcSwingHMiddle: - result += " (Middle)"; + result += F(" (Middle)"); break; case kPanasonicAcSwingHFullRight: - result += " (Full Right)"; + result += F(" (Full Right)"); break; case kPanasonicAcSwingHRight: - result += " (Right)"; + result += F(" (Right)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); break; } } - result += ", Quiet: "; + result += F(", Quiet: "); if (getQuiet()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Powerful: "; + result += F("Off"); + result += F(", Powerful: "); if (getPowerful()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Clock: " + timeToString(getClock()); - result += ", On Timer: "; + result += F("Off"); + result += F(", Clock: "); + result += timeToString(getClock()); + result += F(", On Timer: "); if (isOnTimerEnabled()) result += timeToString(getOnTimer()); else - result += "Off"; - result += ", Off Timer: "; + result += F("Off"); + result += F(", Off Timer: "); if (isOffTimerEnabled()) result += timeToString(getOffTimer()); else - result += "Off"; + result += F("Off"); return result; } diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Panasonic.h b/lib/IRremoteESP8266-2.6.0/src/ir_Panasonic.h similarity index 91% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Panasonic.h rename to lib/IRremoteESP8266-2.6.0/src/ir_Panasonic.h index 762631fe7..1a7b4e114 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Panasonic.h +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Panasonic.h @@ -12,6 +12,9 @@ #endif #include "IRremoteESP8266.h" #include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif // PPPP AAA N N AAA SSSS OOO N N IIIII CCCC // P P A A NN N A A S O O NN N I C @@ -43,7 +46,7 @@ const uint8_t kPanasonicAcMaxTemp = 30; // Celsius const uint8_t kPanasonicAcFanModeTemp = 27; // Celsius const uint8_t kPanasonicAcQuiet = 1; // 0b1 const uint8_t kPanasonicAcPowerful = 0x20; // 0b100000 -// CKP models have Powerful and Quiet bits swapped. +// CKP & RKR models have Powerful and Quiet bits swapped. const uint8_t kPanasonicAcQuietCkp = 0x20; // 0b100000 const uint8_t kPanasonicAcPowerfulCkp = 1; // 0b1 const uint8_t kPanasonicAcSwingVAuto = 0xF; @@ -73,6 +76,7 @@ enum panasonic_ac_remote_model_t { kPanasonicDke = 3, kPanasonicJke = 4, kPanasonicCkp = 5, + kPanasonicRkr = 6, }; class IRPanasonicAc { @@ -81,7 +85,7 @@ class IRPanasonicAc { void stateReset(); #if SEND_PANASONIC - void send(); + void send(const uint16_t repeat = kPanasonicAcDefaultRepeat); #endif // SEND_PANASONIC void begin(); void on(); @@ -122,6 +126,10 @@ class IRPanasonicAc { const bool enable = true); void cancelOffTimer(); bool isOffTimerEnabled(); + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); + uint8_t convertSwingV(const stdAc::swingv_t position); + uint8_t convertSwingH(const stdAc::swingh_t position); #ifdef ARDUINO String toString(); static String timeToString(const uint16_t mins_since_midnight); @@ -132,6 +140,9 @@ class IRPanasonicAc { #ifndef UNIT_TEST private: + IRsend _irsend; +#else + IRsendTest _irsend; #endif uint8_t remote_state[kPanasonicAcStateLength]; uint8_t _swingh; @@ -139,7 +150,6 @@ class IRPanasonicAc { void fixChecksum(const uint16_t length = kPanasonicAcStateLength); static uint8_t calcChecksum(const uint8_t *state, const uint16_t length = kPanasonicAcStateLength); - IRsend _irsend; }; #endif // IR_PANASONIC_H_ diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Pioneer.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Pioneer.cpp similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Pioneer.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_Pioneer.cpp diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Pronto.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Pronto.cpp similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Pronto.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_Pronto.cpp diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_RC5_RC6.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_RC5_RC6.cpp similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_RC5_RC6.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_RC5_RC6.cpp diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_RCMM.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_RCMM.cpp similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_RCMM.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_RCMM.cpp diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Samsung.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Samsung.cpp similarity index 66% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Samsung.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_Samsung.cpp index d943f8cf9..7e54d17df 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Samsung.cpp +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Samsung.cpp @@ -1,5 +1,5 @@ // Copyright 2009 Ken Shirriff -// Copyright 2017 David Conran +// Copyright 2017, 2018, 2019 David Conran #include "ir_Samsung.h" #include @@ -168,6 +168,127 @@ bool IRrecv::decodeSAMSUNG(decode_results *results, uint16_t nbits, } #endif +#if SEND_SAMSUNG36 +// Send a Samsung 36-bit formatted message. +// +// Args: +// data: The message to be sent. +// nbits: The bit size of the message being sent. typically kSamsung36Bits. +// repeat: The number of times the message is to be repeated. +// +// Status: Alpha / Experimental. +// +// Note: +// Protocol is used by Samsung Bluray Remote: ak59-00167a +// +// Ref: +// https://github.com/markszabo/IRremoteESP8266/issues/621 +void IRsend::sendSamsung36(const uint64_t data, const uint16_t nbits, + const uint16_t repeat) { + if (nbits < 16) return; // To small to send. + for (uint16_t r = 0; r <= repeat; r++) { + // Block #1 (16 bits) + sendGeneric(kSamsungHdrMark, kSamsungHdrSpace, + kSamsungBitMark, kSamsungOneSpace, + kSamsungBitMark, kSamsungZeroSpace, + kSamsungBitMark, kSamsungHdrSpace, + data >> (nbits - 16), 16, 38, true, 0, kDutyDefault); + // Block #2 (The rest, typically 20 bits) + sendGeneric(0, 0, // No header + kSamsungBitMark, kSamsungOneSpace, + kSamsungBitMark, kSamsungZeroSpace, + kSamsungBitMark, kSamsungMinGap, // Gap is just a guess. + // Mask off the rest of the bits. + data & ((1ULL << (nbits - 16)) - 1), + nbits - 16, 38, true, 0, kDutyDefault); + } +} +#endif // SEND_SAMSUNG36 + +#if DECODE_SAMSUNG36 +// Decode the supplied Samsung36 message. +// +// Args: +// results: Ptr to the data to decode and where to store the decode result. +// nbits: Nr. of bits to expect in the data portion. +// Typically kSamsung36Bits. +// strict: Flag to indicate if we strictly adhere to the specification. +// Returns: +// boolean: True if it can decode it, false if it can't. +// +// Status: Alpha / Experimental +// +// Note: +// Protocol is used by Samsung Bluray Remote: ak59-00167a +// +// Ref: +// https://github.com/markszabo/IRremoteESP8266/issues/621 +bool IRrecv::decodeSamsung36(decode_results *results, const uint16_t nbits, + const bool strict) { + if (results->rawlen < 2 * nbits + kHeader + kFooter * 2 - 1) + return false; // Can't possibly be a valid Samsung message. + // We need to be looking for > 16 bits to make sense. + if (nbits <= 16) return false; + if (strict && nbits != kSamsung36Bits) + return false; // We expect nbits to be 36 bits of message. + + uint64_t data = 0; + uint16_t offset = kStartOffset; + + // Header + if (!matchMark(results->rawbuf[offset], kSamsungHdrMark)) return false; + // Calculate how long the common tick time is based on the header mark. + uint32_t m_tick = results->rawbuf[offset++] * kRawTick / kSamsungHdrMarkTicks; + if (!matchSpace(results->rawbuf[offset], kSamsungHdrSpace)) return false; + // Calculate how long the common tick time is based on the header space. + uint32_t s_tick = + results->rawbuf[offset++] * kRawTick / kSamsungHdrSpaceTicks; + // Data (Block #1) + match_result_t data_result = + matchData(&(results->rawbuf[offset]), 16, + kSamsungBitMarkTicks * m_tick, kSamsungOneSpaceTicks * s_tick, + kSamsungBitMarkTicks * m_tick, kSamsungZeroSpaceTicks * s_tick); + if (data_result.success == false) return false; + data = data_result.data; + offset += data_result.used; + uint16_t bitsSoFar = data_result.used / 2; + // Footer (Block #1) + if (!matchMark(results->rawbuf[offset++], kSamsungBitMarkTicks * m_tick)) + return false; + if (!matchSpace(results->rawbuf[offset++], kSamsungHdrSpaceTicks * s_tick)) + return false; + // Data (Block #2) + data_result = matchData(&(results->rawbuf[offset]), + nbits - 16, + kSamsungBitMarkTicks * m_tick, + kSamsungOneSpaceTicks * s_tick, + kSamsungBitMarkTicks * m_tick, + kSamsungZeroSpaceTicks * s_tick); + if (data_result.success == false) return false; + data <<= (nbits - 16); + data += data_result.data; + offset += data_result.used; + bitsSoFar += data_result.used / 2; + // Footer (Block #2) + if (!matchMark(results->rawbuf[offset++], kSamsungBitMarkTicks * m_tick)) + return false; + if (offset < results->rawlen && + !matchAtLeast(results->rawbuf[offset], kSamsungMinGapTicks * s_tick)) + return false; + + // Compliance + if (nbits != bitsSoFar) return false; + + // Success + results->bits = bitsSoFar; + results->value = data; + results->decode_type = SAMSUNG36; + results->command = data & ((1ULL << (nbits - 16)) - 1); + results->address = data >> (nbits - 16); + return true; +} +#endif // DECODE_SAMSUNG36 + #if SEND_SAMSUNG_AC // Send a Samsung A/C message. // @@ -180,7 +301,8 @@ bool IRrecv::decodeSAMSUNG(decode_results *results, uint16_t nbits, // // Ref: // https://github.com/markszabo/IRremoteESP8266/issues/505 -void IRsend::sendSamsungAC(uint8_t data[], uint16_t nbytes, uint16_t repeat) { +void IRsend::sendSamsungAC(const uint8_t data[], const uint16_t nbytes, + const uint16_t repeat) { if (nbytes < kSamsungAcStateLength && nbytes % kSamsungACSectionLength) return; // Not an appropriate number of bytes to send a proper message. @@ -226,53 +348,85 @@ void IRSamsungAc::begin() { _irsend.begin(); } uint8_t IRSamsungAc::calcChecksum(const uint8_t state[], const uint16_t length) { uint8_t sum = 0; - uint8_t currentbyte; // Safety check so we don't go outside the array. - if (length <= 5) return 255; + if (length < 7) return 255; // Shamelessly inspired by: // https://github.com/adafruit/Raw-IR-decoder-for-Arduino/pull/3/files // Count most of the '1' bits after the checksum location. - for (uint8_t i = length - 5; i < length - 1; i++) { - currentbyte = state[i]; - if (i == length - 5) currentbyte = state[length - 5] & 0b11111110; - for (; currentbyte; currentbyte >>= 1) - if (currentbyte & 1) sum++; - } + sum += countBits(state[length - 7], 8); + sum -= countBits(state[length - 6] & 0xF, 8); + sum += countBits(state[length - 5] & 0b11111110, 8); + sum += countBits(state + length - 4, 3); return (28 - sum) & 0xF; } bool IRSamsungAc::validChecksum(const uint8_t state[], const uint16_t length) { - if (length <= 5) return true; // No checksum to compare with. Assume okay. - return (state[length - 6] >> 4) == calcChecksum(state, length); + if (length < kSamsungAcStateLength) + return true; // No checksum to compare with. Assume okay. + uint8_t offset = 0; + if (length >= kSamsungAcExtendedStateLength) offset = 7; + return ((state[length - 6] >> 4) == calcChecksum(state, length) && + (state[length - (13 + offset)] >> 4) == calcChecksum(state, length - + (7 + offset))); } // Update the checksum for the internal state. void IRSamsungAc::checksum(uint16_t length) { - if (length < 9) return; + if (length < 13) return; remote_state[length - 6] &= 0x0F; remote_state[length - 6] |= (calcChecksum(remote_state, length) << 4); + remote_state[length - 13] &= 0x0F; + remote_state[length - 13] |= (calcChecksum(remote_state, length - 7) << 4); } #if SEND_SAMSUNG_AC -void IRSamsungAc::send(const bool calcchecksum) { +// Use for most function/mode/settings changes to the unit. +// i.e. When the device is already running. +void IRSamsungAc::send(const uint16_t repeat, const bool calcchecksum) { if (calcchecksum) checksum(); - _irsend.sendSamsungAC(remote_state); + _irsend.sendSamsungAC(remote_state, kSamsungAcStateLength, repeat); } -#endif // SEND_SAMSUNG_AC -#if SEND_SAMSUNG_AC -void IRSamsungAc::sendExtended(const bool calcchecksum) { +// Use this for when you need to power on/off the device. +// Samsung A/C requires an extended length message when you want to +// change the power operating mode of the A/C unit. +void IRSamsungAc::sendExtended(const uint16_t repeat, const bool calcchecksum) { if (calcchecksum) checksum(); uint8_t extended_state[kSamsungAcExtendedStateLength] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xD2, 0x0F, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0xD2, 0x0F, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; // Copy/convert the internal state to an extended state. for (uint16_t i = 0; i < kSamsungACSectionLength; i++) extended_state[i] = remote_state[i]; for (uint16_t i = kSamsungACSectionLength; i < kSamsungAcStateLength; i++) extended_state[i + kSamsungACSectionLength] = remote_state[i]; + // extended_state[8] seems special. This is a guess on how to calculate it. + extended_state[8] = (extended_state[1] & 0x9F) | 0x40; // Send it. - _irsend.sendSamsungAC(extended_state, kSamsungAcExtendedStateLength); + _irsend.sendSamsungAC(extended_state, kSamsungAcExtendedStateLength, repeat); +} + +// Send the special extended "On" message as the library can't seem to reproduce +// this message automatically. +// See: https://github.com/markszabo/IRremoteESP8266/issues/604#issuecomment-475020036 +void IRSamsungAc::sendOn(const uint16_t repeat) { + const uint8_t extended_state[21] = { + 0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0, + 0x01, 0xD2, 0x0F, 0x00, 0x00, 0x00, 0x00, + 0x01, 0xE2, 0xFE, 0x71, 0x80, 0x11, 0xF0}; + _irsend.sendSamsungAC(extended_state, kSamsungAcExtendedStateLength, repeat); +} + +// Send the special extended "Off" message as the library can't seem to +// reproduce this message automatically. +// See: https://github.com/markszabo/IRremoteESP8266/issues/604#issuecomment-475020036 +void IRSamsungAc::sendOff(const uint16_t repeat) { + const uint8_t extended_state[21] = { + 0x02, 0xB2, 0x0F, 0x00, 0x00, 0x00, 0xC0, + 0x01, 0xD2, 0x0F, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x02, 0xFF, 0x71, 0x80, 0x11, 0xC0}; + _irsend.sendSamsungAC(extended_state, kSamsungAcExtendedStateLength, repeat); } #endif // SEND_SAMSUNG_AC @@ -423,6 +577,39 @@ void IRSamsungAc::setQuiet(const bool state) { } } +// Convert a standard A/C mode into its native mode. +uint8_t IRSamsungAc::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kSamsungAcCool; + case stdAc::opmode_t::kHeat: + return kSamsungAcHeat; + case stdAc::opmode_t::kDry: + return kSamsungAcDry; + case stdAc::opmode_t::kFan: + return kSamsungAcFan; + default: + return kSamsungAcAuto; + } +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRSamsungAc::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: + return kSamsungAcFanLow; + case stdAc::fanspeed_t::kMedium: + return kSamsungAcFanMed; + case stdAc::fanspeed_t::kHigh: + return kSamsungAcFanHigh; + case stdAc::fanspeed_t::kMax: + return kSamsungAcFanTurbo; + default: + return kSamsungAcFanAuto; + } +} + // Convert the internal state into a human readable string. #ifdef ARDUINO String IRSamsungAc::toString() { @@ -431,74 +618,77 @@ String IRSamsungAc::toString() { std::string IRSamsungAc::toString() { std::string result = ""; #endif // ARDUINO - result += "Power: "; + result += F("Power: "); if (getPower()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Mode: " + uint64ToString(getMode()); + result += F("Off"); + result += F(", Mode: "); + result += uint64ToString(getMode()); switch (getMode()) { case kSamsungAcAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kSamsungAcCool: - result += " (COOL)"; + result += F(" (COOL)"); break; case kSamsungAcHeat: - result += " (HEAT)"; + result += F(" (HEAT)"); break; case kSamsungAcDry: - result += " (DRY)"; + result += F(" (DRY)"); break; case kSamsungAcFan: - result += " (FAN)"; + result += F(" (FAN)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); } - result += ", Temp: " + uint64ToString(getTemp()) + "C"; - result += ", Fan: " + uint64ToString(getFan()); + result += F(", Temp: "); + result += uint64ToString(getTemp()); + result += F("C, Fan: "); + result += uint64ToString(getFan()); switch (getFan()) { case kSamsungAcFanAuto: case kSamsungAcFanAuto2: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kSamsungAcFanLow: - result += " (LOW)"; + result += F(" (LOW)"); break; case kSamsungAcFanMed: - result += " (MED)"; + result += F(" (MED)"); break; case kSamsungAcFanHigh: - result += " (HIGH)"; + result += F(" (HIGH)"); break; case kSamsungAcFanTurbo: - result += " (TURBO)"; + result += F(" (TURBO)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); break; } - result += ", Swing: "; + result += F(", Swing: "); if (getSwing()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Beep: "; + result += F("Off"); + result += F(", Beep: "); if (getBeep()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Clean: "; + result += F("Off"); + result += F(", Clean: "); if (getBeep()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Quiet: "; + result += F("Off"); + result += F(", Quiet: "); if (getQuiet()) - result += "On"; + result += F("On"); else - result += "Off"; + result += F("Off"); return result; } @@ -571,7 +761,6 @@ bool IRrecv::decodeSamsungAC(decode_results *results, uint16_t nbits, // Is the signature correct? DPRINTLN("DEBUG: Checking signature."); if (results->state[0] != 0x02 || results->state[2] != 0x0F) return false; - if (results->state[1] != 0x92 && results->state[1] != 0xB2) return false; if (strict) { // Is the checksum valid? if (!IRSamsungAc::validChecksum(results->state, nbits / 8)) { diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Samsung.h b/lib/IRremoteESP8266-2.6.0/src/ir_Samsung.h similarity index 84% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Samsung.h rename to lib/IRremoteESP8266-2.6.0/src/ir_Samsung.h index f80b47d20..9df427c6f 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Samsung.h +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Samsung.h @@ -14,6 +14,9 @@ #endif #include "IRremoteESP8266.h" #include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif // SSSS AAA MMM SSSS U U N N GGGG // S A A M M M S U U NN N G @@ -62,8 +65,12 @@ class IRSamsungAc { void stateReset(); #if SEND_SAMSUNG_AC - void send(const bool calcchecksum = true); - void sendExtended(const bool calcchecksum = true); + void send(const uint16_t repeat = kSamsungAcDefaultRepeat, + const bool calcchecksum = true); + void sendExtended(const uint16_t repeat = kSamsungAcDefaultRepeat, + const bool calcchecksum = true); + void sendOn(const uint16_t repeat = kSamsungAcDefaultRepeat); + void sendOff(const uint16_t repeat = kSamsungAcDefaultRepeat); #endif // SEND_SAMSUNG_AC void begin(); void on(); @@ -91,17 +98,23 @@ class IRSamsungAc { const uint16_t length = kSamsungAcStateLength); static uint8_t calcChecksum(const uint8_t state[], const uint16_t length = kSamsungAcStateLength); + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); #ifdef ARDUINO String toString(); #else std::string toString(); #endif +#ifndef UNIT_TEST private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif // The state of the IR remote in IR code form. uint8_t remote_state[kSamsungAcExtendedStateLength]; void checksum(const uint16_t length = kSamsungAcStateLength); - IRsend _irsend; }; #endif // IR_SAMSUNG_H_ diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Sanyo.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Sanyo.cpp similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Sanyo.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_Sanyo.cpp diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Sharp.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Sharp.cpp similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Sharp.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_Sharp.cpp diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Sherwood.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Sherwood.cpp similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Sherwood.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_Sherwood.cpp diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Sony.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Sony.cpp similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Sony.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_Sony.cpp diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Tcl.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Tcl.cpp new file mode 100644 index 000000000..79fb23cf1 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Tcl.cpp @@ -0,0 +1,420 @@ +// Copyright 2019 David Conran + +#include "ir_Tcl.h" +#include +#ifndef ARDUINO +#include +#endif +#include "IRremoteESP8266.h" +#include "IRutils.h" + +// Constants + + +#if SEND_TCL112AC +void IRsend::sendTcl112Ac(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + sendGeneric(kTcl112AcHdrMark, kTcl112AcHdrSpace, + kTcl112AcBitMark, kTcl112AcOneSpace, + kTcl112AcBitMark, kTcl112AcZeroSpace, + kTcl112AcBitMark, kTcl112AcGap, + data, nbytes, 38000, false, repeat, 50); +} +#endif // SEND_TCL112AC + +IRTcl112Ac::IRTcl112Ac(uint16_t pin) : _irsend(pin) { stateReset(); } + +void IRTcl112Ac::begin() { this->_irsend.begin(); } + +#if SEND_TCL112AC +void IRTcl112Ac::send(const uint16_t repeat) { + this->checksum(); + this->_irsend.sendTcl112Ac(remote_state, kTcl112AcStateLength, repeat); +} +#endif // SEND_TCL112AC + +// Calculate the checksum for a given array. +// Args: +// state: The array to calculate the checksum over. +// length: The size of the array. +// Returns: +// The 8 bit checksum value. +uint8_t IRTcl112Ac::calcChecksum(uint8_t state[], + const uint16_t length) { + if (length) + return sumBytes(state, length - 1); + else + return 0; +} + +// Calculate & set the checksum for the current internal state of the remote. +void IRTcl112Ac::checksum(const uint16_t length) { + // Stored the checksum value in the last byte. + if (length > 1) + remote_state[length - 1] = calcChecksum(remote_state, length); +} + +// Verify the checksum is valid for a given state. +// Args: +// state: The array to verify the checksum of. +// length: The size of the state. +// Returns: +// A boolean. +bool IRTcl112Ac::validChecksum(uint8_t state[], const uint16_t length) { + return (length > 1 && state[length - 1] == calcChecksum(state, length)); +} + +void IRTcl112Ac::stateReset() { + for (uint8_t i = 0; i < kTcl112AcStateLength; i++) + remote_state[i] = 0x0; + // A known good state. (On, Cool, 24C) + remote_state[0] = 0x23; + remote_state[1] = 0xCB; + remote_state[2] = 0x26; + remote_state[3] = 0x01; + remote_state[5] = 0x24; + remote_state[6] = 0x03; + remote_state[7] = 0x07; + remote_state[8] = 0x40; + remote_state[13] = 0x03; +} + +uint8_t* IRTcl112Ac::getRaw() { + this->checksum(); + return remote_state; +} + +void IRTcl112Ac::setRaw(const uint8_t new_code[], const uint16_t length) { + for (uint8_t i = 0; i < length && i < kTcl112AcStateLength; i++) { + remote_state[i] = new_code[i]; + } +} + +// Set the requested power state of the A/C to on. +void IRTcl112Ac::on(void) { this->setPower(true); } + +// Set the requested power state of the A/C to off. +void IRTcl112Ac::off(void) { this->setPower(false); } + +// Set the requested power state of the A/C. +void IRTcl112Ac::setPower(const bool on) { + if (on) + remote_state[5] |= kTcl112AcPowerMask; + else + remote_state[5] &= ~kTcl112AcPowerMask; +} + +// Return the requested power state of the A/C. +bool IRTcl112Ac::getPower(void) { + return remote_state[5] & kTcl112AcPowerMask; +} + +// Get the requested climate operation mode of the a/c unit. +// Returns: +// A uint8_t containing the A/C mode. +uint8_t IRTcl112Ac::getMode() { + return remote_state[6] & 0xF; +} + +// Set the requested climate operation mode of the a/c unit. +// Note: Fan/Ventilation mode sets the fan speed to high. +// Unknown values default to Auto. +void IRTcl112Ac::setMode(const uint8_t mode) { + // If we get an unexpected mode, default to AUTO. + switch (mode) { + case kTcl112AcFan: + this->setFan(kTcl112AcFanHigh); + // FALLTHRU + case kTcl112AcAuto: + case kTcl112AcCool: + case kTcl112AcHeat: + case kTcl112AcDry: + remote_state[6] &= 0xF0; + remote_state[6] |= mode; + break; + default: + setMode(kTcl112AcAuto); + } +} + +void IRTcl112Ac::setTemp(const float celsius) { + // Make sure we have desired temp in the correct range. + float safecelsius = std::max(celsius, kTcl112AcTempMin); + safecelsius = std::min(safecelsius, kTcl112AcTempMax); + // Convert to integer nr. of half degrees. + uint8_t nrHalfDegrees = safecelsius * 2; + if (nrHalfDegrees & 1) // Do we have a half degree celsius? + remote_state[12] |= kTcl112AcHalfDegree; // Add 0.5 degrees + else + remote_state[12] &= ~kTcl112AcHalfDegree; // Clear the half degree. + remote_state[7] &= 0xF0; // Clear temp bits. + remote_state[7] |= ((uint8_t)kTcl112AcTempMax - nrHalfDegrees / 2); +} + +float IRTcl112Ac::getTemp() { + float result = kTcl112AcTempMax - (remote_state[7] & 0xF); + if (remote_state[12] & kTcl112AcHalfDegree) result += 0.5; + return result; +} + +// Set the speed of the fan. +// Unknown speeds will default to Auto. +void IRTcl112Ac::setFan(const uint8_t speed) { + switch (speed) { + case kTcl112AcFanAuto: + case kTcl112AcFanLow: + case kTcl112AcFanMed: + case kTcl112AcFanHigh: + remote_state[8] &= ~kTcl112AcFanMask; + remote_state[8] |= speed; + break; + default: + this->setFan(kTcl112AcFanAuto); + } +} + +// Return the currect fan speed. +uint8_t IRTcl112Ac::getFan() { + return remote_state[8] & kTcl112AcFanMask; +} + +// Control economy mode. +void IRTcl112Ac::setEcono(const bool on) { + if (on) + remote_state[5] |= kTcl112AcBitEcono; + else + remote_state[5] &= ~kTcl112AcBitEcono; +} + +// Return the economy state of the A/C. +bool IRTcl112Ac::getEcono(void) { + return remote_state[5] & kTcl112AcBitEcono; +} + +// Control Health mode. +void IRTcl112Ac::setHealth(const bool on) { + if (on) + remote_state[6] |= kTcl112AcBitHealth; + else + remote_state[6] &= ~kTcl112AcBitHealth; +} + +// Return the Health mode state of the A/C. +bool IRTcl112Ac::getHealth(void) { + return remote_state[6] & kTcl112AcBitHealth; +} + +// Control Light/Display mode. +void IRTcl112Ac::setLight(const bool on) { + if (on) + remote_state[5] &= ~kTcl112AcBitLight; + else + remote_state[5] |= kTcl112AcBitLight; +} + +// Return the Light/Display mode state of the A/C. +bool IRTcl112Ac::getLight(void) { + return !(remote_state[5] & kTcl112AcBitLight); +} + +// Control Horizontal Swing. +void IRTcl112Ac::setSwingHorizontal(const bool on) { + if (on) + remote_state[12] |= kTcl112AcBitSwingH; + else + remote_state[12] &= ~kTcl112AcBitSwingH; +} + +// Return the Horizontal Swing state of the A/C. +bool IRTcl112Ac::getSwingHorizontal(void) { + return remote_state[12] & kTcl112AcBitSwingH; +} + +// Control Vertical Swing. +void IRTcl112Ac::setSwingVertical(const bool on) { + if (on) + remote_state[8] |= kTcl112AcBitSwingV; + else + remote_state[8] &= ~kTcl112AcBitSwingV; +} + +// Return the Vertical Swing state of the A/C. +bool IRTcl112Ac::getSwingVertical(void) { + return remote_state[8] & kTcl112AcBitSwingV; +} + +// Control the Turbo setting. +void IRTcl112Ac::setTurbo(const bool on) { + if (on) { + remote_state[6] |= kTcl112AcBitTurbo; + this->setFan(kTcl112AcFanHigh); + this->setSwingVertical(true); + } else { + remote_state[6] &= ~kTcl112AcBitTurbo; + } +} + +// Return the Turbo setting state of the A/C. +bool IRTcl112Ac::getTurbo(void) { + return remote_state[6] & kTcl112AcBitTurbo; +} + +// Convert a standard A/C mode into its native mode. +uint8_t IRTcl112Ac::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kTcl112AcCool; + case stdAc::opmode_t::kHeat: + return kTcl112AcHeat; + case stdAc::opmode_t::kDry: + return kTcl112AcDry; + case stdAc::opmode_t::kFan: + return kTcl112AcFan; + default: + return kTcl112AcAuto; + } +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRTcl112Ac::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: + return kTcl112AcFanLow; + case stdAc::fanspeed_t::kMedium: + return kTcl112AcFanMed; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: + return kTcl112AcFanHigh; + default: + return kTcl112AcFanAuto; + } +} + +// Convert the internal state into a human readable string. +#ifdef ARDUINO +String IRTcl112Ac::toString() { + String result = ""; +#else +std::string IRTcl112Ac::toString() { + std::string result = ""; +#endif // ARDUINO + result += F("Power: "); + result += (this->getPower() ? F("On") : F("Off")); + result += F(", Mode: "); + result += uint64ToString(getMode()); + switch (this->getMode()) { + case kTcl112AcAuto: + result += F(" (AUTO)"); + break; + case kTcl112AcCool: + result += F(" (COOL)"); + break; + case kTcl112AcHeat: + result += F(" (HEAT)"); + break; + case kTcl112AcDry: + result += F(" (DRY)"); + break; + case kTcl112AcFan: + result += F(" (FAN)"); + break; + default: + result += F(" (UNKNOWN)"); + } + uint16_t nrHalfDegrees = this->getTemp() * 2; + result += F(", Temp: "); + result += uint64ToString(nrHalfDegrees / 2); + if (nrHalfDegrees & 1) result += F(".5"); + result += F("C, Fan: "); + result += uint64ToString(getFan()); + switch (getFan()) { + case kTcl112AcFanAuto: + result += F(" (Auto)"); + break; + case kTcl112AcFanLow: + result += F(" (Low)"); + break; + case kTcl112AcFanMed: + result += F(" (Med)"); + break; + case kTcl112AcFanHigh: + result += F(" (High)"); + break; + } + result += F(", Econo: "); + result += (this->getEcono() ? F("On") : F("Off")); + result += ", Health: "; + result += (this->getHealth() ? F("On") : F("Off")); + result += F(", Light: "); + result += (this->getLight() ? F("On") : F("Off")); + result += F(", Turbo: "); + result += (this->getTurbo() ? F("On") : F("Off")); + result += ", Swing (H): "; + result += (this->getSwingHorizontal() ? F("On") : F("Off")); + result += F(", Swing (V): "); + result += (this->getSwingVertical() ? F("On") : F("Off")); + return result; +} + +#if DECODE_TCL112AC +// Decode the supplied TCL112AC message. +// +// Args: +// results: Ptr to the data to decode and where to store the decode result. +// nbits: The number of data bits to expect. Typically kTcl112AcBits. +// strict: Flag indicating if we should perform strict matching. +// Returns: +// boolean: True if it can decode it, false if it can't. +// +// Status: BETA / Appears to mostly work. +// +// Ref: +// https://github.com/markszabo/IRremoteESP8266/issues/619 +bool IRrecv::decodeTcl112Ac(decode_results *results, uint16_t nbits, + bool strict) { + if (results->rawlen < 2 * nbits + kHeader + kFooter - 1) + return false; // Can't possibly be a valid Samsung A/C message. + if (strict && nbits != kTcl112AcBits) return false; + + uint16_t offset = kStartOffset; + uint16_t dataBitsSoFar = 0; + match_result_t data_result; + + // Message Header + if (!matchMark(results->rawbuf[offset++], kTcl112AcHdrMark)) return false; + if (!matchSpace(results->rawbuf[offset++], kTcl112AcHdrSpace)) return false; + + // Data + // Keep reading bytes until we either run out of section or state to fill. + for (uint16_t i = 0; offset <= results->rawlen - 16 && i < nbits / 8; + i++, dataBitsSoFar += 8, offset += data_result.used) { + data_result = matchData(&(results->rawbuf[offset]), 8, kTcl112AcBitMark, + kTcl112AcOneSpace, kTcl112AcBitMark, + kTcl112AcZeroSpace, kTolerance, 0, false); + if (data_result.success == false) { + DPRINT("DEBUG: offset = "); + DPRINTLN(offset + data_result.used); + return false; // Fail + } + results->state[i] = data_result.data; + } + + // Footer + if (!matchMark(results->rawbuf[offset++], kTcl112AcBitMark)) return false; + if (offset <= results->rawlen && + !matchAtLeast(results->rawbuf[offset++], kTcl112AcGap)) return false; + // Compliance + // Re-check we got the correct size/length due to the way we read the data. + if (dataBitsSoFar != nbits) return false; + // Verify we got a valid checksum. + if (strict && !IRTcl112Ac::validChecksum(results->state)) return false; + // Success + results->decode_type = TCL112AC; + results->bits = dataBitsSoFar; + // No need to record the state as we stored it as we decoded it. + // As we use result->state, we don't record value, address, or command as it + // is a union data type. + return true; +} +#endif // DECODE_TCL112AC diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Tcl.h b/lib/IRremoteESP8266-2.6.0/src/ir_Tcl.h new file mode 100644 index 000000000..a1595451d --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Tcl.h @@ -0,0 +1,105 @@ +// Copyright 2019 David Conran + +#ifndef IR_TCL_H_ +#define IR_TCL_H_ + +#ifndef UNIT_TEST +#include +#else +#include +#endif +#include "IRremoteESP8266.h" +#include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif + +// Constants +const uint16_t kTcl112AcHdrMark = 3000; +const uint16_t kTcl112AcHdrSpace = 1650; +const uint16_t kTcl112AcBitMark = 500; +const uint16_t kTcl112AcOneSpace = 1050; +const uint16_t kTcl112AcZeroSpace = 325; +const uint32_t kTcl112AcGap = kDefaultMessageGap; // Just a guess. + +const uint8_t kTcl112AcHeat = 1; +const uint8_t kTcl112AcDry = 2; +const uint8_t kTcl112AcCool = 3; +const uint8_t kTcl112AcFan = 7; +const uint8_t kTcl112AcAuto = 8; +const uint8_t kTcl112AcFanMask = 0b00000111; +const uint8_t kTcl112AcFanAuto = 0b00000000; +const uint8_t kTcl112AcFanLow = 0b00000010; +const uint8_t kTcl112AcFanMed = 0b00000011; +const uint8_t kTcl112AcFanHigh = 0b00000101; + +const uint8_t kTcl112AcHalfDegree = 0b00100000; +const float kTcl112AcTempMax = 31.0; +const float kTcl112AcTempMin = 16.0; + +const uint8_t kTcl112AcPowerMask = 0b00000100; +const uint8_t kTcl112AcBitEcono = 0b10000000; +const uint8_t kTcl112AcBitLight = 0b01000000; +const uint8_t kTcl112AcBitHealth = 0b00010000; +const uint8_t kTcl112AcBitSwingH = 0b00001000; +const uint8_t kTcl112AcBitSwingV = 0b00111000; +const uint8_t kTcl112AcBitTurbo = 0b01000000; + + +class IRTcl112Ac { + public: + explicit IRTcl112Ac(uint16_t pin); + +#if SEND_TCL112AC + void send(const uint16_t repeat = kTcl112AcDefaultRepeat); +#endif // SEND_TCL + void begin(void); + uint8_t* getRaw(void); + void setRaw(const uint8_t new_code[], + const uint16_t length = kTcl112AcStateLength); + void on(void); + void off(void); + void setPower(const bool on); + bool getPower(void); + void setTemp(const float celsius); // Celsius in 0.5 increments + float getTemp(void); + void setMode(const uint8_t mode); + uint8_t getMode(void); + static uint8_t calcChecksum(uint8_t state[], + const uint16_t length = kTcl112AcStateLength); + static bool validChecksum(uint8_t state[], + const uint16_t length = kTcl112AcStateLength); + void setFan(const uint8_t speed); + uint8_t getFan(void); + void setEcono(const bool on); + bool getEcono(void); + void setHealth(const bool on); + bool getHealth(void); + void setLight(const bool on); + bool getLight(void); + void setSwingHorizontal(const bool on); + bool getSwingHorizontal(void); + void setSwingVertical(const bool on); + bool getSwingVertical(void); + void setTurbo(const bool on); + bool getTurbo(void); + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); +#ifdef ARDUINO + String toString(); +#else + std::string toString(); +#endif +#ifndef UNIT_TEST + + private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif + uint8_t remote_state[kTcl112AcStateLength]; + void stateReset(); + void checksum(const uint16_t length = kTcl112AcStateLength); +}; + +#endif // IR_TCL_H_ diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Teco.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Teco.cpp new file mode 100644 index 000000000..779bf8f8f --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Teco.cpp @@ -0,0 +1,278 @@ +// Copyright 2019 Fabien Valthier +/* +Node MCU/ESP8266 Sketch to emulate Teco +*/ + +#include "ir_Teco.h" +#include +#include "IRremoteESP8266.h" +#include "IRutils.h" +#ifndef ARDUINO +#include +#endif + +// Constants +// using SPACE modulation. +const uint16_t kTecoHdrMark = 9000; +const uint16_t kTecoHdrSpace = 4440; +const uint16_t kTecoBitMark = 620; +const uint16_t kTecoOneSpace = 1650; +const uint16_t kTecoZeroSpace = 580; +const uint32_t kTecoGap = kDefaultMessageGap; // Made-up value. Just a guess. + +#if SEND_TECO +// Send a Teco A/C message. +// +// Args: +// data: Contents of the message to be sent. +// nbits: Nr. of bits of data to be sent. Typically kTecoBits. +// repeat: Nr. of additional times the message is to be sent. +void IRsend::sendTeco(uint64_t data, uint16_t nbits, uint16_t repeat) { + sendGeneric(kTecoHdrMark, kTecoHdrSpace, kTecoBitMark, kTecoOneSpace, + kTecoBitMark, kTecoZeroSpace, kTecoBitMark, kTecoGap, + data, nbits, 38000, false, repeat, kDutyDefault); +} +#endif // SEND_TECO + +// Class for decoding and constructing Teco AC messages. +IRTecoAc::IRTecoAc(const uint16_t pin) : _irsend(pin) { stateReset(); } + +void IRTecoAc::begin() { _irsend.begin(); } + +#if SEND_TECO +void IRTecoAc::send(const uint16_t repeat) { + _irsend.sendTeco(remote_state, kTecoBits, repeat); +} +#endif // SEND_TECO + +void IRTecoAc::stateReset(void) { + // Mode:auto, Power:Off, fan:auto, temp:16, swing:off, sleep:off + remote_state = kTecoReset; +} + +uint64_t IRTecoAc::getRaw(void) { return remote_state; } + +void IRTecoAc::setRaw(const uint64_t new_code) { remote_state = new_code; } + +void IRTecoAc::on(void) { remote_state |= kTecoPower; } + +void IRTecoAc::off(void) { remote_state &= ~kTecoPower; } + +void IRTecoAc::setPower(const bool on) { + if (on) + this->on(); + else + this->off(); +} + +bool IRTecoAc::getPower(void) { + return (remote_state & kTecoPower) == kTecoPower; } + +void IRTecoAc::setTemp(const uint8_t temp) { + uint8_t newtemp = temp; + newtemp = std::min(newtemp, kTecoMaxTemp); + newtemp = std::max(newtemp, kTecoMinTemp); + newtemp -= kTecoMinTemp; // 16=0b000 + + remote_state &= ~kTecoTempMask; // reinit temp + remote_state |= (newtemp << 8); +} + +uint8_t IRTecoAc::getTemp(void) { + return ((remote_state & kTecoTempMask) >> 8) + kTecoMinTemp; +} + +// Set the speed of the fan +void IRTecoAc::setFan(const uint8_t speed) { + uint8_t newspeed = speed; + switch (speed) { + case kTecoFanAuto: + case kTecoFanHigh: + case kTecoFanMed: + case kTecoFanLow: + break; + default: + newspeed = kTecoFanAuto; + } + remote_state &= ~kTecoFanMask; // reinit fan + remote_state |= (newspeed << 4); +} + +uint8_t IRTecoAc::getFan(void) { return (remote_state & kTecoFanMask) >> 4; } + +void IRTecoAc::setMode(const uint8_t mode) { + uint8_t newmode = mode; + switch (mode) { + case kTecoAuto: + case kTecoCool: + case kTecoDry: + case kTecoFan: + case kTecoHeat: + break; + default: + newmode = kTecoAuto; + } + remote_state &= ~kTecoModeMask; // reinit mode + remote_state |= newmode; +} + +uint8_t IRTecoAc::getMode(void) { return remote_state & kTecoModeMask; } + +void IRTecoAc::setSwing(const bool on) { + if (on) + remote_state |= kTecoSwing; + else + remote_state &= ~kTecoSwing; +} + +bool IRTecoAc::getSwing(void) { return remote_state & kTecoSwing; } + +void IRTecoAc::setSleep(const bool on) { + if (on) + remote_state |= kTecoSleep; + else + remote_state &= ~kTecoSleep; +} + +bool IRTecoAc::getSleep(void) { return remote_state & kTecoSleep; } + +// Convert a standard A/C mode into its native mode. +uint8_t IRTecoAc::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kTecoCool; + case stdAc::opmode_t::kHeat: + return kTecoHeat; + case stdAc::opmode_t::kDry: + return kTecoDry; + case stdAc::opmode_t::kFan: + return kTecoFan; + default: + return kTecoAuto; + } +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRTecoAc::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: + return kTecoFanLow; + case stdAc::fanspeed_t::kMedium: + return kTecoFanMed; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: + return kTecoFanHigh; + default: + return kTecoFanAuto; + } +} + +// Convert the internal state into a human readable string. +#ifdef ARDUINO +String IRTecoAc::toString(void) { + String result = ""; +#else +std::string IRTecoAc::toString(void) { + std::string result = ""; +#endif // ARDUINO + result += F("Power: "); + result += (this->getPower() ? F("On") : F("Off")); + result += F(", Mode: "); + result += uint64ToString(this->getMode()); + switch (this->getMode()) { + case kTecoAuto: + result += F(" (AUTO)"); + break; + case kTecoCool: + result += F(" (COOL)"); + break; + case kTecoHeat: + result += F(" (HEAT)"); + break; + case kTecoDry: + result += F(" (DRY)"); + break; + case kTecoFan: + result += F(" (FAN)"); + break; + default: + result += F(" (UNKNOWN)"); + } + result += F(", Temp: "); + result += uint64ToString(getTemp()); + result += F("C, Fan: "); + result += uint64ToString(getFan()); + switch (this->getFan()) { + case kTecoFanAuto: + result += F(" (Auto)"); + break; + case kTecoFanHigh: + result += F(" (High)"); + break; + case kTecoFanLow: + result += F(" (Low)"); + break; + case kTecoFanMed: + result += F(" (Med)"); + break; + default: + result += F(" (UNKNOWN)"); + } + result += F(", Sleep: "); + result += (this->getSleep() ? F("On") : F("Off")); + result += F(", Swing: "); + result += (this->getSwing() ? F("On") : F("Off")); + return result; +} + +#if DECODE_TECO +// Decode the supplied Teco message. +// +// Args: +// results: Ptr to the data to decode and where to store the decode result. +// nbits: The number of data bits to expect. Typically kTecoBits. +// strict: Flag indicating if we should perform strict matching. +// Returns: +// boolean: True if it can decode it, false if it can't. +// +// Status: STABLE / Tested. +bool IRrecv::decodeTeco(decode_results* results, uint16_t nbits, bool strict) { + // Check if can possibly be a valid Teco message. + if (results->rawlen < 2 * nbits + kHeader + kFooter - 1) return false; + if (strict && nbits != kTecoBits) return false; // Not what is expected + + uint64_t data = 0; + uint16_t offset = kStartOffset; + match_result_t data_result; + + // Header + if (!matchMark(results->rawbuf[offset++], kTecoHdrMark)) return false; + if (!matchSpace(results->rawbuf[offset++], kTecoHdrSpace)) return false; + // Data (35 bits) + data_result = + matchData(&(results->rawbuf[offset]), 35, kTecoBitMark, kTecoOneSpace, + kTecoBitMark, kTecoZeroSpace, kTolerance, kMarkExcess, false); + if (data_result.success == false) return false; + data = data_result.data; + offset += data_result.used; + uint16_t actualBits = data_result.used / 2; + + // Footer. + if (!matchMark(results->rawbuf[offset++], kTecoBitMark)) return false; + if (offset < results->rawlen && + !matchAtLeast(results->rawbuf[offset], kTecoGap)) return false; + + // Compliance + if (actualBits < nbits) return false; + if (strict && actualBits != nbits) return false; // Not as we expected. + + // Success + results->decode_type = TECO; + results->bits = actualBits; + results->value = data; + results->address = 0; + results->command = 0; + return true; +} +#endif // DECODE_TECO diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Teco.h b/lib/IRremoteESP8266-2.6.0/src/ir_Teco.h new file mode 100644 index 000000000..65a0050ae --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Teco.h @@ -0,0 +1,144 @@ +// Copyright 2019 Fabien Valthier + +#ifndef IR_TECO_H_ +#define IR_TECO_H_ + +#ifndef UNIT_TEST +#include +#else +#include +#endif +#include "IRremoteESP8266.h" +#include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif + +// Constants. Using LSB to be able to send only 35bits. +const uint8_t kTecoAuto = 0; // 0b000 +const uint8_t kTecoCool = 1; // 0b001 +const uint8_t kTecoDry = 2; // 0b010 +const uint8_t kTecoFan = 3; // 0b110 +const uint8_t kTecoHeat = 4; // 0b001 +const uint8_t kTecoFanAuto = 0; // 0b00 +const uint8_t kTecoFanHigh = 3; // 0b11 +const uint8_t kTecoFanMed = 2; // 0b10 +const uint8_t kTecoFanLow = 1; // 0b01 +const uint8_t kTecoMinTemp = 16; // 16C +const uint8_t kTecoMaxTemp = 30; // 30C + +const uint64_t kTecoModeMask = 0b00000000000000000000000000000000111; +const uint64_t kTecoPower = 0b00000000000000000000000000000001000; +const uint64_t kTecoFanMask = 0b00000000000000000000000000000110000; +const uint64_t kTecoSwing = 0b00000000000000000000000000001000000; +const uint64_t kTecoSleep = 0b00000000000000000000000000010000000; +const uint64_t kTecoTempMask = 0b00000000000000000000000111100000000; +const uint64_t kTecoTimerHalfH = 0b00000000000000000000001000000000000; +const uint64_t kTecoTimerTenHr = 0b00000000000000000000110000000000000; +const uint64_t kTecoTimerOn = 0b00000000000000000001000000000000000; +const uint64_t kTecoTimerUniHr = 0b00000000000000011110000000000000000; +const uint64_t kTecoReset = 0b01001010000000000000010000000000000; +/* + (header mark and space) + Teco AC map read and to be sent in LSB with number of bits + + byte 0 = Cst 0x02 + byte 1 = Cst 0x50 + byte 2: + b0-3 = 0b0000 + b4-7 = Timer hours (unit, not thenth) + hours: + 0000 (0) = +0 hour + 0001 (1) = +1 hour + ... + 1001 (9) = +9 hours + byte 3: = timer and Temperature + b0 = Timer (1 = On, 0 = Off) + b1-2 = Timer - number of 10hours + 10Hours: + 00 = 0 * 10hours of timer + 01 = 1 * 10 hours of timer + 10 = 2 * 10hours of timer + b3 = Timer - half hour (1=half hour on, 0 = round hour) + b4-7: Degrees C. + 0000 (0) = 16C + 0001 (1) = 17C + 0010 (2) = 18C + ... + 1101 (13) = 29C + 1110 (14) = 30C + byte 4: Basics + b0 = Sleep Mode (1 = On, 0 = Off) + b1 = Vent swing (1 = On, 0 = Off) + b2-3 = Fan + Fan: + 00 = Auto + 01 = Fan 1 + 10 = Fan 2 + 11 = Fan 3 or higher + b4 = Power Status (1 = On, 0 = Off) + b5-7 = Modes LSB first + Modes: + 000 = Auto (temp = 25C) + 001 = Cool + 010 = Dry (temp = 25C, but not shown) + 011 = Fan + 100 = Heat +*/ + +// Classes +class IRTecoAc { + public: + explicit IRTecoAc(const uint16_t pin); + + void stateReset(void); +#if SEND_TECO + void send(const uint16_t repeat = kTecoDefaultRepeat); +#endif // SEND_TECO + void begin(void); + void on(void); + void off(void); + + void setPower(const bool on); + bool getPower(void); + + void setTemp(const uint8_t temp); + uint8_t getTemp(void); + + void setFan(const uint8_t fan); + uint8_t getFan(void); + + void setMode(const uint8_t mode); + uint8_t getMode(void); + + void setSwing(const bool state); + bool getSwing(void); + + void setSleep(const bool state); + bool getSleep(void); + + // void setTimer(uint8_t time); // To check unit + // uint8_t getTimer(uint8_t); + + uint64_t getRaw(void); + void setRaw(const uint64_t new_code); + + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); +#ifdef ARDUINO + String toString(void); +#else + std::string toString(void); +#endif +#ifndef UNIT_TEST + + private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif + // The state of the IR remote in IR code form. + uint64_t remote_state; +}; + +#endif // IR_TECO_H_ diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Toshiba.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Toshiba.cpp similarity index 86% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Toshiba.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_Toshiba.cpp index 817b5fbaa..a82a2fb24 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Toshiba.cpp +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Toshiba.cpp @@ -89,9 +89,9 @@ void IRToshibaAC::begin() { _irsend.begin(); } #if SEND_TOSHIBA_AC // Send the current desired state to the IR LED. -void IRToshibaAC::send() { +void IRToshibaAC::send(const uint16_t repeat) { checksum(); // Ensure correct checksum before sending. - _irsend.sendToshibaAC(remote_state); + _irsend.sendToshibaAC(remote_state, kToshibaACStateLength, repeat); } #endif // SEND_TOSHIBA_AC @@ -233,6 +233,39 @@ void IRToshibaAC::setMode(uint8_t mode) { } } +// Convert a standard A/C mode into its native mode. +uint8_t IRToshibaAC::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kToshibaAcCool; + case stdAc::opmode_t::kHeat: + return kToshibaAcHeat; + case stdAc::opmode_t::kDry: + return kToshibaAcDry; + // No Fan mode. + default: + return kToshibaAcAuto; + } +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRToshibaAC::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + return kToshibaAcFanMax - 4; + case stdAc::fanspeed_t::kLow: + return kToshibaAcFanMax - 3; + case stdAc::fanspeed_t::kMedium: + return kToshibaAcFanMax - 2; + case stdAc::fanspeed_t::kHigh: + return kToshibaAcFanMax - 1; + case stdAc::fanspeed_t::kMax: + return kToshibaAcFanMax; + default: + return kToshibaAcFanAuto; + } +} + // Convert the internal state into a human readable string. #ifdef ARDUINO String IRToshibaAC::toString() { @@ -241,36 +274,39 @@ String IRToshibaAC::toString() { std::string IRToshibaAC::toString() { std::string result = ""; #endif // ARDUINO - result += "Power: "; + result += F("Power: "); if (getPower()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Mode: " + uint64ToString(getMode()); + result += F("Off"); + result += F(", Mode: "); + result += uint64ToString(getMode()); switch (getMode()) { case kToshibaAcAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kToshibaAcCool: - result += " (COOL)"; + result += F(" (COOL)"); break; case kToshibaAcHeat: - result += " (HEAT)"; + result += F(" (HEAT)"); break; case kToshibaAcDry: - result += " (DRY)"; + result += F(" (DRY)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); } - result += ", Temp: " + uint64ToString(getTemp()) + "C"; - result += ", Fan: " + uint64ToString(getFan()); + result += F(", Temp: "); + result += uint64ToString(getTemp()); + result += F("C, Fan: "); + result += uint64ToString(getFan()); switch (getFan()) { case kToshibaAcFanAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kToshibaAcFanMax: - result += " (MAX)"; + result += F(" (MAX)"); break; } return result; diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Toshiba.h b/lib/IRremoteESP8266-2.6.0/src/ir_Toshiba.h similarity index 90% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Toshiba.h rename to lib/IRremoteESP8266-2.6.0/src/ir_Toshiba.h index 1a1e6cdc8..03b461add 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Toshiba.h +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Toshiba.h @@ -11,6 +11,9 @@ #endif #include "IRremoteESP8266.h" #include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif // TTTTTTT OOOOO SSSSS HH HH IIIII BBBBB AAA // TTT OO OO SS HH HH III BB B AAAAA @@ -48,7 +51,7 @@ class IRToshibaAC { void stateReset(); #if SEND_TOSHIBA_AC - void send(); + void send(const uint16_t repeat = kToshibaACMinRepeat); #endif // SEND_TOSHIBA_AC void begin(); void on(); @@ -65,6 +68,8 @@ class IRToshibaAC { uint8_t* getRaw(); static bool validChecksum(const uint8_t state[], const uint16_t length = kToshibaACStateLength); + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); #ifdef ARDUINO String toString(); #else @@ -73,13 +78,15 @@ class IRToshibaAC { #ifndef UNIT_TEST private: + IRsend _irsend; +#else + IRsendTest _irsend; #endif uint8_t remote_state[kToshibaACStateLength]; void checksum(const uint16_t length = kToshibaACStateLength); static uint8_t calcChecksum(const uint8_t state[], const uint16_t length = kToshibaACStateLength); uint8_t mode_state; - IRsend _irsend; }; #endif // IR_TOSHIBA_H_ diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Trotec.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Trotec.cpp new file mode 100644 index 000000000..b5c15e7fd --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Trotec.cpp @@ -0,0 +1,162 @@ +// Copyright 2017 stufisher + +#include "ir_Trotec.h" +#include +#include "IRremoteESP8266.h" +#include "IRutils.h" + +// Constants +const uint16_t kTrotecHdrMark = 5952; +const uint16_t kTrotecHdrSpace = 7364; +const uint16_t kTrotecOneMark = 592; +const uint16_t kTrotecOneSpace = 1560; +const uint16_t kTrotecZeroMark = 592; +const uint16_t kTrotecZeroSpace = 592; +const uint16_t kTrotecGap = 6184; +const uint16_t kTrotecGapEnd = 1500; // made up value + +#if SEND_TROTEC + +void IRsend::sendTrotec(unsigned char data[], uint16_t nbytes, + uint16_t repeat) { + if (nbytes < kTrotecStateLength) return; + + for (uint16_t r = 0; r <= repeat; r++) { + sendGeneric(kTrotecHdrMark, kTrotecHdrSpace, kTrotecOneMark, + kTrotecOneSpace, kTrotecZeroMark, kTrotecZeroSpace, + kTrotecOneMark, kTrotecGap, data, nbytes, 36, false, + 0, // Repeats handled elsewhere + 50); + // More footer + enableIROut(36); + mark(kTrotecOneMark); + space(kTrotecGapEnd); + } +} +#endif // SEND_TROTEC + +IRTrotecESP::IRTrotecESP(uint16_t pin) : _irsend(pin) { stateReset(); } + +void IRTrotecESP::begin() { _irsend.begin(); } + +#if SEND_TROTEC +void IRTrotecESP::send(const uint16_t repeat) { + checksum(); + _irsend.sendTrotec(remote_state, kTrotecStateLength, repeat); +} +#endif // SEND_TROTEC + +void IRTrotecESP::checksum() { + uint8_t sum = 0; + + for (uint8_t i = 2; i < 8; i++) sum += remote_state[i]; + remote_state[8] = sum & 0xFF; +} + +void IRTrotecESP::stateReset() { + for (uint8_t i = 2; i < kTrotecStateLength; i++) remote_state[i] = 0x0; + + remote_state[0] = kTrotecIntro1; + remote_state[1] = kTrotecIntro2; + + setPower(false); + setTemp(kTrotecDefTemp); + setSpeed(kTrotecFanMed); + setMode(kTrotecAuto); +} + +uint8_t* IRTrotecESP::getRaw() { + checksum(); + return remote_state; +} + +void IRTrotecESP::setPower(const bool on) { + if (on) + remote_state[2] |= kTrotecPowerBit; + else + remote_state[2] &= ~kTrotecPowerBit; +} + +bool IRTrotecESP::getPower() { return remote_state[2] & kTrotecPowerBit; } + +void IRTrotecESP::setSpeed(const uint8_t fan) { + uint8_t speed = std::min(fan, kTrotecFanHigh); + remote_state[2] = (remote_state[2] & 0b11001111) | (speed << 4); +} + +uint8_t IRTrotecESP::getSpeed() { return (remote_state[2] & 0b00110000) >> 4; } + +void IRTrotecESP::setMode(const uint8_t mode) { + switch (mode) { + case kTrotecAuto: + case kTrotecCool: + case kTrotecDry: + case kTrotecFan: + remote_state[2] = (remote_state[2] & 0b11111100) | mode; + return; + default: + this->setMode(kTrotecAuto); + } +} + +uint8_t IRTrotecESP::getMode() { return remote_state[2] & 0b00000011; } + +void IRTrotecESP::setTemp(const uint8_t celsius) { + uint8_t temp = std::max(celsius, kTrotecMinTemp); + temp = std::min(temp, kTrotecMaxTemp); + remote_state[3] = (remote_state[3] & 0x80) | (temp - kTrotecMinTemp); +} + +uint8_t IRTrotecESP::getTemp() { + return (remote_state[3] & 0b01111111) + kTrotecMinTemp; +} + +void IRTrotecESP::setSleep(bool sleep) { + if (sleep) + remote_state[3] |= kTrotecSleepBit; + else + remote_state[3] &= ~kTrotecSleepBit; +} + +bool IRTrotecESP::getSleep(void) { return remote_state[3] & kTrotecSleepBit; } + +void IRTrotecESP::setTimer(const uint8_t timer) { + if (timer) + remote_state[5] |= kTrotecTimerBit; + else + remote_state[5] &= ~kTrotecTimerBit; + remote_state[6] = (timer > kTrotecMaxTimer) ? kTrotecMaxTimer : timer; +} + +uint8_t IRTrotecESP::getTimer() { return remote_state[6]; } + +// Convert a standard A/C mode into its native mode. +uint8_t IRTrotecESP::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kTrotecCool; + case stdAc::opmode_t::kDry: + return kTrotecDry; + case stdAc::opmode_t::kFan: + return kTrotecFan; + // Note: No Heat mode. + default: + return kTrotecAuto; + } +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRTrotecESP::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: + return kTrotecFanLow; + case stdAc::fanspeed_t::kMedium: + return kTrotecFanMed; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: + return kTrotecFanHigh; + default: + return kTrotecFanMed; + } +} diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Trotec.h b/lib/IRremoteESP8266-2.6.0/src/ir_Trotec.h similarity index 68% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Trotec.h rename to lib/IRremoteESP8266-2.6.0/src/ir_Trotec.h index 040d9a722..dfbc26c07 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Trotec.h +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Trotec.h @@ -5,6 +5,9 @@ #include "IRremoteESP8266.h" #include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif // Constants // Byte 0 @@ -19,8 +22,7 @@ const uint8_t kTrotecCool = 1; const uint8_t kTrotecDry = 2; const uint8_t kTrotecFan = 3; -const uint8_t kTrotecOn = 1; -const uint8_t kTrotecOff = 0; +const uint8_t kTrotecPowerBit = 0b00001000; const uint8_t kTrotecFanLow = 1; const uint8_t kTrotecFanMed = 2; @@ -31,13 +33,12 @@ const uint8_t kTrotecMinTemp = 18; const uint8_t kTrotecDefTemp = 25; const uint8_t kTrotecMaxTemp = 32; -const uint8_t kTrotecSleepOn = 1; +const uint8_t kTrotecSleepBit = 0b10000000; // Byte 5 -const uint8_t kTrotecTimerOn = 1; +const uint8_t kTrotecTimerBit = 0b01000000; // Byte 6 -const uint8_t kTrotecMinTimer = 0; const uint8_t kTrotecMaxTimer = 23; // Legacy defines. (Deperecated) @@ -50,7 +51,6 @@ const uint8_t kTrotecMaxTimer = 23; #define TROTEC_FAN_HIGH kTrotecFanHigh #define TROTEC_MIN_TEMP kTrotecMinTemp #define TROTEC_MAX_TEMP kTrotecMaxTemp -#define TROTEC_MIN_TIMER kTrotecMinTimer #define TROTEC_MAX_TIMER kTrotecMaxTimer class IRTrotecESP { @@ -58,35 +58,42 @@ class IRTrotecESP { explicit IRTrotecESP(uint16_t pin); #if SEND_TROTEC - void send(); + void send(const uint16_t repeat = kTrotecDefaultRepeat); #endif // SEND_TROTEC void begin(); - void setPower(bool state); - uint8_t getPower(); + void setPower(const bool state); + bool getPower(); - void setTemp(uint8_t temp); + void setTemp(const uint8_t celsius); uint8_t getTemp(); - void setSpeed(uint8_t fan); + void setSpeed(const uint8_t fan); uint8_t getSpeed(); uint8_t getMode(); - void setMode(uint8_t mode); + void setMode(const uint8_t mode); bool getSleep(); void setSleep(bool sleep); uint8_t getTimer(); - void setTimer(uint8_t timer); + void setTimer(const uint8_t timer); uint8_t* getRaw(); + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); +#ifndef UNIT_TEST + private: - uint8_t trotec[kTrotecStateLength]; + IRsend _irsend; +#else + IRsendTest _irsend; +#endif + uint8_t remote_state[kTrotecStateLength]; void stateReset(); void checksum(); - IRsend _irsend; }; #endif // IR_TROTEC_H_ diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Vestel.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Vestel.cpp new file mode 100644 index 000000000..1fbb822cf --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Vestel.cpp @@ -0,0 +1,583 @@ +// Copyright 2018 Erdem U. Altinyurt +// Copyright 2019 David Conran + +#include "ir_Vestel.h" +#include +#ifndef UNIT_TEST +#include +#else +#include +#endif +#include "IRrecv.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" +#include "IRutils.h" +#include "ir_Haier.h" + +// VV VV EEEEEEE SSSSS TTTTTTTT EEEEEEE LL +// VV VV EE S TT EE LL +// VV VV EEEEE SSSS TT EEEEE LL +// VV VV EE S TT EE LL +// VVV EEEEEEE SSSSS TT EEEEEEE LLLLLLL + +// Vestel added by Erdem U. Altinyurt + +// Equipment it seems compatible with: +// * Vestel AC Model BIOX CXP-9 (9K BTU) +// * + +// Ref: +// None. Totally reverse engineered. + +#if SEND_VESTEL_AC +// Send a Vestel message +// +// Args: +// data: Contents of the message to be sent. +// nbits: Nr. of bits of data to be sent. Typically kVestelBits. +// +// Status: STABLE / Working. +// +void IRsend::sendVestelAc(const uint64_t data, const uint16_t nbits, + const uint16_t repeat) { + if (nbits % 8 != 0) return; // nbits is required to be a multiple of 8. + + sendGeneric(kVestelAcHdrMark, kVestelAcHdrSpace, // Header + kVestelAcBitMark, kVestelAcOneSpace, // Data + kVestelAcBitMark, kVestelAcZeroSpace, // Data + kVestelAcBitMark, 100000, // Footer + repeat gap + data, nbits, 38, false, repeat, 50); +} +#endif + +// Code to emulate Vestel A/C IR remote control unit. + +// Initialise the object. +IRVestelAc::IRVestelAc(uint16_t pin) : _irsend(pin) { stateReset(); } + +// Reset the state of the remote to a known good state/sequence. +void IRVestelAc::stateReset() { + // Power On, Mode Auto, Fan Auto, Temp = 25C/77F + remote_state = kVestelAcStateDefault; + remote_time_state = kVestelAcTimeStateDefault; + use_time_state = false; +} + +// Configure the pin for output. +void IRVestelAc::begin() { + _irsend.begin(); +} + +#if SEND_VESTEL_AC +// Send the current desired state to the IR LED. +void IRVestelAc::send() { + checksum(); // Ensure correct checksum before sending. + uint64_t code_to_send; + if (use_time_state) + code_to_send = remote_time_state; + else + code_to_send = remote_state; + _irsend.sendVestelAc(code_to_send); +} +#endif // SEND_VESTEL_AC + +// Return the internal state date of the remote. +uint64_t IRVestelAc::getRaw() { + checksum(); + if (use_time_state) return remote_time_state; + return remote_state; +} + +// Override the internal state with the new state. +void IRVestelAc::setRaw(uint8_t* newState) { + uint64_t upState = 0; + for (int i = 0; i < 7; i++) + upState |= static_cast(newState[i]) << (i * 8); + this->setRaw(upState); +} + +void IRVestelAc::setRaw(const uint64_t newState) { + use_time_state = false; + remote_state = newState; + remote_time_state = newState; + if (this->isTimeCommand()) { + use_time_state = true; + remote_state = kVestelAcStateDefault; + } else { + remote_time_state = kVestelAcTimeStateDefault; + } +} + +// Set the requested power state of the A/C to on. +void IRVestelAc::on() { setPower(true); } + +// Set the requested power state of the A/C to off. +void IRVestelAc::off() { setPower(false); } + +// Set the requested power state of the A/C. +void IRVestelAc::setPower(const bool state) { + remote_state &= ~((uint64_t)0xF << kVestelAcPowerOffset); + if (state) + remote_state |= ((uint64_t)0xF << kVestelAcPowerOffset); + else + remote_state |= ((uint64_t)0xC << kVestelAcPowerOffset); + use_time_state = false; +} + +// Return the requested power state of the A/C. +bool IRVestelAc::getPower() { + return (remote_state >> kVestelAcPowerOffset == 0xF); +} + +// Set the temperature in Celsius degrees. +void IRVestelAc::setTemp(const uint8_t temp) { + uint8_t new_temp = temp; + new_temp = std::max(kVestelAcMinTempC, new_temp); + // new_temp = std::max(kVestelAcMinTempH, new_temp); Check MODE + new_temp = std::min(kVestelAcMaxTemp, new_temp); + remote_state &= ~((uint64_t)0xF << kVestelAcTempOffset); + remote_state |= (uint64_t)(new_temp - 16) << kVestelAcTempOffset; + use_time_state = false; +} + +// Return the set temperature. +uint8_t IRVestelAc::getTemp(void) { + return ((remote_state >> kVestelAcTempOffset) & 0xF) + 16; +} + +// Set the speed of the fan, +// 1-3 set the fan speed, 0 or anything else set it to auto. +void IRVestelAc::setFan(const uint8_t fan) { + switch (fan) { + case kVestelAcFanLow: + case kVestelAcFanMed: + case kVestelAcFanHigh: + case kVestelAcFanAutoCool: + case kVestelAcFanAutoHot: + case kVestelAcFanAuto: + remote_state &= ~((uint64_t)0xF << kVestelAcFanOffset); + remote_state |= (uint64_t)fan << kVestelAcFanOffset; + break; + default: + setFan(kVestelAcFanAuto); + } + use_time_state = false; +} + +// Return the requested state of the unit's fan. +uint8_t IRVestelAc::getFan() { + return (remote_state >> kVestelAcFanOffset) & 0xF; +} + +// Get the requested climate operation mode of the a/c unit. +// Returns: +// A uint8_t containing the A/C mode. +uint8_t IRVestelAc::getMode() { + return (remote_state >> kVestelAcModeOffset) & 0xF; +} + +// Set the requested climate operation mode of the a/c unit. +void IRVestelAc::setMode(const uint8_t mode) { + // If we get an unexpected mode, default to AUTO. + switch (mode) { + case kVestelAcAuto: + case kVestelAcCool: + case kVestelAcHeat: + case kVestelAcDry: + case kVestelAcFan: + remote_state &= ~((uint64_t)0xF << kVestelAcModeOffset); + remote_state |= (uint64_t)mode << kVestelAcModeOffset; + break; + default: + setMode(kVestelAcAuto); + } + use_time_state = false; +} + +// Set Auto mode of AC. +void IRVestelAc::setAuto(const int8_t autoLevel) { + if (autoLevel < -2 || autoLevel > 2) return; + setMode(kVestelAcAuto); + setFan((autoLevel < 0 ? kVestelAcFanAutoCool : kVestelAcFanAutoHot)); + if (autoLevel == 2) + setTemp(30); + else if (autoLevel == 1) + setTemp(31); + else if (autoLevel == 0) + setTemp(25); + else if (autoLevel == -1) + setTemp(16); + else if (autoLevel == -2) + setTemp(17); +} + +void IRVestelAc::setTimerActive(const bool on) { + if (on) // activation + remote_time_state |= ((uint64_t)1 << kVestelAcTimerFlagOffset); + else // deactivate + remote_time_state &= ~((uint64_t)1 << kVestelAcTimerFlagOffset); + use_time_state = true; +} + +bool IRVestelAc::isTimerActive(void) { + return (remote_time_state >> kVestelAcTimerFlagOffset) & 1; +} + +// Set Timer option of AC. +// Valid time arguments are 0, 0.5, 1, 2, 3 and 5 hours (in min). 0 disables the +// timer. +void IRVestelAc::setTimer(const uint16_t minutes) { + // Clear both On & Off timers. + remote_time_state &= ~((uint64_t)0xFFFF << kVestelAcOffTimeOffset); + // Set the "Off" time with the nr of minutes before we turn off. + remote_time_state |= (uint64_t)(((minutes / 60) << 3) + (minutes % 60) / 10) + << kVestelAcOffTimeOffset; + setOffTimerActive(false); + // Yes. On Timer instead of Off timer active. + setOnTimerActive(minutes != 0); + setTimerActive(minutes != 0); + use_time_state = true; +} + +uint16_t IRVestelAc::getTimer(void) { return getOffTimer(); } + +// Set the AC's internal clock +void IRVestelAc::setTime(const uint16_t minutes) { + remote_time_state &= ~((uint64_t)0x1F << kVestelAcHourOffset); + remote_time_state |= (uint64_t)((minutes / 60) & 0x1F) + << kVestelAcHourOffset; + remote_time_state &= ~((uint64_t)0xFF << kVestelAcMinuteOffset); + remote_time_state |= (uint64_t)((minutes % 60) & 0xFF) + << kVestelAcMinuteOffset; + use_time_state = true; +} + +uint16_t IRVestelAc::getTime(void) { + return ((remote_time_state >> kVestelAcHourOffset) & 0x1F) * 60 + + ((remote_time_state >> kVestelAcMinuteOffset) & 0xFF); +} + +void IRVestelAc::setOnTimerActive(const bool on) { + if (on) // activation + remote_time_state |= ((uint64_t)1 << kVestelAcOnTimerFlagOffset); + else // deactivate + remote_time_state &= ~((uint64_t)1 << kVestelAcOnTimerFlagOffset); + use_time_state = true; +} + +bool IRVestelAc::isOnTimerActive(void) { + return (remote_time_state >> kVestelAcOnTimerFlagOffset) & 1; +} + +// Set AC's wake up time. Takes time in minute. +void IRVestelAc::setOnTimer(const uint16_t minutes) { + remote_time_state &= ~((uint64_t)0xFF << kVestelAcOnTimeOffset); + remote_time_state |= (uint64_t)(((minutes / 60) << 3) + (minutes % 60) / 10) + << kVestelAcOnTimeOffset; + setOnTimerActive(minutes != 0); + setTimerActive(false); + use_time_state = true; +} + +uint16_t IRVestelAc::getOnTimer(void) { + uint8_t ontime = (remote_time_state >> kVestelAcOnTimeOffset) & 0xFF; + return (ontime >> 3) * 60 + (ontime & 0x7) * 10; +} + +void IRVestelAc::setOffTimerActive(const bool on) { + if (on) // activation + remote_time_state |= ((uint64_t)1 << kVestelAcOffTimerFlagOffset); + else // deactivate + remote_time_state &= ~((uint64_t)1 << kVestelAcOffTimerFlagOffset); + use_time_state = true; +} + +bool IRVestelAc::isOffTimerActive(void) { + return (remote_time_state >> kVestelAcOffTimerFlagOffset) & 1; +} + +// Set AC's turn off time. Takes time in minute. +void IRVestelAc::setOffTimer(const uint16_t minutes) { + remote_time_state &= ~((uint64_t)0xFF << kVestelAcOffTimeOffset); + remote_time_state |= + (uint64_t)((((minutes / 60) << 3) + (minutes % 60) / 10) & 0xFF) + << kVestelAcOffTimeOffset; + setOffTimerActive(minutes != 0); + setTimerActive(false); + use_time_state = true; +} + +uint16_t IRVestelAc::getOffTimer(void) { + uint8_t offtime = (remote_time_state >> kVestelAcOffTimeOffset) & 0xFF; + return (offtime >> 3) * 60 + (offtime & 0x7) * 10; +} + +// Set the Sleep state of the A/C. +void IRVestelAc::setSleep(const bool state) { + remote_state &= ~((uint64_t)0xF << kVestelAcTurboSleepOffset); + remote_state |= (uint64_t)(state ? kVestelAcSleep : kVestelAcNormal) + << kVestelAcTurboSleepOffset; + use_time_state = false; +} + +// Return the Sleep state of the A/C. +bool IRVestelAc::getSleep() { + return ((remote_state >> kVestelAcTurboSleepOffset) & 0xF) == kVestelAcSleep; +} + +// Set the Turbo state of the A/C. +void IRVestelAc::setTurbo(const bool state) { + remote_state &= ~((uint64_t)0xF << kVestelAcTurboSleepOffset); + remote_state |= (uint64_t)(state ? kVestelAcTurbo : kVestelAcNormal) + << kVestelAcTurboSleepOffset; + use_time_state = false; +} + +// Return the Turbo state of the A/C. +bool IRVestelAc::getTurbo() { + return ((remote_state >> kVestelAcTurboSleepOffset) & 0xF) == kVestelAcTurbo; +} + +// Set the Ion state of the A/C. +void IRVestelAc::setIon(const bool state) { + remote_state &= ~((uint64_t)0x1 << kVestelAcIonOffset); + + remote_state |= (uint64_t)(state ? 1 : 0) << kVestelAcIonOffset; + use_time_state = false; +} + +// Return the Ion state of the A/C. +bool IRVestelAc::getIon() { return (remote_state >> kVestelAcIonOffset) & 1; } + +// Set the Swing Roaming state of the A/C. +void IRVestelAc::setSwing(const bool state) { + remote_state &= ~((uint64_t)0xF << kVestelAcSwingOffset); + + remote_state |= (uint64_t)(state ? kVestelAcSwing : 0xF) + << kVestelAcSwingOffset; + use_time_state = false; +} + +// Return the Swing Roaming state of the A/C. +bool IRVestelAc::getSwing() { + return ((remote_state >> kVestelAcSwingOffset) & 0xF) == kVestelAcSwing; +} + +// Calculate the checksum for a given array. +// Args: +// state: The state to calculate the checksum over. +// Returns: +// The 8 bit checksum value. +uint8_t IRVestelAc::calcChecksum(const uint64_t state) { + // Just counts the set bits +1 on stream and take inverse after mask + uint8_t sum = 0; + uint64_t temp_state = state & kVestelAcCRCMask; + for (; temp_state; temp_state >>= 1) + if (temp_state & 1) sum++; + sum += 2; + sum = 0xff - sum; + return sum; +} + +// Verify the checksum is valid for a given state. +// Args: +// state: The state to verify the checksum of. +// Returns: +// A boolean. +bool IRVestelAc::validChecksum(const uint64_t state) { + return (((state >> kVestelAcChecksumOffset) & 0xFF) == calcChecksum(state)); +} + +// Calculate & set the checksum for the current internal state of the remote. +void IRVestelAc::checksum() { + // Stored the checksum value in the last byte. + remote_state &= ~((uint64_t)0xFF << kVestelAcChecksumOffset); + remote_state |= (uint64_t)calcChecksum(remote_state) + << kVestelAcChecksumOffset; + + remote_time_state &= ~((uint64_t)0xFF << kVestelAcChecksumOffset); + remote_time_state |= (uint64_t)calcChecksum(remote_time_state) + << kVestelAcChecksumOffset; +} + +bool IRVestelAc::isTimeCommand() { + return (remote_state >> kVestelAcPowerOffset == 0x00 || use_time_state); +} + + +// Convert a standard A/C mode into its native mode. +uint8_t IRVestelAc::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kVestelAcCool; + case stdAc::opmode_t::kHeat: + return kVestelAcHeat; + case stdAc::opmode_t::kDry: + return kVestelAcDry; + case stdAc::opmode_t::kFan: + return kVestelAcFan; + default: + return kVestelAcAuto; + } +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRVestelAc::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: + return kVestelAcFanLow; + case stdAc::fanspeed_t::kMedium: + return kVestelAcFanMed; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: + return kVestelAcFanHigh; + default: + return kVestelAcFanAuto; + } +} + +// Convert the internal state into a human readable string. +#ifdef ARDUINO +String IRVestelAc::toString() { + String result = ""; +#else +std::string IRVestelAc::toString() { + std::string result = ""; +#endif // ARDUINO + if (isTimeCommand()) { + result += F("Time: "); + result += IRHaierAC::timeToString(getTime()); + + result += F(", Timer: "); + result += isTimerActive() ? IRHaierAC::timeToString(getTimer()) : F("Off"); + + result += F(", On Timer: "); + result += (isOnTimerActive() && !isTimerActive()) + ? IRHaierAC::timeToString(getOnTimer()) + : F("Off"); + + result += F(", Off Timer: "); + result += + isOffTimerActive() ? IRHaierAC::timeToString(getOffTimer()) : F("Off"); + return result; + } + // Not a time command, it's a normal command. + result += F("Power: "); + result += (getPower() ? F("On") : F("Off")); + result += F(", Mode: "); + result += uint64ToString(getMode()); + switch (getMode()) { + case kVestelAcAuto: + result += F(" (AUTO)"); + break; + case kVestelAcCool: + result += F(" (COOL)"); + break; + case kVestelAcHeat: + result += F(" (HEAT)"); + break; + case kVestelAcDry: + result += F(" (DRY)"); + break; + case kVestelAcFan: + result += F(" (FAN)"); + break; + default: + result += F(" (UNKNOWN)"); + } + result += F(", Temp: "); + result += uint64ToString(getTemp()); + result += F("C, Fan: "); + result += uint64ToString(getFan()); + switch (getFan()) { + case kVestelAcFanAuto: + result += F(" (AUTO)"); + break; + case kVestelAcFanLow: + result += F(" (LOW)"); + break; + case kVestelAcFanMed: + result += F(" (MEDIUM)"); + break; + case kVestelAcFanHigh: + result += F(" (HIGH)"); + break; + case kVestelAcFanAutoCool: + result += F(" (AUTO COOL)"); + break; + case kVestelAcFanAutoHot: + result += F(" (AUTO HOT)"); + break; + default: + result += F(" (UNKNOWN)"); + } + result += F(", Sleep: "); + result += (getSleep() ? F("On") : F("Off")); + result += F(", Turbo: "); + result += (getTurbo() ? F("On") : F("Off")); + result += F(", Ion: "); + result += (getIon() ? F("On") : F("Off")); + result += F(", Swing: "); + result += (getSwing() ? F("On") : F("Off")); + return result; +} + +#if DECODE_VESTEL_AC +// Decode the supplied Vestel message. +// +// Args: +// results: Ptr to the data to decode and where to store the decode result. +// nbits: The number of data bits to expect. Typically kVestelBits. +// strict: Flag indicating if we should perform strict matching. +// Returns: +// boolean: True if it can decode it, false if it can't. +// +// Status: Alpha / Needs testing against a real device. +// +bool IRrecv::decodeVestelAc(decode_results* results, uint16_t nbits, + bool strict) { + if (nbits % 8 != 0) // nbits has to be a multiple of nr. of bits in a byte. + return false; + + if (strict) + if (nbits != kVestelAcBits) + return false; // Not strictly a Vestel AC message. + + uint64_t data = 0; + uint16_t offset = kStartOffset; + + if (nbits > sizeof(data) * 8) + return false; // We can't possibly capture a Vestel packet that big. + + // Header + if (!matchMark(results->rawbuf[offset++], kVestelAcHdrMark)) return false; + if (!matchSpace(results->rawbuf[offset++], kVestelAcHdrSpace)) return false; + + // Data (Normal) + match_result_t data_result = + matchData(&(results->rawbuf[offset]), nbits, kVestelAcBitMark, + kVestelAcOneSpace, kVestelAcBitMark, kVestelAcZeroSpace, + kVestelAcTolerance, kMarkExcess, false); + + if (data_result.success == false) return false; + offset += data_result.used; + data = data_result.data; + + // Footer + if (!matchMark(results->rawbuf[offset++], kVestelAcBitMark)) return false; + + // Compliance + if (strict) + if (!IRVestelAc::validChecksum(data_result.data)) return false; + + // Success + results->decode_type = VESTEL_AC; + results->bits = nbits; + results->value = data; + results->address = 0; + results->command = 0; + + return true; +} +#endif // DECODE_VESTEL_AC diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Vestel.h b/lib/IRremoteESP8266-2.6.0/src/ir_Vestel.h new file mode 100644 index 000000000..ab04e8b35 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Vestel.h @@ -0,0 +1,177 @@ +// Copyright 2018 Erdem U. Altinyurt +// Copyright 2019 David Conran + +#ifndef IR_VESTEL_H_ +#define IR_VESTEL_H_ + +#define __STDC_LIMIT_MACROS +#include +#ifdef ARDUINO +#include +#else +#include +#endif +#include "IRremoteESP8266.h" +#include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif + +// VV VV EEEEEEE SSSSS TTTTTTTT EEEEEEE LL +// VV VV EE S TT EE LL +// VV VV EEEEE SSSS TT EEEEE LL +// VV VV EE S TT EE LL +// VVV EEEEEEE SSSSS TT EEEEEEE LLLLLLL + +// Vestel added by Erdem U. Altinyurt + +// Structure of a Command message (56 bits) +// Signature: 12 bits. e.g. 0x201 +// Checksum: 8 bits +// Swing: 4 bits. (auto 0xA, stop 0xF) +// turbo_sleep_normal: 4bits. (normal 0x1, sleep 0x3, turbo 0x7) +// Unused: 8 bits. (0x00) +// Temperature: 4 bits. (Celcius, but offset by -16 degrees. e.g. 0x0 = 16C) +// Fan Speed: 4 bits (auto 0x1, low 0x5, mid 0x9, high 0xB, 0xD auto hot, +// 0xC auto cool) +// Mode: 3 bits. (auto 0x0, cold 0x1, dry 0x2, fan 0x3, hot 0x4) +// unknown/unused: 6 bits. +// Ion flag: 1 bit. +// unknown/unused: 1 bit. +// Power/message type: 4 bits. (on 0xF, off 0xC, 0x0 == Timer mesage) +// +// Structure of a Time(r) message (56 bits) +// Signature: 12 bits. e.g. 0x201 +// Checksum: 8 bits +// Off Minutes: 3 bits. (Stored in 10 min increments. eg. xx:20 is 0x2) +// Off Hours: 5 bits. (0x17 == 11PM / 23:00) +// On Minutes: 3 bits. (Stored in 10 min increments. eg. xx:20 is 0x2) +// On Hours: 5 bits. (0x9 == 9AM / 09:00) +// Clock Hours: 5 bits. +// On Timer flag: 1 bit. +// Off Timer flag: 1 bit. +// Timer mode flag: 1 bit. (Off after X many hours/mins, not at clock time.) +// Clock Minutes: 8 bits. (0-59) +// Power/message type: 4 bits. (0x0 == Timer mesage, else see Comman message) + +// Constants +const uint16_t kVestelAcHdrMark = 3110; +const uint16_t kVestelAcHdrSpace = 9066; +const uint16_t kVestelAcBitMark = 520; +const uint16_t kVestelAcOneSpace = 1535; +const uint16_t kVestelAcZeroSpace = 480; +const uint16_t kVestelAcTolerance = 30; + +const uint8_t kVestelAcMinTempH = 16; +const uint8_t kVestelAcMinTempC = 18; +const uint8_t kVestelAcMaxTemp = 30; + +const uint64_t kVestelAcCRCMask = 0xFFFFFFFFFFF00000; + +const uint8_t kVestelAcAuto = 0; +const uint8_t kVestelAcCool = 1; +const uint8_t kVestelAcDry = 2; +const uint8_t kVestelAcFan = 3; +const uint8_t kVestelAcHeat = 4; + +const uint8_t kVestelAcFanAuto = 1; +const uint8_t kVestelAcFanLow = 5; +const uint8_t kVestelAcFanMed = 9; +const uint8_t kVestelAcFanHigh = 0xB; +const uint8_t kVestelAcFanAutoCool = 0xC; +const uint8_t kVestelAcFanAutoHot = 0xD; + +const uint8_t kVestelAcNormal = 1; +const uint8_t kVestelAcSleep = 3; +const uint8_t kVestelAcTurbo = 7; +const uint8_t kVestelAcIon = 4; +const uint8_t kVestelAcSwing = 0xA; + +const uint8_t kVestelAcChecksumOffset = 12; +const uint8_t kVestelAcSwingOffset = 20; +const uint8_t kVestelAcTurboSleepOffset = 24; +const uint8_t kVestelAcTempOffset = 36; +const uint8_t kVestelAcFanOffset = 40; +const uint8_t kVestelAcModeOffset = 44; +const uint8_t kVestelAcIonOffset = 50; +const uint8_t kVestelAcPowerOffset = 52; +const uint8_t kVestelAcOffTimeOffset = 20; +const uint8_t kVestelAcOnTimeOffset = 28; +const uint8_t kVestelAcHourOffset = 36; // 5 bits +const uint8_t kVestelAcOnTimerFlagOffset = kVestelAcHourOffset + 5; +const uint8_t kVestelAcOffTimerFlagOffset = kVestelAcHourOffset + 6; +const uint8_t kVestelAcTimerFlagOffset = kVestelAcHourOffset + 7; +const uint8_t kVestelAcMinuteOffset = 44; + +const uint64_t kVestelAcStateDefault = 0x0F00D9001FEF201ULL; +const uint64_t kVestelAcTimeStateDefault = 0x201ULL; + +class IRVestelAc { + public: + explicit IRVestelAc(uint16_t pin); + + void stateReset(); +#if SEND_VESTEL_AC + void send(); +#endif // SEND_VESTEL_AC + void begin(void); + void on(void); + void off(void); + void setPower(const bool state); + bool getPower(); + void setAuto(const int8_t autoLevel); + void setTimer(const uint16_t minutes); + uint16_t getTimer(void); + void setTime(const uint16_t minutes); + uint16_t getTime(void); + void setOnTimer(const uint16_t minutes); + uint16_t getOnTimer(void); + void setOffTimer(const uint16_t minutes); + uint16_t getOffTimer(void); + void setTemp(const uint8_t temp); + uint8_t getTemp(void); + void setFan(const uint8_t fan); + uint8_t getFan(void); + void setMode(const uint8_t mode); + uint8_t getMode(void); + void setRaw(uint8_t* newState); + void setRaw(const uint64_t newState); + uint64_t getRaw(void); + static bool validChecksum(const uint64_t state); + void setSwing(const bool state); + bool getSwing(void); + void setSleep(const bool state); + bool getSleep(void); + void setTurbo(const bool state); + bool getTurbo(void); + void setIon(const bool state); + bool getIon(void); + bool isTimeCommand(void); + bool isOnTimerActive(void); + void setOnTimerActive(const bool on); + bool isOffTimerActive(void); + void setOffTimerActive(const bool on); + bool isTimerActive(void); + void setTimerActive(const bool on); + static uint8_t calcChecksum(const uint64_t state); + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); +#ifdef ARDUINO + String toString(); +#else + std::string toString(); +#endif +#ifndef UNIT_TEST + + private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif + uint64_t remote_state; + uint64_t remote_time_state; + bool use_time_state; + void checksum(); +}; + +#endif // IR_VESTEL_H_ diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Whirlpool.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Whirlpool.cpp new file mode 100644 index 000000000..048c1a1eb --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Whirlpool.cpp @@ -0,0 +1,671 @@ +// Copyright 2018 David Conran +// +// Code to emulate Whirlpool protocol compatible devices. +// Should be compatible with: +// * SPIS409L, SPIS412L, SPIW409L, SPIW412L, SPIW418L +// Remotes: +// * DG11J1-3A / DG11J1-04 +// * DG11J1-91 +// +// Note: Smart, iFeel, AroundU, PowerSave, & Silent modes are unsupported. +// Advanced 6thSense, Dehumidify, & Sleep modes are not supported. +// FYI: +// Dim == !Light +// Jet == Super == Turbo +// + +#include "ir_Whirlpool.h" +#include +#ifndef ARDUINO +#include +#endif +#include "IRrecv.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" +#include "IRutils.h" + +// WW WW HH HH IIIII RRRRRR LL PPPPPP OOOOO OOOOO LL +// WW WW HH HH III RR RR LL PP PP OO OO OO OO LL +// WW W WW HHHHHHH III RRRRRR LL PPPPPP OO OO OO OO LL +// WW WWW WW HH HH III RR RR LL PP OO OO OO OO LL +// WW WW HH HH IIIII RR RR LLLLLLL PP OOOO0 OOOO0 LLLLLLL + +// Constants +// Ref: https://github.com/markszabo/IRremoteESP8266/issues/509 +const uint16_t kWhirlpoolAcHdrMark = 8950; +const uint16_t kWhirlpoolAcHdrSpace = 4484; +const uint16_t kWhirlpoolAcBitMark = 597; +const uint16_t kWhirlpoolAcOneSpace = 1649; +const uint16_t kWhirlpoolAcZeroSpace = 533; +const uint16_t kWhirlpoolAcGap = 7920; +const uint32_t kWhirlpoolAcMinGap = kDefaultMessageGap; // Just a guess. +const uint8_t kWhirlpoolAcSections = 3; + +#if SEND_WHIRLPOOL_AC +// Send a Whirlpool A/C message. +// +// Args: +// data: An array of bytes containing the IR command. +// nbytes: Nr. of bytes of data in the array. (>=kWhirlpoolAcStateLength) +// repeat: Nr. of times the message is to be repeated. (Default = 0). +// +// Status: ALPHA / Untested. +// +// Ref: +// https://github.com/markszabo/IRremoteESP8266/issues/509 +void IRsend::sendWhirlpoolAC(unsigned char data[], uint16_t nbytes, + uint16_t repeat) { + if (nbytes < kWhirlpoolAcStateLength) + return; // Not enough bytes to send a proper message. + for (uint16_t r = 0; r <= repeat; r++) { + // Section 1 + sendGeneric(kWhirlpoolAcHdrMark, kWhirlpoolAcHdrSpace, kWhirlpoolAcBitMark, + kWhirlpoolAcOneSpace, kWhirlpoolAcBitMark, + kWhirlpoolAcZeroSpace, kWhirlpoolAcBitMark, kWhirlpoolAcGap, + data, 6, // 6 bytes == 48 bits + 38000, // Complete guess of the modulation frequency. + false, 0, 50); + // Section 2 + sendGeneric(0, 0, kWhirlpoolAcBitMark, kWhirlpoolAcOneSpace, + kWhirlpoolAcBitMark, kWhirlpoolAcZeroSpace, kWhirlpoolAcBitMark, + kWhirlpoolAcGap, data + 6, 8, // 8 bytes == 64 bits + 38000, // Complete guess of the modulation frequency. + false, 0, 50); + // Section 3 + sendGeneric(0, 0, kWhirlpoolAcBitMark, kWhirlpoolAcOneSpace, + kWhirlpoolAcBitMark, kWhirlpoolAcZeroSpace, kWhirlpoolAcBitMark, + kWhirlpoolAcMinGap, data + 14, 7, // 7 bytes == 56 bits + 38000, // Complete guess of the modulation frequency. + false, 0, 50); + } +} +#endif // SEND_WHIRLPOOL_AC + +// Class for emulating a Whirlpool A/C remote. +// Decoding help from: +// @redmusicxd, @josh929800, @raducostea + +IRWhirlpoolAc::IRWhirlpoolAc(uint16_t pin) : _irsend(pin) { stateReset(); } + +void IRWhirlpoolAc::stateReset() { + for (uint8_t i = 2; i < kWhirlpoolAcStateLength; i++) remote_state[i] = 0x0; + remote_state[0] = 0x83; + remote_state[1] = 0x06; + remote_state[6] = 0x80; + _setTemp(kWhirlpoolAcAutoTemp); // Default to a sane value. +} + +void IRWhirlpoolAc::begin() { _irsend.begin(); } + +bool IRWhirlpoolAc::validChecksum(uint8_t state[], const uint16_t length) { + if (length > kWhirlpoolAcChecksumByte1 && + state[kWhirlpoolAcChecksumByte1] != + xorBytes(state + 2, kWhirlpoolAcChecksumByte1 - 1 - 2)) { + DPRINTLN("DEBUG: First Whirlpool AC checksum failed."); + return false; + } + if (length > kWhirlpoolAcChecksumByte2 && + state[kWhirlpoolAcChecksumByte2] != + xorBytes(state + kWhirlpoolAcChecksumByte1 + 1, + kWhirlpoolAcChecksumByte2 - kWhirlpoolAcChecksumByte1 - 1)) { + DPRINTLN("DEBUG: Second Whirlpool AC checksum failed."); + return false; + } + // State is too short to have a checksum or everything checked out. + return true; +} + +// Update the checksum for the internal state. +void IRWhirlpoolAc::checksum(uint16_t length) { + if (length >= kWhirlpoolAcChecksumByte1) + remote_state[kWhirlpoolAcChecksumByte1] = + xorBytes(remote_state + 2, kWhirlpoolAcChecksumByte1 - 1 - 2); + if (length >= kWhirlpoolAcChecksumByte2) + remote_state[kWhirlpoolAcChecksumByte2] = + xorBytes(remote_state + kWhirlpoolAcChecksumByte1 + 1, + kWhirlpoolAcChecksumByte2 - kWhirlpoolAcChecksumByte1 - 1); +} + +#if SEND_WHIRLPOOL_AC +void IRWhirlpoolAc::send(const uint16_t repeat, const bool calcchecksum) { + if (calcchecksum) checksum(); + _irsend.sendWhirlpoolAC(remote_state, kWhirlpoolAcStateLength, repeat); +} +#endif // SEND_WHIRLPOOL_AC + +uint8_t *IRWhirlpoolAc::getRaw(const bool calcchecksum) { + if (calcchecksum) checksum(); + return remote_state; +} + +void IRWhirlpoolAc::setRaw(const uint8_t new_code[], const uint16_t length) { + for (uint8_t i = 0; i < length && i < kWhirlpoolAcStateLength; i++) + remote_state[i] = new_code[i]; +} + +whirlpool_ac_remote_model_t IRWhirlpoolAc::getModel() { + if (remote_state[kWhirlpoolAcAltTempPos] & kWhirlpoolAcAltTempMask) + return DG11J191; + else + return DG11J13A; +} + +void IRWhirlpoolAc::setModel(const whirlpool_ac_remote_model_t model) { + switch (model) { + case DG11J191: + remote_state[kWhirlpoolAcAltTempPos] |= kWhirlpoolAcAltTempMask; + break; + case DG11J13A: + // FALL THRU + default: + remote_state[kWhirlpoolAcAltTempPos] &= ~kWhirlpoolAcAltTempMask; + } + _setTemp(_desiredtemp); // Different models have different temp values. +} + +// Return the temp. offset in deg C for the current model. +int8_t IRWhirlpoolAc::getTempOffset() { + switch (getModel()) { + case DG11J191: + return -2; + break; + default: + return 0; + } +} + +// Set the temp. in deg C +void IRWhirlpoolAc::_setTemp(const uint8_t temp, const bool remember) { + if (remember) _desiredtemp = temp; + int8_t offset = getTempOffset(); // Cache the min temp for the model. + uint8_t newtemp = std::max((uint8_t)(kWhirlpoolAcMinTemp + offset), temp); + newtemp = std::min((uint8_t)(kWhirlpoolAcMaxTemp + offset), newtemp); + remote_state[kWhirlpoolAcTempPos] = + (remote_state[kWhirlpoolAcTempPos] & ~kWhirlpoolAcTempMask) | + ((newtemp - (kWhirlpoolAcMinTemp + offset)) << 4); +} + +// Set the temp. in deg C +void IRWhirlpoolAc::setTemp(const uint8_t temp) { + _setTemp(temp); + setSuper(false); // Changing temp cancels Super/Jet mode. + setCommand(kWhirlpoolAcCommandTemp); +} + +// Return the set temp. in deg C +uint8_t IRWhirlpoolAc::getTemp() { + return ((remote_state[kWhirlpoolAcTempPos] & kWhirlpoolAcTempMask) >> 4) + + + kWhirlpoolAcMinTemp + getTempOffset(); +} + +void IRWhirlpoolAc::_setMode(const uint8_t mode) { + switch (mode) { + case kWhirlpoolAcAuto: + setFan(kWhirlpoolAcFanAuto); + _setTemp(kWhirlpoolAcAutoTemp, false); + setSleep(false); // Cancel sleep mode when in auto/6thsense mode. + // FALL THRU + case kWhirlpoolAcHeat: + case kWhirlpoolAcCool: + case kWhirlpoolAcDry: + case kWhirlpoolAcFan: + remote_state[kWhirlpoolAcModePos] &= ~kWhirlpoolAcModeMask; + remote_state[kWhirlpoolAcModePos] |= mode; + setCommand(kWhirlpoolAcCommandMode); + break; + default: + return; + } + if (mode == kWhirlpoolAcAuto) setCommand(kWhirlpoolAcCommand6thSense); +} + +void IRWhirlpoolAc::setMode(const uint8_t mode) { + setSuper(false); // Changing mode cancels Super/Jet mode. + _setMode(mode); +} + +uint8_t IRWhirlpoolAc::getMode() { + return remote_state[kWhirlpoolAcModePos] & kWhirlpoolAcModeMask; +} + +void IRWhirlpoolAc::setFan(const uint8_t speed) { + switch (speed) { + case kWhirlpoolAcFanAuto: + case kWhirlpoolAcFanLow: + case kWhirlpoolAcFanMedium: + case kWhirlpoolAcFanHigh: + remote_state[kWhirlpoolAcFanPos] = + (remote_state[kWhirlpoolAcFanPos] & ~kWhirlpoolAcFanMask) | speed; + setSuper(false); // Changing fan speed cancels Super/Jet mode. + setCommand(kWhirlpoolAcCommandFanSpeed); + break; + } +} + +uint8_t IRWhirlpoolAc::getFan() { + return remote_state[kWhirlpoolAcFanPos] & kWhirlpoolAcFanMask; +} + +void IRWhirlpoolAc::setSwing(const bool on) { + if (on) { + remote_state[kWhirlpoolAcFanPos] |= kWhirlpoolAcSwing1Mask; + remote_state[kWhirlpoolAcOffTimerPos] |= kWhirlpoolAcSwing2Mask; + } else { + remote_state[kWhirlpoolAcFanPos] &= ~kWhirlpoolAcSwing1Mask; + remote_state[kWhirlpoolAcOffTimerPos] &= ~kWhirlpoolAcSwing2Mask; + } + setCommand(kWhirlpoolAcCommandSwing); +} + +bool IRWhirlpoolAc::getSwing() { + return (remote_state[kWhirlpoolAcFanPos] & kWhirlpoolAcSwing1Mask) && + (remote_state[kWhirlpoolAcOffTimerPos] & kWhirlpoolAcSwing2Mask); +} + +void IRWhirlpoolAc::setLight(const bool on) { + if (on) + remote_state[kWhirlpoolAcClockPos] &= ~kWhirlpoolAcLightMask; + else + remote_state[kWhirlpoolAcClockPos] |= kWhirlpoolAcLightMask; +} + +bool IRWhirlpoolAc::getLight() { + return !(remote_state[kWhirlpoolAcClockPos] & kWhirlpoolAcLightMask); +} + +void IRWhirlpoolAc::setTime(const uint16_t pos, + const uint16_t minspastmidnight) { + // Hours + remote_state[pos] &= ~kWhirlpoolAcHourMask; + remote_state[pos] |= (minspastmidnight / 60) % 24; + // Minutes + remote_state[pos + 1] &= ~kWhirlpoolAcMinuteMask; + remote_state[pos + 1] |= minspastmidnight % 60; +} + +uint16_t IRWhirlpoolAc::getTime(const uint16_t pos) { + return (remote_state[pos] & kWhirlpoolAcHourMask) * 60 + + (remote_state[pos + 1] & kWhirlpoolAcMinuteMask); +} + +bool IRWhirlpoolAc::isTimerEnabled(const uint16_t pos) { + return remote_state[pos - 1] & kWhirlpoolAcTimerEnableMask; +} + +void IRWhirlpoolAc::enableTimer(const uint16_t pos, const bool state) { + if (state) + remote_state[pos - 1] |= kWhirlpoolAcTimerEnableMask; + else + remote_state[pos - 1] &= ~kWhirlpoolAcTimerEnableMask; +} + +void IRWhirlpoolAc::setClock(const uint16_t minspastmidnight) { + setTime(kWhirlpoolAcClockPos, minspastmidnight); +} + +uint16_t IRWhirlpoolAc::getClock() { return getTime(kWhirlpoolAcClockPos); } + +void IRWhirlpoolAc::setOffTimer(const uint16_t minspastmidnight) { + setTime(kWhirlpoolAcOffTimerPos, minspastmidnight); +} + +uint16_t IRWhirlpoolAc::getOffTimer() { + return getTime(kWhirlpoolAcOffTimerPos); +} + +bool IRWhirlpoolAc::isOffTimerEnabled() { + return isTimerEnabled(kWhirlpoolAcOffTimerPos); +} + +void IRWhirlpoolAc::enableOffTimer(const bool state) { + enableTimer(kWhirlpoolAcOffTimerPos, state); + setCommand(kWhirlpoolAcCommandOffTimer); +} + +void IRWhirlpoolAc::setOnTimer(const uint16_t minspastmidnight) { + setTime(kWhirlpoolAcOnTimerPos, minspastmidnight); +} + +uint16_t IRWhirlpoolAc::getOnTimer() { return getTime(kWhirlpoolAcOnTimerPos); } + +bool IRWhirlpoolAc::isOnTimerEnabled() { + return isTimerEnabled(kWhirlpoolAcOnTimerPos); +} + +void IRWhirlpoolAc::enableOnTimer(const bool state) { + enableTimer(kWhirlpoolAcOnTimerPos, state); + setCommand(kWhirlpoolAcCommandOnTimer); +} + +void IRWhirlpoolAc::setPowerToggle(const bool on) { + if (on) + remote_state[kWhirlpoolAcPowerTogglePos] |= kWhirlpoolAcPowerToggleMask; + else + remote_state[kWhirlpoolAcPowerTogglePos] &= ~kWhirlpoolAcPowerToggleMask; + setSuper(false); // Changing power cancels Super/Jet mode. + setCommand(kWhirlpoolAcCommandPower); +} + +bool IRWhirlpoolAc::getPowerToggle() { + return remote_state[kWhirlpoolAcPowerTogglePos] & kWhirlpoolAcPowerToggleMask; +} + +uint8_t IRWhirlpoolAc::getCommand() { + return remote_state[kWhirlpoolAcCommandPos]; +} + +void IRWhirlpoolAc::setSleep(const bool on) { + if (on) { + remote_state[kWhirlpoolAcSleepPos] |= kWhirlpoolAcSleepMask; + setFan(kWhirlpoolAcFanLow); + } else { + remote_state[kWhirlpoolAcSleepPos] &= ~kWhirlpoolAcSleepMask; + } + setCommand(kWhirlpoolAcCommandSleep); +} + +bool IRWhirlpoolAc::getSleep() { + return remote_state[kWhirlpoolAcSleepPos] & kWhirlpoolAcSleepMask; +} + +// AKA Jet/Turbo mode. +void IRWhirlpoolAc::setSuper(const bool on) { + if (on) { + setFan(kWhirlpoolAcFanHigh); + switch (getMode()) { + case kWhirlpoolAcHeat: + setTemp(kWhirlpoolAcMaxTemp + getTempOffset()); + break; + case kWhirlpoolAcCool: + default: + setTemp(kWhirlpoolAcMinTemp + getTempOffset()); + setMode(kWhirlpoolAcCool); + break; + } + remote_state[kWhirlpoolAcSuperPos] |= kWhirlpoolAcSuperMask; + } else { + remote_state[kWhirlpoolAcSuperPos] &= ~kWhirlpoolAcSuperMask; + } + setCommand(kWhirlpoolAcCommandSuper); +} + +bool IRWhirlpoolAc::getSuper() { + return remote_state[kWhirlpoolAcSuperPos] & kWhirlpoolAcSuperMask; +} + +void IRWhirlpoolAc::setCommand(const uint8_t code) { + remote_state[kWhirlpoolAcCommandPos] = code; +} + +// Convert a standard A/C mode into its native mode. +uint8_t IRWhirlpoolAc::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kWhirlpoolAcCool; + case stdAc::opmode_t::kHeat: + return kWhirlpoolAcHeat; + case stdAc::opmode_t::kDry: + return kWhirlpoolAcDry; + case stdAc::opmode_t::kFan: + return kWhirlpoolAcFan; + default: + return kWhirlpoolAcAuto; + } +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRWhirlpoolAc::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: + return kWhirlpoolAcFanLow; + case stdAc::fanspeed_t::kMedium: + return kWhirlpoolAcFanMedium; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: + return kWhirlpoolAcFanHigh; + default: + return kWhirlpoolAcFanAuto; + } +} + +#ifdef ARDUINO +String IRWhirlpoolAc::timeToString(const uint16_t minspastmidnight) { + String result = ""; +#else +std::string IRWhirlpoolAc::timeToString(const uint16_t minspastmidnight) { + std::string result = ""; +#endif // ARDUINO + uint8_t hours = minspastmidnight / 60; + if (hours < 10) result += '0'; + result += uint64ToString(hours); + result += ':'; + uint8_t mins = minspastmidnight % 60; + if (mins < 10) result += '0'; + result += uint64ToString(mins); + return result; +} + +// Convert the internal state into a human readable string. +#ifdef ARDUINO +String IRWhirlpoolAc::toString() { + String result = ""; +#else +std::string IRWhirlpoolAc::toString() { + std::string result = ""; +#endif // ARDUINO + result += F("Model: "); + result += uint64ToString(getModel()); + switch (getModel()) { + case DG11J191: + result += F(" (DG11J191)"); + break; + case DG11J13A: + result += F(" (DG11J13A)"); + break; + default: + result += F(" (UNKNOWN)"); + } + result += F(", Power toggle: "); + if (getPowerToggle()) + result += F("On"); + else + result += F("Off"); + result += F(", Mode: "); + result += uint64ToString(getMode()); + switch (getMode()) { + case kWhirlpoolAcHeat: + result += F(" (HEAT)"); + break; + case kWhirlpoolAcAuto: + result += F(" (AUTO)"); + break; + case kWhirlpoolAcCool: + result += F(" (COOL)"); + break; + case kWhirlpoolAcDry: + result += F(" (DRY)"); + break; + case kWhirlpoolAcFan: + result += F(" (FAN)"); + break; + default: + result += F(" (UNKNOWN)"); + } + result += F(", Temp: "); + result += uint64ToString(getTemp()); + result += F("C, Fan: "); + result += uint64ToString(getFan()); + switch (getFan()) { + case kWhirlpoolAcFanAuto: + result += F(" (AUTO)"); + break; + case kWhirlpoolAcFanHigh: + result += F(" (HIGH)"); + break; + case kWhirlpoolAcFanMedium: + result += F(" (MEDIUM)"); + break; + case kWhirlpoolAcFanLow: + result += F(" (LOW)"); + break; + default: + result += F(" (UNKNOWN)"); + break; + } + result += F(", Swing: "); + if (getSwing()) + result += F("On"); + else + result += F("Off"); + result += F(", Light: "); + if (getLight()) + result += F("On"); + else + result += F("Off"); + result += F(", Clock: "); + result += timeToString(getClock()); + result += F(", On Timer: "); + if (isOnTimerEnabled()) + result += timeToString(getOnTimer()); + else + result += F("Off"); + result += F(", Off Timer: "); + if (isOffTimerEnabled()) + result += timeToString(getOffTimer()); + else + result += F("Off"); + result += F(", Sleep: "); + if (getSleep()) + result += F("On"); + else + result += F("Off"); + result += F(", Super: "); + if (getSuper()) + result += F("On"); + else + result += F("Off"); + result += F(", Command: "); + result += uint64ToString(getCommand()); + switch (getCommand()) { + case kWhirlpoolAcCommandLight: + result += F(" (LIGHT)"); + break; + case kWhirlpoolAcCommandPower: + result += F(" (POWER)"); + break; + case kWhirlpoolAcCommandTemp: + result += F(" (TEMP)"); + break; + case kWhirlpoolAcCommandSleep: + result += F(" (SLEEP)"); + break; + case kWhirlpoolAcCommandSuper: + result += F(" (SUPER)"); + break; + case kWhirlpoolAcCommandOnTimer: + result += F(" (ONTIMER)"); + break; + case kWhirlpoolAcCommandMode: + result += F(" (MODE)"); + break; + case kWhirlpoolAcCommandSwing: + result += F(" (SWING)"); + break; + case kWhirlpoolAcCommandIFeel: + result += F(" (IFEEL)"); + break; + case kWhirlpoolAcCommandFanSpeed: + result += F(" (FANSPEED)"); + break; + case kWhirlpoolAcCommand6thSense: + result += F(" (6THSENSE)"); + break; + case kWhirlpoolAcCommandOffTimer: + result += F(" (OFFTIMER)"); + break; + default: + result += F(" (UNKNOWN)"); + break; + } + return result; +} + +#if DECODE_WHIRLPOOL_AC +// Decode the supplied Whirlpool A/C message. +// +// Args: +// results: Ptr to the data to decode and where to store the decode result. +// nbits: The number of data bits to expect. Typically kWhirlpoolAcBits +// strict: Flag indicating if we should perform strict matching. +// Returns: +// boolean: True if it can decode it, false if it can't. +// +// Status: STABLE / Working as intended. +// +// +// Ref: +// https://github.com/markszabo/IRremoteESP8266/issues/509 +bool IRrecv::decodeWhirlpoolAC(decode_results *results, uint16_t nbits, + bool strict) { + if (results->rawlen < 2 * nbits + 4 + kHeader + kFooter - 1) + return false; // Can't possibly be a valid Whirlpool A/C message. + if (strict) { + if (nbits != kWhirlpoolAcBits) return false; + } + + uint16_t offset = kStartOffset; + uint16_t dataBitsSoFar = 0; + uint16_t i = 0; + match_result_t data_result; + uint8_t sectionSize[kWhirlpoolAcSections] = {6, 8, 7}; + + // Header + if (!matchMark(results->rawbuf[offset++], kWhirlpoolAcHdrMark)) return false; + if (!matchSpace(results->rawbuf[offset++], kWhirlpoolAcHdrSpace)) + return false; + + // Data Section + // Keep reading bytes until we either run out of section or state to fill. + for (uint8_t section = 0, pos = 0; section < kWhirlpoolAcSections; + section++) { + pos += sectionSize[section]; + for (; offset <= results->rawlen - 16 && i < pos; + i++, dataBitsSoFar += 8, offset += data_result.used) { + data_result = + matchData(&(results->rawbuf[offset]), 8, kWhirlpoolAcBitMark, + kWhirlpoolAcOneSpace, kWhirlpoolAcBitMark, + kWhirlpoolAcZeroSpace, kTolerance, kMarkExcess, false); + if (data_result.success == false) break; // Fail + // Data is in LSB order. We need to reverse it. + results->state[i] = (uint8_t)data_result.data; + } + // Section Footer + if (!matchMark(results->rawbuf[offset++], kWhirlpoolAcBitMark)) + return false; + if (section < kWhirlpoolAcSections - 1) { // Inter-section gaps. + if (!matchSpace(results->rawbuf[offset++], kWhirlpoolAcGap)) return false; + } else { // Last section / End of message gap. + if (offset <= results->rawlen && + !matchAtLeast(results->rawbuf[offset++], kWhirlpoolAcGap)) + return false; + } + } + + // Compliance + if (strict) { + // Re-check we got the correct size/length due to the way we read the data. + if (dataBitsSoFar != kWhirlpoolAcBits) return false; + if (!IRWhirlpoolAc::validChecksum(results->state, dataBitsSoFar / 8)) + return false; + } + + // Success + results->decode_type = WHIRLPOOL_AC; + results->bits = dataBitsSoFar; + // No need to record the state as we stored it as we decoded it. + // As we use result->state, we don't record value, address, or command as it + // is a union data type. + return true; +} +#endif // WHIRLPOOL_AC diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Whirlpool.h b/lib/IRremoteESP8266-2.6.0/src/ir_Whirlpool.h new file mode 100644 index 000000000..9604d025c --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Whirlpool.h @@ -0,0 +1,167 @@ +// Whirlpool A/C +// +// Copyright 2018 David Conran + +#ifndef IR_WHIRLPOOL_H_ +#define IR_WHIRLPOOL_H_ + +#define __STDC_LIMIT_MACROS +#include +#ifndef UNIT_TEST +#include +#else +#include +#endif +#include "IRremoteESP8266.h" +#include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif + +// WW WW HH HH IIIII RRRRRR LL PPPPPP OOOOO OOOOO LL +// WW WW HH HH III RR RR LL PP PP OO OO OO OO LL +// WW W WW HHHHHHH III RRRRRR LL PPPPPP OO OO OO OO LL +// WW WWW WW HH HH III RR RR LL PP OO OO OO OO LL +// WW WW HH HH IIIII RR RR LLLLLLL PP OOOO0 OOOO0 LLLLLLL + +// Ref: +// https://github.com/markszabo/IRremoteESP8266/issues/509 + +// Constants +const uint8_t kWhirlpoolAcChecksumByte1 = 13; +const uint8_t kWhirlpoolAcChecksumByte2 = kWhirlpoolAcStateLength - 1; +const uint8_t kWhirlpoolAcHeat = 0; +const uint8_t kWhirlpoolAcAuto = 1; +const uint8_t kWhirlpoolAcCool = 2; +const uint8_t kWhirlpoolAcDry = 3; +const uint8_t kWhirlpoolAcFan = 4; +const uint8_t kWhirlpoolAcModeMask = 0b00000111; +const uint8_t kWhirlpoolAcModePos = 3; +const uint8_t kWhirlpoolAcFanAuto = 0; +const uint8_t kWhirlpoolAcFanHigh = 1; +const uint8_t kWhirlpoolAcFanMedium = 2; +const uint8_t kWhirlpoolAcFanLow = 3; +const uint8_t kWhirlpoolAcFanMask = 0b00000011; +const uint8_t kWhirlpoolAcFanPos = 2; +const uint8_t kWhirlpoolAcMinTemp = 18; // 18C (DG11J1-3A), 16C (DG11J1-91) +const uint8_t kWhirlpoolAcMaxTemp = 32; // 32C (DG11J1-3A), 30C (DG11J1-91) +const uint8_t kWhirlpoolAcAutoTemp = 23; // 23C +const uint8_t kWhirlpoolAcTempMask = 0b11110000; +const uint8_t kWhirlpoolAcTempPos = 3; +const uint8_t kWhirlpoolAcSwing1Mask = 0b10000000; +const uint8_t kWhirlpoolAcSwing2Mask = 0b01000000; +const uint8_t kWhirlpoolAcLightMask = 0b00100000; +const uint8_t kWhirlpoolAcPowerToggleMask = 0b00000100; +const uint8_t kWhirlpoolAcPowerTogglePos = 2; +const uint8_t kWhirlpoolAcSleepMask = 0b00001000; +const uint8_t kWhirlpoolAcSleepPos = 2; +const uint8_t kWhirlpoolAcSuperMask = 0b10010000; +const uint8_t kWhirlpoolAcSuperPos = 5; +const uint8_t kWhirlpoolAcHourMask = 0b00011111; +const uint8_t kWhirlpoolAcMinuteMask = 0b00111111; +const uint8_t kWhirlpoolAcTimerEnableMask = 0b10000000; +const uint8_t kWhirlpoolAcClockPos = 6; +const uint8_t kWhirlpoolAcOffTimerPos = 8; +const uint8_t kWhirlpoolAcOnTimerPos = 10; +const uint8_t kWhirlpoolAcCommandPos = 15; +const uint8_t kWhirlpoolAcCommandLight = 0x00; +const uint8_t kWhirlpoolAcCommandPower = 0x01; +const uint8_t kWhirlpoolAcCommandTemp = 0x02; +const uint8_t kWhirlpoolAcCommandSleep = 0x03; +const uint8_t kWhirlpoolAcCommandSuper = 0x04; +const uint8_t kWhirlpoolAcCommandOnTimer = 0x05; +const uint8_t kWhirlpoolAcCommandMode = 0x06; +const uint8_t kWhirlpoolAcCommandSwing = 0x07; +const uint8_t kWhirlpoolAcCommandIFeel = 0x0D; +const uint8_t kWhirlpoolAcCommandFanSpeed = 0x11; +const uint8_t kWhirlpoolAcCommand6thSense = 0x17; +const uint8_t kWhirlpoolAcCommandOffTimer = 0x1D; +const uint8_t kWhirlpoolAcAltTempMask = 0b00001000; +const uint8_t kWhirlpoolAcAltTempPos = 18; + +enum whirlpool_ac_remote_model_t { + DG11J13A = 1, // DG11J1-04 too + DG11J191, +}; + +// Classes +class IRWhirlpoolAc { + public: + explicit IRWhirlpoolAc(uint16_t pin); + + void stateReset(); +#if SEND_WHIRLPOOL_AC + void send(const uint16_t repeat = kWhirlpoolAcDefaultRepeat, + const bool calcchecksum = true); +#endif // SEND_WHIRLPOOL_AC + void begin(); + void on(); + void off(); + void setPowerToggle(const bool on); + bool getPowerToggle(); + void setSleep(const bool on); + bool getSleep(); + void setSuper(const bool on); + bool getSuper(); + void setTemp(const uint8_t temp); + uint8_t getTemp(); + void setFan(const uint8_t speed); + uint8_t getFan(); + void setMode(const uint8_t mode); + uint8_t getMode(); + void setSwing(const bool on); + bool getSwing(); + void setLight(const bool on); + bool getLight(); + uint16_t getClock(); + void setClock(const uint16_t minspastmidnight); + uint16_t getOnTimer(); + void setOnTimer(const uint16_t minspastmidnight); + void enableOnTimer(const bool state); + bool isOnTimerEnabled(); + uint16_t getOffTimer(); + void setOffTimer(const uint16_t minspastmidnight); + void enableOffTimer(const bool state); + bool isOffTimerEnabled(); + void setCommand(const uint8_t code); + uint8_t getCommand(); + whirlpool_ac_remote_model_t getModel(); + void setModel(const whirlpool_ac_remote_model_t model); + uint8_t* getRaw(const bool calcchecksum = true); + void setRaw(const uint8_t new_code[], + const uint16_t length = kWhirlpoolAcStateLength); + static bool validChecksum(uint8_t state[], + const uint16_t length = kWhirlpoolAcStateLength); + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); +#ifdef ARDUINO + String toString(); +#else + std::string toString(); +#endif +#ifndef UNIT_TEST + + private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif + // The state of the IR remote in IR code form. + uint8_t remote_state[kWhirlpoolAcStateLength]; + uint8_t _desiredtemp; + void checksum(const uint16_t length = kWhirlpoolAcStateLength); + uint16_t getTime(const uint16_t pos); + void setTime(const uint16_t pos, const uint16_t minspastmidnight); + bool isTimerEnabled(const uint16_t pos); + void enableTimer(const uint16_t pos, const bool state); + void _setTemp(const uint8_t temp, const bool remember = true); + void _setMode(const uint8_t mode); + int8_t getTempOffset(); +#ifdef ARDUINO + String timeToString(uint16_t minspastmidnight); +#else + std::string timeToString(uint16_t minspastmidnight); +#endif +}; + +#endif // IR_WHIRLPOOL_H_ diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Whynter.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Whynter.cpp similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Whynter.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_Whynter.cpp diff --git a/lib/IRremoteESP8266-2.6.0/test/IRac_test.cpp b/lib/IRremoteESP8266-2.6.0/test/IRac_test.cpp new file mode 100644 index 000000000..39c17a84b --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/test/IRac_test.cpp @@ -0,0 +1,865 @@ +// Copyright 2019 David Conran + +#include "ir_Argo.h" +#include "ir_Daikin.h" +#include "ir_Fujitsu.h" +#include "ir_Gree.h" +#include "ir_Haier.h" +#include "ir_Hitachi.h" +#include "ir_Kelvinator.h" +#include "ir_Midea.h" +#include "ir_Mitsubishi.h" +#include "ir_MitsubishiHeavy.h" +#include "ir_Panasonic.h" +#include "ir_Samsung.h" +#include "ir_Tcl.h" +#include "ir_Teco.h" +#include "ir_Toshiba.h" +#include "ir_Trotec.h" +#include "ir_Vestel.h" +#include "ir_Whirlpool.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "gtest/gtest.h" + +// Tests for IRac class. + +TEST(TestIRac, Argo) { + IRArgoAC ac(0); + IRac irac(0); + + ac.begin(); + irac.argo(&ac, + true, // Power + stdAc::opmode_t::kHeat, // Mode + 21, // Celsius + stdAc::fanspeed_t::kHigh, // Fan speed + stdAc::swingv_t::kOff, // Veritcal swing + false, // Turbo + -1); // Sleep + EXPECT_TRUE(ac.getPower()); + EXPECT_EQ(1, ac.getMode()); + EXPECT_EQ(21, ac.getTemp()); + EXPECT_EQ(kArgoFlapAuto, ac.getFlap()); + EXPECT_FALSE(ac.getMax()); // Turbo + EXPECT_FALSE(ac.getNight()); // Sleep +} + +TEST(TestIRac, Coolix) { + IRCoolixAC ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On, Mode: 3 (HEAT), Fan: 1 (MAX), Temp: 21C, Zone Follow: Off, " + "Sensor Temp: Ignored"; + + ac.begin(); + irac.coolix(&ac, + true, // Power + stdAc::opmode_t::kHeat, // Mode + 21, // Celsius + stdAc::fanspeed_t::kHigh, // Fan speed + stdAc::swingv_t::kOff, // Veritcal swing + stdAc::swingh_t::kOff, // Horizontal swing + false, // Turbo + false, // Light + false, // Clean + -1); // Sleep + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(COOLIX, ac._irsend.capture.decode_type); + ASSERT_EQ(kCoolixBits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.value); + ASSERT_EQ(expected, ac.toString()); +} + +TEST(TestIRac, Daikin) { + IRDaikinESP ac(0); + IRac irac(0); + char expected[] = + "Power: On, Mode: 3 (COOL), Temp: 19C, Fan: 2, Powerful: Off, " + "Quiet: Off, Sensor: Off, Eye: Off, Mold: On, Comfort: Off, " + "Swing (Horizontal): Off, Swing (Vertical): Off, " + "Current Time: 0:00, On Time: Off, Off Time: Off"; + + ac.begin(); + irac.daikin(&ac, + true, // Power + stdAc::opmode_t::kCool, // Mode + 19, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kOff, // Veritcal swing + stdAc::swingh_t::kOff, // Horizontal swing + false, // Quiet + false, // Turbo + true, // Filter + true); // Clean + ASSERT_EQ(expected, ac.toString()); +} + +TEST(TestIRac, Daikin2) { + IRDaikin2 ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On, Mode: 3 (COOL), Temp: 19C, Fan: 2, Swing (V): 14 (Auto), " + "Swing (H): 0, Clock: 0:00, On Time: Off, Off Time: Off, " + "Sleep Time: Off, Beep: 1 (Quiet), Light: 1 (Bright), Mold: On, " + "Clean: Off, Fresh Air: Off, Eye: Off, Eye Auto: Off, Quiet: Off, " + "Powerful: Off, Purify: On, Econo: Off"; + + ac.begin(); + irac.daikin2(&ac, + true, // Power + stdAc::opmode_t::kCool, // Mode + 19, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kOff, // Veritcal swing + stdAc::swingh_t::kOff, // Horizontal swing + false, // Quiet + false, // Turbo + true, // Light + false, // Econo + true, // Filter + true, // Clean (aka Mold) + -1, // Sleep time + -1); // Current time + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(DAIKIN2, ac._irsend.capture.decode_type); + ASSERT_EQ(kDaikin2Bits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state); + ASSERT_EQ(expected, ac.toString()); +} + +TEST(TestIRac, Daikin216) { + IRDaikin216 ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On, Mode: 4 (HEAT), Temp: 31C, Fan: 11 (QUIET), " + "Swing (Horizontal): On, Swing (Vertical): On, Quiet: On"; + + ac.begin(); + irac.daikin216(&ac, + true, // Power + stdAc::opmode_t::kHeat, // Mode + 31, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kAuto, // Veritcal swing + stdAc::swingh_t::kLeft, // Horizontal swing + true); // Quiet + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(DAIKIN216, ac._irsend.capture.decode_type); + ASSERT_EQ(kDaikin216Bits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state); + ASSERT_EQ(expected, ac.toString()); +} + +TEST(TestIRac, Fujitsu) { + IRFujitsuAC ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On, Mode: 1 (COOL), Temp: 19C, Fan: 2 (MED), " + "Swing: Off, Command: N/A"; + + ac.begin(); + irac.fujitsu(&ac, + ARDB1, // Model + true, // Power + stdAc::opmode_t::kCool, // Mode + 19, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kOff, // Veritcal swing + stdAc::swingh_t::kOff, // Horizontal swing + false); // Quiet + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(FUJITSU_AC, ac._irsend.capture.decode_type); + ASSERT_EQ(kFujitsuAcBits - 8, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state, ac._irsend.capture.bits / 8); + ASSERT_EQ(expected, ac.toString()); + + ac._irsend.reset(); + irac.fujitsu(&ac, + ARRAH2E, // Model + true, // Power + stdAc::opmode_t::kCool, // Mode + 19, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kOff, // Veritcal swing + stdAc::swingh_t::kOff, // Horizontal swing + false); // Quiet + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(FUJITSU_AC, ac._irsend.capture.decode_type); + ASSERT_EQ(kFujitsuAcBits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state, ac._irsend.capture.bits / 8); + ASSERT_EQ(expected, ac.toString()); +} + +TEST(TestIRac, Gree) { + IRGreeAC ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On, Mode: 1 (COOL), Temp: 22C, Fan: 2, Turbo: Off, XFan: On, " + "Light: On, Sleep: On, Swing Vertical Mode: Manual, " + "Swing Vertical Pos: 3"; + + ac.begin(); + irac.gree(&ac, + true, // Power + stdAc::opmode_t::kCool, // Mode + 22, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kHigh, // Veritcal swing + false, // Turbo + true, // Light + true, // Clean (aka Mold/XFan) + 8 * 60 + 0); // Sleep time + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(GREE, ac._irsend.capture.decode_type); + ASSERT_EQ(kGreeBits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state); + ASSERT_EQ(expected, ac.toString()); +} + +TEST(TestIRac, Haier) { + IRHaierAC ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Command: 1 (On), Mode: 3 (HEAT), Temp: 24C, Fan: 2, Swing: 1 (Up), " + "Sleep: On, Health: On, Current Time: 13:45, On Timer: Off, " + "Off Timer: Off"; + + ac.begin(); + irac.haier(&ac, + true, // Power + stdAc::opmode_t::kCool, // Mode + 24, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kHigh, // Veritcal swing + true, // Filter + 8 * 60 + 0, // Sleep time + 13 * 60 + 45); // Clock + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(HAIER_AC, ac._irsend.capture.decode_type); + ASSERT_EQ(kHaierACBits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state); + ASSERT_EQ(expected, ac.toString()); +} + + +TEST(TestIRac, HaierYrwo2) { + IRHaierACYRW02 ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On, Button: 5 (Power), Mode: 2 (Cool), Temp: 23C, Fan: 4 (Med), " + "Turbo: 1 (High), Swing: 1 (Top), Sleep: On, Health: On"; + + ac.begin(); + irac.haierYrwo2(&ac, + true, // Power + stdAc::opmode_t::kCool, // Mode + 23, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kHigh, // Veritcal swing + true, // Turbo + true, // Filter + 8 * 60 + 0); // Sleep time + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(HAIER_AC_YRW02, ac._irsend.capture.decode_type); + ASSERT_EQ(kHaierACYRW02Bits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state); + ASSERT_EQ(expected, ac.toString()); +} + +TEST(TestIRac, Hitachi) { + IRHitachiAc ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On, Mode: 2 (AUTO), Temp: 22C, Fan: 3 (UNKNOWN), " + "Swing (Vertical): Off, Swing (Horizontal): On"; + + ac.begin(); + irac.hitachi(&ac, + true, // Power + stdAc::opmode_t::kAuto, // Mode + 22, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kOff, // Veritcal swing + stdAc::swingh_t::kAuto); // Horizontal swing + + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(HITACHI_AC, ac._irsend.capture.decode_type); + ASSERT_EQ(kHitachiAcBits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state); + ASSERT_EQ(expected, ac.toString()); +} + +TEST(TestIRac, Kelvinator) { + IRKelvinatorAC ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On, Mode: 1 (COOL), Temp: 19C, Fan: 3, Turbo: Off, Quiet: Off, " + "XFan: On, IonFilter: On, Light: On, Swing (Horizontal): Off, " + "Swing (Vertical): Off"; + + ac.begin(); + irac.kelvinator(&ac, + true, // Power + stdAc::opmode_t::kCool, // Mode + 19, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kOff, // Veritcal swing + stdAc::swingh_t::kOff, // Horizontal swing + false, // Quiet + false, // Turbo + true, // Light + true, // Filter + true); // Clean + + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(KELVINATOR, ac._irsend.capture.decode_type); + ASSERT_EQ(kKelvinatorBits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state); + ASSERT_EQ(expected, ac.toString()); +} + +TEST(TestIRac, Midea) { + IRMideaAC ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On, Mode: 1 (DRY), Temp: 27C/81F, Fan: 2 (MED), Sleep: On"; + + ac.begin(); + irac.midea(&ac, + true, // Power + stdAc::opmode_t::kDry, // Mode + 27, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + 8 * 60 + 0); // Sleep time + + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(MIDEA, ac._irsend.capture.decode_type); + ASSERT_EQ(kMideaBits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.value); + ASSERT_EQ(expected, ac.toString()); +} + +TEST(TestIRac, Mitsubishi) { + IRMitsubishiAC ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On (COOL), Temp: 20C, FAN: 2, VANE: AUTO, Time: 14:30, " + "On timer: 00:00, Off timer: 00:00, Timer: -"; + + ac.begin(); + irac.mitsubishi(&ac, + true, // Power + stdAc::opmode_t::kCool, // Mode + 20, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kOff, // Veritcal swing + false, // Silent + 14 * 60 + 35); // Clock + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(MITSUBISHI_AC, ac._irsend.capture.decode_type); + ASSERT_EQ(kMitsubishiACBits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state); + ASSERT_EQ(expected, ac.toString()); +} + +TEST(TestIRac, MitsubishiHeavy88) { + IRMitsubishiHeavy88Ac ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On, Mode: 1 (Cool), Temp: 21C, Fan: 3 (Med), " + "Swing (V): 16 (Auto), Swing (H): 0 (Off), Turbo: Off, Econo: Off, " + "3D: Off, Clean: On"; + + ac.begin(); + irac.mitsubishiHeavy88(&ac, + true, // Power + stdAc::opmode_t::kCool, // Mode + 21, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kAuto, // Veritcal swing + stdAc::swingh_t::kOff, // Horizontal swing + false, // Turbo + false, // Econo + true); // Clean + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(MITSUBISHI_HEAVY_88, ac._irsend.capture.decode_type); + ASSERT_EQ(kMitsubishiHeavy88Bits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state); + ASSERT_EQ(expected, ac.toString()); +} + +TEST(TestIRac, MitsubishiHeavy152) { + IRMitsubishiHeavy152Ac ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On, Mode: 1 (Cool), Temp: 20C, Fan: 6 (Econo), " + "Swing (V): 6 (Off), Swing (H): 0 (Auto), Silent: On, Turbo: Off, " + "Econo: On, Night: On, Filter: On, 3D: Off, Clean: Off"; + + ac.begin(); + irac.mitsubishiHeavy152(&ac, + true, // Power + stdAc::opmode_t::kCool, // Mode + 20, // Celsius + stdAc::fanspeed_t::kLow, // Fan speed + stdAc::swingv_t::kOff, // Veritcal swing + stdAc::swingh_t::kAuto, // Horizontal swing + true, // Silent + false, // Turbo + true, // Econo + true, // Filter + false, // Clean + 8 * 60); // Sleep + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(MITSUBISHI_HEAVY_152, ac._irsend.capture.decode_type); + ASSERT_EQ(kMitsubishiHeavy152Bits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state); + ASSERT_EQ(expected, ac.toString()); +} + +TEST(TestIRac, Panasonic) { + IRPanasonicAc ac(0); + IRac irac(0); + IRrecv capture(0); + char expected_nke[] = + "Model: 2 (NKE), Power: On, Mode: 4 (HEAT), Temp: 28C, Fan: 2 (UNKNOWN), " + "Swing (Vertical): 15 (AUTO), Swing (Horizontal): 6 (Middle), Quiet: On, " + "Powerful: Off, Clock: 19:17, On Timer: Off, Off Timer: Off"; + + ac.begin(); + irac.panasonic(&ac, + kPanasonicNke, // Model + true, // Power + stdAc::opmode_t::kHeat, // Mode + 28, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kAuto, // Veritcal swing + stdAc::swingh_t::kLeft, // Horizontal swing + true, // Quiet + false, // Turbo + 19 * 60 + 17); // Clock + ASSERT_EQ(expected_nke, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(PANASONIC_AC, ac._irsend.capture.decode_type); + ASSERT_EQ(kPanasonicAcBits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state); + ASSERT_EQ(expected_nke, ac.toString()); + + char expected_dke[] = + "Model: 3 (DKE), Power: On, Mode: 3 (COOL), Temp: 18C, Fan: 4 (MAX), " + "Swing (Vertical): 1 (Full Up), Swing (Horizontal): 6 (Middle), " + "Quiet: Off, Powerful: On, Clock: 19:17, On Timer: Off, Off Timer: Off"; + ac._irsend.reset(); + irac.panasonic(&ac, + kPanasonicDke, // Model + true, // Power + stdAc::opmode_t::kCool, // Mode + 18, // Celsius + stdAc::fanspeed_t::kMax, // Fan speed + stdAc::swingv_t::kHigh, // Veritcal swing + stdAc::swingh_t::kMiddle, // Horizontal swing + false, // Quiet + true, // Turbo + 19 * 60 + 17); // Clock + ASSERT_EQ(expected_dke, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(PANASONIC_AC, ac._irsend.capture.decode_type); + ASSERT_EQ(kPanasonicAcBits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state); + ASSERT_EQ(expected_dke, ac.toString()); +} + +TEST(TestIRac, Samsung) { + IRSamsungAc ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On, Mode: 0 (AUTO), Temp: 28C, Fan: 6 (AUTO), Swing: On, " + "Beep: On, Clean: On, Quiet: On"; + + ac.begin(); + irac.samsung(&ac, + true, // Power + stdAc::opmode_t::kAuto, // Mode + 28, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kAuto, // Veritcal swing + true, // Quiet + false, // Turbo + true, // Clean + true, // Beep + false); // with the Hack Off + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(SAMSUNG_AC, ac._irsend.capture.decode_type); + ASSERT_EQ(kSamsungAcBits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state); + ASSERT_EQ(expected, ac.toString()); + + ac._irsend.reset(); + irac.samsung(&ac, + true, // Power + stdAc::opmode_t::kAuto, // Mode + 28, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kAuto, // Veritcal swing + true, // Quiet + false, // Turbo + true, // Clean + true, // Beep + true); // with the Hack On + ASSERT_EQ(expected, ac.toString()); // Class should be in the desired mode. + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(SAMSUNG_AC, ac._irsend.capture.decode_type); + ASSERT_EQ(kSamsungAcExtendedBits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state); + // However, we expect a plain "on" state as it should be sent before the + // desired state. + char expected_on[] = + "Power: On, Mode: 0 (AUTO), Temp: 16C, Fan: 0 (AUTO), Swing: Off, " + "Beep: Off, Clean: Off, Quiet: Off"; + ASSERT_EQ(expected_on, ac.toString()); +} + +TEST(TestIRac, Tcl112) { + IRTcl112Ac ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On, Mode: 3 (COOL), Temp: 20C, Fan: 3 (Med), Econo: On, " + "Health: On, Light: On, Turbo: Off, Swing (H): On, Swing (V): Off"; + + ac.begin(); + irac.tcl112(&ac, + true, // Power + stdAc::opmode_t::kCool, // Mode + 20, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kOff, // Veritcal swing + stdAc::swingh_t::kAuto, // Horizontal swing + false, // Turbo + true, // Light + true, // Econo + true); // Filter (aka. Health) + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(TCL112AC, ac._irsend.capture.decode_type); + ASSERT_EQ(kTcl112AcBits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state); + ASSERT_EQ(expected, ac.toString()); +} + +TEST(TestIRac, Teco) { + IRTecoAc ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On, Mode: 0 (AUTO), Temp: 21C, Fan: 2 (Med), Sleep: On, " + "Swing: On"; + + ac.begin(); + irac.teco(&ac, + true, // Power + stdAc::opmode_t::kAuto, // Mode + 21, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kAuto, // Veritcal swing + 8 * 60 + 30); // Sleep + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(TECO, ac._irsend.capture.decode_type); + ASSERT_EQ(kTecoBits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.value); + ASSERT_EQ(expected, ac.toString()); +} + +TEST(TestIRac, Toshiba) { + IRToshibaAC ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = "Power: On, Mode: 2 (DRY), Temp: 29C, Fan: 2"; + + ac.begin(); + irac.toshiba(&ac, + true, // Power + stdAc::opmode_t::kDry, // Mode + 29, // Celsius + stdAc::fanspeed_t::kLow); // Fan speed + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(TOSHIBA_AC, ac._irsend.capture.decode_type); + ASSERT_EQ(kToshibaACBits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state); + ASSERT_EQ(expected, ac.toString()); +} + +TEST(TestIRac, Trotec) { + IRTrotecESP ac(0); + IRac irac(0); + + ac.begin(); + irac.trotec(&ac, + true, // Power + stdAc::opmode_t::kCool, // Mode + 18, // Celsius + stdAc::fanspeed_t::kHigh, // Fan speed + 8 * 60 + 17); // Sleep + EXPECT_TRUE(ac.getPower()); + EXPECT_EQ(kTrotecCool, ac.getMode()); + EXPECT_EQ(18, ac.getTemp()); + EXPECT_EQ(kTrotecFanHigh, ac.getSpeed()); + EXPECT_TRUE(ac.getSleep()); +} + +TEST(TestIRac, Vestel) { + IRVestelAc ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On, Mode: 0 (AUTO), Temp: 22C, Fan: 5 (LOW), Sleep: On, " + "Turbo: Off, Ion: On, Swing: On"; + + ac.begin(); + irac.vestel(&ac, + true, // Power + stdAc::opmode_t::kAuto, // Mode + 22, // Celsius + stdAc::fanspeed_t::kLow, // Fan speed + stdAc::swingv_t::kHigh, // Veritcal swing + false, // Turbo + true, // Filter + 8 * 60 + 0); // Sleep time + // 13 * 60 + 45); // Clock + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(VESTEL_AC, ac._irsend.capture.decode_type); + ASSERT_EQ(kVestelAcBits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state); + ASSERT_EQ(expected, ac.toString()); + + ac._irsend.reset(); + char expected_clocks[] = + "Time: 13:45, Timer: Off, On Timer: Off, Off Timer: Off"; + + ac.begin(); + irac.vestel(&ac, + true, // Power + stdAc::opmode_t::kAuto, // Mode + 22, // Celsius + stdAc::fanspeed_t::kLow, // Fan speed + stdAc::swingv_t::kHigh, // Veritcal swing + false, // Turbo + true, // Filter + 8 * 60 + 0, // Sleep time + 13 * 60 + 45, // Clock + false); // Don't send the normal message. + // Just for testing purposes. + ASSERT_EQ(expected_clocks, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(VESTEL_AC, ac._irsend.capture.decode_type); + ASSERT_EQ(kVestelAcBits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state); + ASSERT_EQ(expected_clocks, ac.toString()); + + // Now check it sends both messages during normal operation when the + // clock is set. + ac._irsend.reset(); + ac.begin(); + irac.vestel(&ac, + true, // Power + stdAc::opmode_t::kAuto, // Mode + 22, // Celsius + stdAc::fanspeed_t::kLow, // Fan speed + stdAc::swingv_t::kHigh, // Veritcal swing + false, // Turbo + true, // Filter + 8 * 60 + 0, // Sleep time + 13 * 60 + 45); // Clock + + EXPECT_EQ( + "f38000d50" + "m3110s9066" + "m520s1535m520s480m520s480m520s480m520s480m520s480m520s480m520s480" + "m520s480m520s1535m520s480m520s480m520s480m520s480m520s480m520s480" + "m520s1535m520s1535m520s1535m520s1535m520s480m520s1535m520s480m520s1535" + "m520s1535m520s1535m520s480m520s480m520s480m520s480m520s480m520s480" + "m520s480m520s480m520s480m520s480m520s480m520s1535m520s1535m520s480" + "m520s1535m520s480m520s1535m520s480m520s480m520s480m520s480m520s480" + "m520s480m520s480m520s1535m520s480m520s1535m520s1535m520s1535m520s1535" + "m520s100000" + "m3110s9066" + "m520s1535m520s480m520s480m520s480m520s480m520s480m520s480m520s480" + "m520s480m520s1535m520s480m520s480m520s480m520s1535m520s1535m520s480" + "m520s1535m520s1535m520s1535m520s1535m520s480m520s480m520s480m520s480" + "m520s480m520s480m520s480m520s480m520s480m520s480m520s480m520s480" + "m520s480m520s480m520s480m520s480m520s1535m520s480m520s1535m520s1535" + "m520s480m520s480m520s480m520s480m520s1535m520s480m520s1535m520s1535" + "m520s480m520s1535m520s480m520s480m520s480m520s480m520s480m520s480" + "m520s100000", ac._irsend.outputStr()); +} + + +TEST(TestIRac, Whirlpool) { + IRWhirlpoolAc ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Model: 1 (DG11J13A), Power toggle: On, Mode: 1 (AUTO), Temp: 21C, " + "Fan: 3 (LOW), Swing: On, Light: On, Clock: 23:58, On Timer: Off, " + "Off Timer: Off, Sleep: On, Super: Off, Command: 1 (POWER)"; + + ac.begin(); + irac.whirlpool(&ac, + DG11J13A, + true, // Power + stdAc::opmode_t::kAuto, // Mode + 21, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kAuto, // Veritcal swing + false, // Turbo + true, // Light + 8 * 60 + 30, // Sleep + 23 * 60 + 58); // Clock + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(WHIRLPOOL_AC, ac._irsend.capture.decode_type); + ASSERT_EQ(kWhirlpoolAcBits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state); + ASSERT_EQ(expected, ac.toString()); +} + +TEST(TestIRac, strToBool) { + EXPECT_TRUE(IRac::strToBool("ON")); + EXPECT_TRUE(IRac::strToBool("1")); + EXPECT_TRUE(IRac::strToBool("TRUE")); + EXPECT_TRUE(IRac::strToBool("YES")); + EXPECT_FALSE(IRac::strToBool("OFF")); + EXPECT_FALSE(IRac::strToBool("0")); + EXPECT_FALSE(IRac::strToBool("FALSE")); + EXPECT_FALSE(IRac::strToBool("NO")); + EXPECT_FALSE(IRac::strToBool("FOOBAR")); + EXPECT_TRUE(IRac::strToBool("FOOBAR", true)); +} + +TEST(TestIRac, strToOpmode) { + EXPECT_EQ(stdAc::opmode_t::kAuto, IRac::strToOpmode("AUTO")); + EXPECT_EQ(stdAc::opmode_t::kCool, IRac::strToOpmode("COOL")); + EXPECT_EQ(stdAc::opmode_t::kHeat, IRac::strToOpmode("HEAT")); + EXPECT_EQ(stdAc::opmode_t::kDry, IRac::strToOpmode("DRY")); + EXPECT_EQ(stdAc::opmode_t::kFan, IRac::strToOpmode("FAN")); + EXPECT_EQ(stdAc::opmode_t::kFan, IRac::strToOpmode("FAN_ONLY")); + EXPECT_EQ(stdAc::opmode_t::kAuto, IRac::strToOpmode("FOOBAR")); + EXPECT_EQ(stdAc::opmode_t::kOff, IRac::strToOpmode("OFF")); + EXPECT_EQ(stdAc::opmode_t::kOff, IRac::strToOpmode("FOOBAR", + stdAc::opmode_t::kOff)); +} + +TEST(TestIRac, strToFanspeed) { + EXPECT_EQ(stdAc::fanspeed_t::kAuto, IRac::strToFanspeed("AUTO")); + EXPECT_EQ(stdAc::fanspeed_t::kMin, IRac::strToFanspeed("MIN")); + EXPECT_EQ(stdAc::fanspeed_t::kLow, IRac::strToFanspeed("LOW")); + EXPECT_EQ(stdAc::fanspeed_t::kMedium, IRac::strToFanspeed("MEDIUM")); + EXPECT_EQ(stdAc::fanspeed_t::kHigh, IRac::strToFanspeed("HIGH")); + EXPECT_EQ(stdAc::fanspeed_t::kMax, IRac::strToFanspeed("MAX")); + EXPECT_EQ(stdAc::fanspeed_t::kAuto, IRac::strToFanspeed("FOOBAR")); + EXPECT_EQ(stdAc::fanspeed_t::kMin, + IRac::strToFanspeed("FOOBAR", stdAc::fanspeed_t::kMin)); +} + +TEST(TestIRac, strToSwingV) { + EXPECT_EQ(stdAc::swingv_t::kAuto, IRac::strToSwingV("AUTO")); + EXPECT_EQ(stdAc::swingv_t::kLowest, IRac::strToSwingV("LOWEST")); + EXPECT_EQ(stdAc::swingv_t::kLow, IRac::strToSwingV("LOW")); + EXPECT_EQ(stdAc::swingv_t::kMiddle, IRac::strToSwingV("MIDDLE")); + EXPECT_EQ(stdAc::swingv_t::kHigh, IRac::strToSwingV("HIGH")); + EXPECT_EQ(stdAc::swingv_t::kHighest, IRac::strToSwingV("HIGHEST")); + EXPECT_EQ(stdAc::swingv_t::kOff, IRac::strToSwingV("OFF")); + EXPECT_EQ(stdAc::swingv_t::kOff, IRac::strToSwingV("FOOBAR")); + EXPECT_EQ(stdAc::swingv_t::kAuto, + IRac::strToSwingV("FOOBAR", stdAc::swingv_t::kAuto)); +} + +TEST(TestIRac, strToSwingH) { + EXPECT_EQ(stdAc::swingh_t::kAuto, IRac::strToSwingH("AUTO")); + EXPECT_EQ(stdAc::swingh_t::kLeftMax, IRac::strToSwingH("MAX LEFT")); + EXPECT_EQ(stdAc::swingh_t::kLeft, IRac::strToSwingH("LEFT")); + EXPECT_EQ(stdAc::swingh_t::kMiddle, IRac::strToSwingH("CENTRE")); + EXPECT_EQ(stdAc::swingh_t::kRight, IRac::strToSwingH("RIGHT")); + EXPECT_EQ(stdAc::swingh_t::kRightMax, IRac::strToSwingH("RIGHTMAX")); + EXPECT_EQ(stdAc::swingh_t::kOff, IRac::strToSwingH("OFF")); + EXPECT_EQ(stdAc::swingh_t::kOff, IRac::strToSwingH("FOOBAR")); + EXPECT_EQ(stdAc::swingh_t::kAuto, + IRac::strToSwingH("FOOBAR", stdAc::swingh_t::kAuto)); +} + +TEST(TestIRac, strToModel) { + EXPECT_EQ(panasonic_ac_remote_model_t::kPanasonicLke, + IRac::strToModel("LKE")); + EXPECT_EQ(panasonic_ac_remote_model_t::kPanasonicLke, + IRac::strToModel("PANASONICLKE")); + EXPECT_EQ(fujitsu_ac_remote_model_t::ARRAH2E, + IRac::strToModel("ARRAH2E")); + EXPECT_EQ(whirlpool_ac_remote_model_t::DG11J13A, + IRac::strToModel("DG11J13A")); + EXPECT_EQ(1, IRac::strToModel("1")); + EXPECT_EQ(10, IRac::strToModel("10")); + EXPECT_EQ(-1, IRac::strToModel("0")); + EXPECT_EQ(-1, IRac::strToModel("FOOBAR")); + EXPECT_EQ(0, IRac::strToModel("FOOBAR", 0)); +} diff --git a/lib/IRremoteESP8266-2.5.2.03/test/IRrecv_test.cpp b/lib/IRremoteESP8266-2.6.0/test/IRrecv_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/test/IRrecv_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/IRrecv_test.cpp diff --git a/lib/IRremoteESP8266-2.5.2.03/test/IRrecv_test.h b/lib/IRremoteESP8266-2.6.0/test/IRrecv_test.h similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/test/IRrecv_test.h rename to lib/IRremoteESP8266-2.6.0/test/IRrecv_test.h diff --git a/lib/IRremoteESP8266-2.6.0/test/IRsend_test.cpp b/lib/IRremoteESP8266-2.6.0/test/IRsend_test.cpp new file mode 100644 index 000000000..ffd69cf71 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/test/IRsend_test.cpp @@ -0,0 +1,686 @@ +// Copyright 2017,2019 David Conran + +#include "IRsend_test.h" +#include "IRsend.h" +#include "IRutils.h" +#include "gtest/gtest.h" + +// Tests sendData(). + +// Test sending zero bits. +TEST(TestSendData, SendZeroBits) { + IRsendTest irsend(4); + irsend.begin(); + irsend.sendData(1, 2, 3, 4, 0b1, 0, true); + EXPECT_EQ("", irsend.outputStr()); +} + +// Test sending zero and one. +TEST(TestSendData, SendSingleBit) { + IRsendTest irsend(4); + irsend.begin(); + irsend.sendData(1, 2, 3, 4, 0b1, 1, true); + EXPECT_EQ("d50m1s2", irsend.outputStr()); + irsend.sendData(1, 2, 3, 4, 0b0, 1, true); + EXPECT_EQ("d50m3s4", irsend.outputStr()); +} + +// Test sending bit order. +TEST(TestSendData, TestingBitSendOrder) { + IRsendTest irsend(4); + irsend.begin(); + irsend.sendData(1, 2, 3, 4, 0b10, 2, true); + EXPECT_EQ("d50m1s2m3s4", irsend.outputStr()); + irsend.sendData(1, 2, 3, 4, 0b10, 2, false); + EXPECT_EQ("d50m3s4m1s2", irsend.outputStr()); + irsend.sendData(1, 2, 3, 4, 0b0001, 4, false); + EXPECT_EQ("d50m1s2m3s4m3s4m3s4", irsend.outputStr()); +} + +// Test sending typical data. +TEST(TestSendData, SendTypicalData) { + IRsendTest irsend(4); + irsend.begin(); + irsend.sendData(1, 2, 3, 4, 0b1010110011110000, 16, true); + EXPECT_EQ( + "d50m1s2m3s4m1s2m3s4m1s2m1s2m3s4m3s4m1s2m1s2m1s2m1s2m3s4m3s4m3s4m3s4", + irsend.outputStr()); + irsend.sendData(1, 2, 3, 4, 0x1234567890ABCDEF, 64, true); + EXPECT_EQ( + "d50" + "m3s4m3s4m3s4m1s2m3s4m3s4m1s2m3s4m3s4m3s4m1s2m1s2m3s4m1s2m3s4m3s4" + "m3s4m1s2m3s4m1s2m3s4m1s2m1s2m3s4m3s4m1s2m1s2m1s2m1s2m3s4m3s4m3s4" + "m1s2m3s4m3s4m1s2m3s4m3s4m3s4m3s4m1s2m3s4m1s2m3s4m1s2m3s4m1s2m1s2" + "m1s2m1s2m3s4m3s4m1s2m1s2m3s4m1s2m1s2m1s2m1s2m3s4m1s2m1s2m1s2m1s2", + irsend.outputStr()); +} + +// Test sending more than expected bits. +TEST(TestSendData, SendOverLargeData) { + IRsendTest irsend(4); + irsend.begin(); + irsend.sendData(1, 2, 3, 4, 0xFFFFFFFFFFFFFFFF, 70, true); + EXPECT_EQ( + "d50" + "m3s4m3s4m3s4m3s4m3s4m3s4" + "m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2" + "m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2" + "m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2" + "m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2", + irsend.outputStr()); +} + +// Test inverting the output. +TEST(TestIRSend, InvertedOutput) { + IRsendTest irsend(4, true); + irsend.begin(); + irsend.sendData(1, 2, 3, 4, 0b1, 1, true); + EXPECT_EQ("d50s1m2", irsend.outputStr()); + irsend.sendData(1, 2, 3, 4, 0b0, 1, true); + EXPECT_EQ("d50s3m4", irsend.outputStr()); +} + +// Test we correctly pick up frequency changes. +TEST(TestIRSend, DetectFreqChanges) { + IRsendTest irsend(0); + + irsend.begin(); + irsend.enableIROut(40); // 40kHz + irsend.sendData(1, 2, 3, 4, 0b1, 1, true); + irsend.enableIROut(38); // 40kHz + irsend.sendData(1, 2, 3, 4, 0b1, 1, true); + irsend.enableIROut(40); // 40kHz + irsend.sendData(1, 2, 3, 4, 0b1, 1, true); + irsend.enableIROut(38); // 40kHz + irsend.sendData(1, 2, 3, 4, 0b1, 1, true); + EXPECT_EQ( + "f40000d50" + "m1s2" + "f38000" + "m1s2" + "f40000" + "m1s2" + "f38000" + "m1s2", + irsend.outputStr()); + irsend.reset(); + irsend.enableIROut(40); // 40kHz + irsend.sendData(1, 2, 3, 4, 0b1, 1, true); + irsend.enableIROut(40); // 40kHz + irsend.sendData(1, 2, 3, 4, 0b1, 1, true); + irsend.enableIROut(38); // 40kHz + irsend.sendData(1, 2, 3, 4, 0b1, 1, true); + irsend.enableIROut(38); // 40kHz + irsend.sendData(1, 2, 3, 4, 0b1, 1, true); + EXPECT_EQ( + "f40000d50" + "m1s2m1s2" + "f38000m1s2m1s2", + irsend.outputStr()); +} + +// Test we correctly pick up duty cycle changes. +TEST(TestIRSend, DetectDutyChanges) { + IRsendTest irsend(0); + + irsend.begin(); + irsend.sendGeneric(1, 2, 3, 4, 5, 6, 7, 8, 0b1, 1, 38000, true, 0, 33); + EXPECT_EQ( + "f38000d33" + "m1s2m3s4m7s8", + irsend.outputStr()); + + irsend.reset(); + irsend.sendGeneric(1, 2, 3, 4, 5, 6, 7, 8, 0b1, 1, 38000, true, 0, 50); + irsend.sendGeneric(1, 2, 3, 4, 5, 6, 7, 8, 0b1, 1, 38000, true, 0, 33); + irsend.sendGeneric(1, 2, 3, 4, 5, 6, 7, 8, 0b1, 1, 38000, true, 0, 25); + EXPECT_EQ( + "f38000d50" + "m1s2m3s4m7s8" + "d33" + "m1s2m3s4m7s8" + "d25" + "m1s2m3s4m7s8", + irsend.outputStr()); +} + + +// Test we correctly pick up frequency AND duty changes. +TEST(TestIRSend, DetectFreqAndDutyChanges) { + IRsendTest irsend(0); + + irsend.begin(); + irsend.sendGeneric(1, 2, 3, 4, 5, 6, 7, 8, 0b1, 1, 38000, true, 0, 50); + irsend.sendGeneric(1, 2, 3, 4, 5, 6, 7, 8, 0b1, 1, 38000, true, 0, 33); + irsend.sendGeneric(1, 2, 3, 4, 5, 6, 7, 8, 0b1, 1, 40000, true, 0, 25); + EXPECT_EQ( + "f38000d50" + "m1s2m3s4m7s8" + "d33" + "m1s2m3s4m7s8" + "f40000d25" + "m1s2m3s4m7s8", + irsend.outputStr()); +} + +// Test typical use of sendRaw(). +TEST(TestSendRaw, GeneralUse) { + IRsendTest irsend(4); + IRrecv irrecv(0); + + irsend.begin(); + // NEC C3E0E0E8 as measured in #204 + uint16_t rawData[67] = { + 8950, 4500, 550, 1650, 600, 1650, 550, 550, 600, 500, 600, 550, + 550, 550, 600, 1650, 550, 1650, 600, 1650, 600, 1650, 550, 1700, + 550, 550, 600, 550, 550, 550, 600, 500, 600, 550, 550, 1650, + 600, 1650, 600, 1650, 550, 550, 600, 500, 600, 500, 600, 550, + 550, 550, 600, 1650, 550, 1650, 600, 1650, 600, 500, 650, 1600, + 600, 500, 600, 550, 550, 550, 600}; + + irsend.sendRaw(rawData, 67, 38); + EXPECT_EQ( + "f38000d50" + "m8950s4500" + "m550s1650m600s1650m550s550m600s500m600s550m550s550m600s1650m550s1650" + "m600s1650m600s1650m550s1700m550s550m600s550m550s550m600s500m600s550" + "m550s1650m600s1650m600s1650m550s550m600s500m600s500m600s550m550s550" + "m600s1650m550s1650m600s1650m600s500m650s1600m600s500m600s550m550s550" + "m600", + irsend.outputStr()); + + irsend.reset(); + irsend.sendRaw(rawData, 67, 38); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decodeNEC(&irsend.capture, kNECBits, false)); + EXPECT_EQ(NEC, irsend.capture.decode_type); + EXPECT_EQ(32, irsend.capture.bits); + EXPECT_EQ(0xC3E0E0E8, irsend.capture.value); + EXPECT_EQ( + "f38000d50" + "m8950s4500" + "m550s1650m600s1650m550s550m600s500m600s550m550s550m600s1650m550s1650" + "m600s1650m600s1650m550s1700m550s550m600s550m550s550m600s500m600s550" + "m550s1650m600s1650m600s1650m550s550m600s500m600s500m600s550m550s550" + "m600s1650m550s1650m600s1650m600s500m650s1600m600s500m600s550m550s550" + "m600", + irsend.outputStr()); +} + +// Incorrect handling of decodes from Raw. i.e. There is no gap recorded at +// the end of a command when using the interrupt code. sendRaw() best emulates +// this for unit testing purposes. sendGC() and sendXXX() will add the trailing +// gap. Users won't see this in normal use. +TEST(TestSendRaw, NoTrailingGap) { + IRsendTest irsend(4); + IRrecv irrecv(4); + irsend.begin(); + + irsend.reset(); + uint16_t rawData[67] = { + 9000, 4500, 650, 550, 650, 1650, 600, 550, 650, 550, 600, 1650, + 650, 550, 600, 1650, 650, 1650, 650, 1650, 600, 550, 650, 1650, + 650, 1650, 650, 550, 600, 1650, 650, 1650, 650, 550, 650, 550, + 650, 1650, 650, 550, 650, 550, 650, 550, 600, 550, 650, 550, + 650, 550, 650, 1650, 600, 550, 650, 1650, 650, 1650, 650, 1650, + 650, 1650, 650, 1650, 650, 1650, 600}; + irsend.sendRaw(rawData, 67, 38); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decodeNEC(&irsend.capture)); + EXPECT_EQ(NEC, irsend.capture.decode_type); + EXPECT_EQ(kNECBits, irsend.capture.bits); +} + +TEST(TestLowLevelSend, MarkFrequencyModulationAt38kHz) { + IRsendLowLevelTest irsend(0); + + irsend.begin(); + + irsend.reset(); + irsend.enableIROut(38000, 50); + EXPECT_EQ(5, irsend.mark(100)); + EXPECT_EQ( + "[On]10usecs[Off]11usecs[On]10usecs[Off]11usecs[On]10usecs[Off]11usecs" + "[On]10usecs[Off]11usecs[On]10usecs[Off]6usecs", + irsend.low_level_sequence); + + irsend.reset(); + irsend.enableIROut(38000, 33); + EXPECT_EQ(5, irsend.mark(100)); + EXPECT_EQ( + "[On]6usecs[Off]15usecs[On]6usecs[Off]15usecs[On]6usecs[Off]15usecs" + "[On]6usecs[Off]15usecs[On]6usecs[Off]10usecs", + irsend.low_level_sequence); + + irsend.reset(); + irsend.enableIROut(38000, 100); + EXPECT_EQ(1, irsend.mark(1000)); + EXPECT_EQ("[On]1000usecs[Off]", irsend.low_level_sequence); +} + +TEST(TestLowLevelSend, MarkFrequencyModulationAt36_7kHz) { + IRsendLowLevelTest irsend(0); + + irsend.begin(); + + irsend.reset(); + irsend.enableIROut(36700, 50); + EXPECT_EQ(5, irsend.mark(100)); + EXPECT_EQ( + "[On]11usecs[Off]11usecs[On]11usecs[Off]11usecs[On]11usecs[Off]11usecs" + "[On]11usecs[Off]11usecs[On]11usecs[Off]1usecs", + irsend.low_level_sequence); + + irsend.reset(); + irsend.enableIROut(36700, 33); + EXPECT_EQ(5, irsend.mark(100)); + EXPECT_EQ( + "[On]7usecs[Off]15usecs[On]7usecs[Off]15usecs[On]7usecs[Off]15usecs" + "[On]7usecs[Off]15usecs[On]7usecs[Off]5usecs", + irsend.low_level_sequence); + + irsend.reset(); + irsend.enableIROut(36700, 100); + EXPECT_EQ(1, irsend.mark(1000)); + EXPECT_EQ("[On]1000usecs[Off]", irsend.low_level_sequence); +} + +TEST(TestLowLevelSend, MarkFrequencyModulationAt40kHz) { + IRsendLowLevelTest irsend(0); + + irsend.begin(); + + irsend.reset(); + irsend.enableIROut(40000, 50); + EXPECT_EQ(5, irsend.mark(100)); + EXPECT_EQ( + "[On]10usecs[Off]10usecs[On]10usecs[Off]10usecs[On]10usecs[Off]10usecs" + "[On]10usecs[Off]10usecs[On]10usecs[Off]10usecs", + irsend.low_level_sequence); + + irsend.reset(); + irsend.enableIROut(40000, 33); + EXPECT_EQ(5, irsend.mark(100)); + EXPECT_EQ( + "[On]6usecs[Off]14usecs[On]6usecs[Off]14usecs[On]6usecs[Off]14usecs" + "[On]6usecs[Off]14usecs[On]6usecs[Off]14usecs", + irsend.low_level_sequence); + + irsend.reset(); + irsend.enableIROut(40000, 100); + EXPECT_EQ(1, irsend.mark(1000)); + EXPECT_EQ("[On]1000usecs[Off]", irsend.low_level_sequence); +} + +TEST(TestLowLevelSend, MarkNoModulation) { + IRsendLowLevelTest irsend(0, false, false); + + irsend.begin(); + + irsend.reset(); + irsend.enableIROut(38000, 50); + EXPECT_EQ(1, irsend.mark(1000)); + EXPECT_EQ("[On]1000usecs[Off]", irsend.low_level_sequence); + + irsend.reset(); + irsend.enableIROut(36700, 25); + EXPECT_EQ(1, irsend.mark(1000)); + EXPECT_EQ("[On]1000usecs[Off]", irsend.low_level_sequence); + + irsend.reset(); + irsend.enableIROut(40000, 75); + EXPECT_EQ(1, irsend.mark(1000)); + EXPECT_EQ("[On]1000usecs[Off]", irsend.low_level_sequence); +} + +TEST(TestLowLevelSend, SpaceFrequencyModulation) { + IRsendLowLevelTest irsend(0); + + irsend.reset(); + irsend.enableIROut(38000); + irsend.space(1000); + EXPECT_EQ("[Off]1000usecs", irsend.low_level_sequence); + + irsend.reset(); + irsend.enableIROut(40000, 75); + irsend.space(1000); + EXPECT_EQ("[Off]1000usecs", irsend.low_level_sequence); + + irsend.reset(); + irsend.enableIROut(38000, 100); + irsend.space(1000); + EXPECT_EQ("[Off]1000usecs", irsend.low_level_sequence); + + irsend.reset(); + irsend.enableIROut(38000, 33); + irsend.space(1000); + EXPECT_EQ("[Off]1000usecs", irsend.low_level_sequence); +} + +TEST(TestLowLevelSend, SpaceNoModulation) { + IRsendLowLevelTest irsend(0, false, false); + + irsend.begin(); + + irsend.reset(); + irsend.enableIROut(38000, 50); + irsend.space(1000); + EXPECT_EQ("[Off]1000usecs", irsend.low_level_sequence); + + irsend.reset(); + irsend.enableIROut(36700, 25); + irsend.space(1000); + EXPECT_EQ("[Off]1000usecs", irsend.low_level_sequence); + + irsend.reset(); + irsend.enableIROut(40000, 75); + irsend.space(1000); + EXPECT_EQ("[Off]1000usecs", irsend.low_level_sequence); +} + +// Test expected to work/produce a message for irsend:send() +TEST(TestSend, GenericSimpleSendMethod) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + irsend.reset(); + ASSERT_TRUE(irsend.send(AIWA_RC_T501, 0x1234, kAiwaRcT501Bits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(AIWA_RC_T501, irsend.capture.decode_type); + EXPECT_EQ(kAiwaRcT501Bits, irsend.capture.bits); + EXPECT_EQ(0x1234, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(CARRIER_AC, 0x1234, kCarrierAcBits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(CARRIER_AC, irsend.capture.decode_type); + EXPECT_EQ(kCarrierAcBits, irsend.capture.bits); + EXPECT_EQ(0x1234, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(COOLIX, 0x1234, kCoolixBits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(COOLIX, irsend.capture.decode_type); + EXPECT_EQ(kCoolixBits, irsend.capture.bits); + EXPECT_EQ(0x1234, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(DENON, 0x1234, kDenonBits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(DENON, irsend.capture.decode_type); + EXPECT_EQ(kDenonBits, irsend.capture.bits); + EXPECT_EQ(0x1234, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(DISH, 0x1234, kDishBits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(DISH, irsend.capture.decode_type); + EXPECT_EQ(kDishBits, irsend.capture.bits); + EXPECT_EQ(0x1234, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(GICABLE, 0x1234, kGicableBits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(GICABLE, irsend.capture.decode_type); + EXPECT_EQ(kGicableBits, irsend.capture.bits); + EXPECT_EQ(0x1234, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(GREE, 0x0009205000200050, kGreeBits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(GREE, irsend.capture.decode_type); + EXPECT_EQ(kGreeBits, irsend.capture.bits); + // No simple value test for gree as it decodes to an Array. + + irsend.reset(); + ASSERT_TRUE(irsend.send(JVC, 0x1234, kJvcBits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(JVC, irsend.capture.decode_type); + EXPECT_EQ(kJvcBits, irsend.capture.bits); + EXPECT_EQ(0x1234, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(LASERTAG, 0x123, kLasertagBits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(LASERTAG, irsend.capture.decode_type); + EXPECT_EQ(kLasertagBits, irsend.capture.bits); + EXPECT_EQ(0x123, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(LG, 0x700992, kLgBits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(LG, irsend.capture.decode_type); + EXPECT_EQ(kLgBits, irsend.capture.bits); + EXPECT_EQ(0x700992, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(LG, 0x700992, kLg32Bits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(LG, irsend.capture.decode_type); + EXPECT_EQ(kLg32Bits, irsend.capture.bits); + EXPECT_EQ(0x700992, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(LG2, 0x880094D, kLgBits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(LG2, irsend.capture.decode_type); + EXPECT_EQ(kLgBits, irsend.capture.bits); + EXPECT_EQ(0x880094D, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(LUTRON, 0x1234, kLutronBits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(LUTRON, irsend.capture.decode_type); + EXPECT_EQ(kLutronBits, irsend.capture.bits); + EXPECT_EQ(0x1234, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(MAGIQUEST, 0x560F40020455, kMagiquestBits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(MAGIQUEST, irsend.capture.decode_type); + EXPECT_EQ(kMagiquestBits, irsend.capture.bits); + EXPECT_EQ(0x560F40020455, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(MIDEA, 0xA18263FFFF6E, kMideaBits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(MIDEA, irsend.capture.decode_type); + EXPECT_EQ(kMideaBits, irsend.capture.bits); + EXPECT_EQ(0xA18263FFFF6E, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(MITSUBISHI, 0x1234, kMitsubishiBits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(MITSUBISHI, irsend.capture.decode_type); + EXPECT_EQ(kMitsubishiBits, irsend.capture.bits); + EXPECT_EQ(0x1234, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(MITSUBISHI2, 0x1234, kMitsubishiBits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(MITSUBISHI2, irsend.capture.decode_type); + EXPECT_EQ(kMitsubishiBits, irsend.capture.bits); + EXPECT_EQ(0x1234, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(NIKAI, 0x1234, kNikaiBits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(NIKAI, irsend.capture.decode_type); + EXPECT_EQ(kNikaiBits, irsend.capture.bits); + EXPECT_EQ(0x1234, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(NEC, 0x4BB640BF, kNECBits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(NEC, irsend.capture.decode_type); + EXPECT_EQ(kNECBits, irsend.capture.bits); + EXPECT_EQ(0x4BB640BF, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(NEC_LIKE, 0x12345678, kNECBits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(NEC_LIKE, irsend.capture.decode_type); + EXPECT_EQ(kNECBits, irsend.capture.bits); + EXPECT_EQ(0x12345678, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(PANASONIC, 0x40040190ED7C, kPanasonicBits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(PANASONIC, irsend.capture.decode_type); + EXPECT_EQ(kPanasonicBits, irsend.capture.bits); + EXPECT_EQ(0x40040190ED7C, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(PIONEER, 0x659A05FAF50AC53A, kPioneerBits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(PIONEER, irsend.capture.decode_type); + EXPECT_EQ(kPioneerBits, irsend.capture.bits); + EXPECT_EQ(0x659A05FAF50AC53A, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(RC5, 0x175, kRC5Bits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(RC5, irsend.capture.decode_type); + EXPECT_EQ(kRC5Bits, irsend.capture.bits); + EXPECT_EQ(0x175, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(RC6, 0xC800F740C, kRC6_36Bits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(RC6, irsend.capture.decode_type); + EXPECT_EQ(kRC6_36Bits, irsend.capture.bits); + EXPECT_EQ(0xC800F740C, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(RCMM, 0x1234, kRCMMBits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(RCMM, irsend.capture.decode_type); + EXPECT_EQ(kRCMMBits, irsend.capture.bits); + EXPECT_EQ(0x1234, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(SAMSUNG, 0xE0E09966, kSamsungBits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(SAMSUNG, irsend.capture.decode_type); + EXPECT_EQ(kSamsungBits, irsend.capture.bits); + EXPECT_EQ(0xE0E09966, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(SAMSUNG36, 0x1234, kSamsung36Bits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(SAMSUNG36, irsend.capture.decode_type); + EXPECT_EQ(kSamsung36Bits, irsend.capture.bits); + EXPECT_EQ(0x1234, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(SANYO_LC7461, 0x2468DCB56A9, kSanyoLC7461Bits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(SANYO_LC7461, irsend.capture.decode_type); + EXPECT_EQ(kSanyoLC7461Bits, irsend.capture.bits); + EXPECT_EQ(0x2468DCB56A9, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(SHARP, 0x7266, kSharpBits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(SHARP, irsend.capture.decode_type); + EXPECT_EQ(kSharpBits, irsend.capture.bits); + EXPECT_EQ(0x7266, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(SHERWOOD, 0x4BB640BF, kSherwoodBits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(NEC, irsend.capture.decode_type); + EXPECT_EQ(kSherwoodBits, irsend.capture.bits); + EXPECT_EQ(0x4BB640BF, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(SONY, 0x1234, kSony20Bits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(SONY, irsend.capture.decode_type); + EXPECT_EQ(kSony20Bits, irsend.capture.bits); + EXPECT_EQ(0x1234, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(TECO, 0x1234, kTecoBits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(TECO, irsend.capture.decode_type); + EXPECT_EQ(kTecoBits, irsend.capture.bits); + EXPECT_EQ(0x1234, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(VESTEL_AC, 0xF4410001FF1201, kVestelAcBits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(VESTEL_AC, irsend.capture.decode_type); + EXPECT_EQ(kVestelAcBits, irsend.capture.bits); + EXPECT_EQ(0xF4410001FF1201, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(WHYNTER, 0x1234, kWhynterBits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(WHYNTER, irsend.capture.decode_type); + EXPECT_EQ(kWhynterBits, irsend.capture.bits); + EXPECT_EQ(0x1234, irsend.capture.value); +} + +// Test some expected types to NOT work/produce a message via irsend:send() +TEST(TestSend, GenericSimpleSendMethodFailure) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + // Check nothing is sent for unexpected protocols + irsend.reset(); + ASSERT_FALSE(irsend.send(KELVINATOR, 0x1234, kKelvinatorBits)); + irsend.makeDecodeResult(); + ASSERT_FALSE(irrecv.decode(&irsend.capture)); + + // For every A/C protocol which decodes to having a state[]. + for (int i = 0; i < 200; i++) { + if (hasACState((decode_type_t)i) && i != GREE) { // Gree is an exception. + // Check it fails. + ASSERT_FALSE(irsend.send((decode_type_t)i, 0, 0)); + } + } + + // Test some other special cases. + ASSERT_FALSE(irsend.send(UNKNOWN, 0, 0)); + ASSERT_FALSE(irsend.send(UNUSED, 0, 0)); + ASSERT_FALSE(irsend.send(RAW, 0, 0)); + ASSERT_FALSE(irsend.send(PRONTO, 0, 0)); + ASSERT_FALSE(irsend.send(GLOBALCACHE, 0, 0)); +} diff --git a/lib/IRremoteESP8266-2.5.2.03/test/IRsend_test.h b/lib/IRremoteESP8266-2.6.0/test/IRsend_test.h similarity index 82% rename from lib/IRremoteESP8266-2.5.2.03/test/IRsend_test.h rename to lib/IRremoteESP8266-2.6.0/test/IRsend_test.h index 6d9fe51b8..f43409433 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/IRsend_test.h +++ b/lib/IRremoteESP8266-2.6.0/test/IRsend_test.h @@ -17,12 +17,14 @@ #ifdef UNIT_TEST // Used to help simulate elapsed time in unit tests. -uint32_t _IRtimer_unittest_now = 0; +extern uint32_t _IRtimer_unittest_now; #endif // UNIT_TEST class IRsendTest : public IRsend { public: uint32_t output[OUTPUT_BUF]; + uint32_t freq[OUTPUT_BUF]; + uint8_t duty[OUTPUT_BUF]; uint16_t last; uint16_t rawbuf[RAW_BUF]; decode_results capture; @@ -40,8 +42,22 @@ class IRsendTest : public IRsend { std::string outputStr() { std::stringstream result; + uint8_t lastduty = UINT8_MAX; // An impossible duty cycle value. + uint32_t lastfreq = 0; // An impossible frequency value. if (last == 0 && output[0] == 0) return ""; for (uint16_t i = 0; i <= last; i++) { + // Display the frequency only if it changes. + if (freq[i] != lastfreq) { + result << "f"; + result << freq[i]; + lastfreq = freq[i]; + } + // Display the duty cycle only if it changes. + if (duty[i] != lastduty) { + result << "d"; + result << static_cast(duty[i]); + lastduty = duty[i]; + } if ((i & 1) != outputOff) // Odd XOR outputOff result << "s"; else @@ -92,6 +108,8 @@ class IRsendTest : public IRsend { output[++last] = usec; else output[last] += usec; + duty[last] = _dutycycle; + freq[last] = _freq_unittest; return 0; } @@ -103,6 +121,8 @@ class IRsendTest : public IRsend { } else { output[++last] = time; } + duty[last] = _dutycycle; + freq[last] = _freq_unittest; } }; diff --git a/lib/IRremoteESP8266-2.5.2.03/test/IRutils_test.cpp b/lib/IRremoteESP8266-2.6.0/test/IRutils_test.cpp similarity index 85% rename from lib/IRremoteESP8266-2.5.2.03/test/IRutils_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/IRutils_test.cpp index 91cf4725c..4a4907649 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/IRutils_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/IRutils_test.cpp @@ -350,3 +350,68 @@ TEST(TestInvertBits, MoreThan64Bits) { ASSERT_EQ(0xAAAA5555AAAA5555, invertBits(0x5555AAAA5555AAAA, 70)); ASSERT_EQ(0xFFFFFFFFFFFFFFFF, invertBits(0x0, 128)); } + +TEST(TestCountBits, Pointer) { + uint8_t data[14] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}; + + ASSERT_EQ(0, countBits(data, 0)); + ASSERT_EQ(0, countBits(data, 1)); + ASSERT_EQ(0, countBits(data, 1, true)); + ASSERT_EQ(8, countBits(data, 1, false)); + ASSERT_EQ(1, countBits(data, 2)); + ASSERT_EQ(15, countBits(data, 2, false)); + ASSERT_EQ(1, countBits(data + 1, 1)); + ASSERT_EQ(2, countBits(data, 3)); + ASSERT_EQ(4, countBits(data, 4)); + ASSERT_EQ(25, countBits(data, 14)); + ASSERT_EQ(25, countBits(data, 14)); + ASSERT_EQ(14 * 8, countBits(data, 14, true) + countBits(data, 14, false)); + ASSERT_EQ(125, countBits(data, 14, true, 100)); +} + +TEST(TestCountBits, Integer) { + uint64_t data = 0xAAAAAAAAAAAAAAAA; + + ASSERT_EQ(0, countBits(data, 0)); + ASSERT_EQ(0, countBits(data, 1)); + ASSERT_EQ(0, countBits(data, 1, true)); + ASSERT_EQ(1, countBits(data, 1, false)); + ASSERT_EQ(1, countBits(data, 3)); + ASSERT_EQ(2, countBits(data, 3, false)); + ASSERT_EQ(4, countBits(data, 8)); + ASSERT_EQ(4, countBits(data, 8, false)); + ASSERT_EQ(32, countBits(data, 64)); + ASSERT_EQ(32, countBits(data, 64, false)); + + data = 0; + ASSERT_EQ(0, countBits(data, 1, true)); + ASSERT_EQ(1, countBits(data, 1, false)); + ASSERT_EQ(0, countBits(data, 64)); + ASSERT_EQ(64, countBits(data, 64, false)); + + data = 0xFFFFFFFFFFFFFFFF; + ASSERT_EQ(1, countBits(data, 1, true)); + ASSERT_EQ(0, countBits(data, 1, false)); + ASSERT_EQ(64, countBits(data, 64)); + ASSERT_EQ(0, countBits(data, 64, false)); +} + +TEST(TestStrToDecodeType, strToDecodeType) { + EXPECT_EQ(decode_type_t::NEC, strToDecodeType("NEC")); + EXPECT_EQ(decode_type_t::KELVINATOR, strToDecodeType("KELVINATOR")); + EXPECT_EQ(decode_type_t::UNKNOWN, strToDecodeType("foo")); +} + +TEST(TestUtils, htmlEscape) { + EXPECT_EQ("", htmlEscape("")); + EXPECT_EQ("No Changes", htmlEscape("No Changes")); + EXPECT_EQ("No\tChanges+_%^$@~`\n:\\", htmlEscape("No\tChanges+_%^$@~`\n:\\")); + EXPECT_EQ(""With Changes"", htmlEscape("\"With Changes\"")); + EXPECT_EQ( + "';!‐"<>&#equals;&#{}" + "()", htmlEscape("';!-\"<>=&#{}()")); + EXPECT_EQ("""", htmlEscape("\"\"")); + EXPECT_EQ( + "&quot;&lt;&apos;&gt;&amp;", + htmlEscape(""<'>&")); +} diff --git a/lib/IRremoteESP8266-2.5.2.03/test/Makefile b/lib/IRremoteESP8266-2.6.0/test/Makefile similarity index 71% rename from lib/IRremoteESP8266-2.5.2.03/test/Makefile rename to lib/IRremoteESP8266-2.6.0/test/Makefile index d53014183..9a64aaaaa 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/Makefile +++ b/lib/IRremoteESP8266-2.6.0/test/Makefile @@ -16,6 +16,7 @@ GTEST_DIR = ../lib/googletest/googletest # Where to find user code. USER_DIR = ../src +INCLUDES = -I$(USER_DIR) -I. # Flags passed to the preprocessor. # Set Google Test's header directory as a system directory, such that @@ -23,7 +24,7 @@ USER_DIR = ../src CPPFLAGS += -isystem $(GTEST_DIR)/include -DUNIT_TEST # Flags passed to the C++ compiler. -CXXFLAGS += -g -Wall -Wextra -pthread +CXXFLAGS += -g -Wall -Wextra -pthread -std=gnu++11 # All tests produced by this Makefile. Remember to add new tests you # created to the list. @@ -36,7 +37,8 @@ TESTS = IRutils_test IRsend_test ir_NEC_test ir_GlobalCache_test \ ir_Toshiba_test ir_Midea_test ir_Magiquest_test ir_Lasertag_test \ ir_Carrier_test ir_Haier_test ir_Hitachi_test ir_GICable_test \ ir_Whirlpool_test ir_Lutron_test ir_Electra_test ir_Pioneer_test \ - ir_MWM_test + ir_MWM_test ir_Vestel_test ir_Teco_test ir_Tcl_test ir_Lego_test IRac_test \ + ir_MitsubishiHeavy_test # All Google Test headers. Usually you shouldn't change this # definition. @@ -80,7 +82,8 @@ PROTOCOLS = ir_NEC.o ir_Sony.o ir_Samsung.o ir_JVC.o ir_RCMM.o ir_RC5_RC6.o \ ir_Kelvinator.o ir_Daikin.o ir_Gree.o ir_Pronto.o ir_Nikai.o ir_Toshiba.o \ ir_Midea.o ir_Magiquest.o ir_Lasertag.o ir_Carrier.o ir_Haier.o \ ir_Hitachi.o ir_GICable.o ir_Whirlpool.o ir_Lutron.o ir_Electra.o \ - ir_Pioneer.o ir_MWM.o + ir_Pioneer.o ir_MWM.o ir_Vestel.o ir_Teco.o ir_Tcl.o ir_Lego.o ir_Argo.o \ + ir_Trotec.o ir_MitsubishiHeavy.o # All the IR Protocol header files. PROTOCOLS_H = $(USER_DIR)/ir_Argo.h \ @@ -93,19 +96,24 @@ PROTOCOLS_H = $(USER_DIR)/ir_Argo.h \ $(USER_DIR)/ir_Daikin.h \ $(USER_DIR)/ir_Kelvinator.h \ $(USER_DIR)/ir_Mitsubishi.h \ + $(USER_DIR)/ir_MitsubishiHeavy.h \ $(USER_DIR)/ir_NEC.h \ $(USER_DIR)/ir_Samsung.h \ $(USER_DIR)/ir_Trotec.h \ $(USER_DIR)/ir_Fujitsu.h \ $(USER_DIR)/ir_LG.h \ - $(USER_DIR)/ir_Panasonic.h + $(USER_DIR)/ir_Panasonic.h \ + $(USER_DIR)/ir_Whirlpool.h \ + $(USER_DIR)/ir_Vestel.h \ + $(USER_DIR)/ir_Tcl.h \ + $(USER_DIR)/ir_Teco.h # Common object files -COMMON_OBJ = IRutils.o IRtimer.o IRsend.o IRrecv.o ir_GlobalCache.o \ +COMMON_OBJ = IRutils.o IRtimer.o IRsend.o IRrecv.o IRac.o ir_GlobalCache.o \ $(PROTOCOLS) gtest_main.a # Common dependencies COMMON_DEPS = $(USER_DIR)/IRrecv.h $(USER_DIR)/IRsend.h $(USER_DIR)/IRtimer.h \ $(USER_DIR)/IRutils.h $(USER_DIR)/IRremoteESP8266.h \ - $(PROTOCOLS_H) + $(USER_DIR)/IRac.h $(PROTOCOLS_H) # Common test dependencies COMMON_TEST_DEPS = $(COMMON_DEPS) IRrecv_test.h IRsend_test.h @@ -136,7 +144,7 @@ IRutils.o : $(USER_DIR)/IRutils.cpp $(USER_DIR)/IRutils.h $(USER_DIR)/IRremoteES $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/IRutils.cpp IRutils_test.o : IRutils_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c IRutils_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c IRutils_test.cpp IRutils_test : IRutils_test.o ir_NEC.o ir_Nikai.o ir_Toshiba.o $(COMMON_OBJ) gtest_main.a $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -148,7 +156,7 @@ IRsend.o : $(USER_DIR)/IRsend.cpp $(USER_DIR)/IRsend.h $(USER_DIR)/IRremoteESP82 $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/IRsend.cpp IRsend_test.o : IRsend_test.cpp $(USER_DIR)/IRsend.h $(USER_DIR)/IRrecv.h IRsend_test.h $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c IRsend_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c IRsend_test.cpp IRsend_test : IRsend_test.o $(COMMON_OBJ) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -157,16 +165,25 @@ IRrecv.o : $(USER_DIR)/IRrecv.cpp $(USER_DIR)/IRrecv.h $(USER_DIR)/IRremoteESP82 $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/IRrecv.cpp IRrecv_test.o : IRrecv_test.cpp $(USER_DIR)/IRsend.h $(USER_DIR)/IRrecv.h IRsend_test.h $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c IRrecv_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c IRrecv_test.cpp IRrecv_test : IRrecv_test.o $(COMMON_OBJ) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ +IRac.o : $(USER_DIR)/IRac.cpp $(USER_DIR)/IRac.h $(COMMON_DEPS) $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/IRac.cpp + +IRac_test.o : IRac_test.cpp $(USER_DIR)/IRac.h $(COMMON_DEPS) $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c IRac_test.cpp + +IRac_test : IRac_test.o $(COMMON_OBJ) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ + ir_NEC.o : $(USER_DIR)/ir_NEC.cpp $(USER_DIR)/ir_NEC.h $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_NEC.cpp ir_NEC_test.o : ir_NEC_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_NEC_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_NEC_test.cpp ir_NEC_test : $(COMMON_OBJ) ir_NEC_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -175,7 +192,7 @@ ir_GlobalCache.o : $(USER_DIR)/ir_GlobalCache.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_GlobalCache.cpp ir_GlobalCache_test.o : ir_GlobalCache_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_GlobalCache_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_GlobalCache_test.cpp ir_GlobalCache_test : $(COMMON_OBJ) ir_GlobalCache_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -184,7 +201,7 @@ ir_Sherwood.o : $(USER_DIR)/ir_Sherwood.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Sherwood.cpp ir_Sherwood_test.o : ir_Sherwood_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Sherwood_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Sherwood_test.cpp ir_Sherwood_test : $(COMMON_OBJ) ir_Sherwood_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -193,25 +210,25 @@ ir_Sony.o : $(USER_DIR)/ir_Sony.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Sony.cpp ir_Sony_test.o : ir_Sony_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Sony_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Sony_test.cpp ir_Sony_test : $(COMMON_OBJ) ir_Sony_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ ir_Samsung.o : $(USER_DIR)/ir_Samsung.cpp $(USER_DIR)/ir_Samsung.h $(COMMON_DEPS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Samsung.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Samsung.cpp ir_Samsung_test.o : ir_Samsung_test.cpp $(USER_DIR)/ir_Samsung.h $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Samsung_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Samsung_test.cpp ir_Samsung_test : $(COMMON_OBJ) ir_Samsung_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ ir_Kelvinator.o : $(USER_DIR)/ir_Kelvinator.cpp $(USER_DIR)/ir_Kelvinator.h $(COMMON_DEPS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Kelvinator.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Kelvinator.cpp ir_Kelvinator_test.o : ir_Kelvinator_test.cpp $(USER_DIR)/ir_Kelvinator.h $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Kelvinator_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Kelvinator_test.cpp ir_Kelvinator_test : $(COMMON_OBJ) ir_Kelvinator_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -220,7 +237,7 @@ ir_JVC.o : $(USER_DIR)/ir_JVC.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_JVC.cpp ir_JVC_test.o : ir_JVC_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_JVC_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_JVC_test.cpp ir_JVC_test : $(COMMON_OBJ) ir_JVC_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -229,7 +246,7 @@ ir_RCMM.o : $(USER_DIR)/ir_RCMM.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_RCMM.cpp ir_RCMM_test.o : ir_RCMM_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_RCMM_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_RCMM_test.cpp ir_RCMM_test : $(COMMON_OBJ) ir_RCMM_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -238,25 +255,34 @@ ir_LG.o : $(USER_DIR)/ir_LG.h $(USER_DIR)/ir_LG.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_LG.cpp ir_LG_test.o : ir_LG_test.cpp $(USER_DIR)/ir_LG.h $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_LG_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_LG_test.cpp ir_LG_test : $(COMMON_OBJ) ir_LG_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ ir_Mitsubishi.o : $(USER_DIR)/ir_Mitsubishi.h $(USER_DIR)/ir_Mitsubishi.cpp $(COMMON_DEPS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Mitsubishi.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Mitsubishi.cpp ir_Mitsubishi_test.o : ir_Mitsubishi_test.cpp $(USER_DIR)/ir_Mitsubishi.h $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Mitsubishi_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Mitsubishi_test.cpp ir_Mitsubishi_test : $(COMMON_OBJ) ir_Mitsubishi_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ +ir_MitsubishiHeavy.o : $(USER_DIR)/ir_MitsubishiHeavy.h $(USER_DIR)/ir_MitsubishiHeavy.cpp $(COMMON_DEPS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_MitsubishiHeavy.cpp + +ir_MitsubishiHeavy_test.o : ir_MitsubishiHeavy_test.cpp $(USER_DIR)/ir_MitsubishiHeavy.h $(COMMON_TEST_DEPS) $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_MitsubishiHeavy_test.cpp + +ir_MitsubishiHeavy_test : $(COMMON_OBJ) ir_MitsubishiHeavy_test.o + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ + ir_Fujitsu.o : $(USER_DIR)/ir_Fujitsu.h $(USER_DIR)/ir_Fujitsu.cpp $(COMMON_DEPS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Fujitsu.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Fujitsu.cpp ir_Fujitsu_test.o : ir_Fujitsu_test.cpp $(USER_DIR)/ir_Fujitsu.h $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Fujitsu_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Fujitsu_test.cpp ir_Fujitsu_test : $(COMMON_OBJ) ir_Fujitsu_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -265,7 +291,7 @@ ir_Sharp.o : $(USER_DIR)/ir_Sharp.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Sharp.cpp ir_Sharp_test.o : ir_Sharp_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Sharp_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Sharp_test.cpp ir_Sharp_test : $(COMMON_OBJ) ir_Sharp_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -274,16 +300,16 @@ ir_RC5_RC6.o : $(USER_DIR)/ir_RC5_RC6.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_RC5_RC6.cpp ir_RC5_RC6_test.o : ir_RC5_RC6_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_RC5_RC6_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_RC5_RC6_test.cpp ir_RC5_RC6_test : $(COMMON_OBJ) ir_RC5_RC6_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ ir_Panasonic.o : $(USER_DIR)/ir_Panasonic.cpp $(COMMON_DEPS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Panasonic.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Panasonic.cpp ir_Panasonic_test.o : ir_Panasonic_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Panasonic_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Panasonic_test.cpp ir_Panasonic_test : $(COMMON_OBJ) ir_Panasonic_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -292,7 +318,7 @@ ir_Dish.o : $(USER_DIR)/ir_Dish.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Dish.cpp ir_Dish_test.o : ir_Dish_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Dish_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Dish_test.cpp ir_Dish_test : $(COMMON_OBJ) ir_Dish_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -301,16 +327,16 @@ ir_Whynter.o : $(USER_DIR)/ir_Whynter.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Whynter.cpp ir_Whynter_test.o : ir_Whynter_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Whynter_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Whynter_test.cpp ir_Whynter_test : $(COMMON_OBJ) ir_Whynter_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ ir_Coolix.o : $(USER_DIR)/ir_Coolix.cpp $(COMMON_DEPS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Coolix.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Coolix.cpp ir_Coolix_test.o : ir_Coolix_test.cpp $(USER_DIR)/ir_Coolix.h $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Coolix_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Coolix_test.cpp ir_Coolix_test : $(COMMON_OBJ) ir_Coolix_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -319,7 +345,7 @@ ir_Aiwa.o : $(USER_DIR)/ir_Aiwa.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Aiwa.cpp ir_Aiwa_test.o : ir_Aiwa_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Aiwa_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Aiwa_test.cpp ir_Aiwa_test : $(COMMON_OBJ) ir_Aiwa_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -328,7 +354,7 @@ ir_Denon.o : $(USER_DIR)/ir_Denon.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Denon.cpp ir_Denon_test.o : ir_Denon_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Denon_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Denon_test.cpp ir_Denon_test : $(COMMON_OBJ) ir_Denon_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -337,25 +363,25 @@ ir_Sanyo.o : $(USER_DIR)/ir_Sanyo.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Sanyo.cpp ir_Sanyo_test.o : ir_Sanyo_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Sanyo_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Sanyo_test.cpp ir_Sanyo_test : $(COMMON_OBJ) ir_Sanyo_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ ir_Daikin.o : $(USER_DIR)/ir_Daikin.cpp $(USER_DIR)/ir_Daikin.h $(COMMON_DEPS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Daikin.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Daikin.cpp ir_Daikin_test.o : ir_Daikin_test.cpp $(USER_DIR)/ir_Daikin.h $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Daikin_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Daikin_test.cpp ir_Daikin_test : $(COMMON_OBJ) ir_Daikin_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ ir_Gree.o : $(USER_DIR)/ir_Gree.cpp $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Gree.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Gree.cpp ir_Gree_test.o : ir_Gree_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Gree_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Gree_test.cpp ir_Gree_test : $(COMMON_OBJ) ir_Gree_test.o ir_Kelvinator.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -364,7 +390,7 @@ ir_Pronto.o : $(USER_DIR)/ir_Pronto.cpp $(GTEST_HEADERS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Pronto.cpp ir_Pronto_test.o : ir_Pronto_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Pronto_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Pronto_test.cpp ir_Pronto_test : $(COMMON_OBJ) ir_Pronto_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -373,25 +399,25 @@ ir_Nikai.o : $(USER_DIR)/ir_Nikai.cpp $(GTEST_HEADERS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Nikai.cpp ir_Nikai_test.o : ir_Nikai_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Nikai_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Nikai_test.cpp ir_Nikai_test : $(COMMON_OBJ) ir_Nikai_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ ir_Toshiba.o : $(USER_DIR)/ir_Toshiba.cpp $(USER_DIR)/ir_Toshiba.h $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Toshiba.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Toshiba.cpp ir_Toshiba_test.o : ir_Toshiba_test.cpp $(USER_DIR)/ir_Toshiba.h $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Toshiba_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Toshiba_test.cpp ir_Toshiba_test : $(COMMON_OBJ) ir_Toshiba_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ ir_Midea.o : $(USER_DIR)/ir_Midea.cpp $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Midea.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Midea.cpp ir_Midea_test.o : ir_Midea_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Midea_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Midea_test.cpp ir_Midea_test : $(COMMON_OBJ) ir_Midea_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -400,7 +426,7 @@ ir_Magiquest.o : $(USER_DIR)/ir_Magiquest.cpp $(GTEST_HEADERS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Magiquest.cpp ir_Magiquest_test.o : ir_Magiquest_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Magiquest_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Magiquest_test.cpp ir_Magiquest_test : $(COMMON_OBJ) ir_Magiquest_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -409,7 +435,7 @@ ir_Lasertag.o : $(USER_DIR)/ir_Lasertag.cpp $(USER_DIR)/ir_RC5_RC6.cpp $(GTEST_H $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Lasertag.cpp ir_Lasertag_test.o : ir_Lasertag_test.cpp $(USER_DIR)/ir_RC5_RC6.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Lasertag_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Lasertag_test.cpp ir_Lasertag_test : $(COMMON_OBJ) ir_Lasertag_test.o ir_RC5_RC6.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -418,25 +444,25 @@ ir_Carrier.o : $(USER_DIR)/ir_Carrier.cpp $(COMMON_DEPS) $(GTEST_HEADERS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Carrier.cpp ir_Carrier_test.o : ir_Carrier_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Carrier_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Carrier_test.cpp ir_Carrier_test : $(COMMON_OBJ) ir_Carrier_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ ir_Haier.o : $(USER_DIR)/ir_Haier.cpp $(USER_DIR)/ir_Haier.h $(COMMON_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Haier.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Haier.cpp ir_Haier_test.o : ir_Haier_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Haier_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Haier_test.cpp ir_Haier_test : $(COMMON_OBJ) ir_Haier_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ ir_Hitachi.o : $(USER_DIR)/ir_Hitachi.cpp $(COMMON_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Hitachi.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Hitachi.cpp ir_Hitachi_test.o : ir_Hitachi_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Hitachi_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Hitachi_test.cpp ir_Hitachi_test : $(COMMON_OBJ) ir_Hitachi_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -445,16 +471,16 @@ ir_GICable.o : $(USER_DIR)/ir_GICable.cpp $(COMMON_DEPS) $(GTEST_HEADERS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_GICable.cpp ir_GICable_test.o : ir_GICable_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_GICable_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_GICable_test.cpp ir_GICable_test : $(COMMON_OBJ) ir_GICable_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ ir_Whirlpool.o : $(USER_DIR)/ir_Whirlpool.cpp $(COMMON_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Whirlpool.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Whirlpool.cpp ir_Whirlpool_test.o : ir_Whirlpool_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Whirlpool_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Whirlpool_test.cpp ir_Whirlpool_test : $(COMMON_OBJ) ir_Whirlpool_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -463,7 +489,7 @@ ir_Lutron.o : $(USER_DIR)/ir_Lutron.cpp $(COMMON_DEPS) $(GTEST_HEADERS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Lutron.cpp ir_Lutron_test.o : ir_Lutron_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Lutron_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Lutron_test.cpp ir_Lutron_test : $(COMMON_OBJ) ir_Lutron_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -472,7 +498,7 @@ ir_Electra.o : $(USER_DIR)/ir_Electra.cpp $(COMMON_DEPS) $(GTEST_HEADERS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Electra.cpp ir_Electra_test.o : ir_Electra_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Electra_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Electra_test.cpp ir_Electra_test : $(COMMON_OBJ) ir_Electra_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -481,7 +507,7 @@ ir_Pioneer.o : $(USER_DIR)/ir_Pioneer.cpp $(COMMON_DEPS) $(GTEST_HEADERS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Pioneer.cpp ir_Pioneer_test.o : ir_Pioneer_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Pioneer_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Pioneer_test.cpp ir_Pioneer_test : $(COMMON_OBJ) ir_Pioneer_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -490,7 +516,49 @@ ir_MWM.o : $(USER_DIR)/ir_MWM.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_MWM.cpp ir_MWM_test.o : ir_MWM_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_MWM_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_MWM_test.cpp ir_MWM_test : $(COMMON_OBJ) ir_MWM_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ + +ir_Vestel.o : $(USER_DIR)/ir_Vestel.cpp $(COMMON_DEPS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Vestel.cpp + +ir_Vestel_test.o : ir_Vestel_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Vestel_test.cpp + +ir_Vestel_test : $(COMMON_OBJ) ir_Vestel_test.o + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ + +ir_Teco.o : $(USER_DIR)/ir_Teco.cpp $(COMMON_DEPS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Teco.cpp + +ir_Teco_test.o : ir_Teco_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Teco_test.cpp + +ir_Teco_test : $(COMMON_OBJ) ir_Teco_test.o + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ + +ir_Tcl.o : $(USER_DIR)/ir_Tcl.cpp $(COMMON_DEPS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Tcl.cpp + +ir_Tcl_test.o : ir_Tcl_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Tcl_test.cpp + +ir_Tcl_test : $(COMMON_OBJ) ir_Tcl_test.o + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ + +ir_Lego.o : $(USER_DIR)/ir_Lego.cpp $(COMMON_DEPS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Lego.cpp + +ir_Lego_test.o : ir_Lego_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Lego_test.cpp + +ir_Lego_test : $(COMMON_OBJ) ir_Lego_test.o + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ + +ir_Argo.o : $(USER_DIR)/ir_Argo.cpp $(COMMON_DEPS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Argo.cpp + +ir_Trotec.o : $(USER_DIR)/ir_Trotec.cpp $(COMMON_DEPS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Trotec.cpp diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_Aiwa_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Aiwa_test.cpp similarity index 99% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_Aiwa_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_Aiwa_test.cpp index c5469d4a5..2fa63afe6 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_Aiwa_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Aiwa_test.cpp @@ -14,6 +14,7 @@ TEST(TestSendAiwa, SendDataOnly) { irsend.reset(); irsend.sendAiwaRCT501(0x7F); // Aiwa Power Toggle. EXPECT_EQ( + "f38000d33" "m8960s4480" "m560s560m560s1680m560s1680m560s1680m560s560m560s1680m560s1680m560s560" "m560s560m560s560m560s560m560s560m560s560m560s1680m560s560m560s560" @@ -43,6 +44,7 @@ TEST(TestSendAiwa, SendWithRepeats) { irsend.reset(); irsend.sendAiwaRCT501(0x7F, kAiwaRcT501Bits, 0); // No repeats. EXPECT_EQ( + "f38000d33" "m8960s4480" "m560s560m560s1680m560s1680m560s1680m560s560m560s1680m560s1680m560s560" "m560s560m560s560m560s560m560s560m560s560m560s1680m560s560m560s560" @@ -54,6 +56,7 @@ TEST(TestSendAiwa, SendWithRepeats) { irsend.reset(); irsend.sendAiwaRCT501(0x7F, kAiwaRcT501Bits, 1); // 1 repeat. EXPECT_EQ( + "f38000d33" "m8960s4480" "m560s560m560s1680m560s1680m560s1680m560s560m560s1680m560s1680m560s560" "m560s560m560s560m560s560m560s560m560s560m560s1680m560s560m560s560" @@ -66,6 +69,7 @@ TEST(TestSendAiwa, SendWithRepeats) { irsend.reset(); irsend.sendAiwaRCT501(0x7F, kAiwaRcT501Bits, 2); // 2 repeats. EXPECT_EQ( + "f38000d33" "m8960s4480" "m560s560m560s1680m560s1680m560s1680m560s560m560s1680m560s1680m560s560" "m560s560m560s560m560s560m560s560m560s560m560s1680m560s560m560s560" @@ -86,6 +90,7 @@ TEST(TestSendAiwa, SendUnusualSize) { irsend.reset(); irsend.sendAiwaRCT501(0x12, 8); EXPECT_EQ( + "f38000d33" "m8960s4480" "m560s560m560s1680m560s1680m560s1680m560s560m560s1680m560s1680m560s560" "m560s560m560s560m560s560m560s560m560s560m560s1680m560s560m560s560" @@ -98,6 +103,7 @@ TEST(TestSendAiwa, SendUnusualSize) { irsend.reset(); irsend.sendAiwaRCT501(0x1234567890, 37); EXPECT_EQ( + "f38000d33" "m8960s4480" "m560s560m560s1680m560s1680m560s1680m560s560m560s1680m560s1680m560s560" "m560s560m560s560m560s560m560s560m560s560m560s1680m560s560m560s560" diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_Carrier_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Carrier_test.cpp similarity index 99% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_Carrier_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_Carrier_test.cpp index 24bdc232a..053b31dd4 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_Carrier_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Carrier_test.cpp @@ -15,6 +15,7 @@ TEST(TestSendCarrierAC, SendDataOnly) { irsend.reset(); irsend.sendCarrierAC(0x0); EXPECT_EQ( + "f38000d50" "m8532s4228" "m628s532m628s532m628s532m628s532m628s532m628s532m628s532m628s532" "m628s532m628s532m628s532m628s532m628s532m628s532m628s532m628s532" @@ -37,6 +38,7 @@ TEST(TestSendCarrierAC, SendDataOnly) { irsend.reset(); irsend.sendCarrierAC(0x12345678); EXPECT_EQ( + "f38000d50" "m8532s4228" "m628s532m628s532m628s532m628s1320m628s532m628s532m628s1320m628s532" "m628s532m628s532m628s1320m628s1320m628s532m628s1320m628s532m628s532" @@ -60,6 +62,7 @@ TEST(TestSendCarrierAC, SendDataOnly) { irsend.reset(); irsend.sendCarrierAC(0x4CCA541D); EXPECT_EQ( + "f38000d50" "m8532s4228" "m628s532m628s1320m628s532m628s532m628s1320m628s1320m628s532m628s532" "m628s1320m628s1320m628s532m628s532m628s1320m628s532m628s1320m628s532" @@ -89,6 +92,7 @@ TEST(TestSendCarrierAC, SendWithRepeats) { irsend.reset(); irsend.sendCarrierAC(0x12345678, kCarrierAcBits, 2); // two repeats. EXPECT_EQ( + "f38000d50" "m8532s4228" "m628s532m628s532m628s532m628s1320m628s532m628s532m628s1320m628s532" "m628s532m628s532m628s1320m628s1320m628s532m628s1320m628s532m628s532" diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_Coolix_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Coolix_test.cpp similarity index 70% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_Coolix_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_Coolix_test.cpp index 8b096ffca..0f97c5ead 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_Coolix_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Coolix_test.cpp @@ -15,6 +15,15 @@ TEST(TestSendCoolix, SendDataOnly) { irsend.reset(); irsend.sendCOOLIX(0x0); EXPECT_EQ( + "f38000d50" + "m4480s4480" + "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" + "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" + "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" + "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" + "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" + "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" + "m560s5040" "m4480s4480" "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" @@ -28,6 +37,15 @@ TEST(TestSendCoolix, SendDataOnly) { irsend.reset(); irsend.sendCOOLIX(0xAA55AA); EXPECT_EQ( + "f38000d50" + "m4480s4480" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" + "m560s5040" "m4480s4480" "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" @@ -41,6 +59,15 @@ TEST(TestSendCoolix, SendDataOnly) { irsend.reset(); irsend.sendCOOLIX(0xFFFFFF); EXPECT_EQ( + "f38000d50" + "m4480s4480" + "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" + "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" + "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" + "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" + "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" + "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" + "m560s5040" "m4480s4480" "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" @@ -60,6 +87,7 @@ TEST(TestSendCoolix, SendWithRepeats) { irsend.reset(); irsend.sendCOOLIX(0xAA55AA, kCoolixBits, 1); // 1 repeat. EXPECT_EQ( + "f38000d50" "m4480s4480" "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" @@ -79,6 +107,7 @@ TEST(TestSendCoolix, SendWithRepeats) { irsend.outputStr()); irsend.sendCOOLIX(0xAA55AA, kCoolixBits, 2); // 2 repeats. EXPECT_EQ( + "f38000d50" "m4480s4480" "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" @@ -114,6 +143,11 @@ TEST(TestSendCoolix, SendUnusualSize) { irsend.reset(); irsend.sendCOOLIX(0x0, 8); EXPECT_EQ( + "f38000d50" + "m4480s4480" + "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" + "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" + "m560s5040" "m4480s4480" "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" @@ -123,6 +157,25 @@ TEST(TestSendCoolix, SendUnusualSize) { irsend.reset(); irsend.sendCOOLIX(0x1234567890ABCDEF, 64); EXPECT_EQ( + "f38000d50" + "m4480s4480" + "m560s560m560s560m560s560m560s1680m560s560m560s560m560s1680m560s560" + "m560s1680m560s1680m560s1680m560s560m560s1680m560s1680m560s560m560s1680" + "m560s560m560s560m560s1680m560s1680m560s560m560s1680m560s560m560s560" + "m560s1680m560s1680m560s560m560s560m560s1680m560s560m560s1680m560s1680" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s1680m560s560" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s560m560s1680" + "m560s560m560s1680m560s1680m560s1680m560s1680m560s560m560s560m560s560" + "m560s1680m560s560m560s560m560s560m560s560m560s1680m560s1680m560s1680" + "m560s1680m560s560m560s560m560s1680m560s560m560s560m560s560m560s560" + "m560s560m560s1680m560s1680m560s560m560s1680m560s1680m560s1680m560s1680" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s1680" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s560" + "m560s1680m560s1680m560s560m560s560m560s1680m560s1680m560s560m560s1680" + "m560s560m560s560m560s1680m560s1680m560s560m560s560m560s1680m560s560" + "m560s1680m560s1680m560s1680m560s560m560s1680m560s1680m560s1680m560s1680" + "m560s560m560s560m560s560m560s1680m560s560m560s560m560s560m560s560" + "m560s5040" "m4480s4480" "m560s560m560s560m560s560m560s1680m560s560m560s560m560s1680m560s560" "m560s1680m560s1680m560s1680m560s560m560s1680m560s1680m560s560m560s1680" @@ -411,7 +464,7 @@ TEST(TestCoolixACClass, HumanReadable) { // Initial starting point. EXPECT_EQ( - "Power: On, Fan: 5 (AUTO), Mode: 2 (AUTO), Temp: 25C, " + "Power: On, Mode: 2 (AUTO), Fan: 5 (AUTO), Temp: 25C, " "Zone Follow: Off, Sensor Temp: Ignored", ircoolix.toString()); @@ -420,11 +473,11 @@ TEST(TestCoolixACClass, HumanReadable) { ircoolix.setMode(kCoolixCool); ircoolix.setFan(kCoolixFanMin); EXPECT_EQ( - "Power: On, Fan: 4 (MIN), Mode: 0 (COOL), Temp: 22C, " + "Power: On, Mode: 0 (COOL), Fan: 4 (MIN), Temp: 22C, " "Zone Follow: On, Sensor Temp: 24C", ircoolix.toString()); ircoolix.setSwing(); - EXPECT_EQ("Power: On, Fan: 3 (UNKNOWN), Swing: Toggle", ircoolix.toString()); + EXPECT_EQ("Power: On, Swing: Toggle", ircoolix.toString()); ircoolix.setPower(false); EXPECT_EQ("Power: Off", ircoolix.toString()); } @@ -434,12 +487,113 @@ TEST(TestCoolixACClass, KnownExamples) { ircoolix.setRaw(0b101100101011111111100100); EXPECT_EQ( - "Power: On, Fan: 5 (AUTO), Mode: 4 (FAN), Zone Follow: Off, " + "Power: On, Mode: 4 (FAN), Fan: 5 (AUTO), Zone Follow: Off, " "Sensor Temp: Ignored", ircoolix.toString()); ircoolix.setRaw(0b101100101001111100000000); EXPECT_EQ( - "Power: On, Fan: 4 (MIN), Mode: 0 (COOL), Temp: 17C, " + "Power: On, Mode: 0 (COOL), Fan: 4 (MIN), Temp: 17C, " "Zone Follow: Off, Sensor Temp: Ignored", ircoolix.toString()); } + +TEST(TestCoolixACClass, Issue579FanAuto0) { + IRCoolixAC ircoolix(0); + + ircoolix.setRaw(0xB21F28); + EXPECT_EQ( + "Power: On, Mode: 2 (AUTO), Fan: 0 (AUTO0), Temp: 20C, " + "Zone Follow: Off, Sensor Temp: Ignored", + ircoolix.toString()); +} + +TEST(TestCoolixACClass, RealCaptureExample) { + IRsendTest irsend(0); + IRrecv irrecv(0); + + // From Issue #579 + uint16_t powerOffRawData[199] = { + 4444, 4434, 590, 1578, 698, 446, 590, 1578, 622, 1596, 622, 500, + 644, 476, 644, 1548, 588, 532, 594, 530, 612, 1578, 590, 532, + 588, 534, 672, 1518, 594, 1598, 590, 510, 612, 1580, 644, 480, + 612, 1578, 644, 1548, 644, 1548, 594, 1598, 642, 506, 644, 1550, + 644, 1548, 594, 1600, 644, 478, 644, 478, 642, 480, 644, 478, + 642, 1548, 594, 530, 590, 532, 614, 1578, 644, 1548, 594, 1600, + 588, 534, 566, 556, 588, 530, 590, 532, 586, 514, 612, 532, + 588, 532, 590, 534, 588, 1578, 642, 1576, 642, 1550, 588, 1602, + 588, 1580, 642, 4712, 4546, 4406, 588, 1606, 642, 478, 644, 1550, + 590, 1604, 588, 534, 586, 532, 586, 1582, 642, 480, 642, 480, + 668, 1550, 642, 480, 642, 478, 642, 1552, 612, 1578, 586, 538, + 588, 1580, 674, 472, 590, 1602, 586, 1580, 618, 1576, 642, 1548, + 594, 530, 590, 1584, 608, 1578, 644, 1550, 642, 480, 642, 478, + 642, 480, 642, 480, 642, 1550, 590, 530, 592, 528, 592, 1602, + 642, 1548, 592, 1604, 586, 584, 642, 480, 640, 480, 640, 480, + 642, 480, 642, 480, 642, 480, 642, 480, 642, 1552, 590, 1604, + 588, 1578, 642, 1552, 640, 1550, 592}; // COOLIX B27BE0 + + irsend.begin(); + + irsend.reset(); + + irsend.sendRaw(powerOffRawData, 199, 38000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(COOLIX, irsend.capture.decode_type); + EXPECT_EQ(kCoolixBits, irsend.capture.bits); + EXPECT_EQ(kCoolixOff, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); +} + + +// Tests to debug/fix: +// https://github.com/markszabo/IRremoteESP8266/issues/624 +TEST(TestCoolixACClass, Issue624HandleSpecialStatesBetter) { + IRCoolixAC ac(0); + ac.begin(); + // Default + EXPECT_EQ( + "Power: On, Mode: 2 (AUTO), Fan: 5 (AUTO), Temp: 25C, Zone Follow: Off, " + "Sensor Temp: Ignored", + ac.toString()); + EXPECT_EQ(0xB2BFC8, ac.getRaw()); + // Change of settings. + ac.setPower(true); + ac.setTemp(24); + ac.setMode(kCoolixCool); + ac.setFan(kCoolixFanAuto); + EXPECT_EQ( + "Power: On, Mode: 0 (COOL), Fan: 5 (AUTO), Temp: 24C, Zone Follow: Off, " + "Sensor Temp: Ignored", + ac.toString()); + EXPECT_EQ(0xB2BF40, ac.getRaw()); + // Turn the unit off. + ac.setPower(false); + EXPECT_EQ( + "Power: Off", + ac.toString()); + EXPECT_EQ(kCoolixOff, ac.getRaw()); + // Repeat change of settings. + ac.setPower(true); + ac.setTemp(24); + ac.setMode(kCoolixCool); + ac.setFan(kCoolixFanAuto); + EXPECT_EQ( + "Power: On, Mode: 0 (COOL), Fan: 5 (AUTO), Temp: 24C, Zone Follow: Off, " + "Sensor Temp: Ignored", + ac.toString()); + EXPECT_EQ(0xB2BF40, ac.getRaw()); + + // Now test if we setRaw() a special state first. + ac.setRaw(kCoolixSwing); + // Repeat change of settings. + ac.setPower(true); + ac.setTemp(24); + ac.setMode(kCoolixCool); + ac.setFan(kCoolixFanAuto); + EXPECT_EQ( + "Power: On, Mode: 0 (COOL), Fan: 5 (AUTO), Temp: 24C, Zone Follow: Off, " + "Sensor Temp: Ignored", + ac.toString()); + EXPECT_EQ(0xB2BF40, ac.getRaw()); +} diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Daikin_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Daikin_test.cpp new file mode 100644 index 000000000..67d144d54 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Daikin_test.cpp @@ -0,0 +1,1880 @@ +// Copyright 2017 David Conran +#include "ir_Daikin.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "gtest/gtest.h" + +// Tests for sendDaikin(). + +// Test sending typical data only. +TEST(TestSendDaikin, SendDataOnly) { + IRsendTest irsend(0); + irsend.begin(); + + uint8_t daikin_code[kDaikinStateLength] = { + 0x11, 0xDA, 0x27, 0x00, 0xC5, 0x00, 0x00, 0xD7, + 0x11, 0xDA, 0x27, 0xF0, 0x00, 0x00, 0x00, 0x20, 0x11, + 0xDA, 0x27, 0x00, 0x00, 0x41, 0x1E, 0x00, 0xB0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xE3}; + + irsend.reset(); + irsend.sendDaikin(daikin_code); + EXPECT_EQ( + "f38000d50" + "m428s428m428s428m428s428m428s428m428s428" + "m428s29428" + "m3650s1623" + "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" + "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" + "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s1280m428s428m428s1280m428s428m428s428m428s428m428s1280m428s1280" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s1280m428s1280m428s1280m428s428m428s1280m428s428m428s1280m428s1280" + "m428s29428" + "m3650s1623" + "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" + "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" + "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s1280m428s1280m428s1280m428s1280" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s1280m428s428m428s428" + "m428s29428" + "m3650s1623" + "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" + "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" + "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s1280m428s428m428s428m428s428m428s428m428s428m428s1280m428s428" + "m428s428m428s1280m428s1280m428s1280m428s1280m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s1280m428s1280m428s428m428s1280" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s1280m428s1280" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s1280m428s1280m428s428m428s428m428s428m428s1280m428s1280m428s1280" + "m428s29428", + irsend.outputStr()); +} + +// Test sending with repeats. +TEST(TestSendDaikin, SendWithRepeats) { + IRsendTest irsend(0); + irsend.begin(); + + irsend.reset(); + uint8_t daikin_code[kDaikinStateLengthShort] = { + 0x11, 0xDA, 0x27, 0xF0, 0x00, 0x00, 0x00, 0x20, 0x11, + 0xDA, 0x27, 0x00, 0x00, 0x41, 0x1E, 0x00, 0xB0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xE3}; + irsend.reset(); + + irsend.sendDaikin(daikin_code, kDaikinStateLengthShort, 1); + EXPECT_EQ( + "f38000d50" + "m428s428m428s428m428s428m428s428m428s428" + "m428s29428m3650s1623" + "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" + "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" + "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s1280m428s428m428s1280m428s428m428s428m428s428m428s1280m428s1280" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s1280m428s1280m428s1280m428s428m428s1280m428s428m428s1280m428s1280" + "m428s29428m3650s1623" + "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" + "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" + "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s1280m428s1280m428s1280m428s1280" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s1280m428s428m428s428" + "m428s29428m3650s1623" + "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" + "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" + "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s1280m428s428m428s428m428s428m428s428m428s428m428s1280m428s428" + "m428s428m428s1280m428s1280m428s1280m428s1280m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s1280m428s1280m428s428m428s1280" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s1280m428s1280" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s1280m428s1280m428s428m428s428m428s428m428s1280m428s1280m428s1280" + "m428s29428" + "m428s428m428s428m428s428m428s428m428s428" + "m428s29428m3650s1623" + "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" + "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" + "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s1280m428s428m428s1280m428s428m428s428m428s428m428s1280m428s1280" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s1280m428s1280m428s1280m428s428m428s1280m428s428m428s1280m428s1280" + "m428s29428m3650s1623" + "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" + "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" + "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s1280m428s1280m428s1280m428s1280" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s1280m428s428m428s428" + "m428s29428m3650s1623" + "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" + "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" + "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s1280m428s428m428s428m428s428m428s428m428s428m428s1280m428s428" + "m428s428m428s1280m428s1280m428s1280m428s1280m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s1280m428s1280m428s428m428s1280" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s1280m428s1280" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s1280m428s1280m428s428m428s428m428s428m428s1280m428s1280m428s1280" + "m428s29428", + irsend.outputStr()); +} + +// Test sending atypical sizes. +TEST(TestSendDaikin, SendUnexpectedSizes) { + IRsendTest irsend(4); + irsend.begin(); + + uint8_t daikin_short_code[kDaikinStateLengthShort - 1] = { + 0x11, 0xDA, 0x27, 0xF0, 0x00, 0x00, 0x00, 0x20, 0x11, + 0xDA, 0x27, 0x00, 0x00, 0x41, 0x1E, 0x00, 0xB0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00}; + + irsend.reset(); + irsend.sendDaikin(daikin_short_code, kDaikinStateLengthShort - 1); + ASSERT_EQ("", irsend.outputStr()); + + uint8_t daikin_long_code[kDaikinStateLengthShort + 1] = { + 0x11, 0xDA, 0x27, 0xF0, 0x00, 0x00, 0x00, 0x20, 0x11, 0xDA, + 0x27, 0x00, 0x00, 0x41, 0x1E, 0x00, 0xB0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xE3, 0x11}; + irsend.reset(); + irsend.sendDaikin(daikin_long_code, kDaikinStateLengthShort + 1); + ASSERT_EQ( + "f38000d50" + "m428s428m428s428m428s428m428s428m428s428" + "m428s29428m3650s1623" + "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" + "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" + "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s1280m428s428m428s1280m428s428m428s428m428s428m428s1280m428s1280" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s1280m428s1280m428s1280m428s428m428s1280m428s428m428s1280m428s1280" + "m428s29428m3650s1623" + "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" + "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" + "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s1280m428s1280m428s1280m428s1280" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s1280m428s428m428s428" + "m428s29428m3650s1623" + "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" + "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" + "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s1280m428s428m428s428m428s428m428s428m428s428m428s1280m428s428" + "m428s428m428s1280m428s1280m428s1280m428s1280m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s1280m428s1280m428s428m428s1280" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s1280m428s1280" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s1280m428s1280m428s428m428s428m428s428m428s1280m428s1280m428s1280" + "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" + "m428s29428", + irsend.outputStr()); +} + +// Tests for IRDaikinESP class. + +TEST(TestDaikinClass, Power) { + IRDaikinESP ac(0); + ac.begin(); + + ac.on(); + EXPECT_TRUE(ac.getPower()); + + ac.off(); + EXPECT_FALSE(ac.getPower()); + + ac.setPower(true); + EXPECT_TRUE(ac.getPower()); + + ac.setPower(false); + EXPECT_FALSE(ac.getPower()); +} + +TEST(TestDaikinClass, Temperature) { + IRDaikinESP ac(0); + ac.begin(); + + ac.setTemp(0); + EXPECT_EQ(kDaikinMinTemp, ac.getTemp()); + + ac.setTemp(255); + EXPECT_EQ(kDaikinMaxTemp, ac.getTemp()); + + ac.setTemp(kDaikinMinTemp); + EXPECT_EQ(kDaikinMinTemp, ac.getTemp()); + + ac.setTemp(kDaikinMaxTemp); + EXPECT_EQ(kDaikinMaxTemp, ac.getTemp()); + + ac.setTemp(kDaikinMinTemp - 1); + EXPECT_EQ(kDaikinMinTemp, ac.getTemp()); + + ac.setTemp(kDaikinMaxTemp + 1); + EXPECT_EQ(kDaikinMaxTemp, ac.getTemp()); + + ac.setTemp(kDaikinMinTemp + 1); + EXPECT_EQ(kDaikinMinTemp + 1, ac.getTemp()); + + ac.setTemp(21); + EXPECT_EQ(21, ac.getTemp()); + + ac.setTemp(25); + EXPECT_EQ(25, ac.getTemp()); + + ac.setTemp(29); + EXPECT_EQ(29, ac.getTemp()); +} + +TEST(TestDaikinClass, OperatingMode) { + IRDaikinESP ac(0); + ac.begin(); + + ac.setMode(kDaikinAuto); + EXPECT_EQ(kDaikinAuto, ac.getMode()); + + ac.setMode(kDaikinCool); + EXPECT_EQ(kDaikinCool, ac.getMode()); + + ac.setMode(kDaikinHeat); + EXPECT_EQ(kDaikinHeat, ac.getMode()); + + ac.setMode(kDaikinDry); + EXPECT_EQ(kDaikinDry, ac.getMode()); + + ac.setMode(kDaikinFan); + EXPECT_EQ(kDaikinFan, ac.getMode()); + + ac.setMode(kDaikinFan + 1); + EXPECT_EQ(kDaikinAuto, ac.getMode()); + + ac.setMode(kDaikinAuto + 1); + EXPECT_EQ(kDaikinAuto, ac.getMode()); + + ac.setMode(255); + EXPECT_EQ(kDaikinAuto, ac.getMode()); +} + +TEST(TestDaikinClass, VaneSwing) { + IRDaikinESP ac(0); + ac.begin(); + + ac.setSwingHorizontal(true); + ac.setSwingVertical(false); + + ac.setSwingHorizontal(true); + EXPECT_TRUE(ac.getSwingHorizontal()); + EXPECT_FALSE(ac.getSwingVertical()); + + ac.setSwingVertical(true); + EXPECT_TRUE(ac.getSwingHorizontal()); + EXPECT_TRUE(ac.getSwingVertical()); + + ac.setSwingHorizontal(false); + EXPECT_FALSE(ac.getSwingHorizontal()); + EXPECT_TRUE(ac.getSwingVertical()); + + ac.setSwingVertical(false); + EXPECT_FALSE(ac.getSwingHorizontal()); + EXPECT_FALSE(ac.getSwingVertical()); +} + +TEST(TestDaikinClass, QuietMode) { + IRDaikinESP ac(0); + ac.begin(); + + ac.setQuiet(true); + EXPECT_TRUE(ac.getQuiet()); + + ac.setQuiet(false); + EXPECT_FALSE(ac.getQuiet()); + + ac.setQuiet(true); + EXPECT_TRUE(ac.getQuiet()); + + // Setting Econo mode should NOT change out of quiet mode. + ac.setEcono(true); + EXPECT_TRUE(ac.getQuiet()); + ac.setEcono(false); + EXPECT_TRUE(ac.getQuiet()); + + // But setting Powerful mode should exit out of quiet mode. + ac.setPowerful(true); + EXPECT_FALSE(ac.getQuiet()); +} + +TEST(TestDaikinClass, PowerfulMode) { + IRDaikinESP ac(0); + ac.begin(); + + ac.setPowerful(true); + EXPECT_TRUE(ac.getPowerful()); + + ac.setPowerful(false); + EXPECT_FALSE(ac.getPowerful()); + + ac.setPowerful(true); + EXPECT_TRUE(ac.getPowerful()); + + ac.setQuiet(true); + EXPECT_FALSE(ac.getPowerful()); + + ac.setPowerful(true); + ac.setEcono(true); + EXPECT_FALSE(ac.getPowerful()); +} + +TEST(TestDaikinClass, EconoMode) { + IRDaikinESP ac(0); + ac.begin(); + + ac.setEcono(true); + EXPECT_TRUE(ac.getEcono()); + + ac.setEcono(false); + EXPECT_FALSE(ac.getEcono()); + + ac.setEcono(true); + EXPECT_TRUE(ac.getEcono()); + + // Setting Quiet mode should NOT change out of Econo mode. + ac.setQuiet(true); + EXPECT_TRUE(ac.getEcono()); + ac.setQuiet(false); + EXPECT_TRUE(ac.getEcono()); + + // But setting Powerful mode should exit out of Econo mode. + ac.setPowerful(true); + EXPECT_FALSE(ac.getEcono()); +} + +TEST(TestDaikinClass, FanSpeed) { + IRDaikinESP ac(0); + ac.begin(); + + // Unexpected value should default to Auto. + ac.setFan(0); + EXPECT_EQ(kDaikinFanAuto, ac.getFan()); + + // Unexpected value should default to Auto. + ac.setFan(255); + EXPECT_EQ(kDaikinFanAuto, ac.getFan()); + + ac.setFan(kDaikinFanMax); + EXPECT_EQ(kDaikinFanMax, ac.getFan()); + + // Beyond Max should default to Auto. + ac.setFan(kDaikinFanMax + 1); + EXPECT_EQ(kDaikinFanAuto, ac.getFan()); + + ac.setFan(kDaikinFanMax - 1); + EXPECT_EQ(kDaikinFanMax - 1, ac.getFan()); + + ac.setFan(kDaikinFanMin); + EXPECT_EQ(kDaikinFanMin, ac.getFan()); + + ac.setFan(kDaikinFanMin + 1); + EXPECT_EQ(kDaikinFanMin + 1, ac.getFan()); + + // Beyond Min should default to Auto. + ac.setFan(kDaikinFanMin - 1); + EXPECT_EQ(kDaikinFanAuto, ac.getFan()); + + ac.setFan(3); + EXPECT_EQ(3, ac.getFan()); + + ac.setFan(kDaikinFanAuto); + EXPECT_EQ(kDaikinFanAuto, ac.getFan()); + + ac.setFan(kDaikinFanQuiet); + EXPECT_EQ(kDaikinFanQuiet, ac.getFan()); +} + +TEST(TestDaikinClass, CurrentTime) { + IRDaikinESP ac(0); + ac.begin(); + + ac.setCurrentTime(0); // 00:00 + EXPECT_EQ(0, ac.getCurrentTime()); + + ac.setCurrentTime(754); // 12:34 + EXPECT_EQ(754, ac.getCurrentTime()); + + ac.setCurrentTime(1439); // 23:59 + EXPECT_EQ(1439, ac.getCurrentTime()); +} + +TEST(TestDaikinClass, OnOffTimers) { + IRDaikinESP ac(0); + ac.begin(); + + // Both timers turned off. + ac.disableOnTimer(); + ac.disableOffTimer(); + EXPECT_FALSE(ac.getOnTimerEnabled()); + EXPECT_EQ(kDaikinUnusedTime, ac.getOnTime()); + EXPECT_FALSE(ac.getOffTimerEnabled()); + EXPECT_EQ(kDaikinUnusedTime, ac.getOffTime()); + + // Turn on just the On Timer. + ac.enableOnTimer(123); + EXPECT_TRUE(ac.getOnTimerEnabled()); + EXPECT_EQ(123, ac.getOnTime()); + EXPECT_FALSE(ac.getOffTimerEnabled()); + EXPECT_EQ(kDaikinUnusedTime, ac.getOffTime()); + + // Now turn on the Off Timer. + ac.enableOffTimer(754); + EXPECT_TRUE(ac.getOffTimerEnabled()); + EXPECT_EQ(754, ac.getOffTime()); + EXPECT_TRUE(ac.getOnTimerEnabled()); + EXPECT_EQ(123, ac.getOnTime()); + + // Turn off the just the On Timer. + ac.disableOnTimer(); + EXPECT_FALSE(ac.getOnTimerEnabled()); + EXPECT_EQ(kDaikinUnusedTime, ac.getOnTime()); + EXPECT_TRUE(ac.getOffTimerEnabled()); + EXPECT_EQ(754, ac.getOffTime()); + + // Now turn off the Off Timer. + ac.disableOffTimer(); + EXPECT_FALSE(ac.getOffTimerEnabled()); + EXPECT_EQ(kDaikinUnusedTime, ac.getOffTime()); + EXPECT_FALSE(ac.getOnTimerEnabled()); + EXPECT_EQ(kDaikinUnusedTime, ac.getOnTime()); + + // Use some canary values around the timers to ensure no accidental + // bit flips happen. i.e. Neighbouring bytes in the state. + // (Found some during testing on systems with different endian-ness) + // Tests here to make sure it never happens again. + ac.setSwingHorizontal(true); + ac.setPowerful(true); + ac.disableOffTimer(); + ac.disableOnTimer(); + ASSERT_TRUE(ac.getSwingHorizontal()); + ASSERT_TRUE(ac.getPowerful()); + ac.enableOnTimer(123); + ac.enableOffTimer(456); + ASSERT_TRUE(ac.getSwingHorizontal()); + ASSERT_TRUE(ac.getPowerful()); + ac.disableOffTimer(); + ac.disableOnTimer(); + ASSERT_TRUE(ac.getSwingHorizontal()); + ASSERT_TRUE(ac.getPowerful()); + + ac.setSwingHorizontal(false); + ac.setPowerful(false); + ac.disableOffTimer(); + ac.disableOnTimer(); + ASSERT_FALSE(ac.getSwingHorizontal()); + ASSERT_FALSE(ac.getPowerful()); + ac.enableOnTimer(123); + ac.enableOffTimer(456); + ASSERT_FALSE(ac.getSwingHorizontal()); + ASSERT_FALSE(ac.getPowerful()); + ac.disableOffTimer(); + ac.disableOnTimer(); + ASSERT_FALSE(ac.getSwingHorizontal()); + ASSERT_FALSE(ac.getPowerful()); +} + +// Test Eye mode. +TEST(TestDaikinClass, EyeSetting) { + IRDaikinESP ac(0); + ac.begin(); + + // The Eye setting is stored in the same byte as Econo mode. + // Econo mode tests are there to make sure it isn't harmed and vice-versa. + ac.setEcono(false); + ac.setEye(false); + ASSERT_FALSE(ac.getEye()); + EXPECT_FALSE(ac.getEcono()); + + ac.setEye(true); + ASSERT_TRUE(ac.getEye()); + EXPECT_FALSE(ac.getEcono()); + + ac.setEcono(false); + ASSERT_TRUE(ac.getEye()); + EXPECT_FALSE(ac.getEcono()); + + ac.setEcono(true); + ASSERT_TRUE(ac.getEye()); + EXPECT_TRUE(ac.getEcono()); + + ac.setEye(false); + ASSERT_FALSE(ac.getEye()); + EXPECT_TRUE(ac.getEcono()); +} + +// Test Mold mode. +TEST(TestDaikinClass, MoldSetting) { + IRDaikinESP ac(0); + ac.begin(); + + ac.setMold(false); + ASSERT_FALSE(ac.getMold()); + + ac.setMold(true); + ASSERT_TRUE(ac.getMold()); + + ac.setMold(false); + ASSERT_FALSE(ac.getMold()); +} + +// Test Comfort mode. +TEST(TestDaikinClass, ComfortSetting) { + IRDaikinESP ac(0); + ac.begin(); + + ac.setComfort(false); + ASSERT_FALSE(ac.getComfort()); + + ac.setComfort(true); + ASSERT_TRUE(ac.getComfort()); + + ac.setComfort(false); + ASSERT_FALSE(ac.getComfort()); +} + +// Test Sensor mode. +TEST(TestDaikinClass, SensorSetting) { + IRDaikinESP ac(0); + ac.begin(); + + ac.setSensor(false); + ASSERT_FALSE(ac.getSensor()); + + ac.setSensor(true); + ASSERT_TRUE(ac.getSensor()); + + ac.setSensor(false); + ASSERT_FALSE(ac.getSensor()); +} + +TEST(TestDaikinClass, RenderTime) { + EXPECT_EQ("0:00", IRDaikinESP::renderTime(0)); + EXPECT_EQ("0:10", IRDaikinESP::renderTime(10)); + EXPECT_EQ("1:00", IRDaikinESP::renderTime(1 * 60 + 0)); + EXPECT_EQ("23:59", IRDaikinESP::renderTime(23 * 60 + 59)); +} + +TEST(TestDaikinClass, SetAndGetRaw) { + IRDaikinESP ac(0); + uint8_t shortState[kDaikinStateLengthShort] = { + 0x11, 0xDA, 0x27, 0x00, 0x42, 0x00, 0x00, 0x54, 0x11, + 0xDA, 0x27, 0x00, 0x00, 0x49, 0x1E, 0x00, 0xB0, 0x00, + 0x00, 0x06, 0x60, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x4F}; + uint8_t longState[kDaikinStateLength] = { + 0x11, 0xDA, 0x27, 0x00, 0xC5, 0x00, 0x00, 0xD7, + 0x11, 0xDA, 0x27, 0x00, 0x42, 0x00, 0x00, 0x54, 0x11, + 0xDA, 0x27, 0x00, 0x00, 0x49, 0x1E, 0x00, 0xB0, 0x00, + 0x00, 0x06, 0x60, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x4F}; + uint8_t expectedState[kDaikinStateLength] = { + 0x11, 0xDA, 0x27, 0x00, 0xC5, 0x00, 0x00, 0xD7, + 0x11, 0xDA, 0x27, 0x00, 0x42, 0x00, 0x00, 0x54, 0x11, + 0xDA, 0x27, 0x00, 0x00, 0x48, 0x2A, 0x00, 0xB0, 0x00, + 0x00, 0x06, 0x60, 0x00, 0x00, 0xC0, 0x00, 0x02, 0x5C}; + + EXPECT_STATE_EQ(longState, ac.getRaw(), kDaikinBits); + // toggle the power state. + ac.setPower(!ac.getPower()); + ac.setTemp(21); + ac.setMold(true); + EXPECT_STATE_EQ(expectedState, ac.getRaw(), kDaikinBits); + ac.setRaw(longState); + EXPECT_STATE_EQ(longState, ac.getRaw(), kDaikinBits); + ac.setRaw(shortState, kDaikinStateLengthShort); + EXPECT_STATE_EQ(longState, ac.getRaw(), kDaikinBits); +} + +TEST(TestDaikinClass, ChecksumValidation) { + uint8_t daikin_code[kDaikinStateLength] = { + 0x11, 0xDA, 0x27, 0x00, 0xC5, 0x00, 0x00, 0xD7, + 0x11, 0xDA, 0x27, 0xF0, 0x00, 0x00, 0x00, 0x02, 0x11, + 0xDA, 0x27, 0x00, 0x00, 0x41, 0x1E, 0x00, 0xB0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xE1}; + + EXPECT_TRUE(IRDaikinESP::validChecksum(daikin_code)); + // Change the array so the checksum is invalid. + daikin_code[0] ^= 0xFF; + EXPECT_FALSE(IRDaikinESP::validChecksum(daikin_code)); + // Restore the previous change, and change another byte. + daikin_code[0] ^= 0xFF; + daikin_code[4] ^= 0xFF; + EXPECT_FALSE(IRDaikinESP::validChecksum(daikin_code)); + daikin_code[4] ^= 0xFF; + // Change something in the 2nd block. + daikin_code[10] ^= 0xFF; + EXPECT_FALSE(IRDaikinESP::validChecksum(daikin_code)); + daikin_code[10] ^= 0xFF; + EXPECT_TRUE(IRDaikinESP::validChecksum(daikin_code)); + // Change something in the 3rd block. + daikin_code[20] ^= 0xFF; + EXPECT_FALSE(IRDaikinESP::validChecksum(daikin_code)); + daikin_code[20] ^= 0xFF; + EXPECT_TRUE(IRDaikinESP::validChecksum(daikin_code)); +} + +// Test human readable output. +TEST(TestDaikinClass, HumanReadable) { + IRDaikinESP ac(0); + + EXPECT_EQ( + "Power: On, Mode: 4 (HEAT), Temp: 15C, Fan: 11 (QUIET), " + "Powerful: Off, Quiet: Off, Sensor: Off, Eye: Off, Mold: Off, " + "Comfort: Off, Swing (Horizontal): Off, Swing (Vertical): Off, " + "Current Time: 0:00, On Time: Off, Off Time: Off", + ac.toString()); + ac.setMode(kDaikinAuto); + ac.setTemp(25); + ac.setFan(kDaikinFanAuto); + ac.setQuiet(true); + ac.setSensor(true); + ac.setEye(true); + ac.setMold(true); + ac.setSwingVertical(true); + ac.setSwingHorizontal(true); + ac.setCurrentTime(9 * 60 + 15); + ac.enableOnTimer(8 * 60 + 0); + ac.enableOffTimer(17 * 60 + 30); + ac.setComfort(true); + ac.off(); + EXPECT_EQ( + "Power: Off, Mode: 0 (AUTO), Temp: 25C, Fan: 10 (AUTO), " + "Powerful: Off, Quiet: On, Sensor: On, Eye: On, Mold: On, Comfort: On, " + "Swing (Horizontal): On, Swing (Vertical): On, " + "Current Time: 9:15, On Time: 8:00, Off Time: 17:30", + ac.toString()); +} + +// Test general message construction after tweaking some settings. +TEST(TestDaikinClass, MessageConstuction) { + IRDaikinESP ac(0); + IRsendTest irsend(4); + ac.begin(); + irsend.begin(); + + ac.setFan(kDaikinFanMin); + ac.setMode(kDaikinCool); + ac.setTemp(27); + ac.setSwingVertical(false); + ac.setSwingHorizontal(true); + ac.setQuiet(false); + ac.setPower(true); + + // Check everything for kicks. + EXPECT_EQ(kDaikinFanMin, ac.getFan()); + EXPECT_EQ(kDaikinCool, ac.getMode()); + EXPECT_EQ(27, ac.getTemp()); + EXPECT_FALSE(ac.getSwingVertical()); + EXPECT_TRUE(ac.getSwingHorizontal()); + EXPECT_FALSE(ac.getQuiet()); + EXPECT_TRUE(ac.getPower()); + + irsend.reset(); + irsend.sendDaikin(ac.getRaw()); + EXPECT_EQ( + "f38000d50" + "m428s428m428s428m428s428m428s428m428s428" + "m428s29428m3650s1623" + "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" + "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" + "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s1280m428s428m428s1280m428s428m428s428m428s428m428s1280m428s1280" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s1280m428s1280m428s1280m428s428m428s1280m428s428m428s1280m428s1280" + "m428s29428m3650s1623" + "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" + "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" + "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s1280m428s428m428s428m428s428m428s428m428s1280m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s1280m428s428m428s1280m428s428m428s1280m428s428" + "m428s29428m3650s1623" + "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" + "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" + "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s1280m428s428m428s428m428s1280m428s1280m428s1280m428s428m428s428" + "m428s428m428s1280m428s1280m428s428m428s1280m428s1280m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s1280m428s1280m428s428m428s428" + "m428s1280m428s1280m428s1280m428s1280m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s1280m428s1280m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s1280m428s1280m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s1280m428s1280" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s1280m428s1280m428s428m428s428m428s1280m428s1280m428s1280" + "m428s29428", + irsend.outputStr()); +} + +// Tests for decodeDaikin(). + +// Test decoding a message captured from a real IR remote. +TEST(TestDecodeDaikin, RealExample) { + IRDaikinESP ac(0); + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + uint8_t expectedState[kDaikinStateLength] = { + 0x11, 0xDA, 0x27, 0x00, 0xC5, 0x00, 0x00, 0xD7, + 0x11, 0xDA, 0x27, 0x00, 0x42, 0x3A, 0x05, 0x93, 0x11, + 0xDA, 0x27, 0x00, 0x00, 0x3F, 0x3A, 0x00, 0xA0, 0x00, + 0x0A, 0x25, 0x17, 0x01, 0x00, 0xC0, 0x00, 0x00, 0x32}; + uint16_t rawData[583] = { + 416, 446, 416, 446, 416, 446, 418, 446, 416, 446, 416, 25434, + 3436, 1768, 390, 1336, 390, 446, 416, 446, 416, 446, 416, 1336, + 390, 446, 416, 446, 416, 446, 416, 446, 416, 1336, 390, 448, + 416, 1336, 390, 1336, 390, 448, 416, 1336, 390, 1336, 390, 1338, + 388, 1338, 390, 1336, 390, 446, 416, 446, 416, 1336, 390, 446, + 416, 446, 416, 446, 416, 446, 416, 446, 416, 446, 416, 448, + 416, 446, 416, 446, 416, 446, 416, 1336, 390, 446, 416, 1336, + 390, 448, 416, 446, 416, 446, 416, 1336, 390, 1336, 390, 446, + 416, 446, 416, 446, 416, 446, 416, 446, 416, 446, 416, 446, + 416, 446, 416, 446, 416, 448, 416, 446, 416, 446, 416, 446, + 416, 448, 414, 448, 416, 448, 416, 1336, 390, 1336, 390, 1336, + 390, 446, 414, 1336, 390, 448, 414, 1336, 390, 1336, 390, 34878, + 3436, 1768, 390, 1336, 390, 446, 416, 448, 416, 446, 416, 1336, + 390, 446, 416, 448, 416, 446, 416, 446, 416, 1336, 390, 446, + 416, 1336, 390, 1336, 390, 446, 416, 1336, 390, 1336, 390, 1336, + 390, 1336, 390, 1336, 392, 446, 414, 448, 416, 1336, 390, 446, + 416, 446, 416, 446, 416, 446, 414, 448, 416, 446, 416, 448, + 414, 448, 416, 446, 416, 446, 416, 446, 414, 1336, 390, 448, + 416, 446, 416, 446, 416, 448, 416, 1336, 390, 446, 416, 446, + 416, 1336, 390, 446, 416, 1336, 390, 1336, 390, 1336, 390, 446, + 416, 446, 414, 1338, 390, 446, 416, 1336, 390, 446, 416, 446, + 416, 446, 416, 446, 416, 446, 416, 1336, 390, 1336, 390, 446, + 416, 446, 416, 1336, 390, 446, 416, 446, 416, 1336, 390, 34876, + 3436, 1768, 388, 1336, 390, 446, 416, 446, 416, 448, 416, 1336, + 390, 446, 416, 446, 416, 446, 416, 448, 416, 1336, 390, 448, + 414, 1336, 390, 1336, 390, 446, 416, 1336, 388, 1338, 388, 1336, + 390, 1336, 390, 1336, 390, 446, 416, 446, 416, 1336, 390, 446, + 420, 442, 416, 446, 416, 446, 416, 446, 416, 446, 416, 446, + 416, 446, 416, 446, 416, 446, 416, 446, 416, 446, 416, 448, + 416, 446, 416, 448, 416, 446, 416, 448, 416, 446, 416, 1336, + 390, 1336, 390, 1336, 388, 1338, 390, 1336, 390, 1336, 392, 446, + 416, 446, 416, 448, 416, 1334, 390, 446, 416, 1338, 388, 1336, + 390, 1336, 390, 446, 416, 446, 416, 448, 414, 446, 416, 446, + 416, 446, 416, 448, 416, 446, 416, 446, 416, 446, 416, 446, + 416, 446, 416, 446, 416, 446, 416, 446, 416, 1336, 390, 446, + 416, 1336, 390, 446, 414, 448, 416, 446, 416, 446, 416, 446, + 416, 448, 416, 446, 416, 446, 416, 446, 416, 1336, 390, 446, + 416, 1336, 390, 446, 416, 446, 416, 446, 416, 448, 416, 1338, + 390, 444, 418, 1336, 390, 448, 416, 446, 416, 1336, 390, 446, + 416, 446, 416, 1336, 390, 1336, 388, 1336, 390, 446, 416, 1336, + 390, 448, 414, 448, 414, 448, 416, 1334, 390, 446, 416, 446, + 416, 446, 416, 448, 416, 446, 416, 446, 416, 448, 416, 446, + 416, 446, 416, 446, 416, 446, 416, 446, 416, 446, 416, 446, + 416, 446, 416, 446, 416, 446, 416, 446, 416, 446, 416, 446, + 416, 448, 416, 1336, 390, 1336, 390, 446, 416, 446, 416, 446, + 416, 446, 414, 446, 416, 448, 416, 446, 416, 448, 414, 446, + 418, 446, 416, 446, 416, 448, 416, 446, 416, 448, 416, 446, + 416, 448, 416, 446, 416, 1336, 390, 446, 416, 446, 416, 1338, + 390, 1336, 390, 446, 416, 446, 416}; // Captured by @sillyfrog + + irsend.reset(); + irsend.sendRaw(rawData, 583, 38000); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(DAIKIN, irsend.capture.decode_type); + ASSERT_EQ(kDaikinBits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + ac.setRaw(irsend.capture.state); + EXPECT_EQ( + "Power: On, Mode: 3 (COOL), Temp: 29C, Fan: 10 (AUTO), Powerful: On, " + "Quiet: Off, Sensor: Off, Eye: Off, Mold: Off, Comfort: Off, " + "Swing (Horizontal): Off, Swing (Vertical): Off, " + "Current Time: 22:18, On Time: 21:30, Off Time: 6:10", ac.toString()); +} + +// Decoding a message we entirely constructed based solely on a given state. +TEST(TestDecodeDaikin, ShortSyntheticExample) { + IRDaikinESP ac(0); + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + uint8_t shortState[kDaikinStateLengthShort] = { + 0x11, 0xDA, 0x27, 0x00, 0x42, 0x3A, 0x05, 0x93, 0x11, + 0xDA, 0x27, 0x00, 0x00, 0x3F, 0x3A, 0x00, 0xA0, 0x00, + 0x0A, 0x25, 0x17, 0x01, 0x00, 0xC0, 0x00, 0x00, 0x32}; + + uint8_t longState[kDaikinStateLength] = { + 0x11, 0xDA, 0x27, 0x00, 0xC5, 0x00, 0x00, 0xD7, + 0x11, 0xDA, 0x27, 0x00, 0x42, 0x3A, 0x05, 0x93, 0x11, + 0xDA, 0x27, 0x00, 0x00, 0x3F, 0x3A, 0x00, 0xA0, 0x00, + 0x0A, 0x25, 0x17, 0x01, 0x00, 0xC0, 0x00, 0x00, 0x32}; + irsend.reset(); + irsend.sendDaikin(shortState, kDaikinStateLengthShort); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(DAIKIN, irsend.capture.decode_type); + ASSERT_EQ(kDaikinBits, irsend.capture.bits); + EXPECT_STATE_EQ(longState, irsend.capture.state, irsend.capture.bits); + ac.setRaw(irsend.capture.state); + EXPECT_EQ( + "Power: On, Mode: 3 (COOL), Temp: 29C, Fan: 10 (AUTO), Powerful: On, " + "Quiet: Off, Sensor: Off, Eye: Off, Mold: Off, Comfort: Off, " + "Swing (Horizontal): Off, Swing (Vertical): Off, " + "Current Time: 22:18, On Time: 21:30, Off Time: 6:10", ac.toString()); +} + +// Decoding a message we entirely constructed based solely on a given state. +TEST(TestDecodeDaikin, LongSyntheticExample) { + IRDaikinESP ac(0); + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + uint8_t expectedState[kDaikinStateLength] = { + 0x11, 0xDA, 0x27, 0x00, 0xC5, 0x00, 0x00, 0xD7, + 0x11, 0xDA, 0x27, 0x00, 0x42, 0x3A, 0x05, 0x93, + 0x11, 0xDA, 0x27, 0x00, 0x00, 0x3F, 0x3A, 0x00, 0xA0, 0x00, + 0x0A, 0x25, 0x17, 0x01, 0x00, 0xC0, 0x00, 0x00, 0x32}; + + irsend.reset(); + irsend.sendDaikin(expectedState); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decodeDaikin(&irsend.capture)); + ASSERT_EQ(DAIKIN, irsend.capture.decode_type); + ASSERT_EQ(kDaikinBits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + ac.setRaw(irsend.capture.state); + EXPECT_EQ( + "Power: On, Mode: 3 (COOL), Temp: 29C, Fan: 10 (AUTO), Powerful: On, " + "Quiet: Off, Sensor: Off, Eye: Off, Mold: Off, Comfort: Off, " + "Swing (Horizontal): Off, Swing (Vertical): Off, " + "Current Time: 22:18, On Time: 21:30, Off Time: 6:10", ac.toString()); +} + +// Test decoding a message captured from a real IR remote. +TEST(TestDecodeDaikin2, RealExample) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + uint8_t expectedState[kDaikin2StateLength] = { + 0x11, 0xDA, 0x27, 0x00, 0x01, 0x7A, 0xC3, 0x70, 0x28, 0x0C, + 0x80, 0x04, 0xB0, 0x16, 0x24, 0x00, 0x00, 0xBE, 0xD5, 0xF5, + 0x11, 0xDA, 0x27, 0x00, 0x00, 0x08, 0x26, 0x00, 0xA0, 0x00, + 0x00, 0x06, 0x60, 0x00, 0x00, 0xC1, 0x80, 0x60, 0xE7}; + // "Off" Data from https://github.com/markszabo/IRremoteESP8266/issues/582 + uint16_t rawData[633] = { // Data supplied by @sheppy99 + 10024, 25180, 3494, 1732, 436, 1300, 436, 436, 432, 438, 430, 438, + 426, 1306, 430, 442, 430, 438, 428, 440, 430, 440, 430, 1304, + 432, 442, 428, 1308, 424, 1312, 428, 442, 428, 1306, 424, 1314, + 426, 1308, 434, 1306, 426, 1308, 428, 444, 426, 442, 428, 1310, + 428, 442, 424, 444, 426, 442, 426, 444, 424, 444, 426, 444, + 424, 446, 422, 446, 422, 446, 422, 446, 418, 1318, 418, 450, + 420, 448, 420, 448, 422, 448, 420, 450, 420, 448, 420, 450, + 420, 452, 418, 1318, 420, 450, 420, 1318, 420, 1314, 418, 1318, + 424, 1314, 424, 448, 422, 1316, 424, 1312, 426, 446, 422, 448, + 420, 448, 422, 448, 422, 1314, 418, 1320, 416, 452, 420, 448, + 420, 448, 422, 448, 422, 1314, 416, 1320, 422, 1316, 422, 450, + 418, 450, 420, 448, 420, 448, 416, 1320, 418, 452, 418, 1316, + 422, 448, 420, 450, 420, 450, 420, 448, 422, 1314, 418, 1320, + 418, 450, 420, 448, 420, 448, 420, 450, 420, 450, 418, 450, + 418, 450, 420, 450, 418, 452, 416, 452, 420, 450, 418, 1318, + 420, 452, 418, 452, 418, 1322, 416, 452, 416, 452, 418, 452, + 418, 452, 416, 454, 418, 452, 416, 456, 414, 452, 418, 454, + 416, 1320, 410, 1324, 418, 452, 418, 1320, 416, 452, 418, 1320, + 418, 1318, 420, 448, 420, 1316, 420, 450, 420, 450, 418, 450, + 420, 450, 418, 452, 418, 1320, 418, 450, 418, 450, 416, 1322, + 412, 458, 420, 450, 416, 452, 418, 452, 416, 452, 418, 452, + 416, 454, 416, 452, 418, 452, 416, 454, 414, 454, 416, 454, + 416, 454, 414, 456, 414, 454, 414, 456, 412, 454, 416, 456, + 414, 456, 412, 1326, 412, 1320, 412, 1322, 414, 1322, 418, 1320, + 420, 452, 418, 1318, 420, 1316, 422, 450, 420, 1314, 424, 448, + 422, 1314, 422, 448, 422, 1314, 418, 1318, 424, 1316, 422, 448, + 422, 1312, 424, 446, 422, 1314, 420, 1318, 422, 1316, 426, 1310, + 426, 35166, 3500, 1724, 446, 1296, 444, 432, 436, 432, 438, 432, + 436, 1296, 440, 434, 434, 436, 432, 436, 434, 436, 434, 1298, + 438, 438, 432, 1304, 428, 1304, 432, 442, 430, 1302, 430, 1308, + 430, 1306, 434, 1302, 432, 1306, 430, 440, 430, 438, 430, 1308, + 434, 438, 430, 440, 428, 440, 430, 440, 428, 442, 426, 444, + 428, 442, 426, 444, 426, 442, 426, 444, 424, 446, 422, 446, + 424, 446, 424, 446, 422, 446, 424, 448, 420, 448, 422, 446, + 422, 448, 422, 450, 420, 450, 414, 1320, 420, 450, 418, 450, + 418, 448, 420, 450, 418, 452, 418, 1320, 418, 1316, 422, 450, + 418, 452, 418, 1320, 420, 448, 418, 450, 420, 450, 418, 452, + 416, 452, 418, 450, 418, 452, 416, 452, 418, 452, 416, 454, + 416, 452, 416, 454, 416, 454, 414, 456, 416, 454, 414, 1322, + 416, 454, 416, 1320, 418, 452, 416, 454, 414, 454, 416, 454, + 414, 454, 414, 454, 414, 456, 414, 456, 412, 456, 414, 456, + 414, 456, 412, 456, 414, 458, 406, 464, 410, 458, 412, 458, + 410, 460, 410, 1326, 412, 1324, 414, 456, 412, 458, 412, 456, + 414, 456, 412, 458, 410, 458, 414, 458, 410, 458, 408, 460, + 410, 470, 400, 1324, 408, 1328, 410, 458, 410, 460, 414, 456, + 410, 456, 414, 458, 412, 460, 410, 458, 412, 458, 412, 460, + 408, 460, 410, 460, 408, 472, 396, 462, 408, 470, 402, 470, + 396, 472, 400, 470, 398, 1326, 412, 460, 408, 472, 396, 472, + 400, 470, 400, 472, 396, 1328, 410, 1324, 414, 458, 410, 458, + 410, 458, 412, 458, 412, 460, 408, 460, 410, 460, 410, 1324, + 414, 458, 410, 460, 408, 460, 410, 458, 410, 460, 410, 1326, + 412, 1322, 416, 456, 412, 1322, 412, 1326, 416, 1322, 418, 452, + 416, 454, 412, 1324, 418, 1320, 420, 1316, 420}; + irsend.reset(); + irsend.sendRaw(rawData, 633, 38000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(DAIKIN2, irsend.capture.decode_type); + ASSERT_EQ(kDaikin2Bits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); +} + +// Decoding a message we entirely constructed based solely on a given state. +TEST(TestDecodeDaikin2, SyntheticExample) { + IRDaikin2 ac(0); + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + uint8_t expectedState[kDaikin2StateLength] = { + 0x11, 0xDA, 0x27, 0x00, 0x01, 0x7A, 0xC3, 0x70, 0x28, 0x0C, + 0x80, 0x04, 0xB0, 0x16, 0x24, 0x00, 0x00, 0xBE, 0xD5, 0xF5, + 0x11, 0xDA, 0x27, 0x00, 0x00, 0x08, 0x26, 0x00, 0xA0, 0x00, + 0x00, 0x06, 0x60, 0x00, 0x00, 0xC1, 0x80, 0x60, 0xE7}; + + irsend.reset(); + irsend.sendDaikin2(expectedState); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(DAIKIN2, irsend.capture.decode_type); + ASSERT_EQ(kDaikin2Bits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + ac.setRaw(irsend.capture.state); + EXPECT_EQ( + "Power: Off, Mode: 0 (AUTO), Temp: 19C, Fan: 10 (Auto), " + "Swing (V): 5, Swing (H): 190 (Auto), " + "Clock: 14:50, On Time: Off, Off Time: Off, Sleep Time: Off, " + "Beep: 1 (Quiet), Light: 3 (Off), Mold: On, Clean: On, Fresh Air: Off, " + "Eye: Off, Eye Auto: Off, Quiet: Off, Powerful: Off, Purify: Off, " + "Econo: Off", + ac.toString()); +} + +TEST(TestDaikin2Class, CurrentTime) { + IRDaikin2 ac(0); + ac.begin(); + + ac.setCurrentTime(0); // 00:00 + EXPECT_EQ(0, ac.getCurrentTime()); + + ac.setCurrentTime(754); // 12:34 + EXPECT_EQ(754, ac.getCurrentTime()); + + ac.setCurrentTime(1439); // 23:59 + EXPECT_EQ(1439, ac.getCurrentTime()); +} + +TEST(TestDaikin2Class, OnOffTimers) { + IRDaikin2 ac(0); + ac.begin(); + + // Both timers turned off. + ac.disableOnTimer(); + ac.disableOffTimer(); + EXPECT_FALSE(ac.getOnTimerEnabled()); + EXPECT_EQ(kDaikinUnusedTime, ac.getOnTime()); + EXPECT_FALSE(ac.getOffTimerEnabled()); + EXPECT_EQ(kDaikinUnusedTime, ac.getOffTime()); + + // Turn on just the On Timer. + ac.enableOnTimer(123); + EXPECT_TRUE(ac.getOnTimerEnabled()); + EXPECT_EQ(123, ac.getOnTime()); + EXPECT_FALSE(ac.getOffTimerEnabled()); + EXPECT_EQ(kDaikinUnusedTime, ac.getOffTime()); + + // Now turn on the Off Timer. + ac.enableOffTimer(754); + EXPECT_TRUE(ac.getOffTimerEnabled()); + EXPECT_EQ(754, ac.getOffTime()); + EXPECT_TRUE(ac.getOnTimerEnabled()); + EXPECT_EQ(123, ac.getOnTime()); + + // Turn off the just the On Timer. + ac.disableOnTimer(); + EXPECT_FALSE(ac.getOnTimerEnabled()); + EXPECT_EQ(kDaikinUnusedTime, ac.getOnTime()); + EXPECT_TRUE(ac.getOffTimerEnabled()); + EXPECT_EQ(754, ac.getOffTime()); + + // Now turn off the Off Timer. + ac.disableOffTimer(); + EXPECT_FALSE(ac.getOffTimerEnabled()); + EXPECT_EQ(kDaikinUnusedTime, ac.getOffTime()); + EXPECT_FALSE(ac.getOnTimerEnabled()); + EXPECT_EQ(kDaikinUnusedTime, ac.getOnTime()); +} + +TEST(TestDaikin2Class, LightAndBeep) { + IRDaikin2 ac(0); + ac.begin(); + + ac.setLight(kDaikinLightOff); + EXPECT_EQ(kDaikinLightOff, ac.getLight()); + ac.setBeep(kDaikinBeepOff); + EXPECT_EQ(kDaikinBeepOff, ac.getBeep()); + ac.setLight(kDaikinLightBright); + EXPECT_EQ(kDaikinLightBright, ac.getLight()); + EXPECT_EQ(kDaikinBeepOff, ac.getBeep()); + ac.setBeep(kDaikinBeepLoud); + EXPECT_EQ(kDaikinBeepLoud, ac.getBeep()); + EXPECT_EQ(kDaikinLightBright, ac.getLight()); +} + +TEST(TestDaikin2Class, FanSpeed) { + IRDaikin2 ac(0); + ac.begin(); + + // Unexpected value should default to Auto. + ac.setFan(0); + EXPECT_EQ(kDaikinFanAuto, ac.getFan()); + + // Unexpected value should default to Auto. + ac.setFan(255); + EXPECT_EQ(kDaikinFanAuto, ac.getFan()); + + ac.setFan(kDaikinFanMax); + EXPECT_EQ(kDaikinFanMax, ac.getFan()); + + // Beyond Max should default to Auto. + ac.setFan(kDaikinFanMax + 1); + EXPECT_EQ(kDaikinFanAuto, ac.getFan()); + + ac.setFan(kDaikinFanMax - 1); + EXPECT_EQ(kDaikinFanMax - 1, ac.getFan()); + + ac.setFan(kDaikinFanMin); + EXPECT_EQ(kDaikinFanMin, ac.getFan()); + + ac.setFan(kDaikinFanMin + 1); + EXPECT_EQ(kDaikinFanMin + 1, ac.getFan()); + + // Beyond Min should default to Auto. + ac.setFan(kDaikinFanMin - 1); + EXPECT_EQ(kDaikinFanAuto, ac.getFan()); + + ac.setFan(3); + EXPECT_EQ(3, ac.getFan()); + + ac.setFan(kDaikinFanAuto); + EXPECT_EQ(kDaikinFanAuto, ac.getFan()); + + ac.setFan(kDaikinFanQuiet); + EXPECT_EQ(kDaikinFanQuiet, ac.getFan()); +} + +// Test Mold mode. +TEST(TestDaikin2Class, MoldSetting) { + IRDaikin2 ac(0); + ac.begin(); + + ac.setMold(false); + ASSERT_FALSE(ac.getMold()); + + ac.setMold(true); + ASSERT_TRUE(ac.getMold()); + + ac.setMold(false); + ASSERT_FALSE(ac.getMold()); +} + +// Test Auto Clean setting. +TEST(TestDaikin2Class, CleanSetting) { + IRDaikin2 ac(0); + ac.begin(); + + ac.setClean(false); + ASSERT_FALSE(ac.getClean()); + + ac.setClean(true); + ASSERT_TRUE(ac.getClean()); + + ac.setClean(false); + ASSERT_FALSE(ac.getClean()); +} + + +TEST(TestDaikin2Class, Temperature) { + IRDaikin2 ac(0); + ac.begin(); + + ac.setMode(kDaikinAuto); + ac.setTemp(0); + EXPECT_EQ(kDaikinMinTemp, ac.getTemp()); + + ac.setTemp(255); + EXPECT_EQ(kDaikinMaxTemp, ac.getTemp()); + + ac.setTemp(kDaikinMinTemp); + EXPECT_EQ(kDaikinMinTemp, ac.getTemp()); + + ac.setTemp(kDaikinMaxTemp); + EXPECT_EQ(kDaikinMaxTemp, ac.getTemp()); + + ac.setTemp(kDaikinMinTemp - 1); + EXPECT_EQ(kDaikinMinTemp, ac.getTemp()); + + ac.setTemp(kDaikinMaxTemp + 1); + EXPECT_EQ(kDaikinMaxTemp, ac.getTemp()); + + ac.setTemp(kDaikinMinTemp + 1); + EXPECT_EQ(kDaikinMinTemp + 1, ac.getTemp()); + + // Now try it with Cool mode, which should set the temp to kDaikin2MinCoolTemp + ASSERT_TRUE(kDaikinMinTemp + 1 < kDaikin2MinCoolTemp); + ac.setMode(kDaikinCool); + EXPECT_EQ(kDaikin2MinCoolTemp, ac.getTemp()); + ac.setTemp(kDaikin2MinCoolTemp - 1); + EXPECT_EQ(kDaikin2MinCoolTemp, ac.getTemp()); + ac.setTemp(kDaikin2MinCoolTemp + 1); + EXPECT_EQ(kDaikin2MinCoolTemp + 1, ac.getTemp()); + // Should be released from that requirement in other modes. + ac.setMode(kDaikinAuto); + ac.setTemp(kDaikin2MinCoolTemp - 1); + EXPECT_EQ(kDaikin2MinCoolTemp - 1, ac.getTemp()); + + ac.setTemp(21); + EXPECT_EQ(21, ac.getTemp()); + + ac.setTemp(25); + EXPECT_EQ(25, ac.getTemp()); + + ac.setTemp(29); + EXPECT_EQ(29, ac.getTemp()); +} + +// Test Fresh Air settings. +TEST(TestDaikin2Class, FreshAirSettings) { + IRDaikin2 ac(0); + ac.begin(); + + ac.setFreshAir(false); + ac.setFreshAirHigh(false); + ASSERT_FALSE(ac.getFreshAir()); + ASSERT_FALSE(ac.getFreshAirHigh()); + + ac.setFreshAir(true); + ASSERT_TRUE(ac.getFreshAir()); + ASSERT_FALSE(ac.getFreshAirHigh()); + + ac.setFreshAirHigh(true); + ASSERT_TRUE(ac.getFreshAir()); + ASSERT_TRUE(ac.getFreshAirHigh()); + + ac.setFreshAir(false); + ASSERT_FALSE(ac.getFreshAir()); + ASSERT_TRUE(ac.getFreshAirHigh()); + + ac.setFreshAirHigh(false); + ASSERT_FALSE(ac.getFreshAir()); + ASSERT_FALSE(ac.getFreshAirHigh()); +} + +// Test Eye mode. +TEST(TestDaikin2Class, EyeSetting) { + IRDaikin2 ac(0); + ac.begin(); + + ac.setEye(false); + ASSERT_FALSE(ac.getEye()); + ac.setEye(true); + ASSERT_TRUE(ac.getEye()); + ac.setEye(false); + ASSERT_FALSE(ac.getEye()); +} + +// Test Econo setting. +TEST(TestDaikin2Class, EconoSetting) { + IRDaikin2 ac(0); + ac.begin(); + + ac.setEcono(false); + ASSERT_FALSE(ac.getEcono()); + ac.setEcono(true); + ASSERT_TRUE(ac.getEcono()); + ac.setEcono(false); + ASSERT_FALSE(ac.getEcono()); +} + +TEST(TestDaikin2Class, SleepTimer) { + IRDaikin2 ac(0); + ac.begin(); + + // NOTE: On & Sleep timer share the same time location. + + // Both timers turned off. + ac.disableOnTimer(); + ac.disableSleepTimer(); + EXPECT_FALSE(ac.getOnTimerEnabled()); + EXPECT_EQ(kDaikinUnusedTime, ac.getOnTime()); + EXPECT_FALSE(ac.getSleepTimerEnabled()); + EXPECT_EQ(kDaikinUnusedTime, ac.getSleepTime()); + + // Turn on just the On Timer. + ac.enableOnTimer(123); + EXPECT_TRUE(ac.getOnTimerEnabled()); + EXPECT_EQ(123, ac.getOnTime()); + EXPECT_FALSE(ac.getSleepTimerEnabled()); + EXPECT_EQ(123, ac.getSleepTime()); + + // Now turn on the Sleep Timer. This shoud disable the On Timer. + ac.enableSleepTimer(754); + EXPECT_TRUE(ac.getSleepTimerEnabled()); + EXPECT_EQ(754, ac.getSleepTime()); + EXPECT_FALSE(ac.getOnTimerEnabled()); + EXPECT_EQ(754, ac.getOnTime()); + + // Turn off the just the On Timer. + ac.disableOnTimer(); + EXPECT_FALSE(ac.getOnTimerEnabled()); + EXPECT_EQ(kDaikinUnusedTime, ac.getOnTime()); + EXPECT_FALSE(ac.getSleepTimerEnabled()); + EXPECT_EQ(kDaikinUnusedTime, ac.getSleepTime()); + + // Now turn on the On Timer and turn off the Sleep Timer. + // Both should be off afterwards. + ac.enableOnTimer(123); + ac.disableSleepTimer(); + EXPECT_FALSE(ac.getSleepTimerEnabled()); + EXPECT_EQ(kDaikinUnusedTime, ac.getSleepTime()); + EXPECT_FALSE(ac.getOnTimerEnabled()); + EXPECT_EQ(kDaikinUnusedTime, ac.getOnTime()); +} + +// Test Vertical Swing. +TEST(TestDaikin2Class, Swing) { + IRDaikin2 ac(0); + ac.begin(); + + // Vertical + ac.setSwingVertical(1); + ASSERT_EQ(1, ac.getSwingVertical()); + ac.setSwingVertical(3); + ASSERT_EQ(3, ac.getSwingVertical()); + ac.setSwingVertical(6); + ASSERT_EQ(6, ac.getSwingVertical()); + ac.setSwingVertical(kDaikin2SwingVBreeze); + ASSERT_EQ(kDaikin2SwingVBreeze, ac.getSwingVertical()); + ac.setSwingVertical(kDaikin2SwingVCirculate); + ASSERT_EQ(kDaikin2SwingVCirculate, ac.getSwingVertical()); + ac.setSwingVertical(kDaikin2SwingVAuto); + ASSERT_EQ(kDaikin2SwingVAuto, ac.getSwingVertical()); + ac.setSwingVertical(0); + ASSERT_EQ(kDaikin2SwingVAuto, ac.getSwingVertical()); + ac.setSwingVertical(7); + ASSERT_EQ(kDaikin2SwingVAuto, ac.getSwingVertical()); + ac.setSwingVertical(255); + ASSERT_EQ(kDaikin2SwingVAuto, ac.getSwingVertical()); + + // Horizontal + ac.setSwingHorizontal(kDaikin2SwingHAuto); + ASSERT_EQ(kDaikin2SwingHAuto, ac.getSwingHorizontal()); + ac.setSwingHorizontal(kDaikin2SwingHSwing); + ASSERT_EQ(kDaikin2SwingHSwing, ac.getSwingHorizontal()); + + ac.setSwingHorizontal(0); + ASSERT_EQ(0, ac.getSwingHorizontal()); + ac.setSwingHorizontal(255); + ASSERT_EQ(255, ac.getSwingHorizontal()); +} + +TEST(TestDaikin2Class, QuietMode) { + IRDaikin2 ac(0); + ac.begin(); + + ac.setQuiet(true); + EXPECT_TRUE(ac.getQuiet()); + + ac.setQuiet(false); + EXPECT_FALSE(ac.getQuiet()); + + ac.setQuiet(true); + EXPECT_TRUE(ac.getQuiet()); + + // But setting Powerful mode should exit out of quiet mode. + ac.setPowerful(true); + EXPECT_FALSE(ac.getQuiet()); +} + +TEST(TestDaikin2Class, PowerfulMode) { + IRDaikin2 ac(0); + ac.begin(); + + ac.setPowerful(true); + EXPECT_TRUE(ac.getPowerful()); + + ac.setPowerful(false); + EXPECT_FALSE(ac.getPowerful()); + + ac.setPowerful(true); + EXPECT_TRUE(ac.getPowerful()); + + ac.setQuiet(true); + EXPECT_FALSE(ac.getPowerful()); +} + +// Test Purify mode. +TEST(TestDaikin2Class, PurifySetting) { + IRDaikin2 ac(0); + ac.begin(); + + ac.setPurify(false); + ASSERT_FALSE(ac.getPurify()); + ac.setPurify(true); + ASSERT_TRUE(ac.getPurify()); + ac.setPurify(false); + ASSERT_FALSE(ac.getPurify()); +} + +TEST(TestDaikin2Class, HumanReadable) { + IRDaikin2 ac(0); + ac.begin(); + ac.setPower(true); + ac.setMode(kDaikinCool); + ac.setTemp(21); + ac.setFan(kDaikinFanMax); + ac.setSwingVertical(kDaikin2SwingVAuto); + ac.setSwingHorizontal(kDaikin2SwingHSwing); + ac.setCurrentTime(12 * 60 + 34); // 12:34 + ac.disableOnTimer(); + ac.enableOffTimer(20 * 60); // 20:00 + ac.enableSleepTimer(4 * 60); // 4:00 + ac.setBeep(kDaikinBeepLoud); + ac.setLight(kDaikinLightDim); + ac.setMold(true); + ac.setClean(false); + ac.setFreshAir(true); + ac.setEye(true); + ac.setEyeAuto(true); + ac.setQuiet(false); + ac.setPowerful(true); + ac.setPurify(true); + ac.setEcono(false); + EXPECT_EQ( + "Power: On, Mode: 3 (COOL), Temp: 21C, Fan: 5 (Max), " + "Swing (V): 14 (Auto), Swing (H): 191 (Swing), Clock: 12:34, " + "On Time: Off, Off Time: 20:00, Sleep Time: 4:00, Beep: 2 (Loud), " + "Light: 2 (Dim), Mold: On, Clean: Off, Fresh Air: On, Eye: On, " + "Eye Auto: On, Quiet: Off, Powerful: On, Purify: On, Econo: Off", + ac.toString()); + ac.setQuiet(true); + ac.setMode(kDaikinHeat); + ac.setBeep(kDaikinBeepQuiet); + ac.setLight(kDaikinLightBright); + ac.setTemp(32); + ac.setFan(kDaikinFanMin); + ac.setCurrentTime(23 * 60 + 45); // 23:45 + ac.enableOnTimer(9 * 60 + 11); // 9:11 + EXPECT_EQ( + "Power: On, Mode: 4 (HEAT), Temp: 32C, Fan: 1 (Min), " + "Swing (V): 14 (Auto), Swing (H): 191 (Swing), Clock: 23:45, " + "On Time: 9:11, Off Time: 20:00, Sleep Time: Off, Beep: 1 (Quiet), " + "Light: 1 (Bright), Mold: On, Clean: Off, Fresh Air: On, Eye: On, " + "Eye Auto: On, Quiet: On, Powerful: Off, Purify: On, Econo: Off", + ac.toString()); +} + +// See if we can construct a known state. +TEST(TestDaikin2Class, KnownConstruction) { + IRDaikin2 ac(0); + + uint8_t expectedState[kDaikin2StateLength] = { + 0x11, 0xDA, 0x27, 0x00, 0x01, 0x7A, 0xC3, 0x70, 0x28, 0x0C, + 0x80, 0x04, 0xB0, 0x16, 0x24, 0x00, 0x00, 0xBE, 0xD5, 0xF5, + 0x11, 0xDA, 0x27, 0x00, 0x00, 0x08, 0x26, 0x00, 0xA0, 0x00, + 0x00, 0x06, 0x60, 0x00, 0x00, 0xC1, 0x80, 0x60, 0xE7}; + + ac.begin(); + ac.setPower(false); + ac.setMode(kDaikinAuto); + ac.setTemp(19); + ac.setFan(kDaikinFanAuto); + ac.setSwingVertical(5); + ac.setSwingHorizontal(kDaikin2SwingHAuto); + ac.setCurrentTime(14 * 60 + 50); // 14:50 + ac.disableOnTimer(); + ac.disableOffTimer(); + ac.disableSleepTimer(); + ac.setBeep(kDaikinBeepQuiet); + ac.setLight(kDaikinLightOff); + ac.setMold(true); + ac.setClean(true); + ac.setFreshAir(false); + ac.setEye(false); + ac.setEyeAuto(false); + ac.setQuiet(false); + ac.setPowerful(false); + ac.setPurify(false); + ac.setEcono(false); + EXPECT_EQ( + "Power: Off, Mode: 0 (AUTO), Temp: 19C, Fan: 10 (Auto), " + "Swing (V): 5, Swing (H): 190 (Auto), " + "Clock: 14:50, On Time: Off, Off Time: Off, Sleep Time: Off, " + "Beep: 1 (Quiet), Light: 3 (Off), Mold: On, Clean: On, Fresh Air: Off, " + "Eye: Off, Eye Auto: Off, Quiet: Off, Powerful: Off, Purify: Off, " + "Econo: Off", + ac.toString()); + EXPECT_STATE_EQ(expectedState, ac.getRaw(), kDaikin2Bits); +} + +TEST(TestUtils, Housekeeping) { + ASSERT_EQ("DAIKIN", typeToString(decode_type_t::DAIKIN)); + ASSERT_EQ(decode_type_t::DAIKIN, strToDecodeType("DAIKIN")); + ASSERT_TRUE(hasACState(decode_type_t::DAIKIN)); + + ASSERT_EQ("DAIKIN2", typeToString(decode_type_t::DAIKIN2)); + ASSERT_EQ(decode_type_t::DAIKIN2, strToDecodeType("DAIKIN2")); + ASSERT_TRUE(hasACState(decode_type_t::DAIKIN2)); + + ASSERT_EQ("DAIKIN216", typeToString(decode_type_t::DAIKIN216)); + ASSERT_EQ(decode_type_t::DAIKIN216, strToDecodeType("DAIKIN216")); + ASSERT_TRUE(hasACState(decode_type_t::DAIKIN216)); +} + +// https://github.com/markszabo/IRremoteESP8266/issues/582#issuecomment-453863879 +TEST(TestDecodeDaikin2, Issue582DeepDecodeExample) { + IRDaikin2 ac(0); + + const uint8_t state[kDaikin2StateLength] = { + 0x11, 0xDA, 0x27, 0x00, 0x01, 0x30, 0x42, 0xF0, 0x28, 0x0C, + 0x80, 0x04, 0xB0, 0x16, 0x24, 0x00, 0x00, 0xBE, 0xCE, 0xA3, + 0x11, 0xDA, 0x27, 0x00, 0x00, 0x09, 0x26, 0x00, 0xA0, 0x00, + 0x00, 0x06, 0x60, 0x00, 0x00, 0xC1, 0x92, 0x60, 0xFA}; + + ac.setRaw(state); + ASSERT_TRUE(ac.getMold()); + ASSERT_TRUE(ac.getEye()); + ASSERT_TRUE(ac.getPurify()); + EXPECT_EQ( + "Power: On, Mode: 0 (AUTO), Temp: 19C, Fan: 10 (Auto), " + "Swing (V): 14 (Auto), Swing (H): 190 (Auto), Clock: 9:20, On Time: Off, " + "Off Time: Off, Sleep Time: Off, Beep: 3 (Off), Light: 3 (Off), " + "Mold: On, Clean: On, Fresh Air: Off, Eye: On, Eye Auto: Off, " + "Quiet: Off, Powerful: Off, Purify: On, Econo: Off", + ac.toString()); +} + +// https://docs.google.com/spreadsheets/d/1f8EGfIbBUo2B-CzUFdrgKQprWakoYNKM80IKZN4KXQE/edit?ts=5c317775#gid=1023395743 +TEST(TestDecodeDaikin2, Issue582PowerfulEconoFix) { + IRDaikin2 ac(0); + + const uint8_t PowerfulOn[39] = { + 0x11, 0xDA, 0x27, 0x00, 0x01, 0x3A, 0x43, 0xF0, 0x28, 0x0C, + 0x80, 0x04, 0xB0, 0x16, 0x24, 0x00, 0x00, 0xBE, 0xCE, 0xAE, + 0x11, 0xDA, 0x27, 0x00, 0x00, 0x39, 0x28, 0x00, 0xA0, 0x00, + 0x00, 0x06, 0x60, 0x01, 0x00, 0xC1, 0x90, 0x60, 0x2B}; + const uint8_t PowerfulOff[39] = { + 0x11, 0xDA, 0x27, 0x00, 0x01, 0x3A, 0x43, 0xF0, 0x28, 0x0C, + 0x80, 0x04, 0xB0, 0x16, 0x24, 0x00, 0x00, 0xBE, 0xCE, 0xAE, + 0x11, 0xDA, 0x27, 0x00, 0x00, 0x39, 0x28, 0x00, 0xA0, 0x00, + 0x00, 0x06, 0x60, 0x00, 0x00, 0xC1, 0x90, 0x60, 0x2A}; + ac.setRaw(PowerfulOn); + ASSERT_TRUE(ac.getPowerful()); + EXPECT_EQ( + "Power: On, Mode: 3 (COOL), Temp: 20C, Fan: 10 (Auto), " + "Swing (V): 14 (Auto), Swing (H): 190 (Auto), Clock: 13:46, " + "On Time: Off, Off Time: Off, Sleep Time: Off, Beep: 3 (Off), " + "Light: 3 (Off), Mold: On, Clean: On, Fresh Air: Off, Eye: Off, " + "Eye Auto: Off, Quiet: Off, Powerful: On, Purify: On, Econo: Off", + ac.toString()); + ac.setRaw(PowerfulOff); + ASSERT_FALSE(ac.getPowerful()); + EXPECT_EQ( + "Power: On, Mode: 3 (COOL), Temp: 20C, Fan: 10 (Auto), " + "Swing (V): 14 (Auto), Swing (H): 190 (Auto), Clock: 13:46, " + "On Time: Off, Off Time: Off, Sleep Time: Off, Beep: 3 (Off), " + "Light: 3 (Off), Mold: On, Clean: On, Fresh Air: Off, Eye: Off, " + "Eye Auto: Off, Quiet: Off, Powerful: Off, Purify: On, Econo: Off", + ac.toString()); + + const uint8_t EconoOn[39] = { + 0x11, 0xDA, 0x27, 0x00, 0x01, 0x3B, 0x43, 0xF0, 0x28, 0x0C, + 0x80, 0x04, 0xB0, 0x16, 0x24, 0x00, 0x00, 0xBE, 0xCE, 0xAF, + 0x11, 0xDA, 0x27, 0x00, 0x00, 0x39, 0x28, 0x00, 0xA0, 0x00, + 0x00, 0x06, 0x60, 0x00, 0x00, 0xC1, 0x94, 0x60, 0x2E}; + const uint8_t EconoOff[39] = { + 0x11, 0xDA, 0x27, 0x00, 0x01, 0x3B, 0x43, 0xF0, 0x28, 0x0C, + 0x80, 0x04, 0xB0, 0x16, 0x24, 0x00, 0x00, 0xBE, 0xCE, 0xAF, + 0x11, 0xDA, 0x27, 0x00, 0x00, 0x39, 0x28, 0x00, 0xA0, 0x00, + 0x00, 0x06, 0x60, 0x00, 0x00, 0xC1, 0x90, 0x60, 0x2A}; + ac.setRaw(EconoOn); + ASSERT_TRUE(ac.getEcono()); + EXPECT_EQ( + "Power: On, Mode: 3 (COOL), Temp: 20C, Fan: 10 (Auto), " + "Swing (V): 14 (Auto), Swing (H): 190 (Auto), Clock: 13:47, " + "On Time: Off, Off Time: Off, Sleep Time: Off, Beep: 3 (Off), " + "Light: 3 (Off), Mold: On, Clean: On, Fresh Air: Off, Eye: Off, " + "Eye Auto: Off, Quiet: Off, Powerful: Off, Purify: On, Econo: On", + ac.toString()); + ac.setRaw(EconoOff); + ASSERT_FALSE(ac.getEcono()); + EXPECT_EQ( + "Power: On, Mode: 3 (COOL), Temp: 20C, Fan: 10 (Auto), " + "Swing (V): 14 (Auto), Swing (H): 190 (Auto), Clock: 13:47, " + "On Time: Off, Off Time: Off, Sleep Time: Off, Beep: 3 (Off), " + "Light: 3 (Off), Mold: On, Clean: On, Fresh Air: Off, Eye: Off, " + "Eye Auto: Off, Quiet: Off, Powerful: Off, Purify: On, Econo: Off", + ac.toString()); +} + +// Tests for IRDaikin216 class. + +TEST(TestDaikin216Class, Power) { + IRDaikin216 ac(0); + ac.begin(); + + ac.on(); + EXPECT_TRUE(ac.getPower()); + + ac.off(); + EXPECT_FALSE(ac.getPower()); + + ac.setPower(true); + EXPECT_TRUE(ac.getPower()); + + ac.setPower(false); + EXPECT_FALSE(ac.getPower()); +} + +TEST(TestDaikin216Class, Temperature) { + IRDaikin216 ac(0); + ac.begin(); + + ac.setTemp(0); + EXPECT_EQ(kDaikinMinTemp, ac.getTemp()); + + ac.setTemp(255); + EXPECT_EQ(kDaikinMaxTemp, ac.getTemp()); + + ac.setTemp(kDaikinMinTemp); + EXPECT_EQ(kDaikinMinTemp, ac.getTemp()); + + ac.setTemp(kDaikinMaxTemp); + EXPECT_EQ(kDaikinMaxTemp, ac.getTemp()); + + ac.setTemp(kDaikinMinTemp - 1); + EXPECT_EQ(kDaikinMinTemp, ac.getTemp()); + + ac.setTemp(kDaikinMaxTemp + 1); + EXPECT_EQ(kDaikinMaxTemp, ac.getTemp()); + + ac.setTemp(kDaikinMinTemp + 1); + EXPECT_EQ(kDaikinMinTemp + 1, ac.getTemp()); + + ac.setTemp(21); + EXPECT_EQ(21, ac.getTemp()); + + ac.setTemp(25); + EXPECT_EQ(25, ac.getTemp()); + + ac.setTemp(29); + EXPECT_EQ(29, ac.getTemp()); +} + +TEST(TestDaikin216Class, OperatingMode) { + IRDaikin216 ac(0); + ac.begin(); + + ac.setMode(kDaikinAuto); + EXPECT_EQ(kDaikinAuto, ac.getMode()); + + ac.setMode(kDaikinCool); + EXPECT_EQ(kDaikinCool, ac.getMode()); + + ac.setMode(kDaikinHeat); + EXPECT_EQ(kDaikinHeat, ac.getMode()); + + ac.setMode(kDaikinDry); + EXPECT_EQ(kDaikinDry, ac.getMode()); + + ac.setMode(kDaikinFan); + EXPECT_EQ(kDaikinFan, ac.getMode()); + + ac.setMode(kDaikinFan + 1); + EXPECT_EQ(kDaikinAuto, ac.getMode()); + + ac.setMode(kDaikinAuto + 1); + EXPECT_EQ(kDaikinAuto, ac.getMode()); + + ac.setMode(255); + EXPECT_EQ(kDaikinAuto, ac.getMode()); +} + + +TEST(TestDaikin216Class, VaneSwing) { + IRDaikin216 ac(0); + ac.begin(); + + ac.setSwingHorizontal(true); + ac.setSwingVertical(false); + + ac.setSwingHorizontal(true); + EXPECT_TRUE(ac.getSwingHorizontal()); + EXPECT_FALSE(ac.getSwingVertical()); + + ac.setSwingVertical(true); + EXPECT_TRUE(ac.getSwingHorizontal()); + EXPECT_TRUE(ac.getSwingVertical()); + + ac.setSwingHorizontal(false); + EXPECT_FALSE(ac.getSwingHorizontal()); + EXPECT_TRUE(ac.getSwingVertical()); + + ac.setSwingVertical(false); + EXPECT_FALSE(ac.getSwingHorizontal()); + EXPECT_FALSE(ac.getSwingVertical()); +} + +TEST(TestDaikin216Class, FanSpeed) { + IRDaikin216 ac(0); + ac.begin(); + + // Unexpected value should default to Auto. + ac.setFan(0); + EXPECT_EQ(kDaikinFanAuto, ac.getFan()); + + // Unexpected value should default to Auto. + ac.setFan(255); + EXPECT_EQ(kDaikinFanAuto, ac.getFan()); + + ac.setFan(kDaikinFanMax); + EXPECT_EQ(kDaikinFanMax, ac.getFan()); + + // Beyond Max should default to Auto. + ac.setFan(kDaikinFanMax + 1); + EXPECT_EQ(kDaikinFanAuto, ac.getFan()); + + ac.setFan(kDaikinFanMax - 1); + EXPECT_EQ(kDaikinFanMax - 1, ac.getFan()); + + ac.setFan(kDaikinFanMin); + EXPECT_EQ(kDaikinFanMin, ac.getFan()); + + ac.setFan(kDaikinFanMin + 1); + EXPECT_EQ(kDaikinFanMin + 1, ac.getFan()); + + // Beyond Min should default to Auto. + ac.setFan(kDaikinFanMin - 1); + EXPECT_EQ(kDaikinFanAuto, ac.getFan()); + + ac.setFan(3); + EXPECT_EQ(3, ac.getFan()); + + ac.setFan(kDaikinFanAuto); + EXPECT_EQ(kDaikinFanAuto, ac.getFan()); + + ac.setFan(kDaikinFanQuiet); + EXPECT_EQ(kDaikinFanQuiet, ac.getFan()); +} + +TEST(TestDaikin216Class, Quiet) { + IRDaikin216 ac(0); + ac.begin(); + + ac.setQuiet(true); + EXPECT_TRUE(ac.getQuiet()); + + ac.setQuiet(false); + EXPECT_FALSE(ac.getQuiet()); + + ac.setQuiet(true); + EXPECT_TRUE(ac.getQuiet()); +} + +TEST(TestDaikin216Class, ExampleStates) { + IRDaikin216 ac(0); + ac.begin(); + // https://github.com/markszabo/IRremoteESP8266/pull/690#issuecomment-487770194 + uint8_t state[kDaikin216StateLength] = { + 0x11, 0xDA, 0x27, 0xF0, 0x00, 0x00, 0x00, 0x02, + 0x11, 0xDA, 0x27, 0x00, 0x00, 0x21, 0xC0, 0x00, 0xA0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x53}; + ac.setRaw(state); + EXPECT_EQ( + "Power: On, Mode: 2 (DRY), Temp: 32C, Fan: 10 (AUTO), " + "Swing (Horizontal): Off, Swing (Vertical): Off, Quiet: Off", + ac.toString()); +} + +TEST(TestDaikin216Class, ReconstructKnownState) { + IRDaikin216 ac(0); + ac.begin(); + // https://github.com/markszabo/IRremoteESP8266/issues/689#issue-438086949 + uint8_t expectedState[kDaikin216StateLength] = { + 0x11, 0xDA, 0x27, 0xF0, 0x00, 0x00, 0x00, 0x02, + 0x11, 0xDA, 0x27, 0x00, 0x00, 0x00, 0x26, 0x00, 0xA0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x98}; + ac.setPower(false); + ac.setMode(kDaikinAuto); + ac.setTemp(19); + ac.setFan(kDaikinFanAuto); + ac.setSwingHorizontal(false); + ac.setSwingVertical(false); + ac.setQuiet(false); + EXPECT_EQ( + "Power: Off, Mode: 0 (AUTO), Temp: 19C, Fan: 10 (AUTO), " + "Swing (Horizontal): Off, Swing (Vertical): Off, Quiet: Off", + ac.toString()); + + EXPECT_STATE_EQ(expectedState, ac.getRaw(), kDaikin216Bits); +} + +// https://github.com/markszabo/IRremoteESP8266/issues/689 +TEST(TestDecodeDaikin216, RealExample) { + IRsendTest irsend(0); + IRrecv irrecv(0); + // https://github.com/markszabo/IRremoteESP8266/issues/689#issue-438086949 + uint16_t rawData[439] = { + 3402, 1770, 382, 1340, 382, 480, 382, 478, 382, 480, 380, 1342, 382, 478, + 356, 504, 382, 480, 380, 478, 384, 1342, 380, 480, 380, 1342, 382, 1342, + 382, 478, 382, 1340, 382, 1340, 384, 1340, 382, 1342, 382, 1340, 380, 480, + 382, 480, 382, 1296, 426, 480, 380, 480, 382, 480, 380, 480, 382, 480, + 382, 478, 382, 1342, 382, 1342, 382, 1340, 356, 1368, 382, 478, 382, 480, + 382, 478, 380, 480, 382, 480, 382, 480, 382, 478, 382, 480, 382, 478, 358, + 504, 382, 480, 380, 480, 382, 480, 382, 480, 380, 480, 382, 478, 382, 480, + 382, 478, 382, 480, 354, 506, 354, 506, 380, 480, 382, 480, 382, 480, 382, + 480, 380, 1342, 382, 480, 382, 480, 382, 478, 382, 478, 382, 478, 384, + 478, 382, 29652, 3426, 1772, 382, 1340, 382, 480, 380, 478, 382, 480, 382, + 1342, 382, 480, 382, 480, 382, 478, 356, 506, 382, 1342, 380, 480, 382, + 1340, 382, 1340, 382, 478, 356, 1366, 382, 1340, 384, 1340, 382, 1340, + 382, 1342, 382, 478, 382, 478, 382, 1340, 382, 478, 382, 478, 382, 478, + 382, 480, 382, 480, 384, 478, 358, 504, 382, 478, 382, 480, 382, 478, 382, + 480, 382, 480, 382, 478, 382, 480, 382, 478, 382, 478, 382, 478, 382, 478, + 384, 478, 382, 478, 360, 500, 358, 504, 382, 478, 382, 480, 382, 480, 382, + 478, 382, 478, 382, 1340, 382, 1342, 382, 480, 380, 480, 382, 1342, 382, + 478, 382, 480, 356, 506, 382, 478, 382, 480, 382, 480, 356, 506, 382, 478, + 382, 480, 382, 478, 382, 480, 382, 478, 382, 480, 380, 480, 380, 480, 382, + 1342, 382, 478, 382, 1342, 382, 480, 382, 480, 382, 478, 382, 478, 382, + 480, 382, 478, 382, 480, 356, 504, 384, 478, 382, 480, 382, 480, 380, 480, + 382, 478, 382, 480, 382, 480, 382, 478, 356, 504, 384, 478, 380, 480, 382, + 480, 382, 480, 382, 478, 356, 506, 382, 478, 382, 480, 380, 480, 382, 478, + 382, 480, 382, 478, 382, 480, 358, 504, 382, 478, 382, 478, 356, 504, 382, + 478, 382, 480, 382, 478, 382, 478, 382, 478, 382, 480, 380, 480, 382, 480, + 380, 480, 356, 506, 356, 504, 382, 480, 382, 478, 382, 478, 382, 478, 382, + 478, 382, 480, 382, 478, 382, 480, 382, 480, 382, 1340, 382, 1342, 382, + 478, 384, 478, 382, 478, 382, 480, 380, 480, 382, 478, 382, 480, 356, 506, + 382, 478, 382, 480, 382, 478, 356, 506, 380, 480, 382, 478, 382, 478, 382, + 478, 382, 480, 382, 480, 380, 480, 382, 1342, 382, 1340, 382, 480, 356, + 504, 382, 1342, 382}; // UNKNOWN E0E32232 + uint8_t expectedState[kDaikin216StateLength] = { + // 8 bytes + 0x11, 0xDA, 0x27, 0xF0, 0x00, 0x00, 0x00, 0x02, + // 19 bytes + 0x11, 0xDA, 0x27, 0x00, 0x00, 0x00, 0x26, 0x00, 0xA0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x98}; + + irsend.begin(); + irsend.reset(); + irsend.sendRaw(rawData, 439, 38000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(DAIKIN216, irsend.capture.decode_type); + ASSERT_EQ(kDaikin216Bits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + + IRDaikin216 ac(0); + ac.setRaw(irsend.capture.state); + EXPECT_EQ( + "Power: Off, Mode: 0 (AUTO), Temp: 19C, Fan: 10 (AUTO), " + "Swing (Horizontal): Off, Swing (Vertical): Off, Quiet: Off", + ac.toString()); +} + +// https://github.com/markszabo/IRremoteESP8266/issues/689 +TEST(TestDecodeDaikin216, SyntheticExample) { + IRsendTest irsend(0); + IRrecv irrecv(0); + // https://github.com/markszabo/IRremoteESP8266/issues/689#issue-438086949 + uint8_t expectedState[kDaikin216StateLength] = { + // 8 bytes + 0x11, 0xDA, 0x27, 0xF0, 0x00, 0x00, 0x00, 0x02, + // 19 bytes + 0x11, 0xDA, 0x27, 0x00, 0x00, 0x00, 0x26, 0x00, 0xA0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x98}; + + irsend.begin(); + irsend.reset(); + irsend.sendDaikin216(expectedState); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(DAIKIN216, irsend.capture.decode_type); + ASSERT_EQ(kDaikin216Bits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); +} diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_Denon_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Denon_test.cpp similarity index 98% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_Denon_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_Denon_test.cpp index 911fd7528..6b80eae02 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_Denon_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Denon_test.cpp @@ -14,6 +14,7 @@ TEST(TestSendDenon, SendDataOnly) { irsend.reset(); irsend.sendDenon(0x2278); // Denon AVR Power On. (Sharp) EXPECT_EQ( + "f38000d33" "m260s780m260s1820m260s780m260s780m260s780m260s1820m260s780m260s780" "m260s1820m260s1820m260s1820m260s1820m260s780m260s780m260s780" "m260s43602" @@ -26,6 +27,7 @@ TEST(TestSendDenon, SendDataOnly) { // Denon Eco Mode On. (Panasonic/Kaseikyo) irsend.sendDenon(0x2A4C028D6CE3, DENON_48_BITS); EXPECT_EQ( + "f36700d50" "m3456s1728" "m432s432m432s432m432s1296m432s432m432s1296m432s432m432s1296m432s432" "m432s432m432s1296m432s432m432s432m432s1296m432s1296m432s432m432s432" @@ -45,6 +47,7 @@ TEST(TestSendDenon, SendNormalWithRepeats) { irsend.reset(); irsend.sendDenon(0x2278, DENON_BITS, 1); // 1 repeat. EXPECT_EQ( + "f38000d33" "m260s780m260s1820m260s780m260s780m260s780m260s1820m260s780m260s780" "m260s1820m260s1820m260s1820m260s1820m260s780m260s780m260s780" "m260s43602" @@ -60,6 +63,7 @@ TEST(TestSendDenon, SendNormalWithRepeats) { irsend.outputStr()); irsend.sendDenon(0x2278, DENON_BITS, 2); // 2 repeats. EXPECT_EQ( + "f38000d33" "m260s780m260s1820m260s780m260s780m260s780m260s1820m260s780m260s780" "m260s1820m260s1820m260s1820m260s1820m260s780m260s780m260s780" "m260s43602" @@ -88,6 +92,7 @@ TEST(TestSendDenon, Send48BitWithRepeats) { irsend.reset(); irsend.sendDenon(0x2A4C028D6CE3, DENON_48_BITS, 1); // 1 repeat. EXPECT_EQ( + "f36700d50" "m3456s1728" "m432s432m432s432m432s1296m432s432m432s1296m432s432m432s1296m432s432" "m432s432m432s1296m432s432m432s432m432s1296m432s1296m432s432m432s432" @@ -107,6 +112,7 @@ TEST(TestSendDenon, Send48BitWithRepeats) { irsend.outputStr()); irsend.sendDenon(0x2A4C028D6CE3, DENON_48_BITS, 2); // 2 repeats. EXPECT_EQ( + "f36700d50" "m3456s1728" "m432s432m432s432m432s1296m432s432m432s1296m432s432m432s1296m432s432" "m432s432m432s1296m432s432m432s432m432s1296m432s1296m432s432m432s432" @@ -142,6 +148,7 @@ TEST(TestSendDenon, SendUnusualSize) { irsend.reset(); irsend.sendDenon(0x12, 8); EXPECT_EQ( + "f38000d33" "m260s780m260s780m260s780m260s1820m260s780m260s780m260s1820m260s780" "m260s43602" "m260s1820m260s1820m260s1820m260s780m260s1820m260s1820m260s780m260s1820" @@ -151,6 +158,7 @@ TEST(TestSendDenon, SendUnusualSize) { irsend.reset(); irsend.sendDenon(0x1234567890ABCDEF, 64); EXPECT_EQ( + "f36700d50" "m3456s1728" "m432s432m432s432m432s432m432s1296m432s432m432s432m432s1296m432s432" "m432s432m432s432m432s1296m432s1296m432s432m432s1296m432s432m432s432" diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_Dish_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Dish_test.cpp similarity index 98% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_Dish_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_Dish_test.cpp index 0c58496ce..21286b7ac 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_Dish_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Dish_test.cpp @@ -14,6 +14,7 @@ TEST(TestSendDish, SendDataOnly) { irsend.reset(); irsend.sendDISH(0x0); EXPECT_EQ( + "f57600d50" "m400s6100" "m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800" "m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800" @@ -32,6 +33,7 @@ TEST(TestSendDish, SendDataOnly) { irsend.reset(); irsend.sendDISH(0x9C00); // Power on. EXPECT_EQ( + "f57600d50" "m400s6100" "m400s1700m400s2800m400s2800m400s1700m400s1700m400s1700m400s2800m400s2800" "m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800" @@ -50,6 +52,7 @@ TEST(TestSendDish, SendDataOnly) { irsend.reset(); irsend.sendDISH(0xFFFF); EXPECT_EQ( + "f57600d50" "m400s6100" "m400s1700m400s1700m400s1700m400s1700m400s1700m400s1700m400s1700m400s1700" "m400s1700m400s1700m400s1700m400s1700m400s1700m400s1700m400s1700m400s1700" @@ -74,6 +77,7 @@ TEST(TestSendDish, SendWithRepeats) { irsend.reset(); irsend.sendDISH(0x9C00, kDishBits, 0); // 0 repeats. EXPECT_EQ( + "f57600d50" "m400s6100" "m400s1700m400s2800m400s2800m400s1700m400s1700m400s1700m400s2800m400s2800" "m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800" @@ -83,6 +87,7 @@ TEST(TestSendDish, SendWithRepeats) { irsend.reset(); irsend.sendDISH(0x9C00, kDishBits, 1); // 1 repeat. EXPECT_EQ( + "f57600d50" "m400s6100" "m400s1700m400s2800m400s2800m400s1700m400s1700m400s1700m400s2800m400s2800" "m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800" @@ -94,6 +99,7 @@ TEST(TestSendDish, SendWithRepeats) { irsend.sendDISH(0x9C00, kDishBits, 2); // 2 repeats. EXPECT_EQ( + "f57600d50" "m400s6100" "m400s1700m400s2800m400s2800m400s1700m400s1700m400s1700m400s2800m400s2800" "m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800" @@ -115,6 +121,7 @@ TEST(TestSendDish, SendUnusualSize) { irsend.reset(); irsend.sendDISH(0x0, 8); EXPECT_EQ( + "f57600d50" "m400s6100" "m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800" "m400s6100" @@ -129,6 +136,7 @@ TEST(TestSendDish, SendUnusualSize) { irsend.reset(); irsend.sendDISH(0x1234567890ABCDEF, 64); EXPECT_EQ( + "f57600d50" "m400s6100" "m400s2800m400s2800m400s2800m400s1700m400s2800m400s2800m400s1700m400s2800" "m400s2800m400s2800m400s1700m400s1700m400s2800m400s1700m400s2800m400s2800" diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_Electra_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Electra_test.cpp similarity index 84% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_Electra_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_Electra_test.cpp index df5dd7a5c..7d6d0c915 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_Electra_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Electra_test.cpp @@ -12,12 +12,13 @@ TEST(TestSendElectraAC, SendDataOnly) { IRsendTest irsend(0); irsend.begin(); - uint8_t data[kElectraAcStateLength] = {0xC3, 0xE1, 0x6F, 0x14, 0x06, - 0x00, 0x04, 0x00, 0x00, 0x04, - 0x00, 0xA0, 0xB0}; + uint8_t data[kElectraAcStateLength] = {0xC3, 0x87, 0xF6, 0x28, 0x60, + 0x00, 0x20, 0x00, 0x00, 0x20, + 0x00, 0x05, 0x0D}; irsend.sendElectraAC(data); EXPECT_EQ( + "f38000d50" "m9166s4470" "m646s1647m646s1647m646s547m646s547m646s547m646s547m646s1647m646s1647" "m646s1647m646s1647m646s1647m646s547m646s547m646s547m646s547m646s1647" @@ -46,9 +47,9 @@ TEST(TestDecodeElectraAC, SyntheticDecode) { // Synthesised Normal ElectraAC message. irsend.reset(); - uint8_t expectedState[kElectraAcStateLength] = {0xC3, 0xE1, 0x6F, 0x14, 0x06, - 0x00, 0x04, 0x00, 0x00, 0x04, - 0x00, 0xA0, 0xB0}; + uint8_t expectedState[kElectraAcStateLength] = {0xC3, 0x87, 0xF6, 0x28, 0x60, + 0x00, 0x20, 0x00, 0x00, 0x20, + 0x00, 0x05, 0x0D}; irsend.sendElectraAC(expectedState); irsend.makeDecodeResult(); EXPECT_TRUE(irrecv.decode(&irsend.capture)); @@ -84,9 +85,9 @@ TEST(TestDecodeElectraAC, RealExampleDecode) { 662, 562, 642, 1686, 582, 570, 634, 566, 604, 576, 636, 566, 610, 578, 634, 1664, 584, 590, 660, 1636, 610, 1642, 664, 590, 610, 590, 636, 566, 634, 568, 686}; // UNKNOWN 9AD8CDB5 - uint8_t expectedState[kElectraAcStateLength] = {0xC3, 0xE1, 0x6F, 0x14, 0x06, - 0x00, 0x04, 0x00, 0x00, 0x04, - 0x00, 0xA0, 0xB0}; + uint8_t expectedState[kElectraAcStateLength] = {0xC3, 0x87, 0xF6, 0x28, 0x60, + 0x00, 0x20, 0x00, 0x00, 0x20, + 0x00, 0x05, 0x0D}; irsend.reset(); irsend.sendRaw(rawData, 211, 38000); diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_Fujitsu_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Fujitsu_test.cpp similarity index 99% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_Fujitsu_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_Fujitsu_test.cpp index 23fa3e7a7..b895e4d9b 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_Fujitsu_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Fujitsu_test.cpp @@ -183,6 +183,7 @@ TEST(TestSendFujitsuAC, GenerateMessage) { irsend.reset(); irsend.sendFujitsuAC(fujitsu.getRaw(), kFujitsuAcStateLength); EXPECT_EQ( + "f38000d50" "m3324s1574" "m448s390m448s390m448s1182m448s390m448s1182m448s390m448s390m448s390" "m448s1182m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390" @@ -217,6 +218,7 @@ TEST(TestSendFujitsuAC, GenerateShortMessage) { irsend.reset(); irsend.sendFujitsuAC(fujitsu.getRaw(), kFujitsuAcStateLengthShort); EXPECT_EQ( + "f38000d50" "m3324s1574m448s390m448s390m448s1182m448s390m448s1182m448s390m448s390m448" "s390m448s1182m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390" "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" @@ -238,6 +240,7 @@ TEST(TestSendFujitsuAC, Issue275) { fujitsu.setCmd(kFujitsuAcCmdTurnOff); irsend.sendFujitsuAC(fujitsu.getRaw(), kFujitsuAcStateLengthShort); EXPECT_EQ( + "f38000d50" // Header "m3324s1574" // 0 0 1 0 1 0 0 0 (0x28) @@ -272,6 +275,7 @@ TEST(TestSendFujitsuAC, Issue275) { 450, 1250, 450}; irsend.sendRaw(off, 115, 38); EXPECT_EQ( + "f38000d50" // Header "m3350s1650" // 0 0 1 0 1 0 0 0 (0x28) @@ -534,6 +538,7 @@ TEST(TestDecodeFujitsuAC, Issue414) { ASSERT_EQ(kFujitsuAcBits, irsend.capture.bits); EXPECT_TRUE(ArraysMatch(state, irsend.capture.state)); EXPECT_EQ( + "f38000d50" "m3324s1574" "m448s390m448s390m448s1182m448s390m448s1182m448s390m448s390m448s390" "m448s1182m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390" diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_GICable_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_GICable_test.cpp similarity index 98% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_GICable_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_GICable_test.cpp index b9bfce997..bad9bbded 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_GICable_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_GICable_test.cpp @@ -12,6 +12,7 @@ TEST(TestSendGICable, SendDataOnly) { irsend.begin(); irsend.sendGICable(0); EXPECT_EQ( + "f39000d50" "m9000s4400" "m550s2200m550s2200m550s2200m550s2200m550s2200m550s2200m550s2200m550s2200" "m550s2200m550s2200m550s2200m550s2200m550s2200m550s2200m550s2200m550s2200" @@ -20,6 +21,7 @@ TEST(TestSendGICable, SendDataOnly) { irsend.outputStr()); irsend.sendGICable(0x8807); EXPECT_EQ( + "f39000d50" "m9000s4400" "m550s4400m550s2200m550s2200m550s2200m550s4400m550s2200m550s2200m550s2200" "m550s2200m550s2200m550s2200m550s2200m550s2200m550s4400m550s4400m550s4400" @@ -28,6 +30,7 @@ TEST(TestSendGICable, SendDataOnly) { irsend.outputStr()); irsend.sendGICable(0xFFFF); EXPECT_EQ( + "f39000d50" "m9000s4400" "m550s4400m550s4400m550s4400m550s4400m550s4400m550s4400m550s4400m550s4400" "m550s4400m550s4400m550s4400m550s4400m550s4400m550s4400m550s4400m550s4400" @@ -43,6 +46,7 @@ TEST(TestSendGICable, SendWithRepeats) { // Send a command with 0 repeats. irsend.sendGICable(0x8807, kGicableBits, 0); EXPECT_EQ( + "f39000d50" "m9000s4400" "m550s4400m550s2200m550s2200m550s2200m550s4400m550s2200m550s2200m550s2200" "m550s2200m550s2200m550s2200m550s2200m550s2200m550s4400m550s4400m550s4400" @@ -51,6 +55,7 @@ TEST(TestSendGICable, SendWithRepeats) { // Send a command with 1 repeat. irsend.sendGICable(0x8807, kGicableBits, 1); EXPECT_EQ( + "f39000d50" "m9000s4400" "m550s4400m550s2200m550s2200m550s2200m550s4400m550s2200m550s2200m550s2200" "m550s2200m550s2200m550s2200m550s2200m550s2200m550s4400m550s4400m550s4400" @@ -60,6 +65,7 @@ TEST(TestSendGICable, SendWithRepeats) { // Send a command with 3 repeats. irsend.sendGICable(0x8807, kGicableBits, 3); EXPECT_EQ( + "f39000d50" "m9000s4400" "m550s4400m550s2200m550s2200m550s2200m550s4400m550s2200m550s2200m550s2200" "m550s2200m550s2200m550s2200m550s2200m550s2200m550s4400m550s4400m550s4400" diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_GlobalCache_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_GlobalCache_test.cpp similarity index 98% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_GlobalCache_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_GlobalCache_test.cpp index 16a556b57..00742aeda 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_GlobalCache_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_GlobalCache_test.cpp @@ -29,6 +29,7 @@ TEST(TestSendGlobalCache, NonRepeatingCode) { EXPECT_EQ(0x4, irsend.capture.address); EXPECT_EQ(0x41, irsend.capture.command); EXPECT_EQ( + "f38000d50" "m8892s4472m546s572m546s546m546s1690m546s546m546s572m546s572" "m546s546m546s572m546s1690m546s1690m546s572m546s1690m546s1690" "m546s1690m546s1690m546s1690m546s1690m546s572m546s572m546s546" @@ -60,6 +61,7 @@ TEST(TestSendGlobalCache, RepeatCode) { EXPECT_EQ(0x4583, irsend.capture.address); EXPECT_EQ(0x11, irsend.capture.command); EXPECT_EQ( + "f38000d50" "m8866s4446m546s1664m546s1664m546s546m546s546m546s546m546s546" "m546s546m546s1664m546s1664m546s546m546s1664m546s546m546s546" "m546s546m546s1664m546s546m546s1664m546s546m546s546m546s546" diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_Gree_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Gree_test.cpp similarity index 98% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_Gree_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_Gree_test.cpp index 6c7a1f637..a05b06391 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_Gree_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Gree_test.cpp @@ -20,7 +20,8 @@ TEST(TestSendGreeChars, SendData) { irsend.reset(); irsend.sendGree(gree_code); EXPECT_EQ( - "m9000s4000" + "f38000d50" + "m9000s4500" "m620s540m620s1600m620s540m620s540m620s1600m620s540m620s540m620s540" "m620s540m620s540m620s1600m620s540m620s1600m620s1600m620s540m620s540" "m620s540m620s1600m620s1600m620s540m620s1600m620s540m620s1600m620s540" @@ -42,7 +43,8 @@ TEST(TestSendGreeUint64, SendData) { irsend.reset(); irsend.sendGree(0x1234567890ABCDEF); EXPECT_EQ( - "m9000s4000" + "f38000d50" + "m9000s4500" "m620s540m620s1600m620s540m620s540m620s1600m620s540m620s540m620s540" "m620s540m620s540m620s1600m620s540m620s1600m620s1600m620s540m620s540" "m620s540m620s1600m620s1600m620s540m620s1600m620s540m620s1600m620s540" @@ -69,7 +71,8 @@ TEST(TestSendGreeChars, SendWithRepeats) { irsend.sendGree(gree_code, kGreeStateLength, 1); EXPECT_EQ( - "m9000s4000" + "f38000d50" + "m9000s4500" "m620s540m620s1600m620s540m620s540m620s1600m620s540m620s540m620s540" "m620s540m620s540m620s1600m620s540m620s1600m620s1600m620s540m620s540" "m620s540m620s1600m620s1600m620s540m620s1600m620s540m620s1600m620s540" @@ -81,7 +84,7 @@ TEST(TestSendGreeChars, SendWithRepeats) { "m620s1600m620s540m620s1600m620s1600m620s540m620s540m620s1600m620s1600" "m620s1600m620s1600m620s1600m620s1600m620s540m620s1600m620s1600m620s1600" "m620s19000" - "m9000s4000" + "m9000s4500" "m620s540m620s1600m620s540m620s540m620s1600m620s540m620s540m620s540" "m620s540m620s540m620s1600m620s540m620s1600m620s1600m620s540m620s540" "m620s540m620s1600m620s1600m620s540m620s1600m620s540m620s1600m620s540" @@ -103,7 +106,8 @@ TEST(TestSendGreeUint64, SendWithRepeats) { irsend.reset(); irsend.sendGree(0x1234567890ABCDEF, kGreeBits, 1); EXPECT_EQ( - "m9000s4000" + "f38000d50" + "m9000s4500" "m620s540m620s1600m620s540m620s540m620s1600m620s540m620s540m620s540" "m620s540m620s540m620s1600m620s540m620s1600m620s1600m620s540m620s540" "m620s540m620s1600m620s1600m620s540m620s1600m620s540m620s1600m620s540" @@ -115,7 +119,7 @@ TEST(TestSendGreeUint64, SendWithRepeats) { "m620s1600m620s540m620s1600m620s1600m620s540m620s540m620s1600m620s1600" "m620s1600m620s1600m620s1600m620s1600m620s540m620s1600m620s1600m620s1600" "m620s19000" - "m9000s4000" + "m9000s4500" "m620s540m620s1600m620s540m620s540m620s1600m620s540m620s540m620s540" "m620s540m620s540m620s1600m620s540m620s1600m620s1600m620s540m620s540" "m620s540m620s1600m620s1600m620s540m620s1600m620s540m620s1600m620s540" @@ -146,7 +150,8 @@ TEST(TestSendGreeChars, SendUnexpectedSizes) { irsend.reset(); irsend.sendGree(gree_long_code, kGreeStateLength + 1); ASSERT_EQ( - "m9000s4000" + "f38000d50" + "m9000s4500" "m620s540m620s1600m620s540m620s540m620s1600m620s540m620s540m620s540" "m620s540m620s540m620s1600m620s540m620s1600m620s1600m620s540m620s540" "m620s540m620s1600m620s1600m620s540m620s1600m620s540m620s1600m620s540" @@ -486,7 +491,7 @@ TEST(TestDecodeGree, NormalSynthetic) { EXPECT_STATE_EQ(gree_code, irsend.capture.state, kGreeBits); } -// Decode a synthetic Gree message. +// Decode a real Gree message. TEST(TestDecodeGree, NormalRealExample) { IRsendTest irsend(4); IRrecv irrecv(4); diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_Haier_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Haier_test.cpp similarity index 90% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_Haier_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_Haier_test.cpp index 11848e00a..8d306cb0b 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_Haier_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Haier_test.cpp @@ -19,6 +19,7 @@ TEST(TestSendHaierAC, SendDataOnly) { irsend.reset(); irsend.sendHaierAC(haier_zero); EXPECT_EQ( + "f38000d50" "m3000s3000m3000s4300" "m520s650m520s650m520s650m520s650m520s650m520s650m520s650m520s650" "m520s650m520s650m520s650m520s650m520s650m520s650m520s650m520s650" @@ -37,6 +38,7 @@ TEST(TestSendHaierAC, SendDataOnly) { irsend.reset(); irsend.sendHaierAC(haier_test); EXPECT_EQ( + "f38000d50" "m3000s3000m3000s4300" "m520s1650m520s650m520s1650m520s650m520s650m520s1650m520s650m520s1650" "m520s650m520s650m520s650m520s650m520s650m520s650m520s650m520s1650" @@ -62,6 +64,7 @@ TEST(TestSendHaierAC, SendWithRepeats) { irsend.reset(); irsend.sendHaierAC(haier_test, kHaierACStateLength, 2); // two repeats. EXPECT_EQ( + "f38000d50" "m3000s3000m3000s4300" "m520s1650m520s650m520s1650m520s650m520s650m520s1650m520s650m520s1650" "m520s650m520s650m520s650m520s650m520s650m520s650m520s650m520s1650" @@ -404,7 +407,7 @@ TEST(TestHaierACClass, MessageConstuction) { haier.toString()); uint8_t expectedState[kHaierACStateLength] = {0xA5, 0x96, 0xEA, 0xCF, 0x32, - 0x2D, 0x0D, 0x74, 0xD4}; + 0xED, 0x2D, 0x74, 0xB4}; EXPECT_STATE_EQ(expectedState, haier.getRaw(), kHaierACBits); // Check that the checksum is valid. @@ -987,3 +990,91 @@ TEST(TestDecodeHaierAC_YRW02, RealExample) { " Health: On", haier.toString()); } + +// Default state of the remote needed to include hidden data. +// Ref: https://github.com/markszabo/IRremoteESP8266/issues/668 +TEST(TestHaierAcIssues, Issue668) { + IRHaierAC ac(0); + IRHaierAC acText(1); + IRrecv irrecv(0); + ac.begin(); + + // Turn on the AC. + ac._irsend.reset(); + char expected_on[] = + "Command: 1 (On), Mode: 0 (AUTO), Temp: 25C, Fan: 0 (AUTO), " + "Swing: 0 (Off), Sleep: Off, Health: Off, Current Time: 00:00, " + "On Timer: Off, Off Timer: Off"; + // State taken from real capture: + // https://github.com/markszabo/IRremoteESP8266/issues/668#issuecomment-483531895 + uint8_t expected_on_state[9] = { + 0xA5, 0x91, 0x20, 0x00, 0x0C, 0xC0, 0x20, 0x00, 0x42}; + ac.setCommand(kHaierAcCmdOn); + EXPECT_EQ(expected_on, ac.toString()); + ac.send(); + ac._irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&ac._irsend.capture)); + ASSERT_EQ(HAIER_AC, ac._irsend.capture.decode_type); + EXPECT_EQ(kHaierACBits, ac._irsend.capture.bits); + EXPECT_STATE_EQ(expected_on_state, + ac._irsend.capture.state, ac._irsend.capture.bits); + acText.setRaw(ac._irsend.capture.state); + EXPECT_EQ(expected_on, acText.toString()); + + // Set the temp to 25C + ac._irsend.reset(); + ac.setTemp(25); + EXPECT_EQ(expected_on, ac.toString()); + ASSERT_EQ(25, ac.getTemp()); + ac.send(); + ac._irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&ac._irsend.capture)); + ASSERT_EQ(HAIER_AC, ac._irsend.capture.decode_type); + EXPECT_EQ(kHaierACBits, ac._irsend.capture.bits); + EXPECT_STATE_EQ(expected_on_state, + ac._irsend.capture.state, ac._irsend.capture.bits); + acText.setRaw(ac._irsend.capture.state); + EXPECT_EQ(expected_on, acText.toString()); + + // Increase the temp by 1. + ac._irsend.reset(); + char expected_temp_plus_one[] = + "Command: 6 (Temp Up), Mode: 0 (AUTO), Temp: 26C, Fan: 0 (AUTO), " + "Swing: 0 (Off), Sleep: Off, Health: Off, Current Time: 00:00, " + "On Timer: Off, Off Timer: Off"; + // State taken from real capture: + // https://github.com/markszabo/IRremoteESP8266/issues/668#issuecomment-483531895 + uint8_t expected_temp_plus_one_state[9] = { + 0xA5, 0xA6, 0x20, 0x00, 0x0C, 0xC0, 0x20, 0x00, 0x57}; + ASSERT_EQ(25, ac.getTemp()); + ac.setTemp(ac.getTemp() + 1); + ASSERT_EQ(26, ac.getTemp()); + EXPECT_EQ(expected_temp_plus_one, ac.toString()); + ac.send(); + ac._irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&ac._irsend.capture)); + ASSERT_EQ(HAIER_AC, ac._irsend.capture.decode_type); + EXPECT_EQ(kHaierACBits, ac._irsend.capture.bits); + EXPECT_STATE_EQ(expected_temp_plus_one_state, + ac._irsend.capture.state, ac._irsend.capture.bits); + acText.setRaw(ac._irsend.capture.state); + EXPECT_EQ(expected_temp_plus_one, acText.toString()); + + // Decrease the temp by 1. + ac._irsend.reset(); + char expected_temp_minus_one[] = + "Command: 7 (Temp Down), Mode: 0 (AUTO), Temp: 25C, Fan: 0 (AUTO), " + "Swing: 0 (Off), Sleep: Off, Health: Off, Current Time: 00:00, " + "On Timer: Off, Off Timer: Off"; + ASSERT_EQ(26, ac.getTemp()); + ac.setTemp(ac.getTemp() - 1); + ASSERT_EQ(25, ac.getTemp()); + EXPECT_EQ(expected_temp_minus_one, ac.toString()); + ac.send(); + ac._irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&ac._irsend.capture)); + ASSERT_EQ(HAIER_AC, ac._irsend.capture.decode_type); + EXPECT_EQ(kHaierACBits, ac._irsend.capture.bits); + acText.setRaw(ac._irsend.capture.state); + EXPECT_EQ(expected_temp_minus_one, acText.toString()); +} diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_Hitachi_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Hitachi_test.cpp similarity index 99% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_Hitachi_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_Hitachi_test.cpp index de0a4a2a1..a2471c4aa 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_Hitachi_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Hitachi_test.cpp @@ -22,6 +22,7 @@ TEST(TestSendHitachiAC, SendData) { irsend.reset(); irsend.sendHitachiAC(hitachi_code); EXPECT_EQ( + "f38000d50" "m3300s1700" "m400s1250m400s500m400s500m400s500m400s500m400s500m400s500m400s500" "m400s500m400s500m400s500m400s500m400s1250m400s500m400s500m400s500" @@ -68,6 +69,7 @@ TEST(TestSendHitachiAC, SendWithRepeats) { irsend.sendHitachiAC(hitachi_code, kHitachiAcStateLength, 1); EXPECT_EQ( + "f38000d50" "m3300s1700" "m400s1250m400s500m400s500m400s500m400s500m400s500m400s500m400s500" "m400s500m400s500m400s500m400s500m400s1250m400s500m400s500m400s500" @@ -151,6 +153,7 @@ TEST(TestSendHitachiAC, SendUnexpectedSizes) { irsend.reset(); irsend.sendHitachiAC(hitachi_long_code, kHitachiAcStateLength + 1); ASSERT_EQ( + "f38000d50" "m3300s1700" "m400s1250m400s500m400s500m400s500m400s500m400s500m400s500m400s500" "m400s500m400s500m400s500m400s500m400s1250m400s500m400s500m400s500" @@ -511,6 +514,7 @@ TEST(TestSendHitachiAC1, SendData) { irsend.reset(); irsend.sendHitachiAC1(hitachi_code); EXPECT_EQ( + "f38000d50" "m3400s3400" "m400s1250m400s500m400s1250m400s1250m400s500m400s500m400s1250m400s500" "m400s1250m400s500m400s1250m400s500m400s1250m400s1250m400s1250m400s500" @@ -585,6 +589,7 @@ TEST(TestSendHitachiAC2, SendData) { irsend.reset(); irsend.sendHitachiAC2(hitachi_code); EXPECT_EQ( + "f38000d50" "m3300s1700" "m400s1250m400s500m400s500m400s500m400s500m400s500m400s500m400s500" "m400s500m400s500m400s500m400s500m400s1250m400s500m400s500m400s500" diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_JVC_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_JVC_test.cpp similarity index 99% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_JVC_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_JVC_test.cpp index c899fa8c6..3cd99c3aa 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_JVC_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_JVC_test.cpp @@ -14,6 +14,7 @@ TEST(TestSendJVC, SendDataOnly) { irsend.reset(); irsend.sendJVC(0xC2B8); // JVC VCR Power On. EXPECT_EQ( + "f38000d33" "m8400s4200" "m525s1725m525s1725m525s525m525s525m525s525m525s525m525s1725m525s525" "m525s1725m525s525m525s1725m525s1725m525s1725m525s525m525s525m525s525" @@ -29,6 +30,7 @@ TEST(TestSendJVC, SendWithRepeats) { irsend.reset(); irsend.sendJVC(0xC2B8, kJvcBits, 1); // 1 repeat. EXPECT_EQ( + "f38000d33" "m8400s4200" "m525s1725m525s1725m525s525m525s525m525s525m525s525m525s1725m525s525" "m525s1725m525s525m525s1725m525s1725m525s1725m525s525m525s525m525s525" @@ -39,6 +41,7 @@ TEST(TestSendJVC, SendWithRepeats) { irsend.outputStr()); irsend.sendJVC(0xC2B8, kJvcBits, 2); // 2 repeats. EXPECT_EQ( + "f38000d33" "m8400s4200" "m525s1725m525s1725m525s525m525s525m525s525m525s525m525s1725m525s525" "m525s1725m525s525m525s1725m525s1725m525s1725m525s525m525s525m525s525" @@ -60,6 +63,7 @@ TEST(TestSendJVC, SendUnusualSize) { irsend.reset(); irsend.sendJVC(0x0, 8); EXPECT_EQ( + "f38000d33" "m8400s4200" "m525s525m525s525m525s525m525s525m525s525m525s525m525s525m525s525" "m525s38475", @@ -68,6 +72,7 @@ TEST(TestSendJVC, SendUnusualSize) { irsend.reset(); irsend.sendJVC(0x1234567890ABCDEF, 64); EXPECT_EQ( + "f38000d33" "m8400s4200" "m525s525m525s525m525s525m525s1725m525s525m525s525m525s1725m525s525" "m525s525m525s525m525s1725m525s1725m525s525m525s1725m525s525m525s525" diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_Kelvinator_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Kelvinator_test.cpp similarity index 99% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_Kelvinator_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_Kelvinator_test.cpp index 001f8bcf2..38a298e58 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_Kelvinator_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Kelvinator_test.cpp @@ -21,6 +21,7 @@ TEST(TestSendKelvinator, SendDataOnly) { irsend.reset(); irsend.sendKelvinator(kelv_code); EXPECT_EQ( + "f38000d50" "m9010s4505" "m680s1530m680s510m680s510m680s1530m680s1530m680s510m680s510m680s510" "m680s1530m680s1530m680s510m680s1530m680s510m680s510m680s510m680s510" @@ -61,6 +62,7 @@ TEST(TestSendKelvinator, SendWithRepeats) { irsend.sendKelvinator(kelv_code, kKelvinatorStateLength, 1); EXPECT_EQ( + "f38000d50" "m9010s4505" "m680s1530m680s510m680s510m680s1530m680s1530m680s510m680s510m680s510" "m680s1530m680s1530m680s510m680s1530m680s510m680s510m680s510m680s510" @@ -131,6 +133,7 @@ TEST(TestSendKelvinator, SendUnexpectedSizes) { // extra data. irsend.sendKelvinator(kelv_long_code, 17); ASSERT_EQ( + "f38000d50" "m9010s4505" "m680s1530m680s510m680s510m680s1530m680s1530m680s510m680s510m680s510" "m680s1530m680s1530m680s510m680s1530m680s510m680s510m680s510m680s510" @@ -472,6 +475,7 @@ TEST(TestKelvinatorClass, MessageConstuction) { irsend.reset(); irsend.sendKelvinator(irkelv.getRaw()); EXPECT_EQ( + "f38000d50" "m9010s4505" "m680s1530m680s510m680s510m680s1530m680s1530m680s510m680s1530m680s510" "m680s1530m680s1530m680s510m680s1530m680s510m680s510m680s510m680s510" diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_LG_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_LG_test.cpp similarity index 88% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_LG_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_LG_test.cpp index 8ab24a731..2925494b9 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_LG_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_LG_test.cpp @@ -30,6 +30,7 @@ TEST(TestSendLG, SendDataOnly) { irsend.reset(); irsend.sendLG(0x4B4AE51); EXPECT_EQ( + "f38000d50" "m8500s4250" "m550s550m550s1600m550s550m550s550" "m550s1600m550s550m550s1600m550s1600m550s550m550s1600m550s550m550s550" @@ -41,6 +42,7 @@ TEST(TestSendLG, SendDataOnly) { irsend.reset(); irsend.sendLG(0xB4B4AE51, kLg32Bits); EXPECT_EQ( + "f38000d33" "m4480s4480" "m560s1680m560s560m560s1680m560s1680m560s560m560s1680m560s560m560s560" "m560s1680m560s560m560s1680m560s1680m560s560m560s1680m560s560m560s560" @@ -59,6 +61,7 @@ TEST(TestSendLG, SendWithRepeats) { irsend.reset(); irsend.sendLG(0x4B4AE51, kLgBits, 1); EXPECT_EQ( + "f38000d50" "m8500s4250" "m550s550m550s1600m550s550m550s550" "m550s1600m550s550m550s1600m550s1600m550s550m550s1600m550s550m550s550" @@ -71,6 +74,7 @@ TEST(TestSendLG, SendWithRepeats) { irsend.reset(); irsend.sendLG(0xB4B4AE51, kLg32Bits, 1); EXPECT_EQ( + "f38000d33" "m4480s4480" "m560s1680m560s560m560s1680m560s1680m560s560m560s1680m560s560m560s560" "m560s1680m560s560m560s1680m560s1680m560s560m560s1680m560s560m560s560" @@ -90,6 +94,7 @@ TEST(TestSendLG, SendUnusualSize) { irsend.reset(); irsend.sendLG(0x0, 31); EXPECT_EQ( + "f38000d50" "m8500s4250" "m550s550m550s550m550s550m550s550m550s550m550s550m550s550m550s550" "m550s550m550s550m550s550m550s550m550s550m550s550m550s550m550s550" @@ -101,6 +106,7 @@ TEST(TestSendLG, SendUnusualSize) { irsend.reset(); irsend.sendLG(0x0, 64); EXPECT_EQ( + "f38000d33" "m4480s4480" "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" @@ -373,6 +379,7 @@ TEST(TestSendLG2, SendDataOnly) { irsend.reset(); irsend.sendLG2(0x880094D); EXPECT_EQ( + "f38000d50" "m3200s9850" "m550s1600m550s550m550s550m550s550m550s1600m550s550m550s550m550s550" "m550s550m550s550m550s550m550s550m550s550m550s550m550s550m550s550" @@ -420,3 +427,54 @@ TEST(TestDecodeLG2, RealLG2Example) { EXPECT_EQ(kLgBits, irsend.capture.bits); EXPECT_EQ(0x880094D, irsend.capture.value); } + +// Tests for issue reported in +// https://github.com/markszabo/IRremoteESP8266/issues/620 +TEST(TestDecodeLG, Issue620) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + // Raw data as reported in initial comment of Issue #620 + uint16_t rawData[59] = { + 8886, 4152, + 560, 1538, 532, 502, 532, 504, 530, 484, 558, 1536, + 508, 516, 558, 502, 532, 484, 558, 502, 532, 500, + 534, 508, 532, 502, 532, 1518, 558, 510, 532, 484, + 556, 486, 556, 510, 532, 1518, 558, 1560, 532, 1528, + 556, 504, 530, 506, 530, 1520, 558, 508, 534, 500, + 532, 512, 530, 484, 556, 1536, 532}; // LG 8808721 + irsend.sendRaw(rawData, 59, 38000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(LG, irsend.capture.decode_type); + EXPECT_EQ(28, irsend.capture.bits); + EXPECT_EQ(0x8808721, irsend.capture.value); + EXPECT_EQ(0x88, irsend.capture.address); + EXPECT_EQ(0x872, irsend.capture.command); + + irsend.reset(); + + // Resend the same code as the report is a sent code doesn't decode + // to the same message code. + irsend.sendLG(0x8808721); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(LG, irsend.capture.decode_type); + EXPECT_EQ(28, irsend.capture.bits); + EXPECT_EQ(0x8808721, irsend.capture.value); + EXPECT_EQ(0x88, irsend.capture.address); + EXPECT_EQ(0x872, irsend.capture.command); + // The following seems to match the rawData above. + EXPECT_EQ( + "f38000d50" + "m8500s4250" + "m550s1600m550s550m550s550m550s550m550s1600" + "m550s550m550s550m550s550m550s550m550s550" + "m550s550m550s550m550s1600m550s550m550s550" + "m550s550m550s550m550s1600m550s1600m550s1600" + "m550s550m550s550m550s1600m550s550m550s550" + "m550s550m550s550m550s1600m550" + "s55550", + irsend.outputStr()); +} diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_Lasertag_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Lasertag_test.cpp similarity index 98% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_Lasertag_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_Lasertag_test.cpp index 041109fb8..bad724f76 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_Lasertag_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Lasertag_test.cpp @@ -20,6 +20,7 @@ TEST(TestSendLasertag, SendDataOnly) { irsend.reset(); irsend.sendLasertag(0x1); // Red 1 EXPECT_EQ( + "f36000d25" "m333s333m333s333m333s333m333s333m333s333m333s333m333s333m333s333m333" "s333m333s333m333s333m333s666m333s100000", irsend.outputStr()); @@ -27,6 +28,7 @@ TEST(TestSendLasertag, SendDataOnly) { irsend.reset(); irsend.sendLasertag(0x2); // Red 2 EXPECT_EQ( + "f36000d25" "m333s333m333s333m333s333m333s333m333s333m333s333m333s333" "m333s333m333s333m333s333m333s666m666s100333", irsend.outputStr()); @@ -38,6 +40,7 @@ TEST(TestSendLasertag, SendDataOnly) { EXPECT_EQ( // m364s364m332s336m384s276m332s364m332s304m416s584 // m692s724m640s360m304s332m392s612m380 + "f36000d25" "m333s333m333s333m333s333m333s333m333s333m333s666" "m666s666m666s333m333s333m333s666m333s100000", irsend.outputStr()); @@ -49,6 +52,7 @@ TEST(TestSendLasertag, SendDataOnly) { EXPECT_EQ( // m332s308m412s280m360s336m332s304m444s248m332s644 // m744s612m696s692m668s636m360 + "f36000d25" "m333s333m333s333m333s333m333s333m333s333m333s666" "m666s666m666s666m666s666m333s100000", irsend.outputStr()); @@ -61,6 +65,7 @@ TEST(TestSendLasertag, SendDataWithRepeat) { irsend.reset(); irsend.sendLasertag(0x1, kLasertagBits, 1); // Red 1, one repeat. EXPECT_EQ( + "f36000d25" "m333s333m333s333m333s333m333s333m333s333m333s333m333s333m333s333" "m333s333m333s333m333s333m333s666m333s100000" "m333s333m333s333m333s333m333s333m333s333m333s333m333s333m333s333" @@ -70,6 +75,7 @@ TEST(TestSendLasertag, SendDataWithRepeat) { irsend.reset(); irsend.sendLasertag(0x52, kLasertagBits, 2); // Green 2, two repeats. EXPECT_EQ( + "f36000d25" "m333s333m333s333m333s333m333s333m333s333m333s666m666s666m666s333" "m333s666m666s100333" "m333s333m333s333m333s333m333s333m333s333m333s666m666s666m666s333" @@ -86,7 +92,8 @@ TEST(TestSendLasertag, SmallestMessageSize) { irsend.reset(); irsend.sendLasertag(0x1555); // Alternating bit pattern will be the smallest. // i.e. 7 actual 'mark' pulses, which is a rawlen of 13. - EXPECT_EQ("m0s333m666s666m666s666m666s666m666s666m666s666m666s666m333s100000", + EXPECT_EQ("f36000d25" + "m0s333m666s666m666s666m666s666m666s666m666s666m666s666m333s100000", irsend.outputStr()); } diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Lego_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Lego_test.cpp new file mode 100644 index 000000000..4e859b170 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Lego_test.cpp @@ -0,0 +1,196 @@ +// Copyright 2019 David Conran + +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "gtest/gtest.h" + +// General housekeeping +TEST(TestLego, Housekeeping) { + ASSERT_EQ("LEGOPF", typeToString(LEGOPF)); + ASSERT_FALSE(hasACState(LEGOPF)); // Uses uint64_t, not uint8_t*. +} + +// Tests for sendLego(). + +// Test sending typical data only. +TEST(TestSendLegoPf, SendDataOnly) { + IRsendTest irsend(0); + irsend.begin(); + + irsend.reset(); + irsend.sendLegoPf(0x1234); + EXPECT_EQ( + "f38000d50" + "m158s1026" + "m158s263m158s263m158s263m158s553m158s263m158s263m158s553m158s263" + "m158s263m158s263m158s553m158s553m158s263m158s553m158s263m158s263" + "m158s70472", irsend.outputStr()); + + irsend.reset(); + irsend.send(LEGOPF, 0x1234, kLegoPfBits); + EXPECT_EQ( + "f38000d50" + "m158s1026" + "m158s263m158s263m158s263m158s553m158s263m158s263m158s553m158s263" + "m158s263m158s263m158s553m158s553m158s263m158s553m158s263m158s263" + "m158s70472", irsend.outputStr()); +} + +// Test sending typical repeat data. +TEST(TestSendLegoPf, SendDataWithRepeats) { + IRsendTest irsend(0); + irsend.begin(); + + irsend.reset(); + irsend.sendLegoPf(0x1234, kLegoPfBits, 1); + EXPECT_EQ( + "f38000d50" + "m0s32000" + "m158s1026" + "m158s263m158s263m158s263m158s553m158s263m158s263m158s553m158s263" + "m158s263m158s263m158s553m158s553m158s263m158s553m158s263m158s263" + "m158s70472" + "m158s1026" + "m158s263m158s263m158s263m158s553m158s263m158s263m158s553m158s263" + "m158s263m158s263m158s553m158s553m158s263m158s553m158s263m158s263" + "m158s70472" + "m158s1026" + "m158s263m158s263m158s263m158s553m158s263m158s263m158s553m158s263" + "m158s263m158s263m158s553m158s553m158s263m158s553m158s263m158s263" + "m158s150472" + "m158s1026" + "m158s263m158s263m158s263m158s553m158s263m158s263m158s553m158s263" + "m158s263m158s263m158s553m158s553m158s263m158s553m158s263m158s263" + "m158s150472" + "m158s1026" + "m158s263m158s263m158s263m158s553m158s263m158s263m158s553m158s263" + "m158s263m158s263m158s553m158s553m158s263m158s553m158s263m158s263" + "m158s150472", irsend.outputStr()); + + irsend.reset(); + irsend.sendLegoPf(0x2345, kLegoPfBits, 2); + EXPECT_EQ( + "f38000d50" + "m0s16000" + "m158s1026" + "m158s263m158s263m158s553m158s263m158s263m158s263m158s553m158s553" + "m158s263m158s553m158s263m158s263m158s263m158s553m158s263m158s553" + "m158s70182" + "m158s1026" + "m158s263m158s263m158s553m158s263m158s263m158s263m158s553m158s553" + "m158s263m158s553m158s263m158s263m158s263m158s553m158s263m158s553" + "m158s70182" + "m158s1026" + "m158s263m158s263m158s553m158s263m158s263m158s263m158s553m158s553" + "m158s263m158s553m158s263m158s263m158s263m158s553m158s263m158s553" + "m158s182182" + "m158s1026" + "m158s263m158s263m158s553m158s263m158s263m158s263m158s553m158s553" + "m158s263m158s553m158s263m158s263m158s263m158s553m158s263m158s553" + "m158s182182" + "m158s1026" + "m158s263m158s263m158s553m158s263m158s263m158s263m158s553m158s553" + "m158s263m158s553m158s263m158s263m158s263m158s553m158s263m158s553" + "m158s182182", irsend.outputStr()); + + irsend.reset(); + irsend.sendLegoPf(0x3456, kLegoPfBits, 7); + EXPECT_EQ( + "f38000d50" + "m158s1026" + "m158s263m158s263m158s553m158s553m158s263m158s553m158s263m158s263" + "m158s263m158s553m158s263m158s553m158s263m158s553m158s553m158s263" + "m158s69892" + "m158s1026" + "m158s263m158s263m158s553m158s553m158s263m158s553m158s263m158s263" + "m158s263m158s553m158s263m158s553m158s263m158s553m158s553m158s263" + "m158s69892" + "m158s1026" + "m158s263m158s263m158s553m158s553m158s263m158s553m158s263m158s263" + "m158s263m158s553m158s263m158s553m158s263m158s553m158s553m158s263" + "m158s213892" + "m158s1026" + "m158s263m158s263m158s553m158s553m158s263m158s553m158s263m158s263" + "m158s263m158s553m158s263m158s553m158s263m158s553m158s553m158s263" + "m158s213892" + "m158s1026" + "m158s263m158s263m158s553m158s553m158s263m158s553m158s263m158s263" + "m158s263m158s553m158s263m158s553m158s263m158s553m158s553m158s263" + "m158s213892" + "m158s1026" + "m158s263m158s263m158s553m158s553m158s263m158s553m158s263m158s263" + "m158s263m158s553m158s263m158s553m158s263m158s553m158s553m158s263" + "m158s213892" + "m158s1026" + "m158s263m158s263m158s553m158s553m158s263m158s553m158s263m158s263" + "m158s263m158s553m158s263m158s553m158s263m158s553m158s553m158s263" + "m158s213892", irsend.outputStr()); +} + +// Tests for decodeLego(). + +// Decode normal "synthetic" messages. +TEST(TestDecodeLegoPf, SyntheticDecode) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + irsend.reset(); + irsend.sendLegoPf(0x000F); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(LEGOPF, irsend.capture.decode_type); + EXPECT_EQ(kLegoPfBits, irsend.capture.bits); + EXPECT_FALSE(irsend.capture.repeat); + EXPECT_EQ(0x000F, irsend.capture.value); + EXPECT_EQ(1, irsend.capture.address); + EXPECT_EQ(0, irsend.capture.command); + + irsend.reset(); + irsend.sendLegoPf(0x100E); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(LEGOPF, irsend.capture.decode_type); + EXPECT_EQ(kLegoPfBits, irsend.capture.bits); + EXPECT_FALSE(irsend.capture.repeat); + EXPECT_EQ(0x100E, irsend.capture.value); + EXPECT_EQ(2, irsend.capture.address); + EXPECT_EQ(0, irsend.capture.command); + + irsend.reset(); + irsend.sendLegoPf(0x221E); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(LEGOPF, irsend.capture.decode_type); + EXPECT_EQ(kLegoPfBits, irsend.capture.bits); + EXPECT_FALSE(irsend.capture.repeat); + EXPECT_EQ(0x221E, irsend.capture.value); + EXPECT_EQ(3, irsend.capture.address); + EXPECT_EQ(0x21, irsend.capture.command); + + // Test a bad LRC is not matched. + irsend.reset(); + irsend.sendLegoPf(0x001F); // LRC should be 0xE, not 0xF. + irsend.makeDecodeResult(); + irrecv.decode(&irsend.capture); + EXPECT_NE(LEGOPF, irsend.capture.decode_type); +} + +// Decode normal "synthetic" message with releats. +TEST(TestDecodeLegoPf, SyntheticDecodeWithRepeat) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + irsend.reset(); + irsend.sendLegoPf(0x330F, kLegoPfBits, 1); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(LEGOPF, irsend.capture.decode_type); + EXPECT_EQ(kLegoPfBits, irsend.capture.bits); + EXPECT_EQ(0x330F, irsend.capture.value); + EXPECT_EQ(4, irsend.capture.address); + EXPECT_EQ(0x30, irsend.capture.command); +} diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_Lutron_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Lutron_test.cpp similarity index 95% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_Lutron_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_Lutron_test.cpp index 6c99b9904..d682967ca 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_Lutron_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Lutron_test.cpp @@ -11,17 +11,19 @@ TEST(TestSendLutron, SendDataOnly) { IRsendTest irsend(0); irsend.begin(); irsend.sendLutron(0); - EXPECT_EQ("m2288s230080", irsend.outputStr()); + EXPECT_EQ("f40000d40m2288s230080", irsend.outputStr()); irsend.sendLutron(0xAAAAAAAAA); // Longest possible sequence. (I think) EXPECT_EQ( + "f40000d40" "m2288s2288m2288s2288m2288s2288m2288s2288m2288s2288m2288s2288m2288s2288" "m2288s2288m2288s2288m2288s2288m2288s2288m2288s2288m2288s2288m2288s2288" "m2288s2288m2288s2288m2288s2288m2288s152288", irsend.outputStr()); irsend.sendLutron(0x7FFFFFFFF); - EXPECT_EQ("m82368s150000", irsend.outputStr()); + EXPECT_EQ("f40000d40m82368s150000", irsend.outputStr()); irsend.sendLutron(0x7F88BD120); EXPECT_EQ( + "f40000d40" "m20592s6864m2288s6864m2288s2288m9152s2288m2288s6864m2288s4576m2288" "s161440", irsend.outputStr()); @@ -34,12 +36,14 @@ TEST(TestSendLutron, SendWithRepeats) { // Send a command with 0 repeats. irsend.sendLutron(0x7F88BD120, kLutronBits, 0); EXPECT_EQ( + "f40000d40" "m20592s6864m2288s6864m2288s2288m9152s2288m2288s6864m2288s4576m2288" "s161440", irsend.outputStr()); // Send a command with 1 repeat. irsend.sendLutron(0x7F88BD120, kLutronBits, 1); EXPECT_EQ( + "f40000d40" "m20592s6864m2288s6864m2288s2288m9152s2288m2288s6864m2288s4576m2288" "s161440" "m20592s6864m2288s6864m2288s2288m9152s2288m2288s6864m2288s4576m2288" @@ -48,6 +52,7 @@ TEST(TestSendLutron, SendWithRepeats) { // Send a command with 3 repeats. irsend.sendLutron(0x7F88BD120, kLutronBits, 3); EXPECT_EQ( + "f40000d40" "m20592s6864m2288s6864m2288s2288m9152s2288m2288s6864m2288s4576m2288" "s161440" "m20592s6864m2288s6864m2288s2288m9152s2288m2288s6864m2288s4576m2288" diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_MWM_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_MWM_test.cpp similarity index 99% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_MWM_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_MWM_test.cpp index 9ecd0eac1..2ca69ac83 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_MWM_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_MWM_test.cpp @@ -36,6 +36,7 @@ TEST(TestSendMWM, SendDataOnly) { */ irsend.sendMWM(test1, sizeof(test1), 0); EXPECT_EQ( + "f38000d25" "m834s834m417s417m834s834" "m417s417m834s834m1251s417" "m2085s417m1251s417" @@ -65,6 +66,7 @@ TEST(TestSendMWM, SendDataOnly) { }; irsend.sendMWM(test2, sizeof(test2), 0); EXPECT_EQ( + "f38000d25" "m417s417m834s834m834s834" "m834s834m834s417m834s417" "m834s834m834s834m417s417" diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_Magiquest_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Magiquest_test.cpp similarity index 99% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_Magiquest_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_Magiquest_test.cpp index e1c3da83d..bbc5f3366 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_Magiquest_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Magiquest_test.cpp @@ -26,6 +26,7 @@ TEST(TestSendMagiQuest, SendDataOnly) { irsend.reset(); irsend.sendMagiQuest(0x0); EXPECT_EQ( + "f36000d50" "m280s850m280s850m280s850m280s850m280s850m280s850m280s850m280s850" "m280s850m280s850m280s850m280s850m280s850m280s850m280s850m280s850" "m280s850m280s850m280s850m280s850m280s850m280s850m280s850m280s850" @@ -37,6 +38,7 @@ TEST(TestSendMagiQuest, SendDataOnly) { irsend.reset(); irsend.sendMagiQuest(0x123456789ABC); EXPECT_EQ( + "f36000d50" "m280s850m280s850m280s850m280s850m280s850m280s850m280s850m280s850" "m280s850m280s850m280s850m580s600m280s850m280s850m580s600m280s850" "m280s850m280s850m580s600m580s600m280s850m580s600m280s850m280s850" @@ -55,6 +57,7 @@ TEST(TestSendMagiQuest, SendWithRepeats) { irsend.reset(); irsend.sendMagiQuest(0x12345678ABCD, kMagiquestBits, 2); // two repeats. EXPECT_EQ( + "f36000d50" "m280s850m280s850m280s850m280s850m280s850m280s850m280s850m280s850" "m280s850m280s850m280s850m580s600m280s850m280s850m580s600m280s850" "m280s850m280s850m580s600m580s600m280s850m580s600m280s850m280s850" diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_Midea_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Midea_test.cpp similarity index 99% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_Midea_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_Midea_test.cpp index 5d5f5e932..ced3ea10c 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_Midea_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Midea_test.cpp @@ -15,6 +15,7 @@ TEST(TestSendMidea, SendDataOnly) { irsend.reset(); irsend.sendMidea(0x0); EXPECT_EQ( + "f38000d50" "m4480s4480" "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" @@ -36,6 +37,7 @@ TEST(TestSendMidea, SendDataOnly) { irsend.reset(); irsend.sendMidea(0x55AA55AA55AA); EXPECT_EQ( + "f38000d50" "m4480s4480" "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" @@ -57,6 +59,7 @@ TEST(TestSendMidea, SendDataOnly) { irsend.reset(); irsend.sendMidea(0xFFFFFFFFFFFF); EXPECT_EQ( + "f38000d50" "m4480s4480" "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" @@ -84,6 +87,7 @@ TEST(TestSendMidea, SendWithRepeats) { irsend.reset(); irsend.sendMidea(0x55AA55AA55AA, kMideaBits, 1); // 1 repeat. EXPECT_EQ( + "f38000d50" "m4480s4480" "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" @@ -119,6 +123,7 @@ TEST(TestSendMidea, SendWithRepeats) { irsend.outputStr()); irsend.sendMidea(0x55AA55AA55AA, kMideaBits, 2); // 2 repeats. EXPECT_EQ( + "f38000d50" "m4480s4480" "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" @@ -178,6 +183,7 @@ TEST(TestSendMidea, SendUnusualSize) { irsend.reset(); irsend.sendMidea(0x0, 8); EXPECT_EQ( + "f38000d50" "m4480s4480" "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" "m560s5600" @@ -189,6 +195,7 @@ TEST(TestSendMidea, SendUnusualSize) { irsend.reset(); irsend.sendMidea(0x1234567890ABCDEF, 64); EXPECT_EQ( + "f38000d50" "m4480s4480" "m560s560m560s560m560s560m560s1680m560s560m560s560m560s1680m560s560" "m560s560m560s560m560s1680m560s1680m560s560m560s1680m560s560m560s560" diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_MitsubishiHeavy_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_MitsubishiHeavy_test.cpp new file mode 100644 index 000000000..340a04078 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/test/ir_MitsubishiHeavy_test.cpp @@ -0,0 +1,851 @@ +// Copyright 2019 David Conran + +#include "ir_MitsubishiHeavy.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "gtest/gtest.h" + +// General housekeeping +TEST(TestMitsubishiHeavy, Housekeeping) { + ASSERT_EQ("MITSUBISHI_HEAVY_88", typeToString(MITSUBISHI_HEAVY_88)); + ASSERT_TRUE(hasACState(MITSUBISHI_HEAVY_88)); + ASSERT_EQ("MITSUBISHI_HEAVY_152", typeToString(MITSUBISHI_HEAVY_152)); + ASSERT_TRUE(hasACState(MITSUBISHI_HEAVY_152)); +} + +// Tests for IRMitsubishiHeavy152Ac class. + +TEST(TestMitsubishiHeavy152AcClass, Power) { + IRMitsubishiHeavy152Ac ac(0); + ac.begin(); + + ac.on(); + EXPECT_TRUE(ac.getPower()); + + ac.off(); + EXPECT_FALSE(ac.getPower()); + + ac.setPower(true); + EXPECT_TRUE(ac.getPower()); + + ac.setPower(false); + EXPECT_FALSE(ac.getPower()); +} + +TEST(TestMitsubishiHeavy152AcClass, Temperature) { + IRMitsubishiHeavy152Ac ac(0); + ac.begin(); + + ac.setMode(kMitsubishiHeavyCool); + + ac.setTemp(0); + EXPECT_EQ(kMitsubishiHeavyMinTemp, ac.getTemp()); + + ac.setTemp(255); + EXPECT_EQ(kMitsubishiHeavyMaxTemp, ac.getTemp()); + + ac.setTemp(kMitsubishiHeavyMinTemp); + EXPECT_EQ(kMitsubishiHeavyMinTemp, ac.getTemp()); + + ac.setTemp(kMitsubishiHeavyMaxTemp); + EXPECT_EQ(kMitsubishiHeavyMaxTemp, ac.getTemp()); + + ac.setTemp(kMitsubishiHeavyMinTemp - 1); + EXPECT_EQ(kMitsubishiHeavyMinTemp, ac.getTemp()); + + ac.setTemp(kMitsubishiHeavyMaxTemp + 1); + EXPECT_EQ(kMitsubishiHeavyMaxTemp, ac.getTemp()); + + ac.setTemp(19); + EXPECT_EQ(19, ac.getTemp()); + + ac.setTemp(21); + EXPECT_EQ(21, ac.getTemp()); + + ac.setTemp(25); + EXPECT_EQ(25, ac.getTemp()); + + ac.setTemp(29); + EXPECT_EQ(29, ac.getTemp()); +} + +TEST(TestMitsubishiHeavy152AcClass, OperatingMode) { + IRMitsubishiHeavy152Ac ac(0); + ac.begin(); + + ac.setMode(kMitsubishiHeavyAuto); + EXPECT_EQ(kMitsubishiHeavyAuto, ac.getMode()); + + ac.setMode(kMitsubishiHeavyCool); + EXPECT_EQ(kMitsubishiHeavyCool, ac.getMode()); + + ac.setMode(kMitsubishiHeavyHeat); + EXPECT_EQ(kMitsubishiHeavyHeat, ac.getMode()); + + ac.setMode(kMitsubishiHeavyDry); + EXPECT_EQ(kMitsubishiHeavyDry, ac.getMode()); + + ac.setMode(kMitsubishiHeavyFan); + EXPECT_EQ(kMitsubishiHeavyFan, ac.getMode()); + + ac.setMode(kMitsubishiHeavyHeat + 1); + EXPECT_EQ(kMitsubishiHeavyAuto, ac.getMode()); + + ac.setMode(255); + EXPECT_EQ(kMitsubishiHeavyAuto, ac.getMode()); +} + + +TEST(TestMitsubishiHeavy152AcClass, Filter) { + IRMitsubishiHeavy152Ac ac(0); + ac.begin(); + + ac.setFilter(true); + EXPECT_TRUE(ac.getFilter()); + + ac.setFilter(false); + EXPECT_FALSE(ac.getFilter()); + + ac.setFilter(true); + EXPECT_TRUE(ac.getFilter()); +} + +TEST(TestMitsubishiHeavy152AcClass, Turbo) { + IRMitsubishiHeavy152Ac ac(0); + ac.begin(); + + ac.setTurbo(true); + EXPECT_TRUE(ac.getTurbo()); + + ac.setTurbo(false); + EXPECT_FALSE(ac.getTurbo()); + + ac.setTurbo(true); + EXPECT_TRUE(ac.getTurbo()); +} + +TEST(TestMitsubishiHeavy152AcClass, Econo) { + IRMitsubishiHeavy152Ac ac(0); + ac.begin(); + + ac.setEcono(true); + EXPECT_TRUE(ac.getEcono()); + + ac.setEcono(false); + EXPECT_FALSE(ac.getEcono()); + + ac.setEcono(true); + EXPECT_TRUE(ac.getEcono()); +} + +TEST(TestMitsubishiHeavy152AcClass, 3D) { + IRMitsubishiHeavy152Ac ac(0); + ac.begin(); + + ac.set3D(true); + EXPECT_TRUE(ac.get3D()); + + ac.set3D(false); + EXPECT_FALSE(ac.get3D()); + + ac.set3D(true); + EXPECT_TRUE(ac.get3D()); +} + +TEST(TestMitsubishiHeavy152AcClass, Night) { + IRMitsubishiHeavy152Ac ac(0); + ac.begin(); + + ac.setNight(true); + EXPECT_TRUE(ac.getNight()); + + ac.setNight(false); + EXPECT_FALSE(ac.getNight()); + + ac.setNight(true); + EXPECT_TRUE(ac.getNight()); +} + +TEST(TestMitsubishiHeavy152AcClass, Clean) { + IRMitsubishiHeavy152Ac ac(0); + ac.begin(); + + ac.setClean(true); + EXPECT_TRUE(ac.getClean()); + + ac.setClean(false); + EXPECT_FALSE(ac.getClean()); + + ac.setClean(true); + EXPECT_TRUE(ac.getClean()); +} + +TEST(TestMitsubishiHeavy152AcClass, FanSpeed) { + IRMitsubishiHeavy152Ac ac(0); + ac.begin(); + + ac.setFan(kMitsubishiHeavy152FanLow); + EXPECT_EQ(kMitsubishiHeavy152FanLow, ac.getFan()); + + ac.setFan(kMitsubishiHeavy152FanAuto); + EXPECT_EQ(kMitsubishiHeavy152FanAuto, ac.getFan()); + + + ac.setFan(255); + EXPECT_EQ(kMitsubishiHeavy152FanAuto, ac.getFan()); + + ac.setFan(kMitsubishiHeavy152FanMax); + EXPECT_EQ(kMitsubishiHeavy152FanMax, ac.getFan()); + + ac.setFan(kMitsubishiHeavy152FanMax + 1); + EXPECT_EQ(kMitsubishiHeavy152FanAuto, ac.getFan()); + + ac.setFan(kMitsubishiHeavy152FanMax - 1); + EXPECT_EQ(kMitsubishiHeavy152FanMax - 1, ac.getFan()); + + ac.setFan(kMitsubishiHeavy152FanLow + 1); + EXPECT_EQ(kMitsubishiHeavy152FanLow + 1, ac.getFan()); + + ac.setFan(kMitsubishiHeavy152FanEcono); + EXPECT_EQ(kMitsubishiHeavy152FanEcono, ac.getFan()); + + ac.setFan(kMitsubishiHeavy152FanTurbo); + EXPECT_EQ(kMitsubishiHeavy152FanTurbo, ac.getFan()); +} + +TEST(TestMitsubishiHeavy152AcClass, VerticalSwing) { + IRMitsubishiHeavy152Ac ac(0); + ac.begin(); + ac.setSwingVertical(kMitsubishiHeavy152SwingVAuto); + EXPECT_EQ(kMitsubishiHeavy152SwingVAuto, ac.getSwingVertical()); + + ac.setSwingVertical(kMitsubishiHeavy152SwingVHighest); + EXPECT_EQ(kMitsubishiHeavy152SwingVHighest, ac.getSwingVertical()); + + ac.setSwingVertical(kMitsubishiHeavy152SwingVHighest + 1); + EXPECT_EQ(kMitsubishiHeavy152SwingVHighest + 1, ac.getSwingVertical()); + + ac.setSwingVertical(kMitsubishiHeavy152SwingVOff); + EXPECT_EQ(kMitsubishiHeavy152SwingVOff, ac.getSwingVertical()); + + ac.setSwingVertical(kMitsubishiHeavy152SwingVOff + 1); + EXPECT_EQ(kMitsubishiHeavy152SwingVOff, ac.getSwingVertical()); + + // Out of bounds. + ac.setSwingVertical(255); + EXPECT_EQ(kMitsubishiHeavy152SwingVOff, ac.getSwingVertical()); +} + +TEST(TestMitsubishiHeavy152AcClass, HorizontalSwing) { + IRMitsubishiHeavy152Ac ac(0); + ac.begin(); + ac.setSwingHorizontal(kMitsubishiHeavy152SwingHAuto); + EXPECT_EQ(kMitsubishiHeavy152SwingHAuto, ac.getSwingHorizontal()); + + ac.setSwingHorizontal(kMitsubishiHeavy152SwingHLeftMax); + EXPECT_EQ(kMitsubishiHeavy152SwingHLeftMax, ac.getSwingHorizontal()); + + ac.setSwingHorizontal(kMitsubishiHeavy152SwingHLeftMax + 1); + EXPECT_EQ(kMitsubishiHeavy152SwingHLeftMax + 1, ac.getSwingHorizontal()); + + ac.setSwingHorizontal(kMitsubishiHeavy152SwingHRightMax); + EXPECT_EQ(kMitsubishiHeavy152SwingHRightMax, ac.getSwingHorizontal()); + + ac.setSwingHorizontal(kMitsubishiHeavy152SwingHRightMax - 1); + EXPECT_EQ(kMitsubishiHeavy152SwingHRightMax - 1, ac.getSwingHorizontal()); + + ac.setSwingHorizontal(kMitsubishiHeavy152SwingHOff); + EXPECT_EQ(kMitsubishiHeavy152SwingHOff, ac.getSwingHorizontal()); + + ac.setSwingHorizontal(kMitsubishiHeavy152SwingHOff + 1); + EXPECT_EQ(kMitsubishiHeavy152SwingHOff, ac.getSwingHorizontal()); + + // Out of bounds. + ac.setSwingHorizontal(255); + EXPECT_EQ(kMitsubishiHeavy152SwingHOff, ac.getSwingHorizontal()); +} + +TEST(TestMitsubishiHeavy152AcClass, Checksums) { + IRMitsubishiHeavy152Ac ac(0); + ac.begin(); + + EXPECT_TRUE(ac.validChecksum(ac.getRaw())); + + uint8_t expected[kMitsubishiHeavy152StateLength] = { + 0xAD, 0x51, 0x3C, 0xE5, 0x1A, 0x0C, 0xF3, 0x07, + 0xF8, 0x04, 0xFB, 0x00, 0xFF, 0x00, 0xFF, 0x00, + 0xFF, 0x80, 0x7F}; + EXPECT_TRUE(IRMitsubishiHeavy152Ac::validChecksum(expected)); + + // Screw up the "checksum" to test it fails. + expected[kMitsubishiHeavy152StateLength - 1] = 0x55; + EXPECT_FALSE(IRMitsubishiHeavy152Ac::validChecksum(expected)); + // getting the after getRaw() should repair it. + ac.setRaw(expected); + EXPECT_TRUE(ac.validChecksum(ac.getRaw())); + EXPECT_TRUE(IRMitsubishiHeavy152Ac::validChecksum(ac.getRaw())); +} + +TEST(TestMitsubishiHeavy152AcClass, HumanReadable) { + IRMitsubishiHeavy152Ac ac(0); + + EXPECT_EQ( + "Power: Off, Mode: 0 (Auto), Temp: 17C, Fan: 0 (Auto), " + "Swing (V): 0 (Auto), Swing (H): 0 (Auto), Silent: Off, Turbo: Off, " + "Econo: Off, Night: Off, Filter: Off, 3D: Off, Clean: Off", + ac.toString()); + ac.on(); + ac.setMode(kMitsubishiHeavyCool); + ac.setTemp(kMitsubishiHeavyMinTemp); + ac.setFan(kMitsubishiHeavy152FanMax); + ac.setFilter(true); + ac.setNight(true); + ac.setTurbo(false); + ac.setSilent(true); + ac.setEcono(false); + ac.set3D(true); + ac.setSwingVertical(kMitsubishiHeavy152SwingVAuto); + ac.setSwingHorizontal(kMitsubishiHeavy152SwingHAuto); + EXPECT_EQ( + "Power: On, Mode: 1 (Cool), Temp: 17C, Fan: 4 (Max), " + "Swing (V): 0 (Auto), Swing (H): 0 (Auto), Silent: On, Turbo: Off, " + "Econo: Off, Night: On, Filter: On, 3D: On, Clean: Off", + ac.toString()); + + ac.setMode(kMitsubishiHeavyHeat); + ac.setTemp(kMitsubishiHeavyMaxTemp); + ac.setFilter(true); + ac.setNight(false); + ac.setTurbo(true); + ac.setEcono(false); + ac.setSilent(false); + ac.set3D(false); + ac.setSwingVertical(kMitsubishiHeavy152SwingVLowest); + ac.setSwingHorizontal(kMitsubishiHeavy152SwingHLeftMax); + + EXPECT_EQ( + "Power: On, Mode: 4 (Heat), Temp: 31C, Fan: 8 (Turbo), " + "Swing (V): 5 (Lowest), Swing (H): 1 (Max Left), Silent: Off, Turbo: On, " + "Econo: Off, Night: Off, Filter: On, 3D: Off, Clean: Off", + ac.toString()); + + ac.setClean(true); + ac.setEcono(true); + ac.setMode(kMitsubishiHeavyAuto); + ac.setSwingVertical(kMitsubishiHeavy152SwingVOff); + + EXPECT_EQ( + "Power: On, Mode: 0 (Auto), Temp: 31C, Fan: 6 (Econo), " + "Swing (V): 6 (Off), Swing (H): 1 (Max Left), Silent: Off, " + "Turbo: Off, Econo: On, Night: Off, Filter: On, 3D: Off, Clean: On", + ac.toString()); + + ac.setClean(false); + ac.setTemp(25); + ac.setEcono(false); + ac.setMode(kMitsubishiHeavyDry); + ac.setSwingHorizontal(kMitsubishiHeavy152SwingHLeftRight); + EXPECT_EQ( + "Power: On, Mode: 2 (Dry), Temp: 25C, Fan: 0 (Auto), " + "Swing (V): 6 (Off), Swing (H): 7 (Left Right), Silent: Off, " + "Turbo: Off, Econo: Off, Night: Off, Filter: Off, 3D: Off, Clean: Off", + ac.toString()); +} + +TEST(TestMitsubishiHeavy152AcClass, ReconstructKnownExample) { + IRMitsubishiHeavy152Ac ac(0); + + EXPECT_EQ( + "Power: Off, Mode: 0 (Auto), Temp: 17C, Fan: 0 (Auto), " + "Swing (V): 0 (Auto), Swing (H): 0 (Auto), Silent: Off, Turbo: Off, " + "Econo: Off, Night: Off, Filter: Off, 3D: Off, Clean: Off", + ac.toString()); + ac.on(); + ac.setMode(kMitsubishiHeavyHeat); + ac.setTemp(24); + ac.setFan(kMitsubishiHeavy152FanMax); + ac.setFilter(true); + ac.setNight(false); + ac.setTurbo(false); + ac.setSilent(false); + ac.setEcono(false); + ac.set3D(false); + ac.setClean(false); + ac.setSwingVertical(kMitsubishiHeavy152SwingVAuto); + ac.setSwingHorizontal(kMitsubishiHeavy152SwingHAuto); + EXPECT_EQ( + "Power: On, Mode: 4 (Heat), Temp: 24C, Fan: 4 (Max), " + "Swing (V): 0 (Auto), Swing (H): 0 (Auto), Silent: Off, Turbo: Off, " + "Econo: Off, Night: Off, Filter: Off, 3D: Off, Clean: Off", + ac.toString()); + + uint8_t expected[kMitsubishiHeavy152StateLength] = { + 0xAD, 0x51, 0x3C, 0xE5, 0x1A, 0x0C, 0xF3, 0x07, + 0xF8, 0x04, 0xFB, 0x00, 0xFF, 0x00, 0xFF, 0x00, + 0xFF, 0x80, 0x7F}; + EXPECT_STATE_EQ(expected, ac.getRaw(), kMitsubishiHeavy152Bits); +} + +// Tests for IRMitsubishiHeavy88Ac class. + +TEST(TestMitsubishiHeavy88AcClass, Power) { + IRMitsubishiHeavy88Ac ac(0); + ac.begin(); + + ac.on(); + EXPECT_TRUE(ac.getPower()); + + ac.off(); + EXPECT_FALSE(ac.getPower()); + + ac.setPower(true); + EXPECT_TRUE(ac.getPower()); + + ac.setPower(false); + EXPECT_FALSE(ac.getPower()); +} + +TEST(TestMitsubishiHeavy88AcClass, Temperature) { + IRMitsubishiHeavy88Ac ac(0); + ac.begin(); + + ac.setMode(kMitsubishiHeavyCool); + + ac.setTemp(0); + EXPECT_EQ(kMitsubishiHeavyMinTemp, ac.getTemp()); + + ac.setTemp(255); + EXPECT_EQ(kMitsubishiHeavyMaxTemp, ac.getTemp()); + + ac.setTemp(kMitsubishiHeavyMinTemp); + EXPECT_EQ(kMitsubishiHeavyMinTemp, ac.getTemp()); + + ac.setTemp(kMitsubishiHeavyMaxTemp); + EXPECT_EQ(kMitsubishiHeavyMaxTemp, ac.getTemp()); + + ac.setTemp(kMitsubishiHeavyMinTemp - 1); + EXPECT_EQ(kMitsubishiHeavyMinTemp, ac.getTemp()); + + ac.setTemp(kMitsubishiHeavyMaxTemp + 1); + EXPECT_EQ(kMitsubishiHeavyMaxTemp, ac.getTemp()); + + ac.setTemp(19); + EXPECT_EQ(19, ac.getTemp()); + + ac.setTemp(21); + EXPECT_EQ(21, ac.getTemp()); + + ac.setTemp(25); + EXPECT_EQ(25, ac.getTemp()); + + ac.setTemp(29); + EXPECT_EQ(29, ac.getTemp()); +} + +TEST(TestMitsubishiHeavy88AcClass, OperatingMode) { + IRMitsubishiHeavy88Ac ac(0); + ac.begin(); + + ac.setMode(kMitsubishiHeavyAuto); + EXPECT_EQ(kMitsubishiHeavyAuto, ac.getMode()); + + ac.setMode(kMitsubishiHeavyCool); + EXPECT_EQ(kMitsubishiHeavyCool, ac.getMode()); + + ac.setMode(kMitsubishiHeavyHeat); + EXPECT_EQ(kMitsubishiHeavyHeat, ac.getMode()); + + ac.setMode(kMitsubishiHeavyDry); + EXPECT_EQ(kMitsubishiHeavyDry, ac.getMode()); + + ac.setMode(kMitsubishiHeavyFan); + EXPECT_EQ(kMitsubishiHeavyFan, ac.getMode()); + + ac.setMode(kMitsubishiHeavyHeat + 1); + EXPECT_EQ(kMitsubishiHeavyAuto, ac.getMode()); + + ac.setMode(255); + EXPECT_EQ(kMitsubishiHeavyAuto, ac.getMode()); +} + +TEST(TestMitsubishiHeavy88AcClass, Turbo) { + IRMitsubishiHeavy88Ac ac(0); + ac.begin(); + + ac.setTurbo(true); + EXPECT_TRUE(ac.getTurbo()); + + ac.setTurbo(false); + EXPECT_FALSE(ac.getTurbo()); + + ac.setTurbo(true); + EXPECT_TRUE(ac.getTurbo()); +} + +TEST(TestMitsubishiHeavy88AcClass, Econo) { + IRMitsubishiHeavy88Ac ac(0); + ac.begin(); + + ac.setEcono(true); + EXPECT_TRUE(ac.getEcono()); + + ac.setEcono(false); + EXPECT_FALSE(ac.getEcono()); + + ac.setEcono(true); + EXPECT_TRUE(ac.getEcono()); +} + +TEST(TestMitsubishiHeavy88AcClass, 3D) { + IRMitsubishiHeavy88Ac ac(0); + ac.begin(); + + ac.set3D(true); + EXPECT_TRUE(ac.get3D()); + + ac.set3D(false); + EXPECT_FALSE(ac.get3D()); + + ac.set3D(true); + EXPECT_TRUE(ac.get3D()); +} + +TEST(TestMitsubishiHeavy88AcClass, Clean) { + IRMitsubishiHeavy88Ac ac(0); + ac.begin(); + + ac.setClean(true); + EXPECT_TRUE(ac.getClean()); + + ac.setClean(false); + EXPECT_FALSE(ac.getClean()); + + ac.setClean(true); + EXPECT_TRUE(ac.getClean()); +} + +TEST(TestMitsubishiHeavy88AcClass, FanSpeed) { + IRMitsubishiHeavy88Ac ac(0); + ac.begin(); + + ac.setFan(kMitsubishiHeavy88FanLow); + EXPECT_EQ(kMitsubishiHeavy88FanLow, ac.getFan()); + + ac.setFan(kMitsubishiHeavy88FanAuto); + EXPECT_EQ(kMitsubishiHeavy88FanAuto, ac.getFan()); + + + ac.setFan(255); + EXPECT_EQ(kMitsubishiHeavy88FanAuto, ac.getFan()); + + ac.setFan(kMitsubishiHeavy88FanHigh); + EXPECT_EQ(kMitsubishiHeavy88FanHigh, ac.getFan()); + + ac.setFan(kMitsubishiHeavy88FanHigh + 1); + EXPECT_EQ(kMitsubishiHeavy88FanAuto, ac.getFan()); + + ac.setFan(kMitsubishiHeavy88FanHigh - 1); + EXPECT_EQ(kMitsubishiHeavy88FanHigh - 1, ac.getFan()); + + ac.setFan(kMitsubishiHeavy88FanLow + 1); + EXPECT_EQ(kMitsubishiHeavy88FanLow + 1, ac.getFan()); + + ac.setFan(kMitsubishiHeavy88FanEcono); + EXPECT_EQ(kMitsubishiHeavy88FanEcono, ac.getFan()); + + ac.setFan(kMitsubishiHeavy88FanTurbo); + EXPECT_EQ(kMitsubishiHeavy88FanTurbo, ac.getFan()); +} + +TEST(TestMitsubishiHeavy88AcClass, VerticalSwing) { + IRMitsubishiHeavy88Ac ac(0); + ac.begin(); + ac.setSwingVertical(kMitsubishiHeavy88SwingVAuto); + EXPECT_EQ(kMitsubishiHeavy88SwingVAuto, ac.getSwingVertical()); + + ac.setSwingVertical(kMitsubishiHeavy88SwingVHighest); + EXPECT_EQ(kMitsubishiHeavy88SwingVHighest, ac.getSwingVertical()); + + ac.setSwingVertical(kMitsubishiHeavy88SwingVOff); + EXPECT_EQ(kMitsubishiHeavy88SwingVOff, ac.getSwingVertical()); + + ac.setSwingVertical(kMitsubishiHeavy88SwingVHighest + 1); + EXPECT_EQ(kMitsubishiHeavy88SwingVOff, ac.getSwingVertical()); + + ac.setSwingVertical(kMitsubishiHeavy88SwingVOff + 1); + EXPECT_EQ(kMitsubishiHeavy88SwingVOff, ac.getSwingVertical()); + + // Out of bounds. + ac.setSwingVertical(255); + EXPECT_EQ(kMitsubishiHeavy88SwingVOff, ac.getSwingVertical()); +} + +TEST(TestMitsubishiHeavy88AcClass, HorizontalSwing) { + IRMitsubishiHeavy88Ac ac(0); + ac.begin(); + ac.setSwingHorizontal(kMitsubishiHeavy88SwingHAuto); + EXPECT_EQ(kMitsubishiHeavy88SwingHAuto, ac.getSwingHorizontal()); + + ac.setSwingHorizontal(kMitsubishiHeavy88SwingHLeftMax); + EXPECT_EQ(kMitsubishiHeavy88SwingHLeftMax, ac.getSwingHorizontal()); + + ac.setSwingHorizontal(kMitsubishiHeavy88SwingHLeftMax + 1); + EXPECT_EQ(kMitsubishiHeavy88SwingHOff, ac.getSwingHorizontal()); + + ac.setSwingHorizontal(kMitsubishiHeavy88SwingHRightMax); + EXPECT_EQ(kMitsubishiHeavy88SwingHRightMax, ac.getSwingHorizontal()); + + ac.setSwingHorizontal(kMitsubishiHeavy88SwingHRightMax - 1); + EXPECT_EQ(kMitsubishiHeavy88SwingHOff, ac.getSwingHorizontal()); + + ac.setSwingHorizontal(kMitsubishiHeavy88SwingHOff); + EXPECT_EQ(kMitsubishiHeavy88SwingHOff, ac.getSwingHorizontal()); + + ac.setSwingHorizontal(kMitsubishiHeavy88SwingHOff + 1); + EXPECT_EQ(kMitsubishiHeavy88SwingHOff, ac.getSwingHorizontal()); + + // Out of bounds. + ac.setSwingHorizontal(255); + EXPECT_EQ(kMitsubishiHeavy88SwingHOff, ac.getSwingHorizontal()); +} + +TEST(TestMitsubishiHeavy88AcClass, Checksums) { + IRMitsubishiHeavy88Ac ac(0); + ac.begin(); + + EXPECT_TRUE(ac.validChecksum(ac.getRaw())); + + uint8_t expected[kMitsubishiHeavy88StateLength] = { + 0xAD, 0x51, 0x3C, 0xD9, 0x26, 0x48, 0xB7, 0x00, 0xFF, 0x8A, 0x75}; + EXPECT_TRUE(IRMitsubishiHeavy88Ac::validChecksum(expected)); + + // Screw up the "checksum" to test it fails. + expected[kMitsubishiHeavy88StateLength - 1] = 0x55; + EXPECT_FALSE(IRMitsubishiHeavy88Ac::validChecksum(expected)); + // getting the after getRaw() should repair it. + ac.setRaw(expected); + EXPECT_TRUE(ac.validChecksum(ac.getRaw())); + EXPECT_TRUE(IRMitsubishiHeavy88Ac::validChecksum(ac.getRaw())); +} + +TEST(TestMitsubishiHeavy88AcClass, HumanReadable) { + IRMitsubishiHeavy88Ac ac(0); + + EXPECT_EQ( + "Power: Off, Mode: 0 (Auto), Temp: 17C, Fan: 0 (Auto), " + "Swing (V): 0 (Off), Swing (H): 0 (Off), " + "Turbo: Off, Econo: Off, 3D: Off, Clean: Off", + ac.toString()); + ac.on(); + ac.setMode(kMitsubishiHeavyCool); + ac.setTemp(kMitsubishiHeavyMinTemp); + ac.setFan(kMitsubishiHeavy88FanHigh); + ac.setTurbo(false); + ac.setEcono(false); + ac.set3D(true); + ac.setSwingVertical(kMitsubishiHeavy88SwingVAuto); + EXPECT_EQ( + "Power: On, Mode: 1 (Cool), Temp: 17C, Fan: 4 (High), " + "Swing (V): 16 (Auto), Swing (H): 200 (3D), " + "Turbo: Off, Econo: Off, 3D: On, Clean: Off", + ac.toString()); + + ac.setMode(kMitsubishiHeavyHeat); + ac.setTemp(kMitsubishiHeavyMaxTemp); + ac.setTurbo(true); + ac.setEcono(false); + ac.set3D(false); + ac.setSwingVertical(kMitsubishiHeavy88SwingVLowest); + ac.setSwingHorizontal(kMitsubishiHeavy88SwingHLeftMax); + + EXPECT_EQ( + "Power: On, Mode: 4 (Heat), Temp: 31C, Fan: 6 (Turbo), " + "Swing (V): 26 (Lowest), Swing (H): 4 (Max Left), Turbo: On, Econo: Off, " + "3D: Off, Clean: Off", + ac.toString()); + + ac.setClean(true); + ac.setEcono(true); + ac.setMode(kMitsubishiHeavyAuto); + ac.setSwingVertical(kMitsubishiHeavy88SwingVOff); + + EXPECT_EQ( + "Power: On, Mode: 0 (Auto), Temp: 31C, Fan: 7 (Econo), " + "Swing (V): 0 (Off), Swing (H): 4 (Max Left), Turbo: Off, Econo: On, " + "3D: Off, Clean: On", + ac.toString()); + + ac.setClean(false); + ac.setTemp(25); + ac.setEcono(false); + ac.setMode(kMitsubishiHeavyDry); + ac.setSwingHorizontal(kMitsubishiHeavy88SwingHLeftRight); + EXPECT_EQ( + "Power: On, Mode: 2 (Dry), Temp: 25C, Fan: 0 (Auto), " + "Swing (V): 0 (Off), Swing (H): 72 (Left Right), Turbo: Off, Econo: Off, " + "3D: Off, Clean: Off", + ac.toString()); +} + +// Tests for decodeMitsubishiHeavy(). + +// Decode a real MitsubishiHeavy 152Bit message. +TEST(TestDecodeMitsubishiHeavy, ZmsRealExample) { + IRsendTest irsend(0); + IRrecv irrecv(0); + IRMitsubishiHeavy152Ac ac(0); + irsend.begin(); + + uint8_t expected[kMitsubishiHeavy152StateLength] = { + 0xAD, 0x51, 0x3C, 0xE5, 0x1A, 0x0C, 0xF3, 0x07, + 0xF8, 0x04, 0xFB, 0x00, 0xFF, 0x00, 0xFF, 0x00, + 0xFF, 0x80, 0x7F}; + + // Ref: https://github.com/markszabo/IRremoteESP8266/issues/660#issuecomment-480571466 + uint16_t rawData[307] = { + 3136, 1638, 364, 428, 366, 1224, 362, 432, 364, 430, 364, 1226, 362, 432, + 364, 1224, 366, 428, 366, 430, 366, 1224, 362, 1228, 362, 1228, 362, 432, + 364, 1224, 364, 432, 364, 1226, 364, 1224, 366, 1226, 364, 428, 364, 430, + 364, 430, 364, 432, 366, 1226, 364, 1224, 364, 430, 364, 1226, 364, 428, + 364, 1224, 368, 1224, 364, 428, 364, 430, 366, 430, 364, 1158, 430, 432, + 366, 1222, 366, 430, 366, 430, 364, 1226, 364, 1224, 364, 1224, 364, 1224, + 366, 1224, 364, 430, 364, 430, 364, 1228, 362, 1226, 364, 1226, 366, 1222, + 366, 430, 364, 430, 364, 1224, 366, 1224, 364, 430, 364, 430, 364, 432, + 364, 430, 364, 428, 364, 430, 364, 430, 366, 1226, 362, 1154, 434, 1228, + 364, 1226, 362, 1226, 364, 1226, 364, 1228, 362, 1226, 362, 432, 364, 430, + 364, 428, 364, 430, 364, 430, 364, 1228, 362, 1228, 362, 432, 364, 1224, + 368, 1224, 364, 1226, 362, 1226, 364, 1226, 366, 428, 366, 430, 364, 1224, + 364, 430, 366, 430, 366, 430, 364, 430, 364, 430, 364, 1226, 364, 1226, + 366, 1224, 366, 1224, 366, 1226, 364, 1224, 366, 1224, 366, 1224, 366, + 428, 364, 430, 366, 428, 364, 430, 364, 430, 366, 428, 364, 430, 364, 432, + 364, 1226, 364, 1226, 364, 1226, 364, 1228, 364, 1222, 370, 1222, 362, + 1228, 362, 1226, 362, 430, 364, 430, 364, 430, 364, 432, 364, 428, 364, + 432, 364, 428, 364, 430, 366, 1226, 362, 1224, 364, 1226, 364, 1226, 364, + 1226, 362, 1226, 366, 1224, 366, 1224, 364, 430, 364, 432, 364, 428, 364, + 432, 364, 428, 364, 430, 366, 430, 364, 430, 364, 1226, 362, 1226, 364, + 1224, 366, 1226, 362, 1228, 364, 1224, 366, 1224, 364, 430, 364, 432, 364, + 428, 364, 430, 364, 430, 364, 430, 366, 430, 364, 430, 338, 1252, 362 + }; // UNKNOWN 5138D49D + + irsend.reset(); + irsend.sendRaw(rawData, 307, 38000); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(MITSUBISHI_HEAVY_152, irsend.capture.decode_type); + ASSERT_EQ(kMitsubishiHeavy152Bits, irsend.capture.bits); + EXPECT_STATE_EQ(expected, irsend.capture.state, irsend.capture.bits); + ac.setRaw(irsend.capture.state); + EXPECT_EQ( + "Power: On, Mode: 4 (Heat), Temp: 24C, Fan: 4 (Max), " + "Swing (V): 0 (Auto), Swing (H): 0 (Auto), Silent: Off, Turbo: Off, " + "Econo: Off, Night: Off, Filter: Off, 3D: Off, Clean: Off", + ac.toString()); +} + +// Decode a Synthetic MitsubishiHeavy 152Bit message. +TEST(TestDecodeMitsubishiHeavy, ZmsSyntheticExample) { + IRsendTest irsend(0); + IRrecv irrecv(0); + IRMitsubishiHeavy152Ac ac(0); + irsend.begin(); + + uint8_t expected[kMitsubishiHeavy152StateLength] = { + 0xAD, 0x51, 0x3C, 0xE5, 0x1A, 0x0C, 0xF3, 0x07, + 0xF8, 0x04, 0xFB, 0x00, 0xFF, 0x00, 0xFF, 0x00, + 0xFF, 0x80, 0x7F}; + + irsend.reset(); + irsend.sendMitsubishiHeavy152(expected); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(MITSUBISHI_HEAVY_152, irsend.capture.decode_type); + ASSERT_EQ(kMitsubishiHeavy152Bits, irsend.capture.bits); + EXPECT_STATE_EQ(expected, irsend.capture.state, irsend.capture.bits); + ac.setRaw(irsend.capture.state); + EXPECT_EQ( + "Power: On, Mode: 4 (Heat), Temp: 24C, Fan: 4 (Max), " + "Swing (V): 0 (Auto), Swing (H): 0 (Auto), Silent: Off, Turbo: Off, " + "Econo: Off, Night: Off, Filter: Off, 3D: Off, Clean: Off", + ac.toString()); +} + +// Decode a real MitsubishiHeavy 152Bit message. +TEST(TestDecodeMitsubishiHeavy, ZmsRealExample2) { + IRsendTest irsend(0); + IRrecv irrecv(0); + IRMitsubishiHeavy152Ac ac(0); + irsend.begin(); + + uint8_t expected[kMitsubishiHeavy152StateLength] = { + 0xAD, 0x51, 0x3C, 0xE5, 0x1A, 0x04, 0xFB, 0x07, + 0xF8, 0x04, 0xFB, 0x00, 0xFF, 0x00, 0xFF, 0x00, + 0xFF, 0x80, 0x7F}; + + // Ref: https://github.com/markszabo/IRremoteESP8266/issues/660#issuecomment-480571466 + uint16_t rawData[307] = { + 3196, 1580, 398, 390, 404, 1190, 400, 390, 402, 390, 402, 1192, 402, 388, + 402, 1192, 400, 390, 402, 392, 402, 1192, 400, 1188, 400, 1188, 400, 390, + 404, 1192, 398, 392, 400, 1192, 402, 1188, 400, 1190, 402, 388, 402, 392, + 404, 392, 402, 392, 404, 1188, 400, 1190, 398, 392, 404, 1188, 398, 392, + 402, 1192, 398, 1190, 400, 390, 404, 390, 402, 392, 404, 1188, 398, 392, + 404, 1190, 400, 392, 400, 394, 402, 1192, 398, 1190, 398, 1192, 398, 1190, + 400, 1190, 398, 392, 402, 1192, 398, 1190, 398, 1190, 398, 1192, 396, + 1192, 398, 396, 400, 394, 398, 1194, 396, 394, 400, 394, 398, 396, 398, + 396, 400, 402, 390, 394, 402, 392, 398, 396, 398, 1194, 396, 1194, 398, + 1192, 398, 1192, 396, 1194, 396, 1192, 396, 1196, 398, 1190, 398, 392, + 402, 392, 402, 394, 398, 394, 400, 394, 400, 1192, 398, 1192, 400, 390, + 402, 1190, 398, 1190, 398, 1192, 402, 1188, 398, 1190, 400, 390, 402, 392, + 402, 1190, 400, 390, 404, 390, 402, 394, 402, 392, 402, 390, 404, 1190, + 400, 1188, 400, 1190, 400, 1190, 402, 1188, 402, 1188, 400, 1188, 402, + 1190, 400, 388, 402, 394, 404, 392, 404, 388, 404, 390, 404, 392, 402, + 394, 402, 390, 402, 1190, 402, 1186, 402, 1190, 400, 1190, 398, 1190, 402, + 1186, 402, 1190, 400, 1188, 400, 390, 404, 392, 404, 390, 402, 392, 402, + 392, 400, 394, 402, 392, 402, 394, 400, 1192, 400, 1190, 400, 1188, 400, + 1192, 400, 1186, 402, 1190, 400, 1190, 400, 1188, 402, 388, 402, 390, 404, + 392, 402, 392, 402, 392, 402, 392, 404, 392, 402, 392, 404, 1190, 400, + 1190, 398, 1190, 400, 1190, 400, 1190, 400, 1188, 400, 1188, 400, 392, + 402, 392, 404, 390, 402, 392, 402, 392, 402, 392, 402, 390, 402, 392, 402, + 1192, 398}; // UNKNOWN A650F2C1 + + irsend.reset(); + irsend.sendRaw(rawData, 307, 38000); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(MITSUBISHI_HEAVY_152, irsend.capture.decode_type); + ASSERT_EQ(kMitsubishiHeavy152Bits, irsend.capture.bits); + EXPECT_STATE_EQ(expected, irsend.capture.state, irsend.capture.bits); + ac.setRaw(irsend.capture.state); + EXPECT_EQ( + "Power: Off, Mode: 4 (Heat), Temp: 24C, Fan: 4 (Max), " + "Swing (V): 0 (Auto), Swing (H): 0 (Auto), Silent: Off, Turbo: Off, " + "Econo: Off, Night: Off, Filter: Off, 3D: Off, Clean: Off", + ac.toString()); +} + +// Decode a Synthetic MitsubishiHeavy 88 Bit message. +TEST(TestDecodeMitsubishiHeavy, ZjsSyntheticExample) { + IRsendTest irsend(0); + IRrecv irrecv(0); + IRMitsubishiHeavy88Ac ac(0); + irsend.begin(); + + uint8_t expected[kMitsubishiHeavy88StateLength] = { + 0xAD, 0x51, 0x3C, 0xD9, 0x26, 0x48, 0xB7, 0x00, 0xFF, 0x8A, 0x75}; + + irsend.reset(); + irsend.sendMitsubishiHeavy88(expected); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(MITSUBISHI_HEAVY_88, irsend.capture.decode_type); + ASSERT_EQ(kMitsubishiHeavy88Bits, irsend.capture.bits); + EXPECT_STATE_EQ(expected, irsend.capture.state, irsend.capture.bits); + ac.setRaw(irsend.capture.state); + EXPECT_EQ( + "Power: On, Mode: 2 (Dry), Temp: 25C, Fan: 0 (Auto), " + "Swing (V): 0 (Off), Swing (H): 72 (Left Right), Turbo: Off, Econo: Off, " + "3D: Off, Clean: Off", + ac.toString()); +} diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_Mitsubishi_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Mitsubishi_test.cpp similarity index 99% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_Mitsubishi_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_Mitsubishi_test.cpp index 7b8eb2192..6c9480b31 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_Mitsubishi_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Mitsubishi_test.cpp @@ -17,6 +17,7 @@ TEST(TestSendMitsubishi, SendDataOnly) { irsend.reset(); irsend.sendMitsubishi(0xE242); EXPECT_EQ( + "f33000d50" "m300s2100m300s2100m300s2100m300s900m300s900m300s900m300s2100m300s900" "m300s900m300s2100m300s900m300s900m300s900m300s900m300s2100m300s900" "m300s28080" @@ -28,6 +29,7 @@ TEST(TestSendMitsubishi, SendDataOnly) { irsend.reset(); irsend.sendMitsubishi(0x0); EXPECT_EQ( + "f33000d50" "m300s900m300s900m300s900m300s900m300s900m300s900m300s900m300s900" "m300s900m300s900m300s900m300s900m300s900m300s900m300s900m300s900" "m300s34080" @@ -39,6 +41,7 @@ TEST(TestSendMitsubishi, SendDataOnly) { irsend.reset(); irsend.sendMitsubishi(0x4321); EXPECT_EQ( + "f33000d50" "m300s900m300s2100m300s900m300s900m300s900m300s900m300s2100m300s2100" "m300s900m300s900m300s2100m300s900m300s900m300s900m300s900m300s2100" "m300s28080" @@ -56,6 +59,7 @@ TEST(TestSendMitsubishi, SendWithRepeats) { irsend.reset(); irsend.sendMitsubishi(0xE242, kMitsubishiBits, 0); // 0 repeat. EXPECT_EQ( + "f33000d50" "m300s2100m300s2100m300s2100m300s900m300s900m300s900m300s2100m300s900" "m300s900m300s2100m300s900m300s900m300s900m300s900m300s2100m300s900" "m300s28080", @@ -64,6 +68,7 @@ TEST(TestSendMitsubishi, SendWithRepeats) { irsend.reset(); irsend.sendMitsubishi(0xE242, kMitsubishiBits, 1); // 1 repeat. EXPECT_EQ( + "f33000d50" "m300s2100m300s2100m300s2100m300s900m300s900m300s900m300s2100m300s900" "m300s900m300s2100m300s900m300s900m300s900m300s900m300s2100m300s900" "m300s28080" @@ -73,6 +78,7 @@ TEST(TestSendMitsubishi, SendWithRepeats) { irsend.outputStr()); irsend.sendMitsubishi(0xE242, kMitsubishiBits, 2); // 2 repeats. EXPECT_EQ( + "f33000d50" "m300s2100m300s2100m300s2100m300s900m300s900m300s900m300s2100m300s900" "m300s900m300s2100m300s900m300s900m300s900m300s900m300s2100m300s900" "m300s28080" @@ -93,6 +99,7 @@ TEST(TestSendMitsubishi, SendUnusualSize) { irsend.reset(); irsend.sendMitsubishi(0x0, 8); EXPECT_EQ( + "f33000d50" "m300s900m300s900m300s900m300s900m300s900m300s900m300s900m300s900" "m300s43680" "m300s900m300s900m300s900m300s900m300s900m300s900m300s900m300s900" @@ -102,6 +109,7 @@ TEST(TestSendMitsubishi, SendUnusualSize) { irsend.reset(); irsend.sendMitsubishi(0x1234567890ABCDEF, 64); EXPECT_EQ( + "f33000d50" "m300s900m300s900m300s900m300s2100m300s900m300s900m300s2100m300s900" "m300s900m300s900m300s2100m300s2100m300s900m300s2100m300s900m300s900" "m300s900m300s2100m300s900m300s2100m300s900m300s2100m300s2100m300s900" @@ -305,6 +313,7 @@ TEST(TestSendMitsubishiAC, SendDataOnly) { irsend.reset(); irsend.sendMitsubishiAC(mitsub_code); EXPECT_EQ( + "f38000d50" "m3400s1750" "m450s1300m450s1300m450s420m450s420m450s420m450s1300m450s420m450s420" "m450s1300m450s1300m450s420m450s1300m450s420m450s420m450s1300m450s1300" @@ -360,6 +369,7 @@ TEST(TestSendMitsubishiAC, SendWithRepeats) { irsend.sendMitsubishiAC(mitsub_code, kMitsubishiACStateLength, 0); EXPECT_EQ( + "f38000d50" "m3400s1750" "m450s1300m450s1300m450s420m450s420m450s420m450s1300m450s420m450s420" "m450s1300m450s1300m450s420m450s1300m450s420m450s420m450s1300m450s1300" @@ -385,6 +395,7 @@ TEST(TestSendMitsubishiAC, SendWithRepeats) { irsend.reset(); irsend.sendMitsubishiAC(mitsub_code, kMitsubishiACStateLength, 2); EXPECT_EQ( + "f38000d50" "m3400s1750" "m450s1300m450s1300m450s420m450s420m450s420m450s1300m450s420m450s420" "m450s1300m450s1300m450s420m450s1300m450s420m450s420m450s1300m450s1300" @@ -466,6 +477,7 @@ TEST(TestSendMitsubishiAC, SendUnexpectedSizes) { irsend.reset(); irsend.sendMitsubishiAC(mitsub_long_code, 19); ASSERT_EQ( + "f38000d50" "m3400s1750" "m450s1300m450s1300m450s420m450s420m450s420m450s1300m450s420m450s420" "m450s1300m450s1300m450s420m450s1300m450s420m450s420m450s1300m450s1300" @@ -665,6 +677,7 @@ TEST(TestMitsubishiACClass, MessageConstuction) { irsend.reset(); irsend.sendMitsubishiAC(mitsub.getRaw()); EXPECT_EQ( + "f38000d50" "m3400s1750" "m450s1300m450s1300m450s420m450s420m450s420m450s1300m450s420m450s420" "m450s1300m450s1300m450s420m450s1300m450s420m450s420m450s1300m450s1300" @@ -984,6 +997,7 @@ TEST(TestSendMitsubishi2, SendDataOnly) { irsend.reset(); irsend.sendMitsubishi2(0xF82); EXPECT_EQ( + "f33000d50" "m8400s4200" "m560s520m560s520m560s520m560s520m560s1560m560s1560m560s1560m560s1560" "m560s4200" @@ -999,6 +1013,7 @@ TEST(TestSendMitsubishi2, SendDataOnly) { irsend.reset(); irsend.sendMitsubishi2(0x0); EXPECT_EQ( + "f33000d50" "m8400s4200" "m560s520m560s520m560s520m560s520m560s520m560s520m560s520m560s520" "m560s4200" @@ -1020,6 +1035,7 @@ TEST(TestSendMitsubishi2, Repeats) { irsend.reset(); irsend.sendMitsubishi2(0xF82, kMitsubishiBits, 0); EXPECT_EQ( + "f33000d50" "m8400s4200" "m560s520m560s520m560s520m560s520m560s1560m560s1560m560s1560m560s1560" "m560s4200" @@ -1030,6 +1046,7 @@ TEST(TestSendMitsubishi2, Repeats) { irsend.reset(); irsend.sendMitsubishi2(0xF82, kMitsubishiBits, 2); EXPECT_EQ( + "f33000d50" "m8400s4200" "m560s520m560s520m560s520m560s520m560s1560m560s1560m560s1560m560s1560" "m560s4200" diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_NEC_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_NEC_test.cpp similarity index 98% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_NEC_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_NEC_test.cpp index 6b84b0ec9..c881b7b44 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_NEC_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_NEC_test.cpp @@ -12,6 +12,7 @@ TEST(TestSendNEC, SendDataOnly) { irsend.begin(); irsend.sendNEC(0); EXPECT_EQ( + "f38000d33" "m8960s4480m560s560m560s560m560s560m560s560m560s560m560s560m560s560" "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" @@ -20,6 +21,7 @@ TEST(TestSendNEC, SendDataOnly) { irsend.outputStr()); irsend.sendNEC(0xAA00FF55); EXPECT_EQ( + "f38000d33" "m8960s4480m560s1680m560s560m560s1680m560s560m560s1680m560s560" "m560s1680m560s560m560s560m560s560m560s560m560s560m560s560m560s560" "m560s560m560s560m560s1680m560s1680m560s1680m560s1680m560s1680" @@ -33,15 +35,17 @@ TEST(TestSendNEC, SendSmallData) { IRsendTest irsend(4); irsend.begin(); irsend.sendNEC(0xA, 4); // Send only 4 data bits. - EXPECT_EQ("m8960s4480m560s1680m560s560m560s1680m560s560m560s87360", + EXPECT_EQ("f38000d33m8960s4480m560s1680m560s560m560s1680m560s560m560s87360", irsend.outputStr()); irsend.sendNEC(0, 8); // Send only 8 data bits. EXPECT_EQ( + "f38000d33" "m8960s4480m560s560m560s560m560s560m560s560m560s560m560s560m560s560" "m560s560m560s85120", irsend.outputStr()); irsend.sendNEC(0x1234567890ABCDEF, 64); // Send 64 data bits. EXPECT_EQ( + "f38000d33" "m8960s4480m560s560m560s560m560s560m560s1680m560s560m560s560" "m560s1680m560s560m560s560m560s560m560s1680m560s1680m560s560" "m560s1680m560s560m560s560m560s560m560s1680m560s560m560s1680" @@ -61,17 +65,20 @@ TEST(TestSendNEC, SendWithRepeats) { irsend.begin(); irsend.sendNEC(0, 8, 0); // Send a command with 0 repeats. EXPECT_EQ( + "f38000d33" "m8960s4480m560s560m560s560m560s560m560s560m560s560m560s560m560s560" "m560s560m560s85120", irsend.outputStr()); irsend.sendNEC(0xAA, 8, 1); // Send a command with 1 repeat. EXPECT_EQ( + "f38000d33" "m8960s4480m560s1680m560s560m560s1680m560s560m560s1680m560s560" "m560s1680m560s560m560s80640" "m8960s2240m560s96320", irsend.outputStr()); irsend.sendNEC(0xAA, 8, 3); // Send a command with 3 repeats. EXPECT_EQ( + "f38000d33" "m8960s4480m560s1680m560s560m560s1680m560s560m560s1680m560s560" "m560s1680m560s560m560s80640" "m8960s2240m560s96320" diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_Nikai_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Nikai_test.cpp similarity index 99% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_Nikai_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_Nikai_test.cpp index 4a4ea05bb..fff242326 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_Nikai_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Nikai_test.cpp @@ -13,6 +13,7 @@ TEST(TestSendNikai, SendDataOnly) { irsend.reset(); irsend.sendNikai(0xD5F2A); // Nikai TV Power Off. EXPECT_EQ( + "f38000d33" "m4000s4000" "m500s2000m500s2000m500s2000m500s2000m500s1000m500s1000m500s2000" "m500s1000m500s2000m500s1000m500s2000m500s1000m500s1000m500s1000" @@ -31,6 +32,7 @@ TEST(TestSendNikai, SendWithRepeats) { irsend.reset(); irsend.sendNikai(0xD5F2A, kNikaiBits, 1); // 1 repeat. EXPECT_EQ( + "f38000d33" "m4000s4000" "m500s2000m500s2000m500s2000m500s2000m500s1000m500s1000m500s2000" "m500s1000m500s2000m500s1000m500s2000m500s1000m500s1000m500s1000" @@ -44,6 +46,7 @@ TEST(TestSendNikai, SendWithRepeats) { irsend.outputStr()); irsend.sendNikai(0xD5F2A, kNikaiBits, 2); // 2 repeat. EXPECT_EQ( + "f38000d33" "m4000s4000" "m500s2000m500s2000m500s2000m500s2000m500s1000m500s1000m500s2000" "m500s1000m500s2000m500s1000m500s2000m500s1000m500s1000m500s1000" diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_Panasonic_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Panasonic_test.cpp similarity index 99% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_Panasonic_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_Panasonic_test.cpp index a1d8a7979..4d10f8fb1 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_Panasonic_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Panasonic_test.cpp @@ -32,6 +32,7 @@ TEST(TestSendPanasonic64, SendDataOnly) { irsend.reset(); irsend.sendPanasonic64(0x0); EXPECT_EQ( + "f36700d50" "m3456s1728" "m432s432m432s432m432s432m432s432m432s432m432s432m432s432m432s432" "m432s432m432s432m432s432m432s432m432s432m432s432m432s432m432s432" @@ -45,6 +46,7 @@ TEST(TestSendPanasonic64, SendDataOnly) { irsend.reset(); irsend.sendPanasonic64(0x40040190ED7C); EXPECT_EQ( + "f36700d50" "m3456s1728" "m432s432m432s1296m432s432m432s432m432s432m432s432m432s432m432s432" "m432s432m432s432m432s432m432s432m432s432m432s1296m432s432m432s432" @@ -58,6 +60,7 @@ TEST(TestSendPanasonic64, SendDataOnly) { irsend.reset(); irsend.sendPanasonic64(0xFFFFFFFFFFFF); EXPECT_EQ( + "f36700d50" "m3456s1728" "m432s1296m432s1296m432s1296m432s1296m432s1296m432s1296m432s1296m432s1296" "m432s1296m432s1296m432s1296m432s1296m432s1296m432s1296m432s1296m432s1296" @@ -77,6 +80,7 @@ TEST(TestSendPanasonic64, SendWithRepeats) { irsend.reset(); irsend.sendPanasonic64(0x40040190ED7C, kPanasonicBits, 0); // 0 repeats. EXPECT_EQ( + "f36700d50" "m3456s1728" "m432s432m432s1296m432s432m432s432m432s432m432s432m432s432m432s432" "m432s432m432s432m432s432m432s432m432s432m432s1296m432s432m432s432" @@ -90,6 +94,7 @@ TEST(TestSendPanasonic64, SendWithRepeats) { irsend.reset(); irsend.sendPanasonic64(0x40040190ED7C, kPanasonicBits, 1); // 1 repeat. EXPECT_EQ( + "f36700d50" "m3456s1728" "m432s432m432s1296m432s432m432s432m432s432m432s432m432s432m432s432" "m432s432m432s432m432s432m432s432m432s432m432s1296m432s432m432s432" @@ -110,6 +115,7 @@ TEST(TestSendPanasonic64, SendWithRepeats) { irsend.sendPanasonic64(0x40040190ED7C, kPanasonicBits, 2); // 2 repeats. EXPECT_EQ( + "f36700d50" "m3456s1728" "m432s432m432s1296m432s432m432s432m432s432m432s432m432s432m432s432" "m432s432m432s432m432s432m432s432m432s432m432s1296m432s432m432s432" @@ -145,6 +151,7 @@ TEST(TestSendPanasonic64, SendUnusualSize) { irsend.reset(); irsend.sendPanasonic64(0x0, 8); EXPECT_EQ( + "f36700d50" "m3456s1728" "m432s432m432s432m432s432m432s432m432s432m432s432m432s432m432s432" "m432s150768", @@ -153,6 +160,7 @@ TEST(TestSendPanasonic64, SendUnusualSize) { irsend.reset(); irsend.sendPanasonic64(0x1234567890ABCDEF, 64); EXPECT_EQ( + "f36700d50" "m3456s1728" "m432s432m432s432m432s432m432s1296m432s432m432s432m432s1296m432s432" "m432s432m432s432m432s1296m432s1296m432s432m432s1296m432s432m432s432" @@ -483,6 +491,7 @@ TEST(TestSendPanasonicAC, SendDataOnly) { 0x00, 0x06, 0x60, 0x00, 0x00, 0x80, 0x00, 0x06, 0x83}; irsend.sendPanasonicAC(state); EXPECT_EQ( + "f36700d50" "m3456s1728" "m432s432m432s1296m432s432m432s432m432s432m432s432m432s432m432s432" "m432s432m432s432m432s432m432s432m432s432m432s1296m432s432m432s432" diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_Pioneer_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Pioneer_test.cpp similarity index 99% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_Pioneer_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_Pioneer_test.cpp index b78469add..36d61c706 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_Pioneer_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Pioneer_test.cpp @@ -13,6 +13,7 @@ TEST(TestSendPioneer, SendDataOnly) { irsend.begin(); irsend.sendPioneer(0); EXPECT_EQ( + "f38000d33" "m8960s4480" "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" @@ -28,6 +29,7 @@ TEST(TestSendPioneer, SendDataOnly) { irsend.outputStr()); irsend.sendPioneer(0x55FF00AAAA00FF55); EXPECT_EQ( + "f38000d33" "m8960s4480" "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" @@ -136,6 +138,7 @@ TEST(TestDecodePioneer, SyntheticPioneerMessage) { irsend.reset(); irsend.sendPioneer(0x659A857AF50A3DC2, 64, 0); EXPECT_EQ( + "f38000d33" "m8960s4480" "m560s560m560s1680m560s1680m560s560m560s560m560s1680m560s560m560s1680" "m560s1680m560s560m560s560m560s1680m560s1680m560s560m560s1680m560s560" diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_Pronto_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Pronto_test.cpp similarity index 96% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_Pronto_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_Pronto_test.cpp index e52c6dd90..513f985da 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_Pronto_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Pronto_test.cpp @@ -58,7 +58,8 @@ TEST(TestSendPronto, MoreDataThanNeededInNormal) { uint16_t pronto_test[8] = {0x0000, 0x0067, 0x0001, 0x0000, 0x0001, 0x0002, 0x0003, 0x0004}; irsend.sendPronto(pronto_test, 8); - EXPECT_EQ("m25s50", irsend.outputStr()); // Only send the data required. + EXPECT_EQ("f40244d50m25s50", + irsend.outputStr()); // Only send the data required. } TEST(TestSendPronto, MoreDataThanNeededInRepeat) { @@ -70,7 +71,8 @@ TEST(TestSendPronto, MoreDataThanNeededInRepeat) { uint16_t pronto_test[8] = {0x0000, 0x0067, 0x0000, 0x0001, 0x0001, 0x0002, 0x0003, 0x0004}; irsend.sendPronto(pronto_test, 8); - EXPECT_EQ("m25s50", irsend.outputStr()); // Only send the data required. + EXPECT_EQ("f40244d50m25s50", + irsend.outputStr()); // Only send the data required. } TEST(TestSendPronto, MoreDataThanNeededInBoth) { @@ -82,9 +84,11 @@ TEST(TestSendPronto, MoreDataThanNeededInBoth) { uint16_t pronto_test[10] = {0x0000, 0x0067, 0x0001, 0x0001, 0x0001, 0x0002, 0x0003, 0x0004, 0x5, 0x6}; irsend.sendPronto(pronto_test, 10); - EXPECT_EQ("m25s50", irsend.outputStr()); // Only send the data required. + EXPECT_EQ("f40244d50m25s50", + irsend.outputStr()); // Only send the data required. irsend.sendPronto(pronto_test, 10, 1); - EXPECT_EQ("m25s50m75s100", irsend.outputStr()); // Only the data required. + EXPECT_EQ("f40244d50m25s50m75s100", + irsend.outputStr()); // Only the data required. } TEST(TestSendPronto, ShortestValidCodeThatSendsNothing) { @@ -134,6 +138,7 @@ TEST(TestSendPronto, NonRepeatingCode) { EXPECT_EQ(0x1, irsend.capture.address); EXPECT_EQ(0x0, irsend.capture.command); EXPECT_EQ( + "f40244d50" "m2400s600" "m600s600m600s600m600s600m600s600m600s600m600s600m600s600m1200s600" "m600s600m600s600m600s600m600s27650" @@ -160,6 +165,7 @@ TEST(TestSendPronto, NonRepeatingCode) { EXPECT_EQ(0x1, irsend.capture.address); EXPECT_EQ(0x0, irsend.capture.command); EXPECT_EQ( + "f40244d50" "m2400s600" "m600s600m600s600m600s600m600s600m600s600m600s600m600s600m1200s600" "m600s600m600s600m600s600m600s27650" @@ -201,6 +207,7 @@ TEST(TestSendPronto, RepeatSequenceOnlyForSony) { EXPECT_EQ(0x1A, irsend.capture.address); EXPECT_EQ(0x24AE, irsend.capture.command); EXPECT_EQ( + "f40244d50" "m2400s600" "m600s600m1200s600m1200s600m1200s600m600s600m1200s600m600s600m600s600" "m1200s600m600s600m1200s600m1200s600m1200s600m600s600m600s600m1200s600" @@ -218,6 +225,7 @@ TEST(TestSendPronto, RepeatSequenceOnlyForSony) { EXPECT_EQ(0x1A, irsend.capture.address); EXPECT_EQ(0x24AE, irsend.capture.command); EXPECT_EQ( + "f40244d50" "m2400s600" "m600s600m1200s600m1200s600m1200s600m600s600m1200s600m600s600m600s600" "m1200s600m600s600m1200s600m1200s600m1200s600m600s600m600s600m1200s600" @@ -265,6 +273,7 @@ TEST(TestSendPronto, RepeatSequenceOnlyForPanasonic) { EXPECT_EQ(0x4004, irsend.capture.address); EXPECT_EQ(0x1007C7D, irsend.capture.command); EXPECT_EQ( + "f36682d50" "m3456s1701" "m432s432m432s1296m432s432m432s432m432s432m432s432m432s432m432s432" "m432s432m432s432m432s432m432s432m432s432m432s1296m432s432m432s432" @@ -305,6 +314,7 @@ TEST(TestSendPronto, NormalPlusRepeatSequence) { EXPECT_EQ(0x18, irsend.capture.address); EXPECT_EQ(0x8, irsend.capture.command); EXPECT_EQ( + "f38028d50" "m8892s4446" "m546s546m546s546m546s546m546s1664m546s1664m546s546m546s546m546s546" "m546s1664m546s1664m546s1664m546s546m546s546m546s1664m546s1664m546s1664" @@ -324,6 +334,7 @@ TEST(TestSendPronto, NormalPlusRepeatSequence) { EXPECT_EQ(0x18, irsend.capture.address); EXPECT_EQ(0x8, irsend.capture.command); EXPECT_EQ( + "f38028d50" "m8892s4446" "m546s546m546s546m546s546m546s1664m546s1664m546s546m546s546m546s546" "m546s1664m546s1664m546s1664m546s546m546s546m546s1664m546s1664m546s1664" @@ -344,6 +355,7 @@ TEST(TestSendPronto, NormalPlusRepeatSequence) { EXPECT_EQ(0x18, irsend.capture.address); EXPECT_EQ(0x8, irsend.capture.command); EXPECT_EQ( + "f38028d50" "m8892s4446" "m546s546m546s546m546s546m546s1664m546s1664m546s546m546s546m546s546" "m546s1664m546s1664m546s1664m546s546m546s546m546s1664m546s1664m546s1664" diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_RC5_RC6_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_RC5_RC6_test.cpp similarity index 98% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_RC5_RC6_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_RC5_RC6_test.cpp index e8aa9bfc1..da9fa027c 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_RC5_RC6_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_RC5_RC6_test.cpp @@ -79,6 +79,7 @@ TEST(TestSendRC5, SendDataOnly) { irsend.reset(); irsend.sendRC5(0x0, kRC5Bits); EXPECT_EQ( + "f36000d25" "m889s889m1778s889m889s889m889s889m889s889m889s889m889" "s889m889s889m889s889m889s889m889s889m889s889m889s90664", irsend.outputStr()); @@ -86,6 +87,7 @@ TEST(TestSendRC5, SendDataOnly) { irsend.reset(); irsend.sendRC5(0x1AAA, kRC5Bits); EXPECT_EQ( + "f36000d25" "m889s889m889s889m1778s1778m1778s1778m1778s1778" "m1778s1778m1778s1778m1778s90664", irsend.outputStr()); @@ -93,6 +95,7 @@ TEST(TestSendRC5, SendDataOnly) { irsend.reset(); irsend.sendRC5(0x175, kRC5Bits); EXPECT_EQ( + "f36000d25" "m889s889m1778s889m889s889m889s1778m1778s1778" "m889s889m889s889m1778s1778m1778s1778m889s89775", irsend.outputStr()); @@ -100,6 +103,7 @@ TEST(TestSendRC5, SendDataOnly) { irsend.reset(); irsend.sendRC5(0x3FFF, kRC5Bits); EXPECT_EQ( + "f36000d25" "m889s889m889s889m889s889m889s889m889s889m889s889m889s889" "m889s889m889s889m889s889m889s889m889s889m889s889m889s89775", irsend.outputStr()); @@ -107,6 +111,7 @@ TEST(TestSendRC5, SendDataOnly) { irsend.reset(); irsend.sendRC5(0x0, kRC5XBits); EXPECT_EQ( + "f36000d25" "m889s889m1778s889m889s889m889s889m889s889m889s889m889" "s889m889s889m889s889m889s889m889s889m889s889m889s90664", irsend.outputStr()); @@ -114,6 +119,7 @@ TEST(TestSendRC5, SendDataOnly) { irsend.reset(); irsend.sendRC5(0x1AAA, kRC5XBits); EXPECT_EQ( + "f36000d25" "m1778s1778m1778s1778m1778s1778m1778" "s1778m1778s1778m1778s1778m1778s90664", irsend.outputStr()); @@ -121,6 +127,7 @@ TEST(TestSendRC5, SendDataOnly) { irsend.reset(); irsend.sendRC5(0x175, kRC5XBits); EXPECT_EQ( + "f36000d25" "m889s889m1778s889m889s889m889s1778m1778s1778" "m889s889m889s889m1778s1778m1778s1778m889s89775", irsend.outputStr()); @@ -128,6 +135,7 @@ TEST(TestSendRC5, SendDataOnly) { irsend.reset(); irsend.sendRC5(0x3FFF, kRC5XBits); EXPECT_EQ( + "f36000d25" "m1778s1778m889s889m889s889m889s889m889s889m889s889m889" "s889m889s889m889s889m889s889m889s889m889s889m889s89775", irsend.outputStr()); @@ -141,6 +149,7 @@ TEST(TestSendRC5, SendWithRepeats) { irsend.reset(); irsend.sendRC5(0x175, kRC5Bits, 1); EXPECT_EQ( + "f36000d25" "m889s889m1778s889m889s889m889s1778m1778s1778" "m889s889m889s889m1778s1778m1778s1778m889s90664" "m889s889m1778s889m889s889m889s1778m1778s1778" @@ -150,6 +159,7 @@ TEST(TestSendRC5, SendWithRepeats) { irsend.reset(); irsend.sendRC5(0x175, kRC5Bits, 2); EXPECT_EQ( + "f36000d25" "m889s889m1778s889m889s889m889s1778m1778s1778" "m889s889m889s889m1778s1778m1778s1778m889s90664" "m889s889m1778s889m889s889m889s1778m1778s1778" @@ -161,6 +171,7 @@ TEST(TestSendRC5, SendWithRepeats) { irsend.reset(); irsend.sendRC5(0x175, kRC5XBits, 1); EXPECT_EQ( + "f36000d25" "m889s889m1778s889m889s889m889s1778m1778s1778" "m889s889m889s889m1778s1778m1778s1778m889s90664" "m889s889m1778s889m889s889m889s1778m1778s1778" @@ -170,6 +181,7 @@ TEST(TestSendRC5, SendWithRepeats) { irsend.reset(); irsend.sendRC5(0x1175, kRC5XBits, 2); EXPECT_EQ( + "f36000d25" "m1778s889m889s889m889s889m889s1778m1778s1778" "m889s889m889s889m1778s1778m1778s1778m889s90664" "m1778s889m889s889m889s889m889s1778m1778s1778" @@ -444,6 +456,7 @@ TEST(TestSendRC6, SendMode0DataOnly) { irsend.reset(); irsend.sendRC6(0x0); EXPECT_EQ( + "f36000d33" "m2664s888" "m444s888m444s444m444s444m444s888m888s444m444s444m444s444" "m444s444m444s444m444s444m444s444m444s444m444s444m444s444" @@ -454,6 +467,7 @@ TEST(TestSendRC6, SendMode0DataOnly) { irsend.reset(); irsend.sendRC6(0x1FFFF); EXPECT_EQ( + "f36000d33" "m2664s888" "m444s888m444s444m444s444m1332s888m444s444m444s444m444s444" "m444s444m444s444m444s444m444s444m444s444m444s444m444s444" @@ -464,6 +478,7 @@ TEST(TestSendRC6, SendMode0DataOnly) { irsend.reset(); irsend.sendRC6(0x15555); EXPECT_EQ( + "f36000d33" "m2664s888" "m444s888m444s444m444s444m1332s1332m888s888m888s888" "m888s888m888s888m888s888m888s888m888s888m888" @@ -479,6 +494,7 @@ TEST(TestSendRC6, Send36BitDataOnly) { irsend.reset(); irsend.sendRC6(0x0, kRC6_36Bits); EXPECT_EQ( + "f36000d33" "m2664s888" "m444s888m444s444m444s444m444" "s888m888" @@ -492,6 +508,7 @@ TEST(TestSendRC6, Send36BitDataOnly) { irsend.reset(); irsend.sendRC6(0xFFFFFFFFF, kRC6_36Bits); EXPECT_EQ( + "f36000d33" "m2664s888" "m444s444m444s444m444s444m444s444" "m888s888" @@ -505,6 +522,7 @@ TEST(TestSendRC6, Send36BitDataOnly) { irsend.reset(); irsend.sendRC6(0xAAAAAAAAAA, kRC6_36Bits); EXPECT_EQ( + "f36000d33" "m2664s888m444s444m444s888m888" "s1332m1332" "s888m888s888m888s888m888s888m888s888m888s888m888s888m888s888m888s888m888" @@ -514,6 +532,7 @@ TEST(TestSendRC6, Send36BitDataOnly) { irsend.reset(); irsend.sendRC6(0xC800F740C, kRC6_36Bits); // Xbox 360 OnOff code EXPECT_EQ( + "f36000d33" "m2664s888" "m444s444m444s444m444s888m444" "s888m1332" @@ -526,6 +545,7 @@ TEST(TestSendRC6, Send36BitDataOnly) { irsend.sendRC6(irsend.toggleRC6(0xC800F740C, kRC6_36Bits), kRC6_36Bits); // Xbox 360 OnOff code (toggled) EXPECT_EQ( + "f36000d33" "m2664s888" "m444s444m444s444m444s888m444" "s888m1332" @@ -544,6 +564,7 @@ TEST(TestSendRC6, SendMode0WithRepeats) { irsend.reset(); irsend.sendRC6(0x175, kRC6Mode0Bits, 0); EXPECT_EQ( + "f36000d33" "m2664s888" "m444s888m444s444m444s444m444" "s888m888" @@ -554,6 +575,7 @@ TEST(TestSendRC6, SendMode0WithRepeats) { irsend.reset(); irsend.sendRC6(0x175, kRC6Mode0Bits, 1); EXPECT_EQ( + "f36000d33" "m2664s888" "m444s888m444s444m444s444m444" "s888m888" @@ -569,6 +591,7 @@ TEST(TestSendRC6, SendMode0WithRepeats) { irsend.reset(); irsend.sendRC6(0x175, kRC6Mode0Bits, 2); EXPECT_EQ( + "f36000d33" "m2664s888" "m444s888m444s444m444s444m444" "s888m888" @@ -595,6 +618,7 @@ TEST(TestSendRC6, Send36BitWithRepeats) { irsend.reset(); irsend.sendRC6(0x175, kRC6_36Bits, 0); EXPECT_EQ( + "f36000d33" "m2664s888" "m444s888m444s444m444s444m444" "s888m888" @@ -607,6 +631,7 @@ TEST(TestSendRC6, Send36BitWithRepeats) { irsend.reset(); irsend.sendRC6(0x175, kRC6_36Bits, 1); EXPECT_EQ( + "f36000d33" "m2664s888" "m444s888m444s444m444s444m444" "s888m888" @@ -626,6 +651,7 @@ TEST(TestSendRC6, Send36BitWithRepeats) { irsend.reset(); irsend.sendRC6(0x175, kRC6_36Bits, 2); EXPECT_EQ( + "f36000d33" "m2664s888" "m444s888m444s444m444s444m444" "s888m888" diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_RCMM_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_RCMM_test.cpp similarity index 98% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_RCMM_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_RCMM_test.cpp index 028dbd8b3..22306a59b 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_RCMM_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_RCMM_test.cpp @@ -14,6 +14,7 @@ TEST(TestSendRCMM, SendDataOnly) { irsend.reset(); irsend.sendRCMM(0xe0a600); EXPECT_EQ( + "f36000d33" "m416s277" "m166s777m166s611m166s277m166s277" "m166s611m166s611m166s444m166s611m166s277m166s277m166s277m166s277" @@ -22,6 +23,7 @@ TEST(TestSendRCMM, SendDataOnly) { irsend.reset(); irsend.sendRCMM(0x28e0a600UL, 32); EXPECT_EQ( + "f36000d33" "m416s277" "m166s277m166s611m166s611m166s277m166s777m166s611m166s277m166s277" "m166s611m166s611m166s444m166s611m166s277m166s277m166s277m166s277" @@ -37,6 +39,7 @@ TEST(TestSendRCMM, SendWithRepeats) { irsend.reset(); irsend.sendRCMM(0x28e0a600, 32, 2); // 2 repeats. EXPECT_EQ( + "f36000d33" "m416s277" "m166s277m166s611m166s611m166s277m166s777m166s611m166s277m166s277" "m166s611m166s611m166s444m166s611m166s277m166s277m166s277m166s277" @@ -60,6 +63,7 @@ TEST(TestSendRCMM, SendUnusualSize) { irsend.reset(); irsend.sendRCMM(0xE0, 8); EXPECT_EQ( + "f36000d33" "m416s277" "m166s777m166s611m166s277m166s277" "m166s24313", @@ -67,6 +71,7 @@ TEST(TestSendRCMM, SendUnusualSize) { irsend.reset(); irsend.sendRCMM(0x28e0a60000UL, 40); EXPECT_EQ( + "f36000d33" "m416s277" "m166s277m166s611m166s611m166s277m166s777m166s611m166s277m166s277" "m166s611m166s611m166s444m166s611m166s277m166s277m166s277m166s277" diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_Samsung_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Samsung_test.cpp similarity index 77% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_Samsung_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_Samsung_test.cpp index 9ee1fcabb..8670ac4ab 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_Samsung_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Samsung_test.cpp @@ -1,4 +1,4 @@ -// Copyright 2017 David Conran +// Copyright 2017, 2018, 2019 David Conran #include "ir_Samsung.h" #include "IRrecv.h" @@ -7,6 +7,13 @@ #include "IRsend_test.h" #include "gtest/gtest.h" + +// General housekeeping +TEST(TestSamsung, Housekeeping) { + ASSERT_EQ("SAMSUNG", typeToString(SAMSUNG)); + ASSERT_FALSE(hasACState(SAMSUNG)); +} + // Tests for sendSAMSUNG(). // Test sending typical data only. @@ -17,6 +24,7 @@ TEST(TestSendSamsung, SendDataOnly) { irsend.reset(); irsend.sendSAMSUNG(0xE0E09966); // Samsung TV Power On. EXPECT_EQ( + "f38000d33" "m4480s4480" "m560s1680m560s1680m560s1680m560s560m560s560m560s560m560s560" "m560s560m560s1680m560s1680m560s1680m560s560m560s560m560s560" @@ -36,6 +44,7 @@ TEST(TestSendSamsung, SendWithRepeats) { irsend.reset(); irsend.sendSAMSUNG(0xE0E09966, kSamsungBits, 1); // 1 repeat. EXPECT_EQ( + "f38000d33" "m4480s4480" "m560s1680m560s1680m560s1680m560s560m560s560m560s560m560s560" "m560s560m560s1680m560s1680m560s1680m560s560m560s560m560s560" @@ -51,6 +60,7 @@ TEST(TestSendSamsung, SendWithRepeats) { irsend.outputStr()); irsend.sendSAMSUNG(0xE0E09966, kSamsungBits, 2); // 2 repeats. EXPECT_EQ( + "f38000d33" "m4480s4480" "m560s1680m560s1680m560s1680m560s560m560s560m560s560m560s560" "m560s560m560s1680m560s1680m560s1680m560s560m560s560m560s560" @@ -281,17 +291,24 @@ TEST(TestDecodeSamsung, FailToDecodeNonSamsungExample) { ASSERT_FALSE(irrecv.decodeSAMSUNG(&irsend.capture, kSamsungBits, false)); } +// General housekeeping +TEST(TestSamsungAC, Housekeeping) { + ASSERT_EQ("SAMSUNG_AC", typeToString(SAMSUNG_AC)); + ASSERT_TRUE(hasACState(SAMSUNG_AC)); +} + // Tests for sendSamsungAC(). // Test sending typical data only. TEST(TestSendSamsungAC, SendDataOnly) { IRsendTest irsend(0); irsend.begin(); - uint8_t data[kSamsungAcStateLength] = {0x02, 0x92, 0x0F, 0x00, 0x00, - 0x00, 0xF0, 0x01, 0x02, 0xAF, - 0x71, 0x00, 0x15, 0xF0}; + uint8_t data[kSamsungAcStateLength] = { + 0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0, + 0x01, 0x02, 0xAF, 0x71, 0x00, 0x15, 0xF0}; irsend.sendSamsungAC(data); EXPECT_EQ( + "f38000d50" "m690s17844" "m3086s8864" "m586s436m586s1432m586s436m586s436m586s436m586s436m586s436m586s436" @@ -320,10 +337,12 @@ TEST(TestSendSamsungAC, SendExtendedData) { irsend.begin(); // "Off" message. uint8_t data[kSamsungAcExtendedStateLength] = { - 0x02, 0xB2, 0x0F, 0x00, 0x00, 0x00, 0xC0, 0x01, 0xD2, 0x0F, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x02, 0xFF, 0x71, 0x80, 0x11, 0xC0}; + 0x02, 0xB2, 0x0F, 0x00, 0x00, 0x00, 0xC0, + 0x01, 0xD2, 0x0F, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x02, 0xFF, 0x71, 0x80, 0x11, 0xC0}; irsend.sendSamsungAC(data, kSamsungAcExtendedStateLength); EXPECT_EQ( + "f38000d50" "m690s17844" "m3086s8864" "m586s436m586s1432m586s436m586s436m586s436m586s436m586s436m586s436" @@ -358,15 +377,16 @@ TEST(TestSendSamsungAC, SendExtendedData) { // Tests for IRSamsungAc class. TEST(TestIRSamsungAcClass, SetAndGetRaw) { - uint8_t expectedState[kSamsungAcStateLength] = {0x02, 0x92, 0x0F, 0x00, 0x00, - 0x00, 0xF0, 0x01, 0xE2, 0xFE, - 0x71, 0x40, 0x11, 0xF0}; + uint8_t expectedState[kSamsungAcStateLength] = { + 0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0, + 0x01, 0xE2, 0xFE, 0x71, 0x40, 0x11, 0xF0}; IRSamsungAc samsung(0); samsung.setRaw(expectedState); EXPECT_STATE_EQ(expectedState, samsung.getRaw(), kSamsungAcBits); uint8_t extendedState[kSamsungAcExtendedStateLength] = { - 0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0, 0x01, 0xD2, 0x0F, 0x00, - 0x00, 0x00, 0x00, 0x01, 0xE2, 0xFE, 0x71, 0x40, 0x11, 0xF0}; + 0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0, + 0x01, 0xD2, 0x0F, 0x00, 0x00, 0x00, 0x00, + 0x01, 0xE2, 0xFE, 0x71, 0x40, 0x11, 0xF0}; samsung.setRaw(extendedState, kSamsungAcExtendedStateLength); // We should NOT get the extended state back. EXPECT_STATE_EQ(expectedState, samsung.getRaw(), kSamsungAcBits); @@ -496,9 +516,15 @@ TEST(TestIRSamsungAcClass, ChecksumCalculation) { const uint8_t originalstate[kSamsungAcStateLength] = { 0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0, 0x01, 0x02, 0xAF, 0x71, 0x00, 0x15, 0xF0}; - uint8_t examplestate[kSamsungAcStateLength] = {0x02, 0x92, 0x0F, 0x00, 0x00, - 0x00, 0xF0, 0x01, 0x02, 0xAF, - 0x71, 0x00, 0x15, 0xF0}; + uint8_t examplestate[kSamsungAcStateLength] = { + 0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0, + 0x01, 0x02, 0xAF, 0x71, 0x00, 0x15, 0xF0}; + + const uint8_t extendedstate[kSamsungAcExtendedStateLength] = { + 0x02, 0xA9, 0x0F, 0x00, 0x00, 0x00, 0xC0, + 0x01, 0xC9, 0x0F, 0x00, 0x00, 0x00, 0x00, + 0x01, 0xF9, 0xCE, 0x71, 0xE0, 0x41, 0xC0}; + EXPECT_TRUE(IRSamsungAc::validChecksum(examplestate)); EXPECT_EQ(0, IRSamsungAc::calcChecksum(examplestate)); @@ -516,6 +542,9 @@ TEST(TestIRSamsungAcClass, ChecksumCalculation) { examplestate[11] = 0x01; EXPECT_FALSE(IRSamsungAc::validChecksum(examplestate)); EXPECT_EQ(0xF, IRSamsungAc::calcChecksum(examplestate)); + + // Check an extended state is valid. + EXPECT_TRUE(IRSamsungAc::validChecksum(extendedstate, 21)); } TEST(TestIRSamsungAcClass, HumanReadable) { @@ -585,9 +614,9 @@ TEST(TestDecodeSamsungAC, SyntheticDecode) { IRrecv irrecv(0); irsend.begin(); irsend.reset(); - uint8_t expectedState[kSamsungAcStateLength] = {0x02, 0x92, 0x0F, 0x00, 0x00, - 0x00, 0xF0, 0x01, 0x02, 0xAF, - 0x71, 0x00, 0x15, 0xF0}; + uint8_t expectedState[kSamsungAcStateLength] = { + 0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0, + 0x01, 0x02, 0xAF, 0x71, 0x00, 0x15, 0xF0}; // Synthesised Normal Samsung A/C message. irsend.sendSamsungAC(expectedState); irsend.makeDecodeResult(); @@ -626,9 +655,9 @@ TEST(TestDecodeSamsungAC, DecodeRealExample) { 584, 1406, 586, 410, 584, 1384, 606, 410, 586, 410, 584, 408, 586, 408, 586, 408, 586, 408, 588, 410, 584, 1408, 590, 1400, 592, 1398, 602, 1388, 612}; - uint8_t expectedState[kSamsungAcStateLength] = {0x02, 0x92, 0x0F, 0x00, 0x00, - 0x00, 0xF0, 0x01, 0x02, 0xAF, - 0x71, 0x00, 0x15, 0xF0}; + uint8_t expectedState[kSamsungAcStateLength] = { + 0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0, + 0x01, 0x02, 0xAF, 0x71, 0x00, 0x15, 0xF0}; irsend.sendRaw(rawData, 233, 38000); irsend.makeDecodeResult(); @@ -675,9 +704,9 @@ TEST(TestDecodeSamsungAC, DecodeRealExample2) { 560, 436, 486, 510, 566, 1400, 598, 420, 576, 418, 582, 414, 586, 410, 584, 410, 584, 410, 586, 410, 584, 1382, 608, 1384, 606, 1384, 606, 1408, 600}; - uint8_t expectedState[kSamsungAcStateLength] = {0x02, 0x92, 0x0F, 0x00, 0x00, - 0x00, 0xF0, 0x01, 0xE2, 0xFE, - 0x71, 0x80, 0x11, 0xF0}; + uint8_t expectedState[kSamsungAcStateLength] = { + 0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0, + 0x01, 0xE2, 0xFE, 0x71, 0x80, 0x11, 0xF0}; irsend.sendRaw(rawData, 233, 38000); irsend.makeDecodeResult(); @@ -735,8 +764,9 @@ TEST(TestDecodeSamsungAC, DecodePowerOnSample) { 518, 480, 520, 480, 520, 1454, 568, 1430, 566, 1432, 566, 1454, 594}; uint8_t expectedState[kSamsungAcExtendedStateLength] = { - 0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0, 0x01, 0xD2, 0x0F, 0x00, - 0x00, 0x00, 0x00, 0x01, 0xE2, 0xFE, 0x71, 0x80, 0x11, 0xF0}; + 0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0, + 0x01, 0xD2, 0x0F, 0x00, 0x00, 0x00, 0x00, + 0x01, 0xE2, 0xFE, 0x71, 0x80, 0x11, 0xF0}; irsend.sendRaw(rawData, 349, 38000); irsend.makeDecodeResult(); @@ -794,8 +824,9 @@ TEST(TestDecodeSamsungAC, DecodePowerOffSample) { 608}; uint8_t expectedState[kSamsungAcExtendedStateLength] = { - 0x02, 0xB2, 0x0F, 0x00, 0x00, 0x00, 0xC0, 0x01, 0xD2, 0x0F, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x02, 0xFF, 0x71, 0x80, 0x11, 0xC0}; + 0x02, 0xB2, 0x0F, 0x00, 0x00, 0x00, 0xC0, + 0x01, 0xD2, 0x0F, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x02, 0xFF, 0x71, 0x80, 0x11, 0xC0}; irsend.sendRaw(rawData, 349, 38000); irsend.makeDecodeResult(); @@ -840,9 +871,9 @@ TEST(TestDecodeSamsungAC, DecodeHeatSample) { 512, 482, 512, 482, 510, 484, 510, 484, 510, 1478, 512, 1504, 488, 1480, 560, 1454, 514}; - uint8_t expectedState[kSamsungAcStateLength] = {0x02, 0x92, 0x0F, 0x00, 0x00, - 0x00, 0xF0, 0x01, 0x02, 0xAF, - 0x71, 0x10, 0x41, 0xF0}; + uint8_t expectedState[kSamsungAcStateLength] = { + 0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0, + 0x01, 0x02, 0xAF, 0x71, 0x10, 0x41, 0xF0}; irsend.sendRaw(rawData, 233, 38000); irsend.makeDecodeResult(); @@ -887,9 +918,9 @@ TEST(TestDecodeSamsungAC, DecodeCoolSample) { 584, 412, 584, 408, 586, 410, 586, 408, 586, 1404, 586, 1408, 582, 1410, 562, 1426, 610}; - uint8_t expectedState[kSamsungAcStateLength] = {0x02, 0x92, 0x0F, 0x00, 0x00, - 0x00, 0xF0, 0x01, 0xE2, 0xFE, - 0x71, 0x40, 0x11, 0xF0}; + uint8_t expectedState[kSamsungAcStateLength] = { + 0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0, + 0x01, 0xE2, 0xFE, 0x71, 0x40, 0x11, 0xF0}; irsend.sendRaw(rawData, 233, 38000); irsend.makeDecodeResult(); @@ -905,3 +936,195 @@ TEST(TestDecodeSamsungAC, DecodeCoolSample) { "Beep: Off, Clean: Off, Quiet: Off", samsung.toString()); } + +TEST(TestDecodeSamsungAC, Issue604DecodeExtended) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + irsend.reset(); + uint16_t sendOff[349] = { + 642, 17730, 3056, 8916, 542, 448, 552, 1440, 552, 444, 552, 444, + 552, 444, 552, 440, 556, 440, 556, 440, 556, 1436, 552, 444, + 552, 444, 552, 1440, 548, 470, 526, 1464, 470, 526, 516, 1470, + 552, 1440, 552, 1440, 550, 1436, 556, 1434, 552, 444, 552, 444, + 552, 444, 552, 442, 552, 444, 546, 470, 526, 470, 526, 470, + 470, 524, 518, 474, 548, 448, 552, 444, 552, 442, 552, 444, + 550, 444, 552, 440, 556, 440, 556, 438, 556, 440, 552, 442, + 552, 444, 552, 442, 552, 444, 550, 470, 526, 466, 524, 470, + 470, 524, 470, 524, 518, 476, 548, 444, 552, 444, 556, 440, + 552, 442, 552, 444, 550, 1436, 556, 1436, 552, 2946, 3026, 8918, + 550, 1440, 552, 444, 548, 468, 526, 468, 470, 526, 470, 526, + 542, 452, 548, 444, 552, 1440, 550, 444, 552, 444, 552, 1436, + 556, 438, 552, 442, 552, 1440, 552, 1440, 552, 1460, 526, 1464, + 470, 1516, 548, 1444, 552, 444, 552, 442, 552, 444, 552, 438, + 556, 440, 556, 440, 552, 444, 552, 444, 552, 444, 552, 444, + 552, 444, 548, 448, 546, 470, 526, 468, 526, 470, 470, 524, + 520, 470, 548, 448, 552, 444, 552, 444, 552, 444, 552, 444, + 552, 438, 556, 440, 556, 438, 552, 444, 552, 442, 552, 444, + 552, 444, 552, 444, 552, 470, 526, 466, 526, 470, 470, 524, + 518, 478, 546, 448, 552, 2920, 3052, 8916, 552, 1434, 556, 440, + 556, 438, 552, 444, 552, 442, 552, 442, 552, 442, 552, 444, + 548, 1444, 548, 470, 526, 470, 522, 1466, 470, 1520, 548, 1438, + 556, 1436, 552, 1440, 552, 442, 552, 1436, 552, 1440, 552, 1440, + 552, 442, 552, 470, 522, 1466, 526, 1466, 470, 1516, 552, 444, + 552, 442, 552, 444, 552, 1436, 556, 1436, 552, 1440, 550, 444, + 552, 444, 552, 444, 548, 448, 546, 448, 548, 470, 526, 1462, + 474, 1518, 548, 1440, 552, 1438, 556, 440, 550, 444, 552, 444, + 552, 444, 552, 440, 556, 1436, 552, 444, 552, 444, 552, 444, + 550, 470, 522, 470, 524, 470, 470, 524, 518, 1474, 548, 1440, + 556}; + + uint8_t expectedState[kSamsungAcExtendedStateLength] = { + 0x02, 0xA9, 0x0F, 0x00, 0x00, 0x00, 0xC0, + 0x01, 0xC9, 0x0F, 0x00, 0x00, 0x00, 0x00, + 0x01, 0xF9, 0xCE, 0x71, 0xE0, 0x41, 0xC0}; + + irsend.sendRaw(sendOff, 349, 38000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(SAMSUNG_AC, irsend.capture.decode_type); + EXPECT_EQ(kSamsungAcExtendedBits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + + IRSamsungAc samsung(0); + samsung.setRaw(irsend.capture.state, irsend.capture.bits / 8); + EXPECT_EQ( + "Power: Off, Mode: 4 (HEAT), Temp: 30C, Fan: 0 (AUTO), Swing: Off, " + "Beep: Off, Clean: Off, Quiet: Off", + samsung.toString()); +} + +TEST(TestSendSamsung36, SendDataOnly) { + IRsendTest irsend(0); + irsend.begin(); + + irsend.reset(); + irsend.sendSamsung36(0); + EXPECT_EQ( + "f38000d50" + "m4480s4480" + "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" + "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" + "m560s4480" + "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" + "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" + "m560s560m560s560m560s560m560s560" + "m560s26880", + irsend.outputStr()); + irsend.sendSamsung36(0x400E00FF); + EXPECT_EQ( + "f38000d50" + "m4480s4480" + "m560s560m560s560m560s560m560s560m560s560m560s1680m560s560m560s560" + "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" + "m560s4480" + "m560s1680m560s1680m560s1680m560s560m560s560m560s560m560s560m560s560" + "m560s560m560s560m560s560m560s560m560s1680m560s1680m560s1680m560s1680" + "m560s1680m560s1680m560s1680m560s1680" + "m560s26880", + irsend.outputStr()); + irsend.reset(); +} + +// General housekeeping +TEST(TestSamsung36, Housekeeping) { + ASSERT_EQ("SAMSUNG36", typeToString(SAMSUNG36)); + ASSERT_FALSE(hasACState(SAMSUNG36)); +} + +// Test sending with different repeats. +TEST(TestSendSamsung36, SendWithRepeats) { + IRsendTest irsend(0); + irsend.begin(); + + irsend.reset(); + irsend.sendSamsung36(0x400E00FF, kSamsung36Bits, 1); // 1 repeat. + EXPECT_EQ( + "f38000d50" + "m4480s4480" + "m560s560m560s560m560s560m560s560m560s560m560s1680m560s560m560s560" + "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" + "m560s4480" + "m560s1680m560s1680m560s1680m560s560m560s560m560s560m560s560m560s560" + "m560s560m560s560m560s560m560s560m560s1680m560s1680m560s1680m560s1680" + "m560s1680m560s1680m560s1680m560s1680" + "m560s26880" + "m4480s4480" + "m560s560m560s560m560s560m560s560m560s560m560s1680m560s560m560s560" + "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" + "m560s4480" + "m560s1680m560s1680m560s1680m560s560m560s560m560s560m560s560m560s560" + "m560s560m560s560m560s560m560s560m560s1680m560s1680m560s1680m560s1680" + "m560s1680m560s1680m560s1680m560s1680" + "m560s26880", + irsend.outputStr()); + irsend.sendSamsung36(0x400E00FF, kSamsung36Bits, 2); // 2 repeats. + EXPECT_EQ( + "f38000d50" + "m4480s4480" + "m560s560m560s560m560s560m560s560m560s560m560s1680m560s560m560s560" + "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" + "m560s4480" + "m560s1680m560s1680m560s1680m560s560m560s560m560s560m560s560m560s560" + "m560s560m560s560m560s560m560s560m560s1680m560s1680m560s1680m560s1680" + "m560s1680m560s1680m560s1680m560s1680" + "m560s26880" + "m4480s4480" + "m560s560m560s560m560s560m560s560m560s560m560s1680m560s560m560s560" + "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" + "m560s4480" + "m560s1680m560s1680m560s1680m560s560m560s560m560s560m560s560m560s560" + "m560s560m560s560m560s560m560s560m560s1680m560s1680m560s1680m560s1680" + "m560s1680m560s1680m560s1680m560s1680" + "m560s26880" + "m4480s4480" + "m560s560m560s560m560s560m560s560m560s560m560s1680m560s560m560s560" + "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" + "m560s4480" + "m560s1680m560s1680m560s1680m560s560m560s560m560s560m560s560m560s560" + "m560s560m560s560m560s560m560s560m560s1680m560s1680m560s1680m560s1680" + "m560s1680m560s1680m560s1680m560s1680" + "m560s26880", + irsend.outputStr()); +} + +TEST(TestDecodeSamsung36, RealExample) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + irsend.reset(); + uint16_t rawData[77] = { + 4542, 4438, 568, 432, 562, 436, 536, 462, 538, 460, 538, 460, 564, 1434, + 564, 434, 534, 464, 536, 462, 562, 436, 536, 464, 564, 432, 538, 462, 536, + 464, 534, 464, 564, 420, 566, 4414, 538, 1462, 566, 1432, 562, 1436, 536, + 462, 564, 436, 562, 436, 560, 436, 562, 436, 562, 436, 560, 438, 536, 462, + 562, 436, 562, 1436, 562, 1434, 536, 1462, 564, 1434, 562, 1436, 564, + 1436, 534, 1462, 534, 1464, 536}; // UNKNOWN E4CD1208 + + irsend.sendRaw(rawData, 77, 38000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(SAMSUNG36, irsend.capture.decode_type); + EXPECT_EQ(kSamsung36Bits, irsend.capture.bits); + EXPECT_EQ(0x400E00FF, irsend.capture.value); + EXPECT_EQ(0xE00FF, irsend.capture.command); + EXPECT_EQ(0x400, irsend.capture.address); +} + +TEST(TestDecodeSamsung36, SyntheticExample) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + irsend.reset(); + + irsend.sendSamsung36(0x400E00FF); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decodeSamsung36(&irsend.capture)); + ASSERT_EQ(SAMSUNG36, irsend.capture.decode_type); + EXPECT_EQ(kSamsung36Bits, irsend.capture.bits); + EXPECT_EQ(0x400E00FF, irsend.capture.value); + EXPECT_EQ(0xE00FF, irsend.capture.command); + EXPECT_EQ(0x400, irsend.capture.address); +} diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_Sanyo_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Sanyo_test.cpp similarity index 99% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_Sanyo_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_Sanyo_test.cpp index 14c1c7da0..165e29f17 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_Sanyo_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Sanyo_test.cpp @@ -27,6 +27,7 @@ TEST(TestEncodeSanyoLC7461, SendDataOnly) { irsend.reset(); irsend.sendSanyoLC7461(0x1D8113F00FF); EXPECT_EQ( + "f38000d33" "m8960s4480" "m560s560m560s1680m560s1680m560s1680m560s560m560s1680m560s1680m560s560" "m560s560m560s560m560s560m560s560m560s560m560s1680m560s560m560s560" @@ -45,6 +46,7 @@ TEST(TestEncodeSanyoLC7461, SendWithRepeats) { irsend.reset(); irsend.sendSanyoLC7461(0x1D8113F00FF, kSanyoLC7461Bits, 1); // 1 repeat. EXPECT_EQ( + "f38000d33" "m8960s4480" "m560s560m560s1680m560s1680m560s1680m560s560m560s1680m560s1680m560s560" "m560s560m560s560m560s560m560s560m560s560m560s1680m560s560m560s560" diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_Sharp_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Sharp_test.cpp similarity index 98% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_Sharp_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_Sharp_test.cpp index 8481a4649..c9d3e851b 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_Sharp_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Sharp_test.cpp @@ -47,6 +47,7 @@ TEST(TestSendSharp, SendDataOnly) { irsend.reset(); irsend.sendSharp(0x11, 0x52); EXPECT_EQ( + "f38000d33" "m260s1820m260s780m260s780m260s780m260s1820m260s780m260s1820m260s780" "m260s1820m260s780m260s780m260s1820m260s780m260s1820m260s780" "m260s43602" @@ -64,6 +65,7 @@ TEST(TestSendSharp, SendWithRepeats) { irsend.reset(); irsend.sendSharp(0x11, 0x52, kSharpBits, 1); // 1 repeat. EXPECT_EQ( + "f38000d33" "m260s1820m260s780m260s780m260s780m260s1820m260s780m260s1820m260s780" "m260s1820m260s780m260s780m260s1820m260s780m260s1820m260s780" "m260s43602" @@ -87,6 +89,7 @@ TEST(TestSendSharp, SendUnusualSize) { irsend.reset(); irsend.sendSharp(0x0, 0x0, 8); EXPECT_EQ( + "f38000d33" "m260s780m260s780m260s780m260s780m260s780m260s780m260s1820m260s780" "m260s43602" "m260s1820m260s1820m260s1820m260s1820m260s1820m260s1820m260s780m260s1820" @@ -96,6 +99,7 @@ TEST(TestSendSharp, SendUnusualSize) { irsend.reset(); irsend.sendSharp(0x0, 0x0, 16); EXPECT_EQ( + "f38000d33" "m260s780m260s780m260s780m260s780m260s780m260s780m260s780m260s780" "m260s780m260s780m260s780m260s780m260s780m260s780m260s1820m260s780" "m260s43602" @@ -115,6 +119,7 @@ TEST(TestSendSharpRaw, SendDataOnly) { irsend.reset(); irsend.sendSharpRaw(0x454A); EXPECT_EQ( + "f38000d33" "m260s1820m260s780m260s780m260s780m260s1820m260s780m260s1820m260s780" "m260s1820m260s780m260s780m260s1820m260s780m260s1820m260s780" "m260s43602" @@ -132,6 +137,7 @@ TEST(TestSendSharpRaw, SendWithRepeats) { irsend.reset(); irsend.sendSharpRaw(0x454A, kSharpBits, 1); // 1 repeat. EXPECT_EQ( + "f38000d33" "m260s1820m260s780m260s780m260s780m260s1820m260s780m260s1820m260s780" "m260s1820m260s780m260s780m260s1820m260s780m260s1820m260s780" "m260s43602" @@ -155,6 +161,7 @@ TEST(TestSendSharpRaw, SendUnusualSize) { irsend.reset(); irsend.sendSharpRaw(0x2, 8); EXPECT_EQ( + "f38000d33" "m260s780m260s780m260s780m260s780m260s780m260s780m260s1820m260s780" "m260s43602" "m260s1820m260s1820m260s1820m260s1820m260s1820m260s1820m260s780m260s1820" @@ -164,6 +171,7 @@ TEST(TestSendSharpRaw, SendUnusualSize) { irsend.reset(); irsend.sendSharpRaw(0x2, 16); EXPECT_EQ( + "f38000d33" "m260s780m260s780m260s780m260s780m260s780m260s780m260s780m260s780" "m260s780m260s780m260s780m260s780m260s780m260s780m260s1820m260s780" "m260s43602" diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_Sherwood_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Sherwood_test.cpp similarity index 97% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_Sherwood_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_Sherwood_test.cpp index 22d9ead38..f1f41d9c8 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_Sherwood_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Sherwood_test.cpp @@ -14,6 +14,7 @@ TEST(TestSendSherwood, SendDataOnly) { irsend.reset(); irsend.sendSherwood(0xC1A28877); EXPECT_EQ( + "f38000d33" "m8960s4480m560s1680m560s1680m560s560m560s560m560s560m560s560" "m560s560m560s1680m560s1680m560s560m560s1680m560s560m560s560" "m560s560m560s1680m560s560m560s1680m560s560m560s560m560s560" @@ -31,6 +32,7 @@ TEST(TestSendSherwood, SendDataWithRepeats) { irsend.reset(); irsend.sendSherwood(0xC1A28877, 32, 2); EXPECT_EQ( + "f38000d33" "m8960s4480m560s1680m560s1680m560s560m560s560m560s560m560s560" "m560s560m560s1680m560s1680m560s560m560s1680m560s560m560s560" "m560s560m560s1680m560s560m560s1680m560s560m560s560m560s560" @@ -50,6 +52,7 @@ TEST(TestSendSherwood, SendDataWithZeroRepeats) { irsend.sendSherwood(0xC1A28877, 32, 0); // Should have a single NEC repeat, as we always send one. EXPECT_EQ( + "f38000d33" "m8960s4480m560s1680m560s1680m560s560m560s560m560s560m560s560" "m560s560m560s1680m560s1680m560s560m560s1680m560s560m560s560" "m560s560m560s1680m560s560m560s1680m560s560m560s560m560s560" diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_Sony_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Sony_test.cpp similarity index 99% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_Sony_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_Sony_test.cpp index c79ff6175..35c3287b0 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_Sony_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Sony_test.cpp @@ -15,6 +15,7 @@ TEST(TestSendSony, SendDataOnly) { irsend.sendSony(0); // We expect three 20-bit commands to be sent. EXPECT_EQ( + "f40000d33" "m2400s600m600s600m600s600m600s600m600s600m600s600m600s600m600s600" "m600s600m600s600m600s600m600s600m600s600m600s600m600s600m600s600" "m600s600m600s600m600s600m600s600m600s18600" @@ -30,6 +31,7 @@ TEST(TestSendSony, SendDataOnly) { irsend.sendSony(0x240C, kSony20Bits); // We expect three 20-bit commands to be sent. EXPECT_EQ( + "f40000d33" "m2400s600m600s600m600s600m600s600m600s600m600s600m600s600m1200s600" "m600s600m600s600m1200s600m600s600m600s600m600s600m600s600m600s600" "m600s600m1200s600m1200s600m600s600m600s16200" @@ -45,6 +47,7 @@ TEST(TestSendSony, SendDataOnly) { irsend.sendSony(0x240C, kSony15Bits); // We expect three 15-bit commands to be sent. EXPECT_EQ( + "f40000d33" "m2400s600m600s600m1200s600m600s600m600s600m1200s600m600s600" "m600s600m600s600m600s600m600s600m600s600m1200s600m1200s600m600s600" "m600s22200" @@ -60,6 +63,7 @@ TEST(TestSendSony, SendDataOnly) { irsend.sendSony(0xA90, kSony12Bits); // We expect three 15-bit commands to be sent. EXPECT_EQ( + "f40000d33" "m2400s600m1200s600m600s600m1200s600m600s600m1200s600m600s600" "m600s600m1200s600m600s600m600s600m600s600m600s25800" "m2400s600m1200s600m600s600m1200s600m600s600m1200s600m600s600" @@ -77,12 +81,14 @@ TEST(TestSendSony, SendWithDiffRepeats) { irsend.reset(); irsend.sendSony(0x240C, kSony20Bits, 0); // Send a command with 0 repeats. EXPECT_EQ( + "f40000d33" "m2400s600m600s600m600s600m600s600m600s600m600s600m600s600m1200s600" "m600s600m600s600m1200s600m600s600m600s600m600s600m600s600m600s600" "m600s600m1200s600m1200s600m600s600m600s16200", irsend.outputStr()); irsend.sendSony(0x240C, kSony20Bits, 1); // Send a command with 1 repeat. EXPECT_EQ( + "f40000d33" "m2400s600m600s600m600s600m600s600m600s600m600s600m600s600m1200s600" "m600s600m600s600m1200s600m600s600m600s600m600s600m600s600m600s600" "m600s600m1200s600m1200s600m600s600m600s16200" @@ -92,6 +98,7 @@ TEST(TestSendSony, SendWithDiffRepeats) { irsend.outputStr()); irsend.sendSony(0x240C, kSony20Bits, 3); // Send a command with 3 repeats. EXPECT_EQ( + "f40000d33" "m2400s600m600s600m600s600m600s600m600s600m600s600m600s600m1200s600" "m600s600m600s600m1200s600m600s600m600s600m600s600m600s600m600s600" "m600s600m1200s600m1200s600m600s600m600s16200" diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Tcl_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Tcl_test.cpp new file mode 100644 index 000000000..249dcc637 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Tcl_test.cpp @@ -0,0 +1,384 @@ +// Copyright 2019 David Conran + +#include "ir_Tcl.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "gtest/gtest.h" + +// General housekeeping +TEST(TestTcl112Ac, Housekeeping) { + ASSERT_EQ("TCL112AC", typeToString(TCL112AC)); + ASSERT_TRUE(hasACState(TCL112AC)); +} + +// Tests for decodeTcl112Ac(). + +// Decode a real Tcl112Ac A/C example from Issue #619 +TEST(TestDecodeTcl112Ac, DecodeRealExample) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + irsend.reset(); + // Tcl112Ac A/C example from Issue #619 On.txt + uint16_t rawData[227] = { + 3030, 1658, 494, 1066, 494, 1068, 498, 320, 494, + 326, 498, 320, 494, 1068, 500, 320, 494, 332, + 494, 1068, 500, 1062, 496, 324, 492, 1044, 524, + 322, 492, 326, 498, 1062, 494, 1074, 494, 326, + 500, 1062, 496, 1066, 490, 328, 496, 322, 492, + 1070, 498, 322, 494, 332, 492, 1068, 498, 320, + 494, 326, 498, 320, 496, 324, 500, 320, 494, + 324, 490, 336, 500, 320, 496, 324, 490, 328, + 496, 322, 492, 328, 498, 322, 492, 326, 498, + 328, 496, 322, 492, 328, 498, 1064, 494, 326, + 498, 320, 494, 1066, 490, 330, 496, 330, 494, + 1066, 490, 1070, 498, 322, 492, 328, 498, 322, + 492, 326, 498, 322, 492, 332, 492, 1068, 498, + 1062, 494, 1066, 500, 318, 496, 324, 490, 328, + 496, 324, 492, 334, 490, 328, 496, 324, 492, + 328, 496, 322, 492, 328, 498, 320, 494, 1068, + 500, 326, 500, 320, 492, 326, 500, 320, 496, + 324, 500, 318, 496, 324, 490, 328, 496, 330, + 496, 324, 490, 328, 496, 324, 490, 328, 498, + 322, 492, 328, 498, 320, 492, 334, 492, 328, + 498, 322, 494, 326, 498, 320, 494, 324, 500, + 322, 492, 324, 490, 336, 498, 320, 494, 324, + 500, 320, 496, 324, 490, 328, 498, 322, 492, + 328, 496, 1070, 496, 1064, 492, 1070, 498, 322, + 494, 326, 500, 320, 494, 324, 500, 320, 494, + 324, 470}; // UNKNOWN CE60D6B9 + uint8_t expectedState[kTcl112AcStateLength] = { + 0x23, 0xCB, 0x26, 0x01, 0x00, 0x24, 0x03, + 0x07, 0x40, 0x00, 0x00, 0x00, 0x80, 0x03}; + + irsend.sendRaw(rawData, 227, 38000); + irsend.makeDecodeResult(); + + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(TCL112AC, irsend.capture.decode_type); + EXPECT_EQ(kTcl112AcBits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + + IRTcl112Ac ac(0); + ac.setRaw(irsend.capture.state); + EXPECT_EQ( + "Power: On, Mode: 3 (COOL), Temp: 24C, Fan: 0 (Auto), Econo: Off, " + "Health: Off, Light: On, Turbo: Off, Swing (H): Off, Swing (V): Off", + ac.toString()); +} + +// Decode a synthetic Tcl112Ac A/C example from Issue #619 +TEST(TestDecodeTcl112Ac, DecodeSyntheticExample) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + irsend.reset(); + + uint8_t expectedState[kTcl112AcStateLength] = {0x23, 0xCB, 0x26, 0x01, 0x00, + 0x24, 0x03, 0x07, 0x40, 0x00, + 0x00, 0x00, 0x80, 0x03}; + + irsend.sendTcl112Ac(expectedState); + irsend.makeDecodeResult(); + + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(TCL112AC, irsend.capture.decode_type); + EXPECT_EQ(kTcl112AcBits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); +} + +TEST(TestTcl112AcClass, Temperature) { + const uint8_t temp16C[kTcl112AcStateLength] = { + 0x23, 0xCB, 0x26, 0x01, 0x00, 0x24, 0x03, + 0x0F, 0x00, 0x00, 0x00, 0x00, 0x80, 0xCB}; + const uint8_t temp16point5C[kTcl112AcStateLength] = { + 0x23, 0xCB, 0x26, 0x01, 0x00, 0x24, 0x03, + 0x0F, 0x00, 0x00, 0x00, 0x00, 0xA0, 0xEB}; + const uint8_t temp19point5C[kTcl112AcStateLength] = { + 0x23, 0xCB, 0x26, 0x01, 0x00, 0x24, 0x03, + 0x0C, 0x00, 0x00, 0x00, 0x00, 0xA0, 0xE8}; + const uint8_t temp31C[kTcl112AcStateLength] = { + 0x23, 0xCB, 0x26, 0x01, 0x00, 0x24, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xBC}; + IRTcl112Ac ac(0); + ac.setRaw(temp16C); + EXPECT_EQ( + "Power: On, Mode: 3 (COOL), Temp: 16C, Fan: 0 (Auto), Econo: Off, " + "Health: Off, Light: On, Turbo: Off, Swing (H): Off, Swing (V): Off", + ac.toString()); + ac.setRaw(temp16point5C); + EXPECT_EQ( + "Power: On, Mode: 3 (COOL), Temp: 16.5C, Fan: 0 (Auto), Econo: Off, " + "Health: Off, Light: On, Turbo: Off, Swing (H): Off, Swing (V): Off", + ac.toString()); + ac.setRaw(temp19point5C); + EXPECT_EQ( + "Power: On, Mode: 3 (COOL), Temp: 19.5C, Fan: 0 (Auto), Econo: Off, " + "Health: Off, Light: On, Turbo: Off, Swing (H): Off, Swing (V): Off", + ac.toString()); + ac.setRaw(temp31C); + EXPECT_EQ( + "Power: On, Mode: 3 (COOL), Temp: 31C, Fan: 0 (Auto), Econo: Off, " + "Health: Off, Light: On, Turbo: Off, Swing (H): Off, Swing (V): Off", + ac.toString()); + + ac.setTemp(kTcl112AcTempMin); + EXPECT_EQ(kTcl112AcTempMin, ac.getTemp()); + + ac.setTemp(kTcl112AcTempMin + 1); + EXPECT_EQ(kTcl112AcTempMin + 1, ac.getTemp()); + + ac.setTemp(kTcl112AcTempMax); + EXPECT_EQ(kTcl112AcTempMax, ac.getTemp()); + + ac.setTemp(kTcl112AcTempMin - 1); + EXPECT_EQ(kTcl112AcTempMin, ac.getTemp()); + + ac.setTemp(kTcl112AcTempMax + 0.5); + EXPECT_EQ(kTcl112AcTempMax, ac.getTemp()); + + ac.setTemp(23); + EXPECT_EQ(23, ac.getTemp()); + + ac.setTemp(27.4); + EXPECT_EQ(27, ac.getTemp()); + + ac.setTemp(22.5); + EXPECT_EQ(22.5, ac.getTemp()); + + ac.setTemp(25.6); + EXPECT_EQ(25.5, ac.getTemp()); + + ac.setTemp(0); + EXPECT_EQ(kTcl112AcTempMin, ac.getTemp()); + + ac.setTemp(255); + EXPECT_EQ(kTcl112AcTempMax, ac.getTemp()); +} + +TEST(TestTcl112AcClass, OperatingMode) { + IRTcl112Ac ac(0); + ac.begin(); + + ac.setMode(kTcl112AcAuto); + EXPECT_EQ(kTcl112AcAuto, ac.getMode()); + + ac.setMode(kTcl112AcCool); + EXPECT_EQ(kTcl112AcCool, ac.getMode()); + + ac.setMode(kTcl112AcHeat); + EXPECT_EQ(kTcl112AcHeat, ac.getMode()); + + ac.setFan(kTcl112AcFanAuto); + ac.setMode(kTcl112AcFan); // Should set fan speed to High. + EXPECT_EQ(kTcl112AcFan, ac.getMode()); + EXPECT_EQ(kTcl112AcFanHigh, ac.getFan()); + + ac.setMode(kTcl112AcDry); + EXPECT_EQ(kTcl112AcDry, ac.getMode()); + + ac.setMode(kTcl112AcHeat - 1); + EXPECT_EQ(kTcl112AcAuto, ac.getMode()); + + ac.setMode(kTcl112AcCool); + EXPECT_EQ(kTcl112AcCool, ac.getMode()); + + ac.setMode(kTcl112AcAuto + 1); + EXPECT_EQ(kTcl112AcAuto, ac.getMode()); + + ac.setMode(kTcl112AcCool); + ac.setMode(255); + EXPECT_EQ(kTcl112AcAuto, ac.getMode()); + + ac.setMode(kTcl112AcCool); + ac.setMode(0); + EXPECT_EQ(kTcl112AcAuto, ac.getMode()); + + const uint8_t automode[] = { + 0x23, 0xCB, 0x26, 0x01, 0x00, 0x24, 0x08, + 0x07, 0x00, 0x00, 0x00, 0x00, 0x80, 0x48}; + ac.setRaw(automode); + EXPECT_EQ( + "Power: On, Mode: 8 (AUTO), Temp: 24C, Fan: 0 (Auto), Econo: Off, " + "Health: Off, Light: On, Turbo: Off, Swing (H): Off, Swing (V): Off", + ac.toString()); +} + +TEST(TestTcl112AcClass, Power) { + IRTcl112Ac ac(0); + ac.begin(); + + ac.setPower(true); + EXPECT_TRUE(ac.getPower()); + + ac.setPower(false); + EXPECT_EQ(false, ac.getPower()); + + ac.setPower(true); + EXPECT_TRUE(ac.getPower()); + + ac.off(); + EXPECT_EQ(false, ac.getPower()); + + ac.on(); + EXPECT_TRUE(ac.getPower()); + + const uint8_t on[kTcl112AcStateLength] = { + 0x23, 0xCB, 0x26, 0x01, 0x00, 0x24, 0x03, + 0x0F, 0x00, 0x00, 0x00, 0x00, 0x80, 0xCB}; + ac.setRaw(on); + EXPECT_EQ( + "Power: On, Mode: 3 (COOL), Temp: 16C, Fan: 0 (Auto), Econo: Off, " + "Health: Off, Light: On, Turbo: Off, Swing (H): Off, Swing (V): Off", + ac.toString()); + + const uint8_t off[kTcl112AcStateLength] = { + 0x23, 0xCB, 0x26, 0x01, 0x00, 0x20, 0x03, + 0x07, 0x40, 0x00, 0x00, 0x00, 0x80, 0xCB}; + ac.setRaw(off); + EXPECT_EQ( + "Power: Off, Mode: 3 (COOL), Temp: 24C, Fan: 0 (Auto), Econo: Off, " + "Health: Off, Light: On, Turbo: Off, Swing (H): Off, Swing (V): Off", + ac.toString()); +} + + +TEST(TestTcl112AcClass, Checksum) { + uint8_t temp16C[kTcl112AcStateLength] = { + 0x23, 0xCB, 0x26, 0x01, 0x00, 0x24, 0x03, + 0x0F, 0x00, 0x00, 0x00, 0x00, 0x80, 0xCB}; + uint8_t temp31C[kTcl112AcStateLength] = { + 0x23, 0xCB, 0x26, 0x01, 0x00, 0x24, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xBC}; + IRTcl112Ac ac(0); + EXPECT_EQ(0xCB, ac.calcChecksum(temp16C)); + ac.setRaw(temp16C); + EXPECT_EQ( + "Power: On, Mode: 3 (COOL), Temp: 16C, Fan: 0 (Auto), Econo: Off, " + "Health: Off, Light: On, Turbo: Off, Swing (H): Off, Swing (V): Off", + ac.toString()); + ac.setRaw(temp31C); + EXPECT_EQ( + "Power: On, Mode: 3 (COOL), Temp: 31C, Fan: 0 (Auto), Econo: Off, " + "Health: Off, Light: On, Turbo: Off, Swing (H): Off, Swing (V): Off", + ac.toString()); + EXPECT_EQ(0xBC, ac.calcChecksum(temp31C)); + + EXPECT_TRUE(IRTcl112Ac::validChecksum(temp16C)); + EXPECT_TRUE(IRTcl112Ac::validChecksum(temp31C)); + EXPECT_TRUE(ac.validChecksum(temp31C)); + ac.setRaw(temp16C); + EXPECT_TRUE(ac.validChecksum(ac.getRaw())); + ac.setTemp(31); + EXPECT_TRUE(ac.validChecksum(ac.getRaw())); + EXPECT_EQ(0xBC, ac.calcChecksum(ac.getRaw())); +} + +TEST(TestTcl112AcClass, Econo) { + IRTcl112Ac ac(0); + ac.begin(); + + ac.setEcono(true); + EXPECT_TRUE(ac.getEcono()); + ac.setEcono(false); + EXPECT_EQ(false, ac.getEcono()); + ac.setEcono(true); + EXPECT_TRUE(ac.getEcono()); +} + +TEST(TestTcl112AcClass, Health) { + IRTcl112Ac ac(0); + ac.begin(); + + ac.setHealth(true); + EXPECT_TRUE(ac.getHealth()); + ac.setHealth(false); + EXPECT_EQ(false, ac.getHealth()); + ac.setHealth(true); + EXPECT_TRUE(ac.getHealth()); +} + +TEST(TestTcl112AcClass, Light) { + IRTcl112Ac ac(0); + ac.begin(); + + ac.setLight(true); + EXPECT_TRUE(ac.getLight()); + ac.setLight(false); + EXPECT_EQ(false, ac.getLight()); + ac.setLight(true); + EXPECT_TRUE(ac.getLight()); +} + +TEST(TestTcl112AcClass, SwingHorizontal) { + IRTcl112Ac ac(0); + ac.begin(); + + ac.setSwingHorizontal(true); + EXPECT_TRUE(ac.getSwingHorizontal()); + ac.setSwingHorizontal(false); + EXPECT_EQ(false, ac.getSwingHorizontal()); + ac.setSwingHorizontal(true); + EXPECT_TRUE(ac.getSwingHorizontal()); +} + +TEST(TestTcl112AcClass, SwingVertical) { + IRTcl112Ac ac(0); + ac.begin(); + + ac.setSwingVertical(true); + EXPECT_TRUE(ac.getSwingVertical()); + ac.setSwingVertical(false); + EXPECT_EQ(false, ac.getSwingVertical()); + ac.setSwingVertical(true); + EXPECT_TRUE(ac.getSwingVertical()); +} + +TEST(TestTcl112AcClass, Turbo) { + IRTcl112Ac ac(0); + ac.begin(); + + ac.setFan(kTcl112AcFanLow); + ac.setSwingHorizontal(false); + + ac.setTurbo(false); + EXPECT_FALSE(ac.getTurbo()); + EXPECT_FALSE(ac.getSwingVertical()); + EXPECT_EQ(kTcl112AcFanLow, ac.getFan()); + + ac.setTurbo(true); + EXPECT_TRUE(ac.getTurbo()); + EXPECT_TRUE(ac.getSwingVertical()); + EXPECT_EQ(kTcl112AcFanHigh, ac.getFan()); + + ac.setTurbo(false); + EXPECT_FALSE(ac.getTurbo()); + ac.setTurbo(true); + EXPECT_TRUE(ac.getTurbo()); +} + +TEST(TestTcl112AcClass, FanSpeed) { + IRTcl112Ac ac(0); + ac.begin(); + + // Unexpected value should default to Auto. + ac.setFan(255); + EXPECT_EQ(kTcl112AcFanAuto, ac.getFan()); + + ac.setFan(kTcl112AcFanLow); + EXPECT_EQ(kTcl112AcFanLow, ac.getFan()); + ac.setFan(kTcl112AcFanMed); + EXPECT_EQ(kTcl112AcFanMed, ac.getFan()); + ac.setFan(kTcl112AcFanHigh); + EXPECT_EQ(kTcl112AcFanHigh, ac.getFan()); + ac.setFan(kTcl112AcFanAuto); + EXPECT_EQ(kTcl112AcFanAuto, ac.getFan()); + + // Beyond High should default to Auto. + ac.setFan(kTcl112AcFanHigh + 1); + EXPECT_EQ(kTcl112AcFanAuto, ac.getFan()); +} diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Teco_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Teco_test.cpp new file mode 100644 index 000000000..6b03a671d --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Teco_test.cpp @@ -0,0 +1,358 @@ +// Copyright 2019 David Conran + +#include "ir_Teco.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "gtest/gtest.h" + +// General housekeeping +TEST(TestTeco, Housekeeping) { + ASSERT_EQ("TECO", typeToString(TECO)); + ASSERT_FALSE(hasACState(TECO)); // Uses uint64_t, not uint8_t*. +} + +// Tests for sendTeco() + +// Test sending typical data only. +TEST(TestSendTeco, SendDataOnly) { + IRsendTest irsend(0); + irsend.begin(); + + irsend.reset(); + irsend.sendTeco(0x250002BC9); + EXPECT_EQ( + "f38000d50" + "m9000s4440" + "m620s1650m620s580m620s580m620s1650m620s580m620s580m620s1650m620s1650" + "m620s1650m620s1650m620s580m620s1650m620s580m620s1650m620s580m620s580" + "m620s580m620s580m620s580m620s580m620s580m620s580m620s580m620s580" + "m620s580m620s580m620s580m620s580m620s1650m620s580m620s1650m620s580" + "m620s580m620s1650m620s580" + "m620s100000", + irsend.outputStr()); +} + +// Test sending typical data with repeats. +TEST(TestSendTeco, SendWithRepeats) { + IRsendTest irsend(0); + irsend.begin(); + + irsend.reset(); + irsend.sendTeco(0x250002BC9, kTecoBits, 2); // two repeats. + EXPECT_EQ( + "f38000d50" + "m9000s4440" + "m620s1650m620s580m620s580m620s1650m620s580m620s580m620s1650m620s1650" + "m620s1650m620s1650m620s580m620s1650m620s580m620s1650m620s580m620s580" + "m620s580m620s580m620s580m620s580m620s580m620s580m620s580m620s580" + "m620s580m620s580m620s580m620s580m620s1650m620s580m620s1650m620s580" + "m620s580m620s1650m620s580" + "m620s100000" + "m9000s4440" + "m620s1650m620s580m620s580m620s1650m620s580m620s580m620s1650m620s1650" + "m620s1650m620s1650m620s580m620s1650m620s580m620s1650m620s580m620s580" + "m620s580m620s580m620s580m620s580m620s580m620s580m620s580m620s580" + "m620s580m620s580m620s580m620s580m620s1650m620s580m620s1650m620s580" + "m620s580m620s1650m620s580" + "m620s100000" + "m9000s4440" + "m620s1650m620s580m620s580m620s1650m620s580m620s580m620s1650m620s1650" + "m620s1650m620s1650m620s580m620s1650m620s580m620s1650m620s580m620s580" + "m620s580m620s580m620s580m620s580m620s580m620s580m620s580m620s580" + "m620s580m620s580m620s580m620s580m620s1650m620s580m620s1650m620s580" + "m620s580m620s1650m620s580" + "m620s100000", + irsend.outputStr()); +} + + +// Tests for IRTeco class. + +TEST(TestTecoACClass, Power) { + IRTecoAc ac(0); + ac.begin(); + + ac.setPower(true); + EXPECT_TRUE(ac.getPower()); + + ac.setPower(false); + EXPECT_EQ(false, ac.getPower()); + + ac.setPower(true); + EXPECT_TRUE(ac.getPower()); + + ac.off(); + EXPECT_EQ(false, ac.getPower()); + + ac.on(); + EXPECT_TRUE(ac.getPower()); +} + +TEST(TestTecoACClass, OperatingMode) { + IRTecoAc ac(0); + ac.begin(); + + ac.setMode(kTecoAuto); + EXPECT_EQ(kTecoAuto, ac.getMode()); + + ac.setMode(kTecoCool); + EXPECT_EQ(kTecoCool, ac.getMode()); + + ac.setMode(kTecoHeat); + EXPECT_EQ(kTecoHeat, ac.getMode()); + + ac.setMode(kTecoFan); + EXPECT_EQ(kTecoFan, ac.getMode()); + + ac.setMode(kTecoDry); + EXPECT_EQ(kTecoDry, ac.getMode()); + + ac.setMode(kTecoAuto - 1); + EXPECT_EQ(kTecoAuto, ac.getMode()); + + ac.setMode(kTecoCool); + EXPECT_EQ(kTecoCool, ac.getMode()); + + ac.setMode(kTecoHeat + 1); + EXPECT_EQ(kTecoAuto, ac.getMode()); + + ac.setMode(255); + EXPECT_EQ(kTecoAuto, ac.getMode()); +} + +TEST(TestTecoACClass, Temperature) { + IRTecoAc ac(0); + ac.begin(); + + ac.setTemp(kTecoMinTemp); + EXPECT_EQ(kTecoMinTemp, ac.getTemp()); + + ac.setTemp(kTecoMinTemp + 1); + EXPECT_EQ(kTecoMinTemp + 1, ac.getTemp()); + + ac.setTemp(kTecoMaxTemp); + EXPECT_EQ(kTecoMaxTemp, ac.getTemp()); + + ac.setTemp(kTecoMinTemp - 1); + EXPECT_EQ(kTecoMinTemp, ac.getTemp()); + + ac.setTemp(kTecoMaxTemp + 1); + EXPECT_EQ(kTecoMaxTemp, ac.getTemp()); + + ac.setTemp(23); + EXPECT_EQ(23, ac.getTemp()); + + ac.setTemp(0); + EXPECT_EQ(kTecoMinTemp, ac.getTemp()); + + ac.setTemp(255); + EXPECT_EQ(kTecoMaxTemp, ac.getTemp()); +} + +TEST(TestTecoACClass, FanSpeed) { + IRTecoAc ac(0); + ac.begin(); + ac.setFan(kTecoFanLow); + + ac.setFan(kTecoFanAuto); + EXPECT_EQ(kTecoFanAuto, ac.getFan()); + + ac.setFan(kTecoFanLow); + EXPECT_EQ(kTecoFanLow, ac.getFan()); + ac.setFan(kTecoFanMed); + EXPECT_EQ(kTecoFanMed, ac.getFan()); + ac.setFan(kTecoFanHigh); + EXPECT_EQ(kTecoFanHigh, ac.getFan()); + + ac.setFan(kTecoFanHigh); + EXPECT_EQ(kTecoFanHigh, ac.getFan()); +} + +TEST(TestTecoACClass, Swing) { + IRTecoAc ac(0); + ac.begin(); + + ac.setSwing(true); + EXPECT_TRUE(ac.getSwing()); + + ac.setSwing(false); + EXPECT_EQ(false, ac.getSwing()); + + ac.setSwing(true); + EXPECT_TRUE(ac.getSwing()); +} + +TEST(TestTecoACClass, Sleep) { + IRTecoAc ac(0); + ac.begin(); + + ac.setSleep(true); + EXPECT_TRUE(ac.getSleep()); + + ac.setSleep(false); + EXPECT_EQ(false, ac.getSleep()); + + ac.setSleep(true); + EXPECT_TRUE(ac.getSleep()); +} + +TEST(TestTecoACClass, MessageConstuction) { + IRTecoAc ac(0); + + EXPECT_EQ( + "Power: Off, Mode: 0 (AUTO), Temp: 16C, Fan: 0 (Auto), Sleep: Off, " + "Swing: Off", + ac.toString()); + ac.setPower(true); + ac.setMode(kTecoCool); + ac.setTemp(21); + ac.setFan(kTecoFanHigh); + ac.setSwing(false); + EXPECT_EQ( + "Power: On, Mode: 1 (COOL), Temp: 21C, Fan: 3 (High), Sleep: Off, " + "Swing: Off", + ac.toString()); + ac.setSwing(true); + EXPECT_EQ( + "Power: On, Mode: 1 (COOL), Temp: 21C, Fan: 3 (High), Sleep: Off, " + "Swing: On", + ac.toString()); + ac.setSwing(false); + ac.setFan(kTecoFanLow); + ac.setSleep(true); + ac.setMode(kTecoHeat); + EXPECT_EQ( + "Power: On, Mode: 4 (HEAT), Temp: 21C, Fan: 1 (Low), Sleep: On, " + "Swing: Off", + ac.toString()); + ac.setSleep(false); + EXPECT_EQ( + "Power: On, Mode: 4 (HEAT), Temp: 21C, Fan: 1 (Low), Sleep: Off, " + "Swing: Off", + ac.toString()); + ac.setTemp(25); + EXPECT_EQ( + "Power: On, Mode: 4 (HEAT), Temp: 25C, Fan: 1 (Low), Sleep: Off, " + "Swing: Off", + ac.toString()); +} + +TEST(TestTecoACClass, ReconstructKnownMessage) { + IRTecoAc ac(0); + + const uint64_t expected = 0x250002BC9; + ASSERT_FALSE(ac.getRaw() == expected); + ac.setPower(true); + ac.setMode(kTecoCool); + ac.setTemp(27); + ac.setFan(kTecoFanAuto); + ac.setSleep(true); + ac.setSwing(true); + EXPECT_EQ(expected, ac.getRaw()); + EXPECT_EQ( + "Power: On, Mode: 1 (COOL), Temp: 27C, Fan: 0 (Auto), Sleep: On, " + "Swing: On", + ac.toString()); +} + +// Tests for decodeTeco(). + +// Decode normal "synthetic" messages. +TEST(TestDecodeTeco, NormalDecodeWithStrict) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + // With the specific decoder. + uint64_t expectedState = kTecoReset; + irsend.reset(); + irsend.sendTeco(expectedState); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decodeTeco(&irsend.capture, kTecoBits, true)); + EXPECT_EQ(TECO, irsend.capture.decode_type); + EXPECT_EQ(kTecoBits, irsend.capture.bits); + EXPECT_FALSE(irsend.capture.repeat); + EXPECT_EQ(expectedState, irsend.capture.value); + EXPECT_EQ(0, irsend.capture.address); + EXPECT_EQ(0, irsend.capture.command); + + // With the all the decoders. + irsend.reset(); + irsend.sendTeco(expectedState); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(TECO, irsend.capture.decode_type); + EXPECT_EQ(kTecoBits, irsend.capture.bits); + EXPECT_FALSE(irsend.capture.repeat); + EXPECT_EQ(expectedState, irsend.capture.value); + EXPECT_EQ(0, irsend.capture.address); + EXPECT_EQ(0, irsend.capture.command); + + IRTecoAc ac(0); + ac.begin(); + ac.setRaw(irsend.capture.value); + EXPECT_EQ( + "Power: Off, Mode: 0 (AUTO), Temp: 16C, Fan: 0 (Auto), Sleep: Off, " + "Swing: Off", + ac.toString()); +} + +// Decode a real message from Raw Data. +TEST(TestDecodeTeco, RealNormalExample) { + IRsendTest irsend(0); + IRrecv irrecv(0); + IRTecoAc ac(0); + irsend.begin(); + + uint16_t rawData1[73] = { + 9076, 4442, 670, 1620, 670, 516, 670, 516, 666, 1626, 670, 516, + 664, 520, 666, 1626, 666, 1626, 664, 1626, 666, 1626, 666, 520, + 666, 1626, 666, 520, 666, 1626, 666, 520, 666, 516, 670, 514, + 670, 516, 666, 520, 670, 516, 666, 520, 666, 516, 672, 514, 670, + 516, 666, 520, 666, 516, 672, 514, 670, 516, 666, 1624, 666, 520, + 666, 1626, 666, 520, 666, 516, 672, 1620, 670, 516, 670}; + uint64_t expected1 = 0b01001010000000000000010101111001001; // 0x250002BC9 + irsend.reset(); + irsend.sendRaw(rawData1, 73, 38); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(TECO, irsend.capture.decode_type); + EXPECT_EQ(kTecoBits, irsend.capture.bits); + EXPECT_FALSE(irsend.capture.repeat); + EXPECT_EQ(expected1, irsend.capture.value); + EXPECT_EQ(0, irsend.capture.address); + EXPECT_EQ(0, irsend.capture.command); + ac.begin(); + ac.setRaw(irsend.capture.value); + EXPECT_EQ( + "Power: On, Mode: 1 (COOL), Temp: 27C, Fan: 0 (Auto), Sleep: On, " + "Swing: On", + ac.toString()); + + uint16_t rawData2[73] = { + 9048, 4472, 636, 548, 636, 1654, 638, 546, 642, 1650, 642, 546, 638, + 1654, 638, 1654, 638, 546, 638, 1654, 636, 546, 642, 1650, 640, 548, + 636, 548, 638, 546, 636, 546, 642, 542, 642, 546, 638, 546, 638, 546, + 636, 548, 642, 542, 642, 546, 636, 548, 636, 546, 642, 542, 642, 546, + 638, 546, 638, 546, 636, 1654, 642, 542, 642, 1650, 642, 546, 638, 546, + 638, 1654, 638, 546, 642}; // TECO 25000056A + uint64_t expected2 = 0b01001010000000000000000010101101010; // 0x25000056A + irsend.reset(); + irsend.sendRaw(rawData2, 73, 38); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(TECO, irsend.capture.decode_type); + EXPECT_EQ(kTecoBits, irsend.capture.bits); + EXPECT_FALSE(irsend.capture.repeat); + EXPECT_EQ(expected2, irsend.capture.value); + EXPECT_EQ(0, irsend.capture.address); + EXPECT_EQ(0, irsend.capture.command); + ac.begin(); + ac.setRaw(irsend.capture.value); + EXPECT_EQ( + "Power: On, Mode: 2 (DRY), Temp: 21C, Fan: 2 (Med), Sleep: Off, " + "Swing: On", + ac.toString()); +} diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_Toshiba_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Toshiba_test.cpp similarity index 99% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_Toshiba_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_Toshiba_test.cpp index b5e1e07a9..d74866f92 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_Toshiba_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Toshiba_test.cpp @@ -18,6 +18,7 @@ TEST(TestSendToshibaAC, SendDataOnly) { irsend.reset(); irsend.sendToshibaAC(toshiba_code); EXPECT_EQ( + "f38000d50" "m4400s4300" "m543s1623m543s1623m543s1623m543s1623m543s472m543s472m543s1623m543s472" "m543s472m543s472m543s472m543s472m543s1623m543s1623m543s472m543s1623" @@ -54,6 +55,7 @@ TEST(TestSendToshibaAC, SendWithRepeats) { irsend.sendToshibaAC(toshiba_code, kToshibaACStateLength, 0); EXPECT_EQ( + "f38000d50" "m4400s4300" "m543s1623m543s1623m543s1623m543s1623m543s472m543s472m543s1623m543s472" "m543s472m543s472m543s472m543s472m543s1623m543s1623m543s472m543s1623" @@ -70,6 +72,7 @@ TEST(TestSendToshibaAC, SendWithRepeats) { irsend.reset(); irsend.sendToshibaAC(toshiba_code, kToshibaACStateLength, 2); EXPECT_EQ( + "f38000d50" "m4400s4300" "m543s1623m543s1623m543s1623m543s1623m543s472m543s472m543s1623m543s472" "m543s472m543s472m543s472m543s472m543s1623m543s1623m543s472m543s1623" @@ -122,6 +125,7 @@ TEST(TestSendToshibaAC, SendUnexpectedSizes) { irsend.reset(); irsend.sendToshibaAC(toshiba_long_code, kToshibaACStateLength + 1); ASSERT_EQ( + "f38000d50" "m4400s4300" "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s1623" "m543s472m543s472m543s472m543s472m543s472m543s472m543s1623m543s472" @@ -380,6 +384,7 @@ TEST(TestToshibaACClass, MessageConstuction) { irsend.reset(); irsend.sendToshibaAC(toshiba.getRaw()); EXPECT_EQ( + "f38000d50" "m4400s4300" "m543s1623m543s1623m543s1623m543s1623m543s472m543s472m543s1623m543s472" "m543s472m543s472m543s472m543s472m543s1623m543s1623m543s472m543s1623" @@ -415,6 +420,7 @@ TEST(TestToshibaACClass, MessageConstuction) { irsend.reset(); irsend.sendToshibaAC(toshiba.getRaw()); EXPECT_EQ( + "f38000d50" "m4400s4300" "m543s1623m543s1623m543s1623m543s1623m543s472m543s472m543s1623m543s472" "m543s472m543s472m543s472m543s472m543s1623m543s1623m543s472m543s1623" @@ -450,6 +456,7 @@ TEST(TestToshibaACClass, MessageConstuction) { irsend.reset(); irsend.sendToshibaAC(toshiba.getRaw()); EXPECT_EQ( + "f38000d50" "m4400s4300" "m543s1623m543s1623m543s1623m543s1623m543s472m543s472m543s1623m543s472" "m543s472m543s472m543s472m543s472m543s1623m543s1623m543s472m543s1623" diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Vestel_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Vestel_test.cpp new file mode 100644 index 000000000..077a0a25e --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Vestel_test.cpp @@ -0,0 +1,513 @@ +// Copyright 2019 David Conran + +#include "ir_Vestel.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "gtest/gtest.h" + +// Tests for sendVestelAc() + +// Test sending typical data only. +TEST(TestSendVestelAc, SendDataOnly) { + IRsendTest irsend(0); + irsend.begin(); + + irsend.reset(); + irsend.sendVestelAc(0x0F00D9001FEF201ULL); + EXPECT_EQ( + "f38000d50" + "m3110s9066" + "m520s1535m520s480m520s480m520s480m520s480m520s480m520s480m520s480" + "m520s480m520s1535m520s480m520s480m520s1535m520s1535m520s1535m520s1535" + "m520s480m520s1535m520s1535m520s1535m520s1535m520s1535m520s1535m520s1535" + "m520s1535m520s480m520s480m520s480m520s480m520s480m520s480m520s480" + "m520s480m520s480m520s480m520s480m520s1535m520s480m520s480m520s1535" + "m520s1535m520s480m520s1535m520s1535m520s480m520s480m520s480m520s480" + "m520s480m520s480m520s480m520s480m520s1535m520s1535m520s1535m520s1535" + "m520s100000", + irsend.outputStr()); +} + +// Test sending typical data with repeats. +TEST(TestSendVestelAc, SendWithRepeats) { + IRsendTest irsend(0); + irsend.begin(); + + irsend.reset(); + irsend.sendVestelAc(0x0F00D9001FEF201ULL, kVestelAcBits, 2); // two repeats. + EXPECT_EQ( + "f38000d50" + "m3110s9066" + "m520s1535m520s480m520s480m520s480m520s480m520s480m520s480m520s480" + "m520s480m520s1535m520s480m520s480m520s1535m520s1535m520s1535m520s1535" + "m520s480m520s1535m520s1535m520s1535m520s1535m520s1535m520s1535m520s1535" + "m520s1535m520s480m520s480m520s480m520s480m520s480m520s480m520s480" + "m520s480m520s480m520s480m520s480m520s1535m520s480m520s480m520s1535" + "m520s1535m520s480m520s1535m520s1535m520s480m520s480m520s480m520s480" + "m520s480m520s480m520s480m520s480m520s1535m520s1535m520s1535m520s1535" + "m520s100000" + "m3110s9066" + "m520s1535m520s480m520s480m520s480m520s480m520s480m520s480m520s480" + "m520s480m520s1535m520s480m520s480m520s1535m520s1535m520s1535m520s1535" + "m520s480m520s1535m520s1535m520s1535m520s1535m520s1535m520s1535m520s1535" + "m520s1535m520s480m520s480m520s480m520s480m520s480m520s480m520s480" + "m520s480m520s480m520s480m520s480m520s1535m520s480m520s480m520s1535" + "m520s1535m520s480m520s1535m520s1535m520s480m520s480m520s480m520s480" + "m520s480m520s480m520s480m520s480m520s1535m520s1535m520s1535m520s1535" + "m520s100000" + "m3110s9066" + "m520s1535m520s480m520s480m520s480m520s480m520s480m520s480m520s480" + "m520s480m520s1535m520s480m520s480m520s1535m520s1535m520s1535m520s1535" + "m520s480m520s1535m520s1535m520s1535m520s1535m520s1535m520s1535m520s1535" + "m520s1535m520s480m520s480m520s480m520s480m520s480m520s480m520s480" + "m520s480m520s480m520s480m520s480m520s1535m520s480m520s480m520s1535" + "m520s1535m520s480m520s1535m520s1535m520s480m520s480m520s480m520s480" + "m520s480m520s480m520s480m520s480m520s1535m520s1535m520s1535m520s1535" + "m520s100000", + irsend.outputStr()); +} + +// Tests for IRVestelAc class. + +TEST(TestVestelAcClass, Power) { + IRVestelAc ac(0); + ac.begin(); + + ac.setPower(true); + EXPECT_TRUE(ac.getPower()); + + ac.setPower(false); + EXPECT_EQ(false, ac.getPower()); + + ac.setPower(true); + EXPECT_TRUE(ac.getPower()); + + ac.off(); + EXPECT_EQ(false, ac.getPower()); + + ac.on(); + EXPECT_TRUE(ac.getPower()); + EXPECT_FALSE(ac.isTimeCommand()); +} + +TEST(TestVestelAcClass, OperatingMode) { + IRVestelAc ac(0); + ac.begin(); + + ac.setMode(kVestelAcAuto); + EXPECT_EQ(kVestelAcAuto, ac.getMode()); + + ac.setMode(kVestelAcCool); + EXPECT_EQ(kVestelAcCool, ac.getMode()); + + ac.setMode(kVestelAcHeat); + EXPECT_EQ(kVestelAcHeat, ac.getMode()); + + ac.setMode(kVestelAcFan); + EXPECT_EQ(kVestelAcFan, ac.getMode()); + + ac.setMode(kVestelAcDry); + EXPECT_EQ(kVestelAcDry, ac.getMode()); + + ac.setMode(kVestelAcAuto - 1); + EXPECT_EQ(kVestelAcAuto, ac.getMode()); + + ac.setMode(kVestelAcCool); + EXPECT_EQ(kVestelAcCool, ac.getMode()); + + ac.setMode(kVestelAcHeat + 1); + EXPECT_EQ(kVestelAcAuto, ac.getMode()); + + ac.setMode(255); + EXPECT_EQ(kVestelAcAuto, ac.getMode()); + EXPECT_FALSE(ac.isTimeCommand()); +} + +TEST(TestVestelAcClass, Temperature) { + IRVestelAc ac(0); + ac.begin(); + + ac.setTemp(kVestelAcMinTempC); + EXPECT_EQ(kVestelAcMinTempC, ac.getTemp()); + + ac.setTemp(kVestelAcMinTempC + 1); + EXPECT_EQ(kVestelAcMinTempC + 1, ac.getTemp()); + + ac.setTemp(kVestelAcMaxTemp); + EXPECT_EQ(kVestelAcMaxTemp, ac.getTemp()); + + ac.setTemp(kVestelAcMinTempC - 1); + EXPECT_EQ(kVestelAcMinTempC, ac.getTemp()); + + ac.setTemp(kVestelAcMaxTemp + 1); + EXPECT_EQ(kVestelAcMaxTemp, ac.getTemp()); + + ac.setTemp(23); + EXPECT_EQ(23, ac.getTemp()); + + ac.setTemp(0); + EXPECT_EQ(kVestelAcMinTempC, ac.getTemp()); + + ac.setTemp(255); + EXPECT_EQ(kVestelAcMaxTemp, ac.getTemp()); + EXPECT_FALSE(ac.isTimeCommand()); +} + +TEST(TestVestelAcClass, FanSpeed) { + IRVestelAc ac(0); + ac.begin(); + ac.setFan(kVestelAcFanLow); + + ac.setFan(kVestelAcFanAuto); + EXPECT_EQ(kVestelAcFanAuto, ac.getFan()); + + ac.setFan(kVestelAcFanLow); + EXPECT_EQ(kVestelAcFanLow, ac.getFan()); + ac.setFan(kVestelAcFanMed); + EXPECT_EQ(kVestelAcFanMed, ac.getFan()); + ac.setFan(kVestelAcFanHigh); + EXPECT_EQ(kVestelAcFanHigh, ac.getFan()); + + ac.setFan(kVestelAcFanHigh); + EXPECT_EQ(kVestelAcFanHigh, ac.getFan()); + EXPECT_FALSE(ac.isTimeCommand()); +} + +TEST(TestVestelAcClass, Swing) { + IRVestelAc ac(0); + ac.begin(); + + ac.setSwing(true); + EXPECT_TRUE(ac.getSwing()); + + ac.setSwing(false); + EXPECT_EQ(false, ac.getSwing()); + + ac.setSwing(true); + EXPECT_TRUE(ac.getSwing()); + EXPECT_FALSE(ac.isTimeCommand()); +} + +TEST(TestVestelAcClass, Ion) { + IRVestelAc ac(0); + ac.begin(); + + ac.setIon(true); + EXPECT_TRUE(ac.getIon()); + + ac.setIon(false); + EXPECT_EQ(false, ac.getIon()); + + ac.setIon(true); + EXPECT_TRUE(ac.getIon()); + EXPECT_FALSE(ac.isTimeCommand()); +} + +TEST(TestVestelAcClass, Turbo) { + IRVestelAc ac(0); + ac.begin(); + + ac.setTurbo(true); + EXPECT_TRUE(ac.getTurbo()); + + ac.setTurbo(false); + EXPECT_EQ(false, ac.getTurbo()); + + ac.setTurbo(true); + EXPECT_TRUE(ac.getTurbo()); + EXPECT_FALSE(ac.isTimeCommand()); +} + +TEST(TestVestelAcClass, Sleep) { + IRVestelAc ac(0); + ac.begin(); + + ac.setSleep(true); + EXPECT_TRUE(ac.getSleep()); + + ac.setSleep(false); + EXPECT_EQ(false, ac.getSleep()); + + ac.setSleep(true); + EXPECT_TRUE(ac.getSleep()); + EXPECT_FALSE(ac.isTimeCommand()); +} + +TEST(TestVestelAcClass, Time) { + IRVestelAc ac(0); + ac.begin(); + + ac.setTime(0); + EXPECT_EQ(0, ac.getTime()); + EXPECT_TRUE(ac.isTimeCommand()); + + ac.setTime(1); + EXPECT_EQ(1, ac.getTime()); + + ac.setTime(1234); + EXPECT_EQ(1234, ac.getTime()); + + ac.setTime(23 * 60 + 59); + EXPECT_EQ(23 * 60 + 59, ac.getTime()); +} + +TEST(TestVestelAcClass, OnTimer) { + IRVestelAc ac(0); + ac.begin(); + + ac.setOnTimer(0); + EXPECT_EQ(0, ac.getOnTimer()); + EXPECT_TRUE(ac.isTimeCommand()); + + ac.setOnTimer(1); + EXPECT_EQ(0, ac.getOnTimer()); + + ac.setOnTimer(10); + EXPECT_EQ(10, ac.getOnTimer()); + + ac.setOnTimer(12 * 60 + 15); // we will round down to 10 min increments. + EXPECT_EQ(12 * 60 + 10, ac.getOnTimer()); + + ac.setOnTimer(23 * 60 + 50); + EXPECT_EQ(23 * 60 + 50, ac.getOnTimer()); +} + +TEST(TestVestelAcClass, OffTimer) { + IRVestelAc ac(0); + ac.begin(); + + ac.setOffTimer(0); + EXPECT_EQ(0, ac.getOffTimer()); + EXPECT_TRUE(ac.isTimeCommand()); + + ac.setOffTimer(1); + EXPECT_EQ(0, ac.getOffTimer()); + + ac.setOffTimer(10); + EXPECT_EQ(10, ac.getOffTimer()); + + ac.setOffTimer(12 * 60 + 15); // we will round down to 10 min increments. + EXPECT_EQ(12 * 60 + 10, ac.getOffTimer()); + + ac.setOffTimer(23 * 60 + 50); + EXPECT_EQ(23 * 60 + 50, ac.getOffTimer()); +} + +TEST(TestVestelAcClass, Timer) { + IRVestelAc ac(0); + ac.begin(); + + ac.setTimer(0); + EXPECT_EQ(0, ac.getTimer()); + EXPECT_EQ(0, ac.getOnTimer()); + EXPECT_TRUE(ac.isTimeCommand()); + + ac.setTimer(10); + EXPECT_EQ(10, ac.getTimer()); + EXPECT_EQ(0, ac.getOnTimer()); + + ac.setTimer(12 * 60 + 15); // we will round down to 10 min increments. + EXPECT_EQ(12 * 60 + 10, ac.getTimer()); + EXPECT_EQ(0, ac.getOnTimer()); + + ac.setTimer(23 * 60 + 50); + EXPECT_EQ(23 * 60 + 50, ac.getTimer()); + EXPECT_EQ(0, ac.getOnTimer()); +} + +TEST(TestVestelAcClass, MessageConstuction) { + IRVestelAc ac(0); + + EXPECT_EQ( + "Power: On, Mode: 0 (AUTO), Temp: 25C, Fan: 13 (AUTO HOT), Sleep: Off, " + "Turbo: Off, Ion: Off, Swing: Off", + ac.toString()); + ac.setMode(kVestelAcCool); + ac.setTemp(21); + ac.setFan(kVestelAcFanHigh); + EXPECT_FALSE(ac.isTimeCommand()); + EXPECT_EQ( + "Power: On, Mode: 1 (COOL), Temp: 21C, Fan: 11 (HIGH), Sleep: Off, " + "Turbo: Off, Ion: Off, Swing: Off", + ac.toString()); + ac.setSwing(true); + ac.setIon(true); + ac.setTurbo(true); + EXPECT_FALSE(ac.isTimeCommand()); + EXPECT_EQ( + "Power: On, Mode: 1 (COOL), Temp: 21C, Fan: 11 (HIGH), Sleep: Off, " + "Turbo: On, Ion: On, Swing: On", + ac.toString()); + + // Now change a few already set things. + ac.setSleep(true); + ac.setMode(kVestelAcHeat); + EXPECT_EQ( + "Power: On, Mode: 4 (HEAT), Temp: 21C, Fan: 11 (HIGH), Sleep: On, " + "Turbo: Off, Ion: On, Swing: On", + ac.toString()); + EXPECT_FALSE(ac.isTimeCommand()); + + ac.setTemp(25); + ac.setPower(false); + EXPECT_EQ( + "Power: Off, Mode: 4 (HEAT), Temp: 25C, Fan: 11 (HIGH), Sleep: On, " + "Turbo: Off, Ion: On, Swing: On", + ac.toString()); + EXPECT_FALSE(ac.isTimeCommand()); + + // Check that the checksum is valid. + EXPECT_TRUE(IRVestelAc::validChecksum(ac.getRaw())); + ac.setTime(23 * 60 + 59); + EXPECT_TRUE(ac.isTimeCommand()); + EXPECT_EQ( + "Time: 23:59, Timer: Off, On Timer: Off, Off Timer: Off", + ac.toString()); + ac.setTimer(8 * 60 + 0); + EXPECT_TRUE(ac.isTimeCommand()); + EXPECT_EQ( + "Time: 23:59, Timer: 8:00, On Timer: Off, Off Timer: Off", + ac.toString()); + ac.setOnTimer(7 * 60 + 40); + EXPECT_EQ( + "Time: 23:59, Timer: Off, On Timer: 7:40, Off Timer: Off", + ac.toString()); + ac.setOffTimer(17 * 60 + 10); + EXPECT_EQ( + "Time: 23:59, Timer: Off, On Timer: 7:40, Off Timer: 17:10", + ac.toString()); + ac.setTimer(8 * 60 + 0); + EXPECT_EQ( + "Time: 23:59, Timer: 8:00, On Timer: Off, Off Timer: Off", + ac.toString()); + ac.setTimer(0); + EXPECT_EQ( + "Time: 23:59, Timer: Off, On Timer: Off, Off Timer: Off", + ac.toString()); + ac.on(); + EXPECT_FALSE(ac.isTimeCommand()); + EXPECT_EQ( + "Power: On, Mode: 4 (HEAT), Temp: 25C, Fan: 11 (HIGH), Sleep: On, " + "Turbo: Off, Ion: On, Swing: On", + ac.toString()); +} + +// Tests for decodeVestelAc(). + +// Decode normal "synthetic" messages. +TEST(TestDecodeVestelAc, NormalDecodeWithStrict) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + // With the specific decoder. + uint64_t expectedState = 0x0F00D9001FEF201ULL; + irsend.reset(); + irsend.sendVestelAc(expectedState); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decodeVestelAc(&irsend.capture, kVestelAcBits, true)); + EXPECT_EQ(VESTEL_AC, irsend.capture.decode_type); + EXPECT_EQ(kVestelAcBits, irsend.capture.bits); + EXPECT_FALSE(irsend.capture.repeat); + EXPECT_EQ(expectedState, irsend.capture.value); + EXPECT_EQ(0, irsend.capture.address); + EXPECT_EQ(0, irsend.capture.command); + + // With the all the decoders. + irsend.reset(); + irsend.sendVestelAc(expectedState); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(VESTEL_AC, irsend.capture.decode_type); + EXPECT_EQ(kVestelAcBits, irsend.capture.bits); + EXPECT_FALSE(irsend.capture.repeat); + EXPECT_EQ(expectedState, irsend.capture.value); + EXPECT_EQ(0, irsend.capture.address); + EXPECT_EQ(0, irsend.capture.command); + + IRVestelAc ac(0); + ac.begin(); + ac.setRaw(irsend.capture.value); + EXPECT_EQ( + "Power: On, Mode: 0 (AUTO), Temp: 25C, Fan: 13 (AUTO HOT), Sleep: Off, " + "Turbo: Off, Ion: Off, Swing: Off", + ac.toString()); +} + +// Decode a real message from Raw Data. +TEST(TestDecodeVestelAc, RealNormalExample) { + IRsendTest irsend(0); + IRrecv irrecv(0); + IRVestelAc ac(0); + irsend.begin(); + + uint16_t rawData[115] = { + 3098, 9080, 548, 1538, 526, 492, 526, 468, 524, 468, 526, 468, + 550, 466, 526, 466, 526, 504, 540, 466, 526, 1538, 526, 466, + 526, 466, 552, 1540, 522, 466, 526, 492, 526, 544, 526, 1536, + 526, 1536, 552, 1536, 526, 1536, 552, 1536, 552, 1536, 526, 1536, + 526, 1574, 542, 1536, 526, 492, 526, 466, 526, 494, 524, 468, + 524, 468, 526, 492, 526, 502, 540, 468, 524, 494, 524, 468, + 526, 468, 524, 468, 526, 492, 526, 468, 524, 520, 524, 1538, + 524, 468, 524, 468, 524, 468, 524, 468, 524, 468, 524, 1538, + 524, 506, 538, 468, 524, 468, 524, 1538, 524, 468, 550, 1538, + 550, 1538, 524, 1538, 534, 1528, 544}; // VESTEL_AC + irsend.reset(); + irsend.sendRaw(rawData, 115, 38); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(VESTEL_AC, irsend.capture.decode_type); + EXPECT_EQ(kVestelAcBits, irsend.capture.bits); + EXPECT_FALSE(irsend.capture.repeat); + EXPECT_EQ(0xF4410001FF1201ULL, irsend.capture.value); + EXPECT_EQ(0, irsend.capture.address); + EXPECT_EQ(0, irsend.capture.command); + ac.begin(); + ac.setRaw(irsend.capture.value); + EXPECT_EQ( + "Power: On, Mode: 4 (HEAT), Temp: 16C, Fan: 1 (AUTO), Sleep: Off, " + "Turbo: Off, Ion: On, Swing: Off", + ac.toString()); +} + +TEST(TestDecodeVestelAc, RealTimerExample) { + IRsendTest irsend(0); + IRrecv irrecv(0); + IRVestelAc ac(0); + irsend.begin(); + + uint16_t rawData[115] = { + 3022, 9080, 546, 1536, 526, 466, 526, 492, 526, 468, 526, 492, + 524, 468, 524, 494, 524, 504, 540, 492, 524, 1538, 526, 468, + 524, 492, 526, 466, 552, 1536, 526, 1536, 526, 1570, 542, 492, + 524, 1538, 550, 1538, 524, 1536, 526, 494, 524, 466, 526, 468, + 524, 1574, 540, 1536, 550, 1536, 526, 468, 550, 1536, 526, 492, + 526, 468, 524, 492, 526, 518, 526, 1536, 552, 1536, 550, 1536, + 526, 494, 550, 1538, 526, 492, 524, 1538, 526, 504, 540, 466, + 526, 1536, 526, 1536, 526, 468, 550, 1538, 524, 468, 524, 1538, + 550, 1574, 540, 468, 550, 1538, 526, 492, 524, 468, 526, 466, + 526, 468, 524, 494, 524, 468, 546}; // VESTEL_AC 2D6570B8EE201 + irsend.reset(); + irsend.sendRaw(rawData, 115, 38); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(VESTEL_AC, irsend.capture.decode_type); + EXPECT_EQ(kVestelAcBits, irsend.capture.bits); + EXPECT_FALSE(irsend.capture.repeat); + EXPECT_EQ(0x2D6570B8EE201ULL, irsend.capture.value); + EXPECT_EQ(0, irsend.capture.address); + EXPECT_EQ(0, irsend.capture.command); + ac.begin(); + ac.setRaw(irsend.capture.value); + EXPECT_EQ( + "Time: 5:45, Timer: Off, On Timer: 14:00, Off Timer: 23:00", + ac.toString()); +} + +// General housekeeping +TEST(TestDecodeVestelAc, Housekeeping) { + ASSERT_EQ("VESTEL_AC", typeToString(VESTEL_AC)); + ASSERT_FALSE(hasACState(VESTEL_AC)); // Uses uint64_t, not uint8_t*. +} diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Whirlpool_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Whirlpool_test.cpp new file mode 100644 index 000000000..e282989f0 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Whirlpool_test.cpp @@ -0,0 +1,585 @@ +// Copyright 2018 David Conran + +#include "ir_Whirlpool.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "gtest/gtest.h" + +// Tests for sendWhirlpoolAC(). + +// Test sending typical data only. +TEST(TestSendWhirlpoolAC, SendDataOnly) { + IRsendTest irsend(0); + irsend.begin(); + uint8_t data[kWhirlpoolAcStateLength] = { + 0x83, 0x06, 0x10, 0x71, 0x00, 0x00, 0x91, 0x1F, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xEF, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x02}; + + irsend.sendWhirlpoolAC(data); + EXPECT_EQ( + "f38000d50" + "m8950s4484" + "m597s1649m597s1649m597s533m597s533m597s533m597s533m597s533m597s1649" + "m597s533m597s1649m597s1649m597s533m597s533m597s533m597s533m597s533" + "m597s533m597s533m597s533m597s533m597s1649m597s533m597s533m597s533" + "m597s1649m597s533m597s533m597s533m597s1649m597s1649m597s1649m597s533" + "m597s533m597s533m597s533m597s533m597s533m597s533m597s533m597s533" + "m597s533m597s533m597s533m597s533m597s533m597s533m597s533m597s533" + "m597s7920" + "m597s1649m597s533m597s533m597s533m597s1649m597s533m597s533m597s1649" + "m597s1649m597s1649m597s1649m597s1649m597s1649m597s533m597s533m597s533" + "m597s533m597s533m597s533m597s533m597s533m597s533m597s533m597s533" + "m597s533m597s533m597s533m597s533m597s533m597s533m597s533m597s533" + "m597s533m597s533m597s533m597s533m597s533m597s533m597s533m597s533" + "m597s533m597s533m597s533m597s533m597s533m597s533m597s533m597s533" + "m597s533m597s533m597s533m597s533m597s533m597s533m597s533m597s533" + "m597s1649m597s1649m597s1649m597s1649m597s533m597s1649m597s1649m597s1649" + "m597s7920" + "m597s533m597s533m597s533m597s533m597s533m597s533m597s533m597s533" + "m597s533m597s1649m597s533m597s533m597s533m597s533m597s533m597s533" + "m597s533m597s533m597s533m597s533m597s533m597s533m597s533m597s533" + "m597s533m597s533m597s533m597s533m597s533m597s533m597s533m597s533" + "m597s533m597s533m597s533m597s533m597s533m597s533m597s533m597s533" + "m597s533m597s533m597s533m597s533m597s533m597s533m597s533m597s533" + "m597s533m597s1649m597s533m597s533m597s533m597s533m597s533m597s533" + "m597s100000", + irsend.outputStr()); +} + +// Tests for decodeWhirlpoolAC(). +// Decode normal WhirlpoolAC messages. +TEST(TestDecodeWhirlpoolAC, SyntheticDecode) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + // Synthesised Normal WhirlpoolAC message. + irsend.reset(); + uint8_t expectedState[kWhirlpoolAcStateLength] = { + 0x83, 0x06, 0x10, 0x71, 0x00, 0x00, 0x91, 0x1F, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xEF, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x02}; + irsend.sendWhirlpoolAC(expectedState); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(WHIRLPOOL_AC, irsend.capture.decode_type); + EXPECT_EQ(kWhirlpoolAcBits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + IRWhirlpoolAc ac(0); + ac.setRaw(irsend.capture.state); + EXPECT_EQ( + "Model: 1 (DG11J13A), Power toggle: Off, Mode: 1 (AUTO), Temp: 25C, " + "Fan: 0 (AUTO), Swing: Off, Light: On, Clock: 17:31, On Timer: Off, " + "Off Timer: Off, Sleep: Off, Super: Off, Command: 2 (TEMP)", + ac.toString()); +} + +TEST(TestDecodeWhirlpoolAC, Real26CFanAutoCoolingSwingOnClock1918) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + irsend.reset(); + uint8_t expectedState[kWhirlpoolAcStateLength] = { + 0x83, 0x06, 0x80, 0x82, 0x00, 0x00, 0x93, 0x12, 0x40, 0x00, 0x00, + 0x00, 0x00, 0xC3, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x07}; + irsend.sendWhirlpoolAC(expectedState); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(WHIRLPOOL_AC, irsend.capture.decode_type); + EXPECT_EQ(kWhirlpoolAcBits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + IRWhirlpoolAc ac(0); + ac.setRaw(irsend.capture.state); + EXPECT_EQ( + "Model: 1 (DG11J13A), Power toggle: Off, Mode: 2 (COOL), Temp: 26C, " + "Fan: 0 (AUTO), Swing: On, Light: On, Clock: 19:18, On Timer: Off, " + "Off Timer: Off, Sleep: Off, Super: Off, Command: 7 (SWING)", + ac.toString()); +} + +TEST(TestDecodeWhirlpoolAC, RealTimerExample) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + irsend.reset(); + // Dehumidify timer on 7:40 off 8:05 + uint16_t rawData[343] = { + 9092, 4556, 604, 1664, 604, 1674, 630, 514, 630, 518, 628, 522, + 604, 550, 628, 530, 602, 1680, 630, 508, 630, 1644, 604, 1674, + 604, 544, 604, 548, 630, 524, 604, 554, 620, 530, 630, 506, + 602, 538, 602, 542, 604, 542, 604, 546, 630, 524, 602, 556, + 628, 518, 604, 1666, 632, 1644, 604, 540, 602, 546, 604, 1680, + 604, 1684, 604, 1686, 630, 520, 602, 534, 606, 538, 602, 540, + 604, 544, 604, 548, 602, 552, 630, 528, 602, 546, 602, 536, + 628, 510, 606, 540, 604, 544, 630, 522, 604, 554, 600, 554, + 602, 528, 602, 8032, 604, 1666, 604, 1668, 602, 1676, 630, 518, + 630, 520, 602, 550, 604, 554, 604, 1678, 630, 1640, 602, 1672, + 602, 542, 602, 544, 628, 522, 630, 1658, 604, 554, 628, 1652, + 630, 508, 602, 538, 630, 514, 630, 1652, 602, 546, 604, 550, + 602, 554, 602, 546, 630, 1638, 604, 536, 630, 1646, 602, 544, + 628, 522, 632, 524, 628, 528, 602, 1686, 594, 1666, 604, 1670, + 602, 1674, 632, 516, 604, 546, 638, 518, 622, 534, 628, 518, + 604, 532, 604, 536, 600, 550, 622, 1652, 630, 520, 602, 1684, + 602, 554, 602, 544, 630, 506, 628, 512, 602, 540, 628, 518, + 602, 550, 602, 552, 604, 554, 602, 544, 628, 1642, 602, 536, + 632, 1646, 630, 516, 602, 1680, 630, 1656, 604, 1688, 602, 1660, + 602, 8030, 604, 532, 604, 536, 604, 540, 602, 544, 628, 522, + 602, 552, 602, 556, 602, 544, 602, 1666, 630, 510, 602, 1674, + 604, 544, 628, 522, 602, 552, 630, 526, 628, 520, 602, 534, + 630, 510, 604, 540, 602, 544, 606, 544, 604, 550, 604, 554, + 602, 544, 604, 534, 602, 538, 602, 542, 604, 542, 604, 546, + 604, 550, 632, 526, 604, 544, 630, 506, 604, 536, 604, 540, + 628, 518, 602, 548, 604, 550, 604, 552, 630, 516, 602, 534, + 604, 536, 630, 512, 604, 544, 602, 548, 630, 524, 602, 554, + 602, 542, 604, 1666, 606, 532, 630, 1644, 602, 544, 630, 520, + 604, 550, 604, 554, 602, 526, 598}; + uint8_t expectedState[kWhirlpoolAcStateLength] = { + 0x83, 0x06, 0x00, 0x73, 0x00, 0x00, 0x87, 0xA3, 0x08, 0x85, 0x07, + 0x28, 0x00, 0xF5, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x05}; + irsend.sendRaw(rawData, 343, 38000); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(WHIRLPOOL_AC, irsend.capture.decode_type); + EXPECT_EQ(kWhirlpoolAcBits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + IRWhirlpoolAc ac(0); + ac.setRaw(irsend.capture.state); + EXPECT_EQ( + "Model: 1 (DG11J13A), Power toggle: Off, Mode: 3 (DRY), Temp: 25C, " + "Fan: 0 (AUTO), Swing: Off, Light: On, Clock: 07:35, On Timer: 07:40, " + "Off Timer: 08:05, Sleep: Off, Super: Off, Command: 5 (ONTIMER)", + ac.toString()); +} + +// Decode a recorded example +TEST(TestDecodeWhirlpoolAC, RealExampleDecode) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + // Real WhirlpoolAC message. + // Ref: https://github.com/markszabo/IRremoteESP8266/issues/509 + uint16_t rawData[343] = { + 8950, 4484, 598, 1642, 598, 1646, 594, 534, 594, 538, 602, 532, + 598, 540, 600, 542, 598, 1650, 600, 522, 598, 1644, 596, 1650, + 600, 532, 598, 538, 602, 536, 594, 548, 592, 538, 602, 518, + 600, 524, 596, 532, 598, 532, 598, 1654, 596, 544, 596, 544, + 596, 536, 594, 1644, 596, 528, 600, 528, 592, 538, 602, 1648, + 602, 1654, 596, 1664, 598, 534, 594, 526, 594, 530, 598, 528, + 602, 530, 600, 534, 596, 542, 598, 542, 598, 534, 596, 526, + 594, 530, 600, 528, 602, 530, 600, 534, 596, 542, 598, 544, + 596, 518, 602, 7916, 598, 1642, 598, 528, 600, 528, 602, 530, + 600, 1652, 598, 542, 598, 544, 596, 1654, 596, 1644, 596, 1648, + 602, 1644, 596, 1654, 596, 1656, 604, 536, 594, 548, 602, 528, + 600, 520, 600, 524, 596, 532, 598, 532, 596, 538, 602, 536, + 594, 546, 594, 538, 602, 518, 600, 524, 596, 532, 598, 532, + 598, 536, 594, 544, 596, 544, 596, 536, 594, 526, 592, 530, + 600, 528, 600, 530, 602, 532, 596, 542, 598, 542, 598, 534, + 596, 524, 596, 528, 600, 526, 592, 538, 592, 542, 598, 540, + 600, 540, 600, 530, 598, 522, 598, 526, 594, 534, 596, 534, + 594, 540, 602, 536, 592, 548, 592, 538, 600, 1636, 594, 1648, + 602, 1642, 598, 1652, 598, 538, 602, 1680, 570, 1662, 598, 1634, + 596, 7924, 600, 520, 598, 526, 592, 534, 596, 534, 596, 540, + 600, 536, 604, 538, 602, 530, 600, 520, 598, 1640, 600, 528, + 600, 530, 600, 534, 594, 544, 596, 544, 596, 534, 596, 526, + 594, 528, 600, 526, 594, 536, 592, 542, 598, 538, 602, 538, + 602, 528, 600, 520, 600, 524, 596, 530, 600, 532, 598, 534, + 596, 542, 598, 542, 598, 532, 598, 524, 596, 528, 602, 526, + 594, 536, 594, 540, 600, 536, 594, 548, 592, 538, 602, 518, + 602, 522, 596, 530, 600, 530, 600, 534, 596, 542, 598, 544, + 596, 534, 596, 524, 594, 1644, 596, 532, 596, 534, 596, 538, + 602, 536, 594, 546, 594, 520, 600}; + uint8_t expectedState[kWhirlpoolAcStateLength] = { + 0x83, 0x06, 0x10, 0x71, 0x00, 0x00, 0x91, 0x1F, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xEF, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x02}; + + irsend.reset(); + irsend.sendRaw(rawData, 343, 38000); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(WHIRLPOOL_AC, irsend.capture.decode_type); + EXPECT_EQ(kWhirlpoolAcBits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + IRWhirlpoolAc ac(0); + ac.setRaw(irsend.capture.state); + EXPECT_EQ( + "Model: 1 (DG11J13A), Power toggle: Off, Mode: 1 (AUTO), Temp: 25C, " + "Fan: 0 (AUTO), Swing: Off, Light: On, Clock: 17:31, On Timer: Off, " + "Off Timer: Off, Sleep: Off, Super: Off, Command: 2 (TEMP)", + ac.toString()); +} + +// Tests for IRWhirlpoolAc class. + +TEST(TestIRWhirlpoolAcClass, SetAndGetRaw) { + uint8_t expectedState[kWhirlpoolAcStateLength] = { + 0x83, 0x06, 0x10, 0x71, 0x00, 0x00, 0x91, 0x1F, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xEF, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x02}; + IRWhirlpoolAc ac(0); + ac.setRaw(expectedState); + EXPECT_STATE_EQ(expectedState, ac.getRaw(), kWhirlpoolAcBits); +} + +TEST(TestIRWhirlpoolAcClass, SetAndGetTemp) { + IRWhirlpoolAc ac(0); + ac.setCommand(0); // Clear the previous command. + + ac.setModel(DG11J13A); + ac.setTemp(25); + EXPECT_EQ(25, ac.getTemp()); + EXPECT_EQ(kWhirlpoolAcCommandTemp, ac.getCommand()); + ac.setTemp(kWhirlpoolAcMinTemp); + EXPECT_EQ(kWhirlpoolAcMinTemp, ac.getTemp()); + ac.setTemp(kWhirlpoolAcMinTemp - 1); + EXPECT_EQ(kWhirlpoolAcMinTemp, ac.getTemp()); + ac.setTemp(kWhirlpoolAcMaxTemp); + EXPECT_EQ(kWhirlpoolAcMaxTemp, ac.getTemp()); + ac.setTemp(kWhirlpoolAcMaxTemp + 1); + EXPECT_EQ(kWhirlpoolAcMaxTemp, ac.getTemp()); + + ac.setModel(DG11J191); // Has a -2 offset on min/max temps. + ac.setTemp(25); + EXPECT_EQ(25, ac.getTemp()); + EXPECT_EQ(kWhirlpoolAcCommandTemp, ac.getCommand()); + ac.setTemp(kWhirlpoolAcMinTemp - 2); + EXPECT_EQ(kWhirlpoolAcMinTemp - 2, ac.getTemp()); + ac.setTemp(kWhirlpoolAcMinTemp - 2 - 1); + EXPECT_EQ(kWhirlpoolAcMinTemp - 2 , ac.getTemp()); + ac.setTemp(kWhirlpoolAcMaxTemp - 2); + EXPECT_EQ(kWhirlpoolAcMaxTemp - 2, ac.getTemp()); + ac.setTemp(kWhirlpoolAcMaxTemp - 2 + 1); + EXPECT_EQ(kWhirlpoolAcMaxTemp - 2, ac.getTemp()); +} + +TEST(TestIRWhirlpoolAcClass, SetAndGetMode) { + IRWhirlpoolAc ac(0); + ac.setCommand(0); // Clear the previous command. + + ac.setMode(kWhirlpoolAcCool); + EXPECT_EQ(kWhirlpoolAcCool, ac.getMode()); + EXPECT_EQ(kWhirlpoolAcCommandMode, ac.getCommand()); + ac.setMode(kWhirlpoolAcHeat); + EXPECT_EQ(kWhirlpoolAcHeat, ac.getMode()); + ac.setMode(kWhirlpoolAcAuto); + EXPECT_EQ(kWhirlpoolAcAuto, ac.getMode()); + EXPECT_EQ(kWhirlpoolAcCommand6thSense, ac.getCommand()); + ac.setMode(kWhirlpoolAcDry); + EXPECT_EQ(kWhirlpoolAcDry, ac.getMode()); + EXPECT_EQ(kWhirlpoolAcCommandMode, ac.getCommand()); +} + +TEST(TestIRWhirlpoolAcClass, SetAndGetFan) { + IRWhirlpoolAc ac(0); + ac.setCommand(0); // Clear the previous command. + + ac.setFan(kWhirlpoolAcFanAuto); + EXPECT_EQ(kWhirlpoolAcFanAuto, ac.getFan()); + EXPECT_EQ(kWhirlpoolAcCommandFanSpeed, ac.getCommand()); + ac.setFan(kWhirlpoolAcFanLow); + EXPECT_EQ(kWhirlpoolAcFanLow, ac.getFan()); + ac.setFan(kWhirlpoolAcFanMedium); + EXPECT_EQ(kWhirlpoolAcFanMedium, ac.getFan()); + ac.setFan(kWhirlpoolAcFanHigh); + EXPECT_EQ(kWhirlpoolAcFanHigh, ac.getFan()); + ac.setFan(kWhirlpoolAcFanAuto); + EXPECT_EQ(kWhirlpoolAcFanAuto, ac.getFan()); + + // Known state with a non-auto fan mode. + const uint8_t state[21] = {0x83, 0x06, 0x0B, 0x82, 0x00, 0x00, 0x93, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, + 0x00, 0x03, 0x00, 0x00, 0x08, 0x00, 0x0B}; + ac.setRaw(state); + EXPECT_EQ(kWhirlpoolAcFanLow, ac.getFan()); +} + +TEST(TestIRWhirlpoolAcClass, SetAndGetSwing) { + IRWhirlpoolAc ac(0); + ac.setCommand(0); // Clear the previous command. + + ac.setSwing(true); + EXPECT_TRUE(ac.getSwing()); + EXPECT_EQ(kWhirlpoolAcCommandSwing, ac.getCommand()); + ac.setSwing(false); + EXPECT_FALSE(ac.getSwing()); + ac.setSwing(true); + EXPECT_TRUE(ac.getSwing()); +} + +TEST(TestIRWhirlpoolAcClass, SetAndGetLight) { + IRWhirlpoolAc ac(0); + ac.setCommand(0); // Clear the previous command. + + ac.setLight(true); + EXPECT_TRUE(ac.getLight()); + ac.setLight(false); + EXPECT_FALSE(ac.getLight()); + ac.setLight(true); + EXPECT_TRUE(ac.getLight()); +} + +TEST(TestIRWhirlpoolAcClass, SetAndGetClock) { + IRWhirlpoolAc ac(0); + ac.setClock(0); + EXPECT_EQ(0, ac.getClock()); + EXPECT_EQ("00:00", ac.timeToString(ac.getClock())); + ac.setClock(1); + EXPECT_EQ(1, ac.getClock()); + EXPECT_EQ("00:01", ac.timeToString(ac.getClock())); + ac.setClock(12 * 60 + 34); + EXPECT_EQ(12 * 60 + 34, ac.getClock()); + EXPECT_EQ("12:34", ac.timeToString(ac.getClock())); + ac.setClock(7 * 60 + 5); + EXPECT_EQ(7 * 60 + 5, ac.getClock()); + EXPECT_EQ("07:05", ac.timeToString(ac.getClock())); + ac.setClock(23 * 60 + 59); + EXPECT_EQ(23 * 60 + 59, ac.getClock()); + EXPECT_EQ("23:59", ac.timeToString(ac.getClock())); + ac.setClock(24 * 60 + 0); + EXPECT_EQ(0, ac.getClock()); + EXPECT_EQ("00:00", ac.timeToString(ac.getClock())); + ac.setClock(25 * 60 + 23); + EXPECT_EQ(1 * 60 + 23, ac.getClock()); + EXPECT_EQ("01:23", ac.timeToString(ac.getClock())); +} + +TEST(TestIRWhirlpoolAcClass, OnOffTimers) { + IRWhirlpoolAc ac(0); + ac.setCommand(0); // Clear the previous command. + + // On Timer + ac.enableOnTimer(false); + ac.setOnTimer(0); + EXPECT_EQ(0, ac.getOnTimer()); + EXPECT_EQ("00:00", ac.timeToString(ac.getOnTimer())); + EXPECT_FALSE(ac.isOnTimerEnabled()); + EXPECT_EQ(kWhirlpoolAcCommandOnTimer, ac.getCommand()); + ac.setOnTimer(1); + EXPECT_EQ(1, ac.getOnTimer()); + EXPECT_EQ("00:01", ac.timeToString(ac.getOnTimer())); + ac.enableOnTimer(true); + ac.setOnTimer(12 * 60 + 34); + EXPECT_EQ(12 * 60 + 34, ac.getOnTimer()); + EXPECT_EQ("12:34", ac.timeToString(ac.getOnTimer())); + EXPECT_TRUE(ac.isOnTimerEnabled()); + ac.setOnTimer(7 * 60 + 5); + EXPECT_EQ(7 * 60 + 5, ac.getOnTimer()); + EXPECT_EQ("07:05", ac.timeToString(ac.getOnTimer())); + ac.setOnTimer(23 * 60 + 59); + EXPECT_EQ(23 * 60 + 59, ac.getOnTimer()); + EXPECT_EQ("23:59", ac.timeToString(ac.getOnTimer())); + ac.setOnTimer(24 * 60 + 0); + EXPECT_EQ(0, ac.getOnTimer()); + EXPECT_EQ("00:00", ac.timeToString(ac.getOnTimer())); + ac.setOnTimer(25 * 60 + 23); + EXPECT_EQ(1 * 60 + 23, ac.getOnTimer()); + EXPECT_EQ("01:23", ac.timeToString(ac.getOnTimer())); + // Off Timer + ac.enableOffTimer(false); + ac.setOffTimer(0); + EXPECT_EQ(0, ac.getOffTimer()); + EXPECT_EQ("00:00", ac.timeToString(ac.getOffTimer())); + EXPECT_FALSE(ac.isOffTimerEnabled()); + EXPECT_EQ(kWhirlpoolAcCommandOffTimer, ac.getCommand()); + ac.setOffTimer(1); + EXPECT_EQ(1, ac.getOffTimer()); + EXPECT_EQ("00:01", ac.timeToString(ac.getOffTimer())); + ac.enableOffTimer(true); + ac.setOffTimer(12 * 60 + 34); + EXPECT_EQ(12 * 60 + 34, ac.getOffTimer()); + EXPECT_EQ("12:34", ac.timeToString(ac.getOffTimer())); + EXPECT_TRUE(ac.isOffTimerEnabled()); + ac.setOffTimer(7 * 60 + 5); + EXPECT_EQ(7 * 60 + 5, ac.getOffTimer()); + EXPECT_EQ("07:05", ac.timeToString(ac.getOffTimer())); + ac.setOffTimer(23 * 60 + 59); + EXPECT_EQ(23 * 60 + 59, ac.getOffTimer()); + EXPECT_EQ("23:59", ac.timeToString(ac.getOffTimer())); + ac.setOffTimer(24 * 60 + 0); + EXPECT_EQ(0, ac.getOffTimer()); + EXPECT_EQ("00:00", ac.timeToString(ac.getOffTimer())); + ac.setOffTimer(25 * 60 + 23); + EXPECT_EQ(1 * 60 + 23, ac.getOffTimer()); + EXPECT_EQ("01:23", ac.timeToString(ac.getOffTimer())); +} + +TEST(TestIRWhirlpoolAcClass, SetAndGetCommand) { + IRWhirlpoolAc ac(0); + ac.setCommand(0); + EXPECT_EQ(0, ac.getCommand()); + ac.setCommand(kWhirlpoolAcCommandFanSpeed); + EXPECT_EQ(kWhirlpoolAcCommandFanSpeed, ac.getCommand()); + ac.setCommand(255); + EXPECT_EQ(255, ac.getCommand()); +} + +TEST(TestIRWhirlpoolAcClass, SetAndGetPowerToggle) { + IRWhirlpoolAc ac(0); + ac.setCommand(0); + + ac.setPowerToggle(false); + EXPECT_FALSE(ac.getPowerToggle()); + ac.setPowerToggle(true); + EXPECT_TRUE(ac.getPowerToggle()); + ac.setPowerToggle(false); + EXPECT_FALSE(ac.getPowerToggle()); + + // Known state with a power toggle in it. + uint8_t state[21] = {0x83, 0x06, 0x07, 0x82, 0x00, 0x00, 0x93, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, + 0x00, 0x01, 0x00, 0x00, 0x08, 0x00, 0x09}; + ac.setRaw(state); + EXPECT_TRUE(ac.getPowerToggle()); +} + +TEST(TestIRWhirlpoolAcClass, SetAndGetModel) { + IRWhirlpoolAc ac(0); + ac.setTemp(19); + ac.setCommand(0); // Set model shouldn't change the command setting. + + ac.setModel(DG11J191); + EXPECT_EQ(DG11J191, ac.getModel()); + EXPECT_EQ(19, ac.getTemp()); + EXPECT_EQ(0, ac.getCommand()); + ac.setModel(DG11J13A); + EXPECT_EQ(DG11J13A, ac.getModel()); + EXPECT_EQ(19, ac.getTemp()); + ac.setModel(DG11J191); + EXPECT_EQ(DG11J191, ac.getModel()); + EXPECT_EQ(19, ac.getTemp()); + EXPECT_EQ(0, ac.getCommand()); + + // One of the models has a lower min temp. Check that desired temp is kept. + ac.setTemp(16); + ac.setCommand(0); // Set model shouldn't change the command setting. + EXPECT_EQ(16, ac.getTemp()); + EXPECT_EQ(0, ac.getCommand()); + ac.setModel(DG11J13A); + EXPECT_EQ(DG11J13A, ac.getModel()); + EXPECT_EQ(18, ac.getTemp()); + ac.setModel(DG11J191); + EXPECT_EQ(DG11J191, ac.getModel()); + EXPECT_EQ(16, ac.getTemp()); + EXPECT_EQ(0, ac.getCommand()); + + // Known states with different models. + uint8_t state_1[21] = {0x83, 0x06, 0x01, 0x30, 0x00, 0x00, 0x92, + 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x95, + 0x00, 0x02, 0x00, 0x00, 0x08, 0x00, 0x0A}; + uint8_t state_2[21] = {0x83, 0x06, 0x00, 0x30, 0x00, 0x00, 0x8B, + 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8E, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x02}; + + ac.setRaw(state_1); + EXPECT_EQ(DG11J191, ac.getModel()); + ac.setRaw(state_2); + EXPECT_EQ(DG11J13A, ac.getModel()); +} + +TEST(TestIRWhirlpoolAcClass, SetAndGetSleep) { + IRWhirlpoolAc ac(0); + ac.setFan(kWhirlpoolAcFanAuto); + ac.setCommand(0); + + ac.setSleep(false); + EXPECT_FALSE(ac.getSleep()); + EXPECT_EQ(kWhirlpoolAcCommandSleep, ac.getCommand()); + ac.setSleep(true); + EXPECT_TRUE(ac.getSleep()); + EXPECT_EQ(kWhirlpoolAcCommandSleep, ac.getCommand()); + EXPECT_EQ(kWhirlpoolAcFanLow, ac.getFan()); + ac.setSleep(false); + EXPECT_FALSE(ac.getSleep()); + + // Known state with sleep mode in it. + uint8_t state[21] = {0x83, 0x06, 0x0B, 0x73, 0x00, 0x00, 0x90, + 0x9E, 0x00, 0xA0, 0x17, 0x3A, 0x00, 0xFB, + 0x00, 0x03, 0x00, 0x00, 0x08, 0x00, 0x0B}; + ac.setRaw(state); + EXPECT_TRUE(ac.getSleep()); +} + +TEST(TestIRWhirlpoolAcClass, SetAndGetSuper) { + IRWhirlpoolAc ac(0); + ac.setFan(kWhirlpoolAcFanAuto); + ac.setMode(kWhirlpoolAcDry); + ac.setCommand(0); + + ac.setSuper(false); + EXPECT_FALSE(ac.getSuper()); + EXPECT_EQ(kWhirlpoolAcCommandSuper, ac.getCommand()); + ac.setSuper(true); + EXPECT_TRUE(ac.getSuper()); + EXPECT_EQ(kWhirlpoolAcCommandSuper, ac.getCommand()); + EXPECT_EQ(kWhirlpoolAcFanHigh, ac.getFan()); + EXPECT_EQ(kWhirlpoolAcCool, ac.getMode()); + EXPECT_EQ(kWhirlpoolAcMinTemp, ac.getTemp()); + + ac.setSuper(false); + EXPECT_FALSE(ac.getSuper()); + EXPECT_EQ(kWhirlpoolAcFanHigh, ac.getFan()); + EXPECT_EQ(kWhirlpoolAcCool, ac.getMode()); + EXPECT_EQ(kWhirlpoolAcMinTemp, ac.getTemp()); + + // When in heat mode, it should stay in heat mode. + ac.setFan(kWhirlpoolAcFanAuto); + ac.setMode(kWhirlpoolAcHeat); + ac.setSuper(true); + EXPECT_TRUE(ac.getSuper()); + EXPECT_EQ(kWhirlpoolAcCommandSuper, ac.getCommand()); + EXPECT_EQ(kWhirlpoolAcFanHigh, ac.getFan()); + EXPECT_EQ(kWhirlpoolAcHeat, ac.getMode()); + EXPECT_EQ(kWhirlpoolAcMaxTemp, ac.getTemp()); + + // Changing mode/temp/fan/power should cancel super, + ac.setMode(kWhirlpoolAcCool); + EXPECT_FALSE(ac.getSuper()); + ac.setSuper(true); + ac.setTemp(25); + EXPECT_FALSE(ac.getSuper()); + ac.setSuper(true); + ac.setFan(kWhirlpoolAcFanMedium); + EXPECT_FALSE(ac.getSuper()); + ac.setSuper(true); + ac.setPowerToggle(true); + EXPECT_FALSE(ac.getSuper()); + + // Known state with Super mode in it. + uint8_t state[21] = {0x83, 0x06, 0x01, 0x02, 0x00, 0x90, 0x90, + 0x9F, 0x00, 0xA0, 0x17, 0x3A, 0x00, 0x11, + 0x00, 0x04, 0x00, 0x00, 0x08, 0x00, 0x0C}; + ac.setRaw(state); + EXPECT_TRUE(ac.getSuper()); +} + +// Build a known good message from scratch. +TEST(TestIRWhirlpoolAcClass, MessageConstruction) { + // Real example captured from a remote. (ref: RealTimerExample) + uint8_t expectedState[kWhirlpoolAcStateLength] = { + 0x83, 0x06, 0x00, 0x73, 0x00, 0x00, 0x87, 0xA3, 0x08, 0x85, 0x07, + 0x28, 0x00, 0xF5, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x05}; + IRWhirlpoolAc ac(0); + ac.setModel(DG11J13A); + ac.setTemp(25); + ac.setPowerToggle(false); + ac.setMode(kWhirlpoolAcDry); + ac.setFan(kWhirlpoolAcFanAuto); + ac.setSwing(false); + ac.setLight(true); + ac.setClock(7 * 60 + 35); + ac.setOnTimer(7 * 60 + 40); + ac.setOffTimer(8 * 60 + 5); + ac.enableOffTimer(true); + ac.setSleep(false); + ac.setSuper(false); + ac.enableOnTimer(true); + + EXPECT_EQ( + "Model: 1 (DG11J13A), Power toggle: Off, Mode: 3 (DRY), Temp: 25C, " + "Fan: 0 (AUTO), Swing: Off, Light: On, Clock: 07:35, On Timer: 07:40, " + "Off Timer: 08:05, Sleep: Off, Super: Off, Command: 5 (ONTIMER)", + ac.toString()); + EXPECT_STATE_EQ(expectedState, ac.getRaw(), kWhirlpoolAcBits); +} diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_Whynter_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Whynter_test.cpp similarity index 98% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_Whynter_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_Whynter_test.cpp index 748a4c9bf..92ced5cf6 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_Whynter_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Whynter_test.cpp @@ -14,6 +14,7 @@ TEST(TestSendWhynter, SendDataOnly) { irsend.reset(); irsend.sendWhynter(0x0); EXPECT_EQ( + "f38000d50" "m750s750m2850s2850" "m750s750m750s750m750s750m750s750m750s750m750s750m750s750m750s750" "m750s750m750s750m750s750m750s750m750s750m750s750m750s750m750s750" @@ -25,6 +26,7 @@ TEST(TestSendWhynter, SendDataOnly) { irsend.reset(); irsend.sendWhynter(0xFFFFFFFF); EXPECT_EQ( + "f38000d50" "m750s750m2850s2850" "m750s2150m750s2150m750s2150m750s2150m750s2150m750s2150m750s2150m750s2150" "m750s2150m750s2150m750s2150m750s2150m750s2150m750s2150m750s2150m750s2150" @@ -36,6 +38,7 @@ TEST(TestSendWhynter, SendDataOnly) { irsend.reset(); irsend.sendWhynter(0x87654321); EXPECT_EQ( + "f38000d50" "m750s750m2850s2850" "m750s2150m750s750m750s750m750s750m750s750m750s2150m750s2150m750s2150" "m750s750m750s2150m750s2150m750s750m750s750m750s2150m750s750m750s2150" @@ -53,6 +56,7 @@ TEST(TestSendWhynter, SendWithRepeats) { irsend.reset(); irsend.sendWhynter(0x87654321, kWhynterBits, 0); // 0 repeats. EXPECT_EQ( + "f38000d50" "m750s750m2850s2850" "m750s2150m750s750m750s750m750s750m750s750m750s2150m750s2150m750s2150" "m750s750m750s2150m750s2150m750s750m750s750m750s2150m750s750m750s2150" @@ -64,6 +68,7 @@ TEST(TestSendWhynter, SendWithRepeats) { irsend.reset(); irsend.sendWhynter(0x87654321, kWhynterBits, 1); // 1 repeat. EXPECT_EQ( + "f38000d50" "m750s750m2850s2850" "m750s2150m750s750m750s750m750s750m750s750m750s2150m750s2150m750s2150" "m750s750m750s2150m750s2150m750s750m750s750m750s2150m750s750m750s2150" @@ -81,6 +86,7 @@ TEST(TestSendWhynter, SendWithRepeats) { irsend.reset(); irsend.sendWhynter(0x87654321, kWhynterBits, 2); // 2 repeats. EXPECT_EQ( + "f38000d50" "m750s750m2850s2850" "m750s2150m750s750m750s750m750s750m750s750m750s2150m750s2150m750s2150" "m750s750m750s2150m750s2150m750s750m750s750m750s2150m750s750m750s2150" @@ -110,6 +116,7 @@ TEST(TestSendWhynter, SendUnusualSize) { irsend.reset(); irsend.sendWhynter(0x0, 8); EXPECT_EQ( + "f38000d50" "m750s750m2850s2850" "m750s750m750s750m750s750m750s750m750s750m750s750m750s750m750s750" "m750s88050", @@ -118,6 +125,7 @@ TEST(TestSendWhynter, SendUnusualSize) { irsend.reset(); irsend.sendWhynter(0x1234567890ABCDEF, 64); EXPECT_EQ( + "f38000d50" "m750s750m2850s2850" "m750s750m750s750m750s750m750s2150m750s750m750s750m750s2150m750s750" "m750s750m750s750m750s2150m750s2150m750s750m750s2150m750s750m750s750" diff --git a/lib/IRremoteESP8266-2.5.2.03/tools/Makefile b/lib/IRremoteESP8266-2.6.0/tools/Makefile similarity index 76% rename from lib/IRremoteESP8266-2.5.2.03/tools/Makefile rename to lib/IRremoteESP8266-2.6.0/tools/Makefile index c303e051d..08488949c 100644 --- a/lib/IRremoteESP8266-2.5.2.03/tools/Makefile +++ b/lib/IRremoteESP8266-2.6.0/tools/Makefile @@ -14,13 +14,14 @@ USER_DIR = ../src # Where to find test code. TEST_DIR = ../test +INCLUDES = -I$(USER_DIR) -I$(TEST_DIR) # Flags passed to the preprocessor. # Set Google Test's header directory as a system directory, such that # the compiler doesn't generate warnings in Google Test headers. CPPFLAGS += -DUNIT_TEST # Flags passed to the C++ compiler. -CXXFLAGS += -g -Wall -Wextra -pthread +CXXFLAGS += -g -Wall -Wextra -pthread -std=gnu++11 all : gc_decode mode2_decode @@ -48,25 +49,27 @@ PROTOCOLS = ir_NEC.o ir_Sony.o ir_Samsung.o ir_JVC.o ir_RCMM.o ir_RC5_RC6.o \ ir_Pronto.o ir_GlobalCache.o ir_Nikai.o ir_Toshiba.o ir_Midea.o \ ir_Magiquest.o ir_Lasertag.o ir_Carrier.o ir_Haier.o ir_Hitachi.o \ ir_GICable.o ir_Whirlpool.o ir_Lutron.o ir_Electra.o ir_Pioneer.o \ - ir_MWM.o + ir_MWM.o ir_Vestel.o ir_Teco.o ir_Tcl.o ir_Lego.o \ + ir_MitsubishiHeavy.o # Common object files COMMON_OBJ = IRutils.o IRtimer.o IRsend.o IRrecv.o $(PROTOCOLS) # Common dependencies COMMON_DEPS = $(USER_DIR)/IRrecv.h $(USER_DIR)/IRsend.h $(USER_DIR)/IRtimer.h \ - $(USER_DIR)/IRutils.h $(USER_DIR)/IRremoteESP8266.h + $(USER_DIR)/IRutils.h $(USER_DIR)/IRremoteESP8266.h \ + $(TEST_DIR)/IRsend_test.h # Common test dependencies COMMON_TEST_DEPS = $(COMMON_DEPS) $(TEST_DIR)/IRsend_test.h gc_decode.o : gc_decode.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -I$(TEST_DIR) -c gc_decode.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c gc_decode.cpp gc_decode : $(COMMON_OBJ) gc_decode.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ mode2_decode.o : mode2_decode.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -I$(TEST_DIR) -c mode2_decode.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c mode2_decode.cpp mode2_decode : $(COMMON_OBJ) mode2_decode.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -83,7 +86,6 @@ IRsend.o : $(USER_DIR)/IRsend.cpp $(USER_DIR)/IRsend.h $(USER_DIR)/IRremoteESP82 IRrecv.o : $(USER_DIR)/IRrecv.cpp $(USER_DIR)/IRrecv.h $(USER_DIR)/IRremoteESP8266.h $(GTEST_HEADERS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/IRrecv.cpp - ir_NEC.o : $(USER_DIR)/ir_NEC.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_NEC.cpp @@ -97,10 +99,10 @@ ir_Sony.o : $(USER_DIR)/ir_Sony.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Sony.cpp ir_Samsung.o : $(USER_DIR)/ir_Samsung.cpp $(USER_DIR)/ir_Samsung.h $(COMMON_DEPS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Samsung.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Samsung.cpp ir_Kelvinator.o : $(USER_DIR)/ir_Kelvinator.cpp $(USER_DIR)/ir_Kelvinator.h $(COMMON_DEPS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Kelvinator.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Kelvinator.cpp ir_JVC.o : $(USER_DIR)/ir_JVC.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_JVC.cpp @@ -112,10 +114,13 @@ ir_LG.o : $(USER_DIR)/ir_LG.h $(USER_DIR)/ir_LG.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_LG.cpp ir_Mitsubishi.o : $(USER_DIR)/ir_Mitsubishi.h $(USER_DIR)/ir_Mitsubishi.cpp $(COMMON_DEPS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Mitsubishi.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Mitsubishi.cpp + +ir_MitsubishiHeavy.o : $(USER_DIR)/ir_MitsubishiHeavy.h $(USER_DIR)/ir_MitsubishiHeavy.cpp $(COMMON_DEPS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_MitsubishiHeavy.cpp ir_Fujitsu.o : $(USER_DIR)/ir_Fujitsu.h $(USER_DIR)/ir_Fujitsu.cpp $(COMMON_DEPS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Fujitsu.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Fujitsu.cpp ir_Sharp.o : $(USER_DIR)/ir_Sharp.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Sharp.cpp @@ -124,7 +129,7 @@ ir_RC5_RC6.o : $(USER_DIR)/ir_RC5_RC6.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_RC5_RC6.cpp ir_Panasonic.o : $(USER_DIR)/ir_Panasonic.cpp $(COMMON_DEPS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Panasonic.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Panasonic.cpp ir_Dish.o : $(USER_DIR)/ir_Dish.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Dish.cpp @@ -133,7 +138,7 @@ ir_Whynter.o : $(USER_DIR)/ir_Whynter.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Whynter.cpp ir_Coolix.o : $(USER_DIR)/ir_Coolix.cpp $(COMMON_DEPS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Coolix.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Coolix.cpp ir_Aiwa.o : $(USER_DIR)/ir_Aiwa.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Aiwa.cpp @@ -145,10 +150,10 @@ ir_Sanyo.o : $(USER_DIR)/ir_Sanyo.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Sanyo.cpp ir_Daikin.o : $(USER_DIR)/ir_Daikin.cpp $(USER_DIR)/ir_Daikin.h $(COMMON_DEPS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Daikin.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Daikin.cpp ir_Gree.o : $(USER_DIR)/ir_Gree.cpp $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Gree.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Gree.cpp ir_Pronto.o : $(USER_DIR)/ir_Pronto.cpp $(GTEST_HEADERS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Pronto.cpp @@ -157,10 +162,10 @@ ir_Nikai.o : $(USER_DIR)/ir_Nikai.cpp $(GTEST_HEADERS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Nikai.cpp ir_Toshiba.o : $(USER_DIR)/ir_Toshiba.h $(USER_DIR)/ir_Toshiba.cpp $(COMMON_DEPS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Toshiba.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Toshiba.cpp ir_Midea.o : $(USER_DIR)/ir_Midea.cpp $(COMMON_DEPS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Midea.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Midea.cpp ir_Magiquest.o : $(USER_DIR)/ir_Magiquest.cpp $(GTEST_HEADERS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Magiquest.cpp @@ -172,16 +177,16 @@ ir_Carrier.o : $(USER_DIR)/ir_Carrier.cpp $(GTEST_HEADERS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Carrier.cpp ir_Haier.o : $(USER_DIR)/ir_Haier.cpp $(USER_DIR)/ir_Haier.h $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Haier.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Haier.cpp ir_Hitachi.o : $(USER_DIR)/ir_Hitachi.cpp $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Hitachi.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Hitachi.cpp ir_GICable.o : $(USER_DIR)/ir_GICable.cpp $(GTEST_HEADERS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_GICable.cpp ir_Whirlpool.o : $(USER_DIR)/ir_Whirlpool.cpp $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Whirlpool.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Whirlpool.cpp ir_Lutron.o : $(USER_DIR)/ir_Lutron.cpp $(GTEST_HEADERS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Lutron.cpp @@ -194,3 +199,15 @@ ir_Pioneer.o : $(USER_DIR)/ir_Pioneer.cpp $(GTEST_HEADERS) ir_MWM.o : $(USER_DIR)/ir_MWM.cpp $(USER_DIR)/ir_RC5_RC6.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_MWM.cpp + +ir_Vestel.o : $(USER_DIR)/ir_Vestel.cpp $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Vestel.cpp + +ir_Teco.o : $(USER_DIR)/ir_Teco.cpp $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Teco.cpp + +ir_Tcl.o : $(USER_DIR)/ir_Tcl.cpp $(USER_DIR)/ir_Tcl.h $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Tcl.cpp + +ir_Lego.o : $(USER_DIR)/ir_Lego.cpp $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Lego.cpp diff --git a/lib/IRremoteESP8266-2.5.2.03/tools/RawToGlobalCache.sh b/lib/IRremoteESP8266-2.6.0/tools/RawToGlobalCache.sh similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/tools/RawToGlobalCache.sh rename to lib/IRremoteESP8266-2.6.0/tools/RawToGlobalCache.sh diff --git a/lib/IRremoteESP8266-2.5.2.03/tools/auto_analyse_raw_data.py b/lib/IRremoteESP8266-2.6.0/tools/auto_analyse_raw_data.py similarity index 98% rename from lib/IRremoteESP8266-2.5.2.03/tools/auto_analyse_raw_data.py rename to lib/IRremoteESP8266-2.6.0/tools/auto_analyse_raw_data.py index 5fd399807..b23cdb46f 100644 --- a/lib/IRremoteESP8266-2.5.2.03/tools/auto_analyse_raw_data.py +++ b/lib/IRremoteESP8266-2.6.0/tools/auto_analyse_raw_data.py @@ -85,9 +85,9 @@ class RawIRMessage(object): " %s (LSB first)\n" " Bin: 0b%s (MSB first)\n" " 0b%s (LSB first)\n" % - (bits, "0x{0:0{1}X}".format(num, bits / 4), - "0x{0:0{1}X}".format(rev_num, bits / 4), num, rev_num, - binary_str, rev_binary_str)) + (bits, ("0x{0:0%dX}" % (bits / 4)).format(num), + ("0x{0:0%dX}" % (bits / 4)).format(rev_num), num, + rev_num, binary_str, rev_binary_str)) def add_data_code(self, bin_str, footer=True): """Add the common "data" sequence of code to send the bulk of a message.""" diff --git a/lib/IRremoteESP8266-2.5.2.03/tools/auto_analyse_raw_data_test.py b/lib/IRremoteESP8266-2.6.0/tools/auto_analyse_raw_data_test.py similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/tools/auto_analyse_raw_data_test.py rename to lib/IRremoteESP8266-2.6.0/tools/auto_analyse_raw_data_test.py diff --git a/lib/IRremoteESP8266-2.5.2.03/tools/gc_decode.cpp b/lib/IRremoteESP8266-2.6.0/tools/gc_decode.cpp similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/tools/gc_decode.cpp rename to lib/IRremoteESP8266-2.6.0/tools/gc_decode.cpp diff --git a/lib/IRremoteESP8266-2.5.2.03/tools/mkkeywords b/lib/IRremoteESP8266-2.6.0/tools/mkkeywords similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/tools/mkkeywords rename to lib/IRremoteESP8266-2.6.0/tools/mkkeywords diff --git a/lib/IRremoteESP8266-2.5.2.03/tools/mode2_decode.cpp b/lib/IRremoteESP8266-2.6.0/tools/mode2_decode.cpp similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/tools/mode2_decode.cpp rename to lib/IRremoteESP8266-2.6.0/tools/mode2_decode.cpp diff --git a/lib/Joba_Tsl2561-2.0.10/.gitignore b/lib/Joba_Tsl2561-2.0.10/.gitignore new file mode 100644 index 000000000..2f4f2baeb --- /dev/null +++ b/lib/Joba_Tsl2561-2.0.10/.gitignore @@ -0,0 +1,8 @@ +.pioenvs +.piolibdeps +.clang_complete +.gcc-flags.json +examples/*/platformio.ini +examples/*/lib +examples/*/.gitignore +examples/*/.travis.yml diff --git a/lib/Joba_Tsl2561-2.0.10/.hgignore b/lib/Joba_Tsl2561-2.0.10/.hgignore new file mode 100644 index 000000000..5dac9f52f --- /dev/null +++ b/lib/Joba_Tsl2561-2.0.10/.hgignore @@ -0,0 +1,4 @@ +.pioenvs +.piolibdeps +.clang_complete +.gcc-flags.json diff --git a/lib/Joba_Tsl2561-2.0.10/.travis.yml b/lib/Joba_Tsl2561-2.0.10/.travis.yml new file mode 100644 index 000000000..52072efd6 --- /dev/null +++ b/lib/Joba_Tsl2561-2.0.10/.travis.yml @@ -0,0 +1,55 @@ +# Continuous Integration (CI) is the practice, in software +# engineering, of merging all developer working copies with a shared mainline +# several times a day < http://docs.platformio.org/page/ci/index.html > +# +# Documentation: +# +# * Travis CI Embedded Builds with PlatformIO +# < https://docs.travis-ci.com/user/integration/platformio/ > +# +# * PlatformIO integration with Travis CI +# < http://docs.platformio.org/page/ci/travis.html > +# +# * User Guide for `platformio ci` command +# < http://docs.platformio.org/page/userguide/cmd_ci.html > +# +# +# Please choice one of the following templates (proposed below) and uncomment +# it (remove "# " before each line) or use own configuration according to the +# Travis CI documentation (see above). +# + + +# +# Template #1: General project. Test it using existing `platformio.ini`. +# + +# language: python +# python: +# - "2.7" +# +# install: +# - pip install -U platformio +# +# script: +# - platformio run + + +# +# Template #2: The project is intended to by used as a library with examples +# + +# language: python +# python: +# - "2.7" +# +# env: +# - PLATFORMIO_CI_SRC=path/to/test/file.c +# - PLATFORMIO_CI_SRC=examples/file.ino +# - PLATFORMIO_CI_SRC=path/to/test/directory +# +# install: +# - pip install -U platformio +# +# script: +# - platformio ci --lib="." --board=ID_1 --board=ID_2 --board=ID_N diff --git a/lib/Joba_Tsl2561-2.0.7/COPYING b/lib/Joba_Tsl2561-2.0.10/COPYING similarity index 100% rename from lib/Joba_Tsl2561-2.0.7/COPYING rename to lib/Joba_Tsl2561-2.0.10/COPYING diff --git a/lib/Joba_Tsl2561-2.0.7/COPYING.LESSER b/lib/Joba_Tsl2561-2.0.10/COPYING.LESSER similarity index 100% rename from lib/Joba_Tsl2561-2.0.7/COPYING.LESSER rename to lib/Joba_Tsl2561-2.0.10/COPYING.LESSER diff --git a/lib/Joba_Tsl2561-2.0.7/README b/lib/Joba_Tsl2561-2.0.10/README similarity index 87% rename from lib/Joba_Tsl2561-2.0.7/README rename to lib/Joba_Tsl2561-2.0.10/README index 86c1f1a0b..d95e731d5 100644 --- a/lib/Joba_Tsl2561-2.0.7/README +++ b/lib/Joba_Tsl2561-2.0.10/README @@ -1,4 +1,4 @@ -This is a library for the TSL2561 digital luminosity sensors from Ams (Taos). +This is an Arduino library for the TSL2561 digital luminosity sensors from Ams (Taos). Design goals: * It is modularized so you can use only what you need if space/ram is constrained. @@ -18,3 +18,5 @@ The library has 3 classes: Tsl2561 All register access as described in the datasheet, except for interrupts Tsl2561Util Convenience functions like lux calculation or automatic gain Tsl2561Int TODO, Interrupt related stuff (not needed if int pin unconnected) + +Tested with boards Nano, ESP8266 and ESP32 diff --git a/lib/Joba_Tsl2561-2.0.7/examples/Autogain/Autogain.ino b/lib/Joba_Tsl2561-2.0.10/examples/Autogain/Autogain.ino similarity index 96% rename from lib/Joba_Tsl2561-2.0.7/examples/Autogain/Autogain.ino rename to lib/Joba_Tsl2561-2.0.10/examples/Autogain/Autogain.ino index 169d2b6e3..9547af45b 100644 --- a/lib/Joba_Tsl2561-2.0.7/examples/Autogain/Autogain.ino +++ b/lib/Joba_Tsl2561-2.0.10/examples/Autogain/Autogain.ino @@ -21,7 +21,7 @@ This file is part of the Joba_Tsl2561 Library. #include -// to mimic Serial.printf() of esp8266 core for other platforms +// to mimic Serial.printf() of esp cores for other platforms char *format( const char *fmt, ... ) { static char buf[128]; va_list arg; @@ -37,7 +37,7 @@ uint8_t id; void setup() { Serial.begin(115200); - Wire.begin(); + Wire.begin(TSL2561_SDA, TSL2561_SCL); while( !Tsl.begin() ) ; // wait until chip detected or wdt reset Serial.println("\nStarting Tsl2561Util autogain loop"); diff --git a/lib/Joba_Tsl2561-2.0.7/examples/Simple/Simple.ino b/lib/Joba_Tsl2561-2.0.10/examples/Simple/Simple.ino similarity index 88% rename from lib/Joba_Tsl2561-2.0.7/examples/Simple/Simple.ino rename to lib/Joba_Tsl2561-2.0.10/examples/Simple/Simple.ino index 5bebb7e50..b63f71b94 100644 --- a/lib/Joba_Tsl2561-2.0.7/examples/Simple/Simple.ino +++ b/lib/Joba_Tsl2561-2.0.10/examples/Simple/Simple.ino @@ -21,7 +21,7 @@ This file is part of the Joba_Tsl2561 Library. #include -// to mimic Serial.printf() of esp8266 core for other platforms +// to mimic Serial.printf() of esp cores for other platforms char *format( const char *fmt, ... ) { static char buf[128]; va_list arg; @@ -36,7 +36,7 @@ Tsl2561 Tsl(Wire); void setup() { Serial.begin(115200); - Wire.begin(); + Wire.begin(TSL2561_SDA, TSL2561_SCL); Serial.println("\nStarting Tsl2561 simple loop"); } @@ -60,7 +60,7 @@ void loop() { Tsl.off(); } else { - Serial.println("No Tsl2561 found. Check wiring."); + Serial.print(format("No Tsl2561 found. Check wiring: SCL=%u, SDA=%u\n", TSL2561_SCL, TSL2561_SDA)); } delay(5000); diff --git a/lib/Joba_Tsl2561-2.0.7/examples/Testing/Testing.ino b/lib/Joba_Tsl2561-2.0.10/examples/Testing/Testing.ino similarity index 97% rename from lib/Joba_Tsl2561-2.0.7/examples/Testing/Testing.ino rename to lib/Joba_Tsl2561-2.0.10/examples/Testing/Testing.ino index 0dbeb09be..861891666 100644 --- a/lib/Joba_Tsl2561-2.0.7/examples/Testing/Testing.ino +++ b/lib/Joba_Tsl2561-2.0.10/examples/Testing/Testing.ino @@ -22,7 +22,7 @@ This file is part of the Joba_Tsl2561 Library. #include -// to mimic Serial.printf() of esp8266 core for other platforms +// to mimic Serial.printf() of esp cores for other platforms char *format( const char *fmt, ... ) { static char buf[128]; va_list arg; @@ -158,7 +158,7 @@ void test( Tsl2561 &tsl ) { void setup() { Serial.begin(115200); - Wire.begin(); + Wire.begin(TSL2561_SDA, TSL2561_SCL); Serial.println("\nStarting Tsl2561 testing loop"); } diff --git a/lib/Joba_Tsl2561-2.0.7/examples/Utility/Utility.ino b/lib/Joba_Tsl2561-2.0.10/examples/Utility/Utility.ino similarity index 93% rename from lib/Joba_Tsl2561-2.0.7/examples/Utility/Utility.ino rename to lib/Joba_Tsl2561-2.0.10/examples/Utility/Utility.ino index d05549616..14ba320d0 100644 --- a/lib/Joba_Tsl2561-2.0.7/examples/Utility/Utility.ino +++ b/lib/Joba_Tsl2561-2.0.10/examples/Utility/Utility.ino @@ -21,7 +21,7 @@ This file is part of the Joba_Tsl2561 Library. #include -// to mimic Serial.printf() of esp8266 core for other platforms +// to mimic Serial.printf() of esp cores for other platforms char *format( const char *fmt, ... ) { static char buf[128]; va_list arg; @@ -37,7 +37,7 @@ Tsl2561 Tsl(Wire); void setup() { Serial.begin(115200); - Wire.begin(); + Wire.begin(TSL2561_SDA, TSL2561_SCL); Serial.println("\nStarting Tsl2561Util loop"); } @@ -91,7 +91,7 @@ void loop() { } if( !found ) { - Serial.println("No Tsl2561 found. Check wiring."); + Serial.print(format("No Tsl2561 found. Check wiring: SCL=%u, SDA=%u\n", TSL2561_SCL, TSL2561_SDA)); } delay(5000); diff --git a/lib/Joba_Tsl2561-2.0.7/platformio.ini b/lib/Joba_Tsl2561-2.0.10/examples/platformio.ini similarity index 67% rename from lib/Joba_Tsl2561-2.0.7/platformio.ini rename to lib/Joba_Tsl2561-2.0.10/examples/platformio.ini index ea6847aaa..07efee0d2 100644 --- a/lib/Joba_Tsl2561-2.0.7/platformio.ini +++ b/lib/Joba_Tsl2561-2.0.10/examples/platformio.ini @@ -18,9 +18,13 @@ [platformio] +src_dir = . +lib_dir = ../.. + ; uncomment one, if you want to build only one ; env_default = nodemcuv2 ; env_default = nano328 +; env_default = espressif32 [env:nodemcuv2] @@ -28,12 +32,12 @@ ; ------------ ; GND <-> GND ; VCC <-> 3V -; SCL <-> D1 -; SDA <-> D2 +; SCL <-> D1 (other pin can be defined below) +; SDA <-> D2 (other pin can be defined below) platform = espressif8266 board = nodemcuv2 framework = arduino -build_flags = -Wall +build_flags = -Wall -DTSL2561_SCL=D1 -DTSL2561_SDA=D2 monitor_speed = 115200 @@ -53,6 +57,25 @@ upload_resetmethod = nodemcu platform = atmelavr board = nanoatmega328 framework = arduino -build_flags = -Wall +build_flags = -Wall -DTSL2561_SCL=A5 -DTSL2561_SDA=A4 monitor_speed = 115200 +;upload_port = /dev/ttyUSB[1-9] + + +[env:espressif32] +; TSL <-> ESP32 +; ------------ +; GND <-> GND +; VCC <-> 3V +; SCL <-> IO22 (other pin can be defined below) +; SDA <-> IO21 (other pin can be defined below) +platform = espressif32 +board = mhetesp32minikit +framework = arduino +build_flags = -Wall -DTSL2561_SCL=22 -DTSL2561_SDA=21 + +monitor_speed = 115200 + +upload_speed = 921600 +;upload_port = /dev/ttyUSB[1-9] diff --git a/lib/Joba_Tsl2561-2.0.7/examples/platformio.sh b/lib/Joba_Tsl2561-2.0.10/examples/platformio.sh similarity index 100% rename from lib/Joba_Tsl2561-2.0.7/examples/platformio.sh rename to lib/Joba_Tsl2561-2.0.10/examples/platformio.sh diff --git a/lib/Joba_Tsl2561-2.0.7/lib/readme.txt b/lib/Joba_Tsl2561-2.0.10/lib/readme.txt similarity index 100% rename from lib/Joba_Tsl2561-2.0.7/lib/readme.txt rename to lib/Joba_Tsl2561-2.0.10/lib/readme.txt diff --git a/lib/Joba_Tsl2561-2.0.7/library.json b/lib/Joba_Tsl2561-2.0.10/library.json similarity index 62% rename from lib/Joba_Tsl2561-2.0.7/library.json rename to lib/Joba_Tsl2561-2.0.10/library.json index 94585c488..86545a6a0 100644 --- a/lib/Joba_Tsl2561-2.0.7/library.json +++ b/lib/Joba_Tsl2561-2.0.10/library.json @@ -1,8 +1,8 @@ { "name": "Joba_Tsl2561", - "version": "2.0.7", + "version": "2.0.10", "keywords": "twowire, i2c, bus, sensor, luminosity, illuminance, lux", - "description": "Arduino Library for ams (taos) luminance chip Tsl2561 with autogain", + "description": "Arduino library for ams (taos) luminance chip Tsl2561 with autogain. Tested with Nano, Esp8266 and Esp32.", "repository": { "type": "git", diff --git a/lib/Joba_Tsl2561-2.0.7/library.properties b/lib/Joba_Tsl2561-2.0.10/library.properties similarity index 67% rename from lib/Joba_Tsl2561-2.0.7/library.properties rename to lib/Joba_Tsl2561-2.0.10/library.properties index ba1840764..5d83edb31 100644 --- a/lib/Joba_Tsl2561-2.0.7/library.properties +++ b/lib/Joba_Tsl2561-2.0.10/library.properties @@ -1,9 +1,9 @@ name=Joba Tsl2561 Library -version=2.0.7 +version=2.0.10 author=joba-1 maintainer=joba-1 sentence=IoT library for using the Tsl2561 luminosity sensor -paragraph=Luminosity measurement in lux with autogain +paragraph=Luminosity measurement in lux with autogain. Tested with Nano, Esp8266 and Esp32. category=Sensors url=https://github.com/joba-1/Joba_Tsl2561 architectures=* diff --git a/lib/Joba_Tsl2561-2.0.7/examples/platformio.ini b/lib/Joba_Tsl2561-2.0.10/platformio.ini similarity index 75% rename from lib/Joba_Tsl2561-2.0.7/examples/platformio.ini rename to lib/Joba_Tsl2561-2.0.10/platformio.ini index ef71e03ae..87d88e183 100644 --- a/lib/Joba_Tsl2561-2.0.7/examples/platformio.ini +++ b/lib/Joba_Tsl2561-2.0.10/platformio.ini @@ -18,20 +18,19 @@ [platformio] -src_dir = . -lib_dir = ../.. - ; uncomment one, if you want to build only one ; env_default = nodemcuv2 ; env_default = nano328 +; env_default = espressif32 + [env:nodemcuv2] ; TSL <-> ESP8266 ; ------------ ; GND <-> GND ; VCC <-> 3V -; SCL <-> D1 -; SDA <-> D2 +; SCL <-> D1 (other pin can be defined below) +; SDA <-> D2 (other pin can be defined below) platform = espressif8266 board = nodemcuv2 framework = arduino @@ -58,4 +57,21 @@ framework = arduino build_flags = -Wall monitor_speed = 115200 + + +[env:espressif32] +; TSL <-> ESP32 +; ------------ +; GND <-> GND +; VCC <-> 3V +; SCL <-> IO22 (other pin can be defined below) +; SDA <-> IO21 (other pin can be defined below) +platform = espressif32 +board = mhetesp32minikit +framework = arduino +build_flags = -Wall -DTSL2561_SCL=22 -DTSL2561_SDA=21 + +monitor_speed = 115200 + +upload_speed = 921600 ;upload_port = /dev/ttyUSB[1-9] diff --git a/lib/Joba_Tsl2561-2.0.7/src/Tsl2561.cpp b/lib/Joba_Tsl2561-2.0.10/src/Tsl2561.cpp similarity index 100% rename from lib/Joba_Tsl2561-2.0.7/src/Tsl2561.cpp rename to lib/Joba_Tsl2561-2.0.10/src/Tsl2561.cpp diff --git a/lib/Joba_Tsl2561-2.0.7/src/Tsl2561.h b/lib/Joba_Tsl2561-2.0.10/src/Tsl2561.h similarity index 94% rename from lib/Joba_Tsl2561-2.0.7/src/Tsl2561.h rename to lib/Joba_Tsl2561-2.0.10/src/Tsl2561.h index 75056f912..147db2a77 100644 --- a/lib/Joba_Tsl2561-2.0.7/src/Tsl2561.h +++ b/lib/Joba_Tsl2561-2.0.10/src/Tsl2561.h @@ -23,6 +23,14 @@ This file is part of the Joba_Tsl2561 Library. #include #include +// To be able to override pin definitions in build flags (used in examples) +#ifndef TSL2561_SDA + #define TSL2561_SDA SDA +#endif +#ifndef TSL2561_SCL + #define TSL2561_SCL SCL +#endif + class Tsl2561 { public: diff --git a/lib/Joba_Tsl2561-2.0.7/src/Tsl2561Util.cpp b/lib/Joba_Tsl2561-2.0.10/src/Tsl2561Util.cpp similarity index 83% rename from lib/Joba_Tsl2561-2.0.7/src/Tsl2561Util.cpp rename to lib/Joba_Tsl2561-2.0.10/src/Tsl2561Util.cpp index ae811f743..aa738bb9f 100644 --- a/lib/Joba_Tsl2561-2.0.7/src/Tsl2561Util.cpp +++ b/lib/Joba_Tsl2561-2.0.10/src/Tsl2561Util.cpp @@ -22,9 +22,10 @@ This file is part of the Joba_Tsl2561 Library. namespace Tsl2561Util { // Tsl2561Util::normalizedLuminosity returncode false can mean: -// - saturation: full and/or ir have value ~0 (aka -1) +// - saturation: full and/or ir have value ~0 (aka -1) and not shortest exposure // - manual exposure time: full and ir are corrected only for gain -// If true, full and ir have values as if exposure was 402 and gain 16. +// If true, full and ir have values as if exposure was 402 and gain 16 +// or ~0 if saturated even at shortest exposure bool normalizedLuminosity( bool gain, Tsl2561::exposure_t exposure, uint32_t &full, uint32_t &ir ) { uint16_t scaledFull = (uint16_t)full; @@ -39,8 +40,8 @@ bool normalizedLuminosity( bool gain, Tsl2561::exposure_t exposure, uint32_t &fu switch( exposure ) { case Tsl2561::EXP_14: - full = (scaledFull >= 5047/4*3) ? 0xffffffff : ((full + 5) * 322) / 11; - ir = (scaledIr >= 5047/4*3) ? 0xffffffff : ((ir + 5) * 322) / 11; + full = (scaledFull == 5047) ? 0xffffffff : ((full + 5) * 322) / 11; + ir = (scaledIr == 5047) ? 0xffffffff : ((ir + 5) * 322) / 11; break; case Tsl2561::EXP_101: full = (scaledFull >= 37177/4*3) ? 0xffffffff : ((full + 40) * 322) / 81; @@ -54,7 +55,7 @@ bool normalizedLuminosity( bool gain, Tsl2561::exposure_t exposure, uint32_t &fu return false; } - return full != 0xffffffff && ir != 0xffffffff; + return exposure == Tsl2561::EXP_14 || (full != 0xffffffff && ir != 0xffffffff); } return false; @@ -93,6 +94,8 @@ bool autoGain( Tsl2561 &tsl, bool &gain, Tsl2561::exposure_t &exposure, uint16_t { true, Tsl2561::EXP_402 } // max }; + // Serial.printf("autoGain start: gain=%u, expo=%u\n", gain, exposure); + // get current sensitivity if( !tsl.getSensitivity(gain, exposure) ) { return false; // I2C error @@ -110,9 +113,10 @@ bool autoGain( Tsl2561 &tsl, bool &gain, Tsl2561::exposure_t &exposure, uint16_t return false; // should not happen... } - // in a loop wait for next sample, get values and adjust sensitivity if needed + // sometimes sensor reports high brightness although it is darker. uint8_t retryOnSaturated = 10; + // in a loop wait for next sample, get values and adjust sensitivity if needed while( true ) { waitNext(exposure); @@ -122,11 +126,14 @@ bool autoGain( Tsl2561 &tsl, bool &gain, Tsl2561::exposure_t &exposure, uint16_t uint16_t limit = getLimit(exposure); if( full >= 1000 && full <= limit ) { - return true; // new value within limits + // Serial.printf("autoGain normal full=%u, limits=1000-%u, curr=%u\n", full, limit, curr); + return true; // new value within limits of good accuracy } + // adjust sensitivity, if possible if( (full < 1000 && ++curr < sizeof(sensitivity)/sizeof(sensitivity[0])) || (full > limit && curr-- > 0) ) { + // Serial.printf("autoGain adjust full=%u, limits=1000-%u, curr=%u\n", full, limit, curr); if( !tsl.setSensitivity(sensitivity[curr].gain, sensitivity[curr].exposure) ) { return false; // I2C error } @@ -134,7 +141,10 @@ bool autoGain( Tsl2561 &tsl, bool &gain, Tsl2561::exposure_t &exposure, uint16_t exposure = sensitivity[curr].exposure; } else { - if( ++curr > 0 && retryOnSaturated-- == 0 ) { + // sensitivity already is at minimum or maximum + if( ++curr > 0 || retryOnSaturated-- == 0 ) { + // Serial.printf("autoGain limit full=%u, ir=%u, limits=1000-%u, curr=%u, retry=%u\n", full, ir, limit, curr, retryOnSaturated); + // dark, or repeatedly confirmed high brightness return true; // saturated, but best we can do } } @@ -176,6 +186,11 @@ bool milliLux( uint32_t full, uint32_t ir, uint32_t &mLux, bool csType, uint8_t return true; } + if( full == 0xffffffff || ir == 0xffffffff ) { + mLux = 99999999; // indicates saturation at shortest exposure + return true; + } + uint32_t milliRatio = ir * 1000 / full; if( csType ) { diff --git a/lib/Joba_Tsl2561-2.0.7/src/Tsl2561Util.h b/lib/Joba_Tsl2561-2.0.10/src/Tsl2561Util.h similarity index 100% rename from lib/Joba_Tsl2561-2.0.7/src/Tsl2561Util.h rename to lib/Joba_Tsl2561-2.0.10/src/Tsl2561Util.h diff --git a/lib/PubSubClient-EspEasy-2.6.09/tests/testcases/mqtt_basic.py b/lib/PubSubClient-EspEasy-2.6.09/tests/testcases/mqtt_basic.py deleted file mode 100644 index 1b0cc65bb..000000000 --- a/lib/PubSubClient-EspEasy-2.6.09/tests/testcases/mqtt_basic.py +++ /dev/null @@ -1,43 +0,0 @@ -import unittest -import settings - -import time -import mosquitto - -import serial - -def on_message(mosq, obj, msg): - obj.message_queue.append(msg) - -class mqtt_basic(unittest.TestCase): - - message_queue = [] - - @classmethod - def setUpClass(self): - self.client = mosquitto.Mosquitto("pubsubclient_ut", clean_session=True,obj=self) - self.client.connect(settings.server_ip) - self.client.on_message = on_message - self.client.subscribe("outTopic",0) - - @classmethod - def tearDownClass(self): - self.client.disconnect() - - def test_one(self): - i=30 - while len(self.message_queue) == 0 and i > 0: - self.client.loop() - time.sleep(0.5) - i -= 1 - self.assertTrue(i>0, "message receive timed-out") - self.assertEqual(len(self.message_queue), 1, "unexpected number of messages received") - msg = self.message_queue[0] - self.assertEqual(msg.mid,0,"message id not 0") - self.assertEqual(msg.topic,"outTopic","message topic incorrect") - self.assertEqual(msg.payload,"hello world") - self.assertEqual(msg.qos,0,"message qos not 0") - self.assertEqual(msg.retain,False,"message retain flag incorrect") - - - diff --git a/lib/PubSubClient-EspEasy-2.6.09/tests/testcases/mqtt_publish_in_callback.py b/lib/PubSubClient-EspEasy-2.6.09/tests/testcases/mqtt_publish_in_callback.py deleted file mode 100644 index 7989f7f17..000000000 --- a/lib/PubSubClient-EspEasy-2.6.09/tests/testcases/mqtt_publish_in_callback.py +++ /dev/null @@ -1,64 +0,0 @@ -import unittest -import settings - -import time -import mosquitto - -import serial - -def on_message(mosq, obj, msg): - obj.message_queue.append(msg) - -class mqtt_publish_in_callback(unittest.TestCase): - - message_queue = [] - - @classmethod - def setUpClass(self): - self.client = mosquitto.Mosquitto("pubsubclient_ut", clean_session=True,obj=self) - self.client.connect(settings.server_ip) - self.client.on_message = on_message - self.client.subscribe("outTopic",0) - - @classmethod - def tearDownClass(self): - self.client.disconnect() - - def test_connect(self): - i=30 - while len(self.message_queue) == 0 and i > 0: - self.client.loop() - time.sleep(0.5) - i -= 1 - self.assertTrue(i>0, "message receive timed-out") - self.assertEqual(len(self.message_queue), 1, "unexpected number of messages received") - msg = self.message_queue.pop(0) - self.assertEqual(msg.mid,0,"message id not 0") - self.assertEqual(msg.topic,"outTopic","message topic incorrect") - self.assertEqual(msg.payload,"hello world") - self.assertEqual(msg.qos,0,"message qos not 0") - self.assertEqual(msg.retain,False,"message retain flag incorrect") - - - def test_publish(self): - self.assertEqual(len(self.message_queue), 0, "message queue not empty") - payload = "abcdefghij" - self.client.publish("inTopic",payload) - - i=30 - while len(self.message_queue) == 0 and i > 0: - self.client.loop() - time.sleep(0.5) - i -= 1 - - self.assertTrue(i>0, "message receive timed-out") - self.assertEqual(len(self.message_queue), 1, "unexpected number of messages received") - msg = self.message_queue.pop(0) - self.assertEqual(msg.mid,0,"message id not 0") - self.assertEqual(msg.topic,"outTopic","message topic incorrect") - self.assertEqual(msg.payload,payload) - self.assertEqual(msg.qos,0,"message qos not 0") - self.assertEqual(msg.retain,False,"message retain flag incorrect") - - - diff --git a/lib/PubSubClient-EspEasy-2.6.09/tests/testsuite.py b/lib/PubSubClient-EspEasy-2.6.09/tests/testsuite.py deleted file mode 100644 index 0a8e70dfd..000000000 --- a/lib/PubSubClient-EspEasy-2.6.09/tests/testsuite.py +++ /dev/null @@ -1,179 +0,0 @@ -#!/usr/bin/env python -import os -import os.path -import sys -import shutil -from subprocess import call -import importlib -import unittest -import re - -from testcases import settings - -class Workspace(object): - - def __init__(self): - self.root_dir = os.getcwd() - self.build_dir = os.path.join(self.root_dir,"tmpbin"); - self.log_dir = os.path.join(self.root_dir,"logs"); - self.tests_dir = os.path.join(self.root_dir,"testcases"); - self.examples_dir = os.path.join(self.root_dir,"../PubSubClient/examples") - self.examples = [] - self.tests = [] - if not os.path.isdir("../PubSubClient"): - raise Exception("Cannot find PubSubClient library") - try: - import ino - except: - raise Exception("ino tool not installed") - - def init(self): - if os.path.isdir(self.build_dir): - shutil.rmtree(self.build_dir) - os.mkdir(self.build_dir) - if os.path.isdir(self.log_dir): - shutil.rmtree(self.log_dir) - os.mkdir(self.log_dir) - - os.chdir(self.build_dir) - call(["ino","init"]) - - shutil.copytree("../../PubSubClient","lib/PubSubClient") - - filenames = [] - for root, dirs, files in os.walk(self.examples_dir): - filenames += [os.path.join(root,f) for f in files if f.endswith(".ino")] - filenames.sort() - for e in filenames: - self.examples.append(Sketch(self,e)) - - filenames = [] - for root, dirs, files in os.walk(self.tests_dir): - filenames += [os.path.join(root,f) for f in files if f.endswith(".ino")] - filenames.sort() - for e in filenames: - self.tests.append(Sketch(self,e)) - - def clean(self): - shutil.rmtree(self.build_dir) - -class Sketch(object): - def __init__(self,wksp,fn): - self.w = wksp - self.filename = fn - self.basename = os.path.basename(self.filename) - self.build_log = os.path.join(self.w.log_dir,"%s.log"%(os.path.basename(self.filename),)) - self.build_err_log = os.path.join(self.w.log_dir,"%s.err.log"%(os.path.basename(self.filename),)) - self.build_upload_log = os.path.join(self.w.log_dir,"%s.upload.log"%(os.path.basename(self.filename),)) - - def build(self): - sys.stdout.write(" Build: ") - sys.stdout.flush() - - # Copy sketch over, replacing IP addresses as necessary - fin = open(self.filename,"r") - lines = fin.readlines() - fin.close() - fout = open(os.path.join(self.w.build_dir,"src","sketch.ino"),"w") - for l in lines: - if re.match(r"^byte server\[\] = {",l): - fout.write("byte server[] = { %s };\n"%(settings.server_ip.replace(".",", "),)) - elif re.match(r"^byte ip\[\] = {",l): - fout.write("byte ip[] = { %s };\n"%(settings.arduino_ip.replace(".",", "),)) - else: - fout.write(l) - fout.flush() - fout.close() - - # Run build - fout = open(self.build_log, "w") - ferr = open(self.build_err_log, "w") - rc = call(["ino","build"],stdout=fout,stderr=ferr) - fout.close() - ferr.close() - if rc == 0: - sys.stdout.write("pass") - sys.stdout.write("\n") - return True - else: - sys.stdout.write("fail") - sys.stdout.write("\n") - with open(self.build_err_log) as f: - for line in f: - print " ",line, - return False - - def upload(self): - sys.stdout.write(" Upload: ") - sys.stdout.flush() - fout = open(self.build_upload_log, "w") - rc = call(["ino","upload"],stdout=fout,stderr=fout) - fout.close() - if rc == 0: - sys.stdout.write("pass") - sys.stdout.write("\n") - return True - else: - sys.stdout.write("fail") - sys.stdout.write("\n") - with open(self.build_upload_log) as f: - for line in f: - print " ",line, - return False - - - def test(self): - # import the matching test case, if it exists - try: - basename = os.path.basename(self.filename)[:-4] - i = importlib.import_module("testcases."+basename) - except: - sys.stdout.write(" Test: no tests found") - sys.stdout.write("\n") - return - c = getattr(i,basename) - - testmethods = [m for m in dir(c) if m.startswith("test_")] - testmethods.sort() - tests = [] - for m in testmethods: - tests.append(c(m)) - - result = unittest.TestResult() - c.setUpClass() - if self.upload(): - sys.stdout.write(" Test: ") - sys.stdout.flush() - for t in tests: - t.run(result) - print "%d/%d"%(result.testsRun-len(result.failures)-len(result.errors),result.testsRun) - if not result.wasSuccessful(): - if len(result.failures) > 0: - for f in result.failures: - print "-- %s"%(str(f[0]),) - print f[1] - if len(result.errors) > 0: - print " Errors:" - for f in result.errors: - print "-- %s"%(str(f[0]),) - print f[1] - c.tearDownClass() - -if __name__ == '__main__': - run_tests = True - - w = Workspace() - w.init() - - for e in w.examples: - print "--------------------------------------" - print "[%s]"%(e.basename,) - if e.build() and run_tests: - e.test() - for e in w.tests: - print "--------------------------------------" - print "[%s]"%(e.basename,) - if e.build() and run_tests: - e.test() - - w.clean() diff --git a/lib/PubSubClient-EspEasy-2.6.09/.gitignore b/lib/PubSubClient-EspEasy-2.7.12/.gitignore similarity index 100% rename from lib/PubSubClient-EspEasy-2.6.09/.gitignore rename to lib/PubSubClient-EspEasy-2.7.12/.gitignore diff --git a/lib/PubSubClient-EspEasy-2.6.09/.travis.yml b/lib/PubSubClient-EspEasy-2.7.12/.travis.yml similarity index 100% rename from lib/PubSubClient-EspEasy-2.6.09/.travis.yml rename to lib/PubSubClient-EspEasy-2.7.12/.travis.yml diff --git a/lib/PubSubClient-EspEasy-2.6.09/CHANGES.txt b/lib/PubSubClient-EspEasy-2.7.12/CHANGES.txt similarity index 86% rename from lib/PubSubClient-EspEasy-2.6.09/CHANGES.txt rename to lib/PubSubClient-EspEasy-2.7.12/CHANGES.txt index 8c8bef64e..ff4da62ab 100644 --- a/lib/PubSubClient-EspEasy-2.6.09/CHANGES.txt +++ b/lib/PubSubClient-EspEasy-2.7.12/CHANGES.txt @@ -1,8 +1,16 @@ +2.7 + * Fix remaining-length handling to prevent buffer overrun + * Add large-payload API - beginPublish/write/publish/endPublish + * Add yield call to improve reliability on ESP + * Add Clean Session flag to connect options + * Add ESP32 support for functional callback signature + * Various other fixes + 2.4 * Add MQTT_SOCKET_TIMEOUT to prevent it blocking indefinitely whilst waiting for inbound data * Fixed return code when publishing >256 bytes - + 2.3 * Add publish(topic,payload,retained) function diff --git a/lib/PubSubClient-EspEasy-2.6.09/LICENSE.txt b/lib/PubSubClient-EspEasy-2.7.12/LICENSE.txt similarity index 100% rename from lib/PubSubClient-EspEasy-2.6.09/LICENSE.txt rename to lib/PubSubClient-EspEasy-2.7.12/LICENSE.txt diff --git a/lib/PubSubClient-EspEasy-2.6.09/README.md b/lib/PubSubClient-EspEasy-2.7.12/README.md similarity index 95% rename from lib/PubSubClient-EspEasy-2.6.09/README.md rename to lib/PubSubClient-EspEasy-2.7.12/README.md index 83176919c..69cbb8f0c 100644 --- a/lib/PubSubClient-EspEasy-2.6.09/README.md +++ b/lib/PubSubClient-EspEasy-2.7.12/README.md @@ -8,7 +8,7 @@ a server that supports MQTT. The library comes with a number of example sketches. See File > Examples > PubSubClient within the Arduino application. -Full API documentation is available here: http://pubsubclient.knolleary.net +Full API documentation is available here: https://pubsubclient.knolleary.net ## Limitations @@ -37,6 +37,7 @@ boards and shields, including: - TI CC3000 WiFi - [library](https://github.com/sparkfun/SFE_CC3000_Library) - Intel Galileo/Edison - ESP8266 + - ESP32 The library cannot currently be used with hardware based on the ENC28J60 chip – such as the Nanode or the Nuelectronics Ethernet Shield. For those, there is an diff --git a/lib/PubSubClient-EspEasy-2.6.09/examples/mqtt_auth/mqtt_auth.ino b/lib/PubSubClient-EspEasy-2.7.12/examples/mqtt_auth/mqtt_auth.ino similarity index 100% rename from lib/PubSubClient-EspEasy-2.6.09/examples/mqtt_auth/mqtt_auth.ino rename to lib/PubSubClient-EspEasy-2.7.12/examples/mqtt_auth/mqtt_auth.ino diff --git a/lib/PubSubClient-EspEasy-2.6.09/examples/mqtt_basic/mqtt_basic.ino b/lib/PubSubClient-EspEasy-2.7.12/examples/mqtt_basic/mqtt_basic.ino similarity index 100% rename from lib/PubSubClient-EspEasy-2.6.09/examples/mqtt_basic/mqtt_basic.ino rename to lib/PubSubClient-EspEasy-2.7.12/examples/mqtt_basic/mqtt_basic.ino diff --git a/lib/PubSubClient-EspEasy-2.6.09/examples/mqtt_esp8266/mqtt_esp8266.ino b/lib/PubSubClient-EspEasy-2.7.12/examples/mqtt_esp8266/mqtt_esp8266.ino similarity index 92% rename from lib/PubSubClient-EspEasy-2.6.09/examples/mqtt_esp8266/mqtt_esp8266.ino rename to lib/PubSubClient-EspEasy-2.7.12/examples/mqtt_esp8266/mqtt_esp8266.ino index e46f85f3e..e7357b507 100644 --- a/lib/PubSubClient-EspEasy-2.6.09/examples/mqtt_esp8266/mqtt_esp8266.ino +++ b/lib/PubSubClient-EspEasy-2.7.12/examples/mqtt_esp8266/mqtt_esp8266.ino @@ -38,14 +38,6 @@ long lastMsg = 0; char msg[50]; int value = 0; -void setup() { - pinMode(BUILTIN_LED, OUTPUT); // Initialize the BUILTIN_LED pin as an output - Serial.begin(115200); - setup_wifi(); - client.setServer(mqtt_server, 1883); - client.setCallback(callback); -} - void setup_wifi() { delay(10); @@ -61,6 +53,8 @@ void setup_wifi() { Serial.print("."); } + randomSeed(micros()); + Serial.println(""); Serial.println("WiFi connected"); Serial.println("IP address: "); @@ -80,7 +74,7 @@ void callback(char* topic, byte* payload, unsigned int length) { if ((char)payload[0] == '1') { digitalWrite(BUILTIN_LED, LOW); // Turn the LED on (Note that LOW is the voltage level // but actually the LED is on; this is because - // it is acive low on the ESP-01) + // it is active low on the ESP-01) } else { digitalWrite(BUILTIN_LED, HIGH); // Turn the LED off by making the voltage HIGH } @@ -91,8 +85,11 @@ void reconnect() { // Loop until we're reconnected while (!client.connected()) { Serial.print("Attempting MQTT connection..."); + // Create a random client ID + String clientId = "ESP8266Client-"; + clientId += String(random(0xffff), HEX); // Attempt to connect - if (client.connect("ESP8266Client")) { + if (client.connect(clientId.c_str())) { Serial.println("connected"); // Once connected, publish an announcement... client.publish("outTopic", "hello world"); @@ -107,6 +104,15 @@ void reconnect() { } } } + +void setup() { + pinMode(BUILTIN_LED, OUTPUT); // Initialize the BUILTIN_LED pin as an output + Serial.begin(115200); + setup_wifi(); + client.setServer(mqtt_server, 1883); + client.setCallback(callback); +} + void loop() { if (!client.connected()) { @@ -118,7 +124,7 @@ void loop() { if (now - lastMsg > 2000) { lastMsg = now; ++value; - snprintf (msg, 75, "hello world #%ld", value); + snprintf (msg, 50, "hello world #%ld", value); Serial.print("Publish message: "); Serial.println(msg); client.publish("outTopic", msg); diff --git a/lib/PubSubClient-EspEasy-2.7.12/examples/mqtt_large_message/mqtt_large_message.ino b/lib/PubSubClient-EspEasy-2.7.12/examples/mqtt_large_message/mqtt_large_message.ino new file mode 100644 index 000000000..e048c3ed3 --- /dev/null +++ b/lib/PubSubClient-EspEasy-2.7.12/examples/mqtt_large_message/mqtt_large_message.ino @@ -0,0 +1,179 @@ +/* + Long message ESP8266 MQTT example + + This sketch demonstrates sending arbitrarily large messages in combination + with the ESP8266 board/library. + + It connects to an MQTT server then: + - publishes "hello world" to the topic "outTopic" + - subscribes to the topic "greenBottles/#", printing out any messages + it receives. NB - it assumes the received payloads are strings not binary + - If the sub-topic is a number, it publishes a "greenBottles/lyrics" message + with a payload consisting of the lyrics to "10 green bottles", replacing + 10 with the number given in the sub-topic. + + It will reconnect to the server if the connection is lost using a blocking + reconnect function. See the 'mqtt_reconnect_nonblocking' example for how to + achieve the same result without blocking the main loop. + + To install the ESP8266 board, (using Arduino 1.6.4+): + - Add the following 3rd party board manager under "File -> Preferences -> Additional Boards Manager URLs": + http://arduino.esp8266.com/stable/package_esp8266com_index.json + - Open the "Tools -> Board -> Board Manager" and click install for the ESP8266" + - Select your ESP8266 in "Tools -> Board" + +*/ + +#include +#include + +// Update these with values suitable for your network. + +const char* ssid = "........"; +const char* password = "........"; +const char* mqtt_server = "broker.mqtt-dashboard.com"; + +WiFiClient espClient; +PubSubClient client(espClient); +long lastMsg = 0; +char msg[50]; +int value = 0; + +void setup_wifi() { + + delay(10); + // We start by connecting to a WiFi network + Serial.println(); + Serial.print("Connecting to "); + Serial.println(ssid); + + WiFi.begin(ssid, password); + + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + + randomSeed(micros()); + + Serial.println(""); + Serial.println("WiFi connected"); + Serial.println("IP address: "); + Serial.println(WiFi.localIP()); +} + +void callback(char* topic, byte* payload, unsigned int length) { + Serial.print("Message arrived ["); + Serial.print(topic); + Serial.print("] "); + for (int i = 0; i < length; i++) { + Serial.print((char)payload[i]); + } + Serial.println(); + + // Find out how many bottles we should generate lyrics for + String topicStr(topic); + int bottleCount = 0; // assume no bottles unless we correctly parse a value from the topic + if (topicStr.indexOf('/') >= 0) { + // The topic includes a '/', we'll try to read the number of bottles from just after that + topicStr.remove(0, topicStr.indexOf('/')+1); + // Now see if there's a number of bottles after the '/' + bottleCount = topicStr.toInt(); + } + + if (bottleCount > 0) { + // Work out how big our resulting message will be + int msgLen = 0; + for (int i = bottleCount; i > 0; i--) { + String numBottles(i); + msgLen += 2*numBottles.length(); + if (i == 1) { + msgLen += 2*String(" green bottle, standing on the wall\n").length(); + } else { + msgLen += 2*String(" green bottles, standing on the wall\n").length(); + } + msgLen += String("And if one green bottle should accidentally fall\nThere'll be ").length(); + switch (i) { + case 1: + msgLen += String("no green bottles, standing on the wall\n\n").length(); + break; + case 2: + msgLen += String("1 green bottle, standing on the wall\n\n").length(); + break; + default: + numBottles = i-1; + msgLen += numBottles.length(); + msgLen += String(" green bottles, standing on the wall\n\n").length(); + break; + }; + } + + // Now we can start to publish the message + client.beginPublish("greenBottles/lyrics", msgLen, false); + for (int i = bottleCount; i > 0; i--) { + for (int j = 0; j < 2; j++) { + client.print(i); + if (i == 1) { + client.print(" green bottle, standing on the wall\n"); + } else { + client.print(" green bottles, standing on the wall\n"); + } + } + client.print("And if one green bottle should accidentally fall\nThere'll be "); + switch (i) { + case 1: + client.print("no green bottles, standing on the wall\n\n"); + break; + case 2: + client.print("1 green bottle, standing on the wall\n\n"); + break; + default: + client.print(i-1); + client.print(" green bottles, standing on the wall\n\n"); + break; + }; + } + // Now we're done! + client.endPublish(); + } +} + +void reconnect() { + // Loop until we're reconnected + while (!client.connected()) { + Serial.print("Attempting MQTT connection..."); + // Create a random client ID + String clientId = "ESP8266Client-"; + clientId += String(random(0xffff), HEX); + // Attempt to connect + if (client.connect(clientId.c_str())) { + Serial.println("connected"); + // Once connected, publish an announcement... + client.publish("outTopic", "hello world"); + // ... and resubscribe + client.subscribe("greenBottles/#"); + } else { + Serial.print("failed, rc="); + Serial.print(client.state()); + Serial.println(" try again in 5 seconds"); + // Wait 5 seconds before retrying + delay(5000); + } + } +} + +void setup() { + pinMode(BUILTIN_LED, OUTPUT); // Initialize the BUILTIN_LED pin as an output + Serial.begin(115200); + setup_wifi(); + client.setServer(mqtt_server, 1883); + client.setCallback(callback); +} + +void loop() { + + if (!client.connected()) { + reconnect(); + } + client.loop(); +} diff --git a/lib/PubSubClient-EspEasy-2.6.09/examples/mqtt_publish_in_callback/mqtt_publish_in_callback.ino b/lib/PubSubClient-EspEasy-2.7.12/examples/mqtt_publish_in_callback/mqtt_publish_in_callback.ino similarity index 100% rename from lib/PubSubClient-EspEasy-2.6.09/examples/mqtt_publish_in_callback/mqtt_publish_in_callback.ino rename to lib/PubSubClient-EspEasy-2.7.12/examples/mqtt_publish_in_callback/mqtt_publish_in_callback.ino diff --git a/lib/PubSubClient-EspEasy-2.6.09/examples/mqtt_reconnect_nonblocking/mqtt_reconnect_nonblocking.ino b/lib/PubSubClient-EspEasy-2.7.12/examples/mqtt_reconnect_nonblocking/mqtt_reconnect_nonblocking.ino similarity index 100% rename from lib/PubSubClient-EspEasy-2.6.09/examples/mqtt_reconnect_nonblocking/mqtt_reconnect_nonblocking.ino rename to lib/PubSubClient-EspEasy-2.7.12/examples/mqtt_reconnect_nonblocking/mqtt_reconnect_nonblocking.ino diff --git a/lib/PubSubClient-EspEasy-2.6.09/examples/mqtt_stream/mqtt_stream.ino b/lib/PubSubClient-EspEasy-2.7.12/examples/mqtt_stream/mqtt_stream.ino similarity index 100% rename from lib/PubSubClient-EspEasy-2.6.09/examples/mqtt_stream/mqtt_stream.ino rename to lib/PubSubClient-EspEasy-2.7.12/examples/mqtt_stream/mqtt_stream.ino diff --git a/lib/PubSubClient-EspEasy-2.6.09/keywords.txt b/lib/PubSubClient-EspEasy-2.7.12/keywords.txt similarity index 91% rename from lib/PubSubClient-EspEasy-2.6.09/keywords.txt rename to lib/PubSubClient-EspEasy-2.7.12/keywords.txt index b979588fe..1ee23d0fa 100644 --- a/lib/PubSubClient-EspEasy-2.6.09/keywords.txt +++ b/lib/PubSubClient-EspEasy-2.7.12/keywords.txt @@ -16,6 +16,9 @@ connect KEYWORD2 disconnect KEYWORD2 publish KEYWORD2 publish_P KEYWORD2 +beginPublish KEYWORD2 +endPublish KEYWORD2 +write KEYWORD2 subscribe KEYWORD2 unsubscribe KEYWORD2 loop KEYWORD2 diff --git a/lib/PubSubClient-EspEasy-2.6.09/library.json b/lib/PubSubClient-EspEasy-2.7.12/library.json similarity index 97% rename from lib/PubSubClient-EspEasy-2.6.09/library.json rename to lib/PubSubClient-EspEasy-2.7.12/library.json index b96739078..8a36a1c5e 100644 --- a/lib/PubSubClient-EspEasy-2.6.09/library.json +++ b/lib/PubSubClient-EspEasy-2.7.12/library.json @@ -6,7 +6,7 @@ "type": "git", "url": "https://github.com/knolleary/pubsubclient.git" }, - "version": "2.6", + "version": "2.7", "exclude": "tests", "examples": "examples/*/*.ino", "frameworks": "arduino", diff --git a/lib/PubSubClient-EspEasy-2.6.09/library.properties b/lib/PubSubClient-EspEasy-2.7.12/library.properties similarity index 98% rename from lib/PubSubClient-EspEasy-2.6.09/library.properties rename to lib/PubSubClient-EspEasy-2.7.12/library.properties index 3ceeda81c..1ae97882e 100644 --- a/lib/PubSubClient-EspEasy-2.6.09/library.properties +++ b/lib/PubSubClient-EspEasy-2.7.12/library.properties @@ -1,5 +1,5 @@ name=PubSubClient -version=2.6 +version=2.7 author=Nick O'Leary maintainer=Nick O'Leary sentence=A client library for MQTT messaging. diff --git a/lib/PubSubClient-EspEasy-2.6.09/src/PubSubClient.cpp b/lib/PubSubClient-EspEasy-2.7.12/src/PubSubClient.cpp similarity index 79% rename from lib/PubSubClient-EspEasy-2.6.09/src/PubSubClient.cpp rename to lib/PubSubClient-EspEasy-2.7.12/src/PubSubClient.cpp index 79eb2d52e..9fe15006a 100644 --- a/lib/PubSubClient-EspEasy-2.6.09/src/PubSubClient.cpp +++ b/lib/PubSubClient-EspEasy-2.7.12/src/PubSubClient.cpp @@ -102,30 +102,41 @@ PubSubClient::PubSubClient(const char* domain, uint16_t port, MQTT_CALLBACK_SIGN } boolean PubSubClient::connect(const char *id) { - return connect(id,NULL,NULL,0,0,0,0); + return connect(id,NULL,NULL,0,0,0,0,1); } boolean PubSubClient::connect(const char *id, const char *user, const char *pass) { - return connect(id,user,pass,0,0,0,0); + return connect(id,user,pass,0,0,0,0,1); } boolean PubSubClient::connect(const char *id, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage) { - return connect(id,NULL,NULL,willTopic,willQos,willRetain,willMessage); + return connect(id,NULL,NULL,willTopic,willQos,willRetain,willMessage,1); } boolean PubSubClient::connect(const char *id, const char *user, const char *pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage) { + return connect(id,user,pass,willTopic,willQos,willRetain,willMessage,1); +} + +boolean PubSubClient::connect(const char *id, const char *user, const char *pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage, boolean cleanSession) { if (!connected()) { int result = 0; - if (domain.length() != 0) { - result = _client->connect(this->domain.c_str(), this->port); + if (_client == nullptr) { + return false; + } + if (_client->connected()) { + result = 1; } else { - result = _client->connect(this->ip, this->port); + if (domain != NULL) { + result = _client->connect(this->domain.c_str(), this->port); + } else { + result = _client->connect(this->ip, this->port); + } } if (result == 1) { nextMsgId = 1; // Leave room in the buffer for header and variable length field - uint16_t length = 5; + uint16_t length = MQTT_MAX_HEADER_SIZE; unsigned int j; #if MQTT_VERSION == MQTT_VERSION_3_1 @@ -141,9 +152,12 @@ boolean PubSubClient::connect(const char *id, const char *user, const char *pass uint8_t v; if (willTopic) { - v = 0x06|(willQos<<3)|(willRetain<<5); + v = 0x04|(willQos<<3)|(willRetain<<5); } else { - v = 0x02; + v = 0x00; + } + if (cleanSession) { + v = v|0x02; } if(user != NULL) { @@ -158,24 +172,31 @@ boolean PubSubClient::connect(const char *id, const char *user, const char *pass buffer[length++] = ((MQTT_KEEPALIVE) >> 8); buffer[length++] = ((MQTT_KEEPALIVE) & 0xFF); + + CHECK_STRING_LENGTH(length,id) length = writeString(id,buffer,length); if (willTopic) { + CHECK_STRING_LENGTH(length,willTopic) length = writeString(willTopic,buffer,length); + CHECK_STRING_LENGTH(length,willMessage) length = writeString(willMessage,buffer,length); } if(user != NULL) { + CHECK_STRING_LENGTH(length,user) length = writeString(user,buffer,length); if(pass != NULL) { + CHECK_STRING_LENGTH(length,pass) length = writeString(pass,buffer,length); } } - write(MQTTCONNECT,buffer,length-5); + write(MQTTCONNECT,buffer,length-MQTT_MAX_HEADER_SIZE); lastInActivity = lastOutActivity = millis(); while (!_client->available()) { + delay(0); // Prevent watchdog crashes unsigned long t = millis(); if (t-lastInActivity >= ((int32_t) MQTT_SOCKET_TIMEOUT*1000UL)) { _state = MQTT_CONNECTION_TIMEOUT; @@ -207,9 +228,12 @@ boolean PubSubClient::connect(const char *id, const char *user, const char *pass // reads a byte into result boolean PubSubClient::readByte(uint8_t * result) { + if (_client == nullptr) { + return false; + } uint32_t previousMillis = millis(); while(!_client->available()) { - delay(1); // Add esp8266 de-blocking (Tasmota #790, EspEasy #1943) + delay(1); // Prevent watchdog crashes uint32_t currentMillis = millis(); if(currentMillis - previousMillis >= ((int32_t) MQTT_SOCKET_TIMEOUT * 1000)){ return false; @@ -241,7 +265,7 @@ uint16_t PubSubClient::readPacket(uint8_t* lengthLength) { uint8_t start = 0; do { - if (len == 6) { + if (len == 5) { // Invalid remaining length encoding - kill the connection _state = MQTT_DISCONNECTED; _client->stop(); @@ -353,11 +377,13 @@ boolean PubSubClient::loop() { } boolean PubSubClient::publish(const char* topic, const char* payload) { - return publish(topic,(const uint8_t*)payload,strlen(payload),false); + size_t plength = (payload != nullptr) ? strlen(payload) : 0; + return publish(topic,(const uint8_t*)payload,plength,false); } boolean PubSubClient::publish(const char* topic, const char* payload, boolean retained) { - return publish(topic,(const uint8_t*)payload,strlen(payload),retained); + size_t plength = (payload != nullptr) ? strlen(payload) : 0; + return publish(topic,(const uint8_t*)payload,plength,retained); } boolean PubSubClient::publish(const char* topic, const uint8_t* payload, unsigned int plength) { @@ -366,12 +392,12 @@ boolean PubSubClient::publish(const char* topic, const uint8_t* payload, unsigne boolean PubSubClient::publish(const char* topic, const uint8_t* payload, unsigned int plength, boolean retained) { if (connected()) { - if (MQTT_MAX_PACKET_SIZE < 5 + 2+strlen(topic) + plength) { + if (MQTT_MAX_PACKET_SIZE < MQTT_MAX_HEADER_SIZE + 2+strlen(topic) + plength) { // Too long return false; } // Leave room in the buffer for header and variable length field - uint16_t length = 5; + uint16_t length = MQTT_MAX_HEADER_SIZE; length = writeString(topic,buffer,length); uint16_t i; for (i=0;iwrite(buffer+(MQTT_MAX_HEADER_SIZE-hlen),length-(MQTT_MAX_HEADER_SIZE-hlen)); + lastOutActivity = millis(); + return (rc == (length-(MQTT_MAX_HEADER_SIZE-hlen))); + } + return false; +} + +int PubSubClient::endPublish() { + return 1; +} + +size_t PubSubClient::write(uint8_t data) { + lastOutActivity = millis(); + if (_client == nullptr) { + return 0; + } + return _client->write(data); +} + +size_t PubSubClient::write(const uint8_t *buffer, size_t size) { + lastOutActivity = millis(); + if (_client == nullptr) { + return 0; + } + return _client->write(buffer,size); +} + +size_t PubSubClient::buildHeader(uint8_t header, uint8_t* buf, uint16_t length) { uint8_t lenBuf[4]; uint8_t llen = 0; uint8_t digit; uint8_t pos = 0; - uint16_t rc; uint16_t len = length; do { digit = len % 128; @@ -450,15 +519,22 @@ boolean PubSubClient::write(uint8_t header, uint8_t* buf, uint16_t length) { buf[4-llen] = header; for (int i=0;i 0) && result) { + delay(0); // Prevent watchdog crashes bytesToWrite = (bytesRemaining > MQTT_MAX_TRANSFER_SIZE)?MQTT_MAX_TRANSFER_SIZE:bytesRemaining; rc = _client->write(writeBuf,bytesToWrite); result = (rc == bytesToWrite); @@ -467,9 +543,9 @@ boolean PubSubClient::write(uint8_t header, uint8_t* buf, uint16_t length) { } return result; #else - rc = _client->write(buf+(4-llen),length+1+llen); + rc = _client->write(buf+(MQTT_MAX_HEADER_SIZE-hlen),length+hlen); lastOutActivity = millis(); - return (rc == 1+llen+length); + return (rc == hlen+length); #endif } @@ -487,7 +563,7 @@ boolean PubSubClient::subscribe(const char* topic, uint8_t qos) { } if (connected()) { // Leave room in the buffer for header and variable length field - uint16_t length = 5; + uint16_t length = MQTT_MAX_HEADER_SIZE; nextMsgId++; if (nextMsgId == 0) { nextMsgId = 1; @@ -496,7 +572,7 @@ boolean PubSubClient::subscribe(const char* topic, uint8_t qos) { buffer[length++] = (nextMsgId & 0xFF); length = writeString((char*)topic, buffer,length); buffer[length++] = qos; - return write(MQTTSUBSCRIBE|MQTTQOS1,buffer,length-5); + return write(MQTTSUBSCRIBE|MQTTQOS1,buffer,length-MQTT_MAX_HEADER_SIZE); } return false; } @@ -507,7 +583,7 @@ boolean PubSubClient::unsubscribe(const char* topic) { return false; } if (connected()) { - uint16_t length = 5; + uint16_t length = MQTT_MAX_HEADER_SIZE; nextMsgId++; if (nextMsgId == 0) { nextMsgId = 1; @@ -515,7 +591,7 @@ boolean PubSubClient::unsubscribe(const char* topic) { buffer[length++] = (nextMsgId >> 8); buffer[length++] = (nextMsgId & 0xFF); length = writeString(topic, buffer,length); - return write(MQTTUNSUBSCRIBE|MQTTQOS1,buffer,length-5); + return write(MQTTUNSUBSCRIBE|MQTTQOS1,buffer,length-MQTT_MAX_HEADER_SIZE); } return false; } @@ -523,7 +599,7 @@ boolean PubSubClient::unsubscribe(const char* topic) { void PubSubClient::disconnect() { buffer[0] = MQTTDISCONNECT; buffer[1] = 0; - if (_client != NULL) { + if (_client != nullptr) { _client->write(buffer,2); _client->flush(); _client->stop(); @@ -558,6 +634,8 @@ boolean PubSubClient::connected() { _client->flush(); _client->stop(); } + } else { + return this->_state == MQTT_CONNECTED; } } return rc; diff --git a/lib/PubSubClient-EspEasy-2.6.09/src/PubSubClient.h b/lib/PubSubClient-EspEasy-2.7.12/src/PubSubClient.h similarity index 75% rename from lib/PubSubClient-EspEasy-2.6.09/src/PubSubClient.h rename to lib/PubSubClient-EspEasy-2.7.12/src/PubSubClient.h index 003df770e..aa2080ed1 100644 --- a/lib/PubSubClient-EspEasy-2.6.09/src/PubSubClient.h +++ b/lib/PubSubClient-EspEasy-2.7.12/src/PubSubClient.h @@ -30,7 +30,8 @@ // MQTT_KEEPALIVE : keepAlive interval in Seconds // Keepalive timeout for default MQTT Broker is 10s #ifndef MQTT_KEEPALIVE -#define MQTT_KEEPALIVE 10 +//#define MQTT_KEEPALIVE 10 +#define MQTT_KEEPALIVE 30 // Tasmota v6.5.0.14 enabling AWS-iot #endif // MQTT_SOCKET_TIMEOUT: socket timeout interval in Seconds @@ -75,6 +76,9 @@ #define MQTTQOS1 (1 << 1) #define MQTTQOS2 (2 << 1) +// Maximum size of fixed header and variable length size header +#define MQTT_MAX_HEADER_SIZE 5 + #if defined(ESP8266) || defined(ESP32) #include #define MQTT_CALLBACK_SIGNATURE std::function callback @@ -82,7 +86,9 @@ #define MQTT_CALLBACK_SIGNATURE void (*callback)(char*, uint8_t*, unsigned int) #endif -class PubSubClient { +#define CHECK_STRING_LENGTH(l,s) if (l+2+strlen(s) > MQTT_MAX_PACKET_SIZE) {_client->stop();return false;} + +class PubSubClient : public Print { private: Client* _client; uint8_t buffer[MQTT_MAX_PACKET_SIZE]; @@ -96,6 +102,11 @@ private: boolean readByte(uint8_t * result, uint16_t * index); boolean write(uint8_t header, uint8_t* buf, uint16_t length); uint16_t writeString(const char* string, uint8_t* buf, uint16_t pos); + // Build up the header ready to send + // Returns the size of the header + // Note: the header is built at the end of the first MQTT_MAX_HEADER_SIZE bytes, so will start + // (MQTT_MAX_HEADER_SIZE - ) bytes into the buffer + size_t buildHeader(uint8_t header, uint8_t* buf, uint16_t length); IPAddress ip; String domain; uint16_t port; @@ -129,12 +140,31 @@ public: boolean connect(const char* id, const char* user, const char* pass); boolean connect(const char* id, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage); boolean connect(const char* id, const char* user, const char* pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage); + boolean connect(const char* id, const char* user, const char* pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage, boolean cleanSession); void disconnect(); boolean publish(const char* topic, const char* payload); boolean publish(const char* topic, const char* payload, boolean retained); boolean publish(const char* topic, const uint8_t * payload, unsigned int plength); boolean publish(const char* topic, const uint8_t * payload, unsigned int plength, boolean retained); + boolean publish_P(const char* topic, const char* payload, boolean retained); boolean publish_P(const char* topic, const uint8_t * payload, unsigned int plength, boolean retained); + // Start to publish a message. + // This API: + // beginPublish(...) + // one or more calls to write(...) + // endPublish() + // Allows for arbitrarily large payloads to be sent without them having to be copied into + // a new buffer and held in memory at one time + // Returns 1 if the message was started successfully, 0 if there was an error + boolean beginPublish(const char* topic, unsigned int plength, boolean retained); + // Finish off this publish message (started with beginPublish) + // Returns 1 if the packet was sent successfully, 0 if there was an error + int endPublish(); + // Write a single byte of payload (only to be used with beginPublish/endPublish) + virtual size_t write(uint8_t); + // Write size bytes from buffer into the payload (only to be used with beginPublish/endPublish) + // Returns the number of bytes written + virtual size_t write(const uint8_t *buffer, size_t size); boolean subscribe(const char* topic); boolean subscribe(const char* topic, uint8_t qos); boolean unsubscribe(const char* topic); diff --git a/lib/PubSubClient-EspEasy-2.6.09/tests/.gitignore b/lib/PubSubClient-EspEasy-2.7.12/tests/.gitignore similarity index 100% rename from lib/PubSubClient-EspEasy-2.6.09/tests/.gitignore rename to lib/PubSubClient-EspEasy-2.7.12/tests/.gitignore diff --git a/lib/PubSubClient-EspEasy-2.6.09/tests/Makefile b/lib/PubSubClient-EspEasy-2.7.12/tests/Makefile similarity index 100% rename from lib/PubSubClient-EspEasy-2.6.09/tests/Makefile rename to lib/PubSubClient-EspEasy-2.7.12/tests/Makefile diff --git a/lib/PubSubClient-EspEasy-2.6.09/tests/README.md b/lib/PubSubClient-EspEasy-2.7.12/tests/README.md similarity index 100% rename from lib/PubSubClient-EspEasy-2.6.09/tests/README.md rename to lib/PubSubClient-EspEasy-2.7.12/tests/README.md diff --git a/lib/PubSubClient-EspEasy-2.6.09/tests/src/connect_spec.cpp b/lib/PubSubClient-EspEasy-2.7.12/tests/src/connect_spec.cpp similarity index 83% rename from lib/PubSubClient-EspEasy-2.6.09/tests/src/connect_spec.cpp rename to lib/PubSubClient-EspEasy-2.7.12/tests/src/connect_spec.cpp index 69f18646f..e27a1f59f 100644 --- a/lib/PubSubClient-EspEasy-2.6.09/tests/src/connect_spec.cpp +++ b/lib/PubSubClient-EspEasy-2.7.12/tests/src/connect_spec.cpp @@ -98,6 +98,33 @@ int test_connect_fails_on_bad_rc() { END_IT } +int test_connect_non_clean_session() { + IT("sends a properly formatted non-clean session connect packet and succeeds"); + ShimClient shimClient; + + shimClient.setAllowConnect(true); + byte expectServer[] = { 172, 16, 0, 2 }; + shimClient.expectConnect(expectServer,1883); + byte connect[] = {0x10,0x18,0x0,0x4,0x4d,0x51,0x54,0x54,0x4,0x0,0x0,0xf,0x0,0xc,0x63,0x6c,0x69,0x65,0x6e,0x74,0x5f,0x74,0x65,0x73,0x74,0x31}; + byte connack[] = { 0x20, 0x02, 0x00, 0x00 }; + + shimClient.expect(connect,26); + shimClient.respond(connack,4); + + PubSubClient client(server, 1883, callback, shimClient); + int state = client.state(); + IS_TRUE(state == MQTT_DISCONNECTED); + + int rc = client.connect((char*)"client_test1",0,0,0,0,0,0,0); + IS_TRUE(rc); + IS_FALSE(shimClient.error()); + + state = client.state(); + IS_TRUE(state == MQTT_CONNECTED); + + END_IT +} + int test_connect_accepts_username_password() { IT("accepts a username and password"); ShimClient shimClient; @@ -133,6 +160,23 @@ int test_connect_accepts_username_no_password() { END_IT } +int test_connect_accepts_username_blank_password() { + IT("accepts a username and blank password"); + ShimClient shimClient; + shimClient.setAllowConnect(true); + + byte connect[] = { 0x10,0x20,0x0,0x4,0x4d,0x51,0x54,0x54,0x4,0xc2,0x0,0xf,0x0,0xc,0x63,0x6c,0x69,0x65,0x6e,0x74,0x5f,0x74,0x65,0x73,0x74,0x31,0x0,0x4,0x75,0x73,0x65,0x72,0x0,0x0}; + byte connack[] = { 0x20, 0x02, 0x00, 0x00 }; + shimClient.expect(connect,0x26); + shimClient.respond(connack,4); + + PubSubClient client(server, 1883, callback, shimClient); + int rc = client.connect((char*)"client_test1",(char*)"user",(char*)"pass"); + IS_TRUE(rc); + IS_FALSE(shimClient.error()); + + END_IT +} int test_connect_ignores_password_no_username() { IT("ignores a password but no username"); @@ -239,10 +283,12 @@ int test_connect_disconnect_connect() { int main() { SUITE("Connect"); + test_connect_fails_no_network(); test_connect_fails_on_no_response(); test_connect_properly_formatted(); + test_connect_non_clean_session(); test_connect_accepts_username_password(); test_connect_fails_on_bad_rc(); test_connect_properly_formatted_hostname(); diff --git a/lib/PubSubClient-EspEasy-2.6.09/tests/src/keepalive_spec.cpp b/lib/PubSubClient-EspEasy-2.7.12/tests/src/keepalive_spec.cpp similarity index 100% rename from lib/PubSubClient-EspEasy-2.6.09/tests/src/keepalive_spec.cpp rename to lib/PubSubClient-EspEasy-2.7.12/tests/src/keepalive_spec.cpp diff --git a/lib/PubSubClient-EspEasy-2.6.09/tests/src/lib/Arduino.h b/lib/PubSubClient-EspEasy-2.7.12/tests/src/lib/Arduino.h similarity index 90% rename from lib/PubSubClient-EspEasy-2.6.09/tests/src/lib/Arduino.h rename to lib/PubSubClient-EspEasy-2.7.12/tests/src/lib/Arduino.h index c6752801a..2a00f24bc 100644 --- a/lib/PubSubClient-EspEasy-2.6.09/tests/src/lib/Arduino.h +++ b/lib/PubSubClient-EspEasy-2.7.12/tests/src/lib/Arduino.h @@ -5,6 +5,7 @@ #include #include #include +#include "Print.h" extern "C"{ @@ -20,4 +21,6 @@ extern "C"{ #define PROGMEM #define pgm_read_byte_near(x) *(x) +#define yield(x) {} + #endif // Arduino_h diff --git a/lib/PubSubClient-EspEasy-2.6.09/tests/src/lib/BDDTest.cpp b/lib/PubSubClient-EspEasy-2.7.12/tests/src/lib/BDDTest.cpp similarity index 100% rename from lib/PubSubClient-EspEasy-2.6.09/tests/src/lib/BDDTest.cpp rename to lib/PubSubClient-EspEasy-2.7.12/tests/src/lib/BDDTest.cpp diff --git a/lib/PubSubClient-EspEasy-2.6.09/tests/src/lib/BDDTest.h b/lib/PubSubClient-EspEasy-2.7.12/tests/src/lib/BDDTest.h similarity index 100% rename from lib/PubSubClient-EspEasy-2.6.09/tests/src/lib/BDDTest.h rename to lib/PubSubClient-EspEasy-2.7.12/tests/src/lib/BDDTest.h diff --git a/lib/PubSubClient-EspEasy-2.6.09/tests/src/lib/Buffer.cpp b/lib/PubSubClient-EspEasy-2.7.12/tests/src/lib/Buffer.cpp similarity index 86% rename from lib/PubSubClient-EspEasy-2.6.09/tests/src/lib/Buffer.cpp rename to lib/PubSubClient-EspEasy-2.7.12/tests/src/lib/Buffer.cpp index 59a2fbbbd..f07759a3a 100644 --- a/lib/PubSubClient-EspEasy-2.6.09/tests/src/lib/Buffer.cpp +++ b/lib/PubSubClient-EspEasy-2.7.12/tests/src/lib/Buffer.cpp @@ -2,9 +2,13 @@ #include "Arduino.h" Buffer::Buffer() { + this->pos = 0; + this->length = 0; } Buffer::Buffer(uint8_t* buf, size_t size) { + this->pos = 0; + this->length = 0; this->add(buf,size); } bool Buffer::available() { diff --git a/lib/PubSubClient-EspEasy-2.6.09/tests/src/lib/Buffer.h b/lib/PubSubClient-EspEasy-2.7.12/tests/src/lib/Buffer.h similarity index 100% rename from lib/PubSubClient-EspEasy-2.6.09/tests/src/lib/Buffer.h rename to lib/PubSubClient-EspEasy-2.7.12/tests/src/lib/Buffer.h diff --git a/lib/PubSubClient-EspEasy-2.6.09/tests/src/lib/Client.h b/lib/PubSubClient-EspEasy-2.7.12/tests/src/lib/Client.h similarity index 100% rename from lib/PubSubClient-EspEasy-2.6.09/tests/src/lib/Client.h rename to lib/PubSubClient-EspEasy-2.7.12/tests/src/lib/Client.h diff --git a/lib/PubSubClient-EspEasy-2.6.09/tests/src/lib/IPAddress.cpp b/lib/PubSubClient-EspEasy-2.7.12/tests/src/lib/IPAddress.cpp similarity index 100% rename from lib/PubSubClient-EspEasy-2.6.09/tests/src/lib/IPAddress.cpp rename to lib/PubSubClient-EspEasy-2.7.12/tests/src/lib/IPAddress.cpp diff --git a/lib/PubSubClient-EspEasy-2.6.09/tests/src/lib/IPAddress.h b/lib/PubSubClient-EspEasy-2.7.12/tests/src/lib/IPAddress.h similarity index 100% rename from lib/PubSubClient-EspEasy-2.6.09/tests/src/lib/IPAddress.h rename to lib/PubSubClient-EspEasy-2.7.12/tests/src/lib/IPAddress.h diff --git a/lib/PubSubClient-EspEasy-2.7.12/tests/src/lib/Print.h b/lib/PubSubClient-EspEasy-2.7.12/tests/src/lib/Print.h new file mode 100644 index 000000000..02ef77c2c --- /dev/null +++ b/lib/PubSubClient-EspEasy-2.7.12/tests/src/lib/Print.h @@ -0,0 +1,28 @@ +/* + Print.h - Base class that provides print() and println() + Copyright (c) 2008 David A. Mellis. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef Print_h +#define Print_h + +class Print { + public: + virtual size_t write(uint8_t) = 0; +}; + +#endif diff --git a/lib/PubSubClient-EspEasy-2.6.09/tests/src/lib/ShimClient.cpp b/lib/PubSubClient-EspEasy-2.7.12/tests/src/lib/ShimClient.cpp similarity index 100% rename from lib/PubSubClient-EspEasy-2.6.09/tests/src/lib/ShimClient.cpp rename to lib/PubSubClient-EspEasy-2.7.12/tests/src/lib/ShimClient.cpp diff --git a/lib/PubSubClient-EspEasy-2.6.09/tests/src/lib/ShimClient.h b/lib/PubSubClient-EspEasy-2.7.12/tests/src/lib/ShimClient.h similarity index 100% rename from lib/PubSubClient-EspEasy-2.6.09/tests/src/lib/ShimClient.h rename to lib/PubSubClient-EspEasy-2.7.12/tests/src/lib/ShimClient.h diff --git a/lib/PubSubClient-EspEasy-2.6.09/tests/src/lib/Stream.cpp b/lib/PubSubClient-EspEasy-2.7.12/tests/src/lib/Stream.cpp similarity index 100% rename from lib/PubSubClient-EspEasy-2.6.09/tests/src/lib/Stream.cpp rename to lib/PubSubClient-EspEasy-2.7.12/tests/src/lib/Stream.cpp diff --git a/lib/PubSubClient-EspEasy-2.6.09/tests/src/lib/Stream.h b/lib/PubSubClient-EspEasy-2.7.12/tests/src/lib/Stream.h similarity index 100% rename from lib/PubSubClient-EspEasy-2.6.09/tests/src/lib/Stream.h rename to lib/PubSubClient-EspEasy-2.7.12/tests/src/lib/Stream.h diff --git a/lib/PubSubClient-EspEasy-2.6.09/tests/src/lib/trace.h b/lib/PubSubClient-EspEasy-2.7.12/tests/src/lib/trace.h similarity index 100% rename from lib/PubSubClient-EspEasy-2.6.09/tests/src/lib/trace.h rename to lib/PubSubClient-EspEasy-2.7.12/tests/src/lib/trace.h diff --git a/lib/PubSubClient-EspEasy-2.6.09/tests/src/publish_spec.cpp b/lib/PubSubClient-EspEasy-2.7.12/tests/src/publish_spec.cpp similarity index 100% rename from lib/PubSubClient-EspEasy-2.6.09/tests/src/publish_spec.cpp rename to lib/PubSubClient-EspEasy-2.7.12/tests/src/publish_spec.cpp diff --git a/lib/PubSubClient-EspEasy-2.6.09/tests/src/receive_spec.cpp b/lib/PubSubClient-EspEasy-2.7.12/tests/src/receive_spec.cpp similarity index 89% rename from lib/PubSubClient-EspEasy-2.6.09/tests/src/receive_spec.cpp rename to lib/PubSubClient-EspEasy-2.7.12/tests/src/receive_spec.cpp index 54a62ee5c..9a18af042 100644 --- a/lib/PubSubClient-EspEasy-2.6.09/tests/src/receive_spec.cpp +++ b/lib/PubSubClient-EspEasy-2.7.12/tests/src/receive_spec.cpp @@ -160,6 +160,35 @@ int test_receive_oversized_message() { END_IT } +int test_drop_invalid_remaining_length_message() { + IT("drops invalid remaining length message"); + reset_callback(); + + ShimClient shimClient; + shimClient.setAllowConnect(true); + + byte connack[] = { 0x20, 0x02, 0x00, 0x00 }; + shimClient.respond(connack,4); + + PubSubClient client(server, 1883, callback, shimClient); + int rc = client.connect((char*)"client_test1"); + IS_TRUE(rc); + + byte publish[] = {0x30,0x92,0x92,0x92,0x92,0x01,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64}; + shimClient.respond(publish,20); + + rc = client.loop(); + + IS_FALSE(rc); + + IS_FALSE(callback_called); + + IS_FALSE(shimClient.error()); + + END_IT +} + + int test_receive_oversized_stream_message() { IT("drops an oversized message"); reset_callback(); @@ -241,6 +270,7 @@ int main() test_receive_callback(); test_receive_stream(); test_receive_max_sized_message(); + test_drop_invalid_remaining_length_message(); test_receive_oversized_message(); test_receive_oversized_stream_message(); test_receive_qos1(); diff --git a/lib/PubSubClient-EspEasy-2.6.09/tests/src/subscribe_spec.cpp b/lib/PubSubClient-EspEasy-2.7.12/tests/src/subscribe_spec.cpp similarity index 100% rename from lib/PubSubClient-EspEasy-2.6.09/tests/src/subscribe_spec.cpp rename to lib/PubSubClient-EspEasy-2.7.12/tests/src/subscribe_spec.cpp diff --git a/lib/PubSubClient-EspEasy-2.6.09/tests/testcases/__init__.py b/lib/PubSubClient-EspEasy-2.7.12/tests/testcases/__init__.py similarity index 100% rename from lib/PubSubClient-EspEasy-2.6.09/tests/testcases/__init__.py rename to lib/PubSubClient-EspEasy-2.7.12/tests/testcases/__init__.py diff --git a/lib/PubSubClient-EspEasy-2.7.12/tests/testcases/mqtt_basic.py b/lib/PubSubClient-EspEasy-2.7.12/tests/testcases/mqtt_basic.py new file mode 100644 index 000000000..f23ef71c1 --- /dev/null +++ b/lib/PubSubClient-EspEasy-2.7.12/tests/testcases/mqtt_basic.py @@ -0,0 +1,39 @@ +import unittest +import settings +import time +import mosquitto + + +def on_message(mosq, obj, msg): + obj.message_queue.append(msg) + + +class mqtt_basic(unittest.TestCase): + + message_queue = [] + + @classmethod + def setUpClass(self): + self.client = mosquitto.Mosquitto("pubsubclient_ut", clean_session=True, obj=self) + self.client.connect(settings.server_ip) + self.client.on_message = on_message + self.client.subscribe("outTopic", 0) + + @classmethod + def tearDownClass(self): + self.client.disconnect() + + def test_one(self): + i = 30 + while len(self.message_queue) == 0 and i > 0: + self.client.loop() + time.sleep(0.5) + i -= 1 + self.assertTrue(i > 0, "message receive timed-out") + self.assertEqual(len(self.message_queue), 1, "unexpected number of messages received") + msg = self.message_queue[0] + self.assertEqual(msg.mid, 0, "message id not 0") + self.assertEqual(msg.topic, "outTopic", "message topic incorrect") + self.assertEqual(msg.payload, "hello world") + self.assertEqual(msg.qos, 0, "message qos not 0") + self.assertEqual(msg.retain, False, "message retain flag incorrect") diff --git a/lib/PubSubClient-EspEasy-2.7.12/tests/testcases/mqtt_publish_in_callback.py b/lib/PubSubClient-EspEasy-2.7.12/tests/testcases/mqtt_publish_in_callback.py new file mode 100644 index 000000000..45b0a8515 --- /dev/null +++ b/lib/PubSubClient-EspEasy-2.7.12/tests/testcases/mqtt_publish_in_callback.py @@ -0,0 +1,59 @@ +import unittest +import settings +import time +import mosquitto + + +def on_message(mosq, obj, msg): + obj.message_queue.append(msg) + + +class mqtt_publish_in_callback(unittest.TestCase): + + message_queue = [] + + @classmethod + def setUpClass(self): + self.client = mosquitto.Mosquitto("pubsubclient_ut", clean_session=True, obj=self) + self.client.connect(settings.server_ip) + self.client.on_message = on_message + self.client.subscribe("outTopic", 0) + + @classmethod + def tearDownClass(self): + self.client.disconnect() + + def test_connect(self): + i = 30 + while len(self.message_queue) == 0 and i > 0: + self.client.loop() + time.sleep(0.5) + i -= 1 + self.assertTrue(i > 0, "message receive timed-out") + self.assertEqual(len(self.message_queue), 1, "unexpected number of messages received") + msg = self.message_queue.pop(0) + self.assertEqual(msg.mid, 0, "message id not 0") + self.assertEqual(msg.topic, "outTopic", "message topic incorrect") + self.assertEqual(msg.payload, "hello world") + self.assertEqual(msg.qos, 0, "message qos not 0") + self.assertEqual(msg.retain, False, "message retain flag incorrect") + + def test_publish(self): + self.assertEqual(len(self.message_queue), 0, "message queue not empty") + payload = "abcdefghij" + self.client.publish("inTopic", payload) + + i = 30 + while len(self.message_queue) == 0 and i > 0: + self.client.loop() + time.sleep(0.5) + i -= 1 + + self.assertTrue(i > 0, "message receive timed-out") + self.assertEqual(len(self.message_queue), 1, "unexpected number of messages received") + msg = self.message_queue.pop(0) + self.assertEqual(msg.mid, 0, "message id not 0") + self.assertEqual(msg.topic, "outTopic", "message topic incorrect") + self.assertEqual(msg.payload, payload) + self.assertEqual(msg.qos, 0, "message qos not 0") + self.assertEqual(msg.retain, False, "message retain flag incorrect") diff --git a/lib/PubSubClient-EspEasy-2.6.09/tests/testcases/settings.py b/lib/PubSubClient-EspEasy-2.7.12/tests/testcases/settings.py similarity index 100% rename from lib/PubSubClient-EspEasy-2.6.09/tests/testcases/settings.py rename to lib/PubSubClient-EspEasy-2.7.12/tests/testcases/settings.py diff --git a/lib/PubSubClient-EspEasy-2.7.12/tests/testsuite.py b/lib/PubSubClient-EspEasy-2.7.12/tests/testsuite.py new file mode 100644 index 000000000..788fc5d97 --- /dev/null +++ b/lib/PubSubClient-EspEasy-2.7.12/tests/testsuite.py @@ -0,0 +1,181 @@ +#!/usr/bin/env python +import os +import os.path +import sys +import shutil +from subprocess import call +import importlib +import unittest +import re + +from testcases import settings + + +class Workspace(object): + + def __init__(self): + self.root_dir = os.getcwd() + self.build_dir = os.path.join(self.root_dir, "tmpbin") + self.log_dir = os.path.join(self.root_dir, "logs") + self.tests_dir = os.path.join(self.root_dir, "testcases") + self.examples_dir = os.path.join(self.root_dir, "../PubSubClient/examples") + self.examples = [] + self.tests = [] + if not os.path.isdir("../PubSubClient"): + raise Exception("Cannot find PubSubClient library") + try: + return __import__('ino') + except ImportError: + raise Exception("ino tool not installed") + + def init(self): + if os.path.isdir(self.build_dir): + shutil.rmtree(self.build_dir) + os.mkdir(self.build_dir) + if os.path.isdir(self.log_dir): + shutil.rmtree(self.log_dir) + os.mkdir(self.log_dir) + + os.chdir(self.build_dir) + call(["ino", "init"]) + + shutil.copytree("../../PubSubClient", "lib/PubSubClient") + + filenames = [] + for root, dirs, files in os.walk(self.examples_dir): + filenames += [os.path.join(root, f) for f in files if f.endswith(".ino")] + filenames.sort() + for e in filenames: + self.examples.append(Sketch(self, e)) + + filenames = [] + for root, dirs, files in os.walk(self.tests_dir): + filenames += [os.path.join(root, f) for f in files if f.endswith(".ino")] + filenames.sort() + for e in filenames: + self.tests.append(Sketch(self, e)) + + def clean(self): + shutil.rmtree(self.build_dir) + + +class Sketch(object): + def __init__(self, wksp, fn): + self.w = wksp + self.filename = fn + self.basename = os.path.basename(self.filename) + self.build_log = os.path.join(self.w.log_dir, "%s.log" % (os.path.basename(self.filename),)) + self.build_err_log = os.path.join(self.w.log_dir, "%s.err.log" % (os.path.basename(self.filename),)) + self.build_upload_log = os.path.join(self.w.log_dir, "%s.upload.log" % (os.path.basename(self.filename),)) + + def build(self): + sys.stdout.write(" Build: ") + sys.stdout.flush() + + # Copy sketch over, replacing IP addresses as necessary + fin = open(self.filename, "r") + lines = fin.readlines() + fin.close() + fout = open(os.path.join(self.w.build_dir, "src", "sketch.ino"), "w") + for l in lines: + if re.match(r"^byte server\[\] = {", l): + fout.write("byte server[] = { %s };\n" % (settings.server_ip.replace(".", ", "),)) + elif re.match(r"^byte ip\[\] = {", l): + fout.write("byte ip[] = { %s };\n" % (settings.arduino_ip.replace(".", ", "),)) + else: + fout.write(l) + fout.flush() + fout.close() + + # Run build + fout = open(self.build_log, "w") + ferr = open(self.build_err_log, "w") + rc = call(["ino", "build"], stdout=fout, stderr=ferr) + fout.close() + ferr.close() + if rc == 0: + sys.stdout.write("pass") + sys.stdout.write("\n") + return True + else: + sys.stdout.write("fail") + sys.stdout.write("\n") + with open(self.build_err_log) as f: + for line in f: + print(" " + line) + return False + + def upload(self): + sys.stdout.write(" Upload: ") + sys.stdout.flush() + fout = open(self.build_upload_log, "w") + rc = call(["ino", "upload"], stdout=fout, stderr=fout) + fout.close() + if rc == 0: + sys.stdout.write("pass") + sys.stdout.write("\n") + return True + else: + sys.stdout.write("fail") + sys.stdout.write("\n") + with open(self.build_upload_log) as f: + for line in f: + print(" " + line) + return False + + def test(self): + # import the matching test case, if it exists + try: + basename = os.path.basename(self.filename)[:-4] + i = importlib.import_module("testcases." + basename) + except: + sys.stdout.write(" Test: no tests found") + sys.stdout.write("\n") + return + c = getattr(i, basename) + + testmethods = [m for m in dir(c) if m.startswith("test_")] + testmethods.sort() + tests = [] + for m in testmethods: + tests.append(c(m)) + + result = unittest.TestResult() + c.setUpClass() + if self.upload(): + sys.stdout.write(" Test: ") + sys.stdout.flush() + for t in tests: + t.run(result) + print(str(result.testsRun - len(result.failures) - len(result.errors)) + "/" + str(result.testsRun)) + if not result.wasSuccessful(): + if len(result.failures) > 0: + for f in result.failures: + print("-- " + str(f[0])) + print(f[1]) + if len(result.errors) > 0: + print(" Errors:") + for f in result.errors: + print("-- " + str(f[0])) + print(f[1]) + c.tearDownClass() + + +if __name__ == '__main__': + run_tests = True + + w = Workspace() + w.init() + + for e in w.examples: + print("--------------------------------------") + print("[" + e.basename + "]") + if e.build() and run_tests: + e.test() + for e in w.tests: + print("--------------------------------------") + print("[" + e.basename + "]") + if e.build() and run_tests: + e.test() + + w.clean() diff --git a/lib/TasmotaSerial-2.3.0/README.md b/lib/TasmotaSerial-2.3.1/README.md similarity index 100% rename from lib/TasmotaSerial-2.3.0/README.md rename to lib/TasmotaSerial-2.3.1/README.md diff --git a/lib/TasmotaSerial-2.3.0/examples/swsertest/swsertest.ino b/lib/TasmotaSerial-2.3.1/examples/swsertest/swsertest.ino similarity index 100% rename from lib/TasmotaSerial-2.3.0/examples/swsertest/swsertest.ino rename to lib/TasmotaSerial-2.3.1/examples/swsertest/swsertest.ino diff --git a/lib/TasmotaSerial-2.3.0/keywords.txt b/lib/TasmotaSerial-2.3.1/keywords.txt similarity index 100% rename from lib/TasmotaSerial-2.3.0/keywords.txt rename to lib/TasmotaSerial-2.3.1/keywords.txt diff --git a/lib/TasmotaSerial-2.3.0/library.json b/lib/TasmotaSerial-2.3.1/library.json similarity index 100% rename from lib/TasmotaSerial-2.3.0/library.json rename to lib/TasmotaSerial-2.3.1/library.json diff --git a/lib/TasmotaSerial-2.3.0/library.properties b/lib/TasmotaSerial-2.3.1/library.properties similarity index 100% rename from lib/TasmotaSerial-2.3.0/library.properties rename to lib/TasmotaSerial-2.3.1/library.properties diff --git a/lib/TasmotaSerial-2.3.0/src/TasmotaSerial.cpp b/lib/TasmotaSerial-2.3.1/src/TasmotaSerial.cpp similarity index 97% rename from lib/TasmotaSerial-2.3.0/src/TasmotaSerial.cpp rename to lib/TasmotaSerial-2.3.1/src/TasmotaSerial.cpp index eecdeb124..d99137edc 100644 --- a/lib/TasmotaSerial-2.3.0/src/TasmotaSerial.cpp +++ b/lib/TasmotaSerial-2.3.1/src/TasmotaSerial.cpp @@ -100,7 +100,7 @@ TasmotaSerial::TasmotaSerial(int receive_pin, int transmit_pin, int hardware_fal m_buffer = (uint8_t*)malloc(TM_SERIAL_BUFFER_SIZE); if (m_buffer == NULL) return; // Use getCycleCount() loop to get as exact timing as possible - m_bit_time = F_CPU / TM_SERIAL_BAUDRATE; + m_bit_time = ESP.getCpuFreqMHz() * 1000000 / TM_SERIAL_BAUDRATE; pinMode(m_rx_pin, INPUT); tms_obj_list[m_rx_pin] = this; attachInterrupt(m_rx_pin, ISRList[m_rx_pin], FALLING); @@ -145,8 +145,8 @@ bool TasmotaSerial::begin(long speed, int stop_bits) { } } else { // Use getCycleCount() loop to get as exact timing as possible - m_bit_time = F_CPU / speed; - m_high_speed = (speed > 9600); + m_bit_time = ESP.getCpuFreqMHz() * 1000000 / speed; + m_high_speed = (speed >= 9600); } return m_valid; } diff --git a/lib/TasmotaSerial-2.3.0/src/TasmotaSerial.h b/lib/TasmotaSerial-2.3.1/src/TasmotaSerial.h similarity index 100% rename from lib/TasmotaSerial-2.3.0/src/TasmotaSerial.h rename to lib/TasmotaSerial-2.3.1/src/TasmotaSerial.h diff --git a/lib/bearssl-esp8266/bearssl_esp8266-customized.txt b/lib/bearssl-esp8266/bearssl_esp8266-customized.txt new file mode 100644 index 000000000..265204558 --- /dev/null +++ b/lib/bearssl-esp8266/bearssl_esp8266-customized.txt @@ -0,0 +1,13 @@ +This library is adapted from bearssl-esp8266 to avoid conflict with the +BearSSL headers in Arduino Core. + +To recreate, copy all original 'src/' and 'inc/' into 'src/' lib. + +Then rename the following: + - "bearssl with "t_bearssl + - "inner with "t_inner + - "config with "t_config + +Add the customized files in src/: + - t_bearssl_tasmota_config.h + - pgmspace_bearssl.h diff --git a/lib/bearssl-esp8266/conf/esp8266.mk b/lib/bearssl-esp8266/conf/esp8266.mk new file mode 100644 index 000000000..5c409b8be --- /dev/null +++ b/lib/bearssl-esp8266/conf/esp8266.mk @@ -0,0 +1,21 @@ +# Configuration for compiling to an ESP8266 from a UNIX system + +# We are on a Unix system so we assume a Single Unix compatible 'make' +# utility, and Unix defaults. +include conf/Unix.mk + +# We override the build directory. +BUILD = esp8266 + +# C compiler, linker, and static library builder. +TOOLCHAIN_PREFIX := xtensa-lx106-elf- +CC := $(TOOLCHAIN_PREFIX)gcc +CFLAGS = -W -Wall -g -O2 -Wpointer-arith -Wl,-EL -nostdlib -mlongcalls -mno-text-section-literals -ffunction-sections -fdata-sections -Werror +CFLAGS += -D__ets__ -DICACHE_FLASH -DESP8266 -DBR_SLOW_MUL15=1 +LD := $(TOOLCHAIN_PREFIX)ld +AR := $(TOOLCHAIN_PREFIX)ar + +# We compile only the static library. +DLL = no +TOOLS = no +TESTS = no diff --git a/lib/bearssl-esp8266/library.properties b/lib/bearssl-esp8266/library.properties new file mode 100644 index 000000000..1d936cb88 --- /dev/null +++ b/lib/bearssl-esp8266/library.properties @@ -0,0 +1,9 @@ +name=BearSSL +version=0.6 +author=Thomas Pornin +maintainer=Earle F. Philhower, III +sentence=BearSSL implementation of the SSL/TLS protocol optimized for ESP8266 by Earle F. Philhower, optimized for Tamota by Stephan Hadinger +paragraph= +category=Other +url=https://github.com/earlephilhower/bearssl-esp8266.git +architectures=esp8266 diff --git a/lib/bearssl-esp8266/src/aead/ccm.c b/lib/bearssl-esp8266/src/aead/ccm.c new file mode 100644 index 000000000..427d1becc --- /dev/null +++ b/lib/bearssl-esp8266/src/aead/ccm.c @@ -0,0 +1,346 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * Implementation Notes + * ==================== + * + * The combined CTR + CBC-MAC functions can only handle full blocks, + * so some buffering is necessary. + * + * - 'ptr' contains a value from 0 to 15, which is the number of bytes + * accumulated in buf[] that still needs to be processed with the + * current CBC-MAC computation. + * + * - When processing the message itself, CTR encryption/decryption is + * also done at the same time. The first 'ptr' bytes of buf[] then + * contains the plaintext bytes, while the last '16 - ptr' bytes of + * buf[] are the remnants of the stream block, to be used against + * the next input bytes, when available. When 'ptr' is 0, the + * contents of buf[] are to be ignored. + * + * - The current counter and running CBC-MAC values are kept in 'ctr' + * and 'cbcmac', respectively. + */ + +/* see bearssl_block.h */ +void +br_ccm_init(br_ccm_context *ctx, const br_block_ctrcbc_class **bctx) +{ + ctx->bctx = bctx; +} + +/* see bearssl_block.h */ +int +br_ccm_reset(br_ccm_context *ctx, const void *nonce, size_t nonce_len, + uint64_t aad_len, uint64_t data_len, size_t tag_len) +{ + unsigned char tmp[16]; + unsigned u, q; + + if (nonce_len < 7 || nonce_len > 13) { + return 0; + } + if (tag_len < 4 || tag_len > 16 || (tag_len & 1) != 0) { + return 0; + } + q = 15 - (unsigned)nonce_len; + ctx->tag_len = tag_len; + + /* + * Block B0, to start CBC-MAC. + */ + tmp[0] = (aad_len > 0 ? 0x40 : 0x00) + | (((unsigned)tag_len - 2) << 2) + | (q - 1); + memcpy(tmp + 1, nonce, nonce_len); + for (u = 0; u < q; u ++) { + tmp[15 - u] = (unsigned char)data_len; + data_len >>= 8; + } + if (data_len != 0) { + /* + * If the data length was not entirely consumed in the + * loop above, then it exceeds the maximum limit of + * q bytes (when encoded). + */ + return 0; + } + + /* + * Start CBC-MAC. + */ + memset(ctx->cbcmac, 0, sizeof ctx->cbcmac); + (*ctx->bctx)->mac(ctx->bctx, ctx->cbcmac, tmp, sizeof tmp); + + /* + * Assemble AAD length header. + */ + if ((aad_len >> 32) != 0) { + ctx->buf[0] = 0xFF; + ctx->buf[1] = 0xFF; + br_enc64be(ctx->buf + 2, aad_len); + ctx->ptr = 10; + } else if (aad_len >= 0xFF00) { + ctx->buf[0] = 0xFF; + ctx->buf[1] = 0xFE; + br_enc32be(ctx->buf + 2, (uint32_t)aad_len); + ctx->ptr = 6; + } else if (aad_len > 0) { + br_enc16be(ctx->buf, (unsigned)aad_len); + ctx->ptr = 2; + } else { + ctx->ptr = 0; + } + + /* + * Make initial counter value and compute tag mask. + */ + ctx->ctr[0] = q - 1; + memcpy(ctx->ctr + 1, nonce, nonce_len); + memset(ctx->ctr + 1 + nonce_len, 0, q); + memset(ctx->tagmask, 0, sizeof ctx->tagmask); + (*ctx->bctx)->ctr(ctx->bctx, ctx->ctr, + ctx->tagmask, sizeof ctx->tagmask); + + return 1; +} + +/* see bearssl_block.h */ +void +br_ccm_aad_inject(br_ccm_context *ctx, const void *data, size_t len) +{ + const unsigned char *dbuf; + size_t ptr; + + dbuf = data; + + /* + * Complete partial block, if needed. + */ + ptr = ctx->ptr; + if (ptr != 0) { + size_t clen; + + clen = (sizeof ctx->buf) - ptr; + if (clen > len) { + memcpy(ctx->buf + ptr, dbuf, len); + ctx->ptr = ptr + len; + return; + } + memcpy(ctx->buf + ptr, dbuf, clen); + dbuf += clen; + len -= clen; + (*ctx->bctx)->mac(ctx->bctx, ctx->cbcmac, + ctx->buf, sizeof ctx->buf); + } + + /* + * Process complete blocks. + */ + ptr = len & 15; + len -= ptr; + (*ctx->bctx)->mac(ctx->bctx, ctx->cbcmac, dbuf, len); + dbuf += len; + + /* + * Copy last partial block in the context buffer. + */ + memcpy(ctx->buf, dbuf, ptr); + ctx->ptr = ptr; +} + +/* see bearssl_block.h */ +void +br_ccm_flip(br_ccm_context *ctx) +{ + size_t ptr; + + /* + * Complete AAD partial block with zeros, if necessary. + */ + ptr = ctx->ptr; + if (ptr != 0) { + memset(ctx->buf + ptr, 0, (sizeof ctx->buf) - ptr); + (*ctx->bctx)->mac(ctx->bctx, ctx->cbcmac, + ctx->buf, sizeof ctx->buf); + ctx->ptr = 0; + } + + /* + * Counter was already set by br_ccm_reset(). + */ +} + +/* see bearssl_block.h */ +void +br_ccm_run(br_ccm_context *ctx, int encrypt, void *data, size_t len) +{ + unsigned char *dbuf; + size_t ptr; + + dbuf = data; + + /* + * Complete a partial block, if any: ctx->buf[] contains + * ctx->ptr plaintext bytes (already reported), and the other + * bytes are CTR stream output. + */ + ptr = ctx->ptr; + if (ptr != 0) { + size_t clen; + size_t u; + + clen = (sizeof ctx->buf) - ptr; + if (clen > len) { + clen = len; + } + if (encrypt) { + for (u = 0; u < clen; u ++) { + unsigned w, x; + + w = ctx->buf[ptr + u]; + x = dbuf[u]; + ctx->buf[ptr + u] = x; + dbuf[u] = w ^ x; + } + } else { + for (u = 0; u < clen; u ++) { + unsigned w; + + w = ctx->buf[ptr + u] ^ dbuf[u]; + dbuf[u] = w; + ctx->buf[ptr + u] = w; + } + } + dbuf += clen; + len -= clen; + ptr += clen; + if (ptr < sizeof ctx->buf) { + ctx->ptr = ptr; + return; + } + (*ctx->bctx)->mac(ctx->bctx, + ctx->cbcmac, ctx->buf, sizeof ctx->buf); + } + + /* + * Process all complete blocks. Note that the ctrcbc API is for + * encrypt-then-MAC (CBC-MAC is computed over the encrypted + * blocks) while CCM uses MAC-and-encrypt (CBC-MAC is computed + * over the plaintext blocks). Therefore, we need to use the + * _decryption_ function for encryption, and the encryption + * function for decryption (this works because CTR encryption + * and decryption are identical, so the choice really is about + * computing the CBC-MAC before or after XORing with the CTR + * stream). + */ + ptr = len & 15; + len -= ptr; + if (encrypt) { + (*ctx->bctx)->decrypt(ctx->bctx, ctx->ctr, ctx->cbcmac, + dbuf, len); + } else { + (*ctx->bctx)->encrypt(ctx->bctx, ctx->ctr, ctx->cbcmac, + dbuf, len); + } + dbuf += len; + + /* + * If there is some remaining data, then we need to compute an + * extra block of CTR stream. + */ + if (ptr != 0) { + size_t u; + + memset(ctx->buf, 0, sizeof ctx->buf); + (*ctx->bctx)->ctr(ctx->bctx, ctx->ctr, + ctx->buf, sizeof ctx->buf); + if (encrypt) { + for (u = 0; u < ptr; u ++) { + unsigned w, x; + + w = ctx->buf[u]; + x = dbuf[u]; + ctx->buf[u] = x; + dbuf[u] = w ^ x; + } + } else { + for (u = 0; u < ptr; u ++) { + unsigned w; + + w = ctx->buf[u] ^ dbuf[u]; + dbuf[u] = w; + ctx->buf[u] = w; + } + } + } + ctx->ptr = ptr; +} + +/* see bearssl_block.h */ +size_t +br_ccm_get_tag(br_ccm_context *ctx, void *tag) +{ + size_t ptr; + size_t u; + + /* + * If there is some buffered data, then we need to pad it with + * zeros and finish up CBC-MAC. + */ + ptr = ctx->ptr; + if (ptr != 0) { + memset(ctx->buf + ptr, 0, (sizeof ctx->buf) - ptr); + (*ctx->bctx)->mac(ctx->bctx, ctx->cbcmac, + ctx->buf, sizeof ctx->buf); + } + + /* + * XOR the tag mask into the CBC-MAC output. + */ + for (u = 0; u < ctx->tag_len; u ++) { + ctx->cbcmac[u] ^= ctx->tagmask[u]; + } + memcpy(tag, ctx->cbcmac, ctx->tag_len); + return ctx->tag_len; +} + +/* see bearssl_block.h */ +uint32_t +br_ccm_check_tag(br_ccm_context *ctx, const void *tag) +{ + unsigned char tmp[16]; + size_t u, tag_len; + uint32_t z; + + tag_len = br_ccm_get_tag(ctx, tmp); + z = 0; + for (u = 0; u < tag_len; u ++) { + z |= tmp[u] ^ ((const unsigned char *)tag)[u]; + } + return EQ0(z); +} diff --git a/lib/bearssl-esp8266/src/aead/eax.c b/lib/bearssl-esp8266/src/aead/eax.c new file mode 100644 index 000000000..9e0b3230b --- /dev/null +++ b/lib/bearssl-esp8266/src/aead/eax.c @@ -0,0 +1,525 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * Implementation Notes + * ==================== + * + * The combined CTR + CBC-MAC functions can only handle full blocks, + * so some buffering is necessary. Moreover, EAX has a special padding + * rule for CBC-MAC, which implies that we cannot compute the MAC over + * the last received full block until we know whether we are at the + * end of the data or not. + * + * - 'ptr' contains a value from 1 to 16, which is the number of bytes + * accumulated in buf[] that still needs to be processed with the + * current OMAC computation. Beware that this can go to 16: a + * complete block cannot be processed until it is known whether it + * is the last block or not. However, it can never be 0, because + * OMAC^t works on an input that is at least one-block long. + * + * - When processing the message itself, CTR encryption/decryption is + * also done at the same time. The first 'ptr' bytes of buf[] then + * contains the encrypted bytes, while the last '16 - ptr' bytes of + * buf[] are the remnants of the stream block, to be used against + * the next input bytes, when available. + * + * - The current counter and running CBC-MAC values are kept in 'ctr' + * and 'cbcmac', respectively. + * + * - The derived keys for padding are kept in L2 and L4 (double and + * quadruple of Enc_K(0^n), in GF(2^128), respectively). + */ + +/* + * Start an OMAC computation; the first block is the big-endian + * representation of the provided value ('val' must fit on one byte). + * We make it a delayed block because it may also be the last one, + */ +static void +omac_start(br_eax_context *ctx, unsigned val) +{ + memset(ctx->cbcmac, 0, sizeof ctx->cbcmac); + memset(ctx->buf, 0, sizeof ctx->buf); + ctx->buf[15] = val; + ctx->ptr = 16; +} + +/* + * Double a value in finite field GF(2^128), defined with modulus + * X^128+X^7+X^2+X+1. + */ +static void +double_gf128(unsigned char *dst, const unsigned char *src) +{ + unsigned cc; + int i; + + cc = 0x87 & -((unsigned)src[0] >> 7); + for (i = 15; i >= 0; i --) { + unsigned z; + + z = (src[i] << 1) ^ cc; + cc = z >> 8; + dst[i] = (unsigned char)z; + } +} + +/* + * Apply padding to the last block, currently in ctx->buf (with + * ctx->ptr bytes), and finalize OMAC computation. + */ +static void +do_pad(br_eax_context *ctx) +{ + unsigned char *pad; + size_t ptr, u; + + ptr = ctx->ptr; + if (ptr == 16) { + pad = ctx->L2; + } else { + ctx->buf[ptr ++] = 0x80; + memset(ctx->buf + ptr, 0x00, 16 - ptr); + pad = ctx->L4; + } + for (u = 0; u < sizeof ctx->buf; u ++) { + ctx->buf[u] ^= pad[u]; + } + (*ctx->bctx)->mac(ctx->bctx, ctx->cbcmac, ctx->buf, sizeof ctx->buf); +} + +/* + * Apply CBC-MAC on the provided data, with buffering management. + * + * Upon entry, two situations are acceptable: + * + * ctx->ptr == 0: there is no data to process in ctx->buf + * ctx->ptr == 16: there is a full block of unprocessed data in ctx->buf + * + * Upon exit, ctx->ptr may be zero only if it was already zero on entry, + * and len == 0. In all other situations, ctx->ptr will be non-zero on + * exit (and may have value 16). + */ +static void +do_cbcmac_chunk(br_eax_context *ctx, const void *data, size_t len) +{ + size_t ptr; + + if (len == 0) { + return; + } + ptr = len & (size_t)15; + if (ptr == 0) { + len -= 16; + ptr = 16; + } else { + len -= ptr; + } + if (ctx->ptr == 16) { + (*ctx->bctx)->mac(ctx->bctx, ctx->cbcmac, + ctx->buf, sizeof ctx->buf); + } + (*ctx->bctx)->mac(ctx->bctx, ctx->cbcmac, data, len); + memcpy(ctx->buf, (const unsigned char *)data + len, ptr); + ctx->ptr = ptr; +} + +/* see bearssl_aead.h */ +void +br_eax_init(br_eax_context *ctx, const br_block_ctrcbc_class **bctx) +{ + unsigned char tmp[16], iv[16]; + + ctx->vtable = &br_eax_vtable; + ctx->bctx = bctx; + + /* + * Encrypt a whole-zero block to compute L2 and L4. + */ + memset(tmp, 0, sizeof tmp); + memset(iv, 0, sizeof iv); + (*bctx)->ctr(bctx, iv, tmp, sizeof tmp); + double_gf128(ctx->L2, tmp); + double_gf128(ctx->L4, ctx->L2); +} + +/* see bearssl_aead.h */ +void +br_eax_capture(const br_eax_context *ctx, br_eax_state *st) +{ + /* + * We capture the three OMAC* states _after_ processing the + * initial block (assuming that nonce, message and AAD are + * all non-empty). + */ + int i; + + memset(st->st, 0, sizeof st->st); + for (i = 0; i < 3; i ++) { + unsigned char tmp[16]; + + memset(tmp, 0, sizeof tmp); + tmp[15] = (unsigned char)i; + (*ctx->bctx)->mac(ctx->bctx, st->st[i], tmp, sizeof tmp); + } +} + +/* see bearssl_aead.h */ +void +br_eax_reset(br_eax_context *ctx, const void *nonce, size_t len) +{ + /* + * Process nonce with OMAC^0. + */ + omac_start(ctx, 0); + do_cbcmac_chunk(ctx, nonce, len); + do_pad(ctx); + memcpy(ctx->nonce, ctx->cbcmac, sizeof ctx->cbcmac); + + /* + * Start OMAC^1 for the AAD ("header" in the EAX specification). + */ + omac_start(ctx, 1); + + /* + * We use ctx->head[0] as temporary flag to mark that we are + * using a "normal" reset(). + */ + ctx->head[0] = 0; +} + +/* see bearssl_aead.h */ +void +br_eax_reset_pre_aad(br_eax_context *ctx, const br_eax_state *st, + const void *nonce, size_t len) +{ + if (len == 0) { + omac_start(ctx, 0); + } else { + memcpy(ctx->cbcmac, st->st[0], sizeof ctx->cbcmac); + ctx->ptr = 0; + do_cbcmac_chunk(ctx, nonce, len); + } + do_pad(ctx); + memcpy(ctx->nonce, ctx->cbcmac, sizeof ctx->cbcmac); + + memcpy(ctx->cbcmac, st->st[1], sizeof ctx->cbcmac); + ctx->ptr = 0; + + memcpy(ctx->ctr, st->st[2], sizeof ctx->ctr); + + /* + * We use ctx->head[0] as a flag to indicate that we use a + * a recorded state, with ctx->ctr containing the preprocessed + * first block for OMAC^2. + */ + ctx->head[0] = 1; +} + +/* see bearssl_aead.h */ +void +br_eax_reset_post_aad(br_eax_context *ctx, const br_eax_state *st, + const void *nonce, size_t len) +{ + if (len == 0) { + omac_start(ctx, 0); + } else { + memcpy(ctx->cbcmac, st->st[0], sizeof ctx->cbcmac); + ctx->ptr = 0; + do_cbcmac_chunk(ctx, nonce, len); + } + do_pad(ctx); + memcpy(ctx->nonce, ctx->cbcmac, sizeof ctx->cbcmac); + memcpy(ctx->ctr, ctx->nonce, sizeof ctx->nonce); + + memcpy(ctx->head, st->st[1], sizeof ctx->head); + + memcpy(ctx->cbcmac, st->st[2], sizeof ctx->cbcmac); + ctx->ptr = 0; +} + +/* see bearssl_aead.h */ +void +br_eax_aad_inject(br_eax_context *ctx, const void *data, size_t len) +{ + size_t ptr; + + ptr = ctx->ptr; + + /* + * If there is a partial block, first complete it. + */ + if (ptr < 16) { + size_t clen; + + clen = 16 - ptr; + if (len <= clen) { + memcpy(ctx->buf + ptr, data, len); + ctx->ptr = ptr + len; + return; + } + memcpy(ctx->buf + ptr, data, clen); + data = (const unsigned char *)data + clen; + len -= clen; + } + + /* + * We now have a full block in buf[], and this is not the last + * block. + */ + do_cbcmac_chunk(ctx, data, len); +} + +/* see bearssl_aead.h */ +void +br_eax_flip(br_eax_context *ctx) +{ + int from_capture; + + /* + * ctx->head[0] may be non-zero if the context was reset with + * a pre-AAD captured state. In that case, ctx->ctr[] contains + * the state for OMAC^2 _after_ processing the first block. + */ + from_capture = ctx->head[0]; + + /* + * Complete the OMAC computation on the AAD. + */ + do_pad(ctx); + memcpy(ctx->head, ctx->cbcmac, sizeof ctx->cbcmac); + + /* + * Start OMAC^2 for the encrypted data. + * If the context was initialized from a captured state, then + * the OMAC^2 value is in the ctr[] array. + */ + if (from_capture) { + memcpy(ctx->cbcmac, ctx->ctr, sizeof ctx->cbcmac); + ctx->ptr = 0; + } else { + omac_start(ctx, 2); + } + + /* + * Initial counter value for CTR is the processed nonce. + */ + memcpy(ctx->ctr, ctx->nonce, sizeof ctx->nonce); +} + +/* see bearssl_aead.h */ +void +br_eax_run(br_eax_context *ctx, int encrypt, void *data, size_t len) +{ + unsigned char *dbuf; + size_t ptr; + + /* + * Ensure that there is actual data to process. + */ + if (len == 0) { + return; + } + + dbuf = data; + ptr = ctx->ptr; + + /* + * We may have ptr == 0 here if we initialized from a captured + * state. In that case, there is no partially consumed block + * or unprocessed data. + */ + if (ptr != 0 && ptr != 16) { + /* + * We have a partially consumed block. + */ + size_t u, clen; + + clen = 16 - ptr; + if (len <= clen) { + clen = len; + } + if (encrypt) { + for (u = 0; u < clen; u ++) { + ctx->buf[ptr + u] ^= dbuf[u]; + } + memcpy(dbuf, ctx->buf + ptr, clen); + } else { + for (u = 0; u < clen; u ++) { + unsigned dx, sx; + + sx = ctx->buf[ptr + u]; + dx = dbuf[u]; + ctx->buf[ptr + u] = dx; + dbuf[u] = sx ^ dx; + } + } + + if (len <= clen) { + ctx->ptr = ptr + clen; + return; + } + dbuf += clen; + len -= clen; + } + + /* + * We now have a complete encrypted block in buf[] that must still + * be processed with OMAC, and this is not the final buf. + * Exception: when ptr == 0, no block has been produced yet. + */ + if (ptr != 0) { + (*ctx->bctx)->mac(ctx->bctx, ctx->cbcmac, + ctx->buf, sizeof ctx->buf); + } + + /* + * Do CTR encryption or decryption and CBC-MAC for all full blocks + * except the last. + */ + ptr = len & (size_t)15; + if (ptr == 0) { + len -= 16; + ptr = 16; + } else { + len -= ptr; + } + if (encrypt) { + (*ctx->bctx)->encrypt(ctx->bctx, ctx->ctr, ctx->cbcmac, + dbuf, len); + } else { + (*ctx->bctx)->decrypt(ctx->bctx, ctx->ctr, ctx->cbcmac, + dbuf, len); + } + dbuf += len; + + /* + * Compute next block of CTR stream, and use it to finish + * encrypting or decrypting the data. + */ + memset(ctx->buf, 0, sizeof ctx->buf); + (*ctx->bctx)->ctr(ctx->bctx, ctx->ctr, ctx->buf, sizeof ctx->buf); + if (encrypt) { + size_t u; + + for (u = 0; u < ptr; u ++) { + ctx->buf[u] ^= dbuf[u]; + } + memcpy(dbuf, ctx->buf, ptr); + } else { + size_t u; + + for (u = 0; u < ptr; u ++) { + unsigned dx, sx; + + sx = ctx->buf[u]; + dx = dbuf[u]; + ctx->buf[u] = dx; + dbuf[u] = sx ^ dx; + } + } + ctx->ptr = ptr; +} + +/* + * Complete tag computation. The final tag is written in ctx->cbcmac. + */ +static void +do_final(br_eax_context *ctx) +{ + size_t u; + + do_pad(ctx); + + /* + * Authentication tag is the XOR of the three OMAC outputs for + * the nonce, AAD and encrypted data. + */ + for (u = 0; u < 16; u ++) { + ctx->cbcmac[u] ^= ctx->nonce[u] ^ ctx->head[u]; + } +} + +/* see bearssl_aead.h */ +void +br_eax_get_tag(br_eax_context *ctx, void *tag) +{ + do_final(ctx); + memcpy(tag, ctx->cbcmac, sizeof ctx->cbcmac); +} + +/* see bearssl_aead.h */ +void +br_eax_get_tag_trunc(br_eax_context *ctx, void *tag, size_t len) +{ + do_final(ctx); + memcpy(tag, ctx->cbcmac, len); +} + +/* see bearssl_aead.h */ +uint32_t +br_eax_check_tag_trunc(br_eax_context *ctx, const void *tag, size_t len) +{ + unsigned char tmp[16]; + size_t u; + int x; + + br_eax_get_tag(ctx, tmp); + x = 0; + for (u = 0; u < len; u ++) { + x |= tmp[u] ^ ((const unsigned char *)tag)[u]; + } + return EQ0(x); +} + +/* see bearssl_aead.h */ +uint32_t +br_eax_check_tag(br_eax_context *ctx, const void *tag) +{ + return br_eax_check_tag_trunc(ctx, tag, 16); +} + +/* see bearssl_aead.h */ +const br_aead_class br_eax_vtable PROGMEM = { + 16, + (void (*)(const br_aead_class **, const void *, size_t)) + &br_eax_reset, + (void (*)(const br_aead_class **, const void *, size_t)) + &br_eax_aad_inject, + (void (*)(const br_aead_class **)) + &br_eax_flip, + (void (*)(const br_aead_class **, int, void *, size_t)) + &br_eax_run, + (void (*)(const br_aead_class **, void *)) + &br_eax_get_tag, + (uint32_t (*)(const br_aead_class **, const void *)) + &br_eax_check_tag, + (void (*)(const br_aead_class **, void *, size_t)) + &br_eax_get_tag_trunc, + (uint32_t (*)(const br_aead_class **, const void *, size_t)) + &br_eax_check_tag_trunc +}; diff --git a/lib/bearssl-esp8266/src/aead/gcm.c b/lib/bearssl-esp8266/src/aead/gcm.c new file mode 100644 index 000000000..40084ed65 --- /dev/null +++ b/lib/bearssl-esp8266/src/aead/gcm.c @@ -0,0 +1,318 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * Implementation Notes + * ==================== + * + * Since CTR and GHASH implementations can handle only full blocks, a + * 16-byte buffer (buf[]) is maintained in the context: + * + * - When processing AAD, buf[] contains the 0-15 unprocessed bytes. + * + * - When doing CTR encryption / decryption, buf[] contains the AES output + * for the last partial block, to be used with the next few bytes of + * data, as well as the already encrypted bytes. For instance, if the + * processed data length so far is 21 bytes, then buf[0..4] contains + * the five last encrypted bytes, and buf[5..15] contains the next 11 + * AES output bytes to be XORed with the next 11 bytes of input. + * + * The recorded AES output bytes are used to complete the block when + * the corresponding bytes are obtained. Note that buf[] always + * contains the _encrypted_ bytes, whether we apply encryption or + * decryption: these bytes are used as input to GHASH when the block + * is complete. + * + * In both cases, the low bits of the data length counters (count_aad, + * count_ctr) are used to work out the current situation. + */ + +/* see bearssl_aead.h */ +void +br_gcm_init(br_gcm_context *ctx, const br_block_ctr_class **bctx, br_ghash gh) +{ + unsigned char iv[12]; + + ctx->vtable = &br_gcm_vtable; + ctx->bctx = bctx; + ctx->gh = gh; + + /* + * The GHASH key h[] is the raw encryption of the all-zero + * block. Since we only have a CTR implementation, we use it + * with an all-zero IV and a zero counter, to CTR-encrypt an + * all-zero block. + */ + memset(ctx->h, 0, sizeof ctx->h); + memset(iv, 0, sizeof iv); + (*bctx)->run(bctx, iv, 0, ctx->h, sizeof ctx->h); +} + +/* see bearssl_aead.h */ +void +br_gcm_reset(br_gcm_context *ctx, const void *iv, size_t len) +{ + /* + * If the provided nonce is 12 bytes, then this is the initial + * IV for CTR mode; it will be used with a counter that starts + * at 2 (value 1 is for encrypting the GHASH output into the tag). + * + * If the provided nonce has any other length, then it is hashed + * (with GHASH) into a 16-byte value that will be the IV for CTR + * (both 12-byte IV and 32-bit counter). + */ + if (len == 12) { + memcpy(ctx->j0_1, iv, 12); + ctx->j0_2 = 1; + } else { + unsigned char ty[16], tmp[16]; + + memset(ty, 0, sizeof ty); + ctx->gh(ty, ctx->h, iv, len); + memset(tmp, 0, 8); + br_enc64be(tmp + 8, (uint64_t)len << 3); + ctx->gh(ty, ctx->h, tmp, 16); + memcpy(ctx->j0_1, ty, 12); + ctx->j0_2 = br_dec32be(ty + 12); + } + ctx->jc = ctx->j0_2 + 1; + memset(ctx->y, 0, sizeof ctx->y); + ctx->count_aad = 0; + ctx->count_ctr = 0; +} + +/* see bearssl_aead.h */ +void +br_gcm_aad_inject(br_gcm_context *ctx, const void *data, size_t len) +{ + size_t ptr, dlen; + + ptr = (size_t)ctx->count_aad & (size_t)15; + if (ptr != 0) { + /* + * If there is a partial block, then we first try to + * complete it. + */ + size_t clen; + + clen = 16 - ptr; + if (len < clen) { + memcpy(ctx->buf + ptr, data, len); + ctx->count_aad += (uint64_t)len; + return; + } + memcpy(ctx->buf + ptr, data, clen); + ctx->gh(ctx->y, ctx->h, ctx->buf, 16); + data = (const unsigned char *)data + clen; + len -= clen; + ctx->count_aad += (uint64_t)clen; + } + + /* + * Now AAD is aligned on a 16-byte block (with regards to GHASH). + * We process all complete blocks, and save the last partial + * block. + */ + dlen = len & ~(size_t)15; + ctx->gh(ctx->y, ctx->h, data, dlen); + memcpy(ctx->buf, (const unsigned char *)data + dlen, len - dlen); + ctx->count_aad += (uint64_t)len; +} + +/* see bearssl_aead.h */ +void +br_gcm_flip(br_gcm_context *ctx) +{ + /* + * We complete the GHASH computation if there is a partial block. + * The GHASH implementation automatically applies padding with + * zeros. + */ + size_t ptr; + + ptr = (size_t)ctx->count_aad & (size_t)15; + if (ptr != 0) { + ctx->gh(ctx->y, ctx->h, ctx->buf, ptr); + } +} + +/* see bearssl_aead.h */ +void +br_gcm_run(br_gcm_context *ctx, int encrypt, void *data, size_t len) +{ + unsigned char *buf; + size_t ptr, dlen; + + buf = data; + ptr = (size_t)ctx->count_ctr & (size_t)15; + if (ptr != 0) { + /* + * If we have a partial block, then we try to complete it. + */ + size_t u, clen; + + clen = 16 - ptr; + if (len < clen) { + clen = len; + } + for (u = 0; u < clen; u ++) { + unsigned x, y; + + x = buf[u]; + y = x ^ ctx->buf[ptr + u]; + ctx->buf[ptr + u] = encrypt ? y : x; + buf[u] = y; + } + ctx->count_ctr += (uint64_t)clen; + buf += clen; + len -= clen; + if (ptr + clen < 16) { + return; + } + ctx->gh(ctx->y, ctx->h, ctx->buf, 16); + } + + /* + * Process full blocks. + */ + dlen = len & ~(size_t)15; + if (!encrypt) { + ctx->gh(ctx->y, ctx->h, buf, dlen); + } + ctx->jc = (*ctx->bctx)->run(ctx->bctx, ctx->j0_1, ctx->jc, buf, dlen); + if (encrypt) { + ctx->gh(ctx->y, ctx->h, buf, dlen); + } + buf += dlen; + len -= dlen; + ctx->count_ctr += (uint64_t)dlen; + + if (len > 0) { + /* + * There is a partial block. + */ + size_t u; + + memset(ctx->buf, 0, sizeof ctx->buf); + ctx->jc = (*ctx->bctx)->run(ctx->bctx, ctx->j0_1, + ctx->jc, ctx->buf, 16); + for (u = 0; u < len; u ++) { + unsigned x, y; + + x = buf[u]; + y = x ^ ctx->buf[u]; + ctx->buf[u] = encrypt ? y : x; + buf[u] = y; + } + ctx->count_ctr += (uint64_t)len; + } +} + +/* see bearssl_aead.h */ +void +br_gcm_get_tag(br_gcm_context *ctx, void *tag) +{ + size_t ptr; + unsigned char tmp[16]; + + ptr = (size_t)ctx->count_ctr & (size_t)15; + if (ptr > 0) { + /* + * There is a partial block: encrypted/decrypted data has + * been produced, but the encrypted bytes must still be + * processed by GHASH. + */ + ctx->gh(ctx->y, ctx->h, ctx->buf, ptr); + } + + /* + * Final block for GHASH: the AAD and plaintext lengths (in bits). + */ + br_enc64be(tmp, ctx->count_aad << 3); + br_enc64be(tmp + 8, ctx->count_ctr << 3); + ctx->gh(ctx->y, ctx->h, tmp, 16); + + /* + * Tag is the GHASH output XORed with the encryption of the + * nonce with the initial counter value. + */ + memcpy(tag, ctx->y, 16); + (*ctx->bctx)->run(ctx->bctx, ctx->j0_1, ctx->j0_2, tag, 16); +} + +/* see bearssl_aead.h */ +void +br_gcm_get_tag_trunc(br_gcm_context *ctx, void *tag, size_t len) +{ + unsigned char tmp[16]; + + br_gcm_get_tag(ctx, tmp); + memcpy(tag, tmp, len); +} + +/* see bearssl_aead.h */ +uint32_t +br_gcm_check_tag_trunc(br_gcm_context *ctx, const void *tag, size_t len) +{ + unsigned char tmp[16]; + size_t u; + int x; + + br_gcm_get_tag(ctx, tmp); + x = 0; + for (u = 0; u < len; u ++) { + x |= tmp[u] ^ ((const unsigned char *)tag)[u]; + } + return EQ0(x); +} + +/* see bearssl_aead.h */ +uint32_t +br_gcm_check_tag(br_gcm_context *ctx, const void *tag) +{ + return br_gcm_check_tag_trunc(ctx, tag, 16); +} + +/* see bearssl_aead.h */ +const br_aead_class br_gcm_vtable PROGMEM = { + 16, + (void (*)(const br_aead_class **, const void *, size_t)) + &br_gcm_reset, + (void (*)(const br_aead_class **, const void *, size_t)) + &br_gcm_aad_inject, + (void (*)(const br_aead_class **)) + &br_gcm_flip, + (void (*)(const br_aead_class **, int, void *, size_t)) + &br_gcm_run, + (void (*)(const br_aead_class **, void *)) + &br_gcm_get_tag, + (uint32_t (*)(const br_aead_class **, const void *)) + &br_gcm_check_tag, + (void (*)(const br_aead_class **, void *, size_t)) + &br_gcm_get_tag_trunc, + (uint32_t (*)(const br_aead_class **, const void *, size_t)) + &br_gcm_check_tag_trunc +}; diff --git a/lib/bearssl-esp8266/src/codec/ccopy.c b/lib/bearssl-esp8266/src/codec/ccopy.c new file mode 100644 index 000000000..33f4b1a49 --- /dev/null +++ b/lib/bearssl-esp8266/src/codec/ccopy.c @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +void +br_ccopy(uint32_t ctl, void *dst, const void *src, size_t len) +{ + unsigned char *d; + const unsigned char *s; + + d = dst; + s = src; + while (len -- > 0) { + uint32_t x, y; + + x = *s ++; + y = *d; + *d = MUX(ctl, x, y); + d ++; + } +} diff --git a/lib/bearssl-esp8266/src/codec/dec16be.c b/lib/bearssl-esp8266/src/codec/dec16be.c new file mode 100644 index 000000000..af7b48074 --- /dev/null +++ b/lib/bearssl-esp8266/src/codec/dec16be.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +void +br_range_dec16be(uint16_t *v, size_t num, const void *src) +{ + const unsigned char *buf; + + buf = src; + while (num -- > 0) { + *v ++ = br_dec16be(buf); + buf += 2; + } +} diff --git a/lib/bearssl-esp8266/src/codec/dec16le.c b/lib/bearssl-esp8266/src/codec/dec16le.c new file mode 100644 index 000000000..4bf0174b9 --- /dev/null +++ b/lib/bearssl-esp8266/src/codec/dec16le.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +void +br_range_dec16le(uint16_t *v, size_t num, const void *src) +{ + const unsigned char *buf; + + buf = src; + while (num -- > 0) { + *v ++ = br_dec16le(buf); + buf += 2; + } +} diff --git a/lib/bearssl-esp8266/src/codec/dec32be.c b/lib/bearssl-esp8266/src/codec/dec32be.c new file mode 100644 index 000000000..51016e5b9 --- /dev/null +++ b/lib/bearssl-esp8266/src/codec/dec32be.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +void +br_range_dec32be(uint32_t *v, size_t num, const void *src) +{ + const unsigned char *buf; + + buf = src; + while (num -- > 0) { + *v ++ = br_dec32be(buf); + buf += 4; + } +} diff --git a/lib/bearssl-esp8266/src/codec/dec32le.c b/lib/bearssl-esp8266/src/codec/dec32le.c new file mode 100644 index 000000000..22b2c01a8 --- /dev/null +++ b/lib/bearssl-esp8266/src/codec/dec32le.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +void +br_range_dec32le(uint32_t *v, size_t num, const void *src) +{ + const unsigned char *buf; + + buf = src; + while (num -- > 0) { + *v ++ = br_dec32le(buf); + buf += 4; + } +} diff --git a/lib/bearssl-esp8266/src/codec/dec64be.c b/lib/bearssl-esp8266/src/codec/dec64be.c new file mode 100644 index 000000000..55632e4a5 --- /dev/null +++ b/lib/bearssl-esp8266/src/codec/dec64be.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +void +br_range_dec64be(uint64_t *v, size_t num, const void *src) +{ + const unsigned char *buf; + + buf = src; + while (num -- > 0) { + *v ++ = br_dec64be(buf); + buf += 8; + } +} diff --git a/lib/bearssl-esp8266/src/codec/dec64le.c b/lib/bearssl-esp8266/src/codec/dec64le.c new file mode 100644 index 000000000..e77614a5c --- /dev/null +++ b/lib/bearssl-esp8266/src/codec/dec64le.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +void +br_range_dec64le(uint64_t *v, size_t num, const void *src) +{ + const unsigned char *buf; + + buf = src; + while (num -- > 0) { + *v ++ = br_dec64le(buf); + buf += 8; + } +} diff --git a/lib/bearssl-esp8266/src/codec/enc16be.c b/lib/bearssl-esp8266/src/codec/enc16be.c new file mode 100644 index 000000000..be29cd6e9 --- /dev/null +++ b/lib/bearssl-esp8266/src/codec/enc16be.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +void +br_range_enc16be(void *dst, const uint16_t *v, size_t num) +{ + unsigned char *buf; + + buf = dst; + while (num -- > 0) { + br_enc16be(buf, *v ++); + buf += 2; + } +} diff --git a/lib/bearssl-esp8266/src/codec/enc16le.c b/lib/bearssl-esp8266/src/codec/enc16le.c new file mode 100644 index 000000000..d5f6f7ea0 --- /dev/null +++ b/lib/bearssl-esp8266/src/codec/enc16le.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +void +br_range_enc16le(void *dst, const uint16_t *v, size_t num) +{ + unsigned char *buf; + + buf = dst; + while (num -- > 0) { + br_enc16le(buf, *v ++); + buf += 2; + } +} diff --git a/lib/bearssl-esp8266/src/codec/enc32be.c b/lib/bearssl-esp8266/src/codec/enc32be.c new file mode 100644 index 000000000..89fad30d6 --- /dev/null +++ b/lib/bearssl-esp8266/src/codec/enc32be.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +void +br_range_enc32be(void *dst, const uint32_t *v, size_t num) +{ + unsigned char *buf; + + buf = dst; + while (num -- > 0) { + br_enc32be(buf, *v ++); + buf += 4; + } +} diff --git a/lib/bearssl-esp8266/src/codec/enc32le.c b/lib/bearssl-esp8266/src/codec/enc32le.c new file mode 100644 index 000000000..4fae44774 --- /dev/null +++ b/lib/bearssl-esp8266/src/codec/enc32le.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +void +br_range_enc32le(void *dst, const uint32_t *v, size_t num) +{ + unsigned char *buf; + + buf = dst; + while (num -- > 0) { + br_enc32le(buf, *v ++); + buf += 4; + } +} diff --git a/lib/bearssl-esp8266/src/codec/enc64be.c b/lib/bearssl-esp8266/src/codec/enc64be.c new file mode 100644 index 000000000..d06ffebbd --- /dev/null +++ b/lib/bearssl-esp8266/src/codec/enc64be.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +void +br_range_enc64be(void *dst, const uint64_t *v, size_t num) +{ + unsigned char *buf; + + buf = dst; + while (num -- > 0) { + br_enc64be(buf, *v ++); + buf += 8; + } +} diff --git a/lib/bearssl-esp8266/src/codec/enc64le.c b/lib/bearssl-esp8266/src/codec/enc64le.c new file mode 100644 index 000000000..8e2f8e887 --- /dev/null +++ b/lib/bearssl-esp8266/src/codec/enc64le.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +void +br_range_enc64le(void *dst, const uint64_t *v, size_t num) +{ + unsigned char *buf; + + buf = dst; + while (num -- > 0) { + br_enc64le(buf, *v ++); + buf += 8; + } +} diff --git a/lib/bearssl-esp8266/src/codec/pemdec.c b/lib/bearssl-esp8266/src/codec/pemdec.c new file mode 100644 index 000000000..0de8d3414 --- /dev/null +++ b/lib/bearssl-esp8266/src/codec/pemdec.c @@ -0,0 +1,536 @@ +/* Automatically generated code; do not modify directly. */ + +#include +#include +#include + +typedef struct { + uint32_t *dp; + uint32_t *rp; + const unsigned char *ip; +} t0_context; + +static uint32_t +t0_parse7E_unsigned(const unsigned char **p) +{ + uint32_t x; + + x = 0; + for (;;) { + unsigned y; + + y = pgm_read_byte((*p)++); + x = (x << 7) | (uint32_t)(y & 0x7F); + if (y < 0x80) { + return x; + } + } +} + +static int32_t +t0_parse7E_signed(const unsigned char **p) +{ + int neg; + uint32_t x; + + neg = (pgm_read_byte(*p) >> 6) & 1; + x = (uint32_t)-neg; + for (;;) { + unsigned y; + + y = pgm_read_byte((*p)++); + x = (x << 7) | (uint32_t)(y & 0x7F); + if (y < 0x80) { + if (neg) { + return -(int32_t)~x - 1; + } else { + return (int32_t)x; + } + } + } +} + +#define T0_VBYTE(x, n) (unsigned char)((((uint32_t)(x) >> (n)) & 0x7F) | 0x80) +#define T0_FBYTE(x, n) (unsigned char)(((uint32_t)(x) >> (n)) & 0x7F) +#define T0_SBYTE(x) (unsigned char)((((uint32_t)(x) >> 28) + 0xF8) ^ 0xF8) +#define T0_INT1(x) T0_FBYTE(x, 0) +#define T0_INT2(x) T0_VBYTE(x, 7), T0_FBYTE(x, 0) +#define T0_INT3(x) T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0) +#define T0_INT4(x) T0_VBYTE(x, 21), T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0) +#define T0_INT5(x) T0_SBYTE(x), T0_VBYTE(x, 21), T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0) + +/* static const unsigned char t0_datablock[]; */ + + +void br_pem_decoder_init_main(void *t0ctx); + +void br_pem_decoder_run(void *t0ctx); + + + +#include "t_inner.h" + +#define CTX ((br_pem_decoder_context *)(void *)((unsigned char *)t0ctx - offsetof(br_pem_decoder_context, cpu))) + +/* see bearssl_pem.h */ +void +br_pem_decoder_init(br_pem_decoder_context *ctx) +{ + memset(ctx, 0, sizeof *ctx); + ctx->cpu.dp = &ctx->dp_stack[0]; + ctx->cpu.rp = &ctx->rp_stack[0]; + br_pem_decoder_init_main(&ctx->cpu); + br_pem_decoder_run(&ctx->cpu); +} + +/* see bearssl_pem.h */ +size_t +br_pem_decoder_push(br_pem_decoder_context *ctx, + const void *data, size_t len) +{ + if (ctx->event) { + return 0; + } + ctx->hbuf = data; + ctx->hlen = len; + br_pem_decoder_run(&ctx->cpu); + return len - ctx->hlen; +} + +/* see bearssl_pem.h */ +int +br_pem_decoder_event(br_pem_decoder_context *ctx) +{ + int event; + + event = ctx->event; + ctx->event = 0; + return event; +} + + + +static const unsigned char t0_datablock[] PROGMEM = { + + 0x00, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x42, 0x45, 0x47, 0x49, 0x4E, 0x20, + 0x00, 0x2D, 0x2D, 0x2D, 0x2D, 0x45, 0x4E, 0x44, 0x20, 0x00 +}; + +static const unsigned char t0_codeblock[] PROGMEM = { + + 0x00, 0x01, 0x00, 0x09, 0x00, 0x00, 0x01, 0x01, 0x07, 0x00, 0x00, 0x01, + 0x01, 0x08, 0x00, 0x00, 0x13, 0x13, 0x00, 0x00, 0x01, + T0_INT2(offsetof(br_pem_decoder_context, event)), 0x00, 0x00, 0x01, + T0_INT2(offsetof(br_pem_decoder_context, name)), 0x00, 0x00, 0x05, + 0x14, 0x2C, 0x14, 0x01, 0x0A, 0x0D, 0x06, 0x03, 0x13, 0x04, 0x76, 0x01, + 0x2D, 0x0C, 0x06, 0x05, 0x2E, 0x01, 0x03, 0x2D, 0x00, 0x01, 0x0D, 0x27, + 0x05, 0x04, 0x01, 0x03, 0x2D, 0x00, 0x15, 0x2E, 0x01, 0x02, 0x2D, 0x00, + 0x01, 0x01, 0x7F, 0x03, 0x00, 0x25, 0x01, 0x00, 0x18, 0x0D, 0x06, 0x03, + 0x13, 0x04, 0x3C, 0x01, 0x7F, 0x18, 0x0D, 0x06, 0x13, 0x13, 0x02, 0x00, + 0x05, 0x06, 0x2E, 0x01, 0x03, 0x2D, 0x04, 0x03, 0x01, 0x7F, 0x23, 0x01, + 0x00, 0x00, 0x04, 0x23, 0x01, 0x01, 0x18, 0x0D, 0x06, 0x09, 0x13, 0x01, + 0x00, 0x23, 0x01, 0x00, 0x00, 0x04, 0x14, 0x01, 0x02, 0x18, 0x0D, 0x06, + 0x06, 0x13, 0x01, 0x7F, 0x00, 0x04, 0x08, 0x13, 0x01, 0x03, 0x2D, 0x01, + 0x00, 0x00, 0x13, 0x01, 0x00, 0x03, 0x00, 0x04, 0xFF, 0x33, 0x01, 0x2C, + 0x14, 0x01, 0x2D, 0x0D, 0x06, 0x04, 0x13, 0x01, 0x7F, 0x00, 0x14, 0x31, + 0x06, 0x02, 0x13, 0x29, 0x14, 0x01, 0x0A, 0x0D, 0x06, 0x04, 0x13, 0x01, + 0x02, 0x00, 0x16, 0x14, 0x1D, 0x06, 0x05, 0x13, 0x2E, 0x01, 0x03, 0x00, + 0x03, 0x00, 0x29, 0x14, 0x01, 0x0A, 0x0D, 0x06, 0x04, 0x13, 0x01, 0x03, + 0x00, 0x16, 0x14, 0x1D, 0x06, 0x05, 0x13, 0x2E, 0x01, 0x03, 0x00, 0x02, + 0x00, 0x01, 0x06, 0x0A, 0x07, 0x03, 0x00, 0x29, 0x14, 0x01, 0x0A, 0x0D, + 0x06, 0x04, 0x13, 0x01, 0x03, 0x00, 0x14, 0x01, 0x3D, 0x0D, 0x06, 0x2E, + 0x13, 0x29, 0x14, 0x01, 0x0A, 0x0D, 0x06, 0x04, 0x13, 0x01, 0x03, 0x00, + 0x2F, 0x05, 0x04, 0x13, 0x01, 0x03, 0x00, 0x01, 0x3D, 0x0C, 0x06, 0x03, + 0x01, 0x03, 0x00, 0x02, 0x00, 0x01, 0x0F, 0x10, 0x06, 0x03, 0x01, 0x03, + 0x00, 0x02, 0x00, 0x01, 0x04, 0x0F, 0x1C, 0x01, 0x01, 0x00, 0x16, 0x14, + 0x1D, 0x06, 0x05, 0x13, 0x2E, 0x01, 0x03, 0x00, 0x02, 0x00, 0x01, 0x06, + 0x0A, 0x07, 0x03, 0x00, 0x29, 0x14, 0x01, 0x0A, 0x0D, 0x06, 0x04, 0x13, + 0x01, 0x03, 0x00, 0x14, 0x01, 0x3D, 0x0D, 0x06, 0x20, 0x13, 0x2F, 0x05, + 0x03, 0x01, 0x03, 0x00, 0x02, 0x00, 0x01, 0x03, 0x10, 0x06, 0x03, 0x01, + 0x03, 0x00, 0x02, 0x00, 0x01, 0x0A, 0x0F, 0x1C, 0x02, 0x00, 0x01, 0x02, + 0x0F, 0x1C, 0x01, 0x01, 0x00, 0x16, 0x14, 0x1D, 0x06, 0x05, 0x13, 0x2E, + 0x01, 0x03, 0x00, 0x02, 0x00, 0x01, 0x06, 0x0A, 0x07, 0x03, 0x00, 0x02, + 0x00, 0x01, 0x10, 0x0F, 0x1C, 0x02, 0x00, 0x01, 0x08, 0x0F, 0x1C, 0x02, + 0x00, 0x1C, 0x01, 0x00, 0x00, 0x00, 0x28, 0x01, 0x01, 0x2D, 0x24, 0x06, + 0x02, 0x04, 0x7B, 0x04, 0x75, 0x00, 0x14, 0x12, 0x2A, 0x14, 0x05, 0x04, + 0x20, 0x01, 0x7F, 0x00, 0x2C, 0x2A, 0x14, 0x01, 0x0A, 0x0D, 0x06, 0x05, + 0x13, 0x20, 0x01, 0x00, 0x00, 0x0D, 0x05, 0x05, 0x13, 0x2E, 0x01, 0x00, + 0x00, 0x1E, 0x04, 0x5E, 0x00, 0x01, 0x01, 0x27, 0x06, 0x0B, 0x22, 0x01, + 0x80, 0x7F, 0x2B, 0x14, 0x06, 0x02, 0x30, 0x00, 0x13, 0x04, 0x6E, 0x00, + 0x2C, 0x14, 0x31, 0x05, 0x01, 0x00, 0x13, 0x04, 0x77, 0x00, 0x14, 0x14, + 0x01, 0x80, 0x61, 0x0E, 0x1B, 0x01, 0x80, 0x7A, 0x0B, 0x10, 0x06, 0x03, + 0x01, 0x20, 0x08, 0x00, 0x01, 0x14, 0x03, 0x00, 0x1B, 0x18, 0x05, 0x05, + 0x20, 0x2E, 0x01, 0x00, 0x00, 0x2C, 0x14, 0x01, 0x0A, 0x0D, 0x06, 0x06, + 0x20, 0x02, 0x00, 0x1B, 0x08, 0x00, 0x14, 0x01, 0x0D, 0x0D, 0x06, 0x03, + 0x13, 0x04, 0x03, 0x2A, 0x18, 0x1A, 0x1E, 0x1B, 0x1F, 0x1B, 0x04, 0x59, + 0x00, 0x19, 0x14, 0x1D, 0x05, 0x01, 0x00, 0x13, 0x11, 0x04, 0x76, 0x00, + 0x21, 0x1A, 0x11, 0x00, 0x00, 0x2C, 0x01, 0x0A, 0x0C, 0x06, 0x02, 0x04, + 0x78, 0x00, 0x01, 0x01, 0x7F, 0x03, 0x00, 0x2C, 0x14, 0x01, 0x0A, 0x0C, + 0x06, 0x09, 0x31, 0x05, 0x04, 0x01, 0x00, 0x03, 0x00, 0x04, 0x70, 0x13, + 0x02, 0x00, 0x00, 0x00, 0x14, 0x06, 0x14, 0x1F, 0x14, 0x22, 0x07, 0x17, + 0x01, 0x2D, 0x0C, 0x06, 0x08, 0x22, 0x07, 0x1E, 0x01, 0x00, 0x1B, 0x1A, + 0x00, 0x04, 0x69, 0x22, 0x1A, 0x00, 0x00, 0x14, 0x01, 0x0A, 0x0C, 0x1B, + 0x01, 0x20, 0x0B, 0x10, 0x00 +}; + +static const uint16_t t0_caddr[] PROGMEM = { + + 0, + 5, + 10, + 15, + 19, + 24, + 29, + 67, + 149, + 384, + 396, + 431, + 450, + 460, + 479, + 523, + 534, + 539, + 549, + 574, + 601 +}; + +#define T0_INTERPRETED 29 + +#define T0_ENTER(ip, rp, slot) do { \ + const unsigned char *t0_newip; \ + uint32_t t0_lnum; \ + t0_newip = &t0_codeblock[pgm_read_word(&t0_caddr[(slot) - T0_INTERPRETED])]; \ + t0_lnum = t0_parse7E_unsigned(&t0_newip); \ + (rp) += t0_lnum; \ + *((rp) ++) = (uint32_t)((ip) - &t0_codeblock[0]) + (t0_lnum << 16); \ + (ip) = t0_newip; \ + } while (0) + +#define T0_DEFENTRY(name, slot) \ +void \ +name(void *ctx) \ +{ \ + t0_context *t0ctx = ctx; \ + t0ctx->ip = &t0_codeblock[0]; \ + T0_ENTER(t0ctx->ip, t0ctx->rp, slot); \ +} + +T0_DEFENTRY(br_pem_decoder_init_main, 38) + +#define T0_NEXT(t0ipp) (pgm_read_byte((*t0ipp)++)) + +void +br_pem_decoder_run(void *t0ctx) +{ + uint32_t *dp, *rp; + const unsigned char *ip; + +#define T0_LOCAL(x) (*(rp - 2 - (x))) +#define T0_POP() (*-- dp) +#define T0_POPi() (*(int32_t *)(-- dp)) +#define T0_PEEK(x) (*(dp - 1 - (x))) +#define T0_PEEKi(x) (*(int32_t *)(dp - 1 - (x))) +#define T0_PUSH(v) do { *dp = (v); dp ++; } while (0) +#define T0_PUSHi(v) do { *(int32_t *)dp = (v); dp ++; } while (0) +#define T0_RPOP() (*-- rp) +#define T0_RPOPi() (*(int32_t *)(-- rp)) +#define T0_RPUSH(v) do { *rp = (v); rp ++; } while (0) +#define T0_RPUSHi(v) do { *(int32_t *)rp = (v); rp ++; } while (0) +#define T0_ROLL(x) do { \ + size_t t0len = (size_t)(x); \ + uint32_t t0tmp = *(dp - 1 - t0len); \ + memmove(dp - t0len - 1, dp - t0len, t0len * sizeof *dp); \ + *(dp - 1) = t0tmp; \ +} while (0) +#define T0_SWAP() do { \ + uint32_t t0tmp = *(dp - 2); \ + *(dp - 2) = *(dp - 1); \ + *(dp - 1) = t0tmp; \ +} while (0) +#define T0_ROT() do { \ + uint32_t t0tmp = *(dp - 3); \ + *(dp - 3) = *(dp - 2); \ + *(dp - 2) = *(dp - 1); \ + *(dp - 1) = t0tmp; \ +} while (0) +#define T0_NROT() do { \ + uint32_t t0tmp = *(dp - 1); \ + *(dp - 1) = *(dp - 2); \ + *(dp - 2) = *(dp - 3); \ + *(dp - 3) = t0tmp; \ +} while (0) +#define T0_PICK(x) do { \ + uint32_t t0depth = (x); \ + T0_PUSH(T0_PEEK(t0depth)); \ +} while (0) +#define T0_CO() do { \ + goto t0_exit; \ +} while (0) +#define T0_RET() goto t0_next + + dp = ((t0_context *)t0ctx)->dp; + rp = ((t0_context *)t0ctx)->rp; + ip = ((t0_context *)t0ctx)->ip; + goto t0_next; + for (;;) { + uint32_t t0x; + + t0_next: + t0x = T0_NEXT(&ip); + if (t0x < T0_INTERPRETED) { + switch (t0x) { + int32_t t0off; + + case 0: /* ret */ + t0x = T0_RPOP(); + rp -= (t0x >> 16); + t0x &= 0xFFFF; + if (t0x == 0) { + ip = NULL; + goto t0_exit; + } + ip = &t0_codeblock[t0x]; + break; + case 1: /* literal constant */ + T0_PUSHi(t0_parse7E_signed(&ip)); + break; + case 2: /* read local */ + T0_PUSH(T0_LOCAL(t0_parse7E_unsigned(&ip))); + break; + case 3: /* write local */ + T0_LOCAL(t0_parse7E_unsigned(&ip)) = T0_POP(); + break; + case 4: /* jump */ + t0off = t0_parse7E_signed(&ip); + ip += t0off; + break; + case 5: /* jump if */ + t0off = t0_parse7E_signed(&ip); + if (T0_POP()) { + ip += t0off; + } + break; + case 6: /* jump if not */ + t0off = t0_parse7E_signed(&ip); + if (!T0_POP()) { + ip += t0off; + } + break; + case 7: { + /* + */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(a + b); + + } + break; + case 8: { + /* - */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(a - b); + + } + break; + case 9: { + /* < */ + + int32_t b = T0_POPi(); + int32_t a = T0_POPi(); + T0_PUSH(-(uint32_t)(a < b)); + + } + break; + case 10: { + /* << */ + + int c = (int)T0_POPi(); + uint32_t x = T0_POP(); + T0_PUSH(x << c); + + } + break; + case 11: { + /* <= */ + + int32_t b = T0_POPi(); + int32_t a = T0_POPi(); + T0_PUSH(-(uint32_t)(a <= b)); + + } + break; + case 12: { + /* <> */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(-(uint32_t)(a != b)); + + } + break; + case 13: { + /* = */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(-(uint32_t)(a == b)); + + } + break; + case 14: { + /* >= */ + + int32_t b = T0_POPi(); + int32_t a = T0_POPi(); + T0_PUSH(-(uint32_t)(a >= b)); + + } + break; + case 15: { + /* >> */ + + int c = (int)T0_POPi(); + int32_t x = T0_POPi(); + T0_PUSHi(x >> c); + + } + break; + case 16: { + /* and */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(a & b); + + } + break; + case 17: { + /* co */ + T0_CO(); + } + break; + case 18: { + /* data-get8 */ + + size_t addr = T0_POP(); + T0_PUSH(pgm_read_byte(&t0_datablock[addr])); + + } + break; + case 19: { + /* drop */ + (void)T0_POP(); + } + break; + case 20: { + /* dup */ + T0_PUSH(T0_PEEK(0)); + } + break; + case 21: { + /* flush-buf */ + + if (CTX->ptr > 0) { + if (CTX->dest) { + CTX->dest(CTX->dest_ctx, CTX->buf, CTX->ptr); + } + CTX->ptr = 0; + } + + } + break; + case 22: { + /* from-base64 */ + + uint32_t c = T0_POP(); + uint32_t p, q, r, z; + p = c - 0x41; + q = c - 0x61; + r = c - 0x30; + + z = ((p + 2) & -LT(p, 26)) + | ((q + 28) & -LT(q, 26)) + | ((r + 54) & -LT(r, 10)) + | (64 & -EQ(c, 0x2B)) + | (65 & -EQ(c, 0x2F)) + | EQ(c, 0x3D); + T0_PUSHi((int32_t)z - 2); + + } + break; + case 23: { + /* get8 */ + + size_t addr = T0_POP(); + T0_PUSH(*((unsigned char *)CTX + addr)); + + } + break; + case 24: { + /* over */ + T0_PUSH(T0_PEEK(1)); + } + break; + case 25: { + /* read8-native */ + + do { + if (CTX->hlen > 0) { + uint8_t ch = pgm_read_byte(CTX->hbuf ++); + CTX->hlen --; + if (ch == '\r') continue; // skip \rs + T0_PUSH(ch); + break; + } else { + T0_PUSHi(-1); + break; + } + } while (1); + + } + break; + case 26: { + /* set8 */ + + size_t addr = T0_POP(); + unsigned x = T0_POP(); + *((unsigned char *)CTX + addr) = x; + + } + break; + case 27: { + /* swap */ + T0_SWAP(); + } + break; + case 28: { + /* write8 */ + + unsigned char x = (unsigned char)T0_POP(); + CTX->buf[CTX->ptr ++] = x; + if (CTX->ptr == sizeof CTX->buf) { + if (CTX->dest) { + CTX->dest(CTX->dest_ctx, CTX->buf, sizeof CTX->buf); + } + CTX->ptr = 0; + } + + } + break; + } + + } else { + T0_ENTER(ip, rp, t0x); + } + } +t0_exit: + ((t0_context *)t0ctx)->dp = dp; + ((t0_context *)t0ctx)->rp = rp; + ((t0_context *)t0ctx)->ip = ip; +} diff --git a/lib/bearssl-esp8266/src/codec/pemenc.c b/lib/bearssl-esp8266/src/codec/pemenc.c new file mode 100644 index 000000000..b6475cd68 --- /dev/null +++ b/lib/bearssl-esp8266/src/codec/pemenc.c @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * Get the appropriate Base64 character for a numeric value in the + * 0..63 range. This is constant-time. + */ +static char +b64char(uint32_t x) +{ + /* + * Values 0 to 25 map to 0x41..0x5A ('A' to 'Z') + * Values 26 to 51 map to 0x61..0x7A ('a' to 'z') + * Values 52 to 61 map to 0x30..0x39 ('0' to '9') + * Value 62 maps to 0x2B ('+') + * Value 63 maps to 0x2F ('/') + */ + uint32_t a, b, c; + + a = x - 26; + b = x - 52; + c = x - 62; + + /* + * Looking at bits 8..15 of values a, b and c: + * + * x a b c + * --------------------- + * 0..25 FF FF FF + * 26..51 00 FF FF + * 52..61 00 00 FF + * 62..63 00 00 00 + */ + return (char)(((x + 0x41) & ((a & b & c) >> 8)) + | ((x + (0x61 - 26)) & ((~a & b & c) >> 8)) + | ((x - (52 - 0x30)) & ((~a & ~b & c) >> 8)) + | ((0x2B + ((x & 1) << 2)) & (~(a | b | c) >> 8))); +} + +/* see bearssl_pem.h */ +size_t +br_pem_encode(void *dest, const void *data, size_t len, + const char *banner, unsigned flags) +{ + size_t dlen, banner_len, lines; + char *d; + unsigned char *buf; + size_t u; + int off, lim; + + banner_len = strlen(banner); + /* FIXME: try to avoid divisions here, as they may pull + an extra libc function. */ + if ((flags & BR_PEM_LINE64) != 0) { + lines = (len + 47) / 48; + } else { + lines = (len + 56) / 57; + } + dlen = (banner_len << 1) + 30 + (((len + 2) / 3) << 2) + + lines + 2; + if ((flags & BR_PEM_CRLF) != 0) { + dlen += lines + 2; + } + + if (dest == NULL) { + return dlen; + } + + d = dest; + + /* + * We always move the source data to the end of output buffer; + * the encoding process never "catches up" except at the very + * end. This also handles all conditions of partial or total + * overlap. + */ + buf = (unsigned char *)d + dlen - len; + memmove(buf, data, len); + + memcpy(d, "-----BEGIN ", 11); + d += 11; + memcpy(d, banner, banner_len); + d += banner_len; + memcpy(d, "-----", 5); + d += 5; + if ((flags & BR_PEM_CRLF) != 0) { + *d ++ = 0x0D; + } + *d ++ = 0x0A; + + off = 0; + lim = (flags & BR_PEM_LINE64) != 0 ? 16 : 19; + for (u = 0; (u + 2) < len; u += 3) { + uint32_t w; + + w = ((uint32_t)buf[u] << 16) + | ((uint32_t)buf[u + 1] << 8) + | (uint32_t)buf[u + 2]; + *d ++ = b64char(w >> 18); + *d ++ = b64char((w >> 12) & 0x3F); + *d ++ = b64char((w >> 6) & 0x3F); + *d ++ = b64char(w & 0x3F); + if (++ off == lim) { + off = 0; + if ((flags & BR_PEM_CRLF) != 0) { + *d ++ = 0x0D; + } + *d ++ = 0x0A; + } + } + if (u < len) { + uint32_t w; + + w = (uint32_t)buf[u] << 16; + if (u + 1 < len) { + w |= (uint32_t)buf[u + 1] << 8; + } + *d ++ = b64char(w >> 18); + *d ++ = b64char((w >> 12) & 0x3F); + if (u + 1 < len) { + *d ++ = b64char((w >> 6) & 0x3F); + } else { + *d ++ = 0x3D; + } + *d ++ = 0x3D; + off ++; + } + if (off != 0) { + if ((flags & BR_PEM_CRLF) != 0) { + *d ++ = 0x0D; + } + *d ++ = 0x0A; + } + + memcpy(d, "-----END ", 9); + d += 9; + memcpy(d, banner, banner_len); + d += banner_len; + memcpy(d, "-----", 5); + d += 5; + if ((flags & BR_PEM_CRLF) != 0) { + *d ++ = 0x0D; + } + *d ++ = 0x0A; + + /* Final zero, not counted in returned length. */ + *d ++ = 0x00; + + return dlen; +} diff --git a/lib/bearssl-esp8266/src/ec/ec_all_m15.c b/lib/bearssl-esp8266/src/ec/ec_all_m15.c new file mode 100644 index 000000000..a8708dd91 --- /dev/null +++ b/lib/bearssl-esp8266/src/ec/ec_all_m15.c @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +static const unsigned char * +api_generator(int curve, size_t *len) +{ + switch (curve) { + case BR_EC_secp256r1: + return br_ec_p256_m15.generator(curve, len); + case BR_EC_curve25519: + return br_ec_c25519_m15.generator(curve, len); + default: + return br_ec_prime_i15.generator(curve, len); + } +} + +static const unsigned char * +api_order(int curve, size_t *len) +{ + switch (curve) { + case BR_EC_secp256r1: + return br_ec_p256_m15.order(curve, len); + case BR_EC_curve25519: + return br_ec_c25519_m15.order(curve, len); + default: + return br_ec_prime_i15.order(curve, len); + } +} + +static size_t +api_xoff(int curve, size_t *len) +{ + switch (curve) { + case BR_EC_secp256r1: + return br_ec_p256_m15.xoff(curve, len); + case BR_EC_curve25519: + return br_ec_c25519_m15.xoff(curve, len); + default: + return br_ec_prime_i15.xoff(curve, len); + } +} + +static uint32_t +api_mul(unsigned char *G, size_t Glen, + const unsigned char *kb, size_t kblen, int curve) +{ + switch (curve) { + case BR_EC_secp256r1: + return br_ec_p256_m15.mul(G, Glen, kb, kblen, curve); + case BR_EC_curve25519: + return br_ec_c25519_m15.mul(G, Glen, kb, kblen, curve); + default: + return br_ec_prime_i15.mul(G, Glen, kb, kblen, curve); + } +} + +static size_t +api_mulgen(unsigned char *R, + const unsigned char *x, size_t xlen, int curve) +{ + switch (curve) { + case BR_EC_secp256r1: + return br_ec_p256_m15.mulgen(R, x, xlen, curve); + case BR_EC_curve25519: + return br_ec_c25519_m15.mulgen(R, x, xlen, curve); + default: + return br_ec_prime_i15.mulgen(R, x, xlen, curve); + } +} + +static uint32_t +api_muladd(unsigned char *A, const unsigned char *B, size_t len, + const unsigned char *x, size_t xlen, + const unsigned char *y, size_t ylen, int curve) +{ + switch (curve) { + case BR_EC_secp256r1: + return br_ec_p256_m15.muladd(A, B, len, + x, xlen, y, ylen, curve); + case BR_EC_curve25519: + return br_ec_c25519_m15.muladd(A, B, len, + x, xlen, y, ylen, curve); + default: + return br_ec_prime_i15.muladd(A, B, len, + x, xlen, y, ylen, curve); + } +} + +/* see bearssl_ec.h */ +const br_ec_impl br_ec_all_m15 PROGMEM = { + (uint32_t)0x23800000, + &api_generator, + &api_order, + &api_xoff, + &api_mul, + &api_mulgen, + &api_muladd +}; diff --git a/lib/bearssl-esp8266/src/ec/ec_c25519_i15.c b/lib/bearssl-esp8266/src/ec/ec_c25519_i15.c new file mode 100644 index 000000000..cea8091d8 --- /dev/null +++ b/lib/bearssl-esp8266/src/ec/ec_c25519_i15.c @@ -0,0 +1,398 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * Parameters for the field: + * - field modulus p = 2^255-19 + * - R^2 mod p (R = 2^(15k) for the smallest k such that R >= p) + */ + +static const uint16_t C255_P[] = { + 0x0110, + 0x7FED, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, + 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, + 0x7FFF +}; + +#define P0I 0x4A1B + +static const uint16_t C255_R2[] = { + 0x0110, + 0x0169, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000 +}; + +/* obsolete +#include +#include +static void +print_int_mont(const char *name, const uint16_t *x) +{ + uint16_t y[18]; + unsigned char tmp[32]; + size_t u; + + printf("%s = ", name); + memcpy(y, x, sizeof y); + br_i15_from_monty(y, C255_P, P0I); + br_i15_encode(tmp, sizeof tmp, y); + for (u = 0; u < sizeof tmp; u ++) { + printf("%02X", tmp[u]); + } + printf("\n"); +} +*/ + +static const uint16_t C255_A24[] = { + 0x0110, + 0x45D3, 0x0046, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000 +}; + +static const unsigned char GEN[] PROGMEM = { + 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static const unsigned char ORDER[] PROGMEM = { + 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; + +static const unsigned char * +api_generator(int curve, size_t *len) +{ + (void)curve; + *len = 32; + return GEN; +} + +static const unsigned char * +api_order(int curve, size_t *len) +{ + (void)curve; + *len = 32; + return ORDER; +} + +static size_t +api_xoff(int curve, size_t *len) +{ + (void)curve; + *len = 32; + return 0; +} + +static void +cswap(uint16_t *a, uint16_t *b, uint32_t ctl) +{ + int i; + + ctl = -ctl; + for (i = 0; i < 18; i ++) { + uint32_t aw, bw, tw; + + aw = a[i]; + bw = b[i]; + tw = ctl & (aw ^ bw); + a[i] = aw ^ tw; + b[i] = bw ^ tw; + } +} + +static void +c255_add(uint16_t *d, const uint16_t *a, const uint16_t *b) +{ + uint32_t ctl; + uint16_t t[18]; + + memcpy_P(t, a, sizeof t); + ctl = br_i15_add(t, b, 1); + ctl |= NOT(br_i15_sub(t, C255_P, 0)); + br_i15_sub(t, C255_P, ctl); + memcpy(d, t, sizeof t); +} + +static void +c255_sub(uint16_t *d, const uint16_t *a, const uint16_t *b) +{ + uint16_t t[18]; + + memcpy_P(t, a, sizeof t); + br_i15_add(t, C255_P, br_i15_sub(t, b, 1)); + memcpy(d, t, sizeof t); +} + +static void +c255_mul(uint16_t *d, const uint16_t *a, const uint16_t *b) +{ + uint16_t t[18]; + + br_i15_montymul(t, a, b, C255_P, P0I); + memcpy(d, t, sizeof t); +} + +static void +byteswap(unsigned char *G) +{ + int i; + + for (i = 0; i < 16; i ++) { + unsigned char t; + + t = G[i]; + G[i] = G[31 - i]; + G[31 - i] = t; + } +} + +static uint32_t +api_mul(unsigned char *G, size_t Glen, + const unsigned char *kb, size_t kblen, int curve) +{ +#define ILEN (18 * sizeof(uint16_t)) + + /* + * The a[] and b[] arrays have an extra word to allow for + * decoding without using br_i15_decode_reduce(). + */ + uint16_t x1[18], x2[18], x3[18], z2[18], z3[18]; + uint16_t a[19], aa[18], b[19], bb[18]; + uint16_t c[18], d[18], e[18], da[18], cb[18]; + unsigned char k[32]; + uint32_t swap; + int i; + + (void)curve; + + /* + * Points are encoded over exactly 32 bytes. Multipliers must fit + * in 32 bytes as well. + * RFC 7748 mandates that the high bit of the last point byte must + * be ignored/cleared. + */ + if (Glen != 32 || kblen > 32) { + return 0; + } + G[31] &= 0x7F; + + /* + * Byteswap the point encoding, because it uses little-endian, and + * the generic decoding routine uses big-endian. + */ + byteswap(G); + + /* + * Decode the point ('u' coordinate). This should be reduced + * modulo p, but we prefer to avoid the dependency on + * br_i15_decode_reduce(). Instead, we use br_i15_decode_mod() + * with a synthetic modulus of value 2^255 (this must work + * since G was truncated to 255 bits), then use a conditional + * subtraction. We use br_i15_decode_mod() and not + * br_i15_decode(), because the ec_prime_i15 implementation uses + * the former but not the latter. + * br_i15_decode_reduce(a, G, 32, C255_P); + */ + br_i15_zero(b, 0x111); + b[18] = 1; + br_i15_decode_mod(a, G, 32, b); + a[0] = 0x110; + br_i15_sub(a, C255_P, NOT(br_i15_sub(a, C255_P, 0))); + + /* + * Initialise variables x1, x2, z2, x3 and z3. We set all of them + * into Montgomery representation. + */ + br_i15_montymul(x1, a, C255_R2, C255_P, P0I); + memcpy(x3, x1, ILEN); + br_i15_zero(z2, C255_P[0]); + memcpy(x2, z2, ILEN); + x2[1] = 19; + memcpy(z3, x2, ILEN); + + memset(k, 0, (sizeof k) - kblen); + memcpy_P(k + (sizeof k) - kblen, kb, kblen); + k[31] &= 0xF8; + k[0] &= 0x7F; + k[0] |= 0x40; + + /* obsolete + print_int_mont("x1", x1); + */ + + swap = 0; + for (i = 254; i >= 0; i --) { + uint32_t kt; + + kt = (k[31 - (i >> 3)] >> (i & 7)) & 1; + swap ^= kt; + cswap(x2, x3, swap); + cswap(z2, z3, swap); + swap = kt; + + /* obsolete + print_int_mont("x2", x2); + print_int_mont("z2", z2); + print_int_mont("x3", x3); + print_int_mont("z3", z3); + */ + + c255_add(a, x2, z2); + c255_mul(aa, a, a); + c255_sub(b, x2, z2); + c255_mul(bb, b, b); + c255_sub(e, aa, bb); + c255_add(c, x3, z3); + c255_sub(d, x3, z3); + c255_mul(da, d, a); + c255_mul(cb, c, b); + + /* obsolete + print_int_mont("a ", a); + print_int_mont("aa", aa); + print_int_mont("b ", b); + print_int_mont("bb", bb); + print_int_mont("e ", e); + print_int_mont("c ", c); + print_int_mont("d ", d); + print_int_mont("da", da); + print_int_mont("cb", cb); + */ + + c255_add(x3, da, cb); + c255_mul(x3, x3, x3); + c255_sub(z3, da, cb); + c255_mul(z3, z3, z3); + c255_mul(z3, z3, x1); + c255_mul(x2, aa, bb); + c255_mul(z2, C255_A24, e); + c255_add(z2, z2, aa); + c255_mul(z2, e, z2); + + /* obsolete + print_int_mont("x2", x2); + print_int_mont("z2", z2); + print_int_mont("x3", x3); + print_int_mont("z3", z3); + */ + } + cswap(x2, x3, swap); + cswap(z2, z3, swap); + + /* + * Inverse z2 with a modular exponentiation. This is a simple + * square-and-multiply algorithm; we mutualise most non-squarings + * since the exponent contains almost only ones. + */ + memcpy(a, z2, ILEN); + for (i = 0; i < 15; i ++) { + c255_mul(a, a, a); + c255_mul(a, a, z2); + } + memcpy(b, a, ILEN); + for (i = 0; i < 14; i ++) { + int j; + + for (j = 0; j < 16; j ++) { + c255_mul(b, b, b); + } + c255_mul(b, b, a); + } + for (i = 14; i >= 0; i --) { + c255_mul(b, b, b); + if ((0xFFEB >> i) & 1) { + c255_mul(b, z2, b); + } + } + c255_mul(b, x2, b); + + /* + * To avoid a dependency on br_i15_from_monty(), we use a + * Montgomery multiplication with 1. + * memcpy(x2, b, ILEN); + * br_i15_from_monty(x2, C255_P, P0I); + */ + br_i15_zero(a, C255_P[0]); + a[1] = 1; + br_i15_montymul(x2, a, b, C255_P, P0I); + + br_i15_encode(G, 32, x2); + byteswap(G); + return 1; + +#undef ILEN +} + +static size_t +api_mulgen(unsigned char *R, + const unsigned char *x, size_t xlen, int curve) +{ + const unsigned char *G; + size_t Glen; + + G = api_generator(curve, &Glen); + memcpy(R, G, Glen); + api_mul(R, Glen, x, xlen, curve); + return Glen; +} + +static uint32_t +api_muladd(unsigned char *A, const unsigned char *B, size_t len, + const unsigned char *x, size_t xlen, + const unsigned char *y, size_t ylen, int curve) +{ + /* + * We don't implement this method, since it is used for ECDSA + * only, and there is no ECDSA over Curve25519 (which instead + * uses EdDSA). + */ + (void)A; + (void)B; + (void)len; + (void)x; + (void)xlen; + (void)y; + (void)ylen; + (void)curve; + return 0; +} + +/* see bearssl_ec.h */ +const br_ec_impl br_ec_c25519_i15 PROGMEM = { + (uint32_t)0x20000000, + &api_generator, + &api_order, + &api_xoff, + &api_mul, + &api_mulgen, + &api_muladd +}; diff --git a/lib/bearssl-esp8266/src/ec/ec_curve25519.c b/lib/bearssl-esp8266/src/ec/ec_curve25519.c new file mode 100644 index 000000000..a475035a1 --- /dev/null +++ b/lib/bearssl-esp8266/src/ec/ec_curve25519.c @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +static const unsigned char GEN[] PROGMEM = { + 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static const unsigned char ORDER[] PROGMEM = { + 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; + +/* see inner.h */ +const br_ec_curve_def br_curve25519 = { + BR_EC_curve25519, + ORDER, sizeof ORDER, + GEN, sizeof GEN +}; diff --git a/lib/bearssl-esp8266/src/ec/ec_default.c b/lib/bearssl-esp8266/src/ec/ec_default.c new file mode 100644 index 000000000..b29d68e33 --- /dev/null +++ b/lib/bearssl-esp8266/src/ec/ec_default.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_ec.h */ +const br_ec_impl * +br_ec_get_default(void) +{ +#if BR_LOMUL + return &br_ec_all_m15; +#else + return &br_ec_all_m31; +#endif +} diff --git a/lib/bearssl-esp8266/src/ec/ec_keygen.c b/lib/bearssl-esp8266/src/ec/ec_keygen.c new file mode 100644 index 000000000..2c9069d66 --- /dev/null +++ b/lib/bearssl-esp8266/src/ec/ec_keygen.c @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_ec.h */ +size_t +br_ec_keygen(const br_prng_class **rng_ctx, + const br_ec_impl *impl, br_ec_private_key *sk, + void *kbuf, int curve) +{ + const unsigned char *order; + unsigned char *buf; + size_t len; + unsigned mask; + + if (curve < 0 || curve >= 32 + || ((impl->supported_curves >> curve) & 1) == 0) + { + return 0; + } + order = impl->order(curve, &len); + while (len > 0 && *order == 0) { + order ++; + len --; + } + if (kbuf == NULL || len == 0) { + return len; + } + mask = order[0]; + mask |= (mask >> 1); + mask |= (mask >> 2); + mask |= (mask >> 4); + + /* + * We generate sequences of random bits of the right size, until + * the value is strictly lower than the curve order (we also + * check for all-zero values, which are invalid). + */ + buf = kbuf; + for (;;) { + size_t u; + unsigned cc, zz; + + (*rng_ctx)->generate(rng_ctx, buf, len); + buf[0] &= mask; + cc = 0; + u = len; + zz = 0; + while (u -- > 0) { + cc = ((unsigned)(buf[u] - order[u] - cc) >> 8) & 1; + zz |= buf[u]; + } + if (cc != 0 && zz != 0) { + break; + } + } + + if (sk != NULL) { + sk->curve = curve; + sk->x = buf; + sk->xlen = len; + } + return len; +} diff --git a/lib/bearssl-esp8266/src/ec/ec_p256_m15.c b/lib/bearssl-esp8266/src/ec/ec_p256_m15.c new file mode 100644 index 000000000..e5d76c6b4 --- /dev/null +++ b/lib/bearssl-esp8266/src/ec/ec_p256_m15.c @@ -0,0 +1,2117 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * If BR_NO_ARITH_SHIFT is undefined, or defined to 0, then we _assume_ + * that right-shifting a signed negative integer copies the sign bit + * (arithmetic right-shift). This is "implementation-defined behaviour", + * i.e. it is not undefined, but it may differ between compilers. Each + * compiler is supposed to document its behaviour in that respect. GCC + * explicitly defines that an arithmetic right shift is used. We expect + * all other compilers to do the same, because underlying CPU offer an + * arithmetic right shift opcode that could not be used otherwise. + */ +#if BR_NO_ARITH_SHIFT +#define ARSH(x, n) (((uint32_t)(x) >> (n)) \ + | ((-((uint32_t)(x) >> 31)) << (32 - (n)))) +#else +#define ARSH(x, n) ((*(int32_t *)&(x)) >> (n)) +#endif + +/* + * Convert an integer from unsigned big-endian encoding to a sequence of + * 13-bit words in little-endian order. The final "partial" word is + * returned. + */ +static uint32_t +be8_to_le13(uint32_t *dst, const unsigned char *src, size_t len) +{ + uint32_t acc; + int acc_len; + + acc = 0; + acc_len = 0; + while (len -- > 0) { + acc |= (uint32_t)src[len] << acc_len; + acc_len += 8; + if (acc_len >= 13) { + *dst ++ = acc & 0x1FFF; + acc >>= 13; + acc_len -= 13; + } + } + return acc; +} + +/* + * Convert an integer (13-bit words, little-endian) to unsigned + * big-endian encoding. The total encoding length is provided; all + * the destination bytes will be filled. + */ +static void +le13_to_be8(unsigned char *dst, size_t len, const uint32_t *src) +{ + uint32_t acc; + int acc_len; + + acc = 0; + acc_len = 0; + while (len -- > 0) { + if (acc_len < 8) { + acc |= (*src ++) << acc_len; + acc_len += 13; + } + dst[len] = (unsigned char)acc; + acc >>= 8; + acc_len -= 8; + } +} + +/* + * Normalise an array of words to a strict 13 bits per word. Returned + * value is the resulting carry. The source (w) and destination (d) + * arrays may be identical, but shall not overlap partially. + */ +static inline uint32_t +norm13(uint32_t *d, const uint32_t *w, size_t len) +{ + size_t u; + uint32_t cc; + + cc = 0; + for (u = 0; u < len; u ++) { + int32_t z; + + z = w[u] + cc; + d[u] = z & 0x1FFF; + cc = ARSH(z, 13); + } + return cc; +} + +/* + * mul20() multiplies two 260-bit integers together. Each word must fit + * on 13 bits; source operands use 20 words, destination operand + * receives 40 words. All overlaps allowed. + * + * square20() computes the square of a 260-bit integer. Each word must + * fit on 13 bits; source operand uses 20 words, destination operand + * receives 40 words. All overlaps allowed. + */ + +#if BR_SLOW_MUL15 + +static void +mul20(uint32_t *d, const uint32_t *a, const uint32_t *b) +{ + /* + * Two-level Karatsuba: turns a 20x20 multiplication into + * nine 5x5 multiplications. We use 13-bit words but do not + * propagate carries immediately, so words may expand: + * + * - First Karatsuba decomposition turns the 20x20 mul on + * 13-bit words into three 10x10 muls, two on 13-bit words + * and one on 14-bit words. + * + * - Second Karatsuba decomposition further splits these into: + * + * * four 5x5 muls on 13-bit words + * * four 5x5 muls on 14-bit words + * * one 5x5 mul on 15-bit words + * + * Highest word value is 8191, 16382 or 32764, for 13-bit, 14-bit + * or 15-bit words, respectively. + */ + uint32_t u[45], v[45], w[90]; + uint32_t cc; + int i; + +#define ZADD(dw, d_off, s1w, s1_off, s2w, s2_off) do { \ + (dw)[5 * (d_off) + 0] = (s1w)[5 * (s1_off) + 0] \ + + (s2w)[5 * (s2_off) + 0]; \ + (dw)[5 * (d_off) + 1] = (s1w)[5 * (s1_off) + 1] \ + + (s2w)[5 * (s2_off) + 1]; \ + (dw)[5 * (d_off) + 2] = (s1w)[5 * (s1_off) + 2] \ + + (s2w)[5 * (s2_off) + 2]; \ + (dw)[5 * (d_off) + 3] = (s1w)[5 * (s1_off) + 3] \ + + (s2w)[5 * (s2_off) + 3]; \ + (dw)[5 * (d_off) + 4] = (s1w)[5 * (s1_off) + 4] \ + + (s2w)[5 * (s2_off) + 4]; \ + } while (0) + +#define ZADDT(dw, d_off, sw, s_off) do { \ + (dw)[5 * (d_off) + 0] += (sw)[5 * (s_off) + 0]; \ + (dw)[5 * (d_off) + 1] += (sw)[5 * (s_off) + 1]; \ + (dw)[5 * (d_off) + 2] += (sw)[5 * (s_off) + 2]; \ + (dw)[5 * (d_off) + 3] += (sw)[5 * (s_off) + 3]; \ + (dw)[5 * (d_off) + 4] += (sw)[5 * (s_off) + 4]; \ + } while (0) + +#define ZSUB2F(dw, d_off, s1w, s1_off, s2w, s2_off) do { \ + (dw)[5 * (d_off) + 0] -= (s1w)[5 * (s1_off) + 0] \ + + (s2w)[5 * (s2_off) + 0]; \ + (dw)[5 * (d_off) + 1] -= (s1w)[5 * (s1_off) + 1] \ + + (s2w)[5 * (s2_off) + 1]; \ + (dw)[5 * (d_off) + 2] -= (s1w)[5 * (s1_off) + 2] \ + + (s2w)[5 * (s2_off) + 2]; \ + (dw)[5 * (d_off) + 3] -= (s1w)[5 * (s1_off) + 3] \ + + (s2w)[5 * (s2_off) + 3]; \ + (dw)[5 * (d_off) + 4] -= (s1w)[5 * (s1_off) + 4] \ + + (s2w)[5 * (s2_off) + 4]; \ + } while (0) + +#define CPR1(w, cprcc) do { \ + uint32_t cprz = (w) + cprcc; \ + (w) = cprz & 0x1FFF; \ + cprcc = cprz >> 13; \ + } while (0) + +#define CPR(dw, d_off) do { \ + uint32_t cprcc; \ + cprcc = 0; \ + CPR1((dw)[(d_off) + 0], cprcc); \ + CPR1((dw)[(d_off) + 1], cprcc); \ + CPR1((dw)[(d_off) + 2], cprcc); \ + CPR1((dw)[(d_off) + 3], cprcc); \ + CPR1((dw)[(d_off) + 4], cprcc); \ + CPR1((dw)[(d_off) + 5], cprcc); \ + CPR1((dw)[(d_off) + 6], cprcc); \ + CPR1((dw)[(d_off) + 7], cprcc); \ + CPR1((dw)[(d_off) + 8], cprcc); \ + (dw)[(d_off) + 9] = cprcc; \ + } while (0) + + memcpy(u, a, 20 * sizeof *a); + ZADD(u, 4, a, 0, a, 1); + ZADD(u, 5, a, 2, a, 3); + ZADD(u, 6, a, 0, a, 2); + ZADD(u, 7, a, 1, a, 3); + ZADD(u, 8, u, 6, u, 7); + + memcpy(v, b, 20 * sizeof *b); + ZADD(v, 4, b, 0, b, 1); + ZADD(v, 5, b, 2, b, 3); + ZADD(v, 6, b, 0, b, 2); + ZADD(v, 7, b, 1, b, 3); + ZADD(v, 8, v, 6, v, 7); + + /* + * Do the eight first 8x8 muls. Source words are at most 16382 + * each, so we can add product results together "as is" in 32-bit + * words. + */ + for (i = 0; i < 40; i += 5) { + w[(i << 1) + 0] = MUL15(u[i + 0], v[i + 0]); + w[(i << 1) + 1] = MUL15(u[i + 0], v[i + 1]) + + MUL15(u[i + 1], v[i + 0]); + w[(i << 1) + 2] = MUL15(u[i + 0], v[i + 2]) + + MUL15(u[i + 1], v[i + 1]) + + MUL15(u[i + 2], v[i + 0]); + w[(i << 1) + 3] = MUL15(u[i + 0], v[i + 3]) + + MUL15(u[i + 1], v[i + 2]) + + MUL15(u[i + 2], v[i + 1]) + + MUL15(u[i + 3], v[i + 0]); + w[(i << 1) + 4] = MUL15(u[i + 0], v[i + 4]) + + MUL15(u[i + 1], v[i + 3]) + + MUL15(u[i + 2], v[i + 2]) + + MUL15(u[i + 3], v[i + 1]) + + MUL15(u[i + 4], v[i + 0]); + w[(i << 1) + 5] = MUL15(u[i + 1], v[i + 4]) + + MUL15(u[i + 2], v[i + 3]) + + MUL15(u[i + 3], v[i + 2]) + + MUL15(u[i + 4], v[i + 1]); + w[(i << 1) + 6] = MUL15(u[i + 2], v[i + 4]) + + MUL15(u[i + 3], v[i + 3]) + + MUL15(u[i + 4], v[i + 2]); + w[(i << 1) + 7] = MUL15(u[i + 3], v[i + 4]) + + MUL15(u[i + 4], v[i + 3]); + w[(i << 1) + 8] = MUL15(u[i + 4], v[i + 4]); + w[(i << 1) + 9] = 0; + } + + /* + * For the 9th multiplication, source words are up to 32764, + * so we must do some carry propagation. If we add up to + * 4 products and the carry is no more than 524224, then the + * result fits in 32 bits, and the next carry will be no more + * than 524224 (because 4*(32764^2)+524224 < 8192*524225). + * + * We thus just skip one of the products in the middle word, + * then do a carry propagation (this reduces words to 13 bits + * each, except possibly the last, which may use up to 17 bits + * or so), then add the missing product. + */ + w[80 + 0] = MUL15(u[40 + 0], v[40 + 0]); + w[80 + 1] = MUL15(u[40 + 0], v[40 + 1]) + + MUL15(u[40 + 1], v[40 + 0]); + w[80 + 2] = MUL15(u[40 + 0], v[40 + 2]) + + MUL15(u[40 + 1], v[40 + 1]) + + MUL15(u[40 + 2], v[40 + 0]); + w[80 + 3] = MUL15(u[40 + 0], v[40 + 3]) + + MUL15(u[40 + 1], v[40 + 2]) + + MUL15(u[40 + 2], v[40 + 1]) + + MUL15(u[40 + 3], v[40 + 0]); + w[80 + 4] = MUL15(u[40 + 0], v[40 + 4]) + + MUL15(u[40 + 1], v[40 + 3]) + + MUL15(u[40 + 2], v[40 + 2]) + + MUL15(u[40 + 3], v[40 + 1]); + /* + MUL15(u[40 + 4], v[40 + 0]) */ + w[80 + 5] = MUL15(u[40 + 1], v[40 + 4]) + + MUL15(u[40 + 2], v[40 + 3]) + + MUL15(u[40 + 3], v[40 + 2]) + + MUL15(u[40 + 4], v[40 + 1]); + w[80 + 6] = MUL15(u[40 + 2], v[40 + 4]) + + MUL15(u[40 + 3], v[40 + 3]) + + MUL15(u[40 + 4], v[40 + 2]); + w[80 + 7] = MUL15(u[40 + 3], v[40 + 4]) + + MUL15(u[40 + 4], v[40 + 3]); + w[80 + 8] = MUL15(u[40 + 4], v[40 + 4]); + + CPR(w, 80); + + w[80 + 4] += MUL15(u[40 + 4], v[40 + 0]); + + /* + * The products on 14-bit words in slots 6 and 7 yield values + * up to 5*(16382^2) each, and we need to subtract two such + * values from the higher word. We need the subtraction to fit + * in a _signed_ 32-bit integer, i.e. 31 bits + a sign bit. + * However, 10*(16382^2) does not fit. So we must perform a + * bit of reduction here. + */ + CPR(w, 60); + CPR(w, 70); + + /* + * Recompose results. + */ + + /* 0..1*0..1 into 0..3 */ + ZSUB2F(w, 8, w, 0, w, 2); + ZSUB2F(w, 9, w, 1, w, 3); + ZADDT(w, 1, w, 8); + ZADDT(w, 2, w, 9); + + /* 2..3*2..3 into 4..7 */ + ZSUB2F(w, 10, w, 4, w, 6); + ZSUB2F(w, 11, w, 5, w, 7); + ZADDT(w, 5, w, 10); + ZADDT(w, 6, w, 11); + + /* (0..1+2..3)*(0..1+2..3) into 12..15 */ + ZSUB2F(w, 16, w, 12, w, 14); + ZSUB2F(w, 17, w, 13, w, 15); + ZADDT(w, 13, w, 16); + ZADDT(w, 14, w, 17); + + /* first-level recomposition */ + ZSUB2F(w, 12, w, 0, w, 4); + ZSUB2F(w, 13, w, 1, w, 5); + ZSUB2F(w, 14, w, 2, w, 6); + ZSUB2F(w, 15, w, 3, w, 7); + ZADDT(w, 2, w, 12); + ZADDT(w, 3, w, 13); + ZADDT(w, 4, w, 14); + ZADDT(w, 5, w, 15); + + /* + * Perform carry propagation to bring all words down to 13 bits. + */ + cc = norm13(d, w, 40); + d[39] += (cc << 13); + +#undef ZADD +#undef ZADDT +#undef ZSUB2F +#undef CPR1 +#undef CPR +} + +static inline void +square20(uint32_t *d, const uint32_t *a) +{ + mul20(d, a, a); +} + +#else +extern void mul20(uint32_t *d, const uint32_t *a, const uint32_t *b); +extern void square20(uint32_t *d, const uint32_t *a); + +#if 0 +static void +mul20(uint32_t *d, const uint32_t *a, const uint32_t *b) +{ + uint32_t t[39]; + + t[ 0] = MUL15(a[ 0], b[ 0]); + t[ 1] = MUL15(a[ 0], b[ 1]) + + MUL15(a[ 1], b[ 0]); + t[ 2] = MUL15(a[ 0], b[ 2]) + + MUL15(a[ 1], b[ 1]) + + MUL15(a[ 2], b[ 0]); + t[ 3] = MUL15(a[ 0], b[ 3]) + + MUL15(a[ 1], b[ 2]) + + MUL15(a[ 2], b[ 1]) + + MUL15(a[ 3], b[ 0]); + t[ 4] = MUL15(a[ 0], b[ 4]) + + MUL15(a[ 1], b[ 3]) + + MUL15(a[ 2], b[ 2]) + + MUL15(a[ 3], b[ 1]) + + MUL15(a[ 4], b[ 0]); + t[ 5] = MUL15(a[ 0], b[ 5]) + + MUL15(a[ 1], b[ 4]) + + MUL15(a[ 2], b[ 3]) + + MUL15(a[ 3], b[ 2]) + + MUL15(a[ 4], b[ 1]) + + MUL15(a[ 5], b[ 0]); + t[ 6] = MUL15(a[ 0], b[ 6]) + + MUL15(a[ 1], b[ 5]) + + MUL15(a[ 2], b[ 4]) + + MUL15(a[ 3], b[ 3]) + + MUL15(a[ 4], b[ 2]) + + MUL15(a[ 5], b[ 1]) + + MUL15(a[ 6], b[ 0]); + t[ 7] = MUL15(a[ 0], b[ 7]) + + MUL15(a[ 1], b[ 6]) + + MUL15(a[ 2], b[ 5]) + + MUL15(a[ 3], b[ 4]) + + MUL15(a[ 4], b[ 3]) + + MUL15(a[ 5], b[ 2]) + + MUL15(a[ 6], b[ 1]) + + MUL15(a[ 7], b[ 0]); + t[ 8] = MUL15(a[ 0], b[ 8]) + + MUL15(a[ 1], b[ 7]) + + MUL15(a[ 2], b[ 6]) + + MUL15(a[ 3], b[ 5]) + + MUL15(a[ 4], b[ 4]) + + MUL15(a[ 5], b[ 3]) + + MUL15(a[ 6], b[ 2]) + + MUL15(a[ 7], b[ 1]) + + MUL15(a[ 8], b[ 0]); + t[ 9] = MUL15(a[ 0], b[ 9]) + + MUL15(a[ 1], b[ 8]) + + MUL15(a[ 2], b[ 7]) + + MUL15(a[ 3], b[ 6]) + + MUL15(a[ 4], b[ 5]) + + MUL15(a[ 5], b[ 4]) + + MUL15(a[ 6], b[ 3]) + + MUL15(a[ 7], b[ 2]) + + MUL15(a[ 8], b[ 1]) + + MUL15(a[ 9], b[ 0]); + t[10] = MUL15(a[ 0], b[10]) + + MUL15(a[ 1], b[ 9]) + + MUL15(a[ 2], b[ 8]) + + MUL15(a[ 3], b[ 7]) + + MUL15(a[ 4], b[ 6]) + + MUL15(a[ 5], b[ 5]) + + MUL15(a[ 6], b[ 4]) + + MUL15(a[ 7], b[ 3]) + + MUL15(a[ 8], b[ 2]) + + MUL15(a[ 9], b[ 1]) + + MUL15(a[10], b[ 0]); + t[11] = MUL15(a[ 0], b[11]) + + MUL15(a[ 1], b[10]) + + MUL15(a[ 2], b[ 9]) + + MUL15(a[ 3], b[ 8]) + + MUL15(a[ 4], b[ 7]) + + MUL15(a[ 5], b[ 6]) + + MUL15(a[ 6], b[ 5]) + + MUL15(a[ 7], b[ 4]) + + MUL15(a[ 8], b[ 3]) + + MUL15(a[ 9], b[ 2]) + + MUL15(a[10], b[ 1]) + + MUL15(a[11], b[ 0]); + t[12] = MUL15(a[ 0], b[12]) + + MUL15(a[ 1], b[11]) + + MUL15(a[ 2], b[10]) + + MUL15(a[ 3], b[ 9]) + + MUL15(a[ 4], b[ 8]) + + MUL15(a[ 5], b[ 7]) + + MUL15(a[ 6], b[ 6]) + + MUL15(a[ 7], b[ 5]) + + MUL15(a[ 8], b[ 4]) + + MUL15(a[ 9], b[ 3]) + + MUL15(a[10], b[ 2]) + + MUL15(a[11], b[ 1]) + + MUL15(a[12], b[ 0]); + t[13] = MUL15(a[ 0], b[13]) + + MUL15(a[ 1], b[12]) + + MUL15(a[ 2], b[11]) + + MUL15(a[ 3], b[10]) + + MUL15(a[ 4], b[ 9]) + + MUL15(a[ 5], b[ 8]) + + MUL15(a[ 6], b[ 7]) + + MUL15(a[ 7], b[ 6]) + + MUL15(a[ 8], b[ 5]) + + MUL15(a[ 9], b[ 4]) + + MUL15(a[10], b[ 3]) + + MUL15(a[11], b[ 2]) + + MUL15(a[12], b[ 1]) + + MUL15(a[13], b[ 0]); + t[14] = MUL15(a[ 0], b[14]) + + MUL15(a[ 1], b[13]) + + MUL15(a[ 2], b[12]) + + MUL15(a[ 3], b[11]) + + MUL15(a[ 4], b[10]) + + MUL15(a[ 5], b[ 9]) + + MUL15(a[ 6], b[ 8]) + + MUL15(a[ 7], b[ 7]) + + MUL15(a[ 8], b[ 6]) + + MUL15(a[ 9], b[ 5]) + + MUL15(a[10], b[ 4]) + + MUL15(a[11], b[ 3]) + + MUL15(a[12], b[ 2]) + + MUL15(a[13], b[ 1]) + + MUL15(a[14], b[ 0]); + t[15] = MUL15(a[ 0], b[15]) + + MUL15(a[ 1], b[14]) + + MUL15(a[ 2], b[13]) + + MUL15(a[ 3], b[12]) + + MUL15(a[ 4], b[11]) + + MUL15(a[ 5], b[10]) + + MUL15(a[ 6], b[ 9]) + + MUL15(a[ 7], b[ 8]) + + MUL15(a[ 8], b[ 7]) + + MUL15(a[ 9], b[ 6]) + + MUL15(a[10], b[ 5]) + + MUL15(a[11], b[ 4]) + + MUL15(a[12], b[ 3]) + + MUL15(a[13], b[ 2]) + + MUL15(a[14], b[ 1]) + + MUL15(a[15], b[ 0]); + t[16] = MUL15(a[ 0], b[16]) + + MUL15(a[ 1], b[15]) + + MUL15(a[ 2], b[14]) + + MUL15(a[ 3], b[13]) + + MUL15(a[ 4], b[12]) + + MUL15(a[ 5], b[11]) + + MUL15(a[ 6], b[10]) + + MUL15(a[ 7], b[ 9]) + + MUL15(a[ 8], b[ 8]) + + MUL15(a[ 9], b[ 7]) + + MUL15(a[10], b[ 6]) + + MUL15(a[11], b[ 5]) + + MUL15(a[12], b[ 4]) + + MUL15(a[13], b[ 3]) + + MUL15(a[14], b[ 2]) + + MUL15(a[15], b[ 1]) + + MUL15(a[16], b[ 0]); + t[17] = MUL15(a[ 0], b[17]) + + MUL15(a[ 1], b[16]) + + MUL15(a[ 2], b[15]) + + MUL15(a[ 3], b[14]) + + MUL15(a[ 4], b[13]) + + MUL15(a[ 5], b[12]) + + MUL15(a[ 6], b[11]) + + MUL15(a[ 7], b[10]) + + MUL15(a[ 8], b[ 9]) + + MUL15(a[ 9], b[ 8]) + + MUL15(a[10], b[ 7]) + + MUL15(a[11], b[ 6]) + + MUL15(a[12], b[ 5]) + + MUL15(a[13], b[ 4]) + + MUL15(a[14], b[ 3]) + + MUL15(a[15], b[ 2]) + + MUL15(a[16], b[ 1]) + + MUL15(a[17], b[ 0]); + t[18] = MUL15(a[ 0], b[18]) + + MUL15(a[ 1], b[17]) + + MUL15(a[ 2], b[16]) + + MUL15(a[ 3], b[15]) + + MUL15(a[ 4], b[14]) + + MUL15(a[ 5], b[13]) + + MUL15(a[ 6], b[12]) + + MUL15(a[ 7], b[11]) + + MUL15(a[ 8], b[10]) + + MUL15(a[ 9], b[ 9]) + + MUL15(a[10], b[ 8]) + + MUL15(a[11], b[ 7]) + + MUL15(a[12], b[ 6]) + + MUL15(a[13], b[ 5]) + + MUL15(a[14], b[ 4]) + + MUL15(a[15], b[ 3]) + + MUL15(a[16], b[ 2]) + + MUL15(a[17], b[ 1]) + + MUL15(a[18], b[ 0]); + t[19] = MUL15(a[ 0], b[19]) + + MUL15(a[ 1], b[18]) + + MUL15(a[ 2], b[17]) + + MUL15(a[ 3], b[16]) + + MUL15(a[ 4], b[15]) + + MUL15(a[ 5], b[14]) + + MUL15(a[ 6], b[13]) + + MUL15(a[ 7], b[12]) + + MUL15(a[ 8], b[11]) + + MUL15(a[ 9], b[10]) + + MUL15(a[10], b[ 9]) + + MUL15(a[11], b[ 8]) + + MUL15(a[12], b[ 7]) + + MUL15(a[13], b[ 6]) + + MUL15(a[14], b[ 5]) + + MUL15(a[15], b[ 4]) + + MUL15(a[16], b[ 3]) + + MUL15(a[17], b[ 2]) + + MUL15(a[18], b[ 1]) + + MUL15(a[19], b[ 0]); + t[20] = MUL15(a[ 1], b[19]) + + MUL15(a[ 2], b[18]) + + MUL15(a[ 3], b[17]) + + MUL15(a[ 4], b[16]) + + MUL15(a[ 5], b[15]) + + MUL15(a[ 6], b[14]) + + MUL15(a[ 7], b[13]) + + MUL15(a[ 8], b[12]) + + MUL15(a[ 9], b[11]) + + MUL15(a[10], b[10]) + + MUL15(a[11], b[ 9]) + + MUL15(a[12], b[ 8]) + + MUL15(a[13], b[ 7]) + + MUL15(a[14], b[ 6]) + + MUL15(a[15], b[ 5]) + + MUL15(a[16], b[ 4]) + + MUL15(a[17], b[ 3]) + + MUL15(a[18], b[ 2]) + + MUL15(a[19], b[ 1]); + t[21] = MUL15(a[ 2], b[19]) + + MUL15(a[ 3], b[18]) + + MUL15(a[ 4], b[17]) + + MUL15(a[ 5], b[16]) + + MUL15(a[ 6], b[15]) + + MUL15(a[ 7], b[14]) + + MUL15(a[ 8], b[13]) + + MUL15(a[ 9], b[12]) + + MUL15(a[10], b[11]) + + MUL15(a[11], b[10]) + + MUL15(a[12], b[ 9]) + + MUL15(a[13], b[ 8]) + + MUL15(a[14], b[ 7]) + + MUL15(a[15], b[ 6]) + + MUL15(a[16], b[ 5]) + + MUL15(a[17], b[ 4]) + + MUL15(a[18], b[ 3]) + + MUL15(a[19], b[ 2]); + t[22] = MUL15(a[ 3], b[19]) + + MUL15(a[ 4], b[18]) + + MUL15(a[ 5], b[17]) + + MUL15(a[ 6], b[16]) + + MUL15(a[ 7], b[15]) + + MUL15(a[ 8], b[14]) + + MUL15(a[ 9], b[13]) + + MUL15(a[10], b[12]) + + MUL15(a[11], b[11]) + + MUL15(a[12], b[10]) + + MUL15(a[13], b[ 9]) + + MUL15(a[14], b[ 8]) + + MUL15(a[15], b[ 7]) + + MUL15(a[16], b[ 6]) + + MUL15(a[17], b[ 5]) + + MUL15(a[18], b[ 4]) + + MUL15(a[19], b[ 3]); + t[23] = MUL15(a[ 4], b[19]) + + MUL15(a[ 5], b[18]) + + MUL15(a[ 6], b[17]) + + MUL15(a[ 7], b[16]) + + MUL15(a[ 8], b[15]) + + MUL15(a[ 9], b[14]) + + MUL15(a[10], b[13]) + + MUL15(a[11], b[12]) + + MUL15(a[12], b[11]) + + MUL15(a[13], b[10]) + + MUL15(a[14], b[ 9]) + + MUL15(a[15], b[ 8]) + + MUL15(a[16], b[ 7]) + + MUL15(a[17], b[ 6]) + + MUL15(a[18], b[ 5]) + + MUL15(a[19], b[ 4]); + t[24] = MUL15(a[ 5], b[19]) + + MUL15(a[ 6], b[18]) + + MUL15(a[ 7], b[17]) + + MUL15(a[ 8], b[16]) + + MUL15(a[ 9], b[15]) + + MUL15(a[10], b[14]) + + MUL15(a[11], b[13]) + + MUL15(a[12], b[12]) + + MUL15(a[13], b[11]) + + MUL15(a[14], b[10]) + + MUL15(a[15], b[ 9]) + + MUL15(a[16], b[ 8]) + + MUL15(a[17], b[ 7]) + + MUL15(a[18], b[ 6]) + + MUL15(a[19], b[ 5]); + t[25] = MUL15(a[ 6], b[19]) + + MUL15(a[ 7], b[18]) + + MUL15(a[ 8], b[17]) + + MUL15(a[ 9], b[16]) + + MUL15(a[10], b[15]) + + MUL15(a[11], b[14]) + + MUL15(a[12], b[13]) + + MUL15(a[13], b[12]) + + MUL15(a[14], b[11]) + + MUL15(a[15], b[10]) + + MUL15(a[16], b[ 9]) + + MUL15(a[17], b[ 8]) + + MUL15(a[18], b[ 7]) + + MUL15(a[19], b[ 6]); + t[26] = MUL15(a[ 7], b[19]) + + MUL15(a[ 8], b[18]) + + MUL15(a[ 9], b[17]) + + MUL15(a[10], b[16]) + + MUL15(a[11], b[15]) + + MUL15(a[12], b[14]) + + MUL15(a[13], b[13]) + + MUL15(a[14], b[12]) + + MUL15(a[15], b[11]) + + MUL15(a[16], b[10]) + + MUL15(a[17], b[ 9]) + + MUL15(a[18], b[ 8]) + + MUL15(a[19], b[ 7]); + t[27] = MUL15(a[ 8], b[19]) + + MUL15(a[ 9], b[18]) + + MUL15(a[10], b[17]) + + MUL15(a[11], b[16]) + + MUL15(a[12], b[15]) + + MUL15(a[13], b[14]) + + MUL15(a[14], b[13]) + + MUL15(a[15], b[12]) + + MUL15(a[16], b[11]) + + MUL15(a[17], b[10]) + + MUL15(a[18], b[ 9]) + + MUL15(a[19], b[ 8]); + t[28] = MUL15(a[ 9], b[19]) + + MUL15(a[10], b[18]) + + MUL15(a[11], b[17]) + + MUL15(a[12], b[16]) + + MUL15(a[13], b[15]) + + MUL15(a[14], b[14]) + + MUL15(a[15], b[13]) + + MUL15(a[16], b[12]) + + MUL15(a[17], b[11]) + + MUL15(a[18], b[10]) + + MUL15(a[19], b[ 9]); + t[29] = MUL15(a[10], b[19]) + + MUL15(a[11], b[18]) + + MUL15(a[12], b[17]) + + MUL15(a[13], b[16]) + + MUL15(a[14], b[15]) + + MUL15(a[15], b[14]) + + MUL15(a[16], b[13]) + + MUL15(a[17], b[12]) + + MUL15(a[18], b[11]) + + MUL15(a[19], b[10]); + t[30] = MUL15(a[11], b[19]) + + MUL15(a[12], b[18]) + + MUL15(a[13], b[17]) + + MUL15(a[14], b[16]) + + MUL15(a[15], b[15]) + + MUL15(a[16], b[14]) + + MUL15(a[17], b[13]) + + MUL15(a[18], b[12]) + + MUL15(a[19], b[11]); + t[31] = MUL15(a[12], b[19]) + + MUL15(a[13], b[18]) + + MUL15(a[14], b[17]) + + MUL15(a[15], b[16]) + + MUL15(a[16], b[15]) + + MUL15(a[17], b[14]) + + MUL15(a[18], b[13]) + + MUL15(a[19], b[12]); + t[32] = MUL15(a[13], b[19]) + + MUL15(a[14], b[18]) + + MUL15(a[15], b[17]) + + MUL15(a[16], b[16]) + + MUL15(a[17], b[15]) + + MUL15(a[18], b[14]) + + MUL15(a[19], b[13]); + t[33] = MUL15(a[14], b[19]) + + MUL15(a[15], b[18]) + + MUL15(a[16], b[17]) + + MUL15(a[17], b[16]) + + MUL15(a[18], b[15]) + + MUL15(a[19], b[14]); + t[34] = MUL15(a[15], b[19]) + + MUL15(a[16], b[18]) + + MUL15(a[17], b[17]) + + MUL15(a[18], b[16]) + + MUL15(a[19], b[15]); + t[35] = MUL15(a[16], b[19]) + + MUL15(a[17], b[18]) + + MUL15(a[18], b[17]) + + MUL15(a[19], b[16]); + t[36] = MUL15(a[17], b[19]) + + MUL15(a[18], b[18]) + + MUL15(a[19], b[17]); + t[37] = MUL15(a[18], b[19]) + + MUL15(a[19], b[18]); + t[38] = MUL15(a[19], b[19]); + d[39] = norm13(d, t, 39); +} + +static void +square20(uint32_t *d, const uint32_t *a) +{ + uint32_t t[39]; + + t[ 0] = MUL15(a[ 0], a[ 0]); + t[ 1] = ((MUL15(a[ 0], a[ 1])) << 1); + t[ 2] = MUL15(a[ 1], a[ 1]) + + ((MUL15(a[ 0], a[ 2])) << 1); + t[ 3] = ((MUL15(a[ 0], a[ 3]) + + MUL15(a[ 1], a[ 2])) << 1); + t[ 4] = MUL15(a[ 2], a[ 2]) + + ((MUL15(a[ 0], a[ 4]) + + MUL15(a[ 1], a[ 3])) << 1); + t[ 5] = ((MUL15(a[ 0], a[ 5]) + + MUL15(a[ 1], a[ 4]) + + MUL15(a[ 2], a[ 3])) << 1); + t[ 6] = MUL15(a[ 3], a[ 3]) + + ((MUL15(a[ 0], a[ 6]) + + MUL15(a[ 1], a[ 5]) + + MUL15(a[ 2], a[ 4])) << 1); + t[ 7] = ((MUL15(a[ 0], a[ 7]) + + MUL15(a[ 1], a[ 6]) + + MUL15(a[ 2], a[ 5]) + + MUL15(a[ 3], a[ 4])) << 1); + t[ 8] = MUL15(a[ 4], a[ 4]) + + ((MUL15(a[ 0], a[ 8]) + + MUL15(a[ 1], a[ 7]) + + MUL15(a[ 2], a[ 6]) + + MUL15(a[ 3], a[ 5])) << 1); + t[ 9] = ((MUL15(a[ 0], a[ 9]) + + MUL15(a[ 1], a[ 8]) + + MUL15(a[ 2], a[ 7]) + + MUL15(a[ 3], a[ 6]) + + MUL15(a[ 4], a[ 5])) << 1); + t[10] = MUL15(a[ 5], a[ 5]) + + ((MUL15(a[ 0], a[10]) + + MUL15(a[ 1], a[ 9]) + + MUL15(a[ 2], a[ 8]) + + MUL15(a[ 3], a[ 7]) + + MUL15(a[ 4], a[ 6])) << 1); + t[11] = ((MUL15(a[ 0], a[11]) + + MUL15(a[ 1], a[10]) + + MUL15(a[ 2], a[ 9]) + + MUL15(a[ 3], a[ 8]) + + MUL15(a[ 4], a[ 7]) + + MUL15(a[ 5], a[ 6])) << 1); + t[12] = MUL15(a[ 6], a[ 6]) + + ((MUL15(a[ 0], a[12]) + + MUL15(a[ 1], a[11]) + + MUL15(a[ 2], a[10]) + + MUL15(a[ 3], a[ 9]) + + MUL15(a[ 4], a[ 8]) + + MUL15(a[ 5], a[ 7])) << 1); + t[13] = ((MUL15(a[ 0], a[13]) + + MUL15(a[ 1], a[12]) + + MUL15(a[ 2], a[11]) + + MUL15(a[ 3], a[10]) + + MUL15(a[ 4], a[ 9]) + + MUL15(a[ 5], a[ 8]) + + MUL15(a[ 6], a[ 7])) << 1); + t[14] = MUL15(a[ 7], a[ 7]) + + ((MUL15(a[ 0], a[14]) + + MUL15(a[ 1], a[13]) + + MUL15(a[ 2], a[12]) + + MUL15(a[ 3], a[11]) + + MUL15(a[ 4], a[10]) + + MUL15(a[ 5], a[ 9]) + + MUL15(a[ 6], a[ 8])) << 1); + t[15] = ((MUL15(a[ 0], a[15]) + + MUL15(a[ 1], a[14]) + + MUL15(a[ 2], a[13]) + + MUL15(a[ 3], a[12]) + + MUL15(a[ 4], a[11]) + + MUL15(a[ 5], a[10]) + + MUL15(a[ 6], a[ 9]) + + MUL15(a[ 7], a[ 8])) << 1); + t[16] = MUL15(a[ 8], a[ 8]) + + ((MUL15(a[ 0], a[16]) + + MUL15(a[ 1], a[15]) + + MUL15(a[ 2], a[14]) + + MUL15(a[ 3], a[13]) + + MUL15(a[ 4], a[12]) + + MUL15(a[ 5], a[11]) + + MUL15(a[ 6], a[10]) + + MUL15(a[ 7], a[ 9])) << 1); + t[17] = ((MUL15(a[ 0], a[17]) + + MUL15(a[ 1], a[16]) + + MUL15(a[ 2], a[15]) + + MUL15(a[ 3], a[14]) + + MUL15(a[ 4], a[13]) + + MUL15(a[ 5], a[12]) + + MUL15(a[ 6], a[11]) + + MUL15(a[ 7], a[10]) + + MUL15(a[ 8], a[ 9])) << 1); + t[18] = MUL15(a[ 9], a[ 9]) + + ((MUL15(a[ 0], a[18]) + + MUL15(a[ 1], a[17]) + + MUL15(a[ 2], a[16]) + + MUL15(a[ 3], a[15]) + + MUL15(a[ 4], a[14]) + + MUL15(a[ 5], a[13]) + + MUL15(a[ 6], a[12]) + + MUL15(a[ 7], a[11]) + + MUL15(a[ 8], a[10])) << 1); + t[19] = ((MUL15(a[ 0], a[19]) + + MUL15(a[ 1], a[18]) + + MUL15(a[ 2], a[17]) + + MUL15(a[ 3], a[16]) + + MUL15(a[ 4], a[15]) + + MUL15(a[ 5], a[14]) + + MUL15(a[ 6], a[13]) + + MUL15(a[ 7], a[12]) + + MUL15(a[ 8], a[11]) + + MUL15(a[ 9], a[10])) << 1); + t[20] = MUL15(a[10], a[10]) + + ((MUL15(a[ 1], a[19]) + + MUL15(a[ 2], a[18]) + + MUL15(a[ 3], a[17]) + + MUL15(a[ 4], a[16]) + + MUL15(a[ 5], a[15]) + + MUL15(a[ 6], a[14]) + + MUL15(a[ 7], a[13]) + + MUL15(a[ 8], a[12]) + + MUL15(a[ 9], a[11])) << 1); + t[21] = ((MUL15(a[ 2], a[19]) + + MUL15(a[ 3], a[18]) + + MUL15(a[ 4], a[17]) + + MUL15(a[ 5], a[16]) + + MUL15(a[ 6], a[15]) + + MUL15(a[ 7], a[14]) + + MUL15(a[ 8], a[13]) + + MUL15(a[ 9], a[12]) + + MUL15(a[10], a[11])) << 1); + t[22] = MUL15(a[11], a[11]) + + ((MUL15(a[ 3], a[19]) + + MUL15(a[ 4], a[18]) + + MUL15(a[ 5], a[17]) + + MUL15(a[ 6], a[16]) + + MUL15(a[ 7], a[15]) + + MUL15(a[ 8], a[14]) + + MUL15(a[ 9], a[13]) + + MUL15(a[10], a[12])) << 1); + t[23] = ((MUL15(a[ 4], a[19]) + + MUL15(a[ 5], a[18]) + + MUL15(a[ 6], a[17]) + + MUL15(a[ 7], a[16]) + + MUL15(a[ 8], a[15]) + + MUL15(a[ 9], a[14]) + + MUL15(a[10], a[13]) + + MUL15(a[11], a[12])) << 1); + t[24] = MUL15(a[12], a[12]) + + ((MUL15(a[ 5], a[19]) + + MUL15(a[ 6], a[18]) + + MUL15(a[ 7], a[17]) + + MUL15(a[ 8], a[16]) + + MUL15(a[ 9], a[15]) + + MUL15(a[10], a[14]) + + MUL15(a[11], a[13])) << 1); + t[25] = ((MUL15(a[ 6], a[19]) + + MUL15(a[ 7], a[18]) + + MUL15(a[ 8], a[17]) + + MUL15(a[ 9], a[16]) + + MUL15(a[10], a[15]) + + MUL15(a[11], a[14]) + + MUL15(a[12], a[13])) << 1); + t[26] = MUL15(a[13], a[13]) + + ((MUL15(a[ 7], a[19]) + + MUL15(a[ 8], a[18]) + + MUL15(a[ 9], a[17]) + + MUL15(a[10], a[16]) + + MUL15(a[11], a[15]) + + MUL15(a[12], a[14])) << 1); + t[27] = ((MUL15(a[ 8], a[19]) + + MUL15(a[ 9], a[18]) + + MUL15(a[10], a[17]) + + MUL15(a[11], a[16]) + + MUL15(a[12], a[15]) + + MUL15(a[13], a[14])) << 1); + t[28] = MUL15(a[14], a[14]) + + ((MUL15(a[ 9], a[19]) + + MUL15(a[10], a[18]) + + MUL15(a[11], a[17]) + + MUL15(a[12], a[16]) + + MUL15(a[13], a[15])) << 1); + t[29] = ((MUL15(a[10], a[19]) + + MUL15(a[11], a[18]) + + MUL15(a[12], a[17]) + + MUL15(a[13], a[16]) + + MUL15(a[14], a[15])) << 1); + t[30] = MUL15(a[15], a[15]) + + ((MUL15(a[11], a[19]) + + MUL15(a[12], a[18]) + + MUL15(a[13], a[17]) + + MUL15(a[14], a[16])) << 1); + t[31] = ((MUL15(a[12], a[19]) + + MUL15(a[13], a[18]) + + MUL15(a[14], a[17]) + + MUL15(a[15], a[16])) << 1); + t[32] = MUL15(a[16], a[16]) + + ((MUL15(a[13], a[19]) + + MUL15(a[14], a[18]) + + MUL15(a[15], a[17])) << 1); + t[33] = ((MUL15(a[14], a[19]) + + MUL15(a[15], a[18]) + + MUL15(a[16], a[17])) << 1); + t[34] = MUL15(a[17], a[17]) + + ((MUL15(a[15], a[19]) + + MUL15(a[16], a[18])) << 1); + t[35] = ((MUL15(a[16], a[19]) + + MUL15(a[17], a[18])) << 1); + t[36] = MUL15(a[18], a[18]) + + ((MUL15(a[17], a[19])) << 1); + t[37] = ((MUL15(a[18], a[19])) << 1); + t[38] = MUL15(a[19], a[19]); + d[39] = norm13(d, t, 39); +} +#endif + +#endif + +/* + * Modulus for field F256 (field for point coordinates in curve P-256). + */ +static const uint32_t F256[] PROGMEM = { + 0x1FFF, 0x1FFF, 0x1FFF, 0x1FFF, 0x1FFF, 0x1FFF, 0x1FFF, 0x001F, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0400, 0x0000, + 0x0000, 0x1FF8, 0x1FFF, 0x01FF +}; + +/* + * The 'b' curve equation coefficient for P-256. + */ +static const uint32_t P256_B[] PROGMEM = { + 0x004B, 0x1E93, 0x0F89, 0x1C78, 0x03BC, 0x187B, 0x114E, 0x1619, + 0x1D06, 0x0328, 0x01AF, 0x0D31, 0x1557, 0x15DE, 0x1ECF, 0x127C, + 0x0A3A, 0x0EC5, 0x118D, 0x00B5 +}; + +/* + * Perform a "short reduction" in field F256 (field for curve P-256). + * The source value should be less than 262 bits; on output, it will + * be at most 257 bits, and less than twice the modulus. + */ +static void +reduce_f256(uint32_t *d) +{ + uint32_t x; + + x = d[19] >> 9; + d[19] &= 0x01FF; + d[17] += x << 3; + d[14] -= x << 10; + d[7] -= x << 5; + d[0] += x; + norm13(d, d, 20); +} + +/* + * Perform a "final reduction" in field F256 (field for curve P-256). + * The source value must be less than twice the modulus. If the value + * is not lower than the modulus, then the modulus is subtracted and + * this function returns 1; otherwise, it leaves it untouched and it + * returns 0. + */ +static uint32_t +reduce_final_f256(uint32_t *d) +{ + uint32_t t[20]; + uint32_t cc; + int i; + + memcpy(t, d, sizeof t); + cc = 0; + for (i = 0; i < 20; i ++) { + uint32_t w; + + w = t[i] - F256[i] - cc; + cc = w >> 31; + t[i] = w & 0x1FFF; + } + cc ^= 1; + CCOPY(cc, d, t, sizeof t); + return cc; +} + +/* + * Perform a multiplication of two integers modulo + * 2^256-2^224+2^192+2^96-1 (for NIST curve P-256). Operands are arrays + * of 20 words, each containing 13 bits of data, in little-endian order. + * On input, upper word may be up to 13 bits (hence value up to 2^260-1); + * on output, value fits on 257 bits and is lower than twice the modulus. + */ +static void +mul_f256(uint32_t *d, const uint32_t *a, const uint32_t *b) +{ + uint32_t t[40], cc; + int i; + + /* + * Compute raw multiplication. All result words fit in 13 bits + * each. + */ + mul20(t, a, b); + + /* + * Modular reduction: each high word in added/subtracted where + * necessary. + * + * The modulus is: + * p = 2^256 - 2^224 + 2^192 + 2^96 - 1 + * Therefore: + * 2^256 = 2^224 - 2^192 - 2^96 + 1 mod p + * + * For a word x at bit offset n (n >= 256), we have: + * x*2^n = x*2^(n-32) - x*2^(n-64) + * - x*2^(n - 160) + x*2^(n-256) mod p + * + * Thus, we can nullify the high word if we reinject it at some + * proper emplacements. + */ + for (i = 39; i >= 20; i --) { + uint32_t x; + + x = t[i]; + t[i - 2] += ARSH(x, 6); + t[i - 3] += (x << 7) & 0x1FFF; + t[i - 4] -= ARSH(x, 12); + t[i - 5] -= (x << 1) & 0x1FFF; + t[i - 12] -= ARSH(x, 4); + t[i - 13] -= (x << 9) & 0x1FFF; + t[i - 19] += ARSH(x, 9); + t[i - 20] += (x << 4) & 0x1FFF; + } + + /* + * Propagate carries. This is a signed propagation, and the + * result may be negative. The loop above may enlarge values, + * but not two much: worst case is the chain involving t[i - 3], + * in which a value may be added to itself up to 7 times. Since + * starting values are 13-bit each, all words fit on 20 bits + * (21 to account for the sign bit). + */ + cc = norm13(t, t, 20); + + /* + * Perform modular reduction again for the bits beyond 256 (the carry + * and the bits 256..259). Since the largest shift below is by 10 + * bits, and the values fit on 21 bits, values fit in 32-bit words, + * thereby allowing injecting full word values. + */ + cc = (cc << 4) | (t[19] >> 9); + t[19] &= 0x01FF; + t[17] += cc << 3; + t[14] -= cc << 10; + t[7] -= cc << 5; + t[0] += cc; + + /* + * If the carry is negative, then after carry propagation, we may + * end up with a value which is negative, and we don't want that. + * Thus, in that case, we add the modulus. Note that the subtraction + * result, when the carry is negative, is always smaller than the + * modulus, so the extra addition will not make the value exceed + * twice the modulus. + */ + cc >>= 31; + t[0] -= cc; + t[7] += cc << 5; + t[14] += cc << 10; + t[17] -= cc << 3; + t[19] += cc << 9; + + norm13(d, t, 20); +} + +/* + * Square an integer modulo 2^256-2^224+2^192+2^96-1 (for NIST curve + * P-256). Operand is an array of 20 words, each containing 13 bits of + * data, in little-endian order. On input, upper word may be up to 13 + * bits (hence value up to 2^260-1); on output, value fits on 257 bits + * and is lower than twice the modulus. + */ +static void +square_f256(uint32_t *d, const uint32_t *a) +{ + uint32_t t[40], cc; + int i; + + /* + * Compute raw square. All result words fit in 13 bits each. + */ + square20(t, a); + + /* + * Modular reduction: each high word in added/subtracted where + * necessary. + * + * The modulus is: + * p = 2^256 - 2^224 + 2^192 + 2^96 - 1 + * Therefore: + * 2^256 = 2^224 - 2^192 - 2^96 + 1 mod p + * + * For a word x at bit offset n (n >= 256), we have: + * x*2^n = x*2^(n-32) - x*2^(n-64) + * - x*2^(n - 160) + x*2^(n-256) mod p + * + * Thus, we can nullify the high word if we reinject it at some + * proper emplacements. + */ + for (i = 39; i >= 20; i --) { + uint32_t x; + + x = t[i]; + t[i - 2] += ARSH(x, 6); + t[i - 3] += (x << 7) & 0x1FFF; + t[i - 4] -= ARSH(x, 12); + t[i - 5] -= (x << 1) & 0x1FFF; + t[i - 12] -= ARSH(x, 4); + t[i - 13] -= (x << 9) & 0x1FFF; + t[i - 19] += ARSH(x, 9); + t[i - 20] += (x << 4) & 0x1FFF; + } + + /* + * Propagate carries. This is a signed propagation, and the + * result may be negative. The loop above may enlarge values, + * but not two much: worst case is the chain involving t[i - 3], + * in which a value may be added to itself up to 7 times. Since + * starting values are 13-bit each, all words fit on 20 bits + * (21 to account for the sign bit). + */ + cc = norm13(t, t, 20); + + /* + * Perform modular reduction again for the bits beyond 256 (the carry + * and the bits 256..259). Since the largest shift below is by 10 + * bits, and the values fit on 21 bits, values fit in 32-bit words, + * thereby allowing injecting full word values. + */ + cc = (cc << 4) | (t[19] >> 9); + t[19] &= 0x01FF; + t[17] += cc << 3; + t[14] -= cc << 10; + t[7] -= cc << 5; + t[0] += cc; + + /* + * If the carry is negative, then after carry propagation, we may + * end up with a value which is negative, and we don't want that. + * Thus, in that case, we add the modulus. Note that the subtraction + * result, when the carry is negative, is always smaller than the + * modulus, so the extra addition will not make the value exceed + * twice the modulus. + */ + cc >>= 31; + t[0] -= cc; + t[7] += cc << 5; + t[14] += cc << 10; + t[17] -= cc << 3; + t[19] += cc << 9; + + norm13(d, t, 20); +} + +/* + * Jacobian coordinates for a point in P-256: affine coordinates (X,Y) + * are such that: + * X = x / z^2 + * Y = y / z^3 + * For the point at infinity, z = 0. + * Each point thus admits many possible representations. + * + * Coordinates are represented in arrays of 32-bit integers, each holding + * 13 bits of data. Values may also be slightly greater than the modulus, + * but they will always be lower than twice the modulus. + */ +typedef struct { + uint32_t x[20]; + uint32_t y[20]; + uint32_t z[20]; +} p256_jacobian; + +/* + * Convert a point to affine coordinates: + * - If the point is the point at infinity, then all three coordinates + * are set to 0. + * - Otherwise, the 'z' coordinate is set to 1, and the 'x' and 'y' + * coordinates are the 'X' and 'Y' affine coordinates. + * The coordinates are guaranteed to be lower than the modulus. + */ +static void +p256_to_affine(p256_jacobian *P) +{ + uint32_t t1[20], t2[20]; + int i; + + /* + * Invert z with a modular exponentiation: the modulus is + * p = 2^256 - 2^224 + 2^192 + 2^96 - 1, and the exponent is + * p-2. Exponent bit pattern (from high to low) is: + * - 32 bits of value 1 + * - 31 bits of value 0 + * - 1 bit of value 1 + * - 96 bits of value 0 + * - 94 bits of value 1 + * - 1 bit of value 0 + * - 1 bit of value 1 + * Thus, we precompute z^(2^31-1) to speed things up. + * + * If z = 0 (point at infinity) then the modular exponentiation + * will yield 0, which leads to the expected result (all three + * coordinates set to 0). + */ + + /* + * A simple square-and-multiply for z^(2^31-1). We could save about + * two dozen multiplications here with an addition chain, but + * this would require a bit more code, and extra stack buffers. + */ + memcpy(t1, P->z, sizeof P->z); + for (i = 0; i < 30; i ++) { + square_f256(t1, t1); + mul_f256(t1, t1, P->z); + } + + /* + * Square-and-multiply. Apart from the squarings, we have a few + * multiplications to set bits to 1; we multiply by the original z + * for setting 1 bit, and by t1 for setting 31 bits. + */ + memcpy(t2, P->z, sizeof P->z); + for (i = 1; i < 256; i ++) { + square_f256(t2, t2); + switch (i) { + case 31: + case 190: + case 221: + case 252: + mul_f256(t2, t2, t1); + break; + case 63: + case 253: + case 255: + mul_f256(t2, t2, P->z); + break; + } + } + + /* + * Now that we have 1/z, multiply x by 1/z^2 and y by 1/z^3. + */ + mul_f256(t1, t2, t2); + mul_f256(P->x, t1, P->x); + mul_f256(t1, t1, t2); + mul_f256(P->y, t1, P->y); + reduce_final_f256(P->x); + reduce_final_f256(P->y); + + /* + * Multiply z by 1/z. If z = 0, then this will yield 0, otherwise + * this will set z to 1. + */ + mul_f256(P->z, P->z, t2); + reduce_final_f256(P->z); +} + +/* + * Double a point in P-256. This function works for all valid points, + * including the point at infinity. + */ +static void +p256_double(p256_jacobian *Q) +{ + /* + * Doubling formulas are: + * + * s = 4*x*y^2 + * m = 3*(x + z^2)*(x - z^2) + * x' = m^2 - 2*s + * y' = m*(s - x') - 8*y^4 + * z' = 2*y*z + * + * These formulas work for all points, including points of order 2 + * and points at infinity: + * - If y = 0 then z' = 0. But there is no such point in P-256 + * anyway. + * - If z = 0 then z' = 0. + */ + uint32_t t1[20], t2[20], t3[20], t4[20]; + int i; + + /* + * Compute z^2 in t1. + */ + square_f256(t1, Q->z); + + /* + * Compute x-z^2 in t2 and x+z^2 in t1. + */ + for (i = 0; i < 20; i ++) { + t2[i] = (F256[i] << 1) + Q->x[i] - t1[i]; + t1[i] += Q->x[i]; + } + norm13(t1, t1, 20); + norm13(t2, t2, 20); + + /* + * Compute 3*(x+z^2)*(x-z^2) in t1. + */ + mul_f256(t3, t1, t2); + for (i = 0; i < 20; i ++) { + t1[i] = MUL15(3, t3[i]); + } + norm13(t1, t1, 20); + + /* + * Compute 4*x*y^2 (in t2) and 2*y^2 (in t3). + */ + square_f256(t3, Q->y); + for (i = 0; i < 20; i ++) { + t3[i] <<= 1; + } + norm13(t3, t3, 20); + mul_f256(t2, Q->x, t3); + for (i = 0; i < 20; i ++) { + t2[i] <<= 1; + } + norm13(t2, t2, 20); + reduce_f256(t2); + + /* + * Compute x' = m^2 - 2*s. + */ + square_f256(Q->x, t1); + for (i = 0; i < 20; i ++) { + Q->x[i] += (F256[i] << 2) - (t2[i] << 1); + } + norm13(Q->x, Q->x, 20); + reduce_f256(Q->x); + + /* + * Compute z' = 2*y*z. + */ + mul_f256(t4, Q->y, Q->z); + for (i = 0; i < 20; i ++) { + Q->z[i] = t4[i] << 1; + } + norm13(Q->z, Q->z, 20); + reduce_f256(Q->z); + + /* + * Compute y' = m*(s - x') - 8*y^4. Note that we already have + * 2*y^2 in t3. + */ + for (i = 0; i < 20; i ++) { + t2[i] += (F256[i] << 1) - Q->x[i]; + } + norm13(t2, t2, 20); + mul_f256(Q->y, t1, t2); + square_f256(t4, t3); + for (i = 0; i < 20; i ++) { + Q->y[i] += (F256[i] << 2) - (t4[i] << 1); + } + norm13(Q->y, Q->y, 20); + reduce_f256(Q->y); +} + +/* + * Add point P2 to point P1. + * + * This function computes the wrong result in the following cases: + * + * - If P1 == 0 but P2 != 0 + * - If P1 != 0 but P2 == 0 + * - If P1 == P2 + * + * In all three cases, P1 is set to the point at infinity. + * + * Returned value is 0 if one of the following occurs: + * + * - P1 and P2 have the same Y coordinate + * - P1 == 0 and P2 == 0 + * - The Y coordinate of one of the points is 0 and the other point is + * the point at infinity. + * + * The third case cannot actually happen with valid points, since a point + * with Y == 0 is a point of order 2, and there is no point of order 2 on + * curve P-256. + * + * Therefore, assuming that P1 != 0 and P2 != 0 on input, then the caller + * can apply the following: + * + * - If the result is not the point at infinity, then it is correct. + * - Otherwise, if the returned value is 1, then this is a case of + * P1+P2 == 0, so the result is indeed the point at infinity. + * - Otherwise, P1 == P2, so a "double" operation should have been + * performed. + */ +static uint32_t +p256_add(p256_jacobian *P1, const p256_jacobian *P2) +{ + /* + * Addtions formulas are: + * + * u1 = x1 * z2^2 + * u2 = x2 * z1^2 + * s1 = y1 * z2^3 + * s2 = y2 * z1^3 + * h = u2 - u1 + * r = s2 - s1 + * x3 = r^2 - h^3 - 2 * u1 * h^2 + * y3 = r * (u1 * h^2 - x3) - s1 * h^3 + * z3 = h * z1 * z2 + */ + uint32_t t1[20], t2[20], t3[20], t4[20], t5[20], t6[20], t7[20]; + uint32_t ret; + int i; + + /* + * Compute u1 = x1*z2^2 (in t1) and s1 = y1*z2^3 (in t3). + */ + square_f256(t3, P2->z); + mul_f256(t1, P1->x, t3); + mul_f256(t4, P2->z, t3); + mul_f256(t3, P1->y, t4); + + /* + * Compute u2 = x2*z1^2 (in t2) and s2 = y2*z1^3 (in t4). + */ + square_f256(t4, P1->z); + mul_f256(t2, P2->x, t4); + mul_f256(t5, P1->z, t4); + mul_f256(t4, P2->y, t5); + + /* + * Compute h = h2 - u1 (in t2) and r = s2 - s1 (in t4). + * We need to test whether r is zero, so we will do some extra + * reduce. + */ + for (i = 0; i < 20; i ++) { + t2[i] += (F256[i] << 1) - t1[i]; + t4[i] += (F256[i] << 1) - t3[i]; + } + norm13(t2, t2, 20); + norm13(t4, t4, 20); + reduce_f256(t4); + reduce_final_f256(t4); + ret = 0; + for (i = 0; i < 20; i ++) { + ret |= t4[i]; + } + ret = (ret | -ret) >> 31; + + /* + * Compute u1*h^2 (in t6) and h^3 (in t5); + */ + square_f256(t7, t2); + mul_f256(t6, t1, t7); + mul_f256(t5, t7, t2); + + /* + * Compute x3 = r^2 - h^3 - 2*u1*h^2. + */ + square_f256(P1->x, t4); + for (i = 0; i < 20; i ++) { + P1->x[i] += (F256[i] << 3) - t5[i] - (t6[i] << 1); + } + norm13(P1->x, P1->x, 20); + reduce_f256(P1->x); + + /* + * Compute y3 = r*(u1*h^2 - x3) - s1*h^3. + */ + for (i = 0; i < 20; i ++) { + t6[i] += (F256[i] << 1) - P1->x[i]; + } + norm13(t6, t6, 20); + mul_f256(P1->y, t4, t6); + mul_f256(t1, t5, t3); + for (i = 0; i < 20; i ++) { + P1->y[i] += (F256[i] << 1) - t1[i]; + } + norm13(P1->y, P1->y, 20); + reduce_f256(P1->y); + + /* + * Compute z3 = h*z1*z2. + */ + mul_f256(t1, P1->z, P2->z); + mul_f256(P1->z, t1, t2); + + return ret; +} + +/* + * Add point P2 to point P1. This is a specialised function for the + * case when P2 is a non-zero point in affine coordinate. + * + * This function computes the wrong result in the following cases: + * + * - If P1 == 0 + * - If P1 == P2 + * + * In both cases, P1 is set to the point at infinity. + * + * Returned value is 0 if one of the following occurs: + * + * - P1 and P2 have the same Y coordinate + * - The Y coordinate of P2 is 0 and P1 is the point at infinity. + * + * The second case cannot actually happen with valid points, since a point + * with Y == 0 is a point of order 2, and there is no point of order 2 on + * curve P-256. + * + * Therefore, assuming that P1 != 0 on input, then the caller + * can apply the following: + * + * - If the result is not the point at infinity, then it is correct. + * - Otherwise, if the returned value is 1, then this is a case of + * P1+P2 == 0, so the result is indeed the point at infinity. + * - Otherwise, P1 == P2, so a "double" operation should have been + * performed. + */ +static uint32_t +p256_add_mixed(p256_jacobian *P1, const p256_jacobian *P2) +{ + /* + * Addtions formulas are: + * + * u1 = x1 + * u2 = x2 * z1^2 + * s1 = y1 + * s2 = y2 * z1^3 + * h = u2 - u1 + * r = s2 - s1 + * x3 = r^2 - h^3 - 2 * u1 * h^2 + * y3 = r * (u1 * h^2 - x3) - s1 * h^3 + * z3 = h * z1 + */ + uint32_t t1[20], t2[20], t3[20], t4[20], t5[20], t6[20], t7[20]; + uint32_t ret; + int i; + + /* + * Compute u1 = x1 (in t1) and s1 = y1 (in t3). + */ + memcpy(t1, P1->x, sizeof t1); + memcpy(t3, P1->y, sizeof t3); + + /* + * Compute u2 = x2*z1^2 (in t2) and s2 = y2*z1^3 (in t4). + */ + square_f256(t4, P1->z); + mul_f256(t2, P2->x, t4); + mul_f256(t5, P1->z, t4); + mul_f256(t4, P2->y, t5); + + /* + * Compute h = h2 - u1 (in t2) and r = s2 - s1 (in t4). + * We need to test whether r is zero, so we will do some extra + * reduce. + */ + for (i = 0; i < 20; i ++) { + t2[i] += (F256[i] << 1) - t1[i]; + t4[i] += (F256[i] << 1) - t3[i]; + } + norm13(t2, t2, 20); + norm13(t4, t4, 20); + reduce_f256(t4); + reduce_final_f256(t4); + ret = 0; + for (i = 0; i < 20; i ++) { + ret |= t4[i]; + } + ret = (ret | -ret) >> 31; + + /* + * Compute u1*h^2 (in t6) and h^3 (in t5); + */ + square_f256(t7, t2); + mul_f256(t6, t1, t7); + mul_f256(t5, t7, t2); + + /* + * Compute x3 = r^2 - h^3 - 2*u1*h^2. + */ + square_f256(P1->x, t4); + for (i = 0; i < 20; i ++) { + P1->x[i] += (F256[i] << 3) - t5[i] - (t6[i] << 1); + } + norm13(P1->x, P1->x, 20); + reduce_f256(P1->x); + + /* + * Compute y3 = r*(u1*h^2 - x3) - s1*h^3. + */ + for (i = 0; i < 20; i ++) { + t6[i] += (F256[i] << 1) - P1->x[i]; + } + norm13(t6, t6, 20); + mul_f256(P1->y, t4, t6); + mul_f256(t1, t5, t3); + for (i = 0; i < 20; i ++) { + P1->y[i] += (F256[i] << 1) - t1[i]; + } + norm13(P1->y, P1->y, 20); + reduce_f256(P1->y); + + /* + * Compute z3 = h*z1*z2. + */ + mul_f256(P1->z, P1->z, t2); + + return ret; +} + +/* + * Decode a P-256 point. This function does not support the point at + * infinity. Returned value is 0 if the point is invalid, 1 otherwise. + */ +static uint32_t +p256_decode(p256_jacobian *P, const void *src, size_t len) +{ + const unsigned char *buf; + uint32_t tx[20], ty[20], t1[20], t2[20]; + uint32_t bad; + int i; + + if (len != 65) { + return 0; + } + buf = src; + + /* + * First byte must be 0x04 (uncompressed format). We could support + * "hybrid format" (first byte is 0x06 or 0x07, and encodes the + * least significant bit of the Y coordinate), but it is explicitly + * forbidden by RFC 5480 (section 2.2). + */ + bad = NEQ(buf[0], 0x04); + + /* + * Decode the coordinates, and check that they are both lower + * than the modulus. + */ + tx[19] = be8_to_le13(tx, buf + 1, 32); + ty[19] = be8_to_le13(ty, buf + 33, 32); + bad |= reduce_final_f256(tx); + bad |= reduce_final_f256(ty); + + /* + * Check curve equation. + */ + square_f256(t1, tx); + mul_f256(t1, tx, t1); + square_f256(t2, ty); + for (i = 0; i < 20; i ++) { + t1[i] += (F256[i] << 3) - MUL15(3, tx[i]) + P256_B[i] - t2[i]; + } + norm13(t1, t1, 20); + reduce_f256(t1); + reduce_final_f256(t1); + for (i = 0; i < 20; i ++) { + bad |= t1[i]; + } + + /* + * Copy coordinates to the point structure. + */ + memcpy(P->x, tx, sizeof tx); + memcpy(P->y, ty, sizeof ty); + memset(P->z, 0, sizeof P->z); + P->z[0] = 1; + return EQ(bad, 0); +} + +/* + * Encode a point into a buffer. This function assumes that the point is + * valid, in affine coordinates, and not the point at infinity. + */ +static void +p256_encode(void *dst, const p256_jacobian *P) +{ + unsigned char *buf; + + buf = dst; + buf[0] = 0x04; + le13_to_be8(buf + 1, 32, P->x); + le13_to_be8(buf + 33, 32, P->y); +} + +/* + * Multiply a curve point by an integer. The integer is assumed to be + * lower than the curve order, and the base point must not be the point + * at infinity. + */ +static void +p256_mul(p256_jacobian *P, const unsigned char *x, size_t xlen) +{ + /* + * qz is a flag that is initially 1, and remains equal to 1 + * as long as the point is the point at infinity. + * + * We use a 2-bit window to handle multiplier bits by pairs. + * The precomputed window really is the points P2 and P3. + */ + uint32_t qz; + p256_jacobian P2, P3, Q, T, U; + + /* + * Compute window values. + */ + P2 = *P; + p256_double(&P2); + P3 = *P; + p256_add(&P3, &P2); + + /* + * We start with Q = 0. We process multiplier bits 2 by 2. + */ + memset(&Q, 0, sizeof Q); + qz = 1; + while (xlen -- > 0) { + int k; + + for (k = 6; k >= 0; k -= 2) { + uint32_t bits; + uint32_t bnz; + + p256_double(&Q); + p256_double(&Q); + T = *P; + U = Q; + bits = (*x >> k) & (uint32_t)3; + bnz = NEQ(bits, 0); + CCOPY(EQ(bits, 2), &T, &P2, sizeof T); + CCOPY(EQ(bits, 3), &T, &P3, sizeof T); + p256_add(&U, &T); + CCOPY(bnz & qz, &Q, &T, sizeof Q); + CCOPY(bnz & ~qz, &Q, &U, sizeof Q); + qz &= ~bnz; + } + x ++; + } + *P = Q; +} + +/* + * Precomputed window: k*G points, where G is the curve generator, and k + * is an integer from 1 to 15 (inclusive). The X and Y coordinates of + * the point are encoded as 20 words of 13 bits each (little-endian + * order); 13-bit words are then grouped 2-by-2 into 32-bit words + * (little-endian order within each word). + */ +static const uint32_t Gwin[15][20] PROGMEM = { + + { 0x04C60296, 0x02721176, 0x19D00F4A, 0x102517AC, + 0x13B8037D, 0x0748103C, 0x1E730E56, 0x08481FE2, + 0x0F97012C, 0x00D605F4, 0x1DFA11F5, 0x0C801A0D, + 0x0F670CBB, 0x0AED0CC5, 0x115E0E33, 0x181F0785, + 0x13F514A7, 0x0FF30E3B, 0x17171E1A, 0x009F18D0 }, + + { 0x1B341978, 0x16911F11, 0x0D9A1A60, 0x1C4E1FC8, + 0x1E040969, 0x096A06B0, 0x091C0030, 0x09EF1A29, + 0x18C40D03, 0x00F91C9E, 0x13C313D1, 0x096F0748, + 0x011419E0, 0x1CC713A6, 0x1DD31DAD, 0x1EE80C36, + 0x1ECD0C69, 0x1A0800A4, 0x08861B8E, 0x000E1DD5 }, + + { 0x173F1D6C, 0x02CC06F1, 0x14C21FB4, 0x043D1EB6, + 0x0F3606B7, 0x1A971C59, 0x1BF71951, 0x01481323, + 0x068D0633, 0x00BD12F9, 0x13EA1032, 0x136209E8, + 0x1C1E19A7, 0x06C7013E, 0x06C10AB0, 0x14C908BB, + 0x05830CE1, 0x1FEF18DD, 0x00620998, 0x010E0D19 }, + + { 0x18180852, 0x0604111A, 0x0B771509, 0x1B6F0156, + 0x00181FE2, 0x1DCC0AF4, 0x16EF0659, 0x11F70E80, + 0x11A912D0, 0x01C414D2, 0x027618C6, 0x05840FC6, + 0x100215C4, 0x187E0C3B, 0x12771C96, 0x150C0B5D, + 0x0FF705FD, 0x07981C67, 0x1AD20C63, 0x01C11C55 }, + + { 0x1E8113ED, 0x0A940370, 0x12920215, 0x1FA31D6F, + 0x1F7C0C82, 0x10CD03F7, 0x02640560, 0x081A0B5E, + 0x1BD21151, 0x00A21642, 0x0D0B0DA4, 0x0176113F, + 0x04440D1D, 0x001A1360, 0x1068012F, 0x1F141E49, + 0x10DF136B, 0x0E4F162B, 0x0D44104A, 0x01C1105F }, + + { 0x011411A9, 0x01551A4F, 0x0ADA0C6B, 0x01BD0EC8, + 0x18120C74, 0x112F1778, 0x099202CB, 0x0C05124B, + 0x195316A4, 0x01600685, 0x1E3B1FE2, 0x189014E3, + 0x0B5E1FD7, 0x0E0311F8, 0x08E000F7, 0x174E00DE, + 0x160702DF, 0x1B5A15BF, 0x03A11237, 0x01D01704 }, + + { 0x0C3D12A3, 0x0C501C0C, 0x17AD1300, 0x1715003F, + 0x03F719F8, 0x18031ED8, 0x1D980667, 0x0F681896, + 0x1B7D00BF, 0x011C14CE, 0x0FA000B4, 0x1C3501B0, + 0x0D901C55, 0x06790C10, 0x029E0736, 0x0DEB0400, + 0x034F183A, 0x030619B4, 0x0DEF0033, 0x00E71AC7 }, + + { 0x1B7D1393, 0x1B3B1076, 0x0BED1B4D, 0x13011F3A, + 0x0E0E1238, 0x156A132B, 0x013A02D3, 0x160A0D01, + 0x1CED1EE9, 0x00C5165D, 0x184C157E, 0x08141A83, + 0x153C0DA5, 0x1ED70F9D, 0x05170D51, 0x02CF13B8, + 0x18AE1771, 0x1B04113F, 0x05EC11E9, 0x015A16B3 }, + + { 0x04A41EE0, 0x1D1412E4, 0x1C591D79, 0x118511B7, + 0x14F00ACB, 0x1AE31E1C, 0x049C0D51, 0x016E061E, + 0x1DB71EDF, 0x01D41A35, 0x0E8208FA, 0x14441293, + 0x011F1E85, 0x1D54137A, 0x026B114F, 0x151D0832, + 0x00A50964, 0x1F9C1E1C, 0x064B12C9, 0x005409D1 }, + + { 0x062B123F, 0x0C0D0501, 0x183704C3, 0x08E31120, + 0x0A2E0A6C, 0x14440FED, 0x090A0D1E, 0x13271964, + 0x0B590A3A, 0x019D1D9B, 0x05780773, 0x09770A91, + 0x0F770CA3, 0x053F19D4, 0x02C80DED, 0x1A761304, + 0x091E0DD9, 0x15D201B8, 0x151109AA, 0x010F0198 }, + + { 0x05E101D1, 0x072314DD, 0x045F1433, 0x1A041541, + 0x10B3142E, 0x01840736, 0x1C1B19DB, 0x098B0418, + 0x1DBC083B, 0x007D1444, 0x01511740, 0x11DD1F3A, + 0x04ED0E2F, 0x1B4B1A62, 0x10480D04, 0x09E911A2, + 0x04211AFA, 0x19140893, 0x04D60CC4, 0x01210648 }, + + { 0x112703C4, 0x018B1BA1, 0x164C1D50, 0x05160BE0, + 0x0BCC1830, 0x01CB1554, 0x13291732, 0x1B2B1918, + 0x0DED0817, 0x00E80775, 0x0A2401D3, 0x0BFE08B3, + 0x0E531199, 0x058616E9, 0x04770B91, 0x110F0C55, + 0x19C11554, 0x0BFB1159, 0x03541C38, 0x000E1C2D }, + + { 0x10390C01, 0x02BB0751, 0x0AC5098E, 0x096C17AB, + 0x03C90E28, 0x10BD18BF, 0x002E1F2D, 0x092B0986, + 0x1BD700AC, 0x002E1F20, 0x1E3D1FD8, 0x077718BB, + 0x06F919C4, 0x187407ED, 0x11370E14, 0x081E139C, + 0x00481ADB, 0x14AB0289, 0x066A0EBE, 0x00C70ED6 }, + + { 0x0694120B, 0x124E1CC9, 0x0E2F0570, 0x17CF081A, + 0x078906AC, 0x066D17CF, 0x1B3207F4, 0x0C5705E9, + 0x10001C38, 0x00A919DE, 0x06851375, 0x0F900BD8, + 0x080401BA, 0x0EEE0D42, 0x1B8B11EA, 0x0B4519F0, + 0x090F18C0, 0x062E1508, 0x0DD909F4, 0x01EB067C }, + + { 0x0CDC1D5F, 0x0D1818F9, 0x07781636, 0x125B18E8, + 0x0D7003AF, 0x13110099, 0x1D9B1899, 0x175C1EB7, + 0x0E34171A, 0x01E01153, 0x081A0F36, 0x0B391783, + 0x1D1F147E, 0x19CE16D7, 0x11511B21, 0x1F2C10F9, + 0x12CA0E51, 0x05A31D39, 0x171A192E, 0x016B0E4F } +}; + +/* + * Lookup one of the Gwin[] values, by index. This is constant-time. + */ +static void +lookup_Gwin(p256_jacobian *T, uint32_t idx) +{ + uint32_t xy[20]; + uint32_t k; + size_t u; + + memset(xy, 0, sizeof xy); + for (k = 0; k < 15; k ++) { + uint32_t m; + + m = -EQ(idx, k + 1); + for (u = 0; u < 20; u ++) { + xy[u] |= m & Gwin[k][u]; + } + } + for (u = 0; u < 10; u ++) { + T->x[(u << 1) + 0] = xy[u] & 0xFFFF; + T->x[(u << 1) + 1] = xy[u] >> 16; + T->y[(u << 1) + 0] = xy[u + 10] & 0xFFFF; + T->y[(u << 1) + 1] = xy[u + 10] >> 16; + } + memset(T->z, 0, sizeof T->z); + T->z[0] = 1; +} + +/* + * Multiply the generator by an integer. The integer is assumed non-zero + * and lower than the curve order. + */ +static void +p256_mulgen(p256_jacobian *P, const unsigned char *x, size_t xlen) +{ + /* + * qz is a flag that is initially 1, and remains equal to 1 + * as long as the point is the point at infinity. + * + * We use a 4-bit window to handle multiplier bits by groups + * of 4. The precomputed window is constant static data, with + * points in affine coordinates; we use a constant-time lookup. + */ + p256_jacobian Q; + uint32_t qz; + + memset(&Q, 0, sizeof Q); + qz = 1; + while (xlen -- > 0) { + int k; + unsigned bx; + + bx = *x ++; + for (k = 0; k < 2; k ++) { + uint32_t bits; + uint32_t bnz; + p256_jacobian T, U; + + p256_double(&Q); + p256_double(&Q); + p256_double(&Q); + p256_double(&Q); + bits = (bx >> 4) & 0x0F; + bnz = NEQ(bits, 0); + lookup_Gwin(&T, bits); + U = Q; + p256_add_mixed(&U, &T); + CCOPY(bnz & qz, &Q, &T, sizeof Q); + CCOPY(bnz & ~qz, &Q, &U, sizeof Q); + qz &= ~bnz; + bx <<= 4; + } + } + *P = Q; +} + +static const unsigned char * +api_generator(int curve, size_t *len) +{ + (void)curve; + *len = br_secp256r1.generator_len; + return br_secp256r1.generator; +} + +static const unsigned char * +api_order(int curve, size_t *len) +{ + (void)curve; + *len = br_secp256r1.order_len; + return br_secp256r1.order; +} + +static size_t +api_xoff(int curve, size_t *len) +{ + (void)curve; + *len = 32; + return 1; +} + +static uint32_t +api_mul(unsigned char *G, size_t Glen, + const unsigned char *x, size_t xlen, int curve) +{ + uint32_t r; + p256_jacobian P; + + (void)curve; + r = p256_decode(&P, G, Glen); + p256_mul(&P, x, xlen); + if (Glen >= 65) { + p256_to_affine(&P); + p256_encode(G, &P); + } + return r; +} + +static size_t +api_mulgen(unsigned char *R, + const unsigned char *x, size_t xlen, int curve) +{ + p256_jacobian P; + + (void)curve; + p256_mulgen(&P, x, xlen); + p256_to_affine(&P); + p256_encode(R, &P); + return 65; + + /* + const unsigned char *G; + size_t Glen; + + G = api_generator(curve, &Glen); + memcpy(R, G, Glen); + api_mul(R, Glen, x, xlen, curve); + return Glen; + */ +} + +static uint32_t +api_muladd(unsigned char *A, const unsigned char *B, size_t len, + const unsigned char *x, size_t xlen, + const unsigned char *y, size_t ylen, int curve) +{ + p256_jacobian P, Q; + uint32_t r, t, z; + int i; + + (void)curve; + r = p256_decode(&P, A, len); + p256_mul(&P, x, xlen); + if (B == NULL) { + p256_mulgen(&Q, y, ylen); + } else { + r &= p256_decode(&Q, B, len); + p256_mul(&Q, y, ylen); + } + + /* + * The final addition may fail in case both points are equal. + */ + t = p256_add(&P, &Q); + reduce_final_f256(P.z); + z = 0; + for (i = 0; i < 20; i ++) { + z |= P.z[i]; + } + z = EQ(z, 0); + p256_double(&Q); + + /* + * If z is 1 then either P+Q = 0 (t = 1) or P = Q (t = 0). So we + * have the following: + * + * z = 0, t = 0 return P (normal addition) + * z = 0, t = 1 return P (normal addition) + * z = 1, t = 0 return Q (a 'double' case) + * z = 1, t = 1 report an error (P+Q = 0) + */ + CCOPY(z & ~t, &P, &Q, sizeof Q); + p256_to_affine(&P); + p256_encode(A, &P); + r &= ~(z & t); + return r; +} + +/* see bearssl_ec.h */ +const br_ec_impl br_ec_p256_m15 PROGMEM = { + (uint32_t)0x00800000, + &api_generator, + &api_order, + &api_xoff, + &api_mul, + &api_mulgen, + &api_muladd +}; diff --git a/lib/bearssl-esp8266/src/ec/ec_prime_i15.c b/lib/bearssl-esp8266/src/ec/ec_prime_i15.c new file mode 100644 index 000000000..1bed22aa4 --- /dev/null +++ b/lib/bearssl-esp8266/src/ec/ec_prime_i15.c @@ -0,0 +1,822 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * Parameters for supported curves: + * - field modulus p + * - R^2 mod p (R = 2^(15k) for the smallest k such that R >= p) + * - b*R mod p (b is the second curve equation parameter) + */ + +static const uint16_t P256_P[] PROGMEM = { + 0x0111, + 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x003F, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x1000, 0x0000, 0x4000, 0x7FFF, + 0x7FFF, 0x0001 +}; + +static const uint16_t P256_R2[] PROGMEM = { + 0x0111, + 0x0000, 0x6000, 0x0000, 0x0000, 0x0000, 0x0000, 0x7FFC, 0x7FFF, + 0x7FBF, 0x7FFF, 0x7FBF, 0x7FFF, 0x7FFF, 0x7FFF, 0x77FF, 0x7FFF, + 0x4FFF, 0x0000 +}; + +static const uint16_t P256_B[] PROGMEM = { + 0x0111, + 0x770C, 0x5EEF, 0x29C4, 0x3EC4, 0x6273, 0x0486, 0x4543, 0x3993, + 0x3C01, 0x6B56, 0x212E, 0x57EE, 0x4882, 0x204B, 0x7483, 0x3C16, + 0x0187, 0x0000 +}; + +static const uint16_t P384_P[] PROGMEM = { + 0x0199, + 0x7FFF, 0x7FFF, 0x0003, 0x0000, 0x0000, 0x0000, 0x7FC0, 0x7FFF, + 0x7EFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, + 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, + 0x7FFF, 0x01FF +}; + +static const uint16_t P384_R2[] PROGMEM = { + 0x0199, + 0x1000, 0x0000, 0x0000, 0x7FFF, 0x7FFF, 0x0001, 0x0000, 0x0010, + 0x0000, 0x0000, 0x0000, 0x7F00, 0x7FFF, 0x01FF, 0x0000, 0x1000, + 0x0000, 0x2000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000 +}; + +static const uint16_t P384_B[] PROGMEM = { + 0x0199, + 0x7333, 0x2096, 0x70D1, 0x2310, 0x3020, 0x6197, 0x1464, 0x35BB, + 0x70CA, 0x0117, 0x1920, 0x4136, 0x5FC8, 0x5713, 0x4938, 0x7DD2, + 0x4DD2, 0x4A71, 0x0220, 0x683E, 0x2C87, 0x4DB1, 0x7BFF, 0x6C09, + 0x0452, 0x0084 +}; + +static const uint16_t P521_P[] PROGMEM = { + 0x022B, + 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, + 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, + 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, + 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, + 0x7FFF, 0x7FFF, 0x07FF +}; + +static const uint16_t P521_R2[] PROGMEM = { + 0x022B, + 0x0100, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000 +}; + +static const uint16_t P521_B[] PROGMEM = { + 0x022B, + 0x7002, 0x6A07, 0x751A, 0x228F, 0x71EF, 0x5869, 0x20F4, 0x1EFC, + 0x7357, 0x37E0, 0x4EEC, 0x605E, 0x1652, 0x26F6, 0x31FA, 0x4A8F, + 0x6193, 0x3C2A, 0x3C42, 0x48C7, 0x3489, 0x6771, 0x4C57, 0x5CCD, + 0x2725, 0x545B, 0x503B, 0x5B42, 0x21A0, 0x2534, 0x687E, 0x70E4, + 0x1618, 0x27D7, 0x0465 +}; + +typedef struct { + const uint16_t *p; + const uint16_t *b; + const uint16_t *R2; + uint16_t p0i; + size_t point_len; +} curve_params; + +static inline const curve_params * +id_to_curve(int curve) +{ + static const curve_params pp[] = { + { P256_P, P256_B, P256_R2, 0x0001, 65 }, + { P384_P, P384_B, P384_R2, 0x0001, 97 }, + { P521_P, P521_B, P521_R2, 0x0001, 133 } + }; + + return &pp[curve - BR_EC_secp256r1]; +} + +#define I15_LEN ((BR_MAX_EC_SIZE + 29) / 15) + +/* + * Type for a point in Jacobian coordinates: + * -- three values, x, y and z, in Montgomery representation + * -- affine coordinates are X = x / z^2 and Y = y / z^3 + * -- for the point at infinity, z = 0 + */ +typedef struct { + uint16_t c[3][I15_LEN]; +} jacobian; + +/* + * We use a custom interpreter that uses a dozen registers, and + * only six operations: + * MSET(d, a) copy a into d + * MADD(d, a) d = d+a (modular) + * MSUB(d, a) d = d-a (modular) + * MMUL(d, a, b) d = a*b (Montgomery multiplication) + * MINV(d, a, b) invert d modulo p; a and b are used as scratch registers + * MTZ(d) clear return value if d = 0 + * Destination of MMUL (d) must be distinct from operands (a and b). + * There is no such constraint for MSUB and MADD. + * + * Registers include the operand coordinates, and temporaries. + */ +#define MSET(d, a) (0x0000 + ((d) << 8) + ((a) << 4)) +#define MADD(d, a) (0x1000 + ((d) << 8) + ((a) << 4)) +#define MSUB(d, a) (0x2000 + ((d) << 8) + ((a) << 4)) +#define MMUL(d, a, b) (0x3000 + ((d) << 8) + ((a) << 4) + (b)) +#define MINV(d, a, b) (0x4000 + ((d) << 8) + ((a) << 4) + (b)) +#define MTZ(d) (0x5000 + ((d) << 8)) +#define ENDCODE 0 + +/* + * Registers for the input operands. + */ +#define P1x 0 +#define P1y 1 +#define P1z 2 +#define P2x 3 +#define P2y 4 +#define P2z 5 + +/* + * Alternate names for the first input operand. + */ +#define Px 0 +#define Py 1 +#define Pz 2 + +/* + * Temporaries. + */ +#define t1 6 +#define t2 7 +#define t3 8 +#define t4 9 +#define t5 10 +#define t6 11 +#define t7 12 + +/* + * Extra scratch registers available when there is no second operand (e.g. + * for "double" and "affine"). + */ +#define t8 3 +#define t9 4 +#define t10 5 + +/* + * Doubling formulas are: + * + * s = 4*x*y^2 + * m = 3*(x + z^2)*(x - z^2) + * x' = m^2 - 2*s + * y' = m*(s - x') - 8*y^4 + * z' = 2*y*z + * + * If y = 0 (P has order 2) then this yields infinity (z' = 0), as it + * should. This case should not happen anyway, because our curves have + * prime order, and thus do not contain any point of order 2. + * + * If P is infinity (z = 0), then again the formulas yield infinity, + * which is correct. Thus, this code works for all points. + * + * Cost: 8 multiplications + */ +static const uint16_t code_double[] PROGMEM = { + /* + * Compute z^2 (in t1). + */ + MMUL(t1, Pz, Pz), + + /* + * Compute x-z^2 (in t2) and then x+z^2 (in t1). + */ + MSET(t2, Px), + MSUB(t2, t1), + MADD(t1, Px), + + /* + * Compute m = 3*(x+z^2)*(x-z^2) (in t1). + */ + MMUL(t3, t1, t2), + MSET(t1, t3), + MADD(t1, t3), + MADD(t1, t3), + + /* + * Compute s = 4*x*y^2 (in t2) and 2*y^2 (in t3). + */ + MMUL(t3, Py, Py), + MADD(t3, t3), + MMUL(t2, Px, t3), + MADD(t2, t2), + + /* + * Compute x' = m^2 - 2*s. + */ + MMUL(Px, t1, t1), + MSUB(Px, t2), + MSUB(Px, t2), + + /* + * Compute z' = 2*y*z. + */ + MMUL(t4, Py, Pz), + MSET(Pz, t4), + MADD(Pz, t4), + + /* + * Compute y' = m*(s - x') - 8*y^4. Note that we already have + * 2*y^2 in t3. + */ + MSUB(t2, Px), + MMUL(Py, t1, t2), + MMUL(t4, t3, t3), + MSUB(Py, t4), + MSUB(Py, t4), + + ENDCODE +}; + +/* + * Addtions formulas are: + * + * u1 = x1 * z2^2 + * u2 = x2 * z1^2 + * s1 = y1 * z2^3 + * s2 = y2 * z1^3 + * h = u2 - u1 + * r = s2 - s1 + * x3 = r^2 - h^3 - 2 * u1 * h^2 + * y3 = r * (u1 * h^2 - x3) - s1 * h^3 + * z3 = h * z1 * z2 + * + * If both P1 and P2 are infinity, then z1 == 0 and z2 == 0, implying that + * z3 == 0, so the result is correct. + * If either of P1 or P2 is infinity, but not both, then z3 == 0, which is + * not correct. + * h == 0 only if u1 == u2; this happens in two cases: + * -- if s1 == s2 then P1 and/or P2 is infinity, or P1 == P2 + * -- if s1 != s2 then P1 + P2 == infinity (but neither P1 or P2 is infinity) + * + * Thus, the following situations are not handled correctly: + * -- P1 = 0 and P2 != 0 + * -- P1 != 0 and P2 = 0 + * -- P1 = P2 + * All other cases are properly computed. However, even in "incorrect" + * situations, the three coordinates still are properly formed field + * elements. + * + * The returned flag is cleared if r == 0. This happens in the following + * cases: + * -- Both points are on the same horizontal line (same Y coordinate). + * -- Both points are infinity. + * -- One point is infinity and the other is on line Y = 0. + * The third case cannot happen with our curves (there is no valid point + * on line Y = 0 since that would be a point of order 2). If the two + * source points are non-infinity, then remains only the case where the + * two points are on the same horizontal line. + * + * This allows us to detect the "P1 == P2" case, assuming that P1 != 0 and + * P2 != 0: + * -- If the returned value is not the point at infinity, then it was properly + * computed. + * -- Otherwise, if the returned flag is 1, then P1+P2 = 0, and the result + * is indeed the point at infinity. + * -- Otherwise (result is infinity, flag is 0), then P1 = P2 and we should + * use the 'double' code. + * + * Cost: 16 multiplications + */ +static const uint16_t code_add[] PROGMEM = { + /* + * Compute u1 = x1*z2^2 (in t1) and s1 = y1*z2^3 (in t3). + */ + MMUL(t3, P2z, P2z), + MMUL(t1, P1x, t3), + MMUL(t4, P2z, t3), + MMUL(t3, P1y, t4), + + /* + * Compute u2 = x2*z1^2 (in t2) and s2 = y2*z1^3 (in t4). + */ + MMUL(t4, P1z, P1z), + MMUL(t2, P2x, t4), + MMUL(t5, P1z, t4), + MMUL(t4, P2y, t5), + + /* + * Compute h = u2 - u1 (in t2) and r = s2 - s1 (in t4). + */ + MSUB(t2, t1), + MSUB(t4, t3), + + /* + * Report cases where r = 0 through the returned flag. + */ + MTZ(t4), + + /* + * Compute u1*h^2 (in t6) and h^3 (in t5). + */ + MMUL(t7, t2, t2), + MMUL(t6, t1, t7), + MMUL(t5, t7, t2), + + /* + * Compute x3 = r^2 - h^3 - 2*u1*h^2. + * t1 and t7 can be used as scratch registers. + */ + MMUL(P1x, t4, t4), + MSUB(P1x, t5), + MSUB(P1x, t6), + MSUB(P1x, t6), + + /* + * Compute y3 = r*(u1*h^2 - x3) - s1*h^3. + */ + MSUB(t6, P1x), + MMUL(P1y, t4, t6), + MMUL(t1, t5, t3), + MSUB(P1y, t1), + + /* + * Compute z3 = h*z1*z2. + */ + MMUL(t1, P1z, P2z), + MMUL(P1z, t1, t2), + + ENDCODE +}; + +/* + * Check that the point is on the curve. This code snippet assumes the + * following conventions: + * -- Coordinates x and y have been freshly decoded in P1 (but not + * converted to Montgomery coordinates yet). + * -- P2x, P2y and P2z are set to, respectively, R^2, b*R and 1. + */ +static const uint16_t code_check[] PROGMEM = { + + /* Convert x and y to Montgomery representation. */ + MMUL(t1, P1x, P2x), + MMUL(t2, P1y, P2x), + MSET(P1x, t1), + MSET(P1y, t2), + + /* Compute x^3 in t1. */ + MMUL(t2, P1x, P1x), + MMUL(t1, P1x, t2), + + /* Subtract 3*x from t1. */ + MSUB(t1, P1x), + MSUB(t1, P1x), + MSUB(t1, P1x), + + /* Add b. */ + MADD(t1, P2y), + + /* Compute y^2 in t2. */ + MMUL(t2, P1y, P1y), + + /* Compare y^2 with x^3 - 3*x + b; they must match. */ + MSUB(t1, t2), + MTZ(t1), + + /* Set z to 1 (in Montgomery representation). */ + MMUL(P1z, P2x, P2z), + + ENDCODE +}; + +/* + * Conversion back to affine coordinates. This code snippet assumes that + * the z coordinate of P2 is set to 1 (not in Montgomery representation). + */ +static const uint16_t code_affine[] PROGMEM = { + + /* Save z*R in t1. */ + MSET(t1, P1z), + + /* Compute z^3 in t2. */ + MMUL(t2, P1z, P1z), + MMUL(t3, P1z, t2), + MMUL(t2, t3, P2z), + + /* Invert to (1/z^3) in t2. */ + MINV(t2, t3, t4), + + /* Compute y. */ + MSET(t3, P1y), + MMUL(P1y, t2, t3), + + /* Compute (1/z^2) in t3. */ + MMUL(t3, t2, t1), + + /* Compute x. */ + MSET(t2, P1x), + MMUL(P1x, t2, t3), + + ENDCODE +}; + +static uint32_t +run_code(jacobian *P1, const jacobian *P2, + const curve_params *cc, const uint16_t *code) +{ + uint32_t r; + uint16_t t[13][I15_LEN]; + size_t u; + + r = 1; + + /* + * Copy the two operands in the dedicated registers. + */ + memcpy(t[P1x], P1->c, 3 * I15_LEN * sizeof(uint16_t)); + memcpy(t[P2x], P2->c, 3 * I15_LEN * sizeof(uint16_t)); + + optimistic_yield(10000); + + /* + * Run formulas. + */ + for (u = 0;; u ++) { + unsigned op, d, a, b; + + op = pgm_read_word(&code[u]); + if (op == 0) { + break; + } + d = (op >> 8) & 0x0F; + a = (op >> 4) & 0x0F; + b = op & 0x0F; + op >>= 12; + switch (op) { + uint32_t ctl; + size_t plen; + unsigned char tp[(BR_MAX_EC_SIZE + 7) >> 3]; + + case 0: + memcpy(t[d], t[a], I15_LEN * sizeof(uint16_t)); + break; + case 1: + ctl = br_i15_add(t[d], t[a], 1); + ctl |= NOT(br_i15_sub(t[d], cc->p, 0)); + br_i15_sub(t[d], cc->p, ctl); + break; + case 2: + br_i15_add(t[d], cc->p, br_i15_sub(t[d], t[a], 1)); + break; + case 3: + br_i15_montymul(t[d], t[a], t[b], cc->p, cc->p0i); + break; + case 4: + plen = (pgm_read_word(&cc->p[0]) - (pgm_read_word(&cc->p[0]) >> 4) + 7) >> 3; + br_i15_encode(tp, plen, cc->p); + tp[plen - 1] -= 2; + br_i15_modpow(t[d], tp, plen, + cc->p, cc->p0i, t[a], t[b]); + break; + default: + r &= ~br_i15_iszero(t[d]); + break; + } + } + + /* + * Copy back result. + */ + memcpy(P1->c, t[P1x], 3 * I15_LEN * sizeof(uint16_t)); + return r; +} + +static void +set_one(uint16_t *x, const uint16_t *p) +{ + size_t plen; + + plen = (pgm_read_word(&p[0]) + 31) >> 4; + memset(x, 0, plen * sizeof *x); + x[0] = pgm_read_word(&p[0]); + x[1] = 0x0001; +} + +static void +point_zero(jacobian *P, const curve_params *cc) +{ + memset(P, 0, sizeof *P); + P->c[0][0] = P->c[1][0] = P->c[2][0] = pgm_read_word(&cc->p[0]); +} + +static inline void +point_double(jacobian *P, const curve_params *cc) +{ + run_code(P, P, cc, code_double); +} + +static inline uint32_t +point_add(jacobian *P1, const jacobian *P2, const curve_params *cc) +{ + return run_code(P1, P2, cc, code_add); +} + +static void +point_mul(jacobian *P, const unsigned char *x, size_t xlen, + const curve_params *cc) +{ + /* + * We do a simple double-and-add ladder with a 2-bit window + * to make only one add every two doublings. We thus first + * precompute 2P and 3P in some local buffers. + * + * We always perform two doublings and one addition; the + * addition is with P, 2P and 3P and is done in a temporary + * array. + * + * The addition code cannot handle cases where one of the + * operands is infinity, which is the case at the start of the + * ladder. We therefore need to maintain a flag that controls + * this situation. + */ + uint32_t qz; + jacobian P2, P3, Q, T, U; + + memcpy(&P2, P, sizeof P2); + point_double(&P2, cc); + memcpy(&P3, P, sizeof P3); + point_add(&P3, &P2, cc); + + point_zero(&Q, cc); + qz = 1; + while (xlen -- > 0) { + int k; + + for (k = 6; k >= 0; k -= 2) { + uint32_t bits; + uint32_t bnz; + + point_double(&Q, cc); + point_double(&Q, cc); + memcpy(&T, P, sizeof T); + memcpy(&U, &Q, sizeof U); + bits = (pgm_read_byte(&*x) >> k) & (uint32_t)3; + bnz = NEQ(bits, 0); + CCOPY(EQ(bits, 2), &T, &P2, sizeof T); + CCOPY(EQ(bits, 3), &T, &P3, sizeof T); + point_add(&U, &T, cc); + CCOPY(bnz & qz, &Q, &T, sizeof Q); + CCOPY(bnz & ~qz, &Q, &U, sizeof Q); + qz &= ~bnz; + } + x ++; + } + memcpy(P, &Q, sizeof Q); +} + +/* + * Decode point into Jacobian coordinates. This function does not support + * the point at infinity. If the point is invalid then this returns 0, but + * the coordinates are still set to properly formed field elements. + */ +static uint32_t +point_decode(jacobian *P, const void *src, size_t len, const curve_params *cc) +{ + /* + * Points must use uncompressed format: + * -- first byte is 0x04; + * -- coordinates X and Y use unsigned big-endian, with the same + * length as the field modulus. + * + * We don't support hybrid format (uncompressed, but first byte + * has value 0x06 or 0x07, depending on the least significant bit + * of Y) because it is rather useless, and explicitly forbidden + * by PKIX (RFC 5480, section 2.2). + * + * We don't support compressed format either, because it is not + * much used in practice (there are or were patent-related + * concerns about point compression, which explains the lack of + * generalised support). Also, point compression support would + * need a bit more code. + */ + const unsigned char *buf; + size_t plen, zlen; + uint32_t r; + jacobian Q; + + buf = src; + point_zero(P, cc); + plen = (pgm_read_word(&cc->p[0]) - (pgm_read_word(&cc->p[0]) >> 4) + 7) >> 3; + if (len != 1 + (plen << 1)) { + return 0; + } + r = br_i15_decode_mod(P->c[0], buf + 1, plen, cc->p); + r &= br_i15_decode_mod(P->c[1], buf + 1 + plen, plen, cc->p); + + /* + * Check first byte. + */ + r &= EQ(pgm_read_byte(&buf[0]), 0x04); + /* obsolete + r &= EQ(buf[0], 0x04) | (EQ(buf[0] & 0xFE, 0x06) + & ~(uint32_t)(buf[0] ^ buf[plen << 1])); + */ + + /* + * Convert coordinates and check that the point is valid. + */ + zlen = ((pgm_read_word(&cc->p[0]) + 31) >> 4) * sizeof(uint16_t); + memcpy_P(Q.c[0], cc->R2, zlen); + memcpy_P(Q.c[1], cc->b, zlen); + set_one(Q.c[2], cc->p); + r &= ~run_code(P, &Q, cc, code_check); + return r; +} + +/* + * Encode a point. This method assumes that the point is correct and is + * not the point at infinity. Encoded size is always 1+2*plen, where + * plen is the field modulus length, in bytes. + */ +static void +point_encode(void *dst, const jacobian *P, const curve_params *cc) +{ + unsigned char *buf; + size_t plen; + jacobian Q, T; + + buf = dst; + plen = (pgm_read_word(&cc->p[0]) - (pgm_read_word(&cc->p[0]) >> 4) + 7) >> 3; + buf[0] = 0x04; + memcpy(&Q, P, sizeof *P); + set_one(T.c[2], cc->p); + run_code(&Q, &T, cc, code_affine); + br_i15_encode(buf + 1, plen, Q.c[0]); + br_i15_encode(buf + 1 + plen, plen, Q.c[1]); +} + +static const br_ec_curve_def * +id_to_curve_def(int curve) +{ + switch (curve) { + case BR_EC_secp256r1: + return &br_secp256r1; + case BR_EC_secp384r1: + return &br_secp384r1; + case BR_EC_secp521r1: + return &br_secp521r1; + } + return NULL; +} + +static const unsigned char * +api_generator(int curve, size_t *len) +{ + const br_ec_curve_def *cd; + + cd = id_to_curve_def(curve); + *len = cd->generator_len; + return cd->generator; +} + +static const unsigned char * +api_order(int curve, size_t *len) +{ + const br_ec_curve_def *cd; + + cd = id_to_curve_def(curve); + *len = cd->order_len; + return cd->order; +} + +static size_t +api_xoff(int curve, size_t *len) +{ + api_generator(curve, len); + *len >>= 1; + return 1; +} + +static uint32_t +api_mul(unsigned char *G, size_t Glen, + const unsigned char *x, size_t xlen, int curve) +{ + uint32_t r; + const curve_params *cc; + jacobian P; + + cc = id_to_curve(curve); + r = point_decode(&P, G, Glen, cc); + point_mul(&P, x, xlen, cc); + if (Glen == cc->point_len) { + point_encode(G, &P, cc); + } + return r; +} + +static size_t +api_mulgen(unsigned char *R, + const unsigned char *x, size_t xlen, int curve) +{ + const unsigned char *G; + size_t Glen; + + G = api_generator(curve, &Glen); + memcpy_P(R, G, Glen); + api_mul(R, Glen, x, xlen, curve); + return Glen; +} + +static uint32_t +api_muladd(unsigned char *A, const unsigned char *B, size_t len, + const unsigned char *x, size_t xlen, + const unsigned char *y, size_t ylen, int curve) +{ + uint32_t r, t, z; + const curve_params *cc; + jacobian P, Q; + + /* + * TODO: see about merging the two ladders. Right now, we do + * two independent point multiplications, which is a bit + * wasteful of CPU resources (but yields short code). + */ + + cc = id_to_curve(curve); + r = point_decode(&P, A, len, cc); + if (B == NULL) { + size_t Glen; + + B = api_generator(curve, &Glen); + } + r &= point_decode(&Q, B, len, cc); + point_mul(&P, x, xlen, cc); + point_mul(&Q, y, ylen, cc); + + /* + * We want to compute P+Q. Since the base points A and B are distinct + * from infinity, and the multipliers are non-zero and lower than the + * curve order, then we know that P and Q are non-infinity. This + * leaves two special situations to test for: + * -- If P = Q then we must use point_double(). + * -- If P+Q = 0 then we must report an error. + */ + t = point_add(&P, &Q, cc); + point_double(&Q, cc); + z = br_i15_iszero(P.c[2]); + + /* + * If z is 1 then either P+Q = 0 (t = 1) or P = Q (t = 0). So we + * have the following: + * + * z = 0, t = 0 return P (normal addition) + * z = 0, t = 1 return P (normal addition) + * z = 1, t = 0 return Q (a 'double' case) + * z = 1, t = 1 report an error (P+Q = 0) + */ + CCOPY(z & ~t, &P, &Q, sizeof Q); + point_encode(A, &P, cc); + r &= ~(z & t); + + return r; +} + +/* see bearssl_ec.h */ +const br_ec_impl br_ec_prime_i15 PROGMEM = { + (uint32_t)0x03800000, + &api_generator, + &api_order, + &api_xoff, + &api_mul, + &api_mulgen, + &api_muladd +}; diff --git a/lib/bearssl-esp8266/src/ec/ec_pubkey.c b/lib/bearssl-esp8266/src/ec/ec_pubkey.c new file mode 100644 index 000000000..638937f84 --- /dev/null +++ b/lib/bearssl-esp8266/src/ec/ec_pubkey.c @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +static const unsigned char POINT_LEN[] PROGMEM = { + 0, /* 0: not a valid curve ID */ + 43, /* sect163k1 */ + 43, /* sect163r1 */ + 43, /* sect163r2 */ + 51, /* sect193r1 */ + 51, /* sect193r2 */ + 61, /* sect233k1 */ + 61, /* sect233r1 */ + 61, /* sect239k1 */ + 73, /* sect283k1 */ + 73, /* sect283r1 */ + 105, /* sect409k1 */ + 105, /* sect409r1 */ + 145, /* sect571k1 */ + 145, /* sect571r1 */ + 41, /* secp160k1 */ + 41, /* secp160r1 */ + 41, /* secp160r2 */ + 49, /* secp192k1 */ + 49, /* secp192r1 */ + 57, /* secp224k1 */ + 57, /* secp224r1 */ + 65, /* secp256k1 */ + 65, /* secp256r1 */ + 97, /* secp384r1 */ + 133, /* secp521r1 */ + 65, /* brainpoolP256r1 */ + 97, /* brainpoolP384r1 */ + 129, /* brainpoolP512r1 */ + 32, /* curve25519 */ + 56, /* curve448 */ +}; + +/* see bearssl_ec.h */ +size_t +br_ec_compute_pub(const br_ec_impl *impl, br_ec_public_key *pk, + void *kbuf, const br_ec_private_key *sk) +{ + int curve; + size_t len; + + curve = sk->curve; + if (curve < 0 || curve >= 32 || curve >= (int)(sizeof POINT_LEN) + || ((impl->supported_curves >> curve) & 1) == 0) + { + return 0; + } + if (kbuf == NULL) { + return pgm_read_byte(&POINT_LEN[curve]); + } + len = impl->mulgen(kbuf, sk->x, sk->xlen, curve); + if (pk != NULL) { + pk->curve = curve; + pk->q = kbuf; + pk->qlen = len; + } + return len; +} diff --git a/lib/bearssl-esp8266/src/ec/ec_secp256r1.c b/lib/bearssl-esp8266/src/ec/ec_secp256r1.c new file mode 100644 index 000000000..70c856a04 --- /dev/null +++ b/lib/bearssl-esp8266/src/ec/ec_secp256r1.c @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +static const unsigned char P256_N[] PROGMEM = { + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xBC, 0xE6, 0xFA, 0xAD, 0xA7, 0x17, 0x9E, 0x84, + 0xF3, 0xB9, 0xCA, 0xC2, 0xFC, 0x63, 0x25, 0x51 +}; + +static const unsigned char P256_G[] PROGMEM = { + 0x04, 0x6B, 0x17, 0xD1, 0xF2, 0xE1, 0x2C, 0x42, + 0x47, 0xF8, 0xBC, 0xE6, 0xE5, 0x63, 0xA4, 0x40, + 0xF2, 0x77, 0x03, 0x7D, 0x81, 0x2D, 0xEB, 0x33, + 0xA0, 0xF4, 0xA1, 0x39, 0x45, 0xD8, 0x98, 0xC2, + 0x96, 0x4F, 0xE3, 0x42, 0xE2, 0xFE, 0x1A, 0x7F, + 0x9B, 0x8E, 0xE7, 0xEB, 0x4A, 0x7C, 0x0F, 0x9E, + 0x16, 0x2B, 0xCE, 0x33, 0x57, 0x6B, 0x31, 0x5E, + 0xCE, 0xCB, 0xB6, 0x40, 0x68, 0x37, 0xBF, 0x51, + 0xF5 +}; + +/* see inner.h */ +const br_ec_curve_def br_secp256r1 PROGMEM = { + BR_EC_secp256r1, + P256_N, sizeof P256_N, + P256_G, sizeof P256_G +}; diff --git a/lib/bearssl-esp8266/src/ec/ec_secp384r1.c b/lib/bearssl-esp8266/src/ec/ec_secp384r1.c new file mode 100644 index 000000000..2e6d63709 --- /dev/null +++ b/lib/bearssl-esp8266/src/ec/ec_secp384r1.c @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +static const unsigned char P384_N[] PROGMEM = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xC7, 0x63, 0x4D, 0x81, 0xF4, 0x37, 0x2D, 0xDF, + 0x58, 0x1A, 0x0D, 0xB2, 0x48, 0xB0, 0xA7, 0x7A, + 0xEC, 0xEC, 0x19, 0x6A, 0xCC, 0xC5, 0x29, 0x73 +}; + +static const unsigned char P384_G[] PROGMEM = { + 0x04, 0xAA, 0x87, 0xCA, 0x22, 0xBE, 0x8B, 0x05, + 0x37, 0x8E, 0xB1, 0xC7, 0x1E, 0xF3, 0x20, 0xAD, + 0x74, 0x6E, 0x1D, 0x3B, 0x62, 0x8B, 0xA7, 0x9B, + 0x98, 0x59, 0xF7, 0x41, 0xE0, 0x82, 0x54, 0x2A, + 0x38, 0x55, 0x02, 0xF2, 0x5D, 0xBF, 0x55, 0x29, + 0x6C, 0x3A, 0x54, 0x5E, 0x38, 0x72, 0x76, 0x0A, + 0xB7, 0x36, 0x17, 0xDE, 0x4A, 0x96, 0x26, 0x2C, + 0x6F, 0x5D, 0x9E, 0x98, 0xBF, 0x92, 0x92, 0xDC, + 0x29, 0xF8, 0xF4, 0x1D, 0xBD, 0x28, 0x9A, 0x14, + 0x7C, 0xE9, 0xDA, 0x31, 0x13, 0xB5, 0xF0, 0xB8, + 0xC0, 0x0A, 0x60, 0xB1, 0xCE, 0x1D, 0x7E, 0x81, + 0x9D, 0x7A, 0x43, 0x1D, 0x7C, 0x90, 0xEA, 0x0E, + 0x5F +}; + +/* see inner.h */ +const br_ec_curve_def br_secp384r1 PROGMEM = { + BR_EC_secp384r1, + P384_N, sizeof P384_N, + P384_G, sizeof P384_G +}; diff --git a/lib/bearssl-esp8266/src/ec/ec_secp521r1.c b/lib/bearssl-esp8266/src/ec/ec_secp521r1.c new file mode 100644 index 000000000..4c082481d --- /dev/null +++ b/lib/bearssl-esp8266/src/ec/ec_secp521r1.c @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +static const unsigned char P521_N[] PROGMEM = { + 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFA, 0x51, 0x86, 0x87, 0x83, 0xBF, 0x2F, + 0x96, 0x6B, 0x7F, 0xCC, 0x01, 0x48, 0xF7, 0x09, + 0xA5, 0xD0, 0x3B, 0xB5, 0xC9, 0xB8, 0x89, 0x9C, + 0x47, 0xAE, 0xBB, 0x6F, 0xB7, 0x1E, 0x91, 0x38, + 0x64, 0x09 +}; + +static const unsigned char P521_G[] PROGMEM = { + 0x04, 0x00, 0xC6, 0x85, 0x8E, 0x06, 0xB7, 0x04, + 0x04, 0xE9, 0xCD, 0x9E, 0x3E, 0xCB, 0x66, 0x23, + 0x95, 0xB4, 0x42, 0x9C, 0x64, 0x81, 0x39, 0x05, + 0x3F, 0xB5, 0x21, 0xF8, 0x28, 0xAF, 0x60, 0x6B, + 0x4D, 0x3D, 0xBA, 0xA1, 0x4B, 0x5E, 0x77, 0xEF, + 0xE7, 0x59, 0x28, 0xFE, 0x1D, 0xC1, 0x27, 0xA2, + 0xFF, 0xA8, 0xDE, 0x33, 0x48, 0xB3, 0xC1, 0x85, + 0x6A, 0x42, 0x9B, 0xF9, 0x7E, 0x7E, 0x31, 0xC2, + 0xE5, 0xBD, 0x66, 0x01, 0x18, 0x39, 0x29, 0x6A, + 0x78, 0x9A, 0x3B, 0xC0, 0x04, 0x5C, 0x8A, 0x5F, + 0xB4, 0x2C, 0x7D, 0x1B, 0xD9, 0x98, 0xF5, 0x44, + 0x49, 0x57, 0x9B, 0x44, 0x68, 0x17, 0xAF, 0xBD, + 0x17, 0x27, 0x3E, 0x66, 0x2C, 0x97, 0xEE, 0x72, + 0x99, 0x5E, 0xF4, 0x26, 0x40, 0xC5, 0x50, 0xB9, + 0x01, 0x3F, 0xAD, 0x07, 0x61, 0x35, 0x3C, 0x70, + 0x86, 0xA2, 0x72, 0xC2, 0x40, 0x88, 0xBE, 0x94, + 0x76, 0x9F, 0xD1, 0x66, 0x50 +}; + +/* see inner.h */ +const br_ec_curve_def br_secp521r1 PROGMEM = { + BR_EC_secp521r1, + P521_N, sizeof P521_N, + P521_G, sizeof P521_G +}; diff --git a/lib/bearssl-esp8266/src/ec/ecdsa_atr.c b/lib/bearssl-esp8266/src/ec/ecdsa_atr.c new file mode 100644 index 000000000..e5b61f05d --- /dev/null +++ b/lib/bearssl-esp8266/src/ec/ecdsa_atr.c @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_ec.h */ +size_t +br_ecdsa_asn1_to_raw(void *sig, size_t sig_len) +{ + /* + * Note: this code is a bit lenient in that it accepts a few + * deviations to DER with regards to minimality of encoding of + * lengths and integer values. These deviations are still + * unambiguous. + * + * Signature format is a SEQUENCE of two INTEGER values. We + * support only integers of less than 127 bytes each (signed + * encoding) so the resulting raw signature will have length + * at most 254 bytes. + */ + + unsigned char *buf, *r, *s; + size_t zlen, rlen, slen, off; + unsigned char tmp[254]; + + buf = sig; + if (sig_len < 8) { + return 0; + } + + /* + * First byte is SEQUENCE tag. + */ + if (buf[0] != 0x30) { + return 0; + } + + /* + * The SEQUENCE length will be encoded over one or two bytes. We + * limit the total SEQUENCE contents to 255 bytes, because it + * makes things simpler; this is enough for subgroup orders up + * to 999 bits. + */ + zlen = buf[1]; + if (zlen > 0x80) { + if (zlen != 0x81) { + return 0; + } + zlen = buf[2]; + if (zlen != sig_len - 3) { + return 0; + } + off = 3; + } else { + if (zlen != sig_len - 2) { + return 0; + } + off = 2; + } + + /* + * First INTEGER (r). + */ + if (buf[off ++] != 0x02) { + return 0; + } + rlen = buf[off ++]; + if (rlen >= 0x80) { + return 0; + } + r = buf + off; + off += rlen; + + /* + * Second INTEGER (s). + */ + if (off + 2 > sig_len) { + return 0; + } + if (buf[off ++] != 0x02) { + return 0; + } + slen = buf[off ++]; + if (slen >= 0x80 || slen != sig_len - off) { + return 0; + } + s = buf + off; + + /* + * Removing leading zeros from r and s. + */ + while (rlen > 0 && *r == 0) { + rlen --; + r ++; + } + while (slen > 0 && *s == 0) { + slen --; + s ++; + } + + /* + * Compute common length for the two integers, then copy integers + * into the temporary buffer, and finally copy it back over the + * signature buffer. + */ + zlen = rlen > slen ? rlen : slen; + sig_len = zlen << 1; + memset(tmp, 0, sig_len); + memcpy(tmp + zlen - rlen, r, rlen); + memcpy(tmp + sig_len - slen, s, slen); + memcpy(sig, tmp, sig_len); + return sig_len; +} diff --git a/lib/bearssl-esp8266/src/ec/ecdsa_default_sign_asn1.c b/lib/bearssl-esp8266/src/ec/ecdsa_default_sign_asn1.c new file mode 100644 index 000000000..c07ad2aff --- /dev/null +++ b/lib/bearssl-esp8266/src/ec/ecdsa_default_sign_asn1.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_ec.h */ +br_ecdsa_sign +br_ecdsa_sign_asn1_get_default(void) +{ +#if BR_LOMUL + return &br_ecdsa_i15_sign_asn1; +#else + return &br_ecdsa_i31_sign_asn1; +#endif +} diff --git a/lib/bearssl-esp8266/src/ec/ecdsa_default_sign_raw.c b/lib/bearssl-esp8266/src/ec/ecdsa_default_sign_raw.c new file mode 100644 index 000000000..cbaa9b80f --- /dev/null +++ b/lib/bearssl-esp8266/src/ec/ecdsa_default_sign_raw.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_ec.h */ +br_ecdsa_sign +br_ecdsa_sign_raw_get_default(void) +{ +#if BR_LOMUL + return &br_ecdsa_i15_sign_raw; +#else + return &br_ecdsa_i31_sign_raw; +#endif +} diff --git a/lib/bearssl-esp8266/src/ec/ecdsa_default_vrfy_asn1.c b/lib/bearssl-esp8266/src/ec/ecdsa_default_vrfy_asn1.c new file mode 100644 index 000000000..0a82bc413 --- /dev/null +++ b/lib/bearssl-esp8266/src/ec/ecdsa_default_vrfy_asn1.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_ec.h */ +br_ecdsa_vrfy +br_ecdsa_vrfy_asn1_get_default(void) +{ +#if BR_LOMUL + return &br_ecdsa_i15_vrfy_asn1; +#else + return &br_ecdsa_i31_vrfy_asn1; +#endif +} diff --git a/lib/bearssl-esp8266/src/ec/ecdsa_default_vrfy_raw.c b/lib/bearssl-esp8266/src/ec/ecdsa_default_vrfy_raw.c new file mode 100644 index 000000000..833e79f16 --- /dev/null +++ b/lib/bearssl-esp8266/src/ec/ecdsa_default_vrfy_raw.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_ec.h */ +br_ecdsa_vrfy +br_ecdsa_vrfy_raw_get_default(void) +{ +#if BR_LOMUL + return &br_ecdsa_i15_vrfy_raw; +#else + return &br_ecdsa_i31_vrfy_raw; +#endif +} diff --git a/lib/bearssl-esp8266/src/ec/ecdsa_i15_bits.c b/lib/bearssl-esp8266/src/ec/ecdsa_i15_bits.c new file mode 100644 index 000000000..e1e754d3f --- /dev/null +++ b/lib/bearssl-esp8266/src/ec/ecdsa_i15_bits.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +void +br_ecdsa_i15_bits2int(uint16_t *x, + const void *src, size_t len, uint32_t ebitlen) +{ + uint32_t bitlen, hbitlen; + int sc; + + bitlen = ebitlen - (ebitlen >> 4); + hbitlen = (uint32_t)len << 3; + if (hbitlen > bitlen) { + len = (bitlen + 7) >> 3; + sc = (int)((hbitlen - bitlen) & 7); + } else { + sc = 0; + } + br_i15_zero(x, ebitlen); + br_i15_decode(x, src, len); + br_i15_rshift(x, sc); + x[0] = ebitlen; +} diff --git a/lib/bearssl-esp8266/src/ec/ecdsa_i15_sign_asn1.c b/lib/bearssl-esp8266/src/ec/ecdsa_i15_sign_asn1.c new file mode 100644 index 000000000..6770e7702 --- /dev/null +++ b/lib/bearssl-esp8266/src/ec/ecdsa_i15_sign_asn1.c @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +#define ORDER_LEN ((BR_MAX_EC_SIZE + 7) >> 3) + +/* see bearssl_ec.h */ +size_t +br_ecdsa_i15_sign_asn1(const br_ec_impl *impl, + const br_hash_class *hf, const void *hash_value, + const br_ec_private_key *sk, void *sig) +{ + unsigned char rsig[(ORDER_LEN << 1) + 12]; + size_t sig_len; + + sig_len = br_ecdsa_i15_sign_raw(impl, hf, hash_value, sk, rsig); + if (sig_len == 0) { + return 0; + } + sig_len = br_ecdsa_raw_to_asn1(rsig, sig_len); + memcpy(sig, rsig, sig_len); + return sig_len; +} diff --git a/lib/bearssl-esp8266/src/ec/ecdsa_i15_sign_raw.c b/lib/bearssl-esp8266/src/ec/ecdsa_i15_sign_raw.c new file mode 100644 index 000000000..83f258741 --- /dev/null +++ b/lib/bearssl-esp8266/src/ec/ecdsa_i15_sign_raw.c @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +#define I15_LEN ((BR_MAX_EC_SIZE + 29) / 15) +#define POINT_LEN (1 + (((BR_MAX_EC_SIZE + 7) >> 3) << 1)) +#define ORDER_LEN ((BR_MAX_EC_SIZE + 7) >> 3) + +/* see bearssl_ec.h */ +size_t +br_ecdsa_i15_sign_raw(const br_ec_impl *impl, + const br_hash_class *hf, const void *hash_value, + const br_ec_private_key *sk, void *sig) +{ + /* + * IMPORTANT: this code is fit only for curves with a prime + * order. This is needed so that modular reduction of the X + * coordinate of a point can be done with a simple subtraction. + * We also rely on the last byte of the curve order to be distinct + * from 0 and 1. + */ + const br_ec_curve_def *cd; + uint16_t n[I15_LEN], r[I15_LEN], s[I15_LEN], x[I15_LEN]; + uint16_t m[I15_LEN], k[I15_LEN], t1[I15_LEN], t2[I15_LEN]; + unsigned char tt[ORDER_LEN << 1]; + unsigned char eU[POINT_LEN]; + size_t hash_len, nlen, ulen; + uint16_t n0i; + uint32_t ctl; + br_hmac_drbg_context drbg; + + /* + * If the curve is not supported, then exit with an error. + */ + if (((impl->supported_curves >> sk->curve) & 1) == 0) { + return 0; + } + + /* + * Get the curve parameters (generator and order). + */ + switch (sk->curve) { + case BR_EC_secp256r1: + cd = &br_secp256r1; + break; + case BR_EC_secp384r1: + cd = &br_secp384r1; + break; + case BR_EC_secp521r1: + cd = &br_secp521r1; + break; + default: + return 0; + } + + /* + * Get modulus. + */ + nlen = cd->order_len; + br_i15_decode(n, cd->order, nlen); + n0i = br_i15_ninv15(n[1]); + + /* + * Get private key as an i15 integer. This also checks that the + * private key is well-defined (not zero, and less than the + * curve order). + */ + if (!br_i15_decode_mod(x, sk->x, sk->xlen, n)) { + return 0; + } + if (br_i15_iszero(x)) { + return 0; + } + + /* + * Get hash length. + */ + hash_len = (hf->desc >> BR_HASHDESC_OUT_OFF) & BR_HASHDESC_OUT_MASK; + + /* + * Truncate and reduce the hash value modulo the curve order. + */ + br_ecdsa_i15_bits2int(m, hash_value, hash_len, n[0]); + br_i15_sub(m, n, br_i15_sub(m, n, 0) ^ 1); + + /* + * RFC 6979 generation of the "k" value. + * + * The process uses HMAC_DRBG (with the hash function used to + * process the message that is to be signed). The seed is the + * concatenation of the encodings of the private key and + * the hash value (after truncation and modular reduction). + */ + br_i15_encode(tt, nlen, x); + br_i15_encode(tt + nlen, nlen, m); + br_hmac_drbg_init(&drbg, hf, tt, nlen << 1); + for (;;) { + br_hmac_drbg_generate(&drbg, tt, nlen); + br_ecdsa_i15_bits2int(k, tt, nlen, n[0]); + if (br_i15_iszero(k)) { + continue; + } + if (br_i15_sub(k, n, 0)) { + break; + } + } + + /* + * Compute k*G and extract the X coordinate, then reduce it + * modulo the curve order. Since we support only curves with + * prime order, that reduction is only a matter of computing + * a subtraction. + */ + br_i15_encode(tt, nlen, k); + ulen = impl->mulgen(eU, tt, nlen, sk->curve); + br_i15_zero(r, n[0]); + br_i15_decode(r, &eU[1], ulen >> 1); + r[0] = n[0]; + br_i15_sub(r, n, br_i15_sub(r, n, 0) ^ 1); + + /* + * Compute 1/k in double-Montgomery representation. We do so by + * first converting _from_ Montgomery representation (twice), + * then using a modular exponentiation. + */ + br_i15_from_monty(k, n, n0i); + br_i15_from_monty(k, n, n0i); + memcpy_P(tt, cd->order, nlen); + tt[nlen - 1] -= 2; + br_i15_modpow(k, tt, nlen, n, n0i, t1, t2); + + /* + * Compute s = (m+xr)/k (mod n). + * The k[] array contains R^2/k (double-Montgomery representation); + * we thus can use direct Montgomery multiplications and conversions + * from Montgomery, avoiding any call to br_i15_to_monty() (which + * is slower). + */ + br_i15_from_monty(m, n, n0i); + br_i15_montymul(t1, x, r, n, n0i); + ctl = br_i15_add(t1, m, 1); + ctl |= br_i15_sub(t1, n, 0) ^ 1; + br_i15_sub(t1, n, ctl); + br_i15_montymul(s, t1, k, n, n0i); + + /* + * Encode r and s in the signature. + */ + br_i15_encode(sig, nlen, r); + br_i15_encode((unsigned char *)sig + nlen, nlen, s); + return nlen << 1; +} diff --git a/lib/bearssl-esp8266/src/ec/ecdsa_i15_vrfy_asn1.c b/lib/bearssl-esp8266/src/ec/ecdsa_i15_vrfy_asn1.c new file mode 100644 index 000000000..f213331c5 --- /dev/null +++ b/lib/bearssl-esp8266/src/ec/ecdsa_i15_vrfy_asn1.c @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +#define FIELD_LEN ((BR_MAX_EC_SIZE + 7) >> 3) + +/* see bearssl_ec.h */ +uint32_t +br_ecdsa_i15_vrfy_asn1(const br_ec_impl *impl, + const void *hash, size_t hash_len, + const br_ec_public_key *pk, + const void *sig, size_t sig_len) +{ + /* + * We use a double-sized buffer because a malformed ASN.1 signature + * may trigger a size expansion when converting to "raw" format. + */ + unsigned char rsig[(FIELD_LEN << 2) + 24]; + + if (sig_len > ((sizeof rsig) >> 1)) { + return 0; + } + memcpy(rsig, sig, sig_len); + sig_len = br_ecdsa_asn1_to_raw(rsig, sig_len); + return br_ecdsa_i15_vrfy_raw(impl, hash, hash_len, pk, rsig, sig_len); +} diff --git a/lib/bearssl-esp8266/src/ec/ecdsa_i15_vrfy_raw.c b/lib/bearssl-esp8266/src/ec/ecdsa_i15_vrfy_raw.c new file mode 100644 index 000000000..d2a16ac0e --- /dev/null +++ b/lib/bearssl-esp8266/src/ec/ecdsa_i15_vrfy_raw.c @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +#define I15_LEN ((BR_MAX_EC_SIZE + 29) / 15) +#define POINT_LEN (1 + (((BR_MAX_EC_SIZE + 7) >> 3) << 1)) + +/* see bearssl_ec.h */ +uint32_t +br_ecdsa_i15_vrfy_raw(const br_ec_impl *impl, + const void *hash, size_t hash_len, + const br_ec_public_key *pk, + const void *sig, size_t sig_len) +{ + /* + * IMPORTANT: this code is fit only for curves with a prime + * order. This is needed so that modular reduction of the X + * coordinate of a point can be done with a simple subtraction. + */ + const br_ec_curve_def *cd; + uint16_t n[I15_LEN], r[I15_LEN], s[I15_LEN], t1[I15_LEN], t2[I15_LEN]; + unsigned char tx[(BR_MAX_EC_SIZE + 7) >> 3]; + unsigned char ty[(BR_MAX_EC_SIZE + 7) >> 3]; + unsigned char eU[POINT_LEN]; + size_t nlen, rlen, ulen; + uint16_t n0i; + uint32_t res; + + /* + * If the curve is not supported, then report an error. + */ + if (((impl->supported_curves >> pk->curve) & 1) == 0) { + return 0; + } + + /* + * Get the curve parameters (generator and order). + */ + switch (pk->curve) { + case BR_EC_secp256r1: + cd = &br_secp256r1; + break; + case BR_EC_secp384r1: + cd = &br_secp384r1; + break; + case BR_EC_secp521r1: + cd = &br_secp521r1; + break; + default: + return 0; + } + + /* + * Signature length must be even. + */ + if (sig_len & 1) { + return 0; + } + rlen = sig_len >> 1; + + /* + * Public key point must have the proper size for this curve. + */ + if (pk->qlen != cd->generator_len) { + return 0; + } + + /* + * Get modulus; then decode the r and s values. They must be + * lower than the modulus, and s must not be null. + */ + nlen = cd->order_len; + br_i15_decode(n, cd->order, nlen); + n0i = br_i15_ninv15(n[1]); + if (!br_i15_decode_mod(r, sig, rlen, n)) { + return 0; + } + if (!br_i15_decode_mod(s, (const unsigned char *)sig + rlen, rlen, n)) { + return 0; + } + if (br_i15_iszero(s)) { + return 0; + } + + /* + * Invert s. We do that with a modular exponentiation; we use + * the fact that for all the curves we support, the least + * significant byte is not 0 or 1, so we can subtract 2 without + * any carry to process. + * We also want 1/s in Montgomery representation, which can be + * done by converting _from_ Montgomery representation before + * the inversion (because (1/s)*R = 1/(s/R)). + */ + br_i15_from_monty(s, n, n0i); + memcpy_P(tx, cd->order, nlen); + tx[nlen - 1] -= 2; + br_i15_modpow(s, tx, nlen, n, n0i, t1, t2); + + /* + * Truncate the hash to the modulus length (in bits) and reduce + * it modulo the curve order. The modular reduction can be done + * with a subtraction since the truncation already reduced the + * value to the modulus bit length. + */ + br_ecdsa_i15_bits2int(t1, hash, hash_len, n[0]); + br_i15_sub(t1, n, br_i15_sub(t1, n, 0) ^ 1); + + /* + * Multiply the (truncated, reduced) hash value with 1/s, result in + * t2, encoded in ty. + */ + br_i15_montymul(t2, t1, s, n, n0i); + br_i15_encode(ty, nlen, t2); + + /* + * Multiply r with 1/s, result in t1, encoded in tx. + */ + br_i15_montymul(t1, r, s, n, n0i); + br_i15_encode(tx, nlen, t1); + + /* + * Compute the point x*Q + y*G. + */ + ulen = cd->generator_len; + memcpy(eU, pk->q, ulen); + res = impl->muladd(eU, NULL, ulen, + tx, nlen, ty, nlen, cd->curve); + + /* + * Get the X coordinate, reduce modulo the curve order, and + * compare with the 'r' value. + * + * The modular reduction can be done with subtractions because + * we work with curves of prime order, so the curve order is + * close to the field order (Hasse's theorem). + */ + br_i15_zero(t1, n[0]); + br_i15_decode(t1, &eU[1], ulen >> 1); + t1[0] = n[0]; + br_i15_sub(t1, n, br_i15_sub(t1, n, 0) ^ 1); + res &= ~br_i15_sub(t1, r, 1); + res &= br_i15_iszero(t1); + return res; +} diff --git a/lib/bearssl-esp8266/src/ec/ecdsa_rta.c b/lib/bearssl-esp8266/src/ec/ecdsa_rta.c new file mode 100644 index 000000000..6550dc727 --- /dev/null +++ b/lib/bearssl-esp8266/src/ec/ecdsa_rta.c @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * Compute ASN.1 encoded length for the provided integer. The ASN.1 + * encoding is signed, so its leading bit must have value 0; it must + * also be of minimal length (so leading bytes of value 0 must be + * removed, except if that would contradict the rule about the sign + * bit). + */ +static size_t +asn1_int_length(const unsigned char *x, size_t xlen) +{ + while (xlen > 0 && *x == 0) { + x ++; + xlen --; + } + if (xlen == 0 || *x >= 0x80) { + xlen ++; + } + return xlen; +} + +/* see bearssl_ec.h */ +size_t +br_ecdsa_raw_to_asn1(void *sig, size_t sig_len) +{ + /* + * Internal buffer is large enough to accommodate a signature + * such that r and s fit on 125 bytes each (signed encoding), + * meaning a curve order of up to 999 bits. This is the limit + * that ensures "simple" length encodings. + */ + unsigned char *buf; + size_t hlen, rlen, slen, zlen, off; + unsigned char tmp[257]; + + buf = sig; + if ((sig_len & 1) != 0) { + return 0; + } + + /* + * Compute lengths for the two integers. + */ + hlen = sig_len >> 1; + rlen = asn1_int_length(buf, hlen); + slen = asn1_int_length(buf + hlen, hlen); + if (rlen > 125 || slen > 125) { + return 0; + } + + /* + * SEQUENCE header. + */ + tmp[0] = 0x30; + zlen = rlen + slen + 4; + if (zlen >= 0x80) { + tmp[1] = 0x81; + tmp[2] = zlen; + off = 3; + } else { + tmp[1] = zlen; + off = 2; + } + + /* + * First INTEGER (r). + */ + tmp[off ++] = 0x02; + tmp[off ++] = rlen; + if (rlen > hlen) { + tmp[off] = 0x00; + memcpy(tmp + off + 1, buf, hlen); + } else { + memcpy(tmp + off, buf + hlen - rlen, rlen); + } + off += rlen; + + /* + * Second INTEGER (s). + */ + tmp[off ++] = 0x02; + tmp[off ++] = slen; + if (slen > hlen) { + tmp[off] = 0x00; + memcpy(tmp + off + 1, buf + hlen, hlen); + } else { + memcpy(tmp + off, buf + sig_len - slen, slen); + } + off += slen; + + /* + * Return ASN.1 signature. + */ + memcpy(sig, tmp, off); + return off; +} diff --git a/lib/bearssl-esp8266/src/hash/dig_oid.c b/lib/bearssl-esp8266/src/hash/dig_oid.c new file mode 100644 index 000000000..f8cfd9937 --- /dev/null +++ b/lib/bearssl-esp8266/src/hash/dig_oid.c @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * This file contains the encoded OID for the standard hash functions. + * Such OID appear in, for instance, the PKCS#1 v1.5 padding for RSA + * signatures. + */ + +static const unsigned char md5_OID[] = { + 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x05 +}; + +static const unsigned char sha1_OID[] = { + 0x2B, 0x0E, 0x03, 0x02, 0x1A +}; + +static const unsigned char sha224_OID[] = { + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04 +}; + +static const unsigned char sha256_OID[] = { + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01 +}; + +static const unsigned char sha384_OID[] = { + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02 +}; + +static const unsigned char sha512_OID[] = { + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03 +}; + +/* see inner.h */ +const unsigned char * +br_digest_OID(int digest_id, size_t *len) +{ + switch (digest_id) { + case br_md5_ID: + *len = sizeof md5_OID; + return md5_OID; + case br_sha1_ID: + *len = sizeof sha1_OID; + return sha1_OID; + case br_sha224_ID: + *len = sizeof sha224_OID; + return sha224_OID; + case br_sha256_ID: + *len = sizeof sha256_OID; + return sha256_OID; + case br_sha384_ID: + *len = sizeof sha384_OID; + return sha384_OID; + case br_sha512_ID: + *len = sizeof sha512_OID; + return sha512_OID; + default: + *len = 0; + return NULL; + } +} diff --git a/lib/bearssl-esp8266/src/hash/dig_size.c b/lib/bearssl-esp8266/src/hash/dig_size.c new file mode 100644 index 000000000..bd3c8dbf7 --- /dev/null +++ b/lib/bearssl-esp8266/src/hash/dig_size.c @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +size_t +br_digest_size_by_ID(int digest_id) +{ + switch (digest_id) { + case br_md5sha1_ID: + return br_md5_SIZE + br_sha1_SIZE; + case br_md5_ID: + return br_md5_SIZE; + case br_sha1_ID: + return br_sha1_SIZE; + case br_sha224_ID: + return br_sha224_SIZE; + case br_sha256_ID: + return br_sha256_SIZE; + case br_sha384_ID: + return br_sha384_SIZE; + case br_sha512_ID: + return br_sha512_SIZE; + default: + /* abort(); */ + return 0; + } +} diff --git a/lib/bearssl-esp8266/src/hash/ghash_ctmul.c b/lib/bearssl-esp8266/src/hash/ghash_ctmul.c new file mode 100644 index 000000000..344830e3f --- /dev/null +++ b/lib/bearssl-esp8266/src/hash/ghash_ctmul.c @@ -0,0 +1,345 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * We compute "carryless multiplications" through normal integer + * multiplications, masking out enough bits to create "holes" in which + * carries may expand without altering our bits; we really use 8 data + * bits per 32-bit word, spaced every fourth bit. Accumulated carries + * may not exceed 8 in total, which fits in 4 bits. + * + * It would be possible to use a 3-bit spacing, allowing two operands, + * one with 7 non-zero data bits, the other one with 10 or 11 non-zero + * data bits; this asymmetric splitting makes the overall code more + * complex with thresholds and exceptions, and does not appear to be + * worth the effort. + */ + +/* + * We cannot really autodetect whether multiplications are "slow" or + * not. A typical example is the ARM Cortex M0+, which exists in two + * versions: one with a 1-cycle multiplication opcode, the other with + * a 32-cycle multiplication opcode. They both use exactly the same + * architecture and ABI, and cannot be distinguished from each other + * at compile-time. + * + * Since most modern CPU (even embedded CPU) still have fast + * multiplications, we use the "fast mul" code by default. + */ + +#if BR_SLOW_MUL + +/* + * This implementation uses Karatsuba-like reduction to make fewer + * integer multiplications (9 instead of 16), at the expense of extra + * logical operations (XOR, shifts...). On modern x86 CPU that offer + * fast, pipelined multiplications, this code is about twice slower than + * the simpler code with 16 multiplications. This tendency may be + * reversed on low-end platforms with expensive multiplications. + */ + +#define MUL32(h, l, x, y) do { \ + uint64_t mul32tmp = MUL(x, y); \ + (h) = (uint32_t)(mul32tmp >> 32); \ + (l) = (uint32_t)mul32tmp; \ + } while (0) + +static inline void +bmul(uint32_t *hi, uint32_t *lo, uint32_t x, uint32_t y) +{ + uint32_t x0, x1, x2, x3; + uint32_t y0, y1, y2, y3; + uint32_t a0, a1, a2, a3, a4, a5, a6, a7, a8; + uint32_t b0, b1, b2, b3, b4, b5, b6, b7, b8; + + x0 = x & (uint32_t)0x11111111; + x1 = x & (uint32_t)0x22222222; + x2 = x & (uint32_t)0x44444444; + x3 = x & (uint32_t)0x88888888; + y0 = y & (uint32_t)0x11111111; + y1 = y & (uint32_t)0x22222222; + y2 = y & (uint32_t)0x44444444; + y3 = y & (uint32_t)0x88888888; + + /* + * (x0+W*x1)*(y0+W*y1) -> a0:b0 + * (x2+W*x3)*(y2+W*y3) -> a3:b3 + * ((x0+x2)+W*(x1+x3))*((y0+y2)+W*(y1+y3)) -> a6:b6 + */ + a0 = x0; + b0 = y0; + a1 = x1 >> 1; + b1 = y1 >> 1; + a2 = a0 ^ a1; + b2 = b0 ^ b1; + a3 = x2 >> 2; + b3 = y2 >> 2; + a4 = x3 >> 3; + b4 = y3 >> 3; + a5 = a3 ^ a4; + b5 = b3 ^ b4; + a6 = a0 ^ a3; + b6 = b0 ^ b3; + a7 = a1 ^ a4; + b7 = b1 ^ b4; + a8 = a6 ^ a7; + b8 = b6 ^ b7; + + MUL32(b0, a0, b0, a0); + MUL32(b1, a1, b1, a1); + MUL32(b2, a2, b2, a2); + MUL32(b3, a3, b3, a3); + MUL32(b4, a4, b4, a4); + MUL32(b5, a5, b5, a5); + MUL32(b6, a6, b6, a6); + MUL32(b7, a7, b7, a7); + MUL32(b8, a8, b8, a8); + + a0 &= (uint32_t)0x11111111; + a1 &= (uint32_t)0x11111111; + a2 &= (uint32_t)0x11111111; + a3 &= (uint32_t)0x11111111; + a4 &= (uint32_t)0x11111111; + a5 &= (uint32_t)0x11111111; + a6 &= (uint32_t)0x11111111; + a7 &= (uint32_t)0x11111111; + a8 &= (uint32_t)0x11111111; + b0 &= (uint32_t)0x11111111; + b1 &= (uint32_t)0x11111111; + b2 &= (uint32_t)0x11111111; + b3 &= (uint32_t)0x11111111; + b4 &= (uint32_t)0x11111111; + b5 &= (uint32_t)0x11111111; + b6 &= (uint32_t)0x11111111; + b7 &= (uint32_t)0x11111111; + b8 &= (uint32_t)0x11111111; + + a2 ^= a0 ^ a1; + b2 ^= b0 ^ b1; + a0 ^= (a2 << 1) ^ (a1 << 2); + b0 ^= (b2 << 1) ^ (b1 << 2); + a5 ^= a3 ^ a4; + b5 ^= b3 ^ b4; + a3 ^= (a5 << 1) ^ (a4 << 2); + b3 ^= (b5 << 1) ^ (b4 << 2); + a8 ^= a6 ^ a7; + b8 ^= b6 ^ b7; + a6 ^= (a8 << 1) ^ (a7 << 2); + b6 ^= (b8 << 1) ^ (b7 << 2); + a6 ^= a0 ^ a3; + b6 ^= b0 ^ b3; + *lo = a0 ^ (a6 << 2) ^ (a3 << 4); + *hi = b0 ^ (b6 << 2) ^ (b3 << 4) ^ (a6 >> 30) ^ (a3 >> 28); +} + +#else + +/* + * Simple multiplication in GF(2)[X], using 16 integer multiplications. + */ + +static inline void +bmul(uint32_t *hi, uint32_t *lo, uint32_t x, uint32_t y) +{ + uint32_t x0, x1, x2, x3; + uint32_t y0, y1, y2, y3; + uint64_t z0, z1, z2, z3; + uint64_t z; + + x0 = x & (uint32_t)0x11111111; + x1 = x & (uint32_t)0x22222222; + x2 = x & (uint32_t)0x44444444; + x3 = x & (uint32_t)0x88888888; + y0 = y & (uint32_t)0x11111111; + y1 = y & (uint32_t)0x22222222; + y2 = y & (uint32_t)0x44444444; + y3 = y & (uint32_t)0x88888888; + z0 = MUL(x0, y0) ^ MUL(x1, y3) ^ MUL(x2, y2) ^ MUL(x3, y1); + z1 = MUL(x0, y1) ^ MUL(x1, y0) ^ MUL(x2, y3) ^ MUL(x3, y2); + z2 = MUL(x0, y2) ^ MUL(x1, y1) ^ MUL(x2, y0) ^ MUL(x3, y3); + z3 = MUL(x0, y3) ^ MUL(x1, y2) ^ MUL(x2, y1) ^ MUL(x3, y0); + z0 &= (uint64_t)0x1111111111111111; + z1 &= (uint64_t)0x2222222222222222; + z2 &= (uint64_t)0x4444444444444444; + z3 &= (uint64_t)0x8888888888888888; + z = z0 | z1 | z2 | z3; + *lo = (uint32_t)z; + *hi = (uint32_t)(z >> 32); +} + +#endif + +/* see bearssl_hash.h */ +void +br_ghash_ctmul(void *y, const void *h, const void *data, size_t len) +{ + const unsigned char *buf, *hb; + unsigned char *yb; + uint32_t yw[4]; + uint32_t hw[4]; + + /* + * Throughout the loop we handle the y and h values as arrays + * of 32-bit words. + */ + buf = data; + yb = y; + hb = h; + yw[3] = br_dec32be(yb); + yw[2] = br_dec32be(yb + 4); + yw[1] = br_dec32be(yb + 8); + yw[0] = br_dec32be(yb + 12); + hw[3] = br_dec32be(hb); + hw[2] = br_dec32be(hb + 4); + hw[1] = br_dec32be(hb + 8); + hw[0] = br_dec32be(hb + 12); + while (len > 0) { + const unsigned char *src; + unsigned char tmp[16]; + int i; + uint32_t a[9], b[9], zw[8]; + uint32_t c0, c1, c2, c3, d0, d1, d2, d3, e0, e1, e2, e3; + + /* + * Get the next 16-byte block (using zero-padding if + * necessary). + */ + if (len >= 16) { + src = buf; + buf += 16; + len -= 16; + } else { + memcpy(tmp, buf, len); + memset(tmp + len, 0, (sizeof tmp) - len); + src = tmp; + len = 0; + } + + /* + * Decode the block. The GHASH standard mandates + * big-endian encoding. + */ + yw[3] ^= br_dec32be(src); + yw[2] ^= br_dec32be(src + 4); + yw[1] ^= br_dec32be(src + 8); + yw[0] ^= br_dec32be(src + 12); + + /* + * We multiply two 128-bit field elements. We use + * Karatsuba to turn that into three 64-bit + * multiplications, which are themselves done with a + * total of nine 32-bit multiplications. + */ + + /* + * y[0,1]*h[0,1] -> 0..2 + * y[2,3]*h[2,3] -> 3..5 + * (y[0,1]+y[2,3])*(h[0,1]+h[2,3]) -> 6..8 + */ + a[0] = yw[0]; + b[0] = hw[0]; + a[1] = yw[1]; + b[1] = hw[1]; + a[2] = a[0] ^ a[1]; + b[2] = b[0] ^ b[1]; + + a[3] = yw[2]; + b[3] = hw[2]; + a[4] = yw[3]; + b[4] = hw[3]; + a[5] = a[3] ^ a[4]; + b[5] = b[3] ^ b[4]; + + a[6] = a[0] ^ a[3]; + b[6] = b[0] ^ b[3]; + a[7] = a[1] ^ a[4]; + b[7] = b[1] ^ b[4]; + a[8] = a[6] ^ a[7]; + b[8] = b[6] ^ b[7]; + + for (i = 0; i < 9; i ++) { + bmul(&b[i], &a[i], b[i], a[i]); + } + + c0 = a[0]; + c1 = b[0] ^ a[2] ^ a[0] ^ a[1]; + c2 = a[1] ^ b[2] ^ b[0] ^ b[1]; + c3 = b[1]; + d0 = a[3]; + d1 = b[3] ^ a[5] ^ a[3] ^ a[4]; + d2 = a[4] ^ b[5] ^ b[3] ^ b[4]; + d3 = b[4]; + e0 = a[6]; + e1 = b[6] ^ a[8] ^ a[6] ^ a[7]; + e2 = a[7] ^ b[8] ^ b[6] ^ b[7]; + e3 = b[7]; + + e0 ^= c0 ^ d0; + e1 ^= c1 ^ d1; + e2 ^= c2 ^ d2; + e3 ^= c3 ^ d3; + c2 ^= e0; + c3 ^= e1; + d0 ^= e2; + d1 ^= e3; + + /* + * GHASH specification has the bits "reversed" (most + * significant is in fact least significant), which does + * not matter for a carryless multiplication, except that + * the 255-bit result must be shifted by 1 bit. + */ + zw[0] = c0 << 1; + zw[1] = (c1 << 1) | (c0 >> 31); + zw[2] = (c2 << 1) | (c1 >> 31); + zw[3] = (c3 << 1) | (c2 >> 31); + zw[4] = (d0 << 1) | (c3 >> 31); + zw[5] = (d1 << 1) | (d0 >> 31); + zw[6] = (d2 << 1) | (d1 >> 31); + zw[7] = (d3 << 1) | (d2 >> 31); + + /* + * We now do the reduction modulo the field polynomial + * to get back to 128 bits. + */ + for (i = 0; i < 4; i ++) { + uint32_t lw; + + lw = zw[i]; + zw[i + 4] ^= lw ^ (lw >> 1) ^ (lw >> 2) ^ (lw >> 7); + zw[i + 3] ^= (lw << 31) ^ (lw << 30) ^ (lw << 25); + } + memcpy(yw, zw + 4, sizeof yw); + } + + /* + * Encode back the result. + */ + br_enc32be(yb, yw[3]); + br_enc32be(yb + 4, yw[2]); + br_enc32be(yb + 8, yw[1]); + br_enc32be(yb + 12, yw[0]); +} diff --git a/lib/bearssl-esp8266/src/hash/ghash_ctmul32.c b/lib/bearssl-esp8266/src/hash/ghash_ctmul32.c new file mode 100644 index 000000000..5f73c8160 --- /dev/null +++ b/lib/bearssl-esp8266/src/hash/ghash_ctmul32.c @@ -0,0 +1,251 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * This implementation uses 32-bit multiplications, and only the low + * 32 bits for each multiplication result. This is meant primarily for + * the ARM Cortex M0 and M0+, whose multiplication opcode does not yield + * the upper 32 bits; but it might also be useful on architectures where + * access to the upper 32 bits requires use of specific registers that + * create contention (e.g. on i386, "mul" necessarily outputs the result + * in edx:eax, while "imul" can use any registers but is limited to the + * low 32 bits). + * + * The implementation trick that is used here is bit-reversing (bit 0 + * is swapped with bit 31, bit 1 with bit 30, and so on). In GF(2)[X], + * for all values x and y, we have: + * rev32(x) * rev32(y) = rev64(x * y) + * In other words, if we bit-reverse (over 32 bits) the operands, then we + * bit-reverse (over 64 bits) the result. + */ + +/* + * Multiplication in GF(2)[X], truncated to its low 32 bits. + */ +static inline uint32_t +bmul32(uint32_t x, uint32_t y) +{ + uint32_t x0, x1, x2, x3; + uint32_t y0, y1, y2, y3; + uint32_t z0, z1, z2, z3; + + x0 = x & (uint32_t)0x11111111; + x1 = x & (uint32_t)0x22222222; + x2 = x & (uint32_t)0x44444444; + x3 = x & (uint32_t)0x88888888; + y0 = y & (uint32_t)0x11111111; + y1 = y & (uint32_t)0x22222222; + y2 = y & (uint32_t)0x44444444; + y3 = y & (uint32_t)0x88888888; + z0 = (x0 * y0) ^ (x1 * y3) ^ (x2 * y2) ^ (x3 * y1); + z1 = (x0 * y1) ^ (x1 * y0) ^ (x2 * y3) ^ (x3 * y2); + z2 = (x0 * y2) ^ (x1 * y1) ^ (x2 * y0) ^ (x3 * y3); + z3 = (x0 * y3) ^ (x1 * y2) ^ (x2 * y1) ^ (x3 * y0); + z0 &= (uint32_t)0x11111111; + z1 &= (uint32_t)0x22222222; + z2 &= (uint32_t)0x44444444; + z3 &= (uint32_t)0x88888888; + return z0 | z1 | z2 | z3; +} + +/* + * Bit-reverse a 32-bit word. + */ +static uint32_t +rev32(uint32_t x) +{ +#define RMS(m, s) do { \ + x = ((x & (uint32_t)(m)) << (s)) \ + | ((x >> (s)) & (uint32_t)(m)); \ + } while (0) + + RMS(0x55555555, 1); + RMS(0x33333333, 2); + RMS(0x0F0F0F0F, 4); + RMS(0x00FF00FF, 8); + return (x << 16) | (x >> 16); + +#undef RMS +} + +/* see bearssl_hash.h */ +void +br_ghash_ctmul32(void *y, const void *h, const void *data, size_t len) +{ + /* + * This implementation is similar to br_ghash_ctmul() except + * that we have to do the multiplication twice, with the + * "normal" and "bit reversed" operands. Hence we end up with + * eighteen 32-bit multiplications instead of nine. + */ + + const unsigned char *buf, *hb; + unsigned char *yb; + uint32_t yw[4]; + uint32_t hw[4], hwr[4]; + + buf = data; + yb = y; + hb = h; + yw[3] = br_dec32be(yb); + yw[2] = br_dec32be(yb + 4); + yw[1] = br_dec32be(yb + 8); + yw[0] = br_dec32be(yb + 12); + hw[3] = br_dec32be(hb); + hw[2] = br_dec32be(hb + 4); + hw[1] = br_dec32be(hb + 8); + hw[0] = br_dec32be(hb + 12); + hwr[3] = rev32(hw[3]); + hwr[2] = rev32(hw[2]); + hwr[1] = rev32(hw[1]); + hwr[0] = rev32(hw[0]); + while (len > 0) { + const unsigned char *src; + unsigned char tmp[16]; + int i; + uint32_t a[18], b[18], c[18]; + uint32_t d0, d1, d2, d3, d4, d5, d6, d7; + uint32_t zw[8]; + + if (len >= 16) { + src = buf; + buf += 16; + len -= 16; + } else { + memcpy(tmp, buf, len); + memset(tmp + len, 0, (sizeof tmp) - len); + src = tmp; + len = 0; + } + yw[3] ^= br_dec32be(src); + yw[2] ^= br_dec32be(src + 4); + yw[1] ^= br_dec32be(src + 8); + yw[0] ^= br_dec32be(src + 12); + + /* + * We are using Karatsuba: the 128x128 multiplication is + * reduced to three 64x64 multiplications, hence nine + * 32x32 multiplications. With the bit-reversal trick, + * we have to perform 18 32x32 multiplications. + */ + + /* + * y[0,1]*h[0,1] -> 0,1,4 + * y[2,3]*h[2,3] -> 2,3,5 + * (y[0,1]+y[2,3])*(h[0,1]+h[2,3]) -> 6,7,8 + */ + + a[0] = yw[0]; + a[1] = yw[1]; + a[2] = yw[2]; + a[3] = yw[3]; + a[4] = a[0] ^ a[1]; + a[5] = a[2] ^ a[3]; + a[6] = a[0] ^ a[2]; + a[7] = a[1] ^ a[3]; + a[8] = a[6] ^ a[7]; + + a[ 9] = rev32(yw[0]); + a[10] = rev32(yw[1]); + a[11] = rev32(yw[2]); + a[12] = rev32(yw[3]); + a[13] = a[ 9] ^ a[10]; + a[14] = a[11] ^ a[12]; + a[15] = a[ 9] ^ a[11]; + a[16] = a[10] ^ a[12]; + a[17] = a[15] ^ a[16]; + + b[0] = hw[0]; + b[1] = hw[1]; + b[2] = hw[2]; + b[3] = hw[3]; + b[4] = b[0] ^ b[1]; + b[5] = b[2] ^ b[3]; + b[6] = b[0] ^ b[2]; + b[7] = b[1] ^ b[3]; + b[8] = b[6] ^ b[7]; + + b[ 9] = hwr[0]; + b[10] = hwr[1]; + b[11] = hwr[2]; + b[12] = hwr[3]; + b[13] = b[ 9] ^ b[10]; + b[14] = b[11] ^ b[12]; + b[15] = b[ 9] ^ b[11]; + b[16] = b[10] ^ b[12]; + b[17] = b[15] ^ b[16]; + + for (i = 0; i < 18; i ++) { + c[i] = bmul32(a[i], b[i]); + } + + c[4] ^= c[0] ^ c[1]; + c[5] ^= c[2] ^ c[3]; + c[8] ^= c[6] ^ c[7]; + + c[13] ^= c[ 9] ^ c[10]; + c[14] ^= c[11] ^ c[12]; + c[17] ^= c[15] ^ c[16]; + + /* + * y[0,1]*h[0,1] -> 0,9^4,1^13,10 + * y[2,3]*h[2,3] -> 2,11^5,3^14,12 + * (y[0,1]+y[2,3])*(h[0,1]+h[2,3]) -> 6,15^8,7^17,16 + */ + d0 = c[0]; + d1 = c[4] ^ (rev32(c[9]) >> 1); + d2 = c[1] ^ c[0] ^ c[2] ^ c[6] ^ (rev32(c[13]) >> 1); + d3 = c[4] ^ c[5] ^ c[8] + ^ (rev32(c[10] ^ c[9] ^ c[11] ^ c[15]) >> 1); + d4 = c[2] ^ c[1] ^ c[3] ^ c[7] + ^ (rev32(c[13] ^ c[14] ^ c[17]) >> 1); + d5 = c[5] ^ (rev32(c[11] ^ c[10] ^ c[12] ^ c[16]) >> 1); + d6 = c[3] ^ (rev32(c[14]) >> 1); + d7 = rev32(c[12]) >> 1; + + zw[0] = d0 << 1; + zw[1] = (d1 << 1) | (d0 >> 31); + zw[2] = (d2 << 1) | (d1 >> 31); + zw[3] = (d3 << 1) | (d2 >> 31); + zw[4] = (d4 << 1) | (d3 >> 31); + zw[5] = (d5 << 1) | (d4 >> 31); + zw[6] = (d6 << 1) | (d5 >> 31); + zw[7] = (d7 << 1) | (d6 >> 31); + + for (i = 0; i < 4; i ++) { + uint32_t lw; + + lw = zw[i]; + zw[i + 4] ^= lw ^ (lw >> 1) ^ (lw >> 2) ^ (lw >> 7); + zw[i + 3] ^= (lw << 31) ^ (lw << 30) ^ (lw << 25); + } + memcpy(yw, zw + 4, sizeof yw); + } + br_enc32be(yb, yw[3]); + br_enc32be(yb + 4, yw[2]); + br_enc32be(yb + 8, yw[1]); + br_enc32be(yb + 12, yw[0]); +} diff --git a/lib/bearssl-esp8266/src/hash/ghash_ctmul64.c b/lib/bearssl-esp8266/src/hash/ghash_ctmul64.c new file mode 100644 index 000000000..45604f7d7 --- /dev/null +++ b/lib/bearssl-esp8266/src/hash/ghash_ctmul64.c @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * This is the 64-bit variant of br_ghash_ctmul32(), with 64-bit operands + * and bit reversal of 64-bit words. + */ + +static inline uint64_t +bmul64(uint64_t x, uint64_t y) +{ + uint64_t x0, x1, x2, x3; + uint64_t y0, y1, y2, y3; + uint64_t z0, z1, z2, z3; + + x0 = x & (uint64_t)0x1111111111111111; + x1 = x & (uint64_t)0x2222222222222222; + x2 = x & (uint64_t)0x4444444444444444; + x3 = x & (uint64_t)0x8888888888888888; + y0 = y & (uint64_t)0x1111111111111111; + y1 = y & (uint64_t)0x2222222222222222; + y2 = y & (uint64_t)0x4444444444444444; + y3 = y & (uint64_t)0x8888888888888888; + z0 = (x0 * y0) ^ (x1 * y3) ^ (x2 * y2) ^ (x3 * y1); + z1 = (x0 * y1) ^ (x1 * y0) ^ (x2 * y3) ^ (x3 * y2); + z2 = (x0 * y2) ^ (x1 * y1) ^ (x2 * y0) ^ (x3 * y3); + z3 = (x0 * y3) ^ (x1 * y2) ^ (x2 * y1) ^ (x3 * y0); + z0 &= (uint64_t)0x1111111111111111; + z1 &= (uint64_t)0x2222222222222222; + z2 &= (uint64_t)0x4444444444444444; + z3 &= (uint64_t)0x8888888888888888; + return z0 | z1 | z2 | z3; +} + +static uint64_t +rev64(uint64_t x) +{ +#define RMS(m, s) do { \ + x = ((x & (uint64_t)(m)) << (s)) \ + | ((x >> (s)) & (uint64_t)(m)); \ + } while (0) + + RMS(0x5555555555555555, 1); + RMS(0x3333333333333333, 2); + RMS(0x0F0F0F0F0F0F0F0F, 4); + RMS(0x00FF00FF00FF00FF, 8); + RMS(0x0000FFFF0000FFFF, 16); + return (x << 32) | (x >> 32); + +#undef RMS +} + +/* see bearssl_ghash.h */ +void +br_ghash_ctmul64(void *y, const void *h, const void *data, size_t len) +{ + const unsigned char *buf, *hb; + unsigned char *yb; + uint64_t y0, y1; + uint64_t h0, h1, h2, h0r, h1r, h2r; + + buf = data; + yb = y; + hb = h; + y1 = br_dec64be(yb); + y0 = br_dec64be(yb + 8); + h1 = br_dec64be(hb); + h0 = br_dec64be(hb + 8); + h0r = rev64(h0); + h1r = rev64(h1); + h2 = h0 ^ h1; + h2r = h0r ^ h1r; + while (len > 0) { + const unsigned char *src; + unsigned char tmp[16]; + uint64_t y0r, y1r, y2, y2r; + uint64_t z0, z1, z2, z0h, z1h, z2h; + uint64_t v0, v1, v2, v3; + + if (len >= 16) { + src = buf; + buf += 16; + len -= 16; + } else { + memcpy(tmp, buf, len); + memset(tmp + len, 0, (sizeof tmp) - len); + src = tmp; + len = 0; + } + y1 ^= br_dec64be(src); + y0 ^= br_dec64be(src + 8); + + y0r = rev64(y0); + y1r = rev64(y1); + y2 = y0 ^ y1; + y2r = y0r ^ y1r; + + z0 = bmul64(y0, h0); + z1 = bmul64(y1, h1); + z2 = bmul64(y2, h2); + z0h = bmul64(y0r, h0r); + z1h = bmul64(y1r, h1r); + z2h = bmul64(y2r, h2r); + z2 ^= z0 ^ z1; + z2h ^= z0h ^ z1h; + z0h = rev64(z0h) >> 1; + z1h = rev64(z1h) >> 1; + z2h = rev64(z2h) >> 1; + + v0 = z0; + v1 = z0h ^ z2; + v2 = z1 ^ z2h; + v3 = z1h; + + v3 = (v3 << 1) | (v2 >> 63); + v2 = (v2 << 1) | (v1 >> 63); + v1 = (v1 << 1) | (v0 >> 63); + v0 = (v0 << 1); + + v2 ^= v0 ^ (v0 >> 1) ^ (v0 >> 2) ^ (v0 >> 7); + v1 ^= (v0 << 63) ^ (v0 << 62) ^ (v0 << 57); + v3 ^= v1 ^ (v1 >> 1) ^ (v1 >> 2) ^ (v1 >> 7); + v2 ^= (v1 << 63) ^ (v1 << 62) ^ (v1 << 57); + + y0 = v2; + y1 = v3; + } + + br_enc64be(yb, y1); + br_enc64be(yb + 8, y0); +} diff --git a/lib/bearssl-esp8266/src/hash/ghash_pclmul.c b/lib/bearssl-esp8266/src/hash/ghash_pclmul.c new file mode 100644 index 000000000..f8feaa5ad --- /dev/null +++ b/lib/bearssl-esp8266/src/hash/ghash_pclmul.c @@ -0,0 +1,389 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BR_ENABLE_INTRINSICS 1 +#include "t_inner.h" + +/* + * This is the GHASH implementation that leverages the pclmulqdq opcode + * (from the AES-NI instructions). + */ + +#if BR_AES_X86NI + +/* + * Test CPU support for PCLMULQDQ. + */ +static inline int +pclmul_supported(void) +{ + /* + * Bit mask for features in ECX: + * 1 PCLMULQDQ support + */ + return br_cpuid(0, 0, 0x00000002, 0); +} + +/* see bearssl_hash.h */ +br_ghash +br_ghash_pclmul_get(void) +{ + return pclmul_supported() ? &br_ghash_pclmul : 0; +} + +BR_TARGETS_X86_UP + +/* + * GHASH is defined over elements of GF(2^128) with "full little-endian" + * representation: leftmost byte is least significant, and, within each + * byte, leftmost _bit_ is least significant. The natural ordering in + * x86 is "mixed little-endian": bytes are ordered from least to most + * significant, but bits within a byte are in most-to-least significant + * order. Going to full little-endian representation would require + * reversing bits within each byte, which is doable but expensive. + * + * Instead, we go to full big-endian representation, by swapping bytes + * around, which is done with a single _mm_shuffle_epi8() opcode (it + * comes with SSSE3; all CPU that offer pclmulqdq also have SSSE3). We + * can use a full big-endian representation because in a carryless + * multiplication, we have a nice bit reversal property: + * + * rev_128(x) * rev_128(y) = rev_255(x * y) + * + * So by using full big-endian, we still get the right result, except + * that it is right-shifted by 1 bit. The left-shift is relatively + * inexpensive, and it can be mutualised. + * + * + * Since SSE2 opcodes do not have facilities for shitfting full 128-bit + * values with bit precision, we have to break down values into 64-bit + * chunks. We number chunks from 0 to 3 in left to right order. + */ + +/* + * Byte-swap a complete 128-bit value. This normally uses + * _mm_shuffle_epi8(), which gets translated to pshufb (an SSSE3 opcode). + * However, this crashes old Clang versions, so, for Clang before 3.8, + * we use an alternate (and less efficient) version. + */ +#if BR_CLANG && !BR_CLANG_3_8 +#define BYTESWAP_DECL +#define BYTESWAP_PREP (void)0 +#define BYTESWAP(x) do { \ + __m128i byteswap1, byteswap2; \ + byteswap1 = (x); \ + byteswap2 = _mm_srli_epi16(byteswap1, 8); \ + byteswap1 = _mm_slli_epi16(byteswap1, 8); \ + byteswap1 = _mm_or_si128(byteswap1, byteswap2); \ + byteswap1 = _mm_shufflelo_epi16(byteswap1, 0x1B); \ + byteswap1 = _mm_shufflehi_epi16(byteswap1, 0x1B); \ + (x) = _mm_shuffle_epi32(byteswap1, 0x4E); \ + } while (0) +#else +#define BYTESWAP_DECL __m128i byteswap_index; +#define BYTESWAP_PREP do { \ + byteswap_index = _mm_set_epi8( \ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); \ + } while (0) +#define BYTESWAP(x) do { \ + (x) = _mm_shuffle_epi8((x), byteswap_index); \ + } while (0) +#endif + +/* + * Call pclmulqdq. Clang appears to have trouble with the intrinsic, so, + * for that compiler, we use inline assembly. Inline assembly is + * potentially a bit slower because the compiler does not understand + * what the opcode does, and thus cannot optimize instruction + * scheduling. + * + * We use a target of "sse2" only, so that Clang may still handle the + * '__m128i' type and allocate SSE2 registers. + */ +#if BR_CLANG +BR_TARGET("sse2") +static inline __m128i +pclmulqdq00(__m128i x, __m128i y) +{ + __asm__ ("pclmulqdq $0x00, %1, %0" : "+x" (x) : "x" (y)); + return x; +} +BR_TARGET("sse2") +static inline __m128i +pclmulqdq11(__m128i x, __m128i y) +{ + __asm__ ("pclmulqdq $0x11, %1, %0" : "+x" (x) : "x" (y)); + return x; +} +#else +#define pclmulqdq00(x, y) _mm_clmulepi64_si128(x, y, 0x00) +#define pclmulqdq11(x, y) _mm_clmulepi64_si128(x, y, 0x11) +#endif + +/* + * From a 128-bit value kw, compute kx as the XOR of the two 64-bit + * halves of kw (into the right half of kx; left half is unspecified). + */ +#define BK(kw, kx) do { \ + kx = _mm_xor_si128(kw, _mm_shuffle_epi32(kw, 0x0E)); \ + } while (0) + +/* + * Combine two 64-bit values (k0:k1) into a 128-bit (kw) value and + * the XOR of the two values (kx). + */ +#define PBK(k0, k1, kw, kx) do { \ + kw = _mm_unpacklo_epi64(k1, k0); \ + kx = _mm_xor_si128(k0, k1); \ + } while (0) + +/* + * Left-shift by 1 bit a 256-bit value (in four 64-bit words). + */ +#define SL_256(x0, x1, x2, x3) do { \ + x0 = _mm_or_si128( \ + _mm_slli_epi64(x0, 1), \ + _mm_srli_epi64(x1, 63)); \ + x1 = _mm_or_si128( \ + _mm_slli_epi64(x1, 1), \ + _mm_srli_epi64(x2, 63)); \ + x2 = _mm_or_si128( \ + _mm_slli_epi64(x2, 1), \ + _mm_srli_epi64(x3, 63)); \ + x3 = _mm_slli_epi64(x3, 1); \ + } while (0) + +/* + * Perform reduction in GF(2^128). The 256-bit value is in x0..x3; + * result is written in x0..x1. + */ +#define REDUCE_F128(x0, x1, x2, x3) do { \ + x1 = _mm_xor_si128( \ + x1, \ + _mm_xor_si128( \ + _mm_xor_si128( \ + x3, \ + _mm_srli_epi64(x3, 1)), \ + _mm_xor_si128( \ + _mm_srli_epi64(x3, 2), \ + _mm_srli_epi64(x3, 7)))); \ + x2 = _mm_xor_si128( \ + _mm_xor_si128( \ + x2, \ + _mm_slli_epi64(x3, 63)), \ + _mm_xor_si128( \ + _mm_slli_epi64(x3, 62), \ + _mm_slli_epi64(x3, 57))); \ + x0 = _mm_xor_si128( \ + x0, \ + _mm_xor_si128( \ + _mm_xor_si128( \ + x2, \ + _mm_srli_epi64(x2, 1)), \ + _mm_xor_si128( \ + _mm_srli_epi64(x2, 2), \ + _mm_srli_epi64(x2, 7)))); \ + x1 = _mm_xor_si128( \ + _mm_xor_si128( \ + x1, \ + _mm_slli_epi64(x2, 63)), \ + _mm_xor_si128( \ + _mm_slli_epi64(x2, 62), \ + _mm_slli_epi64(x2, 57))); \ + } while (0) + +/* + * Square value kw into (dw,dx). + */ +#define SQUARE_F128(kw, dw, dx) do { \ + __m128i z0, z1, z2, z3; \ + z1 = pclmulqdq11(kw, kw); \ + z3 = pclmulqdq00(kw, kw); \ + z0 = _mm_shuffle_epi32(z1, 0x0E); \ + z2 = _mm_shuffle_epi32(z3, 0x0E); \ + SL_256(z0, z1, z2, z3); \ + REDUCE_F128(z0, z1, z2, z3); \ + PBK(z0, z1, dw, dx); \ + } while (0) + +/* see bearssl_hash.h */ +BR_TARGET("ssse3,pclmul") +void +br_ghash_pclmul(void *y, const void *h, const void *data, size_t len) +{ + const unsigned char *buf1, *buf2; + unsigned char tmp[64]; + size_t num4, num1; + __m128i yw, h1w, h1x; + BYTESWAP_DECL + + /* + * We split data into two chunks. First chunk starts at buf1 + * and contains num4 blocks of 64-byte values. Second chunk + * starts at buf2 and contains num1 blocks of 16-byte values. + * We want the first chunk to be as large as possible. + */ + buf1 = data; + num4 = len >> 6; + len &= 63; + buf2 = buf1 + (num4 << 6); + num1 = (len + 15) >> 4; + if ((len & 15) != 0) { + memcpy(tmp, buf2, len); + memset(tmp + len, 0, (num1 << 4) - len); + buf2 = tmp; + } + + /* + * Preparatory step for endian conversions. + */ + BYTESWAP_PREP; + + /* + * Load y and h. + */ + yw = _mm_loadu_si128(y); + h1w = _mm_loadu_si128(h); + BYTESWAP(yw); + BYTESWAP(h1w); + BK(h1w, h1x); + + if (num4 > 0) { + __m128i h2w, h2x, h3w, h3x, h4w, h4x; + __m128i t0, t1, t2, t3; + + /* + * Compute h2 = h^2. + */ + SQUARE_F128(h1w, h2w, h2x); + + /* + * Compute h3 = h^3 = h*(h^2). + */ + t1 = pclmulqdq11(h1w, h2w); + t3 = pclmulqdq00(h1w, h2w); + t2 = _mm_xor_si128(pclmulqdq00(h1x, h2x), + _mm_xor_si128(t1, t3)); + t0 = _mm_shuffle_epi32(t1, 0x0E); + t1 = _mm_xor_si128(t1, _mm_shuffle_epi32(t2, 0x0E)); + t2 = _mm_xor_si128(t2, _mm_shuffle_epi32(t3, 0x0E)); + SL_256(t0, t1, t2, t3); + REDUCE_F128(t0, t1, t2, t3); + PBK(t0, t1, h3w, h3x); + + /* + * Compute h4 = h^4 = (h^2)^2. + */ + SQUARE_F128(h2w, h4w, h4x); + + while (num4 -- > 0) { + __m128i aw0, aw1, aw2, aw3; + __m128i ax0, ax1, ax2, ax3; + + aw0 = _mm_loadu_si128((void *)(buf1 + 0)); + aw1 = _mm_loadu_si128((void *)(buf1 + 16)); + aw2 = _mm_loadu_si128((void *)(buf1 + 32)); + aw3 = _mm_loadu_si128((void *)(buf1 + 48)); + BYTESWAP(aw0); + BYTESWAP(aw1); + BYTESWAP(aw2); + BYTESWAP(aw3); + buf1 += 64; + + aw0 = _mm_xor_si128(aw0, yw); + BK(aw1, ax1); + BK(aw2, ax2); + BK(aw3, ax3); + BK(aw0, ax0); + + t1 = _mm_xor_si128( + _mm_xor_si128( + pclmulqdq11(aw0, h4w), + pclmulqdq11(aw1, h3w)), + _mm_xor_si128( + pclmulqdq11(aw2, h2w), + pclmulqdq11(aw3, h1w))); + t3 = _mm_xor_si128( + _mm_xor_si128( + pclmulqdq00(aw0, h4w), + pclmulqdq00(aw1, h3w)), + _mm_xor_si128( + pclmulqdq00(aw2, h2w), + pclmulqdq00(aw3, h1w))); + t2 = _mm_xor_si128( + _mm_xor_si128( + pclmulqdq00(ax0, h4x), + pclmulqdq00(ax1, h3x)), + _mm_xor_si128( + pclmulqdq00(ax2, h2x), + pclmulqdq00(ax3, h1x))); + t2 = _mm_xor_si128(t2, _mm_xor_si128(t1, t3)); + t0 = _mm_shuffle_epi32(t1, 0x0E); + t1 = _mm_xor_si128(t1, _mm_shuffle_epi32(t2, 0x0E)); + t2 = _mm_xor_si128(t2, _mm_shuffle_epi32(t3, 0x0E)); + SL_256(t0, t1, t2, t3); + REDUCE_F128(t0, t1, t2, t3); + yw = _mm_unpacklo_epi64(t1, t0); + } + } + + while (num1 -- > 0) { + __m128i aw, ax; + __m128i t0, t1, t2, t3; + + aw = _mm_loadu_si128((void *)buf2); + BYTESWAP(aw); + buf2 += 16; + + aw = _mm_xor_si128(aw, yw); + BK(aw, ax); + + t1 = pclmulqdq11(aw, h1w); + t3 = pclmulqdq00(aw, h1w); + t2 = pclmulqdq00(ax, h1x); + t2 = _mm_xor_si128(t2, _mm_xor_si128(t1, t3)); + t0 = _mm_shuffle_epi32(t1, 0x0E); + t1 = _mm_xor_si128(t1, _mm_shuffle_epi32(t2, 0x0E)); + t2 = _mm_xor_si128(t2, _mm_shuffle_epi32(t3, 0x0E)); + SL_256(t0, t1, t2, t3); + REDUCE_F128(t0, t1, t2, t3); + yw = _mm_unpacklo_epi64(t1, t0); + } + + BYTESWAP(yw); + _mm_storeu_si128(y, yw); +} + +BR_TARGETS_X86_DOWN + +#else + +/* see bearssl_hash.h */ +br_ghash +br_ghash_pclmul_get(void) +{ + return 0; +} + +#endif diff --git a/lib/bearssl-esp8266/src/hash/md5.c b/lib/bearssl-esp8266/src/hash/md5.c new file mode 100644 index 000000000..f38ee6ccb --- /dev/null +++ b/lib/bearssl-esp8266/src/hash/md5.c @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +#define F(B, C, D) ((((C) ^ (D)) & (B)) ^ (D)) +#define G(B, C, D) ((((C) ^ (B)) & (D)) ^ (C)) +#define H(B, C, D) ((B) ^ (C) ^ (D)) +#define I(B, C, D) ((C) ^ ((B) | ~(D))) + +#define ROTL(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) + +/* see inner.h */ +const uint32_t br_md5_IV[4] PROGMEM = { + 0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476 +}; + +static const uint32_t K[64] PROGMEM = { + 0xD76AA478, 0xE8C7B756, 0x242070DB, 0xC1BDCEEE, + 0xF57C0FAF, 0x4787C62A, 0xA8304613, 0xFD469501, + 0x698098D8, 0x8B44F7AF, 0xFFFF5BB1, 0x895CD7BE, + 0x6B901122, 0xFD987193, 0xA679438E, 0x49B40821, + + 0xF61E2562, 0xC040B340, 0x265E5A51, 0xE9B6C7AA, + 0xD62F105D, 0x02441453, 0xD8A1E681, 0xE7D3FBC8, + 0x21E1CDE6, 0xC33707D6, 0xF4D50D87, 0x455A14ED, + 0xA9E3E905, 0xFCEFA3F8, 0x676F02D9, 0x8D2A4C8A, + + 0xFFFA3942, 0x8771F681, 0x6D9D6122, 0xFDE5380C, + 0xA4BEEA44, 0x4BDECFA9, 0xF6BB4B60, 0xBEBFBC70, + 0x289B7EC6, 0xEAA127FA, 0xD4EF3085, 0x04881D05, + 0xD9D4D039, 0xE6DB99E5, 0x1FA27CF8, 0xC4AC5665, + + 0xF4292244, 0x432AFF97, 0xAB9423A7, 0xFC93A039, + 0x655B59C3, 0x8F0CCC92, 0xFFEFF47D, 0x85845DD1, + 0x6FA87E4F, 0xFE2CE6E0, 0xA3014314, 0x4E0811A1, + 0xF7537E82, 0xBD3AF235, 0x2AD7D2BB, 0xEB86D391 +}; + +static const unsigned char MP_flash[48] PROGMEM = { + 1, 6, 11, 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, + 5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2, + 0, 7, 14, 5, 12, 3, 10, 1, 8, 15, 6, 13, 4, 11, 2, 9 +}; + +/* see inner.h */ +void +br_md5_round(const unsigned char *buf, uint32_t *val) +{ + uint32_t m[16]; + uint32_t a, b, c, d; + int i; + uint8_t MP[48]; + memcpy_P(MP, MP_flash, 48); + + a = val[0]; + b = val[1]; + c = val[2]; + d = val[3]; + /* obsolete + for (i = 0; i < 16; i ++) { + m[i] = br_dec32le(buf + (i << 2)); + } + */ + br_range_dec32le(m, 16, buf); + + for (i = 0; i < 16; i += 4) { + a = b + ROTL(a + F(b, c, d) + m[i + 0] + K[i + 0], 7); + d = a + ROTL(d + F(a, b, c) + m[i + 1] + K[i + 1], 12); + c = d + ROTL(c + F(d, a, b) + m[i + 2] + K[i + 2], 17); + b = c + ROTL(b + F(c, d, a) + m[i + 3] + K[i + 3], 22); + } + for (i = 16; i < 32; i += 4) { + a = b + ROTL(a + G(b, c, d) + m[MP[i - 16]] + K[i + 0], 5); + d = a + ROTL(d + G(a, b, c) + m[MP[i - 15]] + K[i + 1], 9); + c = d + ROTL(c + G(d, a, b) + m[MP[i - 14]] + K[i + 2], 14); + b = c + ROTL(b + G(c, d, a) + m[MP[i - 13]] + K[i + 3], 20); + } + for (i = 32; i < 48; i += 4) { + a = b + ROTL(a + H(b, c, d) + m[MP[i - 16]] + K[i + 0], 4); + d = a + ROTL(d + H(a, b, c) + m[MP[i - 15]] + K[i + 1], 11); + c = d + ROTL(c + H(d, a, b) + m[MP[i - 14]] + K[i + 2], 16); + b = c + ROTL(b + H(c, d, a) + m[MP[i - 13]] + K[i + 3], 23); + } + for (i = 48; i < 64; i += 4) { + a = b + ROTL(a + I(b, c, d) + m[MP[i - 16]] + K[i + 0], 6); + d = a + ROTL(d + I(a, b, c) + m[MP[i - 15]] + K[i + 1], 10); + c = d + ROTL(c + I(d, a, b) + m[MP[i - 14]] + K[i + 2], 15); + b = c + ROTL(b + I(c, d, a) + m[MP[i - 13]] + K[i + 3], 21); + } + + val[0] += a; + val[1] += b; + val[2] += c; + val[3] += d; +} + +/* see bearssl.h */ +void +br_md5_init(br_md5_context *cc) +{ + cc->vtable = &br_md5_vtable; + memcpy(cc->val, br_md5_IV, sizeof cc->val); + cc->count = 0; +} + +/* see bearssl.h */ +void +br_md5_update(br_md5_context *cc, const void *data, size_t len) +{ + const unsigned char *buf; + size_t ptr; + + buf = data; + ptr = (size_t)cc->count & 63; + while (len > 0) { + size_t clen; + + clen = 64 - ptr; + if (clen > len) { + clen = len; + } + memcpy(cc->buf + ptr, buf, clen); + ptr += clen; + buf += clen; + len -= clen; + cc->count += (uint64_t)clen; + if (ptr == 64) { + br_md5_round(cc->buf, cc->val); + ptr = 0; + } + } +} + +/* see bearssl.h */ +void +br_md5_out(const br_md5_context *cc, void *dst) +{ + unsigned char buf[64]; + uint32_t val[4]; + size_t ptr; + + ptr = (size_t)cc->count & 63; + memcpy(buf, cc->buf, ptr); + memcpy(val, cc->val, sizeof val); + buf[ptr ++] = 0x80; + if (ptr > 56) { + memset(buf + ptr, 0, 64 - ptr); + br_md5_round(buf, val); + memset(buf, 0, 56); + } else { + memset(buf + ptr, 0, 56 - ptr); + } + br_enc64le(buf + 56, cc->count << 3); + br_md5_round(buf, val); + br_range_enc32le(dst, val, 4); +} + +/* see bearssl.h */ +uint64_t +br_md5_state(const br_md5_context *cc, void *dst) +{ + br_range_enc32le(dst, cc->val, 4); + return cc->count; +} + +/* see bearssl.h */ +void +br_md5_set_state(br_md5_context *cc, const void *stb, uint64_t count) +{ + br_range_dec32le(cc->val, 4, stb); + cc->count = count; +} + +/* see bearssl.h */ +const br_hash_class br_md5_vtable PROGMEM = { + sizeof(br_md5_context), + BR_HASHDESC_ID(br_md5_ID) + | BR_HASHDESC_OUT(16) + | BR_HASHDESC_STATE(16) + | BR_HASHDESC_LBLEN(6) + | BR_HASHDESC_MD_PADDING, + (void (*)(const br_hash_class **))&br_md5_init, + (void (*)(const br_hash_class **, const void *, size_t))&br_md5_update, + (void (*)(const br_hash_class *const *, void *))&br_md5_out, + (uint64_t (*)(const br_hash_class *const *, void *))&br_md5_state, + (void (*)(const br_hash_class **, const void *, uint64_t)) + &br_md5_set_state +}; diff --git a/lib/bearssl-esp8266/src/hash/md5sha1.c b/lib/bearssl-esp8266/src/hash/md5sha1.c new file mode 100644 index 000000000..b9553d685 --- /dev/null +++ b/lib/bearssl-esp8266/src/hash/md5sha1.c @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl.h */ +void +br_md5sha1_init(br_md5sha1_context *cc) +{ + cc->vtable = &br_md5sha1_vtable; + memcpy(cc->val_md5, br_md5_IV, sizeof cc->val_md5); + memcpy(cc->val_sha1, br_sha1_IV, sizeof cc->val_sha1); + cc->count = 0; +} + +/* see bearssl.h */ +void +br_md5sha1_update(br_md5sha1_context *cc, const void *data, size_t len) +{ + const unsigned char *buf; + size_t ptr; + + buf = data; + ptr = (size_t)cc->count & 63; + while (len > 0) { + size_t clen; + + clen = 64 - ptr; + if (clen > len) { + clen = len; + } + memcpy(cc->buf + ptr, buf, clen); + ptr += clen; + buf += clen; + len -= clen; + cc->count += (uint64_t)clen; + if (ptr == 64) { + br_md5_round(cc->buf, cc->val_md5); + br_sha1_round(cc->buf, cc->val_sha1); + ptr = 0; + } + } +} + +/* see bearssl.h */ +void +br_md5sha1_out(const br_md5sha1_context *cc, void *dst) +{ + unsigned char buf[64]; + uint32_t val_md5[4]; + uint32_t val_sha1[5]; + size_t ptr; + unsigned char *out; + uint64_t count; + + count = cc->count; + ptr = (size_t)count & 63; + memcpy(buf, cc->buf, ptr); + memcpy(val_md5, cc->val_md5, sizeof val_md5); + memcpy(val_sha1, cc->val_sha1, sizeof val_sha1); + buf[ptr ++] = 0x80; + if (ptr > 56) { + memset(buf + ptr, 0, 64 - ptr); + br_md5_round(buf, val_md5); + br_sha1_round(buf, val_sha1); + memset(buf, 0, 56); + } else { + memset(buf + ptr, 0, 56 - ptr); + } + count <<= 3; + br_enc64le(buf + 56, count); + br_md5_round(buf, val_md5); + br_enc64be(buf + 56, count); + br_sha1_round(buf, val_sha1); + out = dst; + br_range_enc32le(out, val_md5, 4); + br_range_enc32be(out + 16, val_sha1, 5); +} + +/* see bearssl.h */ +uint64_t +br_md5sha1_state(const br_md5sha1_context *cc, void *dst) +{ + unsigned char *out; + + out = dst; + br_range_enc32le(out, cc->val_md5, 4); + br_range_enc32be(out + 16, cc->val_sha1, 5); + return cc->count; +} + +/* see bearssl.h */ +void +br_md5sha1_set_state(br_md5sha1_context *cc, const void *stb, uint64_t count) +{ + const unsigned char *buf; + + buf = stb; + br_range_dec32le(cc->val_md5, 4, buf); + br_range_dec32be(cc->val_sha1, 5, buf + 16); + cc->count = count; +} + +/* see bearssl.h */ +const br_hash_class br_md5sha1_vtable PROGMEM = { + sizeof(br_md5sha1_context), + BR_HASHDESC_ID(br_md5sha1_ID) + | BR_HASHDESC_OUT(36) + | BR_HASHDESC_STATE(36) + | BR_HASHDESC_LBLEN(6), + (void (*)(const br_hash_class **))&br_md5sha1_init, + (void (*)(const br_hash_class **, const void *, size_t)) + &br_md5sha1_update, + (void (*)(const br_hash_class *const *, void *)) + &br_md5sha1_out, + (uint64_t (*)(const br_hash_class *const *, void *)) + &br_md5sha1_state, + (void (*)(const br_hash_class **, const void *, uint64_t)) + &br_md5sha1_set_state +}; diff --git a/lib/bearssl-esp8266/src/hash/mgf1.c b/lib/bearssl-esp8266/src/hash/mgf1.c new file mode 100644 index 000000000..63b42ff44 --- /dev/null +++ b/lib/bearssl-esp8266/src/hash/mgf1.c @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +void +br_mgf1_xor(void *data, size_t len, + const br_hash_class *dig, const void *seed, size_t seed_len) +{ + unsigned char *buf; + size_t u, hlen; + uint32_t c; + + buf = data; + hlen = br_digest_size(dig); + for (u = 0, c = 0; u < len; u += hlen, c ++) { + br_hash_compat_context hc; + unsigned char tmp[64]; + size_t v; + + hc.vtable = dig; + dig->init(&hc.vtable); + dig->update(&hc.vtable, seed, seed_len); + br_enc32be(tmp, c); + dig->update(&hc.vtable, tmp, 4); + dig->out(&hc.vtable, tmp); + for (v = 0; v < hlen; v ++) { + if ((u + v) >= len) { + break; + } + buf[u + v] ^= tmp[v]; + } + } +} diff --git a/lib/bearssl-esp8266/src/hash/multihash.c b/lib/bearssl-esp8266/src/hash/multihash.c new file mode 100644 index 000000000..a039e2e71 --- /dev/null +++ b/lib/bearssl-esp8266/src/hash/multihash.c @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * An aggregate context that is large enough for all supported hash + * functions. + */ +typedef union { + const br_hash_class *vtable; + br_md5_context md5; + br_sha1_context sha1; + br_sha224_context sha224; + br_sha256_context sha256; + br_sha384_context sha384; + br_sha512_context sha512; +} gen_hash_context; + +/* + * Get the offset to the state for a specific hash function within the + * context structure. This shall be called only for the supported hash + * functions, + */ +static size_t +get_state_offset(int id) +{ + if (id >= 5) { + /* + * SHA-384 has id 5, and SHA-512 has id 6. Both use + * eight 64-bit words for their state. + */ + return offsetof(br_multihash_context, val_64) + + ((size_t)(id - 5) * (8 * sizeof(uint64_t))); + } else { + /* + * MD5 has id 1, SHA-1 has id 2, SHA-224 has id 3 and + * SHA-256 has id 4. They use 32-bit words for their + * states (4 words for MD5, 5 for SHA-1, 8 for SHA-224 + * and 8 for SHA-256). + */ + unsigned x; + + x = id - 1; + x = ((x + (x & (x >> 1))) << 2) + (x >> 1); + return offsetof(br_multihash_context, val_32) + + x * sizeof(uint32_t); + } +} + +/* see bearssl_hash.h */ +void +br_multihash_zero(br_multihash_context *ctx) +{ + /* + * This is not standard, but yields very short and efficient code, + * and it works "everywhere". + */ + memset(ctx, 0, sizeof *ctx); +} + +/* see bearssl_hash.h */ +void +br_multihash_init(br_multihash_context *ctx) +{ + int i; + + ctx->count = 0; + for (i = 1; i <= 6; i ++) { + const br_hash_class *hc; + + hc = ctx->impl[i - 1]; + if (hc != NULL) { + gen_hash_context g; + + hc->init(&g.vtable); + hc->state(&g.vtable, + (unsigned char *)ctx + get_state_offset(i)); + } + } +} + +/* see bearssl_hash.h */ +void +br_multihash_update(br_multihash_context *ctx, const void *data, size_t len) +{ + const unsigned char *buf; + size_t ptr; + + buf = data; + ptr = (size_t)ctx->count & 127; + while (len > 0) { + size_t clen; + + clen = 128 - ptr; + if (clen > len) { + clen = len; + } + memcpy(ctx->buf + ptr, buf, clen); + ptr += clen; + buf += clen; + len -= clen; + ctx->count += (uint64_t)clen; + if (ptr == 128) { + int i; + + for (i = 1; i <= 6; i ++) { + const br_hash_class *hc; + + hc = ctx->impl[i - 1]; + if (hc != NULL) { + gen_hash_context g; + unsigned char *state; + + state = (unsigned char *)ctx + + get_state_offset(i); + hc->set_state(&g.vtable, + state, ctx->count - 128); + hc->update(&g.vtable, ctx->buf, 128); + hc->state(&g.vtable, state); + } + } + ptr = 0; + } + } +} + +/* see bearssl_hash.h */ +size_t +br_multihash_out(const br_multihash_context *ctx, int id, void *dst) +{ + const br_hash_class *hc; + gen_hash_context g; + const unsigned char *state; + + hc = ctx->impl[id - 1]; + if (hc == NULL) { + return 0; + } + state = (const unsigned char *)ctx + get_state_offset(id); + hc->set_state(&g.vtable, state, ctx->count & ~(uint64_t)127); + hc->update(&g.vtable, ctx->buf, ctx->count & (uint64_t)127); + hc->out(&g.vtable, dst); + return (hc->desc >> BR_HASHDESC_OUT_OFF) & BR_HASHDESC_OUT_MASK; +} diff --git a/lib/bearssl-esp8266/src/hash/sha1.c b/lib/bearssl-esp8266/src/hash/sha1.c new file mode 100644 index 000000000..9f5cf81b5 --- /dev/null +++ b/lib/bearssl-esp8266/src/hash/sha1.c @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +#define F(B, C, D) ((((C) ^ (D)) & (B)) ^ (D)) +#define G(B, C, D) ((B) ^ (C) ^ (D)) +#define H(B, C, D) (((D) & (C)) | (((D) | (C)) & (B))) +#define I(B, C, D) G(B, C, D) + +#define ROTL(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) + +#define K1 ((uint32_t)0x5A827999) +#define K2 ((uint32_t)0x6ED9EBA1) +#define K3 ((uint32_t)0x8F1BBCDC) +#define K4 ((uint32_t)0xCA62C1D6) + +/* see inner.h */ +const uint32_t br_sha1_IV[5] PROGMEM = { + 0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0 +}; + +/* see inner.h */ +void +br_sha1_round(const unsigned char *buf, uint32_t *val) +{ + uint32_t m[80]; + uint32_t a, b, c, d, e; + int i; + + a = val[0]; + b = val[1]; + c = val[2]; + d = val[3]; + e = val[4]; + br_range_dec32be(m, 16, buf); + for (i = 16; i < 80; i ++) { + uint32_t x = m[i - 3] ^ m[i - 8] ^ m[i - 14] ^ m[i - 16]; + m[i] = ROTL(x, 1); + } + + for (i = 0; i < 20; i += 5) { + e += ROTL(a, 5) + F(b, c, d) + K1 + m[i + 0]; b = ROTL(b, 30); + d += ROTL(e, 5) + F(a, b, c) + K1 + m[i + 1]; a = ROTL(a, 30); + c += ROTL(d, 5) + F(e, a, b) + K1 + m[i + 2]; e = ROTL(e, 30); + b += ROTL(c, 5) + F(d, e, a) + K1 + m[i + 3]; d = ROTL(d, 30); + a += ROTL(b, 5) + F(c, d, e) + K1 + m[i + 4]; c = ROTL(c, 30); + } + for (i = 20; i < 40; i += 5) { + e += ROTL(a, 5) + G(b, c, d) + K2 + m[i + 0]; b = ROTL(b, 30); + d += ROTL(e, 5) + G(a, b, c) + K2 + m[i + 1]; a = ROTL(a, 30); + c += ROTL(d, 5) + G(e, a, b) + K2 + m[i + 2]; e = ROTL(e, 30); + b += ROTL(c, 5) + G(d, e, a) + K2 + m[i + 3]; d = ROTL(d, 30); + a += ROTL(b, 5) + G(c, d, e) + K2 + m[i + 4]; c = ROTL(c, 30); + } + for (i = 40; i < 60; i += 5) { + e += ROTL(a, 5) + H(b, c, d) + K3 + m[i + 0]; b = ROTL(b, 30); + d += ROTL(e, 5) + H(a, b, c) + K3 + m[i + 1]; a = ROTL(a, 30); + c += ROTL(d, 5) + H(e, a, b) + K3 + m[i + 2]; e = ROTL(e, 30); + b += ROTL(c, 5) + H(d, e, a) + K3 + m[i + 3]; d = ROTL(d, 30); + a += ROTL(b, 5) + H(c, d, e) + K3 + m[i + 4]; c = ROTL(c, 30); + } + for (i = 60; i < 80; i += 5) { + e += ROTL(a, 5) + I(b, c, d) + K4 + m[i + 0]; b = ROTL(b, 30); + d += ROTL(e, 5) + I(a, b, c) + K4 + m[i + 1]; a = ROTL(a, 30); + c += ROTL(d, 5) + I(e, a, b) + K4 + m[i + 2]; e = ROTL(e, 30); + b += ROTL(c, 5) + I(d, e, a) + K4 + m[i + 3]; d = ROTL(d, 30); + a += ROTL(b, 5) + I(c, d, e) + K4 + m[i + 4]; c = ROTL(c, 30); + } + + val[0] += a; + val[1] += b; + val[2] += c; + val[3] += d; + val[4] += e; +} + +/* see bearssl.h */ +void +br_sha1_init(br_sha1_context *cc) +{ + cc->vtable = &br_sha1_vtable; + memcpy(cc->val, br_sha1_IV, sizeof cc->val); + cc->count = 0; +} + +/* see bearssl.h */ +void +br_sha1_update(br_sha1_context *cc, const void *data, size_t len) +{ + const unsigned char *buf; + size_t ptr; + + buf = data; + ptr = (size_t)cc->count & 63; + while (len > 0) { + size_t clen; + + clen = 64 - ptr; + if (clen > len) { + clen = len; + } + memcpy(cc->buf + ptr, buf, clen); + ptr += clen; + buf += clen; + len -= clen; + cc->count += (uint64_t)clen; + if (ptr == 64) { + br_sha1_round(cc->buf, cc->val); + ptr = 0; + } + } +} + +/* see bearssl.h */ +void +br_sha1_out(const br_sha1_context *cc, void *dst) +{ + unsigned char buf[64]; + uint32_t val[5]; + size_t ptr; + + ptr = (size_t)cc->count & 63; + memcpy(buf, cc->buf, ptr); + memcpy(val, cc->val, sizeof val); + buf[ptr ++] = 0x80; + if (ptr > 56) { + memset(buf + ptr, 0, 64 - ptr); + br_sha1_round(buf, val); + memset(buf, 0, 56); + } else { + memset(buf + ptr, 0, 56 - ptr); + } + br_enc64be(buf + 56, cc->count << 3); + br_sha1_round(buf, val); + br_range_enc32be(dst, val, 5); +} + +/* see bearssl.h */ +uint64_t +br_sha1_state(const br_sha1_context *cc, void *dst) +{ + br_range_enc32be(dst, cc->val, 5); + return cc->count; +} + +/* see bearssl.h */ +void +br_sha1_set_state(br_sha1_context *cc, const void *stb, uint64_t count) +{ + br_range_dec32be(cc->val, 5, stb); + cc->count = count; +} + +/* see bearssl.h */ +const br_hash_class br_sha1_vtable PROGMEM = { + sizeof(br_sha1_context), + BR_HASHDESC_ID(br_sha1_ID) + | BR_HASHDESC_OUT(20) + | BR_HASHDESC_STATE(20) + | BR_HASHDESC_LBLEN(6) + | BR_HASHDESC_MD_PADDING + | BR_HASHDESC_MD_PADDING_BE, + (void (*)(const br_hash_class **))&br_sha1_init, + (void (*)(const br_hash_class **, const void *, size_t))&br_sha1_update, + (void (*)(const br_hash_class *const *, void *))&br_sha1_out, + (uint64_t (*)(const br_hash_class *const *, void *))&br_sha1_state, + (void (*)(const br_hash_class **, const void *, uint64_t)) + &br_sha1_set_state +}; diff --git a/lib/bearssl-esp8266/src/hash/sha2big.c b/lib/bearssl-esp8266/src/hash/sha2big.c new file mode 100644 index 000000000..2a8d99b95 --- /dev/null +++ b/lib/bearssl-esp8266/src/hash/sha2big.c @@ -0,0 +1,285 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +#define CH(X, Y, Z) ((((Y) ^ (Z)) & (X)) ^ (Z)) +#define MAJ(X, Y, Z) (((Y) & (Z)) | (((Y) | (Z)) & (X))) + +#define ROTR(x, n) (((uint64_t)(x) << (64 - (n))) | ((uint64_t)(x) >> (n))) + +#define BSG5_0(x) (ROTR(x, 28) ^ ROTR(x, 34) ^ ROTR(x, 39)) +#define BSG5_1(x) (ROTR(x, 14) ^ ROTR(x, 18) ^ ROTR(x, 41)) +#define SSG5_0(x) (ROTR(x, 1) ^ ROTR(x, 8) ^ (uint64_t)((x) >> 7)) +#define SSG5_1(x) (ROTR(x, 19) ^ ROTR(x, 61) ^ (uint64_t)((x) >> 6)) + +static const uint64_t IV384[8] PROGMEM = { + 0xCBBB9D5DC1059ED8, 0x629A292A367CD507, + 0x9159015A3070DD17, 0x152FECD8F70E5939, + 0x67332667FFC00B31, 0x8EB44A8768581511, + 0xDB0C2E0D64F98FA7, 0x47B5481DBEFA4FA4 +}; + +static const uint64_t IV512[8] PROGMEM = { + 0x6A09E667F3BCC908, 0xBB67AE8584CAA73B, + 0x3C6EF372FE94F82B, 0xA54FF53A5F1D36F1, + 0x510E527FADE682D1, 0x9B05688C2B3E6C1F, + 0x1F83D9ABFB41BD6B, 0x5BE0CD19137E2179 +}; + +static const uint64_t K[80] PROGMEM = { + 0x428A2F98D728AE22, 0x7137449123EF65CD, + 0xB5C0FBCFEC4D3B2F, 0xE9B5DBA58189DBBC, + 0x3956C25BF348B538, 0x59F111F1B605D019, + 0x923F82A4AF194F9B, 0xAB1C5ED5DA6D8118, + 0xD807AA98A3030242, 0x12835B0145706FBE, + 0x243185BE4EE4B28C, 0x550C7DC3D5FFB4E2, + 0x72BE5D74F27B896F, 0x80DEB1FE3B1696B1, + 0x9BDC06A725C71235, 0xC19BF174CF692694, + 0xE49B69C19EF14AD2, 0xEFBE4786384F25E3, + 0x0FC19DC68B8CD5B5, 0x240CA1CC77AC9C65, + 0x2DE92C6F592B0275, 0x4A7484AA6EA6E483, + 0x5CB0A9DCBD41FBD4, 0x76F988DA831153B5, + 0x983E5152EE66DFAB, 0xA831C66D2DB43210, + 0xB00327C898FB213F, 0xBF597FC7BEEF0EE4, + 0xC6E00BF33DA88FC2, 0xD5A79147930AA725, + 0x06CA6351E003826F, 0x142929670A0E6E70, + 0x27B70A8546D22FFC, 0x2E1B21385C26C926, + 0x4D2C6DFC5AC42AED, 0x53380D139D95B3DF, + 0x650A73548BAF63DE, 0x766A0ABB3C77B2A8, + 0x81C2C92E47EDAEE6, 0x92722C851482353B, + 0xA2BFE8A14CF10364, 0xA81A664BBC423001, + 0xC24B8B70D0F89791, 0xC76C51A30654BE30, + 0xD192E819D6EF5218, 0xD69906245565A910, + 0xF40E35855771202A, 0x106AA07032BBD1B8, + 0x19A4C116B8D2D0C8, 0x1E376C085141AB53, + 0x2748774CDF8EEB99, 0x34B0BCB5E19B48A8, + 0x391C0CB3C5C95A63, 0x4ED8AA4AE3418ACB, + 0x5B9CCA4F7763E373, 0x682E6FF3D6B2B8A3, + 0x748F82EE5DEFB2FC, 0x78A5636F43172F60, + 0x84C87814A1F0AB72, 0x8CC702081A6439EC, + 0x90BEFFFA23631E28, 0xA4506CEBDE82BDE9, + 0xBEF9A3F7B2C67915, 0xC67178F2E372532B, + 0xCA273ECEEA26619C, 0xD186B8C721C0C207, + 0xEADA7DD6CDE0EB1E, 0xF57D4F7FEE6ED178, + 0x06F067AA72176FBA, 0x0A637DC5A2C898A6, + 0x113F9804BEF90DAE, 0x1B710B35131C471B, + 0x28DB77F523047D84, 0x32CAAB7B40C72493, + 0x3C9EBE0A15C9BEBC, 0x431D67C49C100D4C, + 0x4CC5D4BECB3E42B6, 0x597F299CFC657E2A, + 0x5FCB6FAB3AD6FAEC, 0x6C44198C4A475817 +}; + +static void +sha2big_round(const unsigned char *buf, uint64_t *val) +{ + +#define SHA2BIG_STEP(A, B, C, D, E, F, G, H, j) do { \ + uint64_t T1, T2; \ + T1 = H + BSG5_1(E) + CH(E, F, G) + K[j] + w[j]; \ + T2 = BSG5_0(A) + MAJ(A, B, C); \ + D += T1; \ + H = T1 + T2; \ + } while (0) + + int i; + uint64_t a, b, c, d, e, f, g, h; + uint64_t w[80]; + + br_range_dec64be(w, 16, buf); + for (i = 16; i < 80; i ++) { + w[i] = SSG5_1(w[i - 2]) + w[i - 7] + + SSG5_0(w[i - 15]) + w[i - 16]; + } + a = val[0]; + b = val[1]; + c = val[2]; + d = val[3]; + e = val[4]; + f = val[5]; + g = val[6]; + h = val[7]; + for (i = 0; i < 80; i += 8) { + SHA2BIG_STEP(a, b, c, d, e, f, g, h, i + 0); + SHA2BIG_STEP(h, a, b, c, d, e, f, g, i + 1); + SHA2BIG_STEP(g, h, a, b, c, d, e, f, i + 2); + SHA2BIG_STEP(f, g, h, a, b, c, d, e, i + 3); + SHA2BIG_STEP(e, f, g, h, a, b, c, d, i + 4); + SHA2BIG_STEP(d, e, f, g, h, a, b, c, i + 5); + SHA2BIG_STEP(c, d, e, f, g, h, a, b, i + 6); + SHA2BIG_STEP(b, c, d, e, f, g, h, a, i + 7); + } + val[0] += a; + val[1] += b; + val[2] += c; + val[3] += d; + val[4] += e; + val[5] += f; + val[6] += g; + val[7] += h; +} + +static void +sha2big_update(br_sha384_context *cc, const void *data, size_t len) +{ + const unsigned char *buf; + size_t ptr; + + buf = data; + ptr = (size_t)cc->count & 127; + cc->count += (uint64_t)len; + while (len > 0) { + size_t clen; + + clen = 128 - ptr; + if (clen > len) { + clen = len; + } + memcpy(cc->buf + ptr, buf, clen); + ptr += clen; + buf += clen; + len -= clen; + if (ptr == 128) { + sha2big_round(cc->buf, cc->val); + ptr = 0; + } + } +} + +static void +sha2big_out(const br_sha384_context *cc, void *dst, int num) +{ + unsigned char buf[128]; + uint64_t val[8]; + size_t ptr; + + ptr = (size_t)cc->count & 127; + memcpy(buf, cc->buf, ptr); + memcpy(val, cc->val, sizeof val); + buf[ptr ++] = 0x80; + if (ptr > 112) { + memset(buf + ptr, 0, 128 - ptr); + sha2big_round(buf, val); + memset(buf, 0, 112); + } else { + memset(buf + ptr, 0, 112 - ptr); + } + br_enc64be(buf + 112, cc->count >> 61); + br_enc64be(buf + 120, cc->count << 3); + sha2big_round(buf, val); + br_range_enc64be(dst, val, num); +} + +/* see bearssl.h */ +void +br_sha384_init(br_sha384_context *cc) +{ + cc->vtable = &br_sha384_vtable; + memcpy(cc->val, IV384, sizeof IV384); + cc->count = 0; +} + +/* see bearssl.h */ +void +br_sha384_update(br_sha384_context *cc, const void *data, size_t len) +{ + sha2big_update(cc, data, len); +} + +/* see bearssl.h */ +void +br_sha384_out(const br_sha384_context *cc, void *dst) +{ + sha2big_out(cc, dst, 6); +} + +/* see bearssl.h */ +uint64_t +br_sha384_state(const br_sha384_context *cc, void *dst) +{ + br_range_enc64be(dst, cc->val, 8); + return cc->count; +} + +/* see bearssl.h */ +void +br_sha384_set_state(br_sha384_context *cc, const void *stb, uint64_t count) +{ + br_range_dec64be(cc->val, 8, stb); + cc->count = count; +} + +/* see bearssl.h */ +void +br_sha512_init(br_sha512_context *cc) +{ + cc->vtable = &br_sha512_vtable; + memcpy(cc->val, IV512, sizeof IV512); + cc->count = 0; +} + +/* see bearssl.h */ +void +br_sha512_out(const br_sha512_context *cc, void *dst) +{ + sha2big_out(cc, dst, 8); +} + +/* see bearssl.h */ +const br_hash_class br_sha384_vtable PROGMEM = { + sizeof(br_sha384_context), + BR_HASHDESC_ID(br_sha384_ID) + | BR_HASHDESC_OUT(48) + | BR_HASHDESC_STATE(64) + | BR_HASHDESC_LBLEN(7) + | BR_HASHDESC_MD_PADDING + | BR_HASHDESC_MD_PADDING_BE + | BR_HASHDESC_MD_PADDING_128, + (void (*)(const br_hash_class **))&br_sha384_init, + (void (*)(const br_hash_class **, const void *, size_t)) + &br_sha384_update, + (void (*)(const br_hash_class *const *, void *))&br_sha384_out, + (uint64_t (*)(const br_hash_class *const *, void *))&br_sha384_state, + (void (*)(const br_hash_class **, const void *, uint64_t)) + &br_sha384_set_state +}; + +/* see bearssl.h */ +const br_hash_class br_sha512_vtable PROGMEM = { + sizeof(br_sha512_context), + BR_HASHDESC_ID(br_sha512_ID) + | BR_HASHDESC_OUT(64) + | BR_HASHDESC_STATE(64) + | BR_HASHDESC_LBLEN(7) + | BR_HASHDESC_MD_PADDING + | BR_HASHDESC_MD_PADDING_BE + | BR_HASHDESC_MD_PADDING_128, + (void (*)(const br_hash_class **))&br_sha512_init, + (void (*)(const br_hash_class **, const void *, size_t)) + &br_sha512_update, + (void (*)(const br_hash_class *const *, void *))&br_sha512_out, + (uint64_t (*)(const br_hash_class *const *, void *))&br_sha512_state, + (void (*)(const br_hash_class **, const void *, uint64_t)) + &br_sha512_set_state +}; diff --git a/lib/bearssl-esp8266/src/hash/sha2small.c b/lib/bearssl-esp8266/src/hash/sha2small.c new file mode 100644 index 000000000..b4b12b13d --- /dev/null +++ b/lib/bearssl-esp8266/src/hash/sha2small.c @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +#define CH(X, Y, Z) ((((Y) ^ (Z)) & (X)) ^ (Z)) +#define MAJ(X, Y, Z) (((Y) & (Z)) | (((Y) | (Z)) & (X))) + +#define ROTR(x, n) (((uint32_t)(x) << (32 - (n))) | ((uint32_t)(x) >> (n))) + +#define BSG2_0(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22)) +#define BSG2_1(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25)) +#define SSG2_0(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ (uint32_t)((x) >> 3)) +#define SSG2_1(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ (uint32_t)((x) >> 10)) + +/* see inner.h */ +const uint32_t br_sha224_IV[8] PROGMEM = { + 0xC1059ED8, 0x367CD507, 0x3070DD17, 0xF70E5939, + 0xFFC00B31, 0x68581511, 0x64F98FA7, 0xBEFA4FA4 +}; + +/* see inner.h */ +const uint32_t br_sha256_IV[8] PROGMEM = { + 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, + 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19 +}; + +static const uint32_t K[64] PROGMEM = { + 0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, + 0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5, + 0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3, + 0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174, + 0xE49B69C1, 0xEFBE4786, 0x0FC19DC6, 0x240CA1CC, + 0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA, + 0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7, + 0xC6E00BF3, 0xD5A79147, 0x06CA6351, 0x14292967, + 0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13, + 0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85, + 0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3, + 0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070, + 0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5, + 0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3, + 0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208, + 0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2 +}; + +/* see inner.h */ +void +br_sha2small_round(const unsigned char *buf, uint32_t *val) +{ + +#define SHA2_STEP(A, B, C, D, E, F, G, H, j) do { \ + uint32_t T1, T2; \ + T1 = H + BSG2_1(E) + CH(E, F, G) + K[j] + w[j]; \ + T2 = BSG2_0(A) + MAJ(A, B, C); \ + D += T1; \ + H = T1 + T2; \ + } while (0) + + int i; + uint32_t a, b, c, d, e, f, g, h; + uint32_t w[64]; + + br_range_dec32be(w, 16, buf); + for (i = 16; i < 64; i ++) { + w[i] = SSG2_1(w[i - 2]) + w[i - 7] + + SSG2_0(w[i - 15]) + w[i - 16]; + } + a = val[0]; + b = val[1]; + c = val[2]; + d = val[3]; + e = val[4]; + f = val[5]; + g = val[6]; + h = val[7]; + for (i = 0; i < 64; i += 8) { + SHA2_STEP(a, b, c, d, e, f, g, h, i + 0); + SHA2_STEP(h, a, b, c, d, e, f, g, i + 1); + SHA2_STEP(g, h, a, b, c, d, e, f, i + 2); + SHA2_STEP(f, g, h, a, b, c, d, e, i + 3); + SHA2_STEP(e, f, g, h, a, b, c, d, i + 4); + SHA2_STEP(d, e, f, g, h, a, b, c, i + 5); + SHA2_STEP(c, d, e, f, g, h, a, b, i + 6); + SHA2_STEP(b, c, d, e, f, g, h, a, i + 7); + } + val[0] += a; + val[1] += b; + val[2] += c; + val[3] += d; + val[4] += e; + val[5] += f; + val[6] += g; + val[7] += h; + +#if 0 +/* obsolete */ +#define SHA2_MEXP1(pc) do { \ + W[pc] = br_dec32be(buf + ((pc) << 2)); \ + } while (0) + +#define SHA2_MEXP2(pc) do { \ + W[(pc) & 0x0F] = SSG2_1(W[((pc) - 2) & 0x0F]) \ + + W[((pc) - 7) & 0x0F] \ + + SSG2_0(W[((pc) - 15) & 0x0F]) + W[(pc) & 0x0F]; \ + } while (0) + +#define SHA2_STEPn(n, a, b, c, d, e, f, g, h, pc) do { \ + uint32_t t1, t2; \ + SHA2_MEXP ## n(pc); \ + t1 = h + BSG2_1(e) + CH(e, f, g) \ + + K[pcount + (pc)] + W[(pc) & 0x0F]; \ + t2 = BSG2_0(a) + MAJ(a, b, c); \ + d += t1; \ + h = t1 + t2; \ + } while (0) + +#define SHA2_STEP1(a, b, c, d, e, f, g, h, pc) \ + SHA2_STEPn(1, a, b, c, d, e, f, g, h, pc) +#define SHA2_STEP2(a, b, c, d, e, f, g, h, pc) \ + SHA2_STEPn(2, a, b, c, d, e, f, g, h, pc) + + uint32_t A, B, C, D, E, F, G, H; + uint32_t W[16]; + unsigned pcount; + + A = val[0]; + B = val[1]; + C = val[2]; + D = val[3]; + E = val[4]; + F = val[5]; + G = val[6]; + H = val[7]; + pcount = 0; + SHA2_STEP1(A, B, C, D, E, F, G, H, 0); + SHA2_STEP1(H, A, B, C, D, E, F, G, 1); + SHA2_STEP1(G, H, A, B, C, D, E, F, 2); + SHA2_STEP1(F, G, H, A, B, C, D, E, 3); + SHA2_STEP1(E, F, G, H, A, B, C, D, 4); + SHA2_STEP1(D, E, F, G, H, A, B, C, 5); + SHA2_STEP1(C, D, E, F, G, H, A, B, 6); + SHA2_STEP1(B, C, D, E, F, G, H, A, 7); + SHA2_STEP1(A, B, C, D, E, F, G, H, 8); + SHA2_STEP1(H, A, B, C, D, E, F, G, 9); + SHA2_STEP1(G, H, A, B, C, D, E, F, 10); + SHA2_STEP1(F, G, H, A, B, C, D, E, 11); + SHA2_STEP1(E, F, G, H, A, B, C, D, 12); + SHA2_STEP1(D, E, F, G, H, A, B, C, 13); + SHA2_STEP1(C, D, E, F, G, H, A, B, 14); + SHA2_STEP1(B, C, D, E, F, G, H, A, 15); + for (pcount = 16; pcount < 64; pcount += 16) { + SHA2_STEP2(A, B, C, D, E, F, G, H, 0); + SHA2_STEP2(H, A, B, C, D, E, F, G, 1); + SHA2_STEP2(G, H, A, B, C, D, E, F, 2); + SHA2_STEP2(F, G, H, A, B, C, D, E, 3); + SHA2_STEP2(E, F, G, H, A, B, C, D, 4); + SHA2_STEP2(D, E, F, G, H, A, B, C, 5); + SHA2_STEP2(C, D, E, F, G, H, A, B, 6); + SHA2_STEP2(B, C, D, E, F, G, H, A, 7); + SHA2_STEP2(A, B, C, D, E, F, G, H, 8); + SHA2_STEP2(H, A, B, C, D, E, F, G, 9); + SHA2_STEP2(G, H, A, B, C, D, E, F, 10); + SHA2_STEP2(F, G, H, A, B, C, D, E, 11); + SHA2_STEP2(E, F, G, H, A, B, C, D, 12); + SHA2_STEP2(D, E, F, G, H, A, B, C, 13); + SHA2_STEP2(C, D, E, F, G, H, A, B, 14); + SHA2_STEP2(B, C, D, E, F, G, H, A, 15); + } + val[0] += A; + val[1] += B; + val[2] += C; + val[3] += D; + val[4] += E; + val[5] += F; + val[6] += G; + val[7] += H; +#endif +} + +static void +sha2small_update(br_sha224_context *cc, const void *data, size_t len) +{ + const unsigned char *buf; + size_t ptr; + + buf = data; + ptr = (size_t)cc->count & 63; + cc->count += (uint64_t)len; + while (len > 0) { + size_t clen; + + clen = 64 - ptr; + if (clen > len) { + clen = len; + } + memcpy_P(cc->buf + ptr, buf, clen); + ptr += clen; + buf += clen; + len -= clen; + if (ptr == 64) { + br_sha2small_round(cc->buf, cc->val); + ptr = 0; + } + } +} + +static void +sha2small_out(const br_sha224_context *cc, void *dst, int num) +{ + unsigned char buf[64]; + uint32_t val[8]; + size_t ptr; + + ptr = (size_t)cc->count & 63; + memcpy(buf, cc->buf, ptr); + memcpy(val, cc->val, sizeof val); + buf[ptr ++] = 0x80; + if (ptr > 56) { + memset(buf + ptr, 0, 64 - ptr); + br_sha2small_round(buf, val); + memset(buf, 0, 56); + } else { + memset(buf + ptr, 0, 56 - ptr); + } + br_enc64be(buf + 56, cc->count << 3); + br_sha2small_round(buf, val); + br_range_enc32be(dst, val, num); +} + +/* see bearssl.h */ +void +br_sha224_init(br_sha224_context *cc) +{ + cc->vtable = &br_sha224_vtable; + memcpy(cc->val, br_sha224_IV, sizeof cc->val); + cc->count = 0; +} + +/* see bearssl.h */ +void +br_sha224_update(br_sha224_context *cc, const void *data, size_t len) +{ + sha2small_update(cc, data, len); +} + +/* see bearssl.h */ +void +br_sha224_out(const br_sha224_context *cc, void *dst) +{ + sha2small_out(cc, dst, 7); +} + +/* see bearssl.h */ +uint64_t +br_sha224_state(const br_sha224_context *cc, void *dst) +{ + br_range_enc32be(dst, cc->val, 8); + return cc->count; +} + +/* see bearssl.h */ +void +br_sha224_set_state(br_sha224_context *cc, const void *stb, uint64_t count) +{ + br_range_dec32be(cc->val, 8, stb); + cc->count = count; +} + +/* see bearssl.h */ +void +br_sha256_init(br_sha256_context *cc) +{ + cc->vtable = &br_sha256_vtable; + memcpy(cc->val, br_sha256_IV, sizeof cc->val); + cc->count = 0; +} + +/* see bearssl.h */ +void +br_sha256_out(const br_sha256_context *cc, void *dst) +{ + sha2small_out(cc, dst, 8); +} + +/* see bearssl.h */ +const br_hash_class br_sha224_vtable PROGMEM = { + sizeof(br_sha224_context), + BR_HASHDESC_ID(br_sha224_ID) + | BR_HASHDESC_OUT(28) + | BR_HASHDESC_STATE(32) + | BR_HASHDESC_LBLEN(6) + | BR_HASHDESC_MD_PADDING + | BR_HASHDESC_MD_PADDING_BE, + (void (*)(const br_hash_class **))&br_sha224_init, + (void (*)(const br_hash_class **, + const void *, size_t))&br_sha224_update, + (void (*)(const br_hash_class *const *, void *))&br_sha224_out, + (uint64_t (*)(const br_hash_class *const *, void *))&br_sha224_state, + (void (*)(const br_hash_class **, const void *, uint64_t)) + &br_sha224_set_state +}; + +/* see bearssl.h */ +const br_hash_class br_sha256_vtable PROGMEM = { + sizeof(br_sha256_context), + BR_HASHDESC_ID(br_sha256_ID) + | BR_HASHDESC_OUT(32) + | BR_HASHDESC_STATE(32) + | BR_HASHDESC_LBLEN(6) + | BR_HASHDESC_MD_PADDING + | BR_HASHDESC_MD_PADDING_BE, + (void (*)(const br_hash_class **))&br_sha256_init, + (void (*)(const br_hash_class **, + const void *, size_t))&br_sha256_update, + (void (*)(const br_hash_class *const *, void *))&br_sha256_out, + (uint64_t (*)(const br_hash_class *const *, void *))&br_sha256_state, + (void (*)(const br_hash_class **, const void *, uint64_t)) + &br_sha256_set_state +}; diff --git a/lib/bearssl-esp8266/src/int/i15_add.c b/lib/bearssl-esp8266/src/int/i15_add.c new file mode 100644 index 000000000..f82e14259 --- /dev/null +++ b/lib/bearssl-esp8266/src/int/i15_add.c @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +uint32_t +br_i15_add(uint16_t *a, const uint16_t *b, uint32_t ctl) +{ + uint32_t cc; + size_t u, m; + + cc = 0; + m = (a[0] + 31) >> 4; + for (u = 1; u < m; u ++) { + uint32_t aw, bw, naw; + + aw = a[u]; + bw = pgm_read_word(&b[u]); + naw = aw + bw + cc; + cc = naw >> 15; + a[u] = MUX(ctl, naw & 0x7FFF, aw); + } + return cc; +} diff --git a/lib/bearssl-esp8266/src/int/i15_bitlen.c b/lib/bearssl-esp8266/src/int/i15_bitlen.c new file mode 100644 index 000000000..c903a7199 --- /dev/null +++ b/lib/bearssl-esp8266/src/int/i15_bitlen.c @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +uint32_t +br_i15_bit_length(uint16_t *x, size_t xlen) +{ + uint32_t tw, twk; + + tw = 0; + twk = 0; + while (xlen -- > 0) { + uint32_t w, c; + + c = EQ(tw, 0); + w = x[xlen]; + tw = MUX(c, w, tw); + twk = MUX(c, (uint32_t)xlen, twk); + } + return (twk << 4) + BIT_LENGTH(tw); +} diff --git a/lib/bearssl-esp8266/src/int/i15_decmod.c b/lib/bearssl-esp8266/src/int/i15_decmod.c new file mode 100644 index 000000000..1da5dbe97 --- /dev/null +++ b/lib/bearssl-esp8266/src/int/i15_decmod.c @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +uint32_t +br_i15_decode_mod(uint16_t *x, const void *src, size_t len, const uint16_t *m) +{ + /* + * Two-pass algorithm: in the first pass, we determine whether the + * value fits; in the second pass, we do the actual write. + * + * During the first pass, 'r' contains the comparison result so + * far: + * 0x00000000 value is equal to the modulus + * 0x00000001 value is greater than the modulus + * 0xFFFFFFFF value is lower than the modulus + * + * Since we iterate starting with the least significant bytes (at + * the end of src[]), each new comparison overrides the previous + * except when the comparison yields 0 (equal). + * + * During the second pass, 'r' is either 0xFFFFFFFF (value fits) + * or 0x00000000 (value does not fit). + * + * We must iterate over all bytes of the source, _and_ possibly + * some extra virtual bytes (with value 0) so as to cover the + * complete modulus as well. We also add 4 such extra bytes beyond + * the modulus length because it then guarantees that no accumulated + * partial word remains to be processed. + */ + const unsigned char *buf; + size_t mlen, tlen; + int pass; + uint32_t r; + + buf = src; + mlen = (pgm_read_word(&m[0]) + 15) >> 4; + tlen = (mlen << 1); + if (tlen < len) { + tlen = len; + } + tlen += 4; + r = 0; + for (pass = 0; pass < 2; pass ++) { + size_t u, v; + uint32_t acc; + int acc_len; + + v = 1; + acc = 0; + acc_len = 0; + for (u = 0; u < tlen; u ++) { + uint32_t b; + + if (u < len) { + b = pgm_read_byte(&buf[len - 1 - u]); + } else { + b = 0; + } + acc |= (b << acc_len); + acc_len += 8; + if (acc_len >= 15) { + uint32_t xw; + + xw = acc & (uint32_t)0x7FFF; + acc_len -= 15; + acc = b >> (8 - acc_len); + if (v <= mlen) { + if (pass) { + x[v] = r & xw; + } else { + uint32_t cc; + + cc = (uint32_t)CMP(xw, pgm_read_word(&m[v])); + r = MUX(EQ(cc, 0), r, cc); + } + } else { + if (!pass) { + r = MUX(EQ(xw, 0), r, 1); + } + } + v ++; + } + } + + /* + * When we reach this point at the end of the first pass: + * r is either 0, 1 or -1; we want to set r to 0 if it + * is equal to 0 or 1, and leave it to -1 otherwise. + * + * When we reach this point at the end of the second pass: + * r is either 0 or -1; we want to leave that value + * untouched. This is a subcase of the previous. + */ + r >>= 1; + r |= (r << 1); + } + + x[0] = pgm_read_word(&m[0]); + return r & (uint32_t)1; +} diff --git a/lib/bearssl-esp8266/src/int/i15_decode.c b/lib/bearssl-esp8266/src/int/i15_decode.c new file mode 100644 index 000000000..f277a3485 --- /dev/null +++ b/lib/bearssl-esp8266/src/int/i15_decode.c @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +void +br_i15_decode(uint16_t *x, const void *src, size_t len) +{ + const unsigned char *buf; + size_t v; + uint32_t acc; + int acc_len; + + buf = src; + v = 1; + acc = 0; + acc_len = 0; + while (len -- > 0) { + uint32_t b; + + b = pgm_read_byte(&buf[len]); + acc |= (b << acc_len); + acc_len += 8; + if (acc_len >= 15) { + x[v ++] = acc & 0x7FFF; + acc_len -= 15; + acc >>= 15; + } + } + if (acc_len != 0) { + x[v ++] = acc; + } + x[0] = br_i15_bit_length(x + 1, v - 1); +} diff --git a/lib/bearssl-esp8266/src/int/i15_decred.c b/lib/bearssl-esp8266/src/int/i15_decred.c new file mode 100644 index 000000000..c0953cd1e --- /dev/null +++ b/lib/bearssl-esp8266/src/int/i15_decred.c @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +void +br_i15_decode_reduce(uint16_t *x, + const void *src, size_t len, const uint16_t *m) +{ + uint32_t m_ebitlen, m_rbitlen; + size_t mblen, k; + const unsigned char *buf; + uint32_t acc; + int acc_len; + + /* + * Get the encoded bit length. + */ + m_ebitlen = pgm_read_word(&m[0]); + + /* + * Special case for an invalid (null) modulus. + */ + if (m_ebitlen == 0) { + x[0] = 0; + return; + } + + /* + * Clear the destination. + */ + br_i15_zero(x, m_ebitlen); + + /* + * First decode directly as many bytes as possible. This requires + * computing the actual bit length. + */ + m_rbitlen = m_ebitlen >> 4; + m_rbitlen = (m_ebitlen & 15) + (m_rbitlen << 4) - m_rbitlen; + mblen = (m_rbitlen + 7) >> 3; + k = mblen - 1; + if (k >= len) { + br_i15_decode(x, src, len); + x[0] = m_ebitlen; + return; + } + buf = src; + br_i15_decode(x, buf, k); + x[0] = m_ebitlen; + + /* + * Input remaining bytes, using 15-bit words. + */ + acc = 0; + acc_len = 0; + while (k < len) { + uint32_t v; + + v = pgm_read_byte(&buf[k ++]); + acc = (acc << 8) | v; + acc_len += 8; + if (acc_len >= 15) { + br_i15_muladd_small(x, acc >> (acc_len - 15), m); + acc_len -= 15; + acc &= ~((uint32_t)-1 << acc_len); + } + } + + /* + * We may have some bits accumulated. We then perform a shift to + * be able to inject these bits as a full 15-bit word. + */ + if (acc_len != 0) { + acc = (acc | (x[1] << acc_len)) & 0x7FFF; + br_i15_rshift(x, 15 - acc_len); + br_i15_muladd_small(x, acc, m); + } +} diff --git a/lib/bearssl-esp8266/src/int/i15_encode.c b/lib/bearssl-esp8266/src/int/i15_encode.c new file mode 100644 index 000000000..bc4cad407 --- /dev/null +++ b/lib/bearssl-esp8266/src/int/i15_encode.c @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +void +br_i15_encode(void *dst, size_t len, const uint16_t *x) +{ + unsigned char *buf; + size_t u, xlen; + uint32_t acc; + int acc_len; + + xlen = (pgm_read_word(&x[0]) + 15) >> 4; + if (xlen == 0) { + memset(dst, 0, len); + return; + } + u = 1; + acc = 0; + acc_len = 0; + buf = dst; + while (len -- > 0) { + if (acc_len < 8) { + if (u <= xlen) { + acc += (uint32_t)pgm_read_word(&x[u ++]) << acc_len; + } + acc_len += 15; + } + buf[len] = (unsigned char)acc; + acc >>= 8; + acc_len -= 8; + } +} diff --git a/lib/bearssl-esp8266/src/int/i15_fmont.c b/lib/bearssl-esp8266/src/int/i15_fmont.c new file mode 100644 index 000000000..8bf73a59f --- /dev/null +++ b/lib/bearssl-esp8266/src/int/i15_fmont.c @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +void +br_i15_from_monty(uint16_t *x, const uint16_t *m, uint16_t m0i) +{ + size_t len, u, v; + + len = (pgm_read_word(&m[0]) + 15) >> 4; + for (u = 0; u < len; u ++) { + uint32_t f, cc; + + f = MUL15(x[1], m0i) & 0x7FFF; + cc = 0; + for (v = 0; v < len; v ++) { + uint32_t z; + + z = (uint32_t)x[v + 1] + MUL15(f, pgm_read_word(&m[v + 1])) + cc; + cc = z >> 15; + if (v != 0) { + x[v] = z & 0x7FFF; + } + } + x[len] = cc; + } + + /* + * We may have to do an extra subtraction, but only if the + * value in x[] is indeed greater than or equal to that of m[], + * which is why we must do two calls (first call computes the + * carry, second call performs the subtraction only if the carry + * is 0). + */ + br_i15_sub(x, m, NOT(br_i15_sub(x, m, 0))); +} diff --git a/lib/bearssl-esp8266/src/int/i15_iszero.c b/lib/bearssl-esp8266/src/int/i15_iszero.c new file mode 100644 index 000000000..596d8b9a7 --- /dev/null +++ b/lib/bearssl-esp8266/src/int/i15_iszero.c @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +uint32_t +br_i15_iszero(const uint16_t *x) +{ + uint32_t z; + size_t u; + + z = 0; + for (u = (pgm_read_word(&x[0]) + 15) >> 4; u > 0; u --) { + z |= pgm_read_word(&x[u]); + } + return ~(z | -z) >> 31; +} diff --git a/lib/bearssl-esp8266/src/int/i15_moddiv.c b/lib/bearssl-esp8266/src/int/i15_moddiv.c new file mode 100644 index 000000000..d265a7d90 --- /dev/null +++ b/lib/bearssl-esp8266/src/int/i15_moddiv.c @@ -0,0 +1,465 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * In this file, we handle big integers with a custom format, i.e. + * without the usual one-word header. Value is split into 15-bit words, + * each stored in a 16-bit slot (top bit is zero) in little-endian + * order. The length (in words) is provided explicitly. In some cases, + * the value can be negative (using two's complement representation). In + * some cases, the top word is allowed to have a 16th bit. + */ + +/* + * Negate big integer conditionally. The value consists of 'len' words, + * with 15 bits in each word (the top bit of each word should be 0, + * except possibly for the last word). If 'ctl' is 1, the negation is + * computed; otherwise, if 'ctl' is 0, then the value is unchanged. + */ +static void +cond_negate(uint16_t *a, size_t len, uint32_t ctl) +{ + size_t k; + uint32_t cc, xm; + + cc = ctl; + xm = 0x7FFF & -ctl; + for (k = 0; k < len; k ++) { + uint32_t aw; + + aw = a[k]; + aw = (aw ^ xm) + cc; + a[k] = aw & 0x7FFF; + cc = (aw >> 15) & 1; + } +} + +/* + * Finish modular reduction. Rules on input parameters: + * + * if neg = 1, then -m <= a < 0 + * if neg = 0, then 0 <= a < 2*m + * + * If neg = 0, then the top word of a[] may use 16 bits. + * + * Also, modulus m must be odd. + */ +static void +finish_mod(uint16_t *a, size_t len, const uint16_t *m, uint32_t neg) +{ + size_t k; + uint32_t cc, xm, ym; + + /* + * First pass: compare a (assumed nonnegative) with m. + */ + cc = 0; + for (k = 0; k < len; k ++) { + uint32_t aw, mw; + + aw = a[k]; + mw = pgm_read_word(&m[k]); + cc = (aw - mw - cc) >> 31; + } + + /* + * At this point: + * if neg = 1, then we must add m (regardless of cc) + * if neg = 0 and cc = 0, then we must subtract m + * if neg = 0 and cc = 1, then we must do nothing + */ + xm = 0x7FFF & -neg; + ym = -(neg | (1 - cc)); + cc = neg; + for (k = 0; k < len; k ++) { + uint32_t aw, mw; + + aw = a[k]; + mw = (pgm_read_word(&m[k]) ^ xm) & ym; + aw = aw - mw - cc; + a[k] = aw & 0x7FFF; + cc = aw >> 31; + } +} + +/* + * Compute: + * a <- (a*pa+b*pb)/(2^15) + * b <- (a*qa+b*qb)/(2^15) + * The division is assumed to be exact (i.e. the low word is dropped). + * If the final a is negative, then it is negated. Similarly for b. + * Returned value is the combination of two bits: + * bit 0: 1 if a had to be negated, 0 otherwise + * bit 1: 1 if b had to be negated, 0 otherwise + * + * Factors pa, pb, qa and qb must be at most 2^15 in absolute value. + * Source integers a and b must be nonnegative; top word is not allowed + * to contain an extra 16th bit. + */ +static uint32_t +co_reduce(uint16_t *a, uint16_t *b, size_t len, + int32_t pa, int32_t pb, int32_t qa, int32_t qb) +{ + size_t k; + int32_t cca, ccb; + uint32_t nega, negb; + + cca = 0; + ccb = 0; + for (k = 0; k < len; k ++) { + uint32_t wa, wb, za, zb; + uint16_t tta, ttb; + + /* + * Since: + * |pa| <= 2^15 + * |pb| <= 2^15 + * 0 <= wa <= 2^15 - 1 + * 0 <= wb <= 2^15 - 1 + * |cca| <= 2^16 - 1 + * Then: + * |za| <= (2^15-1)*(2^16) + (2^16-1) = 2^31 - 1 + * + * Thus, the new value of cca is such that |cca| <= 2^16 - 1. + * The same applies to ccb. + */ + wa = a[k]; + wb = b[k]; + za = wa * (uint32_t)pa + wb * (uint32_t)pb + (uint32_t)cca; + zb = wa * (uint32_t)qa + wb * (uint32_t)qb + (uint32_t)ccb; + if (k > 0) { + a[k - 1] = za & 0x7FFF; + b[k - 1] = zb & 0x7FFF; + } + tta = za >> 15; + ttb = zb >> 15; + cca = *(int16_t *)&tta; + ccb = *(int16_t *)&ttb; + } + a[len - 1] = (uint16_t)cca; + b[len - 1] = (uint16_t)ccb; + nega = (uint32_t)cca >> 31; + negb = (uint32_t)ccb >> 31; + cond_negate(a, len, nega); + cond_negate(b, len, negb); + return nega | (negb << 1); +} + +/* + * Compute: + * a <- (a*pa+b*pb)/(2^15) mod m + * b <- (a*qa+b*qb)/(2^15) mod m + * + * m0i is equal to -1/m[0] mod 2^15. + * + * Factors pa, pb, qa and qb must be at most 2^15 in absolute value. + * Source integers a and b must be nonnegative; top word is not allowed + * to contain an extra 16th bit. + */ +static void +co_reduce_mod(uint16_t *a, uint16_t *b, size_t len, + int32_t pa, int32_t pb, int32_t qa, int32_t qb, + const uint16_t *m, uint16_t m0i) +{ + size_t k; + int32_t cca, ccb, fa, fb; + + cca = 0; + ccb = 0; + fa = ((a[0] * (uint32_t)pa + b[0] * (uint32_t)pb) * m0i) & 0x7FFF; + fb = ((a[0] * (uint32_t)qa + b[0] * (uint32_t)qb) * m0i) & 0x7FFF; + for (k = 0; k < len; k ++) { + uint32_t wa, wb, za, zb; + uint32_t tta, ttb; + + /* + * In this loop, carries 'cca' and 'ccb' always fit on + * 17 bits (in absolute value). + */ + wa = a[k]; + wb = b[k]; + za = wa * (uint32_t)pa + wb * (uint32_t)pb + + pgm_read_word(&m[k]) * (uint32_t)fa + (uint32_t)cca; + zb = wa * (uint32_t)qa + wb * (uint32_t)qb + + pgm_read_word(&m[k]) * (uint32_t)fb + (uint32_t)ccb; + if (k > 0) { + a[k - 1] = za & 0x7FFF; + b[k - 1] = zb & 0x7FFF; + } + + /* + * The XOR-and-sub construction below does an arithmetic + * right shift in a portable way (technically, right-shifting + * a negative signed value is implementation-defined in C). + */ +#define M ((uint32_t)1 << 16) + tta = za >> 15; + ttb = zb >> 15; + tta = (tta ^ M) - M; + ttb = (ttb ^ M) - M; + cca = *(int32_t *)&tta; + ccb = *(int32_t *)&ttb; +#undef M + } + a[len - 1] = (uint32_t)cca; + b[len - 1] = (uint32_t)ccb; + + /* + * At this point: + * -m <= a < 2*m + * -m <= b < 2*m + * (this is a case of Montgomery reduction) + * The top word of 'a' and 'b' may have a 16-th bit set. + * We may have to add or subtract the modulus. + */ + finish_mod(a, len, m, (uint32_t)cca >> 31); + finish_mod(b, len, m, (uint32_t)ccb >> 31); +} + +/* see inner.h */ +uint32_t +br_i15_moddiv(uint16_t *x, const uint16_t *y, const uint16_t *m, uint16_t m0i, + uint16_t *t) +{ + /* + * Algorithm is an extended binary GCD. We maintain four values + * a, b, u and v, with the following invariants: + * + * a * x = y * u mod m + * b * x = y * v mod m + * + * Starting values are: + * + * a = y + * b = m + * u = x + * v = 0 + * + * The formal definition of the algorithm is a sequence of steps: + * + * - If a is even, then a <- a/2 and u <- u/2 mod m. + * - Otherwise, if b is even, then b <- b/2 and v <- v/2 mod m. + * - Otherwise, if a > b, then a <- (a-b)/2 and u <- (u-v)/2 mod m. + * - Otherwise, b <- (b-a)/2 and v <- (v-u)/2 mod m. + * + * Algorithm stops when a = b. At that point, they both are equal + * to GCD(y,m); the modular division succeeds if that value is 1. + * The result of the modular division is then u (or v: both are + * equal at that point). + * + * Each step makes either a or b shrink by at least one bit; hence, + * if m has bit length k bits, then 2k-2 steps are sufficient. + * + * + * Though complexity is quadratic in the size of m, the bit-by-bit + * processing is not very efficient. We can speed up processing by + * remarking that the decisions are taken based only on observation + * of the top and low bits of a and b. + * + * In the loop below, at each iteration, we use the two top words + * of a and b, and the low words of a and b, to compute reduction + * parameters pa, pb, qa and qb such that the new values for a + * and b are: + * + * a' = (a*pa + b*pb) / (2^15) + * b' = (a*qa + b*qb) / (2^15) + * + * the division being exact. + * + * Since the choices are based on the top words, they may be slightly + * off, requiring an optional correction: if a' < 0, then we replace + * pa with -pa, and pb with -pb. The total length of a and b is + * thus reduced by at least 14 bits at each iteration. + * + * The stopping conditions are still the same, though: when a + * and b become equal, they must be both odd (since m is odd, + * the GCD cannot be even), therefore the next operation is a + * subtraction, and one of the values becomes 0. At that point, + * nothing else happens, i.e. one value is stuck at 0, and the + * other one is the GCD. + */ + size_t len, k; + uint16_t *a, *b, *u, *v; + uint32_t num, r; + + len = (pgm_read_word(&m[0]) + 15) >> 4; + a = t; + b = a + len; + u = x + 1; + v = b + len; + memcpy_P(a, y + 1, len * sizeof *y); + memcpy_P(b, m + 1, len * sizeof *m); + memset(v, 0, len * sizeof *v); + + /* + * Loop below ensures that a and b are reduced by some bits each, + * for a total of at least 14 bits. + */ + for (num = ((pgm_read_word(&m[0]) - (pgm_read_word(&m[0]) >> 4)) << 1) + 14; num >= 14; num -= 14) { + size_t j; + uint32_t c0, c1; + uint32_t a0, a1, b0, b1; + uint32_t a_hi, b_hi, a_lo, b_lo; + int32_t pa, pb, qa, qb; + int i; + + /* + * Extract top words of a and b. If j is the highest + * index >= 1 such that a[j] != 0 or b[j] != 0, then we want + * (a[j] << 15) + a[j - 1], and (b[j] << 15) + b[j - 1]. + * If a and b are down to one word each, then we use a[0] + * and b[0]. + */ + c0 = (uint32_t)-1; + c1 = (uint32_t)-1; + a0 = 0; + a1 = 0; + b0 = 0; + b1 = 0; + j = len; + while (j -- > 0) { + uint32_t aw, bw; + + aw = a[j]; + bw = b[j]; + a0 ^= (a0 ^ aw) & c0; + a1 ^= (a1 ^ aw) & c1; + b0 ^= (b0 ^ bw) & c0; + b1 ^= (b1 ^ bw) & c1; + c1 = c0; + c0 &= (((aw | bw) + 0xFFFF) >> 16) - (uint32_t)1; + } + + /* + * If c1 = 0, then we grabbed two words for a and b. + * If c1 != 0 but c0 = 0, then we grabbed one word. It + * is not possible that c1 != 0 and c0 != 0, because that + * would mean that both integers are zero. + */ + a1 |= a0 & c1; + a0 &= ~c1; + b1 |= b0 & c1; + b0 &= ~c1; + a_hi = (a0 << 15) + a1; + b_hi = (b0 << 15) + b1; + a_lo = a[0]; + b_lo = b[0]; + + /* + * Compute reduction factors: + * + * a' = a*pa + b*pb + * b' = a*qa + b*qb + * + * such that a' and b' are both multiple of 2^15, but are + * only marginally larger than a and b. + */ + pa = 1; + pb = 0; + qa = 0; + qb = 1; + for (i = 0; i < 15; i ++) { + /* + * At each iteration: + * + * a <- (a-b)/2 if: a is odd, b is odd, a_hi > b_hi + * b <- (b-a)/2 if: a is odd, b is odd, a_hi <= b_hi + * a <- a/2 if: a is even + * b <- b/2 if: a is odd, b is even + * + * We multiply a_lo and b_lo by 2 at each + * iteration, thus a division by 2 really is a + * non-multiplication by 2. + */ + uint32_t r, oa, ob, cAB, cBA, cA; + + /* + * cAB = 1 if b must be subtracted from a + * cBA = 1 if a must be subtracted from b + * cA = 1 if a is divided by 2, 0 otherwise + * + * Rules: + * + * cAB and cBA cannot be both 1. + * if a is not divided by 2, b is. + */ + r = GT(a_hi, b_hi); + oa = (a_lo >> i) & 1; + ob = (b_lo >> i) & 1; + cAB = oa & ob & r; + cBA = oa & ob & NOT(r); + cA = cAB | NOT(oa); + + /* + * Conditional subtractions. + */ + a_lo -= b_lo & -cAB; + a_hi -= b_hi & -cAB; + pa -= qa & -(int32_t)cAB; + pb -= qb & -(int32_t)cAB; + b_lo -= a_lo & -cBA; + b_hi -= a_hi & -cBA; + qa -= pa & -(int32_t)cBA; + qb -= pb & -(int32_t)cBA; + + /* + * Shifting. + */ + a_lo += a_lo & (cA - 1); + pa += pa & ((int32_t)cA - 1); + pb += pb & ((int32_t)cA - 1); + a_hi ^= (a_hi ^ (a_hi >> 1)) & -cA; + b_lo += b_lo & -cA; + qa += qa & -(int32_t)cA; + qb += qb & -(int32_t)cA; + b_hi ^= (b_hi ^ (b_hi >> 1)) & (cA - 1); + } + + /* + * Replace a and b with new values a' and b'. + */ + r = co_reduce(a, b, len, pa, pb, qa, qb); + pa -= pa * ((r & 1) << 1); + pb -= pb * ((r & 1) << 1); + qa -= qa * (r & 2); + qb -= qb * (r & 2); + co_reduce_mod(u, v, len, pa, pb, qa, qb, m + 1, m0i); + } + + /* + * Now one of the arrays should be 0, and the other contains + * the GCD. If a is 0, then u is 0 as well, and v contains + * the division result. + * Result is correct if and only if GCD is 1. + */ + r = (a[0] | b[0]) ^ 1; + u[0] |= v[0]; + for (k = 1; k < len; k ++) { + r |= a[k] | b[k]; + u[k] |= v[k]; + } + return EQ0(r); +} diff --git a/lib/bearssl-esp8266/src/int/i15_modpow.c b/lib/bearssl-esp8266/src/int/i15_modpow.c new file mode 100644 index 000000000..bcfa2110c --- /dev/null +++ b/lib/bearssl-esp8266/src/int/i15_modpow.c @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +void +br_i15_modpow(uint16_t *x, + const unsigned char *e, size_t elen, + const uint16_t *m, uint16_t m0i, uint16_t *t1, uint16_t *t2) +{ + size_t mlen; + unsigned k; + + mlen = ((pgm_read_word(&m[0]) + 31) >> 4) * sizeof m[0]; + memcpy(t1, x, mlen); + br_i15_to_monty(t1, m); + br_i15_zero(x, pgm_read_word(&m[0])); + x[1] = 1; + for (k = 0; k < ((unsigned)elen << 3); k ++) { + uint32_t ctl; + + ctl = (pgm_read_byte(&e[elen - 1 - (k >> 3)]) >> (k & 7)) & 1; + br_i15_montymul(t2, x, t1, m, m0i); + CCOPY(ctl, x, t2, mlen); + br_i15_montymul(t2, t1, t1, m, m0i); + memcpy(t1, t2, mlen); + } +} diff --git a/lib/bearssl-esp8266/src/int/i15_modpow2.c b/lib/bearssl-esp8266/src/int/i15_modpow2.c new file mode 100644 index 000000000..e58045f01 --- /dev/null +++ b/lib/bearssl-esp8266/src/int/i15_modpow2.c @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +uint32_t +br_i15_modpow_opt(uint16_t *x, + const unsigned char *e, size_t elen, + const uint16_t *m, uint16_t m0i, uint16_t *tmp, size_t twlen) +{ + size_t mlen, mwlen; + uint16_t *t1, *t2, *base; + size_t u, v; + uint32_t acc; + int acc_len, win_len; + + /* + * Get modulus size. + */ + mwlen = (pgm_read_word(&m[0]) + 31) >> 4; + mlen = mwlen * sizeof m[0]; + mwlen += (mwlen & 1); + t1 = tmp; + t2 = tmp + mwlen; + + /* + * Compute possible window size, with a maximum of 5 bits. + * When the window has size 1 bit, we use a specific code + * that requires only two temporaries. Otherwise, for a + * window of k bits, we need 2^k+1 temporaries. + */ + if (twlen < (mwlen << 1)) { + return 0; + } + for (win_len = 5; win_len > 1; win_len --) { + if ((((uint32_t)1 << win_len) + 1) * mwlen <= twlen) { + break; + } + } + + /* + * Everything is done in Montgomery representation. + */ + br_i15_to_monty(x, m); + + /* + * Compute window contents. If the window has size one bit only, + * then t2 is set to x; otherwise, t2[0] is left untouched, and + * t2[k] is set to x^k (for k >= 1). + */ + if (win_len == 1) { + memcpy(t2, x, mlen); + } else { + memcpy(t2 + mwlen, x, mlen); + base = t2 + mwlen; + for (u = 2; u < ((unsigned)1 << win_len); u ++) { + br_i15_montymul(base + mwlen, base, x, m, m0i); + base += mwlen; + } + } + + /* + * We need to set x to 1, in Montgomery representation. This can + * be done efficiently by setting the high word to 1, then doing + * one word-sized shift. + */ + br_i15_zero(x, pgm_read_word(&m[0])); + x[(pgm_read_word(&m[0]) + 15) >> 4] = 1; + br_i15_muladd_small(x, 0, m); + + /* + * We process bits from most to least significant. At each + * loop iteration, we have acc_len bits in acc. + */ + acc = 0; + acc_len = 0; + while (acc_len > 0 || elen > 0) { + int i, k; + uint32_t bits; + + /* + * Get the next bits. + */ + k = win_len; + if (acc_len < win_len) { + if (elen > 0) { + acc = (acc << 8) | pgm_read_byte(&*e ++); + elen --; + acc_len += 8; + } else { + k = acc_len; + } + } + bits = (acc >> (acc_len - k)) & (((uint32_t)1 << k) - 1); + acc_len -= k; + + /* + * We could get exactly k bits. Compute k squarings. + */ + for (i = 0; i < k; i ++) { + br_i15_montymul(t1, x, x, m, m0i); + memcpy(x, t1, mlen); + } + + /* + * Window lookup: we want to set t2 to the window + * lookup value, assuming the bits are non-zero. If + * the window length is 1 bit only, then t2 is + * already set; otherwise, we do a constant-time lookup. + */ + if (win_len > 1) { + br_i15_zero(t2, pgm_read_word(&m[0])); + base = t2 + mwlen; + for (u = 1; u < ((uint32_t)1 << k); u ++) { + uint32_t mask; + + mask = -EQ(u, bits); + for (v = 1; v < mwlen; v ++) { + t2[v] |= mask & base[v]; + } + base += mwlen; + } + } + + /* + * Multiply with the looked-up value. We keep the + * product only if the exponent bits are not all-zero. + */ + br_i15_montymul(t1, x, t2, m, m0i); + CCOPY(NEQ(bits, 0), x, t1, mlen); + } + + /* + * Convert back from Montgomery representation, and exit. + */ + br_i15_from_monty(x, m, m0i); + return 1; +} diff --git a/lib/bearssl-esp8266/src/int/i15_montmul.c b/lib/bearssl-esp8266/src/int/i15_montmul.c new file mode 100644 index 000000000..9d6f70a49 --- /dev/null +++ b/lib/bearssl-esp8266/src/int/i15_montmul.c @@ -0,0 +1,321 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +void +br_i15_montymul(uint16_t *d, const uint16_t *x, const uint16_t *y, + const uint16_t *m, uint16_t m0i) +{ + size_t len, len4, u, v; + uint32_t dh; + + len = (pgm_read_word(&m[0]) + 15) >> 4; + len4 = len & ~(size_t)3; + br_i15_zero(d, pgm_read_word(&m[0])); + dh = 0; + for (u = 0; u < len; u ++) { + uint32_t f, xu, r, zh; + + xu = pgm_read_word(&x[u + 1]); + f = MUL15((d[1] + MUL15(pgm_read_word(&x[u + 1]), pgm_read_word(&y[1]))) & 0x7FFF, m0i) + & 0x7FFF; +#if BR_ARMEL_CORTEXM_GCC + if (len4 != 0) { + uint16_t *limit; + + limit = d + len4; + asm volatile ( +"\n\ + @ carry: r=r2 \n\ + @ multipliers: xu=r3 f=r4 \n\ + @ base registers: d+v=r5 y+v=r6 m+v=r7 \n\ + @ r8 contains 0x7FFF \n\ + @ r9 contains d+len4 \n\ + ldr r0, %[limit] \n\ + ldr r3, %[xu] \n\ + mov r9, r0 \n\ + ldr r4, %[f] \n\ + eor r2, r2 \n\ + ldr r5, %[d] \n\ + sub r1, r2, #1 \n\ + ldr r6, %[y] \n\ + lsr r1, r1, #17 \n\ + ldr r7, %[m] \n\ + mov r8, r1 \n\ +loop%=: \n\ + ldrh r0, [r6, #2] \n\ + ldrh r1, [r7, #2] \n\ + mul r0, r3 \n\ + mul r1, r4 \n\ + add r2, r0, r2 \n\ + ldrh r0, [r5, #2] \n\ + add r2, r1, r2 \n\ + mov r1, r8 \n\ + add r2, r0, r2 \n\ + and r1, r2 \n\ + lsr r2, r2, #15 \n\ + strh r1, [r5, #0] \n\ + \n\ + ldrh r0, [r6, #4] \n\ + ldrh r1, [r7, #4] \n\ + mul r0, r3 \n\ + mul r1, r4 \n\ + add r2, r0, r2 \n\ + ldrh r0, [r5, #4] \n\ + add r2, r1, r2 \n\ + mov r1, r8 \n\ + add r2, r0, r2 \n\ + and r1, r2 \n\ + lsr r2, r2, #15 \n\ + strh r1, [r5, #2] \n\ + \n\ + ldrh r0, [r6, #6] \n\ + ldrh r1, [r7, #6] \n\ + mul r0, r3 \n\ + mul r1, r4 \n\ + add r2, r0, r2 \n\ + ldrh r0, [r5, #6] \n\ + add r2, r1, r2 \n\ + mov r1, r8 \n\ + add r2, r0, r2 \n\ + and r1, r2 \n\ + lsr r2, r2, #15 \n\ + strh r1, [r5, #4] \n\ + \n\ + ldrh r0, [r6, #8] \n\ + ldrh r1, [r7, #8] \n\ + mul r0, r3 \n\ + mul r1, r4 \n\ + add r2, r0, r2 \n\ + ldrh r0, [r5, #8] \n\ + add r2, r1, r2 \n\ + mov r1, r8 \n\ + add r2, r0, r2 \n\ + and r1, r2 \n\ + lsr r2, r2, #15 \n\ + strh r1, [r5, #6] \n\ + \n\ + add r5, r5, #8 \n\ + add r6, r6, #8 \n\ + add r7, r7, #8 \n\ + cmp r5, r9 \n\ + bne loop%= \n\ + \n\ + str r2, %[carry] \n\ +" +: [carry] "=m" (r) +: [xu] "m" (xu), [f] "m" (f), [d] "m" (d), [y] "m" (y), + [m] "m" (m), [limit] "m" (limit) +: "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9" ); + } else { + r = 0; + } + v = len4; +#else + r = 0; + const uint16_t *py; + const uint16_t *pm; + + py = &y[0]; // addresses of both arrays that will be scanned as uint16_t + pm = &m[0]; + int py_unaligned = (((int)py) & 2) != 0; + int pm_unaligned = (((int)pm) & 2) != 0; + uint32_t ty, tm; // 32 bits buffers + if (!py_unaligned && !pm_unaligned) { + // both are aligned to 32 bits, we always skip the first 16 bits + ty = *(uint32_t*)py; // pre-load with 32 bits value, next value will be loaded at end of loop iteration + tm = *(uint32_t*)pm; + for (v = 0; v < len4; v += 4) { + uint16_t y1, y2, y3, y4; // we define 4 variables for easy reading + uint16_t m1, m2, m3, m4; // but optimizer will collapse them into 1 + + uint32_t z; + + y1 = ty >> 16; // v+1, get upper 16 bits current 32 bits + m1 = tm >> 16; + z = d[v + 1] + MUL15(xu, y1) + MUL15(f, m1) + r; + r = z >> 15; + d[v + 0] = z & 0x7FFF; + // + ty = *(uint32_t*)(py = py + 2); // next 32 bits + y2 = ty; // get lower 16 bits + tm = *(uint32_t*)(pm = pm + 2); + m2 = tm; + z = d[v + 2] + MUL15(xu, y2) + MUL15(f, m2) + r; + r = z >> 15; + d[v + 1] = z & 0x7FFF; + // + y3 = ty >> 16; + m3 = tm >> 16; + z = d[v + 3] + MUL15(xu, y3) + MUL15(f, m3) + r; + r = z >> 15; + d[v + 2] = z & 0x7FFF; + // + ty = *(uint32_t*)(py = py + 2); // next 32 bits + y4 = ty; // get lower 16 bits + tm = *(uint32_t*)(pm = pm + 2); + m4 = tm; + z = d[v + 4] + MUL15(xu, y4) + MUL15(f, m4) + r; + r = z >> 15; + d[v + 3] = z & 0x7FFF; + } + } else if (!py_unaligned && pm_unaligned) { + pm--; // we decrement by 1 because will increment by 2 at beginning of loop + ty = *(uint32_t*)py; // pre-load with 32 bits value + for (v = 0; v < len4; v += 4) { + uint16_t y1, y2, y3, y4; + uint16_t m1, m2, m3, m4; + uint32_t z; + + y1 = ty >> 16; // +1 + tm = *(uint32_t*)(pm = pm + 2); + m1 = tm; + z = d[v + 1] + MUL15(xu, y1) + MUL15(f, m1) + r; + r = z >> 15; + d[v + 0] = z & 0x7FFF; + // + ty = *(uint32_t*)(py = py + 2); // next 32 bits + y2 = ty; + m2 = tm >> 16; + z = d[v + 2] + MUL15(xu, y2) + MUL15(f, m2) + r; + r = z >> 15; + d[v + 1] = z & 0x7FFF; + // + y3 = ty >> 16; + tm = *(uint32_t*)(pm = pm + 2); + m3 = tm; + z = d[v + 3] + MUL15(xu, y3) + MUL15(f, m3) + r; + r = z >> 15; + d[v + 2] = z & 0x7FFF; + // + ty = *(uint32_t*)(py = py + 2); // next 32 bits + y4 = ty; + m4 = tm >> 16; + z = d[v + 4] + MUL15(xu, y4) + MUL15(f, m4) + r; + r = z >> 15; + d[v + 3] = z & 0x7FFF; + } + } else if (py_unaligned && !pm_unaligned) { // buggy + // py unaligned, pm aligned + py--; + tm = *(uint32_t*)pm; + for (v = 0; v < len4; v += 4) { + uint16_t y1, y2, y3, y4; + uint16_t m1, m2, m3, m4; + uint32_t z; + + ty = *(uint32_t*)(py = py + 2); // next 32 bits + y1 = ty; + m1 = tm >> 16; + z = d[v + 1] + MUL15(xu, y1) + MUL15(f, m1) + r; + r = z >> 15; + d[v + 0] = z & 0x7FFF; + // + y2 = ty >> 16; + tm = *(uint32_t*)(pm = pm + 2); + m2 = tm; + z = d[v + 2] + MUL15(xu, y2) + MUL15(f, m2) + r; + r = z >> 15; + d[v + 1] = z & 0x7FFF; + // + ty = *(uint32_t*)(py = py + 2); // next 32 bits + y3 = ty; + m3 = tm >> 16; + z = d[v + 3] + MUL15(xu, y3) + MUL15(f, m3) + r; + r = z >> 15; + d[v + 2] = z & 0x7FFF; + // + y4 = ty >> 16; + tm = *(uint32_t*)(pm = pm + 2); + m4 = tm; + z = d[v + 4] + MUL15(xu, y4) + MUL15(f, m4) + r; + r = z >> 15; + d[v + 3] = z & 0x7FFF; + } + } else { // if (py_unaligned && pm_unaligned) { + // py unaligned, pm unaligned + py--; + pm--; + for (v = 0; v < len4; v += 4) { + uint16_t y1, y2, y3, y4; + uint16_t m1, m2, m3, m4; + uint32_t z; + + ty = *(uint32_t*)(py = py + 2); // next 32 bits + y1 = ty; // +1 + tm = *(uint32_t*)(pm = pm + 2); + m1 = tm; + z = d[v + 1] + MUL15(xu, y1) + MUL15(f, m1) + r; + r = z >> 15; + d[v + 0] = z & 0x7FFF; + // + y2 = ty >> 16; + m2 = tm >> 16; + z = d[v + 2] + MUL15(xu, y2) + MUL15(f, m2) + r; + r = z >> 15; + d[v + 1] = z & 0x7FFF; + // + ty = *(uint32_t*)(py = py + 2); // next 32 bits + y3 = ty; + tm = *(uint32_t*)(pm = pm + 2); + m3 = tm; + z = d[v + 3] + MUL15(xu, y3) + MUL15(f, m3) + r; + r = z >> 15; + d[v + 2] = z & 0x7FFF; + // + y4 = ty >> 16; + m4 = tm >> 16; + z = d[v + 4] + MUL15(xu, y4) + MUL15(f, m4) + r; + r = z >> 15; + d[v + 3] = z & 0x7FFF; + } + } +#endif // BR_ARMEL_CORTEXM_GCC + for (; v < len; v ++) { + uint32_t z; + + z = d[v + 1] + MUL15(xu, pgm_read_word(&y[v + 1])) + + MUL15(f, pgm_read_word(&m[v + 1])) + r; + r = z >> 15; + d[v + 0] = z & 0x7FFF; + } + + zh = dh + r; + d[len] = zh & 0x7FFF; + dh = zh >> 15; + } + + /* + * Restore the bit length (it was overwritten in the loop above). + */ + d[0] = pgm_read_word(&m[0]); + + /* + * d[] may be greater than m[], but it is still lower than twice + * the modulus. + */ + br_i15_sub(d, m, NEQ(dh, 0) | NOT(br_i15_sub(d, m, 0))); +} diff --git a/lib/bearssl-esp8266/src/int/i15_mulacc.c b/lib/bearssl-esp8266/src/int/i15_mulacc.c new file mode 100644 index 000000000..ee4bb157d --- /dev/null +++ b/lib/bearssl-esp8266/src/int/i15_mulacc.c @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +void +br_i15_mulacc(uint16_t *d, const uint16_t *a, const uint16_t *b) +{ + size_t alen, blen, u; + unsigned dl, dh; + + alen = (pgm_read_word(&a[0]) + 15) >> 4; + blen = (pgm_read_word(&b[0]) + 15) >> 4; + + /* + * Announced bit length of d[] will be the sum of the announced + * bit lengths of a[] and b[]; but the lengths are encoded. + */ + dl = (pgm_read_word(&a[0]) & 15) + (pgm_read_word(&b[0]) & 15); + dh = (pgm_read_word(&a[0]) >> 4) + (pgm_read_word(&b[0]) >> 4); + d[0] = (dh << 4) + dl + (~(uint32_t)(dl - 15) >> 31); + + for (u = 0; u < blen; u ++) { + uint32_t f; + size_t v; + uint32_t cc; + + f = pgm_read_word(&b[1 + u]); + cc = 0; + for (v = 0; v < alen; v ++) { + uint32_t z; + + z = (uint32_t)d[1 + u + v] + MUL15(f, pgm_read_word(&a[1 + v])) + cc; + cc = z >> 15; + d[1 + u + v] = z & 0x7FFF; + } + d[1 + u + alen] = cc; + } +} diff --git a/lib/bearssl-esp8266/src/int/i15_muladd.c b/lib/bearssl-esp8266/src/int/i15_muladd.c new file mode 100644 index 000000000..d8d73b335 --- /dev/null +++ b/lib/bearssl-esp8266/src/int/i15_muladd.c @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * Constant-time division. The divisor must not be larger than 16 bits, + * and the quotient must fit on 17 bits. + */ +static uint32_t +divrem16(uint32_t x, uint32_t d, uint32_t *r) +{ + int i; + uint32_t q; + + q = 0; + d <<= 16; + for (i = 16; i >= 0; i --) { + uint32_t ctl; + + ctl = LE(d, x); + q |= ctl << i; + x -= (-ctl) & d; + d >>= 1; + } + if (r != NULL) { + *r = x; + } + return q; +} + +/* see inner.h */ +void +br_i15_muladd_small(uint16_t *x, uint16_t z, const uint16_t *m) +{ + /* + * Constant-time: we accept to leak the exact bit length of the + * modulus m. + */ + unsigned m_bitlen, mblr; + size_t u, mlen; + uint32_t hi, a0, a, b, q; + uint32_t cc, tb, over, under; + + /* + * Simple case: the modulus fits on one word. + */ + m_bitlen = pgm_read_word(&m[0]); + if (m_bitlen == 0) { + return; + } + if (m_bitlen <= 15) { + uint32_t rem; + + divrem16(((uint32_t)x[1] << 15) | z, pgm_read_word(&m[1]), &rem); + x[1] = rem; + return; + } + mlen = (m_bitlen + 15) >> 4; + mblr = m_bitlen & 15; + + /* + * Principle: we estimate the quotient (x*2^15+z)/m by + * doing a 30/15 division with the high words. + * + * Let: + * w = 2^15 + * a = (w*a0 + a1) * w^N + a2 + * b = b0 * w^N + b2 + * such that: + * 0 <= a0 < w + * 0 <= a1 < w + * 0 <= a2 < w^N + * w/2 <= b0 < w + * 0 <= b2 < w^N + * a < w*b + * I.e. the two top words of a are a0:a1, the top word of b is + * b0, we ensured that b0 is "full" (high bit set), and a is + * such that the quotient q = a/b fits on one word (0 <= q < w). + * + * If a = b*q + r (with 0 <= r < q), then we can estimate q by + * using a division on the top words: + * a0*w + a1 = b0*u + v (with 0 <= v < b0) + * Then the following holds: + * 0 <= u <= w + * u-2 <= q <= u + */ + hi = x[mlen]; + if (mblr == 0) { + a0 = x[mlen]; + memmove(x + 2, x + 1, (mlen - 1) * sizeof *x); + x[1] = z; + a = (a0 << 15) + x[mlen]; + b = pgm_read_word(&m[mlen]); + } else { + a0 = (x[mlen] << (15 - mblr)) | (x[mlen - 1] >> mblr); + memmove(x + 2, x + 1, (mlen - 1) * sizeof *x); + x[1] = z; + a = (a0 << 15) | (((x[mlen] << (15 - mblr)) + | (x[mlen - 1] >> mblr)) & 0x7FFF); + b = (pgm_read_word(&m[mlen]) << (15 - mblr)) | (pgm_read_word(&m[mlen - 1]) >> mblr); + } + q = divrem16(a, b, NULL); + + /* + * We computed an estimate for q, but the real one may be q, + * q-1 or q-2; moreover, the division may have returned a value + * 8000 or even 8001 if the two high words were identical, and + * we want to avoid values beyond 7FFF. We thus adjust q so + * that the "true" multiplier will be q+1, q or q-1, and q is + * in the 0000..7FFF range. + */ + q = MUX(EQ(b, a0), 0x7FFF, q - 1 + ((q - 1) >> 31)); + + /* + * We subtract q*m from x (x has an extra high word of value 'hi'). + * Since q may be off by 1 (in either direction), we may have to + * add or subtract m afterwards. + * + * The 'tb' flag will be true (1) at the end of the loop if the + * result is greater than or equal to the modulus (not counting + * 'hi' or the carry). + */ + cc = 0; + tb = 1; + for (u = 1; u <= mlen; u ++) { + uint32_t mw, zl, xw, nxw; + + mw = pgm_read_word(&m[u]); + zl = MUL15(mw, q) + cc; + cc = zl >> 15; + zl &= 0x7FFF; + xw = x[u]; + nxw = xw - zl; + cc += nxw >> 31; + nxw &= 0x7FFF; + x[u] = nxw; + tb = MUX(EQ(nxw, mw), tb, GT(nxw, mw)); + } + + /* + * If we underestimated q, then either cc < hi (one extra bit + * beyond the top array word), or cc == hi and tb is true (no + * extra bit, but the result is not lower than the modulus). + * + * If we overestimated q, then cc > hi. + */ + over = GT(cc, hi); + under = ~over & (tb | LT(cc, hi)); + br_i15_add(x, m, over); + br_i15_sub(x, m, under); +} diff --git a/lib/bearssl-esp8266/src/int/i15_ninv15.c b/lib/bearssl-esp8266/src/int/i15_ninv15.c new file mode 100644 index 000000000..08028e519 --- /dev/null +++ b/lib/bearssl-esp8266/src/int/i15_ninv15.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +uint16_t +br_i15_ninv15(uint16_t x) +{ + uint32_t y; + + y = 2 - x; + y = MUL15(y, 2 - MUL15(x, y)); + y = MUL15(y, 2 - MUL15(x, y)); + y = MUL15(y, 2 - MUL15(x, y)); + return MUX(x & 1, -y, 0) & 0x7FFF; +} diff --git a/lib/bearssl-esp8266/src/int/i15_reduce.c b/lib/bearssl-esp8266/src/int/i15_reduce.c new file mode 100644 index 000000000..714ac5bb0 --- /dev/null +++ b/lib/bearssl-esp8266/src/int/i15_reduce.c @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +void +br_i15_reduce(uint16_t *x, const uint16_t *a, const uint16_t *m) +{ + uint32_t m_bitlen, a_bitlen; + size_t mlen, alen, u; + + m_bitlen = pgm_read_word(&m[0]); + mlen = (m_bitlen + 15) >> 4; + + x[0] = m_bitlen; + if (m_bitlen == 0) { + return; + } + + /* + * If the source is shorter, then simply copy all words from a[] + * and zero out the upper words. + */ + a_bitlen = pgm_read_word(&a[0]); + alen = (a_bitlen + 15) >> 4; + if (a_bitlen < m_bitlen) { + memcpy_P(x + 1, a + 1, alen * sizeof *a); + for (u = alen; u < mlen; u ++) { + x[u + 1] = 0; + } + return; + } + + /* + * The source length is at least equal to that of the modulus. + * We must thus copy N-1 words, and input the remaining words + * one by one. + */ + memcpy_P(x + 1, a + 2 + (alen - mlen), (mlen - 1) * sizeof *a); + x[mlen] = 0; + for (u = 1 + alen - mlen; u > 0; u --) { + br_i15_muladd_small(x, pgm_read_word(&a[u]), m); + } +} diff --git a/lib/bearssl-esp8266/src/int/i15_rshift.c b/lib/bearssl-esp8266/src/int/i15_rshift.c new file mode 100644 index 000000000..08208918c --- /dev/null +++ b/lib/bearssl-esp8266/src/int/i15_rshift.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +void +br_i15_rshift(uint16_t *x, int count) +{ + size_t u, len; + unsigned r; + + len = (x[0] + 15) >> 4; + if (len == 0) { + return; + } + r = x[1] >> count; + for (u = 2; u <= len; u ++) { + unsigned w; + + w = x[u]; + x[u - 1] = ((w << (15 - count)) | r) & 0x7FFF; + r = w >> count; + } + x[len] = r; +} diff --git a/lib/bearssl-esp8266/src/int/i15_sub.c b/lib/bearssl-esp8266/src/int/i15_sub.c new file mode 100644 index 000000000..4f75bda49 --- /dev/null +++ b/lib/bearssl-esp8266/src/int/i15_sub.c @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +uint32_t +br_i15_sub(uint16_t *a, const uint16_t *b, uint32_t ctl) +{ + uint32_t cc; + size_t u, m; + + cc = 0; + m = (a[0] + 31) >> 4; + for (u = 1; u < m; u ++) { + uint32_t aw, bw, naw; + + aw = a[u]; + bw = pgm_read_word(&b[u]); + naw = aw - bw - cc; + cc = naw >> 31; + a[u] = MUX(ctl, naw & 0x7FFF, aw); + } + return cc; +} diff --git a/lib/bearssl-esp8266/src/int/i15_tmont.c b/lib/bearssl-esp8266/src/int/i15_tmont.c new file mode 100644 index 000000000..538ed64cb --- /dev/null +++ b/lib/bearssl-esp8266/src/int/i15_tmont.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +void +br_i15_to_monty(uint16_t *x, const uint16_t *m) +{ + unsigned k; + + for (k = (pgm_read_word(&m[0]) + 15) >> 4; k > 0; k --) { + br_i15_muladd_small(x, 0, m); + } +} diff --git a/lib/bearssl-esp8266/src/kdf/hkdf.c b/lib/bearssl-esp8266/src/kdf/hkdf.c new file mode 100644 index 000000000..3a1814fa8 --- /dev/null +++ b/lib/bearssl-esp8266/src/kdf/hkdf.c @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +const unsigned char br_hkdf_no_salt = 0; + +/* see bearssl_kdf.h */ +void +br_hkdf_init(br_hkdf_context *hc, const br_hash_class *digest_vtable, + const void *salt, size_t salt_len) +{ + br_hmac_key_context kc; + unsigned char tmp[64]; + + if (salt == BR_HKDF_NO_SALT) { + salt = tmp; + salt_len = br_digest_size(digest_vtable); + memset(tmp, 0, salt_len); + } + br_hmac_key_init(&kc, digest_vtable, salt, salt_len); + br_hmac_init(&hc->u.hmac_ctx, &kc, 0); + hc->dig_len = br_hmac_size(&hc->u.hmac_ctx); +} + +/* see bearssl_kdf.h */ +void +br_hkdf_inject(br_hkdf_context *hc, const void *ikm, size_t ikm_len) +{ + br_hmac_update(&hc->u.hmac_ctx, ikm, ikm_len); +} + +/* see bearssl_kdf.h */ +void +br_hkdf_flip(br_hkdf_context *hc) +{ + unsigned char tmp[64]; + + br_hmac_out(&hc->u.hmac_ctx, tmp); + br_hmac_key_init(&hc->u.prk_ctx, + br_hmac_get_digest(&hc->u.hmac_ctx), tmp, hc->dig_len); + hc->ptr = hc->dig_len; + hc->chunk_num = 0; +} + +/* see bearssl_kdf.h */ +size_t +br_hkdf_produce(br_hkdf_context *hc, + const void *info, size_t info_len, void *out, size_t out_len) +{ + size_t tlen; + + tlen = 0; + while (out_len > 0) { + size_t clen; + + if (hc->ptr == hc->dig_len) { + br_hmac_context hmac_ctx; + unsigned char x; + + hc->chunk_num ++; + if (hc->chunk_num == 256) { + return tlen; + } + x = hc->chunk_num; + br_hmac_init(&hmac_ctx, &hc->u.prk_ctx, 0); + if (x != 1) { + br_hmac_update(&hmac_ctx, hc->buf, hc->dig_len); + } + br_hmac_update(&hmac_ctx, info, info_len); + br_hmac_update(&hmac_ctx, &x, 1); + br_hmac_out(&hmac_ctx, hc->buf); + hc->ptr = 0; + } + clen = hc->dig_len - hc->ptr; + if (clen > out_len) { + clen = out_len; + } + memcpy(out, hc->buf + hc->ptr, clen); + out = (unsigned char *)out + clen; + out_len -= clen; + hc->ptr += clen; + tlen += clen; + } + return tlen; +} diff --git a/lib/bearssl-esp8266/src/kdf/shake.c b/lib/bearssl-esp8266/src/kdf/shake.c new file mode 100644 index 000000000..f9376c1c6 --- /dev/null +++ b/lib/bearssl-esp8266/src/kdf/shake.c @@ -0,0 +1,590 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * Round constants. + */ +static const uint64_t RC[] PROGMEM = { + 0x0000000000000001, 0x0000000000008082, + 0x800000000000808A, 0x8000000080008000, + 0x000000000000808B, 0x0000000080000001, + 0x8000000080008081, 0x8000000000008009, + 0x000000000000008A, 0x0000000000000088, + 0x0000000080008009, 0x000000008000000A, + 0x000000008000808B, 0x800000000000008B, + 0x8000000000008089, 0x8000000000008003, + 0x8000000000008002, 0x8000000000000080, + 0x000000000000800A, 0x800000008000000A, + 0x8000000080008081, 0x8000000000008080, + 0x0000000080000001, 0x8000000080008008 +}; + +/* + * XOR a block of data into the provided state. This supports only + * blocks whose length is a multiple of 64 bits. + */ +static void +xor_block(uint64_t *A, const void *data, size_t rate) +{ + size_t u; + + for (u = 0; u < rate; u += 8) { + A[u >> 3] ^= br_dec64le((const unsigned char *)data + u); + } +} + +/* + * Process a block with the provided data. The data length must be a + * multiple of 8 (in bytes); normally, this is the "rate". + */ +static void +process_block(uint64_t *A) +{ + uint64_t t0, t1, t2, t3, t4; + uint64_t tt0, tt1, tt2, tt3; + uint64_t t, kt; + uint64_t c0, c1, c2, c3, c4, bnn; + int j; + + /* + * Compute the 24 rounds. This loop is partially unrolled (each + * iteration computes two rounds). + */ + for (j = 0; j < 24; j += 2) { + + tt0 = A[ 1] ^ A[ 6]; + tt1 = A[11] ^ A[16]; + tt0 ^= A[21] ^ tt1; + tt0 = (tt0 << 1) | (tt0 >> 63); + tt2 = A[ 4] ^ A[ 9]; + tt3 = A[14] ^ A[19]; + tt0 ^= A[24]; + tt2 ^= tt3; + t0 = tt0 ^ tt2; + + tt0 = A[ 2] ^ A[ 7]; + tt1 = A[12] ^ A[17]; + tt0 ^= A[22] ^ tt1; + tt0 = (tt0 << 1) | (tt0 >> 63); + tt2 = A[ 0] ^ A[ 5]; + tt3 = A[10] ^ A[15]; + tt0 ^= A[20]; + tt2 ^= tt3; + t1 = tt0 ^ tt2; + + tt0 = A[ 3] ^ A[ 8]; + tt1 = A[13] ^ A[18]; + tt0 ^= A[23] ^ tt1; + tt0 = (tt0 << 1) | (tt0 >> 63); + tt2 = A[ 1] ^ A[ 6]; + tt3 = A[11] ^ A[16]; + tt0 ^= A[21]; + tt2 ^= tt3; + t2 = tt0 ^ tt2; + + tt0 = A[ 4] ^ A[ 9]; + tt1 = A[14] ^ A[19]; + tt0 ^= A[24] ^ tt1; + tt0 = (tt0 << 1) | (tt0 >> 63); + tt2 = A[ 2] ^ A[ 7]; + tt3 = A[12] ^ A[17]; + tt0 ^= A[22]; + tt2 ^= tt3; + t3 = tt0 ^ tt2; + + tt0 = A[ 0] ^ A[ 5]; + tt1 = A[10] ^ A[15]; + tt0 ^= A[20] ^ tt1; + tt0 = (tt0 << 1) | (tt0 >> 63); + tt2 = A[ 3] ^ A[ 8]; + tt3 = A[13] ^ A[18]; + tt0 ^= A[23]; + tt2 ^= tt3; + t4 = tt0 ^ tt2; + + A[ 0] = A[ 0] ^ t0; + A[ 5] = A[ 5] ^ t0; + A[10] = A[10] ^ t0; + A[15] = A[15] ^ t0; + A[20] = A[20] ^ t0; + A[ 1] = A[ 1] ^ t1; + A[ 6] = A[ 6] ^ t1; + A[11] = A[11] ^ t1; + A[16] = A[16] ^ t1; + A[21] = A[21] ^ t1; + A[ 2] = A[ 2] ^ t2; + A[ 7] = A[ 7] ^ t2; + A[12] = A[12] ^ t2; + A[17] = A[17] ^ t2; + A[22] = A[22] ^ t2; + A[ 3] = A[ 3] ^ t3; + A[ 8] = A[ 8] ^ t3; + A[13] = A[13] ^ t3; + A[18] = A[18] ^ t3; + A[23] = A[23] ^ t3; + A[ 4] = A[ 4] ^ t4; + A[ 9] = A[ 9] ^ t4; + A[14] = A[14] ^ t4; + A[19] = A[19] ^ t4; + A[24] = A[24] ^ t4; + A[ 5] = (A[ 5] << 36) | (A[ 5] >> (64 - 36)); + A[10] = (A[10] << 3) | (A[10] >> (64 - 3)); + A[15] = (A[15] << 41) | (A[15] >> (64 - 41)); + A[20] = (A[20] << 18) | (A[20] >> (64 - 18)); + A[ 1] = (A[ 1] << 1) | (A[ 1] >> (64 - 1)); + A[ 6] = (A[ 6] << 44) | (A[ 6] >> (64 - 44)); + A[11] = (A[11] << 10) | (A[11] >> (64 - 10)); + A[16] = (A[16] << 45) | (A[16] >> (64 - 45)); + A[21] = (A[21] << 2) | (A[21] >> (64 - 2)); + A[ 2] = (A[ 2] << 62) | (A[ 2] >> (64 - 62)); + A[ 7] = (A[ 7] << 6) | (A[ 7] >> (64 - 6)); + A[12] = (A[12] << 43) | (A[12] >> (64 - 43)); + A[17] = (A[17] << 15) | (A[17] >> (64 - 15)); + A[22] = (A[22] << 61) | (A[22] >> (64 - 61)); + A[ 3] = (A[ 3] << 28) | (A[ 3] >> (64 - 28)); + A[ 8] = (A[ 8] << 55) | (A[ 8] >> (64 - 55)); + A[13] = (A[13] << 25) | (A[13] >> (64 - 25)); + A[18] = (A[18] << 21) | (A[18] >> (64 - 21)); + A[23] = (A[23] << 56) | (A[23] >> (64 - 56)); + A[ 4] = (A[ 4] << 27) | (A[ 4] >> (64 - 27)); + A[ 9] = (A[ 9] << 20) | (A[ 9] >> (64 - 20)); + A[14] = (A[14] << 39) | (A[14] >> (64 - 39)); + A[19] = (A[19] << 8) | (A[19] >> (64 - 8)); + A[24] = (A[24] << 14) | (A[24] >> (64 - 14)); + bnn = ~A[12]; + kt = A[ 6] | A[12]; + c0 = A[ 0] ^ kt; + kt = bnn | A[18]; + c1 = A[ 6] ^ kt; + kt = A[18] & A[24]; + c2 = A[12] ^ kt; + kt = A[24] | A[ 0]; + c3 = A[18] ^ kt; + kt = A[ 0] & A[ 6]; + c4 = A[24] ^ kt; + A[ 0] = c0; + A[ 6] = c1; + A[12] = c2; + A[18] = c3; + A[24] = c4; + bnn = ~A[22]; + kt = A[ 9] | A[10]; + c0 = A[ 3] ^ kt; + kt = A[10] & A[16]; + c1 = A[ 9] ^ kt; + kt = A[16] | bnn; + c2 = A[10] ^ kt; + kt = A[22] | A[ 3]; + c3 = A[16] ^ kt; + kt = A[ 3] & A[ 9]; + c4 = A[22] ^ kt; + A[ 3] = c0; + A[ 9] = c1; + A[10] = c2; + A[16] = c3; + A[22] = c4; + bnn = ~A[19]; + kt = A[ 7] | A[13]; + c0 = A[ 1] ^ kt; + kt = A[13] & A[19]; + c1 = A[ 7] ^ kt; + kt = bnn & A[20]; + c2 = A[13] ^ kt; + kt = A[20] | A[ 1]; + c3 = bnn ^ kt; + kt = A[ 1] & A[ 7]; + c4 = A[20] ^ kt; + A[ 1] = c0; + A[ 7] = c1; + A[13] = c2; + A[19] = c3; + A[20] = c4; + bnn = ~A[17]; + kt = A[ 5] & A[11]; + c0 = A[ 4] ^ kt; + kt = A[11] | A[17]; + c1 = A[ 5] ^ kt; + kt = bnn | A[23]; + c2 = A[11] ^ kt; + kt = A[23] & A[ 4]; + c3 = bnn ^ kt; + kt = A[ 4] | A[ 5]; + c4 = A[23] ^ kt; + A[ 4] = c0; + A[ 5] = c1; + A[11] = c2; + A[17] = c3; + A[23] = c4; + bnn = ~A[ 8]; + kt = bnn & A[14]; + c0 = A[ 2] ^ kt; + kt = A[14] | A[15]; + c1 = bnn ^ kt; + kt = A[15] & A[21]; + c2 = A[14] ^ kt; + kt = A[21] | A[ 2]; + c3 = A[15] ^ kt; + kt = A[ 2] & A[ 8]; + c4 = A[21] ^ kt; + A[ 2] = c0; + A[ 8] = c1; + A[14] = c2; + A[15] = c3; + A[21] = c4; + A[ 0] = A[ 0] ^ RC[j + 0]; + + tt0 = A[ 6] ^ A[ 9]; + tt1 = A[ 7] ^ A[ 5]; + tt0 ^= A[ 8] ^ tt1; + tt0 = (tt0 << 1) | (tt0 >> 63); + tt2 = A[24] ^ A[22]; + tt3 = A[20] ^ A[23]; + tt0 ^= A[21]; + tt2 ^= tt3; + t0 = tt0 ^ tt2; + + tt0 = A[12] ^ A[10]; + tt1 = A[13] ^ A[11]; + tt0 ^= A[14] ^ tt1; + tt0 = (tt0 << 1) | (tt0 >> 63); + tt2 = A[ 0] ^ A[ 3]; + tt3 = A[ 1] ^ A[ 4]; + tt0 ^= A[ 2]; + tt2 ^= tt3; + t1 = tt0 ^ tt2; + + tt0 = A[18] ^ A[16]; + tt1 = A[19] ^ A[17]; + tt0 ^= A[15] ^ tt1; + tt0 = (tt0 << 1) | (tt0 >> 63); + tt2 = A[ 6] ^ A[ 9]; + tt3 = A[ 7] ^ A[ 5]; + tt0 ^= A[ 8]; + tt2 ^= tt3; + t2 = tt0 ^ tt2; + + tt0 = A[24] ^ A[22]; + tt1 = A[20] ^ A[23]; + tt0 ^= A[21] ^ tt1; + tt0 = (tt0 << 1) | (tt0 >> 63); + tt2 = A[12] ^ A[10]; + tt3 = A[13] ^ A[11]; + tt0 ^= A[14]; + tt2 ^= tt3; + t3 = tt0 ^ tt2; + + tt0 = A[ 0] ^ A[ 3]; + tt1 = A[ 1] ^ A[ 4]; + tt0 ^= A[ 2] ^ tt1; + tt0 = (tt0 << 1) | (tt0 >> 63); + tt2 = A[18] ^ A[16]; + tt3 = A[19] ^ A[17]; + tt0 ^= A[15]; + tt2 ^= tt3; + t4 = tt0 ^ tt2; + + A[ 0] = A[ 0] ^ t0; + A[ 3] = A[ 3] ^ t0; + A[ 1] = A[ 1] ^ t0; + A[ 4] = A[ 4] ^ t0; + A[ 2] = A[ 2] ^ t0; + A[ 6] = A[ 6] ^ t1; + A[ 9] = A[ 9] ^ t1; + A[ 7] = A[ 7] ^ t1; + A[ 5] = A[ 5] ^ t1; + A[ 8] = A[ 8] ^ t1; + A[12] = A[12] ^ t2; + A[10] = A[10] ^ t2; + A[13] = A[13] ^ t2; + A[11] = A[11] ^ t2; + A[14] = A[14] ^ t2; + A[18] = A[18] ^ t3; + A[16] = A[16] ^ t3; + A[19] = A[19] ^ t3; + A[17] = A[17] ^ t3; + A[15] = A[15] ^ t3; + A[24] = A[24] ^ t4; + A[22] = A[22] ^ t4; + A[20] = A[20] ^ t4; + A[23] = A[23] ^ t4; + A[21] = A[21] ^ t4; + A[ 3] = (A[ 3] << 36) | (A[ 3] >> (64 - 36)); + A[ 1] = (A[ 1] << 3) | (A[ 1] >> (64 - 3)); + A[ 4] = (A[ 4] << 41) | (A[ 4] >> (64 - 41)); + A[ 2] = (A[ 2] << 18) | (A[ 2] >> (64 - 18)); + A[ 6] = (A[ 6] << 1) | (A[ 6] >> (64 - 1)); + A[ 9] = (A[ 9] << 44) | (A[ 9] >> (64 - 44)); + A[ 7] = (A[ 7] << 10) | (A[ 7] >> (64 - 10)); + A[ 5] = (A[ 5] << 45) | (A[ 5] >> (64 - 45)); + A[ 8] = (A[ 8] << 2) | (A[ 8] >> (64 - 2)); + A[12] = (A[12] << 62) | (A[12] >> (64 - 62)); + A[10] = (A[10] << 6) | (A[10] >> (64 - 6)); + A[13] = (A[13] << 43) | (A[13] >> (64 - 43)); + A[11] = (A[11] << 15) | (A[11] >> (64 - 15)); + A[14] = (A[14] << 61) | (A[14] >> (64 - 61)); + A[18] = (A[18] << 28) | (A[18] >> (64 - 28)); + A[16] = (A[16] << 55) | (A[16] >> (64 - 55)); + A[19] = (A[19] << 25) | (A[19] >> (64 - 25)); + A[17] = (A[17] << 21) | (A[17] >> (64 - 21)); + A[15] = (A[15] << 56) | (A[15] >> (64 - 56)); + A[24] = (A[24] << 27) | (A[24] >> (64 - 27)); + A[22] = (A[22] << 20) | (A[22] >> (64 - 20)); + A[20] = (A[20] << 39) | (A[20] >> (64 - 39)); + A[23] = (A[23] << 8) | (A[23] >> (64 - 8)); + A[21] = (A[21] << 14) | (A[21] >> (64 - 14)); + bnn = ~A[13]; + kt = A[ 9] | A[13]; + c0 = A[ 0] ^ kt; + kt = bnn | A[17]; + c1 = A[ 9] ^ kt; + kt = A[17] & A[21]; + c2 = A[13] ^ kt; + kt = A[21] | A[ 0]; + c3 = A[17] ^ kt; + kt = A[ 0] & A[ 9]; + c4 = A[21] ^ kt; + A[ 0] = c0; + A[ 9] = c1; + A[13] = c2; + A[17] = c3; + A[21] = c4; + bnn = ~A[14]; + kt = A[22] | A[ 1]; + c0 = A[18] ^ kt; + kt = A[ 1] & A[ 5]; + c1 = A[22] ^ kt; + kt = A[ 5] | bnn; + c2 = A[ 1] ^ kt; + kt = A[14] | A[18]; + c3 = A[ 5] ^ kt; + kt = A[18] & A[22]; + c4 = A[14] ^ kt; + A[18] = c0; + A[22] = c1; + A[ 1] = c2; + A[ 5] = c3; + A[14] = c4; + bnn = ~A[23]; + kt = A[10] | A[19]; + c0 = A[ 6] ^ kt; + kt = A[19] & A[23]; + c1 = A[10] ^ kt; + kt = bnn & A[ 2]; + c2 = A[19] ^ kt; + kt = A[ 2] | A[ 6]; + c3 = bnn ^ kt; + kt = A[ 6] & A[10]; + c4 = A[ 2] ^ kt; + A[ 6] = c0; + A[10] = c1; + A[19] = c2; + A[23] = c3; + A[ 2] = c4; + bnn = ~A[11]; + kt = A[ 3] & A[ 7]; + c0 = A[24] ^ kt; + kt = A[ 7] | A[11]; + c1 = A[ 3] ^ kt; + kt = bnn | A[15]; + c2 = A[ 7] ^ kt; + kt = A[15] & A[24]; + c3 = bnn ^ kt; + kt = A[24] | A[ 3]; + c4 = A[15] ^ kt; + A[24] = c0; + A[ 3] = c1; + A[ 7] = c2; + A[11] = c3; + A[15] = c4; + bnn = ~A[16]; + kt = bnn & A[20]; + c0 = A[12] ^ kt; + kt = A[20] | A[ 4]; + c1 = bnn ^ kt; + kt = A[ 4] & A[ 8]; + c2 = A[20] ^ kt; + kt = A[ 8] | A[12]; + c3 = A[ 4] ^ kt; + kt = A[12] & A[16]; + c4 = A[ 8] ^ kt; + A[12] = c0; + A[16] = c1; + A[20] = c2; + A[ 4] = c3; + A[ 8] = c4; + A[ 0] = A[ 0] ^ RC[j + 1]; + t = A[ 5]; + A[ 5] = A[18]; + A[18] = A[11]; + A[11] = A[10]; + A[10] = A[ 6]; + A[ 6] = A[22]; + A[22] = A[20]; + A[20] = A[12]; + A[12] = A[19]; + A[19] = A[15]; + A[15] = A[24]; + A[24] = A[ 8]; + A[ 8] = t; + t = A[ 1]; + A[ 1] = A[ 9]; + A[ 9] = A[14]; + A[14] = A[ 2]; + A[ 2] = A[13]; + A[13] = A[23]; + A[23] = A[ 4]; + A[ 4] = A[21]; + A[21] = A[16]; + A[16] = A[ 3]; + A[ 3] = A[17]; + A[17] = A[ 7]; + A[ 7] = t; + } +} + +/* see bearssl_kdf.h */ +void +br_shake_init(br_shake_context *sc, int security_level) +{ + sc->rate = 200 - (size_t)(security_level >> 2); + sc->dptr = 0; + memset(sc->A, 0, sizeof sc->A); + sc->A[ 1] = ~(uint64_t)0; + sc->A[ 2] = ~(uint64_t)0; + sc->A[ 8] = ~(uint64_t)0; + sc->A[12] = ~(uint64_t)0; + sc->A[17] = ~(uint64_t)0; + sc->A[20] = ~(uint64_t)0; +} + +/* see bearssl_kdf.h */ +void +br_shake_inject(br_shake_context *sc, const void *data, size_t len) +{ + const unsigned char *buf; + size_t rate, dptr; + + buf = data; + rate = sc->rate; + dptr = sc->dptr; + while (len > 0) { + size_t clen; + + clen = rate - dptr; + if (clen > len) { + clen = len; + } + memcpy(sc->dbuf + dptr, buf, clen); + dptr += clen; + buf += clen; + len -= clen; + if (dptr == rate) { + xor_block(sc->A, sc->dbuf, rate); + process_block(sc->A); + dptr = 0; + } + } + sc->dptr = dptr; +} + +/* see bearssl_kdf.h */ +void +br_shake_flip(br_shake_context *sc) +{ + /* + * We apply padding and pre-XOR the value into the state. We + * set dptr to the end of the buffer, so that first call to + * shake_extract() will process the block. + */ + if ((sc->dptr + 1) == sc->rate) { + sc->dbuf[sc->dptr ++] = 0x9F; + } else { + sc->dbuf[sc->dptr ++] = 0x1F; + memset(sc->dbuf + sc->dptr, 0x00, sc->rate - sc->dptr - 1); + sc->dbuf[sc->rate - 1] = 0x80; + sc->dptr = sc->rate; + } + xor_block(sc->A, sc->dbuf, sc->rate); +} + +/* see bearssl_kdf.h */ +void +br_shake_produce(br_shake_context *sc, void *out, size_t len) +{ + unsigned char *buf; + size_t dptr, rate; + + buf = out; + dptr = sc->dptr; + rate = sc->rate; + while (len > 0) { + size_t clen; + + if (dptr == rate) { + unsigned char *dbuf; + uint64_t *A; + + A = sc->A; + dbuf = sc->dbuf; + process_block(A); + br_enc64le(dbuf + 0, A[ 0]); + br_enc64le(dbuf + 8, ~A[ 1]); + br_enc64le(dbuf + 16, ~A[ 2]); + br_enc64le(dbuf + 24, A[ 3]); + br_enc64le(dbuf + 32, A[ 4]); + br_enc64le(dbuf + 40, A[ 5]); + br_enc64le(dbuf + 48, A[ 6]); + br_enc64le(dbuf + 56, A[ 7]); + br_enc64le(dbuf + 64, ~A[ 8]); + br_enc64le(dbuf + 72, A[ 9]); + br_enc64le(dbuf + 80, A[10]); + br_enc64le(dbuf + 88, A[11]); + br_enc64le(dbuf + 96, ~A[12]); + br_enc64le(dbuf + 104, A[13]); + br_enc64le(dbuf + 112, A[14]); + br_enc64le(dbuf + 120, A[15]); + br_enc64le(dbuf + 128, A[16]); + br_enc64le(dbuf + 136, ~A[17]); + br_enc64le(dbuf + 144, A[18]); + br_enc64le(dbuf + 152, A[19]); + br_enc64le(dbuf + 160, ~A[20]); + br_enc64le(dbuf + 168, A[21]); + br_enc64le(dbuf + 176, A[22]); + br_enc64le(dbuf + 184, A[23]); + br_enc64le(dbuf + 192, A[24]); + dptr = 0; + } + clen = rate - dptr; + if (clen > len) { + clen = len; + } + memcpy(buf, sc->dbuf + dptr, clen); + dptr += clen; + buf += clen; + len -= clen; + } + sc->dptr = dptr; +} diff --git a/lib/bearssl-esp8266/src/mac/hmac.c b/lib/bearssl-esp8266/src/mac/hmac.c new file mode 100644 index 000000000..ea00a5327 --- /dev/null +++ b/lib/bearssl-esp8266/src/mac/hmac.c @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +static inline size_t +block_size(const br_hash_class *dig) +{ + unsigned ls; + + ls = (unsigned)(dig->desc >> BR_HASHDESC_LBLEN_OFF) + & BR_HASHDESC_LBLEN_MASK; + return (size_t)1 << ls; +} + +static void +process_key(const br_hash_class **hc, void *ks, + const void *key, size_t key_len, unsigned bb) +{ + unsigned char tmp[256]; + size_t blen, u; + + blen = block_size(*hc); + memcpy(tmp, key, key_len); + for (u = 0; u < key_len; u ++) { + tmp[u] ^= (unsigned char)bb; + } + memset(tmp + key_len, bb, blen - key_len); + (*hc)->init(hc); + (*hc)->update(hc, tmp, blen); + (*hc)->state(hc, ks); +} + +/* see bearssl.h */ +void +br_hmac_key_init(br_hmac_key_context *kc, + const br_hash_class *dig, const void *key, size_t key_len) +{ + br_hash_compat_context hc; + unsigned char kbuf[64]; + + kc->dig_vtable = dig; + hc.vtable = dig; + if (key_len > block_size(dig)) { + dig->init(&hc.vtable); + dig->update(&hc.vtable, key, key_len); + dig->out(&hc.vtable, kbuf); + key = kbuf; + key_len = br_digest_size(dig); + } + process_key(&hc.vtable, kc->ksi, key, key_len, 0x36); + process_key(&hc.vtable, kc->kso, key, key_len, 0x5C); +} + +/* see bearssl.h */ +void +br_hmac_init(br_hmac_context *ctx, + const br_hmac_key_context *kc, size_t out_len) +{ + const br_hash_class *dig; + size_t blen, hlen; + + dig = kc->dig_vtable; + blen = block_size(dig); + dig->init(&ctx->dig.vtable); + dig->set_state(&ctx->dig.vtable, kc->ksi, (uint64_t)blen); + memcpy(ctx->kso, kc->kso, sizeof kc->kso); + hlen = br_digest_size(dig); + if (out_len > 0 && out_len < hlen) { + hlen = out_len; + } + ctx->out_len = hlen; +} + +/* see bearssl.h */ +void +br_hmac_update(br_hmac_context *ctx, const void *data, size_t len) +{ + ctx->dig.vtable->update(&ctx->dig.vtable, data, len); +} + +/* see bearssl.h */ +size_t +br_hmac_out(const br_hmac_context *ctx, void *out) +{ + const br_hash_class *dig; + br_hash_compat_context hc; + unsigned char tmp[64]; + size_t blen, hlen; + + dig = ctx->dig.vtable; + dig->out(&ctx->dig.vtable, tmp); + blen = block_size(dig); + dig->init(&hc.vtable); + dig->set_state(&hc.vtable, ctx->kso, (uint64_t)blen); + hlen = br_digest_size(dig); + dig->update(&hc.vtable, tmp, hlen); + dig->out(&hc.vtable, tmp); + memcpy(out, tmp, ctx->out_len); + return ctx->out_len; +} diff --git a/lib/bearssl-esp8266/src/mac/hmac_ct.c b/lib/bearssl-esp8266/src/mac/hmac_ct.c new file mode 100644 index 000000000..576136a13 --- /dev/null +++ b/lib/bearssl-esp8266/src/mac/hmac_ct.c @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +static inline size_t +hash_size(const br_hash_class *dig) +{ + return (unsigned)(dig->desc >> BR_HASHDESC_OUT_OFF) + & BR_HASHDESC_OUT_MASK; +} + +static inline size_t +block_size(const br_hash_class *dig) +{ + unsigned ls; + + ls = (unsigned)(dig->desc >> BR_HASHDESC_LBLEN_OFF) + & BR_HASHDESC_LBLEN_MASK; + return (size_t)1 << ls; +} + +/* see bearssl.h */ +size_t +br_hmac_outCT(const br_hmac_context *ctx, + const void *data, size_t len, size_t min_len, size_t max_len, + void *out) +{ + /* + * Method implemented here is inspired from the descriptions on: + * https://www.imperialviolet.org/2013/02/04/luckythirteen.html + * + * Principle: we input bytes one by one. We use a MUX to push + * padding bytes instead of data bytes when appropriate. At each + * block limit, we get the current hash function state: this is + * a potential output, since we handle MD padding ourselves. + * + * be 1 for big-endian, 0 for little-endian + * po minimal MD padding length + * bs block size (always a power of 2) + * hlen hash output size + */ + + const br_hash_class *dig; + br_hash_compat_context hc; + int be; + uint32_t po, bs; + uint32_t kr, km, kl, kz, u; + uint64_t count, ncount, bit_len; + unsigned char tmp1[64], tmp2[64]; + size_t hlen; + + /* + * Copy the current hash context. + */ + hc = ctx->dig; + + /* + * Get function-specific information. + */ + dig = hc.vtable; + be = (dig->desc & BR_HASHDESC_MD_PADDING_BE) != 0; + po = 9; + if (dig->desc & BR_HASHDESC_MD_PADDING_128) { + po += 8; + } + bs = block_size(dig); + hlen = hash_size(dig); + + /* + * Get current input length and compute total bit length. + */ + count = dig->state(&hc.vtable, tmp1); + bit_len = (count + (uint64_t)len) << 3; + + /* + * We can input the blocks that we are sure we will use. + * This offers better performance (no MUX for these blocks) + * and also ensures that the remaining lengths fit on 32 bits. + */ + ncount = (count + (uint64_t)min_len) & ~(uint64_t)(bs - 1); + if (ncount > count) { + size_t zlen; + + zlen = (size_t)(ncount - count); + dig->update(&hc.vtable, data, zlen); + data = (const unsigned char *)data + zlen; + len -= zlen; + max_len -= zlen; + count = ncount; + } + + /* + * At that point: + * -- 'count' contains the number of bytes already processed + * (in total). + * -- We must input 'len' bytes. 'min_len' is unimportant: we + * used it to know how many full blocks we could process + * directly. Now only len and max_len matter. + * + * We compute kr, kl, kz and km. + * kr number of input bytes already in the current block + * km index of the first byte after the end of the last padding + * block, if length is max_len + * kz index of the last byte of the actual last padding block + * kl index of the start of the encoded length + * + * km, kz and kl are counted from the current offset in the + * input data. + */ + kr = (uint32_t)count & (bs - 1); + kz = ((kr + (uint32_t)len + po + bs - 1) & ~(bs - 1)) - 1 - kr; + kl = kz - 7; + km = ((kr + (uint32_t)max_len + po + bs - 1) & ~(bs - 1)) - kr; + + /* + * We must now process km bytes. For index u from 0 to km-1: + * d is from data[] if u < max_len, 0x00 otherwise + * e is an encoded length byte or 0x00, depending on u + * The tests for d and e need not be constant-time, since + * they relate only to u and max_len, not to the actual length. + * + * Actual input length is then: + * d if u < len + * 0x80 if u == len + * 0x00 if u > len and u < kl + * e if u >= kl + * + * Hash state is obtained whenever we reach a full block. This + * is the result we want if and only if u == kz. + */ + memset(tmp2, 0, sizeof tmp2); + for (u = 0; u < km; u ++) { + uint32_t v; + uint32_t d, e, x0, x1; + unsigned char x[1]; + + d = (u < max_len) ? ((const unsigned char *)data)[u] : 0x00; + v = (kr + u) & (bs - 1); + if (v >= (bs - 8)) { + unsigned j; + + j = (v - (bs - 8)) << 3; + if (be) { + e = (uint32_t)(bit_len >> (56 - j)); + } else { + e = (uint32_t)(bit_len >> j); + } + e &= 0xFF; + } else { + e = 0x00; + } + x0 = MUX(EQ(u, (uint32_t)len), 0x80, d); + x1 = MUX(LT(u, kl), 0x00, e); + x[0] = MUX(LE(u, (uint32_t)len), x0, x1); + dig->update(&hc.vtable, x, 1); + if (v == (bs - 1)) { + dig->state(&hc.vtable, tmp1); + CCOPY(EQ(u, kz), tmp2, tmp1, hlen); + } + } + + /* + * Inner hash output is in tmp2[]; we finish processing. + */ + dig->init(&hc.vtable); + dig->set_state(&hc.vtable, ctx->kso, (uint64_t)bs); + dig->update(&hc.vtable, tmp2, hlen); + dig->out(&hc.vtable, tmp2); + memcpy(out, tmp2, ctx->out_len); + return ctx->out_len; +} diff --git a/lib/bearssl-esp8266/src/pgmspace_bearssl.h b/lib/bearssl-esp8266/src/pgmspace_bearssl.h new file mode 100644 index 000000000..5a42114f5 --- /dev/null +++ b/lib/bearssl-esp8266/src/pgmspace_bearssl.h @@ -0,0 +1,119 @@ +/* pgmspace_bearssl.h - Accessor utilities/types for accessing PROGMEM data */ + +//#pragma GCC optimize ("O2") // -Os is the default, forcing -O2 for BearSSL +//#define PGM_READ_UNALIGNED 0 // all calls are aligned, take the optimized version +//#include + + +#ifndef _PGMSPACE_BEARSSL_H_ +#define _PGMSPACE_BEARSSL_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef ICACHE_RODATA_ATTR + #define ICACHE_RODATA_ATTR __attribute__((section(".irom.text"))) +#endif +#ifndef PROGMEM + // The following two macros cause a parameter to be enclosed in quotes + // by the preopressor (i.e. for concatenating ints to strings) + #define __STRINGIZE_NX(A) #A + #define __STRINGIZE(A) __STRINGIZE_NX(A) + // Since __section__ is supposed to be only use for global variables, + // there could be conflicts when a static/inlined function has them in the + // same file as a non-static PROGMEM object. + // Ref: https://gcc.gnu.org/onlinedocs/gcc-3.2/gcc/Variable-Attributes.html + // Place each progmem object into its own named section, avoiding conflicts + #define PROGMEM __attribute__((section( "\".irom.text." __FILE__ "." __STRINGIZE(__LINE__) "." __STRINGIZE(__COUNTER__) "\""))) +#endif +#ifndef PGM_P + #define PGM_P const char * +#endif +#ifndef PGM_VOID_P + #define PGM_VOID_P const void * +#endif + +// PSTR() macro modified to start on a 32-bit boundary. This adds on average +// 1.5 bytes/string, but in return memcpy_P and strcpy_P will work 4~8x faster +#ifndef PSTR + #define PSTR(s) (__extension__({static const char __c[] __attribute__((__aligned__(4))) PROGMEM = (s); &__c[0];})) +#endif + +// Flash memory must be read using 32 bit aligned addresses else a processor +// exception will be triggered. +// The order within the 32 bit values are: +// -------------- +// b3, b2, b1, b0 +// w1, w0 + +#define pgm_read_with_offset(addr, res) \ + asm("extui %0, %1, 0, 2\n" /* Extract offset within word (in bytes) */ \ + "sub %1, %1, %0\n" /* Subtract offset from addr, yielding an aligned address */ \ + "l32i.n %1, %1, 0x0\n" /* Load word from aligned address */ \ + "slli %0, %0, 3\n" /* Mulitiply offset by 8, yielding an offset in bits */ \ + "ssr %0\n" /* Prepare to shift by offset (in bits) */ \ + "srl %0, %1\n" /* Shift right; now the requested byte is the first one */ \ + :"=r"(res), "=r"(addr) \ + :"1"(addr) \ + :); + +static inline uint8_t pgm_read_byte_inlined(const void* addr) { + register uint32_t res; + pgm_read_with_offset(addr, res); + return (uint8_t) res; /* This masks the lower byte from the returned word */ +} + +/* Although this says "word", it's actually 16 bit, i.e. half word on Xtensa */ +static inline uint16_t pgm_read_word_inlined(const void* addr) { + register uint32_t res; + pgm_read_with_offset(addr, res); + return (uint16_t) res; /* This masks the lower half-word from the returned word */ +} + +#define pgm_read_byte(addr) pgm_read_byte_inlined(addr) +#define pgm_read_word(addr) pgm_read_word_inlined(addr) +#ifdef __cplusplus + #define pgm_read_dword(addr) (*reinterpret_cast(addr)) + #define pgm_read_float(addr) (*reinterpret_cast(addr)) + #define pgm_read_ptr(addr) (*reinterpret_cast(addr)) +#else + #define pgm_read_dword(addr) (*(const uint32_t*)(addr)) + #define pgm_read_float(addr) (*(const float*)(addr)) + #define pgm_read_ptr(addr) (*(const void* const*)(addr)) +#endif + +#define pgm_read_byte_near(addr) pgm_read_byte(addr) +#define pgm_read_word_near(addr) pgm_read_word(addr) +#define pgm_read_dword_near(addr) pgm_read_dword(addr) +#define pgm_read_float_near(addr) pgm_read_float(addr) +#define pgm_read_ptr_near(addr) pgm_read_ptr(addr) +#define pgm_read_byte_far(addr) pgm_read_byte(addr) +#define pgm_read_word_far(addr) pgm_read_word(addr) +#define pgm_read_dword_far(addr) pgm_read_dword(addr) +#define pgm_read_float_far(addr) pgm_read_float(addr) +#define pgm_read_ptr_far(addr) pgm_read_ptr(addr) + +#define _SFR_BYTE(n) (n) + +#ifdef __PROG_TYPES_COMPAT__ + +typedef void prog_void; +typedef char prog_char; +typedef unsigned char prog_uchar; +typedef int8_t prog_int8_t; +typedef uint8_t prog_uint8_t; +typedef int16_t prog_int16_t; +typedef uint16_t prog_uint16_t; +typedef int32_t prog_int32_t; +typedef uint32_t prog_uint32_t; + +#endif // defined(__PROG_TYPES_COMPAT__) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/bearssl-esp8266/src/rand/aesctr_drbg.c b/lib/bearssl-esp8266/src/rand/aesctr_drbg.c new file mode 100644 index 000000000..b3e4ff9db --- /dev/null +++ b/lib/bearssl-esp8266/src/rand/aesctr_drbg.c @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_rand.h */ +void +br_aesctr_drbg_init(br_aesctr_drbg_context *ctx, + const br_block_ctr_class *aesctr, + const void *seed, size_t len) +{ + unsigned char tmp[16]; + + ctx->vtable = &br_aesctr_drbg_vtable; + memset(tmp, 0, sizeof tmp); + aesctr->init(&ctx->sk.vtable, tmp, 16); + ctx->cc = 0; + br_aesctr_drbg_update(ctx, seed, len); +} + +/* see bearssl_rand.h */ +void +br_aesctr_drbg_generate(br_aesctr_drbg_context *ctx, void *out, size_t len) +{ + unsigned char *buf; + unsigned char iv[12]; + + buf = out; + memset(iv, 0, sizeof iv); + while (len > 0) { + size_t clen; + + /* + * We generate data by blocks of at most 65280 bytes. This + * allows for unambiguously testing the counter overflow + * condition; also, it should work on 16-bit architectures + * (where 'size_t' is 16 bits only). + */ + clen = len; + if (clen > 65280) { + clen = 65280; + } + + /* + * We make sure that the counter won't exceed the configured + * limit. + */ + if ((uint32_t)(ctx->cc + ((clen + 15) >> 4)) > 32768) { + clen = (32768 - ctx->cc) << 4; + if (clen > len) { + clen = len; + } + } + + /* + * Run CTR. + */ + memset(buf, 0, clen); + ctx->cc = ctx->sk.vtable->run(&ctx->sk.vtable, + iv, ctx->cc, buf, clen); + buf += clen; + len -= clen; + + /* + * Every 32768 blocks, we force a state update. + */ + if (ctx->cc >= 32768) { + br_aesctr_drbg_update(ctx, NULL, 0); + } + } +} + +/* see bearssl_rand.h */ +void +br_aesctr_drbg_update(br_aesctr_drbg_context *ctx, const void *seed, size_t len) +{ + /* + * We use a Hirose construction on AES-256 to make a hash function. + * Function definition: + * - running state consists in two 16-byte blocks G and H + * - initial values of G and H are conventional + * - there is a fixed block-sized constant C + * - for next data block m: + * set AES key to H||m + * G' = E(G) xor G + * H' = E(G xor C) xor G xor C + * G <- G', H <- H' + * - once all blocks have been processed, output is H||G + * + * Constants: + * G_init = B6 B6 ... B6 + * H_init = A5 A5 ... A5 + * C = 01 00 ... 00 + * + * With this hash function h(), we compute the new state as + * follows: + * - produce a state-dependent value s as encryption of an + * all-one block with AES and the current key + * - compute the new key as the first 128 bits of h(s||seed) + * + * Original Hirose article: + * https://www.iacr.org/archive/fse2006/40470213/40470213.pdf + */ + + unsigned char s[16], iv[12]; + unsigned char G[16], H[16]; + int first; + + /* + * Use an all-one IV to get a fresh output block that depends on the + * current seed. + */ + memset(iv, 0xFF, sizeof iv); + memset(s, 0, 16); + ctx->sk.vtable->run(&ctx->sk.vtable, iv, 0xFFFFFFFF, s, 16); + + /* + * Set G[] and H[] to conventional start values. + */ + memset(G, 0xB6, sizeof G); + memset(H, 0x5A, sizeof H); + + /* + * Process the concatenation of the current state and the seed + * with the custom hash function. + */ + first = 1; + for (;;) { + unsigned char tmp[32]; + unsigned char newG[16]; + + /* + * Assemble new key H||m into tmp[]. + */ + memcpy(tmp, H, 16); + if (first) { + memcpy(tmp + 16, s, 16); + first = 0; + } else { + size_t clen; + + if (len == 0) { + break; + } + clen = len < 16 ? len : 16; + memcpy(tmp + 16, seed, clen); + memset(tmp + 16 + clen, 0, 16 - clen); + seed = (const unsigned char *)seed + clen; + len -= clen; + } + ctx->sk.vtable->init(&ctx->sk.vtable, tmp, 32); + + /* + * Compute new G and H values. + */ + memcpy(iv, G, 12); + memcpy(newG, G, 16); + ctx->sk.vtable->run(&ctx->sk.vtable, iv, + br_dec32be(G + 12), newG, 16); + iv[0] ^= 0x01; + memcpy(H, G, 16); + H[0] ^= 0x01; + ctx->sk.vtable->run(&ctx->sk.vtable, iv, + br_dec32be(G + 12), H, 16); + memcpy(G, newG, 16); + } + + /* + * Output hash value is H||G. We truncate it to its first 128 bits, + * i.e. H; that's our new AES key. + */ + ctx->sk.vtable->init(&ctx->sk.vtable, H, 16); + ctx->cc = 0; +} + +/* see bearssl_rand.h */ +const br_prng_class br_aesctr_drbg_vtable = { + sizeof(br_aesctr_drbg_context), + (void (*)(const br_prng_class **, const void *, const void *, size_t)) + &br_aesctr_drbg_init, + (void (*)(const br_prng_class **, void *, size_t)) + &br_aesctr_drbg_generate, + (void (*)(const br_prng_class **, const void *, size_t)) + &br_aesctr_drbg_update +}; diff --git a/lib/bearssl-esp8266/src/rand/hmac_drbg.c b/lib/bearssl-esp8266/src/rand/hmac_drbg.c new file mode 100644 index 000000000..77246968c --- /dev/null +++ b/lib/bearssl-esp8266/src/rand/hmac_drbg.c @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl.h */ +void +br_hmac_drbg_init(br_hmac_drbg_context *ctx, + const br_hash_class *digest_class, const void *seed, size_t len) +{ + size_t hlen; + + ctx->vtable = &br_hmac_drbg_vtable; + hlen = br_digest_size(digest_class); + memset(ctx->K, 0x00, hlen); + memset(ctx->V, 0x01, hlen); + ctx->digest_class = digest_class; + br_hmac_drbg_update(ctx, seed, len); +} + +/* see bearssl.h */ +void +br_hmac_drbg_generate(br_hmac_drbg_context *ctx, void *out, size_t len) +{ + const br_hash_class *dig; + br_hmac_key_context kc; + br_hmac_context hc; + size_t hlen; + unsigned char *buf; + unsigned char x; + + dig = ctx->digest_class; + hlen = br_digest_size(dig); + br_hmac_key_init(&kc, dig, ctx->K, hlen); + buf = out; + while (len > 0) { + size_t clen; + + br_hmac_init(&hc, &kc, 0); + br_hmac_update(&hc, ctx->V, hlen); + br_hmac_out(&hc, ctx->V); + clen = hlen; + if (clen > len) { + clen = len; + } + memcpy(buf, ctx->V, clen); + buf += clen; + len -= clen; + } + + /* + * To prepare the state for the next request, we should call + * br_hmac_drbg_update() with an empty additional seed. However, + * we already have an initialized HMAC context with the right + * initial key, and we don't want to push another one on the + * stack, so we inline that update() call here. + */ + br_hmac_init(&hc, &kc, 0); + br_hmac_update(&hc, ctx->V, hlen); + x = 0x00; + br_hmac_update(&hc, &x, 1); + br_hmac_out(&hc, ctx->K); + br_hmac_key_init(&kc, dig, ctx->K, hlen); + br_hmac_init(&hc, &kc, 0); + br_hmac_update(&hc, ctx->V, hlen); + br_hmac_out(&hc, ctx->V); +} + +/* see bearssl.h */ +void +br_hmac_drbg_update(br_hmac_drbg_context *ctx, const void *seed, size_t len) +{ + const br_hash_class *dig; + br_hmac_key_context kc; + br_hmac_context hc; + size_t hlen; + unsigned char x; + + dig = ctx->digest_class; + hlen = br_digest_size(dig); + + /* + * 1. K = HMAC(K, V || 0x00 || seed) + */ + br_hmac_key_init(&kc, dig, ctx->K, hlen); + br_hmac_init(&hc, &kc, 0); + br_hmac_update(&hc, ctx->V, hlen); + x = 0x00; + br_hmac_update(&hc, &x, 1); + br_hmac_update(&hc, seed, len); + br_hmac_out(&hc, ctx->K); + br_hmac_key_init(&kc, dig, ctx->K, hlen); + + /* + * 2. V = HMAC(K, V) + */ + br_hmac_init(&hc, &kc, 0); + br_hmac_update(&hc, ctx->V, hlen); + br_hmac_out(&hc, ctx->V); + + /* + * 3. If the additional seed is empty, then stop here. + */ + if (len == 0) { + return; + } + + /* + * 4. K = HMAC(K, V || 0x01 || seed) + */ + br_hmac_init(&hc, &kc, 0); + br_hmac_update(&hc, ctx->V, hlen); + x = 0x01; + br_hmac_update(&hc, &x, 1); + br_hmac_update(&hc, seed, len); + br_hmac_out(&hc, ctx->K); + br_hmac_key_init(&kc, dig, ctx->K, hlen); + + /* + * 5. V = HMAC(K, V) + */ + br_hmac_init(&hc, &kc, 0); + br_hmac_update(&hc, ctx->V, hlen); + br_hmac_out(&hc, ctx->V); +} + +/* see bearssl.h */ +const br_prng_class br_hmac_drbg_vtable PROGMEM = { + sizeof(br_hmac_drbg_context), + (void (*)(const br_prng_class **, const void *, const void *, size_t)) + &br_hmac_drbg_init, + (void (*)(const br_prng_class **, void *, size_t)) + &br_hmac_drbg_generate, + (void (*)(const br_prng_class **, const void *, size_t)) + &br_hmac_drbg_update +}; diff --git a/lib/bearssl-esp8266/src/rand/sysrng.c b/lib/bearssl-esp8266/src/rand/sysrng.c new file mode 100644 index 000000000..c8268de72 --- /dev/null +++ b/lib/bearssl-esp8266/src/rand/sysrng.c @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BR_ENABLE_INTRINSICS 1 +#include "t_inner.h" + +#if BR_USE_URANDOM +#include +#include +#include +#include +#endif + +#if BR_USE_WIN32_RAND +#include +#include +#pragma comment(lib, "advapi32") +#endif + +#if BR_RDRAND +BR_TARGETS_X86_UP +BR_TARGET("rdrnd") +static int +seeder_rdrand(const br_prng_class **ctx) +{ + unsigned char tmp[32]; + size_t u; + + for (u = 0; u < sizeof tmp; u += sizeof(uint32_t)) { + int j; + uint32_t x; + + /* + * We use the 32-bit intrinsic so that code is compatible + * with both 32-bit and 64-bit architectures. + * + * Intel recommends trying at least 10 times in case of + * failure. + */ + for (j = 0; j < 10; j ++) { + if (_rdrand32_step(&x)) { + goto next_word; + } + } + return 0; + next_word: + br_enc32le(tmp + u, x); + } + (*ctx)->update(ctx, tmp, sizeof tmp); + return 1; +} +BR_TARGETS_X86_DOWN + +static int +rdrand_supported(void) +{ + /* + * The RDRND support is bit 30 of ECX, as returned by CPUID. + */ + return br_cpuid(0, 0, 0x40000000, 0); +} + +#endif + +#if BR_USE_URANDOM +static int +seeder_urandom(const br_prng_class **ctx) +{ + int f; + + f = open("/dev/urandom", O_RDONLY); + if (f >= 0) { + unsigned char tmp[32]; + size_t u; + + for (u = 0; u < sizeof tmp;) { + ssize_t len; + + len = read(f, tmp + u, (sizeof tmp) - u); + if (len < 0) { + if (errno == EINTR) { + continue; + } + break; + } + u += (size_t)len; + } + close(f); + if (u == sizeof tmp) { + (*ctx)->update(ctx, tmp, sizeof tmp); + return 1; + } + } + return 0; +} +#endif + +#if BR_USE_WIN32_RAND +static int +seeder_win32(const br_prng_class **ctx) +{ + HCRYPTPROV hp; + + if (CryptAcquireContext(&hp, 0, 0, PROV_RSA_FULL, + CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) + { + BYTE buf[32]; + BOOL r; + + r = CryptGenRandom(hp, sizeof buf, buf); + CryptReleaseContext(hp, 0); + if (r) { + (*ctx)->update(ctx, buf, sizeof buf); + return 1; + } + } + return 0; +} +#endif + +#if BR_USE_ESP8266_RAND +extern uint32_t phy_get_rand(void); // From the ESP8266 SDK + +static int +seeder_esp8266(const br_prng_class **ctx) +{ + uint32_t tmp[32 / sizeof(uint32_t)]; + size_t i; + + for (i=0; iupdate(ctx, tmp, sizeof tmp); + + return 1; +} +#endif + +/* see bearssl_rand.h */ + +br_prng_seeder +br_prng_seeder_system(const char **name) +{ +#if BR_RDRAND + if (rdrand_supported()) { + if (name != NULL) { + *name = "rdrand"; + } + return &seeder_rdrand; + } +#endif +#if BR_USE_URANDOM + if (name != NULL) { + *name = "urandom"; + } + return &seeder_urandom; +#elif BR_USE_WIN32_RAND + if (name != NULL) { + *name = "win32"; + } + return &seeder_win32; +#elif BR_USE_ESP8266_RAND + if (name != NULL) { + *name = "esp8266"; + } + return &seeder_esp8266; +#else + if (name != NULL) { + *name = "none"; + } + return 0; +#endif +} diff --git a/lib/bearssl-esp8266/src/rsa/rsa_default_keygen.c b/lib/bearssl-esp8266/src/rsa/rsa_default_keygen.c new file mode 100644 index 000000000..d07971aa5 --- /dev/null +++ b/lib/bearssl-esp8266/src/rsa/rsa_default_keygen.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_rsa.h */ +br_rsa_keygen +br_rsa_keygen_get_default(void) +{ +#if BR_INT128 || BR_UMUL128 + return &br_rsa_i62_keygen; +#elif BR_LOMUL + return &br_rsa_i15_keygen; +#else + return &br_rsa_i31_keygen; +#endif +} diff --git a/lib/bearssl-esp8266/src/rsa/rsa_default_modulus.c b/lib/bearssl-esp8266/src/rsa/rsa_default_modulus.c new file mode 100644 index 000000000..a4ead3b65 --- /dev/null +++ b/lib/bearssl-esp8266/src/rsa/rsa_default_modulus.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_rsa.h */ +br_rsa_compute_modulus +br_rsa_compute_modulus_get_default(void) +{ +#if BR_LOMUL + return &br_rsa_i15_compute_modulus; +#else + return &br_rsa_i31_compute_modulus; +#endif +} diff --git a/lib/bearssl-esp8266/src/rsa/rsa_default_oaep_decrypt.c b/lib/bearssl-esp8266/src/rsa/rsa_default_oaep_decrypt.c new file mode 100644 index 000000000..7ca825f1e --- /dev/null +++ b/lib/bearssl-esp8266/src/rsa/rsa_default_oaep_decrypt.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_rsa.h */ +br_rsa_oaep_decrypt +br_rsa_oaep_decrypt_get_default(void) +{ +#if BR_INT128 || BR_UMUL128 + return &br_rsa_i62_oaep_decrypt; +#elif BR_LOMUL + return &br_rsa_i15_oaep_decrypt; +#else + return &br_rsa_i31_oaep_decrypt; +#endif +} diff --git a/lib/bearssl-esp8266/src/rsa/rsa_default_oaep_encrypt.c b/lib/bearssl-esp8266/src/rsa/rsa_default_oaep_encrypt.c new file mode 100644 index 000000000..518a5f424 --- /dev/null +++ b/lib/bearssl-esp8266/src/rsa/rsa_default_oaep_encrypt.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_rsa.h */ +br_rsa_oaep_encrypt +br_rsa_oaep_encrypt_get_default(void) +{ +#if BR_INT128 || BR_UMUL128 + return &br_rsa_i62_oaep_encrypt; +#elif BR_LOMUL + return &br_rsa_i15_oaep_encrypt; +#else + return &br_rsa_i31_oaep_encrypt; +#endif +} diff --git a/lib/bearssl-esp8266/src/rsa/rsa_default_pkcs1_sign.c b/lib/bearssl-esp8266/src/rsa/rsa_default_pkcs1_sign.c new file mode 100644 index 000000000..09af0624c --- /dev/null +++ b/lib/bearssl-esp8266/src/rsa/rsa_default_pkcs1_sign.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_rsa.h */ +br_rsa_pkcs1_sign +br_rsa_pkcs1_sign_get_default(void) +{ +#if BR_INT128 || BR_UMUL128 + return &br_rsa_i62_pkcs1_sign; +#elif BR_LOMUL + return &br_rsa_i15_pkcs1_sign; +#else + return &br_rsa_i31_pkcs1_sign; +#endif +} diff --git a/lib/bearssl-esp8266/src/rsa/rsa_default_pkcs1_vrfy.c b/lib/bearssl-esp8266/src/rsa/rsa_default_pkcs1_vrfy.c new file mode 100644 index 000000000..791467d4b --- /dev/null +++ b/lib/bearssl-esp8266/src/rsa/rsa_default_pkcs1_vrfy.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_rsa.h */ +br_rsa_pkcs1_vrfy +br_rsa_pkcs1_vrfy_get_default(void) +{ +#if BR_INT128 || BR_UMUL128 + return &br_rsa_i62_pkcs1_vrfy; +#elif BR_LOMUL + return &br_rsa_i15_pkcs1_vrfy; +#else + return &br_rsa_i31_pkcs1_vrfy; +#endif +} diff --git a/lib/bearssl-esp8266/src/rsa/rsa_default_priv.c b/lib/bearssl-esp8266/src/rsa/rsa_default_priv.c new file mode 100644 index 000000000..8fc9e5cad --- /dev/null +++ b/lib/bearssl-esp8266/src/rsa/rsa_default_priv.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_rsa.h */ +br_rsa_private +br_rsa_private_get_default(void) +{ +#if BR_INT128 || BR_UMUL128 + return &br_rsa_i62_private; +#elif BR_LOMUL + return &br_rsa_i15_private; +#else + return &br_rsa_i31_private; +#endif +} diff --git a/lib/bearssl-esp8266/src/rsa/rsa_default_privexp.c b/lib/bearssl-esp8266/src/rsa/rsa_default_privexp.c new file mode 100644 index 000000000..015ba197a --- /dev/null +++ b/lib/bearssl-esp8266/src/rsa/rsa_default_privexp.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_rsa.h */ +br_rsa_compute_privexp +br_rsa_compute_privexp_get_default(void) +{ +#if BR_LOMUL + return &br_rsa_i15_compute_privexp; +#else + return &br_rsa_i31_compute_privexp; +#endif +} diff --git a/lib/bearssl-esp8266/src/rsa/rsa_default_pss_sign.c b/lib/bearssl-esp8266/src/rsa/rsa_default_pss_sign.c new file mode 100644 index 000000000..72549e66a --- /dev/null +++ b/lib/bearssl-esp8266/src/rsa/rsa_default_pss_sign.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_rsa.h */ +br_rsa_pss_sign +br_rsa_pss_sign_get_default(void) +{ +#if BR_INT128 || BR_UMUL128 + return &br_rsa_i62_pss_sign; +#elif BR_LOMUL + return &br_rsa_i15_pss_sign; +#else + return &br_rsa_i31_pss_sign; +#endif +} diff --git a/lib/bearssl-esp8266/src/rsa/rsa_default_pss_vrfy.c b/lib/bearssl-esp8266/src/rsa/rsa_default_pss_vrfy.c new file mode 100644 index 000000000..1321d0a7f --- /dev/null +++ b/lib/bearssl-esp8266/src/rsa/rsa_default_pss_vrfy.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_rsa.h */ +br_rsa_pss_vrfy +br_rsa_pss_vrfy_get_default(void) +{ +#if BR_INT128 || BR_UMUL128 + return &br_rsa_i62_pss_vrfy; +#elif BR_LOMUL + return &br_rsa_i15_pss_vrfy; +#else + return &br_rsa_i31_pss_vrfy; +#endif +} diff --git a/lib/bearssl-esp8266/src/rsa/rsa_default_pub.c b/lib/bearssl-esp8266/src/rsa/rsa_default_pub.c new file mode 100644 index 000000000..5d42fbd57 --- /dev/null +++ b/lib/bearssl-esp8266/src/rsa/rsa_default_pub.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_rsa.h */ +br_rsa_public +br_rsa_public_get_default(void) +{ +#if BR_INT128 || BR_UMUL128 + return &br_rsa_i62_public; +#elif BR_LOMUL + return &br_rsa_i15_public; +#else + return &br_rsa_i31_public; +#endif +} diff --git a/lib/bearssl-esp8266/src/rsa/rsa_default_pubexp.c b/lib/bearssl-esp8266/src/rsa/rsa_default_pubexp.c new file mode 100644 index 000000000..c3ca497fd --- /dev/null +++ b/lib/bearssl-esp8266/src/rsa/rsa_default_pubexp.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_rsa.h */ +br_rsa_compute_pubexp +br_rsa_compute_pubexp_get_default(void) +{ +#if BR_LOMUL + return &br_rsa_i15_compute_pubexp; +#else + return &br_rsa_i31_compute_pubexp; +#endif +} diff --git a/lib/bearssl-esp8266/src/rsa/rsa_i15_keygen.c b/lib/bearssl-esp8266/src/rsa/rsa_i15_keygen.c new file mode 100644 index 000000000..3481b4636 --- /dev/null +++ b/lib/bearssl-esp8266/src/rsa/rsa_i15_keygen.c @@ -0,0 +1,585 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * Make a random integer of the provided size. The size is encoded. + * The header word is untouched. + */ +static void +mkrand(const br_prng_class **rng, uint16_t *x, uint32_t esize) +{ + size_t u, len; + unsigned m; + + len = (esize + 15) >> 4; + (*rng)->generate(rng, x + 1, len * sizeof(uint16_t)); + for (u = 1; u < len; u ++) { + x[u] &= 0x7FFF; + } + m = esize & 15; + if (m == 0) { + x[len] &= 0x7FFF; + } else { + x[len] &= 0x7FFF >> (15 - m); + } +} + +/* + * This is the big-endian unsigned representation of the product of + * all small primes from 13 to 1481. + */ +static const unsigned char SMALL_PRIMES[] PROGMEM = { + 0x2E, 0xAB, 0x92, 0xD1, 0x8B, 0x12, 0x47, 0x31, 0x54, 0x0A, + 0x99, 0x5D, 0x25, 0x5E, 0xE2, 0x14, 0x96, 0x29, 0x1E, 0xB7, + 0x78, 0x70, 0xCC, 0x1F, 0xA5, 0xAB, 0x8D, 0x72, 0x11, 0x37, + 0xFB, 0xD8, 0x1E, 0x3F, 0x5B, 0x34, 0x30, 0x17, 0x8B, 0xE5, + 0x26, 0x28, 0x23, 0xA1, 0x8A, 0xA4, 0x29, 0xEA, 0xFD, 0x9E, + 0x39, 0x60, 0x8A, 0xF3, 0xB5, 0xA6, 0xEB, 0x3F, 0x02, 0xB6, + 0x16, 0xC3, 0x96, 0x9D, 0x38, 0xB0, 0x7D, 0x82, 0x87, 0x0C, + 0xF7, 0xBE, 0x24, 0xE5, 0x5F, 0x41, 0x04, 0x79, 0x76, 0x40, + 0xE7, 0x00, 0x22, 0x7E, 0xB5, 0x85, 0x7F, 0x8D, 0x01, 0x50, + 0xE9, 0xD3, 0x29, 0x42, 0x08, 0xB3, 0x51, 0x40, 0x7B, 0xD7, + 0x8D, 0xCC, 0x10, 0x01, 0x64, 0x59, 0x28, 0xB6, 0x53, 0xF3, + 0x50, 0x4E, 0xB1, 0xF2, 0x58, 0xCD, 0x6E, 0xF5, 0x56, 0x3E, + 0x66, 0x2F, 0xD7, 0x07, 0x7F, 0x52, 0x4C, 0x13, 0x24, 0xDC, + 0x8E, 0x8D, 0xCC, 0xED, 0x77, 0xC4, 0x21, 0xD2, 0xFD, 0x08, + 0xEA, 0xD7, 0xC0, 0x5C, 0x13, 0x82, 0x81, 0x31, 0x2F, 0x2B, + 0x08, 0xE4, 0x80, 0x04, 0x7A, 0x0C, 0x8A, 0x3C, 0xDC, 0x22, + 0xE4, 0x5A, 0x7A, 0xB0, 0x12, 0x5E, 0x4A, 0x76, 0x94, 0x77, + 0xC2, 0x0E, 0x92, 0xBA, 0x8A, 0xA0, 0x1F, 0x14, 0x51, 0x1E, + 0x66, 0x6C, 0x38, 0x03, 0x6C, 0xC7, 0x4A, 0x4B, 0x70, 0x80, + 0xAF, 0xCA, 0x84, 0x51, 0xD8, 0xD2, 0x26, 0x49, 0xF5, 0xA8, + 0x5E, 0x35, 0x4B, 0xAC, 0xCE, 0x29, 0x92, 0x33, 0xB7, 0xA2, + 0x69, 0x7D, 0x0C, 0xE0, 0x9C, 0xDB, 0x04, 0xD6, 0xB4, 0xBC, + 0x39, 0xD7, 0x7F, 0x9E, 0x9D, 0x78, 0x38, 0x7F, 0x51, 0x54, + 0x50, 0x8B, 0x9E, 0x9C, 0x03, 0x6C, 0xF5, 0x9D, 0x2C, 0x74, + 0x57, 0xF0, 0x27, 0x2A, 0xC3, 0x47, 0xCA, 0xB9, 0xD7, 0x5C, + 0xFF, 0xC2, 0xAC, 0x65, 0x4E, 0xBD +}; + +/* + * We need temporary values for at least 7 integers of the same size + * as a factor (including header word); more space helps with performance + * (in modular exponentiations), but we much prefer to remain under + * 2 kilobytes in total, to save stack space. The macro TEMPS below + * exceeds 1024 (which is a count in 16-bit words) when BR_MAX_RSA_SIZE + * is greater than 4350 (default value is 4096, so the 2-kB limit is + * maintained unless BR_MAX_RSA_SIZE was modified). + */ +#define MAX(x, y) ((x) > (y) ? (x) : (y)) +#define TEMPS MAX(1024, 7 * ((((BR_MAX_RSA_SIZE + 1) >> 1) + 29) / 15)) + +/* + * Perform trial division on a candidate prime. This computes + * y = SMALL_PRIMES mod x, then tries to compute y/y mod x. The + * br_i15_moddiv() function will report an error if y is not invertible + * modulo x. Returned value is 1 on success (none of the small primes + * divides x), 0 on error (a non-trivial GCD is obtained). + * + * This function assumes that x is odd. + */ +static uint32_t +trial_divisions(const uint16_t *x, uint16_t *t) +{ + uint16_t *y; + uint16_t x0i; + unsigned char small_primes_ram[sizeof SMALL_PRIMES]; + memcpy_P(small_primes_ram, SMALL_PRIMES, sizeof SMALL_PRIMES); + + y = t; + t += 1 + ((x[0] + 15) >> 4); + x0i = br_i15_ninv15(x[1]); + br_i15_decode_reduce(y, SMALL_PRIMES, sizeof SMALL_PRIMES, x); + return br_i15_moddiv(y, y, x, x0i, t); +} + +/* + * Perform n rounds of Miller-Rabin on the candidate prime x. This + * function assumes that x = 3 mod 4. + * + * Returned value is 1 on success (all rounds completed successfully), + * 0 otherwise. + */ +static uint32_t +miller_rabin(const br_prng_class **rng, const uint16_t *x, int n, + uint16_t *t, size_t tlen) +{ + /* + * Since x = 3 mod 4, the Miller-Rabin test is simple: + * - get a random base a (such that 1 < a < x-1) + * - compute z = a^((x-1)/2) mod x + * - if z != 1 and z != x-1, the number x is composite + * + * We generate bases 'a' randomly with a size which is + * one bit less than x, which ensures that a < x-1. It + * is not useful to verify that a > 1 because the probability + * that we get a value a equal to 0 or 1 is much smaller + * than the probability of our Miller-Rabin tests not to + * detect a composite, which is already quite smaller than the + * probability of the hardware misbehaving and return a + * composite integer because of some glitch (e.g. bad RAM + * or ill-timed cosmic ray). + */ + unsigned char *xm1d2; + size_t xlen, xm1d2_len, xm1d2_len_u16, u; + uint32_t asize; + unsigned cc; + uint16_t x0i; + + /* + * Compute (x-1)/2 (encoded). + */ + xm1d2 = (unsigned char *)t; + xm1d2_len = ((x[0] - (x[0] >> 4)) + 7) >> 3; + br_i15_encode(xm1d2, xm1d2_len, x); + cc = 0; + for (u = 0; u < xm1d2_len; u ++) { + unsigned w; + + w = xm1d2[u]; + xm1d2[u] = (unsigned char)((w >> 1) | cc); + cc = w << 7; + } + + /* + * We used some words of the provided buffer for (x-1)/2. + */ + xm1d2_len_u16 = (xm1d2_len + 1) >> 1; + t += xm1d2_len_u16; + tlen -= xm1d2_len_u16; + + xlen = (x[0] + 15) >> 4; + asize = x[0] - 1 - EQ0(x[0] & 15); + x0i = br_i15_ninv15(x[1]); + while (n -- > 0) { + uint16_t *a; + uint32_t eq1, eqm1; + + /* + * Generate a random base. We don't need the base to be + * really uniform modulo x, so we just get a random + * number which is one bit shorter than x. + */ + a = t; + a[0] = x[0]; + a[xlen] = 0; + mkrand(rng, a, asize); + + /* + * Compute a^((x-1)/2) mod x. We assume here that the + * function will not fail (the temporary array is large + * enough). + */ + br_i15_modpow_opt(a, xm1d2, xm1d2_len, + x, x0i, t + 1 + xlen, tlen - 1 - xlen); + + /* + * We must obtain either 1 or x-1. Note that x is odd, + * hence x-1 differs from x only in its low word (no + * carry). + */ + eq1 = a[1] ^ 1; + eqm1 = a[1] ^ (x[1] - 1); + for (u = 2; u <= xlen; u ++) { + eq1 |= a[u]; + eqm1 |= a[u] ^ x[u]; + } + + if ((EQ0(eq1) | EQ0(eqm1)) == 0) { + return 0; + } + } + return 1; +} + +/* + * Create a random prime of the provided size. 'size' is the _encoded_ + * bit length. The two top bits and the two bottom bits are set to 1. + */ +static void +mkprime(const br_prng_class **rng, uint16_t *x, uint32_t esize, + uint32_t pubexp, uint16_t *t, size_t tlen) +{ + size_t len; + + x[0] = esize; + len = (esize + 15) >> 4; + for (;;) { + size_t u; + uint32_t m3, m5, m7, m11; + int rounds; + + /* + * Generate random bits. We force the two top bits and the + * two bottom bits to 1. + */ + mkrand(rng, x, esize); + if ((esize & 15) == 0) { + x[len] |= 0x6000; + } else if ((esize & 15) == 1) { + x[len] |= 0x0001; + x[len - 1] |= 0x4000; + } else { + x[len] |= 0x0003 << ((esize & 15) - 2); + } + x[1] |= 0x0003; + + /* + * Trial division with low primes (3, 5, 7 and 11). We + * use the following properties: + * + * 2^2 = 1 mod 3 + * 2^4 = 1 mod 5 + * 2^3 = 1 mod 7 + * 2^10 = 1 mod 11 + */ + m3 = 0; + m5 = 0; + m7 = 0; + m11 = 0; + for (u = 0; u < len; u ++) { + uint32_t w; + + w = x[1 + u]; + m3 += w << (u & 1); + m3 = (m3 & 0xFF) + (m3 >> 8); + m5 += w << ((4 - u) & 3); + m5 = (m5 & 0xFF) + (m5 >> 8); + m7 += w; + m7 = (m7 & 0x1FF) + (m7 >> 9); + m11 += w << (5 & -(u & 1)); + m11 = (m11 & 0x3FF) + (m11 >> 10); + } + + /* + * Maximum values of m* at this point: + * m3: 511 + * m5: 2310 + * m7: 510 + * m11: 2047 + * We use the same properties to make further reductions. + */ + + m3 = (m3 & 0x0F) + (m3 >> 4); /* max: 46 */ + m3 = (m3 & 0x0F) + (m3 >> 4); /* max: 16 */ + m3 = ((m3 * 43) >> 5) & 3; + + m5 = (m5 & 0xFF) + (m5 >> 8); /* max: 263 */ + m5 = (m5 & 0x0F) + (m5 >> 4); /* max: 30 */ + m5 = (m5 & 0x0F) + (m5 >> 4); /* max: 15 */ + m5 -= 10 & -GT(m5, 9); + m5 -= 5 & -GT(m5, 4); + + m7 = (m7 & 0x3F) + (m7 >> 6); /* max: 69 */ + m7 = (m7 & 7) + (m7 >> 3); /* max: 14 */ + m7 = ((m7 * 147) >> 7) & 7; + + /* + * 2^5 = 32 = -1 mod 11. + */ + m11 = (m11 & 0x1F) + 66 - (m11 >> 5); /* max: 97 */ + m11 -= 88 & -GT(m11, 87); + m11 -= 44 & -GT(m11, 43); + m11 -= 22 & -GT(m11, 21); + m11 -= 11 & -GT(m11, 10); + + /* + * If any of these modulo is 0, then the candidate is + * not prime. Also, if pubexp is 3, 5, 7 or 11, and the + * corresponding modulus is 1, then the candidate must + * be rejected, because we need e to be invertible + * modulo p-1. We can use simple comparisons here + * because they won't leak information on a candidate + * that we keep, only on one that we reject (and is thus + * not secret). + */ + if (m3 == 0 || m5 == 0 || m7 == 0 || m11 == 0) { + continue; + } + if ((pubexp == 3 && m3 == 1) + || (pubexp == 5 && m5 == 1) + || (pubexp == 7 && m7 == 1) + || (pubexp == 11 && m11 == 1)) + { + continue; + } + + /* + * More trial divisions. + */ + if (!trial_divisions(x, t)) { + continue; + } + + /* + * Miller-Rabin algorithm. Since we selected a random + * integer, not a maliciously crafted integer, we can use + * relatively few rounds to lower the risk of a false + * positive (i.e. declaring prime a non-prime) under + * 2^(-80). It is not useful to lower the probability much + * below that, since that would be substantially below + * the probability of the hardware misbehaving. Sufficient + * numbers of rounds are extracted from the Handbook of + * Applied Cryptography, note 4.49 (page 149). + * + * Since we work on the encoded size (esize), we need to + * compare with encoded thresholds. + */ + if (esize < 320) { + rounds = 12; + } else if (esize < 480) { + rounds = 9; + } else if (esize < 693) { + rounds = 6; + } else if (esize < 906) { + rounds = 4; + } else if (esize < 1386) { + rounds = 3; + } else { + rounds = 2; + } + + if (miller_rabin(rng, x, rounds, t, tlen)) { + return; + } + } +} + +/* + * Let p be a prime (p > 2^33, p = 3 mod 4). Let m = (p-1)/2, provided + * as parameter (with announced bit length equal to that of p). This + * function computes d = 1/e mod p-1 (for an odd integer e). Returned + * value is 1 on success, 0 on error (an error is reported if e is not + * invertible modulo p-1). + * + * The temporary buffer (t) must have room for at least 4 integers of + * the size of p. + */ +static uint32_t +invert_pubexp(uint16_t *d, const uint16_t *m, uint32_t e, uint16_t *t) +{ + uint16_t *f; + uint32_t r; + + f = t; + t += 1 + ((m[0] + 15) >> 4); + + /* + * Compute d = 1/e mod m. Since p = 3 mod 4, m is odd. + */ + br_i15_zero(d, m[0]); + d[1] = 1; + br_i15_zero(f, m[0]); + f[1] = e & 0x7FFF; + f[2] = (e >> 15) & 0x7FFF; + f[3] = e >> 30; + r = br_i15_moddiv(d, f, m, br_i15_ninv15(m[1]), t); + + /* + * We really want d = 1/e mod p-1, with p = 2m. By the CRT, + * the result is either the d we got, or d + m. + * + * Let's write e*d = 1 + k*m, for some integer k. Integers e + * and m are odd. If d is odd, then e*d is odd, which implies + * that k must be even; in that case, e*d = 1 + (k/2)*2m, and + * thus d is already fine. Conversely, if d is even, then k + * is odd, and we must add m to d in order to get the correct + * result. + */ + br_i15_add(d, m, (uint32_t)(1 - (d[1] & 1))); + + return r; +} + +/* + * Swap two buffers in RAM. They must be disjoint. + */ +static void +bufswap(void *b1, void *b2, size_t len) +{ + size_t u; + unsigned char *buf1, *buf2; + + buf1 = b1; + buf2 = b2; + for (u = 0; u < len; u ++) { + unsigned w; + + w = buf1[u]; + buf1[u] = buf2[u]; + buf2[u] = w; + } +} + +/* see bearssl_rsa.h */ +uint32_t +br_rsa_i15_keygen(const br_prng_class **rng, + br_rsa_private_key *sk, void *kbuf_priv, + br_rsa_public_key *pk, void *kbuf_pub, + unsigned size, uint32_t pubexp) +{ + uint32_t esize_p, esize_q; + size_t plen, qlen, tlen; + uint16_t *p, *q, *t; + uint16_t tmp[TEMPS]; + uint32_t r; + + if (size < BR_MIN_RSA_SIZE || size > BR_MAX_RSA_SIZE) { + return 0; + } + if (pubexp == 0) { + pubexp = 3; + } else if (pubexp == 1 || (pubexp & 1) == 0) { + return 0; + } + + esize_p = (size + 1) >> 1; + esize_q = size - esize_p; + sk->n_bitlen = size; + sk->p = kbuf_priv; + sk->plen = (esize_p + 7) >> 3; + sk->q = sk->p + sk->plen; + sk->qlen = (esize_q + 7) >> 3; + sk->dp = sk->q + sk->qlen; + sk->dplen = sk->plen; + sk->dq = sk->dp + sk->dplen; + sk->dqlen = sk->qlen; + sk->iq = sk->dq + sk->dqlen; + sk->iqlen = sk->plen; + + if (pk != NULL) { + pk->n = kbuf_pub; + pk->nlen = (size + 7) >> 3; + pk->e = pk->n + pk->nlen; + pk->elen = 4; + br_enc32be(pk->e, pubexp); + while (*pk->e == 0) { + pk->e ++; + pk->elen --; + } + } + + /* + * We now switch to encoded sizes. + * + * floor((x * 17477) / (2^18)) is equal to floor(x/15) for all + * integers x from 0 to 23833. + */ + esize_p += MUL15(esize_p, 17477) >> 18; + esize_q += MUL15(esize_q, 17477) >> 18; + plen = (esize_p + 15) >> 4; + qlen = (esize_q + 15) >> 4; + p = tmp; + q = p + 1 + plen; + t = q + 1 + qlen; + tlen = ((sizeof tmp) / sizeof(uint16_t)) - (2 + plen + qlen); + + /* + * When looking for primes p and q, we temporarily divide + * candidates by 2, in order to compute the inverse of the + * public exponent. + */ + + for (;;) { + mkprime(rng, p, esize_p, pubexp, t, tlen); + br_i15_rshift(p, 1); + if (invert_pubexp(t, p, pubexp, t + 1 + plen)) { + br_i15_add(p, p, 1); + p[1] |= 1; + br_i15_encode(sk->p, sk->plen, p); + br_i15_encode(sk->dp, sk->dplen, t); + break; + } + } + + for (;;) { + mkprime(rng, q, esize_q, pubexp, t, tlen); + br_i15_rshift(q, 1); + if (invert_pubexp(t, q, pubexp, t + 1 + qlen)) { + br_i15_add(q, q, 1); + q[1] |= 1; + br_i15_encode(sk->q, sk->qlen, q); + br_i15_encode(sk->dq, sk->dqlen, t); + break; + } + } + + /* + * If p and q have the same size, then it is possible that q > p + * (when the target modulus size is odd, we generate p with a + * greater bit length than q). If q > p, we want to swap p and q + * (and also dp and dq) for two reasons: + * - The final step below (inversion of q modulo p) is easier if + * p > q. + * - While BearSSL's RSA code is perfectly happy with RSA keys such + * that p < q, some other implementations have restrictions and + * require p > q. + * + * Note that we can do a simple non-constant-time swap here, + * because the only information we leak here is that we insist on + * returning p and q such that p > q, which is not a secret. + */ + if (esize_p == esize_q && br_i15_sub(p, q, 0) == 1) { + bufswap(p, q, (1 + plen) * sizeof *p); + bufswap(sk->p, sk->q, sk->plen); + bufswap(sk->dp, sk->dq, sk->dplen); + } + + /* + * We have produced p, q, dp and dq. We can now compute iq = 1/d mod p. + * + * We ensured that p >= q, so this is just a matter of updating the + * header word for q (and possibly adding an extra word). + * + * Theoretically, the call below may fail, in case we were + * extraordinarily unlucky, and p = q. Another failure case is if + * Miller-Rabin failed us _twice_, and p and q are non-prime and + * have a factor is common. We report the error mostly because it + * is cheap and we can, but in practice this never happens (or, at + * least, it happens way less often than hardware glitches). + */ + q[0] = p[0]; + if (plen > qlen) { + q[plen] = 0; + t ++; + tlen --; + } + br_i15_zero(t, p[0]); + t[1] = 1; + r = br_i15_moddiv(t, q, p, br_i15_ninv15(p[1]), t + 1 + plen); + br_i15_encode(sk->iq, sk->iqlen, t); + + /* + * Compute the public modulus too, if required. + */ + if (pk != NULL) { + br_i15_zero(t, p[0]); + br_i15_mulacc(t, p, q); + br_i15_encode(pk->n, pk->nlen, t); + } + + return r; +} diff --git a/lib/bearssl-esp8266/src/rsa/rsa_i15_modulus.c b/lib/bearssl-esp8266/src/rsa/rsa_i15_modulus.c new file mode 100644 index 000000000..6a3532937 --- /dev/null +++ b/lib/bearssl-esp8266/src/rsa/rsa_i15_modulus.c @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_rsa.h */ +size_t +br_rsa_i15_compute_modulus(void *n, const br_rsa_private_key *sk) +{ + uint16_t tmp[4 * (((BR_MAX_RSA_SIZE / 2) + 14) / 15) + 5]; + uint16_t *t, *p, *q; + const unsigned char *pbuf, *qbuf; + size_t nlen, plen, qlen, tlen; + + /* + * Compute actual byte and lengths for p and q. + */ + pbuf = sk->p; + plen = sk->plen; + while (plen > 0 && *pbuf == 0) { + pbuf ++; + plen --; + } + qbuf = sk->q; + qlen = sk->qlen; + while (qlen > 0 && *qbuf == 0) { + qbuf ++; + qlen --; + } + + t = tmp; + tlen = (sizeof tmp) / (sizeof tmp[0]); + + /* + * Decode p. + */ + if ((15 * tlen) < (plen << 3) + 15) { + return 0; + } + br_i15_decode(t, pbuf, plen); + p = t; + plen = (p[0] + 31) >> 4; + t += plen; + tlen -= plen; + + /* + * Decode q. + */ + if ((15 * tlen) < (qlen << 3) + 15) { + return 0; + } + br_i15_decode(t, qbuf, qlen); + q = t; + qlen = (q[0] + 31) >> 4; + t += qlen; + tlen -= qlen; + + /* + * Computation can proceed only if we have enough room for the + * modulus. + */ + if (tlen < (plen + qlen + 1)) { + return 0; + } + + /* + * Private key already contains the modulus bit length, from which + * we can infer the output length. Even if n is NULL, we still had + * to decode p and q to make sure that the product can be computed. + */ + nlen = (sk->n_bitlen + 7) >> 3; + if (n != NULL) { + br_i15_zero(t, p[0]); + br_i15_mulacc(t, p, q); + br_i15_encode(n, nlen, t); + } + return nlen; +} diff --git a/lib/bearssl-esp8266/src/rsa/rsa_i15_oaep_decrypt.c b/lib/bearssl-esp8266/src/rsa/rsa_i15_oaep_decrypt.c new file mode 100644 index 000000000..3ace2a968 --- /dev/null +++ b/lib/bearssl-esp8266/src/rsa/rsa_i15_oaep_decrypt.c @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_rsa.h */ +uint32_t +br_rsa_i15_oaep_decrypt(const br_hash_class *dig, + const void *label, size_t label_len, + const br_rsa_private_key *sk, void *data, size_t *len) +{ + uint32_t r; + + if (*len != ((sk->n_bitlen + 7) >> 3)) { + return 0; + } + r = br_rsa_i15_private(data, sk); + r &= br_rsa_oaep_unpad(dig, label, label_len, data, len); + return r; +} diff --git a/lib/bearssl-esp8266/src/rsa/rsa_i15_oaep_encrypt.c b/lib/bearssl-esp8266/src/rsa/rsa_i15_oaep_encrypt.c new file mode 100644 index 000000000..76a1d9d62 --- /dev/null +++ b/lib/bearssl-esp8266/src/rsa/rsa_i15_oaep_encrypt.c @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_rsa.h */ +size_t +br_rsa_i15_oaep_encrypt( + const br_prng_class **rnd, const br_hash_class *dig, + const void *label, size_t label_len, + const br_rsa_public_key *pk, + void *dst, size_t dst_max_len, + const void *src, size_t src_len) +{ + size_t dlen; + + dlen = br_rsa_oaep_pad(rnd, dig, label, label_len, + pk, dst, dst_max_len, src, src_len); + if (dlen == 0) { + return 0; + } + return dlen & -(size_t)br_rsa_i15_public(dst, dlen, pk); +} diff --git a/lib/bearssl-esp8266/src/rsa/rsa_i15_pkcs1_sign.c b/lib/bearssl-esp8266/src/rsa/rsa_i15_pkcs1_sign.c new file mode 100644 index 000000000..a77f56feb --- /dev/null +++ b/lib/bearssl-esp8266/src/rsa/rsa_i15_pkcs1_sign.c @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_rsa.h */ +uint32_t +br_rsa_i15_pkcs1_sign(const unsigned char *hash_oid, + const unsigned char *hash, size_t hash_len, + const br_rsa_private_key *sk, unsigned char *x) +{ + if (!br_rsa_pkcs1_sig_pad(hash_oid, hash, hash_len, sk->n_bitlen, x)) { + return 0; + } + return br_rsa_i15_private(x, sk); +} diff --git a/lib/bearssl-esp8266/src/rsa/rsa_i15_pkcs1_vrfy.c b/lib/bearssl-esp8266/src/rsa/rsa_i15_pkcs1_vrfy.c new file mode 100644 index 000000000..78e76632c --- /dev/null +++ b/lib/bearssl-esp8266/src/rsa/rsa_i15_pkcs1_vrfy.c @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_rsa.h */ +uint32_t +br_rsa_i15_pkcs1_vrfy(const unsigned char *x, size_t xlen, + const unsigned char *hash_oid, size_t hash_len, + const br_rsa_public_key *pk, unsigned char *hash_out) +{ + unsigned char sig[BR_MAX_RSA_SIZE >> 3]; + + if (xlen > (sizeof sig)) { + return 0; + } + memcpy(sig, x, xlen); + if (!br_rsa_i15_public(sig, xlen, pk)) { + return 0; + } + return br_rsa_pkcs1_sig_unpad(sig, xlen, hash_oid, hash_len, hash_out); +} diff --git a/lib/bearssl-esp8266/src/rsa/rsa_i15_priv.c b/lib/bearssl-esp8266/src/rsa/rsa_i15_priv.c new file mode 100644 index 000000000..6c09a427c --- /dev/null +++ b/lib/bearssl-esp8266/src/rsa/rsa_i15_priv.c @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +#define U (2 + ((BR_MAX_RSA_FACTOR + 14) / 15)) +#define TLEN (8 * U) + +/* see bearssl_rsa.h */ +uint32_t +br_rsa_i15_private(unsigned char *x, const br_rsa_private_key *sk) +{ + const unsigned char *p, *q; + size_t plen, qlen; + size_t fwlen; + uint16_t p0i, q0i; + size_t xlen, u; + uint16_t tmp[1 + TLEN]; + long z; + uint16_t *mp, *mq, *s1, *s2, *t1, *t2, *t3; + uint32_t r; + + /* + * Compute the actual lengths of p and q, in bytes. + * These lengths are not considered secret (we cannot really hide + * them anyway in constant-time code). + */ + p = sk->p; + plen = sk->plen; + while (plen > 0 && *p == 0) { + p ++; + plen --; + } + q = sk->q; + qlen = sk->qlen; + while (qlen > 0 && *q == 0) { + q ++; + qlen --; + } + + /* + * Compute the maximum factor length, in words. + */ + z = (long)(plen > qlen ? plen : qlen) << 3; + fwlen = 1; + while (z > 0) { + z -= 15; + fwlen ++; + } + /* + * Round up the word length to an even number. + */ + fwlen += (fwlen & 1); + + /* + * We need to fit at least 6 values in the stack buffer. + */ + if (6 * fwlen > TLEN) { + return 0; + } + + /* + * Compute signature length (in bytes). + */ + xlen = (sk->n_bitlen + 7) >> 3; + + /* + * Ensure 32-bit alignment for value words. + */ + mq = tmp; + if (((uintptr_t)mq & 2) == 0) { + mq ++; + } + + /* + * Decode q. + */ + br_i15_decode(mq, q, qlen); + + /* + * Decode p. + */ + t1 = mq + fwlen; + br_i15_decode(t1, p, plen); + + /* + * Compute the modulus (product of the two factors), to compare + * it with the source value. We use br_i15_mulacc(), since it's + * already used later on. + */ + t2 = mq + 2 * fwlen; + br_i15_zero(t2, mq[0]); + br_i15_mulacc(t2, mq, t1); + + /* + * We encode the modulus into bytes, to perform the comparison + * with bytes. We know that the product length, in bytes, is + * exactly xlen. + * The comparison actually computes the carry when subtracting + * the modulus from the source value; that carry must be 1 for + * a value in the correct range. We keep it in r, which is our + * accumulator for the error code. + */ + t3 = mq + 4 * fwlen; + br_i15_encode(t3, xlen, t2); + u = xlen; + r = 0; + while (u > 0) { + uint32_t wn, wx; + + u --; + wn = ((unsigned char *)t3)[u]; + wx = x[u]; + r = ((wx - (wn + r)) >> 8) & 1; + } + + /* + * Move the decoded p to another temporary buffer. + */ + mp = mq + 2 * fwlen; + memmove(mp, t1, fwlen * sizeof *t1); + + optimistic_yield(10000); + + /* + * Compute s2 = x^dq mod q. + */ + q0i = br_i15_ninv15(mq[1]); + s2 = mq + fwlen; + br_i15_decode_reduce(s2, x, xlen, mq); + r &= br_i15_modpow_opt(s2, sk->dq, sk->dqlen, mq, q0i, + mq + 3 * fwlen, TLEN - 3 * fwlen); + + optimistic_yield(10000); + + /* + * Compute s1 = x^dq mod q. + */ + p0i = br_i15_ninv15(mp[1]); + s1 = mq + 3 * fwlen; + br_i15_decode_reduce(s1, x, xlen, mp); + r &= br_i15_modpow_opt(s1, sk->dp, sk->dplen, mp, p0i, + mq + 4 * fwlen, TLEN - 4 * fwlen); + + /* + * Compute: + * h = (s1 - s2)*(1/q) mod p + * s1 is an integer modulo p, but s2 is modulo q. PKCS#1 is + * unclear about whether p may be lower than q (some existing, + * widely deployed implementations of RSA don't tolerate p < q), + * but we want to support that occurrence, so we need to use the + * reduction function. + * + * Since we use br_i15_decode_reduce() for iq (purportedly, the + * inverse of q modulo p), we also tolerate improperly large + * values for this parameter. + */ + t1 = mq + 4 * fwlen; + t2 = mq + 5 * fwlen; + br_i15_reduce(t2, s2, mp); + br_i15_add(s1, mp, br_i15_sub(s1, t2, 1)); + br_i15_to_monty(s1, mp); + br_i15_decode_reduce(t1, sk->iq, sk->iqlen, mp); + br_i15_montymul(t2, s1, t1, mp, p0i); + + optimistic_yield(10000); + + /* + * h is now in t2. We compute the final result: + * s = s2 + q*h + * All these operations are non-modular. + * + * We need mq, s2 and t2. We use the t3 buffer as destination. + * The buffers mp, s1 and t1 are no longer needed, so we can + * reuse them for t3. Moreover, the first step of the computation + * is to copy s2 into t3, after which s2 is not needed. Right + * now, mq is in slot 0, s2 is in slot 1, and t2 in slot 5. + * Therefore, we have ample room for t3 by simply using s2. + */ + t3 = s2; + br_i15_mulacc(t3, mq, t2); + + /* + * Encode the result. Since we already checked the value of xlen, + * we can just use it right away. + */ + br_i15_encode(x, xlen, t3); + + /* + * The only error conditions remaining at that point are invalid + * values for p and q (even integers). + */ + return p0i & q0i & r; +} diff --git a/lib/bearssl-esp8266/src/rsa/rsa_i15_privexp.c b/lib/bearssl-esp8266/src/rsa/rsa_i15_privexp.c new file mode 100644 index 000000000..a7a98f1ed --- /dev/null +++ b/lib/bearssl-esp8266/src/rsa/rsa_i15_privexp.c @@ -0,0 +1,320 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_rsa.h */ +size_t +br_rsa_i15_compute_privexp(void *d, + const br_rsa_private_key *sk, uint32_t e) +{ + /* + * We want to invert e modulo phi = (p-1)(q-1). This first + * requires computing phi, which is easy since we have the factors + * p and q in the private key structure. + * + * Since p = 3 mod 4 and q = 3 mod 4, phi/4 is an odd integer. + * We could invert e modulo phi/4 then patch the result to + * modulo phi, but this would involve assembling three modulus-wide + * values (phi/4, 1 and e) and calling moddiv, that requires + * three more temporaries, for a total of six big integers, or + * slightly more than 3 kB of stack space for RSA-4096. This + * exceeds our stack requirements. + * + * Instead, we first use one step of the extended GCD: + * + * - We compute phi = k*e + r (Euclidean division of phi by e). + * If public exponent e is correct, then r != 0 (e must be + * invertible modulo phi). We also have k != 0 since we + * enforce non-ridiculously-small factors. + * + * - We find small u, v such that u*e - v*r = 1 (using a + * binary GCD; we can arrange for u < r and v < e, i.e. all + * values fit on 32 bits). + * + * - Solution is: d = u + v*k + * This last computation is exact: since u < r and v < e, + * the above implies d < r + e*((phi-r)/e) = phi + */ + + uint16_t tmp[4 * ((BR_MAX_RSA_FACTOR + 14) / 15) + 12]; + uint16_t *p, *q, *k, *m, *z, *phi; + const unsigned char *pbuf, *qbuf; + size_t plen, qlen, u, len, dlen; + uint32_t r, a, b, u0, v0, u1, v1, he, hr; + int i; + + /* + * Check that e is correct. + */ + if (e < 3 || (e & 1) == 0) { + return 0; + } + + /* + * Check lengths of p and q, and that they are both odd. + */ + pbuf = sk->p; + plen = sk->plen; + while (plen > 0 && *pbuf == 0) { + pbuf ++; + plen --; + } + if (plen < 5 || plen > (BR_MAX_RSA_FACTOR / 8) + || (pbuf[plen - 1] & 1) != 1) + { + return 0; + } + qbuf = sk->q; + qlen = sk->qlen; + while (qlen > 0 && *qbuf == 0) { + qbuf ++; + qlen --; + } + if (qlen < 5 || qlen > (BR_MAX_RSA_FACTOR / 8) + || (qbuf[qlen - 1] & 1) != 1) + { + return 0; + } + + /* + * Output length is that of the modulus. + */ + dlen = (sk->n_bitlen + 7) >> 3; + if (d == NULL) { + return dlen; + } + + p = tmp; + br_i15_decode(p, pbuf, plen); + plen = (p[0] + 15) >> 4; + q = p + 1 + plen; + br_i15_decode(q, qbuf, qlen); + qlen = (q[0] + 15) >> 4; + + /* + * Compute phi = (p-1)*(q-1), then move it over p-1 and q-1 (that + * we do not need anymore). The mulacc function sets the announced + * bit length of t to be the sum of the announced bit lengths of + * p-1 and q-1, which is usually exact but may overshoot by one 1 + * bit in some cases; we readjust it to its true length. + */ + p[1] --; + q[1] --; + phi = q + 1 + qlen; + br_i15_zero(phi, p[0]); + br_i15_mulacc(phi, p, q); + len = (phi[0] + 15) >> 4; + memmove(tmp, phi, (1 + len) * sizeof *phi); + phi = tmp; + phi[0] = br_i15_bit_length(phi + 1, len); + len = (phi[0] + 15) >> 4; + + /* + * Divide phi by public exponent e. The final remainder r must be + * non-zero (otherwise, the key is invalid). The quotient is k, + * which we write over phi, since we don't need phi after that. + */ + r = 0; + for (u = len; u >= 1; u --) { + /* + * Upon entry, r < e, and phi[u] < 2^15; hence, + * hi:lo < e*2^15. Thus, the produced word k[u] + * must be lower than 2^15, and the new remainder r + * is lower than e. + */ + uint32_t hi, lo; + + hi = r >> 17; + lo = (r << 15) + phi[u]; + phi[u] = br_divrem(hi, lo, e, &r); + } + if (r == 0) { + return 0; + } + k = phi; + + /* + * Compute u and v such that u*e - v*r = GCD(e,r). We use + * a binary GCD algorithm, with 6 extra integers a, b, + * u0, u1, v0 and v1. Initial values are: + * a = e u0 = 1 v0 = 0 + * b = r u1 = r v1 = e-1 + * The following invariants are maintained: + * a = u0*e - v0*r + * b = u1*e - v1*r + * 0 < a <= e + * 0 < b <= r + * 0 <= u0 <= r + * 0 <= v0 <= e + * 0 <= u1 <= r + * 0 <= v1 <= e + * + * At each iteration, we reduce either a or b by one bit, and + * adjust u0, u1, v0 and v1 to maintain the invariants: + * - if a is even, then a <- a/2 + * - otherwise, if b is even, then b <- b/2 + * - otherwise, if a > b, then a <- (a-b)/2 + * - otherwise, if b > a, then b <- (b-a)/2 + * Algorithm stops when a = b. At that point, the common value + * is the GCD of e and r; it must be 1 (otherwise, the private + * key or public exponent is not valid). The (u0,v0) or (u1,v1) + * pairs are the solution we are looking for. + * + * Since either a or b is reduced by at least 1 bit at each + * iteration, 62 iterations are enough to reach the end + * condition. + * + * To maintain the invariants, we must compute the same operations + * on the u* and v* values that we do on a and b: + * - When a is divided by 2, u0 and v0 must be divided by 2. + * - When b is divided by 2, u1 and v1 must be divided by 2. + * - When b is subtracted from a, u1 and v1 are subtracted from + * u0 and v0, respectively. + * - When a is subtracted from b, u0 and v0 are subtracted from + * u1 and v1, respectively. + * + * However, we want to keep the u* and v* values in their proper + * ranges. The following remarks apply: + * + * - When a is divided by 2, then a is even. Therefore: + * + * * If r is odd, then u0 and v0 must have the same parity; + * if they are both odd, then adding r to u0 and e to v0 + * makes them both even, and the division by 2 brings them + * back to the proper range. + * + * * If r is even, then u0 must be even; if v0 is odd, then + * adding r to u0 and e to v0 makes them both even, and the + * division by 2 brings them back to the proper range. + * + * Thus, all we need to do is to look at the parity of v0, + * and add (r,e) to (u0,v0) when v0 is odd. In order to avoid + * a 32-bit overflow, we can add ((r+1)/2,(e/2)+1) after the + * division (r+1 does not overflow since r < e; and (e/2)+1 + * is equal to (e+1)/2 since e is odd). + * + * - When we subtract b from a, three cases may occur: + * + * * u1 <= u0 and v1 <= v0: just do the subtractions + * + * * u1 > u0 and v1 > v0: compute: + * (u0, v0) <- (u0 + r - u1, v0 + e - v1) + * + * * u1 <= u0 and v1 > v0: compute: + * (u0, v0) <- (u0 + r - u1, v0 + e - v1) + * + * The fourth case (u1 > u0 and v1 <= v0) is not possible + * because it would contradict "b < a" (which is the reason + * why we subtract b from a). + * + * The tricky case is the third one: from the equations, it + * seems that u0 may go out of range. However, the invariants + * and ranges of other values imply that, in that case, the + * new u0 does not actually exceed the range. + * + * We can thus handle the subtraction by adding (r,e) based + * solely on the comparison between v0 and v1. + */ + a = e; + b = r; + u0 = 1; + v0 = 0; + u1 = r; + v1 = e - 1; + hr = (r + 1) >> 1; + he = (e >> 1) + 1; + for (i = 0; i < 62; i ++) { + uint32_t oa, ob, agtb, bgta; + uint32_t sab, sba, da, db; + uint32_t ctl; + + oa = a & 1; /* 1 if a is odd */ + ob = b & 1; /* 1 if b is odd */ + agtb = GT(a, b); /* 1 if a > b */ + bgta = GT(b, a); /* 1 if b > a */ + + sab = oa & ob & agtb; /* 1 if a <- a-b */ + sba = oa & ob & bgta; /* 1 if b <- b-a */ + + /* a <- a-b, u0 <- u0-u1, v0 <- v0-v1 */ + ctl = GT(v1, v0); + a -= b & -sab; + u0 -= (u1 - (r & -ctl)) & -sab; + v0 -= (v1 - (e & -ctl)) & -sab; + + /* b <- b-a, u1 <- u1-u0 mod r, v1 <- v1-v0 mod e */ + ctl = GT(v0, v1); + b -= a & -sba; + u1 -= (u0 - (r & -ctl)) & -sba; + v1 -= (v0 - (e & -ctl)) & -sba; + + da = NOT(oa) | sab; /* 1 if a <- a/2 */ + db = (oa & NOT(ob)) | sba; /* 1 if b <- b/2 */ + + /* a <- a/2, u0 <- u0/2, v0 <- v0/2 */ + ctl = v0 & 1; + a ^= (a ^ (a >> 1)) & -da; + u0 ^= (u0 ^ ((u0 >> 1) + (hr & -ctl))) & -da; + v0 ^= (v0 ^ ((v0 >> 1) + (he & -ctl))) & -da; + + /* b <- b/2, u1 <- u1/2 mod r, v1 <- v1/2 mod e */ + ctl = v1 & 1; + b ^= (b ^ (b >> 1)) & -db; + u1 ^= (u1 ^ ((u1 >> 1) + (hr & -ctl))) & -db; + v1 ^= (v1 ^ ((v1 >> 1) + (he & -ctl))) & -db; + } + + /* + * Check that the GCD is indeed 1. If not, then the key is invalid + * (and there's no harm in leaking that piece of information). + */ + if (a != 1) { + return 0; + } + + /* + * Now we have u0*e - v0*r = 1. Let's compute the result as: + * d = u0 + v0*k + * We still have k in the tmp[] array, and its announced bit + * length is that of phi. + */ + m = k + 1 + len; + m[0] = (2 << 4) + 2; /* bit length is 32 bits, encoded */ + m[1] = v0 & 0x7FFF; + m[2] = (v0 >> 15) & 0x7FFF; + m[3] = v0 >> 30; + z = m + 4; + br_i15_zero(z, k[0]); + z[1] = u0 & 0x7FFF; + z[2] = (u0 >> 15) & 0x7FFF; + z[3] = u0 >> 30; + br_i15_mulacc(z, k, m); + + /* + * Encode the result. + */ + br_i15_encode(d, dlen, z); + return dlen; +} diff --git a/lib/bearssl-esp8266/src/rsa/rsa_i15_pss_sign.c b/lib/bearssl-esp8266/src/rsa/rsa_i15_pss_sign.c new file mode 100644 index 000000000..09664e30e --- /dev/null +++ b/lib/bearssl-esp8266/src/rsa/rsa_i15_pss_sign.c @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_rsa.h */ +uint32_t +br_rsa_i15_pss_sign(const br_prng_class **rng, + const br_hash_class *hf_data, const br_hash_class *hf_mgf1, + const unsigned char *hash, size_t salt_len, + const br_rsa_private_key *sk, unsigned char *x) +{ + if (!br_rsa_pss_sig_pad(rng, hf_data, hf_mgf1, hash, + salt_len, sk->n_bitlen, x)) + { + return 0; + } + return br_rsa_i15_private(x, sk); +} diff --git a/lib/bearssl-esp8266/src/rsa/rsa_i15_pss_vrfy.c b/lib/bearssl-esp8266/src/rsa/rsa_i15_pss_vrfy.c new file mode 100644 index 000000000..de5d70195 --- /dev/null +++ b/lib/bearssl-esp8266/src/rsa/rsa_i15_pss_vrfy.c @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_rsa.h */ +uint32_t +br_rsa_i15_pss_vrfy(const unsigned char *x, size_t xlen, + const br_hash_class *hf_data, const br_hash_class *hf_mgf1, + const void *hash, size_t salt_len, const br_rsa_public_key *pk) +{ + unsigned char sig[BR_MAX_RSA_SIZE >> 3]; + + if (xlen > (sizeof sig)) { + return 0; + } + memcpy(sig, x, xlen); + if (!br_rsa_i15_public(sig, xlen, pk)) { + return 0; + } + return br_rsa_pss_sig_unpad(hf_data, hf_mgf1, + hash, salt_len, pk, sig); +} diff --git a/lib/bearssl-esp8266/src/rsa/rsa_i15_pub.c b/lib/bearssl-esp8266/src/rsa/rsa_i15_pub.c new file mode 100644 index 000000000..2fa78312b --- /dev/null +++ b/lib/bearssl-esp8266/src/rsa/rsa_i15_pub.c @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * As a strict minimum, we need four buffers that can hold a + * modular integer. + */ +#define TLEN (4 * (2 + ((BR_MAX_RSA_SIZE + 14) / 15))) + +/* see bearssl_rsa.h */ +uint32_t +br_rsa_i15_public(unsigned char *x, size_t xlen, + const br_rsa_public_key *pk) +{ + const unsigned char *n; + size_t nlen; + uint16_t tmp[1 + TLEN]; + uint16_t *m, *a, *t; + size_t fwlen; + long z; + uint16_t m0i; + uint32_t r; + + /* + * Get the actual length of the modulus, and see if it fits within + * our stack buffer. We also check that the length of x[] is valid. + */ + n = pk->n; + nlen = pk->nlen; + while (nlen > 0 && pgm_read_byte(n) == 0) { + n ++; + nlen --; + } + if (nlen == 0 || nlen > (BR_MAX_RSA_SIZE >> 3) || xlen != nlen) { + return 0; + } + z = (long)nlen << 3; + fwlen = 1; + while (z > 0) { + z -= 15; + fwlen ++; + } + /* + * Round up length to an even number. + */ + fwlen += (fwlen & 1); + + /* + * The modulus gets decoded into m[]. + * The value to exponentiate goes into a[]. + * The temporaries for modular exponentiations are in t[]. + * + * We want the first value word of each integer to be aligned + * on a 32-bit boundary. + */ + m = tmp; + if (((uintptr_t)m & 2) == 0) { + m ++; + } + a = m + fwlen; + t = m + 2 * fwlen; + + /* + * Decode the modulus. + */ + br_i15_decode(m, n, nlen); + m0i = br_i15_ninv15(m[1]); + + /* + * Note: if m[] is even, then m0i == 0. Otherwise, m0i must be + * an odd integer. + */ + r = m0i & 1; + + /* + * Decode x[] into a[]; we also check that its value is proper. + */ + r &= br_i15_decode_mod(a, x, xlen, m); + + /* + * Compute the modular exponentiation. + */ + br_i15_modpow_opt(a, pk->e, pk->elen, m, m0i, t, TLEN - 2 * fwlen); + + /* + * Encode the result. + */ + br_i15_encode(x, xlen, a); + return r; +} diff --git a/lib/bearssl-esp8266/src/rsa/rsa_i15_pubexp.c b/lib/bearssl-esp8266/src/rsa/rsa_i15_pubexp.c new file mode 100644 index 000000000..b17299656 --- /dev/null +++ b/lib/bearssl-esp8266/src/rsa/rsa_i15_pubexp.c @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * Recompute public exponent, based on factor p and reduced private + * exponent dp. + */ +static uint32_t +get_pubexp(const unsigned char *pbuf, size_t plen, + const unsigned char *dpbuf, size_t dplen) +{ + /* + * dp is the inverse of e modulo p-1. If p = 3 mod 4, then + * p-1 = 2*((p-1)/2). Taken modulo 2, e is odd and has inverse 1; + * thus, dp must be odd. + * + * We compute the inverse of dp modulo (p-1)/2. This requires + * first reducing dp modulo (p-1)/2 (this can be done with a + * conditional subtract, no need to use the generic modular + * reduction function); then, we use moddiv. + */ + + uint16_t tmp[6 * ((BR_MAX_RSA_FACTOR + 29) / 15)]; + uint16_t *p, *dp, *x; + size_t len; + uint32_t e; + + /* + * Compute actual factor length (in bytes) and check that it fits + * under our size constraints. + */ + while (plen > 0 && *pbuf == 0) { + pbuf ++; + plen --; + } + if (plen == 0 || plen < 5 || plen > (BR_MAX_RSA_FACTOR / 8)) { + return 0; + } + + /* + * Compute actual reduced exponent length (in bytes) and check that + * it is not longer than p. + */ + while (dplen > 0 && *dpbuf == 0) { + dpbuf ++; + dplen --; + } + if (dplen > plen || dplen == 0 + || (dplen == plen && dpbuf[0] > pbuf[0])) + { + return 0; + } + + /* + * Verify that p = 3 mod 4 and that dp is odd. + */ + if ((pbuf[plen - 1] & 3) != 3 || (dpbuf[dplen - 1] & 1) != 1) { + return 0; + } + + /* + * Decode p and compute (p-1)/2. + */ + p = tmp; + br_i15_decode(p, pbuf, plen); + len = (p[0] + 31) >> 4; + br_i15_rshift(p, 1); + + /* + * Decode dp and make sure its announced bit length matches that of + * p (we already know that the size of dp, in bits, does not exceed + * the size of p, so we just have to copy the header word). + */ + dp = p + len; + memset(dp, 0, len * sizeof *dp); + br_i15_decode(dp, dpbuf, dplen); + dp[0] = p[0]; + + /* + * Subtract (p-1)/2 from dp if necessary. + */ + br_i15_sub(dp, p, NOT(br_i15_sub(dp, p, 0))); + + /* + * If another subtraction is needed, then this means that the + * value was invalid. We don't care to leak information about + * invalid keys. + */ + if (br_i15_sub(dp, p, 0) == 0) { + return 0; + } + + /* + * Invert dp modulo (p-1)/2. If the inversion fails, then the + * key value was invalid. + */ + x = dp + len; + br_i15_zero(x, p[0]); + x[1] = 1; + if (br_i15_moddiv(x, dp, p, br_i15_ninv15(p[1]), x + len) == 0) { + return 0; + } + + /* + * We now have an inverse. We must set it to zero (error) if its + * length is greater than 32 bits and/or if it is an even integer. + * Take care that the bit_length function returns an encoded + * bit length. + */ + e = (uint32_t)x[1] | ((uint32_t)x[2] << 15) | ((uint32_t)x[3] << 30); + e &= -LT(br_i15_bit_length(x + 1, len - 1), 35); + e &= -(e & 1); + return e; +} + +/* see bearssl_rsa.h */ +uint32_t +br_rsa_i15_compute_pubexp(const br_rsa_private_key *sk) +{ + /* + * Get the public exponent from both p and q. This is the right + * exponent if we get twice the same value. + */ + uint32_t ep, eq; + + ep = get_pubexp(sk->p, sk->plen, sk->dp, sk->dplen); + eq = get_pubexp(sk->q, sk->qlen, sk->dq, sk->dqlen); + return ep & -EQ(ep, eq); +} diff --git a/lib/bearssl-esp8266/src/rsa/rsa_oaep_pad.c b/lib/bearssl-esp8266/src/rsa/rsa_oaep_pad.c new file mode 100644 index 000000000..57fd9d78d --- /dev/null +++ b/lib/bearssl-esp8266/src/rsa/rsa_oaep_pad.c @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * Hash some data. This is put as a separate function so that stack + * allocation of the hash function context is done only for the duration + * of the hash. + */ +static void +hash_data(const br_hash_class *dig, void *dst, const void *src, size_t len) +{ + br_hash_compat_context hc; + + hc.vtable = dig; + dig->init(&hc.vtable); + dig->update(&hc.vtable, src, len); + dig->out(&hc.vtable, dst); +} + +/* see inner.h */ +size_t +br_rsa_oaep_pad(const br_prng_class **rnd, const br_hash_class *dig, + const void *label, size_t label_len, + const br_rsa_public_key *pk, + void *dst, size_t dst_max_len, + const void *src, size_t src_len) +{ + size_t k, hlen; + unsigned char *buf; + + hlen = br_digest_size(dig); + + /* + * Compute actual modulus length (in bytes). + */ + k = pk->nlen; + while (k > 0 && pk->n[k - 1] == 0) { + k --; + } + + /* + * An error is reported if: + * - the modulus is too short; + * - the source message length is too long; + * - the destination buffer is too short. + */ + if (k < ((hlen << 1) + 2) + || src_len > (k - (hlen << 1) - 2) + || dst_max_len < k) + { + return 0; + } + + /* + * Apply padding. At this point, things cannot fail. + */ + buf = dst; + + /* + * Assemble: DB = lHash || PS || 0x01 || M + * We first place the source message M with memmove(), so that + * overlaps between source and destination buffers are supported. + */ + memmove(buf + k - src_len, src, src_len); + hash_data(dig, buf + 1 + hlen, label, label_len); + memset(buf + 1 + (hlen << 1), 0, k - src_len - (hlen << 1) - 2); + buf[k - src_len - 1] = 0x01; + + /* + * Make the random seed. + */ + (*rnd)->generate(rnd, buf + 1, hlen); + + /* + * Mask DB with the mask generated from the seed. + */ + br_mgf1_xor(buf + 1 + hlen, k - hlen - 1, dig, buf + 1, hlen); + + /* + * Mask the seed with the mask generated from the masked DB. + */ + br_mgf1_xor(buf + 1, hlen, dig, buf + 1 + hlen, k - hlen - 1); + + /* + * Padding result: EM = 0x00 || maskedSeed || maskedDB. + */ + buf[0] = 0x00; + return k; +} diff --git a/lib/bearssl-esp8266/src/rsa/rsa_oaep_unpad.c b/lib/bearssl-esp8266/src/rsa/rsa_oaep_unpad.c new file mode 100644 index 000000000..a6558dccc --- /dev/null +++ b/lib/bearssl-esp8266/src/rsa/rsa_oaep_unpad.c @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * Hash some data and XOR the result into the provided buffer. This is put + * as a separate function so that stack allocation of the hash function + * context is done only for the duration of the hash. + */ +static void +xor_hash_data(const br_hash_class *dig, void *dst, const void *src, size_t len) +{ + br_hash_compat_context hc; + unsigned char tmp[64]; + unsigned char *buf; + size_t u, hlen; + + hc.vtable = dig; + dig->init(&hc.vtable); + dig->update(&hc.vtable, src, len); + dig->out(&hc.vtable, tmp); + buf = dst; + hlen = br_digest_size(dig); + for (u = 0; u < hlen; u ++) { + buf[u] ^= tmp[u]; + } +} + +/* see inner.h */ +uint32_t +br_rsa_oaep_unpad(const br_hash_class *dig, + const void *label, size_t label_len, + void *data, size_t *len) +{ + size_t u, k, hlen; + unsigned char *buf; + uint32_t r, s, zlen; + + hlen = br_digest_size(dig); + k = *len; + buf = data; + + /* + * There must be room for the padding. + */ + if (k < ((hlen << 1) + 2)) { + return 0; + } + + /* + * Unmask the seed, then the DB value. + */ + br_mgf1_xor(buf + 1, hlen, dig, buf + 1 + hlen, k - hlen - 1); + br_mgf1_xor(buf + 1 + hlen, k - hlen - 1, dig, buf + 1, hlen); + + /* + * Hash the label and XOR it with the value in the array; if + * they are equal then these should yield only zeros. + */ + xor_hash_data(dig, buf + 1 + hlen, label, label_len); + + /* + * At that point, if the padding was correct, when we should + * have: 0x00 || seed || 0x00 ... 0x00 0x01 || M + * Padding is valid as long as: + * - There is at least hlen+1 leading bytes of value 0x00. + * - There is at least one non-zero byte. + * - The first (leftmost) non-zero byte has value 0x01. + * + * Ultimately, we may leak the resulting message length, i.e. + * the position of the byte of value 0x01, but we must take care + * to do so only if the number of zero bytes has been verified + * to be at least hlen+1. + * + * The loop below counts the number of bytes of value 0x00, and + * checks that the next byte has value 0x01, in constant-time. + * + * - If the initial byte (before the seed) is not 0x00, then + * r and s are set to 0, and stay there. + * - Value r is 1 until the first non-zero byte is reached + * (after the seed); it switches to 0 at that point. + * - Value s is set to 1 if and only if the data encountered + * at the time of the transition of r from 1 to 0 has value + * exactly 0x01. + * - Value zlen counts the number of leading bytes of value zero + * (after the seed). + */ + r = 1 - ((buf[0] + 0xFF) >> 8); + s = 0; + zlen = 0; + for (u = hlen + 1; u < k; u ++) { + uint32_t w, nz; + + w = buf[u]; + + /* + * nz == 1 only for the first non-zero byte. + */ + nz = r & ((w + 0xFF) >> 8); + s |= nz & EQ(w, 0x01); + r &= NOT(nz); + zlen += r; + } + + /* + * Padding is correct only if s == 1, _and_ zlen >= hlen. + */ + s &= GE(zlen, (uint32_t)hlen); + + /* + * At that point, padding was verified, and we are now allowed + * to make conditional jumps. + */ + if (s) { + size_t plen; + + plen = 2 + hlen + zlen; + k -= plen; + memmove(buf, buf + plen, k); + *len = k; + } + return s; +} diff --git a/lib/bearssl-esp8266/src/rsa/rsa_pkcs1_sig_pad.c b/lib/bearssl-esp8266/src/rsa/rsa_pkcs1_sig_pad.c new file mode 100644 index 000000000..6150be550 --- /dev/null +++ b/lib/bearssl-esp8266/src/rsa/rsa_pkcs1_sig_pad.c @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +uint32_t +br_rsa_pkcs1_sig_pad(const unsigned char *hash_oid, + const unsigned char *hash, size_t hash_len, + uint32_t n_bitlen, unsigned char *x) +{ + size_t u, x3, xlen; + + /* + * Padded hash value has format: + * 00 01 FF .. FF 00 30 x1 30 x2 06 x3 OID 05 00 04 x4 HASH + * + * with the following rules: + * + * -- Total length is equal to the modulus length (unsigned + * encoding). + * + * -- There must be at least eight bytes of value 0xFF. + * + * -- x4 is equal to the hash length (hash_len). + * + * -- x3 is equal to the encoded OID value length (hash_oid[0]). + * + * -- x2 = x3 + 4. + * + * -- x1 = x2 + x4 + 4 = x3 + x4 + 8. + * + * Note: the "05 00" is optional (signatures with and without + * that sequence exist in practice), but notes in PKCS#1 seem to + * indicate that the presence of that sequence (specifically, + * an ASN.1 NULL value for the hash parameters) may be slightly + * more "standard" than the opposite. + */ + xlen = (n_bitlen + 7) >> 3; + + if (hash_oid == NULL) { + if (xlen < hash_len + 11) { + return 0; + } + x[0] = 0x00; + x[1] = 0x01; + u = xlen - hash_len; + memset(x + 2, 0xFF, u - 3); + x[u - 1] = 0x00; + } else { + x3 = hash_oid[0]; + + /* + * Check that there is enough room for all the elements, + * including at least eight bytes of value 0xFF. + */ + if (xlen < (x3 + hash_len + 21)) { + return 0; + } + x[0] = 0x00; + x[1] = 0x01; + u = xlen - x3 - hash_len - 11; + memset(x + 2, 0xFF, u - 2); + x[u] = 0x00; + x[u + 1] = 0x30; + x[u + 2] = x3 + hash_len + 8; + x[u + 3] = 0x30; + x[u + 4] = x3 + 4; + x[u + 5] = 0x06; + memcpy(x + u + 6, hash_oid, x3 + 1); + u += x3 + 7; + x[u ++] = 0x05; + x[u ++] = 0x00; + x[u ++] = 0x04; + x[u ++] = hash_len; + } + memcpy(x + u, hash, hash_len); + return 1; +} diff --git a/lib/bearssl-esp8266/src/rsa/rsa_pkcs1_sig_unpad.c b/lib/bearssl-esp8266/src/rsa/rsa_pkcs1_sig_unpad.c new file mode 100644 index 000000000..08a553286 --- /dev/null +++ b/lib/bearssl-esp8266/src/rsa/rsa_pkcs1_sig_unpad.c @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_rsa.h */ +uint32_t +br_rsa_pkcs1_sig_unpad(const unsigned char *sig, size_t sig_len, + const unsigned char *hash_oid, size_t hash_len, + unsigned char *hash_out) +{ + static const unsigned char pad1[] = { + 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF + }; + + unsigned char pad2[43]; + size_t u, x2, x3, pad_len, zlen; + + if (sig_len < 11) { + return 0; + } + + /* + * Expected format: + * 00 01 FF ... FF 00 30 x1 30 x2 06 x3 OID [ 05 00 ] 04 x4 HASH + * + * with the following rules: + * + * -- Total length is that of the modulus and the signature + * (this was already verified by br_rsa_i31_public()). + * + * -- There are at least eight bytes of value 0xFF. + * + * -- x4 is equal to the hash length (hash_len). + * + * -- x3 is equal to the encoded OID value length (so x3 is the + * first byte of hash_oid[]). + * + * -- If the "05 00" is present, then x2 == x3 + 4; otherwise, + * x2 == x3 + 2. + * + * -- x1 == x2 + x4 + 4. + * + * So the total length after the last "FF" is either x3 + x4 + 11 + * (with the "05 00") or x3 + x4 + 9 (without the "05 00"). + */ + + /* + * Check the "00 01 FF .. FF 00" with at least eight 0xFF bytes. + * The comparison is valid because we made sure that the signature + * is at least 11 bytes long. + */ + if (memcmp(sig, pad1, sizeof pad1) != 0) { + return 0; + } + for (u = sizeof pad1; u < sig_len; u ++) { + if (sig[u] != 0xFF) { + break; + } + } + + /* + * Remaining length is sig_len - u bytes (including the 00 just + * after the last FF). This must be equal to one of the two + * possible values (depending on whether the "05 00" sequence is + * present or not). + */ + if (hash_oid == NULL) { + if (sig_len - u != hash_len + 1 || sig[u] != 0x00) { + return 0; + } + } else { + x3 = hash_oid[0]; + pad_len = x3 + 9; + memset(pad2, 0, pad_len); + zlen = sig_len - u - hash_len; + if (zlen == pad_len) { + x2 = x3 + 2; + } else if (zlen == pad_len + 2) { + x2 = x3 + 4; + pad_len = zlen; + pad2[pad_len - 4] = 0x05; + } else { + return 0; + } + pad2[1] = 0x30; + pad2[2] = x2 + hash_len + 4; + pad2[3] = 0x30; + pad2[4] = x2; + pad2[5] = 0x06; + memcpy(pad2 + 6, hash_oid, x3 + 1); + pad2[pad_len - 2] = 0x04; + pad2[pad_len - 1] = hash_len; + if (memcmp(pad2, sig + u, pad_len) != 0) { + return 0; + } + } + memcpy(hash_out, sig + sig_len - hash_len, hash_len); + return 1; +} diff --git a/lib/bearssl-esp8266/src/rsa/rsa_pss_sig_pad.c b/lib/bearssl-esp8266/src/rsa/rsa_pss_sig_pad.c new file mode 100644 index 000000000..c65def305 --- /dev/null +++ b/lib/bearssl-esp8266/src/rsa/rsa_pss_sig_pad.c @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +uint32_t +br_rsa_pss_sig_pad(const br_prng_class **rng, + const br_hash_class *hf_data, const br_hash_class *hf_mgf1, + const unsigned char *hash, size_t salt_len, + uint32_t n_bitlen, unsigned char *x) +{ + size_t xlen, hash_len; + br_hash_compat_context hc; + unsigned char *salt, *seed; + + hash_len = br_digest_size(hf_data); + + /* + * The padded string is one bit smaller than the modulus; + * notably, if the modulus length is equal to 1 modulo 8, then + * the padded string will be one _byte_ smaller, and the first + * byte will be set to 0. We apply these transformations here. + */ + n_bitlen --; + if ((n_bitlen & 7) == 0) { + *x ++ = 0; + } + xlen = (n_bitlen + 7) >> 3; + + /* + * Check that the modulus is large enough for the hash value + * length combined with the intended salt length. + */ + if (hash_len > xlen || salt_len > xlen + || (hash_len + salt_len + 2) > xlen) + { + return 0; + } + + /* + * Produce a random salt. + */ + salt = x + xlen - hash_len - salt_len - 1; + if (salt_len != 0) { + (*rng)->generate(rng, salt, salt_len); + } + + /* + * Compute the seed for MGF1. + */ + seed = x + xlen - hash_len - 1; + hf_data->init(&hc.vtable); + memset(seed, 0, 8); + hf_data->update(&hc.vtable, seed, 8); + hf_data->update(&hc.vtable, hash, hash_len); + hf_data->update(&hc.vtable, salt, salt_len); + hf_data->out(&hc.vtable, seed); + + /* + * Prepare string PS (padded salt). The salt is already at the + * right place. + */ + memset(x, 0, xlen - salt_len - hash_len - 2); + x[xlen - salt_len - hash_len - 2] = 0x01; + + /* + * Generate the mask and XOR it into PS. + */ + br_mgf1_xor(x, xlen - hash_len - 1, hf_mgf1, seed, hash_len); + + /* + * Clear the top bits to ensure the value is lower than the + * modulus. + */ + x[0] &= 0xFF >> (((uint32_t)xlen << 3) - n_bitlen); + + /* + * The seed (H) is already in the right place. We just set the + * last byte. + */ + x[xlen - 1] = 0xBC; + + return 1; +} diff --git a/lib/bearssl-esp8266/src/rsa/rsa_pss_sig_unpad.c b/lib/bearssl-esp8266/src/rsa/rsa_pss_sig_unpad.c new file mode 100644 index 000000000..50f4db06a --- /dev/null +++ b/lib/bearssl-esp8266/src/rsa/rsa_pss_sig_unpad.c @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +uint32_t +br_rsa_pss_sig_unpad(const br_hash_class *hf_data, + const br_hash_class *hf_mgf1, + const unsigned char *hash, size_t salt_len, + const br_rsa_public_key *pk, unsigned char *x) +{ + size_t u, xlen, hash_len; + br_hash_compat_context hc; + unsigned char *seed, *salt; + unsigned char tmp[64]; + uint32_t r, n_bitlen; + + hash_len = br_digest_size(hf_data); + + /* + * Value r will be set to a non-zero value is any test fails. + */ + r = 0; + + /* + * The value bit length (as an integer) must be strictly less than + * that of the modulus. + */ + for (u = 0; u < pk->nlen; u ++) { + if (pk->n[u] != 0) { + break; + } + } + if (u == pk->nlen) { + return 0; + } + n_bitlen = BIT_LENGTH(pk->n[u]) + ((uint32_t)(pk->nlen - u - 1) << 3); + n_bitlen --; + if ((n_bitlen & 7) == 0) { + r |= *x ++; + } else { + r |= x[0] & (0xFF << (n_bitlen & 7)); + } + xlen = (n_bitlen + 7) >> 3; + + /* + * Check that the modulus is large enough for the hash value + * length combined with the intended salt length. + */ + if (hash_len > xlen || salt_len > xlen + || (hash_len + salt_len + 2) > xlen) + { + return 0; + } + + /* + * Check value of rightmost byte. + */ + r |= x[xlen - 1] ^ 0xBC; + + /* + * Generate the mask and XOR it into the first bytes to reveal PS; + * we must also mask out the leading bits. + */ + seed = x + xlen - hash_len - 1; + br_mgf1_xor(x, xlen - hash_len - 1, hf_mgf1, seed, hash_len); + if ((n_bitlen & 7) != 0) { + x[0] &= 0xFF >> (8 - (n_bitlen & 7)); + } + + /* + * Check that all padding bytes have the expected value. + */ + for (u = 0; u < (xlen - hash_len - salt_len - 2); u ++) { + r |= x[u]; + } + r |= x[xlen - hash_len - salt_len - 2] ^ 0x01; + + /* + * Recompute H. + */ + salt = x + xlen - hash_len - salt_len - 1; + hf_data->init(&hc.vtable); + memset(tmp, 0, 8); + hf_data->update(&hc.vtable, tmp, 8); + hf_data->update(&hc.vtable, hash, hash_len); + hf_data->update(&hc.vtable, salt, salt_len); + hf_data->out(&hc.vtable, tmp); + + /* + * Check that the recomputed H value matches the one appearing + * in the string. + */ + for (u = 0; u < hash_len; u ++) { + r |= tmp[u] ^ x[(xlen - salt_len - 1) + u]; + } + + return EQ0(r); +} diff --git a/lib/bearssl-esp8266/src/rsa/rsa_ssl_decrypt.c b/lib/bearssl-esp8266/src/rsa/rsa_ssl_decrypt.c new file mode 100644 index 000000000..a45fc7b27 --- /dev/null +++ b/lib/bearssl-esp8266/src/rsa/rsa_ssl_decrypt.c @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_rsa.h */ +uint32_t +br_rsa_ssl_decrypt(br_rsa_private core, const br_rsa_private_key *sk, + unsigned char *data, size_t len) +{ + uint32_t x; + size_t u; + + /* + * A first check on length. Since this test works only on the + * buffer length, it needs not (and cannot) be constant-time. + */ + if (len < 59 || len != (sk->n_bitlen + 7) >> 3) { + return 0; + } + x = core(data, sk); + + x &= EQ(data[0], 0x00); + x &= EQ(data[1], 0x02); + for (u = 2; u < (len - 49); u ++) { + x &= NEQ(data[u], 0); + } + x &= EQ(data[len - 49], 0x00); + memmove(data, data + len - 48, 48); + return x; +} diff --git a/lib/bearssl-esp8266/src/settings.c b/lib/bearssl-esp8266/src/settings.c new file mode 100644 index 000000000..18e9991f6 --- /dev/null +++ b/lib/bearssl-esp8266/src/settings.c @@ -0,0 +1,313 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +static const br_config_option config[] = { + { "BR_64", +#if BR_64 + 1 +#else + 0 +#endif + }, + { "BR_AES_X86NI", +#if BR_AES_X86NI + 1 +#else + 0 +#endif + }, + { "BR_amd64", +#if BR_amd64 + 1 +#else + 0 +#endif + }, + { "BR_ARMEL_CORTEXM_GCC", +#if BR_ARMEL_CORTEXM_GCC + 1 +#else + 0 +#endif + }, + { "BR_BE_UNALIGNED", +#if BR_BE_UNALIGNED + 1 +#else + 0 +#endif + }, + { "BR_CLANG", +#if BR_CLANG + 1 +#else + 0 +#endif + }, + { "BR_CLANG_3_7", +#if BR_CLANG_3_7 + 1 +#else + 0 +#endif + }, + { "BR_CLANG_3_8", +#if BR_CLANG_3_8 + 1 +#else + 0 +#endif + }, + { "BR_CT_MUL15", +#if BR_CT_MUL15 + 1 +#else + 0 +#endif + }, + { "BR_CT_MUL31", +#if BR_CT_MUL31 + 1 +#else + 0 +#endif + }, + { "BR_GCC", +#if BR_GCC + 1 +#else + 0 +#endif + }, + { "BR_GCC_4_4", +#if BR_GCC_4_4 + 1 +#else + 0 +#endif + }, + { "BR_GCC_4_5", +#if BR_GCC_4_5 + 1 +#else + 0 +#endif + }, + { "BR_GCC_4_6", +#if BR_GCC_4_6 + 1 +#else + 0 +#endif + }, + { "BR_GCC_4_7", +#if BR_GCC_4_7 + 1 +#else + 0 +#endif + }, + { "BR_GCC_4_8", +#if BR_GCC_4_8 + 1 +#else + 0 +#endif + }, + { "BR_GCC_4_9", +#if BR_GCC_4_9 + 1 +#else + 0 +#endif + }, + { "BR_GCC_5_0", +#if BR_GCC_5_0 + 1 +#else + 0 +#endif + }, + { "BR_i386", +#if BR_i386 + 1 +#else + 0 +#endif + }, + { "BR_INT128", +#if BR_INT128 + 1 +#else + 0 +#endif + }, + { "BR_LE_UNALIGNED", +#if BR_LE_UNALIGNED + 1 +#else + 0 +#endif + }, + { "BR_LOMUL", +#if BR_LOMUL + 1 +#else + 0 +#endif + }, + { "BR_MAX_EC_SIZE", BR_MAX_EC_SIZE }, + { "BR_MAX_RSA_SIZE", BR_MAX_RSA_SIZE }, + { "BR_MAX_RSA_FACTOR", BR_MAX_RSA_FACTOR }, + { "BR_MSC", +#if BR_MSC + 1 +#else + 0 +#endif + }, + { "BR_MSC_2005", +#if BR_MSC_2005 + 1 +#else + 0 +#endif + }, + { "BR_MSC_2008", +#if BR_MSC_2008 + 1 +#else + 0 +#endif + }, + { "BR_MSC_2010", +#if BR_MSC_2010 + 1 +#else + 0 +#endif + }, + { "BR_MSC_2012", +#if BR_MSC_2012 + 1 +#else + 0 +#endif + }, + { "BR_MSC_2013", +#if BR_MSC_2013 + 1 +#else + 0 +#endif + }, + { "BR_MSC_2015", +#if BR_MSC_2015 + 1 +#else + 0 +#endif + }, + { "BR_POWER8", +#if BR_POWER8 + 1 +#else + 0 +#endif + }, + { "BR_RDRAND", +#if BR_RDRAND + 1 +#else + 0 +#endif + }, + { "BR_ESP8266_RAND", +#if BR_USE_ESP8266_RAND + 1 +#else + 0 +#endif + }, + { "BR_SLOW_MUL", +#if BR_SLOW_MUL + 1 +#else + 0 +#endif + }, + { "BR_SLOW_MUL15", +#if BR_SLOW_MUL15 + 1 +#else + 0 +#endif + }, + { "BR_SSE2", +#if BR_SSE2 + 1 +#else + 0 +#endif + }, + { "BR_UMUL128", +#if BR_UMUL128 + 1 +#else + 0 +#endif + }, + { "BR_USE_UNIX_TIME", +#if BR_USE_UNIX_TIME + 1 +#else + 0 +#endif + }, + { "BR_USE_WIN32_RAND", +#if BR_USE_WIN32_RAND + 1 +#else + 0 +#endif + }, + { "BR_USE_WIN32_TIME", +#if BR_USE_WIN32_TIME + 1 +#else + 0 +#endif + }, + + { NULL, 0 } +}; + +/* see bearssl.h */ +const br_config_option * +br_get_config(void) +{ + return config; +} diff --git a/lib/bearssl-esp8266/src/ssl/prf.c b/lib/bearssl-esp8266/src/ssl/prf.c new file mode 100644 index 000000000..113097213 --- /dev/null +++ b/lib/bearssl-esp8266/src/ssl/prf.c @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +void +br_tls_phash(void *dst, size_t len, + const br_hash_class *dig, + const void *secret, size_t secret_len, const char *label, + size_t seed_num, const br_tls_prf_seed_chunk *seed) +{ + unsigned char *buf; + unsigned char tmp[64], a[64]; + br_hmac_key_context kc; + br_hmac_context hc; + size_t label_len, hlen, u; + + if (len == 0) { + return; + } + buf = dst; + for (label_len = 0; label[label_len]; label_len ++); + hlen = br_digest_size(dig); + br_hmac_key_init(&kc, dig, secret, secret_len); + br_hmac_init(&hc, &kc, 0); + br_hmac_update(&hc, label, label_len); + for (u = 0; u < seed_num; u ++) { + br_hmac_update(&hc, seed[u].data, seed[u].len); + } + br_hmac_out(&hc, a); + for (;;) { + br_hmac_init(&hc, &kc, 0); + br_hmac_update(&hc, a, hlen); + br_hmac_update(&hc, label, label_len); + for (u = 0; u < seed_num; u ++) { + br_hmac_update(&hc, seed[u].data, seed[u].len); + } + br_hmac_out(&hc, tmp); + for (u = 0; u < hlen && u < len; u ++) { + buf[u] ^= tmp[u]; + } + buf += u; + len -= u; + if (len == 0) { + return; + } + br_hmac_init(&hc, &kc, 0); + br_hmac_update(&hc, a, hlen); + br_hmac_out(&hc, a); + } +} diff --git a/lib/bearssl-esp8266/src/ssl/prf_md5sha1.c b/lib/bearssl-esp8266/src/ssl/prf_md5sha1.c new file mode 100644 index 000000000..37e3021f1 --- /dev/null +++ b/lib/bearssl-esp8266/src/ssl/prf_md5sha1.c @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl.h */ +void +br_tls10_prf(void *dst, size_t len, + const void *secret, size_t secret_len, const char *label, + size_t seed_num, const br_tls_prf_seed_chunk *seed) +{ + const unsigned char *s1; + size_t slen; + + s1 = secret; + slen = (secret_len + 1) >> 1; + memset(dst, 0, len); + br_tls_phash(dst, len, &br_md5_vtable, + s1, slen, label, seed_num, seed); + br_tls_phash(dst, len, &br_sha1_vtable, + s1 + secret_len - slen, slen, label, seed_num, seed); +} diff --git a/lib/bearssl-esp8266/src/ssl/prf_sha256.c b/lib/bearssl-esp8266/src/ssl/prf_sha256.c new file mode 100644 index 000000000..dfb8309e7 --- /dev/null +++ b/lib/bearssl-esp8266/src/ssl/prf_sha256.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl.h */ +void +br_tls12_sha256_prf(void *dst, size_t len, + const void *secret, size_t secret_len, const char *label, + size_t seed_num, const br_tls_prf_seed_chunk *seed) +{ + memset(dst, 0, len); + br_tls_phash(dst, len, &br_sha256_vtable, + secret, secret_len, label, seed_num, seed); +} diff --git a/lib/bearssl-esp8266/src/ssl/prf_sha384.c b/lib/bearssl-esp8266/src/ssl/prf_sha384.c new file mode 100644 index 000000000..3b0955002 --- /dev/null +++ b/lib/bearssl-esp8266/src/ssl/prf_sha384.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl.h */ +void +br_tls12_sha384_prf(void *dst, size_t len, + const void *secret, size_t secret_len, const char *label, + size_t seed_num, const br_tls_prf_seed_chunk *seed) +{ + memset(dst, 0, len); + br_tls_phash(dst, len, &br_sha384_vtable, + secret, secret_len, label, seed_num, seed); +} diff --git a/lib/bearssl-esp8266/src/ssl/ssl_ccert_single_ec.c b/lib/bearssl-esp8266/src/ssl/ssl_ccert_single_ec.c new file mode 100644 index 000000000..7e9530cd0 --- /dev/null +++ b/lib/bearssl-esp8266/src/ssl/ssl_ccert_single_ec.c @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +static void +cc_none0(const br_ssl_client_certificate_class **pctx) +{ + (void)pctx; +} + +static void +cc_none1(const br_ssl_client_certificate_class **pctx, size_t len) +{ + (void)pctx; + (void)len; +} + +static void +cc_none2(const br_ssl_client_certificate_class **pctx, + const unsigned char *data, size_t len) +{ + (void)pctx; + (void)data; + (void)len; +} + +static void +cc_choose(const br_ssl_client_certificate_class **pctx, + const br_ssl_client_context *cc, uint32_t auth_types, + br_ssl_client_certificate *choices) +{ + br_ssl_client_certificate_ec_context *zc; + int x; + int scurve; + + zc = (br_ssl_client_certificate_ec_context *)pctx; + scurve = br_ssl_client_get_server_curve(cc); + + if ((zc->allowed_usages & BR_KEYTYPE_KEYX) != 0 + && scurve == zc->sk->curve) + { + int x; + + x = (zc->issuer_key_type == BR_KEYTYPE_RSA) ? 16 : 17; + if (((auth_types >> x) & 1) != 0) { + choices->auth_type = BR_AUTH_ECDH; + choices->hash_id = -1; + choices->chain = zc->chain; + choices->chain_len = zc->chain_len; + } + } + + /* + * For ECDSA authentication, we must choose an appropriate + * hash function. + */ + x = br_ssl_choose_hash((unsigned)(auth_types >> 8)); + if (x == 0 || (zc->allowed_usages & BR_KEYTYPE_SIGN) == 0) { + memset(choices, 0, sizeof *choices); + return; + } + choices->auth_type = BR_AUTH_ECDSA; + choices->hash_id = x; + choices->chain = zc->chain; + choices->chain_len = zc->chain_len; +} + +static uint32_t +cc_do_keyx(const br_ssl_client_certificate_class **pctx, + unsigned char *data, size_t *len) +{ + br_ssl_client_certificate_ec_context *zc; + uint32_t r; + size_t xoff, xlen; + + zc = (br_ssl_client_certificate_ec_context *)pctx; + r = zc->iec->mul(data, *len, zc->sk->x, zc->sk->xlen, zc->sk->curve); + xoff = zc->iec->xoff(zc->sk->curve, &xlen); + memmove(data, data + xoff, xlen); + *len = xlen; + return r; +} + +static size_t +cc_do_sign(const br_ssl_client_certificate_class **pctx, + int hash_id, size_t hv_len, unsigned char *data, size_t len) +{ + br_ssl_client_certificate_ec_context *zc; + unsigned char hv[64]; + const br_hash_class *hc; + + zc = (br_ssl_client_certificate_ec_context *)pctx; + memcpy(hv, data, hv_len); + hc = br_multihash_getimpl(zc->mhash, hash_id); + if (hc == NULL) { + return 0; + } + if (len < 139) { + return 0; + } + return zc->iecdsa(zc->iec, hc, hv, zc->sk, data); +} + +static const br_ssl_client_certificate_class ccert_vtable PROGMEM = { + sizeof(br_ssl_client_certificate_ec_context), + cc_none0, /* start_name_list */ + cc_none1, /* start_name */ + cc_none2, /* append_name */ + cc_none0, /* end_name */ + cc_none0, /* end_name_list */ + cc_choose, + cc_do_keyx, + cc_do_sign +}; + +/* see bearssl_ssl.h */ +void +br_ssl_client_set_single_ec(br_ssl_client_context *cc, + const br_x509_certificate *chain, size_t chain_len, + const br_ec_private_key *sk, unsigned allowed_usages, + unsigned cert_issuer_key_type, + const br_ec_impl *iec, br_ecdsa_sign iecdsa) +{ + cc->client_auth.single_ec.vtable = &ccert_vtable; + cc->client_auth.single_ec.chain = chain; + cc->client_auth.single_ec.chain_len = chain_len; + cc->client_auth.single_ec.sk = sk; + cc->client_auth.single_ec.allowed_usages = allowed_usages; + cc->client_auth.single_ec.issuer_key_type = cert_issuer_key_type; + cc->client_auth.single_ec.mhash = &cc->eng.mhash; + cc->client_auth.single_ec.iec = iec; + cc->client_auth.single_ec.iecdsa = iecdsa; + cc->client_auth_vtable = &cc->client_auth.single_ec.vtable; +} diff --git a/lib/bearssl-esp8266/src/ssl/ssl_ccert_single_rsa.c b/lib/bearssl-esp8266/src/ssl/ssl_ccert_single_rsa.c new file mode 100644 index 000000000..d74bf111d --- /dev/null +++ b/lib/bearssl-esp8266/src/ssl/ssl_ccert_single_rsa.c @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +static void +cc_none0(const br_ssl_client_certificate_class **pctx) +{ + (void)pctx; +} + +static void +cc_none1(const br_ssl_client_certificate_class **pctx, size_t len) +{ + (void)pctx; + (void)len; +} + +static void +cc_none2(const br_ssl_client_certificate_class **pctx, + const unsigned char *data, size_t len) +{ + (void)pctx; + (void)data; + (void)len; +} + +static void +cc_choose(const br_ssl_client_certificate_class **pctx, + const br_ssl_client_context *cc, uint32_t auth_types, + br_ssl_client_certificate *choices) +{ + br_ssl_client_certificate_rsa_context *zc; + int x; + + (void)cc; + zc = (br_ssl_client_certificate_rsa_context *)pctx; + x = br_ssl_choose_hash((unsigned)auth_types); + if (x == 0 && (auth_types & 1) == 0) { + memset(choices, 0, sizeof *choices); + } + choices->auth_type = BR_AUTH_RSA; + choices->hash_id = x; + choices->chain = zc->chain; + choices->chain_len = zc->chain_len; +} + +/* + * OID for hash functions in RSA signatures. + */ +#if 0 +static const unsigned char HASH_OID_SHA1[] = { + 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A +}; + +static const unsigned char HASH_OID_SHA224[] = { + 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04 +}; + +static const unsigned char HASH_OID_SHA256[] = { + 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01 +}; + +static const unsigned char HASH_OID_SHA384[] = { + 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02 +}; + +static const unsigned char HASH_OID_SHA512[] = { + 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03 +}; +#endif +// EFP3 - use copy of these present in another file. GCC won't merge local static arrays, even if they're equal, it seems +extern const unsigned char HASH_OID_SHA1[]; +extern const unsigned char HASH_OID_SHA224[]; +extern const unsigned char HASH_OID_SHA256[]; +extern const unsigned char HASH_OID_SHA384[]; +extern const unsigned char HASH_OID_SHA512[]; + +static const unsigned char *HASH_OID[] PROGMEM = { + HASH_OID_SHA1, + HASH_OID_SHA224, + HASH_OID_SHA256, + HASH_OID_SHA384, + HASH_OID_SHA512 +}; + +static size_t +cc_do_sign(const br_ssl_client_certificate_class **pctx, + int hash_id, size_t hv_len, unsigned char *data, size_t len) +{ + br_ssl_client_certificate_rsa_context *zc; + unsigned char hv[64]; + const unsigned char *hash_oid; + size_t sig_len; + + zc = (br_ssl_client_certificate_rsa_context *)pctx; + memcpy(hv, data, hv_len); + if (hash_id == 0) { + hash_oid = NULL; + } else if (hash_id >= 2 && hash_id <= 6) { + hash_oid = HASH_OID[hash_id - 2]; + } else { + return 0; + } + sig_len = (zc->sk->n_bitlen + 7) >> 3; + if (len < sig_len) { + return 0; + } + return zc->irsasign(hash_oid, hv, hv_len, zc->sk, data) ? sig_len : 0; +} + +static const br_ssl_client_certificate_class ccert_vtable PROGMEM = { + sizeof(br_ssl_client_certificate_rsa_context), + cc_none0, /* start_name_list */ + cc_none1, /* start_name */ + cc_none2, /* append_name */ + cc_none0, /* end_name */ + cc_none0, /* end_name_list */ + cc_choose, + 0, + cc_do_sign +}; + +/* see bearssl_ssl.h */ +void +br_ssl_client_set_single_rsa(br_ssl_client_context *cc, + const br_x509_certificate *chain, size_t chain_len, + const br_rsa_private_key *sk, br_rsa_pkcs1_sign irsasign) +{ + cc->client_auth.single_rsa.vtable = &ccert_vtable; + cc->client_auth.single_rsa.chain = chain; + cc->client_auth.single_rsa.chain_len = chain_len; + cc->client_auth.single_rsa.sk = sk; + cc->client_auth.single_rsa.irsasign = irsasign; + cc->client_auth_vtable = &cc->client_auth.single_rsa.vtable; +} diff --git a/lib/bearssl-esp8266/src/ssl/ssl_client.c b/lib/bearssl-esp8266/src/ssl/ssl_client.c new file mode 100644 index 000000000..73010c43c --- /dev/null +++ b/lib/bearssl-esp8266/src/ssl/ssl_client.c @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_ssl.h */ +void +br_ssl_client_zero(br_ssl_client_context *cc) +{ + /* + * For really standard C, we should explicitly set to NULL all + * pointers, and 0 all other fields. However, on all our target + * architectures, a direct memset() will work, be faster, and + * use a lot less code. + */ + memset(cc, 0, sizeof *cc); +} + +/* see bearssl_ssl.h */ +int +br_ssl_client_reset(br_ssl_client_context *cc, + const char *server_name, int resume_session) +{ + size_t n; + + br_ssl_engine_set_buffer(&cc->eng, NULL, 0, 0); + cc->eng.version_out = cc->eng.version_min; + if (!resume_session) { + br_ssl_client_forget_session(cc); + } + if (!br_ssl_engine_init_rand(&cc->eng)) { + return 0; + } + + /* + * We always set back the "reneg" flag to 0 because we use it + * to distinguish between first handshake and renegotiation. + * Note that "renegotiation" and "session resumption" are two + * different things. + */ + cc->eng.reneg = 0; + + if (server_name == NULL) { + cc->eng.server_name[0] = 0; + } else { + n = strlen(server_name) + 1; + if (n > sizeof cc->eng.server_name) { + br_ssl_engine_fail(&cc->eng, BR_ERR_BAD_PARAM); + return 0; + } + memcpy(cc->eng.server_name, server_name, n); + } + + br_ssl_engine_hs_reset(&cc->eng, + br_ssl_hs_client_init_main, br_ssl_hs_client_run); + return br_ssl_engine_last_error(&cc->eng) == BR_ERR_OK; +} diff --git a/lib/bearssl-esp8266/src/ssl/ssl_client_default_rsapub.c b/lib/bearssl-esp8266/src/ssl/ssl_client_default_rsapub.c new file mode 100644 index 000000000..0cedecdb9 --- /dev/null +++ b/lib/bearssl-esp8266/src/ssl/ssl_client_default_rsapub.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_ssl.h */ +void +br_ssl_client_set_default_rsapub(br_ssl_client_context *cc) +{ + br_ssl_client_set_rsapub(cc, br_rsa_public_get_default()); +} diff --git a/lib/bearssl-esp8266/src/ssl/ssl_client_full.c b/lib/bearssl-esp8266/src/ssl/ssl_client_full.c new file mode 100644 index 000000000..a7755862c --- /dev/null +++ b/lib/bearssl-esp8266/src/ssl/ssl_client_full.c @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_ssl.h */ +void +br_ssl_client_init_full(br_ssl_client_context *cc, + br_x509_minimal_context *xc, + const br_x509_trust_anchor *trust_anchors, size_t trust_anchors_num) +{ + /* + * The "full" profile supports all implemented cipher suites. + * + * Rationale for suite order, from most important to least + * important rule: + * + * -- Don't use 3DES if AES or ChaCha20 is available. + * -- Try to have Forward Secrecy (ECDHE suite) if possible. + * -- When not using Forward Secrecy, ECDH key exchange is + * better than RSA key exchange (slightly more expensive on the + * client, but much cheaper on the server, and it implies smaller + * messages). + * -- ChaCha20+Poly1305 is better than AES/GCM (faster, smaller code). + * -- GCM is better than CCM and CBC. CCM is better than CBC. + * -- CCM is preferable over CCM_8 (with CCM_8, forgeries may succeed + * with probability 2^(-64)). + * -- AES-128 is preferred over AES-256 (AES-128 is already + * strong enough, and AES-256 is 40% more expensive). + */ + static const uint16_t suites[] = { + BR_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + BR_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + BR_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + BR_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + BR_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + BR_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + BR_TLS_ECDHE_ECDSA_WITH_AES_128_CCM, + BR_TLS_ECDHE_ECDSA_WITH_AES_256_CCM, + BR_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, + BR_TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8, + BR_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + BR_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, + BR_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, + BR_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, + BR_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + BR_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + BR_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + BR_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + BR_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, + BR_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, + BR_TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384, + BR_TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384, + BR_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, + BR_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, + BR_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384, + BR_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384, + BR_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, + BR_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, + BR_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, + BR_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, + BR_TLS_RSA_WITH_AES_128_GCM_SHA256, + BR_TLS_RSA_WITH_AES_256_GCM_SHA384, + BR_TLS_RSA_WITH_AES_128_CCM, + BR_TLS_RSA_WITH_AES_256_CCM, + BR_TLS_RSA_WITH_AES_128_CCM_8, + BR_TLS_RSA_WITH_AES_256_CCM_8, + BR_TLS_RSA_WITH_AES_128_CBC_SHA256, + BR_TLS_RSA_WITH_AES_256_CBC_SHA256, + BR_TLS_RSA_WITH_AES_128_CBC_SHA, + BR_TLS_RSA_WITH_AES_256_CBC_SHA, + BR_TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, + BR_TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, + BR_TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, + BR_TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, + BR_TLS_RSA_WITH_3DES_EDE_CBC_SHA + }; + + /* + * All hash functions are activated. + * Note: the X.509 validation engine will nonetheless refuse to + * validate signatures that use MD5 as hash function. + */ + static const br_hash_class *hashes[] = { + &br_md5_vtable, + &br_sha1_vtable, + &br_sha224_vtable, + &br_sha256_vtable, + &br_sha384_vtable, + &br_sha512_vtable + }; + + int id; + + /* + * Reset client context and set supported versions from TLS-1.0 + * to TLS-1.2 (inclusive). + */ + br_ssl_client_zero(cc); + br_ssl_engine_set_versions(&cc->eng, BR_TLS10, BR_TLS12); + + /* + * X.509 engine uses SHA-256 to hash certificate DN (for + * comparisons). + */ + br_x509_minimal_init(xc, &br_sha256_vtable, + trust_anchors, trust_anchors_num); + + /* + * Set suites and asymmetric crypto implementations. We use the + * "i31" code for RSA (it is somewhat faster than the "i32" + * implementation). + * TODO: change that when better implementations are made available. + */ + br_ssl_engine_set_suites(&cc->eng, suites, + (sizeof suites) / (sizeof suites[0])); + br_ssl_client_set_default_rsapub(cc); + br_ssl_engine_set_default_rsavrfy(&cc->eng); + br_ssl_engine_set_default_ecdsa(&cc->eng); + br_x509_minimal_set_rsa(xc, br_ssl_engine_get_rsavrfy(&cc->eng)); + br_x509_minimal_set_ecdsa(xc, + br_ssl_engine_get_ec(&cc->eng), + br_ssl_engine_get_ecdsa(&cc->eng)); + + /* + * Set supported hash functions, for the SSL engine and for the + * X.509 engine. + */ + for (id = br_md5_ID; id <= br_sha512_ID; id ++) { + const br_hash_class *hc; + + hc = hashes[id - 1]; + br_ssl_engine_set_hash(&cc->eng, id, hc); + br_x509_minimal_set_hash(xc, id, hc); + } + + /* + * Link the X.509 engine in the SSL engine. + */ + br_ssl_engine_set_x509(&cc->eng, &xc->vtable); + + /* + * Set the PRF implementations. + */ + br_ssl_engine_set_prf10(&cc->eng, &br_tls10_prf); + br_ssl_engine_set_prf_sha256(&cc->eng, &br_tls12_sha256_prf); + br_ssl_engine_set_prf_sha384(&cc->eng, &br_tls12_sha384_prf); + + /* + * Symmetric encryption. We use the "default" implementations + * (fastest among constant-time implementations). + */ + br_ssl_engine_set_default_aes_cbc(&cc->eng); + br_ssl_engine_set_default_aes_ccm(&cc->eng); + br_ssl_engine_set_default_aes_gcm(&cc->eng); + br_ssl_engine_set_default_des_cbc(&cc->eng); + br_ssl_engine_set_default_chapol(&cc->eng); +} diff --git a/lib/bearssl-esp8266/src/ssl/ssl_engine.c b/lib/bearssl-esp8266/src/ssl/ssl_engine.c new file mode 100644 index 000000000..fdd4d5bb7 --- /dev/null +++ b/lib/bearssl-esp8266/src/ssl/ssl_engine.c @@ -0,0 +1,1569 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +#if 0 +/* obsolete */ + +/* + * If BR_USE_URANDOM is not defined, then try to autodetect its presence + * through compiler macros. + */ +#ifndef BR_USE_URANDOM + +/* + * Macro values documented on: + * https://sourceforge.net/p/predef/wiki/OperatingSystems/ + * + * Only the most common systems have been included here for now. This + * should be enriched later on. + */ +#if defined _AIX \ + || defined __ANDROID__ \ + || defined __FreeBSD__ \ + || defined __NetBSD__ \ + || defined __OpenBSD__ \ + || defined __DragonFly__ \ + || defined __linux__ \ + || (defined __sun && (defined __SVR4 || defined __svr4__)) \ + || (defined __APPLE__ && defined __MACH__) +#define BR_USE_URANDOM 1 +#endif + +#endif + +/* + * If BR_USE_WIN32_RAND is not defined, perform autodetection here. + */ +#ifndef BR_USE_WIN32_RAND + +#if defined _WIN32 || defined _WIN64 +#define BR_USE_WIN32_RAND 1 +#endif + +#endif + +#if BR_USE_URANDOM +#include +#include +#include +#include +#endif + +#if BR_USE_WIN32_RAND +#include +#include +#pragma comment(lib, "advapi32") +#endif + +#endif + +/* ==================================================================== */ +/* + * This part of the file does the low-level record management. + */ + +/* + * IMPLEMENTATION NOTES + * ==================== + * + * In this file, we designate by "input" (and the "i" letter) the "recv" + * operations: incoming records from the peer, from which payload data + * is obtained, and must be extracted by the application (or the SSL + * handshake engine). Similarly, "output" (and the "o" letter) is for + * "send": payload data injected by the application (and SSL handshake + * engine), to be wrapped into records, that are then conveyed to the + * peer over the transport medium. + * + * The input and output buffers may be distinct or shared. When + * shared, input and output cannot occur concurrently; the caller + * must make sure that it never needs to output data while input + * data has been received. In practice, a shared buffer prevents + * pipelining of HTTP requests, or similar protocols; however, a + * shared buffer saves RAM. + * + * The input buffer is pointed to by 'ibuf' and has size 'ibuf_len'; + * the output buffer is pointed to by 'obuf' and has size 'obuf_len'. + * From the size of these buffers is derived the maximum fragment + * length, which will be honoured upon sending records; regardless of + * that length, incoming records will be processed as long as they + * fit in the input buffer, and their length still complies with the + * protocol specification (maximum plaintext payload length is 16384 + * bytes). + * + * Three registers are used to manage buffering in ibuf, called ixa, + * ixb and ixc. Similarly, three registers are used to manage buffering + * in obuf, called oxa, oxb and oxc. + * + * + * At any time, the engine is in one of the following modes: + * -- Failed mode: an error occurs, no I/O can happen. + * -- Input mode: the engine can either receive record bytes from the + * transport layer, or it has some buffered payload bytes to yield. + * -- Output mode: the engine can either receive payload bytes, or it + * has some record bytes to send to the transport layer. + * -- Input/Output mode: both input and output modes are active. When + * the buffer is shared, this can happen only when the buffer is empty + * (no buffered payload bytes or record bytes in either direction). + * + * + * Failed mode: + * ------------ + * + * I/O failed for some reason (invalid received data, not enough room + * for the next record...). No I/O may ever occur again for this context, + * until an explicit reset is performed. This mode, and the error code, + * are also used for protocol errors, especially handshake errors. + * + * + * Input mode: + * ----------- + * + * ixa index within ibuf[] for the currently read data + * ixb maximum index within ibuf[] for the currently read data + * ixc number of bytes not yet received for the current record + * + * -- When ixa == ixb, there is no available data for readers. When + * ixa != ixb, there is available data and it starts at offset ixa. + * + * -- When waiting for the next record header, ixa and ixb are equal + * and contain a value ranging from 0 to 4; ixc is equal to 5-ixa. + * + * -- When the header has been received, record data is obtained. The + * ixc field records how many bytes are still needed to reach the + * end of the current record. + * + * ** If encryption is active, then ixa and ixb are kept equal, and + * point to the end of the currently received record bytes. When + * ixc reaches 0, decryption/MAC is applied, and ixa and ixb are + * adjusted. + * + * ** If encryption is not active, then ixa and ixb are distinct + * and data can be read right away. Additional record data is + * obtained only when ixa == ixb. + * + * Note: in input mode and no encryption, records larger than the buffer + * size are allowed. When encryption is active, the complete record must + * fit within the buffer, since it cannot be decrypted/MACed until it + * has been completely received. + * + * -- When receiving the next record header, 'version_in' contains the + * expected input version (0 if not expecting a specific version); on + * mismatch, the mode switches to 'failed'. + * + * -- When the header has been received, 'version_in' contains the received + * version. It is up to the caller to check and adjust the 'version_in' field + * to implement the required semantics. + * + * -- The 'record_type_in' field is updated with the incoming record type + * when the next record header has been received. + * + * + * Output mode: + * ------------ + * + * oxa index within obuf[] for the currently accumulated data + * oxb maximum index within obuf[] for record data + * oxc pointer for start of record data, and for record sending + * + * -- When oxa != oxb, more data can be accumulated into the current + * record; when oxa == oxb, a closed record is being sent. + * + * -- When accumulating data, oxc points to the start of the data. + * + * -- During record sending, oxa (and oxb) point to the next record byte + * to send, and oxc indicates the end of the current record. + * + * Note: sent records must fit within the buffer, since the header is + * adjusted only when the complete record has been assembled. + * + * -- The 'version_out' and 'record_type_out' fields are used to build the + * record header when the mode is switched to 'sending'. + * + * + * Modes: + * ------ + * + * The state register iomode contains one of the following values: + * + * BR_IO_FAILED I/O failed + * BR_IO_IN input mode + * BR_IO_OUT output mode + * BR_IO_INOUT input/output mode + * + * Whether encryption is active on incoming records is indicated by the + * incrypt flag. For outgoing records, there is no such flag; "encryption" + * is always considered active, but initially uses functions that do not + * encrypt anything. The 'incrypt' flag is needed because when there is + * no active encryption, records larger than the I/O buffer are accepted. + * + * Note: we do not support no-encryption modes (MAC only). + * + * TODO: implement GCM support + * + * + * Misc: + * ----- + * + * 'max_frag_len' is the maximum plaintext size for an outgoing record. + * By default, it is set to the maximum value that fits in the provided + * buffers, in the following list: 512, 1024, 2048, 4096, 16384. The + * caller may change it if needed, but the new value MUST still fit in + * the buffers, and it MUST be one of the list above for compatibility + * with the Maximum Fragment Length extension. + * + * For incoming records, only the total buffer length and current + * encryption mode impact the maximum length for incoming records. The + * 'max_frag_len' value is still adjusted so that records up to that + * length can be both received and sent. + * + * + * Offsets and lengths: + * -------------------- + * + * When sending fragments with TLS-1.1+, the maximum overhead is: + * 5 bytes for the record header + * 16 bytes for the explicit IV + * 48 bytes for the MAC (HMAC/SHA-384) + * 16 bytes for the padding (AES) + * so a total of 85 extra bytes. Note that we support block cipher sizes + * up to 16 bytes (AES) and HMAC output sizes up to 48 bytes (SHA-384). + * + * With TLS-1.0 and CBC mode, we apply a 1/n-1 split, for a maximum + * overhead of: + * 5 bytes for the first record header + * 32 bytes for the first record payload (AES-CBC + HMAC/SHA-1) + * 5 bytes for the second record header + * 20 bytes for the MAC (HMAC/SHA-1) + * 16 bytes for the padding (AES) + * -1 byte to account for the payload byte in the first record + * so a total of 77 extra bytes at most, less than the 85 bytes above. + * Note that with TLS-1.0, the MAC is HMAC with either MD5 or SHA-1, but + * no other hash function. + * + * The implementation does not try to send larger records when the current + * encryption mode has less overhead. + * + * Maximum input record overhead is: + * 5 bytes for the record header + * 16 bytes for the explicit IV (TLS-1.1+) + * 48 bytes for the MAC (HMAC/SHA-384) + * 256 bytes for the padding + * so a total of 325 extra bytes. + * + * When receiving the next record header, it is written into the buffer + * bytes 0 to 4 (inclusive). Record data is always written into buf[] + * starting at offset 5. When encryption is active, the plaintext data + * may start at a larger offset (e.g. because of an explicit IV). + */ + +#define MAX_OUT_OVERHEAD 85 +#define MAX_IN_OVERHEAD 325 + +/* see inner.h */ +void +br_ssl_engine_fail(br_ssl_engine_context *rc, int err) +{ + if (rc->iomode != BR_IO_FAILED) { + rc->iomode = BR_IO_FAILED; + rc->err = err; + } +} + +/* + * Adjust registers for a new incoming record. + */ +static void +make_ready_in(br_ssl_engine_context *rc) +{ + rc->ixa = rc->ixb = 0; + rc->ixc = 5; + if (rc->iomode == BR_IO_IN) { + rc->iomode = BR_IO_INOUT; + } +} + +/* + * Adjust registers for a new outgoing record. + */ +static void +make_ready_out(br_ssl_engine_context *rc) +{ + size_t a, b; + + a = 5; + b = rc->obuf_len - a; + rc->out.vtable->max_plaintext(&rc->out.vtable, &a, &b); + if ((b - a) > rc->max_frag_len) { + b = a + rc->max_frag_len; + } + rc->oxa = a; + rc->oxb = b; + rc->oxc = a; + if (rc->iomode == BR_IO_OUT) { + rc->iomode = BR_IO_INOUT; + } +} + +/* see inner.h */ +void +br_ssl_engine_new_max_frag_len(br_ssl_engine_context *rc, unsigned max_frag_len) +{ + size_t nxb; + + rc->max_frag_len = max_frag_len; + nxb = rc->oxc + max_frag_len; + if (rc->oxa < rc->oxb && rc->oxb > nxb && rc->oxa < nxb) { + rc->oxb = nxb; + } +} + +/* see bearssl_ssl.h */ +void +br_ssl_engine_set_buffer(br_ssl_engine_context *rc, + void *buf, size_t buf_len, int bidi) +{ + if (buf == NULL) { + br_ssl_engine_set_buffers_bidi(rc, NULL, 0, NULL, 0); + } else { + /* + * In bidirectional mode, we want to maximise input + * buffer size, since we support arbitrary fragmentation + * when sending, but the peer will not necessarily + * comply to any low fragment length (in particular if + * we are the server, because the maximum fragment + * length extension is under client control). + * + * We keep a minimum size of 512 bytes for the plaintext + * of our outgoing records. + * + * br_ssl_engine_set_buffers_bidi() will compute the maximum + * fragment length for outgoing records by using the minimum + * of allocated spaces for both input and output records, + * rounded down to a standard length. + */ + if (bidi) { + size_t w; + + if (buf_len < (512 + MAX_IN_OVERHEAD + + 512 + MAX_OUT_OVERHEAD)) + { + rc->iomode = BR_IO_FAILED; + rc->err = BR_ERR_BAD_PARAM; + return; + } else if (buf_len < (16384 + MAX_IN_OVERHEAD + + 512 + MAX_OUT_OVERHEAD)) + { + w = 512 + MAX_OUT_OVERHEAD; + } else { + w = buf_len - (16384 + MAX_IN_OVERHEAD); + } + br_ssl_engine_set_buffers_bidi(rc, + buf, buf_len - w, + (unsigned char *)buf + w, w); + } else { + br_ssl_engine_set_buffers_bidi(rc, + buf, buf_len, NULL, 0); + } + } +} + +/* see bearssl_ssl.h */ +void +br_ssl_engine_set_buffers_bidi(br_ssl_engine_context *rc, + void *ibuf, size_t ibuf_len, void *obuf, size_t obuf_len) +{ + rc->iomode = BR_IO_INOUT; + rc->incrypt = 0; + rc->err = BR_ERR_OK; + rc->version_in = 0; + rc->record_type_in = 0; + rc->version_out = 0; + rc->record_type_out = 0; + if (ibuf == NULL) { + if (rc->ibuf == NULL) { + br_ssl_engine_fail(rc, BR_ERR_BAD_PARAM); + } + } else { + unsigned u; + + rc->ibuf = ibuf; + rc->ibuf_len = ibuf_len; + if (obuf == NULL) { + obuf = ibuf; + obuf_len = ibuf_len; + } + rc->obuf = obuf; + rc->obuf_len = obuf_len; + + /* + * Compute the maximum fragment length, that fits for + * both incoming and outgoing records. This length will + * be used in fragment length negotiation, so we must + * honour it both ways. Regardless, larger incoming + * records will be accepted, as long as they fit in the + * actual buffer size. + */ + for (u = 14; u >= 9; u --) { + size_t flen; + + flen = (size_t)1 << u; + if (obuf_len >= flen + MAX_OUT_OVERHEAD + && ibuf_len >= flen + MAX_IN_OVERHEAD) + { + break; + } + } + if (u == 8) { + br_ssl_engine_fail(rc, BR_ERR_BAD_PARAM); + return; + } else if (u == 13) { + u = 12; + } + rc->max_frag_len = (size_t)1 << u; + rc->log_max_frag_len = u; + rc->peer_log_max_frag_len = 0; + } + rc->out.vtable = &br_sslrec_out_clear_vtable; + make_ready_in(rc); + make_ready_out(rc); +} + +/* + * Clear buffers in both directions. + */ +static void +engine_clearbuf(br_ssl_engine_context *rc) +{ + make_ready_in(rc); + make_ready_out(rc); +} + +/* + * Make sure the internal PRNG is initialised (but not necessarily + * seeded properly yet). + */ +static int +rng_init(br_ssl_engine_context *cc) +{ + const br_hash_class *h; + + if (cc->rng_init_done != 0) { + return 1; + } + + /* + * If using TLS-1.2, then SHA-256 or SHA-384 must be present (or + * both); we prefer SHA-256 which is faster for 32-bit systems. + * + * If using TLS-1.0 or 1.1 then SHA-1 must be present. + * + * Though HMAC_DRBG/SHA-1 is, as far as we know, as safe as + * these things can be, we still prefer the SHA-2 functions over + * SHA-1, if only for public relations (known theoretical + * weaknesses of SHA-1 with regards to collisions are mostly + * irrelevant here, but they still make people nervous). + */ + h = br_multihash_getimpl(&cc->mhash, br_sha256_ID); + if (!h) { + h = br_multihash_getimpl(&cc->mhash, br_sha384_ID); + if (!h) { + h = br_multihash_getimpl(&cc->mhash, + br_sha1_ID); + if (!h) { + br_ssl_engine_fail(cc, BR_ERR_BAD_STATE); + return 0; + } + } + } + br_hmac_drbg_init(&cc->rng, h, NULL, 0); + cc->rng_init_done = 1; + return 1; +} + +/* see inner.h */ +int +br_ssl_engine_init_rand(br_ssl_engine_context *cc) +{ + if (!rng_init(cc)) { + return 0; + } + + /* + * We always try OS/hardware seeding once. If it works, then + * we assume proper seeding. If not, then external entropy must + * have been injected; otherwise, we report an error. + */ + if (!cc->rng_os_rand_done) { + br_prng_seeder sd; + + sd = br_prng_seeder_system(NULL); + if (sd != 0 && sd(&cc->rng.vtable)) { + cc->rng_init_done = 2; + } + cc->rng_os_rand_done = 1; + } + if (cc->rng_init_done < 2) { + br_ssl_engine_fail(cc, BR_ERR_NO_RANDOM); + return 0; + } + return 1; +} + +/* see bearssl_ssl.h */ +void +br_ssl_engine_inject_entropy(br_ssl_engine_context *cc, + const void *data, size_t len) +{ + /* + * Externally provided entropy is assumed to be "good enough" + * (we cannot really test its quality) so if the RNG structure + * could be initialised at all, then we marked the RNG as + * "properly seeded". + */ + if (!rng_init(cc)) { + return; + } + br_hmac_drbg_update(&cc->rng, data, len); + cc->rng_init_done = 2; +} + +/* + * We define a few internal functions that implement the low-level engine + * API for I/O; the external API (br_ssl_engine_sendapp_buf() and similar + * functions) is built upon these function, with special processing for + * records which are not of type "application data". + * + * recvrec_buf, recvrec_ack receives bytes from transport medium + * sendrec_buf, sendrec_ack send bytes to transport medium + * recvpld_buf, recvpld_ack receives payload data from engine + * sendpld_buf, sendpld_ack send payload data to engine + */ + +static unsigned char * +recvrec_buf(const br_ssl_engine_context *rc, size_t *len) +{ + if (rc->shutdown_recv) { + *len = 0; + return NULL; + } + + /* + * Bytes from the transport can be injected only if the mode is + * compatible (in or in/out), and ixa == ixb; ixc then contains + * the number of bytes that are still expected (but it may + * exceed our buffer size). + * + * We cannot get "stuck" here (buffer is full, but still more + * data is expected) because oversized records are detected when + * their header is processed. + */ + switch (rc->iomode) { + case BR_IO_IN: + case BR_IO_INOUT: + if (rc->ixa == rc->ixb) { + size_t z; + + z = rc->ixc; + if (z > rc->ibuf_len - rc->ixa) { + z = rc->ibuf_len - rc->ixa; + } + *len = z; + return rc->ibuf + rc->ixa; + } + break; + } + *len = 0; + return NULL; +} + +static void +recvrec_ack(br_ssl_engine_context *rc, size_t len) +{ + unsigned char *pbuf; + size_t pbuf_len; + + /* + * Adjust state if necessary (for a shared input/output buffer): + * we got some incoming bytes, so we cannot (temporarily) handle + * outgoing data. + */ + if (rc->iomode == BR_IO_INOUT && rc->ibuf == rc->obuf) { + rc->iomode = BR_IO_IN; + } + + /* + * Adjust data pointers. + */ + rc->ixb = (rc->ixa += len); + rc->ixc -= len; + + /* + * If we are receiving a header and did not fully obtained it + * yet, then just wait for the next bytes. + */ + if (rc->ixa < 5) { + return; + } + + /* + * If we just obtained a full header, process it. + */ + if (rc->ixa == 5) { + unsigned version; + unsigned rlen; + + /* + * Get record type and version. We support only versions + * 3.x (if the version major number does not match, then + * we suppose that the record format is too alien for us + * to process it). + * + * Note: right now, we reject clients that try to send + * a ClientHello in a format compatible with SSL-2.0. It + * is unclear whether this will ever be supported; and + * if we want to support it, then this might be done in + * in the server-specific code, not here. + */ + rc->record_type_in = rc->ibuf[0]; + version = br_dec16be(rc->ibuf + 1); + if ((version >> 8) != 3) { + br_ssl_engine_fail(rc, BR_ERR_UNSUPPORTED_VERSION); + return; + } + + /* + * We ensure that successive records have the same + * version. The handshake code must check and adjust the + * variables when necessary to accommodate the protocol + * negotiation details. + */ + if (rc->version_in != 0 && rc->version_in != version) { + br_ssl_engine_fail(rc, BR_ERR_BAD_VERSION); + return; + } + rc->version_in = version; + + /* + * Decode record length. We must check that the length + * is valid (relatively to the current encryption mode) + * and also (if encryption is active) that the record + * will fit in our buffer. + * + * When no encryption is active, we can process records + * by chunks, and thus accept any record up to the + * maximum allowed plaintext length (16384 bytes). + */ + rlen = br_dec16be(rc->ibuf + 3); + if (rc->incrypt) { + if (!rc->in.vtable->check_length( + &rc->in.vtable, rlen)) + { + br_ssl_engine_fail(rc, BR_ERR_BAD_LENGTH); + return; + } + if (rlen > (rc->ibuf_len - 5)) { + br_ssl_engine_fail(rc, BR_ERR_TOO_LARGE); + return; + } + } else { + if (rlen > 16384) { + br_ssl_engine_fail(rc, BR_ERR_BAD_LENGTH); + return; + } + } + + /* + * If the record is completely empty then we must switch + * to a new record. Note that, in that case, we + * completely ignore the record type, which is fitting + * since we received no actual data of that type. + * + * A completely empty record is technically allowed as + * long as encryption/MAC is not active, i.e. before + * completion of the first handshake. It it still weird; + * it might conceptually be useful as a heartbeat or + * keep-alive mechanism while some lengthy operation is + * going on, e.g. interaction with a human user. + */ + if (rlen == 0) { + make_ready_in(rc); + } else { + rc->ixa = rc->ixb = 5; + rc->ixc = rlen; + } + return; + } + + /* + * If there is no active encryption, then the data can be read + * right away. Note that we do not receive bytes from the + * transport medium when we still have payload bytes to be + * acknowledged. + */ + if (!rc->incrypt) { + rc->ixa = 5; + return; + } + + /* + * Since encryption is active, we must wait for a full record + * before processing it. + */ + if (rc->ixc != 0) { + return; + } + + /* + * We got the full record. Decrypt it. + */ + pbuf_len = rc->ixa - 5; + pbuf = rc->in.vtable->decrypt(&rc->in.vtable, + rc->record_type_in, rc->version_in, rc->ibuf + 5, &pbuf_len); + if (pbuf == 0) { + br_ssl_engine_fail(rc, BR_ERR_BAD_MAC); + return; + } + rc->ixa = (size_t)(pbuf - rc->ibuf); + rc->ixb = rc->ixa + pbuf_len; + + /* + * Decryption may have yielded an empty record, in which case + * we get back to "ready" state immediately. + */ + if (rc->ixa == rc->ixb) { + make_ready_in(rc); + } +} + +/* see inner.h */ +int +br_ssl_engine_recvrec_finished(const br_ssl_engine_context *rc) +{ + switch (rc->iomode) { + case BR_IO_IN: + case BR_IO_INOUT: + return rc->ixc == 0 || rc->ixa < 5; + default: + return 1; + } +} + +static unsigned char * +recvpld_buf(const br_ssl_engine_context *rc, size_t *len) +{ + /* + * There is payload data to be read only if the mode is + * compatible, and ixa != ixb. + */ + switch (rc->iomode) { + case BR_IO_IN: + case BR_IO_INOUT: + *len = rc->ixb - rc->ixa; + return (*len == 0) ? NULL : (rc->ibuf + rc->ixa); + default: + *len = 0; + return NULL; + } +} + +static void +recvpld_ack(br_ssl_engine_context *rc, size_t len) +{ + rc->ixa += len; + + /* + * If we read all the available data, then we either expect + * the remainder of the current record (if the current record + * was not finished; this may happen when encryption is not + * active), or go to "ready" state. + */ + if (rc->ixa == rc->ixb) { + if (rc->ixc == 0) { + make_ready_in(rc); + } else { + rc->ixa = rc->ixb = 5; + } + } +} + +static unsigned char * +sendpld_buf(const br_ssl_engine_context *rc, size_t *len) +{ + /* + * Payload data can be injected only if the current mode is + * compatible, and oxa != oxb. + */ + switch (rc->iomode) { + case BR_IO_OUT: + case BR_IO_INOUT: + *len = rc->oxb - rc->oxa; + return (*len == 0) ? NULL : (rc->obuf + rc->oxa); + default: + *len = 0; + return NULL; + } +} + +/* + * If some payload bytes have been accumulated, then wrap them into + * an outgoing record. Otherwise, this function does nothing, unless + * 'force' is non-zero, in which case an empty record is assembled. + * + * The caller must take care not to invoke this function if the engine + * is not currently ready to receive payload bytes to send. + */ +static void +sendpld_flush(br_ssl_engine_context *rc, int force) +{ + size_t xlen; + unsigned char *buf; + + if (rc->oxa == rc->oxb) { + return; + } + xlen = rc->oxa - rc->oxc; + if (xlen == 0 && !force) { + return; + } + buf = rc->out.vtable->encrypt(&rc->out.vtable, + rc->record_type_out, rc->version_out, + rc->obuf + rc->oxc, &xlen); + rc->oxb = rc->oxa = (size_t)(buf - rc->obuf); + rc->oxc = rc->oxa + xlen; +} + +static void +sendpld_ack(br_ssl_engine_context *rc, size_t len) +{ + /* + * If using a shared buffer, then we may have to modify the + * current mode. + */ + if (rc->iomode == BR_IO_INOUT && rc->ibuf == rc->obuf) { + rc->iomode = BR_IO_OUT; + } + rc->oxa += len; + if (rc->oxa >= rc->oxb) { + /* + * Set oxb to one more than oxa so that sendpld_flush() + * does not mistakingly believe that a record is + * already prepared and being sent. + */ + rc->oxb = rc->oxa + 1; + sendpld_flush(rc, 0); + } +} + +static unsigned char * +sendrec_buf(const br_ssl_engine_context *rc, size_t *len) +{ + /* + * When still gathering payload bytes, oxc points to the start + * of the record data, so oxc <= oxa. However, when a full + * record has been completed, oxc points to the end of the record, + * so oxc > oxa. + */ + switch (rc->iomode) { + case BR_IO_OUT: + case BR_IO_INOUT: + if (rc->oxc > rc->oxa) { + *len = rc->oxc - rc->oxa; + return rc->obuf + rc->oxa; + } + break; + } + *len = 0; + return NULL; +} + +static void +sendrec_ack(br_ssl_engine_context *rc, size_t len) +{ + rc->oxb = (rc->oxa += len); + if (rc->oxa == rc->oxc) { + make_ready_out(rc); + } +} + +/* + * Test whether there is some buffered outgoing record that still must + * sent. + */ +static inline int +has_rec_tosend(const br_ssl_engine_context *rc) +{ + return rc->oxa == rc->oxb && rc->oxa != rc->oxc; +} + +/* + * The "no encryption" mode has no overhead. It limits the payload size + * to the maximum size allowed by the standard (16384 bytes); the caller + * is responsible for possibly enforcing a smaller fragment length. + */ +static void +clear_max_plaintext(const br_sslrec_out_clear_context *cc, + size_t *start, size_t *end) +{ + size_t len; + + (void)cc; + len = *end - *start; + if (len > 16384) { + *end = *start + 16384; + } +} + +/* + * In "no encryption" mode, encryption is trivial (a no-operation) so + * we just have to encode the header. + */ +static unsigned char * +clear_encrypt(br_sslrec_out_clear_context *cc, + int record_type, unsigned version, void *data, size_t *data_len) +{ + unsigned char *buf; + + (void)cc; + buf = (unsigned char *)data - 5; + buf[0] = record_type; + br_enc16be(buf + 1, version); + br_enc16be(buf + 3, *data_len); + *data_len += 5; + return buf; +} + +/* see bearssl_ssl.h */ +const br_sslrec_out_class br_sslrec_out_clear_vtable PROGMEM = { + sizeof(br_sslrec_out_clear_context), + (void (*)(const br_sslrec_out_class *const *, size_t *, size_t *)) + &clear_max_plaintext, + (unsigned char *(*)(const br_sslrec_out_class **, + int, unsigned, void *, size_t *)) + &clear_encrypt +}; + +/* ==================================================================== */ +/* + * In this part of the file, we handle the various record types, and + * communications with the handshake processor. + */ + +/* + * IMPLEMENTATION NOTES + * ==================== + * + * The handshake processor is written in T0 and runs as a coroutine. + * It receives the contents of all records except application data, and + * is responsible for producing the contents of all records except + * application data. + * + * A state flag is maintained, which specifies whether application data + * is acceptable or not. When it is set: + * + * -- Application data can be injected as payload data (provided that + * the output buffer is ready for that). + * + * -- Incoming application data records are accepted, and yield data + * that the caller may retrieve. + * + * When the flag is cleared, application data is not accepted from the + * application, and incoming application data records trigger an error. + * + * + * Records of type handshake, alert or change-cipher-spec are handled + * by the handshake processor. The handshake processor is written in T0 + * and runs as a coroutine; it gets invoked whenever one of the following + * situations is reached: + * + * -- An incoming record has type handshake, alert or change-cipher-spec, + * and yields data that can be read (zero-length records are thus + * ignored). + * + * -- An outgoing record has just finished being sent, and the "application + * data" flag is cleared. + * + * -- The caller wishes to perform a close (call to br_ssl_engine_close()). + * + * -- The caller wishes to perform a renegotiation (call to + * br_ssl_engine_renegotiate()). + * + * Whenever the handshake processor is entered, access to the payload + * buffers is provided, along with some information about explicit + * closures or renegotiations. + */ + +/* see bearssl_ssl.h */ +void +br_ssl_engine_set_suites(br_ssl_engine_context *cc, + const uint16_t *suites, size_t suites_num) +{ + if ((suites_num * sizeof *suites) > sizeof cc->suites_buf) { + br_ssl_engine_fail(cc, BR_ERR_BAD_PARAM); + return; + } + memcpy(cc->suites_buf, suites, suites_num * sizeof *suites); + cc->suites_num = suites_num; +} + +/* + * Give control to handshake processor. 'action' is 1 for a close, + * 2 for a renegotiation, or 0 for a jump due to I/O completion. + */ +static void +jump_handshake(br_ssl_engine_context *cc, int action) +{ + /* + * We use a loop because the handshake processor actions may + * allow for more actions; namely, if the processor reads all + * input data, then it may allow for output data to be produced, + * in case of a shared in/out buffer. + */ + for (;;) { + size_t hlen_in, hlen_out; + + /* + * Get input buffer. We do not want to provide + * application data to the handshake processor (we could + * get called with an explicit close or renegotiation + * while there is application data ready to be read). + */ + cc->hbuf_in = recvpld_buf(cc, &hlen_in); + if (cc->hbuf_in != NULL + && cc->record_type_in == BR_SSL_APPLICATION_DATA) + { + hlen_in = 0; + } + + /* + * Get output buffer. The handshake processor never + * leaves an unfinished outgoing record, so if there is + * buffered output, then it MUST be some application + * data, so the processor cannot write to it. + */ + cc->saved_hbuf_out = cc->hbuf_out = sendpld_buf(cc, &hlen_out); + if (cc->hbuf_out != NULL && br_ssl_engine_has_pld_to_send(cc)) { + hlen_out = 0; + } + + /* + * Note: hlen_in and hlen_out can be both non-zero only if + * the input and output buffers are disjoint. Thus, we can + * offer both buffers to the handshake code. + */ + + cc->hlen_in = hlen_in; + cc->hlen_out = hlen_out; + cc->action = action; + cc->hsrun(&cc->cpu); + if (br_ssl_engine_closed(cc)) { + return; + } + if (cc->hbuf_out != cc->saved_hbuf_out) { + sendpld_ack(cc, cc->hbuf_out - cc->saved_hbuf_out); + } + if (hlen_in != cc->hlen_in) { + recvpld_ack(cc, hlen_in - cc->hlen_in); + if (cc->hlen_in == 0) { + /* + * We read all data bytes, which may have + * released the output buffer in case it + * is shared with the input buffer, and + * the handshake code might be waiting for + * that. + */ + action = 0; + continue; + } + } + break; + } +} + +/* see inner.h */ +void +br_ssl_engine_flush_record(br_ssl_engine_context *cc) +{ + if (cc->hbuf_out != cc->saved_hbuf_out) { + sendpld_ack(cc, cc->hbuf_out - cc->saved_hbuf_out); + } + if (br_ssl_engine_has_pld_to_send(cc)) { + sendpld_flush(cc, 0); + } + cc->saved_hbuf_out = cc->hbuf_out = sendpld_buf(cc, &cc->hlen_out); +} + +/* see bearssl_ssl.h */ +unsigned char * +br_ssl_engine_sendapp_buf(const br_ssl_engine_context *cc, size_t *len) +{ + if (!(cc->application_data & 1)) { + *len = 0; + return NULL; + } + return sendpld_buf(cc, len); +} + +/* see bearssl_ssl.h */ +void +br_ssl_engine_sendapp_ack(br_ssl_engine_context *cc, size_t len) +{ + sendpld_ack(cc, len); +} + +/* see bearssl_ssl.h */ +unsigned char * +br_ssl_engine_recvapp_buf(const br_ssl_engine_context *cc, size_t *len) +{ + if (!(cc->application_data & 1) + || cc->record_type_in != BR_SSL_APPLICATION_DATA) + { + *len = 0; + return NULL; + } + return recvpld_buf(cc, len); +} + +/* see bearssl_ssl.h */ +void +br_ssl_engine_recvapp_ack(br_ssl_engine_context *cc, size_t len) +{ + recvpld_ack(cc, len); +} + +/* see bearssl_ssl.h */ +unsigned char * +br_ssl_engine_sendrec_buf(const br_ssl_engine_context *cc, size_t *len) +{ + return sendrec_buf(cc, len); +} + +/* see bearssl_ssl.h */ +void +br_ssl_engine_sendrec_ack(br_ssl_engine_context *cc, size_t len) +{ + sendrec_ack(cc, len); + if (len != 0 && !has_rec_tosend(cc) + && (cc->record_type_out != BR_SSL_APPLICATION_DATA + || (cc->application_data & 1) == 0)) + { + jump_handshake(cc, 0); + } +} + +/* see bearssl_ssl.h */ +unsigned char * +br_ssl_engine_recvrec_buf(const br_ssl_engine_context *cc, size_t *len) +{ + return recvrec_buf(cc, len); +} + +/* see bearssl_ssl.h */ +void +br_ssl_engine_recvrec_ack(br_ssl_engine_context *cc, size_t len) +{ + unsigned char *buf; + + recvrec_ack(cc, len); + if (br_ssl_engine_closed(cc)) { + return; + } + + /* + * We just received some bytes from the peer. This may have + * yielded some payload bytes, in which case we must process + * them according to the record type. + */ + buf = recvpld_buf(cc, &len); + if (buf != NULL) { + switch (cc->record_type_in) { + case BR_SSL_CHANGE_CIPHER_SPEC: + case BR_SSL_ALERT: + case BR_SSL_HANDSHAKE: + jump_handshake(cc, 0); + break; + case BR_SSL_APPLICATION_DATA: + if (cc->application_data == 1) { + break; + } + + /* + * If we are currently closing, and waiting for + * a close_notify from the peer, then incoming + * application data should be discarded. + */ + if (cc->application_data == 2) { + recvpld_ack(cc, len); + break; + } + + /* Fall through */ + default: + br_ssl_engine_fail(cc, BR_ERR_UNEXPECTED); + break; + } + } +} + +/* see bearssl_ssl.h */ +void +br_ssl_engine_close(br_ssl_engine_context *cc) +{ + if (!br_ssl_engine_closed(cc)) { + jump_handshake(cc, 1); + } +} + +/* see bearssl_ssl.h */ +int +br_ssl_engine_renegotiate(br_ssl_engine_context *cc) +{ + size_t len; + + if (br_ssl_engine_closed(cc) || cc->reneg == 1 + || (cc->flags & BR_OPT_NO_RENEGOTIATION) != 0 + || br_ssl_engine_recvapp_buf(cc, &len) != NULL) + { + return 0; + } + jump_handshake(cc, 2); + return 1; +} + +/* see bearssl.h */ +unsigned +br_ssl_engine_current_state(const br_ssl_engine_context *cc) +{ + unsigned s; + size_t len; + + if (br_ssl_engine_closed(cc)) { + return BR_SSL_CLOSED; + } + + s = 0; + if (br_ssl_engine_sendrec_buf(cc, &len) != NULL) { + s |= BR_SSL_SENDREC; + } + if (br_ssl_engine_recvrec_buf(cc, &len) != NULL) { + s |= BR_SSL_RECVREC; + } + if (br_ssl_engine_sendapp_buf(cc, &len) != NULL) { + s |= BR_SSL_SENDAPP; + } + if (br_ssl_engine_recvapp_buf(cc, &len) != NULL) { + s |= BR_SSL_RECVAPP; + } + return s; +} + +/* see bearssl_ssl.h */ +void +br_ssl_engine_flush(br_ssl_engine_context *cc, int force) +{ + if (!br_ssl_engine_closed(cc) && (cc->application_data & 1) != 0) { + sendpld_flush(cc, force); + } +} + +/* see inner.h */ +void +br_ssl_engine_hs_reset(br_ssl_engine_context *cc, + void (*hsinit)(void *), void (*hsrun)(void *)) +{ + engine_clearbuf(cc); + cc->cpu.dp = cc->dp_stack; + cc->cpu.rp = cc->rp_stack; + hsinit(&cc->cpu); + cc->hsrun = hsrun; + cc->shutdown_recv = 0; + cc->application_data = 0; + cc->alert = 0; + jump_handshake(cc, 0); +} + +/* see inner.h */ +br_tls_prf_impl +br_ssl_engine_get_PRF(br_ssl_engine_context *cc, int prf_id) +{ + if (cc->session.version >= BR_TLS12) { + if (prf_id == br_sha384_ID) { + return cc->prf_sha384; + } else { + return cc->prf_sha256; + } + } else { + return cc->prf10; + } +} + +/* see inner.h */ +void +br_ssl_engine_compute_master(br_ssl_engine_context *cc, + int prf_id, const void *pms, size_t pms_len) +{ + br_tls_prf_impl iprf; + br_tls_prf_seed_chunk seed[2] = { + { cc->client_random, sizeof cc->client_random }, + { cc->server_random, sizeof cc->server_random } + }; + + iprf = br_ssl_engine_get_PRF(cc, prf_id); + iprf(cc->session.master_secret, sizeof cc->session.master_secret, + pms, pms_len, "master secret", 2, seed); +} + +/* + * Compute key block. + */ +static void +compute_key_block(br_ssl_engine_context *cc, int prf_id, + size_t half_len, unsigned char *kb) +{ + br_tls_prf_impl iprf; + br_tls_prf_seed_chunk seed[2] = { + { cc->server_random, sizeof cc->server_random }, + { cc->client_random, sizeof cc->client_random } + }; + + iprf = br_ssl_engine_get_PRF(cc, prf_id); + iprf(kb, half_len << 1, + cc->session.master_secret, sizeof cc->session.master_secret, + "key expansion", 2, seed); +} + +/* see inner.h */ +void +br_ssl_engine_switch_cbc_in(br_ssl_engine_context *cc, + int is_client, int prf_id, int mac_id, + const br_block_cbcdec_class *bc_impl, size_t cipher_key_len) +{ + unsigned char kb[192]; + unsigned char *cipher_key, *mac_key, *iv; + const br_hash_class *imh; + size_t mac_key_len, mac_out_len, iv_len; + + imh = br_ssl_engine_get_hash(cc, mac_id); + mac_out_len = (imh->desc >> BR_HASHDESC_OUT_OFF) & BR_HASHDESC_OUT_MASK; + mac_key_len = mac_out_len; + + /* + * TLS 1.1+ uses per-record explicit IV, so no IV to generate here. + */ + if (cc->session.version >= BR_TLS11) { + iv_len = 0; + } else { + iv_len = bc_impl->block_size; + } + compute_key_block(cc, prf_id, + mac_key_len + cipher_key_len + iv_len, kb); + if (is_client) { + mac_key = &kb[mac_key_len]; + cipher_key = &kb[(mac_key_len << 1) + cipher_key_len]; + iv = &kb[((mac_key_len + cipher_key_len) << 1) + iv_len]; + } else { + mac_key = &kb[0]; + cipher_key = &kb[mac_key_len << 1]; + iv = &kb[(mac_key_len + cipher_key_len) << 1]; + } + if (iv_len == 0) { + iv = NULL; + } + cc->icbc_in->init(&cc->in.cbc.vtable, + bc_impl, cipher_key, cipher_key_len, + imh, mac_key, mac_key_len, mac_out_len, iv); + cc->incrypt = 1; +} + +/* see inner.h */ +void +br_ssl_engine_switch_cbc_out(br_ssl_engine_context *cc, + int is_client, int prf_id, int mac_id, + const br_block_cbcenc_class *bc_impl, size_t cipher_key_len) +{ + unsigned char kb[192]; + unsigned char *cipher_key, *mac_key, *iv; + const br_hash_class *imh; + size_t mac_key_len, mac_out_len, iv_len; + + imh = br_ssl_engine_get_hash(cc, mac_id); + mac_out_len = (imh->desc >> BR_HASHDESC_OUT_OFF) & BR_HASHDESC_OUT_MASK; + mac_key_len = mac_out_len; + + /* + * TLS 1.1+ uses per-record explicit IV, so no IV to generate here. + */ + if (cc->session.version >= BR_TLS11) { + iv_len = 0; + } else { + iv_len = bc_impl->block_size; + } + compute_key_block(cc, prf_id, + mac_key_len + cipher_key_len + iv_len, kb); + if (is_client) { + mac_key = &kb[0]; + cipher_key = &kb[mac_key_len << 1]; + iv = &kb[(mac_key_len + cipher_key_len) << 1]; + } else { + mac_key = &kb[mac_key_len]; + cipher_key = &kb[(mac_key_len << 1) + cipher_key_len]; + iv = &kb[((mac_key_len + cipher_key_len) << 1) + iv_len]; + } + if (iv_len == 0) { + iv = NULL; + } + cc->icbc_out->init(&cc->out.cbc.vtable, + bc_impl, cipher_key, cipher_key_len, + imh, mac_key, mac_key_len, mac_out_len, iv); +} + +/* see inner.h */ +void +br_ssl_engine_switch_gcm_in(br_ssl_engine_context *cc, + int is_client, int prf_id, + const br_block_ctr_class *bc_impl, size_t cipher_key_len) +{ + unsigned char kb[72]; + unsigned char *cipher_key, *iv; + + compute_key_block(cc, prf_id, cipher_key_len + 4, kb); + if (is_client) { + cipher_key = &kb[cipher_key_len]; + iv = &kb[(cipher_key_len << 1) + 4]; + } else { + cipher_key = &kb[0]; + iv = &kb[cipher_key_len << 1]; + } + cc->igcm_in->init(&cc->in.gcm.vtable.in, + bc_impl, cipher_key, cipher_key_len, cc->ighash, iv); + cc->incrypt = 1; +} + +/* see inner.h */ +void +br_ssl_engine_switch_gcm_out(br_ssl_engine_context *cc, + int is_client, int prf_id, + const br_block_ctr_class *bc_impl, size_t cipher_key_len) +{ + unsigned char kb[72]; + unsigned char *cipher_key, *iv; + + compute_key_block(cc, prf_id, cipher_key_len + 4, kb); + if (is_client) { + cipher_key = &kb[0]; + iv = &kb[cipher_key_len << 1]; + } else { + cipher_key = &kb[cipher_key_len]; + iv = &kb[(cipher_key_len << 1) + 4]; + } + cc->igcm_out->init(&cc->out.gcm.vtable.out, + bc_impl, cipher_key, cipher_key_len, cc->ighash, iv); +} + +/* see inner.h */ +void +br_ssl_engine_switch_chapol_in(br_ssl_engine_context *cc, + int is_client, int prf_id) +{ + unsigned char kb[88]; + unsigned char *cipher_key, *iv; + + compute_key_block(cc, prf_id, 44, kb); + if (is_client) { + cipher_key = &kb[32]; + iv = &kb[76]; + } else { + cipher_key = &kb[0]; + iv = &kb[64]; + } + cc->ichapol_in->init(&cc->in.chapol.vtable.in, + cc->ichacha, cc->ipoly, cipher_key, iv); + cc->incrypt = 1; +} + +/* see inner.h */ +void +br_ssl_engine_switch_chapol_out(br_ssl_engine_context *cc, + int is_client, int prf_id) +{ + unsigned char kb[88]; + unsigned char *cipher_key, *iv; + + compute_key_block(cc, prf_id, 44, kb); + if (is_client) { + cipher_key = &kb[0]; + iv = &kb[64]; + } else { + cipher_key = &kb[32]; + iv = &kb[76]; + } + cc->ichapol_out->init(&cc->out.chapol.vtable.out, + cc->ichacha, cc->ipoly, cipher_key, iv); +} + +/* see inner.h */ +void +br_ssl_engine_switch_ccm_in(br_ssl_engine_context *cc, + int is_client, int prf_id, + const br_block_ctrcbc_class *bc_impl, + size_t cipher_key_len, size_t tag_len) +{ + unsigned char kb[72]; + unsigned char *cipher_key, *iv; + + compute_key_block(cc, prf_id, cipher_key_len + 4, kb); + if (is_client) { + cipher_key = &kb[cipher_key_len]; + iv = &kb[(cipher_key_len << 1) + 4]; + } else { + cipher_key = &kb[0]; + iv = &kb[cipher_key_len << 1]; + } + cc->iccm_in->init(&cc->in.ccm.vtable.in, + bc_impl, cipher_key, cipher_key_len, iv, tag_len); + cc->incrypt = 1; +} + +/* see inner.h */ +void +br_ssl_engine_switch_ccm_out(br_ssl_engine_context *cc, + int is_client, int prf_id, + const br_block_ctrcbc_class *bc_impl, + size_t cipher_key_len, size_t tag_len) +{ + unsigned char kb[72]; + unsigned char *cipher_key, *iv; + + compute_key_block(cc, prf_id, cipher_key_len + 4, kb); + if (is_client) { + cipher_key = &kb[0]; + iv = &kb[cipher_key_len << 1]; + } else { + cipher_key = &kb[cipher_key_len]; + iv = &kb[(cipher_key_len << 1) + 4]; + } + cc->iccm_out->init(&cc->out.ccm.vtable.out, + bc_impl, cipher_key, cipher_key_len, iv, tag_len); +} diff --git a/lib/bearssl-esp8266/src/ssl/ssl_engine_default_aescbc.c b/lib/bearssl-esp8266/src/ssl/ssl_engine_default_aescbc.c new file mode 100644 index 000000000..8a257e16b --- /dev/null +++ b/lib/bearssl-esp8266/src/ssl/ssl_engine_default_aescbc.c @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_ssl.h */ +void +br_ssl_engine_set_default_aes_cbc(br_ssl_engine_context *cc) +{ +#if BR_AES_X86NI || BR_POWER8 + const br_block_cbcenc_class *ienc; + const br_block_cbcdec_class *idec; +#endif + + br_ssl_engine_set_cbc(cc, + &br_sslrec_in_cbc_vtable, + &br_sslrec_out_cbc_vtable); +#if BR_AES_X86NI + ienc = br_aes_x86ni_cbcenc_get_vtable(); + idec = br_aes_x86ni_cbcdec_get_vtable(); + if (ienc != NULL && idec != NULL) { + br_ssl_engine_set_aes_cbc(cc, ienc, idec); + return; + } +#endif +#if BR_POWER8 + ienc = br_aes_pwr8_cbcenc_get_vtable(); + idec = br_aes_pwr8_cbcdec_get_vtable(); + if (ienc != NULL && idec != NULL) { + br_ssl_engine_set_aes_cbc(cc, ienc, idec); + return; + } +#endif +#if BR_64 + br_ssl_engine_set_aes_cbc(cc, + &br_aes_ct64_cbcenc_vtable, + &br_aes_ct64_cbcdec_vtable); +#else + br_ssl_engine_set_aes_cbc(cc, + &br_aes_ct_cbcenc_vtable, + &br_aes_ct_cbcdec_vtable); +#endif +} diff --git a/lib/bearssl-esp8266/src/ssl/ssl_engine_default_aesccm.c b/lib/bearssl-esp8266/src/ssl/ssl_engine_default_aesccm.c new file mode 100644 index 000000000..815458fc3 --- /dev/null +++ b/lib/bearssl-esp8266/src/ssl/ssl_engine_default_aesccm.c @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_ssl.h */ +void +br_ssl_engine_set_default_aes_ccm(br_ssl_engine_context *cc) +{ +#if BR_AES_X86NI || BR_POWER8 + const br_block_ctrcbc_class *ictrcbc; +#endif + + br_ssl_engine_set_ccm(cc, + &br_sslrec_in_ccm_vtable, + &br_sslrec_out_ccm_vtable); +#if BR_AES_X86NI + ictrcbc = br_aes_x86ni_ctrcbc_get_vtable(); + if (ictrcbc != NULL) { + br_ssl_engine_set_aes_ctrcbc(cc, ictrcbc); + } else { +#if BR_64 + br_ssl_engine_set_aes_ctrcbc(cc, &br_aes_ct64_ctrcbc_vtable); +#else + br_ssl_engine_set_aes_ctrcbc(cc, &br_aes_ct_ctrcbc_vtable); +#endif + } +#elif BR_POWER8 + ictrcbc = br_aes_pwr8_ctrcbc_get_vtable(); + if (ictrcbc != NULL) { + br_ssl_engine_set_aes_ctrcbc(cc, ictrcbc); + } else { +#if BR_64 + br_ssl_engine_set_aes_ctrcbc(cc, &br_aes_ct64_ctrcbc_vtable); +#else + br_ssl_engine_set_aes_ctrcbc(cc, &br_aes_ct_ctrcbc_vtable); +#endif + } +#else +#if BR_64 + br_ssl_engine_set_aes_ctrcbc(cc, &br_aes_ct64_ctrcbc_vtable); +#else + br_ssl_engine_set_aes_ctrcbc(cc, &br_aes_ct_ctrcbc_vtable); +#endif +#endif +} diff --git a/lib/bearssl-esp8266/src/ssl/ssl_engine_default_aesgcm.c b/lib/bearssl-esp8266/src/ssl/ssl_engine_default_aesgcm.c new file mode 100644 index 000000000..0f0220b08 --- /dev/null +++ b/lib/bearssl-esp8266/src/ssl/ssl_engine_default_aesgcm.c @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_ssl.h */ +void +br_ssl_engine_set_default_aes_gcm(br_ssl_engine_context *cc) +{ +#if BR_AES_X86NI || BR_POWER8 + const br_block_ctr_class *ictr; + br_ghash ighash; +#endif + + br_ssl_engine_set_gcm(cc, + &br_sslrec_in_gcm_vtable, + &br_sslrec_out_gcm_vtable); +#if BR_AES_X86NI + ictr = br_aes_x86ni_ctr_get_vtable(); + if (ictr != NULL) { + br_ssl_engine_set_aes_ctr(cc, ictr); + } else { +#if BR_64 + br_ssl_engine_set_aes_ctr(cc, &br_aes_ct64_ctr_vtable); +#else + br_ssl_engine_set_aes_ctr(cc, &br_aes_ct_ctr_vtable); +#endif + } +#elif BR_POWER8 + ictr = br_aes_pwr8_ctr_get_vtable(); + if (ictr != NULL) { + br_ssl_engine_set_aes_ctr(cc, ictr); + } else { +#if BR_64 + br_ssl_engine_set_aes_ctr(cc, &br_aes_ct64_ctr_vtable); +#else + br_ssl_engine_set_aes_ctr(cc, &br_aes_ct_ctr_vtable); +#endif + } +#else +#if BR_64 + br_ssl_engine_set_aes_ctr(cc, &br_aes_ct64_ctr_vtable); +#else + br_ssl_engine_set_aes_ctr(cc, &br_aes_ct_ctr_vtable); +#endif +#endif +#if BR_AES_X86NI + ighash = br_ghash_pclmul_get(); + if (ighash != 0) { + br_ssl_engine_set_ghash(cc, ighash); + return; + } +#endif +#if BR_POWER8 + ighash = br_ghash_pwr8_get(); + if (ighash != 0) { + br_ssl_engine_set_ghash(cc, ighash); + return; + } +#endif +#if BR_LOMUL + br_ssl_engine_set_ghash(cc, &br_ghash_ctmul32); +#elif BR_64 + br_ssl_engine_set_ghash(cc, &br_ghash_ctmul64); +#else + br_ssl_engine_set_ghash(cc, &br_ghash_ctmul); +#endif +} diff --git a/lib/bearssl-esp8266/src/ssl/ssl_engine_default_chapol.c b/lib/bearssl-esp8266/src/ssl/ssl_engine_default_chapol.c new file mode 100644 index 000000000..2a49095f1 --- /dev/null +++ b/lib/bearssl-esp8266/src/ssl/ssl_engine_default_chapol.c @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_ssl.h */ +void +br_ssl_engine_set_default_chapol(br_ssl_engine_context *cc) +{ +#if BR_INT128 || BR_UMUL128 + br_poly1305_run bp; +#endif +#if BR_SSE2 + br_chacha20_run bc; +#endif + + br_ssl_engine_set_chapol(cc, + &br_sslrec_in_chapol_vtable, + &br_sslrec_out_chapol_vtable); +#if BR_SSE2 + bc = br_chacha20_sse2_get(); + if (bc) { + br_ssl_engine_set_chacha20(cc, bc); + } else { +#endif + br_ssl_engine_set_chacha20(cc, &br_chacha20_ct_run); +#if BR_SSE2 + } +#endif +#if BR_INT128 || BR_UMUL128 + bp = br_poly1305_ctmulq_get(); + if (bp) { + br_ssl_engine_set_poly1305(cc, bp); + } else { +#endif +#if BR_LOMUL + br_ssl_engine_set_poly1305(cc, &br_poly1305_ctmul32_run); +#else + br_ssl_engine_set_poly1305(cc, &br_poly1305_ctmul_run); +#endif +#if BR_INT128 || BR_UMUL128 + } +#endif +} diff --git a/lib/bearssl-esp8266/src/ssl/ssl_engine_default_descbc.c b/lib/bearssl-esp8266/src/ssl/ssl_engine_default_descbc.c new file mode 100644 index 000000000..b747d5467 --- /dev/null +++ b/lib/bearssl-esp8266/src/ssl/ssl_engine_default_descbc.c @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_ssl.h */ +void +br_ssl_engine_set_default_des_cbc(br_ssl_engine_context *cc) +{ + br_ssl_engine_set_cbc(cc, + &br_sslrec_in_cbc_vtable, + &br_sslrec_out_cbc_vtable); + br_ssl_engine_set_des_cbc(cc, + &br_des_ct_cbcenc_vtable, + &br_des_ct_cbcdec_vtable); +} diff --git a/lib/bearssl-esp8266/src/ssl/ssl_engine_default_ec.c b/lib/bearssl-esp8266/src/ssl/ssl_engine_default_ec.c new file mode 100644 index 000000000..2ce4f38c7 --- /dev/null +++ b/lib/bearssl-esp8266/src/ssl/ssl_engine_default_ec.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_ssl.h */ +void +br_ssl_engine_set_default_ec(br_ssl_engine_context *cc) +{ +#if BR_LOMUL + br_ssl_engine_set_ec(cc, &br_ec_all_m15); +#else + br_ssl_engine_set_ec(cc, &br_ec_all_m31); +#endif +} diff --git a/lib/bearssl-esp8266/src/ssl/ssl_engine_default_ecdsa.c b/lib/bearssl-esp8266/src/ssl/ssl_engine_default_ecdsa.c new file mode 100644 index 000000000..26edd91d5 --- /dev/null +++ b/lib/bearssl-esp8266/src/ssl/ssl_engine_default_ecdsa.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_ssl.h */ +void +br_ssl_engine_set_default_ecdsa(br_ssl_engine_context *cc) +{ +#if BR_LOMUL + br_ssl_engine_set_ec(cc, &br_ec_all_m15); + br_ssl_engine_set_ecdsa(cc, &br_ecdsa_i15_vrfy_asn1); +#else + br_ssl_engine_set_ec(cc, &br_ec_all_m31); + br_ssl_engine_set_ecdsa(cc, &br_ecdsa_i31_vrfy_asn1); +#endif +} diff --git a/lib/bearssl-esp8266/src/ssl/ssl_engine_default_rsavrfy.c b/lib/bearssl-esp8266/src/ssl/ssl_engine_default_rsavrfy.c new file mode 100644 index 000000000..7ab55f042 --- /dev/null +++ b/lib/bearssl-esp8266/src/ssl/ssl_engine_default_rsavrfy.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_ssl.h */ +void +br_ssl_engine_set_default_rsavrfy(br_ssl_engine_context *cc) +{ + br_ssl_engine_set_rsavrfy(cc, br_rsa_pkcs1_vrfy_get_default()); +} diff --git a/lib/bearssl-esp8266/src/ssl/ssl_hashes.c b/lib/bearssl-esp8266/src/ssl/ssl_hashes.c new file mode 100644 index 000000000..d031f65c8 --- /dev/null +++ b/lib/bearssl-esp8266/src/ssl/ssl_hashes.c @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +int +br_ssl_choose_hash(unsigned bf) +{ + static const unsigned char pref[] = { + br_sha256_ID, br_sha384_ID, br_sha512_ID, + br_sha224_ID, br_sha1_ID + }; + size_t u; + + for (u = 0; u < sizeof pref; u ++) { + int x; + + x = pref[u]; + if ((bf >> x) & 1) { + return x; + } + } + return 0; +} diff --git a/lib/bearssl-esp8266/src/ssl/ssl_hs_client.c b/lib/bearssl-esp8266/src/ssl/ssl_hs_client.c new file mode 100644 index 000000000..3f883a17b --- /dev/null +++ b/lib/bearssl-esp8266/src/ssl/ssl_hs_client.c @@ -0,0 +1,1927 @@ +/* Automatically generated code; do not modify directly. */ + +#include +#include +#include + +typedef struct { + uint32_t *dp; + uint32_t *rp; + const unsigned char *ip; +} t0_context; + +static uint32_t +t0_parse7E_unsigned(const unsigned char **p) +{ + uint32_t x; + + x = 0; + for (;;) { + unsigned y; + + y = pgm_read_byte((*p)++); + x = (x << 7) | (uint32_t)(y & 0x7F); + if (y < 0x80) { + return x; + } + } +} + +static int32_t +t0_parse7E_signed(const unsigned char **p) +{ + int neg; + uint32_t x; + + neg = (pgm_read_byte(*p) >> 6) & 1; + x = (uint32_t)-neg; + for (;;) { + unsigned y; + + y = pgm_read_byte((*p)++); + x = (x << 7) | (uint32_t)(y & 0x7F); + if (y < 0x80) { + if (neg) { + return -(int32_t)~x - 1; + } else { + return (int32_t)x; + } + } + } +} + +#define T0_VBYTE(x, n) (unsigned char)((((uint32_t)(x) >> (n)) & 0x7F) | 0x80) +#define T0_FBYTE(x, n) (unsigned char)(((uint32_t)(x) >> (n)) & 0x7F) +#define T0_SBYTE(x) (unsigned char)((((uint32_t)(x) >> 28) + 0xF8) ^ 0xF8) +#define T0_INT1(x) T0_FBYTE(x, 0) +#define T0_INT2(x) T0_VBYTE(x, 7), T0_FBYTE(x, 0) +#define T0_INT3(x) T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0) +#define T0_INT4(x) T0_VBYTE(x, 21), T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0) +#define T0_INT5(x) T0_SBYTE(x), T0_VBYTE(x, 21), T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0) + +/* static const unsigned char t0_datablock[]; */ + + +void br_ssl_hs_client_init_main(void *t0ctx); + +void br_ssl_hs_client_run(void *t0ctx); + + + +#include +#include + +#include "t_inner.h" + +/* + * This macro evaluates to a pointer to the current engine context. + */ +#define ENG ((br_ssl_engine_context *)(void *)((unsigned char *)t0ctx - offsetof(br_ssl_engine_context, cpu))) + + + + + +/* + * This macro evaluates to a pointer to the client context, under that + * specific name. It must be noted that since the engine context is the + * first field of the br_ssl_client_context structure ('eng'), then + * pointers values of both types are interchangeable, modulo an + * appropriate cast. This also means that "addresses" computed as offsets + * within the structure work for both kinds of context. + */ +#define CTX ((br_ssl_client_context *)ENG) + +/* + * Generate the pre-master secret for RSA key exchange, and encrypt it + * with the server's public key. Returned value is either the encrypted + * data length (in bytes), or -x on error, with 'x' being an error code. + * + * This code assumes that the public key has been already verified (it + * was properly obtained by the X.509 engine, and it has the right type, + * i.e. it is of type RSA and suitable for encryption). + */ +static int +make_pms_rsa(br_ssl_client_context *ctx, int prf_id) +{ + const br_x509_class **xc; + const br_x509_pkey *pk; + const unsigned char *n; + unsigned char *pms; + size_t nlen, u; + + xc = ctx->eng.x509ctx; + pk = (*xc)->get_pkey(xc, NULL); + + /* + * Compute actual RSA key length, in case there are leading zeros. + */ + n = pk->key.rsa.n; + nlen = pk->key.rsa.nlen; + while (nlen > 0 && *n == 0) { + n ++; + nlen --; + } + + /* + * We need at least 59 bytes (48 bytes for pre-master secret, and + * 11 bytes for the PKCS#1 type 2 padding). Note that the X.509 + * minimal engine normally blocks RSA keys shorter than 128 bytes, + * so this is mostly for public keys provided explicitly by the + * caller. + */ + if (nlen < 59) { + return -BR_ERR_X509_WEAK_PUBLIC_KEY; + } + if (nlen > sizeof ctx->eng.pad) { + return -BR_ERR_LIMIT_EXCEEDED; + } + + /* + * Make PMS. + */ + pms = ctx->eng.pad + nlen - 48; + br_enc16be(pms, ctx->eng.version_max); + br_hmac_drbg_generate(&ctx->eng.rng, pms + 2, 46); + br_ssl_engine_compute_master(&ctx->eng, prf_id, pms, 48); + + /* + * Apply PKCS#1 type 2 padding. + */ + ctx->eng.pad[0] = 0x00; + ctx->eng.pad[1] = 0x02; + ctx->eng.pad[nlen - 49] = 0x00; + br_hmac_drbg_generate(&ctx->eng.rng, ctx->eng.pad + 2, nlen - 51); + for (u = 2; u < nlen - 49; u ++) { + while (ctx->eng.pad[u] == 0) { + br_hmac_drbg_generate(&ctx->eng.rng, + &ctx->eng.pad[u], 1); + } + } + + /* + * Compute RSA encryption. + */ + if (!ctx->irsapub(ctx->eng.pad, nlen, &pk->key.rsa)) { + return -BR_ERR_LIMIT_EXCEEDED; + } + return (int)nlen; +} + +/* + * OID for hash functions in RSA signatures. + */ +static const unsigned char *HASH_OID[] PROGMEM = { + BR_HASH_OID_SHA1, + BR_HASH_OID_SHA224, + BR_HASH_OID_SHA256, + BR_HASH_OID_SHA384, + BR_HASH_OID_SHA512 +}; + +/* + * Check the RSA signature on the ServerKeyExchange message. + * + * hash hash function ID (2 to 6), or 0 for MD5+SHA-1 (with RSA only) + * use_rsa non-zero for RSA signature, zero for ECDSA + * sig_len signature length (in bytes); signature value is in the pad + * + * Returned value is 0 on success, or an error code. + */ +static int +verify_SKE_sig(br_ssl_client_context *ctx, + int hash, int use_rsa, size_t sig_len) +{ + const br_x509_class **xc; + const br_x509_pkey *pk; + br_multihash_context mhc; + unsigned char hv[64], head[4]; + size_t hv_len; + + xc = ctx->eng.x509ctx; + pk = (*xc)->get_pkey(xc, NULL); + br_multihash_zero(&mhc); + br_multihash_copyimpl(&mhc, &ctx->eng.mhash); + br_multihash_init(&mhc); + br_multihash_update(&mhc, + ctx->eng.client_random, sizeof ctx->eng.client_random); + br_multihash_update(&mhc, + ctx->eng.server_random, sizeof ctx->eng.server_random); + head[0] = 3; + head[1] = 0; + head[2] = ctx->eng.ecdhe_curve; + head[3] = ctx->eng.ecdhe_point_len; + br_multihash_update(&mhc, head, sizeof head); + br_multihash_update(&mhc, + ctx->eng.ecdhe_point, ctx->eng.ecdhe_point_len); + if (hash) { + hv_len = br_multihash_out(&mhc, hash, hv); + if (hv_len == 0) { + return BR_ERR_INVALID_ALGORITHM; + } + } else { + if (!br_multihash_out(&mhc, br_md5_ID, hv) + || !br_multihash_out(&mhc, br_sha1_ID, hv + 16)) + { + return BR_ERR_INVALID_ALGORITHM; + } + hv_len = 36; + } + if (use_rsa) { + unsigned char tmp[64]; + const unsigned char *hash_oid; + + if (hash) { + hash_oid = HASH_OID[hash - 2]; + } else { + hash_oid = NULL; + } + if (!ctx->eng.irsavrfy(ctx->eng.pad, sig_len, + hash_oid, hv_len, &pk->key.rsa, tmp) + || memcmp(tmp, hv, hv_len) != 0) + { + return BR_ERR_BAD_SIGNATURE; + } + } else { + if (!ctx->eng.iecdsa(ctx->eng.iec, hv, hv_len, &pk->key.ec, + ctx->eng.pad, sig_len)) + { + return BR_ERR_BAD_SIGNATURE; + } + } + return 0; +} + +/* + * Perform client-side ECDH (or ECDHE). The point that should be sent to + * the server is written in the pad; returned value is either the point + * length (in bytes), or -x on error, with 'x' being an error code. + * + * The point _from_ the server is taken from ecdhe_point[] if 'ecdhe' + * is non-zero, or from the X.509 engine context if 'ecdhe' is zero + * (for static ECDH). + */ +static int +make_pms_ecdh(br_ssl_client_context *ctx, unsigned ecdhe, int prf_id) +{ + int curve; + unsigned char key[66], point[133]; + const unsigned char *order, *point_src; + size_t glen, olen, point_len, xoff, xlen; + unsigned char mask; + + if (ecdhe) { + curve = ctx->eng.ecdhe_curve; + point_src = ctx->eng.ecdhe_point; + point_len = ctx->eng.ecdhe_point_len; + } else { + const br_x509_class **xc; + const br_x509_pkey *pk; + + xc = ctx->eng.x509ctx; + pk = (*xc)->get_pkey(xc, NULL); + curve = pk->key.ec.curve; + point_src = pk->key.ec.q; + point_len = pk->key.ec.qlen; + } + if ((ctx->eng.iec->supported_curves & ((uint32_t)1 << curve)) == 0) { + return -BR_ERR_INVALID_ALGORITHM; + } + + /* + * We need to generate our key, as a non-zero random value which + * is lower than the curve order, in a "large enough" range. We + * force top bit to 0 and bottom bit to 1, which guarantees that + * the value is in the proper range. + */ + order = ctx->eng.iec->order(curve, &olen); + mask = 0xFF; + while (mask >= pgm_read_byte(&order[0])) { + mask >>= 1; + } + br_hmac_drbg_generate(&ctx->eng.rng, key, olen); + key[0] &= mask; + key[olen - 1] |= 0x01; + + /* + * Compute the common ECDH point, whose X coordinate is the + * pre-master secret. + */ + ctx->eng.iec->generator(curve, &glen); + if (glen != point_len) { + return -BR_ERR_INVALID_ALGORITHM; + } + + memcpy_P(point, point_src, glen); + if (!ctx->eng.iec->mul(point, glen, key, olen, curve)) { + return -BR_ERR_INVALID_ALGORITHM; + } + + /* + * The pre-master secret is the X coordinate. + */ + xoff = ctx->eng.iec->xoff(curve, &xlen); + br_ssl_engine_compute_master(&ctx->eng, prf_id, point + xoff, xlen); + + ctx->eng.iec->mulgen(point, key, olen, curve); + memcpy(ctx->eng.pad, point, glen); + return (int)glen; +} + +/* + * Perform full static ECDH. This occurs only in the context of client + * authentication with certificates: the server uses an EC public key, + * the cipher suite is of type ECDH (not ECDHE), the server requested a + * client certificate and accepts static ECDH, the client has a + * certificate with an EC public key in the same curve, and accepts + * static ECDH as well. + * + * Returned value is 0 on success, -1 on error. + */ +static int +make_pms_static_ecdh(br_ssl_client_context *ctx, int prf_id) +{ + unsigned char point[133]; + size_t point_len; + const br_x509_class **xc; + const br_x509_pkey *pk; + + xc = ctx->eng.x509ctx; + pk = (*xc)->get_pkey(xc, NULL); + point_len = pk->key.ec.qlen; + if (point_len > sizeof point) { + return -1; + } + memcpy(point, pk->key.ec.q, point_len); + if (!(*ctx->client_auth_vtable)->do_keyx( + ctx->client_auth_vtable, point, &point_len)) + { + return -1; + } + br_ssl_engine_compute_master(&ctx->eng, + prf_id, point, point_len); + return 0; +} + +/* + * Compute the client-side signature. This is invoked only when a + * signature-based client authentication was selected. The computed + * signature is in the pad; its length (in bytes) is returned. On + * error, 0 is returned. + */ +static size_t +make_client_sign(br_ssl_client_context *ctx) +{ + size_t hv_len; + + /* + * Compute hash of handshake messages so far. This "cannot" fail + * because the list of supported hash functions provided to the + * client certificate handler was trimmed to include only the + * hash functions that the multi-hasher supports. + */ + if (ctx->hash_id) { + hv_len = br_multihash_out(&ctx->eng.mhash, + ctx->hash_id, ctx->eng.pad); + } else { + br_multihash_out(&ctx->eng.mhash, + br_md5_ID, ctx->eng.pad); + br_multihash_out(&ctx->eng.mhash, + br_sha1_ID, ctx->eng.pad + 16); + hv_len = 36; + } + return (*ctx->client_auth_vtable)->do_sign( + ctx->client_auth_vtable, ctx->hash_id, hv_len, + ctx->eng.pad, sizeof ctx->eng.pad); +} + + + +static const unsigned char t0_datablock[] PROGMEM = { + + 0x00, 0x00, 0x0A, 0x00, 0x24, 0x00, 0x2F, 0x01, 0x24, 0x00, 0x35, 0x02, + 0x24, 0x00, 0x3C, 0x01, 0x44, 0x00, 0x3D, 0x02, 0x44, 0x00, 0x9C, 0x03, + 0x04, 0x00, 0x9D, 0x04, 0x05, 0xC0, 0x03, 0x40, 0x24, 0xC0, 0x04, 0x41, + 0x24, 0xC0, 0x05, 0x42, 0x24, 0xC0, 0x08, 0x20, 0x24, 0xC0, 0x09, 0x21, + 0x24, 0xC0, 0x0A, 0x22, 0x24, 0xC0, 0x0D, 0x30, 0x24, 0xC0, 0x0E, 0x31, + 0x24, 0xC0, 0x0F, 0x32, 0x24, 0xC0, 0x12, 0x10, 0x24, 0xC0, 0x13, 0x11, + 0x24, 0xC0, 0x14, 0x12, 0x24, 0xC0, 0x23, 0x21, 0x44, 0xC0, 0x24, 0x22, + 0x55, 0xC0, 0x25, 0x41, 0x44, 0xC0, 0x26, 0x42, 0x55, 0xC0, 0x27, 0x11, + 0x44, 0xC0, 0x28, 0x12, 0x55, 0xC0, 0x29, 0x31, 0x44, 0xC0, 0x2A, 0x32, + 0x55, 0xC0, 0x2B, 0x23, 0x04, 0xC0, 0x2C, 0x24, 0x05, 0xC0, 0x2D, 0x43, + 0x04, 0xC0, 0x2E, 0x44, 0x05, 0xC0, 0x2F, 0x13, 0x04, 0xC0, 0x30, 0x14, + 0x05, 0xC0, 0x31, 0x33, 0x04, 0xC0, 0x32, 0x34, 0x05, 0xC0, 0x9C, 0x06, + 0x04, 0xC0, 0x9D, 0x07, 0x04, 0xC0, 0xA0, 0x08, 0x04, 0xC0, 0xA1, 0x09, + 0x04, 0xC0, 0xAC, 0x26, 0x04, 0xC0, 0xAD, 0x27, 0x04, 0xC0, 0xAE, 0x28, + 0x04, 0xC0, 0xAF, 0x29, 0x04, 0xCC, 0xA8, 0x15, 0x04, 0xCC, 0xA9, 0x25, + 0x04, 0x00, 0x00 +}; + +static const unsigned char t0_codeblock[] PROGMEM = { + + 0x00, 0x01, 0x00, 0x0A, 0x00, 0x00, 0x01, 0x00, 0x0D, 0x00, 0x00, 0x01, + 0x00, 0x0E, 0x00, 0x00, 0x01, 0x00, 0x0F, 0x00, 0x00, 0x01, 0x01, 0x08, + 0x00, 0x00, 0x01, 0x01, 0x09, 0x00, 0x00, 0x01, 0x02, 0x08, 0x00, 0x00, + 0x01, 0x02, 0x09, 0x00, 0x00, 0x25, 0x25, 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_BAD_CCS), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_BAD_CIPHER_SUITE), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_BAD_COMPRESSION), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_BAD_FINISHED), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_BAD_FRAGLEN), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_BAD_HANDSHAKE), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_BAD_HELLO_DONE), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_BAD_PARAM), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_BAD_SECRENEG), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_BAD_SNI), 0x00, 0x00, 0x01, T0_INT1(BR_ERR_BAD_VERSION), + 0x00, 0x00, 0x01, T0_INT1(BR_ERR_EXTRA_EXTENSION), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_INVALID_ALGORITHM), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_LIMIT_EXCEEDED), 0x00, 0x00, 0x01, T0_INT1(BR_ERR_OK), + 0x00, 0x00, 0x01, T0_INT1(BR_ERR_OVERSIZED_ID), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_RESUME_MISMATCH), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_UNEXPECTED), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_UNSUPPORTED_VERSION), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_WRONG_KEY_USAGE), 0x00, 0x00, 0x01, + T0_INT2(offsetof(br_ssl_engine_context, action)), 0x00, 0x00, 0x01, + T0_INT2(offsetof(br_ssl_engine_context, alert)), 0x00, 0x00, 0x01, + T0_INT2(offsetof(br_ssl_engine_context, application_data)), 0x00, 0x00, + 0x01, T0_INT2(offsetof(br_ssl_client_context, auth_type)), 0x00, 0x00, + 0x01, + T0_INT2(offsetof(br_ssl_engine_context, session) + offsetof(br_ssl_session_parameters, cipher_suite)), + 0x00, 0x00, 0x01, + T0_INT2(offsetof(br_ssl_engine_context, client_random)), 0x00, 0x00, + 0x01, T0_INT2(offsetof(br_ssl_engine_context, close_received)), 0x00, + 0x00, 0x01, T0_INT2(offsetof(br_ssl_engine_context, ecdhe_curve)), + 0x00, 0x00, 0x01, + T0_INT2(offsetof(br_ssl_engine_context, ecdhe_point)), 0x00, 0x00, + 0x01, T0_INT2(offsetof(br_ssl_engine_context, ecdhe_point_len)), 0x00, + 0x00, 0x01, T0_INT2(offsetof(br_ssl_engine_context, flags)), 0x00, + 0x00, 0x01, T0_INT2(offsetof(br_ssl_client_context, hash_id)), 0x00, + 0x00, 0x01, T0_INT2(offsetof(br_ssl_client_context, hashes)), 0x00, + 0x00, 0x01, T0_INT2(offsetof(br_ssl_engine_context, log_max_frag_len)), + 0x00, 0x00, 0x01, + T0_INT2(offsetof(br_ssl_client_context, min_clienthello_len)), 0x00, + 0x00, 0x01, T0_INT2(offsetof(br_ssl_engine_context, pad)), 0x00, 0x00, + 0x01, T0_INT2(offsetof(br_ssl_engine_context, protocol_names_num)), + 0x00, 0x00, 0x01, + T0_INT2(offsetof(br_ssl_engine_context, record_type_in)), 0x00, 0x00, + 0x01, T0_INT2(offsetof(br_ssl_engine_context, record_type_out)), 0x00, + 0x00, 0x01, T0_INT2(offsetof(br_ssl_engine_context, reneg)), 0x00, + 0x00, 0x01, T0_INT2(offsetof(br_ssl_engine_context, saved_finished)), + 0x00, 0x00, 0x01, + T0_INT2(offsetof(br_ssl_engine_context, selected_protocol)), 0x00, + 0x00, 0x01, T0_INT2(offsetof(br_ssl_engine_context, server_name)), + 0x00, 0x00, 0x01, + T0_INT2(offsetof(br_ssl_engine_context, server_random)), 0x00, 0x00, + 0x01, + T0_INT2(offsetof(br_ssl_engine_context, session) + offsetof(br_ssl_session_parameters, session_id)), + 0x00, 0x00, 0x01, + T0_INT2(offsetof(br_ssl_engine_context, session) + offsetof(br_ssl_session_parameters, session_id_len)), + 0x00, 0x00, 0x01, + T0_INT2(offsetof(br_ssl_engine_context, shutdown_recv)), 0x00, 0x00, + 0x01, T0_INT2(offsetof(br_ssl_engine_context, suites_buf)), 0x00, 0x00, + 0x01, T0_INT2(offsetof(br_ssl_engine_context, suites_num)), 0x00, 0x00, + 0x01, + T0_INT2(offsetof(br_ssl_engine_context, session) + offsetof(br_ssl_session_parameters, version)), + 0x00, 0x00, 0x01, T0_INT2(offsetof(br_ssl_engine_context, version_in)), + 0x00, 0x00, 0x01, + T0_INT2(offsetof(br_ssl_engine_context, version_max)), 0x00, 0x00, + 0x01, T0_INT2(offsetof(br_ssl_engine_context, version_min)), 0x00, + 0x00, 0x01, T0_INT2(offsetof(br_ssl_engine_context, version_out)), + 0x00, 0x00, 0x09, 0x26, 0x59, 0x06, 0x02, 0x69, 0x28, 0x00, 0x00, 0x06, + 0x08, 0x2C, 0x0E, 0x05, 0x02, 0x72, 0x28, 0x04, 0x01, 0x3D, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x01, 0x03, 0x00, 0x9A, 0x26, 0x5F, 0x45, 0x9E, 0x26, + 0x05, 0x04, 0x61, 0x01, 0x00, 0x00, 0x02, 0x00, 0x0E, 0x06, 0x02, 0x9E, + 0x00, 0x5F, 0x04, 0x6B, 0x00, 0x06, 0x02, 0x69, 0x28, 0x00, 0x00, 0x26, + 0x8A, 0x45, 0x05, 0x03, 0x01, 0x0C, 0x08, 0x45, 0x7A, 0x2C, 0xAC, 0x1C, + 0x85, 0x01, 0x0C, 0x31, 0x00, 0x00, 0x26, 0x1F, 0x01, 0x08, 0x0B, 0x45, + 0x5D, 0x1F, 0x08, 0x00, 0x01, 0x03, 0x00, 0x78, 0x2E, 0x02, 0x00, 0x36, + 0x17, 0x01, 0x01, 0x0B, 0x78, 0x3F, 0x29, 0x1A, 0x36, 0x06, 0x07, 0x02, + 0x00, 0xD0, 0x03, 0x00, 0x04, 0x75, 0x01, 0x00, 0xC6, 0x02, 0x00, 0x26, + 0x1A, 0x17, 0x06, 0x02, 0x70, 0x28, 0xD0, 0x04, 0x76, 0x01, 0x01, 0x00, + 0x78, 0x3F, 0x01, 0x16, 0x88, 0x3F, 0x01, 0x00, 0x8B, 0x3D, 0x34, 0xD6, + 0x29, 0xB5, 0x06, 0x09, 0x01, 0x7F, 0xB0, 0x01, 0x7F, 0xD3, 0x04, 0x80, + 0x53, 0xB2, 0x7A, 0x2C, 0xA2, 0x01, T0_INT1(BR_KEYTYPE_SIGN), 0x17, + 0x06, 0x01, 0xB6, 0xB9, 0x26, 0x01, 0x0D, 0x0E, 0x06, 0x07, 0x25, 0xB8, + 0xB9, 0x01, 0x7F, 0x04, 0x02, 0x01, 0x00, 0x03, 0x00, 0x01, 0x0E, 0x0E, + 0x05, 0x02, 0x73, 0x28, 0x06, 0x02, 0x68, 0x28, 0x33, 0x06, 0x02, 0x73, + 0x28, 0x02, 0x00, 0x06, 0x1C, 0xD4, 0x81, 0x2E, 0x01, 0x81, 0x7F, 0x0E, + 0x06, 0x0D, 0x25, 0x01, 0x10, 0xDF, 0x01, 0x00, 0xDE, 0x7A, 0x2C, 0xAC, + 0x24, 0x04, 0x04, 0xD7, 0x06, 0x01, 0xD5, 0x04, 0x01, 0xD7, 0x01, 0x7F, + 0xD3, 0x01, 0x7F, 0xB0, 0x01, 0x01, 0x78, 0x3F, 0x01, 0x17, 0x88, 0x3F, + 0x00, 0x00, 0x38, 0x38, 0x00, 0x00, 0x9B, 0x01, 0x0C, 0x11, 0x01, 0x00, + 0x38, 0x0E, 0x06, 0x05, 0x25, 0x01, + T0_INT1(BR_KEYTYPE_RSA | BR_KEYTYPE_KEYX), 0x04, 0x30, 0x01, 0x01, + 0x38, 0x0E, 0x06, 0x05, 0x25, 0x01, + T0_INT1(BR_KEYTYPE_RSA | BR_KEYTYPE_SIGN), 0x04, 0x25, 0x01, 0x02, + 0x38, 0x0E, 0x06, 0x05, 0x25, 0x01, + T0_INT1(BR_KEYTYPE_EC | BR_KEYTYPE_SIGN), 0x04, 0x1A, 0x01, 0x03, + 0x38, 0x0E, 0x06, 0x05, 0x25, 0x01, + T0_INT1(BR_KEYTYPE_EC | BR_KEYTYPE_KEYX), 0x04, 0x0F, 0x01, 0x04, + 0x38, 0x0E, 0x06, 0x05, 0x25, 0x01, + T0_INT1(BR_KEYTYPE_EC | BR_KEYTYPE_KEYX), 0x04, 0x04, 0x01, 0x00, + 0x45, 0x25, 0x00, 0x00, 0x83, 0x2E, 0x01, 0x0E, 0x0E, 0x06, 0x04, 0x01, + 0x00, 0x04, 0x02, 0x01, 0x05, 0x00, 0x00, 0x41, 0x06, 0x04, 0x01, 0x06, + 0x04, 0x02, 0x01, 0x00, 0x00, 0x00, 0x89, 0x2E, 0x26, 0x06, 0x08, 0x01, + 0x01, 0x09, 0x01, 0x11, 0x07, 0x04, 0x03, 0x25, 0x01, 0x05, 0x00, 0x01, + 0x42, 0x03, 0x00, 0x25, 0x01, 0x00, 0x44, 0x06, 0x03, 0x02, 0x00, 0x08, + 0x43, 0x06, 0x03, 0x02, 0x00, 0x08, 0x26, 0x06, 0x06, 0x01, 0x01, 0x0B, + 0x01, 0x06, 0x08, 0x00, 0x00, 0x8C, 0x40, 0x26, 0x06, 0x03, 0x01, 0x09, + 0x08, 0x00, 0x01, 0x41, 0x26, 0x06, 0x1E, 0x01, 0x00, 0x03, 0x00, 0x26, + 0x06, 0x0E, 0x26, 0x01, 0x01, 0x17, 0x02, 0x00, 0x08, 0x03, 0x00, 0x01, + 0x01, 0x11, 0x04, 0x6F, 0x25, 0x02, 0x00, 0x01, 0x01, 0x0B, 0x01, 0x06, + 0x08, 0x00, 0x00, 0x80, 0x2D, 0x45, 0x11, 0x01, 0x01, 0x17, 0x35, 0x00, + 0x00, 0xA0, 0xCF, 0x26, 0x01, 0x07, 0x17, 0x01, 0x00, 0x38, 0x0E, 0x06, + 0x09, 0x25, 0x01, 0x10, 0x17, 0x06, 0x01, 0xA0, 0x04, 0x35, 0x01, 0x01, + 0x38, 0x0E, 0x06, 0x2C, 0x25, 0x25, 0x01, 0x00, 0x78, 0x3F, 0xB4, 0x89, + 0x2E, 0x01, 0x01, 0x0E, 0x01, 0x01, 0xA9, 0x37, 0x06, 0x17, 0x29, 0x1A, + 0x36, 0x06, 0x04, 0xCF, 0x25, 0x04, 0x78, 0x01, 0x80, 0x64, 0xC6, 0x01, + 0x01, 0x78, 0x3F, 0x01, 0x17, 0x88, 0x3F, 0x04, 0x01, 0xA0, 0x04, 0x03, + 0x73, 0x28, 0x25, 0x04, 0xFF, 0x34, 0x01, 0x26, 0x03, 0x00, 0x09, 0x26, + 0x59, 0x06, 0x02, 0x69, 0x28, 0x02, 0x00, 0x00, 0x00, 0x9B, 0x01, 0x0F, + 0x17, 0x00, 0x00, 0x77, 0x2E, 0x01, 0x00, 0x38, 0x0E, 0x06, 0x10, 0x25, + 0x26, 0x01, 0x01, 0x0D, 0x06, 0x03, 0x25, 0x01, 0x02, 0x77, 0x3F, 0x01, + 0x00, 0x04, 0x21, 0x01, 0x01, 0x38, 0x0E, 0x06, 0x14, 0x25, 0x01, 0x00, + 0x77, 0x3F, 0x26, 0x01, 0x80, 0x64, 0x0E, 0x06, 0x05, 0x01, 0x82, 0x00, + 0x08, 0x28, 0x5B, 0x04, 0x07, 0x25, 0x01, 0x82, 0x00, 0x08, 0x28, 0x25, + 0x00, 0x00, 0x01, 0x00, 0x2F, 0x06, 0x05, 0x3A, 0xAD, 0x37, 0x04, 0x78, + 0x26, 0x06, 0x04, 0x01, 0x01, 0x90, 0x3F, 0x00, 0x01, 0xC0, 0xAB, 0xC0, + 0xAB, 0xC2, 0x85, 0x45, 0x26, 0x03, 0x00, 0xB7, 0x9C, 0x9C, 0x02, 0x00, + 0x4E, 0x26, 0x59, 0x06, 0x0A, 0x01, 0x03, 0xA9, 0x06, 0x02, 0x73, 0x28, + 0x25, 0x04, 0x03, 0x5D, 0x8B, 0x3D, 0x00, 0x00, 0x2F, 0x06, 0x0B, 0x87, + 0x2E, 0x01, 0x14, 0x0D, 0x06, 0x02, 0x73, 0x28, 0x04, 0x11, 0xCF, 0x01, + 0x07, 0x17, 0x26, 0x01, 0x02, 0x0D, 0x06, 0x06, 0x06, 0x02, 0x73, 0x28, + 0x04, 0x70, 0x25, 0xC3, 0x01, 0x01, 0x0D, 0x33, 0x37, 0x06, 0x02, 0x62, + 0x28, 0x26, 0x01, 0x01, 0xC9, 0x36, 0xB3, 0x00, 0x01, 0xB9, 0x01, 0x0B, + 0x0E, 0x05, 0x02, 0x73, 0x28, 0x26, 0x01, 0x03, 0x0E, 0x06, 0x08, 0xC1, + 0x06, 0x02, 0x69, 0x28, 0x45, 0x25, 0x00, 0x45, 0x58, 0xC1, 0xAB, 0x26, + 0x06, 0x23, 0xC1, 0xAB, 0x26, 0x57, 0x26, 0x06, 0x18, 0x26, 0x01, 0x82, + 0x00, 0x0F, 0x06, 0x05, 0x01, 0x82, 0x00, 0x04, 0x01, 0x26, 0x03, 0x00, + 0x85, 0x02, 0x00, 0xB7, 0x02, 0x00, 0x54, 0x04, 0x65, 0x9C, 0x55, 0x04, + 0x5A, 0x9C, 0x9C, 0x56, 0x26, 0x06, 0x02, 0x35, 0x00, 0x25, 0x2B, 0x00, + 0x00, 0x7A, 0x2C, 0xA2, 0x01, 0x7F, 0xB1, 0x26, 0x59, 0x06, 0x02, 0x35, + 0x28, 0x26, 0x05, 0x02, 0x73, 0x28, 0x38, 0x17, 0x0D, 0x06, 0x02, 0x75, + 0x28, 0x3C, 0x00, 0x00, 0x9D, 0xB9, 0x01, 0x14, 0x0D, 0x06, 0x02, 0x73, + 0x28, 0x85, 0x01, 0x0C, 0x08, 0x01, 0x0C, 0xB7, 0x9C, 0x85, 0x26, 0x01, + 0x0C, 0x08, 0x01, 0x0C, 0x30, 0x05, 0x02, 0x65, 0x28, 0x00, 0x00, 0xBA, + 0x06, 0x02, 0x73, 0x28, 0x06, 0x02, 0x67, 0x28, 0x00, 0x0A, 0xB9, 0x01, + 0x02, 0x0E, 0x05, 0x02, 0x73, 0x28, 0xC0, 0x03, 0x00, 0x02, 0x00, 0x96, + 0x2C, 0x0A, 0x02, 0x00, 0x95, 0x2C, 0x0F, 0x37, 0x06, 0x02, 0x74, 0x28, + 0x02, 0x00, 0x94, 0x2C, 0x0D, 0x06, 0x02, 0x6C, 0x28, 0x02, 0x00, 0x97, + 0x3D, 0x8D, 0x01, 0x20, 0xB7, 0x01, 0x00, 0x03, 0x01, 0xC2, 0x03, 0x02, + 0x02, 0x02, 0x01, 0x20, 0x0F, 0x06, 0x02, 0x71, 0x28, 0x85, 0x02, 0x02, + 0xB7, 0x02, 0x02, 0x8F, 0x2E, 0x0E, 0x02, 0x02, 0x01, 0x00, 0x0F, 0x17, + 0x06, 0x0B, 0x8E, 0x85, 0x02, 0x02, 0x30, 0x06, 0x04, 0x01, 0x7F, 0x03, + 0x01, 0x8E, 0x85, 0x02, 0x02, 0x31, 0x02, 0x02, 0x8F, 0x3F, 0x02, 0x00, + 0x93, 0x02, 0x01, 0x99, 0xC0, 0x26, 0xC4, 0x59, 0x06, 0x02, 0x63, 0x28, + 0x26, 0xCE, 0x02, 0x00, 0x01, 0x86, 0x03, 0x0A, 0x17, 0x06, 0x02, 0x63, + 0x28, 0x7A, 0x02, 0x01, 0x99, 0xC2, 0x06, 0x02, 0x64, 0x28, 0x01, 0x00, + 0x3B, 0x26, 0x06, 0x81, 0x47, 0xC0, 0xAB, 0xA7, 0x03, 0x03, 0xA5, 0x03, + 0x04, 0xA3, 0x03, 0x05, 0xA6, 0x03, 0x06, 0xA8, 0x03, 0x07, 0xA4, 0x03, + 0x08, 0x27, 0x03, 0x09, 0x26, 0x06, 0x81, 0x18, 0xC0, 0x01, 0x00, 0x38, + 0x0E, 0x06, 0x0F, 0x25, 0x02, 0x03, 0x05, 0x02, 0x6D, 0x28, 0x01, 0x00, + 0x03, 0x03, 0xBF, 0x04, 0x80, 0x7F, 0x01, 0x01, 0x38, 0x0E, 0x06, 0x0F, + 0x25, 0x02, 0x05, 0x05, 0x02, 0x6D, 0x28, 0x01, 0x00, 0x03, 0x05, 0xBD, + 0x04, 0x80, 0x6A, 0x01, 0x83, 0xFE, 0x01, 0x38, 0x0E, 0x06, 0x0F, 0x25, + 0x02, 0x04, 0x05, 0x02, 0x6D, 0x28, 0x01, 0x00, 0x03, 0x04, 0xBE, 0x04, + 0x80, 0x53, 0x01, 0x0D, 0x38, 0x0E, 0x06, 0x0E, 0x25, 0x02, 0x06, 0x05, + 0x02, 0x6D, 0x28, 0x01, 0x00, 0x03, 0x06, 0xBB, 0x04, 0x3F, 0x01, 0x0A, + 0x38, 0x0E, 0x06, 0x0E, 0x25, 0x02, 0x07, 0x05, 0x02, 0x6D, 0x28, 0x01, + 0x00, 0x03, 0x07, 0xBB, 0x04, 0x2B, 0x01, 0x0B, 0x38, 0x0E, 0x06, 0x0E, + 0x25, 0x02, 0x08, 0x05, 0x02, 0x6D, 0x28, 0x01, 0x00, 0x03, 0x08, 0xBB, + 0x04, 0x17, 0x01, 0x10, 0x38, 0x0E, 0x06, 0x0E, 0x25, 0x02, 0x09, 0x05, + 0x02, 0x6D, 0x28, 0x01, 0x00, 0x03, 0x09, 0xAF, 0x04, 0x03, 0x6D, 0x28, + 0x25, 0x04, 0xFE, 0x64, 0x02, 0x04, 0x06, 0x0D, 0x02, 0x04, 0x01, 0x05, + 0x0F, 0x06, 0x02, 0x6A, 0x28, 0x01, 0x01, 0x89, 0x3F, 0x9C, 0x04, 0x0C, + 0xA5, 0x01, 0x05, 0x0F, 0x06, 0x02, 0x6A, 0x28, 0x01, 0x01, 0x89, 0x3F, + 0x9C, 0x02, 0x01, 0x00, 0x04, 0xB9, 0x01, 0x0C, 0x0E, 0x05, 0x02, 0x73, + 0x28, 0xC2, 0x01, 0x03, 0x0E, 0x05, 0x02, 0x6E, 0x28, 0xC0, 0x26, 0x7D, + 0x3F, 0x26, 0x01, 0x20, 0x10, 0x06, 0x02, 0x6E, 0x28, 0x41, 0x45, 0x11, + 0x01, 0x01, 0x17, 0x05, 0x02, 0x6E, 0x28, 0xC2, 0x26, 0x01, 0x81, 0x05, + 0x0F, 0x06, 0x02, 0x6E, 0x28, 0x26, 0x7F, 0x3F, 0x7E, 0x45, 0xB7, 0x93, + 0x2C, 0x01, 0x86, 0x03, 0x10, 0x03, 0x00, 0x7A, 0x2C, 0xCC, 0x03, 0x01, + 0x01, 0x02, 0x03, 0x02, 0x02, 0x00, 0x06, 0x21, 0xC2, 0x26, 0x26, 0x01, + 0x02, 0x0A, 0x45, 0x01, 0x06, 0x0F, 0x37, 0x06, 0x02, 0x6E, 0x28, 0x03, + 0x02, 0xC2, 0x02, 0x01, 0x01, 0x01, 0x0B, 0x01, 0x03, 0x08, 0x0E, 0x05, + 0x02, 0x6E, 0x28, 0x04, 0x08, 0x02, 0x01, 0x06, 0x04, 0x01, 0x00, 0x03, + 0x02, 0xC0, 0x26, 0x03, 0x03, 0x26, 0x01, 0x84, 0x00, 0x0F, 0x06, 0x02, + 0x6F, 0x28, 0x85, 0x45, 0xB7, 0x02, 0x02, 0x02, 0x01, 0x02, 0x03, 0x51, + 0x26, 0x06, 0x01, 0x28, 0x25, 0x9C, 0x00, 0x02, 0x03, 0x00, 0x03, 0x01, + 0x02, 0x00, 0x98, 0x02, 0x01, 0x02, 0x00, 0x39, 0x26, 0x01, 0x00, 0x0E, + 0x06, 0x02, 0x61, 0x00, 0xD1, 0x04, 0x74, 0x02, 0x01, 0x00, 0x03, 0x00, + 0xC2, 0xAB, 0x26, 0x06, 0x80, 0x43, 0xC2, 0x01, 0x01, 0x38, 0x0E, 0x06, + 0x06, 0x25, 0x01, 0x81, 0x7F, 0x04, 0x2E, 0x01, 0x80, 0x40, 0x38, 0x0E, + 0x06, 0x07, 0x25, 0x01, 0x83, 0xFE, 0x00, 0x04, 0x20, 0x01, 0x80, 0x41, + 0x38, 0x0E, 0x06, 0x07, 0x25, 0x01, 0x84, 0x80, 0x00, 0x04, 0x12, 0x01, + 0x80, 0x42, 0x38, 0x0E, 0x06, 0x07, 0x25, 0x01, 0x88, 0x80, 0x00, 0x04, + 0x04, 0x01, 0x00, 0x45, 0x25, 0x02, 0x00, 0x37, 0x03, 0x00, 0x04, 0xFF, + 0x39, 0x9C, 0x7A, 0x2C, 0xCA, 0x05, 0x09, 0x02, 0x00, 0x01, 0x83, 0xFF, + 0x7F, 0x17, 0x03, 0x00, 0x93, 0x2C, 0x01, 0x86, 0x03, 0x10, 0x06, 0x3A, + 0xBC, 0x26, 0x82, 0x3E, 0x42, 0x25, 0x26, 0x01, 0x08, 0x0B, 0x37, 0x01, + 0x8C, 0x80, 0x00, 0x37, 0x17, 0x02, 0x00, 0x17, 0x02, 0x00, 0x01, 0x8C, + 0x80, 0x00, 0x17, 0x06, 0x19, 0x26, 0x01, 0x81, 0x7F, 0x17, 0x06, 0x05, + 0x01, 0x84, 0x80, 0x00, 0x37, 0x26, 0x01, 0x83, 0xFE, 0x00, 0x17, 0x06, + 0x05, 0x01, 0x88, 0x80, 0x00, 0x37, 0x03, 0x00, 0x04, 0x09, 0x02, 0x00, + 0x01, 0x8C, 0x88, 0x01, 0x17, 0x03, 0x00, 0x16, 0xC0, 0xAB, 0x26, 0x06, + 0x23, 0xC0, 0xAB, 0x26, 0x15, 0x26, 0x06, 0x18, 0x26, 0x01, 0x82, 0x00, + 0x0F, 0x06, 0x05, 0x01, 0x82, 0x00, 0x04, 0x01, 0x26, 0x03, 0x01, 0x85, + 0x02, 0x01, 0xB7, 0x02, 0x01, 0x12, 0x04, 0x65, 0x9C, 0x13, 0x04, 0x5A, + 0x9C, 0x14, 0x9C, 0x02, 0x00, 0x2A, 0x00, 0x00, 0xBA, 0x26, 0x5B, 0x06, + 0x07, 0x25, 0x06, 0x02, 0x67, 0x28, 0x04, 0x74, 0x00, 0x00, 0xC3, 0x01, + 0x03, 0xC1, 0x45, 0x25, 0x45, 0x00, 0x00, 0xC0, 0xC7, 0x00, 0x03, 0x01, + 0x00, 0x03, 0x00, 0xC0, 0xAB, 0x26, 0x06, 0x80, 0x50, 0xC2, 0x03, 0x01, + 0xC2, 0x03, 0x02, 0x02, 0x01, 0x01, 0x08, 0x0E, 0x06, 0x16, 0x02, 0x02, + 0x01, 0x0F, 0x0C, 0x06, 0x0D, 0x01, 0x01, 0x02, 0x02, 0x01, 0x10, 0x08, + 0x0B, 0x02, 0x00, 0x37, 0x03, 0x00, 0x04, 0x2A, 0x02, 0x01, 0x01, 0x02, + 0x10, 0x02, 0x01, 0x01, 0x06, 0x0C, 0x17, 0x02, 0x02, 0x01, 0x01, 0x0E, + 0x02, 0x02, 0x01, 0x03, 0x0E, 0x37, 0x17, 0x06, 0x11, 0x02, 0x00, 0x01, + 0x01, 0x02, 0x02, 0x5E, 0x01, 0x02, 0x0B, 0x02, 0x01, 0x08, 0x0B, 0x37, + 0x03, 0x00, 0x04, 0xFF, 0x2C, 0x9C, 0x02, 0x00, 0x00, 0x00, 0xC0, 0x01, + 0x01, 0x0E, 0x05, 0x02, 0x66, 0x28, 0xC2, 0x01, 0x08, 0x08, 0x83, 0x2E, + 0x0E, 0x05, 0x02, 0x66, 0x28, 0x01, 0x01, 0x3B, 0x00, 0x00, 0xC0, 0x89, + 0x2E, 0x05, 0x15, 0x01, 0x01, 0x0E, 0x05, 0x02, 0x6A, 0x28, 0xC2, 0x01, + 0x00, 0x0E, 0x05, 0x02, 0x6A, 0x28, 0x01, 0x02, 0x89, 0x3F, 0x04, 0x1C, + 0x01, 0x19, 0x0E, 0x05, 0x02, 0x6A, 0x28, 0xC2, 0x01, 0x18, 0x0E, 0x05, + 0x02, 0x6A, 0x28, 0x85, 0x01, 0x18, 0xB7, 0x8A, 0x85, 0x01, 0x18, 0x30, + 0x05, 0x02, 0x6A, 0x28, 0x00, 0x00, 0xC0, 0x06, 0x02, 0x6B, 0x28, 0x00, + 0x00, 0x01, 0x02, 0x98, 0xC3, 0x01, 0x08, 0x0B, 0xC3, 0x08, 0x00, 0x00, + 0x01, 0x03, 0x98, 0xC3, 0x01, 0x08, 0x0B, 0xC3, 0x08, 0x01, 0x08, 0x0B, + 0xC3, 0x08, 0x00, 0x00, 0x01, 0x01, 0x98, 0xC3, 0x00, 0x00, 0x3A, 0x26, + 0x59, 0x05, 0x01, 0x00, 0x25, 0xD1, 0x04, 0x76, 0x02, 0x03, 0x00, 0x92, + 0x2E, 0x03, 0x01, 0x01, 0x00, 0x26, 0x02, 0x01, 0x0A, 0x06, 0x10, 0x26, + 0x01, 0x01, 0x0B, 0x91, 0x08, 0x2C, 0x02, 0x00, 0x0E, 0x06, 0x01, 0x00, + 0x5D, 0x04, 0x6A, 0x25, 0x01, 0x7F, 0x00, 0x00, 0x01, 0x15, 0x88, 0x3F, + 0x45, 0x53, 0x25, 0x53, 0x25, 0x29, 0x00, 0x00, 0x01, 0x01, 0x45, 0xC5, + 0x00, 0x00, 0x45, 0x38, 0x98, 0x45, 0x26, 0x06, 0x05, 0xC3, 0x25, 0x5E, + 0x04, 0x78, 0x25, 0x00, 0x00, 0x26, 0x01, 0x81, 0xAC, 0x00, 0x0E, 0x06, + 0x04, 0x25, 0x01, 0x7F, 0x00, 0x9B, 0x5A, 0x00, 0x02, 0x03, 0x00, 0x7A, + 0x2C, 0x9B, 0x03, 0x01, 0x02, 0x01, 0x01, 0x0F, 0x17, 0x02, 0x01, 0x01, + 0x04, 0x11, 0x01, 0x0F, 0x17, 0x02, 0x01, 0x01, 0x08, 0x11, 0x01, 0x0F, + 0x17, 0x01, 0x00, 0x38, 0x0E, 0x06, 0x10, 0x25, 0x01, 0x00, 0x01, 0x18, + 0x02, 0x00, 0x06, 0x03, 0x4A, 0x04, 0x01, 0x4B, 0x04, 0x81, 0x0D, 0x01, + 0x01, 0x38, 0x0E, 0x06, 0x10, 0x25, 0x01, 0x01, 0x01, 0x10, 0x02, 0x00, + 0x06, 0x03, 0x4A, 0x04, 0x01, 0x4B, 0x04, 0x80, 0x77, 0x01, 0x02, 0x38, + 0x0E, 0x06, 0x10, 0x25, 0x01, 0x01, 0x01, 0x20, 0x02, 0x00, 0x06, 0x03, + 0x4A, 0x04, 0x01, 0x4B, 0x04, 0x80, 0x61, 0x01, 0x03, 0x38, 0x0E, 0x06, + 0x0F, 0x25, 0x25, 0x01, 0x10, 0x02, 0x00, 0x06, 0x03, 0x48, 0x04, 0x01, + 0x49, 0x04, 0x80, 0x4C, 0x01, 0x04, 0x38, 0x0E, 0x06, 0x0E, 0x25, 0x25, + 0x01, 0x20, 0x02, 0x00, 0x06, 0x03, 0x48, 0x04, 0x01, 0x49, 0x04, 0x38, + 0x01, 0x05, 0x38, 0x0E, 0x06, 0x0C, 0x25, 0x25, 0x02, 0x00, 0x06, 0x03, + 0x4C, 0x04, 0x01, 0x4D, 0x04, 0x26, 0x26, 0x01, 0x09, 0x0F, 0x06, 0x02, + 0x69, 0x28, 0x45, 0x25, 0x26, 0x01, 0x01, 0x17, 0x01, 0x04, 0x0B, 0x01, + 0x10, 0x08, 0x45, 0x01, 0x08, 0x17, 0x01, 0x10, 0x45, 0x09, 0x02, 0x00, + 0x06, 0x03, 0x46, 0x04, 0x01, 0x47, 0x00, 0x25, 0x00, 0x00, 0x9B, 0x01, + 0x0C, 0x11, 0x01, 0x02, 0x0F, 0x00, 0x00, 0x9B, 0x01, 0x0C, 0x11, 0x26, + 0x5C, 0x45, 0x01, 0x03, 0x0A, 0x17, 0x00, 0x00, 0x9B, 0x01, 0x0C, 0x11, + 0x01, 0x01, 0x0E, 0x00, 0x00, 0x9B, 0x01, 0x0C, 0x11, 0x5B, 0x00, 0x00, + 0x9B, 0x01, 0x81, 0x70, 0x17, 0x01, 0x20, 0x0D, 0x00, 0x00, 0x1B, 0x01, + 0x00, 0x76, 0x2E, 0x26, 0x06, 0x22, 0x01, 0x01, 0x38, 0x0E, 0x06, 0x06, + 0x25, 0x01, 0x00, 0x9F, 0x04, 0x14, 0x01, 0x02, 0x38, 0x0E, 0x06, 0x0D, + 0x25, 0x78, 0x2E, 0x01, 0x01, 0x0E, 0x06, 0x03, 0x01, 0x10, 0x37, 0x04, + 0x01, 0x25, 0x04, 0x01, 0x25, 0x7C, 0x2E, 0x05, 0x33, 0x2F, 0x06, 0x30, + 0x87, 0x2E, 0x01, 0x14, 0x38, 0x0E, 0x06, 0x06, 0x25, 0x01, 0x02, 0x37, + 0x04, 0x22, 0x01, 0x15, 0x38, 0x0E, 0x06, 0x09, 0x25, 0xAE, 0x06, 0x03, + 0x01, 0x7F, 0x9F, 0x04, 0x13, 0x01, 0x16, 0x38, 0x0E, 0x06, 0x06, 0x25, + 0x01, 0x01, 0x37, 0x04, 0x07, 0x25, 0x01, 0x04, 0x37, 0x01, 0x00, 0x25, + 0x1A, 0x06, 0x03, 0x01, 0x08, 0x37, 0x00, 0x00, 0x1B, 0x26, 0x05, 0x13, + 0x2F, 0x06, 0x10, 0x87, 0x2E, 0x01, 0x15, 0x0E, 0x06, 0x08, 0x25, 0xAE, + 0x01, 0x00, 0x78, 0x3F, 0x04, 0x01, 0x20, 0x00, 0x00, 0xCF, 0x01, 0x07, + 0x17, 0x01, 0x01, 0x0F, 0x06, 0x02, 0x73, 0x28, 0x00, 0x01, 0x03, 0x00, + 0x29, 0x1A, 0x06, 0x05, 0x02, 0x00, 0x88, 0x3F, 0x00, 0xCF, 0x25, 0x04, + 0x74, 0x00, 0x01, 0x14, 0xD2, 0x01, 0x01, 0xDF, 0x29, 0x26, 0x01, 0x00, + 0xC9, 0x01, 0x16, 0xD2, 0xD8, 0x29, 0x00, 0x00, 0x01, 0x0B, 0xDF, 0x4F, + 0x26, 0x26, 0x01, 0x03, 0x08, 0xDE, 0xDE, 0x18, 0x26, 0x59, 0x06, 0x02, + 0x25, 0x00, 0xDE, 0x1D, 0x26, 0x06, 0x05, 0x85, 0x45, 0xD9, 0x04, 0x77, + 0x25, 0x04, 0x6C, 0x00, 0x21, 0x01, 0x0F, 0xDF, 0x26, 0x93, 0x2C, 0x01, + 0x86, 0x03, 0x10, 0x06, 0x0C, 0x01, 0x04, 0x08, 0xDE, 0x81, 0x2E, 0xDF, + 0x79, 0x2E, 0xDF, 0x04, 0x02, 0x5F, 0xDE, 0x26, 0xDD, 0x85, 0x45, 0xD9, + 0x00, 0x02, 0xA5, 0xA7, 0x08, 0xA3, 0x08, 0xA6, 0x08, 0xA8, 0x08, 0xA4, + 0x08, 0x27, 0x08, 0x03, 0x00, 0x01, 0x01, 0xDF, 0x01, 0x27, 0x8F, 0x2E, + 0x08, 0x92, 0x2E, 0x01, 0x01, 0x0B, 0x08, 0x02, 0x00, 0x06, 0x04, 0x5F, + 0x02, 0x00, 0x08, 0x84, 0x2C, 0x38, 0x09, 0x26, 0x5C, 0x06, 0x24, 0x02, + 0x00, 0x05, 0x04, 0x45, 0x5F, 0x45, 0x60, 0x01, 0x04, 0x09, 0x26, 0x59, + 0x06, 0x03, 0x25, 0x01, 0x00, 0x26, 0x01, 0x04, 0x08, 0x02, 0x00, 0x08, + 0x03, 0x00, 0x45, 0x01, 0x04, 0x08, 0x38, 0x08, 0x45, 0x04, 0x03, 0x25, + 0x01, 0x7F, 0x03, 0x01, 0xDE, 0x95, 0x2C, 0xDD, 0x7B, 0x01, 0x04, 0x19, + 0x7B, 0x01, 0x04, 0x08, 0x01, 0x1C, 0x32, 0x7B, 0x01, 0x20, 0xD9, 0x8E, + 0x8F, 0x2E, 0xDB, 0x92, 0x2E, 0x26, 0x01, 0x01, 0x0B, 0xDD, 0x91, 0x45, + 0x26, 0x06, 0x0F, 0x5E, 0x38, 0x2C, 0x26, 0xC8, 0x05, 0x02, 0x63, 0x28, + 0xDD, 0x45, 0x5F, 0x45, 0x04, 0x6E, 0x61, 0x01, 0x01, 0xDF, 0x01, 0x00, + 0xDF, 0x02, 0x00, 0x06, 0x81, 0x5A, 0x02, 0x00, 0xDD, 0xA5, 0x06, 0x0E, + 0x01, 0x83, 0xFE, 0x01, 0xDD, 0x8A, 0xA5, 0x01, 0x04, 0x09, 0x26, 0xDD, + 0x5E, 0xDB, 0xA7, 0x06, 0x16, 0x01, 0x00, 0xDD, 0x8C, 0xA7, 0x01, 0x04, + 0x09, 0x26, 0xDD, 0x01, 0x02, 0x09, 0x26, 0xDD, 0x01, 0x00, 0xDF, 0x01, + 0x03, 0x09, 0xDA, 0xA3, 0x06, 0x0C, 0x01, 0x01, 0xDD, 0x01, 0x01, 0xDD, + 0x83, 0x2E, 0x01, 0x08, 0x09, 0xDF, 0xA6, 0x06, 0x19, 0x01, 0x0D, 0xDD, + 0xA6, 0x01, 0x04, 0x09, 0x26, 0xDD, 0x01, 0x02, 0x09, 0xDD, 0x43, 0x06, + 0x03, 0x01, 0x03, 0xDC, 0x44, 0x06, 0x03, 0x01, 0x01, 0xDC, 0xA8, 0x26, + 0x06, 0x36, 0x01, 0x0A, 0xDD, 0x01, 0x04, 0x09, 0x26, 0xDD, 0x60, 0xDD, + 0x41, 0x01, 0x00, 0x26, 0x01, 0x82, 0x80, 0x80, 0x80, 0x00, 0x17, 0x06, + 0x0A, 0x01, 0xFD, 0xFF, 0xFF, 0xFF, 0x7F, 0x17, 0x01, 0x1D, 0xDD, 0x26, + 0x01, 0x20, 0x0A, 0x06, 0x0C, 0xA1, 0x11, 0x01, 0x01, 0x17, 0x06, 0x02, + 0x26, 0xDD, 0x5D, 0x04, 0x6E, 0x61, 0x04, 0x01, 0x25, 0xA4, 0x06, 0x0A, + 0x01, 0x0B, 0xDD, 0x01, 0x02, 0xDD, 0x01, 0x82, 0x00, 0xDD, 0x27, 0x26, + 0x06, 0x1F, 0x01, 0x10, 0xDD, 0x01, 0x04, 0x09, 0x26, 0xDD, 0x60, 0xDD, + 0x86, 0x2C, 0x01, 0x00, 0xA1, 0x0F, 0x06, 0x0A, 0x26, 0x1E, 0x26, 0xDF, + 0x85, 0x45, 0xD9, 0x5D, 0x04, 0x72, 0x61, 0x04, 0x01, 0x25, 0x02, 0x01, + 0x59, 0x05, 0x11, 0x01, 0x15, 0xDD, 0x02, 0x01, 0x26, 0xDD, 0x26, 0x06, + 0x06, 0x5E, 0x01, 0x00, 0xDF, 0x04, 0x77, 0x25, 0x00, 0x00, 0x01, 0x10, + 0xDF, 0x7A, 0x2C, 0x26, 0xCD, 0x06, 0x0C, 0xAC, 0x23, 0x26, 0x5F, 0xDE, + 0x26, 0xDD, 0x85, 0x45, 0xD9, 0x04, 0x0D, 0x26, 0xCB, 0x45, 0xAC, 0x22, + 0x26, 0x5D, 0xDE, 0x26, 0xDF, 0x85, 0x45, 0xD9, 0x00, 0x00, 0x9D, 0x01, + 0x14, 0xDF, 0x01, 0x0C, 0xDE, 0x85, 0x01, 0x0C, 0xD9, 0x00, 0x00, 0x52, + 0x26, 0x01, 0x00, 0x0E, 0x06, 0x02, 0x61, 0x00, 0xCF, 0x25, 0x04, 0x73, + 0x00, 0x26, 0xDD, 0xD9, 0x00, 0x00, 0x26, 0xDF, 0xD9, 0x00, 0x01, 0x03, + 0x00, 0x42, 0x25, 0x26, 0x01, 0x10, 0x17, 0x06, 0x06, 0x01, 0x04, 0xDF, + 0x02, 0x00, 0xDF, 0x26, 0x01, 0x08, 0x17, 0x06, 0x06, 0x01, 0x03, 0xDF, + 0x02, 0x00, 0xDF, 0x26, 0x01, 0x20, 0x17, 0x06, 0x06, 0x01, 0x05, 0xDF, + 0x02, 0x00, 0xDF, 0x26, 0x01, 0x80, 0x40, 0x17, 0x06, 0x06, 0x01, 0x06, + 0xDF, 0x02, 0x00, 0xDF, 0x01, 0x04, 0x17, 0x06, 0x06, 0x01, 0x02, 0xDF, + 0x02, 0x00, 0xDF, 0x00, 0x00, 0x26, 0x01, 0x08, 0x50, 0xDF, 0xDF, 0x00, + 0x00, 0x26, 0x01, 0x10, 0x50, 0xDF, 0xDD, 0x00, 0x00, 0x26, 0x53, 0x06, + 0x02, 0x25, 0x00, 0xCF, 0x25, 0x04, 0x76 +}; + +static const uint16_t t0_caddr[] PROGMEM = { + + 0, + 5, + 10, + 15, + 20, + 25, + 30, + 35, + 40, + 44, + 48, + 52, + 56, + 60, + 64, + 68, + 72, + 76, + 80, + 84, + 88, + 92, + 96, + 100, + 104, + 108, + 112, + 116, + 120, + 124, + 129, + 134, + 139, + 144, + 149, + 154, + 159, + 164, + 169, + 174, + 179, + 184, + 189, + 194, + 199, + 204, + 209, + 214, + 219, + 224, + 229, + 234, + 239, + 244, + 249, + 254, + 259, + 264, + 269, + 274, + 279, + 284, + 289, + 294, + 303, + 316, + 320, + 345, + 351, + 370, + 381, + 422, + 542, + 546, + 611, + 626, + 637, + 655, + 684, + 694, + 730, + 740, + 818, + 832, + 838, + 897, + 916, + 951, + 1000, + 1076, + 1103, + 1134, + 1145, + 1500, + 1647, + 1671, + 1887, + 1901, + 1910, + 1914, + 2009, + 2033, + 2089, + 2096, + 2107, + 2123, + 2129, + 2140, + 2175, + 2187, + 2193, + 2208, + 2224, + 2417, + 2426, + 2439, + 2448, + 2455, + 2465, + 2571, + 2596, + 2609, + 2625, + 2643, + 2675, + 2709, + 3077, + 3113, + 3126, + 3140, + 3145, + 3150, + 3216, + 3224, + 3232 +}; + +#define T0_INTERPRETED 89 + +#define T0_ENTER(ip, rp, slot) do { \ + const unsigned char *t0_newip; \ + uint32_t t0_lnum; \ + t0_newip = &t0_codeblock[pgm_read_word(&t0_caddr[(slot) - T0_INTERPRETED])]; \ + t0_lnum = t0_parse7E_unsigned(&t0_newip); \ + (rp) += t0_lnum; \ + *((rp) ++) = (uint32_t)((ip) - &t0_codeblock[0]) + (t0_lnum << 16); \ + (ip) = t0_newip; \ + } while (0) + +#define T0_DEFENTRY(name, slot) \ +void \ +name(void *ctx) \ +{ \ + t0_context *t0ctx = ctx; \ + t0ctx->ip = &t0_codeblock[0]; \ + T0_ENTER(t0ctx->ip, t0ctx->rp, slot); \ +} + +T0_DEFENTRY(br_ssl_hs_client_init_main, 170) + +#define T0_NEXT(t0ipp) (pgm_read_byte((*t0ipp)++)) + +void +br_ssl_hs_client_run(void *t0ctx) +{ + uint32_t *dp, *rp; + const unsigned char *ip; + +#define T0_LOCAL(x) (*(rp - 2 - (x))) +#define T0_POP() (*-- dp) +#define T0_POPi() (*(int32_t *)(-- dp)) +#define T0_PEEK(x) (*(dp - 1 - (x))) +#define T0_PEEKi(x) (*(int32_t *)(dp - 1 - (x))) +#define T0_PUSH(v) do { *dp = (v); dp ++; } while (0) +#define T0_PUSHi(v) do { *(int32_t *)dp = (v); dp ++; } while (0) +#define T0_RPOP() (*-- rp) +#define T0_RPOPi() (*(int32_t *)(-- rp)) +#define T0_RPUSH(v) do { *rp = (v); rp ++; } while (0) +#define T0_RPUSHi(v) do { *(int32_t *)rp = (v); rp ++; } while (0) +#define T0_ROLL(x) do { \ + size_t t0len = (size_t)(x); \ + uint32_t t0tmp = *(dp - 1 - t0len); \ + memmove(dp - t0len - 1, dp - t0len, t0len * sizeof *dp); \ + *(dp - 1) = t0tmp; \ +} while (0) +#define T0_SWAP() do { \ + uint32_t t0tmp = *(dp - 2); \ + *(dp - 2) = *(dp - 1); \ + *(dp - 1) = t0tmp; \ +} while (0) +#define T0_ROT() do { \ + uint32_t t0tmp = *(dp - 3); \ + *(dp - 3) = *(dp - 2); \ + *(dp - 2) = *(dp - 1); \ + *(dp - 1) = t0tmp; \ +} while (0) +#define T0_NROT() do { \ + uint32_t t0tmp = *(dp - 1); \ + *(dp - 1) = *(dp - 2); \ + *(dp - 2) = *(dp - 3); \ + *(dp - 3) = t0tmp; \ +} while (0) +#define T0_PICK(x) do { \ + uint32_t t0depth = (x); \ + T0_PUSH(T0_PEEK(t0depth)); \ +} while (0) +#define T0_CO() do { \ + goto t0_exit; \ +} while (0) +#define T0_RET() goto t0_next + + dp = ((t0_context *)t0ctx)->dp; + rp = ((t0_context *)t0ctx)->rp; + ip = ((t0_context *)t0ctx)->ip; + goto t0_next; + for (;;) { + uint32_t t0x; + + t0_next: + t0x = T0_NEXT(&ip); + if (t0x < T0_INTERPRETED) { + switch (t0x) { + int32_t t0off; + + case 0: /* ret */ + t0x = T0_RPOP(); + rp -= (t0x >> 16); + t0x &= 0xFFFF; + if (t0x == 0) { + ip = NULL; + goto t0_exit; + } + ip = &t0_codeblock[t0x]; + break; + case 1: /* literal constant */ + T0_PUSHi(t0_parse7E_signed(&ip)); + break; + case 2: /* read local */ + T0_PUSH(T0_LOCAL(t0_parse7E_unsigned(&ip))); + break; + case 3: /* write local */ + T0_LOCAL(t0_parse7E_unsigned(&ip)) = T0_POP(); + break; + case 4: /* jump */ + t0off = t0_parse7E_signed(&ip); + ip += t0off; + break; + case 5: /* jump if */ + t0off = t0_parse7E_signed(&ip); + if (T0_POP()) { + ip += t0off; + } + break; + case 6: /* jump if not */ + t0off = t0_parse7E_signed(&ip); + if (!T0_POP()) { + ip += t0off; + } + break; + case 7: { + /* * */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(a * b); + + } + break; + case 8: { + /* + */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(a + b); + + } + break; + case 9: { + /* - */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(a - b); + + } + break; + case 10: { + /* < */ + + int32_t b = T0_POPi(); + int32_t a = T0_POPi(); + T0_PUSH(-(uint32_t)(a < b)); + + } + break; + case 11: { + /* << */ + + int c = (int)T0_POPi(); + uint32_t x = T0_POP(); + T0_PUSH(x << c); + + } + break; + case 12: { + /* <= */ + + int32_t b = T0_POPi(); + int32_t a = T0_POPi(); + T0_PUSH(-(uint32_t)(a <= b)); + + } + break; + case 13: { + /* <> */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(-(uint32_t)(a != b)); + + } + break; + case 14: { + /* = */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(-(uint32_t)(a == b)); + + } + break; + case 15: { + /* > */ + + int32_t b = T0_POPi(); + int32_t a = T0_POPi(); + T0_PUSH(-(uint32_t)(a > b)); + + } + break; + case 16: { + /* >= */ + + int32_t b = T0_POPi(); + int32_t a = T0_POPi(); + T0_PUSH(-(uint32_t)(a >= b)); + + } + break; + case 17: { + /* >> */ + + int c = (int)T0_POPi(); + int32_t x = T0_POPi(); + T0_PUSHi(x >> c); + + } + break; + case 18: { + /* anchor-dn-append-name */ + + size_t len; + + len = T0_POP(); + if (CTX->client_auth_vtable != NULL) { + (*CTX->client_auth_vtable)->append_name( + CTX->client_auth_vtable, ENG->pad, len); + } + + } + break; + case 19: { + /* anchor-dn-end-name */ + + if (CTX->client_auth_vtable != NULL) { + (*CTX->client_auth_vtable)->end_name( + CTX->client_auth_vtable); + } + + } + break; + case 20: { + /* anchor-dn-end-name-list */ + + if (CTX->client_auth_vtable != NULL) { + (*CTX->client_auth_vtable)->end_name_list( + CTX->client_auth_vtable); + } + + } + break; + case 21: { + /* anchor-dn-start-name */ + + size_t len; + + len = T0_POP(); + if (CTX->client_auth_vtable != NULL) { + (*CTX->client_auth_vtable)->start_name( + CTX->client_auth_vtable, len); + } + + } + break; + case 22: { + /* anchor-dn-start-name-list */ + + if (CTX->client_auth_vtable != NULL) { + (*CTX->client_auth_vtable)->start_name_list( + CTX->client_auth_vtable); + } + + } + break; + case 23: { + /* and */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(a & b); + + } + break; + case 24: { + /* begin-cert */ + + if (ENG->chain_len == 0) { + T0_PUSHi(-1); + } else { + ENG->cert_cur = ENG->chain->data; + ENG->cert_len = ENG->chain->data_len; + ENG->chain ++; + ENG->chain_len --; + T0_PUSH(ENG->cert_len); + } + + } + break; + case 25: { + /* bzero */ + + size_t len = (size_t)T0_POP(); + void *addr = (unsigned char *)ENG + (size_t)T0_POP(); + memset(addr, 0, len); + + } + break; + case 26: { + /* can-output? */ + + T0_PUSHi(-(ENG->hlen_out > 0)); + + } + break; + case 27: { + /* co */ + T0_CO(); + } + break; + case 28: { + /* compute-Finished-inner */ + + int prf_id = T0_POP(); + int from_client = T0_POPi(); + unsigned char tmp[48]; + br_tls_prf_seed_chunk seed; + + br_tls_prf_impl prf = br_ssl_engine_get_PRF(ENG, prf_id); + seed.data = tmp; + if (ENG->session.version >= BR_TLS12) { + seed.len = br_multihash_out(&ENG->mhash, prf_id, tmp); + } else { + br_multihash_out(&ENG->mhash, br_md5_ID, tmp); + br_multihash_out(&ENG->mhash, br_sha1_ID, tmp + 16); + seed.len = 36; + } + prf(ENG->pad, 12, ENG->session.master_secret, + sizeof ENG->session.master_secret, + from_client ? "client finished" : "server finished", + 1, &seed); + + } + break; + case 29: { + /* copy-cert-chunk */ + + size_t clen; + + clen = ENG->cert_len; + if (clen > sizeof ENG->pad) { + clen = sizeof ENG->pad; + } + memcpy_P(ENG->pad, ENG->cert_cur, clen); + ENG->cert_cur += clen; + ENG->cert_len -= clen; + T0_PUSH(clen); + + } + break; + case 30: { + /* copy-protocol-name */ + + size_t idx = T0_POP(); + size_t len = strlen(ENG->protocol_names[idx]); + memcpy(ENG->pad, ENG->protocol_names[idx], len); + T0_PUSH(len); + + } + break; + case 31: { + /* data-get8 */ + + size_t addr = T0_POP(); + T0_PUSH(pgm_read_byte(&t0_datablock[addr])); + + } + break; + case 32: { + /* discard-input */ + + ENG->hlen_in = 0; + + } + break; + case 33: { + /* do-client-sign */ + + size_t sig_len; + + sig_len = make_client_sign(CTX); + if (sig_len == 0) { + br_ssl_engine_fail(ENG, BR_ERR_INVALID_ALGORITHM); + T0_CO(); + } + T0_PUSH(sig_len); + + } + break; + case 34: { + /* do-ecdh */ + + unsigned prf_id = T0_POP(); + unsigned ecdhe = T0_POP(); + int x; + + x = make_pms_ecdh(CTX, ecdhe, prf_id); + if (x < 0) { + br_ssl_engine_fail(ENG, -x); + T0_CO(); + } else { + T0_PUSH(x); + } + + } + break; + case 35: { + /* do-rsa-encrypt */ + + int x; + + x = make_pms_rsa(CTX, T0_POP()); + if (x < 0) { + br_ssl_engine_fail(ENG, -x); + T0_CO(); + } else { + T0_PUSH(x); + } + + } + break; + case 36: { + /* do-static-ecdh */ + + unsigned prf_id = T0_POP(); + + if (make_pms_static_ecdh(CTX, prf_id) < 0) { + br_ssl_engine_fail(ENG, BR_ERR_INVALID_ALGORITHM); + T0_CO(); + } + + } + break; + case 37: { + /* drop */ + (void)T0_POP(); + } + break; + case 38: { + /* dup */ + T0_PUSH(T0_PEEK(0)); + } + break; + case 39: { + /* ext-ALPN-length */ + + size_t u, len; + + if (ENG->protocol_names_num == 0) { + T0_PUSH(0); + T0_RET(); + } + len = 6; + for (u = 0; u < ENG->protocol_names_num; u ++) { + len += 1 + strlen(ENG->protocol_names[u]); + } + T0_PUSH(len); + + } + break; + case 40: { + /* fail */ + + br_ssl_engine_fail(ENG, (int)T0_POPi()); + T0_CO(); + + } + break; + case 41: { + /* flush-record */ + + br_ssl_engine_flush_record(ENG); + + } + break; + case 42: { + /* get-client-chain */ + + uint32_t auth_types; + + auth_types = T0_POP(); + if (CTX->client_auth_vtable != NULL) { + br_ssl_client_certificate ux; + + (*CTX->client_auth_vtable)->choose(CTX->client_auth_vtable, + CTX, auth_types, &ux); + CTX->auth_type = (unsigned char)ux.auth_type; + CTX->hash_id = (unsigned char)ux.hash_id; + ENG->chain = ux.chain; + ENG->chain_len = ux.chain_len; + } else { + CTX->hash_id = 0; + ENG->chain_len = 0; + } + + } + break; + case 43: { + /* get-key-type-usages */ + + const br_x509_class *xc; + const br_x509_pkey *pk; + unsigned usages; + + xc = *(ENG->x509ctx); + pk = xc->get_pkey(ENG->x509ctx, &usages); + if (pk == NULL) { + T0_PUSH(0); + } else { + T0_PUSH(pk->key_type | usages); + } + + } + break; + case 44: { + /* get16 */ + + size_t addr = (size_t)T0_POP(); + T0_PUSH(*(uint16_t *)(void *)((unsigned char *)ENG + addr)); + + } + break; + case 45: { + /* get32 */ + + size_t addr = (size_t)T0_POP(); + T0_PUSH(*(uint32_t *)(void *)((unsigned char *)ENG + addr)); + + } + break; + case 46: { + /* get8 */ + + size_t addr = (size_t)T0_POP(); + T0_PUSH(*((unsigned char *)ENG + addr)); + + } + break; + case 47: { + /* has-input? */ + + T0_PUSHi(-(ENG->hlen_in != 0)); + + } + break; + case 48: { + /* memcmp */ + + size_t len = (size_t)T0_POP(); + void *addr2 = (unsigned char *)ENG + (size_t)T0_POP(); + void *addr1 = (unsigned char *)ENG + (size_t)T0_POP(); + int x = memcmp(addr1, addr2, len); + T0_PUSH((uint32_t)-(x == 0)); + + } + break; + case 49: { + /* memcpy */ + + size_t len = (size_t)T0_POP(); + void *src = (unsigned char *)ENG + (size_t)T0_POP(); + void *dst = (unsigned char *)ENG + (size_t)T0_POP(); + memcpy(dst, src, len); + + } + break; + case 50: { + /* mkrand */ + + size_t len = (size_t)T0_POP(); + void *addr = (unsigned char *)ENG + (size_t)T0_POP(); + br_hmac_drbg_generate(&ENG->rng, addr, len); + + } + break; + case 51: { + /* more-incoming-bytes? */ + + T0_PUSHi(ENG->hlen_in != 0 || !br_ssl_engine_recvrec_finished(ENG)); + + } + break; + case 52: { + /* multihash-init */ + + br_multihash_init(&ENG->mhash); + + } + break; + case 53: { + /* neg */ + + uint32_t a = T0_POP(); + T0_PUSH(-a); + + } + break; + case 54: { + /* not */ + + uint32_t a = T0_POP(); + T0_PUSH(~a); + + } + break; + case 55: { + /* or */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(a | b); + + } + break; + case 56: { + /* over */ + T0_PUSH(T0_PEEK(1)); + } + break; + case 57: { + /* read-chunk-native */ + + size_t clen = ENG->hlen_in; + if (clen > 0) { + uint32_t addr, len; + + len = T0_POP(); + addr = T0_POP(); + if ((size_t)len < clen) { + clen = (size_t)len; + } + memcpy_P((unsigned char *)ENG + addr, ENG->hbuf_in, clen); + if (ENG->record_type_in == BR_SSL_HANDSHAKE) { + br_multihash_update(&ENG->mhash, ENG->hbuf_in, clen); + } + T0_PUSH(addr + (uint32_t)clen); + T0_PUSH(len - (uint32_t)clen); + ENG->hbuf_in += clen; + ENG->hlen_in -= clen; + } + + } + break; + case 58: { + /* read8-native */ + + if (ENG->hlen_in > 0) { + unsigned char x; + + x = pgm_read_byte(ENG->hbuf_in ++); + if (ENG->record_type_in == BR_SSL_HANDSHAKE) { + br_multihash_update(&ENG->mhash, &x, 1); + } + T0_PUSH(x); + ENG->hlen_in --; + } else { + T0_PUSHi(-1); + } + + } + break; + case 59: { + /* set-mfln-status */ + + int val = T0_POP(); + ENG->max_frag_len_negotiated = val; + + } + break; + case 60: { + /* set-server-curve */ + + const br_x509_class *xc; + const br_x509_pkey *pk; + + xc = *(ENG->x509ctx); + pk = xc->get_pkey(ENG->x509ctx, NULL); + CTX->server_curve = + (pk->key_type == BR_KEYTYPE_EC) ? pk->key.ec.curve : 0; + + } + break; + case 61: { + /* set16 */ + + size_t addr = (size_t)T0_POP(); + *(uint16_t *)(void *)((unsigned char *)ENG + addr) = (uint16_t)T0_POP(); + + } + break; + case 62: { + /* set32 */ + + size_t addr = (size_t)T0_POP(); + *(uint32_t *)(void *)((unsigned char *)ENG + addr) = (uint32_t)T0_POP(); + + } + break; + case 63: { + /* set8 */ + + size_t addr = (size_t)T0_POP(); + *((unsigned char *)ENG + addr) = (unsigned char)T0_POP(); + + } + break; + case 64: { + /* strlen */ + + void *str = (unsigned char *)ENG + (size_t)T0_POP(); + T0_PUSH((uint32_t)strlen(str)); + + } + break; + case 65: { + /* supported-curves */ + + uint32_t x = ENG->iec == NULL ? 0 : ENG->iec->supported_curves; + T0_PUSH(x); + + } + break; + case 66: { + /* supported-hash-functions */ + + int i; + unsigned x, num; + + x = 0; + num = 0; + for (i = br_sha1_ID; i <= br_sha512_ID; i ++) { + if (br_multihash_getimpl(&ENG->mhash, i)) { + x |= 1U << i; + num ++; + } + } + T0_PUSH(x); + T0_PUSH(num); + + } + break; + case 67: { + /* supports-ecdsa? */ + + T0_PUSHi(-(ENG->iecdsa != 0)); + + } + break; + case 68: { + /* supports-rsa-sign? */ + + T0_PUSHi(-(ENG->irsavrfy != 0)); + + } + break; + case 69: { + /* swap */ + T0_SWAP(); + } + break; + case 70: { + /* switch-aesccm-in */ + + int is_client, prf_id; + unsigned cipher_key_len, tag_len; + + tag_len = T0_POP(); + cipher_key_len = T0_POP(); + prf_id = T0_POP(); + is_client = T0_POP(); + br_ssl_engine_switch_ccm_in(ENG, is_client, prf_id, + ENG->iaes_ctrcbc, cipher_key_len, tag_len); + + } + break; + case 71: { + /* switch-aesccm-out */ + + int is_client, prf_id; + unsigned cipher_key_len, tag_len; + + tag_len = T0_POP(); + cipher_key_len = T0_POP(); + prf_id = T0_POP(); + is_client = T0_POP(); + br_ssl_engine_switch_ccm_out(ENG, is_client, prf_id, + ENG->iaes_ctrcbc, cipher_key_len, tag_len); + + } + break; + case 72: { + /* switch-aesgcm-in */ + + int is_client, prf_id; + unsigned cipher_key_len; + + cipher_key_len = T0_POP(); + prf_id = T0_POP(); + is_client = T0_POP(); + br_ssl_engine_switch_gcm_in(ENG, is_client, prf_id, + ENG->iaes_ctr, cipher_key_len); + + } + break; + case 73: { + /* switch-aesgcm-out */ + + int is_client, prf_id; + unsigned cipher_key_len; + + cipher_key_len = T0_POP(); + prf_id = T0_POP(); + is_client = T0_POP(); + br_ssl_engine_switch_gcm_out(ENG, is_client, prf_id, + ENG->iaes_ctr, cipher_key_len); + + } + break; + case 74: { + /* switch-cbc-in */ + + int is_client, prf_id, mac_id, aes; + unsigned cipher_key_len; + + cipher_key_len = T0_POP(); + aes = T0_POP(); + mac_id = T0_POP(); + prf_id = T0_POP(); + is_client = T0_POP(); + br_ssl_engine_switch_cbc_in(ENG, is_client, prf_id, mac_id, + aes ? ENG->iaes_cbcdec : ENG->ides_cbcdec, cipher_key_len); + + } + break; + case 75: { + /* switch-cbc-out */ + + int is_client, prf_id, mac_id, aes; + unsigned cipher_key_len; + + cipher_key_len = T0_POP(); + aes = T0_POP(); + mac_id = T0_POP(); + prf_id = T0_POP(); + is_client = T0_POP(); + br_ssl_engine_switch_cbc_out(ENG, is_client, prf_id, mac_id, + aes ? ENG->iaes_cbcenc : ENG->ides_cbcenc, cipher_key_len); + + } + break; + case 76: { + /* switch-chapol-in */ + + int is_client, prf_id; + + prf_id = T0_POP(); + is_client = T0_POP(); + br_ssl_engine_switch_chapol_in(ENG, is_client, prf_id); + + } + break; + case 77: { + /* switch-chapol-out */ + + int is_client, prf_id; + + prf_id = T0_POP(); + is_client = T0_POP(); + br_ssl_engine_switch_chapol_out(ENG, is_client, prf_id); + + } + break; + case 78: { + /* test-protocol-name */ + + size_t len = T0_POP(); + size_t u; + + for (u = 0; u < ENG->protocol_names_num; u ++) { + const char *name; + + name = ENG->protocol_names[u]; + if (len == strlen(name) && memcmp(ENG->pad, name, len) == 0) { + T0_PUSH(u); + T0_RET(); + } + } + T0_PUSHi(-1); + + } + break; + case 79: { + /* total-chain-length */ + + size_t u; + uint32_t total; + + total = 0; + for (u = 0; u < ENG->chain_len; u ++) { + total += 3 + (uint32_t)ENG->chain[u].data_len; + } + T0_PUSH(total); + + } + break; + case 80: { + /* u>> */ + + int c = (int)T0_POPi(); + uint32_t x = T0_POP(); + T0_PUSH(x >> c); + + } + break; + case 81: { + /* verify-SKE-sig */ + + size_t sig_len = T0_POP(); + int use_rsa = T0_POPi(); + int hash = T0_POPi(); + + T0_PUSH(verify_SKE_sig(CTX, hash, use_rsa, sig_len)); + + } + break; + case 82: { + /* write-blob-chunk */ + + size_t clen = ENG->hlen_out; + if (clen > 0) { + uint32_t addr, len; + + len = T0_POP(); + addr = T0_POP(); + if ((size_t)len < clen) { + clen = (size_t)len; + } + memcpy(ENG->hbuf_out, (unsigned char *)ENG + addr, clen); + if (ENG->record_type_out == BR_SSL_HANDSHAKE) { + br_multihash_update(&ENG->mhash, ENG->hbuf_out, clen); + } + T0_PUSH(addr + (uint32_t)clen); + T0_PUSH(len - (uint32_t)clen); + ENG->hbuf_out += clen; + ENG->hlen_out -= clen; + } + + } + break; + case 83: { + /* write8-native */ + + unsigned char x; + + x = (unsigned char)T0_POP(); + if (ENG->hlen_out > 0) { + if (ENG->record_type_out == BR_SSL_HANDSHAKE) { + br_multihash_update(&ENG->mhash, &x, 1); + } + *ENG->hbuf_out ++ = x; + ENG->hlen_out --; + T0_PUSHi(-1); + } else { + T0_PUSHi(0); + } + + } + break; + case 84: { + /* x509-append */ + + const br_x509_class *xc; + size_t len; + + xc = *(ENG->x509ctx); + len = T0_POP(); + xc->append(ENG->x509ctx, ENG->pad, len); + + } + break; + case 85: { + /* x509-end-cert */ + + const br_x509_class *xc; + + xc = *(ENG->x509ctx); + xc->end_cert(ENG->x509ctx); + + } + break; + case 86: { + /* x509-end-chain */ + + const br_x509_class *xc; + + xc = *(ENG->x509ctx); + T0_PUSH(xc->end_chain(ENG->x509ctx)); + + } + break; + case 87: { + /* x509-start-cert */ + + const br_x509_class *xc; + + xc = *(ENG->x509ctx); + xc->start_cert(ENG->x509ctx, T0_POP()); + + } + break; + case 88: { + /* x509-start-chain */ + + const br_x509_class *xc; + uint32_t bc; + + bc = T0_POP(); + xc = *(ENG->x509ctx); + xc->start_chain(ENG->x509ctx, bc ? ENG->server_name : NULL); + + } + break; + } + + } else { + T0_ENTER(ip, rp, t0x); + } + } +t0_exit: + ((t0_context *)t0ctx)->dp = dp; + ((t0_context *)t0ctx)->rp = rp; + ((t0_context *)t0ctx)->ip = ip; +} diff --git a/lib/bearssl-esp8266/src/ssl/ssl_hs_server.c b/lib/bearssl-esp8266/src/ssl/ssl_hs_server.c new file mode 100644 index 000000000..9bce99645 --- /dev/null +++ b/lib/bearssl-esp8266/src/ssl/ssl_hs_server.c @@ -0,0 +1,1995 @@ +/* Automatically generated code; do not modify directly. */ + +#include +#include +#include + +typedef struct { + uint32_t *dp; + uint32_t *rp; + const unsigned char *ip; +} t0_context; + +static uint32_t +t0_parse7E_unsigned(const unsigned char **p) +{ + uint32_t x; + + x = 0; + for (;;) { + unsigned y; + + y = pgm_read_byte((*p)++); + x = (x << 7) | (uint32_t)(y & 0x7F); + if (y < 0x80) { + return x; + } + } +} + +static int32_t +t0_parse7E_signed(const unsigned char **p) +{ + int neg; + uint32_t x; + + neg = (pgm_read_byte(*p) >> 6) & 1; + x = (uint32_t)-neg; + for (;;) { + unsigned y; + + y = pgm_read_byte((*p)++); + x = (x << 7) | (uint32_t)(y & 0x7F); + if (y < 0x80) { + if (neg) { + return -(int32_t)~x - 1; + } else { + return (int32_t)x; + } + } + } +} + +#define T0_VBYTE(x, n) (unsigned char)((((uint32_t)(x) >> (n)) & 0x7F) | 0x80) +#define T0_FBYTE(x, n) (unsigned char)(((uint32_t)(x) >> (n)) & 0x7F) +#define T0_SBYTE(x) (unsigned char)((((uint32_t)(x) >> 28) + 0xF8) ^ 0xF8) +#define T0_INT1(x) T0_FBYTE(x, 0) +#define T0_INT2(x) T0_VBYTE(x, 7), T0_FBYTE(x, 0) +#define T0_INT3(x) T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0) +#define T0_INT4(x) T0_VBYTE(x, 21), T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0) +#define T0_INT5(x) T0_SBYTE(x), T0_VBYTE(x, 21), T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0) + +/* static const unsigned char t0_datablock[]; */ + + +void br_ssl_hs_server_init_main(void *t0ctx); + +void br_ssl_hs_server_run(void *t0ctx); + + + +#include +#include + +#include "t_inner.h" + +/* + * This macro evaluates to a pointer to the current engine context. + */ +#define ENG ((br_ssl_engine_context *)(void *)((unsigned char *)t0ctx - offsetof(br_ssl_engine_context, cpu))) + + + + + +/* + * This macro evaluates to a pointer to the server context, under that + * specific name. It must be noted that since the engine context is the + * first field of the br_ssl_server_context structure ('eng'), then + * pointers values of both types are interchangeable, modulo an + * appropriate cast. This also means that "addresses" computed as offsets + * within the structure work for both kinds of context. + */ +#define CTX ((br_ssl_server_context *)ENG) + +/* + * Decrypt the pre-master secret (RSA key exchange). + */ +static void +do_rsa_decrypt(br_ssl_server_context *ctx, int prf_id, + unsigned char *epms, size_t len) +{ + uint32_t x; + unsigned char rpms[48]; + + /* + * Decrypt the PMS. + */ + x = (*ctx->policy_vtable)->do_keyx(ctx->policy_vtable, epms, &len); + + /* + * Set the first two bytes to the maximum supported client + * protocol version. These bytes are used for version rollback + * detection; forceing the two bytes will make the master secret + * wrong if the bytes are not correct. This process is + * recommended by RFC 5246 (section 7.4.7.1). + */ + br_enc16be(epms, ctx->client_max_version); + + /* + * Make a random PMS and copy it above the decrypted value if the + * decryption failed. Note that we use a constant-time conditional + * copy. + */ + br_hmac_drbg_generate(&ctx->eng.rng, rpms, sizeof rpms); + br_ccopy(x ^ 1, epms, rpms, sizeof rpms); + + /* + * Compute master secret. + */ + br_ssl_engine_compute_master(&ctx->eng, prf_id, epms, 48); + + /* + * Clear the pre-master secret from RAM: it is normally a buffer + * in the context, hence potentially long-lived. + */ + memset(epms, 0, len); +} + +/* + * Common part for ECDH and ECDHE. + */ +static void +ecdh_common(br_ssl_server_context *ctx, int prf_id, + unsigned char *xcoor, size_t xcoor_len, uint32_t ctl) +{ + unsigned char rpms[80]; + + if (xcoor_len > sizeof rpms) { + xcoor_len = sizeof rpms; + ctl = 0; + } + + /* + * Make a random PMS and copy it above the decrypted value if the + * decryption failed. Note that we use a constant-time conditional + * copy. + */ + br_hmac_drbg_generate(&ctx->eng.rng, rpms, xcoor_len); + br_ccopy(ctl ^ 1, xcoor, rpms, xcoor_len); + + /* + * Compute master secret. + */ + br_ssl_engine_compute_master(&ctx->eng, prf_id, xcoor, xcoor_len); + + /* + * Clear the pre-master secret from RAM: it is normally a buffer + * in the context, hence potentially long-lived. + */ + memset(xcoor, 0, xcoor_len); +} + +/* + * Do the ECDH key exchange (not ECDHE). + */ +static void +do_ecdh(br_ssl_server_context *ctx, int prf_id, + unsigned char *cpoint, size_t cpoint_len) +{ + uint32_t x; + + /* + * Finalise the key exchange. + */ + x = (*ctx->policy_vtable)->do_keyx(ctx->policy_vtable, + cpoint, &cpoint_len); + ecdh_common(ctx, prf_id, cpoint, cpoint_len, x); +} + +/* + * Do the full static ECDH key exchange. When this function is called, + * it has already been verified that the cipher suite uses ECDH (not ECDHE), + * and the client's public key (from its certificate) has type EC and is + * apt for key exchange. + */ +static void +do_static_ecdh(br_ssl_server_context *ctx, int prf_id) +{ + unsigned char cpoint[133]; + size_t cpoint_len; + const br_x509_class **xc; + const br_x509_pkey *pk; + + xc = ctx->eng.x509ctx; + pk = (*xc)->get_pkey(xc, NULL); + cpoint_len = pk->key.ec.qlen; + if (cpoint_len > sizeof cpoint) { + /* + * If the point is larger than our buffer then we need to + * restrict it. Length 2 is not a valid point length, so + * the ECDH will fail. + */ + cpoint_len = 2; + } + memcpy(cpoint, pk->key.ec.q, cpoint_len); + do_ecdh(ctx, prf_id, cpoint, cpoint_len); +} + +static size_t +hash_data(br_ssl_server_context *ctx, + void *dst, int hash_id, const void *src, size_t len) +{ + const br_hash_class *hf; + br_hash_compat_context hc; + + if (hash_id == 0) { + unsigned char tmp[36]; + + hf = br_multihash_getimpl(&ctx->eng.mhash, br_md5_ID); + if (hf == NULL) { + return 0; + } + hf->init(&hc.vtable); + hf->update(&hc.vtable, src, len); + hf->out(&hc.vtable, tmp); + hf = br_multihash_getimpl(&ctx->eng.mhash, br_sha1_ID); + if (hf == NULL) { + return 0; + } + hf->init(&hc.vtable); + hf->update(&hc.vtable, src, len); + hf->out(&hc.vtable, tmp + 16); + memcpy(dst, tmp, 36); + return 36; + } else { + hf = br_multihash_getimpl(&ctx->eng.mhash, hash_id); + if (hf == NULL) { + return 0; + } + hf->init(&hc.vtable); + hf->update(&hc.vtable, src, len); + hf->out(&hc.vtable, dst); + return (hf->desc >> BR_HASHDESC_OUT_OFF) & BR_HASHDESC_OUT_MASK; + } +} + +/* + * Do the ECDHE key exchange (part 1: generation of transient key, and + * computing of the point to send to the client). Returned value is the + * signature length (in bytes), or -x on error (with x being an error + * code). The encoded point is written in the ecdhe_point[] context buffer + * (length in ecdhe_point_len). + */ +static int +do_ecdhe_part1(br_ssl_server_context *ctx, int curve) +{ + unsigned algo_id; + unsigned mask; + const unsigned char *order; + size_t olen, glen; + size_t hv_len, sig_len; + + if (!((ctx->eng.iec->supported_curves >> curve) & 1)) { + return -BR_ERR_INVALID_ALGORITHM; + } + ctx->eng.ecdhe_curve = curve; + + /* + * Generate our private key. We need a non-zero random value + * which is lower than the curve order, in a "large enough" + * range. We force the top bit to 0 and bottom bit to 1, which + * does the trick. Note that contrary to what happens in ECDSA, + * this is not a problem if we do not cover the full range of + * possible values. + */ + order = ctx->eng.iec->order(curve, &olen); + mask = 0xFF; + while (mask >= pgm_read_byte(&order[0])) { + mask >>= 1; + } + br_hmac_drbg_generate(&ctx->eng.rng, ctx->ecdhe_key, olen); + ctx->ecdhe_key[0] &= mask; + ctx->ecdhe_key[olen - 1] |= 0x01; + ctx->ecdhe_key_len = olen; + + /* + * Compute our ECDH point. + */ + glen = ctx->eng.iec->mulgen(ctx->eng.ecdhe_point, + ctx->ecdhe_key, olen, curve); + ctx->eng.ecdhe_point_len = glen; + + /* + * Assemble the message to be signed, and possibly hash it. + */ + memcpy(ctx->eng.pad, ctx->eng.client_random, 32); + memcpy(ctx->eng.pad + 32, ctx->eng.server_random, 32); + ctx->eng.pad[64 + 0] = 0x03; + ctx->eng.pad[64 + 1] = 0x00; + ctx->eng.pad[64 + 2] = curve; + ctx->eng.pad[64 + 3] = ctx->eng.ecdhe_point_len; + memcpy(ctx->eng.pad + 64 + 4, + ctx->eng.ecdhe_point, ctx->eng.ecdhe_point_len); + hv_len = 64 + 4 + ctx->eng.ecdhe_point_len; + algo_id = ctx->sign_hash_id; + if (algo_id >= (unsigned)0xFF00) { + hv_len = hash_data(ctx, ctx->eng.pad, algo_id & 0xFF, + ctx->eng.pad, hv_len); + if (hv_len == 0) { + return -BR_ERR_INVALID_ALGORITHM; + } + } + + sig_len = (*ctx->policy_vtable)->do_sign(ctx->policy_vtable, + algo_id, ctx->eng.pad, hv_len, sizeof ctx->eng.pad); + return sig_len ? (int)sig_len : -BR_ERR_INVALID_ALGORITHM; +} + +/* + * Do the ECDHE key exchange (part 2: computation of the shared secret + * from the point sent by the client). + */ +static void +do_ecdhe_part2(br_ssl_server_context *ctx, int prf_id, + unsigned char *cpoint, size_t cpoint_len) +{ + int curve; + uint32_t ctl; + size_t xoff, xlen; + + curve = ctx->eng.ecdhe_curve; + + /* + * Finalise the key exchange. + */ + ctl = ctx->eng.iec->mul(cpoint, cpoint_len, + ctx->ecdhe_key, ctx->ecdhe_key_len, curve); + xoff = ctx->eng.iec->xoff(curve, &xlen); + ecdh_common(ctx, prf_id, cpoint + xoff, xlen, ctl); + + /* + * Clear the ECDHE private key. Forward Secrecy is achieved insofar + * as that key does not get stolen, so we'd better destroy it + * as soon as it ceases to be useful. + */ + memset(ctx->ecdhe_key, 0, ctx->ecdhe_key_len); +} + +/* + * Offset for hash value within the pad (when obtaining all hash values, + * in preparation for verification of the CertificateVerify message). + * Order is MD5, SHA-1, SHA-224, SHA-256, SHA-384, SHA-512; last value + * is used to get the total length. + */ +static const unsigned char HASH_PAD_OFF[] = { 0, 16, 36, 64, 96, 144, 208 }; + +/* + * OID for hash functions in RSA signatures. + */ +static const unsigned char HASH_OID[][10] PROGMEM = { + { 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A }, //HASH_OID_SHA1, + { 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04 }, // HASH_OID_SHA224, + { 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01 }, // HASH_OID_SHA256, + { 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02 }, // HASH_OID_SHA384, + { 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03 } // HASH_OID_SHA512 +}; + +/* + * Verify the signature in CertificateVerify. Returned value is 0 on + * success, or a non-zero error code. Lack of implementation of the + * designated signature algorithm is reported as a "bad signature" + * error (because it means that the peer did not honour our advertised + * set of supported signature algorithms). + */ +static int +verify_CV_sig(br_ssl_server_context *ctx, size_t sig_len) +{ + const br_x509_class **xc; + const br_x509_pkey *pk; + int id; + + id = ctx->hash_CV_id; + xc = ctx->eng.x509ctx; + pk = (*xc)->get_pkey(xc, NULL); + if (pk->key_type == BR_KEYTYPE_RSA) { + unsigned char tmp[64]; + unsigned char hash_oid_ram[10]; + const unsigned char *hash_oid; + + if (id == 0) { + hash_oid = NULL; + } else { + memcpy_P(hash_oid_ram, HASH_OID[id - 2], sizeof(HASH_OID[0])); + hash_oid = hash_oid_ram; + } + if (ctx->eng.irsavrfy == 0) { + return BR_ERR_BAD_SIGNATURE; + } + if (!ctx->eng.irsavrfy(ctx->eng.pad, sig_len, + hash_oid, ctx->hash_CV_len, &pk->key.rsa, tmp) + || memcmp(tmp, ctx->hash_CV, ctx->hash_CV_len) != 0) + { + return BR_ERR_BAD_SIGNATURE; + } + } else { + if (ctx->eng.iecdsa == 0) { + return BR_ERR_BAD_SIGNATURE; + } + if (!ctx->eng.iecdsa(ctx->eng.iec, + ctx->hash_CV, ctx->hash_CV_len, + &pk->key.ec, ctx->eng.pad, sig_len)) + { + return BR_ERR_BAD_SIGNATURE; + } + } + return 0; +} + + + +static const unsigned char t0_datablock[] PROGMEM = { + + 0x00, 0x00, 0x0A, 0x00, 0x24, 0x00, 0x2F, 0x01, 0x24, 0x00, 0x35, 0x02, + 0x24, 0x00, 0x3C, 0x01, 0x44, 0x00, 0x3D, 0x02, 0x44, 0x00, 0x9C, 0x03, + 0x04, 0x00, 0x9D, 0x04, 0x05, 0xC0, 0x03, 0x40, 0x24, 0xC0, 0x04, 0x41, + 0x24, 0xC0, 0x05, 0x42, 0x24, 0xC0, 0x08, 0x20, 0x24, 0xC0, 0x09, 0x21, + 0x24, 0xC0, 0x0A, 0x22, 0x24, 0xC0, 0x0D, 0x30, 0x24, 0xC0, 0x0E, 0x31, + 0x24, 0xC0, 0x0F, 0x32, 0x24, 0xC0, 0x12, 0x10, 0x24, 0xC0, 0x13, 0x11, + 0x24, 0xC0, 0x14, 0x12, 0x24, 0xC0, 0x23, 0x21, 0x44, 0xC0, 0x24, 0x22, + 0x55, 0xC0, 0x25, 0x41, 0x44, 0xC0, 0x26, 0x42, 0x55, 0xC0, 0x27, 0x11, + 0x44, 0xC0, 0x28, 0x12, 0x55, 0xC0, 0x29, 0x31, 0x44, 0xC0, 0x2A, 0x32, + 0x55, 0xC0, 0x2B, 0x23, 0x04, 0xC0, 0x2C, 0x24, 0x05, 0xC0, 0x2D, 0x43, + 0x04, 0xC0, 0x2E, 0x44, 0x05, 0xC0, 0x2F, 0x13, 0x04, 0xC0, 0x30, 0x14, + 0x05, 0xC0, 0x31, 0x33, 0x04, 0xC0, 0x32, 0x34, 0x05, 0xC0, 0x9C, 0x06, + 0x04, 0xC0, 0x9D, 0x07, 0x04, 0xC0, 0xA0, 0x08, 0x04, 0xC0, 0xA1, 0x09, + 0x04, 0xC0, 0xAC, 0x26, 0x04, 0xC0, 0xAD, 0x27, 0x04, 0xC0, 0xAE, 0x28, + 0x04, 0xC0, 0xAF, 0x29, 0x04, 0xCC, 0xA8, 0x15, 0x04, 0xCC, 0xA9, 0x25, + 0x04, 0x00, 0x00 +}; + +static const unsigned char t0_codeblock[] PROGMEM = { + + 0x00, 0x01, 0x00, 0x0B, 0x00, 0x00, 0x01, 0x00, 0x0E, 0x00, 0x00, 0x01, + 0x00, 0x0F, 0x00, 0x00, 0x01, 0x00, 0x10, 0x00, 0x00, 0x01, 0x01, 0x08, + 0x00, 0x00, 0x01, 0x01, 0x09, 0x00, 0x00, 0x01, 0x02, 0x08, 0x00, 0x00, + 0x01, 0x02, 0x09, 0x00, 0x00, 0x29, 0x29, 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_BAD_CCS), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_BAD_FINISHED), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_BAD_FRAGLEN), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_BAD_HANDSHAKE), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_BAD_PARAM), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_BAD_SECRENEG), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_BAD_SIGNATURE), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_BAD_VERSION), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_INVALID_ALGORITHM), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_LIMIT_EXCEEDED), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_NO_CLIENT_AUTH), 0x00, 0x00, 0x01, T0_INT1(BR_ERR_OK), + 0x00, 0x00, 0x01, T0_INT1(BR_ERR_OVERSIZED_ID), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_UNEXPECTED), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_WRONG_KEY_USAGE), 0x00, 0x00, 0x01, + T0_INT2(offsetof(br_ssl_engine_context, action)), 0x00, 0x00, 0x01, + T0_INT2(offsetof(br_ssl_engine_context, alert)), 0x00, 0x00, 0x01, + T0_INT2(offsetof(br_ssl_engine_context, application_data)), 0x00, 0x00, + 0x01, + T0_INT2(offsetof(br_ssl_engine_context, session) + offsetof(br_ssl_session_parameters, cipher_suite)), + 0x00, 0x00, 0x01, + T0_INT2(offsetof(br_ssl_server_context, client_max_version)), 0x00, + 0x00, 0x01, T0_INT2(offsetof(br_ssl_engine_context, client_random)), + 0x00, 0x00, 0x01, + T0_INT2(offsetof(br_ssl_server_context, client_suites)), 0x00, 0x00, + 0x01, T0_INT2(offsetof(br_ssl_server_context, client_suites_num)), + 0x00, 0x00, 0x01, + T0_INT2(offsetof(br_ssl_engine_context, close_received)), 0x00, 0x00, + 0x01, T0_INT2(offsetof(br_ssl_server_context, curves)), 0x00, 0x00, + 0x01, T0_INT2(offsetof(br_ssl_engine_context, ecdhe_point)), 0x00, + 0x00, 0x01, T0_INT2(offsetof(br_ssl_engine_context, ecdhe_point_len)), + 0x00, 0x00, 0x01, T0_INT2(offsetof(br_ssl_engine_context, flags)), + 0x00, 0x00, 0x01, T0_INT2(offsetof(br_ssl_server_context, hashes)), + 0x00, 0x00, 0x7B, 0x01, + T0_INT2(BR_MAX_CIPHER_SUITES * sizeof(br_suite_translated)), 0x00, + 0x00, 0x01, T0_INT2(offsetof(br_ssl_engine_context, log_max_frag_len)), + 0x00, 0x00, 0x01, T0_INT2(offsetof(br_ssl_engine_context, pad)), 0x00, + 0x00, 0x01, + T0_INT2(offsetof(br_ssl_engine_context, peer_log_max_frag_len)), 0x00, + 0x00, 0x01, + T0_INT2(offsetof(br_ssl_engine_context, protocol_names_num)), 0x00, + 0x00, 0x01, T0_INT2(offsetof(br_ssl_engine_context, record_type_in)), + 0x00, 0x00, 0x01, + T0_INT2(offsetof(br_ssl_engine_context, record_type_out)), 0x00, 0x00, + 0x01, T0_INT2(offsetof(br_ssl_engine_context, reneg)), 0x00, 0x00, + 0x01, T0_INT2(offsetof(br_ssl_engine_context, saved_finished)), 0x00, + 0x00, 0x01, + T0_INT2(offsetof(br_ssl_engine_context, selected_protocol)), 0x00, + 0x00, 0x01, T0_INT2(offsetof(br_ssl_engine_context, server_name)), + 0x00, 0x00, 0x01, + T0_INT2(offsetof(br_ssl_engine_context, server_random)), 0x00, 0x00, + 0x01, + T0_INT2(offsetof(br_ssl_engine_context, session) + offsetof(br_ssl_session_parameters, session_id)), + 0x00, 0x00, 0x01, + T0_INT2(offsetof(br_ssl_engine_context, session) + offsetof(br_ssl_session_parameters, session_id_len)), + 0x00, 0x00, 0x01, + T0_INT2(offsetof(br_ssl_engine_context, shutdown_recv)), 0x00, 0x00, + 0x01, T0_INT2(offsetof(br_ssl_server_context, sign_hash_id)), 0x00, + 0x00, 0x01, T0_INT2(offsetof(br_ssl_engine_context, suites_buf)), 0x00, + 0x00, 0x01, T0_INT2(offsetof(br_ssl_engine_context, suites_num)), 0x00, + 0x00, 0x01, + T0_INT2(offsetof(br_ssl_engine_context, session) + offsetof(br_ssl_session_parameters, version)), + 0x00, 0x00, 0x01, T0_INT2(offsetof(br_ssl_engine_context, version_in)), + 0x00, 0x00, 0x01, + T0_INT2(offsetof(br_ssl_engine_context, version_max)), 0x00, 0x00, + 0x01, T0_INT2(offsetof(br_ssl_engine_context, version_min)), 0x00, + 0x00, 0x01, T0_INT2(offsetof(br_ssl_engine_context, version_out)), + 0x00, 0x00, 0x09, 0x2A, 0x5D, 0x06, 0x02, 0x6A, 0x2B, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x03, 0x00, 0x9B, 0x2A, 0x63, 0x47, 0x9F, 0x2A, 0x05, + 0x04, 0x65, 0x01, 0x00, 0x00, 0x02, 0x00, 0x0F, 0x06, 0x02, 0x9F, 0x00, + 0x63, 0x04, 0x6B, 0x00, 0x06, 0x02, 0x6A, 0x2B, 0x00, 0x00, 0x2A, 0x8B, + 0x47, 0x05, 0x03, 0x01, 0x0C, 0x08, 0x47, 0x78, 0x2E, 0xA8, 0x1C, 0x85, + 0x01, 0x0C, 0x33, 0x00, 0x00, 0x2A, 0x22, 0x01, 0x08, 0x0C, 0x47, 0x61, + 0x22, 0x08, 0x00, 0x01, 0x03, 0x00, 0x77, 0x30, 0x02, 0x00, 0x38, 0x13, + 0x01, 0x01, 0x0C, 0x77, 0x42, 0x2C, 0x19, 0x38, 0x06, 0x07, 0x02, 0x00, + 0xD0, 0x03, 0x00, 0x04, 0x75, 0x01, 0x00, 0xC7, 0x02, 0x00, 0x2A, 0x19, + 0x13, 0x06, 0x02, 0x71, 0x2B, 0xD0, 0x04, 0x76, 0x00, 0x01, 0x00, 0x77, + 0x42, 0x01, 0x16, 0x89, 0x42, 0x01, 0x00, 0x8C, 0x40, 0x36, 0xB1, 0x35, + 0x06, 0x02, 0x73, 0x2B, 0x06, 0x0A, 0xD7, 0x01, 0x00, 0xD3, 0x01, 0x00, + 0xAD, 0x04, 0x80, 0x46, 0xD7, 0xD4, 0x29, 0xD9, 0x50, 0x06, 0x01, 0xD5, + 0xD8, 0x2C, 0x50, 0x06, 0x31, 0x01, 0x00, 0xAE, 0x2A, 0x5D, 0x06, 0x0F, + 0x01, 0x02, 0xA4, 0x05, 0x02, 0x37, 0x2B, 0x29, 0xB2, 0xB0, 0x2A, 0xC9, + 0x29, 0x04, 0x19, 0x2A, 0x5F, 0x06, 0x0B, 0x29, 0x01, 0x02, 0xA4, 0x05, + 0x02, 0x70, 0x2B, 0xB2, 0x04, 0x0A, 0xB4, 0x2A, 0x05, 0x04, 0x29, 0xAB, + 0x04, 0x02, 0xB3, 0xAF, 0x04, 0x01, 0xB2, 0x01, 0x00, 0xAD, 0x01, 0x00, + 0xD3, 0x3E, 0x01, 0x01, 0x77, 0x42, 0x01, 0x17, 0x89, 0x42, 0x00, 0x00, + 0x3A, 0x3A, 0x00, 0x01, 0x03, 0x00, 0x2C, 0x19, 0x38, 0x06, 0x04, 0xCF, + 0x29, 0x04, 0x78, 0x01, 0x02, 0x02, 0x00, 0xC6, 0x19, 0x38, 0x06, 0x04, + 0xCF, 0x29, 0x04, 0x78, 0x02, 0x00, 0x01, 0x84, 0x00, 0x08, 0x2B, 0x00, + 0x00, 0x81, 0x2F, 0x47, 0x12, 0x01, 0x01, 0x13, 0x37, 0x00, 0x00, 0x2A, + 0x05, 0x04, 0x29, 0x01, 0x7F, 0x00, 0x01, 0x00, 0xA2, 0x12, 0x01, 0x01, + 0x13, 0x5F, 0x06, 0x03, 0x61, 0x04, 0x75, 0x47, 0x29, 0x00, 0x00, 0x01, + 0x7F, 0xA1, 0xCF, 0x2A, 0x01, 0x07, 0x13, 0x01, 0x00, 0x3A, 0x0F, 0x06, + 0x0D, 0x29, 0x01, 0x10, 0x13, 0x06, 0x05, 0x01, 0x00, 0x77, 0x42, 0xC5, + 0x04, 0x33, 0x01, 0x01, 0x3A, 0x0F, 0x06, 0x2A, 0x29, 0x29, 0x8A, 0x30, + 0x01, 0x01, 0x0F, 0x01, 0x01, 0xA4, 0x39, 0x06, 0x18, 0xC8, 0x2C, 0x19, + 0x38, 0x06, 0x04, 0xCF, 0x29, 0x04, 0x78, 0x01, 0x80, 0x64, 0xC7, 0x01, + 0x01, 0x77, 0x42, 0x01, 0x17, 0x89, 0x42, 0x04, 0x03, 0x01, 0x00, 0xA1, + 0x04, 0x03, 0x73, 0x2B, 0x29, 0x04, 0xFF, 0x32, 0x01, 0x2A, 0x03, 0x00, + 0x09, 0x2A, 0x5D, 0x06, 0x02, 0x6A, 0x2B, 0x02, 0x00, 0x00, 0x00, 0x9C, + 0x01, 0x0F, 0x13, 0x00, 0x00, 0x76, 0x30, 0x01, 0x00, 0x3A, 0x0F, 0x06, + 0x10, 0x29, 0x2A, 0x01, 0x01, 0x0E, 0x06, 0x03, 0x29, 0x01, 0x02, 0x76, + 0x42, 0x01, 0x00, 0x04, 0x21, 0x01, 0x01, 0x3A, 0x0F, 0x06, 0x14, 0x29, + 0x01, 0x00, 0x76, 0x42, 0x2A, 0x01, 0x80, 0x64, 0x0F, 0x06, 0x05, 0x01, + 0x82, 0x00, 0x08, 0x2B, 0x5F, 0x04, 0x07, 0x29, 0x01, 0x82, 0x00, 0x08, + 0x2B, 0x29, 0x00, 0x00, 0x01, 0x00, 0x31, 0x06, 0x05, 0x3D, 0xA9, 0x39, + 0x04, 0x78, 0x2A, 0x06, 0x04, 0x01, 0x01, 0x91, 0x42, 0x00, 0x00, 0x01, + 0x1F, 0x13, 0x01, 0x12, 0x0F, 0x05, 0x02, 0x74, 0x2B, 0x78, 0x2E, 0x2A, + 0xCB, 0x05, 0x02, 0x73, 0x2B, 0xA8, 0x28, 0x00, 0x02, 0x87, 0x2E, 0x05, + 0x02, 0xBC, 0x00, 0xC0, 0xA7, 0xC0, 0xA7, 0x01, 0x7E, 0x03, 0x00, 0x2A, + 0x06, 0x17, 0xC2, 0x2A, 0x03, 0x01, 0x85, 0x47, 0xB6, 0x02, 0x01, 0x51, + 0x2A, 0x02, 0x00, 0x53, 0x06, 0x04, 0x03, 0x00, 0x04, 0x01, 0x29, 0x04, + 0x66, 0x9D, 0x9D, 0x02, 0x00, 0x61, 0x8C, 0x40, 0x00, 0x00, 0x31, 0x06, + 0x0B, 0x88, 0x30, 0x01, 0x14, 0x0E, 0x06, 0x02, 0x73, 0x2B, 0x04, 0x11, + 0xCF, 0x01, 0x07, 0x13, 0x2A, 0x01, 0x02, 0x0E, 0x06, 0x06, 0x06, 0x02, + 0x73, 0x2B, 0x04, 0x70, 0x29, 0xC3, 0x01, 0x01, 0x0E, 0x35, 0x39, 0x06, + 0x02, 0x66, 0x2B, 0x2A, 0x01, 0x01, 0xCA, 0x38, 0xB5, 0x00, 0x01, 0xBA, + 0x01, 0x0B, 0x0F, 0x05, 0x02, 0x73, 0x2B, 0x2A, 0x01, 0x03, 0x0F, 0x06, + 0x08, 0xC1, 0x06, 0x02, 0x6A, 0x2B, 0x47, 0x29, 0x00, 0x47, 0x5C, 0xC1, + 0xA7, 0x2A, 0x06, 0x23, 0xC1, 0xA7, 0x2A, 0x5B, 0x2A, 0x06, 0x18, 0x2A, + 0x01, 0x82, 0x00, 0x10, 0x06, 0x05, 0x01, 0x82, 0x00, 0x04, 0x01, 0x2A, + 0x03, 0x00, 0x85, 0x02, 0x00, 0xB6, 0x02, 0x00, 0x58, 0x04, 0x65, 0x9D, + 0x59, 0x04, 0x5A, 0x9D, 0x9D, 0x5A, 0x2A, 0x06, 0x02, 0x37, 0x00, 0x29, + 0x2D, 0x00, 0x02, 0x2A, 0x01, 0x20, 0x13, 0x05, 0x02, 0x74, 0x2B, 0x01, + 0x0F, 0x13, 0x03, 0x00, 0xB0, 0x95, 0x2E, 0x01, 0x86, 0x03, 0x11, 0x06, + 0x23, 0xC0, 0x2A, 0x01, 0x81, 0x7F, 0x13, 0x61, 0x01, 0x01, 0x12, 0x02, + 0x00, 0x0F, 0x05, 0x02, 0x6C, 0x2B, 0x01, 0x08, 0x12, 0x2A, 0x01, 0x02, + 0x0B, 0x3A, 0x01, 0x06, 0x10, 0x39, 0x06, 0x02, 0x6E, 0x2B, 0x04, 0x0D, + 0x02, 0x00, 0x01, 0x01, 0x0F, 0x06, 0x04, 0x01, 0x00, 0x04, 0x02, 0x01, + 0x02, 0x20, 0x05, 0x02, 0x6E, 0x2B, 0xC0, 0x2A, 0x03, 0x01, 0x2A, 0x01, + 0x84, 0x00, 0x10, 0x06, 0x02, 0x6F, 0x2B, 0x85, 0x47, 0xB6, 0x02, 0x01, + 0x55, 0x2A, 0x06, 0x01, 0x2B, 0x29, 0x9D, 0x00, 0x00, 0x1D, 0xBA, 0x01, + 0x0F, 0x0F, 0x05, 0x02, 0x73, 0x2B, 0x00, 0x0A, 0xBA, 0x01, 0x01, 0x0F, + 0x05, 0x02, 0x73, 0x2B, 0xC0, 0x2A, 0x03, 0x00, 0x79, 0x40, 0x7A, 0x01, + 0x20, 0xB6, 0xC2, 0x2A, 0x01, 0x20, 0x10, 0x06, 0x02, 0x72, 0x2B, 0x2A, + 0x90, 0x42, 0x8F, 0x47, 0xB6, 0x1A, 0x03, 0x01, 0xC0, 0xA7, 0x01, 0x00, + 0x03, 0x02, 0x01, 0x00, 0x03, 0x03, 0x83, 0xA2, 0x17, 0x3A, 0x08, 0x03, + 0x04, 0x03, 0x05, 0x2A, 0x06, 0x80, 0x6D, 0xC0, 0x2A, 0x03, 0x06, 0x02, + 0x01, 0x06, 0x0A, 0x2A, 0x78, 0x2E, 0x0F, 0x06, 0x04, 0x01, 0x7F, 0x03, + 0x03, 0x2A, 0x01, 0x81, 0x7F, 0x0F, 0x06, 0x0A, 0x8A, 0x30, 0x06, 0x02, + 0x6B, 0x2B, 0x01, 0x7F, 0x03, 0x02, 0x2A, 0x01, 0x81, 0xAC, 0x00, 0x0F, + 0x06, 0x11, 0x02, 0x00, 0x98, 0x2E, 0x11, 0x02, 0x00, 0x97, 0x2E, 0x0B, + 0x13, 0x06, 0x04, 0x01, 0x7F, 0x03, 0x00, 0xC4, 0x2A, 0x5D, 0x06, 0x03, + 0x29, 0x04, 0x26, 0x01, 0x00, 0xA4, 0x06, 0x0B, 0x01, 0x02, 0x0C, 0x7B, + 0x08, 0x02, 0x06, 0x47, 0x40, 0x04, 0x16, 0x29, 0x02, 0x05, 0x02, 0x04, + 0x11, 0x06, 0x02, 0x69, 0x2B, 0x02, 0x06, 0x02, 0x05, 0x40, 0x02, 0x05, + 0x01, 0x04, 0x08, 0x03, 0x05, 0x04, 0xFF, 0x0F, 0x29, 0x01, 0x00, 0x03, + 0x07, 0xC2, 0xA7, 0x2A, 0x06, 0x09, 0xC2, 0x05, 0x04, 0x01, 0x7F, 0x03, + 0x07, 0x04, 0x74, 0x9D, 0x01, 0x00, 0x8D, 0x42, 0x01, 0x88, 0x04, 0x82, + 0x41, 0x01, 0x84, 0x80, 0x80, 0x00, 0x7E, 0x41, 0x2A, 0x06, 0x80, 0x4E, + 0xC0, 0xA7, 0x2A, 0x06, 0x80, 0x47, 0xC0, 0x01, 0x00, 0x3A, 0x0F, 0x06, + 0x04, 0x29, 0xB9, 0x04, 0x39, 0x01, 0x01, 0x3A, 0x0F, 0x06, 0x04, 0x29, + 0xB7, 0x04, 0x2F, 0x01, 0x83, 0xFE, 0x01, 0x3A, 0x0F, 0x06, 0x04, 0x29, + 0xB8, 0x04, 0x23, 0x01, 0x0D, 0x3A, 0x0F, 0x06, 0x04, 0x29, 0xBE, 0x04, + 0x19, 0x01, 0x0A, 0x3A, 0x0F, 0x06, 0x04, 0x29, 0xBF, 0x04, 0x0F, 0x01, + 0x10, 0x3A, 0x0F, 0x06, 0x04, 0x29, 0xAC, 0x04, 0x05, 0x29, 0xBC, 0x01, + 0x00, 0x29, 0x04, 0xFF, 0x35, 0x9D, 0x9D, 0x02, 0x01, 0x02, 0x03, 0x13, + 0x03, 0x01, 0x02, 0x00, 0x5D, 0x06, 0x08, 0x79, 0x2E, 0x99, 0x40, 0x01, + 0x80, 0x56, 0xA3, 0x97, 0x2E, 0x2A, 0x02, 0x00, 0x10, 0x06, 0x03, 0x29, + 0x02, 0x00, 0x2A, 0x01, 0x86, 0x00, 0x0B, 0x06, 0x02, 0x6D, 0x2B, 0x02, + 0x00, 0x98, 0x2E, 0x0B, 0x06, 0x04, 0x01, 0x80, 0x46, 0xA3, 0x02, 0x01, + 0x06, 0x10, 0x95, 0x2E, 0x02, 0x00, 0x0D, 0x06, 0x05, 0x29, 0x95, 0x2E, + 0x04, 0x04, 0x01, 0x00, 0x03, 0x01, 0x2A, 0x95, 0x40, 0x2A, 0x96, 0x40, + 0x2A, 0x99, 0x40, 0x01, 0x86, 0x03, 0x11, 0x03, 0x08, 0x02, 0x02, 0x06, + 0x04, 0x01, 0x02, 0x8A, 0x42, 0x8A, 0x30, 0x05, 0x04, 0x01, 0x01, 0x8A, + 0x42, 0x02, 0x07, 0x05, 0x03, 0x01, 0x28, 0xA3, 0x44, 0x29, 0x01, 0x82, + 0x01, 0x07, 0x01, 0xFC, 0x80, 0x00, 0x39, 0x82, 0x2F, 0x13, 0x2A, 0x82, + 0x41, 0x2A, 0x01, 0x81, 0x7F, 0x13, 0x5E, 0x37, 0x47, 0x01, 0x08, 0x12, + 0x5E, 0x01, 0x02, 0x13, 0x39, 0x01, 0x0C, 0x0C, 0x03, 0x09, 0x7E, 0x2F, + 0x43, 0x13, 0x2A, 0x7E, 0x41, 0x05, 0x04, 0x01, 0x00, 0x03, 0x09, 0x02, + 0x01, 0x06, 0x03, 0x01, 0x7F, 0x00, 0x8F, 0x01, 0x20, 0x34, 0x01, 0x20, + 0x90, 0x42, 0x7B, 0x2A, 0x03, 0x05, 0x2A, 0x02, 0x04, 0x0B, 0x06, 0x80, + 0x49, 0x2A, 0x2E, 0x2A, 0x9C, 0x2A, 0x01, 0x0C, 0x12, 0x2A, 0x01, 0x01, + 0x0F, 0x47, 0x01, 0x02, 0x0F, 0x39, 0x06, 0x0A, 0x2A, 0x02, 0x09, 0x13, + 0x05, 0x04, 0x65, 0x01, 0x00, 0x2A, 0x02, 0x08, 0x05, 0x0E, 0x2A, 0x01, + 0x81, 0x70, 0x13, 0x01, 0x20, 0x0E, 0x06, 0x04, 0x65, 0x01, 0x00, 0x2A, + 0x2A, 0x06, 0x10, 0x02, 0x05, 0x63, 0x40, 0x02, 0x05, 0x40, 0x02, 0x05, + 0x01, 0x04, 0x08, 0x03, 0x05, 0x04, 0x01, 0x65, 0x01, 0x04, 0x08, 0x04, + 0xFF, 0x30, 0x29, 0x02, 0x05, 0x7B, 0x09, 0x01, 0x02, 0x12, 0x2A, 0x05, + 0x03, 0x01, 0x28, 0xA3, 0x7C, 0x42, 0x8C, 0x2E, 0x01, 0x83, 0xFF, 0x7F, + 0x0F, 0x06, 0x0D, 0x01, 0x03, 0xA4, 0x06, 0x04, 0x01, 0x80, 0x78, 0xA3, + 0x01, 0x00, 0x8C, 0x40, 0x18, 0x05, 0x03, 0x01, 0x28, 0xA3, 0x01, 0x00, + 0x00, 0x00, 0xB4, 0xB3, 0x00, 0x04, 0x78, 0x2E, 0xCE, 0x06, 0x16, 0xC0, + 0x2A, 0x01, 0x84, 0x00, 0x10, 0x06, 0x02, 0x6F, 0x2B, 0x2A, 0x03, 0x00, + 0x85, 0x47, 0xB6, 0x02, 0x00, 0x78, 0x2E, 0xA8, 0x27, 0x78, 0x2E, 0x2A, + 0xCC, 0x47, 0xCB, 0x03, 0x01, 0x03, 0x02, 0x02, 0x01, 0x02, 0x02, 0x39, + 0x06, 0x14, 0xC2, 0x2A, 0x03, 0x03, 0x85, 0x47, 0xB6, 0x02, 0x03, 0x78, + 0x2E, 0xA8, 0x02, 0x02, 0x06, 0x03, 0x26, 0x04, 0x01, 0x24, 0x9D, 0x00, + 0x00, 0xBA, 0x01, 0x10, 0x0F, 0x05, 0x02, 0x73, 0x2B, 0x00, 0x00, 0x9E, + 0xBA, 0x01, 0x14, 0x0E, 0x06, 0x02, 0x73, 0x2B, 0x85, 0x01, 0x0C, 0x08, + 0x01, 0x0C, 0xB6, 0x9D, 0x85, 0x2A, 0x01, 0x0C, 0x08, 0x01, 0x0C, 0x32, + 0x05, 0x02, 0x67, 0x2B, 0x00, 0x02, 0x03, 0x00, 0x03, 0x01, 0x02, 0x00, + 0x9A, 0x02, 0x01, 0x02, 0x00, 0x3C, 0x2A, 0x01, 0x00, 0x0F, 0x06, 0x02, + 0x65, 0x00, 0xD1, 0x04, 0x74, 0x00, 0xC0, 0x01, 0x01, 0x0E, 0x06, 0x02, + 0x68, 0x2B, 0xC2, 0x2A, 0x2A, 0x5F, 0x47, 0x01, 0x05, 0x11, 0x39, 0x06, + 0x02, 0x68, 0x2B, 0x01, 0x08, 0x08, 0x2A, 0x84, 0x30, 0x0B, 0x06, 0x0D, + 0x2A, 0x01, 0x01, 0x47, 0x0C, 0x3F, 0x2A, 0x84, 0x42, 0x86, 0x42, 0x04, + 0x01, 0x29, 0x00, 0x00, 0xC0, 0x8A, 0x30, 0x01, 0x00, 0x3A, 0x0F, 0x06, + 0x13, 0x29, 0x01, 0x01, 0x0F, 0x05, 0x02, 0x6B, 0x2B, 0xC2, 0x06, 0x02, + 0x6B, 0x2B, 0x01, 0x02, 0x8A, 0x42, 0x04, 0x28, 0x01, 0x02, 0x3A, 0x0F, + 0x06, 0x1F, 0x29, 0x01, 0x0D, 0x0F, 0x05, 0x02, 0x6B, 0x2B, 0xC2, 0x01, + 0x0C, 0x0F, 0x05, 0x02, 0x6B, 0x2B, 0x85, 0x01, 0x0C, 0xB6, 0x8B, 0x85, + 0x01, 0x0C, 0x32, 0x05, 0x02, 0x6B, 0x2B, 0x04, 0x03, 0x6B, 0x2B, 0x29, + 0x00, 0x00, 0xC0, 0xA7, 0xC0, 0xA7, 0x2A, 0x06, 0x1D, 0xC2, 0x06, 0x03, + 0xBC, 0x04, 0x15, 0xC0, 0x2A, 0x01, 0x81, 0x7F, 0x0D, 0x06, 0x0C, 0x2A, + 0x8D, 0x08, 0x01, 0x00, 0x47, 0x42, 0x8D, 0x47, 0xB6, 0x04, 0x01, 0xC9, + 0x04, 0x60, 0x9D, 0x9D, 0x00, 0x00, 0xBB, 0x2A, 0x5F, 0x06, 0x07, 0x29, + 0x06, 0x02, 0x69, 0x2B, 0x04, 0x74, 0x00, 0x00, 0xC3, 0x01, 0x03, 0xC1, + 0x47, 0x29, 0x47, 0x00, 0x00, 0xC0, 0xC9, 0x00, 0x03, 0x01, 0x00, 0x03, + 0x00, 0xC0, 0xA7, 0x2A, 0x06, 0x80, 0x50, 0xC2, 0x03, 0x01, 0xC2, 0x03, + 0x02, 0x02, 0x01, 0x01, 0x08, 0x0F, 0x06, 0x16, 0x02, 0x02, 0x01, 0x0F, + 0x0D, 0x06, 0x0D, 0x01, 0x01, 0x02, 0x02, 0x01, 0x10, 0x08, 0x0C, 0x02, + 0x00, 0x39, 0x03, 0x00, 0x04, 0x2A, 0x02, 0x01, 0x01, 0x02, 0x11, 0x02, + 0x01, 0x01, 0x06, 0x0D, 0x13, 0x02, 0x02, 0x01, 0x01, 0x0F, 0x02, 0x02, + 0x01, 0x03, 0x0F, 0x39, 0x13, 0x06, 0x11, 0x02, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x62, 0x01, 0x02, 0x0C, 0x02, 0x01, 0x08, 0x0C, 0x39, 0x03, 0x00, + 0x04, 0xFF, 0x2C, 0x9D, 0x02, 0x00, 0x00, 0x00, 0xC0, 0xA7, 0xBD, 0x82, + 0x41, 0x9D, 0x00, 0x00, 0xC0, 0xA7, 0xC0, 0xA7, 0x01, 0x00, 0x7E, 0x41, + 0x2A, 0x06, 0x15, 0xC0, 0x2A, 0x01, 0x20, 0x0B, 0x06, 0x0B, 0x01, 0x01, + 0x47, 0x0C, 0x7E, 0x2F, 0x39, 0x7E, 0x41, 0x04, 0x01, 0x29, 0x04, 0x68, + 0x9D, 0x9D, 0x00, 0x00, 0x01, 0x02, 0x9A, 0xC3, 0x01, 0x08, 0x0C, 0xC3, + 0x08, 0x00, 0x00, 0x01, 0x03, 0x9A, 0xC3, 0x01, 0x08, 0x0C, 0xC3, 0x08, + 0x01, 0x08, 0x0C, 0xC3, 0x08, 0x00, 0x00, 0x01, 0x01, 0x9A, 0xC3, 0x00, + 0x00, 0x3D, 0x2A, 0x5D, 0x05, 0x01, 0x00, 0x29, 0xD1, 0x04, 0x76, 0x02, + 0x03, 0x00, 0x94, 0x30, 0x03, 0x01, 0x01, 0x00, 0x2A, 0x02, 0x01, 0x0B, + 0x06, 0x10, 0x2A, 0x01, 0x01, 0x0C, 0x93, 0x08, 0x2E, 0x02, 0x00, 0x0F, + 0x06, 0x01, 0x00, 0x61, 0x04, 0x6A, 0x29, 0x01, 0x7F, 0x00, 0x00, 0x2C, + 0x19, 0x38, 0x06, 0x04, 0xCF, 0x29, 0x04, 0x78, 0x01, 0x16, 0x89, 0x42, + 0x01, 0x00, 0xE2, 0x01, 0x00, 0xE1, 0x2C, 0x01, 0x17, 0x89, 0x42, 0x00, + 0x00, 0x01, 0x15, 0x89, 0x42, 0x47, 0x57, 0x29, 0x57, 0x29, 0x2C, 0x00, + 0x00, 0x01, 0x01, 0x47, 0xC6, 0x00, 0x00, 0xBB, 0x01, 0x01, 0x0F, 0x05, + 0x02, 0x73, 0x2B, 0x2A, 0xC9, 0x29, 0x00, 0x00, 0x47, 0x3A, 0x9A, 0x47, + 0x2A, 0x06, 0x05, 0xC3, 0x29, 0x62, 0x04, 0x78, 0x29, 0x00, 0x02, 0x03, + 0x00, 0x78, 0x2E, 0x9C, 0x03, 0x01, 0x02, 0x01, 0x01, 0x0F, 0x13, 0x02, + 0x01, 0x01, 0x04, 0x12, 0x01, 0x0F, 0x13, 0x02, 0x01, 0x01, 0x08, 0x12, + 0x01, 0x0F, 0x13, 0x01, 0x00, 0x3A, 0x0F, 0x06, 0x10, 0x29, 0x01, 0x00, + 0x01, 0x18, 0x02, 0x00, 0x06, 0x03, 0x4C, 0x04, 0x01, 0x4D, 0x04, 0x81, + 0x0D, 0x01, 0x01, 0x3A, 0x0F, 0x06, 0x10, 0x29, 0x01, 0x01, 0x01, 0x10, + 0x02, 0x00, 0x06, 0x03, 0x4C, 0x04, 0x01, 0x4D, 0x04, 0x80, 0x77, 0x01, + 0x02, 0x3A, 0x0F, 0x06, 0x10, 0x29, 0x01, 0x01, 0x01, 0x20, 0x02, 0x00, + 0x06, 0x03, 0x4C, 0x04, 0x01, 0x4D, 0x04, 0x80, 0x61, 0x01, 0x03, 0x3A, + 0x0F, 0x06, 0x0F, 0x29, 0x29, 0x01, 0x10, 0x02, 0x00, 0x06, 0x03, 0x4A, + 0x04, 0x01, 0x4B, 0x04, 0x80, 0x4C, 0x01, 0x04, 0x3A, 0x0F, 0x06, 0x0E, + 0x29, 0x29, 0x01, 0x20, 0x02, 0x00, 0x06, 0x03, 0x4A, 0x04, 0x01, 0x4B, + 0x04, 0x38, 0x01, 0x05, 0x3A, 0x0F, 0x06, 0x0C, 0x29, 0x29, 0x02, 0x00, + 0x06, 0x03, 0x4E, 0x04, 0x01, 0x4F, 0x04, 0x26, 0x2A, 0x01, 0x09, 0x10, + 0x06, 0x02, 0x6A, 0x2B, 0x47, 0x29, 0x2A, 0x01, 0x01, 0x13, 0x01, 0x04, + 0x0C, 0x01, 0x10, 0x08, 0x47, 0x01, 0x08, 0x13, 0x01, 0x10, 0x47, 0x09, + 0x02, 0x00, 0x06, 0x03, 0x48, 0x04, 0x01, 0x49, 0x00, 0x29, 0x00, 0x00, + 0x9C, 0x01, 0x0C, 0x12, 0x01, 0x02, 0x10, 0x00, 0x00, 0x9C, 0x01, 0x0C, + 0x12, 0x2A, 0x60, 0x47, 0x01, 0x03, 0x0B, 0x13, 0x00, 0x00, 0x9C, 0x01, + 0x0C, 0x12, 0x01, 0x01, 0x0F, 0x00, 0x00, 0x9C, 0x01, 0x0C, 0x12, 0x5F, + 0x00, 0x00, 0x1B, 0x01, 0x00, 0x75, 0x30, 0x2A, 0x06, 0x22, 0x01, 0x01, + 0x3A, 0x0F, 0x06, 0x06, 0x29, 0x01, 0x00, 0xA0, 0x04, 0x14, 0x01, 0x02, + 0x3A, 0x0F, 0x06, 0x0D, 0x29, 0x77, 0x30, 0x01, 0x01, 0x0F, 0x06, 0x03, + 0x01, 0x10, 0x39, 0x04, 0x01, 0x29, 0x04, 0x01, 0x29, 0x7D, 0x30, 0x05, + 0x33, 0x31, 0x06, 0x30, 0x88, 0x30, 0x01, 0x14, 0x3A, 0x0F, 0x06, 0x06, + 0x29, 0x01, 0x02, 0x39, 0x04, 0x22, 0x01, 0x15, 0x3A, 0x0F, 0x06, 0x09, + 0x29, 0xAA, 0x06, 0x03, 0x01, 0x7F, 0xA0, 0x04, 0x13, 0x01, 0x16, 0x3A, + 0x0F, 0x06, 0x06, 0x29, 0x01, 0x01, 0x39, 0x04, 0x07, 0x29, 0x01, 0x04, + 0x39, 0x01, 0x00, 0x29, 0x19, 0x06, 0x03, 0x01, 0x08, 0x39, 0x00, 0x00, + 0x1B, 0x2A, 0x05, 0x13, 0x31, 0x06, 0x10, 0x88, 0x30, 0x01, 0x15, 0x0F, + 0x06, 0x08, 0x29, 0xAA, 0x01, 0x00, 0x77, 0x42, 0x04, 0x01, 0x23, 0x00, + 0x00, 0xCF, 0x01, 0x07, 0x13, 0x01, 0x01, 0x10, 0x06, 0x02, 0x73, 0x2B, + 0x00, 0x01, 0x03, 0x00, 0x2C, 0x19, 0x06, 0x05, 0x02, 0x00, 0x89, 0x42, + 0x00, 0xCF, 0x29, 0x04, 0x74, 0x00, 0x01, 0x14, 0xD2, 0x01, 0x01, 0xE2, + 0x2C, 0x2A, 0x01, 0x00, 0xCA, 0x01, 0x16, 0xD2, 0xD6, 0x2C, 0x00, 0x00, + 0x01, 0x0B, 0xE2, 0x52, 0x2A, 0x2A, 0x01, 0x03, 0x08, 0xE1, 0xE1, 0x14, + 0x2A, 0x5D, 0x06, 0x02, 0x29, 0x00, 0xE1, 0x1E, 0x2A, 0x06, 0x05, 0x85, + 0x47, 0xDA, 0x04, 0x77, 0x29, 0x04, 0x6C, 0x00, 0x01, 0x00, 0xDC, 0x95, + 0x2E, 0x01, 0x86, 0x03, 0x11, 0x06, 0x05, 0x63, 0x01, 0x00, 0xDD, 0x08, + 0x50, 0x08, 0x01, 0x03, 0x08, 0x01, 0x0D, 0xE2, 0xE1, 0x01, 0x00, 0xDC, + 0xE2, 0x01, 0x01, 0xDC, 0x29, 0x95, 0x2E, 0x01, 0x86, 0x03, 0x11, 0x06, + 0x08, 0x01, 0x00, 0xDD, 0xE0, 0x01, 0x01, 0xDD, 0x29, 0x50, 0xE0, 0x16, + 0x15, 0x2A, 0x5D, 0x06, 0x02, 0x29, 0x00, 0xE0, 0x1F, 0x2A, 0x06, 0x05, + 0x85, 0x47, 0xDA, 0x04, 0x77, 0x29, 0x04, 0x6C, 0x00, 0x9E, 0x01, 0x14, + 0xE2, 0x01, 0x0C, 0xE1, 0x85, 0x01, 0x0C, 0xDA, 0x00, 0x04, 0x03, 0x00, + 0x01, 0x02, 0xE2, 0x01, 0x80, 0x46, 0x8A, 0x30, 0x01, 0x02, 0x0F, 0x06, + 0x0C, 0x02, 0x00, 0x06, 0x04, 0x01, 0x05, 0x04, 0x02, 0x01, 0x1D, 0x04, + 0x02, 0x01, 0x00, 0x03, 0x01, 0x86, 0x30, 0x06, 0x04, 0x01, 0x05, 0x04, + 0x02, 0x01, 0x00, 0x03, 0x02, 0x8C, 0x2E, 0x2A, 0x06, 0x05, 0x62, 0x21, + 0x01, 0x07, 0x08, 0x03, 0x03, 0x02, 0x01, 0x02, 0x02, 0x08, 0x02, 0x03, + 0x08, 0x2A, 0x06, 0x03, 0x01, 0x02, 0x08, 0x08, 0xE1, 0x95, 0x2E, 0xE0, + 0x8E, 0x01, 0x04, 0x17, 0x8E, 0x01, 0x04, 0x08, 0x01, 0x1C, 0x34, 0x8E, + 0x01, 0x20, 0xDA, 0x01, 0x20, 0xE2, 0x8F, 0x01, 0x20, 0xDA, 0x78, 0x2E, + 0xE0, 0x01, 0x00, 0xE2, 0x02, 0x01, 0x02, 0x02, 0x08, 0x02, 0x03, 0x08, + 0x2A, 0x06, 0x80, 0x40, 0xE0, 0x02, 0x01, 0x2A, 0x06, 0x10, 0x01, 0x83, + 0xFE, 0x01, 0xE0, 0x01, 0x04, 0x09, 0x2A, 0xE0, 0x62, 0x8B, 0x47, 0xDB, + 0x04, 0x01, 0x29, 0x02, 0x02, 0x06, 0x0C, 0x01, 0x01, 0xE0, 0x01, 0x01, + 0xE0, 0x86, 0x30, 0x01, 0x08, 0x09, 0xE2, 0x02, 0x03, 0x2A, 0x06, 0x11, + 0x01, 0x10, 0xE0, 0x01, 0x04, 0x09, 0x2A, 0xE0, 0x64, 0x2A, 0xE0, 0x62, + 0x85, 0x47, 0xDB, 0x04, 0x01, 0x29, 0x04, 0x01, 0x29, 0x00, 0x00, 0x01, + 0x0E, 0xE2, 0x01, 0x00, 0xE1, 0x00, 0x03, 0x78, 0x2E, 0xCC, 0x05, 0x01, + 0x00, 0x7E, 0x2F, 0x2A, 0x01, 0x82, 0x80, 0x80, 0x80, 0x00, 0x13, 0x06, + 0x05, 0x29, 0x01, 0x1D, 0x04, 0x0E, 0x2A, 0x01, 0x83, 0xC0, 0x80, 0x80, + 0x00, 0x13, 0x2A, 0x06, 0x01, 0x47, 0x29, 0xA5, 0x03, 0x00, 0x02, 0x00, + 0x25, 0x2A, 0x5D, 0x06, 0x02, 0x37, 0x2B, 0x03, 0x01, 0x95, 0x2E, 0x01, + 0x86, 0x03, 0x11, 0x03, 0x02, 0x01, 0x0C, 0xE2, 0x02, 0x01, 0x80, 0x30, + 0x08, 0x02, 0x02, 0x01, 0x02, 0x13, 0x08, 0x01, 0x06, 0x08, 0xE1, 0x01, + 0x03, 0xE2, 0x02, 0x00, 0xE0, 0x7F, 0x80, 0x30, 0xDB, 0x02, 0x02, 0x06, + 0x1C, 0x92, 0x2E, 0x2A, 0x01, 0x83, 0xFE, 0x00, 0x0B, 0x06, 0x03, 0xE0, + 0x04, 0x0F, 0x01, 0x81, 0x7F, 0x13, 0xE2, 0x78, 0x2E, 0xCD, 0x01, 0x01, + 0x0C, 0x01, 0x03, 0x08, 0xE2, 0x02, 0x01, 0xE0, 0x85, 0x02, 0x01, 0xDA, + 0x00, 0x00, 0x56, 0x2A, 0x01, 0x00, 0x0F, 0x06, 0x02, 0x65, 0x00, 0xCF, + 0x29, 0x04, 0x73, 0x00, 0x2A, 0xE2, 0xDA, 0x00, 0x00, 0x01, 0x00, 0x78, + 0x2E, 0xCB, 0x06, 0x0C, 0x63, 0x3A, 0x06, 0x08, 0x01, 0x80, 0x41, 0xE2, + 0x01, 0x80, 0x42, 0xE2, 0x46, 0x06, 0x07, 0x61, 0x3A, 0x06, 0x03, 0x01, + 0x01, 0xE2, 0x45, 0x06, 0x08, 0x61, 0x3A, 0x06, 0x04, 0x01, 0x80, 0x40, + 0xE2, 0x47, 0x29, 0x00, 0x01, 0x01, 0x00, 0x03, 0x00, 0x46, 0x45, 0x39, + 0x05, 0x14, 0x01, 0x01, 0x01, 0x80, 0x7C, 0xDE, 0x03, 0x00, 0x01, 0x03, + 0x01, 0x80, 0x7C, 0xDE, 0x02, 0x00, 0x08, 0x47, 0x29, 0x00, 0x46, 0x06, + 0x07, 0x01, 0x01, 0x44, 0x29, 0xDE, 0x03, 0x00, 0x45, 0x06, 0x0A, 0x01, + 0x03, 0x44, 0x29, 0xDE, 0x02, 0x00, 0x08, 0x03, 0x00, 0x29, 0x02, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x01, 0x04, 0xDF, 0x01, 0x05, 0xDF, 0x01, 0x06, + 0xDF, 0x01, 0x03, 0xDF, 0x01, 0x02, 0xDF, 0x0A, 0x65, 0x00, 0x01, 0x03, + 0x00, 0x3A, 0x01, 0x01, 0x02, 0x00, 0x0C, 0x13, 0x05, 0x01, 0x00, 0x63, + 0x01, 0x03, 0x3B, 0x06, 0x07, 0x02, 0x00, 0xE2, 0x01, 0x02, 0x3B, 0xE2, + 0x00, 0x00, 0x2A, 0x01, 0x08, 0x54, 0xE2, 0xE2, 0x00, 0x00, 0x2A, 0x01, + 0x10, 0x54, 0xE2, 0xE0, 0x00, 0x00, 0x2A, 0x57, 0x06, 0x02, 0x29, 0x00, + 0xCF, 0x29, 0x04, 0x76 +}; + +static const uint16_t t0_caddr[] PROGMEM = { + + 0, + 5, + 10, + 15, + 20, + 25, + 30, + 35, + 40, + 44, + 48, + 52, + 56, + 60, + 64, + 68, + 72, + 76, + 80, + 84, + 88, + 92, + 96, + 100, + 104, + 109, + 114, + 119, + 124, + 129, + 134, + 139, + 144, + 149, + 154, + 159, + 164, + 169, + 174, + 180, + 185, + 190, + 195, + 200, + 205, + 210, + 215, + 220, + 225, + 230, + 235, + 240, + 245, + 250, + 255, + 260, + 265, + 270, + 275, + 280, + 285, + 290, + 299, + 303, + 328, + 334, + 353, + 364, + 405, + 516, + 520, + 553, + 563, + 587, + 669, + 683, + 689, + 748, + 767, + 789, + 838, + 887, + 963, + 1065, + 1076, + 1670, + 1674, + 1741, + 1751, + 1782, + 1806, + 1852, + 1922, + 1962, + 1976, + 1985, + 1989, + 2084, + 2092, + 2128, + 2139, + 2155, + 2161, + 2172, + 2207, + 2233, + 2245, + 2251, + 2264, + 2279, + 2472, + 2481, + 2494, + 2503, + 2510, + 2616, + 2641, + 2654, + 2670, + 2688, + 2720, + 2793, + 2806, + 2987, + 2995, + 3122, + 3136, + 3141, + 3185, + 3242, + 3263, + 3290, + 3298, + 3306 +}; + +#define T0_INTERPRETED 93 + +#define T0_ENTER(ip, rp, slot) do { \ + const unsigned char *t0_newip; \ + uint32_t t0_lnum; \ + t0_newip = &t0_codeblock[pgm_read_word(&t0_caddr[(slot) - T0_INTERPRETED])]; \ + t0_lnum = t0_parse7E_unsigned(&t0_newip); \ + (rp) += t0_lnum; \ + *((rp) ++) = (uint32_t)((ip) - &t0_codeblock[0]) + (t0_lnum << 16); \ + (ip) = t0_newip; \ + } while (0) + +#define T0_DEFENTRY(name, slot) \ +void \ +name(void *ctx) \ +{ \ + t0_context *t0ctx = ctx; \ + t0ctx->ip = &t0_codeblock[0]; \ + T0_ENTER(t0ctx->ip, t0ctx->rp, slot); \ +} + +T0_DEFENTRY(br_ssl_hs_server_init_main, 166) + +#define T0_NEXT(t0ipp) (pgm_read_byte((*t0ipp)++)) + +void +br_ssl_hs_server_run(void *t0ctx) +{ + uint32_t *dp, *rp; + const unsigned char *ip; + +#define T0_LOCAL(x) (*(rp - 2 - (x))) +#define T0_POP() (*-- dp) +#define T0_POPi() (*(int32_t *)(-- dp)) +#define T0_PEEK(x) (*(dp - 1 - (x))) +#define T0_PEEKi(x) (*(int32_t *)(dp - 1 - (x))) +#define T0_PUSH(v) do { *dp = (v); dp ++; } while (0) +#define T0_PUSHi(v) do { *(int32_t *)dp = (v); dp ++; } while (0) +#define T0_RPOP() (*-- rp) +#define T0_RPOPi() (*(int32_t *)(-- rp)) +#define T0_RPUSH(v) do { *rp = (v); rp ++; } while (0) +#define T0_RPUSHi(v) do { *(int32_t *)rp = (v); rp ++; } while (0) +#define T0_ROLL(x) do { \ + size_t t0len = (size_t)(x); \ + uint32_t t0tmp = *(dp - 1 - t0len); \ + memmove(dp - t0len - 1, dp - t0len, t0len * sizeof *dp); \ + *(dp - 1) = t0tmp; \ +} while (0) +#define T0_SWAP() do { \ + uint32_t t0tmp = *(dp - 2); \ + *(dp - 2) = *(dp - 1); \ + *(dp - 1) = t0tmp; \ +} while (0) +#define T0_ROT() do { \ + uint32_t t0tmp = *(dp - 3); \ + *(dp - 3) = *(dp - 2); \ + *(dp - 2) = *(dp - 1); \ + *(dp - 1) = t0tmp; \ +} while (0) +#define T0_NROT() do { \ + uint32_t t0tmp = *(dp - 1); \ + *(dp - 1) = *(dp - 2); \ + *(dp - 2) = *(dp - 3); \ + *(dp - 3) = t0tmp; \ +} while (0) +#define T0_PICK(x) do { \ + uint32_t t0depth = (x); \ + T0_PUSH(T0_PEEK(t0depth)); \ +} while (0) +#define T0_CO() do { \ + goto t0_exit; \ +} while (0) +#define T0_RET() goto t0_next + + dp = ((t0_context *)t0ctx)->dp; + rp = ((t0_context *)t0ctx)->rp; + ip = ((t0_context *)t0ctx)->ip; + goto t0_next; + for (;;) { + uint32_t t0x; + + t0_next: + t0x = T0_NEXT(&ip); + if (t0x < T0_INTERPRETED) { + switch (t0x) { + int32_t t0off; + + case 0: /* ret */ + t0x = T0_RPOP(); + rp -= (t0x >> 16); + t0x &= 0xFFFF; + if (t0x == 0) { + ip = NULL; + goto t0_exit; + } + ip = &t0_codeblock[t0x]; + break; + case 1: /* literal constant */ + T0_PUSHi(t0_parse7E_signed(&ip)); + break; + case 2: /* read local */ + T0_PUSH(T0_LOCAL(t0_parse7E_unsigned(&ip))); + break; + case 3: /* write local */ + T0_LOCAL(t0_parse7E_unsigned(&ip)) = T0_POP(); + break; + case 4: /* jump */ + t0off = t0_parse7E_signed(&ip); + ip += t0off; + break; + case 5: /* jump if */ + t0off = t0_parse7E_signed(&ip); + if (T0_POP()) { + ip += t0off; + } + break; + case 6: /* jump if not */ + t0off = t0_parse7E_signed(&ip); + if (!T0_POP()) { + ip += t0off; + } + break; + case 7: { + /* * */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(a * b); + + } + break; + case 8: { + /* + */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(a + b); + + } + break; + case 9: { + /* - */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(a - b); + + } + break; + case 10: { + /* -rot */ + T0_NROT(); + } + break; + case 11: { + /* < */ + + int32_t b = T0_POPi(); + int32_t a = T0_POPi(); + T0_PUSH(-(uint32_t)(a < b)); + + } + break; + case 12: { + /* << */ + + int c = (int)T0_POPi(); + uint32_t x = T0_POP(); + T0_PUSH(x << c); + + } + break; + case 13: { + /* <= */ + + int32_t b = T0_POPi(); + int32_t a = T0_POPi(); + T0_PUSH(-(uint32_t)(a <= b)); + + } + break; + case 14: { + /* <> */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(-(uint32_t)(a != b)); + + } + break; + case 15: { + /* = */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(-(uint32_t)(a == b)); + + } + break; + case 16: { + /* > */ + + int32_t b = T0_POPi(); + int32_t a = T0_POPi(); + T0_PUSH(-(uint32_t)(a > b)); + + } + break; + case 17: { + /* >= */ + + int32_t b = T0_POPi(); + int32_t a = T0_POPi(); + T0_PUSH(-(uint32_t)(a >= b)); + + } + break; + case 18: { + /* >> */ + + int c = (int)T0_POPi(); + int32_t x = T0_POPi(); + T0_PUSHi(x >> c); + + } + break; + case 19: { + /* and */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(a & b); + + } + break; + case 20: { + /* begin-cert */ + + if (ENG->chain_len == 0) { + T0_PUSHi(-1); + } else { + ENG->cert_cur = ENG->chain->data; + ENG->cert_len = ENG->chain->data_len; + ENG->chain ++; + ENG->chain_len --; + T0_PUSH(ENG->cert_len); + } + + } + break; + case 21: { + /* begin-ta-name */ + + const br_x500_name *dn; + if (CTX->cur_dn_index >= CTX->num_tas) { + T0_PUSHi(-1); + } else { + if (CTX->ta_names == NULL) { + dn = &CTX->tas[CTX->cur_dn_index].dn; + } else { + dn = &CTX->ta_names[CTX->cur_dn_index]; + } + CTX->cur_dn_index ++; + CTX->cur_dn = dn->data; + CTX->cur_dn_len = dn->len; + T0_PUSH(CTX->cur_dn_len); + } + + } + break; + case 22: { + /* begin-ta-name-list */ + + CTX->cur_dn_index = 0; + + } + break; + case 23: { + /* bzero */ + + size_t len = (size_t)T0_POP(); + void *addr = (unsigned char *)ENG + (size_t)T0_POP(); + memset(addr, 0, len); + + } + break; + case 24: { + /* call-policy-handler */ + + int x; + br_ssl_server_choices choices; + + x = (*CTX->policy_vtable)->choose( + CTX->policy_vtable, CTX, &choices); + ENG->session.cipher_suite = choices.cipher_suite; + CTX->sign_hash_id = choices.algo_id; + ENG->chain = choices.chain; + ENG->chain_len = choices.chain_len; + T0_PUSHi(-(x != 0)); + + } + break; + case 25: { + /* can-output? */ + + T0_PUSHi(-(ENG->hlen_out > 0)); + + } + break; + case 26: { + /* check-resume */ + + if (ENG->session.session_id_len == 32 + && CTX->cache_vtable != NULL && (*CTX->cache_vtable)->load( + CTX->cache_vtable, CTX, &ENG->session)) + { + T0_PUSHi(-1); + } else { + T0_PUSH(0); + } + + } + break; + case 27: { + /* co */ + T0_CO(); + } + break; + case 28: { + /* compute-Finished-inner */ + + int prf_id = T0_POP(); + int from_client = T0_POPi(); + unsigned char tmp[48]; + br_tls_prf_seed_chunk seed; + + br_tls_prf_impl prf = br_ssl_engine_get_PRF(ENG, prf_id); + seed.data = tmp; + if (ENG->session.version >= BR_TLS12) { + seed.len = br_multihash_out(&ENG->mhash, prf_id, tmp); + } else { + br_multihash_out(&ENG->mhash, br_md5_ID, tmp); + br_multihash_out(&ENG->mhash, br_sha1_ID, tmp + 16); + seed.len = 36; + } + prf(ENG->pad, 12, ENG->session.master_secret, + sizeof ENG->session.master_secret, + from_client ? "client finished" : "server finished", + 1, &seed); + + } + break; + case 29: { + /* compute-hash-CV */ + + int i; + + for (i = 1; i <= 6; i ++) { + br_multihash_out(&ENG->mhash, i, + ENG->pad + HASH_PAD_OFF[i - 1]); + } + + } + break; + case 30: { + /* copy-cert-chunk */ + + size_t clen; + + clen = ENG->cert_len; + if (clen > sizeof ENG->pad) { + clen = sizeof ENG->pad; + } + memcpy_P(ENG->pad, ENG->cert_cur, clen); + ENG->cert_cur += clen; + ENG->cert_len -= clen; + T0_PUSH(clen); + + } + break; + case 31: { + /* copy-dn-chunk */ + + size_t clen; + + clen = CTX->cur_dn_len; + if (clen > sizeof ENG->pad) { + clen = sizeof ENG->pad; + } + memcpy(ENG->pad, CTX->cur_dn, clen); + CTX->cur_dn += clen; + CTX->cur_dn_len -= clen; + T0_PUSH(clen); + + } + break; + case 32: { + /* copy-hash-CV */ + + int id = T0_POP(); + size_t off, len; + + if (id == 0) { + off = 0; + len = 36; + } else { + if (br_multihash_getimpl(&ENG->mhash, id) == 0) { + T0_PUSH(0); + T0_RET(); + } + off = HASH_PAD_OFF[id - 1]; + len = HASH_PAD_OFF[id] - off; + } + memcpy(CTX->hash_CV, ENG->pad + off, len); + CTX->hash_CV_len = len; + CTX->hash_CV_id = id; + T0_PUSHi(-1); + + } + break; + case 33: { + /* copy-protocol-name */ + + size_t idx = T0_POP(); + size_t len = strlen(ENG->protocol_names[idx]); + memcpy(ENG->pad, ENG->protocol_names[idx], len); + T0_PUSH(len); + + } + break; + case 34: { + /* data-get8 */ + + size_t addr = T0_POP(); + T0_PUSH(pgm_read_byte(&t0_datablock[addr])); + + } + break; + case 35: { + /* discard-input */ + + ENG->hlen_in = 0; + + } + break; + case 36: { + /* do-ecdh */ + + int prf_id = T0_POPi(); + size_t len = T0_POP(); + do_ecdh(CTX, prf_id, ENG->pad, len); + + } + break; + case 37: { + /* do-ecdhe-part1 */ + + int curve = T0_POPi(); + T0_PUSHi(do_ecdhe_part1(CTX, curve)); + + } + break; + case 38: { + /* do-ecdhe-part2 */ + + int prf_id = T0_POPi(); + size_t len = T0_POP(); + do_ecdhe_part2(CTX, prf_id, ENG->pad, len); + + } + break; + case 39: { + /* do-rsa-decrypt */ + + int prf_id = T0_POPi(); + size_t len = T0_POP(); + do_rsa_decrypt(CTX, prf_id, ENG->pad, len); + + } + break; + case 40: { + /* do-static-ecdh */ + + do_static_ecdh(CTX, T0_POP()); + + } + break; + case 41: { + /* drop */ + (void)T0_POP(); + } + break; + case 42: { + /* dup */ + T0_PUSH(T0_PEEK(0)); + } + break; + case 43: { + /* fail */ + + br_ssl_engine_fail(ENG, (int)T0_POPi()); + T0_CO(); + + } + break; + case 44: { + /* flush-record */ + + br_ssl_engine_flush_record(ENG); + + } + break; + case 45: { + /* get-key-type-usages */ + + const br_x509_class *xc; + const br_x509_pkey *pk; + unsigned usages; + + xc = *(ENG->x509ctx); + pk = xc->get_pkey(ENG->x509ctx, &usages); + if (pk == NULL) { + T0_PUSH(0); + } else { + T0_PUSH(pk->key_type | usages); + } + + } + break; + case 46: { + /* get16 */ + + size_t addr = (size_t)T0_POP(); + T0_PUSH(*(uint16_t *)(void *)((unsigned char *)ENG + addr)); + + } + break; + case 47: { + /* get32 */ + + size_t addr = (size_t)T0_POP(); + T0_PUSH(*(uint32_t *)(void *)((unsigned char *)ENG + addr)); + + } + break; + case 48: { + /* get8 */ + + size_t addr = (size_t)T0_POP(); + T0_PUSH(*((unsigned char *)ENG + addr)); + + } + break; + case 49: { + /* has-input? */ + + T0_PUSHi(-(ENG->hlen_in != 0)); + + } + break; + case 50: { + /* memcmp */ + + size_t len = (size_t)T0_POP(); + void *addr2 = (unsigned char *)ENG + (size_t)T0_POP(); + void *addr1 = (unsigned char *)ENG + (size_t)T0_POP(); + int x = memcmp(addr1, addr2, len); + T0_PUSH((uint32_t)-(x == 0)); + + } + break; + case 51: { + /* memcpy */ + + size_t len = (size_t)T0_POP(); + void *src = (unsigned char *)ENG + (size_t)T0_POP(); + void *dst = (unsigned char *)ENG + (size_t)T0_POP(); + memcpy(dst, src, len); + + } + break; + case 52: { + /* mkrand */ + + size_t len = (size_t)T0_POP(); + void *addr = (unsigned char *)ENG + (size_t)T0_POP(); + br_hmac_drbg_generate(&ENG->rng, addr, len); + + } + break; + case 53: { + /* more-incoming-bytes? */ + + T0_PUSHi(ENG->hlen_in != 0 || !br_ssl_engine_recvrec_finished(ENG)); + + } + break; + case 54: { + /* multihash-init */ + + br_multihash_init(&ENG->mhash); + + } + break; + case 55: { + /* neg */ + + uint32_t a = T0_POP(); + T0_PUSH(-a); + + } + break; + case 56: { + /* not */ + + uint32_t a = T0_POP(); + T0_PUSH(~a); + + } + break; + case 57: { + /* or */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(a | b); + + } + break; + case 58: { + /* over */ + T0_PUSH(T0_PEEK(1)); + } + break; + case 59: { + /* pick */ + T0_PICK(T0_POP()); + } + break; + case 60: { + /* read-chunk-native */ + + size_t clen = ENG->hlen_in; + if (clen > 0) { + uint32_t addr, len; + + len = T0_POP(); + addr = T0_POP(); + if ((size_t)len < clen) { + clen = (size_t)len; + } + memcpy_P((unsigned char *)ENG + addr, ENG->hbuf_in, clen); + if (ENG->record_type_in == BR_SSL_HANDSHAKE) { + br_multihash_update(&ENG->mhash, ENG->hbuf_in, clen); + } + T0_PUSH(addr + (uint32_t)clen); + T0_PUSH(len - (uint32_t)clen); + ENG->hbuf_in += clen; + ENG->hlen_in -= clen; + } + + } + break; + case 61: { + /* read8-native */ + + if (ENG->hlen_in > 0) { + unsigned char x; + + x = pgm_read_byte(ENG->hbuf_in ++); + if (ENG->record_type_in == BR_SSL_HANDSHAKE) { + br_multihash_update(&ENG->mhash, &x, 1); + } + T0_PUSH(x); + ENG->hlen_in --; + } else { + T0_PUSHi(-1); + } + + } + break; + case 62: { + /* save-session */ + + if (CTX->cache_vtable != NULL) { + (*CTX->cache_vtable)->save( + CTX->cache_vtable, CTX, &ENG->session); + } + + } + break; + case 63: { + /* set-max-frag-len */ + + size_t max_frag_len = T0_POP(); + + br_ssl_engine_new_max_frag_len(ENG, max_frag_len); + + /* + * We must adjust our own output limit. Since we call this only + * after receiving a ClientHello and before beginning to send + * the ServerHello, the next output record should be empty at + * that point, so we can use max_frag_len as a limit. + */ + if (ENG->hlen_out > max_frag_len) { + ENG->hlen_out = max_frag_len; + } + + } + break; + case 64: { + /* set16 */ + + size_t addr = (size_t)T0_POP(); + *(uint16_t *)(void *)((unsigned char *)ENG + addr) = (uint16_t)T0_POP(); + + } + break; + case 65: { + /* set32 */ + + size_t addr = (size_t)T0_POP(); + *(uint32_t *)(void *)((unsigned char *)ENG + addr) = (uint32_t)T0_POP(); + + } + break; + case 66: { + /* set8 */ + + size_t addr = (size_t)T0_POP(); + *((unsigned char *)ENG + addr) = (unsigned char)T0_POP(); + + } + break; + case 67: { + /* supported-curves */ + + uint32_t x = ENG->iec == NULL ? 0 : ENG->iec->supported_curves; + T0_PUSH(x); + + } + break; + case 68: { + /* supported-hash-functions */ + + int i; + unsigned x, num; + + x = 0; + num = 0; + for (i = br_sha1_ID; i <= br_sha512_ID; i ++) { + if (br_multihash_getimpl(&ENG->mhash, i)) { + x |= 1U << i; + num ++; + } + } + T0_PUSH(x); + T0_PUSH(num); + + } + break; + case 69: { + /* supports-ecdsa? */ + + T0_PUSHi(-(ENG->iecdsa != 0)); + + } + break; + case 70: { + /* supports-rsa-sign? */ + + T0_PUSHi(-(ENG->irsavrfy != 0)); + + } + break; + case 71: { + /* swap */ + T0_SWAP(); + } + break; + case 72: { + /* switch-aesccm-in */ + + int is_client, prf_id; + unsigned cipher_key_len, tag_len; + + tag_len = T0_POP(); + cipher_key_len = T0_POP(); + prf_id = T0_POP(); + is_client = T0_POP(); + br_ssl_engine_switch_ccm_in(ENG, is_client, prf_id, + ENG->iaes_ctrcbc, cipher_key_len, tag_len); + + } + break; + case 73: { + /* switch-aesccm-out */ + + int is_client, prf_id; + unsigned cipher_key_len, tag_len; + + tag_len = T0_POP(); + cipher_key_len = T0_POP(); + prf_id = T0_POP(); + is_client = T0_POP(); + br_ssl_engine_switch_ccm_out(ENG, is_client, prf_id, + ENG->iaes_ctrcbc, cipher_key_len, tag_len); + + } + break; + case 74: { + /* switch-aesgcm-in */ + + int is_client, prf_id; + unsigned cipher_key_len; + + cipher_key_len = T0_POP(); + prf_id = T0_POP(); + is_client = T0_POP(); + br_ssl_engine_switch_gcm_in(ENG, is_client, prf_id, + ENG->iaes_ctr, cipher_key_len); + + } + break; + case 75: { + /* switch-aesgcm-out */ + + int is_client, prf_id; + unsigned cipher_key_len; + + cipher_key_len = T0_POP(); + prf_id = T0_POP(); + is_client = T0_POP(); + br_ssl_engine_switch_gcm_out(ENG, is_client, prf_id, + ENG->iaes_ctr, cipher_key_len); + + } + break; + case 76: { + /* switch-cbc-in */ + + int is_client, prf_id, mac_id, aes; + unsigned cipher_key_len; + + cipher_key_len = T0_POP(); + aes = T0_POP(); + mac_id = T0_POP(); + prf_id = T0_POP(); + is_client = T0_POP(); + br_ssl_engine_switch_cbc_in(ENG, is_client, prf_id, mac_id, + aes ? ENG->iaes_cbcdec : ENG->ides_cbcdec, cipher_key_len); + + } + break; + case 77: { + /* switch-cbc-out */ + + int is_client, prf_id, mac_id, aes; + unsigned cipher_key_len; + + cipher_key_len = T0_POP(); + aes = T0_POP(); + mac_id = T0_POP(); + prf_id = T0_POP(); + is_client = T0_POP(); + br_ssl_engine_switch_cbc_out(ENG, is_client, prf_id, mac_id, + aes ? ENG->iaes_cbcenc : ENG->ides_cbcenc, cipher_key_len); + + } + break; + case 78: { + /* switch-chapol-in */ + + int is_client, prf_id; + + prf_id = T0_POP(); + is_client = T0_POP(); + br_ssl_engine_switch_chapol_in(ENG, is_client, prf_id); + + } + break; + case 79: { + /* switch-chapol-out */ + + int is_client, prf_id; + + prf_id = T0_POP(); + is_client = T0_POP(); + br_ssl_engine_switch_chapol_out(ENG, is_client, prf_id); + + } + break; + case 80: { + /* ta-names-total-length */ + + size_t u, len; + + len = 0; + if (CTX->ta_names != NULL) { + for (u = 0; u < CTX->num_tas; u ++) { + len += CTX->ta_names[u].len + 2; + } + } else if (CTX->tas != NULL) { + for (u = 0; u < CTX->num_tas; u ++) { + len += CTX->tas[u].dn.len + 2; + } + } + T0_PUSH(len); + + } + break; + case 81: { + /* test-protocol-name */ + + size_t len = T0_POP(); + size_t u; + + for (u = 0; u < ENG->protocol_names_num; u ++) { + const char *name; + + name = ENG->protocol_names[u]; + if (len == strlen(name) && memcmp(ENG->pad, name, len) == 0) { + T0_PUSH(u); + T0_RET(); + } + } + T0_PUSHi(-1); + + } + break; + case 82: { + /* total-chain-length */ + + size_t u; + uint32_t total; + + total = 0; + for (u = 0; u < ENG->chain_len; u ++) { + total += 3 + (uint32_t)ENG->chain[u].data_len; + } + T0_PUSH(total); + + } + break; + case 83: { + /* u< */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(-(uint32_t)(a < b)); + + } + break; + case 84: { + /* u>> */ + + int c = (int)T0_POPi(); + uint32_t x = T0_POP(); + T0_PUSH(x >> c); + + } + break; + case 85: { + /* verify-CV-sig */ + + int err; + + err = verify_CV_sig(CTX, T0_POP()); + T0_PUSHi(err); + + } + break; + case 86: { + /* write-blob-chunk */ + + size_t clen = ENG->hlen_out; + if (clen > 0) { + uint32_t addr, len; + + len = T0_POP(); + addr = T0_POP(); + if ((size_t)len < clen) { + clen = (size_t)len; + } + memcpy(ENG->hbuf_out, (unsigned char *)ENG + addr, clen); + if (ENG->record_type_out == BR_SSL_HANDSHAKE) { + br_multihash_update(&ENG->mhash, ENG->hbuf_out, clen); + } + T0_PUSH(addr + (uint32_t)clen); + T0_PUSH(len - (uint32_t)clen); + ENG->hbuf_out += clen; + ENG->hlen_out -= clen; + } + + } + break; + case 87: { + /* write8-native */ + + unsigned char x; + + x = (unsigned char)T0_POP(); + if (ENG->hlen_out > 0) { + if (ENG->record_type_out == BR_SSL_HANDSHAKE) { + br_multihash_update(&ENG->mhash, &x, 1); + } + *ENG->hbuf_out ++ = x; + ENG->hlen_out --; + T0_PUSHi(-1); + } else { + T0_PUSHi(0); + } + + } + break; + case 88: { + /* x509-append */ + + const br_x509_class *xc; + size_t len; + + xc = *(ENG->x509ctx); + len = T0_POP(); + xc->append(ENG->x509ctx, ENG->pad, len); + + } + break; + case 89: { + /* x509-end-cert */ + + const br_x509_class *xc; + + xc = *(ENG->x509ctx); + xc->end_cert(ENG->x509ctx); + + } + break; + case 90: { + /* x509-end-chain */ + + const br_x509_class *xc; + + xc = *(ENG->x509ctx); + T0_PUSH(xc->end_chain(ENG->x509ctx)); + + } + break; + case 91: { + /* x509-start-cert */ + + const br_x509_class *xc; + + xc = *(ENG->x509ctx); + xc->start_cert(ENG->x509ctx, T0_POP()); + + } + break; + case 92: { + /* x509-start-chain */ + + const br_x509_class *xc; + uint32_t bc; + + bc = T0_POP(); + xc = *(ENG->x509ctx); + xc->start_chain(ENG->x509ctx, bc ? ENG->server_name : NULL); + + } + break; + } + + } else { + T0_ENTER(ip, rp, t0x); + } + } +t0_exit: + ((t0_context *)t0ctx)->dp = dp; + ((t0_context *)t0ctx)->rp = rp; + ((t0_context *)t0ctx)->ip = ip; +} diff --git a/lib/bearssl-esp8266/src/ssl/ssl_io.c b/lib/bearssl-esp8266/src/ssl/ssl_io.c new file mode 100644 index 000000000..a4c54e617 --- /dev/null +++ b/lib/bearssl-esp8266/src/ssl/ssl_io.c @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_ssl.h */ +void +br_sslio_init(br_sslio_context *ctx, + br_ssl_engine_context *engine, + int (*low_read)(void *read_context, + unsigned char *data, size_t len), + void *read_context, + int (*low_write)(void *write_context, + const unsigned char *data, size_t len), + void *write_context) +{ + ctx->engine = engine; + ctx->low_read = low_read; + ctx->read_context = read_context; + ctx->low_write = low_write; + ctx->write_context = write_context; +} + +/* + * Run the engine, until the specified target state is achieved, or + * an error occurs. The target state is SENDAPP, RECVAPP, or the + * combination of both (the combination matches either). When a match is + * achieved, this function returns 0. On error, it returns -1. + */ +static int +run_until(br_sslio_context *ctx, unsigned target) +{ + for (;;) { + unsigned state; + + state = br_ssl_engine_current_state(ctx->engine); + if (state & BR_SSL_CLOSED) { + return -1; + } + + /* + * If there is some record data to send, do it. This takes + * precedence over everything else. + */ + if (state & BR_SSL_SENDREC) { + unsigned char *buf; + size_t len; + int wlen; + + buf = br_ssl_engine_sendrec_buf(ctx->engine, &len); + wlen = ctx->low_write(ctx->write_context, buf, len); + if (wlen < 0) { + /* + * If we received a close_notify and we + * still send something, then we have our + * own response close_notify to send, and + * the peer is allowed by RFC 5246 not to + * wait for it. + */ + if (!ctx->engine->shutdown_recv) { + br_ssl_engine_fail( + ctx->engine, BR_ERR_IO); + } + return -1; + } + if (wlen > 0) { + br_ssl_engine_sendrec_ack(ctx->engine, wlen); + } + continue; + } + + /* + * If we reached our target, then we are finished. + */ + if (state & target) { + return 0; + } + + /* + * If some application data must be read, and we did not + * exit, then this means that we are trying to write data, + * and that's not possible until the application data is + * read. This may happen if using a shared in/out buffer, + * and the underlying protocol is not strictly half-duplex. + * This is unrecoverable here, so we report an error. + */ + if (state & BR_SSL_RECVAPP) { + return -1; + } + + /* + * If we reached that point, then either we are trying + * to read data and there is some, or the engine is stuck + * until a new record is obtained. + */ + if (state & BR_SSL_RECVREC) { + unsigned char *buf; + size_t len; + int rlen; + + buf = br_ssl_engine_recvrec_buf(ctx->engine, &len); + rlen = ctx->low_read(ctx->read_context, buf, len); + if (rlen < 0) { + br_ssl_engine_fail(ctx->engine, BR_ERR_IO); + return -1; + } + if (rlen > 0) { + br_ssl_engine_recvrec_ack(ctx->engine, rlen); + } + continue; + } + + /* + * We can reach that point if the target RECVAPP, and + * the state contains SENDAPP only. This may happen with + * a shared in/out buffer. In that case, we must flush + * the buffered data to "make room" for a new incoming + * record. + */ + br_ssl_engine_flush(ctx->engine, 0); + } +} + +/* see bearssl_ssl.h */ +int +br_sslio_read(br_sslio_context *ctx, void *dst, size_t len) +{ + unsigned char *buf; + size_t alen; + + if (len == 0) { + return 0; + } + if (run_until(ctx, BR_SSL_RECVAPP) < 0) { + return -1; + } + buf = br_ssl_engine_recvapp_buf(ctx->engine, &alen); + if (alen > len) { + alen = len; + } + memcpy(dst, buf, alen); + br_ssl_engine_recvapp_ack(ctx->engine, alen); + return (int)alen; +} + +/* see bearssl_ssl.h */ +int +br_sslio_read_all(br_sslio_context *ctx, void *dst, size_t len) +{ + unsigned char *buf; + + buf = dst; + while (len > 0) { + int rlen; + + rlen = br_sslio_read(ctx, buf, len); + if (rlen < 0) { + return -1; + } + buf += rlen; + len -= (size_t)rlen; + } + return 0; +} + +/* see bearssl_ssl.h */ +int +br_sslio_write(br_sslio_context *ctx, const void *src, size_t len) +{ + unsigned char *buf; + size_t alen; + + if (len == 0) { + return 0; + } + if (run_until(ctx, BR_SSL_SENDAPP) < 0) { + return -1; + } + buf = br_ssl_engine_sendapp_buf(ctx->engine, &alen); + if (alen > len) { + alen = len; + } + memcpy(buf, src, alen); + br_ssl_engine_sendapp_ack(ctx->engine, alen); + return (int)alen; +} + +/* see bearssl_ssl.h */ +int +br_sslio_write_all(br_sslio_context *ctx, const void *src, size_t len) +{ + const unsigned char *buf; + + buf = src; + while (len > 0) { + int wlen; + + wlen = br_sslio_write(ctx, buf, len); + if (wlen < 0) { + return -1; + } + buf += wlen; + len -= (size_t)wlen; + } + return 0; +} + +/* see bearssl_ssl.h */ +int +br_sslio_flush(br_sslio_context *ctx) +{ + /* + * We trigger a flush. We know the data is gone when there is + * no longer any record data to send, and we can either read + * or write application data. The call to run_until() does the + * job because it ensures that any assembled record data is + * first sent down the wire before considering anything else. + */ + br_ssl_engine_flush(ctx->engine, 0); + return run_until(ctx, BR_SSL_SENDAPP | BR_SSL_RECVAPP); +} + +/* see bearssl_ssl.h */ +int +br_sslio_close(br_sslio_context *ctx) +{ + br_ssl_engine_close(ctx->engine); + while (br_ssl_engine_current_state(ctx->engine) != BR_SSL_CLOSED) { + /* + * Discard any incoming application data. + */ + size_t len; + + run_until(ctx, BR_SSL_RECVAPP); + if (br_ssl_engine_recvapp_buf(ctx->engine, &len) != NULL) { + br_ssl_engine_recvapp_ack(ctx->engine, len); + } + } + return br_ssl_engine_last_error(ctx->engine) == BR_ERR_OK; +} diff --git a/lib/bearssl-esp8266/src/ssl/ssl_keyexport.c b/lib/bearssl-esp8266/src/ssl/ssl_keyexport.c new file mode 100644 index 000000000..66fbe9ebb --- /dev/null +++ b/lib/bearssl-esp8266/src/ssl/ssl_keyexport.c @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * Supported cipher suites that use SHA-384 for the PRF when selected + * for TLS 1.2. All other cipher suites are deemed to use SHA-256. + */ +static const uint16_t suites_sha384[] PROGMEM = { + BR_TLS_RSA_WITH_AES_256_GCM_SHA384, + BR_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, + BR_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384, + BR_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, + BR_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384, + BR_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + BR_TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384, + BR_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + BR_TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 +}; + +/* see bearssl_ssl.h */ +int +br_ssl_key_export(br_ssl_engine_context *cc, + void *dst, size_t len, const char *label, + const void *context, size_t context_len) +{ + br_tls_prf_seed_chunk chunks[4]; + br_tls_prf_impl iprf; + size_t num_chunks, u; + unsigned char tmp[2]; + int prf_id; + + if (cc->application_data != 1) { + return 0; + } + chunks[0].data = cc->client_random; + chunks[0].len = sizeof cc->client_random; + chunks[1].data = cc->server_random; + chunks[1].len = sizeof cc->server_random; + if (context != NULL) { + br_enc16be(tmp, (unsigned)context_len); + chunks[2].data = tmp; + chunks[2].len = 2; + chunks[3].data = context; + chunks[3].len = context_len; + num_chunks = 4; + } else { + num_chunks = 2; + } + prf_id = BR_SSLPRF_SHA256; + for (u = 0; u < (sizeof suites_sha384) / sizeof(uint16_t); u ++) { + if (pgm_read_word(&suites_sha384[u]) == cc->session.cipher_suite) { + prf_id = BR_SSLPRF_SHA384; + } + } + iprf = br_ssl_engine_get_PRF(cc, prf_id); + iprf(dst, len, + cc->session.master_secret, sizeof cc->session.master_secret, + label, num_chunks, chunks); + return 1; +} diff --git a/lib/bearssl-esp8266/src/ssl/ssl_lru.c b/lib/bearssl-esp8266/src/ssl/ssl_lru.c new file mode 100644 index 000000000..dd64f42ed --- /dev/null +++ b/lib/bearssl-esp8266/src/ssl/ssl_lru.c @@ -0,0 +1,537 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * Each entry consists in a fixed number of bytes. Entries are concatenated + * in the store block. "Addresses" are really offsets in the block, + * expressed over 32 bits (so the cache may have size at most 4 GB, which + * "ought to be enough for everyone"). The "null address" is 0xFFFFFFFF. + * Note that since the storage block alignment is in no way guaranteed, we + * perform only accesses that can handle unaligned data. + * + * Two concurrent data structures are maintained: + * + * -- Entries are organised in a doubly-linked list; saved entries are added + * at the head, and loaded entries are moved to the head. Eviction uses + * the list tail (this is the LRU algorithm). + * + * -- Entries are indexed with a binary tree: all left descendants of a + * node have a lower session ID (in lexicographic order), while all + * right descendants have a higher session ID. The tree is heuristically + * balanced. + * + * Entry format: + * + * session ID 32 bytes + * master secret 48 bytes + * protocol version 2 bytes (big endian) + * cipher suite 2 bytes (big endian) + * list prev 4 bytes (big endian) + * list next 4 bytes (big endian) + * tree left child 4 bytes (big endian) + * tree right child 4 bytes (big endian) + * + * If an entry has a protocol version set to 0, then it is "disabled": + * it was a session pushed to the cache at some point, but it has + * been explicitly removed. + * + * We need to keep the tree balanced because an attacker could make + * handshakes, selecting some specific sessions (by reusing them) to + * try to make us make an imbalanced tree that makes lookups expensive + * (a denial-of-service attack that would persist as long as the cache + * remains, i.e. even after the attacker made all his connections). + * To do that, we replace the session ID (or the start of the session ID) + * with a HMAC value computed over the replaced part; the hash function + * implementation and the key are obtained from the server context upon + * first save() call. + * + * Theoretically, an attacker could use the exact timing of the lookup + * to infer the current tree topology, and try to revive entries to make + * it as unbalanced as possible. However, since the session ID are + * chosen randomly by the server, and the attacker cannot see the + * indexing values and must thus rely on blind selection, it should be + * exponentially difficult for the attacker to maintain a large + * imbalance. + */ +#define SESSION_ID_LEN 32 +#define MASTER_SECRET_LEN 48 + +#define SESSION_ID_OFF 0 +#define MASTER_SECRET_OFF 32 +#define VERSION_OFF 80 +#define CIPHER_SUITE_OFF 82 +#define LIST_PREV_OFF 84 +#define LIST_NEXT_OFF 88 +#define TREE_LEFT_OFF 92 +#define TREE_RIGHT_OFF 96 + +#define LRU_ENTRY_LEN 100 + +#define ADDR_NULL ((uint32_t)-1) + +#define GETSET(name, off) \ +static inline uint32_t get_ ## name(br_ssl_session_cache_lru *cc, uint32_t x) \ +{ \ + return br_dec32be(cc->store + x + (off)); \ +} \ +static inline void set_ ## name(br_ssl_session_cache_lru *cc, \ + uint32_t x, uint32_t val) \ +{ \ + br_enc32be(cc->store + x + (off), val); \ +} + +GETSET(prev, LIST_PREV_OFF) +GETSET(next, LIST_NEXT_OFF) +GETSET(left, TREE_LEFT_OFF) +GETSET(right, TREE_RIGHT_OFF) + +/* + * Transform the session ID by replacing the first N bytes with a HMAC + * value computed over these bytes, using the random key K (the HMAC + * value is truncated if needed). HMAC will use the same hash function + * as the DRBG in the SSL server context, so with SHA-256, SHA-384, + * or SHA-1, depending on what is available. + * + * The risk of collision is considered too small to be a concern; and + * the impact of a collision is low (the handshake won't succeed). This + * risk is much lower than any transmission error, which would lead to + * the same consequences. + * + * Source and destination arrays msut be disjoint. + */ +static void +mask_id(br_ssl_session_cache_lru *cc, + const unsigned char *src, unsigned char *dst) +{ + br_hmac_key_context hkc; + br_hmac_context hc; + + memcpy(dst, src, SESSION_ID_LEN); + br_hmac_key_init(&hkc, cc->hash, cc->index_key, sizeof cc->index_key); + br_hmac_init(&hc, &hkc, SESSION_ID_LEN); + br_hmac_update(&hc, src, SESSION_ID_LEN); + br_hmac_out(&hc, dst); +} + +/* + * Find a node by ID. Returned value is the node address, or ADDR_NULL if + * the node is not found. + * + * If addr_link is not NULL, then '*addr_link' is set to the address of the + * last followed link. If the found node is the root, or if the tree is + * empty, then '*addr_link' is set to ADDR_NULL. + */ +static uint32_t +find_node(br_ssl_session_cache_lru *cc, const unsigned char *id, + uint32_t *addr_link) +{ + uint32_t x, y; + + x = cc->root; + y = ADDR_NULL; + while (x != ADDR_NULL) { + int r; + + r = memcmp(id, cc->store + x + SESSION_ID_OFF, SESSION_ID_LEN); + if (r < 0) { + y = x + TREE_LEFT_OFF; + x = get_left(cc, x); + } else if (r == 0) { + if (addr_link != NULL) { + *addr_link = y; + } + return x; + } else { + y = x + TREE_RIGHT_OFF; + x = get_right(cc, x); + } + } + if (addr_link != NULL) { + *addr_link = y; + } + return ADDR_NULL; +} + +/* + * For node x, find its replacement upon removal. + * + * -- If node x has no child, then this returns ADDR_NULL. + * -- Otherwise, if node x has a left child, then the replacement is the + * rightmost left-descendent. + * -- Otherwise, the replacement is the leftmost right-descendent. + * + * If a node is returned, then '*al' is set to the address of the field + * that points to that node. Otherwise (node x has no child), '*al' is + * set to ADDR_NULL. + * + * Note that the replacement node, when found, is always a descendent + * of node 'x', so it cannot be the tree root. Thus, '*al' can be set + * to ADDR_NULL only when no node is found and ADDR_NULL is returned. + */ +static uint32_t +find_replacement_node(br_ssl_session_cache_lru *cc, uint32_t x, uint32_t *al) +{ + uint32_t y1, y2; + + y1 = get_left(cc, x); + if (y1 != ADDR_NULL) { + y2 = x + TREE_LEFT_OFF; + for (;;) { + uint32_t z; + + z = get_right(cc, y1); + if (z == ADDR_NULL) { + *al = y2; + return y1; + } + y2 = y1 + TREE_RIGHT_OFF; + y1 = z; + } + } + y1 = get_right(cc, x); + if (y1 != ADDR_NULL) { + y2 = x + TREE_RIGHT_OFF; + for (;;) { + uint32_t z; + + z = get_left(cc, y1); + if (z == ADDR_NULL) { + *al = y2; + return y1; + } + y2 = y1 + TREE_LEFT_OFF; + y1 = z; + } + } + *al = ADDR_NULL; + return ADDR_NULL; +} + +/* + * Set the link at address 'alx' to point to node 'x'. If 'alx' is + * ADDR_NULL, then this sets the tree root to 'x'. + */ +static inline void +set_link(br_ssl_session_cache_lru *cc, uint32_t alx, uint32_t x) +{ + if (alx == ADDR_NULL) { + cc->root = x; + } else { + br_enc32be(cc->store + alx, x); + } +} + +/* + * Remove node 'x' from the tree. This function shall not be called if + * node 'x' is not part of the tree. + */ +static void +remove_node(br_ssl_session_cache_lru *cc, uint32_t x) +{ + uint32_t alx, y, aly; + + /* + * Removal algorithm: + * ------------------ + * + * - If we remove the root, then the tree becomes empty. + * + * - If the removed node has no child, then we can simply remove + * it, with nothing else to do. + * + * - Otherwise, the removed node must be replaced by either its + * rightmost left-descendent, or its leftmost right-descendent. + * The replacement node itself must be removed from its current + * place. By definition, that replacement node has either no + * child, or at most a single child that will replace it in the + * tree. + */ + + /* + * Find node back and its ancestor link. If the node was the + * root, then alx is set to ADDR_NULL. + */ + find_node(cc, cc->store + x + SESSION_ID_OFF, &alx); + + /* + * Find replacement node 'y', and 'aly' is set to the address of + * the link to that replacement node. If the removed node has no + * child, then both 'y' and 'aly' are set to ADDR_NULL. + */ + y = find_replacement_node(cc, x, &aly); + + if (y != ADDR_NULL) { + uint32_t z; + + /* + * The unlinked replacement node may have one child (but + * not two) that takes its place. + */ + z = get_left(cc, y); + if (z == ADDR_NULL) { + z = get_right(cc, y); + } + set_link(cc, aly, z); + + /* + * Link the replacement node in its new place, overwriting + * the current link to the node 'x' (which removes 'x'). + */ + set_link(cc, alx, y); + + /* + * The replacement node adopts the left and right children + * of the removed node. Note that this also works even if + * the replacement node was a direct descendent of the + * removed node, since we unlinked it previously. + */ + set_left(cc, y, get_left(cc, x)); + set_right(cc, y, get_right(cc, x)); + } else { + /* + * No replacement, we simply unlink the node 'x'. + */ + set_link(cc, alx, ADDR_NULL); + } +} + +static void +lru_save(const br_ssl_session_cache_class **ctx, + br_ssl_server_context *server_ctx, + const br_ssl_session_parameters *params) +{ + br_ssl_session_cache_lru *cc; + unsigned char id[SESSION_ID_LEN]; + uint32_t x, alx; + + cc = (br_ssl_session_cache_lru *)ctx; + + /* + * If the buffer is too small, we don't record anything. This + * test avoids problems in subsequent code. + */ + if (cc->store_len < LRU_ENTRY_LEN) { + return; + } + + /* + * Upon the first save in a session cache instance, we obtain + * a random key for our indexing. + */ + if (!cc->init_done) { + br_hmac_drbg_generate(&server_ctx->eng.rng, + cc->index_key, sizeof cc->index_key); + cc->hash = br_hmac_drbg_get_hash(&server_ctx->eng.rng); + cc->init_done = 1; + } + mask_id(cc, params->session_id, id); + + /* + * Look for the node in the tree. If the same ID is already used, + * then reject it. This is a collision event, which should be + * exceedingly rare. + * Note: we do NOT record the emplacement here, because the + * removal of an entry may change the tree topology. + */ + if (find_node(cc, id, NULL) != ADDR_NULL) { + return; + } + + /* + * Find some room for the new parameters. If the cache is not + * full yet, add it to the end of the area and bump the pointer up. + * Otherwise, evict the list tail entry. Note that we already + * filtered out the case of a ridiculously small buffer that + * cannot hold any entry at all; thus, if there is no room for an + * extra entry, then the cache cannot be empty. + */ + if (cc->store_ptr > (cc->store_len - LRU_ENTRY_LEN)) { + /* + * Evict tail. If the buffer has room for a single entry, + * then this may also be the head. + */ + x = cc->tail; + cc->tail = get_prev(cc, x); + if (cc->tail == ADDR_NULL) { + cc->head = ADDR_NULL; + } else { + set_next(cc, cc->tail, ADDR_NULL); + } + + /* + * Remove the node from the tree. + */ + remove_node(cc, x); + } else { + /* + * Allocate room for new node. + */ + x = cc->store_ptr; + cc->store_ptr += LRU_ENTRY_LEN; + } + + /* + * Find the emplacement for the new node, and link it. + */ + find_node(cc, id, &alx); + set_link(cc, alx, x); + set_left(cc, x, ADDR_NULL); + set_right(cc, x, ADDR_NULL); + + /* + * New entry becomes new list head. It may also become the list + * tail if the cache was empty at that point. + */ + if (cc->head == ADDR_NULL) { + cc->tail = x; + } else { + set_prev(cc, cc->head, x); + } + set_prev(cc, x, ADDR_NULL); + set_next(cc, x, cc->head); + cc->head = x; + + /* + * Fill data in the entry. + */ + memcpy(cc->store + x + SESSION_ID_OFF, id, SESSION_ID_LEN); + memcpy(cc->store + x + MASTER_SECRET_OFF, + params->master_secret, MASTER_SECRET_LEN); + br_enc16be(cc->store + x + VERSION_OFF, params->version); + br_enc16be(cc->store + x + CIPHER_SUITE_OFF, params->cipher_suite); +} + +static int +lru_load(const br_ssl_session_cache_class **ctx, + br_ssl_server_context *server_ctx, + br_ssl_session_parameters *params) +{ + br_ssl_session_cache_lru *cc; + unsigned char id[SESSION_ID_LEN]; + uint32_t x; + + (void)server_ctx; + cc = (br_ssl_session_cache_lru *)ctx; + if (!cc->init_done) { + return 0; + } + mask_id(cc, params->session_id, id); + x = find_node(cc, id, NULL); + if (x != ADDR_NULL) { + unsigned version; + + version = br_dec16be(cc->store + x + VERSION_OFF); + if (version == 0) { + /* + * Entry is disabled, we pretend we did not find it. + * Notably, we don't move it to the front of the + * LRU list. + */ + return 0; + } + params->version = version; + params->cipher_suite = br_dec16be( + cc->store + x + CIPHER_SUITE_OFF); + memcpy(params->master_secret, + cc->store + x + MASTER_SECRET_OFF, + MASTER_SECRET_LEN); + if (x != cc->head) { + /* + * Found node is not at list head, so move + * it to the head. + */ + uint32_t p, n; + + p = get_prev(cc, x); + n = get_next(cc, x); + set_next(cc, p, n); + if (n == ADDR_NULL) { + cc->tail = p; + } else { + set_prev(cc, n, p); + } + set_prev(cc, cc->head, x); + set_next(cc, x, cc->head); + set_prev(cc, x, ADDR_NULL); + cc->head = x; + } + return 1; + } + return 0; +} + +static const br_ssl_session_cache_class lru_class = { + sizeof(br_ssl_session_cache_lru), + &lru_save, + &lru_load +}; + +/* see inner.h */ +void +br_ssl_session_cache_lru_init(br_ssl_session_cache_lru *cc, + unsigned char *store, size_t store_len) +{ + cc->vtable = &lru_class; + cc->store = store; + cc->store_len = store_len; + cc->store_ptr = 0; + cc->init_done = 0; + cc->head = ADDR_NULL; + cc->tail = ADDR_NULL; + cc->root = ADDR_NULL; +} + +/* see bearssl_ssl.h */ +void br_ssl_session_cache_lru_forget( + br_ssl_session_cache_lru *cc, const unsigned char *id) +{ + unsigned char mid[SESSION_ID_LEN]; + uint32_t addr; + + /* + * If the cache is not initialised yet, then it is empty, and + * there is nothing to forget. + */ + if (!cc->init_done) { + return; + } + + /* + * Look for the node in the tree. If found, the entry is marked + * as "disabled"; it will be reused in due course, as it ages + * through the list. + * + * We do not go through the complex moves of actually releasing + * the entry right away because explicitly forgetting sessions + * should be a rare event, meant mostly for testing purposes, + * so this is not worth the extra code size. + */ + mask_id(cc, id, mid); + addr = find_node(cc, mid, NULL); + if (addr != ADDR_NULL) { + br_enc16be(cc->store + addr + VERSION_OFF, 0); + } +} diff --git a/lib/bearssl-esp8266/src/ssl/ssl_rec_cbc.c b/lib/bearssl-esp8266/src/ssl/ssl_rec_cbc.c new file mode 100644 index 000000000..5219795c8 --- /dev/null +++ b/lib/bearssl-esp8266/src/ssl/ssl_rec_cbc.c @@ -0,0 +1,440 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +static void +in_cbc_init(br_sslrec_in_cbc_context *cc, + const br_block_cbcdec_class *bc_impl, + const void *bc_key, size_t bc_key_len, + const br_hash_class *dig_impl, + const void *mac_key, size_t mac_key_len, size_t mac_out_len, + const void *iv) +{ + cc->vtable = &br_sslrec_in_cbc_vtable; + cc->seq = 0; + bc_impl->init(&cc->bc.vtable, bc_key, bc_key_len); + br_hmac_key_init(&cc->mac, dig_impl, mac_key, mac_key_len); + cc->mac_len = mac_out_len; + if (iv == NULL) { + memset(cc->iv, 0, sizeof cc->iv); + cc->explicit_IV = 1; + } else { + memcpy(cc->iv, iv, bc_impl->block_size); + cc->explicit_IV = 0; + } +} + +static int +cbc_check_length(const br_sslrec_in_cbc_context *cc, size_t rlen) +{ + /* + * Plaintext size: at most 16384 bytes + * Padding: at most 256 bytes + * MAC: mac_len extra bytes + * TLS 1.1+: each record has an explicit IV + * + * Minimum length includes at least one byte of padding, and the + * MAC. + * + * Total length must be a multiple of the block size. + */ + size_t blen; + size_t min_len, max_len; + + blen = cc->bc.vtable->block_size; + min_len = (blen + cc->mac_len) & ~(blen - 1); + max_len = (16384 + 256 + cc->mac_len) & ~(blen - 1); + if (cc->explicit_IV) { + min_len += blen; + max_len += blen; + } + return min_len <= rlen && rlen <= max_len; +} + +/* + * Rotate array buf[] of length 'len' to the left (towards low indices) + * by 'num' bytes if ctl is 1; otherwise, leave it unchanged. This is + * constant-time. 'num' MUST be lower than 'len'. 'len' MUST be lower + * than or equal to 64. + */ +static void +cond_rotate(uint32_t ctl, unsigned char *buf, size_t len, size_t num) +{ + unsigned char tmp[64]; + size_t u, v; + + for (u = 0, v = num; u < len; u ++) { + tmp[u] = MUX(ctl, buf[v], buf[u]); + if (++ v == len) { + v = 0; + } + } + memcpy(buf, tmp, len); +} + +static unsigned char * +cbc_decrypt(br_sslrec_in_cbc_context *cc, + int record_type, unsigned version, void *data, size_t *data_len) +{ + /* + * We represent all lengths on 32-bit integers, because: + * -- SSL record lengths always fit in 32 bits; + * -- our constant-time primitives operate on 32-bit integers. + */ + unsigned char *buf; + uint32_t u, v, len, blen, min_len, max_len; + uint32_t good, pad_len, rot_count, len_withmac, len_nomac; + unsigned char tmp1[64], tmp2[64]; + int i; + br_hmac_context hc; + + buf = data; + len = *data_len; + blen = cc->bc.vtable->block_size; + + /* + * Decrypt data, and skip the explicit IV (if applicable). Note + * that the total length is supposed to have been verified by + * the caller. If there is an explicit IV, then we actually + * "decrypt" it using the implicit IV (from previous record), + * which is useless but harmless. + */ + cc->bc.vtable->run(&cc->bc.vtable, cc->iv, data, len); + if (cc->explicit_IV) { + buf += blen; + len -= blen; + } + + /* + * Compute minimum and maximum length of plaintext + MAC. These + * lengths can be inferred from the outside: they are not secret. + */ + min_len = (cc->mac_len + 256 < len) ? len - 256 : cc->mac_len; + max_len = len - 1; + + /* + * Use the last decrypted byte to compute the actual payload + * length. Take care not to underflow (we use unsigned types). + */ + pad_len = buf[max_len]; + good = LE(pad_len, (uint32_t)(max_len - min_len)); + len = MUX(good, (uint32_t)(max_len - pad_len), min_len); + + /* + * Check padding contents: all padding bytes must be equal to + * the value of pad_len. + */ + for (u = min_len; u < max_len; u ++) { + good &= LT(u, len) | EQ(buf[u], pad_len); + } + + /* + * Extract the MAC value. This is done in one pass, but results + * in a "rotated" MAC value depending on where it actually + * occurs. The 'rot_count' value is set to the offset of the + * first MAC byte within tmp1[]. + * + * min_len and max_len are also adjusted to the minimum and + * maximum lengths of the plaintext alone (without the MAC). + */ + len_withmac = (uint32_t)len; + len_nomac = len_withmac - cc->mac_len; + min_len -= cc->mac_len; + rot_count = 0; + memset(tmp1, 0, cc->mac_len); + v = 0; + for (u = min_len; u < max_len; u ++) { + tmp1[v] |= MUX(GE(u, len_nomac) & LT(u, len_withmac), + buf[u], 0x00); + rot_count = MUX(EQ(u, len_nomac), v, rot_count); + if (++ v == cc->mac_len) { + v = 0; + } + } + max_len -= cc->mac_len; + + /* + * Rotate back the MAC value. The loop below does the constant-time + * rotation in time n*log n for a MAC output of length n. We assume + * that the MAC output length is no more than 64 bytes, so the + * rotation count fits on 6 bits. + */ + for (i = 5; i >= 0; i --) { + uint32_t rc; + + rc = (uint32_t)1 << i; + cond_rotate(rot_count >> i, tmp1, cc->mac_len, rc); + rot_count &= ~rc; + } + + /* + * Recompute the HMAC value. The input is the concatenation of + * the sequence number (8 bytes), the record header (5 bytes), + * and the payload. + * + * At that point, min_len is the minimum plaintext length, but + * max_len still includes the MAC length. + */ + br_enc64be(tmp2, cc->seq ++); + tmp2[8] = (unsigned char)record_type; + br_enc16be(tmp2 + 9, version); + br_enc16be(tmp2 + 11, len_nomac); + br_hmac_init(&hc, &cc->mac, cc->mac_len); + br_hmac_update(&hc, tmp2, 13); + br_hmac_outCT(&hc, buf, len_nomac, min_len, max_len, tmp2); + + /* + * Compare the extracted and recomputed MAC values. + */ + for (u = 0; u < cc->mac_len; u ++) { + good &= EQ0(tmp1[u] ^ tmp2[u]); + } + + /* + * Check that the plaintext length is valid. The previous + * check was on the encrypted length, but the padding may have + * turned shorter than expected. + * + * Once this final test is done, the critical "constant-time" + * section ends and we can make conditional jumps again. + */ + good &= LE(len_nomac, 16384); + + if (!good) { + return 0; + } + *data_len = len_nomac; + return buf; +} + +/* see bearssl_ssl.h */ +const br_sslrec_in_cbc_class br_sslrec_in_cbc_vtable PROGMEM = { + { + sizeof(br_sslrec_in_cbc_context), + (int (*)(const br_sslrec_in_class *const *, size_t)) + &cbc_check_length, + (unsigned char *(*)(const br_sslrec_in_class **, + int, unsigned, void *, size_t *)) + &cbc_decrypt + }, + (void (*)(const br_sslrec_in_cbc_class **, + const br_block_cbcdec_class *, const void *, size_t, + const br_hash_class *, const void *, size_t, size_t, + const void *)) + &in_cbc_init +}; + +/* + * For CBC output: + * + * -- With TLS 1.1+, there is an explicit IV. Generation method uses + * HMAC, computed over the current sequence number, and the current MAC + * key. The resulting value is truncated to the size of a block, and + * added at the head of the plaintext; it will get encrypted along with + * the data. This custom generation mechanism is "safe" under the + * assumption that HMAC behaves like a random oracle; since the MAC for + * a record is computed over the concatenation of the sequence number, + * the record header and the plaintext, the HMAC-for-IV will not collide + * with the normal HMAC. + * + * -- With TLS 1.0, for application data, we want to enforce a 1/n-1 + * split, as a countermeasure against chosen-plaintext attacks. We thus + * need to leave some room in the buffer for that extra record. + */ + +static void +out_cbc_init(br_sslrec_out_cbc_context *cc, + const br_block_cbcenc_class *bc_impl, + const void *bc_key, size_t bc_key_len, + const br_hash_class *dig_impl, + const void *mac_key, size_t mac_key_len, size_t mac_out_len, + const void *iv) +{ + cc->vtable = &br_sslrec_out_cbc_vtable; + cc->seq = 0; + bc_impl->init(&cc->bc.vtable, bc_key, bc_key_len); + br_hmac_key_init(&cc->mac, dig_impl, mac_key, mac_key_len); + cc->mac_len = mac_out_len; + if (iv == NULL) { + memset(cc->iv, 0, sizeof cc->iv); + cc->explicit_IV = 1; + } else { + memcpy(cc->iv, iv, bc_impl->block_size); + cc->explicit_IV = 0; + } +} + +static void +cbc_max_plaintext(const br_sslrec_out_cbc_context *cc, + size_t *start, size_t *end) +{ + size_t blen, len; + + blen = cc->bc.vtable->block_size; + if (cc->explicit_IV) { + *start += blen; + } else { + *start += 4 + ((cc->mac_len + blen + 1) & ~(blen - 1)); + } + len = (*end - *start) & ~(blen - 1); + len -= 1 + cc->mac_len; + if (len > 16384) { + len = 16384; + } + *end = *start + len; +} + +static unsigned char * +cbc_encrypt(br_sslrec_out_cbc_context *cc, + int record_type, unsigned version, void *data, size_t *data_len) +{ + unsigned char *buf, *rbuf; + size_t len, blen, plen; + unsigned char tmp[13]; + br_hmac_context hc; + + buf = data; + len = *data_len; + blen = cc->bc.vtable->block_size; + + /* + * If using TLS 1.0, with more than one byte of plaintext, and + * the record is application data, then we need to compute + * a "split". We do not perform the split on other record types + * because it turned out that some existing, deployed + * implementations of SSL/TLS do not tolerate the splitting of + * some message types (in particular the Finished message). + * + * If using TLS 1.1+, then there is an explicit IV. We produce + * that IV by adding an extra initial plaintext block, whose + * value is computed with HMAC over the record sequence number. + */ + if (cc->explicit_IV) { + /* + * We use here the fact that all the HMAC variants we + * support can produce at least 16 bytes, while all the + * block ciphers we support have blocks of no more than + * 16 bytes. Thus, we can always truncate the HMAC output + * down to the block size. + */ + br_enc64be(tmp, cc->seq); + br_hmac_init(&hc, &cc->mac, blen); + br_hmac_update(&hc, tmp, 8); + br_hmac_out(&hc, buf - blen); + rbuf = buf - blen - 5; + } else { + if (len > 1 && record_type == BR_SSL_APPLICATION_DATA) { + /* + * To do the split, we use a recursive invocation; + * since we only give one byte to the inner call, + * the recursion stops there. + * + * We need to compute the exact size of the extra + * record, so that the two resulting records end up + * being sequential in RAM. + * + * We use here the fact that cbc_max_plaintext() + * adjusted the start offset to leave room for the + * initial fragment. + */ + size_t xlen; + + rbuf = buf - 4 + - ((cc->mac_len + blen + 1) & ~(blen - 1)); + rbuf[0] = buf[0]; + xlen = 1; + rbuf = cbc_encrypt(cc, record_type, + version, rbuf, &xlen); + buf ++; + len --; + } else { + rbuf = buf - 5; + } + } + + /* + * Compute MAC. + */ + br_enc64be(tmp, cc->seq ++); + tmp[8] = record_type; + br_enc16be(tmp + 9, version); + br_enc16be(tmp + 11, len); + br_hmac_init(&hc, &cc->mac, cc->mac_len); + br_hmac_update(&hc, tmp, 13); + br_hmac_update(&hc, buf, len); + br_hmac_out(&hc, buf + len); + len += cc->mac_len; + + /* + * Add padding. + */ + plen = blen - (len & (blen - 1)); + memset(buf + len, (unsigned)plen - 1, plen); + len += plen; + + /* + * If an explicit IV is used, the corresponding extra block was + * already put in place earlier; we just have to account for it + * here. + */ + if (cc->explicit_IV) { + buf -= blen; + len += blen; + } + + /* + * Encrypt the whole thing. If there is an explicit IV, we also + * encrypt it, which is fine (encryption of a uniformly random + * block is still a uniformly random block). + */ + cc->bc.vtable->run(&cc->bc.vtable, cc->iv, buf, len); + + /* + * Add the header and return. + */ + buf[-5] = record_type; + br_enc16be(buf - 4, version); + br_enc16be(buf - 2, len); + *data_len = (size_t)((buf + len) - rbuf); + return rbuf; +} + +/* see bearssl_ssl.h */ +const br_sslrec_out_cbc_class br_sslrec_out_cbc_vtable PROGMEM = { + { + sizeof(br_sslrec_out_cbc_context), + (void (*)(const br_sslrec_out_class *const *, + size_t *, size_t *)) + &cbc_max_plaintext, + (unsigned char *(*)(const br_sslrec_out_class **, + int, unsigned, void *, size_t *)) + &cbc_encrypt + }, + (void (*)(const br_sslrec_out_cbc_class **, + const br_block_cbcenc_class *, const void *, size_t, + const br_hash_class *, const void *, size_t, size_t, + const void *)) + &out_cbc_init +}; diff --git a/lib/bearssl-esp8266/src/ssl/ssl_rec_ccm.c b/lib/bearssl-esp8266/src/ssl/ssl_rec_ccm.c new file mode 100644 index 000000000..5c03ab7f8 --- /dev/null +++ b/lib/bearssl-esp8266/src/ssl/ssl_rec_ccm.c @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * CCM initialisation. This does everything except setting the vtable, + * which depends on whether this is a context for encrypting or for + * decrypting. + */ +static void +gen_ccm_init(br_sslrec_ccm_context *cc, + const br_block_ctrcbc_class *bc_impl, + const void *key, size_t key_len, + const void *iv, size_t tag_len) +{ + cc->seq = 0; + bc_impl->init(&cc->bc.vtable, key, key_len); + memcpy(cc->iv, iv, sizeof cc->iv); + cc->tag_len = tag_len; +} + +static void +in_ccm_init(br_sslrec_ccm_context *cc, + const br_block_ctrcbc_class *bc_impl, + const void *key, size_t key_len, + const void *iv, size_t tag_len) +{ + cc->vtable.in = &br_sslrec_in_ccm_vtable; + gen_ccm_init(cc, bc_impl, key, key_len, iv, tag_len); +} + +static int +ccm_check_length(const br_sslrec_ccm_context *cc, size_t rlen) +{ + /* + * CCM overhead is 8 bytes for nonce_explicit, and the tag + * (normally 8 or 16 bytes, depending on cipher suite). + */ + size_t over; + + over = 8 + cc->tag_len; + return rlen >= over && rlen <= (16384 + over); +} + +static unsigned char * +ccm_decrypt(br_sslrec_ccm_context *cc, + int record_type, unsigned version, void *data, size_t *data_len) +{ + br_ccm_context zc; + unsigned char *buf; + unsigned char nonce[12], header[13]; + size_t len; + + buf = (unsigned char *)data + 8; + len = *data_len - (8 + cc->tag_len); + + /* + * Make nonce (implicit + explicit parts). + */ + memcpy(nonce, cc->iv, sizeof cc->iv); + memcpy(nonce + 4, data, 8); + + /* + * Assemble synthetic header for the AAD. + */ + br_enc64be(header, cc->seq ++); + header[8] = (unsigned char)record_type; + br_enc16be(header + 9, version); + br_enc16be(header + 11, len); + + /* + * Perform CCM decryption. + */ + br_ccm_init(&zc, &cc->bc.vtable); + br_ccm_reset(&zc, nonce, sizeof nonce, sizeof header, len, cc->tag_len); + br_ccm_aad_inject(&zc, header, sizeof header); + br_ccm_flip(&zc); + br_ccm_run(&zc, 0, buf, len); + if (!br_ccm_check_tag(&zc, buf + len)) { + return NULL; + } + *data_len = len; + return buf; +} + +/* see bearssl_ssl.h */ +const br_sslrec_in_ccm_class br_sslrec_in_ccm_vtable PROGMEM = { + { + sizeof(br_sslrec_ccm_context), + (int (*)(const br_sslrec_in_class *const *, size_t)) + &ccm_check_length, + (unsigned char *(*)(const br_sslrec_in_class **, + int, unsigned, void *, size_t *)) + &ccm_decrypt + }, + (void (*)(const br_sslrec_in_ccm_class **, + const br_block_ctrcbc_class *, const void *, size_t, + const void *, size_t)) + &in_ccm_init +}; + +static void +out_ccm_init(br_sslrec_ccm_context *cc, + const br_block_ctrcbc_class *bc_impl, + const void *key, size_t key_len, + const void *iv, size_t tag_len) +{ + cc->vtable.out = &br_sslrec_out_ccm_vtable; + gen_ccm_init(cc, bc_impl, key, key_len, iv, tag_len); +} + +static void +ccm_max_plaintext(const br_sslrec_ccm_context *cc, + size_t *start, size_t *end) +{ + size_t len; + + *start += 8; + len = *end - *start - cc->tag_len; + if (len > 16384) { + len = 16384; + } + *end = *start + len; +} + +static unsigned char * +ccm_encrypt(br_sslrec_ccm_context *cc, + int record_type, unsigned version, void *data, size_t *data_len) +{ + br_ccm_context zc; + unsigned char *buf; + unsigned char nonce[12], header[13]; + size_t len; + + buf = (unsigned char *)data; + len = *data_len; + + /* + * Make nonce; the explicit part is an encoding of the sequence + * number. + */ + memcpy(nonce, cc->iv, sizeof cc->iv); + br_enc64be(nonce + 4, cc->seq); + + /* + * Assemble synthetic header for the AAD. + */ + br_enc64be(header, cc->seq ++); + header[8] = (unsigned char)record_type; + br_enc16be(header + 9, version); + br_enc16be(header + 11, len); + + /* + * Perform CCM encryption. + */ + br_ccm_init(&zc, &cc->bc.vtable); + br_ccm_reset(&zc, nonce, sizeof nonce, sizeof header, len, cc->tag_len); + br_ccm_aad_inject(&zc, header, sizeof header); + br_ccm_flip(&zc); + br_ccm_run(&zc, 1, buf, len); + br_ccm_get_tag(&zc, buf + len); + + /* + * Assemble header and adjust pointer/length. + */ + len += 8 + cc->tag_len; + buf -= 13; + memcpy(buf + 5, nonce + 4, 8); + buf[0] = (unsigned char)record_type; + br_enc16be(buf + 1, version); + br_enc16be(buf + 3, len); + *data_len = len + 5; + return buf; +} + +/* see bearssl_ssl.h */ +const br_sslrec_out_ccm_class br_sslrec_out_ccm_vtable PROGMEM = { + { + sizeof(br_sslrec_ccm_context), + (void (*)(const br_sslrec_out_class *const *, + size_t *, size_t *)) + &ccm_max_plaintext, + (unsigned char *(*)(const br_sslrec_out_class **, + int, unsigned, void *, size_t *)) + &ccm_encrypt + }, + (void (*)(const br_sslrec_out_ccm_class **, + const br_block_ctrcbc_class *, const void *, size_t, + const void *, size_t)) + &out_ccm_init +}; diff --git a/lib/bearssl-esp8266/src/ssl/ssl_rec_chapol.c b/lib/bearssl-esp8266/src/ssl/ssl_rec_chapol.c new file mode 100644 index 000000000..6273680a3 --- /dev/null +++ b/lib/bearssl-esp8266/src/ssl/ssl_rec_chapol.c @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +static void +gen_chapol_init(br_sslrec_chapol_context *cc, + br_chacha20_run ichacha, br_poly1305_run ipoly, + const void *key, const void *iv) +{ + cc->seq = 0; + cc->ichacha = ichacha; + cc->ipoly = ipoly; + memcpy(cc->key, key, sizeof cc->key); + memcpy(cc->iv, iv, sizeof cc->iv); +} + +static void +gen_chapol_process(br_sslrec_chapol_context *cc, + int record_type, unsigned version, void *data, size_t len, + void *tag, int encrypt) +{ + unsigned char header[13]; + unsigned char nonce[12]; + uint64_t seq; + size_t u; + + seq = cc->seq ++; + br_enc64be(header, seq); + header[8] = (unsigned char)record_type; + br_enc16be(header + 9, version); + br_enc16be(header + 11, len); + memcpy(nonce, cc->iv, 12); + for (u = 0; u < 8; u ++) { + nonce[11 - u] ^= (unsigned char)seq; + seq >>= 8; + } + cc->ipoly(cc->key, nonce, data, len, header, sizeof header, + tag, cc->ichacha, encrypt); +} + +static void +in_chapol_init(br_sslrec_chapol_context *cc, + br_chacha20_run ichacha, br_poly1305_run ipoly, + const void *key, const void *iv) +{ + cc->vtable.in = &br_sslrec_in_chapol_vtable; + gen_chapol_init(cc, ichacha, ipoly, key, iv); +} + +static int +chapol_check_length(const br_sslrec_chapol_context *cc, size_t rlen) +{ + /* + * Overhead is just the authentication tag (16 bytes). + */ + (void)cc; + return rlen >= 16 && rlen <= (16384 + 16); +} + +static unsigned char * +chapol_decrypt(br_sslrec_chapol_context *cc, + int record_type, unsigned version, void *data, size_t *data_len) +{ + unsigned char *buf; + size_t u, len; + unsigned char tag[16]; + unsigned bad; + + buf = data; + len = *data_len - 16; + gen_chapol_process(cc, record_type, version, buf, len, tag, 0); + bad = 0; + for (u = 0; u < 16; u ++) { + bad |= tag[u] ^ buf[len + u]; + } + if (bad) { + return NULL; + } + *data_len = len; + return buf; +} + +/* see bearssl_ssl.h */ +const br_sslrec_in_chapol_class br_sslrec_in_chapol_vtable PROGMEM = { + { + sizeof(br_sslrec_chapol_context), + (int (*)(const br_sslrec_in_class *const *, size_t)) + &chapol_check_length, + (unsigned char *(*)(const br_sslrec_in_class **, + int, unsigned, void *, size_t *)) + &chapol_decrypt + }, + (void (*)(const br_sslrec_in_chapol_class **, + br_chacha20_run, br_poly1305_run, + const void *, const void *)) + &in_chapol_init +}; + +static void +out_chapol_init(br_sslrec_chapol_context *cc, + br_chacha20_run ichacha, br_poly1305_run ipoly, + const void *key, const void *iv) +{ + cc->vtable.out = &br_sslrec_out_chapol_vtable; + gen_chapol_init(cc, ichacha, ipoly, key, iv); +} + +static void +chapol_max_plaintext(const br_sslrec_chapol_context *cc, + size_t *start, size_t *end) +{ + size_t len; + + (void)cc; + len = *end - *start - 16; + if (len > 16384) { + len = 16384; + } + *end = *start + len; +} + +static unsigned char * +chapol_encrypt(br_sslrec_chapol_context *cc, + int record_type, unsigned version, void *data, size_t *data_len) +{ + unsigned char *buf; + size_t len; + + buf = data; + len = *data_len; + gen_chapol_process(cc, record_type, version, buf, len, buf + len, 1); + buf -= 5; + buf[0] = (unsigned char)record_type; + br_enc16be(buf + 1, version); + br_enc16be(buf + 3, len + 16); + *data_len = len + 21; + return buf; +} + +/* see bearssl_ssl.h */ +const br_sslrec_out_chapol_class br_sslrec_out_chapol_vtable PROGMEM = { + { + sizeof(br_sslrec_chapol_context), + (void (*)(const br_sslrec_out_class *const *, + size_t *, size_t *)) + &chapol_max_plaintext, + (unsigned char *(*)(const br_sslrec_out_class **, + int, unsigned, void *, size_t *)) + &chapol_encrypt + }, + (void (*)(const br_sslrec_out_chapol_class **, + br_chacha20_run, br_poly1305_run, + const void *, const void *)) + &out_chapol_init +}; diff --git a/lib/bearssl-esp8266/src/ssl/ssl_rec_gcm.c b/lib/bearssl-esp8266/src/ssl/ssl_rec_gcm.c new file mode 100644 index 000000000..933eaffb4 --- /dev/null +++ b/lib/bearssl-esp8266/src/ssl/ssl_rec_gcm.c @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * GCM initialisation. This does everything except setting the vtable, + * which depends on whether this is a context for encrypting or for + * decrypting. + */ +static void +gen_gcm_init(br_sslrec_gcm_context *cc, + const br_block_ctr_class *bc_impl, + const void *key, size_t key_len, + br_ghash gh_impl, + const void *iv) +{ + unsigned char tmp[12]; + + cc->seq = 0; + bc_impl->init(&cc->bc.vtable, key, key_len); + cc->gh = gh_impl; + memcpy(cc->iv, iv, sizeof cc->iv); + memset(cc->h, 0, sizeof cc->h); + memset(tmp, 0, sizeof tmp); + bc_impl->run(&cc->bc.vtable, tmp, 0, cc->h, sizeof cc->h); +} + +static void +in_gcm_init(br_sslrec_gcm_context *cc, + const br_block_ctr_class *bc_impl, + const void *key, size_t key_len, + br_ghash gh_impl, + const void *iv) +{ + cc->vtable.in = &br_sslrec_in_gcm_vtable; + gen_gcm_init(cc, bc_impl, key, key_len, gh_impl, iv); +} + +static int +gcm_check_length(const br_sslrec_gcm_context *cc, size_t rlen) +{ + /* + * GCM adds a fixed overhead: + * 8 bytes for the nonce_explicit (before the ciphertext) + * 16 bytes for the authentication tag (after the ciphertext) + */ + (void)cc; + return rlen >= 24 && rlen <= (16384 + 24); +} + +/* + * Compute the authentication tag. The value written in 'tag' must still + * be CTR-encrypted. + */ +static void +do_tag(br_sslrec_gcm_context *cc, + int record_type, unsigned version, + void *data, size_t len, void *tag) +{ + unsigned char header[13]; + unsigned char footer[16]; + + /* + * Compute authentication tag. Three elements must be injected in + * sequence, each possibly 0-padded to reach a length multiple + * of the block size: the 13-byte header (sequence number, record + * type, protocol version, record length), the cipher text, and + * the word containing the encodings of the bit lengths of the two + * other elements. + */ + br_enc64be(header, cc->seq ++); + header[8] = (unsigned char)record_type; + br_enc16be(header + 9, version); + br_enc16be(header + 11, len); + br_enc64be(footer, (uint64_t)(sizeof header) << 3); + br_enc64be(footer + 8, (uint64_t)len << 3); + memset(tag, 0, 16); + cc->gh(tag, cc->h, header, sizeof header); + cc->gh(tag, cc->h, data, len); + cc->gh(tag, cc->h, footer, sizeof footer); +} + +/* + * Do CTR encryption. This also does CTR encryption of a single block at + * address 'xortag' with the counter value appropriate for the final + * processing of the authentication tag. + */ +static void +do_ctr(br_sslrec_gcm_context *cc, const void *nonce, void *data, size_t len, + void *xortag) +{ + unsigned char iv[12]; + + memcpy(iv, cc->iv, 4); + memcpy(iv + 4, nonce, 8); + cc->bc.vtable->run(&cc->bc.vtable, iv, 2, data, len); + cc->bc.vtable->run(&cc->bc.vtable, iv, 1, xortag, 16); +} + +static unsigned char * +gcm_decrypt(br_sslrec_gcm_context *cc, + int record_type, unsigned version, void *data, size_t *data_len) +{ + unsigned char *buf; + size_t len, u; + uint32_t bad; + unsigned char tag[16]; + + buf = (unsigned char *)data + 8; + len = *data_len - 24; + do_tag(cc, record_type, version, buf, len, tag); + do_ctr(cc, data, buf, len, tag); + + /* + * Compare the computed tag with the value from the record. It + * is possibly useless to do a constant-time comparison here, + * but it does not hurt. + */ + bad = 0; + for (u = 0; u < 16; u ++) { + bad |= tag[u] ^ buf[len + u]; + } + if (bad) { + return NULL; + } + *data_len = len; + return buf; +} + +/* see bearssl_ssl.h */ +const br_sslrec_in_gcm_class br_sslrec_in_gcm_vtable PROGMEM = { + { + sizeof(br_sslrec_gcm_context), + (int (*)(const br_sslrec_in_class *const *, size_t)) + &gcm_check_length, + (unsigned char *(*)(const br_sslrec_in_class **, + int, unsigned, void *, size_t *)) + &gcm_decrypt + }, + (void (*)(const br_sslrec_in_gcm_class **, + const br_block_ctr_class *, const void *, size_t, + br_ghash, const void *)) + &in_gcm_init +}; + +static void +out_gcm_init(br_sslrec_gcm_context *cc, + const br_block_ctr_class *bc_impl, + const void *key, size_t key_len, + br_ghash gh_impl, + const void *iv) +{ + cc->vtable.out = &br_sslrec_out_gcm_vtable; + gen_gcm_init(cc, bc_impl, key, key_len, gh_impl, iv); +} + +static void +gcm_max_plaintext(const br_sslrec_gcm_context *cc, + size_t *start, size_t *end) +{ + size_t len; + + (void)cc; + *start += 8; + len = *end - *start - 16; + if (len > 16384) { + len = 16384; + } + *end = *start + len; +} + +static unsigned char * +gcm_encrypt(br_sslrec_gcm_context *cc, + int record_type, unsigned version, void *data, size_t *data_len) +{ + unsigned char *buf; + size_t u, len; + unsigned char tmp[16]; + + buf = (unsigned char *)data; + len = *data_len; + memset(tmp, 0, sizeof tmp); + br_enc64be(buf - 8, cc->seq); + do_ctr(cc, buf - 8, buf, len, tmp); + do_tag(cc, record_type, version, buf, len, buf + len); + for (u = 0; u < 16; u ++) { + buf[len + u] ^= tmp[u]; + } + len += 24; + buf -= 13; + buf[0] = (unsigned char)record_type; + br_enc16be(buf + 1, version); + br_enc16be(buf + 3, len); + *data_len = len + 5; + return buf; +} + +/* see bearssl_ssl.h */ +const br_sslrec_out_gcm_class br_sslrec_out_gcm_vtable PROGMEM = { + { + sizeof(br_sslrec_gcm_context), + (void (*)(const br_sslrec_out_class *const *, + size_t *, size_t *)) + &gcm_max_plaintext, + (unsigned char *(*)(const br_sslrec_out_class **, + int, unsigned, void *, size_t *)) + &gcm_encrypt + }, + (void (*)(const br_sslrec_out_gcm_class **, + const br_block_ctr_class *, const void *, size_t, + br_ghash, const void *)) + &out_gcm_init +}; diff --git a/lib/bearssl-esp8266/src/ssl/ssl_scert_single_ec.c b/lib/bearssl-esp8266/src/ssl/ssl_scert_single_ec.c new file mode 100644 index 000000000..9d1bfa52d --- /dev/null +++ b/lib/bearssl-esp8266/src/ssl/ssl_scert_single_ec.c @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +static int +se_choose(const br_ssl_server_policy_class **pctx, + const br_ssl_server_context *cc, + br_ssl_server_choices *choices) +{ + br_ssl_server_policy_ec_context *pc; + const br_suite_translated *st; + size_t u, st_num; + unsigned hash_id; + + pc = (br_ssl_server_policy_ec_context *)pctx; + st = br_ssl_server_get_client_suites(cc, &st_num); + hash_id = br_ssl_choose_hash(br_ssl_server_get_client_hashes(cc) >> 8); + if (cc->eng.session.version < BR_TLS12) { + hash_id = br_sha1_ID; + } + choices->chain = pc->chain; + choices->chain_len = pc->chain_len; + for (u = 0; u < st_num; u ++) { + unsigned tt; + + tt = st[u][1]; + switch (tt >> 12) { + case BR_SSLKEYX_ECDH_RSA: + if ((pc->allowed_usages & BR_KEYTYPE_KEYX) != 0 + && pc->cert_issuer_key_type == BR_KEYTYPE_RSA) + { + choices->cipher_suite = st[u][0]; + return 1; + } + break; + case BR_SSLKEYX_ECDH_ECDSA: + if ((pc->allowed_usages & BR_KEYTYPE_KEYX) != 0 + && pc->cert_issuer_key_type == BR_KEYTYPE_EC) + { + choices->cipher_suite = st[u][0]; + return 1; + } + break; + case BR_SSLKEYX_ECDHE_ECDSA: + if ((pc->allowed_usages & BR_KEYTYPE_SIGN) != 0 + && hash_id != 0) + { + choices->cipher_suite = st[u][0]; + choices->algo_id = hash_id + 0xFF00; + return 1; + } + break; + } + } + return 0; +} + +static uint32_t +se_do_keyx(const br_ssl_server_policy_class **pctx, + unsigned char *data, size_t *len) +{ + br_ssl_server_policy_ec_context *pc; + uint32_t r; + size_t xoff, xlen; + + pc = (br_ssl_server_policy_ec_context *)pctx; + r = pc->iec->mul(data, *len, pc->sk->x, pc->sk->xlen, pc->sk->curve); + xoff = pc->iec->xoff(pc->sk->curve, &xlen); + memmove(data, data + xoff, xlen); + *len = xlen; + return r; +} + +static size_t +se_do_sign(const br_ssl_server_policy_class **pctx, + unsigned algo_id, unsigned char *data, size_t hv_len, size_t len) +{ + br_ssl_server_policy_ec_context *pc; + unsigned char hv[64]; + const br_hash_class *hc; + + algo_id &= 0xFF; + pc = (br_ssl_server_policy_ec_context *)pctx; + hc = br_multihash_getimpl(pc->mhash, algo_id); + if (hc == NULL) { + return 0; + } + memcpy(hv, data, hv_len); + if (len < 139) { + return 0; + } + return pc->iecdsa(pc->iec, hc, hv, pc->sk, data); +} + +static const br_ssl_server_policy_class se_policy_vtable PROGMEM = { + sizeof(br_ssl_server_policy_ec_context), + se_choose, + se_do_keyx, + se_do_sign +}; + +/* see bearssl_ssl.h */ +void +br_ssl_server_set_single_ec(br_ssl_server_context *cc, + const br_x509_certificate *chain, size_t chain_len, + const br_ec_private_key *sk, unsigned allowed_usages, + unsigned cert_issuer_key_type, + const br_ec_impl *iec, br_ecdsa_sign iecdsa) +{ + cc->chain_handler.single_ec.vtable = &se_policy_vtable; + cc->chain_handler.single_ec.chain = chain; + cc->chain_handler.single_ec.chain_len = chain_len; + cc->chain_handler.single_ec.sk = sk; + cc->chain_handler.single_ec.allowed_usages = allowed_usages; + cc->chain_handler.single_ec.cert_issuer_key_type = cert_issuer_key_type; + cc->chain_handler.single_ec.mhash = &cc->eng.mhash; + cc->chain_handler.single_ec.iec = iec; + cc->chain_handler.single_ec.iecdsa = iecdsa; + cc->policy_vtable = &cc->chain_handler.single_ec.vtable; +} diff --git a/lib/bearssl-esp8266/src/ssl/ssl_scert_single_rsa.c b/lib/bearssl-esp8266/src/ssl/ssl_scert_single_rsa.c new file mode 100644 index 000000000..01de6d0c9 --- /dev/null +++ b/lib/bearssl-esp8266/src/ssl/ssl_scert_single_rsa.c @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +static int +sr_choose(const br_ssl_server_policy_class **pctx, + const br_ssl_server_context *cc, + br_ssl_server_choices *choices) +{ + br_ssl_server_policy_rsa_context *pc; + const br_suite_translated *st; + size_t u, st_num; + unsigned hash_id; + int fh; + + pc = (br_ssl_server_policy_rsa_context *)pctx; + st = br_ssl_server_get_client_suites(cc, &st_num); + if (cc->eng.session.version < BR_TLS12) { + hash_id = 0; + fh = 1; + } else { + hash_id = br_ssl_choose_hash( + br_ssl_server_get_client_hashes(cc)); + fh = (hash_id != 0); + } + choices->chain = pc->chain; + choices->chain_len = pc->chain_len; + for (u = 0; u < st_num; u ++) { + unsigned tt; + + tt = st[u][1]; + switch (tt >> 12) { + case BR_SSLKEYX_RSA: + if ((pc->allowed_usages & BR_KEYTYPE_KEYX) != 0) { + choices->cipher_suite = st[u][0]; + return 1; + } + break; + case BR_SSLKEYX_ECDHE_RSA: + if ((pc->allowed_usages & BR_KEYTYPE_SIGN) != 0 && fh) { + choices->cipher_suite = st[u][0]; + choices->algo_id = hash_id + 0xFF00; + return 1; + } + break; + } + } + return 0; +} + +static uint32_t +sr_do_keyx(const br_ssl_server_policy_class **pctx, + unsigned char *data, size_t *len) +{ + br_ssl_server_policy_rsa_context *pc; + + pc = (br_ssl_server_policy_rsa_context *)pctx; + return br_rsa_ssl_decrypt(pc->irsacore, pc->sk, data, *len); +} + +/* + * OID for hash functions in RSA signatures. + */ +/*static*/ const unsigned char HASH_OID_SHA1[] = { + 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A +}; + +/*static*/ const unsigned char HASH_OID_SHA224[] = { + 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04 +}; + +/*static*/ const unsigned char HASH_OID_SHA256[] = { + 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01 +}; + +/*static*/ const unsigned char HASH_OID_SHA384[] = { + 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02 +}; + +/*static*/ const unsigned char HASH_OID_SHA512[] = { + 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03 +}; + +static const unsigned char *HASH_OID[] PROGMEM = { + HASH_OID_SHA1, + HASH_OID_SHA224, + HASH_OID_SHA256, + HASH_OID_SHA384, + HASH_OID_SHA512 +}; + +static size_t +sr_do_sign(const br_ssl_server_policy_class **pctx, + unsigned algo_id, unsigned char *data, size_t hv_len, size_t len) +{ + br_ssl_server_policy_rsa_context *pc; + unsigned char hv[64]; + size_t sig_len; + const unsigned char *hash_oid; + + pc = (br_ssl_server_policy_rsa_context *)pctx; + memcpy(hv, data, hv_len); + algo_id &= 0xFF; + if (algo_id == 0) { + hash_oid = NULL; + } else if (algo_id >= 2 && algo_id <= 6) { + hash_oid = HASH_OID[algo_id - 2]; + } else { + return 0; + } + sig_len = (pc->sk->n_bitlen + 7) >> 3; + if (len < sig_len) { + return 0; + } + return pc->irsasign(hash_oid, hv, hv_len, pc->sk, data) ? sig_len : 0; +} + +static const br_ssl_server_policy_class sr_policy_vtable PROGMEM = { + sizeof(br_ssl_server_policy_rsa_context), + sr_choose, + sr_do_keyx, + sr_do_sign +}; + +/* see bearssl_ssl.h */ +void +br_ssl_server_set_single_rsa(br_ssl_server_context *cc, + const br_x509_certificate *chain, size_t chain_len, + const br_rsa_private_key *sk, unsigned allowed_usages, + br_rsa_private irsacore, br_rsa_pkcs1_sign irsasign) +{ + cc->chain_handler.single_rsa.vtable = &sr_policy_vtable; + cc->chain_handler.single_rsa.chain = chain; + cc->chain_handler.single_rsa.chain_len = chain_len; + cc->chain_handler.single_rsa.sk = sk; + cc->chain_handler.single_rsa.allowed_usages = allowed_usages; + cc->chain_handler.single_rsa.irsacore = irsacore; + cc->chain_handler.single_rsa.irsasign = irsasign; + cc->policy_vtable = &cc->chain_handler.single_rsa.vtable; +} diff --git a/lib/bearssl-esp8266/src/symcipher/aes_big_cbcdec.c b/lib/bearssl-esp8266/src/symcipher/aes_big_cbcdec.c new file mode 100644 index 000000000..fe11a0c4e --- /dev/null +++ b/lib/bearssl-esp8266/src/symcipher/aes_big_cbcdec.c @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_block.h */ +void +br_aes_big_cbcdec_init(br_aes_big_cbcdec_keys *ctx, + const void *key, size_t len) +{ + ctx->vtable = &br_aes_big_cbcdec_vtable; + ctx->num_rounds = br_aes_big_keysched_inv(ctx->skey, key, len); +} + +/* see bearssl_block.h */ +void +br_aes_big_cbcdec_run(const br_aes_big_cbcdec_keys *ctx, + void *iv, void *data, size_t len) +{ + unsigned char *buf, *ivbuf; + + ivbuf = iv; + buf = data; + while (len > 0) { + unsigned char tmp[16]; + int i; + + memcpy(tmp, buf, 16); + br_aes_big_decrypt(ctx->num_rounds, ctx->skey, buf); + for (i = 0; i < 16; i ++) { + buf[i] ^= ivbuf[i]; + } + memcpy(ivbuf, tmp, 16); + buf += 16; + len -= 16; + } +} + +/* see bearssl_block.h */ +const br_block_cbcdec_class br_aes_big_cbcdec_vtable PROGMEM = { + sizeof(br_aes_big_cbcdec_keys), + 16, + 4, + (void (*)(const br_block_cbcdec_class **, const void *, size_t)) + &br_aes_big_cbcdec_init, + (void (*)(const br_block_cbcdec_class *const *, void *, void *, size_t)) + &br_aes_big_cbcdec_run +}; diff --git a/lib/bearssl-esp8266/src/symcipher/aes_big_cbcenc.c b/lib/bearssl-esp8266/src/symcipher/aes_big_cbcenc.c new file mode 100644 index 000000000..215870c48 --- /dev/null +++ b/lib/bearssl-esp8266/src/symcipher/aes_big_cbcenc.c @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_block.h */ +void +br_aes_big_cbcenc_init(br_aes_big_cbcenc_keys *ctx, + const void *key, size_t len) +{ + ctx->vtable = &br_aes_big_cbcenc_vtable; + ctx->num_rounds = br_aes_keysched(ctx->skey, key, len); +} + +/* see bearssl_block.h */ +void +br_aes_big_cbcenc_run(const br_aes_big_cbcenc_keys *ctx, + void *iv, void *data, size_t len) +{ + unsigned char *buf, *ivbuf; + + ivbuf = iv; + buf = data; + while (len > 0) { + int i; + + for (i = 0; i < 16; i ++) { + buf[i] ^= ivbuf[i]; + } + br_aes_big_encrypt(ctx->num_rounds, ctx->skey, buf); + memcpy(ivbuf, buf, 16); + buf += 16; + len -= 16; + } +} + +/* see bearssl_block.h */ +const br_block_cbcenc_class br_aes_big_cbcenc_vtable PROGMEM = { + sizeof(br_aes_big_cbcenc_keys), + 16, + 4, + (void (*)(const br_block_cbcenc_class **, const void *, size_t)) + &br_aes_big_cbcenc_init, + (void (*)(const br_block_cbcenc_class *const *, void *, void *, size_t)) + &br_aes_big_cbcenc_run +}; diff --git a/lib/bearssl-esp8266/src/symcipher/aes_big_ctr.c b/lib/bearssl-esp8266/src/symcipher/aes_big_ctr.c new file mode 100644 index 000000000..6f853dbcb --- /dev/null +++ b/lib/bearssl-esp8266/src/symcipher/aes_big_ctr.c @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_block.h */ +void +br_aes_big_ctr_init(br_aes_big_ctr_keys *ctx, + const void *key, size_t len) +{ + ctx->vtable = &br_aes_big_ctr_vtable; + ctx->num_rounds = br_aes_keysched(ctx->skey, key, len); +} + +static void +xorbuf(void *dst, const void *src, size_t len) +{ + unsigned char *d; + const unsigned char *s; + + d = dst; + s = src; + while (len -- > 0) { + *d ++ ^= *s ++; + } +} + +/* see bearssl_block.h */ +uint32_t +br_aes_big_ctr_run(const br_aes_big_ctr_keys *ctx, + const void *iv, uint32_t cc, void *data, size_t len) +{ + unsigned char *buf; + + buf = data; + while (len > 0) { + unsigned char tmp[16]; + + memcpy(tmp, iv, 12); + br_enc32be(tmp + 12, cc ++); + br_aes_big_encrypt(ctx->num_rounds, ctx->skey, tmp); + if (len <= 16) { + xorbuf(buf, tmp, len); + break; + } + xorbuf(buf, tmp, 16); + buf += 16; + len -= 16; + } + return cc; +} + +/* see bearssl_block.h */ +const br_block_ctr_class br_aes_big_ctr_vtable PROGMEM = { + sizeof(br_aes_big_ctr_keys), + 16, + 4, + (void (*)(const br_block_ctr_class **, const void *, size_t)) + &br_aes_big_ctr_init, + (uint32_t (*)(const br_block_ctr_class *const *, + const void *, uint32_t, void *, size_t)) + &br_aes_big_ctr_run +}; diff --git a/lib/bearssl-esp8266/src/symcipher/aes_big_ctrcbc.c b/lib/bearssl-esp8266/src/symcipher/aes_big_ctrcbc.c new file mode 100644 index 000000000..aed59ed54 --- /dev/null +++ b/lib/bearssl-esp8266/src/symcipher/aes_big_ctrcbc.c @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_block.h */ +void +br_aes_big_ctrcbc_init(br_aes_big_ctrcbc_keys *ctx, + const void *key, size_t len) +{ + ctx->vtable = &br_aes_big_ctrcbc_vtable; + ctx->num_rounds = br_aes_keysched(ctx->skey, key, len); +} + +static void +xorbuf(void *dst, const void *src, size_t len) +{ + unsigned char *d; + const unsigned char *s; + + d = dst; + s = src; + while (len -- > 0) { + *d ++ ^= *s ++; + } +} + +/* see bearssl_block.h */ +void +br_aes_big_ctrcbc_ctr(const br_aes_big_ctrcbc_keys *ctx, + void *ctr, void *data, size_t len) +{ + unsigned char *buf, *bctr; + uint32_t cc0, cc1, cc2, cc3; + + buf = data; + bctr = ctr; + cc3 = br_dec32be(bctr + 0); + cc2 = br_dec32be(bctr + 4); + cc1 = br_dec32be(bctr + 8); + cc0 = br_dec32be(bctr + 12); + while (len > 0) { + unsigned char tmp[16]; + uint32_t carry; + + br_enc32be(tmp + 0, cc3); + br_enc32be(tmp + 4, cc2); + br_enc32be(tmp + 8, cc1); + br_enc32be(tmp + 12, cc0); + br_aes_big_encrypt(ctx->num_rounds, ctx->skey, tmp); + xorbuf(buf, tmp, 16); + buf += 16; + len -= 16; + cc0 ++; + carry = (~(cc0 | -cc0)) >> 31; + cc1 += carry; + carry &= (~(cc1 | -cc1)) >> 31; + cc2 += carry; + carry &= (~(cc2 | -cc2)) >> 31; + cc3 += carry; + } + br_enc32be(bctr + 0, cc3); + br_enc32be(bctr + 4, cc2); + br_enc32be(bctr + 8, cc1); + br_enc32be(bctr + 12, cc0); +} + +/* see bearssl_block.h */ +void +br_aes_big_ctrcbc_mac(const br_aes_big_ctrcbc_keys *ctx, + void *cbcmac, const void *data, size_t len) +{ + const unsigned char *buf; + + buf = data; + while (len > 0) { + xorbuf(cbcmac, buf, 16); + br_aes_big_encrypt(ctx->num_rounds, ctx->skey, cbcmac); + buf += 16; + len -= 16; + } +} + +/* see bearssl_block.h */ +void +br_aes_big_ctrcbc_encrypt(const br_aes_big_ctrcbc_keys *ctx, + void *ctr, void *cbcmac, void *data, size_t len) +{ + br_aes_big_ctrcbc_ctr(ctx, ctr, data, len); + br_aes_big_ctrcbc_mac(ctx, cbcmac, data, len); +} + +/* see bearssl_block.h */ +void +br_aes_big_ctrcbc_decrypt(const br_aes_big_ctrcbc_keys *ctx, + void *ctr, void *cbcmac, void *data, size_t len) +{ + br_aes_big_ctrcbc_mac(ctx, cbcmac, data, len); + br_aes_big_ctrcbc_ctr(ctx, ctr, data, len); +} + +/* see bearssl_block.h */ +const br_block_ctrcbc_class br_aes_big_ctrcbc_vtable PROGMEM = { + sizeof(br_aes_big_ctrcbc_keys), + 16, + 4, + (void (*)(const br_block_ctrcbc_class **, const void *, size_t)) + &br_aes_big_ctrcbc_init, + (void (*)(const br_block_ctrcbc_class *const *, + void *, void *, void *, size_t)) + &br_aes_big_ctrcbc_encrypt, + (void (*)(const br_block_ctrcbc_class *const *, + void *, void *, void *, size_t)) + &br_aes_big_ctrcbc_decrypt, + (void (*)(const br_block_ctrcbc_class *const *, + void *, void *, size_t)) + &br_aes_big_ctrcbc_ctr, + (void (*)(const br_block_ctrcbc_class *const *, + void *, const void *, size_t)) + &br_aes_big_ctrcbc_mac +}; diff --git a/lib/bearssl-esp8266/src/symcipher/aes_big_dec.c b/lib/bearssl-esp8266/src/symcipher/aes_big_dec.c new file mode 100644 index 000000000..a762402ee --- /dev/null +++ b/lib/bearssl-esp8266/src/symcipher/aes_big_dec.c @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * Inverse S-box (used in key schedule for decryption). + */ +static const unsigned char iS[] = { + 0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, + 0x81, 0xF3, 0xD7, 0xFB, 0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, + 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB, 0x54, 0x7B, 0x94, 0x32, + 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E, + 0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, + 0x6D, 0x8B, 0xD1, 0x25, 0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, + 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92, 0x6C, 0x70, 0x48, 0x50, + 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84, + 0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, + 0xB8, 0xB3, 0x45, 0x06, 0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, + 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B, 0x3A, 0x91, 0x11, 0x41, + 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73, + 0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, + 0x1C, 0x75, 0xDF, 0x6E, 0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, + 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B, 0xFC, 0x56, 0x3E, 0x4B, + 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4, + 0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, + 0x27, 0x80, 0xEC, 0x5F, 0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, + 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF, 0xA0, 0xE0, 0x3B, 0x4D, + 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61, + 0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, + 0x55, 0x21, 0x0C, 0x7D +}; + +static const uint32_t iSsm0[] = { + 0x51F4A750, 0x7E416553, 0x1A17A4C3, 0x3A275E96, 0x3BAB6BCB, 0x1F9D45F1, + 0xACFA58AB, 0x4BE30393, 0x2030FA55, 0xAD766DF6, 0x88CC7691, 0xF5024C25, + 0x4FE5D7FC, 0xC52ACBD7, 0x26354480, 0xB562A38F, 0xDEB15A49, 0x25BA1B67, + 0x45EA0E98, 0x5DFEC0E1, 0xC32F7502, 0x814CF012, 0x8D4697A3, 0x6BD3F9C6, + 0x038F5FE7, 0x15929C95, 0xBF6D7AEB, 0x955259DA, 0xD4BE832D, 0x587421D3, + 0x49E06929, 0x8EC9C844, 0x75C2896A, 0xF48E7978, 0x99583E6B, 0x27B971DD, + 0xBEE14FB6, 0xF088AD17, 0xC920AC66, 0x7DCE3AB4, 0x63DF4A18, 0xE51A3182, + 0x97513360, 0x62537F45, 0xB16477E0, 0xBB6BAE84, 0xFE81A01C, 0xF9082B94, + 0x70486858, 0x8F45FD19, 0x94DE6C87, 0x527BF8B7, 0xAB73D323, 0x724B02E2, + 0xE31F8F57, 0x6655AB2A, 0xB2EB2807, 0x2FB5C203, 0x86C57B9A, 0xD33708A5, + 0x302887F2, 0x23BFA5B2, 0x02036ABA, 0xED16825C, 0x8ACF1C2B, 0xA779B492, + 0xF307F2F0, 0x4E69E2A1, 0x65DAF4CD, 0x0605BED5, 0xD134621F, 0xC4A6FE8A, + 0x342E539D, 0xA2F355A0, 0x058AE132, 0xA4F6EB75, 0x0B83EC39, 0x4060EFAA, + 0x5E719F06, 0xBD6E1051, 0x3E218AF9, 0x96DD063D, 0xDD3E05AE, 0x4DE6BD46, + 0x91548DB5, 0x71C45D05, 0x0406D46F, 0x605015FF, 0x1998FB24, 0xD6BDE997, + 0x894043CC, 0x67D99E77, 0xB0E842BD, 0x07898B88, 0xE7195B38, 0x79C8EEDB, + 0xA17C0A47, 0x7C420FE9, 0xF8841EC9, 0x00000000, 0x09808683, 0x322BED48, + 0x1E1170AC, 0x6C5A724E, 0xFD0EFFFB, 0x0F853856, 0x3DAED51E, 0x362D3927, + 0x0A0FD964, 0x685CA621, 0x9B5B54D1, 0x24362E3A, 0x0C0A67B1, 0x9357E70F, + 0xB4EE96D2, 0x1B9B919E, 0x80C0C54F, 0x61DC20A2, 0x5A774B69, 0x1C121A16, + 0xE293BA0A, 0xC0A02AE5, 0x3C22E043, 0x121B171D, 0x0E090D0B, 0xF28BC7AD, + 0x2DB6A8B9, 0x141EA9C8, 0x57F11985, 0xAF75074C, 0xEE99DDBB, 0xA37F60FD, + 0xF701269F, 0x5C72F5BC, 0x44663BC5, 0x5BFB7E34, 0x8B432976, 0xCB23C6DC, + 0xB6EDFC68, 0xB8E4F163, 0xD731DCCA, 0x42638510, 0x13972240, 0x84C61120, + 0x854A247D, 0xD2BB3DF8, 0xAEF93211, 0xC729A16D, 0x1D9E2F4B, 0xDCB230F3, + 0x0D8652EC, 0x77C1E3D0, 0x2BB3166C, 0xA970B999, 0x119448FA, 0x47E96422, + 0xA8FC8CC4, 0xA0F03F1A, 0x567D2CD8, 0x223390EF, 0x87494EC7, 0xD938D1C1, + 0x8CCAA2FE, 0x98D40B36, 0xA6F581CF, 0xA57ADE28, 0xDAB78E26, 0x3FADBFA4, + 0x2C3A9DE4, 0x5078920D, 0x6A5FCC9B, 0x547E4662, 0xF68D13C2, 0x90D8B8E8, + 0x2E39F75E, 0x82C3AFF5, 0x9F5D80BE, 0x69D0937C, 0x6FD52DA9, 0xCF2512B3, + 0xC8AC993B, 0x10187DA7, 0xE89C636E, 0xDB3BBB7B, 0xCD267809, 0x6E5918F4, + 0xEC9AB701, 0x834F9AA8, 0xE6956E65, 0xAAFFE67E, 0x21BCCF08, 0xEF15E8E6, + 0xBAE79BD9, 0x4A6F36CE, 0xEA9F09D4, 0x29B07CD6, 0x31A4B2AF, 0x2A3F2331, + 0xC6A59430, 0x35A266C0, 0x744EBC37, 0xFC82CAA6, 0xE090D0B0, 0x33A7D815, + 0xF104984A, 0x41ECDAF7, 0x7FCD500E, 0x1791F62F, 0x764DD68D, 0x43EFB04D, + 0xCCAA4D54, 0xE49604DF, 0x9ED1B5E3, 0x4C6A881B, 0xC12C1FB8, 0x4665517F, + 0x9D5EEA04, 0x018C355D, 0xFA877473, 0xFB0B412E, 0xB3671D5A, 0x92DBD252, + 0xE9105633, 0x6DD64713, 0x9AD7618C, 0x37A10C7A, 0x59F8148E, 0xEB133C89, + 0xCEA927EE, 0xB761C935, 0xE11CE5ED, 0x7A47B13C, 0x9CD2DF59, 0x55F2733F, + 0x1814CE79, 0x73C737BF, 0x53F7CDEA, 0x5FFDAA5B, 0xDF3D6F14, 0x7844DB86, + 0xCAAFF381, 0xB968C43E, 0x3824342C, 0xC2A3405F, 0x161DC372, 0xBCE2250C, + 0x283C498B, 0xFF0D9541, 0x39A80171, 0x080CB3DE, 0xD8B4E49C, 0x6456C190, + 0x7BCB8461, 0xD532B670, 0x486C5C74, 0xD0B85742 +}; + +static unsigned +mul2(unsigned x) +{ + x <<= 1; + return x ^ ((unsigned)(-(int)(x >> 8)) & 0x11B); +} + +static unsigned +mul9(unsigned x) +{ + return x ^ mul2(mul2(mul2(x))); +} + +static unsigned +mulb(unsigned x) +{ + unsigned x2; + + x2 = mul2(x); + return x ^ x2 ^ mul2(mul2(x2)); +} + +static unsigned +muld(unsigned x) +{ + unsigned x4; + + x4 = mul2(mul2(x)); + return x ^ x4 ^ mul2(x4); +} + +static unsigned +mule(unsigned x) +{ + unsigned x2, x4; + + x2 = mul2(x); + x4 = mul2(x2); + return x2 ^ x4 ^ mul2(x4); +} + +/* see inner.h */ +unsigned +br_aes_big_keysched_inv(uint32_t *skey, const void *key, size_t key_len) +{ + unsigned num_rounds; + int i, m; + + /* + * Sub-keys for decryption are distinct from encryption sub-keys + * in that InvMixColumns() is already applied for the inner + * rounds. + */ + num_rounds = br_aes_keysched(skey, key, key_len); + m = (int)(num_rounds << 2); + for (i = 4; i < m; i ++) { + uint32_t p; + unsigned p0, p1, p2, p3; + uint32_t q0, q1, q2, q3; + + p = skey[i]; + p0 = p >> 24; + p1 = (p >> 16) & 0xFF; + p2 = (p >> 8) & 0xFF; + p3 = p & 0xFF; + q0 = mule(p0) ^ mulb(p1) ^ muld(p2) ^ mul9(p3); + q1 = mul9(p0) ^ mule(p1) ^ mulb(p2) ^ muld(p3); + q2 = muld(p0) ^ mul9(p1) ^ mule(p2) ^ mulb(p3); + q3 = mulb(p0) ^ muld(p1) ^ mul9(p2) ^ mule(p3); + skey[i] = (q0 << 24) | (q1 << 16) | (q2 << 8) | q3; + } + return num_rounds; +} + +static inline uint32_t +rotr(uint32_t x, int n) +{ + return (x << (32 - n)) | (x >> n); +} + +#define iSboxExt0(x) (iSsm0[x]) +#define iSboxExt1(x) (rotr(iSsm0[x], 8)) +#define iSboxExt2(x) (rotr(iSsm0[x], 16)) +#define iSboxExt3(x) (rotr(iSsm0[x], 24)) + +/* see bearssl.h */ +void +br_aes_big_decrypt(unsigned num_rounds, const uint32_t *skey, void *data) +{ + unsigned char *buf; + uint32_t s0, s1, s2, s3; + uint32_t t0, t1, t2, t3; + unsigned u; + + buf = data; + s0 = br_dec32be(buf); + s1 = br_dec32be(buf + 4); + s2 = br_dec32be(buf + 8); + s3 = br_dec32be(buf + 12); + s0 ^= skey[(num_rounds << 2) + 0]; + s1 ^= skey[(num_rounds << 2) + 1]; + s2 ^= skey[(num_rounds << 2) + 2]; + s3 ^= skey[(num_rounds << 2) + 3]; + for (u = num_rounds - 1; u > 0; u --) { + uint32_t v0 = iSboxExt0(s0 >> 24) + ^ iSboxExt1((s3 >> 16) & 0xFF) + ^ iSboxExt2((s2 >> 8) & 0xFF) + ^ iSboxExt3(s1 & 0xFF); + uint32_t v1 = iSboxExt0(s1 >> 24) + ^ iSboxExt1((s0 >> 16) & 0xFF) + ^ iSboxExt2((s3 >> 8) & 0xFF) + ^ iSboxExt3(s2 & 0xFF); + uint32_t v2 = iSboxExt0(s2 >> 24) + ^ iSboxExt1((s1 >> 16) & 0xFF) + ^ iSboxExt2((s0 >> 8) & 0xFF) + ^ iSboxExt3(s3 & 0xFF); + uint32_t v3 = iSboxExt0(s3 >> 24) + ^ iSboxExt1((s2 >> 16) & 0xFF) + ^ iSboxExt2((s1 >> 8) & 0xFF) + ^ iSboxExt3(s0 & 0xFF); + s0 = v0; + s1 = v1; + s2 = v2; + s3 = v3; + s0 ^= skey[u << 2]; + s1 ^= skey[(u << 2) + 1]; + s2 ^= skey[(u << 2) + 2]; + s3 ^= skey[(u << 2) + 3]; + } + t0 = ((uint32_t)iS[s0 >> 24] << 24) + | ((uint32_t)iS[(s3 >> 16) & 0xFF] << 16) + | ((uint32_t)iS[(s2 >> 8) & 0xFF] << 8) + | (uint32_t)iS[s1 & 0xFF]; + t1 = ((uint32_t)iS[s1 >> 24] << 24) + | ((uint32_t)iS[(s0 >> 16) & 0xFF] << 16) + | ((uint32_t)iS[(s3 >> 8) & 0xFF] << 8) + | (uint32_t)iS[s2 & 0xFF]; + t2 = ((uint32_t)iS[s2 >> 24] << 24) + | ((uint32_t)iS[(s1 >> 16) & 0xFF] << 16) + | ((uint32_t)iS[(s0 >> 8) & 0xFF] << 8) + | (uint32_t)iS[s3 & 0xFF]; + t3 = ((uint32_t)iS[s3 >> 24] << 24) + | ((uint32_t)iS[(s2 >> 16) & 0xFF] << 16) + | ((uint32_t)iS[(s1 >> 8) & 0xFF] << 8) + | (uint32_t)iS[s0 & 0xFF]; + s0 = t0 ^ skey[0]; + s1 = t1 ^ skey[1]; + s2 = t2 ^ skey[2]; + s3 = t3 ^ skey[3]; + br_enc32be(buf, s0); + br_enc32be(buf + 4, s1); + br_enc32be(buf + 8, s2); + br_enc32be(buf + 12, s3); +} diff --git a/lib/bearssl-esp8266/src/symcipher/aes_big_enc.c b/lib/bearssl-esp8266/src/symcipher/aes_big_enc.c new file mode 100644 index 000000000..9964dcb87 --- /dev/null +++ b/lib/bearssl-esp8266/src/symcipher/aes_big_enc.c @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +#define S br_aes_S + +static const uint32_t Ssm0[] = { + 0xC66363A5, 0xF87C7C84, 0xEE777799, 0xF67B7B8D, 0xFFF2F20D, 0xD66B6BBD, + 0xDE6F6FB1, 0x91C5C554, 0x60303050, 0x02010103, 0xCE6767A9, 0x562B2B7D, + 0xE7FEFE19, 0xB5D7D762, 0x4DABABE6, 0xEC76769A, 0x8FCACA45, 0x1F82829D, + 0x89C9C940, 0xFA7D7D87, 0xEFFAFA15, 0xB25959EB, 0x8E4747C9, 0xFBF0F00B, + 0x41ADADEC, 0xB3D4D467, 0x5FA2A2FD, 0x45AFAFEA, 0x239C9CBF, 0x53A4A4F7, + 0xE4727296, 0x9BC0C05B, 0x75B7B7C2, 0xE1FDFD1C, 0x3D9393AE, 0x4C26266A, + 0x6C36365A, 0x7E3F3F41, 0xF5F7F702, 0x83CCCC4F, 0x6834345C, 0x51A5A5F4, + 0xD1E5E534, 0xF9F1F108, 0xE2717193, 0xABD8D873, 0x62313153, 0x2A15153F, + 0x0804040C, 0x95C7C752, 0x46232365, 0x9DC3C35E, 0x30181828, 0x379696A1, + 0x0A05050F, 0x2F9A9AB5, 0x0E070709, 0x24121236, 0x1B80809B, 0xDFE2E23D, + 0xCDEBEB26, 0x4E272769, 0x7FB2B2CD, 0xEA75759F, 0x1209091B, 0x1D83839E, + 0x582C2C74, 0x341A1A2E, 0x361B1B2D, 0xDC6E6EB2, 0xB45A5AEE, 0x5BA0A0FB, + 0xA45252F6, 0x763B3B4D, 0xB7D6D661, 0x7DB3B3CE, 0x5229297B, 0xDDE3E33E, + 0x5E2F2F71, 0x13848497, 0xA65353F5, 0xB9D1D168, 0x00000000, 0xC1EDED2C, + 0x40202060, 0xE3FCFC1F, 0x79B1B1C8, 0xB65B5BED, 0xD46A6ABE, 0x8DCBCB46, + 0x67BEBED9, 0x7239394B, 0x944A4ADE, 0x984C4CD4, 0xB05858E8, 0x85CFCF4A, + 0xBBD0D06B, 0xC5EFEF2A, 0x4FAAAAE5, 0xEDFBFB16, 0x864343C5, 0x9A4D4DD7, + 0x66333355, 0x11858594, 0x8A4545CF, 0xE9F9F910, 0x04020206, 0xFE7F7F81, + 0xA05050F0, 0x783C3C44, 0x259F9FBA, 0x4BA8A8E3, 0xA25151F3, 0x5DA3A3FE, + 0x804040C0, 0x058F8F8A, 0x3F9292AD, 0x219D9DBC, 0x70383848, 0xF1F5F504, + 0x63BCBCDF, 0x77B6B6C1, 0xAFDADA75, 0x42212163, 0x20101030, 0xE5FFFF1A, + 0xFDF3F30E, 0xBFD2D26D, 0x81CDCD4C, 0x180C0C14, 0x26131335, 0xC3ECEC2F, + 0xBE5F5FE1, 0x359797A2, 0x884444CC, 0x2E171739, 0x93C4C457, 0x55A7A7F2, + 0xFC7E7E82, 0x7A3D3D47, 0xC86464AC, 0xBA5D5DE7, 0x3219192B, 0xE6737395, + 0xC06060A0, 0x19818198, 0x9E4F4FD1, 0xA3DCDC7F, 0x44222266, 0x542A2A7E, + 0x3B9090AB, 0x0B888883, 0x8C4646CA, 0xC7EEEE29, 0x6BB8B8D3, 0x2814143C, + 0xA7DEDE79, 0xBC5E5EE2, 0x160B0B1D, 0xADDBDB76, 0xDBE0E03B, 0x64323256, + 0x743A3A4E, 0x140A0A1E, 0x924949DB, 0x0C06060A, 0x4824246C, 0xB85C5CE4, + 0x9FC2C25D, 0xBDD3D36E, 0x43ACACEF, 0xC46262A6, 0x399191A8, 0x319595A4, + 0xD3E4E437, 0xF279798B, 0xD5E7E732, 0x8BC8C843, 0x6E373759, 0xDA6D6DB7, + 0x018D8D8C, 0xB1D5D564, 0x9C4E4ED2, 0x49A9A9E0, 0xD86C6CB4, 0xAC5656FA, + 0xF3F4F407, 0xCFEAEA25, 0xCA6565AF, 0xF47A7A8E, 0x47AEAEE9, 0x10080818, + 0x6FBABAD5, 0xF0787888, 0x4A25256F, 0x5C2E2E72, 0x381C1C24, 0x57A6A6F1, + 0x73B4B4C7, 0x97C6C651, 0xCBE8E823, 0xA1DDDD7C, 0xE874749C, 0x3E1F1F21, + 0x964B4BDD, 0x61BDBDDC, 0x0D8B8B86, 0x0F8A8A85, 0xE0707090, 0x7C3E3E42, + 0x71B5B5C4, 0xCC6666AA, 0x904848D8, 0x06030305, 0xF7F6F601, 0x1C0E0E12, + 0xC26161A3, 0x6A35355F, 0xAE5757F9, 0x69B9B9D0, 0x17868691, 0x99C1C158, + 0x3A1D1D27, 0x279E9EB9, 0xD9E1E138, 0xEBF8F813, 0x2B9898B3, 0x22111133, + 0xD26969BB, 0xA9D9D970, 0x078E8E89, 0x339494A7, 0x2D9B9BB6, 0x3C1E1E22, + 0x15878792, 0xC9E9E920, 0x87CECE49, 0xAA5555FF, 0x50282878, 0xA5DFDF7A, + 0x038C8C8F, 0x59A1A1F8, 0x09898980, 0x1A0D0D17, 0x65BFBFDA, 0xD7E6E631, + 0x844242C6, 0xD06868B8, 0x824141C3, 0x299999B0, 0x5A2D2D77, 0x1E0F0F11, + 0x7BB0B0CB, 0xA85454FC, 0x6DBBBBD6, 0x2C16163A +}; + +static inline uint32_t +rotr(uint32_t x, int n) +{ + return (x << (32 - n)) | (x >> n); +} + +#define SboxExt0(x) (Ssm0[x]) +#define SboxExt1(x) (rotr(Ssm0[x], 8)) +#define SboxExt2(x) (rotr(Ssm0[x], 16)) +#define SboxExt3(x) (rotr(Ssm0[x], 24)) + + +/* see bearssl.h */ +void +br_aes_big_encrypt(unsigned num_rounds, const uint32_t *skey, void *data) +{ + unsigned char *buf; + uint32_t s0, s1, s2, s3; + uint32_t t0, t1, t2, t3; + unsigned u; + + buf = data; + s0 = br_dec32be(buf); + s1 = br_dec32be(buf + 4); + s2 = br_dec32be(buf + 8); + s3 = br_dec32be(buf + 12); + s0 ^= skey[0]; + s1 ^= skey[1]; + s2 ^= skey[2]; + s3 ^= skey[3]; + for (u = 1; u < num_rounds; u ++) { + uint32_t v0, v1, v2, v3; + + v0 = SboxExt0(s0 >> 24) + ^ SboxExt1((s1 >> 16) & 0xFF) + ^ SboxExt2((s2 >> 8) & 0xFF) + ^ SboxExt3(s3 & 0xFF); + v1 = SboxExt0(s1 >> 24) + ^ SboxExt1((s2 >> 16) & 0xFF) + ^ SboxExt2((s3 >> 8) & 0xFF) + ^ SboxExt3(s0 & 0xFF); + v2 = SboxExt0(s2 >> 24) + ^ SboxExt1((s3 >> 16) & 0xFF) + ^ SboxExt2((s0 >> 8) & 0xFF) + ^ SboxExt3(s1 & 0xFF); + v3 = SboxExt0(s3 >> 24) + ^ SboxExt1((s0 >> 16) & 0xFF) + ^ SboxExt2((s1 >> 8) & 0xFF) + ^ SboxExt3(s2 & 0xFF); + s0 = v0; + s1 = v1; + s2 = v2; + s3 = v3; + s0 ^= skey[u << 2]; + s1 ^= skey[(u << 2) + 1]; + s2 ^= skey[(u << 2) + 2]; + s3 ^= skey[(u << 2) + 3]; + } + t0 = ((uint32_t)S[s0 >> 24] << 24) + | ((uint32_t)S[(s1 >> 16) & 0xFF] << 16) + | ((uint32_t)S[(s2 >> 8) & 0xFF] << 8) + | (uint32_t)S[s3 & 0xFF]; + t1 = ((uint32_t)S[s1 >> 24] << 24) + | ((uint32_t)S[(s2 >> 16) & 0xFF] << 16) + | ((uint32_t)S[(s3 >> 8) & 0xFF] << 8) + | (uint32_t)S[s0 & 0xFF]; + t2 = ((uint32_t)S[s2 >> 24] << 24) + | ((uint32_t)S[(s3 >> 16) & 0xFF] << 16) + | ((uint32_t)S[(s0 >> 8) & 0xFF] << 8) + | (uint32_t)S[s1 & 0xFF]; + t3 = ((uint32_t)S[s3 >> 24] << 24) + | ((uint32_t)S[(s0 >> 16) & 0xFF] << 16) + | ((uint32_t)S[(s1 >> 8) & 0xFF] << 8) + | (uint32_t)S[s2 & 0xFF]; + s0 = t0 ^ skey[num_rounds << 2]; + s1 = t1 ^ skey[(num_rounds << 2) + 1]; + s2 = t2 ^ skey[(num_rounds << 2) + 2]; + s3 = t3 ^ skey[(num_rounds << 2) + 3]; + br_enc32be(buf, s0); + br_enc32be(buf + 4, s1); + br_enc32be(buf + 8, s2); + br_enc32be(buf + 12, s3); +} diff --git a/lib/bearssl-esp8266/src/symcipher/aes_common.c b/lib/bearssl-esp8266/src/symcipher/aes_common.c new file mode 100644 index 000000000..28c4ca63c --- /dev/null +++ b/lib/bearssl-esp8266/src/symcipher/aes_common.c @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +static const uint32_t Rcon[] PROGMEM = { + 0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, 0x20000000, + 0x40000000, 0x80000000, 0x1B000000, 0x36000000 +}; + +#define S br_aes_S + +/* see inner.h */ +const unsigned char br_aes_S[] = { + 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, + 0xFE, 0xD7, 0xAB, 0x76, 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, + 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, 0xB7, 0xFD, 0x93, 0x26, + 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, + 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, + 0xEB, 0x27, 0xB2, 0x75, 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, + 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, 0x53, 0xD1, 0x00, 0xED, + 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, + 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, + 0x50, 0x3C, 0x9F, 0xA8, 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, + 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, 0xCD, 0x0C, 0x13, 0xEC, + 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73, + 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, + 0xDE, 0x5E, 0x0B, 0xDB, 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, + 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, 0xE7, 0xC8, 0x37, 0x6D, + 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08, + 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, + 0x4B, 0xBD, 0x8B, 0x8A, 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, + 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, 0xE1, 0xF8, 0x98, 0x11, + 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF, + 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, + 0xB0, 0x54, 0xBB, 0x16 +}; + +static uint32_t +SubWord(uint32_t x) +{ + return ((uint32_t)S[x >> 24] << 24) + | ((uint32_t)S[(x >> 16) & 0xFF] << 16) + | ((uint32_t)S[(x >> 8) & 0xFF] << 8) + | (uint32_t)S[x & 0xFF]; +} + +/* see inner.h */ +unsigned +br_aes_keysched(uint32_t *skey, const void *key, size_t key_len) +{ + unsigned num_rounds; + int i, j, k, nk, nkf; + + switch (key_len) { + case 16: + num_rounds = 10; + break; + case 24: + num_rounds = 12; + break; + case 32: + num_rounds = 14; + break; + default: + /* abort(); */ + return 0; + } + nk = (int)(key_len >> 2); + nkf = (int)((num_rounds + 1) << 2); + for (i = 0; i < nk; i ++) { + skey[i] = br_dec32be((const unsigned char *)key + (i << 2)); + } + for (i = nk, j = 0, k = 0; i < nkf; i ++) { + uint32_t tmp; + + tmp = skey[i - 1]; + if (j == 0) { + tmp = (tmp << 8) | (tmp >> 24); + tmp = SubWord(tmp) ^ Rcon[k]; + } else if (nk > 6 && j == 4) { + tmp = SubWord(tmp); + } + skey[i] = skey[i - nk] ^ tmp; + if (++ j == nk) { + j = 0; + k ++; + } + } + return num_rounds; +} diff --git a/lib/bearssl-esp8266/src/symcipher/aes_ct.c b/lib/bearssl-esp8266/src/symcipher/aes_ct.c new file mode 100644 index 000000000..5150f42c8 --- /dev/null +++ b/lib/bearssl-esp8266/src/symcipher/aes_ct.c @@ -0,0 +1,328 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +void +br_aes_ct_bitslice_Sbox(uint32_t *q) +{ + /* + * This S-box implementation is a straightforward translation of + * the circuit described by Boyar and Peralta in "A new + * combinational logic minimization technique with applications + * to cryptology" (https://eprint.iacr.org/2009/191.pdf). + * + * Note that variables x* (input) and s* (output) are numbered + * in "reverse" order (x0 is the high bit, x7 is the low bit). + */ + + uint32_t x0, x1, x2, x3, x4, x5, x6, x7; + uint32_t y1, y2, y3, y4, y5, y6, y7, y8, y9; + uint32_t y10, y11, y12, y13, y14, y15, y16, y17, y18, y19; + uint32_t y20, y21; + uint32_t z0, z1, z2, z3, z4, z5, z6, z7, z8, z9; + uint32_t z10, z11, z12, z13, z14, z15, z16, z17; + uint32_t t0, t1, t2, t3, t4, t5, t6, t7, t8, t9; + uint32_t t10, t11, t12, t13, t14, t15, t16, t17, t18, t19; + uint32_t t20, t21, t22, t23, t24, t25, t26, t27, t28, t29; + uint32_t t30, t31, t32, t33, t34, t35, t36, t37, t38, t39; + uint32_t t40, t41, t42, t43, t44, t45, t46, t47, t48, t49; + uint32_t t50, t51, t52, t53, t54, t55, t56, t57, t58, t59; + uint32_t t60, t61, t62, t63, t64, t65, t66, t67; + uint32_t s0, s1, s2, s3, s4, s5, s6, s7; + + x0 = q[7]; + x1 = q[6]; + x2 = q[5]; + x3 = q[4]; + x4 = q[3]; + x5 = q[2]; + x6 = q[1]; + x7 = q[0]; + + /* + * Top linear transformation. + */ + y14 = x3 ^ x5; + y13 = x0 ^ x6; + y9 = x0 ^ x3; + y8 = x0 ^ x5; + t0 = x1 ^ x2; + y1 = t0 ^ x7; + y4 = y1 ^ x3; + y12 = y13 ^ y14; + y2 = y1 ^ x0; + y5 = y1 ^ x6; + y3 = y5 ^ y8; + t1 = x4 ^ y12; + y15 = t1 ^ x5; + y20 = t1 ^ x1; + y6 = y15 ^ x7; + y10 = y15 ^ t0; + y11 = y20 ^ y9; + y7 = x7 ^ y11; + y17 = y10 ^ y11; + y19 = y10 ^ y8; + y16 = t0 ^ y11; + y21 = y13 ^ y16; + y18 = x0 ^ y16; + + /* + * Non-linear section. + */ + t2 = y12 & y15; + t3 = y3 & y6; + t4 = t3 ^ t2; + t5 = y4 & x7; + t6 = t5 ^ t2; + t7 = y13 & y16; + t8 = y5 & y1; + t9 = t8 ^ t7; + t10 = y2 & y7; + t11 = t10 ^ t7; + t12 = y9 & y11; + t13 = y14 & y17; + t14 = t13 ^ t12; + t15 = y8 & y10; + t16 = t15 ^ t12; + t17 = t4 ^ t14; + t18 = t6 ^ t16; + t19 = t9 ^ t14; + t20 = t11 ^ t16; + t21 = t17 ^ y20; + t22 = t18 ^ y19; + t23 = t19 ^ y21; + t24 = t20 ^ y18; + + t25 = t21 ^ t22; + t26 = t21 & t23; + t27 = t24 ^ t26; + t28 = t25 & t27; + t29 = t28 ^ t22; + t30 = t23 ^ t24; + t31 = t22 ^ t26; + t32 = t31 & t30; + t33 = t32 ^ t24; + t34 = t23 ^ t33; + t35 = t27 ^ t33; + t36 = t24 & t35; + t37 = t36 ^ t34; + t38 = t27 ^ t36; + t39 = t29 & t38; + t40 = t25 ^ t39; + + t41 = t40 ^ t37; + t42 = t29 ^ t33; + t43 = t29 ^ t40; + t44 = t33 ^ t37; + t45 = t42 ^ t41; + z0 = t44 & y15; + z1 = t37 & y6; + z2 = t33 & x7; + z3 = t43 & y16; + z4 = t40 & y1; + z5 = t29 & y7; + z6 = t42 & y11; + z7 = t45 & y17; + z8 = t41 & y10; + z9 = t44 & y12; + z10 = t37 & y3; + z11 = t33 & y4; + z12 = t43 & y13; + z13 = t40 & y5; + z14 = t29 & y2; + z15 = t42 & y9; + z16 = t45 & y14; + z17 = t41 & y8; + + /* + * Bottom linear transformation. + */ + t46 = z15 ^ z16; + t47 = z10 ^ z11; + t48 = z5 ^ z13; + t49 = z9 ^ z10; + t50 = z2 ^ z12; + t51 = z2 ^ z5; + t52 = z7 ^ z8; + t53 = z0 ^ z3; + t54 = z6 ^ z7; + t55 = z16 ^ z17; + t56 = z12 ^ t48; + t57 = t50 ^ t53; + t58 = z4 ^ t46; + t59 = z3 ^ t54; + t60 = t46 ^ t57; + t61 = z14 ^ t57; + t62 = t52 ^ t58; + t63 = t49 ^ t58; + t64 = z4 ^ t59; + t65 = t61 ^ t62; + t66 = z1 ^ t63; + s0 = t59 ^ t63; + s6 = t56 ^ ~t62; + s7 = t48 ^ ~t60; + t67 = t64 ^ t65; + s3 = t53 ^ t66; + s4 = t51 ^ t66; + s5 = t47 ^ t65; + s1 = t64 ^ ~s3; + s2 = t55 ^ ~t67; + + q[7] = s0; + q[6] = s1; + q[5] = s2; + q[4] = s3; + q[3] = s4; + q[2] = s5; + q[1] = s6; + q[0] = s7; +} + +/* see inner.h */ +void +br_aes_ct_ortho(uint32_t *q) +{ +#define SWAPN(cl, ch, s, x, y) do { \ + uint32_t a, b; \ + a = (x); \ + b = (y); \ + (x) = (a & (uint32_t)cl) | ((b & (uint32_t)cl) << (s)); \ + (y) = ((a & (uint32_t)ch) >> (s)) | (b & (uint32_t)ch); \ + } while (0) + +#define SWAP2(x, y) SWAPN(0x55555555, 0xAAAAAAAA, 1, x, y) +#define SWAP4(x, y) SWAPN(0x33333333, 0xCCCCCCCC, 2, x, y) +#define SWAP8(x, y) SWAPN(0x0F0F0F0F, 0xF0F0F0F0, 4, x, y) + + SWAP2(q[0], q[1]); + SWAP2(q[2], q[3]); + SWAP2(q[4], q[5]); + SWAP2(q[6], q[7]); + + SWAP4(q[0], q[2]); + SWAP4(q[1], q[3]); + SWAP4(q[4], q[6]); + SWAP4(q[5], q[7]); + + SWAP8(q[0], q[4]); + SWAP8(q[1], q[5]); + SWAP8(q[2], q[6]); + SWAP8(q[3], q[7]); +} + +static const unsigned char Rcon[] = { + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36 +}; + +static uint32_t +sub_word(uint32_t x) +{ + uint32_t q[8]; + int i; + + for (i = 0; i < 8; i ++) { + q[i] = x; + } + br_aes_ct_ortho(q); + br_aes_ct_bitslice_Sbox(q); + br_aes_ct_ortho(q); + return q[0]; +} + +/* see inner.h */ +unsigned +br_aes_ct_keysched(uint32_t *comp_skey, const void *key, size_t key_len) +{ + unsigned num_rounds; + int i, j, k, nk, nkf; + uint32_t tmp; + uint32_t skey[120]; + + switch (key_len) { + case 16: + num_rounds = 10; + break; + case 24: + num_rounds = 12; + break; + case 32: + num_rounds = 14; + break; + default: + /* abort(); */ + return 0; + } + nk = (int)(key_len >> 2); + nkf = (int)((num_rounds + 1) << 2); + tmp = 0; + for (i = 0; i < nk; i ++) { + tmp = br_dec32le((const unsigned char *)key + (i << 2)); + skey[(i << 1) + 0] = tmp; + skey[(i << 1) + 1] = tmp; + } + for (i = nk, j = 0, k = 0; i < nkf; i ++) { + if (j == 0) { + tmp = (tmp << 24) | (tmp >> 8); + tmp = sub_word(tmp) ^ Rcon[k]; + } else if (nk > 6 && j == 4) { + tmp = sub_word(tmp); + } + tmp ^= skey[(i - nk) << 1]; + skey[(i << 1) + 0] = tmp; + skey[(i << 1) + 1] = tmp; + if (++ j == nk) { + j = 0; + k ++; + } + } + for (i = 0; i < nkf; i += 4) { + br_aes_ct_ortho(skey + (i << 1)); + } + for (i = 0, j = 0; i < nkf; i ++, j += 2) { + comp_skey[i] = (skey[j + 0] & 0x55555555) + | (skey[j + 1] & 0xAAAAAAAA); + } + return num_rounds; +} + +/* see inner.h */ +void +br_aes_ct_skey_expand(uint32_t *skey, + unsigned num_rounds, const uint32_t *comp_skey) +{ + unsigned u, v, n; + + n = (num_rounds + 1) << 2; + for (u = 0, v = 0; u < n; u ++, v += 2) { + uint32_t x, y; + + x = y = comp_skey[u]; + x &= 0x55555555; + skey[v + 0] = x | (x << 1); + y &= 0xAAAAAAAA; + skey[v + 1] = y | (y >> 1); + } +} diff --git a/lib/bearssl-esp8266/src/symcipher/aes_ct64.c b/lib/bearssl-esp8266/src/symcipher/aes_ct64.c new file mode 100644 index 000000000..b4c6ff68e --- /dev/null +++ b/lib/bearssl-esp8266/src/symcipher/aes_ct64.c @@ -0,0 +1,398 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +void +br_aes_ct64_bitslice_Sbox(uint64_t *q) +{ + /* + * This S-box implementation is a straightforward translation of + * the circuit described by Boyar and Peralta in "A new + * combinational logic minimization technique with applications + * to cryptology" (https://eprint.iacr.org/2009/191.pdf). + * + * Note that variables x* (input) and s* (output) are numbered + * in "reverse" order (x0 is the high bit, x7 is the low bit). + */ + + uint64_t x0, x1, x2, x3, x4, x5, x6, x7; + uint64_t y1, y2, y3, y4, y5, y6, y7, y8, y9; + uint64_t y10, y11, y12, y13, y14, y15, y16, y17, y18, y19; + uint64_t y20, y21; + uint64_t z0, z1, z2, z3, z4, z5, z6, z7, z8, z9; + uint64_t z10, z11, z12, z13, z14, z15, z16, z17; + uint64_t t0, t1, t2, t3, t4, t5, t6, t7, t8, t9; + uint64_t t10, t11, t12, t13, t14, t15, t16, t17, t18, t19; + uint64_t t20, t21, t22, t23, t24, t25, t26, t27, t28, t29; + uint64_t t30, t31, t32, t33, t34, t35, t36, t37, t38, t39; + uint64_t t40, t41, t42, t43, t44, t45, t46, t47, t48, t49; + uint64_t t50, t51, t52, t53, t54, t55, t56, t57, t58, t59; + uint64_t t60, t61, t62, t63, t64, t65, t66, t67; + uint64_t s0, s1, s2, s3, s4, s5, s6, s7; + + x0 = q[7]; + x1 = q[6]; + x2 = q[5]; + x3 = q[4]; + x4 = q[3]; + x5 = q[2]; + x6 = q[1]; + x7 = q[0]; + + /* + * Top linear transformation. + */ + y14 = x3 ^ x5; + y13 = x0 ^ x6; + y9 = x0 ^ x3; + y8 = x0 ^ x5; + t0 = x1 ^ x2; + y1 = t0 ^ x7; + y4 = y1 ^ x3; + y12 = y13 ^ y14; + y2 = y1 ^ x0; + y5 = y1 ^ x6; + y3 = y5 ^ y8; + t1 = x4 ^ y12; + y15 = t1 ^ x5; + y20 = t1 ^ x1; + y6 = y15 ^ x7; + y10 = y15 ^ t0; + y11 = y20 ^ y9; + y7 = x7 ^ y11; + y17 = y10 ^ y11; + y19 = y10 ^ y8; + y16 = t0 ^ y11; + y21 = y13 ^ y16; + y18 = x0 ^ y16; + + /* + * Non-linear section. + */ + t2 = y12 & y15; + t3 = y3 & y6; + t4 = t3 ^ t2; + t5 = y4 & x7; + t6 = t5 ^ t2; + t7 = y13 & y16; + t8 = y5 & y1; + t9 = t8 ^ t7; + t10 = y2 & y7; + t11 = t10 ^ t7; + t12 = y9 & y11; + t13 = y14 & y17; + t14 = t13 ^ t12; + t15 = y8 & y10; + t16 = t15 ^ t12; + t17 = t4 ^ t14; + t18 = t6 ^ t16; + t19 = t9 ^ t14; + t20 = t11 ^ t16; + t21 = t17 ^ y20; + t22 = t18 ^ y19; + t23 = t19 ^ y21; + t24 = t20 ^ y18; + + t25 = t21 ^ t22; + t26 = t21 & t23; + t27 = t24 ^ t26; + t28 = t25 & t27; + t29 = t28 ^ t22; + t30 = t23 ^ t24; + t31 = t22 ^ t26; + t32 = t31 & t30; + t33 = t32 ^ t24; + t34 = t23 ^ t33; + t35 = t27 ^ t33; + t36 = t24 & t35; + t37 = t36 ^ t34; + t38 = t27 ^ t36; + t39 = t29 & t38; + t40 = t25 ^ t39; + + t41 = t40 ^ t37; + t42 = t29 ^ t33; + t43 = t29 ^ t40; + t44 = t33 ^ t37; + t45 = t42 ^ t41; + z0 = t44 & y15; + z1 = t37 & y6; + z2 = t33 & x7; + z3 = t43 & y16; + z4 = t40 & y1; + z5 = t29 & y7; + z6 = t42 & y11; + z7 = t45 & y17; + z8 = t41 & y10; + z9 = t44 & y12; + z10 = t37 & y3; + z11 = t33 & y4; + z12 = t43 & y13; + z13 = t40 & y5; + z14 = t29 & y2; + z15 = t42 & y9; + z16 = t45 & y14; + z17 = t41 & y8; + + /* + * Bottom linear transformation. + */ + t46 = z15 ^ z16; + t47 = z10 ^ z11; + t48 = z5 ^ z13; + t49 = z9 ^ z10; + t50 = z2 ^ z12; + t51 = z2 ^ z5; + t52 = z7 ^ z8; + t53 = z0 ^ z3; + t54 = z6 ^ z7; + t55 = z16 ^ z17; + t56 = z12 ^ t48; + t57 = t50 ^ t53; + t58 = z4 ^ t46; + t59 = z3 ^ t54; + t60 = t46 ^ t57; + t61 = z14 ^ t57; + t62 = t52 ^ t58; + t63 = t49 ^ t58; + t64 = z4 ^ t59; + t65 = t61 ^ t62; + t66 = z1 ^ t63; + s0 = t59 ^ t63; + s6 = t56 ^ ~t62; + s7 = t48 ^ ~t60; + t67 = t64 ^ t65; + s3 = t53 ^ t66; + s4 = t51 ^ t66; + s5 = t47 ^ t65; + s1 = t64 ^ ~s3; + s2 = t55 ^ ~t67; + + q[7] = s0; + q[6] = s1; + q[5] = s2; + q[4] = s3; + q[3] = s4; + q[2] = s5; + q[1] = s6; + q[0] = s7; +} + +/* see inner.h */ +void +br_aes_ct64_ortho(uint64_t *q) +{ +#define SWAPN(cl, ch, s, x, y) do { \ + uint64_t a, b; \ + a = (x); \ + b = (y); \ + (x) = (a & (uint64_t)cl) | ((b & (uint64_t)cl) << (s)); \ + (y) = ((a & (uint64_t)ch) >> (s)) | (b & (uint64_t)ch); \ + } while (0) + +#define SWAP2(x, y) SWAPN(0x5555555555555555, 0xAAAAAAAAAAAAAAAA, 1, x, y) +#define SWAP4(x, y) SWAPN(0x3333333333333333, 0xCCCCCCCCCCCCCCCC, 2, x, y) +#define SWAP8(x, y) SWAPN(0x0F0F0F0F0F0F0F0F, 0xF0F0F0F0F0F0F0F0, 4, x, y) + + SWAP2(q[0], q[1]); + SWAP2(q[2], q[3]); + SWAP2(q[4], q[5]); + SWAP2(q[6], q[7]); + + SWAP4(q[0], q[2]); + SWAP4(q[1], q[3]); + SWAP4(q[4], q[6]); + SWAP4(q[5], q[7]); + + SWAP8(q[0], q[4]); + SWAP8(q[1], q[5]); + SWAP8(q[2], q[6]); + SWAP8(q[3], q[7]); +} + +/* see inner.h */ +void +br_aes_ct64_interleave_in(uint64_t *q0, uint64_t *q1, const uint32_t *w) +{ + uint64_t x0, x1, x2, x3; + + x0 = w[0]; + x1 = w[1]; + x2 = w[2]; + x3 = w[3]; + x0 |= (x0 << 16); + x1 |= (x1 << 16); + x2 |= (x2 << 16); + x3 |= (x3 << 16); + x0 &= (uint64_t)0x0000FFFF0000FFFF; + x1 &= (uint64_t)0x0000FFFF0000FFFF; + x2 &= (uint64_t)0x0000FFFF0000FFFF; + x3 &= (uint64_t)0x0000FFFF0000FFFF; + x0 |= (x0 << 8); + x1 |= (x1 << 8); + x2 |= (x2 << 8); + x3 |= (x3 << 8); + x0 &= (uint64_t)0x00FF00FF00FF00FF; + x1 &= (uint64_t)0x00FF00FF00FF00FF; + x2 &= (uint64_t)0x00FF00FF00FF00FF; + x3 &= (uint64_t)0x00FF00FF00FF00FF; + *q0 = x0 | (x2 << 8); + *q1 = x1 | (x3 << 8); +} + +/* see inner.h */ +void +br_aes_ct64_interleave_out(uint32_t *w, uint64_t q0, uint64_t q1) +{ + uint64_t x0, x1, x2, x3; + + x0 = q0 & (uint64_t)0x00FF00FF00FF00FF; + x1 = q1 & (uint64_t)0x00FF00FF00FF00FF; + x2 = (q0 >> 8) & (uint64_t)0x00FF00FF00FF00FF; + x3 = (q1 >> 8) & (uint64_t)0x00FF00FF00FF00FF; + x0 |= (x0 >> 8); + x1 |= (x1 >> 8); + x2 |= (x2 >> 8); + x3 |= (x3 >> 8); + x0 &= (uint64_t)0x0000FFFF0000FFFF; + x1 &= (uint64_t)0x0000FFFF0000FFFF; + x2 &= (uint64_t)0x0000FFFF0000FFFF; + x3 &= (uint64_t)0x0000FFFF0000FFFF; + w[0] = (uint32_t)x0 | (uint32_t)(x0 >> 16); + w[1] = (uint32_t)x1 | (uint32_t)(x1 >> 16); + w[2] = (uint32_t)x2 | (uint32_t)(x2 >> 16); + w[3] = (uint32_t)x3 | (uint32_t)(x3 >> 16); +} + +static const unsigned char Rcon[] = { + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36 +}; + +static uint32_t +sub_word(uint32_t x) +{ + uint64_t q[8]; + + memset(q, 0, sizeof q); + q[0] = x; + br_aes_ct64_ortho(q); + br_aes_ct64_bitslice_Sbox(q); + br_aes_ct64_ortho(q); + return (uint32_t)q[0]; +} + +/* see inner.h */ +unsigned +br_aes_ct64_keysched(uint64_t *comp_skey, const void *key, size_t key_len) +{ + unsigned num_rounds; + int i, j, k, nk, nkf; + uint32_t tmp; + uint32_t skey[60]; + + switch (key_len) { + case 16: + num_rounds = 10; + break; + case 24: + num_rounds = 12; + break; + case 32: + num_rounds = 14; + break; + default: + /* abort(); */ + return 0; + } + nk = (int)(key_len >> 2); + nkf = (int)((num_rounds + 1) << 2); + br_range_dec32le(skey, (key_len >> 2), key); + tmp = skey[(key_len >> 2) - 1]; + for (i = nk, j = 0, k = 0; i < nkf; i ++) { + if (j == 0) { + tmp = (tmp << 24) | (tmp >> 8); + tmp = sub_word(tmp) ^ Rcon[k]; + } else if (nk > 6 && j == 4) { + tmp = sub_word(tmp); + } + tmp ^= skey[i - nk]; + skey[i] = tmp; + if (++ j == nk) { + j = 0; + k ++; + } + } + + for (i = 0, j = 0; i < nkf; i += 4, j += 2) { + uint64_t q[8]; + + br_aes_ct64_interleave_in(&q[0], &q[4], skey + i); + q[1] = q[0]; + q[2] = q[0]; + q[3] = q[0]; + q[5] = q[4]; + q[6] = q[4]; + q[7] = q[4]; + br_aes_ct64_ortho(q); + comp_skey[j + 0] = + (q[0] & (uint64_t)0x1111111111111111) + | (q[1] & (uint64_t)0x2222222222222222) + | (q[2] & (uint64_t)0x4444444444444444) + | (q[3] & (uint64_t)0x8888888888888888); + comp_skey[j + 1] = + (q[4] & (uint64_t)0x1111111111111111) + | (q[5] & (uint64_t)0x2222222222222222) + | (q[6] & (uint64_t)0x4444444444444444) + | (q[7] & (uint64_t)0x8888888888888888); + } + return num_rounds; +} + +/* see inner.h */ +void +br_aes_ct64_skey_expand(uint64_t *skey, + unsigned num_rounds, const uint64_t *comp_skey) +{ + unsigned u, v, n; + + n = (num_rounds + 1) << 1; + for (u = 0, v = 0; u < n; u ++, v += 4) { + uint64_t x0, x1, x2, x3; + + x0 = x1 = x2 = x3 = comp_skey[u]; + x0 &= (uint64_t)0x1111111111111111; + x1 &= (uint64_t)0x2222222222222222; + x2 &= (uint64_t)0x4444444444444444; + x3 &= (uint64_t)0x8888888888888888; + x1 >>= 1; + x2 >>= 2; + x3 >>= 3; + skey[v + 0] = (x0 << 4) - x0; + skey[v + 1] = (x1 << 4) - x1; + skey[v + 2] = (x2 << 4) - x2; + skey[v + 3] = (x3 << 4) - x3; + } +} diff --git a/lib/bearssl-esp8266/src/symcipher/aes_ct64_cbcdec.c b/lib/bearssl-esp8266/src/symcipher/aes_ct64_cbcdec.c new file mode 100644 index 000000000..091a98c3b --- /dev/null +++ b/lib/bearssl-esp8266/src/symcipher/aes_ct64_cbcdec.c @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_block.h */ +void +br_aes_ct64_cbcdec_init(br_aes_ct64_cbcdec_keys *ctx, + const void *key, size_t len) +{ + ctx->vtable = &br_aes_ct64_cbcdec_vtable; + ctx->num_rounds = br_aes_ct64_keysched(ctx->skey, key, len); +} + +/* see bearssl_block.h */ +void +br_aes_ct64_cbcdec_run(const br_aes_ct64_cbcdec_keys *ctx, + void *iv, void *data, size_t len) +{ + unsigned char *buf; + uint64_t sk_exp[120]; + uint32_t ivw[4]; + + br_aes_ct64_skey_expand(sk_exp, ctx->num_rounds, ctx->skey); + br_range_dec32le(ivw, 4, iv); + buf = data; + while (len > 0) { + uint64_t q[8]; + uint32_t w1[16], w2[16]; + int i; + + if (len >= 64) { + br_range_dec32le(w1, 16, buf); + } else { + br_range_dec32le(w1, len >> 2, buf); + } + for (i = 0; i < 4; i ++) { + br_aes_ct64_interleave_in( + &q[i], &q[i + 4], w1 + (i << 2)); + } + br_aes_ct64_ortho(q); + br_aes_ct64_bitslice_decrypt(ctx->num_rounds, sk_exp, q); + br_aes_ct64_ortho(q); + for (i = 0; i < 4; i ++) { + br_aes_ct64_interleave_out( + w2 + (i << 2), q[i], q[i + 4]); + } + for (i = 0; i < 4; i ++) { + w2[i] ^= ivw[i]; + } + if (len >= 64) { + for (i = 4; i < 16; i ++) { + w2[i] ^= w1[i - 4]; + } + memcpy(ivw, w1 + 12, sizeof ivw); + br_range_enc32le(buf, w2, 16); + } else { + int j; + + j = (int)(len >> 2); + for (i = 4; i < j; i ++) { + w2[i] ^= w1[i - 4]; + } + memcpy(ivw, w1 + j - 4, sizeof ivw); + br_range_enc32le(buf, w2, j); + break; + } + buf += 64; + len -= 64; + } + br_range_enc32le(iv, ivw, 4); +} + +/* see bearssl_block.h */ +const br_block_cbcdec_class br_aes_ct64_cbcdec_vtable PROGMEM = { + sizeof(br_aes_ct64_cbcdec_keys), + 16, + 4, + (void (*)(const br_block_cbcdec_class **, const void *, size_t)) + &br_aes_ct64_cbcdec_init, + (void (*)(const br_block_cbcdec_class *const *, void *, void *, size_t)) + &br_aes_ct64_cbcdec_run +}; diff --git a/lib/bearssl-esp8266/src/symcipher/aes_ct64_cbcenc.c b/lib/bearssl-esp8266/src/symcipher/aes_ct64_cbcenc.c new file mode 100644 index 000000000..3ab6e3ea9 --- /dev/null +++ b/lib/bearssl-esp8266/src/symcipher/aes_ct64_cbcenc.c @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_block.h */ +void +br_aes_ct64_cbcenc_init(br_aes_ct64_cbcenc_keys *ctx, + const void *key, size_t len) +{ + ctx->vtable = &br_aes_ct64_cbcenc_vtable; + ctx->num_rounds = br_aes_ct64_keysched(ctx->skey, key, len); +} + +/* see bearssl_block.h */ +void +br_aes_ct64_cbcenc_run(const br_aes_ct64_cbcenc_keys *ctx, + void *iv, void *data, size_t len) +{ + unsigned char *buf; + uint64_t sk_exp[120]; + uint32_t ivw[4]; + + br_aes_ct64_skey_expand(sk_exp, ctx->num_rounds, ctx->skey); + br_range_dec32le(ivw, 4, iv); + buf = data; + while (len > 0) { + uint32_t w[4]; + uint64_t q[8]; + + w[0] = ivw[0] ^ br_dec32le(buf); + w[1] = ivw[1] ^ br_dec32le(buf + 4); + w[2] = ivw[2] ^ br_dec32le(buf + 8); + w[3] = ivw[3] ^ br_dec32le(buf + 12); + br_aes_ct64_interleave_in(&q[0], &q[4], w); + br_aes_ct64_ortho(q); + br_aes_ct64_bitslice_encrypt(ctx->num_rounds, sk_exp, q); + br_aes_ct64_ortho(q); + br_aes_ct64_interleave_out(w, q[0], q[4]); + memcpy(ivw, w, sizeof w); + br_enc32le(buf, w[0]); + br_enc32le(buf + 4, w[1]); + br_enc32le(buf + 8, w[2]); + br_enc32le(buf + 12, w[3]); + buf += 16; + len -= 16; + } + br_range_enc32le(iv, ivw, 4); +} + +/* see bearssl_block.h */ +const br_block_cbcenc_class br_aes_ct64_cbcenc_vtable PROGMEM = { + sizeof(br_aes_ct64_cbcenc_keys), + 16, + 4, + (void (*)(const br_block_cbcenc_class **, const void *, size_t)) + &br_aes_ct64_cbcenc_init, + (void (*)(const br_block_cbcenc_class *const *, void *, void *, size_t)) + &br_aes_ct64_cbcenc_run +}; diff --git a/lib/bearssl-esp8266/src/symcipher/aes_ct64_ctr.c b/lib/bearssl-esp8266/src/symcipher/aes_ct64_ctr.c new file mode 100644 index 000000000..9adac85d4 --- /dev/null +++ b/lib/bearssl-esp8266/src/symcipher/aes_ct64_ctr.c @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_block.h */ +void +br_aes_ct64_ctr_init(br_aes_ct64_ctr_keys *ctx, + const void *key, size_t len) +{ + ctx->vtable = &br_aes_ct64_ctr_vtable; + ctx->num_rounds = br_aes_ct64_keysched(ctx->skey, key, len); +} + +static void +xorbuf(void *dst, const void *src, size_t len) +{ + unsigned char *d; + const unsigned char *s; + + d = dst; + s = src; + while (len -- > 0) { + *d ++ ^= *s ++; + } +} + +/* see bearssl_block.h */ +uint32_t +br_aes_ct64_ctr_run(const br_aes_ct64_ctr_keys *ctx, + const void *iv, uint32_t cc, void *data, size_t len) +{ + unsigned char *buf; + uint32_t ivw[16]; + uint64_t sk_exp[120]; + + br_aes_ct64_skey_expand(sk_exp, ctx->num_rounds, ctx->skey); + br_range_dec32le(ivw, 3, iv); + memcpy(ivw + 4, ivw, 3 * sizeof(uint32_t)); + memcpy(ivw + 8, ivw, 3 * sizeof(uint32_t)); + memcpy(ivw + 12, ivw, 3 * sizeof(uint32_t)); + buf = data; + while (len > 0) { + uint64_t q[8]; + uint32_t w[16]; + unsigned char tmp[64]; + int i; + + /* + * TODO: see if we can save on the first br_aes_ct64_ortho() + * call, since iv0/iv1/iv2 are constant for the whole run. + */ + memcpy(w, ivw, sizeof ivw); + w[3] = br_swap32(cc); + w[7] = br_swap32(cc + 1); + w[11] = br_swap32(cc + 2); + w[15] = br_swap32(cc + 3); + for (i = 0; i < 4; i ++) { + br_aes_ct64_interleave_in( + &q[i], &q[i + 4], w + (i << 2)); + } + br_aes_ct64_ortho(q); + br_aes_ct64_bitslice_encrypt(ctx->num_rounds, sk_exp, q); + br_aes_ct64_ortho(q); + for (i = 0; i < 4; i ++) { + br_aes_ct64_interleave_out( + w + (i << 2), q[i], q[i + 4]); + } + br_range_enc32le(tmp, w, 16); + if (len <= 64) { + xorbuf(buf, tmp, len); + cc += (uint32_t)len >> 4; + break; + } + xorbuf(buf, tmp, 64); + buf += 64; + len -= 64; + cc += 4; + } + return cc; +} + +/* see bearssl_block.h */ +const br_block_ctr_class br_aes_ct64_ctr_vtable PROGMEM = { + sizeof(br_aes_ct64_ctr_keys), + 16, + 4, + (void (*)(const br_block_ctr_class **, const void *, size_t)) + &br_aes_ct64_ctr_init, + (uint32_t (*)(const br_block_ctr_class *const *, + const void *, uint32_t, void *, size_t)) + &br_aes_ct64_ctr_run +}; diff --git a/lib/bearssl-esp8266/src/symcipher/aes_ct64_ctrcbc.c b/lib/bearssl-esp8266/src/symcipher/aes_ct64_ctrcbc.c new file mode 100644 index 000000000..b5e52e420 --- /dev/null +++ b/lib/bearssl-esp8266/src/symcipher/aes_ct64_ctrcbc.c @@ -0,0 +1,433 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_block.h */ +void +br_aes_ct64_ctrcbc_init(br_aes_ct64_ctrcbc_keys *ctx, + const void *key, size_t len) +{ + ctx->vtable = &br_aes_ct64_ctrcbc_vtable; + ctx->num_rounds = br_aes_ct64_keysched(ctx->skey, key, len); +} + +static void +xorbuf(void *dst, const void *src, size_t len) +{ + unsigned char *d; + const unsigned char *s; + + d = dst; + s = src; + while (len -- > 0) { + *d ++ ^= *s ++; + } +} + +/* see bearssl_block.h */ +void +br_aes_ct64_ctrcbc_ctr(const br_aes_ct64_ctrcbc_keys *ctx, + void *ctr, void *data, size_t len) +{ + unsigned char *buf; + unsigned char *ivbuf; + uint32_t iv0, iv1, iv2, iv3; + uint64_t sk_exp[120]; + + br_aes_ct64_skey_expand(sk_exp, ctx->num_rounds, ctx->skey); + + /* + * We keep the counter as four 32-bit values, with big-endian + * convention, because that's what is expected for purposes of + * incrementing the counter value. + */ + ivbuf = ctr; + iv0 = br_dec32be(ivbuf + 0); + iv1 = br_dec32be(ivbuf + 4); + iv2 = br_dec32be(ivbuf + 8); + iv3 = br_dec32be(ivbuf + 12); + + buf = data; + while (len > 0) { + uint64_t q[8]; + uint32_t w[16]; + unsigned char tmp[64]; + int i, j; + + /* + * The bitslice implementation expects values in + * little-endian convention, so we have to byteswap them. + */ + j = (len >= 64) ? 16 : (int)(len >> 2); + for (i = 0; i < j; i += 4) { + uint32_t carry; + + w[i + 0] = br_swap32(iv0); + w[i + 1] = br_swap32(iv1); + w[i + 2] = br_swap32(iv2); + w[i + 3] = br_swap32(iv3); + iv3 ++; + carry = ~(iv3 | -iv3) >> 31; + iv2 += carry; + carry &= -(~(iv2 | -iv2) >> 31); + iv1 += carry; + carry &= -(~(iv1 | -iv1) >> 31); + iv0 += carry; + } + memset(w + i, 0, (16 - i) * sizeof(uint32_t)); + + for (i = 0; i < 4; i ++) { + br_aes_ct64_interleave_in( + &q[i], &q[i + 4], w + (i << 2)); + } + br_aes_ct64_ortho(q); + br_aes_ct64_bitslice_encrypt(ctx->num_rounds, sk_exp, q); + br_aes_ct64_ortho(q); + for (i = 0; i < 4; i ++) { + br_aes_ct64_interleave_out( + w + (i << 2), q[i], q[i + 4]); + } + + br_range_enc32le(tmp, w, 16); + if (len <= 64) { + xorbuf(buf, tmp, len); + break; + } + xorbuf(buf, tmp, 64); + buf += 64; + len -= 64; + } + br_enc32be(ivbuf + 0, iv0); + br_enc32be(ivbuf + 4, iv1); + br_enc32be(ivbuf + 8, iv2); + br_enc32be(ivbuf + 12, iv3); +} + +/* see bearssl_block.h */ +void +br_aes_ct64_ctrcbc_mac(const br_aes_ct64_ctrcbc_keys *ctx, + void *cbcmac, const void *data, size_t len) +{ + const unsigned char *buf; + uint32_t cm0, cm1, cm2, cm3; + uint64_t q[8]; + uint64_t sk_exp[120]; + + br_aes_ct64_skey_expand(sk_exp, ctx->num_rounds, ctx->skey); + + cm0 = br_dec32le((unsigned char *)cbcmac + 0); + cm1 = br_dec32le((unsigned char *)cbcmac + 4); + cm2 = br_dec32le((unsigned char *)cbcmac + 8); + cm3 = br_dec32le((unsigned char *)cbcmac + 12); + + buf = data; + memset(q, 0, sizeof q); + while (len > 0) { + uint32_t w[4]; + + w[0] = cm0 ^ br_dec32le(buf + 0); + w[1] = cm1 ^ br_dec32le(buf + 4); + w[2] = cm2 ^ br_dec32le(buf + 8); + w[3] = cm3 ^ br_dec32le(buf + 12); + + br_aes_ct64_interleave_in(&q[0], &q[4], w); + br_aes_ct64_ortho(q); + br_aes_ct64_bitslice_encrypt(ctx->num_rounds, sk_exp, q); + br_aes_ct64_ortho(q); + br_aes_ct64_interleave_out(w, q[0], q[4]); + + cm0 = w[0]; + cm1 = w[1]; + cm2 = w[2]; + cm3 = w[3]; + buf += 16; + len -= 16; + } + + br_enc32le((unsigned char *)cbcmac + 0, cm0); + br_enc32le((unsigned char *)cbcmac + 4, cm1); + br_enc32le((unsigned char *)cbcmac + 8, cm2); + br_enc32le((unsigned char *)cbcmac + 12, cm3); +} + +/* see bearssl_block.h */ +void +br_aes_ct64_ctrcbc_encrypt(const br_aes_ct64_ctrcbc_keys *ctx, + void *ctr, void *cbcmac, void *data, size_t len) +{ + /* + * When encrypting, the CBC-MAC processing must be lagging by + * one block, since it operates on the encrypted values, so + * it must wait for that encryption to complete. + */ + + unsigned char *buf; + unsigned char *ivbuf; + uint32_t iv0, iv1, iv2, iv3; + uint32_t cm0, cm1, cm2, cm3; + uint64_t sk_exp[120]; + uint64_t q[8]; + int first_iter; + + br_aes_ct64_skey_expand(sk_exp, ctx->num_rounds, ctx->skey); + + /* + * We keep the counter as four 32-bit values, with big-endian + * convention, because that's what is expected for purposes of + * incrementing the counter value. + */ + ivbuf = ctr; + iv0 = br_dec32be(ivbuf + 0); + iv1 = br_dec32be(ivbuf + 4); + iv2 = br_dec32be(ivbuf + 8); + iv3 = br_dec32be(ivbuf + 12); + + /* + * The current CBC-MAC value is kept in little-endian convention. + */ + cm0 = br_dec32le((unsigned char *)cbcmac + 0); + cm1 = br_dec32le((unsigned char *)cbcmac + 4); + cm2 = br_dec32le((unsigned char *)cbcmac + 8); + cm3 = br_dec32le((unsigned char *)cbcmac + 12); + + buf = data; + first_iter = 1; + memset(q, 0, sizeof q); + while (len > 0) { + uint32_t w[8], carry; + + /* + * The bitslice implementation expects values in + * little-endian convention, so we have to byteswap them. + */ + w[0] = br_swap32(iv0); + w[1] = br_swap32(iv1); + w[2] = br_swap32(iv2); + w[3] = br_swap32(iv3); + iv3 ++; + carry = ~(iv3 | -iv3) >> 31; + iv2 += carry; + carry &= -(~(iv2 | -iv2) >> 31); + iv1 += carry; + carry &= -(~(iv1 | -iv1) >> 31); + iv0 += carry; + + /* + * The block for CBC-MAC. + */ + w[4] = cm0; + w[5] = cm1; + w[6] = cm2; + w[7] = cm3; + + br_aes_ct64_interleave_in(&q[0], &q[4], w); + br_aes_ct64_interleave_in(&q[1], &q[5], w + 4); + br_aes_ct64_ortho(q); + br_aes_ct64_bitslice_encrypt(ctx->num_rounds, sk_exp, q); + br_aes_ct64_ortho(q); + br_aes_ct64_interleave_out(w, q[0], q[4]); + br_aes_ct64_interleave_out(w + 4, q[1], q[5]); + + /* + * We do the XOR with the plaintext in 32-bit registers, + * so that the value are available for CBC-MAC processing + * as well. + */ + w[0] ^= br_dec32le(buf + 0); + w[1] ^= br_dec32le(buf + 4); + w[2] ^= br_dec32le(buf + 8); + w[3] ^= br_dec32le(buf + 12); + br_enc32le(buf + 0, w[0]); + br_enc32le(buf + 4, w[1]); + br_enc32le(buf + 8, w[2]); + br_enc32le(buf + 12, w[3]); + + buf += 16; + len -= 16; + + /* + * We set the cm* values to the block to encrypt in the + * next iteration. + */ + if (first_iter) { + first_iter = 0; + cm0 ^= w[0]; + cm1 ^= w[1]; + cm2 ^= w[2]; + cm3 ^= w[3]; + } else { + cm0 = w[0] ^ w[4]; + cm1 = w[1] ^ w[5]; + cm2 = w[2] ^ w[6]; + cm3 = w[3] ^ w[7]; + } + + /* + * If this was the last iteration, then compute the + * extra block encryption to complete CBC-MAC. + */ + if (len == 0) { + w[0] = cm0; + w[1] = cm1; + w[2] = cm2; + w[3] = cm3; + br_aes_ct64_interleave_in(&q[0], &q[4], w); + br_aes_ct64_ortho(q); + br_aes_ct64_bitslice_encrypt( + ctx->num_rounds, sk_exp, q); + br_aes_ct64_ortho(q); + br_aes_ct64_interleave_out(w, q[0], q[4]); + cm0 = w[0]; + cm1 = w[1]; + cm2 = w[2]; + cm3 = w[3]; + break; + } + } + + br_enc32be(ivbuf + 0, iv0); + br_enc32be(ivbuf + 4, iv1); + br_enc32be(ivbuf + 8, iv2); + br_enc32be(ivbuf + 12, iv3); + br_enc32le((unsigned char *)cbcmac + 0, cm0); + br_enc32le((unsigned char *)cbcmac + 4, cm1); + br_enc32le((unsigned char *)cbcmac + 8, cm2); + br_enc32le((unsigned char *)cbcmac + 12, cm3); +} + +/* see bearssl_block.h */ +void +br_aes_ct64_ctrcbc_decrypt(const br_aes_ct64_ctrcbc_keys *ctx, + void *ctr, void *cbcmac, void *data, size_t len) +{ + unsigned char *buf; + unsigned char *ivbuf; + uint32_t iv0, iv1, iv2, iv3; + uint32_t cm0, cm1, cm2, cm3; + uint64_t sk_exp[120]; + uint64_t q[8]; + + br_aes_ct64_skey_expand(sk_exp, ctx->num_rounds, ctx->skey); + + /* + * We keep the counter as four 32-bit values, with big-endian + * convention, because that's what is expected for purposes of + * incrementing the counter value. + */ + ivbuf = ctr; + iv0 = br_dec32be(ivbuf + 0); + iv1 = br_dec32be(ivbuf + 4); + iv2 = br_dec32be(ivbuf + 8); + iv3 = br_dec32be(ivbuf + 12); + + /* + * The current CBC-MAC value is kept in little-endian convention. + */ + cm0 = br_dec32le((unsigned char *)cbcmac + 0); + cm1 = br_dec32le((unsigned char *)cbcmac + 4); + cm2 = br_dec32le((unsigned char *)cbcmac + 8); + cm3 = br_dec32le((unsigned char *)cbcmac + 12); + + buf = data; + memset(q, 0, sizeof q); + while (len > 0) { + uint32_t w[8], carry; + unsigned char tmp[16]; + + /* + * The bitslice implementation expects values in + * little-endian convention, so we have to byteswap them. + */ + w[0] = br_swap32(iv0); + w[1] = br_swap32(iv1); + w[2] = br_swap32(iv2); + w[3] = br_swap32(iv3); + iv3 ++; + carry = ~(iv3 | -iv3) >> 31; + iv2 += carry; + carry &= -(~(iv2 | -iv2) >> 31); + iv1 += carry; + carry &= -(~(iv1 | -iv1) >> 31); + iv0 += carry; + + /* + * The block for CBC-MAC. + */ + w[4] = cm0 ^ br_dec32le(buf + 0); + w[5] = cm1 ^ br_dec32le(buf + 4); + w[6] = cm2 ^ br_dec32le(buf + 8); + w[7] = cm3 ^ br_dec32le(buf + 12); + + br_aes_ct64_interleave_in(&q[0], &q[4], w); + br_aes_ct64_interleave_in(&q[1], &q[5], w + 4); + br_aes_ct64_ortho(q); + br_aes_ct64_bitslice_encrypt(ctx->num_rounds, sk_exp, q); + br_aes_ct64_ortho(q); + br_aes_ct64_interleave_out(w, q[0], q[4]); + br_aes_ct64_interleave_out(w + 4, q[1], q[5]); + + br_enc32le(tmp + 0, w[0]); + br_enc32le(tmp + 4, w[1]); + br_enc32le(tmp + 8, w[2]); + br_enc32le(tmp + 12, w[3]); + xorbuf(buf, tmp, 16); + cm0 = w[4]; + cm1 = w[5]; + cm2 = w[6]; + cm3 = w[7]; + buf += 16; + len -= 16; + } + + br_enc32be(ivbuf + 0, iv0); + br_enc32be(ivbuf + 4, iv1); + br_enc32be(ivbuf + 8, iv2); + br_enc32be(ivbuf + 12, iv3); + br_enc32le((unsigned char *)cbcmac + 0, cm0); + br_enc32le((unsigned char *)cbcmac + 4, cm1); + br_enc32le((unsigned char *)cbcmac + 8, cm2); + br_enc32le((unsigned char *)cbcmac + 12, cm3); +} + +/* see bearssl_block.h */ +const br_block_ctrcbc_class br_aes_ct64_ctrcbc_vtable PROGMEM = { + sizeof(br_aes_ct64_ctrcbc_keys), + 16, + 4, + (void (*)(const br_block_ctrcbc_class **, const void *, size_t)) + &br_aes_ct64_ctrcbc_init, + (void (*)(const br_block_ctrcbc_class *const *, + void *, void *, void *, size_t)) + &br_aes_ct64_ctrcbc_encrypt, + (void (*)(const br_block_ctrcbc_class *const *, + void *, void *, void *, size_t)) + &br_aes_ct64_ctrcbc_decrypt, + (void (*)(const br_block_ctrcbc_class *const *, + void *, void *, size_t)) + &br_aes_ct64_ctrcbc_ctr, + (void (*)(const br_block_ctrcbc_class *const *, + void *, const void *, size_t)) + &br_aes_ct64_ctrcbc_mac +}; diff --git a/lib/bearssl-esp8266/src/symcipher/aes_ct64_dec.c b/lib/bearssl-esp8266/src/symcipher/aes_ct64_dec.c new file mode 100644 index 000000000..687c90a8c --- /dev/null +++ b/lib/bearssl-esp8266/src/symcipher/aes_ct64_dec.c @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +void +br_aes_ct64_bitslice_invSbox(uint64_t *q) +{ + /* + * See br_aes_ct_bitslice_invSbox(). This is the natural extension + * to 64-bit registers. + */ + uint64_t q0, q1, q2, q3, q4, q5, q6, q7; + + q0 = ~q[0]; + q1 = ~q[1]; + q2 = q[2]; + q3 = q[3]; + q4 = q[4]; + q5 = ~q[5]; + q6 = ~q[6]; + q7 = q[7]; + q[7] = q1 ^ q4 ^ q6; + q[6] = q0 ^ q3 ^ q5; + q[5] = q7 ^ q2 ^ q4; + q[4] = q6 ^ q1 ^ q3; + q[3] = q5 ^ q0 ^ q2; + q[2] = q4 ^ q7 ^ q1; + q[1] = q3 ^ q6 ^ q0; + q[0] = q2 ^ q5 ^ q7; + + br_aes_ct64_bitslice_Sbox(q); + + q0 = ~q[0]; + q1 = ~q[1]; + q2 = q[2]; + q3 = q[3]; + q4 = q[4]; + q5 = ~q[5]; + q6 = ~q[6]; + q7 = q[7]; + q[7] = q1 ^ q4 ^ q6; + q[6] = q0 ^ q3 ^ q5; + q[5] = q7 ^ q2 ^ q4; + q[4] = q6 ^ q1 ^ q3; + q[3] = q5 ^ q0 ^ q2; + q[2] = q4 ^ q7 ^ q1; + q[1] = q3 ^ q6 ^ q0; + q[0] = q2 ^ q5 ^ q7; +} + +static void +add_round_key(uint64_t *q, const uint64_t *sk) +{ + int i; + + for (i = 0; i < 8; i ++) { + q[i] ^= sk[i]; + } +} + +static void +inv_shift_rows(uint64_t *q) +{ + int i; + + for (i = 0; i < 8; i ++) { + uint64_t x; + + x = q[i]; + q[i] = (x & (uint64_t)0x000000000000FFFF) + | ((x & (uint64_t)0x000000000FFF0000) << 4) + | ((x & (uint64_t)0x00000000F0000000) >> 12) + | ((x & (uint64_t)0x000000FF00000000) << 8) + | ((x & (uint64_t)0x0000FF0000000000) >> 8) + | ((x & (uint64_t)0x000F000000000000) << 12) + | ((x & (uint64_t)0xFFF0000000000000) >> 4); + } +} + +static inline uint64_t +rotr32(uint64_t x) +{ + return (x << 32) | (x >> 32); +} + +static void +inv_mix_columns(uint64_t *q) +{ + uint64_t q0, q1, q2, q3, q4, q5, q6, q7; + uint64_t r0, r1, r2, r3, r4, r5, r6, r7; + + q0 = q[0]; + q1 = q[1]; + q2 = q[2]; + q3 = q[3]; + q4 = q[4]; + q5 = q[5]; + q6 = q[6]; + q7 = q[7]; + r0 = (q0 >> 16) | (q0 << 48); + r1 = (q1 >> 16) | (q1 << 48); + r2 = (q2 >> 16) | (q2 << 48); + r3 = (q3 >> 16) | (q3 << 48); + r4 = (q4 >> 16) | (q4 << 48); + r5 = (q5 >> 16) | (q5 << 48); + r6 = (q6 >> 16) | (q6 << 48); + r7 = (q7 >> 16) | (q7 << 48); + + q[0] = q5 ^ q6 ^ q7 ^ r0 ^ r5 ^ r7 ^ rotr32(q0 ^ q5 ^ q6 ^ r0 ^ r5); + q[1] = q0 ^ q5 ^ r0 ^ r1 ^ r5 ^ r6 ^ r7 ^ rotr32(q1 ^ q5 ^ q7 ^ r1 ^ r5 ^ r6); + q[2] = q0 ^ q1 ^ q6 ^ r1 ^ r2 ^ r6 ^ r7 ^ rotr32(q0 ^ q2 ^ q6 ^ r2 ^ r6 ^ r7); + q[3] = q0 ^ q1 ^ q2 ^ q5 ^ q6 ^ r0 ^ r2 ^ r3 ^ r5 ^ rotr32(q0 ^ q1 ^ q3 ^ q5 ^ q6 ^ q7 ^ r0 ^ r3 ^ r5 ^ r7); + q[4] = q1 ^ q2 ^ q3 ^ q5 ^ r1 ^ r3 ^ r4 ^ r5 ^ r6 ^ r7 ^ rotr32(q1 ^ q2 ^ q4 ^ q5 ^ q7 ^ r1 ^ r4 ^ r5 ^ r6); + q[5] = q2 ^ q3 ^ q4 ^ q6 ^ r2 ^ r4 ^ r5 ^ r6 ^ r7 ^ rotr32(q2 ^ q3 ^ q5 ^ q6 ^ r2 ^ r5 ^ r6 ^ r7); + q[6] = q3 ^ q4 ^ q5 ^ q7 ^ r3 ^ r5 ^ r6 ^ r7 ^ rotr32(q3 ^ q4 ^ q6 ^ q7 ^ r3 ^ r6 ^ r7); + q[7] = q4 ^ q5 ^ q6 ^ r4 ^ r6 ^ r7 ^ rotr32(q4 ^ q5 ^ q7 ^ r4 ^ r7); +} + +/* see inner.h */ +void +br_aes_ct64_bitslice_decrypt(unsigned num_rounds, + const uint64_t *skey, uint64_t *q) +{ + unsigned u; + + add_round_key(q, skey + (num_rounds << 3)); + for (u = num_rounds - 1; u > 0; u --) { + inv_shift_rows(q); + br_aes_ct64_bitslice_invSbox(q); + add_round_key(q, skey + (u << 3)); + inv_mix_columns(q); + } + inv_shift_rows(q); + br_aes_ct64_bitslice_invSbox(q); + add_round_key(q, skey); +} diff --git a/lib/bearssl-esp8266/src/symcipher/aes_ct64_enc.c b/lib/bearssl-esp8266/src/symcipher/aes_ct64_enc.c new file mode 100644 index 000000000..705717601 --- /dev/null +++ b/lib/bearssl-esp8266/src/symcipher/aes_ct64_enc.c @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +static inline void +add_round_key(uint64_t *q, const uint64_t *sk) +{ + q[0] ^= sk[0]; + q[1] ^= sk[1]; + q[2] ^= sk[2]; + q[3] ^= sk[3]; + q[4] ^= sk[4]; + q[5] ^= sk[5]; + q[6] ^= sk[6]; + q[7] ^= sk[7]; +} + +static inline void +shift_rows(uint64_t *q) +{ + int i; + + for (i = 0; i < 8; i ++) { + uint64_t x; + + x = q[i]; + q[i] = (x & (uint64_t)0x000000000000FFFF) + | ((x & (uint64_t)0x00000000FFF00000) >> 4) + | ((x & (uint64_t)0x00000000000F0000) << 12) + | ((x & (uint64_t)0x0000FF0000000000) >> 8) + | ((x & (uint64_t)0x000000FF00000000) << 8) + | ((x & (uint64_t)0xF000000000000000) >> 12) + | ((x & (uint64_t)0x0FFF000000000000) << 4); + } +} + +static inline uint64_t +rotr32(uint64_t x) +{ + return (x << 32) | (x >> 32); +} + +static inline void +mix_columns(uint64_t *q) +{ + uint64_t q0, q1, q2, q3, q4, q5, q6, q7; + uint64_t r0, r1, r2, r3, r4, r5, r6, r7; + + q0 = q[0]; + q1 = q[1]; + q2 = q[2]; + q3 = q[3]; + q4 = q[4]; + q5 = q[5]; + q6 = q[6]; + q7 = q[7]; + r0 = (q0 >> 16) | (q0 << 48); + r1 = (q1 >> 16) | (q1 << 48); + r2 = (q2 >> 16) | (q2 << 48); + r3 = (q3 >> 16) | (q3 << 48); + r4 = (q4 >> 16) | (q4 << 48); + r5 = (q5 >> 16) | (q5 << 48); + r6 = (q6 >> 16) | (q6 << 48); + r7 = (q7 >> 16) | (q7 << 48); + + q[0] = q7 ^ r7 ^ r0 ^ rotr32(q0 ^ r0); + q[1] = q0 ^ r0 ^ q7 ^ r7 ^ r1 ^ rotr32(q1 ^ r1); + q[2] = q1 ^ r1 ^ r2 ^ rotr32(q2 ^ r2); + q[3] = q2 ^ r2 ^ q7 ^ r7 ^ r3 ^ rotr32(q3 ^ r3); + q[4] = q3 ^ r3 ^ q7 ^ r7 ^ r4 ^ rotr32(q4 ^ r4); + q[5] = q4 ^ r4 ^ r5 ^ rotr32(q5 ^ r5); + q[6] = q5 ^ r5 ^ r6 ^ rotr32(q6 ^ r6); + q[7] = q6 ^ r6 ^ r7 ^ rotr32(q7 ^ r7); +} + +/* see inner.h */ +void +br_aes_ct64_bitslice_encrypt(unsigned num_rounds, + const uint64_t *skey, uint64_t *q) +{ + unsigned u; + + add_round_key(q, skey); + for (u = 1; u < num_rounds; u ++) { + br_aes_ct64_bitslice_Sbox(q); + shift_rows(q); + mix_columns(q); + add_round_key(q, skey + (u << 3)); + } + br_aes_ct64_bitslice_Sbox(q); + shift_rows(q); + add_round_key(q, skey + (num_rounds << 3)); +} diff --git a/lib/bearssl-esp8266/src/symcipher/aes_ct_cbcdec.c b/lib/bearssl-esp8266/src/symcipher/aes_ct_cbcdec.c new file mode 100644 index 000000000..7bc258ae6 --- /dev/null +++ b/lib/bearssl-esp8266/src/symcipher/aes_ct_cbcdec.c @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_block.h */ +void +br_aes_ct_cbcdec_init(br_aes_ct_cbcdec_keys *ctx, + const void *key, size_t len) +{ + ctx->vtable = &br_aes_ct_cbcdec_vtable; + ctx->num_rounds = br_aes_ct_keysched(ctx->skey, key, len); +} + +/* see bearssl_block.h */ +void +br_aes_ct_cbcdec_run(const br_aes_ct_cbcdec_keys *ctx, + void *iv, void *data, size_t len) +{ + unsigned char *buf, *ivbuf; + uint32_t iv0, iv1, iv2, iv3; + uint32_t sk_exp[120]; + + br_aes_ct_skey_expand(sk_exp, ctx->num_rounds, ctx->skey); + ivbuf = iv; + iv0 = br_dec32le(ivbuf); + iv1 = br_dec32le(ivbuf + 4); + iv2 = br_dec32le(ivbuf + 8); + iv3 = br_dec32le(ivbuf + 12); + buf = data; + while (len > 0) { + uint32_t q[8], sq[8]; + + q[0] = br_dec32le(buf); + q[2] = br_dec32le(buf + 4); + q[4] = br_dec32le(buf + 8); + q[6] = br_dec32le(buf + 12); + if (len >= 32) { + q[1] = br_dec32le(buf + 16); + q[3] = br_dec32le(buf + 20); + q[5] = br_dec32le(buf + 24); + q[7] = br_dec32le(buf + 28); + } else { + q[1] = 0; + q[3] = 0; + q[5] = 0; + q[7] = 0; + } + memcpy(sq, q, sizeof q); + br_aes_ct_ortho(q); + br_aes_ct_bitslice_decrypt(ctx->num_rounds, sk_exp, q); + br_aes_ct_ortho(q); + br_enc32le(buf, q[0] ^ iv0); + br_enc32le(buf + 4, q[2] ^ iv1); + br_enc32le(buf + 8, q[4] ^ iv2); + br_enc32le(buf + 12, q[6] ^ iv3); + if (len < 32) { + iv0 = sq[0]; + iv1 = sq[2]; + iv2 = sq[4]; + iv3 = sq[6]; + break; + } + br_enc32le(buf + 16, q[1] ^ sq[0]); + br_enc32le(buf + 20, q[3] ^ sq[2]); + br_enc32le(buf + 24, q[5] ^ sq[4]); + br_enc32le(buf + 28, q[7] ^ sq[6]); + iv0 = sq[1]; + iv1 = sq[3]; + iv2 = sq[5]; + iv3 = sq[7]; + buf += 32; + len -= 32; + } + br_enc32le(ivbuf, iv0); + br_enc32le(ivbuf + 4, iv1); + br_enc32le(ivbuf + 8, iv2); + br_enc32le(ivbuf + 12, iv3); +} + +/* see bearssl_block.h */ +const br_block_cbcdec_class br_aes_ct_cbcdec_vtable PROGMEM = { + sizeof(br_aes_ct_cbcdec_keys), + 16, + 4, + (void (*)(const br_block_cbcdec_class **, const void *, size_t)) + &br_aes_ct_cbcdec_init, + (void (*)(const br_block_cbcdec_class *const *, void *, void *, size_t)) + &br_aes_ct_cbcdec_run +}; diff --git a/lib/bearssl-esp8266/src/symcipher/aes_ct_cbcenc.c b/lib/bearssl-esp8266/src/symcipher/aes_ct_cbcenc.c new file mode 100644 index 000000000..07fd5725a --- /dev/null +++ b/lib/bearssl-esp8266/src/symcipher/aes_ct_cbcenc.c @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_block.h */ +void +br_aes_ct_cbcenc_init(br_aes_ct_cbcenc_keys *ctx, + const void *key, size_t len) +{ + ctx->vtable = &br_aes_ct_cbcenc_vtable; + ctx->num_rounds = br_aes_ct_keysched(ctx->skey, key, len); +} + +/* see bearssl_block.h */ +void +br_aes_ct_cbcenc_run(const br_aes_ct_cbcenc_keys *ctx, + void *iv, void *data, size_t len) +{ + unsigned char *buf, *ivbuf; + uint32_t q[8]; + uint32_t iv0, iv1, iv2, iv3; + uint32_t sk_exp[120]; + + q[1] = 0; + q[3] = 0; + q[5] = 0; + q[7] = 0; + br_aes_ct_skey_expand(sk_exp, ctx->num_rounds, ctx->skey); + ivbuf = iv; + iv0 = br_dec32le(ivbuf); + iv1 = br_dec32le(ivbuf + 4); + iv2 = br_dec32le(ivbuf + 8); + iv3 = br_dec32le(ivbuf + 12); + buf = data; + while (len > 0) { + q[0] = iv0 ^ br_dec32le(buf); + q[2] = iv1 ^ br_dec32le(buf + 4); + q[4] = iv2 ^ br_dec32le(buf + 8); + q[6] = iv3 ^ br_dec32le(buf + 12); + br_aes_ct_ortho(q); + br_aes_ct_bitslice_encrypt(ctx->num_rounds, sk_exp, q); + br_aes_ct_ortho(q); + iv0 = q[0]; + iv1 = q[2]; + iv2 = q[4]; + iv3 = q[6]; + br_enc32le(buf, iv0); + br_enc32le(buf + 4, iv1); + br_enc32le(buf + 8, iv2); + br_enc32le(buf + 12, iv3); + buf += 16; + len -= 16; + } + br_enc32le(ivbuf, iv0); + br_enc32le(ivbuf + 4, iv1); + br_enc32le(ivbuf + 8, iv2); + br_enc32le(ivbuf + 12, iv3); +} + +/* see bearssl_block.h */ +const br_block_cbcenc_class br_aes_ct_cbcenc_vtable PROGMEM = { + sizeof(br_aes_ct_cbcenc_keys), + 16, + 4, + (void (*)(const br_block_cbcenc_class **, const void *, size_t)) + &br_aes_ct_cbcenc_init, + (void (*)(const br_block_cbcenc_class *const *, void *, void *, size_t)) + &br_aes_ct_cbcenc_run +}; diff --git a/lib/bearssl-esp8266/src/symcipher/aes_ct_ctr.c b/lib/bearssl-esp8266/src/symcipher/aes_ct_ctr.c new file mode 100644 index 000000000..dfa882955 --- /dev/null +++ b/lib/bearssl-esp8266/src/symcipher/aes_ct_ctr.c @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_block.h */ +void +br_aes_ct_ctr_init(br_aes_ct_ctr_keys *ctx, + const void *key, size_t len) +{ + ctx->vtable = &br_aes_ct_ctr_vtable; + ctx->num_rounds = br_aes_ct_keysched(ctx->skey, key, len); +} + +static void +xorbuf(void *dst, const void *src, size_t len) +{ + unsigned char *d; + const unsigned char *s; + + d = dst; + s = src; + while (len -- > 0) { + *d ++ ^= *s ++; + } +} + +/* see bearssl_block.h */ +uint32_t +br_aes_ct_ctr_run(const br_aes_ct_ctr_keys *ctx, + const void *iv, uint32_t cc, void *data, size_t len) +{ + unsigned char *buf; + const unsigned char *ivbuf; + uint32_t iv0, iv1, iv2; + uint32_t sk_exp[120]; + + br_aes_ct_skey_expand(sk_exp, ctx->num_rounds, ctx->skey); + ivbuf = iv; + iv0 = br_dec32le(ivbuf); + iv1 = br_dec32le(ivbuf + 4); + iv2 = br_dec32le(ivbuf + 8); + buf = data; + while (len > 0) { + uint32_t q[8]; + unsigned char tmp[32]; + + /* + * TODO: see if we can save on the first br_aes_ct_ortho() + * call, since iv0/iv1/iv2 are constant for the whole run. + */ + q[0] = q[1] = iv0; + q[2] = q[3] = iv1; + q[4] = q[5] = iv2; + q[6] = br_swap32(cc); + q[7] = br_swap32(cc + 1); + br_aes_ct_ortho(q); + br_aes_ct_bitslice_encrypt(ctx->num_rounds, sk_exp, q); + br_aes_ct_ortho(q); + br_enc32le(tmp, q[0]); + br_enc32le(tmp + 4, q[2]); + br_enc32le(tmp + 8, q[4]); + br_enc32le(tmp + 12, q[6]); + br_enc32le(tmp + 16, q[1]); + br_enc32le(tmp + 20, q[3]); + br_enc32le(tmp + 24, q[5]); + br_enc32le(tmp + 28, q[7]); + + if (len <= 32) { + xorbuf(buf, tmp, len); + cc ++; + if (len > 16) { + cc ++; + } + break; + } + xorbuf(buf, tmp, 32); + buf += 32; + len -= 32; + cc += 2; + } + return cc; +} + +/* see bearssl_block.h */ +const br_block_ctr_class br_aes_ct_ctr_vtable PROGMEM = { + sizeof(br_aes_ct_ctr_keys), + 16, + 4, + (void (*)(const br_block_ctr_class **, const void *, size_t)) + &br_aes_ct_ctr_init, + (uint32_t (*)(const br_block_ctr_class *const *, + const void *, uint32_t, void *, size_t)) + &br_aes_ct_ctr_run +}; diff --git a/lib/bearssl-esp8266/src/symcipher/aes_ct_ctrcbc.c b/lib/bearssl-esp8266/src/symcipher/aes_ct_ctrcbc.c new file mode 100644 index 000000000..ec451c6af --- /dev/null +++ b/lib/bearssl-esp8266/src/symcipher/aes_ct_ctrcbc.c @@ -0,0 +1,422 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_block.h */ +void +br_aes_ct_ctrcbc_init(br_aes_ct_ctrcbc_keys *ctx, + const void *key, size_t len) +{ + ctx->vtable = &br_aes_ct_ctrcbc_vtable; + ctx->num_rounds = br_aes_ct_keysched(ctx->skey, key, len); +} + +static void +xorbuf(void *dst, const void *src, size_t len) +{ + unsigned char *d; + const unsigned char *s; + + d = dst; + s = src; + while (len -- > 0) { + *d ++ ^= *s ++; + } +} + +/* see bearssl_block.h */ +void +br_aes_ct_ctrcbc_ctr(const br_aes_ct_ctrcbc_keys *ctx, + void *ctr, void *data, size_t len) +{ + unsigned char *buf; + unsigned char *ivbuf; + uint32_t iv0, iv1, iv2, iv3; + uint32_t sk_exp[120]; + + br_aes_ct_skey_expand(sk_exp, ctx->num_rounds, ctx->skey); + + /* + * We keep the counter as four 32-bit values, with big-endian + * convention, because that's what is expected for purposes of + * incrementing the counter value. + */ + ivbuf = ctr; + iv0 = br_dec32be(ivbuf + 0); + iv1 = br_dec32be(ivbuf + 4); + iv2 = br_dec32be(ivbuf + 8); + iv3 = br_dec32be(ivbuf + 12); + + buf = data; + while (len > 0) { + uint32_t q[8], carry; + unsigned char tmp[32]; + + /* + * The bitslice implementation expects values in + * little-endian convention, so we have to byteswap them. + */ + q[0] = br_swap32(iv0); + q[2] = br_swap32(iv1); + q[4] = br_swap32(iv2); + q[6] = br_swap32(iv3); + iv3 ++; + carry = ~(iv3 | -iv3) >> 31; + iv2 += carry; + carry &= -(~(iv2 | -iv2) >> 31); + iv1 += carry; + carry &= -(~(iv1 | -iv1) >> 31); + iv0 += carry; + q[1] = br_swap32(iv0); + q[3] = br_swap32(iv1); + q[5] = br_swap32(iv2); + q[7] = br_swap32(iv3); + if (len > 16) { + iv3 ++; + carry = ~(iv3 | -iv3) >> 31; + iv2 += carry; + carry &= -(~(iv2 | -iv2) >> 31); + iv1 += carry; + carry &= -(~(iv1 | -iv1) >> 31); + iv0 += carry; + } + + br_aes_ct_ortho(q); + br_aes_ct_bitslice_encrypt(ctx->num_rounds, sk_exp, q); + br_aes_ct_ortho(q); + + br_enc32le(tmp, q[0]); + br_enc32le(tmp + 4, q[2]); + br_enc32le(tmp + 8, q[4]); + br_enc32le(tmp + 12, q[6]); + br_enc32le(tmp + 16, q[1]); + br_enc32le(tmp + 20, q[3]); + br_enc32le(tmp + 24, q[5]); + br_enc32le(tmp + 28, q[7]); + + if (len <= 32) { + xorbuf(buf, tmp, len); + break; + } + xorbuf(buf, tmp, 32); + buf += 32; + len -= 32; + } + br_enc32be(ivbuf + 0, iv0); + br_enc32be(ivbuf + 4, iv1); + br_enc32be(ivbuf + 8, iv2); + br_enc32be(ivbuf + 12, iv3); +} + +/* see bearssl_block.h */ +void +br_aes_ct_ctrcbc_mac(const br_aes_ct_ctrcbc_keys *ctx, + void *cbcmac, const void *data, size_t len) +{ + const unsigned char *buf; + uint32_t cm0, cm1, cm2, cm3; + uint32_t q[8]; + uint32_t sk_exp[120]; + + br_aes_ct_skey_expand(sk_exp, ctx->num_rounds, ctx->skey); + + buf = data; + cm0 = br_dec32le((unsigned char *)cbcmac + 0); + cm1 = br_dec32le((unsigned char *)cbcmac + 4); + cm2 = br_dec32le((unsigned char *)cbcmac + 8); + cm3 = br_dec32le((unsigned char *)cbcmac + 12); + q[1] = 0; + q[3] = 0; + q[5] = 0; + q[7] = 0; + + while (len > 0) { + q[0] = cm0 ^ br_dec32le(buf + 0); + q[2] = cm1 ^ br_dec32le(buf + 4); + q[4] = cm2 ^ br_dec32le(buf + 8); + q[6] = cm3 ^ br_dec32le(buf + 12); + + br_aes_ct_ortho(q); + br_aes_ct_bitslice_encrypt(ctx->num_rounds, sk_exp, q); + br_aes_ct_ortho(q); + + cm0 = q[0]; + cm1 = q[2]; + cm2 = q[4]; + cm3 = q[6]; + buf += 16; + len -= 16; + } + + br_enc32le((unsigned char *)cbcmac + 0, cm0); + br_enc32le((unsigned char *)cbcmac + 4, cm1); + br_enc32le((unsigned char *)cbcmac + 8, cm2); + br_enc32le((unsigned char *)cbcmac + 12, cm3); +} + +/* see bearssl_block.h */ +void +br_aes_ct_ctrcbc_encrypt(const br_aes_ct_ctrcbc_keys *ctx, + void *ctr, void *cbcmac, void *data, size_t len) +{ + /* + * When encrypting, the CBC-MAC processing must be lagging by + * one block, since it operates on the encrypted values, so + * it must wait for that encryption to complete. + */ + + unsigned char *buf; + unsigned char *ivbuf; + uint32_t iv0, iv1, iv2, iv3; + uint32_t cm0, cm1, cm2, cm3; + uint32_t sk_exp[120]; + int first_iter; + + br_aes_ct_skey_expand(sk_exp, ctx->num_rounds, ctx->skey); + + /* + * We keep the counter as four 32-bit values, with big-endian + * convention, because that's what is expected for purposes of + * incrementing the counter value. + */ + ivbuf = ctr; + iv0 = br_dec32be(ivbuf + 0); + iv1 = br_dec32be(ivbuf + 4); + iv2 = br_dec32be(ivbuf + 8); + iv3 = br_dec32be(ivbuf + 12); + + /* + * The current CBC-MAC value is kept in little-endian convention. + */ + cm0 = br_dec32le((unsigned char *)cbcmac + 0); + cm1 = br_dec32le((unsigned char *)cbcmac + 4); + cm2 = br_dec32le((unsigned char *)cbcmac + 8); + cm3 = br_dec32le((unsigned char *)cbcmac + 12); + + buf = data; + first_iter = 1; + while (len > 0) { + uint32_t q[8], carry; + + /* + * The bitslice implementation expects values in + * little-endian convention, so we have to byteswap them. + */ + q[0] = br_swap32(iv0); + q[2] = br_swap32(iv1); + q[4] = br_swap32(iv2); + q[6] = br_swap32(iv3); + iv3 ++; + carry = ~(iv3 | -iv3) >> 31; + iv2 += carry; + carry &= -(~(iv2 | -iv2) >> 31); + iv1 += carry; + carry &= -(~(iv1 | -iv1) >> 31); + iv0 += carry; + + /* + * The odd values are used for CBC-MAC. + */ + q[1] = cm0; + q[3] = cm1; + q[5] = cm2; + q[7] = cm3; + + br_aes_ct_ortho(q); + br_aes_ct_bitslice_encrypt(ctx->num_rounds, sk_exp, q); + br_aes_ct_ortho(q); + + /* + * We do the XOR with the plaintext in 32-bit registers, + * so that the value are available for CBC-MAC processing + * as well. + */ + q[0] ^= br_dec32le(buf + 0); + q[2] ^= br_dec32le(buf + 4); + q[4] ^= br_dec32le(buf + 8); + q[6] ^= br_dec32le(buf + 12); + br_enc32le(buf + 0, q[0]); + br_enc32le(buf + 4, q[2]); + br_enc32le(buf + 8, q[4]); + br_enc32le(buf + 12, q[6]); + + buf += 16; + len -= 16; + + /* + * We set the cm* values to the block to encrypt in the + * next iteration. + */ + if (first_iter) { + first_iter = 0; + cm0 ^= q[0]; + cm1 ^= q[2]; + cm2 ^= q[4]; + cm3 ^= q[6]; + } else { + cm0 = q[0] ^ q[1]; + cm1 = q[2] ^ q[3]; + cm2 = q[4] ^ q[5]; + cm3 = q[6] ^ q[7]; + } + + /* + * If this was the last iteration, then compute the + * extra block encryption to complete CBC-MAC. + */ + if (len == 0) { + q[0] = cm0; + q[2] = cm1; + q[4] = cm2; + q[6] = cm3; + br_aes_ct_ortho(q); + br_aes_ct_bitslice_encrypt(ctx->num_rounds, sk_exp, q); + br_aes_ct_ortho(q); + cm0 = q[0]; + cm1 = q[2]; + cm2 = q[4]; + cm3 = q[6]; + break; + } + } + + br_enc32be(ivbuf + 0, iv0); + br_enc32be(ivbuf + 4, iv1); + br_enc32be(ivbuf + 8, iv2); + br_enc32be(ivbuf + 12, iv3); + br_enc32le((unsigned char *)cbcmac + 0, cm0); + br_enc32le((unsigned char *)cbcmac + 4, cm1); + br_enc32le((unsigned char *)cbcmac + 8, cm2); + br_enc32le((unsigned char *)cbcmac + 12, cm3); +} + +/* see bearssl_block.h */ +void +br_aes_ct_ctrcbc_decrypt(const br_aes_ct_ctrcbc_keys *ctx, + void *ctr, void *cbcmac, void *data, size_t len) +{ + unsigned char *buf; + unsigned char *ivbuf; + uint32_t iv0, iv1, iv2, iv3; + uint32_t cm0, cm1, cm2, cm3; + uint32_t sk_exp[120]; + + br_aes_ct_skey_expand(sk_exp, ctx->num_rounds, ctx->skey); + + /* + * We keep the counter as four 32-bit values, with big-endian + * convention, because that's what is expected for purposes of + * incrementing the counter value. + */ + ivbuf = ctr; + iv0 = br_dec32be(ivbuf + 0); + iv1 = br_dec32be(ivbuf + 4); + iv2 = br_dec32be(ivbuf + 8); + iv3 = br_dec32be(ivbuf + 12); + + /* + * The current CBC-MAC value is kept in little-endian convention. + */ + cm0 = br_dec32le((unsigned char *)cbcmac + 0); + cm1 = br_dec32le((unsigned char *)cbcmac + 4); + cm2 = br_dec32le((unsigned char *)cbcmac + 8); + cm3 = br_dec32le((unsigned char *)cbcmac + 12); + + buf = data; + while (len > 0) { + uint32_t q[8], carry; + unsigned char tmp[16]; + + /* + * The bitslice implementation expects values in + * little-endian convention, so we have to byteswap them. + */ + q[0] = br_swap32(iv0); + q[2] = br_swap32(iv1); + q[4] = br_swap32(iv2); + q[6] = br_swap32(iv3); + iv3 ++; + carry = ~(iv3 | -iv3) >> 31; + iv2 += carry; + carry &= -(~(iv2 | -iv2) >> 31); + iv1 += carry; + carry &= -(~(iv1 | -iv1) >> 31); + iv0 += carry; + + /* + * The odd values are used for CBC-MAC. + */ + q[1] = cm0 ^ br_dec32le(buf + 0); + q[3] = cm1 ^ br_dec32le(buf + 4); + q[5] = cm2 ^ br_dec32le(buf + 8); + q[7] = cm3 ^ br_dec32le(buf + 12); + + br_aes_ct_ortho(q); + br_aes_ct_bitslice_encrypt(ctx->num_rounds, sk_exp, q); + br_aes_ct_ortho(q); + + br_enc32le(tmp + 0, q[0]); + br_enc32le(tmp + 4, q[2]); + br_enc32le(tmp + 8, q[4]); + br_enc32le(tmp + 12, q[6]); + xorbuf(buf, tmp, 16); + cm0 = q[1]; + cm1 = q[3]; + cm2 = q[5]; + cm3 = q[7]; + buf += 16; + len -= 16; + } + + br_enc32be(ivbuf + 0, iv0); + br_enc32be(ivbuf + 4, iv1); + br_enc32be(ivbuf + 8, iv2); + br_enc32be(ivbuf + 12, iv3); + br_enc32le((unsigned char *)cbcmac + 0, cm0); + br_enc32le((unsigned char *)cbcmac + 4, cm1); + br_enc32le((unsigned char *)cbcmac + 8, cm2); + br_enc32le((unsigned char *)cbcmac + 12, cm3); +} + +/* see bearssl_block.h */ +const br_block_ctrcbc_class br_aes_ct_ctrcbc_vtable PROGMEM = { + sizeof(br_aes_ct_ctrcbc_keys), + 16, + 4, + (void (*)(const br_block_ctrcbc_class **, const void *, size_t)) + &br_aes_ct_ctrcbc_init, + (void (*)(const br_block_ctrcbc_class *const *, + void *, void *, void *, size_t)) + &br_aes_ct_ctrcbc_encrypt, + (void (*)(const br_block_ctrcbc_class *const *, + void *, void *, void *, size_t)) + &br_aes_ct_ctrcbc_decrypt, + (void (*)(const br_block_ctrcbc_class *const *, + void *, void *, size_t)) + &br_aes_ct_ctrcbc_ctr, + (void (*)(const br_block_ctrcbc_class *const *, + void *, const void *, size_t)) + &br_aes_ct_ctrcbc_mac +}; diff --git a/lib/bearssl-esp8266/src/symcipher/aes_ct_dec.c b/lib/bearssl-esp8266/src/symcipher/aes_ct_dec.c new file mode 100644 index 000000000..1ba86b7e0 --- /dev/null +++ b/lib/bearssl-esp8266/src/symcipher/aes_ct_dec.c @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +void +br_aes_ct_bitslice_invSbox(uint32_t *q) +{ + /* + * AES S-box is: + * S(x) = A(I(x)) ^ 0x63 + * where I() is inversion in GF(256), and A() is a linear + * transform (0 is formally defined to be its own inverse). + * Since inversion is an involution, the inverse S-box can be + * computed from the S-box as: + * iS(x) = B(S(B(x ^ 0x63)) ^ 0x63) + * where B() is the inverse of A(). Indeed, for any y in GF(256): + * iS(S(y)) = B(A(I(B(A(I(y)) ^ 0x63 ^ 0x63))) ^ 0x63 ^ 0x63) = y + * + * Note: we reuse the implementation of the forward S-box, + * instead of duplicating it here, so that total code size is + * lower. By merging the B() transforms into the S-box circuit + * we could make faster CBC decryption, but CBC decryption is + * already quite faster than CBC encryption because we can + * process two blocks in parallel. + */ + uint32_t q0, q1, q2, q3, q4, q5, q6, q7; + + q0 = ~q[0]; + q1 = ~q[1]; + q2 = q[2]; + q3 = q[3]; + q4 = q[4]; + q5 = ~q[5]; + q6 = ~q[6]; + q7 = q[7]; + q[7] = q1 ^ q4 ^ q6; + q[6] = q0 ^ q3 ^ q5; + q[5] = q7 ^ q2 ^ q4; + q[4] = q6 ^ q1 ^ q3; + q[3] = q5 ^ q0 ^ q2; + q[2] = q4 ^ q7 ^ q1; + q[1] = q3 ^ q6 ^ q0; + q[0] = q2 ^ q5 ^ q7; + + br_aes_ct_bitslice_Sbox(q); + + q0 = ~q[0]; + q1 = ~q[1]; + q2 = q[2]; + q3 = q[3]; + q4 = q[4]; + q5 = ~q[5]; + q6 = ~q[6]; + q7 = q[7]; + q[7] = q1 ^ q4 ^ q6; + q[6] = q0 ^ q3 ^ q5; + q[5] = q7 ^ q2 ^ q4; + q[4] = q6 ^ q1 ^ q3; + q[3] = q5 ^ q0 ^ q2; + q[2] = q4 ^ q7 ^ q1; + q[1] = q3 ^ q6 ^ q0; + q[0] = q2 ^ q5 ^ q7; +} + +static void +add_round_key(uint32_t *q, const uint32_t *sk) +{ + int i; + + for (i = 0; i < 8; i ++) { + q[i] ^= sk[i]; + } +} + +static void +inv_shift_rows(uint32_t *q) +{ + int i; + + for (i = 0; i < 8; i ++) { + uint32_t x; + + x = q[i]; + q[i] = (x & 0x000000FF) + | ((x & 0x00003F00) << 2) | ((x & 0x0000C000) >> 6) + | ((x & 0x000F0000) << 4) | ((x & 0x00F00000) >> 4) + | ((x & 0x03000000) << 6) | ((x & 0xFC000000) >> 2); + } +} + +static inline uint32_t +rotr16(uint32_t x) +{ + return (x << 16) | (x >> 16); +} + +static void +inv_mix_columns(uint32_t *q) +{ + uint32_t q0, q1, q2, q3, q4, q5, q6, q7; + uint32_t r0, r1, r2, r3, r4, r5, r6, r7; + + q0 = q[0]; + q1 = q[1]; + q2 = q[2]; + q3 = q[3]; + q4 = q[4]; + q5 = q[5]; + q6 = q[6]; + q7 = q[7]; + r0 = (q0 >> 8) | (q0 << 24); + r1 = (q1 >> 8) | (q1 << 24); + r2 = (q2 >> 8) | (q2 << 24); + r3 = (q3 >> 8) | (q3 << 24); + r4 = (q4 >> 8) | (q4 << 24); + r5 = (q5 >> 8) | (q5 << 24); + r6 = (q6 >> 8) | (q6 << 24); + r7 = (q7 >> 8) | (q7 << 24); + + q[0] = q5 ^ q6 ^ q7 ^ r0 ^ r5 ^ r7 ^ rotr16(q0 ^ q5 ^ q6 ^ r0 ^ r5); + q[1] = q0 ^ q5 ^ r0 ^ r1 ^ r5 ^ r6 ^ r7 ^ rotr16(q1 ^ q5 ^ q7 ^ r1 ^ r5 ^ r6); + q[2] = q0 ^ q1 ^ q6 ^ r1 ^ r2 ^ r6 ^ r7 ^ rotr16(q0 ^ q2 ^ q6 ^ r2 ^ r6 ^ r7); + q[3] = q0 ^ q1 ^ q2 ^ q5 ^ q6 ^ r0 ^ r2 ^ r3 ^ r5 ^ rotr16(q0 ^ q1 ^ q3 ^ q5 ^ q6 ^ q7 ^ r0 ^ r3 ^ r5 ^ r7); + q[4] = q1 ^ q2 ^ q3 ^ q5 ^ r1 ^ r3 ^ r4 ^ r5 ^ r6 ^ r7 ^ rotr16(q1 ^ q2 ^ q4 ^ q5 ^ q7 ^ r1 ^ r4 ^ r5 ^ r6); + q[5] = q2 ^ q3 ^ q4 ^ q6 ^ r2 ^ r4 ^ r5 ^ r6 ^ r7 ^ rotr16(q2 ^ q3 ^ q5 ^ q6 ^ r2 ^ r5 ^ r6 ^ r7); + q[6] = q3 ^ q4 ^ q5 ^ q7 ^ r3 ^ r5 ^ r6 ^ r7 ^ rotr16(q3 ^ q4 ^ q6 ^ q7 ^ r3 ^ r6 ^ r7); + q[7] = q4 ^ q5 ^ q6 ^ r4 ^ r6 ^ r7 ^ rotr16(q4 ^ q5 ^ q7 ^ r4 ^ r7); +} + +/* see inner.h */ +void +br_aes_ct_bitslice_decrypt(unsigned num_rounds, + const uint32_t *skey, uint32_t *q) +{ + unsigned u; + + add_round_key(q, skey + (num_rounds << 3)); + for (u = num_rounds - 1; u > 0; u --) { + inv_shift_rows(q); + br_aes_ct_bitslice_invSbox(q); + add_round_key(q, skey + (u << 3)); + inv_mix_columns(q); + } + inv_shift_rows(q); + br_aes_ct_bitslice_invSbox(q); + add_round_key(q, skey); +} diff --git a/lib/bearssl-esp8266/src/symcipher/aes_ct_enc.c b/lib/bearssl-esp8266/src/symcipher/aes_ct_enc.c new file mode 100644 index 000000000..f81bc956a --- /dev/null +++ b/lib/bearssl-esp8266/src/symcipher/aes_ct_enc.c @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +static inline void +add_round_key(uint32_t *q, const uint32_t *sk) +{ + q[0] ^= sk[0]; + q[1] ^= sk[1]; + q[2] ^= sk[2]; + q[3] ^= sk[3]; + q[4] ^= sk[4]; + q[5] ^= sk[5]; + q[6] ^= sk[6]; + q[7] ^= sk[7]; +} + +static inline void +shift_rows(uint32_t *q) +{ + int i; + + for (i = 0; i < 8; i ++) { + uint32_t x; + + x = q[i]; + q[i] = (x & 0x000000FF) + | ((x & 0x0000FC00) >> 2) | ((x & 0x00000300) << 6) + | ((x & 0x00F00000) >> 4) | ((x & 0x000F0000) << 4) + | ((x & 0xC0000000) >> 6) | ((x & 0x3F000000) << 2); + } +} + +static inline uint32_t +rotr16(uint32_t x) +{ + return (x << 16) | (x >> 16); +} + +static inline void +mix_columns(uint32_t *q) +{ + uint32_t q0, q1, q2, q3, q4, q5, q6, q7; + uint32_t r0, r1, r2, r3, r4, r5, r6, r7; + + q0 = q[0]; + q1 = q[1]; + q2 = q[2]; + q3 = q[3]; + q4 = q[4]; + q5 = q[5]; + q6 = q[6]; + q7 = q[7]; + r0 = (q0 >> 8) | (q0 << 24); + r1 = (q1 >> 8) | (q1 << 24); + r2 = (q2 >> 8) | (q2 << 24); + r3 = (q3 >> 8) | (q3 << 24); + r4 = (q4 >> 8) | (q4 << 24); + r5 = (q5 >> 8) | (q5 << 24); + r6 = (q6 >> 8) | (q6 << 24); + r7 = (q7 >> 8) | (q7 << 24); + + q[0] = q7 ^ r7 ^ r0 ^ rotr16(q0 ^ r0); + q[1] = q0 ^ r0 ^ q7 ^ r7 ^ r1 ^ rotr16(q1 ^ r1); + q[2] = q1 ^ r1 ^ r2 ^ rotr16(q2 ^ r2); + q[3] = q2 ^ r2 ^ q7 ^ r7 ^ r3 ^ rotr16(q3 ^ r3); + q[4] = q3 ^ r3 ^ q7 ^ r7 ^ r4 ^ rotr16(q4 ^ r4); + q[5] = q4 ^ r4 ^ r5 ^ rotr16(q5 ^ r5); + q[6] = q5 ^ r5 ^ r6 ^ rotr16(q6 ^ r6); + q[7] = q6 ^ r6 ^ r7 ^ rotr16(q7 ^ r7); +} + +/* see inner.h */ +void +br_aes_ct_bitslice_encrypt(unsigned num_rounds, + const uint32_t *skey, uint32_t *q) +{ + unsigned u; + + add_round_key(q, skey); + for (u = 1; u < num_rounds; u ++) { + br_aes_ct_bitslice_Sbox(q); + shift_rows(q); + mix_columns(q); + add_round_key(q, skey + (u << 3)); + } + br_aes_ct_bitslice_Sbox(q); + shift_rows(q); + add_round_key(q, skey + (num_rounds << 3)); +} diff --git a/lib/bearssl-esp8266/src/symcipher/aes_small_cbcdec.c b/lib/bearssl-esp8266/src/symcipher/aes_small_cbcdec.c new file mode 100644 index 000000000..53e4a9805 --- /dev/null +++ b/lib/bearssl-esp8266/src/symcipher/aes_small_cbcdec.c @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_block.h */ +void +br_aes_small_cbcdec_init(br_aes_small_cbcdec_keys *ctx, + const void *key, size_t len) +{ + ctx->vtable = &br_aes_small_cbcdec_vtable; + ctx->num_rounds = br_aes_keysched(ctx->skey, key, len); +} + +/* see bearssl_block.h */ +void +br_aes_small_cbcdec_run(const br_aes_small_cbcdec_keys *ctx, + void *iv, void *data, size_t len) +{ + unsigned char *buf, *ivbuf; + + ivbuf = iv; + buf = data; + while (len > 0) { + unsigned char tmp[16]; + int i; + + memcpy(tmp, buf, 16); + br_aes_small_decrypt(ctx->num_rounds, ctx->skey, buf); + for (i = 0; i < 16; i ++) { + buf[i] ^= ivbuf[i]; + } + memcpy(ivbuf, tmp, 16); + buf += 16; + len -= 16; + } +} + +/* see bearssl_block.h */ +const br_block_cbcdec_class br_aes_small_cbcdec_vtable PROGMEM = { + sizeof(br_aes_small_cbcdec_keys), + 16, + 4, + (void (*)(const br_block_cbcdec_class **, const void *, size_t)) + &br_aes_small_cbcdec_init, + (void (*)(const br_block_cbcdec_class *const *, void *, void *, size_t)) + &br_aes_small_cbcdec_run +}; diff --git a/lib/bearssl-esp8266/src/symcipher/aes_small_cbcenc.c b/lib/bearssl-esp8266/src/symcipher/aes_small_cbcenc.c new file mode 100644 index 000000000..19bbd6c2b --- /dev/null +++ b/lib/bearssl-esp8266/src/symcipher/aes_small_cbcenc.c @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_block.h */ +void +br_aes_small_cbcenc_init(br_aes_small_cbcenc_keys *ctx, + const void *key, size_t len) +{ + ctx->vtable = &br_aes_small_cbcenc_vtable; + ctx->num_rounds = br_aes_keysched(ctx->skey, key, len); +} + +/* see bearssl_block.h */ +void +br_aes_small_cbcenc_run(const br_aes_small_cbcenc_keys *ctx, + void *iv, void *data, size_t len) +{ + unsigned char *buf, *ivbuf; + + ivbuf = iv; + buf = data; + while (len > 0) { + int i; + + for (i = 0; i < 16; i ++) { + buf[i] ^= ivbuf[i]; + } + br_aes_small_encrypt(ctx->num_rounds, ctx->skey, buf); + memcpy(ivbuf, buf, 16); + buf += 16; + len -= 16; + } +} + +/* see bearssl_block.h */ +const br_block_cbcenc_class br_aes_small_cbcenc_vtable PROGMEM = { + sizeof(br_aes_small_cbcenc_keys), + 16, + 4, + (void (*)(const br_block_cbcenc_class **, const void *, size_t)) + &br_aes_small_cbcenc_init, + (void (*)(const br_block_cbcenc_class *const *, void *, void *, size_t)) + &br_aes_small_cbcenc_run +}; diff --git a/lib/bearssl-esp8266/src/symcipher/aes_small_ctr.c b/lib/bearssl-esp8266/src/symcipher/aes_small_ctr.c new file mode 100644 index 000000000..ec8df3e86 --- /dev/null +++ b/lib/bearssl-esp8266/src/symcipher/aes_small_ctr.c @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_block.h */ +void +br_aes_small_ctr_init(br_aes_small_ctr_keys *ctx, + const void *key, size_t len) +{ + ctx->vtable = &br_aes_small_ctr_vtable; + ctx->num_rounds = br_aes_keysched(ctx->skey, key, len); +} + +static void +xorbuf(void *dst, const void *src, size_t len) +{ + unsigned char *d; + const unsigned char *s; + + d = dst; + s = src; + while (len -- > 0) { + *d ++ ^= *s ++; + } +} + +/* see bearssl_block.h */ +uint32_t +br_aes_small_ctr_run(const br_aes_small_ctr_keys *ctx, + const void *iv, uint32_t cc, void *data, size_t len) +{ + unsigned char *buf; + + buf = data; + while (len > 0) { + unsigned char tmp[16]; + + memcpy(tmp, iv, 12); + br_enc32be(tmp + 12, cc ++); + br_aes_small_encrypt(ctx->num_rounds, ctx->skey, tmp); + if (len <= 16) { + xorbuf(buf, tmp, len); + break; + } + xorbuf(buf, tmp, 16); + buf += 16; + len -= 16; + } + return cc; +} + +/* see bearssl_block.h */ +const br_block_ctr_class br_aes_small_ctr_vtable PROGMEM = { + sizeof(br_aes_small_ctr_keys), + 16, + 4, + (void (*)(const br_block_ctr_class **, const void *, size_t)) + &br_aes_small_ctr_init, + (uint32_t (*)(const br_block_ctr_class *const *, + const void *, uint32_t, void *, size_t)) + &br_aes_small_ctr_run +}; diff --git a/lib/bearssl-esp8266/src/symcipher/aes_small_ctrcbc.c b/lib/bearssl-esp8266/src/symcipher/aes_small_ctrcbc.c new file mode 100644 index 000000000..49de1868f --- /dev/null +++ b/lib/bearssl-esp8266/src/symcipher/aes_small_ctrcbc.c @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_block.h */ +void +br_aes_small_ctrcbc_init(br_aes_small_ctrcbc_keys *ctx, + const void *key, size_t len) +{ + ctx->vtable = &br_aes_small_ctrcbc_vtable; + ctx->num_rounds = br_aes_keysched(ctx->skey, key, len); +} + +static void +xorbuf(void *dst, const void *src, size_t len) +{ + unsigned char *d; + const unsigned char *s; + + d = dst; + s = src; + while (len -- > 0) { + *d ++ ^= *s ++; + } +} + +/* see bearssl_block.h */ +void +br_aes_small_ctrcbc_ctr(const br_aes_small_ctrcbc_keys *ctx, + void *ctr, void *data, size_t len) +{ + unsigned char *buf, *bctr; + uint32_t cc0, cc1, cc2, cc3; + + buf = data; + bctr = ctr; + cc3 = br_dec32be(bctr + 0); + cc2 = br_dec32be(bctr + 4); + cc1 = br_dec32be(bctr + 8); + cc0 = br_dec32be(bctr + 12); + while (len > 0) { + unsigned char tmp[16]; + uint32_t carry; + + br_enc32be(tmp + 0, cc3); + br_enc32be(tmp + 4, cc2); + br_enc32be(tmp + 8, cc1); + br_enc32be(tmp + 12, cc0); + br_aes_small_encrypt(ctx->num_rounds, ctx->skey, tmp); + xorbuf(buf, tmp, 16); + buf += 16; + len -= 16; + cc0 ++; + carry = (~(cc0 | -cc0)) >> 31; + cc1 += carry; + carry &= (~(cc1 | -cc1)) >> 31; + cc2 += carry; + carry &= (~(cc2 | -cc2)) >> 31; + cc3 += carry; + } + br_enc32be(bctr + 0, cc3); + br_enc32be(bctr + 4, cc2); + br_enc32be(bctr + 8, cc1); + br_enc32be(bctr + 12, cc0); +} + +/* see bearssl_block.h */ +void +br_aes_small_ctrcbc_mac(const br_aes_small_ctrcbc_keys *ctx, + void *cbcmac, const void *data, size_t len) +{ + const unsigned char *buf; + + buf = data; + while (len > 0) { + xorbuf(cbcmac, buf, 16); + br_aes_small_encrypt(ctx->num_rounds, ctx->skey, cbcmac); + buf += 16; + len -= 16; + } +} + +/* see bearssl_block.h */ +void +br_aes_small_ctrcbc_encrypt(const br_aes_small_ctrcbc_keys *ctx, + void *ctr, void *cbcmac, void *data, size_t len) +{ + br_aes_small_ctrcbc_ctr(ctx, ctr, data, len); + br_aes_small_ctrcbc_mac(ctx, cbcmac, data, len); +} + +/* see bearssl_block.h */ +void +br_aes_small_ctrcbc_decrypt(const br_aes_small_ctrcbc_keys *ctx, + void *ctr, void *cbcmac, void *data, size_t len) +{ + br_aes_small_ctrcbc_mac(ctx, cbcmac, data, len); + br_aes_small_ctrcbc_ctr(ctx, ctr, data, len); +} + +/* see bearssl_block.h */ +const br_block_ctrcbc_class br_aes_small_ctrcbc_vtable PROGMEM = { + sizeof(br_aes_small_ctrcbc_keys), + 16, + 4, + (void (*)(const br_block_ctrcbc_class **, const void *, size_t)) + &br_aes_small_ctrcbc_init, + (void (*)(const br_block_ctrcbc_class *const *, + void *, void *, void *, size_t)) + &br_aes_small_ctrcbc_encrypt, + (void (*)(const br_block_ctrcbc_class *const *, + void *, void *, void *, size_t)) + &br_aes_small_ctrcbc_decrypt, + (void (*)(const br_block_ctrcbc_class *const *, + void *, void *, size_t)) + &br_aes_small_ctrcbc_ctr, + (void (*)(const br_block_ctrcbc_class *const *, + void *, const void *, size_t)) + &br_aes_small_ctrcbc_mac +}; diff --git a/lib/bearssl-esp8266/src/symcipher/aes_small_dec.c b/lib/bearssl-esp8266/src/symcipher/aes_small_dec.c new file mode 100644 index 000000000..6c5753021 --- /dev/null +++ b/lib/bearssl-esp8266/src/symcipher/aes_small_dec.c @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * Inverse S-box. + */ +static const unsigned char iS[] = { + 0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, + 0x81, 0xF3, 0xD7, 0xFB, 0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, + 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB, 0x54, 0x7B, 0x94, 0x32, + 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E, + 0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, + 0x6D, 0x8B, 0xD1, 0x25, 0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, + 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92, 0x6C, 0x70, 0x48, 0x50, + 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84, + 0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, + 0xB8, 0xB3, 0x45, 0x06, 0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, + 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B, 0x3A, 0x91, 0x11, 0x41, + 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73, + 0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, + 0x1C, 0x75, 0xDF, 0x6E, 0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, + 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B, 0xFC, 0x56, 0x3E, 0x4B, + 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4, + 0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, + 0x27, 0x80, 0xEC, 0x5F, 0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, + 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF, 0xA0, 0xE0, 0x3B, 0x4D, + 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61, + 0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, + 0x55, 0x21, 0x0C, 0x7D +}; + +static void +add_round_key(unsigned *state, const uint32_t *skeys) +{ + int i; + + for (i = 0; i < 16; i += 4) { + uint32_t k; + + k = *skeys ++; + state[i + 0] ^= (unsigned)(k >> 24); + state[i + 1] ^= (unsigned)(k >> 16) & 0xFF; + state[i + 2] ^= (unsigned)(k >> 8) & 0xFF; + state[i + 3] ^= (unsigned)k & 0xFF; + } +} + +static void +inv_sub_bytes(unsigned *state) +{ + int i; + + for (i = 0; i < 16; i ++) { + state[i] = iS[state[i]]; + } +} + +static void +inv_shift_rows(unsigned *state) +{ + unsigned tmp; + + tmp = state[13]; + state[13] = state[9]; + state[9] = state[5]; + state[5] = state[1]; + state[1] = tmp; + + tmp = state[2]; + state[2] = state[10]; + state[10] = tmp; + tmp = state[6]; + state[6] = state[14]; + state[14] = tmp; + + tmp = state[3]; + state[3] = state[7]; + state[7] = state[11]; + state[11] = state[15]; + state[15] = tmp; +} + +static inline unsigned +gf256red(unsigned x) +{ + unsigned y; + + y = x >> 8; + return (x ^ y ^ (y << 1) ^ (y << 3) ^ (y << 4)) & 0xFF; +} + +static void +inv_mix_columns(unsigned *state) +{ + int i; + + for (i = 0; i < 16; i += 4) { + unsigned s0, s1, s2, s3; + unsigned t0, t1, t2, t3; + + s0 = state[i + 0]; + s1 = state[i + 1]; + s2 = state[i + 2]; + s3 = state[i + 3]; + t0 = (s0 << 1) ^ (s0 << 2) ^ (s0 << 3) + ^ s1 ^ (s1 << 1) ^ (s1 << 3) + ^ s2 ^ (s2 << 2) ^ (s2 << 3) + ^ s3 ^ (s3 << 3); + t1 = s0 ^ (s0 << 3) + ^ (s1 << 1) ^ (s1 << 2) ^ (s1 << 3) + ^ s2 ^ (s2 << 1) ^ (s2 << 3) + ^ s3 ^ (s3 << 2) ^ (s3 << 3); + t2 = s0 ^ (s0 << 2) ^ (s0 << 3) + ^ s1 ^ (s1 << 3) + ^ (s2 << 1) ^ (s2 << 2) ^ (s2 << 3) + ^ s3 ^ (s3 << 1) ^ (s3 << 3); + t3 = s0 ^ (s0 << 1) ^ (s0 << 3) + ^ s1 ^ (s1 << 2) ^ (s1 << 3) + ^ s2 ^ (s2 << 3) + ^ (s3 << 1) ^ (s3 << 2) ^ (s3 << 3); + state[i + 0] = gf256red(t0); + state[i + 1] = gf256red(t1); + state[i + 2] = gf256red(t2); + state[i + 3] = gf256red(t3); + } +} + +/* see inner.h */ +void +br_aes_small_decrypt(unsigned num_rounds, const uint32_t *skey, void *data) +{ + unsigned char *buf; + unsigned state[16]; + unsigned u; + + buf = data; + for (u = 0; u < 16; u ++) { + state[u] = buf[u]; + } + add_round_key(state, skey + (num_rounds << 2)); + for (u = num_rounds - 1; u > 0; u --) { + inv_shift_rows(state); + inv_sub_bytes(state); + add_round_key(state, skey + (u << 2)); + inv_mix_columns(state); + } + inv_shift_rows(state); + inv_sub_bytes(state); + add_round_key(state, skey); + for (u = 0; u < 16; u ++) { + buf[u] = state[u]; + } +} diff --git a/lib/bearssl-esp8266/src/symcipher/aes_small_enc.c b/lib/bearssl-esp8266/src/symcipher/aes_small_enc.c new file mode 100644 index 000000000..0dca62c49 --- /dev/null +++ b/lib/bearssl-esp8266/src/symcipher/aes_small_enc.c @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +#define S br_aes_S + +static void +add_round_key(unsigned *state, const uint32_t *skeys) +{ + int i; + + for (i = 0; i < 16; i += 4) { + uint32_t k; + + k = *skeys ++; + state[i + 0] ^= (unsigned)(k >> 24); + state[i + 1] ^= (unsigned)(k >> 16) & 0xFF; + state[i + 2] ^= (unsigned)(k >> 8) & 0xFF; + state[i + 3] ^= (unsigned)k & 0xFF; + } +} + +static void +sub_bytes(unsigned *state) +{ + int i; + + for (i = 0; i < 16; i ++) { + state[i] = S[state[i]]; + } +} + +static void +shift_rows(unsigned *state) +{ + unsigned tmp; + + tmp = state[1]; + state[1] = state[5]; + state[5] = state[9]; + state[9] = state[13]; + state[13] = tmp; + + tmp = state[2]; + state[2] = state[10]; + state[10] = tmp; + tmp = state[6]; + state[6] = state[14]; + state[14] = tmp; + + tmp = state[15]; + state[15] = state[11]; + state[11] = state[7]; + state[7] = state[3]; + state[3] = tmp; +} + +static void +mix_columns(unsigned *state) +{ + int i; + + for (i = 0; i < 16; i += 4) { + unsigned s0, s1, s2, s3; + unsigned t0, t1, t2, t3; + + s0 = state[i + 0]; + s1 = state[i + 1]; + s2 = state[i + 2]; + s3 = state[i + 3]; + t0 = (s0 << 1) ^ s1 ^ (s1 << 1) ^ s2 ^ s3; + t1 = s0 ^ (s1 << 1) ^ s2 ^ (s2 << 1) ^ s3; + t2 = s0 ^ s1 ^ (s2 << 1) ^ s3 ^ (s3 << 1); + t3 = s0 ^ (s0 << 1) ^ s1 ^ s2 ^ (s3 << 1); + state[i + 0] = t0 ^ ((unsigned)(-(int)(t0 >> 8)) & 0x11B); + state[i + 1] = t1 ^ ((unsigned)(-(int)(t1 >> 8)) & 0x11B); + state[i + 2] = t2 ^ ((unsigned)(-(int)(t2 >> 8)) & 0x11B); + state[i + 3] = t3 ^ ((unsigned)(-(int)(t3 >> 8)) & 0x11B); + } +} + +/* see inner.h */ +void +br_aes_small_encrypt(unsigned num_rounds, const uint32_t *skey, void *data) +{ + unsigned char *buf; + unsigned state[16]; + unsigned u; + + buf = data; + for (u = 0; u < 16; u ++) { + state[u] = buf[u]; + } + add_round_key(state, skey); + for (u = 1; u < num_rounds; u ++) { + sub_bytes(state); + shift_rows(state); + mix_columns(state); + add_round_key(state, skey + (u << 2)); + } + sub_bytes(state); + shift_rows(state); + add_round_key(state, skey + (num_rounds << 2)); + for (u = 0; u < 16; u ++) { + buf[u] = state[u]; + } +} diff --git a/lib/bearssl-esp8266/src/symcipher/chacha20_ct.c b/lib/bearssl-esp8266/src/symcipher/chacha20_ct.c new file mode 100644 index 000000000..84ea79a1e --- /dev/null +++ b/lib/bearssl-esp8266/src/symcipher/chacha20_ct.c @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_block.h */ +uint32_t +br_chacha20_ct_run(const void *key, + const void *iv, uint32_t cc, void *data, size_t len) +{ + unsigned char *buf; + uint32_t kw[8], ivw[3]; + size_t u; + + static const uint32_t CW[] PROGMEM = { + 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574 + }; + + buf = data; + for (u = 0; u < 8; u ++) { + kw[u] = br_dec32le((const unsigned char *)key + (u << 2)); + } + for (u = 0; u < 3; u ++) { + ivw[u] = br_dec32le((const unsigned char *)iv + (u << 2)); + } + while (len > 0) { + uint32_t state[16]; + int i; + size_t clen; + unsigned char tmp[64]; + + memcpy(&state[0], CW, sizeof CW); + memcpy(&state[4], kw, sizeof kw); + state[12] = cc; + memcpy(&state[13], ivw, sizeof ivw); + for (i = 0; i < 10; i ++) { + +#define QROUND(a, b, c, d) do { \ + state[a] += state[b]; \ + state[d] ^= state[a]; \ + state[d] = (state[d] << 16) | (state[d] >> 16); \ + state[c] += state[d]; \ + state[b] ^= state[c]; \ + state[b] = (state[b] << 12) | (state[b] >> 20); \ + state[a] += state[b]; \ + state[d] ^= state[a]; \ + state[d] = (state[d] << 8) | (state[d] >> 24); \ + state[c] += state[d]; \ + state[b] ^= state[c]; \ + state[b] = (state[b] << 7) | (state[b] >> 25); \ + } while (0) + + QROUND( 0, 4, 8, 12); + QROUND( 1, 5, 9, 13); + QROUND( 2, 6, 10, 14); + QROUND( 3, 7, 11, 15); + QROUND( 0, 5, 10, 15); + QROUND( 1, 6, 11, 12); + QROUND( 2, 7, 8, 13); + QROUND( 3, 4, 9, 14); + +#undef QROUND + + } + for (u = 0; u < 4; u ++) { + br_enc32le(&tmp[u << 2], state[u] + CW[u]); + } + for (u = 4; u < 12; u ++) { + br_enc32le(&tmp[u << 2], state[u] + kw[u - 4]); + } + br_enc32le(&tmp[48], state[12] + cc); + for (u = 13; u < 16; u ++) { + br_enc32le(&tmp[u << 2], state[u] + ivw[u - 13]); + } + + clen = len < 64 ? len : 64; + for (u = 0; u < clen; u ++) { + buf[u] ^= tmp[u]; + } + buf += clen; + len -= clen; + cc ++; + } + return cc; +} diff --git a/lib/bearssl-esp8266/src/symcipher/chacha20_sse2.c b/lib/bearssl-esp8266/src/symcipher/chacha20_sse2.c new file mode 100644 index 000000000..52c213903 --- /dev/null +++ b/lib/bearssl-esp8266/src/symcipher/chacha20_sse2.c @@ -0,0 +1,237 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BR_ENABLE_INTRINSICS 1 +#include "t_inner.h" + +#if BR_SSE2 + +/* + * This file contains a ChaCha20 implementation that leverages SSE2 + * opcodes for better performance. + */ + +/* see bearssl_block.h */ +br_chacha20_run +br_chacha20_sse2_get(void) +{ + /* + * If using 64-bit mode, then SSE2 opcodes should be automatically + * available, since they are part of the ABI. + * + * In 32-bit mode, we use CPUID to detect the SSE2 feature. + */ + +#if BR_amd64 + return &br_chacha20_sse2_run; +#else + + /* + * SSE2 support is indicated by bit 26 in EDX. + */ + if (br_cpuid(0, 0, 0, 0x04000000)) { + return &br_chacha20_sse2_run; + } else { + return 0; + } +#endif +} + +BR_TARGETS_X86_UP + +/* see bearssl_block.h */ +BR_TARGET("sse2") +uint32_t +br_chacha20_sse2_run(const void *key, + const void *iv, uint32_t cc, void *data, size_t len) +{ + unsigned char *buf; + uint32_t ivtmp[4]; + __m128i kw0, kw1; + __m128i iw, cw; + __m128i one; + + static const uint32_t CW[] = { + 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574 + }; + + buf = data; + kw0 = _mm_loadu_si128(key); + kw1 = _mm_loadu_si128((const void *)((const unsigned char *)key + 16)); + ivtmp[0] = cc; + memcpy(ivtmp + 1, iv, 12); + iw = _mm_loadu_si128((const void *)ivtmp); + cw = _mm_loadu_si128((const void *)CW); + one = _mm_set_epi32(0, 0, 0, 1); + + while (len > 0) { + /* + * sj contains state words 4*j to 4*j+3. + */ + __m128i s0, s1, s2, s3; + int i; + + s0 = cw; + s1 = kw0; + s2 = kw1; + s3 = iw; + for (i = 0; i < 10; i ++) { + /* + * Even round is straightforward application on + * the state words. + */ + s0 = _mm_add_epi32(s0, s1); + s3 = _mm_xor_si128(s3, s0); + s3 = _mm_or_si128( + _mm_slli_epi32(s3, 16), + _mm_srli_epi32(s3, 16)); + + s2 = _mm_add_epi32(s2, s3); + s1 = _mm_xor_si128(s1, s2); + s1 = _mm_or_si128( + _mm_slli_epi32(s1, 12), + _mm_srli_epi32(s1, 20)); + + s0 = _mm_add_epi32(s0, s1); + s3 = _mm_xor_si128(s3, s0); + s3 = _mm_or_si128( + _mm_slli_epi32(s3, 8), + _mm_srli_epi32(s3, 24)); + + s2 = _mm_add_epi32(s2, s3); + s1 = _mm_xor_si128(s1, s2); + s1 = _mm_or_si128( + _mm_slli_epi32(s1, 7), + _mm_srli_epi32(s1, 25)); + + /* + * For the odd round, we must rotate some state + * words so that the computations apply on the + * right combinations of words. + */ + s1 = _mm_shuffle_epi32(s1, 0x39); + s2 = _mm_shuffle_epi32(s2, 0x4E); + s3 = _mm_shuffle_epi32(s3, 0x93); + + s0 = _mm_add_epi32(s0, s1); + s3 = _mm_xor_si128(s3, s0); + s3 = _mm_or_si128( + _mm_slli_epi32(s3, 16), + _mm_srli_epi32(s3, 16)); + + s2 = _mm_add_epi32(s2, s3); + s1 = _mm_xor_si128(s1, s2); + s1 = _mm_or_si128( + _mm_slli_epi32(s1, 12), + _mm_srli_epi32(s1, 20)); + + s0 = _mm_add_epi32(s0, s1); + s3 = _mm_xor_si128(s3, s0); + s3 = _mm_or_si128( + _mm_slli_epi32(s3, 8), + _mm_srli_epi32(s3, 24)); + + s2 = _mm_add_epi32(s2, s3); + s1 = _mm_xor_si128(s1, s2); + s1 = _mm_or_si128( + _mm_slli_epi32(s1, 7), + _mm_srli_epi32(s1, 25)); + + /* + * After the odd round, we rotate back the values + * to undo the rotate at the start of the odd round. + */ + s1 = _mm_shuffle_epi32(s1, 0x93); + s2 = _mm_shuffle_epi32(s2, 0x4E); + s3 = _mm_shuffle_epi32(s3, 0x39); + } + + /* + * Addition with the initial state. + */ + s0 = _mm_add_epi32(s0, cw); + s1 = _mm_add_epi32(s1, kw0); + s2 = _mm_add_epi32(s2, kw1); + s3 = _mm_add_epi32(s3, iw); + + /* + * Increment block counter. + */ + iw = _mm_add_epi32(iw, one); + + /* + * XOR final state with the data. + */ + if (len < 64) { + unsigned char tmp[64]; + size_t u; + + _mm_storeu_si128((void *)(tmp + 0), s0); + _mm_storeu_si128((void *)(tmp + 16), s1); + _mm_storeu_si128((void *)(tmp + 32), s2); + _mm_storeu_si128((void *)(tmp + 48), s3); + for (u = 0; u < len; u ++) { + buf[u] ^= tmp[u]; + } + break; + } else { + __m128i b0, b1, b2, b3; + + b0 = _mm_loadu_si128((const void *)(buf + 0)); + b1 = _mm_loadu_si128((const void *)(buf + 16)); + b2 = _mm_loadu_si128((const void *)(buf + 32)); + b3 = _mm_loadu_si128((const void *)(buf + 48)); + b0 = _mm_xor_si128(b0, s0); + b1 = _mm_xor_si128(b1, s1); + b2 = _mm_xor_si128(b2, s2); + b3 = _mm_xor_si128(b3, s3); + _mm_storeu_si128((void *)(buf + 0), b0); + _mm_storeu_si128((void *)(buf + 16), b1); + _mm_storeu_si128((void *)(buf + 32), b2); + _mm_storeu_si128((void *)(buf + 48), b3); + buf += 64; + len -= 64; + } + } + + /* + * _mm_extract_epi32() requires SSE4.1. We prefer to stick to + * raw SSE2, thus we use _mm_extract_epi16(). + */ + return (uint32_t)_mm_extract_epi16(iw, 0) + | ((uint32_t)_mm_extract_epi16(iw, 1) << 16); +} + +BR_TARGETS_X86_DOWN + +#else + +/* see bearssl_block.h */ +br_chacha20_run +br_chacha20_sse2_get(void) +{ + return 0; +} + +#endif diff --git a/lib/bearssl-esp8266/src/symcipher/des_ct.c b/lib/bearssl-esp8266/src/symcipher/des_ct.c new file mode 100644 index 000000000..238717630 --- /dev/null +++ b/lib/bearssl-esp8266/src/symcipher/des_ct.c @@ -0,0 +1,411 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * During key schedule, we need to apply bit extraction PC-2 then permute + * things into our bitslice representation. PC-2 extracts 48 bits out + * of two 28-bit words (kl and kr), and we store these bits into two + * 32-bit words sk0 and sk1. + * + * -- bit 16+x of sk0 comes from bit QL0[x] of kl + * -- bit x of sk0 comes from bit QR0[x] of kr + * -- bit 16+x of sk1 comes from bit QL1[x] of kl + * -- bit x of sk1 comes from bit QR1[x] of kr + */ + +static const unsigned char QL0[] PROGMEM = { + 17, 4, 27, 23, 13, 22, 7, 18, + 16, 24, 2, 20, 1, 8, 15, 26 +}; + +static const unsigned char QR0[] PROGMEM = { + 25, 19, 9, 1, 5, 11, 23, 8, + 17, 0, 22, 3, 6, 20, 27, 24 +}; + +static const unsigned char QL1[] PROGMEM = { + 28, 28, 14, 11, 28, 28, 25, 0, + 28, 28, 5, 9, 28, 28, 12, 21 +}; + +static const unsigned char QR1[] PROGMEM = { + 28, 28, 15, 4, 28, 28, 26, 16, + 28, 28, 12, 7, 28, 28, 10, 14 +}; + +/* + * 32-bit rotation. The C compiler is supposed to recognize it as a + * rotation and use the local architecture rotation opcode (if available). + */ +static inline uint32_t +rotl(uint32_t x, int n) +{ + return (x << n) | (x >> (32 - n)); +} + +/* + * Compute key schedule for 8 key bytes (produces 32 subkey words). + */ +static void +keysched_unit(uint32_t *skey, const void *key) +{ + int i; + + br_des_keysched_unit(skey, key); + + /* + * Apply PC-2 + bitslicing. + */ + for (i = 0; i < 16; i ++) { + uint32_t kl, kr, sk0, sk1; + int j; + + kl = skey[(i << 1) + 0]; + kr = skey[(i << 1) + 1]; + sk0 = 0; + sk1 = 0; + for (j = 0; j < 16; j ++) { + sk0 <<= 1; + sk1 <<= 1; + sk0 |= ((kl >> pgm_read_byte(&QL0[j])) & (uint32_t)1) << 16; + sk0 |= (kr >> pgm_read_byte(&QR0[j])) & (uint32_t)1; + sk1 |= ((kl >> pgm_read_byte(&QL1[j])) & (uint32_t)1) << 16; + sk1 |= (kr >> pgm_read_byte(&QR1[j])) & (uint32_t)1; + } + + skey[(i << 1) + 0] = sk0; + skey[(i << 1) + 1] = sk1; + } + +#if 0 + /* + * Speed-optimized version for PC-2 + bitslicing. + * (Unused. Kept for reference only.) + */ + sk0 = kl & (uint32_t)0x00100000; + sk0 |= (kl & (uint32_t)0x08008000) << 2; + sk0 |= (kl & (uint32_t)0x00400000) << 4; + sk0 |= (kl & (uint32_t)0x00800000) << 5; + sk0 |= (kl & (uint32_t)0x00040000) << 6; + sk0 |= (kl & (uint32_t)0x00010000) << 7; + sk0 |= (kl & (uint32_t)0x00000100) << 10; + sk0 |= (kl & (uint32_t)0x00022000) << 14; + sk0 |= (kl & (uint32_t)0x00000082) << 18; + sk0 |= (kl & (uint32_t)0x00000004) << 19; + sk0 |= (kl & (uint32_t)0x04000000) >> 10; + sk0 |= (kl & (uint32_t)0x00000010) << 26; + sk0 |= (kl & (uint32_t)0x01000000) >> 2; + + sk0 |= kr & (uint32_t)0x00000100; + sk0 |= (kr & (uint32_t)0x00000008) << 1; + sk0 |= (kr & (uint32_t)0x00000200) << 4; + sk0 |= rotl(kr & (uint32_t)0x08000021, 6); + sk0 |= (kr & (uint32_t)0x01000000) >> 24; + sk0 |= (kr & (uint32_t)0x00000002) << 11; + sk0 |= (kr & (uint32_t)0x00100000) >> 18; + sk0 |= (kr & (uint32_t)0x00400000) >> 17; + sk0 |= (kr & (uint32_t)0x00800000) >> 14; + sk0 |= (kr & (uint32_t)0x02020000) >> 10; + sk0 |= (kr & (uint32_t)0x00080000) >> 5; + sk0 |= (kr & (uint32_t)0x00000040) >> 3; + sk0 |= (kr & (uint32_t)0x00000800) >> 1; + + sk1 = kl & (uint32_t)0x02000000; + sk1 |= (kl & (uint32_t)0x00001000) << 5; + sk1 |= (kl & (uint32_t)0x00000200) << 11; + sk1 |= (kl & (uint32_t)0x00004000) << 15; + sk1 |= (kl & (uint32_t)0x00000020) << 16; + sk1 |= (kl & (uint32_t)0x00000800) << 17; + sk1 |= (kl & (uint32_t)0x00000001) << 24; + sk1 |= (kl & (uint32_t)0x00200000) >> 5; + + sk1 |= (kr & (uint32_t)0x00000010) << 8; + sk1 |= (kr & (uint32_t)0x04000000) >> 17; + sk1 |= (kr & (uint32_t)0x00004000) >> 14; + sk1 |= (kr & (uint32_t)0x00000400) >> 9; + sk1 |= (kr & (uint32_t)0x00010000) >> 8; + sk1 |= (kr & (uint32_t)0x00001000) >> 7; + sk1 |= (kr & (uint32_t)0x00000080) >> 3; + sk1 |= (kr & (uint32_t)0x00008000) >> 2; +#endif +} + +/* see inner.h */ +unsigned +br_des_ct_keysched(uint32_t *skey, const void *key, size_t key_len) +{ + switch (key_len) { + case 8: + keysched_unit(skey, key); + return 1; + case 16: + keysched_unit(skey, key); + keysched_unit(skey + 32, (const unsigned char *)key + 8); + br_des_rev_skey(skey + 32); + memcpy(skey + 64, skey, 32 * sizeof *skey); + return 3; + default: + keysched_unit(skey, key); + keysched_unit(skey + 32, (const unsigned char *)key + 8); + br_des_rev_skey(skey + 32); + keysched_unit(skey + 64, (const unsigned char *)key + 16); + return 3; + } +} + +/* + * DES confusion function. This function performs expansion E (32 to + * 48 bits), XOR with subkey, S-boxes, and permutation P. + */ +static inline uint32_t +Fconf(uint32_t r0, const uint32_t *sk) +{ + /* + * Each 6->4 S-box is virtually turned into four 6->1 boxes; we + * thus end up with 32 boxes that we call "T-boxes" here. We will + * evaluate them with bitslice code. + * + * Each T-box is a circuit of multiplexers (sort of) and thus + * takes 70 inputs: the 6 actual T-box inputs, and 64 constants + * that describe the T-box output for all combinations of the + * 6 inputs. With this model, all T-boxes are identical (with + * distinct inputs) and thus can be executed in parallel with + * bitslice code. + * + * T-boxes are numbered from 0 to 31, in least-to-most + * significant order. Thus, S-box S1 corresponds to T-boxes 31, + * 30, 29 and 28, in that order. T-box 'n' is computed with the + * bits at rank 'n' in the 32-bit words. + * + * Words x0 to x5 contain the T-box inputs 0 to 5. + */ + uint32_t x0, x1, x2, x3, x4, x5, z0; + uint32_t y0, y1, y2, y3, y4, y5, y6, y7, y8, y9; + uint32_t y10, y11, y12, y13, y14, y15, y16, y17, y18, y19; + uint32_t y20, y21, y22, y23, y24, y25, y26, y27, y28, y29; + uint32_t y30; + + /* + * Spread input bits over the 6 input words x*. + */ + x1 = r0 & (uint32_t)0x11111111; + x2 = (r0 >> 1) & (uint32_t)0x11111111; + x3 = (r0 >> 2) & (uint32_t)0x11111111; + x4 = (r0 >> 3) & (uint32_t)0x11111111; + x1 = (x1 << 4) - x1; + x2 = (x2 << 4) - x2; + x3 = (x3 << 4) - x3; + x4 = (x4 << 4) - x4; + x0 = (x4 << 4) | (x4 >> 28); + x5 = (x1 >> 4) | (x1 << 28); + + /* + * XOR with the subkey for this round. + */ + x0 ^= sk[0]; + x1 ^= sk[1]; + x2 ^= sk[2]; + x3 ^= sk[3]; + x4 ^= sk[4]; + x5 ^= sk[5]; + + /* + * The T-boxes are done in parallel, since they all use a + * "tree of multiplexer". We use "fake multiplexers": + * + * y = a ^ (x & b) + * + * computes y as either 'a' (if x == 0) or 'a ^ b' (if x == 1). + */ + y0 = (uint32_t)0xEFA72C4D ^ (x0 & (uint32_t)0xEC7AC69C); + y1 = (uint32_t)0xAEAAEDFF ^ (x0 & (uint32_t)0x500FB821); + y2 = (uint32_t)0x37396665 ^ (x0 & (uint32_t)0x40EFA809); + y3 = (uint32_t)0x68D7B833 ^ (x0 & (uint32_t)0xA5EC0B28); + y4 = (uint32_t)0xC9C755BB ^ (x0 & (uint32_t)0x252CF820); + y5 = (uint32_t)0x73FC3606 ^ (x0 & (uint32_t)0x40205801); + y6 = (uint32_t)0xA2A0A918 ^ (x0 & (uint32_t)0xE220F929); + y7 = (uint32_t)0x8222BD90 ^ (x0 & (uint32_t)0x44A3F9E1); + y8 = (uint32_t)0xD6B6AC77 ^ (x0 & (uint32_t)0x794F104A); + y9 = (uint32_t)0x3069300C ^ (x0 & (uint32_t)0x026F320B); + y10 = (uint32_t)0x6CE0D5CC ^ (x0 & (uint32_t)0x7640B01A); + y11 = (uint32_t)0x59A9A22D ^ (x0 & (uint32_t)0x238F1572); + y12 = (uint32_t)0xAC6D0BD4 ^ (x0 & (uint32_t)0x7A63C083); + y13 = (uint32_t)0x21C83200 ^ (x0 & (uint32_t)0x11CCA000); + y14 = (uint32_t)0xA0E62188 ^ (x0 & (uint32_t)0x202F69AA); + /* y15 = (uint32_t)0x00000000 ^ (x0 & (uint32_t)0x00000000); */ + y16 = (uint32_t)0xAF7D655A ^ (x0 & (uint32_t)0x51B33BE9); + y17 = (uint32_t)0xF0168AA3 ^ (x0 & (uint32_t)0x3B0FE8AE); + y18 = (uint32_t)0x90AA30C6 ^ (x0 & (uint32_t)0x90BF8816); + y19 = (uint32_t)0x5AB2750A ^ (x0 & (uint32_t)0x09E34F9B); + y20 = (uint32_t)0x5391BE65 ^ (x0 & (uint32_t)0x0103BE88); + y21 = (uint32_t)0x93372BAF ^ (x0 & (uint32_t)0x49AC8E25); + y22 = (uint32_t)0xF288210C ^ (x0 & (uint32_t)0x922C313D); + y23 = (uint32_t)0x920AF5C0 ^ (x0 & (uint32_t)0x70EF31B0); + y24 = (uint32_t)0x63D312C0 ^ (x0 & (uint32_t)0x6A707100); + y25 = (uint32_t)0x537B3006 ^ (x0 & (uint32_t)0xB97C9011); + y26 = (uint32_t)0xA2EFB0A5 ^ (x0 & (uint32_t)0xA320C959); + y27 = (uint32_t)0xBC8F96A5 ^ (x0 & (uint32_t)0x6EA0AB4A); + y28 = (uint32_t)0xFAD176A5 ^ (x0 & (uint32_t)0x6953DDF8); + y29 = (uint32_t)0x665A14A3 ^ (x0 & (uint32_t)0xF74F3E2B); + y30 = (uint32_t)0xF2EFF0CC ^ (x0 & (uint32_t)0xF0306CAD); + /* y31 = (uint32_t)0x00000000 ^ (x0 & (uint32_t)0x00000000); */ + + y0 = y0 ^ (x1 & y1); + y1 = y2 ^ (x1 & y3); + y2 = y4 ^ (x1 & y5); + y3 = y6 ^ (x1 & y7); + y4 = y8 ^ (x1 & y9); + y5 = y10 ^ (x1 & y11); + y6 = y12 ^ (x1 & y13); + y7 = y14; /* was: y14 ^ (x1 & y15) */ + y8 = y16 ^ (x1 & y17); + y9 = y18 ^ (x1 & y19); + y10 = y20 ^ (x1 & y21); + y11 = y22 ^ (x1 & y23); + y12 = y24 ^ (x1 & y25); + y13 = y26 ^ (x1 & y27); + y14 = y28 ^ (x1 & y29); + y15 = y30; /* was: y30 ^ (x1 & y31) */ + + y0 = y0 ^ (x2 & y1); + y1 = y2 ^ (x2 & y3); + y2 = y4 ^ (x2 & y5); + y3 = y6 ^ (x2 & y7); + y4 = y8 ^ (x2 & y9); + y5 = y10 ^ (x2 & y11); + y6 = y12 ^ (x2 & y13); + y7 = y14 ^ (x2 & y15); + + y0 = y0 ^ (x3 & y1); + y1 = y2 ^ (x3 & y3); + y2 = y4 ^ (x3 & y5); + y3 = y6 ^ (x3 & y7); + + y0 = y0 ^ (x4 & y1); + y1 = y2 ^ (x4 & y3); + + y0 = y0 ^ (x5 & y1); + + /* + * The P permutation: + * -- Each bit move is converted into a mask + left rotation. + * -- Rotations that use the same movement are coalesced together. + * -- Left and right shifts are used as alternatives to a rotation + * where appropriate (this will help architectures that do not have + * a rotation opcode). + */ + z0 = (y0 & (uint32_t)0x00000004) << 3; + z0 |= (y0 & (uint32_t)0x00004000) << 4; + z0 |= rotl(y0 & 0x12020120, 5); + z0 |= (y0 & (uint32_t)0x00100000) << 6; + z0 |= (y0 & (uint32_t)0x00008000) << 9; + z0 |= (y0 & (uint32_t)0x04000000) >> 22; + z0 |= (y0 & (uint32_t)0x00000001) << 11; + z0 |= rotl(y0 & 0x20000200, 12); + z0 |= (y0 & (uint32_t)0x00200000) >> 19; + z0 |= (y0 & (uint32_t)0x00000040) << 14; + z0 |= (y0 & (uint32_t)0x00010000) << 15; + z0 |= (y0 & (uint32_t)0x00000002) << 16; + z0 |= rotl(y0 & 0x40801800, 17); + z0 |= (y0 & (uint32_t)0x00080000) >> 13; + z0 |= (y0 & (uint32_t)0x00000010) << 21; + z0 |= (y0 & (uint32_t)0x01000000) >> 10; + z0 |= rotl(y0 & 0x88000008, 24); + z0 |= (y0 & (uint32_t)0x00000480) >> 7; + z0 |= (y0 & (uint32_t)0x00442000) >> 6; + return z0; +} + +/* + * Process one block through 16 successive rounds, omitting the swap + * in the final round. + */ +static void +process_block_unit(uint32_t *pl, uint32_t *pr, const uint32_t *sk_exp) +{ + int i; + uint32_t l, r; + + l = *pl; + r = *pr; + for (i = 0; i < 16; i ++) { + uint32_t t; + + t = l ^ Fconf(r, sk_exp); + l = r; + r = t; + sk_exp += 6; + } + *pl = r; + *pr = l; +} + +/* see inner.h */ +void +br_des_ct_process_block(unsigned num_rounds, + const uint32_t *sk_exp, void *block) +{ + unsigned char *buf; + uint32_t l, r; + + buf = block; + l = br_dec32be(buf); + r = br_dec32be(buf + 4); + br_des_do_IP(&l, &r); + while (num_rounds -- > 0) { + process_block_unit(&l, &r, sk_exp); + sk_exp += 96; + } + br_des_do_invIP(&l, &r); + br_enc32be(buf, l); + br_enc32be(buf + 4, r); +} + +/* see inner.h */ +void +br_des_ct_skey_expand(uint32_t *sk_exp, + unsigned num_rounds, const uint32_t *skey) +{ + num_rounds <<= 4; + while (num_rounds -- > 0) { + uint32_t v, w0, w1, w2, w3; + + v = *skey ++; + w0 = v & 0x11111111; + w1 = (v >> 1) & 0x11111111; + w2 = (v >> 2) & 0x11111111; + w3 = (v >> 3) & 0x11111111; + *sk_exp ++ = (w0 << 4) - w0; + *sk_exp ++ = (w1 << 4) - w1; + *sk_exp ++ = (w2 << 4) - w2; + *sk_exp ++ = (w3 << 4) - w3; + v = *skey ++; + w0 = v & 0x11111111; + w1 = (v >> 1) & 0x11111111; + *sk_exp ++ = (w0 << 4) - w0; + *sk_exp ++ = (w1 << 4) - w1; + } +} diff --git a/lib/bearssl-esp8266/src/symcipher/des_ct_cbcdec.c b/lib/bearssl-esp8266/src/symcipher/des_ct_cbcdec.c new file mode 100644 index 000000000..37645c98e --- /dev/null +++ b/lib/bearssl-esp8266/src/symcipher/des_ct_cbcdec.c @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_block.h */ +void +br_des_ct_cbcdec_init(br_des_ct_cbcdec_keys *ctx, + const void *key, size_t len) +{ + ctx->vtable = &br_des_ct_cbcdec_vtable; + ctx->num_rounds = br_des_ct_keysched(ctx->skey, key, len); + if (len == 8) { + br_des_rev_skey(ctx->skey); + } else { + int i; + + for (i = 0; i < 48; i += 2) { + uint32_t t; + + t = ctx->skey[i]; + ctx->skey[i] = ctx->skey[94 - i]; + ctx->skey[94 - i] = t; + t = ctx->skey[i + 1]; + ctx->skey[i + 1] = ctx->skey[95 - i]; + ctx->skey[95 - i] = t; + } + } +} + +/* see bearssl_block.h */ +void +br_des_ct_cbcdec_run(const br_des_ct_cbcdec_keys *ctx, + void *iv, void *data, size_t len) +{ + unsigned char *buf, *ivbuf; + uint32_t sk_exp[288]; + + br_des_ct_skey_expand(sk_exp, ctx->num_rounds, ctx->skey); + ivbuf = iv; + buf = data; + while (len > 0) { + unsigned char tmp[8]; + int i; + + memcpy(tmp, buf, 8); + br_des_ct_process_block(ctx->num_rounds, sk_exp, buf); + for (i = 0; i < 8; i ++) { + buf[i] ^= ivbuf[i]; + } + memcpy(ivbuf, tmp, 8); + buf += 8; + len -= 8; + } +} + +/* see bearssl_block.h */ +const br_block_cbcdec_class br_des_ct_cbcdec_vtable PROGMEM = { + sizeof(br_des_ct_cbcdec_keys), + 8, + 3, + (void (*)(const br_block_cbcdec_class **, const void *, size_t)) + &br_des_ct_cbcdec_init, + (void (*)(const br_block_cbcdec_class *const *, void *, void *, size_t)) + &br_des_ct_cbcdec_run +}; diff --git a/lib/bearssl-esp8266/src/symcipher/des_ct_cbcenc.c b/lib/bearssl-esp8266/src/symcipher/des_ct_cbcenc.c new file mode 100644 index 000000000..da6fbb638 --- /dev/null +++ b/lib/bearssl-esp8266/src/symcipher/des_ct_cbcenc.c @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_block.h */ +void +br_des_ct_cbcenc_init(br_des_ct_cbcenc_keys *ctx, + const void *key, size_t len) +{ + ctx->vtable = &br_des_ct_cbcenc_vtable; + ctx->num_rounds = br_des_ct_keysched(ctx->skey, key, len); +} + +/* see bearssl_block.h */ +void +br_des_ct_cbcenc_run(const br_des_ct_cbcenc_keys *ctx, + void *iv, void *data, size_t len) +{ + unsigned char *buf, *ivbuf; + uint32_t sk_exp[288]; + + br_des_ct_skey_expand(sk_exp, ctx->num_rounds, ctx->skey); + ivbuf = iv; + buf = data; + while (len > 0) { + int i; + + for (i = 0; i < 8; i ++) { + buf[i] ^= ivbuf[i]; + } + br_des_ct_process_block(ctx->num_rounds, sk_exp, buf); + memcpy(ivbuf, buf, 8); + buf += 8; + len -= 8; + } +} + +/* see bearssl_block.h */ +const br_block_cbcenc_class br_des_ct_cbcenc_vtable PROGMEM = { + sizeof(br_des_ct_cbcenc_keys), + 8, + 3, + (void (*)(const br_block_cbcenc_class **, const void *, size_t)) + &br_des_ct_cbcenc_init, + (void (*)(const br_block_cbcenc_class *const *, void *, void *, size_t)) + &br_des_ct_cbcenc_run +}; diff --git a/lib/bearssl-esp8266/src/symcipher/des_support.c b/lib/bearssl-esp8266/src/symcipher/des_support.c new file mode 100644 index 000000000..2176c90a1 --- /dev/null +++ b/lib/bearssl-esp8266/src/symcipher/des_support.c @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +void +br_des_do_IP(uint32_t *xl, uint32_t *xr) +{ + /* + * Permutation algorithm is initially from Richard Outerbridge; + * implementation here is adapted from Crypto++ "des.cpp" file + * (which is in public domain). + */ + uint32_t l, r, t; + + l = *xl; + r = *xr; + t = ((l >> 4) ^ r) & (uint32_t)0x0F0F0F0F; + r ^= t; + l ^= t << 4; + t = ((l >> 16) ^ r) & (uint32_t)0x0000FFFF; + r ^= t; + l ^= t << 16; + t = ((r >> 2) ^ l) & (uint32_t)0x33333333; + l ^= t; + r ^= t << 2; + t = ((r >> 8) ^ l) & (uint32_t)0x00FF00FF; + l ^= t; + r ^= t << 8; + t = ((l >> 1) ^ r) & (uint32_t)0x55555555; + r ^= t; + l ^= t << 1; + *xl = l; + *xr = r; +} + +/* see inner.h */ +void +br_des_do_invIP(uint32_t *xl, uint32_t *xr) +{ + /* + * See br_des_do_IP(). + */ + uint32_t l, r, t; + + l = *xl; + r = *xr; + t = ((l >> 1) ^ r) & 0x55555555; + r ^= t; + l ^= t << 1; + t = ((r >> 8) ^ l) & 0x00FF00FF; + l ^= t; + r ^= t << 8; + t = ((r >> 2) ^ l) & 0x33333333; + l ^= t; + r ^= t << 2; + t = ((l >> 16) ^ r) & 0x0000FFFF; + r ^= t; + l ^= t << 16; + t = ((l >> 4) ^ r) & 0x0F0F0F0F; + r ^= t; + l ^= t << 4; + *xl = l; + *xr = r; +} + +/* see inner.h */ +void +br_des_keysched_unit(uint32_t *skey, const void *key) +{ + uint32_t xl, xr, kl, kr; + int i; + + xl = br_dec32be(key); + xr = br_dec32be((const unsigned char *)key + 4); + + /* + * Permutation PC-1 is quite similar to the IP permutation. + * Definition of IP (in FIPS 46-3 notations) is: + * 58 50 42 34 26 18 10 2 + * 60 52 44 36 28 20 12 4 + * 62 54 46 38 30 22 14 6 + * 64 56 48 40 32 24 16 8 + * 57 49 41 33 25 17 9 1 + * 59 51 43 35 27 19 11 3 + * 61 53 45 37 29 21 13 5 + * 63 55 47 39 31 23 15 7 + * + * Definition of PC-1 is: + * 57 49 41 33 25 17 9 1 + * 58 50 42 34 26 18 10 2 + * 59 51 43 35 27 19 11 3 + * 60 52 44 36 + * 63 55 47 39 31 23 15 7 + * 62 54 46 38 30 22 14 6 + * 61 53 45 37 29 21 13 5 + * 28 20 12 4 + */ + br_des_do_IP(&xl, &xr); + kl = ((xr & (uint32_t)0xFF000000) >> 4) + | ((xl & (uint32_t)0xFF000000) >> 12) + | ((xr & (uint32_t)0x00FF0000) >> 12) + | ((xl & (uint32_t)0x00FF0000) >> 20); + kr = ((xr & (uint32_t)0x000000FF) << 20) + | ((xl & (uint32_t)0x0000FF00) << 4) + | ((xr & (uint32_t)0x0000FF00) >> 4) + | ((xl & (uint32_t)0x000F0000) >> 16); + + /* + * For each round, rotate the two 28-bit words kl and kr. + * The extraction of the 48-bit subkey (PC-2) is not done yet. + */ + for (i = 0; i < 16; i ++) { + if ((1 << i) & 0x8103) { + kl = (kl << 1) | (kl >> 27); + kr = (kr << 1) | (kr >> 27); + } else { + kl = (kl << 2) | (kl >> 26); + kr = (kr << 2) | (kr >> 26); + } + kl &= (uint32_t)0x0FFFFFFF; + kr &= (uint32_t)0x0FFFFFFF; + skey[(i << 1) + 0] = kl; + skey[(i << 1) + 1] = kr; + } +} + +/* see inner.h */ +void +br_des_rev_skey(uint32_t *skey) +{ + int i; + + for (i = 0; i < 16; i += 2) { + uint32_t t; + + t = skey[i + 0]; + skey[i + 0] = skey[30 - i]; + skey[30 - i] = t; + t = skey[i + 1]; + skey[i + 1] = skey[31 - i]; + skey[31 - i] = t; + } +} diff --git a/lib/bearssl-esp8266/src/symcipher/des_tab.c b/lib/bearssl-esp8266/src/symcipher/des_tab.c new file mode 100644 index 000000000..1aefe2147 --- /dev/null +++ b/lib/bearssl-esp8266/src/symcipher/des_tab.c @@ -0,0 +1,310 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * PC2left[x] tells where bit x goes when applying PC-2. 'x' is a bit + * position in the left rotated key word. Both position are in normal + * order (rightmost bit is 0). + */ +static const unsigned char PC2left[] = { + 16, 3, 7, 24, 20, 11, 24, + 13, 2, 10, 24, 22, 5, 15, + 23, 1, 9, 21, 12, 24, 6, + 4, 14, 18, 8, 17, 0, 19 +}; + +/* + * Similar to PC2left[x], for the right rotated key word. + */ +static const unsigned char PC2right[] = { + 8, 18, 24, 6, 22, 15, 3, + 10, 12, 19, 5, 14, 11, 24, + 4, 23, 16, 9, 24, 20, 2, + 24, 7, 13, 0, 21, 17, 1 +}; + +/* + * S-boxes and PC-1 merged. + */ +static const uint32_t S1[] PROGMEM = { + 0x00808200, 0x00000000, 0x00008000, 0x00808202, + 0x00808002, 0x00008202, 0x00000002, 0x00008000, + 0x00000200, 0x00808200, 0x00808202, 0x00000200, + 0x00800202, 0x00808002, 0x00800000, 0x00000002, + 0x00000202, 0x00800200, 0x00800200, 0x00008200, + 0x00008200, 0x00808000, 0x00808000, 0x00800202, + 0x00008002, 0x00800002, 0x00800002, 0x00008002, + 0x00000000, 0x00000202, 0x00008202, 0x00800000, + 0x00008000, 0x00808202, 0x00000002, 0x00808000, + 0x00808200, 0x00800000, 0x00800000, 0x00000200, + 0x00808002, 0x00008000, 0x00008200, 0x00800002, + 0x00000200, 0x00000002, 0x00800202, 0x00008202, + 0x00808202, 0x00008002, 0x00808000, 0x00800202, + 0x00800002, 0x00000202, 0x00008202, 0x00808200, + 0x00000202, 0x00800200, 0x00800200, 0x00000000, + 0x00008002, 0x00008200, 0x00000000, 0x00808002 +}; + +static const uint32_t S2[] PROGMEM = { + 0x40084010, 0x40004000, 0x00004000, 0x00084010, + 0x00080000, 0x00000010, 0x40080010, 0x40004010, + 0x40000010, 0x40084010, 0x40084000, 0x40000000, + 0x40004000, 0x00080000, 0x00000010, 0x40080010, + 0x00084000, 0x00080010, 0x40004010, 0x00000000, + 0x40000000, 0x00004000, 0x00084010, 0x40080000, + 0x00080010, 0x40000010, 0x00000000, 0x00084000, + 0x00004010, 0x40084000, 0x40080000, 0x00004010, + 0x00000000, 0x00084010, 0x40080010, 0x00080000, + 0x40004010, 0x40080000, 0x40084000, 0x00004000, + 0x40080000, 0x40004000, 0x00000010, 0x40084010, + 0x00084010, 0x00000010, 0x00004000, 0x40000000, + 0x00004010, 0x40084000, 0x00080000, 0x40000010, + 0x00080010, 0x40004010, 0x40000010, 0x00080010, + 0x00084000, 0x00000000, 0x40004000, 0x00004010, + 0x40000000, 0x40080010, 0x40084010, 0x00084000 +}; + +static const uint32_t S3[] PROGMEM = { + 0x00000104, 0x04010100, 0x00000000, 0x04010004, + 0x04000100, 0x00000000, 0x00010104, 0x04000100, + 0x00010004, 0x04000004, 0x04000004, 0x00010000, + 0x04010104, 0x00010004, 0x04010000, 0x00000104, + 0x04000000, 0x00000004, 0x04010100, 0x00000100, + 0x00010100, 0x04010000, 0x04010004, 0x00010104, + 0x04000104, 0x00010100, 0x00010000, 0x04000104, + 0x00000004, 0x04010104, 0x00000100, 0x04000000, + 0x04010100, 0x04000000, 0x00010004, 0x00000104, + 0x00010000, 0x04010100, 0x04000100, 0x00000000, + 0x00000100, 0x00010004, 0x04010104, 0x04000100, + 0x04000004, 0x00000100, 0x00000000, 0x04010004, + 0x04000104, 0x00010000, 0x04000000, 0x04010104, + 0x00000004, 0x00010104, 0x00010100, 0x04000004, + 0x04010000, 0x04000104, 0x00000104, 0x04010000, + 0x00010104, 0x00000004, 0x04010004, 0x00010100 +}; + +static const uint32_t S4[] PROGMEM = { + 0x80401000, 0x80001040, 0x80001040, 0x00000040, + 0x00401040, 0x80400040, 0x80400000, 0x80001000, + 0x00000000, 0x00401000, 0x00401000, 0x80401040, + 0x80000040, 0x00000000, 0x00400040, 0x80400000, + 0x80000000, 0x00001000, 0x00400000, 0x80401000, + 0x00000040, 0x00400000, 0x80001000, 0x00001040, + 0x80400040, 0x80000000, 0x00001040, 0x00400040, + 0x00001000, 0x00401040, 0x80401040, 0x80000040, + 0x00400040, 0x80400000, 0x00401000, 0x80401040, + 0x80000040, 0x00000000, 0x00000000, 0x00401000, + 0x00001040, 0x00400040, 0x80400040, 0x80000000, + 0x80401000, 0x80001040, 0x80001040, 0x00000040, + 0x80401040, 0x80000040, 0x80000000, 0x00001000, + 0x80400000, 0x80001000, 0x00401040, 0x80400040, + 0x80001000, 0x00001040, 0x00400000, 0x80401000, + 0x00000040, 0x00400000, 0x00001000, 0x00401040 +}; + +static const uint32_t S5[] PROGMEM = { + 0x00000080, 0x01040080, 0x01040000, 0x21000080, + 0x00040000, 0x00000080, 0x20000000, 0x01040000, + 0x20040080, 0x00040000, 0x01000080, 0x20040080, + 0x21000080, 0x21040000, 0x00040080, 0x20000000, + 0x01000000, 0x20040000, 0x20040000, 0x00000000, + 0x20000080, 0x21040080, 0x21040080, 0x01000080, + 0x21040000, 0x20000080, 0x00000000, 0x21000000, + 0x01040080, 0x01000000, 0x21000000, 0x00040080, + 0x00040000, 0x21000080, 0x00000080, 0x01000000, + 0x20000000, 0x01040000, 0x21000080, 0x20040080, + 0x01000080, 0x20000000, 0x21040000, 0x01040080, + 0x20040080, 0x00000080, 0x01000000, 0x21040000, + 0x21040080, 0x00040080, 0x21000000, 0x21040080, + 0x01040000, 0x00000000, 0x20040000, 0x21000000, + 0x00040080, 0x01000080, 0x20000080, 0x00040000, + 0x00000000, 0x20040000, 0x01040080, 0x20000080 +}; + +static const uint32_t S6[] PROGMEM= { + 0x10000008, 0x10200000, 0x00002000, 0x10202008, + 0x10200000, 0x00000008, 0x10202008, 0x00200000, + 0x10002000, 0x00202008, 0x00200000, 0x10000008, + 0x00200008, 0x10002000, 0x10000000, 0x00002008, + 0x00000000, 0x00200008, 0x10002008, 0x00002000, + 0x00202000, 0x10002008, 0x00000008, 0x10200008, + 0x10200008, 0x00000000, 0x00202008, 0x10202000, + 0x00002008, 0x00202000, 0x10202000, 0x10000000, + 0x10002000, 0x00000008, 0x10200008, 0x00202000, + 0x10202008, 0x00200000, 0x00002008, 0x10000008, + 0x00200000, 0x10002000, 0x10000000, 0x00002008, + 0x10000008, 0x10202008, 0x00202000, 0x10200000, + 0x00202008, 0x10202000, 0x00000000, 0x10200008, + 0x00000008, 0x00002000, 0x10200000, 0x00202008, + 0x00002000, 0x00200008, 0x10002008, 0x00000000, + 0x10202000, 0x10000000, 0x00200008, 0x10002008 +}; + +static const uint32_t S7[] PROGMEM= { + 0x00100000, 0x02100001, 0x02000401, 0x00000000, + 0x00000400, 0x02000401, 0x00100401, 0x02100400, + 0x02100401, 0x00100000, 0x00000000, 0x02000001, + 0x00000001, 0x02000000, 0x02100001, 0x00000401, + 0x02000400, 0x00100401, 0x00100001, 0x02000400, + 0x02000001, 0x02100000, 0x02100400, 0x00100001, + 0x02100000, 0x00000400, 0x00000401, 0x02100401, + 0x00100400, 0x00000001, 0x02000000, 0x00100400, + 0x02000000, 0x00100400, 0x00100000, 0x02000401, + 0x02000401, 0x02100001, 0x02100001, 0x00000001, + 0x00100001, 0x02000000, 0x02000400, 0x00100000, + 0x02100400, 0x00000401, 0x00100401, 0x02100400, + 0x00000401, 0x02000001, 0x02100401, 0x02100000, + 0x00100400, 0x00000000, 0x00000001, 0x02100401, + 0x00000000, 0x00100401, 0x02100000, 0x00000400, + 0x02000001, 0x02000400, 0x00000400, 0x00100001 +}; + +static const uint32_t S8[] PROGMEM = { + 0x08000820, 0x00000800, 0x00020000, 0x08020820, + 0x08000000, 0x08000820, 0x00000020, 0x08000000, + 0x00020020, 0x08020000, 0x08020820, 0x00020800, + 0x08020800, 0x00020820, 0x00000800, 0x00000020, + 0x08020000, 0x08000020, 0x08000800, 0x00000820, + 0x00020800, 0x00020020, 0x08020020, 0x08020800, + 0x00000820, 0x00000000, 0x00000000, 0x08020020, + 0x08000020, 0x08000800, 0x00020820, 0x00020000, + 0x00020820, 0x00020000, 0x08020800, 0x00000800, + 0x00000020, 0x08020020, 0x00000800, 0x00020820, + 0x08000800, 0x00000020, 0x08000020, 0x08020000, + 0x08020020, 0x08000000, 0x00020000, 0x08000820, + 0x00000000, 0x08020820, 0x00020020, 0x08000020, + 0x08020000, 0x08000800, 0x08000820, 0x00000000, + 0x08020820, 0x00020800, 0x00020800, 0x00000820, + 0x00000820, 0x00020020, 0x08000000, 0x08020800 +}; + +static inline uint32_t +Fconf(uint32_t r0, uint32_t skl, uint32_t skr) +{ + uint32_t r1; + + r1 = (r0 << 16) | (r0 >> 16); + return + S1[((r1 >> 11) ^ (skl >> 18)) & 0x3F] + | S2[((r0 >> 23) ^ (skl >> 12)) & 0x3F] + | S3[((r0 >> 19) ^ (skl >> 6)) & 0x3F] + | S4[((r0 >> 15) ^ (skl )) & 0x3F] + | S5[((r0 >> 11) ^ (skr >> 18)) & 0x3F] + | S6[((r0 >> 7) ^ (skr >> 12)) & 0x3F] + | S7[((r0 >> 3) ^ (skr >> 6)) & 0x3F] + | S8[((r1 >> 15) ^ (skr )) & 0x3F]; +} + +static void +process_block_unit(uint32_t *pl, uint32_t *pr, const uint32_t *skey) +{ + int i; + uint32_t l, r; + + l = *pl; + r = *pr; + for (i = 0; i < 16; i ++) { + uint32_t t; + + t = l ^ Fconf(r, skey[(i << 1) + 0], skey[(i << 1) + 1]); + l = r; + r = t; + } + *pl = r; + *pr = l; +} + +/* see inner.h */ +void +br_des_tab_process_block(unsigned num_rounds, const uint32_t *skey, void *block) +{ + unsigned char *buf; + uint32_t l, r; + + buf = block; + l = br_dec32be(buf); + r = br_dec32be(buf + 4); + br_des_do_IP(&l, &r); + while (num_rounds -- > 0) { + process_block_unit(&l, &r, skey); + skey += 32; + } + br_des_do_invIP(&l, &r); + br_enc32be(buf, l); + br_enc32be(buf + 4, r); +} + +static void +keysched_unit(uint32_t *skey, const void *key) +{ + int i; + + br_des_keysched_unit(skey, key); + + /* + * Apply PC-2 to get the 48-bit subkeys. + */ + for (i = 0; i < 16; i ++) { + uint32_t xl, xr, ul, ur; + int j; + + xl = skey[(i << 1) + 0]; + xr = skey[(i << 1) + 1]; + ul = 0; + ur = 0; + for (j = 0; j < 28; j ++) { + ul |= (xl & 1) << PC2left[j]; + ur |= (xr & 1) << PC2right[j]; + xl >>= 1; + xr >>= 1; + } + skey[(i << 1) + 0] = ul; + skey[(i << 1) + 1] = ur; + } +} + +/* see inner.h */ +unsigned +br_des_tab_keysched(uint32_t *skey, const void *key, size_t key_len) +{ + switch (key_len) { + case 8: + keysched_unit(skey, key); + return 1; + case 16: + keysched_unit(skey, key); + keysched_unit(skey + 32, (const unsigned char *)key + 8); + br_des_rev_skey(skey + 32); + memcpy(skey + 64, skey, 32 * sizeof *skey); + return 3; + default: + keysched_unit(skey, key); + keysched_unit(skey + 32, (const unsigned char *)key + 8); + br_des_rev_skey(skey + 32); + keysched_unit(skey + 64, (const unsigned char *)key + 16); + return 3; + } +} diff --git a/lib/bearssl-esp8266/src/symcipher/des_tab_cbcdec.c b/lib/bearssl-esp8266/src/symcipher/des_tab_cbcdec.c new file mode 100644 index 000000000..647db004e --- /dev/null +++ b/lib/bearssl-esp8266/src/symcipher/des_tab_cbcdec.c @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_block.h */ +void +br_des_tab_cbcdec_init(br_des_tab_cbcdec_keys *ctx, + const void *key, size_t len) +{ + ctx->vtable = &br_des_tab_cbcdec_vtable; + ctx->num_rounds = br_des_tab_keysched(ctx->skey, key, len); + if (len == 8) { + br_des_rev_skey(ctx->skey); + } else { + int i; + + for (i = 0; i < 48; i += 2) { + uint32_t t; + + t = ctx->skey[i]; + ctx->skey[i] = ctx->skey[94 - i]; + ctx->skey[94 - i] = t; + t = ctx->skey[i + 1]; + ctx->skey[i + 1] = ctx->skey[95 - i]; + ctx->skey[95 - i] = t; + } + } +} + +/* see bearssl_block.h */ +void +br_des_tab_cbcdec_run(const br_des_tab_cbcdec_keys *ctx, + void *iv, void *data, size_t len) +{ + unsigned char *buf, *ivbuf; + + ivbuf = iv; + buf = data; + while (len > 0) { + unsigned char tmp[8]; + int i; + + memcpy(tmp, buf, 8); + br_des_tab_process_block(ctx->num_rounds, ctx->skey, buf); + for (i = 0; i < 8; i ++) { + buf[i] ^= ivbuf[i]; + } + memcpy(ivbuf, tmp, 8); + buf += 8; + len -= 8; + } +} + +/* see bearssl_block.h */ +const br_block_cbcdec_class br_des_tab_cbcdec_vtable PROGMEM = { + sizeof(br_des_tab_cbcdec_keys), + 8, + 3, + (void (*)(const br_block_cbcdec_class **, const void *, size_t)) + &br_des_tab_cbcdec_init, + (void (*)(const br_block_cbcdec_class *const *, void *, void *, size_t)) + &br_des_tab_cbcdec_run +}; diff --git a/lib/bearssl-esp8266/src/symcipher/des_tab_cbcenc.c b/lib/bearssl-esp8266/src/symcipher/des_tab_cbcenc.c new file mode 100644 index 000000000..a7ecee89c --- /dev/null +++ b/lib/bearssl-esp8266/src/symcipher/des_tab_cbcenc.c @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_block.h */ +void +br_des_tab_cbcenc_init(br_des_tab_cbcenc_keys *ctx, + const void *key, size_t len) +{ + ctx->vtable = &br_des_tab_cbcenc_vtable; + ctx->num_rounds = br_des_tab_keysched(ctx->skey, key, len); +} + +/* see bearssl_block.h */ +void +br_des_tab_cbcenc_run(const br_des_tab_cbcenc_keys *ctx, + void *iv, void *data, size_t len) +{ + unsigned char *buf, *ivbuf; + + ivbuf = iv; + buf = data; + while (len > 0) { + int i; + + for (i = 0; i < 8; i ++) { + buf[i] ^= ivbuf[i]; + } + br_des_tab_process_block(ctx->num_rounds, ctx->skey, buf); + memcpy(ivbuf, buf, 8); + buf += 8; + len -= 8; + } +} + +/* see bearssl_block.h */ +const br_block_cbcenc_class br_des_tab_cbcenc_vtable PROGMEM = { + sizeof(br_des_tab_cbcenc_keys), + 8, + 3, + (void (*)(const br_block_cbcenc_class **, const void *, size_t)) + &br_des_tab_cbcenc_init, + (void (*)(const br_block_cbcenc_class *const *, void *, void *, size_t)) + &br_des_tab_cbcenc_run +}; diff --git a/lib/bearssl-esp8266/src/symcipher/poly1305_ctmul.c b/lib/bearssl-esp8266/src/symcipher/poly1305_ctmul.c new file mode 100644 index 000000000..868e12f2b --- /dev/null +++ b/lib/bearssl-esp8266/src/symcipher/poly1305_ctmul.c @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * Perform the inner processing of blocks for Poly1305. The accumulator + * and the r key are provided as arrays of 26-bit words (these words + * are allowed to have an extra bit, i.e. use 27 bits). + * + * On output, all accumulator words fit on 26 bits, except acc[1], which + * may be slightly larger (but by a very small amount only). + */ +static void +poly1305_inner(uint32_t *acc, const uint32_t *r, const void *data, size_t len) +{ + /* + * Implementation notes: we split the 130-bit values into five + * 26-bit words. This gives us some space for carries. + * + * This code is inspired from the public-domain code available + * on: + * https://github.com/floodyberry/poly1305-donna + * + * Since we compute modulo 2^130-5, the "upper words" become + * low words with a factor of 5; that is, x*2^130 = x*5 mod p. + */ + const unsigned char *buf; + uint32_t a0, a1, a2, a3, a4; + uint32_t r0, r1, r2, r3, r4; + uint32_t u1, u2, u3, u4; + + r0 = r[0]; + r1 = r[1]; + r2 = r[2]; + r3 = r[3]; + r4 = r[4]; + + u1 = r1 * 5; + u2 = r2 * 5; + u3 = r3 * 5; + u4 = r4 * 5; + + a0 = acc[0]; + a1 = acc[1]; + a2 = acc[2]; + a3 = acc[3]; + a4 = acc[4]; + + buf = data; + while (len > 0) { + uint64_t w0, w1, w2, w3, w4; + uint64_t c; + unsigned char tmp[16]; + + /* + * If there is a partial block, right-pad it with zeros. + */ + if (len < 16) { + memset(tmp, 0, sizeof tmp); + memcpy(tmp, buf, len); + buf = tmp; + len = 16; + } + + /* + * Decode next block and apply the "high bit"; that value + * is added to the accumulator. + */ + a0 += br_dec32le(buf) & 0x03FFFFFF; + a1 += (br_dec32le(buf + 3) >> 2) & 0x03FFFFFF; + a2 += (br_dec32le(buf + 6) >> 4) & 0x03FFFFFF; + a3 += (br_dec32le(buf + 9) >> 6) & 0x03FFFFFF; + a4 += (br_dec32le(buf + 12) >> 8) | 0x01000000; + + /* + * Compute multiplication. + */ +#define M(x, y) ((uint64_t)(x) * (uint64_t)(y)) + + w0 = M(a0, r0) + M(a1, u4) + M(a2, u3) + M(a3, u2) + M(a4, u1); + w1 = M(a0, r1) + M(a1, r0) + M(a2, u4) + M(a3, u3) + M(a4, u2); + w2 = M(a0, r2) + M(a1, r1) + M(a2, r0) + M(a3, u4) + M(a4, u3); + w3 = M(a0, r3) + M(a1, r2) + M(a2, r1) + M(a3, r0) + M(a4, u4); + w4 = M(a0, r4) + M(a1, r3) + M(a2, r2) + M(a3, r1) + M(a4, r0); + +#undef M + /* + * Perform some (partial) modular reduction. This step is + * enough to keep values in ranges such that there won't + * be carry overflows. Most of the reduction was done in + * the multiplication step (by using the 'u*' values, and + * using the fact that 2^130 = -5 mod p); here we perform + * some carry propagation. + */ + c = w0 >> 26; + a0 = (uint32_t)w0 & 0x3FFFFFF; + w1 += c; + c = w1 >> 26; + a1 = (uint32_t)w1 & 0x3FFFFFF; + w2 += c; + c = w2 >> 26; + a2 = (uint32_t)w2 & 0x3FFFFFF; + w3 += c; + c = w3 >> 26; + a3 = (uint32_t)w3 & 0x3FFFFFF; + w4 += c; + c = w4 >> 26; + a4 = (uint32_t)w4 & 0x3FFFFFF; + a0 += (uint32_t)c * 5; + a1 += a0 >> 26; + a0 &= 0x3FFFFFF; + + buf += 16; + len -= 16; + } + + acc[0] = a0; + acc[1] = a1; + acc[2] = a2; + acc[3] = a3; + acc[4] = a4; +} + +/* see bearssl_block.h */ +void +br_poly1305_ctmul_run(const void *key, const void *iv, + void *data, size_t len, const void *aad, size_t aad_len, + void *tag, br_chacha20_run ichacha, int encrypt) +{ + unsigned char pkey[32], foot[16]; + uint32_t r[5], acc[5], cc, ctl, hi; + uint64_t w; + int i; + + /* + * Compute the MAC key. The 'r' value is the first 16 bytes of + * pkey[]. + */ + memset(pkey, 0, sizeof pkey); + ichacha(key, iv, 0, pkey, sizeof pkey); + + /* + * If encrypting, ChaCha20 must run first, followed by Poly1305. + * When decrypting, the operations are reversed. + */ + if (encrypt) { + ichacha(key, iv, 1, data, len); + } + + /* + * Run Poly1305. We must process the AAD, then ciphertext, then + * the footer (with the lengths). Note that the AAD and ciphertext + * are meant to be padded with zeros up to the next multiple of 16, + * and the length of the footer is 16 bytes as well. + */ + + /* + * Decode the 'r' value into 26-bit words, with the "clamping" + * operation applied. + */ + r[0] = br_dec32le(pkey) & 0x03FFFFFF; + r[1] = (br_dec32le(pkey + 3) >> 2) & 0x03FFFF03; + r[2] = (br_dec32le(pkey + 6) >> 4) & 0x03FFC0FF; + r[3] = (br_dec32le(pkey + 9) >> 6) & 0x03F03FFF; + r[4] = (br_dec32le(pkey + 12) >> 8) & 0x000FFFFF; + + /* + * Accumulator is 0. + */ + memset(acc, 0, sizeof acc); + + /* + * Process the additional authenticated data, ciphertext, and + * footer in due order. + */ + br_enc64le(foot, (uint64_t)aad_len); + br_enc64le(foot + 8, (uint64_t)len); + poly1305_inner(acc, r, aad, aad_len); + poly1305_inner(acc, r, data, len); + poly1305_inner(acc, r, foot, sizeof foot); + + /* + * Finalise modular reduction. This is done with carry propagation + * and applying the '2^130 = -5 mod p' rule. Note that the output + * of poly1035_inner() is already mostly reduced, since only + * acc[1] may be (very slightly) above 2^26. A single loop back + * to acc[1] will be enough to make the value fit in 130 bits. + */ + cc = 0; + for (i = 1; i <= 6; i ++) { + int j; + + j = (i >= 5) ? i - 5 : i; + acc[j] += cc; + cc = acc[j] >> 26; + acc[j] &= 0x03FFFFFF; + } + + /* + * We may still have a value in the 2^130-5..2^130-1 range, in + * which case we must reduce it again. The code below selects, + * in constant-time, between 'acc' and 'acc-p', + */ + ctl = GT(acc[0], 0x03FFFFFA); + for (i = 1; i < 5; i ++) { + ctl &= EQ(acc[i], 0x03FFFFFF); + } + cc = 5; + for (i = 0; i < 5; i ++) { + uint32_t t; + + t = (acc[i] + cc); + cc = t >> 26; + t &= 0x03FFFFFF; + acc[i] = MUX(ctl, t, acc[i]); + } + + /* + * Convert back the accumulator to 32-bit words, and add the + * 's' value (second half of pkey[]). That addition is done + * modulo 2^128. + */ + w = (uint64_t)acc[0] + ((uint64_t)acc[1] << 26) + br_dec32le(pkey + 16); + br_enc32le((unsigned char *)tag, (uint32_t)w); + w = (w >> 32) + ((uint64_t)acc[2] << 20) + br_dec32le(pkey + 20); + br_enc32le((unsigned char *)tag + 4, (uint32_t)w); + w = (w >> 32) + ((uint64_t)acc[3] << 14) + br_dec32le(pkey + 24); + br_enc32le((unsigned char *)tag + 8, (uint32_t)w); + hi = (uint32_t)(w >> 32) + (acc[4] << 8) + br_dec32le(pkey + 28); + br_enc32le((unsigned char *)tag + 12, hi); + + /* + * If decrypting, then ChaCha20 runs _after_ Poly1305. + */ + if (!encrypt) { + ichacha(key, iv, 1, data, len); + } +} diff --git a/lib/bearssl-esp8266/src/symcipher/poly1305_ctmul32.c b/lib/bearssl-esp8266/src/symcipher/poly1305_ctmul32.c new file mode 100644 index 000000000..c2d90941c --- /dev/null +++ b/lib/bearssl-esp8266/src/symcipher/poly1305_ctmul32.c @@ -0,0 +1,297 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * Perform the inner processing of blocks for Poly1305. + */ +static void +poly1305_inner(uint32_t *a, const uint32_t *r, const void *data, size_t len) +{ + /* + * Implementation notes: we split the 130-bit values into ten + * 13-bit words. This gives us some space for carries and allows + * using only 32x32->32 multiplications, which are way faster than + * 32x32->64 multiplications on the ARM Cortex-M0/M0+, and also + * help in making constant-time code on the Cortex-M3. + * + * Since we compute modulo 2^130-5, the "upper words" become + * low words with a factor of 5; that is, x*2^130 = x*5 mod p. + * This has already been integrated in the r[] array, which + * is extended to the 0..18 range. + * + * In each loop iteration, a[] and r[] words are 13-bit each, + * except a[1] which may use 14 bits. + */ + const unsigned char *buf; + + buf = data; + while (len > 0) { + unsigned char tmp[16]; + uint32_t b[10]; + unsigned u, v; + uint32_t z, cc1, cc2; + + /* + * If there is a partial block, right-pad it with zeros. + */ + if (len < 16) { + memset(tmp, 0, sizeof tmp); + memcpy(tmp, buf, len); + buf = tmp; + len = 16; + } + + /* + * Decode next block and apply the "high bit"; that value + * is added to the accumulator. + */ + v = br_dec16le(buf); + a[0] += v & 0x01FFF; + v >>= 13; + v |= buf[2] << 3; + v |= buf[3] << 11; + a[1] += v & 0x01FFF; + v >>= 13; + v |= buf[4] << 6; + a[2] += v & 0x01FFF; + v >>= 13; + v |= buf[5] << 1; + v |= buf[6] << 9; + a[3] += v & 0x01FFF; + v >>= 13; + v |= buf[7] << 4; + v |= buf[8] << 12; + a[4] += v & 0x01FFF; + v >>= 13; + v |= buf[9] << 7; + a[5] += v & 0x01FFF; + v >>= 13; + v |= buf[10] << 2; + v |= buf[11] << 10; + a[6] += v & 0x01FFF; + v >>= 13; + v |= buf[12] << 5; + a[7] += v & 0x01FFF; + v = br_dec16le(buf + 13); + a[8] += v & 0x01FFF; + v >>= 13; + v |= buf[15] << 3; + a[9] += v | 0x00800; + + /* + * At that point, all a[] values fit on 14 bits, while + * all r[] values fit on 13 bits. Thus products fit on + * 27 bits, and we can accumulate up to 31 of them in + * a 32-bit word and still have some room for carries. + */ + + /* + * Now a[] contains words with values up to 14 bits each. + * We perform the multiplication with r[]. + * + * The extended words of r[] may be larger than 13 bits + * (they are 5 times a 13-bit word) so the full summation + * may yield values up to 46 times a 27-bit word, which + * does not fit on a 32-bit word. To avoid that issue, we + * must split the loop below in two, with a carry + * propagation operation in the middle. + */ + cc1 = 0; + for (u = 0; u < 10; u ++) { + uint32_t s; + + s = cc1 + + MUL15(a[0], r[u + 9 - 0]) + + MUL15(a[1], r[u + 9 - 1]) + + MUL15(a[2], r[u + 9 - 2]) + + MUL15(a[3], r[u + 9 - 3]) + + MUL15(a[4], r[u + 9 - 4]); + b[u] = s & 0x1FFF; + cc1 = s >> 13; + } + cc2 = 0; + for (u = 0; u < 10; u ++) { + uint32_t s; + + s = b[u] + cc2 + + MUL15(a[5], r[u + 9 - 5]) + + MUL15(a[6], r[u + 9 - 6]) + + MUL15(a[7], r[u + 9 - 7]) + + MUL15(a[8], r[u + 9 - 8]) + + MUL15(a[9], r[u + 9 - 9]); + b[u] = s & 0x1FFF; + cc2 = s >> 13; + } + memcpy(a, b, sizeof b); + + /* + * The two carries "loop back" with a factor of 5. We + * propagate them into a[0] and a[1]. + */ + z = cc1 + cc2; + z += (z << 2) + a[0]; + a[0] = z & 0x1FFF; + a[1] += z >> 13; + + buf += 16; + len -= 16; + } +} + +/* see bearssl_block.h */ +void +br_poly1305_ctmul32_run(const void *key, const void *iv, + void *data, size_t len, const void *aad, size_t aad_len, + void *tag, br_chacha20_run ichacha, int encrypt) +{ + unsigned char pkey[32], foot[16]; + uint32_t z, r[19], acc[10], cc, ctl; + int i; + + /* + * Compute the MAC key. The 'r' value is the first 16 bytes of + * pkey[]. + */ + memset(pkey, 0, sizeof pkey); + ichacha(key, iv, 0, pkey, sizeof pkey); + + /* + * If encrypting, ChaCha20 must run first, followed by Poly1305. + * When decrypting, the operations are reversed. + */ + if (encrypt) { + ichacha(key, iv, 1, data, len); + } + + /* + * Run Poly1305. We must process the AAD, then ciphertext, then + * the footer (with the lengths). Note that the AAD and ciphertext + * are meant to be padded with zeros up to the next multiple of 16, + * and the length of the footer is 16 bytes as well. + */ + + /* + * Decode the 'r' value into 13-bit words, with the "clamping" + * operation applied. + */ + z = br_dec32le(pkey) & 0x03FFFFFF; + r[9] = z & 0x1FFF; + r[10] = z >> 13; + z = (br_dec32le(pkey + 3) >> 2) & 0x03FFFF03; + r[11] = z & 0x1FFF; + r[12] = z >> 13; + z = (br_dec32le(pkey + 6) >> 4) & 0x03FFC0FF; + r[13] = z & 0x1FFF; + r[14] = z >> 13; + z = (br_dec32le(pkey + 9) >> 6) & 0x03F03FFF; + r[15] = z & 0x1FFF; + r[16] = z >> 13; + z = (br_dec32le(pkey + 12) >> 8) & 0x000FFFFF; + r[17] = z & 0x1FFF; + r[18] = z >> 13; + + /* + * Extend r[] with the 5x factor pre-applied. + */ + for (i = 0; i < 9; i ++) { + r[i] = MUL15(5, r[i + 10]); + } + + /* + * Accumulator is 0. + */ + memset(acc, 0, sizeof acc); + + /* + * Process the additional authenticated data, ciphertext, and + * footer in due order. + */ + br_enc64le(foot, (uint64_t)aad_len); + br_enc64le(foot + 8, (uint64_t)len); + poly1305_inner(acc, r, aad, aad_len); + poly1305_inner(acc, r, data, len); + poly1305_inner(acc, r, foot, sizeof foot); + + /* + * Finalise modular reduction. This is done with carry propagation + * and applying the '2^130 = -5 mod p' rule. Note that the output + * of poly1035_inner() is already mostly reduced, since only + * acc[1] may be (very slightly) above 2^13. A single loop back + * to acc[1] will be enough to make the value fit in 130 bits. + */ + cc = 0; + for (i = 1; i < 10; i ++) { + z = acc[i] + cc; + acc[i] = z & 0x1FFF; + cc = z >> 13; + } + z = acc[0] + cc + (cc << 2); + acc[0] = z & 0x1FFF; + acc[1] += z >> 13; + + /* + * We may still have a value in the 2^130-5..2^130-1 range, in + * which case we must reduce it again. The code below selects, + * in constant-time, between 'acc' and 'acc-p', + */ + ctl = GT(acc[0], 0x1FFA); + for (i = 1; i < 10; i ++) { + ctl &= EQ(acc[i], 0x1FFF); + } + acc[0] = MUX(ctl, acc[0] - 0x1FFB, acc[0]); + for (i = 1; i < 10; i ++) { + acc[i] &= ~(-ctl); + } + + /* + * Convert back the accumulator to 32-bit words, and add the + * 's' value (second half of pkey[]). That addition is done + * modulo 2^128. + */ + z = acc[0] + (acc[1] << 13) + br_dec16le(pkey + 16); + br_enc16le((unsigned char *)tag, z & 0xFFFF); + z = (z >> 16) + (acc[2] << 10) + br_dec16le(pkey + 18); + br_enc16le((unsigned char *)tag + 2, z & 0xFFFF); + z = (z >> 16) + (acc[3] << 7) + br_dec16le(pkey + 20); + br_enc16le((unsigned char *)tag + 4, z & 0xFFFF); + z = (z >> 16) + (acc[4] << 4) + br_dec16le(pkey + 22); + br_enc16le((unsigned char *)tag + 6, z & 0xFFFF); + z = (z >> 16) + (acc[5] << 1) + (acc[6] << 14) + br_dec16le(pkey + 24); + br_enc16le((unsigned char *)tag + 8, z & 0xFFFF); + z = (z >> 16) + (acc[7] << 11) + br_dec16le(pkey + 26); + br_enc16le((unsigned char *)tag + 10, z & 0xFFFF); + z = (z >> 16) + (acc[8] << 8) + br_dec16le(pkey + 28); + br_enc16le((unsigned char *)tag + 12, z & 0xFFFF); + z = (z >> 16) + (acc[9] << 5) + br_dec16le(pkey + 30); + br_enc16le((unsigned char *)tag + 14, z & 0xFFFF); + + /* + * If decrypting, then ChaCha20 runs _after_ Poly1305. + */ + if (!encrypt) { + ichacha(key, iv, 1, data, len); + } +} diff --git a/lib/bearssl-esp8266/src/symcipher/poly1305_ctmulq.c b/lib/bearssl-esp8266/src/symcipher/poly1305_ctmulq.c new file mode 100644 index 000000000..6b09445ce --- /dev/null +++ b/lib/bearssl-esp8266/src/symcipher/poly1305_ctmulq.c @@ -0,0 +1,475 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +#if BR_INT128 || BR_UMUL128 + +#if BR_INT128 + +#define MUL128(hi, lo, x, y) do { \ + unsigned __int128 mul128tmp; \ + mul128tmp = (unsigned __int128)(x) * (unsigned __int128)(y); \ + (hi) = (uint64_t)(mul128tmp >> 64); \ + (lo) = (uint64_t)mul128tmp; \ + } while (0) + +#elif BR_UMUL128 + +#include + +#define MUL128(hi, lo, x, y) do { \ + (lo) = _umul128((x), (y), &(hi)); \ + } while (0) + +#endif + +#define MASK42 ((uint64_t)0x000003FFFFFFFFFF) +#define MASK44 ((uint64_t)0x00000FFFFFFFFFFF) + +/* + * The "accumulator" word is nominally a 130-bit value. We split it into + * words of 44 bits, each held in a 64-bit variable. + * + * If the current accumulator is a = a0 + a1*W + a2*W^2 (where W = 2^44) + * and r = r0 + r1*W + r2*W^2, then: + * + * a*r = (a0*r0) + * + (a0*r1 + a1*r0) * W + * + (a0*r2 + a1*r1 + a2*r0) * W^2 + * + (a1*r2 + a2*r1) * W^3 + * + (a2*r2) * W^4 + * + * We want to reduce that value modulo p = 2^130-5, so W^3 = 20 mod p, + * and W^4 = 20*W mod p. Thus, if we define u1 = 20*r1 and u2 = 20*r2, + * then the equations above become: + * + * b0 = a0*r0 + a1*u2 + a2*u1 + * b1 = a0*r1 + a1*r0 + a2*u2 + * b2 = a0*r2 + a1*r1 + a2*r0 + * + * In order to make u1 fit in 44 bits, we can change these equations + * into: + * + * b0 = a0*r0 + a1*u2 + a2*t1 + * b1 = a0*r1 + a1*r0 + a2*t2 + * b2 = a0*r2 + a1*r1 + a2*r0 + * + * Where t1 is u1 truncated to 44 bits, and t2 is u2 added to the extra + * bits of u1. Note that since r is clamped down to a 124-bit value, the + * values u2 and t2 fit on 44 bits too. + * + * The bx values are larger than 44 bits, so we may split them into a + * lower half (cx, 44 bits) and an upper half (dx). The new values for + * the accumulator are then: + * + * e0 = c0 + 20*d2 + * e1 = c1 + d0 + * e2 = c2 + d1 + * + * The equations allow for some room, i.e. the ax values may be larger + * than 44 bits. Similarly, the ex values will usually be larger than + * the ax. Thus, some sort of carry propagation must be done regularly, + * though not necessarily at each iteration. In particular, we do not + * need to compute the additions (for the bx values) over 128-bit + * quantities; we can stick to 64-bit computations. + * + * + * Since the 128-bit result of a 64x64 multiplication is actually + * represented over two 64-bit registers, it is cheaper to arrange for + * any split that happens between the "high" and "low" halves to be on + * that 64-bit boundary. This is done by left shifting the rx, ux and tx + * by 20 bits (since they all fit on 44 bits each, this shift is + * always possible). + */ + +static void +poly1305_inner_big(uint64_t *acc, uint64_t *r, const void *data, size_t len) +{ + +#define MX(hi, lo, m0, m1, m2) do { \ + uint64_t mxhi, mxlo; \ + MUL128(mxhi, mxlo, a0, m0); \ + (hi) = mxhi; \ + (lo) = mxlo >> 20; \ + MUL128(mxhi, mxlo, a1, m1); \ + (hi) += mxhi; \ + (lo) += mxlo >> 20; \ + MUL128(mxhi, mxlo, a2, m2); \ + (hi) += mxhi; \ + (lo) += mxlo >> 20; \ + } while (0) + + const unsigned char *buf; + uint64_t a0, a1, a2; + uint64_t r0, r1, r2, t1, t2, u2; + + r0 = r[0]; + r1 = r[1]; + r2 = r[2]; + t1 = r[3]; + t2 = r[4]; + u2 = r[5]; + a0 = acc[0]; + a1 = acc[1]; + a2 = acc[2]; + buf = data; + + while (len > 0) { + uint64_t v0, v1, v2; + uint64_t c0, c1, c2, d0, d1, d2; + + v0 = br_dec64le(buf + 0); + v1 = br_dec64le(buf + 8); + v2 = v1 >> 24; + v1 = ((v0 >> 44) | (v1 << 20)) & MASK44; + v0 &= MASK44; + a0 += v0; + a1 += v1; + a2 += v2 + ((uint64_t)1 << 40); + MX(d0, c0, r0, u2, t1); + MX(d1, c1, r1, r0, t2); + MX(d2, c2, r2, r1, r0); + a0 = c0 + 20 * d2; + a1 = c1 + d0; + a2 = c2 + d1; + + v0 = br_dec64le(buf + 16); + v1 = br_dec64le(buf + 24); + v2 = v1 >> 24; + v1 = ((v0 >> 44) | (v1 << 20)) & MASK44; + v0 &= MASK44; + a0 += v0; + a1 += v1; + a2 += v2 + ((uint64_t)1 << 40); + MX(d0, c0, r0, u2, t1); + MX(d1, c1, r1, r0, t2); + MX(d2, c2, r2, r1, r0); + a0 = c0 + 20 * d2; + a1 = c1 + d0; + a2 = c2 + d1; + + v0 = br_dec64le(buf + 32); + v1 = br_dec64le(buf + 40); + v2 = v1 >> 24; + v1 = ((v0 >> 44) | (v1 << 20)) & MASK44; + v0 &= MASK44; + a0 += v0; + a1 += v1; + a2 += v2 + ((uint64_t)1 << 40); + MX(d0, c0, r0, u2, t1); + MX(d1, c1, r1, r0, t2); + MX(d2, c2, r2, r1, r0); + a0 = c0 + 20 * d2; + a1 = c1 + d0; + a2 = c2 + d1; + + v0 = br_dec64le(buf + 48); + v1 = br_dec64le(buf + 56); + v2 = v1 >> 24; + v1 = ((v0 >> 44) | (v1 << 20)) & MASK44; + v0 &= MASK44; + a0 += v0; + a1 += v1; + a2 += v2 + ((uint64_t)1 << 40); + MX(d0, c0, r0, u2, t1); + MX(d1, c1, r1, r0, t2); + MX(d2, c2, r2, r1, r0); + a0 = c0 + 20 * d2; + a1 = c1 + d0; + a2 = c2 + d1; + + a1 += a0 >> 44; + a0 &= MASK44; + a2 += a1 >> 44; + a1 &= MASK44; + a0 += 20 * (a2 >> 44); + a2 &= MASK44; + + buf += 64; + len -= 64; + } + acc[0] = a0; + acc[1] = a1; + acc[2] = a2; + +#undef MX +} + +static void +poly1305_inner_small(uint64_t *acc, uint64_t *r, const void *data, size_t len) +{ + const unsigned char *buf; + uint64_t a0, a1, a2; + uint64_t r0, r1, r2, t1, t2, u2; + + r0 = r[0]; + r1 = r[1]; + r2 = r[2]; + t1 = r[3]; + t2 = r[4]; + u2 = r[5]; + a0 = acc[0]; + a1 = acc[1]; + a2 = acc[2]; + buf = data; + + while (len > 0) { + uint64_t v0, v1, v2; + uint64_t c0, c1, c2, d0, d1, d2; + unsigned char tmp[16]; + + if (len < 16) { + memcpy(tmp, buf, len); + memset(tmp + len, 0, (sizeof tmp) - len); + buf = tmp; + len = 16; + } + v0 = br_dec64le(buf + 0); + v1 = br_dec64le(buf + 8); + + v2 = v1 >> 24; + v1 = ((v0 >> 44) | (v1 << 20)) & MASK44; + v0 &= MASK44; + + a0 += v0; + a1 += v1; + a2 += v2 + ((uint64_t)1 << 40); + +#define MX(hi, lo, m0, m1, m2) do { \ + uint64_t mxhi, mxlo; \ + MUL128(mxhi, mxlo, a0, m0); \ + (hi) = mxhi; \ + (lo) = mxlo >> 20; \ + MUL128(mxhi, mxlo, a1, m1); \ + (hi) += mxhi; \ + (lo) += mxlo >> 20; \ + MUL128(mxhi, mxlo, a2, m2); \ + (hi) += mxhi; \ + (lo) += mxlo >> 20; \ + } while (0) + + MX(d0, c0, r0, u2, t1); + MX(d1, c1, r1, r0, t2); + MX(d2, c2, r2, r1, r0); + +#undef MX + + a0 = c0 + 20 * d2; + a1 = c1 + d0; + a2 = c2 + d1; + + a1 += a0 >> 44; + a0 &= MASK44; + a2 += a1 >> 44; + a1 &= MASK44; + a0 += 20 * (a2 >> 44); + a2 &= MASK44; + + buf += 16; + len -= 16; + } + acc[0] = a0; + acc[1] = a1; + acc[2] = a2; +} + +static inline void +poly1305_inner(uint64_t *acc, uint64_t *r, const void *data, size_t len) +{ + if (len >= 64) { + size_t len2; + + len2 = len & ~(size_t)63; + poly1305_inner_big(acc, r, data, len2); + data = (const unsigned char *)data + len2; + len -= len2; + } + if (len > 0) { + poly1305_inner_small(acc, r, data, len); + } +} + +/* see bearssl_block.h */ +void +br_poly1305_ctmulq_run(const void *key, const void *iv, + void *data, size_t len, const void *aad, size_t aad_len, + void *tag, br_chacha20_run ichacha, int encrypt) +{ + unsigned char pkey[32], foot[16]; + uint64_t r[6], acc[3], r0, r1; + uint32_t v0, v1, v2, v3, v4; + uint64_t w0, w1, w2, w3; + uint32_t ctl; + + /* + * Compute the MAC key. The 'r' value is the first 16 bytes of + * pkey[]. + */ + memset(pkey, 0, sizeof pkey); + ichacha(key, iv, 0, pkey, sizeof pkey); + + /* + * If encrypting, ChaCha20 must run first, followed by Poly1305. + * When decrypting, the operations are reversed. + */ + if (encrypt) { + ichacha(key, iv, 1, data, len); + } + + /* + * Run Poly1305. We must process the AAD, then ciphertext, then + * the footer (with the lengths). Note that the AAD and ciphertext + * are meant to be padded with zeros up to the next multiple of 16, + * and the length of the footer is 16 bytes as well. + */ + + /* + * Apply the "clamping" on r. + */ + pkey[ 3] &= 0x0F; + pkey[ 4] &= 0xFC; + pkey[ 7] &= 0x0F; + pkey[ 8] &= 0xFC; + pkey[11] &= 0x0F; + pkey[12] &= 0xFC; + pkey[15] &= 0x0F; + + /* + * Decode the 'r' value into 44-bit words, left-shifted by 20 bits. + * Also compute the u1 and u2 values. + */ + r0 = br_dec64le(pkey + 0); + r1 = br_dec64le(pkey + 8); + r[0] = r0 << 20; + r[1] = ((r0 >> 24) | (r1 << 40)) & ~(uint64_t)0xFFFFF; + r[2] = (r1 >> 4) & ~(uint64_t)0xFFFFF; + r1 = 20 * (r[1] >> 20); + r[3] = r1 << 20; + r[5] = 20 * r[2]; + r[4] = (r[5] + (r1 >> 24)) & ~(uint64_t)0xFFFFF; + + /* + * Accumulator is 0. + */ + acc[0] = 0; + acc[1] = 0; + acc[2] = 0; + + /* + * Process the additional authenticated data, ciphertext, and + * footer in due order. + */ + br_enc64le(foot, (uint64_t)aad_len); + br_enc64le(foot + 8, (uint64_t)len); + poly1305_inner(acc, r, aad, aad_len); + poly1305_inner(acc, r, data, len); + poly1305_inner_small(acc, r, foot, sizeof foot); + + /* + * Finalise modular reduction. At that point, the value consists + * in three 44-bit values (the lowest one might be slightly above + * 2^44). Two loops shall be sufficient. + */ + acc[1] += (acc[0] >> 44); + acc[0] &= MASK44; + acc[2] += (acc[1] >> 44); + acc[1] &= MASK44; + acc[0] += 5 * (acc[2] >> 42); + acc[2] &= MASK42; + acc[1] += (acc[0] >> 44); + acc[0] &= MASK44; + acc[2] += (acc[1] >> 44); + acc[1] &= MASK44; + acc[0] += 5 * (acc[2] >> 42); + acc[2] &= MASK42; + + /* + * The value may still fall in the 2^130-5..2^130-1 range, in + * which case we must reduce it again. The code below selects, + * in constant-time, between 'acc' and 'acc-p'. We encode the + * value over four 32-bit integers to finish the operation. + */ + v0 = (uint32_t)acc[0]; + v1 = (uint32_t)(acc[0] >> 32) | ((uint32_t)acc[1] << 12); + v2 = (uint32_t)(acc[1] >> 20) | ((uint32_t)acc[2] << 24); + v3 = (uint32_t)(acc[2] >> 8); + v4 = (uint32_t)(acc[2] >> 40); + + ctl = GT(v0, 0xFFFFFFFA); + ctl &= EQ(v1, 0xFFFFFFFF); + ctl &= EQ(v2, 0xFFFFFFFF); + ctl &= EQ(v3, 0xFFFFFFFF); + ctl &= EQ(v4, 0x00000003); + v0 = MUX(ctl, v0 + 5, v0); + v1 = MUX(ctl, 0, v1); + v2 = MUX(ctl, 0, v2); + v3 = MUX(ctl, 0, v3); + + /* + * Add the "s" value. This is done modulo 2^128. Don't forget + * carry propagation... + */ + w0 = (uint64_t)v0 + (uint64_t)br_dec32le(pkey + 16); + w1 = (uint64_t)v1 + (uint64_t)br_dec32le(pkey + 20) + (w0 >> 32); + w2 = (uint64_t)v2 + (uint64_t)br_dec32le(pkey + 24) + (w1 >> 32); + w3 = (uint64_t)v3 + (uint64_t)br_dec32le(pkey + 28) + (w2 >> 32); + v0 = (uint32_t)w0; + v1 = (uint32_t)w1; + v2 = (uint32_t)w2; + v3 = (uint32_t)w3; + + /* + * Encode the tag. + */ + br_enc32le((unsigned char *)tag + 0, v0); + br_enc32le((unsigned char *)tag + 4, v1); + br_enc32le((unsigned char *)tag + 8, v2); + br_enc32le((unsigned char *)tag + 12, v3); + + /* + * If decrypting, then ChaCha20 runs _after_ Poly1305. + */ + if (!encrypt) { + ichacha(key, iv, 1, data, len); + } +} + +/* see bearssl_block.h */ +br_poly1305_run +br_poly1305_ctmulq_get(void) +{ + return &br_poly1305_ctmulq_run; +} + +#else + +/* see bearssl_block.h */ +br_poly1305_run +br_poly1305_ctmulq_get(void) +{ + return 0; +} + +#endif diff --git a/lib/bearssl-esp8266/src/symcipher/poly1305_i15.c b/lib/bearssl-esp8266/src/symcipher/poly1305_i15.c new file mode 100644 index 000000000..684074189 --- /dev/null +++ b/lib/bearssl-esp8266/src/symcipher/poly1305_i15.c @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * This is a "reference" implementation of Poly1305 that uses the + * generic "i15" code for big integers. It is slow, but it handles all + * big-integer operations with generic code, thereby avoiding most + * tricky situations with carry propagation and modular reduction. + */ + +/* + * Modulus: 2^130-5. + */ +static const uint16_t P1305[] = { + 0x008A, + 0x7FFB, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x03FF +}; + +/* + * -p mod 2^15. + */ +#define P0I 0x4CCD + +/* + * R^2 mod p, for conversion to Montgomery representation (R = 2^135, + * since we use 9 words of 15 bits each, and 15*9 = 135). + */ +static const uint16_t R2[] = { + 0x008A, + 0x6400, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 +}; + +/* + * Perform the inner processing of blocks for Poly1305. The "r" array + * is in Montgomery representation, while the "a" array is not. + */ +static void +poly1305_inner(uint16_t *a, const uint16_t *r, const void *data, size_t len) +{ + const unsigned char *buf; + + buf = data; + while (len > 0) { + unsigned char tmp[16], rev[16]; + uint16_t b[10]; + uint32_t ctl; + int i; + + /* + * If there is a partial block, right-pad it with zeros. + */ + if (len < 16) { + memset(tmp, 0, sizeof tmp); + memcpy(tmp, buf, len); + buf = tmp; + len = 16; + } + + /* + * Decode next block and apply the "high bit". Since + * decoding is little-endian, we must byte-swap the buffer. + */ + for (i = 0; i < 16; i ++) { + rev[i] = buf[15 - i]; + } + br_i15_decode_mod(b, rev, sizeof rev, P1305); + b[9] |= 0x0100; + + /* + * Add the accumulator to the decoded block (modular + * addition). + */ + ctl = br_i15_add(b, a, 1); + ctl |= NOT(br_i15_sub(b, P1305, 0)); + br_i15_sub(b, P1305, ctl); + + /* + * Multiply by r, result is the new accumulator value. + */ + br_i15_montymul(a, b, r, P1305, P0I); + + buf += 16; + len -= 16; + } +} + +/* + * Byteswap a 16-byte value. + */ +static void +byteswap16(unsigned char *buf) +{ + int i; + + for (i = 0; i < 8; i ++) { + unsigned x; + + x = buf[i]; + buf[i] = buf[15 - i]; + buf[15 - i] = x; + } +} + +/* see bearssl_block.h */ +void +br_poly1305_i15_run(const void *key, const void *iv, + void *data, size_t len, const void *aad, size_t aad_len, + void *tag, br_chacha20_run ichacha, int encrypt) +{ + unsigned char pkey[32], foot[16]; + uint16_t t[10], r[10], acc[10]; + + /* + * Compute the MAC key. The 'r' value is the first 16 bytes of + * pkey[]. + */ + memset(pkey, 0, sizeof pkey); + ichacha(key, iv, 0, pkey, sizeof pkey); + + /* + * If encrypting, ChaCha20 must run first, followed by Poly1305. + * When decrypting, the operations are reversed. + */ + if (encrypt) { + ichacha(key, iv, 1, data, len); + } + + /* + * Run Poly1305. We must process the AAD, then ciphertext, then + * the footer (with the lengths). Note that the AAD and ciphertext + * are meant to be padded with zeros up to the next multiple of 16, + * and the length of the footer is 16 bytes as well. + */ + + /* + * Apply the "clamping" operation on the encoded 'r' value. + */ + pkey[ 3] &= 0x0F; + pkey[ 7] &= 0x0F; + pkey[11] &= 0x0F; + pkey[15] &= 0x0F; + pkey[ 4] &= 0xFC; + pkey[ 8] &= 0xFC; + pkey[12] &= 0xFC; + + /* + * Decode the clamped 'r' value. Decoding should use little-endian + * so we must byteswap the value first. + */ + byteswap16(pkey); + br_i15_decode_mod(t, pkey, 16, P1305); + + /* + * Convert 'r' to Montgomery representation. + */ + br_i15_montymul(r, t, R2, P1305, P0I); + + /* + * Accumulator is 0. + */ + br_i15_zero(acc, 0x8A); + + /* + * Process the additional authenticated data, ciphertext, and + * footer in due order. + */ + br_enc64le(foot, (uint64_t)aad_len); + br_enc64le(foot + 8, (uint64_t)len); + poly1305_inner(acc, r, aad, aad_len); + poly1305_inner(acc, r, data, len); + poly1305_inner(acc, r, foot, sizeof foot); + + /* + * Decode the value 's'. Again, a byteswap is needed. + */ + byteswap16(pkey + 16); + br_i15_decode_mod(t, pkey + 16, 16, P1305); + + /* + * Add the value 's' to the accumulator. That addition is done + * modulo 2^128, so we just ignore the carry. + */ + br_i15_add(acc, t, 1); + + /* + * Encode the result (128 low bits) to the tag. Encoding should + * be little-endian. + */ + br_i15_encode(tag, 16, acc); + byteswap16(tag); + + /* + * If decrypting, then ChaCha20 runs _after_ Poly1305. + */ + if (!encrypt) { + ichacha(key, iv, 1, data, len); + } +} diff --git a/lib/bearssl-esp8266/src/t_bearssl.h b/lib/bearssl-esp8266/src/t_bearssl.h new file mode 100644 index 000000000..959fad72a --- /dev/null +++ b/lib/bearssl-esp8266/src/t_bearssl.h @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef BR_BEARSSL_H__ +#define BR_BEARSSL_H__ + +#include +#include + +/** \mainpage BearSSL API + * + * # API Layout + * + * The functions and structures defined by the BearSSL API are located + * in various header files: + * + * | Header file | Elements | + * | :-------------- | :------------------------------------------------ | + * | bearssl_hash.h | Hash functions | + * | bearssl_hmac.h | HMAC | + * | bearssl_kdf.h | Key Derivation Functions | + * | bearssl_rand.h | Pseudorandom byte generators | + * | bearssl_prf.h | PRF implementations (for SSL/TLS) | + * | bearssl_block.h | Symmetric encryption | + * | bearssl_aead.h | AEAD algorithms (combined encryption + MAC) | + * | bearssl_rsa.h | RSA encryption and signatures | + * | bearssl_ec.h | Elliptic curves support (including ECDSA) | + * | bearssl_ssl.h | SSL/TLS engine interface | + * | bearssl_x509.h | X.509 certificate decoding and validation | + * | bearssl_pem.h | Base64/PEM decoding support functions | + * + * Applications using BearSSL are supposed to simply include `bearssl.h` + * as follows: + * + * #include + * + * The `bearssl.h` file itself includes all the other header files. It is + * possible to include specific header files, but it has no practical + * advantage for the application. The API is separated into separate + * header files only for documentation convenience. + * + * + * # Conventions + * + * ## MUST and SHALL + * + * In all descriptions, the usual "MUST", "SHALL", "MAY",... terminology + * is used. Failure to meet requirements expressed with a "MUST" or + * "SHALL" implies undefined behaviour, which means that segmentation + * faults, buffer overflows, and other similar adverse events, may occur. + * + * In general, BearSSL is not very forgiving of programming errors, and + * does not include much failsafes or error reporting when the problem + * does not arise from external transient conditions, and can be fixed + * only in the application code. This is done so in order to make the + * total code footprint lighter. + * + * + * ## `NULL` values + * + * Function parameters with a pointer type shall not be `NULL` unless + * explicitly authorised by the documentation. As an exception, when + * the pointer aims at a sequence of bytes and is accompanied with + * a length parameter, and the length is zero (meaning that there is + * no byte at all to retrieve), then the pointer may be `NULL` even if + * not explicitly allowed. + * + * + * ## Memory Allocation + * + * BearSSL does not perform dynamic memory allocation. This implies that + * for any functionality that requires a non-transient state, the caller + * is responsible for allocating the relevant context structure. Such + * allocation can be done in any appropriate area, including static data + * segments, the heap, and the stack, provided that proper alignment is + * respected. The header files define these context structures + * (including size and contents), so the C compiler should handle + * alignment automatically. + * + * Since there is no dynamic resource allocation, there is also nothing to + * release. When the calling code is done with a BearSSL feature, it + * may simple release the context structures it allocated itself, with + * no "close function" to call. If the context structures were allocated + * on the stack (as local variables), then even that release operation is + * implicit. + * + * + * ## Structure Contents + * + * Except when explicitly indicated, structure contents are opaque: they + * are included in the header files so that calling code may know the + * structure sizes and alignment requirements, but callers SHALL NOT + * access individual fields directly. For fields that are supposed to + * be read from or written to, the API defines accessor functions (the + * simplest of these accessor functions are defined as `static inline` + * functions, and the C compiler will optimise them away). + * + * + * # API Usage + * + * BearSSL usage for running a SSL/TLS client or server is described + * on the [BearSSL Web site](https://www.bearssl.org/api1.html). The + * BearSSL source archive also comes with sample code. + */ + +#include "t_bearssl_hash.h" +#include "t_bearssl_hmac.h" +#include "t_bearssl_kdf.h" +#include "t_bearssl_rand.h" +#include "t_bearssl_prf.h" +#include "t_bearssl_block.h" +#include "t_bearssl_aead.h" +#include "t_bearssl_rsa.h" +#include "t_bearssl_ec.h" +#include "t_bearssl_ssl.h" +#include "t_bearssl_x509.h" +#include "t_bearssl_pem.h" + +/** \brief Type for a configuration option. + * + * A "t_configuration option" is a value that is selected when the BearSSL + * library itself is compiled. Most options are boolean; their value is + * then either 1 (option is enabled) or 0 (option is disabled). Some + * values have other integer values. Option names correspond to macro + * names. Some of the options can be explicitly set in the internal + * `"t_config.h"` file. + */ +typedef struct { + /** \brief Configurable option name. */ + const char *name; + /** \brief Configurable option value. */ + long value; +} br_config_option; + +/** \brief Get configuration report. + * + * This function returns compiled configuration options, each as a + * 'long' value. Names match internal macro names, in particular those + * that can be set in the `"t_config.h"` inner file. For boolean options, + * the numerical value is 1 if enabled, 0 if disabled. For maximum + * key sizes, values are expressed in bits. + * + * The returned array is terminated by an entry whose `name` is `NULL`. + * + * \return the configuration report. + */ +const br_config_option *br_get_config(void); + +#endif diff --git a/lib/bearssl-esp8266/src/t_bearssl_aead.h b/lib/bearssl-esp8266/src/t_bearssl_aead.h new file mode 100644 index 000000000..d4a1e777c --- /dev/null +++ b/lib/bearssl-esp8266/src/t_bearssl_aead.h @@ -0,0 +1,1059 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef BR_BEARSSL_AEAD_H__ +#define BR_BEARSSL_AEAD_H__ + +#include +#include + +#include "t_bearssl_block.h" +#include "t_bearssl_hash.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** \file bearssl_aead.h + * + * # Authenticated Encryption with Additional Data + * + * This file documents the API for AEAD encryption. + * + * + * ## Procedural API + * + * An AEAD algorithm processes messages and provides confidentiality + * (encryption) and checked integrity (MAC). It uses the following + * parameters: + * + * - A symmetric key. Exact size depends on the AEAD algorithm. + * + * - A nonce (IV). Size depends on the AEAD algorithm; for most + * algorithms, it is crucial for security that any given nonce + * value is never used twice for the same key and distinct + * messages. + * + * - Data to encrypt and protect. + * + * - Additional authenticated data, which is covered by the MAC but + * otherwise left untouched (i.e. not encrypted). + * + * The AEAD algorithm encrypts the data, and produces an authentication + * tag. It is assumed that the encrypted data, the tag, the additional + * authenticated data and the nonce are sent to the receiver; the + * additional data and the nonce may be implicit (e.g. using elements of + * the underlying transport protocol, such as record sequence numbers). + * The receiver will recompute the tag value and compare it with the one + * received; if they match, then the data is correct, and can be + * decrypted and used; otherwise, at least one of the elements was + * altered in transit, normally leading to wholesale rejection of the + * complete message. + * + * For each AEAD algorithm, identified by a symbolic name (hereafter + * denoted as "`xxx`"), the following functions are defined: + * + * - `br_xxx_init()` + * + * Initialise the AEAD algorithm, on a provided context structure. + * Exact parameters depend on the algorithm, and may include + * pointers to extra implementations and context structures. The + * secret key is provided at this point, either directly or + * indirectly. + * + * - `br_xxx_reset()` + * + * Start a new AEAD computation. The nonce value is provided as + * parameter to this function. + * + * - `br_xxx_aad_inject()` + * + * Inject some additional authenticated data. Additional data may + * be provided in several chunks of arbitrary length. + * + * - `br_xxx_flip()` + * + * This function MUST be called after injecting all additional + * authenticated data, and before beginning to encrypt the plaintext + * (or decrypt the ciphertext). + * + * - `br_xxx_run()` + * + * Process some plaintext (to encrypt) or ciphertext (to decrypt). + * Encryption/decryption is done in place. Data may be provided in + * several chunks of arbitrary length. + * + * - `br_xxx_get_tag()` + * + * Compute the authentication tag. All message data (encrypted or + * decrypted) must have been injected at that point. Also, this + * call may modify internal context elements, so it may be called + * only once for a given AEAD computation. + * + * - `br_xxx_check_tag()` + * + * An alternative to `br_xxx_get_tag()`, meant to be used by the + * receiver: the authentication tag is internally recomputed, and + * compared with the one provided as parameter. + * + * This API makes the following assumptions on the AEAD algorithm: + * + * - Encryption does not expand the size of the ciphertext; there is + * no padding. This is true of most modern AEAD modes such as GCM. + * + * - The additional authenticated data must be processed first, + * before the encrypted/decrypted data. + * + * - Nonce, plaintext and additional authenticated data all consist + * in an integral number of bytes. There is no provision to use + * elements whose length in bits is not a multiple of 8. + * + * Each AEAD algorithm has its own requirements and limits on the sizes + * of additional data and plaintext. This API does not provide any + * way to report invalid usage; it is up to the caller to ensure that + * the provided key, nonce, and data elements all fit the algorithm's + * requirements. + * + * + * ## Object-Oriented API + * + * Each context structure begins with a field (called `vtable`) that + * points to an instance of a structure that references the relevant + * functions through pointers. Each such structure contains the + * following: + * + * - `reset` + * + * Pointer to the reset function, that allows starting a new + * computation. + * + * - `aad_inject` + * + * Pointer to the additional authenticated data injection function. + * + * - `flip` + * + * Pointer to the function that transitions from additional data + * to main message data processing. + * + * - `get_tag` + * + * Pointer to the function that computes and returns the tag. + * + * - `check_tag` + * + * Pointer to the function that computes and verifies the tag against + * a received value. + * + * Note that there is no OOP method for context initialisation: the + * various AEAD algorithms have different requirements that would not + * map well to a single initialisation API. + * + * The OOP API is not provided for CCM, due to its specific requirements + * (length of plaintext must be known in advance). + */ + +/** + * \brief Class type of an AEAD algorithm. + */ +typedef struct br_aead_class_ br_aead_class; +struct br_aead_class_ { + + /** + * \brief Size (in bytes) of authentication tags created by + * this AEAD algorithm. + */ + size_t tag_size; + + /** + * \brief Reset an AEAD context. + * + * This function resets an already initialised AEAD context for + * a new computation run. Implementations and keys are + * conserved. This function can be called at any time; it + * cancels any ongoing AEAD computation that uses the provided + * context structure. + + * The provided IV is a _nonce_. Each AEAD algorithm has its + * own requirements on IV size and contents; for most of them, + * it is crucial to security that each nonce value is used + * only once for a given secret key. + * + * \param cc AEAD context structure. + * \param iv AEAD nonce to use. + * \param len AEAD nonce length (in bytes). + */ + void (*reset)(const br_aead_class **cc, const void *iv, size_t len); + + /** + * \brief Inject additional authenticated data. + * + * The provided data is injected into a running AEAD + * computation. Additional data must be injected _before_ the + * call to `flip()`. Additional data can be injected in several + * chunks of arbitrary length. + * + * \param cc AEAD context structure. + * \param data pointer to additional authenticated data. + * \param len length of additional authenticated data (in bytes). + */ + void (*aad_inject)(const br_aead_class **cc, + const void *data, size_t len); + + /** + * \brief Finish injection of additional authenticated data. + * + * This function MUST be called before beginning the actual + * encryption or decryption (with `run()`), even if no + * additional authenticated data was injected. No additional + * authenticated data may be injected after this function call. + * + * \param cc AEAD context structure. + */ + void (*flip)(const br_aead_class **cc); + + /** + * \brief Encrypt or decrypt some data. + * + * Data encryption or decryption can be done after `flip()` has + * been called on the context. If `encrypt` is non-zero, then + * the provided data shall be plaintext, and it is encrypted in + * place. Otherwise, the data shall be ciphertext, and it is + * decrypted in place. + * + * Data may be provided in several chunks of arbitrary length. + * + * \param cc AEAD context structure. + * \param encrypt non-zero for encryption, zero for decryption. + * \param data data to encrypt or decrypt. + * \param len data length (in bytes). + */ + void (*run)(const br_aead_class **cc, int encrypt, + void *data, size_t len); + + /** + * \brief Compute authentication tag. + * + * Compute the AEAD authentication tag. The tag length depends + * on the AEAD algorithm; it is written in the provided `tag` + * buffer. This call terminates the AEAD run: no data may be + * processed with that AEAD context afterwards, until `reset()` + * is called to initiate a new AEAD run. + * + * The tag value must normally be sent along with the encrypted + * data. When decrypting, the tag value must be recomputed and + * compared with the received tag: if the two tag values differ, + * then either the tag or the encrypted data was altered in + * transit. As an alternative to this function, the + * `check_tag()` function may be used to compute and check the + * tag value. + * + * Tag length depends on the AEAD algorithm. + * + * \param cc AEAD context structure. + * \param tag destination buffer for the tag. + */ + void (*get_tag)(const br_aead_class **cc, void *tag); + + /** + * \brief Compute and check authentication tag. + * + * This function is an alternative to `get_tag()`, and is + * normally used on the receiving end (i.e. when decrypting + * messages). The tag value is recomputed and compared with the + * provided tag value. If they match, 1 is returned; on + * mismatch, 0 is returned. A returned value of 0 means that the + * data or the tag was altered in transit, normally leading to + * wholesale rejection of the complete message. + * + * Tag length depends on the AEAD algorithm. + * + * \param cc AEAD context structure. + * \param tag tag value to compare with. + * \return 1 on success (exact match of tag value), 0 otherwise. + */ + uint32_t (*check_tag)(const br_aead_class **cc, const void *tag); + + /** + * \brief Compute authentication tag (with truncation). + * + * This function is similar to `get_tag()`, except that the tag + * length is provided. Some AEAD algorithms allow several tag + * lengths, usually by truncating the normal tag. Shorter tags + * mechanically increase success probability of forgeries. + * The range of allowed tag lengths depends on the algorithm. + * + * \param cc AEAD context structure. + * \param tag destination buffer for the tag. + * \param len tag length (in bytes). + */ + void (*get_tag_trunc)(const br_aead_class **cc, void *tag, size_t len); + + /** + * \brief Compute and check authentication tag (with truncation). + * + * This function is similar to `check_tag()` except that it + * works over an explicit tag length. See `get_tag()` for a + * discussion of explicit tag lengths; the range of allowed tag + * lengths depends on the algorithm. + * + * \param cc AEAD context structure. + * \param tag tag value to compare with. + * \param len tag length (in bytes). + * \return 1 on success (exact match of tag value), 0 otherwise. + */ + uint32_t (*check_tag_trunc)(const br_aead_class **cc, + const void *tag, size_t len); +}; + +/** + * \brief Context structure for GCM. + * + * GCM is an AEAD mode that combines a block cipher in CTR mode with a + * MAC based on GHASH, to provide authenticated encryption: + * + * - Any block cipher with 16-byte blocks can be used with GCM. + * + * - The nonce can have any length, from 0 up to 2^64-1 bits; however, + * 96-bit nonces (12 bytes) are recommended (nonces with a length + * distinct from 12 bytes are internally hashed, which risks reusing + * nonce value with a small but not always negligible probability). + * + * - Additional authenticated data may have length up to 2^64-1 bits. + * + * - Message length may range up to 2^39-256 bits at most. + * + * - The authentication tag has length 16 bytes. + * + * The GCM initialisation function receives as parameter an + * _initialised_ block cipher implementation context, with the secret + * key already set. A pointer to that context will be kept within the + * GCM context structure. It is up to the caller to allocate and + * initialise that block cipher context. + */ +typedef struct { + /** \brief Pointer to vtable for this context. */ + const br_aead_class *vtable; + +#ifndef BR_DOXYGEN_IGNORE + const br_block_ctr_class **bctx; + br_ghash gh; + unsigned char h[16]; + unsigned char j0_1[12]; + unsigned char buf[16]; + unsigned char y[16]; + uint32_t j0_2, jc; + uint64_t count_aad, count_ctr; +#endif +} br_gcm_context; + +/** + * \brief Initialize a GCM context. + * + * A block cipher implementation, with its initialised context structure, + * is provided. The block cipher MUST use 16-byte blocks in CTR mode, + * and its secret key MUST have been already set in the provided context. + * A GHASH implementation must also be provided. The parameters are linked + * in the GCM context. + * + * After this function has been called, the `br_gcm_reset()` function must + * be called, to provide the IV for GCM computation. + * + * \param ctx GCM context structure. + * \param bctx block cipher context (already initialised with secret key). + * \param gh GHASH implementation. + */ +void br_gcm_init(br_gcm_context *ctx, + const br_block_ctr_class **bctx, br_ghash gh); + +/** + * \brief Reset a GCM context. + * + * This function resets an already initialised GCM context for a new + * computation run. Implementations and keys are conserved. This function + * can be called at any time; it cancels any ongoing GCM computation that + * uses the provided context structure. + * + * The provided IV is a _nonce_. It is critical to GCM security that IV + * values are not repeated for the same encryption key. IV can have + * arbitrary length (up to 2^64-1 bits), but the "normal" length is + * 96 bits (12 bytes). + * + * \param ctx GCM context structure. + * \param iv GCM nonce to use. + * \param len GCM nonce length (in bytes). + */ +void br_gcm_reset(br_gcm_context *ctx, const void *iv, size_t len); + +/** + * \brief Inject additional authenticated data into GCM. + * + * The provided data is injected into a running GCM computation. Additional + * data must be injected _before_ the call to `br_gcm_flip()`. + * Additional data can be injected in several chunks of arbitrary length; + * the maximum total size of additional authenticated data is 2^64-1 + * bits. + * + * \param ctx GCM context structure. + * \param data pointer to additional authenticated data. + * \param len length of additional authenticated data (in bytes). + */ +void br_gcm_aad_inject(br_gcm_context *ctx, const void *data, size_t len); + +/** + * \brief Finish injection of additional authenticated data into GCM. + * + * This function MUST be called before beginning the actual encryption + * or decryption (with `br_gcm_run()`), even if no additional authenticated + * data was injected. No additional authenticated data may be injected + * after this function call. + * + * \param ctx GCM context structure. + */ +void br_gcm_flip(br_gcm_context *ctx); + +/** + * \brief Encrypt or decrypt some data with GCM. + * + * Data encryption or decryption can be done after `br_gcm_flip()` + * has been called on the context. If `encrypt` is non-zero, then the + * provided data shall be plaintext, and it is encrypted in place. + * Otherwise, the data shall be ciphertext, and it is decrypted in place. + * + * Data may be provided in several chunks of arbitrary length. The maximum + * total length for data is 2^39-256 bits, i.e. about 65 gigabytes. + * + * \param ctx GCM context structure. + * \param encrypt non-zero for encryption, zero for decryption. + * \param data data to encrypt or decrypt. + * \param len data length (in bytes). + */ +void br_gcm_run(br_gcm_context *ctx, int encrypt, void *data, size_t len); + +/** + * \brief Compute GCM authentication tag. + * + * Compute the GCM authentication tag. The tag is a 16-byte value which + * is written in the provided `tag` buffer. This call terminates the + * GCM run: no data may be processed with that GCM context afterwards, + * until `br_gcm_reset()` is called to initiate a new GCM run. + * + * The tag value must normally be sent along with the encrypted data. + * When decrypting, the tag value must be recomputed and compared with + * the received tag: if the two tag values differ, then either the tag + * or the encrypted data was altered in transit. As an alternative to + * this function, the `br_gcm_check_tag()` function can be used to + * compute and check the tag value. + * + * \param ctx GCM context structure. + * \param tag destination buffer for the tag (16 bytes). + */ +void br_gcm_get_tag(br_gcm_context *ctx, void *tag); + +/** + * \brief Compute and check GCM authentication tag. + * + * This function is an alternative to `br_gcm_get_tag()`, normally used + * on the receiving end (i.e. when decrypting value). The tag value is + * recomputed and compared with the provided tag value. If they match, 1 + * is returned; on mismatch, 0 is returned. A returned value of 0 means + * that the data or the tag was altered in transit, normally leading to + * wholesale rejection of the complete message. + * + * \param ctx GCM context structure. + * \param tag tag value to compare with (16 bytes). + * \return 1 on success (exact match of tag value), 0 otherwise. + */ +uint32_t br_gcm_check_tag(br_gcm_context *ctx, const void *tag); + +/** + * \brief Compute GCM authentication tag (with truncation). + * + * This function is similar to `br_gcm_get_tag()`, except that it allows + * the tag to be truncated to a smaller length. The intended tag length + * is provided as `len` (in bytes); it MUST be no more than 16, but + * it may be smaller. Note that decreasing tag length mechanically makes + * forgeries easier; NIST SP 800-38D specifies that the tag length shall + * lie between 12 and 16 bytes (inclusive), but may be truncated down to + * 4 or 8 bytes, for specific applications that can tolerate it. It must + * also be noted that successful forgeries leak information on the + * authentication key, making subsequent forgeries easier. Therefore, + * tag truncation, and in particular truncation to sizes lower than 12 + * bytes, shall be envisioned only with great care. + * + * The tag is written in the provided `tag` buffer. This call terminates + * the GCM run: no data may be processed with that GCM context + * afterwards, until `br_gcm_reset()` is called to initiate a new GCM + * run. + * + * The tag value must normally be sent along with the encrypted data. + * When decrypting, the tag value must be recomputed and compared with + * the received tag: if the two tag values differ, then either the tag + * or the encrypted data was altered in transit. As an alternative to + * this function, the `br_gcm_check_tag_trunc()` function can be used to + * compute and check the tag value. + * + * \param ctx GCM context structure. + * \param tag destination buffer for the tag. + * \param len tag length (16 bytes or less). + */ +void br_gcm_get_tag_trunc(br_gcm_context *ctx, void *tag, size_t len); + +/** + * \brief Compute and check GCM authentication tag (with truncation). + * + * This function is an alternative to `br_gcm_get_tag_trunc()`, normally used + * on the receiving end (i.e. when decrypting value). The tag value is + * recomputed and compared with the provided tag value. If they match, 1 + * is returned; on mismatch, 0 is returned. A returned value of 0 means + * that the data or the tag was altered in transit, normally leading to + * wholesale rejection of the complete message. + * + * Tag length MUST be 16 bytes or less. The normal GCM tag length is 16 + * bytes. See `br_check_tag_trunc()` for some discussion on the potential + * perils of truncating authentication tags. + * + * \param ctx GCM context structure. + * \param tag tag value to compare with. + * \param len tag length (in bytes). + * \return 1 on success (exact match of tag value), 0 otherwise. + */ +uint32_t br_gcm_check_tag_trunc(br_gcm_context *ctx, + const void *tag, size_t len); + +/** + * \brief Class instance for GCM. + */ +extern const br_aead_class br_gcm_vtable; + +/** + * \brief Context structure for EAX. + * + * EAX is an AEAD mode that combines a block cipher in CTR mode with + * CBC-MAC using the same block cipher and the same key, to provide + * authenticated encryption: + * + * - Any block cipher with 16-byte blocks can be used with EAX + * (technically, other block sizes are defined as well, but this + * is not implemented by these functions; shorter blocks also + * imply numerous security issues). + * + * - The nonce can have any length, as long as nonce values are + * not reused (thus, if nonces are randomly selected, the nonce + * size should be such that reuse probability is negligible). + * + * - Additional authenticated data length is unlimited. + * + * - Message length is unlimited. + * + * - The authentication tag has length 16 bytes. + * + * The EAX initialisation function receives as parameter an + * _initialised_ block cipher implementation context, with the secret + * key already set. A pointer to that context will be kept within the + * EAX context structure. It is up to the caller to allocate and + * initialise that block cipher context. + */ +typedef struct { + /** \brief Pointer to vtable for this context. */ + const br_aead_class *vtable; + +#ifndef BR_DOXYGEN_IGNORE + const br_block_ctrcbc_class **bctx; + unsigned char L2[16]; + unsigned char L4[16]; + unsigned char nonce[16]; + unsigned char head[16]; + unsigned char ctr[16]; + unsigned char cbcmac[16]; + unsigned char buf[16]; + size_t ptr; +#endif +} br_eax_context; + +/** + * \brief EAX captured state. + * + * Some internal values computed by EAX may be captured at various + * points, and reused for another EAX run with the same secret key, + * for lower per-message overhead. Captured values do not depend on + * the nonce. + */ +typedef struct { +#ifndef BR_DOXYGEN_IGNORE + unsigned char st[3][16]; +#endif +} br_eax_state; + +/** + * \brief Initialize an EAX context. + * + * A block cipher implementation, with its initialised context + * structure, is provided. The block cipher MUST use 16-byte blocks in + * CTR + CBC-MAC mode, and its secret key MUST have been already set in + * the provided context. The parameters are linked in the EAX context. + * + * After this function has been called, the `br_eax_reset()` function must + * be called, to provide the nonce for EAX computation. + * + * \param ctx EAX context structure. + * \param bctx block cipher context (already initialised with secret key). + */ +void br_eax_init(br_eax_context *ctx, const br_block_ctrcbc_class **bctx); + +/** + * \brief Capture pre-AAD state. + * + * This function precomputes key-dependent data, and stores it in the + * provided `st` structure. This structure should then be used with + * `br_eax_reset_pre_aad()`, or updated with `br_eax_get_aad_mac()` + * and then used with `br_eax_reset_post_aad()`. + * + * The EAX context structure is unmodified by this call. + * + * \param ctx EAX context structure. + * \param st recipient for captured state. + */ +void br_eax_capture(const br_eax_context *ctx, br_eax_state *st); + +/** + * \brief Reset an EAX context. + * + * This function resets an already initialised EAX context for a new + * computation run. Implementations and keys are conserved. This function + * can be called at any time; it cancels any ongoing EAX computation that + * uses the provided context structure. + * + * It is critical to EAX security that nonce values are not repeated for + * the same encryption key. Nonces can have arbitrary length. If nonces + * are randomly generated, then a nonce length of at least 128 bits (16 + * bytes) is recommended, to make nonce reuse probability sufficiently + * low. + * + * \param ctx EAX context structure. + * \param nonce EAX nonce to use. + * \param len EAX nonce length (in bytes). + */ +void br_eax_reset(br_eax_context *ctx, const void *nonce, size_t len); + +/** + * \brief Reset an EAX context with a pre-AAD captured state. + * + * This function is an alternative to `br_eax_reset()`, that reuses a + * previously captured state structure for lower per-message overhead. + * The state should have been populated with `br_eax_capture_state()` + * but not updated with `br_eax_get_aad_mac()`. + * + * After this function is called, additional authenticated data MUST + * be injected. At least one byte of additional authenticated data + * MUST be provided with `br_eax_aad_inject()`; computation result will + * be incorrect if `br_eax_flip()` is called right away. + * + * After injection of the AAD and call to `br_eax_flip()`, at least + * one message byte must be provided. Empty messages are not supported + * with this reset mode. + * + * \param ctx EAX context structure. + * \param st pre-AAD captured state. + * \param nonce EAX nonce to use. + * \param len EAX nonce length (in bytes). + */ +void br_eax_reset_pre_aad(br_eax_context *ctx, const br_eax_state *st, + const void *nonce, size_t len); + +/** + * \brief Reset an EAX context with a post-AAD captured state. + * + * This function is an alternative to `br_eax_reset()`, that reuses a + * previously captured state structure for lower per-message overhead. + * The state should have been populated with `br_eax_capture_state()` + * and then updated with `br_eax_get_aad_mac()`. + * + * After this function is called, message data MUST be injected. The + * `br_eax_flip()` function MUST NOT be called. At least one byte of + * message data MUST be provided with `br_eax_run()`; empty messages + * are not supported with this reset mode. + * + * \param ctx EAX context structure. + * \param st post-AAD captured state. + * \param nonce EAX nonce to use. + * \param len EAX nonce length (in bytes). + */ +void br_eax_reset_post_aad(br_eax_context *ctx, const br_eax_state *st, + const void *nonce, size_t len); + +/** + * \brief Inject additional authenticated data into EAX. + * + * The provided data is injected into a running EAX computation. Additional + * data must be injected _before_ the call to `br_eax_flip()`. + * Additional data can be injected in several chunks of arbitrary length; + * the total amount of additional authenticated data is unlimited. + * + * \param ctx EAX context structure. + * \param data pointer to additional authenticated data. + * \param len length of additional authenticated data (in bytes). + */ +void br_eax_aad_inject(br_eax_context *ctx, const void *data, size_t len); + +/** + * \brief Finish injection of additional authenticated data into EAX. + * + * This function MUST be called before beginning the actual encryption + * or decryption (with `br_eax_run()`), even if no additional authenticated + * data was injected. No additional authenticated data may be injected + * after this function call. + * + * \param ctx EAX context structure. + */ +void br_eax_flip(br_eax_context *ctx); + +/** + * \brief Obtain a copy of the MAC on additional authenticated data. + * + * This function may be called only after `br_eax_flip()`; it copies the + * AAD-specific MAC value into the provided state. The MAC value depends + * on the secret key and the additional data itself, but not on the + * nonce. The updated state `st` is meant to be used as parameter for a + * further `br_eax_reset_post_aad()` call. + * + * \param ctx EAX context structure. + * \param st captured state to update. + */ +static inline void +br_eax_get_aad_mac(const br_eax_context *ctx, br_eax_state *st) +{ + memcpy(st->st[1], ctx->head, sizeof ctx->head); +} + +/** + * \brief Encrypt or decrypt some data with EAX. + * + * Data encryption or decryption can be done after `br_eax_flip()` + * has been called on the context. If `encrypt` is non-zero, then the + * provided data shall be plaintext, and it is encrypted in place. + * Otherwise, the data shall be ciphertext, and it is decrypted in place. + * + * Data may be provided in several chunks of arbitrary length. + * + * \param ctx EAX context structure. + * \param encrypt non-zero for encryption, zero for decryption. + * \param data data to encrypt or decrypt. + * \param len data length (in bytes). + */ +void br_eax_run(br_eax_context *ctx, int encrypt, void *data, size_t len); + +/** + * \brief Compute EAX authentication tag. + * + * Compute the EAX authentication tag. The tag is a 16-byte value which + * is written in the provided `tag` buffer. This call terminates the + * EAX run: no data may be processed with that EAX context afterwards, + * until `br_eax_reset()` is called to initiate a new EAX run. + * + * The tag value must normally be sent along with the encrypted data. + * When decrypting, the tag value must be recomputed and compared with + * the received tag: if the two tag values differ, then either the tag + * or the encrypted data was altered in transit. As an alternative to + * this function, the `br_eax_check_tag()` function can be used to + * compute and check the tag value. + * + * \param ctx EAX context structure. + * \param tag destination buffer for the tag (16 bytes). + */ +void br_eax_get_tag(br_eax_context *ctx, void *tag); + +/** + * \brief Compute and check EAX authentication tag. + * + * This function is an alternative to `br_eax_get_tag()`, normally used + * on the receiving end (i.e. when decrypting value). The tag value is + * recomputed and compared with the provided tag value. If they match, 1 + * is returned; on mismatch, 0 is returned. A returned value of 0 means + * that the data or the tag was altered in transit, normally leading to + * wholesale rejection of the complete message. + * + * \param ctx EAX context structure. + * \param tag tag value to compare with (16 bytes). + * \return 1 on success (exact match of tag value), 0 otherwise. + */ +uint32_t br_eax_check_tag(br_eax_context *ctx, const void *tag); + +/** + * \brief Compute EAX authentication tag (with truncation). + * + * This function is similar to `br_eax_get_tag()`, except that it allows + * the tag to be truncated to a smaller length. The intended tag length + * is provided as `len` (in bytes); it MUST be no more than 16, but + * it may be smaller. Note that decreasing tag length mechanically makes + * forgeries easier; NIST SP 800-38D specifies that the tag length shall + * lie between 12 and 16 bytes (inclusive), but may be truncated down to + * 4 or 8 bytes, for specific applications that can tolerate it. It must + * also be noted that successful forgeries leak information on the + * authentication key, making subsequent forgeries easier. Therefore, + * tag truncation, and in particular truncation to sizes lower than 12 + * bytes, shall be envisioned only with great care. + * + * The tag is written in the provided `tag` buffer. This call terminates + * the EAX run: no data may be processed with that EAX context + * afterwards, until `br_eax_reset()` is called to initiate a new EAX + * run. + * + * The tag value must normally be sent along with the encrypted data. + * When decrypting, the tag value must be recomputed and compared with + * the received tag: if the two tag values differ, then either the tag + * or the encrypted data was altered in transit. As an alternative to + * this function, the `br_eax_check_tag_trunc()` function can be used to + * compute and check the tag value. + * + * \param ctx EAX context structure. + * \param tag destination buffer for the tag. + * \param len tag length (16 bytes or less). + */ +void br_eax_get_tag_trunc(br_eax_context *ctx, void *tag, size_t len); + +/** + * \brief Compute and check EAX authentication tag (with truncation). + * + * This function is an alternative to `br_eax_get_tag_trunc()`, normally used + * on the receiving end (i.e. when decrypting value). The tag value is + * recomputed and compared with the provided tag value. If they match, 1 + * is returned; on mismatch, 0 is returned. A returned value of 0 means + * that the data or the tag was altered in transit, normally leading to + * wholesale rejection of the complete message. + * + * Tag length MUST be 16 bytes or less. The normal EAX tag length is 16 + * bytes. See `br_check_tag_trunc()` for some discussion on the potential + * perils of truncating authentication tags. + * + * \param ctx EAX context structure. + * \param tag tag value to compare with. + * \param len tag length (in bytes). + * \return 1 on success (exact match of tag value), 0 otherwise. + */ +uint32_t br_eax_check_tag_trunc(br_eax_context *ctx, + const void *tag, size_t len); + +/** + * \brief Class instance for EAX. + */ +extern const br_aead_class br_eax_vtable; + +/** + * \brief Context structure for CCM. + * + * CCM is an AEAD mode that combines a block cipher in CTR mode with + * CBC-MAC using the same block cipher and the same key, to provide + * authenticated encryption: + * + * - Any block cipher with 16-byte blocks can be used with CCM + * (technically, other block sizes are defined as well, but this + * is not implemented by these functions; shorter blocks also + * imply numerous security issues). + * + * - The authentication tag length, and plaintext length, MUST be + * known when starting processing data. Plaintext and ciphertext + * can still be provided by chunks, but the total size must match + * the value provided upon initialisation. + * + * - The nonce length is constrained between 7 and 13 bytes (inclusive). + * Furthermore, the plaintext length, when encoded, must fit over + * 15-nonceLen bytes; thus, if the nonce has length 13 bytes, then + * the plaintext length cannot exceed 65535 bytes. + * + * - Additional authenticated data length is practically unlimited + * (formal limit is at 2^64 bytes). + * + * - The authentication tag has length 4 to 16 bytes (even values only). + * + * The CCM initialisation function receives as parameter an + * _initialised_ block cipher implementation context, with the secret + * key already set. A pointer to that context will be kept within the + * CCM context structure. It is up to the caller to allocate and + * initialise that block cipher context. + */ +typedef struct { +#ifndef BR_DOXYGEN_IGNORE + const br_block_ctrcbc_class **bctx; + unsigned char ctr[16]; + unsigned char cbcmac[16]; + unsigned char tagmask[16]; + unsigned char buf[16]; + size_t ptr; + size_t tag_len; +#endif +} br_ccm_context; + +/** + * \brief Initialize a CCM context. + * + * A block cipher implementation, with its initialised context + * structure, is provided. The block cipher MUST use 16-byte blocks in + * CTR + CBC-MAC mode, and its secret key MUST have been already set in + * the provided context. The parameters are linked in the CCM context. + * + * After this function has been called, the `br_ccm_reset()` function must + * be called, to provide the nonce for CCM computation. + * + * \param ctx CCM context structure. + * \param bctx block cipher context (already initialised with secret key). + */ +void br_ccm_init(br_ccm_context *ctx, const br_block_ctrcbc_class **bctx); + +/** + * \brief Reset a CCM context. + * + * This function resets an already initialised CCM context for a new + * computation run. Implementations and keys are conserved. This function + * can be called at any time; it cancels any ongoing CCM computation that + * uses the provided context structure. + * + * The `aad_len` parameter contains the total length, in bytes, of the + * additional authenticated data. It may be zero. That length MUST be + * exact. + * + * The `data_len` parameter contains the total length, in bytes, of the + * data that will be injected (plaintext or ciphertext). That length MUST + * be exact. Moreover, that length MUST be less than 2^(8*(15-nonce_len)). + * + * The nonce length (`nonce_len`), in bytes, must be in the 7..13 range + * (inclusive). + * + * The tag length (`tag_len`), in bytes, must be in the 4..16 range, and + * be an even integer. Short tags mechanically allow for higher forgery + * probabilities; hence, tag sizes smaller than 12 bytes shall be used only + * with care. + * + * It is critical to CCM security that nonce values are not repeated for + * the same encryption key. Random generation of nonces is not generally + * recommended, due to the relatively small maximum nonce value. + * + * Returned value is 1 on success, 0 on error. An error is reported if + * the tag or nonce length is out of range, or if the + * plaintext/ciphertext length cannot be encoded with the specified + * nonce length. + * + * \param ctx CCM context structure. + * \param nonce CCM nonce to use. + * \param nonce_len CCM nonce length (in bytes, 7 to 13). + * \param aad_len additional authenticated data length (in bytes). + * \param data_len plaintext/ciphertext length (in bytes). + * \param tag_len tag length (in bytes). + * \return 1 on success, 0 on error. + */ +int br_ccm_reset(br_ccm_context *ctx, const void *nonce, size_t nonce_len, + uint64_t aad_len, uint64_t data_len, size_t tag_len); + +/** + * \brief Inject additional authenticated data into CCM. + * + * The provided data is injected into a running CCM computation. Additional + * data must be injected _before_ the call to `br_ccm_flip()`. + * Additional data can be injected in several chunks of arbitrary length, + * but the total amount MUST exactly match the value which was provided + * to `br_ccm_reset()`. + * + * \param ctx CCM context structure. + * \param data pointer to additional authenticated data. + * \param len length of additional authenticated data (in bytes). + */ +void br_ccm_aad_inject(br_ccm_context *ctx, const void *data, size_t len); + +/** + * \brief Finish injection of additional authenticated data into CCM. + * + * This function MUST be called before beginning the actual encryption + * or decryption (with `br_ccm_run()`), even if no additional authenticated + * data was injected. No additional authenticated data may be injected + * after this function call. + * + * \param ctx CCM context structure. + */ +void br_ccm_flip(br_ccm_context *ctx); + +/** + * \brief Encrypt or decrypt some data with CCM. + * + * Data encryption or decryption can be done after `br_ccm_flip()` + * has been called on the context. If `encrypt` is non-zero, then the + * provided data shall be plaintext, and it is encrypted in place. + * Otherwise, the data shall be ciphertext, and it is decrypted in place. + * + * Data may be provided in several chunks of arbitrary length, provided + * that the total length exactly matches the length provided to the + * `br_ccm_reset()` call. + * + * \param ctx CCM context structure. + * \param encrypt non-zero for encryption, zero for decryption. + * \param data data to encrypt or decrypt. + * \param len data length (in bytes). + */ +void br_ccm_run(br_ccm_context *ctx, int encrypt, void *data, size_t len); + +/** + * \brief Compute CCM authentication tag. + * + * Compute the CCM authentication tag. This call terminates the CCM + * run: all data must have been injected with `br_ccm_run()` (in zero, + * one or more successive calls). After this function has been called, + * no more data can br processed; a `br_ccm_reset()` call is required + * to start a new message. + * + * The tag length was provided upon context initialisation (last call + * to `br_ccm_reset()`); it is returned by this function. + * + * The tag value must normally be sent along with the encrypted data. + * When decrypting, the tag value must be recomputed and compared with + * the received tag: if the two tag values differ, then either the tag + * or the encrypted data was altered in transit. As an alternative to + * this function, the `br_ccm_check_tag()` function can be used to + * compute and check the tag value. + * + * \param ctx CCM context structure. + * \param tag destination buffer for the tag (up to 16 bytes). + * \return the tag length (in bytes). + */ +size_t br_ccm_get_tag(br_ccm_context *ctx, void *tag); + +/** + * \brief Compute and check CCM authentication tag. + * + * This function is an alternative to `br_ccm_get_tag()`, normally used + * on the receiving end (i.e. when decrypting value). The tag value is + * recomputed and compared with the provided tag value. If they match, 1 + * is returned; on mismatch, 0 is returned. A returned value of 0 means + * that the data or the tag was altered in transit, normally leading to + * wholesale rejection of the complete message. + * + * \param ctx CCM context structure. + * \param tag tag value to compare with (up to 16 bytes). + * \return 1 on success (exact match of tag value), 0 otherwise. + */ +uint32_t br_ccm_check_tag(br_ccm_context *ctx, const void *tag); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/bearssl-esp8266/src/t_bearssl_block.h b/lib/bearssl-esp8266/src/t_bearssl_block.h new file mode 100644 index 000000000..683a4906d --- /dev/null +++ b/lib/bearssl-esp8266/src/t_bearssl_block.h @@ -0,0 +1,2618 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef BR_BEARSSL_BLOCK_H__ +#define BR_BEARSSL_BLOCK_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** \file bearssl_block.h + * + * # Block Ciphers and Symmetric Ciphers + * + * This file documents the API for block ciphers and other symmetric + * ciphers. + * + * + * ## Procedural API + * + * For a block cipher implementation, up to three separate sets of + * functions are provided, for CBC encryption, CBC decryption, and CTR + * encryption/decryption. Each set has its own context structure, + * initialised with the encryption key. + * + * For CBC encryption and decryption, the data to encrypt or decrypt is + * referenced as a sequence of blocks. The implementations assume that + * there is no partial block; no padding is applied or removed. The + * caller is responsible for handling any kind of padding. + * + * Function for CTR encryption are defined only for block ciphers with + * blocks of 16 bytes or more (i.e. AES, but not DES/3DES). + * + * Each implemented block cipher is identified by an "internal name" + * from which are derived the names of structures and functions that + * implement the cipher. For the block cipher of internal name "`xxx`", + * the following are defined: + * + * - `br_xxx_BLOCK_SIZE` + * + * A macro that evaluates to the block size (in bytes) of the + * cipher. For all implemented block ciphers, this value is a + * power of two. + * + * - `br_xxx_cbcenc_keys` + * + * Context structure that contains the subkeys resulting from the key + * expansion. These subkeys are appropriate for CBC encryption. The + * structure first field is called `vtable` and points to the + * appropriate OOP structure. + * + * - `br_xxx_cbcenc_init(br_xxx_cbcenc_keys *ctx, const void *key, size_t len)` + * + * Perform key expansion: subkeys for CBC encryption are computed and + * written in the provided context structure. The key length MUST be + * adequate for the implemented block cipher. This function also sets + * the `vtable` field. + * + * - `br_xxx_cbcenc_run(const br_xxx_cbcenc_keys *ctx, void *iv, void *data, size_t len)` + * + * Perform CBC encryption of `len` bytes, in place. The encrypted data + * replaces the cleartext. `len` MUST be a multiple of the block length + * (if it is not, the function may loop forever or overflow a buffer). + * The IV is provided with the `iv` pointer; it is also updated with + * a copy of the last encrypted block. + * + * - `br_xxx_cbcdec_keys` + * + * Context structure that contains the subkeys resulting from the key + * expansion. These subkeys are appropriate for CBC decryption. The + * structure first field is called `vtable` and points to the + * appropriate OOP structure. + * + * - `br_xxx_cbcdec_init(br_xxx_cbcenc_keys *ctx, const void *key, size_t len)` + * + * Perform key expansion: subkeys for CBC decryption are computed and + * written in the provided context structure. The key length MUST be + * adequate for the implemented block cipher. This function also sets + * the `vtable` field. + * + * - `br_xxx_cbcdec_run(const br_xxx_cbcdec_keys *ctx, void *iv, void *data, size_t num_blocks)` + * + * Perform CBC decryption of `len` bytes, in place. The decrypted data + * replaces the ciphertext. `len` MUST be a multiple of the block length + * (if it is not, the function may loop forever or overflow a buffer). + * The IV is provided with the `iv` pointer; it is also updated with + * a copy of the last _encrypted_ block. + * + * - `br_xxx_ctr_keys` + * + * Context structure that contains the subkeys resulting from the key + * expansion. These subkeys are appropriate for CTR encryption and + * decryption. The structure first field is called `vtable` and + * points to the appropriate OOP structure. + * + * - `br_xxx_ctr_init(br_xxx_ctr_keys *ctx, const void *key, size_t len)` + * + * Perform key expansion: subkeys for CTR encryption and decryption + * are computed and written in the provided context structure. The + * key length MUST be adequate for the implemented block cipher. This + * function also sets the `vtable` field. + * + * - `br_xxx_ctr_run(const br_xxx_ctr_keys *ctx, const void *iv, uint32_t cc, void *data, size_t len)` (returns `uint32_t`) + * + * Perform CTR encryption/decryption of some data. Processing is done + * "in place" (the output data replaces the input data). This function + * implements the "standard incrementing function" from NIST SP800-38A, + * annex B: the IV length shall be 4 bytes less than the block size + * (i.e. 12 bytes for AES) and the counter is the 32-bit value starting + * with `cc`. The data length (`len`) is not necessarily a multiple of + * the block size. The new counter value is returned, which supports + * chunked processing, provided that each chunk length (except possibly + * the last one) is a multiple of the block size. + * + * - `br_xxx_ctrcbc_keys` + * + * Context structure that contains the subkeys resulting from the + * key expansion. These subkeys are appropriate for doing combined + * CTR encryption/decryption and CBC-MAC, as used in the CCM and EAX + * authenticated encryption modes. The structure first field is + * called `vtable` and points to the appropriate OOP structure. + * + * - `br_xxx_ctrcbc_init(br_xxx_ctr_keys *ctx, const void *key, size_t len)` + * + * Perform key expansion: subkeys for combined CTR + * encryption/decryption and CBC-MAC are computed and written in the + * provided context structure. The key length MUST be adequate for + * the implemented block cipher. This function also sets the + * `vtable` field. + * + * - `br_xxx_ctrcbc_encrypt(const br_xxx_ctrcbc_keys *ctx, void *ctr, void *cbcmac, void *data, size_t len)` + * + * Perform CTR encryption of some data, and CBC-MAC. Processing is + * done "in place" (the output data replaces the input data). This + * function applies CTR encryption on the data, using a full + * block-size counter (i.e. for 128-bit blocks, the counter is + * incremented as a 128-bit value). The 'ctr' array contains the + * initial value for the counter (used in the first block) and it is + * updated with the new value after data processing. The 'cbcmac' + * value shall point to a block-sized value which is used as IV for + * CBC-MAC, computed over the encrypted data (output of CTR + * encryption); the resulting CBC-MAC is written over 'cbcmac' on + * output. + * + * The data length MUST be a multiple of the block size. + * + * - `br_xxx_ctrcbc_decrypt(const br_xxx_ctrcbc_keys *ctx, void *ctr, void *cbcmac, void *data, size_t len)` + * + * Perform CTR decryption of some data, and CBC-MAC. Processing is + * done "in place" (the output data replaces the input data). This + * function applies CTR decryption on the data, using a full + * block-size counter (i.e. for 128-bit blocks, the counter is + * incremented as a 128-bit value). The 'ctr' array contains the + * initial value for the counter (used in the first block) and it is + * updated with the new value after data processing. The 'cbcmac' + * value shall point to a block-sized value which is used as IV for + * CBC-MAC, computed over the encrypted data (input of CTR + * encryption); the resulting CBC-MAC is written over 'cbcmac' on + * output. + * + * The data length MUST be a multiple of the block size. + * + * - `br_xxx_ctrcbc_ctr(const br_xxx_ctrcbc_keys *ctx, void *ctr, void *data, size_t len)` + * + * Perform CTR encryption or decryption of the provided data. The + * data is processed "in place" (the output data replaces the input + * data). A full block-sized counter is applied (i.e. for 128-bit + * blocks, the counter is incremented as a 128-bit value). The 'ctr' + * array contains the initial value for the counter (used in the + * first block), and it is updated with the new value after data + * processing. + * + * The data length MUST be a multiple of the block size. + * + * - `br_xxx_ctrcbc_mac(const br_xxx_ctrcbc_keys *ctx, void *cbcmac, const void *data, size_t len)` + * + * Compute CBC-MAC over the provided data. The IV for CBC-MAC is + * provided as 'cbcmac'; the output is written over the same array. + * The data itself is untouched. The data length MUST be a multiple + * of the block size. + * + * + * It shall be noted that the key expansion functions return `void`. If + * the provided key length is not allowed, then there will be no error + * reporting; implementations need not validate the key length, thus an + * invalid key length may result in undefined behaviour (e.g. buffer + * overflow). + * + * Subkey structures contain no interior pointer, and no external + * resources are allocated upon key expansion. They can thus be + * discarded without any explicit deallocation. + * + * + * ## Object-Oriented API + * + * Each context structure begins with a field (called `vtable`) that + * points to an instance of a structure that references the relevant + * functions through pointers. Each such structure contains the + * following: + * + * - `context_size` + * + * The size (in bytes) of the context structure for subkeys. + * + * - `block_size` + * + * The cipher block size (in bytes). + * + * - `log_block_size` + * + * The base-2 logarithm of cipher block size (e.g. 4 for blocks + * of 16 bytes). + * + * - `init` + * + * Pointer to the key expansion function. + * + * - `run` + * + * Pointer to the encryption/decryption function. + * + * For combined CTR/CBC-MAC encryption, the `vtable` has a slightly + * different structure: + * + * - `context_size` + * + * The size (in bytes) of the context structure for subkeys. + * + * - `block_size` + * + * The cipher block size (in bytes). + * + * - `log_block_size` + * + * The base-2 logarithm of cipher block size (e.g. 4 for blocks + * of 16 bytes). + * + * - `init` + * + * Pointer to the key expansion function. + * + * - `encrypt` + * + * Pointer to the CTR encryption + CBC-MAC function. + * + * - `decrypt` + * + * Pointer to the CTR decryption + CBC-MAC function. + * + * - `ctr` + * + * Pointer to the CTR encryption/decryption function. + * + * - `mac` + * + * Pointer to the CBC-MAC function. + * + * For block cipher "`xxx`", static, constant instances of these + * structures are defined, under the names: + * + * - `br_xxx_cbcenc_vtable` + * - `br_xxx_cbcdec_vtable` + * - `br_xxx_ctr_vtable` + * - `br_xxx_ctrcbc_vtable` + * + * + * ## Implemented Block Ciphers + * + * Provided implementations are: + * + * | Name | Function | Block Size (bytes) | Key lengths (bytes) | + * | :-------- | :------- | :----------------: | :-----------------: | + * | aes_big | AES | 16 | 16, 24 and 32 | + * | aes_small | AES | 16 | 16, 24 and 32 | + * | aes_ct | AES | 16 | 16, 24 and 32 | + * | aes_ct64 | AES | 16 | 16, 24 and 32 | + * | aes_x86ni | AES | 16 | 16, 24 and 32 | + * | aes_pwr8 | AES | 16 | 16, 24 and 32 | + * | des_ct | DES/3DES | 8 | 8, 16 and 24 | + * | des_tab | DES/3DES | 8 | 8, 16 and 24 | + * + * **Note:** DES/3DES nominally uses keys of 64, 128 and 192 bits (i.e. 8, + * 16 and 24 bytes), but some of the bits are ignored by the algorithm, so + * the _effective_ key lengths, from a security point of view, are 56, + * 112 and 168 bits, respectively. + * + * `aes_big` is a "classical" AES implementation, using tables. It + * is fast but not constant-time, since it makes data-dependent array + * accesses. + * + * `aes_small` is an AES implementation optimized for code size. It + * is substantially slower than `aes_big`; it is not constant-time + * either. + * + * `aes_ct` is a constant-time implementation of AES; its code is about + * as big as that of `aes_big`, while its performance is comparable to + * that of `aes_small`. However, it is constant-time. This + * implementation should thus be considered to be the "default" AES in + * BearSSL, to be used unless the operational context guarantees that a + * non-constant-time implementation is safe, or an architecture-specific + * constant-time implementation can be used (e.g. using dedicated + * hardware opcodes). + * + * `aes_ct64` is another constant-time implementation of AES. It is + * similar to `aes_ct` but uses 64-bit values. On 32-bit machines, + * `aes_ct64` is not faster than `aes_ct`, often a bit slower, and has + * a larger footprint; however, on 64-bit architectures, `aes_ct64` + * is typically twice faster than `aes_ct` for modes that allow parallel + * operations (i.e. CTR, and CBC decryption, but not CBC encryption). + * + * `aes_x86ni` exists only on x86 architectures (32-bit and 64-bit). It + * uses the AES-NI opcodes when available. + * + * `aes_pwr8` exists only on PowerPC / POWER architectures (32-bit and + * 64-bit, both little-endian and big-endian). It uses the AES opcodes + * present in POWER8 and later. + * + * `des_tab` is a classic, table-based implementation of DES/3DES. It + * is not constant-time. + * + * `des_ct` is an constant-time implementation of DES/3DES. It is + * substantially slower than `des_tab`. + * + * ## ChaCha20 and Poly1305 + * + * ChaCha20 is a stream cipher. Poly1305 is a MAC algorithm. They + * are described in [RFC 7539](https://tools.ietf.org/html/rfc7539). + * + * Two function pointer types are defined: + * + * - `br_chacha20_run` describes a function that implements ChaCha20 + * only. + * + * - `br_poly1305_run` describes an implementation of Poly1305, + * in the AEAD combination with ChaCha20 specified in RFC 7539 + * (the ChaCha20 implementation is provided as a function pointer). + * + * `chacha20_ct` is a straightforward implementation of ChaCha20 in + * plain C; it is constant-time, small, and reasonably fast. + * + * `chacha20_sse2` leverages SSE2 opcodes (on x86 architectures that + * support these opcodes). It is faster than `chacha20_ct`. + * + * `poly1305_ctmul` is an implementation of the ChaCha20+Poly1305 AEAD + * construction, where the Poly1305 part is performed with mixed 32-bit + * multiplications (operands are 32-bit, result is 64-bit). + * + * `poly1305_ctmul32` implements ChaCha20+Poly1305 using pure 32-bit + * multiplications (32-bit operands, 32-bit result). It is slower than + * `poly1305_ctmul`, except on some specific architectures such as + * the ARM Cortex M0+. + * + * `poly1305_ctmulq` implements ChaCha20+Poly1305 with mixed 64-bit + * multiplications (operands are 64-bit, result is 128-bit) on 64-bit + * platforms that support such operations. + * + * `poly1305_i15` implements ChaCha20+Poly1305 with the generic "i15" + * big integer implementation. It is meant mostly for testing purposes, + * although it can help with saving a few hundred bytes of code footprint + * on systems where code size is scarce. + */ + +/** + * \brief Class type for CBC encryption implementations. + * + * A `br_block_cbcenc_class` instance points to the functions implementing + * a specific block cipher, when used in CBC mode for encrypting data. + */ +typedef struct br_block_cbcenc_class_ br_block_cbcenc_class; +struct br_block_cbcenc_class_ { + /** + * \brief Size (in bytes) of the context structure appropriate + * for containing subkeys. + */ + size_t context_size; + + /** + * \brief Size of individual blocks (in bytes). + */ + unsigned block_size; + + /** + * \brief Base-2 logarithm of the size of individual blocks, + * expressed in bytes. + */ + unsigned log_block_size; + + /** + * \brief Initialisation function. + * + * This function sets the `vtable` field in the context structure. + * The key length MUST be one of the key lengths supported by + * the implementation. + * + * \param ctx context structure to initialise. + * \param key secret key. + * \param key_len key length (in bytes). + */ + void (*init)(const br_block_cbcenc_class **ctx, + const void *key, size_t key_len); + + /** + * \brief Run the CBC encryption. + * + * The `iv` parameter points to the IV for this run; it is + * updated with a copy of the last encrypted block. The data + * is encrypted "in place"; its length (`len`) MUST be a + * multiple of the block size. + * + * \param ctx context structure (already initialised). + * \param iv IV for CBC encryption (updated). + * \param data data to encrypt. + * \param len data length (in bytes, multiple of block size). + */ + void (*run)(const br_block_cbcenc_class *const *ctx, + void *iv, void *data, size_t len); +}; + +/** + * \brief Class type for CBC decryption implementations. + * + * A `br_block_cbcdec_class` instance points to the functions implementing + * a specific block cipher, when used in CBC mode for decrypting data. + */ +typedef struct br_block_cbcdec_class_ br_block_cbcdec_class; +struct br_block_cbcdec_class_ { + /** + * \brief Size (in bytes) of the context structure appropriate + * for containing subkeys. + */ + size_t context_size; + + /** + * \brief Size of individual blocks (in bytes). + */ + unsigned block_size; + + /** + * \brief Base-2 logarithm of the size of individual blocks, + * expressed in bytes. + */ + unsigned log_block_size; + + /** + * \brief Initialisation function. + * + * This function sets the `vtable` field in the context structure. + * The key length MUST be one of the key lengths supported by + * the implementation. + * + * \param ctx context structure to initialise. + * \param key secret key. + * \param key_len key length (in bytes). + */ + void (*init)(const br_block_cbcdec_class **ctx, + const void *key, size_t key_len); + + /** + * \brief Run the CBC decryption. + * + * The `iv` parameter points to the IV for this run; it is + * updated with a copy of the last encrypted block. The data + * is decrypted "in place"; its length (`len`) MUST be a + * multiple of the block size. + * + * \param ctx context structure (already initialised). + * \param iv IV for CBC decryption (updated). + * \param data data to decrypt. + * \param len data length (in bytes, multiple of block size). + */ + void (*run)(const br_block_cbcdec_class *const *ctx, + void *iv, void *data, size_t len); +}; + +/** + * \brief Class type for CTR encryption/decryption implementations. + * + * A `br_block_ctr_class` instance points to the functions implementing + * a specific block cipher, when used in CTR mode for encrypting or + * decrypting data. + */ +typedef struct br_block_ctr_class_ br_block_ctr_class; +struct br_block_ctr_class_ { + /** + * \brief Size (in bytes) of the context structure appropriate + * for containing subkeys. + */ + size_t context_size; + + /** + * \brief Size of individual blocks (in bytes). + */ + unsigned block_size; + + /** + * \brief Base-2 logarithm of the size of individual blocks, + * expressed in bytes. + */ + unsigned log_block_size; + + /** + * \brief Initialisation function. + * + * This function sets the `vtable` field in the context structure. + * The key length MUST be one of the key lengths supported by + * the implementation. + * + * \param ctx context structure to initialise. + * \param key secret key. + * \param key_len key length (in bytes). + */ + void (*init)(const br_block_ctr_class **ctx, + const void *key, size_t key_len); + + /** + * \brief Run the CTR encryption or decryption. + * + * The `iv` parameter points to the IV for this run; its + * length is exactly 4 bytes less than the block size (e.g. + * 12 bytes for AES/CTR). The IV is combined with a 32-bit + * block counter to produce the block value which is processed + * with the block cipher. + * + * The data to encrypt or decrypt is updated "in place". Its + * length (`len` bytes) is not required to be a multiple of + * the block size; if the final block is partial, then the + * corresponding key stream bits are dropped. + * + * The resulting counter value is returned. + * + * \param ctx context structure (already initialised). + * \param iv IV for CTR encryption/decryption. + * \param cc initial value for the block counter. + * \param data data to encrypt or decrypt. + * \param len data length (in bytes). + * \return the new block counter value. + */ + uint32_t (*run)(const br_block_ctr_class *const *ctx, + const void *iv, uint32_t cc, void *data, size_t len); +}; + +/** + * \brief Class type for combined CTR and CBC-MAC implementations. + * + * A `br_block_ctrcbc_class` instance points to the functions implementing + * a specific block cipher, when used in CTR mode for encrypting or + * decrypting data, along with CBC-MAC. + */ +typedef struct br_block_ctrcbc_class_ br_block_ctrcbc_class; +struct br_block_ctrcbc_class_ { + /** + * \brief Size (in bytes) of the context structure appropriate + * for containing subkeys. + */ + size_t context_size; + + /** + * \brief Size of individual blocks (in bytes). + */ + unsigned block_size; + + /** + * \brief Base-2 logarithm of the size of individual blocks, + * expressed in bytes. + */ + unsigned log_block_size; + + /** + * \brief Initialisation function. + * + * This function sets the `vtable` field in the context structure. + * The key length MUST be one of the key lengths supported by + * the implementation. + * + * \param ctx context structure to initialise. + * \param key secret key. + * \param key_len key length (in bytes). + */ + void (*init)(const br_block_ctrcbc_class **ctx, + const void *key, size_t key_len); + + /** + * \brief Run the CTR encryption + CBC-MAC. + * + * The `ctr` parameter points to the counter; its length shall + * be equal to the block size. It is updated by this function + * as encryption proceeds. + * + * The `cbcmac` parameter points to the IV for CBC-MAC. The MAC + * is computed over the encrypted data (output of CTR + * encryption). Its length shall be equal to the block size. The + * computed CBC-MAC value is written over the `cbcmac` array. + * + * The data to encrypt is updated "in place". Its length (`len` + * bytes) MUST be a multiple of the block size. + * + * \param ctx context structure (already initialised). + * \param ctr counter for CTR encryption (initial and final). + * \param cbcmac IV and output buffer for CBC-MAC. + * \param data data to encrypt. + * \param len data length (in bytes). + */ + void (*encrypt)(const br_block_ctrcbc_class *const *ctx, + void *ctr, void *cbcmac, void *data, size_t len); + + /** + * \brief Run the CTR decryption + CBC-MAC. + * + * The `ctr` parameter points to the counter; its length shall + * be equal to the block size. It is updated by this function + * as decryption proceeds. + * + * The `cbcmac` parameter points to the IV for CBC-MAC. The MAC + * is computed over the encrypted data (i.e. before CTR + * decryption). Its length shall be equal to the block size. The + * computed CBC-MAC value is written over the `cbcmac` array. + * + * The data to decrypt is updated "in place". Its length (`len` + * bytes) MUST be a multiple of the block size. + * + * \param ctx context structure (already initialised). + * \param ctr counter for CTR encryption (initial and final). + * \param cbcmac IV and output buffer for CBC-MAC. + * \param data data to decrypt. + * \param len data length (in bytes). + */ + void (*decrypt)(const br_block_ctrcbc_class *const *ctx, + void *ctr, void *cbcmac, void *data, size_t len); + + /** + * \brief Run the CTR encryption/decryption only. + * + * The `ctr` parameter points to the counter; its length shall + * be equal to the block size. It is updated by this function + * as decryption proceeds. + * + * The data to decrypt is updated "in place". Its length (`len` + * bytes) MUST be a multiple of the block size. + * + * \param ctx context structure (already initialised). + * \param ctr counter for CTR encryption (initial and final). + * \param data data to decrypt. + * \param len data length (in bytes). + */ + void (*ctr)(const br_block_ctrcbc_class *const *ctx, + void *ctr, void *data, size_t len); + + /** + * \brief Run the CBC-MAC only. + * + * The `cbcmac` parameter points to the IV for CBC-MAC. The MAC + * is computed over the encrypted data (i.e. before CTR + * decryption). Its length shall be equal to the block size. The + * computed CBC-MAC value is written over the `cbcmac` array. + * + * The data is unmodified. Its length (`len` bytes) MUST be a + * multiple of the block size. + * + * \param ctx context structure (already initialised). + * \param cbcmac IV and output buffer for CBC-MAC. + * \param data data to decrypt. + * \param len data length (in bytes). + */ + void (*mac)(const br_block_ctrcbc_class *const *ctx, + void *cbcmac, const void *data, size_t len); +}; + +/* + * Traditional, table-based AES implementation. It is fast, but uses + * internal tables (in particular a 1 kB table for encryption, another + * 1 kB table for decryption, and a 256-byte table for key schedule), + * and it is not constant-time. In contexts where cache-timing attacks + * apply, this implementation may leak the secret key. + */ + +/** \brief AES block size (16 bytes). */ +#define br_aes_big_BLOCK_SIZE 16 + +/** + * \brief Context for AES subkeys (`aes_big` implementation, CBC encryption). + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** \brief Pointer to vtable for this context. */ + const br_block_cbcenc_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + uint32_t skey[60]; + unsigned num_rounds; +#endif +} br_aes_big_cbcenc_keys; + +/** + * \brief Context for AES subkeys (`aes_big` implementation, CBC decryption). + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** \brief Pointer to vtable for this context. */ + const br_block_cbcdec_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + uint32_t skey[60]; + unsigned num_rounds; +#endif +} br_aes_big_cbcdec_keys; + +/** + * \brief Context for AES subkeys (`aes_big` implementation, CTR encryption + * and decryption). + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** \brief Pointer to vtable for this context. */ + const br_block_ctr_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + uint32_t skey[60]; + unsigned num_rounds; +#endif +} br_aes_big_ctr_keys; + +/** + * \brief Context for AES subkeys (`aes_big` implementation, CTR encryption + * and decryption + CBC-MAC). + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** \brief Pointer to vtable for this context. */ + const br_block_ctrcbc_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + uint32_t skey[60]; + unsigned num_rounds; +#endif +} br_aes_big_ctrcbc_keys; + +/** + * \brief Class instance for AES CBC encryption (`aes_big` implementation). + */ +extern const br_block_cbcenc_class br_aes_big_cbcenc_vtable; + +/** + * \brief Class instance for AES CBC decryption (`aes_big` implementation). + */ +extern const br_block_cbcdec_class br_aes_big_cbcdec_vtable; + +/** + * \brief Class instance for AES CTR encryption and decryption + * (`aes_big` implementation). + */ +extern const br_block_ctr_class br_aes_big_ctr_vtable; + +/** + * \brief Class instance for AES CTR encryption/decryption + CBC-MAC + * (`aes_big` implementation). + */ +extern const br_block_ctrcbc_class br_aes_big_ctrcbc_vtable; + +/** + * \brief Context initialisation (key schedule) for AES CBC encryption + * (`aes_big` implementation). + * + * \param ctx context to initialise. + * \param key secret key. + * \param len secret key length (in bytes). + */ +void br_aes_big_cbcenc_init(br_aes_big_cbcenc_keys *ctx, + const void *key, size_t len); + +/** + * \brief Context initialisation (key schedule) for AES CBC decryption + * (`aes_big` implementation). + * + * \param ctx context to initialise. + * \param key secret key. + * \param len secret key length (in bytes). + */ +void br_aes_big_cbcdec_init(br_aes_big_cbcdec_keys *ctx, + const void *key, size_t len); + +/** + * \brief Context initialisation (key schedule) for AES CTR encryption + * and decryption (`aes_big` implementation). + * + * \param ctx context to initialise. + * \param key secret key. + * \param len secret key length (in bytes). + */ +void br_aes_big_ctr_init(br_aes_big_ctr_keys *ctx, + const void *key, size_t len); + +/** + * \brief Context initialisation (key schedule) for AES CTR + CBC-MAC + * (`aes_big` implementation). + * + * \param ctx context to initialise. + * \param key secret key. + * \param len secret key length (in bytes). + */ +void br_aes_big_ctrcbc_init(br_aes_big_ctrcbc_keys *ctx, + const void *key, size_t len); + +/** + * \brief CBC encryption with AES (`aes_big` implementation). + * + * \param ctx context (already initialised). + * \param iv IV (updated). + * \param data data to encrypt (updated). + * \param len data length (in bytes, MUST be multiple of 16). + */ +void br_aes_big_cbcenc_run(const br_aes_big_cbcenc_keys *ctx, void *iv, + void *data, size_t len); + +/** + * \brief CBC decryption with AES (`aes_big` implementation). + * + * \param ctx context (already initialised). + * \param iv IV (updated). + * \param data data to decrypt (updated). + * \param len data length (in bytes, MUST be multiple of 16). + */ +void br_aes_big_cbcdec_run(const br_aes_big_cbcdec_keys *ctx, void *iv, + void *data, size_t len); + +/** + * \brief CTR encryption and decryption with AES (`aes_big` implementation). + * + * \param ctx context (already initialised). + * \param iv IV (constant, 12 bytes). + * \param cc initial block counter value. + * \param data data to encrypt or decrypt (updated). + * \param len data length (in bytes). + * \return new block counter value. + */ +uint32_t br_aes_big_ctr_run(const br_aes_big_ctr_keys *ctx, + const void *iv, uint32_t cc, void *data, size_t len); + +/** + * \brief CTR encryption + CBC-MAC with AES (`aes_big` implementation). + * + * \param ctx context (already initialised). + * \param ctr counter for CTR (16 bytes, updated). + * \param cbcmac IV for CBC-MAC (updated). + * \param data data to encrypt (updated). + * \param len data length (in bytes, MUST be a multiple of 16). + */ +void br_aes_big_ctrcbc_encrypt(const br_aes_big_ctrcbc_keys *ctx, + void *ctr, void *cbcmac, void *data, size_t len); + +/** + * \brief CTR decryption + CBC-MAC with AES (`aes_big` implementation). + * + * \param ctx context (already initialised). + * \param ctr counter for CTR (16 bytes, updated). + * \param cbcmac IV for CBC-MAC (updated). + * \param data data to decrypt (updated). + * \param len data length (in bytes, MUST be a multiple of 16). + */ +void br_aes_big_ctrcbc_decrypt(const br_aes_big_ctrcbc_keys *ctx, + void *ctr, void *cbcmac, void *data, size_t len); + +/** + * \brief CTR encryption/decryption with AES (`aes_big` implementation). + * + * \param ctx context (already initialised). + * \param ctr counter for CTR (16 bytes, updated). + * \param data data to MAC (updated). + * \param len data length (in bytes, MUST be a multiple of 16). + */ +void br_aes_big_ctrcbc_ctr(const br_aes_big_ctrcbc_keys *ctx, + void *ctr, void *data, size_t len); + +/** + * \brief CBC-MAC with AES (`aes_big` implementation). + * + * \param ctx context (already initialised). + * \param cbcmac IV for CBC-MAC (updated). + * \param data data to MAC (unmodified). + * \param len data length (in bytes, MUST be a multiple of 16). + */ +void br_aes_big_ctrcbc_mac(const br_aes_big_ctrcbc_keys *ctx, + void *cbcmac, const void *data, size_t len); + +/* + * AES implementation optimized for size. It is slower than the + * traditional table-based AES implementation, but requires much less + * code. It still uses data-dependent table accesses (albeit within a + * much smaller 256-byte table), which makes it conceptually vulnerable + * to cache-timing attacks. + */ + +/** \brief AES block size (16 bytes). */ +#define br_aes_small_BLOCK_SIZE 16 + +/** + * \brief Context for AES subkeys (`aes_small` implementation, CBC encryption). + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** \brief Pointer to vtable for this context. */ + const br_block_cbcenc_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + uint32_t skey[60]; + unsigned num_rounds; +#endif +} br_aes_small_cbcenc_keys; + +/** + * \brief Context for AES subkeys (`aes_small` implementation, CBC decryption). + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** \brief Pointer to vtable for this context. */ + const br_block_cbcdec_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + uint32_t skey[60]; + unsigned num_rounds; +#endif +} br_aes_small_cbcdec_keys; + +/** + * \brief Context for AES subkeys (`aes_small` implementation, CTR encryption + * and decryption). + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** \brief Pointer to vtable for this context. */ + const br_block_ctr_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + uint32_t skey[60]; + unsigned num_rounds; +#endif +} br_aes_small_ctr_keys; + +/** + * \brief Context for AES subkeys (`aes_small` implementation, CTR encryption + * and decryption + CBC-MAC). + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** \brief Pointer to vtable for this context. */ + const br_block_ctrcbc_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + uint32_t skey[60]; + unsigned num_rounds; +#endif +} br_aes_small_ctrcbc_keys; + +/** + * \brief Class instance for AES CBC encryption (`aes_small` implementation). + */ +extern const br_block_cbcenc_class br_aes_small_cbcenc_vtable; + +/** + * \brief Class instance for AES CBC decryption (`aes_small` implementation). + */ +extern const br_block_cbcdec_class br_aes_small_cbcdec_vtable; + +/** + * \brief Class instance for AES CTR encryption and decryption + * (`aes_small` implementation). + */ +extern const br_block_ctr_class br_aes_small_ctr_vtable; + +/** + * \brief Class instance for AES CTR encryption/decryption + CBC-MAC + * (`aes_small` implementation). + */ +extern const br_block_ctrcbc_class br_aes_small_ctrcbc_vtable; + +/** + * \brief Context initialisation (key schedule) for AES CBC encryption + * (`aes_small` implementation). + * + * \param ctx context to initialise. + * \param key secret key. + * \param len secret key length (in bytes). + */ +void br_aes_small_cbcenc_init(br_aes_small_cbcenc_keys *ctx, + const void *key, size_t len); + +/** + * \brief Context initialisation (key schedule) for AES CBC decryption + * (`aes_small` implementation). + * + * \param ctx context to initialise. + * \param key secret key. + * \param len secret key length (in bytes). + */ +void br_aes_small_cbcdec_init(br_aes_small_cbcdec_keys *ctx, + const void *key, size_t len); + +/** + * \brief Context initialisation (key schedule) for AES CTR encryption + * and decryption (`aes_small` implementation). + * + * \param ctx context to initialise. + * \param key secret key. + * \param len secret key length (in bytes). + */ +void br_aes_small_ctr_init(br_aes_small_ctr_keys *ctx, + const void *key, size_t len); + +/** + * \brief Context initialisation (key schedule) for AES CTR + CBC-MAC + * (`aes_small` implementation). + * + * \param ctx context to initialise. + * \param key secret key. + * \param len secret key length (in bytes). + */ +void br_aes_small_ctrcbc_init(br_aes_small_ctrcbc_keys *ctx, + const void *key, size_t len); + +/** + * \brief CBC encryption with AES (`aes_small` implementation). + * + * \param ctx context (already initialised). + * \param iv IV (updated). + * \param data data to encrypt (updated). + * \param len data length (in bytes, MUST be multiple of 16). + */ +void br_aes_small_cbcenc_run(const br_aes_small_cbcenc_keys *ctx, void *iv, + void *data, size_t len); + +/** + * \brief CBC decryption with AES (`aes_small` implementation). + * + * \param ctx context (already initialised). + * \param iv IV (updated). + * \param data data to decrypt (updated). + * \param len data length (in bytes, MUST be multiple of 16). + */ +void br_aes_small_cbcdec_run(const br_aes_small_cbcdec_keys *ctx, void *iv, + void *data, size_t len); + +/** + * \brief CTR encryption and decryption with AES (`aes_small` implementation). + * + * \param ctx context (already initialised). + * \param iv IV (constant, 12 bytes). + * \param cc initial block counter value. + * \param data data to decrypt (updated). + * \param len data length (in bytes). + * \return new block counter value. + */ +uint32_t br_aes_small_ctr_run(const br_aes_small_ctr_keys *ctx, + const void *iv, uint32_t cc, void *data, size_t len); + +/** + * \brief CTR encryption + CBC-MAC with AES (`aes_small` implementation). + * + * \param ctx context (already initialised). + * \param ctr counter for CTR (16 bytes, updated). + * \param cbcmac IV for CBC-MAC (updated). + * \param data data to encrypt (updated). + * \param len data length (in bytes, MUST be a multiple of 16). + */ +void br_aes_small_ctrcbc_encrypt(const br_aes_small_ctrcbc_keys *ctx, + void *ctr, void *cbcmac, void *data, size_t len); + +/** + * \brief CTR decryption + CBC-MAC with AES (`aes_small` implementation). + * + * \param ctx context (already initialised). + * \param ctr counter for CTR (16 bytes, updated). + * \param cbcmac IV for CBC-MAC (updated). + * \param data data to decrypt (updated). + * \param len data length (in bytes, MUST be a multiple of 16). + */ +void br_aes_small_ctrcbc_decrypt(const br_aes_small_ctrcbc_keys *ctx, + void *ctr, void *cbcmac, void *data, size_t len); + +/** + * \brief CTR encryption/decryption with AES (`aes_small` implementation). + * + * \param ctx context (already initialised). + * \param ctr counter for CTR (16 bytes, updated). + * \param data data to MAC (updated). + * \param len data length (in bytes, MUST be a multiple of 16). + */ +void br_aes_small_ctrcbc_ctr(const br_aes_small_ctrcbc_keys *ctx, + void *ctr, void *data, size_t len); + +/** + * \brief CBC-MAC with AES (`aes_small` implementation). + * + * \param ctx context (already initialised). + * \param cbcmac IV for CBC-MAC (updated). + * \param data data to MAC (unmodified). + * \param len data length (in bytes, MUST be a multiple of 16). + */ +void br_aes_small_ctrcbc_mac(const br_aes_small_ctrcbc_keys *ctx, + void *cbcmac, const void *data, size_t len); + +/* + * Constant-time AES implementation. Its size is similar to that of + * 'aes_big', and its performance is similar to that of 'aes_small' (faster + * decryption, slower encryption). However, it is constant-time, i.e. + * immune to cache-timing and similar attacks. + */ + +/** \brief AES block size (16 bytes). */ +#define br_aes_ct_BLOCK_SIZE 16 + +/** + * \brief Context for AES subkeys (`aes_ct` implementation, CBC encryption). + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** \brief Pointer to vtable for this context. */ + const br_block_cbcenc_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + uint32_t skey[60]; + unsigned num_rounds; +#endif +} br_aes_ct_cbcenc_keys; + +/** + * \brief Context for AES subkeys (`aes_ct` implementation, CBC decryption). + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** \brief Pointer to vtable for this context. */ + const br_block_cbcdec_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + uint32_t skey[60]; + unsigned num_rounds; +#endif +} br_aes_ct_cbcdec_keys; + +/** + * \brief Context for AES subkeys (`aes_ct` implementation, CTR encryption + * and decryption). + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** \brief Pointer to vtable for this context. */ + const br_block_ctr_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + uint32_t skey[60]; + unsigned num_rounds; +#endif +} br_aes_ct_ctr_keys; + +/** + * \brief Context for AES subkeys (`aes_ct` implementation, CTR encryption + * and decryption + CBC-MAC). + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** \brief Pointer to vtable for this context. */ + const br_block_ctrcbc_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + uint32_t skey[60]; + unsigned num_rounds; +#endif +} br_aes_ct_ctrcbc_keys; + +/** + * \brief Class instance for AES CBC encryption (`aes_ct` implementation). + */ +extern const br_block_cbcenc_class br_aes_ct_cbcenc_vtable; + +/** + * \brief Class instance for AES CBC decryption (`aes_ct` implementation). + */ +extern const br_block_cbcdec_class br_aes_ct_cbcdec_vtable; + +/** + * \brief Class instance for AES CTR encryption and decryption + * (`aes_ct` implementation). + */ +extern const br_block_ctr_class br_aes_ct_ctr_vtable; + +/** + * \brief Class instance for AES CTR encryption/decryption + CBC-MAC + * (`aes_ct` implementation). + */ +extern const br_block_ctrcbc_class br_aes_ct_ctrcbc_vtable; + +/** + * \brief Context initialisation (key schedule) for AES CBC encryption + * (`aes_ct` implementation). + * + * \param ctx context to initialise. + * \param key secret key. + * \param len secret key length (in bytes). + */ +void br_aes_ct_cbcenc_init(br_aes_ct_cbcenc_keys *ctx, + const void *key, size_t len); + +/** + * \brief Context initialisation (key schedule) for AES CBC decryption + * (`aes_ct` implementation). + * + * \param ctx context to initialise. + * \param key secret key. + * \param len secret key length (in bytes). + */ +void br_aes_ct_cbcdec_init(br_aes_ct_cbcdec_keys *ctx, + const void *key, size_t len); + +/** + * \brief Context initialisation (key schedule) for AES CTR encryption + * and decryption (`aes_ct` implementation). + * + * \param ctx context to initialise. + * \param key secret key. + * \param len secret key length (in bytes). + */ +void br_aes_ct_ctr_init(br_aes_ct_ctr_keys *ctx, + const void *key, size_t len); + +/** + * \brief Context initialisation (key schedule) for AES CTR + CBC-MAC + * (`aes_ct` implementation). + * + * \param ctx context to initialise. + * \param key secret key. + * \param len secret key length (in bytes). + */ +void br_aes_ct_ctrcbc_init(br_aes_ct_ctrcbc_keys *ctx, + const void *key, size_t len); + +/** + * \brief CBC encryption with AES (`aes_ct` implementation). + * + * \param ctx context (already initialised). + * \param iv IV (updated). + * \param data data to encrypt (updated). + * \param len data length (in bytes, MUST be multiple of 16). + */ +void br_aes_ct_cbcenc_run(const br_aes_ct_cbcenc_keys *ctx, void *iv, + void *data, size_t len); + +/** + * \brief CBC decryption with AES (`aes_ct` implementation). + * + * \param ctx context (already initialised). + * \param iv IV (updated). + * \param data data to decrypt (updated). + * \param len data length (in bytes, MUST be multiple of 16). + */ +void br_aes_ct_cbcdec_run(const br_aes_ct_cbcdec_keys *ctx, void *iv, + void *data, size_t len); + +/** + * \brief CTR encryption and decryption with AES (`aes_ct` implementation). + * + * \param ctx context (already initialised). + * \param iv IV (constant, 12 bytes). + * \param cc initial block counter value. + * \param data data to decrypt (updated). + * \param len data length (in bytes). + * \return new block counter value. + */ +uint32_t br_aes_ct_ctr_run(const br_aes_ct_ctr_keys *ctx, + const void *iv, uint32_t cc, void *data, size_t len); + +/** + * \brief CTR encryption + CBC-MAC with AES (`aes_ct` implementation). + * + * \param ctx context (already initialised). + * \param ctr counter for CTR (16 bytes, updated). + * \param cbcmac IV for CBC-MAC (updated). + * \param data data to encrypt (updated). + * \param len data length (in bytes, MUST be a multiple of 16). + */ +void br_aes_ct_ctrcbc_encrypt(const br_aes_ct_ctrcbc_keys *ctx, + void *ctr, void *cbcmac, void *data, size_t len); + +/** + * \brief CTR decryption + CBC-MAC with AES (`aes_ct` implementation). + * + * \param ctx context (already initialised). + * \param ctr counter for CTR (16 bytes, updated). + * \param cbcmac IV for CBC-MAC (updated). + * \param data data to decrypt (updated). + * \param len data length (in bytes, MUST be a multiple of 16). + */ +void br_aes_ct_ctrcbc_decrypt(const br_aes_ct_ctrcbc_keys *ctx, + void *ctr, void *cbcmac, void *data, size_t len); + +/** + * \brief CTR encryption/decryption with AES (`aes_ct` implementation). + * + * \param ctx context (already initialised). + * \param ctr counter for CTR (16 bytes, updated). + * \param data data to MAC (updated). + * \param len data length (in bytes, MUST be a multiple of 16). + */ +void br_aes_ct_ctrcbc_ctr(const br_aes_ct_ctrcbc_keys *ctx, + void *ctr, void *data, size_t len); + +/** + * \brief CBC-MAC with AES (`aes_ct` implementation). + * + * \param ctx context (already initialised). + * \param cbcmac IV for CBC-MAC (updated). + * \param data data to MAC (unmodified). + * \param len data length (in bytes, MUST be a multiple of 16). + */ +void br_aes_ct_ctrcbc_mac(const br_aes_ct_ctrcbc_keys *ctx, + void *cbcmac, const void *data, size_t len); + +/* + * 64-bit constant-time AES implementation. It is similar to 'aes_ct' + * but uses 64-bit registers, making it about twice faster than 'aes_ct' + * on 64-bit platforms, while remaining constant-time and with a similar + * code size. (The doubling in performance is only for CBC decryption + * and CTR mode; CBC encryption is non-parallel and cannot benefit from + * the larger registers.) + */ + +/** \brief AES block size (16 bytes). */ +#define br_aes_ct64_BLOCK_SIZE 16 + +/** + * \brief Context for AES subkeys (`aes_ct64` implementation, CBC encryption). + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** \brief Pointer to vtable for this context. */ + const br_block_cbcenc_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + uint64_t skey[30]; + unsigned num_rounds; +#endif +} br_aes_ct64_cbcenc_keys; + +/** + * \brief Context for AES subkeys (`aes_ct64` implementation, CBC decryption). + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** \brief Pointer to vtable for this context. */ + const br_block_cbcdec_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + uint64_t skey[30]; + unsigned num_rounds; +#endif +} br_aes_ct64_cbcdec_keys; + +/** + * \brief Context for AES subkeys (`aes_ct64` implementation, CTR encryption + * and decryption). + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** \brief Pointer to vtable for this context. */ + const br_block_ctr_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + uint64_t skey[30]; + unsigned num_rounds; +#endif +} br_aes_ct64_ctr_keys; + +/** + * \brief Context for AES subkeys (`aes_ct64` implementation, CTR encryption + * and decryption + CBC-MAC). + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** \brief Pointer to vtable for this context. */ + const br_block_ctrcbc_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + uint64_t skey[30]; + unsigned num_rounds; +#endif +} br_aes_ct64_ctrcbc_keys; + +/** + * \brief Class instance for AES CBC encryption (`aes_ct64` implementation). + */ +extern const br_block_cbcenc_class br_aes_ct64_cbcenc_vtable; + +/** + * \brief Class instance for AES CBC decryption (`aes_ct64` implementation). + */ +extern const br_block_cbcdec_class br_aes_ct64_cbcdec_vtable; + +/** + * \brief Class instance for AES CTR encryption and decryption + * (`aes_ct64` implementation). + */ +extern const br_block_ctr_class br_aes_ct64_ctr_vtable; + +/** + * \brief Class instance for AES CTR encryption/decryption + CBC-MAC + * (`aes_ct64` implementation). + */ +extern const br_block_ctrcbc_class br_aes_ct64_ctrcbc_vtable; + +/** + * \brief Context initialisation (key schedule) for AES CBC encryption + * (`aes_ct64` implementation). + * + * \param ctx context to initialise. + * \param key secret key. + * \param len secret key length (in bytes). + */ +void br_aes_ct64_cbcenc_init(br_aes_ct64_cbcenc_keys *ctx, + const void *key, size_t len); + +/** + * \brief Context initialisation (key schedule) for AES CBC decryption + * (`aes_ct64` implementation). + * + * \param ctx context to initialise. + * \param key secret key. + * \param len secret key length (in bytes). + */ +void br_aes_ct64_cbcdec_init(br_aes_ct64_cbcdec_keys *ctx, + const void *key, size_t len); + +/** + * \brief Context initialisation (key schedule) for AES CTR encryption + * and decryption (`aes_ct64` implementation). + * + * \param ctx context to initialise. + * \param key secret key. + * \param len secret key length (in bytes). + */ +void br_aes_ct64_ctr_init(br_aes_ct64_ctr_keys *ctx, + const void *key, size_t len); + +/** + * \brief Context initialisation (key schedule) for AES CTR + CBC-MAC + * (`aes_ct64` implementation). + * + * \param ctx context to initialise. + * \param key secret key. + * \param len secret key length (in bytes). + */ +void br_aes_ct64_ctrcbc_init(br_aes_ct64_ctrcbc_keys *ctx, + const void *key, size_t len); + +/** + * \brief CBC encryption with AES (`aes_ct64` implementation). + * + * \param ctx context (already initialised). + * \param iv IV (updated). + * \param data data to encrypt (updated). + * \param len data length (in bytes, MUST be multiple of 16). + */ +void br_aes_ct64_cbcenc_run(const br_aes_ct64_cbcenc_keys *ctx, void *iv, + void *data, size_t len); + +/** + * \brief CBC decryption with AES (`aes_ct64` implementation). + * + * \param ctx context (already initialised). + * \param iv IV (updated). + * \param data data to decrypt (updated). + * \param len data length (in bytes, MUST be multiple of 16). + */ +void br_aes_ct64_cbcdec_run(const br_aes_ct64_cbcdec_keys *ctx, void *iv, + void *data, size_t len); + +/** + * \brief CTR encryption and decryption with AES (`aes_ct64` implementation). + * + * \param ctx context (already initialised). + * \param iv IV (constant, 12 bytes). + * \param cc initial block counter value. + * \param data data to decrypt (updated). + * \param len data length (in bytes). + * \return new block counter value. + */ +uint32_t br_aes_ct64_ctr_run(const br_aes_ct64_ctr_keys *ctx, + const void *iv, uint32_t cc, void *data, size_t len); + +/** + * \brief CTR encryption + CBC-MAC with AES (`aes_ct64` implementation). + * + * \param ctx context (already initialised). + * \param ctr counter for CTR (16 bytes, updated). + * \param cbcmac IV for CBC-MAC (updated). + * \param data data to encrypt (updated). + * \param len data length (in bytes, MUST be a multiple of 16). + */ +void br_aes_ct64_ctrcbc_encrypt(const br_aes_ct64_ctrcbc_keys *ctx, + void *ctr, void *cbcmac, void *data, size_t len); + +/** + * \brief CTR decryption + CBC-MAC with AES (`aes_ct64` implementation). + * + * \param ctx context (already initialised). + * \param ctr counter for CTR (16 bytes, updated). + * \param cbcmac IV for CBC-MAC (updated). + * \param data data to decrypt (updated). + * \param len data length (in bytes, MUST be a multiple of 16). + */ +void br_aes_ct64_ctrcbc_decrypt(const br_aes_ct64_ctrcbc_keys *ctx, + void *ctr, void *cbcmac, void *data, size_t len); + +/** + * \brief CTR encryption/decryption with AES (`aes_ct64` implementation). + * + * \param ctx context (already initialised). + * \param ctr counter for CTR (16 bytes, updated). + * \param data data to MAC (updated). + * \param len data length (in bytes, MUST be a multiple of 16). + */ +void br_aes_ct64_ctrcbc_ctr(const br_aes_ct64_ctrcbc_keys *ctx, + void *ctr, void *data, size_t len); + +/** + * \brief CBC-MAC with AES (`aes_ct64` implementation). + * + * \param ctx context (already initialised). + * \param cbcmac IV for CBC-MAC (updated). + * \param data data to MAC (unmodified). + * \param len data length (in bytes, MUST be a multiple of 16). + */ +void br_aes_ct64_ctrcbc_mac(const br_aes_ct64_ctrcbc_keys *ctx, + void *cbcmac, const void *data, size_t len); + +/* + * AES implementation using AES-NI opcodes (x86 platform). + */ + +/** \brief AES block size (16 bytes). */ +#define br_aes_x86ni_BLOCK_SIZE 16 + +/** + * \brief Context for AES subkeys (`aes_x86ni` implementation, CBC encryption). + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** \brief Pointer to vtable for this context. */ + const br_block_cbcenc_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + union { + unsigned char skni[16 * 15]; + } skey; + unsigned num_rounds; +#endif +} br_aes_x86ni_cbcenc_keys; + +/** + * \brief Context for AES subkeys (`aes_x86ni` implementation, CBC decryption). + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** \brief Pointer to vtable for this context. */ + const br_block_cbcdec_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + union { + unsigned char skni[16 * 15]; + } skey; + unsigned num_rounds; +#endif +} br_aes_x86ni_cbcdec_keys; + +/** + * \brief Context for AES subkeys (`aes_x86ni` implementation, CTR encryption + * and decryption). + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** \brief Pointer to vtable for this context. */ + const br_block_ctr_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + union { + unsigned char skni[16 * 15]; + } skey; + unsigned num_rounds; +#endif +} br_aes_x86ni_ctr_keys; + +/** + * \brief Context for AES subkeys (`aes_x86ni` implementation, CTR encryption + * and decryption + CBC-MAC). + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** \brief Pointer to vtable for this context. */ + const br_block_ctrcbc_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + union { + unsigned char skni[16 * 15]; + } skey; + unsigned num_rounds; +#endif +} br_aes_x86ni_ctrcbc_keys; + +/** + * \brief Class instance for AES CBC encryption (`aes_x86ni` implementation). + * + * Since this implementation might be omitted from the library, or the + * AES opcode unavailable on the current CPU, a pointer to this class + * instance should be obtained through `br_aes_x86ni_cbcenc_get_vtable()`. + */ +extern const br_block_cbcenc_class br_aes_x86ni_cbcenc_vtable; + +/** + * \brief Class instance for AES CBC decryption (`aes_x86ni` implementation). + * + * Since this implementation might be omitted from the library, or the + * AES opcode unavailable on the current CPU, a pointer to this class + * instance should be obtained through `br_aes_x86ni_cbcdec_get_vtable()`. + */ +extern const br_block_cbcdec_class br_aes_x86ni_cbcdec_vtable; + +/** + * \brief Class instance for AES CTR encryption and decryption + * (`aes_x86ni` implementation). + * + * Since this implementation might be omitted from the library, or the + * AES opcode unavailable on the current CPU, a pointer to this class + * instance should be obtained through `br_aes_x86ni_ctr_get_vtable()`. + */ +extern const br_block_ctr_class br_aes_x86ni_ctr_vtable; + +/** + * \brief Class instance for AES CTR encryption/decryption + CBC-MAC + * (`aes_x86ni` implementation). + * + * Since this implementation might be omitted from the library, or the + * AES opcode unavailable on the current CPU, a pointer to this class + * instance should be obtained through `br_aes_x86ni_ctrcbc_get_vtable()`. + */ +extern const br_block_ctrcbc_class br_aes_x86ni_ctrcbc_vtable; + +/** + * \brief Context initialisation (key schedule) for AES CBC encryption + * (`aes_x86ni` implementation). + * + * \param ctx context to initialise. + * \param key secret key. + * \param len secret key length (in bytes). + */ +void br_aes_x86ni_cbcenc_init(br_aes_x86ni_cbcenc_keys *ctx, + const void *key, size_t len); + +/** + * \brief Context initialisation (key schedule) for AES CBC decryption + * (`aes_x86ni` implementation). + * + * \param ctx context to initialise. + * \param key secret key. + * \param len secret key length (in bytes). + */ +void br_aes_x86ni_cbcdec_init(br_aes_x86ni_cbcdec_keys *ctx, + const void *key, size_t len); + +/** + * \brief Context initialisation (key schedule) for AES CTR encryption + * and decryption (`aes_x86ni` implementation). + * + * \param ctx context to initialise. + * \param key secret key. + * \param len secret key length (in bytes). + */ +void br_aes_x86ni_ctr_init(br_aes_x86ni_ctr_keys *ctx, + const void *key, size_t len); + +/** + * \brief Context initialisation (key schedule) for AES CTR + CBC-MAC + * (`aes_x86ni` implementation). + * + * \param ctx context to initialise. + * \param key secret key. + * \param len secret key length (in bytes). + */ +void br_aes_x86ni_ctrcbc_init(br_aes_x86ni_ctrcbc_keys *ctx, + const void *key, size_t len); + +/** + * \brief CBC encryption with AES (`aes_x86ni` implementation). + * + * \param ctx context (already initialised). + * \param iv IV (updated). + * \param data data to encrypt (updated). + * \param len data length (in bytes, MUST be multiple of 16). + */ +void br_aes_x86ni_cbcenc_run(const br_aes_x86ni_cbcenc_keys *ctx, void *iv, + void *data, size_t len); + +/** + * \brief CBC decryption with AES (`aes_x86ni` implementation). + * + * \param ctx context (already initialised). + * \param iv IV (updated). + * \param data data to decrypt (updated). + * \param len data length (in bytes, MUST be multiple of 16). + */ +void br_aes_x86ni_cbcdec_run(const br_aes_x86ni_cbcdec_keys *ctx, void *iv, + void *data, size_t len); + +/** + * \brief CTR encryption and decryption with AES (`aes_x86ni` implementation). + * + * \param ctx context (already initialised). + * \param iv IV (constant, 12 bytes). + * \param cc initial block counter value. + * \param data data to decrypt (updated). + * \param len data length (in bytes). + * \return new block counter value. + */ +uint32_t br_aes_x86ni_ctr_run(const br_aes_x86ni_ctr_keys *ctx, + const void *iv, uint32_t cc, void *data, size_t len); + +/** + * \brief CTR encryption + CBC-MAC with AES (`aes_x86ni` implementation). + * + * \param ctx context (already initialised). + * \param ctr counter for CTR (16 bytes, updated). + * \param cbcmac IV for CBC-MAC (updated). + * \param data data to encrypt (updated). + * \param len data length (in bytes, MUST be a multiple of 16). + */ +void br_aes_x86ni_ctrcbc_encrypt(const br_aes_x86ni_ctrcbc_keys *ctx, + void *ctr, void *cbcmac, void *data, size_t len); + +/** + * \brief CTR decryption + CBC-MAC with AES (`aes_x86ni` implementation). + * + * \param ctx context (already initialised). + * \param ctr counter for CTR (16 bytes, updated). + * \param cbcmac IV for CBC-MAC (updated). + * \param data data to decrypt (updated). + * \param len data length (in bytes, MUST be a multiple of 16). + */ +void br_aes_x86ni_ctrcbc_decrypt(const br_aes_x86ni_ctrcbc_keys *ctx, + void *ctr, void *cbcmac, void *data, size_t len); + +/** + * \brief CTR encryption/decryption with AES (`aes_x86ni` implementation). + * + * \param ctx context (already initialised). + * \param ctr counter for CTR (16 bytes, updated). + * \param data data to MAC (updated). + * \param len data length (in bytes, MUST be a multiple of 16). + */ +void br_aes_x86ni_ctrcbc_ctr(const br_aes_x86ni_ctrcbc_keys *ctx, + void *ctr, void *data, size_t len); + +/** + * \brief CBC-MAC with AES (`aes_x86ni` implementation). + * + * \param ctx context (already initialised). + * \param cbcmac IV for CBC-MAC (updated). + * \param data data to MAC (unmodified). + * \param len data length (in bytes, MUST be a multiple of 16). + */ +void br_aes_x86ni_ctrcbc_mac(const br_aes_x86ni_ctrcbc_keys *ctx, + void *cbcmac, const void *data, size_t len); + +/** + * \brief Obtain the `aes_x86ni` AES-CBC (encryption) implementation, if + * available. + * + * This function returns a pointer to `br_aes_x86ni_cbcenc_vtable`, if + * that implementation was compiled in the library _and_ the x86 AES + * opcodes are available on the currently running CPU. If either of + * these conditions is not met, then this function returns `NULL`. + * + * \return the `aes_x86ni` AES-CBC (encryption) implementation, or `NULL`. + */ +const br_block_cbcenc_class *br_aes_x86ni_cbcenc_get_vtable(void); + +/** + * \brief Obtain the `aes_x86ni` AES-CBC (decryption) implementation, if + * available. + * + * This function returns a pointer to `br_aes_x86ni_cbcdec_vtable`, if + * that implementation was compiled in the library _and_ the x86 AES + * opcodes are available on the currently running CPU. If either of + * these conditions is not met, then this function returns `NULL`. + * + * \return the `aes_x86ni` AES-CBC (decryption) implementation, or `NULL`. + */ +const br_block_cbcdec_class *br_aes_x86ni_cbcdec_get_vtable(void); + +/** + * \brief Obtain the `aes_x86ni` AES-CTR implementation, if available. + * + * This function returns a pointer to `br_aes_x86ni_ctr_vtable`, if + * that implementation was compiled in the library _and_ the x86 AES + * opcodes are available on the currently running CPU. If either of + * these conditions is not met, then this function returns `NULL`. + * + * \return the `aes_x86ni` AES-CTR implementation, or `NULL`. + */ +const br_block_ctr_class *br_aes_x86ni_ctr_get_vtable(void); + +/** + * \brief Obtain the `aes_x86ni` AES-CTR + CBC-MAC implementation, if + * available. + * + * This function returns a pointer to `br_aes_x86ni_ctrcbc_vtable`, if + * that implementation was compiled in the library _and_ the x86 AES + * opcodes are available on the currently running CPU. If either of + * these conditions is not met, then this function returns `NULL`. + * + * \return the `aes_x86ni` AES-CTR implementation, or `NULL`. + */ +const br_block_ctrcbc_class *br_aes_x86ni_ctrcbc_get_vtable(void); + +/* + * AES implementation using POWER8 opcodes. + */ + +/** \brief AES block size (16 bytes). */ +#define br_aes_pwr8_BLOCK_SIZE 16 + +/** + * \brief Context for AES subkeys (`aes_pwr8` implementation, CBC encryption). + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** \brief Pointer to vtable for this context. */ + const br_block_cbcenc_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + union { + unsigned char skni[16 * 15]; + } skey; + unsigned num_rounds; +#endif +} br_aes_pwr8_cbcenc_keys; + +/** + * \brief Context for AES subkeys (`aes_pwr8` implementation, CBC decryption). + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** \brief Pointer to vtable for this context. */ + const br_block_cbcdec_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + union { + unsigned char skni[16 * 15]; + } skey; + unsigned num_rounds; +#endif +} br_aes_pwr8_cbcdec_keys; + +/** + * \brief Context for AES subkeys (`aes_pwr8` implementation, CTR encryption + * and decryption). + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** \brief Pointer to vtable for this context. */ + const br_block_ctr_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + union { + unsigned char skni[16 * 15]; + } skey; + unsigned num_rounds; +#endif +} br_aes_pwr8_ctr_keys; + +/** + * \brief Context for AES subkeys (`aes_pwr8` implementation, CTR encryption + * and decryption + CBC-MAC). + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** \brief Pointer to vtable for this context. */ + const br_block_ctrcbc_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + union { + unsigned char skni[16 * 15]; + } skey; + unsigned num_rounds; +#endif +} br_aes_pwr8_ctrcbc_keys; + +/** + * \brief Class instance for AES CBC encryption (`aes_pwr8` implementation). + * + * Since this implementation might be omitted from the library, or the + * AES opcode unavailable on the current CPU, a pointer to this class + * instance should be obtained through `br_aes_pwr8_cbcenc_get_vtable()`. + */ +extern const br_block_cbcenc_class br_aes_pwr8_cbcenc_vtable; + +/** + * \brief Class instance for AES CBC decryption (`aes_pwr8` implementation). + * + * Since this implementation might be omitted from the library, or the + * AES opcode unavailable on the current CPU, a pointer to this class + * instance should be obtained through `br_aes_pwr8_cbcdec_get_vtable()`. + */ +extern const br_block_cbcdec_class br_aes_pwr8_cbcdec_vtable; + +/** + * \brief Class instance for AES CTR encryption and decryption + * (`aes_pwr8` implementation). + * + * Since this implementation might be omitted from the library, or the + * AES opcode unavailable on the current CPU, a pointer to this class + * instance should be obtained through `br_aes_pwr8_ctr_get_vtable()`. + */ +extern const br_block_ctr_class br_aes_pwr8_ctr_vtable; + +/** + * \brief Class instance for AES CTR encryption/decryption + CBC-MAC + * (`aes_pwr8` implementation). + * + * Since this implementation might be omitted from the library, or the + * AES opcode unavailable on the current CPU, a pointer to this class + * instance should be obtained through `br_aes_pwr8_ctrcbc_get_vtable()`. + */ +extern const br_block_ctrcbc_class br_aes_pwr8_ctrcbc_vtable; + +/** + * \brief Context initialisation (key schedule) for AES CBC encryption + * (`aes_pwr8` implementation). + * + * \param ctx context to initialise. + * \param key secret key. + * \param len secret key length (in bytes). + */ +void br_aes_pwr8_cbcenc_init(br_aes_pwr8_cbcenc_keys *ctx, + const void *key, size_t len); + +/** + * \brief Context initialisation (key schedule) for AES CBC decryption + * (`aes_pwr8` implementation). + * + * \param ctx context to initialise. + * \param key secret key. + * \param len secret key length (in bytes). + */ +void br_aes_pwr8_cbcdec_init(br_aes_pwr8_cbcdec_keys *ctx, + const void *key, size_t len); + +/** + * \brief Context initialisation (key schedule) for AES CTR encryption + * and decryption (`aes_pwr8` implementation). + * + * \param ctx context to initialise. + * \param key secret key. + * \param len secret key length (in bytes). + */ +void br_aes_pwr8_ctr_init(br_aes_pwr8_ctr_keys *ctx, + const void *key, size_t len); + +/** + * \brief Context initialisation (key schedule) for AES CTR + CBC-MAC + * (`aes_pwr8` implementation). + * + * \param ctx context to initialise. + * \param key secret key. + * \param len secret key length (in bytes). + */ +void br_aes_pwr8_ctrcbc_init(br_aes_pwr8_ctrcbc_keys *ctx, + const void *key, size_t len); + +/** + * \brief CBC encryption with AES (`aes_pwr8` implementation). + * + * \param ctx context (already initialised). + * \param iv IV (updated). + * \param data data to encrypt (updated). + * \param len data length (in bytes, MUST be multiple of 16). + */ +void br_aes_pwr8_cbcenc_run(const br_aes_pwr8_cbcenc_keys *ctx, void *iv, + void *data, size_t len); + +/** + * \brief CBC decryption with AES (`aes_pwr8` implementation). + * + * \param ctx context (already initialised). + * \param iv IV (updated). + * \param data data to decrypt (updated). + * \param len data length (in bytes, MUST be multiple of 16). + */ +void br_aes_pwr8_cbcdec_run(const br_aes_pwr8_cbcdec_keys *ctx, void *iv, + void *data, size_t len); + +/** + * \brief CTR encryption and decryption with AES (`aes_pwr8` implementation). + * + * \param ctx context (already initialised). + * \param iv IV (constant, 12 bytes). + * \param cc initial block counter value. + * \param data data to decrypt (updated). + * \param len data length (in bytes). + * \return new block counter value. + */ +uint32_t br_aes_pwr8_ctr_run(const br_aes_pwr8_ctr_keys *ctx, + const void *iv, uint32_t cc, void *data, size_t len); + +/** + * \brief CTR encryption + CBC-MAC with AES (`aes_pwr8` implementation). + * + * \param ctx context (already initialised). + * \param ctr counter for CTR (16 bytes, updated). + * \param cbcmac IV for CBC-MAC (updated). + * \param data data to encrypt (updated). + * \param len data length (in bytes, MUST be a multiple of 16). + */ +void br_aes_pwr8_ctrcbc_encrypt(const br_aes_pwr8_ctrcbc_keys *ctx, + void *ctr, void *cbcmac, void *data, size_t len); + +/** + * \brief CTR decryption + CBC-MAC with AES (`aes_pwr8` implementation). + * + * \param ctx context (already initialised). + * \param ctr counter for CTR (16 bytes, updated). + * \param cbcmac IV for CBC-MAC (updated). + * \param data data to decrypt (updated). + * \param len data length (in bytes, MUST be a multiple of 16). + */ +void br_aes_pwr8_ctrcbc_decrypt(const br_aes_pwr8_ctrcbc_keys *ctx, + void *ctr, void *cbcmac, void *data, size_t len); + +/** + * \brief CTR encryption/decryption with AES (`aes_pwr8` implementation). + * + * \param ctx context (already initialised). + * \param ctr counter for CTR (16 bytes, updated). + * \param data data to MAC (updated). + * \param len data length (in bytes, MUST be a multiple of 16). + */ +void br_aes_pwr8_ctrcbc_ctr(const br_aes_pwr8_ctrcbc_keys *ctx, + void *ctr, void *data, size_t len); + +/** + * \brief CBC-MAC with AES (`aes_pwr8` implementation). + * + * \param ctx context (already initialised). + * \param cbcmac IV for CBC-MAC (updated). + * \param data data to MAC (unmodified). + * \param len data length (in bytes, MUST be a multiple of 16). + */ +void br_aes_pwr8_ctrcbc_mac(const br_aes_pwr8_ctrcbc_keys *ctx, + void *cbcmac, const void *data, size_t len); + +/** + * \brief Obtain the `aes_pwr8` AES-CBC (encryption) implementation, if + * available. + * + * This function returns a pointer to `br_aes_pwr8_cbcenc_vtable`, if + * that implementation was compiled in the library _and_ the POWER8 + * crypto opcodes are available on the currently running CPU. If either + * of these conditions is not met, then this function returns `NULL`. + * + * \return the `aes_pwr8` AES-CBC (encryption) implementation, or `NULL`. + */ +const br_block_cbcenc_class *br_aes_pwr8_cbcenc_get_vtable(void); + +/** + * \brief Obtain the `aes_pwr8` AES-CBC (decryption) implementation, if + * available. + * + * This function returns a pointer to `br_aes_pwr8_cbcdec_vtable`, if + * that implementation was compiled in the library _and_ the POWER8 + * crypto opcodes are available on the currently running CPU. If either + * of these conditions is not met, then this function returns `NULL`. + * + * \return the `aes_pwr8` AES-CBC (decryption) implementation, or `NULL`. + */ +const br_block_cbcdec_class *br_aes_pwr8_cbcdec_get_vtable(void); + +/** + * \brief Obtain the `aes_pwr8` AES-CTR implementation, if available. + * + * This function returns a pointer to `br_aes_pwr8_ctr_vtable`, if that + * implementation was compiled in the library _and_ the POWER8 crypto + * opcodes are available on the currently running CPU. If either of + * these conditions is not met, then this function returns `NULL`. + * + * \return the `aes_pwr8` AES-CTR implementation, or `NULL`. + */ +const br_block_ctr_class *br_aes_pwr8_ctr_get_vtable(void); + +/** + * \brief Obtain the `aes_pwr8` AES-CTR + CBC-MAC implementation, if + * available. + * + * This function returns a pointer to `br_aes_pwr8_ctrcbc_vtable`, if + * that implementation was compiled in the library _and_ the POWER8 AES + * opcodes are available on the currently running CPU. If either of + * these conditions is not met, then this function returns `NULL`. + * + * \return the `aes_pwr8` AES-CTR implementation, or `NULL`. + */ +const br_block_ctrcbc_class *br_aes_pwr8_ctrcbc_get_vtable(void); + +/** + * \brief Aggregate structure large enough to be used as context for + * subkeys (CBC encryption) for all AES implementations. + */ +typedef union { + const br_block_cbcenc_class *vtable; + br_aes_big_cbcenc_keys c_big; + br_aes_small_cbcenc_keys c_small; + br_aes_ct_cbcenc_keys c_ct; + br_aes_ct64_cbcenc_keys c_ct64; + br_aes_x86ni_cbcenc_keys c_x86ni; + br_aes_pwr8_cbcenc_keys c_pwr8; +} br_aes_gen_cbcenc_keys; + +/** + * \brief Aggregate structure large enough to be used as context for + * subkeys (CBC decryption) for all AES implementations. + */ +typedef union { + const br_block_cbcdec_class *vtable; + br_aes_big_cbcdec_keys c_big; + br_aes_small_cbcdec_keys c_small; + br_aes_ct_cbcdec_keys c_ct; + br_aes_ct64_cbcdec_keys c_ct64; + br_aes_x86ni_cbcdec_keys c_x86ni; + br_aes_pwr8_cbcdec_keys c_pwr8; +} br_aes_gen_cbcdec_keys; + +/** + * \brief Aggregate structure large enough to be used as context for + * subkeys (CTR encryption and decryption) for all AES implementations. + */ +typedef union { + const br_block_ctr_class *vtable; + br_aes_big_ctr_keys c_big; + br_aes_small_ctr_keys c_small; + br_aes_ct_ctr_keys c_ct; + br_aes_ct64_ctr_keys c_ct64; + br_aes_x86ni_ctr_keys c_x86ni; + br_aes_pwr8_ctr_keys c_pwr8; +} br_aes_gen_ctr_keys; + +/** + * \brief Aggregate structure large enough to be used as context for + * subkeys (CTR encryption/decryption + CBC-MAC) for all AES implementations. + */ +typedef union { + const br_block_ctrcbc_class *vtable; + br_aes_big_ctrcbc_keys c_big; + br_aes_small_ctrcbc_keys c_small; + br_aes_ct_ctrcbc_keys c_ct; + br_aes_ct64_ctrcbc_keys c_ct64; + br_aes_x86ni_ctrcbc_keys c_x86ni; + br_aes_pwr8_ctrcbc_keys c_pwr8; +} br_aes_gen_ctrcbc_keys; + +/* + * Traditional, table-based implementation for DES/3DES. Since tables are + * used, cache-timing attacks are conceptually possible. + */ + +/** \brief DES/3DES block size (8 bytes). */ +#define br_des_tab_BLOCK_SIZE 8 + +/** + * \brief Context for DES subkeys (`des_tab` implementation, CBC encryption). + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** \brief Pointer to vtable for this context. */ + const br_block_cbcenc_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + uint32_t skey[96]; + unsigned num_rounds; +#endif +} br_des_tab_cbcenc_keys; + +/** + * \brief Context for DES subkeys (`des_tab` implementation, CBC decryption). + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** \brief Pointer to vtable for this context. */ + const br_block_cbcdec_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + uint32_t skey[96]; + unsigned num_rounds; +#endif +} br_des_tab_cbcdec_keys; + +/** + * \brief Class instance for DES CBC encryption (`des_tab` implementation). + */ +extern const br_block_cbcenc_class br_des_tab_cbcenc_vtable; + +/** + * \brief Class instance for DES CBC decryption (`des_tab` implementation). + */ +extern const br_block_cbcdec_class br_des_tab_cbcdec_vtable; + +/** + * \brief Context initialisation (key schedule) for DES CBC encryption + * (`des_tab` implementation). + * + * \param ctx context to initialise. + * \param key secret key. + * \param len secret key length (in bytes). + */ +void br_des_tab_cbcenc_init(br_des_tab_cbcenc_keys *ctx, + const void *key, size_t len); + +/** + * \brief Context initialisation (key schedule) for DES CBC decryption + * (`des_tab` implementation). + * + * \param ctx context to initialise. + * \param key secret key. + * \param len secret key length (in bytes). + */ +void br_des_tab_cbcdec_init(br_des_tab_cbcdec_keys *ctx, + const void *key, size_t len); + +/** + * \brief CBC encryption with DES (`des_tab` implementation). + * + * \param ctx context (already initialised). + * \param iv IV (updated). + * \param data data to encrypt (updated). + * \param len data length (in bytes, MUST be multiple of 8). + */ +void br_des_tab_cbcenc_run(const br_des_tab_cbcenc_keys *ctx, void *iv, + void *data, size_t len); + +/** + * \brief CBC decryption with DES (`des_tab` implementation). + * + * \param ctx context (already initialised). + * \param iv IV (updated). + * \param data data to decrypt (updated). + * \param len data length (in bytes, MUST be multiple of 8). + */ +void br_des_tab_cbcdec_run(const br_des_tab_cbcdec_keys *ctx, void *iv, + void *data, size_t len); + +/* + * Constant-time implementation for DES/3DES. It is substantially slower + * (by a factor of about 4x), but also immune to cache-timing attacks. + */ + +/** \brief DES/3DES block size (8 bytes). */ +#define br_des_ct_BLOCK_SIZE 8 + +/** + * \brief Context for DES subkeys (`des_ct` implementation, CBC encryption). + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** \brief Pointer to vtable for this context. */ + const br_block_cbcenc_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + uint32_t skey[96]; + unsigned num_rounds; +#endif +} br_des_ct_cbcenc_keys; + +/** + * \brief Context for DES subkeys (`des_ct` implementation, CBC decryption). + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** \brief Pointer to vtable for this context. */ + const br_block_cbcdec_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + uint32_t skey[96]; + unsigned num_rounds; +#endif +} br_des_ct_cbcdec_keys; + +/** + * \brief Class instance for DES CBC encryption (`des_ct` implementation). + */ +extern const br_block_cbcenc_class br_des_ct_cbcenc_vtable; + +/** + * \brief Class instance for DES CBC decryption (`des_ct` implementation). + */ +extern const br_block_cbcdec_class br_des_ct_cbcdec_vtable; + +/** + * \brief Context initialisation (key schedule) for DES CBC encryption + * (`des_ct` implementation). + * + * \param ctx context to initialise. + * \param key secret key. + * \param len secret key length (in bytes). + */ +void br_des_ct_cbcenc_init(br_des_ct_cbcenc_keys *ctx, + const void *key, size_t len); + +/** + * \brief Context initialisation (key schedule) for DES CBC decryption + * (`des_ct` implementation). + * + * \param ctx context to initialise. + * \param key secret key. + * \param len secret key length (in bytes). + */ +void br_des_ct_cbcdec_init(br_des_ct_cbcdec_keys *ctx, + const void *key, size_t len); + +/** + * \brief CBC encryption with DES (`des_ct` implementation). + * + * \param ctx context (already initialised). + * \param iv IV (updated). + * \param data data to encrypt (updated). + * \param len data length (in bytes, MUST be multiple of 8). + */ +void br_des_ct_cbcenc_run(const br_des_ct_cbcenc_keys *ctx, void *iv, + void *data, size_t len); + +/** + * \brief CBC decryption with DES (`des_ct` implementation). + * + * \param ctx context (already initialised). + * \param iv IV (updated). + * \param data data to decrypt (updated). + * \param len data length (in bytes, MUST be multiple of 8). + */ +void br_des_ct_cbcdec_run(const br_des_ct_cbcdec_keys *ctx, void *iv, + void *data, size_t len); + +/* + * These structures are large enough to accommodate subkeys for all + * DES/3DES implementations. + */ + +/** + * \brief Aggregate structure large enough to be used as context for + * subkeys (CBC encryption) for all DES implementations. + */ +typedef union { + const br_block_cbcenc_class *vtable; + br_des_tab_cbcenc_keys tab; + br_des_ct_cbcenc_keys ct; +} br_des_gen_cbcenc_keys; + +/** + * \brief Aggregate structure large enough to be used as context for + * subkeys (CBC decryption) for all DES implementations. + */ +typedef union { + const br_block_cbcdec_class *vtable; + br_des_tab_cbcdec_keys c_tab; + br_des_ct_cbcdec_keys c_ct; +} br_des_gen_cbcdec_keys; + +/** + * \brief Type for a ChaCha20 implementation. + * + * An implementation follows the description in RFC 7539: + * + * - Key is 256 bits (`key` points to exactly 32 bytes). + * + * - IV is 96 bits (`iv` points to exactly 12 bytes). + * + * - Block counter is over 32 bits and starts at value `cc`; the + * resulting value is returned. + * + * Data (pointed to by `data`, of length `len`) is encrypted/decrypted + * in place. If `len` is not a multiple of 64, then the excess bytes from + * the last block processing are dropped (therefore, "chunked" processing + * works only as long as each non-final chunk has a length multiple of 64). + * + * \param key secret key (32 bytes). + * \param iv IV (12 bytes). + * \param cc initial counter value. + * \param data data to encrypt or decrypt. + * \param len data length (in bytes). + */ +typedef uint32_t (*br_chacha20_run)(const void *key, + const void *iv, uint32_t cc, void *data, size_t len); + +/** + * \brief ChaCha20 implementation (straightforward C code, constant-time). + * + * \see br_chacha20_run + * + * \param key secret key (32 bytes). + * \param iv IV (12 bytes). + * \param cc initial counter value. + * \param data data to encrypt or decrypt. + * \param len data length (in bytes). + */ +uint32_t br_chacha20_ct_run(const void *key, + const void *iv, uint32_t cc, void *data, size_t len); + +/** + * \brief ChaCha20 implementation (SSE2 code, constant-time). + * + * This implementation is available only on x86 platforms, depending on + * compiler support. Moreover, in 32-bit mode, it might not actually run, + * if the underlying hardware does not implement the SSE2 opcode (in + * 64-bit mode, SSE2 is part of the ABI, so if the code could be compiled + * at all, then it can run). Use `br_chacha20_sse2_get()` to safely obtain + * a pointer to that function. + * + * \see br_chacha20_run + * + * \param key secret key (32 bytes). + * \param iv IV (12 bytes). + * \param cc initial counter value. + * \param data data to encrypt or decrypt. + * \param len data length (in bytes). + */ +uint32_t br_chacha20_sse2_run(const void *key, + const void *iv, uint32_t cc, void *data, size_t len); + +/** + * \brief Obtain the `sse2` ChaCha20 implementation, if available. + * + * This function returns a pointer to `br_chacha20_sse2_run`, if + * that implementation was compiled in the library _and_ the SSE2 + * opcodes are available on the currently running CPU. If either of + * these conditions is not met, then this function returns `0`. + * + * \return the `sse2` ChaCha20 implementation, or `0`. + */ +br_chacha20_run br_chacha20_sse2_get(void); + +/** + * \brief Type for a ChaCha20+Poly1305 AEAD implementation. + * + * The provided data is encrypted or decrypted with ChaCha20. The + * authentication tag is computed on the concatenation of the + * additional data and the ciphertext, with the padding and lengths + * as described in RFC 7539 (section 2.8). + * + * After decryption, the caller is responsible for checking that the + * computed tag matches the expected value. + * + * \param key secret key (32 bytes). + * \param iv nonce (12 bytes). + * \param data data to encrypt or decrypt. + * \param len data length (in bytes). + * \param aad additional authenticated data. + * \param aad_len length of additional authenticated data (in bytes). + * \param tag output buffer for the authentication tag. + * \param ichacha implementation of ChaCha20. + * \param encrypt non-zero for encryption, zero for decryption. + */ +typedef void (*br_poly1305_run)(const void *key, const void *iv, + void *data, size_t len, const void *aad, size_t aad_len, + void *tag, br_chacha20_run ichacha, int encrypt); + +/** + * \brief ChaCha20+Poly1305 AEAD implementation (mixed 32-bit multiplications). + * + * \see br_poly1305_run + * + * \param key secret key (32 bytes). + * \param iv nonce (12 bytes). + * \param data data to encrypt or decrypt. + * \param len data length (in bytes). + * \param aad additional authenticated data. + * \param aad_len length of additional authenticated data (in bytes). + * \param tag output buffer for the authentication tag. + * \param ichacha implementation of ChaCha20. + * \param encrypt non-zero for encryption, zero for decryption. + */ +void br_poly1305_ctmul_run(const void *key, const void *iv, + void *data, size_t len, const void *aad, size_t aad_len, + void *tag, br_chacha20_run ichacha, int encrypt); + +/** + * \brief ChaCha20+Poly1305 AEAD implementation (pure 32-bit multiplications). + * + * \see br_poly1305_run + * + * \param key secret key (32 bytes). + * \param iv nonce (12 bytes). + * \param data data to encrypt or decrypt. + * \param len data length (in bytes). + * \param aad additional authenticated data. + * \param aad_len length of additional authenticated data (in bytes). + * \param tag output buffer for the authentication tag. + * \param ichacha implementation of ChaCha20. + * \param encrypt non-zero for encryption, zero for decryption. + */ +void br_poly1305_ctmul32_run(const void *key, const void *iv, + void *data, size_t len, const void *aad, size_t aad_len, + void *tag, br_chacha20_run ichacha, int encrypt); + +/** + * \brief ChaCha20+Poly1305 AEAD implementation (i15). + * + * This implementation relies on the generic big integer code "i15" + * (which uses pure 32-bit multiplications). As such, it may save a + * little code footprint in a context where "i15" is already included + * (e.g. for elliptic curves or for RSA); however, it is also + * substantially slower than the ctmul and ctmul32 implementations. + * + * \see br_poly1305_run + * + * \param key secret key (32 bytes). + * \param iv nonce (12 bytes). + * \param data data to encrypt or decrypt. + * \param len data length (in bytes). + * \param aad additional authenticated data. + * \param aad_len length of additional authenticated data (in bytes). + * \param tag output buffer for the authentication tag. + * \param ichacha implementation of ChaCha20. + * \param encrypt non-zero for encryption, zero for decryption. + */ +void br_poly1305_i15_run(const void *key, const void *iv, + void *data, size_t len, const void *aad, size_t aad_len, + void *tag, br_chacha20_run ichacha, int encrypt); + +/** + * \brief ChaCha20+Poly1305 AEAD implementation (ctmulq). + * + * This implementation uses 64-bit multiplications (result over 128 bits). + * It is available only on platforms that offer such a primitive (in + * practice, 64-bit architectures). Use `br_poly1305_ctmulq_get()` to + * dynamically obtain a pointer to that function, or 0 if not supported. + * + * \see br_poly1305_run + * + * \param key secret key (32 bytes). + * \param iv nonce (12 bytes). + * \param data data to encrypt or decrypt. + * \param len data length (in bytes). + * \param aad additional authenticated data. + * \param aad_len length of additional authenticated data (in bytes). + * \param tag output buffer for the authentication tag. + * \param ichacha implementation of ChaCha20. + * \param encrypt non-zero for encryption, zero for decryption. + */ +void br_poly1305_ctmulq_run(const void *key, const void *iv, + void *data, size_t len, const void *aad, size_t aad_len, + void *tag, br_chacha20_run ichacha, int encrypt); + +/** + * \brief Get the ChaCha20+Poly1305 "ctmulq" implementation, if available. + * + * This function returns a pointer to the `br_poly1305_ctmulq_run()` + * function if supported on the current platform; otherwise, it returns 0. + * + * \return the ctmulq ChaCha20+Poly1305 implementation, or 0. + */ +br_poly1305_run br_poly1305_ctmulq_get(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/bearssl-esp8266/src/t_bearssl_ec.h b/lib/bearssl-esp8266/src/t_bearssl_ec.h new file mode 100644 index 000000000..660bd5186 --- /dev/null +++ b/lib/bearssl-esp8266/src/t_bearssl_ec.h @@ -0,0 +1,967 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef BR_BEARSSL_EC_H__ +#define BR_BEARSSL_EC_H__ + +#include +#include + +#include "t_bearssl_rand.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** \file bearssl_ec.h + * + * # Elliptic Curves + * + * This file documents the EC implementations provided with BearSSL, and + * ECDSA. + * + * ## Elliptic Curve API + * + * Only "named curves" are supported. Each EC implementation supports + * one or several named curves, identified by symbolic identifiers. + * These identifiers are small integers, that correspond to the values + * registered by the + * [IANA](http://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8). + * + * Since all currently defined elliptic curve identifiers are in the 0..31 + * range, it is convenient to encode support of some curves in a 32-bit + * word, such that bit x corresponds to curve of identifier x. + * + * An EC implementation is incarnated by a `br_ec_impl` instance, that + * offers the following fields: + * + * - `supported_curves` + * + * A 32-bit word that documents the identifiers of the curves supported + * by this implementation. + * + * - `generator()` + * + * Callback method that returns a pointer to the conventional generator + * point for that curve. + * + * - `order()` + * + * Callback method that returns a pointer to the subgroup order for + * that curve. That value uses unsigned big-endian encoding. + * + * - `xoff()` + * + * Callback method that returns the offset and length of the X + * coordinate in an encoded point. + * + * - `mul()` + * + * Multiply a curve point with an integer. + * + * - `mulgen()` + * + * Multiply the curve generator with an integer. This may be faster + * than the generic `mul()`. + * + * - `muladd()` + * + * Multiply two curve points by two integers, and return the sum of + * the two products. + * + * All curve points are represented in uncompressed format. The `mul()` + * and `muladd()` methods take care to validate that the provided points + * are really part of the relevant curve subgroup. + * + * For all point multiplication functions, the following holds: + * + * - Functions validate that the provided points are valid members + * of the relevant curve subgroup. An error is reported if that is + * not the case. + * + * - Processing is constant-time, even if the point operands are not + * valid. This holds for both the source and resulting points, and + * the multipliers (integers). Only the byte length of the provided + * multiplier arrays (not their actual value length in bits) may + * leak through timing-based side channels. + * + * - The multipliers (integers) MUST be lower than the subgroup order. + * If this property is not met, then the result is indeterminate, + * but an error value is not ncessearily returned. + * + * + * ## ECDSA + * + * ECDSA signatures have two standard formats, called "raw" and "asn1". + * Internally, such a signature is a pair of modular integers `(r,s)`. + * The "raw" format is the concatenation of the unsigned big-endian + * encodings of these two integers, possibly left-padded with zeros so + * that they have the same encoded length. The "asn1" format is the + * DER encoding of an ASN.1 structure that contains the two integer + * values: + * + * ECDSASignature ::= SEQUENCE { + * r INTEGER, + * s INTEGER + * } + * + * In general, in all of X.509 and SSL/TLS, the "asn1" format is used. + * BearSSL offers ECDSA implementations for both formats; conversion + * functions between the two formats are also provided. Conversion of a + * "raw" format signature into "asn1" may enlarge a signature by no more + * than 9 bytes for all supported curves; conversely, conversion of an + * "asn1" signature to "raw" may expand the signature but the "raw" + * length will never be more than twice the length of the "asn1" length + * (and usually it will be shorter). + * + * Note that for a given signature, the "raw" format is not fully + * deterministic, in that it does not enforce a minimal common length. + */ + +/* + * Standard curve ID. These ID are equal to the assigned numerical + * identifiers assigned to these curves for TLS: + * http://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8 + */ + +/** \brief Identifier for named curve sect163k1. */ +#define BR_EC_sect163k1 1 + +/** \brief Identifier for named curve sect163r1. */ +#define BR_EC_sect163r1 2 + +/** \brief Identifier for named curve sect163r2. */ +#define BR_EC_sect163r2 3 + +/** \brief Identifier for named curve sect193r1. */ +#define BR_EC_sect193r1 4 + +/** \brief Identifier for named curve sect193r2. */ +#define BR_EC_sect193r2 5 + +/** \brief Identifier for named curve sect233k1. */ +#define BR_EC_sect233k1 6 + +/** \brief Identifier for named curve sect233r1. */ +#define BR_EC_sect233r1 7 + +/** \brief Identifier for named curve sect239k1. */ +#define BR_EC_sect239k1 8 + +/** \brief Identifier for named curve sect283k1. */ +#define BR_EC_sect283k1 9 + +/** \brief Identifier for named curve sect283r1. */ +#define BR_EC_sect283r1 10 + +/** \brief Identifier for named curve sect409k1. */ +#define BR_EC_sect409k1 11 + +/** \brief Identifier for named curve sect409r1. */ +#define BR_EC_sect409r1 12 + +/** \brief Identifier for named curve sect571k1. */ +#define BR_EC_sect571k1 13 + +/** \brief Identifier for named curve sect571r1. */ +#define BR_EC_sect571r1 14 + +/** \brief Identifier for named curve secp160k1. */ +#define BR_EC_secp160k1 15 + +/** \brief Identifier for named curve secp160r1. */ +#define BR_EC_secp160r1 16 + +/** \brief Identifier for named curve secp160r2. */ +#define BR_EC_secp160r2 17 + +/** \brief Identifier for named curve secp192k1. */ +#define BR_EC_secp192k1 18 + +/** \brief Identifier for named curve secp192r1. */ +#define BR_EC_secp192r1 19 + +/** \brief Identifier for named curve secp224k1. */ +#define BR_EC_secp224k1 20 + +/** \brief Identifier for named curve secp224r1. */ +#define BR_EC_secp224r1 21 + +/** \brief Identifier for named curve secp256k1. */ +#define BR_EC_secp256k1 22 + +/** \brief Identifier for named curve secp256r1. */ +#define BR_EC_secp256r1 23 + +/** \brief Identifier for named curve secp384r1. */ +#define BR_EC_secp384r1 24 + +/** \brief Identifier for named curve secp521r1. */ +#define BR_EC_secp521r1 25 + +/** \brief Identifier for named curve brainpoolP256r1. */ +#define BR_EC_brainpoolP256r1 26 + +/** \brief Identifier for named curve brainpoolP384r1. */ +#define BR_EC_brainpoolP384r1 27 + +/** \brief Identifier for named curve brainpoolP512r1. */ +#define BR_EC_brainpoolP512r1 28 + +/** \brief Identifier for named curve Curve25519. */ +#define BR_EC_curve25519 29 + +/** \brief Identifier for named curve Curve448. */ +#define BR_EC_curve448 30 + +/** + * \brief Structure for an EC public key. + */ +typedef struct { + /** \brief Identifier for the curve used by this key. */ + int curve; + /** \brief Public curve point (uncompressed format). */ + unsigned char *q; + /** \brief Length of public curve point (in bytes). */ + size_t qlen; +} br_ec_public_key; + +/** + * \brief Structure for an EC private key. + * + * The private key is an integer modulo the curve subgroup order. The + * encoding below tolerates extra leading zeros. In general, it is + * recommended that the private key has the same length as the curve + * subgroup order. + */ +typedef struct { + /** \brief Identifier for the curve used by this key. */ + int curve; + /** \brief Private key (integer, unsigned big-endian encoding). */ + unsigned char *x; + /** \brief Private key length (in bytes). */ + size_t xlen; +} br_ec_private_key; + +/** + * \brief Type for an EC implementation. + */ +typedef struct { + /** + * \brief Supported curves. + * + * This word is a bitfield: bit `x` is set if the curve of ID `x` + * is supported. E.g. an implementation supporting both NIST P-256 + * (secp256r1, ID 23) and NIST P-384 (secp384r1, ID 24) will have + * value `0x01800000` in this field. + */ + uint32_t supported_curves; + + /** + * \brief Get the conventional generator. + * + * This function returns the conventional generator (encoded + * curve point) for the specified curve. This function MUST NOT + * be called if the curve is not supported. + * + * \param curve curve identifier. + * \param len receiver for the encoded generator length (in bytes). + * \return the encoded generator. + */ + const unsigned char *(*generator)(int curve, size_t *len); + + /** + * \brief Get the subgroup order. + * + * This function returns the order of the subgroup generated by + * the conventional generator, for the specified curve. Unsigned + * big-endian encoding is used. This function MUST NOT be called + * if the curve is not supported. + * + * \param curve curve identifier. + * \param len receiver for the encoded order length (in bytes). + * \return the encoded order. + */ + const unsigned char *(*order)(int curve, size_t *len); + + /** + * \brief Get the offset and length for the X coordinate. + * + * This function returns the offset and length (in bytes) of + * the X coordinate in an encoded non-zero point. + * + * \param curve curve identifier. + * \param len receiver for the X coordinate length (in bytes). + * \return the offset for the X coordinate (in bytes). + */ + size_t (*xoff)(int curve, size_t *len); + + /** + * \brief Multiply a curve point by an integer. + * + * The source point is provided in array `G` (of size `Glen` bytes); + * the multiplication result is written over it. The multiplier + * `x` (of size `xlen` bytes) uses unsigned big-endian encoding. + * + * Rules: + * + * - The specified curve MUST be supported. + * + * - The source point must be a valid point on the relevant curve + * subgroup (and not the "point at infinity" either). If this is + * not the case, then this function returns an error (0). + * + * - The multiplier integer MUST be non-zero and less than the + * curve subgroup order. If this property does not hold, then + * the result is indeterminate and an error code is not + * guaranteed. + * + * Returned value is 1 on success, 0 on error. On error, the + * contents of `G` are indeterminate. + * + * \param G point to multiply. + * \param Glen length of the encoded point (in bytes). + * \param x multiplier (unsigned big-endian). + * \param xlen multiplier length (in bytes). + * \param curve curve identifier. + * \return 1 on success, 0 on error. + */ + uint32_t (*mul)(unsigned char *G, size_t Glen, + const unsigned char *x, size_t xlen, int curve); + + /** + * \brief Multiply the generator by an integer. + * + * The multiplier MUST be non-zero and less than the curve + * subgroup order. Results are indeterminate if this property + * does not hold. + * + * \param R output buffer for the point. + * \param x multiplier (unsigned big-endian). + * \param xlen multiplier length (in bytes). + * \param curve curve identifier. + * \return encoded result point length (in bytes). + */ + size_t (*mulgen)(unsigned char *R, + const unsigned char *x, size_t xlen, int curve); + + /** + * \brief Multiply two points by two integers and add the + * results. + * + * The point `x*A + y*B` is computed and written back in the `A` + * array. + * + * Rules: + * + * - The specified curve MUST be supported. + * + * - The source points (`A` and `B`) must be valid points on + * the relevant curve subgroup (and not the "point at + * infinity" either). If this is not the case, then this + * function returns an error (0). + * + * - If the `B` pointer is `NULL`, then the conventional + * subgroup generator is used. With some implementations, + * this may be faster than providing a pointer to the + * generator. + * + * - The multiplier integers (`x` and `y`) MUST be non-zero + * and less than the curve subgroup order. If either integer + * is zero, then an error is reported, but if one of them is + * not lower than the subgroup order, then the result is + * indeterminate and an error code is not guaranteed. + * + * - If the final result is the point at infinity, then an + * error is returned. + * + * Returned value is 1 on success, 0 on error. On error, the + * contents of `A` are indeterminate. + * + * \param A first point to multiply. + * \param B second point to multiply (`NULL` for the generator). + * \param len common length of the encoded points (in bytes). + * \param x multiplier for `A` (unsigned big-endian). + * \param xlen length of multiplier for `A` (in bytes). + * \param y multiplier for `A` (unsigned big-endian). + * \param ylen length of multiplier for `A` (in bytes). + * \param curve curve identifier. + * \return 1 on success, 0 on error. + */ + uint32_t (*muladd)(unsigned char *A, const unsigned char *B, size_t len, + const unsigned char *x, size_t xlen, + const unsigned char *y, size_t ylen, int curve); +} br_ec_impl; + +/** + * \brief EC implementation "i31". + * + * This implementation internally uses generic code for modular integers, + * with a representation as sequences of 31-bit words. It supports secp256r1, + * secp384r1 and secp521r1 (aka NIST curves P-256, P-384 and P-521). + */ +extern const br_ec_impl br_ec_prime_i31; + +/** + * \brief EC implementation "i15". + * + * This implementation internally uses generic code for modular integers, + * with a representation as sequences of 15-bit words. It supports secp256r1, + * secp384r1 and secp521r1 (aka NIST curves P-256, P-384 and P-521). + */ +extern const br_ec_impl br_ec_prime_i15; + +/** + * \brief EC implementation "m15" for P-256. + * + * This implementation uses specialised code for curve secp256r1 (also + * known as NIST P-256), with optional Karatsuba decomposition, and fast + * modular reduction thanks to the field modulus special format. Only + * 32-bit multiplications are used (with 32-bit results, not 64-bit). + */ +extern const br_ec_impl br_ec_p256_m15; + +/** + * \brief EC implementation "m31" for P-256. + * + * This implementation uses specialised code for curve secp256r1 (also + * known as NIST P-256), relying on multiplications of 31-bit values + * (MUL31). + */ +extern const br_ec_impl br_ec_p256_m31; + +/** + * \brief EC implementation "m62" (specialised code) for P-256. + * + * This implementation uses custom code relying on multiplication of + * integers up to 64 bits, with a 128-bit result. This implementation is + * defined only on platforms that offer the 64x64->128 multiplication + * support; use `br_ec_p256_m62_get()` to dynamically obtain a pointer + * to that implementation. + */ +extern const br_ec_impl br_ec_p256_m62; + +/** + * \brief Get the "m62" implementation of P-256, if available. + * + * \return the implementation, or 0. + */ +const br_ec_impl *br_ec_p256_m62_get(void); + +/** + * \brief EC implementation "m64" (specialised code) for P-256. + * + * This implementation uses custom code relying on multiplication of + * integers up to 64 bits, with a 128-bit result. This implementation is + * defined only on platforms that offer the 64x64->128 multiplication + * support; use `br_ec_p256_m64_get()` to dynamically obtain a pointer + * to that implementation. + */ +extern const br_ec_impl br_ec_p256_m64; + +/** + * \brief Get the "m64" implementation of P-256, if available. + * + * \return the implementation, or 0. + */ +const br_ec_impl *br_ec_p256_m64_get(void); + +/** + * \brief EC implementation "i15" (generic code) for Curve25519. + * + * This implementation uses the generic code for modular integers (with + * 15-bit words) to support Curve25519. Due to the specificities of the + * curve definition, the following applies: + * + * - `muladd()` is not implemented (the function returns 0 systematically). + * - `order()` returns 2^255-1, since the point multiplication algorithm + * accepts any 32-bit integer as input (it clears the top bit and low + * three bits systematically). + */ +extern const br_ec_impl br_ec_c25519_i15; + +/** + * \brief EC implementation "i31" (generic code) for Curve25519. + * + * This implementation uses the generic code for modular integers (with + * 31-bit words) to support Curve25519. Due to the specificities of the + * curve definition, the following applies: + * + * - `muladd()` is not implemented (the function returns 0 systematically). + * - `order()` returns 2^255-1, since the point multiplication algorithm + * accepts any 32-bit integer as input (it clears the top bit and low + * three bits systematically). + */ +extern const br_ec_impl br_ec_c25519_i31; + +/** + * \brief EC implementation "m15" (specialised code) for Curve25519. + * + * This implementation uses custom code relying on multiplication of + * integers up to 15 bits. Due to the specificities of the curve + * definition, the following applies: + * + * - `muladd()` is not implemented (the function returns 0 systematically). + * - `order()` returns 2^255-1, since the point multiplication algorithm + * accepts any 32-bit integer as input (it clears the top bit and low + * three bits systematically). + */ +extern const br_ec_impl br_ec_c25519_m15; + +/** + * \brief EC implementation "m31" (specialised code) for Curve25519. + * + * This implementation uses custom code relying on multiplication of + * integers up to 31 bits. Due to the specificities of the curve + * definition, the following applies: + * + * - `muladd()` is not implemented (the function returns 0 systematically). + * - `order()` returns 2^255-1, since the point multiplication algorithm + * accepts any 32-bit integer as input (it clears the top bit and low + * three bits systematically). + */ +extern const br_ec_impl br_ec_c25519_m31; + +/** + * \brief EC implementation "m62" (specialised code) for Curve25519. + * + * This implementation uses custom code relying on multiplication of + * integers up to 62 bits, with a 124-bit result. This implementation is + * defined only on platforms that offer the 64x64->128 multiplication + * support; use `br_ec_c25519_m62_get()` to dynamically obtain a pointer + * to that implementation. Due to the specificities of the curve + * definition, the following applies: + * + * - `muladd()` is not implemented (the function returns 0 systematically). + * - `order()` returns 2^255-1, since the point multiplication algorithm + * accepts any 32-bit integer as input (it clears the top bit and low + * three bits systematically). + */ +extern const br_ec_impl br_ec_c25519_m62; + +/** + * \brief Get the "m62" implementation of Curve25519, if available. + * + * \return the implementation, or 0. + */ +const br_ec_impl *br_ec_c25519_m62_get(void); + +/** + * \brief EC implementation "m64" (specialised code) for Curve25519. + * + * This implementation uses custom code relying on multiplication of + * integers up to 64 bits, with a 128-bit result. This implementation is + * defined only on platforms that offer the 64x64->128 multiplication + * support; use `br_ec_c25519_m64_get()` to dynamically obtain a pointer + * to that implementation. Due to the specificities of the curve + * definition, the following applies: + * + * - `muladd()` is not implemented (the function returns 0 systematically). + * - `order()` returns 2^255-1, since the point multiplication algorithm + * accepts any 32-bit integer as input (it clears the top bit and low + * three bits systematically). + */ +extern const br_ec_impl br_ec_c25519_m64; + +/** + * \brief Get the "m64" implementation of Curve25519, if available. + * + * \return the implementation, or 0. + */ +const br_ec_impl *br_ec_c25519_m64_get(void); + +/** + * \brief Aggregate EC implementation "m15". + * + * This implementation is a wrapper for: + * + * - `br_ec_c25519_m15` for Curve25519 + * - `br_ec_p256_m15` for NIST P-256 + * - `br_ec_prime_i15` for other curves (NIST P-384 and NIST-P512) + */ +extern const br_ec_impl br_ec_all_m15; + +/** + * \brief Aggregate EC implementation "m31". + * + * This implementation is a wrapper for: + * + * - `br_ec_c25519_m31` for Curve25519 + * - `br_ec_p256_m31` for NIST P-256 + * - `br_ec_prime_i31` for other curves (NIST P-384 and NIST-P512) + */ +extern const br_ec_impl br_ec_all_m31; + +/** + * \brief Get the "default" EC implementation for the current system. + * + * This returns a pointer to the preferred implementation on the + * current system. + * + * \return the default EC implementation. + */ +const br_ec_impl *br_ec_get_default(void); + +/** + * \brief Convert a signature from "raw" to "asn1". + * + * Conversion is done "in place" and the new length is returned. + * Conversion may enlarge the signature, but by no more than 9 bytes at + * most. On error, 0 is returned (error conditions include an odd raw + * signature length, or an oversized integer). + * + * \param sig signature to convert. + * \param sig_len signature length (in bytes). + * \return the new signature length, or 0 on error. + */ +size_t br_ecdsa_raw_to_asn1(void *sig, size_t sig_len); + +/** + * \brief Convert a signature from "asn1" to "raw". + * + * Conversion is done "in place" and the new length is returned. + * Conversion may enlarge the signature, but the new signature length + * will be less than twice the source length at most. On error, 0 is + * returned (error conditions include an invalid ASN.1 structure or an + * oversized integer). + * + * \param sig signature to convert. + * \param sig_len signature length (in bytes). + * \return the new signature length, or 0 on error. + */ +size_t br_ecdsa_asn1_to_raw(void *sig, size_t sig_len); + +/** + * \brief Type for an ECDSA signer function. + * + * A pointer to the EC implementation is provided. The hash value is + * assumed to have the length inferred from the designated hash function + * class. + * + * Signature is written in the buffer pointed to by `sig`, and the length + * (in bytes) is returned. On error, nothing is written in the buffer, + * and 0 is returned. This function returns 0 if the specified curve is + * not supported by the provided EC implementation. + * + * The signature format is either "raw" or "asn1", depending on the + * implementation; maximum length is predictable from the implemented + * curve: + * + * | curve | raw | asn1 | + * | :--------- | --: | ---: | + * | NIST P-256 | 64 | 72 | + * | NIST P-384 | 96 | 104 | + * | NIST P-521 | 132 | 139 | + * + * \param impl EC implementation to use. + * \param hf hash function used to process the data. + * \param hash_value signed data (hashed). + * \param sk EC private key. + * \param sig destination buffer. + * \return the signature length (in bytes), or 0 on error. + */ +typedef size_t (*br_ecdsa_sign)(const br_ec_impl *impl, + const br_hash_class *hf, const void *hash_value, + const br_ec_private_key *sk, void *sig); + +/** + * \brief Type for an ECDSA signature verification function. + * + * A pointer to the EC implementation is provided. The hashed value, + * computed over the purportedly signed data, is also provided with + * its length. + * + * The signature format is either "raw" or "asn1", depending on the + * implementation. + * + * Returned value is 1 on success (valid signature), 0 on error. This + * function returns 0 if the specified curve is not supported by the + * provided EC implementation. + * + * \param impl EC implementation to use. + * \param hash signed data (hashed). + * \param hash_len hash value length (in bytes). + * \param pk EC public key. + * \param sig signature. + * \param sig_len signature length (in bytes). + * \return 1 on success, 0 on error. + */ +typedef uint32_t (*br_ecdsa_vrfy)(const br_ec_impl *impl, + const void *hash, size_t hash_len, + const br_ec_public_key *pk, const void *sig, size_t sig_len); + +/** + * \brief ECDSA signature generator, "i31" implementation, "asn1" format. + * + * \see br_ecdsa_sign() + * + * \param impl EC implementation to use. + * \param hf hash function used to process the data. + * \param hash_value signed data (hashed). + * \param sk EC private key. + * \param sig destination buffer. + * \return the signature length (in bytes), or 0 on error. + */ +size_t br_ecdsa_i31_sign_asn1(const br_ec_impl *impl, + const br_hash_class *hf, const void *hash_value, + const br_ec_private_key *sk, void *sig); + +/** + * \brief ECDSA signature generator, "i31" implementation, "raw" format. + * + * \see br_ecdsa_sign() + * + * \param impl EC implementation to use. + * \param hf hash function used to process the data. + * \param hash_value signed data (hashed). + * \param sk EC private key. + * \param sig destination buffer. + * \return the signature length (in bytes), or 0 on error. + */ +size_t br_ecdsa_i31_sign_raw(const br_ec_impl *impl, + const br_hash_class *hf, const void *hash_value, + const br_ec_private_key *sk, void *sig); + +/** + * \brief ECDSA signature verifier, "i31" implementation, "asn1" format. + * + * \see br_ecdsa_vrfy() + * + * \param impl EC implementation to use. + * \param hash signed data (hashed). + * \param hash_len hash value length (in bytes). + * \param pk EC public key. + * \param sig signature. + * \param sig_len signature length (in bytes). + * \return 1 on success, 0 on error. + */ +uint32_t br_ecdsa_i31_vrfy_asn1(const br_ec_impl *impl, + const void *hash, size_t hash_len, + const br_ec_public_key *pk, const void *sig, size_t sig_len); + +/** + * \brief ECDSA signature verifier, "i31" implementation, "raw" format. + * + * \see br_ecdsa_vrfy() + * + * \param impl EC implementation to use. + * \param hash signed data (hashed). + * \param hash_len hash value length (in bytes). + * \param pk EC public key. + * \param sig signature. + * \param sig_len signature length (in bytes). + * \return 1 on success, 0 on error. + */ +uint32_t br_ecdsa_i31_vrfy_raw(const br_ec_impl *impl, + const void *hash, size_t hash_len, + const br_ec_public_key *pk, const void *sig, size_t sig_len); + +/** + * \brief ECDSA signature generator, "i15" implementation, "asn1" format. + * + * \see br_ecdsa_sign() + * + * \param impl EC implementation to use. + * \param hf hash function used to process the data. + * \param hash_value signed data (hashed). + * \param sk EC private key. + * \param sig destination buffer. + * \return the signature length (in bytes), or 0 on error. + */ +size_t br_ecdsa_i15_sign_asn1(const br_ec_impl *impl, + const br_hash_class *hf, const void *hash_value, + const br_ec_private_key *sk, void *sig); + +/** + * \brief ECDSA signature generator, "i15" implementation, "raw" format. + * + * \see br_ecdsa_sign() + * + * \param impl EC implementation to use. + * \param hf hash function used to process the data. + * \param hash_value signed data (hashed). + * \param sk EC private key. + * \param sig destination buffer. + * \return the signature length (in bytes), or 0 on error. + */ +size_t br_ecdsa_i15_sign_raw(const br_ec_impl *impl, + const br_hash_class *hf, const void *hash_value, + const br_ec_private_key *sk, void *sig); + +/** + * \brief ECDSA signature verifier, "i15" implementation, "asn1" format. + * + * \see br_ecdsa_vrfy() + * + * \param impl EC implementation to use. + * \param hash signed data (hashed). + * \param hash_len hash value length (in bytes). + * \param pk EC public key. + * \param sig signature. + * \param sig_len signature length (in bytes). + * \return 1 on success, 0 on error. + */ +uint32_t br_ecdsa_i15_vrfy_asn1(const br_ec_impl *impl, + const void *hash, size_t hash_len, + const br_ec_public_key *pk, const void *sig, size_t sig_len); + +/** + * \brief ECDSA signature verifier, "i15" implementation, "raw" format. + * + * \see br_ecdsa_vrfy() + * + * \param impl EC implementation to use. + * \param hash signed data (hashed). + * \param hash_len hash value length (in bytes). + * \param pk EC public key. + * \param sig signature. + * \param sig_len signature length (in bytes). + * \return 1 on success, 0 on error. + */ +uint32_t br_ecdsa_i15_vrfy_raw(const br_ec_impl *impl, + const void *hash, size_t hash_len, + const br_ec_public_key *pk, const void *sig, size_t sig_len); + +/** + * \brief Get "default" ECDSA implementation (signer, asn1 format). + * + * This returns the preferred implementation of ECDSA signature generation + * ("asn1" output format) on the current system. + * + * \return the default implementation. + */ +br_ecdsa_sign br_ecdsa_sign_asn1_get_default(void); + +/** + * \brief Get "default" ECDSA implementation (signer, raw format). + * + * This returns the preferred implementation of ECDSA signature generation + * ("raw" output format) on the current system. + * + * \return the default implementation. + */ +br_ecdsa_sign br_ecdsa_sign_raw_get_default(void); + +/** + * \brief Get "default" ECDSA implementation (verifier, asn1 format). + * + * This returns the preferred implementation of ECDSA signature verification + * ("asn1" output format) on the current system. + * + * \return the default implementation. + */ +br_ecdsa_vrfy br_ecdsa_vrfy_asn1_get_default(void); + +/** + * \brief Get "default" ECDSA implementation (verifier, raw format). + * + * This returns the preferred implementation of ECDSA signature verification + * ("raw" output format) on the current system. + * + * \return the default implementation. + */ +br_ecdsa_vrfy br_ecdsa_vrfy_raw_get_default(void); + +/** + * \brief Maximum size for EC private key element buffer. + * + * This is the largest number of bytes that `br_ec_keygen()` may need or + * ever return. + */ +#define BR_EC_KBUF_PRIV_MAX_SIZE 72 + +/** + * \brief Maximum size for EC public key element buffer. + * + * This is the largest number of bytes that `br_ec_compute_public()` may + * need or ever return. + */ +#define BR_EC_KBUF_PUB_MAX_SIZE 145 + +/** + * \brief Generate a new EC private key. + * + * If the specified `curve` is not supported by the elliptic curve + * implementation (`impl`), then this function returns zero. + * + * The `sk` structure fields are set to the new private key data. In + * particular, `sk.x` is made to point to the provided key buffer (`kbuf`), + * in which the actual private key data is written. That buffer is assumed + * to be large enough. The `BR_EC_KBUF_PRIV_MAX_SIZE` defines the maximum + * size for all supported curves. + * + * The number of bytes used in `kbuf` is returned. If `kbuf` is `NULL`, then + * the private key is not actually generated, and `sk` may also be `NULL`; + * the minimum length for `kbuf` is still computed and returned. + * + * If `sk` is `NULL` but `kbuf` is not `NULL`, then the private key is + * still generated and stored in `kbuf`. + * + * \param rng_ctx source PRNG context (already initialized). + * \param impl the elliptic curve implementation. + * \param sk the private key structure to fill, or `NULL`. + * \param kbuf the key element buffer, or `NULL`. + * \param curve the curve identifier. + * \return the key data length (in bytes), or zero. + */ +size_t br_ec_keygen(const br_prng_class **rng_ctx, + const br_ec_impl *impl, br_ec_private_key *sk, + void *kbuf, int curve); + +/** + * \brief Compute EC public key from EC private key. + * + * This function uses the provided elliptic curve implementation (`impl`) + * to compute the public key corresponding to the private key held in `sk`. + * The public key point is written into `kbuf`, which is then linked from + * the `*pk` structure. The size of the public key point, i.e. the number + * of bytes used in `kbuf`, is returned. + * + * If `kbuf` is `NULL`, then the public key point is NOT computed, and + * the public key structure `*pk` is unmodified (`pk` may be `NULL` in + * that case). The size of the public key point is still returned. + * + * If `pk` is `NULL` but `kbuf` is not `NULL`, then the public key + * point is computed and stored in `kbuf`, and its size is returned. + * + * If the curve used by the private key is not supported by the curve + * implementation, then this function returns zero. + * + * The private key MUST be valid. An off-range private key value is not + * necessarily detected, and leads to unpredictable results. + * + * \param impl the elliptic curve implementation. + * \param pk the public key structure to fill (or `NULL`). + * \param kbuf the public key point buffer (or `NULL`). + * \param sk the source private key. + * \return the public key point length (in bytes), or zero. + */ +size_t br_ec_compute_pub(const br_ec_impl *impl, br_ec_public_key *pk, + void *kbuf, const br_ec_private_key *sk); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/bearssl-esp8266/src/t_bearssl_hash.h b/lib/bearssl-esp8266/src/t_bearssl_hash.h new file mode 100644 index 000000000..ca4fa26cc --- /dev/null +++ b/lib/bearssl-esp8266/src/t_bearssl_hash.h @@ -0,0 +1,1346 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef BR_BEARSSL_HASH_H__ +#define BR_BEARSSL_HASH_H__ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** \file bearssl_hash.h + * + * # Hash Functions + * + * This file documents the API for hash functions. + * + * + * ## Procedural API + * + * For each implemented hash function, of name "`xxx`", the following + * elements are defined: + * + * - `br_xxx_vtable` + * + * An externally defined instance of `br_hash_class`. + * + * - `br_xxx_SIZE` + * + * A macro that evaluates to the output size (in bytes) of the + * hash function. + * + * - `br_xxx_ID` + * + * A macro that evaluates to a symbolic identifier for the hash + * function. Such identifiers are used with HMAC and signature + * algorithm implementations. + * + * NOTE: for the "standard" hash functions defined in [the TLS + * standard](https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1), + * the symbolic identifiers match the constants used in TLS, i.e. + * 1 to 6 for MD5, SHA-1, SHA-224, SHA-256, SHA-384 and SHA-512, + * respectively. + * + * - `br_xxx_context` + * + * Context for an ongoing computation. It is allocated by the + * caller, and a pointer to it is passed to all functions. A + * context contains no interior pointer, so it can be moved around + * and cloned (with a simple `memcpy()` or equivalent) in order to + * capture the function state at some point. Computations that use + * distinct context structures are independent of each other. The + * first field of `br_xxx_context` is always a pointer to the + * `br_xxx_vtable` structure; `br_xxx_init()` sets that pointer. + * + * - `br_xxx_init(br_xxx_context *ctx)` + * + * Initialise the provided context. Previous contents of the structure + * are ignored. This calls resets the context to the start of a new + * hash computation; it also sets the first field of the context + * structure (called `vtable`) to a pointer to the statically + * allocated constant `br_xxx_vtable` structure. + * + * - `br_xxx_update(br_xxx_context *ctx, const void *data, size_t len)` + * + * Add some more bytes to the hash computation represented by the + * provided context. + * + * - `br_xxx_out(const br_xxx_context *ctx, void *out)` + * + * Complete the hash computation and write the result in the provided + * buffer. The output buffer MUST be large enough to accommodate the + * result. The context is NOT modified by this operation, so this + * function can be used to get a "partial hash" while still keeping + * the possibility of adding more bytes to the input. + * + * - `br_xxx_state(const br_xxx_context *ctx, void *out)` + * + * Get a copy of the "current state" for the computation so far. For + * MD functions (MD5, SHA-1, SHA-2 family), this is the running state + * resulting from the processing of the last complete input block. + * Returned value is the current input length (in bytes). + * + * - `br_xxx_set_state(br_xxx_context *ctx, const void *stb, uint64_t count)` + * + * Set the internal state to the provided values. The 'stb' and + * 'count' values shall match that which was obtained from + * `br_xxx_state()`. This restores the hash state only if the state + * values were at an appropriate block boundary. This does NOT set + * the `vtable` pointer in the context. + * + * Context structures can be discarded without any explicit deallocation. + * Hash function implementations are purely software and don't reserve + * any resources outside of the context structure itself. + * + * + * ## Object-Oriented API + * + * For each hash function that follows the procedural API described + * above, an object-oriented API is also provided. In that API, function + * pointers from the vtable (`br_xxx_vtable`) are used. The vtable + * incarnates object-oriented programming. An introduction on the OOP + * concept used here can be read on the BearSSL Web site:
+ *    [https://www.bearssl.org/oop.html](https://www.bearssl.org/oop.html) + * + * The vtable offers functions called `init()`, `update()`, `out()`, + * `set()` and `set_state()`, which are in fact the functions from + * the procedural API. That vtable also contains two informative fields: + * + * - `context_size` + * + * The size of the context structure (`br_xxx_context`), in bytes. + * This can be used by generic implementations to perform dynamic + * context allocation. + * + * - `desc` + * + * A "descriptor" field that encodes some information on the hash + * function: symbolic identifier, output size, state size, + * internal block size, details on the padding. + * + * Users of this object-oriented API (in particular generic HMAC + * implementations) may make the following assumptions: + * + * - Hash output size is no more than 64 bytes. + * - Hash internal state size is no more than 64 bytes. + * - Internal block size is a power of two, no less than 16 and no more + * than 256. + * + * + * ## Implemented Hash Functions + * + * Implemented hash functions are: + * + * | Function | Name | Output length | State length | + * | :-------- | :------ | :-----------: | :----------: | + * | MD5 | md5 | 16 | 16 | + * | SHA-1 | sha1 | 20 | 20 | + * | SHA-224 | sha224 | 28 | 32 | + * | SHA-256 | sha256 | 32 | 32 | + * | SHA-384 | sha384 | 48 | 64 | + * | SHA-512 | sha512 | 64 | 64 | + * | MD5+SHA-1 | md5sha1 | 36 | 36 | + * + * (MD5+SHA-1 is the concatenation of MD5 and SHA-1 computed over the + * same input; in the implementation, the internal data buffer is + * shared, thus making it more memory-efficient than separate MD5 and + * SHA-1. It can be useful in implementing SSL 3.0, TLS 1.0 and TLS + * 1.1.) + * + * + * ## Multi-Hasher + * + * An aggregate hasher is provided, that can compute several standard + * hash functions in parallel. It uses `br_multihash_context` and a + * procedural API. It is configured with the implementations (the vtables) + * that it should use; it will then compute all these hash functions in + * parallel, on the same input. It is meant to be used in cases when the + * hash of an object will be used, but the exact hash function is not + * known yet (typically, streamed processing on X.509 certificates). + * + * Only the standard hash functions (MD5, SHA-1, SHA-224, SHA-256, SHA-384 + * and SHA-512) are supported by the multi-hasher. + * + * + * ## GHASH + * + * GHASH is not a generic hash function; it is a _universal_ hash function, + * which, as the name does not say, means that it CANNOT be used in most + * places where a hash function is needed. GHASH is used within the GCM + * encryption mode, to provide the checked integrity functionality. + * + * A GHASH implementation is basically a function that uses the type defined + * in this file under the name `br_ghash`: + * + * typedef void (*br_ghash)(void *y, const void *h, const void *data, size_t len); + * + * The `y` pointer refers to a 16-byte value which is used as input, and + * receives the output of the GHASH invocation. `h` is a 16-byte secret + * value (that serves as key). `data` and `len` define the input data. + * + * Three GHASH implementations are provided, all constant-time, based on + * the use of integer multiplications with appropriate masking to cancel + * carry propagation. + */ + +/** + * \brief Class type for hash function implementations. + * + * A `br_hash_class` instance references the methods implementing a hash + * function. Constant instances of this structure are defined for each + * implemented hash function. Such instances are also called "vtables". + * + * Vtables are used to support object-oriented programming, as + * described on [the BearSSL Web site](https://www.bearssl.org/oop.html). + */ +typedef struct br_hash_class_ br_hash_class; +struct br_hash_class_ { + /** + * \brief Size (in bytes) of the context structure appropriate for + * computing this hash function. + */ + size_t context_size; + + /** + * \brief Descriptor word that contains information about the hash + * function. + * + * For each word `xxx` described below, use `BR_HASHDESC_xxx_OFF` + * and `BR_HASHDESC_xxx_MASK` to access the specific value, as + * follows: + * + * (hf->desc >> BR_HASHDESC_xxx_OFF) & BR_HASHDESC_xxx_MASK + * + * The defined elements are: + * + * - `ID`: the symbolic identifier for the function, as defined + * in [TLS](https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1) + * (MD5 = 1, SHA-1 = 2,...). + * + * - `OUT`: hash output size, in bytes. + * + * - `STATE`: internal running state size, in bytes. + * + * - `LBLEN`: base-2 logarithm for the internal block size, as + * defined for HMAC processing (this is 6 for MD5, SHA-1, SHA-224 + * and SHA-256, since these functions use 64-byte blocks; for + * SHA-384 and SHA-512, this is 7, corresponding to their + * 128-byte blocks). + * + * The descriptor may contain a few other flags. + */ + uint32_t desc; + + /** + * \brief Initialisation method. + * + * This method takes as parameter a pointer to a context area, + * that it initialises. The first field of the context is set + * to this vtable; other elements are initialised for a new hash + * computation. + * + * \param ctx pointer to (the first field of) the context. + */ + void (*init)(const br_hash_class **ctx); + + /** + * \brief Data injection method. + * + * The `len` bytes starting at address `data` are injected into + * the running hash computation incarnated by the specified + * context. The context is updated accordingly. It is allowed + * to have `len == 0`, in which case `data` is ignored (and could + * be `NULL`), and nothing happens. + * on the input data. + * + * \param ctx pointer to (the first field of) the context. + * \param data pointer to the first data byte to inject. + * \param len number of bytes to inject. + */ + void (*update)(const br_hash_class **ctx, const void *data, size_t len); + + /** + * \brief Produce hash output. + * + * The hash output corresponding to all data bytes injected in the + * context since the last `init()` call is computed, and written + * in the buffer pointed to by `dst`. The hash output size depends + * on the implemented hash function (e.g. 16 bytes for MD5). + * The context is _not_ modified by this call, so further bytes + * may be afterwards injected to continue the current computation. + * + * \param ctx pointer to (the first field of) the context. + * \param dst destination buffer for the hash output. + */ + void (*out)(const br_hash_class *const *ctx, void *dst); + + /** + * \brief Get running state. + * + * This method saves the current running state into the `dst` + * buffer. What constitutes the "running state" depends on the + * hash function; for Merkle-Damgård hash functions (like + * MD5 or SHA-1), this is the output obtained after processing + * each block. The number of bytes injected so far is returned. + * The context is not modified by this call. + * + * \param ctx pointer to (the first field of) the context. + * \param dst destination buffer for the state. + * \return the injected total byte length. + */ + uint64_t (*state)(const br_hash_class *const *ctx, void *dst); + + /** + * \brief Set running state. + * + * This methods replaces the running state for the function. + * + * \param ctx pointer to (the first field of) the context. + * \param stb source buffer for the state. + * \param count injected total byte length. + */ + void (*set_state)(const br_hash_class **ctx, + const void *stb, uint64_t count); +}; + +#ifndef BR_DOXYGEN_IGNORE +#define BR_HASHDESC_ID(id) ((uint32_t)(id) << BR_HASHDESC_ID_OFF) +#define BR_HASHDESC_ID_OFF 0 +#define BR_HASHDESC_ID_MASK 0xFF + +#define BR_HASHDESC_OUT(size) ((uint32_t)(size) << BR_HASHDESC_OUT_OFF) +#define BR_HASHDESC_OUT_OFF 8 +#define BR_HASHDESC_OUT_MASK 0x7F + +#define BR_HASHDESC_STATE(size) ((uint32_t)(size) << BR_HASHDESC_STATE_OFF) +#define BR_HASHDESC_STATE_OFF 15 +#define BR_HASHDESC_STATE_MASK 0xFF + +#define BR_HASHDESC_LBLEN(ls) ((uint32_t)(ls) << BR_HASHDESC_LBLEN_OFF) +#define BR_HASHDESC_LBLEN_OFF 23 +#define BR_HASHDESC_LBLEN_MASK 0x0F + +#define BR_HASHDESC_MD_PADDING ((uint32_t)1 << 28) +#define BR_HASHDESC_MD_PADDING_128 ((uint32_t)1 << 29) +#define BR_HASHDESC_MD_PADDING_BE ((uint32_t)1 << 30) +#endif + +/* + * Specific hash functions. + * + * Rules for contexts: + * -- No interior pointer. + * -- No pointer to external dynamically allocated resources. + * -- First field is called 'vtable' and is a pointer to a + * const-qualified br_hash_class instance (pointer is set by init()). + * -- SHA-224 and SHA-256 contexts are identical. + * -- SHA-384 and SHA-512 contexts are identical. + * + * Thus, contexts can be moved and cloned to capture the hash function + * current state; and there is no need for any explicit "release" function. + */ + +/** + * \brief Symbolic identifier for MD5. + */ +#define br_md5_ID 1 + +/** + * \brief MD5 output size (in bytes). + */ +#define br_md5_SIZE 16 + +/** + * \brief Constant vtable for MD5. + */ +extern const br_hash_class br_md5_vtable; + +/** + * \brief MD5 context. + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** + * \brief Pointer to vtable for this context. + */ + const br_hash_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + unsigned char buf[64]; + uint64_t count; + uint32_t val[4]; +#endif +} br_md5_context; + +/** + * \brief MD5 context initialisation. + * + * This function initialises or resets a context for a new MD5 + * computation. It also sets the vtable pointer. + * + * \param ctx pointer to the context structure. + */ +void br_md5_init(br_md5_context *ctx); + +/** + * \brief Inject some data bytes in a running MD5 computation. + * + * The provided context is updated with some data bytes. If the number + * of bytes (`len`) is zero, then the data pointer (`data`) is ignored + * and may be `NULL`, and this function does nothing. + * + * \param ctx pointer to the context structure. + * \param data pointer to the injected data. + * \param len injected data length (in bytes). + */ +void br_md5_update(br_md5_context *ctx, const void *data, size_t len); + +/** + * \brief Compute MD5 output. + * + * The MD5 output for the concatenation of all bytes injected in the + * provided context since the last initialisation or reset call, is + * computed and written in the buffer pointed to by `out`. The context + * itself is not modified, so extra bytes may be injected afterwards + * to continue that computation. + * + * \param ctx pointer to the context structure. + * \param out destination buffer for the hash output. + */ +void br_md5_out(const br_md5_context *ctx, void *out); + +/** + * \brief Save MD5 running state. + * + * The running state for MD5 (output of the last internal block + * processing) is written in the buffer pointed to by `out`. The + * number of bytes injected since the last initialisation or reset + * call is returned. The context is not modified. + * + * \param ctx pointer to the context structure. + * \param out destination buffer for the running state. + * \return the injected total byte length. + */ +uint64_t br_md5_state(const br_md5_context *ctx, void *out); + +/** + * \brief Restore MD5 running state. + * + * The running state for MD5 is set to the provided values. + * + * \param ctx pointer to the context structure. + * \param stb source buffer for the running state. + * \param count the injected total byte length. + */ +void br_md5_set_state(br_md5_context *ctx, const void *stb, uint64_t count); + +/** + * \brief Symbolic identifier for SHA-1. + */ +#define br_sha1_ID 2 + +/** + * \brief SHA-1 output size (in bytes). + */ +#define br_sha1_SIZE 20 + +/** + * \brief Constant vtable for SHA-1. + */ +extern const br_hash_class br_sha1_vtable; + +/** + * \brief SHA-1 context. + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** + * \brief Pointer to vtable for this context. + */ + const br_hash_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + unsigned char buf[64]; + uint64_t count; + uint32_t val[5]; +#endif +} br_sha1_context; + +/** + * \brief SHA-1 context initialisation. + * + * This function initialises or resets a context for a new SHA-1 + * computation. It also sets the vtable pointer. + * + * \param ctx pointer to the context structure. + */ +void br_sha1_init(br_sha1_context *ctx); + +/** + * \brief Inject some data bytes in a running SHA-1 computation. + * + * The provided context is updated with some data bytes. If the number + * of bytes (`len`) is zero, then the data pointer (`data`) is ignored + * and may be `NULL`, and this function does nothing. + * + * \param ctx pointer to the context structure. + * \param data pointer to the injected data. + * \param len injected data length (in bytes). + */ +void br_sha1_update(br_sha1_context *ctx, const void *data, size_t len); + +/** + * \brief Compute SHA-1 output. + * + * The SHA-1 output for the concatenation of all bytes injected in the + * provided context since the last initialisation or reset call, is + * computed and written in the buffer pointed to by `out`. The context + * itself is not modified, so extra bytes may be injected afterwards + * to continue that computation. + * + * \param ctx pointer to the context structure. + * \param out destination buffer for the hash output. + */ +void br_sha1_out(const br_sha1_context *ctx, void *out); + +/** + * \brief Save SHA-1 running state. + * + * The running state for SHA-1 (output of the last internal block + * processing) is written in the buffer pointed to by `out`. The + * number of bytes injected since the last initialisation or reset + * call is returned. The context is not modified. + * + * \param ctx pointer to the context structure. + * \param out destination buffer for the running state. + * \return the injected total byte length. + */ +uint64_t br_sha1_state(const br_sha1_context *ctx, void *out); + +/** + * \brief Restore SHA-1 running state. + * + * The running state for SHA-1 is set to the provided values. + * + * \param ctx pointer to the context structure. + * \param stb source buffer for the running state. + * \param count the injected total byte length. + */ +void br_sha1_set_state(br_sha1_context *ctx, const void *stb, uint64_t count); + +/** + * \brief Symbolic identifier for SHA-224. + */ +#define br_sha224_ID 3 + +/** + * \brief SHA-224 output size (in bytes). + */ +#define br_sha224_SIZE 28 + +/** + * \brief Constant vtable for SHA-224. + */ +extern const br_hash_class br_sha224_vtable; + +/** + * \brief SHA-224 context. + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** + * \brief Pointer to vtable for this context. + */ + const br_hash_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + unsigned char buf[64]; + uint64_t count; + uint32_t val[8]; +#endif +} br_sha224_context; + +/** + * \brief SHA-224 context initialisation. + * + * This function initialises or resets a context for a new SHA-224 + * computation. It also sets the vtable pointer. + * + * \param ctx pointer to the context structure. + */ +void br_sha224_init(br_sha224_context *ctx); + +/** + * \brief Inject some data bytes in a running SHA-224 computation. + * + * The provided context is updated with some data bytes. If the number + * of bytes (`len`) is zero, then the data pointer (`data`) is ignored + * and may be `NULL`, and this function does nothing. + * + * \param ctx pointer to the context structure. + * \param data pointer to the injected data. + * \param len injected data length (in bytes). + */ +void br_sha224_update(br_sha224_context *ctx, const void *data, size_t len); + +/** + * \brief Compute SHA-224 output. + * + * The SHA-224 output for the concatenation of all bytes injected in the + * provided context since the last initialisation or reset call, is + * computed and written in the buffer pointed to by `out`. The context + * itself is not modified, so extra bytes may be injected afterwards + * to continue that computation. + * + * \param ctx pointer to the context structure. + * \param out destination buffer for the hash output. + */ +void br_sha224_out(const br_sha224_context *ctx, void *out); + +/** + * \brief Save SHA-224 running state. + * + * The running state for SHA-224 (output of the last internal block + * processing) is written in the buffer pointed to by `out`. The + * number of bytes injected since the last initialisation or reset + * call is returned. The context is not modified. + * + * \param ctx pointer to the context structure. + * \param out destination buffer for the running state. + * \return the injected total byte length. + */ +uint64_t br_sha224_state(const br_sha224_context *ctx, void *out); + +/** + * \brief Restore SHA-224 running state. + * + * The running state for SHA-224 is set to the provided values. + * + * \param ctx pointer to the context structure. + * \param stb source buffer for the running state. + * \param count the injected total byte length. + */ +void br_sha224_set_state(br_sha224_context *ctx, + const void *stb, uint64_t count); + +/** + * \brief Symbolic identifier for SHA-256. + */ +#define br_sha256_ID 4 + +/** + * \brief SHA-256 output size (in bytes). + */ +#define br_sha256_SIZE 32 + +/** + * \brief Constant vtable for SHA-256. + */ +extern const br_hash_class br_sha256_vtable; + +#ifdef BR_DOXYGEN_IGNORE +/** + * \brief SHA-256 context. + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** + * \brief Pointer to vtable for this context. + */ + const br_hash_class *vtable; +} br_sha256_context; +#else +typedef br_sha224_context br_sha256_context; +#endif + +/** + * \brief SHA-256 context initialisation. + * + * This function initialises or resets a context for a new SHA-256 + * computation. It also sets the vtable pointer. + * + * \param ctx pointer to the context structure. + */ +void br_sha256_init(br_sha256_context *ctx); + +#ifdef BR_DOXYGEN_IGNORE +/** + * \brief Inject some data bytes in a running SHA-256 computation. + * + * The provided context is updated with some data bytes. If the number + * of bytes (`len`) is zero, then the data pointer (`data`) is ignored + * and may be `NULL`, and this function does nothing. + * + * \param ctx pointer to the context structure. + * \param data pointer to the injected data. + * \param len injected data length (in bytes). + */ +void br_sha256_update(br_sha256_context *ctx, const void *data, size_t len); +#else +#define br_sha256_update br_sha224_update +#endif + +/** + * \brief Compute SHA-256 output. + * + * The SHA-256 output for the concatenation of all bytes injected in the + * provided context since the last initialisation or reset call, is + * computed and written in the buffer pointed to by `out`. The context + * itself is not modified, so extra bytes may be injected afterwards + * to continue that computation. + * + * \param ctx pointer to the context structure. + * \param out destination buffer for the hash output. + */ +void br_sha256_out(const br_sha256_context *ctx, void *out); + +#ifdef BR_DOXYGEN_IGNORE +/** + * \brief Save SHA-256 running state. + * + * The running state for SHA-256 (output of the last internal block + * processing) is written in the buffer pointed to by `out`. The + * number of bytes injected since the last initialisation or reset + * call is returned. The context is not modified. + * + * \param ctx pointer to the context structure. + * \param out destination buffer for the running state. + * \return the injected total byte length. + */ +uint64_t br_sha256_state(const br_sha256_context *ctx, void *out); +#else +#define br_sha256_state br_sha224_state +#endif + +#ifdef BR_DOXYGEN_IGNORE +/** + * \brief Restore SHA-256 running state. + * + * The running state for SHA-256 is set to the provided values. + * + * \param ctx pointer to the context structure. + * \param stb source buffer for the running state. + * \param count the injected total byte length. + */ +void br_sha256_set_state(br_sha256_context *ctx, + const void *stb, uint64_t count); +#else +#define br_sha256_set_state br_sha224_set_state +#endif + +/** + * \brief Symbolic identifier for SHA-384. + */ +#define br_sha384_ID 5 + +/** + * \brief SHA-384 output size (in bytes). + */ +#define br_sha384_SIZE 48 + +/** + * \brief Constant vtable for SHA-384. + */ +extern const br_hash_class br_sha384_vtable; + +/** + * \brief SHA-384 context. + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** + * \brief Pointer to vtable for this context. + */ + const br_hash_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + unsigned char buf[128]; + uint64_t count; + uint64_t val[8]; +#endif +} br_sha384_context; + +/** + * \brief SHA-384 context initialisation. + * + * This function initialises or resets a context for a new SHA-384 + * computation. It also sets the vtable pointer. + * + * \param ctx pointer to the context structure. + */ +void br_sha384_init(br_sha384_context *ctx); + +/** + * \brief Inject some data bytes in a running SHA-384 computation. + * + * The provided context is updated with some data bytes. If the number + * of bytes (`len`) is zero, then the data pointer (`data`) is ignored + * and may be `NULL`, and this function does nothing. + * + * \param ctx pointer to the context structure. + * \param data pointer to the injected data. + * \param len injected data length (in bytes). + */ +void br_sha384_update(br_sha384_context *ctx, const void *data, size_t len); + +/** + * \brief Compute SHA-384 output. + * + * The SHA-384 output for the concatenation of all bytes injected in the + * provided context since the last initialisation or reset call, is + * computed and written in the buffer pointed to by `out`. The context + * itself is not modified, so extra bytes may be injected afterwards + * to continue that computation. + * + * \param ctx pointer to the context structure. + * \param out destination buffer for the hash output. + */ +void br_sha384_out(const br_sha384_context *ctx, void *out); + +/** + * \brief Save SHA-384 running state. + * + * The running state for SHA-384 (output of the last internal block + * processing) is written in the buffer pointed to by `out`. The + * number of bytes injected since the last initialisation or reset + * call is returned. The context is not modified. + * + * \param ctx pointer to the context structure. + * \param out destination buffer for the running state. + * \return the injected total byte length. + */ +uint64_t br_sha384_state(const br_sha384_context *ctx, void *out); + +/** + * \brief Restore SHA-384 running state. + * + * The running state for SHA-384 is set to the provided values. + * + * \param ctx pointer to the context structure. + * \param stb source buffer for the running state. + * \param count the injected total byte length. + */ +void br_sha384_set_state(br_sha384_context *ctx, + const void *stb, uint64_t count); + +/** + * \brief Symbolic identifier for SHA-512. + */ +#define br_sha512_ID 6 + +/** + * \brief SHA-512 output size (in bytes). + */ +#define br_sha512_SIZE 64 + +/** + * \brief Constant vtable for SHA-512. + */ +extern const br_hash_class br_sha512_vtable; + +#ifdef BR_DOXYGEN_IGNORE +/** + * \brief SHA-512 context. + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** + * \brief Pointer to vtable for this context. + */ + const br_hash_class *vtable; +} br_sha512_context; +#else +typedef br_sha384_context br_sha512_context; +#endif + +/** + * \brief SHA-512 context initialisation. + * + * This function initialises or resets a context for a new SHA-512 + * computation. It also sets the vtable pointer. + * + * \param ctx pointer to the context structure. + */ +void br_sha512_init(br_sha512_context *ctx); + +#ifdef BR_DOXYGEN_IGNORE +/** + * \brief Inject some data bytes in a running SHA-512 computation. + * + * The provided context is updated with some data bytes. If the number + * of bytes (`len`) is zero, then the data pointer (`data`) is ignored + * and may be `NULL`, and this function does nothing. + * + * \param ctx pointer to the context structure. + * \param data pointer to the injected data. + * \param len injected data length (in bytes). + */ +void br_sha512_update(br_sha512_context *ctx, const void *data, size_t len); +#else +#define br_sha512_update br_sha384_update +#endif + +/** + * \brief Compute SHA-512 output. + * + * The SHA-512 output for the concatenation of all bytes injected in the + * provided context since the last initialisation or reset call, is + * computed and written in the buffer pointed to by `out`. The context + * itself is not modified, so extra bytes may be injected afterwards + * to continue that computation. + * + * \param ctx pointer to the context structure. + * \param out destination buffer for the hash output. + */ +void br_sha512_out(const br_sha512_context *ctx, void *out); + +#ifdef BR_DOXYGEN_IGNORE +/** + * \brief Save SHA-512 running state. + * + * The running state for SHA-512 (output of the last internal block + * processing) is written in the buffer pointed to by `out`. The + * number of bytes injected since the last initialisation or reset + * call is returned. The context is not modified. + * + * \param ctx pointer to the context structure. + * \param out destination buffer for the running state. + * \return the injected total byte length. + */ +uint64_t br_sha512_state(const br_sha512_context *ctx, void *out); +#else +#define br_sha512_state br_sha384_state +#endif + +#ifdef BR_DOXYGEN_IGNORE +/** + * \brief Restore SHA-512 running state. + * + * The running state for SHA-512 is set to the provided values. + * + * \param ctx pointer to the context structure. + * \param stb source buffer for the running state. + * \param count the injected total byte length. + */ +void br_sha512_set_state(br_sha512_context *ctx, + const void *stb, uint64_t count); +#else +#define br_sha512_set_state br_sha384_set_state +#endif + +/* + * "md5sha1" is a special hash function that computes both MD5 and SHA-1 + * on the same input, and produces a 36-byte output (MD5 and SHA-1 + * concatenation, in that order). State size is also 36 bytes. + */ + +/** + * \brief Symbolic identifier for MD5+SHA-1. + * + * MD5+SHA-1 is the concatenation of MD5 and SHA-1, computed over the + * same input. It is not one of the functions identified in TLS, so + * we give it a symbolic identifier of value 0. + */ +#define br_md5sha1_ID 0 + +/** + * \brief MD5+SHA-1 output size (in bytes). + */ +#define br_md5sha1_SIZE 36 + +/** + * \brief Constant vtable for MD5+SHA-1. + */ +extern const br_hash_class br_md5sha1_vtable; + +/** + * \brief MD5+SHA-1 context. + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** + * \brief Pointer to vtable for this context. + */ + const br_hash_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + unsigned char buf[64]; + uint64_t count; + uint32_t val_md5[4]; + uint32_t val_sha1[5]; +#endif +} br_md5sha1_context; + +/** + * \brief MD5+SHA-1 context initialisation. + * + * This function initialises or resets a context for a new SHA-512 + * computation. It also sets the vtable pointer. + * + * \param ctx pointer to the context structure. + */ +void br_md5sha1_init(br_md5sha1_context *ctx); + +/** + * \brief Inject some data bytes in a running MD5+SHA-1 computation. + * + * The provided context is updated with some data bytes. If the number + * of bytes (`len`) is zero, then the data pointer (`data`) is ignored + * and may be `NULL`, and this function does nothing. + * + * \param ctx pointer to the context structure. + * \param data pointer to the injected data. + * \param len injected data length (in bytes). + */ +void br_md5sha1_update(br_md5sha1_context *ctx, const void *data, size_t len); + +/** + * \brief Compute MD5+SHA-1 output. + * + * The MD5+SHA-1 output for the concatenation of all bytes injected in the + * provided context since the last initialisation or reset call, is + * computed and written in the buffer pointed to by `out`. The context + * itself is not modified, so extra bytes may be injected afterwards + * to continue that computation. + * + * \param ctx pointer to the context structure. + * \param out destination buffer for the hash output. + */ +void br_md5sha1_out(const br_md5sha1_context *ctx, void *out); + +/** + * \brief Save MD5+SHA-1 running state. + * + * The running state for MD5+SHA-1 (output of the last internal block + * processing) is written in the buffer pointed to by `out`. The + * number of bytes injected since the last initialisation or reset + * call is returned. The context is not modified. + * + * \param ctx pointer to the context structure. + * \param out destination buffer for the running state. + * \return the injected total byte length. + */ +uint64_t br_md5sha1_state(const br_md5sha1_context *ctx, void *out); + +/** + * \brief Restore MD5+SHA-1 running state. + * + * The running state for MD5+SHA-1 is set to the provided values. + * + * \param ctx pointer to the context structure. + * \param stb source buffer for the running state. + * \param count the injected total byte length. + */ +void br_md5sha1_set_state(br_md5sha1_context *ctx, + const void *stb, uint64_t count); + +/** + * \brief Aggregate context for configurable hash function support. + * + * The `br_hash_compat_context` type is a type which is large enough to + * serve as context for all standard hash functions defined above. + */ +typedef union { + const br_hash_class *vtable; + br_md5_context md5; + br_sha1_context sha1; + br_sha224_context sha224; + br_sha256_context sha256; + br_sha384_context sha384; + br_sha512_context sha512; + br_md5sha1_context md5sha1; +} br_hash_compat_context; + +/* + * The multi-hasher is a construct that handles hashing of the same input + * data with several hash functions, with a single shared input buffer. + * It can handle MD5, SHA-1, SHA-224, SHA-256, SHA-384 and SHA-512 + * simultaneously, though which functions are activated depends on + * the set implementation pointers. + */ + +/** + * \brief Multi-hasher context structure. + * + * The multi-hasher runs up to six hash functions in the standard TLS list + * (MD5, SHA-1, SHA-224, SHA-256, SHA-384 and SHA-512) in parallel, over + * the same input. + * + * The multi-hasher does _not_ follow the OOP structure with a vtable. + * Instead, it is configured with the vtables of the hash functions it + * should run. Structure fields are not supposed to be accessed directly. + */ +typedef struct { +#ifndef BR_DOXYGEN_IGNORE + unsigned char buf[128]; + uint64_t count; + uint32_t val_32[25]; + uint64_t val_64[16]; + const br_hash_class *impl[6]; +#endif +} br_multihash_context; + +/** + * \brief Clear a multi-hasher context. + * + * This should always be called once on a given context, _before_ setting + * the implementation pointers. + * + * \param ctx the multi-hasher context. + */ +void br_multihash_zero(br_multihash_context *ctx); + +/** + * \brief Set a hash function implementation. + * + * Implementations shall be set _after_ clearing the context (with + * `br_multihash_zero()`) but _before_ initialising the computation + * (with `br_multihash_init()`). The hash function implementation + * MUST be one of the standard hash functions (MD5, SHA-1, SHA-224, + * SHA-256, SHA-384 or SHA-512); it may also be `NULL` to remove + * an implementation from the multi-hasher. + * + * \param ctx the multi-hasher context. + * \param id the hash function symbolic identifier. + * \param impl the hash function vtable, or `NULL`. + */ +static inline void +br_multihash_setimpl(br_multihash_context *ctx, + int id, const br_hash_class *impl) +{ + /* + * This code relies on hash functions ID being values 1 to 6, + * in the MD5 to SHA-512 order. + */ + ctx->impl[id - 1] = impl; +} + +/** + * \brief Get a hash function implementation. + * + * This function returns the currently configured vtable for a given + * hash function (by symbolic ID). If no such function was configured in + * the provided multi-hasher context, then this function returns `NULL`. + * + * \param ctx the multi-hasher context. + * \param id the hash function symbolic identifier. + * \return the hash function vtable, or `NULL`. + */ +static inline const br_hash_class * +br_multihash_getimpl(const br_multihash_context *ctx, int id) +{ + return ctx->impl[id - 1]; +} + +/** + * \brief Reset a multi-hasher context. + * + * This function prepares the context for a new hashing computation, + * for all implementations configured at that point. + * + * \param ctx the multi-hasher context. + */ +void br_multihash_init(br_multihash_context *ctx); + +/** + * \brief Inject some data bytes in a running multi-hashing computation. + * + * The provided context is updated with some data bytes. If the number + * of bytes (`len`) is zero, then the data pointer (`data`) is ignored + * and may be `NULL`, and this function does nothing. + * + * \param ctx pointer to the context structure. + * \param data pointer to the injected data. + * \param len injected data length (in bytes). + */ +void br_multihash_update(br_multihash_context *ctx, + const void *data, size_t len); + +/** + * \brief Compute a hash output from a multi-hasher. + * + * The hash output for the concatenation of all bytes injected in the + * provided context since the last initialisation or reset call, is + * computed and written in the buffer pointed to by `dst`. The hash + * function to use is identified by `id` and must be one of the standard + * hash functions. If that hash function was indeed configured in the + * multi-hasher context, the corresponding hash value is written in + * `dst` and its length (in bytes) is returned. If the hash function + * was _not_ configured, then nothing is written in `dst` and 0 is + * returned. + * + * The context itself is not modified, so extra bytes may be injected + * afterwards to continue the hash computations. + * + * \param ctx pointer to the context structure. + * \param id the hash function symbolic identifier. + * \param dst destination buffer for the hash output. + * \return the hash output length (in bytes), or 0. + */ +size_t br_multihash_out(const br_multihash_context *ctx, int id, void *dst); + +/** + * \brief Type for a GHASH implementation. + * + * GHASH is a sort of keyed hash meant to be used to implement GCM in + * combination with a block cipher (with 16-byte blocks). + * + * The `y` array has length 16 bytes and is used for input and output; in + * a complete GHASH run, it starts with an all-zero value. `h` is a 16-byte + * value that serves as key (it is derived from the encryption key in GCM, + * using the block cipher). The data length (`len`) is expressed in bytes. + * The `y` array is updated. + * + * If the data length is not a multiple of 16, then the data is implicitly + * padded with zeros up to the next multiple of 16. Thus, when using GHASH + * in GCM, this method may be called twice, for the associated data and + * for the ciphertext, respectively; the zero-padding implements exactly + * the GCM rules. + * + * \param y the array to update. + * \param h the GHASH key. + * \param data the input data (may be `NULL` if `len` is zero). + * \param len the input data length (in bytes). + */ +typedef void (*br_ghash)(void *y, const void *h, const void *data, size_t len); + +/** + * \brief GHASH implementation using multiplications (mixed 32-bit). + * + * This implementation uses multiplications of 32-bit values, with a + * 64-bit result. It is constant-time (if multiplications are + * constant-time). + * + * \param y the array to update. + * \param h the GHASH key. + * \param data the input data (may be `NULL` if `len` is zero). + * \param len the input data length (in bytes). + */ +void br_ghash_ctmul(void *y, const void *h, const void *data, size_t len); + +/** + * \brief GHASH implementation using multiplications (strict 32-bit). + * + * This implementation uses multiplications of 32-bit values, with a + * 32-bit result. It is usually somewhat slower than `br_ghash_ctmul()`, + * but it is expected to be faster on architectures for which the + * 32-bit multiplication opcode does not yield the upper 32 bits of the + * product. It is constant-time (if multiplications are constant-time). + * + * \param y the array to update. + * \param h the GHASH key. + * \param data the input data (may be `NULL` if `len` is zero). + * \param len the input data length (in bytes). + */ +void br_ghash_ctmul32(void *y, const void *h, const void *data, size_t len); + +/** + * \brief GHASH implementation using multiplications (64-bit). + * + * This implementation uses multiplications of 64-bit values, with a + * 64-bit result. It is constant-time (if multiplications are + * constant-time). It is substantially faster than `br_ghash_ctmul()` + * and `br_ghash_ctmul32()` on most 64-bit architectures. + * + * \param y the array to update. + * \param h the GHASH key. + * \param data the input data (may be `NULL` if `len` is zero). + * \param len the input data length (in bytes). + */ +void br_ghash_ctmul64(void *y, const void *h, const void *data, size_t len); + +/** + * \brief GHASH implementation using the `pclmulqdq` opcode (part of the + * AES-NI instructions). + * + * This implementation is available only on x86 platforms where the + * compiler supports the relevant intrinsic functions. Even if the + * compiler supports these functions, the local CPU might not support + * the `pclmulqdq` opcode, meaning that a call will fail with an + * illegal instruction exception. To safely obtain a pointer to this + * function when supported (or 0 otherwise), use `br_ghash_pclmul_get()`. + * + * \param y the array to update. + * \param h the GHASH key. + * \param data the input data (may be `NULL` if `len` is zero). + * \param len the input data length (in bytes). + */ +void br_ghash_pclmul(void *y, const void *h, const void *data, size_t len); + +/** + * \brief Obtain the `pclmul` GHASH implementation, if available. + * + * If the `pclmul` implementation was compiled in the library (depending + * on the compiler abilities) _and_ the local CPU appears to support the + * opcode, then this function will return a pointer to the + * `br_ghash_pclmul()` function. Otherwise, it will return `0`. + * + * \return the `pclmul` GHASH implementation, or `0`. + */ +br_ghash br_ghash_pclmul_get(void); + +/** + * \brief GHASH implementation using the POWER8 opcodes. + * + * This implementation is available only on POWER8 platforms (and later). + * To safely obtain a pointer to this function when supported (or 0 + * otherwise), use `br_ghash_pwr8_get()`. + * + * \param y the array to update. + * \param h the GHASH key. + * \param data the input data (may be `NULL` if `len` is zero). + * \param len the input data length (in bytes). + */ +void br_ghash_pwr8(void *y, const void *h, const void *data, size_t len); + +/** + * \brief Obtain the `pwr8` GHASH implementation, if available. + * + * If the `pwr8` implementation was compiled in the library (depending + * on the compiler abilities) _and_ the local CPU appears to support the + * opcode, then this function will return a pointer to the + * `br_ghash_pwr8()` function. Otherwise, it will return `0`. + * + * \return the `pwr8` GHASH implementation, or `0`. + */ +br_ghash br_ghash_pwr8_get(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/bearssl-esp8266/src/t_bearssl_hmac.h b/lib/bearssl-esp8266/src/t_bearssl_hmac.h new file mode 100644 index 000000000..bab2afe14 --- /dev/null +++ b/lib/bearssl-esp8266/src/t_bearssl_hmac.h @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef BR_BEARSSL_HMAC_H__ +#define BR_BEARSSL_HMAC_H__ + +#include +#include + +#include "t_bearssl_hash.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** \file bearssl_hmac.h + * + * # HMAC + * + * HMAC is initialized with a key and an underlying hash function; it + * then fills a "key context". That context contains the processed + * key. + * + * With the key context, a HMAC context can be initialized to process + * the input bytes and obtain the MAC output. The key context is not + * modified during that process, and can be reused. + * + * IMPORTANT: HMAC shall be used only with functions that have the + * following properties: + * + * - hash output size does not exceed 64 bytes; + * - hash internal state size does not exceed 64 bytes; + * - internal block length is a power of 2 between 16 and 256 bytes. + */ + +/** + * \brief HMAC key context. + * + * The HMAC key context is initialised with a hash function implementation + * and a secret key. Contents are opaque (callers should not access them + * directly). The caller is responsible for allocating the context where + * appropriate. Context initialisation and usage incurs no dynamic + * allocation, so there is no release function. + */ +typedef struct { +#ifndef BR_DOXYGEN_IGNORE + const br_hash_class *dig_vtable; + unsigned char ksi[64], kso[64]; +#endif +} br_hmac_key_context; + +/** + * \brief HMAC key context initialisation. + * + * Initialise the key context with the provided key, using the hash function + * identified by `digest_vtable`. This supports arbitrary key lengths. + * + * \param kc HMAC key context to initialise. + * \param digest_vtable pointer to the hash function implementation vtable. + * \param key pointer to the HMAC secret key. + * \param key_len HMAC secret key length (in bytes). + */ +void br_hmac_key_init(br_hmac_key_context *kc, + const br_hash_class *digest_vtable, const void *key, size_t key_len); + +/* + * \brief Get the underlying hash function. + * + * This function returns a pointer to the implementation vtable of the + * hash function used for this HMAC key context. + * + * \param kc HMAC key context. + * \return the hash function implementation. + */ +static inline const br_hash_class *br_hmac_key_get_digest( + const br_hmac_key_context *kc) +{ + return kc->dig_vtable; +} + +/** + * \brief HMAC computation context. + * + * The HMAC computation context maintains the state for a single HMAC + * computation. It is modified as input bytes are injected. The context + * is caller-allocated and has no release function since it does not + * dynamically allocate external resources. Its contents are opaque. + */ +typedef struct { +#ifndef BR_DOXYGEN_IGNORE + br_hash_compat_context dig; + unsigned char kso[64]; + size_t out_len; +#endif +} br_hmac_context; + +/** + * \brief HMAC computation initialisation. + * + * Initialise a HMAC context with a key context. The key context is + * unmodified. Relevant data from the key context is immediately copied; + * the key context can thus be independently reused, modified or released + * without impacting this HMAC computation. + * + * An explicit output length can be specified; the actual output length + * will be the minimum of that value and the natural HMAC output length. + * If `out_len` is 0, then the natural HMAC output length is selected. The + * "natural output length" is the output length of the underlying hash + * function. + * + * \param ctx HMAC context to initialise. + * \param kc HMAC key context (already initialised with the key). + * \param out_len HMAC output length (0 to select "natural length"). + */ +void br_hmac_init(br_hmac_context *ctx, + const br_hmac_key_context *kc, size_t out_len); + +/** + * \brief Get the HMAC output size. + * + * The HMAC output size is the number of bytes that will actually be + * produced with `br_hmac_out()` with the provided context. This function + * MUST NOT be called on a non-initialised HMAC computation context. + * The returned value is the minimum of the HMAC natural length (output + * size of the underlying hash function) and the `out_len` parameter which + * was used with the last `br_hmac_init()` call on that context (if the + * initialisation `out_len` parameter was 0, then this function will + * return the HMAC natural length). + * + * \param ctx the (already initialised) HMAC computation context. + * \return the HMAC actual output size. + */ +static inline size_t +br_hmac_size(br_hmac_context *ctx) +{ + return ctx->out_len; +} + +/* + * \brief Get the underlying hash function. + * + * This function returns a pointer to the implementation vtable of the + * hash function used for this HMAC context. + * + * \param hc HMAC context. + * \return the hash function implementation. + */ +static inline const br_hash_class *br_hmac_get_digest( + const br_hmac_context *hc) +{ + return hc->dig.vtable; +} + +/** + * \brief Inject some bytes in HMAC. + * + * The provided `len` bytes are injected as extra input in the HMAC + * computation incarnated by the `ctx` HMAC context. It is acceptable + * that `len` is zero, in which case `data` is ignored (and may be + * `NULL`) and this function does nothing. + */ +void br_hmac_update(br_hmac_context *ctx, const void *data, size_t len); + +/** + * \brief Compute the HMAC output. + * + * The destination buffer MUST be large enough to accommodate the result; + * its length is at most the "natural length" of HMAC (i.e. the output + * length of the underlying hash function). The context is NOT modified; + * further bytes may be processed. Thus, "partial HMAC" values can be + * efficiently obtained. + * + * Returned value is the output length (in bytes). + * + * \param ctx HMAC computation context. + * \param out destination buffer for the HMAC output. + * \return the produced value length (in bytes). + */ +size_t br_hmac_out(const br_hmac_context *ctx, void *out); + +/** + * \brief Constant-time HMAC computation. + * + * This function compute the HMAC output in constant time. Some extra + * input bytes are processed, then the output is computed. The extra + * input consists in the `len` bytes pointed to by `data`. The `len` + * parameter must lie between `min_len` and `max_len` (inclusive); + * `max_len` bytes are actually read from `data`. Computing time (and + * memory access pattern) will not depend upon the data byte contents or + * the value of `len`. + * + * The output is written in the `out` buffer, that MUST be large enough + * to receive it. + * + * The difference `max_len - min_len` MUST be less than 230 + * (i.e. about one gigabyte). + * + * This function computes the output properly only if the underlying + * hash function uses MD padding (i.e. MD5, SHA-1, SHA-224, SHA-256, + * SHA-384 or SHA-512). + * + * The provided context is NOT modified. + * + * \param ctx the (already initialised) HMAC computation context. + * \param data the extra input bytes. + * \param len the extra input length (in bytes). + * \param min_len minimum extra input length (in bytes). + * \param max_len maximum extra input length (in bytes). + * \param out destination buffer for the HMAC output. + * \return the produced value length (in bytes). + */ +size_t br_hmac_outCT(const br_hmac_context *ctx, + const void *data, size_t len, size_t min_len, size_t max_len, + void *out); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/bearssl-esp8266/src/t_bearssl_kdf.h b/lib/bearssl-esp8266/src/t_bearssl_kdf.h new file mode 100644 index 000000000..417c8d629 --- /dev/null +++ b/lib/bearssl-esp8266/src/t_bearssl_kdf.h @@ -0,0 +1,284 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef BR_BEARSSL_KDF_H__ +#define BR_BEARSSL_KDF_H__ + +#include +#include + +#include "t_bearssl_hash.h" +#include "t_bearssl_hmac.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** \file bearssl_kdf.h + * + * # Key Derivation Functions + * + * KDF are functions that takes a variable length input, and provide a + * variable length output, meant to be used to derive subkeys from a + * master key. + * + * ## HKDF + * + * HKDF is a KDF defined by [RFC 5869](https://tools.ietf.org/html/rfc5869). + * It is based on HMAC, itself using an underlying hash function. Any + * hash function can be used, as long as it is compatible with the rules + * for the HMAC implementation (i.e. output size is 64 bytes or less, hash + * internal state size is 64 bytes or less, and the internal block length is + * a power of 2 between 16 and 256 bytes). HKDF has two phases: + * + * - HKDF-Extract: the input data in ingested, along with a "salt" value. + * + * - HKDF-Expand: the output is produced, from the result of processing + * the input and salt, and using an extra non-secret parameter called + * "info". + * + * The "salt" and "info" strings are non-secret and can be empty. Their role + * is normally to bind the input and output, respectively, to conventional + * identifiers that qualifu them within the used protocol or application. + * + * The implementation defined in this file uses the following functions: + * + * - `br_hkdf_init()`: initialize an HKDF context, with a hash function, + * and the salt. This starts the HKDF-Extract process. + * + * - `br_hkdf_inject()`: inject more input bytes. This function may be + * called repeatedly if the input data is provided by chunks. + * + * - `br_hkdf_flip()`: end the HKDF-Extract process, and start the + * HKDF-Expand process. + * + * - `br_hkdf_produce()`: get the next bytes of output. This function + * may be called several times to obtain the full output by chunks. + * For correct HKDF processing, the same "info" string must be + * provided for each call. + * + * Note that the HKDF total output size (the number of bytes that + * HKDF-Expand is willing to produce) is limited: if the hash output size + * is _n_ bytes, then the maximum output size is _255*n_. + * + * ## SHAKE + * + * SHAKE is defined in + * [FIPS 202](https://csrc.nist.gov/publications/detail/fips/202/final) + * under two versions: SHAKE128 and SHAKE256, offering an alleged + * "security level" of 128 and 256 bits, respectively (SHAKE128 is + * about 20 to 25% faster than SHAKE256). SHAKE internally relies on + * the Keccak family of sponge functions, not on any externally provided + * hash function. Contrary to HKDF, SHAKE does not have a concept of + * either a "salt" or an "info" string. The API consists in four + * functions: + * + * - `br_shake_init()`: initialize a SHAKE context for a given + * security level. + * + * - `br_shake_inject()`: inject more input bytes. This function may be + * called repeatedly if the input data is provided by chunks. + * + * - `br_shake_flip()`: end the data injection process, and start the + * data production process. + * + * - `br_shake_produce()`: get the next bytes of output. This function + * may be called several times to obtain the full output by chunks. + */ + +/** + * \brief HKDF context. + * + * The HKDF context is initialized with a hash function implementation + * and a salt value. Contents are opaque (callers should not access them + * directly). The caller is responsible for allocating the context where + * appropriate. Context initialisation and usage incurs no dynamic + * allocation, so there is no release function. + */ +typedef struct { +#ifndef BR_DOXYGEN_IGNORE + union { + br_hmac_context hmac_ctx; + br_hmac_key_context prk_ctx; + } u; + unsigned char buf[64]; + size_t ptr; + size_t dig_len; + unsigned chunk_num; +#endif +} br_hkdf_context; + +/** + * \brief HKDF context initialization. + * + * The underlying hash function and salt value are provided. Arbitrary + * salt lengths can be used. + * + * HKDF makes a difference between a salt of length zero, and an + * absent salt (the latter being equivalent to a salt consisting of + * bytes of value zero, of the same length as the hash function output). + * If `salt_len` is zero, then this function assumes that the salt is + * present but of length zero. To specify an _absent_ salt, use + * `BR_HKDF_NO_SALT` as `salt` parameter (`salt_len` is then ignored). + * + * \param hc HKDF context to initialise. + * \param digest_vtable pointer to the hash function implementation vtable. + * \param salt HKDF-Extract salt. + * \param salt_len HKDF-Extract salt length (in bytes). + */ +void br_hkdf_init(br_hkdf_context *hc, const br_hash_class *digest_vtable, + const void *salt, size_t salt_len); + +/** + * \brief The special "absent salt" value for HKDF. + */ +#define BR_HKDF_NO_SALT (&br_hkdf_no_salt) + +#ifndef BR_DOXYGEN_IGNORE +extern const unsigned char br_hkdf_no_salt; +#endif + +/** + * \brief HKDF input injection (HKDF-Extract). + * + * This function injects some more input bytes ("key material") into + * HKDF. This function may be called several times, after `br_hkdf_init()` + * but before `br_hkdf_flip()`. + * + * \param hc HKDF context. + * \param ikm extra input bytes. + * \param ikm_len number of extra input bytes. + */ +void br_hkdf_inject(br_hkdf_context *hc, const void *ikm, size_t ikm_len); + +/** + * \brief HKDF switch to the HKDF-Expand phase. + * + * This call terminates the HKDF-Extract process (input injection), and + * starts the HKDF-Expand process (output production). + * + * \param hc HKDF context. + */ +void br_hkdf_flip(br_hkdf_context *hc); + +/** + * \brief HKDF output production (HKDF-Expand). + * + * Produce more output bytes from the current state. This function may be + * called several times, but only after `br_hkdf_flip()`. + * + * Returned value is the number of actually produced bytes. The total + * output length is limited to 255 times the output length of the + * underlying hash function. + * + * \param hc HKDF context. + * \param info application specific information string. + * \param info_len application specific information string length (in bytes). + * \param out destination buffer for the HKDF output. + * \param out_len the length of the requested output (in bytes). + * \return the produced output length (in bytes). + */ +size_t br_hkdf_produce(br_hkdf_context *hc, + const void *info, size_t info_len, void *out, size_t out_len); + +/** + * \brief SHAKE context. + * + * The HKDF context is initialized with a "security level". The internal + * notion is called "capacity"; the capacity is twice the security level + * (for instance, SHAKE128 has capacity 256). + * + * The caller is responsible for allocating the context where + * appropriate. Context initialisation and usage incurs no dynamic + * allocation, so there is no release function. + */ +typedef struct { +#ifndef BR_DOXYGEN_IGNORE + unsigned char dbuf[200]; + size_t dptr; + size_t rate; + uint64_t A[25]; +#endif +} br_shake_context; + +/** + * \brief SHAKE context initialization. + * + * The context is initialized for the provided "security level". + * Internally, this sets the "capacity" to twice the security level; + * thus, for SHAKE128, the `security_level` parameter should be 128, + * which corresponds to a 256-bit capacity. + * + * Allowed security levels are all multiples of 32, from 32 to 768, + * inclusive. Larger security levels imply lower performance; levels + * beyond 256 bits don't make much sense. Standard levels are 128 + * and 256 bits (for SHAKE128 and SHAKE256, respectively). + * + * \param sc SHAKE context to initialise. + * \param security_level security level (in bits). + */ +void br_shake_init(br_shake_context *sc, int security_level); + +/** + * \brief SHAKE input injection. + * + * This function injects some more input bytes ("key material") into + * SHAKE. This function may be called several times, after `br_shake_init()` + * but before `br_shake_flip()`. + * + * \param sc SHAKE context. + * \param data extra input bytes. + * \param len number of extra input bytes. + */ +void br_shake_inject(br_shake_context *sc, const void *data, size_t len); + +/** + * \brief SHAKE switch to production phase. + * + * This call terminates the input injection process, and starts the + * output production process. + * + * \param sc SHAKE context. + */ +void br_shake_flip(br_shake_context *hc); + +/** + * \brief SHAKE output production. + * + * Produce more output bytes from the current state. This function may be + * called several times, but only after `br_shake_flip()`. + * + * There is no practical limit to the number of bytes that may be produced. + * + * \param sc SHAKE context. + * \param out destination buffer for the SHAKE output. + * \param len the length of the requested output (in bytes). + */ +void br_shake_produce(br_shake_context *sc, void *out, size_t len); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/bearssl-esp8266/src/t_bearssl_pem.h b/lib/bearssl-esp8266/src/t_bearssl_pem.h new file mode 100644 index 000000000..8dba58272 --- /dev/null +++ b/lib/bearssl-esp8266/src/t_bearssl_pem.h @@ -0,0 +1,294 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef BR_BEARSSL_PEM_H__ +#define BR_BEARSSL_PEM_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** \file bearssl_pem.h + * + * # PEM Support + * + * PEM is a traditional encoding layer use to store binary objects (in + * particular X.509 certificates, and private keys) in text files. While + * the acronym comes from an old, defunct standard ("Privacy Enhanced + * Mail"), the format has been reused, with some variations, by many + * systems, and is a _de facto_ standard, even though it is not, actually, + * specified in all clarity anywhere. + * + * ## Format Details + * + * BearSSL contains a generic, streamed PEM decoder, which handles the + * following format: + * + * - The input source (a sequence of bytes) is assumed to be the + * encoding of a text file in an ASCII-compatible charset. This + * includes ISO-8859-1, Windows-1252, and UTF-8 encodings. Each + * line ends on a newline character (U+000A LINE FEED). The + * U+000D CARRIAGE RETURN characters are ignored, so the code + * accepts both Windows-style and Unix-style line endings. + * + * - Each object begins with a banner that occurs at the start of + * a line; the first banner characters are "`-----BEGIN `" (five + * dashes, the word "BEGIN", and a space). The banner matching is + * not case-sensitive. + * + * - The _object name_ consists in the characters that follow the + * banner start sequence, up to the end of the line, but without + * trailing dashes (in "normal" PEM, there are five trailing + * dashes, but this implementation is not picky about these dashes). + * The BearSSL decoder normalises the name characters to uppercase + * (for ASCII letters only) and accepts names up to 127 characters. + * + * - The object ends with a banner that again occurs at the start of + * a line, and starts with "`-----END `" (again case-insensitive). + * + * - Between that start and end banner, only Base64 data shall occur. + * Base64 converts each sequence of three bytes into four + * characters; the four characters are ASCII letters, digits, "`+`" + * or "`-`" signs, and one or two "`=`" signs may occur in the last + * quartet. Whitespace is ignored (whitespace is any ASCII character + * of code 32 or less, so control characters are whitespace) and + * lines may have arbitrary length; the only restriction is that the + * four characters of a quartet must appear on the same line (no + * line break inside a quartet). + * + * - A single file may contain more than one PEM object. Bytes that + * occur between objects are ignored. + * + * + * ## PEM Decoder API + * + * The PEM decoder offers a state-machine API. The caller allocates a + * decoder context, then injects source bytes. Source bytes are pushed + * with `br_pem_decoder_push()`. The decoder stops accepting bytes when + * it reaches an "event", which is either the start of an object, the + * end of an object, or a decoding error within an object. + * + * The `br_pem_decoder_event()` function is used to obtain the current + * event; it also clears it, thus allowing the decoder to accept more + * bytes. When a object start event is raised, the decoder context + * offers the found object name (normalised to ASCII uppercase). + * + * When an object is reached, the caller must set an appropriate callback + * function, which will receive (by chunks) the decoded object data. + * + * Since the decoder context makes no dynamic allocation, it requires + * no explicit deallocation. + */ + +/** + * \brief PEM decoder context. + * + * Contents are opaque (they should not be accessed directly). + */ +typedef struct { +#ifndef BR_DOXYGEN_IGNORE + /* CPU for the T0 virtual machine. */ + struct { + uint32_t *dp; + uint32_t *rp; + const unsigned char *ip; + } cpu; + uint32_t dp_stack[32]; + uint32_t rp_stack[32]; + int err; + + const unsigned char *hbuf; + size_t hlen; + + void (*dest)(void *dest_ctx, const void *src, size_t len); + void *dest_ctx; + + unsigned char event; + char name[128]; + unsigned char buf[255]; + size_t ptr; +#endif +} br_pem_decoder_context; + +/** + * \brief Initialise a PEM decoder structure. + * + * \param ctx decoder context to initialise. + */ +void br_pem_decoder_init(br_pem_decoder_context *ctx); + +/** + * \brief Push some bytes into the decoder. + * + * Returned value is the number of bytes actually consumed; this may be + * less than the number of provided bytes if an event is raised. When an + * event is raised, it must be read (with `br_pem_decoder_event()`); + * until the event is read, this function will return 0. + * + * \param ctx decoder context. + * \param data new data bytes. + * \param len number of new data bytes. + * \return the number of bytes actually received (may be less than `len`). + */ +size_t br_pem_decoder_push(br_pem_decoder_context *ctx, + const void *data, size_t len); + +/** + * \brief Set the receiver for decoded data. + * + * When an object is entered, the provided function (with opaque context + * pointer) will be called repeatedly with successive chunks of decoded + * data for that object. If `dest` is set to 0, then decoded data is + * simply ignored. The receiver can be set at any time, but, in practice, + * it should be called immediately after receiving a "start of object" + * event. + * + * \param ctx decoder context. + * \param dest callback for receiving decoded data. + * \param dest_ctx opaque context pointer for the `dest` callback. + */ +static inline void +br_pem_decoder_setdest(br_pem_decoder_context *ctx, + void (*dest)(void *dest_ctx, const void *src, size_t len), + void *dest_ctx) +{ + ctx->dest = dest; + ctx->dest_ctx = dest_ctx; +} + +/** + * \brief Get the last event. + * + * If an event was raised, then this function returns the event value, and + * also clears it, thereby allowing the decoder to proceed. If no event + * was raised since the last call to `br_pem_decoder_event()`, then this + * function returns 0. + * + * \param ctx decoder context. + * \return the raised event, or 0. + */ +int br_pem_decoder_event(br_pem_decoder_context *ctx); + +/** + * \brief Event: start of object. + * + * This event is raised when the start of a new object has been detected. + * The object name (normalised to uppercase) can be accessed with + * `br_pem_decoder_name()`. + */ +#define BR_PEM_BEGIN_OBJ 1 + +/** + * \brief Event: end of object. + * + * This event is raised when the end of the current object is reached + * (normally, i.e. with no decoding error). + */ +#define BR_PEM_END_OBJ 2 + +/** + * \brief Event: decoding error. + * + * This event is raised when decoding fails within an object. + * This formally closes the current object and brings the decoder back + * to the "out of any object" state. The offending line in the source + * is consumed. + */ +#define BR_PEM_ERROR 3 + +/** + * \brief Get the name of the encountered object. + * + * The encountered object name is defined only when the "start of object" + * event is raised. That name is normalised to uppercase (for ASCII letters + * only) and does not include trailing dashes. + * + * \param ctx decoder context. + * \return the current object name. + */ +static inline const char * +br_pem_decoder_name(br_pem_decoder_context *ctx) +{ + return ctx->name; +} + +/** + * \brief Encode an object in PEM. + * + * This function encodes the provided binary object (`data`, of length `len` + * bytes) into PEM. The `banner` text will be included in the header and + * footer (e.g. use `"CERTIFICATE"` to get a `"BEGIN CERTIFICATE"` header). + * + * The length (in characters) of the PEM output is returned; that length + * does NOT include the terminating zero, that this function nevertheless + * adds. If using the returned value for allocation purposes, the allocated + * buffer size MUST be at least one byte larger than the returned size. + * + * If `dest` is `NULL`, then the encoding does not happen; however, the + * length of the encoded object is still computed and returned. + * + * The `data` pointer may be `NULL` only if `len` is zero (when encoding + * an object of length zero, which is not very useful), or when `dest` + * is `NULL` (in that case, source data bytes are ignored). + * + * Some `flags` can be specified to alter the encoding behaviour: + * + * - If `BR_PEM_LINE64` is set, then line-breaking will occur after + * every 64 characters of output, instead of the default of 76. + * + * - If `BR_PEM_CRLF` is set, then end-of-line sequence will use + * CR+LF instead of a single LF. + * + * The `data` and `dest` buffers may overlap, in which case the source + * binary data is destroyed in the process. Note that the PEM-encoded output + * is always larger than the source binary. + * + * \param dest the destination buffer (or `NULL`). + * \param data the source buffer (can be `NULL` in some cases). + * \param len the source length (in bytes). + * \param banner the PEM banner expression. + * \param flags the behavioural flags. + * \return the PEM object length (in characters), EXCLUDING the final zero. + */ +size_t br_pem_encode(void *dest, const void *data, size_t len, + const char *banner, unsigned flags); + +/** + * \brief PEM encoding flag: split lines at 64 characters. + */ +#define BR_PEM_LINE64 0x0001 + +/** + * \brief PEM encoding flag: use CR+LF line endings. + */ +#define BR_PEM_CRLF 0x0002 + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/bearssl-esp8266/src/t_bearssl_prf.h b/lib/bearssl-esp8266/src/t_bearssl_prf.h new file mode 100644 index 000000000..fdf608c85 --- /dev/null +++ b/lib/bearssl-esp8266/src/t_bearssl_prf.h @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef BR_BEARSSL_PRF_H__ +#define BR_BEARSSL_PRF_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** \file bearssl_prf.h + * + * # The TLS PRF + * + * The "PRF" is the pseudorandom function used internally during the + * SSL/TLS handshake, notably to expand negotiated shared secrets into + * the symmetric encryption keys that will be used to process the + * application data. + * + * TLS 1.0 and 1.1 define a PRF that is based on both MD5 and SHA-1. This + * is implemented by the `br_tls10_prf()` function. + * + * TLS 1.2 redefines the PRF, using an explicit hash function. The + * `br_tls12_sha256_prf()` and `br_tls12_sha384_prf()` functions apply that + * PRF with, respectively, SHA-256 and SHA-384. Most standard cipher suites + * rely on the SHA-256 based PRF, but some use SHA-384. + * + * The PRF always uses as input three parameters: a "secret" (some + * bytes), a "label" (ASCII string), and a "seed" (again some bytes). An + * arbitrary output length can be produced. The "seed" is provided as an + * arbitrary number of binary chunks, that gets internally concatenated. + */ + +/** + * \brief Type for a seed chunk. + * + * Each chunk may have an arbitrary length, and may be empty (no byte at + * all). If the chunk length is zero, then the pointer to the chunk data + * may be `NULL`. + */ +typedef struct { + /** + * \brief Pointer to the chunk data. + */ + const void *data; + + /** + * \brief Chunk length (in bytes). + */ + size_t len; +} br_tls_prf_seed_chunk; + +/** + * \brief PRF implementation for TLS 1.0 and 1.1. + * + * This PRF is the one specified by TLS 1.0 and 1.1. It internally uses + * MD5 and SHA-1. + * + * \param dst destination buffer. + * \param len output length (in bytes). + * \param secret secret value (key) for this computation. + * \param secret_len length of "secret" (in bytes). + * \param label PRF label (zero-terminated ASCII string). + * \param seed_num number of seed chunks. + * \param seed seed chnks for this computation (usually non-secret). + */ +void br_tls10_prf(void *dst, size_t len, + const void *secret, size_t secret_len, const char *label, + size_t seed_num, const br_tls_prf_seed_chunk *seed); + +/** + * \brief PRF implementation for TLS 1.2, with SHA-256. + * + * This PRF is the one specified by TLS 1.2, when the underlying hash + * function is SHA-256. + * + * \param dst destination buffer. + * \param len output length (in bytes). + * \param secret secret value (key) for this computation. + * \param secret_len length of "secret" (in bytes). + * \param label PRF label (zero-terminated ASCII string). + * \param seed_num number of seed chunks. + * \param seed seed chnks for this computation (usually non-secret). + */ +void br_tls12_sha256_prf(void *dst, size_t len, + const void *secret, size_t secret_len, const char *label, + size_t seed_num, const br_tls_prf_seed_chunk *seed); + +/** + * \brief PRF implementation for TLS 1.2, with SHA-384. + * + * This PRF is the one specified by TLS 1.2, when the underlying hash + * function is SHA-384. + * + * \param dst destination buffer. + * \param len output length (in bytes). + * \param secret secret value (key) for this computation. + * \param secret_len length of "secret" (in bytes). + * \param label PRF label (zero-terminated ASCII string). + * \param seed_num number of seed chunks. + * \param seed seed chnks for this computation (usually non-secret). + */ +void br_tls12_sha384_prf(void *dst, size_t len, + const void *secret, size_t secret_len, const char *label, + size_t seed_num, const br_tls_prf_seed_chunk *seed); + +/** + * brief A convenient type name for a PRF implementation. + * + * \param dst destination buffer. + * \param len output length (in bytes). + * \param secret secret value (key) for this computation. + * \param secret_len length of "secret" (in bytes). + * \param label PRF label (zero-terminated ASCII string). + * \param seed_num number of seed chunks. + * \param seed seed chnks for this computation (usually non-secret). + */ +typedef void (*br_tls_prf_impl)(void *dst, size_t len, + const void *secret, size_t secret_len, const char *label, + size_t seed_num, const br_tls_prf_seed_chunk *seed); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/bearssl-esp8266/src/t_bearssl_rand.h b/lib/bearssl-esp8266/src/t_bearssl_rand.h new file mode 100644 index 000000000..84dea93a7 --- /dev/null +++ b/lib/bearssl-esp8266/src/t_bearssl_rand.h @@ -0,0 +1,397 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef BR_BEARSSL_RAND_H__ +#define BR_BEARSSL_RAND_H__ + +#include +#include + +#include "t_bearssl_block.h" +#include "t_bearssl_hash.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** \file bearssl_rand.h + * + * # Pseudo-Random Generators + * + * A PRNG is a state-based engine that outputs pseudo-random bytes on + * demand. It is initialized with an initial seed, and additional seed + * bytes can be added afterwards. Bytes produced depend on the seeds and + * also on the exact sequence of calls (including sizes requested for + * each call). + * + * + * ## Procedural and OOP API + * + * For the PRNG of name "`xxx`", two API are provided. The _procedural_ + * API defined a context structure `br_xxx_context` and three functions: + * + * - `br_xxx_init()` + * + * Initialise the context with an initial seed. + * + * - `br_xxx_generate()` + * + * Produce some pseudo-random bytes. + * + * - `br_xxx_update()` + * + * Inject some additional seed. + * + * The initialisation function sets the first context field (`vtable`) + * to a pointer to the vtable that supports the OOP API. The OOP API + * provides access to the same functions through function pointers, + * named `init()`, `generate()` and `update()`. + * + * Note that the context initialisation method may accept additional + * parameters, provided as a 'const void *' pointer at API level. These + * additional parameters depend on the implemented PRNG. + * + * + * ## HMAC_DRBG + * + * HMAC_DRBG is defined in [NIST SP 800-90A Revision + * 1](http://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-90Ar1.pdf). + * It uses HMAC repeatedly, over some configurable underlying hash + * function. In BearSSL, it is implemented under the "`hmac_drbg`" name. + * The "extra parameters" pointer for context initialisation should be + * set to a pointer to the vtable for the underlying hash function (e.g. + * pointer to `br_sha256_vtable` to use HMAC_DRBG with SHA-256). + * + * According to the NIST standard, each request shall produce up to + * 219 bits (i.e. 64 kB of data); moreover, the context shall + * be reseeded at least once every 248 requests. This + * implementation does not maintain the reseed counter (the threshold is + * too high to be reached in practice) and does not object to producing + * more than 64 kB in a single request; thus, the code cannot fail, + * which corresponds to the fact that the API has no room for error + * codes. However, this implies that requesting more than 64 kB in one + * `generate()` request, or making more than 248 requests + * without reseeding, is formally out of NIST specification. There is + * no currently known security penalty for exceeding the NIST limits, + * and, in any case, HMAC_DRBG usage in implementing SSL/TLS always + * stays much below these thresholds. + * + * + * ## AESCTR_DRBG + * + * AESCTR_DRBG is a custom PRNG based on AES-128 in CTR mode. This is + * meant to be used only in situations where you are desperate for + * speed, and have an hardware-optimized AES/CTR implementation. Whether + * this will yield perceptible improvements depends on what you use the + * pseudorandom bytes for, and how many you want; for instance, RSA key + * pair generation uses a substantial amount of randomness, and using + * AESCTR_DRBG instead of HMAC_DRBG yields a 15 to 20% increase in key + * generation speed on a recent x86 CPU (Intel Core i7-6567U at 3.30 GHz). + * + * Internally, it uses CTR mode with successive counter values, starting + * at zero (counter value expressed over 128 bits, big-endian convention). + * The counter is not allowed to reach 32768; thus, every 32768*16 bytes + * at most, the `update()` function is run (on an empty seed, if none is + * provided). The `update()` function computes the new AES-128 key by + * applying a custom hash function to the concatenation of a state-dependent + * word (encryption of an all-one block with the current key) and the new + * seed. The custom hash function uses Hirose's construction over AES-256; + * see the comments in `aesctr_drbg.c` for details. + * + * This DRBG does not follow an existing standard, and thus should be + * considered as inadequate for production use until it has been properly + * analysed. + */ + +/** + * \brief Class type for PRNG implementations. + * + * A `br_prng_class` instance references the methods implementing a PRNG. + * Constant instances of this structure are defined for each implemented + * PRNG. Such instances are also called "vtables". + */ +typedef struct br_prng_class_ br_prng_class; +struct br_prng_class_ { + /** + * \brief Size (in bytes) of the context structure appropriate for + * running this PRNG. + */ + size_t context_size; + + /** + * \brief Initialisation method. + * + * The context to initialise is provided as a pointer to its + * first field (the vtable pointer); this function sets that + * first field to a pointer to the vtable. + * + * The extra parameters depend on the implementation; each + * implementation defines what kind of extra parameters it + * expects (if any). + * + * Requirements on the initial seed depend on the implemented + * PRNG. + * + * \param ctx PRNG context to initialise. + * \param params extra parameters for the PRNG. + * \param seed initial seed. + * \param seed_len initial seed length (in bytes). + */ + void (*init)(const br_prng_class **ctx, const void *params, + const void *seed, size_t seed_len); + + /** + * \brief Random bytes generation. + * + * This method produces `len` pseudorandom bytes, in the `out` + * buffer. The context is updated accordingly. + * + * \param ctx PRNG context. + * \param out output buffer. + * \param len number of pseudorandom bytes to produce. + */ + void (*generate)(const br_prng_class **ctx, void *out, size_t len); + + /** + * \brief Inject additional seed bytes. + * + * The provided seed bytes are added into the PRNG internal + * entropy pool. + * + * \param ctx PRNG context. + * \param seed additional seed. + * \param seed_len additional seed length (in bytes). + */ + void (*update)(const br_prng_class **ctx, + const void *seed, size_t seed_len); +}; + +/** + * \brief Context for HMAC_DRBG. + * + * The context contents are opaque, except the first field, which + * supports OOP. + */ +typedef struct { + /** + * \brief Pointer to the vtable. + * + * This field is set with the initialisation method/function. + */ + const br_prng_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + unsigned char K[64]; + unsigned char V[64]; + const br_hash_class *digest_class; +#endif +} br_hmac_drbg_context; + +/** + * \brief Statically allocated, constant vtable for HMAC_DRBG. + */ +extern const br_prng_class br_hmac_drbg_vtable; + +/** + * \brief HMAC_DRBG initialisation. + * + * The context to initialise is provided as a pointer to its first field + * (the vtable pointer); this function sets that first field to a + * pointer to the vtable. + * + * The `seed` value is what is called, in NIST terminology, the + * concatenation of the "seed", "nonce" and "personalization string", in + * that order. + * + * The `digest_class` parameter defines the underlying hash function. + * Formally, the NIST standard specifies that the hash function shall + * be only SHA-1 or one of the SHA-2 functions. This implementation also + * works with any other implemented hash function (such as MD5), but + * this is non-standard and therefore not recommended. + * + * \param ctx HMAC_DRBG context to initialise. + * \param digest_class vtable for the underlying hash function. + * \param seed initial seed. + * \param seed_len initial seed length (in bytes). + */ +void br_hmac_drbg_init(br_hmac_drbg_context *ctx, + const br_hash_class *digest_class, const void *seed, size_t seed_len); + +/** + * \brief Random bytes generation with HMAC_DRBG. + * + * This method produces `len` pseudorandom bytes, in the `out` + * buffer. The context is updated accordingly. Formally, requesting + * more than 65536 bytes in one request falls out of specification + * limits (but it won't fail). + * + * \param ctx HMAC_DRBG context. + * \param out output buffer. + * \param len number of pseudorandom bytes to produce. + */ +void br_hmac_drbg_generate(br_hmac_drbg_context *ctx, void *out, size_t len); + +/** + * \brief Inject additional seed bytes in HMAC_DRBG. + * + * The provided seed bytes are added into the HMAC_DRBG internal + * entropy pool. The process does not _replace_ existing entropy, + * thus pushing non-random bytes (i.e. bytes which are known to the + * attackers) does not degrade the overall quality of generated bytes. + * + * \param ctx HMAC_DRBG context. + * \param seed additional seed. + * \param seed_len additional seed length (in bytes). + */ +void br_hmac_drbg_update(br_hmac_drbg_context *ctx, + const void *seed, size_t seed_len); + +/** + * \brief Get the hash function implementation used by a given instance of + * HMAC_DRBG. + * + * This calls MUST NOT be performed on a context which was not + * previously initialised. + * + * \param ctx HMAC_DRBG context. + * \return the hash function vtable. + */ +static inline const br_hash_class * +br_hmac_drbg_get_hash(const br_hmac_drbg_context *ctx) +{ + return ctx->digest_class; +} + +/** + * \brief Type for a provider of entropy seeds. + * + * A "seeder" is a function that is able to obtain random values from + * some source and inject them as entropy seed in a PRNG. A seeder + * shall guarantee that the total entropy of the injected seed is large + * enough to seed a PRNG for purposes of cryptographic key generation + * (i.e. at least 128 bits). + * + * A seeder may report a failure to obtain adequate entropy. Seeders + * shall endeavour to fix themselves transient errors by trying again; + * thus, callers may consider reported errors as permanent. + * + * \param ctx PRNG context to seed. + * \return 1 on success, 0 on error. + */ +typedef int (*br_prng_seeder)(const br_prng_class **ctx); + +/** + * \brief Get a seeder backed by the operating system or hardware. + * + * Get a seeder that feeds on RNG facilities provided by the current + * operating system or hardware. If no such facility is known, then 0 + * is returned. + * + * If `name` is not `NULL`, then `*name` is set to a symbolic string + * that identifies the seeder implementation. If no seeder is returned + * and `name` is not `NULL`, then `*name` is set to a pointer to the + * constant string `"none"`. + * + * \param name receiver for seeder name, or `NULL`. + * \return the system seeder, if available, or 0. + */ +br_prng_seeder br_prng_seeder_system(const char **name); + +/** + * \brief Context for AESCTR_DRBG. + * + * The context contents are opaque, except the first field, which + * supports OOP. + */ +typedef struct { + /** + * \brief Pointer to the vtable. + * + * This field is set with the initialisation method/function. + */ + const br_prng_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + br_aes_gen_ctr_keys sk; + uint32_t cc; +#endif +} br_aesctr_drbg_context; + +/** + * \brief Statically allocated, constant vtable for AESCTR_DRBG. + */ +extern const br_prng_class br_aesctr_drbg_vtable; + +/** + * \brief AESCTR_DRBG initialisation. + * + * The context to initialise is provided as a pointer to its first field + * (the vtable pointer); this function sets that first field to a + * pointer to the vtable. + * + * The internal AES key is first set to the all-zero key; then, the + * `br_aesctr_drbg_update()` function is called with the provided `seed`. + * The call is performed even if the seed length (`seed_len`) is zero. + * + * The `aesctr` parameter defines the underlying AES/CTR implementation. + * + * \param ctx AESCTR_DRBG context to initialise. + * \param aesctr vtable for the AES/CTR implementation. + * \param seed initial seed (can be `NULL` if `seed_len` is zero). + * \param seed_len initial seed length (in bytes). + */ +void br_aesctr_drbg_init(br_aesctr_drbg_context *ctx, + const br_block_ctr_class *aesctr, const void *seed, size_t seed_len); + +/** + * \brief Random bytes generation with AESCTR_DRBG. + * + * This method produces `len` pseudorandom bytes, in the `out` + * buffer. The context is updated accordingly. + * + * \param ctx AESCTR_DRBG context. + * \param out output buffer. + * \param len number of pseudorandom bytes to produce. + */ +void br_aesctr_drbg_generate(br_aesctr_drbg_context *ctx, + void *out, size_t len); + +/** + * \brief Inject additional seed bytes in AESCTR_DRBG. + * + * The provided seed bytes are added into the AESCTR_DRBG internal + * entropy pool. The process does not _replace_ existing entropy, + * thus pushing non-random bytes (i.e. bytes which are known to the + * attackers) does not degrade the overall quality of generated bytes. + * + * \param ctx AESCTR_DRBG context. + * \param seed additional seed. + * \param seed_len additional seed length (in bytes). + */ +void br_aesctr_drbg_update(br_aesctr_drbg_context *ctx, + const void *seed, size_t seed_len); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/bearssl-esp8266/src/t_bearssl_rsa.h b/lib/bearssl-esp8266/src/t_bearssl_rsa.h new file mode 100644 index 000000000..c4f329ec2 --- /dev/null +++ b/lib/bearssl-esp8266/src/t_bearssl_rsa.h @@ -0,0 +1,1655 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef BR_BEARSSL_RSA_H__ +#define BR_BEARSSL_RSA_H__ + +#include +#include + +#include "t_bearssl_hash.h" +#include "t_bearssl_rand.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** \file bearssl_rsa.h + * + * # RSA + * + * This file documents the RSA implementations provided with BearSSL. + * Note that the SSL engine accesses these implementations through a + * configurable API, so it is possible to, for instance, run a SSL + * server which uses a RSA engine which is not based on this code. + * + * ## Key Elements + * + * RSA public and private keys consist in lists of big integers. All + * such integers are represented with big-endian unsigned notation: + * first byte is the most significant, and the value is positive (so + * there is no dedicated "sign bit"). Public and private key structures + * thus contain, for each such integer, a pointer to the first value byte + * (`unsigned char *`), and a length (`size_t`) which is the number of + * relevant bytes. As a general rule, minimal-length encoding is not + * enforced: values may have extra leading bytes of value 0. + * + * RSA public keys consist in two integers: + * + * - the modulus (`n`); + * - the public exponent (`e`). + * + * RSA private keys, as defined in + * [PKCS#1](https://tools.ietf.org/html/rfc3447), contain eight integers: + * + * - the modulus (`n`); + * - the public exponent (`e`); + * - the private exponent (`d`); + * - the first prime factor (`p`); + * - the second prime factor (`q`); + * - the first reduced exponent (`dp`, which is `d` modulo `p-1`); + * - the second reduced exponent (`dq`, which is `d` modulo `q-1`); + * - the CRT coefficient (`iq`, the inverse of `q` modulo `p`). + * + * However, the implementations defined in BearSSL use only five of + * these integers: `p`, `q`, `dp`, `dq` and `iq`. + * + * ## Security Features and Limitations + * + * The implementations contained in BearSSL have the following limitations + * and features: + * + * - They are constant-time. This means that the execution time and + * memory access pattern may depend on the _lengths_ of the private + * key components, but not on their value, nor on the value of + * the operand. Note that this property is not achieved through + * random masking, but "true" constant-time code. + * + * - They support only private keys with two prime factors. RSA private + * keys with three or more prime factors are nominally supported, but + * rarely used; they may offer faster operations, at the expense of + * more code and potentially a reduction in security if there are + * "too many" prime factors. + * + * - The public exponent may have arbitrary length. Of course, it is + * a good idea to keep public exponents small, so that public key + * operations are fast; but, contrary to some widely deployed + * implementations, BearSSL has no problem with public exponents + * longer than 32 bits. + * + * - The two prime factors of the modulus need not have the same length + * (but severely imbalanced factor lengths might reduce security). + * Similarly, there is no requirement that the first factor (`p`) + * be greater than the second factor (`q`). + * + * - Prime factors and modulus must be smaller than a compile-time limit. + * This is made necessary by the use of fixed-size stack buffers, and + * the limit has been adjusted to keep stack usage under 2 kB for the + * RSA operations. Currently, the maximum modulus size is 4096 bits, + * and the maximum prime factor size is 2080 bits. + * + * - The RSA functions themselves do not enforce lower size limits, + * except that which is absolutely necessary for the operation to + * mathematically make sense (e.g. a PKCS#1 v1.5 signature with + * SHA-1 requires a modulus of at least 361 bits). It is up to users + * of this code to enforce size limitations when appropriate (e.g. + * the X.509 validation engine, by default, rejects RSA keys of + * less than 1017 bits). + * + * - Within the size constraints expressed above, arbitrary bit lengths + * are supported. There is no requirement that prime factors or + * modulus have a size multiple of 8 or 16. + * + * - When verifying PKCS#1 v1.5 signatures, both variants of the hash + * function identifying header (with and without the ASN.1 NULL) are + * supported. When producing such signatures, the variant with the + * ASN.1 NULL is used. + * + * ## Implementations + * + * Three RSA implementations are included: + * + * - The **i32** implementation internally represents big integers + * as arrays of 32-bit integers. It is perfunctory and portable, + * but not very efficient. + * + * - The **i31** implementation uses 32-bit integers, each containing + * 31 bits worth of integer data. The i31 implementation is somewhat + * faster than the i32 implementation (the reduced integer size makes + * carry propagation easier) for a similar code footprint, but uses + * very slightly larger stack buffers (about 4% bigger). + * + * - The **i62** implementation is similar to the i31 implementation, + * except that it internally leverages the 64x64->128 multiplication + * opcode. This implementation is available only on architectures + * where such an opcode exists. It is much faster than i31. + * + * - The **i15** implementation uses 16-bit integers, each containing + * 15 bits worth of integer data. Multiplication results fit on + * 32 bits, so this won't use the "widening" multiplication routine + * on ARM Cortex M0/M0+, for much better performance and constant-time + * execution. + */ + +/** + * \brief RSA public key. + * + * The structure references the modulus and the public exponent. Both + * integers use unsigned big-endian representation; extra leading bytes + * of value 0 are allowed. + */ +typedef struct { + /** \brief Modulus. */ + unsigned char *n; + /** \brief Modulus length (in bytes). */ + size_t nlen; + /** \brief Public exponent. */ + unsigned char *e; + /** \brief Public exponent length (in bytes). */ + size_t elen; +} br_rsa_public_key; + +/** + * \brief RSA private key. + * + * The structure references the private factors, reduced private + * exponents, and CRT coefficient. It also contains the bit length of + * the modulus. The big integers use unsigned big-endian representation; + * extra leading bytes of value 0 are allowed. However, the modulus bit + * length (`n_bitlen`) MUST be exact. + */ +typedef struct { + /** \brief Modulus bit length (in bits, exact value). */ + uint32_t n_bitlen; + /** \brief First prime factor. */ + unsigned char *p; + /** \brief First prime factor length (in bytes). */ + size_t plen; + /** \brief Second prime factor. */ + unsigned char *q; + /** \brief Second prime factor length (in bytes). */ + size_t qlen; + /** \brief First reduced private exponent. */ + unsigned char *dp; + /** \brief First reduced private exponent length (in bytes). */ + size_t dplen; + /** \brief Second reduced private exponent. */ + unsigned char *dq; + /** \brief Second reduced private exponent length (in bytes). */ + size_t dqlen; + /** \brief CRT coefficient. */ + unsigned char *iq; + /** \brief CRT coefficient length (in bytes). */ + size_t iqlen; +} br_rsa_private_key; + +/** + * \brief Type for a RSA public key engine. + * + * The public key engine performs the modular exponentiation of the + * provided value with the public exponent. The value is modified in + * place. + * + * The value length (`xlen`) is verified to have _exactly_ the same + * length as the modulus (actual modulus length, without extra leading + * zeros in the modulus representation in memory). If the length does + * not match, then this function returns 0 and `x[]` is unmodified. + * + * It `xlen` is correct, then `x[]` is modified. Returned value is 1 + * on success, 0 on error. Error conditions include an oversized `x[]` + * (the array has the same length as the modulus, but the numerical value + * is not lower than the modulus) and an invalid modulus (e.g. an even + * integer). If an error is reported, then the new contents of `x[]` are + * unspecified. + * + * \param x operand to exponentiate. + * \param xlen length of the operand (in bytes). + * \param pk RSA public key. + * \return 1 on success, 0 on error. + */ +typedef uint32_t (*br_rsa_public)(unsigned char *x, size_t xlen, + const br_rsa_public_key *pk); + +/** + * \brief Type for a RSA signature verification engine (PKCS#1 v1.5). + * + * Parameters are: + * + * - The signature itself. The provided array is NOT modified. + * + * - The encoded OID for the hash function. The provided array must begin + * with a single byte that contains the length of the OID value (in + * bytes), followed by exactly that many bytes. This parameter may + * also be `NULL`, in which case the raw hash value should be used + * with the PKCS#1 v1.5 "type 1" padding (as used in SSL/TLS up + * to TLS-1.1, with a 36-byte hash value). + * + * - The hash output length, in bytes. + * + * - The public key. + * + * - An output buffer for the hash value. The caller must still compare + * it with the hash of the data over which the signature is computed. + * + * **Constraints:** + * + * - Hash length MUST be no more than 64 bytes. + * + * - OID value length MUST be no more than 32 bytes (i.e. `hash_oid[0]` + * must have a value in the 0..32 range, inclusive). + * + * This function verifies that the signature length (`xlen`) matches the + * modulus length (this function returns 0 on mismatch). If the modulus + * size exceeds the maximum supported RSA size, then the function also + * returns 0. + * + * Returned value is 1 on success, 0 on error. + * + * Implementations of this type need not be constant-time. + * + * \param x signature buffer. + * \param xlen signature length (in bytes). + * \param hash_oid encoded hash algorithm OID (or `NULL`). + * \param hash_len expected hash value length (in bytes). + * \param pk RSA public key. + * \param hash_out output buffer for the hash value. + * \return 1 on success, 0 on error. + */ +typedef uint32_t (*br_rsa_pkcs1_vrfy)(const unsigned char *x, size_t xlen, + const unsigned char *hash_oid, size_t hash_len, + const br_rsa_public_key *pk, unsigned char *hash_out); + +/** + * \brief Type for a RSA signature verification engine (PSS). + * + * Parameters are: + * + * - The signature itself. The provided array is NOT modified. + * + * - The hash function which was used to hash the message. + * + * - The hash function to use with MGF1 within the PSS padding. This + * is not necessarily the same hash function as the one which was + * used to hash the signed message. + * + * - The hashed message (as an array of bytes). + * + * - The PSS salt length (in bytes). + * + * - The public key. + * + * **Constraints:** + * + * - Hash message length MUST be no more than 64 bytes. + * + * Note that, contrary to PKCS#1 v1.5 signature, the hash value of the + * signed data cannot be extracted from the signature; it must be + * provided to the verification function. + * + * This function verifies that the signature length (`xlen`) matches the + * modulus length (this function returns 0 on mismatch). If the modulus + * size exceeds the maximum supported RSA size, then the function also + * returns 0. + * + * Returned value is 1 on success, 0 on error. + * + * Implementations of this type need not be constant-time. + * + * \param x signature buffer. + * \param xlen signature length (in bytes). + * \param hf_data hash function applied on the message. + * \param hf_mgf1 hash function to use with MGF1. + * \param hash hash value of the signed message. + * \param salt_len PSS salt length (in bytes). + * \param pk RSA public key. + * \return 1 on success, 0 on error. + */ +typedef uint32_t (*br_rsa_pss_vrfy)(const unsigned char *x, size_t xlen, + const br_hash_class *hf_data, const br_hash_class *hf_mgf1, + const void *hash, size_t salt_len, const br_rsa_public_key *pk); + +/** + * \brief Type for a RSA encryption engine (OAEP). + * + * Parameters are: + * + * - A source of random bytes. The source must be already initialized. + * + * - A hash function, used internally with the mask generation function + * (MGF1). + * + * - A label. The `label` pointer may be `NULL` if `label_len` is zero + * (an empty label, which is the default in PKCS#1 v2.2). + * + * - The public key. + * + * - The destination buffer. Its maximum length (in bytes) is provided; + * if that length is lower than the public key length, then an error + * is reported. + * + * - The source message. + * + * The encrypted message output has exactly the same length as the modulus + * (mathematical length, in bytes, not counting extra leading zeros in the + * modulus representation in the public key). + * + * The source message (`src`, length `src_len`) may overlap with the + * destination buffer (`dst`, length `dst_max_len`). + * + * This function returns the actual encrypted message length, in bytes; + * on error, zero is returned. An error is reported if the output buffer + * is not large enough, or the public is invalid, or the public key + * modulus exceeds the maximum supported RSA size. + * + * \param rnd source of random bytes. + * \param dig hash function to use with MGF1. + * \param label label value (may be `NULL` if `label_len` is zero). + * \param label_len label length, in bytes. + * \param pk RSA public key. + * \param dst destination buffer. + * \param dst_max_len destination buffer length (maximum encrypted data size). + * \param src message to encrypt. + * \param src_len source message length (in bytes). + * \return encrypted message length (in bytes), or 0 on error. + */ +typedef size_t (*br_rsa_oaep_encrypt)( + const br_prng_class **rnd, const br_hash_class *dig, + const void *label, size_t label_len, + const br_rsa_public_key *pk, + void *dst, size_t dst_max_len, + const void *src, size_t src_len); + +/** + * \brief Type for a RSA private key engine. + * + * The `x[]` buffer is modified in place, and its length is inferred from + * the modulus length (`x[]` is assumed to have a length of + * `(sk->n_bitlen+7)/8` bytes). + * + * Returned value is 1 on success, 0 on error. + * + * \param x operand to exponentiate. + * \param sk RSA private key. + * \return 1 on success, 0 on error. + */ +typedef uint32_t (*br_rsa_private)(unsigned char *x, + const br_rsa_private_key *sk); + +/** + * \brief Type for a RSA signature generation engine (PKCS#1 v1.5). + * + * Parameters are: + * + * - The encoded OID for the hash function. The provided array must begin + * with a single byte that contains the length of the OID value (in + * bytes), followed by exactly that many bytes. This parameter may + * also be `NULL`, in which case the raw hash value should be used + * with the PKCS#1 v1.5 "type 1" padding (as used in SSL/TLS up + * to TLS-1.1, with a 36-byte hash value). + * + * - The hash value computes over the data to sign (its length is + * expressed in bytes). + * + * - The RSA private key. + * + * - The output buffer, that receives the signature. + * + * Returned value is 1 on success, 0 on error. Error conditions include + * a too small modulus for the provided hash OID and value, or some + * invalid key parameters. The signature length is exactly + * `(sk->n_bitlen+7)/8` bytes. + * + * This function is expected to be constant-time with regards to the + * private key bytes (lengths of the modulus and the individual factors + * may leak, though) and to the hashed data. + * + * \param hash_oid encoded hash algorithm OID (or `NULL`). + * \param hash hash value. + * \param hash_len hash value length (in bytes). + * \param sk RSA private key. + * \param x output buffer for the signature value. + * \return 1 on success, 0 on error. + */ +typedef uint32_t (*br_rsa_pkcs1_sign)(const unsigned char *hash_oid, + const unsigned char *hash, size_t hash_len, + const br_rsa_private_key *sk, unsigned char *x); + +/** + * \brief Type for a RSA signature generation engine (PSS). + * + * Parameters are: + * + * - An initialized PRNG for salt generation. If the salt length is + * zero (`salt_len` parameter), then the PRNG is optional (this is + * not the typical case, as the security proof of RSA/PSS is + * tighter when a non-empty salt is used). + * + * - The hash function which was used to hash the message. + * + * - The hash function to use with MGF1 within the PSS padding. This + * is not necessarily the same function as the one used to hash the + * message. + * + * - The hashed message. + * + * - The salt length, in bytes. + * + * - The RSA private key. + * + * - The output buffer, that receives the signature. + * + * Returned value is 1 on success, 0 on error. Error conditions include + * a too small modulus for the provided hash and salt lengths, or some + * invalid key parameters. The signature length is exactly + * `(sk->n_bitlen+7)/8` bytes. + * + * This function is expected to be constant-time with regards to the + * private key bytes (lengths of the modulus and the individual factors + * may leak, though) and to the hashed data. + * + * \param rng PRNG for salt generation (`NULL` if `salt_len` is zero). + * \param hf_data hash function used to hash the signed data. + * \param hf_mgf1 hash function to use with MGF1. + * \param hash hashed message. + * \param salt_len salt length (in bytes). + * \param sk RSA private key. + * \param x output buffer for the signature value. + * \return 1 on success, 0 on error. + */ +typedef uint32_t (*br_rsa_pss_sign)(const br_prng_class **rng, + const br_hash_class *hf_data, const br_hash_class *hf_mgf1, + const unsigned char *hash_value, size_t salt_len, + const br_rsa_private_key *sk, unsigned char *x); + +/** + * \brief Encoded OID for SHA-1 (in RSA PKCS#1 signatures). + */ +#define BR_HASH_OID_SHA1 \ + ((const unsigned char *)"\x05\x2B\x0E\x03\x02\x1A") + +/** + * \brief Encoded OID for SHA-224 (in RSA PKCS#1 signatures). + */ +#define BR_HASH_OID_SHA224 \ + ((const unsigned char *)"\x09\x60\x86\x48\x01\x65\x03\x04\x02\x04") + +/** + * \brief Encoded OID for SHA-256 (in RSA PKCS#1 signatures). + */ +#define BR_HASH_OID_SHA256 \ + ((const unsigned char *)"\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01") + +/** + * \brief Encoded OID for SHA-384 (in RSA PKCS#1 signatures). + */ +#define BR_HASH_OID_SHA384 \ + ((const unsigned char *)"\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02") + +/** + * \brief Encoded OID for SHA-512 (in RSA PKCS#1 signatures). + */ +#define BR_HASH_OID_SHA512 \ + ((const unsigned char *)"\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03") + +/** + * \brief Type for a RSA decryption engine (OAEP). + * + * Parameters are: + * + * - A hash function, used internally with the mask generation function + * (MGF1). + * + * - A label. The `label` pointer may be `NULL` if `label_len` is zero + * (an empty label, which is the default in PKCS#1 v2.2). + * + * - The private key. + * + * - The source and destination buffer. The buffer initially contains + * the encrypted message; the buffer contents are altered, and the + * decrypted message is written at the start of that buffer + * (decrypted message is always shorter than the encrypted message). + * + * If decryption fails in any way, then `*len` is unmodified, and the + * function returns 0. Otherwise, `*len` is set to the decrypted message + * length, and 1 is returned. The implementation is responsible for + * checking that the input message length matches the key modulus length, + * and that the padding is correct. + * + * Implementations MUST use constant-time check of the validity of the + * OAEP padding, at least until the leading byte and hash value have + * been checked. Whether overall decryption worked, and the length of + * the decrypted message, may leak. + * + * \param dig hash function to use with MGF1. + * \param label label value (may be `NULL` if `label_len` is zero). + * \param label_len label length, in bytes. + * \param sk RSA private key. + * \param data input/output buffer. + * \param len encrypted/decrypted message length. + * \return 1 on success, 0 on error. + */ +typedef uint32_t (*br_rsa_oaep_decrypt)( + const br_hash_class *dig, const void *label, size_t label_len, + const br_rsa_private_key *sk, void *data, size_t *len); + +/* + * RSA "i32" engine. Integers are internally represented as arrays of + * 32-bit integers, and the core multiplication primitive is the + * 32x32->64 multiplication. + */ + +/** + * \brief RSA public key engine "i32". + * + * \see br_rsa_public + * + * \param x operand to exponentiate. + * \param xlen length of the operand (in bytes). + * \param pk RSA public key. + * \return 1 on success, 0 on error. + */ +uint32_t br_rsa_i32_public(unsigned char *x, size_t xlen, + const br_rsa_public_key *pk); + +/** + * \brief RSA signature verification engine "i32" (PKCS#1 v1.5 signatures). + * + * \see br_rsa_pkcs1_vrfy + * + * \param x signature buffer. + * \param xlen signature length (in bytes). + * \param hash_oid encoded hash algorithm OID (or `NULL`). + * \param hash_len expected hash value length (in bytes). + * \param pk RSA public key. + * \param hash_out output buffer for the hash value. + * \return 1 on success, 0 on error. + */ +uint32_t br_rsa_i32_pkcs1_vrfy(const unsigned char *x, size_t xlen, + const unsigned char *hash_oid, size_t hash_len, + const br_rsa_public_key *pk, unsigned char *hash_out); + +/** + * \brief RSA signature verification engine "i32" (PSS signatures). + * + * \see br_rsa_pss_vrfy + * + * \param x signature buffer. + * \param xlen signature length (in bytes). + * \param hf_data hash function applied on the message. + * \param hf_mgf1 hash function to use with MGF1. + * \param hash hash value of the signed message. + * \param salt_len PSS salt length (in bytes). + * \param pk RSA public key. + * \return 1 on success, 0 on error. + */ +uint32_t br_rsa_i32_pss_vrfy(const unsigned char *x, size_t xlen, + const br_hash_class *hf_data, const br_hash_class *hf_mgf1, + const void *hash, size_t salt_len, const br_rsa_public_key *pk); + +/** + * \brief RSA private key engine "i32". + * + * \see br_rsa_private + * + * \param x operand to exponentiate. + * \param sk RSA private key. + * \return 1 on success, 0 on error. + */ +uint32_t br_rsa_i32_private(unsigned char *x, + const br_rsa_private_key *sk); + +/** + * \brief RSA signature generation engine "i32" (PKCS#1 v1.5 signatures). + * + * \see br_rsa_pkcs1_sign + * + * \param hash_oid encoded hash algorithm OID (or `NULL`). + * \param hash hash value. + * \param hash_len hash value length (in bytes). + * \param sk RSA private key. + * \param x output buffer for the hash value. + * \return 1 on success, 0 on error. + */ +uint32_t br_rsa_i32_pkcs1_sign(const unsigned char *hash_oid, + const unsigned char *hash, size_t hash_len, + const br_rsa_private_key *sk, unsigned char *x); + +/** + * \brief RSA signature generation engine "i32" (PSS signatures). + * + * \see br_rsa_pss_sign + * + * \param rng PRNG for salt generation (`NULL` if `salt_len` is zero). + * \param hf_data hash function used to hash the signed data. + * \param hf_mgf1 hash function to use with MGF1. + * \param hash hashed message. + * \param salt_len salt length (in bytes). + * \param sk RSA private key. + * \param x output buffer for the signature value. + * \return 1 on success, 0 on error. + */ +uint32_t br_rsa_i32_pss_sign(const br_prng_class **rng, + const br_hash_class *hf_data, const br_hash_class *hf_mgf1, + const unsigned char *hash_value, size_t salt_len, + const br_rsa_private_key *sk, unsigned char *x); + +/* + * RSA "i31" engine. Similar to i32, but only 31 bits are used per 32-bit + * word. This uses slightly more stack space (about 4% more) and code + * space, but it quite faster. + */ + +/** + * \brief RSA public key engine "i31". + * + * \see br_rsa_public + * + * \param x operand to exponentiate. + * \param xlen length of the operand (in bytes). + * \param pk RSA public key. + * \return 1 on success, 0 on error. + */ +uint32_t br_rsa_i31_public(unsigned char *x, size_t xlen, + const br_rsa_public_key *pk); + +/** + * \brief RSA signature verification engine "i31" (PKCS#1 v1.5 signatures). + * + * \see br_rsa_pkcs1_vrfy + * + * \param x signature buffer. + * \param xlen signature length (in bytes). + * \param hash_oid encoded hash algorithm OID (or `NULL`). + * \param hash_len expected hash value length (in bytes). + * \param pk RSA public key. + * \param hash_out output buffer for the hash value. + * \return 1 on success, 0 on error. + */ +uint32_t br_rsa_i31_pkcs1_vrfy(const unsigned char *x, size_t xlen, + const unsigned char *hash_oid, size_t hash_len, + const br_rsa_public_key *pk, unsigned char *hash_out); + +/** + * \brief RSA signature verification engine "i31" (PSS signatures). + * + * \see br_rsa_pss_vrfy + * + * \param x signature buffer. + * \param xlen signature length (in bytes). + * \param hf_data hash function applied on the message. + * \param hf_mgf1 hash function to use with MGF1. + * \param hash hash value of the signed message. + * \param salt_len PSS salt length (in bytes). + * \param pk RSA public key. + * \return 1 on success, 0 on error. + */ +uint32_t br_rsa_i31_pss_vrfy(const unsigned char *x, size_t xlen, + const br_hash_class *hf_data, const br_hash_class *hf_mgf1, + const void *hash, size_t salt_len, const br_rsa_public_key *pk); + +/** + * \brief RSA private key engine "i31". + * + * \see br_rsa_private + * + * \param x operand to exponentiate. + * \param sk RSA private key. + * \return 1 on success, 0 on error. + */ +uint32_t br_rsa_i31_private(unsigned char *x, + const br_rsa_private_key *sk); + +/** + * \brief RSA signature generation engine "i31" (PKCS#1 v1.5 signatures). + * + * \see br_rsa_pkcs1_sign + * + * \param hash_oid encoded hash algorithm OID (or `NULL`). + * \param hash hash value. + * \param hash_len hash value length (in bytes). + * \param sk RSA private key. + * \param x output buffer for the hash value. + * \return 1 on success, 0 on error. + */ +uint32_t br_rsa_i31_pkcs1_sign(const unsigned char *hash_oid, + const unsigned char *hash, size_t hash_len, + const br_rsa_private_key *sk, unsigned char *x); + +/** + * \brief RSA signature generation engine "i31" (PSS signatures). + * + * \see br_rsa_pss_sign + * + * \param rng PRNG for salt generation (`NULL` if `salt_len` is zero). + * \param hf_data hash function used to hash the signed data. + * \param hf_mgf1 hash function to use with MGF1. + * \param hash hashed message. + * \param salt_len salt length (in bytes). + * \param sk RSA private key. + * \param x output buffer for the signature value. + * \return 1 on success, 0 on error. + */ +uint32_t br_rsa_i31_pss_sign(const br_prng_class **rng, + const br_hash_class *hf_data, const br_hash_class *hf_mgf1, + const unsigned char *hash_value, size_t salt_len, + const br_rsa_private_key *sk, unsigned char *x); + +/* + * RSA "i62" engine. Similar to i31, but internal multiplication use + * 64x64->128 multiplications. This is available only on architecture + * that offer such an opcode. + */ + +/** + * \brief RSA public key engine "i62". + * + * This function is defined only on architecture that offer a 64x64->128 + * opcode. Use `br_rsa_i62_public_get()` to dynamically obtain a pointer + * to that function. + * + * \see br_rsa_public + * + * \param x operand to exponentiate. + * \param xlen length of the operand (in bytes). + * \param pk RSA public key. + * \return 1 on success, 0 on error. + */ +uint32_t br_rsa_i62_public(unsigned char *x, size_t xlen, + const br_rsa_public_key *pk); + +/** + * \brief RSA signature verification engine "i62" (PKCS#1 v1.5 signatures). + * + * This function is defined only on architecture that offer a 64x64->128 + * opcode. Use `br_rsa_i62_pkcs1_vrfy_get()` to dynamically obtain a pointer + * to that function. + * + * \see br_rsa_pkcs1_vrfy + * + * \param x signature buffer. + * \param xlen signature length (in bytes). + * \param hash_oid encoded hash algorithm OID (or `NULL`). + * \param hash_len expected hash value length (in bytes). + * \param pk RSA public key. + * \param hash_out output buffer for the hash value. + * \return 1 on success, 0 on error. + */ +uint32_t br_rsa_i62_pkcs1_vrfy(const unsigned char *x, size_t xlen, + const unsigned char *hash_oid, size_t hash_len, + const br_rsa_public_key *pk, unsigned char *hash_out); + +/** + * \brief RSA signature verification engine "i62" (PSS signatures). + * + * This function is defined only on architecture that offer a 64x64->128 + * opcode. Use `br_rsa_i62_pss_vrfy_get()` to dynamically obtain a pointer + * to that function. + * + * \see br_rsa_pss_vrfy + * + * \param x signature buffer. + * \param xlen signature length (in bytes). + * \param hf_data hash function applied on the message. + * \param hf_mgf1 hash function to use with MGF1. + * \param hash hash value of the signed message. + * \param salt_len PSS salt length (in bytes). + * \param pk RSA public key. + * \return 1 on success, 0 on error. + */ +uint32_t br_rsa_i62_pss_vrfy(const unsigned char *x, size_t xlen, + const br_hash_class *hf_data, const br_hash_class *hf_mgf1, + const void *hash, size_t salt_len, const br_rsa_public_key *pk); + +/** + * \brief RSA private key engine "i62". + * + * This function is defined only on architecture that offer a 64x64->128 + * opcode. Use `br_rsa_i62_private_get()` to dynamically obtain a pointer + * to that function. + * + * \see br_rsa_private + * + * \param x operand to exponentiate. + * \param sk RSA private key. + * \return 1 on success, 0 on error. + */ +uint32_t br_rsa_i62_private(unsigned char *x, + const br_rsa_private_key *sk); + +/** + * \brief RSA signature generation engine "i62" (PKCS#1 v1.5 signatures). + * + * This function is defined only on architecture that offer a 64x64->128 + * opcode. Use `br_rsa_i62_pkcs1_sign_get()` to dynamically obtain a pointer + * to that function. + * + * \see br_rsa_pkcs1_sign + * + * \param hash_oid encoded hash algorithm OID (or `NULL`). + * \param hash hash value. + * \param hash_len hash value length (in bytes). + * \param sk RSA private key. + * \param x output buffer for the hash value. + * \return 1 on success, 0 on error. + */ +uint32_t br_rsa_i62_pkcs1_sign(const unsigned char *hash_oid, + const unsigned char *hash, size_t hash_len, + const br_rsa_private_key *sk, unsigned char *x); + +/** + * \brief RSA signature generation engine "i62" (PSS signatures). + * + * This function is defined only on architecture that offer a 64x64->128 + * opcode. Use `br_rsa_i62_pss_sign_get()` to dynamically obtain a pointer + * to that function. + * + * \see br_rsa_pss_sign + * + * \param rng PRNG for salt generation (`NULL` if `salt_len` is zero). + * \param hf_data hash function used to hash the signed data. + * \param hf_mgf1 hash function to use with MGF1. + * \param hash hashed message. + * \param salt_len salt length (in bytes). + * \param sk RSA private key. + * \param x output buffer for the signature value. + * \return 1 on success, 0 on error. + */ +uint32_t br_rsa_i62_pss_sign(const br_prng_class **rng, + const br_hash_class *hf_data, const br_hash_class *hf_mgf1, + const unsigned char *hash_value, size_t salt_len, + const br_rsa_private_key *sk, unsigned char *x); + +/** + * \brief Get the RSA "i62" implementation (public key operations), + * if available. + * + * \return the implementation, or 0. + */ +br_rsa_public br_rsa_i62_public_get(void); + +/** + * \brief Get the RSA "i62" implementation (PKCS#1 v1.5 signature verification), + * if available. + * + * \return the implementation, or 0. + */ +br_rsa_pkcs1_vrfy br_rsa_i62_pkcs1_vrfy_get(void); + +/** + * \brief Get the RSA "i62" implementation (PSS signature verification), + * if available. + * + * \return the implementation, or 0. + */ +br_rsa_pss_vrfy br_rsa_i62_pss_vrfy_get(void); + +/** + * \brief Get the RSA "i62" implementation (private key operations), + * if available. + * + * \return the implementation, or 0. + */ +br_rsa_private br_rsa_i62_private_get(void); + +/** + * \brief Get the RSA "i62" implementation (PKCS#1 v1.5 signature generation), + * if available. + * + * \return the implementation, or 0. + */ +br_rsa_pkcs1_sign br_rsa_i62_pkcs1_sign_get(void); + +/** + * \brief Get the RSA "i62" implementation (PSS signature generation), + * if available. + * + * \return the implementation, or 0. + */ +br_rsa_pss_sign br_rsa_i62_pss_sign_get(void); + +/** + * \brief Get the RSA "i62" implementation (OAEP encryption), + * if available. + * + * \return the implementation, or 0. + */ +br_rsa_oaep_encrypt br_rsa_i62_oaep_encrypt_get(void); + +/** + * \brief Get the RSA "i62" implementation (OAEP decryption), + * if available. + * + * \return the implementation, or 0. + */ +br_rsa_oaep_decrypt br_rsa_i62_oaep_decrypt_get(void); + +/* + * RSA "i15" engine. Integers are represented as 15-bit integers, so + * the code uses only 32-bit multiplication (no 64-bit result), which + * is vastly faster (and constant-time) on the ARM Cortex M0/M0+. + */ + +/** + * \brief RSA public key engine "i15". + * + * \see br_rsa_public + * + * \param x operand to exponentiate. + * \param xlen length of the operand (in bytes). + * \param pk RSA public key. + * \return 1 on success, 0 on error. + */ +uint32_t br_rsa_i15_public(unsigned char *x, size_t xlen, + const br_rsa_public_key *pk); + +/** + * \brief RSA signature verification engine "i15" (PKCS#1 v1.5 signatures). + * + * \see br_rsa_pkcs1_vrfy + * + * \param x signature buffer. + * \param xlen signature length (in bytes). + * \param hash_oid encoded hash algorithm OID (or `NULL`). + * \param hash_len expected hash value length (in bytes). + * \param pk RSA public key. + * \param hash_out output buffer for the hash value. + * \return 1 on success, 0 on error. + */ +uint32_t br_rsa_i15_pkcs1_vrfy(const unsigned char *x, size_t xlen, + const unsigned char *hash_oid, size_t hash_len, + const br_rsa_public_key *pk, unsigned char *hash_out); + +/** + * \brief RSA signature verification engine "i15" (PSS signatures). + * + * \see br_rsa_pss_vrfy + * + * \param x signature buffer. + * \param xlen signature length (in bytes). + * \param hf_data hash function applied on the message. + * \param hf_mgf1 hash function to use with MGF1. + * \param hash hash value of the signed message. + * \param salt_len PSS salt length (in bytes). + * \param pk RSA public key. + * \return 1 on success, 0 on error. + */ +uint32_t br_rsa_i15_pss_vrfy(const unsigned char *x, size_t xlen, + const br_hash_class *hf_data, const br_hash_class *hf_mgf1, + const void *hash, size_t salt_len, const br_rsa_public_key *pk); + +/** + * \brief RSA private key engine "i15". + * + * \see br_rsa_private + * + * \param x operand to exponentiate. + * \param sk RSA private key. + * \return 1 on success, 0 on error. + */ +uint32_t br_rsa_i15_private(unsigned char *x, + const br_rsa_private_key *sk); + +/** + * \brief RSA signature generation engine "i15" (PKCS#1 v1.5 signatures). + * + * \see br_rsa_pkcs1_sign + * + * \param hash_oid encoded hash algorithm OID (or `NULL`). + * \param hash hash value. + * \param hash_len hash value length (in bytes). + * \param sk RSA private key. + * \param x output buffer for the hash value. + * \return 1 on success, 0 on error. + */ +uint32_t br_rsa_i15_pkcs1_sign(const unsigned char *hash_oid, + const unsigned char *hash, size_t hash_len, + const br_rsa_private_key *sk, unsigned char *x); + +/** + * \brief RSA signature generation engine "i15" (PSS signatures). + * + * \see br_rsa_pss_sign + * + * \param rng PRNG for salt generation (`NULL` if `salt_len` is zero). + * \param hf_data hash function used to hash the signed data. + * \param hf_mgf1 hash function to use with MGF1. + * \param hash hashed message. + * \param salt_len salt length (in bytes). + * \param sk RSA private key. + * \param x output buffer for the signature value. + * \return 1 on success, 0 on error. + */ +uint32_t br_rsa_i15_pss_sign(const br_prng_class **rng, + const br_hash_class *hf_data, const br_hash_class *hf_mgf1, + const unsigned char *hash_value, size_t salt_len, + const br_rsa_private_key *sk, unsigned char *x); + +/** + * \brief Get "default" RSA implementation (public-key operations). + * + * This returns the preferred implementation of RSA (public-key operations) + * on the current system. + * + * \return the default implementation. + */ +br_rsa_public br_rsa_public_get_default(void); + +/** + * \brief Get "default" RSA implementation (private-key operations). + * + * This returns the preferred implementation of RSA (private-key operations) + * on the current system. + * + * \return the default implementation. + */ +br_rsa_private br_rsa_private_get_default(void); + +/** + * \brief Get "default" RSA implementation (PKCS#1 v1.5 signature verification). + * + * This returns the preferred implementation of RSA (signature verification) + * on the current system. + * + * \return the default implementation. + */ +br_rsa_pkcs1_vrfy br_rsa_pkcs1_vrfy_get_default(void); + +/** + * \brief Get "default" RSA implementation (PSS signature verification). + * + * This returns the preferred implementation of RSA (signature verification) + * on the current system. + * + * \return the default implementation. + */ +br_rsa_pss_vrfy br_rsa_pss_vrfy_get_default(void); + +/** + * \brief Get "default" RSA implementation (PKCS#1 v1.5 signature generation). + * + * This returns the preferred implementation of RSA (signature generation) + * on the current system. + * + * \return the default implementation. + */ +br_rsa_pkcs1_sign br_rsa_pkcs1_sign_get_default(void); + +/** + * \brief Get "default" RSA implementation (PSS signature generation). + * + * This returns the preferred implementation of RSA (signature generation) + * on the current system. + * + * \return the default implementation. + */ +br_rsa_pss_sign br_rsa_pss_sign_get_default(void); + +/** + * \brief Get "default" RSA implementation (OAEP encryption). + * + * This returns the preferred implementation of RSA (OAEP encryption) + * on the current system. + * + * \return the default implementation. + */ +br_rsa_oaep_encrypt br_rsa_oaep_encrypt_get_default(void); + +/** + * \brief Get "default" RSA implementation (OAEP decryption). + * + * This returns the preferred implementation of RSA (OAEP decryption) + * on the current system. + * + * \return the default implementation. + */ +br_rsa_oaep_decrypt br_rsa_oaep_decrypt_get_default(void); + +/** + * \brief RSA decryption helper, for SSL/TLS. + * + * This function performs the RSA decryption for a RSA-based key exchange + * in a SSL/TLS server. The provided RSA engine is used. The `data` + * parameter points to the value to decrypt, of length `len` bytes. On + * success, the 48-byte pre-master secret is copied into `data`, starting + * at the first byte of that buffer; on error, the contents of `data` + * become indeterminate. + * + * This function first checks that the provided value length (`len`) is + * not lower than 59 bytes, and matches the RSA modulus length; if neither + * of this property is met, then this function returns 0 and the buffer + * is unmodified. + * + * Otherwise, decryption and then padding verification are performed, both + * in constant-time. A decryption error, or a bad padding, or an + * incorrect decrypted value length are reported with a returned value of + * 0; on success, 1 is returned. The caller (SSL server engine) is supposed + * to proceed with a random pre-master secret in case of error. + * + * \param core RSA private key engine. + * \param sk RSA private key. + * \param data input/output buffer. + * \param len length (in bytes) of the data to decrypt. + * \return 1 on success, 0 on error. + */ +uint32_t br_rsa_ssl_decrypt(br_rsa_private core, const br_rsa_private_key *sk, + unsigned char *data, size_t len); + +/** + * \brief RSA encryption (OAEP) with the "i15" engine. + * + * \see br_rsa_oaep_encrypt + * + * \param rnd source of random bytes. + * \param dig hash function to use with MGF1. + * \param label label value (may be `NULL` if `label_len` is zero). + * \param label_len label length, in bytes. + * \param pk RSA public key. + * \param dst destination buffer. + * \param dst_max_len destination buffer length (maximum encrypted data size). + * \param src message to encrypt. + * \param src_len source message length (in bytes). + * \return encrypted message length (in bytes), or 0 on error. + */ +size_t br_rsa_i15_oaep_encrypt( + const br_prng_class **rnd, const br_hash_class *dig, + const void *label, size_t label_len, + const br_rsa_public_key *pk, + void *dst, size_t dst_max_len, + const void *src, size_t src_len); + +/** + * \brief RSA decryption (OAEP) with the "i15" engine. + * + * \see br_rsa_oaep_decrypt + * + * \param dig hash function to use with MGF1. + * \param label label value (may be `NULL` if `label_len` is zero). + * \param label_len label length, in bytes. + * \param sk RSA private key. + * \param data input/output buffer. + * \param len encrypted/decrypted message length. + * \return 1 on success, 0 on error. + */ +uint32_t br_rsa_i15_oaep_decrypt( + const br_hash_class *dig, const void *label, size_t label_len, + const br_rsa_private_key *sk, void *data, size_t *len); + +/** + * \brief RSA encryption (OAEP) with the "i31" engine. + * + * \see br_rsa_oaep_encrypt + * + * \param rnd source of random bytes. + * \param dig hash function to use with MGF1. + * \param label label value (may be `NULL` if `label_len` is zero). + * \param label_len label length, in bytes. + * \param pk RSA public key. + * \param dst destination buffer. + * \param dst_max_len destination buffer length (maximum encrypted data size). + * \param src message to encrypt. + * \param src_len source message length (in bytes). + * \return encrypted message length (in bytes), or 0 on error. + */ +size_t br_rsa_i31_oaep_encrypt( + const br_prng_class **rnd, const br_hash_class *dig, + const void *label, size_t label_len, + const br_rsa_public_key *pk, + void *dst, size_t dst_max_len, + const void *src, size_t src_len); + +/** + * \brief RSA decryption (OAEP) with the "i31" engine. + * + * \see br_rsa_oaep_decrypt + * + * \param dig hash function to use with MGF1. + * \param label label value (may be `NULL` if `label_len` is zero). + * \param label_len label length, in bytes. + * \param sk RSA private key. + * \param data input/output buffer. + * \param len encrypted/decrypted message length. + * \return 1 on success, 0 on error. + */ +uint32_t br_rsa_i31_oaep_decrypt( + const br_hash_class *dig, const void *label, size_t label_len, + const br_rsa_private_key *sk, void *data, size_t *len); + +/** + * \brief RSA encryption (OAEP) with the "i32" engine. + * + * \see br_rsa_oaep_encrypt + * + * \param rnd source of random bytes. + * \param dig hash function to use with MGF1. + * \param label label value (may be `NULL` if `label_len` is zero). + * \param label_len label length, in bytes. + * \param pk RSA public key. + * \param dst destination buffer. + * \param dst_max_len destination buffer length (maximum encrypted data size). + * \param src message to encrypt. + * \param src_len source message length (in bytes). + * \return encrypted message length (in bytes), or 0 on error. + */ +size_t br_rsa_i32_oaep_encrypt( + const br_prng_class **rnd, const br_hash_class *dig, + const void *label, size_t label_len, + const br_rsa_public_key *pk, + void *dst, size_t dst_max_len, + const void *src, size_t src_len); + +/** + * \brief RSA decryption (OAEP) with the "i32" engine. + * + * \see br_rsa_oaep_decrypt + * + * \param dig hash function to use with MGF1. + * \param label label value (may be `NULL` if `label_len` is zero). + * \param label_len label length, in bytes. + * \param sk RSA private key. + * \param data input/output buffer. + * \param len encrypted/decrypted message length. + * \return 1 on success, 0 on error. + */ +uint32_t br_rsa_i32_oaep_decrypt( + const br_hash_class *dig, const void *label, size_t label_len, + const br_rsa_private_key *sk, void *data, size_t *len); + +/** + * \brief RSA encryption (OAEP) with the "i62" engine. + * + * This function is defined only on architecture that offer a 64x64->128 + * opcode. Use `br_rsa_i62_oaep_encrypt_get()` to dynamically obtain a pointer + * to that function. + * + * \see br_rsa_oaep_encrypt + * + * \param rnd source of random bytes. + * \param dig hash function to use with MGF1. + * \param label label value (may be `NULL` if `label_len` is zero). + * \param label_len label length, in bytes. + * \param pk RSA public key. + * \param dst destination buffer. + * \param dst_max_len destination buffer length (maximum encrypted data size). + * \param src message to encrypt. + * \param src_len source message length (in bytes). + * \return encrypted message length (in bytes), or 0 on error. + */ +size_t br_rsa_i62_oaep_encrypt( + const br_prng_class **rnd, const br_hash_class *dig, + const void *label, size_t label_len, + const br_rsa_public_key *pk, + void *dst, size_t dst_max_len, + const void *src, size_t src_len); + +/** + * \brief RSA decryption (OAEP) with the "i62" engine. + * + * This function is defined only on architecture that offer a 64x64->128 + * opcode. Use `br_rsa_i62_oaep_decrypt_get()` to dynamically obtain a pointer + * to that function. + * + * \see br_rsa_oaep_decrypt + * + * \param dig hash function to use with MGF1. + * \param label label value (may be `NULL` if `label_len` is zero). + * \param label_len label length, in bytes. + * \param sk RSA private key. + * \param data input/output buffer. + * \param len encrypted/decrypted message length. + * \return 1 on success, 0 on error. + */ +uint32_t br_rsa_i62_oaep_decrypt( + const br_hash_class *dig, const void *label, size_t label_len, + const br_rsa_private_key *sk, void *data, size_t *len); + +/** + * \brief Get buffer size to hold RSA private key elements. + * + * This macro returns the length (in bytes) of the buffer needed to + * receive the elements of a RSA private key, as generated by one of + * the `br_rsa_*_keygen()` functions. If the provided size is a constant + * expression, then the whole macro evaluates to a constant expression. + * + * \param size target key size (modulus size, in bits) + * \return the length of the private key buffer, in bytes. + */ +#define BR_RSA_KBUF_PRIV_SIZE(size) (5 * (((size) + 15) >> 4)) + +/** + * \brief Get buffer size to hold RSA public key elements. + * + * This macro returns the length (in bytes) of the buffer needed to + * receive the elements of a RSA public key, as generated by one of + * the `br_rsa_*_keygen()` functions. If the provided size is a constant + * expression, then the whole macro evaluates to a constant expression. + * + * \param size target key size (modulus size, in bits) + * \return the length of the public key buffer, in bytes. + */ +#define BR_RSA_KBUF_PUB_SIZE(size) (4 + (((size) + 7) >> 3)) + +/** + * \brief Type for RSA key pair generator implementation. + * + * This function generates a new RSA key pair whose modulus has bit + * length `size` bits. The private key elements are written in the + * `kbuf_priv` buffer, and pointer values and length fields to these + * elements are populated in the provided private key structure `sk`. + * Similarly, the public key elements are written in `kbuf_pub`, with + * pointers and lengths set in `pk`. + * + * If `pk` is `NULL`, then `kbuf_pub` may be `NULL`, and only the + * private key is set. + * + * If `pubexp` is not zero, then its value will be used as public + * exponent. Valid RSA public exponent values are odd integers + * greater than 1. If `pubexp` is zero, then the public exponent will + * have value 3. + * + * The provided PRNG (`rng_ctx`) must have already been initialized + * and seeded. + * + * Returned value is 1 on success, 0 on error. An error is reported + * if the requested range is outside of the supported key sizes, or + * if an invalid non-zero public exponent value is provided. Supported + * range starts at 512 bits, and up to an implementation-defined + * maximum (by default 4096 bits). Note that key sizes up to 768 bits + * have been broken in practice, and sizes lower than 2048 bits are + * usually considered to be weak and should not be used. + * + * \param rng_ctx source PRNG context (already initialized) + * \param sk RSA private key structure (destination) + * \param kbuf_priv buffer for private key elements + * \param pk RSA public key structure (destination), or `NULL` + * \param kbuf_pub buffer for public key elements, or `NULL` + * \param size target RSA modulus size (in bits) + * \param pubexp public exponent to use, or zero + * \return 1 on success, 0 on error (invalid parameters) + */ +typedef uint32_t (*br_rsa_keygen)( + const br_prng_class **rng_ctx, + br_rsa_private_key *sk, void *kbuf_priv, + br_rsa_public_key *pk, void *kbuf_pub, + unsigned size, uint32_t pubexp); + +/** + * \brief RSA key pair generation with the "i15" engine. + * + * \see br_rsa_keygen + * + * \param rng_ctx source PRNG context (already initialized) + * \param sk RSA private key structure (destination) + * \param kbuf_priv buffer for private key elements + * \param pk RSA public key structure (destination), or `NULL` + * \param kbuf_pub buffer for public key elements, or `NULL` + * \param size target RSA modulus size (in bits) + * \param pubexp public exponent to use, or zero + * \return 1 on success, 0 on error (invalid parameters) + */ +uint32_t br_rsa_i15_keygen( + const br_prng_class **rng_ctx, + br_rsa_private_key *sk, void *kbuf_priv, + br_rsa_public_key *pk, void *kbuf_pub, + unsigned size, uint32_t pubexp); + +/** + * \brief RSA key pair generation with the "i31" engine. + * + * \see br_rsa_keygen + * + * \param rng_ctx source PRNG context (already initialized) + * \param sk RSA private key structure (destination) + * \param kbuf_priv buffer for private key elements + * \param pk RSA public key structure (destination), or `NULL` + * \param kbuf_pub buffer for public key elements, or `NULL` + * \param size target RSA modulus size (in bits) + * \param pubexp public exponent to use, or zero + * \return 1 on success, 0 on error (invalid parameters) + */ +uint32_t br_rsa_i31_keygen( + const br_prng_class **rng_ctx, + br_rsa_private_key *sk, void *kbuf_priv, + br_rsa_public_key *pk, void *kbuf_pub, + unsigned size, uint32_t pubexp); + +/** + * \brief RSA key pair generation with the "i62" engine. + * + * This function is defined only on architecture that offer a 64x64->128 + * opcode. Use `br_rsa_i62_keygen_get()` to dynamically obtain a pointer + * to that function. + * + * \see br_rsa_keygen + * + * \param rng_ctx source PRNG context (already initialized) + * \param sk RSA private key structure (destination) + * \param kbuf_priv buffer for private key elements + * \param pk RSA public key structure (destination), or `NULL` + * \param kbuf_pub buffer for public key elements, or `NULL` + * \param size target RSA modulus size (in bits) + * \param pubexp public exponent to use, or zero + * \return 1 on success, 0 on error (invalid parameters) + */ +uint32_t br_rsa_i62_keygen( + const br_prng_class **rng_ctx, + br_rsa_private_key *sk, void *kbuf_priv, + br_rsa_public_key *pk, void *kbuf_pub, + unsigned size, uint32_t pubexp); + +/** + * \brief Get the RSA "i62" implementation (key pair generation), + * if available. + * + * \return the implementation, or 0. + */ +br_rsa_keygen br_rsa_i62_keygen_get(void); + +/** + * \brief Get "default" RSA implementation (key pair generation). + * + * This returns the preferred implementation of RSA (key pair generation) + * on the current system. + * + * \return the default implementation. + */ +br_rsa_keygen br_rsa_keygen_get_default(void); + +/** + * \brief Type for a modulus computing function. + * + * Such a function computes the public modulus from the private key. The + * encoded modulus (unsigned big-endian) is written on `n`, and the size + * (in bytes) is returned. If `n` is `NULL`, then the size is returned but + * the modulus itself is not computed. + * + * If the key size exceeds an internal limit, 0 is returned. + * + * \param n destination buffer (or `NULL`). + * \param sk RSA private key. + * \return the modulus length (in bytes), or 0. + */ +typedef size_t (*br_rsa_compute_modulus)(void *n, const br_rsa_private_key *sk); + +/** + * \brief Recompute RSA modulus ("i15" engine). + * + * \see br_rsa_compute_modulus + * + * \param n destination buffer (or `NULL`). + * \param sk RSA private key. + * \return the modulus length (in bytes), or 0. + */ +size_t br_rsa_i15_compute_modulus(void *n, const br_rsa_private_key *sk); + +/** + * \brief Recompute RSA modulus ("i31" engine). + * + * \see br_rsa_compute_modulus + * + * \param n destination buffer (or `NULL`). + * \param sk RSA private key. + * \return the modulus length (in bytes), or 0. + */ +size_t br_rsa_i31_compute_modulus(void *n, const br_rsa_private_key *sk); + +/** + * \brief Get "default" RSA implementation (recompute modulus). + * + * This returns the preferred implementation of RSA (recompute modulus) + * on the current system. + * + * \return the default implementation. + */ +br_rsa_compute_modulus br_rsa_compute_modulus_get_default(void); + +/** + * \brief Type for a public exponent computing function. + * + * Such a function recomputes the public exponent from the private key. + * 0 is returned if any of the following occurs: + * + * - Either `p` or `q` is not equal to 3 modulo 4. + * + * - The public exponent does not fit on 32 bits. + * + * - An internal limit is exceeded. + * + * - The private key is invalid in some way. + * + * For all private keys produced by the key generator functions + * (`br_rsa_keygen` type), this function succeeds and returns the true + * public exponent. The public exponent is always an odd integer greater + * than 1. + * + * \return the public exponent, or 0. + */ +typedef uint32_t (*br_rsa_compute_pubexp)(const br_rsa_private_key *sk); + +/** + * \brief Recompute RSA public exponent ("i15" engine). + * + * \see br_rsa_compute_pubexp + * + * \return the public exponent, or 0. + */ +uint32_t br_rsa_i15_compute_pubexp(const br_rsa_private_key *sk); + +/** + * \brief Recompute RSA public exponent ("i31" engine). + * + * \see br_rsa_compute_pubexp + * + * \return the public exponent, or 0. + */ +uint32_t br_rsa_i31_compute_pubexp(const br_rsa_private_key *sk); + +/** + * \brief Get "default" RSA implementation (recompute public exponent). + * + * This returns the preferred implementation of RSA (recompute public + * exponent) on the current system. + * + * \return the default implementation. + */ +br_rsa_compute_pubexp br_rsa_compute_pubexp_get_default(void); + +/** + * \brief Type for a private exponent computing function. + * + * An RSA private key (`br_rsa_private_key`) contains two reduced + * private exponents, which are sufficient to perform private key + * operations. However, standard encoding formats for RSA private keys + * require also a copy of the complete private exponent (non-reduced), + * which this function recomputes. + * + * This function suceeds if all the following conditions hold: + * + * - Both private factors `p` and `q` are equal to 3 modulo 4. + * + * - The provided public exponent `pubexp` is correct, and, in particular, + * is odd, relatively prime to `p-1` and `q-1`, and greater than 1. + * + * - No internal storage limit is exceeded. + * + * For all private keys produced by the key generator functions + * (`br_rsa_keygen` type), this function succeeds. Note that the API + * restricts the public exponent to a maximum size of 32 bits. + * + * The encoded private exponent is written in `d` (unsigned big-endian + * convention), and the length (in bytes) is returned. If `d` is `NULL`, + * then the exponent is not written anywhere, but the length is still + * returned. On error, 0 is returned. + * + * Not all error conditions are detected when `d` is `NULL`; therefore, the + * returned value shall be checked also when actually producing the value. + * + * \param d destination buffer (or `NULL`). + * \param sk RSA private key. + * \param pubexp the public exponent. + * \return the private exponent length (in bytes), or 0. + */ +typedef size_t (*br_rsa_compute_privexp)(void *d, + const br_rsa_private_key *sk, uint32_t pubexp); + +/** + * \brief Recompute RSA private exponent ("i15" engine). + * + * \see br_rsa_compute_privexp + * + * \param d destination buffer (or `NULL`). + * \param sk RSA private key. + * \param pubexp the public exponent. + * \return the private exponent length (in bytes), or 0. + */ +size_t br_rsa_i15_compute_privexp(void *d, + const br_rsa_private_key *sk, uint32_t pubexp); + +/** + * \brief Recompute RSA private exponent ("i31" engine). + * + * \see br_rsa_compute_privexp + * + * \param d destination buffer (or `NULL`). + * \param sk RSA private key. + * \param pubexp the public exponent. + * \return the private exponent length (in bytes), or 0. + */ +size_t br_rsa_i31_compute_privexp(void *d, + const br_rsa_private_key *sk, uint32_t pubexp); + +/** + * \brief Get "default" RSA implementation (recompute private exponent). + * + * This returns the preferred implementation of RSA (recompute private + * exponent) on the current system. + * + * \return the default implementation. + */ +br_rsa_compute_privexp br_rsa_compute_privexp_get_default(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/bearssl-esp8266/src/t_bearssl_ssl.h b/lib/bearssl-esp8266/src/t_bearssl_ssl.h new file mode 100644 index 000000000..8cd42d9f4 --- /dev/null +++ b/lib/bearssl-esp8266/src/t_bearssl_ssl.h @@ -0,0 +1,4308 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef BR_BEARSSL_SSL_H__ +#define BR_BEARSSL_SSL_H__ + +#include +#include + +#include "t_bearssl_block.h" +#include "t_bearssl_hash.h" +#include "t_bearssl_hmac.h" +#include "t_bearssl_prf.h" +#include "t_bearssl_rand.h" +#include "t_bearssl_x509.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** \file bearssl_ssl.h + * + * # SSL + * + * For an overview of the SSL/TLS API, see [the BearSSL Web + * site](https://www.bearssl.org/api1.html). + * + * The `BR_TLS_*` constants correspond to the standard cipher suites and + * their values in the [IANA + * registry](http://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-4). + * + * The `BR_ALERT_*` constants are for standard TLS alert messages. When + * a fatal alert message is sent of received, then the SSL engine context + * status is set to the sum of that alert value (an integer in the 0..255 + * range) and a fixed offset (`BR_ERR_SEND_FATAL_ALERT` for a sent alert, + * `BR_ERR_RECV_FATAL_ALERT` for a received alert). + */ + +/** \brief Optimal input buffer size. */ +#define BR_SSL_BUFSIZE_INPUT (16384 + 325) + +/** \brief Optimal output buffer size. */ +#define BR_SSL_BUFSIZE_OUTPUT (16384 + 85) + +/** \brief Optimal buffer size for monodirectional engine + (shared input/output buffer). */ +#define BR_SSL_BUFSIZE_MONO BR_SSL_BUFSIZE_INPUT + +/** \brief Optimal buffer size for bidirectional engine + (single buffer split into two separate input/output buffers). */ +#define BR_SSL_BUFSIZE_BIDI (BR_SSL_BUFSIZE_INPUT + BR_SSL_BUFSIZE_OUTPUT) + +/* + * Constants for known SSL/TLS protocol versions (SSL 3.0, TLS 1.0, TLS 1.1 + * and TLS 1.2). Note that though there is a constant for SSL 3.0, that + * protocol version is not actually supported. + */ + +/** \brief Protocol version: SSL 3.0 (unsupported). */ +#define BR_SSL30 0x0300 +/** \brief Protocol version: TLS 1.0. */ +#define BR_TLS10 0x0301 +/** \brief Protocol version: TLS 1.1. */ +#define BR_TLS11 0x0302 +/** \brief Protocol version: TLS 1.2. */ +#define BR_TLS12 0x0303 + +/* + * Error constants. They are used to report the reason why a context has + * been marked as failed. + * + * Implementation note: SSL-level error codes should be in the 1..31 + * range. The 32..63 range is for certificate decoding and validation + * errors. Received fatal alerts imply an error code in the 256..511 range. + */ + +/** \brief SSL status: no error so far (0). */ +#define BR_ERR_OK 0 + +/** \brief SSL status: caller-provided parameter is incorrect. */ +#define BR_ERR_BAD_PARAM 1 + +/** \brief SSL status: operation requested by the caller cannot be applied + with the current context state (e.g. reading data while outgoing data + is waiting to be sent). */ +#define BR_ERR_BAD_STATE 2 + +/** \brief SSL status: incoming protocol or record version is unsupported. */ +#define BR_ERR_UNSUPPORTED_VERSION 3 + +/** \brief SSL status: incoming record version does not match the expected + version. */ +#define BR_ERR_BAD_VERSION 4 + +/** \brief SSL status: incoming record length is invalid. */ +#define BR_ERR_BAD_LENGTH 5 + +/** \brief SSL status: incoming record is too large to be processed, or + buffer is too small for the handshake message to send. */ +#define BR_ERR_TOO_LARGE 6 + +/** \brief SSL status: decryption found an invalid padding, or the record + MAC is not correct. */ +#define BR_ERR_BAD_MAC 7 + +/** \brief SSL status: no initial entropy was provided, and none can be + obtained from the OS. */ +#define BR_ERR_NO_RANDOM 8 + +/** \brief SSL status: incoming record type is unknown. */ +#define BR_ERR_UNKNOWN_TYPE 9 + +/** \brief SSL status: incoming record or message has wrong type with + regards to the current engine state. */ +#define BR_ERR_UNEXPECTED 10 + +/** \brief SSL status: ChangeCipherSpec message from the peer has invalid + contents. */ +#define BR_ERR_BAD_CCS 12 + +/** \brief SSL status: alert message from the peer has invalid contents + (odd length). */ +#define BR_ERR_BAD_ALERT 13 + +/** \brief SSL status: incoming handshake message decoding failed. */ +#define BR_ERR_BAD_HANDSHAKE 14 + +/** \brief SSL status: ServerHello contains a session ID which is larger + than 32 bytes. */ +#define BR_ERR_OVERSIZED_ID 15 + +/** \brief SSL status: server wants to use a cipher suite that we did + not claim to support. This is also reported if we tried to advertise + a cipher suite that we do not support. */ +#define BR_ERR_BAD_CIPHER_SUITE 16 + +/** \brief SSL status: server wants to use a compression that we did not + claim to support. */ +#define BR_ERR_BAD_COMPRESSION 17 + +/** \brief SSL status: server's max fragment length does not match + client's. */ +#define BR_ERR_BAD_FRAGLEN 18 + +/** \brief SSL status: secure renegotiation failed. */ +#define BR_ERR_BAD_SECRENEG 19 + +/** \brief SSL status: server sent an extension type that we did not + announce, or used the same extension type several times in a single + ServerHello. */ +#define BR_ERR_EXTRA_EXTENSION 20 + +/** \brief SSL status: invalid Server Name Indication contents (when + used by the server, this extension shall be empty). */ +#define BR_ERR_BAD_SNI 21 + +/** \brief SSL status: invalid ServerHelloDone from the server (length + is not 0). */ +#define BR_ERR_BAD_HELLO_DONE 22 + +/** \brief SSL status: internal limit exceeded (e.g. server's public key + is too large). */ +#define BR_ERR_LIMIT_EXCEEDED 23 + +/** \brief SSL status: Finished message from peer does not match the + expected value. */ +#define BR_ERR_BAD_FINISHED 24 + +/** \brief SSL status: session resumption attempt with distinct version + or cipher suite. */ +#define BR_ERR_RESUME_MISMATCH 25 + +/** \brief SSL status: unsupported or invalid algorithm (ECDHE curve, + signature algorithm, hash function). */ +#define BR_ERR_INVALID_ALGORITHM 26 + +/** \brief SSL status: invalid signature (on ServerKeyExchange from + server, or in CertificateVerify from client). */ +#define BR_ERR_BAD_SIGNATURE 27 + +/** \brief SSL status: peer's public key does not have the proper type + or is not allowed for requested operation. */ +#define BR_ERR_WRONG_KEY_USAGE 28 + +/** \brief SSL status: client did not send a certificate upon request, + or the client certificate could not be validated. */ +#define BR_ERR_NO_CLIENT_AUTH 29 + +/** \brief SSL status: I/O error or premature close on underlying + transport stream. This error code is set only by the simplified + I/O API ("br_sslio_*"). */ +#define BR_ERR_IO 31 + +/** \brief SSL status: base value for a received fatal alert. + + When a fatal alert is received from the peer, the alert value + is added to this constant. */ +#define BR_ERR_RECV_FATAL_ALERT 256 + +/** \brief SSL status: base value for a sent fatal alert. + + When a fatal alert is sent to the peer, the alert value is added + to this constant. */ +#define BR_ERR_SEND_FATAL_ALERT 512 + +/* ===================================================================== */ + +/** + * \brief Decryption engine for SSL. + * + * When processing incoming records, the SSL engine will use a decryption + * engine that uses a specific context structure, and has a set of + * methods (a vtable) that follows this template. + * + * The decryption engine is responsible for applying decryption, verifying + * MAC, and keeping track of the record sequence number. + */ +typedef struct br_sslrec_in_class_ br_sslrec_in_class; +struct br_sslrec_in_class_ { + /** + * \brief Context size (in bytes). + */ + size_t context_size; + + /** + * \brief Test validity of the incoming record length. + * + * This function returns 1 if the announced length for an + * incoming record is valid, 0 otherwise, + * + * \param ctx decryption engine context. + * \param record_len incoming record length. + * \return 1 of a valid length, 0 otherwise. + */ + int (*check_length)(const br_sslrec_in_class *const *ctx, + size_t record_len); + + /** + * \brief Decrypt the incoming record. + * + * This function may assume that the record length is valid + * (it has been previously tested with `check_length()`). + * Decryption is done in place; `*len` is updated with the + * cleartext length, and the address of the first plaintext + * byte is returned. If the record is correct but empty, then + * `*len` is set to 0 and a non-`NULL` pointer is returned. + * + * On decryption/MAC error, `NULL` is returned. + * + * \param ctx decryption engine context. + * \param record_type record type (23 for application data, etc). + * \param version record version. + * \param payload address of encrypted payload. + * \param len pointer to payload length (updated). + * \return pointer to plaintext, or `NULL` on error. + */ + unsigned char *(*decrypt)(const br_sslrec_in_class **ctx, + int record_type, unsigned version, + void *payload, size_t *len); +}; + +/** + * \brief Encryption engine for SSL. + * + * When building outgoing records, the SSL engine will use an encryption + * engine that uses a specific context structure, and has a set of + * methods (a vtable) that follows this template. + * + * The encryption engine is responsible for applying encryption and MAC, + * and keeping track of the record sequence number. + */ +typedef struct br_sslrec_out_class_ br_sslrec_out_class; +struct br_sslrec_out_class_ { + /** + * \brief Context size (in bytes). + */ + size_t context_size; + + /** + * \brief Compute maximum plaintext sizes and offsets. + * + * When this function is called, the `*start` and `*end` + * values contain offsets designating the free area in the + * outgoing buffer for plaintext data; that free area is + * preceded by a 5-byte space which will receive the record + * header. + * + * The `max_plaintext()` function is responsible for adjusting + * both `*start` and `*end` to make room for any record-specific + * header, MAC, padding, and possible split. + * + * \param ctx encryption engine context. + * \param start pointer to start of plaintext offset (updated). + * \param end pointer to start of plaintext offset (updated). + */ + void (*max_plaintext)(const br_sslrec_out_class *const *ctx, + size_t *start, size_t *end); + + /** + * \brief Perform record encryption. + * + * This function encrypts the record. The plaintext address and + * length are provided. Returned value is the start of the + * encrypted record (or sequence of records, if a split was + * performed), _including_ the 5-byte header, and `*len` is + * adjusted to the total size of the record(s), there again + * including the header(s). + * + * \param ctx decryption engine context. + * \param record_type record type (23 for application data, etc). + * \param version record version. + * \param plaintext address of plaintext. + * \param len pointer to plaintext length (updated). + * \return pointer to start of built record. + */ + unsigned char *(*encrypt)(const br_sslrec_out_class **ctx, + int record_type, unsigned version, + void *plaintext, size_t *len); +}; + +/** + * \brief Context for a no-encryption engine. + * + * The no-encryption engine processes outgoing records during the initial + * handshake, before encryption is applied. + */ +typedef struct { + /** \brief No-encryption engine vtable. */ + const br_sslrec_out_class *vtable; +} br_sslrec_out_clear_context; + +/** \brief Static, constant vtable for the no-encryption engine. */ +extern const br_sslrec_out_class br_sslrec_out_clear_vtable; + +/* ===================================================================== */ + +/** + * \brief Record decryption engine class, for CBC mode. + * + * This class type extends the decryption engine class with an + * initialisation method that receives the parameters needed + * for CBC processing: block cipher implementation, block cipher key, + * HMAC parameters (hash function, key, MAC length), and IV. If the + * IV is `NULL`, then a per-record IV will be used (TLS 1.1+). + */ +typedef struct br_sslrec_in_cbc_class_ br_sslrec_in_cbc_class; +struct br_sslrec_in_cbc_class_ { + /** + * \brief Superclass, as first vtable field. + */ + br_sslrec_in_class inner; + + /** + * \brief Engine initialisation method. + * + * This method sets the vtable field in the context. + * + * \param ctx context to initialise. + * \param bc_impl block cipher implementation (CBC decryption). + * \param bc_key block cipher key. + * \param bc_key_len block cipher key length (in bytes). + * \param dig_impl hash function for HMAC. + * \param mac_key HMAC key. + * \param mac_key_len HMAC key length (in bytes). + * \param mac_out_len HMAC output length (in bytes). + * \param iv initial IV (or `NULL`). + */ + void (*init)(const br_sslrec_in_cbc_class **ctx, + const br_block_cbcdec_class *bc_impl, + const void *bc_key, size_t bc_key_len, + const br_hash_class *dig_impl, + const void *mac_key, size_t mac_key_len, size_t mac_out_len, + const void *iv); +}; + +/** + * \brief Record encryption engine class, for CBC mode. + * + * This class type extends the encryption engine class with an + * initialisation method that receives the parameters needed + * for CBC processing: block cipher implementation, block cipher key, + * HMAC parameters (hash function, key, MAC length), and IV. If the + * IV is `NULL`, then a per-record IV will be used (TLS 1.1+). + */ +typedef struct br_sslrec_out_cbc_class_ br_sslrec_out_cbc_class; +struct br_sslrec_out_cbc_class_ { + /** + * \brief Superclass, as first vtable field. + */ + br_sslrec_out_class inner; + + /** + * \brief Engine initialisation method. + * + * This method sets the vtable field in the context. + * + * \param ctx context to initialise. + * \param bc_impl block cipher implementation (CBC encryption). + * \param bc_key block cipher key. + * \param bc_key_len block cipher key length (in bytes). + * \param dig_impl hash function for HMAC. + * \param mac_key HMAC key. + * \param mac_key_len HMAC key length (in bytes). + * \param mac_out_len HMAC output length (in bytes). + * \param iv initial IV (or `NULL`). + */ + void (*init)(const br_sslrec_out_cbc_class **ctx, + const br_block_cbcenc_class *bc_impl, + const void *bc_key, size_t bc_key_len, + const br_hash_class *dig_impl, + const void *mac_key, size_t mac_key_len, size_t mac_out_len, + const void *iv); +}; + +/** + * \brief Context structure for decrypting incoming records with + * CBC + HMAC. + * + * The first field points to the vtable. The other fields are opaque + * and shall not be accessed directly. + */ +typedef struct { + /** \brief Pointer to vtable. */ + const br_sslrec_in_cbc_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + uint64_t seq; + union { + const br_block_cbcdec_class *vtable; + br_aes_gen_cbcdec_keys aes; + br_des_gen_cbcdec_keys des; + } bc; + br_hmac_key_context mac; + size_t mac_len; + unsigned char iv[16]; + int explicit_IV; +#endif +} br_sslrec_in_cbc_context; + +/** + * \brief Static, constant vtable for record decryption with CBC. + */ +extern const br_sslrec_in_cbc_class br_sslrec_in_cbc_vtable; + +/** + * \brief Context structure for encrypting outgoing records with + * CBC + HMAC. + * + * The first field points to the vtable. The other fields are opaque + * and shall not be accessed directly. + */ +typedef struct { + /** \brief Pointer to vtable. */ + const br_sslrec_out_cbc_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + uint64_t seq; + union { + const br_block_cbcenc_class *vtable; + br_aes_gen_cbcenc_keys aes; + br_des_gen_cbcenc_keys des; + } bc; + br_hmac_key_context mac; + size_t mac_len; + unsigned char iv[16]; + int explicit_IV; +#endif +} br_sslrec_out_cbc_context; + +/** + * \brief Static, constant vtable for record encryption with CBC. + */ +extern const br_sslrec_out_cbc_class br_sslrec_out_cbc_vtable; + +/* ===================================================================== */ + +/** + * \brief Record decryption engine class, for GCM mode. + * + * This class type extends the decryption engine class with an + * initialisation method that receives the parameters needed + * for GCM processing: block cipher implementation, block cipher key, + * GHASH implementation, and 4-byte IV. + */ +typedef struct br_sslrec_in_gcm_class_ br_sslrec_in_gcm_class; +struct br_sslrec_in_gcm_class_ { + /** + * \brief Superclass, as first vtable field. + */ + br_sslrec_in_class inner; + + /** + * \brief Engine initialisation method. + * + * This method sets the vtable field in the context. + * + * \param ctx context to initialise. + * \param bc_impl block cipher implementation (CTR). + * \param key block cipher key. + * \param key_len block cipher key length (in bytes). + * \param gh_impl GHASH implementation. + * \param iv static IV (4 bytes). + */ + void (*init)(const br_sslrec_in_gcm_class **ctx, + const br_block_ctr_class *bc_impl, + const void *key, size_t key_len, + br_ghash gh_impl, + const void *iv); +}; + +/** + * \brief Record encryption engine class, for GCM mode. + * + * This class type extends the encryption engine class with an + * initialisation method that receives the parameters needed + * for GCM processing: block cipher implementation, block cipher key, + * GHASH implementation, and 4-byte IV. + */ +typedef struct br_sslrec_out_gcm_class_ br_sslrec_out_gcm_class; +struct br_sslrec_out_gcm_class_ { + /** + * \brief Superclass, as first vtable field. + */ + br_sslrec_out_class inner; + + /** + * \brief Engine initialisation method. + * + * This method sets the vtable field in the context. + * + * \param ctx context to initialise. + * \param bc_impl block cipher implementation (CTR). + * \param key block cipher key. + * \param key_len block cipher key length (in bytes). + * \param gh_impl GHASH implementation. + * \param iv static IV (4 bytes). + */ + void (*init)(const br_sslrec_out_gcm_class **ctx, + const br_block_ctr_class *bc_impl, + const void *key, size_t key_len, + br_ghash gh_impl, + const void *iv); +}; + +/** + * \brief Context structure for processing records with GCM. + * + * The same context structure is used for encrypting and decrypting. + * + * The first field points to the vtable. The other fields are opaque + * and shall not be accessed directly. + */ +typedef struct { + /** \brief Pointer to vtable. */ + union { + const void *gen; + const br_sslrec_in_gcm_class *in; + const br_sslrec_out_gcm_class *out; + } vtable; +#ifndef BR_DOXYGEN_IGNORE + uint64_t seq; + union { + const br_block_ctr_class *vtable; + br_aes_gen_ctr_keys aes; + } bc; + br_ghash gh; + unsigned char iv[4]; + unsigned char h[16]; +#endif +} br_sslrec_gcm_context; + +/** + * \brief Static, constant vtable for record decryption with GCM. + */ +extern const br_sslrec_in_gcm_class br_sslrec_in_gcm_vtable; + +/** + * \brief Static, constant vtable for record encryption with GCM. + */ +extern const br_sslrec_out_gcm_class br_sslrec_out_gcm_vtable; + +/* ===================================================================== */ + +/** + * \brief Record decryption engine class, for ChaCha20+Poly1305. + * + * This class type extends the decryption engine class with an + * initialisation method that receives the parameters needed + * for ChaCha20+Poly1305 processing: ChaCha20 implementation, + * Poly1305 implementation, key, and 12-byte IV. + */ +typedef struct br_sslrec_in_chapol_class_ br_sslrec_in_chapol_class; +struct br_sslrec_in_chapol_class_ { + /** + * \brief Superclass, as first vtable field. + */ + br_sslrec_in_class inner; + + /** + * \brief Engine initialisation method. + * + * This method sets the vtable field in the context. + * + * \param ctx context to initialise. + * \param ichacha ChaCha20 implementation. + * \param ipoly Poly1305 implementation. + * \param key secret key (32 bytes). + * \param iv static IV (12 bytes). + */ + void (*init)(const br_sslrec_in_chapol_class **ctx, + br_chacha20_run ichacha, + br_poly1305_run ipoly, + const void *key, const void *iv); +}; + +/** + * \brief Record encryption engine class, for ChaCha20+Poly1305. + * + * This class type extends the encryption engine class with an + * initialisation method that receives the parameters needed + * for ChaCha20+Poly1305 processing: ChaCha20 implementation, + * Poly1305 implementation, key, and 12-byte IV. + */ +typedef struct br_sslrec_out_chapol_class_ br_sslrec_out_chapol_class; +struct br_sslrec_out_chapol_class_ { + /** + * \brief Superclass, as first vtable field. + */ + br_sslrec_out_class inner; + + /** + * \brief Engine initialisation method. + * + * This method sets the vtable field in the context. + * + * \param ctx context to initialise. + * \param ichacha ChaCha20 implementation. + * \param ipoly Poly1305 implementation. + * \param key secret key (32 bytes). + * \param iv static IV (12 bytes). + */ + void (*init)(const br_sslrec_out_chapol_class **ctx, + br_chacha20_run ichacha, + br_poly1305_run ipoly, + const void *key, const void *iv); +}; + +/** + * \brief Context structure for processing records with ChaCha20+Poly1305. + * + * The same context structure is used for encrypting and decrypting. + * + * The first field points to the vtable. The other fields are opaque + * and shall not be accessed directly. + */ +typedef struct { + /** \brief Pointer to vtable. */ + union { + const void *gen; + const br_sslrec_in_chapol_class *in; + const br_sslrec_out_chapol_class *out; + } vtable; +#ifndef BR_DOXYGEN_IGNORE + uint64_t seq; + unsigned char key[32]; + unsigned char iv[12]; + br_chacha20_run ichacha; + br_poly1305_run ipoly; +#endif +} br_sslrec_chapol_context; + +/** + * \brief Static, constant vtable for record decryption with ChaCha20+Poly1305. + */ +extern const br_sslrec_in_chapol_class br_sslrec_in_chapol_vtable; + +/** + * \brief Static, constant vtable for record encryption with ChaCha20+Poly1305. + */ +extern const br_sslrec_out_chapol_class br_sslrec_out_chapol_vtable; + +/* ===================================================================== */ + +/** + * \brief Record decryption engine class, for CCM mode. + * + * This class type extends the decryption engine class with an + * initialisation method that receives the parameters needed + * for CCM processing: block cipher implementation, block cipher key, + * and 4-byte IV. + */ +typedef struct br_sslrec_in_ccm_class_ br_sslrec_in_ccm_class; +struct br_sslrec_in_ccm_class_ { + /** + * \brief Superclass, as first vtable field. + */ + br_sslrec_in_class inner; + + /** + * \brief Engine initialisation method. + * + * This method sets the vtable field in the context. + * + * \param ctx context to initialise. + * \param bc_impl block cipher implementation (CTR+CBC). + * \param key block cipher key. + * \param key_len block cipher key length (in bytes). + * \param iv static IV (4 bytes). + * \param tag_len tag length (in bytes) + */ + void (*init)(const br_sslrec_in_ccm_class **ctx, + const br_block_ctrcbc_class *bc_impl, + const void *key, size_t key_len, + const void *iv, size_t tag_len); +}; + +/** + * \brief Record encryption engine class, for CCM mode. + * + * This class type extends the encryption engine class with an + * initialisation method that receives the parameters needed + * for CCM processing: block cipher implementation, block cipher key, + * and 4-byte IV. + */ +typedef struct br_sslrec_out_ccm_class_ br_sslrec_out_ccm_class; +struct br_sslrec_out_ccm_class_ { + /** + * \brief Superclass, as first vtable field. + */ + br_sslrec_out_class inner; + + /** + * \brief Engine initialisation method. + * + * This method sets the vtable field in the context. + * + * \param ctx context to initialise. + * \param bc_impl block cipher implementation (CTR+CBC). + * \param key block cipher key. + * \param key_len block cipher key length (in bytes). + * \param iv static IV (4 bytes). + * \param tag_len tag length (in bytes) + */ + void (*init)(const br_sslrec_out_ccm_class **ctx, + const br_block_ctrcbc_class *bc_impl, + const void *key, size_t key_len, + const void *iv, size_t tag_len); +}; + +/** + * \brief Context structure for processing records with CCM. + * + * The same context structure is used for encrypting and decrypting. + * + * The first field points to the vtable. The other fields are opaque + * and shall not be accessed directly. + */ +typedef struct { + /** \brief Pointer to vtable. */ + union { + const void *gen; + const br_sslrec_in_ccm_class *in; + const br_sslrec_out_ccm_class *out; + } vtable; +#ifndef BR_DOXYGEN_IGNORE + uint64_t seq; + union { + const br_block_ctrcbc_class *vtable; + br_aes_gen_ctrcbc_keys aes; + } bc; + unsigned char iv[4]; + size_t tag_len; +#endif +} br_sslrec_ccm_context; + +/** + * \brief Static, constant vtable for record decryption with CCM. + */ +extern const br_sslrec_in_ccm_class br_sslrec_in_ccm_vtable; + +/** + * \brief Static, constant vtable for record encryption with CCM. + */ +extern const br_sslrec_out_ccm_class br_sslrec_out_ccm_vtable; + +/* ===================================================================== */ + +/** + * \brief Type for session parameters, to be saved for session resumption. + */ +typedef struct { + /** \brief Session ID buffer. */ + unsigned char session_id[32]; + /** \brief Session ID length (in bytes, at most 32). */ + unsigned char session_id_len; + /** \brief Protocol version. */ + uint16_t version; + /** \brief Cipher suite. */ + uint16_t cipher_suite; + /** \brief Master secret. */ + unsigned char master_secret[48]; +} br_ssl_session_parameters; + +#ifndef BR_DOXYGEN_IGNORE +/* + * Maximum number of cipher suites supported by a client or server. + */ +#define BR_MAX_CIPHER_SUITES 48 +#endif + +/** + * \brief Context structure for SSL engine. + * + * This strucuture is common to the client and server; both the client + * context (`br_ssl_client_context`) and the server context + * (`br_ssl_server_context`) include a `br_ssl_engine_context` as their + * first field. + * + * The engine context manages records, including alerts, closures, and + * transitions to new encryption/MAC algorithms. Processing of handshake + * records is delegated to externally provided code. This structure + * should not be used directly. + * + * Structure contents are opaque and shall not be accessed directly. + */ +typedef struct { +#ifndef BR_DOXYGEN_IGNORE + /* + * The error code. When non-zero, then the state is "failed" and + * no I/O may occur until reset. + */ + int err; + + /* + * Configured I/O buffers. They are either disjoint, or identical. + */ + unsigned char *ibuf, *obuf; + size_t ibuf_len, obuf_len; + + /* + * Maximum fragment length applies to outgoing records; incoming + * records can be processed as long as they fit in the input + * buffer. It is guaranteed that incoming records at least as big + * as max_frag_len can be processed. + */ + uint16_t max_frag_len; + unsigned char log_max_frag_len; + unsigned char max_frag_len_negotiated; + unsigned char peer_log_max_frag_len; + + /* + * Buffering management registers. + */ + size_t ixa, ixb, ixc; + size_t oxa, oxb, oxc; + unsigned char iomode; + unsigned char incrypt; + + /* + * Shutdown flag: when set to non-zero, incoming record bytes + * will not be accepted anymore. This is used after a close_notify + * has been received: afterwards, the engine no longer claims that + * it could receive bytes from the transport medium. + */ + unsigned char shutdown_recv; + + /* + * 'record_type_in' is set to the incoming record type when the + * record header has been received. + * 'record_type_out' is used to make the next outgoing record + * header when it is ready to go. + */ + unsigned char record_type_in, record_type_out; + + /* + * When a record is received, its version is extracted: + * -- if 'version_in' is 0, then it is set to the received version; + * -- otherwise, if the received version is not identical to + * the 'version_in' contents, then a failure is reported. + * + * This implements the SSL requirement that all records shall + * use the negotiated protocol version, once decided (in the + * ServerHello). It is up to the handshake handler to adjust this + * field when necessary. + */ + uint16_t version_in; + + /* + * 'version_out' is used when the next outgoing record is ready + * to go. + */ + uint16_t version_out; + + /* + * Record handler contexts. + */ + union { + const br_sslrec_in_class *vtable; + br_sslrec_in_cbc_context cbc; + br_sslrec_gcm_context gcm; + br_sslrec_chapol_context chapol; + br_sslrec_ccm_context ccm; + } in; + union { + const br_sslrec_out_class *vtable; + br_sslrec_out_clear_context clear; + br_sslrec_out_cbc_context cbc; + br_sslrec_gcm_context gcm; + br_sslrec_chapol_context chapol; + br_sslrec_ccm_context ccm; + } out; + + /* + * The "application data" flag. Value: + * 0 handshake is in process, no application data acceptable + * 1 application data can be sent and received + * 2 closing, no application data can be sent, but some + * can still be received (and discarded) + */ + unsigned char application_data; + + /* + * Context RNG. + * + * rng_init_done is initially 0. It is set to 1 when the + * basic structure of the RNG is set, and 2 when some + * entropy has been pushed in. The value 2 marks the RNG + * as "properly seeded". + * + * rng_os_rand_done is initially 0. It is set to 1 when + * some seeding from the OS or hardware has been attempted. + */ + br_hmac_drbg_context rng; + int rng_init_done; + int rng_os_rand_done; + + /* + * Supported minimum and maximum versions, and cipher suites. + */ + uint16_t version_min; + uint16_t version_max; + uint16_t suites_buf[BR_MAX_CIPHER_SUITES]; + unsigned char suites_num; + + /* + * For clients, the server name to send as a SNI extension. For + * servers, the name received in the SNI extension (if any). + */ + char server_name[256]; + + /* + * "Security parameters". These are filled by the handshake + * handler, and used when switching encryption state. + */ + unsigned char client_random[32]; + unsigned char server_random[32]; + br_ssl_session_parameters session; + + /* + * ECDHE elements: curve and point from the peer. The server also + * uses that buffer for the point to send to the client. + */ + unsigned char ecdhe_curve; + unsigned char ecdhe_point[133]; + unsigned char ecdhe_point_len; + + /* + * Secure renegotiation (RFC 5746): 'reneg' can be: + * 0 first handshake (server support is not known) + * 1 peer does not support secure renegotiation + * 2 peer supports secure renegotiation + * + * The saved_finished buffer contains the client and the + * server "Finished" values from the last handshake, in + * that order (12 bytes each). + */ + unsigned char reneg; + unsigned char saved_finished[24]; + + /* + * Behavioural flags. + */ + uint32_t flags; + + /* + * Context variables for the handshake processor. The 'pad' must + * be large enough to accommodate an RSA-encrypted pre-master + * secret, or an RSA signature; since we want to support up to + * RSA-4096, this means at least 512 bytes. (Other pad usages + * require its length to be at least 256.) + */ + struct { + uint32_t *dp; + uint32_t *rp; + const unsigned char *ip; + } cpu; + uint32_t dp_stack[32]; + uint32_t rp_stack[32]; + unsigned char pad[512]; + unsigned char *hbuf_in, *hbuf_out, *saved_hbuf_out; + size_t hlen_in, hlen_out; + void (*hsrun)(void *ctx); + + /* + * The 'action' value communicates OOB information between the + * engine and the handshake processor. + * + * From the engine: + * 0 invocation triggered by I/O + * 1 invocation triggered by explicit close + * 2 invocation triggered by explicit renegotiation + */ + unsigned char action; + + /* + * State for alert messages. Value is either 0, or the value of + * the alert level byte (level is either 1 for warning, or 2 for + * fatal; we convert all other values to 'fatal'). + */ + unsigned char alert; + + /* + * Closure flags. This flag is set when a close_notify has been + * received from the peer. + */ + unsigned char close_received; + + /* + * Multi-hasher for the handshake messages. The handshake handler + * is responsible for resetting it when appropriate. + */ + br_multihash_context mhash; + + /* + * Pointer to the X.509 engine. The engine is supposed to be + * already initialized. It is used to validate the peer's + * certificate. + */ + const br_x509_class **x509ctx; + + /* + * Certificate chain to send. This is used by both client and + * server, when they send their respective Certificate messages. + * If chain_len is 0, then chain may be NULL. + */ + const br_x509_certificate *chain; + size_t chain_len; + const unsigned char *cert_cur; + size_t cert_len; + + /* + * List of supported protocol names (ALPN extension). If unset, + * (number of names is 0), then: + * - the client sends no ALPN extension; + * - the server ignores any incoming ALPN extension. + * + * Otherwise: + * - the client sends an ALPN extension with all the names; + * - the server selects the first protocol in its list that + * the client also supports, or fails (fatal alert 120) + * if the client sends an ALPN extension and there is no + * match. + * + * The 'selected_protocol' field contains 1+n if the matching + * name has index n in the list (the value is 0 if no match was + * performed, e.g. the peer did not send an ALPN extension). + */ + const char **protocol_names; + uint16_t protocol_names_num; + uint16_t selected_protocol; + + /* + * Pointers to implementations; left to NULL for unsupported + * functions. For the raw hash functions, implementations are + * referenced from the multihasher (mhash field). + */ + br_tls_prf_impl prf10; + br_tls_prf_impl prf_sha256; + br_tls_prf_impl prf_sha384; + const br_block_cbcenc_class *iaes_cbcenc; + const br_block_cbcdec_class *iaes_cbcdec; + const br_block_ctr_class *iaes_ctr; + const br_block_ctrcbc_class *iaes_ctrcbc; + const br_block_cbcenc_class *ides_cbcenc; + const br_block_cbcdec_class *ides_cbcdec; + br_ghash ighash; + br_chacha20_run ichacha; + br_poly1305_run ipoly; + const br_sslrec_in_cbc_class *icbc_in; + const br_sslrec_out_cbc_class *icbc_out; + const br_sslrec_in_gcm_class *igcm_in; + const br_sslrec_out_gcm_class *igcm_out; + const br_sslrec_in_chapol_class *ichapol_in; + const br_sslrec_out_chapol_class *ichapol_out; + const br_sslrec_in_ccm_class *iccm_in; + const br_sslrec_out_ccm_class *iccm_out; + const br_ec_impl *iec; + br_rsa_pkcs1_vrfy irsavrfy; + br_ecdsa_vrfy iecdsa; +#endif +} br_ssl_engine_context; + +/** + * \brief Get currently defined engine behavioural flags. + * + * \param cc SSL engine context. + * \return the flags. + */ +static inline uint32_t +br_ssl_engine_get_flags(br_ssl_engine_context *cc) +{ + return cc->flags; +} + +/** + * \brief Set all engine behavioural flags. + * + * \param cc SSL engine context. + * \param flags new value for all flags. + */ +static inline void +br_ssl_engine_set_all_flags(br_ssl_engine_context *cc, uint32_t flags) +{ + cc->flags = flags; +} + +/** + * \brief Set some engine behavioural flags. + * + * The flags set in the `flags` parameter are set in the context; other + * flags are untouched. + * + * \param cc SSL engine context. + * \param flags additional set flags. + */ +static inline void +br_ssl_engine_add_flags(br_ssl_engine_context *cc, uint32_t flags) +{ + cc->flags |= flags; +} + +/** + * \brief Clear some engine behavioural flags. + * + * The flags set in the `flags` parameter are cleared from the context; other + * flags are untouched. + * + * \param cc SSL engine context. + * \param flags flags to remove. + */ +static inline void +br_ssl_engine_remove_flags(br_ssl_engine_context *cc, uint32_t flags) +{ + cc->flags &= ~flags; +} + +/** + * \brief Behavioural flag: enforce server preferences. + * + * If this flag is set, then the server will enforce its own cipher suite + * preference order; otherwise, it follows the client preferences. + */ +#define BR_OPT_ENFORCE_SERVER_PREFERENCES ((uint32_t)1 << 0) + +/** + * \brief Behavioural flag: disable renegotiation. + * + * If this flag is set, then renegotiations are rejected unconditionally: + * they won't be honoured if asked for programmatically, and requests from + * the peer are rejected. + */ +#define BR_OPT_NO_RENEGOTIATION ((uint32_t)1 << 1) + +/** + * \brief Behavioural flag: tolerate lack of client authentication. + * + * If this flag is set in a server and the server requests a client + * certificate, but the authentication fails (the client does not send + * a certificate, or the client's certificate chain cannot be validated), + * then the connection keeps on. Without this flag, a failed client + * authentication terminates the connection. + * + * Notes: + * + * - If the client's certificate can be validated and its public key is + * supported, then a wrong signature value terminates the connection + * regardless of that flag. + * + * - If using full-static ECDH, then a failure to validate the client's + * certificate prevents the handshake from succeeding. + */ +#define BR_OPT_TOLERATE_NO_CLIENT_AUTH ((uint32_t)1 << 2) + +/** + * \brief Behavioural flag: fail on application protocol mismatch. + * + * The ALPN extension ([RFC 7301](https://tools.ietf.org/html/rfc7301)) + * allows the client to send a list of application protocol names, and + * the server to select one. A mismatch is one of the following occurrences: + * + * - On the client: the client sends a list of names, the server + * responds with a protocol name which is _not_ part of the list of + * names sent by the client. + * + * - On the server: the client sends a list of names, and the server + * is also configured with a list of names, but there is no common + * protocol name between the two lists. + * + * Normal behaviour in case of mismatch is to report no matching name + * (`br_ssl_engine_get_selected_protocol()` returns `NULL`) and carry on. + * If the flag is set, then a mismatch implies a protocol failure (if + * the mismatch is detected by the server, it will send a fatal alert). + * + * Note: even with this flag, `br_ssl_engine_get_selected_protocol()` + * may still return `NULL` if the client or the server does not send an + * ALPN extension at all. + */ +#define BR_OPT_FAIL_ON_ALPN_MISMATCH ((uint32_t)1 << 3) + +/** + * \brief Set the minimum and maximum supported protocol versions. + * + * The two provided versions MUST be supported by the implementation + * (i.e. TLS 1.0, 1.1 and 1.2), and `version_max` MUST NOT be lower + * than `version_min`. + * + * \param cc SSL engine context. + * \param version_min minimum supported TLS version. + * \param version_max maximum supported TLS version. + */ +static inline void +br_ssl_engine_set_versions(br_ssl_engine_context *cc, + unsigned version_min, unsigned version_max) +{ + cc->version_min = version_min; + cc->version_max = version_max; +} + +/** + * \brief Set the list of cipher suites advertised by this context. + * + * The provided array is copied into the context. It is the caller + * responsibility to ensure that all provided suites will be supported + * by the context. The engine context has enough room to receive _all_ + * suites supported by the implementation. The provided array MUST NOT + * contain duplicates. + * + * If the engine is for a client, the "signaling" pseudo-cipher suite + * `TLS_FALLBACK_SCSV` can be added at the end of the list, if the + * calling application is performing a voluntary downgrade (voluntary + * downgrades are not recommended, but if such a downgrade is done, then + * adding the fallback pseudo-suite is a good idea). + * + * \param cc SSL engine context. + * \param suites cipher suites. + * \param suites_num number of cipher suites. + */ +void br_ssl_engine_set_suites(br_ssl_engine_context *cc, + const uint16_t *suites, size_t suites_num); + +/** + * \brief Set the X.509 engine. + * + * The caller shall ensure that the X.509 engine is properly initialised. + * + * \param cc SSL engine context. + * \param x509ctx X.509 certificate validation context. + */ +static inline void +br_ssl_engine_set_x509(br_ssl_engine_context *cc, const br_x509_class **x509ctx) +{ + cc->x509ctx = x509ctx; +} + +/** + * \brief Set the supported protocol names. + * + * Protocol names are part of the ALPN extension ([RFC + * 7301](https://tools.ietf.org/html/rfc7301)). Each protocol name is a + * character string, containing no more than 255 characters (256 with the + * terminating zero). When names are set, then: + * + * - The client will send an ALPN extension, containing the names. If + * the server responds with an ALPN extension, the client will verify + * that the response contains one of its name, and report that name + * through `br_ssl_engine_get_selected_protocol()`. + * + * - The server will parse incoming ALPN extension (from clients), and + * try to find a common protocol; if none is found, the connection + * is aborted with a fatal alert. On match, a response ALPN extension + * is sent, and name is reported through + * `br_ssl_engine_get_selected_protocol()`. + * + * The provided array is linked in, and must remain valid while the + * connection is live. + * + * Names MUST NOT be empty. Names MUST NOT be longer than 255 characters + * (excluding the terminating 0). + * + * \param ctx SSL engine context. + * \param names list of protocol names (zero-terminated). + * \param num number of protocol names (MUST be 1 or more). + */ +static inline void +br_ssl_engine_set_protocol_names(br_ssl_engine_context *ctx, + const char **names, size_t num) +{ + ctx->protocol_names = names; + ctx->protocol_names_num = num; +} + +/** + * \brief Get the selected protocol. + * + * If this context was initialised with a non-empty list of protocol + * names, and both client and server sent ALPN extensions during the + * handshake, and a common name was found, then that name is returned. + * Otherwise, `NULL` is returned. + * + * The returned pointer is one of the pointers provided to the context + * with `br_ssl_engine_set_protocol_names()`. + * + * \return the selected protocol, or `NULL`. + */ +static inline const char * +br_ssl_engine_get_selected_protocol(br_ssl_engine_context *ctx) +{ + unsigned k; + + k = ctx->selected_protocol; + return (k == 0 || k == 0xFFFF) ? NULL : ctx->protocol_names[k - 1]; +} + +/** + * \brief Set a hash function implementation (by ID). + * + * Hash functions set with this call will be used for SSL/TLS specific + * usages, not X.509 certificate validation. Only "standard" hash functions + * may be set (MD5, SHA-1, SHA-224, SHA-256, SHA-384, SHA-512). If `impl` + * is `NULL`, then the hash function support is removed, not added. + * + * \param ctx SSL engine context. + * \param id hash function identifier. + * \param impl hash function implementation (or `NULL`). + */ +static inline void +br_ssl_engine_set_hash(br_ssl_engine_context *ctx, + int id, const br_hash_class *impl) +{ + br_multihash_setimpl(&ctx->mhash, id, impl); +} + +/** + * \brief Get a hash function implementation (by ID). + * + * This function retrieves a hash function implementation which was + * set with `br_ssl_engine_set_hash()`. + * + * \param ctx SSL engine context. + * \param id hash function identifier. + * \return the hash function implementation (or `NULL`). + */ +static inline const br_hash_class * +br_ssl_engine_get_hash(br_ssl_engine_context *ctx, int id) +{ + return br_multihash_getimpl(&ctx->mhash, id); +} + +/** + * \brief Set the PRF implementation (for TLS 1.0 and 1.1). + * + * This function sets (or removes, if `impl` is `NULL`) the implementation + * for the PRF used in TLS 1.0 and 1.1. + * + * \param cc SSL engine context. + * \param impl PRF implementation (or `NULL`). + */ +static inline void +br_ssl_engine_set_prf10(br_ssl_engine_context *cc, br_tls_prf_impl impl) +{ + cc->prf10 = impl; +} + +/** + * \brief Set the PRF implementation with SHA-256 (for TLS 1.2). + * + * This function sets (or removes, if `impl` is `NULL`) the implementation + * for the SHA-256 variant of the PRF used in TLS 1.2. + * + * \param cc SSL engine context. + * \param impl PRF implementation (or `NULL`). + */ +static inline void +br_ssl_engine_set_prf_sha256(br_ssl_engine_context *cc, br_tls_prf_impl impl) +{ + cc->prf_sha256 = impl; +} + +/** + * \brief Set the PRF implementation with SHA-384 (for TLS 1.2). + * + * This function sets (or removes, if `impl` is `NULL`) the implementation + * for the SHA-384 variant of the PRF used in TLS 1.2. + * + * \param cc SSL engine context. + * \param impl PRF implementation (or `NULL`). + */ +static inline void +br_ssl_engine_set_prf_sha384(br_ssl_engine_context *cc, br_tls_prf_impl impl) +{ + cc->prf_sha384 = impl; +} + +/** + * \brief Set the AES/CBC implementations. + * + * \param cc SSL engine context. + * \param impl_enc AES/CBC encryption implementation (or `NULL`). + * \param impl_dec AES/CBC decryption implementation (or `NULL`). + */ +static inline void +br_ssl_engine_set_aes_cbc(br_ssl_engine_context *cc, + const br_block_cbcenc_class *impl_enc, + const br_block_cbcdec_class *impl_dec) +{ + cc->iaes_cbcenc = impl_enc; + cc->iaes_cbcdec = impl_dec; +} + +/** + * \brief Set the "default" AES/CBC implementations. + * + * This function configures in the engine the AES implementations that + * should provide best runtime performance on the local system, while + * still being safe (in particular, constant-time). It also sets the + * handlers for CBC records. + * + * \param cc SSL engine context. + */ +void br_ssl_engine_set_default_aes_cbc(br_ssl_engine_context *cc); + +/** + * \brief Set the AES/CTR implementation. + * + * \param cc SSL engine context. + * \param impl AES/CTR encryption/decryption implementation (or `NULL`). + */ +static inline void +br_ssl_engine_set_aes_ctr(br_ssl_engine_context *cc, + const br_block_ctr_class *impl) +{ + cc->iaes_ctr = impl; +} + +/** + * \brief Set the "default" implementations for AES/GCM (AES/CTR + GHASH). + * + * This function configures in the engine the AES/CTR and GHASH + * implementation that should provide best runtime performance on the local + * system, while still being safe (in particular, constant-time). It also + * sets the handlers for GCM records. + * + * \param cc SSL engine context. + */ +void br_ssl_engine_set_default_aes_gcm(br_ssl_engine_context *cc); + +/** + * \brief Set the DES/CBC implementations. + * + * \param cc SSL engine context. + * \param impl_enc DES/CBC encryption implementation (or `NULL`). + * \param impl_dec DES/CBC decryption implementation (or `NULL`). + */ +static inline void +br_ssl_engine_set_des_cbc(br_ssl_engine_context *cc, + const br_block_cbcenc_class *impl_enc, + const br_block_cbcdec_class *impl_dec) +{ + cc->ides_cbcenc = impl_enc; + cc->ides_cbcdec = impl_dec; +} + +/** + * \brief Set the "default" DES/CBC implementations. + * + * This function configures in the engine the DES implementations that + * should provide best runtime performance on the local system, while + * still being safe (in particular, constant-time). It also sets the + * handlers for CBC records. + * + * \param cc SSL engine context. + */ +void br_ssl_engine_set_default_des_cbc(br_ssl_engine_context *cc); + +/** + * \brief Set the GHASH implementation (used in GCM mode). + * + * \param cc SSL engine context. + * \param impl GHASH implementation (or `NULL`). + */ +static inline void +br_ssl_engine_set_ghash(br_ssl_engine_context *cc, br_ghash impl) +{ + cc->ighash = impl; +} + +/** + * \brief Set the ChaCha20 implementation. + * + * \param cc SSL engine context. + * \param ichacha ChaCha20 implementation (or `NULL`). + */ +static inline void +br_ssl_engine_set_chacha20(br_ssl_engine_context *cc, + br_chacha20_run ichacha) +{ + cc->ichacha = ichacha; +} + +/** + * \brief Set the Poly1305 implementation. + * + * \param cc SSL engine context. + * \param ipoly Poly1305 implementation (or `NULL`). + */ +static inline void +br_ssl_engine_set_poly1305(br_ssl_engine_context *cc, + br_poly1305_run ipoly) +{ + cc->ipoly = ipoly; +} + +/** + * \brief Set the "default" ChaCha20 and Poly1305 implementations. + * + * This function configures in the engine the ChaCha20 and Poly1305 + * implementations that should provide best runtime performance on the + * local system, while still being safe (in particular, constant-time). + * It also sets the handlers for ChaCha20+Poly1305 records. + * + * \param cc SSL engine context. + */ +void br_ssl_engine_set_default_chapol(br_ssl_engine_context *cc); + +/** + * \brief Set the AES/CTR+CBC implementation. + * + * \param cc SSL engine context. + * \param impl AES/CTR+CBC encryption/decryption implementation (or `NULL`). + */ +static inline void +br_ssl_engine_set_aes_ctrcbc(br_ssl_engine_context *cc, + const br_block_ctrcbc_class *impl) +{ + cc->iaes_ctrcbc = impl; +} + +/** + * \brief Set the "default" implementations for AES/CCM. + * + * This function configures in the engine the AES/CTR+CBC + * implementation that should provide best runtime performance on the local + * system, while still being safe (in particular, constant-time). It also + * sets the handlers for CCM records. + * + * \param cc SSL engine context. + */ +void br_ssl_engine_set_default_aes_ccm(br_ssl_engine_context *cc); + +/** + * \brief Set the record encryption and decryption engines for CBC + HMAC. + * + * \param cc SSL engine context. + * \param impl_in record CBC decryption implementation (or `NULL`). + * \param impl_out record CBC encryption implementation (or `NULL`). + */ +static inline void +br_ssl_engine_set_cbc(br_ssl_engine_context *cc, + const br_sslrec_in_cbc_class *impl_in, + const br_sslrec_out_cbc_class *impl_out) +{ + cc->icbc_in = impl_in; + cc->icbc_out = impl_out; +} + +/** + * \brief Set the record encryption and decryption engines for GCM. + * + * \param cc SSL engine context. + * \param impl_in record GCM decryption implementation (or `NULL`). + * \param impl_out record GCM encryption implementation (or `NULL`). + */ +static inline void +br_ssl_engine_set_gcm(br_ssl_engine_context *cc, + const br_sslrec_in_gcm_class *impl_in, + const br_sslrec_out_gcm_class *impl_out) +{ + cc->igcm_in = impl_in; + cc->igcm_out = impl_out; +} + +/** + * \brief Set the record encryption and decryption engines for CCM. + * + * \param cc SSL engine context. + * \param impl_in record CCM decryption implementation (or `NULL`). + * \param impl_out record CCM encryption implementation (or `NULL`). + */ +static inline void +br_ssl_engine_set_ccm(br_ssl_engine_context *cc, + const br_sslrec_in_ccm_class *impl_in, + const br_sslrec_out_ccm_class *impl_out) +{ + cc->iccm_in = impl_in; + cc->iccm_out = impl_out; +} + +/** + * \brief Set the record encryption and decryption engines for + * ChaCha20+Poly1305. + * + * \param cc SSL engine context. + * \param impl_in record ChaCha20 decryption implementation (or `NULL`). + * \param impl_out record ChaCha20 encryption implementation (or `NULL`). + */ +static inline void +br_ssl_engine_set_chapol(br_ssl_engine_context *cc, + const br_sslrec_in_chapol_class *impl_in, + const br_sslrec_out_chapol_class *impl_out) +{ + cc->ichapol_in = impl_in; + cc->ichapol_out = impl_out; +} + +/** + * \brief Set the EC implementation. + * + * The elliptic curve implementation will be used for ECDH and ECDHE + * cipher suites, and for ECDSA support. + * + * \param cc SSL engine context. + * \param iec EC implementation (or `NULL`). + */ +static inline void +br_ssl_engine_set_ec(br_ssl_engine_context *cc, const br_ec_impl *iec) +{ + cc->iec = iec; +} + +/** + * \brief Set the "default" EC implementation. + * + * This function sets the elliptic curve implementation for ECDH and + * ECDHE cipher suites, and for ECDSA support. It selects the fastest + * implementation on the current system. + * + * \param cc SSL engine context. + */ +void br_ssl_engine_set_default_ec(br_ssl_engine_context *cc); + +/** + * \brief Get the EC implementation configured in the provided engine. + * + * \param cc SSL engine context. + * \return the EC implementation. + */ +static inline const br_ec_impl * +br_ssl_engine_get_ec(br_ssl_engine_context *cc) +{ + return cc->iec; +} + +/** + * \brief Set the RSA signature verification implementation. + * + * On the client, this is used to verify the server's signature on its + * ServerKeyExchange message (for ECDHE_RSA cipher suites). On the server, + * this is used to verify the client's CertificateVerify message (if a + * client certificate is requested, and that certificate contains a RSA key). + * + * \param cc SSL engine context. + * \param irsavrfy RSA signature verification implementation. + */ +static inline void +br_ssl_engine_set_rsavrfy(br_ssl_engine_context *cc, br_rsa_pkcs1_vrfy irsavrfy) +{ + cc->irsavrfy = irsavrfy; +} + +/** + * \brief Set the "default" RSA implementation (signature verification). + * + * This function sets the RSA implementation (signature verification) + * to the fastest implementation available on the current platform. + * + * \param cc SSL engine context. + */ +void br_ssl_engine_set_default_rsavrfy(br_ssl_engine_context *cc); + +/** + * \brief Get the RSA implementation (signature verification) configured + * in the provided engine. + * + * \param cc SSL engine context. + * \return the RSA signature verification implementation. + */ +static inline br_rsa_pkcs1_vrfy +br_ssl_engine_get_rsavrfy(br_ssl_engine_context *cc) +{ + return cc->irsavrfy; +} + +/* + * \brief Set the ECDSA implementation (signature verification). + * + * On the client, this is used to verify the server's signature on its + * ServerKeyExchange message (for ECDHE_ECDSA cipher suites). On the server, + * this is used to verify the client's CertificateVerify message (if a + * client certificate is requested, that certificate contains an EC key, + * and full-static ECDH is not used). + * + * The ECDSA implementation will use the EC core implementation configured + * in the engine context. + * + * \param cc client context. + * \param iecdsa ECDSA verification implementation. + */ +static inline void +br_ssl_engine_set_ecdsa(br_ssl_engine_context *cc, br_ecdsa_vrfy iecdsa) +{ + cc->iecdsa = iecdsa; +} + +/** + * \brief Set the "default" ECDSA implementation (signature verification). + * + * This function sets the ECDSA implementation (signature verification) + * to the fastest implementation available on the current platform. This + * call also sets the elliptic curve implementation itself, there again + * to the fastest EC implementation available. + * + * \param cc SSL engine context. + */ +void br_ssl_engine_set_default_ecdsa(br_ssl_engine_context *cc); + +/** + * \brief Get the ECDSA implementation (signature verification) configured + * in the provided engine. + * + * \param cc SSL engine context. + * \return the ECDSA signature verification implementation. + */ +static inline br_ecdsa_vrfy +br_ssl_engine_get_ecdsa(br_ssl_engine_context *cc) +{ + return cc->iecdsa; +} + +/** + * \brief Set the I/O buffer for the SSL engine. + * + * Once this call has been made, `br_ssl_client_reset()` or + * `br_ssl_server_reset()` MUST be called before using the context. + * + * The provided buffer will be used as long as the engine context is + * used. The caller is responsible for keeping it available. + * + * If `bidi` is 0, then the engine will operate in half-duplex mode + * (it won't be able to send data while there is unprocessed incoming + * data in the buffer, and it won't be able to receive data while there + * is unsent data in the buffer). The optimal buffer size in half-duplex + * mode is `BR_SSL_BUFSIZE_MONO`; if the buffer is larger, then extra + * bytes are ignored. If the buffer is smaller, then this limits the + * capacity of the engine to support all allowed record sizes. + * + * If `bidi` is 1, then the engine will split the buffer into two + * parts, for separate handling of outgoing and incoming data. This + * enables full-duplex processing, but requires more RAM. The optimal + * buffer size in full-duplex mode is `BR_SSL_BUFSIZE_BIDI`; if the + * buffer is larger, then extra bytes are ignored. If the buffer is + * smaller, then the split will favour the incoming part, so that + * interoperability is maximised. + * + * \param cc SSL engine context + * \param iobuf I/O buffer. + * \param iobuf_len I/O buffer length (in bytes). + * \param bidi non-zero for full-duplex mode. + */ +void br_ssl_engine_set_buffer(br_ssl_engine_context *cc, + void *iobuf, size_t iobuf_len, int bidi); + +/** + * \brief Set the I/O buffers for the SSL engine. + * + * Once this call has been made, `br_ssl_client_reset()` or + * `br_ssl_server_reset()` MUST be called before using the context. + * + * This function is similar to `br_ssl_engine_set_buffer()`, except + * that it enforces full-duplex mode, and the two I/O buffers are + * provided as separate chunks. + * + * The macros `BR_SSL_BUFSIZE_INPUT` and `BR_SSL_BUFSIZE_OUTPUT` + * evaluate to the optimal (maximum) sizes for the input and output + * buffer, respectively. + * + * \param cc SSL engine context + * \param ibuf input buffer. + * \param ibuf_len input buffer length (in bytes). + * \param obuf output buffer. + * \param obuf_len output buffer length (in bytes). + */ +void br_ssl_engine_set_buffers_bidi(br_ssl_engine_context *cc, + void *ibuf, size_t ibuf_len, void *obuf, size_t obuf_len); + +/** + * \brief Determine if MFLN negotiation was successful + * + * \param cc SSL engine context. + */ +static inline uint8_t +br_ssl_engine_get_mfln_negotiated(br_ssl_engine_context *cc) +{ + return cc->max_frag_len_negotiated; +} + +/** + * \brief Inject some "initial entropy" in the context. + * + * This entropy will be added to what can be obtained from the + * underlying operating system, if that OS is supported. + * + * This function may be called several times; all injected entropy chunks + * are cumulatively mixed. + * + * If entropy gathering from the OS is supported and compiled in, then this + * step is optional. Otherwise, it is mandatory to inject randomness, and + * the caller MUST take care to push (as one or several successive calls) + * enough entropy to achieve cryptographic resistance (at least 80 bits, + * preferably 128 or more). The engine will report an error if no entropy + * was provided and none can be obtained from the OS. + * + * Take care that this function cannot assess the cryptographic quality of + * the provided bytes. + * + * In all generality, "entropy" must here be considered to mean "that + * which the attacker cannot predict". If your OS/architecture does not + * have a suitable source of randomness, then you can make do with the + * combination of a large enough secret value (possibly a copy of an + * asymmetric private key that you also store on the system) AND a + * non-repeating value (e.g. current time, provided that the local clock + * cannot be reset or altered by the attacker). + * + * \param cc SSL engine context. + * \param data extra entropy to inject. + * \param len length of the extra data (in bytes). + */ +void br_ssl_engine_inject_entropy(br_ssl_engine_context *cc, + const void *data, size_t len); + +/** + * \brief Get the "server name" in this engine. + * + * For clients, this is the name provided with `br_ssl_client_reset()`; + * for servers, this is the name received from the client as part of the + * ClientHello message. If there is no such name (e.g. the client did + * not send an SNI extension) then the returned string is empty + * (returned pointer points to a byte of value 0). + * + * The returned pointer refers to a buffer inside the context, which may + * be overwritten as part of normal SSL activity (even within the same + * connection, if a renegotiation occurs). + * + * \param cc SSL engine context. + * \return the server name (possibly empty). + */ +static inline const char * +br_ssl_engine_get_server_name(const br_ssl_engine_context *cc) +{ + return cc->server_name; +} + +/** + * \brief Get the protocol version. + * + * This function returns the protocol version that is used by the + * engine. That value is set after sending (for a server) or receiving + * (for a client) the ServerHello message. + * + * \param cc SSL engine context. + * \return the protocol version. + */ +static inline unsigned +br_ssl_engine_get_version(const br_ssl_engine_context *cc) +{ + return cc->session.version; +} + +/** + * \brief Get a copy of the session parameters. + * + * The session parameters are filled during the handshake, so this + * function shall not be called before completion of the handshake. + * The initial handshake is completed when the context first allows + * application data to be injected. + * + * This function copies the current session parameters into the provided + * structure. Beware that the session parameters include the master + * secret, which is sensitive data, to handle with great care. + * + * \param cc SSL engine context. + * \param pp destination structure for the session parameters. + */ +static inline void +br_ssl_engine_get_session_parameters(const br_ssl_engine_context *cc, + br_ssl_session_parameters *pp) +{ + memcpy(pp, &cc->session, sizeof *pp); +} + +/** + * \brief Set the session parameters to the provided values. + * + * This function is meant to be used in the client, before doing a new + * handshake; a session resumption will be attempted with these + * parameters. In the server, this function has no effect. + * + * \param cc SSL engine context. + * \param pp source structure for the session parameters. + */ +static inline void +br_ssl_engine_set_session_parameters(br_ssl_engine_context *cc, + const br_ssl_session_parameters *pp) +{ + memcpy(&cc->session, pp, sizeof *pp); +} + +/** + * \brief Get identifier for the curve used for key exchange. + * + * If the cipher suite uses ECDHE, then this function returns the + * identifier for the curve used for transient parameters. This is + * defined during the course of the handshake, when the ServerKeyExchange + * is sent (on the server) or received (on the client). If the + * cipher suite does not use ECDHE (e.g. static ECDH, or RSA key + * exchange), then this value is indeterminate. + * + * @param cc SSL engine context. + * @return the ECDHE curve identifier. + */ +static inline int +br_ssl_engine_get_ecdhe_curve(br_ssl_engine_context *cc) +{ + return cc->ecdhe_curve; +} + +/** + * \brief Get the current engine state. + * + * An SSL engine (client or server) has, at any time, a state which is + * the combination of zero, one or more of these flags: + * + * - `BR_SSL_CLOSED` + * + * Engine is finished, no more I/O (until next reset). + * + * - `BR_SSL_SENDREC` + * + * Engine has some bytes to send to the peer. + * + * - `BR_SSL_RECVREC` + * + * Engine expects some bytes from the peer. + * + * - `BR_SSL_SENDAPP` + * + * Engine may receive application data to send (or flush). + * + * - `BR_SSL_RECVAPP` + * + * Engine has obtained some application data from the peer, + * that should be read by the caller. + * + * If no flag at all is set (state value is 0), then the engine is not + * fully initialised yet. + * + * The `BR_SSL_CLOSED` flag is exclusive; when it is set, no other flag + * is set. To distinguish between a normal closure and an error, use + * `br_ssl_engine_last_error()`. + * + * Generally speaking, `BR_SSL_SENDREC` and `BR_SSL_SENDAPP` are mutually + * exclusive: the input buffer, at any point, either accumulates + * plaintext data, or contains an assembled record that is being sent. + * Similarly, `BR_SSL_RECVREC` and `BR_SSL_RECVAPP` are mutually exclusive. + * This may change in a future library version. + * + * \param cc SSL engine context. + * \return the current engine state. + */ +unsigned br_ssl_engine_current_state(const br_ssl_engine_context *cc); + +/** \brief SSL engine state: closed or failed. */ +#define BR_SSL_CLOSED 0x0001 +/** \brief SSL engine state: record data is ready to be sent to the peer. */ +#define BR_SSL_SENDREC 0x0002 +/** \brief SSL engine state: engine may receive records from the peer. */ +#define BR_SSL_RECVREC 0x0004 +/** \brief SSL engine state: engine may accept application data to send. */ +#define BR_SSL_SENDAPP 0x0008 +/** \brief SSL engine state: engine has received application data. */ +#define BR_SSL_RECVAPP 0x0010 + +/** + * \brief Get the engine error indicator. + * + * The error indicator is `BR_ERR_OK` (0) if no error was encountered + * since the last call to `br_ssl_client_reset()` or + * `br_ssl_server_reset()`. Other status values are "sticky": they + * remain set, and prevent all I/O activity, until cleared. Only the + * reset calls clear the error indicator. + * + * \param cc SSL engine context. + * \return 0, or a non-zero error code. + */ +static inline int +br_ssl_engine_last_error(const br_ssl_engine_context *cc) +{ + return cc->err; +} + +/* + * There are four I/O operations, each identified by a symbolic name: + * + * sendapp inject application data in the engine + * recvapp retrieving application data from the engine + * sendrec sending records on the transport medium + * recvrec receiving records from the transport medium + * + * Terminology works thus: in a layered model where the SSL engine sits + * between the application and the network, "send" designates operations + * where bytes flow from application to network, and "recv" for the + * reverse operation. Application data (the plaintext that is to be + * conveyed through SSL) is "app", while encrypted records are "rec". + * Note that from the SSL engine point of view, "sendapp" and "recvrec" + * designate bytes that enter the engine ("inject" operation), while + * "recvapp" and "sendrec" designate bytes that exit the engine + * ("extract" operation). + * + * For the operation 'xxx', two functions are defined: + * + * br_ssl_engine_xxx_buf + * Returns a pointer and length to the buffer to use for that + * operation. '*len' is set to the number of bytes that may be read + * from the buffer (extract operation) or written to the buffer + * (inject operation). If no byte may be exchanged for that operation + * at that point, then '*len' is set to zero, and NULL is returned. + * The engine state is unmodified by this call. + * + * br_ssl_engine_xxx_ack + * Informs the engine that 'len' bytes have been read from the buffer + * (extract operation) or written to the buffer (inject operation). + * The 'len' value MUST NOT be zero. The 'len' value MUST NOT exceed + * that which was obtained from a preceding br_ssl_engine_xxx_buf() + * call. + */ + +/** + * \brief Get buffer for application data to send. + * + * If the engine is ready to accept application data to send to the + * peer, then this call returns a pointer to the buffer where such + * data shall be written, and its length is written in `*len`. + * Otherwise, `*len` is set to 0 and `NULL` is returned. + * + * \param cc SSL engine context. + * \param len receives the application data output buffer length, or 0. + * \return the application data output buffer, or `NULL`. + */ +unsigned char *br_ssl_engine_sendapp_buf( + const br_ssl_engine_context *cc, size_t *len); + +/** + * \brief Inform the engine of some new application data. + * + * After writing `len` bytes in the buffer returned by + * `br_ssl_engine_sendapp_buf()`, the application shall call this + * function to trigger any relevant processing. The `len` parameter + * MUST NOT be 0, and MUST NOT exceed the value obtained in the + * `br_ssl_engine_sendapp_buf()` call. + * + * \param cc SSL engine context. + * \param len number of bytes pushed (not zero). + */ +void br_ssl_engine_sendapp_ack(br_ssl_engine_context *cc, size_t len); + +/** + * \brief Get buffer for received application data. + * + * If the engine has received application data from the peer, hen this + * call returns a pointer to the buffer from where such data shall be + * read, and its length is written in `*len`. Otherwise, `*len` is set + * to 0 and `NULL` is returned. + * + * \param cc SSL engine context. + * \param len receives the application data input buffer length, or 0. + * \return the application data input buffer, or `NULL`. + */ +unsigned char *br_ssl_engine_recvapp_buf( + const br_ssl_engine_context *cc, size_t *len); + +/** + * \brief Acknowledge some received application data. + * + * After reading `len` bytes from the buffer returned by + * `br_ssl_engine_recvapp_buf()`, the application shall call this + * function to trigger any relevant processing. The `len` parameter + * MUST NOT be 0, and MUST NOT exceed the value obtained in the + * `br_ssl_engine_recvapp_buf()` call. + * + * \param cc SSL engine context. + * \param len number of bytes read (not zero). + */ +void br_ssl_engine_recvapp_ack(br_ssl_engine_context *cc, size_t len); + +/** + * \brief Get buffer for record data to send. + * + * If the engine has prepared some records to send to the peer, then this + * call returns a pointer to the buffer from where such data shall be + * read, and its length is written in `*len`. Otherwise, `*len` is set + * to 0 and `NULL` is returned. + * + * \param cc SSL engine context. + * \param len receives the record data output buffer length, or 0. + * \return the record data output buffer, or `NULL`. + */ +unsigned char *br_ssl_engine_sendrec_buf( + const br_ssl_engine_context *cc, size_t *len); + +/** + * \brief Acknowledge some sent record data. + * + * After reading `len` bytes from the buffer returned by + * `br_ssl_engine_sendrec_buf()`, the application shall call this + * function to trigger any relevant processing. The `len` parameter + * MUST NOT be 0, and MUST NOT exceed the value obtained in the + * `br_ssl_engine_sendrec_buf()` call. + * + * \param cc SSL engine context. + * \param len number of bytes read (not zero). + */ +void br_ssl_engine_sendrec_ack(br_ssl_engine_context *cc, size_t len); + +/** + * \brief Get buffer for incoming records. + * + * If the engine is ready to accept records from the peer, then this + * call returns a pointer to the buffer where such data shall be + * written, and its length is written in `*len`. Otherwise, `*len` is + * set to 0 and `NULL` is returned. + * + * \param cc SSL engine context. + * \param len receives the record data input buffer length, or 0. + * \return the record data input buffer, or `NULL`. + */ +unsigned char *br_ssl_engine_recvrec_buf( + const br_ssl_engine_context *cc, size_t *len); + +/** + * \brief Inform the engine of some new record data. + * + * After writing `len` bytes in the buffer returned by + * `br_ssl_engine_recvrec_buf()`, the application shall call this + * function to trigger any relevant processing. The `len` parameter + * MUST NOT be 0, and MUST NOT exceed the value obtained in the + * `br_ssl_engine_recvrec_buf()` call. + * + * \param cc SSL engine context. + * \param len number of bytes pushed (not zero). + */ +void br_ssl_engine_recvrec_ack(br_ssl_engine_context *cc, size_t len); + +/** + * \brief Flush buffered application data. + * + * If some application data has been buffered in the engine, then wrap + * it into a record and mark it for sending. If no application data has + * been buffered but the engine would be ready to accept some, AND the + * `force` parameter is non-zero, then an empty record is assembled and + * marked for sending. In all other cases, this function does nothing. + * + * Empty records are technically legal, but not all existing SSL/TLS + * implementations support them. Empty records can be useful as a + * transparent "keep-alive" mechanism to maintain some low-level + * network activity. + * + * \param cc SSL engine context. + * \param force non-zero to force sending an empty record. + */ +void br_ssl_engine_flush(br_ssl_engine_context *cc, int force); + +/** + * \brief Initiate a closure. + * + * If, at that point, the context is open and in ready state, then a + * `close_notify` alert is assembled and marked for sending; this + * triggers the closure protocol. Otherwise, no such alert is assembled. + * + * \param cc SSL engine context. + */ +void br_ssl_engine_close(br_ssl_engine_context *cc); + +/** + * \brief Initiate a renegotiation. + * + * If the engine is failed or closed, or if the peer is known not to + * support secure renegotiation (RFC 5746), or if renegotiations have + * been disabled with the `BR_OPT_NO_RENEGOTIATION` flag, or if there + * is buffered incoming application data, then this function returns 0 + * and nothing else happens. + * + * Otherwise, this function returns 1, and a renegotiation attempt is + * triggered (if a handshake is already ongoing at that point, then + * no new handshake is triggered). + * + * \param cc SSL engine context. + * \return 1 on success, 0 on error. + */ +int br_ssl_engine_renegotiate(br_ssl_engine_context *cc); + +/** + * \brief Export key material from a connected SSL engine (RFC 5705). + * + * This calls compute a secret key of arbitrary length from the master + * secret of a connected SSL engine. If the provided context is not + * currently in "application data" state (initial handshake is not + * finished, another handshake is ongoing, or the connection failed or + * was closed), then this function returns 0. Otherwise, a secret key of + * length `len` bytes is computed and written in the buffer pointed to + * by `dst`, and 1 is returned. + * + * The computed key follows the specification described in RFC 5705. + * That RFC includes two key computations, with and without a "context + * value". If `context` is `NULL`, then the variant without context is + * used; otherwise, the `context_len` bytes located at the address + * pointed to by `context` are used in the computation. Note that it + * is possible to have a "with context" key with a context length of + * zero bytes, by setting `context` to a non-`NULL` value but + * `context_len` to 0. + * + * When context bytes are used, the context length MUST NOT exceed + * 65535 bytes. + * + * \param cc SSL engine context. + * \param dst destination buffer for exported key. + * \param len exported key length (in bytes). + * \param label disambiguation label. + * \param context context value (or `NULL`). + * \param context_len context length (in bytes). + * \return 1 on success, 0 on error. + */ +int br_ssl_key_export(br_ssl_engine_context *cc, + void *dst, size_t len, const char *label, + const void *context, size_t context_len); + +/* + * Pre-declaration for the SSL client context. + */ +typedef struct br_ssl_client_context_ br_ssl_client_context; + +/** + * \brief Type for the client certificate, if requested by the server. + */ +typedef struct { + /** + * \brief Authentication type. + * + * This is either `BR_AUTH_RSA` (RSA signature), `BR_AUTH_ECDSA` + * (ECDSA signature), or `BR_AUTH_ECDH` (static ECDH key exchange). + */ + int auth_type; + + /** + * \brief Hash function for computing the CertificateVerify. + * + * This is the symbolic identifier for the hash function that + * will be used to produce the hash of handshake messages, to + * be signed into the CertificateVerify. For full static ECDH + * (client and server certificates are both EC in the same + * curve, and static ECDH is used), this value is set to -1. + * + * Take care that with TLS 1.0 and 1.1, that value MUST match + * the protocol requirements: value must be 0 (MD5+SHA-1) for + * a RSA signature, or 2 (SHA-1) for an ECDSA signature. Only + * TLS 1.2 allows for other hash functions. + */ + int hash_id; + + /** + * \brief Certificate chain to send to the server. + * + * This is an array of `br_x509_certificate` objects, each + * normally containing a DER-encoded certificate. The client + * code does not try to decode these elements. If there is no + * chain to send to the server, then this pointer shall be + * set to `NULL`. + */ + const br_x509_certificate *chain; + + /** + * \brief Certificate chain length (number of certificates). + * + * If there is no chain to send to the server, then this value + * shall be set to 0. + */ + size_t chain_len; + +} br_ssl_client_certificate; + +/* + * Note: the constants below for signatures match the TLS constants. + */ + +/** \brief Client authentication type: static ECDH. */ +#define BR_AUTH_ECDH 0 +/** \brief Client authentication type: RSA signature. */ +#define BR_AUTH_RSA 1 +/** \brief Client authentication type: ECDSA signature. */ +#define BR_AUTH_ECDSA 3 + +/** + * \brief Class type for a certificate handler (client side). + * + * A certificate handler selects a client certificate chain to send to + * the server, upon explicit request from that server. It receives + * the list of trust anchor DN from the server, and supported types + * of certificates and signatures, and returns the chain to use. It + * is also invoked to perform the corresponding private key operation + * (a signature, or an ECDH computation). + * + * The SSL client engine will first push the trust anchor DN with + * `start_name_list()`, `start_name()`, `append_name()`, `end_name()` + * and `end_name_list()`. Then it will call `choose()`, to select the + * actual chain (and signature/hash algorithms). Finally, it will call + * either `do_sign()` or `do_keyx()`, depending on the algorithm choices. + */ +typedef struct br_ssl_client_certificate_class_ br_ssl_client_certificate_class; +struct br_ssl_client_certificate_class_ { + /** + * \brief Context size (in bytes). + */ + size_t context_size; + + /** + * \brief Begin reception of a list of trust anchor names. This + * is called while parsing the incoming CertificateRequest. + * + * \param pctx certificate handler context. + */ + void (*start_name_list)(const br_ssl_client_certificate_class **pctx); + + /** + * \brief Begin reception of a new trust anchor name. + * + * The total encoded name length is provided; it is less than + * 65535 bytes. + * + * \param pctx certificate handler context. + * \param len encoded name length (in bytes). + */ + void (*start_name)(const br_ssl_client_certificate_class **pctx, + size_t len); + + /** + * \brief Receive some more bytes for the current trust anchor name. + * + * The provided reference (`data`) points to a transient buffer + * they may be reused as soon as this function returns. The chunk + * length (`len`) is never zero. + * + * \param pctx certificate handler context. + * \param data anchor name chunk. + * \param len anchor name chunk length (in bytes). + */ + void (*append_name)(const br_ssl_client_certificate_class **pctx, + const unsigned char *data, size_t len); + + /** + * \brief End current trust anchor name. + * + * This function is called when all the encoded anchor name data + * has been provided. + * + * \param pctx certificate handler context. + */ + void (*end_name)(const br_ssl_client_certificate_class **pctx); + + /** + * \brief End list of trust anchor names. + * + * This function is called when all the anchor names in the + * CertificateRequest message have been obtained. + * + * \param pctx certificate handler context. + */ + void (*end_name_list)(const br_ssl_client_certificate_class **pctx); + + /** + * \brief Select client certificate and algorithms. + * + * This callback function shall fill the provided `choices` + * structure with the selected algorithms and certificate chain. + * The `hash_id`, `chain` and `chain_len` fields must be set. If + * the client cannot or does not wish to send a certificate, + * then it shall set `chain` to `NULL` and `chain_len` to 0. + * + * The `auth_types` parameter describes the authentication types, + * signature algorithms and hash functions that are supported by + * both the client context and the server, and compatible with + * the current protocol version. This is a bit field with the + * following contents: + * + * - If RSA signatures with hash function x are supported, then + * bit x is set. + * + * - If ECDSA signatures with hash function x are supported, + * then bit 8+x is set. + * + * - If static ECDH is supported, with a RSA-signed certificate, + * then bit 16 is set. + * + * - If static ECDH is supported, with an ECDSA-signed certificate, + * then bit 17 is set. + * + * Notes: + * + * - When using TLS 1.0 or 1.1, the hash function for RSA + * signatures is always the special MD5+SHA-1 (id 0), and the + * hash function for ECDSA signatures is always SHA-1 (id 2). + * + * - When using TLS 1.2, the list of hash functions is trimmed + * down to include only hash functions that the client context + * can support. The actual server list can be obtained with + * `br_ssl_client_get_server_hashes()`; that list may be used + * to select the certificate chain to send to the server. + * + * \param pctx certificate handler context. + * \param cc SSL client context. + * \param auth_types supported authentication types and algorithms. + * \param choices destination structure for the policy choices. + */ + void (*choose)(const br_ssl_client_certificate_class **pctx, + const br_ssl_client_context *cc, uint32_t auth_types, + br_ssl_client_certificate *choices); + + /** + * \brief Perform key exchange (client part). + * + * This callback is invoked in case of a full static ECDH key + * exchange: + * + * - the cipher suite uses `ECDH_RSA` or `ECDH_ECDSA`; + * + * - the server requests a client certificate; + * + * - the client has, and sends, a client certificate that + * uses an EC key in the same curve as the server's key, + * and chooses static ECDH (the `hash_id` field in the choice + * structure was set to -1). + * + * In that situation, this callback is invoked to compute the + * client-side ECDH: the provided `data` (of length `*len` bytes) + * is the server's public key point (as decoded from its + * certificate), and the client shall multiply that point with + * its own private key, and write back the X coordinate of the + * resulting point in the same buffer, starting at offset 0. + * The `*len` value shall be modified to designate the actual + * length of the X coordinate. + * + * The callback must uphold the following: + * + * - If the input array does not have the proper length for + * an encoded curve point, then an error (0) shall be reported. + * + * - If the input array has the proper length, then processing + * MUST be constant-time, even if the data is not a valid + * encoded point. + * + * - This callback MUST check that the input point is valid. + * + * Returned value is 1 on success, 0 on error. + * + * \param pctx certificate handler context. + * \param data server public key point. + * \param len public key point length / X coordinate length. + * \return 1 on success, 0 on error. + */ + uint32_t (*do_keyx)(const br_ssl_client_certificate_class **pctx, + unsigned char *data, size_t *len); + + /** + * \brief Perform a signature (client authentication). + * + * This callback is invoked when a client certificate was sent, + * and static ECDH is not used. It shall compute a signature, + * using the client's private key, over the provided hash value + * (which is the hash of all previous handshake messages). + * + * On input, the hash value to sign is in `data`, of size + * `hv_len`; the involved hash function is identified by + * `hash_id`. The signature shall be computed and written + * back into `data`; the total size of that buffer is `len` + * bytes. + * + * This callback shall verify that the signature length does not + * exceed `len` bytes, and abstain from writing the signature if + * it does not fit. + * + * For RSA signatures, the `hash_id` may be 0, in which case + * this is the special header-less signature specified in TLS 1.0 + * and 1.1, with a 36-byte hash value. Otherwise, normal PKCS#1 + * v1.5 signatures shall be computed. + * + * For ECDSA signatures, the signature value shall use the ASN.1 + * based encoding. + * + * Returned value is the signature length (in bytes), or 0 on error. + * + * \param pctx certificate handler context. + * \param hash_id hash function identifier. + * \param hv_len hash value length (in bytes). + * \param data input/output buffer (hash value, then signature). + * \param len total buffer length (in bytes). + * \return signature length (in bytes) on success, or 0 on error. + */ + size_t (*do_sign)(const br_ssl_client_certificate_class **pctx, + int hash_id, size_t hv_len, unsigned char *data, size_t len); +}; + +/** + * \brief A single-chain RSA client certificate handler. + * + * This handler uses a single certificate chain, with a RSA + * signature. The list of trust anchor DN is ignored. + * + * Apart from the first field (vtable pointer), its contents are + * opaque and shall not be accessed directly. + */ +typedef struct { + /** \brief Pointer to vtable. */ + const br_ssl_client_certificate_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + const br_x509_certificate *chain; + size_t chain_len; + const br_rsa_private_key *sk; + br_rsa_pkcs1_sign irsasign; +#endif +} br_ssl_client_certificate_rsa_context; + +/** + * \brief A single-chain EC client certificate handler. + * + * This handler uses a single certificate chain, with a RSA + * signature. The list of trust anchor DN is ignored. + * + * This handler may support both static ECDH, and ECDSA signatures + * (either usage may be selectively disabled). + * + * Apart from the first field (vtable pointer), its contents are + * opaque and shall not be accessed directly. + */ +typedef struct { + /** \brief Pointer to vtable. */ + const br_ssl_client_certificate_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + const br_x509_certificate *chain; + size_t chain_len; + const br_ec_private_key *sk; + unsigned allowed_usages; + unsigned issuer_key_type; + const br_multihash_context *mhash; + const br_ec_impl *iec; + br_ecdsa_sign iecdsa; +#endif +} br_ssl_client_certificate_ec_context; + +/** + * \brief Context structure for a SSL client. + * + * The first field (called `eng`) is the SSL engine; all functions that + * work on a `br_ssl_engine_context` structure shall take as parameter + * a pointer to that field. The other structure fields are opaque and + * must not be accessed directly. + */ +struct br_ssl_client_context_ { + /** + * \brief The encapsulated engine context. + */ + br_ssl_engine_context eng; + +#ifndef BR_DOXYGEN_IGNORE + /* + * Minimum ClientHello length; padding with an extension (RFC + * 7685) is added if necessary to match at least that length. + * Such padding is nominally unnecessary, but it has been used + * to work around some server implementation bugs. + */ + uint16_t min_clienthello_len; + + /* + * Bit field for algoithms (hash + signature) supported by the + * server when requesting a client certificate. + */ + uint32_t hashes; + + /* + * Server's public key curve. + */ + int server_curve; + + /* + * Context for certificate handler. + */ + const br_ssl_client_certificate_class **client_auth_vtable; + + /* + * Client authentication type. + */ + unsigned char auth_type; + + /* + * Hash function to use for the client signature. This is 0xFF + * if static ECDH is used. + */ + unsigned char hash_id; + + /* + * For the core certificate handlers, thus avoiding (in most + * cases) the need for an externally provided policy context. + */ + union { + const br_ssl_client_certificate_class *vtable; + br_ssl_client_certificate_rsa_context single_rsa; + br_ssl_client_certificate_ec_context single_ec; + } client_auth; + + /* + * Implementations. + */ + br_rsa_public irsapub; +#endif +}; + +/** + * \brief Get the hash functions and signature algorithms supported by + * the server. + * + * This value is a bit field: + * + * - If RSA (PKCS#1 v1.5) is supported with hash function of ID `x`, + * then bit `x` is set (hash function ID is 0 for the special MD5+SHA-1, + * or 2 to 6 for the SHA family). + * + * - If ECDSA is supported with hash function of ID `x`, then bit `8+x` + * is set. + * + * - Newer algorithms are symbolic 16-bit identifiers that do not + * represent signature algorithm and hash function separately. If + * the TLS-level identifier is `0x0800+x` for a `x` in the 0..15 + * range, then bit `16+x` is set. + * + * "New algorithms" are currently defined only in draft documents, so + * this support is subject to possible change. Right now (early 2017), + * this maps ed25519 (EdDSA on Curve25519) to bit 23, and ed448 (EdDSA + * on Curve448) to bit 24. If the identifiers on the wire change in + * future document, then the decoding mechanism in BearSSL will be + * amended to keep mapping ed25519 and ed448 on bits 23 and 24, + * respectively. Mapping of other new algorithms (e.g. RSA/PSS) is not + * guaranteed yet. + * + * \param cc client context. + * \return the server-supported hash functions and signature algorithms. + */ +static inline uint32_t +br_ssl_client_get_server_hashes(const br_ssl_client_context *cc) +{ + return cc->hashes; +} + +/** + * \brief Get the server key curve. + * + * This function returns the ID for the curve used by the server's public + * key. This is set when the server's certificate chain is processed; + * this value is 0 if the server's key is not an EC key. + * + * \return the server's public key curve ID, or 0. + */ +static inline int +br_ssl_client_get_server_curve(const br_ssl_client_context *cc) +{ + return cc->server_curve; +} + +/* + * Each br_ssl_client_init_xxx() function sets the list of supported + * cipher suites and used implementations, as specified by the profile + * name 'xxx'. Defined profile names are: + * + * full all supported versions and suites; constant-time implementations + * TODO: add other profiles + */ + +/** + * \brief SSL client profile: full. + * + * This function initialises the provided SSL client context with + * all supported algorithms and cipher suites. It also initialises + * a companion X.509 validation engine with all supported algorithms, + * and the provided trust anchors; the X.509 engine will be used by + * the client context to validate the server's certificate. + * + * \param cc client context to initialise. + * \param xc X.509 validation context to initialise. + * \param trust_anchors trust anchors to use. + * \param trust_anchors_num number of trust anchors. + */ +void br_ssl_client_init_full(br_ssl_client_context *cc, + br_x509_minimal_context *xc, + const br_x509_trust_anchor *trust_anchors, size_t trust_anchors_num); + +/** + * \brief Clear the complete contents of a SSL client context. + * + * Everything is cleared, including the reference to the configured buffer, + * implementations, cipher suites and state. This is a preparatory step + * to assembling a custom profile. + * + * \param cc client context to clear. + */ +void br_ssl_client_zero(br_ssl_client_context *cc); + +/** + * \brief Set an externally provided client certificate handler context. + * + * The handler's methods are invoked when the server requests a client + * certificate. + * + * \param cc client context. + * \param pctx certificate handler context (pointer to its vtable field). + */ +static inline void +br_ssl_client_set_client_certificate(br_ssl_client_context *cc, + const br_ssl_client_certificate_class **pctx) +{ + cc->client_auth_vtable = pctx; +} + +/** + * \brief Set the RSA public-key operations implementation. + * + * This will be used to encrypt the pre-master secret with the server's + * RSA public key (RSA-encryption cipher suites only). + * + * \param cc client context. + * \param irsapub RSA public-key encryption implementation. + */ +static inline void +br_ssl_client_set_rsapub(br_ssl_client_context *cc, br_rsa_public irsapub) +{ + cc->irsapub = irsapub; +} + +/** + * \brief Set the "default" RSA implementation for public-key operations. + * + * This sets the RSA implementation in the client context (for encrypting + * the pre-master secret, in `TLS_RSA_*` cipher suites) to the fastest + * available on the current platform. + * + * \param cc client context. + */ +void br_ssl_client_set_default_rsapub(br_ssl_client_context *cc); + +/** + * \brief Set the minimum ClientHello length (RFC 7685 padding). + * + * If this value is set and the ClientHello would be shorter, then + * the Pad ClientHello extension will be added with enough padding bytes + * to reach the target size. Because of the extension header, the resulting + * size will sometimes be slightly more than `len` bytes if the target + * size cannot be exactly met. + * + * The target length relates to the _contents_ of the ClientHello, not + * counting its 4-byte header. For instance, if `len` is set to 512, + * then the padding will bring the ClientHello size to 516 bytes with its + * header, and 521 bytes when counting the 5-byte record header. + * + * \param cc client context. + * \param len minimum ClientHello length (in bytes). + */ +static inline void +br_ssl_client_set_min_clienthello_len(br_ssl_client_context *cc, uint16_t len) +{ + cc->min_clienthello_len = len; +} + +/** + * \brief Prepare or reset a client context for a new connection. + * + * The `server_name` parameter is used to fill the SNI extension; the + * X.509 "minimal" engine will also match that name against the server + * names included in the server's certificate. If the parameter is + * `NULL` then no SNI extension will be sent, and the X.509 "minimal" + * engine (if used for server certificate validation) will not check + * presence of any specific name in the received certificate. + * + * Therefore, setting the `server_name` to `NULL` shall be reserved + * to cases where alternate or additional methods are used to ascertain + * that the right server public key is used (e.g. a "known key" model). + * + * If `resume_session` is non-zero and the context was previously used + * then the session parameters may be reused (depending on whether the + * server previously sent a non-empty session ID, and accepts the session + * resumption). The session parameters for session resumption can also + * be set explicitly with `br_ssl_engine_set_session_parameters()`. + * + * On failure, the context is marked as failed, and this function + * returns 0. A possible failure condition is when no initial entropy + * was injected, and none could be obtained from the OS (either OS + * randomness gathering is not supported, or it failed). + * + * \param cc client context. + * \param server_name target server name, or `NULL`. + * \param resume_session non-zero to try session resumption. + * \return 0 on failure, 1 on success. + */ +int br_ssl_client_reset(br_ssl_client_context *cc, + const char *server_name, int resume_session); + +/** + * \brief Forget any session in the context. + * + * This means that the next handshake that uses this context will + * necessarily be a full handshake (this applies both to new connections + * and to renegotiations). + * + * \param cc client context. + */ +static inline void +br_ssl_client_forget_session(br_ssl_client_context *cc) +{ + cc->eng.session.session_id_len = 0; +} + +/** + * \brief Set client certificate chain and key (single RSA case). + * + * This function sets a client certificate chain, that the client will + * send to the server whenever a client certificate is requested. This + * certificate uses an RSA public key; the corresponding private key is + * invoked for authentication. Trust anchor names sent by the server are + * ignored. + * + * The provided chain and private key are linked in the client context; + * they must remain valid as long as they may be used, i.e. normally + * for the duration of the connection, since they might be invoked + * again upon renegotiations. + * + * \param cc SSL client context. + * \param chain client certificate chain (SSL order: EE comes first). + * \param chain_len client chain length (number of certificates). + * \param sk client private key. + * \param irsasign RSA signature implementation (PKCS#1 v1.5). + */ +void br_ssl_client_set_single_rsa(br_ssl_client_context *cc, + const br_x509_certificate *chain, size_t chain_len, + const br_rsa_private_key *sk, br_rsa_pkcs1_sign irsasign); + +/* + * \brief Set the client certificate chain and key (single EC case). + * + * This function sets a client certificate chain, that the client will + * send to the server whenever a client certificate is requested. This + * certificate uses an EC public key; the corresponding private key is + * invoked for authentication. Trust anchor names sent by the server are + * ignored. + * + * The provided chain and private key are linked in the client context; + * they must remain valid as long as they may be used, i.e. normally + * for the duration of the connection, since they might be invoked + * again upon renegotiations. + * + * The `allowed_usages` is a combination of usages, namely + * `BR_KEYTYPE_KEYX` and/or `BR_KEYTYPE_SIGN`. The `BR_KEYTYPE_KEYX` + * value allows full static ECDH, while the `BR_KEYTYPE_SIGN` value + * allows ECDSA signatures. If ECDSA signatures are used, then an ECDSA + * signature implementation must be provided; otherwise, the `iecdsa` + * parameter may be 0. + * + * The `cert_issuer_key_type` value is either `BR_KEYTYPE_RSA` or + * `BR_KEYTYPE_EC`; it is the type of the public key used the the CA + * that issued (signed) the client certificate. That value is used with + * full static ECDH: support of the certificate by the server depends + * on how the certificate was signed. (Note: when using TLS 1.2, this + * parameter is ignored; but its value matters for TLS 1.0 and 1.1.) + * + * \param cc server context. + * \param chain server certificate chain to send. + * \param chain_len chain length (number of certificates). + * \param sk server private key (EC). + * \param allowed_usages allowed private key usages. + * \param cert_issuer_key_type issuing CA's key type. + * \param iec EC core implementation. + * \param iecdsa ECDSA signature implementation ("asn1" format). + */ +void br_ssl_client_set_single_ec(br_ssl_client_context *cc, + const br_x509_certificate *chain, size_t chain_len, + const br_ec_private_key *sk, unsigned allowed_usages, + unsigned cert_issuer_key_type, + const br_ec_impl *iec, br_ecdsa_sign iecdsa); + +/** + * \brief Type for a "translated cipher suite", as an array of two + * 16-bit integers. + * + * The first element is the cipher suite identifier (as used on the wire). + * The second element is the concatenation of four 4-bit elements which + * characterise the cipher suite contents. In most to least significant + * order, these 4-bit elements are: + * + * - Bits 12 to 15: key exchange + server key type + * + * | val | symbolic constant | suite type | details | + * | :-- | :----------------------- | :---------- | :----------------------------------------------- | + * | 0 | `BR_SSLKEYX_RSA` | RSA | RSA key exchange, key is RSA (encryption) | + * | 1 | `BR_SSLKEYX_ECDHE_RSA` | ECDHE_RSA | ECDHE key exchange, key is RSA (signature) | + * | 2 | `BR_SSLKEYX_ECDHE_ECDSA` | ECDHE_ECDSA | ECDHE key exchange, key is EC (signature) | + * | 3 | `BR_SSLKEYX_ECDH_RSA` | ECDH_RSA | Key is EC (key exchange), cert signed with RSA | + * | 4 | `BR_SSLKEYX_ECDH_ECDSA` | ECDH_ECDSA | Key is EC (key exchange), cert signed with ECDSA | + * + * - Bits 8 to 11: symmetric encryption algorithm + * + * | val | symbolic constant | symmetric encryption | key strength (bits) | + * | :-- | :--------------------- | :------------------- | :------------------ | + * | 0 | `BR_SSLENC_3DES_CBC` | 3DES/CBC | 168 | + * | 1 | `BR_SSLENC_AES128_CBC` | AES-128/CBC | 128 | + * | 2 | `BR_SSLENC_AES256_CBC` | AES-256/CBC | 256 | + * | 3 | `BR_SSLENC_AES128_GCM` | AES-128/GCM | 128 | + * | 4 | `BR_SSLENC_AES256_GCM` | AES-256/GCM | 256 | + * | 5 | `BR_SSLENC_CHACHA20` | ChaCha20/Poly1305 | 256 | + * + * - Bits 4 to 7: MAC algorithm + * + * | val | symbolic constant | MAC type | details | + * | :-- | :----------------- | :----------- | :------------------------------------ | + * | 0 | `BR_SSLMAC_AEAD` | AEAD | No dedicated MAC (encryption is AEAD) | + * | 2 | `BR_SSLMAC_SHA1` | HMAC/SHA-1 | Value matches `br_sha1_ID` | + * | 4 | `BR_SSLMAC_SHA256` | HMAC/SHA-256 | Value matches `br_sha256_ID` | + * | 5 | `BR_SSLMAC_SHA384` | HMAC/SHA-384 | Value matches `br_sha384_ID` | + * + * - Bits 0 to 3: hash function for PRF when used with TLS-1.2 + * + * | val | symbolic constant | hash function | details | + * | :-- | :----------------- | :------------ | :----------------------------------- | + * | 4 | `BR_SSLPRF_SHA256` | SHA-256 | Value matches `br_sha256_ID` | + * | 5 | `BR_SSLPRF_SHA384` | SHA-384 | Value matches `br_sha384_ID` | + * + * For instance, cipher suite `TLS_RSA_WITH_AES_128_GCM_SHA256` has + * standard identifier 0x009C, and is translated to 0x0304, for, in + * that order: RSA key exchange (0), AES-128/GCM (3), AEAD integrity (0), + * SHA-256 in the TLS PRF (4). + */ +typedef uint16_t br_suite_translated[2]; + +#ifndef BR_DOXYGEN_IGNORE +/* + * Constants are already documented in the br_suite_translated type. + */ + +#define BR_SSLKEYX_RSA 0 +#define BR_SSLKEYX_ECDHE_RSA 1 +#define BR_SSLKEYX_ECDHE_ECDSA 2 +#define BR_SSLKEYX_ECDH_RSA 3 +#define BR_SSLKEYX_ECDH_ECDSA 4 + +#define BR_SSLENC_3DES_CBC 0 +#define BR_SSLENC_AES128_CBC 1 +#define BR_SSLENC_AES256_CBC 2 +#define BR_SSLENC_AES128_GCM 3 +#define BR_SSLENC_AES256_GCM 4 +#define BR_SSLENC_CHACHA20 5 + +#define BR_SSLMAC_AEAD 0 +#define BR_SSLMAC_SHA1 br_sha1_ID +#define BR_SSLMAC_SHA256 br_sha256_ID +#define BR_SSLMAC_SHA384 br_sha384_ID + +#define BR_SSLPRF_SHA256 br_sha256_ID +#define BR_SSLPRF_SHA384 br_sha384_ID + +#endif + +/* + * Pre-declaration for the SSL server context. + */ +typedef struct br_ssl_server_context_ br_ssl_server_context; + +/** + * \brief Type for the server policy choices, taken after analysis of + * the client message (ClientHello). + */ +typedef struct { + /** + * \brief Cipher suite to use with that client. + */ + uint16_t cipher_suite; + + /** + * \brief Hash function or algorithm for signing the ServerKeyExchange. + * + * This parameter is ignored for `TLS_RSA_*` and `TLS_ECDH_*` + * cipher suites; it is used only for `TLS_ECDHE_*` suites, in + * which the server _signs_ the ephemeral EC Diffie-Hellman + * parameters sent to the client. + * + * This identifier must be one of the following values: + * + * - `0xFF00 + id`, where `id` is a hash function identifier + * (0 for MD5+SHA-1, or 2 to 6 for one of the SHA functions); + * + * - a full 16-bit identifier, lower than `0xFF00`. + * + * If the first option is used, then the SSL engine will + * compute the hash of the data that is to be signed, with the + * designated hash function. The `do_sign()` method will be + * invoked with that hash value provided in the the `data` + * buffer. + * + * If the second option is used, then the SSL engine will NOT + * compute a hash on the data; instead, it will provide the + * to-be-signed data itself in `data`, i.e. the concatenation of + * the client random, server random, and encoded ECDH + * parameters. Furthermore, with TLS-1.2 and later, the 16-bit + * identifier will be used "as is" in the protocol, in the + * SignatureAndHashAlgorithm; for instance, `0x0401` stands for + * RSA PKCS#1 v1.5 signature (the `01`) with SHA-256 as hash + * function (the `04`). + * + * Take care that with TLS 1.0 and 1.1, the hash function is + * constrainted by the protocol: RSA signature must use + * MD5+SHA-1 (so use `0xFF00`), while ECDSA must use SHA-1 + * (`0xFF02`). Since TLS 1.0 and 1.1 don't include a + * SignatureAndHashAlgorithm field in their ServerKeyExchange + * messages, any value below `0xFF00` will be usable to send the + * raw ServerKeyExchange data to the `do_sign()` callback, but + * that callback must still follow the protocol requirements + * when generating the signature. + */ + unsigned algo_id; + + /** + * \brief Certificate chain to send to the client. + * + * This is an array of `br_x509_certificate` objects, each + * normally containing a DER-encoded certificate. The server + * code does not try to decode these elements. + */ + const br_x509_certificate *chain; + + /** + * \brief Certificate chain length (number of certificates). + */ + size_t chain_len; + +} br_ssl_server_choices; + +/** + * \brief Class type for a policy handler (server side). + * + * A policy handler selects the policy parameters for a connection + * (cipher suite and other algorithms, and certificate chain to send to + * the client); it also performs the server-side computations involving + * its permanent private key. + * + * The SSL server engine will invoke first `choose()`, once the + * ClientHello message has been received, then either `do_keyx()` + * `do_sign()`, depending on the cipher suite. + */ +typedef struct br_ssl_server_policy_class_ br_ssl_server_policy_class; +struct br_ssl_server_policy_class_ { + /** + * \brief Context size (in bytes). + */ + size_t context_size; + + /** + * \brief Select algorithms and certificates for this connection. + * + * This callback function shall fill the provided `choices` + * structure with the policy choices for this connection. This + * entails selecting the cipher suite, hash function for signing + * the ServerKeyExchange (applicable only to ECDHE cipher suites), + * and certificate chain to send. + * + * The callback receives a pointer to the server context that + * contains the relevant data. In particular, the functions + * `br_ssl_server_get_client_suites()`, + * `br_ssl_server_get_client_hashes()` and + * `br_ssl_server_get_client_curves()` can be used to obtain + * the cipher suites, hash functions and elliptic curves + * supported by both the client and server, respectively. The + * `br_ssl_engine_get_version()` and `br_ssl_engine_get_server_name()` + * functions yield the protocol version and requested server name + * (SNI), respectively. + * + * This function may modify its context structure (`pctx`) in + * arbitrary ways to keep track of its own choices. + * + * This function shall return 1 if appropriate policy choices + * could be made, or 0 if this connection cannot be pursued. + * + * \param pctx policy context. + * \param cc SSL server context. + * \param choices destination structure for the policy choices. + * \return 1 on success, 0 on error. + */ + int (*choose)(const br_ssl_server_policy_class **pctx, + const br_ssl_server_context *cc, + br_ssl_server_choices *choices); + + /** + * \brief Perform key exchange (server part). + * + * This callback is invoked to perform the server-side cryptographic + * operation for a key exchange that is not ECDHE. This callback + * uses the private key. + * + * **For RSA key exchange**, the provided `data` (of length `*len` + * bytes) shall be decrypted with the server's private key, and + * the 48-byte premaster secret copied back to the first 48 bytes + * of `data`. + * + * - The caller makes sure that `*len` is at least 59 bytes. + * + * - This callback MUST check that the provided length matches + * that of the key modulus; it shall report an error otherwise. + * + * - If the length matches that of the RSA key modulus, then + * processing MUST be constant-time, even if decryption fails, + * or the padding is incorrect, or the plaintext message length + * is not exactly 48 bytes. + * + * - This callback needs not check the two first bytes of the + * obtained pre-master secret (the caller will do that). + * + * - If an error is reported (0), then what the callback put + * in the first 48 bytes of `data` is unimportant (the caller + * will use random bytes instead). + * + * **For ECDH key exchange**, the provided `data` (of length `*len` + * bytes) is the elliptic curve point from the client. The + * callback shall multiply it with its private key, and store + * the resulting X coordinate in `data`, starting at offset 0, + * and set `*len` to the length of the X coordinate. + * + * - If the input array does not have the proper length for + * an encoded curve point, then an error (0) shall be reported. + * + * - If the input array has the proper length, then processing + * MUST be constant-time, even if the data is not a valid + * encoded point. + * + * - This callback MUST check that the input point is valid. + * + * Returned value is 1 on success, 0 on error. + * + * \param pctx policy context. + * \param data key exchange data from the client. + * \param len key exchange data length (in bytes). + * \return 1 on success, 0 on error. + */ + uint32_t (*do_keyx)(const br_ssl_server_policy_class **pctx, + unsigned char *data, size_t *len); + + /** + * \brief Perform a signature (for a ServerKeyExchange message). + * + * This callback function is invoked for ECDHE cipher suites. On + * input, the hash value or message to sign is in `data`, of + * size `hv_len`; the involved hash function or algorithm is + * identified by `algo_id`. The signature shall be computed and + * written back into `data`; the total size of that buffer is + * `len` bytes. + * + * This callback shall verify that the signature length does not + * exceed `len` bytes, and abstain from writing the signature if + * it does not fit. + * + * The `algo_id` value matches that which was written in the + * `choices` structures by the `choose()` callback. This will be + * one of the following: + * + * - `0xFF00 + id` for a hash function identifier `id`. In + * that case, the `data` buffer contains a hash value + * already computed over the data that is to be signed, + * of length `hv_len`. The `id` may be 0 to designate the + * special MD5+SHA-1 concatenation (old-style RSA signing). + * + * - Another value, lower than `0xFF00`. The `data` buffer + * then contains the raw, non-hashed data to be signed + * (concatenation of the client and server randoms and + * ECDH parameters). The callback is responsible to apply + * any relevant hashing as part of the signing process. + * + * Returned value is the signature length (in bytes), or 0 on error. + * + * \param pctx policy context. + * \param algo_id hash function / algorithm identifier. + * \param data input/output buffer (message/hash, then signature). + * \param hv_len hash value or message length (in bytes). + * \param len total buffer length (in bytes). + * \return signature length (in bytes) on success, or 0 on error. + */ + size_t (*do_sign)(const br_ssl_server_policy_class **pctx, + unsigned algo_id, + unsigned char *data, size_t hv_len, size_t len); +}; + +/** + * \brief A single-chain RSA policy handler. + * + * This policy context uses a single certificate chain, and a RSA + * private key. The context can be restricted to only signatures or + * only key exchange. + * + * Apart from the first field (vtable pointer), its contents are + * opaque and shall not be accessed directly. + */ +typedef struct { + /** \brief Pointer to vtable. */ + const br_ssl_server_policy_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + const br_x509_certificate *chain; + size_t chain_len; + const br_rsa_private_key *sk; + unsigned allowed_usages; + br_rsa_private irsacore; + br_rsa_pkcs1_sign irsasign; +#endif +} br_ssl_server_policy_rsa_context; + +/** + * \brief A single-chain EC policy handler. + * + * This policy context uses a single certificate chain, and an EC + * private key. The context can be restricted to only signatures or + * only key exchange. + * + * Due to how TLS is defined, this context must be made aware whether + * the server certificate was itself signed with RSA or ECDSA. The code + * does not try to decode the certificate to obtain that information. + * + * Apart from the first field (vtable pointer), its contents are + * opaque and shall not be accessed directly. + */ +typedef struct { + /** \brief Pointer to vtable. */ + const br_ssl_server_policy_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + const br_x509_certificate *chain; + size_t chain_len; + const br_ec_private_key *sk; + unsigned allowed_usages; + unsigned cert_issuer_key_type; + const br_multihash_context *mhash; + const br_ec_impl *iec; + br_ecdsa_sign iecdsa; +#endif +} br_ssl_server_policy_ec_context; + +/** + * \brief Class type for a session parameter cache. + * + * Session parameters are saved in the cache with `save()`, and + * retrieved with `load()`. The cache implementation can apply any + * storage and eviction strategy that it sees fit. The SSL server + * context that performs the request is provided, so that its + * functionalities may be used by the implementation (e.g. hash + * functions or random number generation). + */ +typedef struct br_ssl_session_cache_class_ br_ssl_session_cache_class; +struct br_ssl_session_cache_class_ { + /** + * \brief Context size (in bytes). + */ + size_t context_size; + + /** + * \brief Record a session. + * + * This callback should record the provided session parameters. + * The `params` structure is transient, so its contents shall + * be copied into the cache. The session ID has been randomly + * generated and always has length exactly 32 bytes. + * + * \param ctx session cache context. + * \param server_ctx SSL server context. + * \param params session parameters to save. + */ + void (*save)(const br_ssl_session_cache_class **ctx, + br_ssl_server_context *server_ctx, + const br_ssl_session_parameters *params); + + /** + * \brief Lookup a session in the cache. + * + * The session ID to lookup is in `params` and always has length + * exactly 32 bytes. If the session parameters are found in the + * cache, then the parameters shall be copied into the `params` + * structure. Returned value is 1 on successful lookup, 0 + * otherwise. + * + * \param ctx session cache context. + * \param server_ctx SSL server context. + * \param params destination for session parameters. + * \return 1 if found, 0 otherwise. + */ + int (*load)(const br_ssl_session_cache_class **ctx, + br_ssl_server_context *server_ctx, + br_ssl_session_parameters *params); +}; + +/** + * \brief Context for a basic cache system. + * + * The system stores session parameters in a buffer provided at + * initialisation time. Each entry uses exactly 100 bytes, and + * buffer sizes up to 4294967295 bytes are supported. + * + * Entries are evicted with a LRU (Least Recently Used) policy. A + * search tree is maintained to keep lookups fast even with large + * caches. + * + * Apart from the first field (vtable pointer), the structure + * contents are opaque and shall not be accessed directly. + */ +typedef struct { + /** \brief Pointer to vtable. */ + const br_ssl_session_cache_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + unsigned char *store; + size_t store_len, store_ptr; + unsigned char index_key[32]; + const br_hash_class *hash; + int init_done; + uint32_t head, tail, root; +#endif +} br_ssl_session_cache_lru; + +/** + * \brief Initialise a LRU session cache with the provided storage space. + * + * The provided storage space must remain valid as long as the cache + * is used. Arbitrary lengths are supported, up to 4294967295 bytes; + * each entry uses up exactly 100 bytes. + * + * \param cc session cache context. + * \param store storage space for cached entries. + * \param store_len storage space length (in bytes). + */ +void br_ssl_session_cache_lru_init(br_ssl_session_cache_lru *cc, + unsigned char *store, size_t store_len); + +/** + * \brief Forget an entry in an LRU session cache. + * + * The session cache context must have been initialised. The entry + * with the provided session ID (of exactly 32 bytes) is looked for + * in the cache; if located, it is disabled. + * + * \param cc session cache context. + * \param id session ID to forget. + */ +void br_ssl_session_cache_lru_forget( + br_ssl_session_cache_lru *cc, const unsigned char *id); + +/** + * \brief Context structure for a SSL server. + * + * The first field (called `eng`) is the SSL engine; all functions that + * work on a `br_ssl_engine_context` structure shall take as parameter + * a pointer to that field. The other structure fields are opaque and + * must not be accessed directly. + */ +struct br_ssl_server_context_ { + /** + * \brief The encapsulated engine context. + */ + br_ssl_engine_context eng; + +#ifndef BR_DOXYGEN_IGNORE + /* + * Maximum version from the client. + */ + uint16_t client_max_version; + + /* + * Session cache. + */ + const br_ssl_session_cache_class **cache_vtable; + + /* + * Translated cipher suites supported by the client. The list + * is trimmed to include only the cipher suites that the + * server also supports; they are in the same order as in the + * client message. + */ + br_suite_translated client_suites[BR_MAX_CIPHER_SUITES]; + unsigned char client_suites_num; + + /* + * Hash functions supported by the client, with ECDSA and RSA + * (bit mask). For hash function with id 'x', set bit index is + * x for RSA, x+8 for ECDSA. For newer algorithms, with ID + * 0x08**, bit 16+k is set for algorithm 0x0800+k. + */ + uint32_t hashes; + + /* + * Curves supported by the client (bit mask, for named curves). + */ + uint32_t curves; + + /* + * Context for chain handler. + */ + const br_ssl_server_policy_class **policy_vtable; + uint16_t sign_hash_id; + + /* + * For the core handlers, thus avoiding (in most cases) the + * need for an externally provided policy context. + */ + union { + const br_ssl_server_policy_class *vtable; + br_ssl_server_policy_rsa_context single_rsa; + br_ssl_server_policy_ec_context single_ec; + } chain_handler; + + /* + * Buffer for the ECDHE private key. + */ + unsigned char ecdhe_key[70]; + size_t ecdhe_key_len; + + /* + * Trust anchor names for client authentication. "ta_names" and + * "tas" cannot be both non-NULL. + */ + const br_x500_name *ta_names; + const br_x509_trust_anchor *tas; + size_t num_tas; + size_t cur_dn_index; + const unsigned char *cur_dn; + size_t cur_dn_len; + + /* + * Buffer for the hash value computed over all handshake messages + * prior to CertificateVerify, and identifier for the hash function. + */ + unsigned char hash_CV[64]; + size_t hash_CV_len; + int hash_CV_id; + + /* + * Server-specific implementations. + * (none for now) + */ +#endif +}; + +/* + * Each br_ssl_server_init_xxx() function sets the list of supported + * cipher suites and used implementations, as specified by the profile + * name 'xxx'. Defined profile names are: + * + * full_rsa all supported algorithm, server key type is RSA + * full_ec all supported algorithm, server key type is EC + * TODO: add other profiles + * + * Naming scheme for "minimal" profiles: min123 + * + * -- character 1: key exchange + * r = RSA + * e = ECDHE_RSA + * f = ECDHE_ECDSA + * u = ECDH_RSA + * v = ECDH_ECDSA + * -- character 2: version / PRF + * 0 = TLS 1.0 / 1.1 with MD5+SHA-1 + * 2 = TLS 1.2 with SHA-256 + * 3 = TLS 1.2 with SHA-384 + * -- character 3: encryption + * a = AES/CBC + * d = 3DES/CBC + * g = AES/GCM + * c = ChaCha20+Poly1305 + */ + +/** + * \brief SSL server profile: full_rsa. + * + * This function initialises the provided SSL server context with + * all supported algorithms and cipher suites that rely on a RSA + * key pair. + * + * \param cc server context to initialise. + * \param chain server certificate chain. + * \param chain_len certificate chain length (number of certificate). + * \param sk RSA private key. + */ +void br_ssl_server_init_full_rsa(br_ssl_server_context *cc, + const br_x509_certificate *chain, size_t chain_len, + const br_rsa_private_key *sk); + +/** + * \brief SSL server profile: full_ec. + * + * This function initialises the provided SSL server context with + * all supported algorithms and cipher suites that rely on an EC + * key pair. + * + * The key type of the CA that issued the server's certificate must + * be provided, since it matters for ECDH cipher suites (ECDH_RSA + * suites require a RSA-powered CA). The key type is either + * `BR_KEYTYPE_RSA` or `BR_KEYTYPE_EC`. + * + * \param cc server context to initialise. + * \param chain server certificate chain. + * \param chain_len chain length (number of certificates). + * \param cert_issuer_key_type certificate issuer's key type. + * \param sk EC private key. + */ +void br_ssl_server_init_full_ec(br_ssl_server_context *cc, + const br_x509_certificate *chain, size_t chain_len, + unsigned cert_issuer_key_type, const br_ec_private_key *sk); + +/** + * \brief SSL server profile: minr2g. + * + * This profile uses only TLS_RSA_WITH_AES_128_GCM_SHA256. Server key is + * RSA, and RSA key exchange is used (not forward secure, but uses little + * CPU in the client). + * + * \param cc server context to initialise. + * \param chain server certificate chain. + * \param chain_len certificate chain length (number of certificate). + * \param sk RSA private key. + */ +void br_ssl_server_init_minr2g(br_ssl_server_context *cc, + const br_x509_certificate *chain, size_t chain_len, + const br_rsa_private_key *sk); + +/** + * \brief SSL server profile: mine2g. + * + * This profile uses only TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256. Server key + * is RSA, and ECDHE key exchange is used. This suite provides forward + * security, with a higher CPU expense on the client, and a somewhat + * larger code footprint (compared to "minr2g"). + * + * \param cc server context to initialise. + * \param chain server certificate chain. + * \param chain_len certificate chain length (number of certificate). + * \param sk RSA private key. + */ +void br_ssl_server_init_mine2g(br_ssl_server_context *cc, + const br_x509_certificate *chain, size_t chain_len, + const br_rsa_private_key *sk); + +/** + * \brief SSL server profile: minf2g. + * + * This profile uses only TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256. + * Server key is EC, and ECDHE key exchange is used. This suite provides + * forward security, with a higher CPU expense on the client and server + * (by a factor of about 3 to 4), and a somewhat larger code footprint + * (compared to "minu2g" and "minv2g"). + * + * \param cc server context to initialise. + * \param chain server certificate chain. + * \param chain_len certificate chain length (number of certificate). + * \param sk EC private key. + */ +void br_ssl_server_init_minf2g(br_ssl_server_context *cc, + const br_x509_certificate *chain, size_t chain_len, + const br_ec_private_key *sk); + +/** + * \brief SSL server profile: minu2g. + * + * This profile uses only TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256. + * Server key is EC, and ECDH key exchange is used; the issuing CA used + * a RSA key. + * + * The "minu2g" and "minv2g" profiles do not provide forward secrecy, + * but are the lightest on the server (for CPU usage), and are rather + * inexpensive on the client as well. + * + * \param cc server context to initialise. + * \param chain server certificate chain. + * \param chain_len certificate chain length (number of certificate). + * \param sk EC private key. + */ +void br_ssl_server_init_minu2g(br_ssl_server_context *cc, + const br_x509_certificate *chain, size_t chain_len, + const br_ec_private_key *sk); + +/** + * \brief SSL server profile: minv2g. + * + * This profile uses only TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256. + * Server key is EC, and ECDH key exchange is used; the issuing CA used + * an EC key. + * + * The "minu2g" and "minv2g" profiles do not provide forward secrecy, + * but are the lightest on the server (for CPU usage), and are rather + * inexpensive on the client as well. + * + * \param cc server context to initialise. + * \param chain server certificate chain. + * \param chain_len certificate chain length (number of certificate). + * \param sk EC private key. + */ +void br_ssl_server_init_minv2g(br_ssl_server_context *cc, + const br_x509_certificate *chain, size_t chain_len, + const br_ec_private_key *sk); + +/** + * \brief SSL server profile: mine2c. + * + * This profile uses only TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256. + * Server key is RSA, and ECDHE key exchange is used. This suite + * provides forward security. + * + * \param cc server context to initialise. + * \param chain server certificate chain. + * \param chain_len certificate chain length (number of certificate). + * \param sk RSA private key. + */ +void br_ssl_server_init_mine2c(br_ssl_server_context *cc, + const br_x509_certificate *chain, size_t chain_len, + const br_rsa_private_key *sk); + +/** + * \brief SSL server profile: minf2c. + * + * This profile uses only TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256. + * Server key is EC, and ECDHE key exchange is used. This suite provides + * forward security. + * + * \param cc server context to initialise. + * \param chain server certificate chain. + * \param chain_len certificate chain length (number of certificate). + * \param sk EC private key. + */ +void br_ssl_server_init_minf2c(br_ssl_server_context *cc, + const br_x509_certificate *chain, size_t chain_len, + const br_ec_private_key *sk); + +/** + * \brief Get the supported client suites. + * + * This function shall be called only after the ClientHello has been + * processed, typically from the policy engine. The returned array + * contains the cipher suites that are supported by both the client + * and the server; these suites are in client preference order, unless + * the `BR_OPT_ENFORCE_SERVER_PREFERENCES` flag was set, in which case + * they are in server preference order. + * + * The suites are _translated_, which means that each suite is given + * as two 16-bit integers: the standard suite identifier, and its + * translated version, broken down into its individual components, + * as explained with the `br_suite_translated` type. + * + * The returned array is allocated in the context and will be rewritten + * by each handshake. + * + * \param cc server context. + * \param num receives the array size (number of suites). + * \return the translated common cipher suites, in preference order. + */ +static inline const br_suite_translated * +br_ssl_server_get_client_suites(const br_ssl_server_context *cc, size_t *num) +{ + *num = cc->client_suites_num; + return cc->client_suites; +} + +/** + * \brief Get the hash functions and signature algorithms supported by + * the client. + * + * This value is a bit field: + * + * - If RSA (PKCS#1 v1.5) is supported with hash function of ID `x`, + * then bit `x` is set (hash function ID is 0 for the special MD5+SHA-1, + * or 2 to 6 for the SHA family). + * + * - If ECDSA is supported with hash function of ID `x`, then bit `8+x` + * is set. + * + * - Newer algorithms are symbolic 16-bit identifiers that do not + * represent signature algorithm and hash function separately. If + * the TLS-level identifier is `0x0800+x` for a `x` in the 0..15 + * range, then bit `16+x` is set. + * + * "New algorithms" are currently defined only in draft documents, so + * this support is subject to possible change. Right now (early 2017), + * this maps ed25519 (EdDSA on Curve25519) to bit 23, and ed448 (EdDSA + * on Curve448) to bit 24. If the identifiers on the wire change in + * future document, then the decoding mechanism in BearSSL will be + * amended to keep mapping ed25519 and ed448 on bits 23 and 24, + * respectively. Mapping of other new algorithms (e.g. RSA/PSS) is not + * guaranteed yet. + * + * \param cc server context. + * \return the client-supported hash functions and signature algorithms. + */ +static inline uint32_t +br_ssl_server_get_client_hashes(const br_ssl_server_context *cc) +{ + return cc->hashes; +} + +/** + * \brief Get the elliptic curves supported by the client. + * + * This is a bit field (bit x is set if curve of ID x is supported). + * + * \param cc server context. + * \return the client-supported elliptic curves. + */ +static inline uint32_t +br_ssl_server_get_client_curves(const br_ssl_server_context *cc) +{ + return cc->curves; +} + +/** + * \brief Clear the complete contents of a SSL server context. + * + * Everything is cleared, including the reference to the configured buffer, + * implementations, cipher suites and state. This is a preparatory step + * to assembling a custom profile. + * + * \param cc server context to clear. + */ +void br_ssl_server_zero(br_ssl_server_context *cc); + +/** + * \brief Set an externally provided policy context. + * + * The policy context's methods are invoked to decide the cipher suite + * and certificate chain, and to perform operations involving the server's + * private key. + * + * \param cc server context. + * \param pctx policy context (pointer to its vtable field). + */ +static inline void +br_ssl_server_set_policy(br_ssl_server_context *cc, + const br_ssl_server_policy_class **pctx) +{ + cc->policy_vtable = pctx; +} + +/** + * \brief Set the server certificate chain and key (single RSA case). + * + * This function uses a policy context included in the server context. + * It configures use of a single server certificate chain with a RSA + * private key. The `allowed_usages` is a combination of usages, namely + * `BR_KEYTYPE_KEYX` and/or `BR_KEYTYPE_SIGN`; this enables or disables + * the corresponding cipher suites (i.e. `TLS_RSA_*` use the RSA key for + * key exchange, while `TLS_ECDHE_RSA_*` use the RSA key for signatures). + * + * \param cc server context. + * \param chain server certificate chain to send to the client. + * \param chain_len chain length (number of certificates). + * \param sk server private key (RSA). + * \param allowed_usages allowed private key usages. + * \param irsacore RSA core implementation. + * \param irsasign RSA signature implementation (PKCS#1 v1.5). + */ +void br_ssl_server_set_single_rsa(br_ssl_server_context *cc, + const br_x509_certificate *chain, size_t chain_len, + const br_rsa_private_key *sk, unsigned allowed_usages, + br_rsa_private irsacore, br_rsa_pkcs1_sign irsasign); + +/** + * \brief Set the server certificate chain and key (single EC case). + * + * This function uses a policy context included in the server context. + * It configures use of a single server certificate chain with an EC + * private key. The `allowed_usages` is a combination of usages, namely + * `BR_KEYTYPE_KEYX` and/or `BR_KEYTYPE_SIGN`; this enables or disables + * the corresponding cipher suites (i.e. `TLS_ECDH_*` use the EC key for + * key exchange, while `TLS_ECDHE_ECDSA_*` use the EC key for signatures). + * + * In order to support `TLS_ECDH_*` cipher suites (non-ephemeral ECDH), + * the algorithm type of the key used by the issuing CA to sign the + * server's certificate must be provided, as `cert_issuer_key_type` + * parameter (this value is either `BR_KEYTYPE_RSA` or `BR_KEYTYPE_EC`). + * + * \param cc server context. + * \param chain server certificate chain to send. + * \param chain_len chain length (number of certificates). + * \param sk server private key (EC). + * \param allowed_usages allowed private key usages. + * \param cert_issuer_key_type issuing CA's key type. + * \param iec EC core implementation. + * \param iecdsa ECDSA signature implementation ("asn1" format). + */ +void br_ssl_server_set_single_ec(br_ssl_server_context *cc, + const br_x509_certificate *chain, size_t chain_len, + const br_ec_private_key *sk, unsigned allowed_usages, + unsigned cert_issuer_key_type, + const br_ec_impl *iec, br_ecdsa_sign iecdsa); + +/** + * \brief Activate client certificate authentication. + * + * The trust anchor encoded X.500 names (DN) to send to the client are + * provided. A client certificate will be requested and validated through + * the X.509 validator configured in the SSL engine. If `num` is 0, then + * client certificate authentication is disabled. + * + * If the client does not send a certificate, or on validation failure, + * the handshake aborts. Unauthenticated clients can be tolerated by + * setting the `BR_OPT_TOLERATE_NO_CLIENT_AUTH` flag. + * + * The provided array is linked in, not copied, so that pointer must + * remain valid as long as anchor names may be used. + * + * \param cc server context. + * \param ta_names encoded trust anchor names. + * \param num number of encoded trust anchor names. + */ +static inline void +br_ssl_server_set_trust_anchor_names(br_ssl_server_context *cc, + const br_x500_name *ta_names, size_t num) +{ + cc->ta_names = ta_names; + cc->tas = NULL; + cc->num_tas = num; +} + +/** + * \brief Activate client certificate authentication. + * + * This is a variant for `br_ssl_server_set_trust_anchor_names()`: the + * trust anchor names are provided not as an array of stand-alone names + * (`br_x500_name` structures), but as an array of trust anchors + * (`br_x509_trust_anchor` structures). The server engine itself will + * only use the `dn` field of each trust anchor. This is meant to allow + * defining a single array of trust anchors, to be used here and in the + * X.509 validation engine itself. + * + * The provided array is linked in, not copied, so that pointer must + * remain valid as long as anchor names may be used. + * + * \param cc server context. + * \param tas trust anchors (only names are used). + * \param num number of trust anchors. + */ +static inline void +br_ssl_server_set_trust_anchor_names_alt(br_ssl_server_context *cc, + const br_x509_trust_anchor *tas, size_t num) +{ + cc->ta_names = NULL; + cc->tas = tas; + cc->num_tas = num; +} + +/** + * \brief Configure the cache for session parameters. + * + * The cache context is provided as a pointer to its first field (vtable + * pointer). + * + * \param cc server context. + * \param vtable session cache context. + */ +static inline void +br_ssl_server_set_cache(br_ssl_server_context *cc, + const br_ssl_session_cache_class **vtable) +{ + cc->cache_vtable = vtable; +} + +/** + * \brief Prepare or reset a server context for handling an incoming client. + * + * \param cc server context. + * \return 1 on success, 0 on error. + */ +int br_ssl_server_reset(br_ssl_server_context *cc); + +/* ===================================================================== */ + +/* + * Context for the simplified I/O context. The transport medium is accessed + * through the low_read() and low_write() callback functions, each with + * its own opaque context pointer. + * + * low_read() read some bytes, at most 'len' bytes, into data[]. The + * returned value is the number of read bytes, or -1 on error. + * The 'len' parameter is guaranteed never to exceed 20000, + * so the length always fits in an 'int' on all platforms. + * + * low_write() write up to 'len' bytes, to be read from data[]. The + * returned value is the number of written bytes, or -1 on + * error. The 'len' parameter is guaranteed never to exceed + * 20000, so the length always fits in an 'int' on all + * parameters. + * + * A socket closure (if the transport medium is a socket) should be reported + * as an error (-1). The callbacks shall endeavour to block until at least + * one byte can be read or written; a callback returning 0 at times is + * acceptable, but this normally leads to the callback being immediately + * called again, so the callback should at least always try to block for + * some time if no I/O can take place. + * + * The SSL engine naturally applies some buffering, so the callbacks need + * not apply buffers of their own. + */ +/** + * \brief Context structure for the simplified SSL I/O wrapper. + * + * This structure is initialised with `br_sslio_init()`. Its contents + * are opaque and shall not be accessed directly. + */ +typedef struct { +#ifndef BR_DOXYGEN_IGNORE + br_ssl_engine_context *engine; + int (*low_read)(void *read_context, + unsigned char *data, size_t len); + void *read_context; + int (*low_write)(void *write_context, + const unsigned char *data, size_t len); + void *write_context; +#endif +} br_sslio_context; + +/** + * \brief Initialise a simplified I/O wrapper context. + * + * The simplified I/O wrapper offers a simpler read/write API for a SSL + * engine (client or server), using the provided callback functions for + * reading data from, or writing data to, the transport medium. + * + * The callback functions have the following semantics: + * + * - Each callback receives an opaque context value (of type `void *`) + * that the callback may use arbitrarily (or possibly ignore). + * + * - `low_read()` reads at least one byte, at most `len` bytes, from + * the transport medium. Read bytes shall be written in `data`. + * + * - `low_write()` writes at least one byte, at most `len` bytes, unto + * the transport medium. The bytes to write are read from `data`. + * + * - The `len` parameter is never zero, and is always lower than 20000. + * + * - The number of processed bytes (read or written) is returned. Since + * that number is less than 20000, it always fits on an `int`. + * + * - On error, the callbacks return -1. Reaching end-of-stream is an + * error. Errors are permanent: the SSL connection is terminated. + * + * - Callbacks SHOULD NOT return 0. This is tolerated, as long as + * callbacks endeavour to block for some non-negligible amount of + * time until at least one byte can be sent or received (if a + * callback returns 0, then the wrapper invokes it again + * immediately). + * + * - Callbacks MAY return as soon as at least one byte is processed; + * they MAY also insist on reading or writing _all_ requested bytes. + * Since SSL is a self-terminated protocol (each record has a length + * header), this does not change semantics. + * + * - Callbacks need not apply any buffering (for performance) since SSL + * itself uses buffers. + * + * \param ctx wrapper context to initialise. + * \param engine SSL engine to wrap. + * \param low_read callback for reading data from the transport. + * \param read_context context pointer for `low_read()`. + * \param low_write callback for writing data on the transport. + * \param write_context context pointer for `low_write()`. + */ +void br_sslio_init(br_sslio_context *ctx, + br_ssl_engine_context *engine, + int (*low_read)(void *read_context, + unsigned char *data, size_t len), + void *read_context, + int (*low_write)(void *write_context, + const unsigned char *data, size_t len), + void *write_context); + +/** + * \brief Read some application data from a SSL connection. + * + * If `len` is zero, then this function returns 0 immediately. In + * all other cases, it never returns 0. + * + * This call returns only when at least one byte has been obtained. + * Returned value is the number of bytes read, or -1 on error. The + * number of bytes always fits on an 'int' (data from a single SSL/TLS + * record is returned). + * + * On error or SSL closure, this function returns -1. The caller should + * inspect the error status on the SSL engine to distinguish between + * normal closure and error. + * + * \param cc SSL wrapper context. + * \param dst destination buffer for application data. + * \param len maximum number of bytes to obtain. + * \return number of bytes obtained, or -1 on error. + */ +int br_sslio_read(br_sslio_context *cc, void *dst, size_t len); + +/** + * \brief Read application data from a SSL connection. + * + * This calls returns only when _all_ requested `len` bytes are read, + * or an error is reached. Returned value is 0 on success, -1 on error. + * A normal (verified) SSL closure before that many bytes are obtained + * is reported as an error by this function. + * + * \param cc SSL wrapper context. + * \param dst destination buffer for application data. + * \param len number of bytes to obtain. + * \return 0 on success, or -1 on error. + */ +int br_sslio_read_all(br_sslio_context *cc, void *dst, size_t len); + +/** + * \brief Write some application data unto a SSL connection. + * + * If `len` is zero, then this function returns 0 immediately. In + * all other cases, it never returns 0. + * + * This call returns only when at least one byte has been written. + * Returned value is the number of bytes written, or -1 on error. The + * number of bytes always fits on an 'int' (less than 20000). + * + * On error or SSL closure, this function returns -1. The caller should + * inspect the error status on the SSL engine to distinguish between + * normal closure and error. + * + * **Important:** SSL is buffered; a "written" byte is a byte that was + * injected into the wrapped SSL engine, but this does not necessarily mean + * that it has been scheduled for sending. Use `br_sslio_flush()` to + * ensure that all pending data has been sent to the transport medium. + * + * \param cc SSL wrapper context. + * \param src source buffer for application data. + * \param len maximum number of bytes to write. + * \return number of bytes written, or -1 on error. + */ +int br_sslio_write(br_sslio_context *cc, const void *src, size_t len); + +/** + * \brief Write application data unto a SSL connection. + * + * This calls returns only when _all_ requested `len` bytes have been + * written, or an error is reached. Returned value is 0 on success, -1 + * on error. A normal (verified) SSL closure before that many bytes are + * written is reported as an error by this function. + * + * **Important:** SSL is buffered; a "written" byte is a byte that was + * injected into the wrapped SSL engine, but this does not necessarily mean + * that it has been scheduled for sending. Use `br_sslio_flush()` to + * ensure that all pending data has been sent to the transport medium. + * + * \param cc SSL wrapper context. + * \param src source buffer for application data. + * \param len number of bytes to write. + * \return 0 on success, or -1 on error. + */ +int br_sslio_write_all(br_sslio_context *cc, const void *src, size_t len); + +/** + * \brief Flush pending data. + * + * This call makes sure that any buffered application data in the + * provided context (including the wrapped SSL engine) has been sent + * to the transport medium (i.e. accepted by the `low_write()` callback + * method). If there is no such pending data, then this function does + * nothing (and returns a success, i.e. 0). + * + * If the underlying transport medium has its own buffers, then it is + * up to the caller to ensure the corresponding flushing. + * + * Returned value is 0 on success, -1 on error. + * + * \param cc SSL wrapper context. + * \return 0 on success, or -1 on error. + */ +int br_sslio_flush(br_sslio_context *cc); + +/** + * \brief Close the SSL connection. + * + * This call runs the SSL closure protocol (sending a `close_notify`, + * receiving the response `close_notify`). When it returns, the SSL + * connection is finished. It is still up to the caller to manage the + * possible transport-level termination, if applicable (alternatively, + * the underlying transport stream may be reused for non-SSL messages). + * + * Returned value is 0 on success, -1 on error. A failure by the peer + * to process the complete closure protocol (i.e. sending back the + * `close_notify`) is an error. + * + * \param cc SSL wrapper context. + * \return 0 on success, or -1 on error. + */ +int br_sslio_close(br_sslio_context *cc); + +/* ===================================================================== */ + +/* + * Symbolic constants for cipher suites. + */ + +/* From RFC 5246 */ +#define BR_TLS_NULL_WITH_NULL_NULL 0x0000 +#define BR_TLS_RSA_WITH_NULL_MD5 0x0001 +#define BR_TLS_RSA_WITH_NULL_SHA 0x0002 +#define BR_TLS_RSA_WITH_NULL_SHA256 0x003B +#define BR_TLS_RSA_WITH_RC4_128_MD5 0x0004 +#define BR_TLS_RSA_WITH_RC4_128_SHA 0x0005 +#define BR_TLS_RSA_WITH_3DES_EDE_CBC_SHA 0x000A +#define BR_TLS_RSA_WITH_AES_128_CBC_SHA 0x002F +#define BR_TLS_RSA_WITH_AES_256_CBC_SHA 0x0035 +#define BR_TLS_RSA_WITH_AES_128_CBC_SHA256 0x003C +#define BR_TLS_RSA_WITH_AES_256_CBC_SHA256 0x003D +#define BR_TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA 0x000D +#define BR_TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA 0x0010 +#define BR_TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA 0x0013 +#define BR_TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA 0x0016 +#define BR_TLS_DH_DSS_WITH_AES_128_CBC_SHA 0x0030 +#define BR_TLS_DH_RSA_WITH_AES_128_CBC_SHA 0x0031 +#define BR_TLS_DHE_DSS_WITH_AES_128_CBC_SHA 0x0032 +#define BR_TLS_DHE_RSA_WITH_AES_128_CBC_SHA 0x0033 +#define BR_TLS_DH_DSS_WITH_AES_256_CBC_SHA 0x0036 +#define BR_TLS_DH_RSA_WITH_AES_256_CBC_SHA 0x0037 +#define BR_TLS_DHE_DSS_WITH_AES_256_CBC_SHA 0x0038 +#define BR_TLS_DHE_RSA_WITH_AES_256_CBC_SHA 0x0039 +#define BR_TLS_DH_DSS_WITH_AES_128_CBC_SHA256 0x003E +#define BR_TLS_DH_RSA_WITH_AES_128_CBC_SHA256 0x003F +#define BR_TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 0x0040 +#define BR_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 0x0067 +#define BR_TLS_DH_DSS_WITH_AES_256_CBC_SHA256 0x0068 +#define BR_TLS_DH_RSA_WITH_AES_256_CBC_SHA256 0x0069 +#define BR_TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 0x006A +#define BR_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 0x006B +#define BR_TLS_DH_anon_WITH_RC4_128_MD5 0x0018 +#define BR_TLS_DH_anon_WITH_3DES_EDE_CBC_SHA 0x001B +#define BR_TLS_DH_anon_WITH_AES_128_CBC_SHA 0x0034 +#define BR_TLS_DH_anon_WITH_AES_256_CBC_SHA 0x003A +#define BR_TLS_DH_anon_WITH_AES_128_CBC_SHA256 0x006C +#define BR_TLS_DH_anon_WITH_AES_256_CBC_SHA256 0x006D + +/* From RFC 4492 */ +#define BR_TLS_ECDH_ECDSA_WITH_NULL_SHA 0xC001 +#define BR_TLS_ECDH_ECDSA_WITH_RC4_128_SHA 0xC002 +#define BR_TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA 0xC003 +#define BR_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA 0xC004 +#define BR_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA 0xC005 +#define BR_TLS_ECDHE_ECDSA_WITH_NULL_SHA 0xC006 +#define BR_TLS_ECDHE_ECDSA_WITH_RC4_128_SHA 0xC007 +#define BR_TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA 0xC008 +#define BR_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA 0xC009 +#define BR_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA 0xC00A +#define BR_TLS_ECDH_RSA_WITH_NULL_SHA 0xC00B +#define BR_TLS_ECDH_RSA_WITH_RC4_128_SHA 0xC00C +#define BR_TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA 0xC00D +#define BR_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA 0xC00E +#define BR_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA 0xC00F +#define BR_TLS_ECDHE_RSA_WITH_NULL_SHA 0xC010 +#define BR_TLS_ECDHE_RSA_WITH_RC4_128_SHA 0xC011 +#define BR_TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA 0xC012 +#define BR_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA 0xC013 +#define BR_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA 0xC014 +#define BR_TLS_ECDH_anon_WITH_NULL_SHA 0xC015 +#define BR_TLS_ECDH_anon_WITH_RC4_128_SHA 0xC016 +#define BR_TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA 0xC017 +#define BR_TLS_ECDH_anon_WITH_AES_128_CBC_SHA 0xC018 +#define BR_TLS_ECDH_anon_WITH_AES_256_CBC_SHA 0xC019 + +/* From RFC 5288 */ +#define BR_TLS_RSA_WITH_AES_128_GCM_SHA256 0x009C +#define BR_TLS_RSA_WITH_AES_256_GCM_SHA384 0x009D +#define BR_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 0x009E +#define BR_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 0x009F +#define BR_TLS_DH_RSA_WITH_AES_128_GCM_SHA256 0x00A0 +#define BR_TLS_DH_RSA_WITH_AES_256_GCM_SHA384 0x00A1 +#define BR_TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 0x00A2 +#define BR_TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 0x00A3 +#define BR_TLS_DH_DSS_WITH_AES_128_GCM_SHA256 0x00A4 +#define BR_TLS_DH_DSS_WITH_AES_256_GCM_SHA384 0x00A5 +#define BR_TLS_DH_anon_WITH_AES_128_GCM_SHA256 0x00A6 +#define BR_TLS_DH_anon_WITH_AES_256_GCM_SHA384 0x00A7 + +/* From RFC 5289 */ +#define BR_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 0xC023 +#define BR_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 0xC024 +#define BR_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 0xC025 +#define BR_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 0xC026 +#define BR_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 0xC027 +#define BR_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 0xC028 +#define BR_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 0xC029 +#define BR_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 0xC02A +#define BR_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 0xC02B +#define BR_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 0xC02C +#define BR_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 0xC02D +#define BR_TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 0xC02E +#define BR_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 0xC02F +#define BR_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 0xC030 +#define BR_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 0xC031 +#define BR_TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 0xC032 + +/* From RFC 6655 and 7251 */ +#define BR_TLS_RSA_WITH_AES_128_CCM 0xC09C +#define BR_TLS_RSA_WITH_AES_256_CCM 0xC09D +#define BR_TLS_RSA_WITH_AES_128_CCM_8 0xC0A0 +#define BR_TLS_RSA_WITH_AES_256_CCM_8 0xC0A1 +#define BR_TLS_ECDHE_ECDSA_WITH_AES_128_CCM 0xC0AC +#define BR_TLS_ECDHE_ECDSA_WITH_AES_256_CCM 0xC0AD +#define BR_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 0xC0AE +#define BR_TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8 0xC0AF + +/* From RFC 7905 */ +#define BR_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 0xCCA8 +#define BR_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 0xCCA9 +#define BR_TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 0xCCAA +#define BR_TLS_PSK_WITH_CHACHA20_POLY1305_SHA256 0xCCAB +#define BR_TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256 0xCCAC +#define BR_TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256 0xCCAD +#define BR_TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256 0xCCAE + +/* From RFC 7507 */ +#define BR_TLS_FALLBACK_SCSV 0x5600 + +/* + * Symbolic constants for alerts. + */ +#define BR_ALERT_CLOSE_NOTIFY 0 +#define BR_ALERT_UNEXPECTED_MESSAGE 10 +#define BR_ALERT_BAD_RECORD_MAC 20 +#define BR_ALERT_RECORD_OVERFLOW 22 +#define BR_ALERT_DECOMPRESSION_FAILURE 30 +#define BR_ALERT_HANDSHAKE_FAILURE 40 +#define BR_ALERT_BAD_CERTIFICATE 42 +#define BR_ALERT_UNSUPPORTED_CERTIFICATE 43 +#define BR_ALERT_CERTIFICATE_REVOKED 44 +#define BR_ALERT_CERTIFICATE_EXPIRED 45 +#define BR_ALERT_CERTIFICATE_UNKNOWN 46 +#define BR_ALERT_ILLEGAL_PARAMETER 47 +#define BR_ALERT_UNKNOWN_CA 48 +#define BR_ALERT_ACCESS_DENIED 49 +#define BR_ALERT_DECODE_ERROR 50 +#define BR_ALERT_DECRYPT_ERROR 51 +#define BR_ALERT_PROTOCOL_VERSION 70 +#define BR_ALERT_INSUFFICIENT_SECURITY 71 +#define BR_ALERT_INTERNAL_ERROR 80 +#define BR_ALERT_USER_CANCELED 90 +#define BR_ALERT_NO_RENEGOTIATION 100 +#define BR_ALERT_UNSUPPORTED_EXTENSION 110 +#define BR_ALERT_NO_APPLICATION_PROTOCOL 120 + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/bearssl-esp8266/src/t_bearssl_tasmota_config.h b/lib/bearssl-esp8266/src/t_bearssl_tasmota_config.h new file mode 100644 index 000000000..fdf4f8f13 --- /dev/null +++ b/lib/bearssl-esp8266/src/t_bearssl_tasmota_config.h @@ -0,0 +1,30 @@ +// do not delete + +#ifndef BEARSSL_TASMOTA_CONFIG +#define BEARSSL_TASMOTA_CONFIG + +#ifndef __ets__ +#define __ets__ +#endif + +#ifndef ICACHE_FLASH +#define ICACHE_FLASH +#endif + +#ifndef ESP8266 +#define ESP8266 +#endif + +#ifndef BR_SLOW_MUL15 +#define BR_SLOW_MUL15 1 // shrinks EC code by 8.5k +#endif + +#ifndef BR_MAX_RSA_SIZE +#define BR_MAX_RSA_SIZE 2048 // max 2048 bits RSA keys +#endif + +#ifndef BR_MAX_EC_SIZE +#define BR_MAX_EC_SIZE 256 // max 256 bits EC keys +#endif + +#endif diff --git a/lib/bearssl-esp8266/src/t_bearssl_x509.h b/lib/bearssl-esp8266/src/t_bearssl_x509.h new file mode 100644 index 000000000..47ed4e505 --- /dev/null +++ b/lib/bearssl-esp8266/src/t_bearssl_x509.h @@ -0,0 +1,1592 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef BR_BEARSSL_X509_H__ +#define BR_BEARSSL_X509_H__ + +#include +#include + +#include "t_bearssl_ec.h" +#include "t_bearssl_hash.h" +#include "t_bearssl_rsa.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** \file bearssl_x509.h + * + * # X.509 Certificate Chain Processing + * + * An X.509 processing engine receives an X.509 chain, chunk by chunk, + * as received from a SSL/TLS client or server (the client receives the + * server's certificate chain, and the server receives the client's + * certificate chain if it requested a client certificate). The chain + * is thus injected in the engine in SSL order (end-entity first). + * + * The engine's job is to return the public key to use for SSL/TLS. + * How exactly that key is obtained and verified is entirely up to the + * engine. + * + * **The "known key" engine** returns a public key which is already known + * from out-of-band information (e.g. the client _remembers_ the key from + * a previous connection, as in the usual SSH model). This is the simplest + * engine since it simply ignores the chain, thereby avoiding the need + * for any decoding logic. + * + * **The "minimal" engine** implements minimal X.509 decoding and chain + * validation: + * + * - The provided chain should validate "as is". There is no attempt + * at reordering, skipping or downloading extra certificates. + * + * - X.509 v1, v2 and v3 certificates are supported. + * + * - Trust anchors are a DN and a public key. Each anchor is either a + * "CA" anchor, or a non-CA. + * + * - If the end-entity certificate matches a non-CA anchor (subject DN + * is equal to the non-CA name, and public key is also identical to + * the anchor key), then this is a _direct trust_ case and the + * remaining certificates are ignored. + * + * - Unless direct trust is applied, the chain must be verifiable up to + * a certificate whose issuer DN matches the DN from a "CA" trust anchor, + * and whose signature is verifiable against that anchor's public key. + * Subsequent certificates in the chain are ignored. + * + * - The engine verifies subject/issuer DN matching, and enforces + * processing of Basic Constraints and Key Usage extensions. The + * Authority Key Identifier, Subject Key Identifier, Issuer Alt Name, + * Subject Directory Attribute, CRL Distribution Points, Freshest CRL, + * Authority Info Access and Subject Info Access extensions are + * ignored. The Subject Alt Name is decoded for the end-entity + * certificate under some conditions (see below). Other extensions + * are ignored if non-critical, or imply chain rejection if critical. + * + * - The Subject Alt Name extension is parsed for names of type `dNSName` + * when decoding the end-entity certificate, and only if there is a + * server name to match. If there is no SAN extension, then the + * Common Name from the subjectDN is used. That name matching is + * case-insensitive and honours a single starting wildcard (i.e. if + * the name in the certificate starts with "`*.`" then this matches + * any word as first element). Note: this name matching is performed + * also in the "direct trust" model. + * + * - DN matching is byte-to-byte equality (a future version might + * include some limited processing for case-insensitive matching and + * whitespace normalisation). + * + * - Successful validation produces a public key type but also a set + * of allowed usages (`BR_KEYTYPE_KEYX` and/or `BR_KEYTYPE_SIGN`). + * The caller is responsible for checking that the key type and + * usages are compatible with the expected values (e.g. with the + * selected cipher suite, when the client validates the server's + * certificate). + * + * **Important caveats:** + * + * - The "minimal" engine does not check revocation status. The relevant + * extensions are ignored, and CRL or OCSP responses are not gathered + * or checked. + * + * - The "minimal" engine does not currently support Name Constraints + * (some basic functionality to handle sub-domains may be added in a + * later version). + * + * - The decoder is not "validating" in the sense that it won't reject + * some certificates with invalid field values when these fields are + * not actually processed. + */ + +/* + * X.509 error codes are in the 32..63 range. + */ + +/** \brief X.509 status: validation was successful; this is not actually + an error. */ +#define BR_ERR_X509_OK 32 + +/** \brief X.509 status: invalid value in an ASN.1 structure. */ +#define BR_ERR_X509_INVALID_VALUE 33 + +/** \brief X.509 status: truncated certificate. */ +#define BR_ERR_X509_TRUNCATED 34 + +/** \brief X.509 status: empty certificate chain (no certificate at all). */ +#define BR_ERR_X509_EMPTY_CHAIN 35 + +/** \brief X.509 status: decoding error: inner element extends beyond + outer element size. */ +#define BR_ERR_X509_INNER_TRUNC 36 + +/** \brief X.509 status: decoding error: unsupported tag class (application + or private). */ +#define BR_ERR_X509_BAD_TAG_CLASS 37 + +/** \brief X.509 status: decoding error: unsupported tag value. */ +#define BR_ERR_X509_BAD_TAG_VALUE 38 + +/** \brief X.509 status: decoding error: indefinite length. */ +#define BR_ERR_X509_INDEFINITE_LENGTH 39 + +/** \brief X.509 status: decoding error: extraneous element. */ +#define BR_ERR_X509_EXTRA_ELEMENT 40 + +/** \brief X.509 status: decoding error: unexpected element. */ +#define BR_ERR_X509_UNEXPECTED 41 + +/** \brief X.509 status: decoding error: expected constructed element, but + is primitive. */ +#define BR_ERR_X509_NOT_CONSTRUCTED 42 + +/** \brief X.509 status: decoding error: expected primitive element, but + is constructed. */ +#define BR_ERR_X509_NOT_PRIMITIVE 43 + +/** \brief X.509 status: decoding error: BIT STRING length is not multiple + of 8. */ +#define BR_ERR_X509_PARTIAL_BYTE 44 + +/** \brief X.509 status: decoding error: BOOLEAN value has invalid length. */ +#define BR_ERR_X509_BAD_BOOLEAN 45 + +/** \brief X.509 status: decoding error: value is off-limits. */ +#define BR_ERR_X509_OVERFLOW 46 + +/** \brief X.509 status: invalid distinguished name. */ +#define BR_ERR_X509_BAD_DN 47 + +/** \brief X.509 status: invalid date/time representation. */ +#define BR_ERR_X509_BAD_TIME 48 + +/** \brief X.509 status: certificate contains unsupported features that + cannot be ignored. */ +#define BR_ERR_X509_UNSUPPORTED 49 + +/** \brief X.509 status: key or signature size exceeds internal limits. */ +#define BR_ERR_X509_LIMIT_EXCEEDED 50 + +/** \brief X.509 status: key type does not match that which was expected. */ +#define BR_ERR_X509_WRONG_KEY_TYPE 51 + +/** \brief X.509 status: signature is invalid. */ +#define BR_ERR_X509_BAD_SIGNATURE 52 + +/** \brief X.509 status: validation time is unknown. */ +#define BR_ERR_X509_TIME_UNKNOWN 53 + +/** \brief X.509 status: certificate is expired or not yet valid. */ +#define BR_ERR_X509_EXPIRED 54 + +/** \brief X.509 status: issuer/subject DN mismatch in the chain. */ +#define BR_ERR_X509_DN_MISMATCH 55 + +/** \brief X.509 status: expected server name was not found in the chain. */ +#define BR_ERR_X509_BAD_SERVER_NAME 56 + +/** \brief X.509 status: unknown critical extension in certificate. */ +#define BR_ERR_X509_CRITICAL_EXTENSION 57 + +/** \brief X.509 status: not a CA, or path length constraint violation */ +#define BR_ERR_X509_NOT_CA 58 + +/** \brief X.509 status: Key Usage extension prohibits intended usage. */ +#define BR_ERR_X509_FORBIDDEN_KEY_USAGE 59 + +/** \brief X.509 status: public key found in certificate is too small. */ +#define BR_ERR_X509_WEAK_PUBLIC_KEY 60 + +/** \brief X.509 status: chain could not be linked to a trust anchor. */ +#define BR_ERR_X509_NOT_TRUSTED 62 + +/** + * \brief Aggregate structure for public keys. + */ +typedef struct { + /** \brief Key type: `BR_KEYTYPE_RSA` or `BR_KEYTYPE_EC` */ + unsigned char key_type; + /** \brief Actual public key. */ + union { + /** \brief RSA public key. */ + br_rsa_public_key rsa; + /** \brief EC public key. */ + br_ec_public_key ec; + } key; +} br_x509_pkey; + +/** + * \brief Distinguished Name (X.500) structure. + * + * The DN is DER-encoded. + */ +typedef struct { + /** \brief Encoded DN data. */ + unsigned char *data; + /** \brief Encoded DN length (in bytes). */ + size_t len; +} br_x500_name; + +/** + * \brief Trust anchor structure. + */ +typedef struct { + /** \brief Encoded DN (X.500 name). */ + br_x500_name dn; + /** \brief Anchor flags (e.g. `BR_X509_TA_CA`). */ + unsigned flags; + /** \brief Anchor public key. */ + br_x509_pkey pkey; +} br_x509_trust_anchor; + +/** + * \brief Trust anchor flag: CA. + * + * A "CA" anchor is deemed fit to verify signatures on certificates. + * A "non-CA" anchor is accepted only for direct trust (server's + * certificate name and key match the anchor). + */ +#define BR_X509_TA_CA 0x0001 + +/* + * Key type: combination of a basic key type (low 4 bits) and some + * optional flags. + * + * For a public key, the basic key type only is set. + * + * For an expected key type, the flags indicate the intended purpose(s) + * for the key; the basic key type may be set to 0 to indicate that any + * key type compatible with the indicated purpose is acceptable. + */ +/** \brief Key type: algorithm is RSA. */ +#define BR_KEYTYPE_RSA 1 +/** \brief Key type: algorithm is EC. */ +#define BR_KEYTYPE_EC 2 + +/** + * \brief Key type: usage is "key exchange". + * + * This value is combined (with bitwise OR) with the algorithm + * (`BR_KEYTYPE_RSA` or `BR_KEYTYPE_EC`) when informing the X.509 + * validation engine that it should find a public key of that type, + * fit for key exchanges (e.g. `TLS_RSA_*` and `TLS_ECDH_*` cipher + * suites). + */ +#define BR_KEYTYPE_KEYX 0x10 + +/** + * \brief Key type: usage is "signature". + * + * This value is combined (with bitwise OR) with the algorithm + * (`BR_KEYTYPE_RSA` or `BR_KEYTYPE_EC`) when informing the X.509 + * validation engine that it should find a public key of that type, + * fit for signatures (e.g. `TLS_ECDHE_*` cipher suites). + */ +#define BR_KEYTYPE_SIGN 0x20 + +/* + * start_chain Called when a new chain is started. If 'server_name' + * is not NULL and non-empty, then it is a name that + * should be looked for in the EE certificate (in the + * SAN extension as dNSName, or in the subjectDN's CN + * if there is no SAN extension). + * The caller ensures that the provided 'server_name' + * pointer remains valid throughout validation. + * + * start_cert Begins a new certificate in the chain. The provided + * length is in bytes; this is the total certificate length. + * + * append Get some additional bytes for the current certificate. + * + * end_cert Ends the current certificate. + * + * end_chain Called at the end of the chain. Returned value is + * 0 on success, or a non-zero error code. + * + * get_pkey Returns the EE certificate public key. + * + * For a complete chain, start_chain() and end_chain() are always + * called. For each certificate, start_cert(), some append() calls, then + * end_cert() are called, in that order. There may be no append() call + * at all if the certificate is empty (which is not valid but may happen + * if the peer sends exactly that). + * + * get_pkey() shall return a pointer to a structure that is valid as + * long as a new chain is not started. This may be a sub-structure + * within the context for the engine. This function MAY return a valid + * pointer to a public key even in some cases of validation failure, + * depending on the validation engine. + */ + +/** + * \brief Class type for an X.509 engine. + * + * A certificate chain validation uses a caller-allocated context, which + * contains the running state for that validation. Methods are called + * in due order: + * + * - `start_chain()` is called at the start of the validation. + * - Certificates are processed one by one, in SSL order (end-entity + * comes first). For each certificate, the following methods are + * called: + * + * - `start_cert()` at the beginning of the certificate. + * - `append()` is called zero, one or more times, to provide + * the certificate (possibly in chunks). + * - `end_cert()` at the end of the certificate. + * + * - `end_chain()` is called when the last certificate in the chain + * was processed. + * - `get_pkey()` is called after chain processing, if the chain + * validation was successful. + * + * A context structure may be reused; the `start_chain()` method shall + * ensure (re)initialisation. + */ +typedef struct br_x509_class_ br_x509_class; +struct br_x509_class_ { + /** + * \brief X.509 context size, in bytes. + */ + size_t context_size; + + /** + * \brief Start a new chain. + * + * This method shall set the vtable (first field) of the context + * structure. + * + * The `server_name`, if not `NULL`, will be considered as a + * fully qualified domain name, to be matched against the `dNSName` + * elements of the end-entity certificate's SAN extension (if there + * is no SAN, then the Common Name from the subjectDN will be used). + * If `server_name` is `NULL` then no such matching is performed. + * + * \param ctx validation context. + * \param server_name server name to match (or `NULL`). + */ + void (*start_chain)(const br_x509_class **ctx, + const char *server_name); + + /** + * \brief Start a new certificate. + * + * \param ctx validation context. + * \param length new certificate length (in bytes). + */ + void (*start_cert)(const br_x509_class **ctx, uint32_t length); + + /** + * \brief Receive some bytes for the current certificate. + * + * This function may be called several times in succession for + * a given certificate. The caller guarantees that for each + * call, `len` is not zero, and the sum of all chunk lengths + * for a certificate matches the total certificate length which + * was provided in the previous `start_cert()` call. + * + * If the new certificate is empty (no byte at all) then this + * function won't be called at all. + * + * \param ctx validation context. + * \param buf certificate data chunk. + * \param len certificate data chunk length (in bytes). + */ + void (*append)(const br_x509_class **ctx, + const unsigned char *buf, size_t len); + + /** + * \brief Finish the current certificate. + * + * This function is called when the end of the current certificate + * is reached. + * + * \param ctx validation context. + */ + void (*end_cert)(const br_x509_class **ctx); + + /** + * \brief Finish the chain. + * + * This function is called at the end of the chain. It shall + * return either 0 if the validation was successful, or a + * non-zero error code. The `BR_ERR_X509_*` constants are + * error codes, though other values may be possible. + * + * \param ctx validation context. + * \return 0 on success, or a non-zero error code. + */ + unsigned (*end_chain)(const br_x509_class **ctx); + + /** + * \brief Get the resulting end-entity public key. + * + * The decoded public key is returned. The returned pointer + * may be valid only as long as the context structure is + * unmodified, i.e. it may cease to be valid if the context + * is released or reused. + * + * This function _may_ return `NULL` if the validation failed. + * However, returning a public key does not mean that the + * validation was wholly successful; some engines may return + * a decoded public key even if the chain did not end on a + * trusted anchor. + * + * If validation succeeded and `usage` is not `NULL`, then + * `*usage` is filled with a combination of `BR_KEYTYPE_SIGN` + * and/or `BR_KEYTYPE_KEYX` that specifies the validated key + * usage types. It is the caller's responsibility to check + * that value against the intended use of the public key. + * + * \param ctx validation context. + * \return the end-entity public key, or `NULL`. + */ + const br_x509_pkey *(*get_pkey)( + const br_x509_class *const *ctx, unsigned *usages); +}; + +/** + * \brief The "known key" X.509 engine structure. + * + * The structure contents are opaque (they shall not be accessed directly), + * except for the first field (the vtable). + * + * The "known key" engine returns an externally configured public key, + * and totally ignores the certificate contents. + */ +typedef struct { + /** \brief Reference to the context vtable. */ + const br_x509_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + br_x509_pkey pkey; + unsigned usages; +#endif +} br_x509_knownkey_context; + +/** + * \brief Class instance for the "known key" X.509 engine. + */ +extern const br_x509_class br_x509_knownkey_vtable; + +/** + * \brief Initialize a "known key" X.509 engine with a known RSA public key. + * + * The `usages` parameter indicates the allowed key usages for that key + * (`BR_KEYTYPE_KEYX` and/or `BR_KEYTYPE_SIGN`). + * + * The provided pointers are linked in, not copied, so they must remain + * valid while the public key may be in usage. + * + * \param ctx context to initialise. + * \param pk known public key. + * \param usages allowed key usages. + */ +void br_x509_knownkey_init_rsa(br_x509_knownkey_context *ctx, + const br_rsa_public_key *pk, unsigned usages); + +/** + * \brief Initialize a "known key" X.509 engine with a known EC public key. + * + * The `usages` parameter indicates the allowed key usages for that key + * (`BR_KEYTYPE_KEYX` and/or `BR_KEYTYPE_SIGN`). + * + * The provided pointers are linked in, not copied, so they must remain + * valid while the public key may be in usage. + * + * \param ctx context to initialise. + * \param pk known public key. + * \param usages allowed key usages. + */ +void br_x509_knownkey_init_ec(br_x509_knownkey_context *ctx, + const br_ec_public_key *pk, unsigned usages); + +#ifndef BR_DOXYGEN_IGNORE +/* + * The minimal X.509 engine has some state buffers which must be large + * enough to simultaneously accommodate: + * -- the public key extracted from the current certificate; + * -- the signature on the current certificate or on the previous + * certificate; + * -- the public key extracted from the EE certificate. + * + * We store public key elements in their raw unsigned big-endian + * encoding. We want to support up to RSA-4096 with a short (up to 64 + * bits) public exponent, thus a buffer for a public key must have + * length at least 520 bytes. Similarly, a RSA-4096 signature has length + * 512 bytes. + * + * Though RSA public exponents can formally be as large as the modulus + * (mathematically, even larger exponents would work, but PKCS#1 forbids + * them), exponents that do not fit on 32 bits are extremely rare, + * notably because some widespread implementations (e.g. Microsoft's + * CryptoAPI) don't support them. Moreover, large public exponent do not + * seem to imply any tangible security benefit, and they increase the + * cost of public key operations. The X.509 "minimal" engine will tolerate + * public exponents of arbitrary size as long as the modulus and the + * exponent can fit together in the dedicated buffer. + * + * EC public keys are shorter than RSA public keys; even with curve + * NIST P-521 (the largest curve we care to support), a public key is + * encoded over 133 bytes only. + */ +#define BR_X509_BUFSIZE_KEY 520 +#define BR_X509_BUFSIZE_SIG 512 +#endif + +/** + * \brief Type for receiving a name element. + * + * An array of such structures can be provided to the X.509 decoding + * engines. If the specified elements are found in the certificate + * subject DN or the SAN extension, then the name contents are copied + * as zero-terminated strings into the buffer. + * + * The decoder converts TeletexString and BMPString to UTF8String, and + * ensures that the resulting string is zero-terminated. If the string + * does not fit in the provided buffer, then the copy is aborted and an + * error is reported. + */ +typedef struct { + /** + * \brief Element OID. + * + * For X.500 name elements (to be extracted from the subject DN), + * this is the encoded OID for the requested name element; the + * first byte shall contain the length of the DER-encoded OID + * value, followed by the OID value (for instance, OID 2.5.4.3, + * for id-at-commonName, will be `03 55 04 03`). This is + * equivalent to full DER encoding with the length but without + * the tag. + * + * For SAN name elements, the first byte (`oid[0]`) has value 0, + * followed by another byte that matches the expected GeneralName + * tag. Allowed second byte values are then: + * + * - 1: `rfc822Name` + * + * - 2: `dNSName` + * + * - 6: `uniformResourceIdentifier` + * + * - 0: `otherName` + * + * If first and second byte are 0, then this is a SAN element of + * type `otherName`; the `oid[]` array should then contain, right + * after the two bytes of value 0, an encoded OID (with the same + * conventions as for X.500 name elements). If a match is found + * for that OID, then the corresponding name element will be + * extracted, as long as it is a supported string type. + */ + const unsigned char *oid; + + /** + * \brief Destination buffer. + */ + char *buf; + + /** + * \brief Length (in bytes) of the destination buffer. + * + * The buffer MUST NOT be smaller than 1 byte. + */ + size_t len; + + /** + * \brief Decoding status. + * + * Status is 0 if the name element was not found, 1 if it was + * found and decoded, or -1 on error. Error conditions include + * an unrecognised encoding, an invalid encoding, or a string + * too large for the destination buffer. + */ + int status; + +} br_name_element; + +/** + * \brief The "minimal" X.509 engine structure. + * + * The structure contents are opaque (they shall not be accessed directly), + * except for the first field (the vtable). + * + * The "minimal" engine performs a rudimentary but serviceable X.509 path + * validation. + */ +typedef struct { + const br_x509_class *vtable; + +#ifndef BR_DOXYGEN_IGNORE + /* Structure for returning the EE public key. */ + br_x509_pkey pkey; + + /* CPU for the T0 virtual machine. */ + struct { + uint32_t *dp; + uint32_t *rp; + const unsigned char *ip; + } cpu; + uint32_t dp_stack[32]; + uint32_t rp_stack[32]; + int err; + + /* Server name to match with the SAN / CN of the EE certificate. */ + const char *server_name; + + /* Validated key usages. */ + unsigned char key_usages; + + /* Explicitly set date and time. */ + uint32_t days, seconds; + + /* Current certificate length (in bytes). Set to 0 when the + certificate has been fully processed. */ + uint32_t cert_length; + + /* Number of certificates processed so far in the current chain. + It is incremented at the end of the processing of a certificate, + so it is 0 for the EE. */ + uint32_t num_certs; + + /* Certificate data chunk. */ + const unsigned char *hbuf; + size_t hlen; + + /* The pad serves as destination for various operations. */ + unsigned char pad[256]; + + /* Buffer for EE public key data. */ + unsigned char ee_pkey_data[BR_X509_BUFSIZE_KEY]; + + /* Buffer for currently decoded public key. */ + unsigned char pkey_data[BR_X509_BUFSIZE_KEY]; + + /* Signature type: signer key type, offset to the hash + function OID (in the T0 data block) and hash function + output length (TBS hash length). */ + unsigned char cert_signer_key_type; + uint16_t cert_sig_hash_oid; + unsigned char cert_sig_hash_len; + + /* Current/last certificate signature. */ + unsigned char cert_sig[BR_X509_BUFSIZE_SIG]; + uint16_t cert_sig_len; + + /* Minimum RSA key length (difference in bytes from 128). */ + int16_t min_rsa_size; + + /* Configured trust anchors. */ + const br_x509_trust_anchor *trust_anchors; + size_t trust_anchors_num; + + /* private context for dynamic callbacks */ + void *trust_anchor_dynamic_ctx; + /* Dynamic trust anchor, for on-the-fly loading of TAs */ + const br_x509_trust_anchor* (*trust_anchor_dynamic)(void *ctx, void *hashed_dn, size_t hashed_dn_len); + /* And a chance to free any dynamically allocated TA returned from above */ + void (*trust_anchor_dynamic_free)(void *ctx, const br_x509_trust_anchor *ta); + + /* + * Multi-hasher for the TBS. + */ + unsigned char do_mhash; + br_multihash_context mhash; + unsigned char tbs_hash[64]; + + /* + * Simple hasher for the subject/issuer DN. + */ + unsigned char do_dn_hash; + const br_hash_class *dn_hash_impl; + br_hash_compat_context dn_hash; + unsigned char current_dn_hash[64]; + unsigned char next_dn_hash[64]; + unsigned char saved_dn_hash[64]; + + /* + * Name elements to gather. + */ + br_name_element *name_elts; + size_t num_name_elts; + + /* + * Public key cryptography implementations (signature verification). + */ + br_rsa_pkcs1_vrfy irsa; + br_ecdsa_vrfy iecdsa; + const br_ec_impl *iec; +#endif + +} br_x509_minimal_context; + +/** + * \brief Class instance for the "minimal" X.509 engine. + */ +extern const br_x509_class br_x509_minimal_vtable; + +/** + * \brief Initialise a "minimal" X.509 engine. + * + * The `dn_hash_impl` parameter shall be a hash function internally used + * to match X.500 names (subject/issuer DN, and anchor names). Any standard + * hash function may be used, but a collision-resistant hash function is + * advised. + * + * After initialization, some implementations for signature verification + * (hash functions and signature algorithms) MUST be added. + * + * \param ctx context to initialise. + * \param dn_hash_impl hash function for DN comparisons. + * \param trust_anchors trust anchors. + * \param trust_anchors_num number of trust anchors. + */ +void br_x509_minimal_init(br_x509_minimal_context *ctx, + const br_hash_class *dn_hash_impl, + const br_x509_trust_anchor *trust_anchors, size_t trust_anchors_num); + +/** + * \brief Set the optional dynamic trust anchor lookup callbacks + * + * The dynamic trust anchor lookup callbacks allow an application to implement + * a non-memory resident trust anchor store. This can be useful on embedded + * systems where RAM is at a premium, but there is an external stable store, + * such as embedded flash or SD card, to keep many CA certificates. Set or + * leave these functions as NULL to not use such a feature. + * + * The dynamic routine will be passed in the hashed DN in question using the + * dn_hash_impl, and should compare this DN to its set of hashed known DNs. + * Of course, the same dn_hash_impl needs to be used in the dynamic routine. + * After the trust_anchor* is used, the dynamic_free callback is given a + * chance to deallocate its memory, if needed. + * + * \param ctx context to initialise. + * \param dynamic_ctx private context for the dynamic callback + * \param trust_anchor_dynamic provides a trust_anchor* for a hashed_dn + * \param trust_anchor_dynamic_free allows deallocation of returned TA + */ +static inline void +br_x509_minimal_set_dynamic(br_x509_minimal_context *ctx, void *dynamic_ctx, + const br_x509_trust_anchor* (*dynamic)(void *ctx, void *hashed_dn, size_t hashed_dn_len), + void (*dynamic_free)(void *ctx, const br_x509_trust_anchor *ta)) +{ + ctx->trust_anchor_dynamic_ctx = dynamic_ctx; + ctx->trust_anchor_dynamic = dynamic; + ctx->trust_anchor_dynamic_free = dynamic_free; +} + +/** + * \brief Set a supported hash function in an X.509 "minimal" engine. + * + * Hash functions are used with signature verification algorithms. + * Once initialised (with `br_x509_minimal_init()`), the context must + * be configured with the hash functions it shall support for that + * purpose. The hash function identifier MUST be one of the standard + * hash function identifiers (1 to 6, for MD5, SHA-1, SHA-224, SHA-256, + * SHA-384 and SHA-512). + * + * If `impl` is `NULL`, this _removes_ support for the designated + * hash function. + * + * \param ctx validation context. + * \param id hash function identifier (from 1 to 6). + * \param impl hash function implementation (or `NULL`). + */ +static inline void +br_x509_minimal_set_hash(br_x509_minimal_context *ctx, + int id, const br_hash_class *impl) +{ + br_multihash_setimpl(&ctx->mhash, id, impl); +} + +/** + * \brief Set a RSA signature verification implementation in the X.509 + * "minimal" engine. + * + * Once initialised (with `br_x509_minimal_init()`), the context must + * be configured with the signature verification implementations that + * it is supposed to support. If `irsa` is `0`, then the RSA support + * is disabled. + * + * \param ctx validation context. + * \param irsa RSA signature verification implementation (or `0`). + */ +static inline void +br_x509_minimal_set_rsa(br_x509_minimal_context *ctx, + br_rsa_pkcs1_vrfy irsa) +{ + ctx->irsa = irsa; +} + +/** + * \brief Set a ECDSA signature verification implementation in the X.509 + * "minimal" engine. + * + * Once initialised (with `br_x509_minimal_init()`), the context must + * be configured with the signature verification implementations that + * it is supposed to support. + * + * If `iecdsa` is `0`, then this call disables ECDSA support; in that + * case, `iec` may be `NULL`. Otherwise, `iecdsa` MUST point to a function + * that verifies ECDSA signatures with format "asn1", and it will use + * `iec` as underlying elliptic curve support. + * + * \param ctx validation context. + * \param iec elliptic curve implementation (or `NULL`). + * \param iecdsa ECDSA implementation (or `0`). + */ +static inline void +br_x509_minimal_set_ecdsa(br_x509_minimal_context *ctx, + const br_ec_impl *iec, br_ecdsa_vrfy iecdsa) +{ + ctx->iecdsa = iecdsa; + ctx->iec = iec; +} + +/** + * \brief Initialise a "minimal" X.509 engine with default algorithms. + * + * This function performs the same job as `br_x509_minimal_init()`, but + * also sets implementations for RSA, ECDSA, and the standard hash + * functions. + * + * \param ctx context to initialise. + * \param trust_anchors trust anchors. + * \param trust_anchors_num number of trust anchors. + */ +void br_x509_minimal_init_full(br_x509_minimal_context *ctx, + const br_x509_trust_anchor *trust_anchors, size_t trust_anchors_num); + +/** + * \brief Set the validation time for the X.509 "minimal" engine. + * + * The validation time is set as two 32-bit integers, for days and + * seconds since a fixed epoch: + * + * - Days are counted in a proleptic Gregorian calendar since + * January 1st, 0 AD. Year "0 AD" is the one that preceded "1 AD"; + * it is also traditionally known as "1 BC". + * + * - Seconds are counted since midnight, from 0 to 86400 (a count of + * 86400 is possible only if a leap second happened). + * + * The validation date and time is understood in the UTC time zone. + * + * If the validation date and time are not explicitly set, but BearSSL + * was compiled with support for the system clock on the underlying + * platform, then the current time will automatically be used. Otherwise, + * not setting the validation date and time implies a validation + * failure (except in case of direct trust of the EE key). + * + * \param ctx validation context. + * \param days days since January 1st, 0 AD (Gregorian calendar). + * \param seconds seconds since midnight (0 to 86400). + */ +static inline void +br_x509_minimal_set_time(br_x509_minimal_context *ctx, + uint32_t days, uint32_t seconds) +{ + ctx->days = days; + ctx->seconds = seconds; +} + +/** + * \brief Set the minimal acceptable length for RSA keys (X.509 "minimal" + * engine). + * + * The RSA key length is expressed in bytes. The default minimum key + * length is 128 bytes, corresponding to 1017 bits. RSA keys shorter + * than the configured length will be rejected, implying validation + * failure. This setting applies to keys extracted from certificates + * (both end-entity, and intermediate CA) but not to "CA" trust anchors. + * + * \param ctx validation context. + * \param byte_length minimum RSA key length, **in bytes** (not bits). + */ +static inline void +br_x509_minimal_set_minrsa(br_x509_minimal_context *ctx, int byte_length) +{ + ctx->min_rsa_size = (int16_t)(byte_length - 128); +} + +/** + * \brief Set the name elements to gather. + * + * The provided array is linked in the context. The elements are + * gathered from the EE certificate. If the same element type is + * requested several times, then the relevant structures will be filled + * in the order the matching values are encountered in the certificate. + * + * \param ctx validation context. + * \param elts array of name element structures to fill. + * \param num_elts number of name element structures to fill. + */ +static inline void +br_x509_minimal_set_name_elements(br_x509_minimal_context *ctx, + br_name_element *elts, size_t num_elts) +{ + ctx->name_elts = elts; + ctx->num_name_elts = num_elts; +} + +/** + * \brief X.509 decoder context. + * + * This structure is _not_ for X.509 validation, but for extracting + * names and public keys from encoded certificates. Intended usage is + * to use (self-signed) certificates as trust anchors. + * + * Contents are opaque and shall not be accessed directly. + */ +typedef struct { + +#ifndef BR_DOXYGEN_IGNORE + /* Structure for returning the public key. */ + br_x509_pkey pkey; + + /* CPU for the T0 virtual machine. */ + struct { + uint32_t *dp; + uint32_t *rp; + const unsigned char *ip; + } cpu; + uint32_t dp_stack[32]; + uint32_t rp_stack[32]; + int err; + + /* The pad serves as destination for various operations. */ + unsigned char pad[256]; + + /* Flag set when decoding succeeds. */ + unsigned char decoded; + + /* Validity dates. */ + uint32_t notbefore_days, notbefore_seconds; + uint32_t notafter_days, notafter_seconds; + + /* The "CA" flag. This is set to true if the certificate contains + a Basic Constraints extension that asserts CA status. */ + unsigned char isCA; + + /* DN processing: the subject DN is extracted and pushed to the + provided callback. */ + unsigned char copy_dn; + void *append_dn_ctx; + void (*append_dn)(void *ctx, const void *buf, size_t len); + + /* DN processing: the issuer DN is extracted and pushed to the + provided callback. */ + unsigned char copy_in; + void *append_in_ctx; + void (*append_in)(void *ctx, const void *buf, size_t len); + + /* Certificate data chunk. */ + const unsigned char *hbuf; + size_t hlen; + + /* Buffer for decoded public key. */ + unsigned char pkey_data[BR_X509_BUFSIZE_KEY]; + + /* Type of key and hash function used in the certificate signature. */ + unsigned char signer_key_type; + unsigned char signer_hash_id; +#endif + +} br_x509_decoder_context; + +/** + * \brief Initialise an X.509 decoder context for processing a new + * certificate. + * + * The `append_dn()` callback (with opaque context `append_dn_ctx`) + * will be invoked to receive, chunk by chunk, the certificate's + * subject DN. If `append_dn` is `0` then the subject DN will be + * ignored. + * + * \param ctx X.509 decoder context to initialise. + * \param append_dn DN receiver callback (or `0`). + * \param append_dn_ctx context for the DN receiver callback. + * \param append_in issuer DN receiver callback (or `0`). + * \param append_in_ctx context for the issuer DN receiver callback. + */ +void br_x509_decoder_init(br_x509_decoder_context *ctx, + void (*append_dn)(void *ctx, const void *buf, size_t len), + void *append_dn_ctx, + void (*append_in)(void *ctx, const void *buf, size_t len), + void *append_in_ctx); + +/** + * \brief Push some certificate bytes into a decoder context. + * + * If `len` is non-zero, then that many bytes are pushed, from address + * `data`, into the provided decoder context. + * + * \param ctx X.509 decoder context. + * \param data certificate data chunk. + * \param len certificate data chunk length (in bytes). + */ +void br_x509_decoder_push(br_x509_decoder_context *ctx, + const void *data, size_t len); + +/** + * \brief Obtain the decoded public key. + * + * Returned value is a pointer to a structure internal to the decoder + * context; releasing or reusing the decoder context invalidates that + * structure. + * + * If decoding was not finished, or failed, then `NULL` is returned. + * + * \param ctx X.509 decoder context. + * \return the public key, or `NULL` on unfinished/error. + */ +static inline br_x509_pkey * +br_x509_decoder_get_pkey(br_x509_decoder_context *ctx) +{ + if (ctx->decoded && ctx->err == 0) { + return &ctx->pkey; + } else { + return NULL; + } +} + +/** + * \brief Get decoder error status. + * + * If no error was reported yet but the certificate decoding is not + * finished, then the error code is `BR_ERR_X509_TRUNCATED`. If decoding + * was successful, then 0 is returned. + * + * \param ctx X.509 decoder context. + * \return 0 on successful decoding, or a non-zero error code. + */ +static inline int +br_x509_decoder_last_error(br_x509_decoder_context *ctx) +{ + if (ctx->err != 0) { + return ctx->err; + } + if (!ctx->decoded) { + return BR_ERR_X509_TRUNCATED; + } + return 0; +} + +/** + * \brief Get the "isCA" flag from an X.509 decoder context. + * + * This flag is set if the decoded certificate claims to be a CA through + * a Basic Constraints extension. This flag should not be read before + * decoding completed successfully. + * + * \param ctx X.509 decoder context. + * \return the "isCA" flag. + */ +static inline int +br_x509_decoder_isCA(br_x509_decoder_context *ctx) +{ + return ctx->isCA; +} + +/** + * \brief Get the issuing CA key type (type of algorithm used to sign the + * decoded certificate). + * + * This is `BR_KEYTYPE_RSA` or `BR_KEYTYPE_EC`. The value 0 is returned + * if the signature type was not recognised. + * + * \param ctx X.509 decoder context. + * \return the issuing CA key type. + */ +static inline int +br_x509_decoder_get_signer_key_type(br_x509_decoder_context *ctx) +{ + return ctx->signer_key_type; +} + +/** + * \brief Get the identifier for the hash function used to sign the decoded + * certificate. + * + * This is 0 if the hash function was not recognised. + * + * \param ctx X.509 decoder context. + * \return the signature hash function identifier. + */ +static inline int +br_x509_decoder_get_signer_hash_id(br_x509_decoder_context *ctx) +{ + return ctx->signer_hash_id; +} + +/** + * \brief Type for an X.509 certificate (DER-encoded). + */ +typedef struct { + /** \brief The DER-encoded certificate data. */ + unsigned char *data; + /** \brief The DER-encoded certificate length (in bytes). */ + size_t data_len; +} br_x509_certificate; + +/** + * \brief Private key decoder context. + * + * The private key decoder recognises RSA and EC private keys, either in + * their raw, DER-encoded format, or wrapped in an unencrypted PKCS#8 + * archive (again DER-encoded). + * + * Structure contents are opaque and shall not be accessed directly. + */ +typedef struct { +#ifndef BR_DOXYGEN_IGNORE + /* Structure for returning the private key. */ + union { + br_rsa_private_key rsa; + br_ec_private_key ec; + } key; + + /* CPU for the T0 virtual machine. */ + struct { + uint32_t *dp; + uint32_t *rp; + const unsigned char *ip; + } cpu; + uint32_t dp_stack[32]; + uint32_t rp_stack[32]; + int err; + + /* Private key data chunk. */ + const unsigned char *hbuf; + size_t hlen; + + /* The pad serves as destination for various operations. */ + unsigned char pad[256]; + + /* Decoded key type; 0 until decoding is complete. */ + unsigned char key_type; + + /* Buffer for the private key elements. It shall be large enough + to accommodate all elements for a RSA-4096 private key (roughly + five 2048-bit integers, possibly a bit more). */ + unsigned char key_data[3 * BR_X509_BUFSIZE_SIG]; +#endif +} br_skey_decoder_context; + +/** + * \brief Initialise a private key decoder context. + * + * \param ctx key decoder context to initialise. + */ +void br_skey_decoder_init(br_skey_decoder_context *ctx); + +/** + * \brief Push some data bytes into a private key decoder context. + * + * If `len` is non-zero, then that many data bytes, starting at address + * `data`, are pushed into the decoder. + * + * \param ctx key decoder context. + * \param data private key data chunk. + * \param len private key data chunk length (in bytes). + */ +void br_skey_decoder_push(br_skey_decoder_context *ctx, + const void *data, size_t len); + +/** + * \brief Get the decoding status for a private key. + * + * Decoding status is 0 on success, or a non-zero error code. If the + * decoding is unfinished when this function is called, then the + * status code `BR_ERR_X509_TRUNCATED` is returned. + * + * \param ctx key decoder context. + * \return 0 on successful decoding, or a non-zero error code. + */ +static inline int +br_skey_decoder_last_error(const br_skey_decoder_context *ctx) +{ + if (ctx->err != 0) { + return ctx->err; + } + if (ctx->key_type == 0) { + return BR_ERR_X509_TRUNCATED; + } + return 0; +} + +/** + * \brief Get the decoded private key type. + * + * Private key type is `BR_KEYTYPE_RSA` or `BR_KEYTYPE_EC`. If decoding is + * not finished or failed, then 0 is returned. + * + * \param ctx key decoder context. + * \return decoded private key type, or 0. + */ +static inline int +br_skey_decoder_key_type(const br_skey_decoder_context *ctx) +{ + if (ctx->err == 0) { + return ctx->key_type; + } else { + return 0; + } +} + +/** + * \brief Get the decoded RSA private key. + * + * This function returns `NULL` if the decoding failed, or is not + * finished, or the key is not RSA. The returned pointer references + * structures within the context that can become invalid if the context + * is reused or released. + * + * \param ctx key decoder context. + * \return decoded RSA private key, or `NULL`. + */ +static inline const br_rsa_private_key * +br_skey_decoder_get_rsa(const br_skey_decoder_context *ctx) +{ + if (ctx->err == 0 && ctx->key_type == BR_KEYTYPE_RSA) { + return &ctx->key.rsa; + } else { + return NULL; + } +} + +/** + * \brief Get the decoded EC private key. + * + * This function returns `NULL` if the decoding failed, or is not + * finished, or the key is not EC. The returned pointer references + * structures within the context that can become invalid if the context + * is reused or released. + * + * \param ctx key decoder context. + * \return decoded EC private key, or `NULL`. + */ +static inline const br_ec_private_key * +br_skey_decoder_get_ec(const br_skey_decoder_context *ctx) +{ + if (ctx->err == 0 && ctx->key_type == BR_KEYTYPE_EC) { + return &ctx->key.ec; + } else { + return NULL; + } +} + +/** + * \brief Public key decoder context. + * + * The public key decoder recognises RSA and EC private keys, either in + * their raw, DER-encoded format, or wrapped in an unencrypted PKCS#8 + * archive (again DER-encoded). + * + * Structure contents are opaque and shall not be accessed directly. + */ +typedef struct { +#ifndef BR_DOXYGEN_IGNORE + /* Structure for returning the private key. */ + union { + br_rsa_public_key rsa; + br_ec_public_key ec; + } key; + + /* CPU for the T0 virtual machine. */ + struct { + uint32_t *dp; + uint32_t *rp; + const unsigned char *ip; + } cpu; + uint32_t dp_stack[32]; + uint32_t rp_stack[32]; + int err; + + /* Private key data chunk. */ + const unsigned char *hbuf; + size_t hlen; + + /* The pad serves as destination for various operations. */ + unsigned char pad[256]; + + /* Decoded key type; 0 until decoding is complete. */ + unsigned char key_type; + + /* Buffer for the private key elements. It shall be large enough + to accommodate all elements for a RSA-4096 private key (roughly + five 2048-bit integers, possibly a bit more). */ + unsigned char key_data[3 * BR_X509_BUFSIZE_SIG]; +#endif +} br_pkey_decoder_context; + + +/** + * \brief Initialise a public key decoder context. + * + * \param ctx key decoder context to initialise. + */ +void br_pkey_decoder_init(br_pkey_decoder_context *ctx); + +/** + * \brief Push some data bytes into a public key decoder context. + * + * If `len` is non-zero, then that many data bytes, starting at address + * `data`, are pushed into the decoder. + * + * \param ctx key decoder context. + * \param data private key data chunk. + * \param len private key data chunk length (in bytes). + */ +void br_pkey_decoder_push(br_pkey_decoder_context *ctx, + const void *data, size_t len); + +/** + * \brief Get the decoding status for a public key. + * + * Decoding status is 0 on success, or a non-zero error code. If the + * decoding is unfinished when this function is called, then the + * status code `BR_ERR_X509_TRUNCATED` is returned. + * + * \param ctx key decoder context. + * \return 0 on successful decoding, or a non-zero error code. + */ +static inline int +br_pkey_decoder_last_error(const br_pkey_decoder_context *ctx) +{ + if (ctx->err != 0) { + return ctx->err; + } + if (ctx->key_type == 0) { + return BR_ERR_X509_TRUNCATED; + } + return 0; +} + +/** + * \brief Get the decoded public key type. + * + * Public key type is `BR_KEYTYPE_RSA` or `BR_KEYTYPE_EC`. If decoding is + * not finished or failed, then 0 is returned. + * + * \param ctx key decoder context. + * \return decoded private key type, or 0. + */ +static inline int +br_pkey_decoder_key_type(const br_pkey_decoder_context *ctx) +{ + if (ctx->err == 0) { + return ctx->key_type; + } else { + return 0; + } +} + +/** + * \brief Get the decoded RSA public key. + * + * This function returns `NULL` if the decoding failed, or is not + * finished, or the key is not RSA. The returned pointer references + * structures within the context that can become invalid if the context + * is reused or released. + * + * \param ctx key decoder context. + * \return decoded RSA public key, or `NULL`. + */ +static inline const br_rsa_public_key * +br_pkey_decoder_get_rsa(const br_pkey_decoder_context *ctx) +{ + if (ctx->err == 0 && ctx->key_type == BR_KEYTYPE_RSA) { + return &ctx->key.rsa; + } else { + return NULL; + } +} + +/** + * \brief Get the decoded EC private key. + * + * This function returns `NULL` if the decoding failed, or is not + * finished, or the key is not EC. The returned pointer references + * structures within the context that can become invalid if the context + * is reused or released. + * + * \param ctx key decoder context. + * \return decoded EC private key, or `NULL`. + */ +static inline const br_ec_public_key * +br_pkey_decoder_get_ec(const br_pkey_decoder_context *ctx) +{ + if (ctx->err == 0 && ctx->key_type == BR_KEYTYPE_EC) { + return &ctx->key.ec; + } else { + return NULL; + } +} + +/** + * \brief Encode an RSA private key (raw DER format). + * + * This function encodes the provided key into the "raw" format specified + * in PKCS#1 (RFC 8017, Appendix C, type `RSAPrivateKey`), with DER + * encoding rules. + * + * The key elements are: + * + * - `sk`: the private key (`p`, `q`, `dp`, `dq` and `iq`) + * + * - `pk`: the public key (`n` and `e`) + * + * - `d` (size: `dlen` bytes): the private exponent + * + * The public key elements, and the private exponent `d`, can be + * recomputed from the private key (see `br_rsa_compute_modulus()`, + * `br_rsa_compute_pubexp()` and `br_rsa_compute_privexp()`). + * + * If `dest` is not `NULL`, then the encoded key is written at that + * address, and the encoded length (in bytes) is returned. If `dest` is + * `NULL`, then nothing is written, but the encoded length is still + * computed and returned. + * + * \param dest the destination buffer (or `NULL`). + * \param sk the RSA private key. + * \param pk the RSA public key. + * \param d the RSA private exponent. + * \param dlen the RSA private exponent length (in bytes). + * \return the encoded key length (in bytes). + */ +size_t br_encode_rsa_raw_der(void *dest, const br_rsa_private_key *sk, + const br_rsa_public_key *pk, const void *d, size_t dlen); + +/** + * \brief Encode an RSA private key (PKCS#8 DER format). + * + * This function encodes the provided key into the PKCS#8 format + * (RFC 5958, type `OneAsymmetricKey`). It wraps around the "raw DER" + * format for the RSA key, as implemented by `br_encode_rsa_raw_der()`. + * + * The key elements are: + * + * - `sk`: the private key (`p`, `q`, `dp`, `dq` and `iq`) + * + * - `pk`: the public key (`n` and `e`) + * + * - `d` (size: `dlen` bytes): the private exponent + * + * The public key elements, and the private exponent `d`, can be + * recomputed from the private key (see `br_rsa_compute_modulus()`, + * `br_rsa_compute_pubexp()` and `br_rsa_compute_privexp()`). + * + * If `dest` is not `NULL`, then the encoded key is written at that + * address, and the encoded length (in bytes) is returned. If `dest` is + * `NULL`, then nothing is written, but the encoded length is still + * computed and returned. + * + * \param dest the destination buffer (or `NULL`). + * \param sk the RSA private key. + * \param pk the RSA public key. + * \param d the RSA private exponent. + * \param dlen the RSA private exponent length (in bytes). + * \return the encoded key length (in bytes). + */ +size_t br_encode_rsa_pkcs8_der(void *dest, const br_rsa_private_key *sk, + const br_rsa_public_key *pk, const void *d, size_t dlen); + +/** + * \brief Encode an EC private key (raw DER format). + * + * This function encodes the provided key into the "raw" format specified + * in RFC 5915 (type `ECPrivateKey`), with DER encoding rules. + * + * The private key is provided in `sk`, the public key being `pk`. If + * `pk` is `NULL`, then the encoded key will not include the public key + * in its `publicKey` field (which is nominally optional). + * + * If `dest` is not `NULL`, then the encoded key is written at that + * address, and the encoded length (in bytes) is returned. If `dest` is + * `NULL`, then nothing is written, but the encoded length is still + * computed and returned. + * + * If the key cannot be encoded (e.g. because there is no known OBJECT + * IDENTIFIER for the used curve), then 0 is returned. + * + * \param dest the destination buffer (or `NULL`). + * \param sk the EC private key. + * \param pk the EC public key (or `NULL`). + * \return the encoded key length (in bytes), or 0. + */ +size_t br_encode_ec_raw_der(void *dest, + const br_ec_private_key *sk, const br_ec_public_key *pk); + +/** + * \brief Encode an EC private key (PKCS#8 DER format). + * + * This function encodes the provided key into the PKCS#8 format + * (RFC 5958, type `OneAsymmetricKey`). The curve is identified + * by an OID provided as parameters to the `privateKeyAlgorithm` + * field. The private key value (contents of the `privateKey` field) + * contains the DER encoding of the `ECPrivateKey` type defined in + * RFC 5915, without the `parameters` field (since they would be + * redundant with the information in `privateKeyAlgorithm`). + * + * The private key is provided in `sk`, the public key being `pk`. If + * `pk` is not `NULL`, then the encoded public key is included in the + * `publicKey` field of the private key value (but not in the `publicKey` + * field of the PKCS#8 `OneAsymmetricKey` wrapper). + * + * If `dest` is not `NULL`, then the encoded key is written at that + * address, and the encoded length (in bytes) is returned. If `dest` is + * `NULL`, then nothing is written, but the encoded length is still + * computed and returned. + * + * If the key cannot be encoded (e.g. because there is no known OBJECT + * IDENTIFIER for the used curve), then 0 is returned. + * + * \param dest the destination buffer (or `NULL`). + * \param sk the EC private key. + * \param pk the EC public key (or `NULL`). + * \return the encoded key length (in bytes), or 0. + */ +size_t br_encode_ec_pkcs8_der(void *dest, + const br_ec_private_key *sk, const br_ec_public_key *pk); + +/** + * \brief PEM banner for RSA private key (raw). + */ +#define BR_ENCODE_PEM_RSA_RAW "RSA PRIVATE KEY" + +/** + * \brief PEM banner for EC private key (raw). + */ +#define BR_ENCODE_PEM_EC_RAW "EC PRIVATE KEY" + +/** + * \brief PEM banner for an RSA or EC private key in PKCS#8 format. + */ +#define BR_ENCODE_PEM_PKCS8 "PRIVATE KEY" + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/bearssl-esp8266/src/t_config.h b/lib/bearssl-esp8266/src/t_config.h new file mode 100644 index 000000000..2b0e47b64 --- /dev/null +++ b/lib/bearssl-esp8266/src/t_config.h @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_bearssl_tasmota_config.h" + +#ifndef CONFIG_H__ +#define CONFIG_H__ + +/* + * This file contains compile-time flags that can override the + * autodetection performed in relevant files. Each flag is a macro; it + * deactivates the feature if defined to 0, activates it if defined to a + * non-zero integer (normally 1). If the macro is not defined, then + * autodetection applies. + */ + +/* + * When BR_64 is enabled, 64-bit integer types are assumed to be + * efficient (i.e. the architecture has 64-bit registers and can + * do 64-bit operations as fast as 32-bit operations). + * +#define BR_64 1 + */ + +/* + * When BR_LOMUL is enabled, then multiplications of 32-bit values whose + * result are truncated to the low 32 bits are assumed to be + * substantially more efficient than 32-bit multiplications that yield + * 64-bit results. This is typically the case on low-end ARM Cortex M + * systems (M0, M0+, M1, and arguably M3 and M4 as well). + * +#define BR_LOMUL 1 + */ + +/* + * When BR_SLOW_MUL is enabled, multiplications are assumed to be + * substantially slow with regards to other integer operations, thus + * making it worth to make more operations for a given task if it allows + * using less multiplications. + * +#define BR_SLOW_MUL 1 + */ + +/* + * When BR_SLOW_MUL15 is enabled, short multplications (on 15-bit words) + * are assumed to be substantially slow with regards to other integer + * operations, thus making it worth to make more integer operations if + * it allows using less multiplications. + * +#define BR_SLOW_MUL15 1 + */ + +/* + * When BR_CT_MUL31 is enabled, multiplications of 31-bit values (used + * in the "i31" big integer implementation) use an alternate implementation + * which is slower and larger than the normal multiplication, but should + * ensure constant-time multiplications even on architectures where the + * multiplication opcode takes a variable number of cycles to complete. + * +#define BR_CT_MUL31 1 + */ + +/* + * When BR_CT_MUL15 is enabled, multiplications of 15-bit values (held + * in 32-bit words) use an alternate implementation which is slower and + * larger than the normal multiplication, but should ensure + * constant-time multiplications on most/all architectures where the + * basic multiplication is not constant-time. +#define BR_CT_MUL15 1 + */ + +/* + * When BR_NO_ARITH_SHIFT is enabled, arithmetic right shifts (with sign + * extension) are performed with a sequence of operations which is bigger + * and slower than a simple right shift on a signed value. This avoids + * relying on an implementation-defined behaviour. However, most if not + * all C compilers use sign extension for right shifts on signed values, + * so this alternate macro is disabled by default. +#define BR_NO_ARITH_SHIFT 1 + */ + +/* + * When BR_RDRAND is enabled, the SSL engine will use the RDRAND opcode + * to automatically obtain quality randomness for seeding its internal + * PRNG. Since that opcode is present only in recent x86 CPU, its + * support is dynamically tested; if the current CPU does not support + * it, then another random source will be used, such as /dev/urandom or + * CryptGenRandom(). + * +#define BR_RDRAND 1 + */ + +/* + * When BR_USE_URANDOM is enabled, the SSL engine will use /dev/urandom + * to automatically obtain quality randomness for seedings its internal + * PRNG. + * +#define BR_USE_URANDOM 1 + */ + +/* + * When BR_USE_ESP8266_RAND is enabled, use the phy_get_rand() SDK call + * on the ESP8266 as the entropy source, 32-bits at a time. + * +#define BR_USE_ESP8266_RAND 1 + */ + +/* + * When BR_USE_WIN32_RAND is enabled, the SSL engine will use the Win32 + * (CryptoAPI) functions (CryptAcquireContext(), CryptGenRandom()...) to + * automatically obtain quality randomness for seedings its internal PRNG. + * + * Note: if both BR_USE_URANDOM and BR_USE_WIN32_RAND are defined, the + * former takes precedence. + * +#define BR_USE_WIN32_RAND 1 + */ + +/* + * When BR_USE_UNIX_TIME is enabled, the X.509 validation engine obtains + * the current time from the OS by calling time(), and assuming that the + * returned value (a 'time_t') is an integer that counts time in seconds + * since the Unix Epoch (Jan 1st, 1970, 00:00 UTC). + * +#define BR_USE_UNIX_TIME 1 + */ + +/* + * When BR_USE_WIN32_TIME is enabled, the X.509 validation engine obtains + * the current time from the OS by calling the Win32 function + * GetSystemTimeAsFileTime(). + * + * Note: if both BR_USE_UNIX_TIME and BR_USE_WIN32_TIME are defined, the + * former takes precedence. + * +#define BR_USE_WIN32_TIME 1 + */ + +/* + * When BR_ARMEL_CORTEXM_GCC is enabled, some operations are replaced with + * inline assembly which is shorter and/or faster. This should be used + * only when all of the following are true: + * - target architecture is ARM in Thumb mode + * - target endianness is little-endian + * - compiler is GCC (or GCC-compatible for inline assembly syntax) + * + * This is meant for the low-end cores (Cortex M0, M0+, M1, M3). + * Note: if BR_LOMUL is not explicitly enabled or disabled, then + * enabling BR_ARMEL_CORTEXM_GCC also enables BR_LOMUL. + * +#define BR_ARMEL_CORTEXM_GCC 1 + */ + +/* + * When BR_AES_X86NI is enabled, the AES implementation using the x86 "NI" + * instructions (dedicated AES opcodes) will be compiled. If this is not + * enabled explicitly, then that AES implementation will be compiled only + * if a compatible compiler is detected. If set explicitly to 0, the + * implementation will not be compiled at all. + * +#define BR_AES_X86NI 1 + */ + +/* + * When BR_SSE2 is enabled, SSE2 intrinsics will be used for some + * algorithm implementations that use them (e.g. chacha20_sse2). If this + * is not enabled explicitly, then support for SSE2 intrinsics will be + * automatically detected. If set explicitly to 0, then SSE2 code will + * not be compiled at all. + * +#define BR_SSE2 1 + */ + +/* + * When BR_POWER8 is enabled, the AES implementation using the POWER ISA + * 2.07 opcodes (available on POWER8 processors and later) is compiled. + * If this is not enabled explicitly, then that implementation will be + * compiled only if a compatible compiler is detected, _and_ the target + * architecture is POWER8 or later. + * +#define BR_POWER8 1 + */ + +/* + * When BR_INT128 is enabled, then code using the 'unsigned __int64' + * and 'unsigned __int128' types will be used to leverage 64x64->128 + * unsigned multiplications. This should work with GCC and compatible + * compilers on 64-bit architectures. + * +#define BR_INT128 1 + */ + +/* + * When BR_UMUL128 is enabled, then code using the '_umul128()' and + * '_addcarry_u64()' intrinsics will be used to implement 64x64->128 + * unsigned multiplications. This should work on Visual C on x64 systems. + * +#define BR_UMUL128 1 + */ + +/* + * When BR_LE_UNALIGNED is enabled, then the current architecture is + * assumed to use little-endian encoding for integers, and to tolerate + * unaligned accesses with no or minimal time penalty. + * +#define BR_LE_UNALIGNED 1 + */ + +/* + * When BR_BE_UNALIGNED is enabled, then the current architecture is + * assumed to use big-endian encoding for integers, and to tolerate + * unaligned accesses with no or minimal time penalty. + * +#define BR_BE_UNALIGNED 1 + */ + +#endif diff --git a/lib/bearssl-esp8266/src/t_inner.h b/lib/bearssl-esp8266/src/t_inner.h new file mode 100644 index 000000000..a7267f0df --- /dev/null +++ b/lib/bearssl-esp8266/src/t_inner.h @@ -0,0 +1,2590 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef INNER_H__ +#define INNER_H__ + +#include +#include +#include + +#include "pgmspace_bearssl.h" + +#include "t_config.h" +#include "t_bearssl.h" + +/* + * On MSVC, disable the warning about applying unary minus on an + * unsigned type: it is standard, we do it all the time, and for + * good reasons. + */ +#if _MSC_VER +#pragma warning( disable : 4146 ) +#endif + +/* + * Maximum size for a RSA modulus (in bits). Allocated stack buffers + * depend on that size, so this value should be kept small. Currently, + * 2048-bit RSA keys offer adequate security, and should still do so for + * the next few decades; however, a number of widespread PKI have + * already set their root keys to RSA-4096, so we should be able to + * process such keys. + * + * This value MUST be a multiple of 64. This value MUST NOT exceed 47666 + * (some computations in RSA key generation rely on the factor size being + * no more than 23833 bits). RSA key sizes beyond 3072 bits don't make a + * lot of sense anyway. + */ +#ifndef BR_MAX_RSA_SIZE +#define BR_MAX_RSA_SIZE 4096 +#endif + +/* + * Minimum size for a RSA modulus (in bits); this value is used only to + * filter out invalid parameters for key pair generation. Normally, + * applications should not use RSA keys smaller than 2048 bits; but some + * specific cases might need shorter keys, for legacy or research + * purposes. + */ +#define BR_MIN_RSA_SIZE 512 + +/* + * Maximum size for a RSA factor (in bits). This is for RSA private-key + * operations. Default is to support factors up to a bit more than half + * the maximum modulus size. + * + * This value MUST be a multiple of 32. + */ +#define BR_MAX_RSA_FACTOR ((BR_MAX_RSA_SIZE + 64) >> 1) + +/* + * Maximum size for an EC curve (modulus or order), in bits. Size of + * stack buffers depends on that parameter. This size MUST be a multiple + * of 8 (so that decoding an integer with that many bytes does not + * overflow). + */ +#ifndef BR_MAX_EC_SIZE +#define BR_MAX_EC_SIZE 528 +#endif + +/* + * Some macros to recognize the current architecture. Right now, we are + * interested into automatically recognizing architecture with efficient + * 64-bit types so that we may automatically use implementations that + * use 64-bit registers in that case. Future versions may detect, e.g., + * availability of SSE2 intrinsics. + * + * If 'unsigned long' is a 64-bit type, then we assume that 64-bit types + * are efficient. Otherwise, we rely on macros that depend on compiler, + * OS and architecture. In any case, failure to detect the architecture + * as 64-bit means that the 32-bit code will be used, and that code + * works also on 64-bit architectures (the 64-bit code may simply be + * more efficient). + * + * The test on 'unsigned long' should already catch most cases, the one + * notable exception being Windows code where 'unsigned long' is kept to + * 32-bit for compatibility with all the legacy code that liberally uses + * the 'DWORD' type for 32-bit values. + * + * Macro names are taken from: http://nadeausoftware.com/articles/2012/02/c_c_tip_how_detect_processor_type_using_compiler_predefined_macros + */ +#ifndef BR_64 +#if ((ULONG_MAX >> 31) >> 31) == 3 +#define BR_64 1 +#elif defined(__ia64) || defined(__itanium__) || defined(_M_IA64) +#define BR_64 1 +#elif defined(__powerpc64__) || defined(__ppc64__) || defined(__PPC64__) \ + || defined(__64BIT__) || defined(_LP64) || defined(__LP64__) +#define BR_64 1 +#elif defined(__sparc64__) +#define BR_64 1 +#elif defined(__x86_64__) || defined(_M_X64) +#define BR_64 1 +#elif defined(__aarch64__) || defined(_M_ARM64) +#define BR_64 1 +#elif defined(__mips64) +#define BR_64 1 +#endif +#endif + +/* + * Set BR_LOMUL on platforms where it makes sense. + */ +#ifndef BR_LOMUL +#if BR_ARMEL_CORTEXM_GCC || (defined(ESP8266) && !defined(ESP8266M32)) +#define BR_LOMUL 1 +#endif +#endif + +/* + * Architecture detection. + */ +#ifndef BR_i386 +#if __i386__ || _M_IX86 +#define BR_i386 1 +#endif +#endif + +#ifndef BR_amd64 +#if __x86_64__ || _M_X64 +#define BR_amd64 1 +#endif +#endif + +/* + * Compiler brand and version. + * + * Implementations that use intrinsics need to detect the compiler type + * and version because some specific actions may be needed to activate + * the corresponding opcodes, both for header inclusion, and when using + * them in a function. + * + * BR_GCC, BR_CLANG and BR_MSC will be set to 1 for, respectively, GCC, + * Clang and MS Visual C. For each of them, sub-macros will be defined + * for versions; each sub-macro is set whenever the compiler version is + * at least as recent as the one corresponding to the macro. + */ + +/* + * GCC thresholds are on versions 4.4 to 4.9 and 5.0. + */ +#ifndef BR_GCC +#if __GNUC__ && !__clang__ +#define BR_GCC 1 + +#if __GNUC__ > 4 +#define BR_GCC_5_0 1 +#elif __GNUC__ == 4 && __GNUC_MINOR__ >= 9 +#define BR_GCC_4_9 1 +#elif __GNUC__ == 4 && __GNUC_MINOR__ >= 8 +#define BR_GCC_4_8 1 +#elif __GNUC__ == 4 && __GNUC_MINOR__ >= 7 +#define BR_GCC_4_7 1 +#elif __GNUC__ == 4 && __GNUC_MINOR__ >= 6 +#define BR_GCC_4_6 1 +#elif __GNUC__ == 4 && __GNUC_MINOR__ >= 5 +#define BR_GCC_4_5 1 +#elif __GNUC__ == 4 && __GNUC_MINOR__ >= 4 +#define BR_GCC_4_4 1 +#endif + +#if BR_GCC_5_0 +#define BR_GCC_4_9 1 +#endif +#if BR_GCC_4_9 +#define BR_GCC_4_8 1 +#endif +#if BR_GCC_4_8 +#define BR_GCC_4_7 1 +#endif +#if BR_GCC_4_7 +#define BR_GCC_4_6 1 +#endif +#if BR_GCC_4_6 +#define BR_GCC_4_5 1 +#endif +#if BR_GCC_4_5 +#define BR_GCC_4_4 1 +#endif + +#endif +#endif + +/* + * Clang thresholds are on versions 3.7.0 and 3.8.0. + */ +#ifndef BR_CLANG +#if __clang__ +#define BR_CLANG 1 + +#if __clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ >= 8) +#define BR_CLANG_3_8 1 +#elif __clang_major__ == 3 && __clang_minor__ >= 7 +#define BR_CLANG_3_7 1 +#endif + +#if BR_CLANG_3_8 +#define BR_CLANG_3_7 1 +#endif + +#endif +#endif + +/* + * MS Visual C thresholds are on Visual Studio 2005 to 2015. + */ +#ifndef BR_MSC +#if _MSC_VER +#define BR_MSC 1 + +#if _MSC_VER >= 1900 +#define BR_MSC_2015 1 +#elif _MSC_VER >= 1800 +#define BR_MSC_2013 1 +#elif _MSC_VER >= 1700 +#define BR_MSC_2012 1 +#elif _MSC_VER >= 1600 +#define BR_MSC_2010 1 +#elif _MSC_VER >= 1500 +#define BR_MSC_2008 1 +#elif _MSC_VER >= 1400 +#define BR_MSC_2005 1 +#endif + +#if BR_MSC_2015 +#define BR_MSC_2013 1 +#endif +#if BR_MSC_2013 +#define BR_MSC_2012 1 +#endif +#if BR_MSC_2012 +#define BR_MSC_2010 1 +#endif +#if BR_MSC_2010 +#define BR_MSC_2008 1 +#endif +#if BR_MSC_2008 +#define BR_MSC_2005 1 +#endif + +#endif +#endif + +/* + * GCC 4.4+ and Clang 3.7+ allow tagging specific functions with a + * 'target' attribute that activates support for specific opcodes. + */ +#if BR_GCC_4_4 || BR_CLANG_3_7 +#define BR_TARGET(x) __attribute__((target(x))) +#else +#define BR_TARGET(x) +#endif + +/* + * AES-NI intrinsics are available on x86 (32-bit and 64-bit) with + * GCC 4.8+, Clang 3.7+ and MSC 2012+. + */ +#ifndef BR_AES_X86NI +#if (BR_i386 || BR_amd64) && (BR_GCC_4_8 || BR_CLANG_3_7 || BR_MSC_2012) +#define BR_AES_X86NI 1 +#endif +#endif + +/* + * SSE2 intrinsics are available on x86 (32-bit and 64-bit) with + * GCC 4.4+, Clang 3.7+ and MSC 2005+. + */ +#ifndef BR_SSE2 +#if (BR_i386 || BR_amd64) && (BR_GCC_4_4 || BR_CLANG_3_7 || BR_MSC_2005) +#define BR_SSE2 1 +#endif +#endif + +/* + * RDRAND intrinsics are available on x86 (32-bit and 64-bit) with + * GCC 4.6+, Clang 3.7+ and MSC 2012+. + */ +#ifndef BR_RDRAND +#if (BR_i386 || BR_amd64) && (BR_GCC_4_6 || BR_CLANG_3_7 || BR_MSC_2012) +#define BR_RDRAND 1 +#endif +#endif + +/* + * Use ESP8266 hardware random generator when possible. + */ +#ifndef BR_USE_ESP8266_RAND +#if defined(ESP8266) +#define BR_USE_ESP8266_RAND 1 +#endif +#endif + +/* + * Determine type of OS for random number generation. Macro names and + * values are documented on: + * https://sourceforge.net/p/predef/wiki/OperatingSystems/ + * + * TODO: enrich the list of detected system. Also add detection for + * alternate system calls like getentropy(), which are usually + * preferable when available. + */ + +#ifndef BR_USE_URANDOM +#if defined _AIX \ + || defined __ANDROID__ \ + || defined __FreeBSD__ \ + || defined __NetBSD__ \ + || defined __OpenBSD__ \ + || defined __DragonFly__ \ + || defined __linux__ \ + || (defined __sun && (defined __SVR4 || defined __svr4__)) \ + || (defined __APPLE__ && defined __MACH__) +#define BR_USE_URANDOM 1 +#endif +#endif + +#ifndef BR_USE_WIN32_RAND +#if defined _WIN32 || defined _WIN64 +#define BR_USE_WIN32_RAND 1 +#endif +#endif + +/* + * POWER8 crypto support. We rely on compiler macros for the + * architecture, since we do not have a reliable, simple way to detect + * the required support at runtime (we could try running an opcode, and + * trapping the exception or signal on illegal instruction, but this + * induces some non-trivial OS dependencies that we would prefer to + * avoid if possible). + */ +#ifndef BR_POWER8 +#if __GNUC__ && ((_ARCH_PWR8 || _ARCH_PPC) && __CRYPTO__) +#define BR_POWER8 1 +#endif +#endif + +/* + * Detect endinanness on POWER8. + */ +#if BR_POWER8 +#if defined BR_POWER8_LE +#undef BR_POWER8_BE +#if BR_POWER8_LE +#define BR_POWER8_BE 0 +#else +#define BR_POWER8_BE 1 +#endif +#elif defined BR_POWER8_BE +#undef BR_POWER8_LE +#if BR_POWER8_BE +#define BR_POWER8_LE 0 +#else +#define BR_POWER8_LE 1 +#endif +#else +#if __LITTLE_ENDIAN__ +#define BR_POWER8_LE 1 +#define BR_POWER8_BE 0 +#else +#define BR_POWER8_LE 0 +#define BR_POWER8_BE 1 +#endif +#endif +#endif + +/* + * Detect support for 128-bit integers. + */ +#if !defined BR_INT128 && !defined BR_UMUL128 +#ifdef __SIZEOF_INT128__ +#define BR_INT128 1 +#elif _M_X64 +#define BR_UMUL128 1 +#endif +#endif + +/* + * Detect support for unaligned accesses with known endianness. + * + * x86 (both 32-bit and 64-bit) is little-endian and allows unaligned + * accesses. + * + * POWER/PowerPC allows unaligned accesses when big-endian. POWER8 and + * later also allow unaligned accesses when little-endian. + */ +#if !defined BR_LE_UNALIGNED && !defined BR_BE_UNALIGNED + +#if __i386 || __i386__ || __x86_64__ || _M_IX86 || _M_X64 +#define BR_LE_UNALIGNED 1 +#elif BR_POWER8_BE +#define BR_BE_UNALIGNED 1 +#elif BR_POWER8_LE +#define BR_LE_UNALIGNED 1 +#elif (__powerpc__ || __powerpc64__ || _M_PPC || _ARCH_PPC || _ARCH_PPC64) \ + && __BIG_ENDIAN__ +#define BR_BE_UNALIGNED 1 +#endif + +#endif + +/* + * Detect support for an OS-provided time source. + */ + +#ifndef BR_USE_UNIX_TIME +#if defined __unix__ || defined __linux__ || defined ESP8266 \ + || defined _POSIX_SOURCE || defined _POSIX_C_SOURCE \ + || (defined __APPLE__ && defined __MACH__) +#define BR_USE_UNIX_TIME 1 +#endif +#endif + +#ifndef BR_USE_WIN32_TIME +#if defined _WIN32 || defined _WIN64 +#define BR_USE_WIN32_TIME 1 +#endif +#endif + +/* ==================================================================== */ +/* + * Encoding/decoding functions. + * + * 32-bit and 64-bit decoding, both little-endian and big-endian, is + * implemented with the inline functions below. + * + * When allowed by some compile-time options (autodetected or provided), + * optimised code is used, to perform direct memory access when the + * underlying architecture supports it, both for endianness and + * alignment. This, however, may trigger strict aliasing issues; the + * code below uses unions to perform (supposedly) safe type punning. + * Since the C aliasing rules are relatively complex and were amended, + * or at least re-explained with different phrasing, in all successive + * versions of the C standard, it is always a bit risky to bet that any + * specific version of a C compiler got it right, for some notion of + * "right". + */ + +typedef union { + uint16_t u; + unsigned char b[sizeof(uint16_t)]; +} br_union_u16; + +typedef union { + uint32_t u; + unsigned char b[sizeof(uint32_t)]; +} br_union_u32; + +typedef union { + uint64_t u; + unsigned char b[sizeof(uint64_t)]; +} br_union_u64; + +static inline void +br_enc16le(void *dst, unsigned x) +{ +#if BR_LE_UNALIGNED + ((br_union_u16 *)dst)->u = x; +#else + unsigned char *buf; + + buf = dst; + buf[0] = (unsigned char)x; + buf[1] = (unsigned char)(x >> 8); +#endif +} + +static inline void +br_enc16be(void *dst, unsigned x) +{ +#if BR_BE_UNALIGNED + ((br_union_u16 *)dst)->u = x; +#else + unsigned char *buf; + + buf = dst; + buf[0] = (unsigned char)(x >> 8); + buf[1] = (unsigned char)x; +#endif +} + +static inline unsigned +br_dec16le(const void *src) +{ +#if BR_LE_UNALIGNED + return ((const br_union_u16 *)src)->u; +#else + const unsigned char *buf; + + buf = src; + return (unsigned)buf[0] | ((unsigned)buf[1] << 8); +#endif +} + +static inline unsigned +br_dec16be(const void *src) +{ +#if BR_BE_UNALIGNED + return ((const br_union_u16 *)src)->u; +#else + const unsigned char *buf; + + buf = src; + return ((unsigned)buf[0] << 8) | (unsigned)buf[1]; +#endif +} + +static inline void +br_enc32le(void *dst, uint32_t x) +{ +#if BR_LE_UNALIGNED + ((br_union_u32 *)dst)->u = x; +#else + unsigned char *buf; + + buf = dst; + buf[0] = (unsigned char)x; + buf[1] = (unsigned char)(x >> 8); + buf[2] = (unsigned char)(x >> 16); + buf[3] = (unsigned char)(x >> 24); +#endif +} + +static inline void +br_enc32be(void *dst, uint32_t x) +{ +#if BR_BE_UNALIGNED + ((br_union_u32 *)dst)->u = x; +#else + unsigned char *buf; + + buf = dst; + buf[0] = (unsigned char)(x >> 24); + buf[1] = (unsigned char)(x >> 16); + buf[2] = (unsigned char)(x >> 8); + buf[3] = (unsigned char)x; +#endif +} + +static inline uint32_t +br_dec32le(const void *src) +{ +#if BR_LE_UNALIGNED + return ((const br_union_u32 *)src)->u; +#else + const unsigned char *buf; + + buf = src; + return (uint32_t)buf[0] + | ((uint32_t)buf[1] << 8) + | ((uint32_t)buf[2] << 16) + | ((uint32_t)buf[3] << 24); +#endif +} + +static inline uint32_t +br_dec32be(const void *src) +{ +#if BR_BE_UNALIGNED + return ((const br_union_u32 *)src)->u; +#else + const unsigned char *buf; + + buf = src; + return ((uint32_t)buf[0] << 24) + | ((uint32_t)buf[1] << 16) + | ((uint32_t)buf[2] << 8) + | (uint32_t)buf[3]; +#endif +} + +static inline void +br_enc64le(void *dst, uint64_t x) +{ +#if BR_LE_UNALIGNED + ((br_union_u64 *)dst)->u = x; +#else + unsigned char *buf; + + buf = dst; + br_enc32le(buf, (uint32_t)x); + br_enc32le(buf + 4, (uint32_t)(x >> 32)); +#endif +} + +static inline void +br_enc64be(void *dst, uint64_t x) +{ +#if BR_BE_UNALIGNED + ((br_union_u64 *)dst)->u = x; +#else + unsigned char *buf; + + buf = dst; + br_enc32be(buf, (uint32_t)(x >> 32)); + br_enc32be(buf + 4, (uint32_t)x); +#endif +} + +static inline uint64_t +br_dec64le(const void *src) +{ +#if BR_LE_UNALIGNED + return ((const br_union_u64 *)src)->u; +#else + const unsigned char *buf; + + buf = src; + return (uint64_t)br_dec32le(buf) + | ((uint64_t)br_dec32le(buf + 4) << 32); +#endif +} + +static inline uint64_t +br_dec64be(const void *src) +{ +#if BR_BE_UNALIGNED + return ((const br_union_u64 *)src)->u; +#else + const unsigned char *buf; + + buf = src; + return ((uint64_t)br_dec32be(buf) << 32) + | (uint64_t)br_dec32be(buf + 4); +#endif +} + +/* + * Range decoding and encoding (for several successive values). + */ +void br_range_dec16le(uint16_t *v, size_t num, const void *src); +void br_range_dec16be(uint16_t *v, size_t num, const void *src); +void br_range_enc16le(void *dst, const uint16_t *v, size_t num); +void br_range_enc16be(void *dst, const uint16_t *v, size_t num); + +void br_range_dec32le(uint32_t *v, size_t num, const void *src); +void br_range_dec32be(uint32_t *v, size_t num, const void *src); +void br_range_enc32le(void *dst, const uint32_t *v, size_t num); +void br_range_enc32be(void *dst, const uint32_t *v, size_t num); + +void br_range_dec64le(uint64_t *v, size_t num, const void *src); +void br_range_dec64be(uint64_t *v, size_t num, const void *src); +void br_range_enc64le(void *dst, const uint64_t *v, size_t num); +void br_range_enc64be(void *dst, const uint64_t *v, size_t num); + +/* + * Byte-swap a 32-bit integer. + */ +static inline uint32_t +br_swap32(uint32_t x) +{ + x = ((x & (uint32_t)0x00FF00FF) << 8) + | ((x >> 8) & (uint32_t)0x00FF00FF); + return (x << 16) | (x >> 16); +} + +/* ==================================================================== */ +/* + * Support code for hash functions. + */ + +/* + * IV for MD5, SHA-1, SHA-224 and SHA-256. + */ +extern const uint32_t br_md5_IV[]; +extern const uint32_t br_sha1_IV[]; +extern const uint32_t br_sha224_IV[]; +extern const uint32_t br_sha256_IV[]; + +/* + * Round functions for MD5, SHA-1, SHA-224 and SHA-256 (SHA-224 and + * SHA-256 use the same round function). + */ +void br_md5_round(const unsigned char *buf, uint32_t *val); +void br_sha1_round(const unsigned char *buf, uint32_t *val); +void br_sha2small_round(const unsigned char *buf, uint32_t *val); + +/* + * The core function for the TLS PRF. It computes + * P_hash(secret, label + seed), and XORs the result into the dst buffer. + */ +void br_tls_phash(void *dst, size_t len, + const br_hash_class *dig, + const void *secret, size_t secret_len, const char *label, + size_t seed_num, const br_tls_prf_seed_chunk *seed); + +/* + * Copy all configured hash implementations from a multihash context + * to another. + */ +static inline void +br_multihash_copyimpl(br_multihash_context *dst, + const br_multihash_context *src) +{ + memcpy((void *)dst->impl, src->impl, sizeof src->impl); +} + +/* ==================================================================== */ +/* + * Constant-time primitives. These functions manipulate 32-bit values in + * order to provide constant-time comparisons and multiplexers. + * + * Boolean values (the "ctl" bits) MUST have value 0 or 1. + * + * Implementation notes: + * ===================== + * + * The uintN_t types are unsigned and with width exactly N bits; the C + * standard guarantees that computations are performed modulo 2^N, and + * there can be no overflow. Negation (unary '-') works on unsigned types + * as well. + * + * The intN_t types are guaranteed to have width exactly N bits, with no + * padding bit, and using two's complement representation. Casting + * intN_t to uintN_t really is conversion modulo 2^N. Beware that intN_t + * types, being signed, trigger implementation-defined behaviour on + * overflow (including raising some signal): with GCC, while modular + * arithmetics are usually applied, the optimizer may assume that + * overflows don't occur (unless the -fwrapv command-line option is + * added); Clang has the additional -ftrapv option to explicitly trap on + * integer overflow or underflow. + */ + +/* + * Negate a boolean. + */ +static inline uint32_t +NOT(uint32_t ctl) +{ + return ctl ^ 1; +} + +/* + * Multiplexer: returns x if ctl == 1, y if ctl == 0. + */ +static inline uint32_t +MUX(uint32_t ctl, uint32_t x, uint32_t y) +{ + return y ^ (-ctl & (x ^ y)); +} + +/* + * Equality check: returns 1 if x == y, 0 otherwise. + */ +static inline uint32_t +EQ(uint32_t x, uint32_t y) +{ + uint32_t q; + + q = x ^ y; + return NOT((q | -q) >> 31); +} + +/* + * Inequality check: returns 1 if x != y, 0 otherwise. + */ +static inline uint32_t +NEQ(uint32_t x, uint32_t y) +{ + uint32_t q; + + q = x ^ y; + return (q | -q) >> 31; +} + +/* + * Comparison: returns 1 if x > y, 0 otherwise. + */ +static inline uint32_t +GT(uint32_t x, uint32_t y) +{ + /* + * If both x < 2^31 and x < 2^31, then y-x will have its high + * bit set if x > y, cleared otherwise. + * + * If either x >= 2^31 or y >= 2^31 (but not both), then the + * result is the high bit of x. + * + * If both x >= 2^31 and y >= 2^31, then we can virtually + * subtract 2^31 from both, and we are back to the first case. + * Since (y-2^31)-(x-2^31) = y-x, the subtraction is already + * fine. + */ + uint32_t z; + + z = y - x; + return (z ^ ((x ^ y) & (x ^ z))) >> 31; +} + +/* + * Other comparisons (greater-or-equal, lower-than, lower-or-equal). + */ +#define GE(x, y) NOT(GT(y, x)) +#define LT(x, y) GT(y, x) +#define LE(x, y) NOT(GT(x, y)) + +/* + * General comparison: returned value is -1, 0 or 1, depending on + * whether x is lower than, equal to, or greater than y. + */ +static inline int32_t +CMP(uint32_t x, uint32_t y) +{ + return (int32_t)GT(x, y) | -(int32_t)GT(y, x); +} + +/* + * Returns 1 if x == 0, 0 otherwise. Take care that the operand is signed. + */ +static inline uint32_t +EQ0(int32_t x) +{ + uint32_t q; + + q = (uint32_t)x; + return ~(q | -q) >> 31; +} + +/* + * Returns 1 if x > 0, 0 otherwise. Take care that the operand is signed. + */ +static inline uint32_t +GT0(int32_t x) +{ + /* + * High bit of -x is 0 if x == 0, but 1 if x > 0. + */ + uint32_t q; + + q = (uint32_t)x; + return (~q & -q) >> 31; +} + +/* + * Returns 1 if x >= 0, 0 otherwise. Take care that the operand is signed. + */ +static inline uint32_t +GE0(int32_t x) +{ + return ~(uint32_t)x >> 31; +} + +/* + * Returns 1 if x < 0, 0 otherwise. Take care that the operand is signed. + */ +static inline uint32_t +LT0(int32_t x) +{ + return (uint32_t)x >> 31; +} + +/* + * Returns 1 if x <= 0, 0 otherwise. Take care that the operand is signed. + */ +static inline uint32_t +LE0(int32_t x) +{ + uint32_t q; + + /* + * ~-x has its high bit set if and only if -x is nonnegative (as + * a signed int), i.e. x is in the -(2^31-1) to 0 range. We must + * do an OR with x itself to account for x = -2^31. + */ + q = (uint32_t)x; + return (q | ~-q) >> 31; +} + +/* + * Conditional copy: src[] is copied into dst[] if and only if ctl is 1. + * dst[] and src[] may overlap completely (but not partially). + */ +void br_ccopy(uint32_t ctl, void *dst, const void *src, size_t len); + +#define CCOPY br_ccopy + +/* + * Compute the bit length of a 32-bit integer. Returned value is between 0 + * and 32 (inclusive). + */ +static inline uint32_t +BIT_LENGTH(uint32_t x) +{ + uint32_t k, c; + + k = NEQ(x, 0); + c = GT(x, 0xFFFF); x = MUX(c, x >> 16, x); k += c << 4; + c = GT(x, 0x00FF); x = MUX(c, x >> 8, x); k += c << 3; + c = GT(x, 0x000F); x = MUX(c, x >> 4, x); k += c << 2; + c = GT(x, 0x0003); x = MUX(c, x >> 2, x); k += c << 1; + k += GT(x, 0x0001); + return k; +} + +/* + * Compute the minimum of x and y. + */ +static inline uint32_t +MIN(uint32_t x, uint32_t y) +{ + return MUX(GT(x, y), y, x); +} + +/* + * Compute the maximum of x and y. + */ +static inline uint32_t +MAX(uint32_t x, uint32_t y) +{ + return MUX(GT(x, y), x, y); +} + +/* + * Multiply two 32-bit integers, with a 64-bit result. This default + * implementation assumes that the basic multiplication operator + * yields constant-time code. + */ +#define MUL(x, y) ((uint64_t)(x) * (uint64_t)(y)) + +#if BR_CT_MUL31 + +/* + * Alternate implementation of MUL31, that will be constant-time on some + * (old) platforms where the default MUL31 is not. Unfortunately, it is + * also substantially slower, and yields larger code, on more modern + * platforms, which is why it is deactivated by default. + * + * MUL31_lo() must do some extra work because on some platforms, the + * _signed_ multiplication may return early if the top bits are 1. + * Simply truncating (casting) the output of MUL31() would not be + * sufficient, because the compiler may notice that we keep only the low + * word, and then replace automatically the unsigned multiplication with + * a signed multiplication opcode. + */ +#define MUL31(x, y) ((uint64_t)((x) | (uint32_t)0x80000000) \ + * (uint64_t)((y) | (uint32_t)0x80000000) \ + - ((uint64_t)(x) << 31) - ((uint64_t)(y) << 31) \ + - ((uint64_t)1 << 62)) +static inline uint32_t +MUL31_lo(uint32_t x, uint32_t y) +{ + uint32_t xl, xh; + uint32_t yl, yh; + + xl = (x & 0xFFFF) | (uint32_t)0x80000000; + xh = (x >> 16) | (uint32_t)0x80000000; + yl = (y & 0xFFFF) | (uint32_t)0x80000000; + yh = (y >> 16) | (uint32_t)0x80000000; + return (xl * yl + ((xl * yh + xh * yl) << 16)) & (uint32_t)0x7FFFFFFF; +} + +#else + +/* + * Multiply two 31-bit integers, with a 62-bit result. This default + * implementation assumes that the basic multiplication operator + * yields constant-time code. + * The MUL31_lo() macro returns only the low 31 bits of the product. + */ +#define MUL31(x, y) ((uint64_t)(x) * (uint64_t)(y)) +#define MUL31_lo(x, y) (((uint32_t)(x) * (uint32_t)(y)) & (uint32_t)0x7FFFFFFF) + +#endif + +/* + * Multiply two words together; the sum of the lengths of the two + * operands must not exceed 31 (for instance, one operand may use 16 + * bits if the other fits on 15). If BR_CT_MUL15 is non-zero, then the + * macro will contain some extra operations that help in making the + * operation constant-time on some platforms, where the basic 32-bit + * multiplication is not constant-time. + */ +#if BR_CT_MUL15 +#define MUL15(x, y) (((uint32_t)(x) | (uint32_t)0x80000000) \ + * ((uint32_t)(y) | (uint32_t)0x80000000) \ + & (uint32_t)0x7FFFFFFF) +#else +#define MUL15(x, y) ((uint32_t)(x) * (uint32_t)(y)) +#endif + +/* + * Arithmetic right shift (sign bit is copied). What happens when + * right-shifting a negative value is _implementation-defined_, so it + * does not trigger undefined behaviour, but it is still up to each + * compiler to define (and document) what it does. Most/all compilers + * will do an arithmetic shift, the sign bit being used to fill the + * holes; this is a native operation on the underlying CPU, and it would + * make little sense for the compiler to do otherwise. GCC explicitly + * documents that it follows that convention. + * + * Still, if BR_NO_ARITH_SHIFT is defined (and non-zero), then an + * alternate version will be used, that does not rely on such + * implementation-defined behaviour. Unfortunately, it is also slower + * and yields bigger code, which is why it is deactivated by default. + */ +#if BR_NO_ARITH_SHIFT +#define ARSH(x, n) (((uint32_t)(x) >> (n)) \ + | ((-((uint32_t)(x) >> 31)) << (32 - (n)))) +#else +#define ARSH(x, n) ((*(int32_t *)&(x)) >> (n)) +#endif + +/* + * Constant-time division. The dividend hi:lo is divided by the + * divisor d; the quotient is returned and the remainder is written + * in *r. If hi == d, then the quotient does not fit on 32 bits; + * returned value is thus truncated. If hi > d, returned values are + * indeterminate. + */ +uint32_t br_divrem(uint32_t hi, uint32_t lo, uint32_t d, uint32_t *r); + +/* + * Wrapper for br_divrem(); the remainder is returned, and the quotient + * is discarded. + */ +static inline uint32_t +br_rem(uint32_t hi, uint32_t lo, uint32_t d) +{ + uint32_t r; + + br_divrem(hi, lo, d, &r); + return r; +} + +/* + * Wrapper for br_divrem(); the quotient is returned, and the remainder + * is discarded. + */ +static inline uint32_t +br_div(uint32_t hi, uint32_t lo, uint32_t d) +{ + uint32_t r; + + return br_divrem(hi, lo, d, &r); +} + +/* ==================================================================== */ + +/* + * Integers 'i32' + * -------------- + * + * The 'i32' functions implement computations on big integers using + * an internal representation as an array of 32-bit integers. For + * an array x[]: + * -- x[0] contains the "announced bit length" of the integer + * -- x[1], x[2]... contain the value in little-endian order (x[1] + * contains the least significant 32 bits) + * + * Multiplications rely on the elementary 32x32->64 multiplication. + * + * The announced bit length specifies the number of bits that are + * significant in the subsequent 32-bit words. Unused bits in the + * last (most significant) word are set to 0; subsequent words are + * uninitialized and need not exist at all. + * + * The execution time and memory access patterns of all computations + * depend on the announced bit length, but not on the actual word + * values. For modular integers, the announced bit length of any integer + * modulo n is equal to the actual bit length of n; thus, computations + * on modular integers are "constant-time" (only the modulus length may + * leak). + */ + +/* + * Compute the actual bit length of an integer. The argument x should + * point to the first (least significant) value word of the integer. + * The len 'xlen' contains the number of 32-bit words to access. + * + * CT: value or length of x does not leak. + */ +uint32_t br_i32_bit_length(uint32_t *x, size_t xlen); + +/* + * Decode an integer from its big-endian unsigned representation. The + * "true" bit length of the integer is computed, but all words of x[] + * corresponding to the full 'len' bytes of the source are set. + * + * CT: value or length of x does not leak. + */ +void br_i32_decode(uint32_t *x, const void *src, size_t len); + +/* + * Decode an integer from its big-endian unsigned representation. The + * integer MUST be lower than m[]; the announced bit length written in + * x[] will be equal to that of m[]. All 'len' bytes from the source are + * read. + * + * Returned value is 1 if the decode value fits within the modulus, 0 + * otherwise. In the latter case, the x[] buffer will be set to 0 (but + * still with the announced bit length of m[]). + * + * CT: value or length of x does not leak. Memory access pattern depends + * only of 'len' and the announced bit length of m. Whether x fits or + * not does not leak either. + */ +uint32_t br_i32_decode_mod(uint32_t *x, + const void *src, size_t len, const uint32_t *m); + +/* + * Reduce an integer (a[]) modulo another (m[]). The result is written + * in x[] and its announced bit length is set to be equal to that of m[]. + * + * x[] MUST be distinct from a[] and m[]. + * + * CT: only announced bit lengths leak, not values of x, a or m. + */ +void br_i32_reduce(uint32_t *x, const uint32_t *a, const uint32_t *m); + +/* + * Decode an integer from its big-endian unsigned representation, and + * reduce it modulo the provided modulus m[]. The announced bit length + * of the result is set to be equal to that of the modulus. + * + * x[] MUST be distinct from m[]. + */ +void br_i32_decode_reduce(uint32_t *x, + const void *src, size_t len, const uint32_t *m); + +/* + * Encode an integer into its big-endian unsigned representation. The + * output length in bytes is provided (parameter 'len'); if the length + * is too short then the integer is appropriately truncated; if it is + * too long then the extra bytes are set to 0. + */ +void br_i32_encode(void *dst, size_t len, const uint32_t *x); + +/* + * Multiply x[] by 2^32 and then add integer z, modulo m[]. This + * function assumes that x[] and m[] have the same announced bit + * length, and the announced bit length of m[] matches its true + * bit length. + * + * x[] and m[] MUST be distinct arrays. + * + * CT: only the common announced bit length of x and m leaks, not + * the values of x, z or m. + */ +void br_i32_muladd_small(uint32_t *x, uint32_t z, const uint32_t *m); + +/* + * Extract one word from an integer. The offset is counted in bits. + * The word MUST entirely fit within the word elements corresponding + * to the announced bit length of a[]. + */ +static inline uint32_t +br_i32_word(const uint32_t *a, uint32_t off) +{ + size_t u; + unsigned j; + + u = (size_t)(off >> 5) + 1; + j = (unsigned)off & 31; + if (j == 0) { + return a[u]; + } else { + return (a[u] >> j) | (a[u + 1] << (32 - j)); + } +} + +/* + * Test whether an integer is zero. + */ +uint32_t br_i32_iszero(const uint32_t *x); + +/* + * Add b[] to a[] and return the carry (0 or 1). If ctl is 0, then a[] + * is unmodified, but the carry is still computed and returned. The + * arrays a[] and b[] MUST have the same announced bit length. + * + * a[] and b[] MAY be the same array, but partial overlap is not allowed. + */ +uint32_t br_i32_add(uint32_t *a, const uint32_t *b, uint32_t ctl); + +/* + * Subtract b[] from a[] and return the carry (0 or 1). If ctl is 0, + * then a[] is unmodified, but the carry is still computed and returned. + * The arrays a[] and b[] MUST have the same announced bit length. + * + * a[] and b[] MAY be the same array, but partial overlap is not allowed. + */ +uint32_t br_i32_sub(uint32_t *a, const uint32_t *b, uint32_t ctl); + +/* + * Compute d+a*b, result in d. The initial announced bit length of d[] + * MUST match that of a[]. The d[] array MUST be large enough to + * accommodate the full result, plus (possibly) an extra word. The + * resulting announced bit length of d[] will be the sum of the announced + * bit lengths of a[] and b[] (therefore, it may be larger than the actual + * bit length of the numerical result). + * + * a[] and b[] may be the same array. d[] must be disjoint from both a[] + * and b[]. + */ +void br_i32_mulacc(uint32_t *d, const uint32_t *a, const uint32_t *b); + +/* + * Zeroize an integer. The announced bit length is set to the provided + * value, and the corresponding words are set to 0. + */ +static inline void +br_i32_zero(uint32_t *x, uint32_t bit_len) +{ + *x ++ = bit_len; + memset(x, 0, ((bit_len + 31) >> 5) * sizeof *x); +} + +/* + * Compute -(1/x) mod 2^32. If x is even, then this function returns 0. + */ +uint32_t br_i32_ninv32(uint32_t x); + +/* + * Convert a modular integer to Montgomery representation. The integer x[] + * MUST be lower than m[], but with the same announced bit length. + */ +void br_i32_to_monty(uint32_t *x, const uint32_t *m); + +/* + * Convert a modular integer back from Montgomery representation. The + * integer x[] MUST be lower than m[], but with the same announced bit + * length. The "m0i" parameter is equal to -(1/m0) mod 2^32, where m0 is + * the least significant value word of m[] (this works only if m[] is + * an odd integer). + */ +void br_i32_from_monty(uint32_t *x, const uint32_t *m, uint32_t m0i); + +/* + * Compute a modular Montgomery multiplication. d[] is filled with the + * value of x*y/R modulo m[] (where R is the Montgomery factor). The + * array d[] MUST be distinct from x[], y[] and m[]. x[] and y[] MUST be + * numerically lower than m[]. x[] and y[] MAY be the same array. The + * "m0i" parameter is equal to -(1/m0) mod 2^32, where m0 is the least + * significant value word of m[] (this works only if m[] is an odd + * integer). + */ +void br_i32_montymul(uint32_t *d, const uint32_t *x, const uint32_t *y, + const uint32_t *m, uint32_t m0i); + +/* + * Compute a modular exponentiation. x[] MUST be an integer modulo m[] + * (same announced bit length, lower value). m[] MUST be odd. The + * exponent is in big-endian unsigned notation, over 'elen' bytes. The + * "m0i" parameter is equal to -(1/m0) mod 2^32, where m0 is the least + * significant value word of m[] (this works only if m[] is an odd + * integer). The t1[] and t2[] parameters must be temporary arrays, + * each large enough to accommodate an integer with the same size as m[]. + */ +void br_i32_modpow(uint32_t *x, const unsigned char *e, size_t elen, + const uint32_t *m, uint32_t m0i, uint32_t *t1, uint32_t *t2); + +/* ==================================================================== */ + +/* + * Integers 'i31' + * -------------- + * + * The 'i31' functions implement computations on big integers using + * an internal representation as an array of 32-bit integers. For + * an array x[]: + * -- x[0] encodes the array length and the "announced bit length" + * of the integer: namely, if the announced bit length is k, + * then x[0] = ((k / 31) << 5) + (k % 31). + * -- x[1], x[2]... contain the value in little-endian order, 31 + * bits per word (x[1] contains the least significant 31 bits). + * The upper bit of each word is 0. + * + * Multiplications rely on the elementary 32x32->64 multiplication. + * + * The announced bit length specifies the number of bits that are + * significant in the subsequent 32-bit words. Unused bits in the + * last (most significant) word are set to 0; subsequent words are + * uninitialized and need not exist at all. + * + * The execution time and memory access patterns of all computations + * depend on the announced bit length, but not on the actual word + * values. For modular integers, the announced bit length of any integer + * modulo n is equal to the actual bit length of n; thus, computations + * on modular integers are "constant-time" (only the modulus length may + * leak). + */ + +/* + * Test whether an integer is zero. + */ +uint32_t br_i31_iszero(const uint32_t *x); + +/* + * Add b[] to a[] and return the carry (0 or 1). If ctl is 0, then a[] + * is unmodified, but the carry is still computed and returned. The + * arrays a[] and b[] MUST have the same announced bit length. + * + * a[] and b[] MAY be the same array, but partial overlap is not allowed. + */ +uint32_t br_i31_add(uint32_t *a, const uint32_t *b, uint32_t ctl); + +/* + * Subtract b[] from a[] and return the carry (0 or 1). If ctl is 0, + * then a[] is unmodified, but the carry is still computed and returned. + * The arrays a[] and b[] MUST have the same announced bit length. + * + * a[] and b[] MAY be the same array, but partial overlap is not allowed. + */ +uint32_t br_i31_sub(uint32_t *a, const uint32_t *b, uint32_t ctl); + +/* + * Compute the ENCODED actual bit length of an integer. The argument x + * should point to the first (least significant) value word of the + * integer. The len 'xlen' contains the number of 32-bit words to + * access. The upper bit of each value word MUST be 0. + * Returned value is ((k / 31) << 5) + (k % 31) if the bit length is k. + * + * CT: value or length of x does not leak. + */ +uint32_t br_i31_bit_length(uint32_t *x, size_t xlen); + +/* + * Decode an integer from its big-endian unsigned representation. The + * "true" bit length of the integer is computed and set in the encoded + * announced bit length (x[0]), but all words of x[] corresponding to + * the full 'len' bytes of the source are set. + * + * CT: value or length of x does not leak. + */ +void br_i31_decode(uint32_t *x, const void *src, size_t len); + +/* + * Decode an integer from its big-endian unsigned representation. The + * integer MUST be lower than m[]; the (encoded) announced bit length + * written in x[] will be equal to that of m[]. All 'len' bytes from the + * source are read. + * + * Returned value is 1 if the decode value fits within the modulus, 0 + * otherwise. In the latter case, the x[] buffer will be set to 0 (but + * still with the announced bit length of m[]). + * + * CT: value or length of x does not leak. Memory access pattern depends + * only of 'len' and the announced bit length of m. Whether x fits or + * not does not leak either. + */ +uint32_t br_i31_decode_mod(uint32_t *x, + const void *src, size_t len, const uint32_t *m); + +/* + * Zeroize an integer. The announced bit length is set to the provided + * value, and the corresponding words are set to 0. The ENCODED bit length + * is expected here. + */ +static inline void +br_i31_zero(uint32_t *x, uint32_t bit_len) +{ + *x ++ = bit_len; + memset(x, 0, ((bit_len + 31) >> 5) * sizeof *x); +} + +/* + * Right-shift an integer. The shift amount must be lower than 31 + * bits. + */ +void br_i31_rshift(uint32_t *x, int count); + +/* + * Reduce an integer (a[]) modulo another (m[]). The result is written + * in x[] and its announced bit length is set to be equal to that of m[]. + * + * x[] MUST be distinct from a[] and m[]. + * + * CT: only announced bit lengths leak, not values of x, a or m. + */ +void br_i31_reduce(uint32_t *x, const uint32_t *a, const uint32_t *m); + +/* + * Decode an integer from its big-endian unsigned representation, and + * reduce it modulo the provided modulus m[]. The announced bit length + * of the result is set to be equal to that of the modulus. + * + * x[] MUST be distinct from m[]. + */ +void br_i31_decode_reduce(uint32_t *x, + const void *src, size_t len, const uint32_t *m); + +/* + * Multiply x[] by 2^31 and then add integer z, modulo m[]. This + * function assumes that x[] and m[] have the same announced bit + * length, the announced bit length of m[] matches its true + * bit length. + * + * x[] and m[] MUST be distinct arrays. z MUST fit in 31 bits (upper + * bit set to 0). + * + * CT: only the common announced bit length of x and m leaks, not + * the values of x, z or m. + */ +void br_i31_muladd_small(uint32_t *x, uint32_t z, const uint32_t *m); + +/* + * Encode an integer into its big-endian unsigned representation. The + * output length in bytes is provided (parameter 'len'); if the length + * is too short then the integer is appropriately truncated; if it is + * too long then the extra bytes are set to 0. + */ +void br_i31_encode(void *dst, size_t len, const uint32_t *x); + +/* + * Compute -(1/x) mod 2^31. If x is even, then this function returns 0. + */ +uint32_t br_i31_ninv31(uint32_t x); + +/* + * Compute a modular Montgomery multiplication. d[] is filled with the + * value of x*y/R modulo m[] (where R is the Montgomery factor). The + * array d[] MUST be distinct from x[], y[] and m[]. x[] and y[] MUST be + * numerically lower than m[]. x[] and y[] MAY be the same array. The + * "m0i" parameter is equal to -(1/m0) mod 2^31, where m0 is the least + * significant value word of m[] (this works only if m[] is an odd + * integer). + */ +void br_i31_montymul(uint32_t *d, const uint32_t *x, const uint32_t *y, + const uint32_t *m, uint32_t m0i); + +/* + * Convert a modular integer to Montgomery representation. The integer x[] + * MUST be lower than m[], but with the same announced bit length. + */ +void br_i31_to_monty(uint32_t *x, const uint32_t *m); + +/* + * Convert a modular integer back from Montgomery representation. The + * integer x[] MUST be lower than m[], but with the same announced bit + * length. The "m0i" parameter is equal to -(1/m0) mod 2^32, where m0 is + * the least significant value word of m[] (this works only if m[] is + * an odd integer). + */ +void br_i31_from_monty(uint32_t *x, const uint32_t *m, uint32_t m0i); + +/* + * Compute a modular exponentiation. x[] MUST be an integer modulo m[] + * (same announced bit length, lower value). m[] MUST be odd. The + * exponent is in big-endian unsigned notation, over 'elen' bytes. The + * "m0i" parameter is equal to -(1/m0) mod 2^31, where m0 is the least + * significant value word of m[] (this works only if m[] is an odd + * integer). The t1[] and t2[] parameters must be temporary arrays, + * each large enough to accommodate an integer with the same size as m[]. + */ +void br_i31_modpow(uint32_t *x, const unsigned char *e, size_t elen, + const uint32_t *m, uint32_t m0i, uint32_t *t1, uint32_t *t2); + +/* + * Compute a modular exponentiation. x[] MUST be an integer modulo m[] + * (same announced bit length, lower value). m[] MUST be odd. The + * exponent is in big-endian unsigned notation, over 'elen' bytes. The + * "m0i" parameter is equal to -(1/m0) mod 2^31, where m0 is the least + * significant value word of m[] (this works only if m[] is an odd + * integer). The tmp[] array is used for temporaries, and has size + * 'twlen' words; it must be large enough to accommodate at least two + * temporary values with the same size as m[] (including the leading + * "bit length" word). If there is room for more temporaries, then this + * function may use the extra room for window-based optimisation, + * resulting in faster computations. + * + * Returned value is 1 on success, 0 on error. An error is reported if + * the provided tmp[] array is too short. + */ +uint32_t br_i31_modpow_opt(uint32_t *x, const unsigned char *e, size_t elen, + const uint32_t *m, uint32_t m0i, uint32_t *tmp, size_t twlen); + +/* + * Compute d+a*b, result in d. The initial announced bit length of d[] + * MUST match that of a[]. The d[] array MUST be large enough to + * accommodate the full result, plus (possibly) an extra word. The + * resulting announced bit length of d[] will be the sum of the announced + * bit lengths of a[] and b[] (therefore, it may be larger than the actual + * bit length of the numerical result). + * + * a[] and b[] may be the same array. d[] must be disjoint from both a[] + * and b[]. + */ +void br_i31_mulacc(uint32_t *d, const uint32_t *a, const uint32_t *b); + +/* + * Compute x/y mod m, result in x. Values x and y must be between 0 and + * m-1, and have the same announced bit length as m. Modulus m must be + * odd. The "m0i" parameter is equal to -1/m mod 2^31. The array 't' + * must point to a temporary area that can hold at least three integers + * of the size of m. + * + * m may not overlap x and y. x and y may overlap each other (this can + * be useful to test whether a value is invertible modulo m). t must be + * disjoint from all other arrays. + * + * Returned value is 1 on success, 0 otherwise. Success is attained if + * y is invertible modulo m. + */ +uint32_t br_i31_moddiv(uint32_t *x, const uint32_t *y, + const uint32_t *m, uint32_t m0i, uint32_t *t); + +/* ==================================================================== */ + +/* + * FIXME: document "i15" functions. + */ + +static inline void +br_i15_zero(uint16_t *x, uint16_t bit_len) +{ + *x ++ = bit_len; + memset(x, 0, ((bit_len + 15) >> 4) * sizeof *x); +} + +uint32_t br_i15_iszero(const uint16_t *x); + +uint16_t br_i15_ninv15(uint16_t x); + +uint32_t br_i15_add(uint16_t *a, const uint16_t *b, uint32_t ctl); + +uint32_t br_i15_sub(uint16_t *a, const uint16_t *b, uint32_t ctl); + +void br_i15_muladd_small(uint16_t *x, uint16_t z, const uint16_t *m); + +void br_i15_montymul(uint16_t *d, const uint16_t *x, const uint16_t *y, + const uint16_t *m, uint16_t m0i); + +void br_i15_to_monty(uint16_t *x, const uint16_t *m); + +void br_i15_modpow(uint16_t *x, const unsigned char *e, size_t elen, + const uint16_t *m, uint16_t m0i, uint16_t *t1, uint16_t *t2); + +uint32_t br_i15_modpow_opt(uint16_t *x, const unsigned char *e, size_t elen, + const uint16_t *m, uint16_t m0i, uint16_t *tmp, size_t twlen); + +void br_i15_encode(void *dst, size_t len, const uint16_t *x); + +uint32_t br_i15_decode_mod(uint16_t *x, + const void *src, size_t len, const uint16_t *m); + +void br_i15_rshift(uint16_t *x, int count); + +uint32_t br_i15_bit_length(uint16_t *x, size_t xlen); + +void br_i15_decode(uint16_t *x, const void *src, size_t len); + +void br_i15_from_monty(uint16_t *x, const uint16_t *m, uint16_t m0i); + +void br_i15_decode_reduce(uint16_t *x, + const void *src, size_t len, const uint16_t *m); + +void br_i15_reduce(uint16_t *x, const uint16_t *a, const uint16_t *m); + +void br_i15_mulacc(uint16_t *d, const uint16_t *a, const uint16_t *b); + +uint32_t br_i15_moddiv(uint16_t *x, const uint16_t *y, + const uint16_t *m, uint16_t m0i, uint16_t *t); + +/* + * Variant of br_i31_modpow_opt() that internally uses 64x64->128 + * multiplications. It expects the same parameters as br_i31_modpow_opt(), + * except that the temporaries should be 64-bit integers, not 32-bit + * integers. + */ +uint32_t br_i62_modpow_opt(uint32_t *x31, const unsigned char *e, size_t elen, + const uint32_t *m31, uint32_t m0i31, uint64_t *tmp, size_t twlen); + +/* + * Type for a function with the same API as br_i31_modpow_opt() (some + * implementations of this type may have stricter alignment requirements + * on the temporaries). + */ +typedef uint32_t (*br_i31_modpow_opt_type)(uint32_t *x, + const unsigned char *e, size_t elen, + const uint32_t *m, uint32_t m0i, uint32_t *tmp, size_t twlen); + +/* + * Wrapper for br_i62_modpow_opt() that uses the same type as + * br_i31_modpow_opt(); however, it requires its 'tmp' argument to the + * 64-bit aligned. + */ +uint32_t br_i62_modpow_opt_as_i31(uint32_t *x, + const unsigned char *e, size_t elen, + const uint32_t *m, uint32_t m0i, uint32_t *tmp, size_t twlen); + +/* ==================================================================== */ + +static inline size_t +br_digest_size(const br_hash_class *digest_class) +{ + return (size_t)(digest_class->desc >> BR_HASHDESC_OUT_OFF) + & BR_HASHDESC_OUT_MASK; +} + +/* + * Get the output size (in bytes) of a hash function. + */ +size_t br_digest_size_by_ID(int digest_id); + +/* + * Get the OID (encoded OBJECT IDENTIFIER value, without tag and length) + * for a hash function. If digest_id is not a supported digest identifier + * (in particular if it is equal to 0, i.e. br_md5sha1_ID), then NULL is + * returned and *len is set to 0. + */ +const unsigned char *br_digest_OID(int digest_id, size_t *len); + +/* ==================================================================== */ +/* + * DES support functions. + */ + +/* + * Apply DES Initial Permutation. + */ +void br_des_do_IP(uint32_t *xl, uint32_t *xr); + +/* + * Apply DES Final Permutation (inverse of IP). + */ +void br_des_do_invIP(uint32_t *xl, uint32_t *xr); + +/* + * Key schedule unit: for a DES key (8 bytes), compute 16 subkeys. Each + * subkey is two 28-bit words represented as two 32-bit words; the PC-2 + * bit extration is NOT applied. + */ +void br_des_keysched_unit(uint32_t *skey, const void *key); + +/* + * Reversal of 16 DES sub-keys (for decryption). + */ +void br_des_rev_skey(uint32_t *skey); + +/* + * DES/3DES key schedule for 'des_tab' (encryption direction). Returned + * value is the number of rounds. + */ +unsigned br_des_tab_keysched(uint32_t *skey, const void *key, size_t key_len); + +/* + * DES/3DES key schedule for 'des_ct' (encryption direction). Returned + * value is the number of rounds. + */ +unsigned br_des_ct_keysched(uint32_t *skey, const void *key, size_t key_len); + +/* + * DES/3DES subkey decompression (from the compressed bitsliced subkeys). + */ +void br_des_ct_skey_expand(uint32_t *sk_exp, + unsigned num_rounds, const uint32_t *skey); + +/* + * DES/3DES block encryption/decryption ('des_tab'). + */ +void br_des_tab_process_block(unsigned num_rounds, + const uint32_t *skey, void *block); + +/* + * DES/3DES block encryption/decryption ('des_ct'). + */ +void br_des_ct_process_block(unsigned num_rounds, + const uint32_t *skey, void *block); + +/* ==================================================================== */ +/* + * AES support functions. + */ + +/* + * The AES S-box (256-byte table). + */ +extern const unsigned char br_aes_S[]; + +/* + * AES key schedule. skey[] is filled with n+1 128-bit subkeys, where n + * is the number of rounds (10 to 14, depending on key size). The number + * of rounds is returned. If the key size is invalid (not 16, 24 or 32), + * then 0 is returned. + * + * This implementation uses a 256-byte table and is NOT constant-time. + */ +unsigned br_aes_keysched(uint32_t *skey, const void *key, size_t key_len); + +/* + * AES key schedule for decryption ('aes_big' implementation). + */ +unsigned br_aes_big_keysched_inv(uint32_t *skey, + const void *key, size_t key_len); + +/* + * AES block encryption with the 'aes_big' implementation (fast, but + * not constant-time). This function encrypts a single block "in place". + */ +void br_aes_big_encrypt(unsigned num_rounds, const uint32_t *skey, void *data); + +/* + * AES block decryption with the 'aes_big' implementation (fast, but + * not constant-time). This function decrypts a single block "in place". + */ +void br_aes_big_decrypt(unsigned num_rounds, const uint32_t *skey, void *data); + +/* + * AES block encryption with the 'aes_small' implementation (small, but + * slow and not constant-time). This function encrypts a single block + * "in place". + */ +void br_aes_small_encrypt(unsigned num_rounds, + const uint32_t *skey, void *data); + +/* + * AES block decryption with the 'aes_small' implementation (small, but + * slow and not constant-time). This function decrypts a single block + * "in place". + */ +void br_aes_small_decrypt(unsigned num_rounds, + const uint32_t *skey, void *data); + +/* + * The constant-time implementation is "bitsliced": the 128-bit state is + * split over eight 32-bit words q* in the following way: + * + * -- Input block consists in 16 bytes: + * a00 a10 a20 a30 a01 a11 a21 a31 a02 a12 a22 a32 a03 a13 a23 a33 + * In the terminology of FIPS 197, this is a 4x4 matrix which is read + * column by column. + * + * -- Each byte is split into eight bits which are distributed over the + * eight words, at the same rank. Thus, for a byte x at rank k, bit 0 + * (least significant) of x will be at rank k in q0 (if that bit is b, + * then it contributes "b << k" to the value of q0), bit 1 of x will be + * at rank k in q1, and so on. + * + * -- Ranks given to bits are in "row order" and are either all even, or + * all odd. Two independent AES states are thus interleaved, one using + * the even ranks, the other the odd ranks. Row order means: + * a00 a01 a02 a03 a10 a11 a12 a13 a20 a21 a22 a23 a30 a31 a32 a33 + * + * Converting input bytes from two AES blocks to bitslice representation + * is done in the following way: + * -- Decode first block into the four words q0 q2 q4 q6, in that order, + * using little-endian convention. + * -- Decode second block into the four words q1 q3 q5 q7, in that order, + * using little-endian convention. + * -- Call br_aes_ct_ortho(). + * + * Converting back to bytes is done by using the reverse operations. Note + * that br_aes_ct_ortho() is its own inverse. + */ + +/* + * Perform bytewise orthogonalization of eight 32-bit words. Bytes + * of q0..q7 are spread over all words: for a byte x that occurs + * at rank i in q[j] (byte x uses bits 8*i to 8*i+7 in q[j]), the bit + * of rank k in x (0 <= k <= 7) goes to q[k] at rank 8*i+j. + * + * This operation is an involution. + */ +void br_aes_ct_ortho(uint32_t *q); + +/* + * The AES S-box, as a bitsliced constant-time version. The input array + * consists in eight 32-bit words; 32 S-box instances are computed in + * parallel. Bits 0 to 7 of each S-box input (bit 0 is least significant) + * are spread over the words 0 to 7, at the same rank. + */ +void br_aes_ct_bitslice_Sbox(uint32_t *q); + +/* + * Like br_aes_bitslice_Sbox(), but for the inverse S-box. + */ +void br_aes_ct_bitslice_invSbox(uint32_t *q); + +/* + * Compute AES encryption on bitsliced data. Since input is stored on + * eight 32-bit words, two block encryptions are actually performed + * in parallel. + */ +void br_aes_ct_bitslice_encrypt(unsigned num_rounds, + const uint32_t *skey, uint32_t *q); + +/* + * Compute AES decryption on bitsliced data. Since input is stored on + * eight 32-bit words, two block decryptions are actually performed + * in parallel. + */ +void br_aes_ct_bitslice_decrypt(unsigned num_rounds, + const uint32_t *skey, uint32_t *q); + +/* + * AES key schedule, constant-time version. skey[] is filled with n+1 + * 128-bit subkeys, where n is the number of rounds (10 to 14, depending + * on key size). The number of rounds is returned. If the key size is + * invalid (not 16, 24 or 32), then 0 is returned. + */ +unsigned br_aes_ct_keysched(uint32_t *comp_skey, + const void *key, size_t key_len); + +/* + * Expand AES subkeys as produced by br_aes_ct_keysched(), into + * a larger array suitable for br_aes_ct_bitslice_encrypt() and + * br_aes_ct_bitslice_decrypt(). + */ +void br_aes_ct_skey_expand(uint32_t *skey, + unsigned num_rounds, const uint32_t *comp_skey); + +/* + * For the ct64 implementation, the same bitslicing technique is used, + * but four instances are interleaved. First instance uses bits 0, 4, + * 8, 12,... of each word; second instance uses bits 1, 5, 9, 13,... + * and so on. + */ + +/* + * Perform bytewise orthogonalization of eight 64-bit words. Bytes + * of q0..q7 are spread over all words: for a byte x that occurs + * at rank i in q[j] (byte x uses bits 8*i to 8*i+7 in q[j]), the bit + * of rank k in x (0 <= k <= 7) goes to q[k] at rank 8*i+j. + * + * This operation is an involution. + */ +void br_aes_ct64_ortho(uint64_t *q); + +/* + * Interleave bytes for an AES input block. If input bytes are + * denoted 0123456789ABCDEF, and have been decoded with little-endian + * convention (w[0] contains 0123, with '3' being most significant; + * w[1] contains 4567, and so on), then output word q0 will be + * set to 08192A3B (again little-endian convention) and q1 will + * be set to 4C5D6E7F. + */ +void br_aes_ct64_interleave_in(uint64_t *q0, uint64_t *q1, const uint32_t *w); + +/* + * Perform the opposite of br_aes_ct64_interleave_in(). + */ +void br_aes_ct64_interleave_out(uint32_t *w, uint64_t q0, uint64_t q1); + +/* + * The AES S-box, as a bitsliced constant-time version. The input array + * consists in eight 64-bit words; 64 S-box instances are computed in + * parallel. Bits 0 to 7 of each S-box input (bit 0 is least significant) + * are spread over the words 0 to 7, at the same rank. + */ +void br_aes_ct64_bitslice_Sbox(uint64_t *q); + +/* + * Like br_aes_bitslice_Sbox(), but for the inverse S-box. + */ +void br_aes_ct64_bitslice_invSbox(uint64_t *q); + +/* + * Compute AES encryption on bitsliced data. Since input is stored on + * eight 64-bit words, four block encryptions are actually performed + * in parallel. + */ +void br_aes_ct64_bitslice_encrypt(unsigned num_rounds, + const uint64_t *skey, uint64_t *q); + +/* + * Compute AES decryption on bitsliced data. Since input is stored on + * eight 64-bit words, four block decryptions are actually performed + * in parallel. + */ +void br_aes_ct64_bitslice_decrypt(unsigned num_rounds, + const uint64_t *skey, uint64_t *q); + +/* + * AES key schedule, constant-time version. skey[] is filled with n+1 + * 128-bit subkeys, where n is the number of rounds (10 to 14, depending + * on key size). The number of rounds is returned. If the key size is + * invalid (not 16, 24 or 32), then 0 is returned. + */ +unsigned br_aes_ct64_keysched(uint64_t *comp_skey, + const void *key, size_t key_len); + +/* + * Expand AES subkeys as produced by br_aes_ct64_keysched(), into + * a larger array suitable for br_aes_ct64_bitslice_encrypt() and + * br_aes_ct64_bitslice_decrypt(). + */ +void br_aes_ct64_skey_expand(uint64_t *skey, + unsigned num_rounds, const uint64_t *comp_skey); + +/* + * Test support for AES-NI opcodes. + */ +int br_aes_x86ni_supported(void); + +/* + * AES key schedule, using x86 AES-NI instructions. This yields the + * subkeys in the encryption direction. Number of rounds is returned. + * Key size MUST be 16, 24 or 32 bytes; otherwise, 0 is returned. + */ +unsigned br_aes_x86ni_keysched_enc(unsigned char *skni, + const void *key, size_t len); + +/* + * AES key schedule, using x86 AES-NI instructions. This yields the + * subkeys in the decryption direction. Number of rounds is returned. + * Key size MUST be 16, 24 or 32 bytes; otherwise, 0 is returned. + */ +unsigned br_aes_x86ni_keysched_dec(unsigned char *skni, + const void *key, size_t len); + +/* + * Test support for AES POWER8 opcodes. + */ +int br_aes_pwr8_supported(void); + +/* + * AES key schedule, using POWER8 instructions. This yields the + * subkeys in the encryption direction. Number of rounds is returned. + * Key size MUST be 16, 24 or 32 bytes; otherwise, 0 is returned. + */ +unsigned br_aes_pwr8_keysched(unsigned char *skni, + const void *key, size_t len); + +/* ==================================================================== */ +/* + * RSA. + */ + +/* + * Apply proper PKCS#1 v1.5 padding (for signatures). 'hash_oid' is + * the encoded hash function OID, or NULL. + */ +uint32_t br_rsa_pkcs1_sig_pad(const unsigned char *hash_oid, + const unsigned char *hash, size_t hash_len, + uint32_t n_bitlen, unsigned char *x); + +/* + * Check PKCS#1 v1.5 padding (for signatures). 'hash_oid' is the encoded + * hash function OID, or NULL. The provided 'sig' value is _after_ the + * modular exponentiation, i.e. it should be the padded hash. On + * success, the hashed message is extracted. + */ +uint32_t br_rsa_pkcs1_sig_unpad(const unsigned char *sig, size_t sig_len, + const unsigned char *hash_oid, size_t hash_len, + unsigned char *hash_out); + +/* + * Apply proper PSS padding. The 'x' buffer is output only: it + * receives the value that is to be exponentiated. + */ +uint32_t br_rsa_pss_sig_pad(const br_prng_class **rng, + const br_hash_class *hf_data, const br_hash_class *hf_mgf1, + const unsigned char *hash, size_t salt_len, + uint32_t n_bitlen, unsigned char *x); + +/* + * Check PSS padding. The provided value is the one _after_ + * the modular exponentiation; it is modified by this function. + * This function infers the signature length from the public key + * size, i.e. it assumes that this has already been verified (as + * part of the exponentiation). + */ +uint32_t br_rsa_pss_sig_unpad( + const br_hash_class *hf_data, const br_hash_class *hf_mgf1, + const unsigned char *hash, size_t salt_len, + const br_rsa_public_key *pk, unsigned char *x); + +/* + * Apply OAEP padding. Returned value is the actual padded string length, + * or zero on error. + */ +size_t br_rsa_oaep_pad(const br_prng_class **rnd, const br_hash_class *dig, + const void *label, size_t label_len, const br_rsa_public_key *pk, + void *dst, size_t dst_nax_len, const void *src, size_t src_len); + +/* + * Unravel and check OAEP padding. If the padding is correct, then 1 is + * returned, '*len' is adjusted to the length of the message, and the + * data is moved to the start of the 'data' buffer. If the padding is + * incorrect, then 0 is returned and '*len' is untouched. Either way, + * the complete buffer contents are altered. + */ +uint32_t br_rsa_oaep_unpad(const br_hash_class *dig, + const void *label, size_t label_len, void *data, size_t *len); + +/* + * Compute MGF1 for a given seed, and XOR the output into the provided + * buffer. + */ +void br_mgf1_xor(void *data, size_t len, + const br_hash_class *dig, const void *seed, size_t seed_len); + +/* + * Inner function for RSA key generation; used by the "i31" and "i62" + * implementations. + */ +uint32_t br_rsa_i31_keygen_inner(const br_prng_class **rng, + br_rsa_private_key *sk, void *kbuf_priv, + br_rsa_public_key *pk, void *kbuf_pub, + unsigned size, uint32_t pubexp, br_i31_modpow_opt_type mp31); + +/* ==================================================================== */ +/* + * Elliptic curves. + */ + +/* + * Type for generic EC parameters: curve order (unsigned big-endian + * encoding) and encoded conventional generator. + */ +typedef struct { + int curve; + const unsigned char *order; + size_t order_len; + const unsigned char *generator; + size_t generator_len; +} br_ec_curve_def; + +extern const br_ec_curve_def br_secp256r1; +extern const br_ec_curve_def br_secp384r1; +extern const br_ec_curve_def br_secp521r1; + +/* + * For Curve25519, the advertised "order" really is 2^255-1, since the + * point multipliction function really works over arbitrary 255-bit + * scalars. This value is only meant as a hint for ECDH key generation; + * only ECDSA uses the exact curve order, and ECDSA is not used with + * that specific curve. + */ +extern const br_ec_curve_def br_curve25519; + +/* + * Decode some bytes as an i31 integer, with truncation (corresponding + * to the 'bits2int' operation in RFC 6979). The target ENCODED bit + * length is provided as last parameter. The resulting value will have + * this declared bit length, and consists the big-endian unsigned decoding + * of exactly that many bits in the source (capped at the source length). + */ +void br_ecdsa_i31_bits2int(uint32_t *x, + const void *src, size_t len, uint32_t ebitlen); + +/* + * Decode some bytes as an i15 integer, with truncation (corresponding + * to the 'bits2int' operation in RFC 6979). The target ENCODED bit + * length is provided as last parameter. The resulting value will have + * this declared bit length, and consists the big-endian unsigned decoding + * of exactly that many bits in the source (capped at the source length). + */ +void br_ecdsa_i15_bits2int(uint16_t *x, + const void *src, size_t len, uint32_t ebitlen); + +/* ==================================================================== */ +/* + * ASN.1 support functions. + */ + +/* + * A br_asn1_uint structure contains encoding information about an + * INTEGER nonnegative value: pointer to the integer contents (unsigned + * big-endian representation), length of the integer contents, + * and length of the encoded value. The data shall have minimal length: + * - If the integer value is zero, then 'len' must be zero. + * - If the integer value is not zero, then data[0] must be non-zero. + * + * Under these conditions, 'asn1len' is necessarily equal to either len + * or len+1. + */ +typedef struct { + const unsigned char *data; + size_t len; + size_t asn1len; +} br_asn1_uint; + +/* + * Given an encoded integer (unsigned big-endian, with possible leading + * bytes of value 0), returned the "prepared INTEGER" structure. + */ +br_asn1_uint br_asn1_uint_prepare(const void *xdata, size_t xlen); + +/* + * Encode an ASN.1 length. The length of the encoded length is returned. + * If 'dest' is NULL, then no encoding is performed, but the length of + * the encoded length is still computed and returned. + */ +size_t br_asn1_encode_length(void *dest, size_t len); + +/* + * Convenient macro for computing lengths of lengths. + */ +#define len_of_len(len) br_asn1_encode_length(NULL, len) + +/* + * Encode a (prepared) ASN.1 INTEGER. The encoded length is returned. + * If 'dest' is NULL, then no encoding is performed, but the length of + * the encoded integer is still computed and returned. + */ +size_t br_asn1_encode_uint(void *dest, br_asn1_uint pp); + +/* + * Get the OID that identifies an elliptic curve. Returned value is + * the DER-encoded OID, with the length (always one byte) but without + * the tag. Thus, the first byte of the returned buffer contains the + * number of subsequent bytes in the value. If the curve is not + * recognised, NULL is returned. + */ +const unsigned char *br_get_curve_OID(int curve); + +/* + * Inner function for EC private key encoding. This is equivalent to + * the API function br_encode_ec_raw_der(), except for an extra + * parameter: if 'include_curve_oid' is zero, then the curve OID is + * _not_ included in the output blob (this is for PKCS#8 support). + */ +size_t br_encode_ec_raw_der_inner(void *dest, + const br_ec_private_key *sk, const br_ec_public_key *pk, + int include_curve_oid); + +/* ==================================================================== */ +/* + * SSL/TLS support functions. + */ + +/* + * Record types. + */ +#define BR_SSL_CHANGE_CIPHER_SPEC 20 +#define BR_SSL_ALERT 21 +#define BR_SSL_HANDSHAKE 22 +#define BR_SSL_APPLICATION_DATA 23 + +/* + * Handshake message types. + */ +#define BR_SSL_HELLO_REQUEST 0 +#define BR_SSL_CLIENT_HELLO 1 +#define BR_SSL_SERVER_HELLO 2 +#define BR_SSL_CERTIFICATE 11 +#define BR_SSL_SERVER_KEY_EXCHANGE 12 +#define BR_SSL_CERTIFICATE_REQUEST 13 +#define BR_SSL_SERVER_HELLO_DONE 14 +#define BR_SSL_CERTIFICATE_VERIFY 15 +#define BR_SSL_CLIENT_KEY_EXCHANGE 16 +#define BR_SSL_FINISHED 20 + +/* + * Alert levels. + */ +#define BR_LEVEL_WARNING 1 +#define BR_LEVEL_FATAL 2 + +/* + * Low-level I/O state. + */ +#define BR_IO_FAILED 0 +#define BR_IO_IN 1 +#define BR_IO_OUT 2 +#define BR_IO_INOUT 3 + +/* + * Mark a SSL engine as failed. The provided error code is recorded if + * the engine was not already marked as failed. If 'err' is 0, then the + * engine is marked as closed (without error). + */ +void br_ssl_engine_fail(br_ssl_engine_context *cc, int err); + +/* + * Test whether the engine is closed (normally or as a failure). + */ +static inline int +br_ssl_engine_closed(const br_ssl_engine_context *cc) +{ + return cc->iomode == BR_IO_FAILED; +} + +/* + * Configure a new maximum fragment length. If possible, the maximum + * length for outgoing records is immediately adjusted (if there are + * not already too many buffered bytes for that). + */ +void br_ssl_engine_new_max_frag_len( + br_ssl_engine_context *rc, unsigned max_frag_len); + +/* + * Test whether the current incoming record has been fully received + * or not. This functions returns 0 only if a complete record header + * has been received, but some of the (possibly encrypted) payload + * has not yet been obtained. + */ +int br_ssl_engine_recvrec_finished(const br_ssl_engine_context *rc); + +/* + * Flush the current record (if not empty). This is meant to be called + * from the handshake processor only. + */ +void br_ssl_engine_flush_record(br_ssl_engine_context *cc); + +/* + * Test whether there is some accumulated payload to send. + */ +static inline int +br_ssl_engine_has_pld_to_send(const br_ssl_engine_context *rc) +{ + return rc->oxa != rc->oxb && rc->oxa != rc->oxc; +} + +/* + * Initialize RNG in engine. Returned value is 1 on success, 0 on error. + * This function will try to use the OS-provided RNG, if available. If + * there is no OS-provided RNG, or if it failed, and no entropy was + * injected by the caller, then a failure will be reported. On error, + * the context error code is set. + */ +int br_ssl_engine_init_rand(br_ssl_engine_context *cc); + +/* + * Reset the handshake-related parts of the engine. + */ +void br_ssl_engine_hs_reset(br_ssl_engine_context *cc, + void (*hsinit)(void *), void (*hsrun)(void *)); + +/* + * Get the PRF to use for this context, for the provided PRF hash + * function ID. + */ +br_tls_prf_impl br_ssl_engine_get_PRF(br_ssl_engine_context *cc, int prf_id); + +/* + * Consume the provided pre-master secret and compute the corresponding + * master secret. The 'prf_id' is the ID of the hash function to use + * with the TLS 1.2 PRF (ignored if the version is TLS 1.0 or 1.1). + */ +void br_ssl_engine_compute_master(br_ssl_engine_context *cc, + int prf_id, const void *pms, size_t len); + +/* + * Switch to CBC decryption for incoming records. + * cc the engine context + * is_client non-zero for a client, zero for a server + * prf_id id of hash function for PRF (ignored if not TLS 1.2+) + * mac_id id of hash function for HMAC + * bc_impl block cipher implementation (CBC decryption) + * cipher_key_len block cipher key length (in bytes) + */ +void br_ssl_engine_switch_cbc_in(br_ssl_engine_context *cc, + int is_client, int prf_id, int mac_id, + const br_block_cbcdec_class *bc_impl, size_t cipher_key_len); + +/* + * Switch to CBC encryption for outgoing records. + * cc the engine context + * is_client non-zero for a client, zero for a server + * prf_id id of hash function for PRF (ignored if not TLS 1.2+) + * mac_id id of hash function for HMAC + * bc_impl block cipher implementation (CBC encryption) + * cipher_key_len block cipher key length (in bytes) + */ +void br_ssl_engine_switch_cbc_out(br_ssl_engine_context *cc, + int is_client, int prf_id, int mac_id, + const br_block_cbcenc_class *bc_impl, size_t cipher_key_len); + +/* + * Switch to GCM decryption for incoming records. + * cc the engine context + * is_client non-zero for a client, zero for a server + * prf_id id of hash function for PRF + * bc_impl block cipher implementation (CTR) + * cipher_key_len block cipher key length (in bytes) + */ +void br_ssl_engine_switch_gcm_in(br_ssl_engine_context *cc, + int is_client, int prf_id, + const br_block_ctr_class *bc_impl, size_t cipher_key_len); + +/* + * Switch to GCM encryption for outgoing records. + * cc the engine context + * is_client non-zero for a client, zero for a server + * prf_id id of hash function for PRF + * bc_impl block cipher implementation (CTR) + * cipher_key_len block cipher key length (in bytes) + */ +void br_ssl_engine_switch_gcm_out(br_ssl_engine_context *cc, + int is_client, int prf_id, + const br_block_ctr_class *bc_impl, size_t cipher_key_len); + +/* + * Switch to ChaCha20+Poly1305 decryption for incoming records. + * cc the engine context + * is_client non-zero for a client, zero for a server + * prf_id id of hash function for PRF + */ +void br_ssl_engine_switch_chapol_in(br_ssl_engine_context *cc, + int is_client, int prf_id); + +/* + * Switch to ChaCha20+Poly1305 encryption for outgoing records. + * cc the engine context + * is_client non-zero for a client, zero for a server + * prf_id id of hash function for PRF + */ +void br_ssl_engine_switch_chapol_out(br_ssl_engine_context *cc, + int is_client, int prf_id); + +/* + * Switch to CCM decryption for incoming records. + * cc the engine context + * is_client non-zero for a client, zero for a server + * prf_id id of hash function for PRF + * bc_impl block cipher implementation (CTR+CBC) + * cipher_key_len block cipher key length (in bytes) + * tag_len tag length (in bytes) + */ +void br_ssl_engine_switch_ccm_in(br_ssl_engine_context *cc, + int is_client, int prf_id, + const br_block_ctrcbc_class *bc_impl, + size_t cipher_key_len, size_t tag_len); + +/* + * Switch to GCM encryption for outgoing records. + * cc the engine context + * is_client non-zero for a client, zero for a server + * prf_id id of hash function for PRF + * bc_impl block cipher implementation (CTR+CBC) + * cipher_key_len block cipher key length (in bytes) + * tag_len tag length (in bytes) + */ +void br_ssl_engine_switch_ccm_out(br_ssl_engine_context *cc, + int is_client, int prf_id, + const br_block_ctrcbc_class *bc_impl, + size_t cipher_key_len, size_t tag_len); + +/* + * Calls to T0-generated code. + */ +void br_ssl_hs_client_init_main(void *ctx); +void br_ssl_hs_client_run(void *ctx); +void br_ssl_hs_server_init_main(void *ctx); +void br_ssl_hs_server_run(void *ctx); + +/* + * Get the hash function to use for signatures, given a bit mask of + * supported hash functions. This implements a strict choice order + * (namely SHA-256, SHA-384, SHA-512, SHA-224, SHA-1). If the mask + * does not document support of any of these hash functions, then this + * functions returns 0. + */ +int br_ssl_choose_hash(unsigned bf); + +/* ==================================================================== */ + +/* + * PowerPC / POWER assembly stuff. The special BR_POWER_ASM_MACROS macro + * must be defined before including this file; this is done by source + * files that use some inline assembly for PowerPC / POWER machines. + */ + +#if BR_POWER_ASM_MACROS + +#define lxvw4x(xt, ra, rb) lxvw4x_(xt, ra, rb) +#define stxvw4x(xt, ra, rb) stxvw4x_(xt, ra, rb) + +#define bdnz(foo) bdnz_(foo) +#define bdz(foo) bdz_(foo) +#define beq(foo) beq_(foo) + +#define li(rx, value) li_(rx, value) +#define addi(rx, ra, imm) addi_(rx, ra, imm) +#define cmpldi(rx, imm) cmpldi_(rx, imm) +#define mtctr(rx) mtctr_(rx) +#define vspltb(vrt, vrb, uim) vspltb_(vrt, vrb, uim) +#define vspltw(vrt, vrb, uim) vspltw_(vrt, vrb, uim) +#define vspltisb(vrt, imm) vspltisb_(vrt, imm) +#define vspltisw(vrt, imm) vspltisw_(vrt, imm) +#define vrlw(vrt, vra, vrb) vrlw_(vrt, vra, vrb) +#define vsbox(vrt, vra) vsbox_(vrt, vra) +#define vxor(vrt, vra, vrb) vxor_(vrt, vra, vrb) +#define vand(vrt, vra, vrb) vand_(vrt, vra, vrb) +#define vsro(vrt, vra, vrb) vsro_(vrt, vra, vrb) +#define vsl(vrt, vra, vrb) vsl_(vrt, vra, vrb) +#define vsldoi(vt, va, vb, sh) vsldoi_(vt, va, vb, sh) +#define vsr(vrt, vra, vrb) vsr_(vrt, vra, vrb) +#define vaddcuw(vrt, vra, vrb) vaddcuw_(vrt, vra, vrb) +#define vadduwm(vrt, vra, vrb) vadduwm_(vrt, vra, vrb) +#define vsububm(vrt, vra, vrb) vsububm_(vrt, vra, vrb) +#define vsubuwm(vrt, vra, vrb) vsubuwm_(vrt, vra, vrb) +#define vsrw(vrt, vra, vrb) vsrw_(vrt, vra, vrb) +#define vcipher(vt, va, vb) vcipher_(vt, va, vb) +#define vcipherlast(vt, va, vb) vcipherlast_(vt, va, vb) +#define vncipher(vt, va, vb) vncipher_(vt, va, vb) +#define vncipherlast(vt, va, vb) vncipherlast_(vt, va, vb) +#define vperm(vt, va, vb, vc) vperm_(vt, va, vb, vc) +#define vpmsumd(vt, va, vb) vpmsumd_(vt, va, vb) +#define xxpermdi(vt, va, vb, d) xxpermdi_(vt, va, vb, d) + +#define lxvw4x_(xt, ra, rb) "\tlxvw4x\t" #xt "," #ra "," #rb "\n" +#define stxvw4x_(xt, ra, rb) "\tstxvw4x\t" #xt "," #ra "," #rb "\n" + +#define label(foo) #foo "%=:\n" +#define bdnz_(foo) "\tbdnz\t" #foo "%=\n" +#define bdz_(foo) "\tbdz\t" #foo "%=\n" +#define beq_(foo) "\tbeq\t" #foo "%=\n" + +#define li_(rx, value) "\tli\t" #rx "," #value "\n" +#define addi_(rx, ra, imm) "\taddi\t" #rx "," #ra "," #imm "\n" +#define cmpldi_(rx, imm) "\tcmpldi\t" #rx "," #imm "\n" +#define mtctr_(rx) "\tmtctr\t" #rx "\n" +#define vspltb_(vrt, vrb, uim) "\tvspltb\t" #vrt "," #vrb "," #uim "\n" +#define vspltw_(vrt, vrb, uim) "\tvspltw\t" #vrt "," #vrb "," #uim "\n" +#define vspltisb_(vrt, imm) "\tvspltisb\t" #vrt "," #imm "\n" +#define vspltisw_(vrt, imm) "\tvspltisw\t" #vrt "," #imm "\n" +#define vrlw_(vrt, vra, vrb) "\tvrlw\t" #vrt "," #vra "," #vrb "\n" +#define vsbox_(vrt, vra) "\tvsbox\t" #vrt "," #vra "\n" +#define vxor_(vrt, vra, vrb) "\tvxor\t" #vrt "," #vra "," #vrb "\n" +#define vand_(vrt, vra, vrb) "\tvand\t" #vrt "," #vra "," #vrb "\n" +#define vsro_(vrt, vra, vrb) "\tvsro\t" #vrt "," #vra "," #vrb "\n" +#define vsl_(vrt, vra, vrb) "\tvsl\t" #vrt "," #vra "," #vrb "\n" +#define vsldoi_(vt, va, vb, sh) "\tvsldoi\t" #vt "," #va "," #vb "," #sh "\n" +#define vsr_(vrt, vra, vrb) "\tvsr\t" #vrt "," #vra "," #vrb "\n" +#define vaddcuw_(vrt, vra, vrb) "\tvaddcuw\t" #vrt "," #vra "," #vrb "\n" +#define vadduwm_(vrt, vra, vrb) "\tvadduwm\t" #vrt "," #vra "," #vrb "\n" +#define vsububm_(vrt, vra, vrb) "\tvsububm\t" #vrt "," #vra "," #vrb "\n" +#define vsubuwm_(vrt, vra, vrb) "\tvsubuwm\t" #vrt "," #vra "," #vrb "\n" +#define vsrw_(vrt, vra, vrb) "\tvsrw\t" #vrt "," #vra "," #vrb "\n" +#define vcipher_(vt, va, vb) "\tvcipher\t" #vt "," #va "," #vb "\n" +#define vcipherlast_(vt, va, vb) "\tvcipherlast\t" #vt "," #va "," #vb "\n" +#define vncipher_(vt, va, vb) "\tvncipher\t" #vt "," #va "," #vb "\n" +#define vncipherlast_(vt, va, vb) "\tvncipherlast\t" #vt "," #va "," #vb "\n" +#define vperm_(vt, va, vb, vc) "\tvperm\t" #vt "," #va "," #vb "," #vc "\n" +#define vpmsumd_(vt, va, vb) "\tvpmsumd\t" #vt "," #va "," #vb "\n" +#define xxpermdi_(vt, va, vb, d) "\txxpermdi\t" #vt "," #va "," #vb "," #d "\n" + +#endif + +/* ==================================================================== */ +/* + * Special "activate intrinsics" code, needed for some compiler versions. + * This is defined at the end of this file, so that it won't impact any + * of the inline functions defined previously; and it is controlled by + * a specific macro defined in the caller code. + * + * Calling code conventions: + * + * - Caller must define BR_ENABLE_INTRINSICS before including "t_inner.h". + * - Functions that use intrinsics must be enclosed in an "enabled" + * region (between BR_TARGETS_X86_UP and BR_TARGETS_X86_DOWN). + * - Functions that use intrinsics must be tagged with the appropriate + * BR_TARGET(). + */ + +#if BR_ENABLE_INTRINSICS && (BR_GCC_4_4 || BR_CLANG_3_7 || BR_MSC_2005) + +/* + * x86 intrinsics (both 32-bit and 64-bit). + */ +#if BR_i386 || BR_amd64 + +/* + * On GCC before version 5.0, we need to use the pragma to enable the + * target options globally, because the 'target' function attribute + * appears to be unreliable. Before 4.6 we must also avoid the + * push_options / pop_options mechanism, because it tends to trigger + * some internal compiler errors. + */ +#if BR_GCC && !BR_GCC_5_0 +#if BR_GCC_4_6 +#define BR_TARGETS_X86_UP \ + _Pragma("GCC push_options") \ + _Pragma("GCC target(\"sse2,ssse3,sse4.1,aes,pclmul,rdrnd\")") +#define BR_TARGETS_X86_DOWN \ + _Pragma("GCC pop_options") +#else +#define BR_TARGETS_X86_UP \ + _Pragma("GCC target(\"sse2,ssse3,sse4.1,aes,pclmul\")") +#define BR_TARGETS_X86_DOWN +#endif +#pragma GCC diagnostic ignored "-Wpsabi" +#endif + +#if BR_CLANG && !BR_CLANG_3_8 +#undef __SSE2__ +#undef __SSE3__ +#undef __SSSE3__ +#undef __SSE4_1__ +#undef __AES__ +#undef __PCLMUL__ +#undef __RDRND__ +#define __SSE2__ 1 +#define __SSE3__ 1 +#define __SSSE3__ 1 +#define __SSE4_1__ 1 +#define __AES__ 1 +#define __PCLMUL__ 1 +#define __RDRND__ 1 +#endif + +#ifndef BR_TARGETS_X86_UP +#define BR_TARGETS_X86_UP +#endif +#ifndef BR_TARGETS_X86_DOWN +#define BR_TARGETS_X86_DOWN +#endif + +#if BR_GCC || BR_CLANG +BR_TARGETS_X86_UP +#include +#include +#define br_bswap32 __builtin_bswap32 +BR_TARGETS_X86_DOWN +#endif + +#if BR_MSC +#include +#include +#include +#define br_bswap32 _byteswap_ulong +#endif + +static inline int +br_cpuid(uint32_t mask_eax, uint32_t mask_ebx, + uint32_t mask_ecx, uint32_t mask_edx) +{ +#if BR_GCC || BR_CLANG + unsigned eax, ebx, ecx, edx; + + if (__get_cpuid(1, &eax, &ebx, &ecx, &edx)) { + if ((eax & mask_eax) == mask_eax + && (ebx & mask_ebx) == mask_ebx + && (ecx & mask_ecx) == mask_ecx + && (edx & mask_edx) == mask_edx) + { + return 1; + } + } +#elif BR_MSC + int info[4]; + + __cpuid(info, 1); + if (((uint32_t)info[0] & mask_eax) == mask_eax + && ((uint32_t)info[1] & mask_ebx) == mask_ebx + && ((uint32_t)info[2] & mask_ecx) == mask_ecx + && ((uint32_t)info[3] & mask_edx) == mask_edx) + { + return 1; + } +#endif + return 0; +} + +#endif + +#endif + +#ifdef ESP8266 + + #ifdef __cplusplus + extern "C" { + #endif + + #define _debugBearSSL (0) + extern void optimistic_yield(uint32_t); + #ifdef __cplusplus + } + #endif + +#else + #define optimistic_yield(ignored) +#endif + + +/* ==================================================================== */ + +#endif diff --git a/lib/bearssl-esp8266/src/x509/asn1enc.c b/lib/bearssl-esp8266/src/x509/asn1enc.c new file mode 100644 index 000000000..221bf9c1e --- /dev/null +++ b/lib/bearssl-esp8266/src/x509/asn1enc.c @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +br_asn1_uint +br_asn1_uint_prepare(const void *xdata, size_t xlen) +{ + const unsigned char *x; + br_asn1_uint t; + + x = xdata; + while (xlen > 0 && *x == 0) { + x ++; + xlen --; + } + t.data = x; + t.len = xlen; + t.asn1len = xlen; + if (xlen == 0 || x[0] >= 0x80) { + t.asn1len ++; + } + return t; +} + +/* see inner.h */ +size_t +br_asn1_encode_length(void *dest, size_t len) +{ + unsigned char *buf; + size_t z; + int i, j; + + buf = dest; + if (len < 0x80) { + if (buf != NULL) { + *buf = len; + } + return 1; + } + i = 0; + for (z = len; z != 0; z >>= 8) { + i ++; + } + if (buf != NULL) { + *buf ++ = 0x80 + i; + for (j = i - 1; j >= 0; j --) { + *buf ++ = len >> (j << 3); + } + } + return i + 1; +} + +/* see inner.h */ +size_t +br_asn1_encode_uint(void *dest, br_asn1_uint pp) +{ + unsigned char *buf; + size_t lenlen; + + if (dest == NULL) { + return 1 + br_asn1_encode_length(NULL, pp.asn1len) + pp.asn1len; + } + buf = dest; + *buf ++ = 0x02; + lenlen = br_asn1_encode_length(buf, pp.asn1len); + buf += lenlen; + *buf = 0x00; + memcpy(buf + pp.asn1len - pp.len, pp.data, pp.len); + return 1 + lenlen + pp.asn1len; +} diff --git a/lib/bearssl-esp8266/src/x509/encode_ec_pk8der.c b/lib/bearssl-esp8266/src/x509/encode_ec_pk8der.c new file mode 100644 index 000000000..817a6d31b --- /dev/null +++ b/lib/bearssl-esp8266/src/x509/encode_ec_pk8der.c @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_x509.h */ +size_t +br_encode_ec_pkcs8_der(void *dest, + const br_ec_private_key *sk, const br_ec_public_key *pk) +{ + /* + * ASN.1 format: + * + * OneAsymmetricKey ::= SEQUENCE { + * version Version, + * privateKeyAlgorithm PrivateKeyAlgorithmIdentifier, + * privateKey PrivateKey, + * attributes [0] Attributes OPTIONAL, + * ..., + * [[2: publicKey [1] PublicKey OPTIONAL ]], + * ... + * } + * + * We don't include attributes or public key (the public key + * is included in the private key value instead). The + * 'version' field is an INTEGER that we will set to 0 + * (meaning 'v1', compatible with previous versions of PKCS#8). + * The 'privateKeyAlgorithm' structure is an AlgorithmIdentifier + * whose OID should be id-ecPublicKey, with, as parameters, the + * curve OID. The 'privateKey' is an OCTET STRING, whose value + * is the "raw DER" encoding of the key pair. + */ + + /* + * OID id-ecPublicKey (1.2.840.10045.2.1), DER-encoded (with + * the tag). + */ + static const unsigned char OID_ECPUBKEY[] = { + 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01 + }; + + size_t len_version, len_privateKeyAlgorithm, len_privateKeyValue; + size_t len_privateKey, len_seq; + const unsigned char *oid; + + oid = br_get_curve_OID(sk->curve); + if (oid == NULL) { + return 0; + } + len_version = 3; + len_privateKeyAlgorithm = 2 + sizeof OID_ECPUBKEY + 2 + oid[0]; + len_privateKeyValue = br_encode_ec_raw_der_inner(NULL, sk, pk, 0); + len_privateKey = 1 + len_of_len(len_privateKeyValue) + + len_privateKeyValue; + len_seq = len_version + len_privateKeyAlgorithm + len_privateKey; + + if (dest == NULL) { + return 1 + len_of_len(len_seq) + len_seq; + } else { + unsigned char *buf; + size_t lenlen; + + buf = dest; + *buf ++ = 0x30; /* SEQUENCE tag */ + lenlen = br_asn1_encode_length(buf, len_seq); + buf += lenlen; + + /* version */ + *buf ++ = 0x02; + *buf ++ = 0x01; + *buf ++ = 0x00; + + /* privateKeyAlgorithm */ + *buf ++ = 0x30; + *buf ++ = (sizeof OID_ECPUBKEY) + 2 + oid[0]; + memcpy(buf, OID_ECPUBKEY, sizeof OID_ECPUBKEY); + buf += sizeof OID_ECPUBKEY; + *buf ++ = 0x06; + memcpy(buf, oid, 1 + oid[0]); + buf += 1 + oid[0]; + + /* privateKey */ + *buf ++ = 0x04; + buf += br_asn1_encode_length(buf, len_privateKeyValue); + br_encode_ec_raw_der_inner(buf, sk, pk, 0); + + return 1 + lenlen + len_seq; + } +} diff --git a/lib/bearssl-esp8266/src/x509/encode_ec_rawder.c b/lib/bearssl-esp8266/src/x509/encode_ec_rawder.c new file mode 100644 index 000000000..b84fd951c --- /dev/null +++ b/lib/bearssl-esp8266/src/x509/encode_ec_rawder.c @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +const unsigned char * +br_get_curve_OID(int curve) +{ + static const unsigned char OID_secp256r1[] = { + 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07 + }; + static const unsigned char OID_secp384r1[] = { + 0x05, 0x2b, 0x81, 0x04, 0x00, 0x22 + }; + static const unsigned char OID_secp521r1[] = { + 0x05, 0x2b, 0x81, 0x04, 0x00, 0x23 + }; + + switch (curve) { + case BR_EC_secp256r1: return OID_secp256r1; + case BR_EC_secp384r1: return OID_secp384r1; + case BR_EC_secp521r1: return OID_secp521r1; + default: + return NULL; + } +} + +/* see inner.h */ +size_t +br_encode_ec_raw_der_inner(void *dest, + const br_ec_private_key *sk, const br_ec_public_key *pk, + int include_curve_oid) +{ + /* + * ASN.1 format: + * + * ECPrivateKey ::= SEQUENCE { + * version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1), + * privateKey OCTET STRING, + * parameters [0] ECParameters {{ NamedCurve }} OPTIONAL, + * publicKey [1] BIT STRING OPTIONAL + * } + * + * The tages '[0]' and '[1]' are explicit. The 'ECParameters' + * is a CHOICE; in our case, it will always be an OBJECT IDENTIFIER + * that identifies the curve. + * + * The value of the 'privateKey' field is the raw unsigned big-endian + * encoding of the private key (integer modulo the curve subgroup + * order); there is no INTEGER tag, and the leading bit may be 1. + * Also, leading bytes of value 0x00 are _not_ removed. + * + * The 'publicKey' contents are the raw encoded public key point, + * normally uncompressed (leading byte of value 0x04, followed + * by the unsigned big-endian encodings of the X and Y coordinates, + * padded to the full field length if necessary). + */ + + size_t len_version, len_privateKey, len_parameters, len_publicKey; + size_t len_publicKey_bits, len_seq; + const unsigned char *oid; + + if (include_curve_oid) { + oid = br_get_curve_OID(sk->curve); + if (oid == NULL) { + return 0; + } + } else { + oid = NULL; + } + len_version = 3; + len_privateKey = 1 + len_of_len(sk->xlen) + sk->xlen; + if (include_curve_oid) { + len_parameters = 4 + oid[0]; + } else { + len_parameters = 0; + } + if (pk == NULL) { + len_publicKey = 0; + len_publicKey_bits = 0; + } else { + len_publicKey_bits = 2 + len_of_len(pk->qlen) + pk->qlen; + len_publicKey = 1 + len_of_len(len_publicKey_bits) + + len_publicKey_bits; + } + len_seq = len_version + len_privateKey + len_parameters + len_publicKey; + if (dest == NULL) { + return 1 + len_of_len(len_seq) + len_seq; + } else { + unsigned char *buf; + size_t lenlen; + + buf = dest; + *buf ++ = 0x30; /* SEQUENCE tag */ + lenlen = br_asn1_encode_length(buf, len_seq); + buf += lenlen; + + /* version */ + *buf ++ = 0x02; + *buf ++ = 0x01; + *buf ++ = 0x01; + + /* privateKey */ + *buf ++ = 0x04; + buf += br_asn1_encode_length(buf, sk->xlen); + memcpy(buf, sk->x, sk->xlen); + buf += sk->xlen; + + /* parameters */ + if (include_curve_oid) { + *buf ++ = 0xA0; + *buf ++ = oid[0] + 2; + *buf ++ = 0x06; + memcpy(buf, oid, oid[0] + 1); + buf += oid[0] + 1; + } + + /* publicKey */ + if (pk != NULL) { + *buf ++ = 0xA1; + buf += br_asn1_encode_length(buf, len_publicKey_bits); + *buf ++ = 0x03; + buf += br_asn1_encode_length(buf, pk->qlen + 1); + *buf ++ = 0x00; + memcpy(buf, pk->q, pk->qlen); + /* buf += pk->qlen; */ + } + + return 1 + lenlen + len_seq; + } +} + +/* see bearssl_x509.h */ +size_t +br_encode_ec_raw_der(void *dest, + const br_ec_private_key *sk, const br_ec_public_key *pk) +{ + return br_encode_ec_raw_der_inner(dest, sk, pk, 1); +} diff --git a/lib/bearssl-esp8266/src/x509/encode_rsa_pk8der.c b/lib/bearssl-esp8266/src/x509/encode_rsa_pk8der.c new file mode 100644 index 000000000..da3e0223d --- /dev/null +++ b/lib/bearssl-esp8266/src/x509/encode_rsa_pk8der.c @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_x509.h */ +size_t +br_encode_rsa_pkcs8_der(void *dest, const br_rsa_private_key *sk, + const br_rsa_public_key *pk, const void *d, size_t dlen) +{ + /* + * ASN.1 format: + * + * OneAsymmetricKey ::= SEQUENCE { + * version Version, + * privateKeyAlgorithm PrivateKeyAlgorithmIdentifier, + * privateKey PrivateKey, + * attributes [0] Attributes OPTIONAL, + * ..., + * [[2: publicKey [1] PublicKey OPTIONAL ]], + * ... + * } + * + * We don't include attributes or public key. The 'version' field + * is an INTEGER that we will set to 0 (meaning 'v1', compatible + * with previous versions of PKCS#8). The 'privateKeyAlgorithm' + * structure is an AlgorithmIdentifier whose OID should be + * rsaEncryption, with NULL parameters. The 'privateKey' is an + * OCTET STRING, whose value is the "raw DER" encoding of the + * key pair. + * + * Since the private key value comes last, this function really + * adds a header, which is mostly fixed (only some lengths have + * to be modified. + */ + + /* + * Concatenation of: + * - DER encoding of an INTEGER of value 0 (the 'version' field) + * - DER encoding of a PrivateKeyAlgorithmIdentifier that uses + * the rsaEncryption OID, and NULL parameters + * - An OCTET STRING tag + */ + static const unsigned char PK8_HEAD[] = { + 0x02, 0x01, 0x00, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, + 0x04 + }; + + size_t len_raw, len_seq; + + len_raw = br_encode_rsa_raw_der(NULL, sk, pk, d, dlen); + len_seq = (sizeof PK8_HEAD) + len_of_len(len_raw) + len_raw; + if (dest == NULL) { + return 1 + len_of_len(len_seq) + len_seq; + } else { + unsigned char *buf; + size_t lenlen; + + buf = dest; + *buf ++ = 0x30; /* SEQUENCE tag */ + lenlen = br_asn1_encode_length(buf, len_seq); + buf += lenlen; + + /* version, privateKeyAlgorithm, privateKey tag */ + memcpy(buf, PK8_HEAD, sizeof PK8_HEAD); + buf += sizeof PK8_HEAD; + + /* privateKey */ + buf += br_asn1_encode_length(buf, len_raw); + br_encode_rsa_raw_der(buf, sk, pk, d, dlen); + + return 1 + lenlen + len_seq; + } +} diff --git a/lib/bearssl-esp8266/src/x509/encode_rsa_rawder.c b/lib/bearssl-esp8266/src/x509/encode_rsa_rawder.c new file mode 100644 index 000000000..31116d1f8 --- /dev/null +++ b/lib/bearssl-esp8266/src/x509/encode_rsa_rawder.c @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_x509.h */ +size_t +br_encode_rsa_raw_der(void *dest, const br_rsa_private_key *sk, + const br_rsa_public_key *pk, const void *d, size_t dlen) +{ + /* + * ASN.1 format: + * + * RSAPrivateKey ::= SEQUENCE { + * version Version, + * modulus INTEGER, -- n + * publicExponent INTEGER, -- e + * privateExponent INTEGER, -- d + * prime1 INTEGER, -- p + * prime2 INTEGER, -- q + * exponent1 INTEGER, -- d mod (p-1) + * exponent2 INTEGER, -- d mod (q-1) + * coefficient INTEGER, -- (inverse of q) mod p + * otherPrimeInfos OtherPrimeInfos OPTIONAL + * } + * + * The 'version' field is an INTEGER of value 0 (meaning: there + * are exactly two prime factors), and 'otherPrimeInfos' will + * be absent (because there are exactly two prime factors). + */ + + br_asn1_uint num[9]; + size_t u, slen; + + /* + * For all INTEGER values, get the pointer and length for the + * data bytes. + */ + num[0] = br_asn1_uint_prepare(NULL, 0); + num[1] = br_asn1_uint_prepare(pk->n, pk->nlen); + num[2] = br_asn1_uint_prepare(pk->e, pk->elen); + num[3] = br_asn1_uint_prepare(d, dlen); + num[4] = br_asn1_uint_prepare(sk->p, sk->plen); + num[5] = br_asn1_uint_prepare(sk->q, sk->qlen); + num[6] = br_asn1_uint_prepare(sk->dp, sk->dplen); + num[7] = br_asn1_uint_prepare(sk->dq, sk->dqlen); + num[8] = br_asn1_uint_prepare(sk->iq, sk->iqlen); + + /* + * Get the length of the SEQUENCE contents. + */ + slen = 0; + for (u = 0; u < 9; u ++) { + uint32_t ilen; + + ilen = num[u].asn1len; + slen += 1 + len_of_len(ilen) + ilen; + } + + if (dest == NULL) { + return 1 + len_of_len(slen) + slen; + } else { + unsigned char *buf; + size_t lenlen; + + buf = dest; + *buf ++ = 0x30; /* SEQUENCE tag */ + lenlen = br_asn1_encode_length(buf, slen); + buf += lenlen; + for (u = 0; u < 9; u ++) { + buf += br_asn1_encode_uint(buf, num[u]); + } + return 1 + lenlen + slen; + } +} diff --git a/lib/bearssl-esp8266/src/x509/pkey_decoder.c b/lib/bearssl-esp8266/src/x509/pkey_decoder.c new file mode 100644 index 000000000..84fa057aa --- /dev/null +++ b/lib/bearssl-esp8266/src/x509/pkey_decoder.c @@ -0,0 +1,587 @@ +/* Automatically generated code; do not modify directly. */ + +#include +#include +#include + +typedef struct { + uint32_t *dp; + uint32_t *rp; + const unsigned char *ip; +} t0_context; + +static uint32_t +t0_parse7E_unsigned(const unsigned char **p) +{ + uint32_t x; + + x = 0; + for (;;) { + unsigned y; + + y = pgm_read_byte((*p)++); + x = (x << 7) | (uint32_t)(y & 0x7F); + if (y < 0x80) { + return x; + } + } +} + +static int32_t +t0_parse7E_signed(const unsigned char **p) +{ + int neg; + uint32_t x; + + neg = (pgm_read_byte(*p) >> 6) & 1; + x = (uint32_t)-neg; + for (;;) { + unsigned y; + + y = pgm_read_byte((*p)++); + x = (x << 7) | (uint32_t)(y & 0x7F); + if (y < 0x80) { + if (neg) { + return -(int32_t)~x - 1; + } else { + return (int32_t)x; + } + } + } +} + +#define T0_VBYTE(x, n) (unsigned char)((((uint32_t)(x) >> (n)) & 0x7F) | 0x80) +#define T0_FBYTE(x, n) (unsigned char)(((uint32_t)(x) >> (n)) & 0x7F) +#define T0_SBYTE(x) (unsigned char)((((uint32_t)(x) >> 28) + 0xF8) ^ 0xF8) +#define T0_INT1(x) T0_FBYTE(x, 0) +#define T0_INT2(x) T0_VBYTE(x, 7), T0_FBYTE(x, 0) +#define T0_INT3(x) T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0) +#define T0_INT4(x) T0_VBYTE(x, 21), T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0) +#define T0_INT5(x) T0_SBYTE(x), T0_VBYTE(x, 21), T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0) + +/* static const unsigned char t0_datablock[]; */ + + +void br_pkey_decoder_init_main(void *t0ctx); + +void br_pkey_decoder_run(void *t0ctx); + + + +#include "t_inner.h" + + + + + +#include "t_inner.h" + +#define CTX ((br_pkey_decoder_context *)(void *)((unsigned char *)t0ctx - offsetof(br_pkey_decoder_context, cpu))) +#define CONTEXT_NAME br_pkey_decoder_context + +/* see bearssl_x509.h */ +void +br_pkey_decoder_init(br_pkey_decoder_context *ctx) +{ + memset(ctx, 0, sizeof *ctx); + ctx->cpu.dp = &ctx->dp_stack[0]; + ctx->cpu.rp = &ctx->rp_stack[0]; + br_pkey_decoder_init_main(&ctx->cpu); + br_pkey_decoder_run(&ctx->cpu); +} + +/* see bearssl_x509.h */ +void +br_pkey_decoder_push(br_pkey_decoder_context *ctx, + const void *data, size_t len) +{ + ctx->hbuf = data; + ctx->hlen = len; + br_pkey_decoder_run(&ctx->cpu); +} + + + +static const unsigned char t0_datablock[] PROGMEM = { + + 0x00, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x07, + 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, 0x08, 0x2A, 0x86, 0x48, 0xCE, + 0x3D, 0x03, 0x01, 0x07, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x22, 0x05, 0x2B, + 0x81, 0x04, 0x00, 0x23 +}; + +static const unsigned char t0_codeblock[] PROGMEM = { + + 0x00, 0x01, 0x01, 0x07, 0x00, 0x00, 0x01, 0x01, 0x08, 0x00, 0x00, 0x12, + 0x12, 0x00, 0x00, 0x01, T0_INT1(BR_ERR_X509_BAD_TAG_CLASS), 0x00, 0x00, + 0x01, T0_INT1(BR_ERR_X509_BAD_TAG_VALUE), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_EXTRA_ELEMENT), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_INDEFINITE_LENGTH), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_INNER_TRUNC), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_LIMIT_EXCEEDED), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_NOT_CONSTRUCTED), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_NOT_PRIMITIVE), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_OVERFLOW), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_UNEXPECTED), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_UNSUPPORTED), 0x00, 0x00, 0x01, + T0_INT1(BR_KEYTYPE_EC), 0x00, 0x00, 0x01, T0_INT1(BR_KEYTYPE_RSA), + 0x00, 0x00, 0x01, T0_INT2(offsetof(CONTEXT_NAME, key_data)), 0x00, + 0x00, 0x01, T0_INT2(offsetof(CONTEXT_NAME, key_type)), 0x00, 0x00, + 0x2F, 0x43, 0x00, 0x00, 0x01, T0_INT2(offsetof(CONTEXT_NAME, pad)), + 0x00, 0x00, 0x01, 0x13, 0x00, 0x00, 0x01, 0x1C, 0x00, 0x00, 0x01, 0x22, + 0x00, 0x00, 0x05, 0x02, 0x28, 0x15, 0x00, 0x00, 0x06, 0x02, 0x29, 0x15, + 0x00, 0x00, 0x01, 0x10, 0x39, 0x00, 0x00, 0x0C, 0x05, 0x02, 0x2B, 0x15, + 0x36, 0x00, 0x00, 0x0C, 0x05, 0x02, 0x2B, 0x15, 0x37, 0x00, 0x00, 0x06, + 0x02, 0x24, 0x15, 0x00, 0x01, 0x03, 0x00, 0x51, 0x06, 0x02, 0x2C, 0x15, + 0x50, 0x01, 0x04, 0x3A, 0x02, 0x00, 0x3D, 0x00, 0x02, 0x03, 0x00, 0x13, + 0x03, 0x01, 0x02, 0x01, 0x43, 0x0D, 0x06, 0x02, 0x2C, 0x15, 0x02, 0x01, + 0x2F, 0x47, 0x12, 0x01, 0x00, 0x02, 0x00, 0x05, 0x02, 0x2C, 0x15, 0x02, + 0x00, 0x02, 0x01, 0x1B, 0x00, 0x02, 0x4D, 0x46, 0x05, 0x02, 0x2C, 0x15, + 0x53, 0x14, 0x06, 0x07, 0x55, 0x01, 0x7F, 0x03, 0x01, 0x04, 0x16, 0x42, + 0x14, 0x06, 0x10, 0x01, 0x00, 0x03, 0x01, 0x13, 0x06, 0x03, 0x48, 0x04, + 0x02, 0x01, 0x00, 0x03, 0x00, 0x04, 0x02, 0x2C, 0x15, 0x3B, 0x50, 0x01, + 0x03, 0x3A, 0x4D, 0x02, 0x01, 0x06, 0x03, 0x3F, 0x04, 0x03, 0x02, 0x00, + 0x3C, 0x3B, 0x55, 0x02, 0x01, 0x06, 0x03, 0x2E, 0x04, 0x01, 0x2D, 0x00, + 0x00, 0x51, 0x06, 0x02, 0x2C, 0x15, 0x4E, 0x40, 0x3B, 0x00, 0x03, 0x31, + 0x49, 0x13, 0x13, 0x03, 0x00, 0x03, 0x01, 0x4B, 0x03, 0x02, 0x02, 0x00, + 0x02, 0x02, 0x1C, 0x00, 0x00, 0x17, 0x17, 0x00, 0x00, 0x01, 0x0B, 0x00, + 0x00, 0x01, T0_INT2(3 * BR_X509_BUFSIZE_KEY), 0x00, 0x01, 0x01, 0x87, + 0xFF, 0xFF, 0x7F, 0x4E, 0x50, 0x01, 0x02, 0x17, 0x0C, 0x06, 0x06, 0x12, + 0x37, 0x40, 0x2E, 0x04, 0x1C, 0x01, 0x04, 0x17, 0x0C, 0x06, 0x08, 0x12, + 0x37, 0x01, 0x00, 0x3D, 0x2D, 0x04, 0x0E, 0x01, 0x10, 0x17, 0x0C, 0x06, + 0x05, 0x12, 0x36, 0x3E, 0x04, 0x03, 0x2C, 0x15, 0x12, 0x03, 0x00, 0x3B, + 0x02, 0x00, 0x30, 0x1D, 0x52, 0x24, 0x15, 0x00, 0x01, 0x41, 0x0A, 0x06, + 0x02, 0x26, 0x15, 0x13, 0x03, 0x00, 0x08, 0x02, 0x00, 0x00, 0x00, 0x50, + 0x01, 0x06, 0x3A, 0x4F, 0x00, 0x00, 0x1E, 0x13, 0x06, 0x07, 0x18, 0x13, + 0x06, 0x01, 0x11, 0x04, 0x76, 0x21, 0x00, 0x00, 0x46, 0x05, 0x02, 0x2C, + 0x15, 0x33, 0x14, 0x06, 0x04, 0x01, 0x17, 0x04, 0x12, 0x34, 0x14, 0x06, + 0x04, 0x01, 0x18, 0x04, 0x0A, 0x35, 0x14, 0x06, 0x04, 0x01, 0x19, 0x04, + 0x02, 0x2C, 0x15, 0x00, 0x00, 0x1A, 0x50, 0x01, 0x02, 0x3A, 0x09, 0x4A, + 0x00, 0x03, 0x13, 0x03, 0x00, 0x03, 0x01, 0x03, 0x02, 0x4D, 0x51, 0x13, + 0x01, 0x81, 0x00, 0x0E, 0x06, 0x02, 0x2A, 0x15, 0x13, 0x01, 0x00, 0x0C, + 0x06, 0x0B, 0x12, 0x13, 0x05, 0x04, 0x12, 0x01, 0x00, 0x00, 0x51, 0x04, + 0x6F, 0x02, 0x01, 0x13, 0x05, 0x02, 0x27, 0x15, 0x20, 0x03, 0x01, 0x02, + 0x02, 0x1D, 0x02, 0x02, 0x1F, 0x03, 0x02, 0x13, 0x06, 0x03, 0x51, 0x04, + 0x68, 0x12, 0x02, 0x00, 0x02, 0x01, 0x08, 0x00, 0x00, 0x13, 0x31, 0x1A, + 0x08, 0x1E, 0x1A, 0x07, 0x1E, 0x49, 0x00, 0x01, 0x51, 0x13, 0x01, 0x81, + 0x00, 0x0A, 0x06, 0x01, 0x00, 0x01, 0x81, 0x00, 0x08, 0x13, 0x05, 0x02, + 0x25, 0x15, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x0D, 0x06, + 0x19, 0x02, 0x00, 0x20, 0x03, 0x00, 0x13, 0x01, 0x83, 0xFF, 0xFF, 0x7F, + 0x0D, 0x06, 0x02, 0x26, 0x15, 0x01, 0x08, 0x0B, 0x1E, 0x51, 0x1A, 0x07, + 0x04, 0x60, 0x00, 0x00, 0x4C, 0x45, 0x00, 0x00, 0x50, 0x38, 0x4D, 0x00, + 0x00, 0x4D, 0x13, 0x01, 0x81, 0x7F, 0x0D, 0x06, 0x08, 0x54, 0x01, 0x00, + 0x32, 0x1D, 0x01, 0x00, 0x00, 0x13, 0x32, 0x1D, 0x32, 0x1F, 0x47, 0x01, + 0x7F, 0x00, 0x01, 0x51, 0x03, 0x00, 0x02, 0x00, 0x01, 0x05, 0x0F, 0x01, + 0x01, 0x10, 0x16, 0x02, 0x00, 0x01, 0x06, 0x0F, 0x13, 0x01, 0x01, 0x10, + 0x06, 0x02, 0x22, 0x15, 0x01, 0x04, 0x0B, 0x02, 0x00, 0x01, 0x1F, 0x10, + 0x13, 0x01, 0x1F, 0x0C, 0x06, 0x02, 0x23, 0x15, 0x07, 0x00, 0x00, 0x13, + 0x05, 0x02, 0x26, 0x15, 0x20, 0x52, 0x00, 0x00, 0x19, 0x13, 0x01, 0x00, + 0x0E, 0x06, 0x01, 0x00, 0x12, 0x11, 0x04, 0x74, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x55, 0x12, 0x00, 0x00, 0x13, 0x06, 0x07, 0x56, 0x13, 0x06, 0x01, + 0x11, 0x04, 0x76, 0x00, 0x00, 0x01, 0x00, 0x17, 0x18, 0x09, 0x21, 0x00 +}; + +static const uint16_t t0_caddr[] PROGMEM = { + + 0, + 5, + 10, + 14, + 18, + 22, + 26, + 30, + 34, + 38, + 42, + 46, + 50, + 54, + 58, + 62, + 66, + 71, + 76, + 80, + 85, + 89, + 93, + 97, + 103, + 109, + 114, + 122, + 130, + 136, + 152, + 185, + 252, + 262, + 280, + 284, + 288, + 293, + 352, + 366, + 373, + 387, + 420, + 429, + 496, + 507, + 563, + 567, + 572, + 598, + 642, + 651, + 664, + 668, + 672, + 684 +}; + +#define T0_INTERPRETED 31 + +#define T0_ENTER(ip, rp, slot) do { \ + const unsigned char *t0_newip; \ + uint32_t t0_lnum; \ + t0_newip = &t0_codeblock[pgm_read_word(&t0_caddr[(slot) - T0_INTERPRETED])]; \ + t0_lnum = t0_parse7E_unsigned(&t0_newip); \ + (rp) += t0_lnum; \ + *((rp) ++) = (uint32_t)((ip) - &t0_codeblock[0]) + (t0_lnum << 16); \ + (ip) = t0_newip; \ + } while (0) + +#define T0_DEFENTRY(name, slot) \ +void \ +name(void *ctx) \ +{ \ + t0_context *t0ctx = ctx; \ + t0ctx->ip = &t0_codeblock[0]; \ + T0_ENTER(t0ctx->ip, t0ctx->rp, slot); \ +} + +T0_DEFENTRY(br_pkey_decoder_init_main, 68) + +#define T0_NEXT(t0ipp) (pgm_read_byte((*t0ipp)++)) + +void +br_pkey_decoder_run(void *t0ctx) +{ + uint32_t *dp, *rp; + const unsigned char *ip; + +#define T0_LOCAL(x) (*(rp - 2 - (x))) +#define T0_POP() (*-- dp) +#define T0_POPi() (*(int32_t *)(-- dp)) +#define T0_PEEK(x) (*(dp - 1 - (x))) +#define T0_PEEKi(x) (*(int32_t *)(dp - 1 - (x))) +#define T0_PUSH(v) do { *dp = (v); dp ++; } while (0) +#define T0_PUSHi(v) do { *(int32_t *)dp = (v); dp ++; } while (0) +#define T0_RPOP() (*-- rp) +#define T0_RPOPi() (*(int32_t *)(-- rp)) +#define T0_RPUSH(v) do { *rp = (v); rp ++; } while (0) +#define T0_RPUSHi(v) do { *(int32_t *)rp = (v); rp ++; } while (0) +#define T0_ROLL(x) do { \ + size_t t0len = (size_t)(x); \ + uint32_t t0tmp = *(dp - 1 - t0len); \ + memmove(dp - t0len - 1, dp - t0len, t0len * sizeof *dp); \ + *(dp - 1) = t0tmp; \ +} while (0) +#define T0_SWAP() do { \ + uint32_t t0tmp = *(dp - 2); \ + *(dp - 2) = *(dp - 1); \ + *(dp - 1) = t0tmp; \ +} while (0) +#define T0_ROT() do { \ + uint32_t t0tmp = *(dp - 3); \ + *(dp - 3) = *(dp - 2); \ + *(dp - 2) = *(dp - 1); \ + *(dp - 1) = t0tmp; \ +} while (0) +#define T0_NROT() do { \ + uint32_t t0tmp = *(dp - 1); \ + *(dp - 1) = *(dp - 2); \ + *(dp - 2) = *(dp - 3); \ + *(dp - 3) = t0tmp; \ +} while (0) +#define T0_PICK(x) do { \ + uint32_t t0depth = (x); \ + T0_PUSH(T0_PEEK(t0depth)); \ +} while (0) +#define T0_CO() do { \ + goto t0_exit; \ +} while (0) +#define T0_RET() goto t0_next + + dp = ((t0_context *)t0ctx)->dp; + rp = ((t0_context *)t0ctx)->rp; + ip = ((t0_context *)t0ctx)->ip; + goto t0_next; + for (;;) { + uint32_t t0x; + + t0_next: + t0x = T0_NEXT(&ip); + if (t0x < T0_INTERPRETED) { + switch (t0x) { + int32_t t0off; + + case 0: /* ret */ + t0x = T0_RPOP(); + rp -= (t0x >> 16); + t0x &= 0xFFFF; + if (t0x == 0) { + ip = NULL; + goto t0_exit; + } + ip = &t0_codeblock[t0x]; + break; + case 1: /* literal constant */ + T0_PUSHi(t0_parse7E_signed(&ip)); + break; + case 2: /* read local */ + T0_PUSH(T0_LOCAL(t0_parse7E_unsigned(&ip))); + break; + case 3: /* write local */ + T0_LOCAL(t0_parse7E_unsigned(&ip)) = T0_POP(); + break; + case 4: /* jump */ + t0off = t0_parse7E_signed(&ip); + ip += t0off; + break; + case 5: /* jump if */ + t0off = t0_parse7E_signed(&ip); + if (T0_POP()) { + ip += t0off; + } + break; + case 6: /* jump if not */ + t0off = t0_parse7E_signed(&ip); + if (!T0_POP()) { + ip += t0off; + } + break; + case 7: { + /* + */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(a + b); + + } + break; + case 8: { + /* - */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(a - b); + + } + break; + case 9: { + /* -rot */ + T0_NROT(); + } + break; + case 10: { + /* < */ + + int32_t b = T0_POPi(); + int32_t a = T0_POPi(); + T0_PUSH(-(uint32_t)(a < b)); + + } + break; + case 11: { + /* << */ + + int c = (int)T0_POPi(); + uint32_t x = T0_POP(); + T0_PUSH(x << c); + + } + break; + case 12: { + /* = */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(-(uint32_t)(a == b)); + + } + break; + case 13: { + /* > */ + + int32_t b = T0_POPi(); + int32_t a = T0_POPi(); + T0_PUSH(-(uint32_t)(a > b)); + + } + break; + case 14: { + /* >= */ + + int32_t b = T0_POPi(); + int32_t a = T0_POPi(); + T0_PUSH(-(uint32_t)(a >= b)); + + } + break; + case 15: { + /* >> */ + + int c = (int)T0_POPi(); + int32_t x = T0_POPi(); + T0_PUSHi(x >> c); + + } + break; + case 16: { + /* and */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(a & b); + + } + break; + case 17: { + /* co */ + T0_CO(); + } + break; + case 18: { + /* drop */ + (void)T0_POP(); + } + break; + case 19: { + /* dup */ + T0_PUSH(T0_PEEK(0)); + } + break; + case 20: { + /* eqOID */ + + const unsigned char *a2 = &t0_datablock[T0_POP()]; + const unsigned char *a1 = &CTX->pad[0]; + size_t len = a1[0]; + int x; + if (len == pgm_read_byte(&a2[0])) { + x = -(memcmp_P(a1 + 1, a2 + 1, len) == 0); + } else { + x = 0; + } + T0_PUSH((uint32_t)x); + + } + break; + case 21: { + /* fail */ + + CTX->err = T0_POPi(); + T0_CO(); + + } + break; + case 22: { + /* neg */ + + uint32_t a = T0_POP(); + T0_PUSH(-a); + + } + break; + case 23: { + /* over */ + T0_PUSH(T0_PEEK(1)); + } + break; + case 24: { + /* read-blob-inner */ + + uint32_t len = T0_POP(); + uint32_t addr = T0_POP(); + size_t clen = CTX->hlen; + if (clen > len) { + clen = (size_t)len; + } + if (addr != 0) { + memcpy_P((unsigned char *)CTX + addr, CTX->hbuf, clen); + } + CTX->hbuf += clen; + CTX->hlen -= clen; + T0_PUSH(addr + clen); + T0_PUSH(len - clen); + + } + break; + case 25: { + /* read8-low */ + + if (CTX->hlen == 0) { + T0_PUSHi(-1); + } else { + CTX->hlen --; + T0_PUSH(pgm_read_byte(CTX->hbuf ++)); + } + + } + break; + case 26: { + /* rot */ + T0_ROT(); + } + break; + case 27: { + /* set-ec-key */ + + size_t qlen = T0_POP(); + uint32_t curve = T0_POP(); + CTX->key.ec.curve = curve; + CTX->key.ec.q = CTX->key_data; + CTX->key.ec.qlen = qlen; + + } + break; + case 28: { + /* set-rsa-key */ + + size_t elen = T0_POP(); + size_t nlen = T0_POP(); + + CTX->key.rsa.n = CTX->key_data; + CTX->key.rsa.nlen = nlen; + CTX->key.rsa.e = CTX->key_data + nlen; + CTX->key.rsa.elen = elen; + + } + break; + case 29: { + /* set8 */ + + uint32_t addr = T0_POP(); + *((unsigned char *)CTX + addr) = (unsigned char)T0_POP(); + + } + break; + case 30: { + /* swap */ + T0_SWAP(); + } + break; + } + + } else { + T0_ENTER(ip, rp, t0x); + } + } +t0_exit: + ((t0_context *)t0ctx)->dp = dp; + ((t0_context *)t0ctx)->rp = rp; + ((t0_context *)t0ctx)->ip = ip; +} diff --git a/lib/bearssl-esp8266/src/x509/skey_decoder.c b/lib/bearssl-esp8266/src/x509/skey_decoder.c new file mode 100644 index 000000000..192abcaa0 --- /dev/null +++ b/lib/bearssl-esp8266/src/x509/skey_decoder.c @@ -0,0 +1,654 @@ +/* Automatically generated code; do not modify directly. */ + +#include +#include +#include + +typedef struct { + uint32_t *dp; + uint32_t *rp; + const unsigned char *ip; +} t0_context; + +static uint32_t +t0_parse7E_unsigned(const unsigned char **p) +{ + uint32_t x; + + x = 0; + for (;;) { + unsigned y; + + y = pgm_read_byte((*p)++); + x = (x << 7) | (uint32_t)(y & 0x7F); + if (y < 0x80) { + return x; + } + } +} + +static int32_t +t0_parse7E_signed(const unsigned char **p) +{ + int neg; + uint32_t x; + + neg = (pgm_read_byte(*p) >> 6) & 1; + x = (uint32_t)-neg; + for (;;) { + unsigned y; + + y = pgm_read_byte((*p)++); + x = (x << 7) | (uint32_t)(y & 0x7F); + if (y < 0x80) { + if (neg) { + return -(int32_t)~x - 1; + } else { + return (int32_t)x; + } + } + } +} + +#define T0_VBYTE(x, n) (unsigned char)((((uint32_t)(x) >> (n)) & 0x7F) | 0x80) +#define T0_FBYTE(x, n) (unsigned char)(((uint32_t)(x) >> (n)) & 0x7F) +#define T0_SBYTE(x) (unsigned char)((((uint32_t)(x) >> 28) + 0xF8) ^ 0xF8) +#define T0_INT1(x) T0_FBYTE(x, 0) +#define T0_INT2(x) T0_VBYTE(x, 7), T0_FBYTE(x, 0) +#define T0_INT3(x) T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0) +#define T0_INT4(x) T0_VBYTE(x, 21), T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0) +#define T0_INT5(x) T0_SBYTE(x), T0_VBYTE(x, 21), T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0) + +/* static const unsigned char t0_datablock[]; */ + + +void br_skey_decoder_init_main(void *t0ctx); + +void br_skey_decoder_run(void *t0ctx); + + + +#include "t_inner.h" + + + + + +#include "t_inner.h" + +#define CTX ((br_skey_decoder_context *)(void *)((unsigned char *)t0ctx - offsetof(br_skey_decoder_context, cpu))) +#define CONTEXT_NAME br_skey_decoder_context + +/* see bearssl_x509.h */ +void +br_skey_decoder_init(br_skey_decoder_context *ctx) +{ + memset(ctx, 0, sizeof *ctx); + ctx->cpu.dp = &ctx->dp_stack[0]; + ctx->cpu.rp = &ctx->rp_stack[0]; + br_skey_decoder_init_main(&ctx->cpu); + br_skey_decoder_run(&ctx->cpu); +} + +/* see bearssl_x509.h */ +void +br_skey_decoder_push(br_skey_decoder_context *ctx, + const void *data, size_t len) +{ + ctx->hbuf = data; + ctx->hlen = len; + br_skey_decoder_run(&ctx->cpu); +} + + + +static const unsigned char t0_datablock[] PROGMEM = { + + 0x00, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x07, + 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, 0x08, 0x2A, 0x86, 0x48, 0xCE, + 0x3D, 0x03, 0x01, 0x07, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x22, 0x05, 0x2B, + 0x81, 0x04, 0x00, 0x23 +}; + +static const unsigned char t0_codeblock[] PROGMEM = { + + 0x00, 0x01, 0x01, 0x07, 0x00, 0x00, 0x01, 0x01, 0x08, 0x00, 0x00, 0x13, + 0x13, 0x00, 0x00, 0x01, T0_INT1(BR_ERR_X509_BAD_TAG_CLASS), 0x00, 0x00, + 0x01, T0_INT1(BR_ERR_X509_BAD_TAG_VALUE), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_EXTRA_ELEMENT), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_INDEFINITE_LENGTH), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_INNER_TRUNC), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_INVALID_VALUE), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_LIMIT_EXCEEDED), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_NOT_CONSTRUCTED), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_NOT_PRIMITIVE), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_OVERFLOW), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_UNEXPECTED), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_UNSUPPORTED), 0x00, 0x00, 0x01, + T0_INT1(BR_KEYTYPE_EC), 0x00, 0x00, 0x01, T0_INT1(BR_KEYTYPE_RSA), + 0x00, 0x00, 0x01, T0_INT2(offsetof(CONTEXT_NAME, key_data)), 0x00, + 0x00, 0x01, T0_INT2(offsetof(CONTEXT_NAME, key_type)), 0x00, 0x00, + 0x33, 0x48, 0x00, 0x00, 0x01, T0_INT2(offsetof(CONTEXT_NAME, pad)), + 0x00, 0x00, 0x01, 0x13, 0x00, 0x00, 0x01, 0x1C, 0x00, 0x00, 0x01, 0x22, + 0x00, 0x00, 0x05, 0x02, 0x2C, 0x16, 0x00, 0x00, 0x06, 0x02, 0x2D, 0x16, + 0x00, 0x00, 0x01, 0x10, 0x3D, 0x00, 0x00, 0x0D, 0x05, 0x02, 0x2F, 0x16, + 0x3A, 0x00, 0x00, 0x0D, 0x05, 0x02, 0x2F, 0x16, 0x3B, 0x00, 0x00, 0x06, + 0x02, 0x27, 0x16, 0x00, 0x01, 0x03, 0x00, 0x54, 0x57, 0x01, 0x02, 0x3E, + 0x55, 0x23, 0x06, 0x02, 0x30, 0x16, 0x57, 0x01, 0x04, 0x3E, 0x02, 0x00, + 0x41, 0x3F, 0x00, 0x02, 0x03, 0x00, 0x53, 0x14, 0x14, 0x03, 0x01, 0x48, + 0x0E, 0x06, 0x02, 0x30, 0x16, 0x33, 0x4C, 0x58, 0x01, 0x7F, 0x19, 0x0D, + 0x06, 0x04, 0x13, 0x13, 0x04, 0x29, 0x01, 0x20, 0x19, 0x0D, 0x06, 0x16, + 0x13, 0x3A, 0x53, 0x4D, 0x02, 0x00, 0x06, 0x09, 0x02, 0x00, 0x0C, 0x06, + 0x02, 0x2A, 0x16, 0x04, 0x02, 0x03, 0x00, 0x3F, 0x04, 0x0D, 0x01, 0x21, + 0x19, 0x0D, 0x06, 0x04, 0x13, 0x3A, 0x04, 0x03, 0x30, 0x16, 0x13, 0x5D, + 0x02, 0x00, 0x05, 0x02, 0x30, 0x16, 0x02, 0x00, 0x02, 0x01, 0x1D, 0x00, + 0x02, 0x53, 0x4B, 0x05, 0x02, 0x30, 0x16, 0x5B, 0x15, 0x06, 0x07, 0x5D, + 0x01, 0x7F, 0x03, 0x01, 0x04, 0x16, 0x46, 0x15, 0x06, 0x10, 0x01, 0x00, + 0x03, 0x01, 0x14, 0x06, 0x03, 0x4D, 0x04, 0x02, 0x01, 0x00, 0x03, 0x00, + 0x04, 0x02, 0x30, 0x16, 0x3F, 0x57, 0x01, 0x04, 0x3E, 0x53, 0x02, 0x01, + 0x06, 0x03, 0x43, 0x04, 0x03, 0x02, 0x00, 0x40, 0x3F, 0x5D, 0x02, 0x01, + 0x06, 0x03, 0x32, 0x04, 0x01, 0x31, 0x00, 0x00, 0x54, 0x57, 0x01, 0x02, + 0x3E, 0x55, 0x06, 0x02, 0x30, 0x16, 0x57, 0x01, 0x02, 0x3E, 0x44, 0x3F, + 0x00, 0x07, 0x35, 0x50, 0x14, 0x05, 0x02, 0x2F, 0x16, 0x23, 0x01, 0x03, + 0x0B, 0x33, 0x17, 0x47, 0x07, 0x03, 0x00, 0x4F, 0x4F, 0x35, 0x4E, 0x14, + 0x14, 0x03, 0x01, 0x03, 0x02, 0x51, 0x14, 0x03, 0x03, 0x02, 0x02, 0x07, + 0x14, 0x03, 0x02, 0x51, 0x14, 0x03, 0x04, 0x02, 0x02, 0x07, 0x14, 0x03, + 0x02, 0x51, 0x14, 0x03, 0x05, 0x02, 0x02, 0x07, 0x14, 0x03, 0x02, 0x51, + 0x03, 0x06, 0x02, 0x00, 0x02, 0x01, 0x02, 0x03, 0x02, 0x04, 0x02, 0x05, + 0x02, 0x06, 0x1E, 0x00, 0x00, 0x19, 0x19, 0x00, 0x00, 0x01, 0x0B, 0x00, + 0x00, 0x01, 0x00, 0x20, 0x14, 0x06, 0x08, 0x01, 0x01, 0x21, 0x20, 0x22, + 0x20, 0x04, 0x75, 0x13, 0x00, 0x00, 0x01, + T0_INT2(3 * BR_X509_BUFSIZE_KEY), 0x00, 0x01, 0x01, 0x87, 0xFF, 0xFF, + 0x7F, 0x54, 0x57, 0x01, 0x02, 0x3E, 0x55, 0x01, 0x01, 0x0E, 0x06, 0x02, + 0x30, 0x16, 0x57, 0x01, 0x02, 0x19, 0x0D, 0x06, 0x06, 0x13, 0x3B, 0x44, + 0x32, 0x04, 0x1C, 0x01, 0x04, 0x19, 0x0D, 0x06, 0x08, 0x13, 0x3B, 0x01, + 0x00, 0x41, 0x31, 0x04, 0x0E, 0x01, 0x10, 0x19, 0x0D, 0x06, 0x05, 0x13, + 0x3A, 0x42, 0x04, 0x03, 0x30, 0x16, 0x13, 0x03, 0x00, 0x3F, 0x02, 0x00, + 0x34, 0x1F, 0x5A, 0x27, 0x16, 0x00, 0x01, 0x45, 0x0A, 0x06, 0x02, 0x29, + 0x16, 0x14, 0x03, 0x00, 0x08, 0x02, 0x00, 0x00, 0x00, 0x57, 0x01, 0x06, + 0x3E, 0x56, 0x00, 0x00, 0x20, 0x14, 0x06, 0x07, 0x1A, 0x14, 0x06, 0x01, + 0x12, 0x04, 0x76, 0x24, 0x00, 0x00, 0x4B, 0x05, 0x02, 0x30, 0x16, 0x37, + 0x15, 0x06, 0x04, 0x01, 0x17, 0x04, 0x12, 0x38, 0x15, 0x06, 0x04, 0x01, + 0x18, 0x04, 0x0A, 0x39, 0x15, 0x06, 0x04, 0x01, 0x19, 0x04, 0x02, 0x30, + 0x16, 0x00, 0x00, 0x1C, 0x57, 0x01, 0x02, 0x3E, 0x09, 0x50, 0x00, 0x00, + 0x35, 0x4E, 0x13, 0x00, 0x03, 0x14, 0x03, 0x00, 0x03, 0x01, 0x03, 0x02, + 0x53, 0x59, 0x14, 0x01, 0x81, 0x00, 0x0F, 0x06, 0x02, 0x2E, 0x16, 0x14, + 0x01, 0x00, 0x0D, 0x06, 0x0B, 0x13, 0x14, 0x05, 0x04, 0x13, 0x01, 0x00, + 0x00, 0x59, 0x04, 0x6F, 0x02, 0x01, 0x14, 0x05, 0x02, 0x2B, 0x16, 0x23, + 0x03, 0x01, 0x02, 0x02, 0x1F, 0x02, 0x02, 0x22, 0x03, 0x02, 0x14, 0x06, + 0x03, 0x59, 0x04, 0x68, 0x13, 0x02, 0x00, 0x02, 0x01, 0x08, 0x00, 0x00, + 0x14, 0x35, 0x1C, 0x08, 0x20, 0x1C, 0x07, 0x20, 0x4E, 0x00, 0x01, 0x59, + 0x14, 0x01, 0x81, 0x00, 0x0A, 0x06, 0x01, 0x00, 0x01, 0x81, 0x00, 0x08, + 0x14, 0x05, 0x02, 0x28, 0x16, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, + 0x00, 0x0E, 0x06, 0x19, 0x02, 0x00, 0x23, 0x03, 0x00, 0x14, 0x01, 0x83, + 0xFF, 0xFF, 0x7F, 0x0E, 0x06, 0x02, 0x29, 0x16, 0x01, 0x08, 0x0B, 0x20, + 0x59, 0x1C, 0x07, 0x04, 0x60, 0x00, 0x00, 0x52, 0x4A, 0x00, 0x00, 0x57, + 0x3C, 0x53, 0x00, 0x01, 0x53, 0x14, 0x05, 0x02, 0x2E, 0x16, 0x59, 0x14, + 0x01, 0x81, 0x00, 0x0F, 0x06, 0x02, 0x2E, 0x16, 0x03, 0x00, 0x14, 0x06, + 0x16, 0x59, 0x02, 0x00, 0x14, 0x01, 0x87, 0xFF, 0xFF, 0x7F, 0x0F, 0x06, + 0x02, 0x2E, 0x16, 0x01, 0x08, 0x0B, 0x07, 0x03, 0x00, 0x04, 0x67, 0x13, + 0x02, 0x00, 0x00, 0x00, 0x53, 0x14, 0x01, 0x81, 0x7F, 0x0E, 0x06, 0x08, + 0x5C, 0x01, 0x00, 0x36, 0x1F, 0x01, 0x00, 0x00, 0x14, 0x36, 0x1F, 0x36, + 0x22, 0x4C, 0x01, 0x7F, 0x00, 0x01, 0x59, 0x03, 0x00, 0x02, 0x00, 0x01, + 0x05, 0x10, 0x01, 0x01, 0x11, 0x18, 0x02, 0x00, 0x01, 0x06, 0x10, 0x14, + 0x01, 0x01, 0x11, 0x06, 0x02, 0x25, 0x16, 0x01, 0x04, 0x0B, 0x02, 0x00, + 0x01, 0x1F, 0x11, 0x14, 0x01, 0x1F, 0x0D, 0x06, 0x02, 0x26, 0x16, 0x07, + 0x00, 0x00, 0x14, 0x05, 0x05, 0x01, 0x00, 0x01, 0x7F, 0x00, 0x57, 0x00, + 0x00, 0x14, 0x05, 0x02, 0x29, 0x16, 0x23, 0x5A, 0x00, 0x00, 0x1B, 0x14, + 0x01, 0x00, 0x0F, 0x06, 0x01, 0x00, 0x13, 0x12, 0x04, 0x74, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x5D, 0x13, 0x00, 0x00, 0x14, 0x06, 0x07, 0x5E, 0x14, + 0x06, 0x01, 0x12, 0x04, 0x76, 0x00, 0x00, 0x01, 0x00, 0x19, 0x1A, 0x09, + 0x24, 0x00 +}; + +static const uint16_t t0_caddr[] PROGMEM = { + + 0, + 5, + 10, + 14, + 18, + 22, + 26, + 30, + 34, + 38, + 42, + 46, + 50, + 54, + 58, + 62, + 66, + 70, + 75, + 80, + 84, + 89, + 93, + 97, + 101, + 107, + 113, + 118, + 126, + 134, + 140, + 163, + 244, + 311, + 329, + 404, + 408, + 412, + 429, + 434, + 505, + 519, + 526, + 540, + 573, + 582, + 587, + 654, + 665, + 721, + 725, + 730, + 778, + 804, + 848, + 859, + 868, + 881, + 885, + 889, + 901 +}; + +#define T0_INTERPRETED 34 + +#define T0_ENTER(ip, rp, slot) do { \ + const unsigned char *t0_newip; \ + uint32_t t0_lnum; \ + t0_newip = &t0_codeblock[pgm_read_word(&t0_caddr[(slot) - T0_INTERPRETED])]; \ + t0_lnum = t0_parse7E_unsigned(&t0_newip); \ + (rp) += t0_lnum; \ + *((rp) ++) = (uint32_t)((ip) - &t0_codeblock[0]) + (t0_lnum << 16); \ + (ip) = t0_newip; \ + } while (0) + +#define T0_DEFENTRY(name, slot) \ +void \ +name(void *ctx) \ +{ \ + t0_context *t0ctx = ctx; \ + t0ctx->ip = &t0_codeblock[0]; \ + T0_ENTER(t0ctx->ip, t0ctx->rp, slot); \ +} + +T0_DEFENTRY(br_skey_decoder_init_main, 73) + +#define T0_NEXT(t0ipp) (pgm_read_byte((*t0ipp)++)) + +void +br_skey_decoder_run(void *t0ctx) +{ + uint32_t *dp, *rp; + const unsigned char *ip; + +#define T0_LOCAL(x) (*(rp - 2 - (x))) +#define T0_POP() (*-- dp) +#define T0_POPi() (*(int32_t *)(-- dp)) +#define T0_PEEK(x) (*(dp - 1 - (x))) +#define T0_PEEKi(x) (*(int32_t *)(dp - 1 - (x))) +#define T0_PUSH(v) do { *dp = (v); dp ++; } while (0) +#define T0_PUSHi(v) do { *(int32_t *)dp = (v); dp ++; } while (0) +#define T0_RPOP() (*-- rp) +#define T0_RPOPi() (*(int32_t *)(-- rp)) +#define T0_RPUSH(v) do { *rp = (v); rp ++; } while (0) +#define T0_RPUSHi(v) do { *(int32_t *)rp = (v); rp ++; } while (0) +#define T0_ROLL(x) do { \ + size_t t0len = (size_t)(x); \ + uint32_t t0tmp = *(dp - 1 - t0len); \ + memmove(dp - t0len - 1, dp - t0len, t0len * sizeof *dp); \ + *(dp - 1) = t0tmp; \ +} while (0) +#define T0_SWAP() do { \ + uint32_t t0tmp = *(dp - 2); \ + *(dp - 2) = *(dp - 1); \ + *(dp - 1) = t0tmp; \ +} while (0) +#define T0_ROT() do { \ + uint32_t t0tmp = *(dp - 3); \ + *(dp - 3) = *(dp - 2); \ + *(dp - 2) = *(dp - 1); \ + *(dp - 1) = t0tmp; \ +} while (0) +#define T0_NROT() do { \ + uint32_t t0tmp = *(dp - 1); \ + *(dp - 1) = *(dp - 2); \ + *(dp - 2) = *(dp - 3); \ + *(dp - 3) = t0tmp; \ +} while (0) +#define T0_PICK(x) do { \ + uint32_t t0depth = (x); \ + T0_PUSH(T0_PEEK(t0depth)); \ +} while (0) +#define T0_CO() do { \ + goto t0_exit; \ +} while (0) +#define T0_RET() goto t0_next + + dp = ((t0_context *)t0ctx)->dp; + rp = ((t0_context *)t0ctx)->rp; + ip = ((t0_context *)t0ctx)->ip; + goto t0_next; + for (;;) { + uint32_t t0x; + + t0_next: + t0x = T0_NEXT(&ip); + if (t0x < T0_INTERPRETED) { + switch (t0x) { + int32_t t0off; + + case 0: /* ret */ + t0x = T0_RPOP(); + rp -= (t0x >> 16); + t0x &= 0xFFFF; + if (t0x == 0) { + ip = NULL; + goto t0_exit; + } + ip = &t0_codeblock[t0x]; + break; + case 1: /* literal constant */ + T0_PUSHi(t0_parse7E_signed(&ip)); + break; + case 2: /* read local */ + T0_PUSH(T0_LOCAL(t0_parse7E_unsigned(&ip))); + break; + case 3: /* write local */ + T0_LOCAL(t0_parse7E_unsigned(&ip)) = T0_POP(); + break; + case 4: /* jump */ + t0off = t0_parse7E_signed(&ip); + ip += t0off; + break; + case 5: /* jump if */ + t0off = t0_parse7E_signed(&ip); + if (T0_POP()) { + ip += t0off; + } + break; + case 6: /* jump if not */ + t0off = t0_parse7E_signed(&ip); + if (!T0_POP()) { + ip += t0off; + } + break; + case 7: { + /* + */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(a + b); + + } + break; + case 8: { + /* - */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(a - b); + + } + break; + case 9: { + /* -rot */ + T0_NROT(); + } + break; + case 10: { + /* < */ + + int32_t b = T0_POPi(); + int32_t a = T0_POPi(); + T0_PUSH(-(uint32_t)(a < b)); + + } + break; + case 11: { + /* << */ + + int c = (int)T0_POPi(); + uint32_t x = T0_POP(); + T0_PUSH(x << c); + + } + break; + case 12: { + /* <> */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(-(uint32_t)(a != b)); + + } + break; + case 13: { + /* = */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(-(uint32_t)(a == b)); + + } + break; + case 14: { + /* > */ + + int32_t b = T0_POPi(); + int32_t a = T0_POPi(); + T0_PUSH(-(uint32_t)(a > b)); + + } + break; + case 15: { + /* >= */ + + int32_t b = T0_POPi(); + int32_t a = T0_POPi(); + T0_PUSH(-(uint32_t)(a >= b)); + + } + break; + case 16: { + /* >> */ + + int c = (int)T0_POPi(); + int32_t x = T0_POPi(); + T0_PUSHi(x >> c); + + } + break; + case 17: { + /* and */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(a & b); + + } + break; + case 18: { + /* co */ + T0_CO(); + } + break; + case 19: { + /* drop */ + (void)T0_POP(); + } + break; + case 20: { + /* dup */ + T0_PUSH(T0_PEEK(0)); + } + break; + case 21: { + /* eqOID */ + + const unsigned char *a2 = &t0_datablock[T0_POP()]; + const unsigned char *a1 = &CTX->pad[0]; + size_t len = a1[0]; + int x; + if (len == pgm_read_byte(&a2[0])) { + x = -(memcmp_P(a1 + 1, a2 + 1, len) == 0); + } else { + x = 0; + } + T0_PUSH((uint32_t)x); + + } + break; + case 22: { + /* fail */ + + CTX->err = T0_POPi(); + T0_CO(); + + } + break; + case 23: { + /* get8 */ + + uint32_t addr = T0_POP(); + T0_PUSH(*((unsigned char *)CTX + addr)); + + } + break; + case 24: { + /* neg */ + + uint32_t a = T0_POP(); + T0_PUSH(-a); + + } + break; + case 25: { + /* over */ + T0_PUSH(T0_PEEK(1)); + } + break; + case 26: { + /* read-blob-inner */ + + uint32_t len = T0_POP(); + uint32_t addr = T0_POP(); + size_t clen = CTX->hlen; + if (clen > len) { + clen = (size_t)len; + } + if (addr != 0) { + memcpy_P((unsigned char *)CTX + addr, CTX->hbuf, clen); + } + CTX->hbuf += clen; + CTX->hlen -= clen; + T0_PUSH(addr + clen); + T0_PUSH(len - clen); + + } + break; + case 27: { + /* read8-low */ + + if (CTX->hlen == 0) { + T0_PUSHi(-1); + } else { + CTX->hlen --; + T0_PUSH(pgm_read_byte(CTX->hbuf ++)); + } + + } + break; + case 28: { + /* rot */ + T0_ROT(); + } + break; + case 29: { + /* set-ec-key */ + + size_t xlen = T0_POP(); + uint32_t curve = T0_POP(); + CTX->key.ec.curve = curve; + CTX->key.ec.x = CTX->key_data; + CTX->key.ec.xlen = xlen; + + } + break; + case 30: { + /* set-rsa-key */ + + size_t iqlen = T0_POP(); + size_t dqlen = T0_POP(); + size_t dplen = T0_POP(); + size_t qlen = T0_POP(); + size_t plen = T0_POP(); + uint32_t n_bitlen = T0_POP(); + size_t off; + + CTX->key.rsa.n_bitlen = n_bitlen; + CTX->key.rsa.p = CTX->key_data; + CTX->key.rsa.plen = plen; + off = plen; + CTX->key.rsa.q = CTX->key_data + off; + CTX->key.rsa.qlen = qlen; + off += qlen; + CTX->key.rsa.dp = CTX->key_data + off; + CTX->key.rsa.dplen = dplen; + off += dplen; + CTX->key.rsa.dq = CTX->key_data + off; + CTX->key.rsa.dqlen = dqlen; + off += dqlen; + CTX->key.rsa.iq = CTX->key_data + off; + CTX->key.rsa.iqlen = iqlen; + + } + break; + case 31: { + /* set8 */ + + uint32_t addr = T0_POP(); + *((unsigned char *)CTX + addr) = (unsigned char)T0_POP(); + + } + break; + case 32: { + /* swap */ + T0_SWAP(); + } + break; + case 33: { + /* u>> */ + + int c = (int)T0_POPi(); + uint32_t x = T0_POP(); + T0_PUSH(x >> c); + + } + break; + } + + } else { + T0_ENTER(ip, rp, t0x); + } + } +t0_exit: + ((t0_context *)t0ctx)->dp = dp; + ((t0_context *)t0ctx)->rp = rp; + ((t0_context *)t0ctx)->ip = ip; +} diff --git a/lib/bearssl-esp8266/src/x509/x509_decoder.c b/lib/bearssl-esp8266/src/x509/x509_decoder.c new file mode 100644 index 000000000..b738755f5 --- /dev/null +++ b/lib/bearssl-esp8266/src/x509/x509_decoder.c @@ -0,0 +1,790 @@ +/* Automatically generated code; do not modify directly. */ + +#include +#include +#include + +typedef struct { + uint32_t *dp; + uint32_t *rp; + const unsigned char *ip; +} t0_context; + +static uint32_t +t0_parse7E_unsigned(const unsigned char **p) +{ + uint32_t x; + + x = 0; + for (;;) { + unsigned y; + + y = pgm_read_byte((*p)++); + x = (x << 7) | (uint32_t)(y & 0x7F); + if (y < 0x80) { + return x; + } + } +} + +static int32_t +t0_parse7E_signed(const unsigned char **p) +{ + int neg; + uint32_t x; + + neg = (pgm_read_byte(*p) >> 6) & 1; + x = (uint32_t)-neg; + for (;;) { + unsigned y; + + y = pgm_read_byte((*p)++); + x = (x << 7) | (uint32_t)(y & 0x7F); + if (y < 0x80) { + if (neg) { + return -(int32_t)~x - 1; + } else { + return (int32_t)x; + } + } + } +} + +#define T0_VBYTE(x, n) (unsigned char)((((uint32_t)(x) >> (n)) & 0x7F) | 0x80) +#define T0_FBYTE(x, n) (unsigned char)(((uint32_t)(x) >> (n)) & 0x7F) +#define T0_SBYTE(x) (unsigned char)((((uint32_t)(x) >> 28) + 0xF8) ^ 0xF8) +#define T0_INT1(x) T0_FBYTE(x, 0) +#define T0_INT2(x) T0_VBYTE(x, 7), T0_FBYTE(x, 0) +#define T0_INT3(x) T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0) +#define T0_INT4(x) T0_VBYTE(x, 21), T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0) +#define T0_INT5(x) T0_SBYTE(x), T0_VBYTE(x, 21), T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0) + +/* static const unsigned char t0_datablock[]; */ + + +void br_x509_decoder_init_main(void *t0ctx); + +void br_x509_decoder_run(void *t0ctx); + + + +#include "t_inner.h" + + + + + +#include "t_inner.h" + +#define CTX ((br_x509_decoder_context *)(void *)((unsigned char *)t0ctx - offsetof(br_x509_decoder_context, cpu))) +#define CONTEXT_NAME br_x509_decoder_context + +/* see bearssl_x509.h */ +void +br_x509_decoder_init(br_x509_decoder_context *ctx, + void (*append_dn)(void *ctx, const void *buf, size_t len), + void *append_dn_ctx, + void (*append_in)(void *ctx, const void *buf, size_t len), + void *append_in_ctx) +{ + memset(ctx, 0, sizeof *ctx); + /* obsolete + ctx->err = 0; + ctx->hbuf = NULL; + ctx->hlen = 0; + */ + ctx->append_dn = append_dn; + ctx->append_dn_ctx = append_dn_ctx; + ctx->append_in = append_in; + ctx->append_in_ctx = append_in_ctx; + ctx->cpu.dp = &ctx->dp_stack[0]; + ctx->cpu.rp = &ctx->rp_stack[0]; + br_x509_decoder_init_main(&ctx->cpu); + br_x509_decoder_run(&ctx->cpu); +} + +/* see bearssl_x509.h */ +void +br_x509_decoder_push(br_x509_decoder_context *ctx, + const void *data, size_t len) +{ + ctx->hbuf = data; + ctx->hlen = len; + br_x509_decoder_run(&ctx->cpu); +} + + + +static const unsigned char t0_datablock[] PROGMEM = { + + 0x00, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x09, + 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x05, 0x09, 0x2A, 0x86, + 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0E, 0x09, 0x2A, 0x86, 0x48, 0x86, + 0xF7, 0x0D, 0x01, 0x01, 0x0B, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, + 0x01, 0x01, 0x0C, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, + 0x0D, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, 0x08, 0x2A, 0x86, + 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x22, + 0x05, 0x2B, 0x81, 0x04, 0x00, 0x23, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, + 0x04, 0x01, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x01, 0x08, + 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x02, 0x08, 0x2A, 0x86, 0x48, + 0xCE, 0x3D, 0x04, 0x03, 0x03, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, + 0x03, 0x04, 0x00, 0x1F, 0x03, 0xFC, 0x07, 0x7F, 0x0B, 0x5E, 0x0F, 0x1F, + 0x12, 0xFE, 0x16, 0xBF, 0x1A, 0x9F, 0x1E, 0x7E, 0x22, 0x3F, 0x26, 0x1E, + 0x29, 0xDF, 0x00, 0x1F, 0x03, 0xFD, 0x07, 0x9F, 0x0B, 0x7E, 0x0F, 0x3F, + 0x13, 0x1E, 0x16, 0xDF, 0x1A, 0xBF, 0x1E, 0x9E, 0x22, 0x5F, 0x26, 0x3E, + 0x29, 0xFF, 0x03, 0x55, 0x1D, 0x13 +}; + +static const unsigned char t0_codeblock[] PROGMEM = { + + 0x00, 0x01, 0x00, 0x10, 0x00, 0x00, 0x01, 0x00, 0x11, 0x00, 0x00, 0x01, + 0x01, 0x09, 0x00, 0x00, 0x01, 0x01, 0x0A, 0x00, 0x00, 0x1A, 0x1A, 0x00, + 0x00, 0x01, T0_INT1(BR_ERR_X509_BAD_BOOLEAN), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_BAD_TAG_CLASS), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_BAD_TAG_VALUE), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_BAD_TIME), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_EXTRA_ELEMENT), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_INDEFINITE_LENGTH), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_INNER_TRUNC), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_LIMIT_EXCEEDED), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_NOT_CONSTRUCTED), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_NOT_PRIMITIVE), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_OVERFLOW), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_PARTIAL_BYTE), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_UNEXPECTED), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_UNSUPPORTED), 0x00, 0x00, 0x01, + T0_INT1(BR_KEYTYPE_EC), 0x00, 0x00, 0x01, T0_INT1(BR_KEYTYPE_RSA), + 0x00, 0x00, 0x01, T0_INT2(offsetof(CONTEXT_NAME, copy_dn)), 0x00, 0x00, + 0x01, T0_INT2(offsetof(CONTEXT_NAME, copy_in)), 0x00, 0x00, 0x01, + T0_INT2(offsetof(CONTEXT_NAME, decoded)), 0x00, 0x00, 0x01, + T0_INT2(offsetof(CONTEXT_NAME, isCA)), 0x00, 0x00, 0x01, + T0_INT2(offsetof(br_x509_decoder_context, pkey_data)), 0x01, + T0_INT2(BR_X509_BUFSIZE_KEY), 0x00, 0x00, 0x01, + T0_INT2(offsetof(CONTEXT_NAME, notafter_days)), 0x00, 0x00, 0x01, + T0_INT2(offsetof(CONTEXT_NAME, notafter_seconds)), 0x00, 0x00, 0x01, + T0_INT2(offsetof(CONTEXT_NAME, notbefore_days)), 0x00, 0x00, 0x01, + T0_INT2(offsetof(CONTEXT_NAME, notbefore_seconds)), 0x00, 0x00, 0x01, + T0_INT2(offsetof(CONTEXT_NAME, pad)), 0x00, 0x00, 0x01, + T0_INT2(offsetof(CONTEXT_NAME, signer_hash_id)), 0x00, 0x00, 0x01, + T0_INT2(offsetof(CONTEXT_NAME, signer_key_type)), 0x00, 0x00, 0x01, + 0x80, 0x45, 0x00, 0x00, 0x01, 0x80, 0x4E, 0x00, 0x00, 0x01, 0x80, 0x54, + 0x00, 0x00, 0x01, 0x81, 0x36, 0x00, 0x02, 0x03, 0x00, 0x03, 0x01, 0x1B, + 0x02, 0x01, 0x13, 0x26, 0x02, 0x00, 0x0F, 0x15, 0x00, 0x00, 0x05, 0x02, + 0x34, 0x1D, 0x00, 0x00, 0x06, 0x02, 0x35, 0x1D, 0x00, 0x00, 0x01, 0x10, + 0x50, 0x00, 0x00, 0x11, 0x05, 0x02, 0x38, 0x1D, 0x4D, 0x00, 0x00, 0x11, + 0x05, 0x02, 0x38, 0x1D, 0x4E, 0x00, 0x00, 0x06, 0x02, 0x30, 0x1D, 0x00, + 0x00, 0x1B, 0x19, 0x01, 0x08, 0x0E, 0x26, 0x29, 0x19, 0x09, 0x00, 0x00, + 0x01, 0x30, 0x0A, 0x1B, 0x01, 0x00, 0x01, 0x09, 0x4C, 0x05, 0x02, 0x2F, + 0x1D, 0x00, 0x00, 0x20, 0x20, 0x00, 0x00, 0x01, 0x80, 0x5A, 0x00, 0x00, + 0x01, 0x80, 0x62, 0x00, 0x00, 0x01, 0x80, 0x6B, 0x00, 0x00, 0x01, 0x80, + 0x74, 0x00, 0x00, 0x01, 0x80, 0x7D, 0x00, 0x00, 0x01, 0x3D, 0x00, 0x00, + 0x20, 0x11, 0x06, 0x04, 0x2B, 0x6C, 0x7B, 0x72, 0x00, 0x04, 0x01, 0x00, + 0x3E, 0x25, 0x01, 0x00, 0x3C, 0x25, 0x01, 0x00, 0x3D, 0x25, 0x01, 0x87, + 0xFF, 0xFF, 0x7F, 0x6E, 0x6E, 0x71, 0x1B, 0x01, 0x20, 0x11, 0x06, 0x11, + 0x1A, 0x4D, 0x6C, 0x71, 0x01, 0x02, 0x51, 0x6F, 0x01, 0x02, 0x12, 0x06, + 0x02, 0x39, 0x1D, 0x52, 0x71, 0x01, 0x02, 0x51, 0x6D, 0x6E, 0x7B, 0x01, + 0x01, 0x3D, 0x25, 0x6E, 0x7B, 0x01, 0x00, 0x3D, 0x25, 0x6E, 0x66, 0x44, + 0x24, 0x43, 0x24, 0x66, 0x42, 0x24, 0x41, 0x24, 0x52, 0x01, 0x01, 0x3C, + 0x25, 0x6E, 0x7B, 0x01, 0x00, 0x3C, 0x25, 0x6E, 0x6E, 0x61, 0x05, 0x02, + 0x39, 0x1D, 0x75, 0x1C, 0x06, 0x1C, 0x7B, 0x62, 0x6E, 0x40, 0x69, 0x03, + 0x00, 0x40, 0x26, 0x02, 0x00, 0x09, 0x26, 0x02, 0x00, 0x0A, 0x69, 0x03, + 0x01, 0x52, 0x52, 0x02, 0x00, 0x02, 0x01, 0x18, 0x04, 0x1E, 0x5B, 0x1C, + 0x06, 0x18, 0x65, 0x03, 0x02, 0x52, 0x62, 0x1B, 0x03, 0x03, 0x1B, 0x40, + 0x23, 0x0D, 0x06, 0x02, 0x33, 0x1D, 0x63, 0x02, 0x02, 0x02, 0x03, 0x17, + 0x04, 0x02, 0x39, 0x1D, 0x52, 0x01, 0x00, 0x3F, 0x25, 0x72, 0x01, 0x21, + 0x5C, 0x01, 0x22, 0x5C, 0x1B, 0x01, 0x23, 0x11, 0x06, 0x28, 0x1A, 0x4D, + 0x6C, 0x6E, 0x1B, 0x06, 0x1D, 0x6E, 0x61, 0x1A, 0x71, 0x1B, 0x01, 0x01, + 0x11, 0x06, 0x03, 0x64, 0x1A, 0x71, 0x01, 0x04, 0x51, 0x6C, 0x4B, 0x1C, + 0x06, 0x03, 0x60, 0x04, 0x01, 0x7C, 0x52, 0x52, 0x04, 0x60, 0x52, 0x52, + 0x04, 0x08, 0x01, 0x7F, 0x11, 0x05, 0x02, 0x38, 0x1D, 0x1A, 0x52, 0x6E, + 0x61, 0x06, 0x80, 0x63, 0x76, 0x1C, 0x06, 0x06, 0x01, 0x02, 0x3B, 0x04, + 0x80, 0x57, 0x77, 0x1C, 0x06, 0x06, 0x01, 0x03, 0x3B, 0x04, 0x80, 0x4D, + 0x78, 0x1C, 0x06, 0x06, 0x01, 0x04, 0x3B, 0x04, 0x80, 0x43, 0x79, 0x1C, + 0x06, 0x05, 0x01, 0x05, 0x3B, 0x04, 0x3A, 0x7A, 0x1C, 0x06, 0x05, 0x01, + 0x06, 0x3B, 0x04, 0x31, 0x56, 0x1C, 0x06, 0x05, 0x01, 0x02, 0x3A, 0x04, + 0x28, 0x57, 0x1C, 0x06, 0x05, 0x01, 0x03, 0x3A, 0x04, 0x1F, 0x58, 0x1C, + 0x06, 0x05, 0x01, 0x04, 0x3A, 0x04, 0x16, 0x59, 0x1C, 0x06, 0x05, 0x01, + 0x05, 0x3A, 0x04, 0x0D, 0x5A, 0x1C, 0x06, 0x05, 0x01, 0x06, 0x3A, 0x04, + 0x04, 0x01, 0x00, 0x01, 0x00, 0x04, 0x04, 0x01, 0x00, 0x01, 0x00, 0x47, + 0x25, 0x46, 0x25, 0x7B, 0x62, 0x7B, 0x52, 0x1A, 0x01, 0x01, 0x3E, 0x25, + 0x74, 0x30, 0x1D, 0x00, 0x00, 0x01, 0x81, 0x06, 0x00, 0x01, 0x55, 0x0D, + 0x06, 0x02, 0x32, 0x1D, 0x1B, 0x03, 0x00, 0x0A, 0x02, 0x00, 0x00, 0x00, + 0x6E, 0x72, 0x1B, 0x01, 0x01, 0x11, 0x06, 0x08, 0x64, 0x01, 0x01, 0x15, + 0x3F, 0x25, 0x04, 0x01, 0x2B, 0x7B, 0x00, 0x00, 0x71, 0x01, 0x06, 0x51, + 0x70, 0x00, 0x00, 0x71, 0x01, 0x03, 0x51, 0x6C, 0x73, 0x06, 0x02, 0x37, + 0x1D, 0x00, 0x00, 0x26, 0x1B, 0x06, 0x07, 0x21, 0x1B, 0x06, 0x01, 0x16, + 0x04, 0x76, 0x2B, 0x00, 0x00, 0x01, 0x01, 0x51, 0x6B, 0x01, 0x01, 0x10, + 0x06, 0x02, 0x2C, 0x1D, 0x73, 0x27, 0x00, 0x00, 0x61, 0x05, 0x02, 0x39, + 0x1D, 0x48, 0x1C, 0x06, 0x04, 0x01, 0x17, 0x04, 0x12, 0x49, 0x1C, 0x06, + 0x04, 0x01, 0x18, 0x04, 0x0A, 0x4A, 0x1C, 0x06, 0x04, 0x01, 0x19, 0x04, + 0x02, 0x39, 0x1D, 0x00, 0x04, 0x71, 0x1B, 0x01, 0x17, 0x01, 0x18, 0x4C, + 0x05, 0x02, 0x2F, 0x1D, 0x01, 0x18, 0x11, 0x03, 0x00, 0x4E, 0x6C, 0x67, + 0x02, 0x00, 0x06, 0x0C, 0x01, 0x80, 0x64, 0x08, 0x03, 0x01, 0x67, 0x02, + 0x01, 0x09, 0x04, 0x0E, 0x1B, 0x01, 0x32, 0x0D, 0x06, 0x04, 0x01, 0x80, + 0x64, 0x09, 0x01, 0x8E, 0x6C, 0x09, 0x03, 0x01, 0x02, 0x01, 0x01, 0x82, + 0x6D, 0x08, 0x02, 0x01, 0x01, 0x03, 0x09, 0x01, 0x04, 0x0C, 0x09, 0x02, + 0x01, 0x01, 0x80, 0x63, 0x09, 0x01, 0x80, 0x64, 0x0C, 0x0A, 0x02, 0x01, + 0x01, 0x83, 0x0F, 0x09, 0x01, 0x83, 0x10, 0x0C, 0x09, 0x03, 0x03, 0x01, + 0x01, 0x01, 0x0C, 0x68, 0x2A, 0x01, 0x01, 0x0E, 0x02, 0x01, 0x01, 0x04, + 0x07, 0x28, 0x02, 0x01, 0x01, 0x80, 0x64, 0x07, 0x27, 0x02, 0x01, 0x01, + 0x83, 0x10, 0x07, 0x28, 0x1F, 0x15, 0x06, 0x03, 0x01, 0x18, 0x09, 0x5E, + 0x09, 0x53, 0x1B, 0x01, 0x05, 0x14, 0x02, 0x03, 0x09, 0x03, 0x03, 0x01, + 0x1F, 0x15, 0x01, 0x01, 0x26, 0x68, 0x02, 0x03, 0x09, 0x2A, 0x03, 0x03, + 0x01, 0x00, 0x01, 0x17, 0x68, 0x01, 0x9C, 0x10, 0x08, 0x03, 0x02, 0x01, + 0x00, 0x01, 0x3B, 0x68, 0x01, 0x3C, 0x08, 0x02, 0x02, 0x09, 0x03, 0x02, + 0x01, 0x00, 0x01, 0x3C, 0x68, 0x02, 0x02, 0x09, 0x03, 0x02, 0x73, 0x1B, + 0x01, 0x2E, 0x11, 0x06, 0x0D, 0x1A, 0x73, 0x1B, 0x01, 0x30, 0x01, 0x39, + 0x4C, 0x06, 0x03, 0x1A, 0x04, 0x74, 0x01, 0x80, 0x5A, 0x10, 0x06, 0x02, + 0x2F, 0x1D, 0x52, 0x02, 0x03, 0x02, 0x02, 0x00, 0x01, 0x73, 0x54, 0x01, + 0x0A, 0x08, 0x03, 0x00, 0x73, 0x54, 0x02, 0x00, 0x09, 0x00, 0x02, 0x03, + 0x00, 0x03, 0x01, 0x67, 0x1B, 0x02, 0x01, 0x02, 0x00, 0x4C, 0x05, 0x02, + 0x2F, 0x1D, 0x00, 0x00, 0x23, 0x71, 0x01, 0x02, 0x51, 0x0B, 0x6A, 0x00, + 0x03, 0x1B, 0x03, 0x00, 0x03, 0x01, 0x03, 0x02, 0x6C, 0x73, 0x1B, 0x01, + 0x81, 0x00, 0x13, 0x06, 0x02, 0x36, 0x1D, 0x1B, 0x01, 0x00, 0x11, 0x06, + 0x0B, 0x1A, 0x1B, 0x05, 0x04, 0x1A, 0x01, 0x00, 0x00, 0x73, 0x04, 0x6F, + 0x02, 0x01, 0x1B, 0x05, 0x02, 0x33, 0x1D, 0x2A, 0x03, 0x01, 0x02, 0x02, + 0x25, 0x02, 0x02, 0x29, 0x03, 0x02, 0x1B, 0x06, 0x03, 0x73, 0x04, 0x68, + 0x1A, 0x02, 0x00, 0x02, 0x01, 0x0A, 0x00, 0x01, 0x73, 0x1B, 0x01, 0x81, + 0x00, 0x0D, 0x06, 0x01, 0x00, 0x01, 0x81, 0x00, 0x0A, 0x1B, 0x05, 0x02, + 0x31, 0x1D, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x12, 0x06, + 0x19, 0x02, 0x00, 0x2A, 0x03, 0x00, 0x1B, 0x01, 0x83, 0xFF, 0xFF, 0x7F, + 0x12, 0x06, 0x02, 0x32, 0x1D, 0x01, 0x08, 0x0E, 0x26, 0x73, 0x23, 0x09, + 0x04, 0x60, 0x00, 0x00, 0x6B, 0x5F, 0x00, 0x00, 0x6C, 0x7B, 0x00, 0x00, + 0x71, 0x4F, 0x6C, 0x00, 0x01, 0x6C, 0x1B, 0x05, 0x02, 0x36, 0x1D, 0x73, + 0x1B, 0x01, 0x81, 0x00, 0x13, 0x06, 0x02, 0x36, 0x1D, 0x03, 0x00, 0x1B, + 0x06, 0x16, 0x73, 0x02, 0x00, 0x1B, 0x01, 0x87, 0xFF, 0xFF, 0x7F, 0x13, + 0x06, 0x02, 0x36, 0x1D, 0x01, 0x08, 0x0E, 0x09, 0x03, 0x00, 0x04, 0x67, + 0x1A, 0x02, 0x00, 0x00, 0x00, 0x6C, 0x1B, 0x01, 0x81, 0x7F, 0x12, 0x06, + 0x08, 0x7B, 0x01, 0x00, 0x45, 0x25, 0x01, 0x00, 0x00, 0x1B, 0x45, 0x25, + 0x45, 0x29, 0x63, 0x01, 0x7F, 0x00, 0x01, 0x73, 0x03, 0x00, 0x02, 0x00, + 0x01, 0x05, 0x14, 0x01, 0x01, 0x15, 0x1E, 0x02, 0x00, 0x01, 0x06, 0x14, + 0x1B, 0x01, 0x01, 0x15, 0x06, 0x02, 0x2D, 0x1D, 0x01, 0x04, 0x0E, 0x02, + 0x00, 0x01, 0x1F, 0x15, 0x1B, 0x01, 0x1F, 0x11, 0x06, 0x02, 0x2E, 0x1D, + 0x09, 0x00, 0x00, 0x1B, 0x05, 0x05, 0x01, 0x00, 0x01, 0x7F, 0x00, 0x71, + 0x00, 0x00, 0x1B, 0x05, 0x02, 0x32, 0x1D, 0x2A, 0x74, 0x00, 0x00, 0x22, + 0x1B, 0x01, 0x00, 0x13, 0x06, 0x01, 0x00, 0x1A, 0x16, 0x04, 0x74, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x0B, 0x00, 0x00, 0x01, 0x15, 0x00, 0x00, + 0x01, 0x1F, 0x00, 0x00, 0x01, 0x29, 0x00, 0x00, 0x01, 0x33, 0x00, 0x00, + 0x7C, 0x1A, 0x00, 0x00, 0x1B, 0x06, 0x07, 0x7D, 0x1B, 0x06, 0x01, 0x16, + 0x04, 0x76, 0x00, 0x00, 0x01, 0x00, 0x20, 0x21, 0x0B, 0x2B, 0x00 +}; + +static const uint16_t t0_caddr[] PROGMEM = { + + 0, + 5, + 10, + 15, + 20, + 24, + 28, + 32, + 36, + 40, + 44, + 48, + 52, + 56, + 60, + 64, + 68, + 72, + 76, + 80, + 84, + 88, + 93, + 98, + 103, + 108, + 116, + 121, + 126, + 131, + 136, + 141, + 146, + 151, + 156, + 161, + 166, + 171, + 186, + 192, + 198, + 203, + 211, + 219, + 225, + 236, + 251, + 255, + 260, + 265, + 270, + 275, + 280, + 284, + 294, + 637, + 642, + 656, + 676, + 683, + 695, + 709, + 724, + 757, + 977, + 991, + 1008, + 1017, + 1084, + 1140, + 1144, + 1148, + 1153, + 1201, + 1227, + 1271, + 1282, + 1291, + 1304, + 1308, + 1312, + 1316, + 1320, + 1324, + 1328, + 1332, + 1344 +}; + +#define T0_INTERPRETED 39 + +#define T0_ENTER(ip, rp, slot) do { \ + const unsigned char *t0_newip; \ + uint32_t t0_lnum; \ + t0_newip = &t0_codeblock[pgm_read_word(&t0_caddr[(slot) - T0_INTERPRETED])]; \ + t0_lnum = t0_parse7E_unsigned(&t0_newip); \ + (rp) += t0_lnum; \ + *((rp) ++) = (uint32_t)((ip) - &t0_codeblock[0]) + (t0_lnum << 16); \ + (ip) = t0_newip; \ + } while (0) + +#define T0_DEFENTRY(name, slot) \ +void \ +name(void *ctx) \ +{ \ + t0_context *t0ctx = ctx; \ + t0ctx->ip = &t0_codeblock[0]; \ + T0_ENTER(t0ctx->ip, t0ctx->rp, slot); \ +} + +T0_DEFENTRY(br_x509_decoder_init_main, 93) + +#define T0_NEXT(t0ipp) (pgm_read_byte((*t0ipp)++)) + +void +br_x509_decoder_run(void *t0ctx) +{ + uint32_t *dp, *rp; + const unsigned char *ip; + +#define T0_LOCAL(x) (*(rp - 2 - (x))) +#define T0_POP() (*-- dp) +#define T0_POPi() (*(int32_t *)(-- dp)) +#define T0_PEEK(x) (*(dp - 1 - (x))) +#define T0_PEEKi(x) (*(int32_t *)(dp - 1 - (x))) +#define T0_PUSH(v) do { *dp = (v); dp ++; } while (0) +#define T0_PUSHi(v) do { *(int32_t *)dp = (v); dp ++; } while (0) +#define T0_RPOP() (*-- rp) +#define T0_RPOPi() (*(int32_t *)(-- rp)) +#define T0_RPUSH(v) do { *rp = (v); rp ++; } while (0) +#define T0_RPUSHi(v) do { *(int32_t *)rp = (v); rp ++; } while (0) +#define T0_ROLL(x) do { \ + size_t t0len = (size_t)(x); \ + uint32_t t0tmp = *(dp - 1 - t0len); \ + memmove(dp - t0len - 1, dp - t0len, t0len * sizeof *dp); \ + *(dp - 1) = t0tmp; \ +} while (0) +#define T0_SWAP() do { \ + uint32_t t0tmp = *(dp - 2); \ + *(dp - 2) = *(dp - 1); \ + *(dp - 1) = t0tmp; \ +} while (0) +#define T0_ROT() do { \ + uint32_t t0tmp = *(dp - 3); \ + *(dp - 3) = *(dp - 2); \ + *(dp - 2) = *(dp - 1); \ + *(dp - 1) = t0tmp; \ +} while (0) +#define T0_NROT() do { \ + uint32_t t0tmp = *(dp - 1); \ + *(dp - 1) = *(dp - 2); \ + *(dp - 2) = *(dp - 3); \ + *(dp - 3) = t0tmp; \ +} while (0) +#define T0_PICK(x) do { \ + uint32_t t0depth = (x); \ + T0_PUSH(T0_PEEK(t0depth)); \ +} while (0) +#define T0_CO() do { \ + goto t0_exit; \ +} while (0) +#define T0_RET() goto t0_next + + dp = ((t0_context *)t0ctx)->dp; + rp = ((t0_context *)t0ctx)->rp; + ip = ((t0_context *)t0ctx)->ip; + goto t0_next; + for (;;) { + uint32_t t0x; + + t0_next: + t0x = T0_NEXT(&ip); + if (t0x < T0_INTERPRETED) { + switch (t0x) { + int32_t t0off; + + case 0: /* ret */ + t0x = T0_RPOP(); + rp -= (t0x >> 16); + t0x &= 0xFFFF; + if (t0x == 0) { + ip = NULL; + goto t0_exit; + } + ip = &t0_codeblock[t0x]; + break; + case 1: /* literal constant */ + T0_PUSHi(t0_parse7E_signed(&ip)); + break; + case 2: /* read local */ + T0_PUSH(T0_LOCAL(t0_parse7E_unsigned(&ip))); + break; + case 3: /* write local */ + T0_LOCAL(t0_parse7E_unsigned(&ip)) = T0_POP(); + break; + case 4: /* jump */ + t0off = t0_parse7E_signed(&ip); + ip += t0off; + break; + case 5: /* jump if */ + t0off = t0_parse7E_signed(&ip); + if (T0_POP()) { + ip += t0off; + } + break; + case 6: /* jump if not */ + t0off = t0_parse7E_signed(&ip); + if (!T0_POP()) { + ip += t0off; + } + break; + case 7: { + /* %25 */ + + int32_t b = T0_POPi(); + int32_t a = T0_POPi(); + T0_PUSHi(a % b); + + } + break; + case 8: { + /* * */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(a * b); + + } + break; + case 9: { + /* + */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(a + b); + + } + break; + case 10: { + /* - */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(a - b); + + } + break; + case 11: { + /* -rot */ + T0_NROT(); + } + break; + case 12: { + /* / */ + + int32_t b = T0_POPi(); + int32_t a = T0_POPi(); + T0_PUSHi(a / b); + + } + break; + case 13: { + /* < */ + + int32_t b = T0_POPi(); + int32_t a = T0_POPi(); + T0_PUSH(-(uint32_t)(a < b)); + + } + break; + case 14: { + /* << */ + + int c = (int)T0_POPi(); + uint32_t x = T0_POP(); + T0_PUSH(x << c); + + } + break; + case 15: { + /* <= */ + + int32_t b = T0_POPi(); + int32_t a = T0_POPi(); + T0_PUSH(-(uint32_t)(a <= b)); + + } + break; + case 16: { + /* <> */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(-(uint32_t)(a != b)); + + } + break; + case 17: { + /* = */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(-(uint32_t)(a == b)); + + } + break; + case 18: { + /* > */ + + int32_t b = T0_POPi(); + int32_t a = T0_POPi(); + T0_PUSH(-(uint32_t)(a > b)); + + } + break; + case 19: { + /* >= */ + + int32_t b = T0_POPi(); + int32_t a = T0_POPi(); + T0_PUSH(-(uint32_t)(a >= b)); + + } + break; + case 20: { + /* >> */ + + int c = (int)T0_POPi(); + int32_t x = T0_POPi(); + T0_PUSHi(x >> c); + + } + break; + case 21: { + /* and */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(a & b); + + } + break; + case 22: { + /* co */ + T0_CO(); + } + break; + case 23: { + /* copy-ec-pkey */ + + size_t qlen = T0_POP(); + uint32_t curve = T0_POP(); + CTX->pkey.key_type = BR_KEYTYPE_EC; + CTX->pkey.key.ec.curve = curve; + CTX->pkey.key.ec.q = CTX->pkey_data; + CTX->pkey.key.ec.qlen = qlen; + + } + break; + case 24: { + /* copy-rsa-pkey */ + + size_t elen = T0_POP(); + size_t nlen = T0_POP(); + CTX->pkey.key_type = BR_KEYTYPE_RSA; + CTX->pkey.key.rsa.n = CTX->pkey_data; + CTX->pkey.key.rsa.nlen = nlen; + CTX->pkey.key.rsa.e = CTX->pkey_data + nlen; + CTX->pkey.key.rsa.elen = elen; + + } + break; + case 25: { + /* data-get8 */ + + size_t addr = T0_POP(); + T0_PUSH(pgm_read_byte(&t0_datablock[addr])); + + } + break; + case 26: { + /* drop */ + (void)T0_POP(); + } + break; + case 27: { + /* dup */ + T0_PUSH(T0_PEEK(0)); + } + break; + case 28: { + /* eqOID */ + + const unsigned char *a2 = &t0_datablock[T0_POP()]; + const unsigned char *a1 = &CTX->pad[0]; + size_t len = a1[0]; + int x; + if (len == pgm_read_byte(&a2[0])) { + x = -(memcmp_P(a1 + 1, a2 + 1, len) == 0); + } else { + x = 0; + } + T0_PUSH((uint32_t)x); + + } + break; + case 29: { + /* fail */ + + CTX->err = T0_POPi(); + T0_CO(); + + } + break; + case 30: { + /* neg */ + + uint32_t a = T0_POP(); + T0_PUSH(-a); + + } + break; + case 31: { + /* or */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(a | b); + + } + break; + case 32: { + /* over */ + T0_PUSH(T0_PEEK(1)); + } + break; + case 33: { + /* read-blob-inner */ + + uint32_t len = T0_POP(); + uint32_t addr = T0_POP(); + size_t clen = CTX->hlen; + if (clen > len) { + clen = (size_t)len; + } + if (addr != 0) { + memcpy_P((unsigned char *)CTX + addr, CTX->hbuf, clen); + } + if (CTX->copy_dn && CTX->append_dn) { + CTX->append_dn(CTX->append_dn_ctx, CTX->hbuf, clen); + } + if (CTX->copy_in && CTX->append_in) { + CTX->append_in(CTX->append_in_ctx, CTX->hbuf, clen); + } + CTX->hbuf += clen; + CTX->hlen -= clen; + T0_PUSH(addr + clen); + T0_PUSH(len - clen); + + } + break; + case 34: { + /* read8-low */ + + if (CTX->hlen == 0) { + T0_PUSHi(-1); + } else { + unsigned char x = pgm_read_byte(CTX->hbuf ++); + if (CTX->copy_dn && CTX->append_dn) { + CTX->append_dn(CTX->append_dn_ctx, &x, 1); + } + if (CTX->copy_in && CTX->append_in) { + CTX->append_in(CTX->append_in_ctx, &x, 1); + } + CTX->hlen --; + T0_PUSH(x); + } + + } + break; + case 35: { + /* rot */ + T0_ROT(); + } + break; + case 36: { + /* set32 */ + + uint32_t addr = T0_POP(); + *(uint32_t *)(void *)((unsigned char *)CTX + addr) = T0_POP(); + + } + break; + case 37: { + /* set8 */ + + uint32_t addr = T0_POP(); + *((unsigned char *)CTX + addr) = (unsigned char)T0_POP(); + + } + break; + case 38: { + /* swap */ + T0_SWAP(); + } + break; + } + + } else { + T0_ENTER(ip, rp, t0x); + } + } +t0_exit: + ((t0_context *)t0ctx)->dp = dp; + ((t0_context *)t0ctx)->rp = rp; + ((t0_context *)t0ctx)->ip = ip; +} diff --git a/lib/bearssl-esp8266/src/x509/x509_knownkey.c b/lib/bearssl-esp8266/src/x509/x509_knownkey.c new file mode 100644 index 000000000..b4443baf8 --- /dev/null +++ b/lib/bearssl-esp8266/src/x509/x509_knownkey.c @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_x509.h */ +void +br_x509_knownkey_init_rsa(br_x509_knownkey_context *ctx, + const br_rsa_public_key *pk, unsigned usages) +{ + ctx->vtable = &br_x509_knownkey_vtable; + ctx->pkey.key_type = BR_KEYTYPE_RSA; + ctx->pkey.key.rsa = *pk; + ctx->usages = usages; +} + +/* see bearssl_x509.h */ +void +br_x509_knownkey_init_ec(br_x509_knownkey_context *ctx, + const br_ec_public_key *pk, unsigned usages) +{ + ctx->vtable = &br_x509_knownkey_vtable; + ctx->pkey.key_type = BR_KEYTYPE_EC; + ctx->pkey.key.ec = *pk; + ctx->usages = usages; +} + +static void +kk_start_chain(const br_x509_class **ctx, const char *server_name) +{ + (void)ctx; + (void)server_name; +} + +static void +kk_start_cert(const br_x509_class **ctx, uint32_t length) +{ + (void)ctx; + (void)length; +} + +static void +kk_append(const br_x509_class **ctx, const unsigned char *buf, size_t len) +{ + (void)ctx; + (void)buf; + (void)len; +} + +static void +kk_end_cert(const br_x509_class **ctx) +{ + (void)ctx; +} + +static unsigned +kk_end_chain(const br_x509_class **ctx) +{ + (void)ctx; + return 0; +} + +static const br_x509_pkey * +kk_get_pkey(const br_x509_class *const *ctx, unsigned *usages) +{ + const br_x509_knownkey_context *xc; + + xc = (const br_x509_knownkey_context *)ctx; + if (usages != NULL) { + *usages = xc->usages; + } + return &xc->pkey; +} + +/* see bearssl_x509.h */ +const br_x509_class br_x509_knownkey_vtable PROGMEM = { + sizeof(br_x509_knownkey_context), + kk_start_chain, + kk_start_cert, + kk_append, + kk_end_cert, + kk_end_chain, + kk_get_pkey +}; diff --git a/lib/bearssl-esp8266/src/x509/x509_minimal.c b/lib/bearssl-esp8266/src/x509/x509_minimal.c new file mode 100644 index 000000000..b648d7a2b --- /dev/null +++ b/lib/bearssl-esp8266/src/x509/x509_minimal.c @@ -0,0 +1,1776 @@ +/* Automatically generated code; do not modify directly. */ + +#include +#include +#include + +typedef struct { + uint32_t *dp; + uint32_t *rp; + const unsigned char *ip; +} t0_context; + +static uint32_t +t0_parse7E_unsigned(const unsigned char **p) +{ + uint32_t x; + + x = 0; + for (;;) { + unsigned y; + + y = pgm_read_byte((*p)++); + x = (x << 7) | (uint32_t)(y & 0x7F); + if (y < 0x80) { + return x; + } + } +} + +static int32_t +t0_parse7E_signed(const unsigned char **p) +{ + int neg; + uint32_t x; + + neg = (pgm_read_byte(*p) >> 6) & 1; + x = (uint32_t)-neg; + for (;;) { + unsigned y; + + y = pgm_read_byte((*p)++); + x = (x << 7) | (uint32_t)(y & 0x7F); + if (y < 0x80) { + if (neg) { + return -(int32_t)~x - 1; + } else { + return (int32_t)x; + } + } + } +} + +#define T0_VBYTE(x, n) (unsigned char)((((uint32_t)(x) >> (n)) & 0x7F) | 0x80) +#define T0_FBYTE(x, n) (unsigned char)(((uint32_t)(x) >> (n)) & 0x7F) +#define T0_SBYTE(x) (unsigned char)((((uint32_t)(x) >> 28) + 0xF8) ^ 0xF8) +#define T0_INT1(x) T0_FBYTE(x, 0) +#define T0_INT2(x) T0_VBYTE(x, 7), T0_FBYTE(x, 0) +#define T0_INT3(x) T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0) +#define T0_INT4(x) T0_VBYTE(x, 21), T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0) +#define T0_INT5(x) T0_SBYTE(x), T0_VBYTE(x, 21), T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0) + +/* static const unsigned char t0_datablock[]; */ + + +void br_x509_minimal_init_main(void *t0ctx); + +void br_x509_minimal_run(void *t0ctx); + + + +#include "t_inner.h" + + + + + +#include "t_inner.h" + +/* + * Implementation Notes + * -------------------- + * + * The C code pushes the data by chunks; all decoding is done in the + * T0 code. The cert_length value is set to the certificate length when + * a new certificate is started; the T0 code picks it up as outer limit, + * and decoding functions use it to ensure that no attempt is made at + * reading past it. The T0 code also checks that once the certificate is + * decoded, there are no trailing bytes. + * + * The T0 code sets cert_length to 0 when the certificate is fully + * decoded. + * + * The C code must still perform two checks: + * + * -- If the certificate length is 0, then the T0 code will not be + * invoked at all. This invalid condition must thus be reported by the + * C code. + * + * -- When reaching the end of certificate, the C code must verify that + * the certificate length has been set to 0, thereby signaling that + * the T0 code properly decoded a certificate. + * + * Processing of a chain works in the following way: + * + * -- The error flag is set to a non-zero value when validation is + * finished. The value is either BR_ERR_X509_OK (validation is + * successful) or another non-zero error code. When a non-zero error + * code is obtained, the remaining bytes in the current certificate and + * the subsequent certificates (if any) are completely ignored. + * + * -- Each certificate is decoded in due course, with the following + * "interesting points": + * + * -- Start of the TBS: the multihash engine is reset and activated. + * + * -- Start of the issuer DN: the secondary hash engine is started, + * to process the encoded issuer DN. + * + * -- End of the issuer DN: the secondary hash engine is stopped. The + * resulting hash value is computed and then copied into the + * next_dn_hash[] buffer. + * + * -- Start of the subject DN: the secondary hash engine is started, + * to process the encoded subject DN. + * + * -- For the EE certificate only: the Common Name, if any, is matched + * against the expected server name. + * + * -- End of the subject DN: the secondary hash engine is stopped. The + * resulting hash value is computed into the pad. It is then processed: + * + * -- If this is the EE certificate, then the hash is ignored + * (except for direct trust processing, see later; the hash is + * simply left in current_dn_hash[]). + * + * -- Otherwise, the hashed subject DN is compared with the saved + * hash value (in saved_dn_hash[]). They must match. + * + * Either way, the next_dn_hash[] value is then copied into the + * saved_dn_hash[] value. Thus, at that point, saved_dn_hash[] + * contains the hash of the issuer DN for the current certificate, + * and current_dn_hash[] contains the hash of the subject DN for the + * current certificate. + * + * -- Public key: it is decoded into the cert_pkey[] buffer. Unknown + * key types are reported at that point. + * + * -- If this is the EE certificate, then the key type is compared + * with the expected key type (initialization parameter). The public + * key data is copied to ee_pkey_data[]. The key and hashed subject + * DN are also compared with the "direct trust" keys; if the key + * and DN are matched, then validation ends with a success. + * + * -- Otherwise, the saved signature (cert_sig[]) is verified + * against the saved TBS hash (tbs_hash[]) and that freshly + * decoded public key. Failure here ends validation with an error. + * + * -- Extensions: extension values are processed in due order. + * + * -- Basic Constraints: for all certificates except EE, must be + * present, indicate a CA, and have a path legnth compatible with + * the chain length so far. + * + * -- Key Usage: for the EE, if present, must allow signatures + * or encryption/key exchange, as required for the cipher suite. + * For non-EE, if present, must have the "certificate sign" bit. + * + * -- Subject Alt Name: for the EE, dNSName names are matched + * against the server name. Ignored for non-EE. + * + * -- Authority Key Identifier, Subject Key Identifier, Issuer + * Alt Name, Subject Directory Attributes, CRL Distribution Points + * Freshest CRL, Authority Info Access and Subject Info Access + * extensions are always ignored: they either contain only + * informative data, or they relate to revocation processing, which + * we explicitly do not support. + * + * -- All other extensions are ignored if non-critical. If a + * critical extension other than the ones above is encountered, + * then a failure is reported. + * + * -- End of the TBS: the multihash engine is stopped. + * + * -- Signature algorithm: the signature algorithm on the + * certificate is decoded. A failure is reported if that algorithm + * is unknown. The hashed TBS corresponding to the signature hash + * function is computed and stored in tbs_hash[] (if not supported, + * then a failure is reported). The hash OID and length are stored + * in cert_sig_hash_oid and cert_sig_hash_len. + * + * -- Signature value: the signature value is copied into the + * cert_sig[] array. + * + * -- Certificate end: the hashed issuer DN (saved_dn_hash[]) is + * looked up in the trust store (CA trust anchors only); for all + * that match, the signature (cert_sig[]) is verified against the + * anchor public key (hashed TBS is in tbs_hash[]). If one of these + * signatures is valid, then validation ends with a success. + * + * -- If the chain end is reached without obtaining a validation success, + * then validation is reported as failed. + */ + +#if BR_USE_UNIX_TIME +#include +#endif + +#if BR_USE_WIN32_TIME +#include +#endif + +/* + * The T0 compiler will produce these prototypes declarations in the + * header. + * +void br_x509_minimal_init_main(void *ctx); +void br_x509_minimal_run(void *ctx); + */ + +/* see bearssl_x509.h */ +void +br_x509_minimal_init(br_x509_minimal_context *ctx, + const br_hash_class *dn_hash_impl, + const br_x509_trust_anchor *trust_anchors, size_t trust_anchors_num) +{ + memset(ctx, 0, sizeof *ctx); + ctx->vtable = &br_x509_minimal_vtable; + ctx->dn_hash_impl = dn_hash_impl; + ctx->trust_anchors = trust_anchors; + ctx->trust_anchors_num = trust_anchors_num; +} + +static void +xm_start_chain(const br_x509_class **ctx, const char *server_name) +{ + br_x509_minimal_context *cc; + size_t u; + + cc = (br_x509_minimal_context *)(void *)ctx; + for (u = 0; u < cc->num_name_elts; u ++) { + cc->name_elts[u].status = 0; + cc->name_elts[u].buf[0] = 0; + } + memset(&cc->pkey, 0, sizeof cc->pkey); + cc->num_certs = 0; + cc->err = 0; + cc->cpu.dp = cc->dp_stack; + cc->cpu.rp = cc->rp_stack; + br_x509_minimal_init_main(&cc->cpu); + if (server_name == NULL || *server_name == 0) { + cc->server_name = NULL; + } else { + cc->server_name = server_name; + } +} + +static void +xm_start_cert(const br_x509_class **ctx, uint32_t length) +{ + br_x509_minimal_context *cc; + + cc = (br_x509_minimal_context *)(void *)ctx; + if (cc->err != 0) { + return; + } + if (length == 0) { + cc->err = BR_ERR_X509_TRUNCATED; + return; + } + cc->cert_length = length; +} + +static void +xm_append(const br_x509_class **ctx, const unsigned char *buf, size_t len) +{ + br_x509_minimal_context *cc; + + cc = (br_x509_minimal_context *)(void *)ctx; + if (cc->err != 0) { + return; + } + cc->hbuf = buf; + cc->hlen = len; + br_x509_minimal_run(&cc->cpu); +} + +static void +xm_end_cert(const br_x509_class **ctx) +{ + br_x509_minimal_context *cc; + + cc = (br_x509_minimal_context *)(void *)ctx; + if (cc->err == 0 && cc->cert_length != 0) { + cc->err = BR_ERR_X509_TRUNCATED; + } + cc->num_certs ++; +} + +static unsigned +xm_end_chain(const br_x509_class **ctx) +{ + br_x509_minimal_context *cc; + + cc = (br_x509_minimal_context *)(void *)ctx; + if (cc->err == 0) { + if (cc->num_certs == 0) { + cc->err = BR_ERR_X509_EMPTY_CHAIN; + } else { + cc->err = BR_ERR_X509_NOT_TRUSTED; + } + } else if (cc->err == BR_ERR_X509_OK) { + return 0; + } + return (unsigned)cc->err; +} + +static const br_x509_pkey * +xm_get_pkey(const br_x509_class *const *ctx, unsigned *usages) +{ + br_x509_minimal_context *cc; + + cc = (br_x509_minimal_context *)(void *)ctx; + if (cc->err == BR_ERR_X509_OK + || cc->err == BR_ERR_X509_NOT_TRUSTED) + { + if (usages != NULL) { + *usages = cc->key_usages; + } + return &((br_x509_minimal_context *)(void *)ctx)->pkey; + } else { + return NULL; + } +} + +/* see bearssl_x509.h */ +const br_x509_class br_x509_minimal_vtable PROGMEM = { + sizeof(br_x509_minimal_context), + xm_start_chain, + xm_start_cert, + xm_append, + xm_end_cert, + xm_end_chain, + xm_get_pkey +}; + +#define CTX ((br_x509_minimal_context *)(void *)((unsigned char *)t0ctx - offsetof(br_x509_minimal_context, cpu))) +#define CONTEXT_NAME br_x509_minimal_context + +#define DNHASH_LEN ((CTX->dn_hash_impl->desc >> BR_HASHDESC_OUT_OFF) & BR_HASHDESC_OUT_MASK) +#define dnhash_len ((ctx->dn_hash_impl->desc >> BR_HASHDESC_OUT_OFF) & BR_HASHDESC_OUT_MASK) + +/* + * Hash a DN (from a trust anchor) into the provided buffer. This uses the + * DN hash implementation and context structure from the X.509 engine + * context. + */ +static void +hash_dn(br_x509_minimal_context *ctx, const void *dn, size_t len, + unsigned char *out) +{ + ctx->dn_hash_impl->init(&ctx->dn_hash.vtable); + ctx->dn_hash_impl->update(&ctx->dn_hash.vtable, dn, len); + ctx->dn_hash_impl->out(&ctx->dn_hash.vtable, out); +} + +/* + * Compare two big integers for equality. The integers use unsigned big-endian + * encoding; extra leading bytes (of value 0) are allowed. + */ +static int +eqbigint(const unsigned char *b1, size_t len1, + const unsigned char *b2, size_t len2) +{ + while (len1 > 0 && *b1 == 0) { + b1 ++; + len1 --; + } + while (len2 > 0 && pgm_read_byte(b2) == 0) { + b2 ++; + len2 --; + } + if (len1 != len2) { + return 0; + } + return memcmp_P(b1, b2, len1) == 0; +} + +/* + * Compare two strings for equality, in a case-insensitive way. This + * function handles casing only for ASCII letters. + */ +static int +eqnocase(const void *s1, const void *s2, size_t len) +{ + const unsigned char *buf1, *buf2; + + buf1 = s1; + buf2 = s2; + while (len -- > 0) { + int x1, x2; + + x1 = *buf1 ++; + x2 = *buf2 ++; + if (x1 >= 'A' && x1 <= 'Z') { + x1 += 'a' - 'A'; + } + if (x2 >= 'A' && x2 <= 'Z') { + x2 += 'a' - 'A'; + } + if (x1 != x2) { + return 0; + } + } + return 1; +} + +static int verify_signature(br_x509_minimal_context *ctx, + const br_x509_pkey *pk); + +/* + * Check whether the current certificate (EE) is directly trusted against + * a single trust anchor + */ +static int check_single_direct_trust(br_x509_minimal_context *ctx, + unsigned char hashed_DN[64], + const br_x509_trust_anchor *ta) +{ + int kt; + + if (ta->flags & BR_X509_TA_CA) { + return 0; + } + if (memcmp(hashed_DN, ctx->current_dn_hash, dnhash_len)) { + return 0; + } + kt = ctx->pkey.key_type; + if ((pgm_read_byte(&ta->pkey.key_type) & 0x0F) != kt) { + return 0; + } + switch (kt) { + case BR_KEYTYPE_RSA: + if (!eqbigint(ctx->pkey.key.rsa.n, + ctx->pkey.key.rsa.nlen, + ta->pkey.key.rsa.n, + ta->pkey.key.rsa.nlen) + || !eqbigint(ctx->pkey.key.rsa.e, + ctx->pkey.key.rsa.elen, + ta->pkey.key.rsa.e, + ta->pkey.key.rsa.elen)) + { + return 0; + } + return 1; + case BR_KEYTYPE_EC: + if (ctx->pkey.key.ec.curve != ta->pkey.key.ec.curve + || ctx->pkey.key.ec.qlen != ta->pkey.key.ec.qlen + || memcmp_P(ctx->pkey.key.ec.q, + ta->pkey.key.ec.q, + ta->pkey.key.ec.qlen) != 0) + { + return 0; + } + return 1; + default: + return 0; + } + return 0; /* Should not get here */ +} + + +/* + * Check whether the current certificate (EE) is directly trusted against + * a single CA trust anchor. We use the issuer hash (in saved_dn_hash[]) + * as the CA identifier. + */ +static int check_single_trust_anchor_CA(br_x509_minimal_context *ctx, + unsigned char hashed_DN[64], + const br_x509_trust_anchor *ta) +{ + if (!(ta->flags & BR_X509_TA_CA)) { + return 0; + } + if (memcmp(hashed_DN, ctx->saved_dn_hash, dnhash_len)) { + return 0; + } + if (verify_signature(ctx, &ta->pkey) == 0) { + return 1; + } + return 0; +} + + + + +static const unsigned char t0_datablock[] PROGMEM = { + + 0x00, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x09, + 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x05, 0x09, 0x2A, 0x86, + 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0E, 0x09, 0x2A, 0x86, 0x48, 0x86, + 0xF7, 0x0D, 0x01, 0x01, 0x0B, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, + 0x01, 0x01, 0x0C, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, + 0x0D, 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A, 0x09, 0x60, 0x86, 0x48, 0x01, + 0x65, 0x03, 0x04, 0x02, 0x04, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, + 0x04, 0x02, 0x01, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, + 0x02, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x07, + 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, 0x08, 0x2A, 0x86, 0x48, 0xCE, + 0x3D, 0x03, 0x01, 0x07, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x22, 0x05, 0x2B, + 0x81, 0x04, 0x00, 0x23, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x01, + 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x01, 0x08, 0x2A, 0x86, + 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x02, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, + 0x04, 0x03, 0x03, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x04, + 0x03, 0x55, 0x04, 0x03, 0x00, 0x1F, 0x03, 0xFC, 0x07, 0x7F, 0x0B, 0x5E, + 0x0F, 0x1F, 0x12, 0xFE, 0x16, 0xBF, 0x1A, 0x9F, 0x1E, 0x7E, 0x22, 0x3F, + 0x26, 0x1E, 0x29, 0xDF, 0x00, 0x1F, 0x03, 0xFD, 0x07, 0x9F, 0x0B, 0x7E, + 0x0F, 0x3F, 0x13, 0x1E, 0x16, 0xDF, 0x1A, 0xBF, 0x1E, 0x9E, 0x22, 0x5F, + 0x26, 0x3E, 0x29, 0xFF, 0x03, 0x55, 0x1D, 0x13, 0x03, 0x55, 0x1D, 0x0F, + 0x03, 0x55, 0x1D, 0x11, 0x03, 0x55, 0x1D, 0x20, 0x08, 0x2B, 0x06, 0x01, + 0x05, 0x05, 0x07, 0x02, 0x01, 0x03, 0x55, 0x1D, 0x23, 0x03, 0x55, 0x1D, + 0x0E, 0x03, 0x55, 0x1D, 0x12, 0x03, 0x55, 0x1D, 0x09, 0x03, 0x55, 0x1D, + 0x1F, 0x03, 0x55, 0x1D, 0x2E, 0x08, 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, + 0x01, 0x01, 0x08, 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x0B +}; + +static const unsigned char t0_codeblock[] PROGMEM = { + + 0x00, 0x01, 0x00, 0x0D, 0x00, 0x00, 0x01, 0x00, 0x10, 0x00, 0x00, 0x01, + 0x00, 0x11, 0x00, 0x00, 0x01, 0x01, 0x09, 0x00, 0x00, 0x01, 0x01, 0x0A, + 0x00, 0x00, 0x24, 0x24, 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_BAD_BOOLEAN), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_BAD_DN), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_BAD_SERVER_NAME), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_BAD_TAG_CLASS), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_BAD_TAG_VALUE), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_BAD_TIME), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_CRITICAL_EXTENSION), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_DN_MISMATCH), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_EXPIRED), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_EXTRA_ELEMENT), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_FORBIDDEN_KEY_USAGE), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_INDEFINITE_LENGTH), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_INNER_TRUNC), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_LIMIT_EXCEEDED), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_NOT_CA), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_NOT_CONSTRUCTED), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_NOT_PRIMITIVE), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_OVERFLOW), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_PARTIAL_BYTE), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_UNEXPECTED), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_UNSUPPORTED), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_WEAK_PUBLIC_KEY), 0x00, 0x00, 0x01, + T0_INT1(BR_KEYTYPE_EC), 0x00, 0x00, 0x01, T0_INT1(BR_KEYTYPE_RSA), + 0x00, 0x00, 0x01, T0_INT2(offsetof(CONTEXT_NAME, cert_length)), 0x00, + 0x00, 0x01, T0_INT2(offsetof(CONTEXT_NAME, cert_sig)), 0x00, 0x00, + 0x01, T0_INT2(offsetof(CONTEXT_NAME, cert_sig_hash_len)), 0x00, 0x00, + 0x01, T0_INT2(offsetof(CONTEXT_NAME, cert_sig_hash_oid)), 0x00, 0x00, + 0x01, T0_INT2(offsetof(CONTEXT_NAME, cert_sig_len)), 0x00, 0x00, 0x01, + T0_INT2(offsetof(CONTEXT_NAME, cert_signer_key_type)), 0x00, 0x00, + 0x01, T0_INT2(offsetof(CONTEXT_NAME, current_dn_hash)), 0x00, 0x00, + 0x01, T0_INT2(offsetof(CONTEXT_NAME, key_usages)), 0x00, 0x00, 0x01, + T0_INT2(offsetof(br_x509_minimal_context, pkey_data)), 0x01, + T0_INT2(BR_X509_BUFSIZE_KEY), 0x00, 0x00, 0x01, + T0_INT2(offsetof(CONTEXT_NAME, min_rsa_size)), 0x00, 0x00, 0x01, + T0_INT2(offsetof(CONTEXT_NAME, next_dn_hash)), 0x00, 0x00, 0x01, + T0_INT2(offsetof(CONTEXT_NAME, num_certs)), 0x00, 0x00, 0x01, + T0_INT2(offsetof(CONTEXT_NAME, pad)), 0x00, 0x00, 0x01, + T0_INT2(offsetof(CONTEXT_NAME, saved_dn_hash)), 0x00, 0x00, 0xC9, 0x71, + 0x00, 0x00, 0x01, 0x80, 0x73, 0x00, 0x00, 0x01, 0x80, 0x7C, 0x00, 0x00, + 0x01, 0x81, 0x02, 0x00, 0x00, 0x92, 0x05, 0x05, 0x34, 0x42, 0x01, 0x00, + 0x00, 0x34, 0x01, 0x0A, 0x0E, 0x09, 0x01, 0x9A, 0xFF, 0xB8, 0x00, 0x0A, + 0x00, 0x00, 0x01, 0x82, 0x19, 0x00, 0x00, 0x01, 0x82, 0x01, 0x00, 0x00, + 0x01, 0x81, 0x68, 0x00, 0x04, 0x03, 0x00, 0x03, 0x01, 0x03, 0x02, 0x03, + 0x03, 0x02, 0x03, 0x02, 0x01, 0x11, 0x06, 0x07, 0x02, 0x02, 0x02, 0x00, + 0x0D, 0x04, 0x05, 0x02, 0x03, 0x02, 0x01, 0x0D, 0x00, 0x02, 0x03, 0x00, + 0x03, 0x01, 0x25, 0x02, 0x01, 0x13, 0x3B, 0x02, 0x00, 0x0F, 0x15, 0x00, + 0x00, 0x01, 0x81, 0x74, 0x00, 0x00, 0x05, 0x02, 0x52, 0x28, 0x00, 0x00, + 0x06, 0x02, 0x53, 0x28, 0x00, 0x00, 0x01, 0x10, 0x77, 0x00, 0x00, 0x11, + 0x05, 0x02, 0x56, 0x28, 0x74, 0x00, 0x00, 0x11, 0x05, 0x02, 0x56, 0x28, + 0x75, 0x00, 0x00, 0x06, 0x02, 0x4C, 0x28, 0x00, 0x00, 0x01, 0x82, 0x11, + 0x00, 0x00, 0x25, 0x20, 0x01, 0x08, 0x0E, 0x3B, 0x40, 0x20, 0x09, 0x00, + 0x09, 0x03, 0x00, 0x5B, 0x2B, 0xAF, 0x39, 0xAF, 0xB3, 0x25, 0x01, 0x20, + 0x11, 0x06, 0x11, 0x24, 0x74, 0xAD, 0xB3, 0x01, 0x02, 0x78, 0xB0, 0x01, + 0x02, 0x12, 0x06, 0x02, 0x57, 0x28, 0x79, 0xB3, 0x01, 0x02, 0x78, 0xAE, + 0xAF, 0xC2, 0x9C, 0x65, 0x61, 0x21, 0x16, 0xAF, 0xA7, 0x29, 0x69, 0x06, + 0x02, 0x4B, 0x28, 0xA7, 0x29, 0x71, 0x06, 0x02, 0x4B, 0x28, 0x79, 0x02, + 0x00, 0x06, 0x05, 0x9D, 0x03, 0x01, 0x04, 0x09, 0x9C, 0x61, 0x68, 0x21, + 0x27, 0x05, 0x02, 0x4A, 0x28, 0x68, 0x65, 0x21, 0x16, 0xAF, 0xAF, 0x9E, + 0x05, 0x02, 0x57, 0x28, 0xBC, 0x26, 0x06, 0x27, 0xC2, 0xA4, 0xAF, 0x63, + 0xAA, 0x03, 0x03, 0x63, 0x3B, 0x02, 0x03, 0x09, 0x3B, 0x02, 0x03, 0x0A, + 0xAA, 0x03, 0x04, 0x79, 0x64, 0x2A, 0x01, 0x81, 0x00, 0x09, 0x02, 0x03, + 0x12, 0x06, 0x02, 0x58, 0x28, 0x79, 0x5A, 0x03, 0x02, 0x04, 0x3A, 0x88, + 0x26, 0x06, 0x34, 0x9E, 0x05, 0x02, 0x57, 0x28, 0x6A, 0x26, 0x06, 0x04, + 0x01, 0x17, 0x04, 0x12, 0x6B, 0x26, 0x06, 0x04, 0x01, 0x18, 0x04, 0x0A, + 0x6C, 0x26, 0x06, 0x04, 0x01, 0x19, 0x04, 0x02, 0x57, 0x28, 0x03, 0x05, + 0x79, 0xA4, 0x25, 0x03, 0x06, 0x25, 0x63, 0x34, 0x0D, 0x06, 0x02, 0x50, + 0x28, 0xA5, 0x59, 0x03, 0x02, 0x04, 0x02, 0x57, 0x28, 0x79, 0x02, 0x00, + 0x06, 0x21, 0x02, 0x02, 0x5A, 0x30, 0x11, 0x06, 0x08, 0x24, 0x02, 0x03, + 0x02, 0x04, 0x1D, 0x04, 0x10, 0x59, 0x30, 0x11, 0x06, 0x08, 0x24, 0x02, + 0x05, 0x02, 0x06, 0x1C, 0x04, 0x03, 0x57, 0x28, 0x24, 0x04, 0x24, 0x02, + 0x02, 0x5A, 0x30, 0x11, 0x06, 0x08, 0x24, 0x02, 0x03, 0x02, 0x04, 0x23, + 0x04, 0x10, 0x59, 0x30, 0x11, 0x06, 0x08, 0x24, 0x02, 0x05, 0x02, 0x06, + 0x22, 0x04, 0x03, 0x57, 0x28, 0x24, 0x25, 0x06, 0x01, 0x28, 0x24, 0x01, + 0x00, 0x03, 0x07, 0xB4, 0x01, 0x21, 0x8F, 0x01, 0x22, 0x8F, 0x25, 0x01, + 0x23, 0x11, 0x06, 0x81, 0x26, 0x24, 0x74, 0xAD, 0xAF, 0x25, 0x06, 0x81, + 0x1A, 0x01, 0x00, 0x03, 0x08, 0xAF, 0x9E, 0x24, 0xB3, 0x25, 0x01, 0x01, + 0x11, 0x06, 0x04, 0xA6, 0x03, 0x08, 0xB3, 0x01, 0x04, 0x78, 0xAD, 0x70, + 0x26, 0x06, 0x0F, 0x02, 0x00, 0x06, 0x03, 0xC3, 0x04, 0x05, 0x99, 0x01, + 0x7F, 0x03, 0x07, 0x04, 0x80, 0x6C, 0x91, 0x26, 0x06, 0x06, 0x02, 0x00, + 0x9B, 0x04, 0x80, 0x62, 0xC5, 0x26, 0x06, 0x11, 0x02, 0x00, 0x06, 0x09, + 0x01, 0x00, 0x03, 0x01, 0x98, 0x03, 0x01, 0x04, 0x01, 0xC3, 0x04, 0x80, + 0x4D, 0x73, 0x26, 0x06, 0x0A, 0x02, 0x08, 0x06, 0x03, 0x9A, 0x04, 0x01, + 0xC3, 0x04, 0x3F, 0x6F, 0x26, 0x06, 0x03, 0xC3, 0x04, 0x38, 0xC8, 0x26, + 0x06, 0x03, 0xC3, 0x04, 0x31, 0x90, 0x26, 0x06, 0x03, 0xC3, 0x04, 0x2A, + 0xC6, 0x26, 0x06, 0x03, 0xC3, 0x04, 0x23, 0x7A, 0x26, 0x06, 0x03, 0xC3, + 0x04, 0x1C, 0x85, 0x26, 0x06, 0x03, 0xC3, 0x04, 0x15, 0x6E, 0x26, 0x06, + 0x03, 0xC3, 0x04, 0x0E, 0xC7, 0x26, 0x06, 0x03, 0xC3, 0x04, 0x07, 0x02, + 0x08, 0x06, 0x02, 0x49, 0x28, 0xC3, 0x79, 0x79, 0x04, 0xFE, 0x62, 0x79, + 0x79, 0x04, 0x08, 0x01, 0x7F, 0x11, 0x05, 0x02, 0x56, 0x28, 0x24, 0x79, + 0x3A, 0x02, 0x00, 0x06, 0x08, 0x02, 0x01, 0x3C, 0x2F, 0x05, 0x02, 0x45, + 0x28, 0x02, 0x00, 0x06, 0x01, 0x17, 0x02, 0x00, 0x02, 0x07, 0x2F, 0x05, + 0x02, 0x51, 0x28, 0xB3, 0x76, 0xAD, 0x9E, 0x06, 0x80, 0x77, 0xBD, 0x26, + 0x06, 0x07, 0x01, 0x02, 0x5A, 0x8A, 0x04, 0x80, 0x5E, 0xBE, 0x26, 0x06, + 0x07, 0x01, 0x03, 0x5A, 0x8B, 0x04, 0x80, 0x53, 0xBF, 0x26, 0x06, 0x07, + 0x01, 0x04, 0x5A, 0x8C, 0x04, 0x80, 0x48, 0xC0, 0x26, 0x06, 0x06, 0x01, + 0x05, 0x5A, 0x8D, 0x04, 0x3E, 0xC1, 0x26, 0x06, 0x06, 0x01, 0x06, 0x5A, + 0x8E, 0x04, 0x34, 0x7F, 0x26, 0x06, 0x06, 0x01, 0x02, 0x59, 0x8A, 0x04, + 0x2A, 0x80, 0x26, 0x06, 0x06, 0x01, 0x03, 0x59, 0x8B, 0x04, 0x20, 0x81, + 0x26, 0x06, 0x06, 0x01, 0x04, 0x59, 0x8C, 0x04, 0x16, 0x82, 0x26, 0x06, + 0x06, 0x01, 0x05, 0x59, 0x8D, 0x04, 0x0C, 0x83, 0x26, 0x06, 0x06, 0x01, + 0x06, 0x59, 0x8E, 0x04, 0x02, 0x57, 0x28, 0x5E, 0x35, 0x60, 0x37, 0x1B, + 0x25, 0x05, 0x02, 0x57, 0x28, 0x5D, 0x37, 0x04, 0x02, 0x57, 0x28, 0xC2, + 0xA4, 0x25, 0x01, T0_INT2(BR_X509_BUFSIZE_SIG), 0x12, 0x06, 0x02, 0x50, + 0x28, 0x25, 0x5F, 0x35, 0x5C, 0xA5, 0x79, 0x79, 0x01, 0x00, 0x5B, 0x36, + 0x18, 0x00, 0x00, 0x01, 0x30, 0x0A, 0x25, 0x01, 0x00, 0x01, 0x09, 0x72, + 0x05, 0x02, 0x48, 0x28, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x01, 0x81, + 0x08, 0x00, 0x00, 0x01, 0x81, 0x10, 0x00, 0x00, 0x01, 0x81, 0x19, 0x00, + 0x00, 0x01, 0x81, 0x22, 0x00, 0x00, 0x01, 0x81, 0x2B, 0x00, 0x01, 0x7E, + 0x01, 0x01, 0x11, 0x3B, 0x01, 0x83, 0xFD, 0x7F, 0x11, 0x15, 0x06, 0x03, + 0x3B, 0x24, 0x00, 0x3B, 0x25, 0x03, 0x00, 0x25, 0xCA, 0x05, 0x04, 0x42, + 0x01, 0x00, 0x00, 0x25, 0x01, 0x81, 0x00, 0x0D, 0x06, 0x04, 0x96, 0x04, + 0x80, 0x49, 0x25, 0x01, 0x90, 0x00, 0x0D, 0x06, 0x0F, 0x01, 0x06, 0x14, + 0x01, 0x81, 0x40, 0x2F, 0x96, 0x02, 0x00, 0x01, 0x00, 0x97, 0x04, 0x33, + 0x25, 0x01, 0x83, 0xFF, 0x7F, 0x0D, 0x06, 0x14, 0x01, 0x0C, 0x14, 0x01, + 0x81, 0x60, 0x2F, 0x96, 0x02, 0x00, 0x01, 0x06, 0x97, 0x02, 0x00, 0x01, + 0x00, 0x97, 0x04, 0x17, 0x01, 0x12, 0x14, 0x01, 0x81, 0x70, 0x2F, 0x96, + 0x02, 0x00, 0x01, 0x0C, 0x97, 0x02, 0x00, 0x01, 0x06, 0x97, 0x02, 0x00, + 0x01, 0x00, 0x97, 0x00, 0x00, 0x01, 0x82, 0x15, 0x00, 0x00, 0x25, 0x01, + 0x83, 0xB0, 0x00, 0x01, 0x83, 0xB7, 0x7F, 0x72, 0x00, 0x00, 0x01, 0x81, + 0x34, 0x00, 0x00, 0x01, 0x80, 0x6B, 0x00, 0x00, 0x01, 0x81, 0x78, 0x00, + 0x00, 0x01, 0x3D, 0x00, 0x00, 0x01, 0x80, 0x43, 0x00, 0x00, 0x01, 0x80, + 0x4D, 0x00, 0x00, 0x01, 0x80, 0x57, 0x00, 0x00, 0x01, 0x80, 0x61, 0x00, + 0x00, 0x30, 0x11, 0x06, 0x04, 0x42, 0xAD, 0xC2, 0xB4, 0x00, 0x00, 0x01, + 0x82, 0x09, 0x00, 0x00, 0x01, 0x81, 0x6C, 0x00, 0x00, 0x25, 0x01, 0x83, + 0xB8, 0x00, 0x01, 0x83, 0xBF, 0x7F, 0x72, 0x00, 0x00, 0x01, 0x30, 0x62, + 0x37, 0x01, 0x7F, 0x7C, 0x19, 0x01, 0x00, 0x7C, 0x19, 0x04, 0x7A, 0x00, + 0x01, 0x81, 0x38, 0x00, 0x01, 0x7E, 0x0D, 0x06, 0x02, 0x4F, 0x28, 0x25, + 0x03, 0x00, 0x0A, 0x02, 0x00, 0x00, 0x00, 0x30, 0x25, 0x3F, 0x3B, 0x01, + 0x82, 0x00, 0x13, 0x2F, 0x06, 0x04, 0x42, 0x01, 0x00, 0x00, 0x30, 0x67, + 0x09, 0x37, 0x40, 0x00, 0x00, 0x14, 0x01, 0x3F, 0x15, 0x01, 0x81, 0x00, + 0x2F, 0x96, 0x00, 0x02, 0x01, 0x00, 0x03, 0x00, 0xAF, 0x25, 0x06, 0x80, + 0x59, 0xB3, 0x01, 0x20, 0x30, 0x11, 0x06, 0x17, 0x24, 0x74, 0xAD, 0x9E, + 0x24, 0x01, 0x7F, 0x2E, 0x03, 0x01, 0xB3, 0x01, 0x20, 0x77, 0xAD, 0xB2, + 0x02, 0x01, 0x1F, 0x79, 0x79, 0x04, 0x38, 0x01, 0x21, 0x30, 0x11, 0x06, + 0x08, 0x24, 0x75, 0xB6, 0x01, 0x01, 0x1E, 0x04, 0x2A, 0x01, 0x22, 0x30, + 0x11, 0x06, 0x11, 0x24, 0x75, 0xB6, 0x25, 0x06, 0x06, 0x2C, 0x02, 0x00, + 0x2F, 0x03, 0x00, 0x01, 0x02, 0x1E, 0x04, 0x13, 0x01, 0x26, 0x30, 0x11, + 0x06, 0x08, 0x24, 0x75, 0xB6, 0x01, 0x06, 0x1E, 0x04, 0x05, 0x42, 0xAE, + 0x01, 0x00, 0x24, 0x04, 0xFF, 0x23, 0x79, 0x02, 0x00, 0x00, 0x00, 0xAF, + 0xB4, 0x25, 0x01, 0x01, 0x11, 0x06, 0x08, 0xA6, 0x05, 0x02, 0x51, 0x28, + 0xB4, 0x04, 0x02, 0x51, 0x28, 0x25, 0x01, 0x02, 0x11, 0x06, 0x0C, 0x24, + 0x75, 0xB0, 0x66, 0x2B, 0x41, 0x0D, 0x06, 0x02, 0x51, 0x28, 0xB4, 0x01, + 0x7F, 0x10, 0x06, 0x02, 0x56, 0x28, 0x24, 0x79, 0x00, 0x00, 0xAF, 0x25, + 0x06, 0x1A, 0xAF, 0x9E, 0x24, 0x25, 0x06, 0x11, 0xAF, 0x25, 0x06, 0x0C, + 0xAF, 0x9E, 0x24, 0x89, 0x26, 0x05, 0x02, 0x49, 0x28, 0xC2, 0x04, 0x71, + 0x79, 0x79, 0x04, 0x63, 0x79, 0x00, 0x02, 0x03, 0x00, 0xB3, 0x01, 0x03, + 0x78, 0xAD, 0xBA, 0x03, 0x01, 0x02, 0x01, 0x01, 0x07, 0x12, 0x06, 0x02, + 0x56, 0x28, 0x25, 0x01, 0x00, 0x30, 0x11, 0x06, 0x05, 0x24, 0x4D, 0x28, + 0x04, 0x15, 0x01, 0x01, 0x30, 0x11, 0x06, 0x0A, 0x24, 0xBA, 0x02, 0x01, + 0x14, 0x02, 0x01, 0x0E, 0x04, 0x05, 0x24, 0xBA, 0x01, 0x00, 0x24, 0x02, + 0x00, 0x06, 0x19, 0x01, 0x00, 0x30, 0x01, 0x38, 0x15, 0x06, 0x03, 0x01, + 0x10, 0x2F, 0x3B, 0x01, 0x81, 0x40, 0x15, 0x06, 0x03, 0x01, 0x20, 0x2F, + 0x62, 0x37, 0x04, 0x07, 0x01, 0x04, 0x15, 0x05, 0x02, 0x4D, 0x28, 0xC2, + 0x00, 0x00, 0x38, 0xAF, 0xC2, 0x1A, 0x00, 0x03, 0x01, 0x00, 0x03, 0x00, + 0x38, 0xAF, 0x25, 0x06, 0x30, 0xB3, 0x01, 0x11, 0x77, 0xAD, 0x25, 0x05, + 0x02, 0x44, 0x28, 0x25, 0x06, 0x20, 0xAF, 0x9E, 0x24, 0x87, 0x26, 0x03, + 0x01, 0x01, 0x00, 0x2E, 0x03, 0x02, 0xB2, 0x25, 0x02, 0x01, 0x15, 0x06, + 0x07, 0x2C, 0x06, 0x04, 0x01, 0x7F, 0x03, 0x00, 0x02, 0x02, 0x1F, 0x79, + 0x04, 0x5D, 0x79, 0x04, 0x4D, 0x79, 0x1A, 0x02, 0x00, 0x00, 0x00, 0xB3, + 0x01, 0x06, 0x78, 0xB1, 0x00, 0x00, 0xB8, 0x86, 0x06, 0x0E, 0x3B, 0x25, + 0x05, 0x06, 0x42, 0x01, 0x00, 0x01, 0x00, 0x00, 0xB8, 0x6D, 0x04, 0x08, + 0x92, 0x06, 0x05, 0x24, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0xB9, 0x86, + 0x06, 0x0E, 0x3B, 0x25, 0x05, 0x06, 0x42, 0x01, 0x00, 0x01, 0x00, 0x00, + 0xB9, 0x6D, 0x04, 0x08, 0x92, 0x06, 0x05, 0x24, 0x01, 0x00, 0x04, 0x00, + 0x00, 0x00, 0xBA, 0x25, 0x01, 0x81, 0x00, 0x0D, 0x06, 0x04, 0x00, 0x04, + 0x80, 0x55, 0x25, 0x01, 0x81, 0x40, 0x0D, 0x06, 0x07, 0x24, 0x01, 0x00, + 0x00, 0x04, 0x80, 0x47, 0x25, 0x01, 0x81, 0x60, 0x0D, 0x06, 0x0E, 0x01, + 0x1F, 0x15, 0x01, 0x01, 0xA3, 0x01, 0x81, 0x00, 0x01, 0x8F, 0x7F, 0x04, + 0x32, 0x25, 0x01, 0x81, 0x70, 0x0D, 0x06, 0x0F, 0x01, 0x0F, 0x15, 0x01, + 0x02, 0xA3, 0x01, 0x90, 0x00, 0x01, 0x83, 0xFF, 0x7F, 0x04, 0x1C, 0x25, + 0x01, 0x81, 0x78, 0x0D, 0x06, 0x11, 0x01, 0x07, 0x15, 0x01, 0x03, 0xA3, + 0x01, 0x84, 0x80, 0x00, 0x01, 0x80, 0xC3, 0xFF, 0x7F, 0x04, 0x04, 0x24, + 0x01, 0x00, 0x00, 0x72, 0x05, 0x03, 0x24, 0x01, 0x00, 0x00, 0x00, 0x3B, + 0x25, 0x05, 0x06, 0x42, 0x01, 0x00, 0x01, 0x7F, 0x00, 0xBA, 0x34, 0x25, + 0x3D, 0x06, 0x03, 0x3B, 0x24, 0x00, 0x01, 0x06, 0x0E, 0x3B, 0x25, 0x01, + 0x06, 0x14, 0x01, 0x02, 0x10, 0x06, 0x04, 0x42, 0x01, 0x7F, 0x00, 0x01, + 0x3F, 0x15, 0x09, 0x00, 0x00, 0x25, 0x06, 0x06, 0x0B, 0xA2, 0x34, 0x41, + 0x04, 0x77, 0x24, 0x25, 0x00, 0x00, 0xB3, 0x01, 0x03, 0x78, 0xAD, 0xBA, + 0x06, 0x02, 0x55, 0x28, 0x00, 0x00, 0x3B, 0x25, 0x06, 0x07, 0x31, 0x25, + 0x06, 0x01, 0x19, 0x04, 0x76, 0x42, 0x00, 0x00, 0x01, 0x01, 0x78, 0xAC, + 0x01, 0x01, 0x10, 0x06, 0x02, 0x43, 0x28, 0xBA, 0x3E, 0x00, 0x04, 0xB3, + 0x25, 0x01, 0x17, 0x01, 0x18, 0x72, 0x05, 0x02, 0x48, 0x28, 0x01, 0x18, + 0x11, 0x03, 0x00, 0x75, 0xAD, 0xA8, 0x02, 0x00, 0x06, 0x0C, 0x01, 0x80, + 0x64, 0x08, 0x03, 0x01, 0xA8, 0x02, 0x01, 0x09, 0x04, 0x0E, 0x25, 0x01, + 0x32, 0x0D, 0x06, 0x04, 0x01, 0x80, 0x64, 0x09, 0x01, 0x8E, 0x6C, 0x09, + 0x03, 0x01, 0x02, 0x01, 0x01, 0x82, 0x6D, 0x08, 0x02, 0x01, 0x01, 0x03, + 0x09, 0x01, 0x04, 0x0C, 0x09, 0x02, 0x01, 0x01, 0x80, 0x63, 0x09, 0x01, + 0x80, 0x64, 0x0C, 0x0A, 0x02, 0x01, 0x01, 0x83, 0x0F, 0x09, 0x01, 0x83, + 0x10, 0x0C, 0x09, 0x03, 0x03, 0x01, 0x01, 0x01, 0x0C, 0xA9, 0x41, 0x01, + 0x01, 0x0E, 0x02, 0x01, 0x01, 0x04, 0x07, 0x3F, 0x02, 0x01, 0x01, 0x80, + 0x64, 0x07, 0x3E, 0x02, 0x01, 0x01, 0x83, 0x10, 0x07, 0x3F, 0x2F, 0x15, + 0x06, 0x03, 0x01, 0x18, 0x09, 0x94, 0x09, 0x7B, 0x25, 0x01, 0x05, 0x14, + 0x02, 0x03, 0x09, 0x03, 0x03, 0x01, 0x1F, 0x15, 0x01, 0x01, 0x3B, 0xA9, + 0x02, 0x03, 0x09, 0x41, 0x03, 0x03, 0x01, 0x00, 0x01, 0x17, 0xA9, 0x01, + 0x9C, 0x10, 0x08, 0x03, 0x02, 0x01, 0x00, 0x01, 0x3B, 0xA9, 0x01, 0x3C, + 0x08, 0x02, 0x02, 0x09, 0x03, 0x02, 0x01, 0x00, 0x01, 0x3C, 0xA9, 0x02, + 0x02, 0x09, 0x03, 0x02, 0xBA, 0x25, 0x01, 0x2E, 0x11, 0x06, 0x0D, 0x24, + 0xBA, 0x25, 0x01, 0x30, 0x01, 0x39, 0x72, 0x06, 0x03, 0x24, 0x04, 0x74, + 0x01, 0x80, 0x5A, 0x10, 0x06, 0x02, 0x48, 0x28, 0x79, 0x02, 0x03, 0x02, + 0x02, 0x00, 0x01, 0xBA, 0x7D, 0x01, 0x0A, 0x08, 0x03, 0x00, 0xBA, 0x7D, + 0x02, 0x00, 0x09, 0x00, 0x02, 0x03, 0x00, 0x03, 0x01, 0xA8, 0x25, 0x02, + 0x01, 0x02, 0x00, 0x72, 0x05, 0x02, 0x48, 0x28, 0x00, 0x00, 0x34, 0xB3, + 0x01, 0x02, 0x78, 0x0B, 0xAB, 0x00, 0x03, 0x25, 0x03, 0x00, 0x03, 0x01, + 0x03, 0x02, 0xAD, 0xBA, 0x25, 0x01, 0x81, 0x00, 0x13, 0x06, 0x02, 0x54, + 0x28, 0x25, 0x01, 0x00, 0x11, 0x06, 0x0B, 0x24, 0x25, 0x05, 0x04, 0x24, + 0x01, 0x00, 0x00, 0xBA, 0x04, 0x6F, 0x02, 0x01, 0x25, 0x05, 0x02, 0x50, + 0x28, 0x41, 0x03, 0x01, 0x02, 0x02, 0x37, 0x02, 0x02, 0x40, 0x03, 0x02, + 0x25, 0x06, 0x03, 0xBA, 0x04, 0x68, 0x24, 0x02, 0x00, 0x02, 0x01, 0x0A, + 0x00, 0x01, 0xBA, 0x25, 0x01, 0x81, 0x00, 0x0D, 0x06, 0x01, 0x00, 0x01, + 0x81, 0x00, 0x0A, 0x25, 0x05, 0x02, 0x4E, 0x28, 0x03, 0x00, 0x01, 0x00, + 0x02, 0x00, 0x01, 0x00, 0x12, 0x06, 0x19, 0x02, 0x00, 0x41, 0x03, 0x00, + 0x25, 0x01, 0x83, 0xFF, 0xFF, 0x7F, 0x12, 0x06, 0x02, 0x4F, 0x28, 0x01, + 0x08, 0x0E, 0x3B, 0xBA, 0x34, 0x09, 0x04, 0x60, 0x00, 0x00, 0xAC, 0x95, + 0x00, 0x00, 0xAD, 0xC2, 0x00, 0x00, 0xB3, 0x76, 0xAD, 0x00, 0x01, 0xAD, + 0x25, 0x05, 0x02, 0x54, 0x28, 0xBA, 0x25, 0x01, 0x81, 0x00, 0x13, 0x06, + 0x02, 0x54, 0x28, 0x03, 0x00, 0x25, 0x06, 0x16, 0xBA, 0x02, 0x00, 0x25, + 0x01, 0x87, 0xFF, 0xFF, 0x7F, 0x13, 0x06, 0x02, 0x54, 0x28, 0x01, 0x08, + 0x0E, 0x09, 0x03, 0x00, 0x04, 0x67, 0x24, 0x02, 0x00, 0x00, 0x00, 0xAD, + 0x25, 0x01, 0x81, 0x7F, 0x12, 0x06, 0x08, 0xC2, 0x01, 0x00, 0x67, 0x37, + 0x01, 0x00, 0x00, 0x25, 0x67, 0x37, 0x67, 0x40, 0xA5, 0x01, 0x7F, 0x00, + 0x00, 0xB3, 0x01, 0x0C, 0x30, 0x11, 0x06, 0x05, 0x24, 0x75, 0xB6, 0x04, + 0x3E, 0x01, 0x12, 0x30, 0x11, 0x06, 0x05, 0x24, 0x75, 0xB7, 0x04, 0x33, + 0x01, 0x13, 0x30, 0x11, 0x06, 0x05, 0x24, 0x75, 0xB7, 0x04, 0x28, 0x01, + 0x14, 0x30, 0x11, 0x06, 0x05, 0x24, 0x75, 0xB7, 0x04, 0x1D, 0x01, 0x16, + 0x30, 0x11, 0x06, 0x05, 0x24, 0x75, 0xB7, 0x04, 0x12, 0x01, 0x1E, 0x30, + 0x11, 0x06, 0x05, 0x24, 0x75, 0xB5, 0x04, 0x07, 0x42, 0xAE, 0x01, 0x00, + 0x01, 0x00, 0x24, 0x00, 0x01, 0xBA, 0x03, 0x00, 0x02, 0x00, 0x01, 0x05, + 0x14, 0x01, 0x01, 0x15, 0x2D, 0x02, 0x00, 0x01, 0x06, 0x14, 0x25, 0x01, + 0x01, 0x15, 0x06, 0x02, 0x46, 0x28, 0x01, 0x04, 0x0E, 0x02, 0x00, 0x01, + 0x1F, 0x15, 0x25, 0x01, 0x1F, 0x11, 0x06, 0x02, 0x47, 0x28, 0x09, 0x00, + 0x00, 0x25, 0x05, 0x05, 0x01, 0x00, 0x01, 0x7F, 0x00, 0xB3, 0x00, 0x01, + 0xAD, 0x25, 0x05, 0x05, 0x67, 0x37, 0x01, 0x7F, 0x00, 0x01, 0x01, 0x03, + 0x00, 0x9F, 0x25, 0x01, 0x83, 0xFF, 0x7E, 0x11, 0x06, 0x16, 0x24, 0x25, + 0x06, 0x10, 0xA0, 0x25, 0x05, 0x05, 0x24, 0xC2, 0x01, 0x00, 0x00, 0x02, + 0x00, 0x84, 0x03, 0x00, 0x04, 0x6D, 0x04, 0x1B, 0x25, 0x05, 0x05, 0x24, + 0xC2, 0x01, 0x00, 0x00, 0x02, 0x00, 0x84, 0x03, 0x00, 0x25, 0x06, 0x0B, + 0x9F, 0x25, 0x05, 0x05, 0x24, 0xC2, 0x01, 0x00, 0x00, 0x04, 0x6D, 0x24, + 0x02, 0x00, 0x25, 0x05, 0x01, 0x00, 0x41, 0x67, 0x37, 0x01, 0x7F, 0x00, + 0x01, 0xAD, 0x01, 0x01, 0x03, 0x00, 0x25, 0x06, 0x10, 0xA1, 0x25, 0x05, + 0x05, 0x24, 0xC2, 0x01, 0x00, 0x00, 0x02, 0x00, 0x84, 0x03, 0x00, 0x04, + 0x6D, 0x24, 0x02, 0x00, 0x25, 0x05, 0x01, 0x00, 0x41, 0x67, 0x37, 0x01, + 0x7F, 0x00, 0x01, 0xAD, 0x01, 0x01, 0x03, 0x00, 0x25, 0x06, 0x10, 0xBA, + 0x25, 0x05, 0x05, 0x24, 0xC2, 0x01, 0x00, 0x00, 0x02, 0x00, 0x84, 0x03, + 0x00, 0x04, 0x6D, 0x24, 0x02, 0x00, 0x25, 0x05, 0x01, 0x00, 0x41, 0x67, + 0x37, 0x01, 0x7F, 0x00, 0x00, 0xBA, 0x01, 0x08, 0x0E, 0x3B, 0xBA, 0x34, + 0x09, 0x00, 0x00, 0xBA, 0x3B, 0xBA, 0x01, 0x08, 0x0E, 0x34, 0x09, 0x00, + 0x00, 0x25, 0x05, 0x02, 0x4F, 0x28, 0x41, 0xBB, 0x00, 0x00, 0x32, 0x25, + 0x01, 0x00, 0x13, 0x06, 0x01, 0x00, 0x24, 0x19, 0x04, 0x74, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x01, 0x0B, 0x00, 0x00, 0x01, 0x15, 0x00, 0x00, 0x01, + 0x1F, 0x00, 0x00, 0x01, 0x29, 0x00, 0x00, 0x01, 0x33, 0x00, 0x00, 0xC3, + 0x24, 0x00, 0x00, 0x25, 0x06, 0x07, 0xC4, 0x25, 0x06, 0x01, 0x19, 0x04, + 0x76, 0x00, 0x00, 0x01, 0x00, 0x30, 0x31, 0x0B, 0x42, 0x00, 0x00, 0x01, + 0x81, 0x70, 0x00, 0x00, 0x01, 0x82, 0x0D, 0x00, 0x00, 0x01, 0x82, 0x22, + 0x00, 0x00, 0x01, 0x82, 0x05, 0x00, 0x00, 0x01, 0x03, 0x33, 0x01, 0x03, + 0x33, 0x00, 0x00, 0x25, 0x01, 0x83, 0xFB, 0x50, 0x01, 0x83, 0xFD, 0x5F, + 0x72, 0x06, 0x04, 0x24, 0x01, 0x00, 0x00, 0x25, 0x01, 0x83, 0xB0, 0x00, + 0x01, 0x83, 0xBF, 0x7F, 0x72, 0x06, 0x04, 0x24, 0x01, 0x00, 0x00, 0x01, + 0x83, 0xFF, 0x7F, 0x15, 0x01, 0x83, 0xFF, 0x7E, 0x0D, 0x00 +}; + +static const uint16_t t0_caddr[] PROGMEM = { + + 0, + 5, + 10, + 15, + 20, + 25, + 29, + 33, + 37, + 41, + 45, + 49, + 53, + 57, + 61, + 65, + 69, + 73, + 77, + 81, + 85, + 89, + 93, + 97, + 101, + 105, + 109, + 113, + 117, + 121, + 125, + 130, + 135, + 140, + 145, + 150, + 155, + 160, + 165, + 173, + 178, + 183, + 188, + 193, + 198, + 202, + 207, + 212, + 217, + 238, + 243, + 248, + 253, + 282, + 297, + 302, + 308, + 314, + 319, + 327, + 335, + 341, + 346, + 357, + 992, + 1007, + 1011, + 1016, + 1021, + 1026, + 1031, + 1036, + 1150, + 1155, + 1167, + 1172, + 1177, + 1182, + 1186, + 1191, + 1196, + 1201, + 1206, + 1216, + 1221, + 1226, + 1238, + 1253, + 1258, + 1272, + 1294, + 1305, + 1408, + 1455, + 1488, + 1579, + 1585, + 1648, + 1655, + 1683, + 1711, + 1816, + 1858, + 1871, + 1883, + 1897, + 1912, + 2132, + 2146, + 2163, + 2172, + 2239, + 2295, + 2299, + 2303, + 2308, + 2356, + 2382, + 2458, + 2502, + 2513, + 2598, + 2636, + 2674, + 2684, + 2694, + 2703, + 2716, + 2720, + 2724, + 2728, + 2732, + 2736, + 2740, + 2744, + 2756, + 2764, + 2769, + 2774, + 2779, + 2784, + 2792 +}; + +#define T0_INTERPRETED 61 + +#define T0_ENTER(ip, rp, slot) do { \ + const unsigned char *t0_newip; \ + uint32_t t0_lnum; \ + t0_newip = &t0_codeblock[pgm_read_word(&t0_caddr[(slot) - T0_INTERPRETED])]; \ + t0_lnum = t0_parse7E_unsigned(&t0_newip); \ + (rp) += t0_lnum; \ + *((rp) ++) = (uint32_t)((ip) - &t0_codeblock[0]) + (t0_lnum << 16); \ + (ip) = t0_newip; \ + } while (0) + +#define T0_DEFENTRY(name, slot) \ +void \ +name(void *ctx) \ +{ \ + t0_context *t0ctx = ctx; \ + t0ctx->ip = &t0_codeblock[0]; \ + T0_ENTER(t0ctx->ip, t0ctx->rp, slot); \ +} + +T0_DEFENTRY(br_x509_minimal_init_main, 147) + +#define T0_NEXT(t0ipp) (pgm_read_byte((*t0ipp)++)) + +void +br_x509_minimal_run(void *t0ctx) +{ + uint32_t *dp, *rp; + const unsigned char *ip; + +#define T0_LOCAL(x) (*(rp - 2 - (x))) +#define T0_POP() (*-- dp) +#define T0_POPi() (*(int32_t *)(-- dp)) +#define T0_PEEK(x) (*(dp - 1 - (x))) +#define T0_PEEKi(x) (*(int32_t *)(dp - 1 - (x))) +#define T0_PUSH(v) do { *dp = (v); dp ++; } while (0) +#define T0_PUSHi(v) do { *(int32_t *)dp = (v); dp ++; } while (0) +#define T0_RPOP() (*-- rp) +#define T0_RPOPi() (*(int32_t *)(-- rp)) +#define T0_RPUSH(v) do { *rp = (v); rp ++; } while (0) +#define T0_RPUSHi(v) do { *(int32_t *)rp = (v); rp ++; } while (0) +#define T0_ROLL(x) do { \ + size_t t0len = (size_t)(x); \ + uint32_t t0tmp = *(dp - 1 - t0len); \ + memmove(dp - t0len - 1, dp - t0len, t0len * sizeof *dp); \ + *(dp - 1) = t0tmp; \ +} while (0) +#define T0_SWAP() do { \ + uint32_t t0tmp = *(dp - 2); \ + *(dp - 2) = *(dp - 1); \ + *(dp - 1) = t0tmp; \ +} while (0) +#define T0_ROT() do { \ + uint32_t t0tmp = *(dp - 3); \ + *(dp - 3) = *(dp - 2); \ + *(dp - 2) = *(dp - 1); \ + *(dp - 1) = t0tmp; \ +} while (0) +#define T0_NROT() do { \ + uint32_t t0tmp = *(dp - 1); \ + *(dp - 1) = *(dp - 2); \ + *(dp - 2) = *(dp - 3); \ + *(dp - 3) = t0tmp; \ +} while (0) +#define T0_PICK(x) do { \ + uint32_t t0depth = (x); \ + T0_PUSH(T0_PEEK(t0depth)); \ +} while (0) +#define T0_CO() do { \ + goto t0_exit; \ +} while (0) +#define T0_RET() goto t0_next + + dp = ((t0_context *)t0ctx)->dp; + rp = ((t0_context *)t0ctx)->rp; + ip = ((t0_context *)t0ctx)->ip; + goto t0_next; + for (;;) { + uint32_t t0x; + + t0_next: + t0x = T0_NEXT(&ip); + if (t0x < T0_INTERPRETED) { + switch (t0x) { + int32_t t0off; + + case 0: /* ret */ + t0x = T0_RPOP(); + rp -= (t0x >> 16); + t0x &= 0xFFFF; + if (t0x == 0) { + ip = NULL; + goto t0_exit; + } + ip = &t0_codeblock[t0x]; + break; + case 1: /* literal constant */ + T0_PUSHi(t0_parse7E_signed(&ip)); + break; + case 2: /* read local */ + T0_PUSH(T0_LOCAL(t0_parse7E_unsigned(&ip))); + break; + case 3: /* write local */ + T0_LOCAL(t0_parse7E_unsigned(&ip)) = T0_POP(); + break; + case 4: /* jump */ + t0off = t0_parse7E_signed(&ip); + ip += t0off; + break; + case 5: /* jump if */ + t0off = t0_parse7E_signed(&ip); + if (T0_POP()) { + ip += t0off; + } + break; + case 6: /* jump if not */ + t0off = t0_parse7E_signed(&ip); + if (!T0_POP()) { + ip += t0off; + } + break; + case 7: { + /* %25 */ + + int32_t b = T0_POPi(); + int32_t a = T0_POPi(); + T0_PUSHi(a % b); + + } + break; + case 8: { + /* * */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(a * b); + + } + break; + case 9: { + /* + */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(a + b); + + } + break; + case 10: { + /* - */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(a - b); + + } + break; + case 11: { + /* -rot */ + T0_NROT(); + } + break; + case 12: { + /* / */ + + int32_t b = T0_POPi(); + int32_t a = T0_POPi(); + T0_PUSHi(a / b); + + } + break; + case 13: { + /* < */ + + int32_t b = T0_POPi(); + int32_t a = T0_POPi(); + T0_PUSH(-(uint32_t)(a < b)); + + } + break; + case 14: { + /* << */ + + int c = (int)T0_POPi(); + uint32_t x = T0_POP(); + T0_PUSH(x << c); + + } + break; + case 15: { + /* <= */ + + int32_t b = T0_POPi(); + int32_t a = T0_POPi(); + T0_PUSH(-(uint32_t)(a <= b)); + + } + break; + case 16: { + /* <> */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(-(uint32_t)(a != b)); + + } + break; + case 17: { + /* = */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(-(uint32_t)(a == b)); + + } + break; + case 18: { + /* > */ + + int32_t b = T0_POPi(); + int32_t a = T0_POPi(); + T0_PUSH(-(uint32_t)(a > b)); + + } + break; + case 19: { + /* >= */ + + int32_t b = T0_POPi(); + int32_t a = T0_POPi(); + T0_PUSH(-(uint32_t)(a >= b)); + + } + break; + case 20: { + /* >> */ + + int c = (int)T0_POPi(); + int32_t x = T0_POPi(); + T0_PUSHi(x >> c); + + } + break; + case 21: { + /* and */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(a & b); + + } + break; + case 22: { + /* blobcopy */ + + size_t len = T0_POP(); + unsigned char *src = (unsigned char *)CTX + T0_POP(); + unsigned char *dst = (unsigned char *)CTX + T0_POP(); + memcpy(dst, src, len); + + } + break; + case 23: { + /* check-direct-trust */ + + size_t u; + const br_x509_trust_anchor *ta; + unsigned char hashed_DN[64]; + + for (u = 0; u < CTX->trust_anchors_num; u ++) { + ta = &CTX->trust_anchors[u]; + hash_dn(CTX, ta->dn.data, ta->dn.len, hashed_DN); + if (check_single_direct_trust(CTX, hashed_DN, ta)) { + /* + * Direct trust match! + */ + CTX->err = BR_ERR_X509_OK; + T0_CO(); + } + } + if (CTX->err != BR_ERR_X509_OK && CTX->trust_anchor_dynamic) { + ta = CTX->trust_anchor_dynamic(CTX->trust_anchor_dynamic_ctx, CTX->current_dn_hash, DNHASH_LEN); + if (ta) { + memcpy(hashed_DN, ta->dn.data, DNHASH_LEN); + int ret = check_single_direct_trust(CTX, hashed_DN, ta); + if (CTX->trust_anchor_dynamic_free) { + CTX->trust_anchor_dynamic_free(CTX->trust_anchor_dynamic_ctx, ta); + } + if (ret) { + /* + * Direct trust match! + */ + CTX->err = BR_ERR_X509_OK; + T0_CO(); + } + } + } + + } + break; + case 24: { + /* check-trust-anchor-CA */ + + size_t u; + const br_x509_trust_anchor *ta; + unsigned char hashed_DN[64]; + + for (u = 0; u < CTX->trust_anchors_num; u ++) { + ta = &CTX->trust_anchors[u]; + hash_dn(CTX, ta->dn.data, ta->dn.len, hashed_DN); + if (check_single_trust_anchor_CA(CTX, hashed_DN, ta)) { + CTX->err = BR_ERR_X509_OK; + T0_CO(); + } + } + if (CTX->err != BR_ERR_X509_OK && CTX->trust_anchor_dynamic) { + ta = CTX->trust_anchor_dynamic(CTX->trust_anchor_dynamic_ctx, CTX->saved_dn_hash, DNHASH_LEN); + if (ta) { + memcpy(hashed_DN, ta->dn.data, DNHASH_LEN); + int ret; + ret = check_single_trust_anchor_CA(CTX, hashed_DN, ta); + if (CTX->trust_anchor_dynamic_free) { + CTX->trust_anchor_dynamic_free(CTX->trust_anchor_dynamic_ctx, ta); + } + if (ret) { + CTX->err = BR_ERR_X509_OK; + T0_CO(); + } + } + + } + + } + break; + case 25: { + /* co */ + T0_CO(); + } + break; + case 26: { + /* compute-dn-hash */ + + CTX->dn_hash_impl->out(&CTX->dn_hash.vtable, CTX->current_dn_hash); + CTX->do_dn_hash = 0; + + } + break; + case 27: { + /* compute-tbs-hash */ + + int id = T0_POPi(); + size_t len; + len = br_multihash_out(&CTX->mhash, id, CTX->tbs_hash); + T0_PUSH(len); + + } + break; + case 28: { + /* copy-ee-ec-pkey */ + + size_t qlen = T0_POP(); + uint32_t curve = T0_POP(); + memcpy(CTX->ee_pkey_data, CTX->pkey_data, qlen); + CTX->pkey.key_type = BR_KEYTYPE_EC; + CTX->pkey.key.ec.curve = curve; + CTX->pkey.key.ec.q = CTX->ee_pkey_data; + CTX->pkey.key.ec.qlen = qlen; + + } + break; + case 29: { + /* copy-ee-rsa-pkey */ + + size_t elen = T0_POP(); + size_t nlen = T0_POP(); + memcpy(CTX->ee_pkey_data, CTX->pkey_data, nlen + elen); + CTX->pkey.key_type = BR_KEYTYPE_RSA; + CTX->pkey.key.rsa.n = CTX->ee_pkey_data; + CTX->pkey.key.rsa.nlen = nlen; + CTX->pkey.key.rsa.e = CTX->ee_pkey_data + nlen; + CTX->pkey.key.rsa.elen = elen; + + } + break; + case 30: { + /* copy-name-SAN */ + + unsigned tag = T0_POP(); + unsigned ok = T0_POP(); + size_t u, len; + + len = CTX->pad[0]; + for (u = 0; u < CTX->num_name_elts; u ++) { + br_name_element *ne; + + ne = &CTX->name_elts[u]; + if (ne->status == 0 && ne->oid[0] == 0 && ne->oid[1] == tag) { + if (ok && ne->len > len) { + memcpy(ne->buf, CTX->pad + 1, len); + ne->buf[len] = 0; + ne->status = 1; + } else { + ne->status = -1; + } + break; + } + } + + } + break; + case 31: { + /* copy-name-element */ + + size_t len; + int32_t off = T0_POPi(); + int ok = T0_POPi(); + + if (off >= 0) { + br_name_element *ne = &CTX->name_elts[off]; + + if (ok) { + len = CTX->pad[0]; + if (len < ne->len) { + memcpy(ne->buf, CTX->pad + 1, len); + ne->buf[len] = 0; + ne->status = 1; + } else { + ne->status = -1; + } + } else { + ne->status = -1; + } + } + + } + break; + case 32: { + /* data-get8 */ + + size_t addr = T0_POP(); + T0_PUSH(pgm_read_byte(&t0_datablock[addr])); + + } + break; + case 33: { + /* dn-hash-length */ + + T0_PUSH(DNHASH_LEN); + + } + break; + case 34: { + /* do-ecdsa-vrfy */ + + size_t qlen = T0_POP(); + int curve = T0_POP(); + br_x509_pkey pk; + + pk.key_type = BR_KEYTYPE_EC; + pk.key.ec.curve = curve; + pk.key.ec.q = CTX->pkey_data; + pk.key.ec.qlen = qlen; + T0_PUSH(verify_signature(CTX, &pk)); + + } + break; + case 35: { + /* do-rsa-vrfy */ + + size_t elen = T0_POP(); + size_t nlen = T0_POP(); + br_x509_pkey pk; + + pk.key_type = BR_KEYTYPE_RSA; + pk.key.rsa.n = CTX->pkey_data; + pk.key.rsa.nlen = nlen; + pk.key.rsa.e = CTX->pkey_data + nlen; + pk.key.rsa.elen = elen; + T0_PUSH(verify_signature(CTX, &pk)); + + } + break; + case 36: { + /* drop */ + (void)T0_POP(); + } + break; + case 37: { + /* dup */ + T0_PUSH(T0_PEEK(0)); + } + break; + case 38: { + /* eqOID */ + + const unsigned char *a2 = &t0_datablock[T0_POP()]; + const unsigned char *a1 = &CTX->pad[0]; + size_t len = a1[0]; + int x; + if (len == pgm_read_byte(&a2[0])) { + x = -(memcmp_P(a1 + 1, a2 + 1, len) == 0); + } else { + x = 0; + } + T0_PUSH((uint32_t)x); + + } + break; + case 39: { + /* eqblob */ + + size_t len = T0_POP(); + const unsigned char *a2 = (const unsigned char *)CTX + T0_POP(); + const unsigned char *a1 = (const unsigned char *)CTX + T0_POP(); + T0_PUSHi(-(memcmp(a1, a2, len) == 0)); + + } + break; + case 40: { + /* fail */ + + CTX->err = T0_POPi(); + T0_CO(); + + } + break; + case 41: { + /* get-system-date */ + + if (CTX->days == 0 && CTX->seconds == 0) { +#if BR_USE_UNIX_TIME + time_t x = time(NULL); + + T0_PUSH((uint32_t)(x / 86400) + 719528); + T0_PUSH((uint32_t)(x % 86400)); +#elif BR_USE_WIN32_TIME + FILETIME ft; + uint64_t x; + + GetSystemTimeAsFileTime(&ft); + x = ((uint64_t)ft.dwHighDateTime << 32) + + (uint64_t)ft.dwLowDateTime; + x = (x / 10000000); + T0_PUSH((uint32_t)(x / 86400) + 584754); + T0_PUSH((uint32_t)(x % 86400)); +#else + CTX->err = BR_ERR_X509_TIME_UNKNOWN; + T0_CO(); +#endif + } else { + T0_PUSH(CTX->days); + T0_PUSH(CTX->seconds); + } + + } + break; + case 42: { + /* get16 */ + + uint32_t addr = T0_POP(); + T0_PUSH(*(uint16_t *)(void *)((unsigned char *)CTX + addr)); + + } + break; + case 43: { + /* get32 */ + + uint32_t addr = T0_POP(); + T0_PUSH(*(uint32_t *)(void *)((unsigned char *)CTX + addr)); + + } + break; + case 44: { + /* match-server-name */ + + size_t n1, n2; + + if (CTX->server_name == NULL) { + T0_PUSH(0); + T0_RET(); + } + n1 = strlen(CTX->server_name); + n2 = CTX->pad[0]; + if (n1 == n2 && eqnocase(&CTX->pad[1], CTX->server_name, n1)) { + T0_PUSHi(-1); + T0_RET(); + } + if (n2 >= 2 && CTX->pad[1] == '*' && CTX->pad[2] == '.') { + size_t u; + + u = 0; + while (u < n1 && CTX->server_name[u] != '.') { + u ++; + } + u ++; + n1 -= u; + if ((n2 - 2) == n1 + && eqnocase(&CTX->pad[3], CTX->server_name + u, n1)) + { + T0_PUSHi(-1); + T0_RET(); + } + } + T0_PUSH(0); + + } + break; + case 45: { + /* neg */ + + uint32_t a = T0_POP(); + T0_PUSH(-a); + + } + break; + case 46: { + /* offset-name-element */ + + unsigned san = T0_POP(); + size_t u; + + for (u = 0; u < CTX->num_name_elts; u ++) { + if (CTX->name_elts[u].status == 0) { + const unsigned char *oid; + size_t len, off; + + oid = CTX->name_elts[u].oid; + if (san) { + if (oid[0] != 0 || oid[1] != 0) { + continue; + } + off = 2; + } else { + off = 0; + } + len = oid[off]; + if (len != 0 && len == CTX->pad[0] + && memcmp(oid + off + 1, + CTX->pad + 1, len) == 0) + { + T0_PUSH(u); + T0_RET(); + } + } + } + T0_PUSHi(-1); + + } + break; + case 47: { + /* or */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(a | b); + + } + break; + case 48: { + /* over */ + T0_PUSH(T0_PEEK(1)); + } + break; + case 49: { + /* read-blob-inner */ + + uint32_t len = T0_POP(); + uint32_t addr = T0_POP(); + size_t clen = CTX->hlen; + if (clen > len) { + clen = (size_t)len; + } + if (addr != 0) { + memcpy((unsigned char *)CTX + addr, CTX->hbuf, clen); + } + if (CTX->do_mhash) { + br_multihash_update(&CTX->mhash, CTX->hbuf, clen); + } + if (CTX->do_dn_hash) { + CTX->dn_hash_impl->update( + &CTX->dn_hash.vtable, CTX->hbuf, clen); + } + CTX->hbuf += clen; + CTX->hlen -= clen; + T0_PUSH(addr + clen); + T0_PUSH(len - clen); + + } + break; + case 50: { + /* read8-low */ + + if (CTX->hlen == 0) { + T0_PUSHi(-1); + } else { + unsigned char x = *CTX->hbuf ++; + if (CTX->do_mhash) { + br_multihash_update(&CTX->mhash, &x, 1); + } + if (CTX->do_dn_hash) { + CTX->dn_hash_impl->update(&CTX->dn_hash.vtable, &x, 1); + } + CTX->hlen --; + T0_PUSH(x); + } + + } + break; + case 51: { + /* roll */ + T0_ROLL(T0_POP()); + } + break; + case 52: { + /* rot */ + T0_ROT(); + } + break; + case 53: { + /* set16 */ + + uint32_t addr = T0_POP(); + *(uint16_t *)(void *)((unsigned char *)CTX + addr) = T0_POP(); + + } + break; + case 54: { + /* set32 */ + + uint32_t addr = T0_POP(); + *(uint32_t *)(void *)((unsigned char *)CTX + addr) = T0_POP(); + + } + break; + case 55: { + /* set8 */ + + uint32_t addr = T0_POP(); + *((unsigned char *)CTX + addr) = (unsigned char)T0_POP(); + + } + break; + case 56: { + /* start-dn-hash */ + + CTX->dn_hash_impl->init(&CTX->dn_hash.vtable); + CTX->do_dn_hash = 1; + + } + break; + case 57: { + /* start-tbs-hash */ + + br_multihash_init(&CTX->mhash); + CTX->do_mhash = 1; + + } + break; + case 58: { + /* stop-tbs-hash */ + + CTX->do_mhash = 0; + + } + break; + case 59: { + /* swap */ + T0_SWAP(); + } + break; + case 60: { + /* zero-server-name */ + + T0_PUSHi(-(CTX->server_name == NULL)); + + } + break; + } + + } else { + T0_ENTER(ip, rp, t0x); + } + } +t0_exit: + ((t0_context *)t0ctx)->dp = dp; + ((t0_context *)t0ctx)->rp = rp; + ((t0_context *)t0ctx)->ip = ip; +} + + + +/* + * Verify the signature on the certificate with the provided public key. + * This function checks the public key type with regards to the expected + * type. Returned value is either 0 on success, or a non-zero error code. + */ +static int +verify_signature(br_x509_minimal_context *ctx, const br_x509_pkey *pk) +{ + unsigned char tmp[64]; + unsigned char tmp2[64]; + int kt; + + kt = ctx->cert_signer_key_type; + if ((pgm_read_byte(&pk->key_type) & 0x0F) != kt) { + return BR_ERR_X509_WRONG_KEY_TYPE; + } + switch (kt) { + + case BR_KEYTYPE_RSA: + if (ctx->irsa == 0) { + return BR_ERR_X509_UNSUPPORTED; + } + memcpy_P(tmp2, &t0_datablock[ctx->cert_sig_hash_oid], ctx->cert_sig_hash_len); + if (!ctx->irsa(ctx->cert_sig, ctx->cert_sig_len, + tmp2, //&t0_datablock[ctx->cert_sig_hash_oid], + ctx->cert_sig_hash_len, &pk->key.rsa, tmp)) + { + return BR_ERR_X509_BAD_SIGNATURE; + } + if (memcmp(ctx->tbs_hash, tmp, ctx->cert_sig_hash_len) != 0) { + return BR_ERR_X509_BAD_SIGNATURE; + } + return 0; + + case BR_KEYTYPE_EC: + if (ctx->iecdsa == 0) { + return BR_ERR_X509_UNSUPPORTED; + } + if (!ctx->iecdsa(ctx->iec, ctx->tbs_hash, + ctx->cert_sig_hash_len, &pk->key.ec, + ctx->cert_sig, ctx->cert_sig_len)) + { + return BR_ERR_X509_BAD_SIGNATURE; + } + return 0; + + default: + return BR_ERR_X509_UNSUPPORTED; + } +} diff --git a/lib/bearssl-esp8266/src/x509/x509_minimal_full.c b/lib/bearssl-esp8266/src/x509/x509_minimal_full.c new file mode 100644 index 000000000..89352672a --- /dev/null +++ b/lib/bearssl-esp8266/src/x509/x509_minimal_full.c @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_x509.h */ +void +br_x509_minimal_init_full(br_x509_minimal_context *xc, + const br_x509_trust_anchor *trust_anchors, size_t trust_anchors_num) +{ + /* + * All hash functions are activated. + * Note: the X.509 validation engine will nonetheless refuse to + * validate signatures that use MD5 as hash function. + */ + static const br_hash_class *hashes[] = { + &br_md5_vtable, + &br_sha1_vtable, + &br_sha224_vtable, + &br_sha256_vtable, + &br_sha384_vtable, + &br_sha512_vtable + }; + + int id; + + br_x509_minimal_init(xc, &br_sha256_vtable, + trust_anchors, trust_anchors_num); + br_x509_minimal_set_rsa(xc, &br_rsa_i31_pkcs1_vrfy); + br_x509_minimal_set_ecdsa(xc, + &br_ec_prime_i31, &br_ecdsa_i31_vrfy_asn1); + for (id = br_md5_ID; id <= br_sha512_ID; id ++) { + const br_hash_class *hc; + + hc = hashes[id - 1]; + br_x509_minimal_set_hash(xc, id, hc); + } +} diff --git a/lib/vl53l0x-arduino-1.02/.travis.yml b/lib/vl53l0x-arduino-1.02/.travis.yml new file mode 100644 index 000000000..f3ed70c21 --- /dev/null +++ b/lib/vl53l0x-arduino-1.02/.travis.yml @@ -0,0 +1,24 @@ +language: python + +cache: + directories: + - "~/.platformio" + +install: +- pip install -U platformio + +env: +- BOARD=uno +- BOARD=leonardo +- BOARD=micro +- BOARD=megaatmega2560 +- BOARD=due +- BOARD=yun +- BOARD=genuino101 +- BOARD=zero + +script: +- set -eo pipefail; + for e in examples/*; do + platformio ci --board=$BOARD --lib=. $e/*; + done diff --git a/lib/vl53l0x-arduino-1.02/LICENSE.txt b/lib/vl53l0x-arduino-1.02/LICENSE.txt new file mode 100644 index 000000000..bea985af6 --- /dev/null +++ b/lib/vl53l0x-arduino-1.02/LICENSE.txt @@ -0,0 +1,71 @@ +Copyright (c) 2017 Pololu Corporation. For more information, see + +https://www.pololu.com/ +https://forum.pololu.com/ + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +================================================================= + +Most of the functionality of this library is based on the VL53L0X +API provided by ST (STSW-IMG005), and some of the explanatory +comments are quoted or paraphrased from the API source code, API +user manual (UM2039), and the VL53L0X datasheet. + +The following applies to source code reproduced or derived from +the API: + +----------------------------------------------------------------- + +Copyright © 2016, STMicroelectronics International N.V. 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. +* Neither the name of STMicroelectronics nor the +names of its contributors may be used to endorse or promote +products derived from this software without specific prior +written permission. + +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, FITNESS FOR A PARTICULAR PURPOSE, AND +NON-INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS ARE DISCLAIMED. +IN NO EVENT SHALL STMICROELECTRONICS INTERNATIONAL N.V. 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. + +----------------------------------------------------------------- diff --git a/lib/vl53l0x-arduino-1.02/README.md b/lib/vl53l0x-arduino-1.02/README.md new file mode 100644 index 000000000..917514d70 --- /dev/null +++ b/lib/vl53l0x-arduino-1.02/README.md @@ -0,0 +1,164 @@ +# VL53L0X library for Arduino + +Version: 1.0.2
+Release date: 2017 Jun 27
+[![Build Status](https://travis-ci.org/pololu/vl53l0x-arduino.svg?branch=master)](https://travis-ci.org/pololu/vl53l0x-arduino)
+[www.pololu.com](https://www.pololu.com/) + +## Summary + +This is a library for the Arduino IDE that helps interface with ST's [VL53L0X time-of-flight distance sensor](https://www.pololu.com/product/2490). The library makes it simple to configure the sensor and read range data from it via I²C. + +## Supported platforms + +This library is designed to work with the Arduino IDE versions 1.6.x or later; we have not tested it with earlier versions. This library should support any Arduino-compatible board, including the [Pololu A-Star 32U4 controllers](https://www.pololu.com/category/149/a-star-programmable-controllers). + +## Getting started + +### Hardware + +A [VL53L0X carrier](https://www.pololu.com/product/2490) can be purchased from Pololu's website. Before continuing, careful reading of the [product page](https://www.pololu.com/product/2490) as well as the VL53L0X datasheet is recommended. + +Make the following connections between the Arduino and the VL53L0X board: + +#### 5V Arduino boards + +(including Arduino Uno, Leonardo, Mega; Pololu A-Star 32U4) + + Arduino VL53L0X board + ------- ------------- + 5V - VIN + GND - GND + SDA - SDA + SCL - SCL + +#### 3.3V Arduino boards + +(including Arduino Due) + + Arduino VL53L0X board + ------- ------------- + 3V3 - VIN + GND - GND + SDA - SDA + SCL - SCL + +### Software + +If you are using version 1.6.2 or later of the [Arduino software (IDE)](http://www.arduino.cc/en/Main/Software), you can use the Library Manager to install this library: + +1. In the Arduino IDE, open the "Sketch" menu, select "Include Library", then "Manage Libraries...". +2. Search for "VL53L0X". +3. Click the VL53L0X entry in the list. +4. Click "Install". + +If this does not work, you can manually install the library: + +1. Download the [latest release archive from GitHub](https://github.com/pololu/vl53l0x-arduino/releases) and decompress it. +2. Rename the folder "vl53l0x-arduino-master" to "VL53L0X". +3. Move the "VL53L0X" folder into the "libraries" directory inside your Arduino sketchbook directory. You can view your sketchbook location by opening the "File" menu and selecting "Preferences" in the Arduino IDE. If there is not already a "libraries" folder in that location, you should make the folder yourself. +4. After installing the library, restart the Arduino IDE. + +## Examples + +Several example sketches are available that show how to use the library. You can access them from the Arduino IDE by opening the "File" menu, selecting "Examples", and then selecting "VL53L0X". If you cannot find these examples, the library was probably installed incorrectly and you should retry the installation instructions above. + +## ST's VL53L0X API and this library + +Most of the functionality of this library is based on the [VL53L0X API](http://www.st.com/content/st_com/en/products/embedded-software/proximity-sensors-software/stsw-img005.html) provided by ST (STSW-IMG005), and some of the explanatory comments in the code are quoted or paraphrased from the API source code, API user manual (UM2039), and the VL53L0X datasheet. For more explanation about the library code and how it was derived from the API, see the comments in VL53L0X.cpp. + +This library is intended to provide a quicker and easier way to get started using the VL53L0X with an Arduino-compatible controller, in contrast to customizing and compiling ST's API for the Arduino. The library has a more streamlined interface, as well as smaller storage and memory footprints. However, it does not implement some of the more advanced functionality available in the API (for example, calibrating the sensor to work well under a cover glass), and it has less robust error checking. For advanced applications, especially when storage and memory are less of an issue, consider using the VL53L0X API directly. + +## Library reference + +* `uint8_t last_status`
+ The status of the last I²C write transmission. See the [`Wire.endTransmission()` documentation](http://arduino.cc/en/Reference/WireEndTransmission) for return values. + +* `VL53L0X(void)`
+ Constructor. + +* `void setAddress(uint8_t new_addr)`
+ Changes the I²C slave device address of the VL53L0X to the given value (7-bit). + +* `uint8_t getAddress(void)`
+ Returns the current I²C address. + +* `bool init(bool io_2v8 = true)`
+ Iniitializes and configures the sensor. If the optional argument `io_2v8` is true (the default if not specified), the sensor is configured for 2V8 mode (2.8 V I/O); if false, the sensor is left in 1V8 mode. The return value is a boolean indicating whether the initialization completed successfully. + +* `void writeReg(uint8_t reg, uint8_t value)`
+ Writes an 8-bit sensor register with the given value. + + Register address constants are defined by the regAddr enumeration type in VL53L0X.h.
+ Example use: `sensor.writeReg(VL53L0X::SYSRANGE_START, 0x01);` + +* `void writeReg16Bit(uint8_t reg, uint16_t value)`
+ Writes a 16-bit sensor register with the given value. + +* `void writeReg32Bit(uint8_t reg, uint32_t value)`
+ Writes a 32-bit sensor register with the given value. + +* `uint8_t readReg(uint8_t reg)`
+ Reads an 8-bit sensor register and returns the value read. + +* `uint16_t readReg16Bit(uint8_t reg)`
+ Reads a 16-bit sensor register and returns the value read. + +* `uint32_t readReg32Bit(uint8_t reg)`
+ Reads a 32-bit sensor register and returns the value read. + +* `void writeMulti(uint8_t reg, uint8_t const * src, uint8_t count)`
+ Writes an arbitrary number of bytes from the given array to the sensor, starting at the given register. + +* `void readMulti(uint8_t reg, uint8_t * dst, uint8_t count)`
+ Reads an arbitrary number of bytes from the sensor, starting at the given register, into the given array. + +* `bool setSignalRateLimit(float limit_Mcps)`
+ Sets the return signal rate limit to the given value in units of MCPS (mega counts per second). This is the minimum amplitude of the signal reflected from the target and received by the sensor necessary for it to report a valid reading. Setting a lower limit increases the potential range of the sensor but also increases the likelihood of getting an inaccurate reading because of reflections from objects other than the intended target. This limit is initialized to 0.25 MCPS by default. The return value is a boolean indicating whether the requested limit was valid. + +* `float getSignalRateLimit(void)`
+ Returns the current return signal rate limit in MCPS. + +* `bool setMeasurementTimingBudget(uint32_t budget_us)`
+ Sets the measurement timing budget to the given value in microseconds. This is the time allowed for one range measurement; a longer timing budget allows for more accurate measurements. The default budget is about 33000 microseconds, or 33 ms; the minimum is 20 ms. The return value is a boolean indicating whether the requested budget was valid. + +* `uint32_t getMeasurementTimingBudget(void)`
+ Returns the current measurement timing budget in microseconds. + +* `bool setVcselPulsePeriod(vcselPeriodType type, uint8_t period_pclks)` + Sets the VCSEL (vertical cavity surface emitting laser) pulse period for the given period type (`VL53L0X::VcselPeriodPreRange` or `VL53L0X::VcselPeriodFinalRange`) to the given value (in PCLKs). Longer periods increase the potential range of the sensor. Valid values are (even numbers only): + + Pre: 12 to 18 (initialized to 14 by default)
+ Final: 8 to 14 (initialized to 10 by default) + + The return value is a boolean indicating whether the requested period was valid. + +* `uint8_t getVcselPulsePeriod(vcselPeriodType type)`
+ Returns the current VCSEL pulse period for the given period type. + +* `void startContinuous(uint32_t period_ms = 0)`
+ Starts continuous ranging measurements. If the optional argument `period_ms` is 0 (the default if not specified), continuous back-to-back mode is used (the sensor takes measurements as often as possible); if it is nonzero, continuous timed mode is used, with the specified inter-measurement period in milliseconds determining how often the sensor takes a measurement. + +* `void stopContinuous(void)`
+ Stops continuous mode. + +* `uint16_t readRangeContinuousMillimeters(void)`
+ Returns a range reading in millimeters when continuous mode is active. + +* `uint16_t readRangeSingleMillimeters(void)`
+ Performs a single-shot ranging measurement and returns the reading in millimeters. + +* `void setTimeout(uint16_t timeout)`
+ Sets a timeout period in milliseconds after which read operations will abort if the sensor is not ready. A value of 0 disables the timeout. + +* `uint16_t getTimeout(void)`
+ Returns the current timeout period setting. + +* `bool timeoutOccurred(void)`
+ Indicates whether a read timeout has occurred since the last call to `timeoutOccurred()`. + +## Version history + +* 1.0.2 (2017 Jun 27): Fixed a typo in a register modification in `getSpadInfo()` (thanks @tridge). +* 1.0.1 (2016 Dec 08): Fixed type error in `readReg32Bit()`. +* 1.0.0 (2016 Aug 12): Original release. diff --git a/lib/vl53l0x-arduino-1.02/VL53L0X.cpp b/lib/vl53l0x-arduino-1.02/VL53L0X.cpp new file mode 100644 index 000000000..fb2ed3028 --- /dev/null +++ b/lib/vl53l0x-arduino-1.02/VL53L0X.cpp @@ -0,0 +1,1036 @@ +// Most of the functionality of this library is based on the VL53L0X API +// provided by ST (STSW-IMG005), and some of the explanatory comments are quoted +// or paraphrased from the API source code, API user manual (UM2039), and the +// VL53L0X datasheet. + +#include +#include + +// Defines ///////////////////////////////////////////////////////////////////// + +// The Arduino two-wire interface uses a 7-bit number for the address, +// and sets the last bit correctly based on reads and writes +#define ADDRESS_DEFAULT 0b0101001 + +// Record the current time to check an upcoming timeout against +#define startTimeout() (timeout_start_ms = millis()) + +// Check if timeout is enabled (set to nonzero value) and has expired +#define checkTimeoutExpired() (io_timeout > 0 && ((uint16_t)millis() - timeout_start_ms) > io_timeout) + +// Decode VCSEL (vertical cavity surface emitting laser) pulse period in PCLKs +// from register value +// based on VL53L0X_decode_vcsel_period() +#define decodeVcselPeriod(reg_val) (((reg_val) + 1) << 1) + +// Encode VCSEL pulse period register value from period in PCLKs +// based on VL53L0X_encode_vcsel_period() +#define encodeVcselPeriod(period_pclks) (((period_pclks) >> 1) - 1) + +// Calculate macro period in *nanoseconds* from VCSEL period in PCLKs +// based on VL53L0X_calc_macro_period_ps() +// PLL_period_ps = 1655; macro_period_vclks = 2304 +#define calcMacroPeriod(vcsel_period_pclks) ((((uint32_t)2304 * (vcsel_period_pclks) * 1655) + 500) / 1000) + +// Constructors //////////////////////////////////////////////////////////////// + +VL53L0X::VL53L0X(void) + : address(ADDRESS_DEFAULT) + , io_timeout(0) // no timeout + , did_timeout(false) +{ +} + +// Public Methods ////////////////////////////////////////////////////////////// + +void VL53L0X::setAddress(uint8_t new_addr) +{ + writeReg(I2C_SLAVE_DEVICE_ADDRESS, new_addr & 0x7F); + address = new_addr; +} + +// Initialize sensor using sequence based on VL53L0X_DataInit(), +// VL53L0X_StaticInit(), and VL53L0X_PerformRefCalibration(). +// This function does not perform reference SPAD calibration +// (VL53L0X_PerformRefSpadManagement()), since the API user manual says that it +// is performed by ST on the bare modules; it seems like that should work well +// enough unless a cover glass is added. +// If io_2v8 (optional) is true or not given, the sensor is configured for 2V8 +// mode. +bool VL53L0X::init(bool io_2v8) +{ + // VL53L0X_DataInit() begin + + // sensor uses 1V8 mode for I/O by default; switch to 2V8 mode if necessary + if (io_2v8) + { + writeReg(VHV_CONFIG_PAD_SCL_SDA__EXTSUP_HV, + readReg(VHV_CONFIG_PAD_SCL_SDA__EXTSUP_HV) | 0x01); // set bit 0 + } + + // "Set I2C standard mode" + writeReg(0x88, 0x00); + + writeReg(0x80, 0x01); + writeReg(0xFF, 0x01); + writeReg(0x00, 0x00); + stop_variable = readReg(0x91); + writeReg(0x00, 0x01); + writeReg(0xFF, 0x00); + writeReg(0x80, 0x00); + + // disable SIGNAL_RATE_MSRC (bit 1) and SIGNAL_RATE_PRE_RANGE (bit 4) limit checks + writeReg(MSRC_CONFIG_CONTROL, readReg(MSRC_CONFIG_CONTROL) | 0x12); + + // set final range signal rate limit to 0.25 MCPS (million counts per second) + setSignalRateLimit(0.25); + + writeReg(SYSTEM_SEQUENCE_CONFIG, 0xFF); + + // VL53L0X_DataInit() end + + // VL53L0X_StaticInit() begin + + uint8_t spad_count; + bool spad_type_is_aperture; + if (!getSpadInfo(&spad_count, &spad_type_is_aperture)) { return false; } + + // The SPAD map (RefGoodSpadMap) is read by VL53L0X_get_info_from_device() in + // the API, but the same data seems to be more easily readable from + // GLOBAL_CONFIG_SPAD_ENABLES_REF_0 through _6, so read it from there + uint8_t ref_spad_map[6]; + readMulti(GLOBAL_CONFIG_SPAD_ENABLES_REF_0, ref_spad_map, 6); + + // -- VL53L0X_set_reference_spads() begin (assume NVM values are valid) + + writeReg(0xFF, 0x01); + writeReg(DYNAMIC_SPAD_REF_EN_START_OFFSET, 0x00); + writeReg(DYNAMIC_SPAD_NUM_REQUESTED_REF_SPAD, 0x2C); + writeReg(0xFF, 0x00); + writeReg(GLOBAL_CONFIG_REF_EN_START_SELECT, 0xB4); + + uint8_t first_spad_to_enable = spad_type_is_aperture ? 12 : 0; // 12 is the first aperture spad + uint8_t spads_enabled = 0; + + for (uint8_t i = 0; i < 48; i++) + { + if (i < first_spad_to_enable || spads_enabled == spad_count) + { + // This bit is lower than the first one that should be enabled, or + // (reference_spad_count) bits have already been enabled, so zero this bit + ref_spad_map[i / 8] &= ~(1 << (i % 8)); + } + else if ((ref_spad_map[i / 8] >> (i % 8)) & 0x1) + { + spads_enabled++; + } + } + + writeMulti(GLOBAL_CONFIG_SPAD_ENABLES_REF_0, ref_spad_map, 6); + + // -- VL53L0X_set_reference_spads() end + + // -- VL53L0X_load_tuning_settings() begin + // DefaultTuningSettings from vl53l0x_tuning.h + + writeReg(0xFF, 0x01); + writeReg(0x00, 0x00); + + writeReg(0xFF, 0x00); + writeReg(0x09, 0x00); + writeReg(0x10, 0x00); + writeReg(0x11, 0x00); + + writeReg(0x24, 0x01); + writeReg(0x25, 0xFF); + writeReg(0x75, 0x00); + + writeReg(0xFF, 0x01); + writeReg(0x4E, 0x2C); + writeReg(0x48, 0x00); + writeReg(0x30, 0x20); + + writeReg(0xFF, 0x00); + writeReg(0x30, 0x09); + writeReg(0x54, 0x00); + writeReg(0x31, 0x04); + writeReg(0x32, 0x03); + writeReg(0x40, 0x83); + writeReg(0x46, 0x25); + writeReg(0x60, 0x00); + writeReg(0x27, 0x00); + writeReg(0x50, 0x06); + writeReg(0x51, 0x00); + writeReg(0x52, 0x96); + writeReg(0x56, 0x08); + writeReg(0x57, 0x30); + writeReg(0x61, 0x00); + writeReg(0x62, 0x00); + writeReg(0x64, 0x00); + writeReg(0x65, 0x00); + writeReg(0x66, 0xA0); + + writeReg(0xFF, 0x01); + writeReg(0x22, 0x32); + writeReg(0x47, 0x14); + writeReg(0x49, 0xFF); + writeReg(0x4A, 0x00); + + writeReg(0xFF, 0x00); + writeReg(0x7A, 0x0A); + writeReg(0x7B, 0x00); + writeReg(0x78, 0x21); + + writeReg(0xFF, 0x01); + writeReg(0x23, 0x34); + writeReg(0x42, 0x00); + writeReg(0x44, 0xFF); + writeReg(0x45, 0x26); + writeReg(0x46, 0x05); + writeReg(0x40, 0x40); + writeReg(0x0E, 0x06); + writeReg(0x20, 0x1A); + writeReg(0x43, 0x40); + + writeReg(0xFF, 0x00); + writeReg(0x34, 0x03); + writeReg(0x35, 0x44); + + writeReg(0xFF, 0x01); + writeReg(0x31, 0x04); + writeReg(0x4B, 0x09); + writeReg(0x4C, 0x05); + writeReg(0x4D, 0x04); + + writeReg(0xFF, 0x00); + writeReg(0x44, 0x00); + writeReg(0x45, 0x20); + writeReg(0x47, 0x08); + writeReg(0x48, 0x28); + writeReg(0x67, 0x00); + writeReg(0x70, 0x04); + writeReg(0x71, 0x01); + writeReg(0x72, 0xFE); + writeReg(0x76, 0x00); + writeReg(0x77, 0x00); + + writeReg(0xFF, 0x01); + writeReg(0x0D, 0x01); + + writeReg(0xFF, 0x00); + writeReg(0x80, 0x01); + writeReg(0x01, 0xF8); + + writeReg(0xFF, 0x01); + writeReg(0x8E, 0x01); + writeReg(0x00, 0x01); + writeReg(0xFF, 0x00); + writeReg(0x80, 0x00); + + // -- VL53L0X_load_tuning_settings() end + + // "Set interrupt config to new sample ready" + // -- VL53L0X_SetGpioConfig() begin + + writeReg(SYSTEM_INTERRUPT_CONFIG_GPIO, 0x04); + writeReg(GPIO_HV_MUX_ACTIVE_HIGH, readReg(GPIO_HV_MUX_ACTIVE_HIGH) & ~0x10); // active low + writeReg(SYSTEM_INTERRUPT_CLEAR, 0x01); + + // -- VL53L0X_SetGpioConfig() end + + measurement_timing_budget_us = getMeasurementTimingBudget(); + + // "Disable MSRC and TCC by default" + // MSRC = Minimum Signal Rate Check + // TCC = Target CentreCheck + // -- VL53L0X_SetSequenceStepEnable() begin + + writeReg(SYSTEM_SEQUENCE_CONFIG, 0xE8); + + // -- VL53L0X_SetSequenceStepEnable() end + + // "Recalculate timing budget" + setMeasurementTimingBudget(measurement_timing_budget_us); + + // VL53L0X_StaticInit() end + + // VL53L0X_PerformRefCalibration() begin (VL53L0X_perform_ref_calibration()) + + // -- VL53L0X_perform_vhv_calibration() begin + + writeReg(SYSTEM_SEQUENCE_CONFIG, 0x01); + if (!performSingleRefCalibration(0x40)) { return false; } + + // -- VL53L0X_perform_vhv_calibration() end + + // -- VL53L0X_perform_phase_calibration() begin + + writeReg(SYSTEM_SEQUENCE_CONFIG, 0x02); + if (!performSingleRefCalibration(0x00)) { return false; } + + // -- VL53L0X_perform_phase_calibration() end + + // "restore the previous Sequence Config" + writeReg(SYSTEM_SEQUENCE_CONFIG, 0xE8); + + // VL53L0X_PerformRefCalibration() end + + return true; +} + +// Write an 8-bit register +void VL53L0X::writeReg(uint8_t reg, uint8_t value) +{ + Wire.beginTransmission(address); + Wire.write(reg); + Wire.write(value); + last_status = Wire.endTransmission(); +} + +// Write a 16-bit register +void VL53L0X::writeReg16Bit(uint8_t reg, uint16_t value) +{ + Wire.beginTransmission(address); + Wire.write(reg); + Wire.write((value >> 8) & 0xFF); // value high byte + Wire.write( value & 0xFF); // value low byte + last_status = Wire.endTransmission(); +} + +// Write a 32-bit register +void VL53L0X::writeReg32Bit(uint8_t reg, uint32_t value) +{ + Wire.beginTransmission(address); + Wire.write(reg); + Wire.write((value >> 24) & 0xFF); // value highest byte + Wire.write((value >> 16) & 0xFF); + Wire.write((value >> 8) & 0xFF); + Wire.write( value & 0xFF); // value lowest byte + last_status = Wire.endTransmission(); +} + +// Read an 8-bit register +uint8_t VL53L0X::readReg(uint8_t reg) +{ + uint8_t value; + + Wire.beginTransmission(address); + Wire.write(reg); + last_status = Wire.endTransmission(); + + Wire.requestFrom(address, (uint8_t)1); + value = Wire.read(); + + return value; +} + +// Read a 16-bit register +uint16_t VL53L0X::readReg16Bit(uint8_t reg) +{ + uint16_t value; + + Wire.beginTransmission(address); + Wire.write(reg); + last_status = Wire.endTransmission(); + + Wire.requestFrom(address, (uint8_t)2); + value = (uint16_t)Wire.read() << 8; // value high byte + value |= Wire.read(); // value low byte + + return value; +} + +// Read a 32-bit register +uint32_t VL53L0X::readReg32Bit(uint8_t reg) +{ + uint32_t value; + + Wire.beginTransmission(address); + Wire.write(reg); + last_status = Wire.endTransmission(); + + Wire.requestFrom(address, (uint8_t)4); + value = (uint32_t)Wire.read() << 24; // value highest byte + value |= (uint32_t)Wire.read() << 16; + value |= (uint16_t)Wire.read() << 8; + value |= Wire.read(); // value lowest byte + + return value; +} + +// Write an arbitrary number of bytes from the given array to the sensor, +// starting at the given register +void VL53L0X::writeMulti(uint8_t reg, uint8_t const * src, uint8_t count) +{ + Wire.beginTransmission(address); + Wire.write(reg); + + while (count-- > 0) + { + Wire.write(*(src++)); + } + + last_status = Wire.endTransmission(); +} + +// Read an arbitrary number of bytes from the sensor, starting at the given +// register, into the given array +void VL53L0X::readMulti(uint8_t reg, uint8_t * dst, uint8_t count) +{ + Wire.beginTransmission(address); + Wire.write(reg); + last_status = Wire.endTransmission(); + + Wire.requestFrom(address, count); + + while (count-- > 0) + { + *(dst++) = Wire.read(); + } +} + +// Set the return signal rate limit check value in units of MCPS (mega counts +// per second). "This represents the amplitude of the signal reflected from the +// target and detected by the device"; setting this limit presumably determines +// the minimum measurement necessary for the sensor to report a valid reading. +// Setting a lower limit increases the potential range of the sensor but also +// seems to increase the likelihood of getting an inaccurate reading because of +// unwanted reflections from objects other than the intended target. +// Defaults to 0.25 MCPS as initialized by the ST API and this library. +bool VL53L0X::setSignalRateLimit(float limit_Mcps) +{ + if (limit_Mcps < 0 || limit_Mcps > 511.99) { return false; } + + // Q9.7 fixed point format (9 integer bits, 7 fractional bits) + writeReg16Bit(FINAL_RANGE_CONFIG_MIN_COUNT_RATE_RTN_LIMIT, limit_Mcps * (1 << 7)); + return true; +} + +// Get the return signal rate limit check value in MCPS +float VL53L0X::getSignalRateLimit(void) +{ + return (float)readReg16Bit(FINAL_RANGE_CONFIG_MIN_COUNT_RATE_RTN_LIMIT) / (1 << 7); +} + +// Set the measurement timing budget in microseconds, which is the time allowed +// for one measurement; the ST API and this library take care of splitting the +// timing budget among the sub-steps in the ranging sequence. A longer timing +// budget allows for more accurate measurements. Increasing the budget by a +// factor of N decreases the range measurement standard deviation by a factor of +// sqrt(N). Defaults to about 33 milliseconds; the minimum is 20 ms. +// based on VL53L0X_set_measurement_timing_budget_micro_seconds() +bool VL53L0X::setMeasurementTimingBudget(uint32_t budget_us) +{ + SequenceStepEnables enables; + SequenceStepTimeouts timeouts; + + uint16_t const StartOverhead = 1320; // note that this is different than the value in get_ + uint16_t const EndOverhead = 960; + uint16_t const MsrcOverhead = 660; + uint16_t const TccOverhead = 590; + uint16_t const DssOverhead = 690; + uint16_t const PreRangeOverhead = 660; + uint16_t const FinalRangeOverhead = 550; + + uint32_t const MinTimingBudget = 20000; + + if (budget_us < MinTimingBudget) { return false; } + + uint32_t used_budget_us = StartOverhead + EndOverhead; + + getSequenceStepEnables(&enables); + getSequenceStepTimeouts(&enables, &timeouts); + + if (enables.tcc) + { + used_budget_us += (timeouts.msrc_dss_tcc_us + TccOverhead); + } + + if (enables.dss) + { + used_budget_us += 2 * (timeouts.msrc_dss_tcc_us + DssOverhead); + } + else if (enables.msrc) + { + used_budget_us += (timeouts.msrc_dss_tcc_us + MsrcOverhead); + } + + if (enables.pre_range) + { + used_budget_us += (timeouts.pre_range_us + PreRangeOverhead); + } + + if (enables.final_range) + { + used_budget_us += FinalRangeOverhead; + + // "Note that the final range timeout is determined by the timing + // budget and the sum of all other timeouts within the sequence. + // If there is no room for the final range timeout, then an error + // will be set. Otherwise the remaining time will be applied to + // the final range." + + if (used_budget_us > budget_us) + { + // "Requested timeout too big." + return false; + } + + uint32_t final_range_timeout_us = budget_us - used_budget_us; + + // set_sequence_step_timeout() begin + // (SequenceStepId == VL53L0X_SEQUENCESTEP_FINAL_RANGE) + + // "For the final range timeout, the pre-range timeout + // must be added. To do this both final and pre-range + // timeouts must be expressed in macro periods MClks + // because they have different vcsel periods." + + uint16_t final_range_timeout_mclks = + timeoutMicrosecondsToMclks(final_range_timeout_us, + timeouts.final_range_vcsel_period_pclks); + + if (enables.pre_range) + { + final_range_timeout_mclks += timeouts.pre_range_mclks; + } + + writeReg16Bit(FINAL_RANGE_CONFIG_TIMEOUT_MACROP_HI, + encodeTimeout(final_range_timeout_mclks)); + + // set_sequence_step_timeout() end + + measurement_timing_budget_us = budget_us; // store for internal reuse + } + return true; +} + +// Get the measurement timing budget in microseconds +// based on VL53L0X_get_measurement_timing_budget_micro_seconds() +// in us +uint32_t VL53L0X::getMeasurementTimingBudget(void) +{ + SequenceStepEnables enables; + SequenceStepTimeouts timeouts; + + uint16_t const StartOverhead = 1910; // note that this is different than the value in set_ + uint16_t const EndOverhead = 960; + uint16_t const MsrcOverhead = 660; + uint16_t const TccOverhead = 590; + uint16_t const DssOverhead = 690; + uint16_t const PreRangeOverhead = 660; + uint16_t const FinalRangeOverhead = 550; + + // "Start and end overhead times always present" + uint32_t budget_us = StartOverhead + EndOverhead; + + getSequenceStepEnables(&enables); + getSequenceStepTimeouts(&enables, &timeouts); + + if (enables.tcc) + { + budget_us += (timeouts.msrc_dss_tcc_us + TccOverhead); + } + + if (enables.dss) + { + budget_us += 2 * (timeouts.msrc_dss_tcc_us + DssOverhead); + } + else if (enables.msrc) + { + budget_us += (timeouts.msrc_dss_tcc_us + MsrcOverhead); + } + + if (enables.pre_range) + { + budget_us += (timeouts.pre_range_us + PreRangeOverhead); + } + + if (enables.final_range) + { + budget_us += (timeouts.final_range_us + FinalRangeOverhead); + } + + measurement_timing_budget_us = budget_us; // store for internal reuse + return budget_us; +} + +// Set the VCSEL (vertical cavity surface emitting laser) pulse period for the +// given period type (pre-range or final range) to the given value in PCLKs. +// Longer periods seem to increase the potential range of the sensor. +// Valid values are (even numbers only): +// pre: 12 to 18 (initialized default: 14) +// final: 8 to 14 (initialized default: 10) +// based on VL53L0X_set_vcsel_pulse_period() +bool VL53L0X::setVcselPulsePeriod(vcselPeriodType type, uint8_t period_pclks) +{ + uint8_t vcsel_period_reg = encodeVcselPeriod(period_pclks); + + SequenceStepEnables enables; + SequenceStepTimeouts timeouts; + + getSequenceStepEnables(&enables); + getSequenceStepTimeouts(&enables, &timeouts); + + // "Apply specific settings for the requested clock period" + // "Re-calculate and apply timeouts, in macro periods" + + // "When the VCSEL period for the pre or final range is changed, + // the corresponding timeout must be read from the device using + // the current VCSEL period, then the new VCSEL period can be + // applied. The timeout then must be written back to the device + // using the new VCSEL period. + // + // For the MSRC timeout, the same applies - this timeout being + // dependant on the pre-range vcsel period." + + + if (type == VcselPeriodPreRange) + { + // "Set phase check limits" + switch (period_pclks) + { + case 12: + writeReg(PRE_RANGE_CONFIG_VALID_PHASE_HIGH, 0x18); + break; + + case 14: + writeReg(PRE_RANGE_CONFIG_VALID_PHASE_HIGH, 0x30); + break; + + case 16: + writeReg(PRE_RANGE_CONFIG_VALID_PHASE_HIGH, 0x40); + break; + + case 18: + writeReg(PRE_RANGE_CONFIG_VALID_PHASE_HIGH, 0x50); + break; + + default: + // invalid period + return false; + } + writeReg(PRE_RANGE_CONFIG_VALID_PHASE_LOW, 0x08); + + // apply new VCSEL period + writeReg(PRE_RANGE_CONFIG_VCSEL_PERIOD, vcsel_period_reg); + + // update timeouts + + // set_sequence_step_timeout() begin + // (SequenceStepId == VL53L0X_SEQUENCESTEP_PRE_RANGE) + + uint16_t new_pre_range_timeout_mclks = + timeoutMicrosecondsToMclks(timeouts.pre_range_us, period_pclks); + + writeReg16Bit(PRE_RANGE_CONFIG_TIMEOUT_MACROP_HI, + encodeTimeout(new_pre_range_timeout_mclks)); + + // set_sequence_step_timeout() end + + // set_sequence_step_timeout() begin + // (SequenceStepId == VL53L0X_SEQUENCESTEP_MSRC) + + uint16_t new_msrc_timeout_mclks = + timeoutMicrosecondsToMclks(timeouts.msrc_dss_tcc_us, period_pclks); + + writeReg(MSRC_CONFIG_TIMEOUT_MACROP, + (new_msrc_timeout_mclks > 256) ? 255 : (new_msrc_timeout_mclks - 1)); + + // set_sequence_step_timeout() end + } + else if (type == VcselPeriodFinalRange) + { + switch (period_pclks) + { + case 8: + writeReg(FINAL_RANGE_CONFIG_VALID_PHASE_HIGH, 0x10); + writeReg(FINAL_RANGE_CONFIG_VALID_PHASE_LOW, 0x08); + writeReg(GLOBAL_CONFIG_VCSEL_WIDTH, 0x02); + writeReg(ALGO_PHASECAL_CONFIG_TIMEOUT, 0x0C); + writeReg(0xFF, 0x01); + writeReg(ALGO_PHASECAL_LIM, 0x30); + writeReg(0xFF, 0x00); + break; + + case 10: + writeReg(FINAL_RANGE_CONFIG_VALID_PHASE_HIGH, 0x28); + writeReg(FINAL_RANGE_CONFIG_VALID_PHASE_LOW, 0x08); + writeReg(GLOBAL_CONFIG_VCSEL_WIDTH, 0x03); + writeReg(ALGO_PHASECAL_CONFIG_TIMEOUT, 0x09); + writeReg(0xFF, 0x01); + writeReg(ALGO_PHASECAL_LIM, 0x20); + writeReg(0xFF, 0x00); + break; + + case 12: + writeReg(FINAL_RANGE_CONFIG_VALID_PHASE_HIGH, 0x38); + writeReg(FINAL_RANGE_CONFIG_VALID_PHASE_LOW, 0x08); + writeReg(GLOBAL_CONFIG_VCSEL_WIDTH, 0x03); + writeReg(ALGO_PHASECAL_CONFIG_TIMEOUT, 0x08); + writeReg(0xFF, 0x01); + writeReg(ALGO_PHASECAL_LIM, 0x20); + writeReg(0xFF, 0x00); + break; + + case 14: + writeReg(FINAL_RANGE_CONFIG_VALID_PHASE_HIGH, 0x48); + writeReg(FINAL_RANGE_CONFIG_VALID_PHASE_LOW, 0x08); + writeReg(GLOBAL_CONFIG_VCSEL_WIDTH, 0x03); + writeReg(ALGO_PHASECAL_CONFIG_TIMEOUT, 0x07); + writeReg(0xFF, 0x01); + writeReg(ALGO_PHASECAL_LIM, 0x20); + writeReg(0xFF, 0x00); + break; + + default: + // invalid period + return false; + } + + // apply new VCSEL period + writeReg(FINAL_RANGE_CONFIG_VCSEL_PERIOD, vcsel_period_reg); + + // update timeouts + + // set_sequence_step_timeout() begin + // (SequenceStepId == VL53L0X_SEQUENCESTEP_FINAL_RANGE) + + // "For the final range timeout, the pre-range timeout + // must be added. To do this both final and pre-range + // timeouts must be expressed in macro periods MClks + // because they have different vcsel periods." + + uint16_t new_final_range_timeout_mclks = + timeoutMicrosecondsToMclks(timeouts.final_range_us, period_pclks); + + if (enables.pre_range) + { + new_final_range_timeout_mclks += timeouts.pre_range_mclks; + } + + writeReg16Bit(FINAL_RANGE_CONFIG_TIMEOUT_MACROP_HI, + encodeTimeout(new_final_range_timeout_mclks)); + + // set_sequence_step_timeout end + } + else + { + // invalid type + return false; + } + + // "Finally, the timing budget must be re-applied" + + setMeasurementTimingBudget(measurement_timing_budget_us); + + // "Perform the phase calibration. This is needed after changing on vcsel period." + // VL53L0X_perform_phase_calibration() begin + + uint8_t sequence_config = readReg(SYSTEM_SEQUENCE_CONFIG); + writeReg(SYSTEM_SEQUENCE_CONFIG, 0x02); + performSingleRefCalibration(0x0); + writeReg(SYSTEM_SEQUENCE_CONFIG, sequence_config); + + // VL53L0X_perform_phase_calibration() end + + return true; +} + +// Get the VCSEL pulse period in PCLKs for the given period type. +// based on VL53L0X_get_vcsel_pulse_period() +uint8_t VL53L0X::getVcselPulsePeriod(vcselPeriodType type) +{ + if (type == VcselPeriodPreRange) + { + return decodeVcselPeriod(readReg(PRE_RANGE_CONFIG_VCSEL_PERIOD)); + } + else if (type == VcselPeriodFinalRange) + { + return decodeVcselPeriod(readReg(FINAL_RANGE_CONFIG_VCSEL_PERIOD)); + } + else { return 255; } +} + +// Start continuous ranging measurements. If period_ms (optional) is 0 or not +// given, continuous back-to-back mode is used (the sensor takes measurements as +// often as possible); otherwise, continuous timed mode is used, with the given +// inter-measurement period in milliseconds determining how often the sensor +// takes a measurement. +// based on VL53L0X_StartMeasurement() +void VL53L0X::startContinuous(uint32_t period_ms) +{ + writeReg(0x80, 0x01); + writeReg(0xFF, 0x01); + writeReg(0x00, 0x00); + writeReg(0x91, stop_variable); + writeReg(0x00, 0x01); + writeReg(0xFF, 0x00); + writeReg(0x80, 0x00); + + if (period_ms != 0) + { + // continuous timed mode + + // VL53L0X_SetInterMeasurementPeriodMilliSeconds() begin + + uint16_t osc_calibrate_val = readReg16Bit(OSC_CALIBRATE_VAL); + + if (osc_calibrate_val != 0) + { + period_ms *= osc_calibrate_val; + } + + writeReg32Bit(SYSTEM_INTERMEASUREMENT_PERIOD, period_ms); + + // VL53L0X_SetInterMeasurementPeriodMilliSeconds() end + + writeReg(SYSRANGE_START, 0x04); // VL53L0X_REG_SYSRANGE_MODE_TIMED + } + else + { + // continuous back-to-back mode + writeReg(SYSRANGE_START, 0x02); // VL53L0X_REG_SYSRANGE_MODE_BACKTOBACK + } +} + +// Stop continuous measurements +// based on VL53L0X_StopMeasurement() +void VL53L0X::stopContinuous(void) +{ + writeReg(SYSRANGE_START, 0x01); // VL53L0X_REG_SYSRANGE_MODE_SINGLESHOT + + writeReg(0xFF, 0x01); + writeReg(0x00, 0x00); + writeReg(0x91, 0x00); + writeReg(0x00, 0x01); + writeReg(0xFF, 0x00); +} + +// Returns a range reading in millimeters when continuous mode is active +// (readRangeSingleMillimeters() also calls this function after starting a +// single-shot range measurement) +uint16_t VL53L0X::readRangeContinuousMillimeters(void) +{ + startTimeout(); + while ((readReg(RESULT_INTERRUPT_STATUS) & 0x07) == 0) + { + if (checkTimeoutExpired()) + { + did_timeout = true; + return 65535; + } + } + + // assumptions: Linearity Corrective Gain is 1000 (default); + // fractional ranging is not enabled + uint16_t range = readReg16Bit(RESULT_RANGE_STATUS + 10); + + writeReg(SYSTEM_INTERRUPT_CLEAR, 0x01); + + return range; +} + +// Performs a single-shot range measurement and returns the reading in +// millimeters +// based on VL53L0X_PerformSingleRangingMeasurement() +uint16_t VL53L0X::readRangeSingleMillimeters(void) +{ + writeReg(0x80, 0x01); + writeReg(0xFF, 0x01); + writeReg(0x00, 0x00); + writeReg(0x91, stop_variable); + writeReg(0x00, 0x01); + writeReg(0xFF, 0x00); + writeReg(0x80, 0x00); + + writeReg(SYSRANGE_START, 0x01); + + // "Wait until start bit has been cleared" + startTimeout(); + while (readReg(SYSRANGE_START) & 0x01) + { + if (checkTimeoutExpired()) + { + did_timeout = true; + return 65535; + } + } + + return readRangeContinuousMillimeters(); +} + +// Did a timeout occur in one of the read functions since the last call to +// timeoutOccurred()? +bool VL53L0X::timeoutOccurred() +{ + bool tmp = did_timeout; + did_timeout = false; + return tmp; +} + +// Private Methods ///////////////////////////////////////////////////////////// + +// Get reference SPAD (single photon avalanche diode) count and type +// based on VL53L0X_get_info_from_device(), +// but only gets reference SPAD count and type +bool VL53L0X::getSpadInfo(uint8_t * count, bool * type_is_aperture) +{ + uint8_t tmp; + + writeReg(0x80, 0x01); + writeReg(0xFF, 0x01); + writeReg(0x00, 0x00); + + writeReg(0xFF, 0x06); + writeReg(0x83, readReg(0x83) | 0x04); + writeReg(0xFF, 0x07); + writeReg(0x81, 0x01); + + writeReg(0x80, 0x01); + + writeReg(0x94, 0x6b); + writeReg(0x83, 0x00); + startTimeout(); + while (readReg(0x83) == 0x00) + { + if (checkTimeoutExpired()) { return false; } + } + writeReg(0x83, 0x01); + tmp = readReg(0x92); + + *count = tmp & 0x7f; + *type_is_aperture = (tmp >> 7) & 0x01; + + writeReg(0x81, 0x00); + writeReg(0xFF, 0x06); + writeReg(0x83, readReg(0x83) & ~0x04); + writeReg(0xFF, 0x01); + writeReg(0x00, 0x01); + + writeReg(0xFF, 0x00); + writeReg(0x80, 0x00); + + return true; +} + +// Get sequence step enables +// based on VL53L0X_GetSequenceStepEnables() +void VL53L0X::getSequenceStepEnables(SequenceStepEnables * enables) +{ + uint8_t sequence_config = readReg(SYSTEM_SEQUENCE_CONFIG); + + enables->tcc = (sequence_config >> 4) & 0x1; + enables->dss = (sequence_config >> 3) & 0x1; + enables->msrc = (sequence_config >> 2) & 0x1; + enables->pre_range = (sequence_config >> 6) & 0x1; + enables->final_range = (sequence_config >> 7) & 0x1; +} + +// Get sequence step timeouts +// based on get_sequence_step_timeout(), +// but gets all timeouts instead of just the requested one, and also stores +// intermediate values +void VL53L0X::getSequenceStepTimeouts(SequenceStepEnables const * enables, SequenceStepTimeouts * timeouts) +{ + timeouts->pre_range_vcsel_period_pclks = getVcselPulsePeriod(VcselPeriodPreRange); + + timeouts->msrc_dss_tcc_mclks = readReg(MSRC_CONFIG_TIMEOUT_MACROP) + 1; + timeouts->msrc_dss_tcc_us = + timeoutMclksToMicroseconds(timeouts->msrc_dss_tcc_mclks, + timeouts->pre_range_vcsel_period_pclks); + + timeouts->pre_range_mclks = + decodeTimeout(readReg16Bit(PRE_RANGE_CONFIG_TIMEOUT_MACROP_HI)); + timeouts->pre_range_us = + timeoutMclksToMicroseconds(timeouts->pre_range_mclks, + timeouts->pre_range_vcsel_period_pclks); + + timeouts->final_range_vcsel_period_pclks = getVcselPulsePeriod(VcselPeriodFinalRange); + + timeouts->final_range_mclks = + decodeTimeout(readReg16Bit(FINAL_RANGE_CONFIG_TIMEOUT_MACROP_HI)); + + if (enables->pre_range) + { + timeouts->final_range_mclks -= timeouts->pre_range_mclks; + } + + timeouts->final_range_us = + timeoutMclksToMicroseconds(timeouts->final_range_mclks, + timeouts->final_range_vcsel_period_pclks); +} + +// Decode sequence step timeout in MCLKs from register value +// based on VL53L0X_decode_timeout() +// Note: the original function returned a uint32_t, but the return value is +// always stored in a uint16_t. +uint16_t VL53L0X::decodeTimeout(uint16_t reg_val) +{ + // format: "(LSByte * 2^MSByte) + 1" + return (uint16_t)((reg_val & 0x00FF) << + (uint16_t)((reg_val & 0xFF00) >> 8)) + 1; +} + +// Encode sequence step timeout register value from timeout in MCLKs +// based on VL53L0X_encode_timeout() +// Note: the original function took a uint16_t, but the argument passed to it +// is always a uint16_t. +uint16_t VL53L0X::encodeTimeout(uint16_t timeout_mclks) +{ + // format: "(LSByte * 2^MSByte) + 1" + + uint32_t ls_byte = 0; + uint16_t ms_byte = 0; + + if (timeout_mclks > 0) + { + ls_byte = timeout_mclks - 1; + + while ((ls_byte & 0xFFFFFF00) > 0) + { + ls_byte >>= 1; + ms_byte++; + } + + return (ms_byte << 8) | (ls_byte & 0xFF); + } + else { return 0; } +} + +// Convert sequence step timeout from MCLKs to microseconds with given VCSEL period in PCLKs +// based on VL53L0X_calc_timeout_us() +uint32_t VL53L0X::timeoutMclksToMicroseconds(uint16_t timeout_period_mclks, uint8_t vcsel_period_pclks) +{ + uint32_t macro_period_ns = calcMacroPeriod(vcsel_period_pclks); + + return ((timeout_period_mclks * macro_period_ns) + (macro_period_ns / 2)) / 1000; +} + +// Convert sequence step timeout from microseconds to MCLKs with given VCSEL period in PCLKs +// based on VL53L0X_calc_timeout_mclks() +uint32_t VL53L0X::timeoutMicrosecondsToMclks(uint32_t timeout_period_us, uint8_t vcsel_period_pclks) +{ + uint32_t macro_period_ns = calcMacroPeriod(vcsel_period_pclks); + + return (((timeout_period_us * 1000) + (macro_period_ns / 2)) / macro_period_ns); +} + + +// based on VL53L0X_perform_single_ref_calibration() +bool VL53L0X::performSingleRefCalibration(uint8_t vhv_init_byte) +{ + writeReg(SYSRANGE_START, 0x01 | vhv_init_byte); // VL53L0X_REG_SYSRANGE_MODE_START_STOP + + startTimeout(); + while ((readReg(RESULT_INTERRUPT_STATUS) & 0x07) == 0) + { + if (checkTimeoutExpired()) { return false; } + } + + writeReg(SYSTEM_INTERRUPT_CLEAR, 0x01); + + writeReg(SYSRANGE_START, 0x00); + + return true; +} diff --git a/lib/vl53l0x-arduino-1.02/VL53L0X.h b/lib/vl53l0x-arduino-1.02/VL53L0X.h new file mode 100644 index 000000000..b531ff96a --- /dev/null +++ b/lib/vl53l0x-arduino-1.02/VL53L0X.h @@ -0,0 +1,176 @@ +#ifndef VL53L0X_h +#define VL53L0X_h + +#include + +class VL53L0X +{ + public: + // register addresses from API vl53l0x_device.h (ordered as listed there) + enum regAddr + { + SYSRANGE_START = 0x00, + + SYSTEM_THRESH_HIGH = 0x0C, + SYSTEM_THRESH_LOW = 0x0E, + + SYSTEM_SEQUENCE_CONFIG = 0x01, + SYSTEM_RANGE_CONFIG = 0x09, + SYSTEM_INTERMEASUREMENT_PERIOD = 0x04, + + SYSTEM_INTERRUPT_CONFIG_GPIO = 0x0A, + + GPIO_HV_MUX_ACTIVE_HIGH = 0x84, + + SYSTEM_INTERRUPT_CLEAR = 0x0B, + + RESULT_INTERRUPT_STATUS = 0x13, + RESULT_RANGE_STATUS = 0x14, + + RESULT_CORE_AMBIENT_WINDOW_EVENTS_RTN = 0xBC, + RESULT_CORE_RANGING_TOTAL_EVENTS_RTN = 0xC0, + RESULT_CORE_AMBIENT_WINDOW_EVENTS_REF = 0xD0, + RESULT_CORE_RANGING_TOTAL_EVENTS_REF = 0xD4, + RESULT_PEAK_SIGNAL_RATE_REF = 0xB6, + + ALGO_PART_TO_PART_RANGE_OFFSET_MM = 0x28, + + I2C_SLAVE_DEVICE_ADDRESS = 0x8A, + + MSRC_CONFIG_CONTROL = 0x60, + + PRE_RANGE_CONFIG_MIN_SNR = 0x27, + PRE_RANGE_CONFIG_VALID_PHASE_LOW = 0x56, + PRE_RANGE_CONFIG_VALID_PHASE_HIGH = 0x57, + PRE_RANGE_MIN_COUNT_RATE_RTN_LIMIT = 0x64, + + FINAL_RANGE_CONFIG_MIN_SNR = 0x67, + FINAL_RANGE_CONFIG_VALID_PHASE_LOW = 0x47, + FINAL_RANGE_CONFIG_VALID_PHASE_HIGH = 0x48, + FINAL_RANGE_CONFIG_MIN_COUNT_RATE_RTN_LIMIT = 0x44, + + PRE_RANGE_CONFIG_SIGMA_THRESH_HI = 0x61, + PRE_RANGE_CONFIG_SIGMA_THRESH_LO = 0x62, + + PRE_RANGE_CONFIG_VCSEL_PERIOD = 0x50, + PRE_RANGE_CONFIG_TIMEOUT_MACROP_HI = 0x51, + PRE_RANGE_CONFIG_TIMEOUT_MACROP_LO = 0x52, + + SYSTEM_HISTOGRAM_BIN = 0x81, + HISTOGRAM_CONFIG_INITIAL_PHASE_SELECT = 0x33, + HISTOGRAM_CONFIG_READOUT_CTRL = 0x55, + + FINAL_RANGE_CONFIG_VCSEL_PERIOD = 0x70, + FINAL_RANGE_CONFIG_TIMEOUT_MACROP_HI = 0x71, + FINAL_RANGE_CONFIG_TIMEOUT_MACROP_LO = 0x72, + CROSSTALK_COMPENSATION_PEAK_RATE_MCPS = 0x20, + + MSRC_CONFIG_TIMEOUT_MACROP = 0x46, + + SOFT_RESET_GO2_SOFT_RESET_N = 0xBF, + IDENTIFICATION_MODEL_ID = 0xC0, + IDENTIFICATION_REVISION_ID = 0xC2, + + OSC_CALIBRATE_VAL = 0xF8, + + GLOBAL_CONFIG_VCSEL_WIDTH = 0x32, + GLOBAL_CONFIG_SPAD_ENABLES_REF_0 = 0xB0, + GLOBAL_CONFIG_SPAD_ENABLES_REF_1 = 0xB1, + GLOBAL_CONFIG_SPAD_ENABLES_REF_2 = 0xB2, + GLOBAL_CONFIG_SPAD_ENABLES_REF_3 = 0xB3, + GLOBAL_CONFIG_SPAD_ENABLES_REF_4 = 0xB4, + GLOBAL_CONFIG_SPAD_ENABLES_REF_5 = 0xB5, + + GLOBAL_CONFIG_REF_EN_START_SELECT = 0xB6, + DYNAMIC_SPAD_NUM_REQUESTED_REF_SPAD = 0x4E, + DYNAMIC_SPAD_REF_EN_START_OFFSET = 0x4F, + POWER_MANAGEMENT_GO1_POWER_FORCE = 0x80, + + VHV_CONFIG_PAD_SCL_SDA__EXTSUP_HV = 0x89, + + ALGO_PHASECAL_LIM = 0x30, + ALGO_PHASECAL_CONFIG_TIMEOUT = 0x30, + }; + + enum vcselPeriodType { VcselPeriodPreRange, VcselPeriodFinalRange }; + + uint8_t last_status; // status of last I2C transmission + + VL53L0X(void); + + void setAddress(uint8_t new_addr); + inline uint8_t getAddress(void) { return address; } + + bool init(bool io_2v8 = true); + + void writeReg(uint8_t reg, uint8_t value); + void writeReg16Bit(uint8_t reg, uint16_t value); + void writeReg32Bit(uint8_t reg, uint32_t value); + uint8_t readReg(uint8_t reg); + uint16_t readReg16Bit(uint8_t reg); + uint32_t readReg32Bit(uint8_t reg); + + void writeMulti(uint8_t reg, uint8_t const * src, uint8_t count); + void readMulti(uint8_t reg, uint8_t * dst, uint8_t count); + + bool setSignalRateLimit(float limit_Mcps); + float getSignalRateLimit(void); + + bool setMeasurementTimingBudget(uint32_t budget_us); + uint32_t getMeasurementTimingBudget(void); + + bool setVcselPulsePeriod(vcselPeriodType type, uint8_t period_pclks); + uint8_t getVcselPulsePeriod(vcselPeriodType type); + + void startContinuous(uint32_t period_ms = 0); + void stopContinuous(void); + uint16_t readRangeContinuousMillimeters(void); + uint16_t readRangeSingleMillimeters(void); + + inline void setTimeout(uint16_t timeout) { io_timeout = timeout; } + inline uint16_t getTimeout(void) { return io_timeout; } + bool timeoutOccurred(void); + + private: + // TCC: Target CentreCheck + // MSRC: Minimum Signal Rate Check + // DSS: Dynamic Spad Selection + + struct SequenceStepEnables + { + boolean tcc, msrc, dss, pre_range, final_range; + }; + + struct SequenceStepTimeouts + { + uint16_t pre_range_vcsel_period_pclks, final_range_vcsel_period_pclks; + + uint16_t msrc_dss_tcc_mclks, pre_range_mclks, final_range_mclks; + uint32_t msrc_dss_tcc_us, pre_range_us, final_range_us; + }; + + uint8_t address; + uint16_t io_timeout; + bool did_timeout; + uint16_t timeout_start_ms; + + uint8_t stop_variable; // read by init and used when starting measurement; is StopVariable field of VL53L0X_DevData_t structure in API + uint32_t measurement_timing_budget_us; + + bool getSpadInfo(uint8_t * count, bool * type_is_aperture); + + void getSequenceStepEnables(SequenceStepEnables * enables); + void getSequenceStepTimeouts(SequenceStepEnables const * enables, SequenceStepTimeouts * timeouts); + + bool performSingleRefCalibration(uint8_t vhv_init_byte); + + static uint16_t decodeTimeout(uint16_t value); + static uint16_t encodeTimeout(uint16_t timeout_mclks); + static uint32_t timeoutMclksToMicroseconds(uint16_t timeout_period_mclks, uint8_t vcsel_period_pclks); + static uint32_t timeoutMicrosecondsToMclks(uint32_t timeout_period_us, uint8_t vcsel_period_pclks); +}; + +#endif + + + diff --git a/lib/vl53l0x-arduino-1.02/examples/Continuous/Continuous.ino b/lib/vl53l0x-arduino-1.02/examples/Continuous/Continuous.ino new file mode 100644 index 000000000..33fcd05c4 --- /dev/null +++ b/lib/vl53l0x-arduino-1.02/examples/Continuous/Continuous.ino @@ -0,0 +1,33 @@ +/* This example shows how to use continuous mode to take +range measurements with the VL53L0X. It is based on +vl53l0x_ContinuousRanging_Example.c from the VL53L0X API. + +The range readings are in units of mm. */ + +#include +#include + +VL53L0X sensor; + +void setup() +{ + Serial.begin(9600); + Wire.begin(); + + sensor.init(); + sensor.setTimeout(500); + + // Start continuous back-to-back mode (take readings as + // fast as possible). To use continuous timed mode + // instead, provide a desired inter-measurement period in + // ms (e.g. sensor.startContinuous(100)). + sensor.startContinuous(); +} + +void loop() +{ + Serial.print(sensor.readRangeContinuousMillimeters()); + if (sensor.timeoutOccurred()) { Serial.print(" TIMEOUT"); } + + Serial.println(); +} diff --git a/lib/vl53l0x-arduino-1.02/examples/Single/Single.ino b/lib/vl53l0x-arduino-1.02/examples/Single/Single.ino new file mode 100644 index 000000000..9f24463fc --- /dev/null +++ b/lib/vl53l0x-arduino-1.02/examples/Single/Single.ino @@ -0,0 +1,65 @@ +/* This example shows how to get single-shot range + measurements from the VL53L0X. The sensor can optionally be + configured with different ranging profiles, as described in + the VL53L0X API user manual, to get better performance for + a certain application. This code is based on the four + "SingleRanging" examples in the VL53L0X API. + + The range readings are in units of mm. */ + +#include +#include + +VL53L0X sensor; + + +// Uncomment this line to use long range mode. This +// increases the sensitivity of the sensor and extends its +// potential range, but increases the likelihood of getting +// an inaccurate reading because of reflections from objects +// other than the intended target. It works best in dark +// conditions. + +//#define LONG_RANGE + + +// Uncomment ONE of these two lines to get +// - higher speed at the cost of lower accuracy OR +// - higher accuracy at the cost of lower speed + +//#define HIGH_SPEED +//#define HIGH_ACCURACY + + +void setup() +{ + Serial.begin(9600); + Wire.begin(); + + sensor.init(); + sensor.setTimeout(500); + +#if defined LONG_RANGE + // lower the return signal rate limit (default is 0.25 MCPS) + sensor.setSignalRateLimit(0.1); + // increase laser pulse periods (defaults are 14 and 10 PCLKs) + sensor.setVcselPulsePeriod(VL53L0X::VcselPeriodPreRange, 18); + sensor.setVcselPulsePeriod(VL53L0X::VcselPeriodFinalRange, 14); +#endif + +#if defined HIGH_SPEED + // reduce timing budget to 20 ms (default is about 33 ms) + sensor.setMeasurementTimingBudget(20000); +#elif defined HIGH_ACCURACY + // increase timing budget to 200 ms + sensor.setMeasurementTimingBudget(200000); +#endif +} + +void loop() +{ + Serial.print(sensor.readRangeSingleMillimeters()); + if (sensor.timeoutOccurred()) { Serial.print(" TIMEOUT"); } + + Serial.println(); +} diff --git a/lib/vl53l0x-arduino-1.02/keywords.txt b/lib/vl53l0x-arduino-1.02/keywords.txt new file mode 100644 index 000000000..437add908 --- /dev/null +++ b/lib/vl53l0x-arduino-1.02/keywords.txt @@ -0,0 +1,90 @@ +VL53L0X KEYWORD1 + +setAddress KEYWORD2 +getAddress KEYWORD2 +init KEYWORD2 +writeReg KEYWORD2 +writeReg16Bit KEYWORD2 +writeReg32Bit KEYWORD2 +readReg KEYWORD2 +readReg16Bit KEYWORD2 +readReg32Bit KEYWORD2 +writeMulti KEYWORD2 +readMulti KEYWORD2 +setSignalRateLimit KEYWORD2 +getSignalRateLimit KEYWORD2 +setMeasurementTimingBudget KEYWORD2 +getMeasurementTimingBudget KEYWORD2 +setVcselPulsePeriod KEYWORD2 +getVcselPulsePeriod KEYWORD2 +startContinuous KEYWORD2 +stopContinuous KEYWORD2 +readRangeContinuousMillimeters KEYWORD2 +readRangeSingleMillimeters KEYWORD2 +setTimeout KEYWORD2 +getTimeout KEYWORD2 +timeoutOccurred KEYWORD2 + +SYSRANGE_START LITERAL1 +SYSTEM_THRESH_HIGH LITERAL1 +SYSTEM_THRESH_LOW LITERAL1 +SYSTEM_SEQUENCE_CONFIG LITERAL1 +SYSTEM_RANGE_CONFIG LITERAL1 +SYSTEM_INTERMEASUREMENT_PERIOD LITERAL1 +SYSTEM_INTERRUPT_CONFIG_GPIO LITERAL1 +GPIO_HV_MUX_ACTIVE_HIGH LITERAL1 +SYSTEM_INTERRUPT_CLEAR LITERAL1 +RESULT_INTERRUPT_STATUS LITERAL1 +RESULT_RANGE_STATUS LITERAL1 +RESULT_CORE_AMBIENT_WINDOW_EVENTS_RTN LITERAL1 +RESULT_CORE_RANGING_TOTAL_EVENTS_RTN LITERAL1 +RESULT_CORE_AMBIENT_WINDOW_EVENTS_REF LITERAL1 +RESULT_CORE_RANGING_TOTAL_EVENTS_REF LITERAL1 +RESULT_PEAK_SIGNAL_RATE_REF LITERAL1 +ALGO_PART_TO_PART_RANGE_OFFSET_MM LITERAL1 +I2C_SLAVE_DEVICE_ADDRESS LITERAL1 +MSRC_CONFIG_CONTROL LITERAL1 +PRE_RANGE_CONFIG_MIN_SNR LITERAL1 +PRE_RANGE_CONFIG_VALID_PHASE_LOW LITERAL1 +PRE_RANGE_CONFIG_VALID_PHASE_HIGH LITERAL1 +PRE_RANGE_MIN_COUNT_RATE_RTN_LIMIT LITERAL1 +FINAL_RANGE_CONFIG_MIN_SNR LITERAL1 +FINAL_RANGE_CONFIG_VALID_PHASE_LOW LITERAL1 +FINAL_RANGE_CONFIG_VALID_PHASE_HIGH LITERAL1 +FINAL_RANGE_CONFIG_MIN_COUNT_RATE_RTN_LIMIT LITERAL1 +PRE_RANGE_CONFIG_SIGMA_THRESH_HI LITERAL1 +PRE_RANGE_CONFIG_SIGMA_THRESH_LO LITERAL1 +PRE_RANGE_CONFIG_VCSEL_PERIOD LITERAL1 +PRE_RANGE_CONFIG_TIMEOUT_MACROP_HI LITERAL1 +PRE_RANGE_CONFIG_TIMEOUT_MACROP_LO LITERAL1 +SYSTEM_HISTOGRAM_BIN LITERAL1 +HISTOGRAM_CONFIG_INITIAL_PHASE_SELECT LITERAL1 +HISTOGRAM_CONFIG_READOUT_CTRL LITERAL1 +FINAL_RANGE_CONFIG_VCSEL_PERIOD LITERAL1 +FINAL_RANGE_CONFIG_TIMEOUT_MACROP_HI LITERAL1 +FINAL_RANGE_CONFIG_TIMEOUT_MACROP_LO LITERAL1 +CROSSTALK_COMPENSATION_PEAK_RATE_MCPS LITERAL1 +MSRC_CONFIG_TIMEOUT_MACROP LITERAL1 +SOFT_RESET_GO2_SOFT_RESET_N LITERAL1 +IDENTIFICATION_MODEL_ID LITERAL1 +IDENTIFICATION_REVISION_ID LITERAL1 +OSC_CALIBRATE_VAL LITERAL1 +GLOBAL_CONFIG_VCSEL_WIDTH LITERAL1 +GLOBAL_CONFIG_SPAD_ENABLES_REF_0 LITERAL1 +GLOBAL_CONFIG_SPAD_ENABLES_REF_1 LITERAL1 +GLOBAL_CONFIG_SPAD_ENABLES_REF_2 LITERAL1 +GLOBAL_CONFIG_SPAD_ENABLES_REF_3 LITERAL1 +GLOBAL_CONFIG_SPAD_ENABLES_REF_4 LITERAL1 +GLOBAL_CONFIG_SPAD_ENABLES_REF_5 LITERAL1 +GLOBAL_CONFIG_REF_EN_START_SELECT LITERAL1 +DYNAMIC_SPAD_NUM_REQUESTED_REF_SPAD LITERAL1 +DYNAMIC_SPAD_REF_EN_START_OFFSET LITERAL1 +POWER_MANAGEMENT_GO1_POWER_FORCE LITERAL1 +VHV_CONFIG_PAD_SCL_SDA__EXTSUP_HV LITERAL1 +ALGO_PHASECAL_LIM LITERAL1 +ALGO_PHASECAL_CONFIG_TIMEOUT LITERAL1 + +VcselPeriodPreRange LITERAL1 +VcselPeriodFinalRange LITERAL1 + + diff --git a/lib/vl53l0x-arduino-1.02/library.properties b/lib/vl53l0x-arduino-1.02/library.properties new file mode 100644 index 000000000..43dd4d3a2 --- /dev/null +++ b/lib/vl53l0x-arduino-1.02/library.properties @@ -0,0 +1,9 @@ +name=VL53L0X +version=1.0.2 +author=Pololu +maintainer=Pololu +sentence=VL53L0X distance sensor library +paragraph=This is a library for the Arduino IDE that helps interface with ST's VL53L0X distance sensor. +category=Sensors +url=https://github.com/pololu/vl53l0x-arduino +architectures=* diff --git a/platformio.ini b/platformio.ini index 8d349d974..f4be6ad3f 100644 --- a/platformio.ini +++ b/platformio.ini @@ -57,7 +57,7 @@ platform = espressif8266@1.8.0 build_flags = ${esp82xx_defaults.build_flags} -Wl,-Teagle.flash.1m0.ld -lstdc++ -lsupc++ -; lwIP 1.4 (Default) +; lwIP 1.4 ; -DPIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH ; lwIP 2 - Low Memory ; -DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY @@ -65,12 +65,19 @@ build_flags = ${esp82xx_defaults.build_flags} -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH -DVTABLES_IN_FLASH -[core_2_5_0] -; *** Esp8266 core for Arduino version 2.5.0 -platform = espressif8266@2.0.1 +[core_2_5_2] +; *** Esp8266 core for Arduino version 2.5.2 +platform = espressif8266@~2.2.2 build_flags = ${esp82xx_defaults.build_flags} -Wl,-Teagle.flash.1m.ld -; lwIP 1.4 (Default) +; Code optimization see https://github.com/esp8266/Arduino/issues/5790#issuecomment-475672473 + -O2 + -DBEARSSL_SSL_BASIC +; nonos-sdk 22x + -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x +; nonos-sdk-pre-v3 +; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK3 +; lwIP 1.4 ; -DPIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH ; lwIP 2 - Low Memory ; -DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY @@ -89,11 +96,14 @@ build_flags = ${esp82xx_defaults.build_flags} platform = https://github.com/platformio/platform-espressif8266.git#feature/stage build_flags = ${esp82xx_defaults.build_flags} -Wl,-Teagle.flash.1m.ld +; Code optimization see https://github.com/esp8266/Arduino/issues/5790#issuecomment-475672473 + -O2 + -DBEARSSL_SSL_BASIC ; nonos-sdk 22x -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x ; nonos-sdk-pre-v3 -; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK3 -; lwIP 1.4 (Default) +; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK3 +; lwIP 1.4 ; -DPIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH ; lwIP 2 - Low Memory ; -DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY @@ -123,8 +133,8 @@ platform = ${core_2_3_0.platform} build_flags = ${core_2_3_0.build_flags} ;platform = ${core_2_4_2.platform} ;build_flags = ${core_2_4_2.build_flags} -;platform = ${core_2_5_0.platform} -;build_flags = ${core_2_5_0.build_flags} +;platform = ${core_2_5_2.platform} +;build_flags = ${core_2_5_2.build_flags} ;platform = ${core_stage.platform} ;build_flags = ${core_stage.build_flags} @@ -343,7 +353,7 @@ board = ${common.board} board_build.flash_mode = ${common.board_build.flash_mode} board_build.f_cpu = ${common.board_build.f_cpu} build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags} -DMY_LANGUAGE=es-AR +build_flags = ${common.build_flags} -DMY_LANGUAGE=es-ES monitor_speed = ${common.monitor_speed} upload_port = ${common.upload_port} upload_resetmethod = ${common.upload_resetmethod} diff --git a/scripter.md b/scripter.md new file mode 100644 index 000000000..43c966b6b --- /dev/null +++ b/scripter.md @@ -0,0 +1,728 @@ +**Script Language for Tasmota** + +As an alternative to rules. (about 17k flash size, variable ram size) + +In submenu Configuration =\> edit script +1535 bytes max script size (uses rules buffer) + +to enable: +\#define USE_SCRIPT +\#undef USE_RULES + + +Up to 50 variables (45 numeric and 5 strings, maybe changed by #define) +Freely definable variable names (all names are intentionally case sensitive) +Nested if,then,else up to a level of 8 +Math operators **+,-,\*,/,%,&,|,^** +all operators may be used in the op= form e.g. **+=** +Left right evaluation with optional brackets +all numbers are float +e.g. temp=hum\*(100/37.5)+temp-(timer\*hum%10) +no spaces allowed between math operations +Comparison operators **==,!=,\>,\>=,<,<=** +**and** , **or** support + +strings support **+** and **+=** operators +string comparison **==,!=** +max string size = 19 chars (default, can be increased or decreased by optional >D parameter) + +**Comments** start with **;** + +**Sections** defined: + +>**\>D ssize** +ssize = optional max stringsize (default=19) +define and init variables here, must be the first section, no other code allowed +**p:**vname specifies permanent vars (the number of permanent vars is limited by tasmota rules space (50 bytes) +numeric var=4 bytes, string var=lenght of string+1) +**t:**vname specifies countdown timers, if >0 they are decremented in seconds until zero is reached. see example below +**i:**vname specifies auto increment counters if >=0 (in seconds) +**m:**vname specifies a median filter variable with 5 entries (for elimination of outliers) +**M:**vname specifies a moving average filter variable with 8 entries (for smoothing data) +(max 5 filters in total m+M) optional another filter lenght (1..127) can be given after the definition. +filter vars can be accessed also in indexed mode vname[x] (index = 1...N, index 0 returns current array index pointer) +by this filter vars can be used as arrays + +>all variable names length taken together may not exceed 256 characters, so keep variable names as short as possible. +memory is dynamically allocated as a result of the D section. +copying a string to a number or reverse is supported + +>**\>B** +executed on BOOT time + +>**\>T** +executed on teleperiod time (**SENSOR** and **STATE**), get tele vars only in this section + +>**\>S** +executed every second + +>**\>E** +executed e.g. on power change and mqtt **RESULT** + +>**\>R** +executed on restart, p vars are saved automatically after this call + + +special variables (read only): + +>**upsecs** = seconds since start +**uptime** = minutes since start +**time** = minutes since midnight +**sunrise** = sunrise minutes since midnight +**sunset** = sunset minutes since midnight +**tper** = teleperiod (may be set also) +**tstamp** = timestamp (local date and time) +**topic** = mqtt topic +**gtopic** = mqtt group topic +**prefixn** = prefix n = 1-3 +**pwr[x]** = tasmota power state (x = 1-N) +**sw[x]** = tasmota switch state (x = 1-N) +>**pin[x]** = gpio pin level (x = 0-16) +**pn[x]** = pin number for sensor code x, 99 if none +**pd[x]** = defined sensor for gpio pin nr x none=999 +**gtmp** = global temperature +**ghum** = global humidity +**gprs** = global pressure +**pow(x y)** = calculates the power of x^y +**med(n x)** = calculates a 5 value median filter of x (2 filters possible n=0,1) +**int(x)** = gets the integer part of x (like floor) +**hn(x)** = converts x (0..255) to a hex nibble string +**st(svar c n)** = stringtoken gets the n th substring of svar separated by c +**s(x)** = explicit conversion from number x to string +**mqtts** = state of mqtt disconnected=0, connected>0 +**wifis** = state of wifi disconnected=0, connected>0 + +>**hours** = hours +**mins** = mins +**secs** = seconds +**day** = day of month +**wday** = day of week +**month** = month +**year** = year + +these variables are cleared after reading true +>**chg[var]** = true if a variables value was changed (numeric vars only) +**upd[var]** = true if a variable was updated +**boot** = true on BOOT +**tinit** = true on time init +**tset** = true on time set +**mqttc** = true on mqtt connect +**mqttd** = true on mqtt disconnect +**wific** = true on wifi connect +**wifid** = true on wifi disconnect + +system vars (for debugging) +>**stack** = stack size +**heap** = heap size +**ram** = used ram size +**slen** = script length +**micros** = running microseconds +**millis** = running milliseconds +**loglvl** = loglevel of script cmds, may be set also + +remarks: +if you define a variable with the same name as a special +variable that special variable is discarded + + +**Tasmota** cmds start with **=\>** +within cmds you can replace text with variables with **%varname%** +a single percent sign must be given as **%%** + +**special** cmds: + +>**=\> print** prints to info log for debugging + +to save code space nearly no error messages are provided. However it is taken care of that at least it should not crash on syntax errors. +if a variable does not exist a **???** is given on commands +if a **SENSOR** or **STATUS** or **RESULT** message or a var does not exist the destination variable is NOT updated. + +2 possibilities for conditionals: +>**if** a==b +**and** x==y +**or** k==i +**then** => do this +**else** => do that +**endif** + +OR + +>**if** a==b +**and** x==y +**or** k==i **{** + => do this +**} else {** + => do that +**}** + +you may NOT mix both methods + +also possible e.g. + +>if var1-var2==var3*var4 +then + +remarks: +the last closing bracket must be on a single line +the condition may not be enclosed in brackets + +>**break** exits a section or terminates a for next loop +**dpx** sets decimal precision to x (0-9) +**svars** save permanent vars +**delay(x)** pauses x milliseconds (should be as short as possible) +**spin(x m)** set gpio pin x (0-16) to value m (0,1) only the last bit is used, so even values set the pin to zero and uneven values set the pin to 1 +**spinm(x m)** set pin mode gpio pin x (0-16) to mode m (input=0,output=1) + +>**#name** names a subroutine, subroutines are called with **=#name** +**#name(param)** names a subroutines with a parameter is called with **=#name(param)** +subroutines end with the next '#' or '>' line or break, may be nested +params can be numbers or strings and on mismatch are converted + +>**for var from to inc** +**next** +specifies a for next loop, (loop count must not be less then 1) + +>**switch x** +**case a** +**case b** +**ends** +specifies a switch case selector + +**sd card support** +enable by CARD_CS = gpio pin of card chip select (+ 10k flash) +\#define USE_SCRIPT_FATFS CARD_CS +sd card uses standard hardware spi gpios: mosi,miso,sclk +max 4 files open at a time +allows for e.g. logging sensors to a tab delimited file and then download (see example below) +the download of files may be executed in a kind of "multitasking" when bit 7 of loglvl is set (128+loglevel) +without multitasking 150kb/s (all processes are stopped during download), with multitasking 50kb/s (other tasmota processes are running) +script itself is also stored on sdcard with a default size of 4096 chars + + +enable sd card directory support (+ 1,2k flash) +\#define SDCARD_DIR +shows a web sdcard directory (submeu of scripter) where you can +upload and download files to/from sd card + + +>**fr=fo("fname" m)** open file fname, mode 0=read, 1=write (returns file reference (0-3) or -1 for error) +**res=fw("text" fr)** writes text to (the end of) file fr, returns number of bytes written +**res=fr(svar fr)** reads a string into svar, returns bytes read (string is read until delimiter \t \n \r or eof) +**fc(fr)** close file +**ff(fr)** flush file, writes cached data and updates directory +**fd("fname")** delete file fname +**flx(fname)** create download link for file (x=1 or 2) fname = file name of file to download +**fsm** return 1 if filesystem is mounted, (valid sd card found) + +extended commands (+0,9k flash) +\#define USE_SCRIPT_FATFS_EXT +>**fmd("fname")** make directory fname +>**frd("fname")** remove directory fname +>**fx("fname")** check if file fname exists +>**fe("fname")** execute script fname (max 2048 bytes, script file must start with '>' char on the 1. line) + + +**konsole script cmds** +>**script 1 or 0** switch script on or off +**script >cmdline** executes the script cmdline +can be used e.g. to set variables e.g. **script >mintmp=15** +more then one line may be executed seperated by a semicolon e.g. **script >mintmp=15;maxtemp=40** +script itself cant be set because the size would not fit the mqtt buffers + +***example script*** +meant to show some of the possibilities +(actually this code ist too large) + +**\>D** +; define all vars here +p:mintmp=10 (p:means permanent) +p:maxtmp=30 +t:timer1=30 (t:means countdown timer) +t:mt=0 +i:count=0 (i:means auto counter) +hello="hello world" +string="xxx" +url="[192.168.178.86]" +hum=0 +temp=0 +timer=0 +dimmer=0 +sw=0 +rssi=0 +param=0 + +col="" +ocol="" +chan1=0 +chan2=0 +chan3=0 + +ahum=0 +atemp=0 +tcnt=0 +hour=0 +state=1 +m:med5=0 +M:movav=0 +; define array with 10 entries +m:array=0 10 + +**\>B** + +string=hello+"how are you?" +=\>print BOOT executed +=\>print %hello% +=\>mp3track 1 + +; list gpio pin definitions +for cnt 0 16 1 +tmp=pd[cnt] +=>print %cnt% = %tmp% +next + +; get gpio pin for relais 1 +tmp=pn[21] +=>print relais 1 is on pin %tmp% + +; pulse relais over raw gpio +spin(tmp 1) +delay(100) +spin(tmp 0) + +; raw pin level +=>print level of gpio1 %pin[1]% + +; pulse over tasmota cmd +=>power 1 +delay(100) +=>power 0 + +**\>T** +hum=BME280#Humidity +temp=BME280#Temperature +rssi=Wifi#RSSI +string=SleepMode + +; add to median filter +median=temp +; add to moving average filter +movav=hum + +; show filtered results +=>print %median% %movav% + +if chg[rssi]>0 +then =>print rssi changed to %rssi% +endif + +if temp\>30 +and hum\>70 +then =\>print damn hot! +endif + +**\>S** + +; every second but not completely reliable time here +; use upsecs and uptime or best t: for reliable timers + +; arrays +array[1]=4 +array[2]=5 +tmp=array[1]+array[2] + +; call subrountines with parameters +=#sub1("hallo") +=#sub2(999) + +; stop timer after expired +if timer1==0 +then timer1=-1 +=>print timer1 expired +endif + +; auto counter with restart +if count>=10 +then =>print 10 seconds over +count=0 +endif + +if upsecs%5==0 +then =\>print %upsecs% (every 5 seconds) +endif + +; not recommended for reliable timers +timer+=1 +if timer\>=5 +then =\>print 5 seconds over (may be) +timer=0 +endif + +dimmer+=1 +if dimmer\>100 +then dimmer=0 +endif + +=\>dimmer %dimmer% +=\>WebSend %url% dimmer %dimmer% + +; show on display +dprec0 +=\>displaytext [c1l1f1s2p20] dimmer=%dimmer% + +=\>print %upsecs% %uptime% %time% %sunrise% %sunset% %tstamp% + +if time\>sunset +and time< sunrise +then +; night time +if pwr[1]==0 +then =\>power1 1 +endif +else +; day time +if pwr[1]\>0 +then =\>power1 0 +endif +endif + +; clr display on boot +if boot\>0 +then =\>displaytext [z] +endif + +; frost warning +if temp<0 +and mt<=0 +then =#sendmail("frost alert") +; alarm only every 5 minutes +mt=300 +=>mp3track 2 +endif + +; var has been updated +if upd[hello]>0 +then =>print %hello% +endif + +; send to Thingspeak every 60 seconds +; average data in between +if upsecs%60==0 +then +ahum/=tcnt +atemp/=tcnt +=>Websend [184.106.153.149:80]/update?key=PYUZMVWCICBW492&field1=%atemp%&field2=%ahum% +tcnt=0 +atemp=0 +ahum=0 +else +ahum+=hum +atemp+=temp +tcnt+=1 +endif + +hour=int(time/60) +if chg[hour]>0 +then +; exactly every hour +=>print full hour reached +endif + +if time>5 { +=>print more then 5 minutes after midnight +} else { +=>print less then 5 minutes after midnight +} + + +; publish abs hum every teleperiod time +if mqtts>0 +and upsecs%tper==0 +then +; calc abs humidity +tmp=pow(2.718281828 (17.67\*temp)/(temp+243.5)) +tmp=(6.112\*tmp\*hum\*18.01534)/((273.15+temp)\*8.31447215) +; publish median filtered value +=>Publish tele/%topic%/SENSOR {"Script":{"abshum":%med(0 tmp)%}} +endif + +;switch case state machine +switch state +case 1 +=>print state=%state% , start +state+=1 +case 2 +=>print state=%state% +state+=1 +case 3 +=>print state=%state% , reset +state=1 +ends + + +; subroutines +\#sub1(string) +=>print sub1: %string% +\#sub2(param) +=>print sub2: %param% + +\#sendmail(string) +=>sendmail [smtp.gmail.com:465:user:passwd:::alarm] %string% + +**\>E** +=\>print event executed! + +; get HSBColor 1. component +tmp=st(HSBColor , 1) + +; check if switch changed state +sw=sw[1] +if chg[sw]>0 +then =\>power1 %sw% +endif + +hello="event occured" + +; check for Color change (Color is a string) +col=Color +; color change needs 2 string vars +if col!=ocol +then ocol=col +=>print color changed %col% +endif + +; or check change of color channels +chan1=Channel[1] +chan2=Channel[2] +chan3=Channel[3] + +if chg[chan1]>0 +or chg[chan2]>0 +or chg[chan3]>0 +then => color has changed +endif + +; compose color string for red +col=hn(255)+hn(0)+hn(0) +=>color %col% + +**\>R** +=\>print restarting now + +**a log sensor example** +; define all vars here +; reserve large strings +**\>D** 48 +hum=0 +temp=0 +fr=0 +res=0 +; moving average for 60 seconds +M:mhum=0 60 +M:mtemp=0 60 +str="" + +**\>B** +; set sensor file download link +fl1("slog.txt") +; delete file in case we want to start fresh +;fd("slog.txt") + + +; list all files in root directory +fr=fo("/" 0) +for cnt 1 20 1 +res=fr(str fr) +if res>0 +then +=>print %cnt% : %str% +else +break +endif +next +fc(fr) + + +**\>T** +; get sensor values +temp=BME280#Temperature +hum=BME280#Humidity + +**\>S** +; average sensor values every second +mhum=hum +mtemp=temp + +; write average to sensor log every minute +if upsecs%60==0 +then +; open file for write +fr=fo("slog.txt" 1) +; compose string for tab delimited file entry +str=s(upsecs)+"\t"+s(mhum)+"\t"+s(mtemp)+"\n" +; write string to log file +res=fw(str fr) +; close file +fc(fr) +endif + +**\>R** + + + +**a real example** +epaper 29 with sgp30 and bme280 +some vars are set from iobroker +DisplayText substituted to save script space +**\>D** +hum=0 +temp=0 +press=0 +ahum=0 +tvoc=0 +eco2=0 +zwz=0 +wr1=0 +wr2=0 +wr3=0 +otmp=0 +pwl=0 +tmp=0 +DT="DisplayText" +; preset units in case they are not available +punit="hPa" +tunit="C" + +**\>B** +;reset auto draw +=>%DT% [zD0] +;clr display and draw a frame +=>%DT% [x0y20h296x0y40h296] + +**\>T** +; get tele vars +temp=BME280#Temperature +hum=BME280#Humidity +press=BME280#Pressure +tvoc=SGP30#TVOC +eco2=SGP30#eCO2 +ahum=SGP30#aHumidity +tunit=TempUnit +punit=PressureUnit + +**\>S** +// update display every teleperiod time +if upsecs%tper==0 +then +dprec2 +=>%DT% [f1p7x0y5]%temp% %tunit% +=>%DT% [p5x70y5]%hum% %%[x250y5t] +=>%DT% [p11x140y5]%press% %punit% +=>%DT% [p10x30y25]TVOC: %tvoc% ppb +=>%DT% [p10x160y25]eCO2: %eco2% ppm +=>%DT% [p10c26l5]ahum: %ahum% g^m3 + +dprec0 +=>%DT% [p25c1l5]WR 1 (Dach) : %wr1% W +=>%DT% [p25c1l6]WR 2 (Garage): %-wr3% W +=>%DT% [p25c1l7]WR 3 (Garten): %-wr2% W +=>%DT% [p25c1l8]Aussentemperatur: %otmp% C +=>%DT% [x170y95r120:30f2p6x185y100] %pwl% %% +; now update screen +=>%DT% [d] +endif + + +**\>E** + +**\>R** + +**another real example** +ILI 9488 color LCD Display shows various energy graphs +display switches on and off with proximity sensor +BMP280 and vl5310x +some vars are set from iobroker + +**>D** +temp=0 +press=0 +zwz=0 +wr1=0 +wr2=0 +wr3=0 +otmp=0 +pwl=0 +tmp=0 +dist=0 +DT="DisplayText" +punit="hPa" +tunit="C" +hour=0 + +**>B** +=>%DT% [z] + +// define 2 graphs, 2. has 3 tracks +=>%DT% [zCi1G2656:5:20:400:80:1440:-5000:5000:3Ci7f3x410y20]+5000 W[x410y95]-5000 W [Ci7f1x70y3] Zweirichtungsz~80hler - 24 Stunden +=>%DT% [Ci1G2657:5:120:400:80:1440:0:5000:3Ci7f3x410y120]+5000 W[x410y195]0 W [Ci7f1x70y103] Wechselrichter 1-3 - 24 Stunden +=>%DT% [Ci1G2658:5:120:400:80:1440:0:5000:16][Ci1G2659:5:120:400:80:1440:0:5000:5] +=>%DT% [f1s1b0:260:260:100:50:2:11:4:2:Rel 1:b1:370:260:100:50:2:11:4:2:Dsp off:] +=>mp3volume 100 +=>mp3track 4 + +**>T** +; get some tele vars +temp=BMP280#Temperature +press=BMP280#Pressure +tunit=TempUnit +punit=PressureUnit +dist=VL53L0X#Distance + +; check proximity sensor to switch display on/off +; to prevent burn in +if dist>300 +then +if pwr[2]>0 +then +=>power2 0 +endif +else +if pwr[2]==0 +then +=>power2 1 +endif +endif + + +**>S** +; update graph every teleperiod +if upsecs%tper==0 +then +dprec2 +=>%DT% [f1Ci3x40y260w30Ci1] +=>%DT% [Ci7x120y220t] +=>%DT% [Ci7x180y220T] +=>%DT% [Ci7p8x120y240]%temp% %tunit% +=>%DT% [Ci7x120y260]%press% %punit% +=>%DT% [Ci7x120y280]%dist% mm +dprec0 +=>%DT% [g0:%zwz%g1:%wr1%g2:%-wr2%g3:%-wr3%] +if zwz>0 +then +=>%DT% [p-8x410y55Ci2Bi0]%zwz% W +else +=>%DT% [p-8x410y55Ci3Bi0]%zwz% W +endif +=>%DT% [p-8x410y140Ci3Bi0]%wr1% W +=>%DT% [p-8x410y155Ci16Bi0]%-wr2% W +=>%DT% [p-8x410y170Ci5Bi0]%-wr3% W +endif + +; chime every full hour +hour=int(time/60) +if chg[hour]>0 +then =>mp3track 4 +endif + +**>E** + +**>R** diff --git a/sonoff/Parsing.cpp b/sonoff/Parsing.cpp index 22c59acda..145b72572 100644 --- a/sonoff/Parsing.cpp +++ b/sonoff/Parsing.cpp @@ -415,7 +415,7 @@ bool ESP8266WebServer::_parseForm(WiFiClient& client, String boundary, uint32_t DEBUG_OUTPUT.println(argFilename); #endif //use GET to set the filename if uploading using blob - if (argFilename == F("blob") && hasArg(FPSTR(filename))) + if (argFilename == F("blob") && hasArg(FPSTR(filename))) argFilename = arg(FPSTR(filename)); } #ifdef DEBUG_ESP_HTTP_SERVER @@ -510,7 +510,7 @@ readfile: uint8_t endBuf[boundary.length()]; client.readBytes(endBuf, boundary.length()); - if (strstr((const char*)endBuf, boundary.c_str()) != NULL){ + if (strstr((const char*)endBuf, boundary.c_str()) != nullptr){ if(_currentHandler && _currentHandler->canUpload(_currentUri)) _currentHandler->upload(*this, _currentUri, *_currentUpload); _currentUpload->totalSize += _currentUpload->currentSize; @@ -571,7 +571,7 @@ readfile: arg.value = postArgs[iarg].value; } _currentArgCount = iarg; - if (postArgs) + if (postArgs) delete[] postArgs; return true; } diff --git a/sonoff/StackThunk_light.cpp b/sonoff/StackThunk_light.cpp new file mode 100644 index 000000000..1f0cafa10 --- /dev/null +++ b/sonoff/StackThunk_light.cpp @@ -0,0 +1,144 @@ +/* + StackThunk_light.c - Allow use second stack for BearSSL calls + Light version with reduced Stack size due to Tasmota optimizations. + + BearSSL uses a significant amount of stack space, much larger than + the default Arduino core stack. These routines handle swapping + between a secondary, user-allocated stack on the heap and the real + stack. + + Copyright (c) 2017 Earle F. Philhower, III. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling) +*/ + +#include "my_user_config.h" + +#include +#include +#include "StackThunk_light.h" +#include + +extern "C" { + +uint32_t *stack_thunk_light_ptr = NULL; +uint32_t *stack_thunk_light_top = NULL; +uint32_t *stack_thunk_light_save = NULL; /* Saved A1 while in BearSSL */ +uint32_t stack_thunk_light_refcnt = 0; + +//#define _stackSize (5600/4) +#ifdef USE_MQTT_AWS_IOT + #define _stackSize (5300/4) // using a light version of bearssl we can save 300 bytes +#else + #define _stackSize (3600/4) // using a light version of bearssl we can save 2k +#endif +#define _stackPaint 0xdeadbeef + +/* Add a reference, and allocate the stack if necessary */ +void stack_thunk_light_add_ref() +{ + stack_thunk_light_refcnt++; + if (stack_thunk_light_refcnt == 1) { + stack_thunk_light_ptr = (uint32_t *)malloc(_stackSize * sizeof(uint32_t)); + stack_thunk_light_top = stack_thunk_light_ptr + _stackSize - 1; + stack_thunk_light_save = NULL; + stack_thunk_light_repaint(); + } +} + +/* Drop a reference, and free stack if no more in use */ +void stack_thunk_light_del_ref() +{ + if (stack_thunk_light_refcnt == 0) { + /* Error! */ + return; + } + stack_thunk_light_refcnt--; + if (!stack_thunk_light_refcnt) { + free(stack_thunk_light_ptr); + stack_thunk_light_ptr = NULL; + stack_thunk_light_top = NULL; + stack_thunk_light_save = NULL; + } +} + +void stack_thunk_light_repaint() +{ + if (stack_thunk_light_ptr) { + for (int i=0; i < _stackSize; i++) { + stack_thunk_light_ptr[i] = _stackPaint; + } + } +} + +/* Simple accessor functions used by postmortem */ +uint32_t stack_thunk_light_get_refcnt() { + return stack_thunk_light_refcnt; +} + +uint32_t stack_thunk_light_get_stack_top() { + return (uint32_t)stack_thunk_light_top; +} + +uint32_t stack_thunk_light_get_stack_bot() { + return (uint32_t)stack_thunk_light_ptr; +} + +uint32_t stack_thunk_light_get_cont_sp() { + return (uint32_t)stack_thunk_light_save; +} + +/* Return the number of bytes ever used since the stack was created */ +uint32_t stack_thunk_light_get_max_usage() +{ + uint32_t cnt = 0; + + /* No stack == no usage by definition! */ + if (!stack_thunk_light_ptr) { + return 0; + } + + for (cnt=0; (cnt < _stackSize) && (stack_thunk_light_ptr[cnt] == _stackPaint); cnt++) { + /* Noop, all work done in for() */ + } + return 4 * (_stackSize - cnt); +} + +/* Print the stack from the first used 16-byte chunk to the top, decodable by the exception decoder */ +void stack_thunk_light_dump_stack() +{ + uint32_t *pos = stack_thunk_light_top; + while (pos < stack_thunk_light_ptr) { + if ((pos[0] != _stackPaint) || (pos[1] != _stackPaint) || (pos[2] != _stackPaint) || (pos[3] != _stackPaint)) + break; + pos += 4; + } + ets_printf(">>>stack>>>\n"); + while (pos < stack_thunk_light_ptr) { + ets_printf("%08x: %08x %08x %08x %08x\n", (int32_t)pos, pos[0], pos[1], pos[2], pos[3]); + pos += 4; + } + ets_printf("<< +#include +#include + +extern "C" { +#include "osapi.h" +#include "ets_sys.h" +} +#include "debug.h" +#include "WiFiClientSecureLightBearSSL.h" // needs to be before "ESP8266WiFi.h" to avoid conflict with Arduino headers +#include "ESP8266WiFi.h" +#include "WiFiClient.h" +#include "StackThunk_light.h" +#include "lwip/opt.h" +#include "lwip/ip.h" +#include "lwip/tcp.h" +#include "lwip/inet.h" +#include "lwip/netif.h" +#include +#include "c_types.h" + +#include +#ifndef ARDUINO_ESP8266_RELEASE_2_5_2 +#undef DEBUG_TLS +#endif + +#ifdef DEBUG_TLS +#include "coredecls.h" +#define LOG_HEAP_SIZE(a) _Log_heap_size(a) +void _Log_heap_size(const char *msg) { + register uint32_t *sp asm("a1"); + int freestack = 4 * (sp - g_pcont->stack); + Serial.printf("%s %d, Fragmentation=%d, Thunkstack=%d, Free stack=%d, FreeContStack=%d\n", + msg, ESP.getFreeHeap(), ESP.getHeapFragmentation(), stack_thunk_light_get_max_usage(), + freestack, ESP.getFreeContStack()); +} +#else +#define LOG_HEAP_SIZE(a) +#endif + +// Stack thunked versions of calls +// Initially in BearSSLHelpers.h +extern "C" { +extern unsigned char *thunk_light_br_ssl_engine_recvapp_buf( const br_ssl_engine_context *cc, size_t *len); +extern void thunk_light_br_ssl_engine_recvapp_ack(br_ssl_engine_context *cc, size_t len); +extern unsigned char *thunk_light_br_ssl_engine_recvrec_buf( const br_ssl_engine_context *cc, size_t *len); +extern void thunk_light_br_ssl_engine_recvrec_ack(br_ssl_engine_context *cc, size_t len); +extern unsigned char *thunk_light_br_ssl_engine_sendapp_buf( const br_ssl_engine_context *cc, size_t *len); +extern void thunk_light_br_ssl_engine_sendapp_ack(br_ssl_engine_context *cc, size_t len); +extern unsigned char *thunk_light_br_ssl_engine_sendrec_buf( const br_ssl_engine_context *cc, size_t *len); +extern void thunk_light_br_ssl_engine_sendrec_ack(br_ssl_engine_context *cc, size_t len); +}; + +// Second stack thunked helpers +make_stack_thunk_light(br_ssl_engine_recvapp_ack); +make_stack_thunk_light(br_ssl_engine_recvapp_buf); +make_stack_thunk_light(br_ssl_engine_recvrec_ack); +make_stack_thunk_light(br_ssl_engine_recvrec_buf); +make_stack_thunk_light(br_ssl_engine_sendapp_ack); +make_stack_thunk_light(br_ssl_engine_sendapp_buf); +make_stack_thunk_light(br_ssl_engine_sendrec_ack); +make_stack_thunk_light(br_ssl_engine_sendrec_buf); + +// create new version of Thunk function to store on SYS stack +// unless the Thunk was initialized. Thanks to AES128 GCM, we can keep +// symetric processing on the stack +void min_br_ssl_engine_recvapp_ack(br_ssl_engine_context *cc, size_t len) { + if (stack_thunk_light_get_refcnt()) { + return thunk_light_br_ssl_engine_recvapp_ack(cc, len); + } else { + return br_ssl_engine_recvapp_ack(cc, len); + } +} +unsigned char *min_br_ssl_engine_recvapp_buf(const br_ssl_engine_context *cc, size_t *len) { + if (stack_thunk_light_get_refcnt()) { + return thunk_light_br_ssl_engine_recvapp_buf(cc, len); + } else { + return br_ssl_engine_recvapp_buf(cc, len); + } +} +void min_br_ssl_engine_recvrec_ack(br_ssl_engine_context *cc, size_t len) { + if (stack_thunk_light_get_refcnt()) { + return thunk_light_br_ssl_engine_recvrec_ack(cc, len); + } else { + return br_ssl_engine_recvrec_ack(cc, len); + } +} +unsigned char *min_br_ssl_engine_recvrec_buf(const br_ssl_engine_context *cc, size_t *len) { + if (stack_thunk_light_get_refcnt()) { + return thunk_light_br_ssl_engine_recvrec_buf(cc, len); + } else { + return br_ssl_engine_recvrec_buf(cc, len); + } +} +void min_br_ssl_engine_sendapp_ack(br_ssl_engine_context *cc, size_t len) { + if (stack_thunk_light_get_refcnt()) { + return thunk_light_br_ssl_engine_sendapp_ack(cc, len); + } else { + return br_ssl_engine_sendapp_ack(cc, len); + } +} +unsigned char *min_br_ssl_engine_sendapp_buf(const br_ssl_engine_context *cc, size_t *len) { + if (stack_thunk_light_get_refcnt()) { + return thunk_light_br_ssl_engine_sendapp_buf(cc, len); + } else { + return br_ssl_engine_sendapp_buf(cc, len); + } +} +void min_br_ssl_engine_sendrec_ack(br_ssl_engine_context *cc, size_t len) { + if (stack_thunk_light_get_refcnt()) { + return thunk_light_br_ssl_engine_sendrec_ack(cc, len); + } else { + return br_ssl_engine_sendrec_ack(cc, len); + } +} +unsigned char *min_br_ssl_engine_sendrec_buf(const br_ssl_engine_context *cc, size_t *len) { + if (stack_thunk_light_get_refcnt()) { + return thunk_light_br_ssl_engine_sendrec_buf(cc, len); + } else { + return br_ssl_engine_sendrec_buf(cc, len); + } +} + +// Use min_ instead of original thunk_ +#define br_ssl_engine_recvapp_ack min_br_ssl_engine_recvapp_ack +#define br_ssl_engine_recvapp_buf min_br_ssl_engine_recvapp_buf +#define br_ssl_engine_recvrec_ack min_br_ssl_engine_recvrec_ack +#define br_ssl_engine_recvrec_buf min_br_ssl_engine_recvrec_buf +#define br_ssl_engine_sendapp_ack min_br_ssl_engine_sendapp_ack +#define br_ssl_engine_sendapp_buf min_br_ssl_engine_sendapp_buf +#define br_ssl_engine_sendrec_ack min_br_ssl_engine_sendrec_ack +#define br_ssl_engine_sendrec_buf min_br_ssl_engine_sendrec_buf + +//#define DEBUG_ESP_SSL +#ifdef DEBUG_ESP_SSL +#define DEBUG_BSSL(fmt, ...) DEBUG_ESP_PORT.printf_P((PGM_P)PSTR( "BSSL:" fmt), ## __VA_ARGS__) +//#define DEBUG_BSSL(fmt, ...) Serial.printf(fmt, ## __VA_ARGS__) +#else +#define DEBUG_BSSL(...) +#endif + +namespace BearSSL { + +void WiFiClientSecure_light::_clear() { + // TLS handshake may take more than the 5 second default timeout + _timeout = 10000; // 10 seconds max, it should never go over 6 seconds + + _sc = nullptr; + _ctx_present = false; + _eng = nullptr; + _iobuf_in = nullptr; + _iobuf_out = nullptr; + _now = 0; // You can override or ensure time() is correct w/configTime + setBufferSizes(1024, 1024); // reasonable minimum + _handshake_done = false; + _last_error = 0; + _recvapp_buf = nullptr; + _recvapp_len = 0; + _fingerprint_any = true; // by default accept all fingerprints + _fingerprint1 = nullptr; + _fingerprint2 = nullptr; + _chain_P = nullptr; + _sk_ec_P = nullptr; + _ta_P = nullptr; + _max_thunkstack_use = 0; +} + +// Constructor +WiFiClientSecure_light::WiFiClientSecure_light(int recv, int xmit) : WiFiClient() { + _clear(); +LOG_HEAP_SIZE("StackThunk before"); + //stack_thunk_light_add_ref(); +LOG_HEAP_SIZE("StackThunk after"); + // now finish the setup + setBufferSizes(recv, xmit); // reasonable minimum + allocateBuffers(); +} + +WiFiClientSecure_light::~WiFiClientSecure_light() { + if (_client) { + _client->unref(); + _client = nullptr; + } + //_cipher_list = nullptr; // std::shared will free if last reference + _freeSSL(); +} + +void WiFiClientSecure_light::allocateBuffers(void) { + // We prefer to allocate all buffers at start, rather than lazy allocation and deallocation + // in the long run it avoids heap fragmentation and improves stability + LOG_HEAP_SIZE("allocateBuffers before"); + _sc = std::make_shared(); + LOG_HEAP_SIZE("allocateBuffers ClientContext"); + _iobuf_in = std::shared_ptr(new unsigned char[_iobuf_in_size], std::default_delete()); + _iobuf_out = std::shared_ptr(new unsigned char[_iobuf_out_size], std::default_delete()); + LOG_HEAP_SIZE("allocateBuffers after"); +} + +void WiFiClientSecure_light::setClientECCert(const br_x509_certificate *cert, const br_ec_private_key *sk, + unsigned allowed_usages, unsigned cert_issuer_key_type) { + _chain_P = cert; + _sk_ec_P = sk; + _allowed_usages = allowed_usages; + _cert_issuer_key_type = cert_issuer_key_type; +} + +void WiFiClientSecure_light::setTrustAnchor(const br_x509_trust_anchor *ta) { + _ta_P = ta; +} + +void WiFiClientSecure_light::setBufferSizes(int recv, int xmit) { + // Following constants taken from bearssl/src/ssl/ssl_engine.c (not exported unfortunately) + const int MAX_OUT_OVERHEAD = 85; + const int MAX_IN_OVERHEAD = 325; + + // The data buffers must be between 512B and 16KB + recv = std::max(512, std::min(16384, recv)); + xmit = std::max(512, std::min(16384, xmit)); + + // Add in overhead for SSL protocol + recv += MAX_IN_OVERHEAD; + xmit += MAX_OUT_OVERHEAD; + _iobuf_in_size = recv; + _iobuf_out_size = xmit; +} + +bool WiFiClientSecure_light::stop(unsigned int maxWaitMs) { +#ifdef ARDUINO_ESP8266_RELEASE_2_4_2 + WiFiClient::stop(); // calls our virtual flush() + _freeSSL(); + return true; +#else + bool ret = WiFiClient::stop(maxWaitMs); // calls our virtual flush() + _freeSSL(); + return ret; +#endif +} + +bool WiFiClientSecure_light::flush(unsigned int maxWaitMs) { + (void) _run_until(BR_SSL_SENDAPP); +#ifdef ARDUINO_ESP8266_RELEASE_2_4_2 + WiFiClient::flush(); +#else + return WiFiClient::flush(maxWaitMs); +#endif +} + +int WiFiClientSecure_light::connect(IPAddress ip, uint16_t port) { + clearLastError(); + if (!WiFiClient::connect(ip, port)) { + setLastError(ERR_TCP_CONNECT); + return 0; + } + return _connectSSL(nullptr); +} + +int WiFiClientSecure_light::connect(const char* name, uint16_t port) { + IPAddress remote_addr; + clearLastError(); + if (!WiFi.hostByName(name, remote_addr)) { + DEBUG_BSSL("connect: Name loopup failure\n"); + setLastError(ERR_CANT_RESOLVE_IP); + return 0; + } + if (!WiFiClient::connect(remote_addr, port)) { + DEBUG_BSSL("connect: Unable to connect TCP socket\n"); + _last_error = ERR_TCP_CONNECT; + return 0; + } + LOG_HEAP_SIZE("Before calling _connectSSL"); + return _connectSSL(name); +} + +void WiFiClientSecure_light::_freeSSL() { + _ctx_present = false; + _recvapp_buf = nullptr; + _recvapp_len = 0; + // This connection is toast + _handshake_done = false; +} + +bool WiFiClientSecure_light::_clientConnected() { + return (_client && _client->state() == ESTABLISHED); +} + +uint8_t WiFiClientSecure_light::connected() { + if (available() || (_clientConnected() && _handshake_done)) { + return true; + } + return false; +} + +size_t WiFiClientSecure_light::_write(const uint8_t *buf, size_t size, bool pmem) { + size_t sent_bytes = 0; + + if (!connected() || !size || !_handshake_done) { + return 0; + } + + do { + // Ensure we yield if we need multiple fragments to avoid WDT + if (sent_bytes) { + optimistic_yield(1000); + } + + // Get BearSSL to a state where we can send + if (_run_until(BR_SSL_SENDAPP) < 0) { + break; + } + + if (br_ssl_engine_current_state(_eng) & BR_SSL_SENDAPP) { + size_t sendapp_len; + unsigned char *sendapp_buf = br_ssl_engine_sendapp_buf(_eng, &sendapp_len); + int to_send = size > sendapp_len ? sendapp_len : size; + if (pmem) { + memcpy_P(sendapp_buf, buf, to_send); + } else { + memcpy(sendapp_buf, buf, to_send); + } + br_ssl_engine_sendapp_ack(_eng, to_send); + br_ssl_engine_flush(_eng, 0); + flush(); + buf += to_send; + sent_bytes += to_send; + size -= to_send; + } else { + break; + } + } while (size); + + LOG_HEAP_SIZE("_write"); + return sent_bytes; +} + +size_t WiFiClientSecure_light::write(const uint8_t *buf, size_t size) { + return _write(buf, size, false); +} + +size_t WiFiClientSecure_light::write_P(PGM_P buf, size_t size) { + return _write((const uint8_t *)buf, size, true); +} + +// We have to manually read and send individual chunks. +size_t WiFiClientSecure_light::write(Stream& stream) { + size_t totalSent = 0; + size_t countRead; + size_t countSent; + + if (!connected() || !_handshake_done) { + DEBUG_BSSL("write: Connect/handshake not completed yet\n"); + return 0; + } + + do { + uint8_t temp[256]; // Temporary chunk size same as ClientContext + countSent = 0; + countRead = stream.readBytes(temp, sizeof(temp)); + if (countRead) { + countSent = _write((const uint8_t*)temp, countRead, true); + totalSent += countSent; + } + yield(); // Feed the WDT + } while ((countSent == countRead) && (countSent > 0)); + return totalSent; +} + +int WiFiClientSecure_light::read(uint8_t *buf, size_t size) { + if (!ctx_present() || !_handshake_done) { + return -1; + } + + int avail = available(); + bool conn = connected(); + if (!avail && conn) { + return 0; // We're still connected, but nothing to read + } + if (!avail && !conn) { + DEBUG_BSSL("read: Not connected, none left available\n"); + return -1; + } + + if (avail) { + // Take data from the recvapp buffer + int to_copy = _recvapp_len < size ? _recvapp_len : size; + memcpy(buf, _recvapp_buf, to_copy); + br_ssl_engine_recvapp_ack(_eng, to_copy); + _recvapp_buf = nullptr; + _recvapp_len = 0; + return to_copy; + } + + if (!conn) { + DEBUG_BSSL("read: Not connected\n"); + return -1; + } + return 0; // If we're connected, no error but no read. +} + +int WiFiClientSecure_light::read() { + uint8_t c; + if (1 == read(&c, 1)) { + return c; + } + DEBUG_BSSL("read: failed\n"); + return -1; +} + +int WiFiClientSecure_light::available() { + if (_recvapp_buf) { + return _recvapp_len; // Anything from last call? + } + _recvapp_buf = nullptr; + _recvapp_len = 0; + if (!ctx_present() || _run_until(BR_SSL_RECVAPP, false) < 0) { + return 0; + } + int st = br_ssl_engine_current_state(_eng); + if (st == BR_SSL_CLOSED) { + return 0; // Nothing leftover, SSL is closed + } + if (st & BR_SSL_RECVAPP) { + _recvapp_buf = br_ssl_engine_recvapp_buf(_eng, &_recvapp_len); + return _recvapp_len; + } + + return 0; +} + +int WiFiClientSecure_light::peek() { + if (!ctx_present() || !available()) { + DEBUG_BSSL("peek: Not connected, none left available\n"); + return -1; + } + if (_recvapp_buf && _recvapp_len) { + return _recvapp_buf[0]; + } + DEBUG_BSSL("peek: No data left\n"); + return -1; +} + +size_t WiFiClientSecure_light::peekBytes(uint8_t *buffer, size_t length) { + size_t to_copy = 0; + if (!ctx_present()) { + DEBUG_BSSL("peekBytes: Not connected\n"); + return 0; + } + + _startMillis = millis(); + while ((available() < (int) length) && ((millis() - _startMillis) < 5000)) { + yield(); + } + + to_copy = _recvapp_len < length ? _recvapp_len : length; + memcpy(buffer, _recvapp_buf, to_copy); + return to_copy; +} + +/* --- Copied almost verbatim from BEARSSL SSL_IO.C --- + Run the engine, until the specified target state is achieved, or + an error occurs. The target state is SENDAPP, RECVAPP, or the + combination of both (the combination matches either). When a match is + achieved, this function returns 0. On error, it returns -1. +*/ +int WiFiClientSecure_light::_run_until(unsigned target, bool blocking) { +//LOG_HEAP_SIZE("_run_until 1"); + if (!ctx_present()) { + DEBUG_BSSL("_run_until: Not connected\n"); + return -1; + } + for (int no_work = 0; blocking || no_work < 2;) { + if (blocking) { + // Only for blocking operations can we afford to yield() + optimistic_yield(100); + } + + int state; + state = br_ssl_engine_current_state(_eng); + if (state & BR_SSL_CLOSED) { + return -1; + } + + if (!(_client->state() == ESTABLISHED) && !WiFiClient::available()) { + return (state & target) ? 0 : -1; + } + + /* + If there is some record data to send, do it. This takes + precedence over everything else. + */ + if (state & BR_SSL_SENDREC) { + unsigned char *buf; + size_t len; + int wlen; + + buf = br_ssl_engine_sendrec_buf(_eng, &len); + wlen = WiFiClient::write(buf, len); + if (wlen <= 0) { + /* + If we received a close_notify and we + still send something, then we have our + own response close_notify to send, and + the peer is allowed by RFC 5246 not to + wait for it. + */ + return -1; + } + if (wlen > 0) { + br_ssl_engine_sendrec_ack(_eng, wlen); + } + no_work = 0; + continue; + } + + /* + If we reached our target, then we are finished. + */ + if (state & target) { + return 0; + } + /* + If some application data must be read, and we did not + exit, then this means that we are trying to write data, + and that's not possible until the application data is + read. This may happen if using a shared in/out buffer, + and the underlying protocol is not strictly half-duplex. + This is unrecoverable here, so we report an error. + */ + if (state & BR_SSL_RECVAPP) { + DEBUG_BSSL("_run_until: Fatal protocol state\n"); + return -1; + } + /* + If we reached that point, then either we are trying + to read data and there is some, or the engine is stuck + until a new record is obtained. + */ + if (state & BR_SSL_RECVREC) { + if (WiFiClient::available()) { + unsigned char *buf; + size_t len; + int rlen; + + buf = br_ssl_engine_recvrec_buf(_eng, &len); + rlen = WiFiClient::read(buf, len); + if (rlen < 0) { + return -1; + } + if (rlen > 0) { + br_ssl_engine_recvrec_ack(_eng, rlen); + } + no_work = 0; + continue; + } + } + /* + We can reach that point if the target RECVAPP, and + the state contains SENDAPP only. This may happen with + a shared in/out buffer. In that case, we must flush + the buffered data to "make room" for a new incoming + record. + */ + br_ssl_engine_flush(_eng, 0); + + no_work++; // We didn't actually advance here + } + // We only get here if we ran through the loop without getting anything done + return -1; +} + +bool WiFiClientSecure_light::_wait_for_handshake() { + _handshake_done = false; + while (!_handshake_done && _clientConnected()) { + int ret = _run_until(BR_SSL_SENDAPP); + if (ret < 0) { + DEBUG_BSSL("_wait_for_handshake: failed\n"); + break; + } + if (br_ssl_engine_current_state(_eng) & BR_SSL_SENDAPP) { + _handshake_done = true; + } + optimistic_yield(1000); + } + return _handshake_done; +} + +static uint8_t htoi (unsigned char c) +{ + if (c>='0' && c <='9') return c - '0'; + else if (c>='A' && c<='F') return 10 + c - 'A'; + else if (c>='a' && c<='f') return 10 + c - 'a'; + else return 255; +} + +extern "C" { + + // see https://stackoverflow.com/questions/6357031/how-do-you-convert-a-byte-array-to-a-hexadecimal-string-in-c + void tohex(unsigned char * in, size_t insz, char * out, size_t outsz) { + unsigned char * pin = in; + static const char * hex = "0123456789ABCDEF"; + char * pout = out; + for(; pin < in+insz; pout +=3, pin++){ + pout[0] = hex[(*pin>>4) & 0xF]; + pout[1] = hex[ *pin & 0xF]; + pout[2] = ':'; + if (pout + 3 - out > outsz){ + /* Better to truncate output string than overflow buffer */ + /* it would be still better to either return a status */ + /* or ensure the target buffer is large enough and it never happen */ + break; + } + } + pout[-1] = 0; + } + + + // BearSSL doesn't define a true insecure decoder, so we make one ourselves + // from the simple parser. It generates the issuer and subject hashes and + // the SHA1 fingerprint, only one (or none!) of which will be used to + // "verify" the certificate. + + // Private x509 decoder state + struct br_x509_pubkeyfingerprint_context { + const br_x509_class *vtable; + bool done_cert; // did we parse the first cert already? + bool fingerprint_all; + uint8_t *pubkey_recv_fingerprint; + const uint8_t *fingerprint1; + const uint8_t *fingerprint2; + unsigned usages; // pubkey usage + br_x509_decoder_context ctx; // defined in BearSSL + }; + + // Callback on the first byte of any certificate + static void pubkeyfingerprint_start_chain(const br_x509_class **ctx, const char *server_name) { + br_x509_pubkeyfingerprint_context *xc = (br_x509_pubkeyfingerprint_context *)ctx; + // Don't process anything but the first certificate in the chain + if (!xc->done_cert) { + br_x509_decoder_init(&xc->ctx, nullptr, nullptr, nullptr, nullptr); + } + (void)server_name; // ignore server name + } + + // Callback for each certificate present in the chain (but only operates + // on the first one by design). + static void pubkeyfingerprint_start_cert(const br_x509_class **ctx, uint32_t length) { + (void) ctx; // do nothing + (void) length; + } + + // Callback for each byte stream in the chain. Only process first cert. + static void pubkeyfingerprint_append(const br_x509_class **ctx, const unsigned char *buf, size_t len) { + br_x509_pubkeyfingerprint_context *xc = (br_x509_pubkeyfingerprint_context *)ctx; + // Don't process anything but the first certificate in the chain + if (!xc->done_cert) { + br_x509_decoder_push(&xc->ctx, (const void*)buf, len); + } + } + + // Callback on individual cert end. + static void pubkeyfingerprint_end_cert(const br_x509_class **ctx) { + br_x509_pubkeyfingerprint_context *xc = (br_x509_pubkeyfingerprint_context *)ctx; + xc->done_cert = true; // first cert already processed + } + + static void pubkeyfingerprint_pubkey_fingerprint(br_sha1_context *shactx, br_rsa_public_key rsakey) { + br_sha1_init(shactx); + br_sha1_update(shactx, "ssh-rsa", 7); // tag + br_sha1_update(shactx, rsakey.e, rsakey.elen); // exponent + br_sha1_update(shactx, rsakey.n, rsakey.nlen); // modulus + } + + // Callback when complete chain has been parsed. + // Return 0 on validation success, !0 on validation error + static unsigned pubkeyfingerprint_end_chain(const br_x509_class **ctx) { + br_x509_pubkeyfingerprint_context *xc = (br_x509_pubkeyfingerprint_context *)ctx; + + br_sha1_context sha1_context; + pubkeyfingerprint_pubkey_fingerprint(&sha1_context, xc->ctx.pkey.key.rsa); + br_sha1_out(&sha1_context, xc->pubkey_recv_fingerprint); // copy to fingerprint + + if (!xc->fingerprint_all) { + if (0 == memcmp(xc->fingerprint1, xc->pubkey_recv_fingerprint, 20)) { + return 0; + } + if (0 == memcmp(xc->fingerprint2, xc->pubkey_recv_fingerprint, 20)) { + return 0; + } + return 1; // no match, error + } else { + // Default (no validation at all) or no errors in prior checks = success. + return 0; + } + } + + // Return the public key from the validator (set by x509_minimal) + static const br_x509_pkey *pubkeyfingerprint_get_pkey(const br_x509_class *const *ctx, unsigned *usages) { + const br_x509_pubkeyfingerprint_context *xc = (const br_x509_pubkeyfingerprint_context *)ctx; + + if (usages != NULL) { + *usages = BR_KEYTYPE_KEYX | BR_KEYTYPE_SIGN; // I said we were insecure! + } + return &xc->ctx.pkey; + } + + // Set up the x509 insecure data structures for BearSSL core to use. + void br_x509_pubkeyfingerprint_init(br_x509_pubkeyfingerprint_context *ctx, + const uint8_t *fingerprint1, const uint8_t *fingerprint2, + uint8_t *recv_fingerprint, + bool fingerprint_all) { + static const br_x509_class br_x509_pubkeyfingerprint_vtable PROGMEM = { + sizeof(br_x509_pubkeyfingerprint_context), + pubkeyfingerprint_start_chain, + pubkeyfingerprint_start_cert, + pubkeyfingerprint_append, + pubkeyfingerprint_end_cert, + pubkeyfingerprint_end_chain, + pubkeyfingerprint_get_pkey + }; + + memset(ctx, 0, sizeof * ctx); + ctx->vtable = &br_x509_pubkeyfingerprint_vtable; + ctx->done_cert = false; + ctx->fingerprint1 = fingerprint1; + ctx->fingerprint2 = fingerprint2; + ctx->pubkey_recv_fingerprint = recv_fingerprint; + ctx->fingerprint_all = fingerprint_all; + } + + // We limit to a single cipher to reduce footprint + // we reference it, don't put in PROGMEM + static const uint16_t suites[] = { +#ifdef USE_MQTT_AWS_IOT + BR_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 +#else + BR_TLS_RSA_WITH_AES_128_GCM_SHA256 +#endif + }; + + // Default initializion for our SSL clients + static void br_ssl_client_base_init(br_ssl_client_context *cc) { + br_ssl_client_zero(cc); + // forbid SSL renegociation, as we free the Private Key after handshake + br_ssl_engine_add_flags(&cc->eng, BR_OPT_NO_RENEGOTIATION); + + br_ssl_engine_set_versions(&cc->eng, BR_TLS12, BR_TLS12); + br_ssl_engine_set_suites(&cc->eng, suites, (sizeof suites) / (sizeof suites[0])); + br_ssl_client_set_default_rsapub(cc); + br_ssl_engine_set_default_rsavrfy(&cc->eng); + + // install hashes + br_ssl_engine_set_hash(&cc->eng, br_sha256_ID, &br_sha256_vtable); + br_ssl_engine_set_prf_sha256(&cc->eng, &br_tls12_sha256_prf); + + // AES CTR/GCM small version, not contstant time (we don't really care here as there is no TPM anyways) + br_ssl_engine_set_gcm(&cc->eng, &br_sslrec_in_gcm_vtable, &br_sslrec_out_gcm_vtable); + br_ssl_engine_set_aes_ctr(&cc->eng, &br_aes_small_ctr_vtable); + br_ssl_engine_set_ghash(&cc->eng, &br_ghash_ctmul32); + +#ifdef USE_MQTT_AWS_IOT + // we support only P256 EC curve for AWS IoT, no EC curve for Letsencrypt + br_ssl_engine_set_ec(&cc->eng, &br_ec_p256_m15); +#endif + } +} + +// Called by connect() to do the actual SSL setup and handshake. +// Returns if the SSL handshake succeeded. +bool WiFiClientSecure_light::_connectSSL(const char* hostName) { +#ifdef USE_MQTT_AWS_IOT + if ((!_chain_P) || (!_sk_ec_P)) { + setLastError(ERR_MISSING_EC_KEY); + return false; + } +#endif + + // Validation context, either full CA validation or checking only fingerprints +#ifdef USE_MQTT_TLS_CA_CERT + br_x509_minimal_context *x509_minimal; +#else + br_x509_pubkeyfingerprint_context *x509_insecure; +#endif + + LOG_HEAP_SIZE("_connectSSL.start"); + + do { // used to exit on Out of Memory error and keep all cleanup code at the same place + // ============================================================ + // allocate Thunk stack, move to alternate stack and initialize + stack_thunk_light_add_ref(); + LOG_HEAP_SIZE("Thunk allocated"); + DEBUG_BSSL("_connectSSL: start connection\n"); + _freeSSL(); + clearLastError(); + if (!stack_thunk_light_get_stack_bot()) break; + + _ctx_present = true; + _eng = &_sc->eng; // Allocation/deallocation taken care of by the _sc shared_ptr + + br_ssl_client_base_init(_sc.get()); + + // ============================================================ + // Allocatte and initialize Decoder Context + LOG_HEAP_SIZE("_connectSSL before DecoderContext allocation"); + // Only failure possible in the installation is OOM + #ifdef USE_MQTT_TLS_CA_CERT + x509_minimal = (br_x509_minimal_context*) malloc(sizeof(br_x509_minimal_context)); + if (!x509_minimal) break; + br_x509_minimal_init(x509_minimal, &br_sha256_vtable, _ta_P, 1); + br_x509_minimal_set_rsa(x509_minimal, br_ssl_engine_get_rsavrfy(_eng)); + br_x509_minimal_set_hash(x509_minimal, br_sha256_ID, &br_sha256_vtable); + br_ssl_engine_set_x509(_eng, &x509_minimal->vtable); + + #else + x509_insecure = (br_x509_pubkeyfingerprint_context*) malloc(sizeof(br_x509_pubkeyfingerprint_context)); + //x509_insecure = std::unique_ptr(new br_x509_pubkeyfingerprint_context); + if (!x509_insecure) break; + br_x509_pubkeyfingerprint_init(x509_insecure, _fingerprint1, _fingerprint2, _recv_fingerprint, _fingerprint_any); + br_ssl_engine_set_x509(_eng, &x509_insecure->vtable); + #endif + LOG_HEAP_SIZE("_connectSSL after DecoderContext allocation"); + + // ============================================================ + // Set send/receive buffers + br_ssl_engine_set_buffers_bidi(_eng, _iobuf_in.get(), _iobuf_in_size, _iobuf_out.get(), _iobuf_out_size); + + // ============================================================ + // allocate Private key if needed, only if USE_MQTT_AWS_IOT + LOG_HEAP_SIZE("_connectSSL before PrivKey allocation"); + #ifdef USE_MQTT_AWS_IOT + // ============================================================ + // Set the EC Private Key, only USE_MQTT_AWS_IOT + // limited to P256 curve + br_ssl_client_set_single_ec(_sc.get(), _chain_P, 1, + _sk_ec_P, _allowed_usages, + _cert_issuer_key_type, &br_ec_p256_m15, br_ecdsa_sign_asn1_get_default()); + #endif // USE_MQTT_AWS_IOT + + // ============================================================ + // Start TLS connection, ALL + if (!br_ssl_client_reset(_sc.get(), hostName, 0)) break; + + auto ret = _wait_for_handshake(); + #ifdef DEBUG_ESP_SSL + if (!ret) { + DEBUG_BSSL("Couldn't connect. Error = %d\n", getLastError()); + } else { + DEBUG_BSSL("Connected! MFLNStatus = %d\n", getMFLNStatus()); + } + #endif + LOG_HEAP_SIZE("_connectSSL.end"); + _max_thunkstack_use = stack_thunk_light_get_max_usage(); + stack_thunk_light_del_ref(); + //stack_thunk_light_repaint(); + LOG_HEAP_SIZE("_connectSSL.end, freeing StackThunk"); + + #ifdef USE_MQTT_TLS_CA_CERT + free(x509_minimal); + #else + free(x509_insecure); + #endif + LOG_HEAP_SIZE("_connectSSL after release of Priv Key"); + return ret; + } while (0); + + // ============================================================ + // if we arrived here, this means we had an OOM error, cleaning up + setLastError(ERR_OOM); + DEBUG_BSSL("_connectSSL: Out of memory\n"); + stack_thunk_light_del_ref(); +#ifdef USE_MQTT_TLS_CA_CERT + free(x509_minimal); +#else + free(x509_insecure); +#endif + LOG_HEAP_SIZE("_connectSSL clean_on_error"); + return false; +} + +}; + +#include "t_bearssl_tasmota_config.h" + +#endif // USE_MQTT_TLS diff --git a/sonoff/WiFiClientSecureLightBearSSL.h b/sonoff/WiFiClientSecureLightBearSSL.h new file mode 100644 index 000000000..653c75502 --- /dev/null +++ b/sonoff/WiFiClientSecureLightBearSSL.h @@ -0,0 +1,221 @@ +/* + WiFiClientBearSSL- SSL client/server for esp8266 using BearSSL libraries + - Mostly compatible with Arduino WiFi shield library and standard + WiFiClient/ServerSecure (except for certificate handling). + + Copyright (c) 2018 Earle F. Philhower, III + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include + +#ifndef wificlientlightbearssl_h +#define wificlientlightbearssl_h +#ifdef USE_MQTT_TLS +#include +#include "WiFiClient.h" +#include + +namespace BearSSL { + +class WiFiClientSecure_light : public WiFiClient { + public: + WiFiClientSecure_light(int recv, int xmit); + ~WiFiClientSecure_light() override; + + void allocateBuffers(void); + + int connect(IPAddress ip, uint16_t port) override; + int connect(const char* name, uint16_t port) override; + + uint8_t connected() override; + size_t write(const uint8_t *buf, size_t size) override; + size_t write_P(PGM_P buf, size_t size) override; + size_t write(const char *buf) { + return write((const uint8_t*)buf, strlen(buf)); + } + size_t write_P(const char *buf) { + return write_P((PGM_P)buf, strlen_P(buf)); + } + size_t write(Stream& stream); // Note this is not virtual + int read(uint8_t *buf, size_t size) override; + int available() override; + int read() override; + int peek() override; + size_t peekBytes(uint8_t *buffer, size_t length) override; + bool flush(unsigned int maxWaitMs); + bool stop(unsigned int maxWaitMs); + void flush() override { (void)flush(0); } + void stop() override { (void)stop(0); } + + // Only check SHA1 fingerprint of public key + void setPubKeyFingerprint(const uint8_t *f1, const uint8_t *f2, + bool f_any = false) { + _fingerprint1 = f1; + _fingerprint2 = f2; + _fingerprint_any = f_any; + } + const uint8_t * getRecvPubKeyFingerprint(void) { + return _recv_fingerprint; + } + + void setClientECCert(const br_x509_certificate *cert, const br_ec_private_key *sk, + unsigned allowed_usages, unsigned cert_issuer_key_type); + + void setTrustAnchor(const br_x509_trust_anchor *ta); + + // Sets the requested buffer size for transmit and receive + void setBufferSizes(int recv, int xmit); + + // Returns whether MFLN negotiation for the above buffer sizes succeeded (after connection) + int getMFLNStatus() { + return connected() && br_ssl_engine_get_mfln_negotiated(_eng); + } + + int32_t getLastError(void) { + if (_last_error) { + return _last_error; + } else { + return br_ssl_engine_last_error(_eng); + } + } + inline void setLastError(int32_t err) { + _last_error = err; + } + inline void clearLastError(void) { + _last_error = 0; + } + inline size_t getMaxThunkStackUse(void) { + return _max_thunkstack_use; + } + + private: + void _clear(); + bool _ctx_present; + std::shared_ptr _sc; + inline bool ctx_present() { + return _ctx_present; + } + br_ssl_engine_context *_eng; // &_sc->eng, to allow for client or server contexts + std::shared_ptr _iobuf_in; + std::shared_ptr _iobuf_out; + time_t _now; + int _iobuf_in_size; + int _iobuf_out_size; + bool _handshake_done; + uint64_t _last_error; + + bool _fingerprint_any; // accept all fingerprints + const uint8_t *_fingerprint1; // fingerprint1 to be checked against + const uint8_t *_fingerprint2; // fingerprint2 to be checked against + uint8_t _recv_fingerprint[20]; // fingerprint received + + unsigned char *_recvapp_buf; + size_t _recvapp_len; + + bool _clientConnected(); // Is the underlying socket alive? + bool _connectSSL(const char *hostName); // Do initial SSL handshake + void _freeSSL(); + int _run_until(unsigned target, bool blocking = true); + size_t _write(const uint8_t *buf, size_t size, bool pmem); + bool _wait_for_handshake(); // Sets and return the _handshake_done after connecting + + // Optional client certificate + const br_x509_certificate *_chain_P; // PROGMEM certificate + const br_ec_private_key *_sk_ec_P; // PROGMEM private key + const br_x509_trust_anchor *_ta_P; // PROGMEM server CA + unsigned _allowed_usages; + unsigned _cert_issuer_key_type; + + // record the maximum use of ThunkStack for monitoring + size_t _max_thunkstack_use; + +}; + +#define ERR_OOM -1000 +#define ERR_CANT_RESOLVE_IP -1001 +#define ERR_TCP_CONNECT -1002 +#define ERR_MISSING_EC_KEY -1003 +#define ERR_MISSING_CA -1004 + +// For reference, BearSSL error codes: +// #define BR_ERR_OK 0 +// #define BR_ERR_BAD_PARAM 1 +// #define BR_ERR_BAD_STATE 2 +// #define BR_ERR_UNSUPPORTED_VERSION 3 +// #define BR_ERR_BAD_VERSION 4 +// #define BR_ERR_BAD_LENGTH 5 +// #define BR_ERR_TOO_LARGE 6 +// #define BR_ERR_BAD_MAC 7 +// #define BR_ERR_NO_RANDOM 8 +// #define BR_ERR_UNKNOWN_TYPE 9 +// #define BR_ERR_UNEXPECTED 10 +// #define BR_ERR_BAD_CCS 12 +// #define BR_ERR_BAD_ALERT 13 +// #define BR_ERR_BAD_HANDSHAKE 14 +// #define BR_ERR_OVERSIZED_ID 15 +// #define BR_ERR_BAD_CIPHER_SUITE 16 +// #define BR_ERR_BAD_COMPRESSION 17 +// #define BR_ERR_BAD_FRAGLEN 18 +// #define BR_ERR_BAD_SECRENEG 19 +// #define BR_ERR_EXTRA_EXTENSION 20 +// #define BR_ERR_BAD_SNI 21 +// #define BR_ERR_BAD_HELLO_DONE 22 +// #define BR_ERR_LIMIT_EXCEEDED 23 +// #define BR_ERR_BAD_FINISHED 24 +// #define BR_ERR_RESUME_MISMATCH 25 +// #define BR_ERR_INVALID_ALGORITHM 26 +// #define BR_ERR_BAD_SIGNATURE 27 +// #define BR_ERR_WRONG_KEY_USAGE 28 +// #define BR_ERR_NO_CLIENT_AUTH 29 +// #define BR_ERR_IO 31 +// #define BR_ERR_RECV_FATAL_ALERT 256 +// #define BR_ERR_SEND_FATAL_ALERT 512 +// #define BR_ERR_X509_OK 32 +// #define BR_ERR_X509_INVALID_VALUE 33 +// #define BR_ERR_X509_TRUNCATED 34 +// #define BR_ERR_X509_EMPTY_CHAIN 35 +// #define BR_ERR_X509_INNER_TRUNC 36 +// #define BR_ERR_X509_BAD_TAG_CLASS 37 +// #define BR_ERR_X509_BAD_TAG_VALUE 38 +// #define BR_ERR_X509_INDEFINITE_LENGTH 39 +// #define BR_ERR_X509_EXTRA_ELEMENT 40 +// #define BR_ERR_X509_UNEXPECTED 41 +// #define BR_ERR_X509_NOT_CONSTRUCTED 42 +// #define BR_ERR_X509_NOT_PRIMITIVE 43 +// #define BR_ERR_X509_PARTIAL_BYTE 44 +// #define BR_ERR_X509_BAD_BOOLEAN 45 +// #define BR_ERR_X509_OVERFLOW 46 +// #define BR_ERR_X509_BAD_DN 47 +// #define BR_ERR_X509_BAD_TIME 48 +// #define BR_ERR_X509_UNSUPPORTED 49 +// #define BR_ERR_X509_LIMIT_EXCEEDED 50 +// #define BR_ERR_X509_WRONG_KEY_TYPE 51 +// #define BR_ERR_X509_BAD_SIGNATURE 52 +// #define BR_ERR_X509_TIME_UNKNOWN 53 +// #define BR_ERR_X509_EXPIRED 54 +// #define BR_ERR_X509_DN_MISMATCH 55 +// #define BR_ERR_X509_BAD_SERVER_NAME 56 +// #define BR_ERR_X509_CRITICAL_EXTENSION 57 +// #define BR_ERR_X509_NOT_CA 58 +// #define BR_ERR_X509_FORBIDDEN_KEY_USAGE 59 +// #define BR_ERR_X509_WEAK_PUBLIC_KEY 60 +// #define BR_ERR_X509_NOT_TRUSTED 62 + +}; + +#endif // USE_MQTT_TLS +#endif // wificlientlightbearssl_h diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index 68d677454..2040ab0cf 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -1,4 +1,127 @@ -/* 6.5.0 20190319 +/* + * 6.6.0 + * + * 6.5.0.16 20190611 + * Refactored TLS based on BearSSL, warning breaking change for fingerprints validation (see doc) + * Add checkbox to GUI password field enabling visibility during password entry only (#5934) + * Add using heap when more than 199 IRSend values need to be send. May need increase of define MQTT_MAX_PACKET_SIZE too (#5950) + * Fix channel command for dual dimmers (#5940) + * Add define USE_COUNTER to my_user_config.h to save space in sonoff-basic.bin and sonoff-minimal.bin + * Add define USE_DHT to my_user_config.h to save space in sonoff-basic.bin + * Change TLS+AWS IoT optimization for speed, code and memory footprint + * Add command SetOption40 0..250 to disable button functionality if activated for over 0.1 second. Needs SetOption1 1 and SetOption13 0 (#5449) + * Change converted double to float in rules, and replaced trigonometric functions from stdlib with smaller versions saving 7k code space (#6005) + * Add support for Sonoff L1 thanks to reef-actor (#6002) + * Fix Not restoring white value on power off/power on (#5993) + * + * 6.5.0.15 20190606 + * Change pubsubclient MQTT_KEEPALIVE from 10 to 30 seconds in preparation of AWS IoT support + * Add support for AWS IoT with TLS 1.2 on core 2.5.2. Full doc here: https://github.com/arendst/Sonoff-Tasmota/wiki/AWS-IoT + * Add some MQTT housekeeping which might solve issue (#5755) + * Add command SetOption65 0/1 and more Tuya Serial based device support (#5815) + * Fix include of my_user_config.h in sonoff_aws_iot.cpp (#5930) + * Fix exception 9 when syslog is enabled and NTP is just synced (#5917) + * Fix Toggle functionality to button double press when one button and two devices are detected (#5935) + * + * 6.5.0.14 20190602 + * Change webserver HTML input, button, textarea, and select name based on id + * Fix webserver multiple Javascript window.onload functionality + * Fix PZem startup issue (#5875) + * Add command SetOption39 1..255 to control CSE7766 (Pow R2) or HLW8032 (Blitzwolf SHP5) handling of power loads below 6W. Default setting is 128 (#5756) + * Add Toggle functionality to button double press when more devices are detected + * + * 6.5.0.13 20190527 + * Add command SetOption38 6..255 to set IRReceive protocol detection sensitivity mimizing UNKNOWN protocols (#5853) + * Fix missing white channel for WS2812 (#5869) + * Add reset of Energy values when connection to sensor is lost for over 4 seconds (#5874, #5881) + * Work-around for Philips Hue emulation issue by using part of MAC address for LightId (#5849) + * Add support to Stage Arduino Core (next 2.6.0) + * + * 6.5.0.12 20190521 + * Add AriLux RF control GPIO option "ALux IrSel" (159) replacing "Led4i" (59) for full LED control (#5709) + * Add LED GPIO option "LedLink" (157) and "LedLinki" (158) to select dedicated link status LED (#5709) + * Add support for up to four LEDs related to four power outputs. Enabled when "LedLink(i)" is configured too (#5709) + * Add extended LED power control using command LedPowerX where X is 1 to 4. Enabled when "LedLink(i)" is configured too (#5709) + * Fix core 2.5.x ISR not in IRAM exception (#5837) + * Add support for VL53L0x time of flight sensor. Might interfere with TSL2561 using same I2C address (#5845) + * Add command AdcParam to control ADC0 Temperature and Light formula parameters + * Change default PowerDelta from 80% to 0% on new installations (#5858, #5028, #4813, #4130, #4145, #3795, #3778, #3660, #3648) + * + * 6.5.0.11 20190517 + * Add command SetOption64 0/1 to switch between "-" or "_" as sensor index separator impacting DS18X20, DHT, BMP and SHT3X sensor names (#5689) + * Add initial support for Scripts as replacement for Rules. Default disabled but can be enabled in my_user_config.h (#5689) + * Add rule System#Save executed just before a planned restart + * Add HX711 weight restore after controlled restart or after power restore just before executing command Sensor34 7 (#5367, #5786) + * Remove define USE_EMULATION from my_user_config.h (#5826) + * Add defines USE_EMULATION_WEMO and USE_EMULATION_HUE to my_user_config.h to control emulation features at compile time (#5826) + * Add support for SPS30 Particle sensor thanks to Gerhard Mutz (#5830) + * + * 6.5.0.10 20190513 + * Enable ADC0 by default in my_user_config.h (#5671) + * Add user configurable ADC0 to Module and Template configuration compatible with current FLAG options (#5671) + * Add support for Shelly 1PM Template {"NAME":"Shelly 1PM","GPIO":[56,0,0,0,82,134,0,0,0,0,0,21,0],"FLAG":2,"BASE":18} (#5716) + * Fix Sonoff Pow R2 / S31 invalid energy increments (#5789) + * Add device OverTemp (>73 Celsius) detection to any Energy Monitoring device with temperature sensor powering off all outputs + * Add rule support for single JSON value pair like {"SSerialReceived":"on"} by expanding it to {"SSerialReceived":{"Data":"on"}} allowing for trigger SSerialReceived#Data=on (#5638) + * + * 6.5.0.9 20190418 + * Add command SetOption63 0/1 to disable relay state feedback scan at restart (#5594, #5663) + * Fix TasmotaSerial at 9600 bps solving DFPlayer comms (#5528) + * Fix Shelly 2.5 overtemp + * Set gamma correction as default behavior, ie "Ledtable 1" + * Refactored management of lights, using classes and integers instead of floats. + * Extend PWM resolution from 8 to 10 bits for low brightness lights + * Allow all 5 PWM channels individually adressable with LEDs. (#5741) + * Fixed inversion of WC/WW channels, back to RGBCW + * Fixed the Unescape() function and the SendSerial3 behaviour + * + * 6.5.0.8 20190413 + * Add Tuya Dimmer 10 second heartbeat serial packet required by some Tuya dimmer secondary MCUs + * Fix use of SerialDelimiter value 128 (#5634) + * Fix lost syslog connection regression from 6.5.0.4 + * Add Shelly 2.5 Energy Monitoring (#5592) + * Add all temperature, humidity and pressure for global access + * Add Shelly 2.5 overtemp functionality + * Fix Shelly 2.5 I2C address priority issue when VEML6070 code is present by disabling VEML6070 for Shelly 2.5 (#5592) + * Support for color and colortone for Philips Hue emulation via Alexa (#5600 #4809) + * + * 6.5.0.7 20190410 + * Add command LedMask to assign which relay has access to power LED (#5602, #5612) + * + * 6.5.0.6 20190409 + * Add WebColor parameters to Settings making them persistent and remove the need for using a rule + * Add alternative IRSend command syntax IRSend raw,,
,
,,,, (#5610) + * + * 6.5.0.5 20190406 + * Add compile time GUI hexadecimal only color options in my_user_config.h (#5586) + * Fix template activation and/or module selection regression from 6.5.0.4 (#5598) + * Add rule Http#Initialized + * Add command WebColor to change non-persistent GUI colors on the fly + Use a rule like: + rule3 on http#initialized do webcolor {"webcolor":["#eeeeee","#181818","#4f4f4f","#000000","#dddddd","#008000","#222222","#ff0000","#008000","#ffffff","#1fa3ec","#0e70a4","#d43535","#931f1f","#47c266","#5aaf6f","#ffffff","#999999"]} endon + or + rule3 on http#initialized do webcolor {"webcolor":["#eee","#181818","#4f4f4f","#000","#ddd","#009800","#222"]} endon + to make color changes persistent) + * + * 6.5.0.4 20190402 + * Fix Configure Timer Web GUI (#5568) + * Add validation check when loading settings from flash + * Fixed Display Bug in KNX webmenu for Physical Address + * + * 6.5.0.3 20190328 + * Add command Sensor20 1..255 to change Nova Fitness SDS01 working period in minutes (#5452) + * Change some defines to const + * Change IRsend and receive for 64-bit support (#5523) + * Change IRSend Panasonic protocol to 64-bit (#5523) + * + * 6.5.0.2 20190325 + * Change UDP initial message handling from string to char using static memory and add debug info (#5505) + * Add optional support for Badger HR-E Water Meter (#5539) + * + * 6.5.0.1 20190319 + * Change Web GUI sensor data collection + * + * 6.5.0 20190319 * Remove commands SetOption14 and SetOption63 as it has been superseded by command Interlock * Remove command SetOption35 0-255 for mDNS start-up delay (#4793) * Remove support for MQTT_LIBRARY_TYPE, MQTT_ARDUINOMQTT and MQTT_TASMOTAMQTT (#5474) @@ -14,7 +137,7 @@ * Change image name BE_MINIMAL to FIRMWARE_MINIMAL and USE_xyz to FIRMWARE_xyz (#5106) * Change GUI weblog from XML to plain text solving possible empty screens (#5154) * Fix most compiler warnings - * Fix Display exception 28 when JSON value is NULL received + * Fix Display exception 28 when JSON value is nullptr received * Fix epaper driver (#4785) * Fix HAss Sensor Discovery Software Watchdog restart (#4831, #4988) * Fix allowable MAX_RULE_VARS to 16 (#4933) diff --git a/sonoff/core_esp8266_wiring_pwm.c b/sonoff/core_esp8266_wiring_pwm.c index d7e179b9b..8bd24815c 100644 --- a/sonoff/core_esp8266_wiring_pwm.c +++ b/sonoff/core_esp8266_wiring_pwm.c @@ -191,6 +191,11 @@ extern void __analogWrite(uint8_t pin, int value) prep_pwm_steps(); return; } + if(value == pwm_range) { + digitalWrite(pin, HIGH); + prep_pwm_steps(); + return; + } if((pwm_mask & (1 << pin)) == 0) { if(pwm_mask == 0) { memset(&_pwm_isr_data, 0, sizeof(_pwm_isr_data)); diff --git a/sonoff/i18n.h b/sonoff/i18n.h index 73e228124..e4342f5ea 100644 --- a/sonoff/i18n.h +++ b/sonoff/i18n.h @@ -69,6 +69,7 @@ #define D_JSON_FLASHCHIPID "FlashChipId" #define D_JSON_FLASHMODE "FlashMode" #define D_JSON_FLASHSIZE "FlashSize" +#define D_JSON_FLOWRATE "FlowRate" #define D_JSON_FREEMEMORY "Free" #define D_JSON_FREQUENCY "Frequency" #define D_JSON_FROM "from" @@ -143,6 +144,7 @@ #define D_JSON_TIME "Time" #define D_JSON_TODAY "Today" #define D_JSON_TOTAL "Total" +#define D_JSON_TOTAL_USAGE "TotalUsage" #define D_JSON_TOTAL_REACTIVE "TotalReactivePower" #define D_JSON_TOTAL_START_TIME "TotalStartTime" #define D_JSON_TVOC "TVOC" @@ -213,6 +215,8 @@ #define D_CMND_WEIGHT_RESOLUTION "WeightRes" #define D_CMND_MODULE "Module" #define D_CMND_MODULES "Modules" +#define D_CMND_ADC "ADC" +#define D_CMND_ADCS "ADCs" #define D_CMND_GPIO "GPIO" #define D_JSON_NOT_SUPPORTED "Not supported" #define D_CMND_GPIOS "GPIOs" @@ -264,6 +268,7 @@ #define D_CMND_ALTITUDE "Altitude" #define D_CMND_LEDPOWER "LedPower" #define D_CMND_LEDSTATE "LedState" +#define D_CMND_LEDMASK "LedMask" #define D_CMND_I2CSCAN "I2CScan" #define D_CMND_SERIALSEND "SerialSend" #define D_CMND_SERIALDELIMITER "SerialDelimiter" @@ -309,6 +314,7 @@ #define D_CMND_WEBLOG "WebLog" #define D_CMND_WEBREFRESH "WebRefresh" #define D_CMND_WEBSEND "WebSend" +#define D_CMND_WEBCOLOR "WebColor" #define D_CMND_EMULATION "Emulation" // Commands xdrv_03_energy.ino @@ -422,7 +428,6 @@ /********************************************************************************************/ -#define D_ASTERIX "********" #define D_ASTERISK_PWD "****" #ifndef MY_LANGUAGE @@ -495,25 +500,28 @@ const char S_JSON_COMMAND_NVALUE_ACTIVE_NVALUE[] PROGMEM = "{\"%s\":\"%d (" D const char S_JSON_COMMAND_NVALUE[] PROGMEM = "{\"%s\":%d}"; const char S_JSON_COMMAND_LVALUE[] PROGMEM = "{\"%s\":%lu}"; const char S_JSON_COMMAND_SVALUE[] PROGMEM = "{\"%s\":\"%s\"}"; -const char S_JSON_COMMAND_HVALUE[] PROGMEM = "{\"%s\":\"#%X\"}"; -const char S_JSON_COMMAND_ASTERIX[] PROGMEM = "{\"%s\":\"" D_ASTERIX "\"}"; +const char S_JSON_COMMAND_ASTERISK[] PROGMEM = "{\"%s\":\"" D_ASTERISK_PWD "\"}"; const char S_JSON_COMMAND_XVALUE[] PROGMEM = "{\"%s\":%s}"; // %s must provide quotes on non-number const char S_JSON_COMMAND_INDEX_NVALUE[] PROGMEM = "{\"%s%d\":%d}"; const char S_JSON_COMMAND_INDEX_LVALUE[] PROGMEM = "{\"%s%d\":%lu}"; const char S_JSON_COMMAND_INDEX_SVALUE[] PROGMEM = "{\"%s%d\":\"%s\"}"; -const char S_JSON_COMMAND_INDEX_ASTERIX[] PROGMEM = "{\"%s%d\":\"" D_ASTERIX "\"}"; +const char S_JSON_COMMAND_INDEX_ASTERISK[] PROGMEM = "{\"%s%d\":\"" D_ASTERISK_PWD "\"}"; const char S_JSON_COMMAND_INDEX_SVALUE_SVALUE[] PROGMEM = "{\"%s%d\":\"%s%s\"}"; const char S_JSON_COMMAND_INDEX_NVALUE_ACTIVE_NVALUE[] PROGMEM = "{\"%s%d\":\"%d (" D_JSON_ACTIVE " %d)\"}"; -const char S_JSON_SENSOR_INDEX_NVALUE[] PROGMEM = "{\"" D_CMND_SENSOR "%d\":%d}"; -const char S_JSON_SENSOR_INDEX_SVALUE[] PROGMEM = "{\"" D_CMND_SENSOR "%d\":\"%s\"}"; +const char S_JSON_SENSOR_INDEX_NVALUE[] PROGMEM = "{\"" D_CMND_SENSOR "%d\":%d}"; +const char S_JSON_SENSOR_INDEX_SVALUE[] PROGMEM = "{\"" D_CMND_SENSOR "%d\":\"%s\"}"; -const char S_JSON_DRIVER_INDEX_NVALUE[] PROGMEM = "{\"" D_CMND_DRIVER "%d\":%d}"; -const char S_JSON_DRIVER_INDEX_SVALUE[] PROGMEM = "{\"" D_CMND_DRIVER "%d\":\"%s\"}"; +const char S_JSON_DRIVER_INDEX_NVALUE[] PROGMEM = "{\"" D_CMND_DRIVER "%d\":%d}"; +const char S_JSON_DRIVER_INDEX_SVALUE[] PROGMEM = "{\"" D_CMND_DRIVER "%d\":\"%s\"}"; -const char JSON_SNS_TEMP[] PROGMEM = "%s,\"%s\":{\"" D_JSON_TEMPERATURE "\":%s}"; -const char JSON_SNS_TEMPHUM[] PROGMEM = "%s,\"%s\":{\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_HUMIDITY "\":%s}"; +const char JSON_SNS_TEMP[] PROGMEM = ",\"%s\":{\"" D_JSON_TEMPERATURE "\":%s}"; +const char JSON_SNS_TEMPHUM[] PROGMEM = ",\"%s\":{\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_HUMIDITY "\":%s}"; + +const char JSON_SNS_ILLUMINANCE[] PROGMEM = ",\"%s\":{\"" D_JSON_ILLUMINANCE "\":%d}"; + +const char JSON_SNS_GNGPM[] PROGMEM = ",\"%s\":{\"" D_JSON_TOTAL_USAGE "\":%s,\"" D_JSON_FLOWRATE "\":%s}"; const char S_LOG_I2C_FOUND_AT[] PROGMEM = D_LOG_I2C "%s " D_FOUND_AT " 0x%x"; @@ -561,20 +569,16 @@ const char kOptionBlinkOff[] PROGMEM = "BLINKOFF|" D_BLINKOFF ; // xdrv_02_webserver.ino #ifdef USE_WEBSERVER -const char HTTP_SNS_TEMP[] PROGMEM = "%s{s}%s " D_TEMPERATURE "{m}%s°%c{e}"; // {s} = , {m} = , {e} = -const char HTTP_SNS_HUM[] PROGMEM = "%s{s}%s " D_HUMIDITY "{m}%s%%{e}"; // {s} = , {m} = , {e} = -const char HTTP_SNS_PRESSURE[] PROGMEM = "%s{s}%s " D_PRESSURE "{m}%s %s{e}"; // {s} = , {m} = , {e} = -const char HTTP_SNS_SEAPRESSURE[] PROGMEM = "%s{s}%s " D_PRESSUREATSEALEVEL "{m}%s %s{e}"; // {s} = , {m} = , {e} = -const char HTTP_SNS_ANALOG[] PROGMEM = "%s{s}%s " D_ANALOG_INPUT "%d{m}%d{e}"; // {s} = , {m} = , {e} = -const char HTTP_SNS_ILLUMINANCE[] PROGMEM = "%s{s}%s " D_ILLUMINANCE "{m}%d " D_UNIT_LUX "{e}"; // {s} = , {m} = , {e} = - -#if defined(USE_MHZ19) || defined(USE_SENSEAIR) || defined(USE_AZ7798) || defined(USE_SCD30) -const char HTTP_SNS_CO2[] PROGMEM = "%s{s}%s " D_CO2 "{m}%d " D_UNIT_PARTS_PER_MILLION "{e}"; // {s} = , {m} = , {e} = -#endif // USE_MHZ19 - -#if defined(USE_SCD30) -const char HTTP_SNS_CO2EAVG[] PROGMEM = "%s{s}%s " D_ECO2 "{m}%d " D_UNIT_PARTS_PER_MILLION "{e}"; // {s} = , {m} = , {e} = -#endif // USE_SCD30 +const char HTTP_SNS_TEMP[] PROGMEM = "{s}%s " D_TEMPERATURE "{m}%s°%c{e}"; // {s} = , {m} = , {e} = +const char HTTP_SNS_HUM[] PROGMEM = "{s}%s " D_HUMIDITY "{m}%s%%{e}"; // {s} = , {m} = , {e} = +const char HTTP_SNS_PRESSURE[] PROGMEM = "{s}%s " D_PRESSURE "{m}%s %s{e}"; // {s} = , {m} = , {e} = +const char HTTP_SNS_SEAPRESSURE[] PROGMEM = "{s}%s " D_PRESSUREATSEALEVEL "{m}%s %s{e}"; // {s} = , {m} = , {e} = +const char HTTP_SNS_ANALOG[] PROGMEM = "{s}%s " D_ANALOG_INPUT "%d{m}%d{e}"; // {s} = , {m} = , {e} = +const char HTTP_SNS_ILLUMINANCE[] PROGMEM = "{s}%s " D_ILLUMINANCE "{m}%d " D_UNIT_LUX "{e}"; // {s} = , {m} = , {e} = +const char HTTP_SNS_CO2[] PROGMEM = "{s}%s " D_CO2 "{m}%d " D_UNIT_PARTS_PER_MILLION "{e}"; // {s} = , {m} = , {e} = +const char HTTP_SNS_CO2EAVG[] PROGMEM = "{s}%s " D_ECO2 "{m}%d " D_UNIT_PARTS_PER_MILLION "{e}"; // {s} = , {m} = , {e} = +const char HTTP_SNS_GALLONS[] PROGMEM = "{s}%s " D_TOTAL_USAGE "{m}%s " D_UNIT_GALLONS " {e}"; // {s} = , {m} = , {e} = +const char HTTP_SNS_GPM[] PROGMEM = "{s}%s " D_FLOW_RATE "{m}%s " D_UNIT_GALLONS_PER_MIN" {e}"; // {s} = , {m} = , {e} = const char S_MAIN_MENU[] PROGMEM = D_MAIN_MENU; const char S_CONFIGURATION[] PROGMEM = D_CONFIGURATION; diff --git a/sonoff/language/bg-BG.h b/sonoff/language/bg-BG.h index 2214634f9..aa9b3095f 100644 --- a/sonoff/language/bg-BG.h +++ b/sonoff/language/bg-BG.h @@ -28,7 +28,7 @@ * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. * Use online command Prefix to translate cmnd, stat and tele. * - * Updated until v6.4.1.18 + * Updated until v6.5.0.8 \*********************************************************************/ //#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) @@ -93,6 +93,7 @@ #define D_FALLBACK_TOPIC "Помощен топик" #define D_FALSE "Невярно" #define D_FILE "Файл" +#define D_FLOW_RATE "Дебит" #define D_FREE_MEMORY "Свободна памет" #define D_FREQUENCY "Честота" #define D_GAS "Газ" @@ -156,6 +157,7 @@ #define D_TO "към" #define D_TOGGLE "Превключване" #define D_TOPIC "Топик" +#define D_TOTAL_USAGE "Използвана вода" #define D_TRANSMIT "Предаване" #define D_TRUE "Вярно" #define D_TVOC "TVOC" @@ -220,7 +222,7 @@ // xdrv_02_webserver.ino #define D_NOSCRIPT "Разрешете JavaScript, за да използвате Tasmota" -#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "Минимален фърмуеър
моля надградете го" +#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "Минимален фърмуер
моля надградете го" #define D_WEBSERVER_ACTIVE_ON "Уеб сървърът е активен на" #define D_WITH_IP_ADDRESS "с IP адрес" #define D_WEBSERVER_STOPPED "Уеб сървърът е спрян" @@ -236,16 +238,16 @@ #define D_BUTTON_TOGGLE "Превключване" #define D_CONFIGURATION "Конфигурация" #define D_INFORMATION "Информация" -#define D_FIRMWARE_UPGRADE "Обновяване на фърмуеъра" +#define D_FIRMWARE_UPGRADE "Обновяване на фърмуера" #define D_CONSOLE "Конзола" -#define D_CONFIRM_RESTART "Подтвърдете рестартирането" +#define D_CONFIRM_RESTART "Потвърдете рестартирането" #define D_CONFIGURE_MODULE "Конфигурация на модула" #define D_CONFIGURE_WIFI "Конфигурация на WiFi" #define D_CONFIGURE_MQTT "Конфигурация на MQTT" #define D_CONFIGURE_DOMOTICZ "Конфигурация на Domoticz" #define D_CONFIGURE_LOGGING "Конфигурация на лога" -#define D_CONFIGURE_OTHER "Драги конфигурации" +#define D_CONFIGURE_OTHER "Други конфигурации" #define D_CONFIRM_RESET_CONFIGURATION "Потвърдете изчистването" #define D_RESET_CONFIGURATION "Изчистване на конфигурацията" #define D_BACKUP_CONFIGURATION "Запазване на конфигурацията" @@ -255,6 +257,7 @@ #define D_MODULE_PARAMETERS "Параметри на модула" #define D_MODULE_TYPE "Тип на модула" #define D_PULLUP_ENABLE "Без pull-up за бутон/ключ" +#define D_ADC "ADC" #define D_GPIO "GPIO" #define D_SERIAL_IN "Сериен вход" #define D_SERIAL_OUT "Сериен изход" @@ -281,7 +284,7 @@ #define D_LOGGING_PARAMETERS "Параметри на лога" #define D_SERIAL_LOG_LEVEL "Степен на серийния лог" -#define D_WEB_LOG_LEVEL "Степен на Уеб лога" +#define D_WEB_LOG_LEVEL "Степен на уеб лога" #define D_SYS_LOG_LEVEL "Степен на системния лог" #define D_MORE_DEBUG "Още дебъгване" #define D_SYSLOG_HOST "Хост на системния лог" @@ -299,13 +302,11 @@ #define D_SINGLE_DEVICE "Единично" #define D_MULTI_DEVICE "Мулти" -#define D_CONFIGURE_TEMPLATE "Конфигуриране на модел" -#define D_TEMPLATE_PARAMETERS "Параметри на модел" +#define D_CONFIGURE_TEMPLATE "Конфигуриране на шаблон" +#define D_TEMPLATE_PARAMETERS "Параметри на шаблона" #define D_TEMPLATE_NAME "Име" #define D_BASE_TYPE "Базиран на" -#define D_TEMPLATE_FLAGS "Флагове" -#define D_ALLOW_ADC0 "ADC0 вход" -#define D_ALLOW_PULLUP "Потребителски избор на pull-up" +#define D_TEMPLATE_FLAGS "Опции" #define D_SAVE_CONFIGURATION "Запазване на конфигурацията" #define D_CONFIGURATION_SAVED "Конфигурацията е запазена" @@ -324,7 +325,7 @@ #define D_MQTT_GROUP_TOPIC "MQTT групов топик" #define D_MQTT_FULL_TOPIC "MQTT пълен топик" #define D_MDNS_DISCOVERY "mDNS откриване" -#define D_MDNS_ADVERTISE "mDNS транслация" +#define D_MDNS_ADVERTISE "mDNS известяване" #define D_ESP_CHIP_ID "ID на ESP чипа" #define D_FLASH_CHIP_ID "ID на чипа на флаш паметта" #define D_FLASH_CHIP_SIZE "Размер на флаш паметта" @@ -349,7 +350,7 @@ #define D_UPLOAD_ERR_10 "Грешка при инициализация на RF чипа" #define D_UPLOAD_ERR_11 "Грешка при изтриване на RF чипа" #define D_UPLOAD_ERR_12 "Грешка при записване в RF чипа" -#define D_UPLOAD_ERR_13 "Грешка при декодиране на RF фирмуера" +#define D_UPLOAD_ERR_13 "Грешка при декодиране на RF фърмуера" #define D_UPLOAD_ERROR_CODE "Код на грешка при зареждането" #define D_ENTER_COMMAND "Въвеждане на команда" @@ -359,7 +360,7 @@ // xdrv_01_mqtt.ino #define D_FINGERPRINT "Проверка на TLS отпечатък..." #define D_TLS_CONNECT_FAILED_TO "Неуспешно TLS свързване към" -#define D_RETRY_IN "Повторно след" +#define D_RETRY_IN "Повтори след" #define D_VERIFIED "Проверен отпечтък" #define D_INSECURE "Нешифрована връзка, недействителен отпечатък" #define D_CONNECT_FAILED_TO "Грешка при свързването към" @@ -368,7 +369,7 @@ #define D_MULTICAST_DISABLED "Multicast е изключен" #define D_MULTICAST_REJOINED "Multicast е повторно съединен" #define D_MULTICAST_JOIN_FAILED "Multicast грешка при присъединяването" -#define D_FAILED_TO_SEND_RESPONSE "Не се получи изпращането на отговор" +#define D_FAILED_TO_SEND_RESPONSE "Неуспех при изпращането на отговор" #define D_WEMO "WeMo" #define D_WEMO_BASIC_EVENT "WeMo главно събитие" @@ -430,7 +431,7 @@ #define D_KNX_COMMAND_READ "Четене" #define D_KNX_COMMAND_OTHER "Друго" #define D_SENT_TO "изпратен до" -#define D_KNX_WARNING "Груповият адрес ( 0 / 0 / 0 ) е резервиран и не може да бъде използван." +#define D_KNX_WARNING "Груповият адрес (0/0/0) е резервиран и не може да бъде използван." #define D_KNX_ENHANCEMENT "Подобрена комуникация" #define D_KNX_TX_SLOT "KNX TX" #define D_KNX_RX_SLOT "KNX RX" @@ -492,6 +493,10 @@ #define D_TX20_SOUTH "Ю" #define D_TX20_WEST "З" +//xsns_43_hre.ino +#define D_LOG_HRE "HRE: " + + // sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box #define D_SENSOR_NONE "Няма" #define D_SENSOR_USER "Потребит." @@ -508,6 +513,7 @@ #define D_SENSOR_BUTTON "Бутон" // Suffix "1" #define D_SENSOR_RELAY "Реле" // Suffix "1i" #define D_SENSOR_LED "Led" // Suffix "1i" +#define D_SENSOR_LED_LINK "LedLink" // Suffix "i" #define D_SENSOR_PWM "PWM" // Suffix "1" #define D_SENSOR_COUNTER "Брояч" // Suffix "1" #define D_SENSOR_IRRECV "IRrecv" @@ -573,15 +579,21 @@ #define D_SENSOR_MY92X1_DI "MY92x1 DI" #define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI" #define D_SENSOR_ARIRFRCV "ALux IrRcv" +#define D_SENSOR_ARIRFSEL "ALux IrSel" #define D_SENSOR_TXD "Serial Tx" #define D_SENSOR_RXD "Serial Rx" #define D_SENSOR_ROTARY "Rotary" // Suffix "1A" +#define D_SENSOR_HRE_CLOCK "HRE Clock" +#define D_SENSOR_HRE_DATA "HRE Data" +#define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CENTIMETER "cm" #define D_UNIT_HERTZ "Hz" #define D_UNIT_HOUR "h" +#define D_UNIT_GALLONS "gal" +#define D_UNIT_GALLONS_PER_MIN "gal/min" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_INCREMENTS "inc" #define D_UNIT_KILOMETER_PER_HOUR "km/h" diff --git a/sonoff/language/cs-CZ.h b/sonoff/language/cs-CZ.h index 41d4fc9de..b404b8a0f 100644 --- a/sonoff/language/cs-CZ.h +++ b/sonoff/language/cs-CZ.h @@ -28,7 +28,7 @@ * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. * Use online command Prefix to translate cmnd, stat and tele. * - * Updated until v6.2.1.14 + * Updated until v6.5.0.9 \*********************************************************************/ //#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) @@ -93,6 +93,7 @@ #define D_FALLBACK_TOPIC "Záložní topic" #define D_FALSE "Nepravda" #define D_FILE "Soubor" +#define D_FLOW_RATE "Flow rate" #define D_FREE_MEMORY "Volná paměť" #define D_FREQUENCY "Kmitočet" #define D_GAS "Plyn" @@ -156,6 +157,7 @@ #define D_TO "do" #define D_TOGGLE "Přepni" #define D_TOPIC "Topic" +#define D_TOTAL_USAGE "Total Usage" #define D_TRANSMIT "Odešli" #define D_TRUE "Pravda" #define D_TVOC "TVOC" @@ -220,7 +222,7 @@ // xdrv_02_webserver.ino #define D_NOSCRIPT "Pro používání prostředí Tasmota povolte JavaScript" -#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "Firmware MINIMÁLNÍ
prosím zaktualizujte" +#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "Firmware MINIMÁLNÍ
prosím zaktualizujte" #define D_WEBSERVER_ACTIVE_ON "Aktivní Web server" #define D_WITH_IP_ADDRESS "na IP adrese" #define D_WEBSERVER_STOPPED "Web server zastaven" @@ -254,7 +256,8 @@ #define D_MODULE_PARAMETERS "Nastavení modulu" #define D_MODULE_TYPE "Typ modulu" -#define D_PULLUP_ENABLE "No Button/Switch pull-up" +#define D_PULLUP_ENABLE "Tlačítko/Spínač bez pull-up" +#define D_ADC "ADC" #define D_GPIO "GPIO" #define D_SERIAL_IN "Serial In" #define D_SERIAL_OUT "Serial Out" @@ -289,8 +292,8 @@ #define D_TELEMETRY_PERIOD "Interval telemetrie" #define D_OTHER_PARAMETERS "Další nastavení" -#define D_TEMPLATE "Template" -#define D_ACTIVATE "Activate" +#define D_TEMPLATE "Šablona" +#define D_ACTIVATE "Aktivovat" #define D_WEB_ADMIN_PASSWORD "Heslo Web administrátora" #define D_MQTT_ENABLE "MQTT aktivní" #define D_FRIENDLY_NAME "Friendly Name" @@ -299,13 +302,11 @@ #define D_SINGLE_DEVICE "single device" #define D_MULTI_DEVICE "multi device" -#define D_CONFIGURE_TEMPLATE "Configure Template" -#define D_TEMPLATE_PARAMETERS "Template parameters" -#define D_TEMPLATE_NAME "Name" -#define D_BASE_TYPE "Based on" -#define D_TEMPLATE_FLAGS "Options" -#define D_ALLOW_ADC0 "ADC0 input" -#define D_ALLOW_PULLUP "User pull-up selection" +#define D_CONFIGURE_TEMPLATE "Nastavení šablony" +#define D_TEMPLATE_PARAMETERS "Parametry šablony" +#define D_TEMPLATE_NAME "Název" +#define D_BASE_TYPE "Vzor z" +#define D_TEMPLATE_FLAGS "Volby" #define D_SAVE_CONFIGURATION "Ulož nastavení" #define D_CONFIGURATION_SAVED "Nastavení uloženo" @@ -492,6 +493,9 @@ #define D_TX20_SOUTH "J" #define D_TX20_WEST "Z" +//xsns_43_hre.ino +#define D_LOG_HRE "HRE: " + // sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box #define D_SENSOR_NONE "Není" #define D_SENSOR_USER "User" @@ -508,6 +512,7 @@ #define D_SENSOR_BUTTON "Tlačítko" // Suffix "1" #define D_SENSOR_RELAY "Relé" // Suffix "1i" #define D_SENSOR_LED "Led" // Suffix "1i" +#define D_SENSOR_LED_LINK "LedLink" // Suffix "i" #define D_SENSOR_PWM "PWM" // Suffix "1", #define D_SENSOR_COUNTER "Počítadlo" // Suffix "1" #define D_SENSOR_IRRECV "IRrecv" @@ -573,24 +578,30 @@ #define D_SENSOR_MY92X1_DI "MY92x1 DI" #define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI" #define D_SENSOR_ARIRFRCV "ALux IrRcv" +#define D_SENSOR_ARIRFSEL "ALux IrSel" #define D_SENSOR_TXD "Serial Tx" #define D_SENSOR_RXD "Serial Rx" #define D_SENSOR_ROTARY "Rotary" // Suffix "1A" +#define D_SENSOR_HRE_CLOCK "HRE Clock" +#define D_SENSOR_HRE_DATA "HRE Data" +#define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CENTIMETER "cm" #define D_UNIT_HERTZ "Hz" #define D_UNIT_HOUR "hod" +#define D_UNIT_GALLONS "gal" +#define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_INCREMENTS "inc" #define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" #define D_UNIT_KILOOHM "kOhm" #define D_UNIT_KILOWATTHOUR "kWh" #define D_UNIT_LUX "lx" -#define D_UNIT_MICROGRAM_PER_CUBIC_METER "ug/m3" -#define D_UNIT_MICROMETER "um" -#define D_UNIT_MICROSECOND "us" +#define D_UNIT_MICROGRAM_PER_CUBIC_METER "µg/m³" +#define D_UNIT_MICROMETER "µm" +#define D_UNIT_MICROSECOND "µs" #define D_UNIT_MILLIAMPERE "mA" #define D_UNIT_MILLIMETER "mm" #define D_UNIT_MILLIMETER_MERCURY "mmHg" diff --git a/sonoff/language/de-DE.h b/sonoff/language/de-DE.h index 0808c91c4..bccdbdf30 100644 --- a/sonoff/language/de-DE.h +++ b/sonoff/language/de-DE.h @@ -28,7 +28,7 @@ * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. * Use online command Prefix to translate cmnd, stat and tele. * - * Updated until v6.4.1.18 + * Updated until v6.5.0.7 \*********************************************************************/ //#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) @@ -54,7 +54,7 @@ #define D_ADMIN "Admin" #define D_AIR_QUALITY "Luftqualität" #define D_AP "AP" // Access Point -#define D_AS "wie" +#define D_AS "als" #define D_AUTO "AUTO" #define D_BLINK "Blinken" #define D_BLINKOFF "BlinkenAus" @@ -93,6 +93,7 @@ #define D_FALLBACK_TOPIC "Fallback-Topic" #define D_FALSE "falsch" #define D_FILE "Datei" +#define D_FLOW_RATE "Durchflussmenge" #define D_FREE_MEMORY "Freier Arbeitsspeicher" #define D_FREQUENCY "Frequenz" #define D_GAS "Gas" @@ -156,6 +157,7 @@ #define D_TO "zu" #define D_TOGGLE "An/Aus" #define D_TOPIC "topic" +#define D_TOTAL_USAGE "Total Usage" #define D_TRANSMIT "Übertragen" #define D_TRUE "wahr" #define D_TVOC "TVOC" @@ -220,7 +222,7 @@ // xdrv_02_webserver.ino #define D_NOSCRIPT "JavaScript aktivieren um Tasmota benutzen zu können" -#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "MINIMUM-Firmware
bitte upgraden" +#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "MINIMUM-Firmware
bitte upgraden" #define D_WEBSERVER_ACTIVE_ON "Web-Server aktiv bei" #define D_WITH_IP_ADDRESS "mit IP-Adresse" #define D_WEBSERVER_STOPPED "Web-Server angehalten" @@ -255,6 +257,7 @@ #define D_MODULE_PARAMETERS "Geräte-Einstellungen" #define D_MODULE_TYPE "Gerätetyp" #define D_PULLUP_ENABLE "Kein Taster/Schalter Pull-up" +#define D_ADC "ADC" #define D_GPIO "GPIO" #define D_SERIAL_IN "serieller Eingang [serial in]" #define D_SERIAL_OUT "serieller Ausgang [serial out]" @@ -304,8 +307,6 @@ #define D_TEMPLATE_NAME "Name" #define D_BASE_TYPE "basiert auf" #define D_TEMPLATE_FLAGS "Options" -#define D_ALLOW_ADC0 "ADC0 input" -#define D_ALLOW_PULLUP "Nutzer pull-up Auswahl" #define D_SAVE_CONFIGURATION "Konfiguration speichern" #define D_CONFIGURATION_SAVED "Konfiguration gespeichert" @@ -430,7 +431,7 @@ #define D_KNX_COMMAND_READ "Lesen" #define D_KNX_COMMAND_OTHER "Andere" #define D_SENT_TO "gesendet an" -#define D_KNX_WARNING "Die Gruppenadresse ( 0 / 0 / 0 ) ist reserviert und kann nicht verwendet werden." +#define D_KNX_WARNING "Die Gruppenadresse (0/0/0) ist reserviert und kann nicht verwendet werden." #define D_KNX_ENHANCEMENT "Erweiterte Kommunikation" #define D_KNX_TX_SLOT "KNX TX" #define D_KNX_RX_SLOT "KNX RX" @@ -492,6 +493,9 @@ #define D_TX20_SOUTH "S" #define D_TX20_WEST "W" +//xsns_43_hre.ino +#define D_LOG_HRE "HRE: " + // sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box #define D_SENSOR_NONE "None" #define D_SENSOR_USER "User" @@ -508,6 +512,7 @@ #define D_SENSOR_BUTTON "Button" // Suffix "1" #define D_SENSOR_RELAY "Relay" // Suffix "1i" #define D_SENSOR_LED "Led" // Suffix "1i" +#define D_SENSOR_LED_LINK "LedLink" // Suffix "i" #define D_SENSOR_PWM "PWM" // Suffix "1" #define D_SENSOR_COUNTER "Counter" // Suffix "1" #define D_SENSOR_IRRECV "IRrecv" @@ -573,15 +578,21 @@ #define D_SENSOR_MY92X1_DI "MY92x1 DI" #define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI" #define D_SENSOR_ARIRFRCV "ALux IrRcv" +#define D_SENSOR_ARIRFSEL "ALux IrSel" #define D_SENSOR_TXD "Serial Tx" #define D_SENSOR_RXD "Serial Rx" #define D_SENSOR_ROTARY "Rotary" // Suffix "1A" +#define D_SENSOR_HRE_CLOCK "HRE Clock" +#define D_SENSOR_HRE_DATA "HRE Data" +#define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CENTIMETER "cm" #define D_UNIT_HERTZ "Hz" #define D_UNIT_HOUR "h" +#define D_UNIT_GALLONS "gal" +#define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_INCREMENTS "inc" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_KILOMETER_PER_HOUR "km/h" diff --git a/sonoff/language/el-GR.h b/sonoff/language/el-GR.h index 0e483759d..4ddcc61d3 100644 --- a/sonoff/language/el-GR.h +++ b/sonoff/language/el-GR.h @@ -1,7 +1,7 @@ /* el-GR.h - localization for Greek - Greece for Sonoff-Tasmota - Copyright (C) 2019 Theo Arends (translated by Nick Galfas) + Copyright (C) 2019 Theo Arends, translated by Nick Galfas This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -28,7 +28,7 @@ * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. * Use online command Prefix to translate cmnd, stat and tele. * - * Updated until v6.3.0 + * Updated until v6.5.0 \*********************************************************************/ #define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) @@ -73,7 +73,7 @@ #define D_CONNECTED "Συνδεδεμένο" #define D_COUNT "Μέτρηση" #define D_COUNTER "Μετρητής" -#define D_CURRENT "Τάση" // As in Voltage and Current +#define D_CURRENT "Ένταση" // As in Voltage and Current #define D_DATA "Δεδομένα" #define D_DARKLIGHT "Σκοτεινό" #define D_DEBUG "Debug" @@ -93,6 +93,7 @@ #define D_FALLBACK_TOPIC "Fallback Topic" #define D_FALSE "Ψευδές" #define D_FILE "Αρχείο" +#define D_FLOW_RATE "Flow rate" #define D_FREE_MEMORY "Ελεύθερη μνήμη" #define D_FREQUENCY "Συχνότητα" #define D_GAS "Αέριο" @@ -156,6 +157,7 @@ #define D_TO "έως" #define D_TOGGLE "Εναλλαγή" #define D_TOPIC "Topic" +#define D_TOTAL_USAGE "Total Usage" #define D_TRANSMIT "Μετάδοση" #define D_TRUE "Αληθές" #define D_TVOC "TVOC" @@ -220,7 +222,7 @@ // webserver.ino #define D_NOSCRIPT "To use Tasmota, please enable JavaScript" -#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "MINIMAL firmware
παρακαλώ αναβαθμίστε" +#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "MINIMAL firmware
παρακαλώ αναβαθμίστε" #define D_WEBSERVER_ACTIVE_ON "Ενεργός διακομιστής Web στο" #define D_WITH_IP_ADDRESS "με διεύθυνση IP" #define D_WEBSERVER_STOPPED "Ο διακομιστής Web σταμάτησε" @@ -236,25 +238,26 @@ #define D_BUTTON_TOGGLE "Εναλλαγή" #define D_CONFIGURATION "Ρυθμίσεις" #define D_INFORMATION "Πληροφορίες" -#define D_FIRMWARE_UPGRADE "Αναβάθμιση Firmware" +#define D_FIRMWARE_UPGRADE "Αναβάθμιση" #define D_CONSOLE "Κονσόλα" #define D_CONFIRM_RESTART "Επιβεβαίωση επανεκκίνησης" -#define D_CONFIGURE_MODULE "Ρυθμίσεις μονάδας" -#define D_CONFIGURE_WIFI "Ρυθμίσεις WiFi" -#define D_CONFIGURE_MQTT "Ρυθμίσεις MQTT" -#define D_CONFIGURE_DOMOTICZ "Ρυθμίσεις Domoticz" -#define D_CONFIGURE_LOGGING "Ρυθμίσεις καταγραφής" +#define D_CONFIGURE_MODULE "Μονάδα" +#define D_CONFIGURE_WIFI "WiFi" +#define D_CONFIGURE_MQTT "MQTT" +#define D_CONFIGURE_DOMOTICZ "Domoticz" +#define D_CONFIGURE_LOGGING "Καταγραφή" #define D_CONFIGURE_OTHER "Άλλες ρυθμίσεις" #define D_CONFIRM_RESET_CONFIGURATION "Επιβεβαίωση αρχικοποίησης στις προεπιλεγμένες ρυθμίσεις" -#define D_RESET_CONFIGURATION "Αρχικοποίηση-reset ρυθμίσεων" -#define D_BACKUP_CONFIGURATION "Αποθήκευση ρυθμίσεων" -#define D_RESTORE_CONFIGURATION "Επαναφορά ρυθμίσεων" +#define D_RESET_CONFIGURATION "Reset" +#define D_BACKUP_CONFIGURATION "Εξαγωγή" +#define D_RESTORE_CONFIGURATION "Επαναφορά" #define D_MAIN_MENU "Κεντρικό μενού" #define D_MODULE_PARAMETERS "Παράμετροι μονάδας" #define D_MODULE_TYPE "Τύπος μονάδας" #define D_PULLUP_ENABLE "No Button/Switch pull-up" +#define D_ADC "ADC" #define D_GPIO "GPIO" #define D_SERIAL_IN "Serial In" #define D_SERIAL_OUT "Serial Out" @@ -280,17 +283,17 @@ #define D_FULL_TOPIC "Full Topic" #define D_LOGGING_PARAMETERS "Παράμετροι καταγραφής" -#define D_SERIAL_LOG_LEVEL "Επίπεδο καταγραφής Σειριακής" -#define D_WEB_LOG_LEVEL "Επίπεδο καταγραφής Web" -#define D_SYS_LOG_LEVEL "Επίπεδο καταγραφής Syslog" +#define D_SERIAL_LOG_LEVEL "Επίπεδο Σειριακής" +#define D_WEB_LOG_LEVEL "Επίπεδο Web" +#define D_SYS_LOG_LEVEL "Επίπεδο Syslog" #define D_MORE_DEBUG "More debug" #define D_SYSLOG_HOST "Εξυπηρετητής Syslog" #define D_SYSLOG_PORT "Θύρα Syslog" #define D_TELEMETRY_PERIOD "Περίοδος τηλεμετρίας" #define D_OTHER_PARAMETERS "Άλλες παράμετροι" -#define D_TEMPLATE "Template" -#define D_ACTIVATE "Activate" +#define D_TEMPLATE "Πρότυπο" +#define D_ACTIVATE "Ενεργοποίηση" #define D_WEB_ADMIN_PASSWORD "Κωδικός διαχειριστή" #define D_MQTT_ENABLE "Ενεργοποίηση MQTT" #define D_FRIENDLY_NAME "Φιλική ονομασία" @@ -299,13 +302,11 @@ #define D_SINGLE_DEVICE "μονή συσκευή" #define D_MULTI_DEVICE "πολλαπλές συσκευές" -#define D_CONFIGURE_TEMPLATE "Configure Template" -#define D_TEMPLATE_PARAMETERS "Template parameters" -#define D_TEMPLATE_NAME "Name" -#define D_BASE_TYPE "Based on" -#define D_TEMPLATE_FLAGS "Options" -#define D_ALLOW_ADC0 "ADC0 input" -#define D_ALLOW_PULLUP "User pull-up selection" +#define D_CONFIGURE_TEMPLATE "Πρότυπα" +#define D_TEMPLATE_PARAMETERS "Παράμετροι Πρότυπου" +#define D_TEMPLATE_NAME "Όνομα" +#define D_BASE_TYPE "Βασίζεται στο" +#define D_TEMPLATE_FLAGS "Επιλογές" #define D_SAVE_CONFIGURATION "Αποθήκευση ρυθμίσεων" #define D_CONFIGURATION_SAVED "Οι ρυθμίσεις αποθηκεύτηκαν" @@ -333,7 +334,7 @@ #define D_UPGRADE_BY_WEBSERVER "Αναβάθμιση μέσω web server" #define D_OTA_URL "OTA URL" #define D_START_UPGRADE "Εκκίνηση αναβάθμισης" -#define D_UPGRADE_BY_FILE_UPLOAD "Αναβάθμιση μέσω μεταφόρτωσης αρχείου" +#define D_UPGRADE_BY_FILE_UPLOAD "Αναβάθμιση με μεταφόρτωση αρχείου" #define D_UPLOAD_STARTED "Η μεταφόρτωση ξεκίνησε" #define D_UPGRADE_STARTED "Η αναβάθμιση ξεκίνησε" #define D_UPLOAD_DONE "Η μεταφόρτωση ολοκληρώθηκε" @@ -385,24 +386,24 @@ #define D_3_RESPONSE_PACKETS_SENT "Στάλθηκαν 3 πακέτα απόκρισης" // xdrv_07_domoticz.ino -#define D_DOMOTICZ_PARAMETERS "Ρυθμίσεις Domoticz" +#define D_DOMOTICZ_PARAMETERS "Παράμετροι Domoticz" #define D_DOMOTICZ_IDX "Idx" #define D_DOMOTICZ_KEY_IDX "Key idx" #define D_DOMOTICZ_SWITCH_IDX "Switch idx" #define D_DOMOTICZ_SENSOR_IDX "Sensor idx" - #define D_DOMOTICZ_TEMP "Temp" - #define D_DOMOTICZ_TEMP_HUM "Temp,Hum" - #define D_DOMOTICZ_TEMP_HUM_BARO "Temp,Hum,Baro" - #define D_DOMOTICZ_POWER_ENERGY "Power,Energy" - #define D_DOMOTICZ_ILLUMINANCE "Illuminance" - #define D_DOMOTICZ_COUNT "Count/PM1" - #define D_DOMOTICZ_VOLTAGE "Voltage/PM2.5" - #define D_DOMOTICZ_CURRENT "Current/PM10" - #define D_DOMOTICZ_AIRQUALITY "AirQuality" +#define D_DOMOTICZ_TEMP "Temp" +#define D_DOMOTICZ_TEMP_HUM "Temp,Hum" +#define D_DOMOTICZ_TEMP_HUM_BARO "Temp,Hum,Baro" +#define D_DOMOTICZ_POWER_ENERGY "Power,Energy" +#define D_DOMOTICZ_ILLUMINANCE "Illuminance" +#define D_DOMOTICZ_COUNT "Count/PM1" +#define D_DOMOTICZ_VOLTAGE "Voltage/PM2.5" +#define D_DOMOTICZ_CURRENT "Current/PM10" +#define D_DOMOTICZ_AIRQUALITY "AirQuality" #define D_DOMOTICZ_UPDATE_TIMER "Update timer" // xdrv_09_timers.ino -#define D_CONFIGURE_TIMER "Ρυθμίσεις Χρονικών" +#define D_CONFIGURE_TIMER "Χρονικά" #define D_TIMER_PARAMETERS "Παράμετροι χρονικών" #define D_TIMER_ENABLE "Ενεργοποίηση χρονικών" #define D_TIMER_ARM "Οπλισμένο" @@ -492,6 +493,9 @@ #define D_TX20_SOUTH "Ν" #define D_TX20_WEST "Δ" +//xsns_43_hre.ino +#define D_LOG_HRE "HRE: " + // sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box #define D_SENSOR_NONE "Κανένα" #define D_SENSOR_USER "User" @@ -504,12 +508,13 @@ #define D_SENSOR_WS2812 "WS2812" #define D_SENSOR_DFR562 "MP3 Player" #define D_SENSOR_IRSEND "IRsend" -#define D_SENSOR_SWITCH "Διακόπτης" // Suffix "1" -#define D_SENSOR_BUTTON "Κουμπί" // Suffix "1" -#define D_SENSOR_RELAY "Ρελέ" // Suffix "1i" +#define D_SENSOR_SWITCH "Switch" // Suffix "1" +#define D_SENSOR_BUTTON "Button" // Suffix "1" +#define D_SENSOR_RELAY "Relay" // Suffix "1i" #define D_SENSOR_LED "Led" // Suffix "1i" +#define D_SENSOR_LED_LINK "LedLink" // Suffix "i" #define D_SENSOR_PWM "PWM" // Suffix "1" -#define D_SENSOR_COUNTER "Μετρητής" // Suffix "1" +#define D_SENSOR_COUNTER "Counter" // Suffix "1" #define D_SENSOR_IRRECV "IRrecv" #define D_SENSOR_MHZ_RX "MHZ Rx" #define D_SENSOR_MHZ_TX "MHZ Tx" @@ -573,15 +578,21 @@ #define D_SENSOR_MY92X1_DI "MY92x1 DI" #define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI" #define D_SENSOR_ARIRFRCV "ALux IrRcv" +#define D_SENSOR_ARIRFSEL "ALux IrSel" #define D_SENSOR_TXD "Serial Tx" #define D_SENSOR_RXD "Serial Rx" #define D_SENSOR_ROTARY "Rotary" // Suffix "1A" +#define D_SENSOR_HRE_CLOCK "HRE Clock" +#define D_SENSOR_HRE_DATA "HRE Data" +#define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CENTIMETER "cm" #define D_UNIT_HERTZ "Hz" #define D_UNIT_HOUR "Hr" +#define D_UNIT_GALLONS "gal" +#define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_INCREMENTS "inc" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" diff --git a/sonoff/language/en-GB.h b/sonoff/language/en-GB.h index d5e792462..c9aba7cfd 100644 --- a/sonoff/language/en-GB.h +++ b/sonoff/language/en-GB.h @@ -93,6 +93,7 @@ #define D_FALLBACK_TOPIC "Fallback Topic" #define D_FALSE "False" #define D_FILE "File" +#define D_FLOW_RATE "Flow rate" #define D_FREE_MEMORY "Free Memory" #define D_FREQUENCY "Frequency" #define D_GAS "Gas" @@ -156,6 +157,7 @@ #define D_TO "to" #define D_TOGGLE "Toggle" #define D_TOPIC "Topic" +#define D_TOTAL_USAGE "Total Usage" #define D_TRANSMIT "Transmit" #define D_TRUE "True" #define D_TVOC "TVOC" @@ -220,7 +222,7 @@ // xdrv_02_webserver.ino #define D_NOSCRIPT "To use Tasmota, please enable JavaScript" -#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "MINIMAL firmware
please upgrade" +#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "MINIMAL firmware
please upgrade" #define D_WEBSERVER_ACTIVE_ON "Web server active on" #define D_WITH_IP_ADDRESS "with IP address" #define D_WEBSERVER_STOPPED "Web server stopped" @@ -255,6 +257,7 @@ #define D_MODULE_PARAMETERS "Module parameters" #define D_MODULE_TYPE "Module type" #define D_PULLUP_ENABLE "No Button/Switch pull-up" +#define D_ADC "ADC" #define D_GPIO "GPIO" #define D_SERIAL_IN "Serial In" #define D_SERIAL_OUT "Serial Out" @@ -304,8 +307,6 @@ #define D_TEMPLATE_NAME "Name" #define D_BASE_TYPE "Based on" #define D_TEMPLATE_FLAGS "Options" -#define D_ALLOW_ADC0 "ADC0 input" -#define D_ALLOW_PULLUP "User pull-up selection" #define D_SAVE_CONFIGURATION "Save configuration" #define D_CONFIGURATION_SAVED "Configuration saved" @@ -492,6 +493,9 @@ #define D_TX20_SOUTH "S" #define D_TX20_WEST "W" +//xsns_43_hre.ino +#define D_LOG_HRE "HRE: " + // sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box #define D_SENSOR_NONE "None" #define D_SENSOR_USER "User" @@ -508,6 +512,7 @@ #define D_SENSOR_BUTTON "Button" // Suffix "1" #define D_SENSOR_RELAY "Relay" // Suffix "1i" #define D_SENSOR_LED "Led" // Suffix "1i" +#define D_SENSOR_LED_LINK "LedLink" // Suffix "i" #define D_SENSOR_PWM "PWM" // Suffix "1" #define D_SENSOR_COUNTER "Counter" // Suffix "1" #define D_SENSOR_IRRECV "IRrecv" @@ -573,15 +578,21 @@ #define D_SENSOR_MY92X1_DI "MY92x1 DI" #define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI" #define D_SENSOR_ARIRFRCV "ALux IrRcv" +#define D_SENSOR_ARIRFSEL "ALux IrSel" #define D_SENSOR_TXD "Serial Tx" #define D_SENSOR_RXD "Serial Rx" #define D_SENSOR_ROTARY "Rotary" // Suffix "1A" +#define D_SENSOR_HRE_CLOCK "HRE Clock" +#define D_SENSOR_HRE_DATA "HRE Data" +#define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CENTIMETER "cm" #define D_UNIT_HERTZ "Hz" #define D_UNIT_HOUR "Hr" +#define D_UNIT_GALLONS "gal" +#define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_INCREMENTS "inc" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" diff --git a/sonoff/language/es-AR.h b/sonoff/language/es-ES.h similarity index 95% rename from sonoff/language/es-AR.h rename to sonoff/language/es-ES.h index baf9838bf..579ece011 100644 --- a/sonoff/language/es-AR.h +++ b/sonoff/language/es-ES.h @@ -1,5 +1,5 @@ /* - es-AR.h - localization for Spanish - Argentina for Sonoff-Tasmota + es-ES.h - localization for Spanish - Spain for Sonoff-Tasmota Copyright (C) 2019 Adrian Scillato @@ -17,8 +17,8 @@ along with this program. If not, see . */ -#ifndef _LANGUAGE_ES_AR_H_ -#define _LANGUAGE_ES_AR_H_ +#ifndef _LANGUAGE_ES_ES_H_ +#define _LANGUAGE_ES_ES_H_ /*************************** ATTENTION *******************************\ * @@ -28,12 +28,12 @@ * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. * Use online command Prefix to translate cmnd, stat and tele. * - * Updated until v6.3.0.17 + * Updated until v6.5.0.16 \*********************************************************************/ #define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) -#define LANGUAGE_LCID 11274 +#define LANGUAGE_LCID 1034 // HTML (ISO 639-1) Language Code #define D_HTML_LANGUAGE "es" @@ -93,6 +93,7 @@ #define D_FALLBACK_TOPIC "FallbackTopic" #define D_FALSE "Falso" #define D_FILE "Archivo" +#define D_FLOW_RATE "Caudal" #define D_FREE_MEMORY "Memoria Libre" #define D_FREQUENCY "Frecuencia" #define D_GAS "Gas" @@ -148,7 +149,7 @@ #define D_STOP "Detener" #define D_SUBNET_MASK "Máscara Subred" #define D_SUBSCRIBE_TO "Suscribir a" -#define D_UNSUBSCRIBE_FROM "Unsubscribe from" +#define D_UNSUBSCRIBE_FROM "Desuscribirse de" #define D_SUCCESSFUL "Exitosa" #define D_SUNRISE "Salida del Sol" #define D_SUNSET "Puesta del Sol" @@ -156,6 +157,7 @@ #define D_TO "a" #define D_TOGGLE "Conmutar" #define D_TOPIC "Topic" +#define D_TOTAL_USAGE "Total Usado" #define D_TRANSMIT "Transmitir" #define D_TRUE "Verdadero" #define D_TVOC "TVOC" @@ -220,7 +222,7 @@ // xdrv_02_webserver.ino #define D_NOSCRIPT "Habilitar JavaScript para usar Tasmota" -#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "Firmware MÍNIMO
actualice por favor" +#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "Firmware MÍNIMO
actualice por favor" #define D_WEBSERVER_ACTIVE_ON "Servidor web activo en" #define D_WITH_IP_ADDRESS "con dirección IP" #define D_WEBSERVER_STOPPED "Servidor web detenido" @@ -255,6 +257,7 @@ #define D_MODULE_PARAMETERS "Parámetros del módulo" #define D_MODULE_TYPE "Tipo de módulo" #define D_PULLUP_ENABLE "Botón/Llave sin pull-up" +#define D_ADC "ADC" #define D_GPIO "GPIO" #define D_SERIAL_IN "Serial In" #define D_SERIAL_OUT "Serial Out" @@ -289,8 +292,8 @@ #define D_TELEMETRY_PERIOD "Período de Telemetría" #define D_OTHER_PARAMETERS "Otros parámetros" -#define D_TEMPLATE "Template" -#define D_ACTIVATE "Activate" +#define D_TEMPLATE "Plantilla" +#define D_ACTIVATE "Activar" #define D_WEB_ADMIN_PASSWORD "Clave Administrador Web" #define D_MQTT_ENABLE "Habilitar MQTT" #define D_FRIENDLY_NAME "Nombre Amigable" @@ -299,13 +302,11 @@ #define D_SINGLE_DEVICE "dispositivo simple" #define D_MULTI_DEVICE "dispositivo múltiple" -#define D_CONFIGURE_TEMPLATE "Configure Template" -#define D_TEMPLATE_PARAMETERS "Template parameters" -#define D_TEMPLATE_NAME "Name" -#define D_BASE_TYPE "Based on" -#define D_TEMPLATE_FLAGS "Options" -#define D_ALLOW_ADC0 "ADC0 input" -#define D_ALLOW_PULLUP "User pull-up selection" +#define D_CONFIGURE_TEMPLATE "Configurar Plantilla" +#define D_TEMPLATE_PARAMETERS "Parámetros de Plantilla" +#define D_TEMPLATE_NAME "Nombre" +#define D_BASE_TYPE "Basada en" +#define D_TEMPLATE_FLAGS "Opciones" #define D_SAVE_CONFIGURATION "Grabar configuración" #define D_CONFIGURATION_SAVED "Configuración grabada" @@ -492,6 +493,9 @@ #define D_TX20_SOUTH "S" #define D_TX20_WEST "O" +//xsns_43_hre.ino +#define D_LOG_HRE "HRE: " + // sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box #define D_SENSOR_NONE "Ninguno" #define D_SENSOR_USER "User" @@ -508,6 +512,7 @@ #define D_SENSOR_BUTTON "Botón" // Suffix "1" #define D_SENSOR_RELAY "Relé" // Suffix "1i" #define D_SENSOR_LED "Led" // Suffix "1i" +#define D_SENSOR_LED_LINK "LedLink" // Suffix "i" #define D_SENSOR_PWM "PWM" // Suffix "1" #define D_SENSOR_COUNTER "Contador" // Suffix "1" #define D_SENSOR_IRRECV "IR Rx" @@ -573,15 +578,21 @@ #define D_SENSOR_MY92X1_DI "MY92x1 DI" #define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI" #define D_SENSOR_ARIRFRCV "ALux IrRcv" +#define D_SENSOR_ARIRFSEL "ALux IrSel" #define D_SENSOR_TXD "Serial Tx" #define D_SENSOR_RXD "Serial Rx" #define D_SENSOR_ROTARY "Rotary" // Suffix "1A" +#define D_SENSOR_HRE_CLOCK "HRE Clock" +#define D_SENSOR_HRE_DATA "HRE Data" +#define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CENTIMETER "cm" #define D_UNIT_HERTZ "Hz" #define D_UNIT_HOUR "Hr" +#define D_UNIT_GALLONS "gal" +#define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_INCREMENTS "inc" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" @@ -644,4 +655,4 @@ #define D_UNIT_KWARH "kVArH" #define D_UNIT_ANGLE "Grados" -#endif // _LANGUAGE_ES_AR_H_ +#endif // _LANGUAGE_ES_ES_H_ diff --git a/sonoff/language/fr-FR.h b/sonoff/language/fr-FR.h index 0b7d29eb7..daf849264 100644 --- a/sonoff/language/fr-FR.h +++ b/sonoff/language/fr-FR.h @@ -28,7 +28,7 @@ * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. * Use online command Prefix to translate cmnd, stat and tele. * - * Updated until v6.4.1.18 + * Updated until v6.5.0.7 \*********************************************************************/ #define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) @@ -93,6 +93,7 @@ #define D_FALLBACK_TOPIC "Topic de secours" #define D_FALSE "Faux" #define D_FILE "Fichier" +#define D_FLOW_RATE "Débit" #define D_FREE_MEMORY "Mémoire libre" #define D_FREQUENCY "Fréquence" #define D_GAS "Gaz" @@ -156,6 +157,7 @@ #define D_TO "à" #define D_TOGGLE "Inverser" #define D_TOPIC "Topic" // Keep MQTT keyword +#define D_TOTAL_USAGE "Eau totale" #define D_TRANSMIT "Transmettre" #define D_TRUE "Vrai" #define D_TVOC "TVOC" @@ -220,7 +222,7 @@ // xdrv_02_webserver.ino #define D_NOSCRIPT "Pour utiliser Tasmota, veuillez activer JavaScript" -#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "Firmware MINIMAL
merci de mettre à jour" +#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "Firmware MINIMAL
merci de mettre à jour" #define D_WEBSERVER_ACTIVE_ON "Serveur web actif sur" #define D_WITH_IP_ADDRESS "avec l'adresse IP" #define D_WEBSERVER_STOPPED "Serveur web éteint" @@ -255,6 +257,7 @@ #define D_MODULE_PARAMETERS "Paramètres module" #define D_MODULE_TYPE "Type de module" #define D_PULLUP_ENABLE "Inter. sans pull-up" +#define D_ADC "ADC" #define D_GPIO "GPIO" #define D_SERIAL_IN "Entrée série" #define D_SERIAL_OUT "Sortie série" @@ -304,8 +307,6 @@ #define D_TEMPLATE_NAME "Nom" #define D_BASE_TYPE "Basé sur" #define D_TEMPLATE_FLAGS "Options" -#define D_ALLOW_ADC0 "Entrée ADC0" -#define D_ALLOW_PULLUP "Pull-up utilisateur" #define D_SAVE_CONFIGURATION "Enregistrer la configuration" #define D_CONFIGURATION_SAVED "Configuration enregistrée" @@ -430,7 +431,7 @@ #define D_KNX_COMMAND_READ "Lire" #define D_KNX_COMMAND_OTHER "Autre" #define D_SENT_TO "envoyé à" -#define D_KNX_WARNING "L'Adresse de Groupe ( 0 / 0 / 0 ) est réservée et ne peut être utilisée." +#define D_KNX_WARNING "L'Adresse de Groupe (0/0/0) est réservée et ne peut être utilisée." #define D_KNX_ENHANCEMENT "Amélioration de la communication" #define D_KNX_TX_SLOT "KNX TX" #define D_KNX_RX_SLOT "KNX RX" @@ -492,6 +493,9 @@ #define D_TX20_SOUTH "S" #define D_TX20_WEST "O" +//xsns_43_hre.ino +#define D_LOG_HRE "HRE: " + // sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box #define D_SENSOR_NONE "Aucun" #define D_SENSOR_USER "Utilisateur" @@ -508,6 +512,7 @@ #define D_SENSOR_BUTTON "Bouton" // Suffix "1" #define D_SENSOR_RELAY "Relais" // Suffix "1i" #define D_SENSOR_LED "LED" // Suffix "1i" +#define D_SENSOR_LED_LINK "LedLink" // Suffix "i" #define D_SENSOR_PWM "PWM" // Suffix "1" #define D_SENSOR_COUNTER "Compteur" // Suffix "1" #define D_SENSOR_IRRECV "RécptIR" @@ -573,15 +578,21 @@ #define D_SENSOR_MY92X1_DI "MY92x1 DI" #define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI" #define D_SENSOR_ARIRFRCV "ALux IrRcv" +#define D_SENSOR_ARIRFSEL "ALux IrSel" #define D_SENSOR_TXD "Serial Tx" #define D_SENSOR_RXD "Serial Rx" #define D_SENSOR_ROTARY "Rotary" // Suffix "1A" +#define D_SENSOR_HRE_CLOCK "HRE Clock" +#define D_SENSOR_HRE_DATA "HRE Data" +#define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CENTIMETER "cm" #define D_UNIT_HERTZ "Hz" #define D_UNIT_HOUR "h" +#define D_UNIT_GALLONS "gal" +#define D_UNIT_GALLONS_PER_MIN "gal/mn" #define D_UNIT_INCREMENTS "inc" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_KILOMETER_PER_HOUR "km/h" @@ -595,12 +606,12 @@ #define D_UNIT_MILLIMETER "mm" #define D_UNIT_MILLIMETER_MERCURY "mmHg" #define D_UNIT_MILLISECOND "ms" -#define D_UNIT_MINUTE "Min" +#define D_UNIT_MINUTE "mn" #define D_UNIT_PARTS_PER_BILLION "ppb" #define D_UNIT_PARTS_PER_DECILITER "ppd" #define D_UNIT_PARTS_PER_MILLION "ppm" #define D_UNIT_PRESSURE "hPa" -#define D_UNIT_SECOND "sec" +#define D_UNIT_SECOND "s" #define D_UNIT_SECTORS "secteurs" #define D_UNIT_VA "VA" #define D_UNIT_VAR "VAr" diff --git a/sonoff/language/he-HE.h b/sonoff/language/he-HE.h index b26529cf2..8f8d35cba 100644 --- a/sonoff/language/he-HE.h +++ b/sonoff/language/he-HE.h @@ -93,6 +93,7 @@ #define D_FALLBACK_TOPIC "נושא לחזרה" #define D_FALSE "שגוי" #define D_FILE "קובץ" +#define D_FLOW_RATE "Flow rate" #define D_FREE_MEMORY "זכרון פנוי" #define D_FREQUENCY "תדר" #define D_GAS "גז" @@ -156,6 +157,7 @@ #define D_TO "ל" #define D_TOGGLE "מתג" #define D_TOPIC "נושא" +#define D_TOTAL_USAGE "Total Usage" #define D_TRANSMIT "עבר" #define D_TRUE "נכון" #define D_TVOC "TVOC" @@ -220,7 +222,7 @@ // xdrv_02_webserver.ino #define D_NOSCRIPT "JavaScript - כדי להשתמש בקושחת אסמוטה אנא הפעל" -#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "קושחה מינימלית - בבקשה אנא שדרג" +#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "קושחה מינימלית
בבקשה אנא שדרג" #define D_WEBSERVER_ACTIVE_ON "שרת ווב פעיל" #define D_WITH_IP_ADDRESS "IP עם כתובת" #define D_WEBSERVER_STOPPED "שרת ווב הופסק" @@ -255,6 +257,7 @@ #define D_MODULE_PARAMETERS "מודול פרמטרים" #define D_MODULE_TYPE "סוג מודול" #define D_PULLUP_ENABLE "pull-up אין לחצן/מתג" +#define D_ADC "ADC" #define D_GPIO " רגל " #define D_SERIAL_IN "כניסת סריאל" #define D_SERIAL_OUT "יציאת סריאל" @@ -304,8 +307,6 @@ #define D_TEMPLATE_NAME "שם" #define D_BASE_TYPE "מבוסס על" #define D_TEMPLATE_FLAGS "אפשריות" -#define D_ALLOW_ADC0 "ADC0 כניסת" -#define D_ALLOW_PULLUP "pull-up בחירת משתמש עבור" #define D_SAVE_CONFIGURATION "שמירת הגדרות" #define D_CONFIGURATION_SAVED "הגדרות נשמרו" @@ -492,6 +493,9 @@ #define D_TX20_SOUTH "S" #define D_TX20_WEST "W" +//xsns_43_hre.ino +#define D_LOG_HRE "HRE: " + // sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box #define D_SENSOR_NONE "None" #define D_SENSOR_USER "משתמש" @@ -508,6 +512,7 @@ #define D_SENSOR_BUTTON "לחצן" // Suffix "1" #define D_SENSOR_RELAY "ממסר" // Suffix "1i" #define D_SENSOR_LED "לד" // Suffix "1i" +#define D_SENSOR_LED_LINK "LedLink" // Suffix "i" #define D_SENSOR_PWM "PWM" // Suffix "1" #define D_SENSOR_COUNTER "מונה" // Suffix "1" #define D_SENSOR_IRRECV "IRrecv" @@ -573,15 +578,21 @@ #define D_SENSOR_MY92X1_DI "MY92x1 DI" #define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI" #define D_SENSOR_ARIRFRCV "ALux IrRcv" +#define D_SENSOR_ARIRFSEL "ALux IrSel" #define D_SENSOR_TXD "Serial Tx" #define D_SENSOR_RXD "Serial Rx" #define D_SENSOR_ROTARY "Rotary" // Suffix "1A" +#define D_SENSOR_HRE_CLOCK "HRE Clock" +#define D_SENSOR_HRE_DATA "HRE Data" +#define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CENTIMETER "cm" #define D_UNIT_HERTZ "Hz" #define D_UNIT_HOUR "Hr" +#define D_UNIT_GALLONS "gal" +#define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_INCREMENTS "inc" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" diff --git a/sonoff/language/hu-HU.h b/sonoff/language/hu-HU.h index e8b818029..078169f61 100644 --- a/sonoff/language/hu-HU.h +++ b/sonoff/language/hu-HU.h @@ -93,6 +93,7 @@ #define D_FALLBACK_TOPIC "fallback topik" #define D_FALSE "Hamis" #define D_FILE "Fájl" +#define D_FLOW_RATE "Flow rate" #define D_FREE_MEMORY "Szabad memória" #define D_FREQUENCY "Frekvencia" #define D_GAS "Gáz" @@ -156,6 +157,7 @@ #define D_TO "-nak" #define D_TOGGLE "Megfordítás" #define D_TOPIC "Topic" +#define D_TOTAL_USAGE "Total Usage" #define D_TRANSMIT "Továbbít" #define D_TRUE "Igaz" #define D_TVOC "TVOC" @@ -220,7 +222,7 @@ // xdrv_02_webserver.ino #define D_NOSCRIPT "A Tasmota használatához engedélyezd a Javascriptet!" -#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "MINIMÁLIS firmware
frissítsd!" +#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "MINIMÁLIS firmware
frissítsd!" #define D_WEBSERVER_ACTIVE_ON "Webszerver aktív:" #define D_WITH_IP_ADDRESS "IP cím:" #define D_WEBSERVER_STOPPED "Webszerver leállítva" @@ -255,6 +257,7 @@ #define D_MODULE_PARAMETERS "Modul paraméterek" #define D_MODULE_TYPE "Alkalmazott modul" #define D_PULLUP_ENABLE "Nincs felhúzó ellenállás" +#define D_ADC "ADC" #define D_GPIO "GPIO" #define D_SERIAL_IN "Soros BE" #define D_SERIAL_OUT "Soros KI" @@ -304,8 +307,6 @@ #define D_TEMPLATE_NAME "Name" #define D_BASE_TYPE "Based on" #define D_TEMPLATE_FLAGS "Options" -#define D_ALLOW_ADC0 "ADC0 input" -#define D_ALLOW_PULLUP "User pull-up selection" #define D_SAVE_CONFIGURATION "Beállítások mentése" #define D_CONFIGURATION_SAVED "Beállítások elmentve" @@ -492,6 +493,9 @@ #define D_TX20_SOUTH "D" #define D_TX20_WEST "NY" +//xsns_43_hre.ino +#define D_LOG_HRE "HRE: " + // sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box #define D_SENSOR_NONE "Nincs" #define D_SENSOR_USER "User" @@ -508,6 +512,7 @@ #define D_SENSOR_BUTTON "Gomb" // Suffix "1" #define D_SENSOR_RELAY "Relé" // Suffix "1i" #define D_SENSOR_LED "LED" // Suffix "1i" +#define D_SENSOR_LED_LINK "LedLink" // Suffix "i" #define D_SENSOR_PWM "PWM" // Suffix "1" #define D_SENSOR_COUNTER "Számláló" // Suffix "1" #define D_SENSOR_IRRECV "IR vevő" @@ -573,15 +578,21 @@ #define D_SENSOR_MY92X1_DI "MY92x1 DI" #define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI" #define D_SENSOR_ARIRFRCV "ALux IrRcv" +#define D_SENSOR_ARIRFSEL "ALux IrSel" #define D_SENSOR_TXD "Serial Tx" #define D_SENSOR_RXD "Serial Rx" #define D_SENSOR_ROTARY "Rotary" // Suffix "1A" +#define D_SENSOR_HRE_CLOCK "HRE Clock" +#define D_SENSOR_HRE_DATA "HRE Data" +#define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CENTIMETER "cm" #define D_UNIT_HERTZ "Hz" #define D_UNIT_HOUR "h" +#define D_UNIT_GALLONS "gal" +#define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_INCREMENTS "inc" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" diff --git a/sonoff/language/it-IT.h b/sonoff/language/it-IT.h index db8e58475..f498f9295 100644 --- a/sonoff/language/it-IT.h +++ b/sonoff/language/it-IT.h @@ -93,6 +93,7 @@ #define D_FALLBACK_TOPIC "Topic Riserva" #define D_FALSE "Falso" #define D_FILE "File" +#define D_FLOW_RATE "Flow rate" #define D_FREE_MEMORY "Memoria Libera" #define D_FREQUENCY "Frequenza" #define D_GAS "Gas" @@ -156,6 +157,7 @@ #define D_TO "a" #define D_TOGGLE "Toggle" #define D_TOPIC "Topic" +#define D_TOTAL_USAGE "Total Usage" #define D_TRANSMIT "Trasmesso" #define D_TRUE "Vero" #define D_TVOC "TVOC" @@ -220,7 +222,7 @@ // xdrv_02_webserver.ino #define D_NOSCRIPT "Abilitare JavaScript per utilizzare Tasmota" -#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "MINIMAL firmware
effettuare aggiornamento" +#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "MINIMAL firmware
effettuare aggiornamento" #define D_WEBSERVER_ACTIVE_ON "Web server attivo su" #define D_WITH_IP_ADDRESS "con indirizzo IP" #define D_WEBSERVER_STOPPED "Web server arrestato" @@ -255,6 +257,7 @@ #define D_MODULE_PARAMETERS "Parametri del modulo" #define D_MODULE_TYPE "Tipo modulo" #define D_PULLUP_ENABLE "No Button/Switch pull-up" +#define D_ADC "ADC" #define D_GPIO "GPIO" #define D_SERIAL_IN "Serial In" #define D_SERIAL_OUT "Serial Out" @@ -304,8 +307,6 @@ #define D_TEMPLATE_NAME "Name" #define D_BASE_TYPE "Based on" #define D_TEMPLATE_FLAGS "Options" -#define D_ALLOW_ADC0 "ADC0 input" -#define D_ALLOW_PULLUP "User pull-up selection" #define D_SAVE_CONFIGURATION "Salva configurazione" #define D_CONFIGURATION_SAVED "Configurazione salvata" @@ -492,6 +493,9 @@ #define D_TX20_SOUTH "S" #define D_TX20_WEST "W" +//xsns_43_hre.ino +#define D_LOG_HRE "HRE: " + // sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box #define D_SENSOR_NONE "Nessuno" #define D_SENSOR_USER "User" @@ -508,6 +512,7 @@ #define D_SENSOR_BUTTON "Button" // Suffix "1" #define D_SENSOR_RELAY "Relay" // Suffix "1i" #define D_SENSOR_LED "Led" // Suffix "1i" +#define D_SENSOR_LED_LINK "LedLink" // Suffix "i" #define D_SENSOR_PWM "PWM" // Suffix "1" #define D_SENSOR_COUNTER "Counter" // Suffix "1" #define D_SENSOR_IRRECV "IRrecv" @@ -573,15 +578,21 @@ #define D_SENSOR_MY92X1_DI "MY92x1 DI" #define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI" #define D_SENSOR_ARIRFRCV "ALux IrRcv" +#define D_SENSOR_ARIRFSEL "ALux IrSel" #define D_SENSOR_TXD "Serial Tx" #define D_SENSOR_RXD "Serial Rx" #define D_SENSOR_ROTARY "Rotary" // Suffix "1A" +#define D_SENSOR_HRE_CLOCK "HRE Clock" +#define D_SENSOR_HRE_DATA "HRE Data" +#define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CENTIMETER "cm" #define D_UNIT_HERTZ "Hz" #define D_UNIT_HOUR "Hr" +#define D_UNIT_GALLONS "gal" +#define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_INCREMENTS "inc" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" diff --git a/sonoff/language/ko-KO.h b/sonoff/language/ko-KO.h index 851d24a8e..0372574c9 100644 --- a/sonoff/language/ko-KO.h +++ b/sonoff/language/ko-KO.h @@ -93,6 +93,7 @@ #define D_FALLBACK_TOPIC "Fallback Topic" #define D_FALSE "거짓" #define D_FILE "파일" +#define D_FLOW_RATE "Flow rate" #define D_FREE_MEMORY "남은 메모리" #define D_FREQUENCY "빈도" #define D_GAS "가스" @@ -156,6 +157,7 @@ #define D_TO "to" #define D_TOGGLE "전환" #define D_TOPIC "Topic" +#define D_TOTAL_USAGE "Total Usage" #define D_TRANSMIT "전송" #define D_TRUE "참" #define D_TVOC "TVOC" @@ -220,7 +222,7 @@ // xdrv_02_webserver.ino #define D_NOSCRIPT "Tasmota를 사용하려면 JavaScript를 활성화 하십시오." -#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "MINIMAL firmware
업그레이드가 필요합니다" +#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "MINIMAL firmware
업그레이드가 필요합니다" #define D_WEBSERVER_ACTIVE_ON "Web 서버 작동 중" #define D_WITH_IP_ADDRESS "IP 주소" #define D_WEBSERVER_STOPPED "Web 서버 멈춤" @@ -255,6 +257,7 @@ #define D_MODULE_PARAMETERS "모듈 상세" #define D_MODULE_TYPE "모듈 타입" #define D_PULLUP_ENABLE "No Button/Switch pull-up" +#define D_ADC "ADC" #define D_GPIO "GPIO" #define D_SERIAL_IN "Serial In" #define D_SERIAL_OUT "Serial Out" @@ -304,8 +307,6 @@ #define D_TEMPLATE_NAME "이름" #define D_BASE_TYPE "Based on" #define D_TEMPLATE_FLAGS "옵션" -#define D_ALLOW_ADC0 "ADC0 입력" -#define D_ALLOW_PULLUP "User pull-up selection" #define D_SAVE_CONFIGURATION "설정 저장" #define D_CONFIGURATION_SAVED "설정 저장 완료" @@ -492,6 +493,9 @@ #define D_TX20_SOUTH "S" #define D_TX20_WEST "W" +//xsns_43_hre.ino +#define D_LOG_HRE "HRE: " + // sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box #define D_SENSOR_NONE "없음" #define D_SENSOR_USER "User" @@ -508,6 +512,7 @@ #define D_SENSOR_BUTTON "Button" // Suffix "1" #define D_SENSOR_RELAY "Relay" // Suffix "1i" #define D_SENSOR_LED "Led" // Suffix "1i" +#define D_SENSOR_LED_LINK "LedLink" // Suffix "i" #define D_SENSOR_PWM "PWM" // Suffix "1" #define D_SENSOR_COUNTER "Counter" // Suffix "1" #define D_SENSOR_IRRECV "IRrecv" @@ -573,15 +578,21 @@ #define D_SENSOR_MY92X1_DI "MY92x1 DI" #define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI" #define D_SENSOR_ARIRFRCV "ALux IrRcv" +#define D_SENSOR_ARIRFSEL "ALux IrSel" #define D_SENSOR_TXD "Serial Tx" #define D_SENSOR_RXD "Serial Rx" #define D_SENSOR_ROTARY "Rotary" // Suffix "1A" +#define D_SENSOR_HRE_CLOCK "HRE Clock" +#define D_SENSOR_HRE_DATA "HRE Data" +#define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CENTIMETER "cm" #define D_UNIT_HERTZ "Hz" #define D_UNIT_HOUR "시" +#define D_UNIT_GALLONS "gal" +#define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_INCREMENTS "inc" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" diff --git a/sonoff/language/nl-NL.h b/sonoff/language/nl-NL.h index 9e428165d..364fea246 100644 --- a/sonoff/language/nl-NL.h +++ b/sonoff/language/nl-NL.h @@ -93,6 +93,7 @@ #define D_FALLBACK_TOPIC "Fallback Topic" #define D_FALSE "Onwaar" #define D_FILE "Bestand" +#define D_FLOW_RATE "Flow rate" #define D_FREE_MEMORY "Vrij geheugen" #define D_FREQUENCY "Frequentie" #define D_GAS "Gas" @@ -156,6 +157,7 @@ #define D_TO "naar" #define D_TOGGLE "Toggle" // Wissel, Tuimel #define D_TOPIC "Topic" // Onderwerp +#define D_TOTAL_USAGE "Total Usage" #define D_TRANSMIT "Verzend" #define D_TRUE "Waar" #define D_TVOC "TVOC" @@ -220,7 +222,7 @@ // xdrv_02_webserver.ino #define D_NOSCRIPT "Zet JavaScript aan voor Tasmota" -#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "MINIMAL firmware
opwaarderen" +#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "MINIMAL firmware
opwaarderen" #define D_WEBSERVER_ACTIVE_ON "Webserver actief op" #define D_WITH_IP_ADDRESS "met IP adres" #define D_WEBSERVER_STOPPED "Webserver gestopt" @@ -255,6 +257,7 @@ #define D_MODULE_PARAMETERS "Module parameters" #define D_MODULE_TYPE "Module soort" #define D_PULLUP_ENABLE "Geen schakelaar pull-up" +#define D_ADC "ADC" #define D_GPIO "GPIO" #define D_SERIAL_IN "Serieel In" #define D_SERIAL_OUT "Serieel Uit" @@ -289,8 +292,8 @@ #define D_TELEMETRY_PERIOD "Telemetry periode" #define D_OTHER_PARAMETERS "Overige parameters" -#define D_TEMPLATE "Template" -#define D_ACTIVATE "Activate" +#define D_TEMPLATE "Sjabloon" +#define D_ACTIVATE "Activeer" #define D_WEB_ADMIN_PASSWORD "Web Admin Wachtwoord" #define D_MQTT_ENABLE "MQTT ingeschakeld" #define D_FRIENDLY_NAME "Beschrijvende naam" @@ -299,13 +302,11 @@ #define D_SINGLE_DEVICE "een apparaat" #define D_MULTI_DEVICE "meer apparaten" -#define D_CONFIGURE_TEMPLATE "Configure Template" -#define D_TEMPLATE_PARAMETERS "Template parameters" -#define D_TEMPLATE_NAME "Name" -#define D_BASE_TYPE "Based on" -#define D_TEMPLATE_FLAGS "Options" -#define D_ALLOW_ADC0 "ADC0 input" -#define D_ALLOW_PULLUP "User pull-up selection" +#define D_CONFIGURE_TEMPLATE "Configureer Sjabloon" +#define D_TEMPLATE_PARAMETERS "Sjabloon parameters" +#define D_TEMPLATE_NAME "Naam" +#define D_BASE_TYPE "Op basis van" +#define D_TEMPLATE_FLAGS "Opties" #define D_SAVE_CONFIGURATION "Bewaar configuratie" #define D_CONFIGURATION_SAVED "Configuratie opgeslagen" @@ -492,6 +493,9 @@ #define D_TX20_SOUTH "S" #define D_TX20_WEST "W" +//xsns_43_hre.ino +#define D_LOG_HRE "HRE: " + // sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box #define D_SENSOR_NONE "Geen" #define D_SENSOR_USER "Gebruiker" @@ -508,6 +512,7 @@ #define D_SENSOR_BUTTON "Button" // Suffix "1" #define D_SENSOR_RELAY "Relais" // Suffix "1i" #define D_SENSOR_LED "Led" // Suffix "1i" +#define D_SENSOR_LED_LINK "LedLink" // Suffix "i" #define D_SENSOR_PWM "PWM" // Suffix "1" #define D_SENSOR_COUNTER "Teller" // Suffix "1" #define D_SENSOR_IRRECV "IRrecv" @@ -573,15 +578,21 @@ #define D_SENSOR_MY92X1_DI "MY92x1 DI" #define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI" #define D_SENSOR_ARIRFRCV "ALux IrRcv" +#define D_SENSOR_ARIRFSEL "ALux IrSel" #define D_SENSOR_TXD "Serial Tx" #define D_SENSOR_RXD "Serial Rx" #define D_SENSOR_ROTARY "Rotary" // Suffix "1A" +#define D_SENSOR_HRE_CLOCK "HRE Clock" +#define D_SENSOR_HRE_DATA "HRE Data" +#define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CENTIMETER "cm" #define D_UNIT_HERTZ "Hz" #define D_UNIT_HOUR "h" +#define D_UNIT_GALLONS "gal" +#define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_INCREMENTS "inc" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" diff --git a/sonoff/language/pl-PL.h b/sonoff/language/pl-PL.h index 0f13d248a..a08e9ecc8 100644 --- a/sonoff/language/pl-PL.h +++ b/sonoff/language/pl-PL.h @@ -93,6 +93,7 @@ #define D_FALLBACK_TOPIC "Zastępczy temat" #define D_FALSE "Fałsz" #define D_FILE "Plik" +#define D_FLOW_RATE "Flow rate" #define D_FREE_MEMORY "Wolna pamięć" #define D_FREQUENCY "Frequency" #define D_GAS "Gas" @@ -156,6 +157,7 @@ #define D_TO "do" #define D_TOGGLE "Przełącz" #define D_TOPIC "Temat" +#define D_TOTAL_USAGE "Total Usage" #define D_TRANSMIT "Wyślij" #define D_TRUE "Prawda" #define D_TVOC "TVOC" @@ -220,7 +222,7 @@ // xdrv_02_webserver.ino #define D_NOSCRIPT "Aby korzystać z Tasmota, włącz obsługę JavaScript" -#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "Oprogramowanie MINIMAL
proszę uaktualnić" +#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "Oprogramowanie MINIMAL
proszę uaktualnić" #define D_WEBSERVER_ACTIVE_ON "Aktywny serwer Web" #define D_WITH_IP_ADDRESS "z adresem IP" #define D_WEBSERVER_STOPPED "Serwer Web zatrzymany" @@ -255,6 +257,7 @@ #define D_MODULE_PARAMETERS "Parametry modułu" #define D_MODULE_TYPE "Typ modułu" #define D_PULLUP_ENABLE "No Button/Switch pull-up" +#define D_ADC "ADC" #define D_GPIO "GPIO" #define D_SERIAL_IN "Serial In" #define D_SERIAL_OUT "Serial Out" @@ -304,8 +307,6 @@ #define D_TEMPLATE_NAME "Name" #define D_BASE_TYPE "Based on" #define D_TEMPLATE_FLAGS "Options" -#define D_ALLOW_ADC0 "ADC0 input" -#define D_ALLOW_PULLUP "User pull-up selection" #define D_SAVE_CONFIGURATION "Zapisz ustawienia" #define D_CONFIGURATION_SAVED "Ustawienia zapisane" @@ -492,6 +493,9 @@ #define D_TX20_SOUTH "S" #define D_TX20_WEST "W" +//xsns_43_hre.ino +#define D_LOG_HRE "HRE: " + // sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box #define D_SENSOR_NONE "Brak" #define D_SENSOR_USER "User" @@ -508,6 +512,7 @@ #define D_SENSOR_BUTTON "Przyci" // Suffix "1" #define D_SENSOR_RELAY "Przek" // Suffix "1i" #define D_SENSOR_LED "Led" // Suffix "1i" +#define D_SENSOR_LED_LINK "LedLink" // Suffix "i" #define D_SENSOR_PWM "PWM" // Suffix "1" #define D_SENSOR_COUNTER "Liczni" // Suffix "1" #define D_SENSOR_IRRECV "IRrecv" @@ -573,15 +578,21 @@ #define D_SENSOR_MY92X1_DI "MY92x1 DI" #define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI" #define D_SENSOR_ARIRFRCV "ALux IrRcv" +#define D_SENSOR_ARIRFSEL "ALux IrSel" #define D_SENSOR_TXD "Serial Tx" #define D_SENSOR_RXD "Serial Rx" #define D_SENSOR_ROTARY "Rotary" // Suffix "1A" +#define D_SENSOR_HRE_CLOCK "HRE Clock" +#define D_SENSOR_HRE_DATA "HRE Data" +#define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CENTIMETER "cm" #define D_UNIT_HERTZ "Hz" #define D_UNIT_HOUR "Godz" +#define D_UNIT_GALLONS "gal" +#define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_INCREMENTS "inc" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" diff --git a/sonoff/language/pt-BR.h b/sonoff/language/pt-BR.h index 63efc3f1e..863c40b0f 100644 --- a/sonoff/language/pt-BR.h +++ b/sonoff/language/pt-BR.h @@ -93,6 +93,7 @@ #define D_FALLBACK_TOPIC "Tópico para retornar" #define D_FALSE "Falso" #define D_FILE "Arquivo" +#define D_FLOW_RATE "Flow rate" #define D_FREE_MEMORY "Memória livre" #define D_FREQUENCY "Frequência" #define D_GAS "Gás" @@ -156,6 +157,7 @@ #define D_TO "Para" #define D_TOGGLE "Inverter" #define D_TOPIC "Tópico" +#define D_TOTAL_USAGE "Total Usage" #define D_TRANSMIT "Transmitir" #define D_TRUE "Verdadeiro" #define D_TVOC "TVOC" @@ -220,7 +222,7 @@ // xdrv_02_webserver.ino #define D_NOSCRIPT "To use Tasmota, please enable JavaScript" -#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "Firmware mínimo
Atualizar por favor" +#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "Firmware mínimo
Atualizar por favor" #define D_WEBSERVER_ACTIVE_ON "Servidor WEB ativo em" #define D_WITH_IP_ADDRESS "com o endereço IP" #define D_WEBSERVER_STOPPED "Servidor WEB parou" @@ -255,6 +257,7 @@ #define D_MODULE_PARAMETERS "Parâmetros do módulo" #define D_MODULE_TYPE "Tipo de módulo" #define D_PULLUP_ENABLE "No Button/Switch pull-up" +#define D_ADC "ADC" #define D_GPIO "GPIO" #define D_SERIAL_IN "Entrada serial" #define D_SERIAL_OUT "Saída serial" @@ -304,8 +307,6 @@ #define D_TEMPLATE_NAME "Name" #define D_BASE_TYPE "Based on" #define D_TEMPLATE_FLAGS "Options" -#define D_ALLOW_ADC0 "ADC0 input" -#define D_ALLOW_PULLUP "User pull-up selection" #define D_SAVE_CONFIGURATION "Gravar configuração" #define D_CONFIGURATION_SAVED "Configuração gravada" @@ -492,6 +493,9 @@ #define D_TX20_SOUTH "S" #define D_TX20_WEST "W" +//xsns_43_hre.ino +#define D_LOG_HRE "HRE: " + // sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box #define D_SENSOR_NONE "Nenhum" #define D_SENSOR_USER "User" @@ -508,6 +512,7 @@ #define D_SENSOR_BUTTON "Botão" // Suffix "1" #define D_SENSOR_RELAY "Relé" // Suffix "1i" #define D_SENSOR_LED "Led" // Suffix "1i" +#define D_SENSOR_LED_LINK "LedLink" // Suffix "i" #define D_SENSOR_PWM "PWM" // Suffix "1" #define D_SENSOR_COUNTER "Contador" // Suffix "1" #define D_SENSOR_IRRECV "IRrecv" @@ -573,15 +578,21 @@ #define D_SENSOR_MY92X1_DI "MY92x1 DI" #define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI" #define D_SENSOR_ARIRFRCV "ALux IrRcv" +#define D_SENSOR_ARIRFSEL "ALux IrSel" #define D_SENSOR_TXD "Serial Tx" #define D_SENSOR_RXD "Serial Rx" #define D_SENSOR_ROTARY "Rotary" // Suffix "1A" +#define D_SENSOR_HRE_CLOCK "HRE Clock" +#define D_SENSOR_HRE_DATA "HRE Data" +#define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CENTIMETER "cm" #define D_UNIT_HERTZ "Hz" #define D_UNIT_HOUR "H" +#define D_UNIT_GALLONS "gal" +#define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_INCREMENTS "inc" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" diff --git a/sonoff/language/pt-PT.h b/sonoff/language/pt-PT.h index 171f33d9b..f45e71a73 100644 --- a/sonoff/language/pt-PT.h +++ b/sonoff/language/pt-PT.h @@ -93,6 +93,7 @@ #define D_FALLBACK_TOPIC "Tópico para retornar" #define D_FALSE "Falso" #define D_FILE "Ficheiro" +#define D_FLOW_RATE "Flow rate" #define D_FREE_MEMORY "Memoria Livre" #define D_FREQUENCY "Frequency" #define D_GAS "Gás" @@ -156,6 +157,7 @@ #define D_TO "para" #define D_TOGGLE "Pressionar" #define D_TOPIC "Tópico" +#define D_TOTAL_USAGE "Total Usage" #define D_TRANSMIT "Transmitir" #define D_TRUE "Verdadeiro" #define D_TVOC "TVOC" @@ -220,7 +222,7 @@ // xdrv_02_webserver.ino #define D_NOSCRIPT "To use Tasmota, please enable JavaScript" -#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "MINIMO firmware
Atualizar Por favor" +#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "MINIMO firmware
Atualizar Por favor" #define D_WEBSERVER_ACTIVE_ON "Servidor WEB ativo em" #define D_WITH_IP_ADDRESS "com o endereço IP" #define D_WEBSERVER_STOPPED "Servitor WEB parou" @@ -255,6 +257,7 @@ #define D_MODULE_PARAMETERS "Parametros do Módulo" #define D_MODULE_TYPE "Tipo de Módulo" #define D_PULLUP_ENABLE "No Button/Switch pull-up" +#define D_ADC "ADC" #define D_GPIO "GPIO" #define D_SERIAL_IN "Serial Entrada" #define D_SERIAL_OUT "Serial Saída" @@ -304,8 +307,6 @@ #define D_TEMPLATE_NAME "Name" #define D_BASE_TYPE "Based on" #define D_TEMPLATE_FLAGS "Options" -#define D_ALLOW_ADC0 "ADC0 input" -#define D_ALLOW_PULLUP "User pull-up selection" #define D_SAVE_CONFIGURATION "Salvar configuração" #define D_CONFIGURATION_SAVED "Configuração guardada" @@ -492,6 +493,9 @@ #define D_TX20_SOUTH "S" #define D_TX20_WEST "W" +//xsns_43_hre.ino +#define D_LOG_HRE "HRE: " + // sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box #define D_SENSOR_NONE "Nenhum" #define D_SENSOR_USER "User" @@ -508,6 +512,7 @@ #define D_SENSOR_BUTTON "Botão" // Suffix "1" #define D_SENSOR_RELAY "Relé" // Suffix "1i" #define D_SENSOR_LED "Led" // Suffix "1i" +#define D_SENSOR_LED_LINK "LedLink" // Suffix "i" #define D_SENSOR_PWM "PWM" // Suffix "1" #define D_SENSOR_COUNTER "Contador" // Suffix "1" #define D_SENSOR_IRRECV "IRrecv" @@ -573,15 +578,21 @@ #define D_SENSOR_MY92X1_DI "MY92x1 DI" #define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI" #define D_SENSOR_ARIRFRCV "ALux IrRcv" +#define D_SENSOR_ARIRFSEL "ALux IrSel" #define D_SENSOR_TXD "Serial Tx" #define D_SENSOR_RXD "Serial Rx" #define D_SENSOR_ROTARY "Rotary" // Suffix "1A" +#define D_SENSOR_HRE_CLOCK "HRE Clock" +#define D_SENSOR_HRE_DATA "HRE Data" +#define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CENTIMETER "cm" #define D_UNIT_HERTZ "Hz" #define D_UNIT_HOUR "Hr" +#define D_UNIT_GALLONS "gal" +#define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_INCREMENTS "inc" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" diff --git a/sonoff/language/ru-RU.h b/sonoff/language/ru-RU.h index e4112953c..b76f0e4b4 100644 --- a/sonoff/language/ru-RU.h +++ b/sonoff/language/ru-RU.h @@ -93,6 +93,7 @@ #define D_FALLBACK_TOPIC "Топик обратной связи" #define D_FALSE "Ложно" #define D_FILE "Файл" +#define D_FLOW_RATE "Flow rate" #define D_FREE_MEMORY "Свободная память" #define D_FREQUENCY "Frequency" #define D_GAS "Газ" @@ -156,6 +157,7 @@ #define D_TO "до" #define D_TOGGLE "Переключить" #define D_TOPIC "Топик" +#define D_TOTAL_USAGE "Total Usage" #define D_TRANSMIT "Передать" #define D_TRUE "Истина" #define D_TVOC "TVOC" @@ -220,7 +222,7 @@ // xdrv_02_webserver.ino #define D_NOSCRIPT "To use Tasmota, please enable JavaScript" -#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "Прошивка MINIMAL
пожалуйста обновите" +#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "Прошивка MINIMAL
пожалуйста обновите" #define D_WEBSERVER_ACTIVE_ON "Веб-сервер активен" #define D_WITH_IP_ADDRESS "с IP-адресом" #define D_WEBSERVER_STOPPED "Веб-сервер остановлен" @@ -255,6 +257,7 @@ #define D_MODULE_PARAMETERS "Параметры модуля" #define D_MODULE_TYPE "Тип модуля" #define D_PULLUP_ENABLE "No Button/Switch pull-up" +#define D_ADC "ADC" #define D_GPIO "GPIO" #define D_SERIAL_IN "Serial вход" #define D_SERIAL_OUT "Serial выход" @@ -304,8 +307,6 @@ #define D_TEMPLATE_NAME "Name" #define D_BASE_TYPE "Based on" #define D_TEMPLATE_FLAGS "Options" -#define D_ALLOW_ADC0 "ADC0 input" -#define D_ALLOW_PULLUP "User pull-up selection" #define D_SAVE_CONFIGURATION "Сохранить конфигурацию" #define D_CONFIGURATION_SAVED "Конфигурация сохранена " @@ -492,6 +493,9 @@ #define D_TX20_SOUTH "S" #define D_TX20_WEST "W" +//xsns_43_hre.ino +#define D_LOG_HRE "HRE: " + // sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box #define D_SENSOR_NONE "-нет-" #define D_SENSOR_USER "User" @@ -508,6 +512,7 @@ #define D_SENSOR_BUTTON "Кнопка" // Suffix "1" #define D_SENSOR_RELAY "Реле" // Suffix "1i" #define D_SENSOR_LED "Led" // Suffix "1i" +#define D_SENSOR_LED_LINK "LedLink" // Suffix "i" #define D_SENSOR_PWM "PWM" // Suffix "1" #define D_SENSOR_COUNTER "Счетчик" // Suffix "1" #define D_SENSOR_IRRECV "IRrecv" @@ -573,15 +578,21 @@ #define D_SENSOR_MY92X1_DI "MY92x1 DI" #define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI" #define D_SENSOR_ARIRFRCV "ALux IrRcv" +#define D_SENSOR_ARIRFSEL "ALux IrSel" #define D_SENSOR_TXD "Serial Tx" #define D_SENSOR_RXD "Serial Rx" #define D_SENSOR_ROTARY "Rotary" // Suffix "1A" +#define D_SENSOR_HRE_CLOCK "HRE Clock" +#define D_SENSOR_HRE_DATA "HRE Data" +#define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" // Units #define D_UNIT_AMPERE "А" #define D_UNIT_CENTIMETER "cm" #define D_UNIT_HERTZ "Hz" #define D_UNIT_HOUR "Ч" +#define D_UNIT_GALLONS "gal" +#define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_INCREMENTS "inc" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" diff --git a/sonoff/language/sk-SK.h b/sonoff/language/sk-SK.h index cf99703c8..2c5d126ef 100644 --- a/sonoff/language/sk-SK.h +++ b/sonoff/language/sk-SK.h @@ -75,6 +75,7 @@ #define D_COUNTER "Počítadlo" #define D_CURRENT "Prúd" // As in Voltage and Current #define D_DATA "Dáta" +#define D_FLOW_RATE "Flow rate" #define D_DARKLIGHT "Tmavý" #define D_DEBUG "Debug" #define D_DISABLED "Zablokované" @@ -156,6 +157,7 @@ #define D_TO "do" #define D_TOGGLE "Prepni" #define D_TOPIC "Topic" +#define D_TOTAL_USAGE "Total Usage" #define D_TRANSMIT "Odošli" #define D_TRUE "Pravda" #define D_TVOC "TVOC" @@ -220,7 +222,7 @@ // xdrv_02_webserver.ino #define D_NOSCRIPT "Pre používanie prostredia Tasmota povoľte JavaScript" -#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "Firmware MINIMÁLNY
prosím aktualizujte" +#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "Firmware MINIMÁLNY
prosím aktualizujte" #define D_WEBSERVER_ACTIVE_ON "Aktívny Web server" #define D_WITH_IP_ADDRESS "na IP adrese" #define D_WEBSERVER_STOPPED "Web server zastavený" @@ -255,6 +257,7 @@ #define D_MODULE_PARAMETERS "Nastavenia modulu" #define D_MODULE_TYPE "Typ modulu" #define D_PULLUP_ENABLE "No Button/Switch pull-up" +#define D_ADC "ADC" #define D_GPIO "GPIO" #define D_SERIAL_IN "Serial In" #define D_SERIAL_OUT "Serial Out" @@ -304,8 +307,6 @@ #define D_TEMPLATE_NAME "Name" #define D_BASE_TYPE "Based on" #define D_TEMPLATE_FLAGS "Options" -#define D_ALLOW_ADC0 "ADC0 input" -#define D_ALLOW_PULLUP "User pull-up selection" #define D_SAVE_CONFIGURATION "Ulož nastavenia" #define D_CONFIGURATION_SAVED "Nastavenia uložené" @@ -492,6 +493,9 @@ #define D_TX20_SOUTH "J" #define D_TX20_WEST "Z" +//xsns_43_hre.ino +#define D_LOG_HRE "HRE: " + // sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box #define D_SENSOR_NONE "Žiaden" #define D_SENSOR_USER "User" @@ -508,6 +512,7 @@ #define D_SENSOR_BUTTON "Tlačidlo" // Suffix "1" #define D_SENSOR_RELAY "Relé" // Suffix "1i" #define D_SENSOR_LED "Led" // Suffix "1i" +#define D_SENSOR_LED_LINK "LedLink" // Suffix "i" #define D_SENSOR_PWM "PWM" // Suffix "1", #define D_SENSOR_COUNTER "Počítadlo" // Suffix "1" #define D_SENSOR_IRRECV "IRrecv" @@ -573,15 +578,21 @@ #define D_SENSOR_MY92X1_DI "MY92x1 DI" #define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI" #define D_SENSOR_ARIRFRCV "ALux IrRcv" +#define D_SENSOR_ARIRFSEL "ALux IrSel" #define D_SENSOR_TXD "Serial Tx" #define D_SENSOR_RXD "Serial Rx" #define D_SENSOR_ROTARY "Rotary" // Suffix "1A" +#define D_SENSOR_HRE_CLOCK "HRE Clock" +#define D_SENSOR_HRE_DATA "HRE Data" +#define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CENTIMETER "cm" #define D_UNIT_HERTZ "Hz" #define D_UNIT_HOUR "hod" +#define D_UNIT_GALLONS "gal" +#define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_INCREMENTS "inc" #define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" diff --git a/sonoff/language/sv-SE.h b/sonoff/language/sv-SE.h index 6a92c1bc1..35fec7e4c 100644 --- a/sonoff/language/sv-SE.h +++ b/sonoff/language/sv-SE.h @@ -93,6 +93,7 @@ #define D_FALLBACK_TOPIC "Reservämne" #define D_FALSE "Falskt" #define D_FILE "Fil" +#define D_FLOW_RATE "Flow rate" #define D_FREE_MEMORY "Ledigt minne" #define D_FREQUENCY "Frekvens" #define D_GAS "Gas" @@ -156,6 +157,7 @@ #define D_TO "till" #define D_TOGGLE "Växla" #define D_TOPIC "Ämne" +#define D_TOTAL_USAGE "Total Usage" #define D_TRANSMIT "Sänd" #define D_TRUE "Sant" #define D_TVOC "TVOC" @@ -220,7 +222,7 @@ // xdrv_02_webserver.ino #define D_NOSCRIPT "För att använda Tasmota, aktivera JavaScript" -#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "MINIMAL firmware
var god uppgradera" +#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "MINIMAL firmware
var god uppgradera" #define D_WEBSERVER_ACTIVE_ON "Webbserver aktiv på" #define D_WITH_IP_ADDRESS "med IP-adress" #define D_WEBSERVER_STOPPED "Webbserver stoppad" @@ -255,6 +257,7 @@ #define D_MODULE_PARAMETERS "Modulparameterar" #define D_MODULE_TYPE "Modultyp" #define D_PULLUP_ENABLE "Ingen knapp/brytare pull-up" +#define D_ADC "ADC" #define D_GPIO "GPIO" #define D_SERIAL_IN "Seriell in" #define D_SERIAL_OUT "Seriell ut" @@ -304,8 +307,6 @@ #define D_TEMPLATE_NAME "Name" #define D_BASE_TYPE "Based on" #define D_TEMPLATE_FLAGS "Options" -#define D_ALLOW_ADC0 "ADC0 input" -#define D_ALLOW_PULLUP "User pull-up selection" #define D_SAVE_CONFIGURATION "Spara konfiguration" #define D_CONFIGURATION_SAVED "Konfiguration sparad" @@ -492,6 +493,9 @@ #define D_TX20_SOUTH "S" #define D_TX20_WEST "V" +//xsns_43_hre.ino +#define D_LOG_HRE "HRE: " + // sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box #define D_SENSOR_NONE "Ingen" #define D_SENSOR_USER "User" @@ -508,6 +512,7 @@ #define D_SENSOR_BUTTON "Knapp" // Suffix "1" #define D_SENSOR_RELAY "Relä" // Suffix "1i" #define D_SENSOR_LED "Led" // Suffix "1i" +#define D_SENSOR_LED_LINK "LedLink" // Suffix "i" #define D_SENSOR_PWM "PWM" // Suffix "1" #define D_SENSOR_COUNTER "Räknare" // Suffix "1" #define D_SENSOR_IRRECV "IRrecv" @@ -573,15 +578,21 @@ #define D_SENSOR_MY92X1_DI "MY92x1 DI" #define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI" #define D_SENSOR_ARIRFRCV "ALux IrRcv" +#define D_SENSOR_ARIRFSEL "ALux IrSel" #define D_SENSOR_TXD "Serial Tx" #define D_SENSOR_RXD "Serial Rx" #define D_SENSOR_ROTARY "Rotary" // Suffix "1A" +#define D_SENSOR_HRE_CLOCK "HRE Clock" +#define D_SENSOR_HRE_DATA "HRE Data" +#define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CENTIMETER "cm" #define D_UNIT_HERTZ "Hz" #define D_UNIT_HOUR "Tim" +#define D_UNIT_GALLONS "gal" +#define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_INCREMENTS "ink" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" diff --git a/sonoff/language/tr-TR.h b/sonoff/language/tr-TR.h index b887c89c2..68da47c01 100755 --- a/sonoff/language/tr-TR.h +++ b/sonoff/language/tr-TR.h @@ -93,6 +93,7 @@ #define D_FALLBACK_TOPIC "Geri İletim Topiği" #define D_FALSE "False" #define D_FILE "Dosya" +#define D_FLOW_RATE "Flow rate" #define D_FREE_MEMORY "Boş Hafıza" #define D_FREQUENCY "Frekans" #define D_GAS "Gas" @@ -156,6 +157,7 @@ #define D_TO "den" #define D_TOGGLE "Geçiş Tuşu" #define D_TOPIC "Başlık" +#define D_TOTAL_USAGE "Total Usage" #define D_TRANSMIT "İletim" #define D_TRUE "True" #define D_TVOC "TVOC" @@ -220,7 +222,7 @@ // xdrv_02_webserver.ino #define D_NOSCRIPT "To use Tasmota, please enable JavaScript" -#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "Donanım yazılımı çok düşük
lütfen yükseltin" +#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "Donanım yazılımı çok düşük
lütfen yükseltin" #define D_WEBSERVER_ACTIVE_ON "Web sunucusu aktif" #define D_WITH_IP_ADDRESS "IP adres ile" #define D_WEBSERVER_STOPPED "Web sunucusu durdu" @@ -255,6 +257,7 @@ #define D_MODULE_PARAMETERS "Modül parametreleri" #define D_MODULE_TYPE "Modul türü" #define D_PULLUP_ENABLE "No Button/Switch pull-up" +#define D_ADC "ADC" #define D_GPIO "GPIO" #define D_SERIAL_IN "Serial In" #define D_SERIAL_OUT "Serial Out" @@ -304,8 +307,6 @@ #define D_TEMPLATE_NAME "Name" #define D_BASE_TYPE "Based on" #define D_TEMPLATE_FLAGS "Options" -#define D_ALLOW_ADC0 "ADC0 input" -#define D_ALLOW_PULLUP "User pull-up selection" #define D_SAVE_CONFIGURATION "Ayarları Kaydet" #define D_CONFIGURATION_SAVED "Ayarlar kaydedildi" @@ -492,6 +493,9 @@ #define D_TX20_SOUTH "S" #define D_TX20_WEST "W" +//xsns_43_hre.ino +#define D_LOG_HRE "HRE: " + // sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box #define D_SENSOR_NONE "None" #define D_SENSOR_USER "User" @@ -508,6 +512,7 @@ #define D_SENSOR_BUTTON "Button" // Suffix "1" #define D_SENSOR_RELAY "Relay" // Suffix "1i" #define D_SENSOR_LED "Led" // Suffix "1i" +#define D_SENSOR_LED_LINK "LedLink" // Suffix "i" #define D_SENSOR_PWM "PWM" // Suffix "1" #define D_SENSOR_COUNTER "Counter" // Suffix "1" #define D_SENSOR_IRRECV "IRrecv" @@ -573,14 +578,20 @@ #define D_SENSOR_MY92X1_DI "MY92x1 DI" #define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI" #define D_SENSOR_ARIRFRCV "ALux IrRcv" +#define D_SENSOR_ARIRFSEL "ALux IrSel" #define D_SENSOR_TXD "Serial Tx" #define D_SENSOR_RXD "Serial Rx" #define D_SENSOR_ROTARY "Rotary" // Suffix "1A" +#define D_SENSOR_HRE_CLOCK "HRE Clock" +#define D_SENSOR_HRE_DATA "HRE Data" +#define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CENTIMETER "cm" #define D_UNIT_HOUR "Hr" +#define D_UNIT_GALLONS "gal" +#define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_INCREMENTS "inc" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" diff --git a/sonoff/language/uk-UK.h b/sonoff/language/uk-UK.h index 0efdd1c94..55ef6781b 100644 --- a/sonoff/language/uk-UK.h +++ b/sonoff/language/uk-UK.h @@ -93,6 +93,7 @@ #define D_FALLBACK_TOPIC "Топік зворотнього зв'язку" #define D_FALSE "Помилково" #define D_FILE "Файл" +#define D_FLOW_RATE "Flow rate" #define D_FREE_MEMORY "Вільна память" #define D_FREQUENCY "Частота" #define D_GAS "Газ" @@ -156,6 +157,7 @@ #define D_TO "до" #define D_TOGGLE "Перекл." #define D_TOPIC "Топік" +#define D_TOTAL_USAGE "Total Usage" #define D_TRANSMIT "Передати" #define D_TRUE "Істина" #define D_TVOC "TVOC" @@ -220,7 +222,7 @@ // xdrv_02_webserver.ino #define D_NOSCRIPT "To use Tasmota, please enable JavaScript" -#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "Прошивка MINIMAL
будь-ласка оновіть" +#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "Прошивка MINIMAL
будь-ласка оновіть" #define D_WEBSERVER_ACTIVE_ON "Веб-сервер активний" #define D_WITH_IP_ADDRESS "з IP-адресом" #define D_WEBSERVER_STOPPED "Веб-сервер зупинений" @@ -255,6 +257,7 @@ #define D_MODULE_PARAMETERS "Параметри модулю" #define D_MODULE_TYPE "Тип модулю" #define D_PULLUP_ENABLE "No Button/Switch pull-up" +#define D_ADC "ADC" #define D_GPIO "GPIO" #define D_SERIAL_IN "Serial вхід" #define D_SERIAL_OUT "Serial вихід" @@ -304,8 +307,6 @@ #define D_TEMPLATE_NAME "Name" #define D_BASE_TYPE "Based on" #define D_TEMPLATE_FLAGS "Options" -#define D_ALLOW_ADC0 "ADC0 input" -#define D_ALLOW_PULLUP "User pull-up selection" #define D_SAVE_CONFIGURATION "Зберегти конфігурацію" #define D_CONFIGURATION_SAVED "Конфігурація збережена " @@ -492,6 +493,9 @@ #define D_TX20_SOUTH "S" #define D_TX20_WEST "W" +//xsns_43_hre.ino +#define D_LOG_HRE "HRE: " + // sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box #define D_SENSOR_NONE "-відсутньо-" #define D_SENSOR_USER "User" @@ -508,6 +512,7 @@ #define D_SENSOR_BUTTON "Кнопка" // Suffix "1" #define D_SENSOR_RELAY "Реле" // Suffix "1i" #define D_SENSOR_LED "Led" // Suffix "1i" +#define D_SENSOR_LED_LINK "LedLink" // Suffix "i" #define D_SENSOR_PWM "PWM" // Suffix "1" #define D_SENSOR_COUNTER "Лічильник" // Suffix "1" #define D_SENSOR_IRRECV "IRrecv" @@ -573,15 +578,21 @@ #define D_SENSOR_MY92X1_DI "MY92x1 DI" #define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI" #define D_SENSOR_ARIRFRCV "ALux IrRcv" +#define D_SENSOR_ARIRFSEL "ALux IrSel" #define D_SENSOR_TXD "Serial Tx" #define D_SENSOR_RXD "Serial Rx" #define D_SENSOR_ROTARY "Rotary" // Suffix "1A" +#define D_SENSOR_HRE_CLOCK "HRE Clock" +#define D_SENSOR_HRE_DATA "HRE Data" +#define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" // Units #define D_UNIT_AMPERE "А" #define D_UNIT_CENTIMETER "cм" #define D_UNIT_HERTZ "Гц" #define D_UNIT_HOUR "Г" +#define D_UNIT_GALLONS "gal" +#define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_INCREMENTS "inc" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" diff --git a/sonoff/language/zh-CN.h b/sonoff/language/zh-CN.h index 88767053b..aa4a60492 100644 --- a/sonoff/language/zh-CN.h +++ b/sonoff/language/zh-CN.h @@ -93,6 +93,7 @@ #define D_FALLBACK_TOPIC "回退主题" #define D_FALSE "False" #define D_FILE "文件:" +#define D_FLOW_RATE "Flow rate" #define D_FREE_MEMORY "空闲内存" #define D_FREQUENCY "频率" #define D_GAS "气体" @@ -156,6 +157,7 @@ #define D_TO "to" #define D_TOGGLE "切换" #define D_TOPIC "主题" +#define D_TOTAL_USAGE "Total Usage" #define D_TRANSMIT "发送" #define D_TRUE "True" #define D_TVOC "TVOC" @@ -220,7 +222,7 @@ // webserver.ino #define D_NOSCRIPT "Tasmota要求浏览器支持 JavaScript" -#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "当前是精简版固件
请升级" +#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "当前是精简版固件
请升级" #define D_WEBSERVER_ACTIVE_ON "Web 服务器地址:" #define D_WITH_IP_ADDRESS "IP 地址:" #define D_WEBSERVER_STOPPED "Web 服务已停止" @@ -255,6 +257,7 @@ #define D_MODULE_PARAMETERS "模块设置" #define D_MODULE_TYPE "模块类型" #define D_PULLUP_ENABLE "没有按钮/开关上拉" +#define D_ADC "ADC" #define D_GPIO "GPIO" #define D_SERIAL_IN "串口输入(RX)" #define D_SERIAL_OUT "串口输出(TX)" @@ -304,8 +307,6 @@ #define D_TEMPLATE_NAME "名称" #define D_BASE_TYPE "基于" #define D_TEMPLATE_FLAGS "选项" -#define D_ALLOW_ADC0 "ADC0 输入" -#define D_ALLOW_PULLUP "用户上拉选择" #define D_SAVE_CONFIGURATION "保存设置" #define D_CONFIGURATION_SAVED "设置已保存" @@ -492,6 +493,9 @@ #define D_TX20_SOUTH "南" #define D_TX20_WEST "西" +//xsns_43_hre.ino +#define D_LOG_HRE "HRE: " + // sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box #define D_SENSOR_NONE "无" #define D_SENSOR_USER "User" @@ -508,6 +512,7 @@ #define D_SENSOR_BUTTON "Button" // Suffix "1" #define D_SENSOR_RELAY "Relay" // Suffix "1i" #define D_SENSOR_LED "Led" // Suffix "1i" +#define D_SENSOR_LED_LINK "LedLink" // Suffix "i" #define D_SENSOR_PWM "PWM" // Suffix "1" #define D_SENSOR_COUNTER "Counter" // Suffix "1" #define D_SENSOR_IRRECV "IRrecv" @@ -573,14 +578,20 @@ #define D_SENSOR_MY92X1_DI "MY92x1 DI" #define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI" #define D_SENSOR_ARIRFRCV "ALux IrRcv" +#define D_SENSOR_ARIRFSEL "ALux IrSel" #define D_SENSOR_TXD "Serial Tx" #define D_SENSOR_RXD "Serial Rx" #define D_SENSOR_ROTARY "Rotary" // Suffix "1A" +#define D_SENSOR_HRE_CLOCK "HRE Clock" +#define D_SENSOR_HRE_DATA "HRE Data" +#define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" // Units #define D_UNIT_AMPERE "安" #define D_UNIT_CENTIMETER "厘米" #define D_UNIT_HOUR "时" +#define D_UNIT_GALLONS "gal" +#define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_INCREMENTS "inc" #define D_UNIT_KILOGRAM "千克" #define D_UNIT_KILOMETER_PER_HOUR "公里/时" // or "km/h" diff --git a/sonoff/language/zh-TW.h b/sonoff/language/zh-TW.h index 11f85eef2..2171d1ea0 100644 --- a/sonoff/language/zh-TW.h +++ b/sonoff/language/zh-TW.h @@ -93,6 +93,7 @@ #define D_FALLBACK_TOPIC "Fallback Topic" #define D_FALSE "False" #define D_FILE "文件:" +#define D_FLOW_RATE "Flow rate" #define D_FREE_MEMORY "可用記憶體" #define D_FREQUENCY "Frequency" #define D_GAS "氣體" @@ -156,6 +157,7 @@ #define D_TO "to" #define D_TOGGLE "切換" #define D_TOPIC "主題" +#define D_TOTAL_USAGE "Total Usage" #define D_TRANSMIT "發送" #define D_TRUE "True" #define D_TVOC "TVOC" @@ -220,7 +222,7 @@ // xdrv_02_webserver.ino #define D_NOSCRIPT "To use Tasmota, please enable JavaScript" -#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "固件版本過低
請升級" +#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "固件版本過低
請升級" #define D_WEBSERVER_ACTIVE_ON "Web服務器:" #define D_WITH_IP_ADDRESS "IP地址:" #define D_WEBSERVER_STOPPED "Web 服務器已停止" @@ -255,6 +257,7 @@ #define D_MODULE_PARAMETERS "模塊設置" #define D_MODULE_TYPE "模塊類型" #define D_PULLUP_ENABLE "No Button/Switch pull-up" +#define D_ADC "ADC" #define D_GPIO "GPIO" #define D_SERIAL_IN "串口輸入(RX)" #define D_SERIAL_OUT "串口輸出(TX)" @@ -304,8 +307,6 @@ #define D_TEMPLATE_NAME "Name" #define D_BASE_TYPE "Based on" #define D_TEMPLATE_FLAGS "Options" -#define D_ALLOW_ADC0 "ADC0 input" -#define D_ALLOW_PULLUP "User pull-up selection" #define D_SAVE_CONFIGURATION "保存設置" #define D_CONFIGURATION_SAVED "設置已保存" @@ -492,6 +493,9 @@ #define D_TX20_SOUTH "S" #define D_TX20_WEST "W" +//xsns_43_hre.ino +#define D_LOG_HRE "HRE: " + // sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box #define D_SENSOR_NONE "None" #define D_SENSOR_USER "User" @@ -508,6 +512,7 @@ #define D_SENSOR_BUTTON "Button" // Suffix "1" #define D_SENSOR_RELAY "Relay" // Suffix "1i" #define D_SENSOR_LED "Led" // Suffix "1i" +#define D_SENSOR_LED_LINK "LedLink" // Suffix "i" #define D_SENSOR_PWM "PWM" // Suffix "1" #define D_SENSOR_COUNTER "Counter" // Suffix "1" #define D_SENSOR_IRRECV "IRrecv" @@ -573,15 +578,21 @@ #define D_SENSOR_MY92X1_DI "MY92x1 DI" #define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI" #define D_SENSOR_ARIRFRCV "ALux IrRcv" +#define D_SENSOR_ARIRFSEL "ALux IrSel" #define D_SENSOR_TXD "Serial Tx" #define D_SENSOR_RXD "Serial Rx" #define D_SENSOR_ROTARY "Rotary" // Suffix "1A" +#define D_SENSOR_HRE_CLOCK "HRE Clock" +#define D_SENSOR_HRE_DATA "HRE Data" +#define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" // Units #define D_UNIT_AMPERE "安" #define D_UNIT_CENTIMETER "cm" #define D_UNIT_HERTZ "Hz" #define D_UNIT_HOUR "時" +#define D_UNIT_GALLONS "gal" +#define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_INCREMENTS "inc" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" diff --git a/sonoff/my_user_config.h b/sonoff/my_user_config.h index b7e0846e4..5c692ee95 100644 --- a/sonoff/my_user_config.h +++ b/sonoff/my_user_config.h @@ -88,7 +88,7 @@ #define MQTT_USE 1 // [SetOption3] Select default MQTT use (0 = Off, 1 = On) #define MQTT_HOST "" // [MqttHost] -#define MQTT_FINGERPRINT1 "A5 02 FF 13 99 9F 8B 39 8E F1 83 4F 11 23 65 0B 32 36 FC 07" // [MqttFingerprint1] +#define MQTT_FINGERPRINT1 "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" // [MqttFingerprint1] #define MQTT_FINGERPRINT2 "A5 02 FF 13 99 9F 8B 39 8E F1 83 4F 11 23 65 0B 32 36 FC 07" // [MqttFingerprint2] #define MQTT_PORT 1883 // [MqttPort] MQTT port (10123 on CloudMQTT) #define MQTT_USER "DVES_USER" // [MqttUser] MQTT user @@ -135,6 +135,25 @@ #define WEB_PASSWORD "" // [WebPassword] Web server Admin mode Password for WEB_USERNAME (empty string = Disable) #define FRIENDLY_NAME "Sonoff" // [FriendlyName] Friendlyname up to 32 characters used by webpages and Alexa #define EMULATION EMUL_NONE // [Emulation] Select Belkin WeMo (single relay/light) or Hue Bridge emulation (multi relay/light) (EMUL_NONE, EMUL_WEMO or EMUL_HUE) +// HTML hex color codes. Only 3 and 6 digit hex string values are supported!! See https://www.w3schools.com/colors/colors_hex.asp +#define COLOR_TEXT "#000" // [WebColor1] Global text color - Black +#define COLOR_BACKGROUND "#fff" // [WebColor2] Global background color - White +#define COLOR_FORM "#f2f2f2" // [WebColor3] Form background color - Greyish +#define COLOR_INPUT_TEXT "#000" // [WebColor4] Input text color - Black +#define COLOR_INPUT "#fff" // [WebColor5] Input background color - White +#define COLOR_CONSOLE_TEXT "#000" // [WebColor6] Console text color - Black +#define COLOR_CONSOLE "#fff" // [WebColor7] Console background color - White +#define COLOR_TEXT_WARNING "#f00" // [WebColor8] Warning text color - Red +#define COLOR_TEXT_SUCCESS "#008000" // [WebColor9] Success text color - Green +#define COLOR_BUTTON_TEXT "#fff" // [WebColor10] Button text color - White +#define COLOR_BUTTON "#1fa3ec" // [WebColor11] Button color - Blueish +#define COLOR_BUTTON_HOVER "#0e70a4" // [WebColor12] Button color when hovered over - Darker blueish +#define COLOR_BUTTON_RESET "#d43535" // [WebColor13] Restart/Reset/Delete button color - Redish +#define COLOR_BUTTON_RESET_HOVER "#931f1f" // [WebColor14] Restart/Reset/Delete button color when hovered over - Darker redish +#define COLOR_BUTTON_SAVE "#47c266" // [WebColor15] Save button color - Greenish +#define COLOR_BUTTON_SAVE_HOVER "#5aaf6f" // [WebColor16] Save button color when hovered over - Darker greenish +#define COLOR_TIMER_TAB_TEXT "#fff" // [WebColor17] Config timer tab text color - White +#define COLOR_TIMER_TAB_BACKGROUND "#999" // [WebColor18] Config timer tab background color - Light grey // -- mDNS ---------------------------------------- #define MDNS_ENABLED 0 // [SetOption55] Use mDNS (0 = Disable, 1 = Enable) @@ -168,6 +187,7 @@ #define APP_TIMEZONE 1 // [Timezone] +1 hour (Amsterdam) (-13 .. 14 = 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_LEDMASK 0xFFFF // [LedMask] Assign Relay to Power led (0xFFFF is default) #define APP_PULSETIME 0 // [PulseTime] Time in 0.1 Sec to turn off power for relay 1 (0 = disabled) #define APP_POWERON_STATE POWER_ALL_SAVED // [PowerOnState] Power On Relay state // (POWER_ALL_OFF, POWER_ALL_ON, POWER_ALL_SAVED_TOGGLE, POWER_ALL_SAVED, POWER_ALL_ALWAYS_ON, POWER_ALL_OFF_PULSETIME_ON) @@ -204,7 +224,7 @@ //#define MY_LANGUAGE de-DE // German in Germany //#define MY_LANGUAGE el-GR // Greek in Greece //#define MY_LANGUAGE en-GB // English in Great Britain. Enabled by Default -//#define MY_LANGUAGE es-AR // Spanish in Argentina +//#define MY_LANGUAGE es-ES // Spanish in Spain //#define MY_LANGUAGE fr-FR // French in France //#define MY_LANGUAGE he-HE // Hebrew in Israel //#define MY_LANGUAGE hu-HU // Hungarian in Hungary @@ -242,10 +262,13 @@ #define USE_HOME_ASSISTANT // Enable Home Assistant Discovery Support (+7k code) #define HOME_ASSISTANT_DISCOVERY_PREFIX "homeassistant" // Home Assistant discovery prefix -// -- MQTT - TLS ---------------------------------- - // !!! TLS uses a LOT OF MEMORY so be careful to enable other options at the same time !!! -//#define USE_MQTT_TLS // Use TLS for MQTT connection (+53k code, +15k mem) -// #define USE_MQTT_TLS_CA_CERT // Use LetsEncrypt Certificate from sonoff_letsencrypt.h - Not supported with core 2.3.0 +// -- MQTT - TLS - AWS IoT ------------------------ +// Using TLS starting with version v6.5.0.16 compilation will only work using Core 2.4.2 and 2.5.2. No longer supported: 2.3.0 +//#define USE_MQTT_TLS // Use TLS for MQTT connection (+34.5k code, +7.0k mem and +4.8k additional during connection handshake) +// #define USE_MQTT_TLS_CA_CERT // Force full CA validation instead of fingerprints, slower, but simpler to use (+2.2k code, +1.9k mem during connection handshake) +// #define USE_MQTT_AWS_IOT // Enable MQTT for AWS IoT - requires a private key (+11.4k code, +0.4k mem) + // Note: you need to generate a private key + certificate per device and update 'sonoff/sonoff_aws_iot.cpp' + // Full documentation here: https://github.com/arendst/Sonoff-Tasmota/wiki/AWS-IoT // -- KNX IP Protocol ----------------------------- //#define USE_KNX // Enable KNX IP Protocol Support (+9.4k code, +3k7 mem) @@ -255,10 +278,12 @@ #define USE_WEBSERVER // Enable web server and Wifi Manager (+66k code, +8k mem) #define WEB_PORT 80 // Web server Port for User and Admin mode #define WEB_USERNAME "admin" // Web server Admin mode user name - #define USE_EMULATION // Enable Belkin WeMo and Hue Bridge emulation for Alexa (+16k code, +2k mem) +// #define USE_JAVASCRIPT_ES6 // Enable ECMAScript6 syntax using less JavaScript code bytes (fails on IE11) + #define USE_EMULATION_HUE // Enable Hue Bridge emulation for Alexa (+14k code, +2k mem common) + #define USE_EMULATION_WEMO // Enable Belkin WeMo emulation for Alexa (+6k code, +2k mem common) // -- mDNS ---------------------------------------- -#define USE_DISCOVERY // Enable mDNS for the following services (+8k code, +0.3k mem) +#define USE_DISCOVERY // Enable mDNS for the following services (+8k code or +23.5k code with core 2_5_x, +0.3k mem) #define WEBSERVER_ADVERTISE // Provide access to webserver by name .local/ #define MQTT_HOST_DISCOVERY // Find MQTT host server (overrides MQTT_HOST if found) @@ -268,13 +293,20 @@ #define USE_SUNRISE // Add support for Sunrise and sunset tools (+16k) #define SUNRISE_DAWN_ANGLE DAWN_NORMAL // Select desired Dawn Angle from (DAWN_NORMAL, DAWN_CIVIL, DAWN_NAUTIC, DAWN_ASTRONOMIC) -// -- Rules --------------------------------------- -#define USE_RULES // Add support for rules (+4k4 code) +// -- Rules or Script ---------------------------- +// Select none or only one of the below defines +#define USE_RULES // Add support for rules (+8k code) +//#define USE_SCRIPT // Add support for script (+17k code) + #define USE_SCRIPT_FATFS 4 + // #define USE_EXPRESSION // Add support for expression evaluation in rules (+3k2 code, +64 bytes mem) // #define SUPPORT_MQTT_EVENT // Support trigger event with MQTT subscriptions (+3k5 code) +// -- Counter input ----------------------- +#define USE_COUNTER // Enable inputs as counter (+0k8 code) + // -- Internal Analog input ----------------------- -#define USE_ADC_VCC // Display Vcc in Power status. Disable for use as Analog input on selected devices +//#define USE_ADC_VCC // Display Vcc in Power status. Disable for use as Analog input on selected devices // -- One wire sensors ---------------------------- // WARNING: Select none for default one DS18B20 sensor or enable one of the following two options for multiple sensors @@ -302,7 +334,7 @@ // #define USE_TSL2561 // Enable TSL2561 sensor (I2C address 0x29, 0x39 or 0x49) using library Joba_Tsl2561 (+2k3 code) // #define USE_MGS // Enable Xadow and Grove Mutichannel Gas sensor using library Multichannel_Gas_Sensor (+10k code) #define MGS_SENSOR_ADDR 0x04 // Default Mutichannel Gas sensor i2c address - #define USE_SGP30 // Enable SGP30 sensor (I2C address 0x58) (+1k1 code) +// #define USE_SGP30 // Enable SGP30 sensor (I2C address 0x58) (+1k1 code) // #define USE_SI1145 // Enable SI1145/46/47 sensor (I2C address 0x60) (+1k code) #define USE_LM75AD // Enable LM75AD sensor (I2C addresses 0x48 - 0x4F) (+0k5 code) // #define USE_APDS9960 // Enable APDS9960 Proximity Sensor (I2C address 0x39). Disables SHT and VEML6070 (+4k7 code) @@ -322,6 +354,10 @@ // #define USE_MGC3130 // Enable MGC3130 Electric Field Effect Sensor (I2C address 0x42) (+2k7 code, 0k3 mem) // #define USE_MAX44009 // Enable MAX44009 Ambient Light sensor (I2C addresses 0x4A and 0x4B) (+0k8 code) // #define USE_SCD30 // Enable Sensiron SCd30 CO2 sensor (I2C address 0x61) (+3k3 code) +// #define USE_SPS30 // Enable Sensiron SPS30 particle sensor (I2C address 0x69) (+1.7 code) + #define USE_ADE7953 // Enable ADE7953 Energy monitor as used on Shelly 2.5 (I2C address 0x38) (+1k5) +// #define USE_VL53L0X // Enable VL53L0x time of flight sensor (I2C address 0x29) (+4k code) +// #define USE_MLX90614 // Enable MLX90614 ir temp sensor (I2C address 0x5a) (+0.6k code) // #define USE_DISPLAY // Add I2C Display Support (+2k code) #define USE_DISPLAY_MODES1TO5 // Enable display mode 1 to 5 in addition to mode 0 @@ -368,7 +404,8 @@ #define USE_TUYA_DIMMER // Add support for Tuya Serial Dimmer #define TUYA_DIMMER_ID 0 // Default dimmer Id #define USE_ARMTRONIX_DIMMERS // Add support for Armtronix Dimmers (+1k4 code) -#define USE_PS_16_DZ // Add support for PS-16-DZ Dimmer +#define USE_PS_16_DZ // Add support for PS-16-DZ Dimmer and Sonoff L1 (+2k code) +//#define ROTARY_V1 // Add support for MI Desk Lamp //#define USE_AZ7798 // Add support for AZ-Instrument 7798 CO2 datalogger (+1k6 code) //#define USE_PN532_HSU // Add support for PN532 using HSU (Serial) interface (+1k8 code, 140 bytes mem) // #define USE_PN532_CAUSE_EVENTS // Cause event execution for PN532_UID= and PN532_DATA=[if defined] (+ 30 bytes code) @@ -382,6 +419,8 @@ #define USE_MCP39F501 // Add support for MCP39F501 Energy monitor as used in Shelly 2 (+3k1 code) // -- Low level interface devices ----------------- +#define USE_DHT // Add support for DHT11, AM2301 (DHT21, DHT22, AM2302, AM2321) and SI7021 Temperature and Humidity sensor (1k6 code) + //#define USE_MAX31855 // Add support for MAX31855 K-Type thermocouple sensor using softSPI #define USE_IR_REMOTE // Send IR remote commands using library IRremoteESP8266 and ArduinoJson (+4k3 code, 0k3 mem, 48 iram) @@ -389,7 +428,7 @@ #define USE_IR_RECEIVE // Support for IR receiver (+7k2 code, 264 iram) #define IR_RCV_BUFFER_SIZE 100 // Max number of packets allowed in capture buffer (default 100 (*2 bytes ram)) #define IR_RCV_TIMEOUT 15 // Number of milli-Seconds of no-more-data before we consider a message ended (default 15) - #define IR_RCV_MIN_UNKNOWN_SIZE 6 // Set the smallest sized "UNKNOWN" message packets we actually care about (default 6) + #define IR_RCV_MIN_UNKNOWN_SIZE 6 // Set the smallest sized "UNKNOWN" message packets we actually care about (default 6, max 255) #define USE_WS2812 // WS2812 Led string using library NeoPixelBus (+5k code, +1k mem, 232 iram) - Disable by // #define USE_WS2812_CTYPE NEO_GRB // WS2812 Color type (NEO_RGB, NEO_GRB, NEO_BRG, NEO_RBG, NEO_RGBW, NEO_GRBW) @@ -415,6 +454,8 @@ #define USE_SM16716 // Add support for SM16716 RGB LED controller (+0k7 code) +//#define USE_HRE // Add support for Badger HR-E Water Meter (+1k4 code) + /*********************************************************************************************\ * Debug features are only supported in development branch \*********************************************************************************************/ @@ -438,8 +479,8 @@ * No user configurable items below \*********************************************************************************************/ -#if defined(USE_MQTT_TLS) && defined(USE_WEBSERVER) - #error "Select either USE_MQTT_TLS or USE_WEBSERVER as there is just not enough memory to play with" +#if defined(USE_DISCOVERY) && defined(USE_MQTT_AWS_IOT) + #error "Select either USE_DISCOVERY or USE_MQTT_AWS_IOT, mDNS takes too much code space and is not needed for AWS IoT" #endif #endif // _MY_USER_CONFIG_H_ diff --git a/sonoff/settings.h b/sonoff/settings.h index 5a7827365..0fa7f5d48 100644 --- a/sonoff/settings.h +++ b/sonoff/settings.h @@ -20,7 +20,7 @@ #ifndef _SETTINGS_H_ #define _SETTINGS_H_ -#define PARAM8_SIZE 18 // Number of param bytes (SetOption) +const uint8_t PARAM8_SIZE = 18; // Number of param bytes (SetOption) typedef union { // Restricted by MISRA-C Rule 18.4 but so useful... uint32_t data; // Allow bit manipulation using SetOption @@ -76,9 +76,9 @@ typedef union { // Restricted by MISRA-C Rule 18.4 bu uint32_t sleep_normal : 1; // bit 10 (v6.3.0.15) - SetOption60 - Enable normal sleep instead of dynamic sleep uint32_t button_switch_force_local : 1;// bit 11 (v6.3.0.16) - SetOption61 - Force local operation when button/switch topic is set uint32_t no_hold_retain : 1; // bit 12 (v6.4.1.19) - SetOption62 - Don't use retain flag on HOLD messages - uint32_t spare13 : 1; - uint32_t spare14 : 1; - uint32_t spare15 : 1; + uint32_t no_power_feedback : 1; // bit 13 (v6.5.0.9) - SetOption63 - Don't scan relay power state at restart + uint32_t use_underscore : 1; // bit 14 (v6.5.0.12) - SetOption64 - Enable "_" instead of "-" as sensor index separator + uint32_t tuya_show_dimmer : 1; // bit 15 (v6.5.0.15) - SetOption65 - Enable or Disable Dimmer slider control uint32_t spare16 : 1; uint32_t spare17 : 1; uint32_t spare18 : 1; @@ -185,12 +185,12 @@ struct SYSCFG { unsigned long bootcount; // 00C */ struct SYSCFG { - uint16_t cfg_holder; // 000 v6 header - uint16_t cfg_size; // 002 + uint16_t cfg_holder; // 000 v6 header + uint16_t cfg_size; // 002 unsigned long save_flag; // 004 unsigned long version; // 008 - uint16_t bootcount; // 00C - uint16_t cfg_crc; // 00E + uint16_t bootcount; // 00C + uint16_t cfg_crc; // 00E SysBitfield flag; // 010 int16_t save_data; // 014 int8_t timezone; // 016 @@ -210,9 +210,11 @@ struct SYSCFG { uint8_t webserver; // 1AB uint8_t weblog_level; // 1AC uint8_t mqtt_fingerprint[2][20]; // 1AD + uint8_t adc_param_type; // 1D5 - uint8_t free_1D5[20]; // 1D5 Free since 5.12.0e + uint8_t free_1D6[18]; // 1D6 Free since 5.12.0e + uint8_t sps30_inuse_hours; // 1E8 char mqtt_host[33]; // 1E9 - Keep together with below as being copied as one chunck with reset 6 uint16_t mqtt_port; // 20A - Keep together char mqtt_client[33]; // 20C - Keep together @@ -285,7 +287,7 @@ struct SYSCFG { uint8_t ws_color[4][3]; // 475 uint8_t ws_width[3]; // 481 myio my_gp; // 484 - uint8_t test_step; // 495 + uint8_t my_adc0; // 495 uint16_t light_pixels; // 496 uint8_t light_color[5]; // 498 uint8_t light_correction; // 49D @@ -330,22 +332,25 @@ struct SYSCFG { uint8_t rgbwwTable[5]; // 71A uint8_t user_template_base; // 71F mytmplt user_template; // 720 29 bytes + uint8_t novasds_period; // 73D + uint8_t web_color[18][3]; // 73E - uint8_t free_73D[87]; // 73D + uint8_t free_774[32]; // 774 - uint32_t drivers[3]; // 794 +// uint32_t drivers[3]; // 794 - 6.5.0.12 replaced by below three entries + uint32_t adc_param1; // 794 + uint32_t adc_param2; // 798 + int adc_param3; // 79C uint32_t monitors; // 7A0 uint32_t sensors[3]; // 7A4 uint32_t displays; // 7B0 uint32_t energy_kWhtotal_time; // 7B4 unsigned long weight_item; // 7B8 Weight of one item in gram * 10 - - uint8_t free_7BC[2]; // 7BC - + uint16_t ledmask; // 7BC uint16_t weight_max; // 7BE Total max weight in kilogram unsigned long weight_reference; // 7C0 Reference weight in gram unsigned long weight_calibration; // 7C4 - unsigned long energy_frequency_calibration; // 7C8 + unsigned long energy_frequency_calibration; // 7C8 also used by HX711 to save last weight uint16_t web_refresh; // 7CC char mems[MAX_RULE_MEMS][10]; // 7CE char rules[MAX_RULE_SETS][MAX_RULE_SIZE]; // 800 uses 512 bytes in v5.12.0m, 3 x 512 bytes in v5.14.0b @@ -396,7 +401,7 @@ struct XDRVMAILBOX { char *data; } XdrvMailbox; -#define MAX_RULES_FLAG 7 // Number of bits used in RulesBitfield (tricky I know...) +const uint8_t MAX_RULES_FLAG = 8; // Number of bits used in RulesBitfield (tricky I know...) typedef union { // Restricted by MISRA-C Rule 18.4 but so useful... uint16_t data; // Allow bit manipulation struct { @@ -407,7 +412,7 @@ typedef union { // Restricted by MISRA-C Rule 18.4 bu uint16_t mqtt_disconnected : 1; uint16_t wifi_connected : 1; uint16_t wifi_disconnected : 1; - uint16_t spare07 : 1; + uint16_t http_init : 1; uint16_t spare08 : 1; uint16_t spare09 : 1; uint16_t spare10 : 1; diff --git a/sonoff/settings.ino b/sonoff/settings.ino index dff0d849f..eae6d3817 100644 --- a/sonoff/settings.ino +++ b/sonoff/settings.ino @@ -18,36 +18,36 @@ */ #ifndef DOMOTICZ_UPDATE_TIMER -#define DOMOTICZ_UPDATE_TIMER 0 // [DomoticzUpdateTimer] Send relay status (0 = disable, 1 - 3600 seconds) (Optional) +#define DOMOTICZ_UPDATE_TIMER 0 // [DomoticzUpdateTimer] Send relay status (0 = disable, 1 - 3600 seconds) (Optional) #endif #ifndef EMULATION -#define EMULATION EMUL_NONE // [Emulation] Select Belkin WeMo (single relay/light) or Hue Bridge emulation (multi relay/light) (EMUL_NONE, EMUL_WEMO or EMUL_HUE) +#define EMULATION EMUL_NONE // [Emulation] Select Belkin WeMo (single relay/light) or Hue Bridge emulation (multi relay/light) (EMUL_NONE, EMUL_WEMO or EMUL_HUE) #endif -#ifndef MTX_ADDRESS1 // Add Display Support for up to eigth Matrices -#define MTX_ADDRESS1 0 +#ifndef MTX_ADDRESS1 // Add Display Support for up to eigth Matrices +#define MTX_ADDRESS1 0 #endif #ifndef MTX_ADDRESS2 -#define MTX_ADDRESS2 0 +#define MTX_ADDRESS2 0 #endif #ifndef MTX_ADDRESS3 -#define MTX_ADDRESS3 0 +#define MTX_ADDRESS3 0 #endif #ifndef MTX_ADDRESS4 -#define MTX_ADDRESS4 0 +#define MTX_ADDRESS4 0 #endif #ifndef MTX_ADDRESS5 -#define MTX_ADDRESS5 0 +#define MTX_ADDRESS5 0 #endif #ifndef MTX_ADDRESS6 -#define MTX_ADDRESS6 0 +#define MTX_ADDRESS6 0 #endif #ifndef MTX_ADDRESS7 -#define MTX_ADDRESS7 0 +#define MTX_ADDRESS7 0 #endif #ifndef MTX_ADDRESS8 -#define MTX_ADDRESS8 0 +#define MTX_ADDRESS8 0 #endif #ifndef HOME_ASSISTANT_DISCOVERY_ENABLE @@ -55,17 +55,94 @@ #endif #ifndef LATITUDE -#define LATITUDE 48.858360 // [Latitude] Your location to be used with sunrise and sunset +#define LATITUDE 48.858360 // [Latitude] Your location to be used with sunrise and sunset #endif #ifndef LONGITUDE -#define LONGITUDE 2.294442 // [Longitude] Your location to be used with sunrise and sunset +#define LONGITUDE 2.294442 // [Longitude] Your location to be used with sunrise and sunset #endif +#ifndef WORKING_PERIOD +#define WORKING_PERIOD 5 // Working period of the SDS Sensor, Takes a reading every X Minutes +#endif + +#ifndef COLOR_TEXT +#define COLOR_TEXT "#000" // Global text color - Black +#endif +#ifndef COLOR_BACKGROUND +#define COLOR_BACKGROUND "#fff" // Global background color - White +#endif +#ifndef COLOR_FORM +#define COLOR_FORM "#f2f2f2" // Form background color - Greyish +#endif +#ifndef COLOR_INPUT_TEXT +#define COLOR_INPUT_TEXT "#000" // Input text color - Black +#endif +#ifndef COLOR_INPUT +#define COLOR_INPUT "#fff" // Input background color - White +#endif +#ifndef COLOR_CONSOLE_TEXT +#define COLOR_CONSOLE_TEXT "#000" // Console text color - Black +#endif +#ifndef COLOR_CONSOLE +#define COLOR_CONSOLE "#fff" // Console background color - White +#endif +#ifndef COLOR_TEXT_WARNING +#define COLOR_TEXT_WARNING "#f00" // Warning text color - Red +#endif +#ifndef COLOR_TEXT_SUCCESS +#define COLOR_TEXT_SUCCESS "#008000" // Success text color - Green +#endif +#ifndef COLOR_BUTTON_TEXT +#define COLOR_BUTTON_TEXT "#fff" // Button text color - White +#endif +#ifndef COLOR_BUTTON +#define COLOR_BUTTON "#1fa3ec" // Button color - Blueish +#endif +#ifndef COLOR_BUTTON_HOVER +#define COLOR_BUTTON_HOVER "#0e70a4" // Button color when hovered over - Darker blueish +#endif +#ifndef COLOR_BUTTON_RESET +#define COLOR_BUTTON_RESET "#d43535" // Restart/Reset/Delete button color - Redish +#endif +#ifndef COLOR_BUTTON_RESET_HOVER +#define COLOR_BUTTON_RESET_HOVER "#931f1f" // Restart/Reset/Delete button color when hovered over - Darker redish +#endif +#ifndef COLOR_BUTTON_SAVE +#define COLOR_BUTTON_SAVE "#47c266" // Save button color - Greenish +#endif +#ifndef COLOR_BUTTON_SAVE_HOVER +#define COLOR_BUTTON_SAVE_HOVER "#5aaf6f" // Save button color when hovered over - Darker greenish +#endif +#ifndef COLOR_TIMER_TAB_TEXT +#define COLOR_TIMER_TAB_TEXT "#fff" // Config timer tab text color - White +#endif +#ifndef COLOR_TIMER_TAB_BACKGROUND +#define COLOR_TIMER_TAB_BACKGROUND "#999" // Config timer tab background color - Light grey +#endif +#ifndef IR_RCV_MIN_UNKNOWN_SIZE +#define IR_RCV_MIN_UNKNOWN_SIZE 6 // Set the smallest sized "UNKNOWN" message packets we actually care about (default 6, max 255) +#endif + +enum WebColors { + COL_TEXT, COL_BACKGROUND, COL_FORM, + COL_INPUT_TEXT, COL_INPUT, COL_CONSOLE_TEXT, COL_CONSOLE, + COL_TEXT_WARNING, COL_TEXT_SUCCESS, + COL_BUTTON_TEXT, COL_BUTTON, COL_BUTTON_HOVER, COL_BUTTON_RESET, COL_BUTTON_RESET_HOVER, COL_BUTTON_SAVE, COL_BUTTON_SAVE_HOVER, + COL_TIMER_TAB_TEXT, COL_TIMER_TAB_BACKGROUND, + COL_LAST }; + +const char kWebColors[] PROGMEM = + COLOR_TEXT "|" COLOR_BACKGROUND "|" COLOR_FORM "|" + COLOR_INPUT_TEXT "|" COLOR_INPUT "|" COLOR_CONSOLE_TEXT "|" COLOR_CONSOLE "|" + COLOR_TEXT_WARNING "|" COLOR_TEXT_SUCCESS "|" + COLOR_BUTTON_TEXT "|" COLOR_BUTTON "|" COLOR_BUTTON_HOVER "|" COLOR_BUTTON_RESET "|" COLOR_BUTTON_RESET_HOVER "|" COLOR_BUTTON_SAVE "|" COLOR_BUTTON_SAVE_HOVER "|" + COLOR_TIMER_TAB_TEXT "|" COLOR_TIMER_TAB_BACKGROUND; + /*********************************************************************************************\ * RTC memory \*********************************************************************************************/ -#define RTC_MEM_VALID 0xA55A +const uint16_t RTC_MEM_VALID = 0xA55A; uint32_t rtc_settings_crc = 0; @@ -74,7 +151,7 @@ uint32_t GetRtcSettingsCrc(void) uint32_t crc = 0; uint8_t *bytes = (uint8_t*)&RtcSettings; - for (uint16_t i = 0; i < sizeof(RTCMEM); i++) { + for (uint32_t i = 0; i < sizeof(RTCMEM); i++) { crc += bytes[i]*(i+1); } return crc; @@ -97,7 +174,7 @@ void RtcSettingsLoad(void) RtcSettings.valid = RTC_MEM_VALID; RtcSettings.energy_kWhtoday = Settings.energy_kWhtoday; RtcSettings.energy_kWhtotal = Settings.energy_kWhtotal; - for (uint8_t i = 0; i < MAX_COUNTERS; i++) { + for (uint32_t i = 0; i < MAX_COUNTERS; i++) { RtcSettings.pulse_counter[i] = Settings.pulse_counter[i]; } RtcSettings.power = Settings.power; @@ -120,7 +197,7 @@ uint32_t GetRtcRebootCrc(void) uint32_t crc = 0; uint8_t *bytes = (uint8_t*)&RtcReboot; - for (uint16_t i = 0; i < sizeof(RTCRBT); i++) { + for (uint32_t i = 0; i < sizeof(RTCRBT); i++) { crc += bytes[i]*(i+1); } return crc; @@ -161,19 +238,30 @@ extern "C" { } #include "eboot_command.h" -extern "C" uint32_t _SPIFFS_end; +#if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_1) || defined(ARDUINO_ESP8266_RELEASE_2_4_2) || defined(ARDUINO_ESP8266_RELEASE_2_5_0) || defined(ARDUINO_ESP8266_RELEASE_2_5_1) || defined(ARDUINO_ESP8266_RELEASE_2_5_2) +extern "C" uint32_t _SPIFFS_end; // From libraries/EEPROM/EEPROM.cpp EEPROMClass -#define SPIFFS_END ((uint32_t)&_SPIFFS_end - 0x40200000) / SPI_FLASH_SEC_SIZE +const uint32_t SPIFFS_END = ((uint32_t)&_SPIFFS_end - 0x40200000) / SPI_FLASH_SEC_SIZE; + +#else // Core > 2.5.2 and STAGE + +extern "C" uint32_t _FS_end; +// From libraries/EEPROM/EEPROM.cpp EEPROMClass +const uint32_t SPIFFS_END = ((uint32_t)&_FS_end - 0x40200000) / SPI_FLASH_SEC_SIZE; + +#endif // Version 4.2 config = eeprom area -#define SETTINGS_LOCATION SPIFFS_END // No need for SPIFFS as it uses EEPROM area +const uint32_t SETTINGS_LOCATION = SPIFFS_END; // No need for SPIFFS as it uses EEPROM area // Version 5.2 allow for more flash space -#define CFG_ROTATES 8 // Number of flash sectors used (handles uploads) +const uint8_t CFG_ROTATES = 8; // Number of flash sectors used (handles uploads) /*********************************************************************************************\ - * EEPROM support based on EEPROM library and tuned for Tasmota + * Optional EEPROM support based on EEPROM library and tuned for Tasmota \*********************************************************************************************/ +//#define USE_EEPROM +#ifdef USE_EEPROM uint32_t eeprom_sector = SPIFFS_END; uint8_t* eeprom_data = 0; @@ -301,12 +389,12 @@ void EepromEnd(void) eeprom_size = 0; eeprom_dirty = false; } - +#endif // USE_EEPROM /********************************************************************************************/ uint16_t settings_crc = 0; uint32_t settings_location = SETTINGS_LOCATION; -uint8_t *settings_buffer = NULL; +uint8_t *settings_buffer = nullptr; /********************************************************************************************/ /* @@ -333,9 +421,9 @@ void SetFlashModeDout(void) void SettingsBufferFree(void) { - if (settings_buffer != NULL) { + if (settings_buffer != nullptr) { free(settings_buffer); - settings_buffer = NULL; + settings_buffer = nullptr; } } @@ -354,7 +442,7 @@ uint16_t GetSettingsCrc(void) uint16_t crc = 0; uint8_t *bytes = (uint8_t*)&Settings; - for (uint16_t i = 0; i < sizeof(SYSCFG); i++) { + for (uint32_t i = 0; i < sizeof(SYSCFG); i++) { if ((i < 14) || (i > 15)) { crc += bytes[i]*(i+1); } // Skip crc } return crc; @@ -368,7 +456,10 @@ void SettingsSaveAll(void) Settings.power = 0; } XsnsCall(FUNC_SAVE_BEFORE_RESTART); + XdrvCall(FUNC_SAVE_BEFORE_RESTART); +#ifdef USE_EEPROM EepromCommit(); +#endif SettingsSave(0); } @@ -411,6 +502,7 @@ void SettingsSave(uint8_t rotate) Settings.cfg_size = sizeof(SYSCFG); Settings.cfg_crc = GetSettingsCrc(); +#ifdef USE_EEPROM if (SPIFFS_END == settings_location) { uint8_t* flash_buffer; flash_buffer = new uint8_t[SPI_FLASH_SEC_SIZE]; @@ -428,9 +520,13 @@ void SettingsSave(uint8_t rotate) ESP.flashEraseSector(settings_location); ESP.flashWrite(settings_location * SPI_FLASH_SEC_SIZE, (uint32*)&Settings, sizeof(SYSCFG)); } +#else + ESP.flashEraseSector(settings_location); + ESP.flashWrite(settings_location * SPI_FLASH_SEC_SIZE, (uint32*)&Settings, sizeof(SYSCFG)); +#endif // USE_EEPROM if (!stop_flash_rotate && rotate) { - for (uint8_t i = 1; i < CFG_ROTATES; i++) { + for (uint32_t i = 1; i < CFG_ROTATES; i++) { ESP.flashEraseSector(settings_location -i); // Delete previous configurations by resetting to 0xFF delay(1); } @@ -456,13 +552,17 @@ void SettingsLoad(void) settings_location = 0; uint32_t flash_location = SETTINGS_LOCATION +1; - for (uint8_t i = 0; i < CFG_ROTATES; i++) { + uint16_t cfg_holder = 0; + for (uint32_t i = 0; i < CFG_ROTATES; i++) { flash_location--; ESP.flashRead(flash_location * SPI_FLASH_SEC_SIZE, (uint32*)&Settings, sizeof(SYSCFG)); bool valid = false; if (Settings.version > 0x06000000) { - valid = (Settings.cfg_crc == GetSettingsCrc()); + bool almost_valid = (Settings.cfg_crc == GetSettingsCrc()); + // Sometimes CRC on pages below FB, overwritten by OTA, is fine but Settings are still invalid. So check cfg_holder too + if (almost_valid && (0 == cfg_holder)) { cfg_holder = Settings.cfg_holder; } // At FB always active cfg_holder + valid = (cfg_holder == Settings.cfg_holder); } else { ESP.flashRead((flash_location -1) * SPI_FLASH_SEC_SIZE, (uint32*)&_SettingsH, sizeof(SYSCFGH)); valid = (Settings.cfg_holder == _SettingsH.cfg_holder); @@ -481,7 +581,7 @@ void SettingsLoad(void) } if (settings_location > 0) { ESP.flashRead(settings_location * SPI_FLASH_SEC_SIZE, (uint32*)&Settings, sizeof(SYSCFG)); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_CONFIG D_LOADED_FROM_FLASH_AT " %X, " D_COUNT " %d"), settings_location, Settings.save_flag); + AddLog_P2(LOG_LEVEL_NONE, PSTR(D_LOG_CONFIG D_LOADED_FROM_FLASH_AT " %X, " D_COUNT " %lu"), settings_location, Settings.save_flag); } #ifndef FIRMWARE_MINIMAL @@ -594,7 +694,7 @@ void SettingsDefaultSet2(void) Settings.interlock[0] = 0xFF; // Legacy support using all relays in one interlock group Settings.module = MODULE; ModuleDefault(WEMOS); -// for (uint8_t i = 0; i < sizeof(Settings.my_gp); i++) { Settings.my_gp.io[i] = GPIO_NONE; } +// for (uint32_t i = 0; i < sizeof(Settings.my_gp); i++) { Settings.my_gp.io[i] = GPIO_NONE; } strlcpy(Settings.friendlyname[0], FRIENDLY_NAME, sizeof(Settings.friendlyname[0])); strlcpy(Settings.friendlyname[1], FRIENDLY_NAME"2", sizeof(Settings.friendlyname[1])); strlcpy(Settings.friendlyname[2], FRIENDLY_NAME"3", sizeof(Settings.friendlyname[2])); @@ -608,8 +708,9 @@ void SettingsDefaultSet2(void) Settings.blinktime = APP_BLINKTIME; Settings.blinkcount = APP_BLINKCOUNT; Settings.ledstate = APP_LEDSTATE; + Settings.ledmask = APP_LEDMASK; Settings.pulse_timer[0] = APP_PULSETIME; -// for (uint8_t i = 1; i < MAX_PULSETIMERS; i++) { Settings.pulse_timer[i] = 0; } +// for (uint32_t i = 1; i < MAX_PULSETIMERS; i++) { Settings.pulse_timer[i] = 0; } // Serial Settings.baudrate = APP_BAUDRATE / 1200; @@ -649,7 +750,7 @@ void SettingsDefaultSet2(void) Settings.param[P_HOLD_TIME] = KEY_HOLD_TIME; // Default 4 seconds hold time // Switch - for (uint8_t i = 0; i < MAX_SWITCHES; i++) { Settings.switchmode[i] = SWITCH_MODE; } + for (uint32_t i = 0; i < MAX_SWITCHES; i++) { Settings.switchmode[i] = SWITCH_MODE; } // MQTT Settings.flag.mqtt_enabled = MQTT_USE; @@ -684,12 +785,12 @@ void SettingsDefaultSet2(void) char fingerprint[60]; strlcpy(fingerprint, MQTT_FINGERPRINT1, sizeof(fingerprint)); char *p = fingerprint; - for (uint8_t i = 0; i < 20; i++) { + for (uint32_t i = 0; i < 20; i++) { Settings.mqtt_fingerprint[0][i] = strtol(p, &p, 16); } strlcpy(fingerprint, MQTT_FINGERPRINT2, sizeof(fingerprint)); p = fingerprint; - for (uint8_t i = 0; i < 20; i++) { + for (uint32_t i = 0; i < 20; i++) { Settings.mqtt_fingerprint[1][i] = strtol(p, &p, 16); } Settings.tele_period = TELE_PERIOD; @@ -700,7 +801,7 @@ void SettingsDefaultSet2(void) // Settings.flag2.wattage_resolution = 0; Settings.flag2.energy_resolution = ENERGY_RESOLUTION; Settings.param[P_MAX_POWER_RETRY] = MAX_POWER_RETRY; - Settings.energy_power_delta = DEFAULT_POWER_DELTA; +// Settings.energy_power_delta = 0; Settings.energy_power_calibration = HLW_PREF_PULSE; Settings.energy_voltage_calibration = HLW_UREF_PULSE; Settings.energy_current_calibration = HLW_IREF_PULSE; @@ -724,18 +825,21 @@ void SettingsDefaultSet2(void) // Settings.energy_kWhtotal = 0; RtcSettings.energy_kWhtotal = 0; + // IRRemote + Settings.param[P_IR_UNKNOW_THRESHOLD] = IR_RCV_MIN_UNKNOWN_SIZE; + // RF Bridge -// for (uint8_t i = 0; i < 17; i++) { Settings.rf_code[i][0] = 0; } +// for (uint32_t i = 0; i < 17; i++) { Settings.rf_code[i][0] = 0; } memcpy_P(Settings.rf_code[0], kDefaultRfCode, 9); // Domoticz Settings.domoticz_update_timer = DOMOTICZ_UPDATE_TIMER; -// for (uint8_t i = 0; i < MAX_DOMOTICZ_IDX; i++) { +// for (uint32_t i = 0; i < MAX_DOMOTICZ_IDX; i++) { // Settings.domoticz_relay_idx[i] = 0; // Settings.domoticz_key_idx[i] = 0; // Settings.domoticz_switch_idx[i] = 0; // } -// for (uint8_t i = 0; i < MAX_DOMOTICZ_SNS_IDX; i++) { +// for (uint32_t i = 0; i < MAX_DOMOTICZ_SNS_IDX; i++) { // Settings.domoticz_sensor_idx[i] = 0; // } @@ -750,7 +854,7 @@ void SettingsDefaultSet2(void) // Rules // Settings.rule_enabled = 0; // Settings.rule_once = 0; -// for (uint8_t i = 1; i < MAX_RULE_SETS; i++) { Settings.rules[i][0] = '\0'; } +// for (uint32_t i = 1; i < MAX_RULE_SETS; i++) { Settings.rules[i][0] = '\0'; } Settings.flag2.calc_resolution = CALC_RESOLUTION; // Home Assistant @@ -768,11 +872,11 @@ void SettingsDefaultSet2(void) //Settings.flag.decimal_text = 0; Settings.pwm_frequency = PWM_FREQ; Settings.pwm_range = PWM_RANGE; - for (uint8_t i = 0; i < MAX_PWMS; i++) { + for (uint32_t i = 0; i < MAX_PWMS; i++) { Settings.light_color[i] = 255; // Settings.pwm_value[i] = 0; } -// Settings.light_correction = 0; + Settings.light_correction = 1; Settings.light_dimmer = 10; // Settings.light_fade = 0; Settings.light_speed = 1; @@ -797,8 +901,8 @@ void SettingsDefaultSet2(void) strlcpy(Settings.ntp_server[0], NTP_SERVER1, sizeof(Settings.ntp_server[0])); strlcpy(Settings.ntp_server[1], NTP_SERVER2, sizeof(Settings.ntp_server[1])); strlcpy(Settings.ntp_server[2], NTP_SERVER3, sizeof(Settings.ntp_server[2])); - for (uint8_t j = 0; j < 3; j++) { - for (uint8_t i = 0; i < strlen(Settings.ntp_server[j]); i++) { + for (uint32_t j = 0; j < 3; j++) { + for (uint32_t i = 0; i < strlen(Settings.ntp_server[j]); i++) { if (Settings.ntp_server[j][i] == ',') { Settings.ntp_server[j][i] = '.'; } @@ -811,11 +915,15 @@ void SettingsDefaultSet2(void) Settings.button_debounce = KEY_DEBOUNCE_TIME; Settings.switch_debounce = SWITCH_DEBOUNCE_TIME; - for (uint8_t j = 0; j < 5; j++) { + for (uint32_t j = 0; j < 5; j++) { Settings.rgbwwTable[j] = 255; } - memset(&Settings.drivers, 0xFF, 32); // Enable all possible monitors, displays, drivers and sensors + Settings.novasds_period = WORKING_PERIOD; + + SettingsDefaultWebColor(); + + memset(&Settings.monitors, 0xFF, 20); // Enable all possible monitors, displays and sensors } /********************************************************************************************/ @@ -885,6 +993,14 @@ void SettingsDefaultSet_5_13_1c(void) SettingsResetDst(); } +void SettingsDefaultWebColor(void) +{ + char scolor[10]; + for (uint32_t i = 0; i < COL_LAST; i++) { + WebHexCode(i, GetTextIndexed(scolor, sizeof(scolor), i, kWebColors)); + } +} + /********************************************************************************************/ void SettingsDelta(void) @@ -892,7 +1008,7 @@ void SettingsDelta(void) if (Settings.version != VERSION) { // Fix version dependent changes if (Settings.version < 0x05050000) { - for (uint8_t i = 0; i < 17; i++) { Settings.rf_code[i][0] = 0; } + for (uint32_t i = 0; i < 17; i++) { Settings.rf_code[i][0] = 0; } memcpy_P(Settings.rf_code[0], kDefaultRfCode, 9); } if (Settings.version < 0x05080000) { @@ -902,7 +1018,7 @@ void SettingsDelta(void) Settings.light_color[1] = 0; Settings.light_color[2] = 0; Settings.light_dimmer = 10; - Settings.light_correction = 0; + Settings.light_correction = 1; Settings.light_fade = 0; Settings.light_speed = 1; Settings.light_scheme = 0; @@ -914,12 +1030,12 @@ void SettingsDelta(void) Settings.altitude = 0; } if (Settings.version < 0x0508000B) { - for (uint8_t i = 0; i < sizeof(Settings.my_gp); i++) { // Move GPIO_LEDs + for (uint32_t i = 0; i < sizeof(Settings.my_gp); i++) { // Move GPIO_LEDs if ((Settings.my_gp.io[i] >= 25) && (Settings.my_gp.io[i] <= 32)) { // Was GPIO_LED1 Settings.my_gp.io[i] += 23; // Move GPIO_LED1 } } - for (uint8_t i = 0; i < MAX_PWMS; i++) { // Move pwm_value and reset additional pulse_timerrs + for (uint32_t i = 0; i < MAX_PWMS; i++) { // Move pwm_value and reset additional pulse_timerrs Settings.pwm_value[i] = Settings.pulse_timer[4 +i]; Settings.pulse_timer[4 +i] = 0; } @@ -946,11 +1062,11 @@ void SettingsDelta(void) } if (Settings.version < 0x050C0005) { Settings.light_rotation = 0; - Settings.energy_power_delta = DEFAULT_POWER_DELTA; + Settings.energy_power_delta = 0; char fingerprint[60]; memcpy(fingerprint, Settings.mqtt_fingerprint, sizeof(fingerprint)); char *p = fingerprint; - for (uint8_t i = 0; i < 20; i++) { + for (uint32_t i = 0; i < 20; i++) { Settings.mqtt_fingerprint[0][i] = strtol(p, &p, 16); Settings.mqtt_fingerprint[1][i] = Settings.mqtt_fingerprint[0][i]; } @@ -985,7 +1101,7 @@ void SettingsDelta(void) SettingsDefaultSet_5_13_1c(); } if (Settings.version < 0x050E0002) { - for (uint8_t i = 1; i < MAX_RULE_SETS; i++) { Settings.rules[i][0] = '\0'; } + for (uint32_t i = 1; i < MAX_RULE_SETS; i++) { Settings.rules[i][0] = '\0'; } Settings.rule_enabled = Settings.flag.mqtt_serial_raw; // Was rules_enabled until 5.14.0b Settings.rule_once = Settings.flag.pressure_conversion; // Was rules_once until 5.14.0b } @@ -994,14 +1110,14 @@ void SettingsDelta(void) Settings.cfg_crc = GetSettingsCrc(); } if (Settings.version < 0x06000002) { - for (uint8_t i = 0; i < MAX_SWITCHES; i++) { + for (uint32_t i = 0; i < MAX_SWITCHES; i++) { if (i < 4) { Settings.switchmode[i] = Settings.interlock[i]; } else { Settings.switchmode[i] = SWITCH_MODE; } } - for (uint8_t i = 0; i < sizeof(Settings.my_gp); i++) { + for (uint32_t i = 0; i < sizeof(Settings.my_gp); i++) { if (Settings.my_gp.io[i] >= GPIO_SWT5) { // Move up from GPIO_SWT5 to GPIO_KEY1 Settings.my_gp.io[i] += 4; } @@ -1020,7 +1136,7 @@ void SettingsDelta(void) Settings.switch_debounce = SWITCH_DEBOUNCE_TIME; } if (Settings.version < 0x0602010A) { - for (uint8_t j = 0; j < 5; j++) { + for (uint32_t j = 0; j < 5; j++) { Settings.rgbwwTable[j] = 255; } } @@ -1028,7 +1144,7 @@ void SettingsDelta(void) Settings.timezone_minutes = 0; } if (Settings.version < 0x06030004) { - memset(&Settings.drivers, 0xFF, 32); // Enable all possible monitors, displays, drivers and sensors + memset(&Settings.monitors, 0xFF, 20); // Enable all possible monitors, displays and sensors } if (Settings.version < 0x0603000E) { Settings.flag2.calc_resolution = CALC_RESOLUTION; @@ -1044,7 +1160,7 @@ void SettingsDelta(void) } if (Settings.version < 0x0604010B) { Settings.interlock[0] = 0xFF; // Legacy support using all relays in one interlock group - for (uint8_t i = 1; i < MAX_INTERLOCKS; i++) { Settings.interlock[i] = 0; } + for (uint32_t i = 1; i < MAX_INTERLOCKS; i++) { Settings.interlock[i] = 0; } } if (Settings.version < 0x0604010D) { Settings.param[P_BOOT_LOOP_OFFSET] = BOOT_LOOP_OFFSET; @@ -1055,6 +1171,21 @@ void SettingsDelta(void) if (Settings.version < 0x06040113) { Settings.param[P_RGB_REMAP] = RGB_REMAP_RGBW; } + if (Settings.version < 0x06050003) { + Settings.novasds_period = WORKING_PERIOD; + } + if (Settings.version < 0x06050006) { + SettingsDefaultWebColor(); + } + if (Settings.version < 0x06050007) { + Settings.ledmask = APP_LEDMASK; + } + if (Settings.version < 0x0605000A) { + Settings.my_adc0 = ADC0_NONE; + } + if (Settings.version < 0x0605000D) { + Settings.param[P_IR_UNKNOW_THRESHOLD] = IR_RCV_MIN_UNKNOWN_SIZE; + } Settings.version = VERSION; SettingsSave(1); diff --git a/sonoff/sonoff.h b/sonoff/sonoff.h index 6c44f54fd..8d23d1771 100644 --- a/sonoff/sonoff.h +++ b/sonoff/sonoff.h @@ -32,7 +32,7 @@ #define CODE_IMAGE 0 -#define USE_DHT // Default DHT11 sensor needs no external library +#define USE_LIGHT // Enable light control #define USE_ENERGY_SENSOR // Use energy sensors (+14k code) #define USE_HLW8012 // Use energy sensor for Sonoff Pow and WolfBlitz #define USE_CSE7766 // Use energy sensor for Sonoff S31 and Pow R2 @@ -42,109 +42,102 @@ \*********************************************************************************************/ typedef unsigned long power_t; // Power (Relay) type -#define POWER_MASK 0xffffffffUL // Power (Relay) full mask +const uint32_t POWER_MASK = 0xffffffffUL; // Power (Relay) full mask + +/*********************************************************************************************\ + * Constants +\*********************************************************************************************/ + +// Changes to the following MAX_ defines will impact settings layout +const uint8_t MAX_SWITCHES = 8; // Max number of switches +const uint8_t MAX_RELAYS = 8; // Max number of relays +const uint8_t MAX_INTERLOCKS = 4; // Max number of interlock groups (MAX_RELAYS / 2) +const uint8_t MAX_LEDS = 4; // Max number of leds +const uint8_t MAX_KEYS = 4; // Max number of keys or buttons +const uint8_t MAX_PWMS = 5; // Max number of PWM channels +const uint8_t MAX_COUNTERS = 4; // Max number of counter sensors +const uint8_t MAX_TIMERS = 16; // Max number of Timers +const uint8_t MAX_PULSETIMERS = 8; // Max number of supported pulse timers +const uint8_t MAX_FRIENDLYNAMES = 4; // Max number of Friendly names +const uint8_t MAX_DOMOTICZ_IDX = 4; // Max number of Domoticz device, key and switch indices +const uint8_t MAX_DOMOTICZ_SNS_IDX = 12; // Max number of Domoticz sensors indices +const uint8_t MAX_KNX_GA = 10; // Max number of KNX Group Addresses to read that can be set +const uint8_t MAX_KNX_CB = 10; // Max number of KNX Group Addresses to write that can be set +const uint8_t MAX_XNRG_DRIVERS = 32; // Max number of allowed energy drivers +const uint8_t MAX_XDSP_DRIVERS = 32; // Max number of allowed display drivers +const uint8_t MAX_XDRV_DRIVERS = 96; // Max number of allowed driver drivers +const uint8_t MAX_XSNS_DRIVERS = 96; // Max number of allowed sensor drivers +const uint8_t MAX_RULE_MEMS = 5; // Max number of saved vars +const uint8_t MAX_RULE_SETS = 3; // Max number of rule sets of size 512 characters +const uint16_t MAX_RULE_SIZE = 512; // Max number of characters in rules + +const uint8_t MAX_FAN_SPEED = 4; // Max number of iFan02 fan speeds (0 .. 3) + +const char MQTT_TOKEN_PREFIX[] PROGMEM = "%prefix%"; // To be substituted by mqtt_prefix[x] +const char MQTT_TOKEN_TOPIC[] PROGMEM = "%topic%"; // To be substituted by mqtt_topic, mqtt_grptopic, mqtt_buttontopic, mqtt_switchtopic +const char WIFI_HOSTNAME[] = "%s-%04d"; // Expands to - + +const uint8_t CONFIG_FILE_SIGN = 0xA5; // Configuration file signature +const uint8_t CONFIG_FILE_XOR = 0x5A; // Configuration file xor (0 = No Xor) + +const uint32_t HLW_PREF_PULSE = 12530; // was 4975us = 201Hz = 1000W +const uint32_t HLW_UREF_PULSE = 1950; // was 1666us = 600Hz = 220V +const uint32_t HLW_IREF_PULSE = 3500; // was 1666us = 600Hz = 4.545A + +const uint8_t MQTT_RETRY_SECS = 10; // Minimum seconds to retry MQTT connection +const uint32_t GLOBAL_VALUES_VALID = 300; // Max number of seconds to keep last received values +const power_t APP_POWER = 0; // Default saved power state Off +const uint16_t WS2812_MAX_LEDS = 512; // Max number of LEDs + +const uint32_t PWM_RANGE = 1023; // 255..1023 needs to be devisible by 256 +//const uint16_t PWM_FREQ = 1000; // 100..1000 Hz led refresh +//const uint16_t PWM_FREQ = 910; // 100..1000 Hz led refresh (iTead value) +const uint16_t PWM_FREQ = 880; // 100..1000 Hz led refresh (BN-SZ01 value) +const uint16_t PWM_MAX = 4000; // [PWM_MAX] Maximum frequency - Default: 4000 +const uint16_t PWM_MIN = 100; // [PWM_MIN] Minimum frequency - Default: 100 + // For Dimmers use double of your mains AC frequecy (100 for 50Hz and 120 for 60Hz) + // For Controlling Servos use 50 and also set PWM_FREQ as 50 (DO NOT USE THESE VALUES FOR DIMMERS) +//#define PWM_LIGHTSCHEME0_IGNORE_SLEEP // Do not change sleep value for LightAnimate() scheme 0 + +const uint16_t MAX_POWER_HOLD = 10; // Time in SECONDS to allow max agreed power +const uint16_t MAX_POWER_WINDOW = 30; // Time in SECONDS to disable allow max agreed power +const uint16_t SAFE_POWER_HOLD = 10; // Time in SECONDS to allow max unit safe power +const uint16_t SAFE_POWER_WINDOW = 30; // Time in MINUTES to disable allow max unit safe power +const uint8_t MAX_POWER_RETRY = 5; // Retry count allowing agreed power limit overflow + +const uint8_t STATES = 20; // Number of states per second using 50 mSec interval +const uint8_t IMMINENT_RESET_FACTOR = 10; // Factor to extent button hold time for imminent Reset to default 40 seconds using KEY_HOLD_TIME of 40 +const uint32_t BOOT_LOOP_TIME = 10; // Number of seconds to stop detecting boot loops +const uint16_t SYSLOG_TIMER = 600; // Seconds to restore syslog_level +const uint16_t SERIALLOG_TIMER = 600; // Seconds to disable SerialLog +const uint8_t OTA_ATTEMPTS = 5; // Number of times to try fetching the new firmware + +const uint16_t INPUT_BUFFER_SIZE = 520; // Max number of characters in (serial and http) command buffer +const uint16_t CMDSZ = 24; // Max number of characters in command +const uint16_t TOPSZ = 100; // Max number of characters in topic string +const uint16_t LOGSZ = 520; // Max number of characters in log +const uint16_t MIN_MESSZ = 893; // Min number of characters in MQTT message + +const uint8_t SENSOR_MAX_MISS = 5; // Max number of missed sensor reads before deciding it's offline + +const uint8_t MAX_BACKLOG = 30; // Max number of commands in backlog +const uint32_t MIN_BACKLOG_DELAY = 2; // Minimal backlog delay in 0.1 seconds + +const uint32_t SOFT_BAUDRATE = 9600; // Default software serial baudrate +const uint32_t APP_BAUDRATE = 115200; // Default serial baudrate +const uint32_t SERIAL_POLLING = 100; // Serial receive polling in ms +const uint8_t MAX_STATUS = 11; // Max number of status lines + +const uint32_t DRIVER_BOOT_DELAY = 1; // Number of milliseconds to retard driver cycles during boot-up time to reduce overall CPU load whilst Wifi is connecting +const uint32_t LOOP_SLEEP_DELAY = 50; // Lowest number of milliseconds to go through the main loop using delay when needed /*********************************************************************************************\ * Defines \*********************************************************************************************/ -// Changes to the following MAX_ defines will impact settings layout -#define MAX_SWITCHES 8 // Max number of switches -#define MAX_RELAYS 8 // Max number of relays -#define MAX_INTERLOCKS 4 // Max number of interlock groups (MAX_RELAYS / 2) -#define MAX_LEDS 4 // Max number of leds -#define MAX_KEYS 4 // Max number of keys or buttons -#define MAX_PWMS 5 // Max number of PWM channels -#define MAX_COUNTERS 4 // Max number of counter sensors -#define MAX_TIMERS 16 // Max number of Timers -#define MAX_PULSETIMERS 8 // Max number of supported pulse timers -#define MAX_FRIENDLYNAMES 4 // Max number of Friendly names -#define MAX_DOMOTICZ_IDX 4 // Max number of Domoticz device, key and switch indices -#define MAX_DOMOTICZ_SNS_IDX 12 // Max number of Domoticz sensors indices -#define MAX_KNX_GA 10 // Max number of KNX Group Addresses to read that can be set -#define MAX_KNX_CB 10 // Max number of KNX Group Addresses to write that can be set -#define MAX_XNRG_DRIVERS 32 // Max number of allowed energy drivers -#define MAX_XDSP_DRIVERS 32 // Max number of allowed display drivers -#define MAX_XDRV_DRIVERS 96 // Max number of allowed driver drivers -#define MAX_XSNS_DRIVERS 96 // Max number of allowed sensor drivers -#define MAX_RULE_MEMS 5 // Max number of saved vars -#define MAX_RULE_SETS 3 // Max number of rule sets of size 512 characters -#define MAX_RULE_SIZE 512 // Max number of characters in rules - -// Changes to the following defines have no impact on settings layout #define MAX_RULE_TIMERS 8 // Max number of rule timers (4 bytes / timer) #define MAX_RULE_VARS 5 // Max number of rule variables (10 bytes / variable) -#define MAX_FAN_SPEED 4 // Max number of iFan02 fan speeds (0 .. 3) - -#define MQTT_TOKEN_PREFIX "%prefix%" // To be substituted by mqtt_prefix[x] -#define MQTT_TOKEN_TOPIC "%topic%" // To be substituted by mqtt_topic, mqtt_grptopic, mqtt_buttontopic, mqtt_switchtopic -#define MQTT_TOKEN_HOSTNAME "%hostname%" // To be substituted by mqtt_topic, mqtt_grptopic, mqtt_buttontopic, mqtt_switchtopic -#define MQTT_TOKEN_ID "%id%" // To be substituted by mqtt_topic, mqtt_grptopic, mqtt_buttontopic, mqtt_switchtopic - -#define WIFI_HOSTNAME "%s-%04d" // Expands to - - -#define CONFIG_FILE_SIGN 0xA5 // Configuration file signature -#define CONFIG_FILE_XOR 0x5A // Configuration file xor (0 = No Xor) - -#define HLW_PREF_PULSE 12530 // was 4975us = 201Hz = 1000W -#define HLW_UREF_PULSE 1950 // was 1666us = 600Hz = 220V -#define HLW_IREF_PULSE 3500 // was 1666us = 600Hz = 4.545A - -#define MQTT_RETRY_SECS 10 // Minimum seconds to retry MQTT connection -#define GLOBAL_VALUES_VALID 300 // Max number of seconds to keep last received values -#define APP_POWER 0 // Default saved power state Off -#define WS2812_MAX_LEDS 512 // Max number of LEDs - -#define PWM_RANGE 1023 // 255..1023 needs to be devisible by 256 -//#define PWM_FREQ 1000 // 100..1000 Hz led refresh -//#define PWM_FREQ 910 // 100..1000 Hz led refresh (iTead value) -#define PWM_FREQ 880 // 100..1000 Hz led refresh (BN-SZ01 value) -#define PWM_MAX 4000 // [PWM_MAX] Maximum frequency - Default: 4000 -#define PWM_MIN 100 // [PWM_MIN] Minimum frequency - Default: 100 - // For Dimmers use double of your mains AC frequecy (100 for 50Hz and 120 for 60Hz) - // For Controlling Servos use 50 and also set PWM_FREQ as 50 (DO NOT USE THESE VALUES FOR DIMMERS) -//#define PWM_LIGHTSCHEME0_IGNORE_SLEEP // Do not change sleep value for LightAnimate() scheme 0 - -#define DEFAULT_POWER_DELTA 80 // Power change percentage -#define MAX_POWER_HOLD 10 // Time in SECONDS to allow max agreed power -#define MAX_POWER_WINDOW 30 // Time in SECONDS to disable allow max agreed power -#define SAFE_POWER_HOLD 10 // Time in SECONDS to allow max unit safe power -#define SAFE_POWER_WINDOW 30 // Time in MINUTES to disable allow max unit safe power -#define MAX_POWER_RETRY 5 // Retry count allowing agreed power limit overflow - -#define STATES 20 // Number of states per second using 50 mSec interval -#define IMMINENT_RESET_FACTOR 10 // Factor to extent button hold time for imminent Reset to default 40 seconds using KEY_HOLD_TIME of 40 -#define BOOT_LOOP_TIME 10 // Number of seconds to stop detecting boot loops -#define SYSLOG_TIMER 600 // Seconds to restore syslog_level -#define SERIALLOG_TIMER 600 // Seconds to disable SerialLog -#define OTA_ATTEMPTS 5 // Number of times to try fetching the new firmware - -#define INPUT_BUFFER_SIZE 520 // Max number of characters in (serial and http) command buffer -#define CMDSZ 24 // Max number of characters in command -#define TOPSZ 100 // Max number of characters in topic string -#define LOGSZ 520 // Max number of characters in log -#define MIN_MESSZ 893 // Min number of characters in MQTT message - -#define SENSOR_MAX_MISS 5 // Max number of missed sensor reads before deciding it's offline - -#ifdef USE_MQTT_TLS - #define WEB_LOG_SIZE 2000 // Max number of characters in weblog -#else - #define WEB_LOG_SIZE 4000 // Max number of characters in weblog -#endif - -#define MAX_BACKLOG 30 // Max number of commands in backlog -#define MIN_BACKLOG_DELAY 2 // Minimal backlog delay in 0.1 seconds - -#define SOFT_BAUDRATE 9600 // Default software serial baudrate -#define APP_BAUDRATE 115200 // Default serial baudrate -#define SERIAL_POLLING 100 // Serial receive polling in ms -#define MAX_STATUS 11 // Max number of status lines - -#define DRIVER_BOOT_DELAY 1 // Number of milliseconds to retard driver cycles during boot-up time to reduce overall CPU load whilst Wifi is connecting -#define LOOP_SLEEP_DELAY 50 // Lowest number of milliseconds to go through the main loop using delay when needed - #define NO_EXTRA_4K_HEAP // Allocate 4k heap for WPS in ESP8166/Arduino core v2.4.2 (was always allocated in previous versions) /* @@ -237,14 +230,14 @@ enum ButtonStates { PRESSED, NOT_PRESSED }; enum Shortcuts { SC_CLEAR, SC_DEFAULT, SC_USER }; -enum SettingsParmaIndex {P_HOLD_TIME, P_MAX_POWER_RETRY, P_TUYA_DIMMER_ID, P_MDNS_DELAYED_START, P_BOOT_LOOP_OFFSET, P_RGB_REMAP, P_MAX_PARAM8}; // Max is PARAM8_SIZE (18) - SetOption32 until SetOption49 +enum SettingsParmaIndex {P_HOLD_TIME, P_MAX_POWER_RETRY, P_TUYA_DIMMER_ID, P_MDNS_DELAYED_START, P_BOOT_LOOP_OFFSET, P_RGB_REMAP, P_IR_UNKNOW_THRESHOLD, P_CSE7766_INVALID_POWER, P_HOLD_IGNORE, P_MAX_PARAM8}; // Max is PARAM8_SIZE (18) - SetOption32 until SetOption49 enum DomoticzSensors {DZ_TEMP, DZ_TEMP_HUM, DZ_TEMP_HUM_BARO, DZ_POWER_ENERGY, DZ_ILLUMINANCE, DZ_COUNT, DZ_VOLTAGE, DZ_CURRENT, DZ_AIRQUALITY, DZ_MAX_SENSORS}; enum Ws2812ClockIndex { WS_SECOND, WS_MINUTE, WS_HOUR, WS_MARKER }; enum Ws2812Color { WS_RED, WS_GREEN, WS_BLUE }; -enum LightSubtypes { LST_NONE, LST_SINGLE, LST_COLDWARM, LST_RGB, LST_RGBW, LST_RGBWC }; // Do not insert new fields +enum LightSubtypes { LST_NONE, LST_SINGLE, LST_COLDWARM, LST_RGB, LST_RGBW, LST_RGBWC, LST_MAX=5 }; // Do not insert new fields enum LightTypes { LT_BASIC, LT_PWM1, LT_PWM2, LT_PWM3, LT_PWM4, LT_PWM5, LT_PWM6, LT_PWM7, LT_NU8, LT_SERIAL1, LT_SERIAL2, LT_WS2812, LT_RGBW, LT_RGBWC, LT_NU14, LT_NU15 }; // Do not insert new fields @@ -252,15 +245,17 @@ enum LightSchemes {LS_POWER, LS_WAKEUP, LS_CYCLEUP, LS_CYCLEDN, LS_RANDOM, LS_MA enum XsnsFunctions {FUNC_SETTINGS_OVERRIDE, FUNC_MODULE_INIT, FUNC_PRE_INIT, FUNC_INIT, FUNC_LOOP, FUNC_EVERY_50_MSECOND, FUNC_EVERY_100_MSECOND, FUNC_EVERY_200_MSECOND, FUNC_EVERY_250_MSECOND, FUNC_EVERY_SECOND, - FUNC_PREP_BEFORE_TELEPERIOD, FUNC_JSON_APPEND, FUNC_WEB_APPEND, FUNC_SAVE_BEFORE_RESTART, FUNC_COMMAND, FUNC_COMMAND_SENSOR, FUNC_COMMAND_DRIVER, + FUNC_SAVE_AT_MIDNIGHT, FUNC_SAVE_BEFORE_RESTART, + FUNC_PREP_BEFORE_TELEPERIOD, FUNC_JSON_APPEND, FUNC_WEB_SENSOR, FUNC_COMMAND, FUNC_COMMAND_SENSOR, FUNC_COMMAND_DRIVER, FUNC_MQTT_SUBSCRIBE, FUNC_MQTT_INIT, FUNC_MQTT_DATA, FUNC_SET_POWER, FUNC_SET_DEVICE_POWER, FUNC_SHOW_SENSOR, + FUNC_ENERGY_EVERY_SECOND, FUNC_RULES_PROCESS, FUNC_SERIAL, FUNC_FREE_MEM, FUNC_BUTTON_PRESSED, FUNC_WEB_ADD_BUTTON, FUNC_WEB_ADD_MAIN_BUTTON, FUNC_WEB_ADD_HANDLER, FUNC_SET_CHANNELS}; enum CommandSource { SRC_IGNORE, SRC_MQTT, SRC_RESTART, SRC_BUTTON, SRC_SWITCH, SRC_BACKLOG, SRC_SERIAL, SRC_WEBGUI, SRC_WEBCOMMAND, SRC_WEBCONSOLE, SRC_PULSETIMER, - SRC_TIMER, SRC_RULE, SRC_MAXPOWER, SRC_MAXENERGY, SRC_LIGHT, SRC_KNX, SRC_DISPLAY, SRC_WEMO, SRC_HUE, SRC_RETRY, SRC_MAX }; -const char kCommandSource[] PROGMEM = "I|MQTT|Restart|Button|Switch|Backlog|Serial|WebGui|WebCommand|WebConsole|PulseTimer|Timer|Rule|MaxPower|MaxEnergy|Light|Knx|Display|Wemo|Hue|Retry"; + SRC_TIMER, SRC_RULE, SRC_MAXPOWER, SRC_MAXENERGY, SRC_OVERTEMP, SRC_LIGHT, SRC_KNX, SRC_DISPLAY, SRC_WEMO, SRC_HUE, SRC_RETRY, SRC_MAX }; +const char kCommandSource[] PROGMEM = "I|MQTT|Restart|Button|Switch|Backlog|Serial|WebGui|WebCommand|WebConsole|PulseTimer|Timer|Rule|MaxPower|MaxEnergy|Overtemp|Light|Knx|Display|Wemo|Hue|Retry"; const uint8_t kDefaultRfCode[9] PROGMEM = { 0x21, 0x16, 0x01, 0x0E, 0x03, 0x48, 0x2E, 0x1A, 0x00 }; diff --git a/sonoff/sonoff.ino b/sonoff/sonoff.ino index 51b89031c..c968af406 100755 --- a/sonoff/sonoff.ino +++ b/sonoff/sonoff.ino @@ -33,10 +33,14 @@ #ifdef USE_CONFIG_OVERRIDE #include "user_config_override.h" // Configuration overrides for my_user_config.h #endif +#ifdef USE_MQTT_TLS + #include // we need to include before "sonoff_post.h" to take precedence over the BearSSL version in Arduino +#endif // USE_MQTT_TLS #include "sonoff_post.h" // Configuration overrides for all previous includes #include "i18n.h" // Language support configured by my_user_config.h #include "sonoff_template.h" // Hardware configuration + #ifdef ARDUINO_ESP8266_RELEASE_2_4_0 #include "lwip/init.h" #if LWIP_VERSION_MAJOR != 1 @@ -72,21 +76,21 @@ enum TasmotaCommands { CMND_BACKLOG, CMND_DELAY, CMND_POWER, CMND_FANSPEED, CMND_STATUS, CMND_STATE, CMND_POWERONSTATE, CMND_PULSETIME, CMND_BLINKTIME, CMND_BLINKCOUNT, CMND_SENSOR, CMND_SAVEDATA, CMND_SETOPTION, CMND_TEMPERATURE_RESOLUTION, CMND_HUMIDITY_RESOLUTION, CMND_PRESSURE_RESOLUTION, CMND_POWER_RESOLUTION, CMND_VOLTAGE_RESOLUTION, CMND_FREQUENCY_RESOLUTION, CMND_CURRENT_RESOLUTION, CMND_ENERGY_RESOLUTION, CMND_WEIGHT_RESOLUTION, - CMND_MODULE, CMND_MODULES, CMND_GPIO, CMND_GPIOS, CMND_PWM, CMND_PWMFREQUENCY, CMND_PWMRANGE, CMND_COUNTER, CMND_COUNTERTYPE, + CMND_MODULE, CMND_MODULES, CMND_ADC, CMND_ADCS, CMND_GPIO, CMND_GPIOS, CMND_PWM, CMND_PWMFREQUENCY, CMND_PWMRANGE, CMND_COUNTER, CMND_COUNTERTYPE, CMND_COUNTERDEBOUNCE, CMND_BUTTONDEBOUNCE, CMND_SWITCHDEBOUNCE, CMND_SLEEP, CMND_UPGRADE, CMND_UPLOAD, CMND_OTAURL, CMND_SERIALLOG, CMND_SYSLOG, CMND_LOGHOST, CMND_LOGPORT, CMND_IPADDRESS, CMND_NTPSERVER, CMND_AP, CMND_SSID, CMND_PASSWORD, CMND_HOSTNAME, CMND_WIFICONFIG, CMND_FRIENDLYNAME, CMND_SWITCHMODE, CMND_INTERLOCK, CMND_TEMPLATE, - CMND_TELEPERIOD, CMND_RESTART, CMND_RESET, CMND_TIMEZONE, CMND_TIMESTD, CMND_TIMEDST, CMND_ALTITUDE, CMND_LEDPOWER, CMND_LEDSTATE, + CMND_TELEPERIOD, CMND_RESTART, CMND_RESET, CMND_TIMEZONE, CMND_TIMESTD, CMND_TIMEDST, CMND_ALTITUDE, CMND_LEDPOWER, CMND_LEDSTATE, CMND_LEDMASK, CMND_I2CSCAN, CMND_SERIALSEND, CMND_BAUDRATE, CMND_SERIALDELIMITER, CMND_DRIVER }; const char kTasmotaCommands[] PROGMEM = D_CMND_BACKLOG "|" D_CMND_DELAY "|" D_CMND_POWER "|" D_CMND_FANSPEED "|" D_CMND_STATUS "|" D_CMND_STATE "|" D_CMND_POWERONSTATE "|" D_CMND_PULSETIME "|" D_CMND_BLINKTIME "|" D_CMND_BLINKCOUNT "|" D_CMND_SENSOR "|" D_CMND_SAVEDATA "|" D_CMND_SETOPTION "|" D_CMND_TEMPERATURE_RESOLUTION "|" D_CMND_HUMIDITY_RESOLUTION "|" D_CMND_PRESSURE_RESOLUTION "|" D_CMND_POWER_RESOLUTION "|" D_CMND_VOLTAGE_RESOLUTION "|" D_CMND_FREQUENCY_RESOLUTION "|" D_CMND_CURRENT_RESOLUTION "|" D_CMND_ENERGY_RESOLUTION "|" D_CMND_WEIGHT_RESOLUTION "|" - D_CMND_MODULE "|" D_CMND_MODULES "|" D_CMND_GPIO "|" D_CMND_GPIOS "|" D_CMND_PWM "|" D_CMND_PWMFREQUENCY "|" D_CMND_PWMRANGE "|" D_CMND_COUNTER "|" D_CMND_COUNTERTYPE "|" + D_CMND_MODULE "|" D_CMND_MODULES "|" D_CMND_ADC "|" D_CMND_ADCS "|" D_CMND_GPIO "|" D_CMND_GPIOS "|" D_CMND_PWM "|" D_CMND_PWMFREQUENCY "|" D_CMND_PWMRANGE "|" D_CMND_COUNTER "|" D_CMND_COUNTERTYPE "|" D_CMND_COUNTERDEBOUNCE "|" D_CMND_BUTTONDEBOUNCE "|" D_CMND_SWITCHDEBOUNCE "|" D_CMND_SLEEP "|" D_CMND_UPGRADE "|" D_CMND_UPLOAD "|" D_CMND_OTAURL "|" D_CMND_SERIALLOG "|" D_CMND_SYSLOG "|" D_CMND_LOGHOST "|" D_CMND_LOGPORT "|" D_CMND_IPADDRESS "|" D_CMND_NTPSERVER "|" D_CMND_AP "|" D_CMND_SSID "|" D_CMND_PASSWORD "|" D_CMND_HOSTNAME "|" D_CMND_WIFICONFIG "|" D_CMND_FRIENDLYNAME "|" D_CMND_SWITCHMODE "|" D_CMND_INTERLOCK "|" D_CMND_TEMPLATE "|" - D_CMND_TELEPERIOD "|" D_CMND_RESTART "|" D_CMND_RESET "|" D_CMND_TIMEZONE "|" D_CMND_TIMESTD "|" D_CMND_TIMEDST "|" D_CMND_ALTITUDE "|" D_CMND_LEDPOWER "|" D_CMND_LEDSTATE "|" + D_CMND_TELEPERIOD "|" D_CMND_RESTART "|" D_CMND_RESET "|" D_CMND_TIMEZONE "|" D_CMND_TIMESTD "|" D_CMND_TIMEDST "|" D_CMND_ALTITUDE "|" D_CMND_LEDPOWER "|" D_CMND_LEDSTATE "|" D_CMND_LEDMASK "|" D_CMND_I2CSCAN "|" D_CMND_SERIALSEND "|" D_CMND_BAUDRATE "|" D_CMND_SERIALDELIMITER "|" D_CMND_DRIVER; const char kSleepMode[] PROGMEM = "Dynamic|Normal"; @@ -125,8 +129,9 @@ int blinks = 201; // Number of LED blinks uint32_t uptime = 0; // Counting every second until 4294967295 = 130 year uint32_t loop_load_avg = 0; // Indicative loop load average uint32_t global_update = 0; // Timestamp of last global temperature and humidity update -float global_temperature = 0; // Provide a global temperature to be used by some sensors +float global_temperature = 9999; // Provide a global temperature to be used by some sensors float global_humidity = 0; // Provide a global humidity to be used by some sensors +float global_pressure = 0; // Provide a global pressure to be used by some sensors char *ota_url; // OTA url string pointer uint16_t mqtt_cmnd_publish = 0; // ignore flag for publish command uint16_t blink_counter = 0; // Number of blink cycles @@ -141,7 +146,11 @@ uint8_t backlog_pointer = 0; // Command backlog pointer uint8_t sleep; // Current copy of Settings.sleep uint8_t blinkspeed = 1; // LED blink rate uint8_t pin[GPIO_MAX]; // Possible pin configurations +uint8_t active_device = 1; // Active device in ExecuteCommandPower +uint8_t leds_present = 0; // Max number of LED supported uint8_t led_inverted = 0; // LED inverted flag (1 = (0 = On, 1 = Off)) +uint8_t led_power = 0; // LED power state +uint8_t ledlnk_inverted = 0; // Link LED inverted flag (1 = (0 = On, 1 = Off)) uint8_t pwm_inverted = 0; // PWM inverted flag (1 = inverted) uint8_t counter_no_pullup = 0; // Counter input pullup flag (1 = No pullup) uint8_t energy_flg = 0; // Energy monitor configured @@ -153,6 +162,7 @@ uint8_t devices_present = 0; // Max number of devices supported uint8_t seriallog_level; // Current copy of Settings.seriallog_level uint8_t syslog_level; // Current copy of Settings.syslog_level uint8_t my_module_type; // Current copy of Settings.module or user template type +uint8_t my_adc0; // Active copy of Module ADC0 //uint8_t mdns_delayed_start = 0; // mDNS delayed start bool serial_local = false; // Handle serial locally; bool fallback_topic_flag = false; // Use Topic or FallbackTopic @@ -167,8 +177,9 @@ bool i2c_flg = false; // I2C configured bool spi_flg = false; // SPI configured bool soft_spi_flg = false; // Software SPI configured bool ntp_force_sync = false; // Force NTP sync +bool ntp_synced_message = false; // NTP synced message flag myio my_module; // Active copy of Module GPIOs (17 x 8 bits) -gpio_flag my_module_flag; // Active copy of Module GPIO flags +gpio_flag my_module_flag; // Active copy of Template GPIO flags StateBitfield global_state; // Global states (currently Wifi and Mqtt) (8 bits) char my_version[33]; // Composed version string char my_image[33]; // Code image and/or commit @@ -188,15 +199,15 @@ char* Format(char* output, const char* input, int size) char *token; uint8_t digits = 0; - if (strstr(input, "%")) { + if (strstr(input, "%") != nullptr) { strlcpy(output, input, size); token = strtok(output, "%"); if (strstr(input, "%") == input) { output[0] = '\0'; } else { - token = strtok(NULL, ""); + token = strtok(nullptr, ""); } - if (token != NULL) { + if (token != nullptr) { digits = atoi(token); if (digits) { char tmp[size]; @@ -221,10 +232,10 @@ char* Format(char* output, const char* input, int size) char* GetOtaUrl(char *otaurl, size_t otaurl_size) { - if (strstr(Settings.ota_url, "%04d") != NULL) { // OTA url contains placeholder for chip ID + if (strstr(Settings.ota_url, "%04d") != nullptr) { // OTA url contains placeholder for chip ID snprintf(otaurl, otaurl_size, Settings.ota_url, ESP.getChipId() & 0x1fff); } - else if (strstr(Settings.ota_url, "%d") != NULL) { // OTA url contains placeholder for chip ID + else if (strstr(Settings.ota_url, "%d") != nullptr) { // OTA url contains placeholder for chip ID snprintf_P(otaurl, otaurl_size, Settings.ota_url, ESP.getChipId()); } else { @@ -254,20 +265,21 @@ char* GetTopic_P(char *stopic, uint8_t prefix, char *topic, const char* subtopic fulltopic += F("_fb"); // cmnd/_fb } else { fulltopic = Settings.mqtt_fulltopic; - if ((0 == prefix) && (-1 == fulltopic.indexOf(F(MQTT_TOKEN_PREFIX)))) { - fulltopic += F("/" MQTT_TOKEN_PREFIX); // Need prefix for commands to handle mqtt topic loops + if ((0 == prefix) && (-1 == fulltopic.indexOf(FPSTR(MQTT_TOKEN_PREFIX)))) { + fulltopic += F("/"); + fulltopic += FPSTR(MQTT_TOKEN_PREFIX); // Need prefix for commands to handle mqtt topic loops } - for (uint8_t i = 0; i < 3; i++) { + for (uint32_t i = 0; i < 3; i++) { if ('\0' == Settings.mqtt_prefix[i][0]) { snprintf_P(Settings.mqtt_prefix[i], sizeof(Settings.mqtt_prefix[i]), kPrefixes[i]); } } - fulltopic.replace(F(MQTT_TOKEN_PREFIX), Settings.mqtt_prefix[prefix]); - fulltopic.replace(F(MQTT_TOKEN_TOPIC), topic); - fulltopic.replace(F(MQTT_TOKEN_HOSTNAME), my_hostname); + fulltopic.replace(FPSTR(MQTT_TOKEN_PREFIX), Settings.mqtt_prefix[prefix]); + fulltopic.replace(FPSTR(MQTT_TOKEN_TOPIC), topic); + fulltopic.replace(F("%hostname%"), my_hostname); String token_id = WiFi.macAddress(); token_id.replace(":", ""); - fulltopic.replace(F(MQTT_TOKEN_ID), token_id); + fulltopic.replace(F("%id%"), token_id); } fulltopic.replace(F("#"), ""); fulltopic.replace(F("//"), "/"); @@ -278,7 +290,7 @@ char* GetTopic_P(char *stopic, uint8_t prefix, char *topic, const char* subtopic char* GetFallbackTopic_P(char *stopic, uint8_t prefix, const char* subtopic) { - return GetTopic_P(stopic, prefix +4, NULL, subtopic); + return GetTopic_P(stopic, prefix +4, nullptr, subtopic); } char* GetStateText(uint8_t state) @@ -301,7 +313,7 @@ void SetLatchingRelay(power_t lpower, uint8_t state) latching_relay_pulse = 2; // max 200mS (initiated by stateloop()) } - for (uint8_t i = 0; i < devices_present; i++) { + for (uint32_t i = 0; i < devices_present; i++) { uint8_t port = (i << 1) + ((latching_power >> i) &1); if (pin[GPIO_REL1 +port] < 99) { digitalWrite(pin[GPIO_REL1 +port], bitRead(rel_inverted, port) ? !state : state); @@ -321,10 +333,10 @@ void SetDevicePower(power_t rpower, int source) } if (Settings.flag.interlock) { // Allow only one or no relay set - for (uint8_t i = 0; i < MAX_INTERLOCKS; i++) { + for (uint32_t i = 0; i < MAX_INTERLOCKS; i++) { power_t mask = 1; uint8_t count = 0; - for (uint8_t j = 0; j < devices_present; j++) { + for (uint32_t j = 0; j < devices_present; j++) { if ((Settings.interlock[i] & mask) && (rpower & mask)) { count++; } mask <<= 1; } @@ -356,7 +368,7 @@ void SetDevicePower(power_t rpower, int source) SetLatchingRelay(rpower, 1); } else { - for (uint8_t i = 0; i < devices_present; i++) { + for (uint32_t i = 0; i < devices_present; i++) { state = rpower &1; if ((i < MAX_RELAYS) && (pin[GPIO_REL1 +i] < 99)) { digitalWrite(pin[GPIO_REL1 +i], bitRead(rel_inverted, i) ? !state : state); @@ -366,19 +378,56 @@ void SetDevicePower(power_t rpower, int source) } } +void SetLedPowerIdx(uint8_t led, uint8_t state) +{ + if ((99 == pin[GPIO_LEDLNK]) && (0 == led)) { // Legacy - LED1 is link led only if LED2 is present + if (pin[GPIO_LED2] < 99) { led = 1; } + } + if (pin[GPIO_LED1 + led] < 99) { + uint8_t mask = 1 << led; + if (state) { + state = 1; + led_power |= mask; + } else { + led_power &= (0xFF ^ mask); + } + digitalWrite(pin[GPIO_LED1 + led], bitRead(led_inverted, led) ? !state : state); + } +} + void SetLedPower(uint8_t state) { - if (state) { state = 1; } + if (99 == pin[GPIO_LEDLNK]) { // Legacy - Only use LED1 and/or LED2 + SetLedPowerIdx(0, state); + } else { + power_t mask = 1; + for (uint32_t i = 0; i < leds_present; i++) { // Map leds to power + bool tstate = (power & mask); + SetLedPowerIdx(i, tstate); + mask <<= 1; + } + } +} - uint8_t led_pin = 0; - if (pin[GPIO_LED2] < 99) { led_pin = 1; } - digitalWrite(pin[GPIO_LED1 + led_pin], (bitRead(led_inverted, led_pin)) ? !state : state); +void SetLedPowerAll(uint8_t state) +{ + for (uint32_t i = 0; i < leds_present; i++) { + SetLedPowerIdx(i, state); + } } void SetLedLink(uint8_t state) { - if (state) { state = 1; } - digitalWrite(pin[GPIO_LED1], (bitRead(led_inverted, 0)) ? !state : state); + uint8_t led_pin = pin[GPIO_LEDLNK]; + uint8_t led_inv = ledlnk_inverted; + if (99 == led_pin) { // Legacy - LED1 is status + led_pin = pin[GPIO_LED1]; + led_inv = bitRead(led_inverted, 0); + } + if (led_pin < 99) { + if (state) { state = 1; } + digitalWrite(led_pin, (led_inv) ? !state : state); + } } uint8_t GetFanspeed(void) @@ -400,7 +449,7 @@ uint8_t GetFanspeed(void) void SetFanspeed(uint8_t fanspeed) { - for (uint8_t i = 0; i < MAX_FAN_SPEED -1; i++) { + for (uint32_t i = 0; i < MAX_FAN_SPEED -1; i++) { uint8_t state = kIFan02Speed[fanspeed][i]; // uint8_t state = pgm_read_byte(kIFan02Speed +(speed *3) +i); ExecuteCommandPower(i +2, state, SRC_IGNORE); // Use relay 2, 3 and 4 @@ -452,13 +501,13 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) char command [CMDSZ]; char stemp1[TOPSZ]; char *p; - char *type = NULL; + char *type = nullptr; uint8_t lines = 1; bool jsflg = false; bool grpflg = false; // bool user_append_index = false; - uint16_t i = 0; - uint16_t index; + uint32_t i = 0; + uint32_t index; uint32_t address; #ifdef USE_DEBUG_DRIVER @@ -480,7 +529,7 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) if (XdrvMqttData(topicBuf, sizeof(topicBuf), dataBuf, sizeof(dataBuf))) { return; } - grpflg = (strstr(topicBuf, Settings.mqtt_grptopic) != NULL); + grpflg = (strstr(topicBuf, Settings.mqtt_grptopic) != nullptr); GetFallbackTopic_P(stemp1, CMND, ""); // Full Fallback topic = cmnd/DVES_xxxxxxxx_fb/ fallback_topic_flag = (!strncmp(topicBuf, stemp1, strlen(stemp1))); @@ -488,7 +537,7 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) type = strrchr(topicBuf, '/'); // Last part of received topic is always the command (type) index = 1; - if (type != NULL) { + if (type != nullptr) { type++; for (i = 0; i < strlen(type); i++) { type[i] = toupper(type[i]); @@ -505,14 +554,14 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_RESULT D_GROUP " %d, " D_INDEX " %d, " D_COMMAND " %s, " D_DATA " %s"), grpflg, index, type, dataBuf); - if (type != NULL) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_COMMAND "\":\"" D_JSON_ERROR "\"}")); + if (type != nullptr) { + Response_P(PSTR("{\"" D_JSON_COMMAND "\":\"" D_JSON_ERROR "\"}")); if (Settings.ledstate &0x02) { blinks++; } if (!strcmp(dataBuf,"?")) { data_len = 0; } int16_t payload = -99; // No payload uint16_t payload16 = 0; - long payload32 = strtol(dataBuf, &p, 10); + long payload32 = strtol(dataBuf, &p, 0); // decimal, octal (0) or hex (0x) if (p != dataBuf) { payload = (int16_t) payload32; // -32766 - 32767 payload16 = (uint16_t) payload32; // 0 - 65535 @@ -524,7 +573,7 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) int temp_payload = GetStateNumber(dataBuf); if (temp_payload > -1) { payload = temp_payload; } -// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RSLT: Payload %d, Payload16 %d"), payload, payload16); +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_RESULT "Payload %d, Payload16 %d, payload32 %u"), payload, payload16, payload32); int command_code = GetCommandCode(command, sizeof(command), type, kTasmotaCommands); if (-1 == command_code) { @@ -538,7 +587,7 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) XdrvMailbox.data = dataBuf; if (!XdrvCall(FUNC_COMMAND)) { if (!XsnsCall(FUNC_COMMAND)) { - type = NULL; // Unknown command + type = nullptr; // Unknown command } } } @@ -547,7 +596,7 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) uint8_t bl_pointer = (!backlog_pointer) ? MAX_BACKLOG -1 : backlog_pointer; bl_pointer--; char *blcommand = strtok(dataBuf, ";"); - while ((blcommand != NULL) && (backlog_index != bl_pointer)) { + while ((blcommand != nullptr) && (backlog_index != bl_pointer)) { while(true) { blcommand = Trim(blcommand); if (!strncasecmp_P(blcommand, PSTR(D_CMND_BACKLOG), strlen(D_CMND_BACKLOG))) { @@ -561,14 +610,14 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) backlog_index++; if (backlog_index >= MAX_BACKLOG) backlog_index = 0; } - blcommand = strtok(NULL, ";"); + blcommand = strtok(nullptr, ";"); } -// snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, D_JSON_APPENDED); +// Response_P(S_JSON_COMMAND_SVALUE, command, D_JSON_APPENDED); mqtt_data[0] = '\0'; } else { uint8_t blflag = (backlog_pointer == backlog_index); backlog_pointer = backlog_index; - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, blflag ? D_JSON_EMPTY : D_JSON_ABORTED); + Response_P(S_JSON_COMMAND_SVALUE, command, blflag ? D_JSON_EMPTY : D_JSON_ABORTED); } } else if (CMND_DELAY == command_code) { @@ -578,7 +627,7 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) uint16_t bl_delay = 0; long bl_delta = TimePassedSince(backlog_delay); if (bl_delta < 0) { bl_delay = (bl_delta *-1) / 100; } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, bl_delay); + Response_P(S_JSON_COMMAND_NVALUE, command, bl_delay); } else if ((CMND_POWER == command_code) && (index > 0) && (index <= devices_present)) { if ((payload < 0) || (payload > 4)) { payload = 9; } @@ -601,7 +650,7 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) if ((payload >= 0) && (payload < MAX_FAN_SPEED) && (payload != GetFanspeed())) { SetFanspeed(payload); } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, GetFanspeed()); + Response_P(S_JSON_COMMAND_NVALUE, command, GetFanspeed()); } else if (CMND_STATUS == command_code) { if ((payload < 0) || (payload > MAX_STATUS)) payload = 99; @@ -615,6 +664,11 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) if (Settings.flag3.hass_tele_on_power) { MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_STATE), MQTT_TELE_RETAIN); } +#ifdef USE_HOME_ASSISTANT + if (Settings.flag.hass_discovery) { + HAssPublishStatus(); + } +#endif // USE_HOME_ASSISTANT } else if (CMND_SLEEP == command_code) { if ((payload >= 0) && (payload < 251)) { @@ -622,7 +676,7 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) sleep = payload; WiFiSetSleepMode(); } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE_UNIT_NVALUE_UNIT, command, sleep, (Settings.flag.value_units) ? " " D_UNIT_MILLISECOND : "", Settings.sleep, (Settings.flag.value_units) ? " " D_UNIT_MILLISECOND : ""); + Response_P(S_JSON_COMMAND_NVALUE_UNIT_NVALUE_UNIT, command, sleep, (Settings.flag.value_units) ? " " D_UNIT_MILLISECOND : "", Settings.sleep, (Settings.flag.value_units) ? " " D_UNIT_MILLISECOND : ""); } else if ((CMND_UPGRADE == command_code) || (CMND_UPLOAD == command_code)) { // Check if the payload is numerically 1, and had no trailing chars. @@ -631,36 +685,36 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) // We also need at least 3 chars to make a valid version number string. if (((1 == data_len) && (1 == payload)) || ((data_len >= 3) && NewerVersion(dataBuf))) { ota_state_flag = 3; - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"%s\":\"" D_JSON_VERSION " %s " D_JSON_FROM " %s\"}"), command, my_version, GetOtaUrl(stemp1, sizeof(stemp1))); + Response_P(PSTR("{\"%s\":\"" D_JSON_VERSION " %s " D_JSON_FROM " %s\"}"), command, my_version, GetOtaUrl(stemp1, sizeof(stemp1))); } else { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"%s\":\"" D_JSON_ONE_OR_GT "\"}"), command, my_version); + Response_P(PSTR("{\"%s\":\"" D_JSON_ONE_OR_GT "\"}"), command, my_version); } } else if (CMND_OTAURL == command_code) { if ((data_len > 0) && (data_len < sizeof(Settings.ota_url))) { strlcpy(Settings.ota_url, (SC_DEFAULT == Shortcut(dataBuf)) ? OTA_URL : dataBuf, sizeof(Settings.ota_url)); } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, Settings.ota_url); + Response_P(S_JSON_COMMAND_SVALUE, command, Settings.ota_url); } else if (CMND_SERIALLOG == command_code) { if ((payload >= LOG_LEVEL_NONE) && (payload <= LOG_LEVEL_ALL)) { Settings.flag.mqtt_serial = 0; SetSeriallog(payload); } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE_ACTIVE_NVALUE, command, Settings.seriallog_level, seriallog_level); + Response_P(S_JSON_COMMAND_NVALUE_ACTIVE_NVALUE, command, Settings.seriallog_level, seriallog_level); } else if (CMND_RESTART == command_code) { switch (payload) { case 1: restart_flag = 2; - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, D_JSON_RESTARTING); + Response_P(S_JSON_COMMAND_SVALUE, command, D_JSON_RESTARTING); break; case 99: AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_RESTARTING)); EspRestart(); break; default: - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, D_JSON_ONE_TO_RESTART); + Response_P(S_JSON_COMMAND_SVALUE, command, D_JSON_ONE_TO_RESTART); } } else if ((CMND_POWERONSTATE == command_code) && (my_module_type != MOTOR)) { @@ -674,33 +728,33 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) if ((payload >= POWER_ALL_OFF) && (payload <= POWER_ALL_OFF_PULSETIME_ON)) { Settings.poweronstate = payload; if (POWER_ALL_ALWAYS_ON == Settings.poweronstate) { - for (uint8_t i = 1; i <= devices_present; i++) { + for (uint32_t i = 1; i <= devices_present; i++) { ExecuteCommandPower(i, POWER_ON, SRC_IGNORE); } } } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.poweronstate); + Response_P(S_JSON_COMMAND_NVALUE, command, Settings.poweronstate); } else if ((CMND_PULSETIME == command_code) && (index > 0) && (index <= MAX_PULSETIMERS)) { if (data_len > 0) { Settings.pulse_timer[index -1] = payload16; // 0 - 65535 SetPulseTimer(index -1, payload16); } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_NVALUE_ACTIVE_NVALUE, command, index, Settings.pulse_timer[index -1], GetPulseTimer(index -1)); + Response_P(S_JSON_COMMAND_INDEX_NVALUE_ACTIVE_NVALUE, command, index, Settings.pulse_timer[index -1], GetPulseTimer(index -1)); } else if (CMND_BLINKTIME == command_code) { if ((payload > 1) && (payload <= 3600)) { Settings.blinktime = payload; if (blink_timer > 0) { blink_timer = millis() + (100 * payload); } } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.blinktime); + Response_P(S_JSON_COMMAND_NVALUE, command, Settings.blinktime); } else if (CMND_BLINKCOUNT == command_code) { if (data_len > 0) { Settings.blinkcount = payload16; // 0 - 65535 if (blink_counter) blink_counter = Settings.blinkcount *2; } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.blinkcount); + Response_P(S_JSON_COMMAND_NVALUE, command, Settings.blinkcount); } else if (CMND_SAVEDATA == command_code) { if ((payload >= 0) && (payload <= 3600)) { @@ -711,7 +765,7 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) if (Settings.save_data > 1) { snprintf_P(stemp1, sizeof(stemp1), PSTR(D_JSON_EVERY " %d " D_UNIT_SECOND), Settings.save_data); } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, (Settings.save_data > 1) ? stemp1 : GetStateText(Settings.save_data)); + Response_P(S_JSON_COMMAND_SVALUE, command, (Settings.save_data > 1) ? stemp1 : GetStateText(Settings.save_data)); } else if ((CMND_SENSOR == command_code) || (CMND_DRIVER == command_code)) { XdrvMailbox.index = index; @@ -800,101 +854,116 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) if ((payload >= param_low) && (payload <= param_high)) { Settings.param[pindex] = payload; switch (pindex) { - case P_RGB_REMAP: +#ifdef USE_LIGHT + case P_RGB_REMAP: LightUpdateColorMapping(); break; +#endif +#if defined(USE_IR_REMOTE) && defined(USE_IR_RECEIVE) + case P_IR_UNKNOW_THRESHOLD: + IrReceiveUpdateThreshold(); + break; +#endif } } } } if (ptype < 99) { if (2 == ptype) snprintf_P(stemp1, sizeof(stemp1), PSTR("%d"), Settings.param[pindex]); - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, command, index, (2 == ptype) ? stemp1 : (1 == ptype) ? GetStateText(bitRead(Settings.flag3.data, pindex)) : GetStateText(bitRead(Settings.flag.data, pindex))); + Response_P(S_JSON_COMMAND_INDEX_SVALUE, command, index, (2 == ptype) ? stemp1 : (1 == ptype) ? GetStateText(bitRead(Settings.flag3.data, pindex)) : GetStateText(bitRead(Settings.flag.data, pindex))); } } else if (CMND_TEMPERATURE_RESOLUTION == command_code) { if ((payload >= 0) && (payload <= 3)) { Settings.flag2.temperature_resolution = payload; } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.flag2.temperature_resolution); + Response_P(S_JSON_COMMAND_NVALUE, command, Settings.flag2.temperature_resolution); } else if (CMND_HUMIDITY_RESOLUTION == command_code) { if ((payload >= 0) && (payload <= 3)) { Settings.flag2.humidity_resolution = payload; } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.flag2.humidity_resolution); + Response_P(S_JSON_COMMAND_NVALUE, command, Settings.flag2.humidity_resolution); } else if (CMND_PRESSURE_RESOLUTION == command_code) { if ((payload >= 0) && (payload <= 3)) { Settings.flag2.pressure_resolution = payload; } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.flag2.pressure_resolution); + Response_P(S_JSON_COMMAND_NVALUE, command, Settings.flag2.pressure_resolution); } else if (CMND_POWER_RESOLUTION == command_code) { if ((payload >= 0) && (payload <= 3)) { Settings.flag2.wattage_resolution = payload; } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.flag2.wattage_resolution); + Response_P(S_JSON_COMMAND_NVALUE, command, Settings.flag2.wattage_resolution); } else if (CMND_VOLTAGE_RESOLUTION == command_code) { if ((payload >= 0) && (payload <= 3)) { Settings.flag2.voltage_resolution = payload; } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.flag2.voltage_resolution); + Response_P(S_JSON_COMMAND_NVALUE, command, Settings.flag2.voltage_resolution); } else if (CMND_FREQUENCY_RESOLUTION == command_code) { if ((payload >= 0) && (payload <= 3)) { Settings.flag2.frequency_resolution = payload; } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.flag2.frequency_resolution); + Response_P(S_JSON_COMMAND_NVALUE, command, Settings.flag2.frequency_resolution); } else if (CMND_CURRENT_RESOLUTION == command_code) { if ((payload >= 0) && (payload <= 3)) { Settings.flag2.current_resolution = payload; } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.flag2.current_resolution); + Response_P(S_JSON_COMMAND_NVALUE, command, Settings.flag2.current_resolution); } else if (CMND_ENERGY_RESOLUTION == command_code) { if ((payload >= 0) && (payload <= 5)) { Settings.flag2.energy_resolution = payload; } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.flag2.energy_resolution); + Response_P(S_JSON_COMMAND_NVALUE, command, Settings.flag2.energy_resolution); } else if (CMND_WEIGHT_RESOLUTION == command_code) { if ((payload >= 0) && (payload <= 3)) { Settings.flag2.weight_resolution = payload; } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.flag2.weight_resolution); + Response_P(S_JSON_COMMAND_NVALUE, command, Settings.flag2.weight_resolution); } else if (CMND_MODULE == command_code) { if ((payload >= 0) && (payload <= MAXMODULE)) { - if (0 == payload) { payload = 256; } - payload--; - Settings.last_module = Settings.module; - Settings.module = payload; - SetModuleType(); - if (Settings.last_module != payload) { - for (uint8_t i = 0; i < sizeof(Settings.my_gp); i++) { - Settings.my_gp.io[i] = GPIO_NONE; - } + bool present = false; + if (0 == payload) { + payload = USER_MODULE; + present = true; + } else { + payload--; + present = ValidTemplateModule(payload); + } + if (present) { + Settings.last_module = Settings.module; + Settings.module = payload; + SetModuleType(); + if (Settings.last_module != payload) { + for (uint32_t i = 0; i < sizeof(Settings.my_gp); i++) { + Settings.my_gp.io[i] = GPIO_NONE; + } + } + restart_flag = 2; } - restart_flag = 2; } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE_SVALUE, command, ModuleNr(), ModuleName().c_str()); + Response_P(S_JSON_COMMAND_NVALUE_SVALUE, command, ModuleNr(), ModuleName().c_str()); } else if (CMND_MODULES == command_code) { - for (uint8_t i = 0; i <= MAXMODULE; i++) { + uint8_t midx = USER_MODULE; + for (uint32_t i = 0; i <= sizeof(kModuleNiceList); i++) { + if (i > 0) { midx = pgm_read_byte(kModuleNiceList + i -1); } if (!jsflg) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_MODULES "%d\":["), lines); + Response_P(PSTR("{\"" D_CMND_MODULES "%d\":["), lines); } else { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,"), mqtt_data); + ResponseAppend_P(PSTR(",")); } jsflg = true; - uint8_t j = i; - if (0 == i) { j = USER_MODULE; } else { j--; } - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s\"%d (%s)\""), mqtt_data, i, AnyModuleName(j).c_str()); - if ((strlen(mqtt_data) > (LOGSZ - TOPSZ)) || (i == MAXMODULE)) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s]}"), mqtt_data); + uint8_t j = i ? midx +1 : 0; + if ((ResponseAppend_P(PSTR("\"%d (%s)\""), j, AnyModuleName(midx).c_str()) > (LOGSZ - TOPSZ)) || (i == sizeof(kModuleNiceList))) { + ResponseAppend_P(PSTR("]}")); MqttPublishPrefixTopic_P(RESULT_OR_STAT, type); jsflg = false; lines++; @@ -902,17 +971,37 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) } mqtt_data[0] = '\0'; } +#ifndef USE_ADC_VCC + else if (CMND_ADC == command_code) { + if (ValidAdc() && (payload >= 0) && (payload < ADC0_END)) { + Settings.my_adc0 = payload; + restart_flag = 2; + } + Response_P(PSTR("{\"" D_CMND_ADC "0\":\"%d (%s)\"}"), Settings.my_adc0, GetTextIndexed(stemp1, sizeof(stemp1), Settings.my_adc0, kAdc0Names)); + } + else if (CMND_ADCS == command_code) { + Response_P(PSTR("{\"" D_CMND_ADCS "\":[")); + for (uint32_t i = 0; i < ADC0_END; i++) { + if (jsflg) { + ResponseAppend_P(PSTR(",")); + } + jsflg = true; + ResponseAppend_P(PSTR("\"%d (%s)\""), i, GetTextIndexed(stemp1, sizeof(stemp1), i, kAdc0Names)); + } + ResponseAppend_P(PSTR("]}")); + } +#endif // USE_ADC_VCC else if ((CMND_GPIO == command_code) && (index < sizeof(Settings.my_gp))) { myio cmodule; ModuleGpios(&cmodule); if (ValidGPIO(index, cmodule.io[index]) && (payload >= 0) && (payload < GPIO_SENSOR_END)) { bool present = false; - for (uint8_t i = 0; i < sizeof(kGpioNiceList); i++) { + for (uint32_t i = 0; i < sizeof(kGpioNiceList); i++) { uint8_t midx = pgm_read_byte(kGpioNiceList + i); if (midx == payload) { present = true; } } if (present) { - for (uint8_t i = 0; i < sizeof(Settings.my_gp); i++) { + for (uint32_t i = 0; i < sizeof(Settings.my_gp); i++) { if (ValidGPIO(i, cmodule.io[i]) && (Settings.my_gp.io[i] == payload)) { Settings.my_gp.io[i] = GPIO_NONE; } @@ -921,37 +1010,35 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) restart_flag = 2; } } - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{")); - for (uint8_t i = 0; i < sizeof(Settings.my_gp); i++) { + Response_P(PSTR("{")); + for (uint32_t i = 0; i < sizeof(Settings.my_gp); i++) { if (ValidGPIO(i, cmodule.io[i])) { - if (jsflg) snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,"), mqtt_data); + if (jsflg) { ResponseAppend_P(PSTR(",")); } jsflg = true; - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s\"" D_CMND_GPIO "%d\":\"%d (%s)\""), - mqtt_data, i, Settings.my_gp.io[i], GetTextIndexed(stemp1, sizeof(stemp1), Settings.my_gp.io[i], kSensorNames)); + ResponseAppend_P(PSTR("\"" D_CMND_GPIO "%d\":\"%d (%s)\""), i, Settings.my_gp.io[i], GetTextIndexed(stemp1, sizeof(stemp1), Settings.my_gp.io[i], kSensorNames)); } } if (jsflg) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s}"), mqtt_data); + ResponseJsonEnd(); } else { - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, D_JSON_NOT_SUPPORTED); + Response_P(S_JSON_COMMAND_SVALUE, command, D_JSON_NOT_SUPPORTED); } } else if (CMND_GPIOS == command_code) { myio cmodule; ModuleGpios(&cmodule); uint8_t midx; - for (uint8_t i = 0; i < sizeof(kGpioNiceList); i++) { + for (uint32_t i = 0; i < sizeof(kGpioNiceList); i++) { midx = pgm_read_byte(kGpioNiceList + i); if (!GetUsedInModule(midx, cmodule.io)) { if (!jsflg) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_GPIOS "%d\":["), lines); + Response_P(PSTR("{\"" D_CMND_GPIOS "%d\":["), lines); } else { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,"), mqtt_data); + ResponseAppend_P(PSTR(",")); } jsflg = true; - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s\"%d (%s)\""), mqtt_data, midx, GetTextIndexed(stemp1, sizeof(stemp1), midx, kSensorNames)); - if ((strlen(mqtt_data) > (LOGSZ - TOPSZ)) || (i == sizeof(kGpioNiceList) -1)) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s]}"), mqtt_data); + if ((ResponseAppend_P(PSTR("\"%d (%s)\""), midx, GetTextIndexed(stemp1, sizeof(stemp1), midx, kSensorNames)) > (LOGSZ - TOPSZ)) || (i == sizeof(kGpioNiceList) -1)) { + ResponseAppend_P(PSTR("]}")); MqttPublishPrefixTopic_P(RESULT_OR_STAT, type); jsflg = false; lines++; @@ -964,10 +1051,13 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) // {"NAME":"Generic","GPIO":[17,254,29,254,7,254,254,254,138,254,139,254,254],"FLAG":1,"BASE":255} bool error = false; - if (!strstr(dataBuf, "{")) { // If no JSON it must be parameter + if (strstr(dataBuf, "{") == nullptr) { // If no JSON it must be parameter if ((payload > 0) && (payload <= MAXMODULE)) { - ModuleDefault(payload -1); // Copy template module - if (USER_MODULE == Settings.module) { restart_flag = 2; } + payload--; + if (ValidTemplateModule(payload)) { + ModuleDefault(payload); // Copy template module + if (USER_MODULE == Settings.module) { restart_flag = 2; } + } } else if (0 == payload) { // Copy current template to user template if (Settings.module != USER_MODULE) { @@ -980,7 +1070,7 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) } snprintf_P(Settings.user_template.name, sizeof(Settings.user_template.name), PSTR("Merged")); uint8_t j = 0; - for (uint8_t i = 0; i < sizeof(mycfgio); i++) { + for (uint32_t i = 0; i < sizeof(mycfgio); i++) { if (6 == i) { j = 9; } if (8 == i) { j = 12; } if (my_module.io[j] > GPIO_NONE) { @@ -990,11 +1080,11 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) } } } - else if (data_len > 9) { // Workaround exception if empty JSON like {} - Needs checks + else { if (JsonTemplate(dataBuf)) { // Free 336 bytes StaticJsonBuffer stack space by moving code to function if (USER_MODULE == Settings.module) { restart_flag = 2; } } else { - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, D_JSON_INVALID_JSON); + Response_P(S_JSON_COMMAND_SVALUE, command, D_JSON_INVALID_JSON); error = true; } } @@ -1005,28 +1095,28 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) Settings.pwm_value[index -1] = payload; analogWrite(pin[GPIO_PWM1 + index -1], bitRead(pwm_inverted, index -1) ? Settings.pwm_range - payload : payload); } - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{")); + Response_P(PSTR("{")); MqttShowPWMState(); // Render the PWM status to MQTT - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s}"), mqtt_data); + ResponseJsonEnd(); } else if (CMND_PWMFREQUENCY == command_code) { if ((1 == payload) || ((payload >= PWM_MIN) && (payload <= PWM_MAX))) { Settings.pwm_frequency = (1 == payload) ? PWM_FREQ : payload; analogWriteFreq(Settings.pwm_frequency); // Default is 1000 (core_esp8266_wiring_pwm.c) } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.pwm_frequency); + Response_P(S_JSON_COMMAND_NVALUE, command, Settings.pwm_frequency); } else if (CMND_PWMRANGE == command_code) { if ((1 == payload) || ((payload > 254) && (payload < 1024))) { Settings.pwm_range = (1 == payload) ? PWM_RANGE : payload; - for (uint8_t i = 0; i < MAX_PWMS; i++) { + for (uint32_t i = 0; i < MAX_PWMS; i++) { if (Settings.pwm_value[i] > Settings.pwm_range) { Settings.pwm_value[i] = Settings.pwm_range; } } analogWriteRange(Settings.pwm_range); // Default is 1023 (Arduino.h) } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.pwm_range); + Response_P(S_JSON_COMMAND_NVALUE, command, Settings.pwm_range); } else if ((CMND_COUNTER == command_code) && (index > 0) && (index <= MAX_COUNTERS)) { if ((data_len > 0) && (pin[GPIO_CNTR1 + index -1] < 99)) { @@ -1038,7 +1128,7 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) Settings.pulse_counter[index -1] = payload32; } } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_LVALUE, command, index, RtcSettings.pulse_counter[index -1]); + Response_P(S_JSON_COMMAND_INDEX_LVALUE, command, index, RtcSettings.pulse_counter[index -1]); } else if ((CMND_COUNTERTYPE == command_code) && (index > 0) && (index <= MAX_COUNTERS)) { if ((payload >= 0) && (payload <= 1) && (pin[GPIO_CNTR1 + index -1] < 99)) { @@ -1046,25 +1136,25 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) RtcSettings.pulse_counter[index -1] = 0; Settings.pulse_counter[index -1] = 0; } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_NVALUE, command, index, bitRead(Settings.pulse_counter_type, index -1)); + Response_P(S_JSON_COMMAND_INDEX_NVALUE, command, index, bitRead(Settings.pulse_counter_type, index -1)); } else if (CMND_COUNTERDEBOUNCE == command_code) { if ((data_len > 0) && (payload16 < 32001)) { Settings.pulse_counter_debounce = payload16; } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.pulse_counter_debounce); + Response_P(S_JSON_COMMAND_NVALUE, command, Settings.pulse_counter_debounce); } else if (CMND_BUTTONDEBOUNCE == command_code) { if ((payload > 39) && (payload < 1001)) { Settings.button_debounce = payload; } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.button_debounce); + Response_P(S_JSON_COMMAND_NVALUE, command, Settings.button_debounce); } else if (CMND_SWITCHDEBOUNCE == command_code) { if ((payload > 39) && (payload < 1001)) { Settings.switch_debounce = payload; } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.switch_debounce); + Response_P(S_JSON_COMMAND_NVALUE, command, Settings.switch_debounce); } else if (CMND_BAUDRATE == command_code) { if (payload32 > 1200) { @@ -1072,7 +1162,7 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) baudrate = (1 == payload) ? APP_BAUDRATE : payload32 * 1200; SetSerialBaudrate(baudrate); } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.baudrate * 1200); + Response_P(S_JSON_COMMAND_NVALUE, command, Settings.baudrate * 1200); } else if ((CMND_SERIALSEND == command_code) && (index > 0) && (index <= 5)) { SetSeriallog(LOG_LEVEL_NONE); @@ -1083,7 +1173,7 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) Serial.printf("%s\n", dataBuf); // "Hello Tiger\n" } else if (2 == index || 4 == index) { - for (uint16_t i = 0; i < data_len; i++) { + for (uint32_t i = 0; i < data_len; i++) { Serial.write(dataBuf[i]); // "Hello Tiger" or "A0" } } @@ -1094,7 +1184,7 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) else if (5 == index) { SerialSendRaw(RemoveSpace(dataBuf)); // "AA004566" as hex values } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, D_JSON_DONE); + Response_P(S_JSON_COMMAND_SVALUE, command, D_JSON_DONE); } } else if (CMND_SERIALDELIMITER == command_code) { @@ -1107,27 +1197,25 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) Settings.serial_delimiter = dataBuf[0]; } } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.serial_delimiter); + Response_P(S_JSON_COMMAND_NVALUE, command, Settings.serial_delimiter); } else if (CMND_SYSLOG == command_code) { if ((payload >= LOG_LEVEL_NONE) && (payload <= LOG_LEVEL_ALL)) { - Settings.syslog_level = payload; - syslog_level = payload; - syslog_timer = 0; + SetSyslog(payload); } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE_ACTIVE_NVALUE, command, Settings.syslog_level, syslog_level); + Response_P(S_JSON_COMMAND_NVALUE_ACTIVE_NVALUE, command, Settings.syslog_level, syslog_level); } else if (CMND_LOGHOST == command_code) { if ((data_len > 0) && (data_len < sizeof(Settings.syslog_host))) { strlcpy(Settings.syslog_host, (SC_DEFAULT == Shortcut(dataBuf)) ? SYS_LOG_HOST : dataBuf, sizeof(Settings.syslog_host)); } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, Settings.syslog_host); + Response_P(S_JSON_COMMAND_SVALUE, command, Settings.syslog_host); } else if (CMND_LOGPORT == command_code) { if (payload16 > 0) { Settings.syslog_port = (1 == payload16) ? SYS_LOG_PORT : payload16; } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.syslog_port); + Response_P(S_JSON_COMMAND_NVALUE, command, Settings.syslog_port); } else if ((CMND_IPADDRESS == command_code) && (index > 0) && (index <= 4)) { if (ParseIp(&address, dataBuf)) { @@ -1135,7 +1223,7 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) // restart_flag = 2; } snprintf_P(stemp1, sizeof(stemp1), PSTR(" (%s)"), WiFi.localIP().toString().c_str()); - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE_SVALUE, command, index, IPAddress(Settings.ip_address[index -1]).toString().c_str(), (1 == index) ? stemp1:""); + Response_P(S_JSON_COMMAND_INDEX_SVALUE_SVALUE, command, index, IPAddress(Settings.ip_address[index -1]).toString().c_str(), (1 == index) ? stemp1:""); } else if ((CMND_NTPSERVER == command_code) && (index > 0) && (index <= 3)) { if ((data_len > 0) && (data_len < sizeof(Settings.ntp_server[0]))) { @@ -1146,7 +1234,7 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) // restart_flag = 2; // Issue #3890 ntp_force_sync = true; } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, command, index, Settings.ntp_server[index -1]); + Response_P(S_JSON_COMMAND_INDEX_SVALUE, command, index, Settings.ntp_server[index -1]); } else if (CMND_AP == command_code) { if ((payload >= 0) && (payload <= 2)) { @@ -1160,7 +1248,7 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) } restart_flag = 2; } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE_SVALUE, command, Settings.sta_active +1, Settings.sta_ssid[Settings.sta_active]); + Response_P(S_JSON_COMMAND_NVALUE_SVALUE, command, Settings.sta_active +1, Settings.sta_ssid[Settings.sta_active]); } else if ((CMND_SSID == command_code) && (index > 0) && (index <= 2)) { if ((data_len > 0) && (data_len < sizeof(Settings.sta_ssid[0]))) { @@ -1168,41 +1256,41 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) Settings.sta_active = index -1; restart_flag = 2; } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, command, index, Settings.sta_ssid[index -1]); + Response_P(S_JSON_COMMAND_INDEX_SVALUE, command, index, Settings.sta_ssid[index -1]); } else if ((CMND_PASSWORD == command_code) && (index > 0) && (index <= 2)) { if ((data_len > 4 || SC_CLEAR == Shortcut(dataBuf) || SC_DEFAULT == Shortcut(dataBuf)) && (data_len < sizeof(Settings.sta_pwd[0]))) { strlcpy(Settings.sta_pwd[index -1], (SC_CLEAR == Shortcut(dataBuf)) ? "" : (SC_DEFAULT == Shortcut(dataBuf)) ? (1 == index) ? STA_PASS1 : STA_PASS2 : dataBuf, sizeof(Settings.sta_pwd[0])); Settings.sta_active = index -1; restart_flag = 2; - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, command, index, Settings.sta_pwd[index -1]); + Response_P(S_JSON_COMMAND_INDEX_SVALUE, command, index, Settings.sta_pwd[index -1]); } else { - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_ASTERIX, command, index); + Response_P(S_JSON_COMMAND_INDEX_ASTERISK, command, index); } } else if (CMND_HOSTNAME == command_code) { if (!grpflg && (data_len > 0) && (data_len < sizeof(Settings.hostname))) { strlcpy(Settings.hostname, (SC_DEFAULT == Shortcut(dataBuf)) ? WIFI_HOSTNAME : dataBuf, sizeof(Settings.hostname)); - if (strstr(Settings.hostname,"%")) { + if (strstr(Settings.hostname, "%") != nullptr) { strlcpy(Settings.hostname, WIFI_HOSTNAME, sizeof(Settings.hostname)); } restart_flag = 2; } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, Settings.hostname); + Response_P(S_JSON_COMMAND_SVALUE, command, Settings.hostname); } else if (CMND_WIFICONFIG == command_code) { if ((payload >= WIFI_RESTART) && (payload < MAX_WIFI_OPTION)) { Settings.sta_config = payload; wifi_state_flag = Settings.sta_config; snprintf_P(stemp1, sizeof(stemp1), kWifiConfig[Settings.sta_config]); - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_WIFICONFIG "\":\"%s " D_JSON_SELECTED "\"}"), stemp1); + Response_P(PSTR("{\"" D_CMND_WIFICONFIG "\":\"%s " D_JSON_SELECTED "\"}"), stemp1); if (WifiState() > WIFI_RESTART) { -// snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s after restart"), mqtt_data); +// ResponseAppend_P(PSTR(" after restart")); restart_flag = 2; } } else { snprintf_P(stemp1, sizeof(stemp1), kWifiConfig[Settings.sta_config]); - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE_SVALUE, command, Settings.sta_config, stemp1); + Response_P(S_JSON_COMMAND_NVALUE_SVALUE, command, Settings.sta_config, stemp1); } } else if ((CMND_FRIENDLYNAME == command_code) && (index > 0) && (index <= MAX_FRIENDLYNAMES)) { @@ -1214,13 +1302,13 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) } strlcpy(Settings.friendlyname[index -1], (SC_DEFAULT == Shortcut(dataBuf)) ? stemp1 : dataBuf, sizeof(Settings.friendlyname[index -1])); } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, command, index, Settings.friendlyname[index -1]); + Response_P(S_JSON_COMMAND_INDEX_SVALUE, command, index, Settings.friendlyname[index -1]); } else if ((CMND_SWITCHMODE == command_code) && (index > 0) && (index <= MAX_SWITCHES)) { if ((payload >= 0) && (payload < MAX_SWITCH_OPTION)) { Settings.switchmode[index -1] = payload; } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_NVALUE, command, index, Settings.switchmode[index-1]); + Response_P(S_JSON_COMMAND_INDEX_NVALUE, command, index, Settings.switchmode[index-1]); } else if (CMND_INTERLOCK == command_code) { // Interlock 0 - Off, Interlock 1 - On, Interlock 1,2 3,4 5,6,7 uint8_t max_relays = devices_present; @@ -1228,15 +1316,15 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) if (max_relays > sizeof(Settings.interlock[0]) * 8) { max_relays = sizeof(Settings.interlock[0]) * 8; } if (max_relays > 1) { // Only interlock with more than 1 relay if (data_len > 0) { - if (strstr(dataBuf, ",")) { // Interlock entry - for (uint8_t i = 0; i < MAX_INTERLOCKS; i++) { Settings.interlock[i] = 0; } // Reset current interlocks + if (strstr(dataBuf, ",") != nullptr) { // Interlock entry + for (uint32_t i = 0; i < MAX_INTERLOCKS; i++) { Settings.interlock[i] = 0; } // Reset current interlocks char *group; char *q; uint8_t group_index = 0; power_t relay_mask = 0; - for (group = strtok_r(dataBuf, " ", &q); group && group_index < MAX_INTERLOCKS; group = strtok_r(NULL, " ", &q)) { + for (group = strtok_r(dataBuf, " ", &q); group && group_index < MAX_INTERLOCKS; group = strtok_r(nullptr, " ", &q)) { char *str; - for (str = strtok_r(group, ",", &p); str; str = strtok_r(NULL, ",", &p)) { + for (str = strtok_r(group, ",", &p); str; str = strtok_r(nullptr, ",", &p)) { int pbit = atoi(str); if ((pbit > 0) && (pbit <= max_relays)) { // Only valid relays pbit--; @@ -1248,9 +1336,9 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) } group_index++; } - for (uint8_t i = 0; i < group_index; i++) { + for (uint32_t i = 0; i < group_index; i++) { uint8_t minimal_bits = 0; - for (uint8_t j = 0; j < max_relays; j++) { + for (uint32_t j = 0; j < max_relays; j++) { if (bitRead(Settings.interlock[i], j)) { minimal_bits++; } } if (minimal_bits < 2) { Settings.interlock[i] = 0; } // Discard single relay as interlock @@ -1262,32 +1350,32 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) } } } - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_INTERLOCK "\":\"%s\",\"" D_JSON_GROUPS "\":\""), GetStateText(Settings.flag.interlock)); + Response_P(PSTR("{\"" D_CMND_INTERLOCK "\":\"%s\",\"" D_JSON_GROUPS "\":\""), GetStateText(Settings.flag.interlock)); uint8_t anygroup = 0; - for (uint8_t i = 0; i < MAX_INTERLOCKS; i++) { + for (uint32_t i = 0; i < MAX_INTERLOCKS; i++) { if (Settings.interlock[i]) { anygroup++; - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s%s"), mqtt_data, (anygroup > 1) ? " " : ""); + ResponseAppend_P(PSTR("%s"), (anygroup > 1) ? " " : ""); uint8_t anybit = 0; power_t mask = 1; - for (uint8_t j = 0; j < max_relays; j++) { + for (uint32_t j = 0; j < max_relays; j++) { if (Settings.interlock[i] & mask) { anybit++; - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s%s%d"), mqtt_data, (anybit > 1) ? "," : "", j +1); + ResponseAppend_P(PSTR("%s%d"), (anybit > 1) ? "," : "", j +1); } mask <<= 1; } } } if (!anygroup) { - for (uint8_t j = 1; j <= max_relays; j++) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s%s%d"), mqtt_data, (j > 1) ? "," : "", j); + for (uint32_t j = 1; j <= max_relays; j++) { + ResponseAppend_P(PSTR("%s%d"), (j > 1) ? "," : "", j); } } - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s\"}"), mqtt_data); + ResponseAppend_P(PSTR("\"}")); } else { Settings.flag.interlock = 0; - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, GetStateText(Settings.flag.interlock)); + Response_P(S_JSON_COMMAND_SVALUE, command, GetStateText(Settings.flag.interlock)); } } else if (CMND_TELEPERIOD == command_code) { @@ -1296,20 +1384,20 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) if ((Settings.tele_period > 0) && (Settings.tele_period < 10)) Settings.tele_period = 10; // Do not allow periods < 10 seconds tele_period = Settings.tele_period; } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE_UNIT, command, Settings.tele_period, (Settings.flag.value_units) ? " " D_UNIT_SECOND : ""); + Response_P(S_JSON_COMMAND_NVALUE_UNIT, command, Settings.tele_period, (Settings.flag.value_units) ? " " D_UNIT_SECOND : ""); } else if (CMND_RESET == command_code) { switch (payload) { case 1: restart_flag = 211; - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command , D_JSON_RESET_AND_RESTARTING); + Response_P(S_JSON_COMMAND_SVALUE, command , D_JSON_RESET_AND_RESTARTING); break; case 2 ... 6: restart_flag = 210 + payload; - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_RESET "\":\"" D_JSON_ERASE ", " D_JSON_RESET_AND_RESTARTING "\"}")); + Response_P(PSTR("{\"" D_CMND_RESET "\":\"" D_JSON_ERASE ", " D_JSON_RESET_AND_RESTARTING "\"}")); break; default: - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, D_JSON_ONE_TO_RESET); + Response_P(S_JSON_COMMAND_SVALUE, command, D_JSON_ONE_TO_RESET); } } else if (CMND_TIMEZONE == command_code) { @@ -1319,9 +1407,9 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) if (payload < 15) { p = strtok (dataBuf, ":"); if (p) { - p = strtok (NULL, ":"); + p = strtok (nullptr, ":"); if (p) { - Settings.timezone_minutes = strtol(p, NULL, 10); + Settings.timezone_minutes = strtol(p, nullptr, 10); if (Settings.timezone_minutes > 59) { Settings.timezone_minutes = 59; } } } @@ -1331,10 +1419,10 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) ntp_force_sync = true; } if (99 == Settings.timezone) { - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.timezone); + Response_P(S_JSON_COMMAND_NVALUE, command, Settings.timezone); } else { snprintf_P(stemp1, sizeof(stemp1), PSTR("%+03d:%02d"), Settings.timezone, Settings.timezone_minutes); - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, stemp1); + Response_P(S_JSON_COMMAND_SVALUE, command, stemp1); } } else if ((CMND_TIMESTD == command_code) || (CMND_TIMEDST == command_code)) { @@ -1342,7 +1430,7 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) uint8_t ts = 0; if (CMND_TIMEDST == command_code) { ts = 1; } if (data_len > 0) { - if (strstr(dataBuf, ",")) { // Process parameter entry + if (strstr(dataBuf, ",") != nullptr) { // Process parameter entry uint8_t tpos = 0; // Parameter index int value = 0; p = dataBuf; // Parameters like "1, 2,3 , 4 ,5, -120" or ",,,,,+240" @@ -1375,16 +1463,17 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) ntp_force_sync = true; } } - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"%s\":{\"Hemisphere\":%d,\"Week\":%d,\"Month\":%d,\"Day\":%d,\"Hour\":%d,\"Offset\":%d}}"), + Response_P(PSTR("{\"%s\":{\"Hemisphere\":%d,\"Week\":%d,\"Month\":%d,\"Day\":%d,\"Hour\":%d,\"Offset\":%d}}"), command, Settings.tflag[ts].hemis, Settings.tflag[ts].week, Settings.tflag[ts].month, Settings.tflag[ts].dow, Settings.tflag[ts].hour, Settings.toffset[ts]); } else if (CMND_ALTITUDE == command_code) { if ((data_len > 0) && ((payload >= -30000) && (payload <= 30000))) { Settings.altitude = payload; } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.altitude); + Response_P(S_JSON_COMMAND_NVALUE, command, Settings.altitude); } - else if (CMND_LEDPOWER == command_code) { + else if ((CMND_LEDPOWER == command_code) && (index > 0) && (index <= MAX_LEDS)) { +/* if ((payload >= 0) && (payload <= 2)) { Settings.ledstate &= 8; switch (payload) { @@ -1397,34 +1486,114 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) break; } blinks = 0; - SetLedPower(Settings.ledstate &8); + SetLedPowerIdx(index -1, Settings.ledstate &8); } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, GetStateText(bitRead(Settings.ledstate, 3))); + Response_P(S_JSON_COMMAND_INDEX_SVALUE, command, index, GetStateText(bitRead(Settings.ledstate, 3))); +*/ +/* + if (99 == pin[GPIO_LEDLNK]) { + if ((payload >= 0) && (payload <= 2)) { + Settings.ledstate &= 8; + switch (payload) { + case 0: // Off + case 1: // On + Settings.ledstate = payload << 3; + break; + case 2: // Toggle + Settings.ledstate ^= 8; + break; + } + blinks = 0; + SetLedPower(Settings.ledstate &8); + } + Response_P(S_JSON_COMMAND_SVALUE, command, GetStateText(bitRead(Settings.ledstate, 3))); + } else { + if ((payload >= 0) && (payload <= 2)) { + Settings.ledstate &= 8; // Disable power control + uint8_t mask = 1 << (index -1); // Led to control + switch (payload) { + case 0: // Off + led_power &= (0xFF ^ mask); + case 1: // On + led_power |= mask; + break; + case 2: // Toggle + led_power ^= mask; + break; + } + blinks = 0; + SetLedPowerIdx(index -1, (led_power & mask)); + } + Response_P(S_JSON_COMMAND_INDEX_SVALUE, command, index, GetStateText(bitRead(led_power, index -1))); + } +*/ + if (99 == pin[GPIO_LEDLNK]) { index = 1; } + if ((payload >= 0) && (payload <= 2)) { + Settings.ledstate &= 8; // Disable power control + uint8_t mask = 1 << (index -1); // Led to control + switch (payload) { + case 0: // Off + led_power &= (0xFF ^ mask); + Settings.ledstate = 0; + break; + case 1: // On + led_power |= mask; + Settings.ledstate = 8; + break; + case 2: // Toggle + led_power ^= mask; + Settings.ledstate ^= 8; + break; + } + blinks = 0; + if (99 == pin[GPIO_LEDLNK]) { + SetLedPower(Settings.ledstate &8); + } else { + SetLedPowerIdx(index -1, (led_power & mask)); + } + } + uint8_t state = bitRead(led_power, index -1); + if (99 == pin[GPIO_LEDLNK]) { + state = bitRead(Settings.ledstate, 3); + } + Response_P(S_JSON_COMMAND_INDEX_SVALUE, command, index, GetStateText(state)); } else if (CMND_LEDSTATE == command_code) { if ((payload >= 0) && (payload < MAX_LED_OPTION)) { Settings.ledstate = payload; if (!Settings.ledstate) { - SetLedPower(0); + SetLedPowerAll(0); SetLedLink(0); } } - snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.ledstate); + Response_P(S_JSON_COMMAND_NVALUE, command, Settings.ledstate); + } + else if (CMND_LEDMASK == command_code) { + if (data_len > 0) { + Settings.ledmask = payload16; + } + snprintf_P(stemp1, sizeof(stemp1), PSTR("%d (0x%04X)"), Settings.ledmask, Settings.ledmask); + Response_P(S_JSON_COMMAND_SVALUE, command, stemp1); } #ifdef USE_I2C else if ((CMND_I2CSCAN == command_code) && i2c_flg) { I2cScan(mqtt_data, sizeof(mqtt_data)); } #endif // USE_I2C - else type = NULL; // Unknown command + else type = nullptr; // Unknown command } - if (type == NULL) { + if (type == nullptr) { blinks = 201; snprintf_P(topicBuf, sizeof(topicBuf), PSTR(D_JSON_COMMAND)); - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_COMMAND "\":\"" D_JSON_UNKNOWN "\"}")); + Response_P(PSTR("{\"" D_JSON_COMMAND "\":\"" D_JSON_UNKNOWN "\"}")); type = (char*)topicBuf; } - if (mqtt_data[0] != '\0') MqttPublishPrefixTopic_P(RESULT_OR_STAT, type); + if (mqtt_data[0] != '\0') { + MqttPublishPrefixTopic_P(RESULT_OR_STAT, type); +#ifdef USE_SCRIPT + XdrvRulesProcess(); +#endif + } fallback_topic_flag = false; } @@ -1468,7 +1637,7 @@ bool SendKey(uint8_t key, uint8_t device, uint8_t state) #endif // USE_DOMOTICZ result = !Settings.flag3.button_switch_force_local; } else { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"%s%d\":{\"State\":%d}}"), (key) ? "Switch" : "Button", device, state); + Response_P(PSTR("{\"%s%d\":{\"State\":%d}}"), (key) ? "Switch" : "Button", device, state); result = XdrvRulesProcess(); } #ifdef USE_KNX @@ -1504,7 +1673,10 @@ void ExecuteCommandPower(uint8_t device, uint8_t state, int source) state &= 1; publish_power = 0; } + if ((device < 1) || (device > devices_present)) device = 1; + active_device = device; + if (device <= MAX_PULSETIMERS) { SetPulseTimer(device -1, 0); } power_t mask = 1 << (device -1); // Device to control if (state <= POWER_TOGGLE) { @@ -1515,9 +1687,9 @@ void ExecuteCommandPower(uint8_t device, uint8_t state, int source) if (Settings.flag.interlock && !interlock_mutex) { // Clear all but masked relay in interlock group interlock_mutex = true; - for (uint8_t i = 0; i < MAX_INTERLOCKS; i++) { + for (uint32_t i = 0; i < MAX_INTERLOCKS; i++) { if (Settings.interlock[i] & mask) { // Find interlock group - for (uint8_t j = 0; j < devices_present; j++) { + for (uint32_t j = 0; j < devices_present; j++) { power_t imask = 1 << j; if ((Settings.interlock[i] & imask) && (power & imask) && (mask != imask)) { ExecuteCommandPower(j +1, POWER_OFF, SRC_IGNORE); @@ -1547,11 +1719,7 @@ void ExecuteCommandPower(uint8_t device, uint8_t state, int source) #ifdef USE_KNX KnxUpdatePowerState(device, power); #endif // USE_KNX - if (publish_power && Settings.flag3.hass_tele_on_power) { - mqtt_data[0] = '\0'; - MqttShowState(); - MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_STATE), MQTT_TELE_RETAIN); - } + if (publish_power && Settings.flag3.hass_tele_on_power) { MqttPublishTeleState(); } if (device <= MAX_PULSETIMERS) { // Restart PulseTime if powered On SetPulseTimer(device -1, (((POWER_ALL_OFF_PULSETIME_ON == Settings.poweronstate) ? ~power : power) & mask) ? Settings.pulse_timer[device -1] : 0); } @@ -1581,7 +1749,7 @@ void StopAllPowerBlink(void) { power_t mask; - for (uint8_t i = 1; i <= devices_present; i++) { + for (uint32_t i = 1; i <= devices_present; i++) { mask = 1 << (i -1); if (blink_mask & mask) { blink_mask &= (POWER_MASK ^ mask); // Clear device mask @@ -1591,6 +1759,18 @@ void StopAllPowerBlink(void) } } +void SetAllPower(uint8_t state, int source) +{ + if ((POWER_ALL_OFF == state) || (POWER_ALL_ON == state)) { + power = 0; + if (POWER_ALL_ON == state) { + power = (1 << devices_present) -1; + } + SetDevicePower(power, source); + MqttPublishAllPowerState(); + } +} + void ExecuteCommand(char *cmnd, int source) { char *start; @@ -1602,18 +1782,18 @@ void ExecuteCommand(char *cmnd, int source) ShowSource(source); token = strtok(cmnd, " "); - if (token != NULL) { + if (token != nullptr) { start = strrchr(token, '/'); // Skip possible cmnd/sonoff/ preamble if (start) { token = start +1; } } - uint16_t size = (token != NULL) ? strlen(token) : 0; + uint16_t size = (token != nullptr) ? strlen(token) : 0; char stopic[size +2]; // / + \0 - snprintf_P(stopic, sizeof(stopic), PSTR("/%s"), (token == NULL) ? "" : token); + snprintf_P(stopic, sizeof(stopic), PSTR("/%s"), (token == nullptr) ? "" : token); - token = strtok(NULL, ""); - size = (token != NULL) ? strlen(token) : 0; + token = strtok(nullptr, ""); + size = (token != nullptr) ? strlen(token) : 0; char svalue[size +1]; - strlcpy(svalue, (token == NULL) ? "" : token, sizeof(svalue)); // Fixed 5.8.0b + strlcpy(svalue, (token == nullptr) ? "" : token, sizeof(svalue)); // Fixed 5.8.0b MqttDataHandler(stopic, (uint8_t*)svalue, strlen(svalue)); } @@ -1633,56 +1813,61 @@ void PublishStatus(uint8_t payload) uint8_t maxfn = (devices_present > MAX_FRIENDLYNAMES) ? MAX_FRIENDLYNAMES : (!devices_present) ? 1 : devices_present; if (SONOFF_IFAN02 == my_module_type) { maxfn = 1; } stemp[0] = '\0'; - for (uint8_t i = 0; i < maxfn; i++) { + for (uint32_t i = 0; i < maxfn; i++) { snprintf_P(stemp, sizeof(stemp), PSTR("%s%s\"%s\"" ), stemp, (i > 0 ? "," : ""), Settings.friendlyname[i]); } stemp2[0] = '\0'; - for (uint8_t i = 0; i < MAX_SWITCHES; i++) { + for (uint32_t i = 0; i < MAX_SWITCHES; i++) { snprintf_P(stemp2, sizeof(stemp2), PSTR("%s%s%d" ), stemp2, (i > 0 ? "," : ""), Settings.switchmode[i]); } - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_STATUS "\":{\"" D_CMND_MODULE "\":%d,\"" D_CMND_FRIENDLYNAME "\":[%s],\"" D_CMND_TOPIC "\":\"%s\",\"" D_CMND_BUTTONTOPIC "\":\"%s\",\"" D_CMND_POWER "\":%d,\"" D_CMND_POWERONSTATE "\":%d,\"" D_CMND_LEDSTATE "\":%d,\"" D_CMND_SAVEDATA "\":%d,\"" D_JSON_SAVESTATE "\":%d,\"" D_CMND_SWITCHTOPIC "\":\"%s\",\"" D_CMND_SWITCHMODE "\":[%s],\"" D_CMND_BUTTONRETAIN "\":%d,\"" D_CMND_SWITCHRETAIN "\":%d,\"" D_CMND_SENSORRETAIN "\":%d,\"" D_CMND_POWERRETAIN "\":%d}}"), - ModuleNr(), stemp, mqtt_topic, Settings.button_topic, power, Settings.poweronstate, Settings.ledstate, Settings.save_data, Settings.flag.save_state, Settings.switch_topic, stemp2, Settings.flag.mqtt_button_retain, Settings.flag.mqtt_switch_retain, Settings.flag.mqtt_sensor_retain, Settings.flag.mqtt_power_retain); + Response_P(PSTR("{\"" D_CMND_STATUS "\":{\"" D_CMND_MODULE "\":%d,\"" D_CMND_FRIENDLYNAME "\":[%s],\"" D_CMND_TOPIC "\":\"%s\",\"" D_CMND_BUTTONTOPIC "\":\"%s\",\"" D_CMND_POWER "\":%d,\"" D_CMND_POWERONSTATE "\":%d,\"" D_CMND_LEDSTATE "\":%d,\"" D_CMND_LEDMASK "\":\"%04X\",\"" D_CMND_SAVEDATA "\":%d,\"" D_JSON_SAVESTATE "\":%d,\"" D_CMND_SWITCHTOPIC "\":\"%s\",\"" D_CMND_SWITCHMODE "\":[%s],\"" D_CMND_BUTTONRETAIN "\":%d,\"" D_CMND_SWITCHRETAIN "\":%d,\"" D_CMND_SENSORRETAIN "\":%d,\"" D_CMND_POWERRETAIN "\":%d}}"), + ModuleNr(), stemp, mqtt_topic, Settings.button_topic, power, Settings.poweronstate, Settings.ledstate, Settings.ledmask, Settings.save_data, Settings.flag.save_state, Settings.switch_topic, stemp2, Settings.flag.mqtt_button_retain, Settings.flag.mqtt_switch_retain, Settings.flag.mqtt_sensor_retain, Settings.flag.mqtt_power_retain); MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS)); } if ((0 == payload) || (1 == payload)) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_STATUS D_STATUS1_PARAMETER "\":{\"" D_JSON_BAUDRATE "\":%d,\"" D_CMND_GROUPTOPIC "\":\"%s\",\"" D_CMND_OTAURL "\":\"%s\",\"" D_JSON_RESTARTREASON "\":\"%s\",\"" D_JSON_UPTIME "\":\"%s\",\"" D_JSON_STARTUPUTC "\":\"%s\",\"" D_CMND_SLEEP "\":%d,\"" D_JSON_CONFIG_HOLDER "\":%d,\"" D_JSON_BOOTCOUNT "\":%d,\"" D_JSON_SAVECOUNT "\":%d,\"" D_JSON_SAVEADDRESS "\":\"%X\"}}"), + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS1_PARAMETER "\":{\"" D_JSON_BAUDRATE "\":%d,\"" D_CMND_GROUPTOPIC "\":\"%s\",\"" D_CMND_OTAURL "\":\"%s\",\"" D_JSON_RESTARTREASON "\":\"%s\",\"" D_JSON_UPTIME "\":\"%s\",\"" D_JSON_STARTUPUTC "\":\"%s\",\"" D_CMND_SLEEP "\":%d,\"" D_JSON_CONFIG_HOLDER "\":%d,\"" D_JSON_BOOTCOUNT "\":%d,\"" D_JSON_SAVECOUNT "\":%d,\"" D_JSON_SAVEADDRESS "\":\"%X\"}}"), baudrate, Settings.mqtt_grptopic, Settings.ota_url, GetResetReason().c_str(), GetUptime().c_str(), GetDateAndTime(DT_RESTART).c_str(), Settings.sleep, Settings.cfg_holder, Settings.bootcount, Settings.save_flag, GetSettingsAddress()); MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "1")); } if ((0 == payload) || (2 == payload)) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_STATUS D_STATUS2_FIRMWARE "\":{\"" D_JSON_VERSION "\":\"%s%s\",\"" D_JSON_BUILDDATETIME "\":\"%s\",\"" D_JSON_BOOTVERSION "\":%d,\"" D_JSON_COREVERSION "\":\"" ARDUINO_ESP8266_RELEASE "\",\"" D_JSON_SDKVERSION "\":\"%s\"}}"), + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS2_FIRMWARE "\":{\"" D_JSON_VERSION "\":\"%s%s\",\"" D_JSON_BUILDDATETIME "\":\"%s\",\"" D_JSON_BOOTVERSION "\":%d,\"" D_JSON_COREVERSION "\":\"" ARDUINO_ESP8266_RELEASE "\",\"" D_JSON_SDKVERSION "\":\"%s\"}}"), my_version, my_image, GetBuildDateAndTime().c_str(), ESP.getBootVersion(), ESP.getSdkVersion()); MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "2")); } if ((0 == payload) || (3 == payload)) { stemp2[0] = '\0'; - for (int8_t i = 0; i < PARAM8_SIZE; i++) { + for (uint32_t i = 0; i < PARAM8_SIZE; i++) { snprintf_P(stemp2, sizeof(stemp2), PSTR("%s%02X"), stemp2, Settings.param[i]); } - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_STATUS D_STATUS3_LOGGING "\":{\"" D_CMND_SERIALLOG "\":%d,\"" D_CMND_WEBLOG "\":%d,\"" D_CMND_SYSLOG "\":%d,\"" D_CMND_LOGHOST "\":\"%s\",\"" D_CMND_LOGPORT "\":%d,\"" D_CMND_SSID "\":[\"%s\",\"%s\"],\"" D_CMND_TELEPERIOD "\":%d,\"" D_JSON_RESOLUTION "\":\"%08X\",\"" D_CMND_SETOPTION "\":[\"%08X\",\"%s\",\"%08X\"]}}"), + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS3_LOGGING "\":{\"" D_CMND_SERIALLOG "\":%d,\"" D_CMND_WEBLOG "\":%d,\"" D_CMND_SYSLOG "\":%d,\"" D_CMND_LOGHOST "\":\"%s\",\"" D_CMND_LOGPORT "\":%d,\"" D_CMND_SSID "\":[\"%s\",\"%s\"],\"" D_CMND_TELEPERIOD "\":%d,\"" D_JSON_RESOLUTION "\":\"%08X\",\"" D_CMND_SETOPTION "\":[\"%08X\",\"%s\",\"%08X\"]}}"), Settings.seriallog_level, Settings.weblog_level, Settings.syslog_level, Settings.syslog_host, Settings.syslog_port, Settings.sta_ssid[0], Settings.sta_ssid[1], Settings.tele_period, Settings.flag2.data, Settings.flag.data, stemp2, Settings.flag3.data); MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "3")); } if ((0 == payload) || (4 == payload)) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_STATUS D_STATUS4_MEMORY "\":{\"" D_JSON_PROGRAMSIZE "\":%d,\"" D_JSON_FREEMEMORY "\":%d,\"" D_JSON_HEAPSIZE "\":%d,\"" D_JSON_PROGRAMFLASHSIZE "\":%d,\"" D_JSON_FLASHSIZE "\":%d,\"" D_JSON_FLASHCHIPID "\":\"%06X\",\"" D_JSON_FLASHMODE "\":%d,\"" D_JSON_FEATURES "\":[\"%08X\",\"%08X\",\"%08X\",\"%08X\",\"%08X\"]}}"), + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS4_MEMORY "\":{\"" D_JSON_PROGRAMSIZE "\":%d,\"" D_JSON_FREEMEMORY "\":%d,\"" D_JSON_HEAPSIZE "\":%d,\"" D_JSON_PROGRAMFLASHSIZE "\":%d,\"" D_JSON_FLASHSIZE "\":%d,\"" D_JSON_FLASHCHIPID "\":\"%06X\",\"" D_JSON_FLASHMODE "\":%d,\"" D_JSON_FEATURES "\":[\"%08X\",\"%08X\",\"%08X\",\"%08X\",\"%08X\"]}}"), ESP.getSketchSize()/1024, ESP.getFreeSketchSpace()/1024, ESP.getFreeHeap()/1024, ESP.getFlashChipSize()/1024, ESP.getFlashChipRealSize()/1024, ESP.getFlashChipId(), ESP.getFlashChipMode(), LANGUAGE_LCID, feature_drv1, feature_drv2, feature_sns1, feature_sns2); MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "4")); } if ((0 == payload) || (5 == payload)) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_STATUS D_STATUS5_NETWORK "\":{\"" D_CMND_HOSTNAME "\":\"%s\",\"" D_CMND_IPADDRESS "\":\"%s\",\"" D_JSON_GATEWAY "\":\"%s\",\"" D_JSON_SUBNETMASK "\":\"%s\",\"" D_JSON_DNSSERVER "\":\"%s\",\"" D_JSON_MAC "\":\"%s\",\"" D_CMND_WEBSERVER "\":%d,\"" D_CMND_WIFICONFIG "\":%d}}"), + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS5_NETWORK "\":{\"" D_CMND_HOSTNAME "\":\"%s\",\"" D_CMND_IPADDRESS "\":\"%s\",\"" D_JSON_GATEWAY "\":\"%s\",\"" D_JSON_SUBNETMASK "\":\"%s\",\"" D_JSON_DNSSERVER "\":\"%s\",\"" D_JSON_MAC "\":\"%s\",\"" D_CMND_WEBSERVER "\":%d,\"" D_CMND_WIFICONFIG "\":%d}}"), my_hostname, WiFi.localIP().toString().c_str(), IPAddress(Settings.ip_address[1]).toString().c_str(), IPAddress(Settings.ip_address[2]).toString().c_str(), IPAddress(Settings.ip_address[3]).toString().c_str(), WiFi.macAddress().c_str(), Settings.webserver, Settings.sta_config); MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "5")); } if (((0 == payload) || (6 == payload)) && Settings.flag.mqtt_enabled) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_STATUS D_STATUS6_MQTT "\":{\"" D_CMND_MQTTHOST "\":\"%s\",\"" D_CMND_MQTTPORT "\":%d,\"" D_CMND_MQTTCLIENT D_JSON_MASK "\":\"%s\",\"" D_CMND_MQTTCLIENT "\":\"%s\",\"" D_CMND_MQTTUSER "\":\"%s\",\"" D_JSON_MQTT_COUNT "\":%d,\"MAX_PACKET_SIZE\":%d,\"KEEPALIVE\":%d}}"), +#ifdef USE_MQTT_AWS_IOT + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS6_MQTT "\":{\"" D_CMND_MQTTHOST "\":\"%s%s\",\"" D_CMND_MQTTPORT "\":%d,\"" D_CMND_MQTTCLIENT D_JSON_MASK "\":\"%s\",\"" D_CMND_MQTTCLIENT "\":\"%s\",\"" D_JSON_MQTT_COUNT "\":%d,\"MAX_PACKET_SIZE\":%d,\"KEEPALIVE\":%d}}"), + Settings.mqtt_user, Settings.mqtt_host, Settings.mqtt_port, Settings.mqtt_client, mqtt_client, MqttConnectCount(), MQTT_MAX_PACKET_SIZE, MQTT_KEEPALIVE); +#else + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS6_MQTT "\":{\"" D_CMND_MQTTHOST "\":\"%s\",\"" D_CMND_MQTTPORT "\":%d,\"" D_CMND_MQTTCLIENT D_JSON_MASK "\":\"%s\",\"" D_CMND_MQTTCLIENT "\":\"%s\",\"" D_CMND_MQTTUSER "\":\"%s\",\"" D_JSON_MQTT_COUNT "\":%d,\"MAX_PACKET_SIZE\":%d,\"KEEPALIVE\":%d}}"), Settings.mqtt_host, Settings.mqtt_port, Settings.mqtt_client, mqtt_client, Settings.mqtt_user, MqttConnectCount(), MQTT_MAX_PACKET_SIZE, MQTT_KEEPALIVE); +#endif MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "6")); } @@ -1693,10 +1878,10 @@ void PublishStatus(uint8_t payload) snprintf_P(stemp, sizeof(stemp), PSTR("\"%s\"" ), GetTimeZone().c_str()); } #if defined(USE_TIMERS) && defined(USE_SUNRISE) - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_STATUS D_STATUS7_TIME "\":{\"" D_JSON_UTC_TIME "\":\"%s\",\"" D_JSON_LOCAL_TIME "\":\"%s\",\"" D_JSON_STARTDST "\":\"%s\",\"" D_JSON_ENDDST "\":\"%s\",\"" D_CMND_TIMEZONE "\":%s,\"" D_JSON_SUNRISE "\":\"%s\",\"" D_JSON_SUNSET "\":\"%s\"}}"), + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS7_TIME "\":{\"" D_JSON_UTC_TIME "\":\"%s\",\"" D_JSON_LOCAL_TIME "\":\"%s\",\"" D_JSON_STARTDST "\":\"%s\",\"" D_JSON_ENDDST "\":\"%s\",\"" D_CMND_TIMEZONE "\":%s,\"" D_JSON_SUNRISE "\":\"%s\",\"" D_JSON_SUNSET "\":\"%s\"}}"), GetTime(0).c_str(), GetTime(1).c_str(), GetTime(2).c_str(), GetTime(3).c_str(), stemp, GetSun(0).c_str(), GetSun(1).c_str()); #else - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_STATUS D_STATUS7_TIME "\":{\"" D_JSON_UTC_TIME "\":\"%s\",\"" D_JSON_LOCAL_TIME "\":\"%s\",\"" D_JSON_STARTDST "\":\"%s\",\"" D_JSON_ENDDST "\":\"%s\",\"" D_CMND_TIMEZONE "\":%s}}"), + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS7_TIME "\":{\"" D_JSON_UTC_TIME "\":\"%s\",\"" D_JSON_LOCAL_TIME "\":\"%s\",\"" D_JSON_STARTDST "\":\"%s\",\"" D_JSON_ENDDST "\":\"%s\",\"" D_CMND_TIMEZONE "\":%s}}"), GetTime(0).c_str(), GetTime(1).c_str(), GetTime(2).c_str(), GetTime(3).c_str(), stemp); #endif // USE_TIMERS and USE_SUNRISE MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "7")); @@ -1704,16 +1889,16 @@ void PublishStatus(uint8_t payload) if (energy_flg) { if ((0 == payload) || (9 == payload)) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_STATUS D_STATUS9_MARGIN "\":{\"" D_CMND_POWERDELTA "\":%d,\"" D_CMND_POWERLOW "\":%d,\"" D_CMND_POWERHIGH "\":%d,\"" D_CMND_VOLTAGELOW "\":%d,\"" D_CMND_VOLTAGEHIGH "\":%d,\"" D_CMND_CURRENTLOW "\":%d,\"" D_CMND_CURRENTHIGH "\":%d}}"), + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS9_MARGIN "\":{\"" D_CMND_POWERDELTA "\":%d,\"" D_CMND_POWERLOW "\":%d,\"" D_CMND_POWERHIGH "\":%d,\"" D_CMND_VOLTAGELOW "\":%d,\"" D_CMND_VOLTAGEHIGH "\":%d,\"" D_CMND_CURRENTLOW "\":%d,\"" D_CMND_CURRENTHIGH "\":%d}}"), Settings.energy_power_delta, Settings.energy_min_power, Settings.energy_max_power, Settings.energy_min_voltage, Settings.energy_max_voltage, Settings.energy_min_current, Settings.energy_max_current); MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "9")); } } if ((0 == payload) || (8 == payload) || (10 == payload)) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_STATUS D_STATUS10_SENSOR "\":")); + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS10_SENSOR "\":")); MqttShowSensor(); - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s}"), mqtt_data); + ResponseJsonEnd(); if (8 == payload) { MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "8")); } else { @@ -1722,9 +1907,9 @@ void PublishStatus(uint8_t payload) } if ((0 == payload) || (11 == payload)) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_STATUS D_STATUS11_STATUS "\":")); + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS11_STATUS "\":")); MqttShowState(); - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s}"), mqtt_data); + ResponseJsonEnd(); MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "11")); } @@ -1732,77 +1917,91 @@ void PublishStatus(uint8_t payload) void MqttShowPWMState(void) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s\"" D_CMND_PWM "\":{"), mqtt_data); + ResponseAppend_P(PSTR("\"" D_CMND_PWM "\":{")); bool first = true; - for (uint8_t i = 0; i < MAX_PWMS; i++) { + for (uint32_t i = 0; i < MAX_PWMS; i++) { if (pin[GPIO_PWM1 + i] < 99) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s%s\"" D_CMND_PWM "%d\":%d"), mqtt_data, first ? "" : ",", i+1, Settings.pwm_value[i]); + ResponseAppend_P(PSTR("%s\"" D_CMND_PWM "%d\":%d"), first ? "" : ",", i+1, Settings.pwm_value[i]); first = false; } } - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s}"), mqtt_data); + ResponseJsonEnd(); } void MqttShowState(void) { char stemp1[33]; - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s{\"" D_JSON_TIME "\":\"%s\",\"" D_JSON_UPTIME "\":\"%s\""), mqtt_data, GetDateAndTime(DT_LOCAL).c_str(), GetUptime().c_str()); + ResponseAppend_P(PSTR("{\"" D_JSON_TIME "\":\"%s\",\"" D_JSON_UPTIME "\":\"%s\""), GetDateAndTime(DT_LOCAL).c_str(), GetUptime().c_str()); #ifdef USE_ADC_VCC dtostrfd((double)ESP.getVcc()/1000, 3, stemp1); - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"" D_JSON_VCC "\":%s"), mqtt_data, stemp1); + ResponseAppend_P(PSTR(",\"" D_JSON_VCC "\":%s"), stemp1); #endif - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"SleepMode\":\"%s\",\"Sleep\":%u,\"LoadAvg\":%u"), - mqtt_data, GetTextIndexed(stemp1, sizeof(stemp1), Settings.flag3.sleep_normal, kSleepMode), sleep, loop_load_avg); + ResponseAppend_P(PSTR(",\"" D_JSON_HEAPSIZE "\":%d,\"SleepMode\":\"%s\",\"Sleep\":%u,\"LoadAvg\":%u"), + ESP.getFreeHeap()/1024, GetTextIndexed(stemp1, sizeof(stemp1), Settings.flag3.sleep_normal, kSleepMode), sleep, loop_load_avg); - for (uint8_t i = 0; i < devices_present; i++) { + for (uint32_t i = 0; i < devices_present; i++) { +#ifdef USE_LIGHT if (i == light_device -1) { LightState(1); } else { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"%s\":\"%s\""), mqtt_data, GetPowerDevice(stemp1, i +1, sizeof(stemp1), Settings.flag.device_index_enable), GetStateText(bitRead(power, i))); +#endif + ResponseAppend_P(PSTR(",\"%s\":\"%s\""), GetPowerDevice(stemp1, i +1, sizeof(stemp1), Settings.flag.device_index_enable), GetStateText(bitRead(power, i))); if (SONOFF_IFAN02 == my_module_type) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"" D_CMND_FANSPEED "\":%d"), mqtt_data, GetFanspeed()); + ResponseAppend_P(PSTR(",\"" D_CMND_FANSPEED "\":%d"), GetFanspeed()); break; } +#ifdef USE_LIGHT } +#endif } if (pwm_present) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,"), mqtt_data); + ResponseAppend_P(PSTR(",")); MqttShowPWMState(); } - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"" D_JSON_WIFI "\":{\"" D_JSON_AP "\":%d,\"" D_JSON_SSID "\":\"%s\",\"" D_JSON_BSSID "\":\"%s\",\"" D_JSON_CHANNEL "\":%d,\"" D_JSON_RSSI "\":%d,\"" D_JSON_LINK_COUNT "\":%d,\"" D_JSON_DOWNTIME "\":\"%s\"}}"), - mqtt_data, Settings.sta_active +1, Settings.sta_ssid[Settings.sta_active], WiFi.BSSIDstr().c_str(), WiFi.channel(), WifiGetRssiAsQuality(WiFi.RSSI()), WifiLinkCount(), WifiDowntime().c_str()); + ResponseAppend_P(PSTR(",\"" D_JSON_WIFI "\":{\"" D_JSON_AP "\":%d,\"" D_JSON_SSID "\":\"%s\",\"" D_JSON_BSSID "\":\"%s\",\"" D_JSON_CHANNEL "\":%d,\"" D_JSON_RSSI "\":%d,\"" D_JSON_LINK_COUNT "\":%d,\"" D_JSON_DOWNTIME "\":\"%s\"}}"), + Settings.sta_active +1, Settings.sta_ssid[Settings.sta_active], WiFi.BSSIDstr().c_str(), WiFi.channel(), WifiGetRssiAsQuality(WiFi.RSSI()), WifiLinkCount(), WifiDowntime().c_str()); +} + +void MqttPublishTeleState(void) +{ + mqtt_data[0] = '\0'; + MqttShowState(); + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_STATE), MQTT_TELE_RETAIN); +#ifdef USE_SCRIPT + RulesTeleperiod(); // Allow rule based HA messages +#endif // USE_SCRIPT } bool MqttShowSensor(void) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s{\"" D_JSON_TIME "\":\"%s\""), mqtt_data, GetDateAndTime(DT_LOCAL).c_str()); + ResponseAppend_P(PSTR("{\"" D_JSON_TIME "\":\"%s\""), GetDateAndTime(DT_LOCAL).c_str()); int json_data_start = strlen(mqtt_data); - for (uint8_t i = 0; i < MAX_SWITCHES; i++) { + for (uint32_t i = 0; i < MAX_SWITCHES; i++) { #ifdef USE_TM1638 if ((pin[GPIO_SWT1 +i] < 99) || ((pin[GPIO_TM16CLK] < 99) && (pin[GPIO_TM16DIO] < 99) && (pin[GPIO_TM16STB] < 99))) { #else if (pin[GPIO_SWT1 +i] < 99) { #endif // USE_TM1638 bool swm = ((FOLLOW_INV == Settings.switchmode[i]) || (PUSHBUTTON_INV == Settings.switchmode[i]) || (PUSHBUTTONHOLD_INV == Settings.switchmode[i])); - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"" D_JSON_SWITCH "%d\":\"%s\""), mqtt_data, i +1, GetStateText(swm ^ SwitchLastState(i))); + ResponseAppend_P(PSTR(",\"" D_JSON_SWITCH "%d\":\"%s\""), i +1, GetStateText(swm ^ SwitchLastState(i))); } } XsnsCall(FUNC_JSON_APPEND); bool json_data_available = (strlen(mqtt_data) - json_data_start); - if (strstr_P(mqtt_data, PSTR(D_JSON_PRESSURE))) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"" D_JSON_PRESSURE_UNIT "\":\"%s\""), mqtt_data, PressureUnit().c_str()); + if (strstr_P(mqtt_data, PSTR(D_JSON_PRESSURE)) != nullptr) { + ResponseAppend_P(PSTR(",\"" D_JSON_PRESSURE_UNIT "\":\"%s\""), PressureUnit().c_str()); } - if (strstr_P(mqtt_data, PSTR(D_JSON_TEMPERATURE))) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"" D_JSON_TEMPERATURE_UNIT "\":\"%c\""), mqtt_data, TempUnit()); + if (strstr_P(mqtt_data, PSTR(D_JSON_TEMPERATURE)) != nullptr) { + ResponseAppend_P(PSTR(",\"" D_JSON_TEMPERATURE_UNIT "\":\"%c\""), TempUnit()); } - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s}"), mqtt_data); + ResponseJsonEnd(); - if (json_data_available) XdrvCall(FUNC_SHOW_SENSOR); + if (json_data_available) { XdrvCall(FUNC_SHOW_SENSOR); } return json_data_available; } @@ -1812,6 +2011,13 @@ void PerformEverySecond(void) { uptime++; + if (ntp_synced_message) { + // Moved here to fix syslog UDP exception 9 during RtcSecond + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("NTP: Drift %d, (" D_UTC_TIME ") %s, (" D_DST_TIME ") %s, (" D_STD_TIME ") %s"), + DriftTime(), GetTime(0).c_str(), GetTime(2).c_str(), GetTime(3).c_str()); + ntp_synced_message = false; + } + if (BOOT_LOOP_TIME == uptime) { RtcReboot.fast_reboot_count = 0; RtcRebootSave(); @@ -1855,14 +2061,12 @@ void PerformEverySecond(void) if (tele_period >= Settings.tele_period) { tele_period = 0; - mqtt_data[0] = '\0'; - MqttShowState(); - MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_STATE), MQTT_TELE_RETAIN); + MqttPublishTeleState(); mqtt_data[0] = '\0'; if (MqttShowSensor()) { MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); -#ifdef USE_RULES +#if defined(USE_RULES) || defined(USE_SCRIPT) RulesTeleperiod(); // Allow rule based HA messages #endif // USE_RULES } @@ -1871,13 +2075,14 @@ void PerformEverySecond(void) XdrvCall(FUNC_EVERY_SECOND); XsnsCall(FUNC_EVERY_SECOND); - +/* if ((2 == RtcTime.minute) && latest_uptime_flag) { latest_uptime_flag = false; - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_TIME "\":\"%s\",\"" D_JSON_UPTIME "\":\"%s\"}"), GetDateAndTime(DT_LOCAL).c_str(), GetUptime().c_str()); + Response_P(PSTR("{\"" D_JSON_TIME "\":\"%s\",\"" D_JSON_UPTIME "\":\"%s\"}"), GetDateAndTime(DT_LOCAL).c_str(), GetUptime().c_str()); MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_UPTIME)); } if ((3 == RtcTime.minute) && !latest_uptime_flag) latest_uptime_flag = true; +*/ } /*********************************************************************************************\ @@ -1897,7 +2102,7 @@ void Every100mSeconds(void) if (!latching_relay_pulse) SetLatchingRelay(0, 0); } - for (uint8_t i = 0; i < MAX_PULSETIMERS; i++) { + for (uint32_t i = 0; i < MAX_PULSETIMERS; i++) { if (pulse_timer[i] != 0L) { // Timer active? if (TimeReached(pulse_timer[i])) { // Timer finished? pulse_timer[i] = 0L; // Turn off this timer @@ -1965,8 +2170,6 @@ void Every250mSeconds(void) } } if ((!(Settings.ledstate &0x08)) && ((Settings.ledstate &0x06) || (blinks > 200) || (blinkstate))) { -// if ( (!Settings.flag.global_state && global_state.data) || ((!(Settings.ledstate &0x08)) && ((Settings.ledstate &0x06) || (blinks > 200) || (blinkstate))) ) { -// SetLedPower(blinkstate); // Set led on or off SetLedLink(blinkstate); // Set led on or off } if (!blinkstate) { @@ -1975,7 +2178,7 @@ void Every250mSeconds(void) } } else if (Settings.ledstate &1) { - bool tstate = power; + bool tstate = power & Settings.ledmask; if ((SONOFF_TOUCH == my_module_type) || (SONOFF_T11 == my_module_type) || (SONOFF_T12 == my_module_type) || (SONOFF_T13 == my_module_type)) { tstate = (!power) ? 1 : 0; // As requested invert signal for Touch devices to find them in the dark } @@ -2014,8 +2217,8 @@ void Every250mSeconds(void) #ifndef FIRMWARE_MINIMAL if (RtcSettings.ota_loader) { char *bch = strrchr(mqtt_data, '/'); // Only consider filename after last backslash prevent change of urls having "-" in it - char *pch = strrchr((bch != NULL) ? bch : mqtt_data, '-'); // Change from filename-DE.bin into filename-minimal.bin - char *ech = strrchr((bch != NULL) ? bch : mqtt_data, '.'); // Change from filename.bin into filename-minimal.bin + char *pch = strrchr((bch != nullptr) ? bch : mqtt_data, '-'); // Change from filename-DE.bin into filename-minimal.bin + char *ech = strrchr((bch != nullptr) ? bch : mqtt_data, '.'); // Change from filename.bin into filename-minimal.bin if (!pch) { pch = ech; } if (pch) { mqtt_data[pch - mqtt_data] = '\0'; @@ -2048,9 +2251,9 @@ void Every250mSeconds(void) ota_state_flag = 0; if (ota_result) { // SetFlashModeDout(); // Force DOUT for both ESP8266 and ESP8285 - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR(D_JSON_SUCCESSFUL ". " D_JSON_RESTARTING)); + Response_P(PSTR(D_JSON_SUCCESSFUL ". " D_JSON_RESTARTING)); } else { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR(D_JSON_FAILED " %s"), ESPhttpUpdate.getLastErrorString().c_str()); + Response_P(PSTR(D_JSON_FAILED " %s"), ESPhttpUpdate.getLastErrorString().c_str()); } restart_flag = 2; // Restart anyway to keep memory clean webserver MqttPublishPrefixTopic_P(STAT, PSTR(D_CMND_UPGRADE)); @@ -2058,13 +2261,15 @@ void Every250mSeconds(void) } break; case 1: // Every x.25 second - if (MidnightNow()) { CounterSaveState(); } + if (MidnightNow()) { + XsnsCall(FUNC_SAVE_AT_MIDNIGHT); + } if (save_data_counter && (backlog_pointer == backlog_index)) { save_data_counter--; if (save_data_counter <= 0) { if (Settings.flag.save_state) { power_t mask = POWER_MASK; - for (uint8_t i = 0; i < MAX_PULSETIMERS; i++) { + for (uint32_t i = 0; i < MAX_PULSETIMERS; i++) { if ((Settings.pulse_timer[i] > 0) && (Settings.pulse_timer[i] < 30)) { // 3 seconds mask &= ~(1 << i); } @@ -2116,7 +2321,9 @@ void Every250mSeconds(void) SettingsDefault(); restart_flag = 2; } - SettingsSaveAll(); + if (2 == restart_flag) { + SettingsSaveAll(); + } restart_flag--; if (restart_flag <= 0) { AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_RESTARTING)); @@ -2251,7 +2458,7 @@ void SerialInput(void) if (serial_in_byte || Settings.flag.mqtt_serial_raw) { // Any char between 1 and 127 or any char (0 - 255) if ((serial_in_byte_counter < INPUT_BUFFER_SIZE -1) && // Add char to string if it still fits and ... ((isprint(serial_in_byte) && (128 == Settings.serial_delimiter)) || // Any char between 32 and 127 - (serial_in_byte != Settings.serial_delimiter) || // Any char between 1 and 127 and not being delimiter + ((serial_in_byte != Settings.serial_delimiter) && (128 != Settings.serial_delimiter)) || // Any char between 1 and 127 and not being delimiter Settings.flag.mqtt_serial_raw)) { // Any char between 0 and 255 serial_in_buffer[serial_in_byte_counter++] = serial_in_byte; serial_polling_window = millis(); @@ -2292,16 +2499,16 @@ void SerialInput(void) if (Settings.flag.mqtt_serial && serial_in_byte_counter && (millis() > (serial_polling_window + SERIAL_POLLING))) { serial_in_buffer[serial_in_byte_counter] = 0; // Serial data completed if (!Settings.flag.mqtt_serial_raw) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_SERIALRECEIVED "\":\"%s\"}"), serial_in_buffer); + Response_P(PSTR("{\"" D_JSON_SERIALRECEIVED "\":\"%s\"}"), serial_in_buffer); } else { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_SERIALRECEIVED "\":\"")); - for (int i = 0; i < serial_in_byte_counter; i++) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s%02x"), mqtt_data, serial_in_buffer[i]); + Response_P(PSTR("{\"" D_JSON_SERIALRECEIVED "\":\"")); + for (uint32_t i = 0; i < serial_in_byte_counter; i++) { + ResponseAppend_P(PSTR("%02x"), serial_in_buffer[i]); } - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s\"}"), mqtt_data); + ResponseAppend_P(PSTR("\"}")); } MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_SERIALRECEIVED)); -// XdrvRulesProcess(); + XdrvRulesProcess(); serial_in_byte_counter = 0; } } @@ -2312,9 +2519,11 @@ void GpioInit(void) { uint8_t mpin; - if ((Settings.module >= MAXMODULE) && (Settings.module < USER_MODULE)) { - Settings.module = MODULE; - Settings.last_module = MODULE; + if (!ValidModule(Settings.module)) { + uint8_t module = MODULE; + if (!ValidModule(MODULE)) { module = SONOFF_BASIC; } + Settings.module = module; + Settings.last_module = module; } SetModuleType(); @@ -2322,7 +2531,7 @@ void GpioInit(void) baudrate = APP_BAUDRATE; } - for (uint8_t i = 0; i < sizeof(Settings.user_template.gp); i++) { + for (uint32_t i = 0; i < sizeof(Settings.user_template.gp); i++) { if ((Settings.user_template.gp.io[i] >= GPIO_SENSOR_END) && (Settings.user_template.gp.io[i] < GPIO_USER)) { Settings.user_template.gp.io[i] = GPIO_USER; // Fix not supported sensor ids in template } @@ -2330,23 +2539,33 @@ void GpioInit(void) myio def_gp; ModuleGpios(&def_gp); - for (uint8_t i = 0; i < sizeof(Settings.my_gp); i++) { + for (uint32_t i = 0; i < sizeof(Settings.my_gp); i++) { if ((Settings.my_gp.io[i] >= GPIO_SENSOR_END) && (Settings.my_gp.io[i] < GPIO_USER)) { Settings.my_gp.io[i] = GPIO_NONE; // Fix not supported sensor ids in module } else if (Settings.my_gp.io[i] > GPIO_NONE) { - my_module.io[i] = Settings.my_gp.io[i]; + my_module.io[i] = Settings.my_gp.io[i]; // Set User selected Module sensors } if ((def_gp.io[i] > GPIO_NONE) && (def_gp.io[i] < GPIO_USER)) { - my_module.io[i] = def_gp.io[i]; + my_module.io[i] = def_gp.io[i]; // Force Template override } } + if ((Settings.my_adc0 >= ADC0_END) && (Settings.my_adc0 < ADC0_USER)) { + Settings.my_adc0 = ADC0_NONE; // Fix not supported sensor ids in module + } + else if (Settings.my_adc0 > ADC0_NONE) { + my_adc0 = Settings.my_adc0; // Set User selected Module sensors + } my_module_flag = ModuleFlag(); + uint8_t template_adc0 = my_module_flag.data &15; + if ((template_adc0 > ADC0_NONE) && (template_adc0 < ADC0_USER)) { + my_adc0 = template_adc0; // Force Template override + } - for (uint16_t i = 0; i < GPIO_MAX; i++) { + for (uint32_t i = 0; i < GPIO_MAX; i++) { pin[i] = 99; } - for (uint8_t i = 0; i < sizeof(my_module.io); i++) { + for (uint32_t i = 0; i < sizeof(my_module.io); i++) { mpin = ValidPin(i, my_module.io[i]); // AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DBG: gpio pin %d, mpin %d"), i, mpin); @@ -2377,6 +2596,10 @@ void GpioInit(void) bitSet(led_inverted, mpin - GPIO_LED1_INV); mpin -= (GPIO_LED1_INV - GPIO_LED1); } + else if (mpin == GPIO_LEDLNK_INV) { + ledlnk_inverted = 1; + mpin -= (GPIO_LEDLNK_INV - GPIO_LEDLNK); + } else if ((mpin >= GPIO_PWM1_INV) && (mpin < (GPIO_PWM1_INV + MAX_PWMS))) { bitSet(pwm_inverted, mpin - GPIO_PWM1_INV); mpin -= (GPIO_PWM1_INV - GPIO_PWM1); @@ -2407,7 +2630,7 @@ void GpioInit(void) #ifdef USE_SPI spi_flg = ((((pin[GPIO_SPI_CS] < 99) && (pin[GPIO_SPI_CS] > 14)) || (pin[GPIO_SPI_CS] < 12)) || (((pin[GPIO_SPI_DC] < 99) && (pin[GPIO_SPI_DC] > 14)) || (pin[GPIO_SPI_DC] < 12))); if (spi_flg) { - for (uint16_t i = 0; i < GPIO_MAX; i++) { + for (uint32_t i = 0; i < GPIO_MAX; i++) { if ((pin[i] >= 12) && (pin[i] <=14)) pin[i] = 99; } my_module.io[12] = GPIO_SPI_MISO; @@ -2428,11 +2651,13 @@ void GpioInit(void) devices_present = 1; light_type = LT_BASIC; // Use basic PWM control if SetOption15 = 0 +#ifdef USE_LIGHT if (Settings.flag.pwm_control) { - for (uint8_t i = 0; i < MAX_PWMS; i++) { + for (uint32_t i = 0; i < MAX_PWMS; i++) { if (pin[GPIO_PWM1 +i] < 99) { light_type++; } // Use Dimmer/Color control for all PWM as SetOption15 = 1 } } +#endif // USE_LIGHT if (SONOFF_BRIDGE == my_module_type) { Settings.flag.mqtt_serial = 0; @@ -2460,6 +2685,7 @@ void GpioInit(void) devices_present = 0; baudrate = 19200; } +#ifdef USE_LIGHT else if (SONOFF_BN == my_module_type) { // PWM Single color led (White) light_type = LT_PWM1; } @@ -2472,9 +2698,10 @@ void GpioInit(void) else if (SONOFF_B1 == my_module_type) { // RGBWC led light_type = LT_RGBWC; } +#endif // USE_LIGHT else { if (!light_type) { devices_present = 0; } - for (uint8_t i = 0; i < MAX_RELAYS; i++) { + for (uint32_t i = 0; i < MAX_RELAYS; i++) { if (pin[GPIO_REL1 +i] < 99) { pinMode(pin[GPIO_REL1 +i], OUTPUT); devices_present++; @@ -2486,17 +2713,34 @@ void GpioInit(void) } } - for (uint8_t i = 0; i < MAX_LEDS; i++) { + for (uint32_t i = 0; i < MAX_LEDS; i++) { if (pin[GPIO_LED1 +i] < 99) { - pinMode(pin[GPIO_LED1 +i], OUTPUT); - digitalWrite(pin[GPIO_LED1 +i], bitRead(led_inverted, i)); +#ifdef USE_ARILUX_RF + if ((3 == i) && (leds_present < 2) && (99 == pin[GPIO_ARIRFSEL])) { + pin[GPIO_ARIRFSEL] = pin[GPIO_LED4]; // Legacy support where LED4 was Arilux RF enable + pin[GPIO_LED4] = 99; + } else { +#endif + pinMode(pin[GPIO_LED1 +i], OUTPUT); + leds_present++; + digitalWrite(pin[GPIO_LED1 +i], bitRead(led_inverted, i)); +#ifdef USE_ARILUX_RF + } +#endif } } + if (pin[GPIO_LEDLNK] < 99) { + pinMode(pin[GPIO_LEDLNK], OUTPUT); + digitalWrite(pin[GPIO_LEDLNK], ledlnk_inverted); + } ButtonInit(); SwitchInit(); +#ifdef ROTARY_V1 RotaryInit(); +#endif +#ifdef USE_LIGHT #ifdef USE_WS2812 if (!light_type && (pin[GPIO_WS2812] < 99)) { // RGB led devices_present++; @@ -2508,9 +2752,10 @@ void GpioInit(void) light_type += 3; light_type |= LT_SM16716; } -#endif // ifdef USE_SM16716 +#endif // USE_SM16716 +#endif // USE_LIGHT if (!light_type) { - for (uint8_t i = 0; i < MAX_PWMS; i++) { // Basic PWM control only + for (uint32_t i = 0; i < MAX_PWMS; i++) { // Basic PWM control only if (pin[GPIO_PWM1 +i] < 99) { pwm_present = true; pinMode(pin[GPIO_PWM1 +i], OUTPUT); @@ -2531,6 +2776,8 @@ extern struct rst_info resetInfo; void setup(void) { + global_state.data = 3; // Init global state (wifi_down, mqtt_down) to solve possible network issues + RtcRebootLoad(); if (!RtcRebootValid()) { RtcReboot.fast_reboot_count = 0; } RtcReboot.fast_reboot_count++; @@ -2569,6 +2816,13 @@ void setup(void) sleep = Settings.sleep; #ifndef USE_EMULATION Settings.flag2.emulation = 0; +#else +#ifndef USE_EMULATION_WEMO + if (EMUL_WEMO == Settings.flag2.emulation) { Settings.flag2.emulation = 0; } +#endif +#ifndef USE_EMULATION_HUE + if (EMUL_HUE == Settings.flag2.emulation) { Settings.flag2.emulation = 0; } +#endif #endif // USE_EMULATION if (Settings.param[P_BOOT_LOOP_OFFSET]) { @@ -2576,7 +2830,7 @@ void setup(void) if (RtcReboot.fast_reboot_count > Settings.param[P_BOOT_LOOP_OFFSET]) { // Restart twice Settings.flag3.user_esp8285_enable = 0; // Disable ESP8285 Generic GPIOs interfering with flash SPI if (RtcReboot.fast_reboot_count > Settings.param[P_BOOT_LOOP_OFFSET] +1) { // Restart 3 times - for (uint8_t i = 0; i < MAX_RULE_SETS; i++) { + for (uint32_t i = 0; i < MAX_RULE_SETS; i++) { if (bitRead(Settings.rule_stop, i)) { bitWrite(Settings.rule_enabled, i, 0); // Disable rules causing boot loop } @@ -2586,9 +2840,10 @@ void setup(void) Settings.rule_enabled = 0; // Disable all rules } if (RtcReboot.fast_reboot_count > Settings.param[P_BOOT_LOOP_OFFSET] +3) { // Restarted 5 times - for (uint8_t i = 0; i < sizeof(Settings.my_gp); i++) { + for (uint32_t i = 0; i < sizeof(Settings.my_gp); i++) { Settings.my_gp.io[i] = GPIO_NONE; // Reset user defined GPIO disabling sensors } + Settings.my_adc0 = ADC0_NONE; // Reset user defined ADC0 disabling sensors } if (RtcReboot.fast_reboot_count > Settings.param[P_BOOT_LOOP_OFFSET] +4) { // Restarted 6 times Settings.module = SONOFF_BASIC; // Reset module to Sonoff Basic @@ -2600,7 +2855,7 @@ void setup(void) Format(mqtt_client, Settings.mqtt_client, sizeof(mqtt_client)); Format(mqtt_topic, Settings.mqtt_topic, sizeof(mqtt_topic)); - if (strstr(Settings.hostname, "%")) { + if (strstr(Settings.hostname, "%") != nullptr) { strlcpy(Settings.hostname, WIFI_HOSTNAME, sizeof(Settings.hostname)); snprintf_P(my_hostname, sizeof(my_hostname)-1, Settings.hostname, mqtt_topic, ESP.getChipId() & 0x1FFF); } else { @@ -2650,9 +2905,11 @@ void setup(void) } // Issue #526 and #909 - for (uint8_t i = 0; i < devices_present; i++) { - if ((i < MAX_RELAYS) && (pin[GPIO_REL1 +i] < 99)) { - bitWrite(power, i, digitalRead(pin[GPIO_REL1 +i]) ^ bitRead(rel_inverted, i)); + for (uint32_t i = 0; i < devices_present; i++) { + if (!Settings.flag3.no_power_feedback) { // #5594 and #5663 + if ((i < MAX_RELAYS) && (pin[GPIO_REL1 +i] < 99)) { + bitWrite(power, i, digitalRead(pin[GPIO_REL1 +i]) ^ bitRead(rel_inverted, i)); + } } if ((i < MAX_PULSETIMERS) && (bitRead(power, i) || (POWER_ALL_OFF_PULSETIME_ON == Settings.poweronstate))) { SetPulseTimer(i, Settings.pulse_timer[i]); @@ -2688,7 +2945,9 @@ void loop(void) ButtonLoop(); SwitchLoop(); +#ifdef ROTARY_V1 RotaryLoop(); +#endif if (TimeReached(state_50msecond)) { SetNextTimeInterval(state_50msecond, 50); diff --git a/sonoff/sonoff_aws_iot.cpp b/sonoff/sonoff_aws_iot.cpp new file mode 100644 index 000000000..8f44faf20 --- /dev/null +++ b/sonoff/sonoff_aws_iot.cpp @@ -0,0 +1,152 @@ +/* + sonoff_aws_iot.cpp - TLS AWS IoT for Sonoff-Tasmota - Private key + + Copyright (C) 2019 Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "my_user_config.h" +#ifdef USE_MQTT_AWS_IOT + +#include +#include + +// nasty hack to force PROGMEM +#define static static PROGMEM + +namespace aws_iot_privkey { +/*********************************************************************************************\ + * Private key for AWS IoT + * + * Create the private key, generate the CSR and sign it with AWS IoT Console. + * + * Then generate C version of Private Key and Certificate, cut and paste below + * + * Downloaded from https://www.identrust.com/support/downloads +\*********************************************************************************************/ + +/*********************************************************************************************\ + * Export Private Key as a C structure + * + * $ bearssl skey -C +\*********************************************************************************************/ + +/* --------------- CUT AND PASTE PRIVATE KEY BELOW --------------- */ +/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */ + +static const unsigned char EC_X[] = { + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77 +}; + +static const br_ec_private_key EC = { + 23, + (unsigned char *)EC_X, sizeof EC_X +}; + +/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */ +/* --------------- CUT AND PASTE PRIVATE KEY ABOVE --------------- */ + +/*********************************************************************************************\ + * Export Private Key as a C structure + * + * $ bearssl chain +\*********************************************************************************************/ + +/* --------------- CUT AND PASTE PRIVATE KEY BELOW --------------- */ +/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */ + +static const unsigned char CERT0[] = { + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77 +}; + +static const br_x509_certificate CHAIN[] = { + { (unsigned char *)CERT0, sizeof CERT0 } +}; + +#define CHAIN_LEN 1 + +/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */ +/* --------------- CUT AND PASTE PRIVATE KEY ABOVE --------------- */ + +const br_ec_private_key *AWS_IoT_Private_Key = &EC; +const br_x509_certificate *AWS_IoT_Client_Certificate = &CHAIN[0]; + +} + +#endif // USE_MQTT_AWS_IOT diff --git a/sonoff/sonoff_ca.ino b/sonoff/sonoff_ca.ino new file mode 100644 index 000000000..ed3998443 --- /dev/null +++ b/sonoff/sonoff_ca.ino @@ -0,0 +1,159 @@ +/* + sonoff_ca.ino - Certificate authorities for Sonoff-Tasmota, LetsEncrypt and AWS + + Copyright (C) 2019 Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +// The below is currenlty not used, CA validation takes too much memory and compute time. +// Please use fingerprint validation instead +// However, the CA are available below for future use if it appears to be useful + +#ifdef USE_MQTT_TLS_CA_CERT + +#ifndef USE_MQTT_AWS_IOT +/*********************************************************************************************\ + * LetsEncrypt IdenTrust DST Root CA X3 certificate, RSA 2048 bits SHA 256, valid until 20210417 + * + * https://letsencrypt.org/certificates/ + * Downloaded from https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.pem.txt + * + * to convert do: ‘bearssl ta lets-encrypt-x3-cross-signed.pem.txt’ + * then copy and paste below, chain the generic names to the same as below + * remove ‘static’ and add ‘PROGMEM’ +\*********************************************************************************************/ + +static const unsigned char PROGMEM TA0_DN[] = { + 0x30, 0x4A, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0A, + 0x13, 0x0D, 0x4C, 0x65, 0x74, 0x27, 0x73, 0x20, 0x45, 0x6E, 0x63, 0x72, + 0x79, 0x70, 0x74, 0x31, 0x23, 0x30, 0x21, 0x06, 0x03, 0x55, 0x04, 0x03, + 0x13, 0x1A, 0x4C, 0x65, 0x74, 0x27, 0x73, 0x20, 0x45, 0x6E, 0x63, 0x72, + 0x79, 0x70, 0x74, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6F, 0x72, 0x69, 0x74, + 0x79, 0x20, 0x58, 0x33 +}; + +static const unsigned char PROGMEM TA0_RSA_N[] = { + 0x9C, 0xD3, 0x0C, 0xF0, 0x5A, 0xE5, 0x2E, 0x47, 0xB7, 0x72, 0x5D, 0x37, + 0x83, 0xB3, 0x68, 0x63, 0x30, 0xEA, 0xD7, 0x35, 0x26, 0x19, 0x25, 0xE1, + 0xBD, 0xBE, 0x35, 0xF1, 0x70, 0x92, 0x2F, 0xB7, 0xB8, 0x4B, 0x41, 0x05, + 0xAB, 0xA9, 0x9E, 0x35, 0x08, 0x58, 0xEC, 0xB1, 0x2A, 0xC4, 0x68, 0x87, + 0x0B, 0xA3, 0xE3, 0x75, 0xE4, 0xE6, 0xF3, 0xA7, 0x62, 0x71, 0xBA, 0x79, + 0x81, 0x60, 0x1F, 0xD7, 0x91, 0x9A, 0x9F, 0xF3, 0xD0, 0x78, 0x67, 0x71, + 0xC8, 0x69, 0x0E, 0x95, 0x91, 0xCF, 0xFE, 0xE6, 0x99, 0xE9, 0x60, 0x3C, + 0x48, 0xCC, 0x7E, 0xCA, 0x4D, 0x77, 0x12, 0x24, 0x9D, 0x47, 0x1B, 0x5A, + 0xEB, 0xB9, 0xEC, 0x1E, 0x37, 0x00, 0x1C, 0x9C, 0xAC, 0x7B, 0xA7, 0x05, + 0xEA, 0xCE, 0x4A, 0xEB, 0xBD, 0x41, 0xE5, 0x36, 0x98, 0xB9, 0xCB, 0xFD, + 0x6D, 0x3C, 0x96, 0x68, 0xDF, 0x23, 0x2A, 0x42, 0x90, 0x0C, 0x86, 0x74, + 0x67, 0xC8, 0x7F, 0xA5, 0x9A, 0xB8, 0x52, 0x61, 0x14, 0x13, 0x3F, 0x65, + 0xE9, 0x82, 0x87, 0xCB, 0xDB, 0xFA, 0x0E, 0x56, 0xF6, 0x86, 0x89, 0xF3, + 0x85, 0x3F, 0x97, 0x86, 0xAF, 0xB0, 0xDC, 0x1A, 0xEF, 0x6B, 0x0D, 0x95, + 0x16, 0x7D, 0xC4, 0x2B, 0xA0, 0x65, 0xB2, 0x99, 0x04, 0x36, 0x75, 0x80, + 0x6B, 0xAC, 0x4A, 0xF3, 0x1B, 0x90, 0x49, 0x78, 0x2F, 0xA2, 0x96, 0x4F, + 0x2A, 0x20, 0x25, 0x29, 0x04, 0xC6, 0x74, 0xC0, 0xD0, 0x31, 0xCD, 0x8F, + 0x31, 0x38, 0x95, 0x16, 0xBA, 0xA8, 0x33, 0xB8, 0x43, 0xF1, 0xB1, 0x1F, + 0xC3, 0x30, 0x7F, 0xA2, 0x79, 0x31, 0x13, 0x3D, 0x2D, 0x36, 0xF8, 0xE3, + 0xFC, 0xF2, 0x33, 0x6A, 0xB9, 0x39, 0x31, 0xC5, 0xAF, 0xC4, 0x8D, 0x0D, + 0x1D, 0x64, 0x16, 0x33, 0xAA, 0xFA, 0x84, 0x29, 0xB6, 0xD4, 0x0B, 0xC0, + 0xD8, 0x7D, 0xC3, 0x93 +}; + +static const unsigned char TA0_RSA_E[] = { + 0x01, 0x00, 0x01 +}; + +static const br_x509_trust_anchor PROGMEM LetsEncryptX3CrossSigned_TA = { + { (unsigned char *)TA0_DN, sizeof TA0_DN }, + BR_X509_TA_CA, + { + BR_KEYTYPE_RSA, + { .rsa = { + (unsigned char *)TA0_RSA_N, sizeof TA0_RSA_N, + (unsigned char *)TA0_RSA_E, sizeof TA0_RSA_E, + } } + } +}; + +#define TAs_NUM 1 + +#endif // not USE_MQTT_AWS_IOT + +#ifdef USE_MQTT_AWS_IOT +/*********************************************************************************************\ + * Amazon Root CA, RSA 2048 bits SHA 256, valid until 20380117 + * + * https://letsencrypt.org/certificates/ + * Downloaded from https://www.amazontrust.com/repository/AmazonRootCA1.pem + * + * to convert do: ‘bearssl ta AmazonRootCA1.pem’ + * then copy and paste below, chain the generic names to the same as below + * remove ‘static’ and add ‘PROGMEM’ +\*********************************************************************************************/ + + +const unsigned char PROGMEM TA0_DN[] = { + 0x30, 0x39, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x55, 0x53, 0x31, 0x0F, 0x30, 0x0D, 0x06, 0x03, 0x55, 0x04, 0x0A, + 0x13, 0x06, 0x41, 0x6D, 0x61, 0x7A, 0x6F, 0x6E, 0x31, 0x19, 0x30, 0x17, + 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x10, 0x41, 0x6D, 0x61, 0x7A, 0x6F, + 0x6E, 0x20, 0x52, 0x6F, 0x6F, 0x74, 0x20, 0x43, 0x41, 0x20, 0x31 +}; + +const unsigned char PROGMEM TA0_RSA_N[] = { + 0xB2, 0x78, 0x80, 0x71, 0xCA, 0x78, 0xD5, 0xE3, 0x71, 0xAF, 0x47, 0x80, + 0x50, 0x74, 0x7D, 0x6E, 0xD8, 0xD7, 0x88, 0x76, 0xF4, 0x99, 0x68, 0xF7, + 0x58, 0x21, 0x60, 0xF9, 0x74, 0x84, 0x01, 0x2F, 0xAC, 0x02, 0x2D, 0x86, + 0xD3, 0xA0, 0x43, 0x7A, 0x4E, 0xB2, 0xA4, 0xD0, 0x36, 0xBA, 0x01, 0xBE, + 0x8D, 0xDB, 0x48, 0xC8, 0x07, 0x17, 0x36, 0x4C, 0xF4, 0xEE, 0x88, 0x23, + 0xC7, 0x3E, 0xEB, 0x37, 0xF5, 0xB5, 0x19, 0xF8, 0x49, 0x68, 0xB0, 0xDE, + 0xD7, 0xB9, 0x76, 0x38, 0x1D, 0x61, 0x9E, 0xA4, 0xFE, 0x82, 0x36, 0xA5, + 0xE5, 0x4A, 0x56, 0xE4, 0x45, 0xE1, 0xF9, 0xFD, 0xB4, 0x16, 0xFA, 0x74, + 0xDA, 0x9C, 0x9B, 0x35, 0x39, 0x2F, 0xFA, 0xB0, 0x20, 0x50, 0x06, 0x6C, + 0x7A, 0xD0, 0x80, 0xB2, 0xA6, 0xF9, 0xAF, 0xEC, 0x47, 0x19, 0x8F, 0x50, + 0x38, 0x07, 0xDC, 0xA2, 0x87, 0x39, 0x58, 0xF8, 0xBA, 0xD5, 0xA9, 0xF9, + 0x48, 0x67, 0x30, 0x96, 0xEE, 0x94, 0x78, 0x5E, 0x6F, 0x89, 0xA3, 0x51, + 0xC0, 0x30, 0x86, 0x66, 0xA1, 0x45, 0x66, 0xBA, 0x54, 0xEB, 0xA3, 0xC3, + 0x91, 0xF9, 0x48, 0xDC, 0xFF, 0xD1, 0xE8, 0x30, 0x2D, 0x7D, 0x2D, 0x74, + 0x70, 0x35, 0xD7, 0x88, 0x24, 0xF7, 0x9E, 0xC4, 0x59, 0x6E, 0xBB, 0x73, + 0x87, 0x17, 0xF2, 0x32, 0x46, 0x28, 0xB8, 0x43, 0xFA, 0xB7, 0x1D, 0xAA, + 0xCA, 0xB4, 0xF2, 0x9F, 0x24, 0x0E, 0x2D, 0x4B, 0xF7, 0x71, 0x5C, 0x5E, + 0x69, 0xFF, 0xEA, 0x95, 0x02, 0xCB, 0x38, 0x8A, 0xAE, 0x50, 0x38, 0x6F, + 0xDB, 0xFB, 0x2D, 0x62, 0x1B, 0xC5, 0xC7, 0x1E, 0x54, 0xE1, 0x77, 0xE0, + 0x67, 0xC8, 0x0F, 0x9C, 0x87, 0x23, 0xD6, 0x3F, 0x40, 0x20, 0x7F, 0x20, + 0x80, 0xC4, 0x80, 0x4C, 0x3E, 0x3B, 0x24, 0x26, 0x8E, 0x04, 0xAE, 0x6C, + 0x9A, 0xC8, 0xAA, 0x0D +}; + +static const unsigned char PROGMEM TA0_RSA_E[] = { + 0x01, 0x00, 0x01 +}; + +const br_x509_trust_anchor PROGMEM AmazonRootCA1_TA = { + { (unsigned char *)TA0_DN, sizeof TA0_DN }, + BR_X509_TA_CA, + { + BR_KEYTYPE_RSA, + { .rsa = { + (unsigned char *)TA0_RSA_N, sizeof TA0_RSA_N, + (unsigned char *)TA0_RSA_E, sizeof TA0_RSA_E, + } } + } +}; + +#define TAs_NUM 1 + +#endif // USE_MQTT_AWS_IOT + +#endif // USE_MQTT_TLS_CA_CERT diff --git a/sonoff/sonoff_letsencrypt.h b/sonoff/sonoff_letsencrypt.h deleted file mode 100644 index 24735b2bd..000000000 --- a/sonoff/sonoff_letsencrypt.h +++ /dev/null @@ -1,102 +0,0 @@ -/* - sonoff_letsencrypt.h - TLS Lets Encrypt certificate for Sonoff-Tasmota - - Copyright (C) 2019 Theo Arends - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#ifdef USE_MQTT_TLS_CA_CERT -/*********************************************************************************************\ - * LetsEncrypt IdenTrust DST Root CA X3 certificate valid until 20210930 - * - * https://letsencrypt.org/certificates/ - * Downloaded from https://www.identrust.com/support/downloads -\*********************************************************************************************/ - -#define MQTT_TLS_CA_CERT_LENGTH 846 // Letsencrypt -#define MQTT_TLS_CA_CERT { \ - 0x30, 0x82, 0x03, 0x4a, 0x30, 0x82, 0x02, 0x32, 0xa0, 0x03, 0x02, 0x01, \ - 0x02, 0x02, 0x10, 0x44, 0xaf, 0xb0, 0x80, 0xd6, 0xa3, 0x27, 0xba, 0x89, \ - 0x30, 0x39, 0x86, 0x2e, 0xf8, 0x40, 0x6b, 0x30, 0x0d, 0x06, 0x09, 0x2a, \ - 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x3f, \ - 0x31, 0x24, 0x30, 0x22, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x1b, 0x44, \ - 0x69, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x61, \ - 0x74, 0x75, 0x72, 0x65, 0x20, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x43, \ - 0x6f, 0x2e, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, \ - 0x0e, 0x44, 0x53, 0x54, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41, \ - 0x20, 0x58, 0x33, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x30, 0x30, 0x39, 0x33, \ - 0x30, 0x32, 0x31, 0x31, 0x32, 0x31, 0x39, 0x5a, 0x17, 0x0d, 0x32, 0x31, \ - 0x30, 0x39, 0x33, 0x30, 0x31, 0x34, 0x30, 0x31, 0x31, 0x35, 0x5a, 0x30, \ - 0x3f, 0x31, 0x24, 0x30, 0x22, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x1b, \ - 0x44, 0x69, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x20, 0x53, 0x69, 0x67, 0x6e, \ - 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, \ - 0x43, 0x6f, 0x2e, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x03, \ - 0x13, 0x0e, 0x44, 0x53, 0x54, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, \ - 0x41, 0x20, 0x58, 0x33, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, \ - 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, \ - 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, \ - 0x00, 0xdf, 0xaf, 0xe9, 0x97, 0x50, 0x08, 0x83, 0x57, 0xb4, 0xcc, 0x62, \ - 0x65, 0xf6, 0x90, 0x82, 0xec, 0xc7, 0xd3, 0x2c, 0x6b, 0x30, 0xca, 0x5b, \ - 0xec, 0xd9, 0xc3, 0x7d, 0xc7, 0x40, 0xc1, 0x18, 0x14, 0x8b, 0xe0, 0xe8, \ - 0x33, 0x76, 0x49, 0x2a, 0xe3, 0x3f, 0x21, 0x49, 0x93, 0xac, 0x4e, 0x0e, \ - 0xaf, 0x3e, 0x48, 0xcb, 0x65, 0xee, 0xfc, 0xd3, 0x21, 0x0f, 0x65, 0xd2, \ - 0x2a, 0xd9, 0x32, 0x8f, 0x8c, 0xe5, 0xf7, 0x77, 0xb0, 0x12, 0x7b, 0xb5, \ - 0x95, 0xc0, 0x89, 0xa3, 0xa9, 0xba, 0xed, 0x73, 0x2e, 0x7a, 0x0c, 0x06, \ - 0x32, 0x83, 0xa2, 0x7e, 0x8a, 0x14, 0x30, 0xcd, 0x11, 0xa0, 0xe1, 0x2a, \ - 0x38, 0xb9, 0x79, 0x0a, 0x31, 0xfd, 0x50, 0xbd, 0x80, 0x65, 0xdf, 0xb7, \ - 0x51, 0x63, 0x83, 0xc8, 0xe2, 0x88, 0x61, 0xea, 0x4b, 0x61, 0x81, 0xec, \ - 0x52, 0x6b, 0xb9, 0xa2, 0xe2, 0x4b, 0x1a, 0x28, 0x9f, 0x48, 0xa3, 0x9e, \ - 0x0c, 0xda, 0x09, 0x8e, 0x3e, 0x17, 0x2e, 0x1e, 0xdd, 0x20, 0xdf, 0x5b, \ - 0xc6, 0x2a, 0x8a, 0xab, 0x2e, 0xbd, 0x70, 0xad, 0xc5, 0x0b, 0x1a, 0x25, \ - 0x90, 0x74, 0x72, 0xc5, 0x7b, 0x6a, 0xab, 0x34, 0xd6, 0x30, 0x89, 0xff, \ - 0xe5, 0x68, 0x13, 0x7b, 0x54, 0x0b, 0xc8, 0xd6, 0xae, 0xec, 0x5a, 0x9c, \ - 0x92, 0x1e, 0x3d, 0x64, 0xb3, 0x8c, 0xc6, 0xdf, 0xbf, 0xc9, 0x41, 0x70, \ - 0xec, 0x16, 0x72, 0xd5, 0x26, 0xec, 0x38, 0x55, 0x39, 0x43, 0xd0, 0xfc, \ - 0xfd, 0x18, 0x5c, 0x40, 0xf1, 0x97, 0xeb, 0xd5, 0x9a, 0x9b, 0x8d, 0x1d, \ - 0xba, 0xda, 0x25, 0xb9, 0xc6, 0xd8, 0xdf, 0xc1, 0x15, 0x02, 0x3a, 0xab, \ - 0xda, 0x6e, 0xf1, 0x3e, 0x2e, 0xf5, 0x5c, 0x08, 0x9c, 0x3c, 0xd6, 0x83, \ - 0x69, 0xe4, 0x10, 0x9b, 0x19, 0x2a, 0xb6, 0x29, 0x57, 0xe3, 0xe5, 0x3d, \ - 0x9b, 0x9f, 0xf0, 0x02, 0x5d, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x42, \ - 0x30, 0x40, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, \ - 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0e, 0x06, 0x03, 0x55, \ - 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, \ - 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xc4, 0xa7, \ - 0xb1, 0xa4, 0x7b, 0x2c, 0x71, 0xfa, 0xdb, 0xe1, 0x4b, 0x90, 0x75, 0xff, \ - 0xc4, 0x15, 0x60, 0x85, 0x89, 0x10, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, \ - 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, \ - 0x01, 0x00, 0xa3, 0x1a, 0x2c, 0x9b, 0x17, 0x00, 0x5c, 0xa9, 0x1e, 0xee, \ - 0x28, 0x66, 0x37, 0x3a, 0xbf, 0x83, 0xc7, 0x3f, 0x4b, 0xc3, 0x09, 0xa0, \ - 0x95, 0x20, 0x5d, 0xe3, 0xd9, 0x59, 0x44, 0xd2, 0x3e, 0x0d, 0x3e, 0xbd, \ - 0x8a, 0x4b, 0xa0, 0x74, 0x1f, 0xce, 0x10, 0x82, 0x9c, 0x74, 0x1a, 0x1d, \ - 0x7e, 0x98, 0x1a, 0xdd, 0xcb, 0x13, 0x4b, 0xb3, 0x20, 0x44, 0xe4, 0x91, \ - 0xe9, 0xcc, 0xfc, 0x7d, 0xa5, 0xdb, 0x6a, 0xe5, 0xfe, 0xe6, 0xfd, 0xe0, \ - 0x4e, 0xdd, 0xb7, 0x00, 0x3a, 0xb5, 0x70, 0x49, 0xaf, 0xf2, 0xe5, 0xeb, \ - 0x02, 0xf1, 0xd1, 0x02, 0x8b, 0x19, 0xcb, 0x94, 0x3a, 0x5e, 0x48, 0xc4, \ - 0x18, 0x1e, 0x58, 0x19, 0x5f, 0x1e, 0x02, 0x5a, 0xf0, 0x0c, 0xf1, 0xb1, \ - 0xad, 0xa9, 0xdc, 0x59, 0x86, 0x8b, 0x6e, 0xe9, 0x91, 0xf5, 0x86, 0xca, \ - 0xfa, 0xb9, 0x66, 0x33, 0xaa, 0x59, 0x5b, 0xce, 0xe2, 0xa7, 0x16, 0x73, \ - 0x47, 0xcb, 0x2b, 0xcc, 0x99, 0xb0, 0x37, 0x48, 0xcf, 0xe3, 0x56, 0x4b, \ - 0xf5, 0xcf, 0x0f, 0x0c, 0x72, 0x32, 0x87, 0xc6, 0xf0, 0x44, 0xbb, 0x53, \ - 0x72, 0x6d, 0x43, 0xf5, 0x26, 0x48, 0x9a, 0x52, 0x67, 0xb7, 0x58, 0xab, \ - 0xfe, 0x67, 0x76, 0x71, 0x78, 0xdb, 0x0d, 0xa2, 0x56, 0x14, 0x13, 0x39, \ - 0x24, 0x31, 0x85, 0xa2, 0xa8, 0x02, 0x5a, 0x30, 0x47, 0xe1, 0xdd, 0x50, \ - 0x07, 0xbc, 0x02, 0x09, 0x90, 0x00, 0xeb, 0x64, 0x63, 0x60, 0x9b, 0x16, \ - 0xbc, 0x88, 0xc9, 0x12, 0xe6, 0xd2, 0x7d, 0x91, 0x8b, 0xf9, 0x3d, 0x32, \ - 0x8d, 0x65, 0xb4, 0xe9, 0x7c, 0xb1, 0x57, 0x76, 0xea, 0xc5, 0xb6, 0x28, \ - 0x39, 0xbf, 0x15, 0x65, 0x1c, 0xc8, 0xf6, 0x77, 0x96, 0x6a, 0x0a, 0x8d, \ - 0x77, 0x0b, 0xd8, 0x91, 0x0b, 0x04, 0x8e, 0x07, 0xdb, 0x29, 0xb6, 0x0a, \ - 0xee, 0x9d, 0x82, 0x35, 0x35, 0x10 } - -#endif // USE_MQTT_TLS_CA_CERT diff --git a/sonoff/sonoff_post.h b/sonoff/sonoff_post.h index 095c7e2ac..1f25c308a 100755 --- a/sonoff/sonoff_post.h +++ b/sonoff/sonoff_post.h @@ -46,6 +46,23 @@ void KNX_CB_Action(message_t const &msg, void *arg); * Default global defines \*********************************************************************************************/ +#ifdef USE_EMULATION_HUE +#define USE_EMULATION +#endif +#ifdef USE_EMULATION_WEMO +#define USE_EMULATION +#endif + +#ifdef USE_MQTT_TLS + const uint16_t WEB_LOG_SIZE = 2000; // Max number of characters in weblog +#else + const uint16_t WEB_LOG_SIZE = 4000; // Max number of characters in weblog +#endif + +#if defined(USE_MQTT_TLS) && defined(ARDUINO_ESP8266_RELEASE_2_3_0) + #error "TLS is no more supported on Core 2.3.0, use 2.4.2 or higher." +#endif + #ifndef MODULE #define MODULE SONOFF_BASIC // [Module] Select default model #endif @@ -60,25 +77,27 @@ void KNX_CB_Action(message_t const &msg, void *arg); #undef CODE_IMAGE #define CODE_IMAGE 3 +#define USE_COUNTER // Enable counters #undef USE_ADC_VCC // Add Analog input on selected devices #define USE_DS18x20 // For more than one DS18x20 sensors with id sort, single scan and read retry (+1k3 code) //#define USE_DS18x20_LEGACY // For more than one DS18x20 sensors with dynamic scan using library OneWire (+1k5 code) + #define USE_I2C // I2C using library wire (+10k code, 0k2 mem, 124 iram) #define USE_SHT // Add I2C emulating code for SHT1X sensor (+1k4 code) -#define USE_SHT3X // Add I2C code for SHT3x sensor (+0k6 code) #define USE_HTU // Add I2C code for HTU21/SI7013/SI7020/SI7021 sensor (+1k5 code) -#define USE_LM75AD // Add I2C code for LM75AD sensor (+0k5 code) #define USE_BMP // Add I2C code for BMP085/BMP180/BMP280/BME280 sensor (+4k code) #define USE_BME680 // Add additional support for BME680 sensor using Bosch BME680 library (+4k code) -#define USE_SGP30 // Add I2C code for SGP30 sensor (+1k1 code) #define USE_BH1750 // Add I2C code for BH1750 sensor (+0k5 code) #define USE_VEML6070 // Add I2C code for VEML6070 sensor (+0k5 code) -#define USE_TSL2561 // Add I2C code for TSL2561 sensor using library Adafruit TSL2561 Arduino (+1k2 code) -//#define USE_SI1145 // Add I2C code for SI1145/46/47 sensor (+1k code) #define USE_ADS1115 // Add I2C code for ADS1115 16 bit A/D converter based on Adafruit ADS1x15 library (no library needed) (+0k7 code) //#define USE_ADS1115_I2CDEV // Add I2C code for ADS1115 16 bit A/D converter using library i2cdevlib-Core and i2cdevlib-ADS1115 (+2k code) #define USE_INA219 // Add I2C code for INA219 Low voltage and current sensor (+1k code) +#define USE_SHT3X // Add I2C code for SHT3x sensor (+0k6 code) +#define USE_TSL2561 // Add I2C code for TSL2561 sensor using library Adafruit TSL2561 Arduino (+1k2 code) #define USE_MGS // Add I2C code for Xadow and Grove Mutichannel Gas sensor using library Multichannel_Gas_Sensor (+10k code) +#define USE_SGP30 // Add I2C code for SGP30 sensor (+1k1 code) +//#define USE_SI1145 // Add I2C code for SI1145/46/47 sensor (+1k code) +#define USE_LM75AD // Add I2C code for LM75AD sensor (+0k5 code) //#define USE_APDS9960 // Add I2C code for APDS9960 Proximity Sensor. Disables SHT and VEML6070 (+4k7 code) //#define USE_MCP230xx // Enable MCP23008/MCP23017 - Must define I2C Address in #define USE_MCP230xx_ADDR below - range 0x20 - 0x27 (+4k7 code) // #define USE_MCP230xx_ADDR 0x20 // Enable MCP23008/MCP23017 I2C Address to use (Must be within range 0x20 through 0x27 - set according to your wired setup) @@ -94,6 +113,11 @@ void KNX_CB_Action(message_t const &msg, void *arg); //#define USE_MGC3130 // Enable MGC3130 Electric Field Effect Sensor (I2C address 0x42) (+2k7 code, 0k3 mem) //#define USE_MAX44009 // Enable MAX44009 Ambient Light sensor (I2C addresses 0x4A and 0x4B) (+0k8 code) #define USE_SCD30 // Enable Sensiron SCd30 CO2 sensor (I2C address 0x61) (+3k3 code) +//#define USE_SPS30 // Enable Sensiron SPS30 particle sensor (I2C address 0x69) (+1.7 code) +#define USE_ADE7953 // Enable ADE7953 Energy monitor as used on Shelly 2.5 (I2C address 0x38) (+1k5) +//#define USE_VL53L0X // Enable VL53L0x time of flight sensor (I2C address 0x29) (+4k code) +//#define USE_MLX90614 // Enable MLX90614 ir temp sensor (I2C address 0x5a) (+0.6k code) + #define USE_MHZ19 // Add support for MH-Z19 CO2 sensor (+2k code) #define USE_SENSEAIR // Add support for SenseAir K30, K70 and S8 CO2 sensor (+2k3 code) #ifndef CO2_LOW @@ -113,13 +137,14 @@ void KNX_CB_Action(message_t const &msg, void *arg); #ifndef TUYA_DIMMER_ID #define TUYA_DIMMER_ID 0 // Default dimmer Id #endif -#define USE_PS_16_DZ // Add support for PS-16-DZ Dimmer -//#define USE_AZ7798 // Add support for AZ-Instrument 7798 CO2 datalogger +#define USE_PS_16_DZ // Add support for PS-16-DZ Dimmer and Sonoff L1 (+2k code) +//#define USE_AZ7798 // Add support for AZ-Instrument 7798 CO2 datalogger #define USE_PN532_HSU // Add support for PN532 using HSU (Serial) interface (+1k8 code, 140 bytes mem) #define USE_PZEM004T // Add support for PZEM004T Energy monitor (+2k code) #define USE_PZEM_AC // Add support for PZEM014,016 Energy monitor (+1k1 code) #define USE_PZEM_DC // Add support for PZEM003,017 Energy monitor (+1k1 code) #define USE_MCP39F501 // Add support for MCP39F501 Energy monitor as used in Shelly 2 (+3k1 code) +#define USE_DHT // Add support for DHT11, AM2301 (DHT21, DHT22, AM2302, AM2321) and SI7021 Temperature and Humidity sensor #define USE_MAX31855 // Add support for MAX31855 K-Type thermocouple sensor using softSPI #define USE_IR_REMOTE // Send IR remote commands using library IRremoteESP8266 and ArduinoJson (+4k code, 0k3 mem, 48 iram) #define USE_IR_HVAC // Support for HVAC system using IR (+2k code) @@ -140,6 +165,8 @@ void KNX_CB_Action(message_t const &msg, void *arg); #define USE_RF_SENSOR // Add support for RF sensor receiver (434MHz or 868MHz) (+0k8 code) // #define USE_THEO_V2 // Add support for decoding Theo V2 sensors as documented on https://sidweb.nl using 434MHz RF sensor receiver (+1k4 code) #define USE_ALECTO_V2 // Add support for decoding Alecto V2 sensors like ACH2010, WS3000 and DKW2012 using 868MHz RF sensor receiver (+1k7 code) +#define USE_SM16716 // Add support for SM16716 RGB LED controller (+0k7 code) +#define USE_HRE // Add support for Badger HR-E Water Meter (+1k4 code) #endif // FIRMWARE_SENSORS /*********************************************************************************************\ @@ -152,6 +179,7 @@ void KNX_CB_Action(message_t const &msg, void *arg); #undef CODE_IMAGE #define CODE_IMAGE 2 +#define USE_ADC_VCC // Display Vcc in Power status. Disable for use as Analog input on selected devices #ifndef USE_WPS #define USE_WPS // Add support for WPS as initial wifi configuration tool (+33k code, 1k mem (5k mem with core v2.4.2+)) #endif @@ -167,6 +195,7 @@ void KNX_CB_Action(message_t const &msg, void *arg); #undef USE_TIMERS_WEB // Disable support for timer webpage #undef USE_SUNRISE // Disable support for Sunrise and sunset tools #undef USE_RULES // Disable support for rules +#undef USE_COUNTER // Disable counters #undef USE_I2C // Disable all I2C sensors #undef USE_SPI // Disable all SPI devices #undef USE_MHZ19 // Disable support for MH-Z19 CO2 sensor @@ -179,13 +208,14 @@ void KNX_CB_Action(message_t const &msg, void *arg); #undef USE_MP3_PLAYER // Disable DFPlayer Mini MP3 Player RB-DFR-562 commands: play, volume and stop #undef USE_TUYA_DIMMER // Disable support for Tuya Serial Dimmer #undef USE_ARMTRONIX_DIMMERS // Disable support for Armtronix Dimmers (+1k4 code) -#undef USE_PS_16_DZ // Disable support for PS-16-DZ Dimmer +#undef USE_PS_16_DZ // Disable support for PS-16-DZ Dimmer and Sonoff L1 (+2k code) #undef USE_AZ7798 // Disable support for AZ-Instrument 7798 CO2 datalogger #undef USE_PN532_HSU // Disable support for PN532 using HSU (Serial) interface (+1k8 code, 140 bytes mem) #undef USE_PZEM004T // Disable PZEM004T energy sensor #undef USE_PZEM_AC // Disable PZEM014,016 Energy monitor #undef USE_PZEM_DC // Disable PZEM003,017 Energy monitor #undef USE_MCP39F501 // Disable support for MCP39F501 Energy monitor as used in Shelly 2 (+3k1 code) +#define USE_DHT // Add support for DHT11, AM2301 (DHT21, DHT22, AM2302, AM2321) and SI7021 Temperature and Humidity sensor #undef USE_MAX31855 // Disable MAX31855 K-Type thermocouple sensor using softSPI #undef USE_IR_REMOTE // Disable IR remote commands using library IRremoteESP8266 and ArduinoJson #undef USE_IR_RECEIVE // Disable support for IR receiver @@ -197,6 +227,8 @@ void KNX_CB_Action(message_t const &msg, void *arg); #undef USE_TX20_WIND_SENSOR // Disable support for La Crosse TX20 anemometer #undef USE_RC_SWITCH // Disable support for RF transceiver using library RcSwitch #undef USE_RF_SENSOR // Disable support for RF sensor receiver (434MHz or 868MHz) (+0k8 code) +#undef USE_SM16716 // Disable support for SM16716 RGB LED controller (+0k7 code) +#undef USE_HRE // Disable support for Badger HR-E Water Meter (+1k4 code) #undef DEBUG_THEO // Disable debug code #undef USE_DEBUG_DRIVER // Disable debug code #endif // FIRMWARE_CLASSIC @@ -275,6 +307,7 @@ void KNX_CB_Action(message_t const &msg, void *arg); #undef APP_SLEEP #define APP_SLEEP 1 // Default to sleep = 1 for FIRMWARE_BASIC +#define USE_ADC_VCC // Display Vcc in Power status. Disable for use as Analog input on selected devices //#undef USE_ENERGY_SENSOR // Disable energy sensors #undef USE_ARDUINO_OTA // Disable support for Arduino OTA #undef USE_WPS // Disable support for WPS as initial wifi configuration tool @@ -291,7 +324,7 @@ void KNX_CB_Action(message_t const &msg, void *arg); //#undef USE_TIMERS_WEB // Disable support for timer webpage //#undef USE_SUNRISE // Disable support for Sunrise and sunset tools //#undef USE_RULES // Disable support for rules -#undef USE_DHT // Disable internal DHT sensor +#undef USE_COUNTER // Disable counters #undef USE_DS18x20 // Disable DS18x20 sensor #undef USE_DS18x20_LEGACY // Disable DS18x20 sensor #undef USE_DS18B20 // Disable internal DS18B20 sensor @@ -308,13 +341,14 @@ void KNX_CB_Action(message_t const &msg, void *arg); #undef USE_MP3_PLAYER // Disable DFPlayer Mini MP3 Player RB-DFR-562 commands: play, volume and stop //#undef USE_TUYA_DIMMER // Disable support for Tuya Serial Dimmer #undef USE_ARMTRONIX_DIMMERS // Disable support for Armtronix Dimmers (+1k4 code) -#undef USE_PS_16_DZ // Disable support for PS-16-DZ Dimmer +#undef USE_PS_16_DZ // Disable support for PS-16-DZ Dimmer and Sonoff L1 (+2k code) #undef USE_AZ7798 // Disable support for AZ-Instrument 7798 CO2 datalogger #undef USE_PN532_HSU // Disable support for PN532 using HSU (Serial) interface (+1k8 code, 140 bytes mem) #undef USE_PZEM004T // Disable PZEM004T energy sensor #undef USE_PZEM_AC // Disable PZEM014,016 Energy monitor #undef USE_PZEM_DC // Disable PZEM003,017 Energy monitor //#undef USE_MCP39F501 // Disable MCP39F501 Energy monitor as used in Shelly 2 +#undef USE_DHT // Disable support for DHT11, AM2301 (DHT21, DHT22, AM2302, AM2321) and SI7021 Temperature and Humidity sensor #undef USE_MAX31855 // Disable MAX31855 K-Type thermocouple sensor using softSPI #undef USE_IR_REMOTE // Disable IR driver #undef USE_WS2812 // Disable WS2812 Led string @@ -326,6 +360,8 @@ void KNX_CB_Action(message_t const &msg, void *arg); #undef USE_TX20_WIND_SENSOR // Disable support for La Crosse TX20 anemometer #undef USE_RC_SWITCH // Disable support for RF transceiver using library RcSwitch #undef USE_RF_SENSOR // Disable support for RF sensor receiver (434MHz or 868MHz) (+0k8 code) +#undef USE_SM16716 // Disable support for SM16716 RGB LED controller (+0k7 code) +#undef USE_HRE // Disable support for Badger HR-E Water Meter (+1k4 code) #undef DEBUG_THEO // Disable debug code #undef USE_DEBUG_DRIVER // Disable debug code #endif // FIRMWARE_BASIC @@ -340,6 +376,7 @@ void KNX_CB_Action(message_t const &msg, void *arg); #undef CODE_IMAGE #define CODE_IMAGE 1 +#define USE_ADC_VCC // Display Vcc in Power status. Disable for use as Analog input on selected devices #undef USE_ENERGY_SENSOR // Disable energy sensors #undef USE_ARDUINO_OTA // Disable support for Arduino OTA #undef USE_WPS // Disable support for WPS as initial wifi configuration tool @@ -356,7 +393,9 @@ void KNX_CB_Action(message_t const &msg, void *arg); #undef USE_TIMERS_WEB // Disable support for timer webpage #undef USE_SUNRISE // Disable support for Sunrise and sunset tools #undef USE_RULES // Disable support for rules -#undef USE_DHT // Disable internal DHT sensor +#undef USE_SCRIPT // Disable support for script +#undef USE_LIGHT // Disable support for lights +#undef USE_COUNTER // Disable counters #undef USE_DS18x20 // Disable DS18x20 sensor #undef USE_DS18x20_LEGACY // Disable DS18x20 sensor #undef USE_DS18B20 // Disable internal DS18B20 sensor @@ -373,14 +412,15 @@ void KNX_CB_Action(message_t const &msg, void *arg); #undef USE_MP3_PLAYER // Disable DFPlayer Mini MP3 Player RB-DFR-562 commands: play, volume and stop #undef USE_TUYA_DIMMER // Disable support for Tuya Serial Dimmer #undef USE_ARMTRONIX_DIMMERS // Disable support for Armtronix Dimmers (+1k4 code) -#undef USE_PS_16_DZ // Disable support for PS-16-DZ Dimmer +#undef USE_PS_16_DZ // Disable support for PS-16-DZ Dimmer and Sonoff L1 (+2k code) #undef USE_AZ7798 // Disable support for AZ-Instrument 7798 CO2 datalogger #undef USE_PN532_HSU // Disable support for PN532 using HSU (Serial) interface (+1k8 code, 140 bytes mem) #undef USE_PZEM004T // Disable PZEM004T energy sensor #undef USE_PZEM_AC // Disable PZEM014,016 Energy monitor #undef USE_PZEM_DC // Disable PZEM003,017 Energy monitor #undef USE_MCP39F501 // Disable MCP39F501 Energy monitor as used in Shelly 2 -#undef USE_MAX31855 // DIsable MAX31855 K-Type thermocouple sensor using softSPI +#undef USE_DHT // Disable support for DHT11, AM2301 (DHT21, DHT22, AM2302, AM2321) and SI7021 Temperature and Humidity sensor +#undef USE_MAX31855 // Disable MAX31855 K-Type thermocouple sensor using softSPI #undef USE_IR_REMOTE // Disable IR driver #undef USE_WS2812 // Disable WS2812 Led string #undef USE_ARILUX_RF // Disable support for Arilux RF remote controller @@ -391,6 +431,8 @@ void KNX_CB_Action(message_t const &msg, void *arg); #undef USE_TX20_WIND_SENSOR // Disable support for La Crosse TX20 anemometer #undef USE_RC_SWITCH // Disable support for RF transceiver using library RcSwitch #undef USE_RF_SENSOR // Disable support for RF sensor receiver (434MHz or 868MHz) (+0k8 code) +#undef USE_SM16716 // Disable support for SM16716 RGB LED controller (+0k7 code) +#undef USE_HRE // Disable support for Badger HR-E Water Meter (+1k4 code) #undef DEBUG_THEO // Disable debug code #undef USE_DEBUG_DRIVER // Disable debug code #endif // FIRMWARE_MINIMAL @@ -408,7 +450,8 @@ void KNX_CB_Action(message_t const &msg, void *arg); #endif #ifndef MQTT_FINGERPRINT1 -#define MQTT_FINGERPRINT1 "A5 02 FF 13 99 9F 8B 39 8E F1 83 4F 11 23 65 0B 32 36 FC 07" +// Set an all-zeros default fingerprint to activate auto-learning on first connection (AWS IoT) +#define MQTT_FINGERPRINT1 "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" #endif #ifndef MQTT_FINGERPRINT2 @@ -423,11 +466,14 @@ void KNX_CB_Action(message_t const &msg, void *arg); #define MQTT_MAX_PACKET_SIZE 1000 // Bytes #endif #ifndef MQTT_KEEPALIVE -#define MQTT_KEEPALIVE 15 // Seconds +#define MQTT_KEEPALIVE 30 // Seconds #endif #ifndef MQTT_TIMEOUT #define MQTT_TIMEOUT 10000 // milli seconds #endif +#ifndef MQTT_CLEAN_SESSION +#define MQTT_CLEAN_SESSION 1 // 0 = No clean session, 1 = Clean session (default) +#endif #ifndef MESSZ //#define MESSZ 405 // Max number of characters in JSON message string (6 x DS18x20 sensors) diff --git a/sonoff/sonoff_template.h b/sonoff/sonoff_template.h index c839d2a7e..631d2e712 100644 --- a/sonoff/sonoff_template.h +++ b/sonoff/sonoff_template.h @@ -171,13 +171,19 @@ enum UserSelectablePins { GPIO_DCKI, // my92x1 CLK input GPIO_CSE7766_TX, // CSE7766 Serial interface (S31 and Pow R2) GPIO_CSE7766_RX, // CSE7766 Serial interface (S31 and Pow R2) - GPIO_ARIRFRCV, // AliLux RF Receive input + GPIO_ARIRFRCV, // AriLux RF Receive input GPIO_TXD, // Serial interface GPIO_RXD, // Serial interface GPIO_ROT1A, // Rotary switch1 A Pin GPIO_ROT1B, // Rotary switch1 B Pin GPIO_ROT2A, // Rotary switch2 A Pin GPIO_ROT2B, // Rotary switch2 B Pin + GPIO_HRE_CLOCK, // Clock/Power line for HR-E Water Meter + GPIO_HRE_DATA, // Data line for HR-E Water Meter + GPIO_ADE7953_IRQ, // ADE7953 IRQ + GPIO_LEDLNK, // Link led + GPIO_LEDLNK_INV, // Inverted link led + GPIO_ARIRFSEL, // Arilux RF Receive input selected GPIO_SENSOR_END }; // Programmer selectable GPIO functionality @@ -241,6 +247,36 @@ const char kSensorNames[] PROGMEM = D_SENSOR_CSE7766_TX "|" D_SENSOR_CSE7766_RX "|" D_SENSOR_ARIRFRCV "|" D_SENSOR_TXD "|" D_SENSOR_RXD "|" D_SENSOR_ROTARY "1a|" D_SENSOR_ROTARY "1b|" D_SENSOR_ROTARY "2a|" D_SENSOR_ROTARY "2b|" + D_SENSOR_HRE_CLOCK "|" D_SENSOR_HRE_DATA "|" + D_SENSOR_ADE7953_IRQ "|" + D_SENSOR_LED_LINK "|" D_SENSOR_LED_LINK "i|" + D_SENSOR_ARIRFSEL "|" + ; + +// User selectable ADC0 functionality +enum UserSelectableAdc0 { + ADC0_NONE, // Not used + ADC0_INPUT, // Analog input + ADC0_TEMP, // Thermistor + ADC0_LIGHT, // Light sensor + ADC0_BUTTON, // Button + ADC0_BUTTON_INV, +// ADC0_SWITCH, // Switch +// ADC0_SWITCH_INV, + ADC0_END }; + +// Programmer selectable ADC0 functionality +enum ProgramSelectableAdc0 { + ADC0_FIX_START = 14, + ADC0_USER, // User configurable needs to be 15 + ADC0_MAX }; + +// Text in webpage Module Parameters and commands ADC +const char kAdc0Names[] PROGMEM = + D_SENSOR_NONE "|" D_ANALOG_INPUT "|" + D_TEMPERATURE "|" D_LIGHT "|" + D_SENSOR_BUTTON "|" D_SENSOR_BUTTON "i|" +// D_SENSOR_SWITCH "|" D_SENSOR_SWITCH "i|" ; /********************************************************************************************/ @@ -316,7 +352,8 @@ enum SupportedModules { SP10, WAGA, SYF05, - MAXMODULE }; + SONOFF_L1, + MAXMODULE}; #define USER_MODULE 255 @@ -335,24 +372,17 @@ typedef struct MYCFGIO { uint8_t io[MAX_GPIO_PIN - MIN_FLASH_PINS]; } mycfgio; -#define GPIO_FLAG_USED 1 // Currently only one flag used +#define GPIO_FLAG_USED 0 // Currently two flags used -#define GPIO_FLAG_ADC0 1 // Allow ADC0 when define USE_ADC_VCC is disabled -#define GPIO_FLAG_SPARE01 2 // Allow input pull-up control using SetOption62 - Superseded by user template editing -#define GPIO_FLAG_SPARE02 4 -#define GPIO_FLAG_SPARE03 8 -#define GPIO_FLAG_SPARE04 16 -#define GPIO_FLAG_SPARE05 32 -#define GPIO_FLAG_SPARE06 64 -#define GPIO_FLAG_SPARE07 128 +#define GPIO_FLAG_SPARE04 16 +#define GPIO_FLAG_SPARE05 32 +#define GPIO_FLAG_SPARE06 64 +#define GPIO_FLAG_SPARE07 128 typedef union { uint8_t data; struct { - uint8_t adc0 : 1; // Allow ADC0 when define USE_ADC_VCC is disabled - uint8_t spare01 : 1; - uint8_t spare02 : 1; - uint8_t spare03 : 1; + uint8_t adc0 : 4; // Allow ADC0 when define USE_ADC_VCC is disabled uint8_t spare04 : 1; uint8_t spare05 : 1; uint8_t spare06 : 1; @@ -424,6 +454,8 @@ const uint8_t kGpioNiceList[] PROGMEM = { GPIO_LED3_INV, GPIO_LED4, GPIO_LED4_INV, + GPIO_LEDLNK, // Link led + GPIO_LEDLNK_INV, // Inverted link led GPIO_PWM1, // RGB Red or C Cold White GPIO_PWM1_INV, GPIO_PWM2, // RGB Green or CW Warm White @@ -434,6 +466,7 @@ const uint8_t kGpioNiceList[] PROGMEM = { GPIO_PWM4_INV, GPIO_PWM5, // RGBCW Warm White GPIO_PWM5_INV, +#ifdef USE_COUNTER GPIO_CNTR1, // Counters GPIO_CNTR1_NP, GPIO_CNTR2, @@ -442,6 +475,7 @@ const uint8_t kGpioNiceList[] PROGMEM = { GPIO_CNTR3_NP, GPIO_CNTR4, GPIO_CNTR4_NP, +#endif GPIO_TXD, // Serial interface GPIO_RXD, // Serial interface #ifdef USE_I2C @@ -460,11 +494,15 @@ const uint8_t kGpioNiceList[] PROGMEM = { #ifdef USE_DISPLAY GPIO_BACKLIGHT, // Display backlight control #endif +#ifdef USE_DHT GPIO_DHT11, // DHT11 GPIO_DHT22, // DHT21, DHT22, AM2301, AM2302, AM2321 GPIO_SI7021, // iTead SI7021 +#endif +#if defined(USE_DS18B20) || defined(USE_DS18x20) || defined(USE_DS18x20_LEGACY) GPIO_DSB, // Single wire DS18B20 or DS18S20 -#ifdef USE_WS2812 +#endif +#if defined(USE_LIGHT) && defined(USE_WS2812) GPIO_WS2812, // WS2812 Led string #endif #ifdef USE_IR_REMOTE @@ -499,6 +537,9 @@ const uint8_t kGpioNiceList[] PROGMEM = { GPIO_NRG_CF1, // HLW8012/HLJ-01 CF1 voltage / current GPIO_HLW_CF, // HLW8012 CF power GPIO_HJL_CF, // HJL-01/BL0937 CF power +#endif +#if defined(USE_ENERGY_SENSOR) && defined(USE_I2C) && defined(USE_ADE7953) + GPIO_ADE7953_IRQ, // ADE7953 IRQ #endif GPIO_CSE7766_TX, // CSE7766 Serial interface (S31 and Pow R2) GPIO_CSE7766_RX, // CSE7766 Serial interface (S31 and Pow R2) @@ -552,7 +593,7 @@ const uint8_t kGpioNiceList[] PROGMEM = { #ifdef USE_MP3_PLAYER GPIO_MP3_DFR562, // RB-DFR-562, DFPlayer Mini MP3 Player Serial interface #endif -#ifdef USE_TUYA_DIMMER +#if defined(USE_LIGHT) && defined(USE_TUYA_DIMMER) GPIO_TUYA_TX, // Tuya Serial interface GPIO_TUYA_RX, // Tuya Serial interface #endif @@ -573,21 +614,32 @@ const uint8_t kGpioNiceList[] PROGMEM = { GPIO_MAX31855CLK, // MAX31855 Serial interface GPIO_MAX31855DO, // MAX31855 Serial interface #endif +#ifdef USE_LIGHT GPIO_DI, // my92x1 PWM input GPIO_DCKI, // my92x1 CLK input #ifdef USE_SM16716 GPIO_SM16716_CLK, // SM16716 CLOCK GPIO_SM16716_DAT, // SM16716 DATA GPIO_SM16716_SEL, // SM16716 SELECT -#endif // USE_SM16716 +#endif // USE_SM16716 +#endif // USE_LIGHT +#ifdef ROTARY_V1 GPIO_ROT1A, // Rotary switch1 A Pin GPIO_ROT1B, // Rotary switch1 B Pin GPIO_ROT2A, // Rotary switch2 A Pin GPIO_ROT2B, // Rotary switch2 B Pin - GPIO_ARIRFRCV // AliLux RF Receive input +#endif +#ifdef USE_ARILUX_RF + GPIO_ARIRFRCV, // AriLux RF Receive input + GPIO_ARIRFSEL, // Arilux RF Receive input selected +#endif +#ifdef USE_HRE + GPIO_HRE_CLOCK, + GPIO_HRE_DATA +#endif }; -const uint8_t kModuleNiceList[MAXMODULE] PROGMEM = { +const uint8_t kModuleNiceList[] PROGMEM = { SONOFF_BASIC, // Sonoff Relay Devices SONOFF_RF, SONOFF_TH, @@ -605,6 +657,9 @@ const uint8_t kModuleNiceList[MAXMODULE] PROGMEM = { SONOFF_T13, SONOFF_LED, // Sonoff Light Devices SONOFF_BN, +#ifdef USE_PS_16_DZ + SONOFF_L1, +#endif SONOFF_B1, // Sonoff Light Bulbs SLAMPHER, SONOFF_SC, // Sonoff Environmemtal Sensor @@ -639,9 +694,15 @@ const uint8_t kModuleNiceList[MAXMODULE] PROGMEM = { OBI2, MANZOKU_EU_4, ESP_SWITCH, // Switch Devices +#ifdef USE_TUYA_DIMMER TUYA_DIMMER, // Dimmer Devices +#endif +#ifdef USE_ARMTRONIX_DIMMERS ARMTRONIX_DIMMERS, +#endif +#ifdef USE_PS_16_DZ PS_16_DZ, +#endif H801, // Light Devices MAGICHOME, ARILUX_LC01, @@ -649,7 +710,9 @@ const uint8_t kModuleNiceList[MAXMODULE] PROGMEM = { ARILUX_LC11, ZENGGE_ZF_WF017, HUAFAN_SS, +#ifdef ROTARY_V1 MI_DESK_LAMP, +#endif KMC_70011, AILIGHT, // Light Bulbs PHILIPS, @@ -716,7 +779,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) - Link and Power status GPIO_USER, // GPIO14 Optional sensor 0, 0, - GPIO_FLAG_ADC0 // ADC0 Analog input + ADC0_USER // ADC0 Analog input }, { "Sonoff TH", // Sonoff TH10/16 (ESP8266) GPIO_KEY1, // GPIO00 Button @@ -914,7 +977,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, // GPIO14 Optional sensor GPIO_USER, // GPIO15 Optional sensor GPIO_LED1, // GPIO16 Green/Blue Led (1 = On, 0 = Off) - Link and Power status - GPIO_FLAG_ADC0 // ADC0 A0 Analog input + ADC0_USER // ADC0 A0 Analog input }, { "EXS Relay(s)", // ES-Store Latching relay(s) (ESP8266) // https://ex-store.de/ESP8266-WiFi-Relay-V31 @@ -975,7 +1038,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, // GPIO14 D5 GPIO_USER, // GPIO15 D8 GPIO_USER, // GPIO16 D0 Wemos Wake - GPIO_FLAG_ADC0 // ADC0 A0 Analog input + ADC0_USER // ADC0 A0 Analog input }, { "Sonoff Dev", // Sonoff Dev (ESP8266) GPIO_KEY1, // GPIO00 E-FW Button @@ -995,7 +1058,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, // GPIO14 Optional sensor 0, // GPIO15 0, // GPIO16 - GPIO_FLAG_ADC0 // ADC0 A0 Analog input + ADC0_USER // ADC0 A0 Analog input }, { "H801", // Lixada H801 Wifi (ESP8266) GPIO_USER, // GPIO00 E-FW Button @@ -1064,9 +1127,9 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { 0, 0 }, { "Huafan SS", // Hua Fan Smart Socket (ESP8266) - like Sonoff Pow - GPIO_LED1_INV, // GPIO00 Blue Led (0 = On, 1 = Off) - Link status + GPIO_LEDLNK_INV, // GPIO00 Blue Led (0 = On, 1 = Off) - Link status 0, 0, - GPIO_LED2_INV, // GPIO03 Red Led (0 = On, 1 = Off) - Power status + GPIO_LED1_INV, // GPIO03 Red Led (0 = On, 1 = Off) - Power status GPIO_KEY1, // GPIO04 Button GPIO_REL1_INV, // GPIO05 Relay (0 = On, 1 = Off) // GPIO06 (SD_CLK Flash) @@ -1202,7 +1265,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, // GPIO14 Optional sensor 0, GPIO_LED1, // GPIO16 Led (1 = On, 0 = Off) - Link and Power status - GPIO_FLAG_ADC0 // ADC0 A0 Analog input + ADC0_USER // ADC0 A0 Analog input }, { "Witty Cloud", // Witty Cloud Dev Board (ESP8266) // https://www.aliexpress.com/item/ESP8266-serial-WIFI-Witty-cloud-Development-Board-ESP-12F-module-MINI-nodemcu/32643464555.html @@ -1223,7 +1286,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, // GPIO14 D5 optional sensor GPIO_PWM1, // GPIO15 D8 RGB LED Red GPIO_USER, // GPIO16 D0 optional sensor - GPIO_FLAG_ADC0 // ADC0 A0 Light sensor / Requires USE_ADC_VCC in user_config.h to be disabled + ADC0_USER // ADC0 A0 Light sensor / Requires USE_ADC_VCC in user_config.h to be disabled }, { "Yunshan Relay", // Yunshan Wifi Relay (ESP8266) // https://www.ebay.com/p/Esp8266-220v-10a-Network-Relay-WiFi-Module/1369583381 @@ -1259,7 +1322,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_PWM3, // GPIO12 RGB LED Blue GPIO_USER, // GPIO13 RGBW LED White (optional - set to PWM4 for Cold White or Warm White as used on Arilux LC10) GPIO_PWM1, // GPIO14 RGB LED Red - GPIO_LED4_INV, // GPIO15 RF receiver control (Arilux LC10) + GPIO_ARIRFSEL, // GPIO15 RF receiver control (Arilux LC10) 0, 0 }, { "Luani HVIO", // ESP8266_HVIO @@ -1281,7 +1344,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, // GPIO14 Optional sensor / I2C SCL pad GPIO_LED1, // GPIO15 Led (1 = On, 0 = Off) - Link and Power status 0, - GPIO_FLAG_ADC0 // ADC0 A0 Analog input + ADC0_USER // ADC0 A0 Analog input }, { "KMC 70011", // KMC 70011 // https://www.amazon.com/KMC-Timing-Monitoring-Network-125V-240V/dp/B06XRX2GTQ @@ -1305,7 +1368,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { // (PwmFrequency 1111Hz) GPIO_KEY1, // GPIO00 Optional Button GPIO_USER, // GPIO01 Serial RXD and Optional sensor - GPIO_LED4_INV, // GPIO02 RF receiver control + GPIO_ARIRFSEL, // GPIO02 RF receiver control GPIO_USER, // GPIO03 Serial TXD and Optional sensor GPIO_ARIRFRCV, // GPIO04 IR or RF receiver (optional) GPIO_PWM1, // GPIO05 RGB LED Red @@ -1325,7 +1388,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { // (PwmFrequency 540Hz) GPIO_KEY1, // GPIO00 Optional Button GPIO_USER, // GPIO01 Serial RXD and Optional sensor - GPIO_LED4_INV, // GPIO02 RF receiver control + GPIO_ARIRFSEL, // GPIO02 RF receiver control GPIO_USER, // GPIO03 Serial TXD and Optional sensor GPIO_PWM2, // GPIO04 RGB LED Green GPIO_PWM1, // GPIO05 RGB LED Red @@ -1453,9 +1516,9 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { // https://www.amazon.de/Steckdose-Homecube-intelligente-Verbrauchsanzeige-funktioniert/dp/B076Q2LKHG/ref=sr_1_fkmr0_1 // https://www.amazon.de/Intelligente-Stromverbrauch-Fernsteurung-Schaltbare-Energieklasse/dp/B076WZQS4S/ref=sr_1_1 // https://www.aliexpress.com/store/product/BlitzWolf-BW-SHP6-EU-Plug-Metering-Version-WIFI-Smart-Socket-220V-240V-10A-Work-with-Amazon/1965360_32945504669.html - GPIO_LED2_INV, // GPIO00 Red Led (1 = On, 0 = Off) - Power status + GPIO_LED1_INV, // GPIO00 Red Led (1 = On, 0 = Off) - Power status GPIO_USER, // GPIO01 Serial RXD and Optional sensor - GPIO_LED1_INV, // GPIO02 Blue Led (1 = On, 0 = Off) - Link status + GPIO_LEDLNK_INV, // GPIO02 Blue Led (1 = On, 0 = Off) - Link status GPIO_USER, // GPIO03 Serial TXD and Optional sensor 0, GPIO_HJL_CF, // GPIO05 BL0937 or HJL-01 CF power @@ -1472,10 +1535,10 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { 0, 0 }, { "Shelly 1", // Shelly1 Open Source (ESP8266 - 2MB) - https://shelly.cloud/shelly1-open-source/ - GPIO_USER, // GPIO00 - Only to be used when Shelly is connected to 12V DC - GPIO_USER, // GPIO01 Serial RXD - Only to be used when Shelly is connected to 12V DC + 0, // GPIO00 - Can be changed to GPIO_USER, only if Shelly is powered with 12V DC + 0, // GPIO01 Serial RXD - Can be changed to GPIO_USER, only if Shelly is powered with 12V DC 0, - GPIO_USER, // GPIO03 Serial TXD - Only to be used when Shelly is connected to 12V DC + 0, // GPIO03 Serial TXD - Can be changed to GPIO_USER, only if Shelly is powered with 12V DC GPIO_REL1, // GPIO04 Relay (0 = Off, 1 = On) GPIO_SWT1_NP, // GPIO05 SW pin // GPIO06 (SD_CLK Flash) @@ -1573,13 +1636,13 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_KEY1, // GPIO14 Button 0, GPIO_USER, // GPIO16 - GPIO_FLAG_ADC0 // ADC0 A0 Analog input + ADC0_USER // ADC0 A0 Analog input }, { "Teckin", // https://www.amazon.de/gp/product/B07D5V139R 0, GPIO_KEY1, // GPIO01 Serial TXD and Button 0, - GPIO_LED2_INV, // GPIO03 Serial RXD and Red Led (0 = On, 1 = Off) - Power status + GPIO_LED1_INV, // GPIO03 Serial RXD and Red Led (0 = On, 1 = Off) - Power status GPIO_HJL_CF, // GPIO04 BL0937 or HJL-01 CF power GPIO_NRG_CF1, // GPIO05 BL0937 or HJL-01 CF1 current / voltage // GPIO06 (SD_CLK Flash) @@ -1589,7 +1652,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) // GPIO11 (SD_CMD Flash) GPIO_NRG_SEL_INV, // GPIO12 BL0937 or HJL-01 Sel output (0 = Voltage) - GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link status + GPIO_LEDLNK_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link status GPIO_REL1, // GPIO14 Relay (0 = Off, 1 = On) 0, 0, 0 }, @@ -1633,7 +1696,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { }, { "Gosund SP1 v23", // https://www.amazon.de/gp/product/B0777BWS1P 0, - GPIO_LED1_INV, // GPIO01 Serial RXD and LED1 (blue) inv - Link status + GPIO_LEDLNK_INV, // GPIO01 Serial RXD and LED1 (blue) inv - Link status 0, GPIO_KEY1, // GPIO03 Serial TXD and Button GPIO_HJL_CF, // GPIO04 BL0937 or HJL-01 CF power @@ -1645,7 +1708,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) // GPIO11 (SD_CMD Flash) GPIO_NRG_SEL_INV, // GPIO12 BL0937 or HJL-01 Sel output (0 = Voltage) - GPIO_LED2_INV, // GPIO13 LED2 (red) inv - Power status + GPIO_LED1_INV, // GPIO13 LED2 (red) inv - Power status GPIO_REL1, // GPIO14 Relay (0 = Off, 1 = On) 0, 0, 0 }, @@ -1683,8 +1746,8 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) // GPIO11 (SD_CMD Flash) GPIO_NRG_SEL_INV, // GPIO12 HLW8012 CF Sel output (0 = Voltage) - GPIO_LED2_INV, // GPIO13 Red Led (0 = On, 1 = Off) - Power status - GPIO_LED1_INV, // GPIO14 Blue Led (0 = On, 1 = Off) - Link status + GPIO_LED1_INV, // GPIO13 Red Led (0 = On, 1 = Off) - Power status + GPIO_LEDLNK_INV, // GPIO14 Blue Led (0 = On, 1 = Off) - Link status GPIO_REL1, // GPIO15 Relay (0 = Off, 1 = On) 0, 0 }, @@ -1712,9 +1775,9 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { { "Teckin US", // Teckin SP20 US with Energy Monitoring // https://www.amazon.com/Outlet-Compatible-Monitoring-Function-Required/dp/B079Q5W22B // https://www.amazon.com/Outlet-ZOOZEE-Monitoring-Function-Compatible/dp/B07J2LR5KN - GPIO_LED2_INV, // GPIO00 Red Led (1 = On, 0 = Off) - Power status + GPIO_LED1_INV, // GPIO00 Red Led (1 = On, 0 = Off) - Power status 0, - GPIO_LED1_INV, // GPIO02 Blue Led (1 = On, 0 = Off) - Link status + GPIO_LEDLNK_INV, // GPIO02 Blue Led (1 = On, 0 = Off) - Link status 0, GPIO_REL1, // GPIO04 Relay (0 = Off, 1 = On) GPIO_HJL_CF, // GPIO05 BL0937 or HJL-01 CF power @@ -1764,8 +1827,8 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) // GPIO11 (SD_CMD Flash) - GPIO_LED1_INV, // GPIO12 Green LED - Link status - GPIO_LED2, // GPIO13 Red LED - Power status + GPIO_LEDLNK_INV, // GPIO12 Green LED - Link status + GPIO_LED1, // GPIO13 Red LED - Power status 0, 0, 0, 0 }, { "YTF IR Bridge", // https://www.aliexpress.com/item/Tuya-universal-Smart-IR-Hub-remote-control-Voice-Control-AC-TV-Work-With-Alexa-Google-Home/32951202513.html @@ -1809,7 +1872,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { }, { "KA10", // SMANERGY KA10 (ESP8285 - BL0937 Energy Monitoring) - https://www.amazon.es/dp/B07MBTCH2Y 0, // GPIO00 - GPIO_LED1_INV, // GPIO01 Blue LED - Link status + GPIO_LEDLNK_INV, // GPIO01 Blue LED - Link status 0, // GPIO02 GPIO_KEY1, // GPIO03 Button GPIO_HJL_CF, // GPIO04 BL0937 CF power @@ -1821,7 +1884,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) // GPIO11 (SD_CMD Flash) GPIO_NRG_SEL_INV, // GPIO12 BL0937 Sel output (1 = Voltage) - GPIO_LED2, // GPIO13 Red LED - Power status + GPIO_LED1, // GPIO13 Red LED - Power status GPIO_REL1, // GPIO14 Relay 1 0, 0, 0 }, @@ -1878,7 +1941,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { }, { "WAGA CHCZ02MB", // WAGA life CHCZ02MB (HJL-01 Energy Monitoring) // https://www.ebay.com/itm/332595697006 - GPIO_LED2_INV, // GPIO00 Red LED + GPIO_LED1_INV, // GPIO00 Red LED 0, // GPIO01 Serial RXD 0, // GPIO02 GPIO_NRG_SEL_INV, // GPIO03 HJL-01 Sel output (1 = Voltage) @@ -1893,7 +1956,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_REL1, // GPIO12 Relay GPIO_KEY1, // GPIO13 Button GPIO_NRG_CF1, // GPIO14 HJL-01 CF1 voltage / current - GPIO_LED1_INV, // GPIO15 Blue LED - Link status + GPIO_LEDLNK_INV, // GPIO15 Blue LED - Link status 0, 0 }, { "SYF05", // Sunyesmart SYF05 (a.k.a. Fcmila) = TYWE3S + SM16726 @@ -1918,7 +1981,27 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_SM16716_DAT, // GPIO14 SM16716 Data 0, // GPIO15 wired to GND GPIO_USER, // GPIO16 N.C. - GPIO_FLAG_ADC0 // ADC0 A0 Analog input + ADC0_USER // ADC0 A0 Analog input + }, + { "Sonoff L1", // Sonoff L1 RGB LED controller (ESP8266 w/ separate Nuvoton MCU) + GPIO_USER, + GPIO_TXD, // GPIO01 MCU serial control + GPIO_USER, + GPIO_RXD, // GPIO03 MCU serial control + GPIO_USER, + GPIO_USER, + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_USER, + GPIO_LED1, // GPIO13 WiFi LED - Link and Power status + GPIO_USER, + GPIO_USER, + GPIO_USER, + 0 } }; @@ -2025,7 +2108,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_PWM3, // GPIO12 RGB LED Blue GPIO_PWM4, // GPIO13 RGBW LED White GPIO_PWM1, // GPIO14 RGB LED Red - GPIO_LED4_INV, // GPIO15 RF receiver control + GPIO_ARIRFSEL, // GPIO15 RF receiver control 0, 0 } diff --git a/sonoff/sonoff_version.h b/sonoff/sonoff_version.h index 0e9c322ac..32c1e92e6 100644 --- a/sonoff/sonoff_version.h +++ b/sonoff/sonoff_version.h @@ -20,11 +20,6 @@ #ifndef _SONOFF_VERSION_H_ #define _SONOFF_VERSION_H_ -#define VERSION 0x06050000 - -#define D_PROGRAMNAME "Sonoff-Tasmota" -#define D_AUTHOR "Theo Arends" -//#define D_WEBLINK "https://github.com/arendst/Sonoff-Tasmota" -#define D_WEBLINK "https://bit.ly/tasmota" +const uint32_t VERSION = 0x06060000; #endif // _SONOFF_VERSION_H_ diff --git a/sonoff/support.ino b/sonoff/support.ino index e6068fb77..41e5ffd3b 100644 --- a/sonoff/support.ino +++ b/sonoff/support.ino @@ -28,7 +28,7 @@ uint32_t syslog_host_hash = 0; // Syslog host name hash Ticker tickerOSWatch; -#define OSWATCH_RESET_TIME 120 +const uint32_t OSWATCH_RESET_TIME = 120; static unsigned long oswatch_last_loop_time; uint8_t oswatch_blocked_loop = 0; @@ -108,7 +108,7 @@ void* memchr(const void* ptr, int value, size_t num) return 0; } -// http://clc-wiki.net/wiki/C_standard_library:string.h:strspn +// http://clc-wiki.net/wiki/C_standard_library:string.h:strcspn // Get span until any character in string size_t strcspn(const char *str1, const char *str2) { @@ -123,6 +123,77 @@ size_t strcspn(const char *str1, const char *str2) } return ret; } + +// https://opensource.apple.com/source/Libc/Libc-583/stdlib/FreeBSD/strtoull.c +// Convert a string to an unsigned long long integer +#ifndef __LONG_LONG_MAX__ +#define __LONG_LONG_MAX__ 9223372036854775807LL +#endif +#ifndef ULLONG_MAX +#define ULLONG_MAX (__LONG_LONG_MAX__ * 2ULL + 1) +#endif + +unsigned long long strtoull(const char *__restrict nptr, char **__restrict endptr, int base) +{ + const char *s = nptr; + char c; + do { c = *s++; } while (isspace((unsigned char)c)); // Trim leading spaces + + int neg = 0; + if (c == '-') { // Set minus flag and/or skip sign + neg = 1; + c = *s++; + } else { + if (c == '+') { + c = *s++; + } + } + + if ((base == 0 || base == 16) && (c == '0') && (*s == 'x' || *s == 'X')) { // Set Hexadecimal + c = s[1]; + s += 2; + base = 16; + } + if (base == 0) { base = (c == '0') ? 8 : 10; } // Set Octal or Decimal + + unsigned long long acc = 0; + int any = 0; + if (base > 1 && base < 37) { + unsigned long long cutoff = ULLONG_MAX / base; + int cutlim = ULLONG_MAX % base; + for ( ; ; c = *s++) { + if (c >= '0' && c <= '9') + c -= '0'; + else if (c >= 'A' && c <= 'Z') + c -= 'A' - 10; + else if (c >= 'a' && c <= 'z') + c -= 'a' - 10; + else + break; + + if (c >= base) + break; + + if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim)) + any = -1; + else { + any = 1; + acc *= base; + acc += c; + } + } + if (any < 0) { + acc = ULLONG_MAX; // Range error + } + else if (any && neg) { + acc = -acc; + } + } + + if (endptr != nullptr) { *endptr = (char *)(any ? s - 1 : nptr); } + + return acc; +} #endif // ARDUINO_ESP8266_RELEASE_2_3_0 // Get span until single character in string @@ -139,21 +210,21 @@ size_t strchrspn(const char *str1, int character) char* subStr(char* dest, char* str, const char *delim, int index) { char *act; - char *sub = NULL; + char *sub = nullptr; char *ptr; int i; // Since strtok consumes the first arg, make a copy strncpy(dest, str, strlen(str)+1); - for (i = 1, act = dest; i <= index; i++, act = NULL) { + for (i = 1, act = dest; i <= index; i++, act = nullptr) { sub = strtok_r(act, delim, &ptr); - if (sub == NULL) break; + if (sub == nullptr) break; } sub = Trim(sub); return sub; } -double CharToDouble(const char *str) +float CharToFloat(const char *str) { // simple ascii to double, because atof or strtod are too large char strbuf[24]; @@ -166,23 +237,23 @@ double CharToDouble(const char *str) if (*pt == '-') { sign = -1; } if (*pt == '-' || *pt=='+') { pt++; } // Skip any sign - double left = 0; + float left = 0; if (*pt != '.') { left = atoi(pt); // Get left part while (isdigit(*pt)) { pt++; } // Skip number } - double right = 0; + float right = 0; if (*pt == '.') { pt++; right = atoi(pt); // Decimal part while (isdigit(*pt)) { pt++; - right /= 10.0; + right /= 10.0f; } } - double result = left + right; + float result = left + right; if (sign < 0) { return -result; // Add negative sign } @@ -200,6 +271,27 @@ int TextToInt(char *str) return strtol(str, &p, radix); } +char* ulltoa(unsigned long long value, char *str, int radix) +{ + char digits[64]; + char *dst = str; + int i = 0; + int n = 0; + +// if (radix < 2 || radix > 36) { radix = 10; } + + do { + n = value % radix; + digits[i++] = (n < 10) ? (char)n+'0' : (char)n-10+'A'; + value /= radix; + } while (value != 0); + + while (i > 0) { *dst++ = digits[--i]; } + + *dst = 0; + return str; +} + char* dtostrfd(double number, unsigned char prec, char *s) { if ((isnan(number)) || (isinf(number))) { // Fix for JSON output (https://stackoverflow.com/questions/1423081/json-left-out-infinity-and-nan-json-status-in-ecmascript) @@ -261,7 +353,7 @@ char* Unescape(char* buffer, uint16_t* size) } } *size = end_size; - + *write++ = 0; // add the end string pointer reference // AddLogBuffer(LOG_LEVEL_DEBUG, (uint8_t*)buffer, *size); return buffer; @@ -283,6 +375,19 @@ char* RemoveSpace(char* p) return p; } +char* LowerCase(char* dest, const char* source) +{ + char* write = dest; + const char* read = source; + char ch = '.'; + + while (ch != '\0') { + ch = *read++; + *write++ = tolower(ch); + } + return dest; +} + char* UpperCase(char* dest, const char* source) { char* write = dest; @@ -332,6 +437,21 @@ char* NoAlNumToUnderscore(char* dest, const char* source) return dest; } +char IndexSeparator() +{ +/* + // 20 bytes more costly !?! + const char separators[] = { "-_" }; + + return separators[Settings.flag3.use_underscore]; +*/ + if (Settings.flag3.use_underscore) { + return '_'; + } else { + return '-'; + } +} + void SetShortcut(char* str, uint8_t action) { if ('\0' != str[0]) { // There must be at least one character in the buffer @@ -357,6 +477,14 @@ uint8_t Shortcut(const char* str) return result; } +bool ValidIpAddress(const char* str) +{ + const char* p = str; + + while (*p && ((*p == '.') || ((*p >= '0') && (*p <= '9')))) { p++; } + return (*p == '\0'); +} + bool ParseIp(uint32_t* addr, const char* str) { uint8_t *part = (uint8_t*)addr; @@ -364,9 +492,9 @@ bool ParseIp(uint32_t* addr, const char* str) *addr = 0; for (i = 0; i < 4; i++) { - part[i] = strtoul(str, NULL, 10); // Convert byte + part[i] = strtoul(str, nullptr, 10); // Convert byte str = strchr(str, '.'); - if (str == NULL || *str == '\0') { + if (str == nullptr || *str == '\0') { break; // No more separators, exit } str++; // Point to next character after separator @@ -409,7 +537,7 @@ bool NewerVersion(char* version_str) return false; // Bail if we can't duplicate. Assume bad. } // Loop through the version string, splitting on '.' seperators. - for (char *str = strtok_r(version_dup, ".", &str_ptr); str && i < sizeof(VERSION); str = strtok_r(NULL, ".", &str_ptr), i++) { + for (char *str = strtok_r(version_dup, ".", &str_ptr); str && i < sizeof(VERSION); str = strtok_r(nullptr, ".", &str_ptr), i++) { int field = atoi(str); // The fields in a version string can only range from 0-255. if ((field < 0) || (field > 255)) { @@ -462,21 +590,45 @@ float ConvertTemp(float c) { float result = c; + global_update = uptime; + global_temperature = c; + if (!isnan(c) && Settings.flag.temperature_conversion) { result = c * 1.8 + 32; // Fahrenheit } return result; } +float ConvertTempToCelsius(float c) +{ + float result = c; + + if (!isnan(c) && Settings.flag.temperature_conversion) { + result = (c - 32) / 1.8; // Celsius + } + return result; +} + char TempUnit(void) { return (Settings.flag.temperature_conversion) ? 'F' : 'C'; } +float ConvertHumidity(float h) +{ + global_update = uptime; + global_humidity = h; + + return h; +} + float ConvertPressure(float p) { float result = p; + global_update = uptime; + global_pressure = p; + if (!isnan(p) && Settings.flag.pressure_conversion) { result = p * 0.75006375541921; // mmHg } @@ -488,46 +640,16 @@ String PressureUnit(void) return (Settings.flag.pressure_conversion) ? String(D_UNIT_MILLIMETER_MERCURY) : String(D_UNIT_PRESSURE); } -void SetGlobalValues(float temperature, float humidity) -{ - global_update = uptime; - global_temperature = temperature; - global_humidity = humidity; -} - void ResetGlobalValues(void) { if ((uptime - global_update) > GLOBAL_VALUES_VALID) { // Reset after 5 minutes global_update = 0; - global_temperature = 0; + global_temperature = 9999; global_humidity = 0; + global_pressure = 0; } } -double FastPrecisePow(double a, double b) -{ - // https://martin.ankerl.com/2012/01/25/optimized-approximative-pow-in-c-and-cpp/ - // calculate approximation with fraction of the exponent - int e = (int)b; - union { - double d; - int x[2]; - } u = { a }; - u.x[1] = (int)((b - e) * (u.x[1] - 1072632447) + 1072632447); - u.x[0] = 0; - // exponentiation by squaring with the exponent's integer part - // double r = u.d makes everything much slower, not sure why - double r = 1.0; - while (e) { - if (e & 1) { - r *= a; - } - a *= a; - e >>= 1; - } - return r * u.d; -} - uint32_t SqrtInt(uint32_t num) { if (num <= 1) { @@ -683,7 +805,7 @@ void SerialSendRaw(char *codes) uint32_t GetHash(const char *buffer, size_t size) { uint32_t hash = 0; - for (uint16_t i = 0; i <= size; i++) { + for (uint32_t i = 0; i <= size; i++) { hash += (uint8_t)*buffer++ * (i +1); } return hash; @@ -697,6 +819,72 @@ void ShowSource(int source) } } +void WebHexCode(uint8_t i, const char* code) +{ + char scolor[10]; + + strlcpy(scolor, code, sizeof(scolor)); + char* p = scolor; + if ('#' == p[0]) { p++; } // Skip + + if (3 == strlen(p)) { // Convert 3 character to 6 character color code + p[6] = p[3]; // \0 + p[5] = p[2]; // 3 + p[4] = p[2]; // 3 + p[3] = p[1]; // 2 + p[2] = p[1]; // 2 + p[1] = p[0]; // 1 + } + + uint32_t color = strtol(p, nullptr, 16); +/* + if (3 == strlen(p)) { // Convert 3 character to 6 character color code + uint32_t w = ((color & 0xF00) << 8) | ((color & 0x0F0) << 4) | (color & 0x00F); // 00010203 + color = w | (w << 4); // 00112233 + } +*/ + + Settings.web_color[i][0] = (color >> 16) & 0xFF; // Red + Settings.web_color[i][1] = (color >> 8) & 0xFF; // Green + Settings.web_color[i][2] = color & 0xFF; // Blue +} + +uint32_t WebColor(uint8_t i) +{ + uint32_t tcolor = (Settings.web_color[i][0] << 16) | (Settings.web_color[i][1] << 8) | Settings.web_color[i][2]; + return tcolor; +} + +/*********************************************************************************************\ + * Response data handling +\*********************************************************************************************/ + +int Response_P(const char* format, ...) // Content send snprintf_P char data +{ + // This uses char strings. Be aware of sending %% if % is needed + va_list args; + va_start(args, format); + int len = vsnprintf_P(mqtt_data, sizeof(mqtt_data), format, args); + va_end(args); + return len; +} + +int ResponseAppend_P(const char* format, ...) // Content send snprintf_P char data +{ + // This uses char strings. Be aware of sending %% if % is needed + va_list args; + va_start(args, format); + int mlen = strlen(mqtt_data); + int len = vsnprintf_P(mqtt_data + mlen, sizeof(mqtt_data) - mlen, format, args); + va_end(args); + return len + mlen; +} + +int ResponseJsonEnd(void) +{ + return ResponseAppend_P(PSTR("}")); +} + /*********************************************************************************************\ * GPIO Module and Template management \*********************************************************************************************/ @@ -708,6 +896,22 @@ uint8_t ModuleNr() return (USER_MODULE == Settings.module) ? 0 : Settings.module +1; } +bool ValidTemplateModule(uint8_t index) +{ + for (uint32_t i = 0; i < sizeof(kModuleNiceList); i++) { + if (index == pgm_read_byte(kModuleNiceList + i)) { + return true; + } + } + return false; +} + +bool ValidModule(uint8_t index) +{ + if (index == USER_MODULE) { return true; } + return ValidTemplateModule(index); +} + String AnyModuleName(uint8_t index) { if (USER_MODULE == index) { @@ -738,7 +942,7 @@ void ModuleGpios(myio *gp) // AddLogBuffer(LOG_LEVEL_DEBUG, (uint8_t *)&src, sizeof(mycfgio)); uint8_t j = 0; - for (uint8_t i = 0; i < sizeof(mycfgio); i++) { + for (uint32_t i = 0; i < sizeof(mycfgio); i++) { if (6 == i) { j = 9; } if (8 == i) { j = 12; } dest[j] = src[i]; @@ -792,6 +996,13 @@ bool ValidGPIO(uint8_t pin, uint8_t gpio) return (GPIO_USER == ValidPin(pin, gpio)); // Only allow GPIO_USER pins } +bool ValidAdc() +{ + gpio_flag flag = ModuleFlag(); + uint8_t template_adc0 = flag.data &15; + return (ADC0_USER == template_adc0); +} + bool GetUsedInModule(uint8_t val, uint8_t *arr) { int offset = 0; @@ -846,7 +1057,7 @@ bool GetUsedInModule(uint8_t val, uint8_t *arr) offset = -(GPIO_CNTR1_NP - GPIO_CNTR1); } - for (uint8_t i = 0; i < MAX_GPIO_PIN; i++) { + for (uint32_t i = 0; i < MAX_GPIO_PIN; i++) { if (arr[i] == val) { return true; } if (arr[i] == val + offset) { return true; } } @@ -855,6 +1066,10 @@ bool GetUsedInModule(uint8_t val, uint8_t *arr) bool JsonTemplate(const char* dataBuf) { + // {"NAME":"Generic","GPIO":[17,254,29,254,7,254,254,254,138,254,139,254,254],"FLAG":1,"BASE":255} + + if (strlen(dataBuf) < 9) { return false; } // Workaround exception if empty JSON like {} - Needs checks + StaticJsonBuffer<350> jb; // 331 from https://arduinojson.org/v5/assistant/ JsonObject& obj = jb.parseObject(dataBuf); if (!obj.success()) { return false; } @@ -865,7 +1080,7 @@ bool JsonTemplate(const char* dataBuf) strlcpy(Settings.user_template.name, name, sizeof(Settings.user_template.name)); } if (obj[D_JSON_GPIO].success()) { - for (uint8_t i = 0; i < sizeof(mycfgio); i++) { + for (uint32_t i = 0; i < sizeof(mycfgio); i++) { Settings.user_template.gp.io[i] = obj[D_JSON_GPIO][i] | 0; } } @@ -875,20 +1090,19 @@ bool JsonTemplate(const char* dataBuf) } if (obj[D_JSON_BASE].success()) { uint8_t base = obj[D_JSON_BASE]; - if ((0 == base) || (base >= MAXMODULE)) { base = 17; } else { base--; } - Settings.user_template_base = base; // Default WEMOS + if ((0 == base) || !ValidTemplateModule(base -1)) { base = 18; } + Settings.user_template_base = base -1; // Default WEMOS } return true; } void TemplateJson() { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_NAME "\":\"%s\",\"" D_JSON_GPIO "\":["), Settings.user_template.name); - for (uint8_t i = 0; i < sizeof(Settings.user_template.gp); i++) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s%s%d"), mqtt_data, (i>0)?",":"", Settings.user_template.gp.io[i]); + Response_P(PSTR("{\"" D_JSON_NAME "\":\"%s\",\"" D_JSON_GPIO "\":["), Settings.user_template.name); + for (uint32_t i = 0; i < sizeof(Settings.user_template.gp); i++) { + ResponseAppend_P(PSTR("%s%d"), (i>0)?",":"", Settings.user_template.gp.io[i]); } - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s],\"" D_JSON_FLAG "\":%d,\"" D_JSON_BASE "\":%d}"), - mqtt_data, Settings.user_template.flag, Settings.user_template_base +1); + ResponseAppend_P(PSTR("],\"" D_JSON_FLAG "\":%d,\"" D_JSON_BASE "\":%d}"), Settings.user_template.flag, Settings.user_template_base +1); } /*********************************************************************************************\ @@ -959,29 +1173,31 @@ void SetNextTimeInterval(unsigned long& timer, const unsigned long step) \*********************************************************************************************/ #ifdef USE_I2C -#define I2C_RETRY_COUNTER 3 +const uint8_t I2C_RETRY_COUNTER = 3; uint32_t i2c_buffer = 0; bool I2cValidRead(uint8_t addr, uint8_t reg, uint8_t size) { - uint8_t x = I2C_RETRY_COUNTER; + uint8_t retry = I2C_RETRY_COUNTER; + bool status = false; i2c_buffer = 0; - do { + while (!status && retry) { Wire.beginTransmission(addr); // start transmission to device Wire.write(reg); // sends register address to read from if (0 == Wire.endTransmission(false)) { // Try to become I2C Master, send data and collect bytes, keep master status for next request... Wire.requestFrom((int)addr, (int)size); // send data n-bytes read if (Wire.available() == size) { - for (uint8_t i = 0; i < size; i++) { + for (uint32_t i = 0; i < size; i++) { i2c_buffer = i2c_buffer << 8 | Wire.read(); // receive DATA } + status = true; } } - x--; - } while (Wire.endTransmission(true) != 0 && x != 0); // end transmission - return (x); + retry--; + } + return status; } bool I2cValidRead8(uint8_t *data, uint8_t addr, uint8_t reg) @@ -1179,10 +1395,17 @@ void SetSeriallog(uint8_t loglevel) seriallog_timer = 0; } +void SetSyslog(uint8_t loglevel) +{ + Settings.syslog_level = loglevel; + syslog_level = loglevel; + syslog_timer = 0; +} + #ifdef USE_WEBSERVER void GetLog(uint8_t idx, char** entry_pp, size_t* len_p) { - char* entry_p = NULL; + char* entry_p = nullptr; size_t len = 0; if (idx) { @@ -1210,8 +1433,9 @@ void Syslog(void) // Destroys log_data char syslog_preamble[64]; // Hostname + Id - if (syslog_host_hash != GetHash(Settings.syslog_host, strlen(Settings.syslog_host))) { - syslog_host_hash = GetHash(Settings.syslog_host, strlen(Settings.syslog_host)); + uint32_t current_hash = GetHash(Settings.syslog_host, strlen(Settings.syslog_host)); + if (syslog_host_hash != current_hash) { + syslog_host_hash = current_hash; WiFi.hostByName(Settings.syslog_host, syslog_host_addr); // If sleep enabled this might result in exception so try to do it once using hash } if (PortUdp.beginPacket(syslog_host_addr, Settings.syslog_port)) { @@ -1219,8 +1443,9 @@ void Syslog(void) memmove(log_data + strlen(syslog_preamble), log_data, sizeof(log_data) - strlen(syslog_preamble)); log_data[sizeof(log_data) -1] = '\0'; memcpy(log_data, syslog_preamble, strlen(syslog_preamble)); - PortUdp.write(log_data); + PortUdp.write(log_data, strlen(log_data)); PortUdp.endPacket(); + delay(1); // Add time for UDP handling (#5512) } else { syslog_level = 0; syslog_timer = SYSLOG_TIMER; @@ -1287,7 +1512,7 @@ void AddLog_P2(uint8_t loglevel, PGM_P formatP, ...) void AddLogBuffer(uint8_t loglevel, uint8_t *buffer, int count) { snprintf_P(log_data, sizeof(log_data), PSTR("DMP:")); - for (int i = 0; i < count; i++) { + for (uint32_t i = 0; i < count; i++) { snprintf_P(log_data, sizeof(log_data), PSTR("%s %02X"), log_data, *(buffer++)); } AddLog(loglevel); diff --git a/sonoff/support_button.ino b/sonoff/support_button.ino index c0fe14bd2..9311cd838 100644 --- a/sonoff/support_button.ino +++ b/sonoff/support_button.ino @@ -34,7 +34,8 @@ uint8_t multipress[MAX_KEYS] = { 0 }; // Number of button presses within m uint8_t dual_hex_code = 0; // Sonoff dual input flag uint8_t key_no_pullup = 0; // key no pullup flag (1 = no pullup) uint8_t key_inverted = 0; // Key inverted flag (1 = inverted) -uint8_t buttons_found = 0; // Number of buttons found flag +uint8_t buttons_present = 0; // Number of buttons found flag +uint8_t button_adc = 99; // ADC0 button number /********************************************************************************************/ @@ -50,12 +51,18 @@ void ButtonInvertFlag(uint8 button_bit) void ButtonInit(void) { - buttons_found = 0; - for (uint8_t i = 0; i < MAX_KEYS; i++) { + buttons_present = 0; + for (uint32_t i = 0; i < MAX_KEYS; i++) { if (pin[GPIO_KEY1 +i] < 99) { - buttons_found++; + buttons_present++; pinMode(pin[GPIO_KEY1 +i], bitRead(key_no_pullup, i) ? INPUT : ((16 == pin[GPIO_KEY1 +i]) ? INPUT_PULLDOWN_16 : INPUT_PULLUP)); } +#ifndef USE_ADC_VCC + else if ((99 == button_adc) && ((ADC0_BUTTON == my_adc0) || (ADC0_BUTTON_INV == my_adc0))) { + buttons_present++; + button_adc = i; + } +#endif // USE_ADC_VCC } } @@ -83,21 +90,28 @@ uint8_t ButtonSerial(uint8_t serial_in_byte) /*********************************************************************************************\ * Button handler with single press only or multi-press and hold on all buttons + * + * ButtonDebounce (50) - Debounce time in mSec + * SetOption1 (0) - If set do not execute config commands + * SetOption11 (0) - If set perform single press action on double press and reverse + * SetOption13 (0) - If set act on single press only + * SetOption32 (40) - Max button hold time in Seconds + * SetOption40 (0) - Number of 0.1 seconds until hold is discarded if SetOption1 1 and SetOption13 0 \*********************************************************************************************/ void ButtonHandler(void) { - if (uptime < 4) { return; } // Block GPIO for 4 seconds after poweron to workaround Wemos D1 / Obi RTS circuit + if (uptime < 4) { return; } // Block GPIO for 4 seconds after poweron to workaround Wemos D1 / Obi RTS circuit uint8_t button = NOT_PRESSED; uint8_t button_present = 0; - uint8_t hold_time_extent = IMMINENT_RESET_FACTOR; // Extent hold time factor in case of iminnent Reset command - uint16_t loops_per_second = 1000 / Settings.button_debounce; + uint8_t hold_time_extent = IMMINENT_RESET_FACTOR; // Extent hold time factor in case of iminnent Reset command + uint16_t loops_per_second = 1000 / Settings.button_debounce; // ButtonDebounce (50) char scmnd[20]; // uint8_t maxdev = (devices_present > MAX_KEYS) ? MAX_KEYS : devices_present; -// for (uint8_t button_index = 0; button_index < maxdev; button_index++) { - for (uint8_t button_index = 0; button_index < MAX_KEYS; button_index++) { +// for (uint32_t button_index = 0; button_index < maxdev; button_index++) { + for (uint32_t button_index = 0; button_index < MAX_KEYS; button_index++) { button = NOT_PRESSED; button_present = 0; @@ -107,17 +121,27 @@ void ButtonHandler(void) AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BUTTON " " D_CODE " %04X"), dual_button_code); button = PRESSED; if (0xF500 == dual_button_code) { // Button hold - holdbutton[button_index] = (loops_per_second * Settings.param[P_HOLD_TIME] / 10) -1; + holdbutton[button_index] = (loops_per_second * Settings.param[P_HOLD_TIME] / 10) -1; // SetOption32 (40) hold_time_extent = 1; } dual_button_code = 0; } - } else { - if (pin[GPIO_KEY1 +button_index] < 99) { - button_present = 1; - button = (digitalRead(pin[GPIO_KEY1 +button_index]) != bitRead(key_inverted, button_index)); + } + else if (pin[GPIO_KEY1 +button_index] < 99) { + button_present = 1; + button = (digitalRead(pin[GPIO_KEY1 +button_index]) != bitRead(key_inverted, button_index)); + } +#ifndef USE_ADC_VCC + if (button_adc == button_index) { + button_present = 1; + if (ADC0_BUTTON_INV == my_adc0) { + button = (AdcRead(1) < 128); + } + else if (ADC0_BUTTON == my_adc0) { + button = (AdcRead(1) > 128); } } +#endif // USE_ADC_VCC if (button_present) { XdrvMailbox.index = button_index; @@ -146,7 +170,7 @@ void ButtonHandler(void) } else { if ((PRESSED == button) && (NOT_PRESSED == lastbutton[button_index])) { - if (Settings.flag.button_single) { // Allow only single button press for immediate action + if (Settings.flag.button_single) { // SetOption13 (0) - Allow only single button press for immediate action AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BUTTON "%d " D_IMMEDIATE), button_index +1); if (!SendKey(0, button_index +1, POWER_TOGGLE)) { // Execute Toggle command via MQTT if ButtonTopic is set ExecuteCommandPower(button_index +1, POWER_TOGGLE, SRC_BUTTON); // Execute Toggle command internally @@ -163,20 +187,27 @@ void ButtonHandler(void) holdbutton[button_index] = 0; } else { holdbutton[button_index]++; - if (Settings.flag.button_single) { // Allow only single button press for immediate action - if (holdbutton[button_index] == loops_per_second * hold_time_extent * Settings.param[P_HOLD_TIME] / 10) { // Button held for factor times longer + if (Settings.flag.button_single) { // SetOption13 (0) - Allow only single button press for immediate action + if (holdbutton[button_index] == loops_per_second * hold_time_extent * Settings.param[P_HOLD_TIME] / 10) { // SetOption32 (40) - Button held for factor times longer // Settings.flag.button_single = 0; snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_SETOPTION "13 0")); // Disable single press only ExecuteCommand(scmnd, SRC_BUTTON); } } else { - if (Settings.flag.button_restrict) { // Button restriction - if (holdbutton[button_index] == loops_per_second * Settings.param[P_HOLD_TIME] / 10) { // Button hold + if (Settings.flag.button_restrict) { // SetOption1 (0) - Button restriction + if (Settings.param[P_HOLD_IGNORE] > 0) { // SetOption40 (0) - Do not ignore button hold + if (holdbutton[button_index] > loops_per_second * Settings.param[P_HOLD_IGNORE] / 10) { + holdbutton[button_index] = 0; // Reset button hold counter to stay below hold trigger + multipress[button_index] = 0; // Discard button press to disable functionality +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BUTTON "%d cancel by " D_CMND_SETOPTION "40 %d"), button_index +1, Settings.param[P_HOLD_IGNORE]); + } + } + if (holdbutton[button_index] == loops_per_second * Settings.param[P_HOLD_TIME] / 10) { // SetOption32 (40) - Button hold multipress[button_index] = 0; SendKey(0, button_index +1, 3); // Execute Hold command via MQTT if ButtonTopic is set } } else { - if (holdbutton[button_index] == loops_per_second * hold_time_extent * Settings.param[P_HOLD_TIME] / 10) { // Button held for factor times longer + if (holdbutton[button_index] == loops_per_second * hold_time_extent * Settings.param[P_HOLD_TIME] / 10) { // SetOption32 (40) - Button held for factor times longer multipress[button_index] = 0; snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_RESET " 1")); ExecuteCommand(scmnd, SRC_BUTTON); @@ -185,7 +216,7 @@ void ButtonHandler(void) } } - if (!Settings.flag.button_single) { // Allow multi-press + if (!Settings.flag.button_single) { // SetOption13 (0) - Allow multi-press if (multiwindow[button_index]) { multiwindow[button_index]--; } else { @@ -194,14 +225,22 @@ void ButtonHandler(void) if (multipress[button_index] < 3) { // Single or Double press if ((SONOFF_DUAL_R2 == my_module_type) || (SONOFF_DUAL == my_module_type) || (CH4 == my_module_type)) { single_press = true; - } else { - single_press = (Settings.flag.button_swap +1 == multipress[button_index]); - multipress[button_index] = 1; + } else { + single_press = (Settings.flag.button_swap +1 == multipress[button_index]); // SetOption11 (0) + if ((1 == buttons_present) && (2 == devices_present)) { // Single Button with two devices only + if (Settings.flag.button_swap) { // SetOption11 (0) + multipress[button_index] = (single_press) ? 1 : 2; + } + } else { + multipress[button_index] = 1; + } } } +#ifdef USE_LIGHT if ((MI_DESK_LAMP == my_module_type) && (button_index == 0) && (rotary_changed) && (light_power)) { rotary_changed = 0; // Color temp changed, no need to turn of the light } else { +#endif if (single_press && SendKey(0, button_index + multipress[button_index], POWER_TOGGLE)) { // Execute Toggle command via MQTT if ButtonTopic is set // Success } else { @@ -212,13 +251,15 @@ void ButtonHandler(void) ExecuteCommandPower(button_index + multipress[button_index], POWER_TOGGLE, SRC_BUTTON); // Execute Toggle command internally } } else { // 3 - 7 press - if (!Settings.flag.button_restrict) { + if (!Settings.flag.button_restrict) { // SetOption1 (0) snprintf_P(scmnd, sizeof(scmnd), kCommands[multipress[button_index] -3]); ExecuteCommand(scmnd, SRC_BUTTON); } } } +#ifdef USE_LIGHT } +#endif multipress[button_index] = 0; } } @@ -231,9 +272,9 @@ void ButtonHandler(void) void ButtonLoop(void) { - if (buttons_found) { + if (buttons_present) { if (TimeReached(button_debounce)) { - SetNextTimeInterval(button_debounce, Settings.button_debounce); + SetNextTimeInterval(button_debounce, Settings.button_debounce); // ButtonDebounce (50) ButtonHandler(); } } diff --git a/sonoff/support_features.ino b/sonoff/support_features.ino index f5229e81f..1a551ceaf 100644 --- a/sonoff/support_features.ino +++ b/sonoff/support_features.ino @@ -23,11 +23,13 @@ void GetFeatures(void) { - feature_drv1 = 0x00000000; // xdrv_01_mqtt.ino, xdrv_01_light.ino, xdrv_04_snfbridge.ino + feature_drv1 = 0x00000000; // xdrv_02_mqtt.ino, xdrv_04_light.ino, xdrv_06_snfbridge.ino // feature_drv1 |= 0x00000001; -// feature_drv1 |= 0x00000002; +#ifdef USE_LIGHT + feature_drv1 |= 0x00000002; // sonoff.ino, xdrv_04_light.ino +#endif #ifdef USE_I2C feature_drv1 |= 0x00000004; // sonoff.ino #endif @@ -41,33 +43,33 @@ void GetFeatures(void) feature_drv1 |= 0x00000020; // sonoff.ino #endif #ifdef USE_MQTT_TLS - feature_drv1 |= 0x00000040; // sonoff.ino + feature_drv1 |= 0x00000040; // xdrv_02_mqtt.ino #endif #ifdef USE_WEBSERVER - feature_drv1 |= 0x00000080; // xdrv_02_webserver.ino + feature_drv1 |= 0x00000080; // xdrv_01_webserver.ino #endif #ifdef WEBSERVER_ADVERTISE - feature_drv1 |= 0x00000100; // xdrv_02_webserver.ino + feature_drv1 |= 0x00000100; // xdrv_01_webserver.ino #endif -#ifdef USE_EMULATION - feature_drv1 |= 0x00000200; // xplg_wemohue.ino +#ifdef USE_EMULATION_HUE + feature_drv1 |= 0x00000200; // xdrv_20_hue.ino #endif #if (MQTT_LIBRARY_TYPE == MQTT_PUBSUBCLIENT) - feature_drv1 |= 0x00000400; // xdrv_01_mqtt.ino + feature_drv1 |= 0x00000400; // xdrv_02_mqtt.ino #endif #if (MQTT_LIBRARY_TYPE == MQTT_TASMOTAMQTT) -// feature_drv1 |= 0x00000800; // xdrv_01_mqtt.ino +// feature_drv1 |= 0x00000800; // xdrv_02_mqtt.ino #endif #if (MQTT_LIBRARY_TYPE == MQTT_ESPMQTTARDUINO) // Obsolete since 6.2.1.11 -// feature_drv1 |= 0x00001000; // xdrv_01_mqtt.ino +// feature_drv1 |= 0x00001000; // xdrv_02_mqtt.ino #endif #ifdef MQTT_HOST_DISCOVERY - feature_drv1 |= 0x00002000; // xdrv_01_mqtt.ino + feature_drv1 |= 0x00002000; // xdrv_02_mqtt.ino #endif #ifdef USE_ARILUX_RF feature_drv1 |= 0x00004000; // xdrv_04_light.ino #endif -#ifdef USE_WS2812 +#if defined(USE_LIGHT) && defined(USE_WS2812) feature_drv1 |= 0x00008000; // xdrv_04_light.ino #endif #ifdef USE_WS2812_DMA @@ -116,7 +118,7 @@ void GetFeatures(void) feature_drv1 |= 0x40000000; // support.ino #endif #if (MQTT_LIBRARY_TYPE == MQTT_ARDUINOMQTT) -// feature_drv1 |= 0x80000000; // xdrv_01_mqtt.ino +// feature_drv1 |= 0x80000000; // xdrv_02_mqtt.ino #endif /*********************************************************************************************/ @@ -168,21 +170,25 @@ void GetFeatures(void) #ifdef USE_PCA9685 feature_drv2 |= 0x00004000; // xdrv_15_pca9685.ino #endif -#ifdef USE_TUYA_DIMMER +#if defined(USE_LIGHT) && defined(USE_TUYA_DIMMER) feature_drv2 |= 0x00008000; // xdrv_16_tuyadimmer.ino #endif #ifdef USE_RC_SWITCH feature_drv2 |= 0x00010000; // xdrv_17_rcswitch.ino #endif -#ifdef USE_ARMTRONIX_DIMMERS +#if defined(USE_LIGHT) && defined(USE_ARMTRONIX_DIMMERS) feature_drv2 |= 0x00020000; // xdrv_18_armtronixdimmer.ino #endif -#ifdef USE_SM16716 +#if defined(USE_LIGHT) && defined(USE_SM16716) feature_drv2 |= 0x00040000; // xdrv_04_light.ino #endif +#ifdef USE_SCRIPT + feature_drv2 |= 0x00080000; // xdrv_10_scripter.ino +#endif +#ifdef USE_EMULATION_WEMO + feature_drv2 |= 0x00100000; // xdrv_21_wemo.ino +#endif -// feature_drv2 |= 0x00080000; -// feature_drv2 |= 0x00100000; // feature_drv2 |= 0x00200000; // feature_drv2 |= 0x00400000; @@ -218,10 +224,11 @@ void GetFeatures(void) feature_sns1 = 0x00000000; // xsns_01_counter.ino, xsns_04_snfsc.ino -// feature_sns1 |= 0x00000001; - +#ifdef USE_COUNTER + feature_sns1 |= 0x00000001; // xsns_01_counter.ino +#endif #ifdef USE_ADC_VCC - feature_sns1 |= 0x00000002; // support.ino (ADC) + feature_sns1 |= 0x00000002; // xsns_02_analog.ino #endif #ifdef USE_ENERGY_SENSOR feature_sns1 |= 0x00000004; // xdrv_03_energy.ino @@ -382,13 +389,17 @@ void GetFeatures(void) feature_sns2 |= 0x00100000; // xsns_40_pn532.ino #endif #ifdef USE_MAX44009 - feature_sns2 |= 0x00200000; + feature_sns2 |= 0x00200000; // xsns_41_max44009.ino #endif #ifdef USE_SCD30 - feature_sns2 |= 0x00400000; + feature_sns2 |= 0x00400000; // xsns_42_scd30.ino +#endif +#ifdef USE_HRE + feature_sns2 |= 0x00800000; // xsns_43_hre.ino +#endif +#ifdef USE_ADE7953 + feature_sns2 |= 0x01000000; // xnrg_07_ade7953.ino #endif -// feature_sns2 |= 0x00800000; -// feature_sns2 |= 0x01000000; // feature_sns2 |= 0x02000000; // feature_sns2 |= 0x04000000; // feature_sns2 |= 0x08000000; diff --git a/sonoff/support_float.ino b/sonoff/support_float.ino new file mode 100644 index 000000000..6e9379a53 --- /dev/null +++ b/sonoff/support_float.ino @@ -0,0 +1,373 @@ +/* + support_float.ino - Small floating point support for Sonoff-Tasmota + + Copyright (C) 2019 Theo Arends and Stephan Hadinger + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +//#ifdef ARDUINO_ESP8266_RELEASE_2_3_0 +// Functions not available in 2.3.0 + +float fmodf(float x, float y) +{ + // https://github.com/micropython/micropython/blob/master/lib/libm/fmodf.c + union {float f; uint32_t i;} ux = {x}, uy = {y}; + int ex = ux.i>>23 & 0xff; + int ey = uy.i>>23 & 0xff; + uint32_t sx = ux.i & 0x80000000; + uint32_t i; + uint32_t uxi = ux.i; + + if (uy.i<<1 == 0 || isnan(y) || ex == 0xff) + return (x*y)/(x*y); + if (uxi<<1 <= uy.i<<1) { + if (uxi<<1 == uy.i<<1) + return 0*x; + return x; + } + + // normalize x and y + if (!ex) { + for (i = uxi<<9; i>>31 == 0; ex--, i <<= 1); + uxi <<= -ex + 1; + } else { + uxi &= -1U >> 9; + uxi |= 1U << 23; + } + if (!ey) { + for (i = uy.i<<9; i>>31 == 0; ey--, i <<= 1); + uy.i <<= -ey + 1; + } else { + uy.i &= -1U >> 9; + uy.i |= 1U << 23; + } + + // x mod y + for (; ex > ey; ex--) { + i = uxi - uy.i; + if (i >> 31 == 0) { + if (i == 0) + return 0*x; + uxi = i; + } + uxi <<= 1; + } + i = uxi - uy.i; + if (i >> 31 == 0) { + if (i == 0) + return 0*x; + uxi = i; + } + for (; uxi>>23 == 0; uxi <<= 1, ex--); + + // scale result up + if (ex > 0) { + uxi -= 1U << 23; + uxi |= (uint32_t)ex << 23; + } else { + uxi >>= -ex + 1; + } + uxi |= sx; + ux.i = uxi; + return ux.f; +} +//#endif // ARDUINO_ESP8266_RELEASE_2_3_0 + +double FastPrecisePow(double a, double b) +{ + // https://martin.ankerl.com/2012/01/25/optimized-approximative-pow-in-c-and-cpp/ + // calculate approximation with fraction of the exponent + int e = abs((int)b); + union { + double d; + int x[2]; + } u = { a }; + u.x[1] = (int)((b - e) * (u.x[1] - 1072632447) + 1072632447); + u.x[0] = 0; + // exponentiation by squaring with the exponent's integer part + // double r = u.d makes everything much slower, not sure why + double r = 1.0; + while (e) { + if (e & 1) { + r *= a; + } + a *= a; + e >>= 1; + } + return r * u.d; +} + +float FastPrecisePowf(const float x, const float y) +{ +// return (float)(pow((double)x, (double)y)); + return (float)FastPrecisePow(x, y); +} + +double TaylorLog(double x) +{ + // https://stackoverflow.com/questions/46879166/finding-the-natural-logarithm-of-a-number-using-taylor-series-in-c + + if (x <= 0.0) { return NAN; } + double z = (x + 1) / (x - 1); // We start from power -1, to make sure we get the right power in each iteration; + double step = ((x - 1) * (x - 1)) / ((x + 1) * (x + 1)); // Store step to not have to calculate it each time + double totalValue = 0; + double powe = 1; + double y; + for (uint32_t count = 0; count < 10; count++) { // Experimental number of 10 iterations + z *= step; + y = (1 / powe) * z; + totalValue = totalValue + y; + powe = powe + 2; + } + totalValue *= 2; +/* + char logxs[33]; + dtostrfd(x, 8, logxs); + double log1 = log(x); + char log1s[33]; + dtostrfd(log1, 8, log1s); + char log2s[33]; + dtostrfd(totalValue, 8, log2s); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("input %s, log %s, taylor %s"), logxs, log1s, log2s); +*/ + return totalValue; +} + +// Following code adapted from: http://www.ganssle.com/approx.htm +// ============================================================== +// The following code implements approximations to various trig functions. +// +// This is demo code to guide developers in implementing their own approximation +// software. This code is merely meant to illustrate algorithms. + +inline float sinf(float x) { return sin_52(x); } +inline float cosf(float x) { return cos_52(x); } +inline float tanf(float x) { return tan_56(x); } +inline float atanf(float x) { return atan_66(x); } +inline float asinf(float x) { return asinf1(x); } +inline float acosf(float x) { return acosf1(x); } +inline float sqrtf(float x) { return sqrt1(x); } +inline float powf(float x, float y) { return FastPrecisePow(x, y); } + +// Math constants we'll use +double const f_pi = 3.1415926535897932384626433; // f_pi +double const f_twopi = 2.0 * f_pi; // f_pi times 2 +double const f_two_over_pi = 2.0 / f_pi; // 2/f_pi +double const f_halfpi = f_pi / 2.0; // f_pi divided by 2 +double const f_threehalfpi = 3.0 * f_pi / 2.0; // f_pi times 3/2, used in tan routines +double const f_four_over_pi = 4.0 / f_pi; // 4/f_pi, used in tan routines +double const f_qtrpi = f_pi / 4.0; // f_pi/4.0, used in tan routines +double const f_sixthpi = f_pi / 6.0; // f_pi/6.0, used in atan routines +double const f_tansixthpi = tan(f_sixthpi); // tan(f_pi/6), used in atan routines +double const f_twelfthpi = f_pi / 12.0; // f_pi/12.0, used in atan routines +double const f_tantwelfthpi = tan(f_twelfthpi); // tan(f_pi/12), used in atan routines + +// ******************************************************************* +// *** +// *** Routines to compute sine and cosine to 5.2 digits of accuracy. +// *** +// ******************************************************************* +// +// cos_52s computes cosine (x) +// +// Accurate to about 5.2 decimal digits over the range [0, f_pi/2]. +// The input argument is in radians. +// +// Algorithm: +// cos(x)= c1 + c2*x**2 + c3*x**4 + c4*x**6 +// which is the same as: +// cos(x)= c1 + x**2(c2 + c3*x**2 + c4*x**4) +// cos(x)= c1 + x**2(c2 + x**2(c3 + c4*x**2)) +// +float cos_52s(float x) +{ + const float c1 = 0.9999932946; + const float c2 = -0.4999124376; + const float c3 = 0.0414877472; + const float c4 = -0.0012712095; + + float x2 = x * x; // The input argument squared + return (c1 + x2 * (c2 + x2 * (c3 + c4 * x2))); +} +// +// This is the main cosine approximation "driver" +// It reduces the input argument's range to [0, f_pi/2], +// and then calls the approximator. +// See the notes for an explanation of the range reduction. +// +float cos_52(float x) +{ + x = fmodf(x, f_twopi); // Get rid of values > 2* f_pi + if (x < 0) { x = -x; } // cos(-x) = cos(x) + int quad = int(x * (float)f_two_over_pi); // Get quadrant # (0 to 3) we're in + switch (quad) { + case 0: return cos_52s(x); + case 1: return -cos_52s((float)f_pi - x); + case 2: return -cos_52s(x-(float)f_pi); + case 3: return cos_52s((float)f_twopi - x); + } +} +// +// The sine is just cosine shifted a half-f_pi, so +// we'll adjust the argument and call the cosine approximation. +// +float sin_52(float x) +{ + return cos_52((float)f_halfpi - x); +} + +// ******************************************************************* +// *** +// *** Routines to compute tangent to 5.6 digits of accuracy. +// *** +// ******************************************************************* +// +// tan_56s computes tan(f_pi*x/4) +// +// Accurate to about 5.6 decimal digits over the range [0, f_pi/4]. +// The input argument is in radians. Note that the function +// computes tan(f_pi*x/4), NOT tan(x); it's up to the range +// reduction algorithm that calls this to scale things properly. +// +// Algorithm: +// tan(x)= x(c1 + c2*x**2)/(c3 + x**2) +// +float tan_56s(float x) +{ + const float c1 = -3.16783027; + const float c2 = 0.134516124; + const float c3 = -4.033321984; + + float x2 = x * x; // The input argument squared + return (x * (c1 + c2 * x2) / (c3 + x2)); +} +// +// This is the main tangent approximation "driver" +// It reduces the input argument's range to [0, f_pi/4], +// and then calls the approximator. +// See the notes for an explanation of the range reduction. +// Enter with positive angles only. +// +// WARNING: We do not test for the tangent approaching infinity, +// which it will at x=f_pi/2 and x=3*f_pi/2. If this is a problem +// in your application, take appropriate action. +// +float tan_56(float x) +{ + x = fmodf(x, (float)f_twopi); // Get rid of values >2 *f_pi + int octant = int(x * (float)f_four_over_pi); // Get octant # (0 to 7) + switch (octant){ + case 0: return tan_56s(x * (float)f_four_over_pi); + case 1: return 1.0f / tan_56s(((float)f_halfpi - x) * (float)f_four_over_pi); + case 2: return -1.0f / tan_56s((x-(float)f_halfpi) * (float)f_four_over_pi); + case 3: return - tan_56s(((float)f_pi - x) * (float)f_four_over_pi); + case 4: return tan_56s((x-(float)f_pi) * (float)f_four_over_pi); + case 5: return 1.0f / tan_56s(((float)f_threehalfpi - x) * (float)f_four_over_pi); + case 6: return -1.0f / tan_56s((x-(float)f_threehalfpi) * (float)f_four_over_pi); + case 7: return - tan_56s(((float)f_twopi - x) * (float)f_four_over_pi); + } +} + +// ******************************************************************* +// *** +// *** Routines to compute arctangent to 6.6 digits of accuracy. +// *** +// ******************************************************************* +// +// atan_66s computes atan(x) +// +// Accurate to about 6.6 decimal digits over the range [0, f_pi/12]. +// +// Algorithm: +// atan(x)= x(c1 + c2*x**2)/(c3 + x**2) +// +float atan_66s(float x) +{ + const float c1 = 1.6867629106; + const float c2 = 0.4378497304; + const float c3 = 1.6867633134; + + float x2 = x * x; // The input argument squared + return (x * (c1 + x2 * c2) / (c3 + x2)); +} +// +// This is the main arctangent approximation "driver" +// It reduces the input argument's range to [0, f_pi/12], +// and then calls the approximator. +// +float atan_66(float x) +{ + float y; // return from atan__s function + bool complement= false; // true if arg was >1 + bool region= false; // true depending on region arg is in + bool sign= false; // true if arg was < 0 + + if (x < 0) { + x = -x; + sign = true; // arctan(-x)=-arctan(x) + } + if (x > 1.0) { + x = 1.0 / x; // keep arg between 0 and 1 + complement = true; + } + if (x > (float)f_tantwelfthpi) { + x = (x - (float)f_tansixthpi) / (1 + (float)f_tansixthpi * x); // reduce arg to under tan(f_pi/12) + region = true; + } + + y = atan_66s(x); // run the approximation + if (region) { y += (float)f_sixthpi; } // correct for region we're in + if (complement) { y = (float)f_halfpi-y; } // correct for 1/x if we did that + if (sign) { y = -y; } // correct for negative arg + return (y); +} + +float asinf1(float x) +{ + float d = 1.0f - x * x; + if (d < 0.0f) { return NAN; } + return 2 * atan_66(x / (1 + sqrt1(d))); +} + +float acosf1(float x) +{ + float d = 1.0f - x * x; + if (d < 0.0f) { return NAN; } + float y = asinf1(sqrt1(d)); + if (x >= 0.0f) { + return y; + } else { + return (float)f_pi - y; + } +} + +// https://www.codeproject.com/Articles/69941/Best-Square-Root-Method-Algorithm-Function-Precisi +float sqrt1(const float x) +{ + union { + int i; + float x; + } u; + u.x = x; + u.i = (1 << 29) + (u.i >> 1) - (1 << 22); + + // Two Babylonian Steps (simplified from:) + // u.x = 0.5f * (u.x + x/u.x); + // u.x = 0.5f * (u.x + x/u.x); + u.x = u.x + x / u.x; + u.x = 0.25f * u.x + x / u.x; + + return u.x; +} diff --git a/sonoff/support_rotary.ino b/sonoff/support_rotary.ino index db88d8378..71b5e6fe3 100644 --- a/sonoff/support_rotary.ino +++ b/sonoff/support_rotary.ino @@ -17,8 +17,7 @@ along with this program. If not, see . */ -#define ROTARY_V1 -#ifdef ROTARY_V1 +#ifdef USE_LIGHT /*********************************************************************************************\ * Rotary support \*********************************************************************************************/ @@ -31,6 +30,9 @@ uint8_t rotary_last_position = 128; uint8_t interrupts_in_use = 0; uint8_t rotary_changed = 0; +//#define ROTARY_V1 +#ifdef ROTARY_V1 + /********************************************************************************************/ void update_position(void) @@ -59,6 +61,10 @@ void update_position(void) rotary_state = (s >> 2); } +#ifndef ARDUINO_ESP8266_RELEASE_2_3_0 // Fix core 2.5.x ISR not in IRAM Exception +void update_rotary(void) ICACHE_RAM_ATTR; +#endif // ARDUINO_ESP8266_RELEASE_2_3_0 + void update_rotary(void) { if (MI_DESK_LAMP == my_module_type){ @@ -149,3 +155,4 @@ void RotaryLoop(void) } #endif // ROTARY_V1 +#endif // USE_LIGHT diff --git a/sonoff/support_rtc.ino b/sonoff/support_rtc.ino index 9a36951f0..e55bff4c5 100644 --- a/sonoff/support_rtc.ino +++ b/sonoff/support_rtc.ino @@ -22,10 +22,11 @@ * Timezone by Jack Christensen (https://github.com/JChristensen/Timezone) \*********************************************************************************************/ -#define SECS_PER_MIN ((uint32_t)(60UL)) -#define SECS_PER_HOUR ((uint32_t)(3600UL)) -#define SECS_PER_DAY ((uint32_t)(SECS_PER_HOUR * 24UL)) -#define MINS_PER_HOUR ((uint32_t)(60UL)) +const uint32_t SECS_PER_MIN = 60UL; +const uint32_t SECS_PER_HOUR = 3600UL; +const uint32_t SECS_PER_DAY = SECS_PER_HOUR * 24UL; +const uint32_t MINS_PER_HOUR = 60UL; + #define LEAP_YEAR(Y) (((1970+Y)>0) && !((1970+Y)%4) && (((1970+Y)%100) || !((1970+Y)%400))) extern "C" { @@ -45,10 +46,16 @@ uint32_t standard_time = 0; uint32_t ntp_time = 0; uint32_t midnight = 0; uint32_t restart_time = 0; +int32_t drift_time = 0; int32_t time_timezone = 0; uint8_t midnight_now = 0; uint8_t ntp_sync_minute = 0; +int32_t DriftTime(void) +{ + return drift_time; +} + String GetBuildDateAndTime(void) { // "2017-03-07T11:08:02" - ISO8601:2004 @@ -61,7 +68,7 @@ String GetBuildDateAndTime(void) // sscanf(mdate, "%s %d %d", bdt, &day, &year); // Not implemented in 2.3.0 and probably too much code uint8_t i = 0; - for (char *str = strtok_r(mdate, " ", &p); str && i < 3; str = strtok_r(NULL, " ", &p)) { + for (char *str = strtok_r(mdate, " ", &p); str && i < 3; str = strtok_r(nullptr, " ", &p)) { switch (i++) { case 0: // Month smonth = str; @@ -355,6 +362,7 @@ void RtcSecond(void) ntp_time = sntp_get_current_timestamp(); if (ntp_time > 1451602800) { // Fix NTP bug in core 2.4.1/SDK 2.2.1 (returns Thu Jan 01 08:00:10 1970 after power on) ntp_force_sync = false; + if (utc_time > 1451602800) { drift_time = ntp_time - utc_time; } utc_time = ntp_time; ntp_sync_minute = 60; // Sync so block further requests if (restart_time == 0) { @@ -364,7 +372,11 @@ void RtcSecond(void) RtcTime.year = tmpTime.year + 1970; daylight_saving_time = RuleToTime(Settings.tflag[1], RtcTime.year); standard_time = RuleToTime(Settings.tflag[0], RtcTime.year); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION "(" D_UTC_TIME ") %s, (" D_DST_TIME ") %s, (" D_STD_TIME ") %s"), GetTime(0).c_str(), GetTime(2).c_str(), GetTime(3).c_str()); + + // Do not use AddLog here if syslog is enabled. UDP will force exception 9 +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION "(" D_UTC_TIME ") %s, (" D_DST_TIME ") %s, (" D_STD_TIME ") %s"), GetTime(0).c_str(), GetTime(2).c_str(), GetTime(3).c_str()); + ntp_synced_message = true; + if (local_time < 1451602800) { // 2016-01-01 rules_flag.time_init = 1; } else { diff --git a/sonoff/support_switch.ino b/sonoff/support_switch.ino index 7e84149ba..ca1d486fa 100644 --- a/sonoff/support_switch.ino +++ b/sonoff/support_switch.ino @@ -25,7 +25,7 @@ * Inspired by (https://github.com/OLIMEX/olimex-iot-firmware-esp8266/blob/master/olimex/user/user_switch2.c) \*********************************************************************************************/ -#define SWITCH_PROBE_INTERVAL 10 // Time in milliseconds between switch input probe +const uint8_t SWITCH_PROBE_INTERVAL = 10; // Time in milliseconds between switch input probe #include @@ -71,7 +71,7 @@ void SwitchProbe(void) uint8_t force_high = (Settings.switch_debounce % 50) &1; // 51, 101, 151 etc uint8_t force_low = (Settings.switch_debounce % 50) &2; // 52, 102, 152 etc - for (uint8_t i = 0; i < MAX_SWITCHES; i++) { + for (uint32_t i = 0; i < MAX_SWITCHES; i++) { if (pin[GPIO_SWT1 +i] < 99) { // Olimex user_switch2.c code to fix 50Hz induced pulses if (1 == digitalRead(pin[GPIO_SWT1 +i])) { @@ -111,7 +111,7 @@ void SwitchProbe(void) void SwitchInit(void) { switches_found = 0; - for (uint8_t i = 0; i < MAX_SWITCHES; i++) { + for (uint32_t i = 0; i < MAX_SWITCHES; i++) { lastwallswitch[i] = 1; // Init global to virtual switch state; if (pin[GPIO_SWT1 +i] < 99) { switches_found++; @@ -135,7 +135,7 @@ void SwitchHandler(uint8_t mode) uint8_t switchflag; uint16_t loops_per_second = 1000 / Settings.switch_debounce; - for (uint8_t i = 0; i < MAX_SWITCHES; i++) { + for (uint32_t i = 0; i < MAX_SWITCHES; i++) { if ((pin[GPIO_SWT1 +i] < 99) || (mode)) { if (holdwallswitch[i]) { diff --git a/sonoff/support_udp.ino b/sonoff/support_udp.ino new file mode 100644 index 000000000..eccc4681d --- /dev/null +++ b/sonoff/support_udp.ino @@ -0,0 +1,136 @@ +/* + support_udp.ino - Udp support for Sonoff-Tasmota + + Copyright (C) 2019 Heiko Krupp and Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef USE_EMULATION + +#define UDP_BUFFER_SIZE 200 // Max UDP buffer size needed for M-SEARCH message +#define UDP_MSEARCH_SEND_DELAY 1500 // Delay in ms before M-Search response is send + +#include +Ticker TickerMSearch; + +IPAddress udp_remote_ip; // M-Search remote IP address +uint16_t udp_remote_port; // M-Search remote port + +bool udp_connected = false; +bool udp_response_mutex = false; // M-Search response mutex to control re-entry + +/*********************************************************************************************\ + * UPNP/SSDP search targets +\*********************************************************************************************/ + +const char URN_BELKIN_DEVICE[] PROGMEM = "urn:belkin:device:**"; +const char UPNP_ROOTDEVICE[] PROGMEM = "upnp:rootdevice"; +const char SSDPSEARCH_ALL[] PROGMEM = "ssdpsearch:all"; +const char SSDP_ALL[] PROGMEM = "ssdp:all"; + +/*********************************************************************************************\ + * UDP support routines +\*********************************************************************************************/ + +bool UdpDisconnect(void) +{ + if (udp_connected) { + PortUdp.flush(); + WiFiUDP::stopAll(); + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPNP D_MULTICAST_DISABLED)); + udp_connected = false; + } + return udp_connected; +} + +bool UdpConnect(void) +{ + if (!udp_connected) { + // Simple Service Discovery Protocol (SSDP) + if (PortUdp.beginMulticast(WiFi.localIP(), IPAddress(239,255,255,250), 1900)) { + AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_UPNP D_MULTICAST_REJOINED)); + udp_response_mutex = false; + udp_connected = true; + } else { + AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_UPNP D_MULTICAST_JOIN_FAILED)); + udp_connected = false; + } + } + return udp_connected; +} + +void PollUdp(void) +{ + if (udp_connected) { + if (PortUdp.parsePacket()) { + char packet_buffer[UDP_BUFFER_SIZE]; // buffer to hold incoming UDP/SSDP packet + + int len = PortUdp.read(packet_buffer, UDP_BUFFER_SIZE -1); + packet_buffer[len] = 0; + + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("UDP: Packet (%d)"), len); +// AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("\n%s"), packet_buffer); + + // Simple Service Discovery Protocol (SSDP) + if (devices_present && !udp_response_mutex && (strstr_P(packet_buffer, PSTR("M-SEARCH")) != nullptr)) { + udp_response_mutex = true; + + udp_remote_ip = PortUdp.remoteIP(); + udp_remote_port = PortUdp.remotePort(); + +// AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("UDP: M-SEARCH Packet from %s:%d\n%s"), +// udp_remote_ip.toString().c_str(), udp_remote_port, packet_buffer); + + uint32_t response_delay = UDP_MSEARCH_SEND_DELAY + ((millis() &0x7) * 100); // 1500 - 2200 msec + + LowerCase(packet_buffer, packet_buffer); + RemoveSpace(packet_buffer); + +#ifdef USE_EMULATION_WEMO + if (EMUL_WEMO == Settings.flag2.emulation) { + if (strstr_P(packet_buffer, URN_BELKIN_DEVICE) != nullptr) { // type1 echo dot 2g, echo 1g's + TickerMSearch.attach_ms(response_delay, WemoRespondToMSearch, 1); + return; + } + else if ((strstr_P(packet_buffer, UPNP_ROOTDEVICE) != nullptr) || // type2 Echo 2g (echo & echo plus) + (strstr_P(packet_buffer, SSDPSEARCH_ALL) != nullptr) || + (strstr_P(packet_buffer, SSDP_ALL) != nullptr)) { + TickerMSearch.attach_ms(response_delay, WemoRespondToMSearch, 2); + return; + } + } +#endif // USE_EMULATION_WEMO + +#ifdef USE_EMULATION_HUE + if (EMUL_HUE == Settings.flag2.emulation) { + if ((strstr_P(packet_buffer, PSTR(":device:basic:1")) != nullptr) || + (strstr_P(packet_buffer, UPNP_ROOTDEVICE) != nullptr) || + (strstr_P(packet_buffer, SSDPSEARCH_ALL) != nullptr) || + (strstr_P(packet_buffer, SSDP_ALL) != nullptr)) { + TickerMSearch.attach_ms(response_delay, HueRespondToMSearch); + return; + } + } +#endif // USE_EMULATION_HUE + + udp_response_mutex = false; + } + + } + delay(1); + } +} + +#endif // USE_EMULATION diff --git a/sonoff/support_wifi.ino b/sonoff/support_wifi.ino index 2c7299991..08b8a36e2 100644 --- a/sonoff/support_wifi.ino +++ b/sonoff/support_wifi.ino @@ -22,15 +22,15 @@ \*********************************************************************************************/ #ifndef WIFI_RSSI_THRESHOLD -#define WIFI_RSSI_THRESHOLD 10 // Difference in dB between current network and scanned network +#define WIFI_RSSI_THRESHOLD 10 // Difference in dB between current network and scanned network #endif #ifndef WIFI_RESCAN_MINUTES -#define WIFI_RESCAN_MINUTES 44 // Number of minutes between wifi network rescan +#define WIFI_RESCAN_MINUTES 44 // Number of minutes between wifi network rescan #endif -#define WIFI_CONFIG_SEC 180 // seconds before restart -#define WIFI_CHECK_SEC 20 // seconds -#define WIFI_RETRY_OFFSET_SEC 20 // seconds +const uint8_t WIFI_CONFIG_SEC = 180; // seconds before restart +const uint8_t WIFI_CHECK_SEC = 20; // seconds +const uint8_t WIFI_RETRY_OFFSET_SEC = 20; // seconds /* // This worked for 2_5_0_BETA2 but fails since then. Waiting for a solution from core team (#4952) @@ -131,7 +131,7 @@ void WifiConfig(uint8_t type) { if (!wifi_config_type) { if ((WIFI_RETRY == type) || (WIFI_WAIT == type)) { return; } -#if defined(USE_WEBSERVER) && defined(USE_EMULATION) +#ifdef USE_EMULATION UdpDisconnect(); #endif // USE_EMULATION WiFi.disconnect(); // Solve possible Wifi hangs @@ -159,6 +159,7 @@ void WifiConfig(uint8_t type) #ifdef USE_SMARTCONFIG else if (WIFI_SMARTCONFIG == wifi_config_type) { AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_WCFG_1_SMARTCONFIG " " D_ACTIVE_FOR_3_MINUTES)); + WiFi.mode(WIFI_STA); // Disable AP mode WiFi.beginSmartConfig(); } #endif // USE_SMARTCONFIG @@ -210,7 +211,7 @@ void WifiBegin(uint8_t flag, uint8_t channel) { const char kWifiPhyMode[] = " BGN"; -#if defined(USE_WEBSERVER) && defined(USE_EMULATION) +#ifdef USE_EMULATION UdpDisconnect(); #endif // USE_EMULATION @@ -295,7 +296,7 @@ void WifiBeginAfterScan() if (wifi_scan_result > 0) { // Networks found - for (int8_t i = 0; i < wifi_scan_result; ++i) { + for (uint32_t i = 0; i < wifi_scan_result; ++i) { String ssid_scan; int32_t rssi_scan; @@ -307,7 +308,7 @@ void WifiBeginAfterScan() WiFi.getNetworkInfo(i, ssid_scan, sec_scan, rssi_scan, bssid_scan, chan_scan, hidden_scan); bool known = false; - uint8_t j; + uint32_t j; for (j = 0; j < 2; j++) { if (ssid_scan == Settings.sta_ssid[j]) { // SSID match known = true; @@ -331,7 +332,7 @@ void WifiBeginAfterScan() } wifi_scan_state = 0; // If bssid changed then (re)connect wifi - for (uint8_t i = 0; i < sizeof(wifi_bssid); i++) { + for (uint32_t i = 0; i < sizeof(wifi_bssid); i++) { if (last_bssid[i] != wifi_bssid[i]) { WifiBegin(ap, channel); // 0 (AP1), 1 (AP2) or 3 (default AP) break; @@ -564,7 +565,7 @@ void WifiCheck(uint8_t param) } else { WifiSetState(0); -#if defined(USE_WEBSERVER) && defined(USE_EMULATION) +#ifdef USE_EMULATION UdpDisconnect(); #endif // USE_EMULATION mdns_begun = 0; diff --git a/sonoff/xdrv_01_webserver.ino b/sonoff/xdrv_01_webserver.ino index 415d14e53..3653045ef 100644 --- a/sonoff/xdrv_01_webserver.ino +++ b/sonoff/xdrv_01_webserver.ino @@ -27,21 +27,21 @@ #define XDRV_01 1 -#define CHUNKED_BUFFER_SIZE 400 // Chunk buffer size - #ifndef WIFI_SOFT_AP_CHANNEL -#define WIFI_SOFT_AP_CHANNEL 1 // Soft Access Point Channel number between 1 and 11 as used by SmartConfig web GUI +#define WIFI_SOFT_AP_CHANNEL 1 // Soft Access Point Channel number between 1 and 11 as used by SmartConfig web GUI #endif -#define HTTP_REFRESH_TIME 2345 // milliseconds -#define HTTP_RESTART_RECONNECT_TIME 9000 // milliseconds -#define HTTP_OTA_RESTART_RECONNECT_TIME 20000 // milliseconds +const uint16_t CHUNKED_BUFFER_SIZE = 400; // Chunk buffer size (should be smaller than half mqtt_date size) + +const uint16_t HTTP_REFRESH_TIME = 2345; // milliseconds +#define HTTP_RESTART_RECONNECT_TIME 9000 // milliseconds +#define HTTP_OTA_RESTART_RECONNECT_TIME 20000 // milliseconds #include #include #ifdef USE_RF_FLASH -uint8_t *efm8bb1_update = NULL; +uint8_t *efm8bb1_update = nullptr; #endif // USE_RF_FLASH enum UploadTypes { UPL_TASMOTA, UPL_SETTINGS, UPL_EFM8BB1 }; @@ -55,9 +55,28 @@ const char HTTP_HEAD[] PROGMEM = ""; const char HTTP_HEAD_STYLE1[] PROGMEM = - "" - "Zeichenfläche 1 \ No newline at end of file diff --git a/tools/logo/TASMOTA_Symbol_Vector.svg b/tools/logo/TASMOTA_Symbol_Vector.svg new file mode 100644 index 000000000..43f86da17 --- /dev/null +++ b/tools/logo/TASMOTA_Symbol_Vector.svg @@ -0,0 +1 @@ +Element 1 \ No newline at end of file From 64b9e0a5c5a6f69b838ccd374adde50ddceb9f44 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Fri, 5 Jul 2019 13:53:26 +0200 Subject: [PATCH 025/428] Add development info to README Add development info to README --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index 578d1c8f7..a49fd9f47 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,15 @@ See [RELEASENOTES.md](https://github.com/arendst/Sonoff-Tasmota/blob/master/RELE In addition to the [release webpage](https://github.com/arendst/Sonoff-Tasmota/releases/latest) the binaries can also be downloaded from http://thehackbox.org/tasmota/release/ +### Development +[![Dev Version](https://img.shields.io/badge/development%20version-6.6.0.x-blue.svg)](https://github.com/arendst/Sonoff-Tasmota) +[![Download Dev](https://img.shields.io/badge/download-development-yellow.svg)](http://thehackbox.org/tasmota/) +[![Build Status](https://img.shields.io/travis/arendst/Sonoff-Tasmota.svg)](https://travis-ci.org/arendst/Sonoff-Tasmota) + +See [RELEASENOTES.md](https://github.com/arendst/Sonoff-Tasmota/blob/development/RELEASENOTES.md) for release information and [sonoff/_changelog.ino](https://github.com/arendst/Sonoff-Tasmota/blob/development/sonoff/_changelog.ino) for detailed change information. + +The development codebase is checked hourly for changes and if new commits have been merged and compile successfuly they will be posted at http://thehackbox.org/tasmota/ (this web address can be used for OTA too). It is important to note that these are based on the current development codebase and it is not recommended to flash it to devices used in production or which are hard to reach in the event that you need to manually flash the device if OTA failed. The last compiled commit number is also posted on the same page along with the current build status (if a firmware rebuild is in progress). + #### Disclaimer :warning: **DANGER OF ELECTROCUTION** :warning: From dd7c6b17617c7a86bb875d64395633f26aea07e7 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Fri, 5 Jul 2019 13:55:25 +0200 Subject: [PATCH 026/428] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a49fd9f47..c39889745 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ See [RELEASENOTES.md](https://github.com/arendst/Sonoff-Tasmota/blob/development The development codebase is checked hourly for changes and if new commits have been merged and compile successfuly they will be posted at http://thehackbox.org/tasmota/ (this web address can be used for OTA too). It is important to note that these are based on the current development codebase and it is not recommended to flash it to devices used in production or which are hard to reach in the event that you need to manually flash the device if OTA failed. The last compiled commit number is also posted on the same page along with the current build status (if a firmware rebuild is in progress). -#### Disclaimer +### Disclaimer :warning: **DANGER OF ELECTROCUTION** :warning: A Sonoff device is not a toy. It uses Mains AC so there is a danger of electrocution if not installed properly. If you don't know how to install it, please call an electrician. Remember: _**SAFETY FIRST**_. It is not worth risk to yourself, your family, and your home if you don't know exactly what you are doing. Never try to flash a Sonoff device while it is connected to MAINS AC. From abf248d02cbedc65a88cb62b22597f31cf2deb85 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Fri, 5 Jul 2019 15:54:00 +0200 Subject: [PATCH 027/428] Release 6.6 Release 6.6 --- RELEASENOTES.md | 181 ++++++++++++++++++++++------------------ sonoff/_changelog.ino | 190 ++++++++++++++++-------------------------- 2 files changed, 172 insertions(+), 199 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 1209b76bf..92eed5ef6 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -10,8 +10,13 @@ See [wiki migration path](https://github.com/arendst/Sonoff-Tasmota/wiki/Upgrade 3. Migrate to **Sonoff-Tasmota 5.14** 4. Migrate to **Sonoff-Tasmota 6.x** -## Core version 2.3.0 vs 2.4.2 -This release is based on ESP8266/Arduino library core 2.3.0 (again) as some people encountered wifi related issues on core 2.4.2. For others core 2.4.2 is working just fine. Both version are available from http://thehackbox.org/tasmota/release/ +## Support of TLS +TLS support for core 2.3.0 is removed. + +TLS is supported on core 2.4.2 and up. To save resources when TLS is enabled mDNS needs to be disabled. In addition to TLS using fingerprints now also user supplied CA certs and AWS IoT is supported. See full documentation on https://github.com/arendst/Sonoff-Tasmota/wiki/AWS-IoT + +## Core version 2.3.0 vs 2.4.2 vs 2.5.2 +This release is based on ESP8266/Arduino library core 2.3.0 as some people encountered wifi related issues on core 2.4.2 and 2.5.2. For others core 2.4.2 or 2.5.2 is working just fine. All version are available from http://thehackbox.org/tasmota/release/ ## Change in default initial configuration tool Firmware binary **sonoff-classic.bin** supports **WifiManager, Wps and SmartConfig** for initial configuration. The default tool is **Wps**. @@ -92,6 +97,7 @@ Module | Description 67 SP10 | Tuya SP10 Wifi Smart Switch with Energy Monitoring 68 WAGA CHCZ02MB | WAGA life CHCZ02MB Wifi Smart Switch with Energy Monitoring 69 SYF05 | Sunyesmart SYF05 RGBWW Wifi Led Bulb +70 Sonoff L1 | Sonoff L1 light strip ## Provided Binary Downloads The following binary downloads have been compiled with ESP8266/Arduino library core version **2.3.0**. @@ -107,6 +113,8 @@ The following binary downloads have been compiled with ESP8266/Arduino library c Core version **2.4.2** binaries can be found at http://thehackbox.org/tasmota/release/020402/ +Core version **2.5.2** binaries can be found at http://thehackbox.org/tasmota/release/020502/ + ## Available Features and Sensors | Feature or Sensor | minimal | basic | classic | sonoff | knx | sensors | display | Remarks @@ -118,9 +126,12 @@ Core version **2.4.2** binaries can be found at http://thehackbox.org/tasmota/re | USE_DOMOTICZ | - | - | x | x | x | x | - | | USE_HOME_ASSISTANT | - | - | - | x | x | x | - | | USE_MQTT_TLS | - | - | - | - | - | - | - | +| USE_MQTT_TLS_CA_CERT | - | - | - | - | - | - | - | +| USE_MQTT_AWS_IOT | - | - | - | - | - | - | - | | USE_KNX | - | - | - | - | x | - | - | | USE_WEBSERVER | x | x | x | x | x | x | x | WifiManager -| USE_EMULATION | - | x | x | x | - | x | - | +| USE_EMULATION_HUE | - | x | x | x | - | x | - | +| USE_EMULATION_WEMO | - | x | x | x | - | x | - | | USE_DISCOVERY | - | - | x | x | x | x | x | | WEBSERVER_ADVERTISE | - | - | x | x | x | x | x | | MQTT_HOST_DISCOVERY | - | - | x | x | x | x | x | @@ -128,12 +139,15 @@ Core version **2.4.2** binaries can be found at http://thehackbox.org/tasmota/re | USE_TIMERS_WEB | - | x | - | x | x | x | x | | USE_SUNRISE | - | x | - | x | x | x | x | | USE_RULES | - | x | - | x | x | x | x | +| USE_SCRIPT | - | - | - | - | - | - | - | | USE_EXPRESSION | - | - | - | - | - | - | - | | | | | | | | | | -| USE_ADC_VCC | x | x | x | x | x | - | x | +| USE_ADC_VCC | x | x | x | - | - | - | - | +| USE_COUNTER | - | - | - | x | x | x | x | | USE_DS18B20 | - | - | - | - | - | - | - | Single sensor | USE_DS18x20 | - | - | x | x | x | x | x | Multiple sensors | USE_DS18x20_LEGACY | - | - | - | - | - | - | - | Multiple sensors +| USE_DHT | - | - | x | x | x | x | x | | | | | | | | | | | Feature or Sensor | minimal | basic | classic | sonoff | knx | sensors | display | Remarks | USE_I2C | - | - | - | x | x | x | x | @@ -162,6 +176,10 @@ Core version **2.4.2** binaries can be found at http://thehackbox.org/tasmota/re | USE_MGC3130 | - | - | - | - | - | - | - | | USE_MAX44009 | - | - | - | - | - | - | - | | USE_SCD30 | - | - | - | - | - | x | - | +| USE_SPS30 | - | - | - | - | - | - | - | +| USE_ADE7953 | - | - | - | x | x | x | x | +| USE_VL53L0X | - | - | - | - | - | - | - | +| USE_MLX90614 | - | - | - | - | - | - | - | | | | | | | | | | | Feature or Sensor | minimal | basic | classic | sonoff | knx | sensors | display | Remarks | USE_SPI | - | - | - | - | - | - | x | @@ -206,81 +224,80 @@ Core version **2.4.2** binaries can be found at http://thehackbox.org/tasmota/re | USE_DISPLAY_EPAPER_29 | - | - | - | - | - | - | x | Disabled for core 2.3.0 ## Changelog -Version 6.5.0 20190319 - * Remove commands SetOption14 and SetOption63 as it has been superseded by command Interlock - * Remove command SetOption35 0-255 for mDNS start-up delay (#4793) - * Remove support for MQTT_LIBRARY_TYPE, MQTT_ARDUINOMQTT and MQTT_TASMOTAMQTT (#5474) - * Change webserver content handling from single String to small Chunks increasing RAM - * Change code use of boolean to bool and byte to uint8_t - * Change code uint8_t flags to bool flags - * Change sonoff_template.h layout regarding optional module flags like ADC0 - * Change sonoff_template.h module lay-out by removing non-configurable GPIOs - * Change button driver making it modular - * Change switch driver making it modular and introduce input filter (#4665, #4724) - * Change switch input detection by optimizing switch debounce (#4724) - * Change web authentication (#4865) - * Change image name BE_MINIMAL to FIRMWARE_MINIMAL and USE_xyz to FIRMWARE_xyz (#5106) - * Change GUI weblog from XML to plain text solving possible empty screens (#5154) - * Fix most compiler warnings - * Fix Display exception 28 when JSON value is NULL received - * Fix epaper driver (#4785) - * Fix HAss Sensor Discovery Software Watchdog restart (#4831, #4988) - * Fix allowable MAX_RULE_VARS to 16 (#4933) - * Fix mDNS addService (#4938, #4951) - * Fix HAss discovery of MHZ19(B) sensors (#4992) - * Fix some exceptions and watchdogs due to lack of stack space (#5215) - * Fix GUI wifi password acception starting with asteriks (*) (#5231, #5242) - * Fix command WebSend intermittent results (#5273, #5304) - * Fix additional characters in fallbacktopic, hostname and mqttclient on core 2.5.0 (#5359, #5417) - * Fix Energy TotalStartTime when commands EnergyReset0 and/or EnergyReset3 used (#5373) - * Fix DS18S20 temperature calculation (#5375) - * Fix float calculations in range from 0 to -1 (#5386) - * Fix exception on GUI Configure Logging and Configure Other (#5424) - * Add commands PowerCal, VoltageCal and CurrentCal for HLW8012, HJL01 and BL0937 based energy sensors - * Add command SerialDelimiter 128 to filter reception of only characters between ASCII 32 and 127 (#5131) - * Add command SSerialSend5 \ to SerialBridge - * Add command Interlock 0 / 1 / 1,2 3,4 .. to control interlock ON/OFF and add up to 8 relays in 1 to 4 interlock groups (#4910, #5014) - * Add command Template 255 to copy module configuration over to current active template and store as user template named Merged (#5371) - * Add command WifiConfig 7 to allow reset of device in AP mode without admin password (#5297) - * Add command SetOption36 to control boot loop default restoration (#4645, #5063) - * Add command SetOption37 for RGBCW color mapping (#5326) - * Add command SetOption55 0/1 and define MDNS_ENABLE to disable/enable mDNS (#4793, #4923) - * Add command SetOption62 0/1 to disable retain on Button or Switch hold messages (#5299) - * Add support for Smanergy KA10 Smart Wall Socket with Energy monitoring - * Add support for commands in sensor drivers - * Add support for MAX31855 K-Type thermocouple sensor using softSPI (#4764) - * Add support for Near Field Communication (NFC) controller PN532 using Serial (#4791, #5162) - * Add support for OBI Power Socket 2 (#4829) - * Add support for YTF IR Bridge (#4855) - * Add support for Mi LED Desk Lamp with rotary switch (#4887) - * Add support for Digoo DG-SP202 Smart Socket with Energy monitoring (#4891) - * Add support for MAX44009 Ambient Light sensor (#4907) - * Add support for inverted buttons and inverted buttons without pullup (#4914) - * Add support for Luminea ZX2820 Smart Socket with Energy monitoring (#4921) - * Add support for multiple ADS1115 I2C devices (#5083) - * Add support for online template change using command Template or GUI Configure Other (#5177) - * Add support for Korean language translations (#5344) - * Add support for sensor SCD30 (#5434) - * Add parameter CFG_HOLDER to status 1 message (#5206) - * Add SetOption32 until SetOption49 diagnostic information to Status 3 report as replacement for second property value in SetOption property name - * Add Resolution property to Status 3 report providing previous SetOption second value property - * Add property MqttCount to status 6 message representing number of Mqtt re-connections - * Add property LinkCount to state and status 11 message representing number of Wifi Link re-connections - * Add property Downtime to state and status 11 message representing the duration of wifi connection loss - * Add variable %timestamp% to rules (#4749) - * Add rule support for "==", "!=" ">=" and "<=" (#5122) - * Add rule expression enabled by define USE_EXPRESSION in my_user_config.h (#5210) - * Add Power status functionality to LED2 when configured leaving LED1 for Link status indication - * Add user configuration of HLW8012 and HJL-01/BL0937 Energy Monitoring as used in Sonoff Pow and many Tuya based devices - * Add user configuration of MCP39F501 Energy Monitoring as used in Shelly2 - * Add online template configuration using both commands and Configure Template menu option in GUI - * Add (S)SerialSend3 escape sequence \x to allow hexadecimal byte value (#3560, #4947) - * Add define DS18B20_INTERNAL_PULLUP to select internal input pullup when only one DS18B20 sensor is connected eliminating external resistor (#4738) - * Add button control when no relay configured (#4682) - * Add startup delay of 4 seconds to button control (#4829) - * Add core version conditional compile options to provided PWM files (#4917) - * Add resiliency to saved Settings (#5065) - * Add MHZ19 Temperature as Domoticz Temperature selection (#5128) - * Add HAss status sensor (#5139) - * Add status message to former declined group commands (#5145) - * Add 0x to IRRemote (SetOption29) and RCSwitch (SetOption28) received hexadecimal data (#5431) +Version 6.6.0 20190707 + * Remove support of TLS on core 2.3.0 and extent support on core 2.4.2 and up + * Refactor some defines to const + * Refactor webserver HTML input, button, textarea, and select name based on id + * Refactor webserver sensor data collection + * Refactor TLS based on BearSSL, warning breaking change for fingerprints validation + * Refactor management of lights, using classes and integers instead of floats + * Refactor UDP initial message handling from string to char using static memory and add debug info (#5505) + * Refactor IRsend and receive for 64-bit support (#5523) + * Refactor MQTT which might solve issue (#5755) + * Refactor IRSend by using heap when more than 199 values need to be send. May need increase of define MQTT_MAX_PACKET_SIZE too (#5950) + * Refactor double to float in rules, and replaced trigonometric functions from stdlib with smaller versions (#6005) + * Change pubsubclient MQTT_KEEPALIVE from 10 to 30 seconds for AWS IoT support + * Change gamma correction as default behavior, ie "Ledtable 1" + * Change PWM resolution from 8 to 10 bits for low brightness lights + * Change IRSend Panasonic protocol to 64-bit (#5523) + * Change ADC0 to enabled by default in my_user_config.h (#5671) + * Change define USE_EMULATION by USE_EMULATION_HUE and USE_EMULATION_WEMO (#5826) + * Change default PowerDelta from 80% to 0% on new installations (#5858, #5028, #4813, #4130, #4145, #3795, #3778, #3660, #3648) + * Fix display Bug in KNX webmenu for Physical Address + * Fix the Unescape() function and the SendSerial3 behaviour + * Fix webserver multiple Javascript window.onload functionality + * Fix TasmotaSerial at 9600 bps solving DFPlayer comms (#5528) + * Fix Configure Timer Web GUI (#5568) + * Fix Shelly 2.5 I2C address priority issue when VEML6070 code is present by disabling VEML6070 for Shelly 2.5 (#5592) + * Fix use of SerialDelimiter value 128 (#5634) + * Fix Sonoff Pow R2 / S31 invalid energy increments (#5789) + * Fix core 2.5.x ISR not in IRAM exception (#5837) + * Fix Philips Hue emulation Alexa issue by using part of MAC address for LightId (#5849) + * Fix missing white channel for WS2812 (#5869) + * Fix PZem startup issue (#5875) + * Fix exception 9 when syslog is enabled and NTP is just synced (#5917) + * Fix include of my_user_config.h in sonoff_aws_iot.cpp (#5930) + * Fix Toggle functionality to button double press when one button and two devices are detected (#5935) + * Fix channel command for dual dimmers (#5940) + * Fix not restoring white value on power off/power on (#5993) + * Add command AdcParam to control ADC0 Temperature and Light formula parameters + * Add command LedMask to assign which relay has access to power LED (#5602, #5612) + * Add extended LED power control using command LedPowerX where X is 1 to 4. Enabled when "LedLink(i)" is configured too (#5709) + * Add command Sensor20 1..255 to change Nova Fitness SDS01 working period in minutes (#5452) + * Add command SetOption38 6..255 to set IRReceive protocol detection sensitivity mimizing UNKNOWN protocols (#5853) + * Add command SetOption39 1..255 to control CSE7766 (Pow R2) or HLW8032 (Blitzwolf SHP5) handling of power loads below 6W. Default setting is 128 (#5756) + * Add command SetOption40 0..250 to disable button functionality if activated for over 0.1 second. Needs SetOption1 1 and SetOption13 0 (#5449) + * Add command SetOption63 0/1 to disable relay state feedback scan at restart (#5594, #5663) + * Add command SetOption64 0/1 to switch between "-" or "_" as sensor index separator impacting DS18X20, DHT, BMP and SHT3X sensor names (#5689) + * Add command SetOption65 0/1 and more Tuya Serial based device support (#5815) + * Add command WebColor to change GUI colors on the fly + * Add support for AWS IoT with TLS 1.2 on core 2.4.2 and up. Full doc here: https://github.com/arendst/Sonoff-Tasmota/wiki/AWS-IoT + * Add support for Badger HR-E Water Meter (#5539) + * Add support for Shelly 2.5 Energy and overtemp Monitoring (#5592) + * Add support for color and colortone for Philips Hue emulation via Alexa (#5600 #4809) + * Add support for Scripts as replacement for Rules. Default disabled but can be enabled in my_user_config.h (#5689) + * Add support for up to four LEDs related to four power outputs. Enabled when "LedLink(i)" is configured too (#5709) + * Add support for Shelly 1PM Template {"NAME":"Shelly 1PM","GPIO":[56,0,0,0,82,134,0,0,0,0,0,21,0],"FLAG":2,"BASE":18} (#5716) + * Add support for SPS30 Particle sensor thanks to Gerhard Mutz (#5830) + * Add support for VL53L0x time of flight sensor. Might interfere with TSL2561 using same I2C address (#5845) + * Add support for Sonoff L1 thanks to reef-actor (#6002) + * Add rule Http#Initialized + * Add rule System#Save executed just before a planned restart + * Add rule support for single JSON value pair like {"SSerialReceived":"on"} by expanding it to {"SSerialReceived":{"Data":"on"}} allowing for trigger SSerialReceived#Data=on (#5638) + * Add define USE_COUNTER to my_user_config.h to save space in sonoff-basic.bin and sonoff-minimal.bin + * Add define USE_DHT to my_user_config.h to save space in sonoff-basic.bin + * Add defines USE_EMULATION_WEMO and USE_EMULATION_HUE to my_user_config.h to control emulation features at compile time (#5826) + * Add Toggle functionality to button double press when more devices are detected + * Add device OverTemp (>73 Celsius) detection to Energy Monitoring devices with temperature sensor powering off all outputs + * Add Tuya Dimmer 10 second heartbeat serial packet required by some Tuya dimmer secondary MCUs + * Add all temperature, humidity and pressure for global access + * Add validation check when loading settings from flash + * Add HX711 weight restore after controlled restart or after power restore just before executing command Sensor34 7 (#5367, #5786) + * Add GUI hexadecimal color options in my_user_config.h (#5586) + * Add alternative IRSend command syntax IRSend raw,,
,
,,,, (#5610) + * Add user configurable ADC0 to Module and Template configuration compatible with current FLAG options (#5671) + * Add AriLux RF control GPIO option "ALux IrSel" (159) replacing "Led4i" (59) for full LED control (#5709) + * Add LED GPIO option "LedLink" (157) and "LedLinki" (158) to select dedicated link status LED (#5709) + * Add all 5 PWM channels individually adressable with LEDs. (#5741) + * Add reset of Energy values when connection to sensor is lost for over 4 seconds (#5874, #5881) + * Add checkbox to GUI password field enabling visibility during password entry only (#5934) \ No newline at end of file diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index 2040ab0cf..752dc3a0c 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -1,125 +1,81 @@ -/* - * 6.6.0 - * - * 6.5.0.16 20190611 - * Refactored TLS based on BearSSL, warning breaking change for fingerprints validation (see doc) - * Add checkbox to GUI password field enabling visibility during password entry only (#5934) - * Add using heap when more than 199 IRSend values need to be send. May need increase of define MQTT_MAX_PACKET_SIZE too (#5950) +/*********************************************************************************************\ + * 6.6.0 20190707 + * Remove support of TLS on core 2.3.0 and extent support on core 2.4.2 and up + * Refactor some defines to const + * Refactor webserver HTML input, button, textarea, and select name based on id + * Refactor webserver sensor data collection + * Refactor TLS based on BearSSL, warning breaking change for fingerprints validation + * Refactor management of lights, using classes and integers instead of floats + * Refactor UDP initial message handling from string to char using static memory and add debug info (#5505) + * Refactor IRsend and receive for 64-bit support (#5523) + * Refactor MQTT which might solve issue (#5755) + * Refactor IRSend by using heap when more than 199 values need to be send. May need increase of define MQTT_MAX_PACKET_SIZE too (#5950) + * Refactor double to float in rules, and replaced trigonometric functions from stdlib with smaller versions (#6005) + * Change pubsubclient MQTT_KEEPALIVE from 10 to 30 seconds for AWS IoT support + * Change gamma correction as default behavior, ie "Ledtable 1" + * Change PWM resolution from 8 to 10 bits for low brightness lights + * Change IRSend Panasonic protocol to 64-bit (#5523) + * Change ADC0 to enabled by default in my_user_config.h (#5671) + * Change define USE_EMULATION by USE_EMULATION_HUE and USE_EMULATION_WEMO (#5826) + * Change default PowerDelta from 80% to 0% on new installations (#5858, #5028, #4813, #4130, #4145, #3795, #3778, #3660, #3648) + * Fix display Bug in KNX webmenu for Physical Address + * Fix the Unescape() function and the SendSerial3 behaviour + * Fix webserver multiple Javascript window.onload functionality + * Fix TasmotaSerial at 9600 bps solving DFPlayer comms (#5528) + * Fix Configure Timer Web GUI (#5568) + * Fix Shelly 2.5 I2C address priority issue when VEML6070 code is present by disabling VEML6070 for Shelly 2.5 (#5592) + * Fix use of SerialDelimiter value 128 (#5634) + * Fix Sonoff Pow R2 / S31 invalid energy increments (#5789) + * Fix core 2.5.x ISR not in IRAM exception (#5837) + * Fix Philips Hue emulation Alexa issue by using part of MAC address for LightId (#5849) + * Fix missing white channel for WS2812 (#5869) + * Fix PZem startup issue (#5875) + * Fix exception 9 when syslog is enabled and NTP is just synced (#5917) + * Fix include of my_user_config.h in sonoff_aws_iot.cpp (#5930) + * Fix Toggle functionality to button double press when one button and two devices are detected (#5935) * Fix channel command for dual dimmers (#5940) + * Fix not restoring white value on power off/power on (#5993) + * Add command AdcParam to control ADC0 Temperature and Light formula parameters + * Add command LedMask to assign which relay has access to power LED (#5602, #5612) + * Add extended LED power control using command LedPowerX where X is 1 to 4. Enabled when "LedLink(i)" is configured too (#5709) + * Add command Sensor20 1..255 to change Nova Fitness SDS01 working period in minutes (#5452) + * Add command SetOption38 6..255 to set IRReceive protocol detection sensitivity mimizing UNKNOWN protocols (#5853) + * Add command SetOption39 1..255 to control CSE7766 (Pow R2) or HLW8032 (Blitzwolf SHP5) handling of power loads below 6W. Default setting is 128 (#5756) + * Add command SetOption40 0..250 to disable button functionality if activated for over 0.1 second. Needs SetOption1 1 and SetOption13 0 (#5449) + * Add command SetOption63 0/1 to disable relay state feedback scan at restart (#5594, #5663) + * Add command SetOption64 0/1 to switch between "-" or "_" as sensor index separator impacting DS18X20, DHT, BMP and SHT3X sensor names (#5689) + * Add command SetOption65 0/1 and more Tuya Serial based device support (#5815) + * Add command WebColor to change GUI colors on the fly + * Add support for AWS IoT with TLS 1.2 on core 2.4.2 and up. Full doc here: https://github.com/arendst/Sonoff-Tasmota/wiki/AWS-IoT + * Add support for Badger HR-E Water Meter (#5539) + * Add support for Shelly 2.5 Energy and overtemp Monitoring (#5592) + * Add support for color and colortone for Philips Hue emulation via Alexa (#5600 #4809) + * Add support for Scripts as replacement for Rules. Default disabled but can be enabled in my_user_config.h (#5689) + * Add support for up to four LEDs related to four power outputs. Enabled when "LedLink(i)" is configured too (#5709) + * Add support for Shelly 1PM Template {"NAME":"Shelly 1PM","GPIO":[56,0,0,0,82,134,0,0,0,0,0,21,0],"FLAG":2,"BASE":18} (#5716) + * Add support for SPS30 Particle sensor thanks to Gerhard Mutz (#5830) + * Add support for VL53L0x time of flight sensor. Might interfere with TSL2561 using same I2C address (#5845) + * Add support for Sonoff L1 thanks to reef-actor (#6002) + * Add rule Http#Initialized + * Add rule System#Save executed just before a planned restart + * Add rule support for single JSON value pair like {"SSerialReceived":"on"} by expanding it to {"SSerialReceived":{"Data":"on"}} allowing for trigger SSerialReceived#Data=on (#5638) * Add define USE_COUNTER to my_user_config.h to save space in sonoff-basic.bin and sonoff-minimal.bin * Add define USE_DHT to my_user_config.h to save space in sonoff-basic.bin - * Change TLS+AWS IoT optimization for speed, code and memory footprint - * Add command SetOption40 0..250 to disable button functionality if activated for over 0.1 second. Needs SetOption1 1 and SetOption13 0 (#5449) - * Change converted double to float in rules, and replaced trigonometric functions from stdlib with smaller versions saving 7k code space (#6005) - * Add support for Sonoff L1 thanks to reef-actor (#6002) - * Fix Not restoring white value on power off/power on (#5993) - * - * 6.5.0.15 20190606 - * Change pubsubclient MQTT_KEEPALIVE from 10 to 30 seconds in preparation of AWS IoT support - * Add support for AWS IoT with TLS 1.2 on core 2.5.2. Full doc here: https://github.com/arendst/Sonoff-Tasmota/wiki/AWS-IoT - * Add some MQTT housekeeping which might solve issue (#5755) - * Add command SetOption65 0/1 and more Tuya Serial based device support (#5815) - * Fix include of my_user_config.h in sonoff_aws_iot.cpp (#5930) - * Fix exception 9 when syslog is enabled and NTP is just synced (#5917) - * Fix Toggle functionality to button double press when one button and two devices are detected (#5935) - * - * 6.5.0.14 20190602 - * Change webserver HTML input, button, textarea, and select name based on id - * Fix webserver multiple Javascript window.onload functionality - * Fix PZem startup issue (#5875) - * Add command SetOption39 1..255 to control CSE7766 (Pow R2) or HLW8032 (Blitzwolf SHP5) handling of power loads below 6W. Default setting is 128 (#5756) + * Add defines USE_EMULATION_WEMO and USE_EMULATION_HUE to my_user_config.h to control emulation features at compile time (#5826) * Add Toggle functionality to button double press when more devices are detected - * - * 6.5.0.13 20190527 - * Add command SetOption38 6..255 to set IRReceive protocol detection sensitivity mimizing UNKNOWN protocols (#5853) - * Fix missing white channel for WS2812 (#5869) - * Add reset of Energy values when connection to sensor is lost for over 4 seconds (#5874, #5881) - * Work-around for Philips Hue emulation issue by using part of MAC address for LightId (#5849) - * Add support to Stage Arduino Core (next 2.6.0) - * - * 6.5.0.12 20190521 + * Add device OverTemp (>73 Celsius) detection to Energy Monitoring devices with temperature sensor powering off all outputs + * Add Tuya Dimmer 10 second heartbeat serial packet required by some Tuya dimmer secondary MCUs + * Add all temperature, humidity and pressure for global access + * Add validation check when loading settings from flash + * Add HX711 weight restore after controlled restart or after power restore just before executing command Sensor34 7 (#5367, #5786) + * Add GUI hexadecimal color options in my_user_config.h (#5586) + * Add alternative IRSend command syntax IRSend raw,,
,
,,,, (#5610) + * Add user configurable ADC0 to Module and Template configuration compatible with current FLAG options (#5671) * Add AriLux RF control GPIO option "ALux IrSel" (159) replacing "Led4i" (59) for full LED control (#5709) * Add LED GPIO option "LedLink" (157) and "LedLinki" (158) to select dedicated link status LED (#5709) - * Add support for up to four LEDs related to four power outputs. Enabled when "LedLink(i)" is configured too (#5709) - * Add extended LED power control using command LedPowerX where X is 1 to 4. Enabled when "LedLink(i)" is configured too (#5709) - * Fix core 2.5.x ISR not in IRAM exception (#5837) - * Add support for VL53L0x time of flight sensor. Might interfere with TSL2561 using same I2C address (#5845) - * Add command AdcParam to control ADC0 Temperature and Light formula parameters - * Change default PowerDelta from 80% to 0% on new installations (#5858, #5028, #4813, #4130, #4145, #3795, #3778, #3660, #3648) - * - * 6.5.0.11 20190517 - * Add command SetOption64 0/1 to switch between "-" or "_" as sensor index separator impacting DS18X20, DHT, BMP and SHT3X sensor names (#5689) - * Add initial support for Scripts as replacement for Rules. Default disabled but can be enabled in my_user_config.h (#5689) - * Add rule System#Save executed just before a planned restart - * Add HX711 weight restore after controlled restart or after power restore just before executing command Sensor34 7 (#5367, #5786) - * Remove define USE_EMULATION from my_user_config.h (#5826) - * Add defines USE_EMULATION_WEMO and USE_EMULATION_HUE to my_user_config.h to control emulation features at compile time (#5826) - * Add support for SPS30 Particle sensor thanks to Gerhard Mutz (#5830) - * - * 6.5.0.10 20190513 - * Enable ADC0 by default in my_user_config.h (#5671) - * Add user configurable ADC0 to Module and Template configuration compatible with current FLAG options (#5671) - * Add support for Shelly 1PM Template {"NAME":"Shelly 1PM","GPIO":[56,0,0,0,82,134,0,0,0,0,0,21,0],"FLAG":2,"BASE":18} (#5716) - * Fix Sonoff Pow R2 / S31 invalid energy increments (#5789) - * Add device OverTemp (>73 Celsius) detection to any Energy Monitoring device with temperature sensor powering off all outputs - * Add rule support for single JSON value pair like {"SSerialReceived":"on"} by expanding it to {"SSerialReceived":{"Data":"on"}} allowing for trigger SSerialReceived#Data=on (#5638) - * - * 6.5.0.9 20190418 - * Add command SetOption63 0/1 to disable relay state feedback scan at restart (#5594, #5663) - * Fix TasmotaSerial at 9600 bps solving DFPlayer comms (#5528) - * Fix Shelly 2.5 overtemp - * Set gamma correction as default behavior, ie "Ledtable 1" - * Refactored management of lights, using classes and integers instead of floats. - * Extend PWM resolution from 8 to 10 bits for low brightness lights - * Allow all 5 PWM channels individually adressable with LEDs. (#5741) - * Fixed inversion of WC/WW channels, back to RGBCW - * Fixed the Unescape() function and the SendSerial3 behaviour - * - * 6.5.0.8 20190413 - * Add Tuya Dimmer 10 second heartbeat serial packet required by some Tuya dimmer secondary MCUs - * Fix use of SerialDelimiter value 128 (#5634) - * Fix lost syslog connection regression from 6.5.0.4 - * Add Shelly 2.5 Energy Monitoring (#5592) - * Add all temperature, humidity and pressure for global access - * Add Shelly 2.5 overtemp functionality - * Fix Shelly 2.5 I2C address priority issue when VEML6070 code is present by disabling VEML6070 for Shelly 2.5 (#5592) - * Support for color and colortone for Philips Hue emulation via Alexa (#5600 #4809) - * - * 6.5.0.7 20190410 - * Add command LedMask to assign which relay has access to power LED (#5602, #5612) - * - * 6.5.0.6 20190409 - * Add WebColor parameters to Settings making them persistent and remove the need for using a rule - * Add alternative IRSend command syntax IRSend raw,,
,
,,,, (#5610) - * - * 6.5.0.5 20190406 - * Add compile time GUI hexadecimal only color options in my_user_config.h (#5586) - * Fix template activation and/or module selection regression from 6.5.0.4 (#5598) - * Add rule Http#Initialized - * Add command WebColor to change non-persistent GUI colors on the fly - Use a rule like: - rule3 on http#initialized do webcolor {"webcolor":["#eeeeee","#181818","#4f4f4f","#000000","#dddddd","#008000","#222222","#ff0000","#008000","#ffffff","#1fa3ec","#0e70a4","#d43535","#931f1f","#47c266","#5aaf6f","#ffffff","#999999"]} endon - or - rule3 on http#initialized do webcolor {"webcolor":["#eee","#181818","#4f4f4f","#000","#ddd","#009800","#222"]} endon - to make color changes persistent) - * - * 6.5.0.4 20190402 - * Fix Configure Timer Web GUI (#5568) - * Add validation check when loading settings from flash - * Fixed Display Bug in KNX webmenu for Physical Address - * - * 6.5.0.3 20190328 - * Add command Sensor20 1..255 to change Nova Fitness SDS01 working period in minutes (#5452) - * Change some defines to const - * Change IRsend and receive for 64-bit support (#5523) - * Change IRSend Panasonic protocol to 64-bit (#5523) - * - * 6.5.0.2 20190325 - * Change UDP initial message handling from string to char using static memory and add debug info (#5505) - * Add optional support for Badger HR-E Water Meter (#5539) - * - * 6.5.0.1 20190319 - * Change Web GUI sensor data collection + * Add all 5 PWM channels individually adressable with LEDs. (#5741) + * Add reset of Energy values when connection to sensor is lost for over 4 seconds (#5874, #5881) + * Add checkbox to GUI password field enabling visibility during password entry only (#5934) * * 6.5.0 20190319 * Remove commands SetOption14 and SetOption63 as it has been superseded by command Interlock @@ -1793,4 +1749,4 @@ * 1.0.5 20160310 * Initial public release * Show debug info by selecting option from IDE Tools Debug port: Serial - */ +\*********************************************************************************************/ From bbfc49055218a68f9e12042fcacf8cefaa5a174a Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Fri, 5 Jul 2019 16:03:29 +0200 Subject: [PATCH 028/428] Release 6.6 Release 6.6 --- RELEASENOTES.md | 41 ++++++++++++++++++++--------------------- sonoff/_changelog.ino | 1 - 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 92eed5ef6..fa20871e5 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -232,52 +232,51 @@ Version 6.6.0 20190707 * Refactor TLS based on BearSSL, warning breaking change for fingerprints validation * Refactor management of lights, using classes and integers instead of floats * Refactor UDP initial message handling from string to char using static memory and add debug info (#5505) - * Refactor IRsend and receive for 64-bit support (#5523) + * Refactor ``IRsend`` and receive for 64-bit support (#5523) * Refactor MQTT which might solve issue (#5755) - * Refactor IRSend by using heap when more than 199 values need to be send. May need increase of define MQTT_MAX_PACKET_SIZE too (#5950) + * Refactor ``IRSend`` by using heap when more than 199 values need to be send. May need increase of define MQTT_MAX_PACKET_SIZE too (#5950) * Refactor double to float in rules, and replaced trigonometric functions from stdlib with smaller versions (#6005) * Change pubsubclient MQTT_KEEPALIVE from 10 to 30 seconds for AWS IoT support * Change gamma correction as default behavior, ie "Ledtable 1" * Change PWM resolution from 8 to 10 bits for low brightness lights - * Change IRSend Panasonic protocol to 64-bit (#5523) + * Change ``IRSend`` Panasonic protocol to 64-bit (#5523) * Change ADC0 to enabled by default in my_user_config.h (#5671) * Change define USE_EMULATION by USE_EMULATION_HUE and USE_EMULATION_WEMO (#5826) - * Change default PowerDelta from 80% to 0% on new installations (#5858, #5028, #4813, #4130, #4145, #3795, #3778, #3660, #3648) + * Change default ``PowerDelta`` from 80% to 0% on new installations (#5858, #5028, #4813, #4130, #4145, #3795, #3778, #3660, #3648) * Fix display Bug in KNX webmenu for Physical Address - * Fix the Unescape() function and the SendSerial3 behaviour + * Fix the Unescape() function and the ``SendSerial3`` behaviour * Fix webserver multiple Javascript window.onload functionality * Fix TasmotaSerial at 9600 bps solving DFPlayer comms (#5528) * Fix Configure Timer Web GUI (#5568) * Fix Shelly 2.5 I2C address priority issue when VEML6070 code is present by disabling VEML6070 for Shelly 2.5 (#5592) - * Fix use of SerialDelimiter value 128 (#5634) + * Fix use of ``SerialDelimiter`` value 128 (#5634) * Fix Sonoff Pow R2 / S31 invalid energy increments (#5789) * Fix core 2.5.x ISR not in IRAM exception (#5837) * Fix Philips Hue emulation Alexa issue by using part of MAC address for LightId (#5849) * Fix missing white channel for WS2812 (#5869) * Fix PZem startup issue (#5875) * Fix exception 9 when syslog is enabled and NTP is just synced (#5917) - * Fix include of my_user_config.h in sonoff_aws_iot.cpp (#5930) * Fix Toggle functionality to button double press when one button and two devices are detected (#5935) - * Fix channel command for dual dimmers (#5940) + * Fix command ``Channel`` for dual dimmers (#5940) * Fix not restoring white value on power off/power on (#5993) - * Add command AdcParam to control ADC0 Temperature and Light formula parameters - * Add command LedMask to assign which relay has access to power LED (#5602, #5612) - * Add extended LED power control using command LedPowerX where X is 1 to 4. Enabled when "LedLink(i)" is configured too (#5709) - * Add command Sensor20 1..255 to change Nova Fitness SDS01 working period in minutes (#5452) - * Add command SetOption38 6..255 to set IRReceive protocol detection sensitivity mimizing UNKNOWN protocols (#5853) - * Add command SetOption39 1..255 to control CSE7766 (Pow R2) or HLW8032 (Blitzwolf SHP5) handling of power loads below 6W. Default setting is 128 (#5756) - * Add command SetOption40 0..250 to disable button functionality if activated for over 0.1 second. Needs SetOption1 1 and SetOption13 0 (#5449) - * Add command SetOption63 0/1 to disable relay state feedback scan at restart (#5594, #5663) - * Add command SetOption64 0/1 to switch between "-" or "_" as sensor index separator impacting DS18X20, DHT, BMP and SHT3X sensor names (#5689) - * Add command SetOption65 0/1 and more Tuya Serial based device support (#5815) - * Add command WebColor to change GUI colors on the fly + * Add command ``AdcParam`` to control ADC0 Temperature and Light formula parameters + * Add command ``LedMask`` to assign which relay has access to power LED (#5602, #5612) + * Add extended LED power control using command ``LedPowerX`` where X is 1 to 4. Enabled when "LedLink(i)" is configured too (#5709) + * Add command ``Sensor20 1..255`` to change Nova Fitness SDS01 working period in minutes (#5452) + * Add command ``SetOption38 6..255`` to set IRReceive protocol detection sensitivity mimizing UNKNOWN protocols (#5853) + * Add command ``SetOption39 1..255`` to control CSE7766 (Pow R2) or HLW8032 (Blitzwolf SHP5) handling of power loads below 6W. Default setting is 128 (#5756) + * Add command ``SetOption40 0..250`` to disable button functionality if activated for over 0.1 second. Needs SetOption1 1 and SetOption13 0 (#5449) + * Add command ``SetOption63 0/1`` to disable relay state feedback scan at restart (#5594, #5663) + * Add command ``SetOption64 0/1`` to switch between "-" or "_" as sensor index separator impacting DS18X20, DHT, BMP and SHT3X sensor names (#5689) + * Add command ``SetOption65 0/1`` and more Tuya Serial based device support (#5815) + * Add command ``WebColor`` to change GUI colors on the fly * Add support for AWS IoT with TLS 1.2 on core 2.4.2 and up. Full doc here: https://github.com/arendst/Sonoff-Tasmota/wiki/AWS-IoT * Add support for Badger HR-E Water Meter (#5539) * Add support for Shelly 2.5 Energy and overtemp Monitoring (#5592) * Add support for color and colortone for Philips Hue emulation via Alexa (#5600 #4809) * Add support for Scripts as replacement for Rules. Default disabled but can be enabled in my_user_config.h (#5689) * Add support for up to four LEDs related to four power outputs. Enabled when "LedLink(i)" is configured too (#5709) - * Add support for Shelly 1PM Template {"NAME":"Shelly 1PM","GPIO":[56,0,0,0,82,134,0,0,0,0,0,21,0],"FLAG":2,"BASE":18} (#5716) + * Add support for Shelly 1PM Template ``{"NAME":"Shelly 1PM","GPIO":[56,0,0,0,82,134,0,0,0,0,0,21,0],"FLAG":2,"BASE":18}`` (#5716) * Add support for SPS30 Particle sensor thanks to Gerhard Mutz (#5830) * Add support for VL53L0x time of flight sensor. Might interfere with TSL2561 using same I2C address (#5845) * Add support for Sonoff L1 thanks to reef-actor (#6002) @@ -294,7 +293,7 @@ Version 6.6.0 20190707 * Add validation check when loading settings from flash * Add HX711 weight restore after controlled restart or after power restore just before executing command Sensor34 7 (#5367, #5786) * Add GUI hexadecimal color options in my_user_config.h (#5586) - * Add alternative IRSend command syntax IRSend raw,,
,
,,,, (#5610) + * Add alternative ``IRSend`` command syntax ``IRSend raw,,
,
,,,,`` (#5610) * Add user configurable ADC0 to Module and Template configuration compatible with current FLAG options (#5671) * Add AriLux RF control GPIO option "ALux IrSel" (159) replacing "Led4i" (59) for full LED control (#5709) * Add LED GPIO option "LedLink" (157) and "LedLinki" (158) to select dedicated link status LED (#5709) diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index 752dc3a0c..1b257345f 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -31,7 +31,6 @@ * Fix missing white channel for WS2812 (#5869) * Fix PZem startup issue (#5875) * Fix exception 9 when syslog is enabled and NTP is just synced (#5917) - * Fix include of my_user_config.h in sonoff_aws_iot.cpp (#5930) * Fix Toggle functionality to button double press when one button and two devices are detected (#5935) * Fix channel command for dual dimmers (#5940) * Fix not restoring white value on power off/power on (#5993) From 3041367a36e3a048f5f84f69d620527ffa79ee8c Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Fri, 5 Jul 2019 16:07:44 +0200 Subject: [PATCH 029/428] Release 6.6 Release 6.6 --- RELEASENOTES.md | 1 + sonoff/_changelog.ino | 1 + 2 files changed, 2 insertions(+) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index fa20871e5..0c1e647ba 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -226,6 +226,7 @@ Core version **2.5.2** binaries can be found at http://thehackbox.org/tasmota/re ## Changelog Version 6.6.0 20190707 * Remove support of TLS on core 2.3.0 and extent support on core 2.4.2 and up + * Remove MQTT uptime message every hour * Refactor some defines to const * Refactor webserver HTML input, button, textarea, and select name based on id * Refactor webserver sensor data collection diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index 1b257345f..0b338c5fb 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -1,6 +1,7 @@ /*********************************************************************************************\ * 6.6.0 20190707 * Remove support of TLS on core 2.3.0 and extent support on core 2.4.2 and up + * Remove MQTT uptime message every hour * Refactor some defines to const * Refactor webserver HTML input, button, textarea, and select name based on id * Refactor webserver sensor data collection From fcb6f67dc5df841daf79e846802da6cdfdaf5058 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Fri, 5 Jul 2019 16:45:49 +0200 Subject: [PATCH 030/428] Release 6.6 Release 6.6 --- CONTRIBUTING.md | 3 +-- REFERENCE.md | 3 +-- RELEASENOTES.md | 3 +-- SUPPORT.md | 3 +-- TEMPLATE.md | 3 +-- 5 files changed, 5 insertions(+), 10 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 96e1c3173..529e7baf2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,5 @@ -Logo - # Contributing +Logo **Any contribution helps our team and makes Tasmota better for the entire community!** diff --git a/REFERENCE.md b/REFERENCE.md index 6d2c08747..d38e58354 100644 --- a/REFERENCE.md +++ b/REFERENCE.md @@ -1,6 +1,5 @@ -Logo - # Reference +Logo Tasmota backgound information. diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 0c1e647ba..06fcb95d2 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -1,6 +1,5 @@ -Logo - # RELEASE NOTES +Logo ## Migration Information See [wiki migration path](https://github.com/arendst/Sonoff-Tasmota/wiki/Upgrade#migration-path) for instructions how to migrate to a major version. Pay attention to the following version breaks due to dynamic settings updates: diff --git a/SUPPORT.md b/SUPPORT.md index 26c770b80..7ef7591fa 100644 --- a/SUPPORT.md +++ b/SUPPORT.md @@ -1,6 +1,5 @@ -Logo - # Support +Logo If you're looking for support on **Sonoff-Tasmota** there are some options available: diff --git a/TEMPLATE.md b/TEMPLATE.md index 33465a0e9..ecb39d15e 100644 --- a/TEMPLATE.md +++ b/TEMPLATE.md @@ -1,6 +1,5 @@ -Logo - # Template information +Logo Sonoff-Tasmota uses Device or Module information to control peripherals connected to GPIOs. This information is stored in the ``sonoff_template.h`` file as a device specific template. The template contains information about what GPIO should be connected to what peripheral and what GPIO may be configured online using the ``GPIO`` command or GUI Configure Module menu. In addition a device may need specific coding to process the data from these peripherals. The module number as provided by the ``Modules`` command is used to select this coding. From 618faa9f5a778a5de2e866dc46d0f6c6fa7e9b85 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Fri, 5 Jul 2019 16:52:27 +0200 Subject: [PATCH 031/428] Release 6.6 Release 6.6 --- CONTRIBUTING.md | 3 ++- REFERENCE.md | 3 ++- RELEASENOTES.md | 3 ++- SUPPORT.md | 3 ++- TEMPLATE.md | 3 ++- 5 files changed, 10 insertions(+), 5 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 529e7baf2..96e1c3173 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,7 @@ -# Contributing Logo +# Contributing + **Any contribution helps our team and makes Tasmota better for the entire community!** Everybody is welcome and invited to contribute to Sonoff-Tasmota Project by: diff --git a/REFERENCE.md b/REFERENCE.md index d38e58354..6d2c08747 100644 --- a/REFERENCE.md +++ b/REFERENCE.md @@ -1,6 +1,7 @@ -# Reference Logo +# Reference + Tasmota backgound information. ## Supported Smart Switch with Energy Monitoring GPIO usage diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 06fcb95d2..0c1e647ba 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -1,6 +1,7 @@ -# RELEASE NOTES Logo +# RELEASE NOTES + ## Migration Information See [wiki migration path](https://github.com/arendst/Sonoff-Tasmota/wiki/Upgrade#migration-path) for instructions how to migrate to a major version. Pay attention to the following version breaks due to dynamic settings updates: diff --git a/SUPPORT.md b/SUPPORT.md index 7ef7591fa..26c770b80 100644 --- a/SUPPORT.md +++ b/SUPPORT.md @@ -1,6 +1,7 @@ -# Support Logo +# Support + If you're looking for support on **Sonoff-Tasmota** there are some options available: ## Documentation: diff --git a/TEMPLATE.md b/TEMPLATE.md index ecb39d15e..33465a0e9 100644 --- a/TEMPLATE.md +++ b/TEMPLATE.md @@ -1,6 +1,7 @@ -# Template information Logo +# Template information + Sonoff-Tasmota uses Device or Module information to control peripherals connected to GPIOs. This information is stored in the ``sonoff_template.h`` file as a device specific template. The template contains information about what GPIO should be connected to what peripheral and what GPIO may be configured online using the ``GPIO`` command or GUI Configure Module menu. In addition a device may need specific coding to process the data from these peripherals. The module number as provided by the ``Modules`` command is used to select this coding. Starting with version 6.4.1.16 Sonoff-Tasmota Modules can be extended by users online using a template. To provide easy processing by Sonoff-Tasmota a user template is written as JSON text and could look like this: From ff64d1215416b3d71448630cd34856818ddfeef6 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Fri, 5 Jul 2019 16:55:42 +0200 Subject: [PATCH 032/428] Release 6.6 Release 6.6 --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c39889745..a49ad7768 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ -## Sonoff-Tasmota +Logo -LogoAlternative firmware for _ESP8266 based devices_ like [iTead](https://www.itead.cc/) _**Sonoff**_ with **web UI, rules and timers, OTA updates, custom device templates and sensor support**. Allows control over **MQTT**, **HTTP**, **Serial** and **KNX** for integrations with smart home systems. Written for Arduino IDE and PlatformIO. +## Sonoff-Tasmota +Alternative firmware for _ESP8266 based devices_ like [iTead](https://www.itead.cc/) _**Sonoff**_ with **web UI, rules and timers, OTA updates, custom device templates and sensor support**. Allows control over **MQTT**, **HTTP**, **Serial** and **KNX** for integrations with smart home systems. Written for Arduino IDE and PlatformIO. [![GitHub version](https://img.shields.io/github/release/arendst/Sonoff-Tasmota.svg)](https://github.com/arendst/Sonoff-Tasmota/releases/latest) [![GitHub download](https://img.shields.io/github/downloads/arendst/Sonoff-Tasmota/total.svg)](https://github.com/arendst/Sonoff-Tasmota/releases/latest) From 423a246d71a409a61132ca5069067bb4dbe1edcc Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Fri, 5 Jul 2019 16:59:25 +0200 Subject: [PATCH 033/428] Release 6.6 Release 6.6 --- README.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index a49ad7768..4bafb84f8 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ Logo -## Sonoff-Tasmota +# Sonoff-Tasmota Alternative firmware for _ESP8266 based devices_ like [iTead](https://www.itead.cc/) _**Sonoff**_ with **web UI, rules and timers, OTA updates, custom device templates and sensor support**. Allows control over **MQTT**, **HTTP**, **Serial** and **KNX** for integrations with smart home systems. Written for Arduino IDE and PlatformIO. [![GitHub version](https://img.shields.io/github/release/arendst/Sonoff-Tasmota.svg)](https://github.com/arendst/Sonoff-Tasmota/releases/latest) @@ -18,7 +18,7 @@ See [RELEASENOTES.md](https://github.com/arendst/Sonoff-Tasmota/blob/master/RELE In addition to the [release webpage](https://github.com/arendst/Sonoff-Tasmota/releases/latest) the binaries can also be downloaded from http://thehackbox.org/tasmota/release/ -### Development +## Development [![Dev Version](https://img.shields.io/badge/development%20version-6.6.0.x-blue.svg)](https://github.com/arendst/Sonoff-Tasmota) [![Download Dev](https://img.shields.io/badge/download-development-yellow.svg)](http://thehackbox.org/tasmota/) [![Build Status](https://img.shields.io/travis/arendst/Sonoff-Tasmota.svg)](https://travis-ci.org/arendst/Sonoff-Tasmota) @@ -27,32 +27,32 @@ See [RELEASENOTES.md](https://github.com/arendst/Sonoff-Tasmota/blob/development The development codebase is checked hourly for changes and if new commits have been merged and compile successfuly they will be posted at http://thehackbox.org/tasmota/ (this web address can be used for OTA too). It is important to note that these are based on the current development codebase and it is not recommended to flash it to devices used in production or which are hard to reach in the event that you need to manually flash the device if OTA failed. The last compiled commit number is also posted on the same page along with the current build status (if a firmware rebuild is in progress). -### Disclaimer +## Disclaimer :warning: **DANGER OF ELECTROCUTION** :warning: A Sonoff device is not a toy. It uses Mains AC so there is a danger of electrocution if not installed properly. If you don't know how to install it, please call an electrician. Remember: _**SAFETY FIRST**_. It is not worth risk to yourself, your family, and your home if you don't know exactly what you are doing. Never try to flash a Sonoff device while it is connected to MAINS AC. We don't take any responsibility nor liability for using this software nor for the installation or any tips, advice, videos, etc. given by any member of this site or any related site. -### Note +## Note Please do not ask to add new devices unless it requires additional code for new features. If the device is not listed as a module, try using [Templates](https://github.com/arendst/Sonoff-Tasmota/wiki/Templates) first. If it is not listed in the [Tasmota Device Templates Repository](http://blakadder.github.io/templates) create your own [Template](https://github.com/arendst/Sonoff-Tasmota/wiki/Templates#creating-your-template-). -### Quick Install +## Quick Install Download one of the released binaries from https://github.com/arendst/Sonoff-Tasmota/releases and flash it to your hardware as [documented in the wiki](https://github.com/arendst/Sonoff-Tasmota/wiki/Flashing). -### Important User Compilation Information +## Important User Compilation Information If you want to compile Sonoff-Tasmota yourself keep in mind the following: - Only Flash Mode **DOUT** is supported. Do not use Flash Mode DIO / QIO / QOUT as it might seem to brick your device. See [Wiki](https://github.com/arendst/Sonoff-Tasmota/wiki/Theo's-Tasmota-Tips) for background information. - Sonoff-Tasmota uses a 1M linker script WITHOUT spiffs **1M (no SPIFFS)** for optimal code space. If you compile using ESP/Arduino library 2.3.0 then download the provided new linker script to your Arduino IDE or Platformio base folder. Later version of ESP/Arduino library already contain the correct linker script. See [Wiki > Prerequisites](https://github.com/arendst/Sonoff-Tasmota/wiki/Prerequisites). - To make compile time changes to Sonoff-Tasmota it can use the ``user_config_override.h`` file. It assures keeping your settings when you download and compile a new version. To use ``user_config.override.h`` you will have to make a copy of the provided ``user_config_override_sample.h`` file and add your setting overrides. To enable the override file you will need to use a compile define as documented in the ``user_config_override_sample.h`` file. -### Version Information +## Version Information - Sonoff-Tasmota provides all (Sonoff) modules in one file and starts with module Sonoff Basic. - Once uploaded, select [Module](https://github.com/arendst/Sonoff-Tasmota/wiki/Modules) using the configuration webpage, the commands ```Modules``` and ```Module``` or configure the [Template](https://github.com/arendst/Sonoff-Tasmota/wiki/Templates) for your device - After reboot select config menu again or use commands ```GPIOs``` and ```GPIO``` to change GPIO with desired sensor. -### Migration Information +## Migration Information See [wiki migration path](https://github.com/arendst/Sonoff-Tasmota/wiki/Upgrade#migration-path) for instructions how to migrate to a major version. Pay attention to the following version breaks due to dynamic settings updates: 1. Migrate to **Sonoff-Tasmota 3.9.x** @@ -60,7 +60,7 @@ See [wiki migration path](https://github.com/arendst/Sonoff-Tasmota/wiki/Upgrade 3. Migrate to **Sonoff-Tasmota 5.14** 4. Migrate to **Sonoff-Tasmota 6.x** -### Support Information +## Support Information For a database of supported devices see [Tasmota Device Templates Repository](https://blakadder.github.io/templates) @@ -69,7 +69,7 @@ See [Wiki](https://github.com/arendst/Sonoff-Tasmota/wiki) for use instructions See [Community](https://groups.google.com/d/forum/sonoffusers) for forum.
Visit [Discord Chat](https://discord.gg/Ks2Kzd4) for discussions and troubleshooting. -### Contribute +## Contribute You can contribute to Sonoff-Tasmota by - providing Pull Requests (Features, Proof of Concepts, Language files or Fixes) - testing new released features and report issues @@ -78,9 +78,9 @@ You can contribute to Sonoff-Tasmota by [![donate](https://img.shields.io/badge/donate-PayPal-blue.svg)](https://paypal.me/tasmota) -### Credits +## Credits -#### Libraries Used +### Libraries Used Libraries used with Sonoff-Tasmota are: - [ESP8266 core for Arduino](https://github.com/esp8266/Arduino) - [Adafruit CCS811](https://github.com/adafruit/Adafruit_CCS811) @@ -107,7 +107,7 @@ Libraries used with Sonoff-Tasmota are: - [PubSubClient](https://github.com/knolleary/pubsubclient) - [rc-switch](https://github.com/sui77/rc-switch) -#### People inspiring me +### People inspiring me People helping to keep the show on the road: - David Lang providing initial issue resolution and code optimizations - Heiko Krupp for his IRSend, HTU21, SI70xx and Wemo/Hue emulation drivers @@ -137,6 +137,6 @@ People helping to keep the show on the road: - tmo for designing the official logo - Many more providing Tips, Wips, Pocs or PRs -### License +## License This program is licensed under GPL-3.0 From 7880c03b49bcde6e517dd30f43b6f718c57c1250 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Fri, 5 Jul 2019 17:02:34 +0200 Subject: [PATCH 034/428] Release 6.6 Release 6.6 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4bafb84f8..9a8345d56 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ See [RELEASENOTES.md](https://github.com/arendst/Sonoff-Tasmota/blob/master/RELE In addition to the [release webpage](https://github.com/arendst/Sonoff-Tasmota/releases/latest) the binaries can also be downloaded from http://thehackbox.org/tasmota/release/ ## Development -[![Dev Version](https://img.shields.io/badge/development%20version-6.6.0.x-blue.svg)](https://github.com/arendst/Sonoff-Tasmota) +[![Dev Version](https://img.shields.io/badge/development%20version-v6.6.0.x-blue.svg)](https://github.com/arendst/Sonoff-Tasmota) [![Download Dev](https://img.shields.io/badge/download-development-yellow.svg)](http://thehackbox.org/tasmota/) [![Build Status](https://img.shields.io/travis/arendst/Sonoff-Tasmota.svg)](https://travis-ci.org/arendst/Sonoff-Tasmota) From ef3c17655dabe162b1732f4fb98908bf02f09161 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Fri, 5 Jul 2019 17:04:42 +0200 Subject: [PATCH 035/428] Release 6.6 Release 6.6 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9a8345d56..63c82e3db 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ In addition to the [release webpage](https://github.com/arendst/Sonoff-Tasmota/r [![Download Dev](https://img.shields.io/badge/download-development-yellow.svg)](http://thehackbox.org/tasmota/) [![Build Status](https://img.shields.io/travis/arendst/Sonoff-Tasmota.svg)](https://travis-ci.org/arendst/Sonoff-Tasmota) -See [RELEASENOTES.md](https://github.com/arendst/Sonoff-Tasmota/blob/development/RELEASENOTES.md) for release information and [sonoff/_changelog.ino](https://github.com/arendst/Sonoff-Tasmota/blob/development/sonoff/_changelog.ino) for detailed change information. +See [sonoff/_changelog.ino](https://github.com/arendst/Sonoff-Tasmota/blob/development/sonoff/_changelog.ino) for detailed change information. The development codebase is checked hourly for changes and if new commits have been merged and compile successfuly they will be posted at http://thehackbox.org/tasmota/ (this web address can be used for OTA too). It is important to note that these are based on the current development codebase and it is not recommended to flash it to devices used in production or which are hard to reach in the event that you need to manually flash the device if OTA failed. The last compiled commit number is also posted on the same page along with the current build status (if a firmware rebuild is in progress). From e1993d32cdebb43b19bb50485d8f47c832e60024 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sat, 6 Jul 2019 12:51:35 +0200 Subject: [PATCH 036/428] Extent some char buffers to accomodate UTF-16 character sets Extent some char buffers to accomodate UTF-16 character sets (#6026) --- sonoff/language/ru-RU.h | 14 +++++++------- sonoff/sonoff.ino | 4 ++-- sonoff/xdrv_01_webserver.ino | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/sonoff/language/ru-RU.h b/sonoff/language/ru-RU.h index b76f0e4b4..8289a56f1 100644 --- a/sonoff/language/ru-RU.h +++ b/sonoff/language/ru-RU.h @@ -302,8 +302,8 @@ #define D_SINGLE_DEVICE "одиночное" #define D_MULTI_DEVICE "мульти" -#define D_CONFIGURE_TEMPLATE "Configure Template" -#define D_TEMPLATE_PARAMETERS "Template parameters" +#define D_CONFIGURE_TEMPLATE "Конфигурация Template" +#define D_TEMPLATE_PARAMETERS "Параметры Template" #define D_TEMPLATE_NAME "Name" #define D_BASE_TYPE "Based on" #define D_TEMPLATE_FLAGS "Options" @@ -386,7 +386,7 @@ #define D_3_RESPONSE_PACKETS_SENT "3 ответных пакета получено" // xdrv_07_domoticz.ino -#define D_DOMOTICZ_PARAMETERS "Domoticz parameters" +#define D_DOMOTICZ_PARAMETERS "Параметры Domoticz" #define D_DOMOTICZ_IDX "Idx" #define D_DOMOTICZ_KEY_IDX "Key idx" #define D_DOMOTICZ_SWITCH_IDX "Switch idx" @@ -403,8 +403,8 @@ #define D_DOMOTICZ_UPDATE_TIMER "Update timer" // xdrv_09_timers.ino -#define D_CONFIGURE_TIMER "Configure Timer" -#define D_TIMER_PARAMETERS "Timer parameters" +#define D_CONFIGURE_TIMER "Конфигурация Timer" +#define D_TIMER_PARAMETERS "Параметры Timer" #define D_TIMER_ENABLE "Enable Timers" #define D_TIMER_ARM "Arm" #define D_TIMER_TIME "Time" @@ -414,8 +414,8 @@ #define D_TIMER_ACTION "Action" // xdrv_10_knx.ino -#define D_CONFIGURE_KNX "Configure KNX" -#define D_KNX_PARAMETERS "KNX Parameters" +#define D_CONFIGURE_KNX "Конфигурация KNX" +#define D_KNX_PARAMETERS "Параметры KNX" #define D_KNX_GENERAL_CONFIG "General" #define D_KNX_PHYSICAL_ADDRESS "Physical Address" #define D_KNX_PHYSICAL_ADDRESS_NOTE "( Must be unique on the KNX network )" diff --git a/sonoff/sonoff.ino b/sonoff/sonoff.ino index c968af406..5a26a86c6 100755 --- a/sonoff/sonoff.ino +++ b/sonoff/sonoff.ino @@ -170,7 +170,7 @@ bool backlog_mutex = false; // Command backlog pending bool interlock_mutex = false; // Interlock power command pending bool stop_flash_rotate = false; // Allow flash configuration rotation bool blinkstate = false; // LED state -bool latest_uptime_flag = true; // Signal latest uptime +//bool latest_uptime_flag = true; // Signal latest uptime bool pwm_present = false; // Any PWM channel configured with SetOption15 0 bool dht_flg = false; // DHT configured bool i2c_flg = false; // I2C configured @@ -1801,7 +1801,7 @@ void PublishStatus(uint8_t payload) { uint8_t option = STAT; char stemp[MAX_FRIENDLYNAMES * (sizeof(Settings.friendlyname[0]) +MAX_FRIENDLYNAMES)]; - char stemp2[64]; + char stemp2[100]; // Workaround MQTT - TCP/IP stack queueing when SUB_PREFIX = PUB_PREFIX if (!strcmp(Settings.mqtt_prefix[0],Settings.mqtt_prefix[1]) && (!payload)) { option++; } // TELE diff --git a/sonoff/xdrv_01_webserver.ino b/sonoff/xdrv_01_webserver.ino index 3653045ef..2ce8595cc 100644 --- a/sonoff/xdrv_01_webserver.ino +++ b/sonoff/xdrv_01_webserver.ino @@ -800,10 +800,10 @@ void WSContentSendStyle(void) void WSContentButton(uint8_t title_index) { char action[4]; - char title[64]; + char title[100]; // Large to accomodate UTF-16 as used by Russian if (title_index <= BUTTON_RESET_CONFIGURATION) { - char confirm[64]; + char confirm[100]; WSContentSend_P(PSTR("

"), GetTextIndexed(action, sizeof(action), title_index, kButtonAction), GetTextIndexed(confirm, sizeof(confirm), title_index, kButtonConfirm), From b19689da76c8095a228300e911b2fc83b34f9647 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sat, 6 Jul 2019 14:57:26 +0200 Subject: [PATCH 037/428] Update RELEASENOTES.md --- RELEASENOTES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 0c1e647ba..d7084c559 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -1,4 +1,4 @@ -Logo +Logo # RELEASE NOTES From 9818f8b8195a63f8c1526e82cf08c0f6f43b7347 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Mon, 19 Aug 2019 11:57:19 +0200 Subject: [PATCH 038/428] Update to platformio core 4.0.0 Update to platformio core 4.0.0 --- platformio.ini | 107 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 78 insertions(+), 29 deletions(-) diff --git a/platformio.ini b/platformio.ini index f4be6ad3f..98b4ae83e 100644 --- a/platformio.ini +++ b/platformio.ini @@ -9,36 +9,37 @@ [platformio] src_dir = sonoff +build_dir = .pioenvs ; *** Uncomment one of the lines below to build/upload only one environment -;env_default = sonoff -;env_default = sonoff-minimal -;env_default = sonoff-basic -;env_default = sonoff-classic -;env_default = sonoff-knx -;env_default = sonoff-sensors -;env_default = sonoff-display -;env_default = sonoff-BG -;env_default = sonoff-BR -;env_default = sonoff-CN -;env_default = sonoff-CZ -;env_default = sonoff-DE -;env_default = sonoff-ES -;env_default = sonoff-FR -;env_default = sonoff-GR -;env_default = sonoff-HE -;env_default = sonoff-HU -;env_default = sonoff-IT -;env_default = sonoff-KO -;env_default = sonoff-NL -;env_default = sonoff-PL -;env_default = sonoff-PT -;env_default = sonoff-RU -;env_default = sonoff-SE -;env_default = sonoff-SK -;env_default = sonoff-TR -;env_default = sonoff-TW -;env_default = sonoff-UK +;default_envs = sonoff +;default_envs = sonoff-minimal +;default_envs = sonoff-basic +;default_envs = sonoff-classic +;default_envs = sonoff-knx +;default_envs = sonoff-sensors +;default_envs = sonoff-display +;default_envs = sonoff-BG +;default_envs = sonoff-BR +;default_envs = sonoff-CN +;default_envs = sonoff-CZ +;default_envs = sonoff-DE +;default_envs = sonoff-ES +;default_envs = sonoff-FR +;default_envs = sonoff-GR +;default_envs = sonoff-HE +;default_envs = sonoff-HU +;default_envs = sonoff-IT +;default_envs = sonoff-KO +;default_envs = sonoff-NL +;default_envs = sonoff-PL +;default_envs = sonoff-PT +;default_envs = sonoff-RU +;default_envs = sonoff-SE +;default_envs = sonoff-SK +;default_envs = sonoff-TR +;default_envs = sonoff-TW +;default_envs = sonoff-UK [esp82xx_defaults] build_flags = -D NDEBUG @@ -99,8 +100,10 @@ build_flags = ${esp82xx_defaults.build_flags} ; Code optimization see https://github.com/esp8266/Arduino/issues/5790#issuecomment-475672473 -O2 -DBEARSSL_SSL_BASIC +; nonos-sdk 22y + -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22y ; nonos-sdk 22x - -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x +; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x ; nonos-sdk-pre-v3 ; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK3 ; lwIP 1.4 @@ -127,6 +130,43 @@ build_flags = ${esp82xx_defaults.build_flags} ; -fexceptions ; -lstdc++-exc +[core_pre] +; *** Arduino Esp8266 core pre 2.6.x for Tasmota (mqtt reconnects fixed) +platform = https://github.com/Jason2866/platform-espressif8266.git#Tasmota +build_flags = ${esp82xx_defaults.build_flags} + -Wl,-Tesp8266.flash.1m.ld + -O2 + -DBEARSSL_SSL_BASIC +; nonos-sdk 22y + -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22y +; nonos-sdk 22x +; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x +; nonos-sdk-pre-v3 +; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK3 +; lwIP 1.4 +; -DPIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH +; lwIP 2 - Low Memory +; -DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY +; lwIP 2 - Higher Bandwidth +; -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH +; lwIP 2 - Higher Bandwitdh Low Memory no Features +; -DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY_LOW_FLASH +; lwIP 2 - Higher Bandwitdh no Features + -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH +; VTABLES in Flash (default) + -DVTABLES_IN_FLASH +; VTABLES in Heap +; -DVTABLES_IN_DRAM +; VTABLES in IRAM +; -DVTABLES_IN_IRAM +; enable one option set -> No exception recommended +; No exception code in firmware + -fno-exceptions + -lstdc++ +; Exception code in firmware /needs much space! +; -fexceptions +; -lstdc++-exc + [core_active] ; Select one core set for platform and build_flags platform = ${core_2_3_0.platform} @@ -137,6 +177,8 @@ build_flags = ${core_2_3_0.build_flags} ;build_flags = ${core_2_5_2.build_flags} ;platform = ${core_stage.platform} ;build_flags = ${core_stage.build_flags} +;platform = ${core_pre.platform} +;build_flags = ${core_pre.build_flags} [common] framework = arduino @@ -145,6 +187,13 @@ board_build.flash_mode = dout platform = ${core_active.platform} build_flags = ${core_active.build_flags} + +; *** Optional Debug messages +; -DDEBUG_TASMOTA_CORE +; -DDEBUG_TASMOTA_DRIVER +; -DDEBUG_TASMOTA_SENSOR + +; *** Optional Firmware configurations ; -DFIRMWARE_CLASSIC ; -DFIRMWARE_MINIMAL ; -DFIRMWARE_SENSORS From 1a6ead187d5864dd22ac32608f961bc71c854834 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Wed, 23 Oct 2019 13:11:53 +0200 Subject: [PATCH 039/428] Release 6.7 --- .gitignore | 2 + .travis.yml | 44 +- README.md | 20 +- RELEASENOTES.md | 298 +- SUPPORT.md | 2 +- TEMPLATE.md | 89 - TEMPLATES.md | 613 + arduino/version pre-2.6.0/boards.txt | 6448 ++++++++++ arduino/version pre-2.6.0/platform.txt | 166 + build-container/Dockerfile | 24 + build-container/README.md | 26 + build-container/entrypoint.sh | 35 + .../init_pio_tasmota/platformio.ini | 30 + build-container/init_pio_tasmota/src/main.cpp | 3 + lib/A4988_Stepper/README.adoc | 19 + lib/A4988_Stepper/keywords.txt | 24 + lib/A4988_Stepper/library.properties | 9 + lib/A4988_Stepper/src/A4988_Stepper.cpp | 155 + lib/A4988_Stepper/src/A4988_Stepper.h | 73 + lib/AT24C256/Eeprom24C128_256.cpp | 334 + lib/AT24C256/Eeprom24C128_256.h | 212 + .../Adafruit_SPITFT.cpp | 555 - .../Adafruit_SPITFT.h | 125 - .../Adafruit_SPITFT_Macros.h | 118 - .../.gitignore | 0 .../.travis.yml | 3 +- .../Adafruit_GFX.cpp | 496 +- .../Adafruit_GFX.h | 208 +- .../Adafruit_SPITFT.cpp | 2217 ++++ .../Adafruit_SPITFT.h | 519 + .../Adafruit_SPITFT_Macros.h | 6 + .../Fonts/FreeMono12pt7b.h | 0 .../Fonts/FreeMono18pt7b.h | 0 .../Fonts/FreeMono24pt7b.h | 0 .../Fonts/FreeMono9pt7b.h | 0 .../Fonts/FreeMonoBold12pt7b.h | 0 .../Fonts/FreeMonoBold18pt7b.h | 0 .../Fonts/FreeMonoBold24pt7b.h | 0 .../Fonts/FreeMonoBold9pt7b.h | 0 .../Fonts/FreeMonoBoldOblique12pt7b.h | 0 .../Fonts/FreeMonoBoldOblique18pt7b.h | 0 .../Fonts/FreeMonoBoldOblique24pt7b.h | 0 .../Fonts/FreeMonoBoldOblique9pt7b.h | 0 .../Fonts/FreeMonoOblique12pt7b.h | 0 .../Fonts/FreeMonoOblique18pt7b.h | 0 .../Fonts/FreeMonoOblique24pt7b.h | 0 .../Fonts/FreeMonoOblique9pt7b.h | 0 .../Fonts/FreeSans12pt7b.h | 0 .../Fonts/FreeSans18pt7b.h | 0 .../Fonts/FreeSans24pt7b.h | 0 .../Fonts/FreeSans9pt7b.h | 0 .../Fonts/FreeSansBold12pt7b.h | 0 .../Fonts/FreeSansBold18pt7b.h | 0 .../Fonts/FreeSansBold24pt7b.h | 0 .../Fonts/FreeSansBold9pt7b.h | 0 .../Fonts/FreeSansBoldOblique12pt7b.h | 0 .../Fonts/FreeSansBoldOblique18pt7b.h | 0 .../Fonts/FreeSansBoldOblique24pt7b.h | 0 .../Fonts/FreeSansBoldOblique9pt7b.h | 0 .../Fonts/FreeSansOblique12pt7b.h | 0 .../Fonts/FreeSansOblique18pt7b.h | 0 .../Fonts/FreeSansOblique24pt7b.h | 0 .../Fonts/FreeSansOblique9pt7b.h | 0 .../Fonts/FreeSerif12pt7b.h | 0 .../Fonts/FreeSerif18pt7b.h | 0 .../Fonts/FreeSerif24pt7b.h | 0 .../Fonts/FreeSerif9pt7b.h | 0 .../Fonts/FreeSerifBold12pt7b.h | 0 .../Fonts/FreeSerifBold18pt7b.h | 0 .../Fonts/FreeSerifBold24pt7b.h | 0 .../Fonts/FreeSerifBold9pt7b.h | 0 .../Fonts/FreeSerifBoldItalic12pt7b.h | 0 .../Fonts/FreeSerifBoldItalic18pt7b.h | 0 .../Fonts/FreeSerifBoldItalic24pt7b.h | 0 .../Fonts/FreeSerifBoldItalic9pt7b.h | 0 .../Fonts/FreeSerifItalic12pt7b.h | 0 .../Fonts/FreeSerifItalic18pt7b.h | 0 .../Fonts/FreeSerifItalic24pt7b.h | 0 .../Fonts/FreeSerifItalic9pt7b.h | 0 .../Fonts/Org_01.h | 0 .../Fonts/Picopixel.h | 0 .../Fonts/Tiny3x3a2pt7b.h} | 0 .../Fonts/TomThumb.h | 0 .../README.md | 2 +- .../examples/mock_ili9341/mock_ili9341.ino | 2 +- .../fontconvert/Makefile | 0 .../fontconvert/fontconvert.c | 14 + .../fontconvert/fontconvert_win.md | 0 .../fontconvert/makefonts.sh | 0 .../gfxfont.h | 0 .../glcdfont.c | 4 + .../library.properties | 2 +- .../license.txt | 0 .../Adafruit_MAX31865.cpp | 286 + .../Adafruit_MAX31865.h | 99 + lib/Adafruit_MAX31865-1.1.0-custom/README.md | 2 + lib/Adafruit_MAX31865-1.1.0-custom/README.txt | 16 + .../examples/max31865/max31865.ino | 74 + .../library.properties | 9 + .../Adafruit_SH1106.cpp | 291 + .../Adafruit_SH1106.h | 154 + .../LICENSE.txt} | 0 lib/Adafruit_SH1106-gemu-1.0/README.md | 16 + .../sh1106_128x64_i2c/sh1106_128x64_i2c.ino} | 56 +- .../sh1106_128x64_spi/sh1106_128x64_spi.ino} | 37 +- .../Adafruit_SSD1306.cpp | 729 -- lib/Adafruit_SSD1306-1.1.2/Adafruit_SSD1306.h | 186 - lib/Adafruit_SSD1306-1.1.2/README.md | 32 - lib/Adafruit_SSD1306-1.1.2/README.txt | 24 - .../ssd1306_128x32_i2c/ssd1306_128x32_i2c.ino | 375 - .../ssd1306_128x32_spi/ssd1306_128x32_spi.ino | 368 - lib/Adafruit_SSD1306-1.1.2/library.properties | 9 - .../.github/ISSUE_TEMPLATE.md | 0 .../.github/PULL_REQUEST_TEMPLATE.md | 0 .../.gitignore | 4 + .../.travis.yml | 29 + .../Adafruit_SSD1306.cpp | 1139 ++ .../Adafruit_SSD1306.h | 189 + lib/Adafruit_SSD1306-1.3.0-gemu-1.1/README.md | 54 + .../OLED_featherwing/OLED_featherwing.ino | 79 + .../ssd1306_128x32_i2c/ssd1306_128x32_i2c.ino | 410 + .../ssd1306_128x32_spi/ssd1306_128x32_spi.ino | 423 + .../ssd1306_128x64_i2c/ssd1306_128x64_i2c.ino | 410 + .../ssd1306_128x64_spi/ssd1306_128x64_spi.ino | 424 + .../library.properties | 9 + .../license.txt | 26 + lib/Adafruit_SSD1306-1.3.0-gemu-1.1/splash.h | 108 + lib/Adafruit_SSD1351-gemu-1.0/README.md | 2 + lib/Adafruit_SSD1351-gemu-1.0/SSD1351.cpp | 520 + lib/Adafruit_SSD1351-gemu-1.0/SSD1351.h | 129 + lib/Adafruit_SSD1351-gemu-1.0/Tiger.c | 42 + lib/Adafruit_SSD1351-gemu-1.0/Tiger.rgb | Bin 0 -> 21764 bytes lib/Adafruit_SSD1351-gemu-1.0/keywords.txt | 30 + .../library.properties | 9 + lib/Adafruit_SSD1351-gemu-1.0/spi_register.h | 189 + lib/ArduinoHexParse/README.md | 3 + lib/ArduinoHexParse/keywords.txt | 25 + lib/ArduinoHexParse/library.json | 12 + lib/ArduinoHexParse/library.properties | 9 + lib/ArduinoHexParse/src/ArduinoHexParse.cpp | 134 + lib/ArduinoHexParse/src/ArduinoHexParse.h | 47 + lib/FT6236-gemu-1.0/FT6236.cpp | 159 + lib/FT6236-gemu-1.0/FT6236.h | 28 + lib/IRremoteESP8266-2.6.0/.travis.yml | 66 - .../examples/ControlSamsungAC/platformio.ini | 19 - .../examples/IRGCSendDemo/platformio.ini | 19 - .../examples/IRGCTCPServer/platformio.ini | 19 - .../examples/IRMQTTServer/platformio.ini | 42 - .../examples/IRServer/platformio.ini | 19 - .../examples/IRrecvDemo/platformio.ini | 19 - .../examples/IRrecvDump/platformio.ini | 19 - .../examples/IRrecvDumpV2/platformio.ini | 19 - .../examples/IRsendDemo/platformio.ini | 19 - .../examples/IRsendProntoDemo/platformio.ini | 19 - .../JVCPanasonicSendDemo/platformio.ini | 19 - .../examples/LGACSend/platformio.ini | 19 - .../examples/TurnOnArgoAC/platformio.ini | 19 - .../examples/TurnOnDaikinAC/platformio.ini | 19 - .../examples/TurnOnFujitsuAC/platformio.ini | 19 - .../TurnOnKelvinatorAC/platformio.ini | 19 - .../TurnOnMitsubishiAC/platformio.ini | 19 - .../TurnOnMitsubishiHeavyAc/platformio.ini | 19 - .../examples/TurnOnPanasonicAC/platformio.ini | 19 - .../examples/TurnOnToshibaAC/platformio.ini | 19 - .../examples/TurnOnTrotecAC/platformio.ini | 19 - lib/IRremoteESP8266-2.6.0/platformio.ini | 29 - lib/IRremoteESP8266-2.6.0/src/IRac.cpp | 1125 -- lib/IRremoteESP8266-2.6.0/src/IRutils.h | 48 - lib/IRremoteESP8266-2.6.0/src/ir_Argo.cpp | 264 - lib/IRremoteESP8266-2.6.0/src/ir_Daikin.cpp | 1712 --- lib/IRremoteESP8266-2.6.0/src/ir_Daikin.h | 444 - lib/IRremoteESP8266-2.6.0/src/ir_Electra.cpp | 117 - lib/IRremoteESP8266-2.6.0/src/ir_Fujitsu.cpp | 556 - lib/IRremoteESP8266-2.6.0/src/ir_Gree.h | 141 - lib/IRremoteESP8266-2.6.0/src/ir_LG.h | 17 - lib/IRremoteESP8266-2.6.0/src/ir_Sharp.cpp | 267 - lib/IRremoteESP8266-2.6.0/src/ir_Trotec.cpp | 162 - .../test/IRrecv_test.cpp | 561 - .../test/ir_Electra_test.cpp | 99 - .../test/ir_Fujitsu_test.cpp | 560 - .../.github/CONTRIBUTING.md | 6 +- .../.github/Contributors.md | 4 +- .../.github/issue_template.md | 8 +- .../.gitignore | 7 + .../.gitmodules | 0 .../.style.yapf | 0 lib/IRremoteESP8266-2.6.5/.travis.yml | 74 + .../CPPLINT.cfg | 0 .../LICENSE.txt | 0 .../README.md | 31 +- .../ReleaseNotes.md | 141 + .../SupportedProtocols.md | 141 + .../CommonAcControl/CommonAcControl.ino | 81 + .../examples/CommonAcControl/platformio.ini | 18 + .../ControlSamsungAC/ControlSamsungAC.ino | 31 +- .../examples/ControlSamsungAC/platformio.ini | 18 + .../DumbIRRepeater/DumbIRRepeater.ino | 130 + .../examples/DumbIRRepeater/platformio.ini | 18 + .../examples/IRGCSendDemo/IRGCSendDemo.ino | 4 +- .../examples/IRGCSendDemo/platformio.ini | 18 + .../examples/IRGCTCPServer/IRGCTCPServer.ino | 7 +- .../examples/IRGCTCPServer/platformio.ini | 18 + .../examples/IRMQTTServer/IRMQTTServer.h | 227 +- .../examples/IRMQTTServer/IRMQTTServer.ino | 2135 ++-- .../examples/IRMQTTServer/platformio.ini | 62 + .../examples/IRServer/IRServer.ino | 36 +- .../examples/IRServer/platformio.ini | 18 + .../examples/IRrecvDemo/IRrecvDemo.ino | 4 +- .../examples/IRrecvDemo/platformio.ini | 18 + .../examples/IRrecvDump/IRrecvDump.ino | 2 - .../examples/IRrecvDump/platformio.ini | 18 + .../examples/IRrecvDumpV2/IRrecvDumpV2.ino | 211 +- .../examples/IRrecvDumpV2/platformio.ini | 18 + .../examples/IRsendDemo/IRsendDemo.ino | 8 +- .../examples/IRsendDemo/platformio.ini | 18 + .../IRsendProntoDemo/IRsendProntoDemo.ino | 4 +- .../examples/IRsendProntoDemo/platformio.ini | 18 + .../JVCPanasonicSendDemo.ino | 4 +- .../JVCPanasonicSendDemo/platformio.ini | 18 + .../examples/LGACSend/LGACSend.ino | 0 .../examples/LGACSend/platformio.ini | 18 + .../SmartIRRepeater/SmartIRRepeater.ino | 143 + .../examples/SmartIRRepeater/platformio.ini | 18 + .../examples/TurnOnArgoAC/TurnOnArgoAC.ino | 6 +- .../examples/TurnOnArgoAC/platformio.ini | 18 + .../TurnOnDaikinAC/TurnOnDaikinAC.ino | 4 +- .../examples/TurnOnDaikinAC/platformio.ini | 18 + .../TurnOnFujitsuAC/TurnOnFujitsuAC.ino | 8 +- .../examples/TurnOnFujitsuAC/platformio.ini | 18 + .../TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino | 4 +- .../TurnOnKelvinatorAC/platformio.ini | 18 + .../TurnOnMitsubishiAC/TurnOnMitsubishiAC.ino | 4 +- .../TurnOnMitsubishiAC/platformio.ini | 18 + .../TurnOnMitsubishiHeavyAc.ino | 4 +- .../TurnOnMitsubishiHeavyAc/platformio.ini | 18 + .../TurnOnPanasonicAC/TurnOnPanasonicAC.ino | 4 +- .../examples/TurnOnPanasonicAC/platformio.ini | 18 + .../TurnOnToshibaAC/TurnOnToshibaAC.ino | 4 +- .../examples/TurnOnToshibaAC/platformio.ini | 18 + .../TurnOnTrotecAC/TurnOnTrotecAC.ino | 4 +- .../examples/TurnOnTrotecAC/platformio.ini | 18 + .../keywords.txt | 584 +- .../library.json | 20 +- .../library.properties | 12 +- .../pylintrc | 0 .../src/CPPLINT.cfg | 0 lib/IRremoteESP8266-2.6.5/src/IRac.cpp | 2084 ++++ .../src/IRac.h | 135 +- .../src/IRrecv.cpp | 475 +- .../src/IRrecv.h | 233 +- .../src/IRremoteESP8266.h | 212 +- .../src/IRsend.cpp | 398 +- .../src/IRsend.h | 233 +- .../src/IRtimer.cpp | 0 .../src/IRtimer.h | 0 .../src/IRutils.cpp | 542 +- lib/IRremoteESP8266-2.6.5/src/IRutils.h | 64 + .../src/ir_Aiwa.cpp | 2 + lib/IRremoteESP8266-2.6.5/src/ir_Amcor.cpp | 326 + lib/IRremoteESP8266-2.6.5/src/ir_Amcor.h | 118 + lib/IRremoteESP8266-2.6.5/src/ir_Argo.cpp | 438 + .../src/ir_Argo.h | 129 +- .../src/ir_Carrier.cpp | 47 +- .../src/ir_Coolix.cpp | 224 +- .../src/ir_Coolix.h | 29 +- lib/IRremoteESP8266-2.6.5/src/ir_Daikin.cpp | 3069 +++++ lib/IRremoteESP8266-2.6.5/src/ir_Daikin.h | 763 ++ .../src/ir_Denon.cpp | 43 +- .../src/ir_Dish.cpp | 49 +- lib/IRremoteESP8266-2.6.5/src/ir_Electra.cpp | 336 + lib/IRremoteESP8266-2.6.5/src/ir_Electra.h | 102 + lib/IRremoteESP8266-2.6.5/src/ir_Fujitsu.cpp | 730 ++ .../src/ir_Fujitsu.h | 102 +- .../src/ir_GICable.cpp | 40 +- .../src/ir_GlobalCache.cpp | 12 +- .../src/ir_Goodweather.cpp | 464 + .../src/ir_Goodweather.h | 137 + .../src/ir_Gree.cpp | 430 +- lib/IRremoteESP8266-2.6.5/src/ir_Gree.h | 173 + .../src/ir_Haier.cpp | 473 +- .../src/ir_Haier.h | 125 +- .../src/ir_Hitachi.cpp | 251 +- .../src/ir_Hitachi.h | 43 +- lib/IRremoteESP8266-2.6.5/src/ir_Inax.cpp | 83 + .../src/ir_JVC.cpp | 41 +- .../src/ir_Kelvinator.cpp | 403 +- .../src/ir_Kelvinator.h | 93 +- .../src/ir_LG.cpp | 17 +- lib/IRremoteESP8266-2.6.5/src/ir_LG.h | 15 + .../src/ir_Lasertag.cpp | 7 +- .../src/ir_Lego.cpp | 41 +- .../src/ir_Lutron.cpp | 13 +- .../src/ir_MWM.cpp | 12 +- .../src/ir_Magiquest.cpp | 0 .../src/ir_Magiquest.h | 0 .../src/ir_Midea.cpp | 311 +- .../src/ir_Midea.h | 73 +- .../src/ir_Mitsubishi.cpp | 422 +- .../src/ir_Mitsubishi.h | 92 +- .../src/ir_MitsubishiHeavy.cpp | 366 +- .../src/ir_MitsubishiHeavy.h | 41 +- .../src/ir_NEC.cpp | 51 +- .../src/ir_NEC.h | 12 +- lib/IRremoteESP8266-2.6.5/src/ir_Neoclima.cpp | 554 + lib/IRremoteESP8266-2.6.5/src/ir_Neoclima.h | 154 + .../src/ir_Nikai.cpp | 45 +- .../src/ir_Panasonic.cpp | 438 +- .../src/ir_Panasonic.h | 93 +- .../src/ir_Pioneer.cpp | 102 +- .../src/ir_Pronto.cpp | 8 +- .../src/ir_RC5_RC6.cpp | 12 +- .../src/ir_RCMM.cpp | 19 +- .../src/ir_Samsung.cpp | 471 +- .../src/ir_Samsung.h | 89 +- .../src/ir_Sanyo.cpp | 6 - lib/IRremoteESP8266-2.6.5/src/ir_Sharp.cpp | 557 + lib/IRremoteESP8266-2.6.5/src/ir_Sharp.h | 98 + .../src/ir_Sherwood.cpp | 12 +- .../src/ir_Sony.cpp | 19 +- .../src/ir_Tcl.cpp | 189 +- .../src/ir_Tcl.h | 22 +- .../src/ir_Teco.cpp | 203 +- .../src/ir_Teco.h | 38 +- .../src/ir_Toshiba.cpp | 232 +- .../src/ir_Toshiba.h | 64 +- lib/IRremoteESP8266-2.6.5/src/ir_Trotec.cpp | 296 + .../src/ir_Trotec.h | 40 +- .../src/ir_Vestel.cpp | 260 +- .../src/ir_Vestel.h | 50 +- .../src/ir_Whirlpool.cpp | 381 +- .../src/ir_Whirlpool.h | 82 +- .../src/ir_Whynter.cpp | 54 +- .../test/IRac_test.cpp | 709 +- .../test/IRrecv_test.cpp | 1246 ++ .../test/IRrecv_test.h | 0 .../test/IRsend_test.cpp | 120 +- .../test/IRsend_test.h | 0 .../test/IRutils_test.cpp | 141 +- .../test/Makefile | 70 +- .../test/ir_Aiwa_test.cpp | 0 .../test/ir_Amcor_test.cpp | 351 + .../test/ir_Argo_test.cpp | 221 + .../test/ir_Carrier_test.cpp | 0 .../test/ir_Coolix_test.cpp | 164 +- .../test/ir_Daikin_test.cpp | 1224 +- .../test/ir_Denon_test.cpp | 32 +- .../test/ir_Dish_test.cpp | 0 .../test/ir_Electra_test.cpp | 250 + .../test/ir_Fujitsu_test.cpp | 798 ++ .../test/ir_GICable_test.cpp | 6 +- .../test/ir_GlobalCache_test.cpp | 0 .../test/ir_Goodweather_test.cpp | 511 + .../test/ir_Gree_test.cpp | 176 +- .../test/ir_Haier_test.cpp | 132 +- .../test/ir_Hitachi_test.cpp | 46 +- .../test/ir_Inax_test.cpp | 119 + .../test/ir_JVC_test.cpp | 0 .../test/ir_Kelvinator_test.cpp | 40 +- .../test/ir_LG_test.cpp | 2 +- .../test/ir_Lasertag_test.cpp | 0 .../test/ir_Lego_test.cpp | 0 .../test/ir_Lutron_test.cpp | 2 +- .../test/ir_MWM_test.cpp | 0 .../test/ir_Magiquest_test.cpp | 0 .../test/ir_Midea_test.cpp | 128 +- .../test/ir_MitsubishiHeavy_test.cpp | 105 +- .../test/ir_Mitsubishi_test.cpp | 34 +- .../test/ir_NEC_test.cpp | 0 .../test/ir_Neoclima_test.cpp | 434 + .../test/ir_Nikai_test.cpp | 0 .../test/ir_Panasonic_test.cpp | 71 +- .../test/ir_Pioneer_test.cpp | 84 +- .../test/ir_Pronto_test.cpp | 0 .../test/ir_RC5_RC6_test.cpp | 0 .../test/ir_RCMM_test.cpp | 0 .../test/ir_Samsung_test.cpp | 381 +- .../test/ir_Sanyo_test.cpp | 0 .../test/ir_Sharp_test.cpp | 345 + .../test/ir_Sherwood_test.cpp | 0 .../test/ir_Sony_test.cpp | 20 +- .../test/ir_Tcl_test.cpp | 80 +- .../test/ir_Teco_test.cpp | 102 +- .../test/ir_Toshiba_test.cpp | 35 +- .../test/ir_Trotec_test.cpp | 179 + .../test/ir_Vestel_test.cpp | 57 +- .../test/ir_Whirlpool_test.cpp | 66 +- .../test/ir_Whynter_test.cpp | 8 +- .../tools/Makefile | 29 +- .../tools/RawToGlobalCache.sh | 0 .../tools/auto_analyse_raw_data.py | 8 +- .../tools/auto_analyse_raw_data_test.py | 28 +- .../tools/gc_decode.cpp | 0 .../tools/mkkeywords | 14 +- .../tools/mode2_decode.cpp | 0 .../tools/scrape_supported_devices.py | 255 + lib/JaretBurkett_ILI9488-gemu-1.0/ILI9488.cpp | 1104 ++ lib/JaretBurkett_ILI9488-gemu-1.0/ILI9488.h | 172 + lib/JaretBurkett_ILI9488-gemu-1.0/README.md | 8 + .../examples/graphicstest/graphicstest.ino | 350 + .../keywords.txt | 30 + .../library.properties | 9 + .../spi_register.h | 189 + lib/NeoPixelBus-2.2.9/COPYING | 675 - lib/NeoPixelBus-2.2.9/library.json | 15 - .../src/internal/NeoEsp8266UartMethod.cpp | 216 - .../src/internal/NeoEsp8266UartMethod.h | 178 - .../src/internal/NeoPixelEsp.c | 151 - .../.gitattributes | 0 .../.gitignore | 0 lib/NeoPixelBus-2.5.0.09/COPYING | 165 + .../ReadMe.md | 9 +- .../examples/DotStarTest/DotStarTest.ino | 0 .../NeoPixelBrightness/NeoPixelBrightness.ino | 0 .../examples/NeoPixelGamma/NeoPixelGamma.ino | 0 .../examples/NeoPixelTest/NeoPixelTest.ino | 9 +- .../NeoPixelAnimation/NeoPixelAnimation.ino | 5 +- .../NeoPixelCylon/NeoPixelCylon.ino | 0 .../NeoPixelFunFadeInOut.ino | 12 +- .../NeoPixelFunLoop/NeoPixelFunLoop.ino | 4 +- .../NeoPixelFunRandomChange.ino | 4 +- .../NeoPixelRotateLoop/NeoPixelRotateLoop.ino | 0 .../bitmaps/NeoPixelBitmap/NeoPixelBitmap.ino | 0 .../bitmaps/NeoPixelBitmap/Strings.bmp | Bin .../bitmaps/NeoPixelBitmap/StringsW.bmp | Bin .../bitmaps/NeoPixelBufferCylon/Cylon.pdn | Bin .../bitmaps/NeoPixelBufferCylon/CylonGrb.h | 0 .../bitmaps/NeoPixelBufferCylon/CylonGrbw.h | 0 .../NeoPixelBufferCylon.ino | 0 .../NeoPixelBufferShader.ino | 6 +- .../NeoPixelDibTest/NeoPixelDibTest.ino | 6 +- .../NeoPixelMosaicDump/NeoPixelMosaicDump.ino | 0 .../NeoPixelMosaicTest/NeoPixelMosaicTest.ino | 0 .../NeoPixelRingTopologyTest.ino | 0 .../NeoPixelTilesDump/NeoPixelTilesDump.ino | 0 .../NeoPixelTilesTest/NeoPixelTilesTest.ino | 0 .../NeoPixelTopologyDump.ino | 0 .../NeoPixelTopologyTest.ino | 0 .../extras/curves/circular.png | Bin 0 -> 44762 bytes .../extras/curves/cubic.png | Bin 0 -> 42669 bytes .../extras/curves/different.png | Bin 0 -> 37163 bytes .../extras/curves/exponential.png | Bin 0 -> 43532 bytes .../extras/curves/gamma.png | Bin 0 -> 22467 bytes .../extras/curves/pronounced.png | Bin 0 -> 42590 bytes .../extras/curves/quadratic.png | Bin 0 -> 42622 bytes .../extras/curves/quintic.png | Bin 0 -> 42362 bytes .../extras/curves/sinusoidal.png | Bin 0 -> 40710 bytes .../keywords.txt | 113 +- lib/NeoPixelBus-2.5.0.09/library.json | 14 + .../library.properties | 4 +- .../src/NeoPixelAnimator.h | 7 + .../src/NeoPixelBrightnessBus.h | 0 .../src/NeoPixelBus.h | 23 +- .../src/internal/DotStarAvrMethod.h | 2 +- .../src/internal/DotStarColorFeatures.h | 329 + .../src/internal/DotStarGenericMethod.h | 2 +- .../src/internal/DotStarSpiMethod.h | 34 +- .../src/internal/Esp32_i2s.c | 484 + .../src/internal/Esp32_i2s.h | 40 + .../src/internal/HsbColor.cpp | 0 .../src/internal/HsbColor.h | 0 .../src/internal/HslColor.cpp | 0 .../src/internal/HslColor.h | 0 .../src/internal/HtmlColor.cpp | 0 .../src/internal/HtmlColor.h | 2 +- .../src/internal/HtmlColorNameStrings.cpp | 0 .../src/internal/HtmlColorNameStrings.h | 0 .../src/internal/HtmlColorNames.cpp | 0 .../src/internal/HtmlColorShortNames.cpp | 0 .../src/internal/Layouts.h | 16 +- .../src/internal/NeoArmMethod.h | 160 +- .../src/internal/NeoAvrMethod.h | 26 +- .../src/internal/NeoBitmapFile.h | 67 +- .../src/internal/NeoBuffer.h | 25 +- .../src/internal/NeoBufferContext.h | 0 .../src/internal/NeoBufferMethods.h | 0 .../src/internal/NeoColorFeatures.h | 0 .../src/internal/NeoDib.h | 30 +- .../src/internal/NeoEase.h | 88 + .../src/internal/NeoEsp32I2sMethod.h | 217 + .../src/internal/NeoEsp32RmtMethod.h | 369 + .../src/internal/NeoEsp8266DmaMethod.h | 148 +- .../src/internal/NeoEsp8266UartMethod.cpp | 171 + .../src/internal/NeoEsp8266UartMethod.h | 434 + .../src/internal/NeoEspBitBangMethod.h | 51 +- .../src/internal/NeoGamma.cpp | 0 .../src/internal/NeoGamma.h | 3 +- .../src/internal/NeoHueBlend.h | 0 .../src/internal/NeoMosaic.h | 0 .../src/internal/NeoPixelAnimator.cpp | 32 +- .../src/internal/NeoPixelAvr.c | 6 +- .../src/internal/NeoPixelEsp.c | 169 + .../src/internal/NeoRingTopology.h | 27 + .../src/internal/NeoSpriteSheet.h | 6 +- .../src/internal/NeoTiles.h | 0 .../src/internal/NeoTopology.h | 0 .../src/internal/RgbColor.cpp | 0 .../src/internal/RgbColor.h | 0 .../src/internal/RgbwColor.cpp | 0 .../src/internal/RgbwColor.h | 0 .../README.md | 0 .../examples/modbustest/modbustest.ino | 0 .../keywords.txt | 0 .../library.json | 2 +- .../library.properties | 2 +- .../src/TasmotaModbus.cpp | 48 +- .../src/TasmotaModbus.h | 23 +- lib/TasmotaSerial-2.3.1/src/TasmotaSerial.cpp | 268 - .../README.md | 0 .../examples/swsertest/swsertest.ino | 0 .../keywords.txt | 0 .../library.json | 2 +- .../library.properties | 2 +- lib/TasmotaSerial-2.4.1/src/TasmotaSerial.cpp | 397 + .../src/TasmotaSerial.h | 32 +- lib/Xlatb_RA8876-gemu-1.0/RA8876.cpp | 1403 +++ lib/Xlatb_RA8876-gemu-1.0/RA8876.h | 569 + lib/Xlatb_RA8876-gemu-1.0/README.md | 8 + lib/Xlatb_RA8876-gemu-1.0/keywords.txt | 30 + lib/Xlatb_RA8876-gemu-1.0/library.properties | 9 + lib/Xlatb_RA8876-gemu-1.0/spi_register.h | 189 + lib/base64-1.1.1/LICENSE | 21 + lib/base64-1.1.1/Makefile | 9 + lib/base64-1.1.1/README.md | 44 + lib/base64-1.1.1/catch.cpp | 465 + lib/base64-1.1.1/catch.hpp | 10359 ++++++++++++++++ lib/base64-1.1.1/library.properties | 9 + lib/base64-1.1.1/src/base64.hpp | 195 + lib/bearssl-esp8266/src/int/i15_montmul.c | 4 +- lib/bearssl-esp8266/src/x509/skey_decoder.c | 2 +- .../src/epdpaint.cpp | 322 - .../src/font12.c | 1385 --- .../src/font16.c | 1765 --- .../src/font20.c | 2143 ---- .../src/font24.c | 2521 ---- .../src/font8.c | 1005 -- .../.gitignore | 0 .../.travis.yml | 0 .../Arduino/epd2in9-demo/epd2in9-demo.ino | 0 .../Arduino/libraries/readme.txt | 0 .../LICENSE | 0 .../Makefile | 0 .../README.md | 0 .../components/epaper-29-ws/component.mk | 0 .../components/epaper-29-ws/epaper-29-ws.c | 0 .../components/epaper-29-ws/epaper-29-ws.h | 0 .../components/epaper-29-ws/epaper_font.c | 0 .../components/epaper-29-ws/epaper_fonts.h | 0 .../components/epaper-29-ws/font16.c | 0 .../components/epaper-29-ws/font20.c | 0 .../components/epaper-29-ws/font8.c | 0 .../components/epaper-29-ws/imagedata.cpp | 0 .../components/epaper-29-ws/imagedata.h | 0 .../docs/Doxyfile | 0 .../docs/Makefile | 0 .../docs/README.md | 0 .../docs/conf.py | 0 .../docs/gen-dxd.py | 0 .../docs/index.rst | 0 .../docs/link-roles.py | 0 .../docs/repo_util.py | 0 .../docs/requirements.txt | 0 .../library.properties | 4 +- .../main/README.md | 0 .../main/component.mk | 0 .../main/esp-epaper-29-ws.c | 0 .../main/imagedata.c | 0 .../main/imagedata.h | 0 .../pictures/2.9inch_e-Paper_Datasheet.pdf | Bin .../pictures/e-paper-and-esp-sample-image.jpg | Bin .../pictures/e-paper-and-esp-sample-text.jpg | Bin .../pictures/espresif-logo.bmp | Bin .../pictures/image-conversion-setup.png | Bin .../src/epd2in9.cpp | 172 +- .../src/epd2in9.h | 30 +- .../src/epd4in2.cpp | 566 + .../src/epd4in2.h | 130 + .../src/epdif.cpp | 0 .../src/epdif.h | 0 .../src/epdpaint.cpp | 177 + .../src/epdpaint.h | 39 +- .../src/font12.c | 1500 +++ .../src/font16.c | 1909 +++ .../src/font20.c | 2319 ++++ .../src/font24.c | 2729 ++++ .../src/font8.c | 1085 ++ .../src/fonts.h | 10 +- .../src/renderer.cpp | 519 + .../src/renderer.h | 58 + .../DPT.h | 3 +- .../LICENSE | 0 .../README.md | 0 .../esp-knx-ip-config.cpp | 0 .../esp-knx-ip-conversion.cpp | 0 .../esp-knx-ip-send.cpp | 14 + .../esp-knx-ip-webserver.cpp | 0 .../esp-knx-ip.cpp | 0 .../esp-knx-ip.h | 3 + .../environment-sensor/environment-sensor.ino | 0 .../examples/sonoff/sonoff.ino | 0 .../examples/static-config/static-config.ino | 12 +- .../keywords.txt | 23 +- .../library.properties | 2 +- lib/readme.txt | 36 - pio/http-uploader.py | 9 +- pio/obj-dump.py | 9 + pio/sftp-uploader.py | 10 +- platformio.ini | 167 +- scripter.md | 53 +- sonoff/StackThunk_light.cpp | 2 +- sonoff/WiFiClientSecureLightBearSSL.cpp | 9 +- sonoff/WiFiClientSecureLightBearSSL.h | 2 +- sonoff/_changelog.ino | 176 +- sonoff/i18n.h | 191 +- sonoff/language/bg-BG.h | 70 +- sonoff/language/cs-CZ.h | 71 +- sonoff/language/de-DE.h | 79 +- sonoff/language/el-GR.h | 89 +- sonoff/language/en-GB.h | 71 +- sonoff/language/es-ES.h | 79 +- sonoff/language/fr-FR.h | 89 +- sonoff/language/he-HE.h | 71 +- sonoff/language/hu-HU.h | 69 + sonoff/language/it-IT.h | 137 +- sonoff/language/ko-KO.h | 71 +- sonoff/language/nl-NL.h | 71 +- sonoff/language/pl-PL.h | 89 +- sonoff/language/pt-BR.h | 157 +- sonoff/language/pt-PT.h | 69 + sonoff/language/ru-RU.h | 71 +- sonoff/language/sk-SK.h | 71 +- sonoff/language/sv-SE.h | 71 +- sonoff/language/tr-TR.h | 73 +- sonoff/language/uk-UK.h | 327 +- sonoff/language/zh-CN.h | 73 +- sonoff/language/zh-TW.h | 71 +- sonoff/my_user_config.h | 182 +- sonoff/sendemail.h | 38 + sonoff/sendemail.ino | 369 + sonoff/settings.h | 120 +- sonoff/settings.ino | 378 +- sonoff/sonoff.h | 78 +- sonoff/sonoff.ino | 1817 +-- sonoff/sonoff_aws_iot.cpp | 152 - sonoff/sonoff_ca.ino | 8 +- sonoff/sonoff_post.h | 816 +- sonoff/sonoff_template.h | 605 +- sonoff/sonoff_version.h | 2 +- sonoff/support.ino | 318 +- sonoff/support_button.ino | 162 +- sonoff/support_command.ino | 1519 +++ sonoff/support_features.ino | 141 +- sonoff/support_float.ino | 37 + sonoff/support_rotary.ino | 78 +- sonoff/support_rtc.ino | 216 +- sonoff/support_static_buffer.ino | 212 + sonoff/support_switch.ino | 122 +- sonoff/support_udp.ino | 5 + sonoff/support_wifi.ino | 371 +- sonoff/xdrv_01_webserver.ino | 787 +- sonoff/xdrv_02_mqtt.ino | 1040 +- sonoff/xdrv_03_energy.ino | 1283 +- sonoff/xdrv_04_light.ino | 1838 ++- sonoff/xdrv_05_irremote.ino | 915 +- sonoff/xdrv_05_irremote_full.ino | 662 + sonoff/xdrv_06_snfbridge.ino | 276 +- sonoff/xdrv_07_domoticz.ino | 390 +- sonoff/xdrv_08_serial_bridge.ino | 61 +- sonoff/xdrv_09_timers.ino | 183 +- sonoff/xdrv_10_rules.ino | 1035 +- sonoff/xdrv_10_scripter.ino | 2434 +++- sonoff/xdrv_11_knx.ino | 246 +- sonoff/xdrv_12_home_assistant.ino | 96 +- sonoff/xdrv_13_display.ino | 1303 +- sonoff/xdrv_14_mp3.ino | 2 + sonoff/xdrv_15_pca9685.ino | 3 +- sonoff/xdrv_16_tuyadimmer.ino | 431 - sonoff/xdrv_16_tuyamcu.ino | 662 + sonoff/xdrv_17_rcswitch.ino | 132 +- sonoff/xdrv_18_armtronix_dimmers.ino | 49 +- sonoff/xdrv_19_ps16dz_dimmer.ino | 254 +- sonoff/xdrv_20_hue.ino | 251 +- sonoff/xdrv_21_wemo.ino | 2 +- sonoff/xdrv_22_sonoff_ifan.ino | 272 + sonoff/xdrv_23_zigbee_0_constants.ino | 430 + sonoff/xdrv_23_zigbee_3_devices.ino | 442 + sonoff/xdrv_23_zigbee_5_converters.ino | 994 ++ sonoff/xdrv_23_zigbee_6_commands.ino | 92 + sonoff/xdrv_23_zigbee_7_statemachine.ino | 642 + sonoff/xdrv_23_zigbee_8_parsers.ino | 458 + sonoff/xdrv_23_zigbee_9_impl.ino | 576 + sonoff/xdrv_24_buzzer.ino | 205 + sonoff/xdrv_25_A4988_Stepper.ino | 138 + sonoff/xdrv_26_ariluxrf.ino | 191 + sonoff/xdrv_27_shutter.ino | 662 + sonoff/xdrv_28_pcf8574.ino | 249 + sonoff/xdrv_29_deepsleep.ino | 158 + sonoff/xdrv_30_exs_dimmer.ino | 637 + sonoff/xdrv_31_arduino_slave.ino | 291 + sonoff/xdrv_99_debug.ino | 349 +- sonoff/xdrv_interface.ino | 684 +- sonoff/xdsp_01_lcd.ino | 2 + sonoff/xdsp_02_ssd1306.ino | 217 +- sonoff/xdsp_03_matrix.ino | 3 + sonoff/xdsp_04_ili9341.ino | 6 + sonoff/xdsp_05_epaper_29.ino | 263 +- sonoff/xdsp_06_epaper_42.ino | 160 + sonoff/xdsp_07_sh1106.ino | 195 + sonoff/xdsp_08_ILI9488.ino | 267 + sonoff/xdsp_09_SSD1351.ino | 183 + sonoff/xdsp_10_RA8876.ino | 447 + .../{xplg_ws2812.ino => xlgt_01_ws2812.ino} | 309 +- sonoff/xlgt_02_my92x1.ino | 165 + sonoff/xlgt_03_sm16716.ino | 200 + sonoff/xlgt_04_sm2135.ino | 172 + sonoff/xlgt_05_sonoff_l1.ino | 258 + sonoff/xlgt_interface.ino | 114 + sonoff/xnrg_01_hlw8012.ino | 290 +- sonoff/xnrg_02_cse7766.ino | 177 +- sonoff/xnrg_03_pzem004t.ino | 153 +- sonoff/xnrg_04_mcp39f501.ino | 89 +- sonoff/xnrg_05_pzem_ac.ino | 158 +- sonoff/xnrg_06_pzem_dc.ino | 134 +- sonoff/xnrg_07_ade7953.ino | 192 +- sonoff/xnrg_08_sdm120.ino | 269 + sonoff/xnrg_09_dds2382.ino | 128 + sonoff/xnrg_10_sdm630.ino | 217 + sonoff/xnrg_11_ddsu666.ino | 175 + sonoff/xnrg_12_solaxX1.ino | 528 + sonoff/xnrg_interface.ino | 29 +- sonoff/xsns_01_counter.ino | 201 +- sonoff/xsns_02_analog.ino | 170 +- sonoff/xsns_04_snfsc.ino | 3 + sonoff/xsns_05_ds18b20.ino | 253 - sonoff/xsns_05_ds18x20.ino | 14 +- sonoff/xsns_05_ds18x20_legacy.ino | 245 - sonoff/xsns_06_dht.ino | 66 +- sonoff/xsns_13_ina219.ino | 111 +- sonoff/xsns_14_sht3x.ino | 2 +- sonoff/xsns_18_pms5003.ino | 72 +- sonoff/xsns_20_novasds.ino | 82 +- sonoff/xsns_21_sgp30.ino | 11 +- sonoff/xsns_23_sdm120.ino | 397 - sonoff/xsns_25_sdm630.ino | 362 - sonoff/xsns_29_mcp230xx.ino | 22 +- sonoff/xsns_33_ds3231.ino | 18 +- sonoff/xsns_34_hx711.ino | 262 +- sonoff/xsns_38_az7798.ino | 28 +- sonoff/xsns_40_pn532.ino | 15 +- sonoff/xsns_42_scd30.ino | 8 +- sonoff/xsns_44_sps30.ino | 3 +- sonoff/xsns_45_vl53l0x.ino | 2 +- sonoff/xsns_47_max31865.ino | 139 + sonoff/xsns_48_chirp.ino | 554 + sonoff/xsns_50_paj7620.ino | 570 + sonoff/xsns_51_rdm6300.ino | 177 + sonoff/xsns_52_ibeacon.ino | 590 + sonoff/xsns_53_sml.ino | 2409 ++++ sonoff/xsns_54_ina226.ino | 568 + sonoff/xsns_interface.ino | 622 +- sonoff/zzzz_debug.ino | 309 - tools/decode-config.html | 4 +- tools/decode-config.md | 4 +- tools/decode-config.py | 315 +- tools/decode-status.py | 59 +- .../fw_efm8bb1/RF-Bridge-EFM8BB1-20191006.hex | 491 + 765 files changed, 109946 insertions(+), 37919 deletions(-) delete mode 100644 TEMPLATE.md create mode 100644 TEMPLATES.md create mode 100644 arduino/version pre-2.6.0/boards.txt create mode 100644 arduino/version pre-2.6.0/platform.txt create mode 100644 build-container/Dockerfile create mode 100644 build-container/README.md create mode 100644 build-container/entrypoint.sh create mode 100644 build-container/init_pio_tasmota/platformio.ini create mode 100644 build-container/init_pio_tasmota/src/main.cpp create mode 100644 lib/A4988_Stepper/README.adoc create mode 100644 lib/A4988_Stepper/keywords.txt create mode 100644 lib/A4988_Stepper/library.properties create mode 100644 lib/A4988_Stepper/src/A4988_Stepper.cpp create mode 100644 lib/A4988_Stepper/src/A4988_Stepper.h create mode 100644 lib/AT24C256/Eeprom24C128_256.cpp create mode 100644 lib/AT24C256/Eeprom24C128_256.h delete mode 100644 lib/Adafruit-GFX-Library-1.2.9/Adafruit_SPITFT.cpp delete mode 100644 lib/Adafruit-GFX-Library-1.2.9/Adafruit_SPITFT.h delete mode 100644 lib/Adafruit-GFX-Library-1.2.9/Adafruit_SPITFT_Macros.h rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/.gitignore (100%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/.travis.yml (87%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/Adafruit_GFX.cpp (85%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/Adafruit_GFX.h (52%) create mode 100644 lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Adafruit_SPITFT.cpp create mode 100644 lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Adafruit_SPITFT.h create mode 100644 lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Adafruit_SPITFT_Macros.h rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/Fonts/FreeMono12pt7b.h (100%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/Fonts/FreeMono18pt7b.h (100%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/Fonts/FreeMono24pt7b.h (100%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/Fonts/FreeMono9pt7b.h (100%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/Fonts/FreeMonoBold12pt7b.h (100%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/Fonts/FreeMonoBold18pt7b.h (100%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/Fonts/FreeMonoBold24pt7b.h (100%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/Fonts/FreeMonoBold9pt7b.h (100%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/Fonts/FreeMonoBoldOblique12pt7b.h (100%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/Fonts/FreeMonoBoldOblique18pt7b.h (100%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/Fonts/FreeMonoBoldOblique24pt7b.h (100%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/Fonts/FreeMonoBoldOblique9pt7b.h (100%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/Fonts/FreeMonoOblique12pt7b.h (100%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/Fonts/FreeMonoOblique18pt7b.h (100%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/Fonts/FreeMonoOblique24pt7b.h (100%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/Fonts/FreeMonoOblique9pt7b.h (100%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/Fonts/FreeSans12pt7b.h (100%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/Fonts/FreeSans18pt7b.h (100%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/Fonts/FreeSans24pt7b.h (100%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/Fonts/FreeSans9pt7b.h (100%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/Fonts/FreeSansBold12pt7b.h (100%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/Fonts/FreeSansBold18pt7b.h (100%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/Fonts/FreeSansBold24pt7b.h (100%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/Fonts/FreeSansBold9pt7b.h (100%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/Fonts/FreeSansBoldOblique12pt7b.h (100%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/Fonts/FreeSansBoldOblique18pt7b.h (100%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/Fonts/FreeSansBoldOblique24pt7b.h (100%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/Fonts/FreeSansBoldOblique9pt7b.h (100%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/Fonts/FreeSansOblique12pt7b.h (100%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/Fonts/FreeSansOblique18pt7b.h (100%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/Fonts/FreeSansOblique24pt7b.h (100%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/Fonts/FreeSansOblique9pt7b.h (100%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/Fonts/FreeSerif12pt7b.h (100%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/Fonts/FreeSerif18pt7b.h (100%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/Fonts/FreeSerif24pt7b.h (100%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/Fonts/FreeSerif9pt7b.h (100%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/Fonts/FreeSerifBold12pt7b.h (100%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/Fonts/FreeSerifBold18pt7b.h (100%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/Fonts/FreeSerifBold24pt7b.h (100%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/Fonts/FreeSerifBold9pt7b.h (100%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/Fonts/FreeSerifBoldItalic12pt7b.h (100%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/Fonts/FreeSerifBoldItalic18pt7b.h (100%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/Fonts/FreeSerifBoldItalic24pt7b.h (100%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/Fonts/FreeSerifBoldItalic9pt7b.h (100%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/Fonts/FreeSerifItalic12pt7b.h (100%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/Fonts/FreeSerifItalic18pt7b.h (100%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/Fonts/FreeSerifItalic24pt7b.h (100%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/Fonts/FreeSerifItalic9pt7b.h (100%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/Fonts/Org_01.h (100%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/Fonts/Picopixel.h (100%) rename lib/{Adafruit-GFX-Library-1.2.9/Fonts/Tiny3x3a2pt7b => Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/Tiny3x3a2pt7b.h} (100%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/Fonts/TomThumb.h (100%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/README.md (94%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/examples/mock_ili9341/mock_ili9341.ino (99%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/fontconvert/Makefile (100%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/fontconvert/fontconvert.c (94%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/fontconvert/fontconvert_win.md (100%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/fontconvert/makefonts.sh (100%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/gfxfont.h (100%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/glcdfont.c (98%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/library.properties (96%) rename lib/{Adafruit-GFX-Library-1.2.9 => Adafruit-GFX-Library-1.5.6-gemu-1.0}/license.txt (100%) create mode 100644 lib/Adafruit_MAX31865-1.1.0-custom/Adafruit_MAX31865.cpp create mode 100644 lib/Adafruit_MAX31865-1.1.0-custom/Adafruit_MAX31865.h create mode 100644 lib/Adafruit_MAX31865-1.1.0-custom/README.md create mode 100644 lib/Adafruit_MAX31865-1.1.0-custom/README.txt create mode 100644 lib/Adafruit_MAX31865-1.1.0-custom/examples/max31865/max31865.ino create mode 100644 lib/Adafruit_MAX31865-1.1.0-custom/library.properties create mode 100644 lib/Adafruit_SH1106-gemu-1.0/Adafruit_SH1106.cpp create mode 100644 lib/Adafruit_SH1106-gemu-1.0/Adafruit_SH1106.h rename lib/{Adafruit_SSD1306-1.1.2/license.txt => Adafruit_SH1106-gemu-1.0/LICENSE.txt} (100%) create mode 100644 lib/Adafruit_SH1106-gemu-1.0/README.md rename lib/{Adafruit_SSD1306-1.1.2/examples/ssd1306_128x64_i2c/ssd1306_128x64_i2c.ino => Adafruit_SH1106-gemu-1.0/examples/sh1106_128x64_i2c/sh1106_128x64_i2c.ino} (90%) rename lib/{Adafruit_SSD1306-1.1.2/examples/ssd1306_128x64_spi/ssd1306_128x64_spi.ino => Adafruit_SH1106-gemu-1.0/examples/sh1106_128x64_spi/sh1106_128x64_spi.ino} (92%) delete mode 100644 lib/Adafruit_SSD1306-1.1.2/Adafruit_SSD1306.cpp delete mode 100644 lib/Adafruit_SSD1306-1.1.2/Adafruit_SSD1306.h delete mode 100644 lib/Adafruit_SSD1306-1.1.2/README.md delete mode 100644 lib/Adafruit_SSD1306-1.1.2/README.txt delete mode 100644 lib/Adafruit_SSD1306-1.1.2/examples/ssd1306_128x32_i2c/ssd1306_128x32_i2c.ino delete mode 100644 lib/Adafruit_SSD1306-1.1.2/examples/ssd1306_128x32_spi/ssd1306_128x32_spi.ino delete mode 100644 lib/Adafruit_SSD1306-1.1.2/library.properties rename lib/{Adafruit_SSD1306-1.1.2 => Adafruit_SSD1306-1.3.0-gemu-1.1}/.github/ISSUE_TEMPLATE.md (100%) rename lib/{Adafruit_SSD1306-1.1.2 => Adafruit_SSD1306-1.3.0-gemu-1.1}/.github/PULL_REQUEST_TEMPLATE.md (100%) create mode 100644 lib/Adafruit_SSD1306-1.3.0-gemu-1.1/.gitignore create mode 100644 lib/Adafruit_SSD1306-1.3.0-gemu-1.1/.travis.yml create mode 100644 lib/Adafruit_SSD1306-1.3.0-gemu-1.1/Adafruit_SSD1306.cpp create mode 100644 lib/Adafruit_SSD1306-1.3.0-gemu-1.1/Adafruit_SSD1306.h create mode 100644 lib/Adafruit_SSD1306-1.3.0-gemu-1.1/README.md create mode 100644 lib/Adafruit_SSD1306-1.3.0-gemu-1.1/examples/OLED_featherwing/OLED_featherwing.ino create mode 100644 lib/Adafruit_SSD1306-1.3.0-gemu-1.1/examples/ssd1306_128x32_i2c/ssd1306_128x32_i2c.ino create mode 100644 lib/Adafruit_SSD1306-1.3.0-gemu-1.1/examples/ssd1306_128x32_spi/ssd1306_128x32_spi.ino create mode 100644 lib/Adafruit_SSD1306-1.3.0-gemu-1.1/examples/ssd1306_128x64_i2c/ssd1306_128x64_i2c.ino create mode 100644 lib/Adafruit_SSD1306-1.3.0-gemu-1.1/examples/ssd1306_128x64_spi/ssd1306_128x64_spi.ino create mode 100644 lib/Adafruit_SSD1306-1.3.0-gemu-1.1/library.properties create mode 100644 lib/Adafruit_SSD1306-1.3.0-gemu-1.1/license.txt create mode 100644 lib/Adafruit_SSD1306-1.3.0-gemu-1.1/splash.h create mode 100644 lib/Adafruit_SSD1351-gemu-1.0/README.md create mode 100644 lib/Adafruit_SSD1351-gemu-1.0/SSD1351.cpp create mode 100644 lib/Adafruit_SSD1351-gemu-1.0/SSD1351.h create mode 100644 lib/Adafruit_SSD1351-gemu-1.0/Tiger.c create mode 100644 lib/Adafruit_SSD1351-gemu-1.0/Tiger.rgb create mode 100644 lib/Adafruit_SSD1351-gemu-1.0/keywords.txt create mode 100644 lib/Adafruit_SSD1351-gemu-1.0/library.properties create mode 100644 lib/Adafruit_SSD1351-gemu-1.0/spi_register.h create mode 100644 lib/ArduinoHexParse/README.md create mode 100644 lib/ArduinoHexParse/keywords.txt create mode 100644 lib/ArduinoHexParse/library.json create mode 100644 lib/ArduinoHexParse/library.properties create mode 100644 lib/ArduinoHexParse/src/ArduinoHexParse.cpp create mode 100644 lib/ArduinoHexParse/src/ArduinoHexParse.h create mode 100644 lib/FT6236-gemu-1.0/FT6236.cpp create mode 100644 lib/FT6236-gemu-1.0/FT6236.h delete mode 100644 lib/IRremoteESP8266-2.6.0/.travis.yml delete mode 100644 lib/IRremoteESP8266-2.6.0/examples/ControlSamsungAC/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.6.0/examples/IRGCSendDemo/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.6.0/examples/IRGCTCPServer/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.6.0/examples/IRMQTTServer/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.6.0/examples/IRServer/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.6.0/examples/IRrecvDemo/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.6.0/examples/IRrecvDump/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.6.0/examples/IRrecvDumpV2/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.6.0/examples/IRsendDemo/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.6.0/examples/IRsendProntoDemo/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.6.0/examples/JVCPanasonicSendDemo/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.6.0/examples/LGACSend/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.6.0/examples/TurnOnArgoAC/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.6.0/examples/TurnOnDaikinAC/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.6.0/examples/TurnOnFujitsuAC/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.6.0/examples/TurnOnKelvinatorAC/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.6.0/examples/TurnOnMitsubishiAC/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.6.0/examples/TurnOnMitsubishiHeavyAc/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.6.0/examples/TurnOnPanasonicAC/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.6.0/examples/TurnOnToshibaAC/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.6.0/examples/TurnOnTrotecAC/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.6.0/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.6.0/src/IRac.cpp delete mode 100644 lib/IRremoteESP8266-2.6.0/src/IRutils.h delete mode 100644 lib/IRremoteESP8266-2.6.0/src/ir_Argo.cpp delete mode 100644 lib/IRremoteESP8266-2.6.0/src/ir_Daikin.cpp delete mode 100644 lib/IRremoteESP8266-2.6.0/src/ir_Daikin.h delete mode 100644 lib/IRremoteESP8266-2.6.0/src/ir_Electra.cpp delete mode 100644 lib/IRremoteESP8266-2.6.0/src/ir_Fujitsu.cpp delete mode 100644 lib/IRremoteESP8266-2.6.0/src/ir_Gree.h delete mode 100644 lib/IRremoteESP8266-2.6.0/src/ir_LG.h delete mode 100644 lib/IRremoteESP8266-2.6.0/src/ir_Sharp.cpp delete mode 100644 lib/IRremoteESP8266-2.6.0/src/ir_Trotec.cpp delete mode 100644 lib/IRremoteESP8266-2.6.0/test/IRrecv_test.cpp delete mode 100644 lib/IRremoteESP8266-2.6.0/test/ir_Electra_test.cpp delete mode 100644 lib/IRremoteESP8266-2.6.0/test/ir_Fujitsu_test.cpp rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/.github/CONTRIBUTING.md (90%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/.github/Contributors.md (86%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/.github/issue_template.md (77%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/.gitignore (91%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/.gitmodules (100%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/.style.yapf (100%) create mode 100644 lib/IRremoteESP8266-2.6.5/.travis.yml rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/CPPLINT.cfg (100%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/LICENSE.txt (100%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/README.md (62%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/ReleaseNotes.md (69%) create mode 100644 lib/IRremoteESP8266-2.6.5/SupportedProtocols.md create mode 100644 lib/IRremoteESP8266-2.6.5/examples/CommonAcControl/CommonAcControl.ino create mode 100644 lib/IRremoteESP8266-2.6.5/examples/CommonAcControl/platformio.ini rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/examples/ControlSamsungAC/ControlSamsungAC.ino (79%) create mode 100644 lib/IRremoteESP8266-2.6.5/examples/ControlSamsungAC/platformio.ini create mode 100644 lib/IRremoteESP8266-2.6.5/examples/DumbIRRepeater/DumbIRRepeater.ino create mode 100644 lib/IRremoteESP8266-2.6.5/examples/DumbIRRepeater/platformio.ini rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/examples/IRGCSendDemo/IRGCSendDemo.ino (96%) create mode 100644 lib/IRremoteESP8266-2.6.5/examples/IRGCSendDemo/platformio.ini rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/examples/IRGCTCPServer/IRGCTCPServer.ino (97%) create mode 100644 lib/IRremoteESP8266-2.6.5/examples/IRGCTCPServer/platformio.ini rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/examples/IRMQTTServer/IRMQTTServer.h (50%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/examples/IRMQTTServer/IRMQTTServer.ino (66%) create mode 100644 lib/IRremoteESP8266-2.6.5/examples/IRMQTTServer/platformio.ini rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/examples/IRServer/IRServer.ino (80%) create mode 100644 lib/IRremoteESP8266-2.6.5/examples/IRServer/platformio.ini rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/examples/IRrecvDemo/IRrecvDemo.ino (94%) create mode 100644 lib/IRremoteESP8266-2.6.5/examples/IRrecvDemo/platformio.ini rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/examples/IRrecvDump/IRrecvDump.ino (99%) create mode 100644 lib/IRremoteESP8266-2.6.5/examples/IRrecvDump/platformio.ini rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/examples/IRrecvDumpV2/IRrecvDumpV2.ino (51%) create mode 100644 lib/IRremoteESP8266-2.6.5/examples/IRrecvDumpV2/platformio.ini rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/examples/IRsendDemo/IRsendDemo.ino (94%) create mode 100644 lib/IRremoteESP8266-2.6.5/examples/IRsendDemo/platformio.ini rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/examples/IRsendProntoDemo/IRsendProntoDemo.ino (98%) create mode 100644 lib/IRremoteESP8266-2.6.5/examples/IRsendProntoDemo/platformio.ini rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/examples/JVCPanasonicSendDemo/JVCPanasonicSendDemo.ino (96%) create mode 100644 lib/IRremoteESP8266-2.6.5/examples/JVCPanasonicSendDemo/platformio.ini rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/examples/LGACSend/LGACSend.ino (100%) create mode 100644 lib/IRremoteESP8266-2.6.5/examples/LGACSend/platformio.ini create mode 100644 lib/IRremoteESP8266-2.6.5/examples/SmartIRRepeater/SmartIRRepeater.ino create mode 100644 lib/IRremoteESP8266-2.6.5/examples/SmartIRRepeater/platformio.ini rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/examples/TurnOnArgoAC/TurnOnArgoAC.ino (93%) create mode 100644 lib/IRremoteESP8266-2.6.5/examples/TurnOnArgoAC/platformio.ini rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/examples/TurnOnDaikinAC/TurnOnDaikinAC.ino (95%) create mode 100644 lib/IRremoteESP8266-2.6.5/examples/TurnOnDaikinAC/platformio.ini rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/examples/TurnOnFujitsuAC/TurnOnFujitsuAC.ino (90%) create mode 100644 lib/IRremoteESP8266-2.6.5/examples/TurnOnFujitsuAC/platformio.ini rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/examples/TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino (96%) create mode 100644 lib/IRremoteESP8266-2.6.5/examples/TurnOnKelvinatorAC/platformio.ini rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/examples/TurnOnMitsubishiAC/TurnOnMitsubishiAC.ino (96%) create mode 100644 lib/IRremoteESP8266-2.6.5/examples/TurnOnMitsubishiAC/platformio.ini rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/examples/TurnOnMitsubishiHeavyAc/TurnOnMitsubishiHeavyAc.ino (96%) create mode 100644 lib/IRremoteESP8266-2.6.5/examples/TurnOnMitsubishiHeavyAc/platformio.ini rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/examples/TurnOnPanasonicAC/TurnOnPanasonicAC.ino (96%) create mode 100644 lib/IRremoteESP8266-2.6.5/examples/TurnOnPanasonicAC/platformio.ini rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/examples/TurnOnToshibaAC/TurnOnToshibaAC.ino (96%) create mode 100644 lib/IRremoteESP8266-2.6.5/examples/TurnOnToshibaAC/platformio.ini rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/examples/TurnOnTrotecAC/TurnOnTrotecAC.ino (95%) create mode 100644 lib/IRremoteESP8266-2.6.5/examples/TurnOnTrotecAC/platformio.ini rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/keywords.txt (79%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/library.json (79%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/library.properties (54%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/pylintrc (100%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/CPPLINT.cfg (100%) create mode 100644 lib/IRremoteESP8266-2.6.5/src/IRac.cpp rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/IRac.h (68%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/IRrecv.cpp (56%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/IRrecv.h (54%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/IRremoteESP8266.h (79%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/IRsend.cpp (70%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/IRsend.h (58%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/IRtimer.cpp (100%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/IRtimer.h (100%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/IRutils.cpp (55%) create mode 100644 lib/IRremoteESP8266-2.6.5/src/IRutils.h rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/ir_Aiwa.cpp (98%) create mode 100644 lib/IRremoteESP8266-2.6.5/src/ir_Amcor.cpp create mode 100644 lib/IRremoteESP8266-2.6.5/src/ir_Amcor.h create mode 100644 lib/IRremoteESP8266-2.6.5/src/ir_Argo.cpp rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/ir_Argo.h (52%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/ir_Carrier.cpp (67%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/ir_Coolix.cpp (71%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/ir_Coolix.h (84%) create mode 100644 lib/IRremoteESP8266-2.6.5/src/ir_Daikin.cpp create mode 100644 lib/IRremoteESP8266-2.6.5/src/ir_Daikin.h rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/ir_Denon.cpp (71%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/ir_Dish.cpp (70%) create mode 100644 lib/IRremoteESP8266-2.6.5/src/ir_Electra.cpp create mode 100644 lib/IRremoteESP8266-2.6.5/src/ir_Electra.h create mode 100644 lib/IRremoteESP8266-2.6.5/src/ir_Fujitsu.cpp rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/ir_Fujitsu.h (51%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/ir_GICable.cpp (70%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/ir_GlobalCache.cpp (86%) create mode 100644 lib/IRremoteESP8266-2.6.5/src/ir_Goodweather.cpp create mode 100644 lib/IRremoteESP8266-2.6.5/src/ir_Goodweather.h rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/ir_Gree.cpp (50%) create mode 100644 lib/IRremoteESP8266-2.6.5/src/ir_Gree.h rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/ir_Haier.cpp (66%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/ir_Haier.h (78%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/ir_Hitachi.cpp (63%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/ir_Hitachi.h (68%) create mode 100644 lib/IRremoteESP8266-2.6.5/src/ir_Inax.cpp rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/ir_JVC.cpp (78%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/ir_Kelvinator.cpp (58%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/ir_Kelvinator.h (71%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/ir_LG.cpp (96%) create mode 100644 lib/IRremoteESP8266-2.6.5/src/ir_LG.h rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/ir_Lasertag.cpp (91%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/ir_Lego.cpp (74%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/ir_Lutron.cpp (90%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/ir_MWM.cpp (94%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/ir_Magiquest.cpp (100%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/ir_Magiquest.h (100%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/ir_Midea.cpp (56%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/ir_Midea.h (60%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/ir_Mitsubishi.cpp (66%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/ir_Mitsubishi.h (58%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/ir_MitsubishiHeavy.cpp (73%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/ir_MitsubishiHeavy.h (86%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/ir_NEC.cpp (78%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/ir_NEC.h (86%) create mode 100644 lib/IRremoteESP8266-2.6.5/src/ir_Neoclima.cpp create mode 100644 lib/IRremoteESP8266-2.6.5/src/ir_Neoclima.h rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/ir_Nikai.cpp (58%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/ir_Panasonic.cpp (69%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/ir_Panasonic.h (67%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/ir_Pioneer.cpp (55%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/ir_Pronto.cpp (92%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/ir_RC5_RC6.cpp (97%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/ir_RCMM.cpp (93%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/ir_Samsung.cpp (64%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/ir_Samsung.h (53%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/ir_Sanyo.cpp (96%) create mode 100644 lib/IRremoteESP8266-2.6.5/src/ir_Sharp.cpp create mode 100644 lib/IRremoteESP8266-2.6.5/src/ir_Sharp.h rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/ir_Sherwood.cpp (66%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/ir_Sony.cpp (88%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/ir_Tcl.cpp (69%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/ir_Tcl.h (84%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/ir_Teco.cpp (53%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/ir_Teco.h (78%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/ir_Toshiba.cpp (62%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/ir_Toshiba.h (62%) create mode 100644 lib/IRremoteESP8266-2.6.5/src/ir_Trotec.cpp rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/ir_Trotec.h (63%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/ir_Vestel.cpp (71%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/ir_Vestel.h (83%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/ir_Whirlpool.cpp (62%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/ir_Whirlpool.h (75%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/src/ir_Whynter.cpp (69%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/test/IRac_test.cpp (53%) create mode 100644 lib/IRremoteESP8266-2.6.5/test/IRrecv_test.cpp rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/test/IRrecv_test.h (100%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/test/IRsend_test.cpp (84%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/test/IRsend_test.h (100%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/test/IRutils_test.cpp (74%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/test/Makefile (89%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/test/ir_Aiwa_test.cpp (100%) create mode 100644 lib/IRremoteESP8266-2.6.5/test/ir_Amcor_test.cpp create mode 100644 lib/IRremoteESP8266-2.6.5/test/ir_Argo_test.cpp rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/test/ir_Carrier_test.cpp (100%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/test/ir_Coolix_test.cpp (79%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/test/ir_Daikin_test.cpp (62%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/test/ir_Denon_test.cpp (91%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/test/ir_Dish_test.cpp (100%) create mode 100644 lib/IRremoteESP8266-2.6.5/test/ir_Electra_test.cpp create mode 100644 lib/IRremoteESP8266-2.6.5/test/ir_Fujitsu_test.cpp rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/test/ir_GICable_test.cpp (96%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/test/ir_GlobalCache_test.cpp (100%) create mode 100644 lib/IRremoteESP8266-2.6.5/test/ir_Goodweather_test.cpp rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/test/ir_Gree_test.cpp (76%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/test/ir_Haier_test.cpp (90%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/test/ir_Hitachi_test.cpp (96%) create mode 100644 lib/IRremoteESP8266-2.6.5/test/ir_Inax_test.cpp rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/test/ir_JVC_test.cpp (100%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/test/ir_Kelvinator_test.cpp (93%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/test/ir_LG_test.cpp (99%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/test/ir_Lasertag_test.cpp (100%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/test/ir_Lego_test.cpp (100%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/test/ir_Lutron_test.cpp (98%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/test/ir_MWM_test.cpp (100%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/test/ir_Magiquest_test.cpp (100%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/test/ir_Midea_test.cpp (86%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/test/ir_MitsubishiHeavy_test.cpp (87%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/test/ir_Mitsubishi_test.cpp (97%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/test/ir_NEC_test.cpp (100%) create mode 100644 lib/IRremoteESP8266-2.6.5/test/ir_Neoclima_test.cpp rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/test/ir_Nikai_test.cpp (100%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/test/ir_Panasonic_test.cpp (95%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/test/ir_Pioneer_test.cpp (60%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/test/ir_Pronto_test.cpp (100%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/test/ir_RC5_RC6_test.cpp (100%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/test/ir_RCMM_test.cpp (100%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/test/ir_Samsung_test.cpp (75%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/test/ir_Sanyo_test.cpp (100%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/test/ir_Sharp_test.cpp (52%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/test/ir_Sherwood_test.cpp (100%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/test/ir_Sony_test.cpp (96%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/test/ir_Tcl_test.cpp (78%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/test/ir_Teco_test.cpp (77%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/test/ir_Toshiba_test.cpp (96%) create mode 100644 lib/IRremoteESP8266-2.6.5/test/ir_Trotec_test.cpp rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/test/ir_Vestel_test.cpp (88%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/test/ir_Whirlpool_test.cpp (93%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/test/ir_Whynter_test.cpp (97%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/tools/Makefile (86%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/tools/RawToGlobalCache.sh (100%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/tools/auto_analyse_raw_data.py (98%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/tools/auto_analyse_raw_data_test.py (97%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/tools/gc_decode.cpp (100%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/tools/mkkeywords (82%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.5}/tools/mode2_decode.cpp (100%) create mode 100644 lib/IRremoteESP8266-2.6.5/tools/scrape_supported_devices.py create mode 100644 lib/JaretBurkett_ILI9488-gemu-1.0/ILI9488.cpp create mode 100644 lib/JaretBurkett_ILI9488-gemu-1.0/ILI9488.h create mode 100644 lib/JaretBurkett_ILI9488-gemu-1.0/README.md create mode 100644 lib/JaretBurkett_ILI9488-gemu-1.0/examples/graphicstest/graphicstest.ino create mode 100644 lib/JaretBurkett_ILI9488-gemu-1.0/keywords.txt create mode 100644 lib/JaretBurkett_ILI9488-gemu-1.0/library.properties create mode 100644 lib/JaretBurkett_ILI9488-gemu-1.0/spi_register.h delete mode 100644 lib/NeoPixelBus-2.2.9/COPYING delete mode 100644 lib/NeoPixelBus-2.2.9/library.json delete mode 100644 lib/NeoPixelBus-2.2.9/src/internal/NeoEsp8266UartMethod.cpp delete mode 100644 lib/NeoPixelBus-2.2.9/src/internal/NeoEsp8266UartMethod.h delete mode 100644 lib/NeoPixelBus-2.2.9/src/internal/NeoPixelEsp.c rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/.gitattributes (100%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/.gitignore (100%) create mode 100644 lib/NeoPixelBus-2.5.0.09/COPYING rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/ReadMe.md (70%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/examples/DotStarTest/DotStarTest.ino (100%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/examples/NeoPixelBrightness/NeoPixelBrightness.ino (100%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/examples/NeoPixelGamma/NeoPixelGamma.ino (100%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/examples/NeoPixelTest/NeoPixelTest.ino (91%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/examples/animations/NeoPixelAnimation/NeoPixelAnimation.ino (98%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/examples/animations/NeoPixelCylon/NeoPixelCylon.ino (100%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/examples/animations/NeoPixelFunFadeInOut/NeoPixelFunFadeInOut.ino (92%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/examples/animations/NeoPixelFunLoop/NeoPixelFunLoop.ino (97%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/examples/animations/NeoPixelFunRandomChange/NeoPixelFunRandomChange.ino (96%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/examples/animations/NeoPixelRotateLoop/NeoPixelRotateLoop.ino (100%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/examples/bitmaps/NeoPixelBitmap/NeoPixelBitmap.ino (100%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/examples/bitmaps/NeoPixelBitmap/Strings.bmp (100%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/examples/bitmaps/NeoPixelBitmap/StringsW.bmp (100%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/examples/bitmaps/NeoPixelBufferCylon/Cylon.pdn (100%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/examples/bitmaps/NeoPixelBufferCylon/CylonGrb.h (100%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/examples/bitmaps/NeoPixelBufferCylon/CylonGrbw.h (100%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/examples/bitmaps/NeoPixelBufferCylon/NeoPixelBufferCylon.ino (100%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/examples/bitmaps/NeoPixelBufferShader/NeoPixelBufferShader.ino (98%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/examples/bitmaps/NeoPixelDibTest/NeoPixelDibTest.ino (97%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/examples/topologies/NeoPixelMosaicDump/NeoPixelMosaicDump.ino (100%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/examples/topologies/NeoPixelMosaicTest/NeoPixelMosaicTest.ino (100%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/examples/topologies/NeoPixelRingTopologyTest/NeoPixelRingTopologyTest.ino (100%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/examples/topologies/NeoPixelTilesDump/NeoPixelTilesDump.ino (100%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/examples/topologies/NeoPixelTilesTest/NeoPixelTilesTest.ino (100%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/examples/topologies/NeoPixelTopologyDump/NeoPixelTopologyDump.ino (100%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/examples/topologies/NeoPixelTopologyTest/NeoPixelTopologyTest.ino (100%) create mode 100644 lib/NeoPixelBus-2.5.0.09/extras/curves/circular.png create mode 100644 lib/NeoPixelBus-2.5.0.09/extras/curves/cubic.png create mode 100644 lib/NeoPixelBus-2.5.0.09/extras/curves/different.png create mode 100644 lib/NeoPixelBus-2.5.0.09/extras/curves/exponential.png create mode 100644 lib/NeoPixelBus-2.5.0.09/extras/curves/gamma.png create mode 100644 lib/NeoPixelBus-2.5.0.09/extras/curves/pronounced.png create mode 100644 lib/NeoPixelBus-2.5.0.09/extras/curves/quadratic.png create mode 100644 lib/NeoPixelBus-2.5.0.09/extras/curves/quintic.png create mode 100644 lib/NeoPixelBus-2.5.0.09/extras/curves/sinusoidal.png rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/keywords.txt (54%) create mode 100644 lib/NeoPixelBus-2.5.0.09/library.json rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/library.properties (77%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/src/NeoPixelAnimator.h (95%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/src/NeoPixelBrightnessBus.h (100%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/src/NeoPixelBus.h (94%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/src/internal/DotStarAvrMethod.h (99%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/src/internal/DotStarColorFeatures.h (52%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/src/internal/DotStarGenericMethod.h (99%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/src/internal/DotStarSpiMethod.h (81%) create mode 100644 lib/NeoPixelBus-2.5.0.09/src/internal/Esp32_i2s.c create mode 100644 lib/NeoPixelBus-2.5.0.09/src/internal/Esp32_i2s.h rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/src/internal/HsbColor.cpp (100%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/src/internal/HsbColor.h (100%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/src/internal/HslColor.cpp (100%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/src/internal/HslColor.h (100%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/src/internal/HtmlColor.cpp (100%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/src/internal/HtmlColor.h (99%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/src/internal/HtmlColorNameStrings.cpp (100%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/src/internal/HtmlColorNameStrings.h (100%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/src/internal/HtmlColorNames.cpp (100%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/src/internal/HtmlColorShortNames.cpp (100%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/src/internal/Layouts.h (93%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/src/internal/NeoArmMethod.h (82%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/src/internal/NeoAvrMethod.h (90%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/src/internal/NeoBitmapFile.h (84%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/src/internal/NeoBuffer.h (84%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/src/internal/NeoBufferContext.h (100%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/src/internal/NeoBufferMethods.h (100%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/src/internal/NeoColorFeatures.h (100%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/src/internal/NeoDib.h (90%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/src/internal/NeoEase.h (72%) create mode 100644 lib/NeoPixelBus-2.5.0.09/src/internal/NeoEsp32I2sMethod.h create mode 100644 lib/NeoPixelBus-2.5.0.09/src/internal/NeoEsp32RmtMethod.h rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/src/internal/NeoEsp8266DmaMethod.h (75%) create mode 100644 lib/NeoPixelBus-2.5.0.09/src/internal/NeoEsp8266UartMethod.cpp create mode 100644 lib/NeoPixelBus-2.5.0.09/src/internal/NeoEsp8266UartMethod.h rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/src/internal/NeoEspBitBangMethod.h (73%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/src/internal/NeoGamma.cpp (100%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/src/internal/NeoGamma.h (95%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/src/internal/NeoHueBlend.h (100%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/src/internal/NeoMosaic.h (100%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/src/internal/NeoPixelAnimator.cpp (84%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/src/internal/NeoPixelAvr.c (99%) create mode 100644 lib/NeoPixelBus-2.5.0.09/src/internal/NeoPixelEsp.c rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/src/internal/NeoRingTopology.h (80%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/src/internal/NeoSpriteSheet.h (97%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/src/internal/NeoTiles.h (100%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/src/internal/NeoTopology.h (100%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/src/internal/RgbColor.cpp (100%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/src/internal/RgbColor.h (100%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/src/internal/RgbwColor.cpp (100%) rename lib/{NeoPixelBus-2.2.9 => NeoPixelBus-2.5.0.09}/src/internal/RgbwColor.h (100%) rename lib/{TasmotaModbus-1.1.0 => TasmotaModbus-1.2.0}/README.md (100%) rename lib/{TasmotaModbus-1.1.0 => TasmotaModbus-1.2.0}/examples/modbustest/modbustest.ino (100%) rename lib/{TasmotaModbus-1.1.0 => TasmotaModbus-1.2.0}/keywords.txt (100%) rename lib/{TasmotaModbus-1.1.0 => TasmotaModbus-1.2.0}/library.json (93%) rename lib/{TasmotaModbus-1.1.0 => TasmotaModbus-1.2.0}/library.properties (93%) rename lib/{TasmotaModbus-1.1.0 => TasmotaModbus-1.2.0}/src/TasmotaModbus.cpp (67%) rename lib/{TasmotaModbus-1.1.0 => TasmotaModbus-1.2.0}/src/TasmotaModbus.h (76%) delete mode 100644 lib/TasmotaSerial-2.3.1/src/TasmotaSerial.cpp rename lib/{TasmotaSerial-2.3.1 => TasmotaSerial-2.4.1}/README.md (100%) rename lib/{TasmotaSerial-2.3.1 => TasmotaSerial-2.4.1}/examples/swsertest/swsertest.ino (100%) rename lib/{TasmotaSerial-2.3.1 => TasmotaSerial-2.4.1}/keywords.txt (100%) rename lib/{TasmotaSerial-2.3.1 => TasmotaSerial-2.4.1}/library.json (94%) rename lib/{TasmotaSerial-2.3.1 => TasmotaSerial-2.4.1}/library.properties (94%) create mode 100644 lib/TasmotaSerial-2.4.1/src/TasmotaSerial.cpp rename lib/{TasmotaSerial-2.3.1 => TasmotaSerial-2.4.1}/src/TasmotaSerial.h (75%) create mode 100644 lib/Xlatb_RA8876-gemu-1.0/RA8876.cpp create mode 100644 lib/Xlatb_RA8876-gemu-1.0/RA8876.h create mode 100644 lib/Xlatb_RA8876-gemu-1.0/README.md create mode 100644 lib/Xlatb_RA8876-gemu-1.0/keywords.txt create mode 100644 lib/Xlatb_RA8876-gemu-1.0/library.properties create mode 100644 lib/Xlatb_RA8876-gemu-1.0/spi_register.h create mode 100644 lib/base64-1.1.1/LICENSE create mode 100644 lib/base64-1.1.1/Makefile create mode 100644 lib/base64-1.1.1/README.md create mode 100644 lib/base64-1.1.1/catch.cpp create mode 100644 lib/base64-1.1.1/catch.hpp create mode 100644 lib/base64-1.1.1/library.properties create mode 100644 lib/base64-1.1.1/src/base64.hpp delete mode 100644 lib/esp-epaper-29-ws-20171230-gemu-1.0/src/epdpaint.cpp delete mode 100644 lib/esp-epaper-29-ws-20171230-gemu-1.0/src/font12.c delete mode 100644 lib/esp-epaper-29-ws-20171230-gemu-1.0/src/font16.c delete mode 100644 lib/esp-epaper-29-ws-20171230-gemu-1.0/src/font20.c delete mode 100644 lib/esp-epaper-29-ws-20171230-gemu-1.0/src/font24.c delete mode 100644 lib/esp-epaper-29-ws-20171230-gemu-1.0/src/font8.c rename lib/{esp-epaper-29-ws-20171230-gemu-1.0 => esp-epaper-29-ws-20171230-gemu-1.1}/.gitignore (100%) rename lib/{esp-epaper-29-ws-20171230-gemu-1.0 => esp-epaper-29-ws-20171230-gemu-1.1}/.travis.yml (100%) rename lib/{esp-epaper-29-ws-20171230-gemu-1.0 => esp-epaper-29-ws-20171230-gemu-1.1}/Arduino/epd2in9-demo/epd2in9-demo.ino (100%) rename lib/{esp-epaper-29-ws-20171230-gemu-1.0 => esp-epaper-29-ws-20171230-gemu-1.1}/Arduino/libraries/readme.txt (100%) rename lib/{esp-epaper-29-ws-20171230-gemu-1.0 => esp-epaper-29-ws-20171230-gemu-1.1}/LICENSE (100%) rename lib/{esp-epaper-29-ws-20171230-gemu-1.0 => esp-epaper-29-ws-20171230-gemu-1.1}/Makefile (100%) rename lib/{esp-epaper-29-ws-20171230-gemu-1.0 => esp-epaper-29-ws-20171230-gemu-1.1}/README.md (100%) rename lib/{esp-epaper-29-ws-20171230-gemu-1.0 => esp-epaper-29-ws-20171230-gemu-1.1}/components/epaper-29-ws/component.mk (100%) rename lib/{esp-epaper-29-ws-20171230-gemu-1.0 => esp-epaper-29-ws-20171230-gemu-1.1}/components/epaper-29-ws/epaper-29-ws.c (100%) rename lib/{esp-epaper-29-ws-20171230-gemu-1.0 => esp-epaper-29-ws-20171230-gemu-1.1}/components/epaper-29-ws/epaper-29-ws.h (100%) rename lib/{esp-epaper-29-ws-20171230-gemu-1.0 => esp-epaper-29-ws-20171230-gemu-1.1}/components/epaper-29-ws/epaper_font.c (100%) rename lib/{esp-epaper-29-ws-20171230-gemu-1.0 => esp-epaper-29-ws-20171230-gemu-1.1}/components/epaper-29-ws/epaper_fonts.h (100%) rename lib/{esp-epaper-29-ws-20171230-gemu-1.0 => esp-epaper-29-ws-20171230-gemu-1.1}/components/epaper-29-ws/font16.c (100%) rename lib/{esp-epaper-29-ws-20171230-gemu-1.0 => esp-epaper-29-ws-20171230-gemu-1.1}/components/epaper-29-ws/font20.c (100%) rename lib/{esp-epaper-29-ws-20171230-gemu-1.0 => esp-epaper-29-ws-20171230-gemu-1.1}/components/epaper-29-ws/font8.c (100%) rename lib/{esp-epaper-29-ws-20171230-gemu-1.0 => esp-epaper-29-ws-20171230-gemu-1.1}/components/epaper-29-ws/imagedata.cpp (100%) rename lib/{esp-epaper-29-ws-20171230-gemu-1.0 => esp-epaper-29-ws-20171230-gemu-1.1}/components/epaper-29-ws/imagedata.h (100%) rename lib/{esp-epaper-29-ws-20171230-gemu-1.0 => esp-epaper-29-ws-20171230-gemu-1.1}/docs/Doxyfile (100%) rename lib/{esp-epaper-29-ws-20171230-gemu-1.0 => esp-epaper-29-ws-20171230-gemu-1.1}/docs/Makefile (100%) rename lib/{esp-epaper-29-ws-20171230-gemu-1.0 => esp-epaper-29-ws-20171230-gemu-1.1}/docs/README.md (100%) rename lib/{esp-epaper-29-ws-20171230-gemu-1.0 => esp-epaper-29-ws-20171230-gemu-1.1}/docs/conf.py (100%) rename lib/{esp-epaper-29-ws-20171230-gemu-1.0 => esp-epaper-29-ws-20171230-gemu-1.1}/docs/gen-dxd.py (100%) rename lib/{esp-epaper-29-ws-20171230-gemu-1.0 => esp-epaper-29-ws-20171230-gemu-1.1}/docs/index.rst (100%) rename lib/{esp-epaper-29-ws-20171230-gemu-1.0 => esp-epaper-29-ws-20171230-gemu-1.1}/docs/link-roles.py (100%) rename lib/{esp-epaper-29-ws-20171230-gemu-1.0 => esp-epaper-29-ws-20171230-gemu-1.1}/docs/repo_util.py (100%) rename lib/{esp-epaper-29-ws-20171230-gemu-1.0 => esp-epaper-29-ws-20171230-gemu-1.1}/docs/requirements.txt (100%) rename lib/{esp-epaper-29-ws-20171230-gemu-1.0 => esp-epaper-29-ws-20171230-gemu-1.1}/library.properties (85%) rename lib/{esp-epaper-29-ws-20171230-gemu-1.0 => esp-epaper-29-ws-20171230-gemu-1.1}/main/README.md (100%) rename lib/{esp-epaper-29-ws-20171230-gemu-1.0 => esp-epaper-29-ws-20171230-gemu-1.1}/main/component.mk (100%) rename lib/{esp-epaper-29-ws-20171230-gemu-1.0 => esp-epaper-29-ws-20171230-gemu-1.1}/main/esp-epaper-29-ws.c (100%) rename lib/{esp-epaper-29-ws-20171230-gemu-1.0 => esp-epaper-29-ws-20171230-gemu-1.1}/main/imagedata.c (100%) rename lib/{esp-epaper-29-ws-20171230-gemu-1.0 => esp-epaper-29-ws-20171230-gemu-1.1}/main/imagedata.h (100%) rename lib/{esp-epaper-29-ws-20171230-gemu-1.0 => esp-epaper-29-ws-20171230-gemu-1.1}/pictures/2.9inch_e-Paper_Datasheet.pdf (100%) rename lib/{esp-epaper-29-ws-20171230-gemu-1.0 => esp-epaper-29-ws-20171230-gemu-1.1}/pictures/e-paper-and-esp-sample-image.jpg (100%) rename lib/{esp-epaper-29-ws-20171230-gemu-1.0 => esp-epaper-29-ws-20171230-gemu-1.1}/pictures/e-paper-and-esp-sample-text.jpg (100%) rename lib/{esp-epaper-29-ws-20171230-gemu-1.0 => esp-epaper-29-ws-20171230-gemu-1.1}/pictures/espresif-logo.bmp (100%) rename lib/{esp-epaper-29-ws-20171230-gemu-1.0 => esp-epaper-29-ws-20171230-gemu-1.1}/pictures/image-conversion-setup.png (100%) rename lib/{esp-epaper-29-ws-20171230-gemu-1.0 => esp-epaper-29-ws-20171230-gemu-1.1}/src/epd2in9.cpp (71%) rename lib/{esp-epaper-29-ws-20171230-gemu-1.0 => esp-epaper-29-ws-20171230-gemu-1.1}/src/epd2in9.h (89%) create mode 100644 lib/esp-epaper-29-ws-20171230-gemu-1.1/src/epd4in2.cpp create mode 100644 lib/esp-epaper-29-ws-20171230-gemu-1.1/src/epd4in2.h rename lib/{esp-epaper-29-ws-20171230-gemu-1.0 => esp-epaper-29-ws-20171230-gemu-1.1}/src/epdif.cpp (100%) rename lib/{esp-epaper-29-ws-20171230-gemu-1.0 => esp-epaper-29-ws-20171230-gemu-1.1}/src/epdif.h (100%) create mode 100644 lib/esp-epaper-29-ws-20171230-gemu-1.1/src/epdpaint.cpp rename lib/{esp-epaper-29-ws-20171230-gemu-1.0 => esp-epaper-29-ws-20171230-gemu-1.1}/src/epdpaint.h (58%) create mode 100644 lib/esp-epaper-29-ws-20171230-gemu-1.1/src/font12.c create mode 100644 lib/esp-epaper-29-ws-20171230-gemu-1.1/src/font16.c create mode 100644 lib/esp-epaper-29-ws-20171230-gemu-1.1/src/font20.c create mode 100644 lib/esp-epaper-29-ws-20171230-gemu-1.1/src/font24.c create mode 100644 lib/esp-epaper-29-ws-20171230-gemu-1.1/src/font8.c rename lib/{esp-epaper-29-ws-20171230-gemu-1.0 => esp-epaper-29-ws-20171230-gemu-1.1}/src/fonts.h (99%) create mode 100644 lib/esp-epaper-29-ws-20171230-gemu-1.1/src/renderer.cpp create mode 100644 lib/esp-epaper-29-ws-20171230-gemu-1.1/src/renderer.h rename lib/{esp-knx-ip-0.5.1 => esp-knx-ip-0.5.2}/DPT.h (95%) rename lib/{esp-knx-ip-0.5.1 => esp-knx-ip-0.5.2}/LICENSE (100%) rename lib/{esp-knx-ip-0.5.1 => esp-knx-ip-0.5.2}/README.md (100%) rename lib/{esp-knx-ip-0.5.1 => esp-knx-ip-0.5.2}/esp-knx-ip-config.cpp (100%) rename lib/{esp-knx-ip-0.5.1 => esp-knx-ip-0.5.2}/esp-knx-ip-conversion.cpp (100%) rename lib/{esp-knx-ip-0.5.1 => esp-knx-ip-0.5.2}/esp-knx-ip-send.cpp (93%) rename lib/{esp-knx-ip-0.5.1 => esp-knx-ip-0.5.2}/esp-knx-ip-webserver.cpp (100%) rename lib/{esp-knx-ip-0.5.1 => esp-knx-ip-0.5.2}/esp-knx-ip.cpp (100%) rename lib/{esp-knx-ip-0.5.1 => esp-knx-ip-0.5.2}/esp-knx-ip.h (98%) rename lib/{esp-knx-ip-0.5.1 => esp-knx-ip-0.5.2}/examples/environment-sensor/environment-sensor.ino (100%) rename lib/{esp-knx-ip-0.5.1 => esp-knx-ip-0.5.2}/examples/sonoff/sonoff.ino (100%) rename lib/{esp-knx-ip-0.5.1 => esp-knx-ip-0.5.2}/examples/static-config/static-config.ino (87%) rename lib/{esp-knx-ip-0.5.1 => esp-knx-ip-0.5.2}/keywords.txt (83%) rename lib/{esp-knx-ip-0.5.1 => esp-knx-ip-0.5.2}/library.properties (95%) delete mode 100644 lib/readme.txt create mode 100644 pio/obj-dump.py create mode 100644 sonoff/sendemail.h create mode 100644 sonoff/sendemail.ino delete mode 100644 sonoff/sonoff_aws_iot.cpp create mode 100644 sonoff/support_command.ino create mode 100644 sonoff/support_static_buffer.ino create mode 100644 sonoff/xdrv_05_irremote_full.ino delete mode 100644 sonoff/xdrv_16_tuyadimmer.ino create mode 100644 sonoff/xdrv_16_tuyamcu.ino create mode 100644 sonoff/xdrv_22_sonoff_ifan.ino create mode 100644 sonoff/xdrv_23_zigbee_0_constants.ino create mode 100644 sonoff/xdrv_23_zigbee_3_devices.ino create mode 100644 sonoff/xdrv_23_zigbee_5_converters.ino create mode 100644 sonoff/xdrv_23_zigbee_6_commands.ino create mode 100644 sonoff/xdrv_23_zigbee_7_statemachine.ino create mode 100644 sonoff/xdrv_23_zigbee_8_parsers.ino create mode 100644 sonoff/xdrv_23_zigbee_9_impl.ino create mode 100644 sonoff/xdrv_24_buzzer.ino create mode 100644 sonoff/xdrv_25_A4988_Stepper.ino create mode 100644 sonoff/xdrv_26_ariluxrf.ino create mode 100644 sonoff/xdrv_27_shutter.ino create mode 100644 sonoff/xdrv_28_pcf8574.ino create mode 100644 sonoff/xdrv_29_deepsleep.ino create mode 100644 sonoff/xdrv_30_exs_dimmer.ino create mode 100644 sonoff/xdrv_31_arduino_slave.ino create mode 100644 sonoff/xdsp_06_epaper_42.ino create mode 100644 sonoff/xdsp_07_sh1106.ino create mode 100644 sonoff/xdsp_08_ILI9488.ino create mode 100644 sonoff/xdsp_09_SSD1351.ino create mode 100644 sonoff/xdsp_10_RA8876.ino rename sonoff/{xplg_ws2812.ino => xlgt_01_ws2812.ino} (54%) create mode 100644 sonoff/xlgt_02_my92x1.ino create mode 100644 sonoff/xlgt_03_sm16716.ino create mode 100644 sonoff/xlgt_04_sm2135.ino create mode 100644 sonoff/xlgt_05_sonoff_l1.ino create mode 100644 sonoff/xlgt_interface.ino create mode 100644 sonoff/xnrg_08_sdm120.ino create mode 100644 sonoff/xnrg_09_dds2382.ino create mode 100644 sonoff/xnrg_10_sdm630.ino create mode 100644 sonoff/xnrg_11_ddsu666.ino create mode 100644 sonoff/xnrg_12_solaxX1.ino delete mode 100644 sonoff/xsns_05_ds18b20.ino delete mode 100644 sonoff/xsns_05_ds18x20_legacy.ino delete mode 100644 sonoff/xsns_23_sdm120.ino delete mode 100644 sonoff/xsns_25_sdm630.ino create mode 100644 sonoff/xsns_47_max31865.ino create mode 100644 sonoff/xsns_48_chirp.ino create mode 100644 sonoff/xsns_50_paj7620.ino create mode 100644 sonoff/xsns_51_rdm6300.ino create mode 100644 sonoff/xsns_52_ibeacon.ino create mode 100644 sonoff/xsns_53_sml.ino create mode 100644 sonoff/xsns_54_ina226.ino delete mode 100644 sonoff/zzzz_debug.ino create mode 100644 tools/fw_efm8bb1/RF-Bridge-EFM8BB1-20191006.hex diff --git a/.gitignore b/.gitignore index 2c1ceae12..acdd5c610 100644 --- a/.gitignore +++ b/.gitignore @@ -7,9 +7,11 @@ .piolibdeps .clang_complete .gcc-flags.json +.cache sonoff/user_config_override.h build firmware.map +firmware.asm ## Visual Studio Code specific ###### .vscode diff --git a/.travis.yml b/.travis.yml index 8e950a42e..3f4121386 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,14 +1,48 @@ language: python python: - - '2.7' + - '3.7' sudo: false -cache: - directories: - - "~/.platformio" install: - pip install -U platformio + - platformio upgrade + - platformio update + +cache: + directories: + - .cache + - $HOME/.platformio + +env: + - ENV=sonoff + - ENV=sonoff-minimal + - ENV=sonoff-basic + - ENV=sonoff-knx + - ENV=sonoff-sensors + - ENV=sonoff-display + - ENV=sonoff-ir + - ENV=sonoff-BG + - ENV=sonoff-BR + - ENV=sonoff-CN + - ENV=sonoff-CZ + - ENV=sonoff-DE + - ENV=sonoff-ES + - ENV=sonoff-FR + - ENV=sonoff-GR + - ENV=sonoff-HE + - ENV=sonoff-HU + - ENV=sonoff-IT + - ENV=sonoff-KO + - ENV=sonoff-NL + - ENV=sonoff-PL + - ENV=sonoff-PT + - ENV=sonoff-RU + - ENV=sonoff-SE + - ENV=sonoff-SK + - ENV=sonoff-TR + - ENV=sonoff-TW + - ENV=sonoff-UK script: - - platformio run + - platformio run -e $ENV before_deploy: - for file in .pioenvs/*/firmware.bin; do cp $file ${file%/*}.bin; done diff --git a/README.md b/README.md index 63c82e3db..57249de6c 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Alternative firmware for _ESP8266 based devices_ like [iTead](https://www.itead. [![License](https://img.shields.io/github/license/arendst/Sonoff-Tasmota.svg)](https://github.com/arendst/Sonoff-Tasmota/blob/master/LICENSE.txt) [![Chat](https://img.shields.io/discord/479389167382691863.svg)](https://discord.gg/Ks2Kzd4) -If you like **Sonoff-Tasmota**, give it a star, or fork it and contribute! +If you like **Sonoff-Tasmota**, give it a star, or fork it, and contribute! [![GitHub stars](https://img.shields.io/github/stars/arendst/Sonoff-Tasmota.svg?style=social&label=Star)](https://github.com/arendst/Sonoff-Tasmota/stargazers) [![GitHub forks](https://img.shields.io/github/forks/arendst/Sonoff-Tasmota.svg?style=social&label=Fork)](https://github.com/arendst/Sonoff-Tasmota/network) @@ -25,12 +25,16 @@ In addition to the [release webpage](https://github.com/arendst/Sonoff-Tasmota/r See [sonoff/_changelog.ino](https://github.com/arendst/Sonoff-Tasmota/blob/development/sonoff/_changelog.ino) for detailed change information. -The development codebase is checked hourly for changes and if new commits have been merged and compile successfuly they will be posted at http://thehackbox.org/tasmota/ (this web address can be used for OTA too). It is important to note that these are based on the current development codebase and it is not recommended to flash it to devices used in production or which are hard to reach in the event that you need to manually flash the device if OTA failed. The last compiled commit number is also posted on the same page along with the current build status (if a firmware rebuild is in progress). +Unless your Tasmota powered device exhibits a problem or you need to make use of a feature that is not available in the Tasmota version currently installed on your device, leave your device alone - it works so don't make unnecessary changes! If the release version (i.e., the master branch) exhibits unexpected behaviour for your device and configuration, you should upgrade to the latest development version instead to see if your problem is resolved as some bugs in previous releases or development builds may already have been resolved. + +The Tasmota development codebase is checked every 1-2 hours for changes. If new commits have been merged and they compile successfuly, new binary files for every variant (excluding non-English languages) will be posted at http://thehackbox.org/tasmota/ (this web address can be used for OTA updates too). The last compiled commit number is also indicated on the same page. It is important to note that these binaries are based on the current development codebase. These commits are tested as much as is possible and are typically quite stable. However, it is infeasible to test on the hundreds of different types of devices with all the available configuration options permitted. + +Note that there is a chance, as with any upgrade, that the device may not function as expected. You must always account for the possibility that you may need to flash the device via the serial programming interface if the OTA upgrade fails. Even with the master release, you should always attempt to test the device or a similar prototype before upgrading a device which is in production or is hard to reach. And, as always, make a backup of the device configuration before beginning any firmware update. ## Disclaimer :warning: **DANGER OF ELECTROCUTION** :warning: -A Sonoff device is not a toy. It uses Mains AC so there is a danger of electrocution if not installed properly. If you don't know how to install it, please call an electrician. Remember: _**SAFETY FIRST**_. It is not worth risk to yourself, your family, and your home if you don't know exactly what you are doing. Never try to flash a Sonoff device while it is connected to MAINS AC. +An ESP82xx Wi-Fi device is not a toy. It uses Mains AC so there is a danger of electrocution if not installed properly. If you don't know how to install it, please call an electrician. Remember: _**SAFETY FIRST**_. It is not worth risk to yourself, your family, and your home if you don't know exactly what you are doing. Never try to flash a device using the serial programming interface while it is connected to MAINS AC. We don't take any responsibility nor liability for using this software nor for the installation or any tips, advice, videos, etc. given by any member of this site or any related site. @@ -43,14 +47,12 @@ Download one of the released binaries from https://github.com/arendst/Sonoff-Tas ## Important User Compilation Information If you want to compile Sonoff-Tasmota yourself keep in mind the following: -- Only Flash Mode **DOUT** is supported. Do not use Flash Mode DIO / QIO / QOUT as it might seem to brick your device. See [Wiki](https://github.com/arendst/Sonoff-Tasmota/wiki/Theo's-Tasmota-Tips) for background information. -- Sonoff-Tasmota uses a 1M linker script WITHOUT spiffs **1M (no SPIFFS)** for optimal code space. If you compile using ESP/Arduino library 2.3.0 then download the provided new linker script to your Arduino IDE or Platformio base folder. Later version of ESP/Arduino library already contain the correct linker script. See [Wiki > Prerequisites](https://github.com/arendst/Sonoff-Tasmota/wiki/Prerequisites). +- Only Flash Mode **DOUT** is supported. Do not use Flash Mode DIO / QIO / QOUT as it might seem to brick your device. See [Wiki](https://github.com/arendst/Sonoff-Tasmota/wiki/Flashing) for background information. +- Sonoff-Tasmota uses a 1M linker script WITHOUT spiffs **1M (no SPIFFS)** for optimal code space. If you compile using ESP/Arduino library 2.3.0 then download the provided new linker script to your Arduino IDE or Platformio base folder. Later versions of ESP/Arduino library already contain the correct linker script. See [Wiki > Prerequisites](https://github.com/arendst/Sonoff-Tasmota/wiki/Prerequisites). - To make compile time changes to Sonoff-Tasmota it can use the ``user_config_override.h`` file. It assures keeping your settings when you download and compile a new version. To use ``user_config.override.h`` you will have to make a copy of the provided ``user_config_override_sample.h`` file and add your setting overrides. To enable the override file you will need to use a compile define as documented in the ``user_config_override_sample.h`` file. -## Version Information -- Sonoff-Tasmota provides all (Sonoff) modules in one file and starts with module Sonoff Basic. -- Once uploaded, select [Module](https://github.com/arendst/Sonoff-Tasmota/wiki/Modules) using the configuration webpage, the commands ```Modules``` and ```Module``` or configure the [Template](https://github.com/arendst/Sonoff-Tasmota/wiki/Templates) for your device -- After reboot select config menu again or use commands ```GPIOs``` and ```GPIO``` to change GPIO with desired sensor. +## Configuration Information +Please refer to the Installation and configuration articles in the [wiki](https://github.com/arendst/Sonoff-Tasmota/wiki). ## Migration Information See [wiki migration path](https://github.com/arendst/Sonoff-Tasmota/wiki/Upgrade#migration-path) for instructions how to migrate to a major version. Pay attention to the following version breaks due to dynamic settings updates: diff --git a/RELEASENOTES.md b/RELEASENOTES.md index d7084c559..89efea9d5 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -10,18 +10,16 @@ See [wiki migration path](https://github.com/arendst/Sonoff-Tasmota/wiki/Upgrade 3. Migrate to **Sonoff-Tasmota 5.14** 4. Migrate to **Sonoff-Tasmota 6.x** +## Supported Core versions +This release will be supported from ESP8266/Arduino library Core version **pre-2.6.0** due to reported security and stability issues on previous Core version. + +Although it might still compile on previous Core versions all support will be removed starting in the next Release. + ## Support of TLS -TLS support for core 2.3.0 is removed. +To save resources when TLS is enabled mDNS needs to be disabled. In addition to TLS using fingerprints now also user supplied CA certs and AWS IoT is supported. See full documentation on https://github.com/arendst/Sonoff-Tasmota/wiki/AWS-IoT -TLS is supported on core 2.4.2 and up. To save resources when TLS is enabled mDNS needs to be disabled. In addition to TLS using fingerprints now also user supplied CA certs and AWS IoT is supported. See full documentation on https://github.com/arendst/Sonoff-Tasmota/wiki/AWS-IoT - -## Core version 2.3.0 vs 2.4.2 vs 2.5.2 -This release is based on ESP8266/Arduino library core 2.3.0 as some people encountered wifi related issues on core 2.4.2 and 2.5.2. For others core 2.4.2 or 2.5.2 is working just fine. All version are available from http://thehackbox.org/tasmota/release/ - -## Change in default initial configuration tool -Firmware binary **sonoff-classic.bin** supports **WifiManager, Wps and SmartConfig** for initial configuration. The default tool is **Wps**. - -To save memory space all other binaries support **WifiManager only**. +## Initial configuration tools +For initial configuration this release supports Webserver based **WifiManager** or **Serial** based command interface only. Support for **WPS** and **SmartConfig** has been removed. ## Supported Modules The following hardware modules are supported. @@ -98,74 +96,109 @@ Module | Description 68 WAGA CHCZ02MB | WAGA life CHCZ02MB Wifi Smart Switch with Energy Monitoring 69 SYF05 | Sunyesmart SYF05 RGBWW Wifi Led Bulb 70 Sonoff L1 | Sonoff L1 light strip +71 Sonoff iFan03 | Sonoff iFan03 Wifi Smart Ceiling Fan with Light +72 EXS Dimmer | EXS Wifi Dimmer v4 ## Provided Binary Downloads -The following binary downloads have been compiled with ESP8266/Arduino library core version **2.3.0**. +The following binary downloads have been compiled with ESP8266/Arduino library core version **pre-2.6.0**. -- **sonoff.bin** = The Sonoff version without Wps and SmartConfig configuration but adds more sensors. **RECOMMENDED RELEASE BINARY** -- **sonoff-basic.bin** = The Basic version without Wps and SmartConfig configuration and most sensors. -- **sonoff-classic.bin** = The Classic version allows initial installation using either WifiManager, Wps or SmartConfig. -- **sonoff-BG.bin** to **sonoff-TW.bin** = The Sonoff version without Wps and SmartConfig configuration in different languages. -- **sonoff-knx.bin** = The Knx version without Wps and SmartConfig configuration and some other features but adds KNX support. -- **sonoff-sensors.bin** = The Sensors version without Wps and SmartConfig configuration but adds even more useful sensors. -- **sonoff-display.bin** = The Display version without Wps and SmartConfig configuration and Energy Monitoring but adds display support. +- **sonoff.bin** = The Sonoff version with sensors. **RECOMMENDED RELEASE BINARY** +- **sonoff-BG.bin** to **sonoff-TW.bin** = The Sonoff version in different languages. +- **sonoff-basic.bin** = The Basic version without most sensors. +- **sonoff-knx.bin** = The Knx version without some features but adds KNX support. +- **sonoff-sensors.bin** = The Sensors version adds more useful sensors. +- **sonoff-ir** = The InfraRed Receiver and transmitter version allowing all available protocols provided by library IRremoteESP8266 but without most other features. +- **sonoff-display.bin** = The Display version without Energy Monitoring but adds display support. - **sonoff-minimal.bin** = The Minimal version allows intermediate OTA uploads to support larger versions and does NOT change any persistent parameter. This version **should NOT be used for initial installation**. -Core version **2.4.2** binaries can be found at http://thehackbox.org/tasmota/release/020402/ - -Core version **2.5.2** binaries can be found at http://thehackbox.org/tasmota/release/020502/ - ## Available Features and Sensors -| Feature or Sensor | minimal | basic | classic | sonoff | knx | sensors | display | Remarks -|-----------------------|---------|-------|---------|--------|------|---------|---------|-------- +| Feature or Sensor | minimal | basic | sonoff | knx | sensors | ir | display | Remarks +|-----------------------|---------|-------|--------|-----|---------|----|---------|-------- | MY_LANGUAGE en-GB | x | x | x | x | x | x | x | -| USE_WPS | - | - | x | - | - | - | - | WPS -| USE_SMARTCONFIG | - | - | x | - | - | - | - | SmartConfig | USE_ARDUINO_OTA | - | - | - | - | - | - | - | | USE_DOMOTICZ | - | - | x | x | x | x | - | -| USE_HOME_ASSISTANT | - | - | - | x | x | x | - | +| USE_HOME_ASSISTANT | - | - | x | x | x | x | - | | USE_MQTT_TLS | - | - | - | - | - | - | - | | USE_MQTT_TLS_CA_CERT | - | - | - | - | - | - | - | | USE_MQTT_AWS_IOT | - | - | - | - | - | - | - | -| USE_KNX | - | - | - | - | x | - | - | -| USE_WEBSERVER | x | x | x | x | x | x | x | WifiManager -| USE_EMULATION_HUE | - | x | x | x | - | x | - | -| USE_EMULATION_WEMO | - | x | x | x | - | x | - | -| USE_DISCOVERY | - | - | x | x | x | x | x | -| WEBSERVER_ADVERTISE | - | - | x | x | x | x | x | -| MQTT_HOST_DISCOVERY | - | - | x | x | x | x | x | -| USE_TIMERS | - | x | - | x | x | x | x | -| USE_TIMERS_WEB | - | x | - | x | x | x | x | -| USE_SUNRISE | - | x | - | x | x | x | x | -| USE_RULES | - | x | - | x | x | x | x | +| USE_KNX | - | - | - | x | - | - | - | +| USE_WEBSERVER | x | x | x | x | x | x | x | +| USE_JAVASCRIPT_ES6 | - | - | - | - | - | - | - | +| USE_WEBSEND_RESPONSE | - | - | - | - | - | - | - | +| USE_EMULATION_HUE | - | x | x | - | x | - | - | +| USE_EMULATION_WEMO | - | x | x | - | x | - | - | +| USE_DISCOVERY | - | - | x | x | - | - | x | +| WEBSERVER_ADVERTISE | - | - | x | x | - | - | x | +| MQTT_HOST_DISCOVERY | - | - | x | x | - | - | x | +| USE_TIMERS | - | x | x | x | x | x | x | +| USE_TIMERS_WEB | - | x | x | x | x | x | x | +| USE_SUNRISE | - | x | x | x | x | x | x | +| USE_RULES | - | x | x | x | x | x | x | | USE_SCRIPT | - | - | - | - | - | - | - | | USE_EXPRESSION | - | - | - | - | - | - | - | +| SUPPORT_IF_STATEMENT | - | - | - | - | - | - | - | | | | | | | | | | -| USE_ADC_VCC | x | x | x | - | - | - | - | -| USE_COUNTER | - | - | - | x | x | x | x | -| USE_DS18B20 | - | - | - | - | - | - | - | Single sensor -| USE_DS18x20 | - | - | x | x | x | x | x | Multiple sensors -| USE_DS18x20_LEGACY | - | - | - | - | - | - | - | Multiple sensors +| Feature or Sensor | minimal | basic | sonoff | knx | sensors | ir | display | Remarks +| ROTARY_V1 | - | - | - | - | - | - | - | +| USE_SONOFF_RF | - | - | x | x | x | - | - | +| USE_RF_FLASH | - | - | x | x | x | - | - | +| USE_SONOFF_SC | - | - | x | - | x | - | - | +| USE_TUYA_MCU | - | x | x | x | x | - | x | +| USE_ARMTRONIX_DIMMERS | - | - | x | x | - | - | - | +| USE_PS_16_DZ | - | - | x | x | x | - | - | +| USE_SONOFF_IFAN | - | - | x | x | x | - | - | +| USE_BUZZER | - | - | x | x | x | - | - | +| USE_ARILUX_RF | - | - | x | x | x | - | - | +| USE_SHUTTER | - | - | - | - | - | - | - | +| USE_DEEPSLEEP | - | - | - | - | - | - | - | +| USE_EXS_DIMMER | - | - | - | - | - | - | - | +| | | | | | | | | +| Feature or Sensor | minimal | basic | sonoff | knx | sensors | ir | display | Remarks +| USE_LIGHT | - | x | x | x | x | x | x | +| USE_WS2812 | - | - | x | x | x | - | x | +| USE_WS2812_DMA | - | - | - | - | - | - | - | +| USE_MY92X1 | - | - | x | x | x | - | x | +| USE_SM16716 | - | - | x | x | x | - | x | +| USE_SM2135 | - | - | x | x | x | - | x | +| USE_SONOFF_L1 | - | - | x | x | x | - | x | +| | | | | | | | | +| USE_ENERGY_SENSOR | - | x | x | x | x | - | - | +| USE_PZEM004T | - | - | x | x | x | - | - | +| USE_PZEM_AC | - | - | x | x | x | - | - | +| USE_PZEM_DC | - | - | x | x | x | - | - | +| USE_MCP39F501 | - | x | x | x | x | - | - | +| USE_SDM120 | - | - | - | - | x | - | - | +| USE_SDM630 | - | - | - | - | x | - | - | +| USE_DDS2382 | - | - | - | - | x | - | - | +| USE_DDSU666 | - | - | - | - | x | - | - | +| USE_SOLAX_X1 | - | - | - | - | - | - | - | +| | | | | | | | | +| USE_ADC_VCC | x | x | - | - | - | - | - | +| USE_COUNTER | - | - | x | x | x | x | x | +| USE_DS18x20 | - | - | x | x | x | - | x | | USE_DHT | - | - | x | x | x | x | x | +| USE_MAX31855 | - | - | - | - | x | - | - | +| USE_MAX31865 | - | - | - | - | - | - | - | | | | | | | | | | -| Feature or Sensor | minimal | basic | classic | sonoff | knx | sensors | display | Remarks -| USE_I2C | - | - | - | x | x | x | x | -| USE_SHT | - | - | - | x | x | x | x | -| USE_HTU | - | - | - | x | x | x | x | -| USE_BMP | - | - | - | x | x | x | x | -| USE_BME680 | - | - | - | - | - | x | - | -| USE_BH1750 | - | - | - | x | x | x | x | -| USE_VEML6070 | - | - | - | - | - | x | - | -| USE_ADS1115 | - | - | - | - | - | x | - | +| Feature or Sensor | minimal | basic | sonoff | knx | sensors | ir | display | Remarks +| USE_I2C | - | - | x | x | x | - | x | +| USE_SHT | - | - | x | x | x | - | x | +| USE_HTU | - | - | x | x | x | - | x | +| USE_BMP | - | - | x | x | x | - | x | +| USE_BME680 | - | - | - | - | x | - | - | +| USE_BH1750 | - | - | x | x | x | - | x | +| USE_VEML6070 | - | - | - | - | x | - | - | +| USE_ADS1115 | - | - | - | - | x | - | - | | USE_ADS1115_I2CDEV | - | - | - | - | - | - | - | -| USE_INA219 | - | - | - | - | - | x | - | -| USE_SHT3X | - | - | - | x | x | x | x | -| USE_TSL2561 | - | - | - | - | - | x | - | -| USE_MGS | - | - | - | - | - | x | - | -| USE_SGP30 | - | - | - | x | x | x | x | +| USE_INA219 | - | - | - | - | x | - | - | +| USE_INA226 | - | - | - | - | - | - | - | +| USE_SHT3X | - | - | x | x | x | - | x | +| USE_TSL2561 | - | - | - | - | x | - | - | +| USE_MGS | - | - | - | - | x | - | - | +| USE_SGP30 | - | - | x | x | x | - | x | | USE_SI1145 | - | - | - | - | - | - | - | -| USE_LM75AD | - | - | - | x | x | x | x | +| USE_LM75AD | - | - | x | x | x | - | x | | USE_APDS9960 | - | - | - | - | - | - | - | | USE_MCP230xx | - | - | - | - | - | - | - | | USE_PCA9685 | - | - | - | - | - | - | - | @@ -175,129 +208,54 @@ Core version **2.5.2** binaries can be found at http://thehackbox.org/tasmota/re | USE_DS3231 | - | - | - | - | - | - | - | | USE_MGC3130 | - | - | - | - | - | - | - | | USE_MAX44009 | - | - | - | - | - | - | - | -| USE_SCD30 | - | - | - | - | - | x | - | +| USE_SCD30 | - | - | - | - | x | - | - | | USE_SPS30 | - | - | - | - | - | - | - | -| USE_ADE7953 | - | - | - | x | x | x | x | +| USE_ADE7953 | - | - | x | x | x | - | x | | USE_VL53L0X | - | - | - | - | - | - | - | | USE_MLX90614 | - | - | - | - | - | - | - | +| USE_CHIRP | - | - | - | - | - | - | - | +| USE_PAJ7620 | - | - | - | - | - | - | - | +| USE_PCF8574 | - | - | - | - | - | - | - | | | | | | | | | | -| Feature or Sensor | minimal | basic | classic | sonoff | knx | sensors | display | Remarks +| Feature or Sensor | minimal | basic | sonoff | knx | sensors | ir | display | Remarks | USE_SPI | - | - | - | - | - | - | x | -| USE_MHZ19 | - | - | - | x | x | x | x | -| USE_SENSEAIR | - | - | - | x | x | x | x | -| USE_PMS5003 | - | - | - | x | x | x | x | -| USE_NOVA_SDS | - | - | - | x | x | x | x | -| USE_ENERGY_SENSOR | - | x | x | x | x | x | - | -| USE_PZEM004T | - | - | - | x | x | x | - | -| USE_PZEM_AC | - | - | - | x | x | x | - | -| USE_PZEM_DC | - | - | - | x | x | x | - | -| USE_MCP39F501 | - | x | - | x | x | x | - | -| USE_SERIAL_BRIDGE | - | - | - | x | x | x | x | -| USE_SDM120 | - | - | - | - | - | x | - | -| USE_SDM630 | - | - | - | - | - | x | - | -| USE_MP3_PLAYER | - | - | - | - | - | x | - | -| USE_TUYA_DIMMER | - | x | - | x | x | x | x | -| USE_ARMTRONIX_DIMMERS | - | x | - | x | x | x | x | -| USE_PS_16_DZ | - | x | - | x | x | x | x | +| USE_MHZ19 | - | - | x | x | x | - | x | +| USE_SENSEAIR | - | - | x | x | x | - | x | +| USE_PMS5003 | - | - | x | x | x | - | x | +| USE_NOVA_SDS | - | - | x | x | x | - | x | +| USE_SERIAL_BRIDGE | - | - | x | x | x | - | x | +| USE_MP3_PLAYER | - | - | - | - | x | - | - | | USE_AZ7798 | - | - | - | - | - | - | - | -| USE_PN532_HSU | - | - | - | - | - | x | - | -| USE_IR_REMOTE | - | - | - | x | x | x | x | -| USE_IR_HVAC | - | - | - | - | - | x | - | -| USE_IR_RECEIVE | - | - | - | x | x | x | x | -| USE_WS2812 | - | - | x | x | x | x | x | -| USE_WS2812_DMA | - | - | - | - | - | - | - | -| USE_ARILUX_RF | - | - | - | x | x | x | - | -| USE_SR04 | - | - | - | x | x | x | x | -| USE_TM1638 | - | - | - | - | - | x | - | -| USE_HX711 | - | - | - | x | x | x | x | -| USE_RF_FLASH | - | - | - | x | x | x | - | -| USE_TX20_WIND_SENSOR | - | - | - | x | x | x | x | -| USE_RC_SWITCH | - | - | - | x | x | x | x | -| USE_RF_SENSOR | - | - | - | - | - | x | - | AlectoV2 only -| USE_SM16716 | - | x | x | x | x | x | x | -| USE_HRE | - | - | - | - | - | x | - | +| USE_PN532_HSU | - | - | - | - | x | - | - | +| USE_ZIGBEE | - | - | - | - | - | - | - | Experimental +| | | | | | | | | +| USE_IR_REMOTE | - | - | x | x | x | x | x | +| USE_IR_HVAC | - | - | - | - | x | x | - | +| USE_IR_RECEIVE | - | - | x | x | x | x | x | +| | | | | | | | | +| USE_SR04 | - | - | x | x | x | - | x | +| USE_TM1638 | - | - | - | - | x | - | - | +| USE_HX711 | - | - | x | x | x | - | x | +| USE_TX20_WIND_SENSOR | - | - | - | - | x | - | - | +| USE_RC_SWITCH | - | - | - | - | x | - | - | +| USE_RF_SENSOR | - | - | - | - | x | - | - | AlectoV2 only +| USE_HRE | - | - | - | - | x | - | - | +| USE_A4988_STEPPER | - | - | - | - | - | - | - | +| USE_ARDUINO_SLAVE | - | - | - | - | - | - | - | Experimental +| | | | | | | | | +| Feature or Sensor | minimal | basic | sonoff | knx | sensors | ir | display | Remarks | USE_DISPLAY | - | - | - | - | - | - | x | | USE_DISPLAY_LCD | - | - | - | - | - | - | x | | USE_DISPLAY_SSD1306 | - | - | - | - | - | - | x | | USE_DISPLAY_MATRIX | - | - | - | - | - | - | x | +| USE_DISPLAY_SH1106 | - | - | - | - | - | - | x | | USE_DISPLAY_ILI9341 | - | - | - | - | - | - | x | | USE_DISPLAY_EPAPER_29 | - | - | - | - | - | - | x | Disabled for core 2.3.0 +| USE_DISPLAY_EPAPER_42 | - | - | - | - | - | - | x | Disabled for core 2.3.0 +| USE_DISPLAY_ILI9488 | - | - | - | - | - | - | - | +| USE_DISPLAY_SSD1351 | - | - | - | - | - | - | - | +| USE_DISPLAY_RA8876 | - | - | - | - | - | - | - | ## Changelog -Version 6.6.0 20190707 - * Remove support of TLS on core 2.3.0 and extent support on core 2.4.2 and up - * Remove MQTT uptime message every hour - * Refactor some defines to const - * Refactor webserver HTML input, button, textarea, and select name based on id - * Refactor webserver sensor data collection - * Refactor TLS based on BearSSL, warning breaking change for fingerprints validation - * Refactor management of lights, using classes and integers instead of floats - * Refactor UDP initial message handling from string to char using static memory and add debug info (#5505) - * Refactor ``IRsend`` and receive for 64-bit support (#5523) - * Refactor MQTT which might solve issue (#5755) - * Refactor ``IRSend`` by using heap when more than 199 values need to be send. May need increase of define MQTT_MAX_PACKET_SIZE too (#5950) - * Refactor double to float in rules, and replaced trigonometric functions from stdlib with smaller versions (#6005) - * Change pubsubclient MQTT_KEEPALIVE from 10 to 30 seconds for AWS IoT support - * Change gamma correction as default behavior, ie "Ledtable 1" - * Change PWM resolution from 8 to 10 bits for low brightness lights - * Change ``IRSend`` Panasonic protocol to 64-bit (#5523) - * Change ADC0 to enabled by default in my_user_config.h (#5671) - * Change define USE_EMULATION by USE_EMULATION_HUE and USE_EMULATION_WEMO (#5826) - * Change default ``PowerDelta`` from 80% to 0% on new installations (#5858, #5028, #4813, #4130, #4145, #3795, #3778, #3660, #3648) - * Fix display Bug in KNX webmenu for Physical Address - * Fix the Unescape() function and the ``SendSerial3`` behaviour - * Fix webserver multiple Javascript window.onload functionality - * Fix TasmotaSerial at 9600 bps solving DFPlayer comms (#5528) - * Fix Configure Timer Web GUI (#5568) - * Fix Shelly 2.5 I2C address priority issue when VEML6070 code is present by disabling VEML6070 for Shelly 2.5 (#5592) - * Fix use of ``SerialDelimiter`` value 128 (#5634) - * Fix Sonoff Pow R2 / S31 invalid energy increments (#5789) - * Fix core 2.5.x ISR not in IRAM exception (#5837) - * Fix Philips Hue emulation Alexa issue by using part of MAC address for LightId (#5849) - * Fix missing white channel for WS2812 (#5869) - * Fix PZem startup issue (#5875) - * Fix exception 9 when syslog is enabled and NTP is just synced (#5917) - * Fix Toggle functionality to button double press when one button and two devices are detected (#5935) - * Fix command ``Channel`` for dual dimmers (#5940) - * Fix not restoring white value on power off/power on (#5993) - * Add command ``AdcParam`` to control ADC0 Temperature and Light formula parameters - * Add command ``LedMask`` to assign which relay has access to power LED (#5602, #5612) - * Add extended LED power control using command ``LedPowerX`` where X is 1 to 4. Enabled when "LedLink(i)" is configured too (#5709) - * Add command ``Sensor20 1..255`` to change Nova Fitness SDS01 working period in minutes (#5452) - * Add command ``SetOption38 6..255`` to set IRReceive protocol detection sensitivity mimizing UNKNOWN protocols (#5853) - * Add command ``SetOption39 1..255`` to control CSE7766 (Pow R2) or HLW8032 (Blitzwolf SHP5) handling of power loads below 6W. Default setting is 128 (#5756) - * Add command ``SetOption40 0..250`` to disable button functionality if activated for over 0.1 second. Needs SetOption1 1 and SetOption13 0 (#5449) - * Add command ``SetOption63 0/1`` to disable relay state feedback scan at restart (#5594, #5663) - * Add command ``SetOption64 0/1`` to switch between "-" or "_" as sensor index separator impacting DS18X20, DHT, BMP and SHT3X sensor names (#5689) - * Add command ``SetOption65 0/1`` and more Tuya Serial based device support (#5815) - * Add command ``WebColor`` to change GUI colors on the fly - * Add support for AWS IoT with TLS 1.2 on core 2.4.2 and up. Full doc here: https://github.com/arendst/Sonoff-Tasmota/wiki/AWS-IoT - * Add support for Badger HR-E Water Meter (#5539) - * Add support for Shelly 2.5 Energy and overtemp Monitoring (#5592) - * Add support for color and colortone for Philips Hue emulation via Alexa (#5600 #4809) - * Add support for Scripts as replacement for Rules. Default disabled but can be enabled in my_user_config.h (#5689) - * Add support for up to four LEDs related to four power outputs. Enabled when "LedLink(i)" is configured too (#5709) - * Add support for Shelly 1PM Template ``{"NAME":"Shelly 1PM","GPIO":[56,0,0,0,82,134,0,0,0,0,0,21,0],"FLAG":2,"BASE":18}`` (#5716) - * Add support for SPS30 Particle sensor thanks to Gerhard Mutz (#5830) - * Add support for VL53L0x time of flight sensor. Might interfere with TSL2561 using same I2C address (#5845) - * Add support for Sonoff L1 thanks to reef-actor (#6002) - * Add rule Http#Initialized - * Add rule System#Save executed just before a planned restart - * Add rule support for single JSON value pair like {"SSerialReceived":"on"} by expanding it to {"SSerialReceived":{"Data":"on"}} allowing for trigger SSerialReceived#Data=on (#5638) - * Add define USE_COUNTER to my_user_config.h to save space in sonoff-basic.bin and sonoff-minimal.bin - * Add define USE_DHT to my_user_config.h to save space in sonoff-basic.bin - * Add defines USE_EMULATION_WEMO and USE_EMULATION_HUE to my_user_config.h to control emulation features at compile time (#5826) - * Add Toggle functionality to button double press when more devices are detected - * Add device OverTemp (>73 Celsius) detection to Energy Monitoring devices with temperature sensor powering off all outputs - * Add Tuya Dimmer 10 second heartbeat serial packet required by some Tuya dimmer secondary MCUs - * Add all temperature, humidity and pressure for global access - * Add validation check when loading settings from flash - * Add HX711 weight restore after controlled restart or after power restore just before executing command Sensor34 7 (#5367, #5786) - * Add GUI hexadecimal color options in my_user_config.h (#5586) - * Add alternative ``IRSend`` command syntax ``IRSend raw,,
,
,,,,`` (#5610) - * Add user configurable ADC0 to Module and Template configuration compatible with current FLAG options (#5671) - * Add AriLux RF control GPIO option "ALux IrSel" (159) replacing "Led4i" (59) for full LED control (#5709) - * Add LED GPIO option "LedLink" (157) and "LedLinki" (158) to select dedicated link status LED (#5709) - * Add all 5 PWM channels individually adressable with LEDs. (#5741) - * Add reset of Energy values when connection to sensor is lost for over 4 seconds (#5874, #5881) - * Add checkbox to GUI password field enabling visibility during password entry only (#5934) \ No newline at end of file +Version 6.7.0 20191101 + * TBS diff --git a/SUPPORT.md b/SUPPORT.md index 26c770b80..389c03002 100644 --- a/SUPPORT.md +++ b/SUPPORT.md @@ -22,4 +22,4 @@ If you're looking for support on **Sonoff-Tasmota** there are some options avail * [Bug Report](https://github.com/arendst/Sonoff-Tasmota/issues/new?template=Bug_report.md): For reporting Bugs of Tasmota Software. * [Feature Request](https://github.com/arendst/Sonoff-Tasmota/issues/new?template=Feature_request.md): For requesting features/functions to Tasmota Software. * [Troubleshooting](https://github.com/arendst/Sonoff-Tasmota/issues/new?template=Custom.md): As a last resort, you can open new *Troubleshooting* issue on GitHub if the solution could not be found using the other channels. Just remember: the more info you provide the more chances you'll have to get an accurate answer. -* [Issue a question](https://github.com/arendst/Sonoff-Tasmota/issues/new/choose): As a last resort, you can open new *Question* issue on GitHub if the answer could not be found using the other channels. Just remember: the more info you provide the more chances you'll have to get an accurate answer. +* [Issue a question](https://github.com/arendst/Sonoff-Tasmota/issues/new/choose): As a last resort, you can open a new *Question* issue on GitHub if the answer could not be found using the other channels. Just remember: the more info you provide the more chances you'll have to get an accurate answer. diff --git a/TEMPLATE.md b/TEMPLATE.md deleted file mode 100644 index 33465a0e9..000000000 --- a/TEMPLATE.md +++ /dev/null @@ -1,89 +0,0 @@ -Logo - -# Template information - -Sonoff-Tasmota uses Device or Module information to control peripherals connected to GPIOs. This information is stored in the ``sonoff_template.h`` file as a device specific template. The template contains information about what GPIO should be connected to what peripheral and what GPIO may be configured online using the ``GPIO`` command or GUI Configure Module menu. In addition a device may need specific coding to process the data from these peripherals. The module number as provided by the ``Modules`` command is used to select this coding. - -Starting with version 6.4.1.16 Sonoff-Tasmota Modules can be extended by users online using a template. To provide easy processing by Sonoff-Tasmota a user template is written as JSON text and could look like this: - -{"NAME":"UserModule1","GPIO":[17,148,29,149,7,255,255,255,138,255,139,255,255],"FLAG":0,"BASE":18} - -The four properties with UPPERCASE property names have the following functionality: - -Property name | Property value description ---------------|------------------------------------------------------------------------------------------------------------------- -NAME | Up to 14 characters for the Module name -GPIO | Up to 13 decimal numbers from 0 to 255 representing GPIO0 to GPIO5, GPIO09, GPIO10 and GPIO12 to GPIO16 -FLAG | 8 bit mask flag register -BASE | Module number of a hard-coded device to be used when device specific functionality is needed - -The above example, based on the Generic Module does not allow ADC0 input. - -## GPIO functionality -The GPIO functionality numbers are the same is shown by command ``GPIOs``. In addition code 255 is added to select a GPIO as user configurable via the GUI Configure Module menu. - -## FLAG functionality -The FLAG value is an 8-bit mask where each bit controls a features. Add FLAG values to set multiple bits. - -FLAG | Mask | Feature description ------|----------|------------------------------ - 1 | xxxxxxx1 | Allowing to use Analog0 (ADC0) as input if define USE_ADC_VCC in ``my_user_config.h`` is disabled - 2 | xxxxxx1x | Enable GUI pull-up control message - 4 | xxxxx1xx | Not used - 8 | xxxx1xxx | Not used - 16 | xxx1xxxx | Not used - 32 | xx1xxxxx | Not used - 64 | x1xxxxxx | Not used - 128 | 1xxxxxxx | Not used - -## BASE functionality -The following table lists hard-coded device specific functionality. Notice that not all device modules need special handling. - -BASE | Module | Description ------|----------------|---------------------------------------------- - 4 | Sonoff Dual | Process relay and button via hardware serial interface using GPIO01 and GPIO03. Change baudrate to 19200 bps. Process buttons as single press only - 9 | Sonoff Touch | Invert ledstate 1 functionality - 10 | Sonoff LED | Set light type to 2 PWM channels disregarding SetOption15. Fix device specific LED instabilities by disabling GPIO04, GPIO5 and GPIO14 - 12 | 4 Channel | See 4 - 13 | Motor C/AC | Force all relays ON at Power On and disable command ``PowerOnState`` - 15 | EXS Relay(s) | Enable pulse latching using even/odd numbered relay pairs - 18 | Generic | Show Wemos specific pin information in GUI - 19 | H801 | Change hardware UART Tx from GPIO01 to GPIO02 - 20 | Sonoff SC | Enable and Process data via hardware serial interface using GPIO01 and GPIO03. Change baudrate to 19200 bps - 21 | Sonoff BN-SZ | Set light type to 1 PWM channel disregarding SetOption15 - 22 | Sonoff 4CH Pro | Button handling disregarding SetOption13 only allowing single press to enable RF learning while holding the button - 24 | Sonoff Bridge | Enable and Process data via hardware serial interface using GPIO01 and GPIO03. Change baudrate to 19200 bps. Process 16 buttons in web GUI. Enable EFM8BB1 firmware upload - 25 | Sonoff B1 | Set light type to RGBWC using MY92x1 - 26 | AiLight | Set light type to RGBW using MY92x1 - 27 | Sonoff T1 1CH | See 9 - 28 | Sonoff T1 2CH | See 9 - 29 | Sonoff T1 3CH | See 9 - 38 | Sonoff Dual R2 | Process buttons as single press only - 43 | Sonoff iFan02 | Enable command ``Fanspeed``. Disable Interlock and PulseTime. Tune status information, MQTT data and GUI. Sync with microcontroller. Process Domoticz Fan state - 47 | Xiaomi Philips | Process Color Temperature using PWM2 and Intensity using PWM1 - 53 | Tuya Dimmer | Enable and Process data via software or hardware serial interface using GPIO 148 and 149 or forced GPIO01 and GPIO03. Change baudrate to 9600 bps. Process all Buttons - 55 | ARMTR Dimmer | Enable and Process data via software or hardware serial interface using GPIO 148 and 149. Change baudrate to 115200 bps. - 57 | PS-16-DZ | Enable and Process data via software or hardware serial interface using GPIO 148 and 149. Change baudrate to 19200 bps. - 61 | YTF IR Bridge | Disable serial interface to stop loopback - 65 | Mi Desk Lamp | Process rotary and Button1 data specific to this device - -## Usage -A user provided template can be stored in Sonoff-Tasmota using the ``Template`` command. It has the following options. - -Command | Payload | Description ----------|----------|--------------------------------------- -Template | | Show current user template -Template | 0 | Copy active module template to user template -Template | 1 .. 69 | Copy hard-coded module template to user template - -The following command will store a complete template based on the Generic module -``Template {"NAME":"UserModule1","GPIO":[17,148,29,149,7,255,255,255,138,255,139,255,255],"FLAG":0,"BASE":18}`` - -The following command will update the name of a stored template -``Template {"NAME":"UserModule2"}`` - -The following command will update the flag of a stored template -``Template {"FLAG":1}`` - -The following command will update the base of a stored template to Generic -``Template {"BASE":0}`` diff --git a/TEMPLATES.md b/TEMPLATES.md new file mode 100644 index 000000000..2cb5b4773 --- /dev/null +++ b/TEMPLATES.md @@ -0,0 +1,613 @@ +Logo + +# Templates + +Find below the available templates as of October 22nd, 2019. More template information can be found in the [Tasmota Device Templates Repository](http://blakadder.github.io/templates) + +## Bulb +``` +A19 RGBW 8W E26 {"NAME":"OOOLED 60W RGB","GPIO":[255,255,255,255,39,40,255,255,37,255,38,255,255],"FLAG":1,"BASE":18} +Aisirer 9W A19 E26 {"NAME":"AISIRER E26","GPIO":[0,0,0,0,0,37,0,0,0,0,0,0,0],"FLAG":0,"BASE":18} +Anoopsyche CW/WW 60W {"NAME":"Anoop-CW-WW","GPIO":[0,0,0,0,0,37,0,0,0,38,0,0,0],"FLAG":0,"BASE":18} +Aoycocr A19 {"NAME":"AoycocrA19","GPIO":[0,0,0,0,0,39,0,0,38,0,37,40,0],"FLAG":0,"BASE":18} +Arlec GLD112HA {"NAME":"Arlec CCT","GPIO":[0,0,0,0,0,37,0,0,0,38,0,0,0],"FLAG":0,"BASE":48} +Arlec GLD122HA {"NAME":"Arlec RGBWW","GPIO":[255,255,255,255,37,40,255,255,38,41,39,255,255],"FLAG":0,"BASE":18} +Ausein E14 7W White {"NAME":"Tuya RGBW","GPIO":[255,255,255,255,40,255,255,255,38,39,37,255,255],"FLAG":0,"BASE":18} +Avatar 5W E14 {"NAME":"AVATAR","GPIO":[0,0,0,0,0,0,0,0,0,143,0,144,0],"FLAG":0,"BASE":27} +BNeta A60 E27 {"NAME":"OM60/RGBW","GPIO":[255,255,255,255,140,37,0,0,38,142,141,255,255],"FLAG":0,"BASE":18} +BNeta GU10 RGB {"NAME":"BNeta","GPIO":[255,255,255,255,140,37,0,0,38,142,141,255,255],"FLAG":0,"BASE":18} +Bomcosy 7W E27 RGB CW {"NAME":"Generic","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":1,"BASE":18} +Brilliant 20741 {"NAME":"Brilliant RGB+","GPIO":[255,255,255,255,37,40,255,255,38,0,39,0,0],"FLAG":0,"BASE":18} +Brilliant HK17653S72 {"NAME":"Brilliant E14 ","GPIO":[255,255,255,255,37,40,255,255,38,255,39,255,255],"FLAG":0,"BASE":18} +Candle 6W E14/E27 RGBWW {"NAME":"E14_RGBWW_6W","GPIO":[0,0,0,0,40,41,0,0,38,39,37,0,0],"FLAG":0,"BASE":20} +DGM L-WT9W1 {"NAME":"DGM L-WT9W1","GPIO":[0,0,0,0,140,142,0,0,38,37,141,0,0],"FLAG":0,"BASE":18} +electriQ RGBW B22 {"NAME":"ElectricQ B22","GPIO":[255,255,255,255,37,41,255,255,38,40,39,255,255],"FLAG":0,"BASE":18} +EleLight 7W A19 E27 RGBW {"NAME":"EleLight 7wA19","GPIO":[255,255,255,255,140,37,255,255,255,142,141,255,255],"FLAG":0,"BASE":18} +Esicoo 9W E26 {"NAME":"Esicoo Bulb","GPIO":[0,0,0,0,143,144,0,0,0,0,0,0,0],"FLAG":0,"BASE":27} +Fcmila 7W RGBW E27 {"NAME":"FCMILA 7W","GPIO":[255,255,255,255,39,40,255,255,37,255,38,255,255],"FLAG":0,"BASE":18} +Fcmila EQ723 {"NAME":"FCMILA LED E27","GPIO":[255,255,255,255,40,255,255,255,38,39,37,255,255],"FLAG":0,"BASE":18} +Fcmila FC-12W {"NAME":"SYF05","GPIO":[255,0,255,0,140,37,0,0,255,255,141,0,255],"FLAG":1,"BASE":69} +Feit Electric BPA800/RGBW/AG/2 {"NAME":" BPA800/RGBW","GPIO":[255,255,255,255,37,38,255,255,141,142,140,255,255],"FLAG":0,"BASE":48} +Feit Electric BR30/927CA/AG {"NAME":"Feit BR30 WW","GPIO":[0,0,0,0,0,37,0,0,0,0,0,0,0],"FLAG":0,"BASE":18} +Feit Electric BR30/950CA/AG {"NAME":"Feit BR30 CW","GPIO":[0,0,0,0,0,37,0,0,0,0,0,0,0],"FLAG":0,"BASE":18} +Feit Electric OM60/927CA/AG {"NAME":" Feit P_A800_2","GPIO":[0,0,0,0,0,37,0,0,0,0,0,0,0],"FLAG":0,"BASE":18} +Feit Electric OM60/RGBW/CA/AG {"NAME":"OM60/RGBW","GPIO":[255,255,255,255,140,37,0,0,38,142,141,255,255],"FLAG":0,"BASE":18} +Geeni Lux A21 White {"NAME":"Geeni-1050-WW","GPIO":[0,0,0,0,37,37,0,0,38,0,0,0,0],"FLAG":1,"BASE":18} +Generic 10W E26/E27 RGBW {"NAME":"10W E27 RGBW","GPIO":[0,0,0,0,0,39,0,0,38,0,37,40,0],"FLAG":0,"BASE":18} +Globe CW/WW {"NAME":"Globe CW/WW","GPIO":[0,0,0,0,38,37,0,0,0,0,0,0,0],"FLAG":0,"BASE":18} +Globe RGBW {"NAME":"GlobeRGBWW","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} +Gosund E27 8W RGBW {"NAME":"Gosund RGB+W","GPIO":[255,255,255,255,40,255,255,255,37,38,39,255,255],"FLAG":0,"BASE":18} +Jomarto 9W B22 {"NAME":"Jomarto Wifi S","GPIO":[0,0,0,0,40,0,0,0,38,39,37,0,0],"FLAG":1,"BASE":18} +Kainsy 7W E27 RGBW {"NAME":"KAINSY","GPIO":[17,255,255,255,143,144,0,0,0,0,0,0,0],"FLAG":0,"BASE":27} +Kogan SmarterHome™ White 10W B22 {"NAME":"Kogan White/Wa","GPIO":[0,0,0,0,37,40,0,0,41,38,39,0,0],"FLAG":0,"BASE":18} +Kohree A19 7W {"NAME":"Kohree VHP560","GPIO":[255,255,255,255,37,41,255,255,38,40,39,255,255],"FLAG":0,"BASE":18} +LE A19 RGBCT 9W E26 {"NAME":"LE RGBWW 60W","GPIO":[255,0,255,0,140,37,0,0,38,142,141,0,255],"FLAG":1,"BASE":18} +Legelite E26 {"NAME":"Legelite E26","GPIO":[255,255,255,255,37,40,255,255,38,41,39,255,255],"FLAG":0,"BASE":18} +Linganzh LWE3 6W RGBW {"NAME":"Linganzh LWE3 ","GPIO":[255,255,255,255,255,38,255,255,39,255,40,37,255],"FLAG":0,"BASE":18} +Lohas 5W E12 {"NAME":"LH-5W-ZN01204","GPIO":[0,0,0,0,0,0,0,0,0,143,0,144,0],"FLAG":0,"BASE":27} +Lohas 9W E26 RGBW {"NAME":"Lohas RGBW","GPIO":[0,0,0,0,0,0,0,0,0,143,0,144,0],"FLAG":0,"BASE":18} +Lohas A19 RGBW E12 {"NAME":"Lohas","GPIO":[17,255,255,255,0,0,0,0,0,143,0,144,0],"FLAG":1,"BASE":26} +Lohas E14 R50 {"NAME":"Lohas E14 R50","GPIO":[17,255,255,255,0,0,0,0,0,143,0,144,0],"FLAG":0,"BASE":27} +Lohas ZN031 {"NAME":"Lohas ZN031","GPIO":[255,255,255,255,38,37,255,255,41,39,40,255,255],"FLAG":0,"BASE":18} +LSC Smart Connect Multicolor Spot GU10 {"NAME":"LSC RGBCW LED","GPIO":[0,0,0,0,0,0,0,0,181,0,180,0,0],"FLAG":0,"BASE":18} +LSC Smart Connect Smart Filament LED {"NAME":"LSC-Filam-Big","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":18} +Lumiman E27 Multicolor {"NAME":"Lumiman RGB","GPIO":[255,255,255,255,37,40,255,255,38,41,39,52,255],"FLAG":1,"BASE":18} +Lumiman LM530 E26 {"NAME":"Lumiman LM530","GPIO":[255,255,255,255,37,40,255,255,38,41,39,255,255],"FLAG":1,"BASE":18} +Lumiman LM650 {"NAME":"Legelite E26","GPIO":[255,255,255,255,37,40,255,255,38,41,39,255,255],"FLAG":0,"BASE":18} +Luminea ZX-2831 E27 CCT {"NAME":"Luminea CCT","GPIO":[0,0,0,0,140,37,0,0,38,142,141,0,0],"FLAG":0,"BASE":18} +Luminea ZX-2832 E27 RGBW {"NAME":"Luminea RGBW","GPIO":[255,255,255,255,140,37,255,255,38,142,141,255,255],"FLAG":1,"BASE":18} +Lyasi PT-BW09 {"NAME":"Lyasi PT-BW09","GPIO":[17,255,255,255,143,144,0,0,0,0,0,0,0],"FLAG":0,"BASE":27} +Maxcio YX-L01P-E7 {"NAME":"Maxcio YXL01P","GPIO":[17,255,255,255,143,144,0,0,0,0,0,0,0],"FLAG":0,"BASE":27} +Merkury A19 60W {"NAME":"MI-BW902-999W","GPIO":[0,0,0,0,0,37,0,0,0,0,0,0,0],"FLAG":0,"BASE":18} +Merkury A21 75W {"NAME":"MI-BW904-999W","GPIO":[0,0,0,0,140,37,0,0,0,142,141,0,0],"FLAG":1,"BASE":69} +Merkury BR30 65W {"NAME":"MI-BW905-999W","GPIO":[0,0,0,0,37,37,0,0,38,0,0,0,0],"FLAG":0,"BASE":18} +Mirabella Genio CW {"NAME":"GenioBulbCW","GPIO":[0,0,0,0,38,37,0,0,0,40,39,0,0],"FLAG":0,"BASE":18} +Mirabella Genio Dimmable {"NAME":"GenioBulbCCT","GPIO":[0,0,0,0,0,37,0,0,0,38,0,0,0],"FLAG":0,"BASE":18} +Mirabella Genio RGBW {"NAME":"GenioBulbRGB","GPIO":[255,255,255,255,37,40,255,255,38,255,39,255,255],"FLAG":1,"BASE":18} +Mixigoo 10W E27 RGBW {"NAME":"Mixigoo Bulb","GPIO":[255,255,255,255,41,38,255,255,39,255,37,40,255],"FLAG":0,"BASE":18} +MOKO YX-L01C-E27 {"NAME":"MOKO","GPIO":[17,255,255,255,143,144,0,0,0,0,0,0,0],"FLAG":0,"BASE":27} +Nedis WIFILC10WTE27 {"NAME":"Nedis RGBW","GPIO":[0,0,0,0,140,37,0,0,0,142,141,0,0],"FLAG":1,"BASE":18} +Nedis WIFILW10WTE27 {"NAME":"WIFILW10WTE27","GPIO":[0,0,0,0,0,37,0,0,38,0,0,0,0],"FLAG":0,"BASE":18} +NiteBird TT-WB4 {"NAME":"NiteBird TT-WB","GPIO":[255,255,255,255,40,255,255,255,37,38,39,255,255],"FLAG":0,"BASE":18} +Novostella 12W RGBCT E27 {"NAME":"Novostella","GPIO":[255,255,255,255,37,40,255,255,38,41,39,255,255],"FLAG":0,"BASE":18} +Novostella B22 7W {"NAME":"Novostella B22","GPIO":[255,255,255,255,37,41,255,255,38,40,39,255,255],"FLAG":0,"BASE":18} +Oobest RGBW E27 {"NAME":"WifiBulb","GPIO":[0,0,0,0,0,37,0,0,39,40,38,0,0],"FLAG":1,"BASE":18} +Oobest ZN93028 11W E27 RGB {"NAME":"RGB Bulb 11W","GPIO":[255,255,255,255,40,255,255,255,38,39,37,255,255],"FLAG":1,"BASE":18} +Reafoo 9W A26 {"NAME":"ReaFooE26","GPIO":[0,0,0,0,41,38,0,0,39,0,40,37,0],"FLAG":0,"BASE":18} +RGBW 7W B22 {"NAME":"SM16716","GPIO":[255,255,255,255,140,37,255,255,255,255,141,255,255],"FLAG":0,"BASE":18} +RGBW 7W E14 {"NAME":"7W-E14-RGBW-La","GPIO":[255,255,255,255,38,37,255,255,39,255,40,255,255],"FLAG":0,"BASE":18} +Solimo 12W B22 RGBWW {"NAME":"Solimo RGBWW12","GPIO":[255,255,255,255,37,40,255,255,38,41,39,255,255],"FLAG":0,"BASE":18} +Solimo 9W B22 RGBWW {"NAME":"Solimo RGBWW 9","GPIO":[255,255,255,255,37,40,255,255,38,41,39,255,255],"FLAG":0,"BASE":18} +Sonoff B1 (R2) {"NAME":"Sonoff B1","GPIO":[17,255,255,255,0,0,0,0,143,0,144,0,0],"FLAG":0,"BASE":26} +TCP B22 RGBW {"NAME":"TCP Smart RGBW","GPIO":[255,255,255,255,140,37,0,0,38,142,141,255,255],"FLAG":0,"BASE":18} +Teckin SB50 {"NAME":"Teckin SB50","GPIO":[255,255,255,255,40,255,255,255,38,39,37,255,255],"FLAG":0,"BASE":18} +Teckin SB50 v2 {"NAME":"Teckin SB50","GPIO":[255,255,255,255,37,255,255,255,38,40,39,255,255],"FLAG":0,"BASE":18} +Teckin SB50 V3 {"NAME":"Teckin SB50v3","GPIO":[255,255,255,255,37,40,255,255,38,41,39,255,255],"FLAG":0,"BASE":18} +Teckin SB51 {"NAME":"Teckin SB51","GPIO":[255,255,255,255,40,255,255,255,38,39,37,255,255],"FLAG":0,"BASE":18} +Teckin SB53 {"NAME":"Teckin SB53","GPIO":[255,255,255,255,37,40,255,255,38,41,39,255,255],"FLAG":0,"BASE":18} +TikLOk 7.5W A19 E26 {"NAME":"TikLOk WW-CW-L","GPIO":[0,0,0,0,0,37,0,0,38,0,0,0,0],"FLAG":0,"BASE":18} +Wipro Garnet B22 9W {"NAME":"WiproSmartBulb","GPIO":[255,255,255,255,37,41,255,255,38,40,39,255,255],"FLAG":0,"BASE":18} +Woopower RGBW E14 5W {"NAME":"Woopower E14","GPIO":[0,0,0,0,0,0,0,0,0,143,0,144,0],"FLAG":0,"BASE":27} +Xiaomi Phillips E14 {"NAME":"Xiaomi Philips","GPIO":[0,0,0,0,0,0,0,0,38,0,0,37,0],"FLAG":0,"BASE":48} +Xiaomi Phillips E27 {"NAME":"Xiaomi Philips","GPIO":[0,0,0,0,0,0,0,0,38,0,0,37,0],"FLAG":0,"BASE":48} +XMRJ {"NAME":"XMRJ","GPIO":[0,0,0,0,0,0,0,0,0,143,0,144,0],"FLAG":0,"BASE":27} +ZZHXON 6W E14/E27 {"NAME":"E27_RGB_Bulb","GPIO":[0,0,0,0,40,41,0,0,38,39,37,0,0],"FLAG":0,"BASE":18} +``` + +## Bulb Socket +``` +Elegant Choice E27/E26 {"NAME":"Sm Lghtg Base","GPIO":[0,0,0,0,56,0,0,0,21,17,0,0,0],"FLAG":0,"BASE":18} +Slampher {"NAME":"Slampher","GPIO":[17,255,0,255,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":9} +SmartBase E0260 {"NAME":"SmartBaseE0260","GPIO":[0,0,0,0,56,0,0,0,21,17,0,0,0],"FLAG":0,"BASE":18} +``` + +## Curtain Switch +``` +Teepao {"NAME":"Taopao","GPIO":[255,255,255,18,22,19,255,255,255,21,255,255,17],"FLAG":1,"BASE":18} +WF-CS01 {"NAME":"Tuya Shutter","GPIO":[157,0,54,10,22,19,0,0,17,21,53,23,52],"FLAG":0,"BASE":18} +``` + +## DIY Project +``` +LC-Tech 1Ch and PZEM-004T {"NAME":"HW-655 PZEM","GPIO":[0,63,0,62,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":18} +Luani HVIO {"NAME":"Luani HVIO","GPIO":[0,255,255,255,21,22,0,0,9,10,255,52,0],"FLAG":1,"BASE":35} +SUPLA inCan by Espablo {"NAME":"Supla Espablo","GPIO":[0,255,4,255,17,21,0,0,255,22,255,0,52],"FLAG":1,"BASE":31} +``` + +## Development Board +``` +Witty Cloud {"NAME":"Witty Cloud","GPIO":[255,255,56,255,17,255,0,0,38,39,255,37,255],"FLAG":1,"BASE":32} +``` + +## Dimmer +``` +Armtronix AC Dimmer One Triac Board {"NAME":"ARMTR Dimmer","GPIO":[255,148,255,149,255,255,0,0,255,255,255,255,255],"FLAG":0,"BASE":56} +Armtronix AC Dimmer Two Triac Board {"NAME":"ARMTR Dimmer","GPIO":[255,148,255,149,255,255,0,0,255,255,255,255,255],"FLAG":0,"BASE":56} +CE Smart Home WF500D {"NAME":"CE-WF500D","GPIO":[255,255,255,255,255,255,0,0,255,108,255,107,255],"FLAG":0,"BASE":54} +EVA LOGIK WF31 {"NAME":"WF31 Dimmer","GPIO":[255,107,255,108,255,255,0,0,255,255,255,255,255],"FLAG":0,"BASE":54} +EX-Store 2 Kanal RS232 V4 {"NAME":"EXS Dimmer","GPIO":[0,148,0,149,255,255,0,0,255,183,255,0,0],"FLAG":0,"BASE":72} +Kingart Touch {"NAME":"PS-16-DZ","GPIO":[255,148,255,149,255,255,0,0,255,52,255,255,255],"FLAG":0,"BASE":58} +Moes DS01 {"NAME":"MOES - DS01","GPIO":[255,255,255,255,255,255,0,0,255,108,255,107,255],"FLAG":0,"BASE":54} +Moes QS-WiFi-D01 Dimmer 150W {"NAME":"WiFi-Dimmer","GPIO":[0,148,0,149,0,0,0,0,0,42,37,0,0],"FLAG":0,"BASE":18} +PS-16-DZ {"NAME":"PS-16-DZ","GPIO":[255,148,255,149,255,255,0,0,255,52,255,255,255],"FLAG":0,"BASE":58} +Zemismart KS-7011 {"NAME":"KS-7011 Dimmer","GPIO":[255,107,255,108,255,255,0,0,255,255,255,255,255],"FLAG":0,"BASE":54} +``` + +## Environment Monitor +``` +Sonoff SC {"NAME":"Sonoff SC","GPIO":[17,148,255,149,0,0,0,0,0,56,0,0,0],"FLAG":0,"BASE":21} +``` + +## Fan Controller +``` +Sonoff iFan02 {"NAME":"Sonoff iFan02","GPIO":[17,255,0,255,23,22,18,19,21,56,20,24,0],"FLAG":0,"BASE":44} +Sonoff iFan03 {"NAME":"SonoffiFan03","GPIO":[17,148,0,149,0,0,29,161,23,56,22,24,0],"FLAG":0,"BASE":71} +``` + +## Humidifier +``` +Asakuki 500ml {"NAME":"Oil Diffuser","GPIO":[255,255,255,255,255,255,255,255,255,255,255,21,22],"FLAG":0,"BASE":18} +Maxcio 400ml {"NAME":"MaxcioDiffuser","GPIO":[255,255,255,255,255,255,0,0,255,255,255,255,255],"FLAG":0,"BASE":54} +Oil Diffuser 550ML {"NAME":"Oil Diffuser","GPIO":[255,255,255,255,255,255,255,255,255,255,255,21,22],"FLAG":0,"BASE":18} +``` + +## IR Bridge +``` +Alfawise KS1 {"NAME":"KS1","GPIO":[255,71,17,72,17,51,0,0,56,0,8,0,0],"FLAG":1,"BASE":62} +auvisio S06 {"NAME":"NX-4519-675","GPIO":[255,255,255,255,52,51,255,255,255,255,8,255,255],"FLAG":1,"BASE":18} +Eachen SANT-IR 01 {"NAME":"Eachen IR","GPIO":[0,0,0,0,56,51,255,255,255,17,8,0,0],"FLAG":0,"BASE":18} +Geeklink {"NAME":"GL IR Blaster","GPIO":[255,255,255,255,56,51,0,0,0,17,8,0,0],"FLAG":0,"BASE":62} +Mirabella Genio HKWL-IR02W {"NAME":"Genio IR TxRx","GPIO":[255,255,255,255,56,51,0,0,0,17,8,0,0],"FLAG":0,"BASE":62} +STITCH by Monoprice 35753 {"NAME":"Stitch 35753","GPIO":[0,0,0,0,52,51,0,0,0,90,8,0,0],"FLAG":0,"BASE":18} +YTF {"NAME":"YTF IR Bridge","GPIO":[255,255,255,255,56,51,0,0,0,17,8,0,0],"FLAG":0,"BASE":62} +``` + +## LED Controller +``` +Arilux AL-LC01 {"NAME":"Arilux LC01","GPIO":[17,255,59,255,147,37,0,0,38,39,255,0,0],"FLAG":0,"BASE":37} +Arilux AL-LC06 {"NAME":"Arilux LC06","GPIO":[17,255,255,255,255,255,255,255,38,39,37,41,40],"FLAG":0,"BASE":18} +Arilux AL-LC11 {"NAME":"Arilux LC11","GPIO":[17,255,59,255,38,37,0,0,41,40,39,147,0],"FLAG":0,"BASE":38} +Electrodragon Mosfet Drive {"NAME":"LEDBoard RGBW","GPIO":[255,0,0,0,255,255,0,0,39,38,40,37,52],"FLAG":0,"BASE":18} +H801 {"NAME":"H801","GPIO":[255,52,255,255,41,57,0,0,39,38,40,37,0],"FLAG":0,"BASE":20} +LEDEnet {"NAME":"LEDEnet","GPIO":[0,255,56,255,147,0,0,0,38,39,37,40,0],"FLAG":0,"BASE":34} +Luminea ZX-2844 {"NAME":"Luminea ZX-284","GPIO":[40,255,255,255,0,39,255,255,38,17,37,255,255],"FLAG":0,"BASE":18} +MagicHome RGB ZJ-WFMN-A V1.1 {"NAME":"MagicHome RGB","GPIO":[0,0,0,0,0,37,0,0,38,39,0,0,0],"FLAG":0,"BASE":34} +MagicHome RGBW ZJ-WFMN-A V1.1 {"NAME":"MagicHome RGBW","GPIO":[0,0,0,0,51,38,0,0,37,39,0,40,0],"FLAG":0,"BASE":34} +MagicHome ZJ-ESP-IR-F V1 {"NAME":"ZJ-ESP-IR-F V1","GPIO":[255,255,255,255,51,38,255,255,37,39,255,40,255],"FLAG":0,"BASE":18} +Nexlux {"NAME":"MagicHome V1.1","GPIO":[0,0,0,0,51,37,0,0,38,39,0,0,0],"FLAG":0,"BASE":34} +Shelly RGBW2 {"NAME":"RGBW2","GPIO":[255,255,33,255,24,91,255,255,21,17,23,22,255],"FLAG":0,"BASE":18} +``` + +## LED Strip +``` +BlitzWolf BW-LT11 {"NAME":"BW-LT11 Strip","GPIO":[17,255,255,255,37,40,255,255,38,255,39,255,255],"FLAG":0,"BASE":18} +LSC Smart Connect RGBW {"NAME":"LSC-RGBW-Strip","GPIO":[51,0,0,0,37,0,0,0,38,40,39,0,0],"FLAG":0,"BASE":18} +Merkury Innovations MI-EW003-999W {"NAME":"MI-EW003-999W ","GPIO":[17,255,255,255,37,40,255,255,38,41,39,255,255],"FLAG":0,"BASE":18} +Mirabella Genio 3 Metre {"NAME":"MirabellaStrip","GPIO":[17,255,255,255,37,40,255,255,38,255,39,255,255],"FLAG":0,"BASE":18} +Monster Smart IlluminEssence {"NAME":"MI-EW003-999W ","GPIO":[17,255,255,255,37,40,255,255,38,41,39,255,255],"FLAG":0,"BASE":18} +Sonoff L1 {"NAME":"SonoffL1","GPIO":[0,148,0,149,0,0,0,0,0,56,0,0,0],"FLAG":0,"BASE":70} +Torchstar {"NAME":"Torchstar","GPIO":[255,255,255,255,52,255,255,255,255,255,255,37,53],"FLAG":1,"BASE":18} +``` + +## Light +``` +Brilliant 20702/06 Garden {"NAME":"Brilliant Gard","GPIO":[0,0,0,0,37,0,255,255,38,0,39,0,0],"FLAG":0,"BASE":18} +Brilliant Smart WiFi Downlight CCT {"NAME":"SmartCCTDwnLgt","GPIO":[0,0,0,0,0,37,0,0,0,38,0,0,0],"FLAG":0,"BASE":48} +Fcmila XDD-48W {"NAME":"XDD-48W","GPIO":[0,0,0,0,37,40,0,0,38,25,39,0,0],"FLAG":0,"BASE":18} +Mi LED Desk Lamp MJTD01YL {"NAME":"Mi Desk Lamp","GPIO":[0,0,17,0,37,38,0,0,150,151,0,0,0],"FLAG":0,"BASE":66} +Mirabella Genio Dimmable CCT Downlight {"NAME":"GenioDLightCCT","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":48} +Mirabella Genio Dimmable RGBCCT Downlight {"NAME":"GenioDLightRGB","GPIO":[0,0,0,0,38,37,0,0,41,39,40,0,0],"FLAG":0,"BASE":18} +Sonoff BN-SZ01 {"NAME":"Sonoff BN-SZ","GPIO":[0,0,0,0,0,0,0,0,37,56,0,0,0],"FLAG":0,"BASE":22} +Spotlight 9cm RGB+W 7W {"NAME":"Spotlight RGBW","GPIO":[0,0,0,0,0,0,0,0,0,143,0,144,0],"FLAG":0,"BASE":27} +Utorch PZE-911 {"NAME":"Utorch PZE-911","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":1} +Utorch UT40 {"NAME":"Utorch UT40","GPIO":[0,0,0,0,0,0,0,0,38,0,37,0,0],"FLAG":0,"BASE":1} +Wipro Next 20W Smart LED Batten {"NAME":"WIPROBatten","GPIO":[255,255,255,255,255,37,255,255,255,47,255,255,255],"FLAG":1,"BASE":18} +Zemismart 14W RGB-CCT {"NAME":"ZemiDownLight6","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18} +Zemismart 4" 10W RGBW {"NAME":"ZemiDownLight","GPIO":[0,0,0,0,0,0,0,0,0,143,0,144,0],"FLAG":0,"BASE":27} +``` + +## Outdoor Plug +``` +Acenx SOP04-US Dual {"NAME":"SOP04-US Dual","GPIO":[255,255,255,255,56,57,255,255,21,17,22,255,255],"FLAG":0,"BASE":18} +Albohes PC-1606 {"NAME":"Albohes PC1606","GPIO":[17,0,0,0,0,22,18,0,21,0,0,0,0],"FLAG":1,"BASE":39} +ECF-SOP03 {"NAME":"Outdoor3Outlet","GPIO":[0,0,0,23,56,0,0,0,21,17,22,0,0],"FLAG":0,"BASE":18} +Feit Electric Dual {"NAME":"Prime Smart ou","GPIO":[255,255,255,255,157,56,255,255,21,17,255,255,255],"FLAG":0,"BASE":18} +Geeni GN-OW101-101 {"NAME":"Geeni Outdoor","GPIO":[17,0,0,0,0,57,0,0,0,52,21,0,0],"FLAG":0,"BASE":18} +LEPOWER {"NAME":"LEPOWER Outdoo","GPIO":[255,255,255,255,56,57,255,255,21,17,22,255,255],"FLAG":0,"BASE":18} +Oittm Outdoor {"NAME":"Oittm Outdoor","GPIO":[17,0,0,0,0,0,0,0,0,0,56,21,255],"FLAG":0,"BASE":18} +SK03 {"NAME":"SK03 Outdoor","GPIO":[17,0,0,0,133,132,0,0,131,57,56,21,0],"FLAG":0,"BASE":57} +Top-Max PS-1602 {"NAME":"PS-1602","GPIO":[17,255,255,255,0,22,18,0,21,56,0,0,0],"FLAG":0,"BASE":29} +``` + +## Plug +``` +3Stone Mini {"NAME":"3Stone Smart P","GPIO":[0,17,0,0,0,0,0,0,0,57,21,0,0],"FLAG":0,"BASE":18} +Acashna HS108 {"NAME":"HS108","GPIO":[0,17,0,0,0,0,0,0,0,56,21,0,0],"FLAG":1,"BASE":18} +Aisirer UK1 {"NAME":"AISIRER","GPIO":[255,255,255,255,56,255,255,255,21,17,255,255,255],"FLAG":1,"BASE":18} +Albohes PS-1602 {"NAME":"Albohes PC1606","GPIO":[17,0,0,0,0,22,18,0,21,0,0,0,0],"FLAG":1,"BASE":39} +Aldi Medion Life+ S85225 {"NAME":"Medion","GPIO":[0,0,0,17,134,132,0,0,131,158,21,0,0],"FLAG":0,"BASE":52} +Alexfirst TV-ASP801EU {"NAME":"Alexfirst","GPIO":[17,0,0,0,54,56,0,0,21,0,0,0,0],"FLAG":0,"BASE":18} +Alfawise PE1004T {"NAME":"PE1004T","GPIO":[255,255,255,255,56,57,255,255,21,17,255,255,255],"FLAG":0,"BASE":18} +Amysen YX-WS01 {"NAME":"Amyse YX WS01","GPIO":[255,17,255,255,255,255,255,255,255,56,21,255,255],"FLAG":1,"BASE":18} +ANBES ABS-CZ004 {"NAME":"ANBES ABS-CZ00","GPIO":[0,0,0,0,21,133,0,0,131,17,132,22,18],"FLAG":0,"BASE":18} +Anoopsyche AWP07L {"NAME":"Anoopsyche AWP","GPIO":[0,0,0,0,56,21,0,0,0,17,0,0,0],"FLAG":0,"BASE":18} +Anoopsyche JH-G01B1 {"NAME":"JH-G01B1","GPIO":[0,145,0,146,0,0,0,0,17,56,21,0,0],"FLAG":0,"BASE":41} +Anoopsyche UK1D {"NAME":"UK1D","GPIO":[0,17,0,0,133,132,0,0,130,52,21,0,0],"FLAG":0,"BASE":6} +Aoycocr EU5-16A {"NAME":"Aoycocr","GPIO":[255,0,56,0,0,0,0,0,255,17,0,21,0],"FLAG":0,"BASE":17} +Aoycocr Smart Plug {"NAME":"Aoycocr Smart ","GPIO":[0,0,56,0,0,0,0,0,0,17,0,21,0],"FLAG":1,"BASE":18} +Arlec Grid Connect PC189HA {"NAME":"Arlec","GPIO":[0,0,0,0,123,0,0,0,21,56,17,0,0],"FLAG":0,"BASE":18} +Arlec Twin PC288HA {"NAME":"Arlec Twin","GPIO":[0,17,0,22,0,0,0,0,0,56,21,0,0],"FLAG":0,"BASE":18} +Aukey SH-PA1 {"NAME":"AUKEY SH-PA1","GPIO":[0,0,57,0,0,0,0,0,0,17,0,21,0],"FLAG":0,"BASE":18} +Avatar 10A {"NAME":"AWP07L","GPIO":[56,255,255,255,255,134,255,255,130,17,132,21,255],"FLAG":1,"BASE":18} +Avatar AWP07L 10A {"NAME":"Avatar AWP07L","GPIO":[56,255,255,255,0,134,0,0,131,17,132,21,0],"FLAG":0,"BASE":45} +Avatto JH-G01E {"NAME":"AVATTO JH-G01E","GPIO":[0,145,0,146,0,0,0,0,17,56,21,0,0],"FLAG":0,"BASE":41} +AWP02L-N {"NAME":"AWP02L-N","GPIO":[57,0,56,0,0,0,0,0,0,17,0,21,0],"FLAG":1,"BASE":18} +AWP04L {"NAME":"AWP04L","GPIO":[57,255,255,131,255,134,0,0,21,17,132,56,255],"FLAG":0,"BASE":18} +AWP07L {"NAME":"AISIRER AWP07L","GPIO":[0,0,0,0,56,21,0,0,0,17,0,0,0],"FLAG":0,"BASE":18} +AWP08L {"NAME":"AWP08L","GPIO":[0,0,56,0,0,0,0,0,0,17,0,21,0],"FLAG":1,"BASE":18} +AzpenHome Smart {"NAME":"Socket2Me","GPIO":[52,255,255,255,22,255,0,0,21,255,17,255,255],"FLAG":0,"BASE":18} +Bestek MRJ1011 {"NAME":"BestekMRJ1011","GPIO":[0,0,0,0,56,0,0,0,21,17,0,0,0],"FLAG":0,"BASE":1} +Bilikay SP10 {"NAME":"Bilikay SP10","GPIO":[255,37,255,17,255,255,255,255,255,56,21,255,255],"FLAG":0,"BASE":18} +Blitzwolf BW-SHP2 {"NAME":"BlitzWolf SHP","GPIO":[57,255,56,255,0,134,0,0,131,17,132,21,0],"FLAG":0,"BASE":45} +Blitzwolf BW-SHP3 {"NAME":"Blitzwolf SHP3","GPIO":[56,0,57,0,22,134,0,0,131,18,132,21,17],"FLAG":0,"BASE":45} +Blitzwolf BW-SHP3 alt {"NAME":"Blitzwolf SHP3","GPIO":[18,56,0,131,134,132,0,0,17,0,22,21,0],"FLAG":0,"BASE":18} +Blitzwolf BW-SHP4 {"NAME":"BlitzWolf SHP","GPIO":[57,255,56,255,0,134,0,0,131,17,132,21,0],"FLAG":0,"BASE":45} +Blitzwolf BW-SHP5 {"NAME":"SHP5","GPIO":[57,145,56,146,0,22,0,0,0,0,21,0,17],"FLAG":0,"BASE":18} +Blitzwolf BW-SHP6 {"NAME":"BlitzWolf SHP","GPIO":[57,255,56,255,0,134,0,0,131,17,132,21,0],"FLAG":0,"BASE":45} +Blitzwolf BW-SHP8 {"NAME":"SHP8","GPIO":[0,56,0,17,134,132,0,0,131,53,21,0,0],"FLAG":0,"BASE":64} +BN-LINK BNC-60/U133TJ-2P {"NAME":"BNC-60/U133TJ","GPIO":[0,56,0,17,134,132,0,0,131,57,21,0,0],"FLAG":0,"BASE":18} +Brilliant USB Charger {"NAME":"Brilliant","GPIO":[0,0,0,0,0,21,0,0,0,52,90,0,0],"FLAG":0,"BASE":18} +BSD15 {"NAME":"TuyaPlugBSD15","GPIO":[255,255,255,255,255,255,255,255,21,17,56,255,255],"FLAG":1,"BASE":18} +BSD25 {"NAME":"Tuya Wifi Plug","GPIO":[255,255,255,255,56,255,255,255,21,17,255,255,255],"FLAG":0,"BASE":18} +BSD29 {"NAME":"BSD29","GPIO":[0,0,0,131,134,132,0,0,21,17,56,0,0],"FLAG":0,"BASE":52} +BSD33 {"NAME":"Generic","GPIO":[255,255,255,131,134,132,255,255,21,17,56,255,255],"FLAG":0,"BASE":18} +BSD34 {"NAME":"BSD34 Plug","GPIO":[255,255,255,255,255,255,255,255,21,17,56,255,255],"FLAG":0,"BASE":18} +CE Smart Home - LA-WF3 {"NAME":"CE LA-WF3","GPIO":[0,0,0,0,56,57,0,0,21,17,0,0,0],"FLAG":0,"BASE":18} +Conico SM-PW70 {"NAME":"Conico SM-PW70","GPIO":[0,0,0,0,56,57,0,0,21,17,0,0,0],"FLAG":1,"BASE":18} +Coosa {"NAME":"COOSA","GPIO":[0,0,0,0,57,52,0,0,21,17,255,0,0],"FLAG":0,"BASE":1} +Coosa SP1 {"NAME":"COOSA SP1","GPIO":[57,255,56,255,0,134,0,0,131,17,132,21,0],"FLAG":0,"BASE":45} +CrazyLynX WiFi {"NAME":"CrazyLynX","GPIO":[255,255,255,255,57,56,255,255,21,17,255,255,255],"FLAG":1,"BASE":18} +CYYLTF BIFANS J23 {"NAME":"CYYLTD BIFANS J23","GPIO":[56,0,0,0,0,0,0,0,21,17,0,0,0],"FLAG":1,"BASE":18} +Delock 11826 {"NAME":"Delock 11826","GPIO":[17,0,0,0,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":1} +Digoo DG-SP01 {"NAME":"DG-SP01","GPIO":[255,17,255,21,56,37,255,255,38,39,40,255,255],"FLAG":0,"BASE":18} +Digoo NX-SP202 {"NAME":"Generic_SP202","GPIO":[52,0,0,131,91,134,0,0,22,17,132,21,0],"FLAG":0,"BASE":63} +DILISENS SP201 {"NAME":"Dilisens SP201","GPIO":[0,0,131,0,133,132,52,21,18,22,17,0,0],"FLAG":0,"BASE":18} +DWFeng AWP02L-N {"NAME":"AWP02L-N","GPIO":[255,255,56,255,255,255,255,255,255,17,255,21,255],"FLAG":0,"BASE":18} +ECO Plugs CT-065W {"NAME":"ECO/CT-065W","GPIO":[255,255,255,255,255,255,255,255,255,17,255,21,255],"FLAG":0,"BASE":18} +EFUN SH331W {"NAME":"Efun-Plug","GPIO":[56,0,158,0,0,134,0,0,131,17,132,21,0],"FLAG":0,"BASE":18} +EletecPro 2 {"NAME":"EletecPro-2","GPIO":[255,255,255,255,17,255,255,255,53,52,21,255,255],"FLAG":1,"BASE":18} +ENJOWI SP111-EU-W {"NAME":"Enjowi SP111-E","GPIO":[17,56,0,0,134,132,0,0,131,57,21,0,0],"FLAG":0,"BASE":55} +Epicka {"NAME":"Epicka","GPIO":[255,255,255,255,57,56,255,255,21,17,255,255,255],"FLAG":1,"BASE":18} +Esicoo YX-WS01 {"NAME":"Esicoo Plug","GPIO":[255,17,255,255,255,255,255,255,255,56,21,255,255],"FLAG":0,"BASE":18} +Estink C178 {"NAME":"Estink C178","GPIO":[0,0,0,0,52,57,0,0,21,17,0,0,0],"FLAG":0,"BASE":18} +ESW01-US {"NAME":"ESW01-US","GPIO":[0,0,0,0,21,56,0,0,0,0,9,0,0],"FLAG":1,"BASE":18} +ET RGB {"NAME":"RGB Smart Plug","GPIO":[37,0,39,0,38,134,0,0,131,17,132,21,0],"FLAG":0,"BASE":45} +EVA LOGIK NWF001 {"NAME":"EVA LOGIK Plug","GPIO":[255,17,255,255,255,255,255,255,255,52,21,255,255],"FLAG":0,"BASE":18} +EVO-Smart JH-G01U {"NAME":"EVO JH-G01U","GPIO":[0,0,0,0,21,17,0,0,56,0,0,0,0],"FLAG":0,"BASE":18} +FK-PW901U {"NAME":"FK-PW901U","GPIO":[56,255,255,255,255,23,255,255,21,17,24,22,255],"FLAG":0,"BASE":18} +FLHS-ZN04 {"NAME":"FLHS-ZN04","GPIO":[57,0,56,0,0,134,0,0,131,17,132,21,0],"FLAG":0,"BASE":45} +Fontastic SH01 {"NAME":"Fontastic","GPIO":[255,0,255,0,56,0,255,255,21,17,0,0,0],"FLAG":1,"BASE":18} +FrankEver FLHS-ZN04 {"NAME":"Israel plug","GPIO":[57,0,56,131,0,134,0,0,0,17,132,21,0],"FLAG":0,"BASE":45} +GDTech W-US001 {"NAME":"GDTech W-US001","GPIO":[255,17,255,255,255,255,255,255,255,56,21,255,255],"FLAG":1,"BASE":18} +GDTech W-US003 {"NAME":"W-US003","GPIO":[0,17,255,255,255,0,0,0,0,56,21,0,0],"FLAG":0,"BASE":18} +Geeni Charge {"NAME":"Geeni Charge","GPIO":[0,0,0,0,52,57,0,0,21,17,0,0,0],"FLAG":0,"BASE":18} +Geeni Glo {"NAME":"Geeni Glo","GPIO":[0,0,0,0,56,0,0,0,21,17,22,0,0],"FLAG":0,"BASE":18} +Geeni Spot {"NAME":"Geeni Spot","GPIO":[0,0,0,0,52,57,0,0,21,17,0,0,0],"FLAG":0,"BASE":18} +Geeni Switch Duo {"NAME":"Geeni Duo","GPIO":[0,0,0,0,18,22,0,0,17,52,21,0,53],"FLAG":0,"BASE":18} +Globe Smart {"NAME":"GlobeSmartPlug","GPIO":[0,0,0,0,56,0,0,0,21,0,17,0,0],"FLAG":0,"BASE":18} +Gosund SP1 {"NAME":"Gosund SP1 v23","GPIO":[0,56,0,17,134,132,0,0,131,57,21,0,0],"FLAG":0,"BASE":55} +Gosund SP111 {"NAME":"Gosund SP111","GPIO":[56,0,57,0,0,134,0,0,131,17,132,21,0],"FLAG":0,"BASE":18} +Gosund SP112 {"NAME":"SHP5","GPIO":[56,145,57,146,255,22,0,0,255,255,21,255,18],"FLAG":0,"BASE":18} +Gosund WP1-1 {"NAME":"Gosund WP1-1","GPIO":[0,0,56,0,0,134,0,0,131,17,132,21,0],"FLAG":0,"BASE":45} +Gosund WP211 {"NAME":"Gosund wp211","GPIO":[0,0,0,0,21,0,0,0,0,17,0,22,18],"FLAG":0,"BASE":18} +Gosund WP212 {"NAME":"Gosund_WP212","GPIO":[17,0,0,0,18,0,0,0,21,0,22,0,0],"FLAG":0,"BASE":18} +Gosund WP3 {"NAME":"Gosund WP3","GPIO":[0,0,0,0,17,0,0,0,56,57,21,0,0],"FLAG":0,"BASE":18} +Gosund WP5 {"NAME":"Gosund-WP5","GPIO":[255,255,255,255,17,255,255,255,53,52,21,255,255],"FLAG":1,"BASE":18} +Gosund WP6 {"NAME":"Gosund WP6","GPIO":[0,0,0,17,0,0,0,0,56,57,21,0,0],"FLAG":0,"BASE":18} +Grefic TE101 {"NAME":"Grefic TE101","GPIO":[0,0,0,0,56,0,0,0,21,17,0,0,0],"FLAG":0,"BASE":1} +Gyman SM-PW701U {"NAME":"Gyman","GPIO":[255,255,157,255,56,255,255,255,21,17,255,255,255],"FLAG":0,"BASE":18} +Hauppauge SL-1642 {"NAME":"SL-1642","GPIO":[0,0,0,0,52,0,0,0,21,17,0,0,0],"FLAG":0,"BASE":18} +HiHome WPP-16T {"NAME":"HiHome WPP-16T","GPIO":[17,56,255,255,134,132,255,255,18,255,22,130,21],"FLAG":1,"BASE":18} +hiwild W-US002 {"NAME":"W-US002","GPIO":[0,17,0,0,0,0,0,0,0,52,21,0,158],"FLAG":0,"BASE":18} +Houzetek AWP07L {"NAME":"AWP07L","GPIO":[56,255,255,130,255,134,255,255,0,17,132,21,255],"FLAG":0,"BASE":18} +HuaFan QinLu {"NAME":"Huafan SS","GPIO":[56,0,0,57,17,29,0,0,132,130,133,0,0],"FLAG":0,"BASE":24} +Hyleton 313 {"NAME":"Hyleton 313","GPIO":[57,0,56,0,0,0,0,0,0,17,0,21,0],"FLAG":0,"BASE":18} +Hyleton 317 {"NAME":"hyleton-317","GPIO":[56,0,57,0,58,0,0,0,0,90,0,21,0],"FLAG":0,"BASE":18} +Innens BSD29 {"NAME":"BSD29","GPIO":[0,0,56,131,0,134,0,0,0,17,132,21,0],"FLAG":0,"BASE":52} +iSwitch {"NAME":"Smart Plug XSA","GPIO":[255,17,255,255,255,255,255,255,255,56,21,255,255],"FLAG":0,"BASE":18} +Jeeo TF-SH330 {"NAME":"Jeeo TF-SH330","GPIO":[56,255,255,255,255,255,255,255,255,17,255,21,255],"FLAG":1,"BASE":18} +Jinvoo SM-PW701U {"NAME":"SM-PW702","GPIO":[0,0,0,0,57,56,0,0,21,17,0,0,0],"FLAG":0,"BASE":18} +Jinvoo SM-PW762U {"NAME":"SM-PW762U","GPIO":[0,0,0,0,158,56,0,0,21,17,22,0,0],"FLAG":0,"BASE":18} +Jules V (Upgrade Version) {"NAME":"Jules-V_UV","GPIO":[56,0,0,0,0,134,0,0,21,17,132,57,131],"FLAG":0,"BASE":18} +Jules.V NX-SM300 {"NAME":"NX-SM300","GPIO":[52,0,0,0,0,130,0,0,21,132,133,52,0],"FLAG":0,"BASE":6} +Kimire S12 {"NAME":"Kimire S12","GPIO":[255,255,255,17,255,255,255,255,255,56,21,255,255],"FLAG":0,"BASE":18} +King-Link C128 {"NAME":"King-Link C128","GPIO":[0,0,55,0,22,53,0,0,23,52,17,21,54],"FLAG":0,"BASE":18} +KMC 4 30608 {"NAME":"KMC 4 Outlet","GPIO":[0,56,0,0,133,132,0,0,130,22,23,21,17],"FLAG":0,"BASE":36} +KMC 70011 {"NAME":"KMC 70011","GPIO":[17,0,0,0,133,132,0,0,130,56,21,0,0],"FLAG":0,"BASE":36} +Kogan SmarterHome™ Energy Meter {"NAME":"Kogan Smart Sw","GPIO":[17,0,0,0,133,132,0,0,131,56,21,0,0],"FLAG":0,"BASE":18} +Koogeek KLSP1 {"NAME":"Koogeek-KLSP1","GPIO":[0,0,0,17,134,132,0,0,131,56,21,0,0],"FLAG":0,"BASE":18} +Koogeek KLUP1 {"NAME":"Koogeek-KLUP1","GPIO":[0,0,0,17,134,132,0,0,131,56,21,0,0],"FLAG":0,"BASE":18} +LA-WF3 {"NAME":"CE Smart Plug","GPIO":[255,255,255,255,56,57,255,255,21,17,255,255,255],"FLAG":0,"BASE":18} +Laduo YX-DE01 {"NAME":"YX-DE01","GPIO":[255,17,255,255,255,255,255,255,255,56,21,255,255],"FLAG":0,"BASE":18} +LESHP KS-501 {"NAME":"LESHP KS-501","GPIO":[17,0,0,0,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":1} +LINGANZH 16A {"NAME":"Linganzh Smart","GPIO":[56,0,0,0,0,0,0,0,0,17,0,21,0],"FLAG":0,"BASE":18} +Lonsonho 10A Type E {"NAME":"Lonsonho10ALed","GPIO":[0,0,0,0,56,0,0,0,21,17,0,0,0],"FLAG":0,"BASE":18} +Lonsonho RGB {"NAME":"RGB Smart Plug","GPIO":[37,0,38,0,0,39,0,0,0,17,0,21,0],"FLAG":0,"BASE":45} +LSC Smart Connect {"NAME":"LSC Smart Plug","GPIO":[255,255,255,255,56,255,255,255,21,255,17,255,255],"FLAG":0,"BASE":18} +Luminea ZX-2820-675 {"NAME":"ZX2820-675","GPIO":[0,0,56,0,0,134,0,0,131,17,132,21,0],"FLAG":0,"BASE":45} +Luminea ZX-2820-919 {"NAME":"Luminea ZX2820","GPIO":[17,0,0,0,133,132,0,0,131,56,21,0,0],"FLAG":0,"BASE":65} +Martin Jerry {"NAME":"MJ V01","GPIO":[255,255,255,255,56,255,255,255,21,17,255,255,255],"FLAG":0,"BASE":18} +Martin Jerry XS-SSA01 {"NAME":"MJ_XS-SSA01","GPIO":[0,0,0,0,0,0,0,0,56,17,0,21,0],"FLAG":0,"BASE":18} +Maxcio W-DE004 {"NAME":"Maxcio W-DE004","GPIO":[0,17,0,0,0,0,0,0,0,56,21,0,0],"FLAG":1,"BASE":18} +Maxcio W-UK007 {"NAME":"Maxcio","GPIO":[0,17,0,0,0,0,255,255,0,56,21,0,0],"FLAG":0,"BASE":18} +Maxcio W-UK007S {"NAME":"Maxcio","GPIO":[56,0,255,0,0,134,0,0,131,17,132,21,0],"FLAG":0,"BASE":45} +Maxcio W-US002S {"NAME":"W-US002S","GPIO":[57,0,56,0,0,134,0,0,131,17,132,21,0],"FLAG":0,"BASE":45} +Maxcio W-US003 {"NAME":"W-US003","GPIO":[255,17,255,255,255,255,255,255,255,22,21,255,255],"FLAG":0,"BASE":18} +Maxcio YX-DE02 {"NAME":"Generic","GPIO":[255,17,255,21,56,37,255,255,38,39,40,255,255],"FLAG":1,"BASE":18} +Merisny P2 {"NAME":"Generic","GPIO":[0,0,52,0,0,0,0,0,0,17,0,21,0],"FLAG":1,"BASE":18} +Merkury 70011 MIC-WW102 {"NAME":"MIC-WW102","GPIO":[17,0,0,0,0,0,0,0,0,56,21,0,0],"FLAG":0,"BASE":36} +Merkury Innovations {"NAME":"Merkury Switch","GPIO":[255,255,255,255,53,56,255,255,21,17,255,255,255],"FLAG":1,"BASE":18} +Merkury MI-OW101-101W {"NAME":"Merkury Switch","GPIO":[17,255,255,255,255,56,255,255,255,255,21,255,255],"FLAG":1,"BASE":18} +Mirabella Genio {"NAME":"Genio 1","GPIO":[0,0,56,0,0,0,0,0,21,17,0,0,0],"FLAG":0,"BASE":1} +Nanxin NX-SM200 16A {"NAME":"NX-SM200","GPIO":[56,0,0,0,0,134,0,0,21,17,132,57,131],"FLAG":0,"BASE":18} +NEO Coolcam 16A {"NAME":"Neo Coolcam 16","GPIO":[17,0,0,0,133,132,0,0,131,56,21,0,0],"FLAG":0,"BASE":49} +Obi Stecker {"NAME":"OBI Socket","GPIO":[255,255,0,255,52,21,0,0,54,255,17,0,255],"FLAG":1,"BASE":51} +Obi Stecker 2 {"NAME":"OBI Socket 2","GPIO":[0,0,0,0,21,17,0,0,56,53,0,0,0],"FLAG":0,"BASE":61} +Obi Stecker IP44 {"NAME":"OBI Socket 2","GPIO":[0,0,0,0,21,17,0,0,56,53,0,0,0],"FLAG":0,"BASE":61} +Oittm Smart {"NAME":"Oittm","GPIO":[0,0,0,0,21,56,0,0,17,0,0,0,0],"FLAG":0,"BASE":1} +Oukitel P1 {"NAME":"Oukitel P1Dual","GPIO":[52,255,255,255,255,255,255,255,22,17,255,21,255],"FLAG":0,"BASE":18} +Oukitel X6P {"NAME":"OUKITEL X6P","GPIO":[255,255,57,255,255,255,255,255,255,17,255,21,255],"FLAG":0,"BASE":18} +OxaOxe NX-SM200 {"NAME":"NX-SM200","GPIO":[17,0,0,0,133,132,0,0,131,158,21,0,0],"FLAG":0,"BASE":18} +Oxaoxe NX-SM800 {"NAME":"NX-SM800","GPIO":[0,0,0,131,0,134,0,0,21,17,132,0,0],"FLAG":0,"BASE":45} +OxaOxe NX-SP202 {"NAME":"oxaoxe-dual","GPIO":[18,0,0,0,134,132,0,0,131,56,21,22,17],"FLAG":0,"BASE":18} +OxaOxe NX-SP202 v2 {"NAME":"oxaoxe-dold","GPIO":[56,0,0,131,17,134,0,0,21,18,132,22,0],"FLAG":0,"BASE":18} +Panamalar NX-SM200 16A {"NAME":"NX-SM200","GPIO":[0,0,0,0,56,134,0,0,131,17,132,21,0],"FLAG":1,"BASE":18} +Powertech {"NAME":"Jaycar","GPIO":[56,0,0,0,0,0,0,0,0,9,0,21,0],"FLAG":0,"BASE":6} +Powrui AW-08 {"NAME":"POWRUI AW-08","GPIO":[0,0,0,0,9,21,0,0,0,52,57,0,0],"FLAG":1,"BASE":18} +Prime CCRCWFII113PK {"NAME":"Prime","GPIO":[0,0,0,0,57,56,0,0,21,122,0,0,0],"FLAG":0,"BASE":18} +RGB Light {"NAME":"RGB Light","GPIO":[37,0,38,0,0,39,0,0,0,90,0,21,0],"FLAG":0,"BASE":18} +RSH-WS007 {"NAME":"RSH-WS007","GPIO":[0,0,56,0,0,0,0,0,0,17,0,21,0],"FLAG":1,"BASE":18} +Shelly Plug S {"NAME":"Shelly Plug S","GPIO":[57,255,56,255,0,134,0,0,131,17,132,21,0],"FLAG":2,"BASE":45} +Slitinto NX-SM110 {"NAME":"Slitinto SM110","GPIO":[0,0,56,0,0,134,0,0,131,17,132,21,0],"FLAG":0,"BASE":45} +Slitinto NX-SM112 {"NAME":"NX-SM112","GPIO":[158,0,0,131,0,134,0,0,0,17,132,21,0],"FLAG":0,"BASE":45} +Slitinto NX-SP202 {"NAME":"Slitinto SP202","GPIO":[17,0,0,0,134,132,0,0,131,52,22,21,91],"FLAG":0,"BASE":64} +SM-PW702 {"NAME":"SM-PW702","GPIO":[0,0,0,0,57,56,0,0,21,17,0,0,0],"FLAG":0,"BASE":18} +SmartDGM PP-W162 {"NAME":"SmartDGM Plug","GPIO":[0,0,0,17,134,132,0,0,131,52,21,0,0],"FLAG":0,"BASE":18} +Sonoff S20 {"NAME":"Sonoff S20","GPIO":[17,255,255,255,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":8} +Sonoff S22 TH {"NAME":"Sonoff S22","GPIO":[17,255,0,255,255,0,0,0,21,56,255,0,0],"FLAG":0,"BASE":4} +Sonoff S26 {"NAME":"Sonoff S26","GPIO":[17,255,255,255,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":8} +Sonoff S31 {"NAME":"Sonoff S31","GPIO":[17,145,0,146,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":41} +SP201 Dual {"NAME":"SP-201","GPIO":[31,0,0,131,17,134,0,0,21,18,132,22,0],"FLAG":0,"BASE":45} +SS01 {"NAME":"Smart Plug SS0","GPIO":[255,255,255,255,255,255,255,255,21,17,255,255,255],"FLAG":1,"BASE":18} +STITCH by Monoprice 27937 {"NAME":"Stitch 27937","GPIO":[17,0,56,0,133,132,0,0,131,0,21,0,57],"FLAG":0,"BASE":18} +STITCH by Monoprice 35511 {"NAME":"Stitch 35511","GPIO":[56,0,57,0,0,0,0,0,0,17,0,21,0],"FLAG":1,"BASE":18} +SuperNight Dual {"NAME":"SuperNight Dua","GPIO":[255,17,255,21,132,133,255,255,22,130,58,255,255],"FLAG":1,"BASE":18} +SWA1 {"NAME":"SWA1","GPIO":[0,0,0,0,52,21,0,0,0,17,0,0,0],"FLAG":0,"BASE":18} +SWA1 {"NAME":"SWA1","GPIO":[0,0,0,0,52,21,0,0,0,17,0,0,0],"FLAG":0,"BASE":18} +SWA11 {"NAME":"SWA1","GPIO":[0,0,0,0,52,21,0,0,0,17,0,0,0],"FLAG":0,"BASE":18} +SWA9 {"NAME":"SWA9","GPIO":[0,0,0,0,52,21,0,0,0,17,0,0,0],"FLAG":0,"BASE":18} +TanTan WP3 {"NAME":"TanTan WP3","GPIO":[57,0,56,0,0,0,0,0,0,17,0,21,0],"FLAG":0,"BASE":18} +Teckin SP10 {"NAME":"Teckin SP10","GPIO":[255,255,56,255,255,255,255,255,255,17,255,21,255],"FLAG":0,"BASE":18} +Teckin SP20 {"NAME":"TECKIN SP20","GPIO":[56,255,57,255,21,134,0,0,131,17,132,0,0],"FLAG":0,"BASE":45} +Teckin SP21 {"NAME":"Teckin SP21","GPIO":[0,0,56,0,0,0,0,0,0,17,0,21,0],"FLAG":0,"BASE":45} +Teckin SP22 {"NAME":"Teckin","GPIO":[0,17,0,57,134,132,0,0,131,56,21,0,0],"FLAG":0,"BASE":52} +Teckin SP23 {"NAME":"Teckin SP23","GPIO":[56,255,57,255,0,134,0,0,131,17,132,21,0],"FLAG":0,"BASE":45} +Teckin SP25 {"NAME":"Teckin SP25","GPIO":[56,255,255,255,255,255,255,255,22,17,255,21,255],"FLAG":1,"BASE":18} +Teckin SP27 {"NAME":"Teckin SP27","GPIO":[56,255,255,255,255,255,255,255,255,17,255,21,255],"FLAG":0,"BASE":18} +Tflag NX-SM100 {"NAME":"NX-SM100","GPIO":[56,0,0,0,0,134,0,0,21,17,132,57,131],"FLAG":0,"BASE":18} +THRUMM XS-A11 {"NAME":"THRUMM XS-A11 ","GPIO":[255,17,255,255,255,255,255,255,255,56,29,255,255],"FLAG":0,"BASE":18} +Timethinker TK04 {"NAME":"TimethinkerEU","GPIO":[255,255,255,255,17,255,0,0,255,52,21,255,0],"FLAG":0,"BASE":18} +TimeThinker WS2 {"NAME":"TimeThinkerWS2","GPIO":[0,0,0,0,17,0,0,0,0,52,21,0,0],"FLAG":0,"BASE":18} +TomaxUSA HKWL-SO07W {"NAME":"HKWL-SO07W","GPIO":[17,255,255,255,255,255,255,255,255,255,21,255,56],"FLAG":0,"BASE":18} +WAGA life CHCZ02MB {"NAME":"WAGA CHCZ02MB","GPIO":[57,0,0,131,0,134,0,0,21,17,132,56,0],"FLAG":0,"BASE":68} +WAZA 10A {"NAME":"WAZA","GPIO":[0,0,0,0,21,17,0,0,56,0,0,0,0],"FLAG":1,"BASE":18} +WAZA JH-G01B {"NAME":"Teckin SP27","GPIO":[255,255,255,255,53,255,255,255,21,17,255,255,255],"FLAG":0,"BASE":18} +WAZA JH-G01E {"NAME":"Waza JH-G01E","GPIO":[0,0,0,0,52,0,0,0,21,17,0,0,0],"FLAG":1,"BASE":18} +WiOn 50055 {"NAME":"WiOn","GPIO":[255,0,52,0,0,0,0,0,255,17,0,21,0],"FLAG":0,"BASE":17} +WL-SC01 {"NAME":"WL-SC01","GPIO":[0,0,0,0,56,0,0,0,21,0,17,0,0],"FLAG":0,"BASE":1} +Woox R4026 {"NAME":"WOOX R4026","GPIO":[0,0,0,17,0,0,0,0,0,56,21,0,0],"FLAG":0,"BASE":18} +XS-SSA01 {"NAME":"XS-SSA01","GPIO":[255,17,255,255,255,255,255,255,255,56,21,255,255],"FLAG":0,"BASE":18} +XS-SSA01 Alternate {"NAME":"XS-SSA01","GPIO":[255,17,255,255,255,255,255,255,56,255,255,21,255],"FLAG":0,"BASE":18} +XS-SSA05 {"NAME":"XS-SSA05","GPIO":[30,255,255,131,255,133,255,255,21,17,132,31,255],"FLAG":1,"BASE":18} +XS-SSA06 {"NAME":"RGB Light","GPIO":[37,0,38,0,0,39,0,0,0,90,0,21,0],"FLAG":0,"BASE":18} +Yelomin JH-G01E {"NAME":"Yelomin","GPIO":[0,145,0,146,0,0,0,0,17,56,21,0,0],"FLAG":0,"BASE":18} +YERON US101 {"NAME":"YERON_US101","GPIO":[255,255,255,17,133,132,255,255,131,56,21,255,255],"FLAG":0,"BASE":18} +YT-E003 {"NAME":"YT-E003-SP202","GPIO":[17,0,0,0,134,132,0,0,131,52,22,21,91],"FLAG":0,"BASE":64} +YX-WS02 {"NAME":"Amysen YX-WS02","GPIO":[255,17,255,255,255,255,255,255,255,56,21,255,255],"FLAG":1,"BASE":18} +ZBR-001 {"NAME":"ZBR-001","GPIO":[17,255,255,255,133,132,255,255,130,56,21,255,57],"FLAG":1,"BASE":18} +ZooZee {"NAME":"ZooZee","GPIO":[57,255,56,255,255,255,255,255,255,17,255,21,255],"FLAG":0,"BASE":18} +ZooZee SA102 {"NAME":"ZooZee","GPIO":[57,255,56,255,21,133,255,255,131,17,132,255,255],"FLAG":0,"BASE":18} +ZooZee SE131 {"NAME":"ZooZee SE131","GPIO":[255,0,56,0,0,0,0,0,255,17,0,21,0],"FLAG":0,"BASE":17} +ZSP-001 {"NAME":"ZSP-001","GPIO":[17,255,255,255,133,132,255,255,130,57,21,255,56],"FLAG":1,"BASE":18} +``` + +## Power Strip +``` +3Stone (2019 Model) {"NAME":"3Stone-2019","GPIO":[0,0,56,0,25,22,0,0,24,17,23,21,0],"FLAG":0,"BASE":18} +ACENX 3AC+3USB {"NAME":"ACENX 3-Outlet","GPIO":[56,55,54,53,0,21,0,0,23,24,22,0,17],"FLAG":0,"BASE":18} +Annhome 3AC + 2USB {"NAME":"1200W WiFi SPS","GPIO":[32,0,0,0,57,52,0,0,21,17,22,23,33],"FLAG":0,"BASE":18} +AOFO 3AC+4USB {"NAME":"AOFO","GPIO":[0,56,0,17,22,21,0,0,0,23,24,0,0],"FLAG":1,"BASE":18} +AOFO 4AC+4USB {"NAME":"AOFO4AC4USB","GPIO":[0,56,0,17,22,21,0,0,23,24,25,0,0],"FLAG":1,"BASE":18} +CE Garden Power Stake {"NAME":"CE Power Stake","GPIO":[0,0,0,0,56,57,0,0,21,17,0,0,0],"FLAG":0,"BASE":18} +CE Smart Home LTS-6A-W5 {"NAME":"CE Power Strip","GPIO":[52,0,0,0,22,21,0,0,24,23,25,26,17],"FLAG":0,"BASE":18} +CRST LTS-4G-W {"NAME":"CRST LTS-4G-W","GPIO":[0,0,0,0,24,0,255,255,22,23,21,0,0],"FLAG":0,"BASE":18} +Digoo DG-PS01 {"NAME":"Digoo DG-PS01","GPIO":[0,56,0,17,23,22,0,0,0,24,21,0,0],"FLAG":1,"BASE":18} +Geekbes 4AC+4USB {"NAME":"Geekbes 4xStri","GPIO":[0,56,0,17,22,21,0,0,23,24,25,0,0],"FLAG":1,"BASE":18} +Geeni GN-SW003 {"NAME":"Geeni GNCSW003","GPIO":[52,0,0,0,22,21,0,0,24,25,23,26,17],"FLAG":0,"BASE":18} +Hyleton 330 {"NAME":"Hyleton-330","GPIO":[57,0,0,56,29,17,0,0,31,30,32,0,25],"FLAG":0,"BASE":18} +Hyleton 331 {"NAME":"HLT-331","GPIO":[52,255,255,57,29,17,255,255,31,30,32,255,58],"FLAG":0,"BASE":18} +Hyleton 336 {"NAME":"HLT-336","GPIO":[52,0,0,57,29,17,0,0,31,30,0,0,32],"FLAG":0,"BASE":18} +KMC 5 {"NAME":"KMC 5-Outlet","GPIO":[56,0,0,0,25,9,0,0,22,21,23,0,24],"FLAG":0,"BASE":18} +Koogeek KLOE4 {"NAME":"Koogeek KLOE4","GPIO":[0,56,0,17,22,21,0,0,23,24,25,0,0],"FLAG":1,"BASE":18} +LeFun SK2 {"NAME":"LeFun SK2","GPIO":[0,0,0,17,22,21,0,0,23,24,25,0,0],"FLAG":0,"BASE":18} +Meross MSS425 {"NAME":"Meross MSS425","GPIO":[33,0,0,0,56,0,0,0,21,17,22,23,32],"FLAG":0,"BASE":18} +Monoprice 34082 {"NAME":"Stitch 34082","GPIO":[255,255,255,255,21,20,255,255,23,22,24,255,255],"FLAG":0,"BASE":18} +Nedis WIFIP310FWT {"NAME":"Nedis WIFIP310","GPIO":[0,56,0,17,22,21,0,0,0,23,24,0,0],"FLAG":1,"BASE":18} +Powrui AW-39 {"NAME":"Porui AW-39","GPIO":[56,0,0,0,21,255,0,0,23,24,22,0,9],"FLAG":0,"BASE":18} +S2199EU {"NAME":"S2199EU","GPIO":[0,17,0,52,23,25,0,0,21,24,22,0,0],"FLAG":1,"BASE":18} +SM-S0301 {"NAME":"SM-SO301","GPIO":[56,0,0,0,30,17,0,0,32,31,33,0,21],"FLAG":0,"BASE":18} +SM-S0301 {"NAME":"SM-SO301","GPIO":[52,255,255,57,29,17,255,255,31,30,32,255,25],"FLAG":0,"BASE":18} +SWB1 {"NAME":"SWB1","GPIO":[52,0,0,0,0,24,0,0,21,17,22,23,0],"FLAG":0,"BASE":18} +Teckin SS30 {"NAME":"Teckin SS30","GPIO":[52,255,255,57,29,17,255,255,31,30,32,255,58],"FLAG":0,"BASE":18} +Tellur TLL331031 {"NAME":"Tellur","GPIO":[0,56,0,17,22,21,0,0,0,23,24,0,0],"FLAG":1,"BASE":18} +TESSAN A4L-BK {"NAME":"TESSAN A4L-BK","GPIO":[0,0,0,24,23,0,255,255,21,0,22,0,0],"FLAG":0,"BASE":18} +Viflykoo 3AC+4USB {"NAME":"Viflykoo 3xStr","GPIO":[0,0,53,0,0,23,0,0,21,56,17,24,22],"FLAG":1,"BASE":18} +Woox R4028 {"NAME":"Woox R4028","GPIO":[0,56,0,17,23,22,0,0,0,24,21,0,0],"FLAG":1,"BASE":18} +Xenon SM-S0301 {"NAME":"SM-SO301","GPIO":[52,255,255,57,29,17,255,255,31,30,32,255,25],"FLAG":0,"BASE":18} +XS-A25 {"NAME":"XS-A25","GPIO":[52,0,53,0,25,22,0,0,24,17,23,21,0],"FLAG":0,"BASE":18} +XSA26-EU {"NAME":"XSA26-EU","GPIO":[52,0,53,0,25,22,0,0,24,17,23,21,0],"FLAG":0,"BASE":18} +Yagala {"NAME":"Yagala","GPIO":[0,0,0,17,22,24,0,0,23,25,21,0,0],"FLAG":0,"BASE":18} +Yagala SWB3 {"NAME":"YAGALA SWB3","GPIO":[0,0,53,0,0,23,0,0,21,0,22,24,0],"FLAG":1,"BASE":18} +Yagala SWB3 v2 {"NAME":"YAGALA SWB3","GPIO":[157,0,53,0,0,23,0,0,21,17,22,0,0],"FLAG":0,"BASE":18} +Yuanguo 4AC + 2 USB {"NAME":"YUANGUO","GPIO":[0,20,0,24,19,18,0,0,17,22,21,23,0],"FLAG":0,"BASE":1} +Zeoota PS022 {"NAME":"ZEOOTA 3x plus","GPIO":[0,57,0,56,22,21,0,0,17,23,24,0,0],"FLAG":1,"BASE":18} +ZLD-44EU-W {"NAME":"ZLD-44EU-W","GPIO":[17,255,255,255,22,21,18,19,23,24,25,0,0],"FLAG":0,"BASE":23} +ZLD-44USA-W {"NAME":"ZLD-44USA-W","GPIO":[0,56,0,17,22,21,0,0,23,24,25,0,0],"FLAG":1,"BASE":18} +ZLD64-EU-W {"NAME":"ZLD64-EU-W","GPIO":[0,56,0,17,22,21,0,0,0,0,23,0,0],"FLAG":0,"BASE":18} +``` + +## RF Bridge +``` +Sonoff RF Bridge 433 {"NAME":"Sonoff Bridge","GPIO":[17,148,255,149,255,255,0,0,255,56,255,0,0],"FLAG":0,"BASE":25} +``` + +## Relay +``` +1 Channel Inching/Self-Locking {"NAME":"1 Channel","GPIO":[17,0,0,0,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":12} +Blitzwolf BW-SS1 {"NAME":"BW-SS1","GPIO":[255,255,255,255,157,21,0,0,255,17,255,255,0],"FLAG":0,"BASE":18} +Canwing CW-001 {"NAME":"Canwing CW-001","GPIO":[17,255,0,255,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":1} +EACHEN ST-DC2 {"NAME":"Garage Control","GPIO":[11,0,0,0,23,22,18,0,21,52,12,24,0],"FLAG":1,"BASE":18} +Electrodragon Board SPDT {"NAME":"ED Relay Board","GPIO":[255,255,255,255,255,255,255,255,21,22,255,255,52],"FLAG":1,"BASE":18} +Electrodragon ESP8266 {"NAME":"ElectroDragon","GPIO":[18,255,17,255,255,255,0,0,22,21,255,255,52],"FLAG":1,"BASE":15} +eMylo SS-8839-03 {"NAME":"eMylo","GPIO":[0,0,0,0,56,0,0,0,21,0,17,0,0],"FLAG":1,"BASE":18} +eMylo XL9251WI {"NAME":"XL9251WI","GPIO":[255,255,255,255,56,255,255,255,21,255,17,255,255],"FLAG":0,"BASE":18} +eMylo YSA111A1N-FBA {"NAME":"Emylo","GPIO":[255,255,255,255,52,255,255,255,21,255,17,255,255],"FLAG":0,"BASE":18} +ESP-01 Relay V4.0 {"NAME":"ESP01v4","GPIO":[29,52,0,255,255,255,255,255,255,255,255,255,255],"FLAG":0,"BASE":18} +EX Store 2 Kanal V5 {"NAME":"EXS Relay V5","GPIO":[255,255,255,255,255,255,0,0,21,22,31,52,32],"FLAG":0,"BASE":16} +Geekcreit 2 Channel {"NAME":"Geekcreit 2ch","GPIO":[17,0,0,0,0,22,18,0,21,52,0,0,0],"FLAG":1,"BASE":18} +Gocomma Wi-Fi Smart Switch {"NAME":"GoCommaSmartSw","GPIO":[17,255,255,255,21,255,255,255,255,56,255,255,255],"FLAG":0,"BASE":18} +LC Technology 4CH {"NAME":"LC-Tech_4CH ","GPIO":[52,255,17,255,255,255,255,255,21,22,23,24,255],"FLAG":0,"BASE":18} +LinkNode R4 {"NAME":"LinkNode R4","GPIO":[0,0,0,0,0,0,0,0,21,22,23,0,24],"FLAG":0,"BASE":18} +LinkNode R8 {"NAME":"LinkNode R8","GPIO":[0,0,0,0,25,26,0,28,23,24,22,27,21],"FLAG":0,"BASE":18} +MHCOZY {"NAME":"Portail","GPIO":[9,0,0,0,0,0,0,0,21,56,0,0,0],"FLAG":1,"BASE":18} +Moeshouse 2 way 2 gang MS-104B {"NAME":"moeshouse2gang","GPIO":[0,0,17,0,160,0,0,0,43,42,21,22,0],"FLAG":0,"BASE":18} +Nova Digital Basic 1 MS101 {"NAME":"NovaDigBasic1","GPIO":[255,255,255,255,56,255,255,255,21,17,255,255,255],"FLAG":0,"BASE":18} +QS-WIFI-S03 {"NAME":"Generic","GPIO":[17,255,255,255,255,0,0,0,82,21,0,0,0],"FLAG":0,"BASE":1} +Shelly 1 {"NAME":"Shelly 1","GPIO":[0,0,0,0,21,82,0,0,0,0,0,0,0],"FLAG":0,"BASE":46} +Shelly 1PM {"NAME":"Shelly 1PM","GPIO":[56,0,0,0,82,134,0,0,0,0,0,21,0],"FLAG":2,"BASE":18} +Shelly 2 {"NAME":"Shelly 2","GPIO":[0,135,0,136,21,22,0,0,9,0,10,137,0],"FLAG":0,"BASE":47} +Shelly 2.5 {"NAME":"Shelly 2.5","GPIO":[56,255,17,255,21,83,0,0,6,82,5,22,156],"FLAG":2,"BASE":18} +SmartHome Smart Breaker {"NAME":"SmartHome Swit","GPIO":[255,255,255,255,21,255,255,255,17,57,255,56,255],"FLAG":0,"BASE":18} +Sonoff 4CH (R2) {"NAME":"Sonoff 4CH","GPIO":[17,255,255,255,23,22,18,19,21,56,20,24,0],"FLAG":0,"BASE":7} +Sonoff 4CH Pro (R2) {"NAME":"Sonoff 4CH Pro","GPIO":[17,255,255,255,23,22,18,19,21,56,20,24,0],"FLAG":0,"BASE":23} +Sonoff 5V Inching/Selflock Module RE5V1C {"NAME":"Sonoff RE5V1C","GPIO":[17,255,255,255,255,255,0,0,21,56,0,0,0],"FLAG":0,"BASE":18} +Sonoff Basic {"NAME":"Sonoff Basic","GPIO":[17,255,255,255,255,0,0,0,21,56,255,0,0],"FLAG":0,"BASE":1} +Sonoff Basic R3 {"NAME":"Basic R3","GPIO":[17,255,0,255,255,0,255,255,21,56,255,0,255],"FLAG":0,"BASE":1} +Sonoff Dual {"NAME":"Sonoff Dual","GPIO":[0,148,0,149,255,0,0,0,0,56,255,0,0],"FLAG":0,"BASE":5} +Sonoff Dual R2 {"NAME":"Sonoff Dual R2","GPIO":[255,255,0,255,0,22,255,17,21,56,0,0,0],"FLAG":0,"BASE":39} +Sonoff Mini {"NAME":"Sonoff Mini","GPIO":[17,0,0,0,9,0,0,0,21,56,0,0,255],"FLAG":0,"BASE":1} +Sonoff Pow {"NAME":"Sonoff Pow","GPIO":[17,0,0,0,0,130,0,0,21,132,133,52,0],"FLAG":0,"BASE":6} +Sonoff Pow R2 {"NAME":"Sonoff Pow R2","GPIO":[17,145,0,146,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":43} +Sonoff RF {"NAME":"Sonoff RF","GPIO":[17,255,255,255,255,0,0,0,21,56,255,0,0],"FLAG":0,"BASE":2} +Sonoff SV {"NAME":"Sonoff SV","GPIO":[17,255,0,255,255,255,0,0,21,56,255,0,0],"FLAG":1,"BASE":3} +Sonoff TH10/TH16 {"NAME":"Sonoff TH","GPIO":[17,255,0,255,255,0,0,0,21,56,255,0,0],"FLAG":0,"BASE":4} +Tuya Kinetic Switch {"NAME":"Kinetic Switch","GPIO":[0,0,0,0,52,0,0,0,21,17,0,0,0],"FLAG":0,"BASE":18} +Wemos Module 10A {"NAME":"WeMos Relay","GPIO":[17,255,52,255,21,255,255,255,255,255,255,255,255],"FLAG":0,"BASE":18} +WL-SW01_10 {"NAME":"WL-SW01_10","GPIO":[17,149,0,148,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":1} +Yunshan 10A 7-30VDC {"NAME":"Yunshan Relay","GPIO":[0,255,56,255,21,17,0,0,0,0,0,0,0],"FLAG":0,"BASE":33} +Zemismart ERC309 Kinetic {"NAME":"Kinetic Switch","GPIO":[255,255,255,255,255,255,0,0,255,108,255,107,255],"FLAG":0,"BASE":54} +``` + +## Switch +``` +3A Smart Home HGZB-043 {"NAME":"3A Smart Home ","GPIO":[52,0,55,18,22,19,0,0,17,21,54,23,53],"FLAG":0,"BASE":18} +Deta 6912HA {"NAME":"DETA 2G Switch","GPIO":[0,0,0,0,157,0,0,0,91,21,22,0,90],"FLAG":0,"BASE":18} +DS-102 1 Gang {"NAME":"DS-102 1 Gang","GPIO":[57,0,0,17,0,0,0,0,0,21,56,0,0],"FLAG":1,"BASE":18} +DS-102 2 Gang {"NAME":"DS-102 2 Gang","GPIO":[56,58,0,17,22,18,0,0,0,21,57,255,0],"FLAG":0,"BASE":18} +DS-102 3 Gang {"NAME":"DS-102 3 Gang","GPIO":[158,58,0,18,22,19,0,0,17,21,57,23,56],"FLAG":0,"BASE":18} +Enjowi WF-SK301 {"NAME":"Tuya 3 Channel","GPIO":[0,0,0,0,23,18,0,0,17,21,19,22,157],"FLAG":0,"BASE":18} +Etekcity 3-way {"NAME":"Etekcity 3Way","GPIO":[255,255,0,255,23,29,0,0,82,22,10,0,0],"FLAG":0,"BASE":18} +EtekCity ESWL01 {"NAME":"EtekCityESWL01","GPIO":[255,255,255,255,52,53,255,255,255,21,122,255,255],"FLAG":1,"BASE":18} +Gosund KS-602S {"NAME":"Gosund KS-602S","GPIO":[17,56,0,0,0,0,0,0,0,0,21,0,158],"FLAG":0,"BASE":18} +Jinvoo SM-SW101-1 {"NAME":"SM-SW101-1","GPIO":[52,0,0,18,0,0,0,0,17,21,0,0,0],"FLAG":1,"BASE":18} +Jinvoo SM-SW101-2 {"NAME":"SM-SW101-2","GPIO":[52,0,0,18,22,0,0,0,17,21,0,0,0],"FLAG":1,"BASE":18} +Jinvoo SM-SW101-3 {"NAME":"Jinvoo Wall Sw","GPIO":[52,0,0,18,22,19,0,0,17,21,0,23,0],"FLAG":1,"BASE":18} +Jinvoo SM-SW101-C Curtain {"NAME":"Jinvoo Curtain","GPIO":[52,0,0,18,22,19,0,0,17,21,0,23,0],"FLAG":1,"BASE":18} +KMC 70008 {"NAME":"KMC 70008","GPIO":[17,255,255,0,0,255,255,255,0,56,21,255,255],"FLAG":0,"BASE":18} +KOAANW DS302 {"NAME":"DS302","GPIO":[0,0,0,19,23,18,0,0,17,21,0,22,52],"FLAG":1,"BASE":18} +KS-601 2-way {"NAME":"2way Switch","GPIO":[255,255,158,255,255,83,255,255,22,21,255,82,255],"FLAG":0,"BASE":18} +Kuled K36 {"NAME":"KULED-B","GPIO":[9,255,255,255,255,255,21,52,29,56,255,255,255],"FLAG":0,"BASE":18} +Kuled KS602S {"NAME":"KULED","GPIO":[9,255,255,255,255,255,255,255,21,56,255,255,255],"FLAG":0,"BASE":18} +KYGNE CD-301 {"NAME":"KYGNE Touch","GPIO":[0,0,0,0,52,53,0,0,21,17,0,0,0],"FLAG":0,"BASE":10} +Lonsonho SK3-01 {"NAME":"Tuya 1 Channel","GPIO":[0,0,0,0,0,17,0,0,0,0,0,21,52],"FLAG":0,"BASE":18} +Lonsonho SK3-02 {"NAME":"Tuya 2 Channel","GPIO":[0,0,0,0,22,0,0,0,17,21,18,0,52],"FLAG":0,"BASE":18} +Luminea LHC-101.on {"NAME":"LHC-101.on","GPIO":[157,0,0,17,21,0,0,0,0,0,52,0,0],"FLAG":0,"BASE":18} +Luminea LHC-102.on {"NAME":"LHC-102.on","GPIO":[157,0,53,0,0,18,0,0,17,21,0,22,52],"FLAG":0,"BASE":18} +LX-WIFI-00M 4 Gang {"NAME":"LX-WIFI-00M","GPIO":[17,25,255,255,23,22,18,19,21,0,20,24,0],"FLAG":0,"BASE":7} +LYASI B07PYMG3WD {"NAME":"LYASI Touch Sw","GPIO":[0,0,0,0,56,52,0,0,21,17,0,0,0],"FLAG":0,"BASE":18} +Martin Jerry 15A {"NAME":"MJ Switch","GPIO":[255,255,255,255,57,56,255,255,21,17,255,255,255],"FLAG":0,"BASE":18} +Martin Jerry 3 Way {"NAME":"MJ 3Way Switch","GPIO":[255,255,255,255,52,53,0,0,21,9,157,255,0],"FLAG":0,"BASE":18} +Merkury MI-WW107-199W {"NAME":"MI-WW107-199W","GPIO":[52,0,0,0,0,0,0,0,17,21,0,0,0],"FLAG":0,"BASE":18} +Minitiger 1 Gang {"NAME":"minitiger 1 Gang","GPIO":[17,255,255,255,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":28} +Minitiger 2 Gang {"NAME":"minitiger 2 Gang","GPIO":[17,255,255,255,0,22,18,0,21,56,0,0,0],"FLAG":0,"BASE":28} +Moes 1 Gang Touch {"NAME":"B07C8FJY3G","GPIO":[54,0,0,17,21,0,0,0,0,0,52,0,55],"FLAG":0,"BASE":18} +Moes 2 Gang Touch {"NAME":"B07BLZFQZZ ","GPIO":[52,0,53,0,0,18,0,0,17,21,0,22,54],"FLAG":0,"BASE":18} +Moes 2 Gang Touch {"NAME":"Tuya Moes 2 Ch","GPIO":[25,255,24,0,0,18,0,0,17,21,0,22,23],"FLAG":0,"BASE":18} +Moes 3 Gang Touch {"NAME":"Tuya Moes 3 Ch","GPIO":[27,255,26,18,22,19,0,0,17,21,25,23,24],"FLAG":0,"BASE":18} +Moes 3-Way {"NAME":"Moes 3-Way","GPIO":[255,255,255,255,21,57,0,0,30,10,9,255,255],"FLAG":0,"BASE":18} +Moko {"NAME":"Moko Switch","GPIO":[0,0,0,17,21,134,0,0,0,0,0,0,0],"FLAG":0,"BASE":59} +Nexete DS-123 {"NAME":"DS-123","GPIO":[157,57,255,17,21,18,0,0,255,22,56,255,255],"FLAG":0,"BASE":18} +Sainko 1-Way {"NAME":"SAINKO 1CH","GPIO":[17,255,255,255,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":28} +SANA {"NAME":"SW02-03","GPIO":[56,255,255,19,23,18,255,255,17,21,255,22,255],"FLAG":0,"BASE":18} +SANA SASW-03 {"NAME":"SANA SASW-03","GPIO":[54,0,0,19,23,18,0,0,17,21,0,22,0],"FLAG":0,"BASE":18} +Sesoo SK3-04 {"NAME":"Tuya 4 Channel","GPIO":[52,255,255,19,23,17,0,0,20,24,22,21,18],"FLAG":0,"BASE":18} +Sesoo WIFI-US-SK3-04 {"NAME":"WIFI-US-SK3-04","GPIO":[255,255,255,19,23,17,0,0,20,24,22,21,18],"FLAG":0,"BASE":18} +SmartPlex 3 Gang {"NAME":"Tuya 3 Channel","GPIO":[255,255,255,255,21,18,0,0,19,23,17,22,255],"FLAG":0,"BASE":18} +Sonoff T1 EU 1 Gang {"NAME":"Sonoff T1 1CH","GPIO":[17,255,255,255,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":28} +Sonoff T1 EU 2 Gang {"NAME":"Sonoff T1 2CH","GPIO":[17,255,255,255,0,22,18,0,21,56,0,0,0],"FLAG":0,"BASE":29} +Sonoff T1 UK 1 Gang {"NAME":"Sonoff T1 1CH","GPIO":[17,255,255,255,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":28} +Sonoff T1 UK 2 Gang {"NAME":"Sonoff T1 2CH","GPIO":[17,255,255,255,0,22,18,0,21,56,0,0,0],"FLAG":0,"BASE":29} +Sonoff T1 UK 3 Gang {"NAME":"Sonoff T1 3CH","GPIO":[17,255,255,255,23,22,18,19,21,56,0,0,0],"FLAG":0,"BASE":30} +Sonoff T1 US 1 Gang {"NAME":"Sonoff T1 1CH","GPIO":[17,255,255,255,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":28} +Sonoff T1 US 2 Gang {"NAME":"Sonoff T1 2CH","GPIO":[17,255,255,255,0,22,18,0,21,56,0,0,0],"FLAG":0,"BASE":29} +Sonoff T1 US 3 Gang {"NAME":"Sonoff T1 3CH","GPIO":[17,255,255,255,23,22,18,19,21,56,0,0,0],"FLAG":0,"BASE":30} +Sonoff Touch EU {"NAME":"Sonoff Touch","GPIO":[17,255,0,255,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":10} +Sonoff Touch US {"NAME":"Sonoff Touch","GPIO":[17,255,0,255,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":10} +SS118-01K1 {"NAME":"SS118-01K1","GPIO":[255,255,255,17,21,255,255,255,255,255,56,255,255],"FLAG":0,"BASE":18} +STITCH by Monoprice 35557 {"NAME":"Tuya WF15S ","GPIO":[255,255,0,0,255,255,0,0,255,108,255,107,0],"FLAG":0,"BASE":54} +Teepao Smart-Rollladen-Schalter {"NAME":"Teepao","GPIO":[158,58,23,18,22,19,0,0,56,21,57,0,17],"FLAG":0,"BASE":18} +Tonbux AMZ180648-2 {"NAME":"Tonbux","GPIO":[17,255,255,255,255,0,0,0,21,56,255,0,0],"FLAG":0,"BASE":1} +Touch 2 Gang {"NAME":"tuya_2_gang","GPIO":[52,0,0,0,18,0,0,0,17,21,255,22,29],"FLAG":0,"BASE":18} +TreatLife SS01S {"NAME":"TL SS01S Swtch","GPIO":[0,0,0,0,52,158,0,0,21,17,0,0,0],"FLAG":0,"BASE":18} +WS-US-03 {"NAME":"WS-US-03","GPIO":[17,255,255,255,23,22,18,19,21,56,0,0,0],"FLAG":0,"BASE":30} +Xenon SM-SW102-2 {"NAME":"SM SW102","GPIO":[255,255,255,18,22,255,255,255,17,21,255,255,255],"FLAG":0,"BASE":18} +Youngzuth SW02 2-way {"NAME":"SW02 2W","GPIO":[52,255,255,9,21,255,255,255,10,22,255,255,255],"FLAG":0,"BASE":18} +Zemismart KS-611 3 Gang {"NAME":"Zemismart 3 Ga","GPIO":[0,0,56,0,19,18,0,0,22,21,23,0,17],"FLAG":0,"BASE":18} +Zemismart KS-811 1 gang {"NAME":"KS-811 Single","GPIO":[17,255,255,255,255,255,255,255,21,56,255,255,255],"FLAG":0,"BASE":18} +Zemismart KS-811 2 Gang {"NAME":"KS-811 Dual","GPIO":[255,255,52,255,255,18,255,255,22,21,255,255,17],"FLAG":0,"BASE":18} +Zemismart KS-811 3 Gang {"NAME":"KS-811 Triple","GPIO":[255,255,56,255,19,18,255,255,22,21,23,255,17],"FLAG":0,"BASE":18} +Zemismart WF-BS01 {"NAME":"WF-BS01","GPIO":[53,0,0,17,21,0,0,0,0,0,52,0,0],"FLAG":0,"BASE":18} +Zemismart ZM-L02E {"NAME":"ZSmart ZM-L02E","GPIO":[255,255,255,255,255,17,255,255,18,21,22,255,255],"FLAG":0,"BASE":18} +``` + +## Valve +``` +Hoenyzy DN20 3/4 {"NAME":"Unbranded TYWE3S DN20 WIFI Valve","GPIO":[0,0,0,0,0,0,0,0,17,21,0,0,0],"FLAG":0,"BASE":18} +Jinvoo SM-PW713 {"NAME":"Jinvoo Valve","GPIO":[0,0,0,0,21,52,0,0,17,53,0,0,0],"FLAG":1,"BASE":18} +TopKitchen Irrigation Timer {"NAME":"HoseController","GPIO":[255,255,255,255,255,255,255,255,255,255,255,21,22],"FLAG":1,"BASE":18} +``` + +## Wall Outlet +``` +Bestten LO-2-W2 {"NAME":"BESTTEN LO-2-W","GPIO":[17,0,0,0,57,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":1} +Charging Essentials WiFi Smart {"NAME":"CESmart-Wall","GPIO":[255,255,255,255,255,17,255,255,21,255,255,255,255],"FLAG":0,"BASE":18} +Kesen KS-604 {"NAME":"KS-604","GPIO":[255,255,52,255,255,18,255,255,22,21,255,255,17],"FLAG":0,"BASE":18} +Kesen KS-604S {"NAME":"KS-604S","GPIO":[255,255,31,255,255,18,255,255,22,21,255,255,17],"FLAG":1,"BASE":18} +MakeGood MG-AUWF01 {"NAME":"MG-AUWF01","GPIO":[56,10,157,59,134,132,255,255,131,22,57,21,9],"FLAG":0,"BASE":18} +MoesHouse Smart Socket {"NAME":"Smart Socket","GPIO":[255,255,255,255,52,53,255,255,21,17,255,255,255],"FLAG":0,"BASE":18} +Smanergy KA10 {"NAME":"KA10","GPIO":[0,56,0,17,134,132,0,0,131,53,21,0,0],"FLAG":0,"BASE":64} +Sonoff S55 {"NAME":"Sonoff S55","GPIO":[17,255,0,255,255,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":1} +Topgreener TGWF15RM {"NAME":"TGWF15RM","GPIO":[0,56,0,17,134,132,0,0,131,57,21,0,0],"FLAG":0,"BASE":55} +Vigica VGSPK00815 {"NAME":"VIGICA outlet","GPIO":[17,255,255,255,255,22,18,255,21,255,255,255,255],"FLAG":1,"BASE":18} +WWK-UN-W Glass Panel {"NAME":"tuya wall sock","GPIO":[255,255,255,255,52,53,255,255,21,17,255,255,255],"FLAG":0,"BASE":18} +``` \ No newline at end of file diff --git a/arduino/version pre-2.6.0/boards.txt b/arduino/version pre-2.6.0/boards.txt new file mode 100644 index 000000000..401ff23ea --- /dev/null +++ b/arduino/version pre-2.6.0/boards.txt @@ -0,0 +1,6448 @@ +# +# Do not create pull-requests for this file only, CI will not accept them. +# You *must* edit/modify/run boards.txt.py to regenerate boards.txt. +# All modified files after running with option "--allgen" must be included in the pull-request. +# + +menu.BoardModel=Model +menu.baud=Upload Speed + +menu.UploadTool=Upload Using + +menu.xtal=CPU Frequency +menu.CrystalFreq=Crystal Frequency +menu.eesz=Flash Size +menu.FlashMode=Flash Mode +menu.FlashFreq=Flash Frequency +menu.ResetMethod=Reset Method +menu.ESPModule=Module +menu.dbg=Debug port +menu.lvl=Debug Level +menu.ip=lwIP Variant +menu.vt=VTables +menu.exception=Exceptions +menu.led=Builtin Led +menu.wipe=Erase Flash +menu.sdk=Espressif FW +menu.ssl=SSL Support + +############################################################## +generic.name=Generic ESP8266 Module +generic.build.board=ESP8266_GENERIC +generic.upload.tool=esptool +generic.upload.maximum_data_size=81920 +generic.upload.wait_for_upload_port=true +generic.upload.erase_cmd=version +generic.serial.disableDTR=true +generic.serial.disableRTS=true +generic.build.mcu=esp8266 +generic.build.core=esp8266 +generic.build.variant=generic +generic.build.spiffs_pagesize=256 +generic.build.debug_port= +generic.build.debug_level= + +generic.menu.UploadTool.esptool=Serial +generic.menu.UploadTool.esptool.upload.tool=esptool +generic.menu.UploadTool.esptool.upload.verbose=-vv +generic.menu.UploadTool.espupload=OTA_upload +generic.menu.UploadTool.espupload.upload.tool=espupload + +generic.menu.xtal.80=80 MHz +generic.menu.xtal.80.build.f_cpu=80000000L +generic.menu.xtal.160=160 MHz +generic.menu.xtal.160.build.f_cpu=160000000L +generic.menu.vt.flash=Flash +generic.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +generic.menu.vt.heap=Heap +generic.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +generic.menu.vt.iram=IRAM +generic.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +generic.menu.exception.legacy=Legacy (new can return nullptr) +generic.menu.exception.legacy.build.exception_flags=-fno-exceptions +generic.menu.exception.legacy.build.stdcpp_lib=-lstdc++ +generic.menu.exception.disabled=Disabled (new can abort) +generic.menu.exception.disabled.build.exception_flags=-fno-exceptions -DNEW_OOM_ABORT +generic.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +generic.menu.exception.enabled=Enabled +generic.menu.exception.enabled.build.exception_flags=-fexceptions +generic.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +generic.menu.ssl.all=All SSL ciphers (most compatible) +generic.menu.ssl.all.build.sslflags= +generic.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +generic.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +generic.menu.ResetMethod.ck=ck +generic.menu.ResetMethod.ck.upload.resetmethod=ck +generic.menu.ResetMethod.nodemcu=nodemcu +generic.menu.ResetMethod.nodemcu.upload.resetmethod=nodemcu +generic.menu.ResetMethod.none=none +generic.menu.ResetMethod.none.upload.resetmethod=none +generic.menu.ResetMethod.dtrset=dtrset +generic.menu.ResetMethod.dtrset.upload.resetmethod=dtrset +generic.menu.CrystalFreq.26=26 MHz +generic.menu.CrystalFreq.40=40 MHz +generic.menu.CrystalFreq.40.build.extra_flags=-DF_CRYSTAL=40000000 -DESP8266 +generic.menu.FlashFreq.40=40MHz +generic.menu.FlashFreq.40.build.flash_freq=40 +generic.menu.FlashFreq.80=80MHz +generic.menu.FlashFreq.80.build.flash_freq=80 +generic.menu.FlashFreq.20=20MHz +generic.menu.FlashFreq.20.build.flash_freq=20 +generic.menu.FlashFreq.26=26MHz +generic.menu.FlashFreq.26.build.flash_freq=26 +generic.menu.FlashMode.dout=DOUT (compatible) +generic.menu.FlashMode.dout.build.flash_mode=dout +generic.menu.FlashMode.dout.build.flash_flags=-DFLASHMODE_DOUT +generic.menu.FlashMode.dio=DIO +generic.menu.FlashMode.dio.build.flash_mode=dio +generic.menu.FlashMode.dio.build.flash_flags=-DFLASHMODE_DIO +generic.menu.FlashMode.qout=QOUT +generic.menu.FlashMode.qout.build.flash_mode=qout +generic.menu.FlashMode.qout.build.flash_flags=-DFLASHMODE_QOUT +generic.menu.FlashMode.qio=QIO (fast) +generic.menu.FlashMode.qio.build.flash_mode=qio +generic.menu.FlashMode.qio.build.flash_flags=-DFLASHMODE_QIO +generic.menu.eesz.1M64=1MB (FS:64KB OTA:~470KB) +generic.menu.eesz.1M64.build.flash_size=1M +generic.menu.eesz.1M64.build.flash_size_bytes=0x100000 +generic.menu.eesz.1M64.build.flash_ld=eagle.flash.1m64.ld +generic.menu.eesz.1M64.build.spiffs_pagesize=256 +generic.menu.eesz.1M64.upload.maximum_size=958448 +generic.menu.eesz.1M64.build.rfcal_addr=0xFC000 +generic.menu.eesz.1M64.build.spiffs_start=0xEB000 +generic.menu.eesz.1M64.build.spiffs_end=0xFB000 +generic.menu.eesz.1M64.build.spiffs_blocksize=4096 +generic.menu.eesz.1M128=1MB (FS:128KB OTA:~438KB) +generic.menu.eesz.1M128.build.flash_size=1M +generic.menu.eesz.1M128.build.flash_size_bytes=0x100000 +generic.menu.eesz.1M128.build.flash_ld=eagle.flash.1m128.ld +generic.menu.eesz.1M128.build.spiffs_pagesize=256 +generic.menu.eesz.1M128.upload.maximum_size=892912 +generic.menu.eesz.1M128.build.rfcal_addr=0xFC000 +generic.menu.eesz.1M128.build.spiffs_start=0xDB000 +generic.menu.eesz.1M128.build.spiffs_end=0xFB000 +generic.menu.eesz.1M128.build.spiffs_blocksize=4096 +generic.menu.eesz.1M144=1MB (FS:144KB OTA:~430KB) +generic.menu.eesz.1M144.build.flash_size=1M +generic.menu.eesz.1M144.build.flash_size_bytes=0x100000 +generic.menu.eesz.1M144.build.flash_ld=eagle.flash.1m144.ld +generic.menu.eesz.1M144.build.spiffs_pagesize=256 +generic.menu.eesz.1M144.upload.maximum_size=876528 +generic.menu.eesz.1M144.build.rfcal_addr=0xFC000 +generic.menu.eesz.1M144.build.spiffs_start=0xD7000 +generic.menu.eesz.1M144.build.spiffs_end=0xFB000 +generic.menu.eesz.1M144.build.spiffs_blocksize=4096 +generic.menu.eesz.1M160=1MB (FS:160KB OTA:~422KB) +generic.menu.eesz.1M160.build.flash_size=1M +generic.menu.eesz.1M160.build.flash_size_bytes=0x100000 +generic.menu.eesz.1M160.build.flash_ld=eagle.flash.1m160.ld +generic.menu.eesz.1M160.build.spiffs_pagesize=256 +generic.menu.eesz.1M160.upload.maximum_size=860144 +generic.menu.eesz.1M160.build.rfcal_addr=0xFC000 +generic.menu.eesz.1M160.build.spiffs_start=0xD3000 +generic.menu.eesz.1M160.build.spiffs_end=0xFB000 +generic.menu.eesz.1M160.build.spiffs_blocksize=4096 +generic.menu.eesz.1M192=1MB (FS:192KB OTA:~406KB) +generic.menu.eesz.1M192.build.flash_size=1M +generic.menu.eesz.1M192.build.flash_size_bytes=0x100000 +generic.menu.eesz.1M192.build.flash_ld=eagle.flash.1m192.ld +generic.menu.eesz.1M192.build.spiffs_pagesize=256 +generic.menu.eesz.1M192.upload.maximum_size=827376 +generic.menu.eesz.1M192.build.rfcal_addr=0xFC000 +generic.menu.eesz.1M192.build.spiffs_start=0xCB000 +generic.menu.eesz.1M192.build.spiffs_end=0xFB000 +generic.menu.eesz.1M192.build.spiffs_blocksize=4096 +generic.menu.eesz.1M256=1MB (FS:256KB OTA:~374KB) +generic.menu.eesz.1M256.build.flash_size=1M +generic.menu.eesz.1M256.build.flash_size_bytes=0x100000 +generic.menu.eesz.1M256.build.flash_ld=eagle.flash.1m256.ld +generic.menu.eesz.1M256.build.spiffs_pagesize=256 +generic.menu.eesz.1M256.upload.maximum_size=761840 +generic.menu.eesz.1M256.build.rfcal_addr=0xFC000 +generic.menu.eesz.1M256.build.spiffs_start=0xBB000 +generic.menu.eesz.1M256.build.spiffs_end=0xFB000 +generic.menu.eesz.1M256.build.spiffs_blocksize=4096 +generic.menu.eesz.1M512=1MB (FS:512KB OTA:~246KB) +generic.menu.eesz.1M512.build.flash_size=1M +generic.menu.eesz.1M512.build.flash_size_bytes=0x100000 +generic.menu.eesz.1M512.build.flash_ld=eagle.flash.1m512.ld +generic.menu.eesz.1M512.build.spiffs_pagesize=256 +generic.menu.eesz.1M512.upload.maximum_size=499696 +generic.menu.eesz.1M512.build.rfcal_addr=0xFC000 +generic.menu.eesz.1M512.build.spiffs_start=0x7B000 +generic.menu.eesz.1M512.build.spiffs_end=0xFB000 +generic.menu.eesz.1M512.build.spiffs_blocksize=8192 +generic.menu.eesz.1M=1MB (FS:none OTA:~502KB) +generic.menu.eesz.1M.build.flash_size=1M +generic.menu.eesz.1M.build.flash_size_bytes=0x100000 +generic.menu.eesz.1M.build.flash_ld=eagle.flash.1m.ld +generic.menu.eesz.1M.build.spiffs_pagesize=256 +generic.menu.eesz.1M.upload.maximum_size=1023984 +generic.menu.eesz.1M.build.rfcal_addr=0xFC000 +generic.menu.eesz.2M64=2MB (FS:64KB OTA:~992KB) +generic.menu.eesz.2M64.build.flash_size=2M +generic.menu.eesz.2M64.build.flash_size_bytes=0x200000 +generic.menu.eesz.2M64.build.flash_ld=eagle.flash.2m64.ld +generic.menu.eesz.2M64.build.spiffs_pagesize=256 +generic.menu.eesz.2M64.upload.maximum_size=1044464 +generic.menu.eesz.2M64.build.rfcal_addr=0x1FC000 +generic.menu.eesz.2M64.build.spiffs_start=0x1F0000 +generic.menu.eesz.2M64.build.spiffs_end=0x1FB000 +generic.menu.eesz.2M64.build.spiffs_blocksize=4096 +generic.menu.eesz.2M128=2MB (FS:128KB OTA:~960KB) +generic.menu.eesz.2M128.build.flash_size=2M +generic.menu.eesz.2M128.build.flash_size_bytes=0x200000 +generic.menu.eesz.2M128.build.flash_ld=eagle.flash.2m128.ld +generic.menu.eesz.2M128.build.spiffs_pagesize=256 +generic.menu.eesz.2M128.upload.maximum_size=1044464 +generic.menu.eesz.2M128.build.rfcal_addr=0x1FC000 +generic.menu.eesz.2M128.build.spiffs_start=0x1E0000 +generic.menu.eesz.2M128.build.spiffs_end=0x1FB000 +generic.menu.eesz.2M128.build.spiffs_blocksize=4096 +generic.menu.eesz.2M256=2MB (FS:256KB OTA:~896KB) +generic.menu.eesz.2M256.build.flash_size=2M +generic.menu.eesz.2M256.build.flash_size_bytes=0x200000 +generic.menu.eesz.2M256.build.flash_ld=eagle.flash.2m256.ld +generic.menu.eesz.2M256.build.spiffs_pagesize=256 +generic.menu.eesz.2M256.upload.maximum_size=1044464 +generic.menu.eesz.2M256.build.rfcal_addr=0x1FC000 +generic.menu.eesz.2M256.build.spiffs_start=0x1C0000 +generic.menu.eesz.2M256.build.spiffs_end=0x1FB000 +generic.menu.eesz.2M256.build.spiffs_blocksize=4096 +generic.menu.eesz.2M512=2MB (FS:512KB OTA:~768KB) +generic.menu.eesz.2M512.build.flash_size=2M +generic.menu.eesz.2M512.build.flash_size_bytes=0x200000 +generic.menu.eesz.2M512.build.flash_ld=eagle.flash.2m512.ld +generic.menu.eesz.2M512.build.spiffs_pagesize=256 +generic.menu.eesz.2M512.upload.maximum_size=1044464 +generic.menu.eesz.2M512.build.rfcal_addr=0x1FC000 +generic.menu.eesz.2M512.build.spiffs_start=0x180000 +generic.menu.eesz.2M512.build.spiffs_end=0x1FA000 +generic.menu.eesz.2M512.build.spiffs_blocksize=8192 +generic.menu.eesz.2M1M=2MB (FS:1MB OTA:~512KB) +generic.menu.eesz.2M1M.build.flash_size=2M +generic.menu.eesz.2M1M.build.flash_size_bytes=0x200000 +generic.menu.eesz.2M1M.build.flash_ld=eagle.flash.2m1m.ld +generic.menu.eesz.2M1M.build.spiffs_pagesize=256 +generic.menu.eesz.2M1M.upload.maximum_size=1044464 +generic.menu.eesz.2M1M.build.rfcal_addr=0x1FC000 +generic.menu.eesz.2M1M.build.spiffs_start=0x100000 +generic.menu.eesz.2M1M.build.spiffs_end=0x1FA000 +generic.menu.eesz.2M1M.build.spiffs_blocksize=8192 +generic.menu.eesz.2M=2MB (FS:none OTA:~1019KB) +generic.menu.eesz.2M.build.flash_size=2M +generic.menu.eesz.2M.build.flash_size_bytes=0x200000 +generic.menu.eesz.2M.build.flash_ld=eagle.flash.2m.ld +generic.menu.eesz.2M.build.spiffs_pagesize=256 +generic.menu.eesz.2M.upload.maximum_size=1044464 +generic.menu.eesz.2M.build.rfcal_addr=0x1FC000 +generic.menu.eesz.4M2M=4MB (FS:2MB OTA:~1019KB) +generic.menu.eesz.4M2M.build.flash_size=4M +generic.menu.eesz.4M2M.build.flash_size_bytes=0x400000 +generic.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld +generic.menu.eesz.4M2M.build.spiffs_pagesize=256 +generic.menu.eesz.4M2M.upload.maximum_size=1044464 +generic.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 +generic.menu.eesz.4M2M.build.spiffs_start=0x200000 +generic.menu.eesz.4M2M.build.spiffs_end=0x3FA000 +generic.menu.eesz.4M2M.build.spiffs_blocksize=8192 +generic.menu.eesz.4M3M=4MB (FS:3MB OTA:~512KB) +generic.menu.eesz.4M3M.build.flash_size=4M +generic.menu.eesz.4M3M.build.flash_size_bytes=0x400000 +generic.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld +generic.menu.eesz.4M3M.build.spiffs_pagesize=256 +generic.menu.eesz.4M3M.upload.maximum_size=1044464 +generic.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 +generic.menu.eesz.4M3M.build.spiffs_start=0x100000 +generic.menu.eesz.4M3M.build.spiffs_end=0x3FA000 +generic.menu.eesz.4M3M.build.spiffs_blocksize=8192 +generic.menu.eesz.4M1M=4MB (FS:1MB OTA:~1019KB) +generic.menu.eesz.4M1M.build.flash_size=4M +generic.menu.eesz.4M1M.build.flash_size_bytes=0x400000 +generic.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld +generic.menu.eesz.4M1M.build.spiffs_pagesize=256 +generic.menu.eesz.4M1M.upload.maximum_size=1044464 +generic.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 +generic.menu.eesz.4M1M.build.spiffs_start=0x300000 +generic.menu.eesz.4M1M.build.spiffs_end=0x3FA000 +generic.menu.eesz.4M1M.build.spiffs_blocksize=8192 +generic.menu.eesz.4M=4MB (FS:none OTA:~1019KB) +generic.menu.eesz.4M.build.flash_size=4M +generic.menu.eesz.4M.build.flash_size_bytes=0x400000 +generic.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld +generic.menu.eesz.4M.build.spiffs_pagesize=256 +generic.menu.eesz.4M.upload.maximum_size=1044464 +generic.menu.eesz.4M.build.rfcal_addr=0x3FC000 +generic.menu.eesz.8M6M=8MB (FS:6MB OTA:~1019KB) +generic.menu.eesz.8M6M.build.flash_size=8M +generic.menu.eesz.8M6M.build.flash_size_bytes=0x800000 +generic.menu.eesz.8M6M.build.flash_ld=eagle.flash.8m6m.ld +generic.menu.eesz.8M6M.build.spiffs_pagesize=256 +generic.menu.eesz.8M6M.upload.maximum_size=1044464 +generic.menu.eesz.8M6M.build.rfcal_addr=0x7FC000 +generic.menu.eesz.8M6M.build.spiffs_start=0x200000 +generic.menu.eesz.8M6M.build.spiffs_end=0x7FA000 +generic.menu.eesz.8M6M.build.spiffs_blocksize=8192 +generic.menu.eesz.8M7M=8MB (FS:7MB OTA:~512KB) +generic.menu.eesz.8M7M.build.flash_size=8M +generic.menu.eesz.8M7M.build.flash_size_bytes=0x800000 +generic.menu.eesz.8M7M.build.flash_ld=eagle.flash.8m7m.ld +generic.menu.eesz.8M7M.build.spiffs_pagesize=256 +generic.menu.eesz.8M7M.upload.maximum_size=1044464 +generic.menu.eesz.8M7M.build.rfcal_addr=0x7FC000 +generic.menu.eesz.8M7M.build.spiffs_start=0x100000 +generic.menu.eesz.8M7M.build.spiffs_end=0x7FA000 +generic.menu.eesz.8M7M.build.spiffs_blocksize=8192 +generic.menu.eesz.16M14M=16MB (FS:14MB OTA:~1019KB) +generic.menu.eesz.16M14M.build.flash_size=16M +generic.menu.eesz.16M14M.build.flash_size_bytes=0x1000000 +generic.menu.eesz.16M14M.build.flash_ld=eagle.flash.16m14m.ld +generic.menu.eesz.16M14M.build.spiffs_pagesize=256 +generic.menu.eesz.16M14M.upload.maximum_size=1044464 +generic.menu.eesz.16M14M.build.rfcal_addr=0xFFC000 +generic.menu.eesz.16M14M.build.spiffs_start=0x200000 +generic.menu.eesz.16M14M.build.spiffs_end=0xFFA000 +generic.menu.eesz.16M14M.build.spiffs_blocksize=8192 +generic.menu.eesz.16M15M=16MB (FS:15MB OTA:~512KB) +generic.menu.eesz.16M15M.build.flash_size=16M +generic.menu.eesz.16M15M.build.flash_size_bytes=0x1000000 +generic.menu.eesz.16M15M.build.flash_ld=eagle.flash.16m15m.ld +generic.menu.eesz.16M15M.build.spiffs_pagesize=256 +generic.menu.eesz.16M15M.upload.maximum_size=1044464 +generic.menu.eesz.16M15M.build.rfcal_addr=0xFFC000 +generic.menu.eesz.16M15M.build.spiffs_start=0x100000 +generic.menu.eesz.16M15M.build.spiffs_end=0xFFA000 +generic.menu.eesz.16M15M.build.spiffs_blocksize=8192 +generic.menu.eesz.512K32=512KB (FS:32KB OTA:~230KB) +generic.menu.eesz.512K32.build.flash_size=512K +generic.menu.eesz.512K32.build.flash_size_bytes=0x80000 +generic.menu.eesz.512K32.build.flash_ld=eagle.flash.512k32.ld +generic.menu.eesz.512K32.build.spiffs_pagesize=256 +generic.menu.eesz.512K32.upload.maximum_size=466928 +generic.menu.eesz.512K32.build.rfcal_addr=0x7C000 +generic.menu.eesz.512K32.build.spiffs_start=0x73000 +generic.menu.eesz.512K32.build.spiffs_end=0x7B000 +generic.menu.eesz.512K32.build.spiffs_blocksize=4096 +generic.menu.eesz.512K64=512KB (FS:64KB OTA:~214KB) +generic.menu.eesz.512K64.build.flash_size=512K +generic.menu.eesz.512K64.build.flash_size_bytes=0x80000 +generic.menu.eesz.512K64.build.flash_ld=eagle.flash.512k64.ld +generic.menu.eesz.512K64.build.spiffs_pagesize=256 +generic.menu.eesz.512K64.upload.maximum_size=434160 +generic.menu.eesz.512K64.build.rfcal_addr=0x7C000 +generic.menu.eesz.512K64.build.spiffs_start=0x6B000 +generic.menu.eesz.512K64.build.spiffs_end=0x7B000 +generic.menu.eesz.512K64.build.spiffs_blocksize=4096 +generic.menu.eesz.512K128=512KB (FS:128KB OTA:~182KB) +generic.menu.eesz.512K128.build.flash_size=512K +generic.menu.eesz.512K128.build.flash_size_bytes=0x80000 +generic.menu.eesz.512K128.build.flash_ld=eagle.flash.512k128.ld +generic.menu.eesz.512K128.build.spiffs_pagesize=256 +generic.menu.eesz.512K128.upload.maximum_size=368624 +generic.menu.eesz.512K128.build.rfcal_addr=0x7C000 +generic.menu.eesz.512K128.build.spiffs_start=0x5B000 +generic.menu.eesz.512K128.build.spiffs_end=0x7B000 +generic.menu.eesz.512K128.build.spiffs_blocksize=4096 +generic.menu.eesz.512K=512KB (FS:none OTA:~246KB) +generic.menu.eesz.512K.build.flash_size=512K +generic.menu.eesz.512K.build.flash_size_bytes=0x80000 +generic.menu.eesz.512K.build.flash_ld=eagle.flash.512k.ld +generic.menu.eesz.512K.build.spiffs_pagesize=256 +generic.menu.eesz.512K.upload.maximum_size=499696 +generic.menu.eesz.512K.build.rfcal_addr=0x7C000 +generic.menu.led.2=2 +generic.menu.led.2.build.led=-DLED_BUILTIN=2 +generic.menu.led.0=0 +generic.menu.led.0.build.led=-DLED_BUILTIN=0 +generic.menu.led.1=1 +generic.menu.led.1.build.led=-DLED_BUILTIN=1 +generic.menu.led.3=3 +generic.menu.led.3.build.led=-DLED_BUILTIN=3 +generic.menu.led.4=4 +generic.menu.led.4.build.led=-DLED_BUILTIN=4 +generic.menu.led.5=5 +generic.menu.led.5.build.led=-DLED_BUILTIN=5 +generic.menu.led.6=6 +generic.menu.led.6.build.led=-DLED_BUILTIN=6 +generic.menu.led.7=7 +generic.menu.led.7.build.led=-DLED_BUILTIN=7 +generic.menu.led.8=8 +generic.menu.led.8.build.led=-DLED_BUILTIN=8 +generic.menu.led.9=9 +generic.menu.led.9.build.led=-DLED_BUILTIN=9 +generic.menu.led.10=10 +generic.menu.led.10.build.led=-DLED_BUILTIN=10 +generic.menu.led.11=11 +generic.menu.led.11.build.led=-DLED_BUILTIN=11 +generic.menu.led.12=12 +generic.menu.led.12.build.led=-DLED_BUILTIN=12 +generic.menu.led.13=13 +generic.menu.led.13.build.led=-DLED_BUILTIN=13 +generic.menu.led.14=14 +generic.menu.led.14.build.led=-DLED_BUILTIN=14 +generic.menu.led.15=15 +generic.menu.led.15.build.led=-DLED_BUILTIN=15 +generic.menu.led.16=16 +generic.menu.led.16.build.led=-DLED_BUILTIN=16 +generic.menu.sdk.nonosdk222_100=nonos-sdk 2.2.1+100 (testing) +generic.menu.sdk.nonosdk222_100.build.sdk=NONOSDK22y +generic.menu.sdk.nonosdk221=nonos-sdk 2.2.1 (legacy) +generic.menu.sdk.nonosdk221.build.sdk=NONOSDK221 +generic.menu.sdk.nonosdk3v0=nonos-sdk pre-3 (known issues) +generic.menu.sdk.nonosdk3v0.build.sdk=NONOSDK3V0 +generic.menu.ip.lm2f=v2 Lower Memory +generic.menu.ip.lm2f.build.lwip_include=lwip2/include +generic.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +generic.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +generic.menu.ip.hb2f=v2 Higher Bandwidth +generic.menu.ip.hb2f.build.lwip_include=lwip2/include +generic.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +generic.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +generic.menu.ip.lm2n=v2 Lower Memory (no features) +generic.menu.ip.lm2n.build.lwip_include=lwip2/include +generic.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +generic.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +generic.menu.ip.hb2n=v2 Higher Bandwidth (no features) +generic.menu.ip.hb2n.build.lwip_include=lwip2/include +generic.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +generic.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +generic.menu.ip.lm6f=v2 IPv6 Lower Memory +generic.menu.ip.lm6f.build.lwip_include=lwip2/include +generic.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +generic.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +generic.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +generic.menu.ip.hb6f.build.lwip_include=lwip2/include +generic.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +generic.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +generic.menu.ip.hb1=v1.4 Higher Bandwidth +generic.menu.ip.hb1.build.lwip_lib=-llwip_gcc +generic.menu.ip.hb1.build.lwip_flags=-DLWIP_OPEN_SRC +generic.menu.ip.src=v1.4 Compile from source +generic.menu.ip.src.build.lwip_lib=-llwip_src +generic.menu.ip.src.build.lwip_flags=-DLWIP_OPEN_SRC +generic.menu.ip.src.recipe.hooks.sketch.prebuild.1.pattern=make -C "{runtime.platform.path}/tools/sdk/lwip/src" install TOOLS_PATH="{runtime.tools.xtensa-lx106-elf-gcc.path}/bin/xtensa-lx106-elf-" +generic.menu.dbg.Disabled=Disabled +generic.menu.dbg.Disabled.build.debug_port= +generic.menu.dbg.Serial=Serial +generic.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +generic.menu.dbg.Serial1=Serial1 +generic.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +generic.menu.lvl.None____=None +generic.menu.lvl.None____.build.debug_level= +generic.menu.lvl.SSL=SSL +generic.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +generic.menu.lvl.TLS_MEM=TLS_MEM +generic.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +generic.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +generic.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +generic.menu.lvl.HTTP_SERVER=HTTP_SERVER +generic.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +generic.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +generic.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +generic.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +generic.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +generic.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +generic.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +generic.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +generic.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +generic.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +generic.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +generic.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +generic.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +generic.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +generic.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +generic.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +generic.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +generic.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +generic.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +generic.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +generic.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +generic.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +generic.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +generic.menu.lvl.CORE=CORE +generic.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +generic.menu.lvl.WIFI=WIFI +generic.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +generic.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +generic.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +generic.menu.lvl.UPDATER=UPDATER +generic.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +generic.menu.lvl.OTA=OTA +generic.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +generic.menu.lvl.OOM=OOM +generic.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +generic.menu.lvl.MDNS=MDNS +generic.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +generic.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +generic.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS +generic.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +generic.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS +generic.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +generic.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +generic.menu.wipe.none=Only Sketch +generic.menu.wipe.none.upload.erase_cmd=version +generic.menu.wipe.sdk=Sketch + WiFi Settings +generic.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 +generic.menu.wipe.all=All Flash Contents +generic.menu.wipe.all.upload.erase_cmd=erase_flash +generic.menu.baud.115200=115200 +generic.menu.baud.115200.upload.speed=115200 +generic.menu.baud.57600=57600 +generic.menu.baud.57600.upload.speed=57600 +generic.menu.baud.230400.linux=230400 +generic.menu.baud.230400.macosx=230400 +generic.menu.baud.230400.upload.speed=230400 +generic.menu.baud.256000.windows=256000 +generic.menu.baud.256000.upload.speed=256000 +generic.menu.baud.460800.linux=460800 +generic.menu.baud.460800.macosx=460800 +generic.menu.baud.460800.upload.speed=460800 +generic.menu.baud.512000.windows=512000 +generic.menu.baud.512000.upload.speed=512000 +generic.menu.baud.921600=921600 +generic.menu.baud.921600.upload.speed=921600 +generic.menu.baud.3000000=3000000 +generic.menu.baud.3000000.upload.speed=3000000 + +############################################################## +esp8285.name=Generic ESP8285 Module +esp8285.build.board=ESP8266_ESP01 +esp8285.build.variant=esp8285 +esp8285.upload.tool=esptool +esp8285.upload.maximum_data_size=81920 +esp8285.upload.wait_for_upload_port=true +esp8285.upload.erase_cmd=version +esp8285.serial.disableDTR=true +esp8285.serial.disableRTS=true +esp8285.build.mcu=esp8266 +esp8285.build.core=esp8266 +esp8285.build.spiffs_pagesize=256 +esp8285.build.debug_port= +esp8285.build.debug_level= +esp8285.menu.xtal.80=80 MHz +esp8285.menu.xtal.80.build.f_cpu=80000000L +esp8285.menu.xtal.160=160 MHz +esp8285.menu.xtal.160.build.f_cpu=160000000L +esp8285.menu.vt.flash=Flash +esp8285.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +esp8285.menu.vt.heap=Heap +esp8285.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +esp8285.menu.vt.iram=IRAM +esp8285.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +esp8285.menu.exception.legacy=Legacy (new can return nullptr) +esp8285.menu.exception.legacy.build.exception_flags=-fno-exceptions +esp8285.menu.exception.legacy.build.stdcpp_lib=-lstdc++ +esp8285.menu.exception.disabled=Disabled (new can abort) +esp8285.menu.exception.disabled.build.exception_flags=-fno-exceptions -DNEW_OOM_ABORT +esp8285.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +esp8285.menu.exception.enabled=Enabled +esp8285.menu.exception.enabled.build.exception_flags=-fexceptions +esp8285.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +esp8285.menu.ssl.all=All SSL ciphers (most compatible) +esp8285.menu.ssl.all.build.sslflags= +esp8285.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +esp8285.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +esp8285.menu.ResetMethod.ck=ck +esp8285.menu.ResetMethod.ck.upload.resetmethod=ck +esp8285.menu.ResetMethod.nodemcu=nodemcu +esp8285.menu.ResetMethod.nodemcu.upload.resetmethod=nodemcu +esp8285.menu.ResetMethod.none=none +esp8285.menu.ResetMethod.none.upload.resetmethod=none +esp8285.menu.ResetMethod.dtrset=dtrset +esp8285.menu.ResetMethod.dtrset.upload.resetmethod=dtrset +esp8285.menu.CrystalFreq.26=26 MHz +esp8285.menu.CrystalFreq.40=40 MHz +esp8285.menu.CrystalFreq.40.build.extra_flags=-DF_CRYSTAL=40000000 -DESP8266 +esp8285.build.flash_mode=dout +esp8285.build.flash_flags=-DFLASHMODE_DOUT +esp8285.build.flash_freq=40 +esp8285.menu.eesz.1M64=1MB (FS:64KB OTA:~470KB) +esp8285.menu.eesz.1M64.build.flash_size=1M +esp8285.menu.eesz.1M64.build.flash_size_bytes=0x100000 +esp8285.menu.eesz.1M64.build.flash_ld=eagle.flash.1m64.ld +esp8285.menu.eesz.1M64.build.spiffs_pagesize=256 +esp8285.menu.eesz.1M64.upload.maximum_size=958448 +esp8285.menu.eesz.1M64.build.rfcal_addr=0xFC000 +esp8285.menu.eesz.1M64.build.spiffs_start=0xEB000 +esp8285.menu.eesz.1M64.build.spiffs_end=0xFB000 +esp8285.menu.eesz.1M64.build.spiffs_blocksize=4096 +esp8285.menu.eesz.1M128=1MB (FS:128KB OTA:~438KB) +esp8285.menu.eesz.1M128.build.flash_size=1M +esp8285.menu.eesz.1M128.build.flash_size_bytes=0x100000 +esp8285.menu.eesz.1M128.build.flash_ld=eagle.flash.1m128.ld +esp8285.menu.eesz.1M128.build.spiffs_pagesize=256 +esp8285.menu.eesz.1M128.upload.maximum_size=892912 +esp8285.menu.eesz.1M128.build.rfcal_addr=0xFC000 +esp8285.menu.eesz.1M128.build.spiffs_start=0xDB000 +esp8285.menu.eesz.1M128.build.spiffs_end=0xFB000 +esp8285.menu.eesz.1M128.build.spiffs_blocksize=4096 +esp8285.menu.eesz.1M144=1MB (FS:144KB OTA:~430KB) +esp8285.menu.eesz.1M144.build.flash_size=1M +esp8285.menu.eesz.1M144.build.flash_size_bytes=0x100000 +esp8285.menu.eesz.1M144.build.flash_ld=eagle.flash.1m144.ld +esp8285.menu.eesz.1M144.build.spiffs_pagesize=256 +esp8285.menu.eesz.1M144.upload.maximum_size=876528 +esp8285.menu.eesz.1M144.build.rfcal_addr=0xFC000 +esp8285.menu.eesz.1M144.build.spiffs_start=0xD7000 +esp8285.menu.eesz.1M144.build.spiffs_end=0xFB000 +esp8285.menu.eesz.1M144.build.spiffs_blocksize=4096 +esp8285.menu.eesz.1M160=1MB (FS:160KB OTA:~422KB) +esp8285.menu.eesz.1M160.build.flash_size=1M +esp8285.menu.eesz.1M160.build.flash_size_bytes=0x100000 +esp8285.menu.eesz.1M160.build.flash_ld=eagle.flash.1m160.ld +esp8285.menu.eesz.1M160.build.spiffs_pagesize=256 +esp8285.menu.eesz.1M160.upload.maximum_size=860144 +esp8285.menu.eesz.1M160.build.rfcal_addr=0xFC000 +esp8285.menu.eesz.1M160.build.spiffs_start=0xD3000 +esp8285.menu.eesz.1M160.build.spiffs_end=0xFB000 +esp8285.menu.eesz.1M160.build.spiffs_blocksize=4096 +esp8285.menu.eesz.1M192=1MB (FS:192KB OTA:~406KB) +esp8285.menu.eesz.1M192.build.flash_size=1M +esp8285.menu.eesz.1M192.build.flash_size_bytes=0x100000 +esp8285.menu.eesz.1M192.build.flash_ld=eagle.flash.1m192.ld +esp8285.menu.eesz.1M192.build.spiffs_pagesize=256 +esp8285.menu.eesz.1M192.upload.maximum_size=827376 +esp8285.menu.eesz.1M192.build.rfcal_addr=0xFC000 +esp8285.menu.eesz.1M192.build.spiffs_start=0xCB000 +esp8285.menu.eesz.1M192.build.spiffs_end=0xFB000 +esp8285.menu.eesz.1M192.build.spiffs_blocksize=4096 +esp8285.menu.eesz.1M256=1MB (FS:256KB OTA:~374KB) +esp8285.menu.eesz.1M256.build.flash_size=1M +esp8285.menu.eesz.1M256.build.flash_size_bytes=0x100000 +esp8285.menu.eesz.1M256.build.flash_ld=eagle.flash.1m256.ld +esp8285.menu.eesz.1M256.build.spiffs_pagesize=256 +esp8285.menu.eesz.1M256.upload.maximum_size=761840 +esp8285.menu.eesz.1M256.build.rfcal_addr=0xFC000 +esp8285.menu.eesz.1M256.build.spiffs_start=0xBB000 +esp8285.menu.eesz.1M256.build.spiffs_end=0xFB000 +esp8285.menu.eesz.1M256.build.spiffs_blocksize=4096 +esp8285.menu.eesz.1M512=1MB (FS:512KB OTA:~246KB) +esp8285.menu.eesz.1M512.build.flash_size=1M +esp8285.menu.eesz.1M512.build.flash_size_bytes=0x100000 +esp8285.menu.eesz.1M512.build.flash_ld=eagle.flash.1m512.ld +esp8285.menu.eesz.1M512.build.spiffs_pagesize=256 +esp8285.menu.eesz.1M512.upload.maximum_size=499696 +esp8285.menu.eesz.1M512.build.rfcal_addr=0xFC000 +esp8285.menu.eesz.1M512.build.spiffs_start=0x7B000 +esp8285.menu.eesz.1M512.build.spiffs_end=0xFB000 +esp8285.menu.eesz.1M512.build.spiffs_blocksize=8192 +esp8285.menu.eesz.1M=1MB (FS:none OTA:~502KB) +esp8285.menu.eesz.1M.build.flash_size=1M +esp8285.menu.eesz.1M.build.flash_size_bytes=0x100000 +esp8285.menu.eesz.1M.build.flash_ld=eagle.flash.1m.ld +esp8285.menu.eesz.1M.build.spiffs_pagesize=256 +esp8285.menu.eesz.1M.upload.maximum_size=1023984 +esp8285.menu.eesz.1M.build.rfcal_addr=0xFC000 +esp8285.menu.led.2=2 +esp8285.menu.led.2.build.led=-DLED_BUILTIN=2 +esp8285.menu.led.0=0 +esp8285.menu.led.0.build.led=-DLED_BUILTIN=0 +esp8285.menu.led.1=1 +esp8285.menu.led.1.build.led=-DLED_BUILTIN=1 +esp8285.menu.led.3=3 +esp8285.menu.led.3.build.led=-DLED_BUILTIN=3 +esp8285.menu.led.4=4 +esp8285.menu.led.4.build.led=-DLED_BUILTIN=4 +esp8285.menu.led.5=5 +esp8285.menu.led.5.build.led=-DLED_BUILTIN=5 +esp8285.menu.led.6=6 +esp8285.menu.led.6.build.led=-DLED_BUILTIN=6 +esp8285.menu.led.7=7 +esp8285.menu.led.7.build.led=-DLED_BUILTIN=7 +esp8285.menu.led.8=8 +esp8285.menu.led.8.build.led=-DLED_BUILTIN=8 +esp8285.menu.led.9=9 +esp8285.menu.led.9.build.led=-DLED_BUILTIN=9 +esp8285.menu.led.10=10 +esp8285.menu.led.10.build.led=-DLED_BUILTIN=10 +esp8285.menu.led.11=11 +esp8285.menu.led.11.build.led=-DLED_BUILTIN=11 +esp8285.menu.led.12=12 +esp8285.menu.led.12.build.led=-DLED_BUILTIN=12 +esp8285.menu.led.13=13 +esp8285.menu.led.13.build.led=-DLED_BUILTIN=13 +esp8285.menu.led.14=14 +esp8285.menu.led.14.build.led=-DLED_BUILTIN=14 +esp8285.menu.led.15=15 +esp8285.menu.led.15.build.led=-DLED_BUILTIN=15 +esp8285.menu.led.16=16 +esp8285.menu.led.16.build.led=-DLED_BUILTIN=16 +esp8285.menu.ip.lm2f=v2 Lower Memory +esp8285.menu.ip.lm2f.build.lwip_include=lwip2/include +esp8285.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +esp8285.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +esp8285.menu.ip.hb2f=v2 Higher Bandwidth +esp8285.menu.ip.hb2f.build.lwip_include=lwip2/include +esp8285.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +esp8285.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +esp8285.menu.ip.lm2n=v2 Lower Memory (no features) +esp8285.menu.ip.lm2n.build.lwip_include=lwip2/include +esp8285.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +esp8285.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +esp8285.menu.ip.hb2n=v2 Higher Bandwidth (no features) +esp8285.menu.ip.hb2n.build.lwip_include=lwip2/include +esp8285.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +esp8285.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +esp8285.menu.ip.lm6f=v2 IPv6 Lower Memory +esp8285.menu.ip.lm6f.build.lwip_include=lwip2/include +esp8285.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +esp8285.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +esp8285.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +esp8285.menu.ip.hb6f.build.lwip_include=lwip2/include +esp8285.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +esp8285.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +esp8285.menu.ip.hb1=v1.4 Higher Bandwidth +esp8285.menu.ip.hb1.build.lwip_lib=-llwip_gcc +esp8285.menu.ip.hb1.build.lwip_flags=-DLWIP_OPEN_SRC +esp8285.menu.ip.src=v1.4 Compile from source +esp8285.menu.ip.src.build.lwip_lib=-llwip_src +esp8285.menu.ip.src.build.lwip_flags=-DLWIP_OPEN_SRC +esp8285.menu.ip.src.recipe.hooks.sketch.prebuild.1.pattern=make -C "{runtime.platform.path}/tools/sdk/lwip/src" install TOOLS_PATH="{runtime.tools.xtensa-lx106-elf-gcc.path}/bin/xtensa-lx106-elf-" +esp8285.menu.dbg.Disabled=Disabled +esp8285.menu.dbg.Disabled.build.debug_port= +esp8285.menu.dbg.Serial=Serial +esp8285.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +esp8285.menu.dbg.Serial1=Serial1 +esp8285.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +esp8285.menu.lvl.None____=None +esp8285.menu.lvl.None____.build.debug_level= +esp8285.menu.lvl.SSL=SSL +esp8285.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +esp8285.menu.lvl.TLS_MEM=TLS_MEM +esp8285.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +esp8285.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +esp8285.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +esp8285.menu.lvl.HTTP_SERVER=HTTP_SERVER +esp8285.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +esp8285.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +esp8285.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +esp8285.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +esp8285.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +esp8285.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +esp8285.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +esp8285.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +esp8285.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +esp8285.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +esp8285.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +esp8285.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +esp8285.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +esp8285.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +esp8285.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +esp8285.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +esp8285.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +esp8285.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +esp8285.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +esp8285.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +esp8285.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +esp8285.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +esp8285.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +esp8285.menu.lvl.CORE=CORE +esp8285.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +esp8285.menu.lvl.WIFI=WIFI +esp8285.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +esp8285.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +esp8285.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +esp8285.menu.lvl.UPDATER=UPDATER +esp8285.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +esp8285.menu.lvl.OTA=OTA +esp8285.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +esp8285.menu.lvl.OOM=OOM +esp8285.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +esp8285.menu.lvl.MDNS=MDNS +esp8285.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +esp8285.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +esp8285.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS +esp8285.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +esp8285.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS +esp8285.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +esp8285.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +esp8285.menu.wipe.none=Only Sketch +esp8285.menu.wipe.none.upload.erase_cmd=version +esp8285.menu.wipe.sdk=Sketch + WiFi Settings +esp8285.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 +esp8285.menu.wipe.all=All Flash Contents +esp8285.menu.wipe.all.upload.erase_cmd=erase_flash +esp8285.menu.baud.115200=115200 +esp8285.menu.baud.115200.upload.speed=115200 +esp8285.menu.baud.57600=57600 +esp8285.menu.baud.57600.upload.speed=57600 +esp8285.menu.baud.230400.linux=230400 +esp8285.menu.baud.230400.macosx=230400 +esp8285.menu.baud.230400.upload.speed=230400 +esp8285.menu.baud.256000.windows=256000 +esp8285.menu.baud.256000.upload.speed=256000 +esp8285.menu.baud.460800.linux=460800 +esp8285.menu.baud.460800.macosx=460800 +esp8285.menu.baud.460800.upload.speed=460800 +esp8285.menu.baud.512000.windows=512000 +esp8285.menu.baud.512000.upload.speed=512000 +esp8285.menu.baud.921600=921600 +esp8285.menu.baud.921600.upload.speed=921600 +esp8285.menu.baud.3000000=3000000 +esp8285.menu.baud.3000000.upload.speed=3000000 + +############################################################## +espduino.name=ESPDuino (ESP-13 Module) +espduino.build.board=ESP8266_ESP13 +espduino.build.variant=ESPDuino +espduino.menu.ResetMethod.v1=ESPduino-V1 +espduino.menu.ResetMethod.v1.upload.resetmethod=ck +espduino.menu.ResetMethod.v2=ESPduino-V2 +espduino.menu.ResetMethod.v2.upload.resetmethod=nodemcu +espduino.menu.UploadTool.espota=OTA +espduino.menu.UploadTool.espota.upload.tool=espota +espduino.menu.UploadTool.esptool=Serial +espduino.menu.UploadTool.esptool.upload.tool=esptool +espduino.menu.UploadTool.esptool.upload.verbose=--trace +espduino.upload.tool=esptool +espduino.upload.maximum_data_size=81920 +espduino.upload.wait_for_upload_port=true +espduino.upload.erase_cmd=version +espduino.serial.disableDTR=true +espduino.serial.disableRTS=true +espduino.build.mcu=esp8266 +espduino.build.core=esp8266 +espduino.build.spiffs_pagesize=256 +espduino.build.debug_port= +espduino.build.debug_level= +espduino.menu.xtal.80=80 MHz +espduino.menu.xtal.80.build.f_cpu=80000000L +espduino.menu.xtal.160=160 MHz +espduino.menu.xtal.160.build.f_cpu=160000000L +espduino.menu.vt.flash=Flash +espduino.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +espduino.menu.vt.heap=Heap +espduino.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +espduino.menu.vt.iram=IRAM +espduino.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +espduino.menu.exception.legacy=Legacy (new can return nullptr) +espduino.menu.exception.legacy.build.exception_flags=-fno-exceptions +espduino.menu.exception.legacy.build.stdcpp_lib=-lstdc++ +espduino.menu.exception.disabled=Disabled (new can abort) +espduino.menu.exception.disabled.build.exception_flags=-fno-exceptions -DNEW_OOM_ABORT +espduino.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +espduino.menu.exception.enabled=Enabled +espduino.menu.exception.enabled.build.exception_flags=-fexceptions +espduino.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +espduino.menu.ssl.all=All SSL ciphers (most compatible) +espduino.menu.ssl.all.build.sslflags= +espduino.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +espduino.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +espduino.build.flash_mode=dio +espduino.build.flash_flags=-DFLASHMODE_DIO +espduino.build.flash_freq=40 +espduino.menu.eesz.4M2M=4MB (FS:2MB OTA:~1019KB) +espduino.menu.eesz.4M2M.build.flash_size=4M +espduino.menu.eesz.4M2M.build.flash_size_bytes=0x400000 +espduino.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld +espduino.menu.eesz.4M2M.build.spiffs_pagesize=256 +espduino.menu.eesz.4M2M.upload.maximum_size=1044464 +espduino.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 +espduino.menu.eesz.4M2M.build.spiffs_start=0x200000 +espduino.menu.eesz.4M2M.build.spiffs_end=0x3FA000 +espduino.menu.eesz.4M2M.build.spiffs_blocksize=8192 +espduino.menu.eesz.4M3M=4MB (FS:3MB OTA:~512KB) +espduino.menu.eesz.4M3M.build.flash_size=4M +espduino.menu.eesz.4M3M.build.flash_size_bytes=0x400000 +espduino.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld +espduino.menu.eesz.4M3M.build.spiffs_pagesize=256 +espduino.menu.eesz.4M3M.upload.maximum_size=1044464 +espduino.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 +espduino.menu.eesz.4M3M.build.spiffs_start=0x100000 +espduino.menu.eesz.4M3M.build.spiffs_end=0x3FA000 +espduino.menu.eesz.4M3M.build.spiffs_blocksize=8192 +espduino.menu.eesz.4M1M=4MB (FS:1MB OTA:~1019KB) +espduino.menu.eesz.4M1M.build.flash_size=4M +espduino.menu.eesz.4M1M.build.flash_size_bytes=0x400000 +espduino.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld +espduino.menu.eesz.4M1M.build.spiffs_pagesize=256 +espduino.menu.eesz.4M1M.upload.maximum_size=1044464 +espduino.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 +espduino.menu.eesz.4M1M.build.spiffs_start=0x300000 +espduino.menu.eesz.4M1M.build.spiffs_end=0x3FA000 +espduino.menu.eesz.4M1M.build.spiffs_blocksize=8192 +espduino.menu.eesz.4M=4MB (FS:none OTA:~1019KB) +espduino.menu.eesz.4M.build.flash_size=4M +espduino.menu.eesz.4M.build.flash_size_bytes=0x400000 +espduino.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld +espduino.menu.eesz.4M.build.spiffs_pagesize=256 +espduino.menu.eesz.4M.upload.maximum_size=1044464 +espduino.menu.eesz.4M.build.rfcal_addr=0x3FC000 +espduino.menu.ip.lm2f=v2 Lower Memory +espduino.menu.ip.lm2f.build.lwip_include=lwip2/include +espduino.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +espduino.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +espduino.menu.ip.hb2f=v2 Higher Bandwidth +espduino.menu.ip.hb2f.build.lwip_include=lwip2/include +espduino.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +espduino.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +espduino.menu.ip.lm2n=v2 Lower Memory (no features) +espduino.menu.ip.lm2n.build.lwip_include=lwip2/include +espduino.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +espduino.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +espduino.menu.ip.hb2n=v2 Higher Bandwidth (no features) +espduino.menu.ip.hb2n.build.lwip_include=lwip2/include +espduino.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +espduino.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +espduino.menu.ip.lm6f=v2 IPv6 Lower Memory +espduino.menu.ip.lm6f.build.lwip_include=lwip2/include +espduino.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +espduino.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +espduino.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +espduino.menu.ip.hb6f.build.lwip_include=lwip2/include +espduino.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +espduino.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +espduino.menu.ip.hb1=v1.4 Higher Bandwidth +espduino.menu.ip.hb1.build.lwip_lib=-llwip_gcc +espduino.menu.ip.hb1.build.lwip_flags=-DLWIP_OPEN_SRC +espduino.menu.ip.src=v1.4 Compile from source +espduino.menu.ip.src.build.lwip_lib=-llwip_src +espduino.menu.ip.src.build.lwip_flags=-DLWIP_OPEN_SRC +espduino.menu.ip.src.recipe.hooks.sketch.prebuild.1.pattern=make -C "{runtime.platform.path}/tools/sdk/lwip/src" install TOOLS_PATH="{runtime.tools.xtensa-lx106-elf-gcc.path}/bin/xtensa-lx106-elf-" +espduino.menu.dbg.Disabled=Disabled +espduino.menu.dbg.Disabled.build.debug_port= +espduino.menu.dbg.Serial=Serial +espduino.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +espduino.menu.dbg.Serial1=Serial1 +espduino.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +espduino.menu.lvl.None____=None +espduino.menu.lvl.None____.build.debug_level= +espduino.menu.lvl.SSL=SSL +espduino.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +espduino.menu.lvl.TLS_MEM=TLS_MEM +espduino.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +espduino.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +espduino.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +espduino.menu.lvl.HTTP_SERVER=HTTP_SERVER +espduino.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +espduino.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +espduino.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +espduino.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +espduino.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +espduino.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +espduino.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +espduino.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +espduino.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +espduino.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +espduino.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +espduino.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +espduino.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +espduino.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +espduino.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +espduino.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +espduino.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +espduino.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +espduino.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +espduino.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +espduino.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +espduino.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +espduino.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +espduino.menu.lvl.CORE=CORE +espduino.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +espduino.menu.lvl.WIFI=WIFI +espduino.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +espduino.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +espduino.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +espduino.menu.lvl.UPDATER=UPDATER +espduino.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +espduino.menu.lvl.OTA=OTA +espduino.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +espduino.menu.lvl.OOM=OOM +espduino.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +espduino.menu.lvl.MDNS=MDNS +espduino.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +espduino.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +espduino.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS +espduino.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +espduino.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS +espduino.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +espduino.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +espduino.menu.wipe.none=Only Sketch +espduino.menu.wipe.none.upload.erase_cmd=version +espduino.menu.wipe.sdk=Sketch + WiFi Settings +espduino.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 +espduino.menu.wipe.all=All Flash Contents +espduino.menu.wipe.all.upload.erase_cmd=erase_flash +espduino.menu.baud.115200=115200 +espduino.menu.baud.115200.upload.speed=115200 +espduino.menu.baud.57600=57600 +espduino.menu.baud.57600.upload.speed=57600 +espduino.menu.baud.230400.linux=230400 +espduino.menu.baud.230400.macosx=230400 +espduino.menu.baud.230400.upload.speed=230400 +espduino.menu.baud.256000.windows=256000 +espduino.menu.baud.256000.upload.speed=256000 +espduino.menu.baud.460800.linux=460800 +espduino.menu.baud.460800.macosx=460800 +espduino.menu.baud.460800.upload.speed=460800 +espduino.menu.baud.512000.windows=512000 +espduino.menu.baud.512000.upload.speed=512000 +espduino.menu.baud.921600=921600 +espduino.menu.baud.921600.upload.speed=921600 +espduino.menu.baud.3000000=3000000 +espduino.menu.baud.3000000.upload.speed=3000000 + +############################################################## +huzzah.name=Adafruit Feather HUZZAH ESP8266 +huzzah.build.board=ESP8266_ESP12 +huzzah.build.variant=adafruit +huzzah.upload.tool=esptool +huzzah.upload.maximum_data_size=81920 +huzzah.upload.wait_for_upload_port=true +huzzah.upload.erase_cmd=version +huzzah.serial.disableDTR=true +huzzah.serial.disableRTS=true +huzzah.build.mcu=esp8266 +huzzah.build.core=esp8266 +huzzah.build.spiffs_pagesize=256 +huzzah.build.debug_port= +huzzah.build.debug_level= +huzzah.menu.xtal.80=80 MHz +huzzah.menu.xtal.80.build.f_cpu=80000000L +huzzah.menu.xtal.160=160 MHz +huzzah.menu.xtal.160.build.f_cpu=160000000L +huzzah.menu.vt.flash=Flash +huzzah.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +huzzah.menu.vt.heap=Heap +huzzah.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +huzzah.menu.vt.iram=IRAM +huzzah.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +huzzah.menu.exception.legacy=Legacy (new can return nullptr) +huzzah.menu.exception.legacy.build.exception_flags=-fno-exceptions +huzzah.menu.exception.legacy.build.stdcpp_lib=-lstdc++ +huzzah.menu.exception.disabled=Disabled (new can abort) +huzzah.menu.exception.disabled.build.exception_flags=-fno-exceptions -DNEW_OOM_ABORT +huzzah.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +huzzah.menu.exception.enabled=Enabled +huzzah.menu.exception.enabled.build.exception_flags=-fexceptions +huzzah.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +huzzah.menu.ssl.all=All SSL ciphers (most compatible) +huzzah.menu.ssl.all.build.sslflags= +huzzah.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +huzzah.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +huzzah.upload.resetmethod=nodemcu +huzzah.build.flash_mode=qio +huzzah.build.flash_flags=-DFLASHMODE_QIO +huzzah.build.flash_freq=40 +huzzah.menu.eesz.4M2M=4MB (FS:2MB OTA:~1019KB) +huzzah.menu.eesz.4M2M.build.flash_size=4M +huzzah.menu.eesz.4M2M.build.flash_size_bytes=0x400000 +huzzah.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld +huzzah.menu.eesz.4M2M.build.spiffs_pagesize=256 +huzzah.menu.eesz.4M2M.upload.maximum_size=1044464 +huzzah.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 +huzzah.menu.eesz.4M2M.build.spiffs_start=0x200000 +huzzah.menu.eesz.4M2M.build.spiffs_end=0x3FA000 +huzzah.menu.eesz.4M2M.build.spiffs_blocksize=8192 +huzzah.menu.eesz.4M3M=4MB (FS:3MB OTA:~512KB) +huzzah.menu.eesz.4M3M.build.flash_size=4M +huzzah.menu.eesz.4M3M.build.flash_size_bytes=0x400000 +huzzah.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld +huzzah.menu.eesz.4M3M.build.spiffs_pagesize=256 +huzzah.menu.eesz.4M3M.upload.maximum_size=1044464 +huzzah.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 +huzzah.menu.eesz.4M3M.build.spiffs_start=0x100000 +huzzah.menu.eesz.4M3M.build.spiffs_end=0x3FA000 +huzzah.menu.eesz.4M3M.build.spiffs_blocksize=8192 +huzzah.menu.eesz.4M1M=4MB (FS:1MB OTA:~1019KB) +huzzah.menu.eesz.4M1M.build.flash_size=4M +huzzah.menu.eesz.4M1M.build.flash_size_bytes=0x400000 +huzzah.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld +huzzah.menu.eesz.4M1M.build.spiffs_pagesize=256 +huzzah.menu.eesz.4M1M.upload.maximum_size=1044464 +huzzah.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 +huzzah.menu.eesz.4M1M.build.spiffs_start=0x300000 +huzzah.menu.eesz.4M1M.build.spiffs_end=0x3FA000 +huzzah.menu.eesz.4M1M.build.spiffs_blocksize=8192 +huzzah.menu.eesz.4M=4MB (FS:none OTA:~1019KB) +huzzah.menu.eesz.4M.build.flash_size=4M +huzzah.menu.eesz.4M.build.flash_size_bytes=0x400000 +huzzah.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld +huzzah.menu.eesz.4M.build.spiffs_pagesize=256 +huzzah.menu.eesz.4M.upload.maximum_size=1044464 +huzzah.menu.eesz.4M.build.rfcal_addr=0x3FC000 +huzzah.menu.ip.lm2f=v2 Lower Memory +huzzah.menu.ip.lm2f.build.lwip_include=lwip2/include +huzzah.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +huzzah.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +huzzah.menu.ip.hb2f=v2 Higher Bandwidth +huzzah.menu.ip.hb2f.build.lwip_include=lwip2/include +huzzah.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +huzzah.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +huzzah.menu.ip.lm2n=v2 Lower Memory (no features) +huzzah.menu.ip.lm2n.build.lwip_include=lwip2/include +huzzah.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +huzzah.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +huzzah.menu.ip.hb2n=v2 Higher Bandwidth (no features) +huzzah.menu.ip.hb2n.build.lwip_include=lwip2/include +huzzah.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +huzzah.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +huzzah.menu.ip.lm6f=v2 IPv6 Lower Memory +huzzah.menu.ip.lm6f.build.lwip_include=lwip2/include +huzzah.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +huzzah.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +huzzah.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +huzzah.menu.ip.hb6f.build.lwip_include=lwip2/include +huzzah.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +huzzah.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +huzzah.menu.ip.hb1=v1.4 Higher Bandwidth +huzzah.menu.ip.hb1.build.lwip_lib=-llwip_gcc +huzzah.menu.ip.hb1.build.lwip_flags=-DLWIP_OPEN_SRC +huzzah.menu.ip.src=v1.4 Compile from source +huzzah.menu.ip.src.build.lwip_lib=-llwip_src +huzzah.menu.ip.src.build.lwip_flags=-DLWIP_OPEN_SRC +huzzah.menu.ip.src.recipe.hooks.sketch.prebuild.1.pattern=make -C "{runtime.platform.path}/tools/sdk/lwip/src" install TOOLS_PATH="{runtime.tools.xtensa-lx106-elf-gcc.path}/bin/xtensa-lx106-elf-" +huzzah.menu.dbg.Disabled=Disabled +huzzah.menu.dbg.Disabled.build.debug_port= +huzzah.menu.dbg.Serial=Serial +huzzah.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +huzzah.menu.dbg.Serial1=Serial1 +huzzah.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +huzzah.menu.lvl.None____=None +huzzah.menu.lvl.None____.build.debug_level= +huzzah.menu.lvl.SSL=SSL +huzzah.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +huzzah.menu.lvl.TLS_MEM=TLS_MEM +huzzah.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +huzzah.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +huzzah.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +huzzah.menu.lvl.HTTP_SERVER=HTTP_SERVER +huzzah.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +huzzah.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +huzzah.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +huzzah.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +huzzah.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +huzzah.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +huzzah.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +huzzah.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +huzzah.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +huzzah.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +huzzah.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +huzzah.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +huzzah.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +huzzah.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +huzzah.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +huzzah.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +huzzah.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +huzzah.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +huzzah.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +huzzah.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +huzzah.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +huzzah.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +huzzah.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +huzzah.menu.lvl.CORE=CORE +huzzah.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +huzzah.menu.lvl.WIFI=WIFI +huzzah.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +huzzah.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +huzzah.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +huzzah.menu.lvl.UPDATER=UPDATER +huzzah.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +huzzah.menu.lvl.OTA=OTA +huzzah.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +huzzah.menu.lvl.OOM=OOM +huzzah.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +huzzah.menu.lvl.MDNS=MDNS +huzzah.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +huzzah.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +huzzah.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS +huzzah.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +huzzah.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS +huzzah.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +huzzah.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +huzzah.menu.wipe.none=Only Sketch +huzzah.menu.wipe.none.upload.erase_cmd=version +huzzah.menu.wipe.sdk=Sketch + WiFi Settings +huzzah.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 +huzzah.menu.wipe.all=All Flash Contents +huzzah.menu.wipe.all.upload.erase_cmd=erase_flash +huzzah.menu.baud.115200=115200 +huzzah.menu.baud.115200.upload.speed=115200 +huzzah.menu.baud.57600=57600 +huzzah.menu.baud.57600.upload.speed=57600 +huzzah.menu.baud.230400.linux=230400 +huzzah.menu.baud.230400.macosx=230400 +huzzah.menu.baud.230400.upload.speed=230400 +huzzah.menu.baud.256000.windows=256000 +huzzah.menu.baud.256000.upload.speed=256000 +huzzah.menu.baud.460800.linux=460800 +huzzah.menu.baud.460800.macosx=460800 +huzzah.menu.baud.460800.upload.speed=460800 +huzzah.menu.baud.512000.windows=512000 +huzzah.menu.baud.512000.upload.speed=512000 +huzzah.menu.baud.921600=921600 +huzzah.menu.baud.921600.upload.speed=921600 +huzzah.menu.baud.3000000=3000000 +huzzah.menu.baud.3000000.upload.speed=3000000 + +############################################################## +inventone.name=Invent One +inventone.build.board=ESP8266_GENERIC +inventone.build.variant=inventone +inventone.upload.tool=esptool +inventone.upload.maximum_data_size=81920 +inventone.upload.wait_for_upload_port=true +inventone.upload.erase_cmd=version +inventone.serial.disableDTR=true +inventone.serial.disableRTS=true +inventone.build.mcu=esp8266 +inventone.build.core=esp8266 +inventone.build.spiffs_pagesize=256 +inventone.build.debug_port= +inventone.build.debug_level= +inventone.menu.xtal.80=80 MHz +inventone.menu.xtal.80.build.f_cpu=80000000L +inventone.menu.xtal.160=160 MHz +inventone.menu.xtal.160.build.f_cpu=160000000L +inventone.menu.vt.flash=Flash +inventone.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +inventone.menu.vt.heap=Heap +inventone.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +inventone.menu.vt.iram=IRAM +inventone.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +inventone.menu.exception.legacy=Legacy (new can return nullptr) +inventone.menu.exception.legacy.build.exception_flags=-fno-exceptions +inventone.menu.exception.legacy.build.stdcpp_lib=-lstdc++ +inventone.menu.exception.disabled=Disabled (new can abort) +inventone.menu.exception.disabled.build.exception_flags=-fno-exceptions -DNEW_OOM_ABORT +inventone.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +inventone.menu.exception.enabled=Enabled +inventone.menu.exception.enabled.build.exception_flags=-fexceptions +inventone.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +inventone.menu.ssl.all=All SSL ciphers (most compatible) +inventone.menu.ssl.all.build.sslflags= +inventone.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +inventone.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +inventone.upload.resetmethod=nodemcu +inventone.build.flash_mode=dio +inventone.build.flash_flags=-DFLASHMODE_DIO +inventone.build.flash_freq=40 +inventone.menu.eesz.4M2M=4MB (FS:2MB OTA:~1019KB) +inventone.menu.eesz.4M2M.build.flash_size=4M +inventone.menu.eesz.4M2M.build.flash_size_bytes=0x400000 +inventone.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld +inventone.menu.eesz.4M2M.build.spiffs_pagesize=256 +inventone.menu.eesz.4M2M.upload.maximum_size=1044464 +inventone.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 +inventone.menu.eesz.4M2M.build.spiffs_start=0x200000 +inventone.menu.eesz.4M2M.build.spiffs_end=0x3FA000 +inventone.menu.eesz.4M2M.build.spiffs_blocksize=8192 +inventone.menu.eesz.4M3M=4MB (FS:3MB OTA:~512KB) +inventone.menu.eesz.4M3M.build.flash_size=4M +inventone.menu.eesz.4M3M.build.flash_size_bytes=0x400000 +inventone.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld +inventone.menu.eesz.4M3M.build.spiffs_pagesize=256 +inventone.menu.eesz.4M3M.upload.maximum_size=1044464 +inventone.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 +inventone.menu.eesz.4M3M.build.spiffs_start=0x100000 +inventone.menu.eesz.4M3M.build.spiffs_end=0x3FA000 +inventone.menu.eesz.4M3M.build.spiffs_blocksize=8192 +inventone.menu.eesz.4M1M=4MB (FS:1MB OTA:~1019KB) +inventone.menu.eesz.4M1M.build.flash_size=4M +inventone.menu.eesz.4M1M.build.flash_size_bytes=0x400000 +inventone.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld +inventone.menu.eesz.4M1M.build.spiffs_pagesize=256 +inventone.menu.eesz.4M1M.upload.maximum_size=1044464 +inventone.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 +inventone.menu.eesz.4M1M.build.spiffs_start=0x300000 +inventone.menu.eesz.4M1M.build.spiffs_end=0x3FA000 +inventone.menu.eesz.4M1M.build.spiffs_blocksize=8192 +inventone.menu.eesz.4M=4MB (FS:none OTA:~1019KB) +inventone.menu.eesz.4M.build.flash_size=4M +inventone.menu.eesz.4M.build.flash_size_bytes=0x400000 +inventone.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld +inventone.menu.eesz.4M.build.spiffs_pagesize=256 +inventone.menu.eesz.4M.upload.maximum_size=1044464 +inventone.menu.eesz.4M.build.rfcal_addr=0x3FC000 +inventone.menu.ip.lm2f=v2 Lower Memory +inventone.menu.ip.lm2f.build.lwip_include=lwip2/include +inventone.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +inventone.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +inventone.menu.ip.hb2f=v2 Higher Bandwidth +inventone.menu.ip.hb2f.build.lwip_include=lwip2/include +inventone.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +inventone.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +inventone.menu.ip.lm2n=v2 Lower Memory (no features) +inventone.menu.ip.lm2n.build.lwip_include=lwip2/include +inventone.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +inventone.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +inventone.menu.ip.hb2n=v2 Higher Bandwidth (no features) +inventone.menu.ip.hb2n.build.lwip_include=lwip2/include +inventone.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +inventone.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +inventone.menu.ip.lm6f=v2 IPv6 Lower Memory +inventone.menu.ip.lm6f.build.lwip_include=lwip2/include +inventone.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +inventone.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +inventone.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +inventone.menu.ip.hb6f.build.lwip_include=lwip2/include +inventone.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +inventone.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +inventone.menu.ip.hb1=v1.4 Higher Bandwidth +inventone.menu.ip.hb1.build.lwip_lib=-llwip_gcc +inventone.menu.ip.hb1.build.lwip_flags=-DLWIP_OPEN_SRC +inventone.menu.ip.src=v1.4 Compile from source +inventone.menu.ip.src.build.lwip_lib=-llwip_src +inventone.menu.ip.src.build.lwip_flags=-DLWIP_OPEN_SRC +inventone.menu.ip.src.recipe.hooks.sketch.prebuild.1.pattern=make -C "{runtime.platform.path}/tools/sdk/lwip/src" install TOOLS_PATH="{runtime.tools.xtensa-lx106-elf-gcc.path}/bin/xtensa-lx106-elf-" +inventone.menu.dbg.Disabled=Disabled +inventone.menu.dbg.Disabled.build.debug_port= +inventone.menu.dbg.Serial=Serial +inventone.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +inventone.menu.dbg.Serial1=Serial1 +inventone.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +inventone.menu.lvl.None____=None +inventone.menu.lvl.None____.build.debug_level= +inventone.menu.lvl.SSL=SSL +inventone.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +inventone.menu.lvl.TLS_MEM=TLS_MEM +inventone.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +inventone.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +inventone.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +inventone.menu.lvl.HTTP_SERVER=HTTP_SERVER +inventone.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +inventone.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +inventone.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +inventone.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +inventone.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +inventone.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +inventone.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +inventone.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +inventone.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +inventone.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +inventone.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +inventone.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +inventone.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +inventone.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +inventone.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +inventone.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +inventone.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +inventone.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +inventone.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +inventone.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +inventone.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +inventone.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +inventone.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +inventone.menu.lvl.CORE=CORE +inventone.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +inventone.menu.lvl.WIFI=WIFI +inventone.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +inventone.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +inventone.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +inventone.menu.lvl.UPDATER=UPDATER +inventone.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +inventone.menu.lvl.OTA=OTA +inventone.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +inventone.menu.lvl.OOM=OOM +inventone.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +inventone.menu.lvl.MDNS=MDNS +inventone.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +inventone.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +inventone.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS +inventone.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +inventone.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS +inventone.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +inventone.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +inventone.menu.wipe.none=Only Sketch +inventone.menu.wipe.none.upload.erase_cmd=version +inventone.menu.wipe.sdk=Sketch + WiFi Settings +inventone.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 +inventone.menu.wipe.all=All Flash Contents +inventone.menu.wipe.all.upload.erase_cmd=erase_flash +inventone.menu.baud.115200=115200 +inventone.menu.baud.115200.upload.speed=115200 +inventone.menu.baud.57600=57600 +inventone.menu.baud.57600.upload.speed=57600 +inventone.menu.baud.230400.linux=230400 +inventone.menu.baud.230400.macosx=230400 +inventone.menu.baud.230400.upload.speed=230400 +inventone.menu.baud.256000.windows=256000 +inventone.menu.baud.256000.upload.speed=256000 +inventone.menu.baud.460800.linux=460800 +inventone.menu.baud.460800.macosx=460800 +inventone.menu.baud.460800.upload.speed=460800 +inventone.menu.baud.512000.windows=512000 +inventone.menu.baud.512000.upload.speed=512000 +inventone.menu.baud.921600=921600 +inventone.menu.baud.921600.upload.speed=921600 +inventone.menu.baud.3000000=3000000 +inventone.menu.baud.3000000.upload.speed=3000000 + +############################################################## +cw01.name=XinaBox CW01 +cw01.build.board=ESP8266_GENERIC +cw01.build.variant=xinabox +cw01.upload.tool=esptool +cw01.upload.maximum_data_size=81920 +cw01.upload.wait_for_upload_port=true +cw01.upload.erase_cmd=version +cw01.serial.disableDTR=true +cw01.serial.disableRTS=true +cw01.build.mcu=esp8266 +cw01.build.core=esp8266 +cw01.build.spiffs_pagesize=256 +cw01.build.debug_port= +cw01.build.debug_level= +cw01.menu.xtal.80=80 MHz +cw01.menu.xtal.80.build.f_cpu=80000000L +cw01.menu.xtal.160=160 MHz +cw01.menu.xtal.160.build.f_cpu=160000000L +cw01.menu.vt.flash=Flash +cw01.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +cw01.menu.vt.heap=Heap +cw01.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +cw01.menu.vt.iram=IRAM +cw01.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +cw01.menu.exception.legacy=Legacy (new can return nullptr) +cw01.menu.exception.legacy.build.exception_flags=-fno-exceptions +cw01.menu.exception.legacy.build.stdcpp_lib=-lstdc++ +cw01.menu.exception.disabled=Disabled (new can abort) +cw01.menu.exception.disabled.build.exception_flags=-fno-exceptions -DNEW_OOM_ABORT +cw01.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +cw01.menu.exception.enabled=Enabled +cw01.menu.exception.enabled.build.exception_flags=-fexceptions +cw01.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +cw01.menu.ssl.all=All SSL ciphers (most compatible) +cw01.menu.ssl.all.build.sslflags= +cw01.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +cw01.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +cw01.upload.resetmethod=nodemcu +cw01.menu.CrystalFreq.26=26 MHz +cw01.menu.CrystalFreq.40=40 MHz +cw01.menu.CrystalFreq.40.build.extra_flags=-DF_CRYSTAL=40000000 -DESP8266 +cw01.build.flash_mode=dio +cw01.build.flash_flags=-DFLASHMODE_DIO +cw01.build.flash_freq=40 +cw01.menu.eesz.4M2M=4MB (FS:2MB OTA:~1019KB) +cw01.menu.eesz.4M2M.build.flash_size=4M +cw01.menu.eesz.4M2M.build.flash_size_bytes=0x400000 +cw01.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld +cw01.menu.eesz.4M2M.build.spiffs_pagesize=256 +cw01.menu.eesz.4M2M.upload.maximum_size=1044464 +cw01.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 +cw01.menu.eesz.4M2M.build.spiffs_start=0x200000 +cw01.menu.eesz.4M2M.build.spiffs_end=0x3FA000 +cw01.menu.eesz.4M2M.build.spiffs_blocksize=8192 +cw01.menu.eesz.4M3M=4MB (FS:3MB OTA:~512KB) +cw01.menu.eesz.4M3M.build.flash_size=4M +cw01.menu.eesz.4M3M.build.flash_size_bytes=0x400000 +cw01.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld +cw01.menu.eesz.4M3M.build.spiffs_pagesize=256 +cw01.menu.eesz.4M3M.upload.maximum_size=1044464 +cw01.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 +cw01.menu.eesz.4M3M.build.spiffs_start=0x100000 +cw01.menu.eesz.4M3M.build.spiffs_end=0x3FA000 +cw01.menu.eesz.4M3M.build.spiffs_blocksize=8192 +cw01.menu.eesz.4M1M=4MB (FS:1MB OTA:~1019KB) +cw01.menu.eesz.4M1M.build.flash_size=4M +cw01.menu.eesz.4M1M.build.flash_size_bytes=0x400000 +cw01.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld +cw01.menu.eesz.4M1M.build.spiffs_pagesize=256 +cw01.menu.eesz.4M1M.upload.maximum_size=1044464 +cw01.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 +cw01.menu.eesz.4M1M.build.spiffs_start=0x300000 +cw01.menu.eesz.4M1M.build.spiffs_end=0x3FA000 +cw01.menu.eesz.4M1M.build.spiffs_blocksize=8192 +cw01.menu.eesz.4M=4MB (FS:none OTA:~1019KB) +cw01.menu.eesz.4M.build.flash_size=4M +cw01.menu.eesz.4M.build.flash_size_bytes=0x400000 +cw01.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld +cw01.menu.eesz.4M.build.spiffs_pagesize=256 +cw01.menu.eesz.4M.upload.maximum_size=1044464 +cw01.menu.eesz.4M.build.rfcal_addr=0x3FC000 +cw01.menu.ip.lm2f=v2 Lower Memory +cw01.menu.ip.lm2f.build.lwip_include=lwip2/include +cw01.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +cw01.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +cw01.menu.ip.hb2f=v2 Higher Bandwidth +cw01.menu.ip.hb2f.build.lwip_include=lwip2/include +cw01.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +cw01.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +cw01.menu.ip.lm2n=v2 Lower Memory (no features) +cw01.menu.ip.lm2n.build.lwip_include=lwip2/include +cw01.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +cw01.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +cw01.menu.ip.hb2n=v2 Higher Bandwidth (no features) +cw01.menu.ip.hb2n.build.lwip_include=lwip2/include +cw01.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +cw01.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +cw01.menu.ip.lm6f=v2 IPv6 Lower Memory +cw01.menu.ip.lm6f.build.lwip_include=lwip2/include +cw01.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +cw01.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +cw01.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +cw01.menu.ip.hb6f.build.lwip_include=lwip2/include +cw01.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +cw01.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +cw01.menu.ip.hb1=v1.4 Higher Bandwidth +cw01.menu.ip.hb1.build.lwip_lib=-llwip_gcc +cw01.menu.ip.hb1.build.lwip_flags=-DLWIP_OPEN_SRC +cw01.menu.ip.src=v1.4 Compile from source +cw01.menu.ip.src.build.lwip_lib=-llwip_src +cw01.menu.ip.src.build.lwip_flags=-DLWIP_OPEN_SRC +cw01.menu.ip.src.recipe.hooks.sketch.prebuild.1.pattern=make -C "{runtime.platform.path}/tools/sdk/lwip/src" install TOOLS_PATH="{runtime.tools.xtensa-lx106-elf-gcc.path}/bin/xtensa-lx106-elf-" +cw01.menu.dbg.Disabled=Disabled +cw01.menu.dbg.Disabled.build.debug_port= +cw01.menu.dbg.Serial=Serial +cw01.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +cw01.menu.dbg.Serial1=Serial1 +cw01.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +cw01.menu.lvl.None____=None +cw01.menu.lvl.None____.build.debug_level= +cw01.menu.lvl.SSL=SSL +cw01.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +cw01.menu.lvl.TLS_MEM=TLS_MEM +cw01.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +cw01.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +cw01.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +cw01.menu.lvl.HTTP_SERVER=HTTP_SERVER +cw01.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +cw01.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +cw01.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +cw01.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +cw01.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +cw01.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +cw01.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +cw01.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +cw01.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +cw01.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +cw01.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +cw01.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +cw01.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +cw01.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +cw01.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +cw01.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +cw01.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +cw01.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +cw01.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +cw01.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +cw01.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +cw01.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +cw01.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +cw01.menu.lvl.CORE=CORE +cw01.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +cw01.menu.lvl.WIFI=WIFI +cw01.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +cw01.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +cw01.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +cw01.menu.lvl.UPDATER=UPDATER +cw01.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +cw01.menu.lvl.OTA=OTA +cw01.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +cw01.menu.lvl.OOM=OOM +cw01.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +cw01.menu.lvl.MDNS=MDNS +cw01.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +cw01.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +cw01.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS +cw01.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +cw01.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS +cw01.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +cw01.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +cw01.menu.wipe.none=Only Sketch +cw01.menu.wipe.none.upload.erase_cmd=version +cw01.menu.wipe.sdk=Sketch + WiFi Settings +cw01.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 +cw01.menu.wipe.all=All Flash Contents +cw01.menu.wipe.all.upload.erase_cmd=erase_flash +cw01.menu.baud.115200=115200 +cw01.menu.baud.115200.upload.speed=115200 +cw01.menu.baud.57600=57600 +cw01.menu.baud.57600.upload.speed=57600 +cw01.menu.baud.230400.linux=230400 +cw01.menu.baud.230400.macosx=230400 +cw01.menu.baud.230400.upload.speed=230400 +cw01.menu.baud.256000.windows=256000 +cw01.menu.baud.256000.upload.speed=256000 +cw01.menu.baud.460800.linux=460800 +cw01.menu.baud.460800.macosx=460800 +cw01.menu.baud.460800.upload.speed=460800 +cw01.menu.baud.512000.windows=512000 +cw01.menu.baud.512000.upload.speed=512000 +cw01.menu.baud.921600=921600 +cw01.menu.baud.921600.upload.speed=921600 +cw01.menu.baud.3000000=3000000 +cw01.menu.baud.3000000.upload.speed=3000000 + +############################################################## +espresso_lite_v1.name=ESPresso Lite 1.0 +espresso_lite_v1.build.board=ESP8266_ESPRESSO_LITE_V1 +espresso_lite_v1.build.variant=espresso_lite_v1 +espresso_lite_v1.upload.tool=esptool +espresso_lite_v1.upload.maximum_data_size=81920 +espresso_lite_v1.upload.wait_for_upload_port=true +espresso_lite_v1.upload.erase_cmd=version +espresso_lite_v1.serial.disableDTR=true +espresso_lite_v1.serial.disableRTS=true +espresso_lite_v1.build.mcu=esp8266 +espresso_lite_v1.build.core=esp8266 +espresso_lite_v1.build.spiffs_pagesize=256 +espresso_lite_v1.build.debug_port= +espresso_lite_v1.build.debug_level= +espresso_lite_v1.menu.xtal.80=80 MHz +espresso_lite_v1.menu.xtal.80.build.f_cpu=80000000L +espresso_lite_v1.menu.xtal.160=160 MHz +espresso_lite_v1.menu.xtal.160.build.f_cpu=160000000L +espresso_lite_v1.menu.vt.flash=Flash +espresso_lite_v1.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +espresso_lite_v1.menu.vt.heap=Heap +espresso_lite_v1.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +espresso_lite_v1.menu.vt.iram=IRAM +espresso_lite_v1.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +espresso_lite_v1.menu.exception.legacy=Legacy (new can return nullptr) +espresso_lite_v1.menu.exception.legacy.build.exception_flags=-fno-exceptions +espresso_lite_v1.menu.exception.legacy.build.stdcpp_lib=-lstdc++ +espresso_lite_v1.menu.exception.disabled=Disabled (new can abort) +espresso_lite_v1.menu.exception.disabled.build.exception_flags=-fno-exceptions -DNEW_OOM_ABORT +espresso_lite_v1.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +espresso_lite_v1.menu.exception.enabled=Enabled +espresso_lite_v1.menu.exception.enabled.build.exception_flags=-fexceptions +espresso_lite_v1.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +espresso_lite_v1.menu.ssl.all=All SSL ciphers (most compatible) +espresso_lite_v1.menu.ssl.all.build.sslflags= +espresso_lite_v1.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +espresso_lite_v1.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +espresso_lite_v1.build.flash_mode=dio +espresso_lite_v1.build.flash_flags=-DFLASHMODE_DIO +espresso_lite_v1.build.flash_freq=40 +espresso_lite_v1.menu.eesz.4M2M=4MB (FS:2MB OTA:~1019KB) +espresso_lite_v1.menu.eesz.4M2M.build.flash_size=4M +espresso_lite_v1.menu.eesz.4M2M.build.flash_size_bytes=0x400000 +espresso_lite_v1.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld +espresso_lite_v1.menu.eesz.4M2M.build.spiffs_pagesize=256 +espresso_lite_v1.menu.eesz.4M2M.upload.maximum_size=1044464 +espresso_lite_v1.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 +espresso_lite_v1.menu.eesz.4M2M.build.spiffs_start=0x200000 +espresso_lite_v1.menu.eesz.4M2M.build.spiffs_end=0x3FA000 +espresso_lite_v1.menu.eesz.4M2M.build.spiffs_blocksize=8192 +espresso_lite_v1.menu.eesz.4M3M=4MB (FS:3MB OTA:~512KB) +espresso_lite_v1.menu.eesz.4M3M.build.flash_size=4M +espresso_lite_v1.menu.eesz.4M3M.build.flash_size_bytes=0x400000 +espresso_lite_v1.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld +espresso_lite_v1.menu.eesz.4M3M.build.spiffs_pagesize=256 +espresso_lite_v1.menu.eesz.4M3M.upload.maximum_size=1044464 +espresso_lite_v1.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 +espresso_lite_v1.menu.eesz.4M3M.build.spiffs_start=0x100000 +espresso_lite_v1.menu.eesz.4M3M.build.spiffs_end=0x3FA000 +espresso_lite_v1.menu.eesz.4M3M.build.spiffs_blocksize=8192 +espresso_lite_v1.menu.eesz.4M1M=4MB (FS:1MB OTA:~1019KB) +espresso_lite_v1.menu.eesz.4M1M.build.flash_size=4M +espresso_lite_v1.menu.eesz.4M1M.build.flash_size_bytes=0x400000 +espresso_lite_v1.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld +espresso_lite_v1.menu.eesz.4M1M.build.spiffs_pagesize=256 +espresso_lite_v1.menu.eesz.4M1M.upload.maximum_size=1044464 +espresso_lite_v1.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 +espresso_lite_v1.menu.eesz.4M1M.build.spiffs_start=0x300000 +espresso_lite_v1.menu.eesz.4M1M.build.spiffs_end=0x3FA000 +espresso_lite_v1.menu.eesz.4M1M.build.spiffs_blocksize=8192 +espresso_lite_v1.menu.eesz.4M=4MB (FS:none OTA:~1019KB) +espresso_lite_v1.menu.eesz.4M.build.flash_size=4M +espresso_lite_v1.menu.eesz.4M.build.flash_size_bytes=0x400000 +espresso_lite_v1.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld +espresso_lite_v1.menu.eesz.4M.build.spiffs_pagesize=256 +espresso_lite_v1.menu.eesz.4M.upload.maximum_size=1044464 +espresso_lite_v1.menu.eesz.4M.build.rfcal_addr=0x3FC000 +espresso_lite_v1.menu.ResetMethod.ck=ck +espresso_lite_v1.menu.ResetMethod.ck.upload.resetmethod=ck +espresso_lite_v1.menu.ResetMethod.nodemcu=nodemcu +espresso_lite_v1.menu.ResetMethod.nodemcu.upload.resetmethod=nodemcu +espresso_lite_v1.menu.ip.lm2f=v2 Lower Memory +espresso_lite_v1.menu.ip.lm2f.build.lwip_include=lwip2/include +espresso_lite_v1.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +espresso_lite_v1.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +espresso_lite_v1.menu.ip.hb2f=v2 Higher Bandwidth +espresso_lite_v1.menu.ip.hb2f.build.lwip_include=lwip2/include +espresso_lite_v1.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +espresso_lite_v1.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +espresso_lite_v1.menu.ip.lm2n=v2 Lower Memory (no features) +espresso_lite_v1.menu.ip.lm2n.build.lwip_include=lwip2/include +espresso_lite_v1.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +espresso_lite_v1.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +espresso_lite_v1.menu.ip.hb2n=v2 Higher Bandwidth (no features) +espresso_lite_v1.menu.ip.hb2n.build.lwip_include=lwip2/include +espresso_lite_v1.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +espresso_lite_v1.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +espresso_lite_v1.menu.ip.lm6f=v2 IPv6 Lower Memory +espresso_lite_v1.menu.ip.lm6f.build.lwip_include=lwip2/include +espresso_lite_v1.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +espresso_lite_v1.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +espresso_lite_v1.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +espresso_lite_v1.menu.ip.hb6f.build.lwip_include=lwip2/include +espresso_lite_v1.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +espresso_lite_v1.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +espresso_lite_v1.menu.ip.hb1=v1.4 Higher Bandwidth +espresso_lite_v1.menu.ip.hb1.build.lwip_lib=-llwip_gcc +espresso_lite_v1.menu.ip.hb1.build.lwip_flags=-DLWIP_OPEN_SRC +espresso_lite_v1.menu.ip.src=v1.4 Compile from source +espresso_lite_v1.menu.ip.src.build.lwip_lib=-llwip_src +espresso_lite_v1.menu.ip.src.build.lwip_flags=-DLWIP_OPEN_SRC +espresso_lite_v1.menu.ip.src.recipe.hooks.sketch.prebuild.1.pattern=make -C "{runtime.platform.path}/tools/sdk/lwip/src" install TOOLS_PATH="{runtime.tools.xtensa-lx106-elf-gcc.path}/bin/xtensa-lx106-elf-" +espresso_lite_v1.menu.dbg.Disabled=Disabled +espresso_lite_v1.menu.dbg.Disabled.build.debug_port= +espresso_lite_v1.menu.dbg.Serial=Serial +espresso_lite_v1.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +espresso_lite_v1.menu.dbg.Serial1=Serial1 +espresso_lite_v1.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +espresso_lite_v1.menu.lvl.None____=None +espresso_lite_v1.menu.lvl.None____.build.debug_level= +espresso_lite_v1.menu.lvl.SSL=SSL +espresso_lite_v1.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +espresso_lite_v1.menu.lvl.TLS_MEM=TLS_MEM +espresso_lite_v1.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +espresso_lite_v1.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +espresso_lite_v1.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +espresso_lite_v1.menu.lvl.HTTP_SERVER=HTTP_SERVER +espresso_lite_v1.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +espresso_lite_v1.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +espresso_lite_v1.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +espresso_lite_v1.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +espresso_lite_v1.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +espresso_lite_v1.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +espresso_lite_v1.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +espresso_lite_v1.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +espresso_lite_v1.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +espresso_lite_v1.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +espresso_lite_v1.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +espresso_lite_v1.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +espresso_lite_v1.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +espresso_lite_v1.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +espresso_lite_v1.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +espresso_lite_v1.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +espresso_lite_v1.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +espresso_lite_v1.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +espresso_lite_v1.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +espresso_lite_v1.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +espresso_lite_v1.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +espresso_lite_v1.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +espresso_lite_v1.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +espresso_lite_v1.menu.lvl.CORE=CORE +espresso_lite_v1.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +espresso_lite_v1.menu.lvl.WIFI=WIFI +espresso_lite_v1.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +espresso_lite_v1.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +espresso_lite_v1.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +espresso_lite_v1.menu.lvl.UPDATER=UPDATER +espresso_lite_v1.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +espresso_lite_v1.menu.lvl.OTA=OTA +espresso_lite_v1.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +espresso_lite_v1.menu.lvl.OOM=OOM +espresso_lite_v1.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +espresso_lite_v1.menu.lvl.MDNS=MDNS +espresso_lite_v1.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +espresso_lite_v1.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +espresso_lite_v1.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS +espresso_lite_v1.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +espresso_lite_v1.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS +espresso_lite_v1.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +espresso_lite_v1.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +espresso_lite_v1.menu.wipe.none=Only Sketch +espresso_lite_v1.menu.wipe.none.upload.erase_cmd=version +espresso_lite_v1.menu.wipe.sdk=Sketch + WiFi Settings +espresso_lite_v1.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 +espresso_lite_v1.menu.wipe.all=All Flash Contents +espresso_lite_v1.menu.wipe.all.upload.erase_cmd=erase_flash +espresso_lite_v1.menu.baud.115200=115200 +espresso_lite_v1.menu.baud.115200.upload.speed=115200 +espresso_lite_v1.menu.baud.57600=57600 +espresso_lite_v1.menu.baud.57600.upload.speed=57600 +espresso_lite_v1.menu.baud.230400.linux=230400 +espresso_lite_v1.menu.baud.230400.macosx=230400 +espresso_lite_v1.menu.baud.230400.upload.speed=230400 +espresso_lite_v1.menu.baud.256000.windows=256000 +espresso_lite_v1.menu.baud.256000.upload.speed=256000 +espresso_lite_v1.menu.baud.460800.linux=460800 +espresso_lite_v1.menu.baud.460800.macosx=460800 +espresso_lite_v1.menu.baud.460800.upload.speed=460800 +espresso_lite_v1.menu.baud.512000.windows=512000 +espresso_lite_v1.menu.baud.512000.upload.speed=512000 +espresso_lite_v1.menu.baud.921600=921600 +espresso_lite_v1.menu.baud.921600.upload.speed=921600 +espresso_lite_v1.menu.baud.3000000=3000000 +espresso_lite_v1.menu.baud.3000000.upload.speed=3000000 + +############################################################## +espresso_lite_v2.name=ESPresso Lite 2.0 +espresso_lite_v2.build.board=ESP8266_ESPRESSO_LITE_V2 +espresso_lite_v2.build.variant=espresso_lite_v2 +espresso_lite_v2.upload.tool=esptool +espresso_lite_v2.upload.maximum_data_size=81920 +espresso_lite_v2.upload.wait_for_upload_port=true +espresso_lite_v2.upload.erase_cmd=version +espresso_lite_v2.serial.disableDTR=true +espresso_lite_v2.serial.disableRTS=true +espresso_lite_v2.build.mcu=esp8266 +espresso_lite_v2.build.core=esp8266 +espresso_lite_v2.build.spiffs_pagesize=256 +espresso_lite_v2.build.debug_port= +espresso_lite_v2.build.debug_level= +espresso_lite_v2.menu.xtal.80=80 MHz +espresso_lite_v2.menu.xtal.80.build.f_cpu=80000000L +espresso_lite_v2.menu.xtal.160=160 MHz +espresso_lite_v2.menu.xtal.160.build.f_cpu=160000000L +espresso_lite_v2.menu.vt.flash=Flash +espresso_lite_v2.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +espresso_lite_v2.menu.vt.heap=Heap +espresso_lite_v2.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +espresso_lite_v2.menu.vt.iram=IRAM +espresso_lite_v2.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +espresso_lite_v2.menu.exception.legacy=Legacy (new can return nullptr) +espresso_lite_v2.menu.exception.legacy.build.exception_flags=-fno-exceptions +espresso_lite_v2.menu.exception.legacy.build.stdcpp_lib=-lstdc++ +espresso_lite_v2.menu.exception.disabled=Disabled (new can abort) +espresso_lite_v2.menu.exception.disabled.build.exception_flags=-fno-exceptions -DNEW_OOM_ABORT +espresso_lite_v2.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +espresso_lite_v2.menu.exception.enabled=Enabled +espresso_lite_v2.menu.exception.enabled.build.exception_flags=-fexceptions +espresso_lite_v2.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +espresso_lite_v2.menu.ssl.all=All SSL ciphers (most compatible) +espresso_lite_v2.menu.ssl.all.build.sslflags= +espresso_lite_v2.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +espresso_lite_v2.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +espresso_lite_v2.build.flash_mode=dio +espresso_lite_v2.build.flash_flags=-DFLASHMODE_DIO +espresso_lite_v2.build.flash_freq=40 +espresso_lite_v2.menu.eesz.4M2M=4MB (FS:2MB OTA:~1019KB) +espresso_lite_v2.menu.eesz.4M2M.build.flash_size=4M +espresso_lite_v2.menu.eesz.4M2M.build.flash_size_bytes=0x400000 +espresso_lite_v2.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld +espresso_lite_v2.menu.eesz.4M2M.build.spiffs_pagesize=256 +espresso_lite_v2.menu.eesz.4M2M.upload.maximum_size=1044464 +espresso_lite_v2.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 +espresso_lite_v2.menu.eesz.4M2M.build.spiffs_start=0x200000 +espresso_lite_v2.menu.eesz.4M2M.build.spiffs_end=0x3FA000 +espresso_lite_v2.menu.eesz.4M2M.build.spiffs_blocksize=8192 +espresso_lite_v2.menu.eesz.4M3M=4MB (FS:3MB OTA:~512KB) +espresso_lite_v2.menu.eesz.4M3M.build.flash_size=4M +espresso_lite_v2.menu.eesz.4M3M.build.flash_size_bytes=0x400000 +espresso_lite_v2.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld +espresso_lite_v2.menu.eesz.4M3M.build.spiffs_pagesize=256 +espresso_lite_v2.menu.eesz.4M3M.upload.maximum_size=1044464 +espresso_lite_v2.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 +espresso_lite_v2.menu.eesz.4M3M.build.spiffs_start=0x100000 +espresso_lite_v2.menu.eesz.4M3M.build.spiffs_end=0x3FA000 +espresso_lite_v2.menu.eesz.4M3M.build.spiffs_blocksize=8192 +espresso_lite_v2.menu.eesz.4M1M=4MB (FS:1MB OTA:~1019KB) +espresso_lite_v2.menu.eesz.4M1M.build.flash_size=4M +espresso_lite_v2.menu.eesz.4M1M.build.flash_size_bytes=0x400000 +espresso_lite_v2.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld +espresso_lite_v2.menu.eesz.4M1M.build.spiffs_pagesize=256 +espresso_lite_v2.menu.eesz.4M1M.upload.maximum_size=1044464 +espresso_lite_v2.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 +espresso_lite_v2.menu.eesz.4M1M.build.spiffs_start=0x300000 +espresso_lite_v2.menu.eesz.4M1M.build.spiffs_end=0x3FA000 +espresso_lite_v2.menu.eesz.4M1M.build.spiffs_blocksize=8192 +espresso_lite_v2.menu.eesz.4M=4MB (FS:none OTA:~1019KB) +espresso_lite_v2.menu.eesz.4M.build.flash_size=4M +espresso_lite_v2.menu.eesz.4M.build.flash_size_bytes=0x400000 +espresso_lite_v2.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld +espresso_lite_v2.menu.eesz.4M.build.spiffs_pagesize=256 +espresso_lite_v2.menu.eesz.4M.upload.maximum_size=1044464 +espresso_lite_v2.menu.eesz.4M.build.rfcal_addr=0x3FC000 +espresso_lite_v2.menu.ResetMethod.ck=ck +espresso_lite_v2.menu.ResetMethod.ck.upload.resetmethod=ck +espresso_lite_v2.menu.ResetMethod.nodemcu=nodemcu +espresso_lite_v2.menu.ResetMethod.nodemcu.upload.resetmethod=nodemcu +espresso_lite_v2.menu.ip.lm2f=v2 Lower Memory +espresso_lite_v2.menu.ip.lm2f.build.lwip_include=lwip2/include +espresso_lite_v2.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +espresso_lite_v2.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +espresso_lite_v2.menu.ip.hb2f=v2 Higher Bandwidth +espresso_lite_v2.menu.ip.hb2f.build.lwip_include=lwip2/include +espresso_lite_v2.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +espresso_lite_v2.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +espresso_lite_v2.menu.ip.lm2n=v2 Lower Memory (no features) +espresso_lite_v2.menu.ip.lm2n.build.lwip_include=lwip2/include +espresso_lite_v2.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +espresso_lite_v2.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +espresso_lite_v2.menu.ip.hb2n=v2 Higher Bandwidth (no features) +espresso_lite_v2.menu.ip.hb2n.build.lwip_include=lwip2/include +espresso_lite_v2.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +espresso_lite_v2.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +espresso_lite_v2.menu.ip.lm6f=v2 IPv6 Lower Memory +espresso_lite_v2.menu.ip.lm6f.build.lwip_include=lwip2/include +espresso_lite_v2.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +espresso_lite_v2.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +espresso_lite_v2.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +espresso_lite_v2.menu.ip.hb6f.build.lwip_include=lwip2/include +espresso_lite_v2.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +espresso_lite_v2.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +espresso_lite_v2.menu.ip.hb1=v1.4 Higher Bandwidth +espresso_lite_v2.menu.ip.hb1.build.lwip_lib=-llwip_gcc +espresso_lite_v2.menu.ip.hb1.build.lwip_flags=-DLWIP_OPEN_SRC +espresso_lite_v2.menu.ip.src=v1.4 Compile from source +espresso_lite_v2.menu.ip.src.build.lwip_lib=-llwip_src +espresso_lite_v2.menu.ip.src.build.lwip_flags=-DLWIP_OPEN_SRC +espresso_lite_v2.menu.ip.src.recipe.hooks.sketch.prebuild.1.pattern=make -C "{runtime.platform.path}/tools/sdk/lwip/src" install TOOLS_PATH="{runtime.tools.xtensa-lx106-elf-gcc.path}/bin/xtensa-lx106-elf-" +espresso_lite_v2.menu.dbg.Disabled=Disabled +espresso_lite_v2.menu.dbg.Disabled.build.debug_port= +espresso_lite_v2.menu.dbg.Serial=Serial +espresso_lite_v2.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +espresso_lite_v2.menu.dbg.Serial1=Serial1 +espresso_lite_v2.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +espresso_lite_v2.menu.lvl.None____=None +espresso_lite_v2.menu.lvl.None____.build.debug_level= +espresso_lite_v2.menu.lvl.SSL=SSL +espresso_lite_v2.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +espresso_lite_v2.menu.lvl.TLS_MEM=TLS_MEM +espresso_lite_v2.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +espresso_lite_v2.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +espresso_lite_v2.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +espresso_lite_v2.menu.lvl.HTTP_SERVER=HTTP_SERVER +espresso_lite_v2.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +espresso_lite_v2.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +espresso_lite_v2.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +espresso_lite_v2.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +espresso_lite_v2.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +espresso_lite_v2.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +espresso_lite_v2.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +espresso_lite_v2.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +espresso_lite_v2.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +espresso_lite_v2.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +espresso_lite_v2.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +espresso_lite_v2.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +espresso_lite_v2.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +espresso_lite_v2.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +espresso_lite_v2.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +espresso_lite_v2.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +espresso_lite_v2.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +espresso_lite_v2.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +espresso_lite_v2.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +espresso_lite_v2.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +espresso_lite_v2.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +espresso_lite_v2.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +espresso_lite_v2.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +espresso_lite_v2.menu.lvl.CORE=CORE +espresso_lite_v2.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +espresso_lite_v2.menu.lvl.WIFI=WIFI +espresso_lite_v2.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +espresso_lite_v2.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +espresso_lite_v2.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +espresso_lite_v2.menu.lvl.UPDATER=UPDATER +espresso_lite_v2.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +espresso_lite_v2.menu.lvl.OTA=OTA +espresso_lite_v2.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +espresso_lite_v2.menu.lvl.OOM=OOM +espresso_lite_v2.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +espresso_lite_v2.menu.lvl.MDNS=MDNS +espresso_lite_v2.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +espresso_lite_v2.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +espresso_lite_v2.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS +espresso_lite_v2.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +espresso_lite_v2.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS +espresso_lite_v2.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +espresso_lite_v2.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +espresso_lite_v2.menu.wipe.none=Only Sketch +espresso_lite_v2.menu.wipe.none.upload.erase_cmd=version +espresso_lite_v2.menu.wipe.sdk=Sketch + WiFi Settings +espresso_lite_v2.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 +espresso_lite_v2.menu.wipe.all=All Flash Contents +espresso_lite_v2.menu.wipe.all.upload.erase_cmd=erase_flash +espresso_lite_v2.menu.baud.115200=115200 +espresso_lite_v2.menu.baud.115200.upload.speed=115200 +espresso_lite_v2.menu.baud.57600=57600 +espresso_lite_v2.menu.baud.57600.upload.speed=57600 +espresso_lite_v2.menu.baud.230400.linux=230400 +espresso_lite_v2.menu.baud.230400.macosx=230400 +espresso_lite_v2.menu.baud.230400.upload.speed=230400 +espresso_lite_v2.menu.baud.256000.windows=256000 +espresso_lite_v2.menu.baud.256000.upload.speed=256000 +espresso_lite_v2.menu.baud.460800.linux=460800 +espresso_lite_v2.menu.baud.460800.macosx=460800 +espresso_lite_v2.menu.baud.460800.upload.speed=460800 +espresso_lite_v2.menu.baud.512000.windows=512000 +espresso_lite_v2.menu.baud.512000.upload.speed=512000 +espresso_lite_v2.menu.baud.921600=921600 +espresso_lite_v2.menu.baud.921600.upload.speed=921600 +espresso_lite_v2.menu.baud.3000000=3000000 +espresso_lite_v2.menu.baud.3000000.upload.speed=3000000 + +############################################################## +phoenix_v1.name=Phoenix 1.0 +phoenix_v1.build.board=ESP8266_PHOENIX_V1 +phoenix_v1.build.variant=phoenix_v1 +phoenix_v1.upload.tool=esptool +phoenix_v1.upload.maximum_data_size=81920 +phoenix_v1.upload.wait_for_upload_port=true +phoenix_v1.upload.erase_cmd=version +phoenix_v1.serial.disableDTR=true +phoenix_v1.serial.disableRTS=true +phoenix_v1.build.mcu=esp8266 +phoenix_v1.build.core=esp8266 +phoenix_v1.build.spiffs_pagesize=256 +phoenix_v1.build.debug_port= +phoenix_v1.build.debug_level= +phoenix_v1.menu.xtal.80=80 MHz +phoenix_v1.menu.xtal.80.build.f_cpu=80000000L +phoenix_v1.menu.xtal.160=160 MHz +phoenix_v1.menu.xtal.160.build.f_cpu=160000000L +phoenix_v1.menu.vt.flash=Flash +phoenix_v1.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +phoenix_v1.menu.vt.heap=Heap +phoenix_v1.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +phoenix_v1.menu.vt.iram=IRAM +phoenix_v1.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +phoenix_v1.menu.exception.legacy=Legacy (new can return nullptr) +phoenix_v1.menu.exception.legacy.build.exception_flags=-fno-exceptions +phoenix_v1.menu.exception.legacy.build.stdcpp_lib=-lstdc++ +phoenix_v1.menu.exception.disabled=Disabled (new can abort) +phoenix_v1.menu.exception.disabled.build.exception_flags=-fno-exceptions -DNEW_OOM_ABORT +phoenix_v1.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +phoenix_v1.menu.exception.enabled=Enabled +phoenix_v1.menu.exception.enabled.build.exception_flags=-fexceptions +phoenix_v1.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +phoenix_v1.menu.ssl.all=All SSL ciphers (most compatible) +phoenix_v1.menu.ssl.all.build.sslflags= +phoenix_v1.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +phoenix_v1.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +phoenix_v1.build.flash_mode=dio +phoenix_v1.build.flash_flags=-DFLASHMODE_DIO +phoenix_v1.build.flash_freq=40 +phoenix_v1.menu.eesz.4M2M=4MB (FS:2MB OTA:~1019KB) +phoenix_v1.menu.eesz.4M2M.build.flash_size=4M +phoenix_v1.menu.eesz.4M2M.build.flash_size_bytes=0x400000 +phoenix_v1.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld +phoenix_v1.menu.eesz.4M2M.build.spiffs_pagesize=256 +phoenix_v1.menu.eesz.4M2M.upload.maximum_size=1044464 +phoenix_v1.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 +phoenix_v1.menu.eesz.4M2M.build.spiffs_start=0x200000 +phoenix_v1.menu.eesz.4M2M.build.spiffs_end=0x3FA000 +phoenix_v1.menu.eesz.4M2M.build.spiffs_blocksize=8192 +phoenix_v1.menu.eesz.4M3M=4MB (FS:3MB OTA:~512KB) +phoenix_v1.menu.eesz.4M3M.build.flash_size=4M +phoenix_v1.menu.eesz.4M3M.build.flash_size_bytes=0x400000 +phoenix_v1.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld +phoenix_v1.menu.eesz.4M3M.build.spiffs_pagesize=256 +phoenix_v1.menu.eesz.4M3M.upload.maximum_size=1044464 +phoenix_v1.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 +phoenix_v1.menu.eesz.4M3M.build.spiffs_start=0x100000 +phoenix_v1.menu.eesz.4M3M.build.spiffs_end=0x3FA000 +phoenix_v1.menu.eesz.4M3M.build.spiffs_blocksize=8192 +phoenix_v1.menu.eesz.4M1M=4MB (FS:1MB OTA:~1019KB) +phoenix_v1.menu.eesz.4M1M.build.flash_size=4M +phoenix_v1.menu.eesz.4M1M.build.flash_size_bytes=0x400000 +phoenix_v1.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld +phoenix_v1.menu.eesz.4M1M.build.spiffs_pagesize=256 +phoenix_v1.menu.eesz.4M1M.upload.maximum_size=1044464 +phoenix_v1.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 +phoenix_v1.menu.eesz.4M1M.build.spiffs_start=0x300000 +phoenix_v1.menu.eesz.4M1M.build.spiffs_end=0x3FA000 +phoenix_v1.menu.eesz.4M1M.build.spiffs_blocksize=8192 +phoenix_v1.menu.eesz.4M=4MB (FS:none OTA:~1019KB) +phoenix_v1.menu.eesz.4M.build.flash_size=4M +phoenix_v1.menu.eesz.4M.build.flash_size_bytes=0x400000 +phoenix_v1.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld +phoenix_v1.menu.eesz.4M.build.spiffs_pagesize=256 +phoenix_v1.menu.eesz.4M.upload.maximum_size=1044464 +phoenix_v1.menu.eesz.4M.build.rfcal_addr=0x3FC000 +phoenix_v1.menu.ResetMethod.ck=ck +phoenix_v1.menu.ResetMethod.ck.upload.resetmethod=ck +phoenix_v1.menu.ResetMethod.nodemcu=nodemcu +phoenix_v1.menu.ResetMethod.nodemcu.upload.resetmethod=nodemcu +phoenix_v1.menu.ip.lm2f=v2 Lower Memory +phoenix_v1.menu.ip.lm2f.build.lwip_include=lwip2/include +phoenix_v1.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +phoenix_v1.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +phoenix_v1.menu.ip.hb2f=v2 Higher Bandwidth +phoenix_v1.menu.ip.hb2f.build.lwip_include=lwip2/include +phoenix_v1.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +phoenix_v1.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +phoenix_v1.menu.ip.lm2n=v2 Lower Memory (no features) +phoenix_v1.menu.ip.lm2n.build.lwip_include=lwip2/include +phoenix_v1.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +phoenix_v1.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +phoenix_v1.menu.ip.hb2n=v2 Higher Bandwidth (no features) +phoenix_v1.menu.ip.hb2n.build.lwip_include=lwip2/include +phoenix_v1.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +phoenix_v1.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +phoenix_v1.menu.ip.lm6f=v2 IPv6 Lower Memory +phoenix_v1.menu.ip.lm6f.build.lwip_include=lwip2/include +phoenix_v1.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +phoenix_v1.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +phoenix_v1.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +phoenix_v1.menu.ip.hb6f.build.lwip_include=lwip2/include +phoenix_v1.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +phoenix_v1.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +phoenix_v1.menu.ip.hb1=v1.4 Higher Bandwidth +phoenix_v1.menu.ip.hb1.build.lwip_lib=-llwip_gcc +phoenix_v1.menu.ip.hb1.build.lwip_flags=-DLWIP_OPEN_SRC +phoenix_v1.menu.ip.src=v1.4 Compile from source +phoenix_v1.menu.ip.src.build.lwip_lib=-llwip_src +phoenix_v1.menu.ip.src.build.lwip_flags=-DLWIP_OPEN_SRC +phoenix_v1.menu.ip.src.recipe.hooks.sketch.prebuild.1.pattern=make -C "{runtime.platform.path}/tools/sdk/lwip/src" install TOOLS_PATH="{runtime.tools.xtensa-lx106-elf-gcc.path}/bin/xtensa-lx106-elf-" +phoenix_v1.menu.dbg.Disabled=Disabled +phoenix_v1.menu.dbg.Disabled.build.debug_port= +phoenix_v1.menu.dbg.Serial=Serial +phoenix_v1.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +phoenix_v1.menu.dbg.Serial1=Serial1 +phoenix_v1.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +phoenix_v1.menu.lvl.None____=None +phoenix_v1.menu.lvl.None____.build.debug_level= +phoenix_v1.menu.lvl.SSL=SSL +phoenix_v1.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +phoenix_v1.menu.lvl.TLS_MEM=TLS_MEM +phoenix_v1.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +phoenix_v1.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +phoenix_v1.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +phoenix_v1.menu.lvl.HTTP_SERVER=HTTP_SERVER +phoenix_v1.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +phoenix_v1.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +phoenix_v1.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +phoenix_v1.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +phoenix_v1.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +phoenix_v1.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +phoenix_v1.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +phoenix_v1.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +phoenix_v1.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +phoenix_v1.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +phoenix_v1.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +phoenix_v1.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +phoenix_v1.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +phoenix_v1.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +phoenix_v1.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +phoenix_v1.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +phoenix_v1.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +phoenix_v1.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +phoenix_v1.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +phoenix_v1.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +phoenix_v1.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +phoenix_v1.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +phoenix_v1.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +phoenix_v1.menu.lvl.CORE=CORE +phoenix_v1.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +phoenix_v1.menu.lvl.WIFI=WIFI +phoenix_v1.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +phoenix_v1.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +phoenix_v1.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +phoenix_v1.menu.lvl.UPDATER=UPDATER +phoenix_v1.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +phoenix_v1.menu.lvl.OTA=OTA +phoenix_v1.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +phoenix_v1.menu.lvl.OOM=OOM +phoenix_v1.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +phoenix_v1.menu.lvl.MDNS=MDNS +phoenix_v1.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +phoenix_v1.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +phoenix_v1.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS +phoenix_v1.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +phoenix_v1.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS +phoenix_v1.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +phoenix_v1.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +phoenix_v1.menu.wipe.none=Only Sketch +phoenix_v1.menu.wipe.none.upload.erase_cmd=version +phoenix_v1.menu.wipe.sdk=Sketch + WiFi Settings +phoenix_v1.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 +phoenix_v1.menu.wipe.all=All Flash Contents +phoenix_v1.menu.wipe.all.upload.erase_cmd=erase_flash +phoenix_v1.menu.baud.115200=115200 +phoenix_v1.menu.baud.115200.upload.speed=115200 +phoenix_v1.menu.baud.57600=57600 +phoenix_v1.menu.baud.57600.upload.speed=57600 +phoenix_v1.menu.baud.230400.linux=230400 +phoenix_v1.menu.baud.230400.macosx=230400 +phoenix_v1.menu.baud.230400.upload.speed=230400 +phoenix_v1.menu.baud.256000.windows=256000 +phoenix_v1.menu.baud.256000.upload.speed=256000 +phoenix_v1.menu.baud.460800.linux=460800 +phoenix_v1.menu.baud.460800.macosx=460800 +phoenix_v1.menu.baud.460800.upload.speed=460800 +phoenix_v1.menu.baud.512000.windows=512000 +phoenix_v1.menu.baud.512000.upload.speed=512000 +phoenix_v1.menu.baud.921600=921600 +phoenix_v1.menu.baud.921600.upload.speed=921600 +phoenix_v1.menu.baud.3000000=3000000 +phoenix_v1.menu.baud.3000000.upload.speed=3000000 + +############################################################## +phoenix_v2.name=Phoenix 2.0 +phoenix_v2.build.board=ESP8266_PHOENIX_V2 +phoenix_v2.build.variant=phoenix_v2 +phoenix_v2.upload.tool=esptool +phoenix_v2.upload.maximum_data_size=81920 +phoenix_v2.upload.wait_for_upload_port=true +phoenix_v2.upload.erase_cmd=version +phoenix_v2.serial.disableDTR=true +phoenix_v2.serial.disableRTS=true +phoenix_v2.build.mcu=esp8266 +phoenix_v2.build.core=esp8266 +phoenix_v2.build.spiffs_pagesize=256 +phoenix_v2.build.debug_port= +phoenix_v2.build.debug_level= +phoenix_v2.menu.xtal.80=80 MHz +phoenix_v2.menu.xtal.80.build.f_cpu=80000000L +phoenix_v2.menu.xtal.160=160 MHz +phoenix_v2.menu.xtal.160.build.f_cpu=160000000L +phoenix_v2.menu.vt.flash=Flash +phoenix_v2.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +phoenix_v2.menu.vt.heap=Heap +phoenix_v2.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +phoenix_v2.menu.vt.iram=IRAM +phoenix_v2.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +phoenix_v2.menu.exception.legacy=Legacy (new can return nullptr) +phoenix_v2.menu.exception.legacy.build.exception_flags=-fno-exceptions +phoenix_v2.menu.exception.legacy.build.stdcpp_lib=-lstdc++ +phoenix_v2.menu.exception.disabled=Disabled (new can abort) +phoenix_v2.menu.exception.disabled.build.exception_flags=-fno-exceptions -DNEW_OOM_ABORT +phoenix_v2.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +phoenix_v2.menu.exception.enabled=Enabled +phoenix_v2.menu.exception.enabled.build.exception_flags=-fexceptions +phoenix_v2.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +phoenix_v2.menu.ssl.all=All SSL ciphers (most compatible) +phoenix_v2.menu.ssl.all.build.sslflags= +phoenix_v2.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +phoenix_v2.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +phoenix_v2.build.flash_mode=dio +phoenix_v2.build.flash_flags=-DFLASHMODE_DIO +phoenix_v2.build.flash_freq=40 +phoenix_v2.menu.eesz.4M2M=4MB (FS:2MB OTA:~1019KB) +phoenix_v2.menu.eesz.4M2M.build.flash_size=4M +phoenix_v2.menu.eesz.4M2M.build.flash_size_bytes=0x400000 +phoenix_v2.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld +phoenix_v2.menu.eesz.4M2M.build.spiffs_pagesize=256 +phoenix_v2.menu.eesz.4M2M.upload.maximum_size=1044464 +phoenix_v2.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 +phoenix_v2.menu.eesz.4M2M.build.spiffs_start=0x200000 +phoenix_v2.menu.eesz.4M2M.build.spiffs_end=0x3FA000 +phoenix_v2.menu.eesz.4M2M.build.spiffs_blocksize=8192 +phoenix_v2.menu.eesz.4M3M=4MB (FS:3MB OTA:~512KB) +phoenix_v2.menu.eesz.4M3M.build.flash_size=4M +phoenix_v2.menu.eesz.4M3M.build.flash_size_bytes=0x400000 +phoenix_v2.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld +phoenix_v2.menu.eesz.4M3M.build.spiffs_pagesize=256 +phoenix_v2.menu.eesz.4M3M.upload.maximum_size=1044464 +phoenix_v2.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 +phoenix_v2.menu.eesz.4M3M.build.spiffs_start=0x100000 +phoenix_v2.menu.eesz.4M3M.build.spiffs_end=0x3FA000 +phoenix_v2.menu.eesz.4M3M.build.spiffs_blocksize=8192 +phoenix_v2.menu.eesz.4M1M=4MB (FS:1MB OTA:~1019KB) +phoenix_v2.menu.eesz.4M1M.build.flash_size=4M +phoenix_v2.menu.eesz.4M1M.build.flash_size_bytes=0x400000 +phoenix_v2.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld +phoenix_v2.menu.eesz.4M1M.build.spiffs_pagesize=256 +phoenix_v2.menu.eesz.4M1M.upload.maximum_size=1044464 +phoenix_v2.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 +phoenix_v2.menu.eesz.4M1M.build.spiffs_start=0x300000 +phoenix_v2.menu.eesz.4M1M.build.spiffs_end=0x3FA000 +phoenix_v2.menu.eesz.4M1M.build.spiffs_blocksize=8192 +phoenix_v2.menu.eesz.4M=4MB (FS:none OTA:~1019KB) +phoenix_v2.menu.eesz.4M.build.flash_size=4M +phoenix_v2.menu.eesz.4M.build.flash_size_bytes=0x400000 +phoenix_v2.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld +phoenix_v2.menu.eesz.4M.build.spiffs_pagesize=256 +phoenix_v2.menu.eesz.4M.upload.maximum_size=1044464 +phoenix_v2.menu.eesz.4M.build.rfcal_addr=0x3FC000 +phoenix_v2.menu.ResetMethod.ck=ck +phoenix_v2.menu.ResetMethod.ck.upload.resetmethod=ck +phoenix_v2.menu.ResetMethod.nodemcu=nodemcu +phoenix_v2.menu.ResetMethod.nodemcu.upload.resetmethod=nodemcu +phoenix_v2.menu.ip.lm2f=v2 Lower Memory +phoenix_v2.menu.ip.lm2f.build.lwip_include=lwip2/include +phoenix_v2.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +phoenix_v2.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +phoenix_v2.menu.ip.hb2f=v2 Higher Bandwidth +phoenix_v2.menu.ip.hb2f.build.lwip_include=lwip2/include +phoenix_v2.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +phoenix_v2.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +phoenix_v2.menu.ip.lm2n=v2 Lower Memory (no features) +phoenix_v2.menu.ip.lm2n.build.lwip_include=lwip2/include +phoenix_v2.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +phoenix_v2.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +phoenix_v2.menu.ip.hb2n=v2 Higher Bandwidth (no features) +phoenix_v2.menu.ip.hb2n.build.lwip_include=lwip2/include +phoenix_v2.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +phoenix_v2.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +phoenix_v2.menu.ip.lm6f=v2 IPv6 Lower Memory +phoenix_v2.menu.ip.lm6f.build.lwip_include=lwip2/include +phoenix_v2.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +phoenix_v2.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +phoenix_v2.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +phoenix_v2.menu.ip.hb6f.build.lwip_include=lwip2/include +phoenix_v2.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +phoenix_v2.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +phoenix_v2.menu.ip.hb1=v1.4 Higher Bandwidth +phoenix_v2.menu.ip.hb1.build.lwip_lib=-llwip_gcc +phoenix_v2.menu.ip.hb1.build.lwip_flags=-DLWIP_OPEN_SRC +phoenix_v2.menu.ip.src=v1.4 Compile from source +phoenix_v2.menu.ip.src.build.lwip_lib=-llwip_src +phoenix_v2.menu.ip.src.build.lwip_flags=-DLWIP_OPEN_SRC +phoenix_v2.menu.ip.src.recipe.hooks.sketch.prebuild.1.pattern=make -C "{runtime.platform.path}/tools/sdk/lwip/src" install TOOLS_PATH="{runtime.tools.xtensa-lx106-elf-gcc.path}/bin/xtensa-lx106-elf-" +phoenix_v2.menu.dbg.Disabled=Disabled +phoenix_v2.menu.dbg.Disabled.build.debug_port= +phoenix_v2.menu.dbg.Serial=Serial +phoenix_v2.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +phoenix_v2.menu.dbg.Serial1=Serial1 +phoenix_v2.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +phoenix_v2.menu.lvl.None____=None +phoenix_v2.menu.lvl.None____.build.debug_level= +phoenix_v2.menu.lvl.SSL=SSL +phoenix_v2.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +phoenix_v2.menu.lvl.TLS_MEM=TLS_MEM +phoenix_v2.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +phoenix_v2.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +phoenix_v2.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +phoenix_v2.menu.lvl.HTTP_SERVER=HTTP_SERVER +phoenix_v2.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +phoenix_v2.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +phoenix_v2.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +phoenix_v2.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +phoenix_v2.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +phoenix_v2.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +phoenix_v2.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +phoenix_v2.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +phoenix_v2.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +phoenix_v2.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +phoenix_v2.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +phoenix_v2.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +phoenix_v2.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +phoenix_v2.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +phoenix_v2.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +phoenix_v2.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +phoenix_v2.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +phoenix_v2.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +phoenix_v2.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +phoenix_v2.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +phoenix_v2.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +phoenix_v2.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +phoenix_v2.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +phoenix_v2.menu.lvl.CORE=CORE +phoenix_v2.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +phoenix_v2.menu.lvl.WIFI=WIFI +phoenix_v2.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +phoenix_v2.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +phoenix_v2.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +phoenix_v2.menu.lvl.UPDATER=UPDATER +phoenix_v2.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +phoenix_v2.menu.lvl.OTA=OTA +phoenix_v2.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +phoenix_v2.menu.lvl.OOM=OOM +phoenix_v2.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +phoenix_v2.menu.lvl.MDNS=MDNS +phoenix_v2.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +phoenix_v2.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +phoenix_v2.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS +phoenix_v2.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +phoenix_v2.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS +phoenix_v2.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +phoenix_v2.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +phoenix_v2.menu.wipe.none=Only Sketch +phoenix_v2.menu.wipe.none.upload.erase_cmd=version +phoenix_v2.menu.wipe.sdk=Sketch + WiFi Settings +phoenix_v2.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 +phoenix_v2.menu.wipe.all=All Flash Contents +phoenix_v2.menu.wipe.all.upload.erase_cmd=erase_flash +phoenix_v2.menu.baud.115200=115200 +phoenix_v2.menu.baud.115200.upload.speed=115200 +phoenix_v2.menu.baud.57600=57600 +phoenix_v2.menu.baud.57600.upload.speed=57600 +phoenix_v2.menu.baud.230400.linux=230400 +phoenix_v2.menu.baud.230400.macosx=230400 +phoenix_v2.menu.baud.230400.upload.speed=230400 +phoenix_v2.menu.baud.256000.windows=256000 +phoenix_v2.menu.baud.256000.upload.speed=256000 +phoenix_v2.menu.baud.460800.linux=460800 +phoenix_v2.menu.baud.460800.macosx=460800 +phoenix_v2.menu.baud.460800.upload.speed=460800 +phoenix_v2.menu.baud.512000.windows=512000 +phoenix_v2.menu.baud.512000.upload.speed=512000 +phoenix_v2.menu.baud.921600=921600 +phoenix_v2.menu.baud.921600.upload.speed=921600 +phoenix_v2.menu.baud.3000000=3000000 +phoenix_v2.menu.baud.3000000.upload.speed=3000000 + +############################################################## +nodemcu.name=NodeMCU 0.9 (ESP-12 Module) +nodemcu.build.board=ESP8266_NODEMCU +nodemcu.build.variant=nodemcu +nodemcu.upload.tool=esptool +nodemcu.upload.maximum_data_size=81920 +nodemcu.upload.wait_for_upload_port=true +nodemcu.upload.erase_cmd=version +nodemcu.serial.disableDTR=true +nodemcu.serial.disableRTS=true +nodemcu.build.mcu=esp8266 +nodemcu.build.core=esp8266 +nodemcu.build.spiffs_pagesize=256 +nodemcu.build.debug_port= +nodemcu.build.debug_level= +nodemcu.menu.xtal.80=80 MHz +nodemcu.menu.xtal.80.build.f_cpu=80000000L +nodemcu.menu.xtal.160=160 MHz +nodemcu.menu.xtal.160.build.f_cpu=160000000L +nodemcu.menu.vt.flash=Flash +nodemcu.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +nodemcu.menu.vt.heap=Heap +nodemcu.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +nodemcu.menu.vt.iram=IRAM +nodemcu.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +nodemcu.menu.exception.legacy=Legacy (new can return nullptr) +nodemcu.menu.exception.legacy.build.exception_flags=-fno-exceptions +nodemcu.menu.exception.legacy.build.stdcpp_lib=-lstdc++ +nodemcu.menu.exception.disabled=Disabled (new can abort) +nodemcu.menu.exception.disabled.build.exception_flags=-fno-exceptions -DNEW_OOM_ABORT +nodemcu.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +nodemcu.menu.exception.enabled=Enabled +nodemcu.menu.exception.enabled.build.exception_flags=-fexceptions +nodemcu.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +nodemcu.menu.ssl.all=All SSL ciphers (most compatible) +nodemcu.menu.ssl.all.build.sslflags= +nodemcu.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +nodemcu.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +nodemcu.upload.resetmethod=nodemcu +nodemcu.build.flash_mode=qio +nodemcu.build.flash_flags=-DFLASHMODE_QIO +nodemcu.build.flash_freq=40 +nodemcu.menu.eesz.4M2M=4MB (FS:2MB OTA:~1019KB) +nodemcu.menu.eesz.4M2M.build.flash_size=4M +nodemcu.menu.eesz.4M2M.build.flash_size_bytes=0x400000 +nodemcu.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld +nodemcu.menu.eesz.4M2M.build.spiffs_pagesize=256 +nodemcu.menu.eesz.4M2M.upload.maximum_size=1044464 +nodemcu.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 +nodemcu.menu.eesz.4M2M.build.spiffs_start=0x200000 +nodemcu.menu.eesz.4M2M.build.spiffs_end=0x3FA000 +nodemcu.menu.eesz.4M2M.build.spiffs_blocksize=8192 +nodemcu.menu.eesz.4M3M=4MB (FS:3MB OTA:~512KB) +nodemcu.menu.eesz.4M3M.build.flash_size=4M +nodemcu.menu.eesz.4M3M.build.flash_size_bytes=0x400000 +nodemcu.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld +nodemcu.menu.eesz.4M3M.build.spiffs_pagesize=256 +nodemcu.menu.eesz.4M3M.upload.maximum_size=1044464 +nodemcu.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 +nodemcu.menu.eesz.4M3M.build.spiffs_start=0x100000 +nodemcu.menu.eesz.4M3M.build.spiffs_end=0x3FA000 +nodemcu.menu.eesz.4M3M.build.spiffs_blocksize=8192 +nodemcu.menu.eesz.4M1M=4MB (FS:1MB OTA:~1019KB) +nodemcu.menu.eesz.4M1M.build.flash_size=4M +nodemcu.menu.eesz.4M1M.build.flash_size_bytes=0x400000 +nodemcu.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld +nodemcu.menu.eesz.4M1M.build.spiffs_pagesize=256 +nodemcu.menu.eesz.4M1M.upload.maximum_size=1044464 +nodemcu.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 +nodemcu.menu.eesz.4M1M.build.spiffs_start=0x300000 +nodemcu.menu.eesz.4M1M.build.spiffs_end=0x3FA000 +nodemcu.menu.eesz.4M1M.build.spiffs_blocksize=8192 +nodemcu.menu.eesz.4M=4MB (FS:none OTA:~1019KB) +nodemcu.menu.eesz.4M.build.flash_size=4M +nodemcu.menu.eesz.4M.build.flash_size_bytes=0x400000 +nodemcu.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld +nodemcu.menu.eesz.4M.build.spiffs_pagesize=256 +nodemcu.menu.eesz.4M.upload.maximum_size=1044464 +nodemcu.menu.eesz.4M.build.rfcal_addr=0x3FC000 +nodemcu.menu.ip.lm2f=v2 Lower Memory +nodemcu.menu.ip.lm2f.build.lwip_include=lwip2/include +nodemcu.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +nodemcu.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +nodemcu.menu.ip.hb2f=v2 Higher Bandwidth +nodemcu.menu.ip.hb2f.build.lwip_include=lwip2/include +nodemcu.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +nodemcu.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +nodemcu.menu.ip.lm2n=v2 Lower Memory (no features) +nodemcu.menu.ip.lm2n.build.lwip_include=lwip2/include +nodemcu.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +nodemcu.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +nodemcu.menu.ip.hb2n=v2 Higher Bandwidth (no features) +nodemcu.menu.ip.hb2n.build.lwip_include=lwip2/include +nodemcu.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +nodemcu.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +nodemcu.menu.ip.lm6f=v2 IPv6 Lower Memory +nodemcu.menu.ip.lm6f.build.lwip_include=lwip2/include +nodemcu.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +nodemcu.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +nodemcu.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +nodemcu.menu.ip.hb6f.build.lwip_include=lwip2/include +nodemcu.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +nodemcu.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +nodemcu.menu.ip.hb1=v1.4 Higher Bandwidth +nodemcu.menu.ip.hb1.build.lwip_lib=-llwip_gcc +nodemcu.menu.ip.hb1.build.lwip_flags=-DLWIP_OPEN_SRC +nodemcu.menu.ip.src=v1.4 Compile from source +nodemcu.menu.ip.src.build.lwip_lib=-llwip_src +nodemcu.menu.ip.src.build.lwip_flags=-DLWIP_OPEN_SRC +nodemcu.menu.ip.src.recipe.hooks.sketch.prebuild.1.pattern=make -C "{runtime.platform.path}/tools/sdk/lwip/src" install TOOLS_PATH="{runtime.tools.xtensa-lx106-elf-gcc.path}/bin/xtensa-lx106-elf-" +nodemcu.menu.dbg.Disabled=Disabled +nodemcu.menu.dbg.Disabled.build.debug_port= +nodemcu.menu.dbg.Serial=Serial +nodemcu.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +nodemcu.menu.dbg.Serial1=Serial1 +nodemcu.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +nodemcu.menu.lvl.None____=None +nodemcu.menu.lvl.None____.build.debug_level= +nodemcu.menu.lvl.SSL=SSL +nodemcu.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +nodemcu.menu.lvl.TLS_MEM=TLS_MEM +nodemcu.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +nodemcu.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +nodemcu.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +nodemcu.menu.lvl.HTTP_SERVER=HTTP_SERVER +nodemcu.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +nodemcu.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +nodemcu.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +nodemcu.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +nodemcu.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +nodemcu.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +nodemcu.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +nodemcu.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +nodemcu.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +nodemcu.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +nodemcu.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +nodemcu.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +nodemcu.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +nodemcu.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +nodemcu.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +nodemcu.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +nodemcu.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +nodemcu.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +nodemcu.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +nodemcu.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +nodemcu.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +nodemcu.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +nodemcu.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +nodemcu.menu.lvl.CORE=CORE +nodemcu.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +nodemcu.menu.lvl.WIFI=WIFI +nodemcu.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +nodemcu.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +nodemcu.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +nodemcu.menu.lvl.UPDATER=UPDATER +nodemcu.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +nodemcu.menu.lvl.OTA=OTA +nodemcu.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +nodemcu.menu.lvl.OOM=OOM +nodemcu.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +nodemcu.menu.lvl.MDNS=MDNS +nodemcu.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +nodemcu.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +nodemcu.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS +nodemcu.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +nodemcu.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS +nodemcu.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +nodemcu.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +nodemcu.menu.wipe.none=Only Sketch +nodemcu.menu.wipe.none.upload.erase_cmd=version +nodemcu.menu.wipe.sdk=Sketch + WiFi Settings +nodemcu.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 +nodemcu.menu.wipe.all=All Flash Contents +nodemcu.menu.wipe.all.upload.erase_cmd=erase_flash +nodemcu.menu.baud.115200=115200 +nodemcu.menu.baud.115200.upload.speed=115200 +nodemcu.menu.baud.57600=57600 +nodemcu.menu.baud.57600.upload.speed=57600 +nodemcu.menu.baud.230400.linux=230400 +nodemcu.menu.baud.230400.macosx=230400 +nodemcu.menu.baud.230400.upload.speed=230400 +nodemcu.menu.baud.256000.windows=256000 +nodemcu.menu.baud.256000.upload.speed=256000 +nodemcu.menu.baud.460800.linux=460800 +nodemcu.menu.baud.460800.macosx=460800 +nodemcu.menu.baud.460800.upload.speed=460800 +nodemcu.menu.baud.512000.windows=512000 +nodemcu.menu.baud.512000.upload.speed=512000 +nodemcu.menu.baud.921600=921600 +nodemcu.menu.baud.921600.upload.speed=921600 +nodemcu.menu.baud.3000000=3000000 +nodemcu.menu.baud.3000000.upload.speed=3000000 + +############################################################## +nodemcuv2.name=NodeMCU 1.0 (ESP-12E Module) +nodemcuv2.build.board=ESP8266_NODEMCU +nodemcuv2.build.variant=nodemcu +nodemcuv2.upload.tool=esptool +nodemcuv2.upload.maximum_data_size=81920 +nodemcuv2.upload.wait_for_upload_port=true +nodemcuv2.upload.erase_cmd=version +nodemcuv2.serial.disableDTR=true +nodemcuv2.serial.disableRTS=true +nodemcuv2.build.mcu=esp8266 +nodemcuv2.build.core=esp8266 +nodemcuv2.build.spiffs_pagesize=256 +nodemcuv2.build.debug_port= +nodemcuv2.build.debug_level= +nodemcuv2.menu.xtal.80=80 MHz +nodemcuv2.menu.xtal.80.build.f_cpu=80000000L +nodemcuv2.menu.xtal.160=160 MHz +nodemcuv2.menu.xtal.160.build.f_cpu=160000000L +nodemcuv2.menu.vt.flash=Flash +nodemcuv2.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +nodemcuv2.menu.vt.heap=Heap +nodemcuv2.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +nodemcuv2.menu.vt.iram=IRAM +nodemcuv2.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +nodemcuv2.menu.exception.legacy=Legacy (new can return nullptr) +nodemcuv2.menu.exception.legacy.build.exception_flags=-fno-exceptions +nodemcuv2.menu.exception.legacy.build.stdcpp_lib=-lstdc++ +nodemcuv2.menu.exception.disabled=Disabled (new can abort) +nodemcuv2.menu.exception.disabled.build.exception_flags=-fno-exceptions -DNEW_OOM_ABORT +nodemcuv2.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +nodemcuv2.menu.exception.enabled=Enabled +nodemcuv2.menu.exception.enabled.build.exception_flags=-fexceptions +nodemcuv2.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +nodemcuv2.menu.ssl.all=All SSL ciphers (most compatible) +nodemcuv2.menu.ssl.all.build.sslflags= +nodemcuv2.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +nodemcuv2.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +nodemcuv2.upload.resetmethod=nodemcu +nodemcuv2.build.flash_mode=dio +nodemcuv2.build.flash_flags=-DFLASHMODE_DIO +nodemcuv2.build.flash_freq=40 +nodemcuv2.menu.eesz.4M2M=4MB (FS:2MB OTA:~1019KB) +nodemcuv2.menu.eesz.4M2M.build.flash_size=4M +nodemcuv2.menu.eesz.4M2M.build.flash_size_bytes=0x400000 +nodemcuv2.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld +nodemcuv2.menu.eesz.4M2M.build.spiffs_pagesize=256 +nodemcuv2.menu.eesz.4M2M.upload.maximum_size=1044464 +nodemcuv2.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 +nodemcuv2.menu.eesz.4M2M.build.spiffs_start=0x200000 +nodemcuv2.menu.eesz.4M2M.build.spiffs_end=0x3FA000 +nodemcuv2.menu.eesz.4M2M.build.spiffs_blocksize=8192 +nodemcuv2.menu.eesz.4M3M=4MB (FS:3MB OTA:~512KB) +nodemcuv2.menu.eesz.4M3M.build.flash_size=4M +nodemcuv2.menu.eesz.4M3M.build.flash_size_bytes=0x400000 +nodemcuv2.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld +nodemcuv2.menu.eesz.4M3M.build.spiffs_pagesize=256 +nodemcuv2.menu.eesz.4M3M.upload.maximum_size=1044464 +nodemcuv2.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 +nodemcuv2.menu.eesz.4M3M.build.spiffs_start=0x100000 +nodemcuv2.menu.eesz.4M3M.build.spiffs_end=0x3FA000 +nodemcuv2.menu.eesz.4M3M.build.spiffs_blocksize=8192 +nodemcuv2.menu.eesz.4M1M=4MB (FS:1MB OTA:~1019KB) +nodemcuv2.menu.eesz.4M1M.build.flash_size=4M +nodemcuv2.menu.eesz.4M1M.build.flash_size_bytes=0x400000 +nodemcuv2.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld +nodemcuv2.menu.eesz.4M1M.build.spiffs_pagesize=256 +nodemcuv2.menu.eesz.4M1M.upload.maximum_size=1044464 +nodemcuv2.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 +nodemcuv2.menu.eesz.4M1M.build.spiffs_start=0x300000 +nodemcuv2.menu.eesz.4M1M.build.spiffs_end=0x3FA000 +nodemcuv2.menu.eesz.4M1M.build.spiffs_blocksize=8192 +nodemcuv2.menu.eesz.4M=4MB (FS:none OTA:~1019KB) +nodemcuv2.menu.eesz.4M.build.flash_size=4M +nodemcuv2.menu.eesz.4M.build.flash_size_bytes=0x400000 +nodemcuv2.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld +nodemcuv2.menu.eesz.4M.build.spiffs_pagesize=256 +nodemcuv2.menu.eesz.4M.upload.maximum_size=1044464 +nodemcuv2.menu.eesz.4M.build.rfcal_addr=0x3FC000 +nodemcuv2.menu.ip.lm2f=v2 Lower Memory +nodemcuv2.menu.ip.lm2f.build.lwip_include=lwip2/include +nodemcuv2.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +nodemcuv2.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +nodemcuv2.menu.ip.hb2f=v2 Higher Bandwidth +nodemcuv2.menu.ip.hb2f.build.lwip_include=lwip2/include +nodemcuv2.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +nodemcuv2.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +nodemcuv2.menu.ip.lm2n=v2 Lower Memory (no features) +nodemcuv2.menu.ip.lm2n.build.lwip_include=lwip2/include +nodemcuv2.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +nodemcuv2.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +nodemcuv2.menu.ip.hb2n=v2 Higher Bandwidth (no features) +nodemcuv2.menu.ip.hb2n.build.lwip_include=lwip2/include +nodemcuv2.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +nodemcuv2.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +nodemcuv2.menu.ip.lm6f=v2 IPv6 Lower Memory +nodemcuv2.menu.ip.lm6f.build.lwip_include=lwip2/include +nodemcuv2.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +nodemcuv2.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +nodemcuv2.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +nodemcuv2.menu.ip.hb6f.build.lwip_include=lwip2/include +nodemcuv2.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +nodemcuv2.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +nodemcuv2.menu.ip.hb1=v1.4 Higher Bandwidth +nodemcuv2.menu.ip.hb1.build.lwip_lib=-llwip_gcc +nodemcuv2.menu.ip.hb1.build.lwip_flags=-DLWIP_OPEN_SRC +nodemcuv2.menu.ip.src=v1.4 Compile from source +nodemcuv2.menu.ip.src.build.lwip_lib=-llwip_src +nodemcuv2.menu.ip.src.build.lwip_flags=-DLWIP_OPEN_SRC +nodemcuv2.menu.ip.src.recipe.hooks.sketch.prebuild.1.pattern=make -C "{runtime.platform.path}/tools/sdk/lwip/src" install TOOLS_PATH="{runtime.tools.xtensa-lx106-elf-gcc.path}/bin/xtensa-lx106-elf-" +nodemcuv2.menu.dbg.Disabled=Disabled +nodemcuv2.menu.dbg.Disabled.build.debug_port= +nodemcuv2.menu.dbg.Serial=Serial +nodemcuv2.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +nodemcuv2.menu.dbg.Serial1=Serial1 +nodemcuv2.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +nodemcuv2.menu.lvl.None____=None +nodemcuv2.menu.lvl.None____.build.debug_level= +nodemcuv2.menu.lvl.SSL=SSL +nodemcuv2.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +nodemcuv2.menu.lvl.TLS_MEM=TLS_MEM +nodemcuv2.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +nodemcuv2.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +nodemcuv2.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +nodemcuv2.menu.lvl.HTTP_SERVER=HTTP_SERVER +nodemcuv2.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +nodemcuv2.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +nodemcuv2.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +nodemcuv2.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +nodemcuv2.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +nodemcuv2.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +nodemcuv2.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +nodemcuv2.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +nodemcuv2.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +nodemcuv2.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +nodemcuv2.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +nodemcuv2.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +nodemcuv2.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +nodemcuv2.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +nodemcuv2.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +nodemcuv2.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +nodemcuv2.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +nodemcuv2.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +nodemcuv2.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +nodemcuv2.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +nodemcuv2.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +nodemcuv2.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +nodemcuv2.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +nodemcuv2.menu.lvl.CORE=CORE +nodemcuv2.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +nodemcuv2.menu.lvl.WIFI=WIFI +nodemcuv2.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +nodemcuv2.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +nodemcuv2.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +nodemcuv2.menu.lvl.UPDATER=UPDATER +nodemcuv2.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +nodemcuv2.menu.lvl.OTA=OTA +nodemcuv2.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +nodemcuv2.menu.lvl.OOM=OOM +nodemcuv2.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +nodemcuv2.menu.lvl.MDNS=MDNS +nodemcuv2.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +nodemcuv2.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +nodemcuv2.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS +nodemcuv2.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +nodemcuv2.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS +nodemcuv2.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +nodemcuv2.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +nodemcuv2.menu.wipe.none=Only Sketch +nodemcuv2.menu.wipe.none.upload.erase_cmd=version +nodemcuv2.menu.wipe.sdk=Sketch + WiFi Settings +nodemcuv2.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 +nodemcuv2.menu.wipe.all=All Flash Contents +nodemcuv2.menu.wipe.all.upload.erase_cmd=erase_flash +nodemcuv2.menu.baud.115200=115200 +nodemcuv2.menu.baud.115200.upload.speed=115200 +nodemcuv2.menu.baud.57600=57600 +nodemcuv2.menu.baud.57600.upload.speed=57600 +nodemcuv2.menu.baud.230400.linux=230400 +nodemcuv2.menu.baud.230400.macosx=230400 +nodemcuv2.menu.baud.230400.upload.speed=230400 +nodemcuv2.menu.baud.256000.windows=256000 +nodemcuv2.menu.baud.256000.upload.speed=256000 +nodemcuv2.menu.baud.460800.linux=460800 +nodemcuv2.menu.baud.460800.macosx=460800 +nodemcuv2.menu.baud.460800.upload.speed=460800 +nodemcuv2.menu.baud.512000.windows=512000 +nodemcuv2.menu.baud.512000.upload.speed=512000 +nodemcuv2.menu.baud.921600=921600 +nodemcuv2.menu.baud.921600.upload.speed=921600 +nodemcuv2.menu.baud.3000000=3000000 +nodemcuv2.menu.baud.3000000.upload.speed=3000000 + +############################################################## +modwifi.name=Olimex MOD-WIFI-ESP8266(-DEV) +modwifi.build.board=MOD_WIFI_ESP8266 +modwifi.build.variant=modwifi +modwifi.upload.tool=esptool +modwifi.upload.maximum_data_size=81920 +modwifi.upload.wait_for_upload_port=true +modwifi.upload.erase_cmd=version +modwifi.serial.disableDTR=true +modwifi.serial.disableRTS=true +modwifi.build.mcu=esp8266 +modwifi.build.core=esp8266 +modwifi.build.spiffs_pagesize=256 +modwifi.build.debug_port= +modwifi.build.debug_level= +modwifi.menu.xtal.80=80 MHz +modwifi.menu.xtal.80.build.f_cpu=80000000L +modwifi.menu.xtal.160=160 MHz +modwifi.menu.xtal.160.build.f_cpu=160000000L +modwifi.menu.vt.flash=Flash +modwifi.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +modwifi.menu.vt.heap=Heap +modwifi.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +modwifi.menu.vt.iram=IRAM +modwifi.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +modwifi.menu.exception.legacy=Legacy (new can return nullptr) +modwifi.menu.exception.legacy.build.exception_flags=-fno-exceptions +modwifi.menu.exception.legacy.build.stdcpp_lib=-lstdc++ +modwifi.menu.exception.disabled=Disabled (new can abort) +modwifi.menu.exception.disabled.build.exception_flags=-fno-exceptions -DNEW_OOM_ABORT +modwifi.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +modwifi.menu.exception.enabled=Enabled +modwifi.menu.exception.enabled.build.exception_flags=-fexceptions +modwifi.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +modwifi.menu.ssl.all=All SSL ciphers (most compatible) +modwifi.menu.ssl.all.build.sslflags= +modwifi.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +modwifi.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +modwifi.upload.resetmethod=ck +modwifi.build.flash_mode=qio +modwifi.build.flash_flags=-DFLASHMODE_QIO +modwifi.build.flash_freq=40 +modwifi.menu.eesz.2M64=2MB (FS:64KB OTA:~992KB) +modwifi.menu.eesz.2M64.build.flash_size=2M +modwifi.menu.eesz.2M64.build.flash_size_bytes=0x200000 +modwifi.menu.eesz.2M64.build.flash_ld=eagle.flash.2m64.ld +modwifi.menu.eesz.2M64.build.spiffs_pagesize=256 +modwifi.menu.eesz.2M64.upload.maximum_size=1044464 +modwifi.menu.eesz.2M64.build.rfcal_addr=0x1FC000 +modwifi.menu.eesz.2M64.build.spiffs_start=0x1F0000 +modwifi.menu.eesz.2M64.build.spiffs_end=0x1FB000 +modwifi.menu.eesz.2M64.build.spiffs_blocksize=4096 +modwifi.menu.eesz.2M128=2MB (FS:128KB OTA:~960KB) +modwifi.menu.eesz.2M128.build.flash_size=2M +modwifi.menu.eesz.2M128.build.flash_size_bytes=0x200000 +modwifi.menu.eesz.2M128.build.flash_ld=eagle.flash.2m128.ld +modwifi.menu.eesz.2M128.build.spiffs_pagesize=256 +modwifi.menu.eesz.2M128.upload.maximum_size=1044464 +modwifi.menu.eesz.2M128.build.rfcal_addr=0x1FC000 +modwifi.menu.eesz.2M128.build.spiffs_start=0x1E0000 +modwifi.menu.eesz.2M128.build.spiffs_end=0x1FB000 +modwifi.menu.eesz.2M128.build.spiffs_blocksize=4096 +modwifi.menu.eesz.2M256=2MB (FS:256KB OTA:~896KB) +modwifi.menu.eesz.2M256.build.flash_size=2M +modwifi.menu.eesz.2M256.build.flash_size_bytes=0x200000 +modwifi.menu.eesz.2M256.build.flash_ld=eagle.flash.2m256.ld +modwifi.menu.eesz.2M256.build.spiffs_pagesize=256 +modwifi.menu.eesz.2M256.upload.maximum_size=1044464 +modwifi.menu.eesz.2M256.build.rfcal_addr=0x1FC000 +modwifi.menu.eesz.2M256.build.spiffs_start=0x1C0000 +modwifi.menu.eesz.2M256.build.spiffs_end=0x1FB000 +modwifi.menu.eesz.2M256.build.spiffs_blocksize=4096 +modwifi.menu.eesz.2M512=2MB (FS:512KB OTA:~768KB) +modwifi.menu.eesz.2M512.build.flash_size=2M +modwifi.menu.eesz.2M512.build.flash_size_bytes=0x200000 +modwifi.menu.eesz.2M512.build.flash_ld=eagle.flash.2m512.ld +modwifi.menu.eesz.2M512.build.spiffs_pagesize=256 +modwifi.menu.eesz.2M512.upload.maximum_size=1044464 +modwifi.menu.eesz.2M512.build.rfcal_addr=0x1FC000 +modwifi.menu.eesz.2M512.build.spiffs_start=0x180000 +modwifi.menu.eesz.2M512.build.spiffs_end=0x1FA000 +modwifi.menu.eesz.2M512.build.spiffs_blocksize=8192 +modwifi.menu.eesz.2M1M=2MB (FS:1MB OTA:~512KB) +modwifi.menu.eesz.2M1M.build.flash_size=2M +modwifi.menu.eesz.2M1M.build.flash_size_bytes=0x200000 +modwifi.menu.eesz.2M1M.build.flash_ld=eagle.flash.2m1m.ld +modwifi.menu.eesz.2M1M.build.spiffs_pagesize=256 +modwifi.menu.eesz.2M1M.upload.maximum_size=1044464 +modwifi.menu.eesz.2M1M.build.rfcal_addr=0x1FC000 +modwifi.menu.eesz.2M1M.build.spiffs_start=0x100000 +modwifi.menu.eesz.2M1M.build.spiffs_end=0x1FA000 +modwifi.menu.eesz.2M1M.build.spiffs_blocksize=8192 +modwifi.menu.eesz.2M=2MB (FS:none OTA:~1019KB) +modwifi.menu.eesz.2M.build.flash_size=2M +modwifi.menu.eesz.2M.build.flash_size_bytes=0x200000 +modwifi.menu.eesz.2M.build.flash_ld=eagle.flash.2m.ld +modwifi.menu.eesz.2M.build.spiffs_pagesize=256 +modwifi.menu.eesz.2M.upload.maximum_size=1044464 +modwifi.menu.eesz.2M.build.rfcal_addr=0x1FC000 +modwifi.menu.ip.lm2f=v2 Lower Memory +modwifi.menu.ip.lm2f.build.lwip_include=lwip2/include +modwifi.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +modwifi.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +modwifi.menu.ip.hb2f=v2 Higher Bandwidth +modwifi.menu.ip.hb2f.build.lwip_include=lwip2/include +modwifi.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +modwifi.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +modwifi.menu.ip.lm2n=v2 Lower Memory (no features) +modwifi.menu.ip.lm2n.build.lwip_include=lwip2/include +modwifi.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +modwifi.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +modwifi.menu.ip.hb2n=v2 Higher Bandwidth (no features) +modwifi.menu.ip.hb2n.build.lwip_include=lwip2/include +modwifi.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +modwifi.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +modwifi.menu.ip.lm6f=v2 IPv6 Lower Memory +modwifi.menu.ip.lm6f.build.lwip_include=lwip2/include +modwifi.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +modwifi.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +modwifi.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +modwifi.menu.ip.hb6f.build.lwip_include=lwip2/include +modwifi.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +modwifi.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +modwifi.menu.ip.hb1=v1.4 Higher Bandwidth +modwifi.menu.ip.hb1.build.lwip_lib=-llwip_gcc +modwifi.menu.ip.hb1.build.lwip_flags=-DLWIP_OPEN_SRC +modwifi.menu.ip.src=v1.4 Compile from source +modwifi.menu.ip.src.build.lwip_lib=-llwip_src +modwifi.menu.ip.src.build.lwip_flags=-DLWIP_OPEN_SRC +modwifi.menu.ip.src.recipe.hooks.sketch.prebuild.1.pattern=make -C "{runtime.platform.path}/tools/sdk/lwip/src" install TOOLS_PATH="{runtime.tools.xtensa-lx106-elf-gcc.path}/bin/xtensa-lx106-elf-" +modwifi.menu.dbg.Disabled=Disabled +modwifi.menu.dbg.Disabled.build.debug_port= +modwifi.menu.dbg.Serial=Serial +modwifi.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +modwifi.menu.dbg.Serial1=Serial1 +modwifi.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +modwifi.menu.lvl.None____=None +modwifi.menu.lvl.None____.build.debug_level= +modwifi.menu.lvl.SSL=SSL +modwifi.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +modwifi.menu.lvl.TLS_MEM=TLS_MEM +modwifi.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +modwifi.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +modwifi.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +modwifi.menu.lvl.HTTP_SERVER=HTTP_SERVER +modwifi.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +modwifi.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +modwifi.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +modwifi.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +modwifi.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +modwifi.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +modwifi.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +modwifi.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +modwifi.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +modwifi.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +modwifi.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +modwifi.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +modwifi.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +modwifi.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +modwifi.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +modwifi.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +modwifi.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +modwifi.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +modwifi.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +modwifi.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +modwifi.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +modwifi.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +modwifi.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +modwifi.menu.lvl.CORE=CORE +modwifi.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +modwifi.menu.lvl.WIFI=WIFI +modwifi.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +modwifi.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +modwifi.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +modwifi.menu.lvl.UPDATER=UPDATER +modwifi.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +modwifi.menu.lvl.OTA=OTA +modwifi.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +modwifi.menu.lvl.OOM=OOM +modwifi.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +modwifi.menu.lvl.MDNS=MDNS +modwifi.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +modwifi.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +modwifi.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS +modwifi.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +modwifi.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS +modwifi.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +modwifi.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +modwifi.menu.wipe.none=Only Sketch +modwifi.menu.wipe.none.upload.erase_cmd=version +modwifi.menu.wipe.sdk=Sketch + WiFi Settings +modwifi.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 +modwifi.menu.wipe.all=All Flash Contents +modwifi.menu.wipe.all.upload.erase_cmd=erase_flash +modwifi.menu.baud.115200=115200 +modwifi.menu.baud.115200.upload.speed=115200 +modwifi.menu.baud.57600=57600 +modwifi.menu.baud.57600.upload.speed=57600 +modwifi.menu.baud.230400.linux=230400 +modwifi.menu.baud.230400.macosx=230400 +modwifi.menu.baud.230400.upload.speed=230400 +modwifi.menu.baud.256000.windows=256000 +modwifi.menu.baud.256000.upload.speed=256000 +modwifi.menu.baud.460800.linux=460800 +modwifi.menu.baud.460800.macosx=460800 +modwifi.menu.baud.460800.upload.speed=460800 +modwifi.menu.baud.512000.windows=512000 +modwifi.menu.baud.512000.upload.speed=512000 +modwifi.menu.baud.921600=921600 +modwifi.menu.baud.921600.upload.speed=921600 +modwifi.menu.baud.3000000=3000000 +modwifi.menu.baud.3000000.upload.speed=3000000 + +############################################################## +thing.name=SparkFun ESP8266 Thing +thing.build.board=ESP8266_THING +thing.build.variant=thing +thing.upload.tool=esptool +thing.upload.maximum_data_size=81920 +thing.upload.wait_for_upload_port=true +thing.upload.erase_cmd=version +thing.serial.disableDTR=true +thing.serial.disableRTS=true +thing.build.mcu=esp8266 +thing.build.core=esp8266 +thing.build.spiffs_pagesize=256 +thing.build.debug_port= +thing.build.debug_level= +thing.menu.xtal.80=80 MHz +thing.menu.xtal.80.build.f_cpu=80000000L +thing.menu.xtal.160=160 MHz +thing.menu.xtal.160.build.f_cpu=160000000L +thing.menu.vt.flash=Flash +thing.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +thing.menu.vt.heap=Heap +thing.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +thing.menu.vt.iram=IRAM +thing.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +thing.menu.exception.legacy=Legacy (new can return nullptr) +thing.menu.exception.legacy.build.exception_flags=-fno-exceptions +thing.menu.exception.legacy.build.stdcpp_lib=-lstdc++ +thing.menu.exception.disabled=Disabled (new can abort) +thing.menu.exception.disabled.build.exception_flags=-fno-exceptions -DNEW_OOM_ABORT +thing.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +thing.menu.exception.enabled=Enabled +thing.menu.exception.enabled.build.exception_flags=-fexceptions +thing.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +thing.menu.ssl.all=All SSL ciphers (most compatible) +thing.menu.ssl.all.build.sslflags= +thing.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +thing.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +thing.upload.resetmethod=ck +thing.build.flash_mode=qio +thing.build.flash_flags=-DFLASHMODE_QIO +thing.build.flash_freq=40 +thing.menu.eesz.512K32=512KB (FS:32KB OTA:~230KB) +thing.menu.eesz.512K32.build.flash_size=512K +thing.menu.eesz.512K32.build.flash_size_bytes=0x80000 +thing.menu.eesz.512K32.build.flash_ld=eagle.flash.512k32.ld +thing.menu.eesz.512K32.build.spiffs_pagesize=256 +thing.menu.eesz.512K32.upload.maximum_size=466928 +thing.menu.eesz.512K32.build.rfcal_addr=0x7C000 +thing.menu.eesz.512K32.build.spiffs_start=0x73000 +thing.menu.eesz.512K32.build.spiffs_end=0x7B000 +thing.menu.eesz.512K32.build.spiffs_blocksize=4096 +thing.menu.eesz.512K64=512KB (FS:64KB OTA:~214KB) +thing.menu.eesz.512K64.build.flash_size=512K +thing.menu.eesz.512K64.build.flash_size_bytes=0x80000 +thing.menu.eesz.512K64.build.flash_ld=eagle.flash.512k64.ld +thing.menu.eesz.512K64.build.spiffs_pagesize=256 +thing.menu.eesz.512K64.upload.maximum_size=434160 +thing.menu.eesz.512K64.build.rfcal_addr=0x7C000 +thing.menu.eesz.512K64.build.spiffs_start=0x6B000 +thing.menu.eesz.512K64.build.spiffs_end=0x7B000 +thing.menu.eesz.512K64.build.spiffs_blocksize=4096 +thing.menu.eesz.512K128=512KB (FS:128KB OTA:~182KB) +thing.menu.eesz.512K128.build.flash_size=512K +thing.menu.eesz.512K128.build.flash_size_bytes=0x80000 +thing.menu.eesz.512K128.build.flash_ld=eagle.flash.512k128.ld +thing.menu.eesz.512K128.build.spiffs_pagesize=256 +thing.menu.eesz.512K128.upload.maximum_size=368624 +thing.menu.eesz.512K128.build.rfcal_addr=0x7C000 +thing.menu.eesz.512K128.build.spiffs_start=0x5B000 +thing.menu.eesz.512K128.build.spiffs_end=0x7B000 +thing.menu.eesz.512K128.build.spiffs_blocksize=4096 +thing.menu.eesz.512K=512KB (FS:none OTA:~246KB) +thing.menu.eesz.512K.build.flash_size=512K +thing.menu.eesz.512K.build.flash_size_bytes=0x80000 +thing.menu.eesz.512K.build.flash_ld=eagle.flash.512k.ld +thing.menu.eesz.512K.build.spiffs_pagesize=256 +thing.menu.eesz.512K.upload.maximum_size=499696 +thing.menu.eesz.512K.build.rfcal_addr=0x7C000 +thing.menu.ip.lm2f=v2 Lower Memory +thing.menu.ip.lm2f.build.lwip_include=lwip2/include +thing.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +thing.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +thing.menu.ip.hb2f=v2 Higher Bandwidth +thing.menu.ip.hb2f.build.lwip_include=lwip2/include +thing.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +thing.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +thing.menu.ip.lm2n=v2 Lower Memory (no features) +thing.menu.ip.lm2n.build.lwip_include=lwip2/include +thing.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +thing.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +thing.menu.ip.hb2n=v2 Higher Bandwidth (no features) +thing.menu.ip.hb2n.build.lwip_include=lwip2/include +thing.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +thing.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +thing.menu.ip.lm6f=v2 IPv6 Lower Memory +thing.menu.ip.lm6f.build.lwip_include=lwip2/include +thing.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +thing.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +thing.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +thing.menu.ip.hb6f.build.lwip_include=lwip2/include +thing.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +thing.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +thing.menu.ip.hb1=v1.4 Higher Bandwidth +thing.menu.ip.hb1.build.lwip_lib=-llwip_gcc +thing.menu.ip.hb1.build.lwip_flags=-DLWIP_OPEN_SRC +thing.menu.ip.src=v1.4 Compile from source +thing.menu.ip.src.build.lwip_lib=-llwip_src +thing.menu.ip.src.build.lwip_flags=-DLWIP_OPEN_SRC +thing.menu.ip.src.recipe.hooks.sketch.prebuild.1.pattern=make -C "{runtime.platform.path}/tools/sdk/lwip/src" install TOOLS_PATH="{runtime.tools.xtensa-lx106-elf-gcc.path}/bin/xtensa-lx106-elf-" +thing.menu.dbg.Disabled=Disabled +thing.menu.dbg.Disabled.build.debug_port= +thing.menu.dbg.Serial=Serial +thing.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +thing.menu.dbg.Serial1=Serial1 +thing.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +thing.menu.lvl.None____=None +thing.menu.lvl.None____.build.debug_level= +thing.menu.lvl.SSL=SSL +thing.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +thing.menu.lvl.TLS_MEM=TLS_MEM +thing.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +thing.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +thing.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +thing.menu.lvl.HTTP_SERVER=HTTP_SERVER +thing.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +thing.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +thing.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +thing.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +thing.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +thing.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +thing.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +thing.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +thing.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +thing.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +thing.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +thing.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +thing.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +thing.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +thing.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +thing.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +thing.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +thing.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +thing.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +thing.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +thing.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +thing.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +thing.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +thing.menu.lvl.CORE=CORE +thing.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +thing.menu.lvl.WIFI=WIFI +thing.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +thing.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +thing.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +thing.menu.lvl.UPDATER=UPDATER +thing.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +thing.menu.lvl.OTA=OTA +thing.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +thing.menu.lvl.OOM=OOM +thing.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +thing.menu.lvl.MDNS=MDNS +thing.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +thing.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +thing.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS +thing.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +thing.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS +thing.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +thing.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +thing.menu.wipe.none=Only Sketch +thing.menu.wipe.none.upload.erase_cmd=version +thing.menu.wipe.sdk=Sketch + WiFi Settings +thing.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 +thing.menu.wipe.all=All Flash Contents +thing.menu.wipe.all.upload.erase_cmd=erase_flash +thing.menu.baud.115200=115200 +thing.menu.baud.115200.upload.speed=115200 +thing.menu.baud.57600=57600 +thing.menu.baud.57600.upload.speed=57600 +thing.menu.baud.230400.linux=230400 +thing.menu.baud.230400.macosx=230400 +thing.menu.baud.230400.upload.speed=230400 +thing.menu.baud.256000.windows=256000 +thing.menu.baud.256000.upload.speed=256000 +thing.menu.baud.460800.linux=460800 +thing.menu.baud.460800.macosx=460800 +thing.menu.baud.460800.upload.speed=460800 +thing.menu.baud.512000.windows=512000 +thing.menu.baud.512000.upload.speed=512000 +thing.menu.baud.921600=921600 +thing.menu.baud.921600.upload.speed=921600 +thing.menu.baud.3000000=3000000 +thing.menu.baud.3000000.upload.speed=3000000 + +############################################################## +thingdev.name=SparkFun ESP8266 Thing Dev +thingdev.build.board=ESP8266_THING_DEV +thingdev.build.variant=thing +thingdev.upload.tool=esptool +thingdev.upload.maximum_data_size=81920 +thingdev.upload.wait_for_upload_port=true +thingdev.upload.erase_cmd=version +thingdev.serial.disableDTR=true +thingdev.serial.disableRTS=true +thingdev.build.mcu=esp8266 +thingdev.build.core=esp8266 +thingdev.build.spiffs_pagesize=256 +thingdev.build.debug_port= +thingdev.build.debug_level= +thingdev.menu.xtal.80=80 MHz +thingdev.menu.xtal.80.build.f_cpu=80000000L +thingdev.menu.xtal.160=160 MHz +thingdev.menu.xtal.160.build.f_cpu=160000000L +thingdev.menu.vt.flash=Flash +thingdev.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +thingdev.menu.vt.heap=Heap +thingdev.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +thingdev.menu.vt.iram=IRAM +thingdev.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +thingdev.menu.exception.legacy=Legacy (new can return nullptr) +thingdev.menu.exception.legacy.build.exception_flags=-fno-exceptions +thingdev.menu.exception.legacy.build.stdcpp_lib=-lstdc++ +thingdev.menu.exception.disabled=Disabled (new can abort) +thingdev.menu.exception.disabled.build.exception_flags=-fno-exceptions -DNEW_OOM_ABORT +thingdev.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +thingdev.menu.exception.enabled=Enabled +thingdev.menu.exception.enabled.build.exception_flags=-fexceptions +thingdev.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +thingdev.menu.ssl.all=All SSL ciphers (most compatible) +thingdev.menu.ssl.all.build.sslflags= +thingdev.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +thingdev.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +thingdev.upload.resetmethod=nodemcu +thingdev.build.flash_mode=dio +thingdev.build.flash_flags=-DFLASHMODE_DIO +thingdev.build.flash_freq=40 +thingdev.menu.eesz.512K32=512KB (FS:32KB OTA:~230KB) +thingdev.menu.eesz.512K32.build.flash_size=512K +thingdev.menu.eesz.512K32.build.flash_size_bytes=0x80000 +thingdev.menu.eesz.512K32.build.flash_ld=eagle.flash.512k32.ld +thingdev.menu.eesz.512K32.build.spiffs_pagesize=256 +thingdev.menu.eesz.512K32.upload.maximum_size=466928 +thingdev.menu.eesz.512K32.build.rfcal_addr=0x7C000 +thingdev.menu.eesz.512K32.build.spiffs_start=0x73000 +thingdev.menu.eesz.512K32.build.spiffs_end=0x7B000 +thingdev.menu.eesz.512K32.build.spiffs_blocksize=4096 +thingdev.menu.eesz.512K64=512KB (FS:64KB OTA:~214KB) +thingdev.menu.eesz.512K64.build.flash_size=512K +thingdev.menu.eesz.512K64.build.flash_size_bytes=0x80000 +thingdev.menu.eesz.512K64.build.flash_ld=eagle.flash.512k64.ld +thingdev.menu.eesz.512K64.build.spiffs_pagesize=256 +thingdev.menu.eesz.512K64.upload.maximum_size=434160 +thingdev.menu.eesz.512K64.build.rfcal_addr=0x7C000 +thingdev.menu.eesz.512K64.build.spiffs_start=0x6B000 +thingdev.menu.eesz.512K64.build.spiffs_end=0x7B000 +thingdev.menu.eesz.512K64.build.spiffs_blocksize=4096 +thingdev.menu.eesz.512K128=512KB (FS:128KB OTA:~182KB) +thingdev.menu.eesz.512K128.build.flash_size=512K +thingdev.menu.eesz.512K128.build.flash_size_bytes=0x80000 +thingdev.menu.eesz.512K128.build.flash_ld=eagle.flash.512k128.ld +thingdev.menu.eesz.512K128.build.spiffs_pagesize=256 +thingdev.menu.eesz.512K128.upload.maximum_size=368624 +thingdev.menu.eesz.512K128.build.rfcal_addr=0x7C000 +thingdev.menu.eesz.512K128.build.spiffs_start=0x5B000 +thingdev.menu.eesz.512K128.build.spiffs_end=0x7B000 +thingdev.menu.eesz.512K128.build.spiffs_blocksize=4096 +thingdev.menu.eesz.512K=512KB (FS:none OTA:~246KB) +thingdev.menu.eesz.512K.build.flash_size=512K +thingdev.menu.eesz.512K.build.flash_size_bytes=0x80000 +thingdev.menu.eesz.512K.build.flash_ld=eagle.flash.512k.ld +thingdev.menu.eesz.512K.build.spiffs_pagesize=256 +thingdev.menu.eesz.512K.upload.maximum_size=499696 +thingdev.menu.eesz.512K.build.rfcal_addr=0x7C000 +thingdev.menu.ip.lm2f=v2 Lower Memory +thingdev.menu.ip.lm2f.build.lwip_include=lwip2/include +thingdev.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +thingdev.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +thingdev.menu.ip.hb2f=v2 Higher Bandwidth +thingdev.menu.ip.hb2f.build.lwip_include=lwip2/include +thingdev.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +thingdev.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +thingdev.menu.ip.lm2n=v2 Lower Memory (no features) +thingdev.menu.ip.lm2n.build.lwip_include=lwip2/include +thingdev.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +thingdev.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +thingdev.menu.ip.hb2n=v2 Higher Bandwidth (no features) +thingdev.menu.ip.hb2n.build.lwip_include=lwip2/include +thingdev.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +thingdev.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +thingdev.menu.ip.lm6f=v2 IPv6 Lower Memory +thingdev.menu.ip.lm6f.build.lwip_include=lwip2/include +thingdev.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +thingdev.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +thingdev.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +thingdev.menu.ip.hb6f.build.lwip_include=lwip2/include +thingdev.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +thingdev.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +thingdev.menu.ip.hb1=v1.4 Higher Bandwidth +thingdev.menu.ip.hb1.build.lwip_lib=-llwip_gcc +thingdev.menu.ip.hb1.build.lwip_flags=-DLWIP_OPEN_SRC +thingdev.menu.ip.src=v1.4 Compile from source +thingdev.menu.ip.src.build.lwip_lib=-llwip_src +thingdev.menu.ip.src.build.lwip_flags=-DLWIP_OPEN_SRC +thingdev.menu.ip.src.recipe.hooks.sketch.prebuild.1.pattern=make -C "{runtime.platform.path}/tools/sdk/lwip/src" install TOOLS_PATH="{runtime.tools.xtensa-lx106-elf-gcc.path}/bin/xtensa-lx106-elf-" +thingdev.menu.dbg.Disabled=Disabled +thingdev.menu.dbg.Disabled.build.debug_port= +thingdev.menu.dbg.Serial=Serial +thingdev.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +thingdev.menu.dbg.Serial1=Serial1 +thingdev.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +thingdev.menu.lvl.None____=None +thingdev.menu.lvl.None____.build.debug_level= +thingdev.menu.lvl.SSL=SSL +thingdev.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +thingdev.menu.lvl.TLS_MEM=TLS_MEM +thingdev.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +thingdev.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +thingdev.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +thingdev.menu.lvl.HTTP_SERVER=HTTP_SERVER +thingdev.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +thingdev.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +thingdev.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +thingdev.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +thingdev.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +thingdev.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +thingdev.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +thingdev.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +thingdev.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +thingdev.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +thingdev.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +thingdev.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +thingdev.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +thingdev.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +thingdev.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +thingdev.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +thingdev.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +thingdev.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +thingdev.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +thingdev.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +thingdev.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +thingdev.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +thingdev.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +thingdev.menu.lvl.CORE=CORE +thingdev.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +thingdev.menu.lvl.WIFI=WIFI +thingdev.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +thingdev.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +thingdev.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +thingdev.menu.lvl.UPDATER=UPDATER +thingdev.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +thingdev.menu.lvl.OTA=OTA +thingdev.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +thingdev.menu.lvl.OOM=OOM +thingdev.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +thingdev.menu.lvl.MDNS=MDNS +thingdev.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +thingdev.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +thingdev.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS +thingdev.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +thingdev.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS +thingdev.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +thingdev.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +thingdev.menu.wipe.none=Only Sketch +thingdev.menu.wipe.none.upload.erase_cmd=version +thingdev.menu.wipe.sdk=Sketch + WiFi Settings +thingdev.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 +thingdev.menu.wipe.all=All Flash Contents +thingdev.menu.wipe.all.upload.erase_cmd=erase_flash +thingdev.menu.baud.115200=115200 +thingdev.menu.baud.115200.upload.speed=115200 +thingdev.menu.baud.57600=57600 +thingdev.menu.baud.57600.upload.speed=57600 +thingdev.menu.baud.230400.linux=230400 +thingdev.menu.baud.230400.macosx=230400 +thingdev.menu.baud.230400.upload.speed=230400 +thingdev.menu.baud.256000.windows=256000 +thingdev.menu.baud.256000.upload.speed=256000 +thingdev.menu.baud.460800.linux=460800 +thingdev.menu.baud.460800.macosx=460800 +thingdev.menu.baud.460800.upload.speed=460800 +thingdev.menu.baud.512000.windows=512000 +thingdev.menu.baud.512000.upload.speed=512000 +thingdev.menu.baud.921600=921600 +thingdev.menu.baud.921600.upload.speed=921600 +thingdev.menu.baud.3000000=3000000 +thingdev.menu.baud.3000000.upload.speed=3000000 + +############################################################## +esp210.name=SweetPea ESP-210 +esp210.build.board=ESP8266_ESP210 +esp210.upload.tool=esptool +esp210.upload.maximum_data_size=81920 +esp210.upload.wait_for_upload_port=true +esp210.upload.erase_cmd=version +esp210.serial.disableDTR=true +esp210.serial.disableRTS=true +esp210.build.mcu=esp8266 +esp210.build.core=esp8266 +esp210.build.variant=generic +esp210.build.spiffs_pagesize=256 +esp210.build.debug_port= +esp210.build.debug_level= +esp210.menu.xtal.80=80 MHz +esp210.menu.xtal.80.build.f_cpu=80000000L +esp210.menu.xtal.160=160 MHz +esp210.menu.xtal.160.build.f_cpu=160000000L +esp210.menu.vt.flash=Flash +esp210.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +esp210.menu.vt.heap=Heap +esp210.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +esp210.menu.vt.iram=IRAM +esp210.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +esp210.menu.exception.legacy=Legacy (new can return nullptr) +esp210.menu.exception.legacy.build.exception_flags=-fno-exceptions +esp210.menu.exception.legacy.build.stdcpp_lib=-lstdc++ +esp210.menu.exception.disabled=Disabled (new can abort) +esp210.menu.exception.disabled.build.exception_flags=-fno-exceptions -DNEW_OOM_ABORT +esp210.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +esp210.menu.exception.enabled=Enabled +esp210.menu.exception.enabled.build.exception_flags=-fexceptions +esp210.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +esp210.menu.ssl.all=All SSL ciphers (most compatible) +esp210.menu.ssl.all.build.sslflags= +esp210.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +esp210.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +esp210.upload.resetmethod=ck +esp210.build.flash_mode=qio +esp210.build.flash_flags=-DFLASHMODE_QIO +esp210.build.flash_freq=40 +esp210.menu.eesz.4M2M=4MB (FS:2MB OTA:~1019KB) +esp210.menu.eesz.4M2M.build.flash_size=4M +esp210.menu.eesz.4M2M.build.flash_size_bytes=0x400000 +esp210.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld +esp210.menu.eesz.4M2M.build.spiffs_pagesize=256 +esp210.menu.eesz.4M2M.upload.maximum_size=1044464 +esp210.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 +esp210.menu.eesz.4M2M.build.spiffs_start=0x200000 +esp210.menu.eesz.4M2M.build.spiffs_end=0x3FA000 +esp210.menu.eesz.4M2M.build.spiffs_blocksize=8192 +esp210.menu.eesz.4M3M=4MB (FS:3MB OTA:~512KB) +esp210.menu.eesz.4M3M.build.flash_size=4M +esp210.menu.eesz.4M3M.build.flash_size_bytes=0x400000 +esp210.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld +esp210.menu.eesz.4M3M.build.spiffs_pagesize=256 +esp210.menu.eesz.4M3M.upload.maximum_size=1044464 +esp210.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 +esp210.menu.eesz.4M3M.build.spiffs_start=0x100000 +esp210.menu.eesz.4M3M.build.spiffs_end=0x3FA000 +esp210.menu.eesz.4M3M.build.spiffs_blocksize=8192 +esp210.menu.eesz.4M1M=4MB (FS:1MB OTA:~1019KB) +esp210.menu.eesz.4M1M.build.flash_size=4M +esp210.menu.eesz.4M1M.build.flash_size_bytes=0x400000 +esp210.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld +esp210.menu.eesz.4M1M.build.spiffs_pagesize=256 +esp210.menu.eesz.4M1M.upload.maximum_size=1044464 +esp210.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 +esp210.menu.eesz.4M1M.build.spiffs_start=0x300000 +esp210.menu.eesz.4M1M.build.spiffs_end=0x3FA000 +esp210.menu.eesz.4M1M.build.spiffs_blocksize=8192 +esp210.menu.eesz.4M=4MB (FS:none OTA:~1019KB) +esp210.menu.eesz.4M.build.flash_size=4M +esp210.menu.eesz.4M.build.flash_size_bytes=0x400000 +esp210.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld +esp210.menu.eesz.4M.build.spiffs_pagesize=256 +esp210.menu.eesz.4M.upload.maximum_size=1044464 +esp210.menu.eesz.4M.build.rfcal_addr=0x3FC000 +esp210.menu.ip.lm2f=v2 Lower Memory +esp210.menu.ip.lm2f.build.lwip_include=lwip2/include +esp210.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +esp210.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +esp210.menu.ip.hb2f=v2 Higher Bandwidth +esp210.menu.ip.hb2f.build.lwip_include=lwip2/include +esp210.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +esp210.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +esp210.menu.ip.lm2n=v2 Lower Memory (no features) +esp210.menu.ip.lm2n.build.lwip_include=lwip2/include +esp210.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +esp210.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +esp210.menu.ip.hb2n=v2 Higher Bandwidth (no features) +esp210.menu.ip.hb2n.build.lwip_include=lwip2/include +esp210.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +esp210.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +esp210.menu.ip.lm6f=v2 IPv6 Lower Memory +esp210.menu.ip.lm6f.build.lwip_include=lwip2/include +esp210.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +esp210.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +esp210.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +esp210.menu.ip.hb6f.build.lwip_include=lwip2/include +esp210.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +esp210.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +esp210.menu.ip.hb1=v1.4 Higher Bandwidth +esp210.menu.ip.hb1.build.lwip_lib=-llwip_gcc +esp210.menu.ip.hb1.build.lwip_flags=-DLWIP_OPEN_SRC +esp210.menu.ip.src=v1.4 Compile from source +esp210.menu.ip.src.build.lwip_lib=-llwip_src +esp210.menu.ip.src.build.lwip_flags=-DLWIP_OPEN_SRC +esp210.menu.ip.src.recipe.hooks.sketch.prebuild.1.pattern=make -C "{runtime.platform.path}/tools/sdk/lwip/src" install TOOLS_PATH="{runtime.tools.xtensa-lx106-elf-gcc.path}/bin/xtensa-lx106-elf-" +esp210.menu.dbg.Disabled=Disabled +esp210.menu.dbg.Disabled.build.debug_port= +esp210.menu.dbg.Serial=Serial +esp210.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +esp210.menu.dbg.Serial1=Serial1 +esp210.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +esp210.menu.lvl.None____=None +esp210.menu.lvl.None____.build.debug_level= +esp210.menu.lvl.SSL=SSL +esp210.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +esp210.menu.lvl.TLS_MEM=TLS_MEM +esp210.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +esp210.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +esp210.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +esp210.menu.lvl.HTTP_SERVER=HTTP_SERVER +esp210.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +esp210.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +esp210.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +esp210.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +esp210.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +esp210.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +esp210.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +esp210.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +esp210.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +esp210.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +esp210.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +esp210.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +esp210.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +esp210.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +esp210.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +esp210.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +esp210.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +esp210.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +esp210.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +esp210.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +esp210.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +esp210.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +esp210.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +esp210.menu.lvl.CORE=CORE +esp210.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +esp210.menu.lvl.WIFI=WIFI +esp210.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +esp210.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +esp210.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +esp210.menu.lvl.UPDATER=UPDATER +esp210.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +esp210.menu.lvl.OTA=OTA +esp210.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +esp210.menu.lvl.OOM=OOM +esp210.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +esp210.menu.lvl.MDNS=MDNS +esp210.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +esp210.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +esp210.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS +esp210.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +esp210.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS +esp210.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +esp210.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +esp210.menu.wipe.none=Only Sketch +esp210.menu.wipe.none.upload.erase_cmd=version +esp210.menu.wipe.sdk=Sketch + WiFi Settings +esp210.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 +esp210.menu.wipe.all=All Flash Contents +esp210.menu.wipe.all.upload.erase_cmd=erase_flash +esp210.menu.baud.57600=57600 +esp210.menu.baud.57600.upload.speed=57600 +esp210.menu.baud.115200=115200 +esp210.menu.baud.115200.upload.speed=115200 +esp210.menu.baud.230400.linux=230400 +esp210.menu.baud.230400.macosx=230400 +esp210.menu.baud.230400.upload.speed=230400 +esp210.menu.baud.256000.windows=256000 +esp210.menu.baud.256000.upload.speed=256000 +esp210.menu.baud.460800.linux=460800 +esp210.menu.baud.460800.macosx=460800 +esp210.menu.baud.460800.upload.speed=460800 +esp210.menu.baud.512000.windows=512000 +esp210.menu.baud.512000.upload.speed=512000 +esp210.menu.baud.921600=921600 +esp210.menu.baud.921600.upload.speed=921600 +esp210.menu.baud.3000000=3000000 +esp210.menu.baud.3000000.upload.speed=3000000 + +############################################################## +d1_mini.name=LOLIN(WEMOS) D1 R2 & mini +d1_mini.build.board=ESP8266_WEMOS_D1MINI +d1_mini.build.variant=d1_mini +d1_mini.upload.tool=esptool +d1_mini.upload.maximum_data_size=81920 +d1_mini.upload.wait_for_upload_port=true +d1_mini.upload.erase_cmd=version +d1_mini.serial.disableDTR=true +d1_mini.serial.disableRTS=true +d1_mini.build.mcu=esp8266 +d1_mini.build.core=esp8266 +d1_mini.build.spiffs_pagesize=256 +d1_mini.build.debug_port= +d1_mini.build.debug_level= +d1_mini.menu.xtal.80=80 MHz +d1_mini.menu.xtal.80.build.f_cpu=80000000L +d1_mini.menu.xtal.160=160 MHz +d1_mini.menu.xtal.160.build.f_cpu=160000000L +d1_mini.menu.vt.flash=Flash +d1_mini.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +d1_mini.menu.vt.heap=Heap +d1_mini.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +d1_mini.menu.vt.iram=IRAM +d1_mini.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +d1_mini.menu.exception.legacy=Legacy (new can return nullptr) +d1_mini.menu.exception.legacy.build.exception_flags=-fno-exceptions +d1_mini.menu.exception.legacy.build.stdcpp_lib=-lstdc++ +d1_mini.menu.exception.disabled=Disabled (new can abort) +d1_mini.menu.exception.disabled.build.exception_flags=-fno-exceptions -DNEW_OOM_ABORT +d1_mini.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +d1_mini.menu.exception.enabled=Enabled +d1_mini.menu.exception.enabled.build.exception_flags=-fexceptions +d1_mini.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +d1_mini.menu.ssl.all=All SSL ciphers (most compatible) +d1_mini.menu.ssl.all.build.sslflags= +d1_mini.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +d1_mini.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +d1_mini.upload.resetmethod=nodemcu +d1_mini.build.flash_mode=dio +d1_mini.build.flash_flags=-DFLASHMODE_DIO +d1_mini.build.flash_freq=40 +d1_mini.menu.eesz.4M2M=4MB (FS:2MB OTA:~1019KB) +d1_mini.menu.eesz.4M2M.build.flash_size=4M +d1_mini.menu.eesz.4M2M.build.flash_size_bytes=0x400000 +d1_mini.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld +d1_mini.menu.eesz.4M2M.build.spiffs_pagesize=256 +d1_mini.menu.eesz.4M2M.upload.maximum_size=1044464 +d1_mini.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 +d1_mini.menu.eesz.4M2M.build.spiffs_start=0x200000 +d1_mini.menu.eesz.4M2M.build.spiffs_end=0x3FA000 +d1_mini.menu.eesz.4M2M.build.spiffs_blocksize=8192 +d1_mini.menu.eesz.4M3M=4MB (FS:3MB OTA:~512KB) +d1_mini.menu.eesz.4M3M.build.flash_size=4M +d1_mini.menu.eesz.4M3M.build.flash_size_bytes=0x400000 +d1_mini.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld +d1_mini.menu.eesz.4M3M.build.spiffs_pagesize=256 +d1_mini.menu.eesz.4M3M.upload.maximum_size=1044464 +d1_mini.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 +d1_mini.menu.eesz.4M3M.build.spiffs_start=0x100000 +d1_mini.menu.eesz.4M3M.build.spiffs_end=0x3FA000 +d1_mini.menu.eesz.4M3M.build.spiffs_blocksize=8192 +d1_mini.menu.eesz.4M1M=4MB (FS:1MB OTA:~1019KB) +d1_mini.menu.eesz.4M1M.build.flash_size=4M +d1_mini.menu.eesz.4M1M.build.flash_size_bytes=0x400000 +d1_mini.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld +d1_mini.menu.eesz.4M1M.build.spiffs_pagesize=256 +d1_mini.menu.eesz.4M1M.upload.maximum_size=1044464 +d1_mini.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 +d1_mini.menu.eesz.4M1M.build.spiffs_start=0x300000 +d1_mini.menu.eesz.4M1M.build.spiffs_end=0x3FA000 +d1_mini.menu.eesz.4M1M.build.spiffs_blocksize=8192 +d1_mini.menu.eesz.4M=4MB (FS:none OTA:~1019KB) +d1_mini.menu.eesz.4M.build.flash_size=4M +d1_mini.menu.eesz.4M.build.flash_size_bytes=0x400000 +d1_mini.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld +d1_mini.menu.eesz.4M.build.spiffs_pagesize=256 +d1_mini.menu.eesz.4M.upload.maximum_size=1044464 +d1_mini.menu.eesz.4M.build.rfcal_addr=0x3FC000 +d1_mini.menu.ip.lm2f=v2 Lower Memory +d1_mini.menu.ip.lm2f.build.lwip_include=lwip2/include +d1_mini.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +d1_mini.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +d1_mini.menu.ip.hb2f=v2 Higher Bandwidth +d1_mini.menu.ip.hb2f.build.lwip_include=lwip2/include +d1_mini.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +d1_mini.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +d1_mini.menu.ip.lm2n=v2 Lower Memory (no features) +d1_mini.menu.ip.lm2n.build.lwip_include=lwip2/include +d1_mini.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +d1_mini.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +d1_mini.menu.ip.hb2n=v2 Higher Bandwidth (no features) +d1_mini.menu.ip.hb2n.build.lwip_include=lwip2/include +d1_mini.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +d1_mini.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +d1_mini.menu.ip.lm6f=v2 IPv6 Lower Memory +d1_mini.menu.ip.lm6f.build.lwip_include=lwip2/include +d1_mini.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +d1_mini.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +d1_mini.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +d1_mini.menu.ip.hb6f.build.lwip_include=lwip2/include +d1_mini.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +d1_mini.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +d1_mini.menu.ip.hb1=v1.4 Higher Bandwidth +d1_mini.menu.ip.hb1.build.lwip_lib=-llwip_gcc +d1_mini.menu.ip.hb1.build.lwip_flags=-DLWIP_OPEN_SRC +d1_mini.menu.ip.src=v1.4 Compile from source +d1_mini.menu.ip.src.build.lwip_lib=-llwip_src +d1_mini.menu.ip.src.build.lwip_flags=-DLWIP_OPEN_SRC +d1_mini.menu.ip.src.recipe.hooks.sketch.prebuild.1.pattern=make -C "{runtime.platform.path}/tools/sdk/lwip/src" install TOOLS_PATH="{runtime.tools.xtensa-lx106-elf-gcc.path}/bin/xtensa-lx106-elf-" +d1_mini.menu.dbg.Disabled=Disabled +d1_mini.menu.dbg.Disabled.build.debug_port= +d1_mini.menu.dbg.Serial=Serial +d1_mini.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +d1_mini.menu.dbg.Serial1=Serial1 +d1_mini.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +d1_mini.menu.lvl.None____=None +d1_mini.menu.lvl.None____.build.debug_level= +d1_mini.menu.lvl.SSL=SSL +d1_mini.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +d1_mini.menu.lvl.TLS_MEM=TLS_MEM +d1_mini.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +d1_mini.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +d1_mini.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +d1_mini.menu.lvl.HTTP_SERVER=HTTP_SERVER +d1_mini.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +d1_mini.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +d1_mini.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +d1_mini.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +d1_mini.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +d1_mini.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +d1_mini.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +d1_mini.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +d1_mini.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +d1_mini.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +d1_mini.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +d1_mini.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +d1_mini.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +d1_mini.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +d1_mini.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +d1_mini.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +d1_mini.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +d1_mini.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +d1_mini.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +d1_mini.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +d1_mini.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +d1_mini.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +d1_mini.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +d1_mini.menu.lvl.CORE=CORE +d1_mini.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +d1_mini.menu.lvl.WIFI=WIFI +d1_mini.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +d1_mini.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +d1_mini.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +d1_mini.menu.lvl.UPDATER=UPDATER +d1_mini.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +d1_mini.menu.lvl.OTA=OTA +d1_mini.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +d1_mini.menu.lvl.OOM=OOM +d1_mini.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +d1_mini.menu.lvl.MDNS=MDNS +d1_mini.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +d1_mini.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +d1_mini.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS +d1_mini.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +d1_mini.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS +d1_mini.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +d1_mini.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +d1_mini.menu.wipe.none=Only Sketch +d1_mini.menu.wipe.none.upload.erase_cmd=version +d1_mini.menu.wipe.sdk=Sketch + WiFi Settings +d1_mini.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 +d1_mini.menu.wipe.all=All Flash Contents +d1_mini.menu.wipe.all.upload.erase_cmd=erase_flash +d1_mini.menu.baud.921600=921600 +d1_mini.menu.baud.921600.upload.speed=921600 +d1_mini.menu.baud.57600=57600 +d1_mini.menu.baud.57600.upload.speed=57600 +d1_mini.menu.baud.115200=115200 +d1_mini.menu.baud.115200.upload.speed=115200 +d1_mini.menu.baud.230400.linux=230400 +d1_mini.menu.baud.230400.macosx=230400 +d1_mini.menu.baud.230400.upload.speed=230400 +d1_mini.menu.baud.256000.windows=256000 +d1_mini.menu.baud.256000.upload.speed=256000 +d1_mini.menu.baud.460800.linux=460800 +d1_mini.menu.baud.460800.macosx=460800 +d1_mini.menu.baud.460800.upload.speed=460800 +d1_mini.menu.baud.512000.windows=512000 +d1_mini.menu.baud.512000.upload.speed=512000 +d1_mini.menu.baud.3000000=3000000 +d1_mini.menu.baud.3000000.upload.speed=3000000 + +############################################################## +d1_mini_pro.name=LOLIN(WEMOS) D1 mini Pro +d1_mini_pro.build.board=ESP8266_WEMOS_D1MINIPRO +d1_mini_pro.build.variant=d1_mini +d1_mini_pro.upload.tool=esptool +d1_mini_pro.upload.maximum_data_size=81920 +d1_mini_pro.upload.wait_for_upload_port=true +d1_mini_pro.upload.erase_cmd=version +d1_mini_pro.serial.disableDTR=true +d1_mini_pro.serial.disableRTS=true +d1_mini_pro.build.mcu=esp8266 +d1_mini_pro.build.core=esp8266 +d1_mini_pro.build.spiffs_pagesize=256 +d1_mini_pro.build.debug_port= +d1_mini_pro.build.debug_level= +d1_mini_pro.menu.xtal.80=80 MHz +d1_mini_pro.menu.xtal.80.build.f_cpu=80000000L +d1_mini_pro.menu.xtal.160=160 MHz +d1_mini_pro.menu.xtal.160.build.f_cpu=160000000L +d1_mini_pro.menu.vt.flash=Flash +d1_mini_pro.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +d1_mini_pro.menu.vt.heap=Heap +d1_mini_pro.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +d1_mini_pro.menu.vt.iram=IRAM +d1_mini_pro.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +d1_mini_pro.menu.exception.legacy=Legacy (new can return nullptr) +d1_mini_pro.menu.exception.legacy.build.exception_flags=-fno-exceptions +d1_mini_pro.menu.exception.legacy.build.stdcpp_lib=-lstdc++ +d1_mini_pro.menu.exception.disabled=Disabled (new can abort) +d1_mini_pro.menu.exception.disabled.build.exception_flags=-fno-exceptions -DNEW_OOM_ABORT +d1_mini_pro.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +d1_mini_pro.menu.exception.enabled=Enabled +d1_mini_pro.menu.exception.enabled.build.exception_flags=-fexceptions +d1_mini_pro.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +d1_mini_pro.menu.ssl.all=All SSL ciphers (most compatible) +d1_mini_pro.menu.ssl.all.build.sslflags= +d1_mini_pro.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +d1_mini_pro.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +d1_mini_pro.upload.resetmethod=nodemcu +d1_mini_pro.build.flash_mode=dio +d1_mini_pro.build.flash_flags=-DFLASHMODE_DIO +d1_mini_pro.build.flash_freq=40 +d1_mini_pro.menu.eesz.16M14M=16MB (FS:14MB OTA:~1019KB) +d1_mini_pro.menu.eesz.16M14M.build.flash_size=16M +d1_mini_pro.menu.eesz.16M14M.build.flash_size_bytes=0x1000000 +d1_mini_pro.menu.eesz.16M14M.build.flash_ld=eagle.flash.16m14m.ld +d1_mini_pro.menu.eesz.16M14M.build.spiffs_pagesize=256 +d1_mini_pro.menu.eesz.16M14M.upload.maximum_size=1044464 +d1_mini_pro.menu.eesz.16M14M.build.rfcal_addr=0xFFC000 +d1_mini_pro.menu.eesz.16M14M.build.spiffs_start=0x200000 +d1_mini_pro.menu.eesz.16M14M.build.spiffs_end=0xFFA000 +d1_mini_pro.menu.eesz.16M14M.build.spiffs_blocksize=8192 +d1_mini_pro.menu.eesz.16M15M=16MB (FS:15MB OTA:~512KB) +d1_mini_pro.menu.eesz.16M15M.build.flash_size=16M +d1_mini_pro.menu.eesz.16M15M.build.flash_size_bytes=0x1000000 +d1_mini_pro.menu.eesz.16M15M.build.flash_ld=eagle.flash.16m15m.ld +d1_mini_pro.menu.eesz.16M15M.build.spiffs_pagesize=256 +d1_mini_pro.menu.eesz.16M15M.upload.maximum_size=1044464 +d1_mini_pro.menu.eesz.16M15M.build.rfcal_addr=0xFFC000 +d1_mini_pro.menu.eesz.16M15M.build.spiffs_start=0x100000 +d1_mini_pro.menu.eesz.16M15M.build.spiffs_end=0xFFA000 +d1_mini_pro.menu.eesz.16M15M.build.spiffs_blocksize=8192 +d1_mini_pro.menu.ip.lm2f=v2 Lower Memory +d1_mini_pro.menu.ip.lm2f.build.lwip_include=lwip2/include +d1_mini_pro.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +d1_mini_pro.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +d1_mini_pro.menu.ip.hb2f=v2 Higher Bandwidth +d1_mini_pro.menu.ip.hb2f.build.lwip_include=lwip2/include +d1_mini_pro.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +d1_mini_pro.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +d1_mini_pro.menu.ip.lm2n=v2 Lower Memory (no features) +d1_mini_pro.menu.ip.lm2n.build.lwip_include=lwip2/include +d1_mini_pro.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +d1_mini_pro.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +d1_mini_pro.menu.ip.hb2n=v2 Higher Bandwidth (no features) +d1_mini_pro.menu.ip.hb2n.build.lwip_include=lwip2/include +d1_mini_pro.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +d1_mini_pro.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +d1_mini_pro.menu.ip.lm6f=v2 IPv6 Lower Memory +d1_mini_pro.menu.ip.lm6f.build.lwip_include=lwip2/include +d1_mini_pro.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +d1_mini_pro.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +d1_mini_pro.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +d1_mini_pro.menu.ip.hb6f.build.lwip_include=lwip2/include +d1_mini_pro.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +d1_mini_pro.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +d1_mini_pro.menu.ip.hb1=v1.4 Higher Bandwidth +d1_mini_pro.menu.ip.hb1.build.lwip_lib=-llwip_gcc +d1_mini_pro.menu.ip.hb1.build.lwip_flags=-DLWIP_OPEN_SRC +d1_mini_pro.menu.ip.src=v1.4 Compile from source +d1_mini_pro.menu.ip.src.build.lwip_lib=-llwip_src +d1_mini_pro.menu.ip.src.build.lwip_flags=-DLWIP_OPEN_SRC +d1_mini_pro.menu.ip.src.recipe.hooks.sketch.prebuild.1.pattern=make -C "{runtime.platform.path}/tools/sdk/lwip/src" install TOOLS_PATH="{runtime.tools.xtensa-lx106-elf-gcc.path}/bin/xtensa-lx106-elf-" +d1_mini_pro.menu.dbg.Disabled=Disabled +d1_mini_pro.menu.dbg.Disabled.build.debug_port= +d1_mini_pro.menu.dbg.Serial=Serial +d1_mini_pro.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +d1_mini_pro.menu.dbg.Serial1=Serial1 +d1_mini_pro.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +d1_mini_pro.menu.lvl.None____=None +d1_mini_pro.menu.lvl.None____.build.debug_level= +d1_mini_pro.menu.lvl.SSL=SSL +d1_mini_pro.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +d1_mini_pro.menu.lvl.TLS_MEM=TLS_MEM +d1_mini_pro.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +d1_mini_pro.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +d1_mini_pro.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +d1_mini_pro.menu.lvl.HTTP_SERVER=HTTP_SERVER +d1_mini_pro.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +d1_mini_pro.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +d1_mini_pro.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +d1_mini_pro.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +d1_mini_pro.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +d1_mini_pro.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +d1_mini_pro.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +d1_mini_pro.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +d1_mini_pro.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +d1_mini_pro.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +d1_mini_pro.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +d1_mini_pro.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +d1_mini_pro.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +d1_mini_pro.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +d1_mini_pro.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +d1_mini_pro.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +d1_mini_pro.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +d1_mini_pro.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +d1_mini_pro.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +d1_mini_pro.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +d1_mini_pro.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +d1_mini_pro.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +d1_mini_pro.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +d1_mini_pro.menu.lvl.CORE=CORE +d1_mini_pro.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +d1_mini_pro.menu.lvl.WIFI=WIFI +d1_mini_pro.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +d1_mini_pro.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +d1_mini_pro.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +d1_mini_pro.menu.lvl.UPDATER=UPDATER +d1_mini_pro.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +d1_mini_pro.menu.lvl.OTA=OTA +d1_mini_pro.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +d1_mini_pro.menu.lvl.OOM=OOM +d1_mini_pro.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +d1_mini_pro.menu.lvl.MDNS=MDNS +d1_mini_pro.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +d1_mini_pro.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +d1_mini_pro.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS +d1_mini_pro.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +d1_mini_pro.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS +d1_mini_pro.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +d1_mini_pro.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +d1_mini_pro.menu.wipe.none=Only Sketch +d1_mini_pro.menu.wipe.none.upload.erase_cmd=version +d1_mini_pro.menu.wipe.sdk=Sketch + WiFi Settings +d1_mini_pro.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 +d1_mini_pro.menu.wipe.all=All Flash Contents +d1_mini_pro.menu.wipe.all.upload.erase_cmd=erase_flash +d1_mini_pro.menu.baud.921600=921600 +d1_mini_pro.menu.baud.921600.upload.speed=921600 +d1_mini_pro.menu.baud.57600=57600 +d1_mini_pro.menu.baud.57600.upload.speed=57600 +d1_mini_pro.menu.baud.115200=115200 +d1_mini_pro.menu.baud.115200.upload.speed=115200 +d1_mini_pro.menu.baud.230400.linux=230400 +d1_mini_pro.menu.baud.230400.macosx=230400 +d1_mini_pro.menu.baud.230400.upload.speed=230400 +d1_mini_pro.menu.baud.256000.windows=256000 +d1_mini_pro.menu.baud.256000.upload.speed=256000 +d1_mini_pro.menu.baud.460800.linux=460800 +d1_mini_pro.menu.baud.460800.macosx=460800 +d1_mini_pro.menu.baud.460800.upload.speed=460800 +d1_mini_pro.menu.baud.512000.windows=512000 +d1_mini_pro.menu.baud.512000.upload.speed=512000 +d1_mini_pro.menu.baud.3000000=3000000 +d1_mini_pro.menu.baud.3000000.upload.speed=3000000 + +############################################################## +d1_mini_lite.name=LOLIN(WEMOS) D1 mini Lite +d1_mini_lite.build.board=ESP8266_WEMOS_D1MINILITE +d1_mini_lite.build.variant=d1_mini +d1_mini_lite.upload.tool=esptool +d1_mini_lite.upload.maximum_data_size=81920 +d1_mini_lite.upload.wait_for_upload_port=true +d1_mini_lite.upload.erase_cmd=version +d1_mini_lite.serial.disableDTR=true +d1_mini_lite.serial.disableRTS=true +d1_mini_lite.build.mcu=esp8266 +d1_mini_lite.build.core=esp8266 +d1_mini_lite.build.spiffs_pagesize=256 +d1_mini_lite.build.debug_port= +d1_mini_lite.build.debug_level= +d1_mini_lite.menu.xtal.80=80 MHz +d1_mini_lite.menu.xtal.80.build.f_cpu=80000000L +d1_mini_lite.menu.xtal.160=160 MHz +d1_mini_lite.menu.xtal.160.build.f_cpu=160000000L +d1_mini_lite.menu.vt.flash=Flash +d1_mini_lite.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +d1_mini_lite.menu.vt.heap=Heap +d1_mini_lite.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +d1_mini_lite.menu.vt.iram=IRAM +d1_mini_lite.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +d1_mini_lite.menu.exception.legacy=Legacy (new can return nullptr) +d1_mini_lite.menu.exception.legacy.build.exception_flags=-fno-exceptions +d1_mini_lite.menu.exception.legacy.build.stdcpp_lib=-lstdc++ +d1_mini_lite.menu.exception.disabled=Disabled (new can abort) +d1_mini_lite.menu.exception.disabled.build.exception_flags=-fno-exceptions -DNEW_OOM_ABORT +d1_mini_lite.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +d1_mini_lite.menu.exception.enabled=Enabled +d1_mini_lite.menu.exception.enabled.build.exception_flags=-fexceptions +d1_mini_lite.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +d1_mini_lite.menu.ssl.all=All SSL ciphers (most compatible) +d1_mini_lite.menu.ssl.all.build.sslflags= +d1_mini_lite.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +d1_mini_lite.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +d1_mini_lite.upload.resetmethod=nodemcu +d1_mini_lite.build.flash_mode=dout +d1_mini_lite.build.flash_flags=-DFLASHMODE_DOUT +d1_mini_lite.build.flash_freq=40 +d1_mini_lite.menu.eesz.1M64=1MB (FS:64KB OTA:~470KB) +d1_mini_lite.menu.eesz.1M64.build.flash_size=1M +d1_mini_lite.menu.eesz.1M64.build.flash_size_bytes=0x100000 +d1_mini_lite.menu.eesz.1M64.build.flash_ld=eagle.flash.1m64.ld +d1_mini_lite.menu.eesz.1M64.build.spiffs_pagesize=256 +d1_mini_lite.menu.eesz.1M64.upload.maximum_size=958448 +d1_mini_lite.menu.eesz.1M64.build.rfcal_addr=0xFC000 +d1_mini_lite.menu.eesz.1M64.build.spiffs_start=0xEB000 +d1_mini_lite.menu.eesz.1M64.build.spiffs_end=0xFB000 +d1_mini_lite.menu.eesz.1M64.build.spiffs_blocksize=4096 +d1_mini_lite.menu.eesz.1M128=1MB (FS:128KB OTA:~438KB) +d1_mini_lite.menu.eesz.1M128.build.flash_size=1M +d1_mini_lite.menu.eesz.1M128.build.flash_size_bytes=0x100000 +d1_mini_lite.menu.eesz.1M128.build.flash_ld=eagle.flash.1m128.ld +d1_mini_lite.menu.eesz.1M128.build.spiffs_pagesize=256 +d1_mini_lite.menu.eesz.1M128.upload.maximum_size=892912 +d1_mini_lite.menu.eesz.1M128.build.rfcal_addr=0xFC000 +d1_mini_lite.menu.eesz.1M128.build.spiffs_start=0xDB000 +d1_mini_lite.menu.eesz.1M128.build.spiffs_end=0xFB000 +d1_mini_lite.menu.eesz.1M128.build.spiffs_blocksize=4096 +d1_mini_lite.menu.eesz.1M144=1MB (FS:144KB OTA:~430KB) +d1_mini_lite.menu.eesz.1M144.build.flash_size=1M +d1_mini_lite.menu.eesz.1M144.build.flash_size_bytes=0x100000 +d1_mini_lite.menu.eesz.1M144.build.flash_ld=eagle.flash.1m144.ld +d1_mini_lite.menu.eesz.1M144.build.spiffs_pagesize=256 +d1_mini_lite.menu.eesz.1M144.upload.maximum_size=876528 +d1_mini_lite.menu.eesz.1M144.build.rfcal_addr=0xFC000 +d1_mini_lite.menu.eesz.1M144.build.spiffs_start=0xD7000 +d1_mini_lite.menu.eesz.1M144.build.spiffs_end=0xFB000 +d1_mini_lite.menu.eesz.1M144.build.spiffs_blocksize=4096 +d1_mini_lite.menu.eesz.1M160=1MB (FS:160KB OTA:~422KB) +d1_mini_lite.menu.eesz.1M160.build.flash_size=1M +d1_mini_lite.menu.eesz.1M160.build.flash_size_bytes=0x100000 +d1_mini_lite.menu.eesz.1M160.build.flash_ld=eagle.flash.1m160.ld +d1_mini_lite.menu.eesz.1M160.build.spiffs_pagesize=256 +d1_mini_lite.menu.eesz.1M160.upload.maximum_size=860144 +d1_mini_lite.menu.eesz.1M160.build.rfcal_addr=0xFC000 +d1_mini_lite.menu.eesz.1M160.build.spiffs_start=0xD3000 +d1_mini_lite.menu.eesz.1M160.build.spiffs_end=0xFB000 +d1_mini_lite.menu.eesz.1M160.build.spiffs_blocksize=4096 +d1_mini_lite.menu.eesz.1M192=1MB (FS:192KB OTA:~406KB) +d1_mini_lite.menu.eesz.1M192.build.flash_size=1M +d1_mini_lite.menu.eesz.1M192.build.flash_size_bytes=0x100000 +d1_mini_lite.menu.eesz.1M192.build.flash_ld=eagle.flash.1m192.ld +d1_mini_lite.menu.eesz.1M192.build.spiffs_pagesize=256 +d1_mini_lite.menu.eesz.1M192.upload.maximum_size=827376 +d1_mini_lite.menu.eesz.1M192.build.rfcal_addr=0xFC000 +d1_mini_lite.menu.eesz.1M192.build.spiffs_start=0xCB000 +d1_mini_lite.menu.eesz.1M192.build.spiffs_end=0xFB000 +d1_mini_lite.menu.eesz.1M192.build.spiffs_blocksize=4096 +d1_mini_lite.menu.eesz.1M256=1MB (FS:256KB OTA:~374KB) +d1_mini_lite.menu.eesz.1M256.build.flash_size=1M +d1_mini_lite.menu.eesz.1M256.build.flash_size_bytes=0x100000 +d1_mini_lite.menu.eesz.1M256.build.flash_ld=eagle.flash.1m256.ld +d1_mini_lite.menu.eesz.1M256.build.spiffs_pagesize=256 +d1_mini_lite.menu.eesz.1M256.upload.maximum_size=761840 +d1_mini_lite.menu.eesz.1M256.build.rfcal_addr=0xFC000 +d1_mini_lite.menu.eesz.1M256.build.spiffs_start=0xBB000 +d1_mini_lite.menu.eesz.1M256.build.spiffs_end=0xFB000 +d1_mini_lite.menu.eesz.1M256.build.spiffs_blocksize=4096 +d1_mini_lite.menu.eesz.1M512=1MB (FS:512KB OTA:~246KB) +d1_mini_lite.menu.eesz.1M512.build.flash_size=1M +d1_mini_lite.menu.eesz.1M512.build.flash_size_bytes=0x100000 +d1_mini_lite.menu.eesz.1M512.build.flash_ld=eagle.flash.1m512.ld +d1_mini_lite.menu.eesz.1M512.build.spiffs_pagesize=256 +d1_mini_lite.menu.eesz.1M512.upload.maximum_size=499696 +d1_mini_lite.menu.eesz.1M512.build.rfcal_addr=0xFC000 +d1_mini_lite.menu.eesz.1M512.build.spiffs_start=0x7B000 +d1_mini_lite.menu.eesz.1M512.build.spiffs_end=0xFB000 +d1_mini_lite.menu.eesz.1M512.build.spiffs_blocksize=8192 +d1_mini_lite.menu.eesz.1M=1MB (FS:none OTA:~502KB) +d1_mini_lite.menu.eesz.1M.build.flash_size=1M +d1_mini_lite.menu.eesz.1M.build.flash_size_bytes=0x100000 +d1_mini_lite.menu.eesz.1M.build.flash_ld=eagle.flash.1m.ld +d1_mini_lite.menu.eesz.1M.build.spiffs_pagesize=256 +d1_mini_lite.menu.eesz.1M.upload.maximum_size=1023984 +d1_mini_lite.menu.eesz.1M.build.rfcal_addr=0xFC000 +d1_mini_lite.menu.ip.lm2f=v2 Lower Memory +d1_mini_lite.menu.ip.lm2f.build.lwip_include=lwip2/include +d1_mini_lite.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +d1_mini_lite.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +d1_mini_lite.menu.ip.hb2f=v2 Higher Bandwidth +d1_mini_lite.menu.ip.hb2f.build.lwip_include=lwip2/include +d1_mini_lite.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +d1_mini_lite.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +d1_mini_lite.menu.ip.lm2n=v2 Lower Memory (no features) +d1_mini_lite.menu.ip.lm2n.build.lwip_include=lwip2/include +d1_mini_lite.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +d1_mini_lite.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +d1_mini_lite.menu.ip.hb2n=v2 Higher Bandwidth (no features) +d1_mini_lite.menu.ip.hb2n.build.lwip_include=lwip2/include +d1_mini_lite.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +d1_mini_lite.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +d1_mini_lite.menu.ip.lm6f=v2 IPv6 Lower Memory +d1_mini_lite.menu.ip.lm6f.build.lwip_include=lwip2/include +d1_mini_lite.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +d1_mini_lite.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +d1_mini_lite.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +d1_mini_lite.menu.ip.hb6f.build.lwip_include=lwip2/include +d1_mini_lite.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +d1_mini_lite.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +d1_mini_lite.menu.ip.hb1=v1.4 Higher Bandwidth +d1_mini_lite.menu.ip.hb1.build.lwip_lib=-llwip_gcc +d1_mini_lite.menu.ip.hb1.build.lwip_flags=-DLWIP_OPEN_SRC +d1_mini_lite.menu.ip.src=v1.4 Compile from source +d1_mini_lite.menu.ip.src.build.lwip_lib=-llwip_src +d1_mini_lite.menu.ip.src.build.lwip_flags=-DLWIP_OPEN_SRC +d1_mini_lite.menu.ip.src.recipe.hooks.sketch.prebuild.1.pattern=make -C "{runtime.platform.path}/tools/sdk/lwip/src" install TOOLS_PATH="{runtime.tools.xtensa-lx106-elf-gcc.path}/bin/xtensa-lx106-elf-" +d1_mini_lite.menu.dbg.Disabled=Disabled +d1_mini_lite.menu.dbg.Disabled.build.debug_port= +d1_mini_lite.menu.dbg.Serial=Serial +d1_mini_lite.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +d1_mini_lite.menu.dbg.Serial1=Serial1 +d1_mini_lite.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +d1_mini_lite.menu.lvl.None____=None +d1_mini_lite.menu.lvl.None____.build.debug_level= +d1_mini_lite.menu.lvl.SSL=SSL +d1_mini_lite.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +d1_mini_lite.menu.lvl.TLS_MEM=TLS_MEM +d1_mini_lite.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +d1_mini_lite.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +d1_mini_lite.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +d1_mini_lite.menu.lvl.HTTP_SERVER=HTTP_SERVER +d1_mini_lite.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +d1_mini_lite.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +d1_mini_lite.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +d1_mini_lite.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +d1_mini_lite.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +d1_mini_lite.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +d1_mini_lite.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +d1_mini_lite.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +d1_mini_lite.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +d1_mini_lite.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +d1_mini_lite.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +d1_mini_lite.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +d1_mini_lite.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +d1_mini_lite.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +d1_mini_lite.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +d1_mini_lite.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +d1_mini_lite.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +d1_mini_lite.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +d1_mini_lite.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +d1_mini_lite.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +d1_mini_lite.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +d1_mini_lite.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +d1_mini_lite.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +d1_mini_lite.menu.lvl.CORE=CORE +d1_mini_lite.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +d1_mini_lite.menu.lvl.WIFI=WIFI +d1_mini_lite.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +d1_mini_lite.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +d1_mini_lite.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +d1_mini_lite.menu.lvl.UPDATER=UPDATER +d1_mini_lite.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +d1_mini_lite.menu.lvl.OTA=OTA +d1_mini_lite.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +d1_mini_lite.menu.lvl.OOM=OOM +d1_mini_lite.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +d1_mini_lite.menu.lvl.MDNS=MDNS +d1_mini_lite.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +d1_mini_lite.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +d1_mini_lite.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS +d1_mini_lite.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +d1_mini_lite.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS +d1_mini_lite.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +d1_mini_lite.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +d1_mini_lite.menu.wipe.none=Only Sketch +d1_mini_lite.menu.wipe.none.upload.erase_cmd=version +d1_mini_lite.menu.wipe.sdk=Sketch + WiFi Settings +d1_mini_lite.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 +d1_mini_lite.menu.wipe.all=All Flash Contents +d1_mini_lite.menu.wipe.all.upload.erase_cmd=erase_flash +d1_mini_lite.menu.baud.921600=921600 +d1_mini_lite.menu.baud.921600.upload.speed=921600 +d1_mini_lite.menu.baud.57600=57600 +d1_mini_lite.menu.baud.57600.upload.speed=57600 +d1_mini_lite.menu.baud.115200=115200 +d1_mini_lite.menu.baud.115200.upload.speed=115200 +d1_mini_lite.menu.baud.230400.linux=230400 +d1_mini_lite.menu.baud.230400.macosx=230400 +d1_mini_lite.menu.baud.230400.upload.speed=230400 +d1_mini_lite.menu.baud.256000.windows=256000 +d1_mini_lite.menu.baud.256000.upload.speed=256000 +d1_mini_lite.menu.baud.460800.linux=460800 +d1_mini_lite.menu.baud.460800.macosx=460800 +d1_mini_lite.menu.baud.460800.upload.speed=460800 +d1_mini_lite.menu.baud.512000.windows=512000 +d1_mini_lite.menu.baud.512000.upload.speed=512000 +d1_mini_lite.menu.baud.3000000=3000000 +d1_mini_lite.menu.baud.3000000.upload.speed=3000000 + +############################################################## +d1.name=WeMos D1 R1 +d1.build.board=ESP8266_WEMOS_D1R1 +d1.build.variant=d1 +d1.upload.tool=esptool +d1.upload.maximum_data_size=81920 +d1.upload.wait_for_upload_port=true +d1.upload.erase_cmd=version +d1.serial.disableDTR=true +d1.serial.disableRTS=true +d1.build.mcu=esp8266 +d1.build.core=esp8266 +d1.build.spiffs_pagesize=256 +d1.build.debug_port= +d1.build.debug_level= +d1.menu.xtal.80=80 MHz +d1.menu.xtal.80.build.f_cpu=80000000L +d1.menu.xtal.160=160 MHz +d1.menu.xtal.160.build.f_cpu=160000000L +d1.menu.vt.flash=Flash +d1.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +d1.menu.vt.heap=Heap +d1.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +d1.menu.vt.iram=IRAM +d1.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +d1.menu.exception.legacy=Legacy (new can return nullptr) +d1.menu.exception.legacy.build.exception_flags=-fno-exceptions +d1.menu.exception.legacy.build.stdcpp_lib=-lstdc++ +d1.menu.exception.disabled=Disabled (new can abort) +d1.menu.exception.disabled.build.exception_flags=-fno-exceptions -DNEW_OOM_ABORT +d1.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +d1.menu.exception.enabled=Enabled +d1.menu.exception.enabled.build.exception_flags=-fexceptions +d1.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +d1.menu.ssl.all=All SSL ciphers (most compatible) +d1.menu.ssl.all.build.sslflags= +d1.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +d1.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +d1.upload.resetmethod=nodemcu +d1.build.flash_mode=dio +d1.build.flash_flags=-DFLASHMODE_DIO +d1.build.flash_freq=40 +d1.menu.eesz.4M2M=4MB (FS:2MB OTA:~1019KB) +d1.menu.eesz.4M2M.build.flash_size=4M +d1.menu.eesz.4M2M.build.flash_size_bytes=0x400000 +d1.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld +d1.menu.eesz.4M2M.build.spiffs_pagesize=256 +d1.menu.eesz.4M2M.upload.maximum_size=1044464 +d1.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 +d1.menu.eesz.4M2M.build.spiffs_start=0x200000 +d1.menu.eesz.4M2M.build.spiffs_end=0x3FA000 +d1.menu.eesz.4M2M.build.spiffs_blocksize=8192 +d1.menu.eesz.4M3M=4MB (FS:3MB OTA:~512KB) +d1.menu.eesz.4M3M.build.flash_size=4M +d1.menu.eesz.4M3M.build.flash_size_bytes=0x400000 +d1.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld +d1.menu.eesz.4M3M.build.spiffs_pagesize=256 +d1.menu.eesz.4M3M.upload.maximum_size=1044464 +d1.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 +d1.menu.eesz.4M3M.build.spiffs_start=0x100000 +d1.menu.eesz.4M3M.build.spiffs_end=0x3FA000 +d1.menu.eesz.4M3M.build.spiffs_blocksize=8192 +d1.menu.eesz.4M1M=4MB (FS:1MB OTA:~1019KB) +d1.menu.eesz.4M1M.build.flash_size=4M +d1.menu.eesz.4M1M.build.flash_size_bytes=0x400000 +d1.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld +d1.menu.eesz.4M1M.build.spiffs_pagesize=256 +d1.menu.eesz.4M1M.upload.maximum_size=1044464 +d1.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 +d1.menu.eesz.4M1M.build.spiffs_start=0x300000 +d1.menu.eesz.4M1M.build.spiffs_end=0x3FA000 +d1.menu.eesz.4M1M.build.spiffs_blocksize=8192 +d1.menu.eesz.4M=4MB (FS:none OTA:~1019KB) +d1.menu.eesz.4M.build.flash_size=4M +d1.menu.eesz.4M.build.flash_size_bytes=0x400000 +d1.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld +d1.menu.eesz.4M.build.spiffs_pagesize=256 +d1.menu.eesz.4M.upload.maximum_size=1044464 +d1.menu.eesz.4M.build.rfcal_addr=0x3FC000 +d1.menu.ip.lm2f=v2 Lower Memory +d1.menu.ip.lm2f.build.lwip_include=lwip2/include +d1.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +d1.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +d1.menu.ip.hb2f=v2 Higher Bandwidth +d1.menu.ip.hb2f.build.lwip_include=lwip2/include +d1.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +d1.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +d1.menu.ip.lm2n=v2 Lower Memory (no features) +d1.menu.ip.lm2n.build.lwip_include=lwip2/include +d1.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +d1.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +d1.menu.ip.hb2n=v2 Higher Bandwidth (no features) +d1.menu.ip.hb2n.build.lwip_include=lwip2/include +d1.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +d1.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +d1.menu.ip.lm6f=v2 IPv6 Lower Memory +d1.menu.ip.lm6f.build.lwip_include=lwip2/include +d1.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +d1.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +d1.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +d1.menu.ip.hb6f.build.lwip_include=lwip2/include +d1.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +d1.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +d1.menu.ip.hb1=v1.4 Higher Bandwidth +d1.menu.ip.hb1.build.lwip_lib=-llwip_gcc +d1.menu.ip.hb1.build.lwip_flags=-DLWIP_OPEN_SRC +d1.menu.ip.src=v1.4 Compile from source +d1.menu.ip.src.build.lwip_lib=-llwip_src +d1.menu.ip.src.build.lwip_flags=-DLWIP_OPEN_SRC +d1.menu.ip.src.recipe.hooks.sketch.prebuild.1.pattern=make -C "{runtime.platform.path}/tools/sdk/lwip/src" install TOOLS_PATH="{runtime.tools.xtensa-lx106-elf-gcc.path}/bin/xtensa-lx106-elf-" +d1.menu.dbg.Disabled=Disabled +d1.menu.dbg.Disabled.build.debug_port= +d1.menu.dbg.Serial=Serial +d1.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +d1.menu.dbg.Serial1=Serial1 +d1.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +d1.menu.lvl.None____=None +d1.menu.lvl.None____.build.debug_level= +d1.menu.lvl.SSL=SSL +d1.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +d1.menu.lvl.TLS_MEM=TLS_MEM +d1.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +d1.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +d1.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +d1.menu.lvl.HTTP_SERVER=HTTP_SERVER +d1.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +d1.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +d1.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +d1.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +d1.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +d1.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +d1.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +d1.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +d1.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +d1.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +d1.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +d1.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +d1.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +d1.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +d1.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +d1.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +d1.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +d1.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +d1.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +d1.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +d1.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +d1.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +d1.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +d1.menu.lvl.CORE=CORE +d1.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +d1.menu.lvl.WIFI=WIFI +d1.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +d1.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +d1.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +d1.menu.lvl.UPDATER=UPDATER +d1.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +d1.menu.lvl.OTA=OTA +d1.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +d1.menu.lvl.OOM=OOM +d1.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +d1.menu.lvl.MDNS=MDNS +d1.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +d1.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +d1.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS +d1.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +d1.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS +d1.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +d1.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +d1.menu.wipe.none=Only Sketch +d1.menu.wipe.none.upload.erase_cmd=version +d1.menu.wipe.sdk=Sketch + WiFi Settings +d1.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 +d1.menu.wipe.all=All Flash Contents +d1.menu.wipe.all.upload.erase_cmd=erase_flash +d1.menu.baud.921600=921600 +d1.menu.baud.921600.upload.speed=921600 +d1.menu.baud.57600=57600 +d1.menu.baud.57600.upload.speed=57600 +d1.menu.baud.115200=115200 +d1.menu.baud.115200.upload.speed=115200 +d1.menu.baud.230400.linux=230400 +d1.menu.baud.230400.macosx=230400 +d1.menu.baud.230400.upload.speed=230400 +d1.menu.baud.256000.windows=256000 +d1.menu.baud.256000.upload.speed=256000 +d1.menu.baud.460800.linux=460800 +d1.menu.baud.460800.macosx=460800 +d1.menu.baud.460800.upload.speed=460800 +d1.menu.baud.512000.windows=512000 +d1.menu.baud.512000.upload.speed=512000 +d1.menu.baud.3000000=3000000 +d1.menu.baud.3000000.upload.speed=3000000 + +############################################################## +espino.name=ESPino (ESP-12 Module) +espino.build.board=ESP8266_ESP12 +espino.build.variant=espino +espino.upload.tool=esptool +espino.upload.maximum_data_size=81920 +espino.upload.wait_for_upload_port=true +espino.upload.erase_cmd=version +espino.serial.disableDTR=true +espino.serial.disableRTS=true +espino.build.mcu=esp8266 +espino.build.core=esp8266 +espino.build.spiffs_pagesize=256 +espino.build.debug_port= +espino.build.debug_level= +espino.menu.xtal.80=80 MHz +espino.menu.xtal.80.build.f_cpu=80000000L +espino.menu.xtal.160=160 MHz +espino.menu.xtal.160.build.f_cpu=160000000L +espino.menu.vt.flash=Flash +espino.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +espino.menu.vt.heap=Heap +espino.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +espino.menu.vt.iram=IRAM +espino.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +espino.menu.exception.legacy=Legacy (new can return nullptr) +espino.menu.exception.legacy.build.exception_flags=-fno-exceptions +espino.menu.exception.legacy.build.stdcpp_lib=-lstdc++ +espino.menu.exception.disabled=Disabled (new can abort) +espino.menu.exception.disabled.build.exception_flags=-fno-exceptions -DNEW_OOM_ABORT +espino.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +espino.menu.exception.enabled=Enabled +espino.menu.exception.enabled.build.exception_flags=-fexceptions +espino.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +espino.menu.ssl.all=All SSL ciphers (most compatible) +espino.menu.ssl.all.build.sslflags= +espino.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +espino.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +espino.menu.ResetMethod.ck=ck +espino.menu.ResetMethod.ck.upload.resetmethod=ck +espino.menu.ResetMethod.nodemcu=nodemcu +espino.menu.ResetMethod.nodemcu.upload.resetmethod=nodemcu +espino.build.flash_mode=qio +espino.build.flash_flags=-DFLASHMODE_QIO +espino.build.flash_freq=40 +espino.menu.eesz.4M2M=4MB (FS:2MB OTA:~1019KB) +espino.menu.eesz.4M2M.build.flash_size=4M +espino.menu.eesz.4M2M.build.flash_size_bytes=0x400000 +espino.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld +espino.menu.eesz.4M2M.build.spiffs_pagesize=256 +espino.menu.eesz.4M2M.upload.maximum_size=1044464 +espino.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 +espino.menu.eesz.4M2M.build.spiffs_start=0x200000 +espino.menu.eesz.4M2M.build.spiffs_end=0x3FA000 +espino.menu.eesz.4M2M.build.spiffs_blocksize=8192 +espino.menu.eesz.4M3M=4MB (FS:3MB OTA:~512KB) +espino.menu.eesz.4M3M.build.flash_size=4M +espino.menu.eesz.4M3M.build.flash_size_bytes=0x400000 +espino.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld +espino.menu.eesz.4M3M.build.spiffs_pagesize=256 +espino.menu.eesz.4M3M.upload.maximum_size=1044464 +espino.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 +espino.menu.eesz.4M3M.build.spiffs_start=0x100000 +espino.menu.eesz.4M3M.build.spiffs_end=0x3FA000 +espino.menu.eesz.4M3M.build.spiffs_blocksize=8192 +espino.menu.eesz.4M1M=4MB (FS:1MB OTA:~1019KB) +espino.menu.eesz.4M1M.build.flash_size=4M +espino.menu.eesz.4M1M.build.flash_size_bytes=0x400000 +espino.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld +espino.menu.eesz.4M1M.build.spiffs_pagesize=256 +espino.menu.eesz.4M1M.upload.maximum_size=1044464 +espino.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 +espino.menu.eesz.4M1M.build.spiffs_start=0x300000 +espino.menu.eesz.4M1M.build.spiffs_end=0x3FA000 +espino.menu.eesz.4M1M.build.spiffs_blocksize=8192 +espino.menu.eesz.4M=4MB (FS:none OTA:~1019KB) +espino.menu.eesz.4M.build.flash_size=4M +espino.menu.eesz.4M.build.flash_size_bytes=0x400000 +espino.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld +espino.menu.eesz.4M.build.spiffs_pagesize=256 +espino.menu.eesz.4M.upload.maximum_size=1044464 +espino.menu.eesz.4M.build.rfcal_addr=0x3FC000 +espino.menu.ip.lm2f=v2 Lower Memory +espino.menu.ip.lm2f.build.lwip_include=lwip2/include +espino.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +espino.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +espino.menu.ip.hb2f=v2 Higher Bandwidth +espino.menu.ip.hb2f.build.lwip_include=lwip2/include +espino.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +espino.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +espino.menu.ip.lm2n=v2 Lower Memory (no features) +espino.menu.ip.lm2n.build.lwip_include=lwip2/include +espino.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +espino.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +espino.menu.ip.hb2n=v2 Higher Bandwidth (no features) +espino.menu.ip.hb2n.build.lwip_include=lwip2/include +espino.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +espino.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +espino.menu.ip.lm6f=v2 IPv6 Lower Memory +espino.menu.ip.lm6f.build.lwip_include=lwip2/include +espino.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +espino.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +espino.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +espino.menu.ip.hb6f.build.lwip_include=lwip2/include +espino.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +espino.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +espino.menu.ip.hb1=v1.4 Higher Bandwidth +espino.menu.ip.hb1.build.lwip_lib=-llwip_gcc +espino.menu.ip.hb1.build.lwip_flags=-DLWIP_OPEN_SRC +espino.menu.ip.src=v1.4 Compile from source +espino.menu.ip.src.build.lwip_lib=-llwip_src +espino.menu.ip.src.build.lwip_flags=-DLWIP_OPEN_SRC +espino.menu.ip.src.recipe.hooks.sketch.prebuild.1.pattern=make -C "{runtime.platform.path}/tools/sdk/lwip/src" install TOOLS_PATH="{runtime.tools.xtensa-lx106-elf-gcc.path}/bin/xtensa-lx106-elf-" +espino.menu.dbg.Disabled=Disabled +espino.menu.dbg.Disabled.build.debug_port= +espino.menu.dbg.Serial=Serial +espino.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +espino.menu.dbg.Serial1=Serial1 +espino.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +espino.menu.lvl.None____=None +espino.menu.lvl.None____.build.debug_level= +espino.menu.lvl.SSL=SSL +espino.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +espino.menu.lvl.TLS_MEM=TLS_MEM +espino.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +espino.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +espino.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +espino.menu.lvl.HTTP_SERVER=HTTP_SERVER +espino.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +espino.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +espino.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +espino.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +espino.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +espino.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +espino.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +espino.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +espino.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +espino.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +espino.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +espino.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +espino.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +espino.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +espino.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +espino.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +espino.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +espino.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +espino.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +espino.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +espino.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +espino.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +espino.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +espino.menu.lvl.CORE=CORE +espino.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +espino.menu.lvl.WIFI=WIFI +espino.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +espino.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +espino.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +espino.menu.lvl.UPDATER=UPDATER +espino.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +espino.menu.lvl.OTA=OTA +espino.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +espino.menu.lvl.OOM=OOM +espino.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +espino.menu.lvl.MDNS=MDNS +espino.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +espino.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +espino.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS +espino.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +espino.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS +espino.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +espino.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +espino.menu.wipe.none=Only Sketch +espino.menu.wipe.none.upload.erase_cmd=version +espino.menu.wipe.sdk=Sketch + WiFi Settings +espino.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 +espino.menu.wipe.all=All Flash Contents +espino.menu.wipe.all.upload.erase_cmd=erase_flash +espino.menu.baud.115200=115200 +espino.menu.baud.115200.upload.speed=115200 +espino.menu.baud.57600=57600 +espino.menu.baud.57600.upload.speed=57600 +espino.menu.baud.230400.linux=230400 +espino.menu.baud.230400.macosx=230400 +espino.menu.baud.230400.upload.speed=230400 +espino.menu.baud.256000.windows=256000 +espino.menu.baud.256000.upload.speed=256000 +espino.menu.baud.460800.linux=460800 +espino.menu.baud.460800.macosx=460800 +espino.menu.baud.460800.upload.speed=460800 +espino.menu.baud.512000.windows=512000 +espino.menu.baud.512000.upload.speed=512000 +espino.menu.baud.921600=921600 +espino.menu.baud.921600.upload.speed=921600 +espino.menu.baud.3000000=3000000 +espino.menu.baud.3000000.upload.speed=3000000 + +############################################################## +espinotee.name=ThaiEasyElec's ESPino +espinotee.build.board=ESP8266_ESP13 +espinotee.build.variant=espinotee +espinotee.upload.tool=esptool +espinotee.upload.maximum_data_size=81920 +espinotee.upload.wait_for_upload_port=true +espinotee.upload.erase_cmd=version +espinotee.serial.disableDTR=true +espinotee.serial.disableRTS=true +espinotee.build.mcu=esp8266 +espinotee.build.core=esp8266 +espinotee.build.spiffs_pagesize=256 +espinotee.build.debug_port= +espinotee.build.debug_level= +espinotee.menu.xtal.80=80 MHz +espinotee.menu.xtal.80.build.f_cpu=80000000L +espinotee.menu.xtal.160=160 MHz +espinotee.menu.xtal.160.build.f_cpu=160000000L +espinotee.menu.vt.flash=Flash +espinotee.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +espinotee.menu.vt.heap=Heap +espinotee.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +espinotee.menu.vt.iram=IRAM +espinotee.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +espinotee.menu.exception.legacy=Legacy (new can return nullptr) +espinotee.menu.exception.legacy.build.exception_flags=-fno-exceptions +espinotee.menu.exception.legacy.build.stdcpp_lib=-lstdc++ +espinotee.menu.exception.disabled=Disabled (new can abort) +espinotee.menu.exception.disabled.build.exception_flags=-fno-exceptions -DNEW_OOM_ABORT +espinotee.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +espinotee.menu.exception.enabled=Enabled +espinotee.menu.exception.enabled.build.exception_flags=-fexceptions +espinotee.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +espinotee.menu.ssl.all=All SSL ciphers (most compatible) +espinotee.menu.ssl.all.build.sslflags= +espinotee.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +espinotee.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +espinotee.upload.resetmethod=nodemcu +espinotee.build.flash_mode=qio +espinotee.build.flash_flags=-DFLASHMODE_QIO +espinotee.build.flash_freq=40 +espinotee.menu.eesz.4M2M=4MB (FS:2MB OTA:~1019KB) +espinotee.menu.eesz.4M2M.build.flash_size=4M +espinotee.menu.eesz.4M2M.build.flash_size_bytes=0x400000 +espinotee.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld +espinotee.menu.eesz.4M2M.build.spiffs_pagesize=256 +espinotee.menu.eesz.4M2M.upload.maximum_size=1044464 +espinotee.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 +espinotee.menu.eesz.4M2M.build.spiffs_start=0x200000 +espinotee.menu.eesz.4M2M.build.spiffs_end=0x3FA000 +espinotee.menu.eesz.4M2M.build.spiffs_blocksize=8192 +espinotee.menu.eesz.4M3M=4MB (FS:3MB OTA:~512KB) +espinotee.menu.eesz.4M3M.build.flash_size=4M +espinotee.menu.eesz.4M3M.build.flash_size_bytes=0x400000 +espinotee.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld +espinotee.menu.eesz.4M3M.build.spiffs_pagesize=256 +espinotee.menu.eesz.4M3M.upload.maximum_size=1044464 +espinotee.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 +espinotee.menu.eesz.4M3M.build.spiffs_start=0x100000 +espinotee.menu.eesz.4M3M.build.spiffs_end=0x3FA000 +espinotee.menu.eesz.4M3M.build.spiffs_blocksize=8192 +espinotee.menu.eesz.4M1M=4MB (FS:1MB OTA:~1019KB) +espinotee.menu.eesz.4M1M.build.flash_size=4M +espinotee.menu.eesz.4M1M.build.flash_size_bytes=0x400000 +espinotee.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld +espinotee.menu.eesz.4M1M.build.spiffs_pagesize=256 +espinotee.menu.eesz.4M1M.upload.maximum_size=1044464 +espinotee.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 +espinotee.menu.eesz.4M1M.build.spiffs_start=0x300000 +espinotee.menu.eesz.4M1M.build.spiffs_end=0x3FA000 +espinotee.menu.eesz.4M1M.build.spiffs_blocksize=8192 +espinotee.menu.eesz.4M=4MB (FS:none OTA:~1019KB) +espinotee.menu.eesz.4M.build.flash_size=4M +espinotee.menu.eesz.4M.build.flash_size_bytes=0x400000 +espinotee.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld +espinotee.menu.eesz.4M.build.spiffs_pagesize=256 +espinotee.menu.eesz.4M.upload.maximum_size=1044464 +espinotee.menu.eesz.4M.build.rfcal_addr=0x3FC000 +espinotee.menu.ip.lm2f=v2 Lower Memory +espinotee.menu.ip.lm2f.build.lwip_include=lwip2/include +espinotee.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +espinotee.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +espinotee.menu.ip.hb2f=v2 Higher Bandwidth +espinotee.menu.ip.hb2f.build.lwip_include=lwip2/include +espinotee.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +espinotee.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +espinotee.menu.ip.lm2n=v2 Lower Memory (no features) +espinotee.menu.ip.lm2n.build.lwip_include=lwip2/include +espinotee.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +espinotee.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +espinotee.menu.ip.hb2n=v2 Higher Bandwidth (no features) +espinotee.menu.ip.hb2n.build.lwip_include=lwip2/include +espinotee.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +espinotee.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +espinotee.menu.ip.lm6f=v2 IPv6 Lower Memory +espinotee.menu.ip.lm6f.build.lwip_include=lwip2/include +espinotee.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +espinotee.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +espinotee.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +espinotee.menu.ip.hb6f.build.lwip_include=lwip2/include +espinotee.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +espinotee.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +espinotee.menu.ip.hb1=v1.4 Higher Bandwidth +espinotee.menu.ip.hb1.build.lwip_lib=-llwip_gcc +espinotee.menu.ip.hb1.build.lwip_flags=-DLWIP_OPEN_SRC +espinotee.menu.ip.src=v1.4 Compile from source +espinotee.menu.ip.src.build.lwip_lib=-llwip_src +espinotee.menu.ip.src.build.lwip_flags=-DLWIP_OPEN_SRC +espinotee.menu.ip.src.recipe.hooks.sketch.prebuild.1.pattern=make -C "{runtime.platform.path}/tools/sdk/lwip/src" install TOOLS_PATH="{runtime.tools.xtensa-lx106-elf-gcc.path}/bin/xtensa-lx106-elf-" +espinotee.menu.dbg.Disabled=Disabled +espinotee.menu.dbg.Disabled.build.debug_port= +espinotee.menu.dbg.Serial=Serial +espinotee.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +espinotee.menu.dbg.Serial1=Serial1 +espinotee.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +espinotee.menu.lvl.None____=None +espinotee.menu.lvl.None____.build.debug_level= +espinotee.menu.lvl.SSL=SSL +espinotee.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +espinotee.menu.lvl.TLS_MEM=TLS_MEM +espinotee.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +espinotee.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +espinotee.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +espinotee.menu.lvl.HTTP_SERVER=HTTP_SERVER +espinotee.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +espinotee.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +espinotee.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +espinotee.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +espinotee.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +espinotee.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +espinotee.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +espinotee.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +espinotee.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +espinotee.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +espinotee.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +espinotee.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +espinotee.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +espinotee.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +espinotee.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +espinotee.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +espinotee.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +espinotee.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +espinotee.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +espinotee.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +espinotee.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +espinotee.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +espinotee.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +espinotee.menu.lvl.CORE=CORE +espinotee.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +espinotee.menu.lvl.WIFI=WIFI +espinotee.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +espinotee.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +espinotee.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +espinotee.menu.lvl.UPDATER=UPDATER +espinotee.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +espinotee.menu.lvl.OTA=OTA +espinotee.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +espinotee.menu.lvl.OOM=OOM +espinotee.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +espinotee.menu.lvl.MDNS=MDNS +espinotee.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +espinotee.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +espinotee.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS +espinotee.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +espinotee.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS +espinotee.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +espinotee.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +espinotee.menu.wipe.none=Only Sketch +espinotee.menu.wipe.none.upload.erase_cmd=version +espinotee.menu.wipe.sdk=Sketch + WiFi Settings +espinotee.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 +espinotee.menu.wipe.all=All Flash Contents +espinotee.menu.wipe.all.upload.erase_cmd=erase_flash +espinotee.menu.baud.115200=115200 +espinotee.menu.baud.115200.upload.speed=115200 +espinotee.menu.baud.57600=57600 +espinotee.menu.baud.57600.upload.speed=57600 +espinotee.menu.baud.230400.linux=230400 +espinotee.menu.baud.230400.macosx=230400 +espinotee.menu.baud.230400.upload.speed=230400 +espinotee.menu.baud.256000.windows=256000 +espinotee.menu.baud.256000.upload.speed=256000 +espinotee.menu.baud.460800.linux=460800 +espinotee.menu.baud.460800.macosx=460800 +espinotee.menu.baud.460800.upload.speed=460800 +espinotee.menu.baud.512000.windows=512000 +espinotee.menu.baud.512000.upload.speed=512000 +espinotee.menu.baud.921600=921600 +espinotee.menu.baud.921600.upload.speed=921600 +espinotee.menu.baud.3000000=3000000 +espinotee.menu.baud.3000000.upload.speed=3000000 + +############################################################## +wifinfo.name=WifInfo +wifinfo.build.board=WIFINFO +wifinfo.build.variant=wifinfo +wifinfo.menu.ESPModule.ESP07192=ESP07 (1M/192K SPIFFS) +wifinfo.menu.ESPModule.ESP07192.build.board=ESP8266_ESP07 +wifinfo.menu.ESPModule.ESP07192.build.flash_ld=eagle.flash.1m192.ld +wifinfo.menu.ESPModule.ESP07192.build.flash_size=1M +wifinfo.menu.ESPModule.ESP07192.build.spiffs_blocksize=4096 +wifinfo.menu.ESPModule.ESP07192.build.spiffs_end=0xFB000 +wifinfo.menu.ESPModule.ESP07192.build.spiffs_start=0xCB000 +wifinfo.menu.ESPModule.ESP07192.upload.maximum_size=827376 +wifinfo.menu.ESPModule.ESP12=ESP12 (4M/1M SPIFFS) +wifinfo.menu.ESPModule.ESP12.build.board=ESP8266_ESP12 +wifinfo.menu.ESPModule.ESP12.build.flash_ld=eagle.flash.4m1m.ld +wifinfo.menu.ESPModule.ESP12.build.flash_size=4M +wifinfo.menu.ESPModule.ESP12.build.spiffs_blocksize=8192 +wifinfo.menu.ESPModule.ESP12.build.spiffs_end=0x3FB000 +wifinfo.menu.ESPModule.ESP12.build.spiffs_pagesize=256 +wifinfo.menu.ESPModule.ESP12.build.spiffs_start=0x300000 +wifinfo.menu.ESPModule.ESP12.upload.maximum_size=1044464 +wifinfo.upload.tool=esptool +wifinfo.upload.maximum_data_size=81920 +wifinfo.upload.wait_for_upload_port=true +wifinfo.upload.erase_cmd=version +wifinfo.serial.disableDTR=true +wifinfo.serial.disableRTS=true +wifinfo.build.mcu=esp8266 +wifinfo.build.core=esp8266 +wifinfo.build.spiffs_pagesize=256 +wifinfo.build.debug_port= +wifinfo.build.debug_level= +wifinfo.menu.xtal.80=80 MHz +wifinfo.menu.xtal.80.build.f_cpu=80000000L +wifinfo.menu.xtal.160=160 MHz +wifinfo.menu.xtal.160.build.f_cpu=160000000L +wifinfo.menu.vt.flash=Flash +wifinfo.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +wifinfo.menu.vt.heap=Heap +wifinfo.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +wifinfo.menu.vt.iram=IRAM +wifinfo.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +wifinfo.menu.exception.legacy=Legacy (new can return nullptr) +wifinfo.menu.exception.legacy.build.exception_flags=-fno-exceptions +wifinfo.menu.exception.legacy.build.stdcpp_lib=-lstdc++ +wifinfo.menu.exception.disabled=Disabled (new can abort) +wifinfo.menu.exception.disabled.build.exception_flags=-fno-exceptions -DNEW_OOM_ABORT +wifinfo.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +wifinfo.menu.exception.enabled=Enabled +wifinfo.menu.exception.enabled.build.exception_flags=-fexceptions +wifinfo.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +wifinfo.menu.ssl.all=All SSL ciphers (most compatible) +wifinfo.menu.ssl.all.build.sslflags= +wifinfo.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +wifinfo.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +wifinfo.upload.resetmethod=nodemcu +wifinfo.build.flash_mode=qio +wifinfo.build.flash_flags=-DFLASHMODE_QIO +wifinfo.menu.FlashFreq.40=40MHz +wifinfo.menu.FlashFreq.40.build.flash_freq=40 +wifinfo.menu.FlashFreq.80=80MHz +wifinfo.menu.FlashFreq.80.build.flash_freq=80 +wifinfo.menu.FlashFreq.20=20MHz +wifinfo.menu.FlashFreq.20.build.flash_freq=20 +wifinfo.menu.FlashFreq.26=26MHz +wifinfo.menu.FlashFreq.26.build.flash_freq=26 +wifinfo.menu.eesz.1M64=1MB (FS:64KB OTA:~470KB) +wifinfo.menu.eesz.1M64.build.flash_size=1M +wifinfo.menu.eesz.1M64.build.flash_size_bytes=0x100000 +wifinfo.menu.eesz.1M64.build.flash_ld=eagle.flash.1m64.ld +wifinfo.menu.eesz.1M64.build.spiffs_pagesize=256 +wifinfo.menu.eesz.1M64.upload.maximum_size=958448 +wifinfo.menu.eesz.1M64.build.rfcal_addr=0xFC000 +wifinfo.menu.eesz.1M64.build.spiffs_start=0xEB000 +wifinfo.menu.eesz.1M64.build.spiffs_end=0xFB000 +wifinfo.menu.eesz.1M64.build.spiffs_blocksize=4096 +wifinfo.menu.eesz.1M128=1MB (FS:128KB OTA:~438KB) +wifinfo.menu.eesz.1M128.build.flash_size=1M +wifinfo.menu.eesz.1M128.build.flash_size_bytes=0x100000 +wifinfo.menu.eesz.1M128.build.flash_ld=eagle.flash.1m128.ld +wifinfo.menu.eesz.1M128.build.spiffs_pagesize=256 +wifinfo.menu.eesz.1M128.upload.maximum_size=892912 +wifinfo.menu.eesz.1M128.build.rfcal_addr=0xFC000 +wifinfo.menu.eesz.1M128.build.spiffs_start=0xDB000 +wifinfo.menu.eesz.1M128.build.spiffs_end=0xFB000 +wifinfo.menu.eesz.1M128.build.spiffs_blocksize=4096 +wifinfo.menu.eesz.1M144=1MB (FS:144KB OTA:~430KB) +wifinfo.menu.eesz.1M144.build.flash_size=1M +wifinfo.menu.eesz.1M144.build.flash_size_bytes=0x100000 +wifinfo.menu.eesz.1M144.build.flash_ld=eagle.flash.1m144.ld +wifinfo.menu.eesz.1M144.build.spiffs_pagesize=256 +wifinfo.menu.eesz.1M144.upload.maximum_size=876528 +wifinfo.menu.eesz.1M144.build.rfcal_addr=0xFC000 +wifinfo.menu.eesz.1M144.build.spiffs_start=0xD7000 +wifinfo.menu.eesz.1M144.build.spiffs_end=0xFB000 +wifinfo.menu.eesz.1M144.build.spiffs_blocksize=4096 +wifinfo.menu.eesz.1M160=1MB (FS:160KB OTA:~422KB) +wifinfo.menu.eesz.1M160.build.flash_size=1M +wifinfo.menu.eesz.1M160.build.flash_size_bytes=0x100000 +wifinfo.menu.eesz.1M160.build.flash_ld=eagle.flash.1m160.ld +wifinfo.menu.eesz.1M160.build.spiffs_pagesize=256 +wifinfo.menu.eesz.1M160.upload.maximum_size=860144 +wifinfo.menu.eesz.1M160.build.rfcal_addr=0xFC000 +wifinfo.menu.eesz.1M160.build.spiffs_start=0xD3000 +wifinfo.menu.eesz.1M160.build.spiffs_end=0xFB000 +wifinfo.menu.eesz.1M160.build.spiffs_blocksize=4096 +wifinfo.menu.eesz.1M192=1MB (FS:192KB OTA:~406KB) +wifinfo.menu.eesz.1M192.build.flash_size=1M +wifinfo.menu.eesz.1M192.build.flash_size_bytes=0x100000 +wifinfo.menu.eesz.1M192.build.flash_ld=eagle.flash.1m192.ld +wifinfo.menu.eesz.1M192.build.spiffs_pagesize=256 +wifinfo.menu.eesz.1M192.upload.maximum_size=827376 +wifinfo.menu.eesz.1M192.build.rfcal_addr=0xFC000 +wifinfo.menu.eesz.1M192.build.spiffs_start=0xCB000 +wifinfo.menu.eesz.1M192.build.spiffs_end=0xFB000 +wifinfo.menu.eesz.1M192.build.spiffs_blocksize=4096 +wifinfo.menu.eesz.1M256=1MB (FS:256KB OTA:~374KB) +wifinfo.menu.eesz.1M256.build.flash_size=1M +wifinfo.menu.eesz.1M256.build.flash_size_bytes=0x100000 +wifinfo.menu.eesz.1M256.build.flash_ld=eagle.flash.1m256.ld +wifinfo.menu.eesz.1M256.build.spiffs_pagesize=256 +wifinfo.menu.eesz.1M256.upload.maximum_size=761840 +wifinfo.menu.eesz.1M256.build.rfcal_addr=0xFC000 +wifinfo.menu.eesz.1M256.build.spiffs_start=0xBB000 +wifinfo.menu.eesz.1M256.build.spiffs_end=0xFB000 +wifinfo.menu.eesz.1M256.build.spiffs_blocksize=4096 +wifinfo.menu.eesz.1M512=1MB (FS:512KB OTA:~246KB) +wifinfo.menu.eesz.1M512.build.flash_size=1M +wifinfo.menu.eesz.1M512.build.flash_size_bytes=0x100000 +wifinfo.menu.eesz.1M512.build.flash_ld=eagle.flash.1m512.ld +wifinfo.menu.eesz.1M512.build.spiffs_pagesize=256 +wifinfo.menu.eesz.1M512.upload.maximum_size=499696 +wifinfo.menu.eesz.1M512.build.rfcal_addr=0xFC000 +wifinfo.menu.eesz.1M512.build.spiffs_start=0x7B000 +wifinfo.menu.eesz.1M512.build.spiffs_end=0xFB000 +wifinfo.menu.eesz.1M512.build.spiffs_blocksize=8192 +wifinfo.menu.eesz.1M=1MB (FS:none OTA:~502KB) +wifinfo.menu.eesz.1M.build.flash_size=1M +wifinfo.menu.eesz.1M.build.flash_size_bytes=0x100000 +wifinfo.menu.eesz.1M.build.flash_ld=eagle.flash.1m.ld +wifinfo.menu.eesz.1M.build.spiffs_pagesize=256 +wifinfo.menu.eesz.1M.upload.maximum_size=1023984 +wifinfo.menu.eesz.1M.build.rfcal_addr=0xFC000 +wifinfo.menu.ip.lm2f=v2 Lower Memory +wifinfo.menu.ip.lm2f.build.lwip_include=lwip2/include +wifinfo.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +wifinfo.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +wifinfo.menu.ip.hb2f=v2 Higher Bandwidth +wifinfo.menu.ip.hb2f.build.lwip_include=lwip2/include +wifinfo.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +wifinfo.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +wifinfo.menu.ip.lm2n=v2 Lower Memory (no features) +wifinfo.menu.ip.lm2n.build.lwip_include=lwip2/include +wifinfo.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +wifinfo.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +wifinfo.menu.ip.hb2n=v2 Higher Bandwidth (no features) +wifinfo.menu.ip.hb2n.build.lwip_include=lwip2/include +wifinfo.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +wifinfo.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +wifinfo.menu.ip.lm6f=v2 IPv6 Lower Memory +wifinfo.menu.ip.lm6f.build.lwip_include=lwip2/include +wifinfo.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +wifinfo.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +wifinfo.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +wifinfo.menu.ip.hb6f.build.lwip_include=lwip2/include +wifinfo.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +wifinfo.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +wifinfo.menu.ip.hb1=v1.4 Higher Bandwidth +wifinfo.menu.ip.hb1.build.lwip_lib=-llwip_gcc +wifinfo.menu.ip.hb1.build.lwip_flags=-DLWIP_OPEN_SRC +wifinfo.menu.ip.src=v1.4 Compile from source +wifinfo.menu.ip.src.build.lwip_lib=-llwip_src +wifinfo.menu.ip.src.build.lwip_flags=-DLWIP_OPEN_SRC +wifinfo.menu.ip.src.recipe.hooks.sketch.prebuild.1.pattern=make -C "{runtime.platform.path}/tools/sdk/lwip/src" install TOOLS_PATH="{runtime.tools.xtensa-lx106-elf-gcc.path}/bin/xtensa-lx106-elf-" +wifinfo.menu.dbg.Disabled=Disabled +wifinfo.menu.dbg.Disabled.build.debug_port= +wifinfo.menu.dbg.Serial=Serial +wifinfo.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +wifinfo.menu.dbg.Serial1=Serial1 +wifinfo.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +wifinfo.menu.lvl.None____=None +wifinfo.menu.lvl.None____.build.debug_level= +wifinfo.menu.lvl.SSL=SSL +wifinfo.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +wifinfo.menu.lvl.TLS_MEM=TLS_MEM +wifinfo.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +wifinfo.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +wifinfo.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +wifinfo.menu.lvl.HTTP_SERVER=HTTP_SERVER +wifinfo.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +wifinfo.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +wifinfo.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +wifinfo.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +wifinfo.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +wifinfo.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +wifinfo.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +wifinfo.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +wifinfo.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +wifinfo.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +wifinfo.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +wifinfo.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +wifinfo.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +wifinfo.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +wifinfo.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +wifinfo.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +wifinfo.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +wifinfo.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +wifinfo.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +wifinfo.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +wifinfo.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +wifinfo.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +wifinfo.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +wifinfo.menu.lvl.CORE=CORE +wifinfo.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +wifinfo.menu.lvl.WIFI=WIFI +wifinfo.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +wifinfo.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +wifinfo.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +wifinfo.menu.lvl.UPDATER=UPDATER +wifinfo.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +wifinfo.menu.lvl.OTA=OTA +wifinfo.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +wifinfo.menu.lvl.OOM=OOM +wifinfo.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +wifinfo.menu.lvl.MDNS=MDNS +wifinfo.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +wifinfo.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +wifinfo.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS +wifinfo.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +wifinfo.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS +wifinfo.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +wifinfo.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +wifinfo.menu.wipe.none=Only Sketch +wifinfo.menu.wipe.none.upload.erase_cmd=version +wifinfo.menu.wipe.sdk=Sketch + WiFi Settings +wifinfo.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 +wifinfo.menu.wipe.all=All Flash Contents +wifinfo.menu.wipe.all.upload.erase_cmd=erase_flash +wifinfo.menu.baud.115200=115200 +wifinfo.menu.baud.115200.upload.speed=115200 +wifinfo.menu.baud.57600=57600 +wifinfo.menu.baud.57600.upload.speed=57600 +wifinfo.menu.baud.230400.linux=230400 +wifinfo.menu.baud.230400.macosx=230400 +wifinfo.menu.baud.230400.upload.speed=230400 +wifinfo.menu.baud.256000.windows=256000 +wifinfo.menu.baud.256000.upload.speed=256000 +wifinfo.menu.baud.460800.linux=460800 +wifinfo.menu.baud.460800.macosx=460800 +wifinfo.menu.baud.460800.upload.speed=460800 +wifinfo.menu.baud.512000.windows=512000 +wifinfo.menu.baud.512000.upload.speed=512000 +wifinfo.menu.baud.921600=921600 +wifinfo.menu.baud.921600.upload.speed=921600 +wifinfo.menu.baud.3000000=3000000 +wifinfo.menu.baud.3000000.upload.speed=3000000 + +############################################################## +arduino-esp8266.name=Arduino +arduino-esp8266.build.board=ESP8266_ARDUINO +arduino-esp8266.menu.BoardModel.primo=Primo +arduino-esp8266.menu.BoardModel.primo.build.board=ESP8266_ARDUINO_PRIMO +arduino-esp8266.menu.BoardModel.primo.build.extra_flags=-DF_CRYSTAL=40000000 -DESP8266 +arduino-esp8266.menu.BoardModel.primo.build.variant=arduino_spi +arduino-esp8266.menu.BoardModel.starottodeved=Star OTTO +arduino-esp8266.menu.BoardModel.starottodeved.build.board=ESP8266_ARDUINO_STAR_OTTO +arduino-esp8266.menu.BoardModel.starottodeved.build.extra_flags=-DF_CRYSTAL=40000000 -DESP8266 +arduino-esp8266.menu.BoardModel.starottodeved.build.variant=arduino_uart +arduino-esp8266.menu.BoardModel.unowifideved=Uno WiFi +arduino-esp8266.menu.BoardModel.unowifideved.build.board=ESP8266_ARDUINO_UNOWIFI +arduino-esp8266.menu.BoardModel.unowifideved.build.extra_flags=-DF_CRYSTAL=40000000 -DESP8266 +arduino-esp8266.menu.BoardModel.unowifideved.build.variant=arduino_uart +arduino-esp8266.upload.tool=esptool +arduino-esp8266.upload.maximum_data_size=81920 +arduino-esp8266.upload.wait_for_upload_port=true +arduino-esp8266.upload.erase_cmd=version +arduino-esp8266.serial.disableDTR=true +arduino-esp8266.serial.disableRTS=true +arduino-esp8266.build.mcu=esp8266 +arduino-esp8266.build.core=esp8266 +arduino-esp8266.build.variant=generic +arduino-esp8266.build.spiffs_pagesize=256 +arduino-esp8266.build.debug_port= +arduino-esp8266.build.debug_level= +arduino-esp8266.menu.xtal.80=80 MHz +arduino-esp8266.menu.xtal.80.build.f_cpu=80000000L +arduino-esp8266.menu.xtal.160=160 MHz +arduino-esp8266.menu.xtal.160.build.f_cpu=160000000L +arduino-esp8266.menu.vt.flash=Flash +arduino-esp8266.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +arduino-esp8266.menu.vt.heap=Heap +arduino-esp8266.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +arduino-esp8266.menu.vt.iram=IRAM +arduino-esp8266.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +arduino-esp8266.menu.exception.legacy=Legacy (new can return nullptr) +arduino-esp8266.menu.exception.legacy.build.exception_flags=-fno-exceptions +arduino-esp8266.menu.exception.legacy.build.stdcpp_lib=-lstdc++ +arduino-esp8266.menu.exception.disabled=Disabled (new can abort) +arduino-esp8266.menu.exception.disabled.build.exception_flags=-fno-exceptions -DNEW_OOM_ABORT +arduino-esp8266.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +arduino-esp8266.menu.exception.enabled=Enabled +arduino-esp8266.menu.exception.enabled.build.exception_flags=-fexceptions +arduino-esp8266.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +arduino-esp8266.menu.ssl.all=All SSL ciphers (most compatible) +arduino-esp8266.menu.ssl.all.build.sslflags= +arduino-esp8266.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +arduino-esp8266.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +arduino-esp8266.upload.resetmethod=ck +arduino-esp8266.build.flash_mode=qio +arduino-esp8266.build.flash_flags=-DFLASHMODE_QIO +arduino-esp8266.build.flash_freq=40 +arduino-esp8266.menu.eesz.4M2M=4MB (FS:2MB OTA:~1019KB) +arduino-esp8266.menu.eesz.4M2M.build.flash_size=4M +arduino-esp8266.menu.eesz.4M2M.build.flash_size_bytes=0x400000 +arduino-esp8266.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld +arduino-esp8266.menu.eesz.4M2M.build.spiffs_pagesize=256 +arduino-esp8266.menu.eesz.4M2M.upload.maximum_size=1044464 +arduino-esp8266.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 +arduino-esp8266.menu.eesz.4M2M.build.spiffs_start=0x200000 +arduino-esp8266.menu.eesz.4M2M.build.spiffs_end=0x3FA000 +arduino-esp8266.menu.eesz.4M2M.build.spiffs_blocksize=8192 +arduino-esp8266.menu.eesz.4M3M=4MB (FS:3MB OTA:~512KB) +arduino-esp8266.menu.eesz.4M3M.build.flash_size=4M +arduino-esp8266.menu.eesz.4M3M.build.flash_size_bytes=0x400000 +arduino-esp8266.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld +arduino-esp8266.menu.eesz.4M3M.build.spiffs_pagesize=256 +arduino-esp8266.menu.eesz.4M3M.upload.maximum_size=1044464 +arduino-esp8266.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 +arduino-esp8266.menu.eesz.4M3M.build.spiffs_start=0x100000 +arduino-esp8266.menu.eesz.4M3M.build.spiffs_end=0x3FA000 +arduino-esp8266.menu.eesz.4M3M.build.spiffs_blocksize=8192 +arduino-esp8266.menu.eesz.4M1M=4MB (FS:1MB OTA:~1019KB) +arduino-esp8266.menu.eesz.4M1M.build.flash_size=4M +arduino-esp8266.menu.eesz.4M1M.build.flash_size_bytes=0x400000 +arduino-esp8266.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld +arduino-esp8266.menu.eesz.4M1M.build.spiffs_pagesize=256 +arduino-esp8266.menu.eesz.4M1M.upload.maximum_size=1044464 +arduino-esp8266.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 +arduino-esp8266.menu.eesz.4M1M.build.spiffs_start=0x300000 +arduino-esp8266.menu.eesz.4M1M.build.spiffs_end=0x3FA000 +arduino-esp8266.menu.eesz.4M1M.build.spiffs_blocksize=8192 +arduino-esp8266.menu.eesz.4M=4MB (FS:none OTA:~1019KB) +arduino-esp8266.menu.eesz.4M.build.flash_size=4M +arduino-esp8266.menu.eesz.4M.build.flash_size_bytes=0x400000 +arduino-esp8266.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld +arduino-esp8266.menu.eesz.4M.build.spiffs_pagesize=256 +arduino-esp8266.menu.eesz.4M.upload.maximum_size=1044464 +arduino-esp8266.menu.eesz.4M.build.rfcal_addr=0x3FC000 +arduino-esp8266.menu.ip.lm2f=v2 Lower Memory +arduino-esp8266.menu.ip.lm2f.build.lwip_include=lwip2/include +arduino-esp8266.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +arduino-esp8266.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +arduino-esp8266.menu.ip.hb2f=v2 Higher Bandwidth +arduino-esp8266.menu.ip.hb2f.build.lwip_include=lwip2/include +arduino-esp8266.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +arduino-esp8266.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +arduino-esp8266.menu.ip.lm2n=v2 Lower Memory (no features) +arduino-esp8266.menu.ip.lm2n.build.lwip_include=lwip2/include +arduino-esp8266.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +arduino-esp8266.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +arduino-esp8266.menu.ip.hb2n=v2 Higher Bandwidth (no features) +arduino-esp8266.menu.ip.hb2n.build.lwip_include=lwip2/include +arduino-esp8266.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +arduino-esp8266.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +arduino-esp8266.menu.ip.lm6f=v2 IPv6 Lower Memory +arduino-esp8266.menu.ip.lm6f.build.lwip_include=lwip2/include +arduino-esp8266.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +arduino-esp8266.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +arduino-esp8266.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +arduino-esp8266.menu.ip.hb6f.build.lwip_include=lwip2/include +arduino-esp8266.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +arduino-esp8266.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +arduino-esp8266.menu.ip.hb1=v1.4 Higher Bandwidth +arduino-esp8266.menu.ip.hb1.build.lwip_lib=-llwip_gcc +arduino-esp8266.menu.ip.hb1.build.lwip_flags=-DLWIP_OPEN_SRC +arduino-esp8266.menu.ip.src=v1.4 Compile from source +arduino-esp8266.menu.ip.src.build.lwip_lib=-llwip_src +arduino-esp8266.menu.ip.src.build.lwip_flags=-DLWIP_OPEN_SRC +arduino-esp8266.menu.ip.src.recipe.hooks.sketch.prebuild.1.pattern=make -C "{runtime.platform.path}/tools/sdk/lwip/src" install TOOLS_PATH="{runtime.tools.xtensa-lx106-elf-gcc.path}/bin/xtensa-lx106-elf-" +arduino-esp8266.menu.dbg.Disabled=Disabled +arduino-esp8266.menu.dbg.Disabled.build.debug_port= +arduino-esp8266.menu.dbg.Serial=Serial +arduino-esp8266.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +arduino-esp8266.menu.dbg.Serial1=Serial1 +arduino-esp8266.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +arduino-esp8266.menu.lvl.None____=None +arduino-esp8266.menu.lvl.None____.build.debug_level= +arduino-esp8266.menu.lvl.SSL=SSL +arduino-esp8266.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +arduino-esp8266.menu.lvl.TLS_MEM=TLS_MEM +arduino-esp8266.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +arduino-esp8266.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +arduino-esp8266.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +arduino-esp8266.menu.lvl.HTTP_SERVER=HTTP_SERVER +arduino-esp8266.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +arduino-esp8266.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +arduino-esp8266.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +arduino-esp8266.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +arduino-esp8266.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +arduino-esp8266.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +arduino-esp8266.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +arduino-esp8266.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +arduino-esp8266.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +arduino-esp8266.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +arduino-esp8266.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +arduino-esp8266.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +arduino-esp8266.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +arduino-esp8266.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +arduino-esp8266.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +arduino-esp8266.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +arduino-esp8266.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +arduino-esp8266.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +arduino-esp8266.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +arduino-esp8266.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +arduino-esp8266.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +arduino-esp8266.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +arduino-esp8266.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +arduino-esp8266.menu.lvl.CORE=CORE +arduino-esp8266.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +arduino-esp8266.menu.lvl.WIFI=WIFI +arduino-esp8266.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +arduino-esp8266.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +arduino-esp8266.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +arduino-esp8266.menu.lvl.UPDATER=UPDATER +arduino-esp8266.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +arduino-esp8266.menu.lvl.OTA=OTA +arduino-esp8266.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +arduino-esp8266.menu.lvl.OOM=OOM +arduino-esp8266.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +arduino-esp8266.menu.lvl.MDNS=MDNS +arduino-esp8266.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +arduino-esp8266.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +arduino-esp8266.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS +arduino-esp8266.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +arduino-esp8266.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS +arduino-esp8266.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +arduino-esp8266.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +arduino-esp8266.menu.wipe.none=Only Sketch +arduino-esp8266.menu.wipe.none.upload.erase_cmd=version +arduino-esp8266.menu.wipe.sdk=Sketch + WiFi Settings +arduino-esp8266.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 +arduino-esp8266.menu.wipe.all=All Flash Contents +arduino-esp8266.menu.wipe.all.upload.erase_cmd=erase_flash +arduino-esp8266.menu.baud.115200=115200 +arduino-esp8266.menu.baud.115200.upload.speed=115200 +arduino-esp8266.menu.baud.57600=57600 +arduino-esp8266.menu.baud.57600.upload.speed=57600 +arduino-esp8266.menu.baud.230400.linux=230400 +arduino-esp8266.menu.baud.230400.macosx=230400 +arduino-esp8266.menu.baud.230400.upload.speed=230400 +arduino-esp8266.menu.baud.256000.windows=256000 +arduino-esp8266.menu.baud.256000.upload.speed=256000 +arduino-esp8266.menu.baud.460800.linux=460800 +arduino-esp8266.menu.baud.460800.macosx=460800 +arduino-esp8266.menu.baud.460800.upload.speed=460800 +arduino-esp8266.menu.baud.512000.windows=512000 +arduino-esp8266.menu.baud.512000.upload.speed=512000 +arduino-esp8266.menu.baud.921600=921600 +arduino-esp8266.menu.baud.921600.upload.speed=921600 +arduino-esp8266.menu.baud.3000000=3000000 +arduino-esp8266.menu.baud.3000000.upload.speed=3000000 + +############################################################## +gen4iod.name=4D Systems gen4 IoD Range +gen4iod.build.board=GEN4_IOD +gen4iod.build.f_cpu=160000000L +gen4iod.build.variant=generic +gen4iod.upload.tool=esptool +gen4iod.upload.maximum_data_size=81920 +gen4iod.upload.wait_for_upload_port=true +gen4iod.upload.erase_cmd=version +gen4iod.serial.disableDTR=true +gen4iod.serial.disableRTS=true +gen4iod.build.mcu=esp8266 +gen4iod.build.core=esp8266 +gen4iod.build.spiffs_pagesize=256 +gen4iod.build.debug_port= +gen4iod.build.debug_level= +gen4iod.menu.xtal.80=80 MHz +gen4iod.menu.xtal.80.build.f_cpu=80000000L +gen4iod.menu.xtal.160=160 MHz +gen4iod.menu.xtal.160.build.f_cpu=160000000L +gen4iod.menu.vt.flash=Flash +gen4iod.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +gen4iod.menu.vt.heap=Heap +gen4iod.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +gen4iod.menu.vt.iram=IRAM +gen4iod.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +gen4iod.menu.exception.legacy=Legacy (new can return nullptr) +gen4iod.menu.exception.legacy.build.exception_flags=-fno-exceptions +gen4iod.menu.exception.legacy.build.stdcpp_lib=-lstdc++ +gen4iod.menu.exception.disabled=Disabled (new can abort) +gen4iod.menu.exception.disabled.build.exception_flags=-fno-exceptions -DNEW_OOM_ABORT +gen4iod.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +gen4iod.menu.exception.enabled=Enabled +gen4iod.menu.exception.enabled.build.exception_flags=-fexceptions +gen4iod.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +gen4iod.menu.ssl.all=All SSL ciphers (most compatible) +gen4iod.menu.ssl.all.build.sslflags= +gen4iod.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +gen4iod.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +gen4iod.upload.resetmethod=nodemcu +gen4iod.build.flash_mode=dio +gen4iod.build.flash_flags=-DFLASHMODE_DIO +gen4iod.build.flash_freq=80 +gen4iod.menu.eesz.512K32=512KB (FS:32KB OTA:~230KB) +gen4iod.menu.eesz.512K32.build.flash_size=512K +gen4iod.menu.eesz.512K32.build.flash_size_bytes=0x80000 +gen4iod.menu.eesz.512K32.build.flash_ld=eagle.flash.512k32.ld +gen4iod.menu.eesz.512K32.build.spiffs_pagesize=256 +gen4iod.menu.eesz.512K32.upload.maximum_size=466928 +gen4iod.menu.eesz.512K32.build.rfcal_addr=0x7C000 +gen4iod.menu.eesz.512K32.build.spiffs_start=0x73000 +gen4iod.menu.eesz.512K32.build.spiffs_end=0x7B000 +gen4iod.menu.eesz.512K32.build.spiffs_blocksize=4096 +gen4iod.menu.eesz.512K64=512KB (FS:64KB OTA:~214KB) +gen4iod.menu.eesz.512K64.build.flash_size=512K +gen4iod.menu.eesz.512K64.build.flash_size_bytes=0x80000 +gen4iod.menu.eesz.512K64.build.flash_ld=eagle.flash.512k64.ld +gen4iod.menu.eesz.512K64.build.spiffs_pagesize=256 +gen4iod.menu.eesz.512K64.upload.maximum_size=434160 +gen4iod.menu.eesz.512K64.build.rfcal_addr=0x7C000 +gen4iod.menu.eesz.512K64.build.spiffs_start=0x6B000 +gen4iod.menu.eesz.512K64.build.spiffs_end=0x7B000 +gen4iod.menu.eesz.512K64.build.spiffs_blocksize=4096 +gen4iod.menu.eesz.512K128=512KB (FS:128KB OTA:~182KB) +gen4iod.menu.eesz.512K128.build.flash_size=512K +gen4iod.menu.eesz.512K128.build.flash_size_bytes=0x80000 +gen4iod.menu.eesz.512K128.build.flash_ld=eagle.flash.512k128.ld +gen4iod.menu.eesz.512K128.build.spiffs_pagesize=256 +gen4iod.menu.eesz.512K128.upload.maximum_size=368624 +gen4iod.menu.eesz.512K128.build.rfcal_addr=0x7C000 +gen4iod.menu.eesz.512K128.build.spiffs_start=0x5B000 +gen4iod.menu.eesz.512K128.build.spiffs_end=0x7B000 +gen4iod.menu.eesz.512K128.build.spiffs_blocksize=4096 +gen4iod.menu.eesz.512K=512KB (FS:none OTA:~246KB) +gen4iod.menu.eesz.512K.build.flash_size=512K +gen4iod.menu.eesz.512K.build.flash_size_bytes=0x80000 +gen4iod.menu.eesz.512K.build.flash_ld=eagle.flash.512k.ld +gen4iod.menu.eesz.512K.build.spiffs_pagesize=256 +gen4iod.menu.eesz.512K.upload.maximum_size=499696 +gen4iod.menu.eesz.512K.build.rfcal_addr=0x7C000 +gen4iod.menu.ip.lm2f=v2 Lower Memory +gen4iod.menu.ip.lm2f.build.lwip_include=lwip2/include +gen4iod.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +gen4iod.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +gen4iod.menu.ip.hb2f=v2 Higher Bandwidth +gen4iod.menu.ip.hb2f.build.lwip_include=lwip2/include +gen4iod.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +gen4iod.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +gen4iod.menu.ip.lm2n=v2 Lower Memory (no features) +gen4iod.menu.ip.lm2n.build.lwip_include=lwip2/include +gen4iod.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +gen4iod.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +gen4iod.menu.ip.hb2n=v2 Higher Bandwidth (no features) +gen4iod.menu.ip.hb2n.build.lwip_include=lwip2/include +gen4iod.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +gen4iod.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +gen4iod.menu.ip.lm6f=v2 IPv6 Lower Memory +gen4iod.menu.ip.lm6f.build.lwip_include=lwip2/include +gen4iod.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +gen4iod.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +gen4iod.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +gen4iod.menu.ip.hb6f.build.lwip_include=lwip2/include +gen4iod.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +gen4iod.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +gen4iod.menu.ip.hb1=v1.4 Higher Bandwidth +gen4iod.menu.ip.hb1.build.lwip_lib=-llwip_gcc +gen4iod.menu.ip.hb1.build.lwip_flags=-DLWIP_OPEN_SRC +gen4iod.menu.ip.src=v1.4 Compile from source +gen4iod.menu.ip.src.build.lwip_lib=-llwip_src +gen4iod.menu.ip.src.build.lwip_flags=-DLWIP_OPEN_SRC +gen4iod.menu.ip.src.recipe.hooks.sketch.prebuild.1.pattern=make -C "{runtime.platform.path}/tools/sdk/lwip/src" install TOOLS_PATH="{runtime.tools.xtensa-lx106-elf-gcc.path}/bin/xtensa-lx106-elf-" +gen4iod.menu.dbg.Disabled=Disabled +gen4iod.menu.dbg.Disabled.build.debug_port= +gen4iod.menu.dbg.Serial=Serial +gen4iod.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +gen4iod.menu.dbg.Serial1=Serial1 +gen4iod.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +gen4iod.menu.lvl.None____=None +gen4iod.menu.lvl.None____.build.debug_level= +gen4iod.menu.lvl.SSL=SSL +gen4iod.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +gen4iod.menu.lvl.TLS_MEM=TLS_MEM +gen4iod.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +gen4iod.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +gen4iod.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +gen4iod.menu.lvl.HTTP_SERVER=HTTP_SERVER +gen4iod.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +gen4iod.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +gen4iod.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +gen4iod.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +gen4iod.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +gen4iod.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +gen4iod.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +gen4iod.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +gen4iod.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +gen4iod.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +gen4iod.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +gen4iod.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +gen4iod.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +gen4iod.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +gen4iod.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +gen4iod.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +gen4iod.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +gen4iod.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +gen4iod.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +gen4iod.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +gen4iod.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +gen4iod.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +gen4iod.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +gen4iod.menu.lvl.CORE=CORE +gen4iod.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +gen4iod.menu.lvl.WIFI=WIFI +gen4iod.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +gen4iod.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +gen4iod.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +gen4iod.menu.lvl.UPDATER=UPDATER +gen4iod.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +gen4iod.menu.lvl.OTA=OTA +gen4iod.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +gen4iod.menu.lvl.OOM=OOM +gen4iod.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +gen4iod.menu.lvl.MDNS=MDNS +gen4iod.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +gen4iod.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +gen4iod.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS +gen4iod.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +gen4iod.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS +gen4iod.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +gen4iod.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +gen4iod.menu.wipe.none=Only Sketch +gen4iod.menu.wipe.none.upload.erase_cmd=version +gen4iod.menu.wipe.sdk=Sketch + WiFi Settings +gen4iod.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 +gen4iod.menu.wipe.all=All Flash Contents +gen4iod.menu.wipe.all.upload.erase_cmd=erase_flash +gen4iod.menu.baud.115200=115200 +gen4iod.menu.baud.115200.upload.speed=115200 +gen4iod.menu.baud.57600=57600 +gen4iod.menu.baud.57600.upload.speed=57600 +gen4iod.menu.baud.230400.linux=230400 +gen4iod.menu.baud.230400.macosx=230400 +gen4iod.menu.baud.230400.upload.speed=230400 +gen4iod.menu.baud.256000.windows=256000 +gen4iod.menu.baud.256000.upload.speed=256000 +gen4iod.menu.baud.460800.linux=460800 +gen4iod.menu.baud.460800.macosx=460800 +gen4iod.menu.baud.460800.upload.speed=460800 +gen4iod.menu.baud.512000.windows=512000 +gen4iod.menu.baud.512000.upload.speed=512000 +gen4iod.menu.baud.921600=921600 +gen4iod.menu.baud.921600.upload.speed=921600 +gen4iod.menu.baud.3000000=3000000 +gen4iod.menu.baud.3000000.upload.speed=3000000 + +############################################################## +oak.name=Digistump Oak +oak.build.board=ESP8266_OAK +oak.build.variant=oak +oak.upload.maximum_size=1040368 +oak.upload.tool=esptool +oak.upload.maximum_data_size=81920 +oak.upload.wait_for_upload_port=true +oak.upload.erase_cmd=version +oak.serial.disableDTR=true +oak.serial.disableRTS=true +oak.build.mcu=esp8266 +oak.build.core=esp8266 +oak.build.spiffs_pagesize=256 +oak.build.debug_port= +oak.build.debug_level= +oak.menu.xtal.80=80 MHz +oak.menu.xtal.80.build.f_cpu=80000000L +oak.menu.xtal.160=160 MHz +oak.menu.xtal.160.build.f_cpu=160000000L +oak.menu.vt.flash=Flash +oak.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +oak.menu.vt.heap=Heap +oak.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +oak.menu.vt.iram=IRAM +oak.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +oak.menu.exception.legacy=Legacy (new can return nullptr) +oak.menu.exception.legacy.build.exception_flags=-fno-exceptions +oak.menu.exception.legacy.build.stdcpp_lib=-lstdc++ +oak.menu.exception.disabled=Disabled (new can abort) +oak.menu.exception.disabled.build.exception_flags=-fno-exceptions -DNEW_OOM_ABORT +oak.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +oak.menu.exception.enabled=Enabled +oak.menu.exception.enabled.build.exception_flags=-fexceptions +oak.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +oak.menu.ssl.all=All SSL ciphers (most compatible) +oak.menu.ssl.all.build.sslflags= +oak.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +oak.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +oak.upload.resetmethod=none +oak.build.flash_mode=dio +oak.build.flash_flags=-DFLASHMODE_DIO +oak.build.flash_freq=40 +oak.menu.eesz.4M2M=4MB (FS:2MB OTA:~1019KB) +oak.menu.eesz.4M2M.build.flash_size=4M +oak.menu.eesz.4M2M.build.flash_size_bytes=0x400000 +oak.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld +oak.menu.eesz.4M2M.build.spiffs_pagesize=256 +oak.menu.eesz.4M2M.upload.maximum_size=1044464 +oak.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 +oak.menu.eesz.4M2M.build.spiffs_start=0x200000 +oak.menu.eesz.4M2M.build.spiffs_end=0x3FA000 +oak.menu.eesz.4M2M.build.spiffs_blocksize=8192 +oak.menu.eesz.4M3M=4MB (FS:3MB OTA:~512KB) +oak.menu.eesz.4M3M.build.flash_size=4M +oak.menu.eesz.4M3M.build.flash_size_bytes=0x400000 +oak.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld +oak.menu.eesz.4M3M.build.spiffs_pagesize=256 +oak.menu.eesz.4M3M.upload.maximum_size=1044464 +oak.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 +oak.menu.eesz.4M3M.build.spiffs_start=0x100000 +oak.menu.eesz.4M3M.build.spiffs_end=0x3FA000 +oak.menu.eesz.4M3M.build.spiffs_blocksize=8192 +oak.menu.eesz.4M1M=4MB (FS:1MB OTA:~1019KB) +oak.menu.eesz.4M1M.build.flash_size=4M +oak.menu.eesz.4M1M.build.flash_size_bytes=0x400000 +oak.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld +oak.menu.eesz.4M1M.build.spiffs_pagesize=256 +oak.menu.eesz.4M1M.upload.maximum_size=1044464 +oak.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 +oak.menu.eesz.4M1M.build.spiffs_start=0x300000 +oak.menu.eesz.4M1M.build.spiffs_end=0x3FA000 +oak.menu.eesz.4M1M.build.spiffs_blocksize=8192 +oak.menu.eesz.4M=4MB (FS:none OTA:~1019KB) +oak.menu.eesz.4M.build.flash_size=4M +oak.menu.eesz.4M.build.flash_size_bytes=0x400000 +oak.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld +oak.menu.eesz.4M.build.spiffs_pagesize=256 +oak.menu.eesz.4M.upload.maximum_size=1044464 +oak.menu.eesz.4M.build.rfcal_addr=0x3FC000 +oak.menu.ip.lm2f=v2 Lower Memory +oak.menu.ip.lm2f.build.lwip_include=lwip2/include +oak.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +oak.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +oak.menu.ip.hb2f=v2 Higher Bandwidth +oak.menu.ip.hb2f.build.lwip_include=lwip2/include +oak.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +oak.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +oak.menu.ip.lm2n=v2 Lower Memory (no features) +oak.menu.ip.lm2n.build.lwip_include=lwip2/include +oak.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +oak.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +oak.menu.ip.hb2n=v2 Higher Bandwidth (no features) +oak.menu.ip.hb2n.build.lwip_include=lwip2/include +oak.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +oak.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +oak.menu.ip.lm6f=v2 IPv6 Lower Memory +oak.menu.ip.lm6f.build.lwip_include=lwip2/include +oak.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +oak.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +oak.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +oak.menu.ip.hb6f.build.lwip_include=lwip2/include +oak.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +oak.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +oak.menu.ip.hb1=v1.4 Higher Bandwidth +oak.menu.ip.hb1.build.lwip_lib=-llwip_gcc +oak.menu.ip.hb1.build.lwip_flags=-DLWIP_OPEN_SRC +oak.menu.ip.src=v1.4 Compile from source +oak.menu.ip.src.build.lwip_lib=-llwip_src +oak.menu.ip.src.build.lwip_flags=-DLWIP_OPEN_SRC +oak.menu.ip.src.recipe.hooks.sketch.prebuild.1.pattern=make -C "{runtime.platform.path}/tools/sdk/lwip/src" install TOOLS_PATH="{runtime.tools.xtensa-lx106-elf-gcc.path}/bin/xtensa-lx106-elf-" +oak.menu.dbg.Disabled=Disabled +oak.menu.dbg.Disabled.build.debug_port= +oak.menu.dbg.Serial=Serial +oak.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +oak.menu.dbg.Serial1=Serial1 +oak.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +oak.menu.lvl.None____=None +oak.menu.lvl.None____.build.debug_level= +oak.menu.lvl.SSL=SSL +oak.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +oak.menu.lvl.TLS_MEM=TLS_MEM +oak.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +oak.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +oak.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +oak.menu.lvl.HTTP_SERVER=HTTP_SERVER +oak.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +oak.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +oak.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +oak.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +oak.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +oak.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +oak.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +oak.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +oak.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +oak.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +oak.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +oak.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +oak.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +oak.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +oak.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +oak.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +oak.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +oak.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +oak.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +oak.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +oak.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +oak.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +oak.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +oak.menu.lvl.CORE=CORE +oak.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +oak.menu.lvl.WIFI=WIFI +oak.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +oak.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +oak.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +oak.menu.lvl.UPDATER=UPDATER +oak.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +oak.menu.lvl.OTA=OTA +oak.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +oak.menu.lvl.OOM=OOM +oak.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +oak.menu.lvl.MDNS=MDNS +oak.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +oak.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +oak.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS +oak.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +oak.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS +oak.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +oak.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +oak.menu.wipe.none=Only Sketch +oak.menu.wipe.none.upload.erase_cmd=version +oak.menu.wipe.sdk=Sketch + WiFi Settings +oak.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 +oak.menu.wipe.all=All Flash Contents +oak.menu.wipe.all.upload.erase_cmd=erase_flash +oak.menu.baud.921600=921600 +oak.menu.baud.921600.upload.speed=921600 +oak.menu.baud.57600=57600 +oak.menu.baud.57600.upload.speed=57600 +oak.menu.baud.115200=115200 +oak.menu.baud.115200.upload.speed=115200 +oak.menu.baud.230400.linux=230400 +oak.menu.baud.230400.macosx=230400 +oak.menu.baud.230400.upload.speed=230400 +oak.menu.baud.256000.windows=256000 +oak.menu.baud.256000.upload.speed=256000 +oak.menu.baud.460800.linux=460800 +oak.menu.baud.460800.macosx=460800 +oak.menu.baud.460800.upload.speed=460800 +oak.menu.baud.512000.windows=512000 +oak.menu.baud.512000.upload.speed=512000 +oak.menu.baud.3000000=3000000 +oak.menu.baud.3000000.upload.speed=3000000 + +############################################################## +wifiduino.name=WiFiduino +wifiduino.build.board=WIFIDUINO_ESP8266 +wifiduino.build.variant=wifiduino +wifiduino.upload.tool=esptool +wifiduino.upload.maximum_data_size=81920 +wifiduino.upload.wait_for_upload_port=true +wifiduino.upload.erase_cmd=version +wifiduino.serial.disableDTR=true +wifiduino.serial.disableRTS=true +wifiduino.build.mcu=esp8266 +wifiduino.build.core=esp8266 +wifiduino.build.spiffs_pagesize=256 +wifiduino.build.debug_port= +wifiduino.build.debug_level= +wifiduino.menu.xtal.80=80 MHz +wifiduino.menu.xtal.80.build.f_cpu=80000000L +wifiduino.menu.xtal.160=160 MHz +wifiduino.menu.xtal.160.build.f_cpu=160000000L +wifiduino.menu.vt.flash=Flash +wifiduino.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +wifiduino.menu.vt.heap=Heap +wifiduino.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +wifiduino.menu.vt.iram=IRAM +wifiduino.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +wifiduino.menu.exception.legacy=Legacy (new can return nullptr) +wifiduino.menu.exception.legacy.build.exception_flags=-fno-exceptions +wifiduino.menu.exception.legacy.build.stdcpp_lib=-lstdc++ +wifiduino.menu.exception.disabled=Disabled (new can abort) +wifiduino.menu.exception.disabled.build.exception_flags=-fno-exceptions -DNEW_OOM_ABORT +wifiduino.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +wifiduino.menu.exception.enabled=Enabled +wifiduino.menu.exception.enabled.build.exception_flags=-fexceptions +wifiduino.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +wifiduino.menu.ssl.all=All SSL ciphers (most compatible) +wifiduino.menu.ssl.all.build.sslflags= +wifiduino.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +wifiduino.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +wifiduino.upload.resetmethod=nodemcu +wifiduino.build.flash_mode=dio +wifiduino.build.flash_flags=-DFLASHMODE_DIO +wifiduino.build.flash_freq=40 +wifiduino.menu.eesz.4M2M=4MB (FS:2MB OTA:~1019KB) +wifiduino.menu.eesz.4M2M.build.flash_size=4M +wifiduino.menu.eesz.4M2M.build.flash_size_bytes=0x400000 +wifiduino.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld +wifiduino.menu.eesz.4M2M.build.spiffs_pagesize=256 +wifiduino.menu.eesz.4M2M.upload.maximum_size=1044464 +wifiduino.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 +wifiduino.menu.eesz.4M2M.build.spiffs_start=0x200000 +wifiduino.menu.eesz.4M2M.build.spiffs_end=0x3FA000 +wifiduino.menu.eesz.4M2M.build.spiffs_blocksize=8192 +wifiduino.menu.eesz.4M3M=4MB (FS:3MB OTA:~512KB) +wifiduino.menu.eesz.4M3M.build.flash_size=4M +wifiduino.menu.eesz.4M3M.build.flash_size_bytes=0x400000 +wifiduino.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld +wifiduino.menu.eesz.4M3M.build.spiffs_pagesize=256 +wifiduino.menu.eesz.4M3M.upload.maximum_size=1044464 +wifiduino.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 +wifiduino.menu.eesz.4M3M.build.spiffs_start=0x100000 +wifiduino.menu.eesz.4M3M.build.spiffs_end=0x3FA000 +wifiduino.menu.eesz.4M3M.build.spiffs_blocksize=8192 +wifiduino.menu.eesz.4M1M=4MB (FS:1MB OTA:~1019KB) +wifiduino.menu.eesz.4M1M.build.flash_size=4M +wifiduino.menu.eesz.4M1M.build.flash_size_bytes=0x400000 +wifiduino.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld +wifiduino.menu.eesz.4M1M.build.spiffs_pagesize=256 +wifiduino.menu.eesz.4M1M.upload.maximum_size=1044464 +wifiduino.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 +wifiduino.menu.eesz.4M1M.build.spiffs_start=0x300000 +wifiduino.menu.eesz.4M1M.build.spiffs_end=0x3FA000 +wifiduino.menu.eesz.4M1M.build.spiffs_blocksize=8192 +wifiduino.menu.eesz.4M=4MB (FS:none OTA:~1019KB) +wifiduino.menu.eesz.4M.build.flash_size=4M +wifiduino.menu.eesz.4M.build.flash_size_bytes=0x400000 +wifiduino.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld +wifiduino.menu.eesz.4M.build.spiffs_pagesize=256 +wifiduino.menu.eesz.4M.upload.maximum_size=1044464 +wifiduino.menu.eesz.4M.build.rfcal_addr=0x3FC000 +wifiduino.menu.ip.lm2f=v2 Lower Memory +wifiduino.menu.ip.lm2f.build.lwip_include=lwip2/include +wifiduino.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +wifiduino.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +wifiduino.menu.ip.hb2f=v2 Higher Bandwidth +wifiduino.menu.ip.hb2f.build.lwip_include=lwip2/include +wifiduino.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +wifiduino.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +wifiduino.menu.ip.lm2n=v2 Lower Memory (no features) +wifiduino.menu.ip.lm2n.build.lwip_include=lwip2/include +wifiduino.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +wifiduino.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +wifiduino.menu.ip.hb2n=v2 Higher Bandwidth (no features) +wifiduino.menu.ip.hb2n.build.lwip_include=lwip2/include +wifiduino.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +wifiduino.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +wifiduino.menu.ip.lm6f=v2 IPv6 Lower Memory +wifiduino.menu.ip.lm6f.build.lwip_include=lwip2/include +wifiduino.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +wifiduino.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +wifiduino.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +wifiduino.menu.ip.hb6f.build.lwip_include=lwip2/include +wifiduino.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +wifiduino.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +wifiduino.menu.ip.hb1=v1.4 Higher Bandwidth +wifiduino.menu.ip.hb1.build.lwip_lib=-llwip_gcc +wifiduino.menu.ip.hb1.build.lwip_flags=-DLWIP_OPEN_SRC +wifiduino.menu.ip.src=v1.4 Compile from source +wifiduino.menu.ip.src.build.lwip_lib=-llwip_src +wifiduino.menu.ip.src.build.lwip_flags=-DLWIP_OPEN_SRC +wifiduino.menu.ip.src.recipe.hooks.sketch.prebuild.1.pattern=make -C "{runtime.platform.path}/tools/sdk/lwip/src" install TOOLS_PATH="{runtime.tools.xtensa-lx106-elf-gcc.path}/bin/xtensa-lx106-elf-" +wifiduino.menu.dbg.Disabled=Disabled +wifiduino.menu.dbg.Disabled.build.debug_port= +wifiduino.menu.dbg.Serial=Serial +wifiduino.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +wifiduino.menu.dbg.Serial1=Serial1 +wifiduino.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +wifiduino.menu.lvl.None____=None +wifiduino.menu.lvl.None____.build.debug_level= +wifiduino.menu.lvl.SSL=SSL +wifiduino.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +wifiduino.menu.lvl.TLS_MEM=TLS_MEM +wifiduino.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +wifiduino.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +wifiduino.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +wifiduino.menu.lvl.HTTP_SERVER=HTTP_SERVER +wifiduino.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +wifiduino.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +wifiduino.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +wifiduino.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +wifiduino.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +wifiduino.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +wifiduino.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +wifiduino.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +wifiduino.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +wifiduino.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +wifiduino.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +wifiduino.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +wifiduino.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +wifiduino.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +wifiduino.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +wifiduino.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +wifiduino.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +wifiduino.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +wifiduino.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +wifiduino.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +wifiduino.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +wifiduino.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +wifiduino.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +wifiduino.menu.lvl.CORE=CORE +wifiduino.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +wifiduino.menu.lvl.WIFI=WIFI +wifiduino.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +wifiduino.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +wifiduino.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +wifiduino.menu.lvl.UPDATER=UPDATER +wifiduino.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +wifiduino.menu.lvl.OTA=OTA +wifiduino.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +wifiduino.menu.lvl.OOM=OOM +wifiduino.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +wifiduino.menu.lvl.MDNS=MDNS +wifiduino.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +wifiduino.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +wifiduino.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS +wifiduino.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +wifiduino.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS +wifiduino.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +wifiduino.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +wifiduino.menu.wipe.none=Only Sketch +wifiduino.menu.wipe.none.upload.erase_cmd=version +wifiduino.menu.wipe.sdk=Sketch + WiFi Settings +wifiduino.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 +wifiduino.menu.wipe.all=All Flash Contents +wifiduino.menu.wipe.all.upload.erase_cmd=erase_flash +wifiduino.menu.baud.921600=921600 +wifiduino.menu.baud.921600.upload.speed=921600 +wifiduino.menu.baud.57600=57600 +wifiduino.menu.baud.57600.upload.speed=57600 +wifiduino.menu.baud.115200=115200 +wifiduino.menu.baud.115200.upload.speed=115200 +wifiduino.menu.baud.230400.linux=230400 +wifiduino.menu.baud.230400.macosx=230400 +wifiduino.menu.baud.230400.upload.speed=230400 +wifiduino.menu.baud.256000.windows=256000 +wifiduino.menu.baud.256000.upload.speed=256000 +wifiduino.menu.baud.460800.linux=460800 +wifiduino.menu.baud.460800.macosx=460800 +wifiduino.menu.baud.460800.upload.speed=460800 +wifiduino.menu.baud.512000.windows=512000 +wifiduino.menu.baud.512000.upload.speed=512000 +wifiduino.menu.baud.3000000=3000000 +wifiduino.menu.baud.3000000.upload.speed=3000000 + +############################################################## +wifi_slot.name=Amperka WiFi Slot +wifi_slot.build.board=AMPERKA_WIFI_SLOT +wifi_slot.build.variant=wifi_slot +wifi_slot.upload.tool=esptool +wifi_slot.upload.maximum_data_size=81920 +wifi_slot.upload.wait_for_upload_port=true +wifi_slot.upload.erase_cmd=version +wifi_slot.serial.disableDTR=true +wifi_slot.serial.disableRTS=true +wifi_slot.build.mcu=esp8266 +wifi_slot.build.core=esp8266 +wifi_slot.build.spiffs_pagesize=256 +wifi_slot.build.debug_port= +wifi_slot.build.debug_level= +wifi_slot.menu.xtal.80=80 MHz +wifi_slot.menu.xtal.80.build.f_cpu=80000000L +wifi_slot.menu.xtal.160=160 MHz +wifi_slot.menu.xtal.160.build.f_cpu=160000000L +wifi_slot.menu.vt.flash=Flash +wifi_slot.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +wifi_slot.menu.vt.heap=Heap +wifi_slot.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +wifi_slot.menu.vt.iram=IRAM +wifi_slot.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +wifi_slot.menu.exception.legacy=Legacy (new can return nullptr) +wifi_slot.menu.exception.legacy.build.exception_flags=-fno-exceptions +wifi_slot.menu.exception.legacy.build.stdcpp_lib=-lstdc++ +wifi_slot.menu.exception.disabled=Disabled (new can abort) +wifi_slot.menu.exception.disabled.build.exception_flags=-fno-exceptions -DNEW_OOM_ABORT +wifi_slot.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +wifi_slot.menu.exception.enabled=Enabled +wifi_slot.menu.exception.enabled.build.exception_flags=-fexceptions +wifi_slot.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +wifi_slot.menu.ssl.all=All SSL ciphers (most compatible) +wifi_slot.menu.ssl.all.build.sslflags= +wifi_slot.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +wifi_slot.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +wifi_slot.upload.resetmethod=nodemcu +wifi_slot.menu.FlashFreq.40=40MHz +wifi_slot.menu.FlashFreq.40.build.flash_freq=40 +wifi_slot.menu.FlashFreq.80=80MHz +wifi_slot.menu.FlashFreq.80.build.flash_freq=80 +wifi_slot.menu.FlashFreq.20=20MHz +wifi_slot.menu.FlashFreq.20.build.flash_freq=20 +wifi_slot.menu.FlashFreq.26=26MHz +wifi_slot.menu.FlashFreq.26.build.flash_freq=26 +wifi_slot.menu.FlashMode.dout=DOUT (compatible) +wifi_slot.menu.FlashMode.dout.build.flash_mode=dout +wifi_slot.menu.FlashMode.dout.build.flash_flags=-DFLASHMODE_DOUT +wifi_slot.menu.FlashMode.dio=DIO +wifi_slot.menu.FlashMode.dio.build.flash_mode=dio +wifi_slot.menu.FlashMode.dio.build.flash_flags=-DFLASHMODE_DIO +wifi_slot.menu.FlashMode.qout=QOUT +wifi_slot.menu.FlashMode.qout.build.flash_mode=qout +wifi_slot.menu.FlashMode.qout.build.flash_flags=-DFLASHMODE_QOUT +wifi_slot.menu.FlashMode.qio=QIO (fast) +wifi_slot.menu.FlashMode.qio.build.flash_mode=qio +wifi_slot.menu.FlashMode.qio.build.flash_flags=-DFLASHMODE_QIO +wifi_slot.menu.eesz.1M64=1MB (FS:64KB OTA:~470KB) +wifi_slot.menu.eesz.1M64.build.flash_size=1M +wifi_slot.menu.eesz.1M64.build.flash_size_bytes=0x100000 +wifi_slot.menu.eesz.1M64.build.flash_ld=eagle.flash.1m64.ld +wifi_slot.menu.eesz.1M64.build.spiffs_pagesize=256 +wifi_slot.menu.eesz.1M64.upload.maximum_size=958448 +wifi_slot.menu.eesz.1M64.build.rfcal_addr=0xFC000 +wifi_slot.menu.eesz.1M64.build.spiffs_start=0xEB000 +wifi_slot.menu.eesz.1M64.build.spiffs_end=0xFB000 +wifi_slot.menu.eesz.1M64.build.spiffs_blocksize=4096 +wifi_slot.menu.eesz.1M128=1MB (FS:128KB OTA:~438KB) +wifi_slot.menu.eesz.1M128.build.flash_size=1M +wifi_slot.menu.eesz.1M128.build.flash_size_bytes=0x100000 +wifi_slot.menu.eesz.1M128.build.flash_ld=eagle.flash.1m128.ld +wifi_slot.menu.eesz.1M128.build.spiffs_pagesize=256 +wifi_slot.menu.eesz.1M128.upload.maximum_size=892912 +wifi_slot.menu.eesz.1M128.build.rfcal_addr=0xFC000 +wifi_slot.menu.eesz.1M128.build.spiffs_start=0xDB000 +wifi_slot.menu.eesz.1M128.build.spiffs_end=0xFB000 +wifi_slot.menu.eesz.1M128.build.spiffs_blocksize=4096 +wifi_slot.menu.eesz.1M144=1MB (FS:144KB OTA:~430KB) +wifi_slot.menu.eesz.1M144.build.flash_size=1M +wifi_slot.menu.eesz.1M144.build.flash_size_bytes=0x100000 +wifi_slot.menu.eesz.1M144.build.flash_ld=eagle.flash.1m144.ld +wifi_slot.menu.eesz.1M144.build.spiffs_pagesize=256 +wifi_slot.menu.eesz.1M144.upload.maximum_size=876528 +wifi_slot.menu.eesz.1M144.build.rfcal_addr=0xFC000 +wifi_slot.menu.eesz.1M144.build.spiffs_start=0xD7000 +wifi_slot.menu.eesz.1M144.build.spiffs_end=0xFB000 +wifi_slot.menu.eesz.1M144.build.spiffs_blocksize=4096 +wifi_slot.menu.eesz.1M160=1MB (FS:160KB OTA:~422KB) +wifi_slot.menu.eesz.1M160.build.flash_size=1M +wifi_slot.menu.eesz.1M160.build.flash_size_bytes=0x100000 +wifi_slot.menu.eesz.1M160.build.flash_ld=eagle.flash.1m160.ld +wifi_slot.menu.eesz.1M160.build.spiffs_pagesize=256 +wifi_slot.menu.eesz.1M160.upload.maximum_size=860144 +wifi_slot.menu.eesz.1M160.build.rfcal_addr=0xFC000 +wifi_slot.menu.eesz.1M160.build.spiffs_start=0xD3000 +wifi_slot.menu.eesz.1M160.build.spiffs_end=0xFB000 +wifi_slot.menu.eesz.1M160.build.spiffs_blocksize=4096 +wifi_slot.menu.eesz.1M192=1MB (FS:192KB OTA:~406KB) +wifi_slot.menu.eesz.1M192.build.flash_size=1M +wifi_slot.menu.eesz.1M192.build.flash_size_bytes=0x100000 +wifi_slot.menu.eesz.1M192.build.flash_ld=eagle.flash.1m192.ld +wifi_slot.menu.eesz.1M192.build.spiffs_pagesize=256 +wifi_slot.menu.eesz.1M192.upload.maximum_size=827376 +wifi_slot.menu.eesz.1M192.build.rfcal_addr=0xFC000 +wifi_slot.menu.eesz.1M192.build.spiffs_start=0xCB000 +wifi_slot.menu.eesz.1M192.build.spiffs_end=0xFB000 +wifi_slot.menu.eesz.1M192.build.spiffs_blocksize=4096 +wifi_slot.menu.eesz.1M256=1MB (FS:256KB OTA:~374KB) +wifi_slot.menu.eesz.1M256.build.flash_size=1M +wifi_slot.menu.eesz.1M256.build.flash_size_bytes=0x100000 +wifi_slot.menu.eesz.1M256.build.flash_ld=eagle.flash.1m256.ld +wifi_slot.menu.eesz.1M256.build.spiffs_pagesize=256 +wifi_slot.menu.eesz.1M256.upload.maximum_size=761840 +wifi_slot.menu.eesz.1M256.build.rfcal_addr=0xFC000 +wifi_slot.menu.eesz.1M256.build.spiffs_start=0xBB000 +wifi_slot.menu.eesz.1M256.build.spiffs_end=0xFB000 +wifi_slot.menu.eesz.1M256.build.spiffs_blocksize=4096 +wifi_slot.menu.eesz.1M512=1MB (FS:512KB OTA:~246KB) +wifi_slot.menu.eesz.1M512.build.flash_size=1M +wifi_slot.menu.eesz.1M512.build.flash_size_bytes=0x100000 +wifi_slot.menu.eesz.1M512.build.flash_ld=eagle.flash.1m512.ld +wifi_slot.menu.eesz.1M512.build.spiffs_pagesize=256 +wifi_slot.menu.eesz.1M512.upload.maximum_size=499696 +wifi_slot.menu.eesz.1M512.build.rfcal_addr=0xFC000 +wifi_slot.menu.eesz.1M512.build.spiffs_start=0x7B000 +wifi_slot.menu.eesz.1M512.build.spiffs_end=0xFB000 +wifi_slot.menu.eesz.1M512.build.spiffs_blocksize=8192 +wifi_slot.menu.eesz.1M=1MB (FS:none OTA:~502KB) +wifi_slot.menu.eesz.1M.build.flash_size=1M +wifi_slot.menu.eesz.1M.build.flash_size_bytes=0x100000 +wifi_slot.menu.eesz.1M.build.flash_ld=eagle.flash.1m.ld +wifi_slot.menu.eesz.1M.build.spiffs_pagesize=256 +wifi_slot.menu.eesz.1M.upload.maximum_size=1023984 +wifi_slot.menu.eesz.1M.build.rfcal_addr=0xFC000 +wifi_slot.menu.eesz.2M64=2MB (FS:64KB OTA:~992KB) +wifi_slot.menu.eesz.2M64.build.flash_size=2M +wifi_slot.menu.eesz.2M64.build.flash_size_bytes=0x200000 +wifi_slot.menu.eesz.2M64.build.flash_ld=eagle.flash.2m64.ld +wifi_slot.menu.eesz.2M64.build.spiffs_pagesize=256 +wifi_slot.menu.eesz.2M64.upload.maximum_size=1044464 +wifi_slot.menu.eesz.2M64.build.rfcal_addr=0x1FC000 +wifi_slot.menu.eesz.2M64.build.spiffs_start=0x1F0000 +wifi_slot.menu.eesz.2M64.build.spiffs_end=0x1FB000 +wifi_slot.menu.eesz.2M64.build.spiffs_blocksize=4096 +wifi_slot.menu.eesz.2M128=2MB (FS:128KB OTA:~960KB) +wifi_slot.menu.eesz.2M128.build.flash_size=2M +wifi_slot.menu.eesz.2M128.build.flash_size_bytes=0x200000 +wifi_slot.menu.eesz.2M128.build.flash_ld=eagle.flash.2m128.ld +wifi_slot.menu.eesz.2M128.build.spiffs_pagesize=256 +wifi_slot.menu.eesz.2M128.upload.maximum_size=1044464 +wifi_slot.menu.eesz.2M128.build.rfcal_addr=0x1FC000 +wifi_slot.menu.eesz.2M128.build.spiffs_start=0x1E0000 +wifi_slot.menu.eesz.2M128.build.spiffs_end=0x1FB000 +wifi_slot.menu.eesz.2M128.build.spiffs_blocksize=4096 +wifi_slot.menu.eesz.2M256=2MB (FS:256KB OTA:~896KB) +wifi_slot.menu.eesz.2M256.build.flash_size=2M +wifi_slot.menu.eesz.2M256.build.flash_size_bytes=0x200000 +wifi_slot.menu.eesz.2M256.build.flash_ld=eagle.flash.2m256.ld +wifi_slot.menu.eesz.2M256.build.spiffs_pagesize=256 +wifi_slot.menu.eesz.2M256.upload.maximum_size=1044464 +wifi_slot.menu.eesz.2M256.build.rfcal_addr=0x1FC000 +wifi_slot.menu.eesz.2M256.build.spiffs_start=0x1C0000 +wifi_slot.menu.eesz.2M256.build.spiffs_end=0x1FB000 +wifi_slot.menu.eesz.2M256.build.spiffs_blocksize=4096 +wifi_slot.menu.eesz.2M512=2MB (FS:512KB OTA:~768KB) +wifi_slot.menu.eesz.2M512.build.flash_size=2M +wifi_slot.menu.eesz.2M512.build.flash_size_bytes=0x200000 +wifi_slot.menu.eesz.2M512.build.flash_ld=eagle.flash.2m512.ld +wifi_slot.menu.eesz.2M512.build.spiffs_pagesize=256 +wifi_slot.menu.eesz.2M512.upload.maximum_size=1044464 +wifi_slot.menu.eesz.2M512.build.rfcal_addr=0x1FC000 +wifi_slot.menu.eesz.2M512.build.spiffs_start=0x180000 +wifi_slot.menu.eesz.2M512.build.spiffs_end=0x1FA000 +wifi_slot.menu.eesz.2M512.build.spiffs_blocksize=8192 +wifi_slot.menu.eesz.2M1M=2MB (FS:1MB OTA:~512KB) +wifi_slot.menu.eesz.2M1M.build.flash_size=2M +wifi_slot.menu.eesz.2M1M.build.flash_size_bytes=0x200000 +wifi_slot.menu.eesz.2M1M.build.flash_ld=eagle.flash.2m1m.ld +wifi_slot.menu.eesz.2M1M.build.spiffs_pagesize=256 +wifi_slot.menu.eesz.2M1M.upload.maximum_size=1044464 +wifi_slot.menu.eesz.2M1M.build.rfcal_addr=0x1FC000 +wifi_slot.menu.eesz.2M1M.build.spiffs_start=0x100000 +wifi_slot.menu.eesz.2M1M.build.spiffs_end=0x1FA000 +wifi_slot.menu.eesz.2M1M.build.spiffs_blocksize=8192 +wifi_slot.menu.eesz.2M=2MB (FS:none OTA:~1019KB) +wifi_slot.menu.eesz.2M.build.flash_size=2M +wifi_slot.menu.eesz.2M.build.flash_size_bytes=0x200000 +wifi_slot.menu.eesz.2M.build.flash_ld=eagle.flash.2m.ld +wifi_slot.menu.eesz.2M.build.spiffs_pagesize=256 +wifi_slot.menu.eesz.2M.upload.maximum_size=1044464 +wifi_slot.menu.eesz.2M.build.rfcal_addr=0x1FC000 +wifi_slot.menu.ip.lm2f=v2 Lower Memory +wifi_slot.menu.ip.lm2f.build.lwip_include=lwip2/include +wifi_slot.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +wifi_slot.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +wifi_slot.menu.ip.hb2f=v2 Higher Bandwidth +wifi_slot.menu.ip.hb2f.build.lwip_include=lwip2/include +wifi_slot.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +wifi_slot.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +wifi_slot.menu.ip.lm2n=v2 Lower Memory (no features) +wifi_slot.menu.ip.lm2n.build.lwip_include=lwip2/include +wifi_slot.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +wifi_slot.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +wifi_slot.menu.ip.hb2n=v2 Higher Bandwidth (no features) +wifi_slot.menu.ip.hb2n.build.lwip_include=lwip2/include +wifi_slot.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +wifi_slot.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +wifi_slot.menu.ip.lm6f=v2 IPv6 Lower Memory +wifi_slot.menu.ip.lm6f.build.lwip_include=lwip2/include +wifi_slot.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +wifi_slot.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +wifi_slot.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +wifi_slot.menu.ip.hb6f.build.lwip_include=lwip2/include +wifi_slot.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +wifi_slot.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +wifi_slot.menu.ip.hb1=v1.4 Higher Bandwidth +wifi_slot.menu.ip.hb1.build.lwip_lib=-llwip_gcc +wifi_slot.menu.ip.hb1.build.lwip_flags=-DLWIP_OPEN_SRC +wifi_slot.menu.ip.src=v1.4 Compile from source +wifi_slot.menu.ip.src.build.lwip_lib=-llwip_src +wifi_slot.menu.ip.src.build.lwip_flags=-DLWIP_OPEN_SRC +wifi_slot.menu.ip.src.recipe.hooks.sketch.prebuild.1.pattern=make -C "{runtime.platform.path}/tools/sdk/lwip/src" install TOOLS_PATH="{runtime.tools.xtensa-lx106-elf-gcc.path}/bin/xtensa-lx106-elf-" +wifi_slot.menu.dbg.Disabled=Disabled +wifi_slot.menu.dbg.Disabled.build.debug_port= +wifi_slot.menu.dbg.Serial=Serial +wifi_slot.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +wifi_slot.menu.dbg.Serial1=Serial1 +wifi_slot.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +wifi_slot.menu.lvl.None____=None +wifi_slot.menu.lvl.None____.build.debug_level= +wifi_slot.menu.lvl.SSL=SSL +wifi_slot.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +wifi_slot.menu.lvl.TLS_MEM=TLS_MEM +wifi_slot.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +wifi_slot.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +wifi_slot.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +wifi_slot.menu.lvl.HTTP_SERVER=HTTP_SERVER +wifi_slot.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +wifi_slot.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +wifi_slot.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +wifi_slot.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +wifi_slot.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +wifi_slot.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +wifi_slot.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +wifi_slot.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +wifi_slot.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +wifi_slot.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +wifi_slot.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +wifi_slot.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +wifi_slot.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +wifi_slot.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +wifi_slot.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +wifi_slot.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +wifi_slot.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +wifi_slot.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +wifi_slot.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +wifi_slot.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +wifi_slot.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +wifi_slot.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +wifi_slot.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +wifi_slot.menu.lvl.CORE=CORE +wifi_slot.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +wifi_slot.menu.lvl.WIFI=WIFI +wifi_slot.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +wifi_slot.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +wifi_slot.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +wifi_slot.menu.lvl.UPDATER=UPDATER +wifi_slot.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +wifi_slot.menu.lvl.OTA=OTA +wifi_slot.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +wifi_slot.menu.lvl.OOM=OOM +wifi_slot.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +wifi_slot.menu.lvl.MDNS=MDNS +wifi_slot.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +wifi_slot.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +wifi_slot.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS +wifi_slot.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +wifi_slot.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS +wifi_slot.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +wifi_slot.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +wifi_slot.menu.wipe.none=Only Sketch +wifi_slot.menu.wipe.none.upload.erase_cmd=version +wifi_slot.menu.wipe.sdk=Sketch + WiFi Settings +wifi_slot.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 +wifi_slot.menu.wipe.all=All Flash Contents +wifi_slot.menu.wipe.all.upload.erase_cmd=erase_flash +wifi_slot.menu.baud.115200=115200 +wifi_slot.menu.baud.115200.upload.speed=115200 +wifi_slot.menu.baud.57600=57600 +wifi_slot.menu.baud.57600.upload.speed=57600 +wifi_slot.menu.baud.230400.linux=230400 +wifi_slot.menu.baud.230400.macosx=230400 +wifi_slot.menu.baud.230400.upload.speed=230400 +wifi_slot.menu.baud.256000.windows=256000 +wifi_slot.menu.baud.256000.upload.speed=256000 +wifi_slot.menu.baud.460800.linux=460800 +wifi_slot.menu.baud.460800.macosx=460800 +wifi_slot.menu.baud.460800.upload.speed=460800 +wifi_slot.menu.baud.512000.windows=512000 +wifi_slot.menu.baud.512000.upload.speed=512000 +wifi_slot.menu.baud.921600=921600 +wifi_slot.menu.baud.921600.upload.speed=921600 +wifi_slot.menu.baud.3000000=3000000 +wifi_slot.menu.baud.3000000.upload.speed=3000000 + +############################################################## +wiolink.name=Seeed Wio Link +wiolink.build.board=ESP8266_WIO_LINK +wiolink.build.variant=wiolink +wiolink.upload.tool=esptool +wiolink.upload.maximum_data_size=81920 +wiolink.upload.wait_for_upload_port=true +wiolink.upload.erase_cmd=version +wiolink.serial.disableDTR=true +wiolink.serial.disableRTS=true +wiolink.build.mcu=esp8266 +wiolink.build.core=esp8266 +wiolink.build.spiffs_pagesize=256 +wiolink.build.debug_port= +wiolink.build.debug_level= +wiolink.menu.xtal.80=80 MHz +wiolink.menu.xtal.80.build.f_cpu=80000000L +wiolink.menu.xtal.160=160 MHz +wiolink.menu.xtal.160.build.f_cpu=160000000L +wiolink.menu.vt.flash=Flash +wiolink.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +wiolink.menu.vt.heap=Heap +wiolink.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +wiolink.menu.vt.iram=IRAM +wiolink.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +wiolink.menu.exception.legacy=Legacy (new can return nullptr) +wiolink.menu.exception.legacy.build.exception_flags=-fno-exceptions +wiolink.menu.exception.legacy.build.stdcpp_lib=-lstdc++ +wiolink.menu.exception.disabled=Disabled (new can abort) +wiolink.menu.exception.disabled.build.exception_flags=-fno-exceptions -DNEW_OOM_ABORT +wiolink.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +wiolink.menu.exception.enabled=Enabled +wiolink.menu.exception.enabled.build.exception_flags=-fexceptions +wiolink.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +wiolink.menu.ssl.all=All SSL ciphers (most compatible) +wiolink.menu.ssl.all.build.sslflags= +wiolink.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +wiolink.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +wiolink.upload.resetmethod=nodemcu +wiolink.build.flash_mode=qio +wiolink.build.flash_flags=-DFLASHMODE_QIO +wiolink.build.flash_freq=40 +wiolink.menu.eesz.4M2M=4MB (FS:2MB OTA:~1019KB) +wiolink.menu.eesz.4M2M.build.flash_size=4M +wiolink.menu.eesz.4M2M.build.flash_size_bytes=0x400000 +wiolink.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld +wiolink.menu.eesz.4M2M.build.spiffs_pagesize=256 +wiolink.menu.eesz.4M2M.upload.maximum_size=1044464 +wiolink.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 +wiolink.menu.eesz.4M2M.build.spiffs_start=0x200000 +wiolink.menu.eesz.4M2M.build.spiffs_end=0x3FA000 +wiolink.menu.eesz.4M2M.build.spiffs_blocksize=8192 +wiolink.menu.eesz.4M3M=4MB (FS:3MB OTA:~512KB) +wiolink.menu.eesz.4M3M.build.flash_size=4M +wiolink.menu.eesz.4M3M.build.flash_size_bytes=0x400000 +wiolink.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld +wiolink.menu.eesz.4M3M.build.spiffs_pagesize=256 +wiolink.menu.eesz.4M3M.upload.maximum_size=1044464 +wiolink.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 +wiolink.menu.eesz.4M3M.build.spiffs_start=0x100000 +wiolink.menu.eesz.4M3M.build.spiffs_end=0x3FA000 +wiolink.menu.eesz.4M3M.build.spiffs_blocksize=8192 +wiolink.menu.eesz.4M1M=4MB (FS:1MB OTA:~1019KB) +wiolink.menu.eesz.4M1M.build.flash_size=4M +wiolink.menu.eesz.4M1M.build.flash_size_bytes=0x400000 +wiolink.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld +wiolink.menu.eesz.4M1M.build.spiffs_pagesize=256 +wiolink.menu.eesz.4M1M.upload.maximum_size=1044464 +wiolink.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 +wiolink.menu.eesz.4M1M.build.spiffs_start=0x300000 +wiolink.menu.eesz.4M1M.build.spiffs_end=0x3FA000 +wiolink.menu.eesz.4M1M.build.spiffs_blocksize=8192 +wiolink.menu.eesz.4M=4MB (FS:none OTA:~1019KB) +wiolink.menu.eesz.4M.build.flash_size=4M +wiolink.menu.eesz.4M.build.flash_size_bytes=0x400000 +wiolink.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld +wiolink.menu.eesz.4M.build.spiffs_pagesize=256 +wiolink.menu.eesz.4M.upload.maximum_size=1044464 +wiolink.menu.eesz.4M.build.rfcal_addr=0x3FC000 +wiolink.menu.ip.lm2f=v2 Lower Memory +wiolink.menu.ip.lm2f.build.lwip_include=lwip2/include +wiolink.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +wiolink.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +wiolink.menu.ip.hb2f=v2 Higher Bandwidth +wiolink.menu.ip.hb2f.build.lwip_include=lwip2/include +wiolink.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +wiolink.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +wiolink.menu.ip.lm2n=v2 Lower Memory (no features) +wiolink.menu.ip.lm2n.build.lwip_include=lwip2/include +wiolink.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +wiolink.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +wiolink.menu.ip.hb2n=v2 Higher Bandwidth (no features) +wiolink.menu.ip.hb2n.build.lwip_include=lwip2/include +wiolink.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +wiolink.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +wiolink.menu.ip.lm6f=v2 IPv6 Lower Memory +wiolink.menu.ip.lm6f.build.lwip_include=lwip2/include +wiolink.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +wiolink.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +wiolink.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +wiolink.menu.ip.hb6f.build.lwip_include=lwip2/include +wiolink.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +wiolink.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +wiolink.menu.ip.hb1=v1.4 Higher Bandwidth +wiolink.menu.ip.hb1.build.lwip_lib=-llwip_gcc +wiolink.menu.ip.hb1.build.lwip_flags=-DLWIP_OPEN_SRC +wiolink.menu.ip.src=v1.4 Compile from source +wiolink.menu.ip.src.build.lwip_lib=-llwip_src +wiolink.menu.ip.src.build.lwip_flags=-DLWIP_OPEN_SRC +wiolink.menu.ip.src.recipe.hooks.sketch.prebuild.1.pattern=make -C "{runtime.platform.path}/tools/sdk/lwip/src" install TOOLS_PATH="{runtime.tools.xtensa-lx106-elf-gcc.path}/bin/xtensa-lx106-elf-" +wiolink.menu.dbg.Disabled=Disabled +wiolink.menu.dbg.Disabled.build.debug_port= +wiolink.menu.dbg.Serial=Serial +wiolink.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +wiolink.menu.dbg.Serial1=Serial1 +wiolink.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +wiolink.menu.lvl.None____=None +wiolink.menu.lvl.None____.build.debug_level= +wiolink.menu.lvl.SSL=SSL +wiolink.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +wiolink.menu.lvl.TLS_MEM=TLS_MEM +wiolink.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +wiolink.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +wiolink.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +wiolink.menu.lvl.HTTP_SERVER=HTTP_SERVER +wiolink.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +wiolink.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +wiolink.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +wiolink.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +wiolink.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +wiolink.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +wiolink.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +wiolink.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +wiolink.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +wiolink.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +wiolink.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +wiolink.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +wiolink.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +wiolink.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +wiolink.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +wiolink.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +wiolink.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +wiolink.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +wiolink.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +wiolink.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +wiolink.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +wiolink.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +wiolink.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +wiolink.menu.lvl.CORE=CORE +wiolink.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +wiolink.menu.lvl.WIFI=WIFI +wiolink.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +wiolink.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +wiolink.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +wiolink.menu.lvl.UPDATER=UPDATER +wiolink.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +wiolink.menu.lvl.OTA=OTA +wiolink.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +wiolink.menu.lvl.OOM=OOM +wiolink.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +wiolink.menu.lvl.MDNS=MDNS +wiolink.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +wiolink.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +wiolink.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS +wiolink.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +wiolink.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS +wiolink.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +wiolink.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +wiolink.menu.wipe.none=Only Sketch +wiolink.menu.wipe.none.upload.erase_cmd=version +wiolink.menu.wipe.sdk=Sketch + WiFi Settings +wiolink.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 +wiolink.menu.wipe.all=All Flash Contents +wiolink.menu.wipe.all.upload.erase_cmd=erase_flash +wiolink.menu.baud.115200=115200 +wiolink.menu.baud.115200.upload.speed=115200 +wiolink.menu.baud.57600=57600 +wiolink.menu.baud.57600.upload.speed=57600 +wiolink.menu.baud.230400.linux=230400 +wiolink.menu.baud.230400.macosx=230400 +wiolink.menu.baud.230400.upload.speed=230400 +wiolink.menu.baud.256000.windows=256000 +wiolink.menu.baud.256000.upload.speed=256000 +wiolink.menu.baud.460800.linux=460800 +wiolink.menu.baud.460800.macosx=460800 +wiolink.menu.baud.460800.upload.speed=460800 +wiolink.menu.baud.512000.windows=512000 +wiolink.menu.baud.512000.upload.speed=512000 +wiolink.menu.baud.921600=921600 +wiolink.menu.baud.921600.upload.speed=921600 +wiolink.menu.baud.3000000=3000000 +wiolink.menu.baud.3000000.upload.speed=3000000 + +############################################################## +espectro.name=ESPectro Core +espectro.build.board=ESP8266_ESPECTRO_CORE +espectro.build.variant=espectro +espectro.upload.tool=esptool +espectro.upload.maximum_data_size=81920 +espectro.upload.wait_for_upload_port=true +espectro.upload.erase_cmd=version +espectro.serial.disableDTR=true +espectro.serial.disableRTS=true +espectro.build.mcu=esp8266 +espectro.build.core=esp8266 +espectro.build.spiffs_pagesize=256 +espectro.build.debug_port= +espectro.build.debug_level= +espectro.menu.xtal.80=80 MHz +espectro.menu.xtal.80.build.f_cpu=80000000L +espectro.menu.xtal.160=160 MHz +espectro.menu.xtal.160.build.f_cpu=160000000L +espectro.menu.vt.flash=Flash +espectro.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +espectro.menu.vt.heap=Heap +espectro.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +espectro.menu.vt.iram=IRAM +espectro.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +espectro.menu.exception.legacy=Legacy (new can return nullptr) +espectro.menu.exception.legacy.build.exception_flags=-fno-exceptions +espectro.menu.exception.legacy.build.stdcpp_lib=-lstdc++ +espectro.menu.exception.disabled=Disabled (new can abort) +espectro.menu.exception.disabled.build.exception_flags=-fno-exceptions -DNEW_OOM_ABORT +espectro.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +espectro.menu.exception.enabled=Enabled +espectro.menu.exception.enabled.build.exception_flags=-fexceptions +espectro.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +espectro.menu.ssl.all=All SSL ciphers (most compatible) +espectro.menu.ssl.all.build.sslflags= +espectro.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +espectro.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +espectro.upload.resetmethod=nodemcu +espectro.build.flash_mode=dio +espectro.build.flash_flags=-DFLASHMODE_DIO +espectro.build.flash_freq=40 +espectro.menu.eesz.4M2M=4MB (FS:2MB OTA:~1019KB) +espectro.menu.eesz.4M2M.build.flash_size=4M +espectro.menu.eesz.4M2M.build.flash_size_bytes=0x400000 +espectro.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld +espectro.menu.eesz.4M2M.build.spiffs_pagesize=256 +espectro.menu.eesz.4M2M.upload.maximum_size=1044464 +espectro.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 +espectro.menu.eesz.4M2M.build.spiffs_start=0x200000 +espectro.menu.eesz.4M2M.build.spiffs_end=0x3FA000 +espectro.menu.eesz.4M2M.build.spiffs_blocksize=8192 +espectro.menu.eesz.4M3M=4MB (FS:3MB OTA:~512KB) +espectro.menu.eesz.4M3M.build.flash_size=4M +espectro.menu.eesz.4M3M.build.flash_size_bytes=0x400000 +espectro.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld +espectro.menu.eesz.4M3M.build.spiffs_pagesize=256 +espectro.menu.eesz.4M3M.upload.maximum_size=1044464 +espectro.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 +espectro.menu.eesz.4M3M.build.spiffs_start=0x100000 +espectro.menu.eesz.4M3M.build.spiffs_end=0x3FA000 +espectro.menu.eesz.4M3M.build.spiffs_blocksize=8192 +espectro.menu.eesz.4M1M=4MB (FS:1MB OTA:~1019KB) +espectro.menu.eesz.4M1M.build.flash_size=4M +espectro.menu.eesz.4M1M.build.flash_size_bytes=0x400000 +espectro.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld +espectro.menu.eesz.4M1M.build.spiffs_pagesize=256 +espectro.menu.eesz.4M1M.upload.maximum_size=1044464 +espectro.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 +espectro.menu.eesz.4M1M.build.spiffs_start=0x300000 +espectro.menu.eesz.4M1M.build.spiffs_end=0x3FA000 +espectro.menu.eesz.4M1M.build.spiffs_blocksize=8192 +espectro.menu.eesz.4M=4MB (FS:none OTA:~1019KB) +espectro.menu.eesz.4M.build.flash_size=4M +espectro.menu.eesz.4M.build.flash_size_bytes=0x400000 +espectro.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld +espectro.menu.eesz.4M.build.spiffs_pagesize=256 +espectro.menu.eesz.4M.upload.maximum_size=1044464 +espectro.menu.eesz.4M.build.rfcal_addr=0x3FC000 +espectro.menu.ip.lm2f=v2 Lower Memory +espectro.menu.ip.lm2f.build.lwip_include=lwip2/include +espectro.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +espectro.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +espectro.menu.ip.hb2f=v2 Higher Bandwidth +espectro.menu.ip.hb2f.build.lwip_include=lwip2/include +espectro.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +espectro.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +espectro.menu.ip.lm2n=v2 Lower Memory (no features) +espectro.menu.ip.lm2n.build.lwip_include=lwip2/include +espectro.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +espectro.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +espectro.menu.ip.hb2n=v2 Higher Bandwidth (no features) +espectro.menu.ip.hb2n.build.lwip_include=lwip2/include +espectro.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +espectro.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +espectro.menu.ip.lm6f=v2 IPv6 Lower Memory +espectro.menu.ip.lm6f.build.lwip_include=lwip2/include +espectro.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +espectro.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +espectro.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +espectro.menu.ip.hb6f.build.lwip_include=lwip2/include +espectro.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +espectro.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +espectro.menu.ip.hb1=v1.4 Higher Bandwidth +espectro.menu.ip.hb1.build.lwip_lib=-llwip_gcc +espectro.menu.ip.hb1.build.lwip_flags=-DLWIP_OPEN_SRC +espectro.menu.ip.src=v1.4 Compile from source +espectro.menu.ip.src.build.lwip_lib=-llwip_src +espectro.menu.ip.src.build.lwip_flags=-DLWIP_OPEN_SRC +espectro.menu.ip.src.recipe.hooks.sketch.prebuild.1.pattern=make -C "{runtime.platform.path}/tools/sdk/lwip/src" install TOOLS_PATH="{runtime.tools.xtensa-lx106-elf-gcc.path}/bin/xtensa-lx106-elf-" +espectro.menu.dbg.Disabled=Disabled +espectro.menu.dbg.Disabled.build.debug_port= +espectro.menu.dbg.Serial=Serial +espectro.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +espectro.menu.dbg.Serial1=Serial1 +espectro.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +espectro.menu.lvl.None____=None +espectro.menu.lvl.None____.build.debug_level= +espectro.menu.lvl.SSL=SSL +espectro.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +espectro.menu.lvl.TLS_MEM=TLS_MEM +espectro.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +espectro.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +espectro.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +espectro.menu.lvl.HTTP_SERVER=HTTP_SERVER +espectro.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +espectro.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +espectro.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +espectro.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +espectro.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +espectro.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +espectro.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +espectro.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +espectro.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +espectro.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +espectro.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +espectro.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +espectro.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +espectro.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +espectro.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +espectro.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +espectro.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +espectro.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +espectro.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +espectro.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +espectro.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +espectro.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +espectro.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +espectro.menu.lvl.CORE=CORE +espectro.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +espectro.menu.lvl.WIFI=WIFI +espectro.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +espectro.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +espectro.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +espectro.menu.lvl.UPDATER=UPDATER +espectro.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +espectro.menu.lvl.OTA=OTA +espectro.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +espectro.menu.lvl.OOM=OOM +espectro.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +espectro.menu.lvl.MDNS=MDNS +espectro.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +espectro.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +espectro.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS +espectro.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +espectro.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS +espectro.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +espectro.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +espectro.menu.wipe.none=Only Sketch +espectro.menu.wipe.none.upload.erase_cmd=version +espectro.menu.wipe.sdk=Sketch + WiFi Settings +espectro.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 +espectro.menu.wipe.all=All Flash Contents +espectro.menu.wipe.all.upload.erase_cmd=erase_flash +espectro.menu.baud.115200=115200 +espectro.menu.baud.115200.upload.speed=115200 +espectro.menu.baud.57600=57600 +espectro.menu.baud.57600.upload.speed=57600 +espectro.menu.baud.230400.linux=230400 +espectro.menu.baud.230400.macosx=230400 +espectro.menu.baud.230400.upload.speed=230400 +espectro.menu.baud.256000.windows=256000 +espectro.menu.baud.256000.upload.speed=256000 +espectro.menu.baud.460800.linux=460800 +espectro.menu.baud.460800.macosx=460800 +espectro.menu.baud.460800.upload.speed=460800 +espectro.menu.baud.512000.windows=512000 +espectro.menu.baud.512000.upload.speed=512000 +espectro.menu.baud.921600=921600 +espectro.menu.baud.921600.upload.speed=921600 +espectro.menu.baud.3000000=3000000 +espectro.menu.baud.3000000.upload.speed=3000000 + diff --git a/arduino/version pre-2.6.0/platform.txt b/arduino/version pre-2.6.0/platform.txt new file mode 100644 index 000000000..aed8672f9 --- /dev/null +++ b/arduino/version pre-2.6.0/platform.txt @@ -0,0 +1,166 @@ + +# ESP8266 platform +# ------------------------------ + +# For more info: +# https://github.com/arduino/Arduino/wiki/Arduino-IDE-1.5-3rd-party-Hardware-specification + +name=ESP8266 Boards (2.5.2-196-g45d71ae4) +version=2.5.2-196-g45d71ae4 + +# These will be removed by the packager script when doing a JSON release + + + + +runtime.tools.signing={runtime.platform.path}/tools/signing.py +runtime.tools.elf2bin={runtime.platform.path}/tools/elf2bin.py +runtime.tools.sizes={runtime.platform.path}/tools/sizes.py +runtime.tools.makecorever={runtime.platform.path}/tools/makecorever.py +runtime.tools.eboot={runtime.platform.path}/bootloaders/eboot/eboot.elf + +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_include=lwip/include +build.lwip_flags=-DLWIP_OPEN_SRC + +build.vtable_flags=-DVTABLES_IN_FLASH + +build.sslflags= + +build.exception_flags=-fno-exceptions +build.stdcpp_lib=-lstdc++ +build.stdcpp_level=-std=gnu++11 + +# build.float=-u _printf_float -u _scanf_float +build.float= +build.led= +build.sdk=NONOSDK22y + +compiler.path={runtime.tools.xtensa-lx106-elf-gcc.path}/bin/ +compiler.sdk.path={runtime.platform.path}/tools/sdk + +compiler.libc.path={runtime.platform.path}/tools/sdk/libc/xtensa-lx106-elf +compiler.cpreprocessor.flags=-D__ets__ -DICACHE_FLASH -U__STRICT_ANSI__ "-I{compiler.sdk.path}/include" "-I{compiler.sdk.path}/{build.lwip_include}" "-I{compiler.libc.path}/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 {build.exception_flags} {build.sslflags} + +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 app_entry {build.float} -Wl,-static "-L{compiler.sdk.path}/lib" "-L{compiler.sdk.path}/lib/{build.sdk}" "-L{compiler.sdk.path}/ld" "-L{compiler.libc.path}/lib" "-T{build.flash_ld}" -Wl,--gc-sections -Wl,-wrap,system_restart_local -Wl,-wrap,spi_flash_read + +compiler.c.elf.cmd=xtensa-lx106-elf-gcc +compiler.c.elf.libs=-lhal -lphy -lpp -lnet80211 {build.lwip_lib} -lwpa -lcrypto -lmain -lwps -lbearssl -laxtls -lespnow -lsmartconfig -lairkiss -lwpa2 {build.stdcpp_lib} -lm -lc -lgcc + +compiler.cpp.cmd=xtensa-lx106-elf-g++ +compiler.cpp.flags=-c {compiler.warning_flags} -Os -g -mlongcalls -mtext-section-literals -fno-rtti -falign-functions=4 {build.stdcpp_level} -MMD -ffunction-sections -fdata-sections {build.exception_flags} {build.sslflags} + +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 + +# 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 git +recipe.hooks.sketch.prebuild.pattern="{runtime.tools.python3.path}/python3" "{runtime.tools.signing}" --mode header --publickey "{build.source.path}/public.key" --out "{build.path}/core/Updater_Signing.h" +# This is quite a working hack. This form of prebuild hook, while intuitive, is not explicitly documented. +recipe.hooks.prebuild.10.pattern="{runtime.tools.python3.path}/python3" "{runtime.tools.makecorever}" --build_path "{build.path}" --platform_path "{runtime.platform.path}" --version "unix-{version}" + +## Build the app.ld linker file +recipe.hooks.linking.prelink.1.pattern="{compiler.path}{compiler.c.cmd}" -CC -E -P {build.vtable_flags} "{runtime.platform.path}/tools/sdk/ld/eagle.app.v6.common.ld.h" -o "{build.path}/local.eagle.app.v6.common.ld" + +## Compile c files +recipe.c.o.pattern="{compiler.path}{compiler.c.cmd}" {compiler.cpreprocessor.flags} {compiler.c.flags} -D{build.sdk}=1 -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}" {build.led} {build.flash_flags} {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} -D{build.sdk}=1 -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}" {build.led} {build.flash_flags} {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} -D{build.sdk}=1 -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}" {build.led} {build.flash_flags} {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} "{archive_file_path}" "{object_file}" + +## Combine gc-sections, archives, and objects +recipe.c.combine.pattern="{compiler.path}{compiler.c.elf.cmd}" {build.exception_flags} -Wl,-Map "-Wl,{build.path}/{build.project_name}.map" {compiler.c.elf.flags} {compiler.c.elf.extra_flags} -o "{build.path}/{build.project_name}.elf" -Wl,--start-group {object_files} "{archive_file_path}" {compiler.c.elf.libs} -Wl,--end-group "-L{build.path}" + +## Create eeprom +recipe.objcopy.eep.pattern= + +## Create hex +recipe.objcopy.hex.1.pattern="{runtime.tools.python3.path}/python3" "{runtime.tools.elf2bin}" --eboot "{runtime.tools.eboot}" --app "{build.path}/{build.project_name}.elf" --flash_mode {build.flash_mode} --flash_freq {build.flash_freq} --flash_size {build.flash_size} --path "{runtime.tools.xtensa-lx106-elf-gcc.path}/bin" --out "{build.path}/{build.project_name}.bin" +recipe.objcopy.hex.2.pattern="{runtime.tools.python3.path}/python3" "{runtime.tools.signing}" --mode sign --privatekey "{build.source.path}/private.key" --bin "{build.path}/{build.project_name}.bin" --out "{build.path}/{build.project_name}.bin.signed" --legacy "{build.path}/{build.project_name}.bin.legacy_sig" +recipe.objcopy.hex.3.pattern="{runtime.tools.python3.path}/python3" "{runtime.tools.sizes}" --elf "{build.path}/{build.project_name}.elf" --path "{runtime.tools.xtensa-lx106-elf-gcc.path}/bin" + +## 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|\.text1|\.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.path= +# Because the variable expansion doesn't allow one tool to find another, the following lines +# will point to "{runtime.platform.path}/tools/python3/python3" in GIT and +# "{runtime.tools.python3.path}/python3" for JSON board manager releases. +tools.esptool.cmd={runtime.tools.python3.path}/python3 +tools.esptool.network_cmd={runtime.tools.python3.path}/python3 + + + +tools.esptool.upload.protocol=esp +# esptool.py --trace option is a debug option, not a verbose option +tools.esptool.upload.params.verbose= +tools.esptool.upload.params.quiet= + +# First, potentially perform an erase or nothing +# Next, do the binary upload +# Combined in one rule because Arduino doesn't suport upload.1.pattern/upload.3.pattern +tools.esptool.upload.pattern="{cmd}" "{runtime.platform.path}/tools/upload.py" --chip esp8266 --port "{serial.port}" --baud "{upload.speed}" "{upload.verbose}" {upload.erase_cmd} --end --chip esp8266 --port "{serial.port}" --baud "{upload.speed}" "{upload.verbose}" write_flash 0x0 "{build.path}/{build.project_name}.bin" --end + +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.mklittlefs.cmd=mklittlefs +tools.mklittlefs.cmd.windows=mklittlefs.exe +tools.mklittlefs.path={runtime.platform.path}/tools/mklittlefs + +tools.espupload.cmd=python +tools.espupload.cmd.windows=python.exe +tools.espupload.path={runtime.platform.path}/tools +tools.espupload.upload.protocol=espupload +tools.espupload.upload.params.verbose= +tools.espupload.upload.params.quiet= +tools.espupload.upload.pattern="{cmd}" "{path}/espupload.py" -f "{build.path}/{build.project_name}.bin" diff --git a/build-container/Dockerfile b/build-container/Dockerfile new file mode 100644 index 000000000..a5852d41c --- /dev/null +++ b/build-container/Dockerfile @@ -0,0 +1,24 @@ +FROM python:2 + +LABEL author="Eduard Angold" + +# Install platformio. To be able to build tasmota <=v6.6.0 (and later) +# we have to use version 3.6.7 of platformio. +RUN pip install --upgrade pip &&\ + pip install -U platformio==3.6.7 + +# Init project +COPY init_pio_tasmota /init_pio_tasmota + +# Install project dependencies using a init project. +RUN cd /init_pio_tasmota &&\ + pio run &&\ + cd ../ &&\ + rm -fr init_pio_tasmota &&\ + cp -r /root/.platformio / &&\ + chmod -R 777 /.platformio + +COPY entrypoint.sh /entrypoint.sh + +ENTRYPOINT ["/bin/bash", "/entrypoint.sh"] + diff --git a/build-container/README.md b/build-container/README.md new file mode 100644 index 000000000..a02f1860e --- /dev/null +++ b/build-container/README.md @@ -0,0 +1,26 @@ +# Docker container for tasmota builds +This Container will setup a proper build environment for [Sonoff-Tasmota](https://github.com/arendst/Sonoff-Tasmota) + +## Create container +`docker build -t mytasmota:latest .` + +## Use a ready container from docker hub +Use instead of the container `mytasmota:latest` the published container `eddyhub/docker-tasmota:latest` from docker hub. + +## Build all development binaries +`git clone https://github.com/arendst/Sonoff-Tasmota.git` +`docker run -ti --rm -v $(pwd)/Sonoff-Tasmota:/tasmota -u $UID:$GID mytasmota:latest` + +## Build a specific binary with custom options +Checkout Sonoff-Tasmota: `git clone https://github.com/arendst/Sonoff-Tasmota.git` +Mount the source as volume in `/tasmota`. **Prefix** any parameter available in `Sonoff-Tasmota/sonoff/my_user_config.h` with `TASMOTA_` as a environment variable for the container. **Also don't forget to escape what needs to be escaped in your shell.** **Strings** should be in **double quotes**. My config example: +`docker run -ti --rm -v $(pwd)/Sonoff-Tasmota:/tasmota -e TASMOTA_STA_SSID1='"my-wifi"' -e TASMOTA_STA_PASS1='"my-wifi-password"' -e TASMOTA_MQTT_HOST='my-mqtt-host' -e TASMOTA_MQTT_USER='"my-mqtt-user"' -e TASMOTA_MQTT_PASS='"my-mqtt-password"' -e TASMOTA_WEB_PASSWORD='"my-web-password"' -u $UID:$GID mytasmota:latest --environment sonoff-DE + +Now you should have the file Sonoff-Tasmota/.pioenvs/sonoff-DE/firmware.bin which can be flashed on your device. + +## Build a specific version of tasmota +Checkout out the needed version before using the build instructions above: +- `git clone https://github.com/arendst/Sonoff-Tasmota.git` +- `git -C Sonoff-Tasmota checkout v6.6.0` +Build it: +- `docker run -ti --rm -v $(pwd)/Sonoff-Tasmota:/tasmota -u $UID:$GID mytasmota:latest` diff --git a/build-container/entrypoint.sh b/build-container/entrypoint.sh new file mode 100644 index 000000000..6bdf2dea6 --- /dev/null +++ b/build-container/entrypoint.sh @@ -0,0 +1,35 @@ +# configure build via environment +#!/bin/bash + +TASMOTA_VOLUME='/tasmota' +USER_CONFIG_OVERRIDE="${TASMOTA_VOLUME}/sonoff/user_config_override.h" + +if [ -d $TASMOTA_VOLUME ]; then + cd $TASMOTA_VOLUME + if [ -n "$(env | grep ^TASMOTA_)" ]; then + echo "Removing $USER_CONFIG_OVERRIDE and creating a new one." + rm "$USER_CONFIG_OVERRIDE" + #export PLATFORMIO_BUILD_FLAGS='-DUSE_CONFIG_OVERRIDE' + sed -i 's/^; *-DUSE_CONFIG_OVERRIDE/ -DUSE_CONFIG_OVERRIDE/' platformio.ini + echo '#ifndef _USER_CONFIG_OVERRIDE_H_' >> $USER_CONFIG_OVERRIDE + echo '#define _USER_CONFIG_OVERRIDE_H_' >> $USER_CONFIG_OVERRIDE + echo '#warning **** user_config_override.h: Using Settings from this File ****' >> $USER_CONFIG_OVERRIDE + echo '#undef CFG_HOLDER' >> $USER_CONFIG_OVERRIDE + echo '#define CFG_HOLDER 1' >> $USER_CONFIG_OVERRIDE + for i in $(env | grep ^TASMOTA_); do + config=${i#TASMOTA_} + key=$(echo $config | cut -d '=' -f 1) + value=$(echo $config | cut -d '=' -f 2) + echo "#undef ${key}" >> $USER_CONFIG_OVERRIDE + echo "#define ${key} ${value}" >> $USER_CONFIG_OVERRIDE + done + echo '#endif' >> $USER_CONFIG_OVERRIDE + fi + echo "Compiling..." + #pio run -t clean + pio run $@ + echo "Everything done you find your builds in .pioenvs//firmware.bin" +else + echo ">>> NO TASMOTA VOLUME MOUNTED --> EXITING" + exit 0; +fi diff --git a/build-container/init_pio_tasmota/platformio.ini b/build-container/init_pio_tasmota/platformio.ini new file mode 100644 index 000000000..058e9064f --- /dev/null +++ b/build-container/init_pio_tasmota/platformio.ini @@ -0,0 +1,30 @@ +[env:core_2_3_0] +; *** Esp8266 core for Arduino version 2.3.0 +platform = espressif8266@1.5.0 +framework = arduino +board = esp01_1m + +[env:core_2_4_2] +; *** Esp8266 core for Arduino version 2.4.2 +platform = espressif8266@1.8.0 +framework = arduino +board = esp01_1m + +[env:core_2_5_2] +; *** Esp8266 core for Arduino version 2.5.2 +platform = espressif8266@~2.2.2 +framework = arduino +board = esp01_1m + +[env:core_stage] +; *** Esp8266 core for Arduino version latest beta +platform = https://github.com/platformio/platform-espressif8266.git#feature/stage +framework = arduino +board = esp01_1m + +[env:core_pre] +; *** Arduino Esp8266 core pre 2.6.x for Tasmota (mqtt reconnects fixed) +platform = https://github.com/Jason2866/platform-espressif8266.git#Tasmota +framework = arduino +board = esp01_1m + diff --git a/build-container/init_pio_tasmota/src/main.cpp b/build-container/init_pio_tasmota/src/main.cpp new file mode 100644 index 000000000..27f3768b7 --- /dev/null +++ b/build-container/init_pio_tasmota/src/main.cpp @@ -0,0 +1,3 @@ +#include +void setup() {} +void loop() {} diff --git a/lib/A4988_Stepper/README.adoc b/lib/A4988_Stepper/README.adoc new file mode 100644 index 000000000..0cac353f3 --- /dev/null +++ b/lib/A4988_Stepper/README.adoc @@ -0,0 +1,19 @@ +Stepper Library for Tasmota + +This Class allows you to control bipolar stepper motors. To use it you will need an A4988-StepperDriverCircuit, connected at least with 2 GPIO's (direction and step) and of cause a stepper motor. + +== License == + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA diff --git a/lib/A4988_Stepper/keywords.txt b/lib/A4988_Stepper/keywords.txt new file mode 100644 index 000000000..c83465c8b --- /dev/null +++ b/lib/A4988_Stepper/keywords.txt @@ -0,0 +1,24 @@ +####################################### +# Syntax Coloring Map For Test +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +A4988_Stepper KEYWORD1 A4988_Stepper + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +doMove KEYWORD2 +doRotate KEYWORD2 +setRPM KEYWORD2 +setSPR KEYWORD2 +setMIS KEYWORD2 +version KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### diff --git a/lib/A4988_Stepper/library.properties b/lib/A4988_Stepper/library.properties new file mode 100644 index 000000000..2e6b38bf9 --- /dev/null +++ b/lib/A4988_Stepper/library.properties @@ -0,0 +1,9 @@ +name=A4988_Stepper +version=0.0.1 +author=Tim Leuschner +maintainer=Tim Leuschner +sentence=Allows Tasmota to control stepper motors, connected to A4988-StepperDriverCircuit. +paragraph=This library allows you to control bipolar stepper motors, controlled by A4988-stepperDriverCircuit. +category=Device Control +url= +architectures=* diff --git a/lib/A4988_Stepper/src/A4988_Stepper.cpp b/lib/A4988_Stepper/src/A4988_Stepper.cpp new file mode 100644 index 000000000..cbd72390a --- /dev/null +++ b/lib/A4988_Stepper/src/A4988_Stepper.cpp @@ -0,0 +1,155 @@ +/* + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + Drives a bipolar motor, controlled by A4988 stepper driver circuit + */ +// +#include "Arduino.h" +#include "A4988_Stepper.h" +A4988_Stepper::A4988_Stepper( int m_spr + , int m_rpm + , short m_mis + , short m_dir_pin + , short m_stp_pin + , short m_ena_pin + , short m_ms1_pin + , short m_ms2_pin + , short m_ms3_pin ) { + last_time = 0; // time stamp in us of the last step taken + motor_SPR = m_spr; // StepsPerRevolution + motor_RPM = m_rpm; // RoundsPerMinute + motor_MIS = m_mis; // Microsteps w/o effect if MS1-MS3 not connected - then full steps anyway + motor_dir_pin = m_dir_pin; + motor_stp_pin = m_stp_pin; + motor_ena_pin = m_ena_pin; + motor_ms1_pin = m_ms1_pin; + motor_ms2_pin = m_ms2_pin; + motor_ms3_pin = m_ms3_pin; + + adjustDelay(); + adjustPins(); + adjustMicrosteps(); +} + +void A4988_Stepper::adjustPins(void) { + // setup the pins on the microcontroller: + pinMode(motor_dir_pin, OUTPUT); + pinMode(motor_stp_pin, OUTPUT); + if (motor_ena_pin <99) { + pinMode(motor_ena_pin, OUTPUT); + digitalWrite(motor_ena_pin, HIGH); + } + + if ((motor_ms1_pin<99)&&(motor_ms2_pin<99)&&(motor_ms3_pin<99)) { + pinMode(motor_ms1_pin, OUTPUT); + pinMode(motor_ms2_pin, OUTPUT); + pinMode(motor_ms3_pin, OUTPUT); + } +} + +void A4988_Stepper::adjustMicrosteps() { + if ((motor_ms1_pin<99)&&(motor_ms2_pin<99)&&(motor_ms3_pin<99)) { + unsigned short i = 0; + while (i < 5){ + if (motor_MIS & (1<0?LOW:HIGH); + enable(); + while (steps_togo > 0) { + delay(0); // don't get watchdoged in loop + unsigned long now = micros(); + // move if delay has passed: + if (now - last_time >= motor_delay) { + digitalWrite(motor_stp_pin, lastStepWasHigh?LOW:HIGH); + lastStepWasHigh = !lastStepWasHigh; + // remeber step-time + last_time = now; + if (!lastStepWasHigh) steps_togo--; // same here - only HIGH moves, if pulled LOW step is completed... + } + } + disable(); +} + +void A4988_Stepper::doRotate(long howManyDegrees) +{ long lSteps = 0; + lSteps = motor_SPR*motor_MIS*howManyDegrees/360; + doMove(lSteps); +} + +void A4988_Stepper::doTurn(float howManyTimes) +{ long lSteps = 0; + lSteps = howManyTimes*motor_SPR; + doMove(lSteps); +} + +int A4988_Stepper::version(void) +{ + return 1; +} diff --git a/lib/A4988_Stepper/src/A4988_Stepper.h b/lib/A4988_Stepper/src/A4988_Stepper.h new file mode 100644 index 000000000..a907adfb1 --- /dev/null +++ b/lib/A4988_Stepper/src/A4988_Stepper.h @@ -0,0 +1,73 @@ +/* + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + + +#ifndef A4988_Stepper_h +#define A4988_Stepper_h + +class A4988_Stepper { + public: + // constructor: + A4988_Stepper( int motor_spr + , int motor_rpm + , short motor_mis + , short motor_dir_pin + , short motor_stp_pin + , short motor_ena_pin + , short motor_ms1_pin + , short motor_ms2_pin + , short motor_ms3_pin + ); + + void setRPM (int whatRPM ); + int getRPM (void ); + + void setMIS (short OneToSixteen); + short getMIS (void ); + + void setSPR (int howMany ); + int getSPR (void ); + + void doMove (long steps_to_move); + void doRotate(long degrs_to_turn); + void doTurn (float howManyTimes); + + void enable (void ); + void disable (void ); + + int version (void ); + const unsigned short MIS_TABLE[5] = {0b000,0b001,0b010,0b011,0b111}; + + private: + void adjustDelay(void); + void adjustPins(void); + void adjustMicrosteps(void); + unsigned long motor_delay; // delay between steps, in ms + int motor_SPR; // Steps Per Revolution + int motor_RPM; // Rounds Per Minute + short motor_MIS; // Micro Steps + + // motor pins: + short motor_dir_pin; + short motor_stp_pin; + short motor_ena_pin; + short motor_ms1_pin; + short motor_ms2_pin; + short motor_ms3_pin; + + unsigned long last_time; // timestamp of last pincycle of last step +}; + +#endif diff --git a/lib/AT24C256/Eeprom24C128_256.cpp b/lib/AT24C256/Eeprom24C128_256.cpp new file mode 100644 index 000000000..f7b66afba --- /dev/null +++ b/lib/AT24C256/Eeprom24C128_256.cpp @@ -0,0 +1,334 @@ +/**************************************************************************//** + * \brief EEPROM 24C128 / 24C256 library for Arduino + * \author Copyright (C) 2012 Julien Le Sech - www.idreammicro.com + * \version 1.0 + * \date 20120203 + * + * This file is part of the EEPROM 24C128 / 24C256 library for Arduino. + * + * This library is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) any + * later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see http://www.gnu.org/licenses/ + ******************************************************************************/ + +/**************************************************************************//** + * \file Eeprom24C128_256.cpp + ******************************************************************************/ + +/****************************************************************************** + * Header file inclusions. + ******************************************************************************/ + +#include +#include + +#include + +/****************************************************************************** + * Private macro definitions. + ******************************************************************************/ + +/**************************************************************************//** + * \def EEPROM__PAGE_SIZE + * \brief Size of a page in EEPROM memory. + * This size is given by EEPROM memory datasheet. + ******************************************************************************/ +#define EEPROM__PAGE_SIZE 64 + +/**************************************************************************//** + * \def EEPROM__RD_BUFFER_SIZE + * \brief Size of input TWI buffer. + * This size is equal to BUFFER_LENGTH defined in Wire library (32 bytes). + ******************************************************************************/ + #define xBUFFER_LENGTH 24 +#define EEPROM__RD_BUFFER_SIZE xBUFFER_LENGTH + +/**************************************************************************//** + * \def EEPROM__WR_BUFFER_SIZE + * \brief Size of output TWI buffer. + * This size is equal to BUFFER_LENGTH - 2 bytes reserved for address. + ******************************************************************************/ +#define EEPROM__WR_BUFFER_SIZE (xBUFFER_LENGTH - 2) + +/****************************************************************************** + * Public method definitions. + ******************************************************************************/ + +/**************************************************************************//** + * \fn Eeprom24C128_256::Eeprom24C128_256(byte deviceAddress) + * + * \brief Constructor. + * + * \param deviceAddress EEPROM address on TWI bus. + ******************************************************************************/ +Eeprom24C128_256::Eeprom24C128_256 +( + byte deviceAddress +){ + m_deviceAddress = deviceAddress; +} + +/**************************************************************************//** + * \fn void Eeprom24C128_256::initialize() + * + * \brief Initialize library and TWI bus. + * + * If several devices are connected to TWI bus, this method mustn't be + * called. TWI bus must be initialized out of this library using + * Wire.begin() method. + ******************************************************************************/ +void +Eeprom24C128_256::initialize() +{ + Wire.begin(); +} + +/**************************************************************************//** + * \fn void Eeprom24C128_256::writeByte( + * word address, + * byte data) + * + * \brief Write a byte in EEPROM memory. + * + * \remarks A delay of 10 ms is required after write cycle. + * + * \param address Address. + * \param data Byte to write. + ******************************************************************************/ +void +Eeprom24C128_256::writeByte +( + word address, + byte data +){ + Wire.beginTransmission(m_deviceAddress); + Wire.write(address >> 8); + Wire.write(address & 0xFF); + Wire.write(data); + Wire.endTransmission(); +} + +/**************************************************************************//** + * \fn void Eeprom24C128_256::writeBytes( + * word address, + * word length, + * byte* p_data) + * + * \brief Write bytes in EEPROM memory. + * + * \param address Start address. + * \param length Number of bytes to write. + * \param[in] p_data Bytes to write. + ******************************************************************************/ +void +Eeprom24C128_256::writeBytes +( + word address, + word length, + byte* p_data +){ + // Write first page if not aligned. + byte notAlignedLength = 0; + byte pageOffset = address % EEPROM__PAGE_SIZE; + if (pageOffset > 0) + { + notAlignedLength = EEPROM__PAGE_SIZE - pageOffset; + if (length < notAlignedLength) + { + notAlignedLength = length; + } + writePage(address, notAlignedLength, p_data); + length -= notAlignedLength; + } + + if (length > 0) + { + address += notAlignedLength; + p_data += notAlignedLength; + + // Write complete and aligned pages. + word pageCount = length / EEPROM__PAGE_SIZE; + for (word i = 0; i < pageCount; i++) + { + writePage(address, EEPROM__PAGE_SIZE, p_data); + address += EEPROM__PAGE_SIZE; + p_data += EEPROM__PAGE_SIZE; + length -= EEPROM__PAGE_SIZE; + } + + if (length > 0) + { + // Write remaining uncomplete page. + writePage(address, length, p_data); + } + } +} + +/**************************************************************************//** + * \fn byte Eeprom24C128_256::readByte(word address) + * + * \brief Read a byte in EEPROM memory. + * + * \param address Address. + * + * \return Read byte. + ******************************************************************************/ +byte +Eeprom24C128_256::readByte +( + word address +){ + Wire.beginTransmission(m_deviceAddress); + Wire.write(address >> 8); + Wire.write(address & 0xFF); + Wire.endTransmission(); + Wire.requestFrom(m_deviceAddress, (byte)1); + byte data = 0; + if (Wire.available()) + { + data = Wire.read(); + } + return data; +} + +/**************************************************************************//** + * \fn void Eeprom24C128_256::readBytes( + * word address, + * word length, + * byte* p_data) + * + * \brief Read bytes in EEPROM memory. + * + * \param address Start address. + * \param length Number of bytes to read. + * \patam[in] p_data Byte array to fill with read bytes. + ******************************************************************************/ +void +Eeprom24C128_256::readBytes +( + word address, + word length, + byte* p_data +){ + byte bufferCount = length / EEPROM__RD_BUFFER_SIZE; + for (byte i = 0; i < bufferCount; i++) + { + word offset = i * EEPROM__RD_BUFFER_SIZE; + readBuffer(address + offset, EEPROM__RD_BUFFER_SIZE, p_data + offset); + } + + byte remainingBytes = length % EEPROM__RD_BUFFER_SIZE; + word offset = length - remainingBytes; + readBuffer(address + offset, remainingBytes, p_data + offset); +} + +/****************************************************************************** + * Private method definitions. + ******************************************************************************/ + +/**************************************************************************//** + * \fn void Eeprom24C128_256::writePage( + * word address, + * byte length, + * byte* p_data) + * + * \brief Write page in EEPROM memory. + * + * \param address Start address. + * \param length Number of bytes (EEPROM__PAGE_SIZE bytes max). + * \param[in] p_data Data. + ******************************************************************************/ +void +Eeprom24C128_256::writePage +( + word address, + byte length, + byte* p_data +){ + // Write complete buffers. + byte bufferCount = length / EEPROM__WR_BUFFER_SIZE; + for (byte i = 0; i < bufferCount; i++) + { + byte offset = i * EEPROM__WR_BUFFER_SIZE; + writeBuffer(address + offset, EEPROM__WR_BUFFER_SIZE, p_data + offset); + } + + // Write remaining bytes. + byte remainingBytes = length % EEPROM__WR_BUFFER_SIZE; + byte offset = length - remainingBytes; + writeBuffer(address + offset, remainingBytes, p_data + offset); +} + +/**************************************************************************//** + * \fn void Eeprom24C128_256::writeBuffer( + * word address, + * byte length, + * byte* p_data) + * + * \brief Write bytes into memory. + * + * \param address Start address. + * \param length Number of bytes (EEPROM__WR_BUFFER_SIZE bytes max). + * \param[in] p_data Data. + ******************************************************************************/ +void +Eeprom24C128_256::writeBuffer +( + word address, + byte length, + byte* p_data +){ + Wire.beginTransmission(m_deviceAddress); + Wire.write(address >> 8); + Wire.write(address & 0xFF); + for (byte i = 0; i < length; i++) + { + Wire.write(p_data[i]); + } + Wire.endTransmission(); + + // Write cycle time (tWR). See EEPROM memory datasheet for more details. + delay(10); +} + +/**************************************************************************//** + * \fn void Eeprom24C128_256::readBuffer( + * word address, + * byte length, + * byte* p_data) + * + * \brief Read bytes in memory. + * + * \param address Start address. + * \param length Number of bytes (EEPROM__RD_BUFFER_SIZE bytes max). + * \param[in] p_data Buffer to fill with read bytes. + ******************************************************************************/ +void +Eeprom24C128_256::readBuffer +( + word address, + byte length, + byte* p_data +){ + Wire.beginTransmission(m_deviceAddress); + Wire.write(address >> 8); + Wire.write(address & 0xFF); + Wire.endTransmission(); + Wire.requestFrom(m_deviceAddress, length); + for (byte i = 0; i < length; i++) + { + if (Wire.available()) + { + p_data[i] = Wire.read(); + } + } +} diff --git a/lib/AT24C256/Eeprom24C128_256.h b/lib/AT24C256/Eeprom24C128_256.h new file mode 100644 index 000000000..865c76226 --- /dev/null +++ b/lib/AT24C256/Eeprom24C128_256.h @@ -0,0 +1,212 @@ +/**************************************************************************//** + * \brief EEPROM 24C128 / 24C256 library for Arduino + * \author Copyright (C) 2012 Julien Le Sech - www.idreammicro.com + * \version 1.0 + * \date 20120203 + * + * This file is part of the EEPROM 24C128 / 24C256 library for Arduino. + * + * This library is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) any + * later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see http://www.gnu.org/licenses/ + ******************************************************************************/ + +/**************************************************************************//** + * \headerfile Eeprom24C128_256.h + ******************************************************************************/ + +#ifndef Eeprom24C128_256_h +#define Eeprom24C128_256_h + +/****************************************************************************** + * Header file inclusion. + ******************************************************************************/ + +#include + +/**************************************************************************//** + * \class Eeprom24C128_256 + * + * \brief EEPROM 24C128 / 24C256 memory driver. + * + * This driver is mainly designed for 24C128 and 24C256 EEPROM memories. It's + * also suitable for 24C512 memories. + ******************************************************************************/ +class Eeprom24C128_256 +{ + public: + + /******************************************************************//** + * \fn Eeprom24C128_256(byte deviceAddress) + * + * \brief Constructor. + * + * \param deviceAddress EEPROM address on TWI bus. + **********************************************************************/ + Eeprom24C128_256 + ( + byte deviceAddress + ); + + /******************************************************************//** + * \fn void initialize() + * + * \brief Initialize library abnd TWI bus. + * + * If several devices are connected to TWI bus, this method mustn't be + * called. TWI bus must be initialized out of this library using + * Wire.begin() method. + **********************************************************************/ + void + initialize(); + + /******************************************************************//** + * \fn void writeByte( + * word address, + * byte data) + * + * \brief Write a byte in EEPROM memory. + * + * \remarks A delay of 10 ms is required after write cycle. + * + * \param address Address. + * \param data Byte to write. + **********************************************************************/ + void + writeByte + ( + word address, + byte data + ); + + /******************************************************************//** + * \fn void writeBytes( + * word address, + * word length, + * byte* p_data) + * + * \brief Write bytes in EEPROM memory. + * + * \param address Start address. + * \param length Number of bytes to write. + * \param[in] p_data Bytes to write. + **********************************************************************/ + void + writeBytes + ( + word address, + word length, + byte* p_data + ); + + /******************************************************************//** + * \fn byte readByte(word address) + * + * \brief Read a byte in EEPROM memory. + * + * \param address Address. + * + * \return Read byte. + **********************************************************************/ + byte + readByte + ( + word address + ); + + /******************************************************************//** + * \fn void readBytes( + * word address, + * word length, + * byte* p_data) + * + * \brief Read bytes in EEPROM memory. + * + * \param address Start address. + * \param length Number of bytes to read. + * \patam[in] p_data Byte array to fill with read bytes. + **********************************************************************/ + void + readBytes + ( + word address, + word length, + byte* p_buffer + ); + + private: + + byte m_deviceAddress; + + /******************************************************************//** + * \fn void writePage( + * word address, + * byte length, + * byte* p_data) + * + * \brief Write page in EEPROM memory. + * + * \param address Start address. + * \param length Number of bytes (64 bytes max). + * \param[in] p_data Data. + **********************************************************************/ + void + writePage + ( + word address, + byte length, + byte* p_data + ); + + /******************************************************************//** + * \fn void writeBuffer( + * word address, + * byte length, + * byte* p_data) + * + * \brief Write bytes into memory. + * + * \param address Start address. + * \param length Number of bytes (30 bytes max). + * \param[in] p_date Data. + **********************************************************************/ + void + writeBuffer + ( + word address, + byte length, + byte* p_data + ); + + /******************************************************************//** + * \fn void readBuffer( + * word address, + * byte length, + * byte* p_data) + * + * \brief Read bytes in memory. + * + * \param address Start address. + * \param length Number of bytes to read (32 bytes max). + * \param[in] p_data Buffer to fill with read bytes. + **********************************************************************/ + void + readBuffer + ( + word address, + byte length, + byte* p_data + ); +}; + +#endif // Eeprom24C128_256_h + diff --git a/lib/Adafruit-GFX-Library-1.2.9/Adafruit_SPITFT.cpp b/lib/Adafruit-GFX-Library-1.2.9/Adafruit_SPITFT.cpp deleted file mode 100644 index 51b53cc3b..000000000 --- a/lib/Adafruit-GFX-Library-1.2.9/Adafruit_SPITFT.cpp +++ /dev/null @@ -1,555 +0,0 @@ -/*! -* @file Adafruit_SPITFT.cpp -* -* @mainpage Adafruit SPI TFT Displays -* -* @section intro_sec Introduction - This is our library for generic SPI TFT Displays with - address windows and 16 bit color (e.g. ILI9341, HX8357D, ST7735...) - - Check out the links above for our tutorials and wiring diagrams - These displays use SPI to communicate, 4 or 5 pins are required to - interface (RST is optional) - Adafruit invests time and resources providing this open source code, - please support Adafruit and open-source hardware by purchasing - products from Adafruit! - - Written by Limor Fried/Ladyada for Adafruit Industries. - MIT license, all text above must be included in any redistribution -* @section dependencies Dependencies -* -* This library depends on -* Adafruit_GFX being present on your system. Please make sure you have -* installed the latest version before using this library. -* -* @section author Author -* -* Written by Limor "ladyada" Fried for Adafruit Industries. -* -* @section license License -* -* BSD license, all text here must be included in any redistribution. -* -*/ - -#ifndef __AVR_ATtiny85__ // NOT A CHANCE of this stuff working on ATtiny! - -#include "Adafruit_SPITFT.h" -#ifndef ARDUINO_STM32_FEATHER - #include "pins_arduino.h" -#ifndef RASPI - #include "wiring_private.h" -#endif -#endif -#include - -#include "Adafruit_SPITFT_Macros.h" - - - -/**************************************************************************/ -/*! - @brief Pass 8-bit (each) R,G,B, get back 16-bit packed color - This function converts 8-8-8 RGB data to 16-bit 5-6-5 - @param red Red 8 bit color - @param green Green 8 bit color - @param blue Blue 8 bit color - @return Unsigned 16-bit down-sampled color in 5-6-5 format -*/ -/**************************************************************************/ -uint16_t Adafruit_SPITFT::color565(uint8_t red, uint8_t green, uint8_t blue) { - return ((red & 0xF8) << 8) | ((green & 0xFC) << 3) | ((blue & 0xF8) >> 3); -} - - -/**************************************************************************/ -/*! - @brief Instantiate Adafruit SPI display driver with software SPI - @param w Display width in pixels - @param h Display height in pixels - @param cs Chip select pin # - @param dc Data/Command pin # - @param mosi SPI MOSI pin # - @param sclk SPI Clock pin # - @param rst Reset pin # (optional, pass -1 if unused) - @param miso SPI MISO pin # (optional, pass -1 if unused) -*/ -/**************************************************************************/ -Adafruit_SPITFT::Adafruit_SPITFT(uint16_t w, uint16_t h, - int8_t cs, int8_t dc, int8_t mosi, - int8_t sclk, int8_t rst, int8_t miso) - : Adafruit_GFX(w, h) { - _cs = cs; - _dc = dc; - _rst = rst; - _sclk = sclk; - _mosi = mosi; - _miso = miso; - _freq = 0; -#ifdef USE_FAST_PINIO - dcport = (RwReg *)portOutputRegister(digitalPinToPort(dc)); - dcpinmask = digitalPinToBitMask(dc); - clkport = (RwReg *)portOutputRegister(digitalPinToPort(sclk)); - clkpinmask = digitalPinToBitMask(sclk); - mosiport = (RwReg *)portOutputRegister(digitalPinToPort(mosi)); - mosipinmask = digitalPinToBitMask(mosi); - if(miso >= 0){ - misoport = (RwReg *)portInputRegister(digitalPinToPort(miso)); - misopinmask = digitalPinToBitMask(miso); - } else { - misoport = 0; - misopinmask = 0; - } - if(cs >= 0) { - csport = (RwReg *)portOutputRegister(digitalPinToPort(cs)); - cspinmask = digitalPinToBitMask(cs); - } else { - // No chip-select line defined; might be permanently tied to GND. - // Assign a valid GPIO register (though not used for CS), and an - // empty pin bitmask...the nonsense bit-twiddling might be faster - // than checking _cs and possibly branching. - csport = dcport; - cspinmask = 0; - } -#endif -} - -/**************************************************************************/ -/*! - @brief Instantiate Adafruit SPI display driver with hardware SPI - @param w Display width in pixels - @param h Display height in pixels - @param cs Chip select pin # - @param dc Data/Command pin # - @param rst Reset pin # (optional, pass -1 if unused) -*/ -/**************************************************************************/ -Adafruit_SPITFT::Adafruit_SPITFT(uint16_t w, uint16_t h, - int8_t cs, int8_t dc, int8_t rst) - : Adafruit_GFX(w, h) { - _cs = cs; - _dc = dc; - _rst = rst; - _sclk = -1; - _mosi = -1; - _miso = -1; - _freq = 0; -#ifdef USE_FAST_PINIO - clkport = 0; - clkpinmask = 0; - mosiport = 0; - mosipinmask = 0; - misoport = 0; - misopinmask = 0; - dcport = (RwReg *)portOutputRegister(digitalPinToPort(dc)); - dcpinmask = digitalPinToBitMask(dc); - if(cs >= 0) { - csport = (RwReg *)portOutputRegister(digitalPinToPort(cs)); - cspinmask = digitalPinToBitMask(cs); - } else { - // See notes in prior constructor. - csport = dcport; - cspinmask = 0; - } -#endif -} - -/**************************************************************************/ -/*! - @brief Initialiaze the SPI interface (hardware or software) - @param freq The desired maximum SPI hardware clock frequency -*/ -/**************************************************************************/ -void Adafruit_SPITFT::initSPI(uint32_t freq) { - _freq = freq; - - // Control Pins - if(_cs >= 0) { - pinMode(_cs, OUTPUT); - digitalWrite(_cs, HIGH); // Deselect - } - pinMode(_dc, OUTPUT); - digitalWrite(_dc, LOW); - - // Software SPI - if(_sclk >= 0){ - pinMode(_mosi, OUTPUT); - digitalWrite(_mosi, LOW); - pinMode(_sclk, OUTPUT); - digitalWrite(_sclk, HIGH); - if(_miso >= 0){ - pinMode(_miso, INPUT); - } - } - - // Hardware SPI - SPI_BEGIN(); - - // toggle RST low to reset - if (_rst >= 0) { - pinMode(_rst, OUTPUT); - digitalWrite(_rst, HIGH); - delay(100); - digitalWrite(_rst, LOW); - delay(100); - digitalWrite(_rst, HIGH); - delay(200); - } -} - -/**************************************************************************/ -/*! - @brief Read one byte from SPI interface (hardware or software - @returns One byte, MSB order -*/ -/**************************************************************************/ -uint8_t Adafruit_SPITFT::spiRead() { - if(_sclk < 0){ - return HSPI_READ(); - } - if(_miso < 0){ - return 0; - } - uint8_t r = 0; - for (uint8_t i=0; i<8; i++) { - SSPI_SCK_LOW(); - SSPI_SCK_HIGH(); - r <<= 1; - if (SSPI_MISO_READ()){ - r |= 0x1; - } - } - return r; -} - -/**************************************************************************/ -/*! - @brief Write one byte to SPI interface (hardware or software - @param b One byte to send, MSB order -*/ -/**************************************************************************/ -void Adafruit_SPITFT::spiWrite(uint8_t b) { - if(_sclk < 0){ - HSPI_WRITE(b); - return; - } - for(uint8_t bit = 0x80; bit; bit >>= 1){ - if((b) & bit){ - SSPI_MOSI_HIGH(); - } else { - SSPI_MOSI_LOW(); - } - SSPI_SCK_LOW(); - SSPI_SCK_HIGH(); - } -} - - -/* - * Transaction API - * */ - -/**************************************************************************/ -/*! - @brief Begin an SPI transaction & set CS low. -*/ -/**************************************************************************/ -void inline Adafruit_SPITFT::startWrite(void){ - SPI_BEGIN_TRANSACTION(); - SPI_CS_LOW(); -} - -/**************************************************************************/ -/*! - @brief Begin an SPI transaction & set CS high. -*/ -/**************************************************************************/ -void inline Adafruit_SPITFT::endWrite(void){ - SPI_CS_HIGH(); - SPI_END_TRANSACTION(); -} - -/**************************************************************************/ -/*! - @brief Write a command byte (must have a transaction in progress) - @param cmd The 8-bit command to send -*/ -/**************************************************************************/ -void Adafruit_SPITFT::writeCommand(uint8_t cmd){ - SPI_DC_LOW(); - spiWrite(cmd); - SPI_DC_HIGH(); -} - -/**************************************************************************/ -/*! - @brief Push a 2-byte color to the framebuffer RAM, will start transaction - @param color 16-bit 5-6-5 Color to draw -*/ -/**************************************************************************/ -void Adafruit_SPITFT::pushColor(uint16_t color) { - startWrite(); - SPI_WRITE16(color); - endWrite(); -} - - - -/**************************************************************************/ -/*! - @brief Blit multiple 2-byte colors (must have a transaction in progress) - @param colors Array of 16-bit 5-6-5 Colors to draw - @param len How many pixels to draw - 2 bytes per pixel! -*/ -/**************************************************************************/ -void Adafruit_SPITFT::writePixels(uint16_t * colors, uint32_t len){ - SPI_WRITE_PIXELS((uint8_t*)colors , len * 2); -} - -/**************************************************************************/ -/*! - @brief Blit a 2-byte color many times (must have a transaction in progress) - @param color The 16-bit 5-6-5 Color to draw - @param len How many pixels to draw -*/ -/**************************************************************************/ -void Adafruit_SPITFT::writeColor(uint16_t color, uint32_t len){ -#ifdef SPI_HAS_WRITE_PIXELS - if(_sclk >= 0){ - for (uint32_t t=0; t SPI_MAX_PIXELS_AT_ONCE)?SPI_MAX_PIXELS_AT_ONCE:len; - uint16_t tlen = 0; - - for (uint32_t t=0; tblen)?blen:len; - writePixels(temp, tlen); - len -= tlen; - } -#else - uint8_t hi = color >> 8, lo = color; - if(_sclk < 0){ //AVR Optimization - for (uint32_t t=len; t; t--){ - HSPI_WRITE(hi); - HSPI_WRITE(lo); - } - return; - } - for (uint32_t t=len; t; t--){ - spiWrite(hi); - spiWrite(lo); - } -#endif -} - -/**************************************************************************/ -/*! - @brief Write a pixel (must have a transaction in progress) - @param x x coordinate - @param y y coordinate - @param color 16-bit 5-6-5 Color to draw with -*/ -/**************************************************************************/ -void Adafruit_SPITFT::writePixel(int16_t x, int16_t y, uint16_t color) { - if((x < 0) ||(x >= _width) || (y < 0) || (y >= _height)) return; - setAddrWindow(x,y,1,1); - writePixel(color); -} - -/**************************************************************************/ -/*! - @brief Write a filled rectangle (must have a transaction in progress) - @param x Top left corner x coordinate - @param y Top left corner y coordinate - @param w Width in pixels - @param h Height in pixels - @param color 16-bit 5-6-5 Color to fill with -*/ -/**************************************************************************/ -void Adafruit_SPITFT::writeFillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color){ - if((x >= _width) || (y >= _height)) return; - int16_t x2 = x + w - 1, y2 = y + h - 1; - if((x2 < 0) || (y2 < 0)) return; - - // Clip left/top - if(x < 0) { - x = 0; - w = x2 + 1; - } - if(y < 0) { - y = 0; - h = y2 + 1; - } - - // Clip right/bottom - if(x2 >= _width) w = _width - x; - if(y2 >= _height) h = _height - y; - - int32_t len = (int32_t)w * h; - setAddrWindow(x, y, w, h); - writeColor(color, len); -} - -/**************************************************************************/ -/*! - @brief Write a perfectly vertical line (must have a transaction in progress) - @param x Top-most x coordinate - @param y Top-most y coordinate - @param h Height in pixels - @param color 16-bit 5-6-5 Color to fill with -*/ -/**************************************************************************/ -void inline Adafruit_SPITFT::writeFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color){ - writeFillRect(x, y, 1, h, color); -} - -/**************************************************************************/ -/*! - @brief Write a perfectly horizontal line (must have a transaction in progress) - @param x Left-most x coordinate - @param y Left-most y coordinate - @param w Width in pixels - @param color 16-bit 5-6-5 Color to fill with -*/ -/**************************************************************************/ -void inline Adafruit_SPITFT::writeFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color){ - writeFillRect(x, y, w, 1, color); -} - -/**************************************************************************/ -/*! - @brief Draw a pixel - sets up transaction - @param x x coordinate - @param y y coordinate - @param color 16-bit 5-6-5 Color to draw with -*/ -/**************************************************************************/ -void Adafruit_SPITFT::drawPixel(int16_t x, int16_t y, uint16_t color){ - startWrite(); - writePixel(x, y, color); - endWrite(); -} - -/**************************************************************************/ -/*! - @brief Write a perfectly vertical line - sets up transaction - @param x Top-most x coordinate - @param y Top-most y coordinate - @param h Height in pixels - @param color 16-bit 5-6-5 Color to fill with -*/ -/**************************************************************************/ -void Adafruit_SPITFT::drawFastVLine(int16_t x, int16_t y, - int16_t h, uint16_t color) { - startWrite(); - writeFastVLine(x, y, h, color); - endWrite(); -} - -/**************************************************************************/ -/*! - @brief Write a perfectly horizontal line - sets up transaction - @param x Left-most x coordinate - @param y Left-most y coordinate - @param w Width in pixels - @param color 16-bit 5-6-5 Color to fill with -*/ -/**************************************************************************/ -void Adafruit_SPITFT::drawFastHLine(int16_t x, int16_t y, - int16_t w, uint16_t color) { - startWrite(); - writeFastHLine(x, y, w, color); - endWrite(); -} - -/**************************************************************************/ -/*! - @brief Fill a rectangle completely with one color. - @param x Top left corner x coordinate - @param y Top left corner y coordinate - @param w Width in pixels - @param h Height in pixels - @param color 16-bit 5-6-5 Color to fill with -*/ -/**************************************************************************/ -void Adafruit_SPITFT::fillRect(int16_t x, int16_t y, int16_t w, int16_t h, - uint16_t color) { - startWrite(); - writeFillRect(x,y,w,h,color); - endWrite(); -} - - -/**************************************************************************/ -/*! - @brief Invert the display using built-in hardware command - @param i True if you want to invert, false to make 'normal' -*/ -/**************************************************************************/ -void Adafruit_SPITFT::invertDisplay(boolean i) { - startWrite(); - writeCommand(i ? invertOnCommand : invertOffCommand); - endWrite(); -} - - -/**************************************************************************/ -/*! - @brief Draw a 16-bit image (RGB 5/6/5) at the specified (x,y) position. - For 16-bit display devices; no color reduction performed. - Adapted from https://github.com/PaulStoffregen/ILI9341_t3 - by Marc MERLIN. See examples/pictureEmbed to use this. - 5/6/2017: function name and arguments have changed for compatibility - with current GFX library and to avoid naming problems in prior - implementation. Formerly drawBitmap() with arguments in different order. - - @param x Top left corner x coordinate - @param y Top left corner y coordinate - @param pcolors 16-bit array with 16-bit color bitmap - @param w Width of bitmap in pixels - @param h Height of bitmap in pixels -*/ -/**************************************************************************/ -void Adafruit_SPITFT::drawRGBBitmap(int16_t x, int16_t y, - uint16_t *pcolors, int16_t w, int16_t h) { - - int16_t x2, y2; // Lower-right coord - if(( x >= _width ) || // Off-edge right - ( y >= _height) || // " top - ((x2 = (x+w-1)) < 0 ) || // " left - ((y2 = (y+h-1)) < 0) ) return; // " bottom - - int16_t bx1=0, by1=0, // Clipped top-left within bitmap - saveW=w; // Save original bitmap width value - if(x < 0) { // Clip left - w += x; - bx1 = -x; - x = 0; - } - if(y < 0) { // Clip top - h += y; - by1 = -y; - y = 0; - } - if(x2 >= _width ) w = _width - x; // Clip right - if(y2 >= _height) h = _height - y; // Clip bottom - - pcolors += by1 * saveW + bx1; // Offset bitmap ptr to clipped top-left - startWrite(); - setAddrWindow(x, y, w, h); // Clipped area - while(h--) { // For each (clipped) scanline... - writePixels(pcolors, w); // Push one (clipped) row - pcolors += saveW; // Advance pointer by one full (unclipped) line - } - endWrite(); -} - -#endif // !__AVR_ATtiny85__ diff --git a/lib/Adafruit-GFX-Library-1.2.9/Adafruit_SPITFT.h b/lib/Adafruit-GFX-Library-1.2.9/Adafruit_SPITFT.h deleted file mode 100644 index 53cdd985d..000000000 --- a/lib/Adafruit-GFX-Library-1.2.9/Adafruit_SPITFT.h +++ /dev/null @@ -1,125 +0,0 @@ -#ifndef _ADAFRUIT_SPITFT_ -#define _ADAFRUIT_SPITFT_ - -#if ARDUINO >= 100 - #include "Arduino.h" - #include "Print.h" -#else - #include "WProgram.h" -#endif -#include -#include "Adafruit_GFX.h" - -#define USE_FAST_PINIO - -#if defined(__AVR__) - typedef volatile uint8_t RwReg; -#elif defined(ARDUINO_STM32_FEATHER) - typedef volatile uint32 RwReg; - #undef USE_FAST_PINIO -#elif defined(__OPENCR__) || defined (__OPENCM904__) - #undef USE_FAST_PINIO -#elif defined(ARDUINO_FEATHER52) || defined(__arm__) - typedef volatile uint32_t RwReg; -#elif defined(ESP32) || defined(ESP8266) - typedef volatile uint32_t RwReg; - #undef USE_FAST_PINIO -#else - #undef USE_FAST_PINIO -#endif - -#include "Adafruit_SPITFT_Macros.h" - -/// A heavily optimized SPI display subclass of GFX. Manages SPI bitbanging, transactions, DMA, etc! Despite being called SPITFT, the classic SPI data/command interface is also used by OLEDs. -class Adafruit_SPITFT : public Adafruit_GFX { - protected: - - public: - Adafruit_SPITFT(uint16_t w, uint16_t h, int8_t _CS, int8_t _DC, int8_t _MOSI, int8_t _SCLK, int8_t _RST = -1, int8_t _MISO = -1); - Adafruit_SPITFT(uint16_t w, uint16_t h, int8_t _CS, int8_t _DC, int8_t _RST = -1); - - virtual void begin(uint32_t freq) = 0; ///< Virtual begin() function to set SPI frequency, must be overridden in subclass. @param freq Maximum SPI hardware clock speed - - void initSPI(uint32_t freq); - - // Required Non-Transaction - void drawPixel(int16_t x, int16_t y, uint16_t color); - - // Transaction API - void startWrite(void); - void endWrite(void); - - void writePixel(int16_t x, int16_t y, uint16_t color); - void writeFillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color); - void writeFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color); - void writeFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color); - - // Transaction API not used by GFX - - /*! - @brief SPI displays set an address window rectangle for blitting pixels - @param x Top left corner x coordinate - @param y Top left corner x coordinate - @param w Width of window - @param h Height of window - */ - virtual void setAddrWindow(uint16_t x, uint16_t y, uint16_t w, uint16_t h) = 0; - - /*! - @brief Write a 2-byte color (must have a transaction in progress) - @param color 16-bit 5-6-5 Color to draw - */ - void inline writePixel(uint16_t color) { SPI_WRITE16(color); } - void writePixels(uint16_t * colors, uint32_t len); - void writeColor(uint16_t color, uint32_t len); - void pushColor(uint16_t color); - - // Recommended Non-Transaction - void drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color); - void drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color); - void fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color); - - using Adafruit_GFX::drawRGBBitmap; // Check base class first - void drawRGBBitmap(int16_t x, int16_t y, - uint16_t *pcolors, int16_t w, int16_t h); - void invertDisplay(boolean i); - - uint16_t color565(uint8_t r, uint8_t g, uint8_t b); - - protected: - uint32_t _freq; ///< SPI clock frequency (for hardware SPI) -#if defined (__AVR__) || defined(TEENSYDUINO) || defined (ESP8266) || defined (ESP32) - int8_t _cs, _dc, _rst, _sclk, _mosi, _miso; -#else - int32_t _cs, ///< Arduino pin # for chip-select pin - _dc, ///< Arduino pin # for data-command pin - _rst, ///< Arduino pin # for reset pin - _sclk, ///< Arduino pin # for SPI clock pin - _mosi, ///< Arduino pin # for SPI MOSI pin - _miso; ///< Arduino pin # for SPI MISO pin -#endif - -#ifdef USE_FAST_PINIO - volatile RwReg *mosiport, ///< Direct chip register for toggling MOSI with fast bitbang IO - *misoport, ///< Direct chip register for toggling MISO with fast bitbang IO - *clkport, ///< Direct chip register for toggling CLK with fast bitbang IO - *dcport, ///< Direct chip register for toggling DC with fast bitbang IO - *csport; ///< Direct chip register for toggling CS with fast bitbang IO - RwReg mosipinmask, ///< bitmask for turning on/off MOSI with fast register bitbang IO - misopinmask, ///< bitmask for turning on/off MISO with fast register bitbang IO - clkpinmask, ///< bitmask for turning on/off CLK with fast register bitbang IO - cspinmask, ///< bitmask for turning on/off CS with fast register bitbang IO - dcpinmask; ///< bitmask for turning on/off DC with fast register bitbang IO -#endif - - void writeCommand(uint8_t cmd); - void spiWrite(uint8_t v); - uint8_t spiRead(void); - - uint8_t invertOnCommand = 0, ///< SPI command byte to turn on invert - invertOffCommand = 0; ///< SPI command byte to turn off invert - int16_t _xstart = 0; ///< Many displays don't have pixels starting at (0,0) of the internal framebuffer, this is the x offset from 0 to align - int16_t _ystart = 0; ///< Many displays don't have pixels starting at (0,0) of the internal framebuffer, this is the y offset from 0 to align -}; - -#endif diff --git a/lib/Adafruit-GFX-Library-1.2.9/Adafruit_SPITFT_Macros.h b/lib/Adafruit-GFX-Library-1.2.9/Adafruit_SPITFT_Macros.h deleted file mode 100644 index f0466ef5f..000000000 --- a/lib/Adafruit-GFX-Library-1.2.9/Adafruit_SPITFT_Macros.h +++ /dev/null @@ -1,118 +0,0 @@ -#ifndef _ADAFRUIT_SPITFT_MACROS -#define _ADAFRUIT_SPITFT_MACROS - -/* - * Control Pins - * */ - -#ifdef USE_FAST_PINIO -#define SPI_DC_HIGH() *dcport |= dcpinmask -#define SPI_DC_LOW() *dcport &= ~dcpinmask -#define SPI_CS_HIGH() *csport |= cspinmask -#define SPI_CS_LOW() *csport &= ~cspinmask -#else -#define SPI_DC_HIGH() digitalWrite(_dc, HIGH) -#define SPI_DC_LOW() digitalWrite(_dc, LOW) -#define SPI_CS_HIGH() { if(_cs >= 0) digitalWrite(_cs, HIGH); } -#define SPI_CS_LOW() { if(_cs >= 0) digitalWrite(_cs, LOW); } -#endif - -/* - * Software SPI Macros - * */ - -#ifdef USE_FAST_PINIO -#define SSPI_MOSI_HIGH() *mosiport |= mosipinmask -#define SSPI_MOSI_LOW() *mosiport &= ~mosipinmask -#define SSPI_SCK_HIGH() *clkport |= clkpinmask -#define SSPI_SCK_LOW() *clkport &= ~clkpinmask -#define SSPI_MISO_READ() ((*misoport & misopinmask) != 0) -#else -#define SSPI_MOSI_HIGH() digitalWrite(_mosi, HIGH) -#define SSPI_MOSI_LOW() digitalWrite(_mosi, LOW) -#define SSPI_SCK_HIGH() digitalWrite(_sclk, HIGH) -#define SSPI_SCK_LOW() digitalWrite(_sclk, LOW) -#define SSPI_MISO_READ() digitalRead(_miso) -#endif - -#define SSPI_BEGIN_TRANSACTION() -#define SSPI_END_TRANSACTION() -#define SSPI_WRITE(v) spiWrite(v) -#define SSPI_WRITE16(s) SSPI_WRITE((s) >> 8); SSPI_WRITE(s) -#define SSPI_WRITE32(l) SSPI_WRITE((l) >> 24); SSPI_WRITE((l) >> 16); SSPI_WRITE((l) >> 8); SSPI_WRITE(l) -#define SSPI_WRITE_PIXELS(c,l) for(uint32_t i=0; i<(l); i+=2){ SSPI_WRITE(((uint8_t*)(c))[i+1]); SSPI_WRITE(((uint8_t*)(c))[i]); } - -/* - * Hardware SPI Macros - * */ - -#define SPI_OBJECT SPI - -#if defined (__AVR__) || defined(TEENSYDUINO) || defined(ARDUINO_ARCH_STM32F1) - #define HSPI_SET_CLOCK() SPI_OBJECT.setClockDivider(SPI_CLOCK_DIV2); -#elif defined (__arm__) - #define HSPI_SET_CLOCK() SPI_OBJECT.setClockDivider(11); -#elif defined(ESP8266) || defined(ESP32) - #define HSPI_SET_CLOCK() SPI_OBJECT.setFrequency(_freq); -#elif defined(RASPI) - #define HSPI_SET_CLOCK() SPI_OBJECT.setClock(_freq); -#elif defined(ARDUINO_ARCH_STM32F1) - #define HSPI_SET_CLOCK() SPI_OBJECT.setClock(_freq); -#else - #define HSPI_SET_CLOCK() -#endif - -#ifdef SPI_HAS_TRANSACTION - #define HSPI_BEGIN_TRANSACTION() SPI_OBJECT.beginTransaction(SPISettings(_freq, MSBFIRST, SPI_MODE0)) - #define HSPI_END_TRANSACTION() SPI_OBJECT.endTransaction() -#else - #define HSPI_BEGIN_TRANSACTION() HSPI_SET_CLOCK(); SPI_OBJECT.setBitOrder(MSBFIRST); SPI_OBJECT.setDataMode(SPI_MODE0) - #define HSPI_END_TRANSACTION() -#endif - -#ifdef ESP32 - #define SPI_HAS_WRITE_PIXELS -#endif -#if defined(ESP8266) || defined(ESP32) - // Optimized SPI (ESP8266 and ESP32) - #define HSPI_READ() SPI_OBJECT.transfer(0) - #define HSPI_WRITE(b) SPI_OBJECT.write(b) - #define HSPI_WRITE16(s) SPI_OBJECT.write16(s) - #define HSPI_WRITE32(l) SPI_OBJECT.write32(l) - #ifdef SPI_HAS_WRITE_PIXELS - #define SPI_MAX_PIXELS_AT_ONCE 32 - #define HSPI_WRITE_PIXELS(c,l) SPI_OBJECT.writePixels(c,l) - #else - #define HSPI_WRITE_PIXELS(c,l) for(uint32_t i=0; i<((l)/2); i++){ SPI_WRITE16(((uint16_t*)(c))[i]); } - #endif -#else - // Standard Byte-by-Byte SPI - - #if defined (__AVR__) || defined(TEENSYDUINO) -static inline uint8_t _avr_spi_read(void) __attribute__((always_inline)); -static inline uint8_t _avr_spi_read(void) { - uint8_t r = 0; - SPDR = r; - while(!(SPSR & _BV(SPIF))); - r = SPDR; - return r; -} - #define HSPI_WRITE(b) {SPDR = (b); while(!(SPSR & _BV(SPIF)));} - #define HSPI_READ() _avr_spi_read() - #else - #define HSPI_WRITE(b) SPI_OBJECT.transfer((uint8_t)(b)) - #define HSPI_READ() HSPI_WRITE(0) - #endif - #define HSPI_WRITE16(s) HSPI_WRITE((s) >> 8); HSPI_WRITE(s) - #define HSPI_WRITE32(l) HSPI_WRITE((l) >> 24); HSPI_WRITE((l) >> 16); HSPI_WRITE((l) >> 8); HSPI_WRITE(l) - #define HSPI_WRITE_PIXELS(c,l) for(uint32_t i=0; i<(l); i+=2){ HSPI_WRITE(((uint8_t*)(c))[i+1]); HSPI_WRITE(((uint8_t*)(c))[i]); } -#endif - -#define SPI_BEGIN() if(_sclk < 0){SPI_OBJECT.begin();} -#define SPI_BEGIN_TRANSACTION() if(_sclk < 0){HSPI_BEGIN_TRANSACTION();} -#define SPI_END_TRANSACTION() if(_sclk < 0){HSPI_END_TRANSACTION();} -#define SPI_WRITE16(s) if(_sclk < 0){HSPI_WRITE16(s);}else{SSPI_WRITE16(s);} -#define SPI_WRITE32(l) if(_sclk < 0){HSPI_WRITE32(l);}else{SSPI_WRITE32(l);} -#define SPI_WRITE_PIXELS(c,l) if(_sclk < 0){HSPI_WRITE_PIXELS(c,l);}else{SSPI_WRITE_PIXELS(c,l);} - -#endif // _ADAFRUIT_SPITFT_MACROS diff --git a/lib/Adafruit-GFX-Library-1.2.9/.gitignore b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/.gitignore similarity index 100% rename from lib/Adafruit-GFX-Library-1.2.9/.gitignore rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/.gitignore diff --git a/lib/Adafruit-GFX-Library-1.2.9/.travis.yml b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/.travis.yml similarity index 87% rename from lib/Adafruit-GFX-Library-1.2.9/.travis.yml rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/.travis.yml index a856c6db6..d0836629f 100644 --- a/lib/Adafruit-GFX-Library-1.2.9/.travis.yml +++ b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/.travis.yml @@ -9,7 +9,6 @@ git: quiet: true env: global: - - ARDUINO_IDE_VERSION="1.8.5" - PRETTYNAME="Adafruit GFX Library" before_install: @@ -24,4 +23,4 @@ script: # Generate and deploy documentation after_success: - source <(curl -SLs https://raw.githubusercontent.com/adafruit/travis-ci-arduino/master/library_check.sh) - - source <(curl -SLs https://raw.githubusercontent.com/adafruit/travis-ci-arduino/master/doxy_gen_and_deploy.sh) \ No newline at end of file + - source <(curl -SLs https://raw.githubusercontent.com/adafruit/travis-ci-arduino/master/doxy_gen_and_deploy.sh) diff --git a/lib/Adafruit-GFX-Library-1.2.9/Adafruit_GFX.cpp b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Adafruit_GFX.cpp similarity index 85% rename from lib/Adafruit-GFX-Library-1.2.9/Adafruit_GFX.cpp rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Adafruit_GFX.cpp index c431a17c8..8741e6247 100644 --- a/lib/Adafruit-GFX-Library-1.2.9/Adafruit_GFX.cpp +++ b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Adafruit_GFX.cpp @@ -62,6 +62,30 @@ POSSIBILITY OF SUCH DAMAGE. #define pgm_read_pointer(addr) ((void *)pgm_read_word(addr)) #endif +inline GFXglyph * pgm_read_glyph_ptr(const GFXfont *gfxFont, uint8_t c) +{ +#ifdef __AVR__ + return &(((GFXglyph *)pgm_read_pointer(&gfxFont->glyph))[c]); +#else + // expression in __AVR__ section may generate "dereferencing type-punned pointer will break strict-aliasing rules" warning + // In fact, on other platforms (such as STM32) there is no need to do this pointer magic as program memory may be read in a usual way + // So expression may be simplified + return gfxFont->glyph + c; +#endif //__AVR__ +} + +inline uint8_t * pgm_read_bitmap_ptr(const GFXfont *gfxFont) +{ +#ifdef __AVR__ + return (uint8_t *)pgm_read_pointer(&gfxFont->bitmap); +#else + // expression in __AVR__ section generates "dereferencing type-punned pointer will break strict-aliasing rules" warning + // In fact, on other platforms (such as STM32) there is no need to do this pointer magic as program memory may be read in a usual way + // So expression may be simplified + return gfxFont->bitmap; +#endif //__AVR__ +} + #ifndef min #define min(a,b) (((a) < (b)) ? (a) : (b)) #endif @@ -84,7 +108,7 @@ WIDTH(w), HEIGHT(h) _height = HEIGHT; rotation = 0; cursor_y = cursor_x = 0; - textsize = 1; + textsize_x = textsize_y = 1; textcolor = textbgcolor = 0xFFFF; wrap = true; _cp437 = false; @@ -103,6 +127,9 @@ WIDTH(w), HEIGHT(h) /**************************************************************************/ void Adafruit_GFX::writeLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t color) { +#if defined(ESP8266) + yield(); +#endif int16_t steep = abs(y1 - y0) > abs(x1 - x0); if (steep) { _swap_int16_t(x0, y0); @@ -317,6 +344,9 @@ void Adafruit_GFX::drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, /**************************************************************************/ void Adafruit_GFX::drawCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color) { +#if defined(ESP8266) + yield(); +#endif int16_t f = 1 - r; int16_t ddF_x = 1; int16_t ddF_y = -2 * r; @@ -353,7 +383,7 @@ void Adafruit_GFX::drawCircle(int16_t x0, int16_t y0, int16_t r, /**************************************************************************/ /*! - @brief Quarter-circle drawer, used to do circles and roundrects + @brief Quarter-circle drawer, used to do circles and roundrects @param x0 Center-point x coordinate @param y0 Center-point y coordinate @param r Radius of circle @@ -417,25 +447,29 @@ void Adafruit_GFX::fillCircle(int16_t x0, int16_t y0, int16_t r, /**************************************************************************/ /*! - @brief Quarter-circle drawer with fill, used to do circles and roundrects - @param x0 Center-point x coordinate - @param y0 Center-point y coordinate - @param r Radius of circle - @param cornername Mask bit #1 or bit #2 to indicate which quarters of the circle we're doing - @param delta Offset from center-point, used for round-rects - @param color 16-bit 5-6-5 Color to fill with + @brief Quarter-circle drawer with fill, used for circles and roundrects + @param x0 Center-point x coordinate + @param y0 Center-point y coordinate + @param r Radius of circle + @param corners Mask bits indicating which quarters we're doing + @param delta Offset from center-point, used for round-rects + @param color 16-bit 5-6-5 Color to fill with */ /**************************************************************************/ void Adafruit_GFX::fillCircleHelper(int16_t x0, int16_t y0, int16_t r, - uint8_t cornername, int16_t delta, uint16_t color) { + uint8_t corners, int16_t delta, uint16_t color) { int16_t f = 1 - r; int16_t ddF_x = 1; int16_t ddF_y = -2 * r; int16_t x = 0; int16_t y = r; + int16_t px = x; + int16_t py = y; - while (x= 0) { y--; ddF_y += 2; @@ -444,15 +478,18 @@ void Adafruit_GFX::fillCircleHelper(int16_t x0, int16_t y0, int16_t r, x++; ddF_x += 2; f += ddF_x; - - if (cornername & 0x1) { - writeFastVLine(x0+x, y0-y, 2*y+1+delta, color); - writeFastVLine(x0+y, y0-x, 2*x+1+delta, color); + // These checks avoid double-drawing certain lines, important + // for the SSD1306 library which has an INVERT drawing mode. + if(x < (y + 1)) { + if(corners & 1) writeFastVLine(x0+x, y0-y, 2*y+delta, color); + if(corners & 2) writeFastVLine(x0-x, y0-y, 2*y+delta, color); } - if (cornername & 0x2) { - writeFastVLine(x0-x, y0-y, 2*y+1+delta, color); - writeFastVLine(x0-y, y0-x, 2*x+1+delta, color); + if(y != py) { + if(corners & 1) writeFastVLine(x0+py, y0-px, 2*px+delta, color); + if(corners & 2) writeFastVLine(x0-py, y0-px, 2*px+delta, color); + py = y; } + px = x; } } @@ -488,7 +525,9 @@ void Adafruit_GFX::drawRect(int16_t x, int16_t y, int16_t w, int16_t h, */ /**************************************************************************/ void Adafruit_GFX::drawRoundRect(int16_t x, int16_t y, int16_t w, - int16_t h, int16_t r, uint16_t color) { + int16_t h, int16_t r, uint16_t color) { + int16_t max_radius = ((w < h) ? w : h) / 2; // 1/2 minor axis + if(r > max_radius) r = max_radius; // smarter version startWrite(); writeFastHLine(x+r , y , w-2*r, color); // Top @@ -515,11 +554,12 @@ void Adafruit_GFX::drawRoundRect(int16_t x, int16_t y, int16_t w, */ /**************************************************************************/ void Adafruit_GFX::fillRoundRect(int16_t x, int16_t y, int16_t w, - int16_t h, int16_t r, uint16_t color) { + int16_t h, int16_t r, uint16_t color) { + int16_t max_radius = ((w < h) ? w : h) / 2; // 1/2 minor axis + if(r > max_radius) r = max_radius; // smarter version startWrite(); writeFillRect(x+r, y, w-2*r, h, color); - // draw four corners fillCircleHelper(x+w-r-1, y+r, r, 1, h-2*r-1, color); fillCircleHelper(x+r , y+r, r, 2, h-2*r-1, color); @@ -620,8 +660,8 @@ void Adafruit_GFX::fillTriangle(int16_t x0, int16_t y0, // For lower part of triangle, find scanline crossings for segments // 0-2 and 1-2. This loop is skipped if y1=y2. - sa = dx12 * (y - y1); - sb = dx02 * (y - y0); + sa = (int32_t)dx12 * (y - y1); + sb = (int32_t)dx02 * (y - y0); for(; y<=y2; y++) { a = x1 + sa / dy12; b = x0 + sb / dy02; @@ -646,7 +686,7 @@ void Adafruit_GFX::fillTriangle(int16_t x0, int16_t y0, @param y Top left corner y coordinate @param bitmap byte array with monochrome bitmap @param w Width of bitmap in pixels - @param h Hieght of bitmap in pixels + @param h Height of bitmap in pixels @param color 16-bit 5-6-5 Color to draw with */ /**************************************************************************/ @@ -674,7 +714,7 @@ void Adafruit_GFX::drawBitmap(int16_t x, int16_t y, @param y Top left corner y coordinate @param bitmap byte array with monochrome bitmap @param w Width of bitmap in pixels - @param h Hieght of bitmap in pixels + @param h Height of bitmap in pixels @param color 16-bit 5-6-5 Color to draw pixels with @param bg 16-bit 5-6-5 Color to draw background with */ @@ -704,7 +744,7 @@ void Adafruit_GFX::drawBitmap(int16_t x, int16_t y, @param y Top left corner y coordinate @param bitmap byte array with monochrome bitmap @param w Width of bitmap in pixels - @param h Hieght of bitmap in pixels + @param h Height of bitmap in pixels @param color 16-bit 5-6-5 Color to draw with */ /**************************************************************************/ @@ -732,7 +772,7 @@ void Adafruit_GFX::drawBitmap(int16_t x, int16_t y, @param y Top left corner y coordinate @param bitmap byte array with monochrome bitmap @param w Width of bitmap in pixels - @param h Hieght of bitmap in pixels + @param h Height of bitmap in pixels @param color 16-bit 5-6-5 Color to draw pixels with @param bg 16-bit 5-6-5 Color to draw background with */ @@ -756,7 +796,7 @@ void Adafruit_GFX::drawBitmap(int16_t x, int16_t y, /**************************************************************************/ /*! - @brief Draw PROGMEM-resident XBitMap Files (*.xbm), exported from GIMP. + @brief Draw PROGMEM-resident XBitMap Files (*.xbm), exported from GIMP. Usage: Export from GIMP to *.xbm, rename *.xbm to *.c and open in editor. C Array can be directly used with this function. There is no RAM-resident version of this function; if generating bitmaps @@ -765,7 +805,7 @@ void Adafruit_GFX::drawBitmap(int16_t x, int16_t y, @param y Top left corner y coordinate @param bitmap byte array with monochrome bitmap @param w Width of bitmap in pixels - @param h Hieght of bitmap in pixels + @param h Height of bitmap in pixels @param color 16-bit 5-6-5 Color to draw pixels with */ /**************************************************************************/ @@ -791,13 +831,13 @@ void Adafruit_GFX::drawXBitmap(int16_t x, int16_t y, /**************************************************************************/ /*! - @brief Draw a PROGMEM-resident 8-bit image (grayscale) at the specified (x,y) pos. + @brief Draw a PROGMEM-resident 8-bit image (grayscale) at the specified (x,y) pos. Specifically for 8-bit display devices such as IS31FL3731; no color reduction/expansion is performed. @param x Top left corner x coordinate @param y Top left corner y coordinate @param bitmap byte array with grayscale bitmap @param w Width of bitmap in pixels - @param h Hieght of bitmap in pixels + @param h Height of bitmap in pixels */ /**************************************************************************/ void Adafruit_GFX::drawGrayscaleBitmap(int16_t x, int16_t y, @@ -813,13 +853,13 @@ void Adafruit_GFX::drawGrayscaleBitmap(int16_t x, int16_t y, /**************************************************************************/ /*! - @brief Draw a RAM-resident 8-bit image (grayscale) at the specified (x,y) pos. + @brief Draw a RAM-resident 8-bit image (grayscale) at the specified (x,y) pos. Specifically for 8-bit display devices such as IS31FL3731; no color reduction/expansion is performed. @param x Top left corner x coordinate @param y Top left corner y coordinate @param bitmap byte array with grayscale bitmap @param w Width of bitmap in pixels - @param h Hieght of bitmap in pixels + @param h Height of bitmap in pixels */ /**************************************************************************/ void Adafruit_GFX::drawGrayscaleBitmap(int16_t x, int16_t y, @@ -900,7 +940,7 @@ void Adafruit_GFX::drawGrayscaleBitmap(int16_t x, int16_t y, /**************************************************************************/ /*! - @brief Draw a PROGMEM-resident 16-bit image (RGB 5/6/5) at the specified (x,y) position. + @brief Draw a PROGMEM-resident 16-bit image (RGB 5/6/5) at the specified (x,y) position. For 16-bit display devices; no color reduction performed. @param x Top left corner x coordinate @param y Top left corner y coordinate @@ -922,7 +962,7 @@ void Adafruit_GFX::drawRGBBitmap(int16_t x, int16_t y, /**************************************************************************/ /*! - @brief Draw a RAM-resident 16-bit image (RGB 5/6/5) at the specified (x,y) position. + @brief Draw a RAM-resident 16-bit image (RGB 5/6/5) at the specified (x,y) position. For 16-bit display devices; no color reduction performed. @param x Top left corner x coordinate @param y Top left corner y coordinate @@ -1016,13 +1056,31 @@ void Adafruit_GFX::drawRGBBitmap(int16_t x, int16_t y, /**************************************************************************/ void Adafruit_GFX::drawChar(int16_t x, int16_t y, unsigned char c, uint16_t color, uint16_t bg, uint8_t size) { + drawChar(x, y, c, color, bg, size, size); +} + +// Draw a character +/**************************************************************************/ +/*! + @brief Draw a single character + @param x Bottom left corner x coordinate + @param y Bottom left corner y coordinate + @param c The 8-bit font-indexed character (likely ascii) + @param color 16-bit 5-6-5 Color to draw chraracter with + @param bg 16-bit 5-6-5 Color to fill background with (if same as color, no background) + @param size_x Font magnification level in X-axis, 1 is 'original' size + @param size_y Font magnification level in Y-axis, 1 is 'original' size +*/ +/**************************************************************************/ +void Adafruit_GFX::drawChar(int16_t x, int16_t y, unsigned char c, + uint16_t color, uint16_t bg, uint8_t size_x, uint8_t size_y) { if(!gfxFont) { // 'Classic' built-in font if((x >= _width) || // Clip right (y >= _height) || // Clip bottom - ((x + 6 * size - 1) < 0) || // Clip left - ((y + 8 * size - 1) < 0)) // Clip top + ((x + 6 * size_x - 1) < 0) || // Clip left + ((y + 8 * size_y - 1) < 0)) // Clip top return; if(!_cp437 && (c >= 176)) c++; // Handle 'classic' charset behavior @@ -1032,21 +1090,21 @@ void Adafruit_GFX::drawChar(int16_t x, int16_t y, unsigned char c, uint8_t line = pgm_read_byte(&font[c * 5 + i]); for(int8_t j=0; j<8; j++, line >>= 1) { if(line & 1) { - if(size == 1) + if(size_x == 1 && size_y == 1) writePixel(x+i, y+j, color); else - writeFillRect(x+i*size, y+j*size, size, size, color); + writeFillRect(x+i*size_x, y+j*size_y, size_x, size_y, color); } else if(bg != color) { - if(size == 1) + if(size_x == 1 && size_y == 1) writePixel(x+i, y+j, bg); else - writeFillRect(x+i*size, y+j*size, size, size, bg); + writeFillRect(x+i*size_x, y+j*size_y, size_x, size_y, bg); } } } if(bg != color) { // If opaque, draw vertical line for last column - if(size == 1) writeFastVLine(x+5, y, 8, bg); - else writeFillRect(x+5*size, y, size, 8*size, bg); + if(size_x == 1 && size_y == 1) writeFastVLine(x+5, y, 8, bg); + else writeFillRect(x+5*size_x, y, size_x, 8*size_y, bg); } endWrite(); @@ -1057,8 +1115,8 @@ void Adafruit_GFX::drawChar(int16_t x, int16_t y, unsigned char c, // drawChar() directly with 'bad' characters of font may cause mayhem! c -= (uint8_t)pgm_read_byte(&gfxFont->first); - GFXglyph *glyph = &(((GFXglyph *)pgm_read_pointer(&gfxFont->glyph))[c]); - uint8_t *bitmap = (uint8_t *)pgm_read_pointer(&gfxFont->bitmap); + GFXglyph *glyph = pgm_read_glyph_ptr(gfxFont, c); + uint8_t *bitmap = pgm_read_bitmap_ptr(gfxFont); uint16_t bo = pgm_read_word(&glyph->bitmapOffset); uint8_t w = pgm_read_byte(&glyph->width), @@ -1068,7 +1126,7 @@ void Adafruit_GFX::drawChar(int16_t x, int16_t y, unsigned char c, uint8_t xx, yy, bits = 0, bit = 0; int16_t xo16 = 0, yo16 = 0; - if(size > 1) { + if(size_x > 1 || size_y > 1) { xo16 = xo; yo16 = yo; } @@ -1098,11 +1156,11 @@ void Adafruit_GFX::drawChar(int16_t x, int16_t y, unsigned char c, bits = pgm_read_byte(&bitmap[bo++]); } if(bits & 0x80) { - if(size == 1) { + if(size_x == 1 && size_y == 1) { writePixel(x+xo+xx, y+yo+yy, color); } else { - writeFillRect(x+(xo16+xx)*size, y+(yo16+yy)*size, - size, size, color); + writeFillRect(x+(xo16+xx)*size_x, y+(yo16+yy)*size_y, + size_x, size_y, color); } } bits <<= 1; @@ -1123,39 +1181,38 @@ size_t Adafruit_GFX::write(uint8_t c) { if(c == '\n') { // Newline? cursor_x = 0; // Reset x to zero, - cursor_y += textsize * 8; // advance y one line + cursor_y += textsize_y * 8; // advance y one line } else if(c != '\r') { // Ignore carriage returns - if(wrap && ((cursor_x + textsize * 6) > _width)) { // Off right? + if(wrap && ((cursor_x + textsize_x * 6) > _width)) { // Off right? cursor_x = 0; // Reset x to zero, - cursor_y += textsize * 8; // advance y one line + cursor_y += textsize_y * 8; // advance y one line } - drawChar(cursor_x, cursor_y, c, textcolor, textbgcolor, textsize); - cursor_x += textsize * 6; // Advance x one char + drawChar(cursor_x, cursor_y, c, textcolor, textbgcolor, textsize_x, textsize_y); + cursor_x += textsize_x * 6; // Advance x one char } } else { // Custom font if(c == '\n') { cursor_x = 0; - cursor_y += (int16_t)textsize * + cursor_y += (int16_t)textsize_y * (uint8_t)pgm_read_byte(&gfxFont->yAdvance); } else if(c != '\r') { uint8_t first = pgm_read_byte(&gfxFont->first); if((c >= first) && (c <= (uint8_t)pgm_read_byte(&gfxFont->last))) { - GFXglyph *glyph = &(((GFXglyph *)pgm_read_pointer( - &gfxFont->glyph))[c - first]); + GFXglyph *glyph = pgm_read_glyph_ptr(gfxFont, c - first); uint8_t w = pgm_read_byte(&glyph->width), h = pgm_read_byte(&glyph->height); if((w > 0) && (h > 0)) { // Is there an associated bitmap? int16_t xo = (int8_t)pgm_read_byte(&glyph->xOffset); // sic - if(wrap && ((cursor_x + textsize * (xo + w)) > _width)) { + if(wrap && ((cursor_x + textsize_x * (xo + w)) > _width)) { cursor_x = 0; - cursor_y += (int16_t)textsize * + cursor_y += (int16_t)textsize_y * (uint8_t)pgm_read_byte(&gfxFont->yAdvance); } - drawChar(cursor_x, cursor_y, c, textcolor, textbgcolor, textsize); + drawChar(cursor_x, cursor_y, c, textcolor, textbgcolor, textsize_x, textsize_y); } - cursor_x += (uint8_t)pgm_read_byte(&glyph->xAdvance) * (int16_t)textsize; + cursor_x += (uint8_t)pgm_read_byte(&glyph->xAdvance) * (int16_t)textsize_x; } } @@ -1163,37 +1220,6 @@ size_t Adafruit_GFX::write(uint8_t c) { return 1; } -/**************************************************************************/ -/*! - @brief Set text cursor location - @param x X coordinate in pixels - @param y Y coordinate in pixels -*/ -/**************************************************************************/ -void Adafruit_GFX::setCursor(int16_t x, int16_t y) { - cursor_x = x; - cursor_y = y; -} - -/**************************************************************************/ -/*! - @brief Get text cursor X location - @returns X coordinate in pixels -*/ -/**************************************************************************/ -int16_t Adafruit_GFX::getCursorX(void) const { - return cursor_x; -} - -/**************************************************************************/ -/*! - @brief Get text cursor Y location - @returns Y coordinate in pixels -*/ -/**************************************************************************/ -int16_t Adafruit_GFX::getCursorY(void) const { - return cursor_y; -} /**************************************************************************/ /*! @@ -1202,51 +1228,19 @@ int16_t Adafruit_GFX::getCursorY(void) const { */ /**************************************************************************/ void Adafruit_GFX::setTextSize(uint8_t s) { - textsize = (s > 0) ? s : 1; + setTextSize(s, s); } /**************************************************************************/ /*! - @brief Set text font color with transparant background - @param c 16-bit 5-6-5 Color to draw text with + @brief Set text 'magnification' size. Each increase in s makes 1 pixel that much bigger. + @param s_x Desired text width magnification level in X-axis. 1 is default + @param s_y Desired text width magnification level in Y-axis. 1 is default */ /**************************************************************************/ -void Adafruit_GFX::setTextColor(uint16_t c) { - // For 'transparent' background, we'll set the bg - // to the same as fg instead of using a flag - textcolor = textbgcolor = c; -} - -/**************************************************************************/ -/*! - @brief Set text font color with custom background color - @param c 16-bit 5-6-5 Color to draw text with - @param b 16-bit 5-6-5 Color to draw background/fill with -*/ -/**************************************************************************/ -void Adafruit_GFX::setTextColor(uint16_t c, uint16_t b) { - textcolor = c; - textbgcolor = b; -} - -/**************************************************************************/ -/*! - @brief Whether text that is too long should 'wrap' around to the next line. - @param w Set true for wrapping, false for clipping -*/ -/**************************************************************************/ -void Adafruit_GFX::setTextWrap(boolean w) { - wrap = w; -} - -/**************************************************************************/ -/*! - @brief Get rotation setting for display - @returns 0 thru 3 corresponding to 4 cardinal rotations -*/ -/**************************************************************************/ -uint8_t Adafruit_GFX::getRotation(void) const { - return rotation; +void Adafruit_GFX::setTextSize(uint8_t s_x, uint8_t s_y) { + textsize_x = (s_x > 0) ? s_x : 1; + textsize_y = (s_y > 0) ? s_y : 1; } /**************************************************************************/ @@ -1271,22 +1265,6 @@ void Adafruit_GFX::setRotation(uint8_t x) { } } -/**************************************************************************/ -/*! - @brief Enable (or disable) Code Page 437-compatible charset. - There was an error in glcdfont.c for the longest time -- one character - (#176, the 'light shade' block) was missing -- this threw off the index - of every character that followed it. But a TON of code has been written - with the erroneous character indices. By default, the library uses the - original 'wrong' behavior and old sketches will still work. Pass 'true' - to this function to use correct CP437 character values in your code. - @param x Whether to enable (True) or not (False) -*/ -/**************************************************************************/ -void Adafruit_GFX::cp437(boolean x) { - _cp437 = x; -} - /**************************************************************************/ /*! @brief Set the font to display when print()ing, either custom or default @@ -1329,32 +1307,32 @@ void Adafruit_GFX::charBounds(char c, int16_t *x, int16_t *y, if(c == '\n') { // Newline? *x = 0; // Reset x to zero, advance y by one line - *y += textsize * (uint8_t)pgm_read_byte(&gfxFont->yAdvance); + *y += textsize_y * (uint8_t)pgm_read_byte(&gfxFont->yAdvance); } else if(c != '\r') { // Not a carriage return; is normal char uint8_t first = pgm_read_byte(&gfxFont->first), last = pgm_read_byte(&gfxFont->last); if((c >= first) && (c <= last)) { // Char present in this font? - GFXglyph *glyph = &(((GFXglyph *)pgm_read_pointer( - &gfxFont->glyph))[c - first]); + GFXglyph *glyph = pgm_read_glyph_ptr(gfxFont, c - first); uint8_t gw = pgm_read_byte(&glyph->width), gh = pgm_read_byte(&glyph->height), xa = pgm_read_byte(&glyph->xAdvance); int8_t xo = pgm_read_byte(&glyph->xOffset), yo = pgm_read_byte(&glyph->yOffset); - if(wrap && ((*x+(((int16_t)xo+gw)*textsize)) > _width)) { + if(wrap && ((*x+(((int16_t)xo+gw)*textsize_x)) > _width)) { *x = 0; // Reset x to zero, advance y by one line - *y += textsize * (uint8_t)pgm_read_byte(&gfxFont->yAdvance); + *y += textsize_y * (uint8_t)pgm_read_byte(&gfxFont->yAdvance); } - int16_t ts = (int16_t)textsize, - x1 = *x + xo * ts, - y1 = *y + yo * ts, - x2 = x1 + gw * ts - 1, - y2 = y1 + gh * ts - 1; + int16_t tsx = (int16_t)textsize_x, + tsy = (int16_t)textsize_y, + x1 = *x + xo * tsx, + y1 = *y + yo * tsy, + x2 = x1 + gw * tsx - 1, + y2 = y1 + gh * tsy - 1; if(x1 < *minx) *minx = x1; if(y1 < *miny) *miny = y1; if(x2 > *maxx) *maxx = x2; if(y2 > *maxy) *maxy = y2; - *x += xa * ts; + *x += xa * tsx; } } @@ -1362,20 +1340,20 @@ void Adafruit_GFX::charBounds(char c, int16_t *x, int16_t *y, if(c == '\n') { // Newline? *x = 0; // Reset x to zero, - *y += textsize * 8; // advance y one line + *y += textsize_y * 8; // advance y one line // min/max x/y unchaged -- that waits for next 'normal' character } else if(c != '\r') { // Normal char; ignore carriage returns - if(wrap && ((*x + textsize * 6) > _width)) { // Off right? + if(wrap && ((*x + textsize_x * 6) > _width)) { // Off right? *x = 0; // Reset x to zero, - *y += textsize * 8; // advance y one line + *y += textsize_y * 8; // advance y one line } - int x2 = *x + textsize * 6 - 1, // Lower-right pixel of char - y2 = *y + textsize * 8 - 1; + int x2 = *x + textsize_x * 6 - 1, // Lower-right pixel of char + y2 = *y + textsize_y * 8 - 1; if(x2 > *maxx) *maxx = x2; // Track max x, y if(y2 > *maxy) *maxy = y2; if(*x < *minx) *minx = *x; // Track min x, y if(*y < *miny) *miny = *y; - *x += textsize * 6; // Advance x one char + *x += textsize_x * 6; // Advance x one char } } } @@ -1470,26 +1448,6 @@ void Adafruit_GFX::getTextBounds(const __FlashStringHelper *str, } } -/**************************************************************************/ -/*! - @brief Get width of the display, accounting for the current rotation - @returns Width in pixels -*/ -/**************************************************************************/ -int16_t Adafruit_GFX::width(void) const { - return _width; -} - -/**************************************************************************/ -/*! - @brief Get height of the display, accounting for the current rotation - @returns Height in pixels -*/ -/**************************************************************************/ -int16_t Adafruit_GFX::height(void) const { - return _height; -} - /**************************************************************************/ /*! @brief Invert the display (ideally using built-in hardware command) @@ -1537,6 +1495,33 @@ void Adafruit_GFX_Button::initButton( textcolor, label, textsize); } +/**************************************************************************/ +/*! + @brief Initialize button with our desired color/size/settings + @param gfx Pointer to our display so we can draw to it! + @param x The X coordinate of the center of the button + @param y The Y coordinate of the center of the button + @param w Width of the buttton + @param h Height of the buttton + @param outline Color of the outline (16-bit 5-6-5 standard) + @param fill Color of the button fill (16-bit 5-6-5 standard) + @param textcolor Color of the button label (16-bit 5-6-5 standard) + @param label Ascii string of the text inside the button + @param textsize_x The font magnification in X-axis of the label text + @param textsize_y The font magnification in Y-axis of the label text +*/ +/**************************************************************************/ +// Classic initButton() function: pass center & size +void Adafruit_GFX_Button::initButton( + Adafruit_GFX *gfx, int16_t x, int16_t y, uint16_t w, uint16_t h, + uint16_t outline, uint16_t fill, uint16_t textcolor, + char *label, uint8_t textsize_x, uint8_t textsize_y) +{ + // Tweak arguments and pass to the newer initButtonUL() function... + initButtonUL(gfx, x - (w / 2), y - (h / 2), w, h, outline, fill, + textcolor, label, textsize_x, textsize_y); +} + /**************************************************************************/ /*! @brief Initialize button with our desired color/size/settings, with upper-left coordinates @@ -1556,6 +1541,30 @@ void Adafruit_GFX_Button::initButtonUL( Adafruit_GFX *gfx, int16_t x1, int16_t y1, uint16_t w, uint16_t h, uint16_t outline, uint16_t fill, uint16_t textcolor, char *label, uint8_t textsize) +{ + initButtonUL(gfx, x1, y1, w, h, outline, fill, textcolor, label, textsize, textsize); +} + +/**************************************************************************/ +/*! + @brief Initialize button with our desired color/size/settings, with upper-left coordinates + @param gfx Pointer to our display so we can draw to it! + @param x1 The X coordinate of the Upper-Left corner of the button + @param y1 The Y coordinate of the Upper-Left corner of the button + @param w Width of the buttton + @param h Height of the buttton + @param outline Color of the outline (16-bit 5-6-5 standard) + @param fill Color of the button fill (16-bit 5-6-5 standard) + @param textcolor Color of the button label (16-bit 5-6-5 standard) + @param label Ascii string of the text inside the button + @param textsize_x The font magnification in X-axis of the label text + @param textsize_y The font magnification in Y-axis of the label text +*/ +/**************************************************************************/ +void Adafruit_GFX_Button::initButtonUL( + Adafruit_GFX *gfx, int16_t x1, int16_t y1, uint16_t w, uint16_t h, + uint16_t outline, uint16_t fill, uint16_t textcolor, + char *label, uint8_t textsize_x, uint8_t textsize_y) { _x1 = x1; _y1 = y1; @@ -1564,7 +1573,8 @@ void Adafruit_GFX_Button::initButtonUL( _outlinecolor = outline; _fillcolor = fill; _textcolor = textcolor; - _textsize = textsize; + _textsize_x = textsize_x; + _textsize_y = textsize_y; _gfx = gfx; strncpy(_label, label, 9); } @@ -1592,19 +1602,20 @@ void Adafruit_GFX_Button::drawButton(boolean inverted) { _gfx->fillRoundRect(_x1, _y1, _w, _h, r, fill); _gfx->drawRoundRect(_x1, _y1, _w, _h, r, outline); - _gfx->setCursor(_x1 + (_w/2) - (strlen(_label) * 3 * _textsize), - _y1 + (_h/2) - (4 * _textsize)); + _gfx->setCursor(_x1 + (_w/2) - (strlen(_label) * 3 * _textsize_x), + _y1 + (_h/2) - (4 * _textsize_y)); _gfx->setTextColor(text); - _gfx->setTextSize(_textsize); + _gfx->setTextSize(_textsize_x, _textsize_y); _gfx->print(_label); + } /**************************************************************************/ /*! - @brief Helper to let us know if a coordinate is within the bounds of the button + @brief Helper to let us know if a coordinate is within the bounds of the button @param x The X coordinate to check @param y The Y coordinate to check - @returns True if within button graphics outline + @returns True if within button graphics outline */ /**************************************************************************/ boolean Adafruit_GFX_Button::contains(int16_t x, int16_t y) { @@ -1612,25 +1623,6 @@ boolean Adafruit_GFX_Button::contains(int16_t x, int16_t y) { (y >= _y1) && (y < (int16_t) (_y1 + _h))); } -/**************************************************************************/ -/*! - @brief Sets the state of the button, should be done by some touch function - @param p True for pressed, false for not. -*/ -/**************************************************************************/ -void Adafruit_GFX_Button::press(boolean p) { - laststate = currstate; - currstate = p; -} - -/**************************************************************************/ -/*! - @brief Query whether the button is currently pressed - @returns True if pressed -*/ -/**************************************************************************/ -boolean Adafruit_GFX_Button::isPressed() { return currstate; } - /**************************************************************************/ /*! @brief Query whether the button was pressed since we last checked state @@ -1691,20 +1683,10 @@ GFXcanvas1::~GFXcanvas1(void) { /**************************************************************************/ /*! - @brief Get a pointer to the internal buffer memory - @returns A pointer to the allocated buffer -*/ -/**************************************************************************/ -uint8_t* GFXcanvas1::getBuffer(void) { - return buffer; -} - -/**************************************************************************/ -/*! - @brief Draw a pixel to the canvas framebuffer - @param x x coordinate - @param y y coordinate - @param color 16-bit 5-6-5 Color to fill with + @brief Draw a pixel to the canvas framebuffer + @param x x coordinate + @param y y coordinate + @param color 16-bit 5-6-5 Color to fill with */ /**************************************************************************/ void GFXcanvas1::drawPixel(int16_t x, int16_t y, uint16_t color) { @@ -1749,8 +1731,8 @@ void GFXcanvas1::drawPixel(int16_t x, int16_t y, uint16_t color) { /**************************************************************************/ /*! - @brief Fill the framebuffer completely with one color - @param color 16-bit 5-6-5 Color to fill with + @brief Fill the framebuffer completely with one color + @param color 16-bit 5-6-5 Color to fill with */ /**************************************************************************/ void GFXcanvas1::fillScreen(uint16_t color) { @@ -1783,23 +1765,12 @@ GFXcanvas8::~GFXcanvas8(void) { if(buffer) free(buffer); } - /**************************************************************************/ /*! - @brief Get a pointer to the internal buffer memory - @returns A pointer to the allocated buffer -*/ -/**************************************************************************/ -uint8_t* GFXcanvas8::getBuffer(void) { - return buffer; -} - -/**************************************************************************/ -/*! - @brief Draw a pixel to the canvas framebuffer - @param x x coordinate - @param y y coordinate - @param color 16-bit 5-6-5 Color to fill with + @brief Draw a pixel to the canvas framebuffer + @param x x coordinate + @param y y coordinate + @param color 16-bit 5-6-5 Color to fill with */ /**************************************************************************/ void GFXcanvas8::drawPixel(int16_t x, int16_t y, uint16_t color) { @@ -1830,8 +1801,8 @@ void GFXcanvas8::drawPixel(int16_t x, int16_t y, uint16_t color) { /**************************************************************************/ /*! - @brief Fill the framebuffer completely with one color - @param color 16-bit 5-6-5 Color to fill with + @brief Fill the framebuffer completely with one color + @param color 16-bit 5-6-5 Color to fill with */ /**************************************************************************/ void GFXcanvas8::fillScreen(uint16_t color) { @@ -1900,20 +1871,10 @@ GFXcanvas16::~GFXcanvas16(void) { /**************************************************************************/ /*! - @brief Get a pointer to the internal buffer memory - @returns A pointer to the allocated buffer -*/ -/**************************************************************************/ -uint16_t* GFXcanvas16::getBuffer(void) { - return buffer; -} - -/**************************************************************************/ -/*! - @brief Draw a pixel to the canvas framebuffer - @param x x coordinate - @param y y coordinate - @param color 16-bit 5-6-5 Color to fill with + @brief Draw a pixel to the canvas framebuffer + @param x x coordinate + @param y y coordinate + @param color 16-bit 5-6-5 Color to fill with */ /**************************************************************************/ void GFXcanvas16::drawPixel(int16_t x, int16_t y, uint16_t color) { @@ -1944,8 +1905,8 @@ void GFXcanvas16::drawPixel(int16_t x, int16_t y, uint16_t color) { /**************************************************************************/ /*! - @brief Fill the framebuffer completely with one color - @param color 16-bit 5-6-5 Color to fill with + @brief Fill the framebuffer completely with one color + @param color 16-bit 5-6-5 Color to fill with */ /**************************************************************************/ void GFXcanvas16::fillScreen(uint16_t color) { @@ -1960,3 +1921,22 @@ void GFXcanvas16::fillScreen(uint16_t color) { } } +/**************************************************************************/ +/*! + @brief Reverses the "endian-ness" of each 16-bit pixel within the + canvas; little-endian to big-endian, or big-endian to little. + Most microcontrollers (such as SAMD) are little-endian, while + most displays tend toward big-endianness. All the drawing + functions (including RGB bitmap drawing) take care of this + automatically, but some specialized code (usually involving + DMA) can benefit from having pixel data already in the + display-native order. Note that this does NOT convert to a + SPECIFIC endian-ness, it just flips the bytes within each word. +*/ +/**************************************************************************/ +void GFXcanvas16::byteSwap(void) { + if(buffer) { + uint32_t i, pixels = WIDTH * HEIGHT; + for(i=0; i= 100 virtual size_t write(uint8_t); #else virtual void write(uint8_t); #endif + size_t iwrite(uint8_t); + /************************************************************************/ + /*! + @brief Get width of the display, accounting for current rotation + @returns Width in pixels + */ + /************************************************************************/ + int16_t width(void) const { return _width; }; - int16_t height(void) const; - int16_t width(void) const; + /************************************************************************/ + /*! + @brief Get height of the display, accounting for current rotation + @returns Height in pixels + */ + /************************************************************************/ + int16_t height(void) const { return _height; } - uint8_t getRotation(void) const; + /************************************************************************/ + /*! + @brief Get rotation setting for display + @returns 0 thru 3 corresponding to 4 cardinal rotations + */ + /************************************************************************/ + uint8_t getRotation(void) const { return rotation; } - // get current cursor position (get rotation safe maximum values, using: width() for x, height() for y) - int16_t getCursorX(void) const; - int16_t getCursorY(void) const; + // get current cursor position (get rotation safe maximum values, + // using: width() for x, height() for y) + /************************************************************************/ + /*! + @brief Get text cursor X location + @returns X coordinate in pixels + */ + /************************************************************************/ + int16_t getCursorX(void) const { return cursor_x; } + + /************************************************************************/ + /*! + @brief Get text cursor Y location + @returns Y coordinate in pixels + */ + /************************************************************************/ + int16_t getCursorY(void) const { return cursor_y; }; + + uint16_t + textcolor, ///< 16-bit background color for print() + textbgcolor; ///< 16-bit text color for print() protected: void charBounds(char c, int16_t *x, int16_t *y, int16_t *minx, int16_t *miny, int16_t *maxx, int16_t *maxy); - const int16_t + int16_t WIDTH, ///< This is the 'raw' display width - never changes HEIGHT; ///< This is the 'raw' display height - never changes int16_t @@ -137,11 +229,12 @@ class Adafruit_GFX : public Print { _height, ///< Display height as modified by current rotation cursor_x, ///< x location to start print()ing text cursor_y; ///< y location to start print()ing text - uint16_t - textcolor, ///< 16-bit background color for print() - textbgcolor; ///< 16-bit text color for print() + //uint16_t + // textcolor, ///< 16-bit background color for print() + // textbgcolor; ///< 16-bit text color for print() uint8_t - textsize, ///< Desired magnification of text to print() + textsize_x, ///< Desired magnification in X-axis of text to print() + textsize_y, ///< Desired magnification in Y-axis of text to print() rotation; ///< Display rotation (0 thru 3) boolean wrap, ///< If set, 'wrap' text at right edge of display @@ -160,23 +253,44 @@ class Adafruit_GFX_Button { void initButton(Adafruit_GFX *gfx, int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t outline, uint16_t fill, uint16_t textcolor, char *label, uint8_t textsize); + void initButton(Adafruit_GFX *gfx, int16_t x, int16_t y, + uint16_t w, uint16_t h, uint16_t outline, uint16_t fill, + uint16_t textcolor, char *label, uint8_t textsize_x, uint8_t textsize_y); // New/alt initButton() uses upper-left corner & size void initButtonUL(Adafruit_GFX *gfx, int16_t x1, int16_t y1, uint16_t w, uint16_t h, uint16_t outline, uint16_t fill, uint16_t textcolor, char *label, uint8_t textsize); + void initButtonUL(Adafruit_GFX *gfx, int16_t x1, int16_t y1, + uint16_t w, uint16_t h, uint16_t outline, uint16_t fill, + uint16_t textcolor, char *label, uint8_t textsize_x, uint8_t textsize_y); void drawButton(boolean inverted = false); boolean contains(int16_t x, int16_t y); - void press(boolean p); - boolean isPressed(); + /**********************************************************************/ + /*! + @brief Sets button state, should be done by some touch function + @param p True for pressed, false for not. + */ + /**********************************************************************/ + void press(boolean p) { laststate = currstate; currstate = p; } + boolean justPressed(); boolean justReleased(); + /**********************************************************************/ + /*! + @brief Query whether the button is currently pressed + @returns True if pressed + */ + /**********************************************************************/ + boolean isPressed(void) { return currstate; }; + private: Adafruit_GFX *_gfx; int16_t _x1, _y1; // Coordinates of top-left corner uint16_t _w, _h; - uint8_t _textsize; + uint8_t _textsize_x; + uint8_t _textsize_y; uint16_t _outlinecolor, _fillcolor, _textcolor; char _label[10]; @@ -191,7 +305,13 @@ class GFXcanvas1 : public Adafruit_GFX { ~GFXcanvas1(void); void drawPixel(int16_t x, int16_t y, uint16_t color), fillScreen(uint16_t color); - uint8_t *getBuffer(void); + /**********************************************************************/ + /*! + @brief Get a pointer to the internal buffer memory + @returns A pointer to the allocated buffer + */ + /**********************************************************************/ + uint8_t *getBuffer(void) const { return buffer; } private: uint8_t *buffer; }; @@ -205,8 +325,13 @@ class GFXcanvas8 : public Adafruit_GFX { void drawPixel(int16_t x, int16_t y, uint16_t color), fillScreen(uint16_t color), writeFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color); - - uint8_t *getBuffer(void); + /**********************************************************************/ + /*! + @brief Get a pointer to the internal buffer memory + @returns A pointer to the allocated buffer + */ + /**********************************************************************/ + uint8_t *getBuffer(void) const { return buffer; } private: uint8_t *buffer; }; @@ -218,8 +343,15 @@ class GFXcanvas16 : public Adafruit_GFX { GFXcanvas16(uint16_t w, uint16_t h); ~GFXcanvas16(void); void drawPixel(int16_t x, int16_t y, uint16_t color), - fillScreen(uint16_t color); - uint16_t *getBuffer(void); + fillScreen(uint16_t color), + byteSwap(void); + /**********************************************************************/ + /*! + @brief Get a pointer to the internal buffer memory + @returns A pointer to the allocated buffer + */ + /**********************************************************************/ + uint16_t *getBuffer(void) const { return buffer; } private: uint16_t *buffer; }; diff --git a/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Adafruit_SPITFT.cpp b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Adafruit_SPITFT.cpp new file mode 100644 index 000000000..945ef41ab --- /dev/null +++ b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Adafruit_SPITFT.cpp @@ -0,0 +1,2217 @@ +/*! + * @file Adafruit_SPITFT.cpp + * + * @mainpage Adafruit SPI TFT Displays (and some others) + * + * @section intro_sec Introduction + * + * Part of Adafruit's GFX graphics library. Originally this class was + * written to handle a range of color TFT displays connected via SPI, + * but over time this library and some display-specific subclasses have + * mutated to include some color OLEDs as well as parallel-interfaced + * displays. The name's been kept for the sake of older code. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + + * @section dependencies Dependencies + * + * This library depends on + * Adafruit_GFX being present on your system. Please make sure you have + * installed the latest version before using this library. + * + * @section author Author + * + * Written by Limor "ladyada" Fried for Adafruit Industries, + * with contributions from the open source community. + * + * @section license License + * + * BSD license, all text here must be included in any redistribution. + */ + +#if !defined(__AVR_ATtiny85__) // Not for ATtiny, at all + +#include "Adafruit_SPITFT.h" + +#if defined(__AVR__) +#if defined(__AVR_XMEGA__) //only tested with __AVR_ATmega4809__ +#define AVR_WRITESPI(x) for(SPI0_DATA = (x); (!(SPI0_INTFLAGS & _BV(SPI_IF_bp))); ) +#else +#define AVR_WRITESPI(x) for(SPDR = (x); (!(SPSR & _BV(SPIF))); ) +#endif +#endif + +#if defined(PORT_IOBUS) +// On SAMD21, redefine digitalPinToPort() to use the slightly-faster +// PORT_IOBUS rather than PORT (not needed on SAMD51). +#undef digitalPinToPort +#define digitalPinToPort(P) (&(PORT_IOBUS->Group[g_APinDescription[P].ulPort])) +#endif // end PORT_IOBUS + +#if defined(USE_SPI_DMA) + #include + #include "wiring_private.h" // pinPeripheral() function + #include // memalign() function + #define tcNum 2 // Timer/Counter for parallel write strobe PWM + #define wrPeripheral PIO_CCL // Use CCL to invert write strobe + + // DMA transfer-in-progress indicator and callback + static volatile bool dma_busy = false; + static void dma_callback(Adafruit_ZeroDMA *dma) { + dma_busy = false; + } + + #if defined(__SAMD51__) + // Timer/counter info by index # + static const struct { + Tc *tc; // -> Timer/Counter base address + int gclk; // GCLK ID + int evu; // EVSYS user ID + } tcList[] = { + { TC0, TC0_GCLK_ID, EVSYS_ID_USER_TC0_EVU }, + { TC1, TC1_GCLK_ID, EVSYS_ID_USER_TC1_EVU }, + { TC2, TC2_GCLK_ID, EVSYS_ID_USER_TC2_EVU }, + { TC3, TC3_GCLK_ID, EVSYS_ID_USER_TC3_EVU }, + #if defined(TC4) + { TC4, TC4_GCLK_ID, EVSYS_ID_USER_TC4_EVU }, + #endif + #if defined(TC5) + { TC5, TC5_GCLK_ID, EVSYS_ID_USER_TC5_EVU }, + #endif + #if defined(TC6) + { TC6, TC6_GCLK_ID, EVSYS_ID_USER_TC6_EVU }, + #endif + #if defined(TC7) + { TC7, TC7_GCLK_ID, EVSYS_ID_USER_TC7_EVU } + #endif + }; + #define NUM_TIMERS (sizeof tcList / sizeof tcList[0]) ///< # timer/counters + #endif // end __SAMD51__ + +#endif // end USE_SPI_DMA + +// Possible values for Adafruit_SPITFT.connection: +#define TFT_HARD_SPI 0 ///< Display interface = hardware SPI +#define TFT_SOFT_SPI 1 ///< Display interface = software SPI +#define TFT_PARALLEL 2 ///< Display interface = 8- or 16-bit parallel + + +// CONSTRUCTORS ------------------------------------------------------------ + +/*! + @brief Adafruit_SPITFT constructor for software (bitbang) SPI. + @param w Display width in pixels at default rotation setting (0). + @param h Display height in pixels at default rotation setting (0). + @param cs Arduino pin # for chip-select (-1 if unused, tie CS low). + @param dc Arduino pin # for data/command select (required). + @param mosi Arduino pin # for bitbang SPI MOSI signal (required). + @param sck Arduino pin # for bitbang SPI SCK signal (required). + @param rst Arduino pin # for display reset (optional, display reset + can be tied to MCU reset, default of -1 means unused). + @param miso Arduino pin # for bitbang SPI MISO signal (optional, + -1 default, many displays don't support SPI read). + @return Adafruit_SPITFT object. + @note Output pins are not initialized; application typically will + need to call subclass' begin() function, which in turn calls + this library's initSPI() function to initialize pins. +*/ +Adafruit_SPITFT::Adafruit_SPITFT(uint16_t w, uint16_t h, + int8_t cs, int8_t dc, int8_t mosi, int8_t sck, int8_t rst, int8_t miso) : + Adafruit_GFX(w, h), connection(TFT_SOFT_SPI), _rst(rst), _cs(cs), _dc(dc) { + swspi._sck = sck; + swspi._mosi = mosi; + swspi._miso = miso; +#if defined(USE_FAST_PINIO) + #if defined(HAS_PORT_SET_CLR) + #if defined(CORE_TEENSY) + #if !defined(KINETISK) + dcPinMask = digitalPinToBitMask(dc); + swspi.sckPinMask = digitalPinToBitMask(sck); + swspi.mosiPinMask = digitalPinToBitMask(mosi); + #endif + dcPortSet = portSetRegister(dc); + dcPortClr = portClearRegister(dc); + swspi.sckPortSet = portSetRegister(sck); + swspi.sckPortClr = portClearRegister(sck); + swspi.mosiPortSet = portSetRegister(mosi); + swspi.mosiPortClr = portClearRegister(mosi); + if(cs >= 0) { + #if !defined(KINETISK) + csPinMask = digitalPinToBitMask(cs); + #endif + csPortSet = portSetRegister(cs); + csPortClr = portClearRegister(cs); + } else { + #if !defined(KINETISK) + csPinMask = 0; + #endif + csPortSet = dcPortSet; + csPortClr = dcPortClr; + } + if(miso >= 0) { + swspi.misoPort = portInputRegister(miso); + #if !defined(KINETISK) + swspi.misoPinMask = digitalPinToBitMask(miso); + #endif + } else { + swspi.misoPort = portInputRegister(dc); + } + #else // !CORE_TEENSY + dcPinMask =digitalPinToBitMask(dc); + swspi.sckPinMask =digitalPinToBitMask(sck); + swspi.mosiPinMask=digitalPinToBitMask(mosi); + dcPortSet =&(PORT->Group[g_APinDescription[dc].ulPort].OUTSET.reg); + dcPortClr =&(PORT->Group[g_APinDescription[dc].ulPort].OUTCLR.reg); + swspi.sckPortSet =&(PORT->Group[g_APinDescription[sck].ulPort].OUTSET.reg); + swspi.sckPortClr =&(PORT->Group[g_APinDescription[sck].ulPort].OUTCLR.reg); + swspi.mosiPortSet=&(PORT->Group[g_APinDescription[mosi].ulPort].OUTSET.reg); + swspi.mosiPortClr=&(PORT->Group[g_APinDescription[mosi].ulPort].OUTCLR.reg); + if(cs >= 0) { + csPinMask = digitalPinToBitMask(cs); + csPortSet = &(PORT->Group[g_APinDescription[cs].ulPort].OUTSET.reg); + csPortClr = &(PORT->Group[g_APinDescription[cs].ulPort].OUTCLR.reg); + } else { + // No chip-select line defined; might be permanently tied to GND. + // Assign a valid GPIO register (though not used for CS), and an + // empty pin bitmask...the nonsense bit-twiddling might be faster + // than checking _cs and possibly branching. + csPortSet = dcPortSet; + csPortClr = dcPortClr; + csPinMask = 0; + } + if(miso >= 0) { + swspi.misoPinMask=digitalPinToBitMask(miso); + swspi.misoPort =(PORTreg_t)portInputRegister(digitalPinToPort(miso)); + } else { + swspi.misoPinMask=0; + swspi.misoPort =(PORTreg_t)portInputRegister(digitalPinToPort(dc)); + } + #endif // end !CORE_TEENSY + #else // !HAS_PORT_SET_CLR + dcPort =(PORTreg_t)portOutputRegister(digitalPinToPort(dc)); + dcPinMaskSet =digitalPinToBitMask(dc); + swspi.sckPort =(PORTreg_t)portOutputRegister(digitalPinToPort(sck)); + swspi.sckPinMaskSet =digitalPinToBitMask(sck); + swspi.mosiPort =(PORTreg_t)portOutputRegister(digitalPinToPort(mosi)); + swspi.mosiPinMaskSet=digitalPinToBitMask(mosi); + if(cs >= 0) { + csPort = (PORTreg_t)portOutputRegister(digitalPinToPort(cs)); + csPinMaskSet = digitalPinToBitMask(cs); + } else { + // No chip-select line defined; might be permanently tied to GND. + // Assign a valid GPIO register (though not used for CS), and an + // empty pin bitmask...the nonsense bit-twiddling might be faster + // than checking _cs and possibly branching. + csPort = dcPort; + csPinMaskSet = 0; + } + if(miso >= 0) { + swspi.misoPort =(PORTreg_t)portInputRegister(digitalPinToPort(miso)); + swspi.misoPinMask=digitalPinToBitMask(miso); + } else { + swspi.misoPort =(PORTreg_t)portInputRegister(digitalPinToPort(dc)); + swspi.misoPinMask=0; + } + csPinMaskClr = ~csPinMaskSet; + dcPinMaskClr = ~dcPinMaskSet; + swspi.sckPinMaskClr = ~swspi.sckPinMaskSet; + swspi.mosiPinMaskClr = ~swspi.mosiPinMaskSet; + #endif // !end HAS_PORT_SET_CLR +#endif // end USE_FAST_PINIO +} + +/*! + @brief Adafruit_SPITFT constructor for hardware SPI using the board's + default SPI peripheral. + @param w Display width in pixels at default rotation setting (0). + @param h Display height in pixels at default rotation setting (0). + @param cs Arduino pin # for chip-select (-1 if unused, tie CS low). + @param dc Arduino pin # for data/command select (required). + @param rst Arduino pin # for display reset (optional, display reset + can be tied to MCU reset, default of -1 means unused). + @return Adafruit_SPITFT object. + @note Output pins are not initialized; application typically will + need to call subclass' begin() function, which in turn calls + this library's initSPI() function to initialize pins. +*/ +#if defined(ESP8266) // See notes below +Adafruit_SPITFT::Adafruit_SPITFT(uint16_t w, uint16_t h, int8_t cs, + int8_t dc, int8_t rst) : Adafruit_GFX(w, h), + connection(TFT_HARD_SPI), _rst(rst), _cs(cs), _dc(dc) { + hwspi._spi = &SPI; +} +#else // !ESP8266 +Adafruit_SPITFT::Adafruit_SPITFT(uint16_t w, uint16_t h, int8_t cs, + int8_t dc, int8_t rst) : Adafruit_SPITFT(w, h, &SPI, cs, dc, rst) { + // This just invokes the hardware SPI constructor below, + // passing the default SPI device (&SPI). +} +#endif // end !ESP8266 + +#if !defined(ESP8266) +// ESP8266 compiler freaks out at this constructor -- it can't disambiguate +// beteween the SPIClass pointer (argument #3) and a regular integer. +// Solution here it to just not offer this variant on the ESP8266. You can +// use the default hardware SPI peripheral, or you can use software SPI, +// but if there's any library out there that creates a 'virtual' SPIClass +// peripheral and drives it with software bitbanging, that's not supported. +/*! + @brief Adafruit_SPITFT constructor for hardware SPI using a specific + SPI peripheral. + @param w Display width in pixels at default rotation (0). + @param h Display height in pixels at default rotation (0). + @param spiClass Pointer to SPIClass type (e.g. &SPI or &SPI1). + @param cs Arduino pin # for chip-select (-1 if unused, tie CS low). + @param dc Arduino pin # for data/command select (required). + @param rst Arduino pin # for display reset (optional, display reset + can be tied to MCU reset, default of -1 means unused). + @return Adafruit_SPITFT object. + @note Output pins are not initialized in constructor; application + typically will need to call subclass' begin() function, which + in turn calls this library's initSPI() function to initialize + pins. EXCEPT...if you have built your own SERCOM SPI peripheral + (calling the SPIClass constructor) rather than one of the + built-in SPI devices (e.g. &SPI, &SPI1 and so forth), you will + need to call the begin() function for your object as well as + pinPeripheral() for the MOSI, MISO and SCK pins to configure + GPIO manually. Do this BEFORE calling the display-specific + begin or init function. Unfortunate but unavoidable. +*/ +Adafruit_SPITFT::Adafruit_SPITFT(uint16_t w, uint16_t h, SPIClass *spiClass, + int8_t cs, int8_t dc, int8_t rst) : Adafruit_GFX(w, h), + connection(TFT_HARD_SPI), _rst(rst), _cs(cs), _dc(dc) { + hwspi._spi = spiClass; +#if defined(USE_FAST_PINIO) + #if defined(HAS_PORT_SET_CLR) + #if defined(CORE_TEENSY) + #if !defined(KINETISK) + dcPinMask = digitalPinToBitMask(dc); + #endif + dcPortSet = portSetRegister(dc); + dcPortClr = portClearRegister(dc); + if(cs >= 0) { + #if !defined(KINETISK) + csPinMask = digitalPinToBitMask(cs); + #endif + csPortSet = portSetRegister(cs); + csPortClr = portClearRegister(cs); + } else { // see comments below + #if !defined(KINETISK) + csPinMask = 0; + #endif + csPortSet = dcPortSet; + csPortClr = dcPortClr; + } + #else // !CORE_TEENSY + dcPinMask = digitalPinToBitMask(dc); + dcPortSet = &(PORT->Group[g_APinDescription[dc].ulPort].OUTSET.reg); + dcPortClr = &(PORT->Group[g_APinDescription[dc].ulPort].OUTCLR.reg); + if(cs >= 0) { + csPinMask = digitalPinToBitMask(cs); + csPortSet = &(PORT->Group[g_APinDescription[cs].ulPort].OUTSET.reg); + csPortClr = &(PORT->Group[g_APinDescription[cs].ulPort].OUTCLR.reg); + } else { + // No chip-select line defined; might be permanently tied to GND. + // Assign a valid GPIO register (though not used for CS), and an + // empty pin bitmask...the nonsense bit-twiddling might be faster + // than checking _cs and possibly branching. + csPortSet = dcPortSet; + csPortClr = dcPortClr; + csPinMask = 0; + } + #endif // end !CORE_TEENSY + #else // !HAS_PORT_SET_CLR + dcPort = (PORTreg_t)portOutputRegister(digitalPinToPort(dc)); + dcPinMaskSet = digitalPinToBitMask(dc); + if(cs >= 0) { + csPort = (PORTreg_t)portOutputRegister(digitalPinToPort(cs)); + csPinMaskSet = digitalPinToBitMask(cs); + } else { + // No chip-select line defined; might be permanently tied to GND. + // Assign a valid GPIO register (though not used for CS), and an + // empty pin bitmask...the nonsense bit-twiddling might be faster + // than checking _cs and possibly branching. + csPort = dcPort; + csPinMaskSet = 0; + } + csPinMaskClr = ~csPinMaskSet; + dcPinMaskClr = ~dcPinMaskSet; + #endif // end !HAS_PORT_SET_CLR +#endif // end USE_FAST_PINIO +} +#endif // end !ESP8266 + +/*! + @brief Adafruit_SPITFT constructor for parallel display connection. + @param w Display width in pixels at default rotation (0). + @param h Display height in pixels at default rotation (0). + @param busWidth If tft16 (enumeration in header file), is a 16-bit + parallel connection, else 8-bit. + 16-bit isn't fully implemented or tested yet so + applications should pass "tft8bitbus" for now...needed to + stick a required enum argument in there to + disambiguate this constructor from the soft-SPI case. + Argument is ignored on 8-bit architectures (no 'wide' + support there since PORTs are 8 bits anyway). + @param d0 Arduino pin # for data bit 0 (1+ are extrapolated). + The 8 (or 16) data bits MUST be contiguous and byte- + aligned (or word-aligned for wide interface) within + the same PORT register (might not correspond to + Arduino pin sequence). + @param wr Arduino pin # for write strobe (required). + @param dc Arduino pin # for data/command select (required). + @param cs Arduino pin # for chip-select (optional, -1 if unused, + tie CS low). + @param rst Arduino pin # for display reset (optional, display reset + can be tied to MCU reset, default of -1 means unused). + @param rd Arduino pin # for read strobe (optional, -1 if unused). + @return Adafruit_SPITFT object. + @note Output pins are not initialized; application typically will need + to call subclass' begin() function, which in turn calls this + library's initSPI() function to initialize pins. + Yes, the name is a misnomer...this library originally handled + only SPI displays, parallel being a recent addition (but not + wanting to break existing code). +*/ +Adafruit_SPITFT::Adafruit_SPITFT(uint16_t w, uint16_t h, tftBusWidth busWidth, + int8_t d0, int8_t wr, int8_t dc, int8_t cs, int8_t rst, int8_t rd) : + Adafruit_GFX(w, h), connection(TFT_PARALLEL), _rst(rst), _cs(cs), _dc(dc) { + tft8._d0 = d0; + tft8._wr = wr; + tft8._rd = rd; + tft8.wide = (busWidth == tft16bitbus); +#if defined(USE_FAST_PINIO) + #if defined(HAS_PORT_SET_CLR) + #if defined(CORE_TEENSY) + tft8.wrPortSet = portSetRegister(wr); + tft8.wrPortClr = portClearRegister(wr); + #if !defined(KINETISK) + dcPinMask = digitalPinToBitMask(dc); + #endif + dcPortSet = portSetRegister(dc); + dcPortClr = portClearRegister(dc); + if(cs >= 0) { + #if !defined(KINETISK) + csPinMask = digitalPinToBitMask(cs); + #endif + csPortSet = portSetRegister(cs); + csPortClr = portClearRegister(cs); + } else { // see comments below + #if !defined(KINETISK) + csPinMask = 0; + #endif + csPortSet = dcPortSet; + csPortClr = dcPortClr; + } + if(rd >= 0) { // if read-strobe pin specified... + #if defined(KINETISK) + tft8.rdPinMask = 1; + #else // !KINETISK + tft8.rdPinMask = digitalPinToBitMask(rd); + #endif + tft8.rdPortSet = portSetRegister(rd); + tft8.rdPortClr = portClearRegister(rd); + } else { + tft8.rdPinMask = 0; + tft8.rdPortSet = dcPortSet; + tft8.rdPortClr = dcPortClr; + } + // These are all uint8_t* pointers -- elsewhere they're recast + // as necessary if a 'wide' 16-bit interface is in use. + tft8.writePort = portOutputRegister(d0); + tft8.readPort = portInputRegister(d0); + tft8.dirSet = portModeRegister(d0); + tft8.dirClr = portModeRegister(d0); + #else // !CORE_TEENSY + tft8.wrPinMask = digitalPinToBitMask(wr); + tft8.wrPortSet = &(PORT->Group[g_APinDescription[wr].ulPort].OUTSET.reg); + tft8.wrPortClr = &(PORT->Group[g_APinDescription[wr].ulPort].OUTCLR.reg); + dcPinMask = digitalPinToBitMask(dc); + dcPortSet = &(PORT->Group[g_APinDescription[dc].ulPort].OUTSET.reg); + dcPortClr = &(PORT->Group[g_APinDescription[dc].ulPort].OUTCLR.reg); + if(cs >= 0) { + csPinMask = digitalPinToBitMask(cs); + csPortSet = &(PORT->Group[g_APinDescription[cs].ulPort].OUTSET.reg); + csPortClr = &(PORT->Group[g_APinDescription[cs].ulPort].OUTCLR.reg); + } else { + // No chip-select line defined; might be permanently tied to GND. + // Assign a valid GPIO register (though not used for CS), and an + // empty pin bitmask...the nonsense bit-twiddling might be faster + // than checking _cs and possibly branching. + csPortSet = dcPortSet; + csPortClr = dcPortClr; + csPinMask = 0; + } + if(rd >= 0) { // if read-strobe pin specified... + tft8.rdPinMask =digitalPinToBitMask(rd); + tft8.rdPortSet =&(PORT->Group[g_APinDescription[rd].ulPort].OUTSET.reg); + tft8.rdPortClr =&(PORT->Group[g_APinDescription[rd].ulPort].OUTCLR.reg); + } else { + tft8.rdPinMask = 0; + tft8.rdPortSet = dcPortSet; + tft8.rdPortClr = dcPortClr; + } + // Get pointers to PORT write/read/dir bytes within 32-bit PORT + uint8_t dBit = g_APinDescription[d0].ulPin; // d0 bit # in PORT + PortGroup *p = (&(PORT->Group[g_APinDescription[d0].ulPort])); + uint8_t offset = dBit / 8; // d[7:0] byte # within PORT + if(tft8.wide) offset &= ~1; // d[15:8] byte # within PORT + // These are all uint8_t* pointers -- elsewhere they're recast + // as necessary if a 'wide' 16-bit interface is in use. + tft8.writePort = (volatile uint8_t *)&(p->OUT.reg) + offset; + tft8.readPort = (volatile uint8_t *)&(p->IN.reg) + offset; + tft8.dirSet = (volatile uint8_t *)&(p->DIRSET.reg) + offset; + tft8.dirClr = (volatile uint8_t *)&(p->DIRCLR.reg) + offset; + #endif // end !CORE_TEENSY + #else // !HAS_PORT_SET_CLR + tft8.wrPort = (PORTreg_t)portOutputRegister(digitalPinToPort(wr)); + tft8.wrPinMaskSet = digitalPinToBitMask(wr); + dcPort = (PORTreg_t)portOutputRegister(digitalPinToPort(dc)); + dcPinMaskSet = digitalPinToBitMask(dc); + if(cs >= 0) { + csPort = (PORTreg_t)portOutputRegister(digitalPinToPort(cs)); + csPinMaskSet = digitalPinToBitMask(cs); + } else { + // No chip-select line defined; might be permanently tied to GND. + // Assign a valid GPIO register (though not used for CS), and an + // empty pin bitmask...the nonsense bit-twiddling might be faster + // than checking _cs and possibly branching. + csPort = dcPort; + csPinMaskSet = 0; + } + if(rd >= 0) { // if read-strobe pin specified... + tft8.rdPort =(PORTreg_t)portOutputRegister(digitalPinToPort(rd)); + tft8.rdPinMaskSet =digitalPinToBitMask(rd); + } else { + tft8.rdPort = dcPort; + tft8.rdPinMaskSet = 0; + } + csPinMaskClr = ~csPinMaskSet; + dcPinMaskClr = ~dcPinMaskSet; + tft8.wrPinMaskClr = ~tft8.wrPinMaskSet; + tft8.rdPinMaskClr = ~tft8.rdPinMaskSet; + tft8.writePort = (PORTreg_t)portOutputRegister(digitalPinToPort(d0)); + tft8.readPort = (PORTreg_t)portInputRegister(digitalPinToPort(d0)); + tft8.portDir = (PORTreg_t)portModeRegister(digitalPinToPort(d0)); + #endif // end !HAS_PORT_SET_CLR +#endif // end USE_FAST_PINIO +} + +// end constructors ------- + + +// CLASS MEMBER FUNCTIONS -------------------------------------------------- + +// begin() and setAddrWindow() MUST be declared by any subclass. + +/*! + @brief Configure microcontroller pins for TFT interfacing. Typically + called by a subclass' begin() function. + @param freq SPI frequency when using hardware SPI. If default (0) + is passed, will fall back on a device-specific value. + Value is ignored when using software SPI or parallel + connection. + @param spiMode SPI mode when using hardware SPI. MUST be one of the + values SPI_MODE0, SPI_MODE1, SPI_MODE2 or SPI_MODE3 + defined in SPI.h. Do NOT attempt to pass '0' for + SPI_MODE0 and so forth...the values are NOT the same! + Use ONLY the defines! (Pity it's not an enum.) + @note Another anachronistically-named function; this is called even + when the display connection is parallel (not SPI). Also, this + could probably be made private...quite a few class functions + were generously put in the public section. +*/ +void Adafruit_SPITFT::initSPI(uint32_t freq, uint8_t spiMode) { + + if(!freq) freq = DEFAULT_SPI_FREQ; // If no freq specified, use default + + // Init basic control pins common to all connection types + if(_cs >= 0) { + pinMode(_cs, OUTPUT); + digitalWrite(_cs, HIGH); // Deselect + } + pinMode(_dc, OUTPUT); + digitalWrite(_dc, HIGH); // Data mode + + if(connection == TFT_HARD_SPI) { + +#if defined(SPI_HAS_TRANSACTION) + hwspi.settings = SPISettings(freq, MSBFIRST, spiMode); +#else + hwspi._freq = freq; // Save freq value for later +#endif + hwspi._mode = spiMode; // Save spiMode value for later + // Call hwspi._spi->begin() ONLY if this is among the 'established' + // SPI interfaces in variant.h. For DIY roll-your-own SERCOM SPIs, + // begin() and pinPeripheral() calls MUST be made in one's calling + // code, BEFORE the screen-specific begin/init function is called. + // Reason for this is that SPI::begin() makes its own calls to + // pinPeripheral() based on g_APinDescription[n].ulPinType, which + // on non-established SPI interface pins will always be PIO_DIGITAL + // or similar, while we need PIO_SERCOM or PIO_SERCOM_ALT...it's + // highly unique between devices and variants for each pin or + // SERCOM so we can't make those calls ourselves here. And the SPI + // device needs to be set up before calling this because it's + // immediately followed with initialization commands. Blargh. + if( +#if !defined(SPI_INTERFACES_COUNT) + 1 +#endif +#if SPI_INTERFACES_COUNT > 0 + (hwspi._spi == &SPI) +#endif +#if SPI_INTERFACES_COUNT > 1 + || (hwspi._spi == &SPI1) +#endif +#if SPI_INTERFACES_COUNT > 2 + || (hwspi._spi == &SPI2) +#endif +#if SPI_INTERFACES_COUNT > 3 + || (hwspi._spi == &SPI3) +#endif +#if SPI_INTERFACES_COUNT > 4 + || (hwspi._spi == &SPI4) +#endif +#if SPI_INTERFACES_COUNT > 5 + || (hwspi._spi == &SPI5) +#endif + ) { + hwspi._spi->begin(); + } + } else if(connection == TFT_SOFT_SPI) { + + pinMode(swspi._mosi, OUTPUT); + digitalWrite(swspi._mosi, LOW); + pinMode(swspi._sck, OUTPUT); + digitalWrite(swspi._sck, LOW); + if(swspi._miso >= 0) { + pinMode(swspi._miso, INPUT); + } + + } else { // TFT_PARALLEL + + // Initialize data pins. We were only passed d0, so scan + // the pin description list looking for the other pins. + // They'll be on the same PORT, and within the next 7 (or 15) bits + // (because we need to write to a contiguous PORT byte or word). +#if defined(__AVR__) + // PORT registers are 8 bits wide, so just need a register match... + for(uint8_t i=0; i= dBit ) && + (g_APinDescription[i].ulPin <= (uint32_t)lastBit)) { + pinMode(i, OUTPUT); + digitalWrite(i, LOW); + } + } + #endif // end !CORE_TEENSY +#endif + pinMode(tft8._wr, OUTPUT); + digitalWrite(tft8._wr, HIGH); + if(tft8._rd >= 0) { + pinMode(tft8._rd, OUTPUT); + digitalWrite(tft8._rd, HIGH); + } + } + + if(_rst >= 0) { + // Toggle _rst low to reset + pinMode(_rst, OUTPUT); + digitalWrite(_rst, HIGH); + delay(100); + digitalWrite(_rst, LOW); + delay(100); + digitalWrite(_rst, HIGH); + delay(200); + } + +#if defined(USE_SPI_DMA) + if(((connection == TFT_HARD_SPI) || (connection == TFT_PARALLEL)) && + (dma.allocate() == DMA_STATUS_OK)) { // Allocate channel + // The DMA library needs to alloc at least one valid descriptor, + // so we do that here. It's not used in the usual sense though, + // just before a transfer we copy descriptor[0] to this address. + if(dptr = dma.addDescriptor(NULL, NULL, 42, DMA_BEAT_SIZE_BYTE, + false, false)) { + // Alloc 2 scanlines worth of pixels on display's major axis, + // whichever that is, rounding each up to 2-pixel boundary. + int major = (WIDTH > HEIGHT) ? WIDTH : HEIGHT; + major += (major & 1); // -> next 2-pixel bound, if needed. + maxFillLen = major * 2; // 2 scanlines + // Note to future self: if you decide to make the pixel buffer + // much larger, remember that DMA transfer descriptors can't + // exceed 65,535 bytes (not 65,536), meaning 32,767 pixels max. + // Not that we have that kind of RAM to throw around right now. + if((pixelBuf[0] = + (uint16_t *)malloc(maxFillLen * sizeof(uint16_t)))) { + // Alloc OK. Get pointer to start of second scanline. + pixelBuf[1] = &pixelBuf[0][major]; + // Determine number of DMA descriptors needed to cover + // entire screen when entire 2-line pixelBuf is used + // (round up for fractional last descriptor). + int numDescriptors = (WIDTH * HEIGHT + (maxFillLen - 1)) / + maxFillLen; + // DMA descriptors MUST be 128-bit (16 byte) aligned. + // memalign() is considered obsolete but it's replacements + // (aligned_alloc() or posix_memalign()) are not currently + // available in the version of ARM GCC in use, but this + // is, so here we are. + if((descriptor = (DmacDescriptor *)memalign(16, + numDescriptors * sizeof(DmacDescriptor)))) { + int dmac_id; + volatile uint32_t *data_reg; + + if(connection == TFT_HARD_SPI) { + // THIS IS AN AFFRONT TO NATURE, but I don't know + // any "clean" way to get the sercom number from the + // the SPIClass pointer (e.g. &SPI or &SPI1), which + // is all we have to work with. SPIClass does contain + // a SERCOM pointer but it is a PRIVATE member! + // Doing an UNSPEAKABLY HORRIBLE THING here, directly + // accessing the first 32-bit value in the SPIClass + // structure, knowing that's (currently) where the + // SERCOM pointer lives, but this ENTIRELY DEPENDS on + // that structure not changing nor the compiler + // rearranging things. Oh the humanity! + + if(*(SERCOM **)hwspi._spi == &sercom0) { + dmac_id = SERCOM0_DMAC_ID_TX; + data_reg = &SERCOM0->SPI.DATA.reg; +#if defined SERCOM1 + } else if(*(SERCOM **)hwspi._spi == &sercom1) { + dmac_id = SERCOM1_DMAC_ID_TX; + data_reg = &SERCOM1->SPI.DATA.reg; +#endif +#if defined SERCOM2 + } else if(*(SERCOM **)hwspi._spi == &sercom2) { + dmac_id = SERCOM2_DMAC_ID_TX; + data_reg = &SERCOM2->SPI.DATA.reg; +#endif +#if defined SERCOM3 + } else if(*(SERCOM **)hwspi._spi == &sercom3) { + dmac_id = SERCOM3_DMAC_ID_TX; + data_reg = &SERCOM3->SPI.DATA.reg; +#endif +#if defined SERCOM4 + } else if(*(SERCOM **)hwspi._spi == &sercom4) { + dmac_id = SERCOM4_DMAC_ID_TX; + data_reg = &SERCOM4->SPI.DATA.reg; +#endif +#if defined SERCOM5 + } else if(*(SERCOM **)hwspi._spi == &sercom5) { + dmac_id = SERCOM5_DMAC_ID_TX; + data_reg = &SERCOM5->SPI.DATA.reg; +#endif +#if defined SERCOM6 + } else if(*(SERCOM **)hwspi._spi == &sercom6) { + dmac_id = SERCOM6_DMAC_ID_TX; + data_reg = &SERCOM6->SPI.DATA.reg; +#endif +#if defined SERCOM7 + } else if(*(SERCOM **)hwspi._spi == &sercom7) { + dmac_id = SERCOM7_DMAC_ID_TX; + data_reg = &SERCOM7->SPI.DATA.reg; +#endif + } + dma.setPriority(DMA_PRIORITY_3); + dma.setTrigger(dmac_id); + dma.setAction(DMA_TRIGGER_ACTON_BEAT); + + // Initialize descriptor list. + for(int d=0; dChannel[dmaChannel].CHEVCTRL.bit.EVOE = 1; + DMAC->Channel[dmaChannel].CHEVCTRL.bit.EVOMODE = 0; + + // CONFIGURE TIMER/COUNTER (for write strobe) + + Tc *timer = tcList[tcNum].tc; // -> Timer struct + int id = tcList[tcNum].gclk; // Timer GCLK ID + GCLK_PCHCTRL_Type pchctrl; + + // Set up timer clock source from GCLK + GCLK->PCHCTRL[id].bit.CHEN = 0; // Stop timer + while(GCLK->PCHCTRL[id].bit.CHEN); // Wait for it + pchctrl.bit.GEN = GCLK_PCHCTRL_GEN_GCLK0_Val; + pchctrl.bit.CHEN = 1; // Enable + GCLK->PCHCTRL[id].reg = pchctrl.reg; + while(!GCLK->PCHCTRL[id].bit.CHEN); // Wait for it + + // Disable timer/counter before configuring it + timer->COUNT8.CTRLA.bit.ENABLE = 0; + while(timer->COUNT8.SYNCBUSY.bit.STATUS); + + timer->COUNT8.WAVE.bit.WAVEGEN = 2; // NPWM + timer->COUNT8.CTRLA.bit.MODE = 1; // 8-bit + timer->COUNT8.CTRLA.bit.PRESCALER = 0; // 1:1 + while(timer->COUNT8.SYNCBUSY.bit.STATUS); + + timer->COUNT8.CTRLBCLR.bit.DIR = 1; // Count UP + while(timer->COUNT8.SYNCBUSY.bit.CTRLB); + timer->COUNT8.CTRLBSET.bit.ONESHOT = 1; // One-shot + while(timer->COUNT8.SYNCBUSY.bit.CTRLB); + timer->COUNT8.PER.reg = 6; // PWM top + while(timer->COUNT8.SYNCBUSY.bit.PER); + timer->COUNT8.CC[0].reg = 2; // Compare + while(timer->COUNT8.SYNCBUSY.bit.CC0); + // Enable async input events, + // event action = restart. + timer->COUNT8.EVCTRL.bit.TCEI = 1; + timer->COUNT8.EVCTRL.bit.EVACT = 1; + + // Enable timer + timer->COUNT8.CTRLA.reg |= TC_CTRLA_ENABLE; + while(timer->COUNT8.SYNCBUSY.bit.STATUS); + +#if(wrPeripheral == PIO_CCL) + // CONFIGURE CCL (inverts timer/counter output) + + MCLK->APBCMASK.bit.CCL_ = 1; // Enable CCL clock + CCL->CTRL.bit.ENABLE = 0; // Disable to config + CCL->CTRL.bit.SWRST = 1; // Reset CCL registers + CCL->LUTCTRL[tcNum].bit.ENABLE = 0; // Disable LUT + CCL->LUTCTRL[tcNum].bit.FILTSEL = 0; // No filter + CCL->LUTCTRL[tcNum].bit.INSEL0 = 6; // TC input + CCL->LUTCTRL[tcNum].bit.INSEL1 = 0; // MASK + CCL->LUTCTRL[tcNum].bit.INSEL2 = 0; // MASK + CCL->LUTCTRL[tcNum].bit.TRUTH = 1; // Invert in 0 + CCL->LUTCTRL[tcNum].bit.ENABLE = 1; // Enable LUT + CCL->CTRL.bit.ENABLE = 1; // Enable CCL +#endif + + // CONFIGURE EVENT SYSTEM + + // Set up event system clock source from GCLK... + // Disable EVSYS, wait for disable + GCLK->PCHCTRL[EVSYS_GCLK_ID_0].bit.CHEN = 0; + while(GCLK->PCHCTRL[EVSYS_GCLK_ID_0].bit.CHEN); + pchctrl.bit.GEN = GCLK_PCHCTRL_GEN_GCLK0_Val; + pchctrl.bit.CHEN = 1; // Re-enable + GCLK->PCHCTRL[EVSYS_GCLK_ID_0].reg = pchctrl.reg; + // Wait for it, then enable EVSYS clock + while(!GCLK->PCHCTRL[EVSYS_GCLK_ID_0].bit.CHEN); + MCLK->APBBMASK.bit.EVSYS_ = 1; + + // Connect Timer EVU to ch 0 + EVSYS->USER[tcList[tcNum].evu].reg = 1; + // Datasheet recommends single write operation; + // reg instead of bit. Also datasheet: PATH bits + // must be zero when using async! + EVSYS_CHANNEL_Type ev; + ev.reg = 0; + ev.bit.PATH = 2; // Asynchronous + ev.bit.EVGEN = 0x22 + dmaChannel; // DMA channel 0+ + EVSYS->Channel[0].CHANNEL.reg = ev.reg; + + // Initialize descriptor list. + for(int d=0; d= 0) SPI_CS_LOW(); +} + +/*! + @brief Call after issuing command(s) or data to display. Performs + chip-deselect (if required) and ends an SPI transaction (if + using hardware SPI and transactions are supported). Required + for all display types; not an SPI-specific function. +*/ +void Adafruit_SPITFT::endWrite(void) { + if(_cs >= 0) SPI_CS_HIGH(); + SPI_END_TRANSACTION(); +} + + +// ------------------------------------------------------------------------- +// Lower-level graphics operations. These functions require a chip-select +// and/or SPI transaction around them (via startWrite(), endWrite() above). +// Higher-level graphics primitives might start a single transaction and +// then make multiple calls to these functions (e.g. circle or text +// rendering might make repeated lines or rects) before ending the +// transaction. It's more efficient than starting a transaction every time. + +/*! + @brief Draw a single pixel to the display at requested coordinates. + Not self-contained; should follow a startWrite() call. + @param x Horizontal position (0 = left). + @param y Vertical position (0 = top). + @param color 16-bit pixel color in '565' RGB format. +*/ +void Adafruit_SPITFT::writePixel(int16_t x, int16_t y, uint16_t color) { + if((x >= 0) && (x < _width) && (y >= 0) && (y < _height)) { + setAddrWindow(x, y, 1, 1); + SPI_WRITE16(color); + } +} + +/*! + @brief Issue a series of pixels from memory to the display. Not self- + contained; should follow startWrite() and setAddrWindow() calls. + @param colors Pointer to array of 16-bit pixel values in '565' RGB + format. + @param len Number of elements in 'colors' array. + @param block If true (default case if unspecified), function blocks + until DMA transfer is complete. This is simply IGNORED + if DMA is not enabled. If false, the function returns + immediately after the last DMA transfer is started, + and one should use the dmaWait() function before + doing ANY other display-related activities (or even + any SPI-related activities, if using an SPI display + that shares the bus with other devices). + @param bigEndian If using DMA, and if set true, bitmap in memory is in + big-endian order (most significant byte first). By + default this is false, as most microcontrollers seem + to be little-endian and 16-bit pixel values must be + byte-swapped before issuing to the display (which tend + to be big-endian when using SPI or 8-bit parallel). + If an application can optimize around this -- for + example, a bitmap in a uint16_t array having the byte + values already reordered big-endian, this can save + some processing time here, ESPECIALLY if using this + function's non-blocking DMA mode. Not all cases are + covered...this is really here only for SAMD DMA and + much forethought on the application side. +*/ +void Adafruit_SPITFT::writePixels(uint16_t *colors, uint32_t len, + bool block, bool bigEndian) { + + if(!len) return; // Avoid 0-byte transfers + +#if defined(ESP32) // ESP32 has a special SPI pixel-writing function... + if(connection == TFT_HARD_SPI) { + hwspi._spi->writePixels(colors, len * 2); + return; + } +#elif defined(USE_SPI_DMA) + if((connection == TFT_HARD_SPI) || (connection == TFT_PARALLEL)) { + int maxSpan = maxFillLen / 2; // One scanline max + uint8_t pixelBufIdx = 0; // Active pixel buffer number + #if defined(__SAMD51__) + if(connection == TFT_PARALLEL) { + // Switch WR pin to PWM or CCL + pinPeripheral(tft8._wr, wrPeripheral); + } + #endif // end __SAMD51__ + if(!bigEndian) { // Normal little-endian situation... + while(len) { + int count = (len < maxSpan) ? len : maxSpan; + + // Because TFT and SAMD endianisms are different, must swap + // bytes from the 'colors' array passed into a DMA working + // buffer. This can take place while the prior DMA transfer + // is in progress, hence the need for two pixelBufs. + for(int i=0; isetDataMode(hwspi._mode); + } else { + pinPeripheral(tft8._wr, PIO_OUTPUT); // Switch WR back to GPIO + } + #endif // end __SAMD51__ || _SAMD21_ + } + return; + } +#endif // end USE_SPI_DMA + + // All other cases (bitbang SPI or non-DMA hard SPI or parallel), + // use a loop with the normal 16-bit data write function: + while(len--) { + SPI_WRITE16(*colors++); + } +} + +/*! + @brief Wait for the last DMA transfer in a prior non-blocking + writePixels() call to complete. This does nothing if DMA + is not enabled, and is not needed if blocking writePixels() + was used (as is the default case). +*/ +void Adafruit_SPITFT::dmaWait(void) { +#if defined(USE_SPI_DMA) + while(dma_busy); + #if defined(__SAMD51__) || defined(_SAMD21_) + if(connection == TFT_HARD_SPI) { + // See SAMD51/21 note in writeColor() + hwspi._spi->setDataMode(hwspi._mode); + } else { + pinPeripheral(tft8._wr, PIO_OUTPUT); // Switch WR back to GPIO + } + #endif // end __SAMD51__ || _SAMD21_ +#endif +} + +/*! + @brief Issue a series of pixels, all the same color. Not self- + contained; should follow startWrite() and setAddrWindow() calls. + @param color 16-bit pixel color in '565' RGB format. + @param len Number of pixels to draw. +*/ +void Adafruit_SPITFT::writeColor(uint16_t color, uint32_t len) { + + if(!len) return; // Avoid 0-byte transfers + + uint8_t hi = color >> 8, lo = color; + +#if defined(ESP32) // ESP32 has a special SPI pixel-writing function... + if(connection == TFT_HARD_SPI) { + #define SPI_MAX_PIXELS_AT_ONCE 32 + #define TMPBUF_LONGWORDS (SPI_MAX_PIXELS_AT_ONCE + 1) / 2 + #define TMPBUF_PIXELS (TMPBUF_LONGWORDS * 2) + static uint32_t temp[TMPBUF_LONGWORDS]; + uint32_t c32 = color * 0x00010001; + uint16_t bufLen = (len < TMPBUF_PIXELS) ? len : TMPBUF_PIXELS, + xferLen, fillLen; + // Fill temp buffer 32 bits at a time + fillLen = (bufLen + 1) / 2; // Round up to next 32-bit boundary + for(uint32_t t=0; t= 16)) { // Don't bother with DMA on short pixel runs + int i, d, numDescriptors; + if(hi == lo) { // If high & low bytes are same... + onePixelBuf = color; + // Can do this with a relatively short descriptor list, + // each transferring a max of 32,767 (not 32,768) pixels. + // This won't run off the end of the allocated descriptor list, + // since we're using much larger chunks per descriptor here. + numDescriptors = (len + 32766) / 32767; + for(d=0; d lastFillLen) { + int fillStart = lastFillLen / 2, + fillEnd = (((len < maxFillLen) ? + len : maxFillLen) + 1) / 2; + for(i=fillStart; isetDataMode(hwspi._mode); + } else { + pinPeripheral(tft8._wr, PIO_OUTPUT); // Switch WR back to GPIO + } + #endif // end __SAMD51__ + return; + } + #endif // end USE_SPI_DMA +#endif // end !ESP32 + + // All other cases (non-DMA hard SPI, bitbang SPI, parallel)... + + if(connection == TFT_HARD_SPI) { +#if defined(ESP8266) + do { + uint32_t pixelsThisPass = len; + if(pixelsThisPass > 50000) pixelsThisPass = 50000; + len -= pixelsThisPass; + yield(); // Periodic yield() on long fills + while(pixelsThisPass--) { + hwspi._spi->write(hi); + hwspi._spi->write(lo); + } + } while(len); +#else // !ESP8266 + while(len--) { + #if defined(__AVR__) + AVR_WRITESPI(hi); + AVR_WRITESPI(lo); + #elif defined(ESP32) + hwspi._spi->write(hi); + hwspi._spi->write(lo); + #else + hwspi._spi->transfer(hi); + hwspi._spi->transfer(lo); + #endif + } +#endif // end !ESP8266 + } else if(connection == TFT_SOFT_SPI) { +#if defined(ESP8266) + do { + uint32_t pixelsThisPass = len; + if(pixelsThisPass > 20000) pixelsThisPass = 20000; + len -= pixelsThisPass; + yield(); // Periodic yield() on long fills + while(pixelsThisPass--) { + for(uint16_t bit=0, x=color; bit<16; bit++) { + if(x & 0x8000) SPI_MOSI_HIGH(); + else SPI_MOSI_LOW(); + SPI_SCK_HIGH(); + SPI_SCK_LOW(); + x <<= 1; + } + } + } while(len); +#else // !ESP8266 + while(len--) { + #if defined(__AVR__) + for(uint8_t bit=0, x=hi; bit<8; bit++) { + if(x & 0x80) SPI_MOSI_HIGH(); + else SPI_MOSI_LOW(); + SPI_SCK_HIGH(); + SPI_SCK_LOW(); + x <<= 1; + } + for(uint8_t bit=0, x=lo; bit<8; bit++) { + if(x & 0x80) SPI_MOSI_HIGH(); + else SPI_MOSI_LOW(); + SPI_SCK_HIGH(); + SPI_SCK_LOW(); + x <<= 1; + } + #else // !__AVR__ + for(uint16_t bit=0, x=color; bit<16; bit++) { + if(x & 0x8000) SPI_MOSI_HIGH(); + else SPI_MOSI_LOW(); + SPI_SCK_HIGH(); + x <<= 1; + SPI_SCK_LOW(); + } + #endif // end !__AVR__ + } +#endif // end !ESP8266 + } else { // PARALLEL + if(hi == lo) { +#if defined(__AVR__) + len *= 2; + *tft8.writePort = hi; + while(len--) { + TFT_WR_STROBE(); + } +#elif defined(USE_FAST_PINIO) + if(!tft8.wide) { + len *= 2; + *tft8.writePort = hi; + } else { + *(volatile uint16_t *)tft8.writePort = color; + } + while(len--) { + TFT_WR_STROBE(); + } +#endif + } else { + while(len--) { +#if defined(__AVR__) + *tft8.writePort = hi; + TFT_WR_STROBE(); + *tft8.writePort = lo; +#elif defined(USE_FAST_PINIO) + if(!tft8.wide) { + *tft8.writePort = hi; + TFT_WR_STROBE(); + *tft8.writePort = lo; + } else { + *(volatile uint16_t *)tft8.writePort = color; + } +#endif + TFT_WR_STROBE(); + } + } + } +} + +/*! + @brief Draw a filled rectangle to the display. Not self-contained; + should follow startWrite(). Typically used by higher-level + graphics primitives; user code shouldn't need to call this and + is likely to use the self-contained fillRect() instead. + writeFillRect() performs its own edge clipping and rejection; + see writeFillRectPreclipped() for a more 'raw' implementation. + @param x Horizontal position of first corner. + @param y Vertical position of first corner. + @param w Rectangle width in pixels (positive = right of first + corner, negative = left of first corner). + @param h Rectangle height in pixels (positive = below first + corner, negative = above first corner). + @param color 16-bit fill color in '565' RGB format. + @note Written in this deep-nested way because C by definition will + optimize for the 'if' case, not the 'else' -- avoids branches + and rejects clipped rectangles at the least-work possibility. +*/ +void Adafruit_SPITFT::writeFillRect(int16_t x, int16_t y, + int16_t w, int16_t h, uint16_t color) { + if(w && h) { // Nonzero width and height? + if(w < 0) { // If negative width... + x += w + 1; // Move X to left edge + w = -w; // Use positive width + } + if(x < _width) { // Not off right + if(h < 0) { // If negative height... + y += h + 1; // Move Y to top edge + h = -h; // Use positive height + } + if(y < _height) { // Not off bottom + int16_t x2 = x + w - 1; + if(x2 >= 0) { // Not off left + int16_t y2 = y + h - 1; + if(y2 >= 0) { // Not off top + // Rectangle partly or fully overlaps screen + if(x < 0) { x = 0; w = x2 + 1; } // Clip left + if(y < 0) { y = 0; h = y2 + 1; } // Clip top + if(x2 >= _width) { w = _width - x; } // Clip right + if(y2 >= _height) { h = _height - y; } // Clip bottom + writeFillRectPreclipped(x, y, w, h, color); + } + } + } + } + } +} + +/*! + @brief Draw a horizontal line on the display. Performs edge clipping + and rejection. Not self-contained; should follow startWrite(). + Typically used by higher-level graphics primitives; user code + shouldn't need to call this and is likely to use the self- + contained drawFastHLine() instead. + @param x Horizontal position of first point. + @param y Vertical position of first point. + @param w Line width in pixels (positive = right of first point, + negative = point of first corner). + @param color 16-bit line color in '565' RGB format. +*/ +void inline Adafruit_SPITFT::writeFastHLine(int16_t x, int16_t y, int16_t w, + uint16_t color) { + if((y >= 0) && (y < _height) && w) { // Y on screen, nonzero width + if(w < 0) { // If negative width... + x += w + 1; // Move X to left edge + w = -w; // Use positive width + } + if(x < _width) { // Not off right + int16_t x2 = x + w - 1; + if(x2 >= 0) { // Not off left + // Line partly or fully overlaps screen + if(x < 0) { x = 0; w = x2 + 1; } // Clip left + if(x2 >= _width) { w = _width - x; } // Clip right + writeFillRectPreclipped(x, y, w, 1, color); + } + } + } +} + +/*! + @brief Draw a vertical line on the display. Performs edge clipping and + rejection. Not self-contained; should follow startWrite(). + Typically used by higher-level graphics primitives; user code + shouldn't need to call this and is likely to use the self- + contained drawFastVLine() instead. + @param x Horizontal position of first point. + @param y Vertical position of first point. + @param h Line height in pixels (positive = below first point, + negative = above first point). + @param color 16-bit line color in '565' RGB format. +*/ +void inline Adafruit_SPITFT::writeFastVLine(int16_t x, int16_t y, int16_t h, + uint16_t color) { + if((x >= 0) && (x < _width) && h) { // X on screen, nonzero height + if(h < 0) { // If negative height... + y += h + 1; // Move Y to top edge + h = -h; // Use positive height + } + if(y < _height) { // Not off bottom + int16_t y2 = y + h - 1; + if(y2 >= 0) { // Not off top + // Line partly or fully overlaps screen + if(y < 0) { y = 0; h = y2 + 1; } // Clip top + if(y2 >= _height) { h = _height - y; } // Clip bottom + writeFillRectPreclipped(x, y, 1, h, color); + } + } + } +} + +/*! + @brief A lower-level version of writeFillRect(). This version requires + all inputs are in-bounds, that width and height are positive, + and no part extends offscreen. NO EDGE CLIPPING OR REJECTION IS + PERFORMED. If higher-level graphics primitives are written to + handle their own clipping earlier in the drawing process, this + can avoid unnecessary function calls and repeated clipping + operations in the lower-level functions. + @param x Horizontal position of first corner. MUST BE WITHIN + SCREEN BOUNDS. + @param y Vertical position of first corner. MUST BE WITHIN SCREEN + BOUNDS. + @param w Rectangle width in pixels. MUST BE POSITIVE AND NOT + EXTEND OFF SCREEN. + @param h Rectangle height in pixels. MUST BE POSITIVE AND NOT + EXTEND OFF SCREEN. + @param color 16-bit fill color in '565' RGB format. + @note This is a new function, no graphics primitives besides rects + and horizontal/vertical lines are written to best use this yet. +*/ +inline void Adafruit_SPITFT::writeFillRectPreclipped(int16_t x, int16_t y, + int16_t w, int16_t h, uint16_t color) { + setAddrWindow(x, y, w, h); + writeColor(color, (uint32_t)w * h); +} + + +// ------------------------------------------------------------------------- +// Ever-so-slightly higher-level graphics operations. Similar to the 'write' +// functions above, but these contain their own chip-select and SPI +// transactions as needed (via startWrite(), endWrite()). They're typically +// used solo -- as graphics primitives in themselves, not invoked by higher- +// level primitives (which should use the functions above for better +// performance). + +/*! + @brief Draw a single pixel to the display at requested coordinates. + Self-contained and provides its own transaction as needed + (see writePixel(x,y,color) for a lower-level variant). + Edge clipping is performed here. + @param x Horizontal position (0 = left). + @param y Vertical position (0 = top). + @param color 16-bit pixel color in '565' RGB format. +*/ +void Adafruit_SPITFT::drawPixel(int16_t x, int16_t y, uint16_t color) { + // Clip first... + if((x >= 0) && (x < _width) && (y >= 0) && (y < _height)) { + // THEN set up transaction (if needed) and draw... + startWrite(); + setAddrWindow(x, y, 1, 1); + SPI_WRITE16(color); + endWrite(); + } +} + +/*! + @brief Draw a filled rectangle to the display. Self-contained and + provides its own transaction as needed (see writeFillRect() or + writeFillRectPreclipped() for lower-level variants). Edge + clipping and rejection is performed here. + @param x Horizontal position of first corner. + @param y Vertical position of first corner. + @param w Rectangle width in pixels (positive = right of first + corner, negative = left of first corner). + @param h Rectangle height in pixels (positive = below first + corner, negative = above first corner). + @param color 16-bit fill color in '565' RGB format. + @note This repeats the writeFillRect() function almost in its entirety, + with the addition of a transaction start/end. It's done this way + (rather than starting the transaction and calling writeFillRect() + to handle clipping and so forth) so that the transaction isn't + performed at all if the rectangle is rejected. It's really not + that much code. +*/ +void Adafruit_SPITFT::fillRect(int16_t x, int16_t y, int16_t w, int16_t h, + uint16_t color) { + if(w && h) { // Nonzero width and height? + if(w < 0) { // If negative width... + x += w + 1; // Move X to left edge + w = -w; // Use positive width + } + if(x < _width) { // Not off right + if(h < 0) { // If negative height... + y += h + 1; // Move Y to top edge + h = -h; // Use positive height + } + if(y < _height) { // Not off bottom + int16_t x2 = x + w - 1; + if(x2 >= 0) { // Not off left + int16_t y2 = y + h - 1; + if(y2 >= 0) { // Not off top + // Rectangle partly or fully overlaps screen + if(x < 0) { x = 0; w = x2 + 1; } // Clip left + if(y < 0) { y = 0; h = y2 + 1; } // Clip top + if(x2 >= _width) { w = _width - x; } // Clip right + if(y2 >= _height) { h = _height - y; } // Clip bottom + startWrite(); + writeFillRectPreclipped(x, y, w, h, color); + endWrite(); + } + } + } + } + } +} + +/*! + @brief Draw a horizontal line on the display. Self-contained and + provides its own transaction as needed (see writeFastHLine() for + a lower-level variant). Edge clipping and rejection is performed + here. + @param x Horizontal position of first point. + @param y Vertical position of first point. + @param w Line width in pixels (positive = right of first point, + negative = point of first corner). + @param color 16-bit line color in '565' RGB format. + @note This repeats the writeFastHLine() function almost in its + entirety, with the addition of a transaction start/end. It's + done this way (rather than starting the transaction and calling + writeFastHLine() to handle clipping and so forth) so that the + transaction isn't performed at all if the line is rejected. +*/ +void Adafruit_SPITFT::drawFastHLine(int16_t x, int16_t y, int16_t w, + uint16_t color) { + if((y >= 0) && (y < _height) && w) { // Y on screen, nonzero width + if(w < 0) { // If negative width... + x += w + 1; // Move X to left edge + w = -w; // Use positive width + } + if(x < _width) { // Not off right + int16_t x2 = x + w - 1; + if(x2 >= 0) { // Not off left + // Line partly or fully overlaps screen + if(x < 0) { x = 0; w = x2 + 1; } // Clip left + if(x2 >= _width) { w = _width - x; } // Clip right + startWrite(); + writeFillRectPreclipped(x, y, w, 1, color); + endWrite(); + } + } + } +} + +/*! + @brief Draw a vertical line on the display. Self-contained and provides + its own transaction as needed (see writeFastHLine() for a lower- + level variant). Edge clipping and rejection is performed here. + @param x Horizontal position of first point. + @param y Vertical position of first point. + @param h Line height in pixels (positive = below first point, + negative = above first point). + @param color 16-bit line color in '565' RGB format. + @note This repeats the writeFastVLine() function almost in its + entirety, with the addition of a transaction start/end. It's + done this way (rather than starting the transaction and calling + writeFastVLine() to handle clipping and so forth) so that the + transaction isn't performed at all if the line is rejected. +*/ +void Adafruit_SPITFT::drawFastVLine(int16_t x, int16_t y, int16_t h, + uint16_t color) { + if((x >= 0) && (x < _width) && h) { // X on screen, nonzero height + if(h < 0) { // If negative height... + y += h + 1; // Move Y to top edge + h = -h; // Use positive height + } + if(y < _height) { // Not off bottom + int16_t y2 = y + h - 1; + if(y2 >= 0) { // Not off top + // Line partly or fully overlaps screen + if(y < 0) { y = 0; h = y2 + 1; } // Clip top + if(y2 >= _height) { h = _height - y; } // Clip bottom + startWrite(); + writeFillRectPreclipped(x, y, 1, h, color); + endWrite(); + } + } + } +} + +/*! + @brief Essentially writePixel() with a transaction around it. I don't + think this is in use by any of our code anymore (believe it was + for some older BMP-reading examples), but is kept here in case + any user code relies on it. Consider it DEPRECATED. + @param color 16-bit pixel color in '565' RGB format. +*/ +void Adafruit_SPITFT::pushColor(uint16_t color) { + startWrite(); + SPI_WRITE16(color); + endWrite(); +} + +/*! + @brief Draw a 16-bit image (565 RGB) at the specified (x,y) position. + For 16-bit display devices; no color reduction performed. + Adapted from https://github.com/PaulStoffregen/ILI9341_t3 + by Marc MERLIN. See examples/pictureEmbed to use this. + 5/6/2017: function name and arguments have changed for + compatibility with current GFX library and to avoid naming + problems in prior implementation. Formerly drawBitmap() with + arguments in different order. Handles its own transaction and + edge clipping/rejection. + @param x Top left corner horizontal coordinate. + @param y Top left corner vertical coordinate. + @param pcolors Pointer to 16-bit array of pixel values. + @param w Width of bitmap in pixels. + @param h Height of bitmap in pixels. +*/ +void Adafruit_SPITFT::drawRGBBitmap(int16_t x, int16_t y, + uint16_t *pcolors, int16_t w, int16_t h) { + + int16_t x2, y2; // Lower-right coord + if(( x >= _width ) || // Off-edge right + ( y >= _height) || // " top + ((x2 = (x+w-1)) < 0 ) || // " left + ((y2 = (y+h-1)) < 0) ) return; // " bottom + + int16_t bx1=0, by1=0, // Clipped top-left within bitmap + saveW=w; // Save original bitmap width value + if(x < 0) { // Clip left + w += x; + bx1 = -x; + x = 0; + } + if(y < 0) { // Clip top + h += y; + by1 = -y; + y = 0; + } + if(x2 >= _width ) w = _width - x; // Clip right + if(y2 >= _height) h = _height - y; // Clip bottom + + pcolors += by1 * saveW + bx1; // Offset bitmap ptr to clipped top-left + startWrite(); + setAddrWindow(x, y, w, h); // Clipped area + while(h--) { // For each (clipped) scanline... + writePixels(pcolors, w); // Push one (clipped) row + pcolors += saveW; // Advance pointer by one full (unclipped) line + } + endWrite(); +} + + +// ------------------------------------------------------------------------- +// Miscellaneous class member functions that don't draw anything. + +/*! + @brief Invert the colors of the display (if supported by hardware). + Self-contained, no transaction setup required. + @param i true = inverted display, false = normal display. +*/ +void Adafruit_SPITFT::invertDisplay(bool i) { + startWrite(); + writeCommand(i ? invertOnCommand : invertOffCommand); + endWrite(); +} + +/*! + @brief Given 8-bit red, green and blue values, return a 'packed' + 16-bit color value in '565' RGB format (5 bits red, 6 bits + green, 5 bits blue). This is just a mathematical operation, + no hardware is touched. + @param red 8-bit red brightnesss (0 = off, 255 = max). + @param green 8-bit green brightnesss (0 = off, 255 = max). + @param blue 8-bit blue brightnesss (0 = off, 255 = max). + @return 'Packed' 16-bit color value (565 format). +*/ +uint16_t Adafruit_SPITFT::color565(uint8_t red, uint8_t green, uint8_t blue) { + return ((red & 0xF8) << 8) | ((green & 0xFC) << 3) | (blue >> 3); +} + +/*! + @brief Adafruit_SPITFT Send Command handles complete sending of commands and data + @param commandByte The Command Byte + @param dataBytes A pointer to the Data bytes to send + @param numDataBytes The number of bytes we should send + */ +void Adafruit_SPITFT::sendCommand(uint8_t commandByte, uint8_t *dataBytes, uint8_t numDataBytes) { + SPI_BEGIN_TRANSACTION(); + if(_cs >= 0) SPI_CS_LOW(); + + SPI_DC_LOW(); // Command mode + spiWrite(commandByte); // Send the command byte + + SPI_DC_HIGH(); + for (int i=0; i= 0) SPI_CS_HIGH(); + SPI_END_TRANSACTION(); +} + +/*! + @brief Adafruit_SPITFT Send Command handles complete sending of commands and const data + @param commandByte The Command Byte + @param dataBytes A pointer to the Data bytes to send + @param numDataBytes The number of bytes we should send + */ +void Adafruit_SPITFT::sendCommand(uint8_t commandByte, const uint8_t *dataBytes, uint8_t numDataBytes) { + SPI_BEGIN_TRANSACTION(); + if(_cs >= 0) SPI_CS_LOW(); + + SPI_DC_LOW(); // Command mode + spiWrite(commandByte); // Send the command byte + + SPI_DC_HIGH(); + for (int i=0; i= 0) SPI_CS_HIGH(); + SPI_END_TRANSACTION(); +} + +/*! + @brief Read 8 bits of data from display configuration memory (not RAM). + This is highly undocumented/supported and should be avoided, + function is only included because some of the examples use it. + @param commandByte + The command register to read data from. + @param index + The byte index into the command to read from. + @return Unsigned 8-bit data read from display register. + */ +/**************************************************************************/ +uint8_t Adafruit_SPITFT::readcommand8(uint8_t commandByte, uint8_t index) { + uint8_t result; + startWrite(); + SPI_DC_LOW(); // Command mode + spiWrite(commandByte); + SPI_DC_HIGH(); // Data mode + do { + result = spiRead(); + } while(index--); // Discard bytes up to index'th + endWrite(); + return result; +} + +// ------------------------------------------------------------------------- +// Lowest-level hardware-interfacing functions. Many of these are inline and +// compile to different things based on #defines -- typically just a few +// instructions. Others, not so much, those are not inlined. + +/*! + @brief Start an SPI transaction if using the hardware SPI interface to + the display. If using an earlier version of the Arduino platform + (before the addition of SPI transactions), this instead attempts + to set up the SPI clock and mode. No action is taken if the + connection is not hardware SPI-based. This does NOT include a + chip-select operation -- see startWrite() for a function that + encapsulated both actions. +*/ +inline void Adafruit_SPITFT::SPI_BEGIN_TRANSACTION(void) { + if(connection == TFT_HARD_SPI) { +#if defined(SPI_HAS_TRANSACTION) + hwspi._spi->beginTransaction(hwspi.settings); +#else // No transactions, configure SPI manually... + #if defined(__AVR__) || defined(TEENSYDUINO) || defined(ARDUINO_ARCH_STM32F1) + hwspi._spi->setClockDivider(SPI_CLOCK_DIV2); + #elif defined(__arm__) + hwspi._spi->setClockDivider(11); + #elif defined(ESP8266) || defined(ESP32) + hwspi._spi->setFrequency(hwspi._freq); + #elif defined(RASPI) || defined(ARDUINO_ARCH_STM32F1) + hwspi._spi->setClock(hwspi._freq); + #endif + hwspi._spi->setBitOrder(MSBFIRST); + hwspi._spi->setDataMode(hwspi._mode); +#endif // end !SPI_HAS_TRANSACTION + } +} + +/*! + @brief End an SPI transaction if using the hardware SPI interface to + the display. No action is taken if the connection is not + hardware SPI-based or if using an earlier version of the Arduino + platform (before the addition of SPI transactions). This does + NOT include a chip-deselect operation -- see endWrite() for a + function that encapsulated both actions. +*/ +inline void Adafruit_SPITFT::SPI_END_TRANSACTION(void) { +#if defined(SPI_HAS_TRANSACTION) + if(connection == TFT_HARD_SPI) { + hwspi._spi->endTransaction(); + } +#endif +} + +/*! + @brief Issue a single 8-bit value to the display. Chip-select, + transaction and data/command selection must have been + previously set -- this ONLY issues the byte. This is another of + those functions in the library with a now-not-accurate name + that's being maintained for compatibility with outside code. + This function is used even if display connection is parallel. + @param b 8-bit value to write. +*/ +void Adafruit_SPITFT::spiWrite(uint8_t b) { + if(connection == TFT_HARD_SPI) { +#if defined(__AVR__) + AVR_WRITESPI(b); +#elif defined(ESP8266) || defined(ESP32) + hwspi._spi->write(b); +#else + hwspi._spi->transfer(b); +#endif + } else if(connection == TFT_SOFT_SPI) { + for(uint8_t bit=0; bit<8; bit++) { + if(b & 0x80) SPI_MOSI_HIGH(); + else SPI_MOSI_LOW(); + SPI_SCK_HIGH(); + b <<= 1; + SPI_SCK_LOW(); + } + } else { // TFT_PARALLEL +#if defined(__AVR__) + *tft8.writePort = b; +#elif defined(USE_FAST_PINIO) + if(!tft8.wide) *tft8.writePort = b; + else *(volatile uint16_t *)tft8.writePort = b; +#endif + TFT_WR_STROBE(); + } +} + +/*! + @brief Write a single command byte to the display. Chip-select and + transaction must have been previously set -- this ONLY sets + the device to COMMAND mode, issues the byte and then restores + DATA mode. There is no corresponding explicit writeData() + function -- just use spiWrite(). + @param cmd 8-bit command to write. +*/ +void Adafruit_SPITFT::writeCommand(uint8_t cmd) { + SPI_DC_LOW(); + spiWrite(cmd); + SPI_DC_HIGH(); +} + +/*! + @brief Read a single 8-bit value from the display. Chip-select and + transaction must have been previously set -- this ONLY reads + the byte. This is another of those functions in the library + with a now-not-accurate name that's being maintained for + compatibility with outside code. This function is used even if + display connection is parallel. + @return Unsigned 8-bit value read (always zero if USE_FAST_PINIO is + not supported by the MCU architecture). +*/ +uint8_t Adafruit_SPITFT::spiRead(void) { + uint8_t b = 0; + uint16_t w = 0; + if(connection == TFT_HARD_SPI) { + return hwspi._spi->transfer((uint8_t)0); + } else if(connection == TFT_SOFT_SPI) { + if(swspi._miso >= 0) { + for(uint8_t i=0; i<8; i++) { + SPI_SCK_HIGH(); + b <<= 1; + if(SPI_MISO_READ()) b++; + SPI_SCK_LOW(); + } + } + return b; + } else { // TFT_PARALLEL + if(tft8._rd >= 0) { +#if defined(USE_FAST_PINIO) + TFT_RD_LOW(); // Read line LOW + #if defined(__AVR__) + *tft8.portDir = 0x00; // Set port to input state + w = *tft8.readPort; // Read value from port + *tft8.portDir = 0xFF; // Restore port to output + #else // !__AVR__ + if(!tft8.wide) { // 8-bit TFT connection + #if defined(HAS_PORT_SET_CLR) + *tft8.dirClr = 0xFF; // Set port to input state + w = *tft8.readPort; // Read value from port + *tft8.dirSet = 0xFF; // Restore port to output + #else // !HAS_PORT_SET_CLR + *tft8.portDir = 0x00; // Set port to input state + w = *tft8.readPort; // Read value from port + *tft8.portDir = 0xFF; // Restore port to output + #endif // end HAS_PORT_SET_CLR + } else { // 16-bit TFT connection + #if defined(HAS_PORT_SET_CLR) + *(volatile uint16_t *)tft8.dirClr = 0xFFFF; // Input state + w = *(volatile uint16_t *)tft8.readPort; // 16-bit read + *(volatile uint16_t *)tft8.dirSet = 0xFFFF; // Output state + #else // !HAS_PORT_SET_CLR + *(volatile uint16_t *)tft8.portDir = 0x0000; // Input state + w = *(volatile uint16_t *)tft8.readPort; // 16-bit read + *(volatile uint16_t *)tft8.portDir = 0xFFFF; // Output state + #endif // end !HAS_PORT_SET_CLR + } + TFT_RD_HIGH(); // Read line HIGH + #endif // end !__AVR__ +#else // !USE_FAST_PINIO + w = 0; // Parallel TFT is NOT SUPPORTED without USE_FAST_PINIO +#endif // end !USE_FAST_PINIO + } + return w; + } +} + +/*! + @brief Set the software (bitbang) SPI MOSI line HIGH. +*/ +inline void Adafruit_SPITFT::SPI_MOSI_HIGH(void) { +#if defined(USE_FAST_PINIO) + #if defined(HAS_PORT_SET_CLR) + #if defined(KINETISK) + *swspi.mosiPortSet = 1; + #else // !KINETISK + *swspi.mosiPortSet = swspi.mosiPinMask; + #endif + #else // !HAS_PORT_SET_CLR + *swspi.mosiPort |= swspi.mosiPinMaskSet; + #endif // end !HAS_PORT_SET_CLR +#else // !USE_FAST_PINIO + digitalWrite(swspi._mosi, HIGH); + #if defined(ESP32) + for(volatile uint8_t i=0; i<1; i++); + #endif // end ESP32 +#endif // end !USE_FAST_PINIO +} + +/*! + @brief Set the software (bitbang) SPI MOSI line LOW. +*/ +inline void Adafruit_SPITFT::SPI_MOSI_LOW(void) { +#if defined(USE_FAST_PINIO) + #if defined(HAS_PORT_SET_CLR) + #if defined(KINETISK) + *swspi.mosiPortClr = 1; + #else // !KINETISK + *swspi.mosiPortClr = swspi.mosiPinMask; + #endif + #else // !HAS_PORT_SET_CLR + *swspi.mosiPort &= swspi.mosiPinMaskClr; + #endif // end !HAS_PORT_SET_CLR +#else // !USE_FAST_PINIO + digitalWrite(swspi._mosi, LOW); + #if defined(ESP32) + for(volatile uint8_t i=0; i<1; i++); + #endif // end ESP32 +#endif // end !USE_FAST_PINIO +} + +/*! + @brief Set the software (bitbang) SPI SCK line HIGH. +*/ +inline void Adafruit_SPITFT::SPI_SCK_HIGH(void) { +#if defined(USE_FAST_PINIO) + #if defined(HAS_PORT_SET_CLR) + #if defined(KINETISK) + *swspi.sckPortSet = 1; + #else // !KINETISK + *swspi.sckPortSet = swspi.sckPinMask; + #if defined(__IMXRT1052__) || defined(__IMXRT1062__) // Teensy 4.x + for(volatile uint8_t i=0; i<1; i++); + #endif + #endif + #else // !HAS_PORT_SET_CLR + *swspi.sckPort |= swspi.sckPinMaskSet; + #endif // end !HAS_PORT_SET_CLR +#else // !USE_FAST_PINIO + digitalWrite(swspi._sck, HIGH); + #if defined(ESP32) + for(volatile uint8_t i=0; i<1; i++); + #endif // end ESP32 +#endif // end !USE_FAST_PINIO +} + +/*! + @brief Set the software (bitbang) SPI SCK line LOW. +*/ +inline void Adafruit_SPITFT::SPI_SCK_LOW(void) { +#if defined(USE_FAST_PINIO) + #if defined(HAS_PORT_SET_CLR) + #if defined(KINETISK) + *swspi.sckPortClr = 1; + #else // !KINETISK + *swspi.sckPortClr = swspi.sckPinMask; + #if defined(__IMXRT1052__) || defined(__IMXRT1062__) // Teensy 4.x + for(volatile uint8_t i=0; i<1; i++); + #endif + #endif + #else // !HAS_PORT_SET_CLR + *swspi.sckPort &= swspi.sckPinMaskClr; + #endif // end !HAS_PORT_SET_CLR +#else // !USE_FAST_PINIO + digitalWrite(swspi._sck, LOW); + #if defined(ESP32) + for(volatile uint8_t i=0; i<1; i++); + #endif // end ESP32 +#endif // end !USE_FAST_PINIO +} + +/*! + @brief Read the state of the software (bitbang) SPI MISO line. + @return true if HIGH, false if LOW. +*/ +inline bool Adafruit_SPITFT::SPI_MISO_READ(void) { +#if defined(USE_FAST_PINIO) + #if defined(KINETISK) + return *swspi.misoPort; + #else // !KINETISK + return *swspi.misoPort & swspi.misoPinMask; + #endif // end !KINETISK +#else // !USE_FAST_PINIO + return digitalRead(swspi._miso); +#endif // end !USE_FAST_PINIO +} + +/*! + @brief Issue a single 16-bit value to the display. Chip-select, + transaction and data/command selection must have been + previously set -- this ONLY issues the word. Despite the name, + this function is used even if display connection is parallel; + name was maintaned for backward compatibility. Naming is also + not consistent with the 8-bit version, spiWrite(). Sorry about + that. Again, staying compatible with outside code. + @param w 16-bit value to write. +*/ +void Adafruit_SPITFT::SPI_WRITE16(uint16_t w) { + if(connection == TFT_HARD_SPI) { +#if defined(__AVR__) + AVR_WRITESPI(w >> 8); + AVR_WRITESPI(w); +#elif defined(ESP8266) || defined(ESP32) + hwspi._spi->write16(w); +#else + hwspi._spi->transfer(w >> 8); + hwspi._spi->transfer(w); +#endif + } else if(connection == TFT_SOFT_SPI) { + for(uint8_t bit=0; bit<16; bit++) { + if(w & 0x8000) SPI_MOSI_HIGH(); + else SPI_MOSI_LOW(); + SPI_SCK_HIGH(); + SPI_SCK_LOW(); + w <<= 1; + } + } else { // TFT_PARALLEL +#if defined(__AVR__) + *tft8.writePort = w >> 8; + TFT_WR_STROBE(); + *tft8.writePort = w; +#elif defined(USE_FAST_PINIO) + if(!tft8.wide) { + *tft8.writePort = w >> 8; + TFT_WR_STROBE(); + *tft8.writePort = w; + } else { + *(volatile uint16_t *)tft8.writePort = w; + } +#endif + TFT_WR_STROBE(); + } +} + +/*! + @brief Issue a single 32-bit value to the display. Chip-select, + transaction and data/command selection must have been + previously set -- this ONLY issues the longword. Despite the + name, this function is used even if display connection is + parallel; name was maintaned for backward compatibility. Naming + is also not consistent with the 8-bit version, spiWrite(). + Sorry about that. Again, staying compatible with outside code. + @param l 32-bit value to write. +*/ +void Adafruit_SPITFT::SPI_WRITE32(uint32_t l) { + if(connection == TFT_HARD_SPI) { +#if defined(__AVR__) + AVR_WRITESPI(l >> 24); + AVR_WRITESPI(l >> 16); + AVR_WRITESPI(l >> 8); + AVR_WRITESPI(l ); +#elif defined(ESP8266) || defined(ESP32) + hwspi._spi->write32(l); +#else + hwspi._spi->transfer(l >> 24); + hwspi._spi->transfer(l >> 16); + hwspi._spi->transfer(l >> 8); + hwspi._spi->transfer(l); +#endif + } else if(connection == TFT_SOFT_SPI) { + for(uint8_t bit=0; bit<32; bit++) { + if(l & 0x80000000) SPI_MOSI_HIGH(); + else SPI_MOSI_LOW(); + SPI_SCK_HIGH(); + SPI_SCK_LOW(); + l <<= 1; + } + } else { // TFT_PARALLEL +#if defined(__AVR__) + *tft8.writePort = l >> 24; + TFT_WR_STROBE(); + *tft8.writePort = l >> 16; + TFT_WR_STROBE(); + *tft8.writePort = l >> 8; + TFT_WR_STROBE(); + *tft8.writePort = l; +#elif defined(USE_FAST_PINIO) + if(!tft8.wide) { + *tft8.writePort = l >> 24; + TFT_WR_STROBE(); + *tft8.writePort = l >> 16; + TFT_WR_STROBE(); + *tft8.writePort = l >> 8; + TFT_WR_STROBE(); + *tft8.writePort = l; + } else { + *(volatile uint16_t *)tft8.writePort = l >> 16; + TFT_WR_STROBE(); + *(volatile uint16_t *)tft8.writePort = l; + } +#endif + TFT_WR_STROBE(); + } +} + +/*! + @brief Set the WR line LOW, then HIGH. Used for parallel-connected + interfaces when writing data. +*/ +inline void Adafruit_SPITFT::TFT_WR_STROBE(void) { +#if defined(USE_FAST_PINIO) + #if defined(HAS_PORT_SET_CLR) + #if defined(KINETISK) + *tft8.wrPortClr = 1; + *tft8.wrPortSet = 1; + #else // !KINETISK + *tft8.wrPortClr = tft8.wrPinMask; + *tft8.wrPortSet = tft8.wrPinMask; + #endif // end !KINETISK + #else // !HAS_PORT_SET_CLR + *tft8.wrPort &= tft8.wrPinMaskClr; + *tft8.wrPort |= tft8.wrPinMaskSet; + #endif // end !HAS_PORT_SET_CLR +#else // !USE_FAST_PINIO + digitalWrite(tft8._wr, LOW); + digitalWrite(tft8._wr, HIGH); +#endif // end !USE_FAST_PINIO +} + +/*! + @brief Set the RD line HIGH. Used for parallel-connected interfaces + when reading data. +*/ +inline void Adafruit_SPITFT::TFT_RD_HIGH(void) { +#if defined(USE_FAST_PINIO) + #if defined(HAS_PORT_SET_CLR) + *tft8.rdPortSet = tft8.rdPinMask; + #else // !HAS_PORT_SET_CLR + *tft8.rdPort |= tft8.rdPinMaskSet; + #endif // end !HAS_PORT_SET_CLR +#else // !USE_FAST_PINIO + digitalWrite(tft8._rd, HIGH); +#endif // end !USE_FAST_PINIO +} + +/*! + @brief Set the RD line LOW. Used for parallel-connected interfaces + when reading data. +*/ +inline void Adafruit_SPITFT::TFT_RD_LOW(void) { +#if defined(USE_FAST_PINIO) + #if defined(HAS_PORT_SET_CLR) + *tft8.rdPortClr = tft8.rdPinMask; + #else // !HAS_PORT_SET_CLR + *tft8.rdPort &= tft8.rdPinMaskClr; + #endif // end !HAS_PORT_SET_CLR +#else // !USE_FAST_PINIO + digitalWrite(tft8._rd, LOW); +#endif // end !USE_FAST_PINIO +} + +#endif // end __AVR_ATtiny85__ diff --git a/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Adafruit_SPITFT.h b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Adafruit_SPITFT.h new file mode 100644 index 000000000..33a911774 --- /dev/null +++ b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Adafruit_SPITFT.h @@ -0,0 +1,519 @@ +/*! + * @file Adafruit_SPITFT.h + * + * Part of Adafruit's GFX graphics library. Originally this class was + * written to handle a range of color TFT displays connected via SPI, + * but over time this library and some display-specific subclasses have + * mutated to include some color OLEDs as well as parallel-interfaced + * displays. The name's been kept for the sake of older code. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Written by Limor "ladyada" Fried for Adafruit Industries, + * with contributions from the open source community. + * + * BSD license, all text here must be included in any redistribution. + */ + +#ifndef _ADAFRUIT_SPITFT_H_ +#define _ADAFRUIT_SPITFT_H_ + +#if !defined(__AVR_ATtiny85__) // Not for ATtiny, at all + +#include +#include "Adafruit_GFX.h" + +// HARDWARE CONFIG --------------------------------------------------------- + +#if defined(__AVR__) + typedef uint8_t ADAGFX_PORT_t; ///< PORT values are 8-bit + #define USE_FAST_PINIO ///< Use direct PORT register access +#elif defined(ARDUINO_STM32_FEATHER) // WICED + typedef class HardwareSPI SPIClass; ///< SPI is a bit odd on WICED + typedef uint32_t ADAGFX_PORT_t; ///< PORT values are 32-bit +#elif defined(__arm__) + #if defined(ARDUINO_ARCH_SAMD) + // Adafruit M0, M4 + typedef uint32_t ADAGFX_PORT_t; ///< PORT values are 32-bit + #define USE_FAST_PINIO ///< Use direct PORT register access + #define HAS_PORT_SET_CLR ///< PORTs have set & clear registers + #elif defined(CORE_TEENSY) + // PJRC Teensy 4.x + #if defined(__IMXRT1052__) || defined(__IMXRT1062__) // Teensy 4.x + typedef uint32_t ADAGFX_PORT_t; ///< PORT values are 32-bit + // PJRC Teensy 3.x + #else + typedef uint8_t ADAGFX_PORT_t; ///< PORT values are 8-bit + #endif + #define USE_FAST_PINIO ///< Use direct PORT register access + #define HAS_PORT_SET_CLR ///< PORTs have set & clear registers + #else + // Arduino Due? + typedef uint32_t ADAGFX_PORT_t; ///< PORT values are 32-bit + // USE_FAST_PINIO not available here (yet)...Due has a totally different + // GPIO register set and will require some changes elsewhere (e.g. in + // constructors especially). + #endif +#else // !ARM + // Probably ESP8266 or ESP32. USE_FAST_PINIO is not available here (yet) + // but don't worry about it too much...the digitalWrite() implementation + // on these platforms is reasonably efficient and already RAM-resident, + // only gotcha then is no parallel connection support for now. + typedef uint32_t ADAGFX_PORT_t; ///< PORT values are 32-bit +#endif // end !ARM +typedef volatile ADAGFX_PORT_t* PORTreg_t; ///< PORT register type + +#if defined(__AVR__) + #define DEFAULT_SPI_FREQ 8000000L ///< Hardware SPI default speed +#else + #define DEFAULT_SPI_FREQ 16000000L ///< Hardware SPI default speed +#endif + +#if defined(ADAFRUIT_PYPORTAL) || defined(ADAFRUIT_PYBADGE_M4_EXPRESS) || defined(ADAFRUIT_PYGAMER_M4_EXPRESS) + #define USE_SPI_DMA ///< Auto DMA if using PyPortal +#else + //#define USE_SPI_DMA ///< If set, use DMA if available +#endif +// Another "oops" name -- this now also handles parallel DMA. +// If DMA is enabled, Arduino sketch MUST #include +// Estimated RAM usage: +// 4 bytes/pixel on display major axis + 8 bytes/pixel on minor axis, +// e.g. 320x240 pixels = 320 * 4 + 240 * 8 = 3,200 bytes. + +#if !defined(ARDUINO_ARCH_SAMD) + #undef USE_SPI_DMA ///< DMA currently for SAMD chips only +#endif + +#if defined(USE_SPI_DMA) + #pragma message ("GFX DMA IS ENABLED. HIGHLY EXPERIMENTAL.") + #include +#endif + +// This is kind of a kludge. Needed a way to disambiguate the software SPI +// and parallel constructors via their argument lists. Originally tried a +// bool as the first argument to the parallel constructor (specifying 8-bit +// vs 16-bit interface) but the compiler regards this as equivalent to an +// integer and thus still ambiguous. SO...the parallel constructor requires +// an enumerated type as the first argument: tft8 (for 8-bit parallel) or +// tft16 (for 16-bit)...even though 16-bit isn't fully implemented or tested +// and might never be, still needed that disambiguation from soft SPI. +enum tftBusWidth { tft8bitbus, tft16bitbus }; ///< For first arg to parallel constructor + +// CLASS DEFINITION -------------------------------------------------------- + +/*! + @brief Adafruit_SPITFT is an intermediary class between Adafruit_GFX + and various hardware-specific subclasses for different displays. + It handles certain operations that are common to a range of + displays (address window, area fills, etc.). Originally these were + all color TFT displays interfaced via SPI, but it's since expanded + to include color OLEDs and parallel-interfaced TFTs. THE NAME HAS + BEEN KEPT TO AVOID BREAKING A LOT OF SUBCLASSES AND EXAMPLE CODE. + Many of the class member functions similarly live on with names + that don't necessarily accurately describe what they're doing, + again to avoid breaking a lot of other code. If in doubt, read + the comments. +*/ +class Adafruit_SPITFT : public Adafruit_GFX { + + public: + + // CONSTRUCTORS -------------------------------------------------------- + + // Software SPI constructor: expects width & height (at default rotation + // setting 0), 4 signal pins (cs, dc, mosi, sclk), 2 optional pins + // (reset, miso). cs argument is required but can be -1 if unused -- + // rather than moving it to the optional arguments, it was done this way + // to avoid breaking existing code (-1 option was a later addition). + Adafruit_SPITFT(uint16_t w, uint16_t h, + int8_t cs, int8_t dc, int8_t mosi, int8_t sck, + int8_t rst = -1, int8_t miso = -1); + + // Hardware SPI constructor using the default SPI port: expects width & + // height (at default rotation setting 0), 2 signal pins (cs, dc), + // optional reset pin. cs is required but can be -1 if unused -- rather + // than moving it to the optional arguments, it was done this way to + // avoid breaking existing code (-1 option was a later addition). + Adafruit_SPITFT(uint16_t w, uint16_t h, + int8_t cs, int8_t dc, int8_t rst = -1); + +#if !defined(ESP8266) // See notes in .cpp + // Hardware SPI constructor using an arbitrary SPI peripheral: expects + // width & height (rotation 0), SPIClass pointer, 2 signal pins (cs, dc) + // and optional reset pin. cs is required but can be -1 if unused. + Adafruit_SPITFT(uint16_t w, uint16_t h, SPIClass *spiClass, + int8_t cs, int8_t dc, int8_t rst = -1); +#endif // end !ESP8266 + + // Parallel constructor: expects width & height (rotation 0), flag + // indicating whether 16-bit (true) or 8-bit (false) interface, 3 signal + // pins (d0, wr, dc), 3 optional pins (cs, rst, rd). 16-bit parallel + // isn't even fully implemented but the 'wide' flag was added as a + // required argument to avoid ambiguity with other constructors. + Adafruit_SPITFT(uint16_t w, uint16_t h, tftBusWidth busWidth, + int8_t d0, int8_t wr, int8_t dc, + int8_t cs = -1, int8_t rst = -1, int8_t rd = -1); + + // CLASS MEMBER FUNCTIONS ---------------------------------------------- + + // These first two functions MUST be declared by subclasses: + + /*! + @brief Display-specific initialization function. + @param freq SPI frequency, in hz (or 0 for default or unused). + */ + virtual void begin(uint32_t freq) = 0; + + /*! + @brief Set up the specific display hardware's "address window" + for subsequent pixel-pushing operations. + @param x Leftmost pixel of area to be drawn (MUST be within + display bounds at current rotation setting). + @param y Topmost pixel of area to be drawn (MUST be within + display bounds at current rotation setting). + @param w Width of area to be drawn, in pixels (MUST be >0 and, + added to x, within display bounds at current rotation). + @param h Height of area to be drawn, in pixels (MUST be >0 and, + added to x, within display bounds at current rotation). + */ + virtual void setAddrWindow( + uint16_t x, uint16_t y, uint16_t w, uint16_t h) = 0; + + // Remaining functions do not need to be declared in subclasses + // unless they wish to provide hardware-specific optimizations. + // Brief comments here...documented more thoroughly in .cpp file. + + // Subclass' begin() function invokes this to initialize hardware. + // freq=0 to use default SPI speed. spiMode must be one of the SPI_MODEn + // values defined in SPI.h, which are NOT the same as 0 for SPI_MODE0, + // 1 for SPI_MODE1, etc...use ONLY the SPI_MODEn defines! Only! + // Name is outdated (interface may be parallel) but for compatibility: + void initSPI(uint32_t freq = 0, uint8_t spiMode = SPI_MODE0); + // Chip select and/or hardware SPI transaction start as needed: + void startWrite(void); + // Chip deselect and/or hardware SPI transaction end as needed: + void endWrite(void); + void sendCommand(uint8_t commandByte, uint8_t *dataBytes = NULL, uint8_t numDataBytes = 0); + void sendCommand(uint8_t commandByte, const uint8_t *dataBytes, uint8_t numDataBytes); + uint8_t readcommand8(uint8_t commandByte, uint8_t index = 0); + + // These functions require a chip-select and/or SPI transaction + // around them. Higher-level graphics primitives might start a + // single transaction and then make multiple calls to these functions + // (e.g. circle or text rendering might make repeated lines or rects) + // before ending the transaction. It's more efficient than starting a + // transaction every time. + void writePixel(int16_t x, int16_t y, uint16_t color); + void writePixels(uint16_t *colors, uint32_t len, + bool block=true, bool bigEndian=false); + void writeColor(uint16_t color, uint32_t len); + void writeFillRect(int16_t x, int16_t y, int16_t w, int16_t h, + uint16_t color); + void writeFastHLine(int16_t x, int16_t y, int16_t w, + uint16_t color); + void writeFastVLine(int16_t x, int16_t y, int16_t h, + uint16_t color); + // This is a new function, similar to writeFillRect() except that + // all arguments MUST be onscreen, sorted and clipped. If higher-level + // primitives can handle their own sorting/clipping, it avoids repeating + // such operations in the low-level code, making it potentially faster. + // CALLING THIS WITH UNCLIPPED OR NEGATIVE VALUES COULD BE DISASTROUS. + inline void writeFillRectPreclipped(int16_t x, int16_t y, + int16_t w, int16_t h, uint16_t color); + // Another new function, companion to the new non-blocking + // writePixels() variant. + void dmaWait(void); + + + // These functions are similar to the 'write' functions above, but with + // a chip-select and/or SPI transaction built-in. They're typically used + // solo -- that is, as graphics primitives in themselves, not invoked by + // higher-level primitives (which should use the functions above). + void drawPixel(int16_t x, int16_t y, uint16_t color); + void fillRect(int16_t x, int16_t y, int16_t w, int16_t h, + uint16_t color); + void drawFastHLine(int16_t x, int16_t y, int16_t w, + uint16_t color); + void drawFastVLine(int16_t x, int16_t y, int16_t h, + uint16_t color); + // A single-pixel push encapsulated in a transaction. I don't think + // this is used anymore (BMP demos might've used it?) but is provided + // for backward compatibility, consider it deprecated: + void pushColor(uint16_t color); + + using Adafruit_GFX::drawRGBBitmap; // Check base class first + void drawRGBBitmap(int16_t x, int16_t y, + uint16_t *pcolors, int16_t w, int16_t h); + + void invertDisplay(bool i); + uint16_t color565(uint8_t r, uint8_t g, uint8_t b); + + // Despite parallel additions, function names kept for compatibility: + void spiWrite(uint8_t b); // Write single byte as DATA + void writeCommand(uint8_t cmd); // Write single byte as COMMAND + uint8_t spiRead(void); // Read single byte of data + + // Most of these low-level functions were formerly macros in + // Adafruit_SPITFT_Macros.h. Some have been made into inline functions + // to avoid macro mishaps. Despite the addition of code for a parallel + // display interface, the names have been kept for backward + // compatibility (some subclasses may be invoking these): + void SPI_WRITE16(uint16_t w); // Not inline + void SPI_WRITE32(uint32_t l); // Not inline + // Old code had both a spiWrite16() function and SPI_WRITE16 macro + // in addition to the SPI_WRITE32 macro. The latter two have been + // made into functions here, and spiWrite16() removed (use SPI_WRITE16() + // instead). It looks like most subclasses had gotten comfortable with + // SPI_WRITE16 and SPI_WRITE32 anyway so those names were kept rather + // than the less-obnoxious camelcase variants, oh well. + + // Placing these functions entirely in the class definition inlines + // them implicitly them while allowing their use in other code: + + /*! + @brief Set the chip-select line HIGH. Does NOT check whether CS pin + is set (>=0), that should be handled in calling function. + Despite function name, this is used even if the display + connection is parallel. + */ + void SPI_CS_HIGH(void) { + #if defined(USE_FAST_PINIO) + #if defined(HAS_PORT_SET_CLR) + #if defined(KINETISK) + *csPortSet = 1; + #else // !KINETISK + *csPortSet = csPinMask; + #endif // end !KINETISK + #else // !HAS_PORT_SET_CLR + *csPort |= csPinMaskSet; + #endif // end !HAS_PORT_SET_CLR + #else // !USE_FAST_PINIO + digitalWrite(_cs, HIGH); + #endif // end !USE_FAST_PINIO + } + + /*! + @brief Set the chip-select line LOW. Does NOT check whether CS pin + is set (>=0), that should be handled in calling function. + Despite function name, this is used even if the display + connection is parallel. + */ + void SPI_CS_LOW(void) { + #if defined(USE_FAST_PINIO) + #if defined(HAS_PORT_SET_CLR) + #if defined(KINETISK) + *csPortClr = 1; + #else // !KINETISK + *csPortClr = csPinMask; + #endif // end !KINETISK + #else // !HAS_PORT_SET_CLR + *csPort &= csPinMaskClr; + #endif // end !HAS_PORT_SET_CLR + #else // !USE_FAST_PINIO + digitalWrite(_cs, LOW); + #endif // end !USE_FAST_PINIO + } + + /*! + @brief Set the data/command line HIGH (data mode). + */ + void SPI_DC_HIGH(void) { + #if defined(USE_FAST_PINIO) + #if defined(HAS_PORT_SET_CLR) + #if defined(KINETISK) + *dcPortSet = 1; + #else // !KINETISK + *dcPortSet = dcPinMask; + #endif // end !KINETISK + #else // !HAS_PORT_SET_CLR + *dcPort |= dcPinMaskSet; + #endif // end !HAS_PORT_SET_CLR + #else // !USE_FAST_PINIO + digitalWrite(_dc, HIGH); + #endif // end !USE_FAST_PINIO + } + + /*! + @brief Set the data/command line LOW (command mode). + */ + void SPI_DC_LOW(void) { + #if defined(USE_FAST_PINIO) + #if defined(HAS_PORT_SET_CLR) + #if defined(KINETISK) + *dcPortClr = 1; + #else // !KINETISK + *dcPortClr = dcPinMask; + #endif // end !KINETISK + #else // !HAS_PORT_SET_CLR + *dcPort &= dcPinMaskClr; + #endif // end !HAS_PORT_SET_CLR + #else // !USE_FAST_PINIO + digitalWrite(_dc, LOW); + #endif // end !USE_FAST_PINIO + } + + protected: + + // A few more low-level member functions -- some may have previously + // been macros. Shouldn't have a need to access these externally, so + // they've been moved to the protected section. Additionally, they're + // declared inline here and the code is in the .cpp file, since outside + // code doesn't need to see these. + inline void SPI_MOSI_HIGH(void); + inline void SPI_MOSI_LOW(void); + inline void SPI_SCK_HIGH(void); + inline void SPI_SCK_LOW(void); + inline bool SPI_MISO_READ(void); + inline void SPI_BEGIN_TRANSACTION(void); + inline void SPI_END_TRANSACTION(void); + inline void TFT_WR_STROBE(void); // Parallel interface write strobe + inline void TFT_RD_HIGH(void); // Parallel interface read high + inline void TFT_RD_LOW(void); // Parallel interface read low + + // CLASS INSTANCE VARIABLES -------------------------------------------- + + // Here be dragons! There's a big union of three structures here -- + // one each for hardware SPI, software (bitbang) SPI, and parallel + // interfaces. This is to save some memory, since a display's connection + // will be only one of these. The order of some things is a little weird + // in an attempt to get values to align and pack better in RAM. + +#if defined(USE_FAST_PINIO) +#if defined(HAS_PORT_SET_CLR) + PORTreg_t csPortSet; ///< PORT register for chip select SET + PORTreg_t csPortClr; ///< PORT register for chip select CLEAR + PORTreg_t dcPortSet; ///< PORT register for data/command SET + PORTreg_t dcPortClr; ///< PORT register for data/command CLEAR +#else // !HAS_PORT_SET_CLR + PORTreg_t csPort; ///< PORT register for chip select + PORTreg_t dcPort; ///< PORT register for data/command +#endif // end HAS_PORT_SET_CLR +#endif // end USE_FAST_PINIO +#if defined(__cplusplus) && (__cplusplus >= 201100) + union { +#endif + struct { // Values specific to HARDWARE SPI: + SPIClass *_spi; ///< SPI class pointer +#if defined(SPI_HAS_TRANSACTION) + SPISettings settings; ///< SPI transaction settings +#else + uint32_t _freq; ///< SPI bitrate (if no SPI transactions) +#endif + uint32_t _mode; ///< SPI data mode (transactions or no) + } hwspi; ///< Hardware SPI values + struct { // Values specific to SOFTWARE SPI: +#if defined(USE_FAST_PINIO) + PORTreg_t misoPort; ///< PORT (PIN) register for MISO +#if defined(HAS_PORT_SET_CLR) + PORTreg_t mosiPortSet; ///< PORT register for MOSI SET + PORTreg_t mosiPortClr; ///< PORT register for MOSI CLEAR + PORTreg_t sckPortSet; ///< PORT register for SCK SET + PORTreg_t sckPortClr; ///< PORT register for SCK CLEAR + #if !defined(KINETISK) + ADAGFX_PORT_t mosiPinMask; ///< Bitmask for MOSI + ADAGFX_PORT_t sckPinMask; ///< Bitmask for SCK + #endif // end !KINETISK +#else // !HAS_PORT_SET_CLR + PORTreg_t mosiPort; ///< PORT register for MOSI + PORTreg_t sckPort; ///< PORT register for SCK + ADAGFX_PORT_t mosiPinMaskSet; ///< Bitmask for MOSI SET (OR) + ADAGFX_PORT_t mosiPinMaskClr; ///< Bitmask for MOSI CLEAR (AND) + ADAGFX_PORT_t sckPinMaskSet; ///< Bitmask for SCK SET (OR bitmask) + ADAGFX_PORT_t sckPinMaskClr; ///< Bitmask for SCK CLEAR (AND) +#endif // end HAS_PORT_SET_CLR + #if !defined(KINETISK) + ADAGFX_PORT_t misoPinMask; ///< Bitmask for MISO + #endif // end !KINETISK +#endif // end USE_FAST_PINIO + int8_t _mosi; ///< MOSI pin # + int8_t _miso; ///< MISO pin # + int8_t _sck; ///< SCK pin # + } swspi; ///< Software SPI values + struct { // Values specific to 8-bit parallel: +#if defined(USE_FAST_PINIO) + + #if defined(__IMXRT1052__) || defined(__IMXRT1062__) // Teensy 4.x + volatile uint32_t *writePort; ///< PORT register for DATA WRITE + volatile uint32_t *readPort; ///< PORT (PIN) register for DATA READ + #else + volatile uint8_t *writePort; ///< PORT register for DATA WRITE + volatile uint8_t *readPort; ///< PORT (PIN) register for DATA READ + #endif +#if defined(HAS_PORT_SET_CLR) + // Port direction register pointers are always 8-bit regardless of + // PORTreg_t -- even if 32-bit port, we modify a byte-aligned 8 bits. + #if defined(__IMXRT1052__) || defined(__IMXRT1062__) // Teensy 4.x + volatile uint32_t *dirSet; ///< PORT byte data direction SET + volatile uint32_t *dirClr; ///< PORT byte data direction CLEAR + #else + volatile uint8_t *dirSet; ///< PORT byte data direction SET + volatile uint8_t *dirClr; ///< PORT byte data direction CLEAR + #endif + PORTreg_t wrPortSet; ///< PORT register for write strobe SET + PORTreg_t wrPortClr; ///< PORT register for write strobe CLEAR + PORTreg_t rdPortSet; ///< PORT register for read strobe SET + PORTreg_t rdPortClr; ///< PORT register for read strobe CLEAR + #if !defined(KINETISK) + ADAGFX_PORT_t wrPinMask; ///< Bitmask for write strobe + #endif // end !KINETISK + ADAGFX_PORT_t rdPinMask; ///< Bitmask for read strobe +#else // !HAS_PORT_SET_CLR + // Port direction register pointer is always 8-bit regardless of + // PORTreg_t -- even if 32-bit port, we modify a byte-aligned 8 bits. + volatile uint8_t *portDir; ///< PORT direction register + PORTreg_t wrPort; ///< PORT register for write strobe + PORTreg_t rdPort; ///< PORT register for read strobe + ADAGFX_PORT_t wrPinMaskSet; ///< Bitmask for write strobe SET (OR) + ADAGFX_PORT_t wrPinMaskClr; ///< Bitmask for write strobe CLEAR (AND) + ADAGFX_PORT_t rdPinMaskSet; ///< Bitmask for read strobe SET (OR) + ADAGFX_PORT_t rdPinMaskClr; ///< Bitmask for read strobe CLEAR (AND) +#endif // end HAS_PORT_SET_CLR +#endif // end USE_FAST_PINIO + int8_t _d0; ///< Data pin 0 # + int8_t _wr; ///< Write strobe pin # + int8_t _rd; ///< Read strobe pin # (or -1) + bool wide = 0; ///< If true, is 16-bit interface + } tft8; ///< Parallel interface settings +#if defined(__cplusplus) && (__cplusplus >= 201100) + }; ///< Only one interface is active +#endif +#if defined(USE_SPI_DMA) // Used by hardware SPI and tft8 + Adafruit_ZeroDMA dma; ///< DMA instance + DmacDescriptor *dptr = NULL; ///< 1st descriptor + DmacDescriptor *descriptor = NULL; ///< Allocated descriptor list + uint16_t *pixelBuf[2]; ///< Working buffers + uint16_t maxFillLen; ///< Max pixels per DMA xfer + uint16_t lastFillColor = 0; ///< Last color used w/fill + uint32_t lastFillLen = 0; ///< # of pixels w/last fill + uint8_t onePixelBuf; ///< For hi==lo fill +#endif +#if defined(USE_FAST_PINIO) +#if defined(HAS_PORT_SET_CLR) + #if !defined(KINETISK) + ADAGFX_PORT_t csPinMask; ///< Bitmask for chip select + ADAGFX_PORT_t dcPinMask; ///< Bitmask for data/command + #endif // end !KINETISK +#else // !HAS_PORT_SET_CLR + ADAGFX_PORT_t csPinMaskSet; ///< Bitmask for chip select SET (OR) + ADAGFX_PORT_t csPinMaskClr; ///< Bitmask for chip select CLEAR (AND) + ADAGFX_PORT_t dcPinMaskSet; ///< Bitmask for data/command SET (OR) + ADAGFX_PORT_t dcPinMaskClr; ///< Bitmask for data/command CLEAR (AND) +#endif // end HAS_PORT_SET_CLR +#endif // end USE_FAST_PINIO + uint8_t connection; ///< TFT_HARD_SPI, TFT_SOFT_SPI, etc. + int8_t _rst; ///< Reset pin # (or -1) + int8_t _cs; ///< Chip select pin # (or -1) + int8_t _dc; ///< Data/command pin # + + int16_t _xstart = 0; ///< Internal framebuffer X offset + int16_t _ystart = 0; ///< Internal framebuffer Y offset + uint8_t invertOnCommand = 0; ///< Command to enable invert mode + uint8_t invertOffCommand = 0; ///< Command to disable invert mode + + uint32_t _freq = 0; ///< Dummy var to keep subclasses happy +}; + +#endif // end __AVR_ATtiny85__ +#endif // end _ADAFRUIT_SPITFT_H_ diff --git a/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Adafruit_SPITFT_Macros.h b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Adafruit_SPITFT_Macros.h new file mode 100644 index 000000000..fcd6253e6 --- /dev/null +++ b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Adafruit_SPITFT_Macros.h @@ -0,0 +1,6 @@ +// THIS FILE INTENTIONALLY LEFT BLANK. + +// Macros previously #defined here have been made into (mostly) inline +// functions in the Adafruit_SPITFT class. Other libraries might still +// contain code trying to #include this header file, so until everything's +// updated this file still exists (but doing nothing) to avoid trouble. diff --git a/lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeMono12pt7b.h b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMono12pt7b.h similarity index 100% rename from lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeMono12pt7b.h rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMono12pt7b.h diff --git a/lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeMono18pt7b.h b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMono18pt7b.h similarity index 100% rename from lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeMono18pt7b.h rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMono18pt7b.h diff --git a/lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeMono24pt7b.h b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMono24pt7b.h similarity index 100% rename from lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeMono24pt7b.h rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMono24pt7b.h diff --git a/lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeMono9pt7b.h b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMono9pt7b.h similarity index 100% rename from lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeMono9pt7b.h rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMono9pt7b.h diff --git a/lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeMonoBold12pt7b.h b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoBold12pt7b.h similarity index 100% rename from lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeMonoBold12pt7b.h rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoBold12pt7b.h diff --git a/lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeMonoBold18pt7b.h b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoBold18pt7b.h similarity index 100% rename from lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeMonoBold18pt7b.h rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoBold18pt7b.h diff --git a/lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeMonoBold24pt7b.h b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoBold24pt7b.h similarity index 100% rename from lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeMonoBold24pt7b.h rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoBold24pt7b.h diff --git a/lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeMonoBold9pt7b.h b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoBold9pt7b.h similarity index 100% rename from lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeMonoBold9pt7b.h rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoBold9pt7b.h diff --git a/lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeMonoBoldOblique12pt7b.h b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoBoldOblique12pt7b.h similarity index 100% rename from lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeMonoBoldOblique12pt7b.h rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoBoldOblique12pt7b.h diff --git a/lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeMonoBoldOblique18pt7b.h b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoBoldOblique18pt7b.h similarity index 100% rename from lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeMonoBoldOblique18pt7b.h rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoBoldOblique18pt7b.h diff --git a/lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeMonoBoldOblique24pt7b.h b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoBoldOblique24pt7b.h similarity index 100% rename from lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeMonoBoldOblique24pt7b.h rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoBoldOblique24pt7b.h diff --git a/lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeMonoBoldOblique9pt7b.h b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoBoldOblique9pt7b.h similarity index 100% rename from lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeMonoBoldOblique9pt7b.h rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoBoldOblique9pt7b.h diff --git a/lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeMonoOblique12pt7b.h b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoOblique12pt7b.h similarity index 100% rename from lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeMonoOblique12pt7b.h rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoOblique12pt7b.h diff --git a/lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeMonoOblique18pt7b.h b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoOblique18pt7b.h similarity index 100% rename from lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeMonoOblique18pt7b.h rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoOblique18pt7b.h diff --git a/lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeMonoOblique24pt7b.h b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoOblique24pt7b.h similarity index 100% rename from lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeMonoOblique24pt7b.h rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoOblique24pt7b.h diff --git a/lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeMonoOblique9pt7b.h b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoOblique9pt7b.h similarity index 100% rename from lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeMonoOblique9pt7b.h rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeMonoOblique9pt7b.h diff --git a/lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSans12pt7b.h b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSans12pt7b.h similarity index 100% rename from lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSans12pt7b.h rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSans12pt7b.h diff --git a/lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSans18pt7b.h b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSans18pt7b.h similarity index 100% rename from lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSans18pt7b.h rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSans18pt7b.h diff --git a/lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSans24pt7b.h b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSans24pt7b.h similarity index 100% rename from lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSans24pt7b.h rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSans24pt7b.h diff --git a/lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSans9pt7b.h b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSans9pt7b.h similarity index 100% rename from lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSans9pt7b.h rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSans9pt7b.h diff --git a/lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSansBold12pt7b.h b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansBold12pt7b.h similarity index 100% rename from lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSansBold12pt7b.h rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansBold12pt7b.h diff --git a/lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSansBold18pt7b.h b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansBold18pt7b.h similarity index 100% rename from lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSansBold18pt7b.h rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansBold18pt7b.h diff --git a/lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSansBold24pt7b.h b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansBold24pt7b.h similarity index 100% rename from lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSansBold24pt7b.h rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansBold24pt7b.h diff --git a/lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSansBold9pt7b.h b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansBold9pt7b.h similarity index 100% rename from lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSansBold9pt7b.h rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansBold9pt7b.h diff --git a/lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSansBoldOblique12pt7b.h b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansBoldOblique12pt7b.h similarity index 100% rename from lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSansBoldOblique12pt7b.h rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansBoldOblique12pt7b.h diff --git a/lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSansBoldOblique18pt7b.h b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansBoldOblique18pt7b.h similarity index 100% rename from lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSansBoldOblique18pt7b.h rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansBoldOblique18pt7b.h diff --git a/lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSansBoldOblique24pt7b.h b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansBoldOblique24pt7b.h similarity index 100% rename from lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSansBoldOblique24pt7b.h rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansBoldOblique24pt7b.h diff --git a/lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSansBoldOblique9pt7b.h b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansBoldOblique9pt7b.h similarity index 100% rename from lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSansBoldOblique9pt7b.h rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansBoldOblique9pt7b.h diff --git a/lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSansOblique12pt7b.h b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansOblique12pt7b.h similarity index 100% rename from lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSansOblique12pt7b.h rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansOblique12pt7b.h diff --git a/lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSansOblique18pt7b.h b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansOblique18pt7b.h similarity index 100% rename from lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSansOblique18pt7b.h rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansOblique18pt7b.h diff --git a/lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSansOblique24pt7b.h b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansOblique24pt7b.h similarity index 100% rename from lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSansOblique24pt7b.h rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansOblique24pt7b.h diff --git a/lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSansOblique9pt7b.h b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansOblique9pt7b.h similarity index 100% rename from lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSansOblique9pt7b.h rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSansOblique9pt7b.h diff --git a/lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSerif12pt7b.h b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerif12pt7b.h similarity index 100% rename from lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSerif12pt7b.h rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerif12pt7b.h diff --git a/lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSerif18pt7b.h b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerif18pt7b.h similarity index 100% rename from lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSerif18pt7b.h rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerif18pt7b.h diff --git a/lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSerif24pt7b.h b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerif24pt7b.h similarity index 100% rename from lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSerif24pt7b.h rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerif24pt7b.h diff --git a/lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSerif9pt7b.h b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerif9pt7b.h similarity index 100% rename from lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSerif9pt7b.h rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerif9pt7b.h diff --git a/lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSerifBold12pt7b.h b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifBold12pt7b.h similarity index 100% rename from lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSerifBold12pt7b.h rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifBold12pt7b.h diff --git a/lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSerifBold18pt7b.h b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifBold18pt7b.h similarity index 100% rename from lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSerifBold18pt7b.h rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifBold18pt7b.h diff --git a/lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSerifBold24pt7b.h b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifBold24pt7b.h similarity index 100% rename from lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSerifBold24pt7b.h rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifBold24pt7b.h diff --git a/lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSerifBold9pt7b.h b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifBold9pt7b.h similarity index 100% rename from lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSerifBold9pt7b.h rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifBold9pt7b.h diff --git a/lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSerifBoldItalic12pt7b.h b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifBoldItalic12pt7b.h similarity index 100% rename from lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSerifBoldItalic12pt7b.h rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifBoldItalic12pt7b.h diff --git a/lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSerifBoldItalic18pt7b.h b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifBoldItalic18pt7b.h similarity index 100% rename from lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSerifBoldItalic18pt7b.h rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifBoldItalic18pt7b.h diff --git a/lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSerifBoldItalic24pt7b.h b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifBoldItalic24pt7b.h similarity index 100% rename from lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSerifBoldItalic24pt7b.h rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifBoldItalic24pt7b.h diff --git a/lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSerifBoldItalic9pt7b.h b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifBoldItalic9pt7b.h similarity index 100% rename from lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSerifBoldItalic9pt7b.h rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifBoldItalic9pt7b.h diff --git a/lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSerifItalic12pt7b.h b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifItalic12pt7b.h similarity index 100% rename from lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSerifItalic12pt7b.h rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifItalic12pt7b.h diff --git a/lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSerifItalic18pt7b.h b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifItalic18pt7b.h similarity index 100% rename from lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSerifItalic18pt7b.h rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifItalic18pt7b.h diff --git a/lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSerifItalic24pt7b.h b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifItalic24pt7b.h similarity index 100% rename from lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSerifItalic24pt7b.h rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifItalic24pt7b.h diff --git a/lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSerifItalic9pt7b.h b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifItalic9pt7b.h similarity index 100% rename from lib/Adafruit-GFX-Library-1.2.9/Fonts/FreeSerifItalic9pt7b.h rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/FreeSerifItalic9pt7b.h diff --git a/lib/Adafruit-GFX-Library-1.2.9/Fonts/Org_01.h b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/Org_01.h similarity index 100% rename from lib/Adafruit-GFX-Library-1.2.9/Fonts/Org_01.h rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/Org_01.h diff --git a/lib/Adafruit-GFX-Library-1.2.9/Fonts/Picopixel.h b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/Picopixel.h similarity index 100% rename from lib/Adafruit-GFX-Library-1.2.9/Fonts/Picopixel.h rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/Picopixel.h diff --git a/lib/Adafruit-GFX-Library-1.2.9/Fonts/Tiny3x3a2pt7b b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/Tiny3x3a2pt7b.h similarity index 100% rename from lib/Adafruit-GFX-Library-1.2.9/Fonts/Tiny3x3a2pt7b rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/Tiny3x3a2pt7b.h diff --git a/lib/Adafruit-GFX-Library-1.2.9/Fonts/TomThumb.h b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/TomThumb.h similarity index 100% rename from lib/Adafruit-GFX-Library-1.2.9/Fonts/TomThumb.h rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/Fonts/TomThumb.h diff --git a/lib/Adafruit-GFX-Library-1.2.9/README.md b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/README.md similarity index 94% rename from lib/Adafruit-GFX-Library-1.2.9/README.md rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/README.md index cd27c33b6..37751c734 100644 --- a/lib/Adafruit-GFX-Library-1.2.9/README.md +++ b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/README.md @@ -1,4 +1,4 @@ -# Adafruit GFX Library # [![Build Status](https://travis-ci.org/adafruit/Adafruit_GFX.svg?branch=master)](https://travis-ci.org/adafruit/Adafruit_GFX) +# Adafruit GFX Library [![Build Status](https://travis-ci.com/adafruit/Adafruit-GFX-Library.svg?branch=master)](https://travis-ci.com/adafruit/Adafruit-GFX-Library) This is the core graphics library for all our displays, providing a common set of graphics primitives (points, lines, circles, etc.). It needs to be paired with a hardware-specific library for each display device we carry (to handle the lower-level functions). diff --git a/lib/Adafruit-GFX-Library-1.2.9/examples/mock_ili9341/mock_ili9341.ino b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/examples/mock_ili9341/mock_ili9341.ino similarity index 99% rename from lib/Adafruit-GFX-Library-1.2.9/examples/mock_ili9341/mock_ili9341.ino rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/examples/mock_ili9341/mock_ili9341.ino index 3154d4095..d14183904 100644 --- a/lib/Adafruit-GFX-Library-1.2.9/examples/mock_ili9341/mock_ili9341.ino +++ b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/examples/mock_ili9341/mock_ili9341.ino @@ -362,4 +362,4 @@ unsigned long testFilledRoundRects() { } return micros() - start; -} \ No newline at end of file +} diff --git a/lib/Adafruit-GFX-Library-1.2.9/fontconvert/Makefile b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/fontconvert/Makefile similarity index 100% rename from lib/Adafruit-GFX-Library-1.2.9/fontconvert/Makefile rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/fontconvert/Makefile diff --git a/lib/Adafruit-GFX-Library-1.2.9/fontconvert/fontconvert.c b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/fontconvert/fontconvert.c similarity index 94% rename from lib/Adafruit-GFX-Library-1.2.9/fontconvert/fontconvert.c rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/fontconvert/fontconvert.c index bfd21103c..b8a6ac944 100644 --- a/lib/Adafruit-GFX-Library-1.2.9/fontconvert/fontconvert.c +++ b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/fontconvert/fontconvert.c @@ -16,12 +16,14 @@ Keep 7-bit fonts around as an option in that case, more compact. See notes at end for glyph nomenclature & other tidbits. */ +#ifndef ARDUINO #include #include #include #include #include FT_GLYPH_H +#include FT_TRUETYPE_DRIVER_H #include "../gfxfont.h" // Adafruit_GFX font structures #define DPI 141 // Approximate res. of Adafruit 2.8" TFT @@ -116,6 +118,16 @@ int main(int argc, char *argv[]) { fprintf(stderr, "FreeType init error: %d", err); return err; } + + // Use TrueType engine version 35, without subpixel rendering. + // This improves clarity of fonts since this library does not + // support rendering multiple levels of gray in a glyph. + // See https://github.com/adafruit/Adafruit-GFX-Library/issues/103 + FT_UInt interpreter_version = TT_INTERPRETER_VERSION_35; + FT_Property_Set( library, "truetype", + "interpreter-version", + &interpreter_version ); + if((err = FT_New_Face(library, argv[1], 0, &face))) { fprintf(stderr, "Font load error: %d", err); FT_Done_FreeType(library); @@ -282,3 +294,5 @@ the cursor on the X axis after drawing the corresponding symbol. There's also some changes with regard to 'background' color and new GFX fonts (classic fonts unchanged). See Adafruit_GFX.cpp for explanation. */ + +#endif /* !ARDUINO */ diff --git a/lib/Adafruit-GFX-Library-1.2.9/fontconvert/fontconvert_win.md b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/fontconvert/fontconvert_win.md similarity index 100% rename from lib/Adafruit-GFX-Library-1.2.9/fontconvert/fontconvert_win.md rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/fontconvert/fontconvert_win.md diff --git a/lib/Adafruit-GFX-Library-1.2.9/fontconvert/makefonts.sh b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/fontconvert/makefonts.sh similarity index 100% rename from lib/Adafruit-GFX-Library-1.2.9/fontconvert/makefonts.sh rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/fontconvert/makefonts.sh diff --git a/lib/Adafruit-GFX-Library-1.2.9/gfxfont.h b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/gfxfont.h similarity index 100% rename from lib/Adafruit-GFX-Library-1.2.9/gfxfont.h rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/gfxfont.h diff --git a/lib/Adafruit-GFX-Library-1.2.9/glcdfont.c b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/glcdfont.c similarity index 98% rename from lib/Adafruit-GFX-Library-1.2.9/glcdfont.c rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/glcdfont.c index 6f88bd23a..50245933c 100644 --- a/lib/Adafruit-GFX-Library-1.2.9/glcdfont.c +++ b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/glcdfont.c @@ -9,6 +9,10 @@ #include #elif defined(ESP8266) #include +#elif defined(__IMXRT1052__) || defined(__IMXRT1062__) +// PROGMEM is defefind for T4 to place data in specific memory section + #undef PROGMEM + #define PROGMEM #else #define PROGMEM #endif diff --git a/lib/Adafruit-GFX-Library-1.2.9/library.properties b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/library.properties similarity index 96% rename from lib/Adafruit-GFX-Library-1.2.9/library.properties rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/library.properties index eea478015..78bbdbafa 100644 --- a/lib/Adafruit-GFX-Library-1.2.9/library.properties +++ b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/library.properties @@ -1,5 +1,5 @@ name=Adafruit GFX Library -version=1.2.9 +version=1.5.6 author=Adafruit maintainer=Adafruit sentence=Adafruit GFX graphics core library, this is the 'core' class that all our other graphics libraries derive from. diff --git a/lib/Adafruit-GFX-Library-1.2.9/license.txt b/lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/license.txt similarity index 100% rename from lib/Adafruit-GFX-Library-1.2.9/license.txt rename to lib/Adafruit-GFX-Library-1.5.6-gemu-1.0/license.txt diff --git a/lib/Adafruit_MAX31865-1.1.0-custom/Adafruit_MAX31865.cpp b/lib/Adafruit_MAX31865-1.1.0-custom/Adafruit_MAX31865.cpp new file mode 100644 index 000000000..984e5572a --- /dev/null +++ b/lib/Adafruit_MAX31865-1.1.0-custom/Adafruit_MAX31865.cpp @@ -0,0 +1,286 @@ +/*************************************************** + This is a library for the Adafruit PT100/P1000 RTD Sensor w/MAX31865 + + Designed specifically to work with the Adafruit RTD Sensor + ----> https://www.adafruit.com/products/3328 + + This sensor uses SPI to communicate, 4 pins are required to + interface + Adafruit invests time and resources providing this open source code, + please support Adafruit and open-source hardware by purchasing + products from Adafruit! + + Written by Limor Fried/Ladyada for Adafruit Industries. + BSD license, all text above must be included in any redistribution + ****************************************************/ + +#include "Adafruit_MAX31865.h" +#ifdef __AVR + #include +#elif defined(ESP8266) + #include +#endif + +#include +#include + +static SPISettings max31865_spisettings = SPISettings(500000, MSBFIRST, SPI_MODE1); + + + +// Software (bitbang) SPI +Adafruit_MAX31865::Adafruit_MAX31865(int8_t spi_cs, int8_t spi_mosi, int8_t spi_miso, int8_t spi_clk) { + setPins( spi_cs, spi_mosi, spi_miso, spi_clk); +} + +void Adafruit_MAX31865::setPins(int8_t spi_cs, int8_t spi_mosi, int8_t spi_miso, int8_t spi_clk) { + _sclk = spi_clk; + _cs = spi_cs; + _miso = spi_miso; + _mosi = spi_mosi; +} + +// Hardware SPI init +Adafruit_MAX31865::Adafruit_MAX31865(int8_t spi_cs) { + _cs = spi_cs; + _sclk = _miso = _mosi = -1; +} + +// Default constructor +Adafruit_MAX31865::Adafruit_MAX31865(void) { + _cs = _sclk = _miso = _mosi = -1; +} + +boolean Adafruit_MAX31865::begin(max31865_numwires_t wires) { + pinMode(_cs, OUTPUT); + digitalWrite(_cs, HIGH); + + if (_sclk != -1) { + //define pin modes + pinMode(_sclk, OUTPUT); + digitalWrite(_sclk, LOW); + pinMode(_mosi, OUTPUT); + pinMode(_miso, INPUT); + } else { + //start and configure hardware SPI + SPI.begin(); + } + + for (uint8_t i=0; i<16; i++) { + // readRegister8(i); + } + + setWires(wires); + enableBias(false); + autoConvert(false); + clearFault(); + + //Serial.print("config: "); Serial.println(readRegister8(MAX31856_CONFIG_REG), HEX); + return true; +} + + +uint8_t Adafruit_MAX31865::readFault(void) { + return readRegister8(MAX31856_FAULTSTAT_REG); +} + +void Adafruit_MAX31865::clearFault(void) { + uint8_t t = readRegister8(MAX31856_CONFIG_REG); + t &= ~0x2C; + t |= MAX31856_CONFIG_FAULTSTAT; + writeRegister8(MAX31856_CONFIG_REG, t); +} + +void Adafruit_MAX31865::enableBias(boolean b) { + uint8_t t = readRegister8(MAX31856_CONFIG_REG); + if (b) { + t |= MAX31856_CONFIG_BIAS; // enable bias + } else { + t &= ~MAX31856_CONFIG_BIAS; // disable bias + } + writeRegister8(MAX31856_CONFIG_REG, t); +} + +void Adafruit_MAX31865::autoConvert(boolean b) { + uint8_t t = readRegister8(MAX31856_CONFIG_REG); + if (b) { + t |= MAX31856_CONFIG_MODEAUTO; // enable autoconvert + } else { + t &= ~MAX31856_CONFIG_MODEAUTO; // disable autoconvert + } + writeRegister8(MAX31856_CONFIG_REG, t); +} + +void Adafruit_MAX31865::setWires(max31865_numwires_t wires ) { + uint8_t t = readRegister8(MAX31856_CONFIG_REG); + if (wires == MAX31865_3WIRE) { + t |= MAX31856_CONFIG_3WIRE; + } else { + // 2 or 4 wire + t &= ~MAX31856_CONFIG_3WIRE; + } + writeRegister8(MAX31856_CONFIG_REG, t); +} + +float Adafruit_MAX31865::rtd_to_temperature(uint16_t rtd, float RTDnominal, float refResistor) { +//float Adafruit_MAX31865::temperature(float RTDnominal, float refResistor) { + // http://www.analog.com/media/en/technical-documentation/application-notes/AN709_0.pdf + + float Z1, Z2, Z3, Z4, Rt, temp; + + Rt = rtd; + Rt /= 32768; + Rt *= refResistor; + + // Serial.print("\nResistance: "); Serial.println(Rt, 8); + + Z1 = -RTD_A; + Z2 = RTD_A * RTD_A - (4 * RTD_B); + Z3 = (4 * RTD_B) / RTDnominal; + Z4 = 2 * RTD_B; + + temp = Z2 + (Z3 * Rt); + temp = (sqrt(temp) + Z1) / Z4; + + if (temp >= 0) return temp; + + // ugh. + Rt /= RTDnominal; + Rt *= 100; // normalize to 100 ohm + + float rpoly = Rt; + + temp = -242.02; + temp += 2.2228 * rpoly; + rpoly *= Rt; // square + temp += 2.5859e-3 * rpoly; + rpoly *= Rt; // ^3 + temp -= 4.8260e-6 * rpoly; + rpoly *= Rt; // ^4 + temp -= 2.8183e-8 * rpoly; + rpoly *= Rt; // ^5 + temp += 1.5243e-10 * rpoly; + + return temp; +} + +float Adafruit_MAX31865::rtd_to_resistance(uint16_t rtd, float refResistor) { + float Rt; + + Rt = rtd; + Rt /= 32768; + Rt *= refResistor; + + return Rt; +} + +float Adafruit_MAX31865::temperature(float RTDnominal, float refResistor) { + uint16_t rtd = readRTD(); + return rtd_to_temperature(rtd, RTDnominal, refResistor); +} + +uint16_t Adafruit_MAX31865::readRTD (void) { + clearFault(); + enableBias(true); + delay(10); + uint8_t t = readRegister8(MAX31856_CONFIG_REG); + t |= MAX31856_CONFIG_1SHOT; + writeRegister8(MAX31856_CONFIG_REG, t); + delay(65); + + uint16_t rtd = readRegister16(MAX31856_RTDMSB_REG); + + // remove fault + rtd >>= 1; + + return rtd; +} + +/**********************************************/ + +uint8_t Adafruit_MAX31865::readRegister8(uint8_t addr) { + uint8_t ret = 0; + readRegisterN(addr, &ret, 1); + + return ret; +} + +uint16_t Adafruit_MAX31865::readRegister16(uint8_t addr) { + uint8_t buffer[2] = {0, 0}; + readRegisterN(addr, buffer, 2); + + uint16_t ret = buffer[0]; + ret <<= 8; + ret |= buffer[1]; + + return ret; +} + + +void Adafruit_MAX31865::readRegisterN(uint8_t addr, uint8_t buffer[], uint8_t n) { + addr &= 0x7F; // make sure top bit is not set + + if (_sclk == -1) + SPI.beginTransaction(max31865_spisettings); + else + digitalWrite(_sclk, LOW); + + digitalWrite(_cs, LOW); + + spixfer(addr); + + //Serial.print("$"); Serial.print(addr, HEX); Serial.print(": "); + while (n--) { + buffer[0] = spixfer(0xFF); + //Serial.print(" 0x"); Serial.print(buffer[0], HEX); + buffer++; + } + //Serial.println(); + + if (_sclk == -1) + SPI.endTransaction(); + + digitalWrite(_cs, HIGH); +} + + +void Adafruit_MAX31865::writeRegister8(uint8_t addr, uint8_t data) { + if (_sclk == -1) + SPI.beginTransaction(max31865_spisettings); + else + digitalWrite(_sclk, LOW); + + digitalWrite(_cs, LOW); + + spixfer(addr | 0x80); // make sure top bit is set + spixfer(data); + + //Serial.print("$"); Serial.print(addr, HEX); Serial.print(" = 0x"); Serial.println(data, HEX); + + if (_sclk == -1) + SPI.endTransaction(); + + digitalWrite(_cs, HIGH); +} + + + +uint8_t Adafruit_MAX31865::spixfer(uint8_t x) { + if (_sclk == -1) + return SPI.transfer(x); + + // software spi + //Serial.println("Software SPI"); + uint8_t reply = 0; + + for (int i=7; i>=0; i--) { + reply <<= 1; + digitalWrite(_sclk, HIGH); + digitalWrite(_mosi, x & (1< https://www.adafruit.com/products/3328 + + This sensor uses SPI to communicate, 4 pins are required to + interface + Adafruit invests time and resources providing this open source code, + please support Adafruit and open-source hardware by purchasing + products from Adafruit! + + Written by Limor Fried/Ladyada for Adafruit Industries. + BSD license, all text above must be included in any redistribution + ****************************************************/ + +#ifndef ADAFRUIT_MAX31865_H +#define ADAFRUIT_MAX31865_H + +#define MAX31856_CONFIG_REG 0x00 +#define MAX31856_CONFIG_BIAS 0x80 +#define MAX31856_CONFIG_MODEAUTO 0x40 +#define MAX31856_CONFIG_MODEOFF 0x00 +#define MAX31856_CONFIG_1SHOT 0x20 +#define MAX31856_CONFIG_3WIRE 0x10 +#define MAX31856_CONFIG_24WIRE 0x00 +#define MAX31856_CONFIG_FAULTSTAT 0x02 +#define MAX31856_CONFIG_FILT50HZ 0x01 +#define MAX31856_CONFIG_FILT60HZ 0x00 + +#define MAX31856_RTDMSB_REG 0x01 +#define MAX31856_RTDLSB_REG 0x02 +#define MAX31856_HFAULTMSB_REG 0x03 +#define MAX31856_HFAULTLSB_REG 0x04 +#define MAX31856_LFAULTMSB_REG 0x05 +#define MAX31856_LFAULTLSB_REG 0x06 +#define MAX31856_FAULTSTAT_REG 0x07 + + +#define MAX31865_FAULT_HIGHTHRESH 0x80 +#define MAX31865_FAULT_LOWTHRESH 0x40 +#define MAX31865_FAULT_REFINLOW 0x20 +#define MAX31865_FAULT_REFINHIGH 0x10 +#define MAX31865_FAULT_RTDINLOW 0x08 +#define MAX31865_FAULT_OVUV 0x04 + + +#define RTD_A 3.9083e-3 +#define RTD_B -5.775e-7 + +#if (ARDUINO >= 100) + #include "Arduino.h" +#else + #include "WProgram.h" +#endif + +typedef enum max31865_numwires { + MAX31865_2WIRE = 0, + MAX31865_3WIRE = 1, + MAX31865_4WIRE = 0 +} max31865_numwires_t; + + +class Adafruit_MAX31865 { + public: + Adafruit_MAX31865(int8_t spi_cs, int8_t spi_mosi, int8_t spi_miso, int8_t spi_clk); + Adafruit_MAX31865(int8_t spi_cs); + Adafruit_MAX31865(void); + + void setPins(int8_t spi_cs, int8_t spi_mosi, int8_t spi_miso, int8_t spi_clk); + boolean begin(max31865_numwires_t x = MAX31865_2WIRE); + + uint8_t readFault(void); + void clearFault(void); + uint16_t readRTD(); + + + void setWires(max31865_numwires_t wires); + void autoConvert(boolean b); + void enableBias(boolean b); + + float temperature(float RTDnominal, float refResistor); + float rtd_to_temperature(uint16_t rtd, float RTDnominal, float refResistor); + float rtd_to_resistance(uint16_t rtd, float refResistor); + + private: + int8_t _sclk, _miso, _mosi, _cs; + + void readRegisterN(uint8_t addr, uint8_t buffer[], uint8_t n); + + uint8_t readRegister8(uint8_t addr); + uint16_t readRegister16(uint8_t addr); + + void writeRegister8(uint8_t addr, uint8_t reg); + uint8_t spixfer(uint8_t addr); +}; + + +#endif diff --git a/lib/Adafruit_MAX31865-1.1.0-custom/README.md b/lib/Adafruit_MAX31865-1.1.0-custom/README.md new file mode 100644 index 000000000..d3b9d2369 --- /dev/null +++ b/lib/Adafruit_MAX31865-1.1.0-custom/README.md @@ -0,0 +1,2 @@ +# Adafruit_MAX31865 +Arduino Library for Adafruit MAX31865 RTD Sensor diff --git a/lib/Adafruit_MAX31865-1.1.0-custom/README.txt b/lib/Adafruit_MAX31865-1.1.0-custom/README.txt new file mode 100644 index 000000000..edace57fd --- /dev/null +++ b/lib/Adafruit_MAX31865-1.1.0-custom/README.txt @@ -0,0 +1,16 @@ +This is the Adafruit MAX31856 Arduino Library + +Tested and works great with the Adafruit Thermocouple Breakout w/MAX31856 + + * http://www.adafruit.com/products/3328 + +These sensors use SPI to communicate, 4 pins are required to +interface + +Adafruit invests time and resources providing this open source code, +please support Adafruit and open-source hardware by purchasing +products from Adafruit! + +Written by Limor Fried/Ladyada for Adafruit Industries. +BSD license, check license.txt for more information +All text above must be included in any redistribution \ No newline at end of file diff --git a/lib/Adafruit_MAX31865-1.1.0-custom/examples/max31865/max31865.ino b/lib/Adafruit_MAX31865-1.1.0-custom/examples/max31865/max31865.ino new file mode 100644 index 000000000..5fc872f54 --- /dev/null +++ b/lib/Adafruit_MAX31865-1.1.0-custom/examples/max31865/max31865.ino @@ -0,0 +1,74 @@ +/*************************************************** + This is a library for the Adafruit PT100/P1000 RTD Sensor w/MAX31865 + + Designed specifically to work with the Adafruit RTD Sensor + ----> https://www.adafruit.com/products/3328 + + This sensor uses SPI to communicate, 4 pins are required to + interface + Adafruit invests time and resources providing this open source code, + please support Adafruit and open-source hardware by purchasing + products from Adafruit! + + Written by Limor Fried/Ladyada for Adafruit Industries. + BSD license, all text above must be included in any redistribution + ****************************************************/ + +#include + +// Use software SPI: CS, DI, DO, CLK +Adafruit_MAX31865 max = Adafruit_MAX31865(10, 11, 12, 13); +// use hardware SPI, just pass in the CS pin +//Adafruit_MAX31865 max = Adafruit_MAX31865(10); + +// The value of the Rref resistor. Use 430.0 for PT100 and 4300.0 for PT1000 +#define RREF 430.0 +// The 'nominal' 0-degrees-C resistance of the sensor +// 100.0 for PT100, 1000.0 for PT1000 +#define RNOMINAL 100.0 + +void setup() { + Serial.begin(115200); + Serial.println("Adafruit MAX31865 PT100 Sensor Test!"); + + max.begin(MAX31865_3WIRE); // set to 2WIRE or 4WIRE as necessary +} + + +void loop() { + uint16_t rtd = max.readRTD(); + + Serial.print("RTD value: "); Serial.println(rtd); + float ratio = rtd; + ratio /= 32768; + Serial.print("Ratio = "); Serial.println(ratio,8); + Serial.print("Resistance = "); Serial.println(RREF*ratio,8); + Serial.print("Temperature = "); Serial.println(max.temperature(RNOMINAL, RREF)); + + // Check and print any faults + uint8_t fault = max.readFault(); + if (fault) { + Serial.print("Fault 0x"); Serial.println(fault, HEX); + if (fault & MAX31865_FAULT_HIGHTHRESH) { + Serial.println("RTD High Threshold"); + } + if (fault & MAX31865_FAULT_LOWTHRESH) { + Serial.println("RTD Low Threshold"); + } + if (fault & MAX31865_FAULT_REFINLOW) { + Serial.println("REFIN- > 0.85 x Bias"); + } + if (fault & MAX31865_FAULT_REFINHIGH) { + Serial.println("REFIN- < 0.85 x Bias - FORCE- open"); + } + if (fault & MAX31865_FAULT_RTDINLOW) { + Serial.println("RTDIN- < 0.85 x Bias - FORCE- open"); + } + if (fault & MAX31865_FAULT_OVUV) { + Serial.println("Under/Over voltage"); + } + max.clearFault(); + } + Serial.println(); + delay(1000); +} diff --git a/lib/Adafruit_MAX31865-1.1.0-custom/library.properties b/lib/Adafruit_MAX31865-1.1.0-custom/library.properties new file mode 100644 index 000000000..f132a890a --- /dev/null +++ b/lib/Adafruit_MAX31865-1.1.0-custom/library.properties @@ -0,0 +1,9 @@ +name=Adafruit MAX31865 library +version=1.0.1 +author=Adafruit +maintainer=Adafruit +sentence=Library for the Adafruit RTD Amplifier breakout with MAX31865 +paragraph=Library for the Adafruit RTD Amplifier breakout with MAX31865 +category=Sensors +url=https://github.com/adafruit/Adafruit_MAX31865 +architectures=* diff --git a/lib/Adafruit_SH1106-gemu-1.0/Adafruit_SH1106.cpp b/lib/Adafruit_SH1106-gemu-1.0/Adafruit_SH1106.cpp new file mode 100644 index 000000000..fac803b53 --- /dev/null +++ b/lib/Adafruit_SH1106-gemu-1.0/Adafruit_SH1106.cpp @@ -0,0 +1,291 @@ +/********************************************************************* +This is a library for our Monochrome OLEDs based on SSD1306 drivers + + Pick one up today in the adafruit shop! + ------> http://www.adafruit.com/category/63_98 + +These displays use SPI to communicate, 4 or 5 pins are required to +interface + +Adafruit invests time and resources providing this open source code, +please support Adafruit and open-source hardware by purchasing +products from Adafruit! + +Written by Limor Fried/Ladyada for Adafruit Industries. +BSD license, check license.txt for more information +All text above, and the splash screen below must be included in any redistribution +*********************************************************************/ + +/********************************************************************* +I change the adafruit SSD1306 to SH1106 + +SH1106 driver similar to SSD1306 so, just change the display() method. + +However, SH1106 driver don't provide several functions such as scroll commands. + + +*********************************************************************/ + +//#include +#ifndef __SAM3X8E__ +// #include +#endif +#include +#include +//#include <../../sonoff/settings.h> + +#include "Adafruit_SH1106.h" +#define DISPLAY_INIT_MODE 0 + +// the memory buffer for the LCD +extern uint8_t *buffer; + +Adafruit_SH1106::Adafruit_SH1106(int16_t width, int16_t height) : +Renderer(width,height) { +} + +void Adafruit_SH1106::DisplayOnff(int8_t on) { + if (on) { + SH1106_command(SH1106_DISPLAYON); + } else { + SH1106_command(SH1106_DISPLAYOFF); + } +} + +void Adafruit_SH1106::Updateframe() { + display(); +} + +void Adafruit_SH1106::DisplayInit(int8_t p,int8_t size,int8_t rot,int8_t font) { +// ignore update mode + //if (p==DISPLAY_INIT_MODE) { + setRotation(rot); + invertDisplay(false); + setTextWrap(false); // Allow text to run off edges + cp437(true); + setTextFont(font); + setTextSize(size); + setTextColor(WHITE,BLACK); + setCursor(0,0); + fillScreen(BLACK); + Updateframe(); + //} +} + +int16_t Adafruit_SH1106::Begin(int16_t p1,int16_t p2,int16_t p3) { + begin(p1,p2,p3); +} + + +void Adafruit_SH1106::begin(uint8_t vccstate, uint8_t i2caddr, bool reset) { + _vccstate = vccstate; + _i2caddr = i2caddr; + // I2C Init + Wire.begin(); + + #if defined SH1106_128_32 + // Init sequence for 128x32 OLED module + SH1106_command(SH1106_DISPLAYOFF); // 0xAE + SH1106_command(SH1106_SETDISPLAYCLOCKDIV); // 0xD5 + SH1106_command(0x80); // the suggested ratio 0x80 + SH1106_command(SH1106_SETMULTIPLEX); // 0xA8 + SH1106_command(0x1F); + SH1106_command(SH1106_SETDISPLAYOFFSET); // 0xD3 + SH1106_command(0x0); // no offset + SH1106_command(SH1106_SETSTARTLINE | 0x0); // line #0 + SH1106_command(SH1106_CHARGEPUMP); // 0x8D + if (vccstate == SH1106_EXTERNALVCC) + { SH1106_command(0x10); } + else + { SH1106_command(0x14); } + SH1106_command(SH1106_MEMORYMODE); // 0x20 + SH1106_command(0x00); // 0x0 act like ks0108 + SH1106_command(SH1106_SEGREMAP | 0x1); + SH1106_command(SH1106_COMSCANDEC); + SH1106_command(SH1106_SETCOMPINS); // 0xDA + SH1106_command(0x02); + SH1106_command(SH1106_SETCONTRAST); // 0x81 + SH1106_command(0x8F); + SH1106_command(SH1106_SETPRECHARGE); // 0xd9 + if (vccstate == SH1106_EXTERNALVCC) + { SH1106_command(0x22); } + else + { SH1106_command(0xF1); } + SH1106_command(SH1106_SETVCOMDETECT); // 0xDB + SH1106_command(0x40); + SH1106_command(SH1106_DISPLAYALLON_RESUME); // 0xA4 + SH1106_command(SH1106_NORMALDISPLAY); // 0xA6 + #endif + + #if defined SH1106_128_64 + // Init sequence for 128x64 OLED module + SH1106_command(SH1106_DISPLAYOFF); // 0xAE + SH1106_command(SH1106_SETDISPLAYCLOCKDIV); // 0xD5 + SH1106_command(0x80); // the suggested ratio 0x80 + SH1106_command(SH1106_SETMULTIPLEX); // 0xA8 + SH1106_command(0x3F); + SH1106_command(SH1106_SETDISPLAYOFFSET); // 0xD3 + SH1106_command(0x00); // no offset + + SH1106_command(SH1106_SETSTARTLINE | 0x0); // line #0 0x40 + SH1106_command(SH1106_CHARGEPUMP); // 0x8D + if (vccstate == SH1106_EXTERNALVCC) + { SH1106_command(0x10); } + else + { SH1106_command(0x14); } + SH1106_command(SH1106_MEMORYMODE); // 0x20 + SH1106_command(0x00); // 0x0 act like ks0108 + SH1106_command(SH1106_SEGREMAP | 0x1); + SH1106_command(SH1106_COMSCANDEC); + SH1106_command(SH1106_SETCOMPINS); // 0xDA + SH1106_command(0x12); + SH1106_command(SH1106_SETCONTRAST); // 0x81 + if (vccstate == SH1106_EXTERNALVCC) + { SH1106_command(0x9F); } + else + { SH1106_command(0xCF); } + SH1106_command(SH1106_SETPRECHARGE); // 0xd9 + if (vccstate == SH1106_EXTERNALVCC) + { SH1106_command(0x22); } + else + { SH1106_command(0xF1); } + SH1106_command(SH1106_SETVCOMDETECT); // 0xDB + SH1106_command(0x40); + SH1106_command(SH1106_DISPLAYALLON_RESUME); // 0xA4 + SH1106_command(SH1106_NORMALDISPLAY); // 0xA6 + #endif + + #if defined SH1106_96_16 + // Init sequence for 96x16 OLED module + SH1106_command(SH1106_DISPLAYOFF); // 0xAE + SH1106_command(SH1106_SETDISPLAYCLOCKDIV); // 0xD5 + SH1106_command(0x80); // the suggested ratio 0x80 + SH1106_command(SH1106_SETMULTIPLEX); // 0xA8 + SH1106_command(0x0F); + SH1106_command(SH1106_SETDISPLAYOFFSET); // 0xD3 + SH1106_command(0x00); // no offset + SH1106_command(SH1106_SETSTARTLINE | 0x0); // line #0 + SH1106_command(SH1106_CHARGEPUMP); // 0x8D + if (vccstate == SH1106_EXTERNALVCC) + { SH1106_command(0x10); } + else + { SH1106_command(0x14); } + SH1106_command(SH1106_MEMORYMODE); // 0x20 + SH1106_command(0x00); // 0x0 act like ks0108 + SH1106_command(SH1106_SEGREMAP | 0x1); + SH1106_command(SH1106_COMSCANDEC); + SH1106_command(SH1106_SETCOMPINS); // 0xDA + SH1106_command(0x2); //ada x12 + SH1106_command(SH1106_SETCONTRAST); // 0x81 + if (vccstate == SH1106_EXTERNALVCC) + { SH1106_command(0x10); } + else + { SH1106_command(0xAF); } + SH1106_command(SH1106_SETPRECHARGE); // 0xd9 + if (vccstate == SH1106_EXTERNALVCC) + { SH1106_command(0x22); } + else + { SH1106_command(0xF1); } + SH1106_command(SH1106_SETVCOMDETECT); // 0xDB + SH1106_command(0x40); + SH1106_command(SH1106_DISPLAYALLON_RESUME); // 0xA4 + SH1106_command(SH1106_NORMALDISPLAY); // 0xA6 + #endif + + SH1106_command(SH1106_DISPLAYON);//--turn on oled panel +} + + +void Adafruit_SH1106::invertDisplay(uint8_t i) { + if (i) { + SH1106_command(SH1106_INVERTDISPLAY); + } else { + SH1106_command(SH1106_NORMALDISPLAY); + } +} + +void Adafruit_SH1106::SH1106_command(uint8_t c) { + + // I2C + uint8_t control = 0x00; // Co = 0, D/C = 0 + Wire.beginTransmission(_i2caddr); + WIRE_WRITE(control); + WIRE_WRITE(c); + Wire.endTransmission(); + +} + +// Dim the display +// dim = true: display is dimmed +// dim = false: display is normal +void Adafruit_SH1106::dim(uint8_t dim) { + uint8_t contrast; + + if (dim) { + contrast = 0; // Dimmed display + } else { + if (_vccstate == SH1106_EXTERNALVCC) { + contrast = 0x9F; + } else { + contrast = 0xCF; + } + } + // the range of contrast to too small to be really useful + // it is useful to dim the display + SH1106_command(SH1106_SETCONTRAST); + SH1106_command(contrast); +} + +void Adafruit_SH1106::SH1106_data(uint8_t c) { + // I2C + uint8_t control = 0x40; // Co = 0, D/C = 1 + Wire.beginTransmission(_i2caddr); + WIRE_WRITE(control); + WIRE_WRITE(c); + Wire.endTransmission(); +} + +void Adafruit_SH1106::display(void) { + SH1106_command(SH1106_SETLOWCOLUMN | 0x0); // low col = 0 + SH1106_command(SH1106_SETHIGHCOLUMN | 0x0); // hi col = 0 + SH1106_command(SH1106_SETSTARTLINE | 0x0); // line #0 + // I2C + //height >>= 3; + //width >>= 3; + byte height=64; + byte width=132; + byte m_row = 0; + byte m_col = 2; + + + height >>= 3; + width >>= 3; + //Serial.println(width); + + int p = 0; + + byte i, j, k =0; + + for ( i = 0; i < height; i++) { + + // send a bunch of data in one xmission + SH1106_command(0xB0 + i + m_row);//set page address + SH1106_command(m_col & 0xf);//set lower column address + SH1106_command(0x10 | (m_col >> 4));//set higher column address + + for( j = 0; j < 8; j++){ + Wire.beginTransmission(_i2caddr); + Wire.write(0x40); + for ( k = 0; k < width; k++, p++) { + Wire.write(buffer[p]); + } + Wire.endTransmission(); + } + } + +} + +// clear everything +void Adafruit_SH1106::clearDisplay(void) { + memset(buffer, 0, (SH1106_LCDWIDTH*SH1106_LCDHEIGHT/8)); +} diff --git a/lib/Adafruit_SH1106-gemu-1.0/Adafruit_SH1106.h b/lib/Adafruit_SH1106-gemu-1.0/Adafruit_SH1106.h new file mode 100644 index 000000000..b0562629b --- /dev/null +++ b/lib/Adafruit_SH1106-gemu-1.0/Adafruit_SH1106.h @@ -0,0 +1,154 @@ +/********************************************************************* +This is a library for our Monochrome OLEDs based on SSD1306 drivers + + Pick one up today in the adafruit shop! + ------> http://www.adafruit.com/category/63_98 + +These displays use SPI to communicate, 4 or 5 pins are required to +interface + +Adafruit invests time and resources providing this open source code, +please support Adafruit and open-source hardware by purchasing +products from Adafruit! + +Written by Limor Fried/Ladyada for Adafruit Industries. +BSD license, check license.txt for more information +All text above, and the splash screen must be included in any redistribution +*********************************************************************/ + +/********************************************************************* +I change the adafruit SSD1306 to SH1106 + +SH1106 driver similar to SSD1306 so, just change the display() method. + +However, SH1106 driver don't provide several functions such as scroll commands. + + +*********************************************************************/ + +#if ARDUINO >= 100 + #include "Arduino.h" + #define WIRE_WRITE Wire.write +#else + #include "WProgram.h" + #define WIRE_WRITE Wire.send +#endif + +#include + +#define BLACK 0 +#define WHITE 1 +#define INVERSE 2 + +#define SH1106_I2C_ADDRESS 0x3C // 011110+SA0+RW - 0x3C or 0x3D +// Address for 128x32 is 0x3C +// Address for 128x64 is 0x3D (default) or 0x3C (if SA0 is grounded) + +/*========================================================================= + SH1106 Displays + ----------------------------------------------------------------------- + The driver is used in multiple displays (128x64, 128x32, etc.). + Select the appropriate display below to create an appropriately + sized framebuffer, etc. + + SH1106_128_64 128x64 pixel display + + SH1106_128_32 128x32 pixel display + + SH1106_96_16 + + -----------------------------------------------------------------------*/ + #define SH1106_128_64 +// #define SH1106_128_32 +// #define SH1106_96_16 +/*=========================================================================*/ + +#if defined SH1106_128_64 && defined SH1106_128_32 + #error "Only one SH1106 display can be specified at once in SH1106.h" +#endif +#if !defined SH1106_128_64 && !defined SH1106_128_32 && !defined SH1106_96_16 + #error "At least one SH1106 display must be specified in SH1106.h" +#endif + +#if defined SH1106_128_64 + #define SH1106_LCDWIDTH 128 + #define SH1106_LCDHEIGHT 64 +#endif +#if defined SH1106_128_32 + #define SH1106_LCDWIDTH 128 + #define SH1106_LCDHEIGHT 32 +#endif +#if defined SH1106_96_16 + #define SH1106_LCDWIDTH 96 + #define SH1106_LCDHEIGHT 16 +#endif + +#define SH1106_SETCONTRAST 0x81 +#define SH1106_DISPLAYALLON_RESUME 0xA4 +#define SH1106_DISPLAYALLON 0xA5 +#define SH1106_NORMALDISPLAY 0xA6 +#define SH1106_INVERTDISPLAY 0xA7 +#define SH1106_DISPLAYOFF 0xAE +#define SH1106_DISPLAYON 0xAF + +#define SH1106_SETDISPLAYOFFSET 0xD3 +#define SH1106_SETCOMPINS 0xDA + +#define SH1106_SETVCOMDETECT 0xDB + +#define SH1106_SETDISPLAYCLOCKDIV 0xD5 +#define SH1106_SETPRECHARGE 0xD9 + +#define SH1106_SETMULTIPLEX 0xA8 + +#define SH1106_SETLOWCOLUMN 0x00 +#define SH1106_SETHIGHCOLUMN 0x10 + +#define SH1106_SETSTARTLINE 0x40 + +#define SH1106_MEMORYMODE 0x20 +#define SH1106_COLUMNADDR 0x21 +#define SH1106_PAGEADDR 0x22 + +#define SH1106_COMSCANINC 0xC0 +#define SH1106_COMSCANDEC 0xC8 + +#define SH1106_SEGREMAP 0xA0 + +#define SH1106_CHARGEPUMP 0x8D + +#define SH1106_EXTERNALVCC 0x1 +#define SH1106_SWITCHCAPVCC 0x2 + +// Scrolling #defines +#define SH1106_ACTIVATE_SCROLL 0x2F +#define SH1106_DEACTIVATE_SCROLL 0x2E +#define SH1106_SET_VERTICAL_SCROLL_AREA 0xA3 +#define SH1106_RIGHT_HORIZONTAL_SCROLL 0x26 +#define SH1106_LEFT_HORIZONTAL_SCROLL 0x27 +#define SH1106_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL 0x29 +#define SH1106_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL 0x2A + +class Adafruit_SH1106 : public Renderer { + public: + Adafruit_SH1106(int16_t width, int16_t height); + + void begin(uint8_t switchvcc = SH1106_SWITCHCAPVCC, uint8_t i2caddr = SH1106_I2C_ADDRESS, bool reset=true); + void SH1106_command(uint8_t c); + void SH1106_data(uint8_t c); + + void clearDisplay(void); + void invertDisplay(uint8_t i); + void display(); + + + void DisplayOnff(int8_t on); + void DisplayInit(int8_t p,int8_t size,int8_t rot,int8_t font); + int16_t Begin(int16_t p1,int16_t p2,int16_t p3); + void Updateframe(); + void dim(uint8_t contrast); + + private: + int8_t _i2caddr, _vccstate, rst; + +}; diff --git a/lib/Adafruit_SSD1306-1.1.2/license.txt b/lib/Adafruit_SH1106-gemu-1.0/LICENSE.txt similarity index 100% rename from lib/Adafruit_SSD1306-1.1.2/license.txt rename to lib/Adafruit_SH1106-gemu-1.0/LICENSE.txt diff --git a/lib/Adafruit_SH1106-gemu-1.0/README.md b/lib/Adafruit_SH1106-gemu-1.0/README.md new file mode 100644 index 000000000..c457a3cc8 --- /dev/null +++ b/lib/Adafruit_SH1106-gemu-1.0/README.md @@ -0,0 +1,16 @@ +Adafruit_SH1106 +=============== + +Adafruit graphic library for SH1106 driver lcds. + +some small oled lcd use SH1106 driver. + +I change the adafruit SSD1306 to SH1106 + +SH1106 driver similar to SSD1306. thus, just change the display() method. + +However, SH1106 driver don't provide several functions such as scroll commands. + + + Adafruit-GFX-Library + https://github.com/adafruit/Adafruit-GFX-Library diff --git a/lib/Adafruit_SSD1306-1.1.2/examples/ssd1306_128x64_i2c/ssd1306_128x64_i2c.ino b/lib/Adafruit_SH1106-gemu-1.0/examples/sh1106_128x64_i2c/sh1106_128x64_i2c.ino similarity index 90% rename from lib/Adafruit_SSD1306-1.1.2/examples/ssd1306_128x64_i2c/ssd1306_128x64_i2c.ino rename to lib/Adafruit_SH1106-gemu-1.0/examples/sh1106_128x64_i2c/sh1106_128x64_i2c.ino index c44701ed0..2b2c90368 100644 --- a/lib/Adafruit_SSD1306-1.1.2/examples/ssd1306_128x64_i2c/ssd1306_128x64_i2c.ino +++ b/lib/Adafruit_SH1106-gemu-1.0/examples/sh1106_128x64_i2c/sh1106_128x64_i2c.ino @@ -16,13 +16,20 @@ BSD license, check license.txt for more information All text above, and the splash screen must be included in any redistribution *********************************************************************/ +/********************************************************************* +I change the adafruit SSD1306 to SH1106 + +SH1106 driver don't provide several functions such as scroll commands. + +*********************************************************************/ + #include #include #include -#include +#include #define OLED_RESET 4 -Adafruit_SSD1306 display(OLED_RESET); +Adafruit_SH1106 display(OLED_RESET); #define NUMFLAKES 10 #define XPOS 0 @@ -50,15 +57,15 @@ static const unsigned char PROGMEM logo16_glcd_bmp[] = B01110000, B01110000, B00000000, B00110000 }; -#if (SSD1306_LCDHEIGHT != 64) -#error("Height incorrect, please fix Adafruit_SSD1306.h!"); +#if (SH1106_LCDHEIGHT != 64) +#error("Height incorrect, please fix Adafruit_SH1106.h!"); #endif void setup() { Serial.begin(9600); // by default, we'll generate the high voltage from the 3.3v line internally! (neat!) - display.begin(SSD1306_SWITCHCAPVCC, 0x3D); // initialize with the I2C addr 0x3D (for the 128x64) + display.begin(SH1106_SWITCHCAPVCC, 0x3C); // initialize with the I2C addr 0x3D (for the 128x64) // init done // Show image buffer on the display hardware. @@ -132,9 +139,9 @@ void setup() { display.clearDisplay(); // draw scrolling text - testscrolltext(); + /* testscrolltext(); delay(2000); - display.clearDisplay(); + display.clearDisplay();*/ // text display tests display.setTextSize(1); @@ -148,19 +155,17 @@ void setup() { display.print("0x"); display.println(0xDEADBEEF, HEX); display.display(); delay(2000); - display.clearDisplay(); // miniature bitmap display + display.clearDisplay(); display.drawBitmap(30, 16, logo16_glcd_bmp, 16, 16, 1); display.display(); - delay(1); // invert the display display.invertDisplay(true); delay(1000); display.invertDisplay(false); delay(1000); - display.clearDisplay(); // draw a bitmap icon and 'animate' movement testdrawbitmap(logo16_glcd_bmp, LOGO16_GLCD_HEIGHT, LOGO16_GLCD_WIDTH); @@ -192,21 +197,21 @@ void testdrawbitmap(const uint8_t *bitmap, uint8_t w, uint8_t h) { while (1) { // draw each icon for (uint8_t f=0; f< NUMFLAKES; f++) { - display.drawBitmap(icons[f][XPOS], icons[f][YPOS], bitmap, w, h, WHITE); + display.drawBitmap(icons[f][XPOS], icons[f][YPOS], logo16_glcd_bmp, w, h, WHITE); } display.display(); delay(200); // then erase it + move it for (uint8_t f=0; f< NUMFLAKES; f++) { - display.drawBitmap(icons[f][XPOS], icons[f][YPOS], bitmap, w, h, BLACK); + display.drawBitmap(icons[f][XPOS], icons[f][YPOS], logo16_glcd_bmp, w, h, BLACK); // move it icons[f][YPOS] += icons[f][DELTAY]; // if its gone, reinit if (icons[f][YPOS] > display.height()) { - icons[f][XPOS] = random(display.width()); - icons[f][YPOS] = 0; - icons[f][DELTAY] = random(5) + 1; + icons[f][XPOS] = random(display.width()); + icons[f][YPOS] = 0; + icons[f][DELTAY] = random(5) + 1; } } } @@ -225,14 +230,12 @@ void testdrawchar(void) { display.println(); } display.display(); - delay(1); } void testdrawcircle(void) { for (int16_t i=0; i=0; i-=4) { display.drawLine(0, display.height()-1, display.width()-1, i, WHITE); display.display(); - delay(1); } delay(250); @@ -327,12 +320,10 @@ void testdrawline() { for (int16_t i=display.width()-1; i>=0; i-=4) { display.drawLine(display.width()-1, display.height()-1, i, 0, WHITE); display.display(); - delay(1); } for (int16_t i=display.height()-1; i>=0; i-=4) { display.drawLine(display.width()-1, display.height()-1, 0, i, WHITE); display.display(); - delay(1); } delay(250); @@ -340,24 +331,21 @@ void testdrawline() { for (int16_t i=0; i http://www.adafruit.com/category/63_98 @@ -19,7 +19,7 @@ All text above, and the splash screen must be included in any redistribution #include #include #include -#include +#include // If using software SPI (the default case): #define OLED_MOSI 9 @@ -27,13 +27,13 @@ All text above, and the splash screen must be included in any redistribution #define OLED_DC 11 #define OLED_CS 12 #define OLED_RESET 13 -Adafruit_SSD1306 display(OLED_MOSI, OLED_CLK, OLED_DC, OLED_RESET, OLED_CS); +Adafruit_SH1106 display(OLED_MOSI, OLED_CLK, OLED_DC, OLED_RESET, OLED_CS); /* Uncomment this block to use hardware SPI #define OLED_DC 6 #define OLED_CS 7 #define OLED_RESET 8 -Adafruit_SSD1306 display(OLED_DC, OLED_RESET, OLED_CS); +Adafruit_SH1106 display(OLED_DC, OLED_RESET, OLED_CS); */ #define NUMFLAKES 10 @@ -61,15 +61,15 @@ static const unsigned char PROGMEM logo16_glcd_bmp[] = B01110000, B01110000, B00000000, B00110000 }; -#if (SSD1306_LCDHEIGHT != 64) -#error("Height incorrect, please fix Adafruit_SSD1306.h!"); +#if (SH1106_LCDHEIGHT != 64) +#error("Height incorrect, please fix Adafruit_SH1106.h!"); #endif void setup() { Serial.begin(9600); - + // by default, we'll generate the high voltage from the 3.3v line internally! (neat!) - display.begin(SSD1306_SWITCHCAPVCC); + display.begin(SH1106_SWITCHCAPVCC); // init done // Show image buffer on the display hardware. @@ -143,10 +143,10 @@ void setup() { display.clearDisplay(); // draw scrolling text - testscrolltext(); + /*testscrolltext(); delay(2000); - display.clearDisplay(); - + display.clearDisplay();*/ + // text display tests display.setTextSize(1); display.setTextColor(WHITE); @@ -159,9 +159,9 @@ void setup() { display.print("0x"); display.println(0xDEADBEEF, HEX); display.display(); delay(2000); - display.clearDisplay(); // miniature bitmap display + display.clearDisplay(); display.drawBitmap(30, 16, logo16_glcd_bmp, 16, 16, 1); display.display(); @@ -170,7 +170,6 @@ void setup() { delay(1000); display.invertDisplay(false); delay(1000); - display.clearDisplay(); // draw a bitmap icon and 'animate' movement testdrawbitmap(logo16_glcd_bmp, LOGO16_GLCD_HEIGHT, LOGO16_GLCD_WIDTH); @@ -202,21 +201,21 @@ void testdrawbitmap(const uint8_t *bitmap, uint8_t w, uint8_t h) { while (1) { // draw each icon for (uint8_t f=0; f< NUMFLAKES; f++) { - display.drawBitmap(icons[f][XPOS], icons[f][YPOS], bitmap, w, h, WHITE); + display.drawBitmap(icons[f][XPOS], icons[f][YPOS], logo16_glcd_bmp, w, h, WHITE); } display.display(); delay(200); // then erase it + move it for (uint8_t f=0; f< NUMFLAKES; f++) { - display.drawBitmap(icons[f][XPOS], icons[f][YPOS], bitmap, w, h, BLACK); + display.drawBitmap(icons[f][XPOS], icons[f][YPOS], logo16_glcd_bmp, w, h, BLACK); // move it icons[f][YPOS] += icons[f][DELTAY]; // if its gone, reinit if (icons[f][YPOS] > display.height()) { - icons[f][XPOS] = random(display.width()); - icons[f][YPOS] = 0; - icons[f][DELTAY] = random(5) + 1; + icons[f][XPOS] = random(display.width()); + icons[f][YPOS] = 0; + icons[f][DELTAY] = random(5) + 1; } } } @@ -344,6 +343,7 @@ void testdrawline() { delay(250); } +/* void testscrolltext(void) { display.setTextSize(2); display.setTextColor(WHITE); @@ -366,3 +366,4 @@ void testscrolltext(void) { delay(2000); display.stopscroll(); } +*/ diff --git a/lib/Adafruit_SSD1306-1.1.2/Adafruit_SSD1306.cpp b/lib/Adafruit_SSD1306-1.1.2/Adafruit_SSD1306.cpp deleted file mode 100644 index 570a33584..000000000 --- a/lib/Adafruit_SSD1306-1.1.2/Adafruit_SSD1306.cpp +++ /dev/null @@ -1,729 +0,0 @@ -/********************************************************************* -This is a library for our Monochrome OLEDs based on SSD1306 drivers - - Pick one up today in the adafruit shop! - ------> http://www.adafruit.com/category/63_98 - -These displays use SPI to communicate, 4 or 5 pins are required to -interface - -Adafruit invests time and resources providing this open source code, -please support Adafruit and open-source hardware by purchasing -products from Adafruit! - -Written by Limor Fried/Ladyada for Adafruit Industries. -BSD license, check license.txt for more information -All text above, and the splash screen below must be included in any redistribution -*********************************************************************/ - -#ifdef __AVR__ - #include -#elif defined(ESP8266) || defined(ESP32) - #include -#else - #define pgm_read_byte(addr) (*(const unsigned char *)(addr)) -#endif - -#if !defined(__ARM_ARCH) && !defined(ENERGIA) && !defined(ESP8266) && !defined(ESP32) && !defined(__arc__) - #include -#endif - -#include - -#include -#include -#include "Adafruit_GFX.h" -#include "Adafruit_SSD1306.h" - -// the memory buffer for the LCD - -static uint8_t buffer[SSD1306_LCDHEIGHT * SSD1306_LCDWIDTH / 8] = { -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, -0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x80, 0x80, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xF8, 0xE0, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, -0x80, 0x80, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0xFF, -#if (SSD1306_LCDHEIGHT * SSD1306_LCDWIDTH > 96*16) -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, -0x80, 0xFF, 0xFF, 0x80, 0x80, 0x00, 0x80, 0x80, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x80, 0x80, -0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x8C, 0x8E, 0x84, 0x00, 0x00, 0x80, 0xF8, -0xF8, 0xF8, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xE0, 0xE0, 0xC0, 0x80, -0x00, 0xE0, 0xFC, 0xFE, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xC7, 0x01, 0x01, -0x01, 0x01, 0x83, 0xFF, 0xFF, 0x00, 0x00, 0x7C, 0xFE, 0xC7, 0x01, 0x01, 0x01, 0x01, 0x83, 0xFF, -0xFF, 0xFF, 0x00, 0x38, 0xFE, 0xC7, 0x83, 0x01, 0x01, 0x01, 0x83, 0xC7, 0xFF, 0xFF, 0x00, 0x00, -0x01, 0xFF, 0xFF, 0x01, 0x01, 0x00, 0xFF, 0xFF, 0x07, 0x01, 0x01, 0x01, 0x00, 0x00, 0x7F, 0xFF, -0x80, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x7F, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x01, 0xFF, -0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x03, 0x0F, 0x3F, 0x7F, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0xC7, 0xC7, 0x8F, -0x8F, 0x9F, 0xBF, 0xFF, 0xFF, 0xC3, 0xC0, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFC, 0xFC, -0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xF8, 0xF8, 0xF0, 0xF0, 0xE0, 0xC0, 0x00, 0x01, 0x03, 0x03, 0x03, -0x03, 0x03, 0x01, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x03, 0x03, 0x03, 0x01, 0x01, -0x03, 0x01, 0x00, 0x00, 0x00, 0x01, 0x03, 0x03, 0x03, 0x03, 0x01, 0x01, 0x03, 0x03, 0x00, 0x00, -0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, -0x03, 0x03, 0x03, 0x03, 0x03, 0x01, 0x00, 0x00, 0x00, 0x01, 0x03, 0x01, 0x00, 0x00, 0x00, 0x03, -0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -#if (SSD1306_LCDHEIGHT == 64) -0x00, 0x00, 0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0x1F, 0x0F, -0x87, 0xC7, 0xF7, 0xFF, 0xFF, 0x1F, 0x1F, 0x3D, 0xFC, 0xF8, 0xF8, 0xF8, 0xF8, 0x7C, 0x7D, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x3F, 0x0F, 0x07, 0x00, 0x30, 0x30, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFE, 0xFE, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xC0, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0xC0, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x7F, 0x3F, 0x1F, -0x0F, 0x07, 0x1F, 0x7F, 0xFF, 0xFF, 0xF8, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xF8, 0xE0, -0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFE, 0x00, 0x00, -0x00, 0xFC, 0xFE, 0xFC, 0x0C, 0x06, 0x06, 0x0E, 0xFC, 0xF8, 0x00, 0x00, 0xF0, 0xF8, 0x1C, 0x0E, -0x06, 0x06, 0x06, 0x0C, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFE, 0xFE, 0x00, 0x00, 0x00, 0x00, 0xFC, -0xFE, 0xFC, 0x00, 0x18, 0x3C, 0x7E, 0x66, 0xE6, 0xCE, 0x84, 0x00, 0x00, 0x06, 0xFF, 0xFF, 0x06, -0x06, 0xFC, 0xFE, 0xFC, 0x0C, 0x06, 0x06, 0x06, 0x00, 0x00, 0xFE, 0xFE, 0x00, 0x00, 0xC0, 0xF8, -0xFC, 0x4E, 0x46, 0x46, 0x46, 0x4E, 0x7C, 0x78, 0x40, 0x18, 0x3C, 0x76, 0xE6, 0xCE, 0xCC, 0x80, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x01, 0x07, 0x0F, 0x1F, 0x1F, 0x3F, 0x3F, 0x3F, 0x3F, 0x1F, 0x0F, 0x03, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, -0x00, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x03, 0x07, 0x0E, 0x0C, -0x18, 0x18, 0x0C, 0x06, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x01, 0x0F, 0x0E, 0x0C, 0x18, 0x0C, 0x0F, -0x07, 0x01, 0x00, 0x04, 0x0E, 0x0C, 0x18, 0x0C, 0x0F, 0x07, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, -0x00, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x07, -0x07, 0x0C, 0x0C, 0x18, 0x1C, 0x0C, 0x06, 0x06, 0x00, 0x04, 0x0E, 0x0C, 0x18, 0x0C, 0x0F, 0x07, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -#endif -#endif -}; - -#define ssd1306_swap(a, b) { int16_t t = a; a = b; b = t; } - -// the most basic function, set a single pixel -void Adafruit_SSD1306::drawPixel(int16_t x, int16_t y, uint16_t color) { - if ((x < 0) || (x >= width()) || (y < 0) || (y >= height())) - return; - - // check rotation, move pixel around if necessary - switch (getRotation()) { - case 1: - ssd1306_swap(x, y); - x = WIDTH - x - 1; - break; - case 2: - x = WIDTH - x - 1; - y = HEIGHT - y - 1; - break; - case 3: - ssd1306_swap(x, y); - y = HEIGHT - y - 1; - break; - } - - // x is which column - switch (color) - { - case WHITE: buffer[x+ (y/8)*SSD1306_LCDWIDTH] |= (1 << (y&7)); break; - case BLACK: buffer[x+ (y/8)*SSD1306_LCDWIDTH] &= ~(1 << (y&7)); break; - case INVERSE: buffer[x+ (y/8)*SSD1306_LCDWIDTH] ^= (1 << (y&7)); break; - } - -} - -Adafruit_SSD1306::Adafruit_SSD1306(int8_t SID, int8_t SCLK, int8_t DC, int8_t RST, int8_t CS) : Adafruit_GFX(SSD1306_LCDWIDTH, SSD1306_LCDHEIGHT) { - cs = CS; - rst = RST; - dc = DC; - sclk = SCLK; - sid = SID; - hwSPI = false; -} - -// constructor for hardware SPI - we indicate DataCommand, ChipSelect, Reset -Adafruit_SSD1306::Adafruit_SSD1306(int8_t DC, int8_t RST, int8_t CS) : Adafruit_GFX(SSD1306_LCDWIDTH, SSD1306_LCDHEIGHT) { - dc = DC; - rst = RST; - cs = CS; - hwSPI = true; -} - -// initializer for I2C - we only indicate the reset pin! -Adafruit_SSD1306::Adafruit_SSD1306(int8_t reset) : -Adafruit_GFX(SSD1306_LCDWIDTH, SSD1306_LCDHEIGHT) { - sclk = dc = cs = sid = -1; - rst = reset; -} - - -void Adafruit_SSD1306::begin(uint8_t vccstate, uint8_t i2caddr, bool reset) { - _vccstate = vccstate; - _i2caddr = i2caddr; - - // set pin directions - if (sid != -1){ - pinMode(dc, OUTPUT); - pinMode(cs, OUTPUT); -#ifdef HAVE_PORTREG - csport = portOutputRegister(digitalPinToPort(cs)); - cspinmask = digitalPinToBitMask(cs); - dcport = portOutputRegister(digitalPinToPort(dc)); - dcpinmask = digitalPinToBitMask(dc); -#endif - if (!hwSPI){ - // set pins for software-SPI - pinMode(sid, OUTPUT); - pinMode(sclk, OUTPUT); -#ifdef HAVE_PORTREG - clkport = portOutputRegister(digitalPinToPort(sclk)); - clkpinmask = digitalPinToBitMask(sclk); - mosiport = portOutputRegister(digitalPinToPort(sid)); - mosipinmask = digitalPinToBitMask(sid); -#endif - } - if (hwSPI){ - SPI.begin(); -#ifdef SPI_HAS_TRANSACTION - SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0)); -#else - SPI.setClockDivider (4); -#endif - } - } - else - { - // I2C Init - Wire.begin(); -#ifdef __SAM3X8E__ - // Force 400 KHz I2C, rawr! (Uses pins 20, 21 for SDA, SCL) - TWI1->TWI_CWGR = 0; - TWI1->TWI_CWGR = ((VARIANT_MCK / (2 * 400000)) - 4) * 0x101; -#endif - } - if ((reset) && (rst >= 0)) { - // Setup reset pin direction (used by both SPI and I2C) - pinMode(rst, OUTPUT); - digitalWrite(rst, HIGH); - // VDD (3.3V) goes high at start, lets just chill for a ms - delay(1); - // bring reset low - digitalWrite(rst, LOW); - // wait 10ms - delay(10); - // bring out of reset - digitalWrite(rst, HIGH); - // turn on VCC (9V?) - } - - // Init sequence - ssd1306_command(SSD1306_DISPLAYOFF); // 0xAE - ssd1306_command(SSD1306_SETDISPLAYCLOCKDIV); // 0xD5 - ssd1306_command(0x80); // the suggested ratio 0x80 - - ssd1306_command(SSD1306_SETMULTIPLEX); // 0xA8 - ssd1306_command(SSD1306_LCDHEIGHT - 1); - - ssd1306_command(SSD1306_SETDISPLAYOFFSET); // 0xD3 - ssd1306_command(0x0); // no offset - ssd1306_command(SSD1306_SETSTARTLINE | 0x0); // line #0 - ssd1306_command(SSD1306_CHARGEPUMP); // 0x8D - if (vccstate == SSD1306_EXTERNALVCC) - { ssd1306_command(0x10); } - else - { ssd1306_command(0x14); } - ssd1306_command(SSD1306_MEMORYMODE); // 0x20 - ssd1306_command(0x00); // 0x0 act like ks0108 - ssd1306_command(SSD1306_SEGREMAP | 0x1); - ssd1306_command(SSD1306_COMSCANDEC); - - #if defined SSD1306_128_32 - ssd1306_command(SSD1306_SETCOMPINS); // 0xDA - ssd1306_command(0x02); - ssd1306_command(SSD1306_SETCONTRAST); // 0x81 - ssd1306_command(0x8F); - -#elif defined SSD1306_128_64 - ssd1306_command(SSD1306_SETCOMPINS); // 0xDA - ssd1306_command(0x12); - ssd1306_command(SSD1306_SETCONTRAST); // 0x81 - if (vccstate == SSD1306_EXTERNALVCC) - { ssd1306_command(0x9F); } - else - { ssd1306_command(0xCF); } - -#elif defined SSD1306_96_16 - ssd1306_command(SSD1306_SETCOMPINS); // 0xDA - ssd1306_command(0x2); //ada x12 - ssd1306_command(SSD1306_SETCONTRAST); // 0x81 - if (vccstate == SSD1306_EXTERNALVCC) - { ssd1306_command(0x10); } - else - { ssd1306_command(0xAF); } - -#endif - - ssd1306_command(SSD1306_SETPRECHARGE); // 0xd9 - if (vccstate == SSD1306_EXTERNALVCC) - { ssd1306_command(0x22); } - else - { ssd1306_command(0xF1); } - ssd1306_command(SSD1306_SETVCOMDETECT); // 0xDB - ssd1306_command(0x40); - ssd1306_command(SSD1306_DISPLAYALLON_RESUME); // 0xA4 - ssd1306_command(SSD1306_NORMALDISPLAY); // 0xA6 - - ssd1306_command(SSD1306_DEACTIVATE_SCROLL); - - ssd1306_command(SSD1306_DISPLAYON);//--turn on oled panel -} - - -void Adafruit_SSD1306::invertDisplay(uint8_t i) { - if (i) { - ssd1306_command(SSD1306_INVERTDISPLAY); - } else { - ssd1306_command(SSD1306_NORMALDISPLAY); - } -} - -void Adafruit_SSD1306::ssd1306_command(uint8_t c) { - if (sid != -1) - { - // SPI -#ifdef HAVE_PORTREG - *csport |= cspinmask; - *dcport &= ~dcpinmask; - *csport &= ~cspinmask; -#else - digitalWrite(cs, HIGH); - digitalWrite(dc, LOW); - digitalWrite(cs, LOW); -#endif - fastSPIwrite(c); -#ifdef HAVE_PORTREG - *csport |= cspinmask; -#else - digitalWrite(cs, HIGH); -#endif - } - else - { - // I2C - uint8_t control = 0x00; // Co = 0, D/C = 0 - Wire.beginTransmission(_i2caddr); - Wire.write(control); - Wire.write(c); - Wire.endTransmission(); - } -} - -// startscrollright -// Activate a right handed scroll for rows start through stop -// Hint, the display is 16 rows tall. To scroll the whole display, run: -// display.scrollright(0x00, 0x0F) -void Adafruit_SSD1306::startscrollright(uint8_t start, uint8_t stop){ - ssd1306_command(SSD1306_RIGHT_HORIZONTAL_SCROLL); - ssd1306_command(0X00); - ssd1306_command(start); - ssd1306_command(0X00); - ssd1306_command(stop); - ssd1306_command(0X00); - ssd1306_command(0XFF); - ssd1306_command(SSD1306_ACTIVATE_SCROLL); -} - -// startscrollleft -// Activate a right handed scroll for rows start through stop -// Hint, the display is 16 rows tall. To scroll the whole display, run: -// display.scrollright(0x00, 0x0F) -void Adafruit_SSD1306::startscrollleft(uint8_t start, uint8_t stop){ - ssd1306_command(SSD1306_LEFT_HORIZONTAL_SCROLL); - ssd1306_command(0X00); - ssd1306_command(start); - ssd1306_command(0X00); - ssd1306_command(stop); - ssd1306_command(0X00); - ssd1306_command(0XFF); - ssd1306_command(SSD1306_ACTIVATE_SCROLL); -} - -// startscrolldiagright -// Activate a diagonal scroll for rows start through stop -// Hint, the display is 16 rows tall. To scroll the whole display, run: -// display.scrollright(0x00, 0x0F) -void Adafruit_SSD1306::startscrolldiagright(uint8_t start, uint8_t stop){ - ssd1306_command(SSD1306_SET_VERTICAL_SCROLL_AREA); - ssd1306_command(0X00); - ssd1306_command(SSD1306_LCDHEIGHT); - ssd1306_command(SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL); - ssd1306_command(0X00); - ssd1306_command(start); - ssd1306_command(0X00); - ssd1306_command(stop); - ssd1306_command(0X01); - ssd1306_command(SSD1306_ACTIVATE_SCROLL); -} - -// startscrolldiagleft -// Activate a diagonal scroll for rows start through stop -// Hint, the display is 16 rows tall. To scroll the whole display, run: -// display.scrollright(0x00, 0x0F) -void Adafruit_SSD1306::startscrolldiagleft(uint8_t start, uint8_t stop){ - ssd1306_command(SSD1306_SET_VERTICAL_SCROLL_AREA); - ssd1306_command(0X00); - ssd1306_command(SSD1306_LCDHEIGHT); - ssd1306_command(SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL); - ssd1306_command(0X00); - ssd1306_command(start); - ssd1306_command(0X00); - ssd1306_command(stop); - ssd1306_command(0X01); - ssd1306_command(SSD1306_ACTIVATE_SCROLL); -} - -void Adafruit_SSD1306::stopscroll(void){ - ssd1306_command(SSD1306_DEACTIVATE_SCROLL); -} - -// Dim the display -// dim = true: display is dimmed -// dim = false: display is normal -void Adafruit_SSD1306::dim(boolean dim) { - uint8_t contrast; - - if (dim) { - contrast = 0; // Dimmed display - } else { - if (_vccstate == SSD1306_EXTERNALVCC) { - contrast = 0x9F; - } else { - contrast = 0xCF; - } - } - // the range of contrast to too small to be really useful - // it is useful to dim the display - ssd1306_command(SSD1306_SETCONTRAST); - ssd1306_command(contrast); -} - -void Adafruit_SSD1306::display(void) { - ssd1306_command(SSD1306_COLUMNADDR); - ssd1306_command(0); // Column start address (0 = reset) - ssd1306_command(SSD1306_LCDWIDTH-1); // Column end address (127 = reset) - - ssd1306_command(SSD1306_PAGEADDR); - ssd1306_command(0); // Page start address (0 = reset) - #if SSD1306_LCDHEIGHT == 64 - ssd1306_command(7); // Page end address - #endif - #if SSD1306_LCDHEIGHT == 32 - ssd1306_command(3); // Page end address - #endif - #if SSD1306_LCDHEIGHT == 16 - ssd1306_command(1); // Page end address - #endif - - if (sid != -1) - { - // SPI -#ifdef HAVE_PORTREG - *csport |= cspinmask; - *dcport |= dcpinmask; - *csport &= ~cspinmask; -#else - digitalWrite(cs, HIGH); - digitalWrite(dc, HIGH); - digitalWrite(cs, LOW); -#endif - - for (uint16_t i=0; i<(SSD1306_LCDWIDTH*SSD1306_LCDHEIGHT/8); i++) { - fastSPIwrite(buffer[i]); - } -#ifdef HAVE_PORTREG - *csport |= cspinmask; -#else - digitalWrite(cs, HIGH); -#endif - } - else - { - // save I2C bitrate -#ifdef TWBR - uint8_t twbrbackup = TWBR; - TWBR = 12; // upgrade to 400KHz! -#endif - - //Serial.println(TWBR, DEC); - //Serial.println(TWSR & 0x3, DEC); - - // I2C - for (uint16_t i=0; i<(SSD1306_LCDWIDTH*SSD1306_LCDHEIGHT/8); i++) { - // send a bunch of data in one xmission - Wire.beginTransmission(_i2caddr); - WIRE_WRITE(0x40); - for (uint8_t x=0; x<16; x++) { - WIRE_WRITE(buffer[i]); - i++; - } - i--; - Wire.endTransmission(); - } -#ifdef TWBR - TWBR = twbrbackup; -#endif - } -} - -// clear everything -void Adafruit_SSD1306::clearDisplay(void) { - memset(buffer, 0, (SSD1306_LCDWIDTH*SSD1306_LCDHEIGHT/8)); -} - - -inline void Adafruit_SSD1306::fastSPIwrite(uint8_t d) { - - if(hwSPI) { - (void)SPI.transfer(d); - } else { - for(uint8_t bit = 0x80; bit; bit >>= 1) { -#ifdef HAVE_PORTREG - *clkport &= ~clkpinmask; - if(d & bit) *mosiport |= mosipinmask; - else *mosiport &= ~mosipinmask; - *clkport |= clkpinmask; -#else - digitalWrite(sclk, LOW); - if(d & bit) digitalWrite(sid, HIGH); - else digitalWrite(sid, LOW); - digitalWrite(sclk, HIGH); -#endif - } - } -} - -void Adafruit_SSD1306::drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color) { - boolean bSwap = false; - switch(rotation) { - case 0: - // 0 degree rotation, do nothing - break; - case 1: - // 90 degree rotation, swap x & y for rotation, then invert x - bSwap = true; - ssd1306_swap(x, y); - x = WIDTH - x - 1; - break; - case 2: - // 180 degree rotation, invert x and y - then shift y around for height. - x = WIDTH - x - 1; - y = HEIGHT - y - 1; - x -= (w-1); - break; - case 3: - // 270 degree rotation, swap x & y for rotation, then invert y and adjust y for w (not to become h) - bSwap = true; - ssd1306_swap(x, y); - y = HEIGHT - y - 1; - y -= (w-1); - break; - } - - if(bSwap) { - drawFastVLineInternal(x, y, w, color); - } else { - drawFastHLineInternal(x, y, w, color); - } -} - -void Adafruit_SSD1306::drawFastHLineInternal(int16_t x, int16_t y, int16_t w, uint16_t color) { - // Do bounds/limit checks - if(y < 0 || y >= HEIGHT) { return; } - - // make sure we don't try to draw below 0 - if(x < 0) { - w += x; - x = 0; - } - - // make sure we don't go off the edge of the display - if( (x + w) > WIDTH) { - w = (WIDTH - x); - } - - // if our width is now negative, punt - if(w <= 0) { return; } - - // set up the pointer for movement through the buffer - register uint8_t *pBuf = buffer; - // adjust the buffer pointer for the current row - pBuf += ((y/8) * SSD1306_LCDWIDTH); - // and offset x columns in - pBuf += x; - - register uint8_t mask = 1 << (y&7); - - switch (color) - { - case WHITE: while(w--) { *pBuf++ |= mask; }; break; - case BLACK: mask = ~mask; while(w--) { *pBuf++ &= mask; }; break; - case INVERSE: while(w--) { *pBuf++ ^= mask; }; break; - } -} - -void Adafruit_SSD1306::drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color) { - bool bSwap = false; - switch(rotation) { - case 0: - break; - case 1: - // 90 degree rotation, swap x & y for rotation, then invert x and adjust x for h (now to become w) - bSwap = true; - ssd1306_swap(x, y); - x = WIDTH - x - 1; - x -= (h-1); - break; - case 2: - // 180 degree rotation, invert x and y - then shift y around for height. - x = WIDTH - x - 1; - y = HEIGHT - y - 1; - y -= (h-1); - break; - case 3: - // 270 degree rotation, swap x & y for rotation, then invert y - bSwap = true; - ssd1306_swap(x, y); - y = HEIGHT - y - 1; - break; - } - - if(bSwap) { - drawFastHLineInternal(x, y, h, color); - } else { - drawFastVLineInternal(x, y, h, color); - } -} - - -void Adafruit_SSD1306::drawFastVLineInternal(int16_t x, int16_t __y, int16_t __h, uint16_t color) { - - // do nothing if we're off the left or right side of the screen - if(x < 0 || x >= WIDTH) { return; } - - // make sure we don't try to draw below 0 - if(__y < 0) { - // __y is negative, this will subtract enough from __h to account for __y being 0 - __h += __y; - __y = 0; - - } - - // make sure we don't go past the height of the display - if( (__y + __h) > HEIGHT) { - __h = (HEIGHT - __y); - } - - // if our height is now negative, punt - if(__h <= 0) { - return; - } - - // this display doesn't need ints for coordinates, use local byte registers for faster juggling - register uint8_t y = __y; - register uint8_t h = __h; - - - // set up the pointer for fast movement through the buffer - register uint8_t *pBuf = buffer; - // adjust the buffer pointer for the current row - pBuf += ((y/8) * SSD1306_LCDWIDTH); - // and offset x columns in - pBuf += x; - - // do the first partial byte, if necessary - this requires some masking - register uint8_t mod = (y&7); - if(mod) { - // mask off the high n bits we want to set - mod = 8-mod; - - // note - lookup table results in a nearly 10% performance improvement in fill* functions - // register uint8_t mask = ~(0xFF >> (mod)); - static uint8_t premask[8] = {0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE }; - register uint8_t mask = premask[mod]; - - // adjust the mask if we're not going to reach the end of this byte - if( h < mod) { - mask &= (0XFF >> (mod-h)); - } - - switch (color) - { - case WHITE: *pBuf |= mask; break; - case BLACK: *pBuf &= ~mask; break; - case INVERSE: *pBuf ^= mask; break; - } - - // fast exit if we're done here! - if(h= 8) { - if (color == INVERSE) { // separate copy of the code so we don't impact performance of the black/white write version with an extra comparison per loop - do { - *pBuf=~(*pBuf); - - // adjust the buffer forward 8 rows worth of data - pBuf += SSD1306_LCDWIDTH; - - // adjust h & y (there's got to be a faster way for me to do this, but this should still help a fair bit for now) - h -= 8; - } while(h >= 8); - } - else { - // store a local value to work with - register uint8_t val = (color == WHITE) ? 255 : 0; - - do { - // write our value in - *pBuf = val; - - // adjust the buffer forward 8 rows worth of data - pBuf += SSD1306_LCDWIDTH; - - // adjust h & y (there's got to be a faster way for me to do this, but this should still help a fair bit for now) - h -= 8; - } while(h >= 8); - } - } - - // now do the final partial byte, if necessary - if(h) { - mod = h & 7; - // this time we want to mask the low bits of the byte, vs the high bits we did above - // register uint8_t mask = (1 << mod) - 1; - // note - lookup table results in a nearly 10% performance improvement in fill* functions - static uint8_t postmask[8] = {0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F }; - register uint8_t mask = postmask[mod]; - switch (color) - { - case WHITE: *pBuf |= mask; break; - case BLACK: *pBuf &= ~mask; break; - case INVERSE: *pBuf ^= mask; break; - } - } -} diff --git a/lib/Adafruit_SSD1306-1.1.2/Adafruit_SSD1306.h b/lib/Adafruit_SSD1306-1.1.2/Adafruit_SSD1306.h deleted file mode 100644 index 1d43dfddf..000000000 --- a/lib/Adafruit_SSD1306-1.1.2/Adafruit_SSD1306.h +++ /dev/null @@ -1,186 +0,0 @@ -/********************************************************************* -This is a library for our Monochrome OLEDs based on SSD1306 drivers - - Pick one up today in the adafruit shop! - ------> http://www.adafruit.com/category/63_98 - -These displays use SPI to communicate, 4 or 5 pins are required to -interface - -Adafruit invests time and resources providing this open source code, -please support Adafruit and open-source hardware by purchasing -products from Adafruit! - -Written by Limor Fried/Ladyada for Adafruit Industries. -BSD license, check license.txt for more information -All text above, and the splash screen must be included in any redistribution -*********************************************************************/ -#ifndef _Adafruit_SSD1306_H_ -#define _Adafruit_SSD1306_H_ - -#if ARDUINO >= 100 - #include "Arduino.h" - #define WIRE_WRITE Wire.write -#else - #include "WProgram.h" - #define WIRE_WRITE Wire.send -#endif - -#if defined(__SAM3X8E__) - typedef volatile RwReg PortReg; - typedef uint32_t PortMask; - #define HAVE_PORTREG -#elif defined(ARDUINO_ARCH_SAMD) -// not supported -#elif defined(ESP8266) || defined(ESP32) || defined(ARDUINO_STM32_FEATHER) || defined(__arc__) - typedef volatile uint32_t PortReg; - typedef uint32_t PortMask; -#elif defined(__AVR__) - typedef volatile uint8_t PortReg; - typedef uint8_t PortMask; - #define HAVE_PORTREG -#else - // chances are its 32 bit so assume that - typedef volatile uint32_t PortReg; - typedef uint32_t PortMask; -#endif - -#include -#include - -#define BLACK 0 -#define WHITE 1 -#define INVERSE 2 - -#define SSD1306_I2C_ADDRESS 0x3C // 011110+SA0+RW - 0x3C or 0x3D -// Address for 128x32 is 0x3C -// Address for 128x64 is 0x3D (default) or 0x3C (if SA0 is grounded) - -/*========================================================================= - SSD1306 Displays - ----------------------------------------------------------------------- - The driver is used in multiple displays (128x64, 128x32, etc.). - Select the appropriate display below to create an appropriately - sized framebuffer, etc. - - SSD1306_128_64 128x64 pixel display - - SSD1306_128_32 128x32 pixel display - - SSD1306_96_16 - - -----------------------------------------------------------------------*/ - #define SSD1306_128_64 -// #define SSD1306_128_32 -// #define SSD1306_96_16 -/*=========================================================================*/ - -#if defined SSD1306_128_64 && defined SSD1306_128_32 - #error "Only one SSD1306 display can be specified at once in SSD1306.h" -#endif -#if !defined SSD1306_128_64 && !defined SSD1306_128_32 && !defined SSD1306_96_16 - #error "At least one SSD1306 display must be specified in SSD1306.h" -#endif - -#if defined SSD1306_128_64 - #define SSD1306_LCDWIDTH 128 - #define SSD1306_LCDHEIGHT 64 -#endif -#if defined SSD1306_128_32 - #define SSD1306_LCDWIDTH 128 - #define SSD1306_LCDHEIGHT 32 -#endif -#if defined SSD1306_96_16 - #define SSD1306_LCDWIDTH 96 - #define SSD1306_LCDHEIGHT 16 -#endif - -#define SSD1306_SETCONTRAST 0x81 -#define SSD1306_DISPLAYALLON_RESUME 0xA4 -#define SSD1306_DISPLAYALLON 0xA5 -#define SSD1306_NORMALDISPLAY 0xA6 -#define SSD1306_INVERTDISPLAY 0xA7 -#define SSD1306_DISPLAYOFF 0xAE -#define SSD1306_DISPLAYON 0xAF - -#define SSD1306_SETDISPLAYOFFSET 0xD3 -#define SSD1306_SETCOMPINS 0xDA - -#define SSD1306_SETVCOMDETECT 0xDB - -#define SSD1306_SETDISPLAYCLOCKDIV 0xD5 -#define SSD1306_SETPRECHARGE 0xD9 - -#define SSD1306_SETMULTIPLEX 0xA8 - -#define SSD1306_SETLOWCOLUMN 0x00 -#define SSD1306_SETHIGHCOLUMN 0x10 - -#define SSD1306_SETSTARTLINE 0x40 - -#define SSD1306_MEMORYMODE 0x20 -#define SSD1306_COLUMNADDR 0x21 -#define SSD1306_PAGEADDR 0x22 - -#define SSD1306_COMSCANINC 0xC0 -#define SSD1306_COMSCANDEC 0xC8 - -#define SSD1306_SEGREMAP 0xA0 - -#define SSD1306_CHARGEPUMP 0x8D - -#define SSD1306_EXTERNALVCC 0x1 -#define SSD1306_SWITCHCAPVCC 0x2 - -// Scrolling #defines -#define SSD1306_ACTIVATE_SCROLL 0x2F -#define SSD1306_DEACTIVATE_SCROLL 0x2E -#define SSD1306_SET_VERTICAL_SCROLL_AREA 0xA3 -#define SSD1306_RIGHT_HORIZONTAL_SCROLL 0x26 -#define SSD1306_LEFT_HORIZONTAL_SCROLL 0x27 -#define SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL 0x29 -#define SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL 0x2A - -class Adafruit_SSD1306 : public Adafruit_GFX { - public: - Adafruit_SSD1306(int8_t SID, int8_t SCLK, int8_t DC, int8_t RST, int8_t CS); - Adafruit_SSD1306(int8_t DC, int8_t RST, int8_t CS); - Adafruit_SSD1306(int8_t RST = -1); - - void begin(uint8_t switchvcc = SSD1306_SWITCHCAPVCC, uint8_t i2caddr = SSD1306_I2C_ADDRESS, bool reset=true); - void ssd1306_command(uint8_t c); - - void clearDisplay(void); - void invertDisplay(uint8_t i); - void display(); - - void startscrollright(uint8_t start, uint8_t stop); - void startscrollleft(uint8_t start, uint8_t stop); - - void startscrolldiagright(uint8_t start, uint8_t stop); - void startscrolldiagleft(uint8_t start, uint8_t stop); - void stopscroll(void); - - void dim(boolean dim); - - void drawPixel(int16_t x, int16_t y, uint16_t color); - - virtual void drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color); - virtual void drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color); - - private: - int8_t _i2caddr, _vccstate, sid, sclk, dc, rst, cs; - void fastSPIwrite(uint8_t c); - - boolean hwSPI; -#ifdef HAVE_PORTREG - PortReg *mosiport, *clkport, *csport, *dcport; - PortMask mosipinmask, clkpinmask, cspinmask, dcpinmask; -#endif - - inline void drawFastVLineInternal(int16_t x, int16_t y, int16_t h, uint16_t color) __attribute__((always_inline)); - inline void drawFastHLineInternal(int16_t x, int16_t y, int16_t w, uint16_t color) __attribute__((always_inline)); - -}; - -#endif /* _Adafruit_SSD1306_H_ */ diff --git a/lib/Adafruit_SSD1306-1.1.2/README.md b/lib/Adafruit_SSD1306-1.1.2/README.md deleted file mode 100644 index d76bb285c..000000000 --- a/lib/Adafruit_SSD1306-1.1.2/README.md +++ /dev/null @@ -1,32 +0,0 @@ -# Adafruit_SSD1306 - - -## Compatibility - -MCU | Tested Works | Doesn't Work | Not Tested | Notes ------------------- | :----------: | :----------: | :---------: | ----- -Atmega328 @ 16MHz | X | | | -Atmega328 @ 12MHz | X | | | -Atmega32u4 @ 16MHz | X | | | -Atmega32u4 @ 8MHz | X | | | -ESP8266 | X | | | change OLED_RESET to different pin if using default I2C pins D4/D5. -Atmega2560 @ 16MHz | X | | | -ATSAM3X8E | X | | | -ATSAM21D | X | | | -ATtiny85 @ 16MHz | | X | | -ATtiny85 @ 8MHz | | X | | -Intel Curie @ 32MHz | | | X | -STM32F2 | | | X | - - * ATmega328 @ 16MHz : Arduino UNO, Adafruit Pro Trinket 5V, Adafruit Metro 328, Adafruit Metro Mini - * ATmega328 @ 12MHz : Adafruit Pro Trinket 3V - * ATmega32u4 @ 16MHz : Arduino Leonardo, Arduino Micro, Arduino Yun, Teensy 2.0 - * ATmega32u4 @ 8MHz : Adafruit Flora, Bluefruit Micro - * ESP8266 : Adafruit Huzzah - * ATmega2560 @ 16MHz : Arduino Mega - * ATSAM3X8E : Arduino Due - * ATSAM21D : Arduino Zero, M0 Pro - * ATtiny85 @ 16MHz : Adafruit Trinket 5V - * ATtiny85 @ 8MHz : Adafruit Gemma, Arduino Gemma, Adafruit Trinket 3V - - diff --git a/lib/Adafruit_SSD1306-1.1.2/README.txt b/lib/Adafruit_SSD1306-1.1.2/README.txt deleted file mode 100644 index 420cc153c..000000000 --- a/lib/Adafruit_SSD1306-1.1.2/README.txt +++ /dev/null @@ -1,24 +0,0 @@ -This is a library for our Monochrome OLEDs based on SSD1306 drivers - - Pick one up today in the adafruit shop! - ------> http://www.adafruit.com/category/63_98 - -These displays use SPI to communicate, 4 or 5 pins are required to -interface - -Adafruit invests time and resources providing this open source code, -please support Adafruit and open-source hardware by purchasing -products from Adafruit! - -Written by Limor Fried/Ladyada for Adafruit Industries. -Scrolling code contributed by Michael Gregg -BSD license, check license.txt for more information -All text above must be included in any redistribution - -To download. click the DOWNLOADS button in the top right corner, rename the uncompressed folder Adafruit_SSD1306. Check that the Adafruit_SSD1306 folder contains Adafruit_SSD1306.cpp and Adafruit_SSD1306.h - -Place the Adafruit_SSD1306 library folder your /libraries/ folder. You may need to create the libraries subfolder if its your first library. Restart the IDE. - -You will also have to download the Adafruit GFX Graphics core which does all the circles, text, rectangles, etc. You can get it from -https://github.com/adafruit/Adafruit-GFX-Library -and download/install that library as well \ No newline at end of file diff --git a/lib/Adafruit_SSD1306-1.1.2/examples/ssd1306_128x32_i2c/ssd1306_128x32_i2c.ino b/lib/Adafruit_SSD1306-1.1.2/examples/ssd1306_128x32_i2c/ssd1306_128x32_i2c.ino deleted file mode 100644 index b3b8bfa9a..000000000 --- a/lib/Adafruit_SSD1306-1.1.2/examples/ssd1306_128x32_i2c/ssd1306_128x32_i2c.ino +++ /dev/null @@ -1,375 +0,0 @@ -/********************************************************************* -This is an example for our Monochrome OLEDs based on SSD1306 drivers - - Pick one up today in the adafruit shop! - ------> http://www.adafruit.com/category/63_98 - -This example is for a 128x32 size display using I2C to communicate -3 pins are required to interface (2 I2C and one reset) - -Adafruit invests time and resources providing this open source code, -please support Adafruit and open-source hardware by purchasing -products from Adafruit! - -Written by Limor Fried/Ladyada for Adafruit Industries. -BSD license, check license.txt for more information -All text above, and the splash screen must be included in any redistribution -*********************************************************************/ - -#include -#include -#include -#include - -#define OLED_RESET 4 -Adafruit_SSD1306 display(OLED_RESET); - -#define NUMFLAKES 10 -#define XPOS 0 -#define YPOS 1 -#define DELTAY 2 - - -#define LOGO16_GLCD_HEIGHT 16 -#define LOGO16_GLCD_WIDTH 16 -static const unsigned char PROGMEM logo16_glcd_bmp[] = -{ B00000000, B11000000, - B00000001, B11000000, - B00000001, B11000000, - B00000011, B11100000, - B11110011, B11100000, - B11111110, B11111000, - B01111110, B11111111, - B00110011, B10011111, - B00011111, B11111100, - B00001101, B01110000, - B00011011, B10100000, - B00111111, B11100000, - B00111111, B11110000, - B01111100, B11110000, - B01110000, B01110000, - B00000000, B00110000 }; - -#if (SSD1306_LCDHEIGHT != 32) -#error("Height incorrect, please fix Adafruit_SSD1306.h!"); -#endif - -void setup() { - Serial.begin(9600); - - // by default, we'll generate the high voltage from the 3.3v line internally! (neat!) - display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // initialize with the I2C addr 0x3C (for the 128x32) - // init done - - // Show image buffer on the display hardware. - // Since the buffer is intialized with an Adafruit splashscreen - // internally, this will display the splashscreen. - display.display(); - delay(2000); - - // Clear the buffer. - display.clearDisplay(); - - // draw a single pixel - display.drawPixel(10, 10, WHITE); - // Show the display buffer on the hardware. - // NOTE: You _must_ call display after making any drawing commands - // to make them visible on the display hardware! - display.display(); - delay(2000); - display.clearDisplay(); - - // draw many lines - testdrawline(); - display.display(); - delay(2000); - display.clearDisplay(); - - // draw rectangles - testdrawrect(); - display.display(); - delay(2000); - display.clearDisplay(); - - // draw multiple rectangles - testfillrect(); - display.display(); - delay(2000); - display.clearDisplay(); - - // draw mulitple circles - testdrawcircle(); - display.display(); - delay(2000); - display.clearDisplay(); - - // draw a white circle, 10 pixel radius - display.fillCircle(display.width()/2, display.height()/2, 10, WHITE); - display.display(); - delay(2000); - display.clearDisplay(); - - testdrawroundrect(); - delay(2000); - display.clearDisplay(); - - testfillroundrect(); - delay(2000); - display.clearDisplay(); - - testdrawtriangle(); - delay(2000); - display.clearDisplay(); - - testfilltriangle(); - delay(2000); - display.clearDisplay(); - - // draw the first ~12 characters in the font - testdrawchar(); - display.display(); - delay(2000); - display.clearDisplay(); - - // draw scrolling text - testscrolltext(); - delay(2000); - display.clearDisplay(); - - // text display tests - display.setTextSize(1); - display.setTextColor(WHITE); - display.setCursor(0,0); - display.println("Hello, world!"); - display.setTextColor(BLACK, WHITE); // 'inverted' text - display.println(3.141592); - display.setTextSize(2); - display.setTextColor(WHITE); - display.print("0x"); display.println(0xDEADBEEF, HEX); - display.display(); - delay(2000); - display.clearDisplay(); - - // miniature bitmap display - display.drawBitmap(30, 16, logo16_glcd_bmp, 16, 16, 1); - display.display(); - delay(1); - - // invert the display - display.invertDisplay(true); - delay(1000); - display.invertDisplay(false); - delay(1000); - display.clearDisplay(); - - // draw a bitmap icon and 'animate' movement - testdrawbitmap(logo16_glcd_bmp, LOGO16_GLCD_HEIGHT, LOGO16_GLCD_WIDTH); -} - - -void loop() { - -} - - -void testdrawbitmap(const uint8_t *bitmap, uint8_t w, uint8_t h) { - uint8_t icons[NUMFLAKES][3]; - - // initialize - for (uint8_t f=0; f< NUMFLAKES; f++) { - icons[f][XPOS] = random(display.width()); - icons[f][YPOS] = 0; - icons[f][DELTAY] = random(5) + 1; - - Serial.print("x: "); - Serial.print(icons[f][XPOS], DEC); - Serial.print(" y: "); - Serial.print(icons[f][YPOS], DEC); - Serial.print(" dy: "); - Serial.println(icons[f][DELTAY], DEC); - } - - while (1) { - // draw each icon - for (uint8_t f=0; f< NUMFLAKES; f++) { - display.drawBitmap(icons[f][XPOS], icons[f][YPOS], bitmap, w, h, WHITE); - } - display.display(); - delay(200); - - // then erase it + move it - for (uint8_t f=0; f< NUMFLAKES; f++) { - display.drawBitmap(icons[f][XPOS], icons[f][YPOS], bitmap, w, h, BLACK); - // move it - icons[f][YPOS] += icons[f][DELTAY]; - // if its gone, reinit - if (icons[f][YPOS] > display.height()) { - icons[f][XPOS] = random(display.width()); - icons[f][YPOS] = 0; - icons[f][DELTAY] = random(5) + 1; - } - } - } -} - - -void testdrawchar(void) { - display.setTextSize(1); - display.setTextColor(WHITE); - display.setCursor(0,0); - - for (uint8_t i=0; i < 168; i++) { - if (i == '\n') continue; - display.write(i); - if ((i > 0) && (i % 21 == 0)) - display.println(); - } - display.display(); - delay(1); -} - -void testdrawcircle(void) { - for (int16_t i=0; i0; i-=5) { - display.fillTriangle(display.width()/2, display.height()/2-i, - display.width()/2-i, display.height()/2+i, - display.width()/2+i, display.height()/2+i, WHITE); - if (color == WHITE) color = BLACK; - else color = WHITE; - display.display(); - delay(1); - } -} - -void testdrawroundrect(void) { - for (int16_t i=0; i=0; i-=4) { - display.drawLine(0, display.height()-1, display.width()-1, i, WHITE); - display.display(); - delay(1); - } - delay(250); - - display.clearDisplay(); - for (int16_t i=display.width()-1; i>=0; i-=4) { - display.drawLine(display.width()-1, display.height()-1, i, 0, WHITE); - display.display(); - delay(1); - } - for (int16_t i=display.height()-1; i>=0; i-=4) { - display.drawLine(display.width()-1, display.height()-1, 0, i, WHITE); - display.display(); - delay(1); - } - delay(250); - - display.clearDisplay(); - for (int16_t i=0; i http://www.adafruit.com/category/63_98 - -This example is for a 128x32 size display using SPI to communicate -4 or 5 pins are required to interface - -Adafruit invests time and resources providing this open source code, -please support Adafruit and open-source hardware by purchasing -products from Adafruit! - -Written by Limor Fried/Ladyada for Adafruit Industries. -BSD license, check license.txt for more information -All text above, and the splash screen must be included in any redistribution -*********************************************************************/ - -#include -#include -#include -#include - -// If using software SPI (the default case): -#define OLED_MOSI 9 -#define OLED_CLK 10 -#define OLED_DC 11 -#define OLED_CS 12 -#define OLED_RESET 13 -Adafruit_SSD1306 display(OLED_MOSI, OLED_CLK, OLED_DC, OLED_RESET, OLED_CS); - -/* Uncomment this block to use hardware SPI -#define OLED_DC 6 -#define OLED_CS 7 -#define OLED_RESET 8 -Adafruit_SSD1306 display(OLED_DC, OLED_RESET, OLED_CS); -*/ - -#define NUMFLAKES 10 -#define XPOS 0 -#define YPOS 1 -#define DELTAY 2 - -#define LOGO16_GLCD_HEIGHT 16 -#define LOGO16_GLCD_WIDTH 16 -static const unsigned char PROGMEM logo16_glcd_bmp[] = -{ B00000000, B11000000, - B00000001, B11000000, - B00000001, B11000000, - B00000011, B11100000, - B11110011, B11100000, - B11111110, B11111000, - B01111110, B11111111, - B00110011, B10011111, - B00011111, B11111100, - B00001101, B01110000, - B00011011, B10100000, - B00111111, B11100000, - B00111111, B11110000, - B01111100, B11110000, - B01110000, B01110000, - B00000000, B00110000 }; - -#if (SSD1306_LCDHEIGHT != 32) -#error("Height incorrect, please fix Adafruit_SSD1306.h!"); -#endif - -void setup() { - Serial.begin(9600); - - // by default, we'll generate the high voltage from the 3.3v line internally! (neat!) - display.begin(SSD1306_SWITCHCAPVCC); - // init done - - // Show image buffer on the display hardware. - // Since the buffer is intialized with an Adafruit splashscreen - // internally, this will display the splashscreen. - display.display(); - delay(2000); - - // Clear the buffer. - display.clearDisplay(); - - // draw a single pixel - display.drawPixel(10, 10, WHITE); - // Show the display buffer on the hardware. - // NOTE: You _must_ call display after making any drawing commands - // to make them visible on the display hardware! - display.display(); - delay(2000); - display.clearDisplay(); - - // draw many lines - testdrawline(); - display.display(); - delay(2000); - display.clearDisplay(); - - // draw rectangles - testdrawrect(); - display.display(); - delay(2000); - display.clearDisplay(); - - // draw multiple rectangles - testfillrect(); - display.display(); - delay(2000); - display.clearDisplay(); - - // draw mulitple circles - testdrawcircle(); - display.display(); - delay(2000); - display.clearDisplay(); - - // draw a white circle, 10 pixel radius - display.fillCircle(display.width()/2, display.height()/2, 10, WHITE); - display.display(); - delay(2000); - display.clearDisplay(); - - testdrawroundrect(); - delay(2000); - display.clearDisplay(); - - testfillroundrect(); - delay(2000); - display.clearDisplay(); - - testdrawtriangle(); - delay(2000); - display.clearDisplay(); - - testfilltriangle(); - delay(2000); - display.clearDisplay(); - - // draw the first ~12 characters in the font - testdrawchar(); - display.display(); - delay(2000); - display.clearDisplay(); - - // draw scrolling text - testscrolltext(); - delay(2000); - display.clearDisplay(); - - // text display tests - display.setTextSize(1); - display.setTextColor(WHITE); - display.setCursor(0,0); - display.println("Hello, world!"); - display.setTextColor(BLACK, WHITE); // 'inverted' text - display.println(3.141592); - display.setTextSize(2); - display.setTextColor(WHITE); - display.print("0x"); display.println(0xDEADBEEF, HEX); - display.display(); - delay(2000); - display.clearDisplay(); - - // miniature bitmap display - display.drawBitmap(30, 16, logo16_glcd_bmp, 16, 16, 1); - display.display(); - - // invert the display - display.invertDisplay(true); - delay(1000); - display.invertDisplay(false); - delay(1000); - display.clearDisplay(); - - // draw a bitmap icon and 'animate' movement - testdrawbitmap(logo16_glcd_bmp, LOGO16_GLCD_HEIGHT, LOGO16_GLCD_WIDTH); -} - - -void loop() { - -} - - -void testdrawbitmap(const uint8_t *bitmap, uint8_t w, uint8_t h) { - uint8_t icons[NUMFLAKES][3]; - - // initialize - for (uint8_t f=0; f< NUMFLAKES; f++) { - icons[f][XPOS] = random(display.width()); - icons[f][YPOS] = 0; - icons[f][DELTAY] = random(5) + 1; - - Serial.print("x: "); - Serial.print(icons[f][XPOS], DEC); - Serial.print(" y: "); - Serial.print(icons[f][YPOS], DEC); - Serial.print(" dy: "); - Serial.println(icons[f][DELTAY], DEC); - } - - while (1) { - // draw each icon - for (uint8_t f=0; f< NUMFLAKES; f++) { - display.drawBitmap(icons[f][XPOS], icons[f][YPOS], bitmap, w, h, WHITE); - } - display.display(); - delay(200); - - // then erase it + move it - for (uint8_t f=0; f< NUMFLAKES; f++) { - display.drawBitmap(icons[f][XPOS], icons[f][YPOS], bitmap, w, h, BLACK); - // move it - icons[f][YPOS] += icons[f][DELTAY]; - // if its gone, reinit - if (icons[f][YPOS] > display.height()) { - icons[f][XPOS] = random(display.width()); - icons[f][YPOS] = 0; - icons[f][DELTAY] = random(5) + 1; - } - } - } -} - - -void testdrawchar(void) { - display.setTextSize(1); - display.setTextColor(WHITE); - display.setCursor(0,0); - - for (uint8_t i=0; i < 168; i++) { - if (i == '\n') continue; - display.write(i); - if ((i > 0) && (i % 21 == 0)) - display.println(); - } - display.display(); -} - -void testdrawcircle(void) { - for (int16_t i=0; i0; i-=5) { - display.fillTriangle(display.width()/2, display.height()/2-i, - display.width()/2-i, display.height()/2+i, - display.width()/2+i, display.height()/2+i, WHITE); - if (color == WHITE) color = BLACK; - else color = WHITE; - display.display(); - } -} - -void testdrawroundrect(void) { - for (int16_t i=0; i=0; i-=4) { - display.drawLine(0, display.height()-1, display.width()-1, i, WHITE); - display.display(); - } - delay(250); - - display.clearDisplay(); - for (int16_t i=display.width()-1; i>=0; i-=4) { - display.drawLine(display.width()-1, display.height()-1, i, 0, WHITE); - display.display(); - } - for (int16_t i=display.height()-1; i>=0; i-=4) { - display.drawLine(display.width()-1, display.height()-1, 0, i, WHITE); - display.display(); - } - delay(250); - - display.clearDisplay(); - for (int16_t i=0; i -sentence=SSD1306 oled driver library for 'monochrome' 128x64 and 128x32 OLEDs! -paragraph=SSD1306 oled driver library for 'monochrome' 128x64 and 128x32 OLEDs! -category=Display -url=https://github.com/adafruit/Adafruit_SSD1306 -architectures=* diff --git a/lib/Adafruit_SSD1306-1.1.2/.github/ISSUE_TEMPLATE.md b/lib/Adafruit_SSD1306-1.3.0-gemu-1.1/.github/ISSUE_TEMPLATE.md similarity index 100% rename from lib/Adafruit_SSD1306-1.1.2/.github/ISSUE_TEMPLATE.md rename to lib/Adafruit_SSD1306-1.3.0-gemu-1.1/.github/ISSUE_TEMPLATE.md diff --git a/lib/Adafruit_SSD1306-1.1.2/.github/PULL_REQUEST_TEMPLATE.md b/lib/Adafruit_SSD1306-1.3.0-gemu-1.1/.github/PULL_REQUEST_TEMPLATE.md similarity index 100% rename from lib/Adafruit_SSD1306-1.1.2/.github/PULL_REQUEST_TEMPLATE.md rename to lib/Adafruit_SSD1306-1.3.0-gemu-1.1/.github/PULL_REQUEST_TEMPLATE.md diff --git a/lib/Adafruit_SSD1306-1.3.0-gemu-1.1/.gitignore b/lib/Adafruit_SSD1306-1.3.0-gemu-1.1/.gitignore new file mode 100644 index 000000000..c2a26c038 --- /dev/null +++ b/lib/Adafruit_SSD1306-1.3.0-gemu-1.1/.gitignore @@ -0,0 +1,4 @@ +# Our handy .gitignore for automation ease +Doxyfile* +doxygen_sqlite3.db +html diff --git a/lib/Adafruit_SSD1306-1.3.0-gemu-1.1/.travis.yml b/lib/Adafruit_SSD1306-1.3.0-gemu-1.1/.travis.yml new file mode 100644 index 000000000..1d9184e52 --- /dev/null +++ b/lib/Adafruit_SSD1306-1.3.0-gemu-1.1/.travis.yml @@ -0,0 +1,29 @@ +language: c +sudo: false +cache: + directories: + - ~/arduino_ide + - ~/.arduino15/packages/ +git: + depth: false + quiet: true +env: + global: + - ARDUINO_IDE_VERSION="1.8.5" + - PRETTYNAME="Adafruit SSD1306" +# Optional, will default to "$TRAVIS_BUILD_DIR/Doxyfile" +# - DOXYFILE: $TRAVIS_BUILD_DIR/Doxyfile + +before_install: + - source <(curl -SLs https://raw.githubusercontent.com/adafruit/travis-ci-arduino/master/install.sh) + +install: + - arduino --install-library "Adafruit GFX Library" + +script: + - build_main_platforms + +# Generate and deploy documentation +after_success: + - source <(curl -SLs https://raw.githubusercontent.com/adafruit/travis-ci-arduino/master/library_check.sh) + - source <(curl -SLs https://raw.githubusercontent.com/adafruit/travis-ci-arduino/master/doxy_gen_and_deploy.sh) diff --git a/lib/Adafruit_SSD1306-1.3.0-gemu-1.1/Adafruit_SSD1306.cpp b/lib/Adafruit_SSD1306-1.3.0-gemu-1.1/Adafruit_SSD1306.cpp new file mode 100644 index 000000000..9015c6e83 --- /dev/null +++ b/lib/Adafruit_SSD1306-1.3.0-gemu-1.1/Adafruit_SSD1306.cpp @@ -0,0 +1,1139 @@ +/*! + * @file Adafruit_SSD1306.cpp + * + * @mainpage Arduino library for monochrome OLEDs based on SSD1306 drivers. + * + * @section intro_sec Introduction + * + * This is documentation for Adafruit's SSD1306 library for monochrome + * OLED displays: http://www.adafruit.com/category/63_98 + * + * These displays use I2C or SPI to communicate. I2C requires 2 pins + * (SCL+SDA) and optionally a RESET pin. SPI requires 4 pins (MOSI, SCK, + * select, data/command) and optionally a reset pin. Hardware SPI or + * 'bitbang' software SPI are both supported. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * @section dependencies Dependencies + * + * This library depends on + * Adafruit_GFX being present on your system. Please make sure you have + * installed the latest version before using this library. + * + * @section author Author + * + * Written by Limor Fried/Ladyada for Adafruit Industries, with + * contributions from the open source community. + * + * @section license License + * + * BSD license, all text above, and the splash screen included below, + * must be included in any redistribution. + * + */ + +#ifdef __AVR__ + #include +#elif defined(ESP8266) || defined(ESP32) + #include +#else + #define pgm_read_byte(addr) \ + (*(const unsigned char *)(addr)) ///< PROGMEM workaround for non-AVR +#endif + +#if !defined(__ARM_ARCH) && !defined(ENERGIA) && !defined(ESP8266) && !defined(ESP32) && !defined(__arc__) + #include +#endif + +#include +#include "Adafruit_SSD1306.h" +#include "splash.h" + +// SOME DEFINES AND STATIC VARIABLES USED INTERNALLY ----------------------- + +#if defined(BUFFER_LENGTH) + #define WIRE_MAX BUFFER_LENGTH ///< AVR or similar Wire lib +#elif defined(SERIAL_BUFFER_SIZE) + #define WIRE_MAX (SERIAL_BUFFER_SIZE-1) ///< Newer Wire uses RingBuffer +#else + #define WIRE_MAX 32 ///< Use common Arduino core default +#endif + +#define ssd1306_swap(a, b) \ + (((a) ^= (b)), ((b) ^= (a)), ((a) ^= (b))) ///< No-temp-var swap operation + +#if ARDUINO >= 100 + #define WIRE_WRITE wire->write ///< Wire write function in recent Arduino lib +#else + #define WIRE_WRITE wire->send ///< Wire write function in older Arduino lib +#endif + +#ifdef HAVE_PORTREG + #define SSD1306_SELECT *csPort &= ~csPinMask; ///< Device select + #define SSD1306_DESELECT *csPort |= csPinMask; ///< Device deselect + #define SSD1306_MODE_COMMAND *dcPort &= ~dcPinMask; ///< Command mode + #define SSD1306_MODE_DATA *dcPort |= dcPinMask; ///< Data mode +#else + #define SSD1306_SELECT digitalWrite(csPin, LOW); ///< Device select + #define SSD1306_DESELECT digitalWrite(csPin, HIGH); ///< Device deselect + #define SSD1306_MODE_COMMAND digitalWrite(dcPin, LOW); ///< Command mode + #define SSD1306_MODE_DATA digitalWrite(dcPin, HIGH); ///< Data mode +#endif + +#if (ARDUINO >= 157) && !defined(ARDUINO_STM32_FEATHER) + #define SETWIRECLOCK wire->setClock(wireClk) ///< Set before I2C transfer + #define RESWIRECLOCK wire->setClock(restoreClk) ///< Restore after I2C xfer +#else // setClock() is not present in older Arduino Wire lib (or WICED) + #define SETWIRECLOCK ///< Dummy stand-in define + #define RESWIRECLOCK ///< keeps compiler happy +#endif + +#if defined(SPI_HAS_TRANSACTION) + #define SPI_TRANSACTION_START spi->beginTransaction(spiSettings) ///< Pre-SPI + #define SPI_TRANSACTION_END spi->endTransaction() ///< Post-SPI +#else // SPI transactions likewise not present in older Arduino SPI lib + #define SPI_TRANSACTION_START ///< Dummy stand-in define + #define SPI_TRANSACTION_END ///< keeps compiler happy +#endif + +// The definition of 'transaction' is broadened a bit in the context of +// this library -- referring not just to SPI transactions (if supported +// in the version of the SPI library being used), but also chip select +// (if SPI is being used, whether hardware or soft), and also to the +// beginning and end of I2C transfers (the Wire clock may be sped up before +// issuing data to the display, then restored to the default rate afterward +// so other I2C device types still work). All of these are encapsulated +// in the TRANSACTION_* macros. + +// Check first if Wire, then hardware SPI, then soft SPI: +#define TRANSACTION_START \ + if(wire) { \ + SETWIRECLOCK; \ + } else { \ + if(spi) { \ + SPI_TRANSACTION_START; \ + } \ + SSD1306_SELECT; \ + } ///< Wire, SPI or bitbang transfer setup +#define TRANSACTION_END \ + if(wire) { \ + RESWIRECLOCK; \ + } else { \ + SSD1306_DESELECT; \ + if(spi) { \ + SPI_TRANSACTION_END; \ + } \ + } ///< Wire, SPI or bitbang transfer end + +// CONSTRUCTORS, DESTRUCTOR ------------------------------------------------ + +/*! + @brief Constructor for I2C-interfaced SSD1306 displays. + @param w + Display width in pixels + @param h + Display height in pixels + @param twi + Pointer to an existing TwoWire instance (e.g. &Wire, the + microcontroller's primary I2C bus). + @param rst_pin + Reset pin (using Arduino pin numbering), or -1 if not used + (some displays might be wired to share the microcontroller's + reset pin). + @param clkDuring + Speed (in Hz) for Wire transmissions in SSD1306 library calls. + Defaults to 400000 (400 KHz), a known 'safe' value for most + microcontrollers, and meets the SSD1306 datasheet spec. + Some systems can operate I2C faster (800 KHz for ESP32, 1 MHz + for many other 32-bit MCUs), and some (perhaps not all) + SSD1306's can work with this -- so it's optionally be specified + here and is not a default behavior. (Ignored if using pre-1.5.7 + Arduino software, which operates I2C at a fixed 100 KHz.) + @param clkAfter + Speed (in Hz) for Wire transmissions following SSD1306 library + calls. Defaults to 100000 (100 KHz), the default Arduino Wire + speed. This is done rather than leaving it at the 'during' speed + because other devices on the I2C bus might not be compatible + with the faster rate. (Ignored if using pre-1.5.7 Arduino + software, which operates I2C at a fixed 100 KHz.) + @return Adafruit_SSD1306 object. + @note Call the object's begin() function before use -- buffer + allocation is performed there! +*/ +Adafruit_SSD1306::Adafruit_SSD1306(uint8_t w, uint8_t h, TwoWire *twi, + int8_t rst_pin, uint32_t clkDuring, uint32_t clkAfter) : + Renderer(w, h), spi(NULL), wire(twi ? twi : &Wire), xbuffer(NULL), + mosiPin(-1), clkPin(-1), dcPin(-1), csPin(-1), rstPin(rst_pin), + wireClk(clkDuring), restoreClk(clkAfter) { +} + +/*! + @brief Constructor for SPI SSD1306 displays, using software (bitbang) + SPI. + @param w + Display width in pixels + @param h + Display height in pixels + @param mosi_pin + MOSI (master out, slave in) pin (using Arduino pin numbering). + This transfers serial data from microcontroller to display. + @param sclk_pin + SCLK (serial clock) pin (using Arduino pin numbering). + This clocks each bit from MOSI. + @param dc_pin + Data/command pin (using Arduino pin numbering), selects whether + display is receiving commands (low) or data (high). + @param rst_pin + Reset pin (using Arduino pin numbering), or -1 if not used + (some displays might be wired to share the microcontroller's + reset pin). + @param cs_pin + Chip-select pin (using Arduino pin numbering) for sharing the + bus with other devices. Active low. + @return Adafruit_SSD1306 object. + @note Call the object's begin() function before use -- buffer + allocation is performed there! +*/ +Adafruit_SSD1306::Adafruit_SSD1306(uint8_t w, uint8_t h, + int8_t mosi_pin, int8_t sclk_pin, int8_t dc_pin, int8_t rst_pin, + int8_t cs_pin) : Renderer(w, h), spi(NULL), wire(NULL), xbuffer(NULL), + mosiPin(mosi_pin), clkPin(sclk_pin), dcPin(dc_pin), csPin(cs_pin), + rstPin(rst_pin) { +} + +/*! + @brief Constructor for SPI SSD1306 displays, using native hardware SPI. + @param w + Display width in pixels + @param h + Display height in pixels + @param spi + Pointer to an existing SPIClass instance (e.g. &SPI, the + microcontroller's primary SPI bus). + @param dc_pin + Data/command pin (using Arduino pin numbering), selects whether + display is receiving commands (low) or data (high). + @param rst_pin + Reset pin (using Arduino pin numbering), or -1 if not used + (some displays might be wired to share the microcontroller's + reset pin). + @param cs_pin + Chip-select pin (using Arduino pin numbering) for sharing the + bus with other devices. Active low. + @param bitrate + SPI clock rate for transfers to this display. Default if + unspecified is 8000000UL (8 MHz). + @return Adafruit_SSD1306 object. + @note Call the object's begin() function before use -- buffer + allocation is performed there! +*/ +Adafruit_SSD1306::Adafruit_SSD1306(uint8_t w, uint8_t h, SPIClass *spi, + int8_t dc_pin, int8_t rst_pin, int8_t cs_pin, uint32_t bitrate) : + Renderer(w, h), spi(spi ? spi : &SPI), wire(NULL), xbuffer(NULL), + mosiPin(-1), clkPin(-1), dcPin(dc_pin), csPin(cs_pin), rstPin(rst_pin) { +#ifdef SPI_HAS_TRANSACTION + spiSettings = SPISettings(bitrate, MSBFIRST, SPI_MODE0); +#endif +} + +/*! + @brief DEPRECATED constructor for SPI SSD1306 displays, using software + (bitbang) SPI. Provided for older code to maintain compatibility + with the current library. Screen size is determined by enabling + one of the SSD1306_* size defines in Adafruit_SSD1306.h. New + code should NOT use this. + @param mosi_pin + MOSI (master out, slave in) pin (using Arduino pin numbering). + This transfers serial data from microcontroller to display. + @param sclk_pin + SCLK (serial clock) pin (using Arduino pin numbering). + This clocks each bit from MOSI. + @param dc_pin + Data/command pin (using Arduino pin numbering), selects whether + display is receiving commands (low) or data (high). + @param rst_pin + Reset pin (using Arduino pin numbering), or -1 if not used + (some displays might be wired to share the microcontroller's + reset pin). + @param cs_pin + Chip-select pin (using Arduino pin numbering) for sharing the + bus with other devices. Active low. + @return Adafruit_SSD1306 object. + @note Call the object's begin() function before use -- buffer + allocation is performed there! +*/ +Adafruit_SSD1306::Adafruit_SSD1306(int8_t mosi_pin, int8_t sclk_pin, + int8_t dc_pin, int8_t rst_pin, int8_t cs_pin) : + Renderer(SSD1306_LCDWIDTH, SSD1306_LCDHEIGHT), spi(NULL), wire(NULL), + xbuffer(NULL), mosiPin(mosi_pin), clkPin(sclk_pin), dcPin(dc_pin), + csPin(cs_pin), rstPin(rst_pin) { +} + +/*! + @brief DEPRECATED constructor for SPI SSD1306 displays, using native + hardware SPI. Provided for older code to maintain compatibility + with the current library. Screen size is determined by enabling + one of the SSD1306_* size defines in Adafruit_SSD1306.h. New + code should NOT use this. Only the primary SPI bus is supported, + and bitrate is fixed at 8 MHz. + @param dc_pin + Data/command pin (using Arduino pin numbering), selects whether + display is receiving commands (low) or data (high). + @param rst_pin + Reset pin (using Arduino pin numbering), or -1 if not used + (some displays might be wired to share the microcontroller's + reset pin). + @param cs_pin + Chip-select pin (using Arduino pin numbering) for sharing the + bus with other devices. Active low. + @return Adafruit_SSD1306 object. + @note Call the object's begin() function before use -- buffer + allocation is performed there! +*/ +Adafruit_SSD1306::Adafruit_SSD1306(int8_t dc_pin, int8_t rst_pin, + int8_t cs_pin) : Renderer(SSD1306_LCDWIDTH, SSD1306_LCDHEIGHT), + spi(&SPI), wire(NULL), xbuffer(NULL), mosiPin(-1), clkPin(-1), + dcPin(dc_pin), csPin(cs_pin), rstPin(rst_pin) { +#ifdef SPI_HAS_TRANSACTION + spiSettings = SPISettings(8000000, MSBFIRST, SPI_MODE0); +#endif +} + +/*! + @brief DEPRECATED constructor for I2C SSD1306 displays. Provided for + older code to maintain compatibility with the current library. + Screen size is determined by enabling one of the SSD1306_* size + defines in Adafruit_SSD1306.h. New code should NOT use this. + Only the primary I2C bus is supported. + @param rst_pin + Reset pin (using Arduino pin numbering), or -1 if not used + (some displays might be wired to share the microcontroller's + reset pin). + @return Adafruit_SSD1306 object. + @note Call the object's begin() function before use -- buffer + allocation is performed there! +*/ +Adafruit_SSD1306::Adafruit_SSD1306(int8_t rst_pin) : + Renderer(SSD1306_LCDWIDTH, SSD1306_LCDHEIGHT), spi(NULL), wire(&Wire), + xbuffer(NULL), mosiPin(-1), clkPin(-1), dcPin(-1), csPin(-1), + rstPin(rst_pin) { +} + +/*! + @brief Destructor for Adafruit_SSD1306 object. +*/ +Adafruit_SSD1306::~Adafruit_SSD1306(void) { + if(buffer) { + free(buffer); + buffer = NULL; + } +} + +// LOW-LEVEL UTILS --------------------------------------------------------- + +// Issue single byte out SPI, either soft or hardware as appropriate. +// SPI transaction/selection must be performed in calling function. +inline void Adafruit_SSD1306::SPIwrite(uint8_t d) { + if(spi) { + (void)spi->transfer(d); + } else { + for(uint8_t bit = 0x80; bit; bit >>= 1) { +#ifdef HAVE_PORTREG + if(d & bit) *mosiPort |= mosiPinMask; + else *mosiPort &= ~mosiPinMask; + *clkPort |= clkPinMask; // Clock high + *clkPort &= ~clkPinMask; // Clock low +#else + digitalWrite(mosiPin, d & bit); + digitalWrite(clkPin , HIGH); + digitalWrite(clkPin , LOW); +#endif + } + } +} + +// Issue single command to SSD1306, using I2C or hard/soft SPI as needed. +// Because command calls are often grouped, SPI transaction and selection +// must be started/ended in calling function for efficiency. +// This is a private function, not exposed (see ssd1306_command() instead). +void Adafruit_SSD1306::ssd1306_command1(uint8_t c) { + if(wire) { // I2C + wire->beginTransmission(i2caddr); + WIRE_WRITE((uint8_t)0x00); // Co = 0, D/C = 0 + WIRE_WRITE(c); + wire->endTransmission(); + } else { // SPI (hw or soft) -- transaction started in calling function + SSD1306_MODE_COMMAND + SPIwrite(c); + } +} + +// Issue list of commands to SSD1306, same rules as above re: transactions. +// This is a private function, not exposed. +void Adafruit_SSD1306::ssd1306_commandList(const uint8_t *c, uint8_t n) { + if(wire) { // I2C + wire->beginTransmission(i2caddr); + WIRE_WRITE((uint8_t)0x00); // Co = 0, D/C = 0 + uint8_t bytesOut = 1; + while(n--) { + if(bytesOut >= WIRE_MAX) { + wire->endTransmission(); + wire->beginTransmission(i2caddr); + WIRE_WRITE((uint8_t)0x00); // Co = 0, D/C = 0 + bytesOut = 1; + } + WIRE_WRITE(pgm_read_byte(c++)); + bytesOut++; + } + wire->endTransmission(); + } else { // SPI -- transaction started in calling function + SSD1306_MODE_COMMAND + while(n--) SPIwrite(pgm_read_byte(c++)); + } +} + +// A public version of ssd1306_command1(), for existing user code that +// might rely on that function. This encapsulates the command transfer +// in a transaction start/end, similar to old library's handling of it. +/*! + @brief Issue a single low-level command directly to the SSD1306 + display, bypassing the library. + @param c + Command to issue (0x00 to 0xFF, see datasheet). + @return None (void). +*/ +void Adafruit_SSD1306::ssd1306_command(uint8_t c) { + TRANSACTION_START + ssd1306_command1(c); + TRANSACTION_END +} + +// ALLOCATE & INIT DISPLAY ------------------------------------------------- + +/*! + @brief Allocate RAM for image buffer, initialize peripherals and pins. + @param vcs + VCC selection. Pass SSD1306_SWITCHCAPVCC to generate the display + voltage (step up) from the 3.3V source, or SSD1306_EXTERNALVCC + otherwise. Most situations with Adafruit SSD1306 breakouts will + want SSD1306_SWITCHCAPVCC. + @param addr + I2C address of corresponding SSD1306 display (or pass 0 to use + default of 0x3C for 128x32 display, 0x3D for all others). + SPI displays (hardware or software) do not use addresses, but + this argument is still required (pass 0 or any value really, + it will simply be ignored). Default if unspecified is 0. + @param reset + If true, and if the reset pin passed to the constructor is + valid, a hard reset will be performed before initializing the + display. If using multiple SSD1306 displays on the same bus, and + if they all share the same reset pin, you should only pass true + on the first display being initialized, false on all others, + else the already-initialized displays would be reset. Default if + unspecified is true. + @param periphBegin + If true, and if a hardware peripheral is being used (I2C or SPI, + but not software SPI), call that peripheral's begin() function, + else (false) it has already been done in one's sketch code. + Cases where false might be used include multiple displays or + other devices sharing a common bus, or situations on some + platforms where a nonstandard begin() function is available + (e.g. a TwoWire interface on non-default pins, as can be done + on the ESP8266 and perhaps others). + @return true on successful allocation/init, false otherwise. + Well-behaved code should check the return value before + proceeding. + @note MUST call this function before any drawing or updates! +*/ +boolean Adafruit_SSD1306::begin(uint8_t vcs, uint8_t addr, boolean reset, + boolean periphBegin) { + +// if((!buffer) && !(buffer = (uint8_t *)malloc(WIDTH * ((HEIGHT + 7) / 8)))) +// return false; + + clearDisplay(); + + /* + if(HEIGHT > 32) { + drawBitmap((WIDTH - splash1_width) / 2, (HEIGHT - splash1_height) / 2, + splash1_data, splash1_width, splash1_height, 1); + } else { + drawBitmap((WIDTH - splash2_width) / 2, (HEIGHT - splash2_height) / 2, + splash2_data, splash2_width, splash2_height, 1); + } +*/ + vccstate = vcs; + + // Setup pin directions + if(wire) { // Using I2C + // If I2C address is unspecified, use default + // (0x3C for 32-pixel-tall displays, 0x3D for all others). + i2caddr = addr ? addr : ((HEIGHT == 32) ? 0x3C : 0x3D); + // TwoWire begin() function might be already performed by the calling + // function if it has unusual circumstances (e.g. TWI variants that + // can accept different SDA/SCL pins, or if two SSD1306 instances + // with different addresses -- only a single begin() is needed). + if(periphBegin) wire->begin(); + } else { // Using one of the SPI modes, either soft or hardware + pinMode(dcPin, OUTPUT); // Set data/command pin as output + pinMode(csPin, OUTPUT); // Same for chip select +#ifdef HAVE_PORTREG + dcPort = (PortReg *)portOutputRegister(digitalPinToPort(dcPin)); + dcPinMask = digitalPinToBitMask(dcPin); + csPort = (PortReg *)portOutputRegister(digitalPinToPort(csPin)); + csPinMask = digitalPinToBitMask(csPin); +#endif + SSD1306_DESELECT + if(spi) { // Hardware SPI + // SPI peripheral begin same as wire check above. + if(periphBegin) spi->begin(); + } else { // Soft SPI + pinMode(mosiPin, OUTPUT); // MOSI and SCLK outputs + pinMode(clkPin , OUTPUT); +#ifdef HAVE_PORTREG + mosiPort = (PortReg *)portOutputRegister(digitalPinToPort(mosiPin)); + mosiPinMask = digitalPinToBitMask(mosiPin); + clkPort = (PortReg *)portOutputRegister(digitalPinToPort(clkPin)); + clkPinMask = digitalPinToBitMask(clkPin); + *clkPort &= ~clkPinMask; // Clock low +#else + digitalWrite(clkPin, LOW); // Clock low +#endif + } + } + + // Reset SSD1306 if requested and reset pin specified in constructor + if(reset && (rstPin >= 0)) { + pinMode( rstPin, OUTPUT); + digitalWrite(rstPin, HIGH); + delay(1); // VDD goes high at start, pause for 1 ms + digitalWrite(rstPin, LOW); // Bring reset low + delay(10); // Wait 10 ms + digitalWrite(rstPin, HIGH); // Bring out of reset + } + + TRANSACTION_START + + // Init sequence + static const uint8_t PROGMEM init1[] = { + SSD1306_DISPLAYOFF, // 0xAE + SSD1306_SETDISPLAYCLOCKDIV, // 0xD5 + 0x80, // the suggested ratio 0x80 + SSD1306_SETMULTIPLEX }; // 0xA8 + ssd1306_commandList(init1, sizeof(init1)); + ssd1306_command1(HEIGHT - 1); + + static const uint8_t PROGMEM init2[] = { + SSD1306_SETDISPLAYOFFSET, // 0xD3 + 0x0, // no offset + SSD1306_SETSTARTLINE | 0x0, // line #0 + SSD1306_CHARGEPUMP }; // 0x8D + ssd1306_commandList(init2, sizeof(init2)); + + ssd1306_command1((vccstate == SSD1306_EXTERNALVCC) ? 0x10 : 0x14); + + static const uint8_t PROGMEM init3[] = { + SSD1306_MEMORYMODE, // 0x20 + 0x00, // 0x0 act like ks0108 + SSD1306_SEGREMAP | 0x1, + SSD1306_COMSCANDEC }; + ssd1306_commandList(init3, sizeof(init3)); + + if((WIDTH == 128) && (HEIGHT == 32)) { + static const uint8_t PROGMEM init4a[] = { + SSD1306_SETCOMPINS, // 0xDA + 0x02, + SSD1306_SETCONTRAST, // 0x81 + 0x8F }; + ssd1306_commandList(init4a, sizeof(init4a)); + } else if((WIDTH == 128) && (HEIGHT == 64)) { + static const uint8_t PROGMEM init4b[] = { + SSD1306_SETCOMPINS, // 0xDA + 0x12, + SSD1306_SETCONTRAST }; // 0x81 + ssd1306_commandList(init4b, sizeof(init4b)); + ssd1306_command1((vccstate == SSD1306_EXTERNALVCC) ? 0x9F : 0xCF); + } else if((WIDTH == 96) && (HEIGHT == 16)) { + static const uint8_t PROGMEM init4c[] = { + SSD1306_SETCOMPINS, // 0xDA + 0x2, // ada x12 + SSD1306_SETCONTRAST }; // 0x81 + ssd1306_commandList(init4c, sizeof(init4c)); + ssd1306_command1((vccstate == SSD1306_EXTERNALVCC) ? 0x10 : 0xAF); + } else { + // Other screen varieties -- TBD + } + + ssd1306_command1(SSD1306_SETPRECHARGE); // 0xd9 + ssd1306_command1((vccstate == SSD1306_EXTERNALVCC) ? 0x22 : 0xF1); + static const uint8_t PROGMEM init5[] = { + SSD1306_SETVCOMDETECT, // 0xDB + 0x40, + SSD1306_DISPLAYALLON_RESUME, // 0xA4 + SSD1306_NORMALDISPLAY, // 0xA6 + SSD1306_DEACTIVATE_SCROLL, + SSD1306_DISPLAYON }; // Main screen turn on + ssd1306_commandList(init5, sizeof(init5)); + + TRANSACTION_END + + return true; // Success +} + + +void Adafruit_SSD1306::DisplayInit(int8_t p,int8_t size,int8_t rot,int8_t font) { +// ignore update mode + //if (p==DISPLAY_INIT_MODE) { + setRotation(rot); + invertDisplay(false); + setTextWrap(false); // Allow text to run off edges + cp437(true); + setTextFont(font); + setTextSize(size); + setTextColor(WHITE,BLACK); + setCursor(0,0); + fillScreen(BLACK); + Updateframe(); + //} +} + +#if 0 + +// DRAWING FUNCTIONS ------------------------------------------------------- + +/*! + @brief Set/clear/invert a single pixel. This is also invoked by the + Adafruit_GFX library in generating many higher-level graphics + primitives. + @param x + Column of display -- 0 at left to (screen width - 1) at right. + @param y + Row of display -- 0 at top to (screen height -1) at bottom. + @param color + Pixel color, one of: BLACK, WHITE or INVERT. + @return None (void). + @note Changes buffer contents only, no immediate effect on display. + Follow up with a call to display(), or with other graphics + commands as needed by one's own application. +*/ +void Adafruit_SSD1306::drawPixel(int16_t x, int16_t y, uint16_t color) { + if((x >= 0) && (x < width()) && (y >= 0) && (y < height())) { + // Pixel is in-bounds. Rotate coordinates if needed. + switch(getRotation()) { + case 1: + ssd1306_swap(x, y); + x = WIDTH - x - 1; + break; + case 2: + x = WIDTH - x - 1; + y = HEIGHT - y - 1; + break; + case 3: + ssd1306_swap(x, y); + y = HEIGHT - y - 1; + break; + } + switch(color) { + case WHITE: buffer[x + (y/8)*WIDTH] |= (1 << (y&7)); break; + case BLACK: buffer[x + (y/8)*WIDTH] &= ~(1 << (y&7)); break; + case INVERSE: buffer[x + (y/8)*WIDTH] ^= (1 << (y&7)); break; + } + } +} + +/*! + @brief Clear contents of display buffer (set all pixels to off). + @return None (void). + @note Changes buffer contents only, no immediate effect on display. + Follow up with a call to display(), or with other graphics + commands as needed by one's own application. +*/ +void Adafruit_SSD1306::clearDisplay(void) { + memset(buffer, 0, WIDTH * ((HEIGHT + 7) / 8)); +} + +/*! + @brief Draw a horizontal line. This is also invoked by the Adafruit_GFX + library in generating many higher-level graphics primitives. + @param x + Leftmost column -- 0 at left to (screen width - 1) at right. + @param y + Row of display -- 0 at top to (screen height -1) at bottom. + @param w + Width of line, in pixels. + @param color + Line color, one of: BLACK, WHITE or INVERT. + @return None (void). + @note Changes buffer contents only, no immediate effect on display. + Follow up with a call to display(), or with other graphics + commands as needed by one's own application. +*/ +void Adafruit_SSD1306::drawFastHLine( + int16_t x, int16_t y, int16_t w, uint16_t color) { + boolean bSwap = false; + switch(rotation) { + case 1: + // 90 degree rotation, swap x & y for rotation, then invert x + bSwap = true; + ssd1306_swap(x, y); + x = WIDTH - x - 1; + break; + case 2: + // 180 degree rotation, invert x and y, then shift y around for height. + x = WIDTH - x - 1; + y = HEIGHT - y - 1; + x -= (w-1); + break; + case 3: + // 270 degree rotation, swap x & y for rotation, + // then invert y and adjust y for w (not to become h) + bSwap = true; + ssd1306_swap(x, y); + y = HEIGHT - y - 1; + y -= (w-1); + break; + } + + if(bSwap) drawFastVLineInternal(x, y, w, color); + else drawFastHLineInternal(x, y, w, color); +} + + + +/*! + @brief Draw a vertical line. This is also invoked by the Adafruit_GFX + library in generating many higher-level graphics primitives. + @param x + Column of display -- 0 at left to (screen width -1) at right. + @param y + Topmost row -- 0 at top to (screen height - 1) at bottom. + @param h + Height of line, in pixels. + @param color + Line color, one of: BLACK, WHITE or INVERT. + @return None (void). + @note Changes buffer contents only, no immediate effect on display. + Follow up with a call to display(), or with other graphics + commands as needed by one's own application. +*/ +void Adafruit_SSD1306::drawFastVLine( + int16_t x, int16_t y, int16_t h, uint16_t color) { + boolean bSwap = false; + switch(rotation) { + case 1: + // 90 degree rotation, swap x & y for rotation, + // then invert x and adjust x for h (now to become w) + bSwap = true; + ssd1306_swap(x, y); + x = WIDTH - x - 1; + x -= (h-1); + break; + case 2: + // 180 degree rotation, invert x and y, then shift y around for height. + x = WIDTH - x - 1; + y = HEIGHT - y - 1; + y -= (h-1); + break; + case 3: + // 270 degree rotation, swap x & y for rotation, then invert y + bSwap = true; + ssd1306_swap(x, y); + y = HEIGHT - y - 1; + break; + } + + if(bSwap) drawFastHLineInternal(x, y, h, color); + else drawFastVLineInternal(x, y, h, color); +} +#endif + +void Adafruit_SSD1306::drawFastHLineInternal( + int16_t x, int16_t y, int16_t w, uint16_t color) { + + if((y >= 0) && (y < HEIGHT)) { // Y coord in bounds? + if(x < 0) { // Clip left + w += x; + x = 0; + } + if((x + w) > WIDTH) { // Clip right + w = (WIDTH - x); + } + if(w > 0) { // Proceed only if width is positive + uint8_t *pBuf = &buffer[(y / 8) * WIDTH + x], + mask = 1 << (y & 7); + switch(color) { + case WHITE: while(w--) { *pBuf++ |= mask; }; break; + case BLACK: mask = ~mask; while(w--) { *pBuf++ &= mask; }; break; + case INVERSE: while(w--) { *pBuf++ ^= mask; }; break; + } + } + } +} + +void Adafruit_SSD1306::drawFastVLineInternal( + int16_t x, int16_t __y, int16_t __h, uint16_t color) { + + if((x >= 0) && (x < WIDTH)) { // X coord in bounds? + if(__y < 0) { // Clip top + __h += __y; + __y = 0; + } + if((__y + __h) > HEIGHT) { // Clip bottom + __h = (HEIGHT - __y); + } + if(__h > 0) { // Proceed only if height is now positive + // this display doesn't need ints for coordinates, + // use local byte registers for faster juggling + uint8_t y = __y, h = __h; + uint8_t *pBuf = &buffer[(y / 8) * WIDTH + x]; + + // do the first partial byte, if necessary - this requires some masking + uint8_t mod = (y & 7); + if(mod) { + // mask off the high n bits we want to set + mod = 8 - mod; + // note - lookup table results in a nearly 10% performance + // improvement in fill* functions + // uint8_t mask = ~(0xFF >> mod); + static const uint8_t PROGMEM premask[8] = + { 0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE }; + uint8_t mask = pgm_read_byte(&premask[mod]); + // adjust the mask if we're not going to reach the end of this byte + if(h < mod) mask &= (0XFF >> (mod - h)); + + switch(color) { + case WHITE: *pBuf |= mask; break; + case BLACK: *pBuf &= ~mask; break; + case INVERSE: *pBuf ^= mask; break; + } + pBuf += WIDTH; + } + + if(h >= mod) { // More to go? + h -= mod; + // Write solid bytes while we can - effectively 8 rows at a time + if(h >= 8) { + if(color == INVERSE) { + // separate copy of the code so we don't impact performance of + // black/white write version with an extra comparison per loop + do { + *pBuf ^= 0xFF; // Invert byte + pBuf += WIDTH; // Advance pointer 8 rows + h -= 8; // Subtract 8 rows from height + } while(h >= 8); + } else { + // store a local value to work with + uint8_t val = (color != BLACK) ? 255 : 0; + do { + *pBuf = val; // Set byte + pBuf += WIDTH; // Advance pointer 8 rows + h -= 8; // Subtract 8 rows from height + } while(h >= 8); + } + } + + if(h) { // Do the final partial byte, if necessary + mod = h & 7; + // this time we want to mask the low bits of the byte, + // vs the high bits we did above + // uint8_t mask = (1 << mod) - 1; + // note - lookup table results in a nearly 10% performance + // improvement in fill* functions + static const uint8_t PROGMEM postmask[8] = + { 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F }; + uint8_t mask = pgm_read_byte(&postmask[mod]); + switch(color) { + case WHITE: *pBuf |= mask; break; + case BLACK: *pBuf &= ~mask; break; + case INVERSE: *pBuf ^= mask; break; + } + } + } + } // endif positive height + } // endif x in bounds +} + +/*! + @brief Return color of a single pixel in display buffer. + @param x + Column of display -- 0 at left to (screen width - 1) at right. + @param y + Row of display -- 0 at top to (screen height -1) at bottom. + @return true if pixel is set (usually WHITE, unless display invert mode + is enabled), false if clear (BLACK). + @note Reads from buffer contents; may not reflect current contents of + screen if display() has not been called. +*/ +boolean Adafruit_SSD1306::getPixel(int16_t x, int16_t y) { + if((x >= 0) && (x < width()) && (y >= 0) && (y < height())) { + // Pixel is in-bounds. Rotate coordinates if needed. + switch(getRotation()) { + case 1: + ssd1306_swap(x, y); + x = WIDTH - x - 1; + break; + case 2: + x = WIDTH - x - 1; + y = HEIGHT - y - 1; + break; + case 3: + ssd1306_swap(x, y); + y = HEIGHT - y - 1; + break; + } + return (buffer[x + (y / 8) * WIDTH] & (1 << (y & 7))); + } + return false; // Pixel out of bounds +} + +/*! + @brief Get base address of display buffer for direct reading or writing. + @return Pointer to an unsigned 8-bit array, column-major, columns padded + to full byte boundary if needed. +*/ +uint8_t *Adafruit_SSD1306::getBuffer(void) { + return xbuffer; +} + + +// REFRESH DISPLAY --------------------------------------------------------- + +/*! + @brief Push data currently in RAM to SSD1306 display. + @return None (void). + @note Drawing operations are not visible until this function is + called. Call after each graphics command, or after a whole set + of graphics commands, as best needed by one's own application. +*/ +void Adafruit_SSD1306::display(void) { + TRANSACTION_START + static const uint8_t PROGMEM dlist1[] = { + SSD1306_PAGEADDR, + 0, // Page start address + 0xFF, // Page end (not really, but works here) + SSD1306_COLUMNADDR, + 0 }; // Column start address + ssd1306_commandList(dlist1, sizeof(dlist1)); + ssd1306_command1(WIDTH - 1); // Column end address + +#if defined(ESP8266) + // ESP8266 needs a periodic yield() call to avoid watchdog reset. + // With the limited size of SSD1306 displays, and the fast bitrate + // being used (1 MHz or more), I think one yield() immediately before + // a screen write and one immediately after should cover it. But if + // not, if this becomes a problem, yields() might be added in the + // 32-byte transfer condition below. + yield(); +#endif + uint16_t count = WIDTH * ((HEIGHT + 7) / 8); + uint8_t *ptr = buffer; + if(wire) { // I2C + wire->beginTransmission(i2caddr); + WIRE_WRITE((uint8_t)0x40); + uint8_t bytesOut = 1; + while(count--) { + if(bytesOut >= WIRE_MAX) { + wire->endTransmission(); + wire->beginTransmission(i2caddr); + WIRE_WRITE((uint8_t)0x40); + bytesOut = 1; + } + WIRE_WRITE(*ptr++); + bytesOut++; + } + wire->endTransmission(); + } else { // SPI + SSD1306_MODE_DATA + while(count--) SPIwrite(*ptr++); + } + TRANSACTION_END +#if defined(ESP8266) + yield(); +#endif +} + +// SCROLLING FUNCTIONS ----------------------------------------------------- + +/*! + @brief Activate a right-handed scroll for all or part of the display. + @param start + First row. + @param stop + Last row. + @return None (void). +*/ +// To scroll the whole display, run: display.startscrollright(0x00, 0x0F) +void Adafruit_SSD1306::startscrollright(uint8_t start, uint8_t stop) { + TRANSACTION_START + static const uint8_t PROGMEM scrollList1a[] = { + SSD1306_RIGHT_HORIZONTAL_SCROLL, + 0X00 }; + ssd1306_commandList(scrollList1a, sizeof(scrollList1a)); + ssd1306_command1(start); + ssd1306_command1(0X00); + ssd1306_command1(stop); + static const uint8_t PROGMEM scrollList1b[] = { + 0X00, + 0XFF, + SSD1306_ACTIVATE_SCROLL }; + ssd1306_commandList(scrollList1b, sizeof(scrollList1b)); + TRANSACTION_END +} + +/*! + @brief Activate a left-handed scroll for all or part of the display. + @param start + First row. + @param stop + Last row. + @return None (void). +*/ +// To scroll the whole display, run: display.startscrollleft(0x00, 0x0F) +void Adafruit_SSD1306::startscrollleft(uint8_t start, uint8_t stop) { + TRANSACTION_START + static const uint8_t PROGMEM scrollList2a[] = { + SSD1306_LEFT_HORIZONTAL_SCROLL, + 0X00 }; + ssd1306_commandList(scrollList2a, sizeof(scrollList2a)); + ssd1306_command1(start); + ssd1306_command1(0X00); + ssd1306_command1(stop); + static const uint8_t PROGMEM scrollList2b[] = { + 0X00, + 0XFF, + SSD1306_ACTIVATE_SCROLL }; + ssd1306_commandList(scrollList2b, sizeof(scrollList2b)); + TRANSACTION_END +} + +/*! + @brief Activate a diagonal scroll for all or part of the display. + @param start + First row. + @param stop + Last row. + @return None (void). +*/ +// display.startscrolldiagright(0x00, 0x0F) +void Adafruit_SSD1306::startscrolldiagright(uint8_t start, uint8_t stop) { + TRANSACTION_START + static const uint8_t PROGMEM scrollList3a[] = { + SSD1306_SET_VERTICAL_SCROLL_AREA, + 0X00 }; + ssd1306_commandList(scrollList3a, sizeof(scrollList3a)); + ssd1306_command1(HEIGHT); + static const uint8_t PROGMEM scrollList3b[] = { + SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL, + 0X00 }; + ssd1306_commandList(scrollList3b, sizeof(scrollList3b)); + ssd1306_command1(start); + ssd1306_command1(0X00); + ssd1306_command1(stop); + static const uint8_t PROGMEM scrollList3c[] = { + 0X01, + SSD1306_ACTIVATE_SCROLL }; + ssd1306_commandList(scrollList3c, sizeof(scrollList3c)); + TRANSACTION_END +} + +/*! + @brief Activate alternate diagonal scroll for all or part of the display. + @param start + First row. + @param stop + Last row. + @return None (void). +*/ +// To scroll the whole display, run: display.startscrolldiagleft(0x00, 0x0F) +void Adafruit_SSD1306::startscrolldiagleft(uint8_t start, uint8_t stop) { + TRANSACTION_START + static const uint8_t PROGMEM scrollList4a[] = { + SSD1306_SET_VERTICAL_SCROLL_AREA, + 0X00 }; + ssd1306_commandList(scrollList4a, sizeof(scrollList4a)); + ssd1306_command1(HEIGHT); + static const uint8_t PROGMEM scrollList4b[] = { + SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL, + 0X00 }; + ssd1306_commandList(scrollList4b, sizeof(scrollList4b)); + ssd1306_command1(start); + ssd1306_command1(0X00); + ssd1306_command1(stop); + static const uint8_t PROGMEM scrollList4c[] = { + 0X01, + SSD1306_ACTIVATE_SCROLL }; + ssd1306_commandList(scrollList4c, sizeof(scrollList4c)); + TRANSACTION_END +} + +/*! + @brief Cease a previously-begun scrolling action. + @return None (void). +*/ +void Adafruit_SSD1306::stopscroll(void) { + TRANSACTION_START + ssd1306_command1(SSD1306_DEACTIVATE_SCROLL); + TRANSACTION_END +} + +// OTHER HARDWARE SETTINGS ------------------------------------------------- + +/*! + @brief Enable or disable display invert mode (white-on-black vs + black-on-white). + @param i + If true, switch to invert mode (black-on-white), else normal + mode (white-on-black). + @return None (void). + @note This has an immediate effect on the display, no need to call the + display() function -- buffer contents are not changed, rather a + different pixel mode of the display hardware is used. When + enabled, drawing BLACK (value 0) pixels will actually draw white, + WHITE (value 1) will draw black. +*/ +void Adafruit_SSD1306::invertDisplay(boolean i) { + TRANSACTION_START + ssd1306_command1(i ? SSD1306_INVERTDISPLAY : SSD1306_NORMALDISPLAY); + TRANSACTION_END +} + +/*! + @brief Dim the display. + @param dim + true to enable lower brightness mode, false for full brightness. + @return None (void). + @note This has an immediate effect on the display, no need to call the + display() function -- buffer contents are not changed. +*/ +void Adafruit_SSD1306::dim(boolean dim) { + uint8_t contrast; + + if(dim) { + contrast = 0; // Dimmed display + } else { + contrast = (vccstate == SSD1306_EXTERNALVCC) ? 0x9F : 0xCF; + } + // the range of contrast to too small to be really useful + // it is useful to dim the display + TRANSACTION_START + ssd1306_command1(SSD1306_SETCONTRAST); + ssd1306_command1(contrast); + TRANSACTION_END +} + +void Adafruit_SSD1306::DisplayOnff(int8_t on) { + TRANSACTION_START + if(on) { + ssd1306_command1(SSD1306_DISPLAYON); + } else { + ssd1306_command1(SSD1306_DISPLAYOFF); + } + TRANSACTION_END +} + +void Adafruit_SSD1306::Updateframe(void) { + display(); +} diff --git a/lib/Adafruit_SSD1306-1.3.0-gemu-1.1/Adafruit_SSD1306.h b/lib/Adafruit_SSD1306-1.3.0-gemu-1.1/Adafruit_SSD1306.h new file mode 100644 index 000000000..5be544199 --- /dev/null +++ b/lib/Adafruit_SSD1306-1.3.0-gemu-1.1/Adafruit_SSD1306.h @@ -0,0 +1,189 @@ +/*! + * @file Adafruit_SSD1306.h + * + * This is part of for Adafruit's SSD1306 library for monochrome + * OLED displays: http://www.adafruit.com/category/63_98 + * + * These displays use I2C or SPI to communicate. I2C requires 2 pins + * (SCL+SDA) and optionally a RESET pin. SPI requires 4 pins (MOSI, SCK, + * select, data/command) and optionally a reset pin. Hardware SPI or + * 'bitbang' software SPI are both supported. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Written by Limor Fried/Ladyada for Adafruit Industries, with + * contributions from the open source community. + * + * BSD license, all text above, and the splash screen header file, + * must be included in any redistribution. + * + */ + +#ifndef _Adafruit_SSD1306_H_ +#define _Adafruit_SSD1306_H_ + +#include + +extern uint8_t *buffer; + +// ONE of the following three lines must be #defined: +//#define SSD1306_128_64 ///< DEPRECTAED: old way to specify 128x64 screen +#define SSD1306_128_32 ///< DEPRECATED: old way to specify 128x32 screen +//#define SSD1306_96_16 ///< DEPRECATED: old way to specify 96x16 screen +// This establishes the screen dimensions in old Adafruit_SSD1306 sketches +// (NEW CODE SHOULD IGNORE THIS, USE THE CONSTRUCTORS THAT ACCEPT WIDTH +// AND HEIGHT ARGUMENTS). + +#if defined(ARDUINO_STM32_FEATHER) + typedef class HardwareSPI SPIClass; +#endif + +#include +#include +#include + +#if defined(__AVR__) + typedef volatile uint8_t PortReg; + typedef uint8_t PortMask; + #define HAVE_PORTREG +#elif defined(__SAM3X8E__) + typedef volatile RwReg PortReg; + typedef uint32_t PortMask; + #define HAVE_PORTREG +#elif defined(__arm__) || defined(ARDUINO_FEATHER52) + typedef volatile uint32_t PortReg; + typedef uint32_t PortMask; + #define HAVE_PORTREG +#endif + +#define BLACK 0 ///< Draw 'off' pixels +#define WHITE 1 ///< Draw 'on' pixels +#define INVERSE 2 ///< Invert pixels + +#define SSD1306_MEMORYMODE 0x20 ///< See datasheet +#define SSD1306_COLUMNADDR 0x21 ///< See datasheet +#define SSD1306_PAGEADDR 0x22 ///< See datasheet +#define SSD1306_SETCONTRAST 0x81 ///< See datasheet +#define SSD1306_CHARGEPUMP 0x8D ///< See datasheet +#define SSD1306_SEGREMAP 0xA0 ///< See datasheet +#define SSD1306_DISPLAYALLON_RESUME 0xA4 ///< See datasheet +#define SSD1306_DISPLAYALLON 0xA5 ///< Not currently used +#define SSD1306_NORMALDISPLAY 0xA6 ///< See datasheet +#define SSD1306_INVERTDISPLAY 0xA7 ///< See datasheet +#define SSD1306_SETMULTIPLEX 0xA8 ///< See datasheet +#define SSD1306_DISPLAYOFF 0xAE ///< See datasheet +#define SSD1306_DISPLAYON 0xAF ///< See datasheet +#define SSD1306_COMSCANINC 0xC0 ///< Not currently used +#define SSD1306_COMSCANDEC 0xC8 ///< See datasheet +#define SSD1306_SETDISPLAYOFFSET 0xD3 ///< See datasheet +#define SSD1306_SETDISPLAYCLOCKDIV 0xD5 ///< See datasheet +#define SSD1306_SETPRECHARGE 0xD9 ///< See datasheet +#define SSD1306_SETCOMPINS 0xDA ///< See datasheet +#define SSD1306_SETVCOMDETECT 0xDB ///< See datasheet + +#define SSD1306_SETLOWCOLUMN 0x00 ///< Not currently used +#define SSD1306_SETHIGHCOLUMN 0x10 ///< Not currently used +#define SSD1306_SETSTARTLINE 0x40 ///< See datasheet + +#define SSD1306_EXTERNALVCC 0x01 ///< External display voltage source +#define SSD1306_SWITCHCAPVCC 0x02 ///< Gen. display voltage from 3.3V + +#define SSD1306_RIGHT_HORIZONTAL_SCROLL 0x26 ///< Init rt scroll +#define SSD1306_LEFT_HORIZONTAL_SCROLL 0x27 ///< Init left scroll +#define SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL 0x29 ///< Init diag scroll +#define SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL 0x2A ///< Init diag scroll +#define SSD1306_DEACTIVATE_SCROLL 0x2E ///< Stop scroll +#define SSD1306_ACTIVATE_SCROLL 0x2F ///< Start scroll +#define SSD1306_SET_VERTICAL_SCROLL_AREA 0xA3 ///< Set scroll range + +// Deprecated size stuff for backwards compatibility with old sketches +#if defined SSD1306_128_64 + #define SSD1306_LCDWIDTH 128 ///< DEPRECATED: width w/SSD1306_128_64 defined + #define SSD1306_LCDHEIGHT 64 ///< DEPRECATED: height w/SSD1306_128_64 defined +#endif +#if defined SSD1306_128_32 + #define SSD1306_LCDWIDTH 128 ///< DEPRECATED: width w/SSD1306_128_32 defined + #define SSD1306_LCDHEIGHT 32 ///< DEPRECATED: height w/SSD1306_128_32 defined +#endif +#if defined SSD1306_96_16 + #define SSD1306_LCDWIDTH 96 ///< DEPRECATED: width w/SSD1306_96_16 defined + #define SSD1306_LCDHEIGHT 16 ///< DEPRECATED: height w/SSD1306_96_16 defined +#endif + +/*! + @brief Class that stores state and functions for interacting with + SSD1306 OLED displays. +*/ +class Adafruit_SSD1306 : public Renderer { +public: + // NEW CONSTRUCTORS -- recommended for new projects + Adafruit_SSD1306(uint8_t w, uint8_t h, TwoWire *twi=&Wire, int8_t rst_pin=-1, + uint32_t clkDuring=400000UL, uint32_t clkAfter=100000UL); + Adafruit_SSD1306(uint8_t w, uint8_t h, int8_t mosi_pin, int8_t sclk_pin, + int8_t dc_pin, int8_t rst_pin, int8_t cs_pin); + Adafruit_SSD1306(uint8_t w, uint8_t h, SPIClass *spi, + int8_t dc_pin, int8_t rst_pin, int8_t cs_pin, uint32_t bitrate=8000000UL); + + // DEPRECATED CONSTRUCTORS - for back compatibility, avoid in new projects + Adafruit_SSD1306(int8_t mosi_pin, int8_t sclk_pin, int8_t dc_pin, + int8_t rst_pin, int8_t cs_pin); + Adafruit_SSD1306(int8_t dc_pin, int8_t rst_pin, int8_t cs_pin); + Adafruit_SSD1306(int8_t rst_pin = -1); + + ~Adafruit_SSD1306(void); + + boolean begin(uint8_t switchvcc=SSD1306_SWITCHCAPVCC, + uint8_t i2caddr=0, boolean reset=true, + boolean periphBegin=true); + void display(void); + void invertDisplay(boolean i); + void dim(boolean dim); + void DisplayOnff(int8_t on); + void DisplayInit(int8_t p,int8_t size,int8_t rot,int8_t font); + + #if 0 + void clearDisplay(void); + void drawPixel(int16_t x, int16_t y, uint16_t color); + virtual void drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color); + virtual void drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color); + #endif + void startscrollright(uint8_t start, uint8_t stop); + void startscrollleft(uint8_t start, uint8_t stop); + void startscrolldiagright(uint8_t start, uint8_t stop); + void startscrolldiagleft(uint8_t start, uint8_t stop); + void stopscroll(void); + void ssd1306_command(uint8_t c); + boolean getPixel(int16_t x, int16_t y); + uint8_t *getBuffer(void); + void Updateframe(void); + + private: + inline void SPIwrite(uint8_t d) __attribute__((always_inline)); + void drawFastHLineInternal(int16_t x, int16_t y, int16_t w, + uint16_t color); + void drawFastVLineInternal(int16_t x, int16_t y, int16_t h, + uint16_t color); + void ssd1306_command1(uint8_t c); + void ssd1306_commandList(const uint8_t *c, uint8_t n); + + SPIClass *spi; + TwoWire *wire; + uint8_t *xbuffer; + int8_t i2caddr, vccstate, page_end; + int8_t mosiPin , clkPin , dcPin , csPin, rstPin; +#ifdef HAVE_PORTREG + PortReg *mosiPort , *clkPort , *dcPort , *csPort; + PortMask mosiPinMask, clkPinMask, dcPinMask, csPinMask; +#endif +#if defined(SPI_HAS_TRANSACTION) + SPISettings spiSettings; +#endif +#if ARDUINO >= 157 + uint32_t wireClk; // Wire speed for SSD1306 transfers + uint32_t restoreClk; // Wire speed following SSD1306 transfers +#endif +}; + +#endif // _Adafruit_SSD1306_H_ diff --git a/lib/Adafruit_SSD1306-1.3.0-gemu-1.1/README.md b/lib/Adafruit_SSD1306-1.3.0-gemu-1.1/README.md new file mode 100644 index 000000000..f2e01ad4b --- /dev/null +++ b/lib/Adafruit_SSD1306-1.3.0-gemu-1.1/README.md @@ -0,0 +1,54 @@ +# Adafruit_SSD1306 [![Build Status](https://travis-ci.org/adafruit/Adafruit_SSD1306.svg?branch=master)](https://travis-ci.org/adafruit/Adafruit_SSD1306) + +This is a library for our Monochrome OLEDs based on SSD1306 drivers + + Pick one up today in the adafruit shop! + ------> http://www.adafruit.com/category/63_98 + +These displays use I2C or SPI to communicate, 2 to 5 pins are required to interface. + +Adafruit invests time and resources providing this open source code, +please support Adafruit and open-source hardware by purchasing +products from Adafruit! + +Written by Limor Fried/Ladyada for Adafruit Industries, with contributions from the open source community. Scrolling code contributed by Michael Gregg. Dynamic buffer allocation based on work by Andrew Canaday. +BSD license, check license.txt for more information. All text above must be included in any redistribution + +Preferred installation method is to use the Arduino IDE Library Manager. To download the source from Github instead, click "Clone or download" above, then "Download ZIP." After uncompressing, rename the resulting folder Adafruit_SSD1306. Check that the Adafruit_SSD1306 folder contains Adafruit_SSD1306.cpp and Adafruit_SSD1306.h. + +You will also have to install the **Adafruit GFX library** which provides graphics primitves such as lines, circles, text, etc. This also can be found in the Arduino Library Manager, or you can get the source from https://github.com/adafruit/Adafruit-GFX-Library + +## Changes + +Version 1.2 (November 2018) introduces some significant changes: + + * Display dimensions are now specified in the constructor...you no longer need to edit the .h file for different screens (though old sketches can continue to work that way). + * SPI transactions are used and SPI bitrate can be specified (both require Arduino 1.6 or later). + * SPI and Wire (I2C) interfaces other than the defaults are supported. + + + +## Compatibility + +MCU |Tested Works|Doesn't Work|Not Tested|Notes +------------|:----------:|:----------:|:--------:|----- +Atmega328 | X | | | +Atmega32u4 | X | | | +Atmega2560 | X | | | +ESP8266 | X | | | Change OLED_RESET to different pin if using default I2C pins D4/D5. +ESP32 | X | | | +ATSAM3X8E | X | | | +ATSAM21D | X | | | +Intel Curie | X | | | +WICED | X | | | No hardware SPI - bitbang only +ATtiny85 | | X | | + + * ATmega328 : Arduino UNO, Adafruit Pro Trinket, Adafruit Metro 328, Adafruit Metro Mini + * ATmega32u4 : Arduino Leonardo, Arduino Micro, Arduino Yun, Teensy 2.0, Adafruit Flora, Bluefruit Micro + * ATmega2560 : Arduino Mega + * ESP8266 : Adafruit Huzzah + * ATSAM3X8E : Arduino Due + * ATSAM21D : Arduino Zero, M0 Pro, Adafruit Metro Express, Feather M0 + * ATtiny85 : Adafruit Gemma, Arduino Gemma, Adafruit Trinket + + diff --git a/lib/Adafruit_SSD1306-1.3.0-gemu-1.1/examples/OLED_featherwing/OLED_featherwing.ino b/lib/Adafruit_SSD1306-1.3.0-gemu-1.1/examples/OLED_featherwing/OLED_featherwing.ino new file mode 100644 index 000000000..2d0d24646 --- /dev/null +++ b/lib/Adafruit_SSD1306-1.3.0-gemu-1.1/examples/OLED_featherwing/OLED_featherwing.ino @@ -0,0 +1,79 @@ +#include +#include +#include +#include + +Adafruit_SSD1306 display = Adafruit_SSD1306(128, 32, &Wire); + +// OLED FeatherWing buttons map to different pins depending on board: +#if defined(ESP8266) + #define BUTTON_A 0 + #define BUTTON_B 16 + #define BUTTON_C 2 +#elif defined(ESP32) + #define BUTTON_A 15 + #define BUTTON_B 32 + #define BUTTON_C 14 +#elif defined(ARDUINO_STM32_FEATHER) + #define BUTTON_A PA15 + #define BUTTON_B PC7 + #define BUTTON_C PC5 +#elif defined(TEENSYDUINO) + #define BUTTON_A 4 + #define BUTTON_B 3 + #define BUTTON_C 8 +#elif defined(ARDUINO_FEATHER52832) + #define BUTTON_A 31 + #define BUTTON_B 30 + #define BUTTON_C 27 +#else // 32u4, M0, M4, nrf52840 and 328p + #define BUTTON_A 9 + #define BUTTON_B 6 + #define BUTTON_C 5 +#endif + +void setup() { + Serial.begin(9600); + + Serial.println("OLED FeatherWing test"); + // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally + display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // Address 0x3C for 128x32 + + Serial.println("OLED begun"); + + // Show image buffer on the display hardware. + // Since the buffer is intialized with an Adafruit splashscreen + // internally, this will display the splashscreen. + display.display(); + delay(1000); + + // Clear the buffer. + display.clearDisplay(); + display.display(); + + Serial.println("IO test"); + + pinMode(BUTTON_A, INPUT_PULLUP); + pinMode(BUTTON_B, INPUT_PULLUP); + pinMode(BUTTON_C, INPUT_PULLUP); + + // text display tests + display.setTextSize(1); + display.setTextColor(WHITE); + display.setCursor(0,0); + display.print("Connecting to SSID\n'adafruit':"); + display.print("connected!"); + display.println("IP: 10.0.1.23"); + display.println("Sending val #0"); + display.setCursor(0,0); + display.display(); // actually display all of the above +} + +void loop() { + if(!digitalRead(BUTTON_A)) display.print("A"); + if(!digitalRead(BUTTON_B)) display.print("B"); + if(!digitalRead(BUTTON_C)) display.print("C"); + delay(10); + yield(); + display.display(); +} diff --git a/lib/Adafruit_SSD1306-1.3.0-gemu-1.1/examples/ssd1306_128x32_i2c/ssd1306_128x32_i2c.ino b/lib/Adafruit_SSD1306-1.3.0-gemu-1.1/examples/ssd1306_128x32_i2c/ssd1306_128x32_i2c.ino new file mode 100644 index 000000000..68bfee354 --- /dev/null +++ b/lib/Adafruit_SSD1306-1.3.0-gemu-1.1/examples/ssd1306_128x32_i2c/ssd1306_128x32_i2c.ino @@ -0,0 +1,410 @@ +/************************************************************************** + This is an example for our Monochrome OLEDs based on SSD1306 drivers + + Pick one up today in the adafruit shop! + ------> http://www.adafruit.com/category/63_98 + + This example is for a 128x32 pixel display using I2C to communicate + 3 pins are required to interface (two I2C and one reset). + + Adafruit invests time and resources providing this open + source code, please support Adafruit and open-source + hardware by purchasing products from Adafruit! + + Written by Limor Fried/Ladyada for Adafruit Industries, + with contributions from the open source community. + BSD license, check license.txt for more information + All text above, and the splash screen below must be + included in any redistribution. + **************************************************************************/ + +#include +#include +#include +#include + +#define SCREEN_WIDTH 128 // OLED display width, in pixels +#define SCREEN_HEIGHT 32 // OLED display height, in pixels + +// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins) +#define OLED_RESET 4 // Reset pin # (or -1 if sharing Arduino reset pin) +Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); + +#define NUMFLAKES 10 // Number of snowflakes in the animation example + +#define LOGO_HEIGHT 16 +#define LOGO_WIDTH 16 +static const unsigned char PROGMEM logo_bmp[] = +{ B00000000, B11000000, + B00000001, B11000000, + B00000001, B11000000, + B00000011, B11100000, + B11110011, B11100000, + B11111110, B11111000, + B01111110, B11111111, + B00110011, B10011111, + B00011111, B11111100, + B00001101, B01110000, + B00011011, B10100000, + B00111111, B11100000, + B00111111, B11110000, + B01111100, B11110000, + B01110000, B01110000, + B00000000, B00110000 }; + +void setup() { + Serial.begin(9600); + + // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally + if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3C for 128x32 + Serial.println(F("SSD1306 allocation failed")); + for(;;); // Don't proceed, loop forever + } + + // Show initial display buffer contents on the screen -- + // the library initializes this with an Adafruit splash screen. + display.display(); + delay(2000); // Pause for 2 seconds + + // Clear the buffer + display.clearDisplay(); + + // Draw a single pixel in white + display.drawPixel(10, 10, WHITE); + + // Show the display buffer on the screen. You MUST call display() after + // drawing commands to make them visible on screen! + display.display(); + delay(2000); + // display.display() is NOT necessary after every single drawing command, + // unless that's what you want...rather, you can batch up a bunch of + // drawing operations and then update the screen all at once by calling + // display.display(). These examples demonstrate both approaches... + + testdrawline(); // Draw many lines + + testdrawrect(); // Draw rectangles (outlines) + + testfillrect(); // Draw rectangles (filled) + + testdrawcircle(); // Draw circles (outlines) + + testfillcircle(); // Draw circles (filled) + + testdrawroundrect(); // Draw rounded rectangles (outlines) + + testfillroundrect(); // Draw rounded rectangles (filled) + + testdrawtriangle(); // Draw triangles (outlines) + + testfilltriangle(); // Draw triangles (filled) + + testdrawchar(); // Draw characters of the default font + + testdrawstyles(); // Draw 'stylized' characters + + testscrolltext(); // Draw scrolling text + + testdrawbitmap(); // Draw a small bitmap image + + // Invert and restore display, pausing in-between + display.invertDisplay(true); + delay(1000); + display.invertDisplay(false); + delay(1000); + + testanimate(logo_bmp, LOGO_WIDTH, LOGO_HEIGHT); // Animate bitmaps +} + +void loop() { +} + +void testdrawline() { + int16_t i; + + display.clearDisplay(); // Clear display buffer + + for(i=0; i=0; i-=4) { + display.drawLine(0, display.height()-1, display.width()-1, i, WHITE); + display.display(); + delay(1); + } + delay(250); + + display.clearDisplay(); + + for(i=display.width()-1; i>=0; i-=4) { + display.drawLine(display.width()-1, display.height()-1, i, 0, WHITE); + display.display(); + delay(1); + } + for(i=display.height()-1; i>=0; i-=4) { + display.drawLine(display.width()-1, display.height()-1, 0, i, WHITE); + display.display(); + delay(1); + } + delay(250); + + display.clearDisplay(); + + for(i=0; i0; i-=3) { + // The INVERSE color is used so circles alternate white/black + display.fillCircle(display.width() / 2, display.height() / 2, i, INVERSE); + display.display(); // Update screen with each newly-drawn circle + delay(1); + } + + delay(2000); +} + +void testdrawroundrect(void) { + display.clearDisplay(); + + for(int16_t i=0; i0; i-=5) { + // The INVERSE color is used so triangles alternate white/black + display.fillTriangle( + display.width()/2 , display.height()/2-i, + display.width()/2-i, display.height()/2+i, + display.width()/2+i, display.height()/2+i, INVERSE); + display.display(); + delay(1); + } + + delay(2000); +} + +void testdrawchar(void) { + display.clearDisplay(); + + display.setTextSize(1); // Normal 1:1 pixel scale + display.setTextColor(WHITE); // Draw white text + display.setCursor(0, 0); // Start at top-left corner + display.cp437(true); // Use full 256 char 'Code Page 437' font + + // Not all the characters will fit on the display. This is normal. + // Library will draw what it can and the rest will be clipped. + for(int16_t i=0; i<256; i++) { + if(i == '\n') display.write(' '); + else display.write(i); + } + + display.display(); + delay(2000); +} + +void testdrawstyles(void) { + display.clearDisplay(); + + display.setTextSize(1); // Normal 1:1 pixel scale + display.setTextColor(WHITE); // Draw white text + display.setCursor(0,0); // Start at top-left corner + display.println(F("Hello, world!")); + + display.setTextColor(BLACK, WHITE); // Draw 'inverse' text + display.println(3.141592); + + display.setTextSize(2); // Draw 2X-scale text + display.setTextColor(WHITE); + display.print(F("0x")); display.println(0xDEADBEEF, HEX); + + display.display(); + delay(2000); +} + +void testscrolltext(void) { + display.clearDisplay(); + + display.setTextSize(2); // Draw 2X-scale text + display.setTextColor(WHITE); + display.setCursor(10, 0); + display.println(F("scroll")); + display.display(); // Show initial text + delay(100); + + // Scroll in various directions, pausing in-between: + display.startscrollright(0x00, 0x0F); + delay(2000); + display.stopscroll(); + delay(1000); + display.startscrollleft(0x00, 0x0F); + delay(2000); + display.stopscroll(); + delay(1000); + display.startscrolldiagright(0x00, 0x07); + delay(2000); + display.startscrolldiagleft(0x00, 0x07); + delay(2000); + display.stopscroll(); + delay(1000); +} + +void testdrawbitmap(void) { + display.clearDisplay(); + + display.drawBitmap( + (display.width() - LOGO_WIDTH ) / 2, + (display.height() - LOGO_HEIGHT) / 2, + logo_bmp, LOGO_WIDTH, LOGO_HEIGHT, 1); + display.display(); + delay(1000); +} + +#define XPOS 0 // Indexes into the 'icons' array in function below +#define YPOS 1 +#define DELTAY 2 + +void testanimate(const uint8_t *bitmap, uint8_t w, uint8_t h) { + int8_t f, icons[NUMFLAKES][3]; + + // Initialize 'snowflake' positions + for(f=0; f< NUMFLAKES; f++) { + icons[f][XPOS] = random(1 - LOGO_WIDTH, display.width()); + icons[f][YPOS] = -LOGO_HEIGHT; + icons[f][DELTAY] = random(1, 6); + Serial.print(F("x: ")); + Serial.print(icons[f][XPOS], DEC); + Serial.print(F(" y: ")); + Serial.print(icons[f][YPOS], DEC); + Serial.print(F(" dy: ")); + Serial.println(icons[f][DELTAY], DEC); + } + + for(;;) { // Loop forever... + display.clearDisplay(); // Clear the display buffer + + // Draw each snowflake: + for(f=0; f< NUMFLAKES; f++) { + display.drawBitmap(icons[f][XPOS], icons[f][YPOS], bitmap, w, h, WHITE); + } + + display.display(); // Show the display buffer on the screen + delay(200); // Pause for 1/10 second + + // Then update coordinates of each flake... + for(f=0; f< NUMFLAKES; f++) { + icons[f][YPOS] += icons[f][DELTAY]; + // If snowflake is off the bottom of the screen... + if (icons[f][YPOS] >= display.height()) { + // Reinitialize to a random position, just off the top + icons[f][XPOS] = random(1 - LOGO_WIDTH, display.width()); + icons[f][YPOS] = -LOGO_HEIGHT; + icons[f][DELTAY] = random(1, 6); + } + } + } +} diff --git a/lib/Adafruit_SSD1306-1.3.0-gemu-1.1/examples/ssd1306_128x32_spi/ssd1306_128x32_spi.ino b/lib/Adafruit_SSD1306-1.3.0-gemu-1.1/examples/ssd1306_128x32_spi/ssd1306_128x32_spi.ino new file mode 100644 index 000000000..b254785f3 --- /dev/null +++ b/lib/Adafruit_SSD1306-1.3.0-gemu-1.1/examples/ssd1306_128x32_spi/ssd1306_128x32_spi.ino @@ -0,0 +1,423 @@ +/************************************************************************** + This is an example for our Monochrome OLEDs based on SSD1306 drivers + + Pick one up today in the adafruit shop! + ------> http://www.adafruit.com/category/63_98 + + This example is for a 128x32 pixel display using SPI to communicate + 4 or 5 pins are required to interface. + + Adafruit invests time and resources providing this open + source code, please support Adafruit and open-source + hardware by purchasing products from Adafruit! + + Written by Limor Fried/Ladyada for Adafruit Industries, + with contributions from the open source community. + BSD license, check license.txt for more information + All text above, and the splash screen below must be + included in any redistribution. + **************************************************************************/ + +#include +#include +#include +#include + +#define SCREEN_WIDTH 128 // OLED display width, in pixels +#define SCREEN_HEIGHT 32 // OLED display height, in pixels + +// Declaration for SSD1306 display connected using software SPI (default case): +#define OLED_MOSI 9 +#define OLED_CLK 10 +#define OLED_DC 11 +#define OLED_CS 12 +#define OLED_RESET 13 +Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, + OLED_MOSI, OLED_CLK, OLED_DC, OLED_RESET, OLED_CS); + +/* Comment out above, uncomment this block to use hardware SPI +#define OLED_DC 6 +#define OLED_CS 7 +#define OLED_RESET 8 +Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, + &SPI, OLED_DC, OLED_RESET, OLED_CS); +*/ + +#define NUMFLAKES 10 // Number of snowflakes in the animation example + +#define LOGO_HEIGHT 16 +#define LOGO_WIDTH 16 +static const unsigned char PROGMEM logo_bmp[] = +{ B00000000, B11000000, + B00000001, B11000000, + B00000001, B11000000, + B00000011, B11100000, + B11110011, B11100000, + B11111110, B11111000, + B01111110, B11111111, + B00110011, B10011111, + B00011111, B11111100, + B00001101, B01110000, + B00011011, B10100000, + B00111111, B11100000, + B00111111, B11110000, + B01111100, B11110000, + B01110000, B01110000, + B00000000, B00110000 }; + +void setup() { + Serial.begin(9600); + + // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally + if(!display.begin(SSD1306_SWITCHCAPVCC)) { + Serial.println(F("SSD1306 allocation failed")); + for(;;); // Don't proceed, loop forever + } + + // Show initial display buffer contents on the screen -- + // the library initializes this with an Adafruit splash screen. + display.display(); + delay(2000); // Pause for 2 seconds + + // Clear the buffer + display.clearDisplay(); + + // Draw a single pixel in white + display.drawPixel(10, 10, WHITE); + + // Show the display buffer on the screen. You MUST call display() after + // drawing commands to make them visible on screen! + display.display(); + delay(2000); + // display.display() is NOT necessary after every single drawing command, + // unless that's what you want...rather, you can batch up a bunch of + // drawing operations and then update the screen all at once by calling + // display.display(). These examples demonstrate both approaches... + + testdrawline(); // Draw many lines + + testdrawrect(); // Draw rectangles (outlines) + + testfillrect(); // Draw rectangles (filled) + + testdrawcircle(); // Draw circles (outlines) + + testfillcircle(); // Draw circles (filled) + + testdrawroundrect(); // Draw rounded rectangles (outlines) + + testfillroundrect(); // Draw rounded rectangles (filled) + + testdrawtriangle(); // Draw triangles (outlines) + + testfilltriangle(); // Draw triangles (filled) + + testdrawchar(); // Draw characters of the default font + + testdrawstyles(); // Draw 'stylized' characters + + testscrolltext(); // Draw scrolling text + + testdrawbitmap(); // Draw a small bitmap image + + // Invert and restore display, pausing in-between + display.invertDisplay(true); + delay(1000); + display.invertDisplay(false); + delay(1000); + + testanimate(logo_bmp, LOGO_WIDTH, LOGO_HEIGHT); // Animate bitmaps +} + +void loop() { +} + +void testdrawline() { + int16_t i; + + display.clearDisplay(); // Clear display buffer + + for(i=0; i=0; i-=4) { + display.drawLine(0, display.height()-1, display.width()-1, i, WHITE); + display.display(); + delay(1); + } + delay(250); + + display.clearDisplay(); + + for(i=display.width()-1; i>=0; i-=4) { + display.drawLine(display.width()-1, display.height()-1, i, 0, WHITE); + display.display(); + delay(1); + } + for(i=display.height()-1; i>=0; i-=4) { + display.drawLine(display.width()-1, display.height()-1, 0, i, WHITE); + display.display(); + delay(1); + } + delay(250); + + display.clearDisplay(); + + for(i=0; i0; i-=3) { + // The INVERSE color is used so circles alternate white/black + display.fillCircle(display.width() / 2, display.height() / 2, i, INVERSE); + display.display(); // Update screen with each newly-drawn circle + delay(1); + } + + delay(2000); +} + +void testdrawroundrect(void) { + display.clearDisplay(); + + for(int16_t i=0; i0; i-=5) { + // The INVERSE color is used so triangles alternate white/black + display.fillTriangle( + display.width()/2 , display.height()/2-i, + display.width()/2-i, display.height()/2+i, + display.width()/2+i, display.height()/2+i, INVERSE); + display.display(); + delay(1); + } + + delay(2000); +} + +void testdrawchar(void) { + display.clearDisplay(); + + display.setTextSize(1); // Normal 1:1 pixel scale + display.setTextColor(WHITE); // Draw white text + display.setCursor(0, 0); // Start at top-left corner + display.cp437(true); // Use full 256 char 'Code Page 437' font + + // Not all the characters will fit on the display. This is normal. + // Library will draw what it can and the rest will be clipped. + for(int16_t i=0; i<256; i++) { + if(i == '\n') display.write(' '); + else display.write(i); + } + + display.display(); + delay(2000); +} + +void testdrawstyles(void) { + display.clearDisplay(); + + display.setTextSize(1); // Normal 1:1 pixel scale + display.setTextColor(WHITE); // Draw white text + display.setCursor(0,0); // Start at top-left corner + display.println(F("Hello, world!")); + + display.setTextColor(BLACK, WHITE); // Draw 'inverse' text + display.println(3.141592); + + display.setTextSize(2); // Draw 2X-scale text + display.setTextColor(WHITE); + display.print(F("0x")); display.println(0xDEADBEEF, HEX); + + display.display(); + delay(2000); +} + +void testscrolltext(void) { + display.clearDisplay(); + + display.setTextSize(2); // Draw 2X-scale text + display.setTextColor(WHITE); + display.setCursor(10, 0); + display.println(F("scroll")); + display.display(); // Show initial text + delay(100); + + // Scroll in various directions, pausing in-between: + display.startscrollright(0x00, 0x0F); + delay(2000); + display.stopscroll(); + delay(1000); + display.startscrollleft(0x00, 0x0F); + delay(2000); + display.stopscroll(); + delay(1000); + display.startscrolldiagright(0x00, 0x07); + delay(2000); + display.startscrolldiagleft(0x00, 0x07); + delay(2000); + display.stopscroll(); + delay(1000); +} + +void testdrawbitmap(void) { + display.clearDisplay(); + + display.drawBitmap( + (display.width() - LOGO_WIDTH ) / 2, + (display.height() - LOGO_HEIGHT) / 2, + logo_bmp, LOGO_WIDTH, LOGO_HEIGHT, 1); + display.display(); + delay(1000); +} + +#define XPOS 0 // Indexes into the 'icons' array in function below +#define YPOS 1 +#define DELTAY 2 + +void testanimate(const uint8_t *bitmap, uint8_t w, uint8_t h) { + int8_t f, icons[NUMFLAKES][3]; + + // Initialize 'snowflake' positions + for(f=0; f< NUMFLAKES; f++) { + icons[f][XPOS] = random(1 - LOGO_WIDTH, display.width()); + icons[f][YPOS] = -LOGO_HEIGHT; + icons[f][DELTAY] = random(1, 6); + Serial.print(F("x: ")); + Serial.print(icons[f][XPOS], DEC); + Serial.print(F(" y: ")); + Serial.print(icons[f][YPOS], DEC); + Serial.print(F(" dy: ")); + Serial.println(icons[f][DELTAY], DEC); + } + + for(;;) { // Loop forever... + display.clearDisplay(); // Clear the display buffer + + // Draw each snowflake: + for(f=0; f< NUMFLAKES; f++) { + display.drawBitmap(icons[f][XPOS], icons[f][YPOS], bitmap, w, h, WHITE); + } + + display.display(); // Show the display buffer on the screen + delay(200); // Pause for 1/10 second + + // Then update coordinates of each flake... + for(f=0; f< NUMFLAKES; f++) { + icons[f][YPOS] += icons[f][DELTAY]; + // If snowflake is off the bottom of the screen... + if (icons[f][YPOS] >= display.height()) { + // Reinitialize to a random position, just off the top + icons[f][XPOS] = random(1 - LOGO_WIDTH, display.width()); + icons[f][YPOS] = -LOGO_HEIGHT; + icons[f][DELTAY] = random(1, 6); + } + } + } +} diff --git a/lib/Adafruit_SSD1306-1.3.0-gemu-1.1/examples/ssd1306_128x64_i2c/ssd1306_128x64_i2c.ino b/lib/Adafruit_SSD1306-1.3.0-gemu-1.1/examples/ssd1306_128x64_i2c/ssd1306_128x64_i2c.ino new file mode 100644 index 000000000..6d7d5ddd0 --- /dev/null +++ b/lib/Adafruit_SSD1306-1.3.0-gemu-1.1/examples/ssd1306_128x64_i2c/ssd1306_128x64_i2c.ino @@ -0,0 +1,410 @@ +/************************************************************************** + This is an example for our Monochrome OLEDs based on SSD1306 drivers + + Pick one up today in the adafruit shop! + ------> http://www.adafruit.com/category/63_98 + + This example is for a 128x32 pixel display using I2C to communicate + 3 pins are required to interface (two I2C and one reset). + + Adafruit invests time and resources providing this open + source code, please support Adafruit and open-source + hardware by purchasing products from Adafruit! + + Written by Limor Fried/Ladyada for Adafruit Industries, + with contributions from the open source community. + BSD license, check license.txt for more information + All text above, and the splash screen below must be + included in any redistribution. + **************************************************************************/ + +#include +#include +#include +#include + +#define SCREEN_WIDTH 128 // OLED display width, in pixels +#define SCREEN_HEIGHT 64 // OLED display height, in pixels + +// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins) +#define OLED_RESET 4 // Reset pin # (or -1 if sharing Arduino reset pin) +Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); + +#define NUMFLAKES 10 // Number of snowflakes in the animation example + +#define LOGO_HEIGHT 16 +#define LOGO_WIDTH 16 +static const unsigned char PROGMEM logo_bmp[] = +{ B00000000, B11000000, + B00000001, B11000000, + B00000001, B11000000, + B00000011, B11100000, + B11110011, B11100000, + B11111110, B11111000, + B01111110, B11111111, + B00110011, B10011111, + B00011111, B11111100, + B00001101, B01110000, + B00011011, B10100000, + B00111111, B11100000, + B00111111, B11110000, + B01111100, B11110000, + B01110000, B01110000, + B00000000, B00110000 }; + +void setup() { + Serial.begin(9600); + + // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally + if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3D)) { // Address 0x3D for 128x64 + Serial.println(F("SSD1306 allocation failed")); + for(;;); // Don't proceed, loop forever + } + + // Show initial display buffer contents on the screen -- + // the library initializes this with an Adafruit splash screen. + display.display(); + delay(2000); // Pause for 2 seconds + + // Clear the buffer + display.clearDisplay(); + + // Draw a single pixel in white + display.drawPixel(10, 10, WHITE); + + // Show the display buffer on the screen. You MUST call display() after + // drawing commands to make them visible on screen! + display.display(); + delay(2000); + // display.display() is NOT necessary after every single drawing command, + // unless that's what you want...rather, you can batch up a bunch of + // drawing operations and then update the screen all at once by calling + // display.display(). These examples demonstrate both approaches... + + testdrawline(); // Draw many lines + + testdrawrect(); // Draw rectangles (outlines) + + testfillrect(); // Draw rectangles (filled) + + testdrawcircle(); // Draw circles (outlines) + + testfillcircle(); // Draw circles (filled) + + testdrawroundrect(); // Draw rounded rectangles (outlines) + + testfillroundrect(); // Draw rounded rectangles (filled) + + testdrawtriangle(); // Draw triangles (outlines) + + testfilltriangle(); // Draw triangles (filled) + + testdrawchar(); // Draw characters of the default font + + testdrawstyles(); // Draw 'stylized' characters + + testscrolltext(); // Draw scrolling text + + testdrawbitmap(); // Draw a small bitmap image + + // Invert and restore display, pausing in-between + display.invertDisplay(true); + delay(1000); + display.invertDisplay(false); + delay(1000); + + testanimate(logo_bmp, LOGO_WIDTH, LOGO_HEIGHT); // Animate bitmaps +} + +void loop() { +} + +void testdrawline() { + int16_t i; + + display.clearDisplay(); // Clear display buffer + + for(i=0; i=0; i-=4) { + display.drawLine(0, display.height()-1, display.width()-1, i, WHITE); + display.display(); + delay(1); + } + delay(250); + + display.clearDisplay(); + + for(i=display.width()-1; i>=0; i-=4) { + display.drawLine(display.width()-1, display.height()-1, i, 0, WHITE); + display.display(); + delay(1); + } + for(i=display.height()-1; i>=0; i-=4) { + display.drawLine(display.width()-1, display.height()-1, 0, i, WHITE); + display.display(); + delay(1); + } + delay(250); + + display.clearDisplay(); + + for(i=0; i0; i-=3) { + // The INVERSE color is used so circles alternate white/black + display.fillCircle(display.width() / 2, display.height() / 2, i, INVERSE); + display.display(); // Update screen with each newly-drawn circle + delay(1); + } + + delay(2000); +} + +void testdrawroundrect(void) { + display.clearDisplay(); + + for(int16_t i=0; i0; i-=5) { + // The INVERSE color is used so triangles alternate white/black + display.fillTriangle( + display.width()/2 , display.height()/2-i, + display.width()/2-i, display.height()/2+i, + display.width()/2+i, display.height()/2+i, INVERSE); + display.display(); + delay(1); + } + + delay(2000); +} + +void testdrawchar(void) { + display.clearDisplay(); + + display.setTextSize(1); // Normal 1:1 pixel scale + display.setTextColor(WHITE); // Draw white text + display.setCursor(0, 0); // Start at top-left corner + display.cp437(true); // Use full 256 char 'Code Page 437' font + + // Not all the characters will fit on the display. This is normal. + // Library will draw what it can and the rest will be clipped. + for(int16_t i=0; i<256; i++) { + if(i == '\n') display.write(' '); + else display.write(i); + } + + display.display(); + delay(2000); +} + +void testdrawstyles(void) { + display.clearDisplay(); + + display.setTextSize(1); // Normal 1:1 pixel scale + display.setTextColor(WHITE); // Draw white text + display.setCursor(0,0); // Start at top-left corner + display.println(F("Hello, world!")); + + display.setTextColor(BLACK, WHITE); // Draw 'inverse' text + display.println(3.141592); + + display.setTextSize(2); // Draw 2X-scale text + display.setTextColor(WHITE); + display.print(F("0x")); display.println(0xDEADBEEF, HEX); + + display.display(); + delay(2000); +} + +void testscrolltext(void) { + display.clearDisplay(); + + display.setTextSize(2); // Draw 2X-scale text + display.setTextColor(WHITE); + display.setCursor(10, 0); + display.println(F("scroll")); + display.display(); // Show initial text + delay(100); + + // Scroll in various directions, pausing in-between: + display.startscrollright(0x00, 0x0F); + delay(2000); + display.stopscroll(); + delay(1000); + display.startscrollleft(0x00, 0x0F); + delay(2000); + display.stopscroll(); + delay(1000); + display.startscrolldiagright(0x00, 0x07); + delay(2000); + display.startscrolldiagleft(0x00, 0x07); + delay(2000); + display.stopscroll(); + delay(1000); +} + +void testdrawbitmap(void) { + display.clearDisplay(); + + display.drawBitmap( + (display.width() - LOGO_WIDTH ) / 2, + (display.height() - LOGO_HEIGHT) / 2, + logo_bmp, LOGO_WIDTH, LOGO_HEIGHT, 1); + display.display(); + delay(1000); +} + +#define XPOS 0 // Indexes into the 'icons' array in function below +#define YPOS 1 +#define DELTAY 2 + +void testanimate(const uint8_t *bitmap, uint8_t w, uint8_t h) { + int8_t f, icons[NUMFLAKES][3]; + + // Initialize 'snowflake' positions + for(f=0; f< NUMFLAKES; f++) { + icons[f][XPOS] = random(1 - LOGO_WIDTH, display.width()); + icons[f][YPOS] = -LOGO_HEIGHT; + icons[f][DELTAY] = random(1, 6); + Serial.print(F("x: ")); + Serial.print(icons[f][XPOS], DEC); + Serial.print(F(" y: ")); + Serial.print(icons[f][YPOS], DEC); + Serial.print(F(" dy: ")); + Serial.println(icons[f][DELTAY], DEC); + } + + for(;;) { // Loop forever... + display.clearDisplay(); // Clear the display buffer + + // Draw each snowflake: + for(f=0; f< NUMFLAKES; f++) { + display.drawBitmap(icons[f][XPOS], icons[f][YPOS], bitmap, w, h, WHITE); + } + + display.display(); // Show the display buffer on the screen + delay(200); // Pause for 1/10 second + + // Then update coordinates of each flake... + for(f=0; f< NUMFLAKES; f++) { + icons[f][YPOS] += icons[f][DELTAY]; + // If snowflake is off the bottom of the screen... + if (icons[f][YPOS] >= display.height()) { + // Reinitialize to a random position, just off the top + icons[f][XPOS] = random(1 - LOGO_WIDTH, display.width()); + icons[f][YPOS] = -LOGO_HEIGHT; + icons[f][DELTAY] = random(1, 6); + } + } + } +} diff --git a/lib/Adafruit_SSD1306-1.3.0-gemu-1.1/examples/ssd1306_128x64_spi/ssd1306_128x64_spi.ino b/lib/Adafruit_SSD1306-1.3.0-gemu-1.1/examples/ssd1306_128x64_spi/ssd1306_128x64_spi.ino new file mode 100644 index 000000000..dbe300d43 --- /dev/null +++ b/lib/Adafruit_SSD1306-1.3.0-gemu-1.1/examples/ssd1306_128x64_spi/ssd1306_128x64_spi.ino @@ -0,0 +1,424 @@ +/************************************************************************** + This is an example for our Monochrome OLEDs based on SSD1306 drivers + + Pick one up today in the adafruit shop! + ------> http://www.adafruit.com/category/63_98 + + This example is for a 128x64 pixel display using SPI to communicate + 4 or 5 pins are required to interface. + + Adafruit invests time and resources providing this open + source code, please support Adafruit and open-source + hardware by purchasing products from Adafruit! + + Written by Limor Fried/Ladyada for Adafruit Industries, + with contributions from the open source community. + BSD license, check license.txt for more information + All text above, and the splash screen below must be + included in any redistribution. + **************************************************************************/ + +#include +#include +#include +#include + +#define SCREEN_WIDTH 128 // OLED display width, in pixels +#define SCREEN_HEIGHT 64 // OLED display height, in pixels + +// Declaration for SSD1306 display connected using software SPI (default case): +#define OLED_MOSI 9 +#define OLED_CLK 10 +#define OLED_DC 11 +#define OLED_CS 12 +#define OLED_RESET 13 +Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, + OLED_MOSI, OLED_CLK, OLED_DC, OLED_RESET, OLED_CS); + +/* Comment out above, uncomment this block to use hardware SPI +#define OLED_DC 6 +#define OLED_CS 7 +#define OLED_RESET 8 +Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, + &SPI, OLED_DC, OLED_RESET, OLED_CS); +*/ + +#define NUMFLAKES 10 // Number of snowflakes in the animation example + +#define LOGO_HEIGHT 16 +#define LOGO_WIDTH 16 +static const unsigned char PROGMEM logo_bmp[] = +{ B00000000, B11000000, + B00000001, B11000000, + B00000001, B11000000, + B00000011, B11100000, + B11110011, B11100000, + B11111110, B11111000, + B01111110, B11111111, + B00110011, B10011111, + B00011111, B11111100, + B00001101, B01110000, + B00011011, B10100000, + B00111111, B11100000, + B00111111, B11110000, + B01111100, B11110000, + B01110000, B01110000, + B00000000, B00110000 }; + +void setup() { + Serial.begin(9600); + + // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally + if(!display.begin(SSD1306_SWITCHCAPVCC)) { + Serial.println(F("SSD1306 allocation failed")); + for(;;); // Don't proceed, loop forever + } + + + // Show initial display buffer contents on the screen -- + // the library initializes this with an Adafruit splash screen. + display.display(); + delay(2000); // Pause for 2 seconds + + // Clear the buffer + display.clearDisplay(); + + // Draw a single pixel in white + display.drawPixel(10, 10, WHITE); + + // Show the display buffer on the screen. You MUST call display() after + // drawing commands to make them visible on screen! + display.display(); + delay(2000); + // display.display() is NOT necessary after every single drawing command, + // unless that's what you want...rather, you can batch up a bunch of + // drawing operations and then update the screen all at once by calling + // display.display(). These examples demonstrate both approaches... + + testdrawline(); // Draw many lines + + testdrawrect(); // Draw rectangles (outlines) + + testfillrect(); // Draw rectangles (filled) + + testdrawcircle(); // Draw circles (outlines) + + testfillcircle(); // Draw circles (filled) + + testdrawroundrect(); // Draw rounded rectangles (outlines) + + testfillroundrect(); // Draw rounded rectangles (filled) + + testdrawtriangle(); // Draw triangles (outlines) + + testfilltriangle(); // Draw triangles (filled) + + testdrawchar(); // Draw characters of the default font + + testdrawstyles(); // Draw 'stylized' characters + + testscrolltext(); // Draw scrolling text + + testdrawbitmap(); // Draw a small bitmap image + + // Invert and restore display, pausing in-between + display.invertDisplay(true); + delay(1000); + display.invertDisplay(false); + delay(1000); + + testanimate(logo_bmp, LOGO_WIDTH, LOGO_HEIGHT); // Animate bitmaps +} + +void loop() { +} + +void testdrawline() { + int16_t i; + + display.clearDisplay(); // Clear display buffer + + for(i=0; i=0; i-=4) { + display.drawLine(0, display.height()-1, display.width()-1, i, WHITE); + display.display(); + delay(1); + } + delay(250); + + display.clearDisplay(); + + for(i=display.width()-1; i>=0; i-=4) { + display.drawLine(display.width()-1, display.height()-1, i, 0, WHITE); + display.display(); + delay(1); + } + for(i=display.height()-1; i>=0; i-=4) { + display.drawLine(display.width()-1, display.height()-1, 0, i, WHITE); + display.display(); + delay(1); + } + delay(250); + + display.clearDisplay(); + + for(i=0; i0; i-=3) { + // The INVERSE color is used so circles alternate white/black + display.fillCircle(display.width() / 2, display.height() / 2, i, INVERSE); + display.display(); // Update screen with each newly-drawn circle + delay(1); + } + + delay(2000); +} + +void testdrawroundrect(void) { + display.clearDisplay(); + + for(int16_t i=0; i0; i-=5) { + // The INVERSE color is used so triangles alternate white/black + display.fillTriangle( + display.width()/2 , display.height()/2-i, + display.width()/2-i, display.height()/2+i, + display.width()/2+i, display.height()/2+i, INVERSE); + display.display(); + delay(1); + } + + delay(2000); +} + +void testdrawchar(void) { + display.clearDisplay(); + + display.setTextSize(1); // Normal 1:1 pixel scale + display.setTextColor(WHITE); // Draw white text + display.setCursor(0, 0); // Start at top-left corner + display.cp437(true); // Use full 256 char 'Code Page 437' font + + // Not all the characters will fit on the display. This is normal. + // Library will draw what it can and the rest will be clipped. + for(int16_t i=0; i<256; i++) { + if(i == '\n') display.write(' '); + else display.write(i); + } + + display.display(); + delay(2000); +} + +void testdrawstyles(void) { + display.clearDisplay(); + + display.setTextSize(1); // Normal 1:1 pixel scale + display.setTextColor(WHITE); // Draw white text + display.setCursor(0,0); // Start at top-left corner + display.println(F("Hello, world!")); + + display.setTextColor(BLACK, WHITE); // Draw 'inverse' text + display.println(3.141592); + + display.setTextSize(2); // Draw 2X-scale text + display.setTextColor(WHITE); + display.print(F("0x")); display.println(0xDEADBEEF, HEX); + + display.display(); + delay(2000); +} + +void testscrolltext(void) { + display.clearDisplay(); + + display.setTextSize(2); // Draw 2X-scale text + display.setTextColor(WHITE); + display.setCursor(10, 0); + display.println(F("scroll")); + display.display(); // Show initial text + delay(100); + + // Scroll in various directions, pausing in-between: + display.startscrollright(0x00, 0x0F); + delay(2000); + display.stopscroll(); + delay(1000); + display.startscrollleft(0x00, 0x0F); + delay(2000); + display.stopscroll(); + delay(1000); + display.startscrolldiagright(0x00, 0x07); + delay(2000); + display.startscrolldiagleft(0x00, 0x07); + delay(2000); + display.stopscroll(); + delay(1000); +} + +void testdrawbitmap(void) { + display.clearDisplay(); + + display.drawBitmap( + (display.width() - LOGO_WIDTH ) / 2, + (display.height() - LOGO_HEIGHT) / 2, + logo_bmp, LOGO_WIDTH, LOGO_HEIGHT, 1); + display.display(); + delay(1000); +} + +#define XPOS 0 // Indexes into the 'icons' array in function below +#define YPOS 1 +#define DELTAY 2 + +void testanimate(const uint8_t *bitmap, uint8_t w, uint8_t h) { + int8_t f, icons[NUMFLAKES][3]; + + // Initialize 'snowflake' positions + for(f=0; f< NUMFLAKES; f++) { + icons[f][XPOS] = random(1 - LOGO_WIDTH, display.width()); + icons[f][YPOS] = -LOGO_HEIGHT; + icons[f][DELTAY] = random(1, 6); + Serial.print(F("x: ")); + Serial.print(icons[f][XPOS], DEC); + Serial.print(F(" y: ")); + Serial.print(icons[f][YPOS], DEC); + Serial.print(F(" dy: ")); + Serial.println(icons[f][DELTAY], DEC); + } + + for(;;) { // Loop forever... + display.clearDisplay(); // Clear the display buffer + + // Draw each snowflake: + for(f=0; f< NUMFLAKES; f++) { + display.drawBitmap(icons[f][XPOS], icons[f][YPOS], bitmap, w, h, WHITE); + } + + display.display(); // Show the display buffer on the screen + delay(200); // Pause for 1/10 second + + // Then update coordinates of each flake... + for(f=0; f< NUMFLAKES; f++) { + icons[f][YPOS] += icons[f][DELTAY]; + // If snowflake is off the bottom of the screen... + if (icons[f][YPOS] >= display.height()) { + // Reinitialize to a random position, just off the top + icons[f][XPOS] = random(1 - LOGO_WIDTH, display.width()); + icons[f][YPOS] = -LOGO_HEIGHT; + icons[f][DELTAY] = random(1, 6); + } + } + } +} diff --git a/lib/Adafruit_SSD1306-1.3.0-gemu-1.1/library.properties b/lib/Adafruit_SSD1306-1.3.0-gemu-1.1/library.properties new file mode 100644 index 000000000..61b8efa07 --- /dev/null +++ b/lib/Adafruit_SSD1306-1.3.0-gemu-1.1/library.properties @@ -0,0 +1,9 @@ +name=Adafruit SSD1306 +version=1.3.0 +author=Adafruit +maintainer=Adafruit +sentence=SSD1306 oled driver library for monochrome 128x64 and 128x32 displays +paragraph=SSD1306 oled driver library for monochrome 128x64 and 128x32 displays +category=Display +url=https://github.com/adafruit/Adafruit_SSD1306 +architectures=* diff --git a/lib/Adafruit_SSD1306-1.3.0-gemu-1.1/license.txt b/lib/Adafruit_SSD1306-1.3.0-gemu-1.1/license.txt new file mode 100644 index 000000000..f6a0f22b8 --- /dev/null +++ b/lib/Adafruit_SSD1306-1.3.0-gemu-1.1/license.txt @@ -0,0 +1,26 @@ +Software License Agreement (BSD License) + +Copyright (c) 2012, Adafruit Industries +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: +1. Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. +2. 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. +3. Neither the name of the copyright holders nor the +names of its contributors may be used to endorse or promote products +derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''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 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. diff --git a/lib/Adafruit_SSD1306-1.3.0-gemu-1.1/splash.h b/lib/Adafruit_SSD1306-1.3.0-gemu-1.1/splash.h new file mode 100644 index 000000000..487daecb4 --- /dev/null +++ b/lib/Adafruit_SSD1306-1.3.0-gemu-1.1/splash.h @@ -0,0 +1,108 @@ +#define splash1_width 82 +#define splash1_height 64 + +const uint8_t PROGMEM splash1_data[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x07, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x07, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0F, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1F, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, + 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xE0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xF0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xF0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xF8, 0x7F, 0xF0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFE, 0x7F, 0xF0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x3F, 0xFF, 0x7F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1F, 0xFF, 0xFB, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0F, 0xFF, 0xF9, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0F, 0xFF, 0xF9, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, + 0xFF, 0xF1, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFC, + 0x73, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFE, 0x3F, + 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x1E, 0x0F, + 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFE, 0x1F, 0xFC, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xF8, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xDF, 0xFF, 0xE0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x19, 0xFF, 0xC0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3F, 0x3C, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x7E, 0x7C, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7F, 0xFE, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, + 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xEF, + 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xCF, 0xFE, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0x07, 0xFE, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFC, 0x07, 0xFE, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xF0, 0x03, 0xFE, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, + 0x80, 0x00, 0xFC, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x00, 0x07, 0x80, + 0x01, 0xFC, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x00, 0x07, 0x80, 0x01, + 0xFC, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x00, 0x07, 0x80, 0x01, 0xE0, + 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x07, 0x80, 0x01, 0xE0, 0x00, + 0x00, 0x00, 0x1E, 0x00, 0x7F, 0xE3, 0xF7, 0x9F, 0xF9, 0xFD, 0xE7, 0x78, + 0x7B, 0xDF, 0xC0, 0xFF, 0xF7, 0xFF, 0xBF, 0xFD, 0xFD, 0xFF, 0x78, 0x7B, + 0xDF, 0xC0, 0xFF, 0xF7, 0xFF, 0xBF, 0xFD, 0xFD, 0xFF, 0x78, 0x7B, 0xDF, + 0xC0, 0xF0, 0xF7, 0x87, 0xBC, 0x3D, 0xE1, 0xFF, 0x78, 0x7B, 0xDE, 0x00, + 0xF0, 0xF7, 0x87, 0xBC, 0x3D, 0xE1, 0xF0, 0x78, 0x7B, 0xDE, 0x00, 0x00, + 0xF7, 0x87, 0x80, 0x3D, 0xE1, 0xE0, 0x78, 0x7B, 0xDE, 0x00, 0x7F, 0xF7, + 0x87, 0x9F, 0xFD, 0xE1, 0xE0, 0x78, 0x7B, 0xDE, 0x00, 0xFF, 0xF7, 0x87, + 0xBF, 0xFD, 0xE1, 0xE0, 0x78, 0x7B, 0xDE, 0x00, 0xF0, 0xF7, 0x87, 0xBC, + 0x3D, 0xE1, 0xE0, 0x78, 0x7B, 0xDE, 0x00, 0xF0, 0xF7, 0x87, 0xBC, 0x3D, + 0xE1, 0xE0, 0x78, 0x7B, 0xDE, 0x00, 0xF0, 0xF7, 0x87, 0xBC, 0x3D, 0xE1, + 0xE0, 0x78, 0x7B, 0xDE, 0x00, 0xFF, 0xF7, 0xFF, 0xBF, 0xFD, 0xE1, 0xE0, + 0x7F, 0xFB, 0xDF, 0xC0, 0xFF, 0xF7, 0xFF, 0xBF, 0xFD, 0xE1, 0xE0, 0x7F, + 0xFB, 0xDF, 0xC0, 0x7C, 0xF3, 0xF3, 0x9F, 0x3D, 0xE1, 0xE0, 0x3E, 0x7B, + 0xCF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFD, 0x68, 0xDB, 0x11, 0x1A, 0x31, 0xC0, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFD, 0x2B, 0x5A, 0xFB, 0x6A, 0xEF, 0xC0, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFD, 0x4B, 0x5B, 0x3B, 0x1A, 0x33, 0xC0, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFD, 0x6B, 0x5B, 0xDB, 0x6A, 0xFD, 0xC0 }; + +#define splash2_width 115 +#define splash2_height 32 + +const uint8_t PROGMEM splash2_data[] = { + 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xE0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xF0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x03, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x07, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xF8, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xF8, + 0x00, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x7E, 0x00, 0x00, 0x01, 0xE0, 0x00, + 0x7F, 0x0F, 0xF8, 0x00, 0x00, 0x00, 0x03, 0xC0, 0x00, 0xFE, 0x00, 0x00, + 0x01, 0xE0, 0x00, 0xFF, 0xEF, 0xF8, 0x00, 0x00, 0x00, 0x03, 0xC0, 0x00, + 0xFE, 0x00, 0x00, 0x01, 0xE0, 0x00, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x00, + 0x03, 0xC0, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x7F, 0xFE, 0x7F, + 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x0F, 0x00, + 0x3F, 0xFE, 0x7F, 0xF8, 0x3F, 0xF1, 0xFB, 0xCF, 0xFC, 0xFE, 0xF3, 0xBC, + 0x3D, 0xEF, 0xE0, 0x1F, 0xFE, 0x7F, 0xFF, 0x7F, 0xFB, 0xFF, 0xDF, 0xFE, + 0xFE, 0xFF, 0xBC, 0x3D, 0xEF, 0xE0, 0x1F, 0xC6, 0xFF, 0xFF, 0x7F, 0xFB, + 0xFF, 0xDF, 0xFE, 0xFE, 0xFF, 0xBC, 0x3D, 0xEF, 0xE0, 0x0F, 0xE3, 0xC7, + 0xFE, 0x78, 0x7B, 0xC3, 0xDE, 0x1E, 0xF0, 0xFF, 0xBC, 0x3D, 0xEF, 0x00, + 0x07, 0xFF, 0x87, 0xFC, 0x78, 0x7B, 0xC3, 0xDE, 0x1E, 0xF0, 0xF8, 0x3C, + 0x3D, 0xEF, 0x00, 0x01, 0xFF, 0xFF, 0xF0, 0x00, 0x7B, 0xC3, 0xC0, 0x1E, + 0xF0, 0xF0, 0x3C, 0x3D, 0xEF, 0x00, 0x01, 0xF3, 0x7F, 0xE0, 0x3F, 0xFB, + 0xC3, 0xCF, 0xFE, 0xF0, 0xF0, 0x3C, 0x3D, 0xEF, 0x00, 0x03, 0xE3, 0x3F, + 0x80, 0x7F, 0xFB, 0xC3, 0xDF, 0xFE, 0xF0, 0xF0, 0x3C, 0x3D, 0xEF, 0x00, + 0x07, 0xE7, 0x3C, 0x00, 0x78, 0x7B, 0xC3, 0xDE, 0x1E, 0xF0, 0xF0, 0x3C, + 0x3D, 0xEF, 0x00, 0x07, 0xFF, 0xBE, 0x00, 0x78, 0x7B, 0xC3, 0xDE, 0x1E, + 0xF0, 0xF0, 0x3C, 0x3D, 0xEF, 0x00, 0x07, 0xFF, 0xFE, 0x00, 0x78, 0x7B, + 0xC3, 0xDE, 0x1E, 0xF0, 0xF0, 0x3C, 0x3D, 0xEF, 0x00, 0x0F, 0xFF, 0xFE, + 0x00, 0x7F, 0xFB, 0xFF, 0xDF, 0xFE, 0xF0, 0xF0, 0x3F, 0xFD, 0xEF, 0xE0, + 0x0F, 0xFF, 0xFF, 0x00, 0x7F, 0xFB, 0xFF, 0xDF, 0xFE, 0xF0, 0xF0, 0x3F, + 0xFD, 0xEF, 0xE0, 0x0F, 0xF9, 0xFF, 0x00, 0x3E, 0x79, 0xF9, 0xCF, 0x9E, + 0xF0, 0xF0, 0x1F, 0x3D, 0xE7, 0xE0, 0x1F, 0xF1, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x80, 0xFF, + 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, + 0x1C, 0x00, 0x7F, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFE, 0xB4, 0x6D, 0x88, + 0x8D, 0x18, 0xE0, 0x00, 0x00, 0x1F, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFE, + 0x95, 0xAD, 0x7D, 0xB5, 0x77, 0xE0, 0x00, 0x00, 0x0F, 0x00, 0x7F, 0xFF, + 0xFF, 0xFF, 0xFE, 0xA5, 0xAD, 0x9D, 0x8D, 0x19, 0xE0, 0x00, 0x00, 0x06, + 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFE, 0xB5, 0xAD, 0xED, 0xB5, 0x7E, 0xE0 }; diff --git a/lib/Adafruit_SSD1351-gemu-1.0/README.md b/lib/Adafruit_SSD1351-gemu-1.0/README.md new file mode 100644 index 000000000..6414419d0 --- /dev/null +++ b/lib/Adafruit_SSD1351-gemu-1.0/README.md @@ -0,0 +1,2 @@ +### SSD3115 Arduino Library +This library is for support for the 128x128 oled controller over 3 wire SPI. It is based heavily on the [Adafruit_SSD1351](https://github.com/adafruit/Adafruit-SSD1351-library) library and is designed to work with the [Adafruit_GFX library](https://github.com/adafruit/Adafruit-GFX-Library). diff --git a/lib/Adafruit_SSD1351-gemu-1.0/SSD1351.cpp b/lib/Adafruit_SSD1351-gemu-1.0/SSD1351.cpp new file mode 100644 index 000000000..3e1ccb2c7 --- /dev/null +++ b/lib/Adafruit_SSD1351-gemu-1.0/SSD1351.cpp @@ -0,0 +1,520 @@ +/*************************************************** + This is our library for the Adafruit SSD1351 Breakout and Shield + + Check out the links above for our tutorials and wiring diagrams + These displays use SPI to communicate, 4 or 5 pins are required to + interface (RST is optional) + Adafruit invests time and resources providing this open source code, + please support Adafruit and open-source hardware by purchasing + products from Adafruit! + + Written by Limor Fried/Ladyada for Adafruit Industries. + MIT license, all text above must be included in any redistribution + ****************************************************/ +#include +#include "SSD1351.h" +#include +#include + + +const uint16_t ssd1351_colors[]={SSD1351_BLACK,SSD1351_WHITE,SSD1351_RED,SSD1351_GREEN,SSD1351_BLUE,SSD1351_CYAN,SSD1351_MAGENTA,\ + SSD1351_YELLOW,SSD1351_NAVY,SSD1351_DARKGREEN,SSD1351_DARKCYAN,SSD1351_MAROON,SSD1351_PURPLE,SSD1351_OLIVE,\ +SSD1351_LIGHTGREY,SSD1351_DARKGREY,SSD1351_ORANGE,SSD1351_GREENYELLOW,SSD1351_PINK}; + +// Constructor when using software SPI. All output pins are configurable. +SSD1351::SSD1351(int8_t cs,int8_t mosi,int8_t sclk) : Renderer(SSD1351_WIDTH, SSD1351_HEIGHT) { + _cs = cs; + _mosi = mosi; + _sclk = sclk; + _hwspi = 0; +} + +#include "spi_register.h" + +/* CPU Clock = 80 Mhz +max clock of display is 4.545 Mhz (220ns sclk cycle) +so cpu/18 => 4.44 Mhz should be ok +HSPI CLK 5 GPIO14 +HSPI /CS 8 GPIO15 +HSPI MOSI 7 GPIO13 +*/ + +uint8_t ssd131_start; + +uint32_t ssd1351_clock; +uint32_t ssd1351_usr; +uint32_t ssd1351_usr1; +uint32_t ssd1351_usr2; +uint32_t ssd1351_spi1c; +uint32_t ssd1351_spi1p; +//uint32_t ssd1351_gpmux; +uint32_t ssd1351_mtdo; + + +uint32_t ssd1351_clock_prev; +uint32_t ssd1351_usr_prev; +uint32_t ssd1351_usr1_prev; +uint32_t ssd1351_usr2_prev; +uint32_t ssd1351_spi1c_prev; +uint32_t ssd1351_spi1p_prev; +//uint32_t ssd1351_gpmux_prev; +uint32_t ssd1351_mtdo_prev; + +// code from espressif SDK +/****************************************************************************** + * FunctionName : spi_lcd_mode_init + * Description : SPI master initial function for driving LCD 3 wire spi +*******************************************************************************/ +void SSD1351::spi_lcd_mode_init(void) { + uint32 regvalue; + + ssd1351_clock_prev=SPI1CLK; + ssd1351_usr_prev=SPI1U; + ssd1351_usr1_prev=SPI1U1; + ssd1351_usr2_prev=SPI1U2; + ssd1351_spi1c_prev=SPI1C; + ssd1351_spi1p_prev=SPI1P; + //ssd1351_gpmux_prev=GPMUX; + ssd1351_mtdo_prev=READ_PERI_REG(PERIPHS_IO_MUX_MTDO_U); + + SPI1U = SPIUMOSI | SPIUDUPLEX | SPIUSSE; + SPI1U1=0; + SPI1C = 0; + + //bit9 of PERIPHS_IO_MUX should be cleared when HSPI clock doesn't equal CPU clock + //bit8 of PERIPHS_IO_MUX should be cleared when SPI clock doesn't equal CPU clock + + WRITE_PERI_REG(PERIPHS_IO_MUX, 0x105); //clear bit9 + //PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDI_U, 2);//configure miso to spi mode + PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, 2);//configure mosi to spi mode + PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTMS_U, 2);//configure sclk to spi mode + PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U, 2);//configure cs to spi mode + +// the current implementation leaves about 1 us between transfers ???? +// due to lack of documentation i could not find the reason +// skipping this would double the speed !!! + + //SET_PERI_REG_MASK(SPI_USER(1), SPI_CS_SETUP|SPI_CS_HOLD|SPI_USR_COMMAND); + + SET_PERI_REG_MASK(SPI_USER(1), SPI_USR_COMMAND); + + CLEAR_PERI_REG_MASK(SPI_USER(1), SPI_FLASH_MODE); + // SPI clock=CPU clock/8 => 10 Mhz + /* + WRITE_PERI_REG(SPI_CLOCK(1), + ((1&SPI_CLKDIV_PRE)<>1)&0x7f; + + start(); + +//#define SPI_USR_COMMAND_BITLEN 0x0000000F +//#define SPI_USR_COMMAND_BITLEN_S 28 + + regvalue= ((8&SPI_USR_COMMAND_BITLEN)<>1)|0x80; + + start(); + + regvalue= ((8&SPI_USR_COMMAND_BITLEN)<=sizeof(ssd1351_colors)/2) index=0; + return ssd1351_colors[index]; +} + +void SSD1351::DisplayInit(int8_t p,int8_t size,int8_t rot,int8_t font) { + setRotation(rot); + invertDisplay(false); + setTextWrap(false); // Allow text to run off edges + cp437(true); + setTextFont(font&3); + setTextSize(size&7); + setTextColor(SSD1351_WHITE,SSD1351_BLACK); + setCursor(0,0); + fillScreen(SSD1351_BLACK); + stop(); +} + +void SSD1351::DisplayOnff(int8_t on) { + if (on) { + writecommand(SSD1351_CMD_DISPLAYON); //Display on + } else { + writecommand(SSD1351_CMD_DISPLAYOFF); + } + stop(); +} + +// dimmer 0-100 +void SSD1351::dim(uint8_t contrast) { + writecommand(SSD1351_CMD_CONTRASTMASTER); + if (contrast>15) contrast=15; + writedata(contrast); + stop(); +} + +/* +if (SSD_COMSPLIT == 1){ + _remapReg |= ((1 << 5)); + } else { + _remapReg |= ((0 << 5)); + } + setRegister_cont(CMD_CMDLOCK,SSD_COMMANDLOCK1); + setRegister_cont(CMD_CMDLOCK,SSD_COMMANDLOCK2); + writecommand_cont(CMD_DISPLAYOFF); + setRegister_cont(CMD_CLOCKDIV,SSD_CLOCKDIV); + setRegister_cont(CMD_MUXRATIO,SSD_MUXRATIO); + setRegister_cont(CMD_STARTLINE,SSD_STARTLINE); >>> + setRegister_cont(CMD_DISPLAYOFFSET,SSD_DISPLAYOFFSET); + setRegister_cont(CMD_SETGPIO,SSD_SETGPIO); + setRegister_cont(CMD_FUNCTIONSELECT,SSD_FUNCTIONSELECT); + writecommand_cont(CMD_SETVSL); + writedata8_cont(SSD_SETVSL_A);writedata8_cont(SSD_SETVSL_B);writedata8_cont(SSD_SETVSL_C); + writecommand_cont(CMD_CONTRASTABC); + writedata8_cont(SSD_CONTRAST_A);writedata8_cont(SSD_CONTRAST_B);writedata8_cont(SSD_CONTRAST_C); + setRegister_cont(CMD_MASTERCURRENT,SSD_MASTERCURRENT); >>> + writecommand_cont(CMD_DISPLAYENHANCE); >> + if (SSD_ENHANCE){ + writedata8_cont(0xA4); + } else { + writedata8_cont(0x00); + } + writedata8_cont(0x00); + writedata8_cont(0x00); + #if defined(SSD_GAMMASET) + //writecommand_cont(CMD_GRAYSCALE); for (uint8_t i =0;i<32;i++){writedata8_cont(SSD_GRAYTABLE[i]);} + #else + writecommand_cont(CMD_USELUT); + #endif + // phase here + setRegister_cont(CMD_PRECHARGE,SSD_PRECHARGE); >> + setRegister_cont(CMD_PRECHARGE2,SSD_PRECHARGE2); + setRegister_cont(CMD_VCOMH,SSD_VCOMH); + #endif + //setAddrWindow_cont(0,0,SSD_WIDTH-1,SSD_HEIGHT-1,false);// ??? + //_pushColors_cont(_defaultBgColor, SSD_CGRAM);//??? + //Normal Display and turn ON + writecommand_cont(CMD_NORMALDISPLAY); +*/ +static const uint8_t PROGMEM initList[] = { + SSD1351_CMD_COMMANDLOCK, 1, // Set command lock, 1 arg + 0x12, + SSD1351_CMD_COMMANDLOCK, 1, // Set command lock, 1 arg + 0xB1, + SSD1351_CMD_DISPLAYOFF, 0, // Display off, no args + SSD1351_CMD_CLOCKDIV, 1, + 0xF1, // 7:4 = Oscillator Freq, 3:0 = CLK Div Ratio (A[3:0]+1 = 1..16) + SSD1351_CMD_MUXRATIO, 1, + 127, + SSD1351_CMD_DISPLAYOFFSET, 1, + 0x0, + SSD1351_CMD_SETGPIO, 1, + 0x00, + SSD1351_CMD_FUNCTIONSELECT, 1, + 0x01, // internal (diode drop) + SSD1351_CMD_PRECHARGE, 1, + 0x32, + SSD1351_CMD_VCOMH, 1, + 0x05, + SSD1351_CMD_STARTLINE, 1, + 0x00, + SSD1351_CMD_NORMALDISPLAY, 0, + SSD1351_CMD_CONTRASTABC, 3, + 0xC8, 0x80, 0xC8, + SSD1351_CMD_CONTRASTMASTER, 1, + 0x0F, + SSD1351_CMD_SETVSL, 3, + 0xA0, 0xB5, 0x55, + SSD1351_CMD_PRECHARGE2, 1, + 0x01, + SSD1351_CMD_HORIZSCROLL, 1, + 0x00, + SSD1351_CMD_STOPSCROLL, 0, + SSD1351_CMD_DISPLAYON, 0, // Main screen turn on + 0 }; // END OF COMMAND LIST + + + void SSD1351::sendcommand(uint8_t commandByte, const uint8_t *dataBytes, uint8_t numDataBytes) { + writecommand(commandByte); + for (int i=0; i 0) { // '0' command ends list + x = pgm_read_byte(addr++); + numArgs = x & 0x7F; + if (cmd != 0xFF) { // '255' is ignored + sendcommand(cmd, addr, numArgs); + } + addr += numArgs; + } + delay(100); + setRotation(0); + stop(); +} + + +#define ssd1351_swap(a, b) (((a) ^= (b)), ((b) ^= (a)), ((a) ^= (b))) ///< No-temp-var swap operation + + + +void SSD1351::setAddrWindow_i(uint16_t x1, uint16_t y1, uint16_t w, uint16_t h) { + uint16_t x2 = x1 + w - 1, + y2 = y1 + h - 1; + if (rotation&1) { // Vertical address increment mode + ssd1351_swap(x1,y1); + ssd1351_swap(x2,y2); + } + writecommand(SSD1351_CMD_SETCOLUMN); // X range + writedata(x1); + writedata(x2); + writecommand(SSD1351_CMD_SETROW); // Y range + writedata(y1); + writedata(y2); + writecommand(SSD1351_CMD_WRITERAM); // Begin write +} + +void SSD1351::write16BitColor(uint16_t color){ + writedata(color>>8); + writedata(color&0xff); +} + +#define MADCTL_MY 0x80 +#define MADCTL_MX 0x40 +#define MADCTL_MV 0x20 +#define MADCTL_ML 0x10 +#define MADCTL_RGB 0x00 +#define MADCTL_BGR 0x08 +#define MADCTL_MH 0x04 + +void SSD1351::setRotation(uint8_t r) { + // madctl bits: + // 6,7 Color depth (01 = 64K) + // 5 Odd/even split COM (0: disable, 1: enable) + // 4 Scan direction (0: top-down, 1: bottom-up) + // 3 Reserved + // 2 Color remap (0: A->B->C, 1: C->B->A) + // 1 Column remap (0: 0-127, 1: 127-0) + // 0 Address increment (0: horizontal, 1: vertical) + uint8_t madctl = 0b01100100; // 64K, enable split, CBA + + rotation = r & 3; // Clip input to valid range + + switch(rotation) { + case 0: + madctl |= 0b00010000; // Scan bottom-up + _width = SSD1351_WIDTH; + _height = SSD1351_HEIGHT; + break; + case 1: + madctl |= 0b00010011; // Scan bottom-up, column remap 127-0, vertical + _width = SSD1351_HEIGHT; + _height = SSD1351_WIDTH; + break; + case 2: + madctl |= 0b00000010; // Column remap 127-0 + _width = SSD1351_WIDTH; + _height = SSD1351_HEIGHT; + break; + case 3: + madctl |= 0b00000001; // Vertical + _width = SSD1351_HEIGHT; + _height = SSD1351_WIDTH; + break; + } + + sendcommand(SSD1351_CMD_SETREMAP, &madctl, 1); + uint8_t startline = (rotation < 2) ? SSD1351_HEIGHT : 0; + sendcommand(SSD1351_CMD_STARTLINE, &startline, 1); + stop(); +} + +void SSD1351::invertDisplay(boolean i) { + writecommand(i ? SSD1351_CMD_INVERTDISPLAY : SSD1351_CMD_NORMALDISPLAY); + stop(); +} + +void SSD1351::drawPixel(int16_t x, int16_t y, uint16_t color) { + if((x < 0) ||(x >= _width) || (y < 0) || (y >= _height)) return; + setAddrWindow_i(x,y,1,1); + write16BitColor(color); + stop(); +} + +void SSD1351::setAddrWindow(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2) { + // uint16_t x2 = x1 + w - 1, + // y2 = y1 + h - 1; + uint8_t flag=0; + + if (!x1 && !y1 && !x2 && !y2) { + x1=0; + y1=0; + x2=_width; + y2=_height; + flag=1; + } + + if (x2>_width) x2=_width; + if (y2>_height) y2=_height; + + x2--; + y2--; + if (rotation&1) { // Vertical address increment mode + ssd1351_swap(x1,y1); + ssd1351_swap(x2,y2); + } + //Serial.printf("x1:%d x2:%d y1:%d y2:%d\n",x1,x2,y1,y2); + writecommand(SSD1351_CMD_SETCOLUMN); // X range + writedata(x1); + writedata(x2); + writecommand(SSD1351_CMD_SETROW); // Y range + writedata(y1); + writedata(y2); + writecommand(SSD1351_CMD_WRITERAM); // Begin write + if (flag) stop(); +} + +void SSD1351::pushColors(uint16_t *data, uint8_t len, boolean first) { + for (uint16_t b=0; b= _width) || (y >= _height)) return; + if ((y+h-1) >= _height) h = _height-y; + + setAddrWindow_i(x,y,1,h); + while (h--) { + write16BitColor(color); + } + stop(); +} + +void SSD1351::drawFastHLine(int16_t x,int16_t y,int16_t w,uint16_t color) { + // Rudimentary clipping + if ((x >= _width) || (y >= _height)) return; + if ((x+w-1) >= _width) w = _width-x; + + setAddrWindow_i(x,y,w,1); + while (w--) { + write16BitColor(color); + } + stop(); +} + +void ICACHE_RAM_ATTR SSD1351::fastSPIwrite(uint8_t d,uint8_t dc) { + + WRITE_PERI_REG( PIN_OUT_CLEAR, 1<<_cs); + WRITE_PERI_REG( PIN_OUT_CLEAR, 1<<_sclk); + if(dc) WRITE_PERI_REG( PIN_OUT_SET, 1<<_mosi); + else WRITE_PERI_REG( PIN_OUT_CLEAR, 1<<_mosi); + WRITE_PERI_REG( PIN_OUT_SET, 1<<_sclk); + + for(uint8_t bit = 0x80; bit; bit >>= 1) { + WRITE_PERI_REG( PIN_OUT_CLEAR, 1<<_sclk); + if(d&bit) WRITE_PERI_REG( PIN_OUT_SET, 1<<_mosi); + else WRITE_PERI_REG( PIN_OUT_CLEAR, 1<<_mosi); + WRITE_PERI_REG( PIN_OUT_SET, 1<<_sclk); + } + WRITE_PERI_REG( PIN_OUT_SET, 1<<_cs); +} diff --git a/lib/Adafruit_SSD1351-gemu-1.0/SSD1351.h b/lib/Adafruit_SSD1351-gemu-1.0/SSD1351.h new file mode 100644 index 000000000..ecfac6b17 --- /dev/null +++ b/lib/Adafruit_SSD1351-gemu-1.0/SSD1351.h @@ -0,0 +1,129 @@ +/*************************************************** + This is our library for the Adafruit SSD1351 Breakout and Shield + ----> http://www.adafruit.com/products/1651 + + Check out the links above for our tutorials and wiring diagrams + These displays use SPI to communicate, 3 pins are required to + interface + Adafruit invests time and resources providing this open source code, + please support Adafruit and open-source hardware by purchasing + products from Adafruit! + + Written by Limor Fried/Ladyada for Adafruit Industries. + MIT license, all text above must be included in any redistribution + ****************************************************/ + +#ifndef _SSD1351_ +#define _SSD1351_ + +#if ARDUINO >= 100 + #include "Arduino.h" + #include "Print.h" +#else + #include "WProgram.h" +#endif + +#include +#include + + + +#define SSD1351_WIDTH 128 +#define SSD1351_HEIGHT 128 + + +// Color definitions +#define SSD1351_BLACK 0x0000 /* 0, 0, 0 */ +#define SSD1351_NAVY 0x000F /* 0, 0, 128 */ +#define SSD1351_DARKGREEN 0x03E0 /* 0, 128, 0 */ +#define SSD1351_DARKCYAN 0x03EF /* 0, 128, 128 */ +#define SSD1351_MAROON 0x7800 /* 128, 0, 0 */ +#define SSD1351_PURPLE 0x780F /* 128, 0, 128 */ +#define SSD1351_OLIVE 0x7BE0 /* 128, 128, 0 */ +#define SSD1351_LIGHTGREY 0xC618 /* 192, 192, 192 */ +#define SSD1351_DARKGREY 0x7BEF /* 128, 128, 128 */ +#define SSD1351_BLUE 0x001F /* 0, 0, 255 */ +#define SSD1351_GREEN 0x07E0 /* 0, 255, 0 */ +#define SSD1351_CYAN 0x07FF /* 0, 255, 255 */ +#define SSD1351_RED 0xF800 /* 255, 0, 0 */ +#define SSD1351_MAGENTA 0xF81F /* 255, 0, 255 */ +#define SSD1351_YELLOW 0xFFE0 /* 255, 255, 0 */ +#define SSD1351_WHITE 0xFFFF /* 255, 255, 255 */ +#define SSD1351_ORANGE 0xFD20 /* 255, 165, 0 */ +#define SSD1351_GREENYELLOW 0xAFE5 /* 173, 255, 47 */ +#define SSD1351_PINK 0xF81F + +#define SSD1351_CMD_SETCOLUMN 0x15 +#define SSD1351_CMD_SETROW 0x75 +#define SSD1351_CMD_WRITERAM 0x5C +#define SSD1351_CMD_READRAM 0x5D +#define SSD1351_CMD_SETREMAP 0xA0 +#define SSD1351_CMD_STARTLINE 0xA1 +#define SSD1351_CMD_DISPLAYOFFSET 0xA2 +#define SSD1351_CMD_DISPLAYALLOFF 0xA4 +#define SSD1351_CMD_DISPLAYALLON 0xA5 +#define SSD1351_CMD_NORMALDISPLAY 0xA6 +#define SSD1351_CMD_INVERTDISPLAY 0xA7 +#define SSD1351_CMD_FUNCTIONSELECT 0xAB +#define SSD1351_CMD_DISPLAYOFF 0xAE +#define SSD1351_CMD_DISPLAYON 0xAF +#define SSD1351_CMD_PRECHARGE 0xB1 +#define SSD1351_CMD_DISPLAYENHANCE 0xB2 +#define SSD1351_CMD_CLOCKDIV 0xB3 +#define SSD1351_CMD_SETVSL 0xB4 +#define SSD1351_CMD_SETGPIO 0xB5 +#define SSD1351_CMD_PRECHARGE2 0xB6 +#define SSD1351_CMD_SETGRAY 0xB8 +#define SSD1351_CMD_USELUT 0xB9 +#define SSD1351_CMD_PRECHARGELEVEL 0xBB +#define SSD1351_CMD_VCOMH 0xBE +#define SSD1351_CMD_CONTRASTABC 0xC1 +#define SSD1351_CMD_CONTRASTMASTER 0xC7 +#define SSD1351_CMD_MUXRATIO 0xCA +#define SSD1351_CMD_COMMANDLOCK 0xFD +#define SSD1351_CMD_HORIZSCROLL 0x96 +#define SSD1351_CMD_STOPSCROLL 0x9E +#define SSD1351_CMD_STARTSCROLL 0x9F + + +#define PIN_OUT_SET 0x60000304 +#define PIN_OUT_CLEAR 0x60000308 + +class SSD1351 : public Renderer { + + public: + + SSD1351(int8_t cs,int8_t mosi,int8_t sclk); + + void begin(void); + void DisplayInit(int8_t p,int8_t size,int8_t rot,int8_t font); + void setAddrWindow_i(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1); + void setAddrWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1); + void pushColors(uint16_t *data, uint8_t len, boolean first); + void drawPixel(int16_t x, int16_t y, uint16_t color); + void write16BitColor(uint16_t color); + void setRotation(uint8_t r); + void invertDisplay(boolean i); + uint16_t GetColorFromIndex(uint8_t index); + void DisplayOnff(int8_t on); + void writecommand(uint8_t c); + void writedata(uint8_t d); + void commandList(uint8_t *addr); + void hw_spi_init(); + void sendcommand(uint8_t commandByte, const uint8_t *dataBytes, uint8_t numDataBytes); + void sendcommand(uint8_t commandByte,uint8_t *dataBytes, uint8_t numDataBytes); + void drawFastVLine(int16_t x,int16_t y,int16_t h,uint16_t color); + void drawFastHLine(int16_t x,int16_t y,int16_t w,uint16_t color); + void spi_lcd_mode_init(void); + void dim(uint8_t contrast); + + private: + uint8_t tabcolor; + void fastSPIwrite(uint8_t d,uint8_t dc); + void start(void); + void stop(void); + int8_t _cs, _mosi, _sclk, _hwspi; + +}; + +#endif diff --git a/lib/Adafruit_SSD1351-gemu-1.0/Tiger.c b/lib/Adafruit_SSD1351-gemu-1.0/Tiger.c new file mode 100644 index 000000000..83e0ba9bf --- /dev/null +++ b/lib/Adafruit_SSD1351-gemu-1.0/Tiger.c @@ -0,0 +1,42 @@ +#include +// picture with 51 x 34 pixels +// table size 3468 bytes +#if 0 +const uint16_t picture[] = { +0x0000,0x0841,0x0841,0x0840,0x0841,0x0841,0x1041,0x1041,0x1041,0x1041,0x1041,0x1880,0x1041,0x1041,0x1040,0x1041,0x1881,0x1880,0x1880,0x1880,0x2081,0x2081,0x2080,0x20c1,0x3942,0x3942,0x3101,0x2080,0x2881,0x2880,0x2881,0x2881,0x2881,0x2880,0x2881,0x30c1,0x30c1,0x1840,0x2881,0x30c1,0x3901,0x3941,0x3901,0x3101,0x3101,0x3101,0x3101,0x3101,0x4182,0x4182,0x0000, +0x0000,0x0841,0x0841,0x0841,0x1881,0x1881,0x1881,0x1881,0x1881,0x1881,0x2081,0x28c1,0x2081,0x2080,0x2081,0x2081,0x28c1,0x28c1,0x2901,0x3101,0x3101,0x3101,0x3101,0x3942,0x5a04,0x6244,0x6244,0x51c2,0x4101,0x4101,0x4101,0x4101,0x3901,0x4101,0x4101,0x5181,0x4941,0x59c2,0x4141,0x30c1,0x4941,0x4982,0x38c1,0x5182,0x4141,0x4101,0x4941,0x5182,0x5182,0x6a43,0x0000, +0x0000,0x1041,0x0841,0x1041,0x1881,0x20c1,0x2081,0x20c1,0x2081,0x28c1,0x20c1,0x2901,0x2081,0x2081,0x20c1,0x28c1,0x28c1,0x3101,0x3101,0x3101,0x3101,0x3941,0x3942,0x4183,0x5a45,0x6244,0x6a85,0x6a84,0x5a03,0x4141,0x4101,0x4101,0x4101,0x4101,0x4101,0x4101,0x4101,0x4941,0x4141,0x59c2,0x2880,0x38c1,0x4941,0x4941,0x4141,0x4941,0x5181,0x5182,0x6203,0x6203,0x0000, +0x0000,0x20c1,0x1041,0x1881,0x2901,0x3142,0x20c1,0x28c1,0x2901,0x2901,0x2901,0x2901,0x2901,0x2901,0x3101,0x3101,0x3141,0x3942,0x4182,0x4182,0x49c3,0x49c3,0x5204,0x5a45,0x5a45,0x5a45,0x6245,0x6245,0x5a45,0x5a04,0x51c2,0x4141,0x4101,0x4101,0x4101,0x6244,0x8388,0x5a04,0x4101,0x4101,0x4901,0x5182,0x30c1,0x4982,0x4982,0x5182,0x4941,0x4941,0x5181,0x4141,0x0000, +0x0000,0x1881,0x1041,0x1881,0x2101,0x2902,0x28c1,0x2901,0x2901,0x2901,0x2901,0x28c1,0x20c1,0x20c1,0x28c1,0x28c1,0x28c1,0x3942,0x4182,0x4141,0x4182,0x4982,0x4982,0x51c3,0x51c3,0x51c3,0x51c3,0x51c3,0x51c3,0x51c3,0x5a04,0x51c3,0x4982,0x4141,0x9c09,0xd612,0xbd50,0xa48c,0x4100,0x4101,0x4101,0x4101,0x59c2,0x4981,0x3901,0x3901,0x3901,0x4941,0x4101,0x5182,0x0000, +0x0000,0x1881,0x1041,0x1881,0x2902,0x2902,0x2902,0x3142,0x3102,0x3102,0x2901,0x28c1,0x28c1,0x28c1,0x28c1,0x28c1,0x3942,0x51c3,0x6a84,0x3941,0x4182,0x4982,0x4982,0x5a03,0x5a03,0x6244,0x5a04,0x51c3,0x51c3,0x5a03,0x5a44,0x5204,0x3942,0x9bc8,0xcd8f,0xc590,0xc5d1,0xcdd2,0x4100,0x4101,0x4101,0x4101,0x4941,0x4941,0x5182,0x3101,0x4941,0x4941,0x4941,0x4941,0x0000, +0x0000,0x1881,0x1041,0x1881,0x3142,0x3142,0x3102,0x3142,0x3142,0x3102,0x3101,0x28c1,0x28c1,0x28c1,0x28c1,0x5204,0x8bc9,0xbd4f,0xcdd1,0xcdd0,0x6244,0x4982,0x51c2,0x5a45,0xac4a,0x9387,0x4982,0x8b87,0x9387,0x8346,0x6a86,0x5204,0xac89,0xaccc,0xc54f,0xb4ce,0x9c4b,0xc5d1,0x4100,0x4901,0x4101,0x4941,0x4941,0x4101,0x4142,0x6203,0x4941,0x2081,0x38c1,0x5182,0x0000, +0x0000,0x1881,0x1041,0x2081,0x3143,0x3142,0x3142,0x3983,0x3943,0x3102,0x3101,0x28c1,0x28c1,0x28c1,0x2901,0x8b89,0x9c0b,0x9c0a,0xd612,0xac8c,0xd5d1,0x9bc8,0xbc8a,0x9bc7,0xa3c7,0x9bc7,0x9b86,0x9bc7,0x8347,0x93c8,0xac8b,0xc50c,0xcd4b,0xb48a,0xde12,0xde94,0xaccd,0x6ac6,0x4101,0x4101,0x4941,0x4901,0x4101,0x4941,0x4101,0x38c1,0x3901,0x4141,0x2081,0x3901,0x0000, +0x0000,0x1881,0x1041,0x20c1,0x3984,0x3143,0x3943,0x41c4,0x3983,0x3142,0x3101,0x3101,0x3101,0x3101,0x3101,0x9c0a,0x7ac5,0x6203,0xa48c,0xc54f,0xa44b,0xbd0c,0xac08,0x8306,0xc54c,0x72c5,0x7b06,0x6a85,0xddcd,0xcd4c,0x93c7,0xcd0b,0x9386,0xdd8c,0xe60f,0xac8b,0x8389,0x6244,0x6203,0x5181,0x4101,0x4101,0x4901,0x4901,0x4101,0x3901,0x3081,0x38c1,0x59c2,0x4141,0x0000, +0x0000,0x2081,0x1841,0x28c1,0x41c4,0x3142,0x3983,0x49c5,0x4184,0x3102,0x3101,0x3101,0x3101,0x3101,0x3101,0x8348,0xa44b,0x6203,0x6204,0xb50e,0xbd0d,0xe60f,0xe650,0xee90,0x8346,0xb48b,0xb48b,0x7b07,0x72c6,0xd54c,0xbd0b,0xf758,0xffd9,0xb489,0xac07,0xe60e,0xee92,0x5204,0x51c3,0x5181,0x4941,0x4941,0x4101,0x4101,0x4941,0x4101,0x4101,0x3081,0x30c1,0x6a43,0x0000, +0x0000,0x1881,0x1841,0x28c1,0x41c4,0x3983,0x3983,0x41c4,0x3983,0x3102,0x3101,0x3101,0x3101,0x3101,0x3101,0x3941,0x6245,0x7285,0x9c4b,0xa44b,0xee90,0xdd8c,0x8b05,0xde10,0x940b,0xd58e,0xddcd,0xcd0b,0xdd8c,0x8b47,0x9b87,0xbd90,0xd654,0xffda,0xb48b,0xa3c7,0x9c08,0x6285,0x49c3,0x4983,0x4942,0x4941,0x5181,0x4101,0x4101,0x4941,0x4101,0x38c1,0x30c1,0x4141,0x0000, +0x0000,0x1881,0x1881,0x28c1,0x41c4,0x3983,0x41c4,0x4183,0x3942,0x3101,0x3101,0x3101,0x3101,0x3901,0x3901,0x3901,0x4142,0x8bc9,0x6286,0x8b87,0xbc89,0x9345,0xc50b,0xffd9,0xce13,0xe716,0xcd4b,0x7ac5,0xc4ca,0x7284,0x9bc8,0xcdd0,0xde53,0xbd91,0xce13,0xbd0d,0xe60f,0xb4cc,0x6ac6,0x51c4,0x49c3,0x4982,0x5182,0x5181,0x4941,0x4101,0x4101,0x4101,0x4101,0x4101,0x0000, +0x0000,0x1881,0x1881,0x2901,0x41c4,0x3983,0x41c4,0x4183,0x3942,0x3101,0x3101,0x3901,0x3901,0x3101,0x3101,0x6244,0x8b05,0xa409,0xd58d,0xb448,0x82c4,0xbcca,0xee93,0xf757,0xb50f,0xbd4f,0x9c4b,0xb489,0x8b46,0xcd0a,0x7ac4,0x9bc8,0xff98,0xb50f,0xacce,0x940a,0xb4cc,0xac8b,0xeed4,0x51c4,0x49c3,0x49c3,0x4983,0x51c3,0x4941,0x4101,0x4101,0x3901,0x4101,0x38c1,0x0000, +0x0000,0x2081,0x2081,0x2901,0x4184,0x3983,0x4183,0x3942,0x3142,0x3101,0x3101,0x3101,0x3941,0x3101,0x7ac5,0x7ac5,0x6a43,0x6a43,0x6a43,0xbcca,0xe5cd,0x4182,0x9c4b,0x4183,0x8bca,0xa44c,0xb4cd,0xb447,0xb448,0xccc9,0xccca,0xc4c9,0xee94,0x6245,0x838a,0x8b46,0xd5cf,0xde52,0xef15,0x51c3,0x49c3,0x49c3,0x49c3,0x5a45,0x4982,0x4941,0x5141,0x4941,0x3901,0x4101,0x0000, +0x0000,0x2081,0x2080,0x3102,0x3983,0x4184,0x3942,0x3142,0x3942,0x3942,0x3901,0x3941,0x3942,0x7ac5,0x7ac4,0x82c4,0x59c2,0xa3c8,0x5a45,0xd5cd,0xe60f,0xbccb,0x9c09,0xc54e,0x8389,0x93c8,0x9b87,0x69c2,0x8ac4,0xbc89,0xcd0a,0xbc88,0x8bca,0x8b86,0xbd92,0x8305,0xddcf,0xb50e,0xacce,0x5a45,0x51c3,0x49c3,0x49c3,0x5a46,0x4982,0x4982,0x4941,0x4101,0x30c1,0x30c1,0x0000, +0x0000,0x2081,0x2081,0x3142,0x49c4,0x4a05,0x4184,0x4183,0x4183,0x49c3,0x4183,0x7ac6,0x9bc8,0x9b86,0xa386,0x6a44,0x3901,0xb48b,0x4182,0xd54c,0x3101,0xc4ca,0x6244,0x8348,0x61c2,0x6202,0x5a04,0x7a84,0x69c2,0xac07,0xcd0a,0xb448,0x7307,0xf79a,0x7284,0xbc89,0xaccd,0x940b,0x6245,0x51c3,0x51c3,0x49c3,0x49c3,0x6246,0x4982,0x4982,0x4982,0x4141,0x3081,0x2881,0x0000, +0x0000,0x28c1,0x28c1,0x4184,0x5206,0x4a05,0x49c4,0x49c4,0x49c4,0x5205,0x9b87,0x7b06,0x9386,0xa3c6,0x51c2,0x61c2,0x5981,0xe652,0x5204,0xa449,0x3901,0xa3c7,0xac07,0x9305,0x8348,0xb4ce,0x28c1,0x61c2,0x7243,0x9b45,0xcd0a,0xbc89,0x8b05,0xcd8e,0xcd4b,0x8347,0xffd9,0xd654,0x6a87,0x6286,0x6286,0x5a46,0x5a46,0x6287,0x5a45,0x5204,0x5204,0x5204,0x4182,0x2081,0x0000, +0x0000,0x28c1,0x28c1,0x3983,0x41c4,0x41c4,0x4184,0x4184,0x7285,0x82c5,0xa387,0x49c3,0xa3c7,0x9b86,0x4182,0x6a02,0x61c2,0x8b88,0x4142,0x72c5,0x6a84,0xa3c7,0x4982,0x3101,0xa3c6,0xbc8a,0x51c3,0x4941,0x7202,0xac07,0xbc89,0xcd0a,0xccca,0x9b87,0xcd0b,0x8b89,0x7b8a,0xa48e,0x5204,0x5204,0x49c4,0x49c3,0x49c3,0x49c4,0x49c3,0x4983,0x4183,0x4182,0x4182,0x3982,0x0000, +0x0000,0x2081,0x28c1,0x3983,0x41c4,0x4184,0x4184,0x5a03,0x9b86,0x9b86,0x7ac5,0x6244,0x9346,0x9b46,0x4141,0x6a02,0x69c2,0x8305,0x7285,0x3941,0xa449,0xac08,0xcd0c,0x9b86,0x7ac5,0xac08,0x9388,0x6202,0x7a83,0xac09,0xa408,0xcd0b,0xc50b,0xa3c6,0xbcca,0xff99,0x940c,0xaccf,0x49c3,0x49c3,0x49c3,0x4183,0x4183,0x4183,0x4183,0x4182,0x3942,0x3942,0x3942,0x4183,0x0000, +0x0000,0x2081,0x28c1,0x3943,0x41c4,0x4184,0x4183,0x9386,0x9b87,0xa408,0x9346,0x6a44,0x8b05,0x8284,0x5982,0x7a43,0x5181,0x7a43,0x9c09,0x72c6,0xbd0c,0x5a03,0x93c8,0x6a45,0x8b05,0x9b86,0x7a84,0x7243,0x7243,0x9b86,0xb489,0xbccb,0xbccb,0xb447,0xbc8a,0x6ac7,0xce13,0xc5d2,0x49c3,0x49c3,0x4983,0x4183,0x4183,0x4182,0x4182,0x4182,0x3942,0x3942,0x3942,0x4183,0x0000, +0x0000,0x2080,0x28c1,0x3942,0x3984,0x7b06,0x7285,0x7ac5,0x9b87,0x9b86,0x9345,0x6203,0x8284,0x7a83,0x5982,0x7202,0x7203,0x7243,0x9387,0x7b07,0x8b88,0x8347,0x2080,0xb4cb,0xbccc,0x5a03,0x6a03,0x8b04,0x61c2,0x8b05,0xb48b,0xbccb,0xcd4c,0xac09,0xcd0b,0xc590,0x9c8d,0x7b89,0x4983,0x4983,0x4983,0x4183,0x4182,0x4182,0x4182,0x4182,0x3942,0x3942,0x3941,0x4183,0x0000, +0x0000,0x2081,0x28c1,0x3942,0x8b46,0x8b05,0x8b05,0x3101,0x9b86,0x9345,0x8ac4,0x82c4,0x7a43,0x7a83,0x6a02,0x8283,0x8283,0x61c2,0x9b86,0x8b88,0x5204,0xb48b,0x51c3,0xac8a,0x3942,0x6a03,0x7243,0x9345,0x7284,0x8305,0xbccc,0xc54d,0xcd8d,0xc58e,0xcd4c,0xac8c,0xcdd2,0x7307,0x4183,0x4983,0x4982,0x4182,0x4182,0x4182,0x4182,0x3982,0x3942,0x3942,0x3101,0x4183,0x0000, +0x0000,0x2081,0x5a03,0x59c2,0x4141,0x8283,0x82c4,0x4982,0x8b05,0x8b05,0x8284,0x82c4,0x8283,0x8283,0x7a43,0x8283,0x7a02,0x8ac3,0x9346,0x7285,0x93c9,0x5a44,0xa44a,0xb4cb,0x9388,0x7284,0x7a84,0x7243,0x4182,0x7ac5,0xa44a,0x9c4b,0xcd4e,0xbd0e,0xb4cb,0xde53,0xa4ce,0x7308,0x8bca,0x49c3,0x4182,0x4182,0x4182,0x4182,0x4142,0x3942,0x3942,0x3942,0x3101,0x4183,0x0000, +0x0000,0x7243,0x7a43,0x8283,0x3101,0x59c2,0x7a83,0x7a83,0x4141,0x9345,0x8b05,0x7202,0x7203,0x7a43,0x7a43,0x69c2,0x61c2,0x7a02,0x82c4,0x9387,0xa44b,0x3101,0x4182,0x6a86,0x8b88,0xbd4e,0x6244,0x4981,0xac8b,0x4142,0x6203,0xb3c8,0x8307,0xffd9,0xac8b,0x8389,0xa48d,0xa4cd,0x8bca,0x7b49,0x49c3,0x4182,0x4182,0x3982,0x4182,0x3942,0x3942,0x3141,0x3101,0x4183,0x0000, +0x0000,0x6a02,0x7a43,0x7a83,0x8283,0x3901,0x7243,0x7a43,0x7a43,0x4982,0x8283,0x7a43,0x7243,0x5981,0x7202,0x7202,0x69c2,0x5981,0x7a42,0x9bc7,0x51c3,0x93c8,0xa449,0x93c9,0x7b48,0x8347,0x4142,0x6a84,0x8346,0xbccc,0x8b88,0x5182,0xde53,0xcdd1,0xd613,0x8bca,0xb50f,0x8bca,0x8bca,0x8389,0x6286,0x5204,0x4a04,0x49c3,0x4183,0x4183,0x4183,0x4182,0x3982,0x4a04,0x0000, +0x0000,0x8283,0x61c2,0x59c2,0x7202,0x8283,0x4941,0x7a83,0x7243,0x7243,0x7a83,0x7a43,0x7a43,0x7242,0x6182,0x6181,0x7202,0x61c2,0x5981,0x7a43,0x6a43,0x8347,0x8346,0xb48b,0xb4cb,0x9c09,0x5a04,0x7b07,0x93c9,0x9c0a,0x9c0a,0x5a44,0xb4cd,0xa44d,0x8389,0xb50e,0xa44c,0x8b89,0x8b89,0x93ca,0x8388,0x8348,0x8348,0x7b48,0x7b07,0x7307,0x7307,0x72c7,0x6ac7,0x7307,0x0000, +0x0000,0x8ac4,0x8283,0x7a83,0x61c2,0x7a43,0x7a43,0x4942,0x8ac4,0x6a02,0x4982,0x7a83,0x7202,0x7243,0x7a43,0x6a02,0x5941,0x69c2,0x5981,0x5981,0x9386,0x8b46,0x7284,0x5a03,0x51c3,0x8b88,0x6244,0x4183,0x4142,0x7b07,0x3941,0x5a44,0x3982,0x7b07,0x9c0b,0x8348,0x8bc9,0x8b89,0x8388,0x8388,0x8348,0x8348,0x8348,0x8348,0x7b47,0x7b07,0x7b07,0x7307,0x72c6,0x72c7,0x0000, +0x0000,0x8ac4,0x8b04,0x82c3,0x82c4,0x82c4,0x8283,0x82c4,0x4982,0x8ac4,0x6a02,0x5182,0x7243,0x7202,0x7202,0x7202,0x6182,0x5141,0x6182,0x69c2,0x4941,0x7284,0x8b46,0x9bc8,0x8b46,0x93c8,0x49c3,0x3101,0x5a03,0x8306,0x72c7,0xb50e,0xacce,0xb50e,0x8348,0x8348,0x8b89,0x8348,0x8348,0x8348,0x8348,0x8348,0x8347,0x7b07,0x7b07,0x7b07,0x7307,0x72c7,0x72c6,0x72c6,0x0000, +0x0000,0x8b04,0x8ac4,0x9305,0x8ac4,0x8ac4,0x9304,0x8ac4,0x8b04,0x59c2,0x6a03,0x7a43,0x6a03,0x6a02,0x7203,0x69c2,0x69c2,0x6182,0x5141,0x5941,0x69c2,0x5141,0x6203,0x7243,0x7ac5,0x7ac5,0x8346,0xa44a,0xb50d,0xbd0e,0xcd8f,0xd613,0xbd50,0x8bca,0x72c7,0x72c6,0x72c6,0x72c6,0x72c6,0x72c6,0x7307,0x7b07,0x7b07,0x7b07,0x7b07,0x7307,0x72c6,0x72c6,0x72c6,0x72c6,0x0000, +0x0000,0x9345,0x9304,0x9345,0x9345,0x9345,0x9305,0x9345,0x9305,0x8ac4,0x7283,0x5182,0x6202,0x7243,0x6a02,0x69c2,0x61c2,0x5981,0x5982,0x5141,0x4101,0x5101,0x5982,0x5141,0x5141,0x61c2,0x8284,0x9386,0xa44a,0xde53,0xad0f,0xef16,0xe6d6,0x6245,0x4982,0x4982,0x4982,0x4982,0x49c2,0x49c2,0x49c3,0x49c3,0x51c3,0x51c3,0x51c3,0x5203,0x5203,0x5203,0x5203,0x5203,0x0000, +0x0000,0x7a84,0x51c2,0x9b86,0x9b85,0x9304,0x8b04,0x8b04,0x8b04,0x9345,0x7a83,0x7a83,0x7a84,0x30c0,0x7243,0x61c2,0x6182,0x5981,0x5981,0x5141,0x5141,0x38c1,0x2881,0x38c1,0x5181,0x6a02,0x8b04,0xa407,0xcd4c,0xde52,0xef16,0xef57,0xd695,0x6285,0x5a04,0x51c3,0x49c3,0x4982,0x4182,0x3942,0x3941,0x3101,0x3101,0x28c1,0x28c1,0x20c1,0x2081,0x2081,0x2081,0x2081,0x0000, +0x0000,0xa407,0x9b86,0x3901,0x9b86,0xa386,0x9345,0x82c4,0x8b04,0x82c4,0x9304,0x7243,0x59c2,0x7243,0x28c1,0x69c2,0x5981,0x5981,0x4941,0x4901,0x4901,0x5141,0x5141,0x5141,0x4101,0x4941,0x5182,0x5182,0x4182,0xcd8f,0xef16,0xef57,0xa4cf,0x7b07,0x7b07,0x7b07,0x7ac6,0x72c6,0x72c6,0x6a85,0x6a85,0x6245,0x6244,0x5a04,0x5203,0x49c3,0x4182,0x3942,0x3941,0x3101,0x0000, +0x0000,0xac48,0xa407,0xac08,0x30c1,0x8b45,0xa3c7,0x9345,0x8b04,0x8ac4,0x82c4,0x82c4,0x7a83,0x6202,0x6a02,0x30c1,0x59c2,0x61c2,0x5141,0x4901,0x4101,0x4101,0x4101,0x4901,0x5141,0x6182,0x7202,0x8ac4,0xa407,0xac8a,0xa48d,0xef57,0xb550,0x7b06,0x7306,0x72c6,0x72c6,0x72c6,0x72c6,0x72c6,0x72c6,0x72c6,0x72c6,0x72c6,0x72c6,0x6ac6,0x6ac6,0x6a85,0x6a85,0x6a85,0x0000, +0x0000,0x0000,0x1081,0x1081,0x1081,0x0000,0x1041,0x1041,0x0841,0x0840,0x0841,0x0840,0x0841,0x0000,0x0840,0x0840,0x0000,0x0800,0x0840,0x0800,0x0000,0x0000,0x0000,0x0000,0x0000,0x0800,0x0840,0x0840,0x1041,0x1082,0x1082,0x18c3,0x1081,0x0841,0x0841,0x0841,0x0841,0x0841,0x0840,0x0840,0x0841,0x0841,0x0840,0x0841,0x0840,0x0841,0x0840,0x0840,0x0840,0x0840,0x0000, +0x0000 +}; +#endif diff --git a/lib/Adafruit_SSD1351-gemu-1.0/Tiger.rgb b/lib/Adafruit_SSD1351-gemu-1.0/Tiger.rgb new file mode 100644 index 0000000000000000000000000000000000000000..e3c49ce828c716c4b161c3e43f6d6340ef1e550f GIT binary patch literal 21764 zcmeHvOH>-^+HMf>9%GWeWa!YI>8zPqAZU4s!F2Dm%)%LkfAr%FCR&AfDNSNy@4x>A zGnqAehBH`8k-}@4PI}|NyZ6#rJ2bO6^gPe^RUstV`>ZoP>*QNNRbiCpeV_OGy~X^U z@9+Hoe|PRE9iOZ|#a;_|<%7>ykDRsJ?;I^WQk<*2*1y}oJAj2(9P_AYxxR6Ivpe7# z(!CY6qVB1Cp*>YoUEbYqcL(kDEsnqC6_>YuT^#e=rT7$|>Qi0H!T^3h{!a_9;F>0u zJFJEFLe80{DvE}mR#Yt{k9jl(;CKS5sxxH-5tt6Aa^NEi>QHHE}#f4gcNllqygp5;QYYuw~D}5 z4wYv$Ty+7l7Qz`3hZZ0KT*3d4Kc$7-5iTt>5^2ES*CYd5&(OJ}S;*t|Sm1|-fmUEA zYNF?3w?XIB^~cENxzD?&w$6+IJ2f#9X^r61LZ?_wXeH)G{LnivDnbI~=jBzWoE7bvC99nIVNZ?N{yU?0M#!qRO0 z3;fY!bTLX9xWi>|+Sd8!WFV}7JeEHLe6tH+efhZ-+8v-JxV*4+{7*i>8tC1nNVzNk zeR)<<%d^Rt8BfNNv44TTZ zQQ(*8<@5j14jfj%hIR)fdZNV=+};@&el$No%w)QTxN^r!>i(N+bV; z{$clm`;W+FKXeG~lGlRUbqNRt@Qj*ol22Ul@hQq!QuBKu;+L_H@sEp$`7E)^ zYppZ341_(`F?vTL`{}OiT5dAiwYjX{%7>Bjj#q(=Betmv11@-dpUNmmOCTHM&(Z@h zKEf{y-s)eG(Vlrw-TDRl8}Li4jvR0XDJ|6BcO+r|6eR%GGc$3xFY^;VBj>?#&(xQl z1uz?Mi>yG(pe_NaH^nv7mN+i_YCXMpU9$3Pxn(^SrDUAQN&iQD{wVMb-GQ$aRuB^L zQ^W!33&{jgW??(wJLmGS>@k0xE2pIr5 zS6QH!GWgfetzi#1K?y*cSim?G)K3a;t%qkD=45d&KI z4kO~XloYuj&UvIX;vXh{F5&wHexQ_#vcST5%Me!+{ss@}aKQCVBYwb_JZQr22DVSu z?B1+%tu-8=Rm?Cu)?n#7{mE!g>F)OZGhjELlwOEa`^Km^puN(w)xdHqzNA%h|tP?utHn= z%u2-~LhxLyy;=7RFpC9=0KL3U{v$pL{FK1_qmk%wp|^DZY`WCD{nmPGZkYE<4EyuB{3B@zKrbzcJ_WYXp#M64q5pxFZ+z6Wt#_j5qVA>k{}p~R194uV z-|rTq%9FN==G&XBzpUu2K@7ng2t3-^Mk{SvRh@1U0w079{v zwJy(GLy8xEJTjUX(BZ*d zueEg(xxLgsc!3BAWFz6#{890!GL|pTG7qZ6MwkU72R35|=}{vUMK%Cd_`Kbvj$BWv zGyBSXjNf?=Hs8CwY|#4OSu47u^(Oo_Cw9Nx8Ds=Nb%hK7nL$6$4elUHAoh9pn)u(S z|CVq34fyS2uOIo4w1I!YfI-*Lk#_OT;%{5e zPi9Z^r}+(Q!)y%`f8DB8m#}Z=ig*uxhdKu@WRCm@eFIbq=pi)te^zqR1I|B^xvwJu zFaepM4)o3V$#~?Tb?;A^?5UHv)52s9pAZtD_SbT434Cs7E_AtC&_aR8sNQWJR@4H$ zbI>>}t`rYX#)?5>r8r*jM>J%2jNFRXA6ZHt7gr0ddV3B(RT$4bHlJSp{pzdh&C9oz z1^lP^?lrA$`%(sAfpZ0tkjEOLfUFMTiPhb~dF1)?{fvnp^Pe$r;gL&dpZssZ!;eml z?oA2_Fs~K<&-MWh59F(B$UW1QUC&SCs_|+(sK2!ym!4kEoUP?g^1oWItiFrWnp)_x zjw-Fje&(nG?iUv0W=sqDV~d&g{0j8EUYow^y?p!O;mvO!`p$Z`-_+Kv-b?FG>%80S zF(1S4eRom2V)k>IKQ>y!%xmiwQ0IZbZ8Jf^F+=oP^SBK{#BKAP`u8EVAu=%4s? zVzw=S_$x|9ksSCOeh0m4s2ZKf9j2F!WXvC}#=En~)8M5ia%=hV?61~T2@*ID7(iN7 z3+2!b>XeS$`uSRIrNEfDAA`?>WylD4Qat^0t=4UJT)?sK^WY8}Zxz57`fEAL<*NhyHO4 z^c5v3B=GO?k0vH^UD;|JwLCm-n?9PDHlJQhn%Im)T{e(yjVL&8YQWksDLyIP7bBdrz&> zUqS+u0XQyVzx_<++meu1h<_d(5?X?ywnQK?b$8$vzp#IL0N`(-#sz-*JHODr3H*?N z(un_KOHjOKv@<)ATQ-Qk8sD3pE;%fH@yalKv}%a0XCi-(n?;yR?$d%12M7hhS6k&1{U zi}+hM2W}C64nKQ~;J^ZVWuh0-=>H{t_5f`B3K`(wHx(1TTG~J}ekp-lIkbkmfWIp{ zW-#Yy4e!ST(e?c1C1aog?NhfKSNAVoTJJu*{h%mj>>gM>Wp!llU3F%Ih<1Ui$oKTKaexw?08|DrvC91~fztnnE2ops9sUf~&H z9-eoa#`nMT?=&v;Q z|78Ei(Z9e?Z43Jcerf-TzmESFbMw(ic*ST>PzJ=`o~TBvu`&HH{nC1J_4-5ayh3cJuLcz&!a~sfhBVSylTj~ zA%VFk$be9-AamKzOj;`iWIC1V)>t86oSy&V=1Ga{KF#li+UE|^?#OCkDP0bwGHRjQ zIz1nWdh`kFP3`xa-*0M{|1E6bkDKZ3k;uje>-?zTQ`|Ghg?ksv25cZC_K^e&m}AO5 zx2*N*43Lpf_yBf(pnv%CTmLVZFSPIYKN;~Q6BzqsPp?7y4q_)4vSpyK;vLeJiQMZ_ z-*z>+Gk8>-zOw!tD^#>SEtU4@_b&bdR75|OT}?dMK179ddfuidqvgJguoAU!`M;9zZht;@j&3AEs$p!20GE5>?GS4P6j zhbnR4Hd>;G$oad?ACbfUEF_SMQVI{xI&-~OF6F4uZFX7vTWjZjRD(YC^v|XA7r_1d z&9BJ%HZOeMRsE-n?oBJ-T?#=4{_sTZi)$lK{I@a?5`Yxseuwad)C{FyKX}Ycpyjjr z-|c74L(DP<5=XZFl{!`);lcCzgA75R%bScQV~bI%P>p&L)0a=KtnGu2~viRgM1-B=qI` zcUIpvyb=Dw{j(MQyPN&=!<%;>#&bVj{@2Y!R@i{;3F|BX4`2g~iX!{N=d9ZRj*$m7 z;&0TygI`*J(0-l&j-SIm3-nA~^LGg;;8{~xzXpv~ z=BVBW{Lg^t=bNV&-<>@?ThIS~^Y_cW(BVn96*LsJ+iK5uqY89~lkvM(>>m6BKl|Oq zY5qIte#88)n^fe-%f5?O)^|5X{wV$B^(*sZ{7$DC5&#b%1!3{F4G1of0Uzc6_Fg8d zKN0f<>vhR~Mm|PEBCRi2x{Y5R?WqByEuD5CP;(E=p;)*19qXT&Z7qJq!Pjfk7v7x)|bFZcMk z+hyYyF%Lalu>^W{^>5J&y$c2?cy2*AEtN4fXdjl2-dB`+Y{;^r{@6-kqQ=Y~HjUrY zxw)RJsF17LlN~|6_^$Tz&0nrvL#cFEZD#;pzqV32JmK@&xpCfQEf+(VU%_II)5pd; zv#&InHDj&er#CCcZy&z9X`g#=_K%yf1a}K$CSdDc>@Wqwb+7PMa$RzuZUL}+HHfNjB(?;NyRoyH)Udx6t;AYqIydiKp8k0k znO|E%R|4V3W+1F9o|zPK*C(Yzqiyc1>y0aS*rT@<-0+a?`A*o(;YoFi*vBD<^yP-B zDXo!Y-1W^nWIad4QKQG~yXek-e~lbs^B*@yMkN#oJIEdVvwv1*Jg{{yq5+PHoh)dy zf5!b={C>oGiC%O)fSPfS=%H)r_v}|nM)9I^1#N4gj(jQuPdAI*<#>74jE_Y2kmDZ} znfFa!^_cD%U70ewHdE2D#ANPWZ8Wi#@66sUtrk%+#{!wk>T`Sc3-?eRF4z39laRuZEpy;9yta`@wzI z03YFJ1w<*h*!u&1;rFEOY54*@CE&<_HcvKaN(KALtV74(y<5VF@~Fp>+G^oVts3=t z$8)TD7zK`tljcFXEirQug7;d>-`nnh6jt*_p(Cru{jru<`33w%dK8rob>C%;7CNmS z^XvEiXsh0lOGY~rf85*|N=A+Idl!8dHS|SaZj4Hc=>rHK4t!D zv41(1yNCYGaUmI9Ev(m$k-6>7?#CvxtXlTC8=;Qs1|;W?x4>8Iy=aNH=SPM9Cu)Z$ zqlMGj7pG<9)ceu>*cswPD!o$ato4}31#}g*$L4fpJ$IUCCJYAH`ftSVFuE%AK0vm*lnJKW(yU5S8E_sAyxqa_ zb98NTW*iYAe2i-5sEFMLFMD3@2<^o+G!lXQpPMl==8Me_wj~T$4yG7pTcSxVkU6Y$ zS!?ipsQU}6#kbZ{v?H-${`00|E`UCvaei{XWTdZNTV0!7o1KZ)a4IU0Qv!7vc)~&S zj~(qhuL{}!?ZSY--+zl=qUU@AyC0}`f!;IYNC37VX0_zAT`9G%KMmuOFPPymB zSig9Tg-}KF$94vham+qOzw+`Mv%RcKL?1v~`NC3Nf>>%%eb36S; zL65tUDNq8sMhjpKtQN)!9>X=HCVsz}LT=nuo2VURTthP#UtK@DetOpaAM|o^d9Y*3 z#m~q1!o_1-BDP%kw?!YQssHEnvC;eWdd7pmZ!>@$hKA+A?^}!>WcIfXwgQogx)1D- zLnfHfpiO8Hy;UMOF5JJke>M`qjzD@fzn=l`y!+sZ2OW%+N_#PgTF)vN`F5S%d@)1f zWvpl_w%^x=@!hi-HzNytdpg$94d>GGnbij z7CgGkJCS{MeRidITfV{GjOvfMV=d8Eqq;R(sQxkm1{@-LUM_m{#n@3{0v=(t*pY1w zS5Y_pgqrZH_peLD&sE1RC4gtjgZQa4$!5xc+SIYju-l;j;SL8sq8}n3SWnDAZK{X? zQUW}S1<}`%I(J9*)L@!?ruLID>{vwU!*`&6ceE3`d0kl-vWA_31K7$^n)**|xzVi* zqC2Rl9wUgp?oxUcOlh6rZZi6^Q7-t>tqJTBFe646s0hAbw`8@@WlnH!$V^2aoIO14 zn0t2Jv-#6SDk51gk=j<h8S}$uS`JT<- z%$xkfQ)a~b()ZhukG>g8=xh(YD0w6hix~_jj${572ygMz|HB8`_<>vEhXm~3rMBsa z@LNln6qY}hO79`RgB-S&wt^WC;@=&if9xthFTc=UAP0^d7qFK*(}H?f&8zvHL9joA z7*JI69vyq*>5hCy9{4NAl_hveOekOyEkaFIEy8rKzlg zpE(co-JtQ0@jJTbD>_uL^H259>W8SQf8qDw2Yi^hT85^B;NX4+`3_>fscA~k@ECz~ z5Lvs&NNPI+?g;E39$c9%zwpPGQ2mOXe`RODjFIC$<*3LU__4)W0lTHM?1i@H#tLcy zlaEpQe?KIlS**CeVYzlrz zE}{K8{!jGZpnc};)NV6=+W+oAi-X_xdYIMpE~2grruo?4YR~QeV#YnNZHXS-U#+Za zq4@#$ZsET%g9Z*%G~myeG4A$jDkgVIF7H8l(weexY;uPO{xI#2GLJB|5%i2GJ!Vcq z|F9J9{u23W@wkFscp;D(FH9n)eMH8y9+X@Nb{T z55I>+?I$s%x(6{~dnzZkN(a>1y2XbT^5K6|7 z7stnQqdI#=2kGNNXBO=5Z%bgBt8kc}$le*k-hae{4k@ig^bfJ`<;UL7L59rlDZPa5 z-_+jJ9$TlV`I-H=Bjed4Ksvx)bd4sq@Y2YE=Novye`Prd&^lqf~`OJtw9y4DYh3&)N(cbf`dCohm=6lTX z{2u0617QU`VNT4y#on14*wG2pmyBb0%)5vbQ{c)vW@q}&Mic8bHMeG=XKKB(PIDb| zD~S&DKWz4s`@#==jNImb-T%uT?#K9L?-PCzz0D+QFg;EiudKF00(-N&1K7g>`zZzL z9th$0$$jF5*5Lyo31}bZTII*eDm+8%02WgYEgNkK&UKFJ<2i5u9WP9?<#>O}GBS!} zjNM{-3+8@)bdr|gb%I7Ge)gnr7jthXg;%Jir%UZ~KVGh5-*?sc`R2(s7WNk>v#9@7 zH31eh**nh~JU|^gS)hpSE#7bNf6RY(`~BexG>N-u`ZU!a-xK(V)*svLFNdgq6I$27 zc!{4fz?qN&^e82O2T^Je!l7b+Z)tCvg@U( z`_YlGn)p=z;tKHNwL$+9J@em2{BBsH4_K*J*z)WGBtVS-2a)31&VZ&e?!yAGzeTk2 zK^aK&^nweB0x}L#0{(bMt`mL0Hq1qh=k`MLk2K^IW#qm=eHq!&Vfw+@WG)cd83f<( z*RcCjG-LAvJ_S1ozjPEQDxHO{;$8I5`nK8en1mnT+{ZK~pC_~H@BpH}feauO-H$pF zuz4@bE65)i5zBLnJ^l-#~oBs`W_<+Sn2|%0BHt_6zJB+*Fq(pEI$@pXQ{eDEN3YbmH zCwAs5%=RT(k-@#to}&VIUj9$W0A9T_r{>!9YK%T$F>1zW_nZk2g!eJ)gnbS4eoqRk zg-&$IMqmN-k%yTedPBQ|=Bg*Xw52IOU2(tsFW1bcC$pOu)7$UNm#BWGx2MthA4{~) zv8UBO=P*FLI`{?mne%ZeLi++gvK%M>8}1`=iINsgglE038^rzC);?@rPM40-Z>`!DqyA~WYi%qcyIYHqUgUveiS=C9 z+M=ihX35R+gAru3 zr%vQ34+E1@cS}8}lOCHrrDbE;m^KeF`EM00?3MLyzc$}lZ>+^rZeT-tnqRp~)p$DV~qYV5p1+h@_ zfeg^m%ZLAiZ}%hC(F%O1TbchUAs2K`{tFpA$4(QvVPr@PYPyAxfjzJ>O!+LQje?45 zfIR_Netsmog>ted#c|%fvgAnpt^}E z3HC@v^#E|1u{OrUyn%Z_x^hNt99c8H7Vahr0RudxXQqm2(n-`4tg3#s*kN5JYJ1@f z_-*XYtKfu<-(dxM5?F|s4*%!-@ClK#^K-&tsdL;NLq1T)uzkcMBCcSH$Bp-Q2X+1w zBtYEskYo$b*!{wEEm)wz3u@?dV-9JyRmW}#B4;Je{W8>exZgul%IK$?@ikbd25(3y2rEE- zA4fHf+=64j(#wd46X+0`hyk?q9o(%{jdx~~xCd+nlQGP6CvYdtbZHaQ zE|a+5byT1u#~Co?_^l!PlY71vwm~F)3y& z!=}5khek&t86D3tA8bpUU~-ms4&6U{TYGT&4txD8(0O~^n=k9l`W+Qo7PvopNBkVT zew%nW>~F>o)FuhkJb<5LiHrlmSEB`jO-dSNtnq9v6G7ZuaFs=GGBr5PtTr|#uD$$cbI+pZ|wFU%R{HHeXf4L z{EWCyw&6~BdB^C8{0;#`S1Bt;Vw4#r$HiCq2sGq>sq`XPFBWi1X;2EhLLaaQPT(r# zfy#(}kU0P&!|ni)Q}?$15%X9P(El&M4+;ig=7`<^3)ukW!9~fGpEH{1u(K5QL&x(= zsQ-Sg&0N$jH%c4k!_%+dv+{d*T1MtqtylP{X&U=olwp1Gn-(;cdm?4=EO$p5p5>HK zh*zBa2lWp-6VN$rpZphHPFMuF&xlAKNdFMTy9&Gj{U3b>Duh12=y?d=|3b2#+9!UA z6u9x7Je;}X5#<2Q13x4M??~^8znE#yaWDK7lkNAF;;HmG-dEFC;+w2ciY|KKOm0bM#LsxKz#^&Ep@|*0(EDtnC4Pyyu$o`(h-;9^LS@mUqPX%}Rowd;K=)c@w zMW>egpE`Q>lmM(jG9Jh!3(yBnlc&;8APz!rA_qYI>l%UtX#GTw%nw<=uzQXAuk&3A z`C#|+_=XnVeF-@XKo0PP$TZRU;&j0ncDL4n|L<3unD_tseeL4e`yZ~qet++8sTgy? zMcxyQMLTbhh~Ro%9>^mo4Os|2X;?A zNYet+S6^N~1^%Bdez^YO`s}i2lhJLoDfT&@U&sRHsfwRl0wcpGY^glkkM1RYSI;$#}uc4h3p@RJuN8xMRLw zv%i5+L3Iz!<6aBgdyjAI!ZW5nb}@S|vH^TM5C8H1@i*{qjG{+~yNI(dbAg%FxtZ|s!?_|12X}8!moMW&pBha6MarjFpK`D?1J*Sb-cAc(R +sentence=Parse hex files created by Arduino for Uno/Mini/Nano +paragraph= +category=Signal Input/Output +url= +architectures=esp8266 diff --git a/lib/ArduinoHexParse/src/ArduinoHexParse.cpp b/lib/ArduinoHexParse/src/ArduinoHexParse.cpp new file mode 100644 index 000000000..d4125f2f5 --- /dev/null +++ b/lib/ArduinoHexParse/src/ArduinoHexParse.cpp @@ -0,0 +1,134 @@ +/* + Copyright (C) 2019 Andre Thomas and Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include +#include "ArduinoHexParse.h" + +ArduinoHexParse::ArduinoHexParse(void) +{ + loadAddress[0] = 0; + loadAddress[1] = 0; +} + +void ArduinoHexParse::ParseLine(byte* hexline) +{ + recordType = GetRecordType(hexline); + if (0 == recordType) { + address = GetAddress(hexline); + len = GetLength(hexline); + GetData(hexline, len); + if (128 == PageMemIdx) { + if (!firstRun) { + loadAddress[1] += 0x40; + if (0 == loadAddress[1]) { + loadAddress[0] += 1; + } + } + firstRun = false; + FlashPageReady = true; + PageMemIdx = 0; + } + nextAddress = address + len; + } + if (1 == recordType) { + EndOfFile(); + FlashPageReady = true; + } +} + +bool ArduinoHexParse::IsFlashPageReady(void) +{ + return FlashPageReady; +} + +byte* ArduinoHexParse::GetFlashPage(void) +{ + FlashPageReady = false; + return FlashPage; +} + +byte* ArduinoHexParse::GetLoadAddress(void) +{ + return loadAddress; +} + +void ArduinoHexParse::GetLoadAddress(byte* hexline) +{ + char buff[3]; + buff[2] = '\0'; + buff[0] = hexline[3]; + buff[1] = hexline[4]; + loadAddress[0] = strtol(buff, 0, 16); + buff[0] = hexline[5]; + buff[1] = hexline[6]; + loadAddress[1] = strtol(buff, 0, 16); +} + +byte* ArduinoHexParse::GetData(byte* hexline, uint32_t len) +{ + uint32_t start = 9; + uint32_t end = (len * 2) + start; + char buff[3]; + buff[2] = '\0'; + for (uint32_t x = start; x < end; x = x+2) { + buff[0] = hexline[x]; + buff[1] = hexline[x+1]; + FlashPage[PageMemIdx] = strtol(buff, 0, 16); + PageMemIdx++; + } +} + +void ArduinoHexParse::EndOfFile(void) +{ + loadAddress[1] += 0x40; + if (0 == loadAddress[1]) { + loadAddress[0] += 1; + } + while (128 > PageMemIdx) { // Fill the remaing space in the memory page with 0xFF + FlashPage[PageMemIdx] = 0xFF; + PageMemIdx++; + } +} + +uint32_t ArduinoHexParse::GetAddress(byte* hexline) +{ + char buff[5]; + buff[0] = hexline[3]; + buff[1] = hexline[4]; + buff[2] = hexline[5]; + buff[3] = hexline[6]; + buff[4] = '\0'; + return strtol(buff, 0, 16); +} + +uint16_t ArduinoHexParse::GetLength(byte* hexline) +{ + char buff[3]; + buff[0] = hexline[1]; + buff[1] = hexline[2]; + buff[2] = '\0'; + return strtol(buff, 0, 16); +} + +uint16_t ArduinoHexParse::GetRecordType(byte* hexline) +{ + char buff[3]; + buff[0] = hexline[7]; + buff[1] = hexline[8]; + buff[2] = '\0'; + return strtol(buff, 0, 16); +} diff --git a/lib/ArduinoHexParse/src/ArduinoHexParse.h b/lib/ArduinoHexParse/src/ArduinoHexParse.h new file mode 100644 index 000000000..7b941eea1 --- /dev/null +++ b/lib/ArduinoHexParse/src/ArduinoHexParse.h @@ -0,0 +1,47 @@ +/* + Copyright (C) 2019 Andre Thomas and Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef __ARDUINOHEXPARSE_H__ + +#include + +class ArduinoHexParse { + public: + ArduinoHexParse(void); + void ParseLine(byte* data); + byte* GetFlashPage(void); + byte* GetLoadAddress(void); + bool IsFlashPageReady(void); + private: + uint32_t address = 0; + uint32_t len = 0; + uint32_t nextAddress = 0; + uint32_t PageMemIdx = 0; + uint32_t recordType = 0; + byte FlashPage[128]; + byte loadAddress[2]; + bool FlashPageReady = false; + bool firstRun = true; + uint32_t GetAddress(byte* hexline); + uint16_t GetLength(byte* hexline); + uint16_t GetRecordType(byte* hexline); + byte* GetData(byte* hexline, uint32_t len); + void GetLoadAddress(byte* hexline); + void EndOfFile(void); +}; + +#endif // __ARDUINOHEXPARSE_H__ \ No newline at end of file diff --git a/lib/FT6236-gemu-1.0/FT6236.cpp b/lib/FT6236-gemu-1.0/FT6236.cpp new file mode 100644 index 000000000..6397190fb --- /dev/null +++ b/lib/FT6236-gemu-1.0/FT6236.cpp @@ -0,0 +1,159 @@ +#include +#include + +/* + * This is a static library so we need to make sure we process stuff as quick as possible + * as we do not want it to interfere with the RTOS by delaying routines unnecessarily. + * So, no delay()'s etc and opto the code as much as possible. + * ^^^ Need to be on TODO list to go through and make sure everything is as opto as + * possible + */ + +uint8_t FT6236buf[FT6236_BUFFER_SIZE]; +uint8_t FT6236_i2c_addr = 0x38; +uint8_t lenLibVersion = 0; +uint8_t firmwareId = 0; + +struct tbuttonregister { + uint16_t BUTTONID; + uint16_t xmin; + uint16_t xmax; + uint16_t ymin; + uint16_t ymax; +} buttonregister[FT6236_MAX_BUTTONS]; // we're limiting to 16 buttons for now - can reduce or increase later as needed. + +uint8_t buttoncount = 0; + +void FT6236flushbuttonregister(void) { + uint16_t bid; + for (bid=0;bid 0) { + uint16_t x = tl[0].x; + uint16_t y = tl[0].y; + for (bid=0;bid= buttonregister[bid].xmin) { + if (x <= buttonregister[bid].xmax) { + if (y >= buttonregister[bid].ymin) { + if (y <= buttonregister[bid].ymax) { + return buttonregister[bid].BUTTONID; + } + } + } + } + } + } + return 0; +} + +void FT6236begin(uint8_t i2c_addr) { + FT6236_i2c_addr=i2c_addr; + Wire.begin(); + FT6236writeTouchRegister(0,FT6236_MODE_NORMAL); + lenLibVersion = FT6236readTouchAddr(0x0a1, FT6236buf, 2 ); + firmwareId = FT6236readTouchRegister( 0xa6 ); +} + +void FT6236writeTouchRegister(uint8_t reg, uint8_t val) +{ + Wire.beginTransmission(FT6236_i2c_addr); + Wire.write(reg); // register 0 + Wire.write(val); // value + Wire.endTransmission(); +} + +uint8_t FT6236readTouchRegister(uint8_t reg) +{ + Wire.beginTransmission(FT6236_i2c_addr); + Wire.write(reg); // register 0 + uint8_t retVal = Wire.endTransmission(); + uint8_t returned = Wire.requestFrom(FT6236_i2c_addr,uint8_t(1)); // request 6 uint8_ts from slave device #2 + if (Wire.available()) + { + retVal = Wire.read(); + } + return retVal; +} + +uint8_t FT6236readTouchAddr( uint8_t regAddr, uint8_t * pBuf, uint8_t len ) +{ + Wire.beginTransmission(FT6236_i2c_addr); + Wire.write( regAddr ); // register 0 + uint8_t retVal = Wire.endTransmission(); + uint8_t returned = Wire.requestFrom(FT6236_i2c_addr, len); // request 1 bytes from slave device #2 + uint8_t i; + for (i = 0; (i < len) && Wire.available(); i++) { + pBuf[i] = Wire.read(); + } + return i; +} + +uint8_t FT6236readTouchLocation( TouchLocation * pLoc, uint8_t num ) +{ + uint8_t retVal = 0; + uint8_t i; + uint8_t k; + do + { + if (!pLoc) break; // must have a buffer + if (!num) break; // must be able to take at least one + uint8_t status = FT6236readTouchRegister(2); + static uint8_t tbuf[40]; + if ((status & 0x0f) == 0) break; // no points detected + uint8_t hitPoints = status & 0x0f; + FT6236readTouchAddr( 0x03, tbuf, hitPoints*6); + for (k=0,i = 0; (i < hitPoints*6)&&(k < num); k++, i += 6) { + pLoc[k].x = (tbuf[i+0] & 0x0f) << 8 | tbuf[i+1]; + pLoc[k].y = (tbuf[i+2] & 0x0f) << 8 | tbuf[i+3]; + } + retVal = k; + } while (0); + return retVal; +} + +uint32_t FT6236dist(const TouchLocation & loc) +{ + uint32_t retVal = 0; + uint32_t x = loc.x; + uint32_t y = loc.y; + retVal = x*x + y*y; + return retVal; +} + + +/* +uint32_t FT6236dist(const TouchLocation & loc1, const TouchLocation & loc2) +{ + uint32_t retVal = 0; + uint32_t x = loc1.x - loc2.x; + uint32_t y = loc1.y - loc2.y; + retVal = sqrt(x*x + y*y); + return retVal; +} +*/ + +bool FT6236sameLoc( const TouchLocation & loc, const TouchLocation & loc2 ) +{ + return FT6236dist(loc,loc2) < 50; +} diff --git a/lib/FT6236-gemu-1.0/FT6236.h b/lib/FT6236-gemu-1.0/FT6236.h new file mode 100644 index 000000000..601d9c67e --- /dev/null +++ b/lib/FT6236-gemu-1.0/FT6236.h @@ -0,0 +1,28 @@ +#ifndef FT6236 +#define FT6236 + +#define FT6236_MODE_NORMAL 0x00 +#define FT6236_MODE_TEST 0x04 +#define FT6236_MODE_SYSTEM 0x01 + +#define FT6236_BUFFER_SIZE 0x1E // 30 bytes buffer +#define FT6236_MAX_BUTTONS 1 // 50 buttons should be enough for just about any page + +struct TouchLocation { + uint16_t y; // we swop x and y in position because we're using the screen in portrait mode + uint16_t x; +}; + +void FT6236flushbuttonregister(void); +void FT6236registerbutton(uint16_t buttonid,uint16_t xmin,uint16_t ymin,uint16_t xmax, uint16_t ymax); +uint16_t FT6236GetButtonMask(void); +void FT6236begin(uint8_t i2c_addr); +uint8_t FT6236readTouchRegister( uint8_t reg ); +uint8_t FT6236readTouchLocation( TouchLocation * pLoc, uint8_t num ); +uint8_t FT6236readTouchAddr( uint8_t regAddr, uint8_t * pBuf, uint8_t len ); +void FT6236writeTouchRegister( uint8_t reg, uint8_t val); +uint32_t FT6236dist(const TouchLocation & loc); +uint32_t FT6236dist(const TouchLocation & loc1, const TouchLocation & loc2); +bool FT6236sameLoc( const TouchLocation & loc, const TouchLocation & loc2 ); + +#endif diff --git a/lib/IRremoteESP8266-2.6.0/.travis.yml b/lib/IRremoteESP8266-2.6.0/.travis.yml deleted file mode 100644 index ae2d9fe3c..000000000 --- a/lib/IRremoteESP8266-2.6.0/.travis.yml +++ /dev/null @@ -1,66 +0,0 @@ -language: c -env: - - BD=esp8266:esp8266:nodemcuv2:xtal=80,eesz=4M3M,ip=lm2f,exception=disabled - - BD=esp8266:esp8266:d1_mini:xtal=80,eesz=4M3M,ip=lm2f,exception=disabled -before_install: - - "/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_1.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :1 -ac -screen 0 1280x1024x16" - - sleep 3 - - export DISPLAY=:1.0 - - wget http://downloads.arduino.cc/arduino-1.8.8-linux64.tar.xz - - tar xf arduino-1.8.8-linux64.tar.xz - - sudo mv arduino-1.8.8 /usr/local/share/arduino - - sudo ln -s /usr/local/share/arduino/arduino /usr/local/bin/arduino - - wget https://raw.githubusercontent.com/google/styleguide/gh-pages/cpplint/cpplint.py -install: - - ln -s $PWD /usr/local/share/arduino/libraries/ - - git clone https://github.com/tzapu/WiFiManager.git /usr/local/share/arduino/libraries/WiFiManager - - git clone https://github.com/knolleary/pubsubclient.git /usr/local/share/arduino/libraries/PubSubClient - - git clone https://github.com/bblanchon/ArduinoJson.git --branch 5.x /usr/local/share/arduino/libraries/ArduinoJson - - arduino --pref "boardsmanager.additional.urls=http://arduino.esp8266.com/stable/package_esp8266com_index.json" --save-prefs - - arduino --install-boards esp8266:esp8266 - - arduino --board $BD --save-prefs - - arduino --pref "compiler.warning_level=all" --save-prefs - - sudo apt-get install jq - - sudo pip install pylint -script: - # Check that everything compiles. - - arduino --verify --board $BD $PWD/examples/IRrecvDemo/IRrecvDemo.ino - - arduino --verify --board $BD $PWD/examples/IRGCSendDemo/IRGCSendDemo.ino - - arduino --verify --board $BD $PWD/examples/IRGCTCPServer/IRGCTCPServer.ino - - arduino --verify --board $BD $PWD/examples/IRServer/IRServer.ino - - arduino --verify --board $BD $PWD/examples/IRrecvDumpV2/IRrecvDumpV2.ino - - arduino --verify --board $BD $PWD/examples/IRsendDemo/IRsendDemo.ino - - arduino --verify --board $BD $PWD/examples/JVCPanasonicSendDemo/JVCPanasonicSendDemo.ino - - arduino --verify --board $BD $PWD/examples/TurnOnDaikinAC/TurnOnDaikinAC.ino - - arduino --verify --board $BD $PWD/examples/TurnOnFujitsuAC/TurnOnFujitsuAC.ino - - arduino --verify --board $BD $PWD/examples/TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino - - arduino --verify --board $BD $PWD/examples/TurnOnMitsubishiAC/TurnOnMitsubishiAC.ino - - arduino --verify --board $BD $PWD/examples/IRsendProntoDemo/IRsendProntoDemo.ino - - arduino --verify --board $BD $PWD/examples/TurnOnTrotecAC/TurnOnTrotecAC.ino - - arduino --verify --board $BD $PWD/examples/LGACSend/LGACSend.ino - - arduino --verify --board $BD $PWD/examples/TurnOnArgoAC/TurnOnArgoAC.ino - - arduino --verify --board $BD $PWD/examples/IRMQTTServer/IRMQTTServer.ino - - arduino --verify --board $BD $PWD/examples/TurnOnToshibaAC/TurnOnToshibaAC.ino - - arduino --verify --board $BD $PWD/examples/ControlSamsungAC/ControlSamsungAC.ino - - arduino --verify --board $BD $PWD/examples/TurnOnPanasonicAC/TurnOnPanasonicAC.ino - - arduino --verify --board $BD $PWD/examples/TurnOnMitsubishiHeavyAc/TurnOnMitsubishiHeavyAc.ino - - # Also check the tools programs compile. - - (cd tools; make all) - # Check for lint issues. - - shopt -s nullglob - - python cpplint.py --extensions=c,cc,cpp,ino --headers=h,hpp {src,test,tools}/*.{h,c,cc,cpp,hpp,ino} examples/*/*.{h,c,cc,cpp,hpp,ino} - - pylint {src,test,tools}/*.py - - shopt -u nullglob - # Build and run the unit tests. - - (cd test; make run) - - (cd tools; make run_tests) - # Check the version numbers match. - - LIB_VERSION=$(egrep "^#define\s+_IRREMOTEESP8266_VERSION_\s+" src/IRremoteESP8266.h | cut -d\" -f2) - - test ${LIB_VERSION} == "$(jq -r .version library.json)" - - grep -q "^version=${LIB_VERSION}$" library.properties - -notifications: - email: - on_success: change - on_failure: change diff --git a/lib/IRremoteESP8266-2.6.0/examples/ControlSamsungAC/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/ControlSamsungAC/platformio.ini deleted file mode 100644 index ec84f22f3..000000000 --- a/lib/IRremoteESP8266-2.6.0/examples/ControlSamsungAC/platformio.ini +++ /dev/null @@ -1,19 +0,0 @@ -[platformio] -lib_extra_dirs = ../../ -src_dir=. - -[common] -build_flags = -lib_deps_builtin = -lib_deps_external = -lib_ldf_mode = chain+ - -[env:nodemcuv2] -platform = espressif8266 -framework = arduino -board = nodemcuv2 -lib_ldf_mode = ${common.lib_ldf_mode} -build_flags = ${common.build_flags} -lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.6.0/examples/IRGCSendDemo/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/IRGCSendDemo/platformio.ini deleted file mode 100644 index ec84f22f3..000000000 --- a/lib/IRremoteESP8266-2.6.0/examples/IRGCSendDemo/platformio.ini +++ /dev/null @@ -1,19 +0,0 @@ -[platformio] -lib_extra_dirs = ../../ -src_dir=. - -[common] -build_flags = -lib_deps_builtin = -lib_deps_external = -lib_ldf_mode = chain+ - -[env:nodemcuv2] -platform = espressif8266 -framework = arduino -board = nodemcuv2 -lib_ldf_mode = ${common.lib_ldf_mode} -build_flags = ${common.build_flags} -lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.6.0/examples/IRGCTCPServer/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/IRGCTCPServer/platformio.ini deleted file mode 100644 index ec84f22f3..000000000 --- a/lib/IRremoteESP8266-2.6.0/examples/IRGCTCPServer/platformio.ini +++ /dev/null @@ -1,19 +0,0 @@ -[platformio] -lib_extra_dirs = ../../ -src_dir=. - -[common] -build_flags = -lib_deps_builtin = -lib_deps_external = -lib_ldf_mode = chain+ - -[env:nodemcuv2] -platform = espressif8266 -framework = arduino -board = nodemcuv2 -lib_ldf_mode = ${common.lib_ldf_mode} -build_flags = ${common.build_flags} -lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.6.0/examples/IRMQTTServer/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/IRMQTTServer/platformio.ini deleted file mode 100644 index 243b36a99..000000000 --- a/lib/IRremoteESP8266-2.6.0/examples/IRMQTTServer/platformio.ini +++ /dev/null @@ -1,42 +0,0 @@ -[platformio] -lib_extra_dirs = ../../ -src_dir=. - -[common] -build_flags = -DMQTT_MAX_PACKET_SIZE=768 -lib_ldf_mode = chain+ -lib_deps_builtin = -lib_deps_external = - PubSubClient - WifiManager@0.14 - ArduinoJson - -[env:nodemcuv2] -platform = espressif8266 -framework = arduino -board = nodemcuv2 -lib_ldf_mode = ${common.lib_ldf_mode} -build_flags = ${common.build_flags} -lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} - -[env:d1_mini] -platform=espressif8266 -framework=arduino -board=d1_mini -lib_ldf_mode = ${common.lib_ldf_mode} -build_flags = ${common.build_flags} -lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} - -[env:d1_mini_no_mqtt] -platform=espressif8266 -framework=arduino -board=d1_mini -lib_ldf_mode = ${common.lib_ldf_mode} -build_flags = ${common.build_flags} -DMQTT_ENABLE=false -lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.6.0/examples/IRServer/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/IRServer/platformio.ini deleted file mode 100644 index ec84f22f3..000000000 --- a/lib/IRremoteESP8266-2.6.0/examples/IRServer/platformio.ini +++ /dev/null @@ -1,19 +0,0 @@ -[platformio] -lib_extra_dirs = ../../ -src_dir=. - -[common] -build_flags = -lib_deps_builtin = -lib_deps_external = -lib_ldf_mode = chain+ - -[env:nodemcuv2] -platform = espressif8266 -framework = arduino -board = nodemcuv2 -lib_ldf_mode = ${common.lib_ldf_mode} -build_flags = ${common.build_flags} -lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.6.0/examples/IRrecvDemo/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/IRrecvDemo/platformio.ini deleted file mode 100644 index ec84f22f3..000000000 --- a/lib/IRremoteESP8266-2.6.0/examples/IRrecvDemo/platformio.ini +++ /dev/null @@ -1,19 +0,0 @@ -[platformio] -lib_extra_dirs = ../../ -src_dir=. - -[common] -build_flags = -lib_deps_builtin = -lib_deps_external = -lib_ldf_mode = chain+ - -[env:nodemcuv2] -platform = espressif8266 -framework = arduino -board = nodemcuv2 -lib_ldf_mode = ${common.lib_ldf_mode} -build_flags = ${common.build_flags} -lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.6.0/examples/IRrecvDump/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/IRrecvDump/platformio.ini deleted file mode 100644 index ec84f22f3..000000000 --- a/lib/IRremoteESP8266-2.6.0/examples/IRrecvDump/platformio.ini +++ /dev/null @@ -1,19 +0,0 @@ -[platformio] -lib_extra_dirs = ../../ -src_dir=. - -[common] -build_flags = -lib_deps_builtin = -lib_deps_external = -lib_ldf_mode = chain+ - -[env:nodemcuv2] -platform = espressif8266 -framework = arduino -board = nodemcuv2 -lib_ldf_mode = ${common.lib_ldf_mode} -build_flags = ${common.build_flags} -lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.6.0/examples/IRrecvDumpV2/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/IRrecvDumpV2/platformio.ini deleted file mode 100644 index ec84f22f3..000000000 --- a/lib/IRremoteESP8266-2.6.0/examples/IRrecvDumpV2/platformio.ini +++ /dev/null @@ -1,19 +0,0 @@ -[platformio] -lib_extra_dirs = ../../ -src_dir=. - -[common] -build_flags = -lib_deps_builtin = -lib_deps_external = -lib_ldf_mode = chain+ - -[env:nodemcuv2] -platform = espressif8266 -framework = arduino -board = nodemcuv2 -lib_ldf_mode = ${common.lib_ldf_mode} -build_flags = ${common.build_flags} -lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.6.0/examples/IRsendDemo/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/IRsendDemo/platformio.ini deleted file mode 100644 index ec84f22f3..000000000 --- a/lib/IRremoteESP8266-2.6.0/examples/IRsendDemo/platformio.ini +++ /dev/null @@ -1,19 +0,0 @@ -[platformio] -lib_extra_dirs = ../../ -src_dir=. - -[common] -build_flags = -lib_deps_builtin = -lib_deps_external = -lib_ldf_mode = chain+ - -[env:nodemcuv2] -platform = espressif8266 -framework = arduino -board = nodemcuv2 -lib_ldf_mode = ${common.lib_ldf_mode} -build_flags = ${common.build_flags} -lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.6.0/examples/IRsendProntoDemo/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/IRsendProntoDemo/platformio.ini deleted file mode 100644 index ec84f22f3..000000000 --- a/lib/IRremoteESP8266-2.6.0/examples/IRsendProntoDemo/platformio.ini +++ /dev/null @@ -1,19 +0,0 @@ -[platformio] -lib_extra_dirs = ../../ -src_dir=. - -[common] -build_flags = -lib_deps_builtin = -lib_deps_external = -lib_ldf_mode = chain+ - -[env:nodemcuv2] -platform = espressif8266 -framework = arduino -board = nodemcuv2 -lib_ldf_mode = ${common.lib_ldf_mode} -build_flags = ${common.build_flags} -lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.6.0/examples/JVCPanasonicSendDemo/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/JVCPanasonicSendDemo/platformio.ini deleted file mode 100644 index ec84f22f3..000000000 --- a/lib/IRremoteESP8266-2.6.0/examples/JVCPanasonicSendDemo/platformio.ini +++ /dev/null @@ -1,19 +0,0 @@ -[platformio] -lib_extra_dirs = ../../ -src_dir=. - -[common] -build_flags = -lib_deps_builtin = -lib_deps_external = -lib_ldf_mode = chain+ - -[env:nodemcuv2] -platform = espressif8266 -framework = arduino -board = nodemcuv2 -lib_ldf_mode = ${common.lib_ldf_mode} -build_flags = ${common.build_flags} -lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.6.0/examples/LGACSend/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/LGACSend/platformio.ini deleted file mode 100644 index ec84f22f3..000000000 --- a/lib/IRremoteESP8266-2.6.0/examples/LGACSend/platformio.ini +++ /dev/null @@ -1,19 +0,0 @@ -[platformio] -lib_extra_dirs = ../../ -src_dir=. - -[common] -build_flags = -lib_deps_builtin = -lib_deps_external = -lib_ldf_mode = chain+ - -[env:nodemcuv2] -platform = espressif8266 -framework = arduino -board = nodemcuv2 -lib_ldf_mode = ${common.lib_ldf_mode} -build_flags = ${common.build_flags} -lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.6.0/examples/TurnOnArgoAC/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/TurnOnArgoAC/platformio.ini deleted file mode 100644 index ec84f22f3..000000000 --- a/lib/IRremoteESP8266-2.6.0/examples/TurnOnArgoAC/platformio.ini +++ /dev/null @@ -1,19 +0,0 @@ -[platformio] -lib_extra_dirs = ../../ -src_dir=. - -[common] -build_flags = -lib_deps_builtin = -lib_deps_external = -lib_ldf_mode = chain+ - -[env:nodemcuv2] -platform = espressif8266 -framework = arduino -board = nodemcuv2 -lib_ldf_mode = ${common.lib_ldf_mode} -build_flags = ${common.build_flags} -lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.6.0/examples/TurnOnDaikinAC/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/TurnOnDaikinAC/platformio.ini deleted file mode 100644 index ec84f22f3..000000000 --- a/lib/IRremoteESP8266-2.6.0/examples/TurnOnDaikinAC/platformio.ini +++ /dev/null @@ -1,19 +0,0 @@ -[platformio] -lib_extra_dirs = ../../ -src_dir=. - -[common] -build_flags = -lib_deps_builtin = -lib_deps_external = -lib_ldf_mode = chain+ - -[env:nodemcuv2] -platform = espressif8266 -framework = arduino -board = nodemcuv2 -lib_ldf_mode = ${common.lib_ldf_mode} -build_flags = ${common.build_flags} -lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.6.0/examples/TurnOnFujitsuAC/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/TurnOnFujitsuAC/platformio.ini deleted file mode 100644 index ec84f22f3..000000000 --- a/lib/IRremoteESP8266-2.6.0/examples/TurnOnFujitsuAC/platformio.ini +++ /dev/null @@ -1,19 +0,0 @@ -[platformio] -lib_extra_dirs = ../../ -src_dir=. - -[common] -build_flags = -lib_deps_builtin = -lib_deps_external = -lib_ldf_mode = chain+ - -[env:nodemcuv2] -platform = espressif8266 -framework = arduino -board = nodemcuv2 -lib_ldf_mode = ${common.lib_ldf_mode} -build_flags = ${common.build_flags} -lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.6.0/examples/TurnOnKelvinatorAC/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/TurnOnKelvinatorAC/platformio.ini deleted file mode 100644 index ec84f22f3..000000000 --- a/lib/IRremoteESP8266-2.6.0/examples/TurnOnKelvinatorAC/platformio.ini +++ /dev/null @@ -1,19 +0,0 @@ -[platformio] -lib_extra_dirs = ../../ -src_dir=. - -[common] -build_flags = -lib_deps_builtin = -lib_deps_external = -lib_ldf_mode = chain+ - -[env:nodemcuv2] -platform = espressif8266 -framework = arduino -board = nodemcuv2 -lib_ldf_mode = ${common.lib_ldf_mode} -build_flags = ${common.build_flags} -lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.6.0/examples/TurnOnMitsubishiAC/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/TurnOnMitsubishiAC/platformio.ini deleted file mode 100644 index ec84f22f3..000000000 --- a/lib/IRremoteESP8266-2.6.0/examples/TurnOnMitsubishiAC/platformio.ini +++ /dev/null @@ -1,19 +0,0 @@ -[platformio] -lib_extra_dirs = ../../ -src_dir=. - -[common] -build_flags = -lib_deps_builtin = -lib_deps_external = -lib_ldf_mode = chain+ - -[env:nodemcuv2] -platform = espressif8266 -framework = arduino -board = nodemcuv2 -lib_ldf_mode = ${common.lib_ldf_mode} -build_flags = ${common.build_flags} -lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.6.0/examples/TurnOnMitsubishiHeavyAc/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/TurnOnMitsubishiHeavyAc/platformio.ini deleted file mode 100644 index ec84f22f3..000000000 --- a/lib/IRremoteESP8266-2.6.0/examples/TurnOnMitsubishiHeavyAc/platformio.ini +++ /dev/null @@ -1,19 +0,0 @@ -[platformio] -lib_extra_dirs = ../../ -src_dir=. - -[common] -build_flags = -lib_deps_builtin = -lib_deps_external = -lib_ldf_mode = chain+ - -[env:nodemcuv2] -platform = espressif8266 -framework = arduino -board = nodemcuv2 -lib_ldf_mode = ${common.lib_ldf_mode} -build_flags = ${common.build_flags} -lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.6.0/examples/TurnOnPanasonicAC/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/TurnOnPanasonicAC/platformio.ini deleted file mode 100644 index ec84f22f3..000000000 --- a/lib/IRremoteESP8266-2.6.0/examples/TurnOnPanasonicAC/platformio.ini +++ /dev/null @@ -1,19 +0,0 @@ -[platformio] -lib_extra_dirs = ../../ -src_dir=. - -[common] -build_flags = -lib_deps_builtin = -lib_deps_external = -lib_ldf_mode = chain+ - -[env:nodemcuv2] -platform = espressif8266 -framework = arduino -board = nodemcuv2 -lib_ldf_mode = ${common.lib_ldf_mode} -build_flags = ${common.build_flags} -lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.6.0/examples/TurnOnToshibaAC/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/TurnOnToshibaAC/platformio.ini deleted file mode 100644 index ec84f22f3..000000000 --- a/lib/IRremoteESP8266-2.6.0/examples/TurnOnToshibaAC/platformio.ini +++ /dev/null @@ -1,19 +0,0 @@ -[platformio] -lib_extra_dirs = ../../ -src_dir=. - -[common] -build_flags = -lib_deps_builtin = -lib_deps_external = -lib_ldf_mode = chain+ - -[env:nodemcuv2] -platform = espressif8266 -framework = arduino -board = nodemcuv2 -lib_ldf_mode = ${common.lib_ldf_mode} -build_flags = ${common.build_flags} -lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.6.0/examples/TurnOnTrotecAC/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/TurnOnTrotecAC/platformio.ini deleted file mode 100644 index ec84f22f3..000000000 --- a/lib/IRremoteESP8266-2.6.0/examples/TurnOnTrotecAC/platformio.ini +++ /dev/null @@ -1,19 +0,0 @@ -[platformio] -lib_extra_dirs = ../../ -src_dir=. - -[common] -build_flags = -lib_deps_builtin = -lib_deps_external = -lib_ldf_mode = chain+ - -[env:nodemcuv2] -platform = espressif8266 -framework = arduino -board = nodemcuv2 -lib_ldf_mode = ${common.lib_ldf_mode} -build_flags = ${common.build_flags} -lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.6.0/platformio.ini b/lib/IRremoteESP8266-2.6.0/platformio.ini deleted file mode 100644 index b6020c165..000000000 --- a/lib/IRremoteESP8266-2.6.0/platformio.ini +++ /dev/null @@ -1,29 +0,0 @@ -[platformio] -lib_extra_dirs = . -src_dir = examples/IRrecvDumpV2 - -[common] -build_flags = -lib_deps_builtin = -lib_deps_external = -lib_ldf_mode = chain+ - -[env:nodemcuv2] -platform = espressif8266 -framework = arduino -board = nodemcuv2 -lib_ldf_mode = ${common.lib_ldf_mode} -build_flags = ${common.build_flags} -lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} - -[env:d1_mini] -platform = espressif8266 -framework = arduino -board = d1_mini -lib_ldf_mode = ${common.lib_ldf_mode} -build_flags = ${common.build_flags} -lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.6.0/src/IRac.cpp b/lib/IRremoteESP8266-2.6.0/src/IRac.cpp deleted file mode 100644 index 782c147c2..000000000 --- a/lib/IRremoteESP8266-2.6.0/src/IRac.cpp +++ /dev/null @@ -1,1125 +0,0 @@ -// Copyright 2019 David Conran - -// Provide a universal/standard interface for sending A/C nessages. -// It does not provide complete and maximum granular control but tries -// to off most common functionallity across all supported devices. - -#include "IRac.h" -#ifndef UNIT_TEST -#include -#endif - -#include -#ifndef ARDUINO -#include -#endif -#include "IRsend.h" -#include "IRremoteESP8266.h" -#include "ir_Argo.h" -#include "ir_Coolix.h" -#include "ir_Daikin.h" -#include "ir_Fujitsu.h" -#include "ir_Haier.h" -#include "ir_Hitachi.h" -#include "ir_Kelvinator.h" -#include "ir_Midea.h" -#include "ir_Mitsubishi.h" -#include "ir_MitsubishiHeavy.h" -#include "ir_Panasonic.h" -#include "ir_Samsung.h" -#include "ir_Tcl.h" -#include "ir_Teco.h" -#include "ir_Toshiba.h" -#include "ir_Trotec.h" -#include "ir_Vestel.h" -#include "ir_Whirlpool.h" - -IRac::IRac(uint8_t pin) { _pin = pin; } - -// Is the given protocol supported by the IRac class? -bool IRac::isProtocolSupported(const decode_type_t protocol) { - switch (protocol) { -#if SEND_ARGO - case decode_type_t::ARGO: -#endif -#if SEND_COOLIX - case decode_type_t::COOLIX: -#endif -#if SEND_DAIKIN - case decode_type_t::DAIKIN: -#endif -#if SEND_DAIKIN2 - case decode_type_t::DAIKIN2: -#endif -#if SEND_DAIKIN216 - case decode_type_t::DAIKIN216: -#endif -#if SEND_FUJITSU_AC - case decode_type_t::FUJITSU_AC: -#endif -#if SEND_GREE - case decode_type_t::GREE: -#endif -#if SEND_HAIER_AC - case decode_type_t::HAIER_AC: -#endif -#if SEND_HAIER_AC_YRW02 - case decode_type_t::HAIER_AC_YRW02: -#endif -#if SEND_HITACHI_AC - case decode_type_t::HITACHI_AC: -#endif -#if SEND_KELVINATOR - case decode_type_t::KELVINATOR: -#endif -#if SEND_MIDEA - case decode_type_t::MIDEA: -#endif -#if SEND_MITSUBISHI_AC - case decode_type_t::MITSUBISHI_AC: -#endif -#if SEND_MITSUBISHIHEAVY - case decode_type_t::MITSUBISHI_HEAVY_88: - case decode_type_t::MITSUBISHI_HEAVY_152: -#endif -#if SEND_PANASONIC_AC - case decode_type_t::PANASONIC_AC: -#endif -#if SEND_SAMSUNG_AC - case decode_type_t::SAMSUNG_AC: -#endif -#if SEND_TCL112AC - case decode_type_t::TCL112AC: -#endif -#if SEND_TECO - case decode_type_t::TECO: -#endif -#if SEND_TOSHIBA_AC - case decode_type_t::TOSHIBA_AC: -#endif -#if SEND_TROTEC - case decode_type_t::TROTEC: -#endif -#if SEND_VESTEL_AC - case decode_type_t::VESTEL_AC: -#endif -#if SEND_WHIRLPOOL_AC - case decode_type_t::WHIRLPOOL_AC: -#endif - return true; - default: - return false; - } -} - -#if SEND_ARGO -void IRac::argo(IRArgoAC *ac, - const bool on, const stdAc::opmode_t mode, const float degrees, - const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, - const bool turbo, const int16_t sleep) { - ac->setPower(on); - switch (mode) { - case stdAc::opmode_t::kCool: - ac->setCoolMode(kArgoCoolOn); - break; - case stdAc::opmode_t::kHeat: - ac->setHeatMode(kArgoHeatOn); - break; - case stdAc::opmode_t::kDry: - ac->setCoolMode(kArgoCoolHum); - break; - default: // No idea how to set Fan mode. - ac->setCoolMode(kArgoCoolAuto); - } - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setFlap(ac->convertSwingV(swingv)); - // No Quiet setting available. - // No Light setting available. - // No Filter setting available. - ac->setMax(turbo); - // No Economy setting available. - // No Clean setting available. - // No Beep setting available. - ac->setNight(sleep >= 0); // Convert to a boolean. - ac->send(); -} -#endif // SEND_ARGO - -#if SEND_COOLIX -void IRac::coolix(IRCoolixAC *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool turbo, const bool light, const bool clean, - const int16_t sleep) { - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - // No Filter setting available. - // No Beep setting available. - // No Clock setting available. - // No Econo setting available. - // No Quiet setting available. - if (swingv != stdAc::swingv_t::kOff || swingh != stdAc::swingh_t::kOff) { - // Swing has a special command that needs to be sent independently. - ac->setSwing(); - ac->send(); - } - if (turbo) { - // Turbo has a special command that needs to be sent independently. - ac->setTurbo(); - ac->send(); - } - if (sleep > 0) { - // Sleep has a special command that needs to be sent independently. - ac->setSleep(); - ac->send(); - } - if (light) { - // Light has a special command that needs to be sent independently. - ac->setLed(); - ac->send(); - } - if (clean) { - // Clean has a special command that needs to be sent independently. - ac->setClean(); - ac->send(); - } - // Power gets done last, as off has a special command. - ac->setPower(on); - ac->send(); -} -#endif // SEND_COOLIX - -#if SEND_DAIKIN -void IRac::daikin(IRDaikinESP *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool quiet, const bool turbo, const bool econo, - const bool clean) { - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingVertical((int8_t)swingv >= 0); - ac->setSwingHorizontal((int8_t)swingh >= 0); - ac->setQuiet(quiet); - // No Light setting available. - // No Filter setting available. - ac->setPowerful(turbo); - ac->setEcono(econo); - ac->setMold(clean); - // No Beep setting available. - // No Sleep setting available. - // No Clock setting available. - ac->send(); -} -#endif // SEND_DAIKIN - -#if SEND_DAIKIN2 -void IRac::daikin2(IRDaikin2 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool quiet, const bool turbo, const bool light, - const bool econo, const bool filter, const bool clean, - const bool beep, const int16_t sleep, const int16_t clock) { - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingVertical(ac->convertSwingV(swingv)); - ac->setSwingHorizontal((int8_t)swingh >= 0); - ac->setQuiet(quiet); - ac->setLight(light); - ac->setPowerful(turbo); - ac->setEcono(econo); - ac->setPurify(filter); - ac->setMold(clean); - ac->setBeep(beep); - if (sleep > 0) ac->enableSleepTimer(sleep); - if (clock >= 0) ac->setCurrentTime(clock); - ac->send(); -} -#endif // SEND_DAIKIN2 - -#if SEND_DAIKIN216 -void IRac::daikin216(IRDaikin216 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool quiet) { - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingVertical((int8_t)swingv >= 0); - ac->setSwingHorizontal((int8_t)swingh >= 0); - ac->setQuiet(quiet); - ac->send(); -} -#endif // SEND_DAIKIN216 - -#if SEND_FUJITSU_AC -void IRac::fujitsu(IRFujitsuAC *ac, const fujitsu_ac_remote_model_t model, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool quiet) { - ac->setModel(model); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFanSpeed(ac->convertFan(fan)); - uint8_t swing = kFujitsuAcSwingOff; - if (swingv > stdAc::swingv_t::kOff) swing |= kFujitsuAcSwingVert; - if (swingh > stdAc::swingh_t::kOff) swing |= kFujitsuAcSwingHoriz; - ac->setSwing(swing); - if (quiet) ac->setFanSpeed(kFujitsuAcFanQuiet); - // No Turbo setting available. - // No Light setting available. - // No Econo setting available. - // No Filter setting available. - // No Clean setting available. - // No Beep setting available. - // No Sleep setting available. - // No Clock setting available. - if (!on) ac->off(); - ac->send(); -} -#endif // SEND_FUJITSU_AC - -#if SEND_GREE -void IRac::gree(IRGreeAC *ac, - const bool on, const stdAc::opmode_t mode, const float degrees, - const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, - const bool turbo, const bool light, const bool clean, - const int16_t sleep) { - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingVertical(swingv == stdAc::swingv_t::kAuto, // Set auto flag. - ac->convertSwingV(swingv)); - ac->setLight(light); - ac->setTurbo(turbo); - ac->setXFan(clean); - ac->setSleep(sleep >= 0); // Sleep on this A/C is either on or off. - // No Horizontal Swing setting available. - // No Filter setting available. - // No Beep setting available. - // No Quiet setting available. - // No Clock setting available. - ac->send(); -} -#endif // SEND_GREE - -#if SEND_HAIER_AC -void IRac::haier(IRHaierAC *ac, - const bool on, const stdAc::opmode_t mode, const float degrees, - const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, - const bool filter, const int16_t sleep, const int16_t clock) { - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwing(ac->convertSwingV(swingv)); - // No Horizontal Swing setting available. - // No Quiet setting available. - // No Turbo setting available. - // No Light setting available. - ac->setHealth(filter); - // No Clean setting available. - // No Beep setting available. - ac->setSleep(sleep >= 0); // Sleep on this A/C is either on or off. - if (clock >=0) ac->setCurrTime(clock); - if (on) - ac->setCommand(kHaierAcCmdOn); - else - ac->setCommand(kHaierAcCmdOff); - ac->send(); -} -#endif // SEND_HAIER_AC - -#if SEND_HAIER_AC_YRW02 -void IRac::haierYrwo2(IRHaierACYRW02 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const bool turbo, - const bool filter, const int16_t sleep) { - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwing(ac->convertSwingV(swingv)); - // No Horizontal Swing setting available. - // No Quiet setting available. - ac->setTurbo(turbo); - // No Light setting available. - ac->setHealth(filter); - // No Clean setting available. - // No Beep setting available. - ac->setSleep(sleep >= 0); // Sleep on this A/C is either on or off. - ac->setPower(on); - ac->send(); -} -#endif // SEND_HAIER_AC_YRW02 - -#if SEND_HITACHI_AC -void IRac::hitachi(IRHitachiAc *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh) { - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingVertical(swingv != stdAc::swingv_t::kOff); - ac->setSwingHorizontal(swingh != stdAc::swingh_t::kOff); - // No Quiet setting available. - // No Turbo setting available. - // No Light setting available. - // No Filter setting available. - // No Clean setting available. - // No Beep setting available. - // No Sleep setting available. - // No Clock setting available. - ac->send(); -} -#endif // SEND_HITACHI_AC - -#if SEND_KELVINATOR -void IRac::kelvinator(IRKelvinatorAC *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, - const stdAc::swingh_t swingh, - const bool quiet, const bool turbo, const bool light, - const bool filter, const bool clean) { - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan((uint8_t)fan); // No conversion needed. - ac->setSwingVertical((int8_t)swingv >= 0); - ac->setSwingHorizontal((int8_t)swingh >= 0); - ac->setQuiet(quiet); - ac->setTurbo(turbo); - ac->setLight(light); - ac->setIonFilter(filter); - ac->setXFan(clean); - // No Beep setting available. - // No Sleep setting available. - // No Clock setting available. - ac->send(); -} -#endif // SEND_KELVINATOR - -#if SEND_MIDEA -void IRac::midea(IRMideaAC *ac, - const bool on, const stdAc::opmode_t mode, const float degrees, - const stdAc::fanspeed_t fan, const int16_t sleep) { - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees, true); // true means use Celsius. - ac->setFan(ac->convertFan(fan)); - // No Vertical swing setting available. - // No Horizontal swing setting available. - // No Quiet setting available. - // No Turbo setting available. - // No Light setting available. - // No Filter setting available. - // No Clean setting available. - // No Beep setting available. - ac->setSleep(sleep >= 0); // Sleep on this A/C is either on or off. - // No Clock setting available. - ac->send(); -} -#endif // SEND_MIDEA - -#if SEND_MITSUBISHI_AC -void IRac::mitsubishi(IRMitsubishiAC *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, - const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, - const bool quiet, const int16_t clock) { - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setVane(ac->convertSwingV(swingv)); - // No Horizontal swing setting available. - if (quiet) ac->setFan(kMitsubishiAcFanSilent); - // No Turbo setting available. - // No Light setting available. - // No Filter setting available. - // No Clean setting available. - // No Beep setting available. - // No Sleep setting available. - if (clock >= 0) ac->setClock(clock / 10); // Clock is in 10 min increments. - ac->send(); -} -#endif // SEND_MITSUBISHI_AC - -#if SEND_MITSUBISHIHEAVY -void IRac::mitsubishiHeavy88(IRMitsubishiHeavy88Ac *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, - const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, - const stdAc::swingh_t swingh, - const bool turbo, const bool econo, - const bool clean) { - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingVertical(ac->convertSwingV(swingv)); - ac->setSwingHorizontal(ac->convertSwingH(swingh)); - // No Quiet setting available. - ac->setTurbo(turbo); - // No Light setting available. - ac->setEcono(econo); - // No Filter setting available. - ac->setClean(clean); - // No Beep setting available. - // No Sleep setting available. - // No Clock setting available. - ac->send(); -} - -void IRac::mitsubishiHeavy152(IRMitsubishiHeavy152Ac *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, - const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, - const stdAc::swingh_t swingh, - const bool quiet, const bool turbo, - const bool econo, const bool filter, - const bool clean, const int16_t sleep) { - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingVertical(ac->convertSwingV(swingv)); - ac->setSwingHorizontal(ac->convertSwingH(swingh)); - ac->setSilent(quiet); - ac->setTurbo(turbo); - // No Light setting available. - ac->setEcono(econo); - ac->setClean(clean); - ac->setFilter(filter); - // No Beep setting available. - ac->setNight(sleep >= 0); // Sleep is either on/off, so convert to boolean. - // No Clock setting available. - ac->send(); -} -#endif // SEND_MITSUBISHIHEAVY - -#if SEND_PANASONIC_AC -void IRac::panasonic(IRPanasonicAc *ac, const panasonic_ac_remote_model_t model, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool quiet, const bool turbo, const int16_t clock) { - ac->setModel(model); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingVertical(ac->convertSwingV(swingv)); - ac->setSwingHorizontal(ac->convertSwingH(swingh)); - ac->setQuiet(quiet); - ac->setPowerful(turbo); - // No Light setting available. - // No Econo setting available. - // No Filter setting available. - // No Clean setting available. - // No Beep setting available. - // No Sleep setting available. - if (clock >= 0) ac->setClock(clock); - ac->send(); -} -#endif // SEND_PANASONIC_AC - -#if SEND_SAMSUNG_AC -void IRac::samsung(IRSamsungAc *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, - const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, - const bool quiet, const bool turbo, const bool clean, - const bool beep, const bool sendOnOffHack) { - if (sendOnOffHack) { - // Use a hack to for the unit on or off. - // See: https://github.com/markszabo/IRremoteESP8266/issues/604#issuecomment-475020036 - if (on) - ac->sendOn(); - else - ac->sendOff(); - } - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwing(swingv != stdAc::swingv_t::kOff); - // No Horizontal swing setting available. - ac->setQuiet(quiet); - if (turbo) ac->setFan(kSamsungAcFanTurbo); - // No Light setting available. - // No Econo setting available. - // No Filter setting available. - ac->setClean(clean); - ac->setBeep(beep); - // No Sleep setting available. - // No Clock setting available. - // Do setMode() again as it can affect fan speed. - ac->setMode(ac->convertMode(mode)); - ac->send(); -} -#endif // SEND_SAMSUNG_AC - -#if SEND_TCL112AC -void IRac::tcl112(IRTcl112Ac *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool turbo, const bool light, const bool econo, - const bool filter) { - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingVertical(swingv != stdAc::swingv_t::kOff); - ac->setSwingHorizontal(swingh != stdAc::swingh_t::kOff); - // No Quiet setting available. - ac->setTurbo(turbo); - ac->setLight(light); - ac->setEcono(econo); - ac->setHealth(filter); - // No Clean setting available. - // No Beep setting available. - // No Sleep setting available. - // No Clock setting available. - ac->send(); -} -#endif // SEND_TCL112AC - -#if SEND_TECO -void IRac::teco(IRTecoAc *ac, - const bool on, const stdAc::opmode_t mode, const float degrees, - const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, - const int16_t sleep) { - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwing(swingv != stdAc::swingv_t::kOff); - // No Horizontal swing setting available. - // No Quiet setting available. - // No Turbo setting available. - // No Light setting available. - // No Filter setting available. - // No Clean setting available. - // No Beep setting available. - ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. - // No Clock setting available. - ac->send(); -} -#endif // SEND_TECO - -#if SEND_TOSHIBA_AC -void IRac::toshiba(IRToshibaAC *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan) { - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - // No Vertical swing setting available. - // No Horizontal swing setting available. - // No Quiet setting available. - // No Turbo setting available. - // No Light setting available. - // No Filter setting available. - // No Clean setting available. - // No Beep setting available. - // No Sleep setting available. - // No Clock setting available. - ac->send(); -} -#endif // SEND_TOSHIBA_AC - -#if SEND_TROTEC -void IRac::trotec(IRTrotecESP *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const int16_t sleep) { - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setSpeed(ac->convertFan(fan)); - // No Vertical swing setting available. - // No Horizontal swing setting available. - // No Quiet setting available. - // No Turbo setting available. - // No Light setting available. - // No Filter setting available. - // No Clean setting available. - // No Beep setting available. - ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. - // No Clock setting available. - ac->send(); -} -#endif // SEND_TROTEC - -#if SEND_VESTEL_AC -void IRac::vestel(IRVestelAc *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, - const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, - const bool turbo, const bool filter, const int16_t sleep, - const int16_t clock, const bool sendNormal) { - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwing(swingv != stdAc::swingv_t::kOff); - // No Horizontal swing setting available. - // No Quiet setting available. - ac->setTurbo(turbo); - // No Light setting available. - ac->setIon(filter); - // No Clean setting available. - // No Beep setting available. - ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. - if (sendNormal) ac->send(); // Send the normal message. - if (clock >= 0) { - ac->setTime(clock); - ac->send(); // Setting the clock requires a different "timer" message. - } -} -#endif // SEND_VESTEL_AC - -#if SEND_WHIRLPOOL_AC -void IRac::whirlpool(IRWhirlpoolAc *ac, const whirlpool_ac_remote_model_t model, - const bool on, const stdAc::opmode_t mode, - const float degrees, - const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, - const bool turbo, const bool light, - const int16_t sleep, const int16_t clock) { - ac->setModel(model); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwing(swingv != stdAc::swingv_t::kOff); - // No Horizontal swing setting available. - // No Quiet setting available. - ac->setSuper(turbo); - ac->setLight(light); - // No Filter setting available - // No Clean setting available. - // No Beep setting available. - ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. - if (clock >= 0) ac->setClock(clock); - ac->setPowerToggle(on); - ac->send(); -} -#endif // SEND_WHIRLPOOL_AC - -// Send A/C message for a given device using common A/C settings. -// Args: -// vendor: The type of A/C protocol to use. -// model: The specific model of A/C if supported/applicable. -// on: Should the unit be powered on? (or in some cases, toggled) -// mode: What operating mode should the unit perform? e.g. Cool, Heat etc. -// degrees: What temperature should the unit be set to? -// celsius: Use degreees Celsius, otherwise Fahrenheit. -// fan: Fan speed. -// The following args are all "if supported" by the underlying A/C classes. -// swingv: Control the vertical swing of the vanes. -// swingh: Control the horizontal swing of the vanes. -// quiet: Set the unit to quiet (fan) operation mode. -// turbo: Set the unit to turbo operating mode. e.g. Max fan & cooling etc. -// econo: Set the unit to economical operating mode. -// light: Turn on the display/LEDs etc. -// filter: Turn on any particle/ion/allergy filter etc. -// clean: Turn on any settings to reduce mold etc. (Not self-clean mode.) -// beep: Control if the unit beeps upon receiving commands. -// sleep: Nr. of mins of sleep mode, or use sleep mode. (< 0 means off.) -// clock: Nr. of mins past midnight to set the clock to. (< 0 means off.) -// Returns: -// boolean: True, if accepted/converted/attempted. False, if unsupported. -bool IRac::sendAc(const decode_type_t vendor, const int16_t model, - const bool power, const stdAc::opmode_t mode, - const float degrees, const bool celsius, - const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool quiet, const bool turbo, const bool econo, - const bool light, const bool filter, const bool clean, - const bool beep, const int16_t sleep, const int16_t clock) { - // Convert the temperature to Celsius. - float degC; - bool on = power; - if (celsius) - degC = degrees; - else - degC = (degrees - 32.0) * (5.0 / 9.0); - // A hack for Home Assistant, it appears to need/want an Off opmode. - if (mode == stdAc::opmode_t::kOff) on = false; - // Per vendor settings & setup. - switch (vendor) { -#if SEND_ARGO - case ARGO: - { - IRArgoAC ac(_pin); - argo(&ac, on, mode, degC, fan, swingv, turbo, sleep); - break; - } -#endif // SEND_DAIKIN -#if SEND_COOLIX - case COOLIX: - { - IRCoolixAC ac(_pin); - coolix(&ac, on, mode, degC, fan, swingv, swingh, - quiet, turbo, econo, clean); - break; - } -#endif // SEND_DAIKIN -#if SEND_DAIKIN - case DAIKIN: - { - IRDaikinESP ac(_pin); - daikin(&ac, on, mode, degC, fan, swingv, swingh, - quiet, turbo, econo, clean); - break; - } -#endif // SEND_DAIKIN -#if SEND_DAIKIN2 - case DAIKIN2: - { - IRDaikin2 ac(_pin); - daikin2(&ac, on, mode, degC, fan, swingv, swingh, quiet, turbo, - light, econo, filter, clean, beep, sleep, clock); - break; - } -#endif // SEND_DAIKIN216 -#if SEND_DAIKIN216 - case DAIKIN216: - { - IRDaikin216 ac(_pin); - daikin216(&ac, on, mode, degC, fan, swingv, swingh, quiet); - break; - } -#endif // SEND_DAIKIN216 -#if SEND_FUJITSU_AC - case FUJITSU_AC: - { - IRFujitsuAC ac(_pin); - ac.begin(); - fujitsu(&ac, (fujitsu_ac_remote_model_t)model, on, mode, degC, fan, - swingv, swingh, quiet); - break; - } -#endif // SEND_FUJITSU_AC -#if SEND_GREE - case GREE: - { - IRGreeAC ac(_pin); - ac.begin(); - gree(&ac, on, mode, degC, fan, swingv, light, turbo, clean, sleep); - break; - } -#endif // SEND_GREE -#if SEND_HAIER_AC - case HAIER_AC: - { - IRHaierAC ac(_pin); - ac.begin(); - haier(&ac, on, mode, degC, fan, swingv, filter, sleep, clock); - break; - } -#endif // SEND_HAIER_AC -#if SEND_HAIER_AC_YRW02 - case HAIER_AC_YRW02: - { - IRHaierACYRW02 ac(_pin); - ac.begin(); - haierYrwo2(&ac, on, mode, degC, fan, swingv, turbo, filter, sleep); - break; - } -#endif // SEND_HAIER_AC_YRW02 -#if SEND_HITACHI_AC - case HITACHI_AC: - { - IRHitachiAc ac(_pin); - ac.begin(); - hitachi(&ac, on, mode, degC, fan, swingv, swingh); - break; - } -#endif // SEND_HITACHI_AC -#if SEND_KELVINATOR - case KELVINATOR: - { - IRKelvinatorAC ac(_pin); - ac.begin(); - kelvinator(&ac, on, mode, degC, fan, swingv, swingh, quiet, turbo, - light, filter, clean); - break; - } -#endif // SEND_KELVINATOR -#if SEND_MIDEA - case MIDEA: - { - IRMideaAC ac(_pin); - ac.begin(); - midea(&ac, on, mode, degC, fan, sleep); - break; - } -#endif // SEND_MIDEA -#if SEND_MITSUBISHI_AC - case MITSUBISHI_AC: - { - IRMitsubishiAC ac(_pin); - ac.begin(); - mitsubishi(&ac, on, mode, degC, fan, swingv, quiet, clock); - break; - } -#endif // SEND_MITSUBISHI_AC -#if SEND_MITSUBISHIHEAVY - case MITSUBISHI_HEAVY_88: - { - IRMitsubishiHeavy88Ac ac(_pin); - ac.begin(); - mitsubishiHeavy88(&ac, on, mode, degC, fan, swingv, swingh, - turbo, econo, clean); - break; - } - case MITSUBISHI_HEAVY_152: - { - IRMitsubishiHeavy152Ac ac(_pin); - ac.begin(); - mitsubishiHeavy152(&ac, on, mode, degC, fan, swingv, swingh, - quiet, turbo, econo, filter, clean, sleep); - break; - } -#endif // SEND_MITSUBISHIHEAVY -#if SEND_PANASONIC_AC - case PANASONIC_AC: - { - IRPanasonicAc ac(_pin); - ac.begin(); - panasonic(&ac, (panasonic_ac_remote_model_t)model, on, mode, degC, fan, - swingv, swingh, quiet, turbo, clock); - break; - } -#endif // SEND_PANASONIC_AC -#if SEND_SAMSUNG_AC - case SAMSUNG_AC: - { - IRSamsungAc ac(_pin); - ac.begin(); - samsung(&ac, on, mode, degC, fan, swingv, quiet, turbo, clean, beep); - break; - } -#endif // SEND_SAMSUNG_AC -#if SEND_TCL112AC - case TCL112AC: - { - IRTcl112Ac ac(_pin); - ac.begin(); - tcl112(&ac, on, mode, degC, fan, swingv, swingh, turbo, light, econo, - filter); - break; - } -#endif // SEND_TCL112AC -#if SEND_TECO - case TECO: - { - IRTecoAc ac(_pin); - ac.begin(); - teco(&ac, on, mode, degC, fan, swingv, sleep); - break; - } -#endif // SEND_TECO -#if SEND_TOSHIBA_AC - case TOSHIBA_AC: - { - IRToshibaAC ac(_pin); - ac.begin(); - toshiba(&ac, on, mode, degC, fan); - break; - } -#endif // SEND_TOSHIBA_AC -#if SEND_TROTEC - case TROTEC: - { - IRTrotecESP ac(_pin); - ac.begin(); - trotec(&ac, on, mode, degC, fan, sleep); - break; - } -#endif // SEND_TROTEC -#if SEND_VESTEL_AC - case VESTEL_AC: - { - IRVestelAc ac(_pin); - ac.begin(); - vestel(&ac, on, mode, degC, fan, swingv, turbo, filter, sleep, clock); - break; - } -#endif // SEND_VESTEL_AC -#if SEND_WHIRLPOOL_AC - case WHIRLPOOL_AC: - { - IRWhirlpoolAc ac(_pin); - ac.begin(); - whirlpool(&ac, (whirlpool_ac_remote_model_t)model, on, mode, degC, fan, - swingv, turbo, light, sleep, clock); - break; - } -#endif // SEND_WHIRLPOOL_AC - default: - return false; // Fail, didn't match anything. - } - return true; // Success. -} - -stdAc::opmode_t IRac::strToOpmode(const char *str, - const stdAc::opmode_t def) { - if (!strcmp(str, "AUTO") || !strcmp(str, "AUTOMATIC")) - return stdAc::opmode_t::kAuto; - else if (!strcmp(str, "OFF") || !strcmp(str, "STOP")) - return stdAc::opmode_t::kOff; - else if (!strcmp(str, "COOL") || !strcmp(str, "COOLING")) - return stdAc::opmode_t::kCool; - else if (!strcmp(str, "HEAT") || !strcmp(str, "HEATING")) - return stdAc::opmode_t::kHeat; - else if (!strcmp(str, "DRY") || !strcmp(str, "DRYING") || - !strcmp(str, "DEHUMIDIFY")) - return stdAc::opmode_t::kDry; - else if (!strcmp(str, "FAN") || !strcmp(str, "FANONLY") || - !strcmp(str, "FAN_ONLY")) - return stdAc::opmode_t::kFan; - else - return def; -} - -stdAc::fanspeed_t IRac::strToFanspeed(const char *str, - const stdAc::fanspeed_t def) { - if (!strcmp(str, "AUTO") || !strcmp(str, "AUTOMATIC")) - return stdAc::fanspeed_t::kAuto; - else if (!strcmp(str, "MIN") || !strcmp(str, "MINIMUM") || - !strcmp(str, "LOWEST")) - return stdAc::fanspeed_t::kMin; - else if (!strcmp(str, "LOW")) - return stdAc::fanspeed_t::kLow; - else if (!strcmp(str, "MED") || !strcmp(str, "MEDIUM") || - !strcmp(str, "MID")) - return stdAc::fanspeed_t::kMedium; - else if (!strcmp(str, "HIGH") || !strcmp(str, "HI")) - return stdAc::fanspeed_t::kHigh; - else if (!strcmp(str, "MAX") || !strcmp(str, "MAXIMUM") || - !strcmp(str, "HIGHEST")) - return stdAc::fanspeed_t::kMax; - else - return def; -} - -stdAc::swingv_t IRac::strToSwingV(const char *str, - const stdAc::swingv_t def) { - if (!strcmp(str, "AUTO") || !strcmp(str, "AUTOMATIC") || - !strcmp(str, "ON") || !strcmp(str, "SWING")) - return stdAc::swingv_t::kAuto; - else if (!strcmp(str, "OFF") || !strcmp(str, "STOP")) - return stdAc::swingv_t::kOff; - else if (!strcmp(str, "MIN") || !strcmp(str, "MINIMUM") || - !strcmp(str, "LOWEST") || !strcmp(str, "BOTTOM") || - !strcmp(str, "DOWN")) - return stdAc::swingv_t::kLowest; - else if (!strcmp(str, "LOW")) - return stdAc::swingv_t::kLow; - else if (!strcmp(str, "MID") || !strcmp(str, "MIDDLE") || - !strcmp(str, "MED") || !strcmp(str, "MEDIUM") || - !strcmp(str, "CENTRE") || !strcmp(str, "CENTER")) - return stdAc::swingv_t::kMiddle; - else if (!strcmp(str, "HIGH") || !strcmp(str, "HI")) - return stdAc::swingv_t::kHigh; - else if (!strcmp(str, "HIGHEST") || !strcmp(str, "MAX") || - !strcmp(str, "MAXIMUM") || !strcmp(str, "TOP") || - !strcmp(str, "UP")) - return stdAc::swingv_t::kHighest; - else - return def; -} - -stdAc::swingh_t IRac::strToSwingH(const char *str, - const stdAc::swingh_t def) { - if (!strcmp(str, "AUTO") || !strcmp(str, "AUTOMATIC") || - !strcmp(str, "ON") || !strcmp(str, "SWING")) - return stdAc::swingh_t::kAuto; - else if (!strcmp(str, "OFF") || !strcmp(str, "STOP")) - return stdAc::swingh_t::kOff; - else if (!strcmp(str, "LEFTMAX") || !strcmp(str, "LEFT MAX") || - !strcmp(str, "MAXLEFT") || !strcmp(str, "MAX LEFT") || - !strcmp(str, "FARLEFT") || !strcmp(str, "FAR LEFT")) - return stdAc::swingh_t::kLeftMax; - else if (!strcmp(str, "LEFT")) - return stdAc::swingh_t::kLeft; - else if (!strcmp(str, "MID") || !strcmp(str, "MIDDLE") || - !strcmp(str, "MED") || !strcmp(str, "MEDIUM") || - !strcmp(str, "CENTRE") || !strcmp(str, "CENTER")) - return stdAc::swingh_t::kMiddle; - else if (!strcmp(str, "RIGHT")) - return stdAc::swingh_t::kRight; - else if (!strcmp(str, "RIGHTMAX") || !strcmp(str, "RIGHT MAX") || - !strcmp(str, "MAXRIGHT") || !strcmp(str, "MAX RIGHT") || - !strcmp(str, "FARRIGHT") || !strcmp(str, "FAR RIGHT")) - return stdAc::swingh_t::kRightMax; - else - return def; -} - -// Assumes str is upper case or an integer >= 1. -int16_t IRac::strToModel(const char *str, const int16_t def) { - // Fujitsu A/C models - if (!strcmp(str, "ARRAH2E")) { - return fujitsu_ac_remote_model_t::ARRAH2E; - } else if (!strcmp(str, "ARDB1")) { - return fujitsu_ac_remote_model_t::ARDB1; - // Panasonic A/C families - } else if (!strcmp(str, "LKE") || !strcmp(str, "PANASONICLKE")) { - return panasonic_ac_remote_model_t::kPanasonicLke; - } else if (!strcmp(str, "NKE") || !strcmp(str, "PANASONICNKE")) { - return panasonic_ac_remote_model_t::kPanasonicNke; - } else if (!strcmp(str, "DKE") || !strcmp(str, "PANASONICDKE")) { - return panasonic_ac_remote_model_t::kPanasonicDke; - } else if (!strcmp(str, "JKE") || !strcmp(str, "PANASONICJKE")) { - return panasonic_ac_remote_model_t::kPanasonicJke; - } else if (!strcmp(str, "CKP") || !strcmp(str, "PANASONICCKP")) { - return panasonic_ac_remote_model_t::kPanasonicCkp; - } else if (!strcmp(str, "RKR") || !strcmp(str, "PANASONICRKR")) { - return panasonic_ac_remote_model_t::kPanasonicRkr; - // Whirlpool A/C models - } else if (!strcmp(str, "DG11J13A") || !strcmp(str, "DG11J104") || - !strcmp(str, "DG11J1-04")) { - return whirlpool_ac_remote_model_t::DG11J13A; - } else if (!strcmp(str, "DG11J191")) { - return whirlpool_ac_remote_model_t::DG11J191; - } else { - int16_t number = atoi(str); - if (number > 0) - return number; - else - return def; - } -} - -// Assumes str is upper case. -bool IRac::strToBool(const char *str, const bool def) { - if (!strcmp(str, "ON") || !strcmp(str, "1") || !strcmp(str, "YES") || - !strcmp(str, "TRUE")) - return true; - else if (!strcmp(str, "OFF") || !strcmp(str, "0") || - !strcmp(str, "NO") || !strcmp(str, "FALSE")) - return false; - else - return def; -} diff --git a/lib/IRremoteESP8266-2.6.0/src/IRutils.h b/lib/IRremoteESP8266-2.6.0/src/IRutils.h deleted file mode 100644 index 0d0b677b5..000000000 --- a/lib/IRremoteESP8266-2.6.0/src/IRutils.h +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef IRUTILS_H_ -#define IRUTILS_H_ - -// Copyright 2017 David Conran - -#ifndef UNIT_TEST -#include -#endif -#define __STDC_LIMIT_MACROS -#include -#ifndef ARDUINO -#include -#endif -#include "IRremoteESP8266.h" -#include "IRrecv.h" - -uint64_t reverseBits(uint64_t input, uint16_t nbits); -#ifdef ARDUINO // Arduino's & C++'s string implementations can't co-exist. -String uint64ToString(uint64_t input, uint8_t base = 10); -String typeToString(const decode_type_t protocol, - const bool isRepeat = false); -void serialPrintUint64(uint64_t input, uint8_t base = 10); -String resultToSourceCode(const decode_results *results); -String resultToTimingInfo(const decode_results *results); -String resultToHumanReadableBasic(const decode_results *results); -String resultToHexidecimal(const decode_results *result); -String htmlEscape(const String unescaped); -#else // ARDUINO -std::string uint64ToString(uint64_t input, uint8_t base = 10); -std::string typeToString(const decode_type_t protocol, - const bool isRepeat = false); -std::string resultToSourceCode(const decode_results *results); -std::string resultToTimingInfo(const decode_results *results); -std::string resultToHumanReadableBasic(const decode_results *results); -std::string resultToHexidecimal(const decode_results *result); -std::string htmlEscape(const std::string unescaped); -#endif // ARDUINO -bool hasACState(const decode_type_t protocol); -uint16_t getCorrectedRawLength(const decode_results *results); -uint8_t sumBytes(uint8_t *start, const uint16_t length, const uint8_t init = 0); -uint8_t xorBytes(uint8_t *start, const uint16_t length, const uint8_t init = 0); -uint16_t countBits(const uint8_t *start, const uint16_t length, - const bool ones = true, const uint16_t init = 0); -uint16_t countBits(const uint64_t data, const uint8_t length, - const bool ones = true, const uint16_t init = 0); -uint64_t invertBits(const uint64_t data, const uint16_t nbits); -decode_type_t strToDecodeType(const char *str); -#endif // IRUTILS_H_ diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Argo.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Argo.cpp deleted file mode 100644 index d6711acd3..000000000 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Argo.cpp +++ /dev/null @@ -1,264 +0,0 @@ -/* -Node MCU/ESP8266 Sketch to emulate Argo Ulisse 13 DCI remote -Controls Argo Ulisse 13 DCI A/C -Copyright 2017 Schmolders -*/ - -#include "ir_Argo.h" -#include -#include "IRremoteESP8266.h" -#include "IRutils.h" - -// Constants -// using SPACE modulation. MARK is always const 400u -const uint16_t kArgoHdrMark = 6400; -const uint16_t kArgoHdrSpace = 3300; -const uint16_t kArgoBitMark = 400; -const uint16_t kArgoOneSpace = 2200; -const uint16_t kArgoZeroSpace = 900; - -#if SEND_ARGO -// Send an Argo A/C message. -// -// Args: -// data: An array of kArgoStateLength bytes containing the IR command. -// -// Status: ALPHA / Untested. - -void IRsend::sendArgo(unsigned char data[], uint16_t nbytes, uint16_t repeat) { - // Check if we have enough bytes to send a proper message. - if (nbytes < kArgoStateLength) return; - // TODO(kaschmo): validate - sendGeneric(kArgoHdrMark, kArgoHdrSpace, kArgoBitMark, kArgoOneSpace, - kArgoBitMark, kArgoZeroSpace, 0, 0, // No Footer. - data, nbytes, 38, false, repeat, kDutyDefault); -} -#endif // SEND_ARGO - -IRArgoAC::IRArgoAC(uint16_t pin) : _irsend(pin) { stateReset(); } - -void IRArgoAC::begin() { _irsend.begin(); } - -#if SEND_ARGO -void IRArgoAC::send(const uint16_t repeat) { - checksum(); // Create valid checksum before sending - _irsend.sendArgo(argo, kArgoStateLength, repeat); -} -#endif // SEND_ARGO - -void IRArgoAC::checksum() { - uint8_t sum = 2; // Corresponds to byte 11 being constant 0b01 - uint8_t i; - - // Only add up bytes to 9. byte 10 is 0b01 constant anyway. - // Assume that argo array is MSB first (left) - for (i = 0; i < 10; i++) sum += argo[i]; - - sum = sum % 256; // modulo 256 - // Append sum to end of array - // Set const part of checksum bit 10 - argo[10] = 0b00000010; - argo[10] += sum << 2; // Shift up 2 bits and append to byte 10 - argo[11] = sum >> 6; // Shift down 6 bits and add in two LSBs of bit 11 -} - -void IRArgoAC::stateReset() { - for (uint8_t i = 0; i < kArgoStateLength; i++) argo[i] = 0x0; - - // Argo Message. Store MSB left. - // Default message: - argo[0] = 0b10101100; // LSB first (as sent) 0b00110101; //const preamble - argo[1] = 0b11110101; // LSB first: 0b10101111; //const preamble - // Keep payload 2-9 at zero - argo[10] = 0b00000010; // Const 01, checksum 6bit - argo[11] = 0b00000000; // Checksum 2bit - - this->off(); - this->setTemp(20); - this->setRoomTemp(25); - this->setCoolMode(kArgoCoolAuto); - this->setFan(kArgoFanAuto); -} - -uint8_t* IRArgoAC::getRaw() { - checksum(); // Ensure correct bit array before returning - return argo; -} - -void IRArgoAC::on() { - // state = ON; - ac_state = 1; - // Bit 5 of byte 9 is on/off - // in MSB first - argo[9] = argo[9] | 0b00100000; // Set ON/OFF bit to 1 -} - -void IRArgoAC::off() { - // state = OFF; - ac_state = 0; - // in MSB first - // bit 5 of byte 9 to off - argo[9] = argo[9] & 0b11011111; // Set on/off bit to 0 -} - -void IRArgoAC::setPower(bool state) { - if (state) - on(); - else - off(); -} - -uint8_t IRArgoAC::getPower() { return ac_state; } - -void IRArgoAC::setMax(bool state) { - max_mode = state; - if (max_mode) - argo[9] |= 0b00001000; - else - argo[9] &= 0b11110111; -} - -bool IRArgoAC::getMax() { return max_mode; } - -// Set the temp in deg C -// Sending 0 equals +4 -void IRArgoAC::setTemp(uint8_t temp) { - if (temp < kArgoMinTemp) - temp = kArgoMinTemp; - else if (temp > kArgoMaxTemp) - temp = kArgoMaxTemp; - - // Store in attributes - set_temp = temp; - // offset 4 degrees. "If I want 12 degrees, I need to send 8" - temp -= 4; - // Settemp = Bit 6,7 of byte 2, and bit 0-2 of byte 3 - // mask out bits - // argo[13] & 0x00000100; // mask out ON/OFF Bit - argo[2] &= 0b00111111; - argo[3] &= 0b11111000; - - argo[2] += temp << 6; // append to bit 6,7 - argo[3] += temp >> 2; // remove lowest to bits and append in 0-2 -} - -uint8_t IRArgoAC::getTemp() { return set_temp; } - -// Set the speed of the fan -void IRArgoAC::setFan(uint8_t fan) { - // Set the fan speed bits, leave low 4 bits alone - fan_mode = fan; - // Mask out bits - argo[3] &= 0b11100111; - // Set fan mode at bit positions - argo[3] += fan << 3; -} - -uint8_t IRArgoAC::getFan() { return fan_mode; } - -void IRArgoAC::setFlap(uint8_t flap) { - flap_mode = flap; - // TODO(kaschmo): set correct bits for flap mode -} - -uint8_t IRArgoAC::getFlap() { return flap_mode; } - -uint8_t IRArgoAC::getMode() { - // return cooling 0, heating 1 - return ac_mode; -} - -void IRArgoAC::setCoolMode(uint8_t mode) { - ac_mode = 0; // Set ac mode to cooling - cool_mode = mode; - // Mask out bits, also leave bit 5 on 0 for cooling - argo[2] &= 0b11000111; - - // Set cool mode at bit positions - argo[2] += mode << 3; -} - -uint8_t IRArgoAC::getCoolMode() { return cool_mode; } - -void IRArgoAC::setHeatMode(uint8_t mode) { - ac_mode = 1; // Set ac mode to heating - heat_mode = mode; - // Mask out bits - argo[2] &= 0b11000111; - // Set heating bit - argo[2] |= 0b00100000; - // Set cool mode at bit positions - argo[2] += mode << 3; -} - -uint8_t IRArgoAC::getHeatMode() { return heat_mode; } - -void IRArgoAC::setNight(bool state) { - night_mode = state; - if (night_mode) - // Set bit at night position: bit 2 - argo[9] |= 0b00000100; - else - argo[9] &= 0b11111011; -} - -bool IRArgoAC::getNight() { return night_mode; } - -void IRArgoAC::setiFeel(bool state) { - ifeel_mode = state; - if (ifeel_mode) - // Set bit at iFeel position: bit 7 - argo[9] |= 0b10000000; - else - argo[9] &= 0b01111111; -} - -bool IRArgoAC::getiFeel() { return ifeel_mode; } - -void IRArgoAC::setTime() { - // TODO(kaschmo): use function call from checksum to set time first -} - -void IRArgoAC::setRoomTemp(uint8_t temp) { - temp -= 4; - // Mask out bits - argo[3] &= 0b00011111; - argo[4] &= 0b11111100; - - argo[3] += temp << 5; // Append to bit 5,6,7 - argo[4] += temp >> 3; // Remove lowest 3 bits and append in 0,1 -} - -// Convert a standard A/C Fan speed into its native fan speed. -uint8_t IRArgoAC::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: - case stdAc::fanspeed_t::kLow: - return kArgoFan1; - case stdAc::fanspeed_t::kMedium: - return kArgoFan2; - case stdAc::fanspeed_t::kHigh: - case stdAc::fanspeed_t::kMax: - return kArgoFan3; - default: - return kArgoFanAuto; - } -} - -// Convert a standard A/C Fan speed into its native fan speed. -uint8_t IRArgoAC::convertSwingV(const stdAc::swingv_t position) { - switch (position) { - case stdAc::swingv_t::kHighest: - return kArgoFlapFull; - case stdAc::swingv_t::kHigh: - return kArgoFlap5; - case stdAc::swingv_t::kMiddle: - return kArgoFlap4; - case stdAc::swingv_t::kLow: - return kArgoFlap3; - case stdAc::swingv_t::kLowest: - return kArgoFlap1; - default: - return kArgoFlapAuto; - } -} diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Daikin.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Daikin.cpp deleted file mode 100644 index 358dbd603..000000000 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Daikin.cpp +++ /dev/null @@ -1,1712 +0,0 @@ -/* -An Arduino sketch to emulate IR Daikin ARC433** & ARC477A1 remote control unit -Read more at: -http://harizanov.com/2012/02/control-daikin-air-conditioner-over-the-internet/ - -Copyright 2016 sillyfrog -Copyright 2017 sillyfrog, crankyoldgit -Copyright 2018-2019 crankyoldgit -*/ - -#include "ir_Daikin.h" -#include -#ifndef ARDUINO -#include -#endif -#include "IRrecv.h" -#include "IRremoteESP8266.h" -#include "IRsend.h" -#ifdef UNIT_TEST -#include "IRsend_test.h" -#endif -#include "IRutils.h" - -// DDDDD AAA IIIII KK KK IIIII NN NN -// DD DD AAAAA III KK KK III NNN NN -// DD DD AA AA III KKKK III NN N NN -// DD DD AAAAAAA III KK KK III NN NNN -// DDDDDD AA AA IIIII KK KK IIIII NN NN - -// Constants -// Ref: -// https://github.com/mharizanov/Daikin-AC-remote-control-over-the-Internet/tree/master/IRremote -// http://rdlab.cdmt.vn/project-2013/daikin-ir-protocol -// https://github.com/markszabo/IRremoteESP8266/issues/582 - -#if SEND_DAIKIN -// Send a Daikin A/C message. -// -// Args: -// data: An array of kDaikinStateLength bytes containing the IR command. -// -// Status: STABLE -// -// Ref: -// IRDaikinESP.cpp -// https://github.com/mharizanov/Daikin-AC-remote-control-over-the-Internet/tree/master/IRremote -// https://github.com/blafois/Daikin-IR-Reverse -void IRsend::sendDaikin(const unsigned char data[], const uint16_t nbytes, - const uint16_t repeat) { - if (nbytes < kDaikinStateLengthShort) - return; // Not enough bytes to send a proper message. - - for (uint16_t r = 0; r <= repeat; r++) { - uint16_t offset = 0; - // Send the header, 0b00000 - sendGeneric(0, 0, // No header for the header - kDaikinBitMark, kDaikinOneSpace, kDaikinBitMark, - kDaikinZeroSpace, kDaikinBitMark, kDaikinZeroSpace + kDaikinGap, - (uint64_t)0b00000, kDaikinHeaderLength, 38, false, 0, 50); - // Data #1 - if (nbytes < kDaikinStateLength) { // Are we using the legacy size? - // Do this as a constant to save RAM and keep in flash memory - sendGeneric(kDaikinHdrMark, kDaikinHdrSpace, kDaikinBitMark, - kDaikinOneSpace, kDaikinBitMark, kDaikinZeroSpace, - kDaikinBitMark, kDaikinZeroSpace + kDaikinGap, - kDaikinFirstHeader64, 64, 38, false, 0, 50); - } else { // We are using the newer/more correct size. - sendGeneric(kDaikinHdrMark, kDaikinHdrSpace, kDaikinBitMark, - kDaikinOneSpace, kDaikinBitMark, kDaikinZeroSpace, - kDaikinBitMark, kDaikinZeroSpace + kDaikinGap, - data, kDaikinSection1Length, 38, false, 0, 50); - offset += kDaikinSection1Length; - } - // Data #2 - sendGeneric(kDaikinHdrMark, kDaikinHdrSpace, kDaikinBitMark, - kDaikinOneSpace, kDaikinBitMark, kDaikinZeroSpace, - kDaikinBitMark, kDaikinZeroSpace + kDaikinGap, - data + offset, kDaikinSection2Length, 38, false, 0, 50); - offset += kDaikinSection2Length; - // Data #3 - sendGeneric(kDaikinHdrMark, kDaikinHdrSpace, kDaikinBitMark, - kDaikinOneSpace, kDaikinBitMark, kDaikinZeroSpace, - kDaikinBitMark, kDaikinZeroSpace + kDaikinGap, - data + offset, nbytes - offset, 38, false, 0, 50); - } -} -#endif // SEND_DAIKIN - -IRDaikinESP::IRDaikinESP(uint16_t pin) : _irsend(pin) { stateReset(); } - -void IRDaikinESP::begin(void) { _irsend.begin(); } - -#if SEND_DAIKIN -void IRDaikinESP::send(const uint16_t repeat) { - this->checksum(); - _irsend.sendDaikin(remote, kDaikinStateLength, repeat); -} -#endif // SEND_DAIKIN - -// Verify the checksums are valid for a given state. -// Args: -// state: The array to verify the checksums of. -// length: The size of the state. -// Returns: -// A boolean. -bool IRDaikinESP::validChecksum(uint8_t state[], const uint16_t length) { - // Data #1 - if (length < kDaikinSection1Length || - state[kDaikinByteChecksum1] != sumBytes(state, kDaikinSection1Length - 1)) - return false; - // Data #2 - if (length < kDaikinSection1Length + kDaikinSection2Length || - state[kDaikinByteChecksum2] != sumBytes(state + kDaikinSection1Length, - kDaikinSection2Length - 1)) - return false; - // Data #3 - if (length < kDaikinSection1Length + kDaikinSection2Length + 2 || - state[length - 1] != sumBytes(state + kDaikinSection1Length + - kDaikinSection2Length, - length - (kDaikinSection1Length + - kDaikinSection2Length) - 1)) - return false; - return true; -} - -// Calculate and set the checksum values for the internal state. -void IRDaikinESP::checksum(void) { - remote[kDaikinByteChecksum1] = sumBytes(remote, kDaikinSection1Length - 1); - remote[kDaikinByteChecksum2] = sumBytes(remote + kDaikinSection1Length, - kDaikinSection2Length - 1); - remote[kDaikinByteChecksum3] = sumBytes(remote + kDaikinSection1Length + - kDaikinSection2Length, - kDaikinSection3Length - 1); -} - -void IRDaikinESP::stateReset(void) { - for (uint8_t i = 0; i < kDaikinStateLength; i++) remote[i] = 0x0; - - remote[0] = 0x11; - remote[1] = 0xDA; - remote[2] = 0x27; - remote[4] = 0xC5; - // remote[7] is a checksum byte, it will be set by checksum(). - - remote[8] = 0x11; - remote[9] = 0xDA; - remote[10] = 0x27; - remote[12] = 0x42; - // remote[15] is a checksum byte, it will be set by checksum(). - remote[16] = 0x11; - remote[17] = 0xDA; - remote[18] = 0x27; - remote[21] = 0x49; - remote[22] = 0x1E; - remote[24] = 0xB0; - remote[27] = 0x06; - remote[28] = 0x60; - remote[31] = 0xC0; - // remote[34] is a checksum byte, it will be set by checksum(). - this->checksum(); -} - -uint8_t *IRDaikinESP::getRaw(void) { - this->checksum(); // Ensure correct settings before sending. - return remote; -} - -void IRDaikinESP::setRaw(const uint8_t new_code[], const uint16_t length) { - uint8_t offset = 0; - if (length == kDaikinStateLengthShort) { // Handle the "short" length case. - offset = kDaikinStateLength - kDaikinStateLengthShort; - this->stateReset(); - } - for (uint8_t i = 0; i < length && i < kDaikinStateLength; i++) - remote[i + offset] = new_code[i]; -} - -void IRDaikinESP::on(void) { remote[kDaikinBytePower] |= kDaikinBitPower; } - -void IRDaikinESP::off(void) { remote[kDaikinBytePower] &= ~kDaikinBitPower; } - -void IRDaikinESP::setPower(const bool on) { - if (on) - this->on(); - else - this->off(); -} - -bool IRDaikinESP::getPower(void) { - return remote[kDaikinBytePower] & kDaikinBitPower; -} - -// Set the temp in deg C -void IRDaikinESP::setTemp(const uint8_t temp) { - uint8_t degrees = std::max(temp, kDaikinMinTemp); - degrees = std::min(degrees, kDaikinMaxTemp); - remote[kDaikinByteTemp] = degrees << 1; -} - -uint8_t IRDaikinESP::getTemp(void) { return remote[kDaikinByteTemp] >> 1; } - -// Set the speed of the fan, 1-5 or kDaikinFanAuto or kDaikinFanQuiet -void IRDaikinESP::setFan(const uint8_t fan) { - // Set the fan speed bits, leave low 4 bits alone - uint8_t fanset; - if (fan == kDaikinFanQuiet || fan == kDaikinFanAuto) - fanset = fan; - else if (fan < kDaikinFanMin || fan > kDaikinFanMax) - fanset = kDaikinFanAuto; - else - fanset = 2 + fan; - remote[kDaikinByteFan] &= 0x0F; - remote[kDaikinByteFan] |= (fanset << 4); -} - -uint8_t IRDaikinESP::getFan(void) { - uint8_t fan = remote[kDaikinByteFan] >> 4; - if (fan != kDaikinFanQuiet && fan != kDaikinFanAuto) fan -= 2; - return fan; -} - -uint8_t IRDaikinESP::getMode(void) { return remote[kDaikinBytePower] >> 4; } - -void IRDaikinESP::setMode(const uint8_t mode) { - switch (mode) { - case kDaikinAuto: - case kDaikinCool: - case kDaikinHeat: - case kDaikinFan: - case kDaikinDry: - remote[kDaikinBytePower] &= 0b10001111; - remote[kDaikinBytePower] |= (mode << 4); - break; - default: - this->setMode(kDaikinAuto); - } -} - -void IRDaikinESP::setSwingVertical(const bool on) { - if (on) - remote[kDaikinByteFan] |= 0x0F; - else - remote[kDaikinByteFan] &= 0xF0; -} - -bool IRDaikinESP::getSwingVertical(void) { - return remote[kDaikinByteFan] & 0x0F; -} - -void IRDaikinESP::setSwingHorizontal(const bool on) { - if (on) - remote[kDaikinByteSwingH] |= 0x0F; - else - remote[kDaikinByteSwingH] &= 0xF0; -} - -bool IRDaikinESP::getSwingHorizontal(void) { - return remote[kDaikinByteSwingH] & 0x0F; -} - -void IRDaikinESP::setQuiet(const bool on) { - if (on) { - remote[kDaikinByteSilent] |= kDaikinBitSilent; - // Powerful & Quiet mode being on are mutually exclusive. - this->setPowerful(false); - } else { - remote[kDaikinByteSilent] &= ~kDaikinBitSilent; - } -} - -bool IRDaikinESP::getQuiet(void) { - return remote[kDaikinByteSilent] & kDaikinBitSilent; -} - -void IRDaikinESP::setPowerful(const bool on) { - if (on) { - remote[kDaikinBytePowerful] |= kDaikinBitPowerful; - // Powerful, Quiet, & Econo mode being on are mutually exclusive. - this->setQuiet(false); - this->setEcono(false); - } else { - remote[kDaikinBytePowerful] &= ~kDaikinBitPowerful; - } -} - -bool IRDaikinESP::getPowerful(void) { - return remote[kDaikinBytePowerful] & kDaikinBitPowerful; -} - -void IRDaikinESP::setSensor(const bool on) { - if (on) - remote[kDaikinByteSensor] |= kDaikinBitSensor; - else - remote[kDaikinByteSensor] &= ~kDaikinBitSensor; -} - -bool IRDaikinESP::getSensor(void) { - return remote[kDaikinByteSensor] & kDaikinBitSensor; -} - -void IRDaikinESP::setEcono(const bool on) { - if (on) { - remote[kDaikinByteEcono] |= kDaikinBitEcono; - // Powerful & Econo mode being on are mutually exclusive. - this->setPowerful(false); - } else { - remote[kDaikinByteEcono] &= ~kDaikinBitEcono; - } -} - -bool IRDaikinESP::getEcono(void) { - return remote[kDaikinByteEcono] & kDaikinBitEcono; -} - -void IRDaikinESP::setEye(const bool on) { - if (on) - remote[kDaikinByteEye] |= kDaikinBitEye; - else - remote[kDaikinByteEye] &= ~kDaikinBitEye; -} - -bool IRDaikinESP::getEye(void) { - return remote[kDaikinByteEye] & kDaikinBitEye; -} - -void IRDaikinESP::setMold(const bool on) { - if (on) - remote[kDaikinByteMold] |= kDaikinBitMold; - else - remote[kDaikinByteMold] &= ~kDaikinBitMold; -} - -bool IRDaikinESP::getMold(void) { - return remote[kDaikinByteMold] & kDaikinBitMold; -} - -void IRDaikinESP::setComfort(const bool on) { - if (on) - remote[kDaikinByteComfort] |= kDaikinBitComfort; - else - remote[kDaikinByteComfort] &= ~kDaikinBitComfort; -} - -bool IRDaikinESP::getComfort(void) { - return remote[kDaikinByteComfort] & kDaikinBitComfort; -} - -// starttime: Number of minutes after midnight. -void IRDaikinESP::enableOnTimer(const uint16_t starttime) { - remote[kDaikinByteOnTimer] |= kDaikinBitOnTimer; - remote[kDaikinByteOnTimerMinsLow] = starttime; - // only keep 4 bits - remote[kDaikinByteOnTimerMinsHigh] &= 0xF0; - remote[kDaikinByteOnTimerMinsHigh] |= ((starttime >> 8) & 0x0F); -} - -void IRDaikinESP::disableOnTimer(void) { - this->enableOnTimer(kDaikinUnusedTime); - remote[kDaikinByteOnTimer] &= ~kDaikinBitOnTimer; -} - -uint16_t IRDaikinESP::getOnTime(void) { - return ((remote[kDaikinByteOnTimerMinsHigh] & 0x0F) << 8) + - remote[kDaikinByteOnTimerMinsLow]; -} - -bool IRDaikinESP::getOnTimerEnabled(void) { - return remote[kDaikinByteOnTimer] & kDaikinBitOnTimer; -} - -// endtime: Number of minutes after midnight. -void IRDaikinESP::enableOffTimer(const uint16_t endtime) { - remote[kDaikinByteOffTimer] |= kDaikinBitOffTimer; - remote[kDaikinByteOffTimerMinsHigh] = endtime >> 4; - remote[kDaikinByteOffTimerMinsLow] &= 0x0F; - remote[kDaikinByteOffTimerMinsLow] |= ((endtime & 0x0F) << 4); -} - -void IRDaikinESP::disableOffTimer(void) { - this->enableOffTimer(kDaikinUnusedTime); - remote[kDaikinByteOffTimer] &= ~kDaikinBitOffTimer; -} - -uint16_t IRDaikinESP::getOffTime(void) { - return (remote[kDaikinByteOffTimerMinsHigh] << 4) + - ((remote[kDaikinByteOffTimerMinsLow] & 0xF0) >> 4); -} - -bool IRDaikinESP::getOffTimerEnabled(void) { - return remote[kDaikinByteOffTimer] & kDaikinBitOffTimer; -} - -void IRDaikinESP::setCurrentTime(const uint16_t mins_since_midnight) { - uint16_t mins = mins_since_midnight; - if (mins > 24 * 60) mins = 0; // If > 23:59, set to 00:00 - remote[kDaikinByteClockMinsLow] = mins; - // only keep 4 bits - remote[kDaikinByteClockMinsHigh] &= 0xF0; - remote[kDaikinByteClockMinsHigh] |= ((mins >> 8) & 0x0F); -} - -uint16_t IRDaikinESP::getCurrentTime(void) { - return ((remote[kDaikinByteClockMinsHigh] & 0x0F) << 8) + - remote[kDaikinByteClockMinsLow]; -} - -#ifdef ARDUINO -String IRDaikinESP::renderTime(const uint16_t timemins) { - String ret; -#else // ARDUINO -std::string IRDaikinESP::renderTime(const uint16_t timemins) { - std::string ret; -#endif // ARDUINO - ret = uint64ToString(timemins / 60) + ':'; - uint8_t mins = timemins % 60; - if (mins < 10) ret += '0'; - ret += uint64ToString(mins); - return ret; -} - -// Convert the internal state into a human readable string. -#ifdef ARDUINO -String IRDaikinESP::toString(void) { - String result = ""; -#else // ARDUINO -std::string IRDaikinESP::toString(void) { - std::string result = ""; -#endif // ARDUINO - result += F("Power: "); - result += this->getPower() ? F("On") : F("Off"); - result += F(", Mode: "); - result += uint64ToString(this->getMode()); - switch (this->getMode()) { - case kDaikinAuto: - result += F(" (AUTO)"); - break; - case kDaikinCool: - result += F(" (COOL)"); - break; - case kDaikinHeat: - result += F(" (HEAT)"); - break; - case kDaikinDry: - result += F(" (DRY)"); - break; - case kDaikinFan: - result += F(" (FAN)"); - break; - default: - result += F(" (UNKNOWN)"); - } - result += F(", Temp: "); - result += uint64ToString(this->getTemp()); - result += F("C, Fan: "); - result += uint64ToString(this->getFan()); - switch (this->getFan()) { - case kDaikinFanAuto: - result += F(" (AUTO)"); - break; - case kDaikinFanQuiet: - result += F(" (QUIET)"); - break; - case kDaikinFanMin: - result += F(" (MIN)"); - break; - case kDaikinFanMax: - result += F(" (MAX)"); - break; - } - result += F(", Powerful: "); - result += this->getPowerful() ? F("On") : F("Off"); - result += F(", Quiet: "); - result += this->getQuiet() ? F("On") : F("Off"); - result += F(", Sensor: "); - result += this->getSensor() ? F("On") : F("Off"); - result += F(", Eye: "); - result += this->getEye() ? F("On") : F("Off"); - result += F(", Mold: "); - result += this->getMold() ? F("On") : F("Off"); - result += F(", Comfort: "); - result += this->getComfort() ? F("On") : F("Off"); - result += F(", Swing (Horizontal): "); - result += this->getSwingHorizontal() ? F("On") : F("Off"); - result += F(", Swing (Vertical): "); - result += this->getSwingVertical() ? F("On") : F("Off"); - result += F(", Current Time: "); - result += this->renderTime(this->getCurrentTime()); - result += F(", On Time: "); - if (this->getOnTimerEnabled()) - result += this->renderTime(this->getOnTime()); - else - result += F("Off"); - result += F(", Off Time: "); - if (this->getOffTimerEnabled()) - result += this->renderTime(this->getOffTime()); - else - result += F("Off"); - return result; -} - -// Convert a standard A/C mode into its native mode. -uint8_t IRDaikinESP::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kCool: - return kDaikinCool; - case stdAc::opmode_t::kHeat: - return kDaikinHeat; - case stdAc::opmode_t::kDry: - return kDaikinDry; - case stdAc::opmode_t::kFan: - return kDaikinFan; - default: - return kDaikinAuto; - } -} - -// Convert a standard A/C Fan speed into its native fan speed. -uint8_t IRDaikinESP::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: - return kDaikinFanQuiet; - case stdAc::fanspeed_t::kLow: - return kDaikinFanMin; - case stdAc::fanspeed_t::kMedium: - return kDaikinFanMin + 1; - case stdAc::fanspeed_t::kHigh: - return kDaikinFanMax - 1; - case stdAc::fanspeed_t::kMax: - return kDaikinFanMax; - default: - return kDaikinFanAuto; - } -} - -#if DECODE_DAIKIN -// Decode the supplied Daikin A/C message. -// Args: -// results: Ptr to the data to decode and where to store the decode result. -// nbits: Nr. of bits to expect in the data portion. (kDaikinBits) -// strict: Flag to indicate if we strictly adhere to the specification. -// Returns: -// boolean: True if it can decode it, false if it can't. -// -// Status: BETA / Should be working. -// -// Ref: -// https://github.com/mharizanov/Daikin-AC-remote-control-over-the-Internet/tree/master/IRremote -bool IRrecv::decodeDaikin(decode_results *results, const uint16_t nbits, - const bool strict) { - // Is there enough data to match successfully? - if (results->rawlen < (2 * (nbits + kDaikinHeaderLength) + - kDaikinSections * (kHeader + kFooter) + kFooter - 1)) - return false; - - // Compliance - if (strict && nbits != kDaikinBits) return false; - - uint16_t offset = kStartOffset; - match_result_t data_result; - uint16_t dataBitsSoFar = 0; - uint16_t i = 0; - - // Header #1 - Doesn't count as data. - data_result = matchData(&(results->rawbuf[offset]), kDaikinHeaderLength, - kDaikinBitMark, kDaikinOneSpace, - kDaikinBitMark, kDaikinZeroSpace, - kDaikinTolerance, kDaikinMarkExcess, false); - offset += data_result.used; - if (data_result.success == false) return false; // Fail - if (data_result.data) return false; // The header bits should be zero. - - // Read the Data sections. - // Keep reading bytes until we either run out of section or state to fill. - const uint8_t kSectionSize[kDaikinSections] = { - kDaikinSection1Length, kDaikinSection2Length, kDaikinSection3Length}; - for (uint8_t section = 0, pos = 0; section < kDaikinSections; - section++) { - pos += kSectionSize[section]; - // Section Footer - if (!matchMark(results->rawbuf[offset++], kDaikinBitMark, - kDaikinTolerance, kDaikinMarkExcess)) return false; - if (!matchSpace(results->rawbuf[offset++], kDaikinZeroSpace + kDaikinGap, - kDaikinTolerance, kDaikinMarkExcess)) return false; - // Section Header - if (!matchMark(results->rawbuf[offset++], kDaikinHdrMark, - kDaikinTolerance, kDaikinMarkExcess)) return false; - if (!matchSpace(results->rawbuf[offset++], kDaikinHdrSpace, - kDaikinTolerance, kDaikinMarkExcess)) return false; - - // Section Data - for (; offset <= results->rawlen - 16 && i < pos; - i++, dataBitsSoFar += 8, offset += data_result.used) { - // Read in a byte at a time. - data_result = - matchData(&(results->rawbuf[offset]), 8, - kDaikinBitMark, kDaikinOneSpace, - kDaikinBitMark, kDaikinZeroSpace, - kDaikinTolerance, kDaikinMarkExcess, false); - if (data_result.success == false) break; // Fail - results->state[i] = (uint8_t)data_result.data; - } - } - - // Footer - if (!matchMark(results->rawbuf[offset++], kDaikinBitMark)) return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset], kDaikinGap)) - return false; - - // Compliance - if (strict) { - // Re-check we got the correct size/length due to the way we read the data. - if (dataBitsSoFar != kDaikinBits) return false; - // Validate the checksum. - if (!IRDaikinESP::validChecksum(results->state)) return false; - } - - // Success - results->decode_type = DAIKIN; - results->bits = dataBitsSoFar; - // No need to record the state as we stored it as we decoded it. - // As we use result->state, we don't record value, address, or command as it - // is a union data type. - return true; -} -#endif // DECODE_DAIKIN - -#if SEND_DAIKIN2 -// Send a Daikin2 A/C message. -// -// Args: -// data: An array of kDaikin2StateLength bytes containing the IR command. -// -// Status: BETA/Appears to work. -// -// Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/582 -void IRsend::sendDaikin2(unsigned char data[], uint16_t nbytes, - uint16_t repeat) { - if (nbytes < kDaikin2Section1Length) - return; // Not enough bytes to send a partial message. - - for (uint16_t r = 0; r <= repeat; r++) { - // Leader - sendGeneric(kDaikin2LeaderMark, kDaikin2LeaderSpace, - 0, 0, 0, 0, 0, 0, (uint64_t) 0, // No data payload. - 0, kDaikin2Freq, false, 0, 50); - // Section #1 - sendGeneric(kDaikin2HdrMark, kDaikin2HdrSpace, kDaikin2BitMark, - kDaikin2OneSpace, kDaikin2BitMark, kDaikin2ZeroSpace, - kDaikin2BitMark, kDaikin2Gap, data, kDaikin2Section1Length, - kDaikin2Freq, false, 0, 50); - // Section #2 - sendGeneric(kDaikin2HdrMark, kDaikin2HdrSpace, kDaikin2BitMark, - kDaikin2OneSpace, kDaikin2BitMark, kDaikin2ZeroSpace, - kDaikin2BitMark, kDaikin2Gap, data + kDaikin2Section1Length, - nbytes - kDaikin2Section1Length, - kDaikin2Freq, false, 0, 50); - } -} -#endif // SEND_DAIKIN2 - -// Class for handling Daikin2 A/C messages. -// -// Code by crankyoldgit, Reverse engineering analysis by sheppy99 -// -// Supported Remotes: Daikin ARC477A1 remote -// -// Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/582 -// https://docs.google.com/spreadsheets/d/1f8EGfIbBUo2B-CzUFdrgKQprWakoYNKM80IKZN4KXQE/edit?usp=sharing -// https://www.daikin.co.nz/sites/default/files/daikin-split-system-US7-FTXZ25-50NV1B.pdf -IRDaikin2::IRDaikin2(uint16_t pin) : _irsend(pin) { stateReset(); } - -void IRDaikin2::begin() { _irsend.begin(); } - -#if SEND_DAIKIN2 -void IRDaikin2::send(const uint16_t repeat) { - checksum(); - _irsend.sendDaikin2(remote_state, kDaikin2StateLength, repeat); -} -#endif // SEND_DAIKIN2 - -// Verify the checksum is valid for a given state. -// Args: -// state: The array to verify the checksum of. -// length: The size of the state. -// Returns: -// A boolean. -bool IRDaikin2::validChecksum(uint8_t state[], const uint16_t length) { - // Validate the checksum of section #1. - if (length <= kDaikin2Section1Length - 1 || - state[kDaikin2Section1Length - 1] != sumBytes(state, - kDaikin2Section1Length - 1)) - return false; - // Validate the checksum of section #2 (a.k.a. the rest) - if (length <= kDaikin2Section1Length + 1 || - state[length - 1] != sumBytes(state + kDaikin2Section1Length, - length - kDaikin2Section1Length - 1)) - return false; - return true; -} - -// Calculate and set the checksum values for the internal state. -void IRDaikin2::checksum() { - remote_state[kDaikin2Section1Length - 1] = sumBytes( - remote_state, kDaikin2Section1Length - 1); - remote_state[kDaikin2StateLength -1 ] = sumBytes( - remote_state + kDaikin2Section1Length, kDaikin2Section2Length - 1); -} - -void IRDaikin2::stateReset() { - for (uint8_t i = 0; i < kDaikin2StateLength; i++) remote_state[i] = 0x0; - - remote_state[0] = 0x11; - remote_state[1] = 0xDA; - remote_state[2] = 0x27; - remote_state[4] = 0x01; - remote_state[6] = 0xC0; - remote_state[7] = 0x70; - remote_state[8] = 0x08; - remote_state[9] = 0x0C; - remote_state[10] = 0x80; - remote_state[11] = 0x04; - remote_state[12] = 0xB0; - remote_state[13] = 0x16; - remote_state[14] = 0x24; - remote_state[17] = 0xBE; - remote_state[18] = 0xD0; - // remote_state[19] is a checksum byte, it will be set by checksum(). - remote_state[20] = 0x11; - remote_state[21] = 0xDA; - remote_state[22] = 0x27; - remote_state[25] = 0x08; - remote_state[28] = 0xA0; - remote_state[35] = 0xC1; - remote_state[36] = 0x80; - remote_state[37] = 0x60; - // remote_state[38] is a checksum byte, it will be set by checksum(). - disableOnTimer(); - disableOffTimer(); - disableSleepTimer(); - checksum(); -} - -uint8_t *IRDaikin2::getRaw() { - checksum(); // Ensure correct settings before sending. - return remote_state; -} - -void IRDaikin2::setRaw(const uint8_t new_code[]) { - for (uint8_t i = 0; i < kDaikin2StateLength; i++) - remote_state[i] = new_code[i]; -} - -void IRDaikin2::on() { - remote_state[25] |= kDaikinBitPower; - remote_state[6] &= ~kDaikin2BitPower; -} - -void IRDaikin2::off() { - remote_state[25] &= ~kDaikinBitPower; - remote_state[6] |= kDaikin2BitPower; -} - -void IRDaikin2::setPower(const bool state) { - if (state) - on(); - else - off(); -} - -bool IRDaikin2::getPower() { - return (remote_state[25] & kDaikinBitPower) && - !(remote_state[6] & kDaikin2BitPower); -} - -uint8_t IRDaikin2::getMode() { return remote_state[25] >> 4; } - -void IRDaikin2::setMode(const uint8_t desired_mode) { - uint8_t mode = desired_mode; - switch (mode) { - case kDaikinCool: - case kDaikinHeat: - case kDaikinFan: - case kDaikinDry: - break; - default: - mode = kDaikinAuto; - } - remote_state[25] &= 0b10001111; - remote_state[25] |= (mode << 4); - // Redo the temp setting as Cool mode has a different min temp. - if (mode == kDaikinCool) this->setTemp(this->getTemp()); -} - -// Set the temp in deg C -void IRDaikin2::setTemp(const uint8_t desired) { - // The A/C has a different min temp if in cool mode. - uint8_t temp = std::max( - (this->getMode() == kDaikinCool) ? kDaikin2MinCoolTemp : kDaikinMinTemp, - desired); - temp = std::min(kDaikinMaxTemp, temp); - remote_state[26] = temp * 2; -} - -// Set the speed of the fan, 1-5 or kDaikinFanAuto or kDaikinFanQuiet -void IRDaikin2::setFan(const uint8_t fan) { - // Set the fan speed bits, leave low 4 bits alone - uint8_t fanset; - if (fan == kDaikinFanQuiet || fan == kDaikinFanAuto) - fanset = fan; - else if (fan < kDaikinFanMin || fan > kDaikinFanMax) - fanset = kDaikinFanAuto; - else - fanset = 2 + fan; - remote_state[28] &= 0x0F; - remote_state[28] |= (fanset << 4); -} - -uint8_t IRDaikin2::getFan() { - uint8_t fan = remote_state[28] >> 4; - if (fan != kDaikinFanQuiet && fan != kDaikinFanAuto) fan -= 2; - return fan; -} - -uint8_t IRDaikin2::getTemp() { return remote_state[26] / 2; } - -void IRDaikin2::setSwingVertical(const uint8_t position) { - switch (position) { - case kDaikin2SwingVHigh: - case 2: - case 3: - case 4: - case 5: - case kDaikin2SwingVLow: - case kDaikin2SwingVBreeze: - case kDaikin2SwingVCirculate: - case kDaikin2SwingVAuto: - remote_state[18] &= 0xF0; - remote_state[18] |= (position & 0x0F); - } -} - -uint8_t IRDaikin2::getSwingVertical() { return remote_state[18] & 0x0F; } - -void IRDaikin2::setSwingHorizontal(const uint8_t position) { - remote_state[17] = position; -} - -uint8_t IRDaikin2::getSwingHorizontal() { return remote_state[17]; } - -void IRDaikin2::setCurrentTime(const uint16_t numMins) { - uint16_t mins = numMins; - if (numMins > 24 * 60) mins = 0; // If > 23:59, set to 00:00 - remote_state[5] = (uint8_t)(mins & 0xFF); - // only keep 4 bits - remote_state[6] &= 0xF0; - remote_state[6] |= (uint8_t)((mins >> 8) & 0x0F); -} - -uint16_t IRDaikin2::getCurrentTime() { - return ((remote_state[6] & 0x0F) << 8) + remote_state[5]; -} - -// starttime: Number of minutes after midnight. -// Note: Timer location is shared with sleep timer. -void IRDaikin2::enableOnTimer(const uint16_t starttime) { - clearSleepTimerFlag(); - remote_state[25] |= kDaikinBitOnTimer; // Set the On Timer flag. - remote_state[30] = (uint8_t)(starttime & 0xFF); - // only keep 4 bits - remote_state[31] &= 0xF0; - remote_state[31] |= (uint8_t)((starttime >> 8) & 0x0F); -} - -void IRDaikin2::clearOnTimerFlag() { - remote_state[25] &= ~kDaikinBitOnTimer; -} - -void IRDaikin2::disableOnTimer() { - enableOnTimer(kDaikinUnusedTime); - clearOnTimerFlag(); - clearSleepTimerFlag(); -} - -uint16_t IRDaikin2::getOnTime() { - return ((remote_state[31] & 0x0F) << 8) + remote_state[30]; -} - -bool IRDaikin2::getOnTimerEnabled() { - return remote_state[25] & kDaikinBitOnTimer; -} - -// endtime: Number of minutes after midnight. -void IRDaikin2::enableOffTimer(const uint16_t endtime) { - remote_state[25] |= kDaikinBitOffTimer; // Set the Off Timer flag. - remote_state[32] = (uint8_t)((endtime >> 4) & 0xFF); - remote_state[31] &= 0x0F; - remote_state[31] |= (uint8_t)((endtime & 0xF) << 4); -} - -void IRDaikin2::disableOffTimer() { - enableOffTimer(kDaikinUnusedTime); - remote_state[25] &= ~kDaikinBitOffTimer; // Clear the Off Timer flag. -} - -uint16_t IRDaikin2::getOffTime() { - return (remote_state[32] << 4) + (remote_state[31] >> 4); -} - -bool IRDaikin2::getOffTimerEnabled() { - return remote_state[25] & kDaikinBitOffTimer; -} - -uint8_t IRDaikin2::getBeep() { - return remote_state[7] >> 6; -} - -void IRDaikin2::setBeep(const uint8_t beep) { - remote_state[7] &= ~kDaikin2BeepMask; - remote_state[7] |= ((beep << 6) & kDaikin2BeepMask); -} - -uint8_t IRDaikin2::getLight() { - return (remote_state[7] & kDaikin2LightMask) >> 4; -} - -void IRDaikin2::setLight(const uint8_t light) { - remote_state[7] &= ~kDaikin2LightMask; - remote_state[7] |= ((light << 4) & kDaikin2LightMask); -} - -void IRDaikin2::setMold(const bool on) { - if (on) - remote_state[8] |= kDaikin2BitMold; - else - remote_state[8] &= ~kDaikin2BitMold; -} - -bool IRDaikin2::getMold() { - return remote_state[8] & kDaikin2BitMold; -} - -// Auto clean setting. -void IRDaikin2::setClean(const bool on) { - if (on) - remote_state[8] |= kDaikin2BitClean; - else - remote_state[8] &= ~kDaikin2BitClean; -} - -bool IRDaikin2::getClean() { - return remote_state[8] & kDaikin2BitClean; -} - -// Fresh Air settings. -void IRDaikin2::setFreshAir(const bool on) { - if (on) - remote_state[8] |= kDaikin2BitFreshAir; - else - remote_state[8] &= ~kDaikin2BitFreshAir; -} - -bool IRDaikin2::getFreshAir() { - return remote_state[8] & kDaikin2BitFreshAir; -} - -void IRDaikin2::setFreshAirHigh(const bool on) { - if (on) - remote_state[8] |= kDaikin2BitFreshAirHigh; - else - remote_state[8] &= ~kDaikin2BitFreshAirHigh; -} - -bool IRDaikin2::getFreshAirHigh() { - return remote_state[8] & kDaikin2BitFreshAirHigh; -} - -void IRDaikin2::setEyeAuto(bool on) { - if (on) - remote_state[13] |= kDaikin2BitEyeAuto; - else - remote_state[13] &= ~kDaikin2BitEyeAuto; -} - -bool IRDaikin2::getEyeAuto() { - return remote_state[13] & kDaikin2BitEyeAuto; -} - -void IRDaikin2::setEye(bool on) { - if (on) - remote_state[36] |= kDaikin2BitEye; - else - remote_state[36] &= ~kDaikin2BitEye; -} - -bool IRDaikin2::getEye() { - return remote_state[36] & kDaikin2BitEye; -} - -void IRDaikin2::setEcono(bool on) { - if (on) - remote_state[36] |= kDaikinBitEcono; - else - remote_state[36] &= ~kDaikinBitEcono; -} - -bool IRDaikin2::getEcono() { - return remote_state[36] & kDaikinBitEcono; -} - -// sleeptime: Number of minutes. -// Note: Timer location is shared with On Timer. -void IRDaikin2::enableSleepTimer(const uint16_t sleeptime) { - enableOnTimer(sleeptime); - clearOnTimerFlag(); - remote_state[36] |= kDaikin2BitSleepTimer; // Set the Sleep Timer flag. -} - -void IRDaikin2::clearSleepTimerFlag() { - remote_state[36] &= ~kDaikin2BitSleepTimer; -} - -void IRDaikin2::disableSleepTimer() { - disableOnTimer(); -} - -uint16_t IRDaikin2::getSleepTime() { - return getOnTime(); -} - -bool IRDaikin2::getSleepTimerEnabled() { - return remote_state[36] & kDaikin2BitSleepTimer; -} - -void IRDaikin2::setQuiet(const bool on) { - if (on) { - remote_state[33] |= kDaikinBitSilent; - // Powerful & Quiet mode being on are mutually exclusive. - setPowerful(false); - } else { - remote_state[33] &= ~kDaikinBitSilent; - } -} - -bool IRDaikin2::getQuiet() { return remote_state[33] & kDaikinBitSilent; } - -void IRDaikin2::setPowerful(const bool on) { - if (on) { - remote_state[33] |= kDaikinBitPowerful; - // Powerful & Quiet mode being on are mutually exclusive. - setQuiet(false); - } else { - remote_state[33] &= ~kDaikinBitPowerful; - } -} - -bool IRDaikin2::getPowerful() { return remote_state[33] & kDaikinBitPowerful; } - -void IRDaikin2::setPurify(const bool on) { - if (on) - remote_state[36] |= kDaikin2BitPurify; - else - remote_state[36] &= ~kDaikin2BitPurify; -} - -bool IRDaikin2::getPurify() { return remote_state[36] & kDaikin2BitPurify; } - -// Convert a standard A/C mode into its native mode. -uint8_t IRDaikin2::convertMode(const stdAc::opmode_t mode) { - return IRDaikinESP::convertMode(mode); -} - -// Convert a standard A/C Fan speed into its native fan speed. -uint8_t IRDaikin2::convertFan(const stdAc::fanspeed_t speed) { - return IRDaikinESP::convertFan(speed); -} - -// Convert a standard A/C vertical swing into its native version. -uint8_t IRDaikin2::convertSwingV(const stdAc::swingv_t position) { - switch (position) { - case stdAc::swingv_t::kHighest: - case stdAc::swingv_t::kHigh: - case stdAc::swingv_t::kMiddle: - case stdAc::swingv_t::kLow: - case stdAc::swingv_t::kLowest: - return (uint8_t)position + kDaikin2SwingVHigh; - default: - return kDaikin2SwingVAuto; - } -} - -// Convert the internal state into a human readable string. -#ifdef ARDUINO -String IRDaikin2::toString() { - String result = ""; -#else // ARDUINO -std::string IRDaikin2::toString() { - std::string result = ""; -#endif // ARDUINO - result += F("Power: "); - if (getPower()) - result += F("On"); - else - result += F("Off"); - result += F(", Mode: "); - result += uint64ToString(getMode()); - switch (getMode()) { - case kDaikinAuto: - result += F(" (AUTO)"); - break; - case kDaikinCool: - result += F(" (COOL)"); - break; - case kDaikinHeat: - result += F(" (HEAT)"); - break; - case kDaikinDry: - result += F(" (DRY)"); - break; - case kDaikinFan: - result += F(" (FAN)"); - break; - default: - result += F(" (UNKNOWN)"); - } - result += F(", Temp: "); - result += uint64ToString(getTemp()); - result += F("C, Fan: "); - result += uint64ToString(getFan()); - switch (getFan()) { - case kDaikinFanAuto: - result += F(" (Auto)"); - break; - case kDaikinFanQuiet: - result += F(" (Quiet)"); - break; - case kDaikinFanMin: - result += F(" (Min)"); - break; - case kDaikinFanMax: - result += F(" (Max)"); - break; - } - result += F(", Swing (V): "); - result += uint64ToString(getSwingVertical()); - switch (getSwingVertical()) { - case kDaikin2SwingVHigh: - result += F(" (Highest)"); - break; - case 2: - case 3: - case 4: - case 5: - break; - case kDaikin2SwingVLow: - result += F(" (Lowest)"); - break; - case kDaikin2SwingVBreeze: - result += F(" (Breeze)"); - break; - case kDaikin2SwingVCirculate: - result += F(" (Circulate)"); - break; - case kDaikin2SwingVAuto: - result += F(" (Auto)"); - break; - default: - result += F(" (Unknown)"); - } - result += F(", Swing (H): "); - result += uint64ToString(getSwingHorizontal()); - switch (getSwingHorizontal()) { - case kDaikin2SwingHAuto: - result += F(" (Auto)"); - break; - case kDaikin2SwingHSwing: - result += F(" (Swing)"); - break; - } - result += F(", Clock: "); - result += IRDaikinESP::renderTime(getCurrentTime()); - result += F(", On Time: "); - if (getOnTimerEnabled()) - result += IRDaikinESP::renderTime(getOnTime()); - else - result += F("Off"); - result += F(", Off Time: "); - if (getOffTimerEnabled()) - result += IRDaikinESP::renderTime(getOffTime()); - else - result += F("Off"); - result += F(", Sleep Time: "); - if (getSleepTimerEnabled()) - result += IRDaikinESP::renderTime(getSleepTime()); - else - result += F("Off"); - result += F(", Beep: "); - result += uint64ToString(getBeep()); - switch (getBeep()) { - case kDaikinBeepLoud: - result += F(" (Loud)"); - break; - case kDaikinBeepQuiet: - result += F(" (Quiet)"); - break; - case kDaikinBeepOff: - result += F(" (Off)"); - break; - default: - result += F(" (UNKNOWN)"); - } - result += F(", Light: "); - result += uint64ToString(getLight()); - switch (getLight()) { - case kDaikinLightBright: - result += F(" (Bright)"); - break; - case kDaikinLightDim: - result += F(" (Dim)"); - break; - case kDaikinLightOff: - result += F(" (Off)"); - break; - default: - result += F(" (UNKNOWN)"); - } - result += F(", Mold: "); - result += (getMold() ? F("On") : F("Off")); - result += F(", Clean: "); - result += (getClean() ? F("On") : F("Off")); - result += F(", Fresh Air: "); - if (getFreshAir()) - result += (getFreshAirHigh() ? "High" : "On"); - else - result += F("Off"); - result += F(", Eye: "); - result += (getEye() ? F("On") : F("Off")); - result += F(", Eye Auto: "); - result += (getEyeAuto() ? F("On") : F("Off")); - result += F(", Quiet: "); - result += (getQuiet() ? F("On") : F("Off")); - result += F(", Powerful: "); - result += (getPowerful() ? F("On") : F("Off")); - result += ", Purify: "; - result += (getPurify() ? F("On") : F("Off")); - result += F(", Econo: "); - result += (getEcono() ? F("On") : F("Off")); - return result; -} - -#if DECODE_DAIKIN2 -// Decode the supplied Daikin2 A/C message. -// Args: -// results: Ptr to the data to decode and where to store the decode result. -// nbits: Nr. of bits to expect in the data portion. (kDaikin2Bits) -// strict: Flag to indicate if we strictly adhere to the specification. -// Returns: -// boolean: True if it can decode it, false if it can't. -// -// Supported devices: -// - Daikin FTXZ25NV1B, FTXZ35NV1B, FTXZ50NV1B Aircon -// - Daikin ARC477A1 remote -// -// Status: BETA / Work as expected. -// -// Ref: -// https://github.com/mharizanov/Daikin-AC-remote-control-over-the-Internet/tree/master/IRremote -bool IRrecv::decodeDaikin2(decode_results *results, uint16_t nbits, - bool strict) { - if (results->rawlen < 2 * (nbits + kHeader + kFooter) + kHeader - 1) - return false; - - // Compliance - if (strict && nbits != kDaikin2Bits) return false; - - uint16_t offset = kStartOffset; - uint16_t dataBitsSoFar = 0; - uint16_t i = 0; - match_result_t data_result; - uint8_t sectionSize[kDaikin2Sections] = {kDaikin2Section1Length, - kDaikin2Section2Length}; - - // Leader - if (!matchMark(results->rawbuf[offset++], kDaikin2LeaderMark, - kDaikin2Tolerance)) return false; - if (!matchSpace(results->rawbuf[offset++], kDaikin2LeaderSpace, - kDaikin2Tolerance)) return false; - - // Sections - // Keep reading bytes until we either run out of section or state to fill. - for (uint8_t section = 0, pos = 0; section < kDaikin2Sections; - section++) { - pos += sectionSize[section]; - - // Section Header - if (!matchMark(results->rawbuf[offset++], kDaikin2HdrMark, - kDaikin2Tolerance)) return false; - if (!matchSpace(results->rawbuf[offset++], kDaikin2HdrSpace, - kDaikin2Tolerance)) return false; - - // Section Data - for (; offset <= results->rawlen - 16 && i < pos; - i++, dataBitsSoFar += 8, offset += data_result.used) { - // Read in a byte at a time. - data_result = - matchData(&(results->rawbuf[offset]), 8, kDaikin2BitMark, - kDaikin2OneSpace, kDaikin2BitMark, - kDaikin2ZeroSpace, kDaikin2Tolerance, kMarkExcess, false); - if (data_result.success == false) break; // Fail - results->state[i] = (uint8_t)data_result.data; - } - - // Section Footer - if (!matchMark(results->rawbuf[offset++], kDaikin2BitMark, - kDaikin2Tolerance)) return false; - if (section < kDaikin2Sections - 1) { // Inter-section gaps. - if (!matchSpace(results->rawbuf[offset++], kDaikin2Gap, - kDaikin2Tolerance)) return false; - } else { // Last section / End of message gap. - if (offset <= results->rawlen && - !matchAtLeast(results->rawbuf[offset++], kDaikin2Gap, - kDaikin2Tolerance)) return false; - } - } - - // Compliance - if (strict) { - // Re-check we got the correct size/length due to the way we read the data. - if (dataBitsSoFar != kDaikin2Bits) return false; - // Validate the checksum. - if (!IRDaikin2::validChecksum(results->state)) return false; - } - - // Success - results->decode_type = DAIKIN2; - results->bits = dataBitsSoFar; - // No need to record the state as we stored it as we decoded it. - // As we use result->state, we don't record value, address, or command as it - // is a union data type. - return true; -} -#endif // DECODE_DAIKIN2 - -#if SEND_DAIKIN216 -// Send a Daikin 216 bit A/C message. -// -// Args: -// data: An array of kDaikin216StateLength bytes containing the IR command. -// -// Status: Alpha/Untested on a real device. -// -// Supported devices: -// - Daikin ARC433B69 remote. -// -// Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/689 -// https://github.com/danny-source/Arduino_DY_IRDaikin -void IRsend::sendDaikin216(const unsigned char data[], const uint16_t nbytes, - const uint16_t repeat) { - if (nbytes < kDaikin216Section1Length) - return; // Not enough bytes to send a partial message. - - for (uint16_t r = 0; r <= repeat; r++) { - // Section #1 - sendGeneric(kDaikin216HdrMark, kDaikin216HdrSpace, kDaikin216BitMark, - kDaikin216OneSpace, kDaikin216BitMark, kDaikin216ZeroSpace, - kDaikin216BitMark, kDaikin216Gap, data, - kDaikin216Section1Length, - kDaikin216Freq, false, 0, kDutyDefault); - // Section #2 - sendGeneric(kDaikin216HdrMark, kDaikin216HdrSpace, kDaikin216BitMark, - kDaikin216OneSpace, kDaikin216BitMark, kDaikin216ZeroSpace, - kDaikin216BitMark, kDaikin216Gap, - data + kDaikin216Section1Length, - nbytes - kDaikin216Section1Length, - kDaikin216Freq, false, 0, kDutyDefault); - } -} -#endif // SEND_DAIKIN216 - -// Class for handling Daikin 216 bit / 27 byte A/C messages. -// -// Code by crankyoldgit. -// -// Supported Remotes: Daikin ARC433B69 remote -// -// Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/689 -// https://github.com/danny-source/Arduino_DY_IRDaikin -IRDaikin216::IRDaikin216(uint16_t pin) : _irsend(pin) { stateReset(); } - -void IRDaikin216::begin() { _irsend.begin(); } - -#if SEND_DAIKIN216 -void IRDaikin216::send(const uint16_t repeat) { - checksum(); - _irsend.sendDaikin216(remote_state, kDaikin216StateLength, repeat); -} -#endif // SEND_DAIKIN216 - -// Verify the checksum is valid for a given state. -// Args: -// state: The array to verify the checksum of. -// length: The size of the state. -// Returns: -// A boolean. -bool IRDaikin216::validChecksum(uint8_t state[], const uint16_t length) { - // Validate the checksum of section #1. - if (length <= kDaikin216Section1Length - 1 || - state[kDaikin216Section1Length - 1] != sumBytes( - state, kDaikin216Section1Length - 1)) - return false; - // Validate the checksum of section #2 (a.k.a. the rest) - if (length <= kDaikin216Section1Length + 1 || - state[length - 1] != sumBytes(state + kDaikin216Section1Length, - length - kDaikin216Section1Length - 1)) - return false; - return true; -} - -// Calculate and set the checksum values for the internal state. -void IRDaikin216::checksum() { - remote_state[kDaikin216Section1Length - 1] = sumBytes( - remote_state, kDaikin216Section1Length - 1); - remote_state[kDaikin216StateLength - 1] = sumBytes( - remote_state + kDaikin216Section1Length, kDaikin216Section2Length - 1); -} - -void IRDaikin216::stateReset() { - for (uint8_t i = 0; i < kDaikin216StateLength; i++) remote_state[i] = 0x00; - remote_state[0] = 0x11; - remote_state[1] = 0xDA; - remote_state[2] = 0x27; - remote_state[3] = 0xF0; - // remote_state[7] is a checksum byte, it will be set by checksum(). - remote_state[8] = 0x11; - remote_state[9] = 0xDA; - remote_state[10] = 0x27; - remote_state[23] = 0xC0; - // remote_state[26] is a checksum byte, it will be set by checksum(). -} - -uint8_t *IRDaikin216::getRaw() { - checksum(); // Ensure correct settings before sending. - return remote_state; -} - -void IRDaikin216::setRaw(const uint8_t new_code[]) { - for (uint8_t i = 0; i < kDaikin216StateLength; i++) - remote_state[i] = new_code[i]; -} - - -void IRDaikin216::on() { - remote_state[kDaikin216BytePower] |= kDaikinBitPower; -} - -void IRDaikin216::off() { - remote_state[kDaikin216BytePower] &= ~kDaikinBitPower; -} - -void IRDaikin216::setPower(const bool state) { - if (state) - on(); - else - off(); -} - -bool IRDaikin216::getPower() { - return remote_state[kDaikin216BytePower] & kDaikinBitPower; -} - -uint8_t IRDaikin216::getMode() { - return (remote_state[kDaikin216ByteMode] & kDaikin216MaskMode) >> 4; -} - -void IRDaikin216::setMode(const uint8_t mode) { - switch (mode) { - case kDaikinAuto: - case kDaikinCool: - case kDaikinHeat: - case kDaikinFan: - case kDaikinDry: - remote_state[kDaikin216ByteMode] &= ~kDaikin216MaskMode; - remote_state[kDaikin216ByteMode] |= (mode << 4); - break; - default: - this->setMode(kDaikinAuto); - } -} - -// Convert a standard A/C mode into its native mode. -uint8_t IRDaikin216::convertMode(const stdAc::opmode_t mode) { - return IRDaikinESP::convertMode(mode); -} - -// Set the temp in deg C -void IRDaikin216::setTemp(const uint8_t temp) { - uint8_t degrees = std::max(temp, kDaikinMinTemp); - degrees = std::min(degrees, kDaikinMaxTemp); - remote_state[kDaikin216ByteTemp] &= ~kDaikin216MaskTemp; - remote_state[kDaikin216ByteTemp] |= (degrees << 1); -} - -uint8_t IRDaikin216::getTemp(void) { - return (remote_state[kDaikin216ByteTemp] & kDaikin216MaskTemp) >> 1; -} - -// Set the speed of the fan, 1-5 or kDaikinFanAuto or kDaikinFanQuiet -void IRDaikin216::setFan(const uint8_t fan) { - // Set the fan speed bits, leave low 4 bits alone - uint8_t fanset; - if (fan == kDaikinFanQuiet || fan == kDaikinFanAuto) - fanset = fan; - else if (fan < kDaikinFanMin || fan > kDaikinFanMax) - fanset = kDaikinFanAuto; - else - fanset = 2 + fan; - remote_state[kDaikin216ByteFan] &= ~kDaikin216MaskFan; - remote_state[kDaikin216ByteFan] |= (fanset << 4); -} - -uint8_t IRDaikin216::getFan() { - uint8_t fan = remote_state[kDaikin216ByteFan] >> 4; - if (fan != kDaikinFanQuiet && fan != kDaikinFanAuto) fan -= 2; - return fan; -} - -// Convert a standard A/C Fan speed into its native fan speed. -uint8_t IRDaikin216::convertFan(const stdAc::fanspeed_t speed) { - return IRDaikinESP::convertFan(speed); -} - -void IRDaikin216::setSwingVertical(const bool on) { - if (on) - remote_state[kDaikin216ByteSwingV] |= kDaikin216MaskSwingV; - else - remote_state[kDaikin216ByteSwingV] &= ~kDaikin216MaskSwingV; -} - -bool IRDaikin216::getSwingVertical(void) { - return remote_state[kDaikin216ByteSwingV] & kDaikin216MaskSwingV; -} - -void IRDaikin216::setSwingHorizontal(const bool on) { - if (on) - remote_state[kDaikin216ByteSwingH] |= kDaikin216MaskSwingH; - else - remote_state[kDaikin216ByteSwingH] &= ~kDaikin216MaskSwingH; -} - -bool IRDaikin216::getSwingHorizontal(void) { - return remote_state[kDaikin216ByteSwingH] & kDaikin216MaskSwingH; -} - -// This is a horrible hack till someone works out the quiet mode bit. -void IRDaikin216::setQuiet(const bool on) { - if (on) - this->setFan(kDaikinFanQuiet); - else if (this->getFan() == kDaikinFanQuiet) - this->setFan(kDaikinFanAuto); -} - -// This is a horrible hack till someone works out the quiet mode bit. -bool IRDaikin216::getQuiet(void) { - return this->getFan() == kDaikinFanQuiet; -} - -// Convert the internal state into a human readable string. -#ifdef ARDUINO -String IRDaikin216::toString() { - String result = ""; -#else // ARDUINO -std::string IRDaikin216::toString() { - std::string result = ""; -#endif // ARDUINO - result += F("Power: "); - if (this->getPower()) - result += F("On"); - else - result += F("Off"); - result += F(", Mode: "); - result += uint64ToString(this->getMode()); - switch (getMode()) { - case kDaikinAuto: - result += F(" (AUTO)"); - break; - case kDaikinCool: - result += F(" (COOL)"); - break; - case kDaikinHeat: - result += F(" (HEAT)"); - break; - case kDaikinDry: - result += F(" (DRY)"); - break; - case kDaikinFan: - result += F(" (FAN)"); - break; - default: - result += F(" (UNKNOWN)"); - } - result += F(", Temp: "); - result += uint64ToString(this->getTemp()); - result += F("C, Fan: "); - result += uint64ToString(this->getFan()); - switch (this->getFan()) { - case kDaikinFanAuto: - result += F(" (AUTO)"); - break; - case kDaikinFanQuiet: - result += F(" (QUIET)"); - break; - case kDaikinFanMin: - result += F(" (MIN)"); - break; - case kDaikinFanMax: - result += F(" (MAX)"); - break; - } - result += F(", Swing (Horizontal): "); - result += this->getSwingHorizontal() ? F("On") : F("Off"); - result += F(", Swing (Vertical): "); - result += this->getSwingVertical() ? F("On") : F("Off"); - result += F(", Quiet: "); - result += (getQuiet() ? F("On") : F("Off")); - return result; -} - -#if DECODE_DAIKIN216 -// Decode the supplied Daikin 216 bit A/C message. -// Args: -// results: Ptr to the data to decode and where to store the decode result. -// nbits: Nr. of bits to expect in the data portion. (kDaikin216Bits) -// strict: Flag to indicate if we strictly adhere to the specification. -// Returns: -// boolean: True if it can decode it, false if it can't. -// -// Supported devices: -// - Daikin ARC433B69 remote. -// -// Status: BETA / Should be working. -// -// Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/689 -// https://github.com/danny-source/Arduino_DY_IRDaikin -bool IRrecv::decodeDaikin216(decode_results *results, const uint16_t nbits, - const bool strict) { - if (results->rawlen < 2 * (nbits + kHeader + kFooter) - 1) - return false; - - // Compliance - if (strict && nbits != kDaikin216Bits) return false; - - uint16_t offset = kStartOffset; - uint16_t dataBitsSoFar = 0; - uint16_t i = 0; - match_result_t data_result; - uint8_t sectionSize[kDaikin216Sections] = {kDaikin216Section1Length, - kDaikin216Section2Length}; - - // Sections - // Keep reading bytes until we either run out of section or state to fill. - for (uint8_t section = 0, pos = 0; section < kDaikin216Sections; - section++) { - pos += sectionSize[section]; - - // Section Header - if (!matchMark(results->rawbuf[offset++], kDaikin216HdrMark)) return false; - if (!matchSpace(results->rawbuf[offset++], kDaikin2HdrSpace)) return false; - - // Section Data - for (; offset <= results->rawlen - 16 && i < pos; - i++, dataBitsSoFar += 8, offset += data_result.used) { - // Read in a byte at a time. - data_result = - matchData(&(results->rawbuf[offset]), 8, kDaikin216BitMark, - kDaikin216OneSpace, kDaikin216BitMark, - kDaikin216ZeroSpace, kTolerance, kMarkExcess, false); - if (data_result.success == false) break; // Fail - results->state[i] = (uint8_t)data_result.data; - } - - // Section Footer - if (!matchMark(results->rawbuf[offset++], kDaikin216BitMark)) return false; - if (section < kDaikin216Sections - 1) { // Inter-section gaps. - if (!matchSpace(results->rawbuf[offset++], kDaikin216Gap)) return false; - } else { // Last section / End of message gap. - if (offset <= results->rawlen && - !matchAtLeast(results->rawbuf[offset++], kDaikin216Gap)) return false; - } - } - - // Compliance - if (strict) { - // Re-check we got the correct size/length due to the way we read the data. - if (dataBitsSoFar != kDaikin216Bits) return false; - // Validate the checksum. - if (!IRDaikin216::validChecksum(results->state)) return false; - } - - // Success - results->decode_type = decode_type_t::DAIKIN216; - results->bits = dataBitsSoFar; - // No need to record the state as we stored it as we decoded it. - // As we use result->state, we don't record value, address, or command as it - // is a union data type. - return true; -} -#endif // DECODE_DAIKIN216 diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Daikin.h b/lib/IRremoteESP8266-2.6.0/src/ir_Daikin.h deleted file mode 100644 index 038e8edd9..000000000 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Daikin.h +++ /dev/null @@ -1,444 +0,0 @@ -// Copyright 2016 sillyfrog -// Copyright 2017 sillyfrog, crankyoldgit -// Copyright 2018-2019 crankyoldgit -#ifndef IR_DAIKIN_H_ -#define IR_DAIKIN_H_ - -#ifndef UNIT_TEST -#include -#else -#include -#endif -#include "IRrecv.h" -#include "IRremoteESP8266.h" -#include "IRsend.h" -#ifdef UNIT_TEST -#include "IRsend_test.h" -#endif - -// DDDDD AAA IIIII KK KK IIIII NN NN -// DD DD AAAAA III KK KK III NNN NN -// DD DD AA AA III KKKK III NN N NN -// DD DD AAAAAAA III KK KK III NN NNN -// DDDDDD AA AA IIIII KK KK IIIII NN NN - -/* - Daikin AC map - byte 6= - b4:Comfort - byte 7= checksum of the first part (and last byte before a 29ms pause) - byte 13=Current time, mins past midnight, low bits - byte 14 - b0-b3=Current time, mins past midnight, high bits - byte 15= checksum of the second part (and last byte before a 29ms pause) - byte 21=mode - b7 = 0 - b6+b5+b4 = Mode - Modes: b6+b5+b4 - 011 = Cool - 100 = Heat (temp 23) - 110 = FAN (temp not shown, but 25) - 000 = Fully Automatic (temp 25) - 010 = DRY (temp 0xc0 = 96 degrees c) - b3 = 1 - b2 = OFF timer set - b1 = ON timer set - b0 = Air Conditioner ON - byte 22=temp*2 (Temp should be between 10 - 32) - byte 24=Fan - FAN control - b7+b6+b5+b4 = Fan speed - Fan: b7+b6+b5+b4 - 0×3 = 1 bar - 0×4 = 2 bar - 0×5 = 3 bar - 0×6 = 4 bar - 0×7 = 5 bar - 0xa = Auto - 0xb = Quite - b3+b2+b1+b0 = Swing control up/down - Swing control up/down: - 0000 = Swing up/down off - 1111 = Swing up/down on - byte 25 - Swing control left/right: - 0000 = Swing left/right off - 1111 = Swing left/right on - byte 26=On timer mins past midnight, low bits - byte 27 - b0-b3=On timer mins past midnight, high bits - b4-b7=Off timer mins past midnight, low bits - byte 28=Off timer mins past midnight, high bits - byte 29=Aux -> Powerful (bit 1), Silent (bit 5) - byte 32=Aux2 - b1: Sensor - b2: Econo mode - b7: Intelligent eye on - byte 33=Aux3 - b1: Mold Proof - byte 34= checksum of the third part -*/ - -// Constants -const uint8_t kDaikinAuto = 0b000; -const uint8_t kDaikinDry = 0b010; -const uint8_t kDaikinCool = 0b011; -const uint8_t kDaikinHeat = 0b100; -const uint8_t kDaikinFan = 0b110; -const uint8_t kDaikinMinTemp = 10; // Celsius -const uint8_t kDaikinMaxTemp = 32; // Celsius -const uint8_t kDaikinFanMin = 1; -const uint8_t kDaikinFanMax = 5; -const uint8_t kDaikinFanAuto = 0b1010; -const uint8_t kDaikinFanQuiet = 0b1011; -const uint16_t kDaikinHeaderLength = 5; -const uint8_t kDaikinSections = 3; -const uint8_t kDaikinSection1Length = 8; -const uint8_t kDaikinSection2Length = 8; -const uint8_t kDaikinSection3Length = - kDaikinStateLength - kDaikinSection1Length - kDaikinSection2Length; -const uint8_t kDaikinByteComfort = 6; -const uint8_t kDaikinByteChecksum1 = 7; -const uint8_t kDaikinBitComfort = 0b00010000; -const uint8_t kDaikinByteClockMinsLow = 13; -const uint8_t kDaikinByteClockMinsHigh = 14; -const uint8_t kDaikinByteChecksum2 = 15; -const uint8_t kDaikinBytePower = 21; -const uint8_t kDaikinBitPower = 0b00000001; -const uint8_t kDaikinByteTemp = 22; -const uint8_t kDaikinByteFan = 24; -const uint8_t kDaikinByteSwingH = 25; -const uint8_t kDaikinByteOnTimerMinsLow = 26; -const uint8_t kDaikinByteOnTimerMinsHigh = 27; -const uint8_t kDaikinByteOffTimerMinsLow = kDaikinByteOnTimerMinsHigh; -const uint8_t kDaikinByteOffTimerMinsHigh = 28; -const uint8_t kDaikinBytePowerful = 29; -const uint8_t kDaikinBitPowerful = 0b00000001; -const uint8_t kDaikinByteSilent = kDaikinBytePowerful; -const uint8_t kDaikinBitSilent = 0b00100000; -const uint8_t kDaikinByteSensor = 32; -const uint8_t kDaikinBitSensor = 0b00000010; -const uint8_t kDaikinByteEcono = kDaikinByteSensor; -const uint8_t kDaikinBitEcono = 0b00000100; -const uint8_t kDaikinByteEye = kDaikinByteSensor; -const uint8_t kDaikinBitEye = 0b10000000; -const uint8_t kDaikinByteMold = 33; -const uint8_t kDaikinBitMold = 0b00000010; -const uint8_t kDaikinByteOffTimer = kDaikinBytePower; -const uint8_t kDaikinBitOffTimer = 0b00000100; -const uint8_t kDaikinByteOnTimer = kDaikinByteOffTimer; -const uint8_t kDaikinBitOnTimer = 0b00000010; -const uint8_t kDaikinByteChecksum3 = kDaikinStateLength - 1; -const uint16_t kDaikinUnusedTime = 0x600; -const uint8_t kDaikinBeepQuiet = 1; -const uint8_t kDaikinBeepLoud = 2; -const uint8_t kDaikinBeepOff = 3; -const uint8_t kDaikinLightBright = 1; -const uint8_t kDaikinLightDim = 2; -const uint8_t kDaikinLightOff = 3; -const uint8_t kDaikinCurBit = kDaikinStateLength; -const uint8_t kDaikinCurIndex = kDaikinStateLength + 1; -const uint8_t kDaikinTolerance = 35; -const uint16_t kDaikinMarkExcess = kMarkExcess; -const uint16_t kDaikinHdrMark = 3650; // kDaikinBitMark * 8 -const uint16_t kDaikinHdrSpace = 1623; // kDaikinBitMark * 4 -const uint16_t kDaikinBitMark = 428; -const uint16_t kDaikinZeroSpace = 428; -const uint16_t kDaikinOneSpace = 1280; -const uint16_t kDaikinGap = 29000; -// Note bits in each octet swapped so can be sent as a single value -const uint64_t kDaikinFirstHeader64 = - 0b1101011100000000000000001100010100000000001001111101101000010001; - -// Another variant of the protocol for the Daikin ARC477A1 remote. -const uint16_t kDaikin2Freq = 36700; // Modulation Frequency in Hz. -const uint16_t kDaikin2LeaderMark = 10024; -const uint16_t kDaikin2LeaderSpace = 25180; -const uint16_t kDaikin2Gap = kDaikin2LeaderMark + kDaikin2LeaderSpace; -const uint16_t kDaikin2HdrMark = 3500; -const uint16_t kDaikin2HdrSpace = 1728; -const uint16_t kDaikin2BitMark = 460; -const uint16_t kDaikin2OneSpace = 1270; -const uint16_t kDaikin2ZeroSpace = 420; -const uint16_t kDaikin2Sections = 2; -const uint16_t kDaikin2Section1Length = 20; -const uint16_t kDaikin2Section2Length = 19; -const uint8_t kDaikin2Tolerance = kTolerance + 5; - -const uint8_t kDaikin2BitSleepTimer = 0b00100000; -const uint8_t kDaikin2BitPurify = 0b00010000; -const uint8_t kDaikin2BitEye = 0b00000010; -const uint8_t kDaikin2BitEyeAuto = 0b10000000; -const uint8_t kDaikin2BitMold = 0b00001000; -const uint8_t kDaikin2BitClean = 0b00100000; -const uint8_t kDaikin2BitFreshAir = 0b00000001; -const uint8_t kDaikin2BitFreshAirHigh = 0b10000000; -const uint8_t kDaikin2BitPower = 0b10000000; -const uint8_t kDaikin2LightMask = 0b00110000; -const uint8_t kDaikin2BeepMask = 0b11000000; -const uint8_t kDaikin2SwingVHigh = 0x1; -const uint8_t kDaikin2SwingVLow = 0x6; -const uint8_t kDaikin2SwingVBreeze = 0xC; -const uint8_t kDaikin2SwingVCirculate = 0xD; -const uint8_t kDaikin2SwingVAuto = 0xE; -const uint8_t kDaikin2SwingHAuto = 0xBE; -const uint8_t kDaikin2SwingHSwing = 0xBF; -const uint8_t kDaikin2MinCoolTemp = 18; // Min temp (in C) when in Cool mode. - -// Another variant of the protocol for the Daikin ARC433B69 remote. -const uint16_t kDaikin216Freq = 38000; // Modulation Frequency in Hz. -const uint16_t kDaikin216HdrMark = 3400; -const uint16_t kDaikin216HdrSpace = 1800; -const uint16_t kDaikin216BitMark = 380; -const uint16_t kDaikin216OneSpace = 1350; -const uint16_t kDaikin216ZeroSpace = 480; -const uint16_t kDaikin216Gap = 29650; -const uint16_t kDaikin216Sections = 2; -const uint16_t kDaikin216Section1Length = 8; -const uint16_t kDaikin216Section2Length = kDaikin216StateLength - - kDaikin216Section1Length; -const uint8_t kDaikin216BytePower = 13; -const uint8_t kDaikin216ByteMode = kDaikin216BytePower; -const uint8_t kDaikin216MaskMode = 0b01110000; -const uint8_t kDaikin216ByteTemp = 14; -const uint8_t kDaikin216MaskTemp = 0b01111110; -const uint8_t kDaikin216ByteFan = 16; -const uint8_t kDaikin216MaskFan = 0b11110000; -const uint8_t kDaikin216ByteSwingV = 16; -const uint8_t kDaikin216MaskSwingV = 0b00001111; -const uint8_t kDaikin216ByteSwingH = 17; -const uint8_t kDaikin216MaskSwingH = kDaikin216MaskSwingV; - - -// Legacy defines. -#define DAIKIN_COOL kDaikinCool -#define DAIKIN_HEAT kDaikinHeat -#define DAIKIN_FAN kDaikinFan -#define DAIKIN_AUTO kDaikinAuto -#define DAIKIN_DRY kDaikinDry -#define DAIKIN_MIN_TEMP kDaikinMinTemp -#define DAIKIN_MAX_TEMP kDaikinMaxTemp -#define DAIKIN_FAN_MIN kDaikinFanMin -#define DAIKIN_FAN_MAX kDaikinFanMax -#define DAIKIN_FAN_AUTO kDaikinFanAuto -#define DAIKIN_FAN_QUIET kDaikinFanQuiet - -class IRDaikinESP { - public: - explicit IRDaikinESP(uint16_t pin); - -#if SEND_DAIKIN - void send(const uint16_t repeat = kDaikinDefaultRepeat); -#endif - void begin(void); - void on(void); - void off(void); - void setPower(const bool on); - bool getPower(void); - void setTemp(const uint8_t temp); - uint8_t getTemp(); - void setFan(const uint8_t fan); - uint8_t getFan(void); - void setMode(const uint8_t mode); - uint8_t getMode(void); - void setSwingVertical(const bool on); - bool getSwingVertical(void); - void setSwingHorizontal(const bool on); - bool getSwingHorizontal(void); - bool getQuiet(void); - void setQuiet(const bool on); - bool getPowerful(void); - void setPowerful(const bool on); - void setSensor(const bool on); - bool getSensor(void); - void setEcono(const bool on); - bool getEcono(void); - void setEye(const bool on); - bool getEye(void); - void setMold(const bool on); - bool getMold(void); - void setComfort(const bool on); - bool getComfort(void); - void enableOnTimer(const uint16_t starttime); - void disableOnTimer(void); - uint16_t getOnTime(void); - bool getOnTimerEnabled(); - void enableOffTimer(const uint16_t endtime); - void disableOffTimer(void); - uint16_t getOffTime(void); - bool getOffTimerEnabled(void); - void setCurrentTime(const uint16_t mins_since_midnight); - uint16_t getCurrentTime(void); - uint8_t* getRaw(void); - void setRaw(const uint8_t new_code[], - const uint16_t length = kDaikinStateLength); - static bool validChecksum(uint8_t state[], - const uint16_t length = kDaikinStateLength); - static uint8_t convertMode(const stdAc::opmode_t mode); - static uint8_t convertFan(const stdAc::fanspeed_t speed); -#ifdef ARDUINO - String toString(void); - static String renderTime(const uint16_t timemins); -#else - std::string toString(void); - static std::string renderTime(const uint16_t timemins); -#endif -#ifndef UNIT_TEST - - private: - IRsend _irsend; -#else - IRsendTest _irsend; -#endif - // # of bytes per command - uint8_t remote[kDaikinStateLength]; - void stateReset(void); - void checksum(void); -}; - -// Class to emulate a Daikin ARC477A1 remote. -class IRDaikin2 { - public: - explicit IRDaikin2(uint16_t pin); - -#if SEND_DAIKIN2 - void send(const uint16_t repeat = kDaikin2DefaultRepeat); -#endif - void begin(); - void on(); - void off(); - void setPower(const bool state); - bool getPower(); - void setTemp(const uint8_t temp); - uint8_t getTemp(); - void setFan(const uint8_t fan); - uint8_t getFan(); - uint8_t getMode(); - void setMode(const uint8_t mode); - void setSwingVertical(const uint8_t position); - uint8_t getSwingVertical(); - void setSwingHorizontal(const uint8_t position); - uint8_t getSwingHorizontal(); - bool getQuiet(); - void setQuiet(const bool on); - bool getPowerful(); - void setPowerful(const bool on); - void setSensor(const bool on); - bool getSensor(); - void setEcono(const bool on); - bool getEcono(); - void setEye(const bool on); - bool getEye(); - void setEyeAuto(const bool on); - bool getEyeAuto(); - void setPurify(const bool on); - bool getPurify(); - void setMold(const bool on); - bool getMold(); - void enableOnTimer(const uint16_t starttime); - void disableOnTimer(); - uint16_t getOnTime(); - bool getOnTimerEnabled(); - void enableSleepTimer(const uint16_t sleeptime); - void disableSleepTimer(); - uint16_t getSleepTime(); - bool getSleepTimerEnabled(); - void enableOffTimer(const uint16_t endtime); - void disableOffTimer(); - uint16_t getOffTime(); - bool getOffTimerEnabled(); - void setCurrentTime(const uint16_t time); - uint16_t getCurrentTime(); - void setBeep(const uint8_t beep); - uint8_t getBeep(); - void setLight(const uint8_t light); - uint8_t getLight(); - void setClean(const bool on); - bool getClean(); - void setFreshAir(const bool on); - bool getFreshAir(); - void setFreshAirHigh(const bool on); - bool getFreshAirHigh(); - uint8_t* getRaw(); - void setRaw(const uint8_t new_code[]); - uint32_t getCommand(); - void setCommand(uint32_t value); - static bool validChecksum(uint8_t state[], - const uint16_t length = kDaikin2StateLength); - static uint8_t convertMode(const stdAc::opmode_t mode); - static uint8_t convertFan(const stdAc::fanspeed_t speed); - uint8_t convertSwingV(const stdAc::swingv_t position); -#ifdef ARDUINO - String toString(); - static String renderTime(uint16_t timemins); -#else - std::string toString(); - static std::string renderTime(uint16_t timemins); -#endif -#ifndef UNIT_TEST - - private: - IRsend _irsend; -#else - IRsendTest _irsend; -#endif - // # of bytes per command - uint8_t remote_state[kDaikin2StateLength]; - void stateReset(); - void checksum(); - void clearOnTimerFlag(); - void clearSleepTimerFlag(); -}; - -// Class to emulate a Daikin ARC433B69 remote. -class IRDaikin216 { - public: - explicit IRDaikin216(uint16_t pin); - -#if SEND_DAIKIN216 - void send(const uint16_t repeat = kDaikin216DefaultRepeat); -#endif - void begin(); - uint8_t* getRaw(); - void setRaw(const uint8_t new_code[]); - static bool validChecksum(uint8_t state[], - const uint16_t length = kDaikin216StateLength); - void on(void); - void off(void); - void setPower(const bool on); - bool getPower(void); - void setTemp(const uint8_t temp); - uint8_t getTemp(); - void setMode(const uint8_t mode); - uint8_t getMode(void); - static uint8_t convertMode(const stdAc::opmode_t mode); - void setFan(const uint8_t fan); - uint8_t getFan(void); - static uint8_t convertFan(const stdAc::fanspeed_t speed); - void setSwingVertical(const bool on); - bool getSwingVertical(void); - void setSwingHorizontal(const bool on); - bool getSwingHorizontal(void); - void setQuiet(const bool on); - bool getQuiet(void); -#ifdef ARDUINO - String toString(void); - static String renderTime(const uint16_t timemins); -#else - std::string toString(void); - static std::string renderTime(const uint16_t timemins); -#endif -#ifndef UNIT_TEST - - private: - IRsend _irsend; -#else - IRsendTest _irsend; -#endif - // # of bytes per command - uint8_t remote_state[kDaikin216StateLength]; - void stateReset(); - void checksum(); -}; - -#endif // IR_DAIKIN_H_ diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Electra.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Electra.cpp deleted file mode 100644 index 0700ab698..000000000 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Electra.cpp +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright 2018, 2019 David Conran - -#include "IRrecv.h" -#include "IRsend.h" -#include "IRutils.h" - -// EEEEEEE LL EEEEEEE CCCCC TTTTTTT RRRRRR AAA -// EE LL EE CC C TTT RR RR AAAAA -// EEEEE LL EEEEE CC TTT RRRRRR AA AA -// EE LL EE CC C TTT RR RR AAAAAAA -// EEEEEEE LLLLLLL EEEEEEE CCCCC TTT RR RR AA AA - -// Electra A/C added by crankyoldgit -// -// Equipment it seems compatible with: -// * - -// Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/527 -// https://github.com/markszabo/IRremoteESP8266/issues/642 - -// Constants -const uint16_t kElectraAcHdrMark = 9166; -const uint16_t kElectraAcBitMark = 646; -const uint16_t kElectraAcHdrSpace = 4470; -const uint16_t kElectraAcOneSpace = 1647; -const uint16_t kElectraAcZeroSpace = 547; -const uint32_t kElectraAcMessageGap = kDefaultMessageGap; // Just a guess. - -#if SEND_ELECTRA_AC -// Send a Electra message -// -// Args: -// data: Contents of the message to be sent. (Guessing MSBF order) -// nbits: Nr. of bits of data to be sent. Typically kElectraAcBits. -// repeat: Nr. of additional times the message is to be sent. -// -// Status: Alpha / Needs testing against a real device. -// -void IRsend::sendElectraAC(uint8_t data[], uint16_t nbytes, uint16_t repeat) { - for (uint16_t r = 0; r <= repeat; r++) - sendGeneric(kElectraAcHdrMark, kElectraAcHdrSpace, kElectraAcBitMark, - kElectraAcOneSpace, kElectraAcBitMark, kElectraAcZeroSpace, - kElectraAcBitMark, kElectraAcMessageGap, data, nbytes, - 38000, // Complete guess of the modulation frequency. - false, // Send data in LSB order per byte - 0, 50); -} -#endif - -#if DECODE_ELECTRA_AC -// Decode the supplied Electra A/C message. -// -// Args: -// results: Ptr to the data to decode and where to store the decode result. -// nbits: The number of data bits to expect. Typically kElectraAcBits. -// strict: Flag indicating if we should perform strict matching. -// Returns: -// boolean: True if it can decode it, false if it can't. -// -// Status: Beta / Probably works. -// -bool IRrecv::decodeElectraAC(decode_results *results, uint16_t nbits, - bool strict) { - if (nbits % 8 != 0) // nbits has to be a multiple of nr. of bits in a byte. - return false; - - if (strict) { - if (nbits != kElectraAcBits) - return false; // Not strictly a ELECTRA_AC message. - } - - if (results->rawlen < 2 * nbits + kHeader + kFooter - 1) - return false; // Can't possibly be a valid ELECTRA_AC message. - - uint16_t offset = kStartOffset; - - // Message Header - if (!matchMark(results->rawbuf[offset++], kElectraAcHdrMark)) return false; - if (!matchSpace(results->rawbuf[offset++], kElectraAcHdrSpace)) return false; - - // Data Section - match_result_t data_result; - uint16_t dataBitsSoFar = 0; - // Keep reading bytes until we either run out of section or state to fill. - for (uint16_t i = 0; offset <= results->rawlen - 16 && i < nbits / 8; - i++, dataBitsSoFar += 8, offset += data_result.used) { - data_result = matchData(&(results->rawbuf[offset]), 8, kElectraAcBitMark, - kElectraAcOneSpace, kElectraAcBitMark, - kElectraAcZeroSpace, kTolerance, 0, false); - if (data_result.success == false) return false; // Fail - results->state[i] = data_result.data; - } - - // Message Footer - if (!matchMark(results->rawbuf[offset++], kElectraAcBitMark)) return false; - if (offset <= results->rawlen && - !matchAtLeast(results->rawbuf[offset++], kElectraAcMessageGap)) - return false; - - // Compliance - if (strict) { - if (dataBitsSoFar != nbits) return false; - // Verify the checksum. - if (sumBytes(results->state, (dataBitsSoFar / 8) - 1) != - results->state[(dataBitsSoFar / 8) - 1]) return false; - } - - // Success - results->decode_type = ELECTRA_AC; - results->bits = dataBitsSoFar; - // No need to record the state as we stored it as we decoded it. - // As we use result->state, we don't record value, address, or command as it - // is a union data type. - return true; -} -#endif // DECODE_ELECTRA_AC diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Fujitsu.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Fujitsu.cpp deleted file mode 100644 index de1b97e87..000000000 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Fujitsu.cpp +++ /dev/null @@ -1,556 +0,0 @@ -// Copyright 2017 Jonny Graham, David Conran -#include "ir_Fujitsu.h" -#include -#ifndef ARDUINO -#include -#endif -#include "IRsend.h" -#include "IRutils.h" - -// Fujitsu A/C support added by Jonny Graham & David Conran - -// Equipment it seems compatible with: -// * Fujitsu ASYG30LFCA with remote AR-RAH2E -// * Fujitsu AST9RSGCW with remote AR-DB1 -// * - -// Ref: -// These values are based on averages of measurements -const uint16_t kFujitsuAcHdrMark = 3324; -const uint16_t kFujitsuAcHdrSpace = 1574; -const uint16_t kFujitsuAcBitMark = 448; -const uint16_t kFujitsuAcOneSpace = 1182; -const uint16_t kFujitsuAcZeroSpace = 390; -const uint16_t kFujitsuAcMinGap = 8100; - -#if SEND_FUJITSU_AC -// Send a Fujitsu A/C message. -// -// Args: -// data: An array of bytes containing the IR command. -// nbytes: Nr. of bytes of data in the array. Typically one of: -// kFujitsuAcStateLength -// kFujitsuAcStateLength - 1 -// kFujitsuAcStateLengthShort -// kFujitsuAcStateLengthShort - 1 -// repeat: Nr. of times the message is to be repeated. -// (Default = kFujitsuAcMinRepeat). -// -// Status: BETA / Appears to be working. -// -void IRsend::sendFujitsuAC(unsigned char data[], uint16_t nbytes, - uint16_t repeat) { - sendGeneric(kFujitsuAcHdrMark, kFujitsuAcHdrSpace, kFujitsuAcBitMark, - kFujitsuAcOneSpace, kFujitsuAcBitMark, kFujitsuAcZeroSpace, - kFujitsuAcBitMark, kFujitsuAcMinGap, data, nbytes, 38, false, - repeat, 50); -} -#endif // SEND_FUJITSU_AC - -// Code to emulate Fujitsu A/C IR remote control unit. - -// Initialise the object. -IRFujitsuAC::IRFujitsuAC(uint16_t pin, fujitsu_ac_remote_model_t model) - : _irsend(pin) { - setModel(model); - stateReset(); -} - -void IRFujitsuAC::setModel(fujitsu_ac_remote_model_t model) { - _model = model; - switch (model) { - case ARDB1: - _state_length = kFujitsuAcStateLength - 1; - _state_length_short = kFujitsuAcStateLengthShort - 1; - break; - default: - _state_length = kFujitsuAcStateLength; - _state_length_short = kFujitsuAcStateLengthShort; - } -} - -// Reset the state of the remote to a known good state/sequence. -void IRFujitsuAC::stateReset() { - _temp = 24; - _fanSpeed = kFujitsuAcFanHigh; - _mode = kFujitsuAcModeCool; - _swingMode = kFujitsuAcSwingBoth; - _cmd = kFujitsuAcCmdTurnOn; - buildState(); -} - -// Configure the pin for output. -void IRFujitsuAC::begin() { _irsend.begin(); } - -#if SEND_FUJITSU_AC -// Send the current desired state to the IR LED. -void IRFujitsuAC::send(const uint16_t repeat) { - getRaw(); - _irsend.sendFujitsuAC(remote_state, getStateLength(), repeat); -} -#endif // SEND_FUJITSU_AC - -void IRFujitsuAC::buildState() { - remote_state[0] = 0x14; - remote_state[1] = 0x63; - remote_state[2] = 0x00; - remote_state[3] = 0x10; - remote_state[4] = 0x10; - bool fullCmd = false; - switch (_cmd) { - case kFujitsuAcCmdTurnOff: - remote_state[5] = 0x02; - break; - case kFujitsuAcCmdStepHoriz: - remote_state[5] = 0x79; - break; - case kFujitsuAcCmdStepVert: - remote_state[5] = 0x6C; - break; - default: - switch (_model) { - case ARRAH2E: - remote_state[5] = 0xFE; - break; - case ARDB1: - remote_state[5] = 0xFC; - break; - } - fullCmd = true; - break; - } - if (fullCmd) { // long codes - uint8_t tempByte = _temp - kFujitsuAcMinTemp; - // Nr. of bytes in the message after this byte. - remote_state[6] = _state_length - 7; - - remote_state[7] = 0x30; - remote_state[8] = (_cmd == kFujitsuAcCmdTurnOn) | (tempByte << 4); - remote_state[9] = _mode | 0 << 4; // timer off - remote_state[10] = _fanSpeed | _swingMode << 4; - remote_state[11] = 0; // timerOff values - remote_state[12] = 0; // timerOff/On values - remote_state[13] = 0; // timerOn values - if (_model == ARRAH2E) - remote_state[14] = 0x20; - else - remote_state[14] = 0x00; - - uint8_t checksum = 0; - uint8_t checksum_complement = 0; - if (_model == ARRAH2E) { - checksum = sumBytes(remote_state + _state_length_short, - _state_length - _state_length_short - 1); - } else if (_model == ARDB1) { - checksum = sumBytes(remote_state, _state_length - 1); - checksum_complement = 0x9B; - } - // and negate the checksum and store it in the last byte. - remote_state[_state_length - 1] = checksum_complement - checksum; - } else { // short codes - if (_model == ARRAH2E) - // The last byte is the inverse of penultimate byte - remote_state[_state_length_short - 1] = - ~remote_state[_state_length_short - 2]; - // Zero the rest of the state. - for (uint8_t i = _state_length_short; i < kFujitsuAcStateLength; i++) - remote_state[i] = 0; - } -} - -uint8_t IRFujitsuAC::getStateLength() { - buildState(); // Force an update of the internal state. - if ((_model == ARRAH2E && remote_state[5] != 0xFE) || - (_model == ARDB1 && remote_state[5] != 0xFC)) - return _state_length_short; - else - return _state_length; -} - -// Return a pointer to the internal state date of the remote. -uint8_t* IRFujitsuAC::getRaw() { - buildState(); - return remote_state; -} - -void IRFujitsuAC::buildFromState(const uint16_t length) { - switch (length) { - case kFujitsuAcStateLength - 1: - case kFujitsuAcStateLengthShort - 1: - setModel(ARDB1); - break; - default: - setModel(ARRAH2E); - } - switch (remote_state[6]) { - case 8: - setModel(ARDB1); - break; - case 9: - setModel(ARRAH2E); - break; - } - setTemp((remote_state[8] >> 4) + kFujitsuAcMinTemp); - if (remote_state[8] & 0x1) - setCmd(kFujitsuAcCmdTurnOn); - else - setCmd(kFujitsuAcCmdStayOn); - setMode(remote_state[9] & 0b111); - setFanSpeed(remote_state[10] & 0b111); - setSwing(remote_state[10] >> 4); - switch (remote_state[5]) { - case kFujitsuAcCmdTurnOff: - case kFujitsuAcCmdStepHoriz: - case kFujitsuAcCmdStepVert: - setCmd(remote_state[5]); - break; - } -} - -bool IRFujitsuAC::setRaw(const uint8_t newState[], const uint16_t length) { - if (length > kFujitsuAcStateLength) return false; - for (uint16_t i = 0; i < kFujitsuAcStateLength; i++) { - if (i < length) - remote_state[i] = newState[i]; - else - remote_state[i] = 0; - } - buildFromState(length); - return true; -} - -// Set the requested power state of the A/C to off. -void IRFujitsuAC::off() { _cmd = kFujitsuAcCmdTurnOff; } - -void IRFujitsuAC::stepHoriz() { - switch (_model) { - case ARDB1: - break; // This remote doesn't have a horizontal option. - default: - _cmd = kFujitsuAcCmdStepHoriz; - } -} - -void IRFujitsuAC::stepVert() { _cmd = kFujitsuAcCmdStepVert; } - -// Set the requested command of the A/C. -void IRFujitsuAC::setCmd(uint8_t cmd) { - switch (cmd) { - case kFujitsuAcCmdTurnOff: - case kFujitsuAcCmdTurnOn: - case kFujitsuAcCmdStayOn: - case kFujitsuAcCmdStepVert: - _cmd = cmd; - break; - case kFujitsuAcCmdStepHoriz: - if (_model != ARDB1) // AR-DB1 remote doesn't have step horizontal. - _cmd = cmd; - // FALLTHRU - default: - _cmd = kFujitsuAcCmdStayOn; - break; - } -} - -uint8_t IRFujitsuAC::getCmd() { return _cmd; } - -bool IRFujitsuAC::getPower() { return _cmd != kFujitsuAcCmdTurnOff; } - -// Set the temp. in deg C -void IRFujitsuAC::setTemp(uint8_t temp) { - temp = std::max((uint8_t)kFujitsuAcMinTemp, temp); - temp = std::min((uint8_t)kFujitsuAcMaxTemp, temp); - _temp = temp; -} - -uint8_t IRFujitsuAC::getTemp() { return _temp; } - -// Set the speed of the fan -void IRFujitsuAC::setFanSpeed(uint8_t fanSpeed) { - if (fanSpeed > kFujitsuAcFanQuiet) - fanSpeed = kFujitsuAcFanHigh; // Set the fan to maximum if out of range. - _fanSpeed = fanSpeed; -} -uint8_t IRFujitsuAC::getFanSpeed() { return _fanSpeed; } - -// Set the requested climate operation mode of the a/c unit. -void IRFujitsuAC::setMode(uint8_t mode) { - if (mode > kFujitsuAcModeHeat) - mode = kFujitsuAcModeHeat; // Set the mode to maximum if out of range. - _mode = mode; -} - -uint8_t IRFujitsuAC::getMode() { return _mode; } - -// Set the requested swing operation mode of the a/c unit. -void IRFujitsuAC::setSwing(uint8_t swingMode) { - switch (_model) { - case ARDB1: - // Set the mode to max if out of range - if (swingMode > kFujitsuAcSwingVert) swingMode = kFujitsuAcSwingVert; - break; - case ARRAH2E: - default: - // Set the mode to max if out of range - if (swingMode > kFujitsuAcSwingBoth) swingMode = kFujitsuAcSwingBoth; - } - _swingMode = swingMode; -} - -uint8_t IRFujitsuAC::getSwing() { return _swingMode; } - -bool IRFujitsuAC::validChecksum(uint8_t state[], uint16_t length) { - uint8_t sum = 0; - uint8_t sum_complement = 0; - uint8_t checksum = state[length - 1]; - switch (length) { - case kFujitsuAcStateLengthShort: // ARRAH2E - return state[length - 1] == (uint8_t)~state[length - 2]; - case kFujitsuAcStateLength - 1: // ARDB1 - sum = sumBytes(state, length - 1); - sum_complement = 0x9B; - break; - case kFujitsuAcStateLength: // ARRAH2E - sum = sumBytes(state + kFujitsuAcStateLengthShort, - length - 1 - kFujitsuAcStateLengthShort); - break; - default: // Includes ARDB1 short. - return true; // Assume the checksum is valid for other lengths. - } - return checksum == (uint8_t)(sum_complement - sum); // Does it match? -} - -// Convert a standard A/C mode into its native mode. -uint8_t IRFujitsuAC::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kCool: - return kFujitsuAcModeCool; - case stdAc::opmode_t::kHeat: - return kFujitsuAcModeHeat; - case stdAc::opmode_t::kDry: - return kFujitsuAcModeDry; - case stdAc::opmode_t::kFan: - return kFujitsuAcModeFan; - default: - return kFujitsuAcModeAuto; - } -} - -// Convert a standard A/C Fan speed into its native fan speed. -uint8_t IRFujitsuAC::convertFan(stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: - return kFujitsuAcFanQuiet; - case stdAc::fanspeed_t::kLow: - return kFujitsuAcFanLow; - case stdAc::fanspeed_t::kMedium: - return kFujitsuAcFanMed; - case stdAc::fanspeed_t::kHigh: - case stdAc::fanspeed_t::kMax: - return kFujitsuAcFanHigh; - default: - return kFujitsuAcFanAuto; - } -} - -// Convert the internal state into a human readable string. -#ifdef ARDUINO -String IRFujitsuAC::toString() { - String result = ""; -#else -std::string IRFujitsuAC::toString() { - std::string result = ""; -#endif // ARDUINO - result += F("Power: "); - if (getPower()) - result += F("On"); - else - result += F("Off"); - result += F(", Mode: "); - result += uint64ToString(getMode()); - switch (getMode()) { - case kFujitsuAcModeAuto: - result += F(" (AUTO)"); - break; - case kFujitsuAcModeCool: - result += F(" (COOL)"); - break; - case kFujitsuAcModeHeat: - result += F(" (HEAT)"); - break; - case kFujitsuAcModeDry: - result += F(" (DRY)"); - break; - case kFujitsuAcModeFan: - result += F(" (FAN)"); - break; - default: - result += F(" (UNKNOWN)"); - } - result += F(", Temp: "); - result += uint64ToString(getTemp()); - result += F("C, Fan: "); - result += uint64ToString(getFanSpeed()); - switch (getFanSpeed()) { - case kFujitsuAcFanAuto: - result += F(" (AUTO)"); - break; - case kFujitsuAcFanHigh: - result += F(" (HIGH)"); - break; - case kFujitsuAcFanMed: - result += F(" (MED)"); - break; - case kFujitsuAcFanLow: - result += F(" (LOW)"); - break; - case kFujitsuAcFanQuiet: - result += F(" (QUIET)"); - break; - } - result += F(", Swing: "); - switch (getSwing()) { - case kFujitsuAcSwingOff: - result += F("Off"); - break; - case kFujitsuAcSwingVert: - result += F("Vert"); - break; - case kFujitsuAcSwingHoriz: - result += F("Horiz"); - break; - case kFujitsuAcSwingBoth: - result += F("Vert + Horiz"); - break; - default: - result += F("UNKNOWN"); - } - result += F(", Command: "); - switch (getCmd()) { - case kFujitsuAcCmdStepHoriz: - result += F("Step vane horizontally"); - break; - case kFujitsuAcCmdStepVert: - result += F("Step vane vertically"); - break; - default: - result += F("N/A"); - } - return result; -} - -#if DECODE_FUJITSU_AC -// Decode a Fujitsu AC IR message if possible. -// Places successful decode information in the results pointer. -// Args: -// results: Ptr to the data to decode and where to store the decode result. -// nbits: The number of data bits to expect. Typically kFujitsuAcBits. -// strict: Flag to indicate if we strictly adhere to the specification. -// Returns: -// boolean: True if it can decode it, false if it can't. -// -// Status: ALPHA / Untested. -// -// Ref: -// -bool IRrecv::decodeFujitsuAC(decode_results* results, uint16_t nbits, - bool strict) { - uint16_t offset = kStartOffset; - uint16_t dataBitsSoFar = 0; - - // Have we got enough data to successfully decode? - if (results->rawlen < (2 * kFujitsuAcMinBits) + kHeader + kFooter - 1) - return false; // Can't possibly be a valid message. - - // Compliance - if (strict) { - switch (nbits) { - case kFujitsuAcBits: - case kFujitsuAcBits - 8: - case kFujitsuAcMinBits: - case kFujitsuAcMinBits + 8: - break; - default: - return false; // Must be called with the correct nr. of bits. - } - } - - // Header - if (!matchMark(results->rawbuf[offset++], kFujitsuAcHdrMark)) return false; - if (!matchSpace(results->rawbuf[offset++], kFujitsuAcHdrSpace)) return false; - - // Data (Fixed signature) - match_result_t data_result = - matchData(&(results->rawbuf[offset]), kFujitsuAcMinBits - 8, - kFujitsuAcBitMark, kFujitsuAcOneSpace, kFujitsuAcBitMark, - kFujitsuAcZeroSpace, kTolerance, kMarkExcess, false); - if (data_result.success == false) return false; // Fail - if (data_result.data != 0x1010006314) return false; // Signature failed. - dataBitsSoFar += kFujitsuAcMinBits - 8; - offset += data_result.used; - results->state[0] = 0x14; - results->state[1] = 0x63; - results->state[2] = 0x00; - results->state[3] = 0x10; - results->state[4] = 0x10; - - // Keep reading bytes until we either run out of message or state to fill. - for (uint16_t i = 5; - offset <= results->rawlen - 16 && i < kFujitsuAcStateLength; - i++, dataBitsSoFar += 8, offset += data_result.used) { - data_result = matchData( - &(results->rawbuf[offset]), 8, kFujitsuAcBitMark, kFujitsuAcOneSpace, - kFujitsuAcBitMark, kFujitsuAcZeroSpace, kTolerance, kMarkExcess, false); - if (data_result.success == false) break; // Fail - results->state[i] = data_result.data; - } - - // Footer - if (offset > results->rawlen || - !matchMark(results->rawbuf[offset++], kFujitsuAcBitMark)) - return false; - // The space is optional if we are out of capture. - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset], kFujitsuAcMinGap)) - return false; - - // Compliance - if (strict) { - if (dataBitsSoFar != nbits) return false; - } - - results->decode_type = FUJITSU_AC; - results->bits = dataBitsSoFar; - - // Compliance - switch (dataBitsSoFar) { - case kFujitsuAcMinBits: - // Check if this values indicate that this should have been a long state - // message. - if (results->state[5] == 0xFC) return false; - return true; // Success - case kFujitsuAcMinBits + 8: - // Check if this values indicate that this should have been a long state - // message. - if (results->state[5] == 0xFE) return false; - // The last byte needs to be the inverse of the penultimate byte. - if (results->state[5] != (uint8_t)~results->state[6]) return false; - return true; // Success - case kFujitsuAcBits - 8: - // Long messages of this size require this byte be correct. - if (results->state[5] != 0xFC) return false; - break; - case kFujitsuAcBits: - // Long messages of this size require this byte be correct. - if (results->state[5] != 0xFE) return false; - break; - default: - return false; // Unexpected size. - } - if (!IRFujitsuAC::validChecksum(results->state, dataBitsSoFar / 8)) - return false; - - // Success - return true; // All good. -} -#endif // DECODE_FUJITSU_AC diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Gree.h b/lib/IRremoteESP8266-2.6.0/src/ir_Gree.h deleted file mode 100644 index c3c5916dc..000000000 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Gree.h +++ /dev/null @@ -1,141 +0,0 @@ -// Kelvinator A/C -// -// Copyright 2016 David Conran - -#ifndef IR_GREE_H_ -#define IR_GREE_H_ - -#define __STDC_LIMIT_MACROS -#include -#ifndef UNIT_TEST -#include -#else -#include -#endif -#include "IRremoteESP8266.h" -#include "IRsend.h" -#ifdef UNIT_TEST -#include "IRsend_test.h" -#endif - -// GGGG RRRRRR EEEEEEE EEEEEEE -// GG GG RR RR EE EE -// GG RRRRRR EEEEE EEEEE -// GG GG RR RR EE EE -// GGGGGG RR RR EEEEEEE EEEEEEE - -// Constants -const uint8_t kGreeAuto = 0; -const uint8_t kGreeCool = 1; -const uint8_t kGreeDry = 2; -const uint8_t kGreeFan = 3; -const uint8_t kGreeHeat = 4; - -// Byte 0 -const uint8_t kGreeModeMask = 0b00000111; -const uint8_t kGreePower1Mask = 0b00001000; -const uint8_t kGreeFanMask = 0b00110000; -const uint8_t kGreeSwingAutoMask = 0b01000000; -const uint8_t kGreeSleepMask = 0b10000000; -// Byte 2 -const uint8_t kGreeTurboMask = 0b00010000; -const uint8_t kGreeLightMask = 0b00100000; -const uint8_t kGreePower2Mask = 0b01000000; -const uint8_t kGreeXfanMask = 0b10000000; -// Byte 4 -const uint8_t kGreeSwingPosMask = 0b00001111; - -const uint8_t kGreeMinTemp = 16; // Celsius -const uint8_t kGreeMaxTemp = 30; // Celsius -const uint8_t kGreeFanAuto = 0; -const uint8_t kGreeFanMin = 1; -const uint8_t kGreeFanMax = 3; - -const uint8_t kGreeSwingLastPos = 0b00000000; -const uint8_t kGreeSwingAuto = 0b00000001; -const uint8_t kGreeSwingUp = 0b00000010; -const uint8_t kGreeSwingMiddleUp = 0b00000011; -const uint8_t kGreeSwingMiddle = 0b00000100; -const uint8_t kGreeSwingMiddleDown = 0b00000101; -const uint8_t kGreeSwingDown = 0b00000110; -const uint8_t kGreeSwingDownAuto = 0b00000111; -const uint8_t kGreeSwingMiddleAuto = 0b00001001; -const uint8_t kGreeSwingUpAuto = 0b00001011; - -// Legacy defines. -#define GREE_AUTO kGreeAuto -#define GREE_COOL kGreeCool -#define GREE_DRY kGreeDry -#define GREE_FAN kGreeFan -#define GREE_HEAT kGreeHeat -#define GREE_MIN_TEMP kGreeMinTemp -#define GREE_MAX_TEMP kGreeMaxTemp -#define GREE_FAN_MAX kGreeFanMax -#define GREE_SWING_LAST_POS kGreeSwingLastPos -#define GREE_SWING_AUTO kGreeSwingAuto -#define GREE_SWING_UP kGreeSwingUp -#define GREE_SWING_MIDDLE_UP kGreeSwingMiddleUp -#define GREE_SWING_MIDDLE kGreeSwingMiddle -#define GREE_SWING_MIDDLE_DOWN kGreeSwingMiddleDown -#define GREE_SWING_DOWN kGreeSwingDown -#define GREE_SWING_DOWN_AUTO kGreeSwingDownAuto -#define GREE_SWING_MIDDLE_AUTO kGreeSwingMiddleAuto -#define GREE_SWING_UP_AUTO kGreeSwingUpAuto - -// Classes -class IRGreeAC { - public: - explicit IRGreeAC(uint16_t pin); - - void stateReset(); -#if SEND_GREE - void send(const uint16_t repeat = kGreeDefaultRepeat); -#endif // SEND_GREE - void begin(); - void on(); - void off(); - void setPower(const bool state); - bool getPower(); - void setTemp(const uint8_t temp); - uint8_t getTemp(); - void setFan(const uint8_t speed); - uint8_t getFan(); - void setMode(const uint8_t new_mode); - uint8_t getMode(); - void setLight(const bool state); - bool getLight(); - void setXFan(const bool state); - bool getXFan(); - void setSleep(const bool state); - bool getSleep(); - void setTurbo(const bool state); - bool getTurbo(); - void setSwingVertical(const bool automatic, const uint8_t position); - bool getSwingVerticalAuto(); - uint8_t getSwingVerticalPosition(); - uint8_t convertMode(const stdAc::opmode_t mode); - uint8_t convertFan(const stdAc::fanspeed_t speed); - uint8_t convertSwingV(const stdAc::swingv_t swingv); - uint8_t* getRaw(); - void setRaw(uint8_t new_code[]); - static bool validChecksum(const uint8_t state[], - const uint16_t length = kGreeStateLength); -#ifdef ARDUINO - String toString(); -#else - std::string toString(); -#endif -#ifndef UNIT_TEST - - private: - IRsend _irsend; -#else // UNIT_TEST - IRsendTest _irsend; -#endif // UNIT_TEST - // The state of the IR remote in IR code form. - uint8_t remote_state[kGreeStateLength]; - void checksum(const uint16_t length = kGreeStateLength); - void fixup(); -}; - -#endif // IR_GREE_H_ diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_LG.h b/lib/IRremoteESP8266-2.6.0/src/ir_LG.h deleted file mode 100644 index 25d56bc26..000000000 --- a/lib/IRremoteESP8266-2.6.0/src/ir_LG.h +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2017 David Conran - -#ifndef IR_LG_H_ -#define IR_LG_H_ - -// L GGGG -// L G -// L G GG -// L G G -// LLLLL GGG - -#define __STDC_LIMIT_MACROS -#include - -uint8_t calcLGChecksum(uint16_t data); - -#endif // IR_LG_H_ diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Sharp.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Sharp.cpp deleted file mode 100644 index ae1b59c74..000000000 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Sharp.cpp +++ /dev/null @@ -1,267 +0,0 @@ -// Copyright 2009 Ken Shirriff -// Copyright 2017 David Conran - -#include -#include "IRrecv.h" -#include "IRsend.h" -#include "IRutils.h" - -// SSSS H H AAA RRRR PPPP -// S H H A A R R P P -// SSS HHHHH AAAAA RRRR PPPP -// S H H A A R R P -// SSSS H H A A R R P - -// Equipment it seems compatible with: -// * Sharp LC-52D62U -// * -// - -// Constants -// period time = 1/38000Hz = 26.316 microseconds. -// Ref: -// GlobalCache's IR Control Tower data. -// http://www.sbprojects.com/knowledge/ir/sharp.php -const uint16_t kSharpTick = 26; -const uint16_t kSharpBitMarkTicks = 10; -const uint16_t kSharpBitMark = kSharpBitMarkTicks * kSharpTick; -const uint16_t kSharpOneSpaceTicks = 70; -const uint16_t kSharpOneSpace = kSharpOneSpaceTicks * kSharpTick; -const uint16_t kSharpZeroSpaceTicks = 30; -const uint16_t kSharpZeroSpace = kSharpZeroSpaceTicks * kSharpTick; -const uint16_t kSharpGapTicks = 1677; -const uint16_t kSharpGap = kSharpGapTicks * kSharpTick; -// Address(5) + Command(8) + Expansion(1) + Check(1) -const uint64_t kSharpToggleMask = - ((uint64_t)1 << (kSharpBits - kSharpAddressBits)) - 1; -const uint64_t kSharpAddressMask = ((uint64_t)1 << kSharpAddressBits) - 1; -const uint64_t kSharpCommandMask = ((uint64_t)1 << kSharpCommandBits) - 1; - -#if (SEND_SHARP || SEND_DENON) -// Send a (raw) Sharp message -// -// Args: -// data: Contents of the message to be sent. -// nbits: Nr. of bits of data to be sent. Typically kSharpBits. -// repeat: Nr. of additional times the message is to be sent. -// -// Status: BETA / Previously working fine. -// -// Notes: -// This procedure handles the inversion of bits required per protocol. -// The protocol spec says to send the LSB first, but legacy code & usage -// has us sending the MSB first. Grrrr. Normal invocation of encodeSharp() -// handles this for you, assuming you are using the correct/standard values. -// e.g. sendSharpRaw(encodeSharp(address, command)); -// -// Ref: -// http://www.sbprojects.com/knowledge/ir/sharp.htm -// http://lirc.sourceforge.net/remotes/sharp/GA538WJSA -// http://www.mwftr.com/ucF08/LEC14%20PIC%20IR.pdf -// http://www.hifi-remote.com/johnsfine/DecodeIR.html#Sharp -void IRsend::sendSharpRaw(uint64_t data, uint16_t nbits, uint16_t repeat) { - for (uint16_t i = 0; i <= repeat; i++) { - // Protocol demands that the data be sent twice; once normally, - // then with all but the address bits inverted. - // Note: Previously this used to be performed 3 times (normal, inverted, - // normal), however all data points to that being incorrect. - for (uint8_t n = 0; n < 2; n++) { - sendGeneric(0, 0, // No Header - kSharpBitMark, kSharpOneSpace, kSharpBitMark, kSharpZeroSpace, - kSharpBitMark, kSharpGap, data, nbits, 38, true, - 0, // Repeats are handled already. - 33); - // Invert the data per protocol. This is always called twice, so it's - // retured to original upon exiting the inner loop. - data ^= kSharpToggleMask; - } - } -} - -// Encode a (raw) Sharp message from it's components. -// -// Args: -// address: The value of the address to be sent. -// command: The value of the address to be sent. (8 bits) -// expansion: The value of the expansion bit to use. (0 or 1, typically 1) -// check: The value of the check bit to use. (0 or 1, typically 0) -// MSBfirst: Flag indicating MSB first or LSB first order. (Default: false) -// Returns: -// An uint32_t containing the raw Sharp message for sendSharpRaw(). -// -// Status: BETA / Should work okay. -// -// Notes: -// Assumes the standard Sharp bit sizes. -// Historically sendSharp() sends address & command in -// MSB first order. This is actually incorrect. It should be sent in LSB -// order. The behaviour of sendSharp() hasn't been changed to maintain -// backward compatibility. -// -// Ref: -// http://www.sbprojects.com/knowledge/ir/sharp.htm -// http://lirc.sourceforge.net/remotes/sharp/GA538WJSA -// http://www.mwftr.com/ucF08/LEC14%20PIC%20IR.pdf -uint32_t IRsend::encodeSharp(uint16_t address, uint16_t command, - uint16_t expansion, uint16_t check, - bool MSBfirst) { - // Mask any unexpected bits. - address &= ((1 << kSharpAddressBits) - 1); - command &= ((1 << kSharpCommandBits) - 1); - expansion &= 1; - check &= 1; - - if (!MSBfirst) { // Correct bit order if needed. - address = reverseBits(address, kSharpAddressBits); - command = reverseBits(command, kSharpCommandBits); - } - // Concatinate all the bits. - return (address << (kSharpCommandBits + 2)) | (command << 2) | - (expansion << 1) | check; -} - -// Send a Sharp message -// -// Args: -// address: Address value to be sent. -// command: Command value to be sent. -// nbits: Nr. of bits of data to be sent. Typically kSharpBits. -// repeat: Nr. of additional times the message is to be sent. -// -// Status: DEPRICATED / Previously working fine. -// -// Notes: -// This procedure has a non-standard invocation style compared to similar -// sendProtocol() routines. This is due to legacy, compatibility, & historic -// reasons. Normally the calling syntax version is like sendSharpRaw(). -// This procedure transmits the address & command in MSB first order, which is -// incorrect. This behaviour is left as-is to maintain backward -// compatibility with legacy code. -// In short, you should use sendSharpRaw(), encodeSharp(), and the correct -// values of address & command instead of using this, & the wrong values. -// -// Ref: -// http://www.sbprojects.com/knowledge/ir/sharp.htm -// http://lirc.sourceforge.net/remotes/sharp/GA538WJSA -// http://www.mwftr.com/ucF08/LEC14%20PIC%20IR.pdf -void IRsend::sendSharp(uint16_t address, uint16_t command, uint16_t nbits, - uint16_t repeat) { - sendSharpRaw(encodeSharp(address, command, 1, 0, true), nbits, repeat); -} -#endif // (SEND_SHARP || SEND_DENON) - -#if (DECODE_SHARP || DECODE_DENON) -// Decode the supplied Sharp message. -// -// Args: -// results: Ptr to the data to decode and where to store the decode result. -// nbits: Nr. of data bits to expect. Typically kSharpBits. -// strict: Flag indicating if we should perform strict matching. -// expansion: Should we expect the expansion bit to be set. Default is true. -// Returns: -// boolean: True if it can decode it, false if it can't. -// -// Status: STABLE / Working fine. -// -// Note: -// This procedure returns a value suitable for use in sendSharpRaw(). -// TODO(crankyoldgit): Need to ensure capture of the inverted message as it can -// be missed due to the interrupt timeout used to detect an end of message. -// Several compliance checks are disabled until that is resolved. -// Ref: -// http://www.sbprojects.com/knowledge/ir/sharp.php -// http://www.mwftr.com/ucF08/LEC14%20PIC%20IR.pdf -// http://www.hifi-remote.com/johnsfine/DecodeIR.html#Sharp -bool IRrecv::decodeSharp(decode_results *results, uint16_t nbits, bool strict, - bool expansion) { - if (results->rawlen < 2 * nbits + kFooter - 1) - return false; // Not enough entries to be a Sharp message. - // Compliance - if (strict) { - if (nbits != kSharpBits) return false; // Request is out of spec. - // DISABLED - See TODO -#ifdef UNIT_TEST - // An in spec message has the data sent normally, then inverted. So we - // expect twice as many entries than to just get the results. - if (results->rawlen < 2 * (2 * nbits + kFooter)) return false; -#endif - } - - uint64_t data = 0; - uint16_t offset = kStartOffset; - - // No header - // But try to auto-calibrate off the initial mark signal. - if (!matchMark(results->rawbuf[offset], kSharpBitMark, 35)) return false; - // Calculate how long the common tick time is based on the header mark. - uint32_t tick = results->rawbuf[offset] * kRawTick / kSharpBitMarkTicks; - // Data - for (uint16_t i = 0; i < nbits; i++, offset++) { - // Use a higher tolerance value for kSharpBitMark as it is quite small. - if (!matchMark(results->rawbuf[offset++], kSharpBitMarkTicks * tick, 35)) - return false; - if (matchSpace(results->rawbuf[offset], kSharpOneSpaceTicks * tick)) - data = (data << 1) | 1; // 1 - else if (matchSpace(results->rawbuf[offset], kSharpZeroSpaceTicks * tick)) - data <<= 1; // 0 - else - return false; - } - - // Footer - if (!match(results->rawbuf[offset++], kSharpBitMarkTicks * tick)) - return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset], kSharpGapTicks * tick)) - return false; - - // Compliance - if (strict) { - // Check the state of the expansion bit is what we expect. - if ((data & 0b10) >> 1 != expansion) return false; - // The check bit should be cleared in a normal message. - if (data & 0b1) return false; - // DISABLED - See TODO -#ifdef UNIT_TEST - // Grab the second copy of the data (i.e. inverted) - // Header - // i.e. The inter-data/command repeat gap. - if (!matchSpace(results->rawbuf[offset++], kSharpGapTicks * tick)) - return false; - - // Data - uint64_t second_data = 0; - for (uint16_t i = 0; i < nbits; i++, offset++) { - // Use a higher tolerance value for kSharpBitMark as it is quite small. - if (!matchMark(results->rawbuf[offset++], kSharpBitMarkTicks * tick, 35)) - return false; - if (matchSpace(results->rawbuf[offset], kSharpOneSpaceTicks * tick)) - second_data = (second_data << 1) | 1; // 1 - else if (matchSpace(results->rawbuf[offset], kSharpZeroSpaceTicks * tick)) - second_data <<= 1; // 0 - else - return false; - } - // Footer - if (!match(results->rawbuf[offset++], kSharpBitMarkTicks * tick)) - return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset], kSharpGapTicks * tick)) - return false; - - // Check that second_data has been inverted correctly. - if (data != (second_data ^ kSharpToggleMask)) return false; -#endif // UNIT_TEST - } - - // Success - results->decode_type = SHARP; - results->bits = nbits; - results->value = data; - // Address & command are actually transmitted in LSB first order. - results->address = reverseBits(data, nbits) & kSharpAddressMask; - results->command = - reverseBits((data >> 2) & kSharpCommandMask, kSharpCommandBits); - return true; -} -#endif // (DECODE_SHARP || DECODE_DENON) diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Trotec.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Trotec.cpp deleted file mode 100644 index b5c15e7fd..000000000 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Trotec.cpp +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright 2017 stufisher - -#include "ir_Trotec.h" -#include -#include "IRremoteESP8266.h" -#include "IRutils.h" - -// Constants -const uint16_t kTrotecHdrMark = 5952; -const uint16_t kTrotecHdrSpace = 7364; -const uint16_t kTrotecOneMark = 592; -const uint16_t kTrotecOneSpace = 1560; -const uint16_t kTrotecZeroMark = 592; -const uint16_t kTrotecZeroSpace = 592; -const uint16_t kTrotecGap = 6184; -const uint16_t kTrotecGapEnd = 1500; // made up value - -#if SEND_TROTEC - -void IRsend::sendTrotec(unsigned char data[], uint16_t nbytes, - uint16_t repeat) { - if (nbytes < kTrotecStateLength) return; - - for (uint16_t r = 0; r <= repeat; r++) { - sendGeneric(kTrotecHdrMark, kTrotecHdrSpace, kTrotecOneMark, - kTrotecOneSpace, kTrotecZeroMark, kTrotecZeroSpace, - kTrotecOneMark, kTrotecGap, data, nbytes, 36, false, - 0, // Repeats handled elsewhere - 50); - // More footer - enableIROut(36); - mark(kTrotecOneMark); - space(kTrotecGapEnd); - } -} -#endif // SEND_TROTEC - -IRTrotecESP::IRTrotecESP(uint16_t pin) : _irsend(pin) { stateReset(); } - -void IRTrotecESP::begin() { _irsend.begin(); } - -#if SEND_TROTEC -void IRTrotecESP::send(const uint16_t repeat) { - checksum(); - _irsend.sendTrotec(remote_state, kTrotecStateLength, repeat); -} -#endif // SEND_TROTEC - -void IRTrotecESP::checksum() { - uint8_t sum = 0; - - for (uint8_t i = 2; i < 8; i++) sum += remote_state[i]; - remote_state[8] = sum & 0xFF; -} - -void IRTrotecESP::stateReset() { - for (uint8_t i = 2; i < kTrotecStateLength; i++) remote_state[i] = 0x0; - - remote_state[0] = kTrotecIntro1; - remote_state[1] = kTrotecIntro2; - - setPower(false); - setTemp(kTrotecDefTemp); - setSpeed(kTrotecFanMed); - setMode(kTrotecAuto); -} - -uint8_t* IRTrotecESP::getRaw() { - checksum(); - return remote_state; -} - -void IRTrotecESP::setPower(const bool on) { - if (on) - remote_state[2] |= kTrotecPowerBit; - else - remote_state[2] &= ~kTrotecPowerBit; -} - -bool IRTrotecESP::getPower() { return remote_state[2] & kTrotecPowerBit; } - -void IRTrotecESP::setSpeed(const uint8_t fan) { - uint8_t speed = std::min(fan, kTrotecFanHigh); - remote_state[2] = (remote_state[2] & 0b11001111) | (speed << 4); -} - -uint8_t IRTrotecESP::getSpeed() { return (remote_state[2] & 0b00110000) >> 4; } - -void IRTrotecESP::setMode(const uint8_t mode) { - switch (mode) { - case kTrotecAuto: - case kTrotecCool: - case kTrotecDry: - case kTrotecFan: - remote_state[2] = (remote_state[2] & 0b11111100) | mode; - return; - default: - this->setMode(kTrotecAuto); - } -} - -uint8_t IRTrotecESP::getMode() { return remote_state[2] & 0b00000011; } - -void IRTrotecESP::setTemp(const uint8_t celsius) { - uint8_t temp = std::max(celsius, kTrotecMinTemp); - temp = std::min(temp, kTrotecMaxTemp); - remote_state[3] = (remote_state[3] & 0x80) | (temp - kTrotecMinTemp); -} - -uint8_t IRTrotecESP::getTemp() { - return (remote_state[3] & 0b01111111) + kTrotecMinTemp; -} - -void IRTrotecESP::setSleep(bool sleep) { - if (sleep) - remote_state[3] |= kTrotecSleepBit; - else - remote_state[3] &= ~kTrotecSleepBit; -} - -bool IRTrotecESP::getSleep(void) { return remote_state[3] & kTrotecSleepBit; } - -void IRTrotecESP::setTimer(const uint8_t timer) { - if (timer) - remote_state[5] |= kTrotecTimerBit; - else - remote_state[5] &= ~kTrotecTimerBit; - remote_state[6] = (timer > kTrotecMaxTimer) ? kTrotecMaxTimer : timer; -} - -uint8_t IRTrotecESP::getTimer() { return remote_state[6]; } - -// Convert a standard A/C mode into its native mode. -uint8_t IRTrotecESP::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kCool: - return kTrotecCool; - case stdAc::opmode_t::kDry: - return kTrotecDry; - case stdAc::opmode_t::kFan: - return kTrotecFan; - // Note: No Heat mode. - default: - return kTrotecAuto; - } -} - -// Convert a standard A/C Fan speed into its native fan speed. -uint8_t IRTrotecESP::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: - case stdAc::fanspeed_t::kLow: - return kTrotecFanLow; - case stdAc::fanspeed_t::kMedium: - return kTrotecFanMed; - case stdAc::fanspeed_t::kHigh: - case stdAc::fanspeed_t::kMax: - return kTrotecFanHigh; - default: - return kTrotecFanMed; - } -} diff --git a/lib/IRremoteESP8266-2.6.0/test/IRrecv_test.cpp b/lib/IRremoteESP8266-2.6.0/test/IRrecv_test.cpp deleted file mode 100644 index 85b6685f0..000000000 --- a/lib/IRremoteESP8266-2.6.0/test/IRrecv_test.cpp +++ /dev/null @@ -1,561 +0,0 @@ -// Copyright 2017 David Conran - -#include "IRrecv_test.h" -#include "IRrecv.h" -#include "IRremoteESP8266.h" -#include "IRsend.h" -#include "IRsend_test.h" -#include "gtest/gtest.h" - -// Tests for the IRrecv object. -TEST(TestIRrecv, DefaultBufferSize) { - IRrecv irrecv_default(1); - EXPECT_EQ(kRawBuf, irrecv_default.getBufSize()); -} - -TEST(TestIRrecv, LargeBufferSize) { - IRrecv irrecv_large(3, 1024); - EXPECT_EQ(1024, irrecv_large.getBufSize()); -} - -TEST(TestIRrecv, SmallBufferSize) { - IRrecv irrecv_small(4, 80); - EXPECT_EQ(80, irrecv_small.getBufSize()); -} - -TEST(TestIRrecv, MediumBufferSize) { - IRrecv irrecv_medium(4, 512); - EXPECT_EQ(512, irrecv_medium.getBufSize()); -} - -TEST(TestIRrecv, IRrecvDestructor) { - IRrecv *irrecv_ptr = new IRrecv(1); - EXPECT_EQ(kRawBuf, irrecv_ptr->getBufSize()); - - delete irrecv_ptr; - irrecv_ptr = new IRrecv(1, 1234); - EXPECT_EQ(1234, irrecv_ptr->getBufSize()); - delete irrecv_ptr; - - irrecv_ptr = new IRrecv(1, 123); - EXPECT_EQ(123, irrecv_ptr->getBufSize()); - delete irrecv_ptr; -} - -// Tests for copyIrParams() - -TEST(TestCopyIrParams, CopyEmpty) { - irparams_t src; - irparams_t dst; - uint16_t test_size = 1234; - src.bufsize = test_size; - src.rawlen = 0; - src.rawbuf = new uint16_t[test_size]; - src.overflow = false; - dst.bufsize = 4567; - dst.rawlen = 123; - dst.rawbuf = new uint16_t[test_size]; - dst.overflow = true; - // Confirm we are looking at different memory for the buffers. - ASSERT_NE(src.rawbuf, dst.rawbuf); - - IRrecv irrecv(4); - irrecv.copyIrParams(&src, &dst); - - ASSERT_EQ(src.bufsize, dst.bufsize); - ASSERT_EQ(src.rawlen, dst.rawlen); - ASSERT_NE(src.rawbuf, dst.rawbuf); // Pointers, not content. - ASSERT_EQ(src.overflow, dst.overflow); - // Contents of the buffers needs to match. - EXPECT_EQ(0, memcmp(src.rawbuf, dst.rawbuf, src.bufsize * sizeof(uint16_t))); -} - -TEST(TestCopyIrParams, CopyNonEmpty) { - irparams_t src; - irparams_t dst; - uint16_t test_size = 1234; - src.bufsize = test_size; - src.rawlen = 67; - src.rawbuf = new uint16_t[test_size]; - src.rawbuf[0] = 0xF00D; - src.rawbuf[1] = 0xBEEF; - src.rawbuf[test_size - 1] = 0xDEAD; - src.overflow = true; - dst.bufsize = 0; - dst.rawlen = 0; - dst.rawbuf = new uint16_t[test_size]; - dst.overflow = false; - // Confirm we are looking at different memory for the buffers. - ASSERT_NE(src.rawbuf, dst.rawbuf); - // and that they differ before we test. - EXPECT_NE(0, memcmp(src.rawbuf, dst.rawbuf, src.bufsize * sizeof(uint16_t))); - - IRrecv irrecv(4); - irrecv.copyIrParams(&src, &dst); - - ASSERT_EQ(src.bufsize, dst.bufsize); - EXPECT_EQ(test_size, dst.bufsize); - ASSERT_EQ(src.rawlen, dst.rawlen); - EXPECT_EQ(67, dst.rawlen); - ASSERT_EQ(src.overflow, dst.overflow); - EXPECT_TRUE(dst.overflow); - ASSERT_NE(src.rawbuf, dst.rawbuf); // Pointers, not content. - // Contents of the buffers needs to match. - EXPECT_EQ(0, memcmp(src.rawbuf, dst.rawbuf, src.bufsize * sizeof(uint16_t))); - // Check the canary values. - EXPECT_EQ(0xF00D, dst.rawbuf[0]); - EXPECT_EQ(0xBEEF, dst.rawbuf[1]); - EXPECT_EQ(0xDEAD, dst.rawbuf[test_size - 1]); -} - -// Tests for decode(). - -// Test decode of a NEC message. -TEST(TestDecode, DecodeNEC) { - IRsendTest irsend(0); - IRrecv irrecv(1); - irsend.begin(); - irsend.reset(); - irsend.sendNEC(0x807F40BF); - irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decode(&irsend.capture)); - EXPECT_EQ(NEC, irsend.capture.decode_type); - EXPECT_EQ(kNECBits, irsend.capture.bits); - EXPECT_EQ(0x807F40BF, irsend.capture.value); -} - -// Test decode of a JVC message. -TEST(TestDecode, DecodeJVC) { - IRsendTest irsend(0); - IRrecv irrecv(1); - irsend.begin(); - irsend.reset(); - irsend.sendJVC(0xC2B8); - irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decode(&irsend.capture)); - EXPECT_EQ(JVC, irsend.capture.decode_type); - EXPECT_EQ(kJvcBits, irsend.capture.bits); - EXPECT_EQ(0xC2B8, irsend.capture.value); -} - -// Test decode of a LG message. -TEST(TestDecode, DecodeLG) { - IRsendTest irsend(0); - IRrecv irrecv(1); - irsend.begin(); - irsend.reset(); - irsend.sendLG(0x4B4AE51); - irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decode(&irsend.capture)); - EXPECT_EQ(LG, irsend.capture.decode_type); - EXPECT_EQ(kLgBits, irsend.capture.bits); - EXPECT_EQ(0x4B4AE51, irsend.capture.value); - - irsend.reset(); - irsend.sendLG(0xB4B4AE51, kLg32Bits); - irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decode(&irsend.capture)); - EXPECT_EQ(LG, irsend.capture.decode_type); - EXPECT_EQ(kLg32Bits, irsend.capture.bits); - EXPECT_EQ(0xB4B4AE51, irsend.capture.value); -} - -// Test decode of a Panasonic message. -TEST(TestDecode, DecodePanasonic) { - IRsendTest irsend(0); - IRrecv irrecv(1); - irsend.begin(); - irsend.reset(); - irsend.sendPanasonic64(0x40040190ED7C); - irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decodePanasonic(&irsend.capture, kPanasonicBits, true)); - EXPECT_EQ(PANASONIC, irsend.capture.decode_type); - EXPECT_EQ(kPanasonicBits, irsend.capture.bits); - EXPECT_EQ(0x40040190ED7C, irsend.capture.value); -} - -// Test decode of a Samsun message. -TEST(TestDecode, DecodeSamsung) { - IRsendTest irsend(0); - IRrecv irrecv(1); - irsend.begin(); - irsend.reset(); - irsend.sendSAMSUNG(0xE0E09966); - irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decode(&irsend.capture)); - EXPECT_EQ(SAMSUNG, irsend.capture.decode_type); - EXPECT_EQ(kSamsungBits, irsend.capture.bits); - EXPECT_EQ(0xE0E09966, irsend.capture.value); -} - -// Test decode of a Sherwood message. -TEST(TestDecode, DecodeSherwood) { - IRsendTest irsend(0); - IRrecv irrecv(1); - irsend.begin(); - irsend.reset(); - irsend.sendSherwood(0x807F40BF); - irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decode(&irsend.capture)); - // Sherwood codes are really NEC codes. - EXPECT_EQ(NEC, irsend.capture.decode_type); - EXPECT_EQ(kNECBits, irsend.capture.bits); - EXPECT_EQ(0x807F40BF, irsend.capture.value); -} - -// Test decode of a Whynter message. -TEST(TestDecode, DecodeWhynter) { - IRsendTest irsend(0); - IRrecv irrecv(1); - irsend.begin(); - irsend.reset(); - irsend.sendWhynter(0x87654321); - irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decode(&irsend.capture)); - EXPECT_EQ(WHYNTER, irsend.capture.decode_type); - EXPECT_EQ(kWhynterBits, irsend.capture.bits); - EXPECT_EQ(0x87654321, irsend.capture.value); -} - -// Test decode of Sony messages. -TEST(TestDecode, DecodeSony) { - IRsendTest irsend(0); - IRrecv irrecv(1); - irsend.begin(); - - // Synthesised Normal Sony 20-bit message. - irsend.reset(); - irsend.sendSony(irsend.encodeSony(kSony20Bits, 0x1, 0x1, 0x1)); - irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decode(&irsend.capture)); - EXPECT_EQ(SONY, irsend.capture.decode_type); - EXPECT_EQ(kSony20Bits, irsend.capture.bits); - EXPECT_EQ(0x81080, irsend.capture.value); - - // Synthesised Normal Sony 15-bit message. - irsend.reset(); - irsend.sendSony(irsend.encodeSony(kSony15Bits, 21, 1), kSony15Bits); - irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decode(&irsend.capture)); - EXPECT_EQ(SONY, irsend.capture.decode_type); - EXPECT_EQ(kSony15Bits, irsend.capture.bits); - EXPECT_EQ(0x5480, irsend.capture.value); - - // Synthesised Normal Sony 12-bit message. - irsend.reset(); - irsend.sendSony(irsend.encodeSony(kSony12Bits, 21, 1), kSony12Bits); - irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decode(&irsend.capture)); - EXPECT_EQ(SONY, irsend.capture.decode_type); - EXPECT_EQ(kSony12Bits, irsend.capture.bits); - EXPECT_EQ(0xA90, irsend.capture.value); -} - -// Test decode of Sharp messages. -TEST(TestDecode, DecodeSharp) { - IRsendTest irsend(0); - IRrecv irrecv(1); - irsend.begin(); - irsend.reset(); - irsend.sendSharpRaw(0x454A); - irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decode(&irsend.capture)); - EXPECT_EQ(SHARP, irsend.capture.decode_type); - EXPECT_EQ(kSharpBits, irsend.capture.bits); - EXPECT_EQ(0x454A, irsend.capture.value); -} - -// Test decode of Sanyo messages. -TEST(TestDecode, DecodeSanyo) { - IRsendTest irsend(0); - IRrecv irrecv(1); - irsend.begin(); - irsend.reset(); - irsend.sendSanyoLC7461(0x2468DCB56A9); - irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decode(&irsend.capture)); - EXPECT_EQ(SANYO_LC7461, irsend.capture.decode_type); - EXPECT_EQ(kSanyoLC7461Bits, irsend.capture.bits); - EXPECT_EQ(0x2468DCB56A9, irsend.capture.value); -} - -// Test decode of RC-MM messages. -TEST(TestDecode, DecodeRCMM) { - IRsendTest irsend(0); - IRrecv irrecv(1); - irsend.begin(); - - // Normal RCMM 24-bit message. - irsend.reset(); - irsend.sendRCMM(0xe0a600); - irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decode(&irsend.capture)); - EXPECT_EQ(RCMM, irsend.capture.decode_type); - EXPECT_EQ(kRCMMBits, irsend.capture.bits); - EXPECT_EQ(0xe0a600, irsend.capture.value); - - // Normal RCMM 12-bit message. - irsend.reset(); - irsend.sendRCMM(0x600, 12); - irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decode(&irsend.capture)); - EXPECT_EQ(RCMM, irsend.capture.decode_type); - EXPECT_EQ(12, irsend.capture.bits); - EXPECT_EQ(0x600, irsend.capture.value); - - // Normal RCMM 32-bit message. - irsend.reset(); - irsend.sendRCMM(0x28e0a600, 32); - irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decode(&irsend.capture)); - EXPECT_EQ(RCMM, irsend.capture.decode_type); - EXPECT_EQ(32, irsend.capture.bits); - EXPECT_EQ(0x28e0a600, irsend.capture.value); -} - -// Test decode of Mitsubishi messages. -TEST(TestDecode, DecodeMitsubishi) { - IRsendTest irsend(0); - IRrecv irrecv(1); - irsend.begin(); - irsend.reset(); - irsend.sendMitsubishi(0xC2B8); - irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decode(&irsend.capture)); - EXPECT_EQ(MITSUBISHI, irsend.capture.decode_type); - EXPECT_EQ(kMitsubishiBits, irsend.capture.bits); - EXPECT_EQ(0xC2B8, irsend.capture.value); -} - -// Test decode of RC-5/RC-5X messages. -TEST(TestDecode, DecodeRC5) { - IRsendTest irsend(0); - IRrecv irrecv(1); - irsend.begin(); - // Normal RC-5 12-bit message. - irsend.reset(); - irsend.sendRC5(0x175); - irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decode(&irsend.capture)); - EXPECT_EQ(RC5, irsend.capture.decode_type); - EXPECT_EQ(kRC5Bits, irsend.capture.bits); - EXPECT_EQ(0x175, irsend.capture.value); - // Synthesised Normal RC-5X 13-bit message. - irsend.reset(); - irsend.sendRC5(irsend.encodeRC5X(0x02, 0x41, true), kRC5XBits); - irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decode(&irsend.capture)); - EXPECT_EQ(RC5X, irsend.capture.decode_type); - EXPECT_EQ(kRC5XBits, irsend.capture.bits); - EXPECT_EQ(0x1881, irsend.capture.value); -} - -// Test decode of RC-6 messages. -TEST(TestDecode, DecodeRC6) { - IRsendTest irsend(0); - IRrecv irrecv(1); - irsend.begin(); - // Normal RC-6 Mode 0 (20-bit) message. - irsend.reset(); - irsend.sendRC6(0x175); - irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decode(&irsend.capture)); - EXPECT_EQ(RC6, irsend.capture.decode_type); - EXPECT_EQ(kRC6Mode0Bits, irsend.capture.bits); - EXPECT_EQ(0x175, irsend.capture.value); - - // Normal RC-6 36-bit message. - irsend.reset(); - irsend.sendRC6(0xC800F742A, kRC6_36Bits); - irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decode(&irsend.capture)); - EXPECT_EQ(RC6, irsend.capture.decode_type); - EXPECT_EQ(kRC6_36Bits, irsend.capture.bits); - EXPECT_EQ(0xC800F742A, irsend.capture.value); -} - -// Test decode of Dish messages. -TEST(TestDecode, DecodeDish) { - IRsendTest irsend(0); - IRrecv irrecv(1); - irsend.begin(); - irsend.reset(); - irsend.sendDISH(0x9C00); - irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decode(&irsend.capture)); - EXPECT_EQ(DISH, irsend.capture.decode_type); - EXPECT_EQ(kDishBits, irsend.capture.bits); - EXPECT_EQ(0x9C00, irsend.capture.value); -} - -// Test decode of Denon messages. -TEST(TestDecode, DecodeDenon) { - IRsendTest irsend(0); - IRrecv irrecv(1); - irsend.begin(); - // Normal Denon 15-bit message. (Sharp) - irsend.reset(); - irsend.sendDenon(0x2278); - irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decode(&irsend.capture)); - EXPECT_EQ(DENON, irsend.capture.decode_type); - EXPECT_EQ(DENON_BITS, irsend.capture.bits); - EXPECT_EQ(0x2278, irsend.capture.value); - // Legacy Denon 14-bit message. - irsend.reset(); - irsend.sendDenon(0x1278, kDenonLegacyBits); - irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decode(&irsend.capture)); - EXPECT_EQ(DENON, irsend.capture.decode_type); - EXPECT_EQ(DENON_BITS, irsend.capture.bits); - EXPECT_EQ(0x1278, irsend.capture.value); - // Normal Denon 48-bit message. (Panasonic/Kaseikyo) - irsend.reset(); - irsend.sendDenon(0x2A4C028D6CE3, DENON_48_BITS); - irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decode(&irsend.capture)); - EXPECT_EQ(DENON, irsend.capture.decode_type); - EXPECT_EQ(DENON_48_BITS, irsend.capture.bits); - EXPECT_EQ(0x2A4C028D6CE3, irsend.capture.value); -} - -// Test decode of Coolix messages. -TEST(TestDecode, DecodeCoolix) { - IRsendTest irsend(0); - IRrecv irrecv(1); - irsend.begin(); - irsend.reset(); - irsend.sendCOOLIX(0x123456); - irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decode(&irsend.capture)); - EXPECT_EQ(COOLIX, irsend.capture.decode_type); - EXPECT_EQ(kCoolixBits, irsend.capture.bits); - EXPECT_EQ(0x123456, irsend.capture.value); -} - -// Test decode of Aiwa messages. -TEST(TestDecode, DecodeAiwa) { - IRsendTest irsend(0); - IRrecv irrecv(1); - irsend.begin(); - irsend.reset(); - irsend.sendAiwaRCT501(0x7F); - irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decode(&irsend.capture)); - EXPECT_EQ(AIWA_RC_T501, irsend.capture.decode_type); - EXPECT_EQ(kAiwaRcT501Bits, irsend.capture.bits); - EXPECT_EQ(0x7F, irsend.capture.value); -} - -// Test matchData() on space encoded data. -TEST(TestMatchData, SpaceEncoded) { - IRsendTest irsend(0); - IRrecv irrecv(1); - irsend.begin(); - - uint16_t space_encoded_raw[11] = {500, 500, 500, 1500, 499, 499, - 501, 1501, 499, 1490, 500}; - match_result_t result; - - irsend.reset(); - irsend.sendRaw(space_encoded_raw, 11, 38000); - irsend.makeDecodeResult(); - result = irrecv.matchData(irsend.capture.rawbuf + 1, 5, 500, 1500, 500, 500); - ASSERT_TRUE(result.success); - EXPECT_EQ(0b01011, result.data); - EXPECT_EQ(10, result.used); - - irsend.reset(); - irsend.sendRaw(space_encoded_raw, 11, 38000); - irsend.makeDecodeResult(); - result = irrecv.matchData(irsend.capture.rawbuf + 1, 5, 500, 1000, 500, 500); - ASSERT_FALSE(result.success); -} - -// Test matchData() on mark encoded data. -TEST(TestMatchData, MarkEncoded) { - IRsendTest irsend(0); - IRrecv irrecv(1); - irsend.begin(); - - uint16_t mark_encoded_raw[11] = {500, 500, 1500, 500, 499, 499, - 1501, 501, 1499, 490, 500}; - match_result_t result; - - irsend.reset(); - irsend.sendRaw(mark_encoded_raw, 11, 38000); - irsend.makeDecodeResult(); - // MSBF order. - result = irrecv.matchData(irsend.capture.rawbuf + 1, 5, 1500, 500, 500, 500); - ASSERT_TRUE(result.success); - EXPECT_EQ(0b01011, result.data); - EXPECT_EQ(10, result.used); - // LSBF order. - result = irrecv.matchData(irsend.capture.rawbuf + 1, 5, 1500, 500, 500, 500, - kTolerance, kMarkExcess, false); - ASSERT_TRUE(result.success); - EXPECT_EQ(0b11010, result.data); // Bits reversed of the previous test. - EXPECT_EQ(10, result.used); - - irsend.reset(); - irsend.sendRaw(mark_encoded_raw, 11, 38000); - irsend.makeDecodeResult(); - // MSBF order. - result = irrecv.matchData(irsend.capture.rawbuf + 1, 5, 1000, 500, 500, 500); - ASSERT_FALSE(result.success); - // LSBF order. - result = irrecv.matchData(irsend.capture.rawbuf + 1, 5, 1000, 500, 500, 500, - kTolerance, kMarkExcess, false); - ASSERT_FALSE(result.success); -} - -// Test matchData() on "equal total bit time" encoded data. -TEST(TestMatchData, EqualTotalBitTimeEncoded) { - IRsendTest irsend(0); - IRrecv irrecv(1); - irsend.begin(); - - uint16_t equal_encoded_raw[11] = {500, 1500, 1500, 500, 499, 1499, - 1501, 501, 1499, 490, 500}; - match_result_t result; - - irsend.reset(); - irsend.sendRaw(equal_encoded_raw, 11, 38000); - irsend.makeDecodeResult(); - result = irrecv.matchData(irsend.capture.rawbuf + 1, 5, 1500, 500, 500, 1500); - ASSERT_TRUE(result.success); - EXPECT_EQ(0b01011, result.data); - EXPECT_EQ(10, result.used); - - irsend.reset(); - irsend.sendRaw(equal_encoded_raw, 11, 38000); - irsend.makeDecodeResult(); - result = irrecv.matchData(irsend.capture.rawbuf + 1, 5, 1000, 500, 500, 1000); - ASSERT_FALSE(result.success); -} - -// Test matchData() on arbitrary encoded data. -TEST(TestMatchData, ArbitraryEncoded) { - IRsendTest irsend(0); - IRrecv irrecv(1); - irsend.begin(); - - uint16_t arbitrary_encoded_raw[11] = {500, 1500, 3000, 1000, 499, 1499, - 3001, 1001, 2999, 990, 500}; - match_result_t result; - - irsend.reset(); - irsend.sendRaw(arbitrary_encoded_raw, 11, 38000); - irsend.makeDecodeResult(); - result = - irrecv.matchData(irsend.capture.rawbuf + 1, 5, 3000, 1000, 500, 1500); - ASSERT_TRUE(result.success); - EXPECT_EQ(0b01011, result.data); - EXPECT_EQ(10, result.used); - - irsend.reset(); - irsend.sendRaw(arbitrary_encoded_raw, 11, 38000); - irsend.makeDecodeResult(); - result = irrecv.matchData(irsend.capture.rawbuf + 1, 5, 1000, 500, 500, 1000); - ASSERT_FALSE(result.success); -} diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Electra_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Electra_test.cpp deleted file mode 100644 index 7d6d0c915..000000000 --- a/lib/IRremoteESP8266-2.6.0/test/ir_Electra_test.cpp +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright 2018 David Conran - -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" -#include "gtest/gtest.h" - -// Tests for sendElectraAC(). - -// Test sending typical data only. -TEST(TestSendElectraAC, SendDataOnly) { - IRsendTest irsend(0); - irsend.begin(); - uint8_t data[kElectraAcStateLength] = {0xC3, 0x87, 0xF6, 0x28, 0x60, - 0x00, 0x20, 0x00, 0x00, 0x20, - 0x00, 0x05, 0x0D}; - - irsend.sendElectraAC(data); - EXPECT_EQ( - "f38000d50" - "m9166s4470" - "m646s1647m646s1647m646s547m646s547m646s547m646s547m646s1647m646s1647" - "m646s1647m646s1647m646s1647m646s547m646s547m646s547m646s547m646s1647" - "m646s547m646s1647m646s1647m646s547m646s1647m646s1647m646s1647m646s1647" - "m646s547m646s547m646s547m646s1647m646s547m646s1647m646s547m646s547" - "m646s547m646s547m646s547m646s547m646s547m646s1647m646s1647m646s547" - "m646s547m646s547m646s547m646s547m646s547m646s547m646s547m646s547" - "m646s547m646s547m646s547m646s547m646s547m646s1647m646s547m646s547" - "m646s547m646s547m646s547m646s547m646s547m646s547m646s547m646s547" - "m646s547m646s547m646s547m646s547m646s547m646s547m646s547m646s547" - "m646s547m646s547m646s547m646s547m646s547m646s1647m646s547m646s547" - "m646s547m646s547m646s547m646s547m646s547m646s547m646s547m646s547" - "m646s1647m646s547m646s1647m646s547m646s547m646s547m646s547m646s547" - "m646s1647m646s547m646s1647m646s1647m646s547m646s547m646s547m646s547" - "m646s100000", - irsend.outputStr()); -} - -// Tests for decodeElectraAC(). -// Decode normal ElectraAC messages. - -TEST(TestDecodeElectraAC, SyntheticDecode) { - IRsendTest irsend(0); - IRrecv irrecv(0); - irsend.begin(); - - // Synthesised Normal ElectraAC message. - irsend.reset(); - uint8_t expectedState[kElectraAcStateLength] = {0xC3, 0x87, 0xF6, 0x28, 0x60, - 0x00, 0x20, 0x00, 0x00, 0x20, - 0x00, 0x05, 0x0D}; - irsend.sendElectraAC(expectedState); - irsend.makeDecodeResult(); - EXPECT_TRUE(irrecv.decode(&irsend.capture)); - EXPECT_EQ(ELECTRA_AC, irsend.capture.decode_type); - EXPECT_EQ(kElectraAcBits, irsend.capture.bits); - EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); -} - -// Decode a recorded example -TEST(TestDecodeElectraAC, RealExampleDecode) { - IRsendTest irsend(0); - IRrecv irrecv(0); - irsend.begin(); - - // Real ElectraAC message. - // Ref: https://github.com/markszabo/IRremoteESP8266/issues/527 - uint16_t rawData[211] = { - 9166, 4470, 642, 1632, 642, 1632, 668, 534, 666, 534, 668, 534, - 614, 536, 640, 1636, 640, 1646, 694, 1662, 612, 1628, 642, 1666, - 664, 532, 668, 534, 666, 534, 666, 532, 666, 1644, 642, 532, - 640, 1634, 668, 1632, 642, 538, 666, 1660, 610, 1666, 664, 1632, - 642, 1672, 610, 536, 666, 534, 694, 532, 666, 1636, 614, 538, - 666, 1632, 642, 536, 666, 544, 692, 534, 640, 558, 640, 534, - 640, 540, 666, 534, 638, 1666, 638, 1636, 640, 550, 666, 534, - 640, 540, 666, 534, 640, 540, 666, 536, 638, 540, 666, 536, - 638, 550, 664, 536, 638, 540, 664, 536, 638, 540, 666, 534, - 638, 1640, 664, 536, 692, 546, 664, 536, 664, 536, 664, 536, - 664, 546, 612, 532, 636, 538, 664, 536, 664, 546, 612, 538, - 638, 538, 638, 538, 664, 536, 690, 538, 662, 538, 664, 538, - 662, 548, 664, 536, 662, 538, 662, 562, 638, 564, 636, 564, - 636, 1668, 582, 556, 652, 572, 612, 568, 636, 564, 610, 570, - 636, 556, 616, 550, 656, 566, 610, 570, 632, 578, 608, 1640, - 662, 562, 642, 1686, 582, 570, 634, 566, 604, 576, 636, 566, - 610, 578, 634, 1664, 584, 590, 660, 1636, 610, 1642, 664, 590, - 610, 590, 636, 566, 634, 568, 686}; // UNKNOWN 9AD8CDB5 - uint8_t expectedState[kElectraAcStateLength] = {0xC3, 0x87, 0xF6, 0x28, 0x60, - 0x00, 0x20, 0x00, 0x00, 0x20, - 0x00, 0x05, 0x0D}; - - irsend.reset(); - irsend.sendRaw(rawData, 211, 38000); - irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decode(&irsend.capture)); - ASSERT_EQ(ELECTRA_AC, irsend.capture.decode_type); - EXPECT_EQ(kElectraAcBits, irsend.capture.bits); - EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); -} diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Fujitsu_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Fujitsu_test.cpp deleted file mode 100644 index b895e4d9b..000000000 --- a/lib/IRremoteESP8266-2.6.0/test/ir_Fujitsu_test.cpp +++ /dev/null @@ -1,560 +0,0 @@ -// Copyright 2017 Jonny Graham, David Conran - -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" -#include "ir_Fujitsu.h" -#include "gtest/gtest.h" - -template -::testing::AssertionResult ArraysMatch(const T (&expected)[size], - const T* actual) { - for (size_t i(0); i < size; ++i) { - if (expected[i] != actual[i]) { - int e = expected[i]; - int a = actual[i]; - return ::testing::AssertionFailure() << "array[" << i - << "] (" << std::hex << a << std::dec << ") != expected[" << i - << "] (" << std::hex << e << std::dec << ")"; - } - } - return ::testing::AssertionSuccess(); -} -// Tests for Fujitsu A/C methods. - -// Test sending typical data only. -TEST(TestIRFujitsuACClass, GetRawDefault) { - IRFujitsuAC fujitsu = IRFujitsuAC(4); // AR-RAH2E - fujitsu.setCmd(kFujitsuAcCmdTurnOn); - fujitsu.setSwing(kFujitsuAcSwingBoth); - fujitsu.setMode(kFujitsuAcModeCool); - fujitsu.setFanSpeed(kFujitsuAcFanHigh); - fujitsu.setTemp(24); - uint8_t expected_arrah2e[16] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, - 0x81, 0x01, 0x31, 0x00, 0x00, 0x00, 0x20, 0xFD}; - EXPECT_TRUE(ArraysMatch(expected_arrah2e, fujitsu.getRaw())); - EXPECT_EQ(kFujitsuAcStateLength, fujitsu.getStateLength()); - EXPECT_EQ("Power: On, Mode: 1 (COOL), Temp: 24C, Fan: 1 (HIGH), " - "Swing: Vert + Horiz, Command: N/A", fujitsu.toString()); - - uint8_t expected_ardb1[15] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFC, 0x08, 0x30, - 0x81, 0x01, 0x31, 0x00, 0x00, 0x00, 0x1D}; - fujitsu.setModel(ARDB1); - EXPECT_TRUE(ArraysMatch(expected_ardb1, fujitsu.getRaw())); - EXPECT_EQ(kFujitsuAcStateLength - 1, fujitsu.getStateLength()); - EXPECT_EQ("Power: On, Mode: 1 (COOL), Temp: 24C, Fan: 1 (HIGH), " - "Swing: Vert + Horiz, Command: N/A", fujitsu.toString()); -} - -TEST(TestIRFujitsuACClass, GetRawTurnOff) { - IRFujitsuAC fujitsu = IRFujitsuAC(4); - fujitsu.setModel(ARRAH2E); - fujitsu.off(); - uint8_t expected_arrah2e[7] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x02, 0xFD}; - EXPECT_TRUE(ArraysMatch(expected_arrah2e, fujitsu.getRaw())); - EXPECT_EQ(kFujitsuAcStateLengthShort, fujitsu.getStateLength()); - EXPECT_EQ("Power: Off, Mode: 1 (COOL), Temp: 24C, Fan: 1 (HIGH), " - "Swing: Vert + Horiz, Command: N/A", fujitsu.toString()); - - fujitsu.setModel(ARDB1); - uint8_t expected_ardb1[6] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x02}; - EXPECT_TRUE(ArraysMatch(expected_ardb1, fujitsu.getRaw())); - EXPECT_EQ(kFujitsuAcStateLengthShort - 1, fujitsu.getStateLength()); - EXPECT_EQ("Power: Off, Mode: 1 (COOL), Temp: 24C, Fan: 1 (HIGH), " - "Swing: Vert + Horiz, Command: N/A", fujitsu.toString()); -} - -TEST(TestIRFujitsuACClass, GetRawStepHoriz) { - IRFujitsuAC fujitsu = IRFujitsuAC(4); - fujitsu.stepHoriz(); - uint8_t expected[7] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x79, 0x86}; - EXPECT_TRUE(ArraysMatch(expected, fujitsu.getRaw())); - EXPECT_EQ(kFujitsuAcStateLengthShort, fujitsu.getStateLength()); - EXPECT_EQ("Power: On, Mode: 1 (COOL), Temp: 24C, Fan: 1 (HIGH), " - "Swing: Vert + Horiz, Command: Step vane horizontally", - fujitsu.toString()); -} - -TEST(TestIRFujitsuACClass, GetRawStepVert) { - IRFujitsuAC fujitsu = IRFujitsuAC(4); - fujitsu.setModel(ARRAH2E); - fujitsu.stepVert(); - uint8_t expected_arrah2e[7] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x6C, 0x93}; - EXPECT_TRUE(ArraysMatch(expected_arrah2e, fujitsu.getRaw())); - EXPECT_EQ(kFujitsuAcStateLengthShort, fujitsu.getStateLength()); - EXPECT_EQ("Power: On, Mode: 1 (COOL), Temp: 24C, Fan: 1 (HIGH), " - "Swing: Vert + Horiz, Command: Step vane vertically", - fujitsu.toString()); - - fujitsu.setModel(ARDB1); - fujitsu.stepVert(); - uint8_t expected_ardb1[6] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x6C}; - EXPECT_TRUE(ArraysMatch(expected_ardb1, fujitsu.getRaw())); - EXPECT_EQ(kFujitsuAcStateLengthShort - 1, - fujitsu.getStateLength()); - EXPECT_EQ("Power: On, Mode: 1 (COOL), Temp: 24C, Fan: 1 (HIGH), " - "Swing: Vert + Horiz, Command: Step vane vertically", - fujitsu.toString()); -} - -TEST(TestIRFujitsuACClass, GetRawWithSwingHoriz) { - IRFujitsuAC fujitsu = IRFujitsuAC(4); - fujitsu.setCmd(kFujitsuAcCmdStayOn); - fujitsu.setSwing(kFujitsuAcSwingHoriz); - fujitsu.setMode(kFujitsuAcModeCool); - fujitsu.setFanSpeed(kFujitsuAcFanQuiet); - fujitsu.setTemp(25); - uint8_t expected[16] = {0x14, 0x63, 0x0, 0x10, 0x10, 0xFE, 0x9, 0x30, - 0x90, 0x1, 0x24, 0x0, 0x0, 0x0, 0x20, 0xFB}; - EXPECT_TRUE(ArraysMatch(expected, fujitsu.getRaw())); - EXPECT_EQ("Power: On, Mode: 1 (COOL), Temp: 25C, Fan: 4 (QUIET), " - "Swing: Horiz, Command: N/A", - fujitsu.toString()); -} - -TEST(TestIRFujitsuACClass, GetRawWithFan) { - IRFujitsuAC fujitsu = IRFujitsuAC(4); - fujitsu.setCmd(kFujitsuAcCmdStayOn); - fujitsu.setSwing(kFujitsuAcSwingHoriz); - fujitsu.setMode(kFujitsuAcModeFan); - fujitsu.setFanSpeed(kFujitsuAcFanMed); - fujitsu.setTemp(20); // temp doesn't matter for fan - // but it is sent by the RC anyway - fujitsu.setModel(ARRAH2E); - uint8_t expected_arrah2e[16] = { - 0x14, 0x63, 0x0, 0x10, 0x10, 0xFE, 0x9, 0x30, - 0x40, 0x3, 0x22, 0x0, 0x0, 0x0, 0x20, 0x4B}; - EXPECT_TRUE(ArraysMatch(expected_arrah2e, fujitsu.getRaw())); - EXPECT_EQ(kFujitsuAcStateLength, fujitsu.getStateLength()); - EXPECT_EQ("Power: On, Mode: 3 (FAN), Temp: 20C, Fan: 2 (MED), Swing: Horiz, " - "Command: N/A", fujitsu.toString()); - - fujitsu.setModel(ARDB1); - uint8_t expected_ardb1[15] = { - 0x14, 0x63, 0x0, 0x10, 0x10, 0xFC, 0x8, 0x30, - 0x40, 0x3, 0x22, 0x0, 0x0, 0x0, 0x6B}; - EXPECT_TRUE(ArraysMatch(expected_ardb1, fujitsu.getRaw())); - EXPECT_EQ(kFujitsuAcStateLength - 1, fujitsu.getStateLength()); - EXPECT_EQ("Power: On, Mode: 3 (FAN), Temp: 20C, Fan: 2 (MED), Swing: Horiz, " - "Command: N/A", fujitsu.toString()); -} - -TEST(TestIRFujitsuACClass, SetRaw) { - IRFujitsuAC fujitsu = IRFujitsuAC(0); - EXPECT_EQ(kFujitsuAcStateLength, fujitsu.getStateLength()); - uint8_t expected_default_arrah2e[kFujitsuAcStateLength] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, - 0x81, 0x01, 0x31, 0x00, 0x00, 0x00, 0x20, 0xFD}; - EXPECT_TRUE(ArraysMatch(expected_default_arrah2e, fujitsu.getRaw())); - EXPECT_EQ("Power: On, Mode: 1 (COOL), Temp: 24C, Fan: 1 (HIGH), " - "Swing: Vert + Horiz, Command: N/A", fujitsu.toString()); - // Now set a new state via setRaw(); - // This state is a real state from an AR-DB1 remote. - uint8_t new_state1[kFujitsuAcStateLength - 1] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFC, 0x08, 0x30, - 0x30, 0x01, 0x00, 0x00, 0x00, 0x00, 0x9F}; - fujitsu.setRaw(new_state1, kFujitsuAcStateLength - 1); - EXPECT_EQ(kFujitsuAcStateLength - 1, fujitsu.getStateLength()); - EXPECT_TRUE(ArraysMatch(new_state1, fujitsu.getRaw())); - EXPECT_EQ("Power: On, Mode: 1 (COOL), Temp: 19C, Fan: 0 (AUTO), " - "Swing: Off, Command: N/A", fujitsu.toString()); -} - -TEST(TestSendFujitsuAC, GenerateMessage) { - IRFujitsuAC fujitsu = IRFujitsuAC(4); - IRsendTest irsend(4); - fujitsu.begin(); - irsend.begin(); - - fujitsu.setCmd(kFujitsuAcCmdStayOn); - fujitsu.setSwing(kFujitsuAcSwingBoth); - fujitsu.setMode(kFujitsuAcModeCool); - fujitsu.setFanSpeed(kFujitsuAcFanHigh); - fujitsu.setTemp(24); - - EXPECT_EQ(kFujitsuAcFanHigh, fujitsu.getFanSpeed()); - EXPECT_EQ(kFujitsuAcModeCool, fujitsu.getMode()); - EXPECT_EQ(24, fujitsu.getTemp()); - EXPECT_EQ(kFujitsuAcSwingBoth, fujitsu.getSwing()); - EXPECT_EQ(kFujitsuAcCmdStayOn, fujitsu.getCmd()); - - irsend.reset(); - irsend.sendFujitsuAC(fujitsu.getRaw(), kFujitsuAcStateLength); - EXPECT_EQ( - "f38000d50" - "m3324s1574" - "m448s390m448s390m448s1182m448s390m448s1182m448s390m448s390m448s390" - "m448s1182m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" - "m448s390m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182" - "m448s1182m448s390m448s390m448s1182m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s1182m448s1182m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s1182" - "m448s1182m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s1182m448s390m448s390" - "m448s390m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182" - "m448s8100", - irsend.outputStr()); -} - -TEST(TestSendFujitsuAC, GenerateShortMessage) { - IRFujitsuAC fujitsu = IRFujitsuAC(4); - IRsendTest irsend(4); - fujitsu.begin(); - irsend.begin(); - - fujitsu.off(); - - EXPECT_EQ(kFujitsuAcCmdTurnOff, fujitsu.getCmd()); - - irsend.reset(); - irsend.sendFujitsuAC(fujitsu.getRaw(), kFujitsuAcStateLengthShort); - EXPECT_EQ( - "f38000d50" - "m3324s1574m448s390m448s390m448s1182m448s390m448s1182m448s390m448s390m448" - "s390m448s1182m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s1182m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s1182m448s390m448s390m448s390m448s390m448s1182m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s1182m448s390m448s1182m448" - "s1182m448s1182m448s1182m448s1182m448s1182m448s8100", - irsend.outputStr()); -} - -// Issue #275 -TEST(TestSendFujitsuAC, Issue275) { - IRFujitsuAC fujitsu = IRFujitsuAC(4); - IRsendTest irsend(4); - fujitsu.begin(); - irsend.begin(); - irsend.reset(); - - fujitsu.setCmd(kFujitsuAcCmdTurnOff); - irsend.sendFujitsuAC(fujitsu.getRaw(), kFujitsuAcStateLengthShort); - EXPECT_EQ( - "f38000d50" - // Header - "m3324s1574" - // 0 0 1 0 1 0 0 0 (0x28) - "m448s390m448s390m448s1182m448s390m448s1182m448s390m448s390m448s390" - // 1 1 0 0 0 1 1 0 (0xC6) - "m448s1182m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390" - // 0 0 0 0 0 0 0 0 (0x00) - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - // 0 0 0 0 1 0 0 0 (0x08) - "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" - // 0 0 0 0 1 0 0 0 (0x08) - "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" - // 0 1 0 0 0 0 0 0 (0x40) - "m448s390m448s1182m448s390m448s390m448s390m448s390m448s390m448s390" - // 1 0 1 1 1 1 1 1 (0xBF) - "m448s1182m448s390m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182" - // Footer - "m448s8100", irsend.outputStr()); - - irsend.reset(); - // Per report in Issue #275 - uint16_t off[115] = { - 3350, 1650, - 450, 400, 450, 450, 450, 1250, 450, 400, 450, 1250, 450, 400, 450, 400, - 450, 400, 450, 1250, 450, 1250, 450, 400, 450, 400, 450, 400, 450, 1250, - 450, 1250, 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, - 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, - 450, 1250, 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, - 450, 400, 450, 1250, 450, 400, 450, 400, 450, 400, 450, 400, 450, 1250, - 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, 450, 1250, - 450, 400, 450, 1250, 450, 1250, 450, 1250, 450, 1250, 450, 1250, - 450, 1250, 450}; - irsend.sendRaw(off, 115, 38); - EXPECT_EQ( - "f38000d50" - // Header - "m3350s1650" - // 0 0 1 0 1 0 0 0 (0x28) - "m450s400m450s450m450s1250m450s400m450s1250m450s400m450s400m450s400" - // 1 1 0 0 0 1 1 0 (0xC6) - "m450s1250m450s1250m450s400m450s400m450s400m450s1250m450s1250m450s400" - // 0 0 0 0 0 0 0 0 (0x00) - "m450s400m450s400m450s400m450s400m450s400m450s400m450s400m450s400" - // 0 0 0 0 1 0 0 0 (0x08) - "m450s400m450s400m450s400m450s400m450s1250m450s400m450s400m450s400" - // 0 0 0 0 1 0 0 0 (0x08) - "m450s400m450s400m450s400m450s400m450s1250m450s400m450s400m450s400" - // 0 1 0 0 0 0 0 0 (0x40) - "m450s400m450s1250m450s400m450s400m450s400m450s400m450s400m450s400" - // 1 0 1 1 1 1 1 1 (0xBF) - "m450s1250m450s400m450s1250m450s1250m450s1250m450s1250m450s1250m450s1250" - // Footer - "m450", - irsend.outputStr()); -} - -TEST(TestDecodeFujitsuAC, SyntheticShortMessages) { - IRsendTest irsend(0); - IRFujitsuAC fujitsu = IRFujitsuAC(0); - IRrecv irrecv(0); - - irsend.begin(); - irsend.reset(); - - fujitsu.setModel(ARRAH2E); - fujitsu.setCmd(kFujitsuAcCmdTurnOff); - irsend.sendFujitsuAC(fujitsu.getRaw(), fujitsu.getStateLength()); - irsend.makeDecodeResult(); - EXPECT_TRUE(irrecv.decode(&irsend.capture)); - ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); - ASSERT_EQ(kFujitsuAcMinBits + 8, irsend.capture.bits); - uint8_t expected_arrah2e[7] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x02, 0xFD}; - EXPECT_TRUE(ArraysMatch(expected_arrah2e, irsend.capture.state)); - - irsend.reset(); - - fujitsu.setModel(ARDB1); - fujitsu.setCmd(kFujitsuAcCmdTurnOff); - irsend.sendFujitsuAC(fujitsu.getRaw(), fujitsu.getStateLength()); - irsend.makeDecodeResult(); - EXPECT_TRUE(irrecv.decode(&irsend.capture)); - ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); - ASSERT_EQ(kFujitsuAcMinBits, irsend.capture.bits); - uint8_t expected_ardb1[6] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x02}; - EXPECT_TRUE(ArraysMatch(expected_ardb1, irsend.capture.state)); -} - -TEST(TestDecodeFujitsuAC, SyntheticLongMessages) { - IRsendTest irsend(0); - IRFujitsuAC fujitsu = IRFujitsuAC(0); - IRrecv irrecv(0); - irsend.begin(); - - irsend.reset(); - - fujitsu.setModel(ARRAH2E); - fujitsu.setCmd(kFujitsuAcCmdStayOn); - fujitsu.setSwing(kFujitsuAcSwingVert); - fujitsu.setMode(kFujitsuAcModeCool); - fujitsu.setFanSpeed(kFujitsuAcFanQuiet); - fujitsu.setTemp(18); - irsend.sendFujitsuAC(fujitsu.getRaw(), fujitsu.getStateLength()); - ASSERT_EQ(kFujitsuAcStateLength, fujitsu.getStateLength()); - irsend.makeDecodeResult(); - EXPECT_TRUE(irrecv.decodeFujitsuAC(&irsend.capture)); - ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); - ASSERT_EQ(kFujitsuAcBits, irsend.capture.bits); - uint8_t expected_arrah2e[kFujitsuAcStateLength] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, - 0x20, 0x01, 0x14, 0x00, 0x00, 0x00, 0x20, 0x7B}; - EXPECT_TRUE(ArraysMatch(expected_arrah2e, irsend.capture.state)); - fujitsu.setRaw(irsend.capture.state, irsend.capture.bits / 8); - EXPECT_EQ(kFujitsuAcStateLength, fujitsu.getStateLength()); - EXPECT_EQ("Power: On, Mode: 1 (COOL), Temp: 18C, Fan: 4 (QUIET), " - "Swing: Vert, Command: N/A", fujitsu.toString()); - - irsend.reset(); - - fujitsu.setModel(ARDB1); - irsend.sendFujitsuAC(fujitsu.getRaw(), fujitsu.getStateLength()); - irsend.makeDecodeResult(); - EXPECT_TRUE(irrecv.decode(&irsend.capture)); - ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); - ASSERT_EQ(kFujitsuAcBits - 8, irsend.capture.bits); - uint8_t expected_ardb1[kFujitsuAcStateLength - 1] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFC, 0x08, 0x30, - 0x20, 0x01, 0x14, 0x00, 0x00, 0x00, 0x9B}; - EXPECT_TRUE(ArraysMatch(expected_ardb1, irsend.capture.state)); - fujitsu.setRaw(irsend.capture.state, irsend.capture.bits / 8); - EXPECT_EQ(kFujitsuAcStateLength - 1, fujitsu.getStateLength()); - EXPECT_EQ("Power: On, Mode: 1 (COOL), Temp: 18C, Fan: 4 (QUIET), " - "Swing: Vert, Command: N/A", fujitsu.toString()); -} - -TEST(TestDecodeFujitsuAC, RealShortARDB1OffExample) { - IRsendTest irsend(0); - IRrecv irrecv(0); - IRFujitsuAC fujitsu = IRFujitsuAC(0); - - irsend.begin(); - - irsend.reset(); - // "Off" Message recorded from an AR-DB1 remote. - uint16_t rawData[99] = { - 3310, 1636, 440, 386, 440, 394, 442, 1210, 442, 390, 414, 1220, - 444, 390, 446, 380, 446, 380, 436, 1216, 438, 1214, 438, 388, - 438, 386, 438, 396, 410, 1222, 440, 1220, 442, 384, 442, 384, - 442, 384, 442, 382, 444, 382, 442, 382, 444, 380, 446, 380, - 446, 380, 444, 380, 436, 390, 436, 388, 436, 388, 438, 1214, - 438, 386, 438, 388, 438, 386, 440, 386, 440, 384, 442, 384, - 442, 384, 442, 1210, 444, 382, 444, 382, 444, 382, 444, 380, - 446, 1206, 436, 390, 436, 388, 436, 388, 438, 388, 438, 396, - 420, 388, 436}; - irsend.sendRaw(rawData, 99, 38000); - irsend.makeDecodeResult(); - EXPECT_TRUE(irrecv.decode(&irsend.capture)); - ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); - ASSERT_EQ(kFujitsuAcMinBits, irsend.capture.bits); - uint8_t expected[6] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x02}; - EXPECT_TRUE(ArraysMatch(expected, irsend.capture.state)); - fujitsu.setRaw(irsend.capture.state, irsend.capture.bits / 8); - EXPECT_EQ(kFujitsuAcStateLengthShort - 1, fujitsu.getStateLength()); - EXPECT_EQ("Power: Off, Mode: 0 (AUTO), Temp: 16C, Fan: 0 (AUTO), " - "Swing: Off, Command: N/A", fujitsu.toString()); -} - -TEST(TestDecodeFujitsuAC, RealLongARDB1Example) { - IRsendTest irsend(0); - IRrecv irrecv(0); - IRFujitsuAC fujitsu = IRFujitsuAC(0); - - irsend.begin(); - irsend.reset(); - uint16_t rawData1[243] = { - 3316, 1632, 444, 390, 438, 388, 436, 1216, 438, 388, 438, 1214, - 438, 388, 438, 386, 440, 386, 440, 1212, 440, 1210, 442, 392, - 412, 396, 442, 392, 444, 1208, 444, 1208, 444, 380, 444, 380, - 446, 380, 436, 390, 436, 390, 436, 390, 436, 388, 438, 388, - 438, 388, 438, 388, 438, 386, 438, 386, 440, 384, 440, 1210, - 442, 384, 442, 382, 442, 384, 442, 384, 442, 382, 442, 382, - 444, 382, 444, 1208, 444, 382, 444, 380, 446, 380, 436, 390, - 436, 390, 436, 1214, 438, 1214, 438, 1212, 440, 1212, 440, 1220, - 412, 1222, 440, 394, 442, 382, 442, 382, 444, 1208, 444, 382, - 444, 380, 446, 380, 446, 380, 434, 390, 436, 388, 438, 388, - 438, 388, 438, 1214, 438, 1212, 440, 386, 440, 394, 412, 1222, - 440, 394, 442, 384, 442, 384, 442, 382, 442, 1208, 444, 390, - 414, 394, 442, 1216, 446, 380, 436, 390, 436, 390, 436, 388, - 436, 390, 436, 388, 438, 386, 440, 386, 440, 386, 438, 1212, - 440, 386, 440, 384, 440, 384, 442, 392, 412, 396, 440, 394, - 442, 382, 444, 382, 444, 382, 444, 380, 444, 380, 444, 382, - 444, 380, 446, 380, 436, 388, 436, 390, 436, 388, 438, 388, - 438, 388, 438, 388, 438, 386, 440, 386, 440, 386, 442, 384, - 440, 386, 442, 384, 440, 384, 442, 384, 442, 382, 442, 382, - 444, 1208, 444, 382, 444, 1208, 444, 380, 446, 1206, 436, 390, - 436, 1216, 436}; - irsend.sendRaw(rawData1, 243, 38000); - irsend.makeDecodeResult(); - EXPECT_TRUE(irrecv.decode(&irsend.capture)); - ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); - ASSERT_EQ(kFujitsuAcBits - 8, irsend.capture.bits); - uint8_t expected1[kFujitsuAcStateLength - 1] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFC, 0x08, 0x30, - 0x21, 0x01, 0x04, 0x00, 0x00, 0x00, 0xAA}; - EXPECT_TRUE(ArraysMatch(expected1, irsend.capture.state)); - fujitsu.setRaw(irsend.capture.state, irsend.capture.bits / 8); - EXPECT_EQ(kFujitsuAcStateLength - 1, fujitsu.getStateLength()); - EXPECT_EQ("Power: On, Mode: 1 (COOL), Temp: 18C, Fan: 4 (QUIET), " - "Swing: Off, Command: N/A", fujitsu.toString()); - - irsend.reset(); - uint16_t rawData2[243] = { - 3316, 1630, 436, 398, 438, 386, 438, 1212, 440, 384, 440, 1212, - 442, 384, 442, 392, 414, 394, 442, 1218, 446, 1206, 436, 390, - 436, 388, 438, 388, 438, 1214, 440, 1212, 440, 384, 442, 384, - 442, 384, 442, 382, 444, 382, 444, 382, 444, 380, 446, 380, - 444, 380, 436, 390, 436, 388, 438, 396, 418, 388, 438, 1232, - 410, 396, 440, 394, 442, 384, 442, 384, 442, 382, 442, 392, - 414, 392, 444, 1216, 446, 380, 436, 390, 436, 396, 418, 390, - 436, 398, 438, 1214, 440, 1212, 440, 1210, 442, 1208, 444, 1216, - 416, 1218, 444, 388, 436, 390, 436, 388, 438, 1214, 440, 386, - 438, 386, 440, 386, 440, 384, 442, 384, 442, 384, 442, 382, - 444, 382, 444, 1206, 446, 1206, 436, 390, 436, 388, 438, 388, - 438, 386, 440, 394, 410, 396, 440, 1220, 442, 1210, 442, 392, - 414, 394, 442, 1218, 446, 406, 410, 388, 436, 390, 436, 390, - 436, 388, 438, 386, 440, 386, 440, 386, 440, 386, 440, 384, - 442, 384, 442, 384, 442, 382, 444, 382, 444, 380, 446, 380, - 446, 380, 436, 390, 436, 390, 436, 388, 438, 386, 438, 388, - 438, 386, 440, 386, 440, 384, 442, 384, 442, 384, 442, 384, - 442, 382, 444, 382, 444, 380, 446, 380, 446, 380, 436, 390, - 436, 388, 436, 388, 438, 386, 438, 386, 440, 386, 440, 1212, - 440, 1210, 442, 1210, 442, 1208, 444, 1208, 436, 390, 436, 388, - 436, 1214, 440}; - irsend.sendRaw(rawData2, 243, 38000); - irsend.makeDecodeResult(); - EXPECT_TRUE(irrecv.decode(&irsend.capture)); - ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); - ASSERT_EQ(kFujitsuAcBits - 8, irsend.capture.bits); - uint8_t expected2[kFujitsuAcStateLength - 1] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFC, 0x08, 0x30, - 0x30, 0x01, 0x00, 0x00, 0x00, 0x00, 0x9F}; - EXPECT_TRUE(ArraysMatch(expected2, irsend.capture.state)); - fujitsu.setRaw(irsend.capture.state, irsend.capture.bits / 8); - EXPECT_EQ(kFujitsuAcStateLength - 1, fujitsu.getStateLength()); - EXPECT_EQ("Power: On, Mode: 1 (COOL), Temp: 19C, Fan: 0 (AUTO), " - "Swing: Off, Command: N/A", fujitsu.toString()); -} - -TEST(TestDecodeFujitsuAC, Issue414) { - IRsendTest irsend(0); - IRrecv irrecv(0); - IRFujitsuAC fujitsu = IRFujitsuAC(0); - - // Capture as supplied by arpmota - uint16_t rawData[259] = {3352, 1574, 480, 350, 480, 346, 480, 1190, 458, 346, - 508, 1140, 480, 346, 506, 346, 458, 346, 480, 1168, 480, 1192, 452, 374, - 458, 346, 480, 346, 508, 1168, 480, 1140, 480, 346, 506, 346, 458, 346, - 480, 346, 480, 346, 480, 346, 484, 372, 454, 374, 456, 346, 508, 318, - 480, 374, 458, 374, 480, 318, 480, 1196, 452, 346, 480, 346, 484, 342, - 484, 346, 480, 374, 458, 346, 506, 318, 508, 1170, 452, 346, 480, 374, - 458, 346, 506, 318, 480, 1196, 452, 1190, 458, 1162, 480, 1196, 452, - 1170, 480, 1190, 458, 1164, 480, 1196, 480, 318, 508, 346, 456, 1192, - 480, 346, 456, 374, 452, 346, 480, 374, 458, 342, 484, 346, 508, 346, - 456, 342, 512, 1164, 458, 1164, 508, 346, 456, 346, 480, 1190, 456, 342, - 484, 346, 506, 346, 456, 374, 452, 346, 508, 346, 458, 1164, 508, 346, - 458, 374, 452, 1168, 480, 374, 480, 318, 480, 374, 456, 346, 508, 318, - 480, 346, 484, 374, 480, 318, 484, 342, 484, 374, 480, 318, 484, 342, - 484, 346, 508, 318, 508, 346, 458, 346, 506, 318, 480, 374, 458, 346, - 506, 318, 480, 346, 484, 374, 480, 318, 482, 372, 456, 346, 508, 318, - 506, 348, 456, 342, 484, 346, 508, 318, 484, 374, 480, 318, 508, 318, - 484, 346, 508, 318, 480, 374, 456, 346, 508, 346, 480, 318, 480, 346, - 484, 374, 480, 320, 484, 1164, 508, 346, 458, 342, 512, 1164, 458, 1190, - 454, 346, 484, 1164, 508, 346, 458, 1164, 480, 350, 480, 374, 480}; - uint8_t state[16] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, 0x81, 0x04, 0x00, 0x00, - 0x00, 0x00, 0x20, 0x2B}; - irsend.begin(); - irsend.reset(); - irsend.sendRaw(rawData, 259, 38000); - irsend.makeDecodeResult(); - EXPECT_TRUE(irrecv.decode(&irsend.capture)); - ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); - ASSERT_EQ(kFujitsuAcBits, irsend.capture.bits); - EXPECT_TRUE(ArraysMatch(state, irsend.capture.state)); - fujitsu.setRaw(irsend.capture.state, irsend.capture.bits / 8); - EXPECT_EQ(kFujitsuAcStateLength, fujitsu.getStateLength()); - EXPECT_EQ("Power: On, Mode: 4 (HEAT), Temp: 24C, Fan: 0 (AUTO), " - "Swing: Off, Command: N/A", fujitsu.toString()); - - // Resend it using the state this time. - irsend.reset(); - irsend.sendFujitsuAC(state, 16); - irsend.makeDecodeResult(); - EXPECT_TRUE(irrecv.decode(&irsend.capture)); - ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); - ASSERT_EQ(kFujitsuAcBits, irsend.capture.bits); - EXPECT_TRUE(ArraysMatch(state, irsend.capture.state)); - EXPECT_EQ( - "f38000d50" - "m3324s1574" - "m448s390m448s390m448s1182m448s390m448s1182m448s390m448s390m448s390" - "m448s1182m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" - "m448s390m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182" - "m448s1182m448s390m448s390m448s1182m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s1182m448s1182m448s390m448s390" - "m448s1182m448s390m448s390m448s390m448s390m448s390m448s390m448s1182" - "m448s390m448s390m448s1182m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s1182m448s390m448s390" - "m448s1182m448s1182m448s390m448s1182m448s390m448s1182m448s390m448s390" - "m448s8100", irsend.outputStr()); -} diff --git a/lib/IRremoteESP8266-2.6.0/.github/CONTRIBUTING.md b/lib/IRremoteESP8266-2.6.5/.github/CONTRIBUTING.md similarity index 90% rename from lib/IRremoteESP8266-2.6.0/.github/CONTRIBUTING.md rename to lib/IRremoteESP8266-2.6.5/.github/CONTRIBUTING.md index 9614d90a5..20bb97d94 100644 --- a/lib/IRremoteESP8266-2.6.0/.github/CONTRIBUTING.md +++ b/lib/IRremoteESP8266-2.6.5/.github/CONTRIBUTING.md @@ -33,8 +33,8 @@ Before creating bug reports, please check [this list](#before-submitting-a-bug-r #### Before Submitting A Bug Report -* **Check the [Troubleshooting Guide](https://github.com/markszabo/IRremoteESP8266/wiki/Troubleshooting-Guide).** You might be able to find the cause of the problem and fix it yourself. Most importantly, check if you can reproduce the problem in the latest version (a.k.a. 'master') of the library. -* **Perform a [cursory search](https://github.com/issues?q=+is%3Aissue+repo%3Amarkszabo/IRremoteESP8266)** to see if the problem is already reported. If it has **and the issue is still open**, add a comment to the existing issue instead of opening a new one. +* **Check the [Troubleshooting Guide](https://github.com/crankyoldgit/IRremoteESP8266/wiki/Troubleshooting-Guide).** You might be able to find the cause of the problem and fix it yourself. Most importantly, check if you can reproduce the problem in the latest version (a.k.a. 'master') of the library. +* **Perform a [cursory search](https://github.com/issues?q=+is%3Aissue+repo%3Acrankyoldgit/IRremoteESP8266)** to see if the problem is already reported. If it has **and the issue is still open**, add a comment to the existing issue instead of opening a new one. #### How Do I Submit A (Good) Bug Report? @@ -53,7 +53,7 @@ Provide more context by answering these questions: * **Can you reproduce the problem in one of the code examples?** * **Did the problem start happening recently** (e.g. after updating to a new version of Arduino or the library) or was this always a problem? -* If the problem started happening recently, **can you reproduce the problem in an older version of the library?** What's the most recent version in which the problem doesn't happen? You can download older versions of the library from [the releases page](https://github.com/markszabo/IRremoteESP8266/releases). +* If the problem started happening recently, **can you reproduce the problem in an older version of the library?** What's the most recent version in which the problem doesn't happen? You can download older versions of the library from [the releases page](https://github.com/crankyoldgit/IRremoteESP8266/releases). * **Can you reliably reproduce the issue?** If not, provide details about how often the problem happens and under which conditions it normally happens. Include details about your configuration, circuit and environment: diff --git a/lib/IRremoteESP8266-2.6.0/.github/Contributors.md b/lib/IRremoteESP8266-2.6.5/.github/Contributors.md similarity index 86% rename from lib/IRremoteESP8266-2.6.0/.github/Contributors.md rename to lib/IRremoteESP8266-2.6.5/.github/Contributors.md index af9734d69..a4958fe70 100644 --- a/lib/IRremoteESP8266-2.6.0/.github/Contributors.md +++ b/lib/IRremoteESP8266-2.6.5/.github/Contributors.md @@ -2,7 +2,7 @@ ### Main contributors & maintainers - [Mark Szabo](https://github.com/markszabo/) : Initial IR sending on ESP8266 - [Sébastien Warin](https://github.com/sebastienwarin/) (http://sebastien.warin.fr) : Initial IR receiving on ESP8266 -- [David Conran](https://github.com/crankyoldgit/) +- [David Conran](https://github.com/crankyoldgit/) : ESP32 support and pretty much everything else. - [Roi Dayan](https://github.com/roidayan/) - [Marcos de Alcântara Marinho](https://github.com/marcosamarinho/) - [Massimiliano Pinto](https://github.com/pintomax/) @@ -15,6 +15,6 @@ - [Fabien Valthier](https://github.com/hcoohb) - [Ajay Pala](https://github.com/ajaypala/) -All contributors can be found on the [contributors site](https://github.com/markszabo/IRremoteESP8266/graphs/contributors). +All contributors can be found on the [contributors site](https://github.com/crankyoldgit/IRremoteESP8266/graphs/contributors). ### Contributors of the [original project](https://github.com/z3t0/Arduino-IRremote) can be found on the [original project's contributors page](https://github.com/z3t0/Arduino-IRremote/blob/master/Contributors.md) diff --git a/lib/IRremoteESP8266-2.6.0/.github/issue_template.md b/lib/IRremoteESP8266-2.6.5/.github/issue_template.md similarity index 77% rename from lib/IRremoteESP8266-2.6.0/.github/issue_template.md rename to lib/IRremoteESP8266-2.6.5/.github/issue_template.md index 024a0398c..d9b80dab6 100644 --- a/lib/IRremoteESP8266-2.6.0/.github/issue_template.md +++ b/lib/IRremoteESP8266-2.6.5/.github/issue_template.md @@ -1,6 +1,6 @@ -_(Please use this template for reporting issues. You can delete what ever is not relevant. Giving us this information will help us help you faster. Please also read the [FAQ](https://github.com/markszabo/IRremoteESP8266/wiki/Frequently-Asked-Questions) & [Troubleshooting Guide](https://github.com/markszabo/IRremoteESP8266/wiki/Troubleshooting-Guide). Your problem may already have an answer there.)_ +_(Please use this template for reporting issues. You can delete what ever is not relevant. Giving us this information will help us help you faster. Please also read the [FAQ](https://github.com/crankyoldgit/IRremoteESP8266/wiki/Frequently-Asked-Questions) & [Troubleshooting Guide](https://github.com/crankyoldgit/IRremoteESP8266/wiki/Troubleshooting-Guide). Your problem may already have an answer there.)_ -### Version/revison of the library used +### Version/revision of the library used _Typically located in the `library.json` & `src/IRremoteESP8266.h` files in the root directory of the library. e.g. v2.0.0, or 'master' as at 1st of June, 2017. etc._ @@ -30,9 +30,9 @@ _What can we do to (pref. reliably) repeat what is happening?_ _Include all relevant code snippets or links to the actual code files. Tip: [How to quote your code so it is still readable](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet#code)._ #### Circuit diagram and hardware used (if applicable) -_Link to an image of the circuit diagram used. Part number of the IR receiver module etc._ +_Link to an image of the circuit diagram used. Part number of the IR receiver module etc. ESP8266 or ESP32 board type._ -### I have followed the steps in the [Troubleshooting Guide](https://github.com/markszabo/IRremoteESP8266/wiki/Troubleshooting-Guide) & read the [FAQ](https://github.com/markszabo/IRremoteESP8266/wiki/Frequently-Asked-Questions) +### I have followed the steps in the [Troubleshooting Guide](https://github.com/crankyoldgit/IRremoteESP8266/wiki/Troubleshooting-Guide) & read the [FAQ](https://github.com/crankyoldgit/IRremoteESP8266/wiki/Frequently-Asked-Questions) _Yes/No._ ### Has this library/code previously worked as expected for you? diff --git a/lib/IRremoteESP8266-2.6.0/.gitignore b/lib/IRremoteESP8266-2.6.5/.gitignore similarity index 91% rename from lib/IRremoteESP8266-2.6.0/.gitignore rename to lib/IRremoteESP8266-2.6.5/.gitignore index 23e21ca3e..c02171953 100644 --- a/lib/IRremoteESP8266-2.6.0/.gitignore +++ b/lib/IRremoteESP8266-2.6.5/.gitignore @@ -8,8 +8,12 @@ # vi/vim **/*.swp +# vscode +.vscode + ## Build environments # Platformio +**/.pio/ **/.pioenvs/ **/.piolibdeps/ **/.clang_complete @@ -44,3 +48,6 @@ tools/mode2_decode #Cygwin builds *.exe + +# Mac extended attributes +.DS_Store diff --git a/lib/IRremoteESP8266-2.6.0/.gitmodules b/lib/IRremoteESP8266-2.6.5/.gitmodules similarity index 100% rename from lib/IRremoteESP8266-2.6.0/.gitmodules rename to lib/IRremoteESP8266-2.6.5/.gitmodules diff --git a/lib/IRremoteESP8266-2.6.0/.style.yapf b/lib/IRremoteESP8266-2.6.5/.style.yapf similarity index 100% rename from lib/IRremoteESP8266-2.6.0/.style.yapf rename to lib/IRremoteESP8266-2.6.5/.style.yapf diff --git a/lib/IRremoteESP8266-2.6.5/.travis.yml b/lib/IRremoteESP8266-2.6.5/.travis.yml new file mode 100644 index 000000000..e8bf3d832 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.5/.travis.yml @@ -0,0 +1,74 @@ +language: c +env: + - BD=esp8266:esp8266:nodemcuv2:xtal=80,eesz=4M3M,ip=lm2f,exception=disabled + # - BD=esp8266:esp8266:d1_mini:xtal=80,eesz=4M3M,ip=lm2f,exception=disabled +before_install: + - "/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_1.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :1 -ac -screen 0 1280x1024x16" + - sleep 3 + - export DISPLAY=:1.0 + - wget http://downloads.arduino.cc/arduino-1.8.8-linux64.tar.xz + - tar xf arduino-1.8.8-linux64.tar.xz + - sudo mv arduino-1.8.8 /usr/local/share/arduino + - sudo ln -s /usr/local/share/arduino/arduino /usr/local/bin/arduino + - wget https://raw.githubusercontent.com/google/styleguide/gh-pages/cpplint/cpplint.py +install: + - ln -s $PWD /usr/local/share/arduino/libraries/ + - git clone https://github.com/tzapu/WiFiManager.git /usr/local/share/arduino/libraries/WiFiManager + - git clone https://github.com/knolleary/pubsubclient.git /usr/local/share/arduino/libraries/PubSubClient + - git clone https://github.com/bblanchon/ArduinoJson.git --branch 5.x /usr/local/share/arduino/libraries/ArduinoJson + - arduino --pref "boardsmanager.additional.urls=http://arduino.esp8266.com/stable/package_esp8266com_index.json" --save-prefs + - arduino --install-boards esp8266:esp8266 + - arduino --board $BD --save-prefs + - arduino --pref "compiler.warning_level=all" --save-prefs + - sudo apt-get install jq + - sudo apt-get purge python-enum34 + - sudo apt-get install pylint3 +script: echo Running checks +notifications: + email: + on_success: change + on_failure: change +jobs: + include: + - script: + # Check that everything compiles. (Part 1) + - arduino --verify --board $BD $PWD/examples/IRrecvDemo/IRrecvDemo.ino 2> /dev/null + - arduino --verify --board $BD $PWD/examples/IRGCSendDemo/IRGCSendDemo.ino 2> /dev/null + - arduino --verify --board $BD $PWD/examples/IRGCTCPServer/IRGCTCPServer.ino 2> /dev/null + - arduino --verify --board $BD $PWD/examples/IRServer/IRServer.ino 2> /dev/null + - arduino --verify --board $BD $PWD/examples/IRrecvDumpV2/IRrecvDumpV2.ino 2> /dev/null + - arduino --verify --board $BD $PWD/examples/IRsendDemo/IRsendDemo.ino 2> /dev/null + - arduino --verify --board $BD $PWD/examples/JVCPanasonicSendDemo/JVCPanasonicSendDemo.ino 2> /dev/null + - arduino --verify --board $BD $PWD/examples/TurnOnDaikinAC/TurnOnDaikinAC.ino 2> /dev/null + - arduino --verify --board $BD $PWD/examples/TurnOnFujitsuAC/TurnOnFujitsuAC.ino 2> /dev/null + - arduino --verify --board $BD $PWD/examples/TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino 2> /dev/null + - arduino --verify --board $BD $PWD/examples/TurnOnMitsubishiAC/TurnOnMitsubishiAC.ino 2> /dev/null + - script: + # Check that everything compiles. (Part 2) + - arduino --verify --board $BD $PWD/examples/IRsendProntoDemo/IRsendProntoDemo.ino 2> /dev/null + - arduino --verify --board $BD $PWD/examples/TurnOnTrotecAC/TurnOnTrotecAC.ino 2> /dev/null + - arduino --verify --board $BD $PWD/examples/LGACSend/LGACSend.ino 2> /dev/null + - arduino --verify --board $BD $PWD/examples/TurnOnArgoAC/TurnOnArgoAC.ino 2> /dev/null + - arduino --verify --board $BD $PWD/examples/IRMQTTServer/IRMQTTServer.ino 2> /dev/null + - arduino --verify --board $BD $PWD/examples/TurnOnToshibaAC/TurnOnToshibaAC.ino 2> /dev/null + - arduino --verify --board $BD $PWD/examples/ControlSamsungAC/ControlSamsungAC.ino 2> /dev/null + - arduino --verify --board $BD $PWD/examples/TurnOnPanasonicAC/TurnOnPanasonicAC.ino 2> /dev/null + - arduino --verify --board $BD $PWD/examples/TurnOnMitsubishiHeavyAc/TurnOnMitsubishiHeavyAc.ino 2> /dev/null + - arduino --verify --board $BD $PWD/examples/DumbIRRepeater/DumbIRRepeater.ino 2> /dev/null + - arduino --verify --board $BD $PWD/examples/SmartIRRepeater/SmartIRRepeater.ino 2> /dev/null + - arduino --verify --board $BD $PWD/examples/CommonAcControl/CommonAcControl.ino 2> /dev/null + - script: + # Check the version numbers match. + - LIB_VERSION=$(egrep "^#define\s+_IRREMOTEESP8266_VERSION_\s+" src/IRremoteESP8266.h | cut -d\" -f2) + - test ${LIB_VERSION} == "$(jq -r .version library.json)" + - grep -q "^version=${LIB_VERSION}$" library.properties + # Check the tools programs compile. + - (cd tools; make all) + # Check for lint issues. + - shopt -s nullglob + - python cpplint.py --extensions=c,cc,cpp,ino --headers=h,hpp {src,test,tools}/*.{h,c,cc,cpp,hpp,ino} examples/*/*.{h,c,cc,cpp,hpp,ino} + - pylint3 -d F0001 {src,test,tools}/*.py + - shopt -u nullglob + # Build and run the unit tests. + - (cd test; make run) + - (cd tools; make run_tests) diff --git a/lib/IRremoteESP8266-2.6.0/CPPLINT.cfg b/lib/IRremoteESP8266-2.6.5/CPPLINT.cfg similarity index 100% rename from lib/IRremoteESP8266-2.6.0/CPPLINT.cfg rename to lib/IRremoteESP8266-2.6.5/CPPLINT.cfg diff --git a/lib/IRremoteESP8266-2.6.0/LICENSE.txt b/lib/IRremoteESP8266-2.6.5/LICENSE.txt similarity index 100% rename from lib/IRremoteESP8266-2.6.0/LICENSE.txt rename to lib/IRremoteESP8266-2.6.5/LICENSE.txt diff --git a/lib/IRremoteESP8266-2.6.0/README.md b/lib/IRremoteESP8266-2.6.5/README.md similarity index 62% rename from lib/IRremoteESP8266-2.6.0/README.md rename to lib/IRremoteESP8266-2.6.5/README.md index 1eaaa21b4..c4cb31515 100644 --- a/lib/IRremoteESP8266-2.6.0/README.md +++ b/lib/IRremoteESP8266-2.6.5/README.md @@ -1,18 +1,19 @@ # IRremote ESP8266 Library -[![Build Status](https://travis-ci.org/markszabo/IRremoteESP8266.svg?branch=master)](https://travis-ci.org/markszabo/IRremoteESP8266) +[![Build Status](https://travis-ci.org/crankyoldgit/IRremoteESP8266.svg?branch=master)](https://travis-ci.org/crankyoldgit/IRremoteESP8266) [![arduino-library-badge](https://www.ardu-badge.com/badge/IRremoteESP8266.svg?)](https://www.ardu-badge.com/IRremoteESP8266) -[![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/markszabo/IRremoteESP8266.svg)](http://isitmaintained.com/project/markszabo/IRremoteESP8266 "Average time to resolve an issue") -[![Percentage of issues still open](http://isitmaintained.com/badge/open/markszabo/IRremoteESP8266.svg)](http://isitmaintained.com/project/markszabo/IRremoteESP8266 "Percentage of issues still open") -[![GitLicense](https://gitlicense.com/badge/markszabo/IRremoteESP8266)](https://gitlicense.com/license/markszabo/IRremoteESP8266) +[![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/crankyoldgit/IRremoteESP8266.svg)](http://isitmaintained.com/project/crankyoldgit/IRremoteESP8266 "Average time to resolve an issue") +[![Percentage of issues still open](http://isitmaintained.com/badge/open/crankyoldgit/IRremoteESP8266.svg)](http://isitmaintained.com/project/crankyoldgit/IRremoteESP8266 "Percentage of issues still open") +[![GitLicense](https://gitlicense.com/badge/crankyoldgit/IRremoteESP8266)](https://gitlicense.com/license/crankyoldgit/IRremoteESP8266) -This library enables you to **send _and_ receive** infra-red signals on an [ESP8266 using the Arduino framework](https://github.com/esp8266/Arduino) using common 940nm IR LEDs and common IR receiver modules. e.g. TSOP{17,22,24,36,38,44,48}* etc. +This library enables you to **send _and_ receive** infra-red signals on an [ESP8266](https://github.com/esp8266/Arduino) or an +[ESP32](https://github.com/espressif/arduino-esp32) using the [Arduino framework](https://www.arduino.cc/) using common 940nm IR LEDs and common IR receiver modules. e.g. TSOP{17,22,24,36,38,44,48}* demodulators etc. -## v2.6.0 Now Available -Version 2.6.0 of the library is now [available](https://github.com/markszabo/IRremoteESP8266/releases/latest). You can view the [Release Notes](ReleaseNotes.md) for all the significant changes. +## v2.6.5 Now Available +Version 2.6.4 of the library is now [available](https://github.com/crankyoldgit/IRremoteESP8266/releases/latest). You can view the [Release Notes](ReleaseNotes.md) for all the significant changes. #### Upgrading from pre-v2.0 -Usage of the library has been slightly changed in v2.0. You will need to change your usage to work with v2.0 and beyond. You can read more about the changes required on our [Upgrade to v2.0](https://github.com/markszabo/IRremoteESP8266/wiki/Upgrading-to-v2.0) page. +Usage of the library has been slightly changed in v2.0. You will need to change your usage to work with v2.0 and beyond. You can read more about the changes required on our [Upgrade to v2.0](https://github.com/crankyoldgit/IRremoteESP8266/wiki/Upgrading-to-v2.0) page. #### Upgrading from pre-v2.5 The library has changed from using constants declared as `#define` to @@ -29,11 +30,15 @@ something you likely should not have. You should be able to quickly determine the new name from the old. e.g. `CONSTANT_NAME` to `kConstantName`. Use common sense or examining the library's code if this does affect code. +## Supported protocols +You can find the details of which protocols & devices are supported +[here](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/SupportedProtocols.md). + ## Troubleshooting -Before reporting an issue or asking for help, please try to follow our [Troubleshooting Guide](https://github.com/markszabo/IRremoteESP8266/wiki/Troubleshooting-Guide) first. +Before reporting an issue or asking for help, please try to follow our [Troubleshooting Guide](https://github.com/crankyoldgit/IRremoteESP8266/wiki/Troubleshooting-Guide) first. ## Frequently Asked Questions -Some common answers to common questions and problems are on our [F.A.Q. wiki page](https://github.com/markszabo/IRremoteESP8266/wiki/Frequently-Asked-Questions). +Some common answers to common questions and problems are on our [F.A.Q. wiki page](https://github.com/crankyoldgit/IRremoteESP8266/wiki/Frequently-Asked-Questions). ## Installation ##### Official releases via the Arduino IDE v1.8+ (Windows & Linux) @@ -43,7 +48,7 @@ Some common answers to common questions and problems are on our [F.A.Q. wiki pag 1. Select the version you wish to install and click _"Install"_. ##### Manual Installation for Windows -1. Click on _"Clone or Download"_ button, then _"[Download ZIP](https://github.com/markszabo/IRremoteESP8266/archive->master.zip)"_ on the page. +1. Click on _"Clone or Download"_ button, then _"[Download ZIP](https://github.com/crankyoldgit/IRremoteESP8266/archive->master.zip)"_ on the page. 1. Extract the contents of the downloaded zip file. 1. Rename the extracted folder to _"IRremoteESP8266"_. 1. Move this folder to your libraries directory. (under windows: `C:\Users\YOURNAME\Documents\Arduino\libraries\`) @@ -53,7 +58,7 @@ Some common answers to common questions and problems are on our [F.A.Q. wiki pag ##### Using Git to install library ( Linux ) ``` cd ~/Arduino/libraries -git clone https://github.com/markszabo/IRremoteESP8266.git +git clone https://github.com/crankyoldgit/IRremoteESP8266.git ``` ###### To Update to the latest version of the library ``` @@ -74,6 +79,6 @@ Available [here](.github/Contributors.md) ## Library History This library was originally based on Ken Shirriff's work (https://github.com/shirriff/Arduino-IRremote/) -[Mark Szabo](https://github.com/markszabo/IRremoteESP8266) has updated the IRsend class to work on ESP8266 and [Sebastien Warin](https://github.com/sebastienwarin/IRremoteESP8266) the receiving & decoding part (IRrecv class). +[Mark Szabo](https://github.com/crankyoldgit/IRremoteESP8266) has updated the IRsend class to work on ESP8266 and [Sebastien Warin](https://github.com/sebastienwarin/IRremoteESP8266) the receiving & decoding part (IRrecv class). As of v2.0, the library was almost entirely re-written with the ESP8266's resources in mind. diff --git a/lib/IRremoteESP8266-2.6.0/ReleaseNotes.md b/lib/IRremoteESP8266-2.6.5/ReleaseNotes.md similarity index 69% rename from lib/IRremoteESP8266-2.6.0/ReleaseNotes.md rename to lib/IRremoteESP8266-2.6.5/ReleaseNotes.md index 98416a12a..5672d2483 100644 --- a/lib/IRremoteESP8266-2.6.0/ReleaseNotes.md +++ b/lib/IRremoteESP8266-2.6.5/ReleaseNotes.md @@ -1,5 +1,146 @@ # Release Notes +## _v2.6.5 (20190828)_ + +**[Bug Fixes]** +- IRMQTTServer: Remove duplicate MQTT_CLIMATE from HA discovery (#869) +- Fujitsu: Ensure `on()` is called in common a/c framework. (#862) +- Update `strToModel()` (#861) +- IRMQTTServer: Add missing header file. (#858) +- IRMQTTServer: Fix a compile error when HTML_PASSWORD_ENABLE is enabled. (#856) + +**[Features]** +- IRrecv: Allow tolerance percentage to be set at run-time. (#865) +- Basic support for Daikin152 A/C protocol. (#874) +- Teco: Add light, humid, & save support. (#871) +- Detailed support for Amcor A/C protocol. (#836, #854) +- IRMQTTServer: Add ability to report Vcc at the ESP chip. (#845) +- Gree: Add timer support. (#849) +- IRac/Mitsubishi A/C: Support wide `swingh_t` mode (#844) +- IRMQTTServer: Generate protocol and bit size html selects (#838) + +**[Misc]** +- New example code to show how to use the `IRac` class to control A/Cs (#839) +- Improve/fix `swingh_t::kWide` support (#846) +- Kelvinator: Optimise code a little to save space. (#843) + + +## _v2.6.4 (20190726)_ + +**[Bug Fixes]** +- Fix some swing problems with the Mitsubishi HAVC protocol (#831) +- Fix parameter ordering for Gree in common a/c code. (#815) +- Fix parameters for Coolix in IRac::sendAc() (#829) +- IRMQTTServer: Fix sending >64 bit codes. (#811) + +**[Features]** +- Daikin128: Full detailed support & common a/c support. (#832) +- Midea: Support native temp units of Celsius & SwingV. (#823) +- Gree: Support `YBOFB` models and bug fix. (#815) +- Pioneer: Fix sendPioneer with Pioneer specific timings (#830) +- Daikin128: Initial support for Daikin 17 Series/BRC52B63 (#828) +- Coolix: Better `toCommon()` support. (#825) +- Experimental detailed support for Daikin 176 bits (#816) +- Add setting of output options to A/C classes. (#808) +- Add invert flag support to Samsung AC (#807) + +**[Misc]** +- Daikin176: making some change on Daikin176 to work with IRMQTTServer (#826) +- Reduce duplicate code to save (3K+) space. (#813) +- Daikin176: Experiment Daikin176bits with IRMQTTServer (#824) +- Update platformio.ini files for PlatformIO v4.0.0 (#812) +- Change repo URLs to new location. (#806) +- Move `htmlEscape()` to the IRutils namespace (#801) + + +## _v2.6.3 (20190704)_ + +**[Bug Fixes]** +- IRMQTTServer: REPLAY_DECODED_AC_MESSAGE not working. (#784, #797) +- ESP32: Ensure `IRrecv`'s GPIO is set to input mode. (#774) + +**[Features]** +- IRMQTTServer: Show available sketch space for OTA uploads. (#795) +- Experimental detailed support for Electra/AUX protocol (#788) +- IRMQTTServer: Ability to resend existing climate state via MQTT & HTTP (#784) +- Daikin160: Add detailed & common a/c support. (#777) +- Experimental detailed support for Neoclima protocol. (#767) +- Gree: add WiFi and IFeel bits (#770) +- Handle A/Cs with toggles better. (#758) +- IRMQTTServer: Allow sending/receiving climate via JSON over MQTT. (#763) + +**[Misc]** +- Move converting of IR A/C messages out of example code. (#798) +- Reduce example code size and complexity (#790) +- Change `ControlSamsungAC` example to not use `sendExtended()` (#792) +- IRMQTTServer: Add MQTT_CLIMATE_IR_SEND_ON_RESTART compile-time flag. (#784) +- Refactor A/C's toString()'s to reduce code size. Saves ~3.5k (#782) +- Add sanity tests for unexpected conditions in IRrecv. (#773) +- IRMQTTServer: Fixed the HA config documentation (missing '-') (#776) +- Improve `mkkeywords` tool. (#766) +- Refactor with generic decode routines in `IRrecv` class. Saves ~7k. (#765) + + +## _v2.6.2 (20190616)_ + +**[Features]** +- Initial support for the ESP32 architecture & boards. (#742) +- Add changable GPIO settings to IRMQTTServer. (#730) +- IRMQTTServer: Enforce a repeat for all Coolix calls (#752) +- Basic DAIKIN 160bit send and decode. (#754) +- Add example code for a Smart(er) IR Repeater. (#740) +- Enforce Samsung A/C Quiet & Powerful mutual exclusivity. + +**[Misc]** +- IRMQTTServer: Add some memory alloc safety checks. (#749) +- Move some ToString() functions to IRac.cpp (#748) +- Increase tolerance value for TCL112AC protocol. (#745) +- Fix compiler warning in IRutils_test.cpp (#756) +- Scrape Supported Protocols and generate SupportedProtocols.md (#755) +- Make supported device info more organised. (#753) + + +## _v2.6.1 (20190609)_ + +**[Breaking Changes]** +- Major rework/breaking changes to Argo A/C support. (#705) + +**[Bug Fixes]** +- Correct `set/getQuiet` for Samsung A/C (#736) +- Add missing `on/off()` to IRCoolixAC class. (#725) +- Daikin `set/getEye()` uses wrong bit. (#711) +- IRMQTTServer: Continue to use same Temperature units. (#710) +- Fixed a bug with `setMode()`/`getMode()` for HAIER_AC. (#705) + +**[Features]** +- Add set/getPowerful for Samsung A/C (#736) +- Add `calibrate()` to all the A/C classes. (#735) +- IRMQTTServer: Add sequencing for sending MQTT IR commands. (#723) +- Add support for Fujitsu AR-REB1E & AR-JW2 remotes. (#718) +- Add Beta `decodeTrotec()` support. (#719) +- Add experimental `decodeArgo()` support. (#717) +- Support for Goodweather A/Cs. (#715) +- Add `DISABLE_CAPTURE_WHILE_TRANSMITTING` feature to IRMQTTServer. (#713) +- Support for Lixil Inax Toilet protocol. (#712) +- Add `set/getWeeklyTimerEnable()` to Daikin (#711) +- IRMQTTServer: Update Common A/C settings based on received IR messages. (#705) +- Add day of week to DAIKIN protocol (#699) +- Add limited support for Sharp A/C (#696) +- SAMSUNG_AC: Make sure special power mode messages are sent. (#695) +- Add `set/getPowerful()` (turbo) to DAIKIN216 (#693) + +**[Misc]** +- Add kPeriodOffset for CPU Freq of 160MHz. (#729) +- Example code for a Dumb IR repeater. (#737) +- Update swing handling for Fujitsu A/Cs. (#724) +- Add function to convert `decode_results` to `sendRaw()` array. (#721) +- Attempt to reduce heap fragmentation from strings. (#707) +- Update Fujitsu A/C example code to safer settings (#716) +- Enforce better `const` usage in IRUtils. (#708) +- Attempt to reduce heap fragmentation by A/C `toString()`s. (#694) +- Minor changes to DAIKIN216 timings and features. (#693) + + ## _v2.6.0 (20190430)_ **[Bug Fixes]** diff --git a/lib/IRremoteESP8266-2.6.5/SupportedProtocols.md b/lib/IRremoteESP8266-2.6.5/SupportedProtocols.md new file mode 100644 index 000000000..c9d286973 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.5/SupportedProtocols.md @@ -0,0 +1,141 @@ + +# IR Protocols supported by this library + +| Protocol | Brand | Model | A/C Model | Detailed A/C Support | +| --- | --- | --- | --- | --- | +| [Aiwa](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Aiwa.cpp) | **Aiwa** | RC-T501 RCU | | - | +| [Amcor](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Amcor.cpp) | **[Amcor](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Amcor.h)** | ADR-853H A/C
ADR-853H A/C
TAC-444 remote
TAC-444 remote
TAC-495 remote
TAC-495 remote | | Yes | +| [Argo](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Argo.cpp) | **[Argo](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Argo.h)** | Ulisse 13 DCI Mobile Split A/C | | Yes | +| [Carrier](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Carrier.cpp) | **Carrier/Surrey** | 42QG5A55970 remote
53NGK009/012 Inverter
619EGX0090E0 A/C
619EGX0120E0 A/C
619EGX0180E0 A/C
619EGX0220E0 A/C | | - | +| [Coolix](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Coolix.cpp) | **[Beko](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Coolix.h)** | BINR 070/071 split-type A/C
BINR 070/071 split-type A/C
RG57K7(B)/BGEF Remote
RG57K7(B)/BGEF Remote | | Yes | +| [Coolix](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Coolix.cpp) | **[Midea](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Coolix.h)** | MS12FU-10HRDN1-QRD0GW(B) A/C
MS12FU-10HRDN1-QRD0GW(B) A/C
MSABAU-07HRFN1-QRD0GW A/C (circa 2016)
MSABAU-07HRFN1-QRD0GW A/C (circa 2016)
RG52D/BGE Remote
RG52D/BGE Remote | | Yes | +| [Daikin](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Daikin.cpp) | **[Daikin](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Daikin.h)** | 17 Series A/C (DAIKIN128)
ARC423A5 remote
ARC433** remote
ARC433B69 remote
ARC477A1 remote
ARC480A5 remote (DAIKIN152)
BRC4C153 remote
BRC52B63 remote (DAIKIN128)
FTE12HV2S A/C
FTXB09AXVJU A/C (DAIKIN128)
FTXB12AXVJU A/C (DAIKIN128)
FTXZ25NV1B A/C
FTXZ35NV1B A/C
FTXZ50NV1B A/C | | Yes | +| [Denon](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Denon.cpp) | **Unknown** | | | - | +| [Dish](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Dish.cpp) | **DISH NETWORK** | echostar 301 | | - | +| [Electra](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Electra.cpp) | **[AUX](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Electra.h)** | KFR-35GW/BpNFW=3 A/C
YKR-T/011 remote | | Yes | +| [Fujitsu](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Fujitsu.cpp) | **[Fujitsu](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Fujitsu.h)** | AR-DB1 remote
AR-RAE1E remote
AR-RAH2E remote
AR-REB1E remote
AST9RSGCW A/C
ASYG30LFCA A/C
ASYG7LMCA A/C | ARDB1
ARJW2
ARRAH2E
ARREB1E | Yes | +| [Fujitsu](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Fujitsu.cpp) | **[Fujitsu General](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Fujitsu.h)** | AR-JW2 remote | ARDB1
ARJW2
ARRAH2E
ARREB1E | Yes | +| [GICable](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_GICable.cpp) | **Unknown** | | | - | +| [GlobalCache](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_GlobalCache.cpp) | **Unknown** | | | - | +| [Goodweather](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Goodweather.cpp) | **[Goodweather](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Goodweather.h)** | ZH/JT-03 remote | | Yes | +| [Gree](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Gree.cpp) | **[EKOKAI](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Gree.h)** | A/C | YAW1F
YBOFB | Yes | +| [Gree](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Gree.cpp) | **[Green](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Gree.h)** | YBOFB remote
YBOFB2 remote | YAW1F
YBOFB | Yes | +| [Gree](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Gree.cpp) | **[RusClimate](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Gree.h)** | EACS/I-09HAR_X/N3 A/C
YAW1F remote | YAW1F
YBOFB | Yes | +| [Gree](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Gree.cpp) | **[Ultimate](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Gree.h)** | Heat Pump | YAW1F
YBOFB | Yes | +| [Haier](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Haier.cpp) | **[Haier](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Haier.h)** | HSU-09HMC203 A/C
HSU07-HEA03 remote
YR-W02 remote | | Yes | +| [Hitachi](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Hitachi.cpp) | **[Hitachi](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Hitachi.h)** | LT0541-HTA remote
RAS-35THA6 remote
Series VI A/C (Circa 2007) | | Yes | +| [Inax](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Inax.cpp) | **Lixil** | Inax DT-BA283 Toilet | | - | +| [JVC](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_JVC.cpp) | **Unknown** | | | - | +| [Kelvinator](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Kelvinator.cpp) | **[Green](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Kelvinator.h)** | YAPOF3 remote | | Yes | +| [Kelvinator](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Kelvinator.cpp) | **[Kelvinator](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Kelvinator.h)** | KSV26CRC A/C
KSV26HRC A/C
KSV35CRC A/C
KSV35HRC A/C
KSV53HRC A/C
KSV62HRC A/C
KSV70CRC A/C
KSV70HRC A/C
KSV80HRC A/C
YALIF Remote | | Yes | +| [LG](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_LG.cpp) | **[LG](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_LG.h)** | 6711A20083V remote
6711A20083V remote
AKB74395308 remote
AKB74395308 remote | | Yes | +| [Lasertag](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Lasertag.cpp) | **Unknown** | | | - | +| [Lego](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Lego.cpp) | **LEGO Power Functions** | IR Receiver | | - | +| [Lutron](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Lutron.cpp) | **Unknown** | | | - | +| [MWM](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_MWM.cpp) | **Unknown** | | | - | +| [Magiquest](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Magiquest.cpp) | **[Unknown](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Magiquest.h)** | | | Yes | +| [Midea](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Midea.cpp) | **[Comfee](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Midea.h)** | MPD1-12CRN7 A/C | | Yes | +| [Midea](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Midea.cpp) | **[Keystone](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Midea.h)** | RG57H4(B)BGEF remote | | Yes | +| [Midea](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Midea.cpp) | **[Pioneer System](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Midea.h)** | RUBO18GMFILCAD A/C (18K BTU)
RYBO12GMFILCAD A/C (12K BTU) | | Yes | +| [Mitsubishi](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Mitsubishi.cpp) | **[Mitsubishi](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Mitsubishi.h)** | HC3000 Projector
TV | | Yes | +| [MitsubishiHeavy](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_MitsubishiHeavy.cpp) | **[Mitsubishi Heavy Industries](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_MitsubishiHeavy.h)** | RKX502A001C remote
RLA502A700B remote
SRKxxZJ-S A/C
SRKxxZM-S A/C
SRKxxZMXA-S A/C | | Yes | +| [NEC](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_NEC.cpp) | **[Yamaha](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_NEC.h)** | RAV561 remote
RXV585B A/V Receiver | | Yes | +| [Neoclima](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Neoclima.cpp) | **[Neoclima](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Neoclima.h)** | NS-09AHTI A/C
NS-09AHTI A/C
ZH/TY-01 remote
ZH/TY-01 remote | | Yes | +| [Nikai](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Nikai.cpp) | **Unknown** | | | - | +| [Panasonic](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Panasonic.cpp) | **[Panasonic](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Panasonic.h)** | A75C2311 remote (CKP)
A75C3704 remote
A75C3747 remote
A75C3747 remote
A75C3747 remote
A75C3747 remote
CKP series A/C
CS-ME10CKPG A/C
CS-ME12CKPG A/C
CS-ME14CKPG A/C
CS-YW9MKD A/C
CS-Z9RKR A/C
DKE series A/C
JKE series A/C
NKE series A/C
RKR series A/C
TV | CKP
DKE
JKE
LKE
NKE
RKR | Yes | +| [Pioneer](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Pioneer.cpp) | **Unknown** | | | - | +| [Pronto](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Pronto.cpp) | **Unknown** | | | - | +| [RC5_RC6](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_RC5_RC6.cpp) | **Unknown** | | | - | +| [RCMM](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_RCMM.cpp) | **Microsoft** | XBOX 360 | | - | +| [Samsung](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Samsung.cpp) | **[Samsung](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Samsung.h)** | AR12HSSDBWKNEU A/C
AR12KSFPEWQNET A/C
IEC-R03 remote
UA55H6300 TV | | Yes | +| [Sanyo](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Sanyo.cpp) | **Unknown** | | | - | +| [Sharp](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Sharp.cpp) | **[Sharp](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Sharp.h)** | AY-ZP40KR A/C
LC-52D62U TV | | Yes | +| [Sherwood](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Sherwood.cpp) | **Sherwood** | RC-138 remote
RD6505(B) Receiver | | - | +| [Sony](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Sony.cpp) | **Unknown** | | | - | +| [Tcl](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Tcl.cpp) | **[Leberg](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Tcl.h)** | LBS-TOR07 A/C | | Yes | +| [Teco](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Teco.cpp) | **[Alaska](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Teco.h)** | SAC9010QC A/C
SAC9010QC remote | | Yes | +| [Toshiba](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Toshiba.cpp) | **[Toshiba](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Toshiba.h)** | Akita EVO II
RAS 18SKP-ES
RAS-B13N3KV2
RAS-B13N3KVP-E
WC-L03SE
WH-TA04NE | | Yes | +| [Trotec](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Trotec.cpp) | **[Unknown](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Trotec.h)** | | | Yes | +| [Vestel](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Vestel.cpp) | **[Vestel](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Vestel.h)** | BIOX CXP-9 A/C (9K BTU) | | Yes | +| [Whirlpool](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Whirlpool.cpp) | **[Whirlpool](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Whirlpool.h)** | DG11J1-04 remote
DG11J1-3A remote
DG11J1-91 remote
SPIS409L A/C
SPIS412L A/C
SPIW409L A/C
SPIW412L A/C
SPIW418L A/C | DG11J13A
DG11J191 | Yes | +| [Whynter](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Whynter.cpp) | **Whynter** | ARC-110WD A/C | | - | + + +## Send only protocols: + +- GLOBALCACHE +- PRONTO +- RAW +- SHERWOOD + + +## Send & decodable protocols: + +- AIWA_RC_T501 +- AMCOR +- ARGO +- CARRIER_AC +- COOLIX +- DAIKIN +- DAIKIN128 +- DAIKIN152 +- DAIKIN160 +- DAIKIN176 +- DAIKIN2 +- DAIKIN216 +- DENON +- DISH +- ELECTRA_AC +- FUJITSU_AC +- GICABLE +- GOODWEATHER +- GREE +- HAIER_AC +- HAIER_AC_YRW02 +- HITACHI_AC +- HITACHI_AC1 +- HITACHI_AC2 +- INAX +- JVC +- KELVINATOR +- LASERTAG +- LEGOPF +- LG +- LG2 +- LUTRON +- MAGIQUEST +- MIDEA +- MITSUBISHI +- MITSUBISHI2 +- MITSUBISHI_AC +- MITSUBISHI_HEAVY_152 +- MITSUBISHI_HEAVY_88 +- MWM +- NEC +- NEC_LIKE +- NEOCLIMA +- NIKAI +- PANASONIC +- PANASONIC_AC +- PIONEER +- RC5 +- RC5X +- RC6 +- RCMM +- SAMSUNG +- SAMSUNG36 +- SAMSUNG_AC +- SANYO +- SANYO_LC7461 +- SHARP +- SHARP_AC +- SONY +- TCL112AC +- TECO +- TOSHIBA_AC +- TROTEC +- VESTEL_AC +- WHIRLPOOL_AC +- WHYNTER diff --git a/lib/IRremoteESP8266-2.6.5/examples/CommonAcControl/CommonAcControl.ino b/lib/IRremoteESP8266-2.6.5/examples/CommonAcControl/CommonAcControl.ino new file mode 100644 index 000000000..6f0416b51 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.5/examples/CommonAcControl/CommonAcControl.ino @@ -0,0 +1,81 @@ +/* Copyright 2019 David Conran +* +* This example code demonstrates how to use the "Common" IRac class to control +* various air conditions. The IRac class does not support all the features +* for every protocol. Some have more detailed support that what the "Common" +* interface offers, and some only have a limited subset of the "Common" options. +* +* This example code will: +* o Try to turn on, then off every fully supported A/C protocol we know of. +* o It will try to put the A/C unit into Cooling mode at 25C, with a medium +* fan speed, and no fan swinging. +* Note: Some protocols support multiple models, only the first model is tried. +* +*/ +#include +#include +#include +#include + +const uint16_t kIrLed = 4; // The ESP GPIO pin to use that controls the IR LED. +IRac ac(kIrLed); // Create a A/C object using GPIO to sending messages with. +stdAc::state_t state; // Where we will store the desired state of the A/C. +stdAc::state_t prev; // Where we will store the previous state of the A/C. + +void setup() { + Serial.begin(115200); + delay(200); + + // Set up what we want to send. + // See state_t, opmode_t, fanspeed_t, swingv_t, & swingh_t in IRsend.h for + // all the various options. + state.protocol = decode_type_t::DAIKIN; // Set a protocol to use. + state.model = 1; // Some A/C's have different models. Let's try using just 1. + state.mode = stdAc::opmode_t::kCool; // Run in cool mode initially. + state.celsius = true; // Use Celsius for units of temp. False = Fahrenheit + state.degrees = 25; // 25 degrees. + state.fanspeed = stdAc::fanspeed_t::kMedium; // Start with the fan at medium. + state.swingv = stdAc::swingv_t::kOff; // Don't swing the fan up or down. + state.swingh = stdAc::swingh_t::kOff; // Don't swing the fan left or right. + state.light = false; // Turn off any LED/Lights/Display that we can. + state.beep = false; // Turn off any beep from the A/C if we can. + state.econo = false; // Turn off any economy modes if we can. + state.filter = false; // Turn off any Ion/Mold/Health filters if we can. + state.turbo = false; // Don't use any turbo/powerful/etc modes. + state.quiet = false; // Don't use any quiet/silent/etc modes. + state.sleep = -1; // Don't set any sleep time or modes. + state.clean = false; // Turn off any Cleaning options if we can. + state.clock = -1; // Don't set any current time if we can avoid it. + state.power = false; // Initially start with the unit off. + + prev = state; // Make sure we have a valid previous state. +} + +void loop() { + // For every protocol the library has ... + for (int i = 1; i < kLastDecodeType; i++) { + decode_type_t protocol = (decode_type_t)i; + // If the protocol is supported by the IRac class ... + if (ac.isProtocolSupported(protocol)) { + state.protocol = protocol; // Change the protocol used. + + Serial.println("Protocol " + String(protocol) + " / " + + typeToString(protocol)); + state.power = true; // We want to turn on the A/C unit. + // Have the IRac class create and send a message. + // We need a `prev` state as some A/Cs use toggle messages. + // e.g. On & Off are the same message. When given the previous state, + // it will try to do the correct thing for you. + ac.sendAc(state, &prev); // Construct and send the message. + Serial.println("Sent a message to turn ON the A/C unit."); + prev = state; // Copy new state over the previous one. + delay(5000); // Wait 5 seconds. + state.power = false; // Now we want to turn the A/C off. + ac.sendAc(state, &prev); // Construct and send the message. + Serial.println("Sent a message to turn OFF the A/C unit."); + prev = state; // Copy new state over the previous one. + delay(1000); // Wait 1 second. + } + } + Serial.println("Starting from the begining again ..."); +} diff --git a/lib/IRremoteESP8266-2.6.5/examples/CommonAcControl/platformio.ini b/lib/IRremoteESP8266-2.6.5/examples/CommonAcControl/platformio.ini new file mode 100644 index 000000000..1aba0afcc --- /dev/null +++ b/lib/IRremoteESP8266-2.6.5/examples/CommonAcControl/platformio.ini @@ -0,0 +1,18 @@ +[platformio] +src_dir = . + +[env] +lib_extra_dirs = ../../ +lib_ldf_mode = deep+ +lib_ignore = examples +build_flags = + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev diff --git a/lib/IRremoteESP8266-2.6.0/examples/ControlSamsungAC/ControlSamsungAC.ino b/lib/IRremoteESP8266-2.6.5/examples/ControlSamsungAC/ControlSamsungAC.ino similarity index 79% rename from lib/IRremoteESP8266-2.6.0/examples/ControlSamsungAC/ControlSamsungAC.ino rename to lib/IRremoteESP8266-2.6.5/examples/ControlSamsungAC/ControlSamsungAC.ino index df910fe87..4463013c1 100644 --- a/lib/IRremoteESP8266-2.6.0/examples/ControlSamsungAC/ControlSamsungAC.ino +++ b/lib/IRremoteESP8266-2.6.5/examples/ControlSamsungAC/ControlSamsungAC.ino @@ -6,7 +6,7 @@ * TL;DR: The IR LED needs to be driven by a transistor for a good result. * * Suggested circuit: -* https://github.com/markszabo/IRremoteESP8266/wiki#ir-sending +* https://github.com/crankyoldgit/IRremoteESP8266/wiki#ir-sending * * Common mistakes & tips: * * Don't just connect the IR LED directly to the pin, it won't @@ -23,9 +23,7 @@ * * ESP-01 modules are tricky. We suggest you use a module with more GPIOs * for your first time. e.g. ESP-12 etc. */ -#ifndef UNIT_TEST #include -#endif #include #include #include @@ -57,31 +55,29 @@ void setup() { } void loop() { - // Turn the A/C unit on and set to cooling mode. - // Power changes require we send an extended message. - Serial.println("Sending an extended IR command to A/C ..."); + // Turn the A/C unit on + Serial.println("Turn on the A/C ..."); ac.on(); + ac.send(); + printState(); + delay(15000); // wait 15 seconds + // and set to cooling mode. + Serial.println("Set the A/C mode to cooling ..."); ac.setMode(kSamsungAcCool); - ac.sendExtended(); + ac.send(); printState(); delay(15000); // wait 15 seconds // Increase the fan speed. - Serial.println("Sending a normal IR command to A/C ..."); + Serial.println("Set the fan to high and the swing on ..."); ac.setFan(kSamsungAcFanHigh); - ac.send(); - printState(); - delay(15000); - - // Change to swing the fan. - Serial.println("Sending a normal IR command to A/C ..."); ac.setSwing(true); ac.send(); printState(); delay(15000); // Change to Fan mode, lower the speed, and stop the swing. - Serial.println("Sending a normal IR command to A/C ..."); + Serial.println("Set the A/C to fan only with a low speed, & no swing ..."); ac.setSwing(false); ac.setMode(kSamsungAcFan); ac.setFan(kSamsungAcFanLow); @@ -90,10 +86,9 @@ void loop() { delay(15000); // Turn the A/C unit off. - // Power changes require we send an extended message. - Serial.println("Sending an extended IR command to A/C ..."); + Serial.println("Turn off the A/C ..."); ac.off(); - ac.sendExtended(); + ac.send(); printState(); delay(15000); // wait 15 seconds } diff --git a/lib/IRremoteESP8266-2.6.5/examples/ControlSamsungAC/platformio.ini b/lib/IRremoteESP8266-2.6.5/examples/ControlSamsungAC/platformio.ini new file mode 100644 index 000000000..1aba0afcc --- /dev/null +++ b/lib/IRremoteESP8266-2.6.5/examples/ControlSamsungAC/platformio.ini @@ -0,0 +1,18 @@ +[platformio] +src_dir = . + +[env] +lib_extra_dirs = ../../ +lib_ldf_mode = deep+ +lib_ignore = examples +build_flags = + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev diff --git a/lib/IRremoteESP8266-2.6.5/examples/DumbIRRepeater/DumbIRRepeater.ino b/lib/IRremoteESP8266-2.6.5/examples/DumbIRRepeater/DumbIRRepeater.ino new file mode 100644 index 000000000..80f5ce64a --- /dev/null +++ b/lib/IRremoteESP8266-2.6.5/examples/DumbIRRepeater/DumbIRRepeater.ino @@ -0,0 +1,130 @@ +/* + * IRremoteESP8266: DumbIRRepeater.ino - Record and playback IR codes. + * Copyright 2019 David Conran (crankyoldgit) + * + * This program will try to capture incoming IR messages and replay them back. + * It doesn't use any of the advanced detection features, thus it will just + * replay the messages at fixed modulated frequency (kFrequency) and a 50% duty + * cycle. + * + * Note: + * This might NOT be the frequency of the incoming message, so some replayed + * messages may not work. The frequency of incoming messages & duty cycle is + * lost at the point of the Hardware IR demodulator. The ESP can't see it. + * + * W A R N I N G + * This code is just for educational/example use only. No help will be given + * to you to make it do something else, or to make it work with some + * weird device or circuit, or to make it more usable or practical. + * If it works for you. Great. If not, Congratulations on changing/fixing it. + * + * An IR detector/demodulator must be connected to the input, kRecvPin. + * An IR LED circuit must be connected to the output, kIrLedPin. + * + * Example circuit diagrams (both are needed): + * https://github.com/crankyoldgit/IRremoteESP8266/wiki#ir-receiving + * https://github.com/crankyoldgit/IRremoteESP8266/wiki#ir-sending + * + * Common mistakes & tips: + * * Don't just connect the IR LED directly to the pin, it won't + * have enough current to drive the IR LED effectively. + * * Make sure you have the IR LED polarity correct. + * See: https://learn.sparkfun.com/tutorials/polarity/diode-and-led-polarity + * * Some digital camera/phones can be used to see if the IR LED is flashed. + * Replace the IR LED with a normal LED if you don't have a digital camera + * when debugging. + * * Avoid using the following pins unless you really know what you are doing: + * * Pin 0/D3: Can interfere with the boot/program mode & support circuits. + * * Pin 1/TX/TXD0: Any serial transmissions from the ESP will interfere. + * * Pin 3/RX/RXD0: Any serial transmissions to the ESP will interfere. + * * ESP-01 modules are tricky. We suggest you use a module with more GPIOs + * for your first time. e.g. ESP-12 etc. + * + * Changes: + * Version 1.0: June, 2019 + * - Initial version. + */ + +#include +#include +#include +#include +#include + +// ==================== start of TUNEABLE PARAMETERS ==================== + +// The GPIO an IR detector/demodulator is connected to. Recommended: 14 (D5) +const uint16_t kRecvPin = 14; + +// GPIO to use to control the IR LED circuit. Recommended: 4 (D2). +const uint16_t kIrLedPin = 4; + +// The Serial connection baud rate. +// NOTE: Make sure you set your Serial Monitor to the same speed. +const uint32_t kBaudRate = 115200; + +// As this program is a special purpose capture/resender, let's use a larger +// than expected buffer so we can handle very large IR messages. +// i.e. Up to 512 bits. +const uint16_t kCaptureBufferSize = 1024; + +// kTimeout is the Nr. of milli-Seconds of no-more-data before we consider a +// message ended. +const uint8_t kTimeout = 50; // Milli-Seconds + +// kFrequency is the modulation frequency all messages will be replayed at. +const uint16_t kFrequency = 38000; // in Hz. e.g. 38kHz. + +// ==================== end of TUNEABLE PARAMETERS ==================== + +// The IR transmitter. +IRsend irsend(kIrLedPin); +// The IR receiver. +IRrecv irrecv(kRecvPin, kCaptureBufferSize, kTimeout, false); +// Somewhere to store the captured message. +decode_results results; + +// This section of code runs only once at start-up. +void setup() { + irrecv.enableIRIn(); // Start up the IR receiver. + irsend.begin(); // Start up the IR sender. + + Serial.begin(kBaudRate, SERIAL_8N1); + while (!Serial) // Wait for the serial connection to be establised. + delay(50); + Serial.println(); + + Serial.print("DumbIRRepeater is now running and waiting for IR input " + "on Pin "); + Serial.println(kRecvPin); + Serial.print("and will retransmit it on Pin "); + Serial.println(kIrLedPin); +} + +// The repeating section of the code +void loop() { + // Check if an IR message has been received. + if (irrecv.decode(&results)) { // We have captured something. + // The capture has stopped at this point. + + // Convert the results into an array suitable for sendRaw(). + // resultToRawArray() allocates the memory we need for the array. + uint16_t *raw_array = resultToRawArray(&results); + // Find out how many elements are in the array. + uint16_t length = getCorrectedRawLength(&results); + // Send it out via the IR LED circuit. + irsend.sendRaw(raw_array, length, kFrequency); + // Resume capturing IR messages. It was not restarted until after we sent + // the message so we didn't capture our own message. + irrecv.resume(); + // Deallocate the memory allocated by resultToRawArray(). + delete [] raw_array; + + // Display a crude timestamp & notification. + uint32_t now = millis(); + Serial.printf( + "%06u.%03u: A message that was %d entries long was retransmitted.\n", + now / 1000, now % 1000, length); + } + yield(); // Or delay(milliseconds); This ensures the ESP doesn't WDT reset. +} diff --git a/lib/IRremoteESP8266-2.6.5/examples/DumbIRRepeater/platformio.ini b/lib/IRremoteESP8266-2.6.5/examples/DumbIRRepeater/platformio.ini new file mode 100644 index 000000000..1aba0afcc --- /dev/null +++ b/lib/IRremoteESP8266-2.6.5/examples/DumbIRRepeater/platformio.ini @@ -0,0 +1,18 @@ +[platformio] +src_dir = . + +[env] +lib_extra_dirs = ../../ +lib_ldf_mode = deep+ +lib_ignore = examples +build_flags = + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev diff --git a/lib/IRremoteESP8266-2.6.0/examples/IRGCSendDemo/IRGCSendDemo.ino b/lib/IRremoteESP8266-2.6.5/examples/IRGCSendDemo/IRGCSendDemo.ino similarity index 96% rename from lib/IRremoteESP8266-2.6.0/examples/IRGCSendDemo/IRGCSendDemo.ino rename to lib/IRremoteESP8266-2.6.5/examples/IRGCSendDemo/IRGCSendDemo.ino index 03c80e18b..0b6ea5e84 100644 --- a/lib/IRremoteESP8266-2.6.0/examples/IRGCSendDemo/IRGCSendDemo.ino +++ b/lib/IRremoteESP8266-2.6.5/examples/IRGCSendDemo/IRGCSendDemo.ino @@ -17,7 +17,7 @@ * TL;DR: The IR LED needs to be driven by a transistor for a good result. * * Suggested circuit: - * https://github.com/markszabo/IRremoteESP8266/wiki#ir-sending + * https://github.com/crankyoldgit/IRremoteESP8266/wiki#ir-sending * * Common mistakes & tips: * * Don't just connect the IR LED directly to the pin, it won't @@ -36,9 +36,7 @@ * for your first time. e.g. ESP-12 etc. */ -#ifndef UNIT_TEST #include -#endif #include #include diff --git a/lib/IRremoteESP8266-2.6.5/examples/IRGCSendDemo/platformio.ini b/lib/IRremoteESP8266-2.6.5/examples/IRGCSendDemo/platformio.ini new file mode 100644 index 000000000..1aba0afcc --- /dev/null +++ b/lib/IRremoteESP8266-2.6.5/examples/IRGCSendDemo/platformio.ini @@ -0,0 +1,18 @@ +[platformio] +src_dir = . + +[env] +lib_extra_dirs = ../../ +lib_ldf_mode = deep+ +lib_ignore = examples +build_flags = + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev diff --git a/lib/IRremoteESP8266-2.6.0/examples/IRGCTCPServer/IRGCTCPServer.ino b/lib/IRremoteESP8266-2.6.5/examples/IRGCTCPServer/IRGCTCPServer.ino similarity index 97% rename from lib/IRremoteESP8266-2.6.0/examples/IRGCTCPServer/IRGCTCPServer.ino rename to lib/IRremoteESP8266-2.6.5/examples/IRGCTCPServer/IRGCTCPServer.ino index 69f7299fb..b69373d34 100644 --- a/lib/IRremoteESP8266-2.6.0/examples/IRGCTCPServer/IRGCTCPServer.ino +++ b/lib/IRremoteESP8266-2.6.5/examples/IRGCTCPServer/IRGCTCPServer.ino @@ -37,10 +37,13 @@ * can check your wifi router for it's address. */ -#ifndef UNIT_TEST #include -#endif +#if defined(ESP8266) #include +#endif // ESP8266 +#if defined(ESP32) +#include +#endif // ESP32 #include #include #include diff --git a/lib/IRremoteESP8266-2.6.5/examples/IRGCTCPServer/platformio.ini b/lib/IRremoteESP8266-2.6.5/examples/IRGCTCPServer/platformio.ini new file mode 100644 index 000000000..1aba0afcc --- /dev/null +++ b/lib/IRremoteESP8266-2.6.5/examples/IRGCTCPServer/platformio.ini @@ -0,0 +1,18 @@ +[platformio] +src_dir = . + +[env] +lib_extra_dirs = ../../ +lib_ldf_mode = deep+ +lib_ignore = examples +build_flags = + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev diff --git a/lib/IRremoteESP8266-2.6.0/examples/IRMQTTServer/IRMQTTServer.h b/lib/IRremoteESP8266-2.6.5/examples/IRMQTTServer/IRMQTTServer.h similarity index 50% rename from lib/IRremoteESP8266-2.6.0/examples/IRMQTTServer/IRMQTTServer.h rename to lib/IRremoteESP8266-2.6.5/examples/IRMQTTServer/IRMQTTServer.h index 9494dbe2b..73821dc05 100644 --- a/lib/IRremoteESP8266-2.6.0/examples/IRMQTTServer/IRMQTTServer.h +++ b/lib/IRremoteESP8266-2.6.5/examples/IRMQTTServer/IRMQTTServer.h @@ -5,6 +5,9 @@ #ifndef EXAMPLES_IRMQTTSERVER_IRMQTTSERVER_H_ #define EXAMPLES_IRMQTTSERVER_IRMQTTSERVER_H_ +#if defined(ESP8266) +#include +#endif // ESP8266 #include #include #include @@ -18,17 +21,38 @@ #define MQTT_ENABLE true // Whether or not MQTT is used at all. #endif // MQTT_ENABLE +#ifndef EXAMPLES_ENABLE +// Whether or not examples are included. `false` saves ~2.5K of program space. +#define EXAMPLES_ENABLE true +#endif // EXAMPLES_ENABLE + // ---------------------- Board Related Settings ------------------------------- // NOTE: Make sure you set your Serial Monitor to the same speed. #define BAUD_RATE 115200 // Serial port Baud rate. -// GPIO the IR LED is connected to/controlled by. GPIO 4 = D2. -#define IR_LED 4 // <=- CHANGE_ME (optional) -// define IR_LED 3 // For an ESP-01 we suggest you use RX/GPIO3/Pin 7. +// Change if you need multiple independent send gpios & topics. (MQTT only) +const uint8_t kNrOfIrTxGpios = 1; +// Default GPIO the IR LED is connected to/controlled by. GPIO 4 = D2. +// For an ESP-01 we suggest you use RX/GPIO3/Pin 7. i.e. kDefaultIrLed = 3 +// Note: A value of -1 means unused. +const int8_t kDefaultIrLed = 4; // <=- CHANGE_ME (optional) -// GPIO the IR RX module is connected to/controlled by. e.g. GPIO 14 = D5. -// Comment this out to disable receiving/decoding IR messages entirely. -#define IR_RX 14 // <=- CHANGE_ME (optional) +// **DANGER** Optional flag to invert the output. (default = false) +// `false`: The LED is illuminated when the GPIO is HIGH. +// `true`: The LED is illuminated when GPIO is LOW rather than HIGH. +// Setting this to something other than the default could +// easily destroy your IR LED if you are overdriving it. +// Unless you *REALLY* know what you are doing, don't change this. +const bool kInvertTxOutput = false; + +// Default GPIO the IR demodulator is connected to/controlled by. GPIO 14 = D5. +const int8_t kDefaultIrRx = 14; // <=- CHANGE_ME (optional) + +// Enable/disable receiving/decoding IR messages entirely. +// Note: IR_RX costs about 40k+ of program memory. +#define IR_RX true + +// Should we use PULLUP on the IR Rx gpio? #define IR_RX_PULLUP false // --------------------- Network Related Settings ------------------------------ @@ -49,6 +73,8 @@ const IPAddress kSubnetMask = IPAddress(255, 255, 255, 0); // before we will connect. // The unset default is 8%. // (Uncomment to enable) +// Do you want/need mdns enabled? (https://en.wikipedia.org/wiki/Multicast_DNS) +#define MDNS_ENABLE true // `false` to disable and save ~21k of program space. // ----------------------- HTTP Related Settings ------------------------------- #define FIRMWARE_OTA true // Allow remote update of the firmware via http. @@ -56,8 +82,9 @@ const IPAddress kSubnetMask = IPAddress(255, 255, 255, 0); // Note: Firmware OTA is also disabled until // a password is set. #define HTML_PASSWORD_ENABLE false // Protect access to the HTML interface. - // Note: OTA update is always passworded. -// If you do not set a password, Firmware OTA updates will be blocked. + // Note: OTA & GPIO updates are always + // passworded. +// If you do not set a password, Firmware OTA & GPIO updates will be blocked. // ----------------------- MQTT Related Settings ------------------------------- #if MQTT_ENABLE @@ -71,13 +98,29 @@ const uint32_t kMqttReconnectTime = 5000; // Delay(ms) between reconnect tries. #define MQTT_CLIMATE "ac" // Sub-topic for the climate topics. #define MQTT_CLIMATE_CMND "cmnd" // Sub-topic for the climate command topics. #define MQTT_CLIMATE_STAT "stat" // Sub-topic for the climate stat topics. -#define MQTTbroadcastInterval 10 * 60 // Seconds between rebroadcasts +// Enable sending/receiving climate via JSON. `true` cost ~5k of program space. +#define MQTT_CLIMATE_JSON false +// Do we send an IR message when we reboot and recover the existing A/C state? +// If set to `false` you may miss requested state changes while the ESP was +// down. If set to `true`, it will resend the previous desired state sent to the +// A/C. Depending on your circumstances, you may need to change this. +#define MQTT_CLIMATE_IR_SEND_ON_RESTART false +#define MQTTbroadcastInterval 10 * 60 // Seconds between rebroadcasts. #define QOS 1 // MQTT broker should queue up any unreceived messages for us // #define QOS 0 // MQTT broker WON'T queue up messages for us. Fire & Forget. +// Enable(true)/Disable(false) the option to send a MQTT Discovery message for +// the AirCon/Climate system to Home Assistant. `false` saves ~1.5k. +#define MQTT_DISCOVERY_ENABLE true #endif // MQTT_ENABLE // ------------------------ IR Capture Settings -------------------------------- +// Should we stop listening for IR messages when we send a message via IR? +// Set this to `true` if your IR demodulator is picking up self transmissions. +// Use `false` if it isn't or can't see the self-sent transmissions +// Using `true` may mean some incoming IR messages are lost or garbled. +// i.e. `false` is better if you can get away with it. +#define DISABLE_CAPTURE_WHILE_TRANSMITTING true // Let's use a larger than normal buffer so we can handle AirCon remote codes. const uint16_t kCaptureBufferSize = 1024; #if DECODE_AC @@ -94,17 +137,38 @@ const uint16_t kMinUnknownSize = 2 * 10; #define REPORT_RAW_UNKNOWNS false // Report the whole buffer, recommended: // MQTT_MAX_PACKET_SIZE of 1024 or more -// ------------------------ Advanced Usage Only -------------------------------- -// Change if you need multiple independent send gpio/topics. -const uint8_t gpioTable[] = { - IR_LED, // Default GPIO. e.g. ir_server/send or ir_server/send_0 - // Uncomment the following as needed. - // NOTE: Remember to disable DEBUG if you are using one of the serial pins. - // 5, // GPIO 5 / D1 e.g. ir_server/send_1 - // 14, // GPIO 14 / D5 e.g. ir_server/send_2 - // 16, // GPIO 16 / D0 e.g. ir_server/send_3 -}; +// Should we use and report individual A/C settings we capture via IR if we +// can understand the individual settings of the remote. +// e.g. Aquire the A/C settings from an actual A/C IR remote and override +// any local settings set via MQTT/HTTP etc. +#define USE_DECODED_AC_SETTINGS true // `false` to disable. `true` to enable. +// Should we allow or ignore an A/C IR remote to override the A/C protocol/model +// as set via MQTT or HTTP? +// e.g. If `true`, you can use any fully supported A/C remote to control +// another brand's or model's A/C unit. `false` means change to the new +// protocol/model if we support it via `USE_DECODED_AC_SETTINGS`. +#define IGNORE_DECODED_AC_PROTOCOL true +// Do we (re-)send the captured & decoded A/C message via the IR_LED? +// `false` if you don't want to repeat the captured message. +// e.g. Useful if the IR demodulator is located in the path between the remote +// and the A/C unit so the command isn't sent twice. +// `true` if you want it sent anyway. +// e.g. The IR demodulator is in a completely different location than than the +// actual a/c unit. +#define REPLAY_DECODED_AC_MESSAGE false +// ------------------------ Advanced Usage Only -------------------------------- + +// Reports the input voltage to the ESP chip. **NOT** the input voltage +// to the development board (e.g. NodeMCU, D1 Mini etc) which are typically +// powered by USB (5V) which is then lowered to 3V via a Low Drop Out (LDO) +// Voltage regulator. Hence, this feature is turned off by default as it +// make little sense for most users as it really isn't the actual input voltage. +// E.g. For purposes of monitoring a battery etc. +// Note: Turning on the feature costs ~250 bytes of prog space. +#define REPORT_VCC false // Do we report Vcc via html info page & MQTT? + +// Keywords for MQTT topics, html arguments, or config file. #define KEY_PROTOCOL "protocol" #define KEY_MODEL "model" #define KEY_POWER "power" @@ -119,10 +183,12 @@ const uint8_t gpioTable[] = { #define KEY_BEEP "beep" #define KEY_ECONO "econo" #define KEY_SLEEP "sleep" -#define KEY_CLOCK "clock" #define KEY_FILTER "filter" #define KEY_CLEAN "clean" #define KEY_CELSIUS "use_celsius" +#define KEY_JSON "json" +#define KEY_RESEND "resend" +#define KEY_VCC "vcc" // HTML arguments we will parse for IR code information. #define KEY_TYPE "type" // KEY_PROTOCOL is also checked too. @@ -130,6 +196,10 @@ const uint8_t gpioTable[] = { #define KEY_BITS "bits" #define KEY_REPEAT "repeats" +// GPIO html/config keys +#define KEY_TX_GPIO "tx" +#define KEY_RX_GPIO "rx" + // Text for Last Will & Testament status messages. const char* kLwtOnline = "Online"; const char* kLwtOffline = "Offline"; @@ -140,24 +210,39 @@ const uint8_t kUsernameLength = 15; const uint8_t kPasswordLength = 20; // -------------------------- Debug Settings ----------------------------------- -// Disable debug output if any of the IR pins are on the TX (D1) pin. -// Note: This is a crude method to catch the common use cases. -// See `isSerialGpioUsedByIr()` for the better method. -#if (IR_LED != 1 && IR_RX != 1) +// Debug output is disabled if any of the IR pins are on the TX (D1) pin. +// See `isSerialGpioUsedByIr()`. +// Note: Debug costs ~6k of program space. #ifndef DEBUG -#define DEBUG true // Change to 'false' to disable all serial output. +#define DEBUG false // Change to 'true' for serial debug output. #endif // DEBUG -#else // (IR_LED != 1 && IR_RX != 1) -#undef DEBUG -#define DEBUG false -#endif // ----------------- End of User Configuration Section ------------------------- // Constants -#define _MY_VERSION_ "v1.0.0" +#define _MY_VERSION_ "v1.3.4" + +const uint8_t kRebootTime = 15; // Seconds +const uint8_t kQuickDisplayTime = 2; // Seconds + +// Common bit sizes for the simple protocols. +const uint8_t kCommonBitSizes[] = { + 12, 13, 15, 16, 20, 24, 28, 32, 35, 36, 42, 48, 56, 64}; +// Gpio related +#if defined(ESP8266) +const int8_t kTxGpios[] = {-1, 0, 1, 2, 3, 4, 5, 12, 13, 14, 15, 16}; +const int8_t kRxGpios[] = {-1, 0, 1, 2, 3, 4, 5, 12, 13, 14, 15}; +#endif // ESP8266 +#if defined(ESP32) +// Ref: https://randomnerdtutorials.com/esp32-pinout-reference-gpios/ +const int8_t kTxGpios[] = { + -1, 0, 1, 2, 3, 4, 5, 12, 13, 14, 15, 16, 17, 18, 19, 21, 22, 23, + 25, 26, 27, 32, 33}; +const int8_t kRxGpios[] = { + -1, 1, 2, 3, 4, 5, 12, 13, 14, 15, 16, 17, 18, 19, 21, 22, 23, + 25, 26, 27, 32, 33, 34, 35, 36, 39}; +#endif // ESP32 -const uint8_t kSendTableSize = sizeof(gpioTable); // JSON stuff // Name of the json config file in SPIFFS. const char* kConfigFile = "/config.json"; @@ -169,47 +254,94 @@ const char* kMqttPrefixKey = "mqtt_prefix"; const char* kHostnameKey = "hostname"; const char* kHttpUserKey = "http_user"; const char* kHttpPassKey = "http_pass"; +const char* kCommandDelimiter = ","; + +// URLs +const char* kUrlRoot = "/"; +const char* kUrlAdmin = "/admin"; +const char* kUrlAircon = "/aircon"; +const char* kUrlSendDiscovery = "/send_discovery"; +const char* kUrlExamples = "/examples"; +const char* kUrlGpio = "/gpio"; +const char* kUrlGpioSet = "/gpio/set"; +const char* kUrlInfo = "/info"; +const char* kUrlReboot = "/quitquitquit"; +const char* kUrlWipe = "/reset"; #if MQTT_ENABLE const uint32_t kBroadcastPeriodMs = MQTTbroadcastInterval * 1000; // mSeconds. const uint32_t kStatListenPeriodMs = 5 * 1000; // mSeconds +const int32_t kMaxPauseMs = 10000; // 10 Seconds. +const char* kSequenceDelimiter = ";"; +const char kPauseChar = 'P'; +#if defined(ESP8266) +const uint32_t kChipId = ESP.getChipId(); +#endif // ESP8266 +#if defined(ESP32) +const uint32_t kChipId = ESP.getEfuseMac(); // Discard the top 16 bits. +#endif // ESP32 + +const char* kClimateTopics = + "(" KEY_PROTOCOL "|" KEY_MODEL "|" KEY_POWER "|" KEY_MODE "|" KEY_TEMP "|" + KEY_FANSPEED "|" KEY_SWINGV "|" KEY_SWINGH "|" KEY_QUIET "|" + KEY_TURBO "|" KEY_LIGHT "|" KEY_BEEP "|" KEY_ECONO "|" KEY_SLEEP "|" + KEY_FILTER "|" KEY_CLEAN "|" KEY_CELSIUS "|" KEY_RESEND +#if MQTT_CLIMATE_JSON + "|" KEY_JSON +#endif // MQTT_CLIMATE_JSON + ")
"; void mqttCallback(char* topic, byte* payload, unsigned int length); String listOfCommandTopics(void); void handleSendMqttDiscovery(void); void subscribing(const String topic_name); void unsubscribing(const String topic_name); -void mqttLog(const String mesg); +void mqttLog(const char* str); +bool mountSpiffs(void); bool reconnect(void); void receivingMQTT(String const topic_name, String const callback_str); void callback(char* topic, byte* payload, unsigned int length); void sendMQTTDiscovery(const char *topic); void doBroadcast(TimerMs *timer, const uint32_t interval, - const commonAcState_t state, const bool retain, + const stdAc::state_t state, const bool retain, const bool force); +#if MQTT_CLIMATE_JSON +stdAc::state_t jsonToState(const stdAc::state_t current, const String str); +void sendJsonState(const stdAc::state_t state, const String topic, + const bool retain = false, const bool ha_mode = true); +#endif // MQTT_CLIMATE_JSON #endif // MQTT_ENABLE +#if REPORT_VCC +String vccToString(void); +#endif // REPORT_VCC bool isSerialGpioUsedByIr(void); void debug(const char *str); void saveWifiConfigCallback(void); void saveWifiConfig(void); void loadWifiConfigFile(void); +void doRestart(const char* str, const bool serial_only = false); String msToHumanString(uint32_t const msecs); String timeElapsed(uint32_t const msec); String timeSince(uint32_t const start); -String listOfSendGpios(void); +String gpioToString(const int16_t gpio); +uint8_t getDefaultIrSendIdx(void); +IRsend* getDefaultIrSendPtr(void); +int8_t getDefaultTxGpio(void); +String listOfTxGpios(void); bool hasUnsafeHTMLChars(String input); +String htmlHeader(const String title, const String h1_text = ""); +String htmlEnd(void); +String htmlButton(const String url, const String button, + const String text = ""); String htmlMenu(void); void handleRoot(void); String addJsReloadUrl(const String url, const uint16_t timeout_s, const bool notify); void handleExamples(void); -String boolToString(const bool value); -String opmodeToString(const stdAc::opmode_t mode); -String fanspeedToString(const stdAc::fanspeed_t speed); -String swingvToString(const stdAc::swingv_t swingv); -String swinghToString(const stdAc::swingh_t swingh); String htmlSelectBool(const String name, const bool def); -String htmlSelectProtocol(const String name, const decode_type_t def); +String htmlSelectClimateProtocol(const String name, const decode_type_t def); +String htmlSelectAcStateProtocol(const String name, const decode_type_t def, + const bool simple); String htmlSelectModel(const String name, const int16_t def); String htmlSelectMode(const String name, const stdAc::opmode_t def); String htmlSelectFanspeed(const String name, const stdAc::fanspeed_t def); @@ -221,7 +353,7 @@ void handleAdmin(void); void handleInfo(void); void handleReset(void); void handleReboot(void); -bool parseStringAndSendAirCon(IRsend *irsend, const uint16_t irType, +bool parseStringAndSendAirCon(IRsend *irsend, const decode_type_t irType, const String str); uint16_t countValuesInStr(const String str, char sep); uint16_t * newCodeArray(const uint16_t size); @@ -241,18 +373,21 @@ void setup_wifi(void); void init_vars(void); void setup(void); void loop(void); +uint32_t maxSketchSpace(void); uint64_t getUInt64fromHex(char const *str); -bool sendIRCode(IRsend *irsend, int const ir_type, +bool sendIRCode(IRsend *irsend, decode_type_t const ir_type, uint64_t const code, char const * code_str, uint16_t bits, uint16_t repeat); bool sendInt(const String topic, const int32_t num, const bool retain); bool sendBool(const String topic, const bool on, const bool retain); bool sendString(const String topic, const String str, const bool retain); bool sendFloat(const String topic, const float_t temp, const bool retain); -commonAcState_t updateClimate(commonAcState_t current, const String str, +stdAc::state_t updateClimate(stdAc::state_t current, const String str, const String prefix, const String payload); -bool cmpClimate(const commonAcState_t a, const commonAcState_t b); -bool sendClimate(const commonAcState_t prev, const commonAcState_t next, +bool cmpClimate(const stdAc::state_t a, const stdAc::state_t b); +bool sendClimate(const stdAc::state_t prev, const stdAc::state_t next, const String topic_prefix, const bool retain, - const bool forceMQTT, const bool forceIR); + const bool forceMQTT, const bool forceIR, + const bool enableIR = true); +bool decodeCommonAc(const decode_results *decode); #endif // EXAMPLES_IRMQTTSERVER_IRMQTTSERVER_H_ diff --git a/lib/IRremoteESP8266-2.6.0/examples/IRMQTTServer/IRMQTTServer.ino b/lib/IRremoteESP8266-2.6.5/examples/IRMQTTServer/IRMQTTServer.ino similarity index 66% rename from lib/IRremoteESP8266-2.6.0/examples/IRMQTTServer/IRMQTTServer.ino rename to lib/IRremoteESP8266-2.6.5/examples/IRMQTTServer/IRMQTTServer.ino index 31e40432d..730a8965f 100644 --- a/lib/IRremoteESP8266-2.6.0/examples/IRMQTTServer/IRMQTTServer.ino +++ b/lib/IRremoteESP8266-2.6.5/examples/IRMQTTServer/IRMQTTServer.ino @@ -7,11 +7,13 @@ * resources. I'm *NOT* claiming complete Copyright ownership of all the code. * Likewise, feel free to borrow from this as much as you want. * - * NOTE: An IR LED circuit SHOULD be connected to ESP8266 GPIO4 (D2) if - * you want to send IR messages. - * A compatible IR RX modules SHOULD be connected to ESP8266 GPIO14 (D5) - * if you want to capture & decode IR nessages. - * See 'IR_LED' & 'IR_RX' in IRMQTTServer.h. + * NOTE: An IR LED circuit SHOULD be connected to the ESP if + * you want to send IR messages. e.g. GPIO4 (D2) + * A compatible IR RX modules SHOULD be connected to ESP + * if you want to capture & decode IR nessages. e.g. GPIO14 (D5) + * See 'IR_RX' in IRMQTTServer.h. + * GPIOs are configurable from the http:///gpio + * page. * * WARN: This is *very* advanced & complicated example code. Not for beginners. * You are strongly suggested to try & look at other example code first @@ -29,21 +31,25 @@ * * - Arduino IDE: * o Install the following libraries via Library Manager - * - ArduinoJson (https://arduinojson.org/) (Version >= 5.x and < 6) + * - ArduinoJson (https://arduinojson.org/) (Version >= 5.0 and < 6.0) * - PubSubClient (https://pubsubclient.knolleary.net/) - * - WiFiManager (https://github.com/tzapu/WiFiManager) (Version >= 0.14) + * - WiFiManager (https://github.com/tzapu/WiFiManager) + * (ESP8266: Version >= 0.14, ESP32: 'development' branch.) * o You MUST change to have the following (or larger) value: * (with REPORT_RAW_UNKNOWNS 1024 or more is recommended) * #define MQTT_MAX_PACKET_SIZE 768 + * o Use the smallest non-zero SPIFFS size you can for your board. + * (See the Tools -> Flash Size menu) + * * - PlatformIO IDE: * If you are using PlatformIO, this should already been done for you in * the accompanying platformio.ini file. * * ## First Boot (Initial setup) - * The ESP8266 board will boot into the WiFiManager's AP mode. + * The ESP board will boot into the WiFiManager's AP mode. * i.e. It will create a WiFi Access Point with a SSID like: "ESP123456" etc. * Connect to that SSID. Then point your browser to http://192.168.4.1/ and - * configure the ESP8266 to connect to your desired WiFi network and associated + * configure the ESP to connect to your desired WiFi network and associated * required settings. It will remember these details on next boot if the device * connects successfully. * More information can be found here: @@ -55,6 +61,9 @@ * ## Normal Use (After initial setup) * Enter 'http:///gpio page to configure the GPIOs + * for the IR LED(s) and/or IR RX demodulator. + * * You can send URLs like the following, with similar data type limitations as * the MQTT formating in the next section. e.g: * http:///ir?type=7&code=E0E09966 @@ -102,6 +111,19 @@ * bit/byte size you want to send as some A/C units have units * have different sized messages. e.g. Fujitsu A/C units. * + * Sequences. + * You can send a sequence of IR messages via MQTT using the above methods + * if you separate them with a ';' character. In addition you can add a + * pause/gap between sequenced messages by using 'P' followed immediately by + * the number of milliseconds you wish to wait (up to a max of kMaxPauseMs). + * e.g. 7,E0E09966;4,f50,12 + * Send a Samsung(7) TV Power on code, followed immediately by a Sony(4) + * TV power off message. + * or: 19,C1A28877;P500;19,C1A25AA5;P500;19,C1A2E21D,0,30 + * Turn on a Sherwood(19) Amplifier, Wait 1/2 a second, Switch the + * Amplifier to Video input 2, wait 1/2 a second, then send the Sherwood + * Amp the "Volume Up" message 30 times. + * * In short: * No spaces after/before commas. * Values are comma separated. @@ -163,6 +185,13 @@ * acknowledge this via the relevant state topic for that command. * e.g. If the aircon/climate changes from power off to power on, it will * send an "on" payload to "ir_server/ac/stat/power" + * + * There is a special command available to force the ESP to resend the current + * A/C state in an IR message. To do so use the `resend` command MQTT topic, + * e.g. `ir_server/ac/cmnd/resend` with a payload message of `resend`. + * There is no corresponding "stat" message update for this particular topic, + * but a log message is produced indicating it was received. + * * NOTE: These "stat" messages have the MQTT retain flag set to on. Thus the * MQTT broker will remember them until reset/restarted etc. * @@ -195,43 +224,43 @@ * In HA's configuration.yaml, add: * * climate: - * platform: mqtt - * name: Living Room Aircon - * modes: - * - "off" - * - "auto" - * - "cool" - * - "heat" - * - "dry" - * - "fan_only" - * fan_modes: - * - "auto" - * - "min" - * - "low" - * - "medium" - * - "high" - * - "max" - * swing_modes: - * - "off" - * - "auto" - * - "highest" - * - "high" - * - "middle" - * - "low" - * - "lowest" - * power_command_topic: "ir_server/ac/cmnd/power" - * mode_command_topic: "ir_server/ac/cmnd/mode" - * mode_state_topic: "ir_server/ac/stat/mode" - * temperature_command_topic: "ir_server/ac/cmnd/temp" - * temperature_state_topic: "ir_server/ac/stat/temp" - * fan_mode_command_topic: "ir_server/ac/cmnd/fanspeed" - * fan_mode_state_topic: "ir_server/ac/stat/fanspeed" - * swing_mode_command_topic: "ir_server/ac/cmnd/swingv" - * swing_mode_state_topic: "ir_server/ac/stat/swingv" - * min_temp: 16 - * max_temp: 32 - * temp_step: 1 - * retain: false + * - platform: mqtt + * name: Living Room Aircon + * modes: + * - "off" + * - "auto" + * - "cool" + * - "heat" + * - "dry" + * - "fan_only" + * fan_modes: + * - "auto" + * - "min" + * - "low" + * - "medium" + * - "high" + * - "max" + * swing_modes: + * - "off" + * - "auto" + * - "highest" + * - "high" + * - "middle" + * - "low" + * - "lowest" + * power_command_topic: "ir_server/ac/cmnd/power" + * mode_command_topic: "ir_server/ac/cmnd/mode" + * mode_state_topic: "ir_server/ac/stat/mode" + * temperature_command_topic: "ir_server/ac/cmnd/temp" + * temperature_state_topic: "ir_server/ac/stat/temp" + * fan_mode_command_topic: "ir_server/ac/cmnd/fanspeed" + * fan_mode_state_topic: "ir_server/ac/stat/fanspeed" + * swing_mode_command_topic: "ir_server/ac/cmnd/swingv" + * swing_mode_state_topic: "ir_server/ac/stat/swingv" + * min_temp: 16 + * max_temp: 32 + * temp_step: 1 + * retain: false * * ### via HTTP: * Use the "http:///aircon/set" URL and pass on @@ -250,9 +279,13 @@ * `ir_server/log` * * ## Updates - * You can upload new firmware over the air (OTA) via the form on the device's - * main page. No need to connect to the device again via USB. \o/ - * Your WiFi settings should be remembered between updates. \o/ \o/ + * You can upload new firmware Over The Air (OTA) via the form on the device's + * "Admin" page. No need to connect to the device again via USB. \o/ + * Your settings should be remembered between updates. \o/ \o/ + * + * On boards with 1 Meg of flash should use an SPIFFS size of 64k if you want a + * hope of being able to load a firmware via OTA. + * Boards with only 512k flash have no chance of OTA with this firmware. * * ## Security * @@ -279,12 +312,21 @@ #include #include #include +#if defined(ESP8266) #include +#include +#include +#endif // ESP8266 +#if defined(ESP32) +#include +#include +#include +#include +#include +#endif // ESP32 #include #include -#include #include -#include #include #include #include @@ -303,13 +345,22 @@ #include #include +using irutils::msToString; + +#if REPORT_VCC + ADC_MODE(ADC_VCC); +#endif // REPORT_VCC + // Globals +#if defined(ESP8266) ESP8266WebServer server(kHttpPort); -#ifdef IR_RX -IRrecv irrecv(IR_RX, kCaptureBufferSize, kCaptureTimeout, true); -decode_results capture; // Somewhere to store inbound IR messages. -#endif // IR_RX +#endif // ESP8266 +#if defined(ESP32) +WebServer server(kHttpPort); +#endif // ESP32 +#if MDNS_ENABLE MDNSResponder mdns; +#endif // MDNS_ENABLE WiFiClient espClient; WiFiManager wifiManager; bool flagSaveWifiConfig = false; @@ -319,23 +370,28 @@ char Hostname[kHostnameLength + 1] = "ir_server"; // Default hostname. uint16_t *codeArray; uint32_t lastReconnectAttempt = 0; // MQTT last attempt reconnection number bool boot = true; -bool lockIr = false; // Primitive locking for gating the IR LED. +volatile bool lockIr = false; // Primitive locking for gating the IR LED. uint32_t sendReqCounter = 0; bool lastSendSucceeded = false; // Store the success status of the last send. uint32_t lastSendTime = 0; int8_t offset; // The calculated period offset for this chip and library. -IRsend *IrSendTable[kSendTableSize]; - -#ifdef IR_RX +IRsend *IrSendTable[kNrOfIrTxGpios]; +int8_t txGpioTable[kNrOfIrTxGpios] = {kDefaultIrLed}; +String lastClimateSource; +#if IR_RX +IRrecv *irrecv = NULL; +decode_results capture; // Somewhere to store inbound IR messages. +int8_t rx_gpio = kDefaultIrRx; String lastIrReceived = "None"; uint32_t lastIrReceivedTime = 0; uint32_t irRecvCounter = 0; #endif // IR_RX // Climate stuff -commonAcState_t climate; -commonAcState_t climate_prev; -IRac commonAc(gpioTable[0]); +stdAc::state_t climate; +stdAc::state_t climate_prev; +IRac *commonAc = NULL; + TimerMs lastClimateIr = TimerMs(); // When we last sent the IR Climate mesg. uint32_t irClimateCounter = 0; // How many have we sent? // Store the success status of the last climate send. @@ -368,7 +424,9 @@ String MqttLwt; // Topic for the Last Will & Testament. String MqttClimate; // Sub-topic for the climate topics. String MqttClimateCmnd; // Sub-topic for the climate command topics. String MqttClimateStat; // Sub-topic for the climate stat topics. +#if MQTT_DISCOVERY_ENABLE String MqttDiscovery; +#endif // MQTT_DISCOVERY_ENABLE String MqttHAName; String MqttClientId; @@ -382,16 +440,29 @@ TimerMs statListenTime = TimerMs(); // How long we've been listening for. #endif // MQTT_ENABLE bool isSerialGpioUsedByIr(void) { - const uint8_t kSerialTxGpio = 1; // The GPIO serial output is sent too. - // Note: *DOES NOT* control Serial output. + const int8_t kSerialTxGpio = 1; // The GPIO serial output is sent to. + // Note: *DOES NOT* control Serial output. +#if defined(ESP32) + const int8_t kSerialRxGpio = 3; // The GPIO serial input is received on. +#endif // ESP32 // Ensure we are not trodding on anything IR related. -#ifdef IR_RX - if (IR_RX == kSerialTxGpio) - return true; // Serial port is in use by IR capture. Abort. +#if IR_RX + switch (rx_gpio) { +#if defined(ESP32) + case kSerialRxGpio: +#endif // ESP32 + case kSerialTxGpio: + return true; // Serial port is in use by IR capture. Abort. + } #endif // IR_RX - for (uint8_t i = 0; i < kSendTableSize; i++) - if (gpioTable[i] == kSerialTxGpio) - return true; // Serial port is in use for IR sending. Abort. + for (uint8_t i = 0; i < kNrOfIrTxGpios; i++) + switch (txGpioTable[i]) { +#if defined(ESP32) + case kSerialRxGpio: +#endif // ESP32 + case kSerialTxGpio: + return true; // Serial port is in use for IR sending. Abort. + } return false; // Not in use as far as we can tell. } @@ -410,8 +481,27 @@ void saveWifiConfigCallback(void) { flagSaveWifiConfig = true; } -void saveWifiConfig(void) { - debug("Saving the wifi config."); +// Forcibly mount the SPIFFS. Formatting the SPIFFS if needed. +// +// Returns: +// A boolean indicating success or failure. +bool mountSpiffs(void) { + debug("Mounting SPIFFS..."); + if (SPIFFS.begin()) return true; // We mounted it okay. + // We failed the first time. + debug("Failed to mount SPIFFS!\nFormatting SPIFFS and trying again..."); + SPIFFS.format(); + if (!SPIFFS.begin()) { // Did we fail? + debug("DANGER: Failed to mount SPIFFS even after formatting!"); + delay(10000); // Make sure the debug message doesn't just float by. + return false; + } + return true; // Success! +} + +bool saveConfig(void) { + debug("Saving the config."); + bool success = false; DynamicJsonBuffer jsonBuffer; JsonObject& json = jsonBuffer.createObject(); #if MQTT_ENABLE @@ -424,8 +514,15 @@ void saveWifiConfig(void) { json[kHostnameKey] = Hostname; json[kHttpUserKey] = HttpUsername; json[kHttpPassKey] = HttpPassword; +#if IR_RX + json[KEY_RX_GPIO] = static_cast(rx_gpio); +#endif // IR_RX + for (uint16_t i = 0; i < kNrOfIrTxGpios; i++) { + const String key = KEY_TX_GPIO + String(i); + json[key] = static_cast(txGpioTable[i]); + } - if (SPIFFS.begin()) { + if (mountSpiffs()) { File configFile = SPIFFS.open(kConfigFile, "w"); if (!configFile) { debug("Failed to open config file for writing."); @@ -434,15 +531,17 @@ void saveWifiConfig(void) { json.printTo(configFile); configFile.close(); debug("Finished writing config file."); + success = true; } SPIFFS.end(); } + return success; } -void loadWifiConfigFile(void) { - debug("Trying to mount SPIFFS"); - if (SPIFFS.begin()) { - debug("mounted file system"); +bool loadConfigFile(void) { + bool success = false; + if (mountSpiffs()) { + debug("mounted the file system"); if (SPIFFS.exists(kConfigFile)) { debug("config file exists"); @@ -468,7 +567,17 @@ void loadWifiConfigFile(void) { strncpy(Hostname, json[kHostnameKey] | "", kHostnameLength); strncpy(HttpUsername, json[kHttpUserKey] | "", kUsernameLength); strncpy(HttpPassword, json[kHttpPassKey] | "", kPasswordLength); + // Read in the GPIO settings. +#if IR_RX + // Single RX gpio + rx_gpio = json[KEY_RX_GPIO] | kDefaultIrRx; +#endif // IR_RX + // Potentially multiple TX gpios + for (uint16_t i = 0; i < kNrOfIrTxGpios; i++) + txGpioTable[i] = json[String(KEY_TX_GPIO + String(i)).c_str()] | + kDefaultIrLed; debug("Recovered Json fields."); + success = true; } else { debug("Failed to load json config"); } @@ -480,89 +589,89 @@ void loadWifiConfigFile(void) { } debug("Unmounting SPIFFS."); SPIFFS.end(); - } else { - debug("Failed to mount SPIFFS"); } -} - -String msToHumanString(uint32_t const msecs) { - uint32_t totalseconds = msecs / 1000; - if (totalseconds == 0) return "Now"; - - // Note: millis() can only count up to 45 days, so uint8_t is safe. - uint8_t days = totalseconds / (60 * 60 * 24); - uint8_t hours = (totalseconds / (60 * 60)) % 24; - uint8_t minutes = (totalseconds / 60) % 60; - uint8_t seconds = totalseconds % 60; - - String result = ""; - if (days) result += String(days) + " day"; - if (days > 1) result += 's'; - if (hours) result += ' ' + String(hours) + " hour"; - if (hours > 1) result += 's'; - if (minutes) result += ' ' + String(minutes) + " minute"; - if (minutes > 1) result += 's'; - if (seconds) result += ' ' + String(seconds) + " second"; - if (seconds > 1) result += 's'; - result.trim(); - return result; + return success; } String timeElapsed(uint32_t const msec) { - String result = msToHumanString(msec); + String result = msToString(msec); if (result.equalsIgnoreCase("Now")) return result; else - return result + " ago"; + return result + F(" ago"); } String timeSince(uint32_t const start) { if (start == 0) - return "Never"; + return F("Never"); uint32_t diff = 0; uint32_t now = millis(); if (start < now) diff = now - start; else diff = UINT32_MAX - start + now; - return msToHumanString(diff) + " ago"; + return msToString(diff) + F(" ago"); +} + +String gpioToString(const int16_t gpio) { + if (gpio == kGpioUnused) + return F("Unused"); + else + return String(gpio); +} + +int8_t getDefaultTxGpio(void) { + for (int8_t i = 0; i < kNrOfIrTxGpios; i++) + if (txGpioTable[i] != kGpioUnused) return txGpioTable[i]; + return kGpioUnused; } // Return a string containing the comma separated list of sending gpios. -String listOfSendGpios(void) { - String result = String(gpioTable[0]); - if (kSendTableSize > 1) result += " (default)"; - for (uint8_t i = 1; i < kSendTableSize; i++) { - result += ", " + String(gpioTable[i]); +String listOfTxGpios(void) { + bool found = false; + String result = ""; + for (uint8_t i = 0; i < kNrOfIrTxGpios; i++) { + if (i) result += ", "; + result += gpioToString(txGpioTable[i]); + if (!found && txGpioTable[i] == getDefaultTxGpio()) { + result += " (default)"; + found = true; + } } return result; } String htmlMenu(void) { - return F( - "
" - "" - "" - "" - "" - "" - "
" - "
"); + String html = F("
"); + html += htmlButton(kUrlRoot, F("Home")); + html += htmlButton(kUrlAircon, F("Aircon")); +#if EXAMPLES_ENABLE + html += htmlButton(kUrlExamples, F("Examples")); +#endif // EXAMPLES_ENABLE + html += htmlButton(kUrlInfo, F("System Info")); + html += htmlButton(kUrlAdmin, F("Admin")); + html += F("

"); + return html; +} + +String htmlSelectAcStateProtocol(const String name, const decode_type_t def, + const bool simple) { + String html = ""); + return html; } // Root web page with example usage etc. @@ -573,72 +682,29 @@ void handleRoot(void) { return server.requestAuthentication(); } #endif - String html = F( - "IR MQTT server" - "" - "

ESP8266 IR MQTT Server

" - "
" _MY_VERSION_ "
"); + String html = htmlHeader(F("ESP IR MQTT Server")); + html += F("
" _MY_VERSION_ "
"); html += htmlMenu(); html += F( "

Send a simple IR message

" "

" - "Type: " - "" + "Type: "); + html += htmlSelectAcStateProtocol(KEY_TYPE, decode_type_t::NEC, true); + html += F( " Code: 0x" " Bit size: " "" " Repeats: " @@ -647,38 +713,20 @@ void handleRoot(void) { "

" "

Send a complex (Air Conditioner) IR message

" "" - "Type: " - "" + "Type: "); + html += htmlSelectAcStateProtocol(KEY_TYPE, decode_type_t::KELVINATOR, false); + html += F( " State code: 0x" "" + " value='" +#if EXAMPLES_ENABLE + "190B8050000000E0190B8070000010F0" +#endif // EXAMPLES_ENABLE + "'>" " " "

" "

" @@ -686,11 +734,15 @@ void handleRoot(void) { "
" "" "String: (freq,array data) " + "1638,520,1638,520,1638,520,1638,520" +#endif // EXAMPLES_ENABLE + "'>" " " "
" "

" @@ -699,10 +751,14 @@ void handleRoot(void) { "
" "" "String: 1:1,1," + "63,20,63,20,63,20,1798" +#endif // EXAMPLES_ENABLE + "'>" " " "
" "

" @@ -711,15 +767,20 @@ void handleRoot(void) { "
" "" "String (comma separated): " + "0018,0018,0018,0018,0030,0018,0018,03f6" +#endif // EXAMPLES_ENABLE + "'>" " Repeats: " " " "
" - "
"); + "
"); + html += htmlEnd(); server.send(200, "text/html", html); } @@ -747,6 +808,7 @@ String addJsReloadUrl(const String url, const uint16_t timeout_s, return html; } +#if EXAMPLES_ENABLE // Web page with hardcoded example usage etc. void handleExamples(void) { #if HTML_PASSWORD_ENABLE @@ -755,11 +817,7 @@ void handleExamples(void) { return server.requestAuthentication(); } #endif - String html = F( - "IR MQTT examples" - "" - "

ESP8266 IR MQTT Server

" - "
" _MY_VERSION_ "
"); + String html = htmlHeader(F("IR MQTT examples")); html += htmlMenu(); html += F( "

Hardcoded examples

" @@ -792,121 +850,35 @@ void handleExamples(void) { "Change just the temp to 27C (via HTTP aircon interface)

" "

" "Turn OFF the current A/C (via HTTP aircon interface)

" - "

"); + "

"); + html += htmlEnd(); server.send(200, "text/html", html); } +#endif // EXAMPLES_ENABLE -String boolToString(const bool value) { - return value ? F("on") : F("off"); -} - - -String opmodeToString(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kOff: - return F("off"); - case stdAc::opmode_t::kAuto: - return F("auto"); - case stdAc::opmode_t::kCool: - return F("cool"); - case stdAc::opmode_t::kHeat: - return F("heat"); - case stdAc::opmode_t::kDry: - return F("dry"); - case stdAc::opmode_t::kFan: - return F("fan_only"); - default: - return F("unknown"); - } -} - -String fanspeedToString(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kAuto: - return F("auto"); - case stdAc::fanspeed_t::kMax: - return F("max"); - case stdAc::fanspeed_t::kHigh: - return F("high"); - case stdAc::fanspeed_t::kMedium: - return F("medium"); - case stdAc::fanspeed_t::kLow: - return F("low"); - case stdAc::fanspeed_t::kMin: - return F("min"); - default: - return F("unknown"); - } -} - -String swingvToString(const stdAc::swingv_t swingv) { - switch (swingv) { - case stdAc::swingv_t::kOff: - return F("off"); - case stdAc::swingv_t::kAuto: - return F("auto"); - case stdAc::swingv_t::kHighest: - return F("highest"); - case stdAc::swingv_t::kHigh: - return F("high"); - case stdAc::swingv_t::kMiddle: - return F("middle"); - case stdAc::swingv_t::kLow: - return F("low"); - case stdAc::swingv_t::kLowest: - return F("lowest"); - default: - return F("unknown"); - } -} - -String swinghToString(const stdAc::swingh_t swingh) { - switch (swingh) { - case stdAc::swingh_t::kOff: - return F("off"); - case stdAc::swingh_t::kAuto: - return F("auto"); - case stdAc::swingh_t::kLeftMax: - return F("leftmax"); - case stdAc::swingh_t::kLeft: - return F("left"); - case stdAc::swingh_t::kMiddle: - return F("middle"); - case stdAc::swingh_t::kRight: - return F("right"); - case stdAc::swingh_t::kRightMax: - return F("rightmax"); - default: - return F("unknown"); - } +String htmlOptionItem(const String value, const String text, bool selected) { + String html = F(""); } html += F(""); @@ -936,15 +917,9 @@ String htmlSelectModel(const String name, const int16_t def) { String htmlSelectMode(const String name, const stdAc::opmode_t def) { String html = ""); return html; @@ -952,15 +927,9 @@ String htmlSelectMode(const String name, const stdAc::opmode_t def) { String htmlSelectFanspeed(const String name, const stdAc::fanspeed_t def) { String html = ""); return html; @@ -968,15 +937,9 @@ String htmlSelectFanspeed(const String name, const stdAc::fanspeed_t def) { String htmlSelectSwingv(const String name, const stdAc::swingv_t def) { String html = ""); return html; @@ -984,32 +947,50 @@ String htmlSelectSwingv(const String name, const stdAc::swingv_t def) { String htmlSelectSwingh(const String name, const stdAc::swingh_t def) { String html = ""); return html; } +String htmlHeader(const String title, const String h1_text) { + String html = F(""); + html += title; + html += F("

"); + if (h1_text.length()) + html += h1_text; + else + html += title; + html += F("

"); + return html; +} + +String htmlEnd(void) { + return F(""); +} + +String htmlButton(const String url, const String button, const String text) { + String html = F(" "); + html += text; + return html; +} + // Admin web page void handleAirCon(void) { - String html = F( - "AirCon control" - "" - "

Air Conditioner Control

"); + String html = htmlHeader(F("Air Conditioner Control")); html += htmlMenu(); html += "

Current Settings

" "
" "" "" + htmlSelectClimateProtocol(KEY_PROTOCOL, climate.protocol) + + "" "" "" "" + "" "
Protocol" + - htmlSelectProtocol(KEY_PROTOCOL, climate.protocol) + "
Model" + htmlSelectModel(KEY_MODEL, climate.model) + "
Power" + htmlSelectBool(KEY_POWER, climate.power) + @@ -1045,11 +1026,12 @@ void handleAirCon(void) { "
Beep" + htmlSelectBool(KEY_BEEP, climate.beep) + "
Force resend" + htmlSelectBool(KEY_RESEND, false) + + "
" "" "
"; - // Display the current settings. - html += F(""); + html += htmlEnd(); server.send(200, "text/html", html); } @@ -1061,63 +1043,66 @@ void handleAirConSet(void) { return server.requestAuthentication(); } #endif - commonAcState_t result = climate; + stdAc::state_t result = climate; debug("New common a/c received via HTTP"); - for (uint16_t i = 0; i < server.args(); i++) - result = updateClimate(result, server.argName(i), "", server.arg(i)); + bool force_resend = false; + for (uint16_t i = 0; i < server.args(); i++) { + if (server.argName(i).equals(KEY_RESEND)) + force_resend = IRac::strToBool(server.arg(i).c_str()); + else + result = updateClimate(result, server.argName(i), "", server.arg(i)); + } #if MQTT_ENABLE - sendClimate(climate, result, MqttClimateStat, - true, false, false); + sendClimate(climate, result, MqttClimateStat, true, false, force_resend); #else // MQTT_ENABLE - sendClimate(climate, result, "", false, false, false); + sendClimate(climate, result, "", false, false, force_resend); #endif // MQTT_ENABLE + lastClimateSource = F("HTTP"); // Update the old climate state with the new one. climate = result; // Redirect back to the aircon page. - String html = F( - "Update Aircon" - "" - "

Aircon updated!

"); - html += addJsReloadUrl("/aircon", 2, false); - html += F(""); + String html = htmlHeader(F("Aircon updated!")); + html += addJsReloadUrl(kUrlAircon, kQuickDisplayTime, false); + html += htmlEnd(); server.send(200, "text/html", html); } +String htmlDisabled(void) { + String html = F( + "Updates disabled until you set a password. " + "You will need to wipe & reset to set one.

"); + return html; +} + // Admin web page void handleAdmin(void) { - String html = F( - "IR MQTT server admin" - "" - "

Administration

"); + String html = htmlHeader(F("Administration")); html += htmlMenu(); - html += F( - "

Special commands

" + html += F("

Special commands

"); #if MQTT_ENABLE - " " - "Send a Climate MQTT discovery message to Home Assistant.

" +#if MQTT_DISCOVERY_ENABLE + html += htmlButton( + kUrlSendDiscovery, F("Send MQTT Discovery"), + F("Send a Climate MQTT discovery message to Home Assistant.

")); +#endif // MQTT_DISCOVERY_ENABLE #endif // MQTT_ENABLE - " A simple reboot of the ESP8266. " - "ie. No changes

" - " Warning: " - "Resets the device back to original settings. " - "ie. Goes back to AP/Setup mode.
"); + html += htmlButton( + kUrlReboot, F("Reboot"), + F("A simple reboot of the ESP8266. ie. No changes
" + "
")); + html += htmlButton( + kUrlWipe, F("Wipe Settings"), + F("Warning: Resets the device back to original settings. " + "ie. Goes back to AP/Setup mode.

")); + html += htmlButton(kUrlGpio, F("GPIOs"), F("Change the IR GPIOs.
")); #if FIRMWARE_OTA html += F("

Update firmware

" - "Warning:
"); + "Warning:
"); if (!strlen(HttpPassword)) // Deny if password not set - html += F("OTA firmware is disabled until you set a password. " - "You will need to wipe & reset to set one." - "

"); + html += htmlDisabled(); else // default password has been changed, so allow it. html += F( "Updating your firmware may screw up your access to the device. " @@ -1128,16 +1113,25 @@ void handleAdmin(void) { "" ""); #endif // FIRMWARE_OTA - html += F(""); + html += htmlEnd(); server.send(200, "text/html", html); } +uint32_t maxSketchSpace(void) { +#if defined(ESP8266) + return (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000; +#else // defined(ESP8266) + return UPDATE_SIZE_UNKNOWN; +#endif // defined(ESP8266) +} + +#if REPORT_VCC +String vccToString(void) { return String(ESP.getVcc() / 1000.0); } +#endif // REPORT_VCC + // Info web page void handleInfo(void) { - String html = - "IR MQTT server info" - "" - "

Information

"; + String html = htmlHeader(F("IR MQTT server info")); html += htmlMenu(); html += "

General

" @@ -1149,13 +1143,22 @@ void handleInfo(void) { " " __TIME__ "
" "Period Offset: " + String(offset) + "us
" "IR Lib Version: " _IRREMOTEESP8266_VERSION_ "
" +#if defined(ESP8266) "ESP8266 Core Version: " + ESP.getCoreVersion() + "
" - "IR Send GPIO(s): " + listOfSendGpios() + "
" + "Free Sketch Space: " + String(maxSketchSpace() >> 10) + "k
" +#endif // ESP8266 +#if defined(ESP32) + "ESP32 SDK Version: " + ESP.getSdkVersion() + "
" +#endif // ESP32 + "Cpu Freq: " + String(ESP.getCpuFreqMHz()) + "MHz
" + "IR Send GPIO(s): " + listOfTxGpios() + "
" + + irutils::addBoolToString(kInvertTxOutput, + "Inverting GPIO output", false) + "
" "Total send requests: " + String(sendReqCounter) + "
" "Last message sent: " + String(lastSendSucceeded ? "Ok" : "FAILED") + " (" + timeSince(lastSendTime) + ")
" -#ifdef IR_RX - "IR Recv GPIO: " + String(IR_RX) + +#if IR_RX + "IR Recv GPIO: " + gpioToString(rx_gpio) + #if IR_RX_PULLUP " (pullup)" #endif // IR_RX_PULLUP @@ -1180,6 +1183,11 @@ void handleInfo(void) { "Off" #endif // DEBUG "
" +#if REPORT_VCC + "Vcc: "; + html += vccToString(); + html += "V
" +#endif // REPORT_VCC "

" #if MQTT_ENABLE "

MQTT Information

" @@ -1191,7 +1199,7 @@ void handleInfo(void) { "Client id: " + MqttClientId + "
" "Command topic(s): " + listOfCommandTopics() + "
" "Acknowledgements topic: " + MqttAck + "
" -#ifdef IR_RX +#if IR_RX "IR Received topic: " + MqttRecv + "
" #endif // IR_RX "Log topic: " + MqttLog + "
" @@ -1199,8 +1207,9 @@ void handleInfo(void) { "QoS: " + String(QOS) + "
" // lastMqttCmd* is unescaped untrusted input. // Avoid any possible HTML/XSS when displaying it. - "Last MQTT command seen: (topic) '" + htmlEscape(lastMqttCmdTopic) + - "' (payload) '" + htmlEscape(lastMqttCmd) + "' (" + + "Last MQTT command seen: (topic) '" + + irutils::htmlEscape(lastMqttCmdTopic) + + "' (payload) '" + irutils::htmlEscape(lastMqttCmd) + "' (" + timeSince(lastMqttCmdTime) + ")
" "Total published: " + String(mqttSentCounter) + "
" "Total received: " + String(mqttRecvCounter) + "
" @@ -1208,15 +1217,16 @@ void handleInfo(void) { #endif // MQTT_ENABLE "

Climate Information

" "

" - "IR Send GPIO: " + String(gpioTable[0]) + "
" + "IR Send GPIO: " + String(txGpioTable[0]) + "
" + "Last update source: " + lastClimateSource + "
" "Total sent: " + String(irClimateCounter) + "
" "Last send: " + String(hasClimateBeenSent ? (String(lastClimateSucceeded ? "Ok" : "FAILED") + " (" + timeElapsed(lastClimateIr.elapsed()) + ")") : "Never") + "
" #if MQTT_ENABLE - "State listen period: " + msToHumanString(kStatListenPeriodMs) + "
" - "State broadcast period: " + msToHumanString(kBroadcastPeriodMs) + "
" + "State listen period: " + msToString(kStatListenPeriodMs) + "
" + "State broadcast period: " + msToString(kBroadcastPeriodMs) + "
" "Last state broadcast: " + (hasBroadcastBeenSent ? timeElapsed(lastBroadcast.elapsed()) : String("Never")) + "
" @@ -1226,47 +1236,51 @@ void handleInfo(void) { timeElapsed(lastDiscovery.elapsed()) : String("Never"))) + "
" - "Command topics: " + MqttClimateCmnd + - "(" KEY_PROTOCOL "|" KEY_MODEL "|" KEY_POWER "|" KEY_MODE "|" KEY_TEMP "|" - KEY_FANSPEED "|" KEY_SWINGV "|" KEY_SWINGH "|" KEY_QUIET "|" - KEY_TURBO "|" KEY_LIGHT "|" KEY_BEEP "|" KEY_ECONO "|" KEY_SLEEP "|" - KEY_CLOCK "|" KEY_FILTER "|" KEY_CLEAN "|" KEY_CELSIUS ")
" - "State topics: " + MqttClimateStat + - "(" KEY_PROTOCOL "|" KEY_MODEL "|" KEY_POWER "|" KEY_MODE "|" KEY_TEMP "|" - KEY_FANSPEED "|" KEY_SWINGV "|" KEY_SWINGH "|" KEY_QUIET "|" - KEY_TURBO "|" KEY_LIGHT "|" KEY_BEEP "|" KEY_ECONO "|" KEY_SLEEP "|" - KEY_CLOCK "|" KEY_FILTER "|" KEY_CLEAN "|" KEY_CELSIUS ")
" + "Command topics: " + MqttClimateCmnd + kClimateTopics + + "State topics: " + MqttClimateStat + kClimateTopics + #endif // MQTT_ENABLE "

" // Page footer "

" "(Note: Page will refresh every 60 seconds.)" "

"; - html += addJsReloadUrl("/info", 60, false); - html += ""; + html += addJsReloadUrl(kUrlInfo, 60, false); + html += htmlEnd(); server.send(200, "text/html", html); } + +void doRestart(const char* str, const bool serial_only) { +#if MQTT_ENABLE + if (!serial_only) + mqttLog(str); + else +#endif // MQTT_ENABLE + debug(str); + delay(2000); // Enough time for messages to be sent. + ESP.restart(); + delay(5000); // Enough time to ensure we don't return. +} + // Reset web page void handleReset(void) { #if HTML_PASSWORD_ENABLE if (!server.authenticate(HttpUsername, HttpPassword)) { - debug("Basic HTTP authentication failure for /reset."); + debug(("Basic HTTP authentication failure for " + + String(kUrlWipe)).c_str()); return server.requestAuthentication(); } #endif server.send(200, "text/html", - "Reset WiFi Config" - "" - "

Resetting the WiFiManager config back to defaults.

" + htmlHeader(F("Reset WiFi Config"), + F("Resetting the WiFiManager config back to defaults.")) + "

Device restarting. Try connecting in a few seconds.

" + - addJsReloadUrl("/", 10, true) + - ""); + addJsReloadUrl(kUrlRoot, 10, true) + + htmlEnd()); // Do the reset. #if MQTT_ENABLE mqttLog("Wiping all saved config settings."); #endif // MQTT_ENABLE - debug("Trying to mount SPIFFS"); - if (SPIFFS.begin()) { + if (mountSpiffs()) { debug("Removing JSON config file"); SPIFFS.remove(kConfigFile); SPIFFS.end(); @@ -1274,34 +1288,24 @@ void handleReset(void) { delay(1000); debug("Reseting wifiManager's settings."); wifiManager.resetSettings(); - delay(1000); - debug("rebooting..."); - ESP.restart(); - delay(1000); + doRestart("Rebooting..."); } // Reboot web page void handleReboot() { #if HTML_PASSWORD_ENABLE if (!server.authenticate(HttpUsername, HttpPassword)) { - debug("Basic HTTP authentication failure for /quitquitquit."); + debug(("Basic HTTP authentication failure for " + + String(kUrlReboot)).c_str()); return server.requestAuthentication(); } #endif server.send(200, "text/html", - "Rebooting" - "" - "

Device restarting.

" + htmlHeader(F("Device restarting.")) + "

Try connecting in a few seconds.

" + - addJsReloadUrl("/", 15, true) + - ""); -#if MQTT_ENABLE - mqttLog("Reboot requested"); -#endif // MQTT_ENABLE - // Do the reset. - delay(1000); - ESP.restart(); - delay(1000); + addJsReloadUrl(kUrlRoot, kRebootTime, true) + + htmlEnd()); + doRestart("Reboot requested"); } // Parse an Air Conditioner A/C Hex String/code and send it. @@ -1311,7 +1315,7 @@ void handleReboot() { // str: A hexadecimal string containing the state to be sent. // Returns: // bool: Successfully sent or not. -bool parseStringAndSendAirCon(IRsend *irsend, const uint16_t irType, +bool parseStringAndSendAirCon(IRsend *irsend, const decode_type_t irType, const String str) { uint8_t strOffset = 0; uint8_t state[kStateSizeMax] = {0}; // All array elements are set to 0. @@ -1327,12 +1331,6 @@ bool parseStringAndSendAirCon(IRsend *irsend, const uint16_t irType, } switch (irType) { // Get the correct state size for the protocol. - case KELVINATOR: - stateSize = kKelvinatorStateLength; - break; - case TOSHIBA_AC: - stateSize = kToshibaACStateLength; - break; case DAIKIN: // Daikin has 2 different possible size states. // (The correct size, and a legacy shorter size.) @@ -1350,36 +1348,6 @@ bool parseStringAndSendAirCon(IRsend *irsend, const uint16_t irType, // Lastly, it should never exceed the "normal" size. stateSize = std::min(stateSize, kDaikinStateLength); break; - case DAIKIN2: - stateSize = kDaikin2StateLength; - break; - case DAIKIN216: - stateSize = kDaikin216StateLength; - break; - case ELECTRA_AC: - stateSize = kElectraAcStateLength; - break; - case MITSUBISHI_AC: - stateSize = kMitsubishiACStateLength; - break; - case MITSUBISHI_HEAVY_88: - stateSize = kMitsubishiHeavy88StateLength; - break; - case MITSUBISHI_HEAVY_152: - stateSize = kMitsubishiHeavy152StateLength; - break; - case PANASONIC_AC: - stateSize = kPanasonicAcStateLength; - break; - case TROTEC: - stateSize = kTrotecStateLength; - break; - case ARGO: - stateSize = kArgoStateLength; - break; - case GREE: - stateSize = kGreeStateLength; - break; case FUJITSU_AC: // Fujitsu has four distinct & different size states, so make a best guess // which one we are being presented with based on the number of @@ -1396,23 +1364,16 @@ bool parseStringAndSendAirCon(IRsend *irsend, const uint16_t irType, // Lastly, it should never exceed the maximum "normal" size. stateSize = std::min(stateSize, kFujitsuAcStateLength); break; - case HAIER_AC: - stateSize = kHaierACStateLength; - break; - case HAIER_AC_YRW02: - stateSize = kHaierACYRW02StateLength; - break; - case HITACHI_AC: - stateSize = kHitachiAcStateLength; - break; - case HITACHI_AC1: - stateSize = kHitachiAc1StateLength; - break; - case HITACHI_AC2: - stateSize = kHitachiAc2StateLength; - break; - case WHIRLPOOL_AC: - stateSize = kWhirlpoolAcStateLength; + case MWM: + // MWM has variable size states, so make a best guess + // which one we are being presented with based on the number of + // hexadecimal digits provided. i.e. Zero-pad if you need to to get + // the correct length/byte size. + stateSize = inputLength / 2; // Every two hex chars is a byte. + // Use at least the minimum size. + stateSize = std::max(stateSize, (uint16_t) 3); + // Cap the maximum size. + stateSize = std::min(stateSize, kStateSizeMax); break; case SAMSUNG_AC: // Samsung has two distinct & different size states, so make a best guess @@ -1430,23 +1391,13 @@ bool parseStringAndSendAirCon(IRsend *irsend, const uint16_t irType, // Lastly, it should never exceed the maximum "extended" size. stateSize = std::min(stateSize, kSamsungAcExtendedStateLength); break; - case MWM: - // MWM has variable size states, so make a best guess - // which one we are being presented with based on the number of - // hexadecimal digits provided. i.e. Zero-pad if you need to to get - // the correct length/byte size. - stateSize = inputLength / 2; // Every two hex chars is a byte. - // Use at least the minimum size. - stateSize = std::max(stateSize, (uint16_t) 3); - // Cap the maximum size. - stateSize = std::min(stateSize, kStateSizeMax); - break; - case TCL112AC: - stateSize = kTcl112AcStateLength; - break; - default: // Not a protocol we expected. Abort. - debug("Unexpected AirCon protocol detected. Ignoring."); - return false; + default: // Everything else. + stateSize = IRsend::defaultBits(irType) / 8; + if (!stateSize || !hasACState(irType)) { + // Not a protocol we expected. Abort. + debug("Unexpected AirCon protocol detected. Ignoring."); + return false; + } } if (inputLength > stateSize * 2) { debug("AirCon code to large for the given protocol."); @@ -1477,125 +1428,9 @@ bool parseStringAndSendAirCon(IRsend *irsend, const uint16_t irType, *statePtr = c; } } - - // Make the appropriate call for the protocol type. - switch (irType) { -#if SEND_KELVINATOR - case KELVINATOR: - irsend->sendKelvinator(reinterpret_cast(state)); - break; -#endif -#if SEND_TOSHIBA_AC - case TOSHIBA_AC: - irsend->sendToshibaAC(reinterpret_cast(state)); - break; -#endif -#if SEND_DAIKIN - case DAIKIN: - irsend->sendDaikin(reinterpret_cast(state)); - break; -#endif -#if SEND_DAIKIN2 - case DAIKIN2: - irsend->sendDaikin2(reinterpret_cast(state)); - break; -#endif -#if SEND_DAIKIN216 - case DAIKIN216: - irsend->sendDaikin216(reinterpret_cast(state)); - break; -#endif // SEND_DAIKIN216 -#if SEND_MITSUBISHI_AC - case MITSUBISHI_AC: - irsend->sendMitsubishiAC(reinterpret_cast(state)); - break; -#endif -#if SEND_MITSUBISHIHEAVY - case MITSUBISHI_HEAVY_88: // 59 - irsend->sendMitsubishiHeavy88(reinterpret_cast(state)); - break; - case MITSUBISHI_HEAVY_152: // 60 - irsend->sendMitsubishiHeavy152(reinterpret_cast(state)); - break; -#endif // SEND_MITSUBISHIHEAVY -#if SEND_TROTEC - case TROTEC: - irsend->sendTrotec(reinterpret_cast(state)); - break; -#endif -#if SEND_ARGO - case ARGO: - irsend->sendArgo(reinterpret_cast(state)); - break; -#endif -#if SEND_GREE - case GREE: - irsend->sendGree(reinterpret_cast(state)); - break; -#endif -#if SEND_FUJITSU_AC - case FUJITSU_AC: - irsend->sendFujitsuAC(reinterpret_cast(state), stateSize); - break; -#endif -#if SEND_HAIER_AC - case HAIER_AC: - irsend->sendHaierAC(reinterpret_cast(state)); - break; -#endif -#if SEND_HAIER_AC_YRW02 - case HAIER_AC_YRW02: - irsend->sendHaierACYRW02(reinterpret_cast(state)); - break; -#endif -#if SEND_HITACHI_AC - case HITACHI_AC: - irsend->sendHitachiAC(reinterpret_cast(state)); - break; -#endif -#if SEND_HITACHI_AC1 - case HITACHI_AC1: - irsend->sendHitachiAC1(reinterpret_cast(state)); - break; -#endif -#if SEND_HITACHI_AC2 - case HITACHI_AC2: - irsend->sendHitachiAC2(reinterpret_cast(state)); - break; -#endif -#if SEND_WHIRLPOOL_AC - case WHIRLPOOL_AC: - irsend->sendWhirlpoolAC(reinterpret_cast(state)); - break; -#endif -#if SEND_SAMSUNG_AC - case SAMSUNG_AC: - irsend->sendSamsungAC(reinterpret_cast(state), stateSize); - break; -#endif -#if SEND_ELECTRA_AC - case ELECTRA_AC: - irsend->sendElectraAC(reinterpret_cast(state)); - break; -#endif -#if SEND_PANASONIC_AC - case PANASONIC_AC: - irsend->sendPanasonicAC(reinterpret_cast(state)); - break; -#endif -#if SEND_MWM - case MWM: - irsend->sendMWM(reinterpret_cast(state), stateSize); - break; -#endif -#if SEND_TCL112AC - case TCL112AC: - irsend->sendTcl112Ac(reinterpret_cast(state)); - break; -#endif - default: - debug("Unexpected AirCon type in send request. Not sent."); - return false; + if (!irsend->send(irType, state, stateSize)) { + debug("Unexpected AirCon type in send request. Not sent."); + return false; } return true; // We were successful as far as we can tell. } @@ -1620,20 +1455,16 @@ uint16_t countValuesInStr(const String str, char sep) { // Args: // size: Nr. of uint16_t's need to be in the new array. // Returns: -// A Ptr to the new array. Restarts the ESP8266 if it fails. +// A Ptr to the new array. Restarts the ESP if it fails. uint16_t * newCodeArray(const uint16_t size) { uint16_t *result; result = reinterpret_cast(malloc(size * sizeof(uint16_t))); // Check we malloc'ed successfully. - if (result == NULL) { // malloc failed, so give up. - Serial.printf("\nCan't allocate %d bytes. (%d bytes free)\n", - size * sizeof(uint16_t), ESP.getFreeHeap()); - Serial.println("Giving up & forcing a reboot."); - ESP.restart(); // Reboot. - delay(500); // Wait for the restart to happen. - return result; // Should never get here, but just in case. - } + if (result == NULL) // malloc failed, so give up. + doRestart( + "FATAL: Can't allocate memory for an array for a new message! " + "Forcing a reboot!", true); // Send to serial only as we are in low mem return result; } @@ -1676,7 +1507,6 @@ bool parseStringAndSendGC(IRsend *irsend, const String str) { start_from = index + 1; count++; } while (index != -1); - irsend->sendGC(code_array, count); // All done. Send it. free(code_array); // Free up the memory allocated. if (count > 0) @@ -1795,6 +1625,16 @@ bool parseStringAndSendRaw(IRsend *irsend, const String str) { } #endif // SEND_RAW +uint8_t getDefaultIrSendIdx(void) { + for (uint16_t i = 0; i < kNrOfIrTxGpios; i++) + if (IrSendTable[i] != NULL) return i; + return 0; +} + +IRsend* getDefaultIrSendPtr(void) { + return IrSendTable[getDefaultIrSendIdx()]; +} + // Parse the URL args to find the IR code. void handleIr(void) { #if HTML_PASSWORD_ENABLE @@ -1805,7 +1645,7 @@ void handleIr(void) { #endif uint64_t data = 0; String data_str = ""; - int16_t ir_type = decode_type_t::NEC; // Default to NEC codes. + decode_type_t ir_type = decode_type_t::NEC; // Default to NEC codes. uint16_t nbits = 0; uint16_t repeat = 0; @@ -1823,17 +1663,104 @@ void handleIr(void) { } } debug("New code received via HTTP"); - lastSendSucceeded = sendIRCode(IrSendTable[0], ir_type, data, + lastSendSucceeded = sendIRCode(getDefaultIrSendPtr(), ir_type, data, data_str.c_str(), nbits, repeat); - String html = F( - "Send IR command" - "" - "

IR command sent!

"); - html += addJsReloadUrl("/", 2, true); - html += F(""); + String html = htmlHeader(F("IR command sent!")); + html += addJsReloadUrl(kUrlRoot, kQuickDisplayTime, true); + html += htmlEnd(); server.send(200, "text/html", html); } +// GPIO menu page +void handleGpio(void) { +#if HTML_PASSWORD_ENABLE + if (!server.authenticate(HttpUsername, HttpPassword)) { + debug("Basic HTTP authentication failure for /gpios."); + return server.requestAuthentication(); + } +#endif + String html = htmlHeader(F("GPIO config")); + html += F( + "
"); + html += htmlMenu(); + html += F("

WARNING: Choose carefully! You can cause damage to your " + "hardware or make the device unresponsive.

"); + html += F("

Send

IR LED"); + for (uint16_t i = 0; i < kNrOfIrTxGpios; i++) { + if (kNrOfIrTxGpios > 1) { + html += F(" #"); + html += String(i); + } + html += htmlSelectGpio(KEY_TX_GPIO + String(i), txGpioTable[i], kTxGpios, + sizeof(kTxGpios)); + } +#if IR_RX + html += F("

Receive

IR RX Module"); + html += htmlSelectGpio(KEY_RX_GPIO, rx_gpio, kRxGpios, + sizeof(kRxGpios)); +#endif // IR_RX + html += F("


"); + if (strlen(HttpPassword)) // Allow if password set + html += F(""); + else + html += htmlDisabled(); + html += F("
"); + html += htmlEnd(); + server.send(200, "text/html", html); +} + +// GPIO setting page +void handleGpioSetting(void) { + bool changed = false; + if (!server.authenticate(HttpUsername, HttpPassword)) { + debug("Basic HTTP authentication failure for /gpios."); + return server.requestAuthentication(); + } + String html = htmlHeader(F("Update GPIOs")); + if (!strlen(HttpPassword)) { // Don't allow if password not set + html += htmlDisabled(); + } else { + debug("Attempt to change GPIOs"); + for (uint16_t arg = 0; arg < server.args(); arg++) { + int8_t num = std::max(static_cast(server.arg(arg).toInt()), + kGpioUnused); +#if IR_RX + if (server.argName(arg).equals(KEY_RX_GPIO)) { + if (rx_gpio != num) { + rx_gpio = num; + changed = true; + } + } else { +#endif // IR_RX + for (uint16_t i = 0; i < kNrOfIrTxGpios; i++) { + if (server.argName(arg).equals(KEY_TX_GPIO + String(i))) { + if (txGpioTable[i] != num) { + txGpioTable[i] = num; + changed = true; + } + } + } +#if IR_RX + } +#endif // IR_RX + } + if (!changed) { + html += F("

No changes detected!

"); + } else if (saveConfig()) { + html += F("

Saved changes & rebooting.

"); + } else { + html += F("

ERROR: Changes didn't save correctly! " + "Rebooting.

"); + } + } + html += addJsReloadUrl(changed ? kUrlRoot : kUrlGpio, + changed ? kRebootTime : kQuickDisplayTime, + true); + html += htmlEnd(); + server.send(200, "text/html", html); + if (changed) doRestart("GPIOs were changed. Rebooting!"); +} + void handleNotFound(void) { String message = "File Not Found\n\n"; message += "URI: "; @@ -1850,7 +1777,7 @@ void handleNotFound(void) { void setup_wifi(void) { delay(10); - loadWifiConfigFile(); + loadConfigFile(); // We start by connecting to a WiFi network wifiManager.setTimeout(300); // Time out after 5 mins. // Set up additional parameters for WiFiManager config menu page. @@ -1896,7 +1823,7 @@ void setup_wifi(void) { kMqttPrefixKey, "Leave empty to use Hostname", MqttPrefix, kHostnameLength); wifiManager.addParameter(&custom_mqtt_prefix); - #endif // MQTT_ENABLE +#endif // MQTT_ENABLE #if USE_STATIC_IP // Use a static IP config rather than the one supplied via DHCP. wifiManager.setSTAStaticIPConfig(kIPAddress, kGateway, kSubnetMask); @@ -1906,13 +1833,9 @@ void setup_wifi(void) { #endif // MIN_SIGNAL_STRENGTH wifiManager.setRemoveDuplicateAPs(HIDE_DUPLIATE_NETWORKS); - if (!wifiManager.autoConnect()) { - debug("Wifi failed to connect and hit timeout. Rebooting..."); - delay(3000); + if (!wifiManager.autoConnect()) // Reboot. A.k.a. "Have you tried turning it Off and On again?" - ESP.reset(); - delay(5000); - } + doRestart("Wifi failed to connect and hit timeout. Rebooting...", true); #if MQTT_ENABLE strncpy(MqttServer, custom_mqtt_server.getValue(), kHostnameLength); @@ -1925,7 +1848,7 @@ void setup_wifi(void) { strncpy(HttpUsername, custom_http_username.getValue(), kUsernameLength); strncpy(HttpPassword, custom_http_password.getValue(), kPasswordLength); if (flagSaveWifiConfig) { - saveWifiConfig(); + saveConfig(); } debug("WiFi connected. IP address:"); debug(WiFi.localIP().toString().c_str()); @@ -1951,10 +1874,12 @@ void init_vars(void) { MqttClimateCmnd = MqttClimate + '/' + MQTT_CLIMATE_CMND + '/'; // Sub-topic for the climate stat topics. MqttClimateStat = MqttClimate + '/' + MQTT_CLIMATE_STAT + '/'; +#if MQTT_DISCOVERY_ENABLE MqttDiscovery = "homeassistant/climate/" + String(Hostname) + "/config"; +#endif // MQTT_DISCOVERY_ENABLE MqttHAName = String(Hostname) + "_aircon"; // Create a unique MQTT client id. - MqttClientId = String(Hostname) + String(ESP.getChipId(), HEX); + MqttClientId = String(Hostname) + String(kChipId, HEX); #endif // MQTT_ENABLE } @@ -1979,67 +1904,98 @@ void setup(void) { climate.sleep = -1; // Off climate.clock = -1; // Don't set. climate_prev = climate; - - // Initialise all the IR transmitters. - for (uint8_t i = 0; i < kSendTableSize; i++) { - IrSendTable[i] = new IRsend(gpioTable[i]); - IrSendTable[i]->begin(); - offset = IrSendTable[i]->calibrate(); - } -#ifdef IR_RX -#if IR_RX_PULLUP - pinMode(IR_RX, INPUT_PULLUP); -#endif // IR_RX_PULLUP -#if DECODE_HASH - // Ignore messages with less than minimum on or off pulses. - irrecv.setUnknownThreshold(kMinUnknownSize); -#endif // DECODE_HASH - irrecv.enableIRIn(); // Start the receiver -#endif // IR_RX + lastClimateSource = F("None"); #if DEBUG if (!isSerialGpioUsedByIr()) { +#if defined(ESP8266) // Use SERIAL_TX_ONLY so that the RX pin can be freed up for GPIO/IR use. Serial.begin(BAUD_RATE, SERIAL_8N1, SERIAL_TX_ONLY); +#else // ESP8266 + Serial.begin(BAUD_RATE, SERIAL_8N1); +#endif // ESP8266 while (!Serial) // Wait for the serial connection to be establised. delay(50); Serial.println(); - debug("IRMQTTServer " _MY_VERSION_" has booted."); + debug("IRMQTTServer " _MY_VERSION_ " has booted."); } #endif // DEBUG setup_wifi(); +#if DEBUG + // After the config has been loaded, check again if we are using a Serial GPIO + if (isSerialGpioUsedByIr()) Serial.end(); +#endif // DEBUG + + // Initialise all the IR transmitters. + for (uint8_t i = 0; i < kNrOfIrTxGpios; i++) { + if (txGpioTable[i] == kGpioUnused) { + IrSendTable[i] = NULL; + } else { + IrSendTable[i] = new IRsend(txGpioTable[i], kInvertTxOutput); + if (IrSendTable[i] == NULL) break; + IrSendTable[i]->begin(); + offset = IrSendTable[i]->calibrate(); + } + } +#if IR_RX + if (rx_gpio != kGpioUnused) + irrecv = new IRrecv(rx_gpio, kCaptureBufferSize, kCaptureTimeout, true); + if (irrecv != NULL) { +#if DECODE_HASH + // Ignore messages with less than minimum on or off pulses. + irrecv->setUnknownThreshold(kMinUnknownSize); +#endif // DECODE_HASH + irrecv->enableIRIn(IR_RX_PULLUP); // Start the receiver + } +#endif // IR_RX + commonAc = new IRac(txGpioTable[0], kInvertTxOutput); + // Wait a bit for things to settle. delay(500); lastReconnectAttempt = 0; +#if MDNS_ENABLE +#if defined(ESP8266) if (mdns.begin(Hostname, WiFi.localIP())) { +#else // ESP8266 + if (mdns.begin(Hostname)) { +#endif // ESP8266 debug("MDNS responder started"); } +#endif // MDNS_ENABLE // Setup the root web page. - server.on("/", handleRoot); + server.on(kUrlRoot, handleRoot); +#if EXAMPLES_ENABLE // Setup the examples web page. - server.on("/examples", handleExamples); + server.on(kUrlExamples, handleExamples); +#endif // EXAMPLES_ENABLE // Setup the page to handle web-based IR codes. server.on("/ir", handleIr); // Setup the aircon page. - server.on("/aircon", handleAirCon); + server.on(kUrlAircon, handleAirCon); // Setup the aircon update page. server.on("/aircon/set", handleAirConSet); // Setup the info page. - server.on("/info", handleInfo); + server.on(kUrlInfo, handleInfo); // Setup the admin page. - server.on("/admin", handleAdmin); + server.on(kUrlAdmin, handleAdmin); // Setup a reset page to cause WiFiManager information to be reset. - server.on("/reset", handleReset); + server.on(kUrlWipe, handleReset); // Reboot url - server.on("/quitquitquit", handleReboot); + server.on(kUrlReboot, handleReboot); + // Show & pick which gpios are used for what etc. + server.on(kUrlGpio, handleGpio); + // Parse and update the new gpios. + server.on(kUrlGpioSet, handleGpioSetting); #if MQTT_ENABLE +#if MQTT_DISCOVERY_ENABLE // MQTT Discovery url - server.on("/send_discovery", handleSendMqttDiscovery); + server.on(kUrlSendDiscovery, handleSendMqttDiscovery); +#endif // MQTT_DISCOVERY_ENABLE // Finish setup of the mqtt clent object. mqtt_client.setServer(MqttServer, atoi(MqttPort)); mqtt_client.setCallback(mqttCallback); @@ -2056,9 +2012,7 @@ void setup(void) { delay(1000); #endif // MQTT_ENABLE server.send(200, "text/html", - "Updating firmware." - "" - "

Updating firmware

" + htmlHeader(F("Updating firmware")) + "
" "

Warning! Don't power off the device for 60 seconds!

" "

The firmware is uploading and will try to flash itself. " @@ -2066,11 +2020,9 @@ void setup(void) { "

The firmware upload seems to have " + String(Update.hasError() ? "FAILED!" : "SUCCEEDED!") + " Rebooting!

" + - addJsReloadUrl("/", 20, true) + - ""); - delay(1000); - ESP.restart(); - delay(1000); + addJsReloadUrl(kUrlRoot, 20, true) + + htmlEnd()); + doRestart("Post firmware reboot."); }, [](){ if (!server.authenticate(HttpUsername, HttpPassword)) { debug("Basic HTTP authentication failure for /update."); @@ -2078,12 +2030,12 @@ void setup(void) { } HTTPUpload& upload = server.upload(); if (upload.status == UPLOAD_FILE_START) { - WiFiUDP::stopAll(); debug("Update:"); debug(upload.filename.c_str()); - uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & - 0xFFFFF000; - if (!Update.begin(maxSketchSpace)) { // start with max available size +#if defined(ESP8266) + WiFiUDP::stopAll(); +#endif // defined(ESP8266) + if (!Update.begin(maxSketchSpace())) { // start with max available #if DEBUG if (!isSerialGpioUsedByIr()) Update.printError(Serial); @@ -2138,9 +2090,9 @@ void unsubscribing(const String topic_name) { debug(topic_name.c_str()); } -void mqttLog(const String mesg) { - debug(mesg.c_str()); - mqtt_client.publish(MqttLog.c_str(), mesg.c_str()); +void mqttLog(const char* str) { + debug(str); + mqtt_client.publish(MqttLog.c_str(), str); mqttSentCounter++; } @@ -2174,7 +2126,7 @@ bool reconnect(void) { // Subscribing to topic(s) subscribing(MqttSend); - for (uint8_t i = 0; i < kSendTableSize; i++) { + for (uint8_t i = 0; i < kNrOfIrTxGpios; i++) { subscribing(MqttSend + '_' + String(static_cast(i))); } // Climate command topics. @@ -2193,12 +2145,13 @@ bool reconnect(void) { // Return a string containing the comma separated list of MQTT command topics. String listOfCommandTopics(void) { String result = MqttSend; - for (uint16_t i = 0; i < kSendTableSize; i++) { + for (uint16_t i = 0; i < kNrOfIrTxGpios; i++) { result += ", " + MqttSend + '_' + String(i); } return result; } +#if MQTT_DISCOVERY_ENABLE // MQTT Discovery web page void handleSendMqttDiscovery(void) { #if HTML_PASSWORD_ENABLE @@ -2208,9 +2161,7 @@ void handleSendMqttDiscovery(void) { } #endif // HTML_PASSWORD_ENABLE server.send(200, "text/html", - "Sending MQTT Discovery message" - "" - "

Sending MQTT Discovery message.

" + + htmlHeader(F("Sending MQTT Discovery message")) + htmlMenu() + "

The Home Assistant MQTT Discovery message is being sent to topic: " + MqttDiscovery + ". It will show up in Home Assistant in a few seconds." @@ -2218,29 +2169,35 @@ void handleSendMqttDiscovery(void) { "

Warning!

" "

Home Assistant's config for this device is reset each time this is " " is sent.

" + - addJsReloadUrl("/", 15, true) + - ""); + addJsReloadUrl(kUrlRoot, kRebootTime, true) + + htmlEnd()); sendMQTTDiscovery(MqttDiscovery.c_str()); } +#endif // MQTT_DISCOVERY_ENABLE void doBroadcast(TimerMs *timer, const uint32_t interval, - const commonAcState_t state, const bool retain, + const stdAc::state_t state, const bool retain, const bool force) { if (force || (!lockMqttBroadcast && timer->elapsed() > interval)) { debug("Sending MQTT stat update broadcast."); sendClimate(state, state, MqttClimateStat, retain, true, false); +#if REPORT_VCC + sendString(MqttClimateStat + KEY_VCC, vccToString(), false); +#endif // REPORT_VCC +#if MQTT_CLIMATE_JSON + sendJsonState(state, MqttClimateStat + KEY_JSON); +#endif // MQTT_CLIMATE_JSON timer->reset(); // It's been sent, so reset the timer. hasBroadcastBeenSent = true; } } void receivingMQTT(String const topic_name, String const callback_str) { - char* tok_ptr; uint64_t code = 0; uint16_t nbits = 0; uint16_t repeat = 0; - uint8_t channel = 0; // Default to the first channel. e.g. "*_0" + uint8_t channel = getDefaultIrSendIdx(); // Default to first usable channel. debug("Receiving data by MQTT topic:"); debug(topic_name.c_str()); @@ -2255,10 +2212,18 @@ void receivingMQTT(String const topic_name, String const callback_str) { if (topic_name.startsWith(MqttClimate)) { if (topic_name.startsWith(MqttClimateCmnd)) { debug("It's a climate command topic"); - commonAcState_t updated = updateClimate( + stdAc::state_t updated = updateClimate( climate, topic_name, MqttClimateCmnd, callback_str); - sendClimate(climate, updated, MqttClimateStat, - true, false, false); + // Handle the special command for forcing a resend of the state via IR. + bool force_resend = false; + if (topic_name.equals(MqttClimateCmnd + KEY_RESEND) && + callback_str.equalsIgnoreCase(KEY_RESEND)) { + force_resend = true; + mqttLog("Climate resend requested."); + } + if (sendClimate(climate, updated, MqttClimateStat, + true, false, force_resend) && !force_resend) + lastClimateSource = F("MQTT"); climate = updated; } else if (topic_name.startsWith(MqttClimateStat)) { debug("It's a climate state topic. Update internal state and DON'T send"); @@ -2268,7 +2233,7 @@ void receivingMQTT(String const topic_name, String const callback_str) { return; // We are done for now. } // Check if a specific channel was requested by looking for a "*_[0-9]" suffix - for (uint8_t i = 0; i < kSendTableSize; i++) { + for (uint8_t i = 0; i < kNrOfIrTxGpios; i++) { debug(("Checking if " + topic_name + " ends with _" + String(i)).c_str()); if (topic_name.endsWith("_" + String(i))) { channel = i; @@ -2278,39 +2243,67 @@ void receivingMQTT(String const topic_name, String const callback_str) { } debug(("Using transmit channel " + String(static_cast(channel)) + - " / GPIO " + String(static_cast(gpioTable[channel]))).c_str()); + " / GPIO " + String(static_cast(txGpioTable[channel]))).c_str()); // Make a copy of the callback string as strtok destroys it. char* callback_c_str = strdup(callback_str.c_str()); debug("MQTT Payload (raw):"); debug(callback_c_str); - // Get the numeric protocol type. - int ir_type = strtoul(strtok_r(callback_c_str, ",", &tok_ptr), NULL, 10); - char* next = strtok_r(NULL, ",", &tok_ptr); - // If there is unparsed string left, try to convert it assuming it's hex. - if (next != NULL) { - code = getUInt64fromHex(next); - next = strtok_r(NULL, ",", &tok_ptr); - } else { - // We require at least two value in the string. Give up. - return; + // Chop up the str into command chunks. + // i.e. commands in a sequence are delimitered by ';'. + char* sequence_tok_ptr; + for (char* sequence_item = strtok_r(callback_c_str, kSequenceDelimiter, + &sequence_tok_ptr); + sequence_item != NULL; + sequence_item = strtok_r(NULL, kSequenceDelimiter, &sequence_tok_ptr)) { + // Now, process each command individually. + char* tok_ptr; + // Make a copy of the sequence_item str as strtok_r stomps on it. + char* ircommand = strdup(sequence_item); + // Check if it is a pause command. + switch (ircommand[0]) { + case kPauseChar: + { // It's a pause. Everything after the 'P' should be a number. + int32_t msecs = std::min((int32_t) strtoul(ircommand + 1, NULL, 10), + kMaxPauseMs); + delay(msecs); + mqtt_client.publish(MqttAck.c_str(), + String(kPauseChar + String(msecs)).c_str()); + mqttSentCounter++; + break; + } + default: // It's an IR command. + { + // Get the numeric protocol type. + decode_type_t ir_type = (decode_type_t)atoi(strtok_r( + ircommand, kCommandDelimiter, &tok_ptr)); + char* next = strtok_r(NULL, kCommandDelimiter, &tok_ptr); + // If there is unparsed string left, try to convert it assuming it's + // hex. + if (next != NULL) { + code = getUInt64fromHex(next); + next = strtok_r(NULL, kCommandDelimiter, &tok_ptr); + } else { + // We require at least two value in the string. Give up. + break; + } + // If there is still string left, assume it is the bit size. + if (next != NULL) { + nbits = atoi(next); + next = strtok_r(NULL, kCommandDelimiter, &tok_ptr); + } + // If there is still string left, assume it is the repeat count. + if (next != NULL) + repeat = atoi(next); + // send received MQTT value by IR signal + lastSendSucceeded = sendIRCode( + IrSendTable[channel], ir_type, code, + strchr(sequence_item, kCommandDelimiter[0]) + 1, nbits, repeat); + } + } + free(ircommand); } - // If there is still string left, assume it is the bit size. - if (next != NULL) { - nbits = atoi(next); - next = strtok_r(NULL, ",", &tok_ptr); - } - // If there is still string left, assume it is the repeat count. - if (next != NULL) - repeat = atoi(next); - free(callback_c_str); - - // send received MQTT value by IR signal - lastSendSucceeded = sendIRCode( - IrSendTable[channel], ir_type, code, - callback_str.substring(callback_str.indexOf(",") + 1).c_str(), - nbits, repeat); } // Callback function, when we receive an MQTT value on the topics @@ -2321,6 +2314,10 @@ void mqttCallback(char* topic, byte* payload, unsigned int length) { // constructing the PUBLISH packet. // Allocate the correct amount of memory for the payload copy byte* payload_copy = reinterpret_cast(malloc(length + 1)); + if (payload_copy == NULL) { + debug("Can't allocate memory for `payload_copy`. Skipping callback!"); + return; + } // Copy the payload to the new buffer memcpy(payload_copy, payload, length); @@ -2336,35 +2333,29 @@ void mqttCallback(char* topic, byte* payload, unsigned int length) { free(payload_copy); } +#if MQTT_DISCOVERY_ENABLE void sendMQTTDiscovery(const char *topic) { if (mqtt_client.publish( topic, String( "{" "\"~\":\"" + MqttClimate + "\"," "\"name\":\"" + MqttHAName + "\"," - "\"pow_cmd_t\":\"~" MQTT_CLIMATE "/" MQTT_CLIMATE_CMND "/" KEY_POWER "\"," - "\"mode_cmd_t\":\"~" MQTT_CLIMATE "/" MQTT_CLIMATE_CMND "/" KEY_MODE "\"," - "\"mode_stat_t\":\"~" MQTT_CLIMATE "/" MQTT_CLIMATE_STAT "/" KEY_MODE - "\"," + "\"pow_cmd_t\":\"~/" MQTT_CLIMATE_CMND "/" KEY_POWER "\"," + "\"mode_cmd_t\":\"~/" MQTT_CLIMATE_CMND "/" KEY_MODE "\"," + "\"mode_stat_t\":\"~/" MQTT_CLIMATE_STAT "/" KEY_MODE "\"," "\"modes\":[\"off\",\"auto\",\"cool\",\"heat\",\"dry\",\"fan_only\"]," - "\"temp_cmd_t\":\"~" MQTT_CLIMATE "/" MQTT_CLIMATE_CMND "/" KEY_TEMP "\"," - "\"temp_stat_t\":\"~" MQTT_CLIMATE "/" MQTT_CLIMATE_STAT "/" KEY_TEMP - "\"," + "\"temp_cmd_t\":\"~/" MQTT_CLIMATE_CMND "/" KEY_TEMP "\"," + "\"temp_stat_t\":\"~/" MQTT_CLIMATE_STAT "/" KEY_TEMP "\"," "\"min_temp\":\"16\"," "\"max_temp\":\"30\"," "\"temp_step\":\"1\"," - "\"fan_mode_cmd_t\":\"~" MQTT_CLIMATE "/" MQTT_CLIMATE_CMND "/" - KEY_FANSPEED "\"," - "\"fan_mode_stat_t\":\"~" MQTT_CLIMATE "/" MQTT_CLIMATE_STAT "/" - KEY_FANSPEED "\"," + "\"fan_mode_cmd_t\":\"~/" MQTT_CLIMATE_CMND "/" KEY_FANSPEED "\"," + "\"fan_mode_stat_t\":\"~/" MQTT_CLIMATE_STAT "/" KEY_FANSPEED "\"," "\"fan_modes\":[\"auto\",\"min\",\"low\",\"medium\",\"high\",\"max\"]," - "\"swing_mode_cmd_t\":\"~" MQTT_CLIMATE "/" MQTT_CLIMATE_CMND "/" - KEY_SWINGV "\"," - "\"swing_mode_stat_t\":\"~" MQTT_CLIMATE "/" MQTT_CLIMATE_STAT "/" - KEY_SWINGV "\"," + "\"swing_mode_cmd_t\":\"~/" MQTT_CLIMATE_CMND "/" KEY_SWINGV "\"," + "\"swing_mode_stat_t\":\"~/" MQTT_CLIMATE_STAT "/" KEY_SWINGV "\"," "\"swing_modes\":[" - "\"off\",\"auto\",\"highest\",\"high\",\"middle\",\"low\",\"lowest\"" - "]" + "\"off\",\"auto\",\"highest\",\"high\",\"middle\",\"low\",\"lowest\"]" "}").c_str())) { mqttLog("MQTT climate discovery successful sent."); hasDiscoveryBeenSent = true; @@ -2374,6 +2365,7 @@ void sendMQTTDiscovery(const char *topic) { mqttLog("MQTT climate discovery FAILED to send."); } } +#endif // MQTT_DISCOVERY_ENABLE #endif // MQTT_ENABLE void loop(void) { @@ -2397,11 +2389,12 @@ void loop(void) { lastReconnectAttempt = 0; wasConnected = true; if (boot) { - mqttLog("IR Server just booted"); + mqttLog("IRMQTTServer " _MY_VERSION_ " just booted"); boot = false; } else { - mqttLog("IR Server just (re)connected to MQTT. " - "Lost connection about " + timeSince(lastConnectedTime)); + mqttLog(String( + "IRMQTTServer just (re)connected to MQTT. Lost connection about " + + timeSince(lastConnectedTime)).c_str()); } lastConnectedTime = now; debug("successful client mqtt connection"); @@ -2422,10 +2415,11 @@ void loop(void) { if (lockMqttBroadcast && statListenTime.elapsed() > kStatListenPeriodMs) { unsubscribing(MqttClimateStat + '+'); mqttLog("Finished listening for previous state."); - if (cmpClimate(climate, climate_prev)) { // Something changed. + if (IRac::cmpStates(climate, climate_prev)) { // Something changed. mqttLog("The state was recovered from MQTT broker. Updating."); sendClimate(climate_prev, climate, MqttClimateStat, - true, false, false); + true, false, false, MQTT_CLIMATE_IR_SEND_ON_RESTART); + lastClimateSource = F("MQTT (via retain)"); } lockMqttBroadcast = false; // Release the lock so we can broadcast again. } @@ -2433,15 +2427,16 @@ void loop(void) { doBroadcast(&lastBroadcast, kBroadcastPeriodMs, climate, false, false); } #endif // MQTT_ENABLE -#ifdef IR_RX +#if IR_RX // Check if an IR code has been received via the IR RX module. #if REPORT_UNKNOWNS - if (irrecv.decode(&capture)) { + if (irrecv != NULL && irrecv->decode(&capture)) { #else // REPORT_UNKNOWNS - if (irrecv.decode(&capture) && capture.decode_type != UNKNOWN) { + if (irrecv != NULL && irrecv->decode(&capture) && + capture.decode_type != UNKNOWN) { #endif // REPORT_UNKNOWNS lastIrReceivedTime = millis(); - lastIrReceived = String(capture.decode_type) + "," + + lastIrReceived = String(capture.decode_type) + kCommandDelimiter[0] + resultToHexidecimal(&capture); #if REPORT_RAW_UNKNOWNS if (capture.decode_type == UNKNOWN) { @@ -2461,14 +2456,17 @@ void loop(void) { #endif // REPORT_RAW_UNKNOWNS // If it isn't an AC code, add the bits. if (!hasACState(capture.decode_type)) - lastIrReceived += "," + String(capture.bits); + lastIrReceived += kCommandDelimiter[0] + String(capture.bits); #if MQTT_ENABLE mqtt_client.publish(MqttRecv.c_str(), lastIrReceived.c_str()); mqttSentCounter++; -#endif // MQTT_ENABLE - irRecvCounter++; debug("Incoming IR message sent to MQTT:"); debug(lastIrReceived.c_str()); +#endif // MQTT_ENABLE + irRecvCounter++; +#if USE_DECODED_AC_SETTINGS + if (decodeCommonAc(&capture)) lastClimateSource = F("IR"); +#endif // USE_DECODED_AC_SETTINGS } #endif // IR_RX delay(100); @@ -2505,297 +2503,51 @@ uint64_t getUInt64fromHex(char const *str) { // repeat: Nr. of times the message is to be repeated. (Not all protcols.) // Returns: // bool: Successfully sent or not. -bool sendIRCode(IRsend *irsend, int const ir_type, +bool sendIRCode(IRsend *irsend, decode_type_t const ir_type, uint64_t const code, char const * code_str, uint16_t bits, uint16_t repeat) { + if (irsend == NULL) return false; + bool success = true; // Assume success. + // Ensure we have enough repeats. + repeat = std::max(IRsend::minRepeats(ir_type), repeat); + if (bits == 0) bits = IRsend::defaultBits(ir_type); // Create a pseudo-lock so we don't try to send two codes at the same time. while (lockIr) delay(20); lockIr = true; - bool success = true; // Assume success. + // Turn off IR capture if we need to. +#if IR_RX && DISABLE_CAPTURE_WHILE_TRANSMITTING + if (irrecv != NULL) irrecv->disableIRIn(); // Stop the IR receiver +#endif // IR_RX && DISABLE_CAPTURE_WHILE_TRANSMITTING // send the IR message. switch (ir_type) { -#if SEND_RC5 - case RC5: // 1 - if (bits == 0) - bits = kRC5Bits; - irsend->sendRC5(code, bits, repeat); - break; -#endif -#if SEND_RC6 - case RC6: // 2 - if (bits == 0) - bits = kRC6Mode0Bits; - irsend->sendRC6(code, bits, repeat); - break; -#endif -#if SEND_NEC - case NEC: // 3 - if (bits == 0) - bits = kNECBits; - irsend->sendNEC(code, bits, repeat); - break; -#endif -#if SEND_SONY - case SONY: // 4 - if (bits == 0) - bits = kSony12Bits; - repeat = std::max(repeat, kSonyMinRepeat); - irsend->sendSony(code, bits, repeat); - break; -#endif -#if SEND_PANASONIC - case PANASONIC: // 5 - if (bits == 0) - bits = kPanasonicBits; - irsend->sendPanasonic64(code, bits, repeat); - break; -#endif -#if SEND_JVC - case JVC: // 6 - if (bits == 0) - bits = kJvcBits; - irsend->sendJVC(code, bits, repeat); - break; -#endif -#if SEND_SAMSUNG - case SAMSUNG: // 7 - if (bits == 0) - bits = kSamsungBits; - irsend->sendSAMSUNG(code, bits, repeat); - break; -#endif -#if SEND_SAMSUNG36 - case SAMSUNG36: // 56 - if (bits == 0) - bits = kSamsung36Bits; - irsend->sendSamsung36(code, bits, repeat); - break; -#endif -#if SEND_WHYNTER - case WHYNTER: // 8 - if (bits == 0) - bits = kWhynterBits; - irsend->sendWhynter(code, bits, repeat); - break; -#endif -#if SEND_AIWA_RC_T501 - case AIWA_RC_T501: // 9 - if (bits == 0) - bits = kAiwaRcT501Bits; - repeat = std::max(repeat, kAiwaRcT501MinRepeats); - irsend->sendAiwaRCT501(code, bits, repeat); - break; -#endif -#if SEND_LG - case LG: // 10 - if (bits == 0) - bits = kLgBits; - irsend->sendLG(code, bits, repeat); - break; -#endif -#if SEND_MITSUBISHI - case MITSUBISHI: // 12 - if (bits == 0) - bits = kMitsubishiBits; - repeat = std::max(repeat, kMitsubishiMinRepeat); - irsend->sendMitsubishi(code, bits, repeat); - break; -#endif -#if SEND_DISH - case DISH: // 13 - if (bits == 0) - bits = kDishBits; - repeat = std::max(repeat, kDishMinRepeat); - irsend->sendDISH(code, bits, repeat); - break; -#endif -#if SEND_SHARP - case SHARP: // 14 - if (bits == 0) - bits = kSharpBits; - irsend->sendSharpRaw(code, bits, repeat); - break; -#endif -#if SEND_COOLIX - case COOLIX: // 15 - if (bits == 0) - bits = kCoolixBits; - irsend->sendCOOLIX(code, bits, repeat); - break; -#endif - case DAIKIN: // 16 - case DAIKIN2: // 53 - case DAIKIN216: // 61 - case KELVINATOR: // 18 - case MITSUBISHI_AC: // 20 - case GREE: // 24 - case ARGO: // 27 - case TROTEC: // 28 - case TOSHIBA_AC: // 32 - case FUJITSU_AC: // 33 - case HAIER_AC: // 38 - case HAIER_AC_YRW02: // 44 - case HITACHI_AC: // 40 - case HITACHI_AC1: // 41 - case HITACHI_AC2: // 42 - case WHIRLPOOL_AC: // 45 - case SAMSUNG_AC: // 46 - case ELECTRA_AC: // 48 - case PANASONIC_AC: // 49 - case MWM: // 52 - success = parseStringAndSendAirCon(irsend, ir_type, code_str); - break; -#if SEND_DENON - case DENON: // 17 - if (bits == 0) - bits = DENON_BITS; - irsend->sendDenon(code, bits, repeat); - break; -#endif -#if SEND_SHERWOOD - case SHERWOOD: // 19 - if (bits == 0) - bits = kSherwoodBits; - repeat = std::max(repeat, kSherwoodMinRepeat); - irsend->sendSherwood(code, bits, repeat); - break; -#endif -#if SEND_RCMM - case RCMM: // 21 - if (bits == 0) - bits = kRCMMBits; - irsend->sendRCMM(code, bits, repeat); - break; -#endif -#if SEND_SANYO - case SANYO_LC7461: // 22 - if (bits == 0) - bits = kSanyoLC7461Bits; - irsend->sendSanyoLC7461(code, bits, repeat); - break; -#endif -#if SEND_RC5 - case RC5X: // 23 - if (bits == 0) - bits = kRC5XBits; - irsend->sendRC5(code, bits, repeat); - break; -#endif #if SEND_PRONTO - case PRONTO: // 25 + case decode_type_t::PRONTO: // 25 success = parseStringAndSendPronto(irsend, code_str, repeat); break; -#endif -#if SEND_NIKAI - case NIKAI: // 29 - if (bits == 0) - bits = kNikaiBits; - irsend->sendNikai(code, bits, repeat); - break; -#endif +#endif // SEND_PRONTO + case decode_type_t::RAW: // 30 #if SEND_RAW - case RAW: // 30 success = parseStringAndSendRaw(irsend, code_str); break; #endif #if SEND_GLOBALCACHE - case GLOBALCACHE: // 31 + case decode_type_t::GLOBALCACHE: // 31 success = parseStringAndSendGC(irsend, code_str); break; #endif -#if SEND_MIDEA - case MIDEA: // 34 - if (bits == 0) - bits = kMideaBits; - irsend->sendMidea(code, bits, repeat); - break; -#endif -#if SEND_MAGIQUEST - case MAGIQUEST: // 35 - if (bits == 0) - bits = kMagiquestBits; - irsend->sendMagiQuest(code, bits, repeat); - break; -#endif -#if SEND_LASERTAG - case LASERTAG: // 36 - if (bits == 0) - bits = kLasertagBits; - irsend->sendLasertag(code, bits, repeat); - break; -#endif -#if SEND_CARRIER_AC - case CARRIER_AC: // 37 - if (bits == 0) - bits = kCarrierAcBits; - irsend->sendCarrierAC(code, bits, repeat); - break; -#endif -#if SEND_MITSUBISHI2 - case MITSUBISHI2: // 39 - if (bits == 0) - bits = kMitsubishiBits; - repeat = std::max(repeat, kMitsubishiMinRepeat); - irsend->sendMitsubishi2(code, bits, repeat); - break; -#endif -#if SEND_GICABLE - case GICABLE: // 43 - if (bits == 0) - bits = kGicableBits; - repeat = std::max(repeat, kGicableMinRepeat); - irsend->sendGICable(code, bits, repeat); - break; -#endif -#if SEND_LUTRON - case LUTRON: // 47 - if (bits == 0) - bits = kLutronBits; - irsend->sendLutron(code, bits, repeat); - break; -#endif -#if SEND_PIONEER - case PIONEER: // 50 - if (bits == 0) - bits = kPioneerBits; - irsend->sendPioneer(code, bits, repeat); - break; -#endif -#if SEND_LG - case LG2: // 51 - if (bits == 0) - bits = kLgBits; - irsend->sendLG2(code, bits, repeat); - break; -#endif -#if SEND_VESTEL_AC - case VESTEL_AC: // 54 - if (bits == 0) - bits = kVestelAcBits; - irsend->sendVestelAc(code, bits, repeat); - break; -#endif -#if SEND_TECO - case TECO: // 55 - if (bits == 0) - bits = kTecoBits; - irsend->sendTeco(code, bits, repeat); - break; -#endif -#if SEND_LEGOPF - case LEGOPF: // 58 - if (bits == 0) - bits = kLegoPfBits; - irsend->sendLegoPf(code, bits, repeat); - break; -#endif - default: - // If we got here, we didn't know how to send it. - success = false; + default: // Everything else. + if (hasACState(ir_type)) // protocols with > 64 bits + success = parseStringAndSendAirCon(irsend, ir_type, code_str); + else // protocols with <= 64 bits + success = irsend->send(ir_type, code, bits, repeat); } +#if IR_RX && DISABLE_CAPTURE_WHILE_TRANSMITTING + // Turn IR capture back on if we need to. + if (irrecv != NULL) irrecv->enableIRIn(); // Restart the receiver +#endif // IR_RX && DISABLE_CAPTURE_WHILE_TRANSMITTING lastSendTime = millis(); // Release the lock. lockIr = false; @@ -2810,9 +2562,7 @@ bool sendIRCode(IRsend *irsend, int const ir_type, debug("Type:"); debug(String(ir_type).c_str()); // For "long" codes we basically repeat what we got. - if (hasACState((decode_type_t) ir_type) || - ir_type == PRONTO || - ir_type == RAW || + if (hasACState(ir_type) || ir_type == PRONTO || ir_type == RAW || ir_type == GLOBALCACHE) { debug("Code: "); debug(code_str); @@ -2820,11 +2570,14 @@ bool sendIRCode(IRsend *irsend, int const ir_type, #if MQTT_ENABLE if (success) { if (ir_type == PRONTO && repeat > 0) - mqtt_client.publish(MqttAck.c_str(), (String(ir_type) + ",R" + - String(repeat) + "," + + mqtt_client.publish(MqttAck.c_str(), (String(ir_type) + + kCommandDelimiter[0] + 'R' + + String(repeat) + + kCommandDelimiter[0] + String(code_str)).c_str()); else - mqtt_client.publish(MqttAck.c_str(), (String(ir_type) + "," + + mqtt_client.publish(MqttAck.c_str(), (String(ir_type) + + kCommandDelimiter[0] + String(code_str)).c_str()); mqttSentCounter++; } @@ -2835,9 +2588,12 @@ bool sendIRCode(IRsend *irsend, int const ir_type, debug(("Repeats: " + String(repeat)).c_str()); #if MQTT_ENABLE if (success) { - mqtt_client.publish(MqttAck.c_str(), (String(ir_type) + "," + - uint64ToString(code, 16) - + "," + String(bits) + "," + + mqtt_client.publish(MqttAck.c_str(), (String(ir_type) + + kCommandDelimiter[0] + + uint64ToString(code, 16) + + kCommandDelimiter[0] + + String(bits) + + kCommandDelimiter[0] + String(repeat)).c_str()); mqttSentCounter++; } @@ -2882,62 +2638,135 @@ bool sendFloat(const String topic, const float_t temp, const bool retain) { #endif // MQTT_ENABLE } -commonAcState_t updateClimate(commonAcState_t current, const String str, - const String prefix, const String payload) { - commonAcState_t result = current; - String value = payload; - value.toUpperCase(); +#if MQTT_CLIMATE_JSON +void sendJsonState(const stdAc::state_t state, const String topic, + const bool retain, const bool ha_mode) { + DynamicJsonBuffer jsonBuffer; + JsonObject& json = jsonBuffer.createObject(); + json[KEY_PROTOCOL] = typeToString(state.protocol); + json[KEY_MODEL] = state.model; + json[KEY_POWER] = IRac::boolToString(state.power); + json[KEY_MODE] = IRac::opmodeToString(state.mode); + // Home Assistant wants mode to be off if power is also off & vice-versa. + if (ha_mode && (state.mode == stdAc::opmode_t::kOff || !state.power)) { + json[KEY_MODE] = IRac::opmodeToString(stdAc::opmode_t::kOff); + json[KEY_POWER] = IRac::boolToString(false); + } + json[KEY_CELSIUS] = IRac::boolToString(state.celsius); + json[KEY_TEMP] = state.degrees; + json[KEY_FANSPEED] = IRac::fanspeedToString(state.fanspeed); + json[KEY_SWINGV] = IRac::swingvToString(state.swingv); + json[KEY_SWINGH] = IRac::swinghToString(state.swingh); + json[KEY_QUIET] = IRac::boolToString(state.quiet); + json[KEY_TURBO] = IRac::boolToString(state.turbo); + json[KEY_ECONO] = IRac::boolToString(state.econo); + json[KEY_LIGHT] = IRac::boolToString(state.light); + json[KEY_FILTER] = IRac::boolToString(state.filter); + json[KEY_CLEAN] = IRac::boolToString(state.clean); + json[KEY_BEEP] = IRac::boolToString(state.beep); + json[KEY_SLEEP] = state.sleep; + + String payload = ""; + payload.reserve(200); + json.printTo(payload); + sendString(topic, payload, retain); +} + +stdAc::state_t jsonToState(const stdAc::state_t current, const String str) { + DynamicJsonBuffer jsonBuffer; + JsonObject& json = jsonBuffer.parseObject(str); + if (!json.success()) { + debug("json MQTT message did not parse. Skipping!"); + return current; + } + stdAc::state_t result = current; + if (json.containsKey(KEY_PROTOCOL)) + result.protocol = strToDecodeType(json[KEY_PROTOCOL]); + if (json.containsKey(KEY_MODEL)) + result.model = IRac::strToModel(json[KEY_MODEL]); + if (json.containsKey(KEY_MODE)) + result.mode = IRac::strToOpmode(json[KEY_MODE]); + if (json.containsKey(KEY_FANSPEED)) + result.fanspeed = IRac::strToFanspeed(json[KEY_FANSPEED]); + if (json.containsKey(KEY_SWINGV)) + result.swingv = IRac::strToSwingV(json[KEY_SWINGV]); + if (json.containsKey(KEY_SWINGH)) + result.swingh = IRac::strToSwingH(json[KEY_SWINGH]); + if (json.containsKey(KEY_TEMP)) + result.degrees = json[KEY_TEMP]; + if (json.containsKey(KEY_SLEEP)) + result.sleep = json[KEY_SLEEP]; + if (json.containsKey(KEY_POWER)) + result.power = IRac::strToBool(json[KEY_POWER]); + if (json.containsKey(KEY_QUIET)) + result.quiet = IRac::strToBool(json[KEY_QUIET]); + if (json.containsKey(KEY_TURBO)) + result.turbo = IRac::strToBool(json[KEY_TURBO]); + if (json.containsKey(KEY_ECONO)) + result.econo = IRac::strToBool(json[KEY_ECONO]); + if (json.containsKey(KEY_LIGHT)) + result.light = IRac::strToBool(json[KEY_LIGHT]); + if (json.containsKey(KEY_CLEAN)) + result.clean = IRac::strToBool(json[KEY_CLEAN]); + if (json.containsKey(KEY_FILTER)) + result.filter = IRac::strToBool(json[KEY_FILTER]); + if (json.containsKey(KEY_BEEP)) + result.beep = IRac::strToBool(json[KEY_BEEP]); + if (json.containsKey(KEY_CELSIUS)) + result.celsius = IRac::strToBool(json[KEY_CELSIUS]); + return result; +} +#endif // MQTT_CLIMATE_JSON + +stdAc::state_t updateClimate(stdAc::state_t current, const String str, + const String prefix, const String payload) { + stdAc::state_t result = current; +#if MQTT_CLIMATE_JSON + if (str.equals(prefix + KEY_JSON)) + result = jsonToState(result, payload.c_str()); + else +#endif // MQTT_CLIMATE_JSON if (str.equals(prefix + KEY_PROTOCOL)) - result.protocol = strToDecodeType(value.c_str()); + result.protocol = strToDecodeType(payload.c_str()); else if (str.equals(prefix + KEY_MODEL)) - result.model = IRac::strToModel(value.c_str()); + result.model = IRac::strToModel(payload.c_str()); else if (str.equals(prefix + KEY_POWER)) - result.power = IRac::strToBool(value.c_str()); + result.power = IRac::strToBool(payload.c_str()); else if (str.equals(prefix + KEY_MODE)) - result.mode = IRac::strToOpmode(value.c_str()); + result.mode = IRac::strToOpmode(payload.c_str()); else if (str.equals(prefix + KEY_TEMP)) - result.degrees = value.toFloat(); + result.degrees = payload.toFloat(); else if (str.equals(prefix + KEY_FANSPEED)) - result.fanspeed = IRac::strToFanspeed(value.c_str()); + result.fanspeed = IRac::strToFanspeed(payload.c_str()); else if (str.equals(prefix + KEY_SWINGV)) - result.swingv = IRac::strToSwingV(value.c_str()); + result.swingv = IRac::strToSwingV(payload.c_str()); else if (str.equals(prefix + KEY_SWINGH)) - result.swingh = IRac::strToSwingH(value.c_str()); + result.swingh = IRac::strToSwingH(payload.c_str()); else if (str.equals(prefix + KEY_QUIET)) - result.quiet = IRac::strToBool(value.c_str()); + result.quiet = IRac::strToBool(payload.c_str()); else if (str.equals(prefix + KEY_TURBO)) - result.turbo = IRac::strToBool(value.c_str()); + result.turbo = IRac::strToBool(payload.c_str()); else if (str.equals(prefix + KEY_ECONO)) - result.econo = IRac::strToBool(value.c_str()); + result.econo = IRac::strToBool(payload.c_str()); else if (str.equals(prefix + KEY_LIGHT)) - result.light = IRac::strToBool(value.c_str()); + result.light = IRac::strToBool(payload.c_str()); else if (str.equals(prefix + KEY_BEEP)) - result.beep = IRac::strToBool(value.c_str()); + result.beep = IRac::strToBool(payload.c_str()); else if (str.equals(prefix + KEY_FILTER)) - result.filter = IRac::strToBool(value.c_str()); + result.filter = IRac::strToBool(payload.c_str()); else if (str.equals(prefix + KEY_CLEAN)) - result.clean = IRac::strToBool(value.c_str()); + result.clean = IRac::strToBool(payload.c_str()); + else if (str.equals(prefix + KEY_CELSIUS)) + result.celsius = IRac::strToBool(payload.c_str()); else if (str.equals(prefix + KEY_SLEEP)) - result.sleep = value.toInt(); - else if (str.equals(prefix + KEY_CLOCK)) - result.clock = value.toInt(); + result.sleep = payload.toInt(); return result; } -// Compare two AirCon states (climates). -// Returns: True if they differ, False if they don't. -bool cmpClimate(const commonAcState_t a, const commonAcState_t b) { - return a.protocol != b.protocol || a.model != b.model || a.power != b.power || - a.mode != b.mode || a.degrees != b.degrees || a.celsius != b.celsius || - a.fanspeed != b.fanspeed || a.swingv != b.swingv || - a.swingh != b.swingh || a.quiet != b.quiet || a.turbo != b.turbo || - a.econo != b.econo || a.light != b.light || a.filter != b.filter || - a.clean != b.clean || a.beep != b.beep || a.sleep != b.sleep; -} - -bool sendClimate(const commonAcState_t prev, const commonAcState_t next, +bool sendClimate(const stdAc::state_t prev, const stdAc::state_t next, const String topic_prefix, const bool retain, - const bool forceMQTT, const bool forceIR) { + const bool forceMQTT, const bool forceIR, + const bool enableIR) { bool diff = false; bool success = true; @@ -2954,7 +2783,8 @@ bool sendClimate(const commonAcState_t prev, const commonAcState_t next, diff = true; success &= sendBool(topic_prefix + KEY_POWER, next.power, retain); success &= sendString(topic_prefix + KEY_MODE, - (next.power ? opmodeToString(next.mode) : F("off")), + (next.power ? IRac::opmodeToString(next.mode) + : F("off")), retain); } if (prev.degrees != next.degrees || forceMQTT) { @@ -2968,17 +2798,17 @@ bool sendClimate(const commonAcState_t prev, const commonAcState_t next, if (prev.fanspeed != next.fanspeed || forceMQTT) { diff = true; success &= sendString(topic_prefix + KEY_FANSPEED, - fanspeedToString(next.fanspeed), retain); + IRac::fanspeedToString(next.fanspeed), retain); } if (prev.swingv != next.swingv || forceMQTT) { diff = true; success &= sendString(topic_prefix + KEY_SWINGV, - swingvToString(next.swingv), retain); + IRac::swingvToString(next.swingv), retain); } if (prev.swingh != next.swingh || forceMQTT) { diff = true; success &= sendString(topic_prefix + KEY_SWINGH, - swinghToString(next.swingh), retain); + IRac::swinghToString(next.swingh), retain); } if (prev.quiet != next.quiet || forceMQTT) { diff = true; @@ -3012,18 +2842,26 @@ bool sendClimate(const commonAcState_t prev, const commonAcState_t next, diff = true; success &= sendInt(topic_prefix + KEY_SLEEP, next.sleep, retain); } - if (diff && !forceMQTT) + if (diff && !forceMQTT) { debug("Difference in common A/C state detected."); - else +#if MQTT_CLIMATE_JSON + sendJsonState(next, MqttClimateStat + KEY_JSON); +#endif // MQTT_CLIMATE_JSON + } else { debug("NO difference in common A/C state detected."); + } // Only send an IR message if we need to. - if ((diff && !forceMQTT) || forceIR) { + if (enableIR && ((diff && !forceMQTT) || forceIR)) { debug("Sending common A/C state via IR."); - lastClimateSucceeded = commonAc.sendAc( - next.protocol, next.model, next.power, next.mode, - next.degrees, next.celsius, next.fanspeed, next.swingv, next.swingh, - next.quiet, next.turbo, next.econo, next.light, next.filter, next.clean, - next.beep, next.sleep, -1); +#if IR_RX && DISABLE_CAPTURE_WHILE_TRANSMITTING + // Turn IR capture off if we need to. + if (irrecv != NULL) irrecv->disableIRIn(); // Stop the IR receiver +#endif // IR_RX && DISABLE_CAPTURE_WHILE_TRANSMITTING + lastClimateSucceeded = commonAc->sendAc(next, &prev); +#if IR_RX && DISABLE_CAPTURE_WHILE_TRANSMITTING + // Turn IR capture back on if we need to. + if (irrecv != NULL) irrecv->enableIRIn(); // Restart the receiver +#endif // IR_RX && DISABLE_CAPTURE_WHILE_TRANSMITTING if (lastClimateSucceeded) hasClimateBeenSent = true; success &= lastClimateSucceeded; lastClimateIr.reset(); @@ -3032,3 +2870,48 @@ bool sendClimate(const commonAcState_t prev, const commonAcState_t next, } return success; } + +#if USE_DECODED_AC_SETTINGS && IR_RX +// Decode and use a valid IR A/C remote that we understand enough to convert +// to a Common A/C format. +// Args: +// decode: A successful raw IR decode object. +// Returns: +// A boolean indicating success or failure. +bool decodeCommonAc(const decode_results *decode) { + if (!IRac::isProtocolSupported(decode->decode_type)) { + debug("Inbound IR messages isn't a supported common A/C protocol"); + return false; + } + stdAc::state_t state = climate; + debug("Converting inbound IR A/C message to common A/C"); + if (!IRAcUtils::decodeToState(decode, &state, &climate)) { + debug("Failed to convert to common A/C."); // This shouldn't happen! + return false; + } +#if IGNORE_DECODED_AC_PROTOCOL + if (climate.protocol != decode_type_t::UNKNOWN) { + // Use the previous protcol/model if set. + state.protocol = climate.protocol; + state.model = climate.model; + } +#endif // IGNORE_DECODED_AC_PROTOCOL +// Continue to use the previously prefered temperature units. +// i.e. Keep using Celsius or Fahrenheit. +if (climate.celsius != state.celsius) { + // We've got a mismatch, so we need to convert. + state.degrees = climate.celsius ? fahrenheitToCelsius(state.degrees) + : celsiusToFahrenheit(state.degrees); + state.celsius = climate.celsius; +} +#if MQTT_ENABLE + sendClimate(climate, state, MqttClimateStat, true, false, + REPLAY_DECODED_AC_MESSAGE, REPLAY_DECODED_AC_MESSAGE); +#else // MQTT_ENABLE + sendClimate(climate, state, "", false, false, REPLAY_DECODED_AC_MESSAGE, + REPLAY_DECODED_AC_MESSAGE); +#endif // MQTT_ENABLE + climate = state; // Copy over the new climate state. + return true; +} +#endif // USE_DECODED_AC_SETTINGS && IR_RX diff --git a/lib/IRremoteESP8266-2.6.5/examples/IRMQTTServer/platformio.ini b/lib/IRremoteESP8266-2.6.5/examples/IRMQTTServer/platformio.ini new file mode 100644 index 000000000..2d82260ad --- /dev/null +++ b/lib/IRremoteESP8266-2.6.5/examples/IRMQTTServer/platformio.ini @@ -0,0 +1,62 @@ +[platformio] +src_dir = . + +[env] +lib_extra_dirs = ../../ +lib_ldf_mode = deep+ +lib_ignore = examples +build_flags = -DMQTT_MAX_PACKET_SIZE=768 + +[common] +lib_deps_builtin = +lib_deps_external = + PubSubClient + ArduinoJson@<6.0 + +[common_esp8266] +lib_deps_external = + ${common.lib_deps_builtin} + ${common.lib_deps_external} + WifiManager@>=0.14 + +[common_esp32] +lib_deps_external = + ${common.lib_deps_builtin} + ${common.lib_deps_external} + https://github.com/tzapu/WiFiManager.git#development + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 +lib_deps = ${common_esp8266.lib_deps_external} + +[env:d1_mini] +platform = espressif8266 +framework = arduino +board = d1_mini +lib_deps = ${common_esp8266.lib_deps_external} + +[env:d1_mini_no_mqtt] +platform = espressif8266 +framework = arduino +board = d1_mini +build_flags = + ${env.build_flags} + -DMQTT_ENABLE=false +lib_deps = ${common_esp8266.lib_deps_external} + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev +lib_deps = ${common_esp32.lib_deps_external} + +[env:esp01_1m] +platform = espressif8266 +framework = arduino +board = esp01_1m +build_flags = + ${env.build_flags} + -Wl,-Teagle.flash.1m64.ld +lib_deps = ${common_esp8266.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.6.0/examples/IRServer/IRServer.ino b/lib/IRremoteESP8266-2.6.5/examples/IRServer/IRServer.ino similarity index 80% rename from lib/IRremoteESP8266-2.6.0/examples/IRServer/IRServer.ino rename to lib/IRremoteESP8266-2.6.5/examples/IRServer/IRServer.ino index b378d3bd5..96fad95d2 100644 --- a/lib/IRremoteESP8266-2.6.0/examples/IRServer/IRServer.ino +++ b/lib/IRremoteESP8266-2.6.5/examples/IRServer/IRServer.ino @@ -1,15 +1,17 @@ /* * IRremoteESP8266: IRServer - demonstrates sending IR codes controlled from a webserver + * Version 0.3 May, 2019 * Version 0.2 June, 2017 * Copyright 2015 Mark Szabo + * Copyright 2019 David Conran * - * An IR LED circuit *MUST* be connected to the ESP8266 on a pin + * An IR LED circuit *MUST* be connected to the ESP on a pin * as specified by kIrLed below. * * TL;DR: The IR LED needs to be driven by a transistor for a good result. * * Suggested circuit: - * https://github.com/markszabo/IRremoteESP8266/wiki#ir-sending + * https://github.com/crankyoldgit/IRremoteESP8266/wiki#ir-sending * * Common mistakes & tips: * * Don't just connect the IR LED directly to the pin, it won't @@ -26,12 +28,17 @@ * * ESP-01 modules are tricky. We suggest you use a module with more GPIOs * for your first time. e.g. ESP-12 etc. */ -#ifndef UNIT_TEST #include -#endif +#if defined(ESP8266) #include #include #include +#endif // ESP8266 +#if defined(ESP32) +#include +#include +#include +#endif // ESP32 #include #include #include @@ -40,18 +47,27 @@ const char* kSsid = "....."; const char* kPassword = "....."; MDNSResponder mdns; +#if defined(ESP8266) ESP8266WebServer server(80); +#undef HOSTNAME +#define HOSTNAME "esp8266" +#endif // ESP8266 +#if defined(ESP32) +WebServer server(80); +#undef HOSTNAME +#define HOSTNAME "esp32" +#endif // ESP32 -const uint16_t kIrLed = 4; // ESP8266 GPIO pin to use. Recommended: 4 (D2). +const uint16_t kIrLed = 4; // ESP GPIO pin to use. Recommended: 4 (D2). IRsend irsend(kIrLed); // Set the GPIO to be used to sending the message. void handleRoot() { server.send(200, "text/html", "" \ - "ESP8266 Demo" \ + "" HOSTNAME " Demo" \ "" \ - "

Hello from ESP8266, you can send NEC encoded IR" \ + "

Hello from " HOSTNAME ", you can send NEC encoded IR" \ "signals from here!

" \ "

Send 0xFFE01F

" \ "

Send 0xFAB123

" \ @@ -104,7 +120,11 @@ void setup(void) { Serial.print("IP address: "); Serial.println(WiFi.localIP().toString()); - if (mdns.begin("esp8266", WiFi.localIP())) { +#if defined(ESP8266) + if (mdns.begin(HOSTNAME, WiFi.localIP())) { +#else // ESP8266 + if (mdns.begin(HOSTNAME)) { +#endif // ESP8266 Serial.println("MDNS responder started"); } diff --git a/lib/IRremoteESP8266-2.6.5/examples/IRServer/platformio.ini b/lib/IRremoteESP8266-2.6.5/examples/IRServer/platformio.ini new file mode 100644 index 000000000..1aba0afcc --- /dev/null +++ b/lib/IRremoteESP8266-2.6.5/examples/IRServer/platformio.ini @@ -0,0 +1,18 @@ +[platformio] +src_dir = . + +[env] +lib_extra_dirs = ../../ +lib_ldf_mode = deep+ +lib_ignore = examples +build_flags = + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev diff --git a/lib/IRremoteESP8266-2.6.0/examples/IRrecvDemo/IRrecvDemo.ino b/lib/IRremoteESP8266-2.6.5/examples/IRrecvDemo/IRrecvDemo.ino similarity index 94% rename from lib/IRremoteESP8266-2.6.0/examples/IRrecvDemo/IRrecvDemo.ino rename to lib/IRremoteESP8266-2.6.5/examples/IRrecvDemo/IRrecvDemo.ino index 09babe4fe..5fd03f4b4 100644 --- a/lib/IRremoteESP8266-2.6.0/examples/IRrecvDemo/IRrecvDemo.ino +++ b/lib/IRremoteESP8266-2.6.5/examples/IRrecvDemo/IRrecvDemo.ino @@ -6,7 +6,7 @@ * An IR detector/demodulator must be connected to the input kRecvPin. * Copyright 2009 Ken Shirriff, http://arcfn.com * Example circuit diagram: - * https://github.com/markszabo/IRremoteESP8266/wiki#ir-receiving + * https://github.com/crankyoldgit/IRremoteESP8266/wiki#ir-receiving * Changes: * Version 0.2 June, 2017 * Changed GPIO pin to the same as other examples. @@ -16,9 +16,7 @@ * Based on Ken Shirriff's IrsendDemo Version 0.1 July, 2009 */ -#ifndef UNIT_TEST #include -#endif #include #include #include diff --git a/lib/IRremoteESP8266-2.6.5/examples/IRrecvDemo/platformio.ini b/lib/IRremoteESP8266-2.6.5/examples/IRrecvDemo/platformio.ini new file mode 100644 index 000000000..1aba0afcc --- /dev/null +++ b/lib/IRremoteESP8266-2.6.5/examples/IRrecvDemo/platformio.ini @@ -0,0 +1,18 @@ +[platformio] +src_dir = . + +[env] +lib_extra_dirs = ../../ +lib_ldf_mode = deep+ +lib_ignore = examples +build_flags = + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev diff --git a/lib/IRremoteESP8266-2.6.0/examples/IRrecvDump/IRrecvDump.ino b/lib/IRremoteESP8266-2.6.5/examples/IRrecvDump/IRrecvDump.ino similarity index 99% rename from lib/IRremoteESP8266-2.6.0/examples/IRrecvDump/IRrecvDump.ino rename to lib/IRremoteESP8266-2.6.5/examples/IRrecvDump/IRrecvDump.ino index 34f10dc83..2a65cb624 100644 --- a/lib/IRremoteESP8266-2.6.0/examples/IRrecvDump/IRrecvDump.ino +++ b/lib/IRremoteESP8266-2.6.5/examples/IRrecvDump/IRrecvDump.ino @@ -22,9 +22,7 @@ * LG added by Darryl Smith (based on the JVC protocol) */ -#ifndef UNIT_TEST #include -#endif #include #include #include diff --git a/lib/IRremoteESP8266-2.6.5/examples/IRrecvDump/platformio.ini b/lib/IRremoteESP8266-2.6.5/examples/IRrecvDump/platformio.ini new file mode 100644 index 000000000..1aba0afcc --- /dev/null +++ b/lib/IRremoteESP8266-2.6.5/examples/IRrecvDump/platformio.ini @@ -0,0 +1,18 @@ +[platformio] +src_dir = . + +[env] +lib_extra_dirs = ../../ +lib_ldf_mode = deep+ +lib_ignore = examples +build_flags = + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev diff --git a/lib/IRremoteESP8266-2.6.0/examples/IRrecvDumpV2/IRrecvDumpV2.ino b/lib/IRremoteESP8266-2.6.5/examples/IRrecvDumpV2/IRrecvDumpV2.ino similarity index 51% rename from lib/IRremoteESP8266-2.6.0/examples/IRrecvDumpV2/IRrecvDumpV2.ino rename to lib/IRremoteESP8266-2.6.5/examples/IRrecvDumpV2/IRrecvDumpV2.ino index 2dee0597c..f69c14aed 100644 --- a/lib/IRremoteESP8266-2.6.0/examples/IRrecvDumpV2/IRrecvDumpV2.ino +++ b/lib/IRremoteESP8266-2.6.5/examples/IRrecvDumpV2/IRrecvDumpV2.ino @@ -3,12 +3,14 @@ * An IR detector/demodulator must be connected to the input kRecvPin. * * Copyright 2009 Ken Shirriff, http://arcfn.com - * Copyright 2017 David Conran + * Copyright 2017-2019 David Conran * * Example circuit diagram: - * https://github.com/markszabo/IRremoteESP8266/wiki#ir-receiving + * https://github.com/crankyoldgit/IRremoteESP8266/wiki#ir-receiving * * Changes: + * Version 0.5 June, 2019 + * - Move A/C description to IRac.cpp. * Version 0.4 July, 2018 * - Minor improvements and more A/C unit support. * Version 0.3 November, 2017 @@ -19,31 +21,11 @@ * Based on Ken Shirriff's IrsendDemo Version 0.1 July, 2009, */ -#ifndef UNIT_TEST #include -#endif #include #include +#include #include -// The following are only needed for extended decoding of A/C Messages -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - // ==================== start of TUNEABLE PARAMETERS ==================== // An IR detector/demodulator is connected to GPIO pin 14 @@ -114,171 +96,19 @@ const uint16_t kMinUnknownSize = 12; // Use turn on the save buffer feature for more complete capture coverage. IRrecv irrecv(kRecvPin, kCaptureBufferSize, kTimeout, true); - decode_results results; // Somewhere to store the results -// Display the human readable state of an A/C message if we can. -void dumpACInfo(decode_results *results) { - String description = ""; -#if DECODE_DAIKIN - if (results->decode_type == DAIKIN) { - IRDaikinESP ac(0); - ac.setRaw(results->state); - description = ac.toString(); - } -#endif // DECODE_DAIKIN -#if DECODE_DAIKIN2 - if (results->decode_type == DAIKIN2) { - IRDaikin2 ac(0); - ac.setRaw(results->state); - description = ac.toString(); - } -#endif // DECODE_DAIKIN2 -#if DECODE_DAIKIN216 - if (results->decode_type == DAIKIN216) { - IRDaikin216 ac(0); - ac.setRaw(results->state); - description = ac.toString(); - } -#endif // DECODE_DAIKIN216 -#if DECODE_FUJITSU_AC - if (results->decode_type == FUJITSU_AC) { - IRFujitsuAC ac(0); - ac.setRaw(results->state, results->bits / 8); - description = ac.toString(); - } -#endif // DECODE_FUJITSU_AC -#if DECODE_KELVINATOR - if (results->decode_type == KELVINATOR) { - IRKelvinatorAC ac(0); - ac.setRaw(results->state); - description = ac.toString(); - } -#endif // DECODE_KELVINATOR -#if DECODE_MITSUBISHI_AC - if (results->decode_type == MITSUBISHI_AC) { - IRMitsubishiAC ac(0); - ac.setRaw(results->state); - description = ac.toString(); - } -#endif // DECODE_MITSUBISHI_AC -#if DECODE_MITSUBISHIHEAVY - if (results->decode_type == MITSUBISHI_HEAVY_88) { - IRMitsubishiHeavy88Ac ac(0); - ac.setRaw(results->state); - description = ac.toString(); - } - if (results->decode_type == MITSUBISHI_HEAVY_152) { - IRMitsubishiHeavy152Ac ac(0); - ac.setRaw(results->state); - description = ac.toString(); - } -#endif // DECODE_MITSUBISHIHEAVY -#if DECODE_TOSHIBA_AC - if (results->decode_type == TOSHIBA_AC) { - IRToshibaAC ac(0); - ac.setRaw(results->state); - description = ac.toString(); - } -#endif // DECODE_TOSHIBA_AC -#if DECODE_GREE - if (results->decode_type == GREE) { - IRGreeAC ac(0); - ac.setRaw(results->state); - description = ac.toString(); - } -#endif // DECODE_GREE -#if DECODE_MIDEA - if (results->decode_type == MIDEA) { - IRMideaAC ac(0); - ac.setRaw(results->value); // Midea uses value instead of state. - description = ac.toString(); - } -#endif // DECODE_MIDEA -#if DECODE_HAIER_AC - if (results->decode_type == HAIER_AC) { - IRHaierAC ac(0); - ac.setRaw(results->state); - description = ac.toString(); - } -#endif // DECODE_HAIER_AC -#if DECODE_HAIER_AC_YRW02 - if (results->decode_type == HAIER_AC_YRW02) { - IRHaierACYRW02 ac(0); - ac.setRaw(results->state); - description = ac.toString(); - } -#endif // DECODE_HAIER_AC_YRW02 -#if DECODE_SAMSUNG_AC - if (results->decode_type == SAMSUNG_AC) { - IRSamsungAc ac(0); - ac.setRaw(results->state, results->bits / 8); - description = ac.toString(); - } -#endif // DECODE_SAMSUNG_AC -#if DECODE_COOLIX - if (results->decode_type == COOLIX) { - IRCoolixAC ac(0); - ac.setRaw(results->value); // Coolix uses value instead of state. - description = ac.toString(); - } -#endif // DECODE_COOLIX -#if DECODE_PANASONIC_AC - if (results->decode_type == PANASONIC_AC && - results->bits > kPanasonicAcShortBits) { - IRPanasonicAc ac(0); - ac.setRaw(results->state); - description = ac.toString(); - } -#endif // DECODE_PANASONIC_AC -#if DECODE_HITACHI_AC - if (results->decode_type == HITACHI_AC) { - IRHitachiAc ac(0); - ac.setRaw(results->state); - description = ac.toString(); - } -#endif // DECODE_HITACHI_AC -#if DECODE_WHIRLPOOL_AC - if (results->decode_type == WHIRLPOOL_AC) { - IRWhirlpoolAc ac(0); - ac.setRaw(results->state); - description = ac.toString(); - } -#endif // DECODE_WHIRLPOOL_AC -#if DECODE_VESTEL_AC - if (results->decode_type == VESTEL_AC) { - IRVestelAc ac(0); - ac.setRaw(results->value); // Like Coolix, use value instead of state. - description = ac.toString(); - } -#endif // DECODE_VESTEL_AC -#if DECODE_TECO - if (results->decode_type == TECO) { - IRTecoAc ac(0); - ac.setRaw(results->value); // Like Coolix, use value instead of state. - description = ac.toString(); - } -#endif // DECODE_TECO -#if DECODE_TCL112AC - if (results->decode_type == TCL112AC) { - IRTcl112Ac ac(0); - ac.setRaw(results->state); - description = ac.toString(); - } -#endif // DECODE_TCL112AC - // If we got a human-readable description of the message, display it. - if (description != "") Serial.println("Mesg Desc.: " + description); -} - -// The section of code run only once at start-up. +// This section of code runs only once at start-up. void setup() { +#if defined(ESP8266) Serial.begin(kBaudRate, SERIAL_8N1, SERIAL_TX_ONLY); +#else // ESP8266 + Serial.begin(kBaudRate, SERIAL_8N1); +#endif // ESP8266 while (!Serial) // Wait for the serial connection to be establised. delay(50); - Serial.println(); - Serial.print("IRrecvDumpV2 is now running and waiting for IR input on Pin "); - Serial.println(kRecvPin); - + Serial.printf("\nIRrecvDumpV2 is now running and waiting for IR input on Pin " + "%d\n", kRecvPin); #if DECODE_HASH // Ignore messages with less than minimum on or off pulses. irrecv.setUnknownThreshold(kMinUnknownSize); @@ -287,36 +117,33 @@ void setup() { } // The repeating section of the code -// void loop() { // Check if the IR code has been received. if (irrecv.decode(&results)) { // Display a crude timestamp. uint32_t now = millis(); Serial.printf("Timestamp : %06u.%03u\n", now / 1000, now % 1000); + // Check if we got an IR message tha was to big for our capture buffer. if (results.overflow) Serial.printf( "WARNING: IR code is too big for buffer (>= %d). " "This result shouldn't be trusted until this is resolved. " "Edit & increase kCaptureBufferSize.\n", kCaptureBufferSize); + // Display the library version the message was captured with. + Serial.println("Library : v" _IRREMOTEESP8266_VERSION_ "\n"); // Display the basic output of what we found. Serial.print(resultToHumanReadableBasic(&results)); - dumpACInfo(&results); // Display any extra A/C info if we have it. + // Display any extra A/C info if we have it. + String description = IRAcUtils::resultAcToString(&results); + if (description.length()) Serial.println("Mesg Desc.: " + description); yield(); // Feed the WDT as the text output can take a while to print. - - // Display the library version the message was captured with. - Serial.print("Library : v"); - Serial.println(_IRREMOTEESP8266_VERSION_); - Serial.println(); - // Output RAW timing info of the result. Serial.println(resultToTimingInfo(&results)); yield(); // Feed the WDT (again) - // Output the results as source code Serial.println(resultToSourceCode(&results)); - Serial.println(""); // Blank line between entries + Serial.println(); // Blank line between entries yield(); // Feed the WDT (again) } } diff --git a/lib/IRremoteESP8266-2.6.5/examples/IRrecvDumpV2/platformio.ini b/lib/IRremoteESP8266-2.6.5/examples/IRrecvDumpV2/platformio.ini new file mode 100644 index 000000000..1aba0afcc --- /dev/null +++ b/lib/IRremoteESP8266-2.6.5/examples/IRrecvDumpV2/platformio.ini @@ -0,0 +1,18 @@ +[platformio] +src_dir = . + +[env] +lib_extra_dirs = ../../ +lib_ldf_mode = deep+ +lib_ignore = examples +build_flags = + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev diff --git a/lib/IRremoteESP8266-2.6.0/examples/IRsendDemo/IRsendDemo.ino b/lib/IRremoteESP8266-2.6.5/examples/IRsendDemo/IRsendDemo.ino similarity index 94% rename from lib/IRremoteESP8266-2.6.0/examples/IRsendDemo/IRsendDemo.ino rename to lib/IRremoteESP8266-2.6.5/examples/IRsendDemo/IRsendDemo.ino index 19f118671..b9d995834 100644 --- a/lib/IRremoteESP8266-2.6.0/examples/IRsendDemo/IRsendDemo.ino +++ b/lib/IRremoteESP8266-2.6.5/examples/IRsendDemo/IRsendDemo.ino @@ -10,7 +10,7 @@ * TL;DR: The IR LED needs to be driven by a transistor for a good result. * * Suggested circuit: - * https://github.com/markszabo/IRremoteESP8266/wiki#ir-sending + * https://github.com/crankyoldgit/IRremoteESP8266/wiki#ir-sending * * Common mistakes & tips: * * Don't just connect the IR LED directly to the pin, it won't @@ -28,9 +28,7 @@ * for your first time. e.g. ESP-12 etc. */ -#ifndef UNIT_TEST #include -#endif #include #include @@ -53,7 +51,11 @@ uint8_t samsungState[kSamsungAcStateLength] = { void setup() { irsend.begin(); +#if ESP8266 Serial.begin(115200, SERIAL_8N1, SERIAL_TX_ONLY); +#else // ESP8266 + Serial.begin(115200, SERIAL_8N1); +#endif // ESP8266 } void loop() { diff --git a/lib/IRremoteESP8266-2.6.5/examples/IRsendDemo/platformio.ini b/lib/IRremoteESP8266-2.6.5/examples/IRsendDemo/platformio.ini new file mode 100644 index 000000000..1aba0afcc --- /dev/null +++ b/lib/IRremoteESP8266-2.6.5/examples/IRsendDemo/platformio.ini @@ -0,0 +1,18 @@ +[platformio] +src_dir = . + +[env] +lib_extra_dirs = ../../ +lib_ldf_mode = deep+ +lib_ignore = examples +build_flags = + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev diff --git a/lib/IRremoteESP8266-2.6.0/examples/IRsendProntoDemo/IRsendProntoDemo.ino b/lib/IRremoteESP8266-2.6.5/examples/IRsendProntoDemo/IRsendProntoDemo.ino similarity index 98% rename from lib/IRremoteESP8266-2.6.0/examples/IRsendProntoDemo/IRsendProntoDemo.ino rename to lib/IRremoteESP8266-2.6.5/examples/IRsendProntoDemo/IRsendProntoDemo.ino index 3bef2179e..09101c9dc 100644 --- a/lib/IRremoteESP8266-2.6.0/examples/IRsendProntoDemo/IRsendProntoDemo.ino +++ b/lib/IRremoteESP8266-2.6.5/examples/IRsendProntoDemo/IRsendProntoDemo.ino @@ -11,7 +11,7 @@ * TL;DR: The IR LED needs to be driven by a transistor for a good result. * * Suggested circuit: - * https://github.com/markszabo/IRremoteESP8266/wiki#ir-sending + * https://github.com/crankyoldgit/IRremoteESP8266/wiki#ir-sending * * Common mistakes & tips: * * Don't just connect the IR LED directly to the pin, it won't @@ -29,9 +29,7 @@ * for your first time. e.g. ESP-12 etc. */ -#ifndef UNIT_TEST #include -#endif #include #include diff --git a/lib/IRremoteESP8266-2.6.5/examples/IRsendProntoDemo/platformio.ini b/lib/IRremoteESP8266-2.6.5/examples/IRsendProntoDemo/platformio.ini new file mode 100644 index 000000000..1aba0afcc --- /dev/null +++ b/lib/IRremoteESP8266-2.6.5/examples/IRsendProntoDemo/platformio.ini @@ -0,0 +1,18 @@ +[platformio] +src_dir = . + +[env] +lib_extra_dirs = ../../ +lib_ldf_mode = deep+ +lib_ignore = examples +build_flags = + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev diff --git a/lib/IRremoteESP8266-2.6.0/examples/JVCPanasonicSendDemo/JVCPanasonicSendDemo.ino b/lib/IRremoteESP8266-2.6.5/examples/JVCPanasonicSendDemo/JVCPanasonicSendDemo.ino similarity index 96% rename from lib/IRremoteESP8266-2.6.0/examples/JVCPanasonicSendDemo/JVCPanasonicSendDemo.ino rename to lib/IRremoteESP8266-2.6.5/examples/JVCPanasonicSendDemo/JVCPanasonicSendDemo.ino index ee2422915..cce72c35d 100644 --- a/lib/IRremoteESP8266-2.6.0/examples/JVCPanasonicSendDemo/JVCPanasonicSendDemo.ino +++ b/lib/IRremoteESP8266-2.6.5/examples/JVCPanasonicSendDemo/JVCPanasonicSendDemo.ino @@ -10,7 +10,7 @@ * TL;DR: The IR LED needs to be driven by a transistor for a good result. * * Suggested circuit: - * https://github.com/markszabo/IRremoteESP8266/wiki#ir-sending + * https://github.com/crankyoldgit/IRremoteESP8266/wiki#ir-sending * * Common mistakes & tips: * * Don't just connect the IR LED directly to the pin, it won't @@ -28,9 +28,7 @@ * for your first time. e.g. ESP-12 etc. */ -#ifndef UNIT_TEST #include -#endif #include #include diff --git a/lib/IRremoteESP8266-2.6.5/examples/JVCPanasonicSendDemo/platformio.ini b/lib/IRremoteESP8266-2.6.5/examples/JVCPanasonicSendDemo/platformio.ini new file mode 100644 index 000000000..1aba0afcc --- /dev/null +++ b/lib/IRremoteESP8266-2.6.5/examples/JVCPanasonicSendDemo/platformio.ini @@ -0,0 +1,18 @@ +[platformio] +src_dir = . + +[env] +lib_extra_dirs = ../../ +lib_ldf_mode = deep+ +lib_ignore = examples +build_flags = + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev diff --git a/lib/IRremoteESP8266-2.6.0/examples/LGACSend/LGACSend.ino b/lib/IRremoteESP8266-2.6.5/examples/LGACSend/LGACSend.ino similarity index 100% rename from lib/IRremoteESP8266-2.6.0/examples/LGACSend/LGACSend.ino rename to lib/IRremoteESP8266-2.6.5/examples/LGACSend/LGACSend.ino diff --git a/lib/IRremoteESP8266-2.6.5/examples/LGACSend/platformio.ini b/lib/IRremoteESP8266-2.6.5/examples/LGACSend/platformio.ini new file mode 100644 index 000000000..1aba0afcc --- /dev/null +++ b/lib/IRremoteESP8266-2.6.5/examples/LGACSend/platformio.ini @@ -0,0 +1,18 @@ +[platformio] +src_dir = . + +[env] +lib_extra_dirs = ../../ +lib_ldf_mode = deep+ +lib_ignore = examples +build_flags = + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev diff --git a/lib/IRremoteESP8266-2.6.5/examples/SmartIRRepeater/SmartIRRepeater.ino b/lib/IRremoteESP8266-2.6.5/examples/SmartIRRepeater/SmartIRRepeater.ino new file mode 100644 index 000000000..8dd202382 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.5/examples/SmartIRRepeater/SmartIRRepeater.ino @@ -0,0 +1,143 @@ +/* + * IRremoteESP8266: SmartIRRepeater.ino - Record and playback IR codes. + * Copyright 2019 David Conran (crankyoldgit) + * + * This program will try to capture incoming IR messages and tries to + * intelligently replay them back. + * It uses the advanced detection features of the library, and the custom + * sending routines. Thus it will try to use the correct frequencies, + * duty cycles, and repeats as it thinks is required. + * Anything it doesn't understand, it will try to replay back as best it can, + * but at 38kHz. + * Note: + * That might NOT be the frequency of the incoming message, so some not + * recogised messages that are replayed may not work. The frequency & duty + * cycle of unknown incoming messages is lost at the point of the Hardware IR + * demodulator. The ESP can't see it. + * + * W A R N I N G + * This code is just for educational/example use only. No help will be given + * to you to make it do something else, or to make it work with some + * weird device or circuit, or to make it more usable or practical. + * If it works for you. Great. If not, Congratulations on changing/fixing it. + * + * An IR detector/demodulator must be connected to the input, kRecvPin. + * An IR LED circuit must be connected to the output, kIrLedPin. + * + * Example circuit diagrams (both are needed): + * https://github.com/crankyoldgit/IRremoteESP8266/wiki#ir-receiving + * https://github.com/crankyoldgit/IRremoteESP8266/wiki#ir-sending + * + * Common mistakes & tips: + * * Don't just connect the IR LED directly to the pin, it won't + * have enough current to drive the IR LED effectively. + * * Make sure you have the IR LED polarity correct. + * See: https://learn.sparkfun.com/tutorials/polarity/diode-and-led-polarity + * * Some digital camera/phones can be used to see if the IR LED is flashed. + * Replace the IR LED with a normal LED if you don't have a digital camera + * when debugging. + * * Avoid using the following pins unless you really know what you are doing: + * * Pin 0/D3: Can interfere with the boot/program mode & support circuits. + * * Pin 1/TX/TXD0: Any serial transmissions from the ESP will interfere. + * * Pin 3/RX/RXD0: Any serial transmissions to the ESP will interfere. + * * ESP-01 modules are tricky. We suggest you use a module with more GPIOs + * for your first time. e.g. ESP-12 etc. + * + * Changes: + * Version 1.0: June, 2019 + * - Initial version. + */ + +#include +#include +#include +#include +#include + +// ==================== start of TUNEABLE PARAMETERS ==================== + +// The GPIO an IR detector/demodulator is connected to. Recommended: 14 (D5) +const uint16_t kRecvPin = 14; + +// GPIO to use to control the IR LED circuit. Recommended: 4 (D2). +const uint16_t kIrLedPin = 4; + +// The Serial connection baud rate. +// NOTE: Make sure you set your Serial Monitor to the same speed. +const uint32_t kBaudRate = 115200; + +// As this program is a special purpose capture/resender, let's use a larger +// than expected buffer so we can handle very large IR messages. +const uint16_t kCaptureBufferSize = 1024; // 1024 == ~511 bits + +// kTimeout is the Nr. of milli-Seconds of no-more-data before we consider a +// message ended. +const uint8_t kTimeout = 50; // Milli-Seconds + +// kFrequency is the modulation frequency all UNKNOWN messages will be sent at. +const uint16_t kFrequency = 38000; // in Hz. e.g. 38kHz. + +// ==================== end of TUNEABLE PARAMETERS ==================== + +// The IR transmitter. +IRsend irsend(kIrLedPin); +// The IR receiver. +IRrecv irrecv(kRecvPin, kCaptureBufferSize, kTimeout, false); +// Somewhere to store the captured message. +decode_results results; + +// This section of code runs only once at start-up. +void setup() { + irrecv.enableIRIn(); // Start up the IR receiver. + irsend.begin(); // Start up the IR sender. + + Serial.begin(kBaudRate, SERIAL_8N1); + while (!Serial) // Wait for the serial connection to be establised. + delay(50); + Serial.println(); + + Serial.print("SmartIRRepeater is now running and waiting for IR input " + "on Pin "); + Serial.println(kRecvPin); + Serial.print("and will retransmit it on Pin "); + Serial.println(kIrLedPin); +} + +// The repeating section of the code +void loop() { + // Check if an IR message has been received. + if (irrecv.decode(&results)) { // We have captured something. + // The capture has stopped at this point. + decode_type_t protocol = results.decode_type; + uint16_t size = results.bits; + bool success = true; + // Is it a protocol we don't understand? + if (protocol == decode_type_t::UNKNOWN) { // Yes. + // Convert the results into an array suitable for sendRaw(). + // resultToRawArray() allocates the memory we need for the array. + uint16_t *raw_array = resultToRawArray(&results); + // Find out how many elements are in the array. + size = getCorrectedRawLength(&results); + // Send it out via the IR LED circuit. + irsend.sendRaw(raw_array, size, kFrequency); + // Deallocate the memory allocated by resultToRawArray(). + delete [] raw_array; + } else if (hasACState(protocol)) { // Does the message require a state[]? + // It does, so send with bytes instead. + success = irsend.send(protocol, results.state, size / 8); + } else { // Anything else must be a simple message protocol. ie. <= 64 bits + success = irsend.send(protocol, results.value, size); + } + // Resume capturing IR messages. It was not restarted until after we sent + // the message so we didn't capture our own message. + irrecv.resume(); + + // Display a crude timestamp & notification. + uint32_t now = millis(); + Serial.printf( + "%06u.%03u: A %d-bit %s message was %ssuccessfully retransmitted.\n", + now / 1000, now % 1000, size, typeToString(protocol).c_str(), + success ? "" : "un"); + } + yield(); // Or delay(milliseconds); This ensures the ESP doesn't WDT reset. +} diff --git a/lib/IRremoteESP8266-2.6.5/examples/SmartIRRepeater/platformio.ini b/lib/IRremoteESP8266-2.6.5/examples/SmartIRRepeater/platformio.ini new file mode 100644 index 000000000..1aba0afcc --- /dev/null +++ b/lib/IRremoteESP8266-2.6.5/examples/SmartIRRepeater/platformio.ini @@ -0,0 +1,18 @@ +[platformio] +src_dir = . + +[env] +lib_extra_dirs = ../../ +lib_ldf_mode = deep+ +lib_ignore = examples +build_flags = + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev diff --git a/lib/IRremoteESP8266-2.6.0/examples/TurnOnArgoAC/TurnOnArgoAC.ino b/lib/IRremoteESP8266-2.6.5/examples/TurnOnArgoAC/TurnOnArgoAC.ino similarity index 93% rename from lib/IRremoteESP8266-2.6.0/examples/TurnOnArgoAC/TurnOnArgoAC.ino rename to lib/IRremoteESP8266-2.6.5/examples/TurnOnArgoAC/TurnOnArgoAC.ino index 3993d1151..9a5457d0c 100644 --- a/lib/IRremoteESP8266-2.6.0/examples/TurnOnArgoAC/TurnOnArgoAC.ino +++ b/lib/IRremoteESP8266-2.6.5/examples/TurnOnArgoAC/TurnOnArgoAC.ino @@ -5,7 +5,7 @@ * TL;DR: The IR LED needs to be driven by a transistor for a good result. * * Suggested circuit: -* https://github.com/markszabo/IRremoteESP8266/wiki#ir-sending +* https://github.com/crankyoldgit/IRremoteESP8266/wiki#ir-sending * * Common mistakes & tips: * * Don't just connect the IR LED directly to the pin, it won't @@ -23,9 +23,7 @@ * for your first time. e.g. ESP-12 etc. */ -#ifndef UNIT_TEST #include -#endif #include #include #include @@ -44,7 +42,7 @@ void loop() { // Set up what we want to send. See ir_Argo.cpp for all the options. ac.setPower(true); ac.setFan(kArgoFan1); - ac.setCoolMode(kArgoCoolAuto); + ac.setMode(kArgoAuto); ac.setTemp(25); #if SEND_ARGO diff --git a/lib/IRremoteESP8266-2.6.5/examples/TurnOnArgoAC/platformio.ini b/lib/IRremoteESP8266-2.6.5/examples/TurnOnArgoAC/platformio.ini new file mode 100644 index 000000000..1aba0afcc --- /dev/null +++ b/lib/IRremoteESP8266-2.6.5/examples/TurnOnArgoAC/platformio.ini @@ -0,0 +1,18 @@ +[platformio] +src_dir = . + +[env] +lib_extra_dirs = ../../ +lib_ldf_mode = deep+ +lib_ignore = examples +build_flags = + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev diff --git a/lib/IRremoteESP8266-2.6.0/examples/TurnOnDaikinAC/TurnOnDaikinAC.ino b/lib/IRremoteESP8266-2.6.5/examples/TurnOnDaikinAC/TurnOnDaikinAC.ino similarity index 95% rename from lib/IRremoteESP8266-2.6.0/examples/TurnOnDaikinAC/TurnOnDaikinAC.ino rename to lib/IRremoteESP8266-2.6.5/examples/TurnOnDaikinAC/TurnOnDaikinAC.ino index b3ab757de..d9eb9005f 100644 --- a/lib/IRremoteESP8266-2.6.0/examples/TurnOnDaikinAC/TurnOnDaikinAC.ino +++ b/lib/IRremoteESP8266-2.6.5/examples/TurnOnDaikinAC/TurnOnDaikinAC.ino @@ -6,7 +6,7 @@ * TL;DR: The IR LED needs to be driven by a transistor for a good result. * * Suggested circuit: -* https://github.com/markszabo/IRremoteESP8266/wiki#ir-sending +* https://github.com/crankyoldgit/IRremoteESP8266/wiki#ir-sending * * Common mistakes & tips: * * Don't just connect the IR LED directly to the pin, it won't @@ -24,9 +24,7 @@ * for your first time. e.g. ESP-12 etc. */ -#ifndef UNIT_TEST #include -#endif #include #include #include diff --git a/lib/IRremoteESP8266-2.6.5/examples/TurnOnDaikinAC/platformio.ini b/lib/IRremoteESP8266-2.6.5/examples/TurnOnDaikinAC/platformio.ini new file mode 100644 index 000000000..1aba0afcc --- /dev/null +++ b/lib/IRremoteESP8266-2.6.5/examples/TurnOnDaikinAC/platformio.ini @@ -0,0 +1,18 @@ +[platformio] +src_dir = . + +[env] +lib_extra_dirs = ../../ +lib_ldf_mode = deep+ +lib_ignore = examples +build_flags = + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev diff --git a/lib/IRremoteESP8266-2.6.0/examples/TurnOnFujitsuAC/TurnOnFujitsuAC.ino b/lib/IRremoteESP8266-2.6.5/examples/TurnOnFujitsuAC/TurnOnFujitsuAC.ino similarity index 90% rename from lib/IRremoteESP8266-2.6.0/examples/TurnOnFujitsuAC/TurnOnFujitsuAC.ino rename to lib/IRremoteESP8266-2.6.5/examples/TurnOnFujitsuAC/TurnOnFujitsuAC.ino index 823a3f485..010d84cac 100644 --- a/lib/IRremoteESP8266-2.6.0/examples/TurnOnFujitsuAC/TurnOnFujitsuAC.ino +++ b/lib/IRremoteESP8266-2.6.5/examples/TurnOnFujitsuAC/TurnOnFujitsuAC.ino @@ -1,7 +1,5 @@ // Copyright 2017 Jonny Graham, 2018 David Conran -#ifndef UNIT_TEST #include -#endif #include #include #include @@ -30,11 +28,13 @@ void setup() { Serial.println("Default state of the remote."); printState(); Serial.println("Setting desired state for A/C."); - ac.setCmd(kFujitsuAcCmdTurnOn); - ac.setSwing(kFujitsuAcSwingBoth); + // See `fujitsu_ac_remote_model_t` in `ir_Fujitsu.h` for a list of models. + ac.setModel(ARRAH2E); + ac.setSwing(kFujitsuAcSwingOff); ac.setMode(kFujitsuAcModeCool); ac.setFanSpeed(kFujitsuAcFanHigh); ac.setTemp(24); // 24C + ac.setCmd(kFujitsuAcCmdTurnOn); } void loop() { diff --git a/lib/IRremoteESP8266-2.6.5/examples/TurnOnFujitsuAC/platformio.ini b/lib/IRremoteESP8266-2.6.5/examples/TurnOnFujitsuAC/platformio.ini new file mode 100644 index 000000000..1aba0afcc --- /dev/null +++ b/lib/IRremoteESP8266-2.6.5/examples/TurnOnFujitsuAC/platformio.ini @@ -0,0 +1,18 @@ +[platformio] +src_dir = . + +[env] +lib_extra_dirs = ../../ +lib_ldf_mode = deep+ +lib_ignore = examples +build_flags = + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev diff --git a/lib/IRremoteESP8266-2.6.0/examples/TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino b/lib/IRremoteESP8266-2.6.5/examples/TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino similarity index 96% rename from lib/IRremoteESP8266-2.6.0/examples/TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino rename to lib/IRremoteESP8266-2.6.5/examples/TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino index b9b700741..ebdb7536b 100644 --- a/lib/IRremoteESP8266-2.6.0/examples/TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino +++ b/lib/IRremoteESP8266-2.6.5/examples/TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino @@ -6,7 +6,7 @@ * TL;DR: The IR LED needs to be driven by a transistor for a good result. * * Suggested circuit: -* https://github.com/markszabo/IRremoteESP8266/wiki#ir-sending +* https://github.com/crankyoldgit/IRremoteESP8266/wiki#ir-sending * * Common mistakes & tips: * * Don't just connect the IR LED directly to the pin, it won't @@ -23,9 +23,7 @@ * * ESP-01 modules are tricky. We suggest you use a module with more GPIOs * for your first time. e.g. ESP-12 etc. */ -#ifndef UNIT_TEST #include -#endif #include #include #include diff --git a/lib/IRremoteESP8266-2.6.5/examples/TurnOnKelvinatorAC/platformio.ini b/lib/IRremoteESP8266-2.6.5/examples/TurnOnKelvinatorAC/platformio.ini new file mode 100644 index 000000000..1aba0afcc --- /dev/null +++ b/lib/IRremoteESP8266-2.6.5/examples/TurnOnKelvinatorAC/platformio.ini @@ -0,0 +1,18 @@ +[platformio] +src_dir = . + +[env] +lib_extra_dirs = ../../ +lib_ldf_mode = deep+ +lib_ignore = examples +build_flags = + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev diff --git a/lib/IRremoteESP8266-2.6.0/examples/TurnOnMitsubishiAC/TurnOnMitsubishiAC.ino b/lib/IRremoteESP8266-2.6.5/examples/TurnOnMitsubishiAC/TurnOnMitsubishiAC.ino similarity index 96% rename from lib/IRremoteESP8266-2.6.0/examples/TurnOnMitsubishiAC/TurnOnMitsubishiAC.ino rename to lib/IRremoteESP8266-2.6.5/examples/TurnOnMitsubishiAC/TurnOnMitsubishiAC.ino index e719af68e..49bbb89ca 100644 --- a/lib/IRremoteESP8266-2.6.0/examples/TurnOnMitsubishiAC/TurnOnMitsubishiAC.ino +++ b/lib/IRremoteESP8266-2.6.5/examples/TurnOnMitsubishiAC/TurnOnMitsubishiAC.ino @@ -6,7 +6,7 @@ * TL;DR: The IR LED needs to be driven by a transistor for a good result. * * Suggested circuit: -* https://github.com/markszabo/IRremoteESP8266/wiki#ir-sending +* https://github.com/crankyoldgit/IRremoteESP8266/wiki#ir-sending * * Common mistakes & tips: * * Don't just connect the IR LED directly to the pin, it won't @@ -23,9 +23,7 @@ * * ESP-01 modules are tricky. We suggest you use a module with more GPIOs * for your first time. e.g. ESP-12 etc. */ -#ifndef UNIT_TEST #include -#endif #include #include #include diff --git a/lib/IRremoteESP8266-2.6.5/examples/TurnOnMitsubishiAC/platformio.ini b/lib/IRremoteESP8266-2.6.5/examples/TurnOnMitsubishiAC/platformio.ini new file mode 100644 index 000000000..1aba0afcc --- /dev/null +++ b/lib/IRremoteESP8266-2.6.5/examples/TurnOnMitsubishiAC/platformio.ini @@ -0,0 +1,18 @@ +[platformio] +src_dir = . + +[env] +lib_extra_dirs = ../../ +lib_ldf_mode = deep+ +lib_ignore = examples +build_flags = + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev diff --git a/lib/IRremoteESP8266-2.6.0/examples/TurnOnMitsubishiHeavyAc/TurnOnMitsubishiHeavyAc.ino b/lib/IRremoteESP8266-2.6.5/examples/TurnOnMitsubishiHeavyAc/TurnOnMitsubishiHeavyAc.ino similarity index 96% rename from lib/IRremoteESP8266-2.6.0/examples/TurnOnMitsubishiHeavyAc/TurnOnMitsubishiHeavyAc.ino rename to lib/IRremoteESP8266-2.6.5/examples/TurnOnMitsubishiHeavyAc/TurnOnMitsubishiHeavyAc.ino index 2ad2d7bc3..dbece716f 100644 --- a/lib/IRremoteESP8266-2.6.0/examples/TurnOnMitsubishiHeavyAc/TurnOnMitsubishiHeavyAc.ino +++ b/lib/IRremoteESP8266-2.6.5/examples/TurnOnMitsubishiHeavyAc/TurnOnMitsubishiHeavyAc.ino @@ -6,7 +6,7 @@ * TL;DR: The IR LED needs to be driven by a transistor for a good result. * * Suggested circuit: -* https://github.com/markszabo/IRremoteESP8266/wiki#ir-sending +* https://github.com/crankyoldgit/IRremoteESP8266/wiki#ir-sending * * Common mistakes & tips: * * Don't just connect the IR LED directly to the pin, it won't @@ -23,9 +23,7 @@ * * ESP-01 modules are tricky. We suggest you use a module with more GPIOs * for your first time. e.g. ESP-12 etc. */ -#ifndef UNIT_TEST #include -#endif #include #include #include diff --git a/lib/IRremoteESP8266-2.6.5/examples/TurnOnMitsubishiHeavyAc/platformio.ini b/lib/IRremoteESP8266-2.6.5/examples/TurnOnMitsubishiHeavyAc/platformio.ini new file mode 100644 index 000000000..1aba0afcc --- /dev/null +++ b/lib/IRremoteESP8266-2.6.5/examples/TurnOnMitsubishiHeavyAc/platformio.ini @@ -0,0 +1,18 @@ +[platformio] +src_dir = . + +[env] +lib_extra_dirs = ../../ +lib_ldf_mode = deep+ +lib_ignore = examples +build_flags = + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev diff --git a/lib/IRremoteESP8266-2.6.0/examples/TurnOnPanasonicAC/TurnOnPanasonicAC.ino b/lib/IRremoteESP8266-2.6.5/examples/TurnOnPanasonicAC/TurnOnPanasonicAC.ino similarity index 96% rename from lib/IRremoteESP8266-2.6.0/examples/TurnOnPanasonicAC/TurnOnPanasonicAC.ino rename to lib/IRremoteESP8266-2.6.5/examples/TurnOnPanasonicAC/TurnOnPanasonicAC.ino index ea39ac5e2..b7e399f09 100644 --- a/lib/IRremoteESP8266-2.6.0/examples/TurnOnPanasonicAC/TurnOnPanasonicAC.ino +++ b/lib/IRremoteESP8266-2.6.5/examples/TurnOnPanasonicAC/TurnOnPanasonicAC.ino @@ -6,7 +6,7 @@ * TL;DR: The IR LED needs to be driven by a transistor for a good result. * * Suggested circuit: -* https://github.com/markszabo/IRremoteESP8266/wiki#ir-sending +* https://github.com/crankyoldgit/IRremoteESP8266/wiki#ir-sending * * Common mistakes & tips: * * Don't just connect the IR LED directly to the pin, it won't @@ -23,9 +23,7 @@ * * ESP-01 modules are tricky. We suggest you use a module with more GPIOs * for your first time. e.g. ESP-12 etc. */ -#ifndef UNIT_TEST #include -#endif #include #include #include diff --git a/lib/IRremoteESP8266-2.6.5/examples/TurnOnPanasonicAC/platformio.ini b/lib/IRremoteESP8266-2.6.5/examples/TurnOnPanasonicAC/platformio.ini new file mode 100644 index 000000000..1aba0afcc --- /dev/null +++ b/lib/IRremoteESP8266-2.6.5/examples/TurnOnPanasonicAC/platformio.ini @@ -0,0 +1,18 @@ +[platformio] +src_dir = . + +[env] +lib_extra_dirs = ../../ +lib_ldf_mode = deep+ +lib_ignore = examples +build_flags = + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev diff --git a/lib/IRremoteESP8266-2.6.0/examples/TurnOnToshibaAC/TurnOnToshibaAC.ino b/lib/IRremoteESP8266-2.6.5/examples/TurnOnToshibaAC/TurnOnToshibaAC.ino similarity index 96% rename from lib/IRremoteESP8266-2.6.0/examples/TurnOnToshibaAC/TurnOnToshibaAC.ino rename to lib/IRremoteESP8266-2.6.5/examples/TurnOnToshibaAC/TurnOnToshibaAC.ino index d78178098..a37a07e5c 100644 --- a/lib/IRremoteESP8266-2.6.0/examples/TurnOnToshibaAC/TurnOnToshibaAC.ino +++ b/lib/IRremoteESP8266-2.6.5/examples/TurnOnToshibaAC/TurnOnToshibaAC.ino @@ -6,7 +6,7 @@ * TL;DR: The IR LED needs to be driven by a transistor for a good result. * * Suggested circuit: -* https://github.com/markszabo/IRremoteESP8266/wiki#ir-sending +* https://github.com/crankyoldgit/IRremoteESP8266/wiki#ir-sending * * Common mistakes & tips: * * Don't just connect the IR LED directly to the pin, it won't @@ -23,9 +23,7 @@ * * ESP-01 modules are tricky. We suggest you use a module with more GPIOs * for your first time. e.g. ESP-12 etc. */ -#ifndef UNIT_TEST #include -#endif #include #include #include diff --git a/lib/IRremoteESP8266-2.6.5/examples/TurnOnToshibaAC/platformio.ini b/lib/IRremoteESP8266-2.6.5/examples/TurnOnToshibaAC/platformio.ini new file mode 100644 index 000000000..1aba0afcc --- /dev/null +++ b/lib/IRremoteESP8266-2.6.5/examples/TurnOnToshibaAC/platformio.ini @@ -0,0 +1,18 @@ +[platformio] +src_dir = . + +[env] +lib_extra_dirs = ../../ +lib_ldf_mode = deep+ +lib_ignore = examples +build_flags = + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev diff --git a/lib/IRremoteESP8266-2.6.0/examples/TurnOnTrotecAC/TurnOnTrotecAC.ino b/lib/IRremoteESP8266-2.6.5/examples/TurnOnTrotecAC/TurnOnTrotecAC.ino similarity index 95% rename from lib/IRremoteESP8266-2.6.0/examples/TurnOnTrotecAC/TurnOnTrotecAC.ino rename to lib/IRremoteESP8266-2.6.5/examples/TurnOnTrotecAC/TurnOnTrotecAC.ino index b7881eead..014272955 100644 --- a/lib/IRremoteESP8266-2.6.0/examples/TurnOnTrotecAC/TurnOnTrotecAC.ino +++ b/lib/IRremoteESP8266-2.6.5/examples/TurnOnTrotecAC/TurnOnTrotecAC.ino @@ -5,7 +5,7 @@ * TL;DR: The IR LED needs to be driven by a transistor for a good result. * * Suggested circuit: -* https://github.com/markszabo/IRremoteESP8266/wiki#ir-sending +* https://github.com/crankyoldgit/IRremoteESP8266/wiki#ir-sending * * Common mistakes & tips: * * Don't just connect the IR LED directly to the pin, it won't @@ -23,9 +23,7 @@ * for your first time. e.g. ESP-12 etc. */ -#ifndef UNIT_TEST #include -#endif #include #include #include diff --git a/lib/IRremoteESP8266-2.6.5/examples/TurnOnTrotecAC/platformio.ini b/lib/IRremoteESP8266-2.6.5/examples/TurnOnTrotecAC/platformio.ini new file mode 100644 index 000000000..1aba0afcc --- /dev/null +++ b/lib/IRremoteESP8266-2.6.5/examples/TurnOnTrotecAC/platformio.ini @@ -0,0 +1,18 @@ +[platformio] +src_dir = . + +[env] +lib_extra_dirs = ../../ +lib_ldf_mode = deep+ +lib_ignore = examples +build_flags = + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev diff --git a/lib/IRremoteESP8266-2.6.0/keywords.txt b/lib/IRremoteESP8266-2.6.5/keywords.txt similarity index 79% rename from lib/IRremoteESP8266-2.6.0/keywords.txt rename to lib/IRremoteESP8266-2.6.5/keywords.txt index a498c5d61..db2d398a1 100644 --- a/lib/IRremoteESP8266-2.6.0/keywords.txt +++ b/lib/IRremoteESP8266-2.6.5/keywords.txt @@ -20,12 +20,19 @@ # Datatypes & Classes (KEYWORD1) ####################################### +IRAmcorAc KEYWORD1 IRArgoAC KEYWORD1 IRCoolixAC KEYWORD1 +IRDaikin128 KEYWORD1 +IRDaikin152 KEYWORD1 +IRDaikin160 KEYWORD1 +IRDaikin176 KEYWORD1 IRDaikin2 KEYWORD1 IRDaikin216 KEYWORD1 IRDaikinESP KEYWORD1 +IRElectraAc KEYWORD1 IRFujitsuAC KEYWORD1 +IRGoodweatherAc KEYWORD1 IRGreeAC KEYWORD1 IRHaierAC KEYWORD1 IRHaierACYRW02 KEYWORD1 @@ -35,8 +42,10 @@ IRMideaAC KEYWORD1 IRMitsubishiAC KEYWORD1 IRMitsubishiHeavy152Ac KEYWORD1 IRMitsubishiHeavy88Ac KEYWORD1 +IRNeoclimaAc KEYWORD1 IRPanasonicAc KEYWORD1 IRSamsungAc KEYWORD1 +IRSharpAc KEYWORD1 IRTcl112Ac KEYWORD1 IRTecoAc KEYWORD1 IRToshibaAC KEYWORD1 @@ -49,60 +58,100 @@ IRsend KEYWORD1 IRtimer KEYWORD1 TimerMs KEYWORD1 decode_results KEYWORD1 -ir_params_t KEYWORD1 +decode_type_t KEYWORD1 +fanspeed_t KEYWORD1 +fujitsu_ac_remote_model_t KEYWORD1 +gree_ac_remote_model_t KEYWORD1 +irparams_t KEYWORD1 match_result_t KEYWORD1 +opmode_t KEYWORD1 +panasonic_ac_remote_model_t KEYWORD1 +state_t KEYWORD1 +swingh_t KEYWORD1 +swingv_t KEYWORD1 +whirlpool_ac_remote_model_t KEYWORD1 ####################################### # Methods and Functions (KEYWORD2) ####################################### _delayMicroseconds KEYWORD2 +_matchGeneric KEYWORD2 _setMode KEYWORD2 _setTemp KEYWORD2 +_validTolerance KEYWORD2 add KEYWORD2 +addBoolToString KEYWORD2 +addFanToString KEYWORD2 +addIntToString KEYWORD2 +addLabeledString KEYWORD2 +addModeToString KEYWORD2 +addTempToString KEYWORD2 +amcor KEYWORD2 argo KEYWORD2 +bcdToUint8 KEYWORD2 begin KEYWORD2 +boolToString KEYWORD2 buildFromState KEYWORD2 buildState KEYWORD2 calcBlockChecksum KEYWORD2 calcChecksum KEYWORD2 +calcFirstChecksum KEYWORD2 calcLGChecksum KEYWORD2 +calcSecondChecksum KEYWORD2 calcUSecPeriod KEYWORD2 calculateChecksum KEYWORD2 calibrate KEYWORD2 cancelOffTimer KEYWORD2 cancelOnTimer KEYWORD2 cancelTimers KEYWORD2 +celsiusToFahrenheit KEYWORD2 checkZjsSig KEYWORD2 checkZmsSig KEYWORD2 checksum KEYWORD2 clearOnTimerFlag KEYWORD2 clearSensorTemp KEYWORD2 clearSleepTimerFlag KEYWORD2 +cmpStates KEYWORD2 compare KEYWORD2 +convertFan KEYWORD2 +convertMode KEYWORD2 +convertSwingH KEYWORD2 +convertSwingV KEYWORD2 coolix KEYWORD2 copyIrParams KEYWORD2 countBits KEYWORD2 daikin KEYWORD2 +daikin128 KEYWORD2 +daikin160 KEYWORD2 +daikin176 KEYWORD2 daikin2 KEYWORD2 daikin216 KEYWORD2 decode KEYWORD2 decodeAiwaRCT501 KEYWORD2 +decodeAmcor KEYWORD2 +decodeArgo KEYWORD2 decodeCOOLIX KEYWORD2 decodeCarrierAC KEYWORD2 decodeDISH KEYWORD2 decodeDaikin KEYWORD2 +decodeDaikin128 KEYWORD2 +decodeDaikin152 KEYWORD2 +decodeDaikin160 KEYWORD2 +decodeDaikin176 KEYWORD2 decodeDaikin2 KEYWORD2 decodeDaikin216 KEYWORD2 decodeDenon KEYWORD2 decodeElectraAC KEYWORD2 decodeFujitsuAC KEYWORD2 decodeGICable KEYWORD2 +decodeGoodweather KEYWORD2 decodeGree KEYWORD2 decodeHaierAC KEYWORD2 decodeHaierACYRW02 KEYWORD2 decodeHash KEYWORD2 decodeHitachiAC KEYWORD2 +decodeInax KEYWORD2 decodeJVC KEYWORD2 decodeKelvinator KEYWORD2 decodeLG KEYWORD2 @@ -117,6 +166,7 @@ decodeMitsubishi2 KEYWORD2 decodeMitsubishiAC KEYWORD2 decodeMitsubishiHeavy KEYWORD2 decodeNEC KEYWORD2 +decodeNeoclima KEYWORD2 decodeNikai KEYWORD2 decodePanasonic KEYWORD2 decodePanasonicAC KEYWORD2 @@ -130,18 +180,23 @@ decodeSamsungAC KEYWORD2 decodeSanyo KEYWORD2 decodeSanyoLC7461 KEYWORD2 decodeSharp KEYWORD2 +decodeSharpAc KEYWORD2 decodeSony KEYWORD2 decodeTcl112Ac KEYWORD2 decodeTeco KEYWORD2 +decodeToState KEYWORD2 decodeToshibaAC KEYWORD2 +decodeTrotec KEYWORD2 decodeVestelAc KEYWORD2 decodeWhirlpoolAC KEYWORD2 decodeWhynter KEYWORD2 +defaultBits KEYWORD2 disableIRIn KEYWORD2 disableOffTimer KEYWORD2 disableOnTimer KEYWORD2 disableSleepTimer KEYWORD2 elapsed KEYWORD2 +electra KEYWORD2 enableIRIn KEYWORD2 enableIROut KEYWORD2 enableOffTimer KEYWORD2 @@ -162,11 +217,13 @@ encodeSanyoLC7461 KEYWORD2 encodeSharp KEYWORD2 encodeSony KEYWORD2 encodeTime KEYWORD2 -fanspeed_t KEYWORD2 +fahrenheitToCelsius KEYWORD2 +fanspeedToString KEYWORD2 fixChecksum KEYWORD2 fixup KEYWORD2 fujitsu KEYWORD2 get3D KEYWORD2 +get8CHeat KEYWORD2 getBeep KEYWORD2 getBufSize KEYWORD2 getButton KEYWORD2 @@ -175,9 +232,9 @@ getClock KEYWORD2 getCmd KEYWORD2 getComfort KEYWORD2 getCommand KEYWORD2 -getCoolMode KEYWORD2 getCorrectedRawLength KEYWORD2 getCurrTime KEYWORD2 +getCurrentDay KEYWORD2 getCurrentTime KEYWORD2 getEcono KEYWORD2 getEye KEYWORD2 @@ -186,14 +243,19 @@ getFan KEYWORD2 getFanSpeed KEYWORD2 getFilter KEYWORD2 getFlap KEYWORD2 +getFollow KEYWORD2 +getFresh KEYWORD2 getFreshAir KEYWORD2 getFreshAirHigh KEYWORD2 getHealth KEYWORD2 -getHeatMode KEYWORD2 +getHold KEYWORD2 +getHumid KEYWORD2 +getIFeel KEYWORD2 getIon KEYWORD2 getIonFilter KEYWORD2 getLed KEYWORD2 getLight KEYWORD2 +getLightToggle KEYWORD2 getMax KEYWORD2 getMode KEYWORD2 getMold KEYWORD2 @@ -205,6 +267,7 @@ getOffTimerEnabled KEYWORD2 getOnTime KEYWORD2 getOnTimer KEYWORD2 getOnTimerEnabled KEYWORD2 +getOutsideQuiet KEYWORD2 getPower KEYWORD2 getPowerToggle KEYWORD2 getPowerful KEYWORD2 @@ -212,6 +275,8 @@ getPurify KEYWORD2 getQuiet KEYWORD2 getRClevel KEYWORD2 getRaw KEYWORD2 +getRoomTemp KEYWORD2 +getSave KEYWORD2 getSensor KEYWORD2 getSensorTemp KEYWORD2 getSilent KEYWORD2 @@ -224,7 +289,10 @@ getStateLength KEYWORD2 getStopClock KEYWORD2 getSuper KEYWORD2 getSwing KEYWORD2 +getSwingH KEYWORD2 getSwingHorizontal KEYWORD2 +getSwingV KEYWORD2 +getSwingVToggle KEYWORD2 getSwingVertical KEYWORD2 getSwingVerticalAuto KEYWORD2 getSwingVerticalPosition KEYWORD2 @@ -233,11 +301,18 @@ getTempOffset KEYWORD2 getTempRaw KEYWORD2 getTime KEYWORD2 getTimer KEYWORD2 +getTimerEnabled KEYWORD2 +getTolerance KEYWORD2 getTurbo KEYWORD2 +getUseCelsius KEYWORD2 getVane KEYWORD2 +getWeeklyTimerEnable KEYWORD2 +getWiFi KEYWORD2 +getWideVane KEYWORD2 getXFan KEYWORD2 getZoneFollow KEYWORD2 getiFeel KEYWORD2 +goodweather KEYWORD2 gree KEYWORD2 haier KEYWORD2 haierYrwo2 KEYWORD2 @@ -251,6 +326,7 @@ isOnTimerActive KEYWORD2 isOnTimerEnabled KEYWORD2 isProtocolSupported KEYWORD2 isSpecialState KEYWORD2 +isSwingVToggle KEYWORD2 isTimeCommand KEYWORD2 isTimerActive KEYWORD2 isTimerEnabled KEYWORD2 @@ -260,24 +336,29 @@ ledOn KEYWORD2 mark KEYWORD2 match KEYWORD2 matchAtLeast KEYWORD2 +matchBytes KEYWORD2 matchData KEYWORD2 +matchGeneric KEYWORD2 matchMark KEYWORD2 matchSpace KEYWORD2 midea KEYWORD2 +minRepeats KEYWORD2 +minsToString KEYWORD2 mitsubishi KEYWORD2 mitsubishiHeavy152 KEYWORD2 mitsubishiHeavy88 KEYWORD2 -mode) KEYWORD2 +msToString KEYWORD2 +neoclima KEYWORD2 off KEYWORD2 on KEYWORD2 -opmode_t KEYWORD2 +opmodeToString KEYWORD2 panasonic KEYWORD2 -position) KEYWORD2 recoverSavedState KEYWORD2 -renderTime KEYWORD2 reset KEYWORD2 +resultAcToString KEYWORD2 resultToHexidecimal KEYWORD2 resultToHumanReadableBasic KEYWORD2 +resultToRawArray KEYWORD2 resultToSourceCode KEYWORD2 resultToTimingInfo KEYWORD2 resume KEYWORD2 @@ -286,11 +367,16 @@ samsung KEYWORD2 send KEYWORD2 sendAc KEYWORD2 sendAiwaRCT501 KEYWORD2 +sendAmcor KEYWORD2 sendArgo KEYWORD2 sendCOOLIX KEYWORD2 sendCarrierAC KEYWORD2 sendDISH KEYWORD2 sendDaikin KEYWORD2 +sendDaikin128 KEYWORD2 +sendDaikin152 KEYWORD2 +sendDaikin160 KEYWORD2 +sendDaikin176 KEYWORD2 sendDaikin2 KEYWORD2 sendDaikin216 KEYWORD2 sendData KEYWORD2 @@ -301,12 +387,14 @@ sendFujitsuAC KEYWORD2 sendGC KEYWORD2 sendGICable KEYWORD2 sendGeneric KEYWORD2 +sendGoodweather KEYWORD2 sendGree KEYWORD2 sendHaierAC KEYWORD2 sendHaierACYRW02 KEYWORD2 sendHitachiAC KEYWORD2 sendHitachiAC1 KEYWORD2 sendHitachiAC2 KEYWORD2 +sendInax KEYWORD2 sendJVC KEYWORD2 sendKelvinator KEYWORD2 sendLG KEYWORD2 @@ -323,6 +411,7 @@ sendMitsubishiAC KEYWORD2 sendMitsubishiHeavy152 KEYWORD2 sendMitsubishiHeavy88 KEYWORD2 sendNEC KEYWORD2 +sendNeoclima KEYWORD2 sendNikai KEYWORD2 sendOff KEYWORD2 sendOn KEYWORD2 @@ -340,6 +429,7 @@ sendSamsung36 KEYWORD2 sendSamsungAC KEYWORD2 sendSanyoLC7461 KEYWORD2 sendSharp KEYWORD2 +sendSharpAc KEYWORD2 sendSharpRaw KEYWORD2 sendSherwood KEYWORD2 sendSony KEYWORD2 @@ -352,6 +442,7 @@ sendWhirlpoolAC KEYWORD2 sendWhynter KEYWORD2 serialPrintUint64 KEYWORD2 set3D KEYWORD2 +set8CHeat KEYWORD2 setAuto KEYWORD2 setBeep KEYWORD2 setButton KEYWORD2 @@ -360,8 +451,8 @@ setClock KEYWORD2 setCmd KEYWORD2 setComfort KEYWORD2 setCommand KEYWORD2 -setCoolMode KEYWORD2 setCurrTime KEYWORD2 +setCurrentDay KEYWORD2 setCurrentTime KEYWORD2 setEcono KEYWORD2 setEye KEYWORD2 @@ -370,14 +461,19 @@ setFan KEYWORD2 setFanSpeed KEYWORD2 setFilter KEYWORD2 setFlap KEYWORD2 +setFollow KEYWORD2 +setFresh KEYWORD2 setFreshAir KEYWORD2 setFreshAirHigh KEYWORD2 setHealth KEYWORD2 -setHeatMode KEYWORD2 +setHold KEYWORD2 +setHumid KEYWORD2 +setIFeel KEYWORD2 setIon KEYWORD2 setIonFilter KEYWORD2 setLed KEYWORD2 setLight KEYWORD2 +setLightToggle KEYWORD2 setMax KEYWORD2 setMode KEYWORD2 setModel KEYWORD2 @@ -385,8 +481,11 @@ setMold KEYWORD2 setNight KEYWORD2 setOffTimer KEYWORD2 setOffTimerActive KEYWORD2 +setOffTimerEnabled KEYWORD2 setOnTimer KEYWORD2 setOnTimerActive KEYWORD2 +setOnTimerEnabled KEYWORD2 +setOutsideQuiet KEYWORD2 setPower KEYWORD2 setPowerToggle KEYWORD2 setPowerful KEYWORD2 @@ -394,6 +493,7 @@ setPurify KEYWORD2 setQuiet KEYWORD2 setRaw KEYWORD2 setRoomTemp KEYWORD2 +setSave KEYWORD2 setSensor KEYWORD2 setSensorTemp KEYWORD2 setSensorTempRaw KEYWORD2 @@ -404,42 +504,54 @@ setStartClock KEYWORD2 setStopClock KEYWORD2 setSuper KEYWORD2 setSwing KEYWORD2 +setSwingH KEYWORD2 setSwingHorizontal KEYWORD2 +setSwingV KEYWORD2 +setSwingVToggle KEYWORD2 setSwingVertical KEYWORD2 setTemp KEYWORD2 setTempRaw KEYWORD2 setTime KEYWORD2 setTimer KEYWORD2 setTimerActive KEYWORD2 +setTimerEnabled KEYWORD2 +setTolerance KEYWORD2 setTurbo KEYWORD2 setUnknownThreshold KEYWORD2 +setUseCelsius KEYWORD2 setVane KEYWORD2 +setWeeklyTimerEnable KEYWORD2 +setWiFi KEYWORD2 +setWideVane KEYWORD2 setXFan KEYWORD2 setZoneFollow KEYWORD2 setiFeel KEYWORD2 +sharp KEYWORD2 space KEYWORD2 -speed) KEYWORD2 stateReset KEYWORD2 stepHoriz KEYWORD2 stepVert KEYWORD2 strToBool KEYWORD2 +strToDecodeType KEYWORD2 strToModel KEYWORD2 sumBytes KEYWORD2 -swingh_t KEYWORD2 -swingv) KEYWORD2 -swingv_t KEYWORD2 +sumNibbles KEYWORD2 +swinghToString KEYWORD2 +swingvToString KEYWORD2 tcl112 KEYWORD2 teco KEYWORD2 ticksHigh KEYWORD2 ticksLow KEYWORD2 -timeToString KEYWORD2 toString KEYWORD2 toggleRC5 KEYWORD2 toggleRC6 KEYWORD2 +toggleSwingHoriz KEYWORD2 +toggleSwingVert KEYWORD2 toshiba KEYWORD2 trotec KEYWORD2 typeToString KEYWORD2 uint64ToString KEYWORD2 +uint8ToBcd KEYWORD2 updateSavedState KEYWORD2 validChecksum KEYWORD2 vestel KEYWORD2 @@ -450,9 +562,11 @@ xorBytes KEYWORD2 # Constants (LITERAL1) ####################################### +// LITERAL1 AIWA_RC_T501 LITERAL1 AIWA_RC_T501_BITS LITERAL1 ALLOW_DELAY_CALLS LITERAL1 +AMCOR LITERAL1 ARDB1 LITERAL1 ARGO LITERAL1 ARGO_COMMAND_LENGTH LITERAL1 @@ -477,12 +591,18 @@ ARGO_HEAT_BLINK LITERAL1 ARGO_HEAT_ON LITERAL1 ARGO_MAX_TEMP LITERAL1 ARGO_MIN_TEMP LITERAL1 +ARJW2 LITERAL1 ARRAH2E LITERAL1 +ARREB1E LITERAL1 CARRIER_AC LITERAL1 CARRIER_AC_BITS LITERAL1 COOLIX LITERAL1 COOLIX_BITS LITERAL1 DAIKIN LITERAL1 +DAIKIN128 LITERAL1 +DAIKIN152 LITERAL1 +DAIKIN160 LITERAL1 +DAIKIN176 LITERAL1 DAIKIN2 LITERAL1 DAIKIN216 LITERAL1 DAIKIN_AUTO LITERAL1 @@ -499,10 +619,15 @@ DAIKIN_MAX_TEMP LITERAL1 DAIKIN_MIN_TEMP LITERAL1 DECODE_AC LITERAL1 DECODE_AIWA_RC_T501 LITERAL1 +DECODE_AMCOR LITERAL1 DECODE_ARGO LITERAL1 DECODE_CARRIER_AC LITERAL1 DECODE_COOLIX LITERAL1 DECODE_DAIKIN LITERAL1 +DECODE_DAIKIN128 LITERAL1 +DECODE_DAIKIN152 LITERAL1 +DECODE_DAIKIN160 LITERAL1 +DECODE_DAIKIN176 LITERAL1 DECODE_DAIKIN2 LITERAL1 DECODE_DAIKIN216 LITERAL1 DECODE_DENON LITERAL1 @@ -511,6 +636,7 @@ DECODE_ELECTRA_AC LITERAL1 DECODE_FUJITSU_AC LITERAL1 DECODE_GICABLE LITERAL1 DECODE_GLOBALCACHE LITERAL1 +DECODE_GOODWEATHER LITERAL1 DECODE_GREE LITERAL1 DECODE_HAIER_AC LITERAL1 DECODE_HAIER_AC_YRW02 LITERAL1 @@ -518,6 +644,7 @@ DECODE_HASH LITERAL1 DECODE_HITACHI_AC LITERAL1 DECODE_HITACHI_AC1 LITERAL1 DECODE_HITACHI_AC2 LITERAL1 +DECODE_INAX LITERAL1 DECODE_JVC LITERAL1 DECODE_KELVINATOR LITERAL1 DECODE_LASERTAG LITERAL1 @@ -532,6 +659,7 @@ DECODE_MITSUBISHIHEAVY LITERAL1 DECODE_MITSUBISHI_AC LITERAL1 DECODE_MWM LITERAL1 DECODE_NEC LITERAL1 +DECODE_NEOCLIMA LITERAL1 DECODE_NIKAI LITERAL1 DECODE_PANASONIC LITERAL1 DECODE_PANASONIC_AC LITERAL1 @@ -545,6 +673,7 @@ DECODE_SAMSUNG36 LITERAL1 DECODE_SAMSUNG_AC LITERAL1 DECODE_SANYO LITERAL1 DECODE_SHARP LITERAL1 +DECODE_SHARP_AC LITERAL1 DECODE_SHERWOOD LITERAL1 DECODE_SONY LITERAL1 DECODE_TCL112AC LITERAL1 @@ -593,6 +722,7 @@ FUJITSU_AC_SWING_VERT LITERAL1 GICABLE LITERAL1 GICABLE_BITS LITERAL1 GLOBALCACHE LITERAL1 +GOODWEATHER LITERAL1 GREE LITERAL1 GREE_AUTO LITERAL1 GREE_COOL LITERAL1 @@ -682,6 +812,7 @@ HITACHI_AC2_STATE_LENGTH LITERAL1 HITACHI_AC_BITS LITERAL1 HITACHI_AC_STATE_LENGTH LITERAL1 ICACHE_RAM_ATTR LITERAL1 +INAX LITERAL1 JVC LITERAL1 JVC_BITS LITERAL1 KELVINATOR LITERAL1 @@ -749,6 +880,7 @@ MWM LITERAL1 NEC LITERAL1 NEC_BITS LITERAL1 NEC_LIKE LITERAL1 +NEOCLIMA LITERAL1 NIKAI LITERAL1 NIKAI_BITS LITERAL1 ONCE LITERAL1 @@ -777,10 +909,15 @@ SANYO_LC7461 LITERAL1 SANYO_LC7461_BITS LITERAL1 SANYO_SA8650B_BITS LITERAL1 SEND_AIWA_RC_T501 LITERAL1 +SEND_AMCOR LITERAL1 SEND_ARGO LITERAL1 SEND_CARRIER_AC LITERAL1 SEND_COOLIX LITERAL1 SEND_DAIKIN LITERAL1 +SEND_DAIKIN128 LITERAL1 +SEND_DAIKIN152 LITERAL1 +SEND_DAIKIN160 LITERAL1 +SEND_DAIKIN176 LITERAL1 SEND_DAIKIN2 LITERAL1 SEND_DAIKIN216 LITERAL1 SEND_DENON LITERAL1 @@ -789,12 +926,14 @@ SEND_ELECTRA_AC LITERAL1 SEND_FUJITSU_AC LITERAL1 SEND_GICABLE LITERAL1 SEND_GLOBALCACHE LITERAL1 +SEND_GOODWEATHER LITERAL1 SEND_GREE LITERAL1 SEND_HAIER_AC LITERAL1 SEND_HAIER_AC_YRW02 LITERAL1 SEND_HITACHI_AC LITERAL1 SEND_HITACHI_AC1 LITERAL1 SEND_HITACHI_AC2 LITERAL1 +SEND_INAX LITERAL1 SEND_JVC LITERAL1 SEND_KELVINATOR LITERAL1 SEND_LASERTAG LITERAL1 @@ -809,6 +948,7 @@ SEND_MITSUBISHIHEAVY LITERAL1 SEND_MITSUBISHI_AC LITERAL1 SEND_MWM LITERAL1 SEND_NEC LITERAL1 +SEND_NEOCLIMA LITERAL1 SEND_NIKAI LITERAL1 SEND_PANASONIC LITERAL1 SEND_PANASONIC_AC LITERAL1 @@ -823,6 +963,7 @@ SEND_SAMSUNG36 LITERAL1 SEND_SAMSUNG_AC LITERAL1 SEND_SANYO LITERAL1 SEND_SHARP LITERAL1 +SEND_SHARP_AC LITERAL1 SEND_SHERWOOD LITERAL1 SEND_SONY LITERAL1 SEND_TCL112AC LITERAL1 @@ -833,6 +974,7 @@ SEND_VESTEL_AC LITERAL1 SEND_WHIRLPOOL_AC LITERAL1 SEND_WHYNTER LITERAL1 SHARP LITERAL1 +SHARP_AC LITERAL1 SHARP_BITS LITERAL1 SHERWOOD LITERAL1 SHERWOOD_BITS LITERAL1 @@ -868,26 +1010,66 @@ TROTEC_MAX_TIMER LITERAL1 TROTEC_MIN_TEMP LITERAL1 UNKNOWN LITERAL1 UNUSED LITERAL1 +USE_IRAM_ATTR LITERAL1 VESTEL_AC LITERAL1 WHIRLPOOL_AC LITERAL1 WHYNTER LITERAL1 WHYNTER_BITS LITERAL1 +YAW1F LITERAL1 +YBOFB LITERAL1 kAiwaRcT501Bits LITERAL1 kAiwaRcT501MinRepeats LITERAL1 kAiwaRcT501PostBits LITERAL1 kAiwaRcT501PostData LITERAL1 kAiwaRcT501PreBits LITERAL1 kAiwaRcT501PreData LITERAL1 +kAmcorAuto LITERAL1 +kAmcorBits LITERAL1 +kAmcorChecksumByte LITERAL1 +kAmcorCool LITERAL1 +kAmcorDefaultRepeat LITERAL1 +kAmcorDry LITERAL1 +kAmcorFan LITERAL1 +kAmcorFanAuto LITERAL1 +kAmcorFanMask LITERAL1 +kAmcorFanMax LITERAL1 +kAmcorFanMed LITERAL1 +kAmcorFanMin LITERAL1 +kAmcorFooterMark LITERAL1 +kAmcorGap LITERAL1 +kAmcorHdrMark LITERAL1 +kAmcorHdrSpace LITERAL1 +kAmcorHeat LITERAL1 +kAmcorMaxMask LITERAL1 +kAmcorMaxTemp LITERAL1 +kAmcorMinTemp LITERAL1 +kAmcorModeFanByte LITERAL1 +kAmcorModeMask LITERAL1 +kAmcorOneMark LITERAL1 +kAmcorOneSpace LITERAL1 +kAmcorPowerByte LITERAL1 +kAmcorPowerMask LITERAL1 +kAmcorPowerOff LITERAL1 +kAmcorPowerOn LITERAL1 +kAmcorSpecialByte LITERAL1 +kAmcorStateLength LITERAL1 +kAmcorTempByte LITERAL1 +kAmcorTempMask LITERAL1 +kAmcorTolerance LITERAL1 +kAmcorVentMask LITERAL1 +kAmcorZeroMark LITERAL1 +kAmcorZeroSpace LITERAL1 +kArgoAuto LITERAL1 kArgoBitMark LITERAL1 -kArgoCoolAuto LITERAL1 -kArgoCoolHum LITERAL1 -kArgoCoolOff LITERAL1 -kArgoCoolOn LITERAL1 +kArgoBits LITERAL1 +kArgoCool LITERAL1 kArgoDefaultRepeat LITERAL1 +kArgoDry LITERAL1 kArgoFan1 LITERAL1 kArgoFan2 LITERAL1 kArgoFan3 LITERAL1 kArgoFanAuto LITERAL1 +kArgoFanMask LITERAL1 kArgoFlap1 LITERAL1 kArgoFlap2 LITERAL1 kArgoFlap3 LITERAL1 @@ -896,15 +1078,29 @@ kArgoFlap5 LITERAL1 kArgoFlap6 LITERAL1 kArgoFlapAuto LITERAL1 kArgoFlapFull LITERAL1 +kArgoGap LITERAL1 kArgoHdrMark LITERAL1 kArgoHdrSpace LITERAL1 +kArgoHeat LITERAL1 kArgoHeatAuto LITERAL1 +kArgoHeatBit LITERAL1 kArgoHeatBlink LITERAL1 -kArgoHeatOn LITERAL1 +kArgoIFeelBit LITERAL1 +kArgoMaxBit LITERAL1 +kArgoMaxRoomTemp LITERAL1 kArgoMaxTemp LITERAL1 kArgoMinTemp LITERAL1 +kArgoModeMask LITERAL1 +kArgoNightBit LITERAL1 +kArgoOff LITERAL1 kArgoOneSpace LITERAL1 +kArgoPowerBit LITERAL1 +kArgoRoomTempHighMask LITERAL1 +kArgoRoomTempLowMask LITERAL1 kArgoStateLength LITERAL1 +kArgoTempHighMask LITERAL1 +kArgoTempLowMask LITERAL1 +kArgoTempOffset LITERAL1 kArgoZeroSpace LITERAL1 kAuto LITERAL1 kCarrierAcBitMark LITERAL1 @@ -965,11 +1161,128 @@ kCoolixUnknown LITERAL1 kCoolixZeroSpace LITERAL1 kCoolixZeroSpaceTicks LITERAL1 kCoolixZoneFollowMask LITERAL1 +kDaikin128Auto LITERAL1 +kDaikin128BitCeiling LITERAL1 +kDaikin128BitEcono LITERAL1 +kDaikin128BitHalfHour LITERAL1 +kDaikin128BitMark LITERAL1 +kDaikin128BitPowerToggle LITERAL1 +kDaikin128BitSleep LITERAL1 +kDaikin128BitSwing LITERAL1 +kDaikin128BitTimerEnabled LITERAL1 +kDaikin128BitWall LITERAL1 +kDaikin128Bits LITERAL1 +kDaikin128ByteClockHours LITERAL1 +kDaikin128ByteClockMins LITERAL1 +kDaikin128ByteEconoLight LITERAL1 +kDaikin128ByteModeFan LITERAL1 +kDaikin128ByteOffTimer LITERAL1 +kDaikin128ByteOnTimer LITERAL1 +kDaikin128BytePowerSwingSleep LITERAL1 +kDaikin128ByteTemp LITERAL1 +kDaikin128Cool LITERAL1 +kDaikin128DefaultRepeat LITERAL1 +kDaikin128Dry LITERAL1 +kDaikin128Fan LITERAL1 +kDaikin128FanAuto LITERAL1 +kDaikin128FanHigh LITERAL1 +kDaikin128FanLow LITERAL1 +kDaikin128FanMed LITERAL1 +kDaikin128FanPowerful LITERAL1 +kDaikin128FanQuiet LITERAL1 +kDaikin128FooterMark LITERAL1 +kDaikin128Freq LITERAL1 +kDaikin128Gap LITERAL1 +kDaikin128HdrMark LITERAL1 +kDaikin128HdrSpace LITERAL1 +kDaikin128Heat LITERAL1 +kDaikin128LeaderMark LITERAL1 +kDaikin128LeaderSpace LITERAL1 +kDaikin128MaskFan LITERAL1 +kDaikin128MaskHours LITERAL1 +kDaikin128MaskLight LITERAL1 +kDaikin128MaskMode LITERAL1 +kDaikin128MaxTemp LITERAL1 +kDaikin128MinTemp LITERAL1 +kDaikin128OneSpace LITERAL1 +kDaikin128SectionLength LITERAL1 +kDaikin128Sections LITERAL1 +kDaikin128StateLength LITERAL1 +kDaikin128ZeroSpace LITERAL1 +kDaikin152BitMark LITERAL1 +kDaikin152Bits LITERAL1 +kDaikin152DefaultRepeat LITERAL1 +kDaikin152Freq LITERAL1 +kDaikin152Gap LITERAL1 +kDaikin152HdrMark LITERAL1 +kDaikin152HdrSpace LITERAL1 +kDaikin152LeaderBits LITERAL1 +kDaikin152OneSpace LITERAL1 +kDaikin152StateLength LITERAL1 +kDaikin152ZeroSpace LITERAL1 +kDaikin160BitMark LITERAL1 +kDaikin160Bits LITERAL1 +kDaikin160ByteFan LITERAL1 +kDaikin160ByteMode LITERAL1 +kDaikin160BytePower LITERAL1 +kDaikin160ByteSwingV LITERAL1 +kDaikin160ByteTemp LITERAL1 +kDaikin160DefaultRepeat LITERAL1 +kDaikin160Freq LITERAL1 +kDaikin160Gap LITERAL1 +kDaikin160HdrMark LITERAL1 +kDaikin160HdrSpace LITERAL1 +kDaikin160MaskFan LITERAL1 +kDaikin160MaskMode LITERAL1 +kDaikin160MaskSwingV LITERAL1 +kDaikin160MaskTemp LITERAL1 +kDaikin160OneSpace LITERAL1 +kDaikin160Section1Length LITERAL1 +kDaikin160Section2Length LITERAL1 +kDaikin160Sections LITERAL1 +kDaikin160StateLength LITERAL1 +kDaikin160SwingVAuto LITERAL1 +kDaikin160SwingVHigh LITERAL1 +kDaikin160SwingVHighest LITERAL1 +kDaikin160SwingVLow LITERAL1 +kDaikin160SwingVLowest LITERAL1 +kDaikin160SwingVMiddle LITERAL1 +kDaikin160ZeroSpace LITERAL1 +kDaikin176BitMark LITERAL1 +kDaikin176Bits LITERAL1 +kDaikin176ByteFan LITERAL1 +kDaikin176ByteMode LITERAL1 +kDaikin176ByteModeButton LITERAL1 +kDaikin176BytePower LITERAL1 +kDaikin176ByteSwingH LITERAL1 +kDaikin176ByteTemp LITERAL1 +kDaikin176Cool LITERAL1 +kDaikin176DefaultRepeat LITERAL1 +kDaikin176DryFanTemp LITERAL1 +kDaikin176FanMax LITERAL1 +kDaikin176Freq LITERAL1 +kDaikin176Gap LITERAL1 +kDaikin176HdrMark LITERAL1 +kDaikin176HdrSpace LITERAL1 +kDaikin176MaskFan LITERAL1 +kDaikin176MaskMode LITERAL1 +kDaikin176MaskSwingH LITERAL1 +kDaikin176MaskTemp LITERAL1 +kDaikin176ModeButton LITERAL1 +kDaikin176OneSpace LITERAL1 +kDaikin176Section1Length LITERAL1 +kDaikin176Section2Length LITERAL1 +kDaikin176Sections LITERAL1 +kDaikin176StateLength LITERAL1 +kDaikin176SwingHAuto LITERAL1 +kDaikin176SwingHOff LITERAL1 +kDaikin176ZeroSpace LITERAL1 kDaikin216BitMark LITERAL1 kDaikin216Bits LITERAL1 kDaikin216ByteFan LITERAL1 kDaikin216ByteMode LITERAL1 kDaikin216BytePower LITERAL1 +kDaikin216BytePowerful LITERAL1 kDaikin216ByteSwingH LITERAL1 kDaikin216ByteSwingV LITERAL1 kDaikin216ByteTemp LITERAL1 @@ -1039,6 +1352,7 @@ kDaikinBitPower LITERAL1 kDaikinBitPowerful LITERAL1 kDaikinBitSensor LITERAL1 kDaikinBitSilent LITERAL1 +kDaikinBitWeeklyTimer LITERAL1 kDaikinBits LITERAL1 kDaikinBitsShort LITERAL1 kDaikinByteChecksum1 LITERAL1 @@ -1063,6 +1377,7 @@ kDaikinByteSensor LITERAL1 kDaikinByteSilent LITERAL1 kDaikinByteSwingH LITERAL1 kDaikinByteTemp LITERAL1 +kDaikinByteWeeklyTimer LITERAL1 kDaikinCool LITERAL1 kDaikinCurBit LITERAL1 kDaikinCurIndex LITERAL1 @@ -1071,6 +1386,7 @@ kDaikinDry LITERAL1 kDaikinFan LITERAL1 kDaikinFanAuto LITERAL1 kDaikinFanMax LITERAL1 +kDaikinFanMed LITERAL1 kDaikinFanMin LITERAL1 kDaikinFanQuiet LITERAL1 kDaikinFirstHeader64 LITERAL1 @@ -1095,7 +1411,9 @@ kDaikinStateLengthShort LITERAL1 kDaikinTolerance LITERAL1 kDaikinUnusedTime LITERAL1 kDaikinZeroSpace LITERAL1 +kDefaultESP32Timer LITERAL1 kDefaultMessageGap LITERAL1 +kDenon48Bits LITERAL1 kDenonBitMark LITERAL1 kDenonBitMarkTicks LITERAL1 kDenonBits LITERAL1 @@ -1131,13 +1449,32 @@ kDishZeroSpaceTicks LITERAL1 kDry LITERAL1 kDutyDefault LITERAL1 kDutyMax LITERAL1 +kElectraAcAuto LITERAL1 kElectraAcBitMark LITERAL1 kElectraAcBits LITERAL1 +kElectraAcCool LITERAL1 +kElectraAcDry LITERAL1 +kElectraAcFan LITERAL1 +kElectraAcFanAuto LITERAL1 +kElectraAcFanHigh LITERAL1 +kElectraAcFanLow LITERAL1 +kElectraAcFanMask LITERAL1 +kElectraAcFanMed LITERAL1 kElectraAcHdrMark LITERAL1 kElectraAcHdrSpace LITERAL1 +kElectraAcHeat LITERAL1 +kElectraAcMaxTemp LITERAL1 kElectraAcMessageGap LITERAL1 +kElectraAcMinRepeat LITERAL1 +kElectraAcMinTemp LITERAL1 +kElectraAcModeMask LITERAL1 +kElectraAcOffsetTemp LITERAL1 kElectraAcOneSpace LITERAL1 +kElectraAcPowerMask LITERAL1 kElectraAcStateLength LITERAL1 +kElectraAcSwingHMask LITERAL1 +kElectraAcSwingVMask LITERAL1 +kElectraAcTempMask LITERAL1 kElectraAcZeroSpace LITERAL1 kFan LITERAL1 kFnvBasis32 LITERAL1 @@ -1145,9 +1482,13 @@ kFnvPrime32 LITERAL1 kFooter LITERAL1 kFujitsuAcBitMark LITERAL1 kFujitsuAcBits LITERAL1 +kFujitsuAcCmdEcono LITERAL1 +kFujitsuAcCmdPowerful LITERAL1 kFujitsuAcCmdStayOn LITERAL1 kFujitsuAcCmdStepHoriz LITERAL1 kFujitsuAcCmdStepVert LITERAL1 +kFujitsuAcCmdToggleSwingHoriz LITERAL1 +kFujitsuAcCmdToggleSwingVert LITERAL1 kFujitsuAcCmdTurnOff LITERAL1 kFujitsuAcCmdTurnOn LITERAL1 kFujitsuAcFanAuto LITERAL1 @@ -1191,6 +1532,58 @@ kGlobalCacheMinUsec LITERAL1 kGlobalCacheRptIndex LITERAL1 kGlobalCacheRptStartIndex LITERAL1 kGlobalCacheStartIndex LITERAL1 +kGoodweatherAuto LITERAL1 +kGoodweatherBitCommand LITERAL1 +kGoodweatherBitFan LITERAL1 +kGoodweatherBitLight LITERAL1 +kGoodweatherBitMark LITERAL1 +kGoodweatherBitMode LITERAL1 +kGoodweatherBitPower LITERAL1 +kGoodweatherBitSleep LITERAL1 +kGoodweatherBitSwing LITERAL1 +kGoodweatherBitTemp LITERAL1 +kGoodweatherBitTurbo LITERAL1 +kGoodweatherBits LITERAL1 +kGoodweatherCmdAirFlow LITERAL1 +kGoodweatherCmdDownTemp LITERAL1 +kGoodweatherCmdFan LITERAL1 +kGoodweatherCmdHold LITERAL1 +kGoodweatherCmdLight LITERAL1 +kGoodweatherCmdMode LITERAL1 +kGoodweatherCmdPower LITERAL1 +kGoodweatherCmdSleep LITERAL1 +kGoodweatherCmdSwing LITERAL1 +kGoodweatherCmdTimer LITERAL1 +kGoodweatherCmdTurbo LITERAL1 +kGoodweatherCmdUpTemp LITERAL1 +kGoodweatherCommandMask LITERAL1 +kGoodweatherCool LITERAL1 +kGoodweatherDry LITERAL1 +kGoodweatherFan LITERAL1 +kGoodweatherFanAuto LITERAL1 +kGoodweatherFanHigh LITERAL1 +kGoodweatherFanLow LITERAL1 +kGoodweatherFanMask LITERAL1 +kGoodweatherFanMed LITERAL1 +kGoodweatherHdrMark LITERAL1 +kGoodweatherHdrSpace LITERAL1 +kGoodweatherHeat LITERAL1 +kGoodweatherLightMask LITERAL1 +kGoodweatherMinRepeat LITERAL1 +kGoodweatherModeMask LITERAL1 +kGoodweatherOneSpace LITERAL1 +kGoodweatherPowerMask LITERAL1 +kGoodweatherSleepMask LITERAL1 +kGoodweatherSwingFast LITERAL1 +kGoodweatherSwingMask LITERAL1 +kGoodweatherSwingOff LITERAL1 +kGoodweatherSwingSlow LITERAL1 +kGoodweatherTempMask LITERAL1 +kGoodweatherTempMax LITERAL1 +kGoodweatherTempMin LITERAL1 +kGoodweatherTurboMask LITERAL1 +kGoodweatherZeroSpace LITERAL1 +kGpioUnused LITERAL1 kGreeAuto LITERAL1 kGreeBitMark LITERAL1 kGreeBits LITERAL1 @@ -1203,10 +1596,12 @@ kGreeFan LITERAL1 kGreeFanAuto LITERAL1 kGreeFanMask LITERAL1 kGreeFanMax LITERAL1 +kGreeFanMed LITERAL1 kGreeFanMin LITERAL1 kGreeHdrMark LITERAL1 kGreeHdrSpace LITERAL1 kGreeHeat LITERAL1 +kGreeIFeelMask LITERAL1 kGreeLightMask LITERAL1 kGreeMaxTemp LITERAL1 kGreeMinTemp LITERAL1 @@ -1229,7 +1624,15 @@ kGreeSwingMiddleUp LITERAL1 kGreeSwingPosMask LITERAL1 kGreeSwingUp LITERAL1 kGreeSwingUpAuto LITERAL1 +kGreeTempMask LITERAL1 +kGreeTimer1Mask LITERAL1 +kGreeTimerEnabledBit LITERAL1 +kGreeTimerHalfHrBit LITERAL1 +kGreeTimerHoursMask LITERAL1 +kGreeTimerMax LITERAL1 +kGreeTimerTensHrMask LITERAL1 kGreeTurboMask LITERAL1 +kGreeWiFiMask LITERAL1 kGreeXfanMask LITERAL1 kGreeZeroSpace LITERAL1 kHaierACBits LITERAL1 @@ -1265,8 +1668,10 @@ kHaierAcMaxTemp LITERAL1 kHaierAcMaxTime LITERAL1 kHaierAcMinGap LITERAL1 kHaierAcMinTemp LITERAL1 +kHaierAcModeMask LITERAL1 kHaierAcOneSpace LITERAL1 kHaierAcPrefix LITERAL1 +kHaierAcSleepBit LITERAL1 kHaierAcSwingChg LITERAL1 kHaierAcSwingDown LITERAL1 kHaierAcSwingOff LITERAL1 @@ -1324,6 +1729,7 @@ kHitachiAcFan LITERAL1 kHitachiAcFanAuto LITERAL1 kHitachiAcFanHigh LITERAL1 kHitachiAcFanLow LITERAL1 +kHitachiAcFanMed LITERAL1 kHitachiAcHdrMark LITERAL1 kHitachiAcHdrSpace LITERAL1 kHitachiAcHeat LITERAL1 @@ -1334,6 +1740,15 @@ kHitachiAcOneSpace LITERAL1 kHitachiAcStateLength LITERAL1 kHitachiAcZeroSpace LITERAL1 kIdleState LITERAL1 +kInaxBitMark LITERAL1 +kInaxBits LITERAL1 +kInaxHdrMark LITERAL1 +kInaxHdrSpace LITERAL1 +kInaxMinGap LITERAL1 +kInaxMinRepeat LITERAL1 +kInaxOneSpace LITERAL1 +kInaxTick LITERAL1 +kInaxZeroSpace LITERAL1 kJvcBitMark LITERAL1 kJvcBitMarkTicks LITERAL1 kJvcBits LITERAL1 @@ -1367,6 +1782,7 @@ kKelvinatorFan LITERAL1 kKelvinatorFanAuto LITERAL1 kKelvinatorFanMask LITERAL1 kKelvinatorFanMax LITERAL1 +kKelvinatorFanMin LITERAL1 kKelvinatorFanOffset LITERAL1 kKelvinatorGapSpace LITERAL1 kKelvinatorGapSpaceTicks LITERAL1 @@ -1409,6 +1825,10 @@ kLasertagMinSamples LITERAL1 kLasertagTick LITERAL1 kLasertagTolerance LITERAL1 kLastDecodeType LITERAL1 +kLastFanspeedEnum LITERAL1 +kLastOpmodeEnum LITERAL1 +kLastSwinghEnum LITERAL1 +kLastSwingvEnum LITERAL1 kLeft LITERAL1 kLeftMax LITERAL1 kLegoPfBitMark LITERAL1 @@ -1480,6 +1900,7 @@ kMaxTimeoutMs LITERAL1 kMedium LITERAL1 kMiddle LITERAL1 kMideaACAuto LITERAL1 +kMideaACCelsiusBit LITERAL1 kMideaACChecksumMask LITERAL1 kMideaACCool LITERAL1 kMideaACDry LITERAL1 @@ -1499,6 +1920,7 @@ kMideaACPower LITERAL1 kMideaACSleep LITERAL1 kMideaACStateMask LITERAL1 kMideaACTempMask LITERAL1 +kMideaACToggleSwingV LITERAL1 kMideaBitMark LITERAL1 kMideaBitMarkTicks LITERAL1 kMideaBits LITERAL1 @@ -1531,6 +1953,7 @@ kMitsubishiAcCool LITERAL1 kMitsubishiAcDry LITERAL1 kMitsubishiAcFanAuto LITERAL1 kMitsubishiAcFanMax LITERAL1 +kMitsubishiAcFanQuiet LITERAL1 kMitsubishiAcFanRealMax LITERAL1 kMitsubishiAcFanSilent LITERAL1 kMitsubishiAcHdrMark LITERAL1 @@ -1548,6 +1971,7 @@ kMitsubishiAcStartTimer LITERAL1 kMitsubishiAcStopTimer LITERAL1 kMitsubishiAcVaneAuto LITERAL1 kMitsubishiAcVaneAutoMove LITERAL1 +kMitsubishiAcWideVaneAuto LITERAL1 kMitsubishiAcZeroSpace LITERAL1 kMitsubishiBitMark LITERAL1 kMitsubishiBitMarkTicks LITERAL1 @@ -1666,6 +2090,60 @@ kNecRptSpaceTicks LITERAL1 kNecTick LITERAL1 kNecZeroSpace LITERAL1 kNecZeroSpaceTicks LITERAL1 +kNeoclima8CHeatMask LITERAL1 +kNeoclimaAuto LITERAL1 +kNeoclimaBitMark LITERAL1 +kNeoclimaBits LITERAL1 +kNeoclimaButton8CHeat LITERAL1 +kNeoclimaButtonAirFlow LITERAL1 +kNeoclimaButtonEye LITERAL1 +kNeoclimaButtonFanSpeed LITERAL1 +kNeoclimaButtonFollow LITERAL1 +kNeoclimaButtonFresh LITERAL1 +kNeoclimaButtonHold LITERAL1 +kNeoclimaButtonIon LITERAL1 +kNeoclimaButtonLight LITERAL1 +kNeoclimaButtonMask LITERAL1 +kNeoclimaButtonMode LITERAL1 +kNeoclimaButtonPower LITERAL1 +kNeoclimaButtonSleep LITERAL1 +kNeoclimaButtonSwing LITERAL1 +kNeoclimaButtonTempDown LITERAL1 +kNeoclimaButtonTempUp LITERAL1 +kNeoclimaButtonTurbo LITERAL1 +kNeoclimaCool LITERAL1 +kNeoclimaDry LITERAL1 +kNeoclimaEyeMask LITERAL1 +kNeoclimaFan LITERAL1 +kNeoclimaFanAuto LITERAL1 +kNeoclimaFanHigh LITERAL1 +kNeoclimaFanLow LITERAL1 +kNeoclimaFanMask LITERAL1 +kNeoclimaFanMed LITERAL1 +kNeoclimaFollowMe LITERAL1 +kNeoclimaFreshMask LITERAL1 +kNeoclimaHdrMark LITERAL1 +kNeoclimaHdrSpace LITERAL1 +kNeoclimaHeat LITERAL1 +kNeoclimaHoldMask LITERAL1 +kNeoclimaIonMask LITERAL1 +kNeoclimaLightMask LITERAL1 +kNeoclimaMaxTemp LITERAL1 +kNeoclimaMinGap LITERAL1 +kNeoclimaMinRepeat LITERAL1 +kNeoclimaMinTemp LITERAL1 +kNeoclimaModeMask LITERAL1 +kNeoclimaOneSpace LITERAL1 +kNeoclimaPowerMask LITERAL1 +kNeoclimaSleepMask LITERAL1 +kNeoclimaStateLength LITERAL1 +kNeoclimaSwingHMask LITERAL1 +kNeoclimaSwingVMask LITERAL1 +kNeoclimaSwingVOff LITERAL1 +kNeoclimaSwingVOn LITERAL1 +kNeoclimaTempMask LITERAL1 +kNeoclimaTurboMask LITERAL1 +kNeoclimaZeroSpace LITERAL1 kNikaiBitMark LITERAL1 kNikaiBitMarkTicks LITERAL1 kNikaiBits LITERAL1 @@ -1692,6 +2170,7 @@ kPanasonicAcExcess LITERAL1 kPanasonicAcFan LITERAL1 kPanasonicAcFanAuto LITERAL1 kPanasonicAcFanMax LITERAL1 +kPanasonicAcFanMed LITERAL1 kPanasonicAcFanMin LITERAL1 kPanasonicAcFanModeTemp LITERAL1 kPanasonicAcFanOffset LITERAL1 @@ -1751,7 +2230,22 @@ kPanasonicUnknown LITERAL1 kPanasonicZeroSpace LITERAL1 kPanasonicZeroSpaceTicks LITERAL1 kPeriodOffset LITERAL1 +kPioneerBitMark LITERAL1 +kPioneerBitMarkTicks LITERAL1 kPioneerBits LITERAL1 +kPioneerHdrMark LITERAL1 +kPioneerHdrMarkTicks LITERAL1 +kPioneerHdrSpace LITERAL1 +kPioneerHdrSpaceTicks LITERAL1 +kPioneerMinCommandLength LITERAL1 +kPioneerMinCommandLengthTicks LITERAL1 +kPioneerMinGap LITERAL1 +kPioneerMinGapTicks LITERAL1 +kPioneerOneSpace LITERAL1 +kPioneerOneSpaceTicks LITERAL1 +kPioneerTick LITERAL1 +kPioneerZeroSpace LITERAL1 +kPioneerZeroSpaceTicks LITERAL1 kProntoDataOffset LITERAL1 kProntoFreqFactor LITERAL1 kProntoFreqOffset LITERAL1 @@ -1835,9 +2329,12 @@ kSamsungAcMinTemp LITERAL1 kSamsungAcModeMask LITERAL1 kSamsungAcOneSpace LITERAL1 kSamsungAcPowerMask1 LITERAL1 -kSamsungAcPowerMask2 LITERAL1 +kSamsungAcPowerMask6 LITERAL1 kSamsungAcPowerSection LITERAL1 -kSamsungAcQuietMask11 LITERAL1 +kSamsungAcPowerfulMask10 LITERAL1 +kSamsungAcPowerfulMask8 LITERAL1 +kSamsungAcQuietMask1 LITERAL1 +kSamsungAcQuietMask5 LITERAL1 kSamsungAcSectionGap LITERAL1 kSamsungAcSectionMark LITERAL1 kSamsungAcSectionSpace LITERAL1 @@ -1885,6 +2382,37 @@ kSanyoSa8650bHdrSpace LITERAL1 kSanyoSa8650bOneMark LITERAL1 kSanyoSa8650bRptLength LITERAL1 kSanyoSa8650bZeroMark LITERAL1 +kSharpAcAuto LITERAL1 +kSharpAcBitFanManual LITERAL1 +kSharpAcBitMark LITERAL1 +kSharpAcBitPower LITERAL1 +kSharpAcBitTempManual LITERAL1 +kSharpAcBits LITERAL1 +kSharpAcByteFan LITERAL1 +kSharpAcByteManual LITERAL1 +kSharpAcByteMode LITERAL1 +kSharpAcBytePower LITERAL1 +kSharpAcByteTemp LITERAL1 +kSharpAcCool LITERAL1 +kSharpAcDefaultRepeat LITERAL1 +kSharpAcDry LITERAL1 +kSharpAcFanAuto LITERAL1 +kSharpAcFanHigh LITERAL1 +kSharpAcFanMax LITERAL1 +kSharpAcFanMed LITERAL1 +kSharpAcFanMin LITERAL1 +kSharpAcGap LITERAL1 +kSharpAcHdrMark LITERAL1 +kSharpAcHdrSpace LITERAL1 +kSharpAcHeat LITERAL1 +kSharpAcMaskFan LITERAL1 +kSharpAcMaskMode LITERAL1 +kSharpAcMaskTemp LITERAL1 +kSharpAcMaxTemp LITERAL1 +kSharpAcMinTemp LITERAL1 +kSharpAcOneSpace LITERAL1 +kSharpAcStateLength LITERAL1 +kSharpAcZeroSpace LITERAL1 kSharpAddressBits LITERAL1 kSharpAddressMask LITERAL1 kSharpBitMark LITERAL1 @@ -1954,6 +2482,7 @@ kTcl112AcPowerMask LITERAL1 kTcl112AcStateLength LITERAL1 kTcl112AcTempMax LITERAL1 kTcl112AcTempMin LITERAL1 +kTcl112AcTolerance LITERAL1 kTcl112AcZeroSpace LITERAL1 kTecoAuto LITERAL1 kTecoBitMark LITERAL1 @@ -1971,12 +2500,15 @@ kTecoGap LITERAL1 kTecoHdrMark LITERAL1 kTecoHdrSpace LITERAL1 kTecoHeat LITERAL1 +kTecoHumid LITERAL1 +kTecoLight LITERAL1 kTecoMaxTemp LITERAL1 kTecoMinTemp LITERAL1 kTecoModeMask LITERAL1 kTecoOneSpace LITERAL1 kTecoPower LITERAL1 kTecoReset LITERAL1 +kTecoSave LITERAL1 kTecoSleep LITERAL1 kTecoSwing LITERAL1 kTecoTempMask LITERAL1 @@ -1996,6 +2528,8 @@ kToshibaAcCool LITERAL1 kToshibaAcDry LITERAL1 kToshibaAcFanAuto LITERAL1 kToshibaAcFanMax LITERAL1 +kToshibaAcFanMed LITERAL1 +kToshibaAcFanMin LITERAL1 kToshibaAcHdrMark LITERAL1 kToshibaAcHdrSpace LITERAL1 kToshibaAcHeat LITERAL1 @@ -2006,6 +2540,8 @@ kToshibaAcOneSpace LITERAL1 kToshibaAcPower LITERAL1 kToshibaAcZeroSpace LITERAL1 kTrotecAuto LITERAL1 +kTrotecBitMark LITERAL1 +kTrotecBits LITERAL1 kTrotecCool LITERAL1 kTrotecDefTemp LITERAL1 kTrotecDefaultRepeat LITERAL1 @@ -2023,15 +2559,14 @@ kTrotecIntro2 LITERAL1 kTrotecMaxTemp LITERAL1 kTrotecMaxTimer LITERAL1 kTrotecMinTemp LITERAL1 -kTrotecOneMark LITERAL1 kTrotecOneSpace LITERAL1 kTrotecPowerBit LITERAL1 kTrotecSleepBit LITERAL1 kTrotecStateLength LITERAL1 kTrotecTimerBit LITERAL1 -kTrotecZeroMark LITERAL1 kTrotecZeroSpace LITERAL1 kUnknownThreshold LITERAL1 +kUseDefTol LITERAL1 kVestelAcAuto LITERAL1 kVestelAcBitMark LITERAL1 kVestelAcBits LITERAL1 @@ -2153,3 +2688,4 @@ kWhynterOneSpaceTicks LITERAL1 kWhynterTick LITERAL1 kWhynterZeroSpace LITERAL1 kWhynterZeroSpaceTicks LITERAL1 +kWide LITERAL1 diff --git a/lib/IRremoteESP8266-2.6.0/library.json b/lib/IRremoteESP8266-2.6.5/library.json similarity index 79% rename from lib/IRremoteESP8266-2.6.0/library.json rename to lib/IRremoteESP8266-2.6.5/library.json index 95867de1d..c5136f18e 100644 --- a/lib/IRremoteESP8266-2.6.0/library.json +++ b/lib/IRremoteESP8266-2.6.5/library.json @@ -1,17 +1,18 @@ { "name": "IRremoteESP8266", - "version": "2.6.0", - "keywords": "infrared, ir, remote, esp8266", - "description": "Send and receive infrared signals with multiple protocols (ESP8266)", + "version": "2.6.5", + "keywords": "infrared, ir, remote, esp8266, esp32", + "description": "Send and receive infrared signals with multiple protocols (ESP8266/ESP32)", "repository": { "type": "git", - "url": "https://github.com/markszabo/IRremoteESP8266.git" + "url": "https://github.com/crankyoldgit/IRremoteESP8266.git" }, "authors": [ { - "name": "Ken Shirriff", - "email": "zetoslab@gmail.com" + "name": "David Conran", + "url": "https://plus.google.com/+davidconran", + "maintainer": true }, { "name": "Mark Szabo", @@ -24,9 +25,8 @@ "maintainer": true }, { - "name": "David Conran", - "url": "https://plus.google.com/+davidconran", - "maintainer": true + "name": "Ken Shirriff", + "email": "zetoslab@gmail.com" }, { "name": "Roi Dayan", @@ -40,5 +40,5 @@ } ], "frameworks": "arduino", - "platforms": "espressif8266" + "platforms": ["espressif8266", "espressif32"] } diff --git a/lib/IRremoteESP8266-2.6.0/library.properties b/lib/IRremoteESP8266-2.6.5/library.properties similarity index 54% rename from lib/IRremoteESP8266-2.6.0/library.properties rename to lib/IRremoteESP8266-2.6.5/library.properties index f122067c5..f83a80428 100644 --- a/lib/IRremoteESP8266-2.6.0/library.properties +++ b/lib/IRremoteESP8266-2.6.5/library.properties @@ -1,9 +1,9 @@ name=IRremoteESP8266 -version=2.6.0 -author=Sebastien Warin, Mark Szabo, Ken Shirriff, David Conran +version=2.6.5 +author=David Conran, Sebastien Warin, Mark Szabo, Ken Shirriff maintainer=Mark Szabo, David Conran, Sebastien Warin, Roi Dayan, Massimiliano Pinto -sentence=Send and receive infrared signals with multiple protocols (ESP8266) -paragraph=This library enables you to send and receive infra-red signals on an ESP8266. +sentence=Send and receive infrared signals with multiple protocols (ESP8266/ESP32) +paragraph=This library enables you to send and receive infra-red signals on an ESP8266 or an ESP32. category=Device Control -url=https://github.com/markszabo/IRremoteESP8266 -architectures=esp8266 +url=https://github.com/crankyoldgit/IRremoteESP8266 +architectures=esp8266,esp32 diff --git a/lib/IRremoteESP8266-2.6.0/pylintrc b/lib/IRremoteESP8266-2.6.5/pylintrc similarity index 100% rename from lib/IRremoteESP8266-2.6.0/pylintrc rename to lib/IRremoteESP8266-2.6.5/pylintrc diff --git a/lib/IRremoteESP8266-2.6.0/src/CPPLINT.cfg b/lib/IRremoteESP8266-2.6.5/src/CPPLINT.cfg similarity index 100% rename from lib/IRremoteESP8266-2.6.0/src/CPPLINT.cfg rename to lib/IRremoteESP8266-2.6.5/src/CPPLINT.cfg diff --git a/lib/IRremoteESP8266-2.6.5/src/IRac.cpp b/lib/IRremoteESP8266-2.6.5/src/IRac.cpp new file mode 100644 index 000000000..df668d836 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.5/src/IRac.cpp @@ -0,0 +1,2084 @@ +// Copyright 2019 David Conran + +// Provide a universal/standard interface for sending A/C nessages. +// It does not provide complete and maximum granular control but tries +// to off most common functionallity across all supported devices. + +#include "IRac.h" +#ifndef UNIT_TEST +#include +#endif +#include +#ifndef ARDUINO +#include +#endif +#include "IRsend.h" +#include "IRremoteESP8266.h" +#include "IRutils.h" +#include "ir_Amcor.h" +#include "ir_Argo.h" +#include "ir_Coolix.h" +#include "ir_Daikin.h" +#include "ir_Electra.h" +#include "ir_Fujitsu.h" +#include "ir_Haier.h" +#include "ir_Hitachi.h" +#include "ir_Kelvinator.h" +#include "ir_Midea.h" +#include "ir_Mitsubishi.h" +#include "ir_MitsubishiHeavy.h" +#include "ir_Neoclima.h" +#include "ir_Panasonic.h" +#include "ir_Samsung.h" +#include "ir_Sharp.h" +#include "ir_Tcl.h" +#include "ir_Teco.h" +#include "ir_Toshiba.h" +#include "ir_Trotec.h" +#include "ir_Vestel.h" +#include "ir_Whirlpool.h" + +IRac::IRac(const uint16_t pin, const bool inverted, const bool use_modulation) { + _pin = pin; + _inverted = inverted; + _modulation = use_modulation; +} + +// Is the given protocol supported by the IRac class? +bool IRac::isProtocolSupported(const decode_type_t protocol) { + switch (protocol) { +#if SEND_AMCOR + case decode_type_t::AMCOR: +#endif +#if SEND_AMCOR + case decode_type_t::ARGO: +#endif +#if SEND_COOLIX + case decode_type_t::COOLIX: +#endif +#if SEND_DAIKIN + case decode_type_t::DAIKIN: +#endif +#if SEND_DAIKIN128 + case decode_type_t::DAIKIN128: +#endif +#if SEND_DAIKIN160 + case decode_type_t::DAIKIN160: +#endif +#if SEND_DAIKIN176 + case decode_type_t::DAIKIN176: +#endif +#if SEND_DAIKIN2 + case decode_type_t::DAIKIN2: +#endif +#if SEND_DAIKIN216 + case decode_type_t::DAIKIN216: +#endif +#if SEND_ELECTRA_AC + case decode_type_t::ELECTRA_AC: +#endif +#if SEND_FUJITSU_AC + case decode_type_t::FUJITSU_AC: +#endif +#if SEND_GOODWEATHER + case decode_type_t::GOODWEATHER: +#endif +#if SEND_GREE + case decode_type_t::GREE: +#endif +#if SEND_HAIER_AC + case decode_type_t::HAIER_AC: +#endif +#if SEND_HAIER_AC_YRW02 + case decode_type_t::HAIER_AC_YRW02: +#endif +#if SEND_HITACHI_AC + case decode_type_t::HITACHI_AC: +#endif +#if SEND_KELVINATOR + case decode_type_t::KELVINATOR: +#endif +#if SEND_MIDEA + case decode_type_t::MIDEA: +#endif +#if SEND_MITSUBISHI_AC + case decode_type_t::MITSUBISHI_AC: +#endif +#if SEND_MITSUBISHIHEAVY + case decode_type_t::MITSUBISHI_HEAVY_88: + case decode_type_t::MITSUBISHI_HEAVY_152: +#endif +#if SEND_NEOCLIMA + case decode_type_t::NEOCLIMA: +#endif +#if SEND_PANASONIC_AC + case decode_type_t::PANASONIC_AC: +#endif +#if SEND_SAMSUNG_AC + case decode_type_t::SAMSUNG_AC: +#endif +#if SEND_SHARP_AC + case decode_type_t::SHARP_AC: +#endif +#if SEND_TCL112AC + case decode_type_t::TCL112AC: +#endif +#if SEND_TECO + case decode_type_t::TECO: +#endif +#if SEND_TOSHIBA_AC + case decode_type_t::TOSHIBA_AC: +#endif +#if SEND_TROTEC + case decode_type_t::TROTEC: +#endif +#if SEND_VESTEL_AC + case decode_type_t::VESTEL_AC: +#endif +#if SEND_WHIRLPOOL_AC + case decode_type_t::WHIRLPOOL_AC: +#endif + return true; + default: + return false; + } +} + +#if SEND_AMCOR +void IRac::amcor(IRAmcorAc *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan) { + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + // No Swing setting available. + // No Quiet setting available. + // No Light setting available. + // No Filter setting available. + // No Turbo setting available. + // No Economy setting available. + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + ac->send(); +} +#endif // SEND_AMCOR + +#if SEND_ARGO +void IRac::argo(IRArgoAC *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const bool turbo, const int16_t sleep) { + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setFlap(ac->convertSwingV(swingv)); + // No Quiet setting available. + // No Light setting available. + // No Filter setting available. + ac->setMax(turbo); + // No Economy setting available. + // No Clean setting available. + // No Beep setting available. + ac->setNight(sleep >= 0); // Convert to a boolean. + ac->send(); +} +#endif // SEND_ARGO + +#if SEND_COOLIX +void IRac::coolix(IRCoolixAC *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool turbo, const bool light, const bool clean, + const int16_t sleep) { + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + // No Filter setting available. + // No Beep setting available. + // No Clock setting available. + // No Econo setting available. + // No Quiet setting available. + if (swingv != stdAc::swingv_t::kOff || swingh != stdAc::swingh_t::kOff) { + // Swing has a special command that needs to be sent independently. + ac->setSwing(); + ac->send(); + } + if (turbo) { + // Turbo has a special command that needs to be sent independently. + ac->setTurbo(); + ac->send(); + } + if (sleep > 0) { + // Sleep has a special command that needs to be sent independently. + ac->setSleep(); + ac->send(); + } + if (light) { + // Light has a special command that needs to be sent independently. + ac->setLed(); + ac->send(); + } + if (clean) { + // Clean has a special command that needs to be sent independently. + ac->setClean(); + ac->send(); + } + // Power gets done last, as off has a special command. + ac->setPower(on); + ac->send(); +} +#endif // SEND_COOLIX + +#if SEND_DAIKIN +void IRac::daikin(IRDaikinESP *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const bool econo, + const bool clean) { + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical((int8_t)swingv >= 0); + ac->setSwingHorizontal((int8_t)swingh >= 0); + ac->setQuiet(quiet); + // No Light setting available. + // No Filter setting available. + ac->setPowerful(turbo); + ac->setEcono(econo); + ac->setMold(clean); + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_DAIKIN + +#if SEND_DAIKIN128 +void IRac::daikin128(IRDaikin128 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const bool quiet, const bool turbo, const bool light, + const bool econo, const int16_t sleep, const int16_t clock) { + ac->setPowerToggle(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical((int8_t)swingv >= 0); + // No Horizontal Swing setting avaliable. + ac->setQuiet(quiet); + ac->setLightToggle(light ? kDaikin128BitWall : 0); + // No Filter setting available. + ac->setPowerful(turbo); + ac->setEcono(econo); + // No Clean setting available. + // No Beep setting available. + ac->setSleep(sleep > 0); + if (clock >= 0) ac->setClock(clock); + ac->send(); +} +#endif // SEND_DAIKIN128 + +#if SEND_DAIKIN160 +void IRac::daikin160(IRDaikin160 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv) { + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical(ac->convertSwingV(swingv)); + ac->send(); +} +#endif // SEND_DAIKIN160 + +#if SEND_DAIKIN176 +void IRac::daikin176(IRDaikin176 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingh_t swingh) { + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingHorizontal(ac->convertSwingH(swingh)); + ac->send(); +} +#endif // SEND_DAIKIN176 + +#if SEND_DAIKIN2 +void IRac::daikin2(IRDaikin2 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const bool light, + const bool econo, const bool filter, const bool clean, + const bool beep, const int16_t sleep, const int16_t clock) { + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical(ac->convertSwingV(swingv)); + ac->setSwingHorizontal((int8_t)swingh >= 0); + ac->setQuiet(quiet); + ac->setLight(light); + ac->setPowerful(turbo); + ac->setEcono(econo); + ac->setPurify(filter); + ac->setMold(clean); + ac->setBeep(beep); + if (sleep > 0) ac->enableSleepTimer(sleep); + if (clock >= 0) ac->setCurrentTime(clock); + ac->send(); +} +#endif // SEND_DAIKIN2 + +#if SEND_DAIKIN216 +void IRac::daikin216(IRDaikin216 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet, const bool turbo) { + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical((int8_t)swingv >= 0); + ac->setSwingHorizontal((int8_t)swingh >= 0); + ac->setQuiet(quiet); + ac->setPowerful(turbo); + ac->send(); +} +#endif // SEND_DAIKIN216 + +#if SEND_ELECTRA_AC +void IRac::electra(IRElectraAc *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const stdAc::swingh_t swingh) { + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingV(swingv != stdAc::swingv_t::kOff); + ac->setSwingH(swingh != stdAc::swingh_t::kOff); + // No Quiet setting available. + // No Turbo setting available. + // No Light setting available. + // No Econo setting available. + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_ELECTRA_AC + +#if SEND_FUJITSU_AC +void IRac::fujitsu(IRFujitsuAC *ac, const fujitsu_ac_remote_model_t model, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const bool econo) { + ac->setModel(model); + if (on) { + // Do all special messages (except "Off") first, + // These need to be sent separately. + switch (ac->getModel()) { + // Some functions are only available on some models. + case fujitsu_ac_remote_model_t::ARREB1E: + if (turbo) { + ac->setCmd(kFujitsuAcCmdPowerful); + // Powerful is a separate command. + ac->send(); + } + if (econo) { + ac->setCmd(kFujitsuAcCmdEcono); + // Econo is a separate command. + ac->send(); + } + break; + default: + {}; + } + // Normal operation. + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFanSpeed(ac->convertFan(fan)); + uint8_t swing = kFujitsuAcSwingOff; + if (swingv > stdAc::swingv_t::kOff) swing |= kFujitsuAcSwingVert; + if (swingh > stdAc::swingh_t::kOff) swing |= kFujitsuAcSwingHoriz; + ac->setSwing(swing); + if (quiet) ac->setFanSpeed(kFujitsuAcFanQuiet); + // No Light setting available. + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + ac->on(); // Ref: Issue #860 + } else { + // Off is special case/message. We don't need to send other messages. + ac->off(); + } + ac->send(); +} +#endif // SEND_FUJITSU_AC + +#if SEND_GOODWEATHER +void IRac::goodweather(IRGoodweatherAc *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const bool turbo, const bool light, + const int16_t sleep) { + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwing(swingv == stdAc::swingv_t::kOff ? kGoodweatherSwingOff + : kGoodweatherSwingSlow); + ac->setTurbo(turbo); + ac->setLight(light); + // No Clean setting available. + ac->setSleep(sleep >= 0); // Sleep on this A/C is either on or off. + // No Horizontal Swing setting available. + // No Econo setting available. + // No Filter setting available. + // No Beep setting available. + // No Quiet setting available. + // No Clock setting available. + ac->setPower(on); + ac->send(); +} +#endif // SEND_GOODWEATHER + +#if SEND_GREE +void IRac::gree(IRGreeAC *ac, const gree_ac_remote_model_t model, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const bool turbo, const bool light, const bool clean, + const int16_t sleep) { + ac->setModel(model); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical(swingv == stdAc::swingv_t::kAuto, // Set auto flag. + ac->convertSwingV(swingv)); + ac->setLight(light); + ac->setTurbo(turbo); + ac->setXFan(clean); + ac->setSleep(sleep >= 0); // Sleep on this A/C is either on or off. + // No Horizontal Swing setting available. + // No Econo setting available. + // No Filter setting available. + // No Beep setting available. + // No Quiet setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_GREE + +#if SEND_HAIER_AC +void IRac::haier(IRHaierAC *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const bool filter, const int16_t sleep, const int16_t clock) { + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwing(ac->convertSwingV(swingv)); + // No Horizontal Swing setting available. + // No Quiet setting available. + // No Turbo setting available. + // No Light setting available. + ac->setHealth(filter); + // No Clean setting available. + // No Beep setting available. + ac->setSleep(sleep >= 0); // Sleep on this A/C is either on or off. + if (clock >=0) ac->setCurrTime(clock); + if (on) + ac->setCommand(kHaierAcCmdOn); + else + ac->setCommand(kHaierAcCmdOff); + ac->send(); +} +#endif // SEND_HAIER_AC + +#if SEND_HAIER_AC_YRW02 +void IRac::haierYrwo2(IRHaierACYRW02 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const bool turbo, + const bool filter, const int16_t sleep) { + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwing(ac->convertSwingV(swingv)); + // No Horizontal Swing setting available. + // No Quiet setting available. + ac->setTurbo(turbo); + // No Light setting available. + ac->setHealth(filter); + // No Clean setting available. + // No Beep setting available. + ac->setSleep(sleep >= 0); // Sleep on this A/C is either on or off. + ac->setPower(on); + ac->send(); +} +#endif // SEND_HAIER_AC_YRW02 + +#if SEND_HITACHI_AC +void IRac::hitachi(IRHitachiAc *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh) { + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical(swingv != stdAc::swingv_t::kOff); + ac->setSwingHorizontal(swingh != stdAc::swingh_t::kOff); + // No Quiet setting available. + // No Turbo setting available. + // No Light setting available. + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_HITACHI_AC + +#if SEND_KELVINATOR +void IRac::kelvinator(IRKelvinatorAC *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const bool light, + const bool filter, const bool clean) { + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan((uint8_t)fan); // No conversion needed. + ac->setSwingVertical((int8_t)swingv >= 0); + ac->setSwingHorizontal((int8_t)swingh >= 0); + ac->setQuiet(quiet); + ac->setTurbo(turbo); + ac->setLight(light); + ac->setIonFilter(filter); + ac->setXFan(clean); + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_KELVINATOR + +#if SEND_MIDEA +void IRac::midea(IRMideaAC *ac, + const bool on, const stdAc::opmode_t mode, const bool celsius, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const int16_t sleep) { + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setUseCelsius(celsius); + ac->setTemp(degrees, celsius); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVToggle(swingv != stdAc::swingv_t::kOff); + // No Horizontal swing setting available. + // No Quiet setting available. + // No Turbo setting available. + // No Light setting available. + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + ac->setSleep(sleep >= 0); // Sleep on this A/C is either on or off. + // No Clock setting available. + ac->send(); +} +#endif // SEND_MIDEA + +#if SEND_MITSUBISHI_AC +void IRac::mitsubishi(IRMitsubishiAC *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const stdAc::swingh_t swingh, + const bool quiet, const int16_t clock) { + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setVane(ac->convertSwingV(swingv)); + ac->setWideVane(ac->convertSwingH(swingh)); + if (quiet) ac->setFan(kMitsubishiAcFanSilent); + // No Turbo setting available. + // No Light setting available. + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + if (clock >= 0) ac->setClock(clock / 10); // Clock is in 10 min increments. + ac->send(); +} +#endif // SEND_MITSUBISHI_AC + +#if SEND_MITSUBISHIHEAVY +void IRac::mitsubishiHeavy88(IRMitsubishiHeavy88Ac *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const stdAc::swingh_t swingh, + const bool turbo, const bool econo, + const bool clean) { + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical(ac->convertSwingV(swingv)); + ac->setSwingHorizontal(ac->convertSwingH(swingh)); + // No Quiet setting available. + ac->setTurbo(turbo); + // No Light setting available. + ac->setEcono(econo); + // No Filter setting available. + ac->setClean(clean); + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + ac->send(); +} + +void IRac::mitsubishiHeavy152(IRMitsubishiHeavy152Ac *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, + const bool econo, const bool filter, + const bool clean, const int16_t sleep) { + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical(ac->convertSwingV(swingv)); + ac->setSwingHorizontal(ac->convertSwingH(swingh)); + ac->setSilent(quiet); + ac->setTurbo(turbo); + // No Light setting available. + ac->setEcono(econo); + ac->setClean(clean); + ac->setFilter(filter); + // No Beep setting available. + ac->setNight(sleep >= 0); // Sleep is either on/off, so convert to boolean. + // No Clock setting available. + ac->send(); +} +#endif // SEND_MITSUBISHIHEAVY + +#if SEND_NEOCLIMA +void IRac::neoclima(IRNeoclimaAc *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool turbo, const bool light, const bool filter, + const int16_t sleep) { + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingV(swingv != stdAc::swingv_t::kOff); + ac->setSwingH(swingh != stdAc::swingh_t::kOff); + // No Quiet setting available. + ac->setTurbo(turbo); + ac->setLight(light); + // No Econo setting available. + ac->setIon(filter); + // No Clean setting available. + // No Beep setting available. + ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. + // No Clock setting available. + ac->setPower(on); + ac->send(); +} +#endif // SEND_NEOCLIMA + +#if SEND_PANASONIC_AC +void IRac::panasonic(IRPanasonicAc *ac, const panasonic_ac_remote_model_t model, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const int16_t clock) { + ac->setModel(model); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical(ac->convertSwingV(swingv)); + ac->setSwingHorizontal(ac->convertSwingH(swingh)); + ac->setQuiet(quiet); + ac->setPowerful(turbo); + // No Light setting available. + // No Econo setting available. + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + if (clock >= 0) ac->setClock(clock); + ac->send(); +} +#endif // SEND_PANASONIC_AC + +#if SEND_SAMSUNG_AC +void IRac::samsung(IRSamsungAc *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const bool quiet, const bool turbo, const bool clean, + const bool beep, const bool dopower) { + // dopower is for unit testing only. It should only ever be false in tests. + if (dopower) ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwing(swingv != stdAc::swingv_t::kOff); + // No Horizontal swing setting available. + ac->setQuiet(quiet); + ac->setPowerful(turbo); + // No Light setting available. + // No Econo setting available. + // No Filter setting available. + ac->setClean(clean); + ac->setBeep(beep); + // No Sleep setting available. + // No Clock setting available. + // Do setMode() again as it can affect fan speed. + ac->setMode(ac->convertMode(mode)); + ac->send(); +} +#endif // SEND_SAMSUNG_AC + +#if SEND_SHARP_AC +void IRac::sharp(IRSharpAc *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan) { + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + // No Vertical swing setting available. + // No Horizontal swing setting available. + // No Quiet setting available. + // No Turbo setting available. + // No Light setting available. + // No Econo setting available. + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + // Do setMode() again as it can affect fan speed and temp. + ac->setMode(ac->convertMode(mode)); + ac->send(); +} +#endif // SEND_SHARP_AC + +#if SEND_TCL112AC +void IRac::tcl112(IRTcl112Ac *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool turbo, const bool light, const bool econo, + const bool filter) { + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical(swingv != stdAc::swingv_t::kOff); + ac->setSwingHorizontal(swingh != stdAc::swingh_t::kOff); + // No Quiet setting available. + ac->setTurbo(turbo); + ac->setLight(light); + ac->setEcono(econo); + ac->setHealth(filter); + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_TCL112AC + +#if SEND_TECO +void IRac::teco(IRTecoAc *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const bool light, const int16_t sleep) { + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwing(swingv != stdAc::swingv_t::kOff); + // No Horizontal swing setting available. + // No Quiet setting available. + // No Turbo setting available. + ac->setLight(light); + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. + // No Clock setting available. + ac->send(); +} +#endif // SEND_TECO + +#if SEND_TOSHIBA_AC +void IRac::toshiba(IRToshibaAC *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan) { + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + // No Vertical swing setting available. + // No Horizontal swing setting available. + // No Quiet setting available. + // No Turbo setting available. + // No Light setting available. + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_TOSHIBA_AC + +#if SEND_TROTEC +void IRac::trotec(IRTrotecESP *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const int16_t sleep) { + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setSpeed(ac->convertFan(fan)); + // No Vertical swing setting available. + // No Horizontal swing setting available. + // No Quiet setting available. + // No Turbo setting available. + // No Light setting available. + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. + // No Clock setting available. + ac->send(); +} +#endif // SEND_TROTEC + +#if SEND_VESTEL_AC +void IRac::vestel(IRVestelAc *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const bool turbo, const bool filter, const int16_t sleep, + const int16_t clock, const bool sendNormal) { + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwing(swingv != stdAc::swingv_t::kOff); + // No Horizontal swing setting available. + // No Quiet setting available. + ac->setTurbo(turbo); + // No Light setting available. + ac->setIon(filter); + // No Clean setting available. + // No Beep setting available. + ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. + if (sendNormal) ac->send(); // Send the normal message. + if (clock >= 0) { + ac->setTime(clock); + ac->send(); // Setting the clock requires a different "timer" message. + } +} +#endif // SEND_VESTEL_AC + +#if SEND_WHIRLPOOL_AC +void IRac::whirlpool(IRWhirlpoolAc *ac, const whirlpool_ac_remote_model_t model, + const bool on, const stdAc::opmode_t mode, + const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const bool turbo, const bool light, + const int16_t sleep, const int16_t clock) { + ac->setModel(model); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwing(swingv != stdAc::swingv_t::kOff); + // No Horizontal swing setting available. + // No Quiet setting available. + ac->setSuper(turbo); + ac->setLight(light); + // No Filter setting available + // No Clean setting available. + // No Beep setting available. + ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. + if (clock >= 0) ac->setClock(clock); + ac->setPowerToggle(on); + ac->send(); +} +#endif // SEND_WHIRLPOOL_AC + +// Create a new state base on desired & previous states but handle +// any state changes for options that need to be toggled. +// Args: +// desired: The state_t structure describing the desired a/c state. +// prev: Ptr to the previous state_t structure. +// +// Returns: +// A stdAc::state_t with the needed settings. +stdAc::state_t IRac::handleToggles(const stdAc::state_t desired, + const stdAc::state_t *prev) { + stdAc::state_t result = desired; + // If we've been given a previous state AND the it's the same A/C basically. + if (prev != NULL && desired.protocol == prev->protocol && + desired.model == prev->model) { + // Check if we have to handle toggle settings for specific A/C protocols. + switch (desired.protocol) { + case decode_type_t::COOLIX: + if ((desired.swingv == stdAc::swingv_t::kOff) ^ + (prev->swingv == stdAc::swingv_t::kOff)) // It changed, so toggle. + result.swingv = stdAc::swingv_t::kAuto; + else + result.swingv = stdAc::swingv_t::kOff; // No change, so no toggle. + result.turbo = desired.turbo ^ prev->turbo; + result.light = desired.light ^ prev->light; + result.clean = desired.clean ^ prev->clean; + result.sleep = ((desired.sleep >= 0) ^ (prev->sleep >= 0)) ? 0 : -1; + break; + case decode_type_t::DAIKIN128: + result.power = desired.power ^ prev->power; + result.light = desired.light ^ prev->light; + break; + case decode_type_t::MIDEA: + if ((desired.swingv == stdAc::swingv_t::kOff) ^ + (prev->swingv == stdAc::swingv_t::kOff)) // It changed, so toggle. + result.swingv = stdAc::swingv_t::kAuto; + else + result.swingv = stdAc::swingv_t::kOff; // No change, so no toggle. + break; + case decode_type_t::WHIRLPOOL_AC: + result.power = desired.power ^ prev->power; + break; + case decode_type_t::PANASONIC_AC: + // CKP models use a power mode toggle. + if (desired.model == panasonic_ac_remote_model_t::kPanasonicCkp) + result.power = desired.power ^ prev->power; + break; + default: + {}; + } + } + return result; +} + +// Send A/C message for a given device using common A/C settings. +// Args: +// vendor: The type of A/C protocol to use. +// model: The specific model of A/C if supported/applicable. +// on: Should the unit be powered on? (or in some cases, toggled) +// mode: What operating mode should the unit perform? e.g. Cool, Heat etc. +// degrees: What temperature should the unit be set to? +// celsius: Use degrees Celsius, otherwise Fahrenheit. +// fan: Fan speed. +// The following args are all "if supported" by the underlying A/C classes. +// swingv: Control the vertical swing of the vanes. +// swingh: Control the horizontal swing of the vanes. +// quiet: Set the unit to quiet (fan) operation mode. +// turbo: Set the unit to turbo operating mode. e.g. Max fan & cooling etc. +// econo: Set the unit to economical operating mode. +// light: Turn on the display/LEDs etc. +// filter: Turn on any particle/ion/allergy filter etc. +// clean: Turn on any settings to reduce mold etc. (Not self-clean mode.) +// beep: Control if the unit beeps upon receiving commands. +// sleep: Nr. of mins of sleep mode, or use sleep mode. (< 0 means off.) +// clock: Nr. of mins past midnight to set the clock to. (< 0 means off.) +// Returns: +// boolean: True, if accepted/converted/attempted. False, if unsupported. +bool IRac::sendAc(const decode_type_t vendor, const int16_t model, + const bool power, const stdAc::opmode_t mode, + const float degrees, const bool celsius, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const bool econo, + const bool light, const bool filter, const bool clean, + const bool beep, const int16_t sleep, const int16_t clock) { + // Convert the temperature to Celsius. + float degC; + if (celsius) + degC = degrees; + else + degC = fahrenheitToCelsius(degrees); + bool on = power; + // A hack for Home Assistant, it appears to need/want an Off opmode. + if (mode == stdAc::opmode_t::kOff) on = false; + // Per vendor settings & setup. + switch (vendor) { +#if SEND_AMCOR + case AMCOR: + { + IRAmcorAc ac(_pin, _inverted, _modulation); + amcor(&ac, on, mode, degC, fan); + break; + } +#endif // SEND_AMCOR +#if SEND_ARGO + case ARGO: + { + IRArgoAC ac(_pin, _inverted, _modulation); + argo(&ac, on, mode, degC, fan, swingv, turbo, sleep); + break; + } +#endif // SEND_ARGO +#if SEND_COOLIX + case COOLIX: + { + IRCoolixAC ac(_pin, _inverted, _modulation); + coolix(&ac, on, mode, degC, fan, swingv, swingh, + turbo, light, clean, sleep); + break; + } +#endif // SEND_COOLIX +#if SEND_DAIKIN + case DAIKIN: + { + IRDaikinESP ac(_pin, _inverted, _modulation); + daikin(&ac, on, mode, degC, fan, swingv, swingh, + quiet, turbo, econo, clean); + break; + } +#endif // SEND_DAIKIN +#if SEND_DAIKIN128 + case DAIKIN128: + { + IRDaikin128 ac(_pin, _inverted, _modulation); + daikin128(&ac, on, mode, degC, fan, swingv, quiet, turbo, + light, econo, sleep, clock); + break; + } +#endif // SEND_DAIKIN2 +#if SEND_DAIKIN160 + case DAIKIN160: + { + IRDaikin160 ac(_pin, _inverted, _modulation); + daikin160(&ac, on, mode, degC, fan, swingv); + break; + } +#endif // SEND_DAIKIN160 +#if SEND_DAIKIN176 + case DAIKIN176: + { + IRDaikin176 ac(_pin, _inverted, _modulation); + daikin176(&ac, on, mode, degC, fan, swingh); + break; + } +#endif // SEND_DAIKIN176 +#if SEND_DAIKIN2 + case DAIKIN2: + { + IRDaikin2 ac(_pin, _inverted, _modulation); + daikin2(&ac, on, mode, degC, fan, swingv, swingh, quiet, turbo, + light, econo, filter, clean, beep, sleep, clock); + break; + } +#endif // SEND_DAIKIN2 +#if SEND_DAIKIN216 + case DAIKIN216: + { + IRDaikin216 ac(_pin, _inverted, _modulation); + daikin216(&ac, on, mode, degC, fan, swingv, swingh, quiet, turbo); + break; + } +#endif // SEND_DAIKIN216 +#if SEND_ELECTRA_AC + case ELECTRA_AC: + { + IRElectraAc ac(_pin, _inverted, _modulation); + ac.begin(); + electra(&ac, on, mode, degC, fan, swingv, swingh); + break; + } +#endif // SEND_ELECTRA_AC +#if SEND_FUJITSU_AC + case FUJITSU_AC: + { + IRFujitsuAC ac(_pin, (fujitsu_ac_remote_model_t)model, _inverted, + _modulation); + ac.begin(); + fujitsu(&ac, (fujitsu_ac_remote_model_t)model, on, mode, degC, fan, + swingv, swingh, quiet, turbo, econo); + break; + } +#endif // SEND_FUJITSU_AC +#if SEND_GOODWEATHER + case GOODWEATHER: + { + IRGoodweatherAc ac(_pin, _inverted, _modulation); + ac.begin(); + goodweather(&ac, on, mode, degC, fan, swingv, turbo, light, sleep); + break; + } +#endif // SEND_GOODWEATHER +#if SEND_GREE + case GREE: + { + IRGreeAC ac(_pin, (gree_ac_remote_model_t)model, _inverted, _modulation); + ac.begin(); + gree(&ac, (gree_ac_remote_model_t)model, on, mode, degC, fan, swingv, + turbo, light, clean, sleep); + break; + } +#endif // SEND_GREE +#if SEND_HAIER_AC + case HAIER_AC: + { + IRHaierAC ac(_pin, _inverted, _modulation); + ac.begin(); + haier(&ac, on, mode, degC, fan, swingv, filter, sleep, clock); + break; + } +#endif // SEND_HAIER_AC +#if SEND_HAIER_AC_YRW02 + case HAIER_AC_YRW02: + { + IRHaierACYRW02 ac(_pin, _inverted, _modulation); + ac.begin(); + haierYrwo2(&ac, on, mode, degC, fan, swingv, turbo, filter, sleep); + break; + } +#endif // SEND_HAIER_AC_YRW02 +#if SEND_HITACHI_AC + case HITACHI_AC: + { + IRHitachiAc ac(_pin, _inverted, _modulation); + ac.begin(); + hitachi(&ac, on, mode, degC, fan, swingv, swingh); + break; + } +#endif // SEND_HITACHI_AC +#if SEND_KELVINATOR + case KELVINATOR: + { + IRKelvinatorAC ac(_pin, _inverted, _modulation); + ac.begin(); + kelvinator(&ac, on, mode, degC, fan, swingv, swingh, quiet, turbo, + light, filter, clean); + break; + } +#endif // SEND_KELVINATOR +#if SEND_MIDEA + case MIDEA: + { + IRMideaAC ac(_pin, _inverted, _modulation); + ac.begin(); + midea(&ac, on, mode, celsius, degrees, fan, swingv, sleep); + break; + } +#endif // SEND_MIDEA +#if SEND_MITSUBISHI_AC + case MITSUBISHI_AC: + { + IRMitsubishiAC ac(_pin, _inverted, _modulation); + ac.begin(); + mitsubishi(&ac, on, mode, degC, fan, swingv, swingh, quiet, clock); + break; + } +#endif // SEND_MITSUBISHI_AC +#if SEND_MITSUBISHIHEAVY + case MITSUBISHI_HEAVY_88: + { + IRMitsubishiHeavy88Ac ac(_pin, _inverted, _modulation); + ac.begin(); + mitsubishiHeavy88(&ac, on, mode, degC, fan, swingv, swingh, + turbo, econo, clean); + break; + } + case MITSUBISHI_HEAVY_152: + { + IRMitsubishiHeavy152Ac ac(_pin, _inverted, _modulation); + ac.begin(); + mitsubishiHeavy152(&ac, on, mode, degC, fan, swingv, swingh, + quiet, turbo, econo, filter, clean, sleep); + break; + } +#endif // SEND_MITSUBISHIHEAVY +#if SEND_NEOCLIMA + case NEOCLIMA: + { + IRNeoclimaAc ac(_pin, _inverted, _modulation); + ac.begin(); + neoclima(&ac, on, mode, degC, fan, swingv, swingh, turbo, light, filter, + sleep); + break; + } +#endif // SEND_NEOCLIMA +#if SEND_PANASONIC_AC + case PANASONIC_AC: + { + IRPanasonicAc ac(_pin, _inverted, _modulation); + ac.begin(); + panasonic(&ac, (panasonic_ac_remote_model_t)model, on, mode, degC, fan, + swingv, swingh, quiet, turbo, clock); + break; + } +#endif // SEND_PANASONIC_AC +#if SEND_SAMSUNG_AC + case SAMSUNG_AC: + { + IRSamsungAc ac(_pin, _inverted, _modulation); + ac.begin(); + samsung(&ac, on, mode, degC, fan, swingv, quiet, turbo, clean, beep); + break; + } +#endif // SEND_SAMSUNG_AC +#if SEND_SHARP_AC + case SHARP_AC: + { + IRSharpAc ac(_pin, _inverted, _modulation); + ac.begin(); + sharp(&ac, on, mode, degC, fan); + break; + } +#endif // SEND_SHARP_AC +#if SEND_TCL112AC + case TCL112AC: + { + IRTcl112Ac ac(_pin, _inverted, _modulation); + ac.begin(); + tcl112(&ac, on, mode, degC, fan, swingv, swingh, turbo, light, econo, + filter); + break; + } +#endif // SEND_TCL112AC +#if SEND_TECO + case TECO: + { + IRTecoAc ac(_pin, _inverted, _modulation); + ac.begin(); + teco(&ac, on, mode, degC, fan, swingv, light, sleep); + break; + } +#endif // SEND_TECO +#if SEND_TOSHIBA_AC + case TOSHIBA_AC: + { + IRToshibaAC ac(_pin, _inverted, _modulation); + ac.begin(); + toshiba(&ac, on, mode, degC, fan); + break; + } +#endif // SEND_TOSHIBA_AC +#if SEND_TROTEC + case TROTEC: + { + IRTrotecESP ac(_pin, _inverted, _modulation); + ac.begin(); + trotec(&ac, on, mode, degC, fan, sleep); + break; + } +#endif // SEND_TROTEC +#if SEND_VESTEL_AC + case VESTEL_AC: + { + IRVestelAc ac(_pin, _inverted, _modulation); + ac.begin(); + vestel(&ac, on, mode, degC, fan, swingv, turbo, filter, sleep, clock); + break; + } +#endif // SEND_VESTEL_AC +#if SEND_WHIRLPOOL_AC + case WHIRLPOOL_AC: + { + IRWhirlpoolAc ac(_pin, _inverted, _modulation); + ac.begin(); + whirlpool(&ac, (whirlpool_ac_remote_model_t)model, on, mode, degC, fan, + swingv, turbo, light, sleep, clock); + break; + } +#endif // SEND_WHIRLPOOL_AC + default: + return false; // Fail, didn't match anything. + } + return true; // Success. +} + +// Send A/C message for a given device using state_t structures. +// Args: +// desired: The state_t structure describing the desired new a/c state. +// prev: Ptr to the previous state_t structure. +// +// Returns: +// boolean: True, if accepted/converted/attempted. False, if unsupported. +bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) { + stdAc::state_t final = this->handleToggles(desired, prev); + return this->sendAc(final.protocol, final.model, final.power, final.mode, + final.degrees, final.celsius, final.fanspeed, + final.swingv, final.swingh, final.quiet, final.turbo, + final.econo, final.light, final.filter, final.clean, + final.beep, final.sleep, final.clock); +} + +// Compare two AirCon states. +// Returns: True if they differ, False if they don't. +// Note: Excludes clock. +bool IRac::cmpStates(const stdAc::state_t a, const stdAc::state_t b) { + return a.protocol != b.protocol || a.model != b.model || a.power != b.power || + a.mode != b.mode || a.degrees != b.degrees || a.celsius != b.celsius || + a.fanspeed != b.fanspeed || a.swingv != b.swingv || + a.swingh != b.swingh || a.quiet != b.quiet || a.turbo != b.turbo || + a.econo != b.econo || a.light != b.light || a.filter != b.filter || + a.clean != b.clean || a.beep != b.beep || a.sleep != b.sleep; +} + +stdAc::opmode_t IRac::strToOpmode(const char *str, + const stdAc::opmode_t def) { + if (!strcasecmp(str, "AUTO") || !strcasecmp(str, "AUTOMATIC")) + return stdAc::opmode_t::kAuto; + else if (!strcasecmp(str, "OFF") || !strcasecmp(str, "STOP")) + return stdAc::opmode_t::kOff; + else if (!strcasecmp(str, "COOL") || !strcasecmp(str, "COOLING")) + return stdAc::opmode_t::kCool; + else if (!strcasecmp(str, "HEAT") || !strcasecmp(str, "HEATING")) + return stdAc::opmode_t::kHeat; + else if (!strcasecmp(str, "DRY") || !strcasecmp(str, "DRYING") || + !strcasecmp(str, "DEHUMIDIFY")) + return stdAc::opmode_t::kDry; + else if (!strcasecmp(str, "FAN") || !strcasecmp(str, "FANONLY") || + !strcasecmp(str, "FAN_ONLY")) + return stdAc::opmode_t::kFan; + else + return def; +} + +stdAc::fanspeed_t IRac::strToFanspeed(const char *str, + const stdAc::fanspeed_t def) { + if (!strcasecmp(str, "AUTO") || !strcasecmp(str, "AUTOMATIC")) + return stdAc::fanspeed_t::kAuto; + else if (!strcasecmp(str, "MIN") || !strcasecmp(str, "MINIMUM") || + !strcasecmp(str, "LOWEST")) + return stdAc::fanspeed_t::kMin; + else if (!strcasecmp(str, "LOW")) + return stdAc::fanspeed_t::kLow; + else if (!strcasecmp(str, "MED") || !strcasecmp(str, "MEDIUM") || + !strcasecmp(str, "MID")) + return stdAc::fanspeed_t::kMedium; + else if (!strcasecmp(str, "HIGH") || !strcasecmp(str, "HI")) + return stdAc::fanspeed_t::kHigh; + else if (!strcasecmp(str, "MAX") || !strcasecmp(str, "MAXIMUM") || + !strcasecmp(str, "HIGHEST")) + return stdAc::fanspeed_t::kMax; + else + return def; +} + +stdAc::swingv_t IRac::strToSwingV(const char *str, + const stdAc::swingv_t def) { + if (!strcasecmp(str, "AUTO") || !strcasecmp(str, "AUTOMATIC") || + !strcasecmp(str, "ON") || !strcasecmp(str, "SWING")) + return stdAc::swingv_t::kAuto; + else if (!strcasecmp(str, "OFF") || !strcasecmp(str, "STOP")) + return stdAc::swingv_t::kOff; + else if (!strcasecmp(str, "MIN") || !strcasecmp(str, "MINIMUM") || + !strcasecmp(str, "LOWEST") || !strcasecmp(str, "BOTTOM") || + !strcasecmp(str, "DOWN")) + return stdAc::swingv_t::kLowest; + else if (!strcasecmp(str, "LOW")) + return stdAc::swingv_t::kLow; + else if (!strcasecmp(str, "MID") || !strcasecmp(str, "MIDDLE") || + !strcasecmp(str, "MED") || !strcasecmp(str, "MEDIUM") || + !strcasecmp(str, "CENTRE") || !strcasecmp(str, "CENTER")) + return stdAc::swingv_t::kMiddle; + else if (!strcasecmp(str, "HIGH") || !strcasecmp(str, "HI")) + return stdAc::swingv_t::kHigh; + else if (!strcasecmp(str, "HIGHEST") || !strcasecmp(str, "MAX") || + !strcasecmp(str, "MAXIMUM") || !strcasecmp(str, "TOP") || + !strcasecmp(str, "UP")) + return stdAc::swingv_t::kHighest; + else + return def; +} + +stdAc::swingh_t IRac::strToSwingH(const char *str, + const stdAc::swingh_t def) { + if (!strcasecmp(str, "AUTO") || !strcasecmp(str, "AUTOMATIC") || + !strcasecmp(str, "ON") || !strcasecmp(str, "SWING")) + return stdAc::swingh_t::kAuto; + else if (!strcasecmp(str, "OFF") || !strcasecmp(str, "STOP")) + return stdAc::swingh_t::kOff; + else if (!strcasecmp(str, "LEFTMAX") || !strcasecmp(str, "LEFT MAX") || + !strcasecmp(str, "MAXLEFT") || !strcasecmp(str, "MAX LEFT") || + !strcasecmp(str, "FARLEFT") || !strcasecmp(str, "FAR LEFT")) + return stdAc::swingh_t::kLeftMax; + else if (!strcasecmp(str, "LEFT")) + return stdAc::swingh_t::kLeft; + else if (!strcasecmp(str, "MID") || !strcasecmp(str, "MIDDLE") || + !strcasecmp(str, "MED") || !strcasecmp(str, "MEDIUM") || + !strcasecmp(str, "CENTRE") || !strcasecmp(str, "CENTER")) + return stdAc::swingh_t::kMiddle; + else if (!strcasecmp(str, "RIGHT")) + return stdAc::swingh_t::kRight; + else if (!strcasecmp(str, "RIGHTMAX") || !strcasecmp(str, "RIGHT MAX") || + !strcasecmp(str, "MAXRIGHT") || !strcasecmp(str, "MAX RIGHT") || + !strcasecmp(str, "FARRIGHT") || !strcasecmp(str, "FAR RIGHT")) + return stdAc::swingh_t::kRightMax; + else if (!strcasecmp(str, "WIDE")) + return stdAc::swingh_t::kWide; + else + return def; +} + +// Assumes str is the model or an integer >= 1. +int16_t IRac::strToModel(const char *str, const int16_t def) { + // Gree + if (!strcasecmp(str, "YAW1F")) { + return gree_ac_remote_model_t::YAW1F; + } else if (!strcasecmp(str, "YBOFB")) { + return gree_ac_remote_model_t::YBOFB; + // Fujitsu A/C models + } else if (!strcasecmp(str, "ARRAH2E")) { + return fujitsu_ac_remote_model_t::ARRAH2E; + } else if (!strcasecmp(str, "ARDB1")) { + return fujitsu_ac_remote_model_t::ARDB1; + } else if (!strcasecmp(str, "ARREB1E")) { + return fujitsu_ac_remote_model_t::ARREB1E; + } else if (!strcasecmp(str, "ARJW2")) { + return fujitsu_ac_remote_model_t::ARJW2; + // Panasonic A/C families + } else if (!strcasecmp(str, "LKE") || !strcasecmp(str, "PANASONICLKE")) { + return panasonic_ac_remote_model_t::kPanasonicLke; + } else if (!strcasecmp(str, "NKE") || !strcasecmp(str, "PANASONICNKE")) { + return panasonic_ac_remote_model_t::kPanasonicNke; + } else if (!strcasecmp(str, "DKE") || !strcasecmp(str, "PANASONICDKE")) { + return panasonic_ac_remote_model_t::kPanasonicDke; + } else if (!strcasecmp(str, "JKE") || !strcasecmp(str, "PANASONICJKE")) { + return panasonic_ac_remote_model_t::kPanasonicJke; + } else if (!strcasecmp(str, "CKP") || !strcasecmp(str, "PANASONICCKP")) { + return panasonic_ac_remote_model_t::kPanasonicCkp; + } else if (!strcasecmp(str, "RKR") || !strcasecmp(str, "PANASONICRKR")) { + return panasonic_ac_remote_model_t::kPanasonicRkr; + // Whirlpool A/C models + } else if (!strcasecmp(str, "DG11J13A") || !strcasecmp(str, "DG11J104") || + !strcasecmp(str, "DG11J1-04")) { + return whirlpool_ac_remote_model_t::DG11J13A; + } else if (!strcasecmp(str, "DG11J191")) { + return whirlpool_ac_remote_model_t::DG11J191; + } else { + int16_t number = atoi(str); + if (number > 0) + return number; + else + return def; + } +} + +bool IRac::strToBool(const char *str, const bool def) { + if (!strcasecmp(str, "ON") || !strcasecmp(str, "1") || + !strcasecmp(str, "YES") || !strcasecmp(str, "TRUE")) + return true; + else if (!strcasecmp(str, "OFF") || !strcasecmp(str, "0") || + !strcasecmp(str, "NO") || !strcasecmp(str, "FALSE")) + return false; + else + return def; +} + +String IRac::boolToString(const bool value) { + return value ? F("on") : F("off"); +} + +String IRac::opmodeToString(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kOff: + return F("off"); + case stdAc::opmode_t::kAuto: + return F("auto"); + case stdAc::opmode_t::kCool: + return F("cool"); + case stdAc::opmode_t::kHeat: + return F("heat"); + case stdAc::opmode_t::kDry: + return F("dry"); + case stdAc::opmode_t::kFan: + return F("fan_only"); + default: + return F("unknown"); + } +} + +String IRac::fanspeedToString(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kAuto: + return F("auto"); + case stdAc::fanspeed_t::kMax: + return F("max"); + case stdAc::fanspeed_t::kHigh: + return F("high"); + case stdAc::fanspeed_t::kMedium: + return F("medium"); + case stdAc::fanspeed_t::kLow: + return F("low"); + case stdAc::fanspeed_t::kMin: + return F("min"); + default: + return F("unknown"); + } +} + +String IRac::swingvToString(const stdAc::swingv_t swingv) { + switch (swingv) { + case stdAc::swingv_t::kOff: + return F("off"); + case stdAc::swingv_t::kAuto: + return F("auto"); + case stdAc::swingv_t::kHighest: + return F("highest"); + case stdAc::swingv_t::kHigh: + return F("high"); + case stdAc::swingv_t::kMiddle: + return F("middle"); + case stdAc::swingv_t::kLow: + return F("low"); + case stdAc::swingv_t::kLowest: + return F("lowest"); + default: + return F("unknown"); + } +} + +String IRac::swinghToString(const stdAc::swingh_t swingh) { + switch (swingh) { + case stdAc::swingh_t::kOff: + return F("off"); + case stdAc::swingh_t::kAuto: + return F("auto"); + case stdAc::swingh_t::kLeftMax: + return F("leftmax"); + case stdAc::swingh_t::kLeft: + return F("left"); + case stdAc::swingh_t::kMiddle: + return F("middle"); + case stdAc::swingh_t::kRight: + return F("right"); + case stdAc::swingh_t::kRightMax: + return F("rightmax"); + case stdAc::swingh_t::kWide: + return F("wide"); + default: + return F("unknown"); + } +} + +namespace IRAcUtils { + // Display the human readable state of an A/C message if we can. + // Args: + // result: A Ptr to the captured `decode_results` that contains an A/C mesg. + // Returns: + // A string with the human description of the A/C message. "" if we can't. + String resultAcToString(const decode_results * const result) { + switch (result->decode_type) { +#if DECODE_AMCOR + case decode_type_t::AMCOR: { + IRAmcorAc ac(0); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_AMCOR +#if DECODE_ARGO + case decode_type_t::ARGO: { + IRArgoAC ac(0); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_ARGO +#if DECODE_DAIKIN + case decode_type_t::DAIKIN: { + IRDaikinESP ac(0); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_DAIKIN +#if DECODE_DAIKIN128 + case decode_type_t::DAIKIN128: { + IRDaikin128 ac(0); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_DAIKIN128 +#if DECODE_DAIKIN160 + case decode_type_t::DAIKIN160: { + IRDaikin160 ac(0); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_DAIKIN160 +#if DECODE_DAIKIN176 + case decode_type_t::DAIKIN176: { + IRDaikin176 ac(0); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_DAIKIN160 +#if DECODE_DAIKIN2 + case decode_type_t::DAIKIN2: { + IRDaikin2 ac(0); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_DAIKIN2 +#if DECODE_DAIKIN216 + case decode_type_t::DAIKIN216: { + IRDaikin216 ac(0); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_DAIKIN216 +#if DECODE_ELECTRA_AC + case decode_type_t::ELECTRA_AC: { + IRElectraAc ac(0); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_ELECTRA_AC +#if DECODE_FUJITSU_AC + case decode_type_t::FUJITSU_AC: { + IRFujitsuAC ac(0); + ac.setRaw(result->state, result->bits / 8); + return ac.toString(); + } +#endif // DECODE_FUJITSU_AC +#if DECODE_KELVINATOR + case decode_type_t::KELVINATOR: { + IRKelvinatorAC ac(0); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_KELVINATOR +#if DECODE_MITSUBISHI_AC + case decode_type_t::MITSUBISHI_AC: { + IRMitsubishiAC ac(0); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_MITSUBISHI_AC +#if DECODE_MITSUBISHIHEAVY + case decode_type_t::MITSUBISHI_HEAVY_88: { + IRMitsubishiHeavy88Ac ac(0); + ac.setRaw(result->state); + return ac.toString(); + } + case decode_type_t::MITSUBISHI_HEAVY_152: { + IRMitsubishiHeavy152Ac ac(0); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_MITSUBISHIHEAVY +#if DECODE_NEOCLIMA + case decode_type_t::NEOCLIMA: { + IRNeoclimaAc ac(0); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_NEOCLIMA +#if DECODE_TOSHIBA_AC + case decode_type_t::TOSHIBA_AC: { + IRToshibaAC ac(0); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_TOSHIBA_AC +#if DECODE_TROTEC + case decode_type_t::TROTEC: { + IRTrotecESP ac(0); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_TROTEC +#if DECODE_GOODWEATHER + case decode_type_t::GOODWEATHER: { + IRGoodweatherAc ac(0); + ac.setRaw(result->value); // Goodweather uses value instead of state. + return ac.toString(); + } +#endif // DECODE_GOODWEATHER +#if DECODE_GREE + case decode_type_t::GREE: { + IRGreeAC ac(0); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_GREE +#if DECODE_MIDEA + case decode_type_t::MIDEA: { + IRMideaAC ac(0); + ac.setRaw(result->value); // Midea uses value instead of state. + return ac.toString(); + } +#endif // DECODE_MIDEA +#if DECODE_HAIER_AC + case decode_type_t::HAIER_AC: { + IRHaierAC ac(0); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_HAIER_AC +#if DECODE_HAIER_AC_YRW02 + case decode_type_t::HAIER_AC_YRW02: { + IRHaierACYRW02 ac(0); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_HAIER_AC_YRW02 +#if DECODE_SAMSUNG_AC + case decode_type_t::SAMSUNG_AC: { + IRSamsungAc ac(0); + ac.setRaw(result->state, result->bits / 8); + return ac.toString(); + } +#endif // DECODE_SAMSUNG_AC +#if DECODE_SHARP_AC + case decode_type_t::SHARP_AC: { + IRSharpAc ac(0); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_SHARP_AC +#if DECODE_COOLIX + case decode_type_t::COOLIX: { + IRCoolixAC ac(0); + ac.setRaw(result->value); // Coolix uses value instead of state. + return ac.toString(); + } +#endif // DECODE_COOLIX +#if DECODE_PANASONIC_AC + case decode_type_t::PANASONIC_AC: { + if (result->bits > kPanasonicAcShortBits) { + IRPanasonicAc ac(0); + ac.setRaw(result->state); + return ac.toString(); + } + return ""; + } +#endif // DECODE_PANASONIC_AC +#if DECODE_HITACHI_AC + case decode_type_t::HITACHI_AC: { + IRHitachiAc ac(0); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_HITACHI_AC +#if DECODE_WHIRLPOOL_AC + case decode_type_t::WHIRLPOOL_AC: { + IRWhirlpoolAc ac(0); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_WHIRLPOOL_AC +#if DECODE_VESTEL_AC + case decode_type_t::VESTEL_AC: { + IRVestelAc ac(0); + ac.setRaw(result->value); // Like Coolix, use value instead of state. + return ac.toString(); + } +#endif // DECODE_VESTEL_AC +#if DECODE_TECO + case decode_type_t::TECO: { + IRTecoAc ac(0); + ac.setRaw(result->value); // Like Coolix, use value instead of state. + return ac.toString(); + } +#endif // DECODE_TECO +#if DECODE_TCL112AC + case decode_type_t::TCL112AC: { + IRTcl112Ac ac(0); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_TCL112AC + default: + return ""; + } + } + + // Convert a valid IR A/C remote message that we understand enough into a + // Common A/C state. + // + // Args: + // decode: A PTR to a successful raw IR decode object. + // result: A PTR to a state structure to store the result in. + // prev: A PTR to a state structure which has the prev. state. (optional) + // Returns: + // A boolean indicating success or failure. + bool decodeToState(const decode_results *decode, stdAc::state_t *result, + const stdAc::state_t *prev) { + if (decode == NULL || result == NULL) return false; // Safety check. + switch (decode->decode_type) { +#if DECODE_AMCOR + case decode_type_t::AMCOR: { + IRAmcorAc ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_AMCOR +#if DECODE_ARGO + case decode_type_t::ARGO: { + IRArgoAC ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_ARGO +#if DECODE_COOLIX + case decode_type_t::COOLIX: { + IRCoolixAC ac(kGpioUnused); + ac.setRaw(decode->value); // Uses value instead of state. + *result = ac.toCommon(prev); + break; + } +#endif // DECODE_COOLIX +#if DECODE_DAIKIN + case decode_type_t::DAIKIN: { + IRDaikinESP ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_DAIKIN +#if DECODE_DAIKIN160 + case decode_type_t::DAIKIN160: { + IRDaikin160 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_DAIKIN160 +#if DECODE_DAIKIN176 + case decode_type_t::DAIKIN176: { + IRDaikin176 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_DAIKIN160 +#if DECODE_DAIKIN2 + case decode_type_t::DAIKIN2: { + IRDaikin2 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_DAIKIN2 +#if DECODE_DAIKIN216 + case decode_type_t::DAIKIN216: { + IRDaikin216 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_DAIKIN216 +#if DECODE_ELECTRA_AC + case decode_type_t::ELECTRA_AC: { + IRElectraAc ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_ELECTRA_AC +#if DECODE_FUJITSU_AC + case decode_type_t::FUJITSU_AC: { + IRFujitsuAC ac(kGpioUnused); + ac.setRaw(decode->state, decode->bits / 8); + *result = ac.toCommon(); + break; + } +#endif // DECODE_FUJITSU_AC +#if DECODE_GOODWEATHER + case decode_type_t::GOODWEATHER: { + IRGoodweatherAc ac(kGpioUnused); + ac.setRaw(decode->value); // Uses value instead of state. + *result = ac.toCommon(); + break; + } +#endif // DECODE_GOODWEATHER +#if DECODE_GREE + case decode_type_t::GREE: { + IRGreeAC ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_GREE +#if DECODE_HAIER_AC + case decode_type_t::HAIER_AC: { + IRHaierAC ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_HAIER_AC +#if DECODE_HAIER_AC_YRW02 + case decode_type_t::HAIER_AC_YRW02: { + IRHaierACYRW02 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_HAIER_AC_YRW02 +#if (DECODE_HITACHI_AC || DECODE_HITACHI_AC2) + case decode_type_t::HITACHI_AC: { + IRHitachiAc ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // (DECODE_HITACHI_AC || DECODE_HITACHI_AC2) +#if DECODE_KELVINATOR + case decode_type_t::KELVINATOR: { + IRKelvinatorAC ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_KELVINATOR +#if DECODE_MIDEA + case decode_type_t::MIDEA: { + IRMideaAC ac(kGpioUnused); + ac.setRaw(decode->value); // Uses value instead of state. + *result = ac.toCommon(prev); + break; + } +#endif // DECODE_MIDEA +#if DECODE_MITSUBISHI_AC + case decode_type_t::MITSUBISHI_AC: { + IRMitsubishiAC ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_MITSUBISHI_AC +#if DECODE_MITSUBISHIHEAVY + case decode_type_t::MITSUBISHI_HEAVY_88: { + IRMitsubishiHeavy88Ac ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } + case decode_type_t::MITSUBISHI_HEAVY_152: { + IRMitsubishiHeavy152Ac ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_MITSUBISHIHEAVY +#if DECODE_NEOCLIMA + case decode_type_t::NEOCLIMA: { + IRNeoclimaAc ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_NEOCLIMA +#if DECODE_PANASONIC_AC + case decode_type_t::PANASONIC_AC: { + IRPanasonicAc ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_PANASONIC_AC +#if DECODE_SAMSUNG_AC + case decode_type_t::SAMSUNG_AC: { + IRSamsungAc ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_SAMSUNG_AC +#if DECODE_SHARP_AC + case decode_type_t::SHARP_AC: { + IRSharpAc ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_SHARP_AC +#if DECODE_TCL112AC + case decode_type_t::TCL112AC: { + IRTcl112Ac ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_TCL112AC +#if DECODE_TECO + case decode_type_t::TECO: { + IRTecoAc ac(kGpioUnused); + ac.setRaw(decode->value); // Uses value instead of state. + *result = ac.toCommon(); + break; + } +#endif // DECODE_TECO +#if DECODE_TOSHIBA_AC + case decode_type_t::TOSHIBA_AC: { + IRToshibaAC ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_TOSHIBA_AC +#if DECODE_TROTEC + case decode_type_t::TROTEC: { + IRTrotecESP ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_TROTEC +#if DECODE_VESTEL_AC + case decode_type_t::VESTEL_AC: { + IRVestelAc ac(kGpioUnused); + ac.setRaw(decode->value); // Uses value instead of state. + *result = ac.toCommon(); + break; + } +#endif // DECODE_VESTEL_AC +#if DECODE_WHIRLPOOL_AC + case decode_type_t::WHIRLPOOL_AC: { + IRWhirlpoolAc ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_WHIRLPOOL_AC + default: + return false; + } + return true; + } +} // namespace IRAcUtils diff --git a/lib/IRremoteESP8266-2.6.0/src/IRac.h b/lib/IRremoteESP8266-2.6.5/src/IRac.h similarity index 68% rename from lib/IRremoteESP8266-2.6.0/src/IRac.h rename to lib/IRremoteESP8266-2.6.5/src/IRac.h index ce8d50507..73ee4b2a3 100644 --- a/lib/IRremoteESP8266-2.6.0/src/IRac.h +++ b/lib/IRremoteESP8266-2.6.5/src/IRac.h @@ -6,14 +6,14 @@ #ifndef UNIT_TEST #include #endif -#ifndef ARDUINO -#include -#endif #include "IRremoteESP8266.h" +#include "ir_Amcor.h" #include "ir_Argo.h" #include "ir_Coolix.h" #include "ir_Daikin.h" #include "ir_Fujitsu.h" +#include "ir_Electra.h" +#include "ir_Goodweather.h" #include "ir_Gree.h" #include "ir_Haier.h" #include "ir_Hitachi.h" @@ -21,8 +21,10 @@ #include "ir_Midea.h" #include "ir_Mitsubishi.h" #include "ir_MitsubishiHeavy.h" +#include "ir_Neoclima.h" #include "ir_Panasonic.h" #include "ir_Samsung.h" +#include "ir_Sharp.h" #include "ir_Tcl.h" #include "ir_Teco.h" #include "ir_Toshiba.h" @@ -30,9 +32,14 @@ #include "ir_Vestel.h" #include "ir_Whirlpool.h" +// Constants +const int8_t kGpioUnused = -1; + +// Class class IRac { public: - explicit IRac(uint8_t pin); + explicit IRac(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); static bool isProtocolSupported(const decode_type_t protocol); bool sendAc(const decode_type_t vendor, const int16_t model, const bool power, const stdAc::opmode_t mode, const float degrees, @@ -42,23 +49,36 @@ class IRac { const bool light, const bool filter, const bool clean, const bool beep, const int16_t sleep = -1, const int16_t clock = -1); - + bool sendAc(const stdAc::state_t desired, const stdAc::state_t *prev = NULL); + static bool cmpStates(const stdAc::state_t a, const stdAc::state_t b); static bool strToBool(const char *str, const bool def = false); static int16_t strToModel(const char *str, const int16_t def = -1); static stdAc::opmode_t strToOpmode( - const char *str, const stdAc::opmode_t def = stdAc::opmode_t::kAuto); + const char *str, const stdAc::opmode_t def = stdAc::opmode_t::kAuto); static stdAc::fanspeed_t strToFanspeed( - const char *str, - const stdAc::fanspeed_t def = stdAc::fanspeed_t::kAuto); + const char *str, + const stdAc::fanspeed_t def = stdAc::fanspeed_t::kAuto); static stdAc::swingv_t strToSwingV( - const char *str, const stdAc::swingv_t def = stdAc::swingv_t::kOff); + const char *str, const stdAc::swingv_t def = stdAc::swingv_t::kOff); static stdAc::swingh_t strToSwingH( - const char *str, const stdAc::swingh_t def = stdAc::swingh_t::kOff); + const char *str, const stdAc::swingh_t def = stdAc::swingh_t::kOff); + static String boolToString(const bool value); + static String opmodeToString(const stdAc::opmode_t mode); + static String fanspeedToString(const stdAc::fanspeed_t speed); + static String swingvToString(const stdAc::swingv_t swingv); + static String swinghToString(const stdAc::swingh_t swingh); #ifndef UNIT_TEST private: #endif - uint8_t _pin; + uint16_t _pin; + bool _inverted; + bool _modulation; +#if SEND_AMCOR + void amcor(IRAmcorAc *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan); +#endif // SEND_AMCOR #if SEND_ARGO void argo(IRArgoAC *ac, const bool on, const stdAc::opmode_t mode, const float degrees, @@ -81,6 +101,27 @@ class IRac { const bool quiet, const bool turbo, const bool econo, const bool clean); #endif // SEND_DAIKIN +#if SEND_DAIKIN128 + void daikin128(IRDaikin128 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const bool quiet, const bool turbo, const bool light, + const bool econo, const int16_t sleep = -1, + const int16_t clock = -1); +#endif // SEND_DAIKIN128 +#if SEND_DAIKIN160 +void daikin160(IRDaikin160 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv); +#endif // SEND_DAIKIN160 +#if SEND_DAIKIN176 +void daikin176(IRDaikin176 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingh_t swingh); +#endif // SEND_DAIKIN176 #if SEND_DAIKIN2 void daikin2(IRDaikin2 *ac, const bool on, const stdAc::opmode_t mode, @@ -96,17 +137,33 @@ void daikin216(IRDaikin216 *ac, const bool on, const stdAc::opmode_t mode, const float degrees, const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool quiet); + const bool quiet, const bool turbo); #endif // SEND_DAIKIN216 +#if SEND_ELECTRA_AC +void electra(IRElectraAc *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const stdAc::swingh_t swingh); +#endif // SEND_ELECTRA_AC #if SEND_FUJITSU_AC void fujitsu(IRFujitsuAC *ac, const fujitsu_ac_remote_model_t model, const bool on, const stdAc::opmode_t mode, const float degrees, const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool quiet); + const bool quiet, const bool turbo, const bool econo); #endif // SEND_FUJITSU_AC +#if SEND_GOODWEATHER + void goodweather(IRGoodweatherAc *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const bool turbo, const bool light, + const int16_t sleep = -1); +#endif // SEND_GOODWEATHER #if SEND_GREE - void gree(IRGreeAC *ac, + void gree(IRGreeAC *ac, const gree_ac_remote_model_t model, const bool on, const stdAc::opmode_t mode, const float degrees, const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, const bool turbo, const bool light, const bool clean, @@ -143,14 +200,16 @@ void daikin216(IRDaikin216 *ac, #endif // SEND_KELVINATOR #if SEND_MIDEA void midea(IRMideaAC *ac, - const bool on, const stdAc::opmode_t mode, const float degrees, - const stdAc::fanspeed_t fan, const int16_t sleep = -1); + const bool on, const stdAc::opmode_t mode, const bool celsius, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const int16_t sleep = -1); #endif // SEND_MIDEA #if SEND_MITSUBISHI_AC void mitsubishi(IRMitsubishiAC *ac, const bool on, const stdAc::opmode_t mode, const float degrees, const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const stdAc::swingh_t swingh, const bool quiet, const int16_t clock = -1); #endif // SEND_MITSUBISHI_AC #if SEND_MITSUBISHIHEAVY @@ -169,6 +228,13 @@ void daikin216(IRDaikin216 *ac, const bool filter, const bool clean, const int16_t sleep = -1); #endif // SEND_MITSUBISHIHEAVY +#if SEND_NEOCLIMA + void neoclima(IRNeoclimaAc *ac, const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool turbo, const bool light, const bool filter, + const int16_t sleep = -1); +#endif // SEND_NEOCLIMA #if SEND_PANASONIC_AC void panasonic(IRPanasonicAc *ac, const panasonic_ac_remote_model_t model, const bool on, const stdAc::opmode_t mode, const float degrees, @@ -181,8 +247,13 @@ void daikin216(IRDaikin216 *ac, const bool on, const stdAc::opmode_t mode, const float degrees, const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, const bool quiet, const bool turbo, const bool clean, - const bool beep, const bool sendOnOffHack = true); + const bool beep, const bool dopower = true); #endif // SEND_SAMSUNG_AC +#if SEND_SHARP_AC + void sharp(IRSharpAc *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan); +#endif // SEND_SHARP_AC #if SEND_TCL112AC void tcl112(IRTcl112Ac *ac, const bool on, const stdAc::opmode_t mode, const float degrees, @@ -195,7 +266,7 @@ void daikin216(IRDaikin216 *ac, void teco(IRTecoAc *ac, const bool on, const stdAc::opmode_t mode, const float degrees, const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, - const int16_t sleep = -1); + const bool light, const int16_t sleep = -1); #endif // SEND_TECO #if SEND_TOSHIBA_AC void toshiba(IRToshibaAC *ac, @@ -222,27 +293,13 @@ void daikin216(IRDaikin216 *ac, const bool turbo, const bool light, const int16_t sleep = -1, const int16_t clock = -1); #endif // SEND_WHIRLPOOL_AC +static stdAc::state_t handleToggles(const stdAc::state_t desired, + const stdAc::state_t *prev = NULL); }; // IRac class -// Structure to hold a common A/C state. -typedef struct { - decode_type_t protocol; - int16_t model; - bool power; - stdAc::opmode_t mode; - float degrees; - bool celsius; - stdAc::fanspeed_t fanspeed; - stdAc::swingv_t swingv; - stdAc::swingh_t swingh; - bool quiet; - bool turbo; - bool econo; - bool light; - bool filter; - bool clean; - bool beep; - int16_t sleep; - int16_t clock; -} commonAcState_t; +namespace IRAcUtils { + String resultAcToString(const decode_results * const results); + bool decodeToState(const decode_results *decode, stdAc::state_t *result, + const stdAc::state_t *prev = NULL); +} // namespace IRAcUtils #endif // IRAC_H_ diff --git a/lib/IRremoteESP8266-2.6.0/src/IRrecv.cpp b/lib/IRremoteESP8266-2.6.5/src/IRrecv.cpp similarity index 56% rename from lib/IRremoteESP8266-2.6.0/src/IRrecv.cpp rename to lib/IRremoteESP8266-2.6.5/src/IRrecv.cpp index eac868084..739ced38f 100644 --- a/lib/IRremoteESP8266-2.6.0/src/IRrecv.cpp +++ b/lib/IRremoteESP8266-2.6.5/src/IRrecv.cpp @@ -1,18 +1,23 @@ // Copyright 2009 Ken Shirriff // Copyright 2015 Mark Szabo // Copyright 2015 Sebastien Warin -// Copyright 2017 David Conran +// Copyright 2017, 2019 David Conran #include "IRrecv.h" #include #ifndef UNIT_TEST +#if defined(ESP8266) extern "C" { #include #include } +#endif // ESP8266 #include #endif #include +#ifdef UNIT_TEST +#include +#endif // UNIT_TEST #include "IRremoteESP8266.h" #include "IRutils.h" @@ -20,32 +25,68 @@ extern "C" { #undef ICACHE_RAM_ATTR #define ICACHE_RAM_ATTR #endif + +#ifndef USE_IRAM_ATTR +#if defined(ESP8266) +#define USE_IRAM_ATTR ICACHE_RAM_ATTR +#endif // ESP8266 +#if defined(ESP32) +#define USE_IRAM_ATTR IRAM_ATTR +#endif // ESP32 +#endif // USE_IRAM_ATTR + +#define ONCE 0 + +// Updated by David Conran (https://github.com/crankyoldgit) for receiving IR +// code on ESP32 // Updated by Sebastien Warin (http://sebastien.warin.fr) for receiving IR code // on ESP8266 -// Updated by markszabo (https://github.com/markszabo/IRremoteESP8266) for +// Updated by markszabo (https://github.com/crankyoldgit/IRremoteESP8266) for // sending IR code on ESP8266 // Globals #ifndef UNIT_TEST +#if defined(ESP8266) static ETSTimer timer; -#endif +#endif // ESP8266 +#if defined(ESP32) +static hw_timer_t * timer = NULL; +#endif // ESP32 +#endif // UNIT_TEST + +#if defined(ESP32) +portMUX_TYPE irremote_mux = portMUX_INITIALIZER_UNLOCKED; +#endif // ESP32 volatile irparams_t irparams; irparams_t *irparams_save; // A copy of the interrupt state while decoding. #ifndef UNIT_TEST -static void ICACHE_RAM_ATTR read_timeout(void *arg __attribute__((unused))) { +#if defined(ESP8266) +static void USE_IRAM_ATTR read_timeout(void *arg __attribute__((unused))) { os_intr_lock(); +#endif // ESP8266 +#if defined(ESP32) +static void USE_IRAM_ATTR read_timeout(void) { + portENTER_CRITICAL(&irremote_mux); +#endif // ESP32 if (irparams.rawlen) irparams.rcvstate = kStopState; +#if defined(ESP8266) os_intr_unlock(); +#endif // ESP8266 +#if defined(ESP32) + portEXIT_CRITICAL(&irremote_mux); +#endif // ESP32 } -static void ICACHE_RAM_ATTR gpio_intr() { - uint32_t now = system_get_time(); - uint32_t gpio_status = GPIO_REG_READ(GPIO_STATUS_ADDRESS); +static void USE_IRAM_ATTR gpio_intr() { + uint32_t now = micros(); static uint32_t start = 0; +#if defined(ESP8266) + uint32_t gpio_status = GPIO_REG_READ(GPIO_STATUS_ADDRESS); os_timer_disarm(&timer); GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, gpio_status); +#endif // ESP8266 // Grab a local copy of rawlen to reduce instructions used in IRAM. // This is an ugly premature optimisation code-wise, but we do everything we @@ -74,8 +115,14 @@ static void ICACHE_RAM_ATTR gpio_intr() { irparams.rawlen++; start = now; -#define ONCE 0 + +#if defined(ESP8266) os_timer_arm(&timer, irparams.timeout, ONCE); +#endif // ESP8266 +#if defined(ESP32) + timerWrite(timer, 0); // Reset the timeout. + timerAlarmEnable(timer); +#endif // ESP32 } #endif // UNIT_TEST @@ -87,11 +134,21 @@ static void ICACHE_RAM_ATTR gpio_intr() { // bufsize: Nr. of entries to have in the capture buffer. (Default: kRawBuf) // timeout: Nr. of milli-Seconds of no signal before we stop capturing data. // (Default: kTimeoutMs) -// save_buffer: Use a second (save) buffer to decode from. (Def: false) +// save_buffer: Use a second (save) buffer to decode from. (Default: false) +// timer_num: Which ESP32 timer number to use? ESP32 only, otherwise unused. +// (Range: 0-3. Default: kDefaultESP32Timer) // Returns: // An IRrecv class object. -IRrecv::IRrecv(uint16_t recvpin, uint16_t bufsize, uint8_t timeout, - bool save_buffer) { +#if defined(ESP32) +IRrecv::IRrecv(const uint16_t recvpin, const uint16_t bufsize, + const uint8_t timeout, const bool save_buffer, + const uint8_t timer_num) { + // There are only 4 timers. 0 to 3. + _timer_num = std::min(timer_num, (uint8_t)3); +#else // ESP32 +IRrecv::IRrecv(const uint16_t recvpin, const uint16_t bufsize, + const uint8_t timeout, const bool save_buffer) { +#endif // ESP32 irparams.recvpin = recvpin; irparams.bufsize = bufsize; // Ensure we are going to be able to store all possible values in the @@ -123,8 +180,9 @@ IRrecv::IRrecv(uint16_t recvpin, uint16_t bufsize, uint8_t timeout, irparams_save = NULL; } #if DECODE_HASH - unknown_threshold = kUnknownThreshold; + _unknown_threshold = kUnknownThreshold; #endif // DECODE_HASH + _tolerance = kTolerance; } // Class destructor @@ -134,35 +192,70 @@ IRrecv::~IRrecv(void) { delete[] irparams_save->rawbuf; delete irparams_save; } + disableIRIn(); +#if defined(ESP32) + if (timer != NULL) timerEnd(timer); // Cleanup the ESP32 timeout timer. +#endif // ESP32 } -// initialization -void IRrecv::enableIRIn() { - // initialize state machine variables +// Set up and (re)start the IR capture mechanism. +// +// Args: +// pullup: A flag indicating should the GPIO use the internal pullup resistor. +// (Default: `false`. i.e. No.) +void IRrecv::enableIRIn(const bool pullup) { + // ESP32's seem to require explicitly setting the GPIO to INPUT etc. + // This wasn't required on the ESP8266s, but it shouldn't hurt to make sure. + if (pullup) { +#ifndef UNIT_TEST + pinMode(irparams.recvpin, INPUT_PULLUP); + } else { + pinMode(irparams.recvpin, INPUT); +#endif // UNIT_TEST + } +#if defined(ESP32) + // Initialize the ESP32 timer. + timer = timerBegin(_timer_num, 80, true); // 80MHz / 80 = 1 uSec granularity. + // Set the timer so it only fires once, and set it's trigger in uSeconds. + timerAlarmWrite(timer, MS_TO_USEC(irparams.timeout), ONCE); + // Note: Interrupt needs to be attached before it can be enabled or disabled. + timerAttachInterrupt(timer, &read_timeout, true); +#endif // ESP32 + + // Initialize state machine variables resume(); #ifndef UNIT_TEST - // Initialize timer +#if defined(ESP8266) + // Initialize ESP8266 timer. os_timer_disarm(&timer); os_timer_setfn(&timer, reinterpret_cast(read_timeout), NULL); - +#endif // ESP8266 // Attach Interrupt attachInterrupt(irparams.recvpin, gpio_intr, CHANGE); -#endif +#endif // UNIT_TEST } -void IRrecv::disableIRIn() { +void IRrecv::disableIRIn(void) { #ifndef UNIT_TEST +#if defined(ESP8266) os_timer_disarm(&timer); +#endif // ESP8266 +#if defined(ESP32) + timerAlarmDisable(timer); +#endif // ESP32 detachInterrupt(irparams.recvpin); -#endif +#endif // UNIT_TEST } -void IRrecv::resume() { +void IRrecv::resume(void) { irparams.rcvstate = kIdleState; irparams.rawlen = 0; irparams.overflow = false; +#if defined(ESP32) + timerAlarmDisable(timer); +#endif // ESP32 } // Make a copy of the interrupt state & buffer data. @@ -196,15 +289,24 @@ void IRrecv::copyIrParams(volatile irparams_t *src, irparams_t *dst) { // Obtain the maximum number of entries possible in the capture buffer. // i.e. It's size. -uint16_t IRrecv::getBufSize() { return irparams.bufsize; } +uint16_t IRrecv::getBufSize(void) { return irparams.bufsize; } #if DECODE_HASH // Set the minimum length we will consider for reporting UNKNOWN message types. -void IRrecv::setUnknownThreshold(uint16_t length) { - unknown_threshold = length; +void IRrecv::setUnknownThreshold(const uint16_t length) { + _unknown_threshold = length; } #endif // DECODE_HASH + +// Set the base tolerance percentage for matching incoming IR messages. +void IRrecv::setTolerance(const uint8_t percent) { + _tolerance = std::min(percent, (uint8_t)100); +} + +// Get the base tolerance percentage for matching incoming IR messages. +uint8_t IRrecv::getTolerance(void) { return _tolerance; } + // Decodes the received IR message. // If the interrupt state is saved, we will immediately resume waiting // for the next IR message to avoid missing messages. @@ -335,7 +437,7 @@ bool IRrecv::decode(decode_results *results, irparams_t *save) { #if DECODE_DENON // Denon needs to precede Panasonic as it is a special case of Panasonic. DPRINTLN("Attempting Denon decode"); - if (decodeDenon(results, DENON_48_BITS) || decodeDenon(results, DENON_BITS) || + if (decodeDenon(results, kDenon48Bits) || decodeDenon(results, kDenonBits) || decodeDenon(results, kDenonLegacyBits)) return true; #endif @@ -523,6 +625,50 @@ bool IRrecv::decode(decode_results *results, irparams_t *save) { DPRINTLN("Attempting MITSUBISHIHEAVY (88 bit) decode"); if (decodeMitsubishiHeavy(results, kMitsubishiHeavy88Bits)) return true; #endif +#if DECODE_ARGO + DPRINTLN("Attempting Argo decode"); + if (decodeArgo(results)) return true; +#endif // DECODE_ARGO +#if DECODE_SHARP_AC + DPRINTLN("Attempting SHARP_AC decode"); + if (decodeSharpAc(results)) return true; +#endif +#if DECODE_GOODWEATHER + DPRINTLN("Attempting GOODWEATHER decode"); + if (decodeGoodweather(results)) return true; +#endif // DECODE_GOODWEATHER +#if DECODE_INAX + DPRINTLN("Attempting Inax decode"); + if (decodeInax(results)) return true; +#endif // DECODE_INAX +#if DECODE_TROTEC + DPRINTLN("Attempting Trotec decode"); + if (decodeTrotec(results)) return true; +#endif // DECODE_TROTEC +#if DECODE_DAIKIN160 + DPRINTLN("Attempting Daikin160 decode"); + if (decodeDaikin160(results)) return true; +#endif // DECODE_DAIKIN160 +#if DECODE_NEOCLIMA + DPRINTLN("Attempting Neoclima decode"); + if (decodeNeoclima(results)) return true; +#endif // DECODE_NEOCLIMA +#if DECODE_DAIKIN176 + DPRINTLN("Attempting Daikin176 decode"); + if (decodeDaikin176(results)) return true; +#endif // DECODE_DAIKIN176 +#if DECODE_DAIKIN128 + DPRINTLN("Attempting Daikin128 decode"); + if (decodeDaikin128(results)) return true; +#endif // DECODE_DAIKIN128 +#if DECODE_AMCOR + DPRINTLN("Attempting Amcor decode"); + if (decodeAmcor(results)) return true; +#endif // DECODE_AMCOR +#if DECODE_DAIKIN152 + DPRINTLN("Attempting Daikin152 decode"); + if (decodeDaikin152(results)) return true; +#endif // DECODE_DAIKIN152 #if DECODE_HASH // decodeHash returns a hash on any input. // Thus, it needs to be last in the list. @@ -537,6 +683,11 @@ bool IRrecv::decode(decode_results *results, irparams_t *save) { return false; } +// Convert the tolerance percentage into something valid. +uint8_t IRrecv::_validTolerance(const uint8_t percentage) { + return (percentage > 100) ? _tolerance : percentage; +} + // Calculate the lower bound of the nr. of ticks. // // Args: @@ -545,10 +696,12 @@ bool IRrecv::decode(decode_results *results, irparams_t *save) { // delta: A non-scaling amount to reduce usecs by. // Returns: // Nr. of ticks. -uint32_t IRrecv::ticksLow(uint32_t usecs, uint8_t tolerance, uint16_t delta) { +uint32_t IRrecv::ticksLow(const uint32_t usecs, const uint8_t tolerance, + const uint16_t delta) { // max() used to ensure the result can't drop below 0 before the cast. return ((uint32_t)std::max( - (int32_t)(usecs * (1.0 - tolerance / 100.0) - delta), 0)); + (int32_t)(usecs * (1.0 - _validTolerance(tolerance) / 100.0) - delta), + 0)); } // Calculate the upper bound of the nr. of ticks. @@ -559,8 +712,10 @@ uint32_t IRrecv::ticksLow(uint32_t usecs, uint8_t tolerance, uint16_t delta) { // delta: A non-scaling amount to increase usecs by. // Returns: // Nr. of ticks. -uint32_t IRrecv::ticksHigh(uint32_t usecs, uint8_t tolerance, uint16_t delta) { - return ((uint32_t)(usecs * (1.0 + tolerance / 100.0)) + 1 + delta); +uint32_t IRrecv::ticksHigh(const uint32_t usecs, const uint8_t tolerance, + const uint16_t delta) { + return ((uint32_t)(usecs * (1.0 + _validTolerance(tolerance) / 100.0)) + 1 + + delta); } // Check if we match a pulse(measured) with the desired within @@ -583,6 +738,17 @@ bool IRrecv::match(uint32_t measured, uint32_t desired, uint8_t tolerance, DPRINT(measured); DPRINT(" <= "); DPRINTLN(ticksHigh(desired, tolerance, delta)); +#ifdef UNIT_TEST + // Sanity checks that we don't have values that cause integer over/underflow. + // Only performed during testing so there is no performance hit in normal + // operation. + assert(ticksLow(desired, tolerance, delta) <= desired); + // Check if we overflowed. (UINT32_MAX >> 3 is approx 9 minutes!) + assert(ticksHigh(desired, tolerance, delta) < UINT32_MAX >> 3); + // Check if our high mark is below where we started. This could happen. + // If there is a legit case, then this should be removed. + assert(ticksHigh(desired, tolerance, delta) >= desired); +#endif // UNIT_TEST return (measured >= ticksLow(desired, tolerance, delta) && measured <= ticksHigh(desired, tolerance, delta)); } @@ -616,6 +782,17 @@ bool IRrecv::matchAtLeast(uint32_t measured, uint32_t desired, DPRINT(", "); DPRINT(ticksLow(MS_TO_USEC(irparams.timeout), tolerance, delta)); DPRINTLN(")]"); +#ifdef UNIT_TEST + // Sanity checks that we don't have values that cause integer over/underflow. + // Only performed during testing so there is no performance hit in normal + // operation. + assert(ticksLow(desired, tolerance, delta) <= desired); + // Check if we overflowed. (UINT32_MAX >> 3 is approx 9 minutes!) + assert(ticksHigh(desired, tolerance, delta) < UINT32_MAX >> 3); + // Check if our high mark is below where we started. This could happen. + // If there is a legit case, then this should be removed. + assert(ticksHigh(desired, tolerance, delta) >= desired); +#endif // UNIT_TEST // We really should never get a value of 0, except as the last value // in the buffer. If that is the case, then assume infinity and return true. if (measured == 0) return true; @@ -686,7 +863,7 @@ bool IRrecv::matchSpace(uint32_t measured, uint32_t desired, uint8_t tolerance, // Compare two tick values, returning 0 if newval is shorter, // 1 if newval is equal, and 2 if newval is longer // Use a tolerance of 20% -int16_t IRrecv::compare(uint16_t oldval, uint16_t newval) { +uint16_t IRrecv::compare(const uint16_t oldval, const uint16_t newval) { if (newval < oldval * 0.8) return 0; else if (oldval < newval * 0.8) @@ -702,14 +879,14 @@ int16_t IRrecv::compare(uint16_t oldval, uint16_t newval) { */ bool IRrecv::decodeHash(decode_results *results) { // Require at least some samples to prevent triggering on noise - if (results->rawlen < unknown_threshold) return false; + if (results->rawlen < _unknown_threshold) return false; int32_t hash = kFnvBasis32; // 'rawlen - 2' to avoid the look ahead from going out of bounds. // Should probably be -3 to avoid comparing the trailing space entry, // however it is left this way for compatibility with previously captured // values. for (uint16_t i = 1; i < results->rawlen - 2; i++) { - int16_t value = compare(results->rawbuf[i], results->rawbuf[i + 2]); + uint16_t value = compare(results->rawbuf[i], results->rawbuf[i + 2]); // Add value into the hash hash = (hash * kFnvPrime32) ^ value; } @@ -733,7 +910,7 @@ bool IRrecv::decodeHash(decode_results *results) { // onespace: Nr. of uSeconds in an expected space signal for a '1' bit. // zeromark: Nr. of uSeconds in an expected mark signal for a '0' bit. // zerospace: Nr. of uSeconds in an expected space signal for a '0' bit. -// tolerance: Percentage error margin to allow. (Def: kTolerance) +// tolerance: Percentage error margin to allow. (Def: kUseDefTol) // excess: Nr. of useconds. (Def: kMarkExcess) // MSBfirst: Bit order to save the data in. (Def: true) // Returns: @@ -765,4 +942,236 @@ match_result_t IRrecv::matchData( return result; } +// Match & decode the typical data section of an IR message. +// The bytes are stored at result_ptr. The first byte in the result equates to +// the first byte encountered, and so on. +// +// Args: +// data_ptr: A pointer to where we are at in the capture buffer. +// result_ptr: A pointer to where to start storing the bytes we decoded. +// remaining: The size of the capture buffer are remaining. +// nbytes: Nr. of data bytes we expect. +// onemark: Nr. of uSeconds in an expected mark signal for a '1' bit. +// onespace: Nr. of uSeconds in an expected space signal for a '1' bit. +// zeromark: Nr. of uSeconds in an expected mark signal for a '0' bit. +// zerospace: Nr. of uSeconds in an expected space signal for a '0' bit. +// tolerance: Percentage error margin to allow. (Def: kUseDefTol) +// excess: Nr. of useconds. (Def: kMarkExcess) +// MSBfirst: Bit order to save the data in. (Def: true) +// Returns: +// A uint16_t: If successful, how many buffer entries were used. Otherwise 0. +uint16_t IRrecv::matchBytes(volatile uint16_t *data_ptr, uint8_t *result_ptr, + const uint16_t remaining, const uint16_t nbytes, + const uint16_t onemark, const uint32_t onespace, + const uint16_t zeromark, const uint32_t zerospace, + const uint8_t tolerance, const int16_t excess, + const bool MSBfirst) { + // Check if there is enough capture buffer to possibly have the desired bytes. + if (remaining < nbytes * 8 * 2) return 0; // Nope, so abort. + uint16_t offset = 0; + for (uint16_t byte_pos = 0; byte_pos < nbytes; byte_pos++) { + match_result_t result = matchData(data_ptr + offset, 8, onemark, onespace, + zeromark, zerospace, tolerance, excess, + MSBfirst); + if (result.success == false) return 0; // Fail + result_ptr[byte_pos] = (uint8_t)result.data; + offset += result.used; + } + return offset; +} + +// Match & decode a generic/typical IR message. +// The data is stored in result_bits_ptr or result_bytes_ptr depending on flag +// `use_bits`. +// Values of 0 for hdrmark, hdrspace, footermark, or footerspace mean skip +// that requirement. +// +// Args: +// data_ptr: A pointer to where we are at in the capture buffer. +// result_bits_ptr: A pointer to where to start storing the bits we decoded. +// result_bytes_ptr: A pointer to where to start storing the bytes we decoded. +// use_bits: A flag indicating if we are to decode bits or bytes. +// remaining: The size of the capture buffer are remaining. +// nbits: Nr. of data bits we expect. +// hdrmark: Nr. of uSeconds for the expected header mark signal. +// hdrspace: Nr. of uSeconds for the expected header space signal. +// onemark: Nr. of uSeconds in an expected mark signal for a '1' bit. +// onespace: Nr. of uSeconds in an expected space signal for a '1' bit. +// zeromark: Nr. of uSeconds in an expected mark signal for a '0' bit. +// zerospace: Nr. of uSeconds in an expected space signal for a '0' bit. +// footermark: Nr. of uSeconds for the expected footer mark signal. +// footerspace: Nr. of uSeconds for the expected footer space/gap signal. +// atleast: Is the match on the footerspace a matchAtLeast or matchSpace? +// tolerance: Percentage error margin to allow. (Def: kUseDefTol) +// excess: Nr. of useconds. (Def: kMarkExcess) +// MSBfirst: Bit order to save the data in. (Def: true) +// Returns: +// A uint16_t: If successful, how many buffer entries were used. Otherwise 0. +uint16_t IRrecv::_matchGeneric(volatile uint16_t *data_ptr, + uint64_t *result_bits_ptr, + uint8_t *result_bytes_ptr, + const bool use_bits, + const uint16_t remaining, + const uint16_t nbits, + const uint16_t hdrmark, + const uint32_t hdrspace, + const uint16_t onemark, + const uint32_t onespace, + const uint16_t zeromark, + const uint32_t zerospace, + const uint16_t footermark, + const uint32_t footerspace, + const bool atleast, + const uint8_t tolerance, + const int16_t excess, + const bool MSBfirst) { + // If we are expecting byte sizes, check it's a factor of 8 or fail. + if (!use_bits && nbits % 8 != 0) return 0; + // Calculate how much remaining buffer is required. + uint16_t min_remaining = nbits * 2; + + if (hdrmark) min_remaining++; + if (hdrspace) min_remaining++; + if (footermark) min_remaining++; + // Don't need to extend for footerspace because it could be the end of message + + // Check if there is enough capture buffer to possibly have the message. + if (remaining < min_remaining) return 0; // Nope, so abort. + uint16_t offset = 0; + + // Header + if (hdrmark && !matchMark(*(data_ptr + offset++), hdrmark, tolerance, excess)) + return 0; + if (hdrspace && !matchSpace(*(data_ptr + offset++), hdrspace, tolerance, + excess)) + return 0; + + // Data + if (use_bits) { // Bits. + match_result_t result = IRrecv::matchData(data_ptr + offset, nbits, + onemark, onespace, + zeromark, zerospace, tolerance, + excess, MSBfirst); + if (!result.success) return 0; + *result_bits_ptr = result.data; + offset += result.used; + } else { // bytes + uint16_t data_used = IRrecv::matchBytes(data_ptr + offset, result_bytes_ptr, + remaining - offset, nbits / 8, + onemark, onespace, + zeromark, zerospace, tolerance, + excess, MSBfirst); + if (!data_used) return 0; + offset += data_used; + } + // Footer + if (footermark && !matchMark(*(data_ptr + offset++), footermark, tolerance, + excess)) + return 0; + // If we have something still to match & haven't reached the end of the buffer + if (footerspace && offset < remaining) { + if (atleast) { + if (!matchAtLeast(*(data_ptr + offset), footerspace, tolerance, excess)) + return 0; + } else { + if (!matchSpace(*(data_ptr + offset), footerspace, tolerance, excess)) + return 0; + } + offset++; + } + return offset; +} + +// Match & decode a generic/typical <= 64bit IR message. +// The data is stored at result_ptr. +// Values of 0 for hdrmark, hdrspace, footermark, or footerspace mean skip +// that requirement. +// +// Args: +// data_ptr: A pointer to where we are at in the capture buffer. +// result_ptr: A pointer to where to start storing the bits we decoded. +// remaining: The size of the capture buffer are remaining. +// nbits: Nr. of data bits we expect. +// hdrmark: Nr. of uSeconds for the expected header mark signal. +// hdrspace: Nr. of uSeconds for the expected header space signal. +// onemark: Nr. of uSeconds in an expected mark signal for a '1' bit. +// onespace: Nr. of uSeconds in an expected space signal for a '1' bit. +// zeromark: Nr. of uSeconds in an expected mark signal for a '0' bit. +// zerospace: Nr. of uSeconds in an expected space signal for a '0' bit. +// footermark: Nr. of uSeconds for the expected footer mark signal. +// footerspace: Nr. of uSeconds for the expected footer space/gap signal. +// atleast: Is the match on the footerspace a matchAtLeast or matchSpace? +// tolerance: Percentage error margin to allow. (Def: kUseDefTol) +// excess: Nr. of useconds. (Def: kMarkExcess) +// MSBfirst: Bit order to save the data in. (Def: true) +// Returns: +// A uint16_t: If successful, how many buffer entries were used. Otherwise 0. +uint16_t IRrecv::matchGeneric(volatile uint16_t *data_ptr, + uint64_t *result_ptr, + const uint16_t remaining, + const uint16_t nbits, + const uint16_t hdrmark, + const uint32_t hdrspace, + const uint16_t onemark, + const uint32_t onespace, + const uint16_t zeromark, + const uint32_t zerospace, + const uint16_t footermark, + const uint32_t footerspace, + const bool atleast, + const uint8_t tolerance, + const int16_t excess, + const bool MSBfirst) { + return _matchGeneric(data_ptr, result_ptr, NULL, true, remaining, nbits, + hdrmark, hdrspace, onemark, onespace, + zeromark, zerospace, footermark, footerspace, atleast, + tolerance, excess, MSBfirst); +} + +// Match & decode a generic/typical > 64bit IR message. +// The bytes are stored at result_ptr. The first byte in the result equates to +// the first byte encountered, and so on. +// Values of 0 for hdrmark, hdrspace, footermark, or footerspace mean skip +// that requirement. +// +// Args: +// data_ptr: A pointer to where we are at in the capture buffer. +// result_ptr: A pointer to where to start storing the bytes we decoded. +// remaining: The size of the capture buffer are remaining. +// nbits: Nr. of data bits we expect. +// hdrmark: Nr. of uSeconds for the expected header mark signal. +// hdrspace: Nr. of uSeconds for the expected header space signal. +// onemark: Nr. of uSeconds in an expected mark signal for a '1' bit. +// onespace: Nr. of uSeconds in an expected space signal for a '1' bit. +// zeromark: Nr. of uSeconds in an expected mark signal for a '0' bit. +// zerospace: Nr. of uSeconds in an expected space signal for a '0' bit. +// footermark: Nr. of uSeconds for the expected footer mark signal. +// footerspace: Nr. of uSeconds for the expected footer space/gap signal. +// atleast: Is the match on the footerspace a matchAtLeast or matchSpace? +// tolerance: Percentage error margin to allow. (Def: kUseDefTol) +// excess: Nr. of useconds. (Def: kMarkExcess) +// MSBfirst: Bit order to save the data in. (Def: true) +// Returns: +// A uint16_t: If successful, how many buffer entries were used. Otherwise 0. +uint16_t IRrecv::matchGeneric(volatile uint16_t *data_ptr, + uint8_t *result_ptr, + const uint16_t remaining, + const uint16_t nbits, + const uint16_t hdrmark, + const uint32_t hdrspace, + const uint16_t onemark, + const uint32_t onespace, + const uint16_t zeromark, + const uint32_t zerospace, + const uint16_t footermark, + const uint32_t footerspace, + const bool atleast, + const uint8_t tolerance, + const int16_t excess, + const bool MSBfirst) { + return _matchGeneric(data_ptr, NULL, result_ptr, false, remaining, nbits, + hdrmark, hdrspace, onemark, onespace, + zeromark, zerospace, footermark, footerspace, atleast, + tolerance, excess, MSBfirst); +} // End of IRrecv class ------------------- diff --git a/lib/IRremoteESP8266-2.6.0/src/IRrecv.h b/lib/IRremoteESP8266-2.6.5/src/IRrecv.h similarity index 54% rename from lib/IRremoteESP8266-2.6.0/src/IRrecv.h rename to lib/IRremoteESP8266-2.6.5/src/IRrecv.h index 0659f093e..72c168269 100644 --- a/lib/IRremoteESP8266-2.6.0/src/IRrecv.h +++ b/lib/IRremoteESP8266-2.6.5/src/IRrecv.h @@ -32,8 +32,9 @@ const uint8_t kIdleState = 2; const uint8_t kMarkState = 3; const uint8_t kSpaceState = 4; const uint8_t kStopState = 5; -const uint8_t kTolerance = 25; // default percent tolerance in measurements. -const uint16_t kRawTick = 2; // Capture tick to uSec factor. +const uint8_t kTolerance = 25; // default percent tolerance in measurements. +const uint8_t kUseDefTol = 255; // Indicate to use the class default tolerance. +const uint16_t kRawTick = 2; // Capture tick to uSec factor. #define RAWTICK kRawTick // Deprecated. For legacy user code support only. // How long (ms) before we give up wait for more data? // Don't exceed kMaxTimeoutMs without a good reason. @@ -51,6 +52,9 @@ const uint16_t kMaxTimeoutMs = kRawTick * (UINT16_MAX / MS_TO_USEC(1)); const uint32_t kFnvPrime32 = 16777619UL; const uint32_t kFnvBasis32 = 2166136261UL; +// Which of the ESP32 timers to use by default. (0-3) +const uint8_t kDefaultESP32Timer = 3; + #if DECODE_AC // Hitachi AC is the current largest state size. const uint16_t kStateSizeMax = kHitachiAc2StateLength; @@ -108,54 +112,123 @@ class decode_results { // main class for receiving IR class IRrecv { public: - explicit IRrecv(uint16_t recvpin, uint16_t bufsize = kRawBuf, - uint8_t timeout = kTimeoutMs, - bool save_buffer = false); // Constructor - ~IRrecv(); // Destructor +#if defined(ESP32) + explicit IRrecv(const uint16_t recvpin, const uint16_t bufsize = kRawBuf, + const uint8_t timeout = kTimeoutMs, + const bool save_buffer = false, + const uint8_t timer_num = kDefaultESP32Timer); // Constructor +#else // ESP32 + explicit IRrecv(const uint16_t recvpin, const uint16_t bufsize = kRawBuf, + const uint8_t timeout = kTimeoutMs, + const bool save_buffer = false); // Constructor +#endif // ESP32 + ~IRrecv(void); // Destructor + void setTolerance(const uint8_t percent = kTolerance); + uint8_t getTolerance(void); bool decode(decode_results *results, irparams_t *save = NULL); - void enableIRIn(); - void disableIRIn(); - void resume(); - uint16_t getBufSize(); + void enableIRIn(const bool pullup = false); + void disableIRIn(void); + void resume(void); + uint16_t getBufSize(void); #if DECODE_HASH - void setUnknownThreshold(uint16_t length); + void setUnknownThreshold(const uint16_t length); #endif - static bool match(uint32_t measured, uint32_t desired, - uint8_t tolerance = kTolerance, uint16_t delta = 0); - static bool matchMark(uint32_t measured, uint32_t desired, - uint8_t tolerance = kTolerance, - int16_t excess = kMarkExcess); - static bool matchSpace(uint32_t measured, uint32_t desired, - uint8_t tolerance = kTolerance, - int16_t excess = kMarkExcess); + bool match(const uint32_t measured, const uint32_t desired, + const uint8_t tolerance = kUseDefTol, + const uint16_t delta = 0); + bool matchMark(const uint32_t measured, const uint32_t desired, + const uint8_t tolerance = kUseDefTol, + const int16_t excess = kMarkExcess); + bool matchSpace(const uint32_t measured, const uint32_t desired, + const uint8_t tolerance = kUseDefTol, + const int16_t excess = kMarkExcess); #ifndef UNIT_TEST private: #endif irparams_t *irparams_save; + uint8_t _tolerance; +#if defined(ESP32) + uint8_t _timer_num; +#endif // defined(ESP32) #if DECODE_HASH - uint16_t unknown_threshold; + uint16_t _unknown_threshold; #endif // These are called by decode + uint8_t _validTolerance(const uint8_t percentage); void copyIrParams(volatile irparams_t *src, irparams_t *dst); - int16_t compare(uint16_t oldval, uint16_t newval); - static uint32_t ticksLow(uint32_t usecs, uint8_t tolerance = kTolerance, - uint16_t delta = 0); - static uint32_t ticksHigh(uint32_t usecs, uint8_t tolerance = kTolerance, - uint16_t delta = 0); - bool matchAtLeast(uint32_t measured, uint32_t desired, - uint8_t tolerance = kTolerance, uint16_t delta = 0); + uint16_t compare(const uint16_t oldval, const uint16_t newval); + uint32_t ticksLow(const uint32_t usecs, + const uint8_t tolerance = kUseDefTol, + const uint16_t delta = 0); + uint32_t ticksHigh(const uint32_t usecs, + const uint8_t tolerance = kUseDefTol, + const uint16_t delta = 0); + bool matchAtLeast(const uint32_t measured, const uint32_t desired, + const uint8_t tolerance = kUseDefTol, + const uint16_t delta = 0); + uint16_t _matchGeneric(volatile uint16_t *data_ptr, + uint64_t *result_bits_ptr, + uint8_t *result_ptr, + const bool use_bits, + const uint16_t remaining, + const uint16_t required, + const uint16_t hdrmark, + const uint32_t hdrspace, + const uint16_t onemark, + const uint32_t onespace, + const uint16_t zeromark, + const uint32_t zerospace, + const uint16_t footermark, + const uint32_t footerspace, + const bool atleast = false, + const uint8_t tolerance = kUseDefTol, + const int16_t excess = kMarkExcess, + const bool MSBfirst = true); match_result_t matchData(volatile uint16_t *data_ptr, const uint16_t nbits, const uint16_t onemark, const uint32_t onespace, const uint16_t zeromark, const uint32_t zerospace, - const uint8_t tolerance = kTolerance, + const uint8_t tolerance = kUseDefTol, const int16_t excess = kMarkExcess, const bool MSBfirst = true); + uint16_t matchBytes(volatile uint16_t *data_ptr, uint8_t *result_ptr, + const uint16_t remaining, const uint16_t nbytes, + const uint16_t onemark, const uint32_t onespace, + const uint16_t zeromark, const uint32_t zerospace, + const uint8_t tolerance = kUseDefTol, + const int16_t excess = kMarkExcess, + const bool MSBfirst = true); + uint16_t matchGeneric(volatile uint16_t *data_ptr, + uint64_t *result_ptr, + const uint16_t remaining, const uint16_t nbits, + const uint16_t hdrmark, const uint32_t hdrspace, + const uint16_t onemark, const uint32_t onespace, + const uint16_t zeromark, const uint32_t zerospace, + const uint16_t footermark, const uint32_t footerspace, + const bool atleast = false, + const uint8_t tolerance = kUseDefTol, + const int16_t excess = kMarkExcess, + const bool MSBfirst = true); + uint16_t matchGeneric(volatile uint16_t *data_ptr, uint8_t *result_ptr, + const uint16_t remaining, const uint16_t nbits, + const uint16_t hdrmark, const uint32_t hdrspace, + const uint16_t onemark, const uint32_t onespace, + const uint16_t zeromark, const uint32_t zerospace, + const uint16_t footermark, + const uint32_t footerspace, + const bool atleast = false, + const uint8_t tolerance = kUseDefTol, + const int16_t excess = kMarkExcess, + const bool MSBfirst = true); bool decodeHash(decode_results *results); #if (DECODE_NEC || DECODE_SHERWOOD || DECODE_AIWA_RC_T501 || SEND_SANYO) bool decodeNEC(decode_results *results, uint16_t nbits = kNECBits, bool strict = true); #endif +#if DECODE_ARGO + bool decodeArgo(decode_results *results, const uint16_t nbits = kArgoBits, + const bool strict = true); +#endif // DECODE_ARGO #if DECODE_SONY bool decodeSony(decode_results *results, uint16_t nbits = kSonyMinBits, bool strict = false); @@ -187,7 +260,7 @@ class IRrecv { #endif #if (DECODE_RC5 || DECODE_R6 || DECODE_LASERTAG || DECODE_MWM) int16_t getRClevel(decode_results *results, uint16_t *offset, uint16_t *used, - uint16_t bitTime, uint8_t tolerance = kTolerance, + uint16_t bitTime, uint8_t tolerance = kUseDefTol, int16_t excess = kMarkExcess, uint16_t delta = 0, uint8_t maxwidth = 3); #endif @@ -204,21 +277,27 @@ class IRrecv { bool strict = false); #endif #if (DECODE_PANASONIC || DECODE_DENON) - bool decodePanasonic(decode_results *results, uint16_t nbits = kPanasonicBits, - bool strict = false, - uint32_t manufacturer = kPanasonicManufacturer); + bool decodePanasonic(decode_results *results, + const uint16_t nbits = kPanasonicBits, + const bool strict = false, + const uint32_t manufacturer = kPanasonicManufacturer); #endif #if DECODE_LG bool decodeLG(decode_results *results, uint16_t nbits = kLgBits, bool strict = false); #endif +#if DECODE_INAX + bool decodeInax(decode_results *results, const uint16_t nbits = kInaxBits, + const bool strict = true); +#endif // DECODE_INAX #if DECODE_JVC bool decodeJVC(decode_results *results, uint16_t nbits = kJvcBits, bool strict = true); #endif #if DECODE_SAMSUNG - bool decodeSAMSUNG(decode_results *results, uint16_t nbits = kSamsungBits, - bool strict = true); + bool decodeSAMSUNG(decode_results *results, + const uint16_t nbits = kSamsungBits, + const bool strict = true); #endif #if DECODE_SAMSUNG bool decodeSamsung36(decode_results *results, @@ -226,8 +305,9 @@ class IRrecv { const bool strict = true); #endif #if DECODE_SAMSUNG_AC - bool decodeSamsungAC(decode_results *results, uint16_t nbits = kSamsungAcBits, - bool strict = true); + bool decodeSamsungAC(decode_results *results, + const uint16_t nbits = kSamsungAcBits, + const bool strict = true); #endif #if DECODE_WHYNTER bool decodeWhynter(decode_results *results, uint16_t nbits = kWhynterBits, @@ -238,7 +318,7 @@ class IRrecv { bool strict = true); #endif #if DECODE_DENON - bool decodeDenon(decode_results *results, uint16_t nbits = DENON_BITS, + bool decodeDenon(decode_results *results, uint16_t nbits = kDenonBits, bool strict = true); #endif #if DECODE_DISH @@ -246,8 +326,13 @@ class IRrecv { bool strict = true); #endif #if (DECODE_SHARP || DECODE_DENON) - bool decodeSharp(decode_results *results, uint16_t nbits = kSharpBits, - bool strict = true, bool expansion = true); + bool decodeSharp(decode_results *results, const uint16_t nbits = kSharpBits, + const bool strict = true, const bool expansion = true); +#endif +#if DECODE_SHARP_AC + bool decodeSharpAc(decode_results *results, + const uint16_t nbits = kSharpAcBits, + const bool strict = true); #endif #if DECODE_AIWA_RC_T501 bool decodeAiwaRCT501(decode_results *results, @@ -269,6 +354,26 @@ class IRrecv { bool decodeDaikin(decode_results *results, const uint16_t nbits = kDaikinBits, const bool strict = true); #endif +#if DECODE_DAIKIN128 + bool decodeDaikin128(decode_results *results, + const uint16_t nbits = kDaikin128Bits, + const bool strict = true); +#endif // DECODE_DAIKIN128 +#if DECODE_DAIKIN152 + bool decodeDaikin152(decode_results *results, + const uint16_t nbits = kDaikin152Bits, + const bool strict = true); +#endif // DECODE_DAIKIN152 +#if DECODE_DAIKIN160 + bool decodeDaikin160(decode_results *results, + const uint16_t nbits = kDaikin160Bits, + const bool strict = true); +#endif // DECODE_DAIKIN160 +#if DECODE_DAIKIN176 + bool decodeDaikin176(decode_results *results, + const uint16_t nbits = kDaikin176Bits, + const bool strict = true); +#endif // DECODE_DAIKIN176 #if DECODE_DAIKIN2 bool decodeDaikin2(decode_results *results, uint16_t nbits = kDaikin2Bits, bool strict = true); @@ -280,8 +385,13 @@ class IRrecv { #endif #if DECODE_TOSHIBA_AC bool decodeToshibaAC(decode_results *results, - uint16_t nbytes = kToshibaACBits, bool strict = true); + const uint16_t nbytes = kToshibaACBits, + const bool strict = true); #endif +#if DECODE_TROTEC + bool decodeTrotec(decode_results *results, const uint16_t nbits = kTrotecBits, + const bool strict = true); +#endif // DECODE_TROTEC #if DECODE_MIDEA bool decodeMidea(decode_results *results, uint16_t nbits = kMideaBits, bool strict = true); @@ -298,6 +408,11 @@ class IRrecv { bool decodeCarrierAC(decode_results *results, uint16_t nbits = kCarrierAcBits, bool strict = true); #endif +#if DECODE_GOODWEATHER + bool decodeGoodweather(decode_results *results, + const uint16_t nbits = kGoodweatherBits, + const bool strict = true); +#endif // DECODE_GOODWEATHER #if DECODE_GREE bool decodeGree(decode_results *results, uint16_t nbits = kGreeBits, bool strict = true); @@ -312,12 +427,14 @@ class IRrecv { bool strict = true); #endif #if (DECODE_HITACHI_AC || DECODE_HITACHI_AC2) - bool decodeHitachiAC(decode_results *results, uint16_t nbits = kHitachiAcBits, - bool strict = true); + bool decodeHitachiAC(decode_results *results, + const uint16_t nbits = kHitachiAcBits, + const bool strict = true); #endif #if DECODE_HITACHI_AC1 bool decodeHitachiAC1(decode_results *results, - uint16_t nbits = kHitachiAc1Bits, bool strict = true); + const uint16_t nbits = kHitachiAc1Bits, + const bool strict = true); #endif #if DECODE_GICABLE bool decodeGICable(decode_results *results, uint16_t nbits = kGicableBits, @@ -325,7 +442,8 @@ class IRrecv { #endif #if DECODE_WHIRLPOOL_AC bool decodeWhirlpoolAC(decode_results *results, - uint16_t nbits = kWhirlpoolAcBits, bool strict = true); + const uint16_t nbits = kWhirlpoolAcBits, + const bool strict = true); #endif #if DECODE_LUTRON bool decodeLutron(decode_results *results, uint16_t nbits = kLutronBits, @@ -337,7 +455,8 @@ class IRrecv { #endif #if DECODE_PANASONIC_AC bool decodePanasonicAC(decode_results *results, - uint16_t nbits = kPanasonicAcBits, bool strict = true); + const uint16_t nbits = kPanasonicAcBits, + const bool strict = true); #endif #if DECODE_PIONEER bool decodePioneer(decode_results *results, @@ -349,21 +468,33 @@ class IRrecv { bool strict = true); #endif #if DECODE_VESTEL_AC - bool decodeVestelAc(decode_results *results, uint16_t nbits = kVestelAcBits, - bool strict = true); + bool decodeVestelAc(decode_results *results, + const uint16_t nbits = kVestelAcBits, + const bool strict = true); #endif #if DECODE_TCL112AC - bool decodeTcl112Ac(decode_results *results, uint16_t nbits = kTcl112AcBits, - bool strict = true); + bool decodeTcl112Ac(decode_results *results, + const uint16_t nbits = kTcl112AcBits, + const bool strict = true); #endif #if DECODE_TECO - bool decodeTeco(decode_results *results, uint16_t nbits = kTecoBits, - bool strict = false); + bool decodeTeco(decode_results *results, const uint16_t nbits = kTecoBits, + const bool strict = false); #endif #if DECODE_LEGOPF bool decodeLegoPf(decode_results *results, const uint16_t nbits = kLegoPfBits, const bool strict = true); #endif +#if DECODE_NEOCLIMA +bool decodeNeoclima(decode_results *results, + const uint16_t nbits = kNeoclimaBits, + const bool strict = true); +#endif // DECODE_NEOCLIMA +#if DECODE_AMCOR +bool decodeAmcor(decode_results *results, + const uint16_t nbits = kAmcorBits, + const bool strict = true); +#endif // DECODE_AMCOR }; #endif // IRRECV_H_ diff --git a/lib/IRremoteESP8266-2.6.0/src/IRremoteESP8266.h b/lib/IRremoteESP8266-2.6.5/src/IRremoteESP8266.h similarity index 79% rename from lib/IRremoteESP8266-2.6.0/src/IRremoteESP8266.h rename to lib/IRremoteESP8266-2.6.5/src/IRremoteESP8266.h index b532cb1c0..0e1f00fa3 100644 --- a/lib/IRremoteESP8266-2.6.0/src/IRremoteESP8266.h +++ b/lib/IRremoteESP8266-2.6.5/src/IRremoteESP8266.h @@ -26,7 +26,7 @@ * DISH decode by marcosamarinho * Gree Heatpump sending added by Ville Skyttä (scop) * (derived from https://github.com/ToniA/arduino-heatpumpir/blob/master/GreeHeatpumpIR.cpp) - * Updated by markszabo (https://github.com/markszabo/IRremoteESP8266) for sending IR code on ESP8266 + * Updated by markszabo (https://github.com/crankyoldgit/IRremoteESP8266) for sending IR code on ESP8266 * Updated by Sebastien Warin (http://sebastien.warin.fr) for receiving IR code on ESP8266 * * Updated by sillyfrog for Daikin, adopted from @@ -47,16 +47,18 @@ #include #ifdef UNIT_TEST #include -#endif +#include +#endif // UNIT_TEST // Library Version -#define _IRREMOTEESP8266_VERSION_ "2.6.0" +#define _IRREMOTEESP8266_VERSION_ "2.6.5" // Supported IR protocols // Each protocol you include costs memory and, during decode, costs time // Disable (set to false) all the protocols you do not need/want! // The Air Conditioner protocols are the most expensive memory-wise. // -/* + +#ifdef USE_IR_REMOTE_FULL // full IR protocols #define DECODE_HASH true // Semi-unique code for unknown messages #define SEND_RAW true @@ -118,6 +120,9 @@ #define DECODE_SHARP true #define SEND_SHARP true +#define DECODE_SHARP_AC true +#define SEND_SHARP_AC true + #define DECODE_DENON true #define SEND_DENON true @@ -130,6 +135,9 @@ #define DECODE_FUJITSU_AC true #define SEND_FUJITSU_AC true +#define DECODE_INAX true +#define SEND_INAX true + #define DECODE_DAIKIN true #define SEND_DAIKIN true @@ -139,16 +147,19 @@ #define DECODE_GLOBALCACHE false // Not written. #define SEND_GLOBALCACHE true +#define DECODE_GOODWEATHER true +#define SEND_GOODWEATHER true + #define DECODE_GREE true #define SEND_GREE true #define DECODE_PRONTO false // Not written. #define SEND_PRONTO true -#define DECODE_ARGO false // Not written. +#define DECODE_ARGO true // Experimental #define SEND_ARGO true -#define DECODE_TROTEC false // Not implemented. +#define DECODE_TROTEC true #define SEND_TROTEC true #define DECODE_NIKAI true @@ -225,7 +236,26 @@ #define DECODE_DAIKIN216 true #define SEND_DAIKIN216 true -*/ + +#define DECODE_DAIKIN160 true +#define SEND_DAIKIN160 true + +#define DECODE_NEOCLIMA true +#define SEND_NEOCLIMA true + +#define DECODE_DAIKIN176 true +#define SEND_DAIKIN176 true + +#define DECODE_DAIKIN128 true +#define SEND_DAIKIN128 true + +#define DECODE_AMCOR true +#define SEND_AMCOR true + +#define DECODE_DAIKIN152 true +#define SEND_DAIKIN152 true + +#else // defined(FIRMWARE_IR) || defined(FIRMWARE_IR_CUSTOM) // full IR protocols // Tasmota supported protocols (less protocols is less code size) #define DECODE_HASH true // Semi-unique code for unknown messages @@ -236,7 +266,7 @@ #define SEND_NEC true #define DECODE_SHERWOOD false // Doesn't exist. Actually is DECODE_NEC -#define SEND_SHERWOOD false +#define SEND_SHERWOOD true #define DECODE_RC5 true #define SEND_RC5 true @@ -245,7 +275,7 @@ #define SEND_RC6 true #define DECODE_RCMM false -#define SEND_RCMM false +#define SEND_RCMM true #define DECODE_SONY true #define SEND_SONY true @@ -260,40 +290,43 @@ #define SEND_SAMSUNG true #define DECODE_SAMSUNG36 false -#define SEND_SAMSUNG36 false +#define SEND_SAMSUNG36 true #define DECODE_SAMSUNG_AC false -#define SEND_SAMSUNG_AC false +#define SEND_SAMSUNG_AC true #define DECODE_WHYNTER false -#define SEND_WHYNTER false +#define SEND_WHYNTER true #define DECODE_AIWA_RC_T501 false -#define SEND_AIWA_RC_T501 false +#define SEND_AIWA_RC_T501 true #define DECODE_LG true #define SEND_LG true #define DECODE_SANYO false -#define SEND_SANYO false +#define SEND_SANYO true #define DECODE_MITSUBISHI false -#define SEND_MITSUBISHI false +#define SEND_MITSUBISHI true #define DECODE_MITSUBISHI2 false -#define SEND_MITSUBISHI2 false +#define SEND_MITSUBISHI2 true #define DECODE_DISH false #define SEND_DISH true #define DECODE_SHARP false -#define SEND_SHARP false +#define SEND_SHARP true + +#define DECODE_SHARP_AC false +#define SEND_SHARP_AC true #define DECODE_DENON false -#define SEND_DENON false +#define SEND_DENON true #define DECODE_KELVINATOR false -#define SEND_KELVINATOR false +#define SEND_KELVINATOR true #define DECODE_MITSUBISHI_AC false // Beta. #define SEND_MITSUBISHI_AC true @@ -301,101 +334,121 @@ #define DECODE_FUJITSU_AC false #define SEND_FUJITSU_AC true +#define DECODE_INAX false +#define SEND_INAX true + #define DECODE_DAIKIN false -#define SEND_DAIKIN false +#define SEND_DAIKIN true #define DECODE_COOLIX false -#define SEND_COOLIX false +#define SEND_COOLIX true #define DECODE_GLOBALCACHE false // Not written. -#define SEND_GLOBALCACHE false +#define SEND_GLOBALCACHE true + +#define DECODE_GOODWEATHER false +#define SEND_GOODWEATHER true #define DECODE_GREE false -#define SEND_GREE false +#define SEND_GREE true #define DECODE_PRONTO false // Not written. -#define SEND_PRONTO false +#define SEND_PRONTO true -#define DECODE_ARGO false // Not written. -#define SEND_ARGO false +#define DECODE_ARGO false // Experimental +#define SEND_ARGO true -#define DECODE_TROTEC false // Not implemented. -#define SEND_TROTEC false +#define DECODE_TROTEC false +#define SEND_TROTEC true #define DECODE_NIKAI false -#define SEND_NIKAI false +#define SEND_NIKAI true #define DECODE_TOSHIBA_AC false -#define SEND_TOSHIBA_AC false +#define SEND_TOSHIBA_AC true #define DECODE_MAGIQUEST false -#define SEND_MAGIQUEST false +#define SEND_MAGIQUEST true #define DECODE_MIDEA false -#define SEND_MIDEA false +#define SEND_MIDEA true #define DECODE_LASERTAG false -#define SEND_LASERTAG false +#define SEND_LASERTAG true #define DECODE_CARRIER_AC false -#define SEND_CARRIER_AC false +#define SEND_CARRIER_AC true #define DECODE_HAIER_AC false -#define SEND_HAIER_AC false +#define SEND_HAIER_AC true #define DECODE_HITACHI_AC false -#define SEND_HITACHI_AC false +#define SEND_HITACHI_AC true #define DECODE_HITACHI_AC1 false -#define SEND_HITACHI_AC1 false +#define SEND_HITACHI_AC1 true #define DECODE_HITACHI_AC2 false -#define SEND_HITACHI_AC2 false +#define SEND_HITACHI_AC2 true #define DECODE_GICABLE false -#define SEND_GICABLE false +#define SEND_GICABLE true #define DECODE_HAIER_AC_YRW02 false -#define SEND_HAIER_AC_YRW02 false +#define SEND_HAIER_AC_YRW02 true #define DECODE_WHIRLPOOL_AC false -#define SEND_WHIRLPOOL_AC false +#define SEND_WHIRLPOOL_AC true #define DECODE_LUTRON false -#define SEND_LUTRON false +#define SEND_LUTRON true #define DECODE_ELECTRA_AC false -#define SEND_ELECTRA_AC false +#define SEND_ELECTRA_AC true #define DECODE_PANASONIC_AC false -#define SEND_PANASONIC_AC false +#define SEND_PANASONIC_AC true #define DECODE_MWM false -#define SEND_MWM false +#define SEND_MWM true #define DECODE_PIONEER false -#define SEND_PIONEER false +#define SEND_PIONEER true #define DECODE_DAIKIN2 false -#define SEND_DAIKIN2 false +#define SEND_DAIKIN2 true #define DECODE_VESTEL_AC false -#define SEND_VESTEL_AC false +#define SEND_VESTEL_AC true #define DECODE_TECO false -#define SEND_TECO false +#define SEND_TECO true #define DECODE_TCL112AC false -#define SEND_TCL112AC false +#define SEND_TCL112AC true #define DECODE_LEGOPF false -#define SEND_LEGOPF false +#define SEND_LEGOPF true #define DECODE_MITSUBISHIHEAVY false -#define SEND_MITSUBISHIHEAVY false +#define SEND_MITSUBISHIHEAVY true #define DECODE_DAIKIN216 false -#define SEND_DAIKIN216 false +#define SEND_DAIKIN216 true + +#define DECODE_DAIKIN160 false +#define SEND_DAIKIN160 true + +#define DECODE_NEOCLIMA false +#define SEND_NEOCLIMA true + +#define DECODE_DAIKIN176 false +#define SEND_DAIKIN176 true + +#define DECODE_DAIKIN128 false +#define SEND_DAIKIN128 true + +#endif // defined(FIRMWARE_IR) || defined(FIRMWARE_IR_CUSTOM) // full IR protocols #if (DECODE_ARGO || DECODE_DAIKIN || DECODE_FUJITSU_AC || DECODE_GREE || \ DECODE_KELVINATOR || DECODE_MITSUBISHI_AC || DECODE_TOSHIBA_AC || \ @@ -404,7 +457,9 @@ DECODE_WHIRLPOOL_AC || DECODE_SAMSUNG_AC || DECODE_ELECTRA_AC || \ DECODE_PANASONIC_AC || DECODE_MWM || DECODE_DAIKIN2 || \ DECODE_VESTEL_AC || DECODE_TCL112AC || DECODE_MITSUBISHIHEAVY || \ - DECODE_DAIKIN216) + DECODE_DAIKIN216 || DECODE_SHARP_AC || DECODE_DAIKIN160 || \ + DECODE_NEOCLIMA || DECODE_DAIKIN176 || DECODE_DAIKIN128 || \ + DECODE_AMCOR || DECODE_DAIKIN152) #define DECODE_AC true // We need some common infrastructure for decoding A/Cs. #else #define DECODE_AC false // We don't need that infrastructure. @@ -413,7 +468,7 @@ // Use millisecond 'delay()' calls where we can to avoid tripping the WDT. // Note: If you plan to send IR messages in the callbacks of the AsyncWebserver // library, you need to set ALLOW_DELAY_CALLS to false. -// Ref: https://github.com/markszabo/IRremoteESP8266/issues/430 +// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/430 #define ALLOW_DELAY_CALLS true /* @@ -485,8 +540,17 @@ enum decode_type_t { MITSUBISHI_HEAVY_88, MITSUBISHI_HEAVY_152, // 60 DAIKIN216, + SHARP_AC, + GOODWEATHER, + INAX, + DAIKIN160, // 65 + NEOCLIMA, + DAIKIN176, + DAIKIN128, + AMCOR, + DAIKIN152, // 70 // Add new entries before this one, and update it to point to the last entry. - kLastDecodeType = DAIKIN216, + kLastDecodeType = DAIKIN152, }; // Message lengths & required repeat values @@ -495,10 +559,14 @@ const uint16_t kSingleRepeat = 1; const uint16_t kAiwaRcT501Bits = 15; const uint16_t kAiwaRcT501MinRepeats = kSingleRepeat; +const uint16_t kAmcorStateLength = 8; +const uint16_t kAmcorBits = kAmcorStateLength * 8; +const uint16_t kAmcorDefaultRepeat = kSingleRepeat; const uint16_t kArgoStateLength = 12; +const uint16_t kArgoBits = kArgoStateLength * 8; const uint16_t kArgoDefaultRepeat = kNoRepeat; const uint16_t kCoolixBits = 24; -const uint16_t kCoolixDefaultRepeat = 1; +const uint16_t kCoolixDefaultRepeat = kSingleRepeat; const uint16_t kCarrierAcBits = 32; const uint16_t kCarrierAcMinRepeat = kNoRepeat; const uint16_t kDaikinStateLength = 35; @@ -509,15 +577,29 @@ const uint16_t kDaikinDefaultRepeat = kNoRepeat; const uint16_t kDaikin2StateLength = 39; const uint16_t kDaikin2Bits = kDaikin2StateLength * 8; const uint16_t kDaikin2DefaultRepeat = kNoRepeat; +const uint16_t kDaikin160StateLength = 20; +const uint16_t kDaikin160Bits = kDaikin160StateLength * 8; +const uint16_t kDaikin160DefaultRepeat = kNoRepeat; +const uint16_t kDaikin128StateLength = 16; +const uint16_t kDaikin128Bits = kDaikin128StateLength * 8; +const uint16_t kDaikin128DefaultRepeat = kNoRepeat; +const uint16_t kDaikin152StateLength = 19; +const uint16_t kDaikin152Bits = kDaikin152StateLength * 8; +const uint16_t kDaikin152DefaultRepeat = kNoRepeat; +const uint16_t kDaikin176StateLength = 22; +const uint16_t kDaikin176Bits = kDaikin176StateLength * 8; +const uint16_t kDaikin176DefaultRepeat = kNoRepeat; const uint16_t kDaikin216StateLength = 27; const uint16_t kDaikin216Bits = kDaikin216StateLength * 8; const uint16_t kDaikin216DefaultRepeat = kNoRepeat; const uint16_t kDenonBits = 15; +const uint16_t kDenon48Bits = 48; const uint16_t kDenonLegacyBits = 14; const uint16_t kDishBits = 16; const uint16_t kDishMinRepeat = 3; const uint16_t kElectraAcStateLength = 13; const uint16_t kElectraAcBits = kElectraAcStateLength * 8; +const uint16_t kElectraAcMinRepeat = kNoRepeat; const uint16_t kFujitsuAcMinRepeat = kNoRepeat; const uint16_t kFujitsuAcStateLength = 16; const uint16_t kFujitsuAcStateLengthShort = 7; @@ -525,6 +607,8 @@ const uint16_t kFujitsuAcBits = kFujitsuAcStateLength * 8; const uint16_t kFujitsuAcMinBits = (kFujitsuAcStateLengthShort - 1) * 8; const uint16_t kGicableBits = 16; const uint16_t kGicableMinRepeat = kSingleRepeat; +const uint16_t kGoodweatherBits = 48; +const uint16_t kGoodweatherMinRepeat = kNoRepeat; const uint16_t kGreeStateLength = 8; const uint16_t kGreeBits = kGreeStateLength * 8; const uint16_t kGreeDefaultRepeat = kNoRepeat; @@ -541,6 +625,8 @@ const uint16_t kHitachiAc1StateLength = 13; const uint16_t kHitachiAc1Bits = kHitachiAc1StateLength * 8; const uint16_t kHitachiAc2StateLength = 53; const uint16_t kHitachiAc2Bits = kHitachiAc2StateLength * 8; +const uint16_t kInaxBits = 24; +const uint16_t kInaxMinRepeat = kSingleRepeat; const uint16_t kJvcBits = 16; const uint16_t kKelvinatorStateLength = 16; const uint16_t kKelvinatorBits = kKelvinatorStateLength * 8; @@ -570,6 +656,9 @@ const uint16_t kMitsubishiHeavy152Bits = kMitsubishiHeavy152StateLength * 8; const uint16_t kMitsubishiHeavy152MinRepeat = kNoRepeat; const uint16_t kNikaiBits = 24; const uint16_t kNECBits = 32; +const uint16_t kNeoclimaStateLength = 12; +const uint16_t kNeoclimaBits = kNeoclimaStateLength * 8; +const uint16_t kNeoclimaMinRepeat = kNoRepeat; const uint16_t kPanasonicBits = 48; const uint32_t kPanasonicManufacturer = 0x4004; const uint16_t kPanasonicAcStateLength = 27; @@ -600,6 +689,9 @@ const uint16_t kSanyoLC7461Bits = (kSanyoLC7461AddressBits + const uint8_t kSharpAddressBits = 5; const uint8_t kSharpCommandBits = 8; const uint16_t kSharpBits = kSharpAddressBits + kSharpCommandBits + 2; // 15 +const uint16_t kSharpAcStateLength = 13; +const uint16_t kSharpAcBits = kSharpAcStateLength * 8; // 104 +const uint16_t kSharpAcDefaultRepeat = kNoRepeat; const uint8_t kSherwoodBits = kNECBits; const uint16_t kSherwoodMinRepeat = kSingleRepeat; const uint16_t kSony12Bits = 12; @@ -616,6 +708,7 @@ const uint16_t kToshibaACStateLength = 9; const uint16_t kToshibaACBits = kToshibaACStateLength * 8; const uint16_t kToshibaACMinRepeat = kSingleRepeat; const uint16_t kTrotecStateLength = 9; +const uint16_t kTrotecBits = kTrotecStateLength * 8; const uint16_t kTrotecDefaultRepeat = kNoRepeat; const uint16_t kWhirlpoolAcStateLength = 21; const uint16_t kWhirlpoolAcBits = kWhirlpoolAcStateLength * 8; @@ -631,7 +724,7 @@ const uint8_t kVestelAcBits = 56; #define CARRIER_AC_BITS kCarrierAcBits #define DAIKIN_COMMAND_LENGTH kDaikinStateLength #define DENON_BITS kDenonBits -#define DENON_48_BITS kPanasonicBits +#define DENON_48_BITS kDenon48Bits #define DENON_LEGACY_BITS kDenonLegacyBits #define DISH_BITS kDishBits #define FUJITSU_AC_MIN_REPEAT kFujitsuAcMinRepeat @@ -700,9 +793,10 @@ const uint8_t kVestelAcBits = 56; // Create a no-op F() macro so the code base still compiles outside of the // Arduino framework. Thus we can safely use the Arduino 'F()' macro through-out // the code base. That macro stores constants in Flash (PROGMEM) memory. -// See: https://github.com/markszabo/IRremoteESP8266/issues/667 +// See: https://github.com/crankyoldgit/IRremoteESP8266/issues/667 #define F(x) x #endif // F +typedef std::string String; #endif // UNIT_TEST #endif // IRREMOTEESP8266_H_ diff --git a/lib/IRremoteESP8266-2.6.0/src/IRsend.cpp b/lib/IRremoteESP8266-2.6.5/src/IRsend.cpp similarity index 70% rename from lib/IRremoteESP8266-2.6.0/src/IRsend.cpp rename to lib/IRremoteESP8266-2.6.5/src/IRsend.cpp index 22c0c874b..b094fdff5 100644 --- a/lib/IRremoteESP8266-2.6.0/src/IRsend.cpp +++ b/lib/IRremoteESP8266-2.6.5/src/IRsend.cpp @@ -16,7 +16,7 @@ #include "IRtimer.h" // Originally from https://github.com/shirriff/Arduino-IRremote/ -// Updated by markszabo (https://github.com/markszabo/IRremoteESP8266) for +// Updated by markszabo (https://github.com/crankyoldgit/IRremoteESP8266) for // sending IR code on ESP8266 // IRsend ---------------------------------------------------------------------- @@ -491,178 +491,341 @@ void IRsend::sendRaw(uint16_t buf[], uint16_t len, uint16_t hz) { } #endif // SEND_RAW +// Get the minimum number of repeats for a given protocol. +// Args: +// protocol: Protocol number/type of the message you want to send. +// Returns: +// int16_t: The number of repeats required. +uint16_t IRsend::minRepeats(const decode_type_t protocol) { + switch (protocol) { + // Single repeats + case AIWA_RC_T501: + case AMCOR: + case COOLIX: + case GICABLE: + case INAX: + case MITSUBISHI: + case MITSUBISHI2: + case MITSUBISHI_AC: + case SHERWOOD: + case TOSHIBA_AC: + return kSingleRepeat; + // Special + case DISH: + return kDishMinRepeat; + case SONY: + return kSonyMinRepeat; + default: + return kNoRepeat; + } +} + +// Get the default number of bits for a given protocol. +// Args: +// protocol: Protocol number/type you want the default nr. of bits for. +// Returns: +// int16_t: The number of bits. +uint16_t IRsend::defaultBits(const decode_type_t protocol) { + switch (protocol) { + case RC5: + return 12; + case LASERTAG: + case RC5X: + return 13; + case AIWA_RC_T501: + case DENON: + case SHARP: + return 15; + case DISH: + case GICABLE: + case JVC: + case LEGOPF: + case MITSUBISHI: + case MITSUBISHI2: + return 16; + case RC6: + case SONY: + return 20; + case COOLIX: + case INAX: + case NIKAI: + case RCMM: + return 24; + case LG: + case LG2: + return 28; + case CARRIER_AC: + case NEC: + case NEC_LIKE: + case SAMSUNG: + case SHERWOOD: + case WHYNTER: + return 32; + case LUTRON: + case TECO: + return 35; + case SAMSUNG36: + return 36; + case SANYO_LC7461: + return kSanyoLC7461Bits; // 42 + case GOODWEATHER: + case MIDEA: + case PANASONIC: + return 48; + case MAGIQUEST: + case VESTEL_AC: + return 56; + case AMCOR: + case PIONEER: + return 64; + case ARGO: + return kArgoBits; + case DAIKIN: + return kDaikinBits; + case DAIKIN128: + return kDaikin128Bits; + case DAIKIN152: + return kDaikin152Bits; + case DAIKIN160: + return kDaikin160Bits; + case DAIKIN176: + return kDaikin176Bits; + case DAIKIN2: + return kDaikin2Bits; + case DAIKIN216: + return kDaikin216Bits; + case ELECTRA_AC: + return kElectraAcBits; + case GREE: + return kGreeBits; + case HAIER_AC: + return kHaierACBits; + case HAIER_AC_YRW02: + return kHaierACYRW02Bits; + case HITACHI_AC: + return kHitachiAcBits; + case HITACHI_AC1: + return kHitachiAc1Bits; + case HITACHI_AC2: + return kHitachiAc2Bits; + case KELVINATOR: + return kKelvinatorBits; + case MITSUBISHI_AC: + return kMitsubishiACBits; + case MITSUBISHI_HEAVY_152: + return kMitsubishiHeavy152Bits; + case MITSUBISHI_HEAVY_88: + return kMitsubishiHeavy88Bits; + case NEOCLIMA: + return kNeoclimaBits; + case PANASONIC_AC: + return kNeoclimaBits; + case SAMSUNG_AC: + return kSamsungAcBits; + case SHARP_AC: + return kSharpAcBits; + case TCL112AC: + return kTcl112AcBits; + case TOSHIBA_AC: + return kToshibaACBits; + case TROTEC: + return kTrotecBits; + case WHIRLPOOL_AC: + return kWhirlpoolAcBits; + // No default amount of bits. + case FUJITSU_AC: + case MWM: + default: + return 0; + } +} + // Send a simple (up to 64 bits) IR message of a given type. // An unknown/unsupported type will do nothing. // Args: // type: Protocol number/type of the message you want to send. // data: The data you want to send (up to 64 bits). // nbits: How many bits long the message is to be. +// repeat: How many repeats to do? // Returns: // bool: True if it is a type we can attempt to send, false if not. -bool IRsend::send(decode_type_t type, uint64_t data, uint16_t nbits) { +bool IRsend::send(const decode_type_t type, const uint64_t data, + const uint16_t nbits, const uint16_t repeat) { + uint16_t min_repeat = std::max(IRsend::minRepeats(type), repeat); switch (type) { #if SEND_AIWA_RC_T501 case AIWA_RC_T501: - sendAiwaRCT501(data, nbits); + sendAiwaRCT501(data, nbits, min_repeat); break; #endif #if SEND_CARRIER_AC case CARRIER_AC: - sendCarrierAC(data, nbits); + sendCarrierAC(data, nbits, min_repeat); break; #endif #if SEND_COOLIX case COOLIX: - sendCOOLIX(data, nbits); + sendCOOLIX(data, nbits, min_repeat); break; #endif #if SEND_DENON case DENON: - sendDenon(data, nbits); + sendDenon(data, nbits, min_repeat); break; #endif #if SEND_DISH case DISH: - sendDISH(data, nbits); + sendDISH(data, nbits, min_repeat); break; #endif #if SEND_GICABLE case GICABLE: - sendGICable(data, nbits); + sendGICable(data, nbits, min_repeat); + break; +#endif +#if SEND_GOODWEATHER + case GOODWEATHER: + sendGoodweather(data, nbits, min_repeat); break; #endif #if SEND_GREE case GREE: - sendGree(data, nbits); + sendGree(data, nbits, min_repeat); break; #endif +#if SEND_INAX + case INAX: + sendInax(data, nbits, min_repeat); + break; +#endif // SEND_INAX #if SEND_JVC case JVC: - sendJVC(data, nbits); + sendJVC(data, nbits, min_repeat); break; #endif #if SEND_LASERTAG case LASERTAG: - sendLasertag(data, nbits); + sendLasertag(data, nbits, min_repeat); break; #endif #if SEND_LEGOPF case LEGOPF: - sendLegoPf(data, nbits); + sendLegoPf(data, nbits, min_repeat); break; #endif #if SEND_LG case LG: - sendLG(data, nbits); + sendLG(data, nbits, min_repeat); break; case LG2: - sendLG2(data, nbits); + sendLG2(data, nbits, min_repeat); break; #endif #if SEND_LUTRON case LUTRON: - sendLutron(data, nbits); + sendLutron(data, nbits, min_repeat); break; #endif #if SEND_MAGIQUEST case MAGIQUEST: - sendMagiQuest(data, nbits); + sendMagiQuest(data, nbits, min_repeat); break; #endif #if SEND_MIDEA case MIDEA: - sendMidea(data, nbits); + sendMidea(data, nbits, min_repeat); break; #endif #if SEND_MITSUBISHI case MITSUBISHI: - sendMitsubishi(data, nbits); + sendMitsubishi(data, nbits, min_repeat); break; #endif #if SEND_MITSUBISHI2 case MITSUBISHI2: - sendMitsubishi2(data, nbits); + sendMitsubishi2(data, nbits, min_repeat); break; #endif #if SEND_NIKAI case NIKAI: - sendNikai(data, nbits); + sendNikai(data, nbits, min_repeat); break; #endif #if SEND_NEC case NEC: case NEC_LIKE: - sendNEC(data, nbits); + sendNEC(data, nbits, min_repeat); break; #endif #if SEND_PANASONIC case PANASONIC: - sendPanasonic64(data, nbits); + sendPanasonic64(data, nbits, min_repeat); break; #endif #if SEND_PIONEER case PIONEER: - sendPioneer(data, nbits); + sendPioneer(data, nbits, min_repeat); break; #endif #if SEND_RC5 case RC5: - sendRC5(data, nbits); + case RC5X: + sendRC5(data, nbits, min_repeat); break; #endif #if SEND_RC6 case RC6: - sendRC6(data, nbits); + sendRC6(data, nbits, min_repeat); break; #endif #if SEND_RCMM case RCMM: - sendRCMM(data, nbits); + sendRCMM(data, nbits, min_repeat); break; #endif #if SEND_SAMSUNG case SAMSUNG: - sendSAMSUNG(data, nbits); + sendSAMSUNG(data, nbits, min_repeat); break; #endif #if SEND_SAMSUNG36 case SAMSUNG36: - sendSamsung36(data, nbits); + sendSamsung36(data, nbits, min_repeat); break; #endif #if SEND_SANYO case SANYO_LC7461: - sendSanyoLC7461(data, nbits); + sendSanyoLC7461(data, nbits, min_repeat); break; #endif #if SEND_SHARP case SHARP: - sendSharpRaw(data, nbits); + sendSharpRaw(data, nbits, min_repeat); break; #endif #if SEND_SHERWOOD case SHERWOOD: - sendSherwood(data, nbits); + sendSherwood(data, nbits, min_repeat); break; #endif #if SEND_SONY case SONY: - sendSony(data, nbits); + sendSony(data, nbits, min_repeat); break; #endif #if SEND_TECO case TECO: - sendTeco(data, nbits); + sendTeco(data, nbits, min_repeat); break; #endif #if SEND_VESTEL_AC case VESTEL_AC: - sendVestelAc(data, nbits); + sendVestelAc(data, nbits, min_repeat); break; #endif #if SEND_WHYNTER case WHYNTER: - sendWhynter(data, nbits); + sendWhynter(data, nbits, min_repeat); break; #endif default: @@ -670,3 +833,168 @@ bool IRsend::send(decode_type_t type, uint64_t data, uint16_t nbits) { } return true; } + +// Send a complex (>= 64 bits) IR message of a given type. +// An unknown/unsupported type will do nothing. +// Args: +// type: Protocol number/type of the message you want to send. +// state: A pointer to the array of bytes that make up the state[]. +// nbytes: How many bytes are in the state. +// Returns: +// bool: True if it is a type we can attempt to send, false if not. +bool IRsend::send(const decode_type_t type, const unsigned char *state, + const uint16_t nbytes) { + switch (type) { +#if SEND_AMCOR + case AMCOR: + sendAmcor(state, nbytes); + break; +#endif +#if SEND_ARGO + case ARGO: + sendArgo(state, nbytes); + break; +#endif // SEND_ARGO +#if SEND_DAIKIN + case DAIKIN: + sendDaikin(state, nbytes); + break; +#endif // SEND_DAIKIN +#if SEND_DAIKIN128 + case DAIKIN128: + sendDaikin128(state, nbytes); + break; +#endif // SEND_DAIKIN128 +#if SEND_DAIKIN152 + case DAIKIN152: + sendDaikin152(state, nbytes); + break; +#endif // SEND_DAIKIN152 +#if SEND_DAIKIN160 + case DAIKIN160: + sendDaikin160(state, nbytes); + break; +#endif // SEND_DAIKIN160 +#if SEND_DAIKIN176 + case DAIKIN176: + sendDaikin176(state, nbytes); + break; +#endif // SEND_DAIKIN176 +#if SEND_DAIKIN2 + case DAIKIN2: + sendDaikin2(state, nbytes); + break; +#endif // SEND_DAIKIN2 +#if SEND_DAIKIN216 + case DAIKIN216: + sendDaikin216(state, nbytes); + break; +#endif // SEND_DAIKIN216 +#if SEND_ELECTRA_AC + case ELECTRA_AC: + sendElectraAC(state, nbytes); + break; +#endif // SEND_ELECTRA_AC +#if SEND_FUJITSU_AC + case FUJITSU_AC: + sendFujitsuAC(state, nbytes); + break; +#endif // SEND_FUJITSU_AC +#if SEND_GREE + case GREE: + sendGree(state, nbytes); + break; +#endif // SEND_GREE +#if SEND_HAIER_AC + case HAIER_AC: + sendHaierAC(state, nbytes); + break; +#endif // SEND_HAIER_AC +#if SEND_HAIER_AC_YRW02 + case HAIER_AC_YRW02: + sendHaierACYRW02(state, nbytes); + break; +#endif // SEND_HAIER_AC_YRW02 +#if SEND_HITACHI_AC + case HITACHI_AC: + sendHitachiAC(state, nbytes); + break; +#endif // SEND_HITACHI_AC +#if SEND_HITACHI_AC1 + case HITACHI_AC1: + sendHitachiAC1(state, nbytes); + break; +#endif // SEND_HITACHI_AC1 +#if SEND_HITACHI_AC2 + case HITACHI_AC2: + sendHitachiAC2(state, nbytes); + break; +#endif // SEND_HITACHI_AC2 +#if SEND_KELVINATOR + case KELVINATOR: + sendKelvinator(state, nbytes); + break; +#endif // SEND_KELVINATOR +#if SEND_MITSUBISHI_AC + case MITSUBISHI_AC: + sendMitsubishiAC(state, nbytes); + break; +#endif // SEND_MITSUBISHI_AC +#if SEND_MITSUBISHIHEAVY + case MITSUBISHI_HEAVY_88: + sendMitsubishiHeavy88(state, nbytes); + break; + case MITSUBISHI_HEAVY_152: + sendMitsubishiHeavy152(state, nbytes); + break; +#endif // SEND_MITSUBISHIHEAVY +#if SEND_MWM + case MWM: + sendMWM(state, nbytes); + break; +#endif // SEND_MWM +#if SEND_NEOCLIMA + case NEOCLIMA: + sendNeoclima(state, nbytes); + break; +#endif // SEND_NEOCLIMA +#if SEND_PANASONIC_AC + case PANASONIC_AC: + sendPanasonicAC(state, nbytes); + break; +#endif // SEND_PANASONIC_AC +#if SEND_SAMSUNG_AC + case SAMSUNG_AC: + sendSamsungAC(state, nbytes); + break; +#endif // SEND_SAMSUNG_AC +#if SEND_SHARP_AC + case SHARP_AC: + sendSharpAc(state, nbytes); + break; +#endif // SEND_SHARP_AC +#if SEND_TCL112AC + case TCL112AC: + sendTcl112Ac(state, nbytes); + break; +#endif // SEND_TCL112AC +#if SEND_TOSHIBA_AC + case TOSHIBA_AC: + sendToshibaAC(state, nbytes); + break; +#endif // SEND_TOSHIBA_AC +#if SEND_TROTEC + case TROTEC: + sendTrotec(state, nbytes); + break; +#endif // SEND_TROTEC +#if SEND_WHIRLPOOL_AC + case WHIRLPOOL_AC: + sendWhirlpoolAC(state, nbytes); + break; +#endif // SEND_WHIRLPOOL_AC + default: + return false; + } + return true; +} diff --git a/lib/IRremoteESP8266-2.6.0/src/IRsend.h b/lib/IRremoteESP8266-2.6.5/src/IRsend.h similarity index 58% rename from lib/IRremoteESP8266-2.6.0/src/IRsend.h rename to lib/IRremoteESP8266-2.6.5/src/IRsend.h index b065f6582..cbccee479 100644 --- a/lib/IRremoteESP8266-2.6.0/src/IRsend.h +++ b/lib/IRremoteESP8266-2.6.5/src/IRsend.h @@ -9,7 +9,7 @@ #include "IRremoteESP8266.h" // Originally from https://github.com/shirriff/Arduino-IRremote/ -// Updated by markszabo (https://github.com/markszabo/IRremoteESP8266) for +// Updated by markszabo (https://github.com/crankyoldgit/IRremoteESP8266) for // sending IR code on ESP8266 #if TEST || UNIT_TEST @@ -21,8 +21,17 @@ // Constants // Offset (in microseconds) to use in Period time calculations to account for // code excution time in producing the software PWM signal. -// Value was calculated on Wemos D1 mini using v2.4.1 with v2.4.0 ESP core +#if defined(ESP32) +// Calculated on a generic ESP-WROOM-32 board with v3.2-18 SDK @ 240MHz +const int8_t kPeriodOffset = -2; +#elif (defined(ESP8266) && F_CPU == 160000000L) // NOLINT(whitespace/parens) +// Calculated on an ESP8266 NodeMCU v2 board using: +// v2.6.0 with v2.5.2 ESP core @ 160MHz +const int8_t kPeriodOffset = -2; +#else // (defined(ESP8266) && F_CPU == 160000000L) +// Calculated on ESP8266 Wemos D1 mini using v2.4.1 with v2.4.0 ESP core @ 40MHz const int8_t kPeriodOffset = -5; +#endif // (defined(ESP8266) && F_CPU == 160000000L) const uint8_t kDutyDefault = 50; // Percentage const uint8_t kDutyMax = 100; // Percentage // delayMicroseconds() is only accurate to 16383us. @@ -40,6 +49,8 @@ namespace stdAc { kHeat = 2, kDry = 3, kFan = 4, + // Add new entries before this one, and update it to point to the last entry + kLastOpmodeEnum = kFan, }; enum class fanspeed_t { @@ -49,6 +60,8 @@ namespace stdAc { kMedium = 3, kHigh = 4, kMax = 5, + // Add new entries before this one, and update it to point to the last entry + kLastFanspeedEnum = kMax, }; enum class swingv_t { @@ -59,6 +72,8 @@ namespace stdAc { kMiddle = 3, kLow = 4, kLowest = 5, + // Add new entries before this one, and update it to point to the last entry + kLastSwingvEnum = kLowest, }; enum class swingh_t { @@ -69,7 +84,32 @@ namespace stdAc { kMiddle = 3, kRight = 4, kRightMax = 5, + kWide = 6, // a.k.a. left & right at the same time. + // Add new entries before this one, and update it to point to the last entry + kLastSwinghEnum = kWide, }; + + // Structure to hold a common A/C state. + typedef struct { + decode_type_t protocol; + int16_t model; + bool power; + stdAc::opmode_t mode; + float degrees; + bool celsius; + stdAc::fanspeed_t fanspeed; + stdAc::swingv_t swingv; + stdAc::swingh_t swingh; + bool quiet; + bool turbo; + bool econo; + bool light; + bool filter; + bool clean; + bool beep; + int16_t sleep; + int16_t clock; + } state_t; }; // namespace stdAc // Classes @@ -109,7 +149,12 @@ class IRsend { const uint8_t *dataptr, const uint16_t nbytes, const uint16_t frequency, const bool MSBfirst, const uint16_t repeat, const uint8_t dutycycle); - bool send(decode_type_t type, uint64_t data, uint16_t nbits); + static uint16_t minRepeats(const decode_type_t protocol); + static uint16_t defaultBits(const decode_type_t protocol); + bool send(const decode_type_t type, const uint64_t data, + const uint16_t nbits, const uint16_t repeat = kNoRepeat); + bool send(const decode_type_t type, const uint8_t state[], + const uint16_t nbytes); #if (SEND_NEC || SEND_SHERWOOD || SEND_AIWA_RC_T501 || SEND_SANYO) void sendNEC(uint64_t data, uint16_t nbits = kNECBits, uint16_t repeat = kNoRepeat); @@ -131,9 +176,9 @@ class IRsend { uint16_t repeat = kSherwoodMinRepeat); #endif #if SEND_SAMSUNG - void sendSAMSUNG(uint64_t data, uint16_t nbits = kSamsungBits, - uint16_t repeat = kNoRepeat); - uint32_t encodeSAMSUNG(uint8_t customer, uint8_t command); + void sendSAMSUNG(const uint64_t data, const uint16_t nbits = kSamsungBits, + const uint16_t repeat = kNoRepeat); + uint32_t encodeSAMSUNG(const uint8_t customer, const uint8_t command); #endif #if SEND_SAMSUNG36 void sendSamsung36(const uint64_t data, const uint16_t nbits = kSamsung36Bits, @@ -152,21 +197,27 @@ class IRsend { uint32_t encodeLG(uint16_t address, uint16_t command); #endif #if (SEND_SHARP || SEND_DENON) - uint32_t encodeSharp(uint16_t address, uint16_t command, - uint16_t expansion = 1, uint16_t check = 0, - bool MSBfirst = false); - void sendSharp(uint16_t address, uint16_t command, - uint16_t nbits = kSharpBits, uint16_t repeat = kNoRepeat); - void sendSharpRaw(uint64_t data, uint16_t nbits = kSharpBits, - uint16_t repeat = kNoRepeat); + uint32_t encodeSharp(const uint16_t address, const uint16_t command, + const uint16_t expansion = 1, const uint16_t check = 0, + const bool MSBfirst = false); + void sendSharp(const uint16_t address, const uint16_t command, + const uint16_t nbits = kSharpBits, + const uint16_t repeat = kNoRepeat); + void sendSharpRaw(const uint64_t data, const uint16_t nbits = kSharpBits, + const uint16_t repeat = kNoRepeat); #endif +#if SEND_SHARP_AC + void sendSharpAc(const unsigned char data[], + const uint16_t nbytes = kSharpAcStateLength, + const uint16_t repeat = kSharpAcDefaultRepeat); +#endif // SEND_SHARP_AC #if SEND_JVC void sendJVC(uint64_t data, uint16_t nbits = kJvcBits, uint16_t repeat = kNoRepeat); uint16_t encodeJVC(uint8_t address, uint8_t command); #endif #if SEND_DENON - void sendDenon(uint64_t data, uint16_t nbits = DENON_BITS, + void sendDenon(uint64_t data, uint16_t nbits = kDenonBits, uint16_t repeat = kNoRepeat); #endif #if SEND_SANYO @@ -183,13 +234,14 @@ class IRsend { uint16_t repeat = kDishMinRepeat); #endif #if (SEND_PANASONIC || SEND_DENON) - void sendPanasonic64(uint64_t data, uint16_t nbits = kPanasonicBits, - uint16_t repeat = kNoRepeat); - void sendPanasonic(uint16_t address, uint32_t data, - uint16_t nbits = kPanasonicBits, - uint16_t repeat = kNoRepeat); - uint64_t encodePanasonic(uint16_t manufacturer, uint8_t device, - uint8_t subdevice, uint8_t function); + void sendPanasonic64(const uint64_t data, + const uint16_t nbits = kPanasonicBits, + const uint16_t repeat = kNoRepeat); + void sendPanasonic(const uint16_t address, const uint32_t data, + const uint16_t nbits = kPanasonicBits, + const uint16_t repeat = kNoRepeat); + uint64_t encodePanasonic(const uint16_t manufacturer, const uint8_t device, + const uint8_t subdevice, const uint8_t function); #endif #if SEND_RC5 void sendRC5(uint64_t data, uint16_t nbits = kRC5XBits, @@ -228,9 +280,9 @@ class IRsend { uint16_t repeat = kMitsubishiMinRepeat); #endif #if SEND_MITSUBISHI_AC - void sendMitsubishiAC(unsigned char data[], - uint16_t nbytes = kMitsubishiACStateLength, - uint16_t repeat = kMitsubishiACMinRepeat); + void sendMitsubishiAC(const unsigned char data[], + const uint16_t nbytes = kMitsubishiACStateLength, + const uint16_t repeat = kMitsubishiACMinRepeat); #endif #if SEND_MITSUBISHIHEAVY void sendMitsubishiHeavy88( @@ -243,25 +295,50 @@ class IRsend { const uint16_t repeat = kMitsubishiHeavy152MinRepeat); #endif #if SEND_FUJITSU_AC - void sendFujitsuAC(unsigned char data[], uint16_t nbytes, - uint16_t repeat = kFujitsuAcMinRepeat); + void sendFujitsuAC(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat = kFujitsuAcMinRepeat); #endif +#if SEND_INAX + void sendInax(const uint64_t data, const uint16_t nbits = kInaxBits, + const uint16_t repeat = kInaxMinRepeat); +#endif // SEND_INAX #if SEND_GLOBALCACHE void sendGC(uint16_t buf[], uint16_t len); #endif #if SEND_KELVINATOR - void sendKelvinator(unsigned char data[], - uint16_t nbytes = kKelvinatorStateLength, - uint16_t repeat = kKelvinatorDefaultRepeat); + void sendKelvinator(const unsigned char data[], + const uint16_t nbytes = kKelvinatorStateLength, + const uint16_t repeat = kKelvinatorDefaultRepeat); #endif #if SEND_DAIKIN void sendDaikin(const unsigned char data[], const uint16_t nbytes = kDaikinStateLength, const uint16_t repeat = kDaikinDefaultRepeat); #endif +#if SEND_DAIKIN128 + void sendDaikin128(const unsigned char data[], + const uint16_t nbytes = kDaikin128StateLength, + const uint16_t repeat = kDaikin128DefaultRepeat); +#endif // SEND_DAIKIN128 +#if SEND_DAIKIN152 + void sendDaikin152(const unsigned char data[], + const uint16_t nbytes = kDaikin152StateLength, + const uint16_t repeat = kDaikin152DefaultRepeat); +#endif // SEND_DAIKIN152 +#if SEND_DAIKIN160 + void sendDaikin160(const unsigned char data[], + const uint16_t nbytes = kDaikin160StateLength, + const uint16_t repeat = kDaikin160DefaultRepeat); +#endif // SEND_DAIKIN160 +#if SEND_DAIKIN176 + void sendDaikin176(const unsigned char data[], + const uint16_t nbytes = kDaikin176StateLength, + const uint16_t repeat = kDaikin176DefaultRepeat); +#endif // SEND_DAIKIN176 #if SEND_DAIKIN2 - void sendDaikin2(unsigned char data[], uint16_t nbytes = kDaikin2StateLength, - uint16_t repeat = kDaikin2DefaultRepeat); + void sendDaikin2(const unsigned char data[], + const uint16_t nbytes = kDaikin2StateLength, + const uint16_t repeat = kDaikin2DefaultRepeat); #endif #if SEND_DAIKIN216 void sendDaikin216(const unsigned char data[], @@ -273,30 +350,37 @@ class IRsend { uint16_t repeat = kAiwaRcT501MinRepeats); #endif #if SEND_GREE - void sendGree(uint64_t data, uint16_t nbits = kGreeBits, - uint16_t repeat = kGreeDefaultRepeat); - void sendGree(uint8_t data[], uint16_t nbytes = kGreeStateLength, - uint16_t repeat = kGreeDefaultRepeat); + void sendGree(const uint64_t data, const uint16_t nbits = kGreeBits, + const uint16_t repeat = kGreeDefaultRepeat); + void sendGree(const uint8_t data[], const uint16_t nbytes = kGreeStateLength, + const uint16_t repeat = kGreeDefaultRepeat); #endif +#if SEND_GOODWEATHER + void sendGoodweather(const uint64_t data, + const uint16_t nbits = kGoodweatherBits, + const uint16_t repeat = kGoodweatherMinRepeat); +#endif // SEND_GOODWEATHER #if SEND_PRONTO void sendPronto(uint16_t data[], uint16_t len, uint16_t repeat = kNoRepeat); #endif #if SEND_ARGO - void sendArgo(unsigned char data[], uint16_t nbytes = kArgoStateLength, - uint16_t repeat = kArgoDefaultRepeat); + void sendArgo(const unsigned char data[], + const uint16_t nbytes = kArgoStateLength, + const uint16_t repeat = kArgoDefaultRepeat); #endif #if SEND_TROTEC - void sendTrotec(unsigned char data[], uint16_t nbytes = kTrotecStateLength, - uint16_t repeat = kTrotecDefaultRepeat); + void sendTrotec(const unsigned char data[], + const uint16_t nbytes = kTrotecStateLength, + const uint16_t repeat = kTrotecDefaultRepeat); #endif #if SEND_NIKAI void sendNikai(uint64_t data, uint16_t nbits = kNikaiBits, uint16_t repeat = kNoRepeat); #endif #if SEND_TOSHIBA_AC - void sendToshibaAC(unsigned char data[], - uint16_t nbytes = kToshibaACStateLength, - uint16_t repeat = kToshibaACMinRepeat); + void sendToshibaAC(const unsigned char data[], + const uint16_t nbytes = kToshibaACStateLength, + const uint16_t repeat = kToshibaACMinRepeat); #endif #if SEND_MIDEA void sendMidea(uint64_t data, uint16_t nbits = kMideaBits, @@ -316,51 +400,52 @@ class IRsend { uint16_t repeat = kCarrierAcMinRepeat); #endif #if (SEND_HAIER_AC || SEND_HAIER_AC_YRW02) - void sendHaierAC(unsigned char data[], uint16_t nbytes = kHaierACStateLength, - uint16_t repeat = kHaierAcDefaultRepeat); + void sendHaierAC(const unsigned char data[], + const uint16_t nbytes = kHaierACStateLength, + const uint16_t repeat = kHaierAcDefaultRepeat); #endif #if SEND_HAIER_AC_YRW02 - void sendHaierACYRW02(unsigned char data[], - uint16_t nbytes = kHaierACYRW02StateLength, - uint16_t repeat = kHaierAcYrw02DefaultRepeat); + void sendHaierACYRW02(const unsigned char data[], + const uint16_t nbytes = kHaierACYRW02StateLength, + const uint16_t repeat = kHaierAcYrw02DefaultRepeat); #endif #if SEND_HITACHI_AC - void sendHitachiAC(unsigned char data[], - uint16_t nbytes = kHitachiAcStateLength, - uint16_t repeat = kHitachiAcDefaultRepeat); + void sendHitachiAC(const unsigned char data[], + const uint16_t nbytes = kHitachiAcStateLength, + const uint16_t repeat = kHitachiAcDefaultRepeat); #endif #if SEND_HITACHI_AC1 - void sendHitachiAC1(unsigned char data[], - uint16_t nbytes = kHitachiAc1StateLength, - uint16_t repeat = kNoRepeat); + void sendHitachiAC1(const unsigned char data[], + const uint16_t nbytes = kHitachiAc1StateLength, + const uint16_t repeat = kNoRepeat); #endif #if SEND_HITACHI_AC2 - void sendHitachiAC2(unsigned char data[], - uint16_t nbytes = kHitachiAc2StateLength, - uint16_t repeat = kNoRepeat); + void sendHitachiAC2(const unsigned char data[], + const uint16_t nbytes = kHitachiAc2StateLength, + const uint16_t repeat = kNoRepeat); #endif #if SEND_GICABLE void sendGICable(uint64_t data, uint16_t nbits = kGicableBits, uint16_t repeat = kGicableMinRepeat); #endif #if SEND_WHIRLPOOL_AC - void sendWhirlpoolAC(unsigned char data[], - uint16_t nbytes = kWhirlpoolAcStateLength, - uint16_t repeat = kWhirlpoolAcDefaultRepeat); + void sendWhirlpoolAC(const unsigned char data[], + const uint16_t nbytes = kWhirlpoolAcStateLength, + const uint16_t repeat = kWhirlpoolAcDefaultRepeat); #endif #if SEND_LUTRON void sendLutron(uint64_t data, uint16_t nbits = kLutronBits, uint16_t repeat = kNoRepeat); #endif #if SEND_ELECTRA_AC - void sendElectraAC(unsigned char data[], - uint16_t nbytes = kElectraAcStateLength, - uint16_t repeat = kNoRepeat); + void sendElectraAC(const unsigned char data[], + const uint16_t nbytes = kElectraAcStateLength, + const uint16_t repeat = kNoRepeat); #endif #if SEND_PANASONIC_AC - void sendPanasonicAC(unsigned char data[], - uint16_t nbytes = kPanasonicAcStateLength, - uint16_t repeat = kPanasonicAcDefaultRepeat); + void sendPanasonicAC(const unsigned char data[], + const uint16_t nbytes = kPanasonicAcStateLength, + const uint16_t repeat = kPanasonicAcDefaultRepeat); #endif #if SEND_PIONEER void sendPioneer(const uint64_t data, const uint16_t nbits = kPioneerBits, @@ -368,8 +453,8 @@ class IRsend { uint64_t encodePioneer(uint16_t address, uint16_t command); #endif #if SEND_MWM - void sendMWM(unsigned char data[], uint16_t nbytes, - uint16_t repeat = kNoRepeat); + void sendMWM(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat = kNoRepeat); #endif #if SEND_VESTEL_AC void sendVestelAc(const uint64_t data, const uint16_t nbits = kVestelAcBits, @@ -381,13 +466,23 @@ class IRsend { const uint16_t repeat = kTcl112AcDefaultRepeat); #endif #if SEND_TECO - void sendTeco(uint64_t data, uint16_t nbits = kTecoBits, - uint16_t repeat = kNoRepeat); + void sendTeco(const uint64_t data, const uint16_t nbits = kTecoBits, + const uint16_t repeat = kNoRepeat); #endif #if SEND_LEGOPF void sendLegoPf(const uint64_t data, const uint16_t nbits = kLegoPfBits, const uint16_t repeat = kLegoPfMinRepeat); #endif +#if SEND_NEOCLIMA + void sendNeoclima(const unsigned char data[], + const uint16_t nbytes = kNeoclimaStateLength, + const uint16_t repeat = kNeoclimaMinRepeat); +#endif // SEND_NEOCLIMA +#if SEND_AMCOR + void sendAmcor(const unsigned char data[], + const uint16_t nbytes = kAmcorStateLength, + const uint16_t repeat = kAmcorDefaultRepeat); +#endif // SEND_AMCOR protected: diff --git a/lib/IRremoteESP8266-2.6.0/src/IRtimer.cpp b/lib/IRremoteESP8266-2.6.5/src/IRtimer.cpp similarity index 100% rename from lib/IRremoteESP8266-2.6.0/src/IRtimer.cpp rename to lib/IRremoteESP8266-2.6.5/src/IRtimer.cpp diff --git a/lib/IRremoteESP8266-2.6.0/src/IRtimer.h b/lib/IRremoteESP8266-2.6.5/src/IRtimer.h similarity index 100% rename from lib/IRremoteESP8266-2.6.0/src/IRtimer.h rename to lib/IRremoteESP8266-2.6.5/src/IRtimer.h diff --git a/lib/IRremoteESP8266-2.6.0/src/IRutils.cpp b/lib/IRremoteESP8266-2.6.5/src/IRutils.cpp similarity index 55% rename from lib/IRremoteESP8266-2.6.0/src/IRutils.cpp rename to lib/IRremoteESP8266-2.6.5/src/IRutils.cpp index d90925241..6f589aa3d 100644 --- a/lib/IRremoteESP8266-2.6.0/src/IRutils.cpp +++ b/lib/IRremoteESP8266-2.6.5/src/IRutils.cpp @@ -44,19 +44,19 @@ uint64_t reverseBits(uint64_t input, uint16_t nbits) { // Returns: // A string representation of the integer. // Note: Based on Arduino's Print::printNumber() -#ifdef ARDUINO // Arduino's & C++'s string implementations can't co-exist. String uint64ToString(uint64_t input, uint8_t base) { String result = ""; -#else -std::string uint64ToString(uint64_t input, uint8_t base) { - std::string result = ""; -#endif // prevent issues if called with base <= 1 if (base < 2) base = 10; // Check we have a base that we can actually print. // i.e. [0-9A-Z] == 36 if (base > 36) base = 10; + // Reserve some string space to reduce fragmentation. + // 16 bytes should store a uint64 in hex text which is the likely worst case. + // 64 bytes would be the worst case (base 2). + result.reserve(16); + do { char c = input % base; input /= base; @@ -82,133 +82,157 @@ void serialPrintUint64(uint64_t input, uint8_t base) { } #endif -// Convert a c-style str to a decode_type_t -// Note: Assumes str is upper case. +// Convert a C-style str to a decode_type_t // // Args: -// str: An upper-case C-style string. +// str: A C-style string containing a protocol name or number. // Returns: // A decode_type_t enum. -decode_type_t strToDecodeType(const char *str) { - if (!strcmp(str, "UNKNOWN")) +decode_type_t strToDecodeType(const char * const str) { + if (!strcasecmp(str, "UNKNOWN")) return decode_type_t::UNKNOWN; - else if (!strcmp(str, "UNUSED")) + else if (!strcasecmp(str, "UNUSED")) return decode_type_t::UNUSED; - else if (!strcmp(str, "AIWA_RC_T501")) + else if (!strcasecmp(str, "AIWA_RC_T501")) return decode_type_t::AIWA_RC_T501; - else if (!strcmp(str, "ARGO")) + else if (!strcasecmp(str, "AMCOR")) + return decode_type_t::AMCOR; + else if (!strcasecmp(str, "ARGO")) return decode_type_t::ARGO; - else if (!strcmp(str, "CARRIER_AC")) + else if (!strcasecmp(str, "CARRIER_AC")) return decode_type_t::CARRIER_AC; - else if (!strcmp(str, "COOLIX")) + else if (!strcasecmp(str, "COOLIX")) return decode_type_t::COOLIX; - else if (!strcmp(str, "DAIKIN")) + else if (!strcasecmp(str, "DAIKIN")) return decode_type_t::DAIKIN; - else if (!strcmp(str, "DAIKIN2")) + else if (!strcasecmp(str, "DAIKIN128")) + return decode_type_t::DAIKIN128; + else if (!strcasecmp(str, "DAIKIN152")) + return decode_type_t::DAIKIN152; + else if (!strcasecmp(str, "DAIKIN160")) + return decode_type_t::DAIKIN160; + else if (!strcasecmp(str, "DAIKIN176")) + return decode_type_t::DAIKIN176; + else if (!strcasecmp(str, "DAIKIN2")) return decode_type_t::DAIKIN2; - else if (!strcmp(str, "DAIKIN216")) + else if (!strcasecmp(str, "DAIKIN216")) return decode_type_t::DAIKIN216; - else if (!strcmp(str, "DENON")) + else if (!strcasecmp(str, "DENON")) return decode_type_t::DENON; - else if (!strcmp(str, "DISH")) + else if (!strcasecmp(str, "DISH")) return decode_type_t::DISH; - else if (!strcmp(str, "ELECTRA_AC")) + else if (!strcasecmp(str, "ELECTRA_AC")) return decode_type_t::ELECTRA_AC; - else if (!strcmp(str, "FUJITSU_AC")) + else if (!strcasecmp(str, "FUJITSU_AC")) return decode_type_t::FUJITSU_AC; - else if (!strcmp(str, "GICABLE")) + else if (!strcasecmp(str, "GICABLE")) return decode_type_t::GICABLE; - else if (!strcmp(str, "GLOBALCACHE")) + else if (!strcasecmp(str, "GLOBALCACHE")) return decode_type_t::GLOBALCACHE; - else if (!strcmp(str, "GREE")) + else if (!strcasecmp(str, "GOODWEATHER")) + return decode_type_t::GOODWEATHER; + else if (!strcasecmp(str, "GREE")) return decode_type_t::GREE; - else if (!strcmp(str, "HAIER_AC")) + else if (!strcasecmp(str, "HAIER_AC")) return decode_type_t::HAIER_AC; - else if (!strcmp(str, "HAIER_AC_YRW02")) + else if (!strcasecmp(str, "HAIER_AC_YRW02")) return decode_type_t::HAIER_AC_YRW02; - else if (!strcmp(str, "HITACHI_AC")) + else if (!strcasecmp(str, "HITACHI_AC")) return decode_type_t::HITACHI_AC; - else if (!strcmp(str, "HITACHI_AC1")) + else if (!strcasecmp(str, "HITACHI_AC1")) return decode_type_t::HITACHI_AC1; - else if (!strcmp(str, "HITACHI_AC2")) + else if (!strcasecmp(str, "HITACHI_AC2")) return decode_type_t::HITACHI_AC2; - else if (!strcmp(str, "JVC")) + else if (!strcasecmp(str, "INAX")) + return decode_type_t::INAX; + else if (!strcasecmp(str, "JVC")) return decode_type_t::JVC; - else if (!strcmp(str, "KELVINATOR")) + else if (!strcasecmp(str, "KELVINATOR")) return decode_type_t::KELVINATOR; - else if (!strcmp(str, "LEGOPF")) + else if (!strcasecmp(str, "LEGOPF")) return decode_type_t::LEGOPF; - else if (!strcmp(str, "LG")) + else if (!strcasecmp(str, "LG")) return decode_type_t::LG; - else if (!strcmp(str, "LG2")) + else if (!strcasecmp(str, "LG2")) return decode_type_t::LG2; - else if (!strcmp(str, "LASERTAG")) + else if (!strcasecmp(str, "LASERTAG")) return decode_type_t::LASERTAG; - else if (!strcmp(str, "LUTRON")) + else if (!strcasecmp(str, "LUTRON")) return decode_type_t::LUTRON; - else if (!strcmp(str, "MAGIQUEST")) + else if (!strcasecmp(str, "MAGIQUEST")) return decode_type_t::MAGIQUEST; - else if (!strcmp(str, "MIDEA")) + else if (!strcasecmp(str, "MIDEA")) return decode_type_t::MIDEA; - else if (!strcmp(str, "MITSUBISHI")) + else if (!strcasecmp(str, "MITSUBISHI")) return decode_type_t::MITSUBISHI; - else if (!strcmp(str, "MITSUBISHI2")) + else if (!strcasecmp(str, "MITSUBISHI2")) return decode_type_t::MITSUBISHI2; - else if (!strcmp(str, "MITSUBISHI_AC")) + else if (!strcasecmp(str, "MITSUBISHI_AC")) return decode_type_t::MITSUBISHI_AC; - else if (!strcmp(str, "MWM")) + else if (!strcasecmp(str, "MITSUBISHI_HEAVY_88")) + return decode_type_t::MITSUBISHI_HEAVY_88; + else if (!strcasecmp(str, "MITSUBISHI_HEAVY_152")) + return decode_type_t::MITSUBISHI_HEAVY_152; + else if (!strcasecmp(str, "MWM")) return decode_type_t::MWM; - else if (!strcmp(str, "NEC") || !strcmp(str, "NEC (NON-STRICT")) + else if (!strcasecmp(str, "NEOCLIMA")) + return decode_type_t::NEOCLIMA; + else if (!strcasecmp(str, "NEC")) return decode_type_t::NEC; - else if (!strcmp(str, "NIKAI")) + else if (!strcasecmp(str, "NEC_LIKE") || + !strcasecmp(str, "NEC (NON-STRICT)")) + return decode_type_t::NEC_LIKE; + else if (!strcasecmp(str, "NIKAI")) return decode_type_t::NIKAI; - else if (!strcmp(str, "PANASONIC")) + else if (!strcasecmp(str, "PANASONIC")) return decode_type_t::PANASONIC; - else if (!strcmp(str, "PANASONIC_AC")) + else if (!strcasecmp(str, "PANASONIC_AC")) return decode_type_t::PANASONIC_AC; - else if (!strcmp(str, "PIONEER")) + else if (!strcasecmp(str, "PIONEER")) return decode_type_t::PIONEER; - else if (!strcmp(str, "PRONTO")) + else if (!strcasecmp(str, "PRONTO")) return decode_type_t::PRONTO; - else if (!strcmp(str, "RAW")) + else if (!strcasecmp(str, "RAW")) return decode_type_t::RAW; - else if (!strcmp(str, "RC5")) + else if (!strcasecmp(str, "RC5")) return decode_type_t::RC5; - else if (!strcmp(str, "RC5X")) + else if (!strcasecmp(str, "RC5X")) return decode_type_t::RC5X; - else if (!strcmp(str, "RC6")) + else if (!strcasecmp(str, "RC6")) return decode_type_t::RC6; - else if (!strcmp(str, "RCMM")) + else if (!strcasecmp(str, "RCMM")) return decode_type_t::RCMM; - else if (!strcmp(str, "SAMSUNG")) + else if (!strcasecmp(str, "SAMSUNG")) return decode_type_t::SAMSUNG; - else if (!strcmp(str, "SAMSUNG36")) + else if (!strcasecmp(str, "SAMSUNG36")) return decode_type_t::SAMSUNG36; - else if (!strcmp(str, "SAMSUNG_AC")) + else if (!strcasecmp(str, "SAMSUNG_AC")) return decode_type_t::SAMSUNG_AC; - else if (!strcmp(str, "SANYO")) + else if (!strcasecmp(str, "SANYO")) return decode_type_t::SANYO; - else if (!strcmp(str, "SANYO_LC7461")) + else if (!strcasecmp(str, "SANYO_LC7461")) return decode_type_t::SANYO_LC7461; - else if (!strcmp(str, "SHARP")) + else if (!strcasecmp(str, "SHARP")) return decode_type_t::SHARP; - else if (!strcmp(str, "SHERWOOD")) + else if (!strcasecmp(str, "SHARP_AC")) + return decode_type_t::SHARP_AC; + else if (!strcasecmp(str, "SHERWOOD")) return decode_type_t::SHERWOOD; - else if (!strcmp(str, "SONY")) + else if (!strcasecmp(str, "SONY")) return decode_type_t::SONY; - else if (!strcmp(str, "TCL112AC")) + else if (!strcasecmp(str, "TCL112AC")) return decode_type_t::TCL112AC; - else if (!strcmp(str, "TECO")) + else if (!strcasecmp(str, "TECO")) return decode_type_t::TECO; - else if (!strcmp(str, "TOSHIBA_AC")) + else if (!strcasecmp(str, "TOSHIBA_AC")) return decode_type_t::TOSHIBA_AC; - else if (!strcmp(str, "TROTEC")) + else if (!strcasecmp(str, "TROTEC")) return decode_type_t::TROTEC; - else if (!strcmp(str, "VESTEL_AC")) + else if (!strcasecmp(str, "VESTEL_AC")) return decode_type_t::VESTEL_AC; - else if (!strcmp(str, "WHIRLPOOL_AC")) + else if (!strcasecmp(str, "WHIRLPOOL_AC")) return decode_type_t::WHIRLPOOL_AC; - else if (!strcmp(str, "WHYNTER")) + else if (!strcasecmp(str, "WHYNTER")) return decode_type_t::WHYNTER; // Handle integer values of the type by converting to a string and back again. decode_type_t result = strToDecodeType( @@ -219,86 +243,14 @@ decode_type_t strToDecodeType(const char *str) { return decode_type_t::UNKNOWN; } -// Escape any special HTML (unsafe) characters in a string. e.g. anti-XSS. -// Args: -// unescaped: A string containing text to make HTML safe. -// Returns: -// A string that is HTML safe. -#ifdef ARDUINO // Arduino's & C++'s string implementations can't co-exist. -String htmlEscape(const String unescaped) { - String result = ""; -#else -std::string htmlEscape(const std::string unescaped) { - std::string result = ""; -#endif - uint16_t ulen = unescaped.length(); - result.reserve(ulen); // The result will be at least the size of input. - for (size_t i = 0; i < ulen; i++) { - char c = unescaped[i]; - switch (c) { - // ';!-"<>=&#{}() are all unsafe. - case '\'': - result += F("'"); - break; - case ';': - result += F(";"); - break; - case '!': - result += F("!"); - break; - case '-': - result += F("‐"); - break; - case '\"': - result += F("""); - break; - case '<': - result += F("<"); - break; - case '>': - result += F(">"); - break; - case '=': - result += F("&#equals;"); - break; - case '&': - result += F("&"); - break; - case '#': - result += F("#"); - break; - case '{': - result += F("{"); - break; - case '}': - result += F("}"); - break; - case '(': - result += F("("); - break; - case ')': - result += F(")"); - break; - default: - result += c; - } - } - return result; -} - // Convert a protocol type (enum etc) to a human readable string. // Args: // protocol: Nr. (enum) of the protocol. // isRepeat: A flag indicating if it is a repeat message of the protocol. // Returns: // A string containing the protocol name. -#ifdef ARDUINO // Arduino's & C++'s string implementations can't co-exist. String typeToString(const decode_type_t protocol, const bool isRepeat) { String result = ""; -#else -std::string typeToString(const decode_type_t protocol, const bool isRepeat) { - std::string result = ""; -#endif switch (protocol) { case UNUSED: result = F("UNUSED"); @@ -306,6 +258,9 @@ std::string typeToString(const decode_type_t protocol, const bool isRepeat) { case AIWA_RC_T501: result = F("AIWA_RC_T501"); break; + case AMCOR: + result = F("AMCOR"); + break; case ARGO: result = F("ARGO"); break; @@ -318,6 +273,18 @@ std::string typeToString(const decode_type_t protocol, const bool isRepeat) { case DAIKIN: result = F("DAIKIN"); break; + case DAIKIN128: + result = F("DAIKIN128"); + break; + case DAIKIN152: + result = F("DAIKIN152"); + break; + case DAIKIN160: + result = F("DAIKIN160"); + break; + case DAIKIN176: + result = F("DAIKIN176"); + break; case DAIKIN2: result = F("DAIKIN2"); break; @@ -342,6 +309,9 @@ std::string typeToString(const decode_type_t protocol, const bool isRepeat) { case GLOBALCACHE: result = F("GLOBALCACHE"); break; + case GOODWEATHER: + result = F("GOODWEATHER"); + break; case GREE: result = F("GREE"); break; @@ -360,6 +330,9 @@ std::string typeToString(const decode_type_t protocol, const bool isRepeat) { case HITACHI_AC2: result = F("HITACHI_AC2"); break; + case INAX: + result = F("INAX"); + break; case JVC: result = F("JVC"); break; @@ -405,6 +378,9 @@ std::string typeToString(const decode_type_t protocol, const bool isRepeat) { case MWM: result = F("MWM"); break; + case NEOCLIMA: + result = F("NEOCLIMA"); + break; case NEC: result = F("NEC"); break; @@ -459,6 +435,9 @@ std::string typeToString(const decode_type_t protocol, const bool isRepeat) { case SHARP: result = F("SHARP"); break; + case SHARP_AC: + result = F("SHARP_AC"); + break; case SHERWOOD: result = F("SHERWOOD"); break; @@ -498,7 +477,13 @@ std::string typeToString(const decode_type_t protocol, const bool isRepeat) { // Does the given protocol use a complex state as part of the decode? bool hasACState(const decode_type_t protocol) { switch (protocol) { + case AMCOR: + case ARGO: case DAIKIN: + case DAIKIN128: + case DAIKIN152: + case DAIKIN160: + case DAIKIN176: case DAIKIN2: case DAIKIN216: case ELECTRA_AC: @@ -514,10 +499,13 @@ bool hasACState(const decode_type_t protocol) { case MITSUBISHI_HEAVY_88: case MITSUBISHI_HEAVY_152: case MWM: + case NEOCLIMA: case PANASONIC_AC: case SAMSUNG_AC: + case SHARP_AC: case TCL112AC: case TOSHIBA_AC: + case TROTEC: case WHIRLPOOL_AC: return true; default: @@ -531,7 +519,7 @@ bool hasACState(const decode_type_t protocol) { // results: A ptr to a decode result. // Returns: // A uint16_t containing the length. -uint16_t getCorrectedRawLength(const decode_results *results) { +uint16_t getCorrectedRawLength(const decode_results * const results) { uint16_t extended_length = results->rawlen - 1; for (uint16_t i = 0; i < results->rawlen - 1; i++) { uint32_t usecs = results->rawbuf[i] * kRawTick; @@ -543,13 +531,10 @@ uint16_t getCorrectedRawLength(const decode_results *results) { // Return a string containing the key values of a decode_results structure // in a C/C++ code style format. -#ifdef ARDUINO -String resultToSourceCode(const decode_results *results) { +String resultToSourceCode(const decode_results * const results) { String output = ""; -#else -std::string resultToSourceCode(const decode_results *results) { - std::string output = ""; -#endif + // Reserve some space for the string to reduce heap fragmentation. + output.reserve(1536); // 1.5KB should cover most cases. // Start declaration output += F("uint16_t "); // variable type output += F("rawData["); // array name @@ -625,15 +610,12 @@ std::string resultToSourceCode(const decode_results *results) { // Dump out the decode_results structure. // -#ifdef ARDUINO -String resultToTimingInfo(const decode_results *results) { +String resultToTimingInfo(const decode_results * const results) { String output = ""; String value = ""; -#else -std::string resultToTimingInfo(const decode_results *results) { - std::string output = ""; - std::string value = ""; -#endif + // Reserve some space for the string to reduce heap fragmentation. + output.reserve(2048); // 2KB should cover most cases. + value.reserve(6); // Max value should be 2^17 = 131072 output += F("Raw Timing["); output += uint64ToString(results->rawlen - 1, 10); output += F("]:\n"); @@ -657,13 +639,10 @@ std::string resultToTimingInfo(const decode_results *results) { // Convert the decode_results structure's value/state to simple hexadecimal. // -#ifdef ARDUINO -String resultToHexidecimal(const decode_results *result) { +String resultToHexidecimal(const decode_results * const result) { String output = ""; -#else -std::string resultToHexidecimal(const decode_results *result) { - std::string output = ""; -#endif + // Reserve some space for the string to reduce heap fragmentation. + output.reserve(2 * kStateSizeMax); // Should cover worst cases. if (hasACState(result->decode_type)) { #if DECODE_AC for (uint16_t i = 0; result->bits > i * 8; i++) { @@ -679,13 +658,10 @@ std::string resultToHexidecimal(const decode_results *result) { // Dump out the decode_results structure. // -#ifdef ARDUINO -String resultToHumanReadableBasic(const decode_results *results) { +String resultToHumanReadableBasic(const decode_results * const results) { String output = ""; -#else -std::string resultToHumanReadableBasic(const decode_results *results) { - std::string output = ""; -#endif + // Reserve some space for the string to reduce heap fragmentation. + output.reserve(2 * kStateSizeMax + 50); // Should cover most cases. // Show Encoding standard output += F("Encoding : "); output += typeToString(results->decode_type, results->repeat); @@ -700,16 +676,43 @@ std::string resultToHumanReadableBasic(const decode_results *results) { return output; } -uint8_t sumBytes(uint8_t *start, const uint16_t length, const uint8_t init) { +// Convert a decode_results into an array suitable for `sendRaw()`. +// Args: +// decode: A pointer to an IR decode_results structure that contains a mesg. +// Returns: +// A pointer to a dynamically allocated uint16_t sendRaw compatible array. +// Note: +// Result needs to be delete[]'ed/free()'ed (deallocated) after use by caller. +uint16_t* resultToRawArray(const decode_results * const decode) { + uint16_t *result = new uint16_t[getCorrectedRawLength(decode)]; + if (result != NULL) { // The memory was allocated successfully. + // Convert the decode data. + uint16_t pos = 0; + for (uint16_t i = 1; i < decode->rawlen; i++) { + uint32_t usecs = decode->rawbuf[i] * kRawTick; + while (usecs > UINT16_MAX) { // Keep truncating till it fits. + result[pos++] = UINT16_MAX; + result[pos++] = 0; // A 0 in a sendRaw() array basically means skip. + usecs -= UINT16_MAX; + } + result[pos++] = usecs; + } + } + return result; +} + +uint8_t sumBytes(const uint8_t * const start, const uint16_t length, + const uint8_t init) { uint8_t checksum = init; - uint8_t *ptr; + const uint8_t *ptr; for (ptr = start; ptr - start < length; ptr++) checksum += *ptr; return checksum; } -uint8_t xorBytes(uint8_t *start, const uint16_t length, const uint8_t init) { +uint8_t xorBytes(const uint8_t * const start, const uint16_t length, + const uint8_t init) { uint8_t checksum = init; - uint8_t *ptr; + const uint8_t *ptr; for (ptr = start; ptr - start < length; ptr++) checksum ^= *ptr; return checksum; } @@ -722,8 +725,8 @@ uint8_t xorBytes(uint8_t *start, const uint16_t length, const uint8_t init) { // init: Start the counting from this value. // Returns: // Nr. of bits found. -uint16_t countBits(const uint8_t *start, const uint16_t length, const bool ones, - const uint16_t init) { +uint16_t countBits(const uint8_t * const start, const uint16_t length, + const bool ones, const uint16_t init) { uint16_t count = init; for (uint16_t offset = 0; offset < length; offset++) for (uint8_t currentbyte = *(start + offset); @@ -766,3 +769,196 @@ uint64_t invertBits(const uint64_t data, const uint16_t nbits) { // Mask off any unwanted bits and return the result. return (result & ((1ULL << nbits) - 1)); } + +float celsiusToFahrenheit(const float deg) { return (deg * 9.0) / 5.0 + 32.0; } + +float fahrenheitToCelsius(const float deg) { return (deg - 32.0) * 5.0 / 9.0; } + +namespace irutils { + String addLabeledString(const String value, const String label, + const bool precomma) { + String result = ""; + if (precomma) result += F(", "); + result += label; + result += F(": "); + return result + value; + } + + String addBoolToString(const bool value, const String label, + const bool precomma) { + return addLabeledString((value ? F("On") : F("Off")), label, precomma); + } + + String addIntToString(const uint16_t value, const String label, + const bool precomma) { + return addLabeledString(uint64ToString(value), label, precomma); + } + + String addTempToString(const uint16_t degrees, const bool celsius, + const bool precomma) { + String result = addIntToString(degrees, F("Temp"), precomma); + result += celsius ? 'C' : 'F'; + return result; + } + + String addModeToString(const uint8_t mode, const uint8_t automatic, + const uint8_t cool, const uint8_t heat, + const uint8_t dry, const uint8_t fan) { + String result = addIntToString(mode, F("Mode")); + result += F(" ("); + if (mode == automatic) result += F("AUTO"); + else if (mode == cool) result += F("COOL"); + else if (mode == heat) result += F("HEAT"); + else if (mode == dry) result += F("DRY"); + else if (mode == fan) result += F("FAN"); + else + result += F("UNKNOWN"); + return result + ')'; + } + + String addFanToString(const uint8_t speed, const uint8_t high, + const uint8_t low, const uint8_t automatic, + const uint8_t quiet, const uint8_t medium) { + String result = addIntToString(speed, F("Fan")); + result += F(" ("); + if (speed == high) result += F("High"); + else if (speed == low) result += F("Low"); + else if (speed == automatic) result += F("Auto"); + else if (speed == quiet) result += F("Quiet"); + else if (speed == medium) result += F("Medium"); + else + result += F("UNKNOWN"); + return result + ')'; + } + + // Escape any special HTML (unsafe) characters in a string. e.g. anti-XSS. + // Args: + // unescaped: A string containing text to make HTML safe. + // Returns: + // A string that is HTML safe. + String htmlEscape(const String unescaped) { + String result = ""; + uint16_t ulen = unescaped.length(); + result.reserve(ulen); // The result will be at least the size of input. + for (size_t i = 0; i < ulen; i++) { + char c = unescaped[i]; + switch (c) { + // ';!-"<>=&#{}() are all unsafe. + case '\'': + result += F("'"); + break; + case ';': + result += F(";"); + break; + case '!': + result += F("!"); + break; + case '-': + result += F("‐"); + break; + case '\"': + result += F("""); + break; + case '<': + result += F("<"); + break; + case '>': + result += F(">"); + break; + case '=': + result += F("&#equals;"); + break; + case '&': + result += F("&"); + break; + case '#': + result += F("#"); + break; + case '{': + result += F("{"); + break; + case '}': + result += F("}"); + break; + case '(': + result += F("("); + break; + case ')': + result += F(")"); + break; + default: + result += c; + } + } + return result; + } + + String msToString(uint32_t const msecs) { + uint32_t totalseconds = msecs / 1000; + if (totalseconds == 0) return F("Now"); + + // Note: uint32_t can only hold up to 45 days, so uint8_t is safe. + uint8_t days = totalseconds / (60 * 60 * 24); + uint8_t hours = (totalseconds / (60 * 60)) % 24; + uint8_t minutes = (totalseconds / 60) % 60; + uint8_t seconds = totalseconds % 60; + + String result = ""; + if (days) { + result += uint64ToString(days) + F(" day"); + if (days > 1) result += 's'; + } + if (hours) { + if (result.length()) result += ' '; + result += uint64ToString(hours) + F(" hour"); + if (hours > 1) result += 's'; + } + if (minutes) { + if (result.length()) result += ' '; + result += uint64ToString(minutes) + F(" minute"); + if (minutes > 1) result += 's'; + } + if (seconds) { + if (result.length()) result += ' '; + result += uint64ToString(seconds) + F(" second"); + if (seconds > 1) result += 's'; + } + return result; + } + + String minsToString(const uint16_t mins) { + String result = ""; + result.reserve(5); // 23:59 is the typical worst case. + if (mins / 60 < 10) result += '0'; // Zero pad the hours + result += uint64ToString(mins / 60) + ':'; + if (mins % 60 < 10) result += '0'; // Zero pad the minutes. + result += uint64ToString(mins % 60); + return result; + } + + // Sum all the nibbles together in a series of bytes. + // Args: + // start: PTR to the start of the bytes. + // length: Nr of bytes to sum the nibbles of. + // init: Starting value of the sum. + // Returns: + // A uint8_t sum of all the nibbles inc the init. + uint8_t sumNibbles(const uint8_t * const start, const uint16_t length, + const uint8_t init) { + uint8_t sum = init; + const uint8_t *ptr; + for (ptr = start; ptr - start < length; ptr++) + sum += (*ptr >> 4) + (*ptr & 0xF); + return sum; + } + + uint8_t bcdToUint8(const uint8_t bcd) { + if (bcd > 0x99) return 255; // Too big. + return (bcd >> 4) * 10 + (bcd & 0xF); + } + + uint8_t uint8ToBcd(const uint8_t integer) { + if (integer > 99) return 255; // Too big. + return ((integer / 10) << 4) + (integer % 10); + } +} // namespace irutils diff --git a/lib/IRremoteESP8266-2.6.5/src/IRutils.h b/lib/IRremoteESP8266-2.6.5/src/IRutils.h new file mode 100644 index 000000000..8d4226577 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.5/src/IRutils.h @@ -0,0 +1,64 @@ +#ifndef IRUTILS_H_ +#define IRUTILS_H_ + +// Copyright 2017 David Conran + +#ifndef UNIT_TEST +#include +#endif +#define __STDC_LIMIT_MACROS +#include +#ifndef ARDUINO +#include +#endif +#include "IRremoteESP8266.h" +#include "IRrecv.h" + +uint64_t reverseBits(uint64_t input, uint16_t nbits); +String uint64ToString(uint64_t input, uint8_t base = 10); +String typeToString(const decode_type_t protocol, + const bool isRepeat = false); +void serialPrintUint64(uint64_t input, uint8_t base = 10); +String resultToSourceCode(const decode_results * const results); +String resultToTimingInfo(const decode_results * const results); +String resultToHumanReadableBasic(const decode_results * const results); +String resultToHexidecimal(const decode_results * const result); +bool hasACState(const decode_type_t protocol); +uint16_t getCorrectedRawLength(const decode_results * const results); +uint16_t *resultToRawArray(const decode_results * const decode); +uint8_t sumBytes(const uint8_t * const start, const uint16_t length, + const uint8_t init = 0); +uint8_t xorBytes(const uint8_t * const start, const uint16_t length, + const uint8_t init = 0); +uint16_t countBits(const uint8_t * const start, const uint16_t length, + const bool ones = true, const uint16_t init = 0); +uint16_t countBits(const uint64_t data, const uint8_t length, + const bool ones = true, const uint16_t init = 0); +uint64_t invertBits(const uint64_t data, const uint16_t nbits); +decode_type_t strToDecodeType(const char *str); +float celsiusToFahrenheit(const float deg); +float fahrenheitToCelsius(const float deg); +namespace irutils { + String addBoolToString(const bool value, const String label, + const bool precomma = true); + String addIntToString(const uint16_t value, const String label, + const bool precomma = true); + String addLabeledString(const String value, const String label, + const bool precomma = true); + String addTempToString(const uint16_t degrees, const bool celsius = true, + const bool precomma = true); + String addModeToString(const uint8_t mode, const uint8_t automatic, + const uint8_t cool, const uint8_t heat, + const uint8_t dry, const uint8_t fan); + String addFanToString(const uint8_t speed, const uint8_t high, + const uint8_t low, const uint8_t automatic, + const uint8_t quiet, const uint8_t medium); + String htmlEscape(const String unescaped); + String msToString(uint32_t const msecs); + String minsToString(const uint16_t mins); + uint8_t sumNibbles(const uint8_t * const start, const uint16_t length, + const uint8_t init = 0); + uint8_t bcdToUint8(const uint8_t bcd); + uint8_t uint8ToBcd(const uint8_t integer); +} // namespace irutils +#endif // IRUTILS_H_ diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Aiwa.cpp b/lib/IRremoteESP8266-2.6.5/src/ir_Aiwa.cpp similarity index 98% rename from lib/IRremoteESP8266-2.6.0/src/ir_Aiwa.cpp rename to lib/IRremoteESP8266-2.6.5/src/ir_Aiwa.cpp index 617711a99..70dbc85b2 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Aiwa.cpp +++ b/lib/IRremoteESP8266-2.6.5/src/ir_Aiwa.cpp @@ -12,6 +12,8 @@ // Based off the RC-T501 RCU // Added by David Conran. (Inspired by IRremoteESP8266's implementation: // https://github.com/z3t0/Arduino-IRremote) +// Supports: +// Brand: Aiwa, Model: RC-T501 RCU const uint16_t kAiwaRcT501PreBits = 26; const uint16_t kAiwaRcT501PostBits = 1; diff --git a/lib/IRremoteESP8266-2.6.5/src/ir_Amcor.cpp b/lib/IRremoteESP8266-2.6.5/src/ir_Amcor.cpp new file mode 100644 index 000000000..8c0b73c8c --- /dev/null +++ b/lib/IRremoteESP8266-2.6.5/src/ir_Amcor.cpp @@ -0,0 +1,326 @@ +// Copyright 2019 David Conran + +// Supports: +// Brand: Amcor, Model: ADR-853H A/C +// Brand: Amcor, Model: TAC-495 remote +// Brand: Amcor, Model: TAC-444 remote + +#include "ir_Amcor.h" +#include +#include "IRrecv.h" +#include "IRsend.h" +#include "IRutils.h" + +// Constants +// Ref: +// https://github.com/crankyoldgit/IRremoteESP8266/issues/385 +const uint16_t kAmcorHdrMark = 8200; +const uint16_t kAmcorHdrSpace = 4200; +const uint16_t kAmcorOneMark = 1500; +const uint16_t kAmcorZeroMark = 600; +const uint16_t kAmcorOneSpace = kAmcorZeroMark; +const uint16_t kAmcorZeroSpace = kAmcorOneMark; +const uint16_t kAmcorFooterMark = 1900; +const uint16_t kAmcorGap = 34300; +const uint8_t kAmcorTolerance = 40; + +using irutils::addBoolToString; +using irutils::addModeToString; +using irutils::addFanToString; +using irutils::addTempToString; + +#if SEND_AMCOR +// Send a Amcor HVAC formatted message. +// +// Args: +// data: The message to be sent. +// nbytes: The byte size of the array being sent. typically kAmcorStateLength. +// repeat: The number of times the message is to be repeated. +// +// Status: STABLE / Reported as working. +// +void IRsend::sendAmcor(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + // Check if we have enough bytes to send a proper message. + if (nbytes < kAmcorStateLength) return; + sendGeneric(kAmcorHdrMark, kAmcorHdrSpace, kAmcorOneMark, kAmcorOneSpace, + kAmcorZeroMark, kAmcorZeroSpace, kAmcorFooterMark, kAmcorGap, + data, nbytes, 38, false, repeat, kDutyDefault); +} +#endif + +#if DECODE_AMCOR +// Decode the supplied Amcor HVAC message. +// Args: +// results: Ptr to the data to decode and where to store the decode result. +// nbits: Nr. of bits to expect in the data portion. +// Typically kAmcorBits. +// strict: Flag to indicate if we strictly adhere to the specification. +// Returns: +// boolean: True if it can decode it, false if it can't. +// +// Status: STABLE / Reported as working. +// +bool IRrecv::decodeAmcor(decode_results *results, uint16_t nbits, + bool strict) { + if (results->rawlen < 2 * nbits + kHeader - 1) + return false; // Can't possibly be a valid Amcor message. + if (strict && nbits != kAmcorBits) + return false; // We expect Amcor to be 64 bits of message. + + uint16_t offset = kStartOffset; + + uint16_t used; + // Header + Data Block (64 bits) + Footer + used = matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, 64, + kAmcorHdrMark, kAmcorHdrSpace, + kAmcorOneMark, kAmcorOneSpace, + kAmcorZeroMark, kAmcorZeroSpace, + kAmcorFooterMark, kAmcorGap, true, + kAmcorTolerance, 0, false); + if (!used) return false; + offset += used; + + if (strict) { + if (!IRAmcorAc::validChecksum(results->state)) return false; + } + + // Success + results->bits = nbits; + results->decode_type = AMCOR; + // No need to record the state as we stored it as we decoded it. + // As we use result->state, we don't record value, address, or command as it + // is a union data type. + return true; +} +#endif + +IRAmcorAc::IRAmcorAc(const uint16_t pin, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { this->stateReset(); } + +void IRAmcorAc::begin(void) { _irsend.begin(); } + +#if SEND_AMCOR +void IRAmcorAc::send(const uint16_t repeat) { + this->checksum(); // Create valid checksum before sending + _irsend.sendAmcor(remote_state, kAmcorStateLength, repeat); +} +#endif // SEND_AMCOR + +uint8_t IRAmcorAc::calcChecksum(const uint8_t state[], const uint16_t length) { + return irutils::sumNibbles(state, length - 1); +} + +bool IRAmcorAc::validChecksum(const uint8_t state[], const uint16_t length) { + return (state[length - 1] == IRAmcorAc::calcChecksum(state, length)); +} + +void IRAmcorAc::checksum(void) { + remote_state[kAmcorChecksumByte] = IRAmcorAc::calcChecksum(remote_state, + kAmcorStateLength); +} + +void IRAmcorAc::stateReset(void) { + for (uint8_t i = 1; i < kAmcorStateLength; i++) remote_state[i] = 0x0; + remote_state[0] = 0x01; + setFan(kAmcorFanAuto); + setMode(kAmcorAuto); + setTemp(25); // 25C +} + +uint8_t* IRAmcorAc::getRaw(void) { + this->checksum(); // Ensure correct bit array before returning + return remote_state; +} + +void IRAmcorAc::setRaw(const uint8_t state[]) { + for (uint8_t i = 0; i < kAmcorStateLength; i++) remote_state[i] = state[i]; +} + +void IRAmcorAc::on(void) { setPower(true); } + +void IRAmcorAc::off(void) { setPower(false); } + +void IRAmcorAc::setPower(const bool on) { + remote_state[kAmcorPowerByte] &= ~kAmcorPowerMask; + remote_state[kAmcorPowerByte] |= (on ? kAmcorPowerOn : kAmcorPowerOff); +} + +bool IRAmcorAc::getPower(void) { + return ((remote_state[kAmcorPowerByte] & kAmcorPowerMask) == kAmcorPowerOn); +} + +// Set the temp in deg C +void IRAmcorAc::setTemp(const uint8_t degrees) { + uint8_t temp = std::max(kAmcorMinTemp, degrees); + temp = std::min(kAmcorMaxTemp, temp); + + temp <<= 1; + remote_state[kAmcorTempByte] &= ~kAmcorTempMask; + remote_state[kAmcorTempByte] |= temp; +} + +uint8_t IRAmcorAc::getTemp(void) { + return (remote_state[kAmcorTempByte] & kAmcorTempMask) >> 1; +} + +// Maximum Cooling or Hearing +void IRAmcorAc::setMax(const bool on) { + if (on) { + switch (getMode()) { + case kAmcorCool: + setTemp(kAmcorMinTemp); + break; + case kAmcorHeat: + setTemp(kAmcorMaxTemp); + break; + default: // Not allowed in all other operating modes. + return; + } + remote_state[kAmcorSpecialByte] |= kAmcorMaxMask; + } else { + remote_state[kAmcorSpecialByte] &= ~kAmcorMaxMask; + } +} + +bool IRAmcorAc::getMax(void) { + return ((remote_state[kAmcorSpecialByte] & kAmcorMaxMask) == kAmcorMaxMask); +} + +// Set the speed of the fan +void IRAmcorAc::setFan(const uint8_t speed) { + switch (speed) { + case kAmcorFanAuto: + case kAmcorFanMin: + case kAmcorFanMed: + case kAmcorFanMax: + remote_state[kAmcorModeFanByte] &= ~kAmcorFanMask; + remote_state[kAmcorModeFanByte] |= speed << 4; + break; + default: + setFan(kAmcorFanAuto); + } +} + +uint8_t IRAmcorAc::getFan(void) { + return (remote_state[kAmcorModeFanByte] & kAmcorFanMask) >> 4; +} + +uint8_t IRAmcorAc::getMode(void) { + return remote_state[kAmcorModeFanByte] & kAmcorModeMask; +} + +void IRAmcorAc::setMode(const uint8_t mode) { + remote_state[kAmcorSpecialByte] &= ~kAmcorVentMask; // Clear the vent setting + switch (mode) { + case kAmcorFan: + remote_state[kAmcorSpecialByte] |= kAmcorVentMask; // Set the vent option + // FALL-THRU + case kAmcorCool: + case kAmcorHeat: + case kAmcorDry: + case kAmcorAuto: + // Mask out bits + remote_state[kAmcorModeFanByte] &= ~kAmcorModeMask; + // Set the mode at bit positions + remote_state[kAmcorModeFanByte] |= mode; + return; + default: + this->setMode(kAmcorAuto); + } +} + +// Convert a standard A/C mode into its native mode. +uint8_t IRAmcorAc::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kAmcorCool; + case stdAc::opmode_t::kHeat: + return kAmcorHeat; + case stdAc::opmode_t::kDry: + return kAmcorDry; + case stdAc::opmode_t::kFan: + return kAmcorFan; + default: + return kAmcorAuto; + } +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRAmcorAc::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: + return kAmcorFanMin; + case stdAc::fanspeed_t::kMedium: + return kAmcorFanMed; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: + return kAmcorFanMax; + default: + return kAmcorFanAuto; + } +} + +// Convert a native mode to it's common equivalent. +stdAc::opmode_t IRAmcorAc::toCommonMode(const uint8_t mode) { + switch (mode) { + case kAmcorCool: return stdAc::opmode_t::kCool; + case kAmcorHeat: return stdAc::opmode_t::kHeat; + case kAmcorDry: return stdAc::opmode_t::kDry; + case kAmcorFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRAmcorAc::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kAmcorFanMax: return stdAc::fanspeed_t::kMax; + case kAmcorFanMed: return stdAc::fanspeed_t::kMedium; + case kAmcorFanMin: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRAmcorAc::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::AMCOR; + result.power = this->getPower(); + result.mode = this->toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(); + result.fanspeed = this->toCommonFanSpeed(this->getFan()); + // Not supported. + result.model = -1; + result.turbo = false; + result.swingv = stdAc::swingv_t::kOff; + result.swingh = stdAc::swingh_t::kOff; + result.light = false; + result.filter = false; + result.econo = false; + result.quiet = false; + result.clean = false; + result.beep = false; + result.sleep = -1; + result.clock = -1; + return result; +} + +// Convert the internal state into a human readable string. +String IRAmcorAc::toString() { + String result = ""; + result.reserve(70); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(getPower(), F("Power"), false); + result += addModeToString(getMode(), kAmcorAuto, kAmcorCool, + kAmcorHeat, kAmcorDry, kAmcorFan); + result += addFanToString(getFan(), kAmcorFanMax, kAmcorFanMin, + kAmcorFanAuto, kAmcorFanAuto, + kAmcorFanMed); + result += addTempToString(getTemp()); + result += addBoolToString(getMax(), F("Max")); + return result; +} diff --git a/lib/IRremoteESP8266-2.6.5/src/ir_Amcor.h b/lib/IRremoteESP8266-2.6.5/src/ir_Amcor.h new file mode 100644 index 000000000..73beb27a7 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.5/src/ir_Amcor.h @@ -0,0 +1,118 @@ +// Amcor A/C +// +// Copyright 2019 David Conran + +#ifndef IR_AMCOR_H_ +#define IR_AMCOR_H_ + +#define __STDC_LIMIT_MACROS +#include +#ifndef UNIT_TEST +#include +#endif +#include "IRremoteESP8266.h" +#include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif + +// Supports: +// Brand: Amcor, Model: ADR-853H A/C +// Brand: Amcor, Model: TAC-495 remote +// Brand: Amcor, Model: TAC-444 remote +// Ref: +// https://github.com/crankyoldgit/IRremoteESP8266/issues/834 +// Kudos: +// ldellus: For the breakdown and mapping of the bit values. + +// Constants + + +// state[1] +const uint8_t kAmcorModeFanByte = 1; +// Fan Control +const uint8_t kAmcorFanMin = 0b001; +const uint8_t kAmcorFanMed = 0b010; +const uint8_t kAmcorFanMax = 0b011; +const uint8_t kAmcorFanAuto = 0b100; +const uint8_t kAmcorFanMask = 0b01110000; +// Modes +const uint8_t kAmcorCool = 0b001; +const uint8_t kAmcorHeat = 0b010; +const uint8_t kAmcorFan = 0b011; // Aka "Vent" +const uint8_t kAmcorDry = 0b100; +const uint8_t kAmcorAuto = 0b101; +const uint8_t kAmcorModeMask = 0b00000111; + +// state[2] +const uint8_t kAmcorTempByte = 2; +// Temperature +const uint8_t kAmcorMinTemp = 12; // Celsius +const uint8_t kAmcorMaxTemp = 32; // Celsius +const uint8_t kAmcorTempMask = 0b01111110; + +// state[5] +// Power +const uint8_t kAmcorPowerByte = 5; +const uint8_t kAmcorPowerMask = 0b11110000; +const uint8_t kAmcorPowerOn = 0b00110000; // 0x30 +const uint8_t kAmcorPowerOff = 0b11000000; // 0xC0 + +// state[6] +const uint8_t kAmcorSpecialByte = 6; +// Max Mode (aka "Lo" in Cool and "Hi" in Heat) +const uint8_t kAmcorMaxMask = 0b00000011; // 0x03 +// "Vent" Mode +const uint8_t kAmcorVentMask = 0b11000000; // 0xC0 + +// state[7] +// Checksum byte. +const uint8_t kAmcorChecksumByte = kAmcorStateLength - 1; + +// Classes +class IRAmcorAc { + public: + explicit IRAmcorAc(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); + + void stateReset(); +#if SEND_AMCOR + void send(const uint16_t repeat = kAmcorDefaultRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } +#endif // SEND_AMCOR + void begin(); + static uint8_t calcChecksum(const uint8_t state[], + const uint16_t length = kAmcorStateLength); + static bool validChecksum(const uint8_t state[], + const uint16_t length = kAmcorStateLength); + void setPower(const bool state); + bool getPower(); + void on(); + void off(); + void setTemp(const uint8_t temp); + uint8_t getTemp(); + void setMax(const bool on); + bool getMax(void); + void setFan(const uint8_t speed); + uint8_t getFan(); + void setMode(const uint8_t mode); + uint8_t getMode(); + uint8_t* getRaw(); + void setRaw(const uint8_t state[]); + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + stdAc::state_t toCommon(void); + String toString(); +#ifndef UNIT_TEST + + private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif + uint8_t remote_state[kAmcorStateLength]; // The state of the IR remote. + void checksum(void); +}; +#endif // IR_AMCOR_H_ diff --git a/lib/IRremoteESP8266-2.6.5/src/ir_Argo.cpp b/lib/IRremoteESP8266-2.6.5/src/ir_Argo.cpp new file mode 100644 index 000000000..522ede7e6 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.5/src/ir_Argo.cpp @@ -0,0 +1,438 @@ +/* +Node MCU/ESP8266 Sketch to emulate Argo Ulisse 13 DCI remote +Controls Argo Ulisse 13 DCI A/C +Copyright 2017 Schmolders +Copyright 2019 crankyoldgit +*/ + +#include "ir_Argo.h" +#include +#ifndef UNIT_TEST +#include +#endif // UNIT_TEST +#include "IRremoteESP8266.h" +#include "IRutils.h" + +// Constants +// using SPACE modulation. MARK is always const 400u +const uint16_t kArgoHdrMark = 6400; +const uint16_t kArgoHdrSpace = 3300; +const uint16_t kArgoBitMark = 400; +const uint16_t kArgoOneSpace = 2200; +const uint16_t kArgoZeroSpace = 900; +const uint32_t kArgoGap = kDefaultMessageGap; // Made up value. Complete guess. + +using irutils::addBoolToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addTempToString; + +#if SEND_ARGO +// Send an Argo A/C message. +// +// Args: +// data: An array of kArgoStateLength bytes containing the IR command. +// +// Status: ALPHA / Untested. + +void IRsend::sendArgo(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + // Check if we have enough bytes to send a proper message. + if (nbytes < kArgoStateLength) return; + // TODO(kaschmo): validate + sendGeneric(kArgoHdrMark, kArgoHdrSpace, kArgoBitMark, kArgoOneSpace, + kArgoBitMark, kArgoZeroSpace, 0, 0, // No Footer. + data, nbytes, 38, false, repeat, kDutyDefault); +} +#endif // SEND_ARGO + +IRArgoAC::IRArgoAC(const uint16_t pin, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { this->stateReset(); } + +void IRArgoAC::begin(void) { _irsend.begin(); } + +#if SEND_ARGO +void IRArgoAC::send(const uint16_t repeat) { + this->checksum(); // Create valid checksum before sending + _irsend.sendArgo(argo, kArgoStateLength, repeat); +} +#endif // SEND_ARGO + +uint8_t IRArgoAC::calcChecksum(const uint8_t state[], const uint16_t length) { + // Corresponds to byte 11 being constant 0b01 + // Only add up bytes to 9. byte 10 is 0b01 constant anyway. + // Assume that argo array is MSB first (left) + return sumBytes(state, length - 2, 2); +} + +bool IRArgoAC::validChecksum(const uint8_t state[], const uint16_t length) { + return ((state[length - 2] >> 2) + (state[length - 1] << 6)) == + IRArgoAC::calcChecksum(state, length); +} + +void IRArgoAC::checksum(void) { + uint8_t sum = IRArgoAC::calcChecksum(argo, kArgoStateLength); + // Append sum to end of array + // Set const part of checksum bit 10 + argo[10] = 0b00000010; + argo[10] += sum << 2; // Shift up 2 bits and append to byte 10 + argo[11] = sum >> 6; // Shift down 6 bits and add in two LSBs of bit 11 +} + +void IRArgoAC::stateReset(void) { + for (uint8_t i = 0; i < kArgoStateLength; i++) argo[i] = 0x0; + + // Argo Message. Store MSB left. + // Default message: + argo[0] = 0b10101100; // LSB first (as sent) 0b00110101; //const preamble + argo[1] = 0b11110101; // LSB first: 0b10101111; //const preamble + // Keep payload 2-9 at zero + argo[10] = 0b00000010; // Const 01, checksum 6bit + argo[11] = 0b00000000; // Checksum 2bit + + this->off(); + this->setTemp(20); + this->setRoomTemp(25); + this->setMode(kArgoAuto); + this->setFan(kArgoFanAuto); +} + +uint8_t* IRArgoAC::getRaw(void) { + this->checksum(); // Ensure correct bit array before returning + return argo; +} + +void IRArgoAC::setRaw(const uint8_t state[]) { + for (uint8_t i = 0; i < kArgoStateLength; i++) argo[i] = state[i]; +} + +void IRArgoAC::on(void) { + // Bit 5 of byte 9 is on/off + // in MSB first + argo[9]|= kArgoPowerBit; // Set ON/OFF bit to 1 +} + +void IRArgoAC::off(void) { + // in MSB first + // bit 5 of byte 9 to off + argo[9] &= ~kArgoPowerBit; // Set on/off bit to 0 +} + +void IRArgoAC::setPower(const bool on) { + if (on) + this->on(); + else + this->off(); +} + +bool IRArgoAC::getPower(void) { return argo[9] & kArgoPowerBit; } + +void IRArgoAC::setMax(const bool on) { + if (on) + argo[9] |= kArgoMaxBit; + else + argo[9] &= ~kArgoMaxBit; +} + +bool IRArgoAC::getMax(void) { return argo[9] & kArgoMaxBit; } + +// Set the temp in deg C +// Sending 0 equals +4 +void IRArgoAC::setTemp(const uint8_t degrees) { + uint8_t temp = std::max(kArgoMinTemp, degrees); + temp = std::min(kArgoMaxTemp, temp); + + // offset 4 degrees. "If I want 12 degrees, I need to send 8" + temp -= kArgoTempOffset; + // Settemp = Bit 6,7 of byte 2, and bit 0-2 of byte 3 + // mask out bits + // argo[13] & 0x00000100; // mask out ON/OFF Bit + argo[2] &= ~kArgoTempLowMask; + argo[3] &= ~kArgoTempHighMask; + + // append to bit 6,7 + argo[2] += temp << 6; + // remove lowest to bits and append in 0-2 + argo[3] += ((temp >> 2) & kArgoTempHighMask); +} + +uint8_t IRArgoAC::getTemp(void) { + return (((argo[3] & kArgoTempHighMask) << 2 ) | (argo[2] >> 6)) + + kArgoTempOffset; +} + +// Set the speed of the fan +void IRArgoAC::setFan(const uint8_t fan) { + // Mask out bits + argo[3] &= ~kArgoFanMask; + // Set fan mode at bit positions + argo[3] |= (std::min(fan, kArgoFan3) << 3); +} + +uint8_t IRArgoAC::getFan(void) { return (argo[3] & kArgoFanMask) >> 3; } + +void IRArgoAC::setFlap(const uint8_t flap) { + flap_mode = flap; + // TODO(kaschmo): set correct bits for flap mode +} + +uint8_t IRArgoAC::getFlap(void) { return flap_mode; } + +uint8_t IRArgoAC::getMode(void) { + return (argo[2] & kArgoModeMask) >> 3; +} + +void IRArgoAC::setMode(const uint8_t mode) { + switch (mode) { + case kArgoCool: + case kArgoDry: + case kArgoAuto: + case kArgoOff: + case kArgoHeat: + case kArgoHeatAuto: + // Mask out bits + argo[2] &= ~kArgoModeMask; + // Set the mode at bit positions + argo[2] |= ((mode << 3) & kArgoModeMask); + return; + default: + this->setMode(kArgoAuto); + } +} + +void IRArgoAC::setNight(const bool on) { + if (on) + // Set bit at night position: bit 2 + argo[9] |= kArgoNightBit; + else + argo[9] &= ~kArgoNightBit; +} + +bool IRArgoAC::getNight(void) { return argo[9] & kArgoNightBit; } + +void IRArgoAC::setiFeel(const bool on) { + if (on) + // Set bit at iFeel position: bit 7 + argo[9] |= kArgoIFeelBit; + else + argo[9] &= ~kArgoIFeelBit; +} + +bool IRArgoAC::getiFeel(void) { return argo[9] & kArgoIFeelBit; } + +void IRArgoAC::setTime(void) { + // TODO(kaschmo): use function call from checksum to set time first +} + +void IRArgoAC::setRoomTemp(const uint8_t degrees) { + uint8_t temp = std::min(degrees, kArgoMaxRoomTemp); + temp = std::max(temp, kArgoTempOffset); + temp -= kArgoTempOffset;; + // Mask out bits + argo[3] &= ~kArgoRoomTempLowMask; + argo[4] &= ~kArgoRoomTempHighMask; + + argo[3] += temp << 5; // Append to bit 5,6,7 + argo[4] += temp >> 3; // Remove lowest 3 bits and append in 0,1 +} + +uint8_t IRArgoAC::getRoomTemp(void) { + return ((argo[4] & kArgoRoomTempHighMask) << 3 | (argo[3] >> 5)) + + kArgoTempOffset; +} + +// Convert a standard A/C mode into its native mode. +uint8_t IRArgoAC::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kArgoCool; + case stdAc::opmode_t::kHeat: + return kArgoHeat; + case stdAc::opmode_t::kDry: + return kArgoDry; + case stdAc::opmode_t::kOff: + return kArgoOff; + // No fan mode. + default: + return kArgoAuto; + } +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRArgoAC::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: + return kArgoFan1; + case stdAc::fanspeed_t::kMedium: + return kArgoFan2; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: + return kArgoFan3; + default: + return kArgoFanAuto; + } +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRArgoAC::convertSwingV(const stdAc::swingv_t position) { + switch (position) { + case stdAc::swingv_t::kHighest: + return kArgoFlapFull; + case stdAc::swingv_t::kHigh: + return kArgoFlap5; + case stdAc::swingv_t::kMiddle: + return kArgoFlap4; + case stdAc::swingv_t::kLow: + return kArgoFlap3; + case stdAc::swingv_t::kLowest: + return kArgoFlap1; + default: + return kArgoFlapAuto; + } +} + +// Convert a native mode to it's common equivalent. +stdAc::opmode_t IRArgoAC::toCommonMode(const uint8_t mode) { + switch (mode) { + case kArgoCool: return stdAc::opmode_t::kCool; + case kArgoHeat: return stdAc::opmode_t::kHeat; + case kArgoDry: return stdAc::opmode_t::kDry; + // No fan mode. + default: return stdAc::opmode_t::kAuto; + } +} + +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRArgoAC::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kArgoFan3: return stdAc::fanspeed_t::kMax; + case kArgoFan2: return stdAc::fanspeed_t::kMedium; + case kArgoFan1: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRArgoAC::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::ARGO; + result.power = this->getPower(); + result.mode = this->toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(); + result.fanspeed = this->toCommonFanSpeed(this->getFan()); + result.turbo = this->getMax(); + result.sleep = this->getNight() ? 0 : -1; + // Not supported. + result.model = -1; // Not supported. + result.swingv = stdAc::swingv_t::kOff; + result.swingh = stdAc::swingh_t::kOff; + result.light = false; + result.filter = false; + result.econo = false; + result.quiet = false; + result.clean = false; + result.beep = false; + result.clock = -1; + return result; +} + +// Convert the internal state into a human readable string. +String IRArgoAC::toString() { + String result = ""; + result.reserve(100); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(getPower(), F("Power"), false); + result += addIntToString(getMode(), F("Mode")); + switch (getMode()) { + case kArgoAuto: + result += F(" (AUTO)"); + break; + case kArgoCool: + result += F(" (COOL)"); + break; + case kArgoHeat: + result += F(" (HEAT)"); + break; + case kArgoDry: + result += F(" (DRY)"); + break; + case kArgoHeatAuto: + result += F(" (HEATAUTO)"); + break; + case kArgoOff: + result += F(" (OFF)"); + break; + default: + result += F(" (UNKNOWN)"); + } + result += addIntToString(getFan(), F("Fan")); + switch (getFan()) { + case kArgoFanAuto: + result += F(" (AUTO)"); + break; + case kArgoFan3: + result += F(" (MAX)"); + break; + case kArgoFan1: + result += F(" (MIN)"); + break; + case kArgoFan2: + result += F(" (MED)"); + break; + default: + result += F(" (UNKNOWN)"); + } + result += addTempToString(getTemp()); + result += F(", Room "); + result += addTempToString(getRoomTemp(), true, false); + result += addBoolToString(getMax(), F("Max")); + result += addBoolToString(getiFeel(), F("iFeel")); + result += addBoolToString(getNight(), F("Night")); + return result; +} + +#if DECODE_ARGO +// Decode the supplied Argo message. +// +// Args: +// results: Ptr to the data to decode and where to store the decode result. +// nbits: The number of data bits to expect. Typically kArgoBits. +// strict: Flag indicating if we should perform strict matching. +// Returns: +// boolean: True if it can decode it, false if it can't. +// +// Status: ALPHA / Probably doesn't work. +// +// Note: +// This decoder is based soley off sendArgo(). We have no actual captures +// to test this against. If you have one of these units, please let us know. +bool IRrecv::decodeArgo(decode_results *results, const uint16_t nbits, + const bool strict) { + if (strict && nbits != kArgoBits) return false; + + uint16_t offset = kStartOffset; + + // Match Header + Data + if (!matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, nbits, + kArgoHdrMark, kArgoHdrSpace, + kArgoBitMark, kArgoOneSpace, + kArgoBitMark, kArgoZeroSpace, + 0, 0, // Footer (None, allegedly. This seems very wrong.) + true, _tolerance, 0, false)) return false; + + // Compliance + // Verify we got a valid checksum. + if (strict && !IRArgoAC::validChecksum(results->state)) return false; + // Success + results->decode_type = decode_type_t::ARGO; + results->bits = nbits; + // No need to record the state as we stored it as we decoded it. + // As we use result->state, we don't record value, address, or command as it + // is a union data type. + return true; +} +#endif // DECODE_ARGO diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Argo.h b/lib/IRremoteESP8266-2.6.5/src/ir_Argo.h similarity index 52% rename from lib/IRremoteESP8266-2.6.0/src/ir_Argo.h rename to lib/IRremoteESP8266-2.6.5/src/ir_Argo.h index 883c2ddfd..49c8c42e5 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Argo.h +++ b/lib/IRremoteESP8266-2.6.5/src/ir_Argo.h @@ -1,9 +1,15 @@ -/* Copyright 2017 Schmolders +// Copyright 2017 Schmolders // Adds support for Argo Ulisse 13 DCI Mobile Split ACs. -*/ + +// Supports: +// Brand: Argo, Model: Ulisse 13 DCI Mobile Split A/C + #ifndef IR_ARGO_H_ #define IR_ARGO_H_ +#ifndef UNIT_TEST +#include +#endif #include "IRremoteESP8266.h" #include "IRsend.h" #ifdef UNIT_TEST @@ -33,19 +39,42 @@ // Constants. Store MSB left. -const uint8_t kArgoCoolOn = 0; // 0b000 -const uint8_t kArgoCoolOff = 3; // 0b110 -const uint8_t kArgoCoolAuto = 2; // 0b010 -const uint8_t kArgoCoolHum = 1; // 0b100 -const uint8_t kArgoHeatOn = 0; // 0b001 -const uint8_t kArgoHeatAuto = 1; // 0b101 -const uint8_t kArgoHeatBlink = 2; // 0b011 // ??no idea what mode that is -const uint8_t kArgoMinTemp = 10; // Celsius offset +4 -const uint8_t kArgoMaxTemp = 32; // Celsius +// byte[2] +const uint8_t kArgoHeatBit = 0b00100000; +const uint8_t kArgoModeMask = 0b00111000; +const uint8_t kArgoTempLowMask = 0b11000000; +const uint8_t kArgoCool = 0b000; // 0b000 (LSB) 0 +const uint8_t kArgoDry = 0b001; // 0b100 (LSB) 1 +const uint8_t kArgoAuto = 0b010; // 0b010 (LSB) 2 +const uint8_t kArgoOff = 0b011; // 0b110 (LSB) 3 +const uint8_t kArgoHeat = 0b100; // 0b001 (LSB) 4 +const uint8_t kArgoHeatAuto = 0b101; // 0b101 (LSB) 5 +// ?no idea what mode that is +const uint8_t kArgoHeatBlink = 0b110; // 0b011 (LSB) 6 + +// byte[3] +const uint8_t kArgoTempHighMask = 0b00000111; +const uint8_t kArgoFanMask = 0b00011000; +const uint8_t kArgoRoomTempLowMask = 0b11100000; const uint8_t kArgoFanAuto = 0; // 0b00 const uint8_t kArgoFan3 = 3; // 0b11 const uint8_t kArgoFan2 = 2; // 0b01 const uint8_t kArgoFan1 = 1; // 0b10 + +// byte[4] +const uint8_t kArgoRoomTempHighMask = 0b00000011; +const uint8_t kArgoTempOffset = 4; +const uint8_t kArgoMaxRoomTemp = ((1 << 5) - 1) + kArgoTempOffset; // 35C + +// byte[9] +const uint8_t kArgoPowerBit = 0b00100000; +const uint8_t kArgoMaxBit = 0b00001000; +const uint8_t kArgoNightBit = 0b00000100; +const uint8_t kArgoIFeelBit = 0b10000000; + +const uint8_t kArgoMinTemp = 10; // Celsius offset +4 +const uint8_t kArgoMaxTemp = 32; // Celsius + const uint8_t kArgoFlapAuto = 0; // 0b000 const uint8_t kArgoFlap1 = 1; // 0b100 const uint8_t kArgoFlap2 = 2; // 0b010 @@ -81,49 +110,58 @@ const uint8_t kArgoFlapFull = 7; // 0b111 class IRArgoAC { public: - explicit IRArgoAC(uint16_t pin); + explicit IRArgoAC(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); #if SEND_ARGO void send(const uint16_t repeat = kArgoDefaultRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } #endif // SEND_ARGO - void begin(); - void on(); - void off(); + void begin(void); + void on(void); + void off(void); - void setPower(bool state); - uint8_t getPower(); + void setPower(const bool on); + bool getPower(void); - void setTemp(uint8_t temp); - uint8_t getTemp(); + void setTemp(const uint8_t degrees); + uint8_t getTemp(void); - void setFan(uint8_t fan); - uint8_t getFan(); + void setFan(const uint8_t fan); + uint8_t getFan(void); - void setFlap(uint8_t flap); - uint8_t getFlap(); + void setFlap(const uint8_t flap); + uint8_t getFlap(void); - void setCoolMode(uint8_t mode); - uint8_t getCoolMode(); + void setMode(const uint8_t mode); + uint8_t getMode(void); - void setHeatMode(uint8_t mode); - uint8_t getHeatMode(); - uint8_t getMode(); + void setMax(const bool on); + bool getMax(void); - void setMax(bool state); - bool getMax(); + void setNight(const bool on); + bool getNight(void); - void setNight(bool state); - bool getNight(); + void setiFeel(const bool on); + bool getiFeel(void); - void setiFeel(bool state); - bool getiFeel(); + void setTime(void); + void setRoomTemp(const uint8_t degrees); + uint8_t getRoomTemp(void); - void setTime(); - void setRoomTemp(uint8_t temp); - - uint8_t* getRaw(); - uint8_t convertFan(const stdAc::fanspeed_t speed); - uint8_t convertSwingV(const stdAc::swingv_t position); + uint8_t* getRaw(void); + void setRaw(const uint8_t state[]); + static uint8_t calcChecksum(const uint8_t state[], + const uint16_t length = kArgoStateLength); + static bool validChecksum(const uint8_t state[], + const uint16_t length = kArgoStateLength); + static uint8_t convertMode(const stdAc::opmode_t mode); + static uint8_t convertFan(const stdAc::fanspeed_t speed); + static uint8_t convertSwingV(const stdAc::swingv_t position); + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + stdAc::state_t toCommon(void); + String toString(); #ifndef UNIT_TEST private: @@ -133,20 +171,13 @@ class IRArgoAC { #endif // # of bytes per command uint8_t argo[kArgoStateLength]; // Defined in IRremoteESP8266.h - void stateReset(); - void checksum(); + void stateReset(void); + void checksum(void); // Attributes - uint8_t set_temp; - uint8_t fan_mode; uint8_t flap_mode; - uint8_t ac_state; - uint8_t ac_mode; // heat 1, cool 0 uint8_t heat_mode; uint8_t cool_mode; - uint8_t night_mode; // on/off - uint8_t max_mode; // on/off - uint8_t ifeel_mode; // on/off }; #endif // IR_ARGO_H_ diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Carrier.cpp b/lib/IRremoteESP8266-2.6.5/src/ir_Carrier.cpp similarity index 67% rename from lib/IRremoteESP8266-2.6.0/src/ir_Carrier.cpp rename to lib/IRremoteESP8266-2.6.5/src/ir_Carrier.cpp index 350d61cc1..e31ddd55f 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Carrier.cpp +++ b/lib/IRremoteESP8266-2.6.5/src/ir_Carrier.cpp @@ -1,23 +1,20 @@ // Copyright 2018 David Conran +// Supports: +// Brand: Carrier/Surrey, Model: 42QG5A55970 remote +// Brand: Carrier/Surrey, Model: 619EGX0090E0 A/C +// Brand: Carrier/Surrey, Model: 619EGX0120E0 A/C +// Brand: Carrier/Surrey, Model: 619EGX0180E0 A/C +// Brand: Carrier/Surrey, Model: 619EGX0220E0 A/C +// Brand: Carrier/Surrey, Model: 53NGK009/012 Inverter + #include "IRrecv.h" #include "IRsend.h" #include "IRutils.h" -// CCCCC AAA RRRRRR RRRRRR IIIII EEEEEEE RRRRRR -// CC C AAAAA RR RR RR RR III EE RR RR -// CC AA AA RRRRRR RRRRRR III EEEEE RRRRRR -// CC C AAAAAAA RR RR RR RR III EE RR RR -// CCCCC AA AA RR RR RR RR IIIII EEEEEEE RR RR - -// Suits Carrier/Surrey HVAC models: -// 42QG5A55970 (remote) -// 619EGX0090E0 / 619EGX0120E0 / 619EGX0180E0 / 619EGX0220E0 (indoor units) -// 53NGK009/012 (inverter) - // Constants // Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/385 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/385 const uint16_t kCarrierAcHdrMark = 8532; const uint16_t kCarrierAcHdrSpace = 4228; const uint16_t kCarrierAcBitMark = 628; @@ -77,22 +74,16 @@ bool IRrecv::decodeCarrierAC(decode_results *results, uint16_t nbits, for (uint8_t i = 0; i < 3; i++) { prev_data = data; - // Header - if (!matchMark(results->rawbuf[offset++], kCarrierAcHdrMark)) return false; - if (!matchSpace(results->rawbuf[offset++], kCarrierAcHdrSpace)) - return false; - // Data - match_result_t data_result = - matchData(&(results->rawbuf[offset]), nbits, kCarrierAcBitMark, - kCarrierAcOneSpace, kCarrierAcBitMark, kCarrierAcZeroSpace); - if (data_result.success == false) return false; - data = data_result.data; - offset += data_result.used; - // Footer - if (!matchMark(results->rawbuf[offset++], kCarrierAcBitMark)) return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset++], kCarrierAcGap)) - return false; + // Match Header + Data + Footer + uint16_t used; + used = matchGeneric(results->rawbuf + offset, &data, + results->rawlen - offset, nbits, + kCarrierAcHdrMark, kCarrierAcHdrSpace, + kCarrierAcBitMark, kCarrierAcOneSpace, + kCarrierAcBitMark, kCarrierAcZeroSpace, + kCarrierAcBitMark, kCarrierAcGap, true); + if (!used) return false; + offset += used; // Compliance. if (strict) { // Check if the data is an inverted copy of the previous data. diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Coolix.cpp b/lib/IRremoteESP8266-2.6.5/src/ir_Coolix.cpp similarity index 71% rename from lib/IRremoteESP8266-2.6.0/src/ir_Coolix.cpp rename to lib/IRremoteESP8266-2.6.5/src/ir_Coolix.cpp index 2659a1d88..616b417ce 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Coolix.cpp +++ b/lib/IRremoteESP8266-2.6.5/src/ir_Coolix.cpp @@ -1,5 +1,5 @@ // Copyright bakrus -// Copyright 2017 David Conran +// Copyright 2017,2019 David Conran #include "ir_Coolix.h" #include @@ -10,18 +10,16 @@ #include "IRsend.h" #include "IRutils.h" -// CCCCC OOOOO OOOOO LL IIIII XX XX -// CC C OO OO OO OO LL III XX XX -// CC OO OO OO OO LL III XXXX -// CC C OO OO OO OO LL III XX XX -// CCCCC OOOO0 OOOO0 LLLLLLL IIIII XX XX - // Coolix A/C / heatpump added by (send) bakrus & (decode) crankyoldgit // // Supports: -// RG57K7(B)/BGEF remote control for Beko BINR 070/071 split-type aircon. +// Brand: Beko, Model: RG57K7(B)/BGEF Remote +// Brand: Beko, Model: BINR 070/071 split-type A/C +// Brand: Midea, Model: RG52D/BGE Remote +// Brand: Midea, Model: MS12FU-10HRDN1-QRD0GW(B) A/C +// Brand: Midea, Model: MSABAU-07HRFN1-QRD0GW A/C (circa 2016) // Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/484 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/484 // Constants // Pulse parms are *50-100 for the Mark and *50+100 for the space @@ -41,6 +39,12 @@ const uint16_t kCoolixHdrSpace = kCoolixHdrSpaceTicks * kCoolixTick; const uint16_t kCoolixMinGapTicks = kCoolixHdrMarkTicks + kCoolixZeroSpaceTicks; const uint16_t kCoolixMinGap = kCoolixMinGapTicks * kCoolixTick; +using irutils::addBoolToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addTempToString; + #if SEND_COOLIX // Send a Coolix message // @@ -83,6 +87,7 @@ void IRsend::sendCOOLIX(uint64_t data, uint16_t nbits, uint16_t repeat) { mark(kCoolixBitMark); space(kCoolixMinGap); // Pause before repeating } + space(kDefaultMessageGap); } #endif @@ -90,8 +95,10 @@ void IRsend::sendCOOLIX(uint64_t data, uint16_t nbits, uint16_t repeat) { // Supports: // RG57K7(B)/BGEF remote control for Beko BINR 070/071 split-type aircon. // Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/484 -IRCoolixAC::IRCoolixAC(uint16_t pin) : _irsend(pin) { stateReset(); } +// https://github.com/crankyoldgit/IRremoteESP8266/issues/484 +IRCoolixAC::IRCoolixAC(const uint16_t pin, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { stateReset(); } void IRCoolixAC::stateReset() { setRaw(kCoolixDefaultState); } @@ -200,6 +207,14 @@ void IRCoolixAC::setPower(const bool power) { } } +void IRCoolixAC::on(void) { + this->setPower(true); +} + +void IRCoolixAC::off(void) { + this->setPower(false); +} + bool IRCoolixAC::getSwing() { return remote_state == kCoolixSwing; } void IRCoolixAC::setSwing() { @@ -260,18 +275,30 @@ void IRCoolixAC::clearSensorTemp() { void IRCoolixAC::setMode(const uint8_t mode) { uint32_t actualmode = mode; + switch (actualmode) { + case kCoolixAuto: + case kCoolixDry: + if (this->getFan() == kCoolixFanAuto) + // No kCoolixFanAuto in Dry/Auto mode. + this->setFan(kCoolixFanAuto0, false); + break; + case kCoolixCool: + case kCoolixHeat: + case kCoolixFan: + if (this->getFan() == kCoolixFanAuto0) + // kCoolixFanAuto0 only in Dry/Auto mode. + this->setFan(kCoolixFanAuto, false); + break; + default: // Anything else, go with Auto mode. + this->setMode(kCoolixAuto); + return; + } // Fan mode is a special case of Dry. if (mode == kCoolixFan) actualmode = kCoolixDry; - switch (actualmode) { - case kCoolixCool: - case kCoolixAuto: - case kCoolixHeat: - case kCoolixDry: - recoverSavedState(); - remote_state = (remote_state & ~kCoolixModeMask) | (actualmode << 2); - // Force the temp into a known-good state. - setTemp(getTemp()); - } + recoverSavedState(); + remote_state = (remote_state & ~kCoolixModeMask) | (actualmode << 2); + // Force the temp into a known-good state. + setTemp(getTemp()); if (mode == kCoolixFan) setTempRaw(kCoolixFanTempCode); } @@ -286,15 +313,33 @@ uint8_t IRCoolixAC::getFan() { return (getNormalState() & kCoolixFanMask) >> 13; } -void IRCoolixAC::setFan(const uint8_t speed) { +void IRCoolixAC::setFan(const uint8_t speed, const bool modecheck) { recoverSavedState(); uint8_t newspeed = speed; switch (speed) { + case kCoolixFanAuto: // Dry & Auto mode can't have this speed. + if (modecheck) { + switch (this->getMode()) { + case kCoolixAuto: + case kCoolixDry: + newspeed = kCoolixFanAuto0; + } + } + break; + case kCoolixFanAuto0: // Only Dry & Auto mode can have this speed. + if (modecheck) { + switch (this->getMode()) { + case kCoolixAuto: + case kCoolixDry: + break; + default: + newspeed = kCoolixFanAuto; + } + } + break; case kCoolixFanMin: case kCoolixFanMed: case kCoolixFanMax: - case kCoolixFanAuto: - case kCoolixFanAuto0: case kCoolixFanZoneFollow: case kCoolixFanFixed: break; @@ -337,21 +382,88 @@ uint8_t IRCoolixAC::convertFan(const stdAc::fanspeed_t speed) { } } +// Convert a native mode to it's common equivalent. +stdAc::opmode_t IRCoolixAC::toCommonMode(const uint8_t mode) { + switch (mode) { + case kCoolixCool: return stdAc::opmode_t::kCool; + case kCoolixHeat: return stdAc::opmode_t::kHeat; + case kCoolixDry: return stdAc::opmode_t::kDry; + case kCoolixFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRCoolixAC::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kCoolixFanMax: return stdAc::fanspeed_t::kMax; + case kCoolixFanMed: return stdAc::fanspeed_t::kMedium; + case kCoolixFanMin: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +// Convert the A/C state to it's common equivalent. Utilise the previous +// state if supplied. +stdAc::state_t IRCoolixAC::toCommon(const stdAc::state_t *prev) { + stdAc::state_t result; + // Start with the previous state if given it. + if (prev != NULL) { + result = *prev; + } else { + // Set defaults for non-zero values that are not implicitly set for when + // there is no previous state. + result.swingv = stdAc::swingv_t::kOff; + result.sleep = -1; + } + // Not supported. + result.model = -1; // No models used. + result.swingh = stdAc::swingh_t::kOff; + result.quiet = false; + result.econo = false; + result.filter = false; + result.beep = false; + result.clock = -1; + + // Supported. + result.protocol = decode_type_t::COOLIX; + result.celsius = true; + result.power = this->getPower(); + // Power off state no other state info. Use the previous state if we have it. + if (!result.power) return result; + // Handle the special single command (Swing/Turbo/Light/Clean/Sleep) toggle + // messages. These have no other state info so use the rest of the previous + // state if we have it for them. + if (this->getSwing()) { + result.swingv = result.swingv != stdAc::swingv_t::kOff ? + stdAc::swingv_t::kOff : stdAc::swingv_t::kAuto; // Invert swing. + return result; + } else if (this->getTurbo()) { + result.turbo = !result.turbo; + return result; + } else if (this->getLed()) { + result.light = !result.light; + return result; + } else if (this->getClean()) { + result.clean = !result.clean; + return result; + } else if (this->getSleep()) { + result.sleep = result.sleep >= 0 ? -1 : 0; // Invert sleep. + return result; + } + // Back to "normal" stateful messages. + result.mode = this->toCommonMode(this->getMode()); + result.degrees = this->getTemp(); + result.fanspeed = this->toCommonFanSpeed(this->getFan()); + return result; +} + // Convert the internal state into a human readable string. -#ifdef ARDUINO String IRCoolixAC::toString() { String result = ""; -#else -std::string IRCoolixAC::toString() { - std::string result = ""; -#endif // ARDUINO - result += F("Power: "); - if (getPower()) { - result += F("On"); - } else { - result += F("Off"); - return result; // If it's off, there is no other info. - } + result.reserve(100); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(getPower(), F("Power"), false); + if (!getPower()) return result; // If it's off, there is no other info. // Special modes. if (getSwing()) { result += F(", Swing: Toggle"); @@ -373,29 +485,10 @@ std::string IRCoolixAC::toString() { result += F(", Clean: Toggle"); return result; } - result += F(", Mode: "); - result += uint64ToString(getMode()); - switch (getMode()) { - case kCoolixAuto: - result += F(" (AUTO)"); - break; - case kCoolixCool: - result += F(" (COOL)"); - break; - case kCoolixHeat: - result += F(" (HEAT)"); - break; - case kCoolixDry: - result += F(" (DRY)"); - break; - case kCoolixFan: - result += F(" (FAN)"); - break; - default: - result += F(" (UNKNOWN)"); - } - result += F(", Fan: "); - result += uint64ToString(getFan()); + result += addModeToString(getMode(), kCoolixAuto, + kCoolixCool, kCoolixHeat, + kCoolixDry, kCoolixFan); + result += addIntToString(getFan(), F("Fan")); switch (getFan()) { case kCoolixFanAuto: result += F(" (AUTO)"); @@ -421,16 +514,9 @@ std::string IRCoolixAC::toString() { default: result += F(" (UNKNOWN)"); } - if (getMode() != kCoolixFan) { // Fan mode doesn't have a temperature. - result += F(", Temp: "); - result += uint64ToString(getTemp()); - result += 'C'; - } - result += F(", Zone Follow: "); - if (getZoneFollow()) - result += F("On"); - else - result += F("Off"); + // Fan mode doesn't have a temperature. + if (getMode() != kCoolixFan) result += addTempToString(getTemp()); + result += addBoolToString(getZoneFollow(), F("Zone Follow")); result += F(", Sensor Temp: "); if (getSensorTemp() > kCoolixSensorTempMax) result += F("Ignored"); diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Coolix.h b/lib/IRremoteESP8266-2.6.5/src/ir_Coolix.h similarity index 84% rename from lib/IRremoteESP8266-2.6.0/src/ir_Coolix.h rename to lib/IRremoteESP8266-2.6.5/src/ir_Coolix.h index d85db98d7..0c2e44676 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Coolix.h +++ b/lib/IRremoteESP8266-2.6.5/src/ir_Coolix.h @@ -9,8 +9,6 @@ #include #ifndef UNIT_TEST #include -#else -#include #endif #include "IRremoteESP8266.h" #include "IRsend.h" @@ -18,16 +16,14 @@ #include "IRsend_test.h" #endif -// CCCCC OOOOO OOOOO LL IIIII XX XX -// CC C OO OO OO OO LL III XX XX -// CC OO OO OO OO LL III XXXX -// CC C OO OO OO OO LL III XX XX -// CCCCC OOOO0 OOOO0 LLLLLLL IIIII XX XX - // Supports: -// RG57K7(B)/BGEF remote control for Beko BINR 070/071 split-type aircon. +// Brand: Beko, Model: RG57K7(B)/BGEF Remote +// Brand: Beko, Model: BINR 070/071 split-type A/C +// Brand: Midea, Model: RG52D/BGE Remote +// Brand: Midea, Model: MS12FU-10HRDN1-QRD0GW(B) A/C +// Brand: Midea, Model: MSABAU-07HRFN1-QRD0GW A/C (circa 2016) // Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/484 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/484 // Kudos: // Hamper: For the breakdown and mapping of the bit values. @@ -90,11 +86,13 @@ const uint32_t kCoolixDefaultState = 0b101100101011111111001000; // 0xB2BFC8 // Classes class IRCoolixAC { public: - explicit IRCoolixAC(uint16_t pin); + explicit IRCoolixAC(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); void stateReset(); #if SEND_COOLIX void send(const uint16_t repeat = kCoolixDefaultRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } #endif // SEND_COOLIX void begin(); void on(); @@ -106,7 +104,7 @@ class IRCoolixAC { void setSensorTemp(const uint8_t desired); uint8_t getSensorTemp(); void clearSensorTemp(); - void setFan(const uint8_t fan); + void setFan(const uint8_t speed, const bool modecheck = true); uint8_t getFan(); void setMode(const uint8_t mode); uint8_t getMode(); @@ -125,11 +123,10 @@ class IRCoolixAC { void setRaw(const uint32_t new_code); uint8_t convertMode(const stdAc::opmode_t mode); uint8_t convertFan(const stdAc::fanspeed_t speed); -#ifdef ARDUINO + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + stdAc::state_t toCommon(const stdAc::state_t *prev = NULL); String toString(); -#else - std::string toString(); -#endif #ifndef UNIT_TEST private: diff --git a/lib/IRremoteESP8266-2.6.5/src/ir_Daikin.cpp b/lib/IRremoteESP8266-2.6.5/src/ir_Daikin.cpp new file mode 100644 index 000000000..317526f5e --- /dev/null +++ b/lib/IRremoteESP8266-2.6.5/src/ir_Daikin.cpp @@ -0,0 +1,3069 @@ +/* +An Arduino sketch to emulate IR Daikin ARC433** & ARC477A1 remote control unit +Read more at: +http://harizanov.com/2012/02/control-daikin-air-conditioner-over-the-internet/ + +Copyright 2016 sillyfrog +Copyright 2017 sillyfrog, crankyoldgit +Copyright 2018-2019 crankyoldgit +Copyright 2019 pasna (IRDaikin160 class / Daikin176 class) +*/ + +#include "ir_Daikin.h" +#include +#ifndef ARDUINO +#include +#endif +#include "IRrecv.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif +#include "IRutils.h" + +// Constants +// Ref: +// https://github.com/mharizanov/Daikin-AC-remote-control-over-the-Internet/tree/master/IRremote +// http://rdlab.cdmt.vn/project-2013/daikin-ir-protocol +// https://github.com/crankyoldgit/IRremoteESP8266/issues/582 + +using irutils::addBoolToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addTempToString; +using irutils::addFanToString; +using irutils::bcdToUint8; +using irutils::minsToString; +using irutils::sumNibbles; +using irutils::uint8ToBcd; + + +#if SEND_DAIKIN +// Send a Daikin A/C message. +// +// Args: +// data: An array of kDaikinStateLength bytes containing the IR command. +// +// Status: STABLE +// +// Ref: +// IRDaikinESP.cpp +// https://github.com/mharizanov/Daikin-AC-remote-control-over-the-Internet/tree/master/IRremote +// https://github.com/blafois/Daikin-IR-Reverse +void IRsend::sendDaikin(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes < kDaikinStateLengthShort) + return; // Not enough bytes to send a proper message. + + for (uint16_t r = 0; r <= repeat; r++) { + uint16_t offset = 0; + // Send the header, 0b00000 + sendGeneric(0, 0, // No header for the header + kDaikinBitMark, kDaikinOneSpace, kDaikinBitMark, + kDaikinZeroSpace, kDaikinBitMark, kDaikinZeroSpace + kDaikinGap, + (uint64_t)0b00000, kDaikinHeaderLength, 38, false, 0, 50); + // Data #1 + if (nbytes < kDaikinStateLength) { // Are we using the legacy size? + // Do this as a constant to save RAM and keep in flash memory + sendGeneric(kDaikinHdrMark, kDaikinHdrSpace, kDaikinBitMark, + kDaikinOneSpace, kDaikinBitMark, kDaikinZeroSpace, + kDaikinBitMark, kDaikinZeroSpace + kDaikinGap, + kDaikinFirstHeader64, 64, 38, false, 0, 50); + } else { // We are using the newer/more correct size. + sendGeneric(kDaikinHdrMark, kDaikinHdrSpace, kDaikinBitMark, + kDaikinOneSpace, kDaikinBitMark, kDaikinZeroSpace, + kDaikinBitMark, kDaikinZeroSpace + kDaikinGap, + data, kDaikinSection1Length, 38, false, 0, 50); + offset += kDaikinSection1Length; + } + // Data #2 + sendGeneric(kDaikinHdrMark, kDaikinHdrSpace, kDaikinBitMark, + kDaikinOneSpace, kDaikinBitMark, kDaikinZeroSpace, + kDaikinBitMark, kDaikinZeroSpace + kDaikinGap, + data + offset, kDaikinSection2Length, 38, false, 0, 50); + offset += kDaikinSection2Length; + // Data #3 + sendGeneric(kDaikinHdrMark, kDaikinHdrSpace, kDaikinBitMark, + kDaikinOneSpace, kDaikinBitMark, kDaikinZeroSpace, + kDaikinBitMark, kDaikinZeroSpace + kDaikinGap, + data + offset, nbytes - offset, 38, false, 0, 50); + } +} +#endif // SEND_DAIKIN + +IRDaikinESP::IRDaikinESP(const uint16_t pin, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { stateReset(); } + +void IRDaikinESP::begin(void) { _irsend.begin(); } + +#if SEND_DAIKIN +void IRDaikinESP::send(const uint16_t repeat) { + this->checksum(); + _irsend.sendDaikin(remote, kDaikinStateLength, repeat); +} +#endif // SEND_DAIKIN + +// Verify the checksums are valid for a given state. +// Args: +// state: The array to verify the checksums of. +// length: The size of the state. +// Returns: +// A boolean. +bool IRDaikinESP::validChecksum(uint8_t state[], const uint16_t length) { + // Data #1 + if (length < kDaikinSection1Length || + state[kDaikinByteChecksum1] != sumBytes(state, kDaikinSection1Length - 1)) + return false; + // Data #2 + if (length < kDaikinSection1Length + kDaikinSection2Length || + state[kDaikinByteChecksum2] != sumBytes(state + kDaikinSection1Length, + kDaikinSection2Length - 1)) + return false; + // Data #3 + if (length < kDaikinSection1Length + kDaikinSection2Length + 2 || + state[length - 1] != sumBytes(state + kDaikinSection1Length + + kDaikinSection2Length, + length - (kDaikinSection1Length + + kDaikinSection2Length) - 1)) + return false; + return true; +} + +// Calculate and set the checksum values for the internal state. +void IRDaikinESP::checksum(void) { + remote[kDaikinByteChecksum1] = sumBytes(remote, kDaikinSection1Length - 1); + remote[kDaikinByteChecksum2] = sumBytes(remote + kDaikinSection1Length, + kDaikinSection2Length - 1); + remote[kDaikinByteChecksum3] = sumBytes(remote + kDaikinSection1Length + + kDaikinSection2Length, + kDaikinSection3Length - 1); +} + +void IRDaikinESP::stateReset(void) { + for (uint8_t i = 0; i < kDaikinStateLength; i++) remote[i] = 0x0; + + remote[0] = 0x11; + remote[1] = 0xDA; + remote[2] = 0x27; + remote[4] = 0xC5; + // remote[7] is a checksum byte, it will be set by checksum(). + + remote[8] = 0x11; + remote[9] = 0xDA; + remote[10] = 0x27; + remote[12] = 0x42; + // remote[15] is a checksum byte, it will be set by checksum(). + remote[16] = 0x11; + remote[17] = 0xDA; + remote[18] = 0x27; + remote[21] = 0x49; + remote[22] = 0x1E; + remote[24] = 0xB0; + remote[27] = 0x06; + remote[28] = 0x60; + remote[31] = 0xC0; + // remote[34] is a checksum byte, it will be set by checksum(). + this->checksum(); +} + +uint8_t *IRDaikinESP::getRaw(void) { + this->checksum(); // Ensure correct settings before sending. + return remote; +} + +void IRDaikinESP::setRaw(const uint8_t new_code[], const uint16_t length) { + uint8_t offset = 0; + if (length == kDaikinStateLengthShort) { // Handle the "short" length case. + offset = kDaikinStateLength - kDaikinStateLengthShort; + this->stateReset(); + } + for (uint8_t i = 0; i < length && i < kDaikinStateLength; i++) + remote[i + offset] = new_code[i]; +} + +void IRDaikinESP::on(void) { remote[kDaikinBytePower] |= kDaikinBitPower; } + +void IRDaikinESP::off(void) { remote[kDaikinBytePower] &= ~kDaikinBitPower; } + +void IRDaikinESP::setPower(const bool on) { + if (on) + this->on(); + else + this->off(); +} + +bool IRDaikinESP::getPower(void) { + return remote[kDaikinBytePower] & kDaikinBitPower; +} + +// Set the temp in deg C +void IRDaikinESP::setTemp(const uint8_t temp) { + uint8_t degrees = std::max(temp, kDaikinMinTemp); + degrees = std::min(degrees, kDaikinMaxTemp); + remote[kDaikinByteTemp] = degrees << 1; +} + +uint8_t IRDaikinESP::getTemp(void) { return remote[kDaikinByteTemp] >> 1; } + +// Set the speed of the fan, 1-5 or kDaikinFanAuto or kDaikinFanQuiet +void IRDaikinESP::setFan(const uint8_t fan) { + // Set the fan speed bits, leave low 4 bits alone + uint8_t fanset; + if (fan == kDaikinFanQuiet || fan == kDaikinFanAuto) + fanset = fan; + else if (fan < kDaikinFanMin || fan > kDaikinFanMax) + fanset = kDaikinFanAuto; + else + fanset = 2 + fan; + remote[kDaikinByteFan] &= 0x0F; + remote[kDaikinByteFan] |= (fanset << 4); +} + +uint8_t IRDaikinESP::getFan(void) { + uint8_t fan = remote[kDaikinByteFan] >> 4; + if (fan != kDaikinFanQuiet && fan != kDaikinFanAuto) fan -= 2; + return fan; +} + +uint8_t IRDaikinESP::getMode(void) { return remote[kDaikinBytePower] >> 4; } + +void IRDaikinESP::setMode(const uint8_t mode) { + switch (mode) { + case kDaikinAuto: + case kDaikinCool: + case kDaikinHeat: + case kDaikinFan: + case kDaikinDry: + remote[kDaikinBytePower] &= 0b10001111; + remote[kDaikinBytePower] |= (mode << 4); + break; + default: + this->setMode(kDaikinAuto); + } +} + +void IRDaikinESP::setSwingVertical(const bool on) { + if (on) + remote[kDaikinByteFan] |= 0x0F; + else + remote[kDaikinByteFan] &= 0xF0; +} + +bool IRDaikinESP::getSwingVertical(void) { + return remote[kDaikinByteFan] & 0x0F; +} + +void IRDaikinESP::setSwingHorizontal(const bool on) { + if (on) + remote[kDaikinByteSwingH] |= 0x0F; + else + remote[kDaikinByteSwingH] &= 0xF0; +} + +bool IRDaikinESP::getSwingHorizontal(void) { + return remote[kDaikinByteSwingH] & 0x0F; +} + +void IRDaikinESP::setQuiet(const bool on) { + if (on) { + remote[kDaikinByteSilent] |= kDaikinBitSilent; + // Powerful & Quiet mode being on are mutually exclusive. + this->setPowerful(false); + } else { + remote[kDaikinByteSilent] &= ~kDaikinBitSilent; + } +} + +bool IRDaikinESP::getQuiet(void) { + return remote[kDaikinByteSilent] & kDaikinBitSilent; +} + +void IRDaikinESP::setPowerful(const bool on) { + if (on) { + remote[kDaikinBytePowerful] |= kDaikinBitPowerful; + // Powerful, Quiet, & Econo mode being on are mutually exclusive. + this->setQuiet(false); + this->setEcono(false); + } else { + remote[kDaikinBytePowerful] &= ~kDaikinBitPowerful; + } +} + +bool IRDaikinESP::getPowerful(void) { + return remote[kDaikinBytePowerful] & kDaikinBitPowerful; +} + +void IRDaikinESP::setSensor(const bool on) { + if (on) + remote[kDaikinByteSensor] |= kDaikinBitSensor; + else + remote[kDaikinByteSensor] &= ~kDaikinBitSensor; +} + +bool IRDaikinESP::getSensor(void) { + return remote[kDaikinByteSensor] & kDaikinBitSensor; +} + +void IRDaikinESP::setEcono(const bool on) { + if (on) { + remote[kDaikinByteEcono] |= kDaikinBitEcono; + // Powerful & Econo mode being on are mutually exclusive. + this->setPowerful(false); + } else { + remote[kDaikinByteEcono] &= ~kDaikinBitEcono; + } +} + +bool IRDaikinESP::getEcono(void) { + return remote[kDaikinByteEcono] & kDaikinBitEcono; +} + +void IRDaikinESP::setMold(const bool on) { + if (on) + remote[kDaikinByteMold] |= kDaikinBitMold; + else + remote[kDaikinByteMold] &= ~kDaikinBitMold; +} + +bool IRDaikinESP::getMold(void) { + return remote[kDaikinByteMold] & kDaikinBitMold; +} + +void IRDaikinESP::setComfort(const bool on) { + if (on) + remote[kDaikinByteComfort] |= kDaikinBitComfort; + else + remote[kDaikinByteComfort] &= ~kDaikinBitComfort; +} + +bool IRDaikinESP::getComfort(void) { + return remote[kDaikinByteComfort] & kDaikinBitComfort; +} + +// starttime: Number of minutes after midnight. +void IRDaikinESP::enableOnTimer(const uint16_t starttime) { + remote[kDaikinByteOnTimer] |= kDaikinBitOnTimer; + remote[kDaikinByteOnTimerMinsLow] = starttime; + // only keep 4 bits + remote[kDaikinByteOnTimerMinsHigh] &= 0xF0; + remote[kDaikinByteOnTimerMinsHigh] |= ((starttime >> 8) & 0x0F); +} + +void IRDaikinESP::disableOnTimer(void) { + this->enableOnTimer(kDaikinUnusedTime); + remote[kDaikinByteOnTimer] &= ~kDaikinBitOnTimer; +} + +uint16_t IRDaikinESP::getOnTime(void) { + return ((remote[kDaikinByteOnTimerMinsHigh] & 0x0F) << 8) + + remote[kDaikinByteOnTimerMinsLow]; +} + +bool IRDaikinESP::getOnTimerEnabled(void) { + return remote[kDaikinByteOnTimer] & kDaikinBitOnTimer; +} + +// endtime: Number of minutes after midnight. +void IRDaikinESP::enableOffTimer(const uint16_t endtime) { + remote[kDaikinByteOffTimer] |= kDaikinBitOffTimer; + remote[kDaikinByteOffTimerMinsHigh] = endtime >> 4; + remote[kDaikinByteOffTimerMinsLow] &= 0x0F; + remote[kDaikinByteOffTimerMinsLow] |= ((endtime & 0x0F) << 4); +} + +void IRDaikinESP::disableOffTimer(void) { + this->enableOffTimer(kDaikinUnusedTime); + remote[kDaikinByteOffTimer] &= ~kDaikinBitOffTimer; +} + +uint16_t IRDaikinESP::getOffTime(void) { + return (remote[kDaikinByteOffTimerMinsHigh] << 4) + + ((remote[kDaikinByteOffTimerMinsLow] & 0xF0) >> 4); +} + +bool IRDaikinESP::getOffTimerEnabled(void) { + return remote[kDaikinByteOffTimer] & kDaikinBitOffTimer; +} + +void IRDaikinESP::setCurrentTime(const uint16_t mins_since_midnight) { + uint16_t mins = mins_since_midnight; + if (mins > 24 * 60) mins = 0; // If > 23:59, set to 00:00 + remote[kDaikinByteClockMinsLow] = mins; + // only keep 3 bits + remote[kDaikinByteClockMinsHigh] &= 0xF8; + remote[kDaikinByteClockMinsHigh] |= ((mins >> 8) & 0x07); +} + +uint16_t IRDaikinESP::getCurrentTime(void) { + return ((remote[kDaikinByteClockMinsHigh] & 0x07) << 8) + + remote[kDaikinByteClockMinsLow]; +} + +void IRDaikinESP::setCurrentDay(const uint8_t day_of_week) { + // 1 is SUN, 2 is MON, ..., 7 is SAT + uint8_t days = day_of_week; + if (days > 7) days = 0; // Enforce the limit + // Update bits 5-3 + remote[kDaikinByteClockMinsHigh] &= 0xc7; + remote[kDaikinByteClockMinsHigh] |= days << 3; +} + +uint8_t IRDaikinESP::getCurrentDay(void) { + return ((remote[kDaikinByteClockMinsHigh] & 0x38) >> 3); +} + +void IRDaikinESP::setWeeklyTimerEnable(const bool on) { + if (on) + remote[kDaikinByteWeeklyTimer] &= ~kDaikinBitWeeklyTimer; // Clear the bit. + else + remote[kDaikinByteWeeklyTimer] |= kDaikinBitWeeklyTimer; // Set the bit. +} + +bool IRDaikinESP::getWeeklyTimerEnable(void) { + return !(remote[kDaikinByteWeeklyTimer] & kDaikinBitWeeklyTimer); +} + +// Convert a standard A/C mode into its native mode. +uint8_t IRDaikinESP::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kDaikinCool; + case stdAc::opmode_t::kHeat: + return kDaikinHeat; + case stdAc::opmode_t::kDry: + return kDaikinDry; + case stdAc::opmode_t::kFan: + return kDaikinFan; + default: + return kDaikinAuto; + } +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRDaikinESP::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + return kDaikinFanQuiet; + case stdAc::fanspeed_t::kLow: + return kDaikinFanMin; + case stdAc::fanspeed_t::kMedium: + return kDaikinFanMin + 1; + case stdAc::fanspeed_t::kHigh: + return kDaikinFanMax - 1; + case stdAc::fanspeed_t::kMax: + return kDaikinFanMax; + default: + return kDaikinFanAuto; + } +} + +// Convert a native mode to it's common equivalent. +stdAc::opmode_t IRDaikinESP::toCommonMode(const uint8_t mode) { + switch (mode) { + case kDaikinCool: return stdAc::opmode_t::kCool; + case kDaikinHeat: return stdAc::opmode_t::kHeat; + case kDaikinDry: return stdAc::opmode_t::kDry; + case kDaikinFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRDaikinESP::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kDaikinFanMax: return stdAc::fanspeed_t::kMax; + case kDaikinFanMax - 1: return stdAc::fanspeed_t::kHigh; + case kDaikinFanMin + 1: return stdAc::fanspeed_t::kMedium; + case kDaikinFanMin: return stdAc::fanspeed_t::kLow; + case kDaikinFanQuiet: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRDaikinESP::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::DAIKIN; + result.model = -1; // No models used. + result.power = this->getPower(); + result.mode = this->toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(); + result.fanspeed = this->toCommonFanSpeed(this->getFan()); + result.swingv = this->getSwingVertical() ? stdAc::swingv_t::kAuto : + stdAc::swingv_t::kOff; + result.swingh = this->getSwingHorizontal() ? stdAc::swingh_t::kAuto : + stdAc::swingh_t::kOff; + result.quiet = this->getQuiet(); + result.turbo = this->getPowerful(); + result.clean = this->getMold(); + result.econo = this->getEcono(); + // Not supported. + result.filter = false; + result.light = false; + result.beep = false; + result.sleep = -1; + result.clock = -1; + return result; +} + +// Convert the internal state into a human readable string. +String IRDaikinESP::toString(void) { + String result = ""; + result.reserve(230); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(getPower(), F("Power"), false); + result += addModeToString(getMode(), kDaikinAuto, kDaikinCool, kDaikinHeat, + kDaikinDry, kDaikinFan); + result += addTempToString(getTemp()); + result += addFanToString(getFan(), kDaikinFanMax, kDaikinFanMin, + kDaikinFanAuto, kDaikinFanQuiet, kDaikinFanMed); + result += addBoolToString(getPowerful(), F("Powerful")); + result += addBoolToString(getQuiet(), F("Quiet")); + result += addBoolToString(getSensor(), F("Sensor")); + result += addBoolToString(getMold(), F("Mold")); + result += addBoolToString(getComfort(), F("Comfort")); + result += addBoolToString(getSwingHorizontal(), F("Swing (Horizontal)")); + result += addBoolToString(getSwingVertical(), F("Swing (Vertical)")); + result += addLabeledString(minsToString(this->getCurrentTime()), + F("Current Time")); + result += F(", Current Day: "); + switch (this->getCurrentDay()) { + case 1: + result +=F("SUN"); break; + case 2: + result +=F("MON"); break; + case 3: + result +=F("TUE"); break; + case 4: + result +=F("WED"); break; + case 5: + result +=F("THU"); break; + case 6: + result +=F("FRI"); break; + case 7: + result +=F("SAT"); break; + default: + result +=F("(UNKNOWN)"); break; + } + result += addLabeledString(getOnTimerEnabled() + ? minsToString(this->getOnTime()) : F("Off"), + F("On Time")); + result += addLabeledString(getOffTimerEnabled() + ? minsToString(this->getOffTime()) : F("Off"), + F("Off Time")); + result += addBoolToString(getWeeklyTimerEnable(), F("Weekly Timer")); + return result; +} + +#if DECODE_DAIKIN +// Decode the supplied Daikin A/C message. +// Args: +// results: Ptr to the data to decode and where to store the decode result. +// nbits: Nr. of bits to expect in the data portion. (kDaikinBits) +// strict: Flag to indicate if we strictly adhere to the specification. +// Returns: +// boolean: True if it can decode it, false if it can't. +// +// Status: BETA / Should be working. +// +// Ref: +// https://github.com/mharizanov/Daikin-AC-remote-control-over-the-Internet/tree/master/IRremote +bool IRrecv::decodeDaikin(decode_results *results, const uint16_t nbits, + const bool strict) { + // Is there enough data to match successfully? + if (results->rawlen < (2 * (nbits + kDaikinHeaderLength) + + kDaikinSections * (kHeader + kFooter) + kFooter - 1)) + return false; + + // Compliance + if (strict && nbits != kDaikinBits) return false; + + uint16_t offset = kStartOffset; + match_result_t data_result; + + // Header #1 - Doesn't count as data. + data_result = matchData(&(results->rawbuf[offset]), kDaikinHeaderLength, + kDaikinBitMark, kDaikinOneSpace, + kDaikinBitMark, kDaikinZeroSpace, + kDaikinTolerance, kDaikinMarkExcess, false); + offset += data_result.used; + if (data_result.success == false) return false; // Fail + if (data_result.data) return false; // The header bits should be zero. + // Footer + if (!matchMark(results->rawbuf[offset++], kDaikinBitMark, + kDaikinTolerance, kDaikinMarkExcess)) return false; + if (!matchSpace(results->rawbuf[offset++], kDaikinZeroSpace + kDaikinGap, + kDaikinTolerance, kDaikinMarkExcess)) return false; + // Sections + const uint8_t ksectionSize[kDaikinSections] = { + kDaikinSection1Length, kDaikinSection2Length, kDaikinSection3Length}; + uint16_t pos = 0; + for (uint8_t section = 0; section < kDaikinSections; section++) { + uint16_t used; + // Section Header + Section Data (7 bytes) + Section Footer + used = matchGeneric(results->rawbuf + offset, results->state + pos, + results->rawlen - offset, ksectionSize[section] * 8, + kDaikinHdrMark, kDaikinHdrSpace, + kDaikinBitMark, kDaikinOneSpace, + kDaikinBitMark, kDaikinZeroSpace, + kDaikinBitMark, kDaikinZeroSpace + kDaikinGap, + section >= kDaikinSections - 1, + kDaikinTolerance, kDaikinMarkExcess, false); + if (used == 0) return false; + offset += used; + pos += ksectionSize[section]; + } + // Compliance + if (strict) { + // Re-check we got the correct size/length due to the way we read the data. + if (pos * 8 != kDaikinBits) return false; + // Validate the checksum. + if (!IRDaikinESP::validChecksum(results->state)) return false; + } + + // Success + results->decode_type = DAIKIN; + results->bits = nbits; + // No need to record the state as we stored it as we decoded it. + // As we use result->state, we don't record value, address, or command as it + // is a union data type. + return true; +} +#endif // DECODE_DAIKIN + +#if SEND_DAIKIN2 +// Send a Daikin2 A/C message. +// +// Args: +// data: An array of kDaikin2StateLength bytes containing the IR command. +// +// Status: BETA/Appears to work. +// +// Ref: +// https://github.com/crankyoldgit/IRremoteESP8266/issues/582 +void IRsend::sendDaikin2(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes < kDaikin2Section1Length) + return; // Not enough bytes to send a partial message. + + for (uint16_t r = 0; r <= repeat; r++) { + // Leader + sendGeneric(kDaikin2LeaderMark, kDaikin2LeaderSpace, + 0, 0, 0, 0, 0, 0, (uint64_t) 0, // No data payload. + 0, kDaikin2Freq, false, 0, 50); + // Section #1 + sendGeneric(kDaikin2HdrMark, kDaikin2HdrSpace, kDaikin2BitMark, + kDaikin2OneSpace, kDaikin2BitMark, kDaikin2ZeroSpace, + kDaikin2BitMark, kDaikin2Gap, data, kDaikin2Section1Length, + kDaikin2Freq, false, 0, 50); + // Section #2 + sendGeneric(kDaikin2HdrMark, kDaikin2HdrSpace, kDaikin2BitMark, + kDaikin2OneSpace, kDaikin2BitMark, kDaikin2ZeroSpace, + kDaikin2BitMark, kDaikin2Gap, data + kDaikin2Section1Length, + nbytes - kDaikin2Section1Length, + kDaikin2Freq, false, 0, 50); + } +} +#endif // SEND_DAIKIN2 + +// Class for handling Daikin2 A/C messages. +// +// Code by crankyoldgit, Reverse engineering analysis by sheppy99 +// +// Supported Remotes: Daikin ARC477A1 remote +// +// Ref: +// https://github.com/crankyoldgit/IRremoteESP8266/issues/582 +// https://docs.google.com/spreadsheets/d/1f8EGfIbBUo2B-CzUFdrgKQprWakoYNKM80IKZN4KXQE/edit?usp=sharing +// https://www.daikin.co.nz/sites/default/files/daikin-split-system-US7-FTXZ25-50NV1B.pdf +IRDaikin2::IRDaikin2(const uint16_t pin, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { stateReset(); } + +void IRDaikin2::begin() { _irsend.begin(); } + +#if SEND_DAIKIN2 +void IRDaikin2::send(const uint16_t repeat) { + checksum(); + _irsend.sendDaikin2(remote_state, kDaikin2StateLength, repeat); +} +#endif // SEND_DAIKIN2 + +// Verify the checksum is valid for a given state. +// Args: +// state: The array to verify the checksum of. +// length: The size of the state. +// Returns: +// A boolean. +bool IRDaikin2::validChecksum(uint8_t state[], const uint16_t length) { + // Validate the checksum of section #1. + if (length <= kDaikin2Section1Length - 1 || + state[kDaikin2Section1Length - 1] != sumBytes(state, + kDaikin2Section1Length - 1)) + return false; + // Validate the checksum of section #2 (a.k.a. the rest) + if (length <= kDaikin2Section1Length + 1 || + state[length - 1] != sumBytes(state + kDaikin2Section1Length, + length - kDaikin2Section1Length - 1)) + return false; + return true; +} + +// Calculate and set the checksum values for the internal state. +void IRDaikin2::checksum() { + remote_state[kDaikin2Section1Length - 1] = sumBytes( + remote_state, kDaikin2Section1Length - 1); + remote_state[kDaikin2StateLength -1 ] = sumBytes( + remote_state + kDaikin2Section1Length, kDaikin2Section2Length - 1); +} + +void IRDaikin2::stateReset() { + for (uint8_t i = 0; i < kDaikin2StateLength; i++) remote_state[i] = 0x0; + + remote_state[0] = 0x11; + remote_state[1] = 0xDA; + remote_state[2] = 0x27; + remote_state[4] = 0x01; + remote_state[6] = 0xC0; + remote_state[7] = 0x70; + remote_state[8] = 0x08; + remote_state[9] = 0x0C; + remote_state[10] = 0x80; + remote_state[11] = 0x04; + remote_state[12] = 0xB0; + remote_state[13] = 0x16; + remote_state[14] = 0x24; + remote_state[17] = 0xBE; + remote_state[18] = 0xD0; + // remote_state[19] is a checksum byte, it will be set by checksum(). + remote_state[20] = 0x11; + remote_state[21] = 0xDA; + remote_state[22] = 0x27; + remote_state[25] = 0x08; + remote_state[28] = 0xA0; + remote_state[35] = 0xC1; + remote_state[36] = 0x80; + remote_state[37] = 0x60; + // remote_state[38] is a checksum byte, it will be set by checksum(). + disableOnTimer(); + disableOffTimer(); + disableSleepTimer(); + checksum(); +} + +uint8_t *IRDaikin2::getRaw() { + checksum(); // Ensure correct settings before sending. + return remote_state; +} + +void IRDaikin2::setRaw(const uint8_t new_code[]) { + for (uint8_t i = 0; i < kDaikin2StateLength; i++) + remote_state[i] = new_code[i]; +} + +void IRDaikin2::on() { + remote_state[25] |= kDaikinBitPower; + remote_state[6] &= ~kDaikin2BitPower; +} + +void IRDaikin2::off() { + remote_state[25] &= ~kDaikinBitPower; + remote_state[6] |= kDaikin2BitPower; +} + +void IRDaikin2::setPower(const bool state) { + if (state) + on(); + else + off(); +} + +bool IRDaikin2::getPower() { + return (remote_state[25] & kDaikinBitPower) && + !(remote_state[6] & kDaikin2BitPower); +} + +uint8_t IRDaikin2::getMode() { return remote_state[25] >> 4; } + +void IRDaikin2::setMode(const uint8_t desired_mode) { + uint8_t mode = desired_mode; + switch (mode) { + case kDaikinCool: + case kDaikinHeat: + case kDaikinFan: + case kDaikinDry: + break; + default: + mode = kDaikinAuto; + } + remote_state[25] &= 0b10001111; + remote_state[25] |= (mode << 4); + // Redo the temp setting as Cool mode has a different min temp. + if (mode == kDaikinCool) this->setTemp(this->getTemp()); +} + +// Set the temp in deg C +void IRDaikin2::setTemp(const uint8_t desired) { + // The A/C has a different min temp if in cool mode. + uint8_t temp = std::max( + (this->getMode() == kDaikinCool) ? kDaikin2MinCoolTemp : kDaikinMinTemp, + desired); + temp = std::min(kDaikinMaxTemp, temp); + remote_state[26] = temp * 2; +} + +// Set the speed of the fan, 1-5 or kDaikinFanAuto or kDaikinFanQuiet +void IRDaikin2::setFan(const uint8_t fan) { + // Set the fan speed bits, leave low 4 bits alone + uint8_t fanset; + if (fan == kDaikinFanQuiet || fan == kDaikinFanAuto) + fanset = fan; + else if (fan < kDaikinFanMin || fan > kDaikinFanMax) + fanset = kDaikinFanAuto; + else + fanset = 2 + fan; + remote_state[28] &= 0x0F; + remote_state[28] |= (fanset << 4); +} + +uint8_t IRDaikin2::getFan() { + uint8_t fan = remote_state[28] >> 4; + if (fan != kDaikinFanQuiet && fan != kDaikinFanAuto) fan -= 2; + return fan; +} + +uint8_t IRDaikin2::getTemp() { return remote_state[26] / 2; } + +void IRDaikin2::setSwingVertical(const uint8_t position) { + switch (position) { + case kDaikin2SwingVHigh: + case 2: + case 3: + case 4: + case 5: + case kDaikin2SwingVLow: + case kDaikin2SwingVBreeze: + case kDaikin2SwingVCirculate: + case kDaikin2SwingVAuto: + remote_state[18] &= 0xF0; + remote_state[18] |= (position & 0x0F); + } +} + +uint8_t IRDaikin2::getSwingVertical() { return remote_state[18] & 0x0F; } + +void IRDaikin2::setSwingHorizontal(const uint8_t position) { + remote_state[17] = position; +} + +uint8_t IRDaikin2::getSwingHorizontal() { return remote_state[17]; } + +void IRDaikin2::setCurrentTime(const uint16_t numMins) { + uint16_t mins = numMins; + if (numMins > 24 * 60) mins = 0; // If > 23:59, set to 00:00 + remote_state[5] = (uint8_t)(mins & 0xFF); + // only keep 4 bits + remote_state[6] &= 0xF0; + remote_state[6] |= (uint8_t)((mins >> 8) & 0x0F); +} + +uint16_t IRDaikin2::getCurrentTime() { + return ((remote_state[6] & 0x0F) << 8) + remote_state[5]; +} + +// starttime: Number of minutes after midnight. +// Note: Timer location is shared with sleep timer. +void IRDaikin2::enableOnTimer(const uint16_t starttime) { + clearSleepTimerFlag(); + remote_state[25] |= kDaikinBitOnTimer; // Set the On Timer flag. + remote_state[30] = (uint8_t)(starttime & 0xFF); + // only keep 4 bits + remote_state[31] &= 0xF0; + remote_state[31] |= (uint8_t)((starttime >> 8) & 0x0F); +} + +void IRDaikin2::clearOnTimerFlag() { + remote_state[25] &= ~kDaikinBitOnTimer; +} + +void IRDaikin2::disableOnTimer() { + enableOnTimer(kDaikinUnusedTime); + clearOnTimerFlag(); + clearSleepTimerFlag(); +} + +uint16_t IRDaikin2::getOnTime() { + return ((remote_state[31] & 0x0F) << 8) + remote_state[30]; +} + +bool IRDaikin2::getOnTimerEnabled() { + return remote_state[25] & kDaikinBitOnTimer; +} + +// endtime: Number of minutes after midnight. +void IRDaikin2::enableOffTimer(const uint16_t endtime) { + remote_state[25] |= kDaikinBitOffTimer; // Set the Off Timer flag. + remote_state[32] = (uint8_t)((endtime >> 4) & 0xFF); + remote_state[31] &= 0x0F; + remote_state[31] |= (uint8_t)((endtime & 0xF) << 4); +} + +void IRDaikin2::disableOffTimer() { + enableOffTimer(kDaikinUnusedTime); + remote_state[25] &= ~kDaikinBitOffTimer; // Clear the Off Timer flag. +} + +uint16_t IRDaikin2::getOffTime() { + return (remote_state[32] << 4) + (remote_state[31] >> 4); +} + +bool IRDaikin2::getOffTimerEnabled() { + return remote_state[25] & kDaikinBitOffTimer; +} + +uint8_t IRDaikin2::getBeep() { + return remote_state[7] >> 6; +} + +void IRDaikin2::setBeep(const uint8_t beep) { + remote_state[7] &= ~kDaikin2BeepMask; + remote_state[7] |= ((beep << 6) & kDaikin2BeepMask); +} + +uint8_t IRDaikin2::getLight() { + return (remote_state[7] & kDaikin2LightMask) >> 4; +} + +void IRDaikin2::setLight(const uint8_t light) { + remote_state[7] &= ~kDaikin2LightMask; + remote_state[7] |= ((light << 4) & kDaikin2LightMask); +} + +void IRDaikin2::setMold(const bool on) { + if (on) + remote_state[8] |= kDaikin2BitMold; + else + remote_state[8] &= ~kDaikin2BitMold; +} + +bool IRDaikin2::getMold() { + return remote_state[8] & kDaikin2BitMold; +} + +// Auto clean setting. +void IRDaikin2::setClean(const bool on) { + if (on) + remote_state[8] |= kDaikin2BitClean; + else + remote_state[8] &= ~kDaikin2BitClean; +} + +bool IRDaikin2::getClean() { + return remote_state[8] & kDaikin2BitClean; +} + +// Fresh Air settings. +void IRDaikin2::setFreshAir(const bool on) { + if (on) + remote_state[8] |= kDaikin2BitFreshAir; + else + remote_state[8] &= ~kDaikin2BitFreshAir; +} + +bool IRDaikin2::getFreshAir() { + return remote_state[8] & kDaikin2BitFreshAir; +} + +void IRDaikin2::setFreshAirHigh(const bool on) { + if (on) + remote_state[8] |= kDaikin2BitFreshAirHigh; + else + remote_state[8] &= ~kDaikin2BitFreshAirHigh; +} + +bool IRDaikin2::getFreshAirHigh() { + return remote_state[8] & kDaikin2BitFreshAirHigh; +} + +void IRDaikin2::setEyeAuto(bool on) { + if (on) + remote_state[13] |= kDaikin2BitEyeAuto; + else + remote_state[13] &= ~kDaikin2BitEyeAuto; +} + +bool IRDaikin2::getEyeAuto() { + return remote_state[13] & kDaikin2BitEyeAuto; +} + +void IRDaikin2::setEye(bool on) { + if (on) + remote_state[36] |= kDaikin2BitEye; + else + remote_state[36] &= ~kDaikin2BitEye; +} + +bool IRDaikin2::getEye() { + return remote_state[36] & kDaikin2BitEye; +} + +void IRDaikin2::setEcono(bool on) { + if (on) + remote_state[36] |= kDaikinBitEcono; + else + remote_state[36] &= ~kDaikinBitEcono; +} + +bool IRDaikin2::getEcono() { + return remote_state[36] & kDaikinBitEcono; +} + +// sleeptime: Number of minutes. +// Note: Timer location is shared with On Timer. +void IRDaikin2::enableSleepTimer(const uint16_t sleeptime) { + enableOnTimer(sleeptime); + clearOnTimerFlag(); + remote_state[36] |= kDaikin2BitSleepTimer; // Set the Sleep Timer flag. +} + +void IRDaikin2::clearSleepTimerFlag() { + remote_state[36] &= ~kDaikin2BitSleepTimer; +} + +void IRDaikin2::disableSleepTimer() { + disableOnTimer(); +} + +uint16_t IRDaikin2::getSleepTime() { + return getOnTime(); +} + +bool IRDaikin2::getSleepTimerEnabled() { + return remote_state[36] & kDaikin2BitSleepTimer; +} + +void IRDaikin2::setQuiet(const bool on) { + if (on) { + remote_state[33] |= kDaikinBitSilent; + // Powerful & Quiet mode being on are mutually exclusive. + setPowerful(false); + } else { + remote_state[33] &= ~kDaikinBitSilent; + } +} + +bool IRDaikin2::getQuiet() { return remote_state[33] & kDaikinBitSilent; } + +void IRDaikin2::setPowerful(const bool on) { + if (on) { + remote_state[33] |= kDaikinBitPowerful; + // Powerful & Quiet mode being on are mutually exclusive. + setQuiet(false); + } else { + remote_state[33] &= ~kDaikinBitPowerful; + } +} + +bool IRDaikin2::getPowerful() { return remote_state[33] & kDaikinBitPowerful; } + +void IRDaikin2::setPurify(const bool on) { + if (on) + remote_state[36] |= kDaikin2BitPurify; + else + remote_state[36] &= ~kDaikin2BitPurify; +} + +bool IRDaikin2::getPurify() { return remote_state[36] & kDaikin2BitPurify; } + +// Convert a standard A/C mode into its native mode. +uint8_t IRDaikin2::convertMode(const stdAc::opmode_t mode) { + return IRDaikinESP::convertMode(mode); +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRDaikin2::convertFan(const stdAc::fanspeed_t speed) { + return IRDaikinESP::convertFan(speed); +} + +// Convert a standard A/C vertical swing into its native version. +uint8_t IRDaikin2::convertSwingV(const stdAc::swingv_t position) { + switch (position) { + case stdAc::swingv_t::kHighest: + case stdAc::swingv_t::kHigh: + case stdAc::swingv_t::kMiddle: + case stdAc::swingv_t::kLow: + case stdAc::swingv_t::kLowest: + return (uint8_t)position + kDaikin2SwingVHigh; + default: + return kDaikin2SwingVAuto; + } +} + +// Convert a native vertical swing to it's common equivalent. +stdAc::swingv_t IRDaikin2::toCommonSwingV(const uint8_t setting) { + switch (setting) { + case kDaikin2SwingVHigh: return stdAc::swingv_t::kHighest; + case kDaikin2SwingVHigh + 1: return stdAc::swingv_t::kHigh; + case kDaikin2SwingVHigh + 2: + case kDaikin2SwingVHigh + 3: return stdAc::swingv_t::kMiddle; + case kDaikin2SwingVLow - 1: return stdAc::swingv_t::kLow; + case kDaikin2SwingVLow: return stdAc::swingv_t::kLowest; + default: return stdAc::swingv_t::kAuto; + } +} + +// Convert a native horizontal swing to it's common equivalent. +stdAc::swingh_t IRDaikin2::toCommonSwingH(const uint8_t setting) { + switch (setting) { + case kDaikin2SwingHSwing: + case kDaikin2SwingHAuto: return stdAc::swingh_t::kAuto; + default: return stdAc::swingh_t::kOff; + } +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRDaikin2::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::DAIKIN2; + result.model = -1; // No models used. + result.power = this->getPower(); + result.mode = IRDaikinESP::toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(); + result.fanspeed = IRDaikinESP::toCommonFanSpeed(this->getFan()); + result.swingv = this->toCommonSwingV(this->getSwingVertical()); + result.swingh = this->toCommonSwingH(this->getSwingHorizontal()); + result.quiet = this->getQuiet(); + result.light = this->getLight(); + result.turbo = this->getPowerful(); + result.clean = this->getMold(); + result.econo = this->getEcono(); + result.filter = this->getPurify(); + result.beep = this->getBeep(); + result.sleep = this->getSleepTimerEnabled() ? this->getSleepTime() : -1; + // Not supported. + result.clock = -1; + return result; +} + +// Convert the internal state into a human readable string. +String IRDaikin2::toString() { + String result = ""; + result.reserve(310); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(getPower(), F("Power"), false); + result += addModeToString(getMode(), kDaikinAuto, kDaikinCool, kDaikinHeat, + kDaikinDry, kDaikinFan); + result += addTempToString(getTemp()); + result += addFanToString(getFan(), kDaikinFanMax, kDaikinFanMin, + kDaikinFanAuto, kDaikinFanQuiet, kDaikinFanMed); + result += addIntToString(getSwingVertical(), F("Swing (V)")); + switch (getSwingVertical()) { + case kDaikin2SwingVHigh: + result += F(" (Highest)"); + break; + case 2: + case 3: + case 4: + case 5: + break; + case kDaikin2SwingVLow: + result += F(" (Lowest)"); + break; + case kDaikin2SwingVBreeze: + result += F(" (Breeze)"); + break; + case kDaikin2SwingVCirculate: + result += F(" (Circulate)"); + break; + case kDaikin2SwingVAuto: + result += F(" (Auto)"); + break; + default: + result += F(" (Unknown)"); + } + result += addIntToString(getSwingHorizontal(), F("Swing (H)")); + switch (getSwingHorizontal()) { + case kDaikin2SwingHAuto: + result += F(" (Auto)"); + break; + case kDaikin2SwingHSwing: + result += F(" (Swing)"); + break; + } + result += addLabeledString(minsToString(getCurrentTime()), F("Clock")); + result += addLabeledString( + getOnTimerEnabled() ? minsToString(getOnTime()) : F("Off"), F("On Time")); + result += addLabeledString( + getOffTimerEnabled() ? minsToString(getOffTime()) : F("Off"), + F("Off Time")); + result += addLabeledString( + getSleepTimerEnabled() ? minsToString(getSleepTime()) : F("Off"), + F("Sleep Time")); + result += addIntToString(getBeep(), F("Beep")); + switch (getBeep()) { + case kDaikinBeepLoud: + result += F(" (Loud)"); + break; + case kDaikinBeepQuiet: + result += F(" (Quiet)"); + break; + case kDaikinBeepOff: + result += F(" (Off)"); + break; + default: + result += F(" (UNKNOWN)"); + } + result += addIntToString(getLight(), F("Light")); + switch (getLight()) { + case kDaikinLightBright: + result += F(" (Bright)"); + break; + case kDaikinLightDim: + result += F(" (Dim)"); + break; + case kDaikinLightOff: + result += F(" (Off)"); + break; + default: + result += F(" (UNKNOWN)"); + } + result += addBoolToString(getMold(), F("Mold")); + result += addBoolToString(getClean(), F("Clean")); + result += addLabeledString( + getFreshAir() ? (getFreshAirHigh() ? F("High") : F("On")) : F("Off"), + F("Fresh Air")); + result += addBoolToString(getEye(), F("Eye")); + result += addBoolToString(getEyeAuto(), F("Eye Auto")); + result += addBoolToString(getQuiet(), F("Quiet")); + result += addBoolToString(getPowerful(), F("Powerful")); + result += addBoolToString(getPurify(), F("Purify")); + result += addBoolToString(getEcono(), F("Econo")); + return result; +} + +#if DECODE_DAIKIN2 +// Decode the supplied Daikin2 A/C message. +// Args: +// results: Ptr to the data to decode and where to store the decode result. +// nbits: Nr. of bits to expect in the data portion. (kDaikin2Bits) +// strict: Flag to indicate if we strictly adhere to the specification. +// Returns: +// boolean: True if it can decode it, false if it can't. +// +// Supported devices: +// - Daikin FTXZ25NV1B, FTXZ35NV1B, FTXZ50NV1B Aircon +// - Daikin ARC477A1 remote +// +// Status: BETA / Work as expected. +// +// Ref: +// https://github.com/mharizanov/Daikin-AC-remote-control-over-the-Internet/tree/master/IRremote +bool IRrecv::decodeDaikin2(decode_results *results, uint16_t nbits, + bool strict) { + if (results->rawlen < 2 * (nbits + kHeader + kFooter) + kHeader - 1) + return false; + + // Compliance + if (strict && nbits != kDaikin2Bits) return false; + + uint16_t offset = kStartOffset; + const uint8_t ksectionSize[kDaikin2Sections] = {kDaikin2Section1Length, + kDaikin2Section2Length}; + + // Leader + if (!matchMark(results->rawbuf[offset++], kDaikin2LeaderMark, + _tolerance + kDaikin2Tolerance)) return false; + if (!matchSpace(results->rawbuf[offset++], kDaikin2LeaderSpace, + _tolerance + kDaikin2Tolerance)) return false; + + // Sections + uint16_t pos = 0; + for (uint8_t section = 0; section < kDaikin2Sections; section++) { + uint16_t used; + // Section Header + Section Data + Section Footer + used = matchGeneric(results->rawbuf + offset, results->state + pos, + results->rawlen - offset, ksectionSize[section] * 8, + kDaikin2HdrMark, kDaikin2HdrSpace, + kDaikin2BitMark, kDaikin2OneSpace, + kDaikin2BitMark, kDaikin2ZeroSpace, + kDaikin2BitMark, kDaikin2Gap, + section >= kDaikin2Sections - 1, + _tolerance + kDaikin2Tolerance, kDaikinMarkExcess, + false); + if (used == 0) return false; + offset += used; + pos += ksectionSize[section]; + } + // Compliance + if (strict) { + // Re-check we got the correct size/length due to the way we read the data. + if (pos * 8 != kDaikin2Bits) return false; + // Validate the checksum. + if (!IRDaikin2::validChecksum(results->state)) return false; + } + + // Success + results->decode_type = DAIKIN2; + results->bits = nbits; + // No need to record the state as we stored it as we decoded it. + // As we use result->state, we don't record value, address, or command as it + // is a union data type. + return true; +} +#endif // DECODE_DAIKIN2 + +#if SEND_DAIKIN216 +// Send a Daikin 216 bit A/C message. +// +// Args: +// data: An array of kDaikin216StateLength bytes containing the IR command. +// +// Status: Alpha/Untested on a real device. +// +// Supported devices: +// - Daikin ARC433B69 remote. +// +// Ref: +// https://github.com/crankyoldgit/IRremoteESP8266/issues/689 +// https://github.com/danny-source/Arduino_DY_IRDaikin +void IRsend::sendDaikin216(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes < kDaikin216Section1Length) + return; // Not enough bytes to send a partial message. + + for (uint16_t r = 0; r <= repeat; r++) { + // Section #1 + sendGeneric(kDaikin216HdrMark, kDaikin216HdrSpace, kDaikin216BitMark, + kDaikin216OneSpace, kDaikin216BitMark, kDaikin216ZeroSpace, + kDaikin216BitMark, kDaikin216Gap, data, + kDaikin216Section1Length, + kDaikin216Freq, false, 0, kDutyDefault); + // Section #2 + sendGeneric(kDaikin216HdrMark, kDaikin216HdrSpace, kDaikin216BitMark, + kDaikin216OneSpace, kDaikin216BitMark, kDaikin216ZeroSpace, + kDaikin216BitMark, kDaikin216Gap, + data + kDaikin216Section1Length, + nbytes - kDaikin216Section1Length, + kDaikin216Freq, false, 0, kDutyDefault); + } +} +#endif // SEND_DAIKIN216 + +// Class for handling Daikin 216 bit / 27 byte A/C messages. +// +// Code by crankyoldgit. +// +// Supported Remotes: Daikin ARC433B69 remote +// +// Ref: +// https://github.com/crankyoldgit/IRremoteESP8266/issues/689 +// https://github.com/danny-source/Arduino_DY_IRDaikin +IRDaikin216::IRDaikin216(const uint16_t pin, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { stateReset(); } + +void IRDaikin216::begin() { _irsend.begin(); } + +#if SEND_DAIKIN216 +void IRDaikin216::send(const uint16_t repeat) { + checksum(); + _irsend.sendDaikin216(remote_state, kDaikin216StateLength, repeat); +} +#endif // SEND_DAIKIN216 + +// Verify the checksum is valid for a given state. +// Args: +// state: The array to verify the checksum of. +// length: The size of the state. +// Returns: +// A boolean. +bool IRDaikin216::validChecksum(uint8_t state[], const uint16_t length) { + // Validate the checksum of section #1. + if (length <= kDaikin216Section1Length - 1 || + state[kDaikin216Section1Length - 1] != sumBytes( + state, kDaikin216Section1Length - 1)) + return false; + // Validate the checksum of section #2 (a.k.a. the rest) + if (length <= kDaikin216Section1Length + 1 || + state[length - 1] != sumBytes(state + kDaikin216Section1Length, + length - kDaikin216Section1Length - 1)) + return false; + return true; +} + +// Calculate and set the checksum values for the internal state. +void IRDaikin216::checksum() { + remote_state[kDaikin216Section1Length - 1] = sumBytes( + remote_state, kDaikin216Section1Length - 1); + remote_state[kDaikin216StateLength - 1] = sumBytes( + remote_state + kDaikin216Section1Length, kDaikin216Section2Length - 1); +} + +void IRDaikin216::stateReset() { + for (uint8_t i = 0; i < kDaikin216StateLength; i++) remote_state[i] = 0x00; + remote_state[0] = 0x11; + remote_state[1] = 0xDA; + remote_state[2] = 0x27; + remote_state[3] = 0xF0; + // remote_state[7] is a checksum byte, it will be set by checksum(). + remote_state[8] = 0x11; + remote_state[9] = 0xDA; + remote_state[10] = 0x27; + remote_state[23] = 0xC0; + // remote_state[26] is a checksum byte, it will be set by checksum(). +} + +uint8_t *IRDaikin216::getRaw() { + checksum(); // Ensure correct settings before sending. + return remote_state; +} + +void IRDaikin216::setRaw(const uint8_t new_code[]) { + for (uint8_t i = 0; i < kDaikin216StateLength; i++) + remote_state[i] = new_code[i]; +} + + +void IRDaikin216::on() { + remote_state[kDaikin216BytePower] |= kDaikinBitPower; +} + +void IRDaikin216::off() { + remote_state[kDaikin216BytePower] &= ~kDaikinBitPower; +} + +void IRDaikin216::setPower(const bool state) { + if (state) + on(); + else + off(); +} + +bool IRDaikin216::getPower() { + return remote_state[kDaikin216BytePower] & kDaikinBitPower; +} + +uint8_t IRDaikin216::getMode() { + return (remote_state[kDaikin216ByteMode] & kDaikin216MaskMode) >> 4; +} + +void IRDaikin216::setMode(const uint8_t mode) { + switch (mode) { + case kDaikinAuto: + case kDaikinCool: + case kDaikinHeat: + case kDaikinFan: + case kDaikinDry: + remote_state[kDaikin216ByteMode] &= ~kDaikin216MaskMode; + remote_state[kDaikin216ByteMode] |= (mode << 4); + break; + default: + this->setMode(kDaikinAuto); + } +} + +// Convert a standard A/C mode into its native mode. +uint8_t IRDaikin216::convertMode(const stdAc::opmode_t mode) { + return IRDaikinESP::convertMode(mode); +} + +// Set the temp in deg C +void IRDaikin216::setTemp(const uint8_t temp) { + uint8_t degrees = std::max(temp, kDaikinMinTemp); + degrees = std::min(degrees, kDaikinMaxTemp); + remote_state[kDaikin216ByteTemp] &= ~kDaikin216MaskTemp; + remote_state[kDaikin216ByteTemp] |= (degrees << 1); +} + +uint8_t IRDaikin216::getTemp(void) { + return (remote_state[kDaikin216ByteTemp] & kDaikin216MaskTemp) >> 1; +} + +// Set the speed of the fan, 1-5 or kDaikinFanAuto or kDaikinFanQuiet +void IRDaikin216::setFan(const uint8_t fan) { + // Set the fan speed bits, leave low 4 bits alone + uint8_t fanset; + if (fan == kDaikinFanQuiet || fan == kDaikinFanAuto) + fanset = fan; + else if (fan < kDaikinFanMin || fan > kDaikinFanMax) + fanset = kDaikinFanAuto; + else + fanset = 2 + fan; + remote_state[kDaikin216ByteFan] &= ~kDaikin216MaskFan; + remote_state[kDaikin216ByteFan] |= (fanset << 4); +} + +uint8_t IRDaikin216::getFan() { + uint8_t fan = remote_state[kDaikin216ByteFan] >> 4; + if (fan != kDaikinFanQuiet && fan != kDaikinFanAuto) fan -= 2; + return fan; +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRDaikin216::convertFan(const stdAc::fanspeed_t speed) { + return IRDaikinESP::convertFan(speed); +} + +void IRDaikin216::setSwingVertical(const bool on) { + if (on) + remote_state[kDaikin216ByteSwingV] |= kDaikin216MaskSwingV; + else + remote_state[kDaikin216ByteSwingV] &= ~kDaikin216MaskSwingV; +} + +bool IRDaikin216::getSwingVertical(void) { + return remote_state[kDaikin216ByteSwingV] & kDaikin216MaskSwingV; +} + +void IRDaikin216::setSwingHorizontal(const bool on) { + if (on) + remote_state[kDaikin216ByteSwingH] |= kDaikin216MaskSwingH; + else + remote_state[kDaikin216ByteSwingH] &= ~kDaikin216MaskSwingH; +} + +bool IRDaikin216::getSwingHorizontal(void) { + return remote_state[kDaikin216ByteSwingH] & kDaikin216MaskSwingH; +} + +// This is a horrible hack till someone works out the quiet mode bit. +void IRDaikin216::setQuiet(const bool on) { + if (on) { + this->setFan(kDaikinFanQuiet); + // Powerful & Quiet mode being on are mutually exclusive. + this->setPowerful(false); + } else if (this->getFan() == kDaikinFanQuiet) { + this->setFan(kDaikinFanAuto); + } +} + +// This is a horrible hack till someone works out the quiet mode bit. +bool IRDaikin216::getQuiet(void) { + return this->getFan() == kDaikinFanQuiet; +} + +void IRDaikin216::setPowerful(const bool on) { + if (on) { + remote_state[kDaikin216BytePowerful] |= kDaikinBitPowerful; + // Powerful & Quiet mode being on are mutually exclusive. + this->setQuiet(false); + } else { + remote_state[kDaikin216BytePowerful] &= ~kDaikinBitPowerful; + } +} + +bool IRDaikin216::getPowerful() { + return remote_state[kDaikin216BytePowerful] & kDaikinBitPowerful; +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRDaikin216::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::DAIKIN216; + result.model = -1; // No models used. + result.power = this->getPower(); + result.mode = IRDaikinESP::toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(); + result.fanspeed = IRDaikinESP::toCommonFanSpeed(this->getFan()); + result.swingv = this->getSwingVertical() ? stdAc::swingv_t::kAuto : + stdAc::swingv_t::kOff; + result.swingh = this->getSwingHorizontal() ? stdAc::swingh_t::kAuto : + stdAc::swingh_t::kOff; + result.quiet = this->getQuiet(); + result.turbo = this->getPowerful(); + // Not supported. + result.light = false; + result.clean = false; + result.econo = false; + result.filter = false; + result.beep = false; + result.sleep = -1; + result.clock = -1; + return result; +} + +// Convert the internal state into a human readable string. +String IRDaikin216::toString() { + String result = ""; + result.reserve(120); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(getPower(), F("Power"), false); + result += addModeToString(getMode(), kDaikinAuto, kDaikinCool, kDaikinHeat, + kDaikinDry, kDaikinFan); + result += addTempToString(getTemp()); + result += addFanToString(getFan(), kDaikinFanMax, kDaikinFanMin, + kDaikinFanAuto, kDaikinFanQuiet, kDaikinFanMed); + result += addBoolToString(getSwingHorizontal(), F("Swing (Horizontal)")); + result += addBoolToString(getSwingVertical(), F("Swing (Vertical)")); + result += addBoolToString(getQuiet(), F("Quiet")); + result += addBoolToString(getPowerful(), F("Powerful")); + return result; +} + +#if DECODE_DAIKIN216 +// Decode the supplied Daikin 216 bit A/C message. +// Args: +// results: Ptr to the data to decode and where to store the decode result. +// nbits: Nr. of bits to expect in the data portion. (kDaikin216Bits) +// strict: Flag to indicate if we strictly adhere to the specification. +// Returns: +// boolean: True if it can decode it, false if it can't. +// +// Supported devices: +// - Daikin ARC433B69 remote. +// +// Status: BETA / Should be working. +// +// Ref: +// https://github.com/crankyoldgit/IRremoteESP8266/issues/689 +// https://github.com/danny-source/Arduino_DY_IRDaikin +bool IRrecv::decodeDaikin216(decode_results *results, const uint16_t nbits, + const bool strict) { + if (results->rawlen < 2 * (nbits + kHeader + kFooter) - 1) + return false; + + // Compliance + if (strict && nbits != kDaikin216Bits) return false; + + uint16_t offset = kStartOffset; + const uint8_t ksectionSize[kDaikin216Sections] = {kDaikin216Section1Length, + kDaikin216Section2Length}; + // Sections + uint16_t pos = 0; + for (uint8_t section = 0; section < kDaikin216Sections; section++) { + uint16_t used; + // Section Header + Section Data + Section Footer + used = matchGeneric(results->rawbuf + offset, results->state + pos, + results->rawlen - offset, ksectionSize[section] * 8, + kDaikin216HdrMark, kDaikin216HdrSpace, + kDaikin216BitMark, kDaikin216OneSpace, + kDaikin216BitMark, kDaikin216ZeroSpace, + kDaikin216BitMark, kDaikin216Gap, + section >= kDaikin216Sections - 1, + kDaikinTolerance, kDaikinMarkExcess, false); + if (used == 0) return false; + offset += used; + pos += ksectionSize[section]; + } + // Compliance + if (strict) { + if (pos * 8 != kDaikin216Bits) return false; + // Validate the checksum. + if (!IRDaikin216::validChecksum(results->state)) return false; + } + + // Success + results->decode_type = decode_type_t::DAIKIN216; + results->bits = nbits; + // No need to record the state as we stored it as we decoded it. + // As we use result->state, we don't record value, address, or command as it + // is a union data type. + return true; +} +#endif // DECODE_DAIKIN216 + +#if SEND_DAIKIN160 +// Send a Daikin 160 bit A/C message. +// +// Args: +// data: An array of kDaikin160StateLength bytes containing the IR command. +// +// Status: STABLE / Confirmed working. +// +// Supported devices: +// - Daikin ARC423A5 remote. +// +// Ref: +// https://github.com/crankyoldgit/IRremoteESP8266/issues/731 +void IRsend::sendDaikin160(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes < kDaikin160Section1Length) + return; // Not enough bytes to send a partial message. + + for (uint16_t r = 0; r <= repeat; r++) { + // Section #1 + sendGeneric(kDaikin160HdrMark, kDaikin160HdrSpace, kDaikin160BitMark, + kDaikin160OneSpace, kDaikin160BitMark, kDaikin160ZeroSpace, + kDaikin160BitMark, kDaikin160Gap, data, + kDaikin160Section1Length, + kDaikin160Freq, false, 0, kDutyDefault); + // Section #2 + sendGeneric(kDaikin160HdrMark, kDaikin160HdrSpace, kDaikin160BitMark, + kDaikin160OneSpace, kDaikin160BitMark, kDaikin160ZeroSpace, + kDaikin160BitMark, kDaikin160Gap, + data + kDaikin160Section1Length, + nbytes - kDaikin160Section1Length, + kDaikin160Freq, false, 0, kDutyDefault); + } +} +#endif // SEND_DAIKIN160 + +// Class for handling Daikin 160 bit / 20 byte A/C messages. +// +// Code by crankyoldgit. +// +// Supported Remotes: Daikin ARC423A5 remote +// +// Ref: +// https://github.com/crankyoldgit/IRremoteESP8266/issues/731 +IRDaikin160::IRDaikin160(const uint16_t pin, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { stateReset(); } + +void IRDaikin160::begin() { _irsend.begin(); } + +// Verify the checksum is valid for a given state. +// Args: +// state: The array to verify the checksum of. +// length: The size of the state. +// Returns: +// A boolean. +bool IRDaikin160::validChecksum(uint8_t state[], const uint16_t length) { + // Validate the checksum of section #1. + if (length <= kDaikin160Section1Length - 1 || + state[kDaikin160Section1Length - 1] != sumBytes( + state, kDaikin160Section1Length - 1)) + return false; + // Validate the checksum of section #2 (a.k.a. the rest) + if (length <= kDaikin160Section1Length + 1 || + state[length - 1] != sumBytes(state + kDaikin160Section1Length, + length - kDaikin160Section1Length - 1)) + return false; + return true; +} + +// Calculate and set the checksum values for the internal state. +void IRDaikin160::checksum() { + remote_state[kDaikin160Section1Length - 1] = sumBytes( + remote_state, kDaikin160Section1Length - 1); + remote_state[kDaikin160StateLength - 1] = sumBytes( + remote_state + kDaikin160Section1Length, kDaikin160Section2Length - 1); +} + +void IRDaikin160::stateReset() { + for (uint8_t i = 0; i < kDaikin160StateLength; i++) remote_state[i] = 0x00; + remote_state[0] = 0x11; + remote_state[1] = 0xDA; + remote_state[2] = 0x27; + remote_state[3] = 0xF0; + remote_state[4] = 0x0D; + // remote_state[6] is a checksum byte, it will be set by checksum(). + remote_state[7] = 0x11; + remote_state[8] = 0xDA; + remote_state[9] = 0x27; + remote_state[11] = 0xD3; + remote_state[12] = 0x30; + remote_state[13] = 0x11; + remote_state[16] = 0x1E; + remote_state[17] = 0x0A; + remote_state[18] = 0x08; + // remote_state[19] is a checksum byte, it will be set by checksum(). +} + +uint8_t *IRDaikin160::getRaw() { + checksum(); // Ensure correct settings before sending. + return remote_state; +} + +void IRDaikin160::setRaw(const uint8_t new_code[]) { + for (uint8_t i = 0; i < kDaikin160StateLength; i++) + remote_state[i] = new_code[i]; +} + +#if SEND_DAIKIN160 +void IRDaikin160::send(const uint16_t repeat) { + checksum(); + _irsend.sendDaikin160(remote_state, kDaikin160StateLength, repeat); +} +#endif // SEND_DAIKIN160 + +void IRDaikin160::on() { + remote_state[kDaikin160BytePower] |= kDaikinBitPower; +} + +void IRDaikin160::off() { + remote_state[kDaikin160BytePower] &= ~kDaikinBitPower; +} + +void IRDaikin160::setPower(const bool state) { + if (state) + on(); + else + off(); +} + +bool IRDaikin160::getPower() { + return remote_state[kDaikin160BytePower] & kDaikinBitPower; +} + +uint8_t IRDaikin160::getMode() { + return (remote_state[kDaikin160ByteMode] & kDaikin160MaskMode) >> 4; +} + +void IRDaikin160::setMode(const uint8_t mode) { + switch (mode) { + case kDaikinAuto: + case kDaikinCool: + case kDaikinHeat: + case kDaikinFan: + case kDaikinDry: + remote_state[kDaikin160ByteMode] &= ~kDaikin160MaskMode; + remote_state[kDaikin160ByteMode] |= (mode << 4); + break; + default: + this->setMode(kDaikinAuto); + } +} + +// Convert a standard A/C mode into its native mode. +uint8_t IRDaikin160::convertMode(const stdAc::opmode_t mode) { + return IRDaikinESP::convertMode(mode); +} + +// Set the temp in deg C +void IRDaikin160::setTemp(const uint8_t temp) { + uint8_t degrees = std::max(temp, kDaikinMinTemp); + degrees = std::min(degrees, kDaikinMaxTemp) * 2 - 20; + remote_state[kDaikin160ByteTemp] &= ~kDaikin160MaskTemp; + remote_state[kDaikin160ByteTemp] |= degrees; +} + +uint8_t IRDaikin160::getTemp(void) { + return (((remote_state[kDaikin160ByteTemp] & kDaikin160MaskTemp) / 2 ) + 10); +} + +// Set the speed of the fan, 1-5 or kDaikinFanAuto or kDaikinFanQuiet +void IRDaikin160::setFan(const uint8_t fan) { + uint8_t fanset; + if (fan == kDaikinFanQuiet || fan == kDaikinFanAuto) + fanset = fan; + else if (fan < kDaikinFanMin || fan > kDaikinFanMax) + fanset = kDaikinFanAuto; + else + fanset = 2 + fan; + // Set the fan speed bits, leave *upper* 4 bits alone + remote_state[kDaikin160ByteFan] &= ~kDaikin160MaskFan; + remote_state[kDaikin160ByteFan] |= fanset; +} + +uint8_t IRDaikin160::getFan() { + uint8_t fan = remote_state[kDaikin160ByteFan] & kDaikin160MaskFan; + if (fan != kDaikinFanQuiet && fan != kDaikinFanAuto) fan -= 2; + return fan; +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRDaikin160::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: return kDaikinFanMin; + case stdAc::fanspeed_t::kLow: return kDaikinFanMin + 1; + case stdAc::fanspeed_t::kMedium: return kDaikinFanMin + 2; + case stdAc::fanspeed_t::kHigh: return kDaikinFanMax - 1; + case stdAc::fanspeed_t::kMax: return kDaikinFanMax; + default: + return kDaikinFanAuto; + } +} + +void IRDaikin160::setSwingVertical(const uint8_t position) { + switch (position) { + case kDaikin160SwingVLowest: + case kDaikin160SwingVLow: + case kDaikin160SwingVMiddle: + case kDaikin160SwingVHigh: + case kDaikin160SwingVHighest: + case kDaikin160SwingVAuto: + remote_state[kDaikin160ByteSwingV] &= ~kDaikin160MaskSwingV; + remote_state[kDaikin160ByteSwingV] |= (position << 4); + break; + default: + setSwingVertical(kDaikin160SwingVAuto); + } +} + +uint8_t IRDaikin160::getSwingVertical(void) { + return remote_state[kDaikin160ByteSwingV] >> 4; +} + +// Convert a standard A/C vertical swing into its native version. +uint8_t IRDaikin160::convertSwingV(const stdAc::swingv_t position) { + switch (position) { + case stdAc::swingv_t::kHighest: + case stdAc::swingv_t::kHigh: + case stdAc::swingv_t::kMiddle: + case stdAc::swingv_t::kLow: + case stdAc::swingv_t::kLowest: + return kDaikin160SwingVHighest + 1 - (uint8_t)position; + default: + return kDaikin160SwingVAuto; + } +} + +// Convert a native vertical swing to it's common equivalent. +stdAc::swingv_t IRDaikin160::toCommonSwingV(const uint8_t setting) { + switch (setting) { + case kDaikin160SwingVHighest: return stdAc::swingv_t::kHighest; + case kDaikin160SwingVHigh: return stdAc::swingv_t::kHigh; + case kDaikin160SwingVMiddle: return stdAc::swingv_t::kMiddle; + case kDaikin160SwingVLow: return stdAc::swingv_t::kLow; + case kDaikin160SwingVLowest: return stdAc::swingv_t::kLowest; + default: + return stdAc::swingv_t::kAuto; + } +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRDaikin160::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::DAIKIN160; + result.model = -1; // No models used. + result.power = this->getPower(); + result.mode = IRDaikinESP::toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(); + result.fanspeed = IRDaikinESP::toCommonFanSpeed(this->getFan()); + result.swingv = this->toCommonSwingV(this->getSwingVertical()); + // Not supported. + result.swingh = stdAc::swingh_t::kOff; + result.quiet = false; + result.turbo = false; + result.light = false; + result.clean = false; + result.econo = false; + result.filter = false; + result.beep = false; + result.sleep = -1; + result.clock = -1; + return result; +} + +// Convert the internal state into a human readable string. +String IRDaikin160::toString() { + String result = ""; + result.reserve(150); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(getPower(), F("Power"), false); + result += addModeToString(getMode(), kDaikinAuto, kDaikinCool, kDaikinHeat, + kDaikinDry, kDaikinFan); + result += addTempToString(getTemp()); + result += addFanToString(getFan(), kDaikinFanMax, kDaikinFanMin, + kDaikinFanAuto, kDaikinFanQuiet, kDaikinFanMed); + result += addIntToString(getSwingVertical(), F("Vent Position (V)")); + switch (getSwingVertical()) { + case kDaikin160SwingVHighest: result += F(" (Highest)"); break; + case kDaikin160SwingVHigh: result += F(" (High)"); break; + case kDaikin160SwingVMiddle: result += F(" (Middle)"); break; + case kDaikin160SwingVLow: result += F(" (Low)"); break; + case kDaikin160SwingVLowest: result += F(" (Lowest)"); break; + case kDaikin160SwingVAuto: result += F(" (Auto)"); break; + default: + result += F(" (Unknown)"); + } + return result; +} + +#if DECODE_DAIKIN160 +// Decode the supplied Daikin 160 bit A/C message. +// Args: +// results: Ptr to the data to decode and where to store the decode result. +// nbits: Nr. of bits to expect in the data portion. (kDaikin160Bits) +// strict: Flag to indicate if we strictly adhere to the specification. +// Returns: +// boolean: True if it can decode it, false if it can't. +// +// Supported devices: +// - Daikin ARC423A5 remote. +// +// Status: STABLE / Confirmed working. +// +// Ref: +// https://github.com/crankyoldgit/IRremoteESP8266/issues/731 +bool IRrecv::decodeDaikin160(decode_results *results, const uint16_t nbits, + const bool strict) { + if (results->rawlen < 2 * (nbits + kHeader + kFooter) - 1) + return false; + + // Compliance + if (strict && nbits != kDaikin160Bits) return false; + + uint16_t offset = kStartOffset; + const uint8_t ksectionSize[kDaikin160Sections] = {kDaikin160Section1Length, + kDaikin160Section2Length}; + + // Sections + uint16_t pos = 0; + for (uint8_t section = 0; section < kDaikin160Sections; section++) { + uint16_t used; + // Section Header + Section Data (7 bytes) + Section Footer + used = matchGeneric(results->rawbuf + offset, results->state + pos, + results->rawlen - offset, ksectionSize[section] * 8, + kDaikin160HdrMark, kDaikin160HdrSpace, + kDaikin160BitMark, kDaikin160OneSpace, + kDaikin160BitMark, kDaikin160ZeroSpace, + kDaikin160BitMark, kDaikin160Gap, + section >= kDaikin160Sections - 1, + kDaikinTolerance, kDaikinMarkExcess, false); + if (used == 0) return false; + offset += used; + pos += ksectionSize[section]; + } + // Compliance + if (strict) { + // Validate the checksum. + if (!IRDaikin160::validChecksum(results->state)) return false; + } + + // Success + results->decode_type = decode_type_t::DAIKIN160; + results->bits = nbits; + // No need to record the state as we stored it as we decoded it. + // As we use result->state, we don't record value, address, or command as it + // is a union data type. + return true; +} +#endif // DECODE_DAIKIN160 + +#if SEND_DAIKIN176 +// Send a Daikin 176 bit A/C message. +// +// Args: +// data: An array of kDaikin176StateLength bytes containing the IR command. +// +// Status: Alpha/Untested on a real device. +// +// Supported devices: +// - Daikin BRC4C153 remote. +// +void IRsend::sendDaikin176(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes < kDaikin176Section1Length) + return; // Not enough bytes to send a partial message. + + for (uint16_t r = 0; r <= repeat; r++) { + // Section #1 + sendGeneric(kDaikin176HdrMark, kDaikin176HdrSpace, kDaikin176BitMark, + kDaikin176OneSpace, kDaikin176BitMark, kDaikin176ZeroSpace, + kDaikin176BitMark, kDaikin176Gap, data, + kDaikin176Section1Length, + kDaikin176Freq, false, 0, kDutyDefault); + // Section #2 + sendGeneric(kDaikin176HdrMark, kDaikin176HdrSpace, kDaikin176BitMark, + kDaikin176OneSpace, kDaikin176BitMark, kDaikin176ZeroSpace, + kDaikin176BitMark, kDaikin176Gap, + data + kDaikin176Section1Length, + nbytes - kDaikin176Section1Length, + kDaikin176Freq, false, 0, kDutyDefault); + } +} +#endif // SEND_DAIKIN176 + +// Class for handling Daikin 176 bit / 22 byte A/C messages. +// +// Code by crankyoldgit. +// +// Supported Remotes: Daikin BRC4C153 remote +// +IRDaikin176::IRDaikin176(const uint16_t pin, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { stateReset(); } + +void IRDaikin176::begin() { _irsend.begin(); } + +// Verify the checksum is valid for a given state. +// Args: +// state: The array to verify the checksum of. +// length: The size of the state. +// Returns: +// A boolean. +bool IRDaikin176::validChecksum(uint8_t state[], const uint16_t length) { + // Validate the checksum of section #1. + if (length <= kDaikin176Section1Length - 1 || + state[kDaikin176Section1Length - 1] != sumBytes( + state, kDaikin176Section1Length - 1)) + return false; + // Validate the checksum of section #2 (a.k.a. the rest) + if (length <= kDaikin176Section1Length + 1 || + state[length - 1] != sumBytes(state + kDaikin176Section1Length, + length - kDaikin176Section1Length - 1)) + return false; + return true; +} + +// Calculate and set the checksum values for the internal state. +void IRDaikin176::checksum() { + remote_state[kDaikin176Section1Length - 1] = sumBytes( + remote_state, kDaikin176Section1Length - 1); + remote_state[kDaikin176StateLength - 1] = sumBytes( + remote_state + kDaikin176Section1Length, kDaikin176Section2Length - 1); +} + +void IRDaikin176::stateReset() { + for (uint8_t i = 0; i < kDaikin176StateLength; i++) remote_state[i] = 0x00; + remote_state[0] = 0x11; + remote_state[1] = 0xDA; + remote_state[2] = 0x17; + remote_state[3] = 0x18; + remote_state[4] = 0x04; + // remote_state[6] is a checksum byte, it will be set by checksum(). + remote_state[7] = 0x11; + remote_state[8] = 0xDA; + remote_state[9] = 0x17; + remote_state[10] = 0x18; + remote_state[12] = 0x73; + remote_state[14] = 0x20; + remote_state[18] = 0x16; // Fan speed and swing + remote_state[20] = 0x20; + // remote_state[21] is a checksum byte, it will be set by checksum(). + _saved_temp = getTemp(); +} + +uint8_t *IRDaikin176::getRaw() { + checksum(); // Ensure correct settings before sending. + return remote_state; +} + +void IRDaikin176::setRaw(const uint8_t new_code[]) { + for (uint8_t i = 0; i < kDaikin176StateLength; i++) + remote_state[i] = new_code[i]; + _saved_temp = getTemp(); +} + +#if SEND_DAIKIN176 +void IRDaikin176::send(const uint16_t repeat) { + checksum(); + _irsend.sendDaikin176(remote_state, kDaikin176StateLength, repeat); +} +#endif // SEND_DAIKIN176 + +void IRDaikin176::on() { setPower(true); } + +void IRDaikin176::off() { setPower(false); } + +void IRDaikin176::setPower(const bool state) { + remote_state[kDaikin176ByteModeButton] = 0; + if (state) + remote_state[kDaikin176BytePower] |= kDaikinBitPower; + else + remote_state[kDaikin176BytePower] &= ~kDaikinBitPower; +} + +bool IRDaikin176::getPower() { + return remote_state[kDaikin176BytePower] & kDaikinBitPower; +} + +uint8_t IRDaikin176::getMode() { + return (remote_state[kDaikin176ByteMode] & kDaikin176MaskMode) >> 4; +} + +void IRDaikin176::setMode(const uint8_t mode) { + uint8_t altmode = 0; + switch (mode) { + case kDaikinFan: altmode = 0; break; + case kDaikinDry: altmode = 7; break; + case kDaikin176Cool: altmode = 2; break; + default: this->setMode(kDaikin176Cool); return; + } + // Set the mode. + remote_state[kDaikin176ByteMode] &= ~kDaikin176MaskMode; + remote_state[kDaikin176ByteMode] |= (mode << 4); + // Set the altmode + remote_state[kDaikin176BytePower] &= ~kDaikin176MaskMode; + remote_state[kDaikin176BytePower] |= (altmode << 4); + setTemp(_saved_temp); + // Needs to happen after setTemp() as it will clear it. + remote_state[kDaikin176ByteModeButton] = kDaikin176ModeButton; +} + +// Convert a standard A/C mode into its native mode. +uint8_t IRDaikin176::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kDry: + return kDaikinDry; + case stdAc::opmode_t::kHeat: // Heat not supported, but fan is the closest. + case stdAc::opmode_t::kFan: + return kDaikinFan; + default: + return kDaikin176Cool; + } +} + +// Convert a native mode to it's common equivalent. +stdAc::opmode_t IRDaikin176::toCommonMode(const uint8_t mode) { + switch (mode) { + case kDaikinDry: return stdAc::opmode_t::kDry; + case kDaikinHeat: // There is no heat mode, but fan is the closest. + case kDaikinFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kCool; + } +} + +// Set the temp in deg C +void IRDaikin176::setTemp(const uint8_t temp) { + uint8_t degrees = std::min(kDaikinMaxTemp, std::max(temp, kDaikinMinTemp)); + _saved_temp = degrees; + switch (getMode()) { + case kDaikinDry: + case kDaikinFan: + degrees = kDaikin176DryFanTemp; + } + degrees = degrees * 2 - 18; + remote_state[kDaikin176ByteTemp] &= ~kDaikin176MaskTemp; + remote_state[kDaikin176ByteTemp] |= degrees; + remote_state[kDaikin176ByteModeButton] = 0; +} + +uint8_t IRDaikin176::getTemp(void) { + return (((remote_state[kDaikin176ByteTemp] & kDaikin176MaskTemp) / 2 ) + 9); +} + +// Set the speed of the fan, 1 for Min or 3 for Max +void IRDaikin176::setFan(const uint8_t fan) { + switch (fan) { + case kDaikinFanMin: + case kDaikin176FanMax: + remote_state[kDaikin176ByteFan] &= ~kDaikin176MaskFan; + remote_state[kDaikin176ByteFan] |= (fan << 4); + remote_state[kDaikin176ByteModeButton] = 0; + break; + default: + setFan(kDaikin176FanMax); + } +} + +uint8_t IRDaikin176::getFan() { return remote_state[kDaikin176ByteFan] >> 4; } + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRDaikin176::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: + return kDaikinFanMin; + default: + return kDaikin176FanMax; + } +} + +void IRDaikin176::setSwingHorizontal(const uint8_t position) { + switch (position) { + case kDaikin176SwingHOff: + case kDaikin176SwingHAuto: + remote_state[kDaikin176ByteSwingH] &= ~kDaikin176MaskSwingH; + remote_state[kDaikin176ByteSwingH] |= position; + break; + default: + setSwingHorizontal(kDaikin176SwingHAuto); + } +} + +uint8_t IRDaikin176::getSwingHorizontal() { + return remote_state[kDaikin176ByteSwingH] & kDaikin176MaskSwingH; +} + +// Convert a standard A/C horizontal swing into its native version. +uint8_t IRDaikin176::convertSwingH(const stdAc::swingh_t position) { + switch (position) { + case stdAc::swingh_t::kOff: + return kDaikin176SwingHOff; + case stdAc::swingh_t::kAuto: + return kDaikin176SwingHAuto; + default: + return kDaikin176SwingHAuto; + } +} +// Convert a native horizontal swing to it's common equivalent. +stdAc::swingh_t IRDaikin176::toCommonSwingH(const uint8_t setting) { + switch (setting) { + case kDaikin176SwingHOff: return stdAc::swingh_t::kOff; + case kDaikin176SwingHAuto: return stdAc::swingh_t::kAuto; + default: + return stdAc::swingh_t::kAuto; + } +} + +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRDaikin176::toCommonFanSpeed(const uint8_t speed) { + return (speed == kDaikinFanMin) ? stdAc::fanspeed_t::kMin + : stdAc::fanspeed_t::kMax; +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRDaikin176::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::DAIKIN176; + result.model = -1; // No models used. + result.power = this->getPower(); + result.mode = IRDaikin176::toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(); + result.fanspeed = this->toCommonFanSpeed(this->getFan()); + result.swingh = this->toCommonSwingH(this->getSwingHorizontal()); + + // Not supported. + result.swingv = stdAc::swingv_t::kOff; + result.quiet = false; + result.turbo = false; + result.light = false; + result.clean = false; + result.econo = false; + result.filter = false; + result.beep = false; + result.sleep = -1; + result.clock = -1; + return result; +} + +// Convert the internal state into a human readable string. +String IRDaikin176::toString() { + String result = ""; + result.reserve(80); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(getPower(), F("Power"), false); + result += addModeToString(getMode(), kDaikinAuto, kDaikin176Cool, kDaikinHeat, + kDaikinDry, kDaikinFan); + result += addTempToString(getTemp()); + result += addFanToString(getFan(), kDaikin176FanMax, kDaikinFanMin, + kDaikinFanMin, kDaikinFanMin, kDaikinFanMin); + result += F(", Swing (H): "); + result += uint64ToString(getSwingHorizontal()); + switch (getSwingHorizontal()) { + case kDaikin176SwingHAuto: + result += F(" (Auto)"); + break; + case kDaikin176SwingHOff: + result += F(" (Off)"); + break; + default: + result += F(" (UNKNOWN)"); + } + return result; +} + +#if DECODE_DAIKIN176 +// Decode the supplied Daikin 176 bit A/C message. +// Args: +// results: Ptr to the data to decode and where to store the decode result. +// nbits: Nr. of bits to expect in the data portion. (kDaikin176Bits) +// strict: Flag to indicate if we strictly adhere to the specification. +// Returns: +// boolean: True if it can decode it, false if it can't. +// +// Supported devices: +// - Daikin BRC4C153 remote. +// +// Status: BETA / Probably works. +// + +bool IRrecv::decodeDaikin176(decode_results *results, const uint16_t nbits, + const bool strict) { + if (results->rawlen < 2 * (nbits + kHeader + kFooter) - 1) + return false; + + // Compliance + if (strict && nbits != kDaikin176Bits) return false; + + uint16_t offset = kStartOffset; + const uint8_t ksectionSize[kDaikin176Sections] = {kDaikin176Section1Length, + kDaikin176Section2Length}; + + // Sections + uint16_t pos = 0; + for (uint8_t section = 0; section < kDaikin176Sections; section++) { + uint16_t used; + // Section Header + Section Data (7 bytes) + Section Footer + used = matchGeneric(results->rawbuf + offset, results->state + pos, + results->rawlen - offset, ksectionSize[section] * 8, + kDaikin176HdrMark, kDaikin176HdrSpace, + kDaikin176BitMark, kDaikin176OneSpace, + kDaikin176BitMark, kDaikin176ZeroSpace, + kDaikin176BitMark, kDaikin176Gap, + section >= kDaikin176Sections - 1, + kDaikinTolerance, kDaikinMarkExcess, false); + if (used == 0) return false; + offset += used; + pos += ksectionSize[section]; + } + // Compliance + if (strict) { + // Validate the checksum. + if (!IRDaikin176::validChecksum(results->state)) return false; + } + + // Success + results->decode_type = decode_type_t::DAIKIN176; + results->bits = nbits; + // No need to record the state as we stored it as we decoded it. + // As we use result->state, we don't record value, address, or command as it + // is a union data type. + return true; +} +#endif // DECODE_DAIKIN176 + +#if SEND_DAIKIN128 +// Send a Daikin 128 bit A/C message. +// +// Args: +// data: An array of kDaikin128StateLength bytes containing the IR command. +// +// Status: STABLE / Known Working. +// +// Supported devices: +// - Daikin BRC52B63 remote. +// +// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/827 +void IRsend::sendDaikin128(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes < kDaikin128SectionLength) + return; // Not enough bytes to send a partial message. + + for (uint16_t r = 0; r <= repeat; r++) { + enableIROut(kDaikin128Freq); + // Leader + for (uint8_t i = 0; i < 2; i++) { + mark(kDaikin128LeaderMark); + space(kDaikin128LeaderSpace); + } + // Section #1 (Header + Data) + sendGeneric(kDaikin128HdrMark, kDaikin128HdrSpace, kDaikin128BitMark, + kDaikin128OneSpace, kDaikin128BitMark, kDaikin128ZeroSpace, + kDaikin128BitMark, kDaikin128Gap, data, + kDaikin128SectionLength, + kDaikin128Freq, false, 0, kDutyDefault); + // Section #2 (Data + Footer) + sendGeneric(0, 0, kDaikin128BitMark, + kDaikin128OneSpace, kDaikin128BitMark, kDaikin128ZeroSpace, + kDaikin128FooterMark, kDaikin128Gap, + data + kDaikin128SectionLength, + nbytes - kDaikin128SectionLength, + kDaikin128Freq, false, 0, kDutyDefault); + } +} +#endif // SEND_DAIKIN128 + +// Class for handling Daikin 128 bit / 16 byte A/C messages. +// +// Code by crankyoldgit. +// Analysis by Daniel Vena +// +// Status: STABLE / Known Working. +// +// Supported Remotes: Daikin BRC52B63 remote +// +IRDaikin128::IRDaikin128(const uint16_t pin, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { stateReset(); } + +void IRDaikin128::begin() { _irsend.begin(); } + +uint8_t IRDaikin128::calcFirstChecksum(const uint8_t state[]) { + return sumNibbles(state, kDaikin128SectionLength - 1, + state[kDaikin128SectionLength - 1] & 0x0F) & 0x0F; +} + +uint8_t IRDaikin128::calcSecondChecksum(const uint8_t state[]) { + return sumNibbles(state + kDaikin128SectionLength, + kDaikin128SectionLength - 1); +} + +// Verify the checksum is valid for a given state. +// Args: +// state: The array to verify the checksum of. +// Returns: +// A boolean. +bool IRDaikin128::validChecksum(uint8_t state[]) { + // Validate the checksum of section #1. + if (state[kDaikin128SectionLength - 1] >> 4 != calcFirstChecksum(state)) + return false; + // Validate the checksum of section #2 + if (state[kDaikin128StateLength - 1] != calcSecondChecksum(state)) + return false; + return true; +} + +// Calculate and set the checksum values for the internal state. +void IRDaikin128::checksum() { + remote_state[kDaikin128SectionLength - 1] &= 0x0F; // Clear upper half. + remote_state[kDaikin128SectionLength - 1] |= + (calcFirstChecksum(remote_state) << 4); + remote_state[kDaikin128StateLength - 1] = calcSecondChecksum(remote_state); +} + +void IRDaikin128::stateReset() { + for (uint8_t i = 0; i < kDaikin128StateLength; i++) remote_state[i] = 0x00; + remote_state[0] = 0x16; + remote_state[7] = 0x04; // Most significant nibble is a checksum. + remote_state[8] = 0xA1; + // remote_state[15] is a checksum byte, it will be set by checksum(). +} + +uint8_t *IRDaikin128::getRaw() { + checksum(); // Ensure correct settings before sending. + return remote_state; +} + +void IRDaikin128::setRaw(const uint8_t new_code[]) { + for (uint8_t i = 0; i < kDaikin128StateLength; i++) + remote_state[i] = new_code[i]; +} + +#if SEND_DAIKIN128 +void IRDaikin128::send(const uint16_t repeat) { + checksum(); + _irsend.sendDaikin128(remote_state, kDaikin128StateLength, repeat); +} +#endif // SEND_DAIKIN128 + +void IRDaikin128::setPowerToggle(const bool toggle) { + if (toggle) + remote_state[kDaikin128BytePowerSwingSleep] |= kDaikin128BitPowerToggle; + else + remote_state[kDaikin128BytePowerSwingSleep] &= ~kDaikin128BitPowerToggle; +} + +bool IRDaikin128::getPowerToggle(void) { + return remote_state[kDaikin128BytePowerSwingSleep] & kDaikin128BitPowerToggle; +} + +uint8_t IRDaikin128::getMode() { + return remote_state[kDaikin128ByteModeFan] & kDaikin128MaskMode; +} + +void IRDaikin128::setMode(const uint8_t mode) { + switch (mode) { + case kDaikin128Auto: + case kDaikin128Cool: + case kDaikin128Heat: + case kDaikin128Fan: + case kDaikin128Dry: + remote_state[kDaikin128ByteModeFan] &= ~kDaikin128MaskMode; + remote_state[kDaikin128ByteModeFan] |= mode; + break; + default: + this->setMode(kDaikin128Auto); + return; + } + // Force a reset of mode dependant things. + setFan(getFan()); // Covers Quiet & Powerful too. + setEcono(getEcono()); +} + +// Convert a standard A/C mode into its native mode. +uint8_t IRDaikin128::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kDaikin128Cool; + case stdAc::opmode_t::kHeat: + return kDaikin128Heat; + case stdAc::opmode_t::kDry: + return kDaikinDry; + case stdAc::opmode_t::kFan: + return kDaikin128Fan; + default: + return kDaikin128Auto; + } +} + +// Convert a native mode to it's common equivalent. +stdAc::opmode_t IRDaikin128::toCommonMode(const uint8_t mode) { + switch (mode) { + case kDaikin128Cool: return stdAc::opmode_t::kCool; + case kDaikin128Heat: return stdAc::opmode_t::kHeat; + case kDaikin128Dry: return stdAc::opmode_t::kDry; + case kDaikin128Fan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +// Set the temp in deg C +void IRDaikin128::setTemp(const uint8_t temp) { + remote_state[kDaikin128ByteTemp] = uint8ToBcd( + std::min(kDaikin128MaxTemp, std::max(temp, kDaikin128MinTemp))); +} + +uint8_t IRDaikin128::getTemp(void) { + return bcdToUint8(remote_state[kDaikin128ByteTemp]); +} + +uint8_t IRDaikin128::getFan() { + return (remote_state[kDaikin128ByteModeFan] & kDaikin128MaskFan) >> 4; +} + +void IRDaikin128::setFan(const uint8_t speed) { + uint8_t new_speed = speed; + uint8_t mode = getMode(); + switch (speed) { + case kDaikin128FanQuiet: + case kDaikin128FanPowerful: + if (mode == kDaikin128Auto) new_speed = kDaikin128FanAuto; + // FALL-THRU + case kDaikin128FanAuto: + case kDaikin128FanHigh: + case kDaikin128FanMed: + case kDaikin128FanLow: + // if (mode == kDaikinDry) new_speed = kDaikin128FanMed; + remote_state[kDaikin128ByteModeFan] &= ~kDaikin128MaskFan; + remote_state[kDaikin128ByteModeFan] |= (new_speed << 4); + break; + default: + this->setFan(kDaikin128FanAuto); + } +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRDaikin128::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: return kDaikinFanQuiet; + case stdAc::fanspeed_t::kLow: return kDaikin128FanLow; + case stdAc::fanspeed_t::kMedium: return kDaikin128FanMed; + case stdAc::fanspeed_t::kHigh: return kDaikin128FanHigh; + case stdAc::fanspeed_t::kMax: return kDaikin128FanPowerful; + default: return kDaikin128FanAuto; + } +} + +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRDaikin128::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kDaikin128FanPowerful: return stdAc::fanspeed_t::kMax; + case kDaikin128FanHigh: return stdAc::fanspeed_t::kHigh; + case kDaikin128FanMed: return stdAc::fanspeed_t::kMedium; + case kDaikin128FanLow: return stdAc::fanspeed_t::kLow; + case kDaikinFanQuiet: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +void IRDaikin128::setSwingVertical(const bool on) { + if (on) + remote_state[kDaikin128BytePowerSwingSleep] |= kDaikin128BitSwing; + else + remote_state[kDaikin128BytePowerSwingSleep] &= ~kDaikin128BitSwing; +} + +bool IRDaikin128::getSwingVertical(void) { + return remote_state[kDaikin128BytePowerSwingSleep] & kDaikin128BitSwing; +} + +void IRDaikin128::setSleep(const bool on) { + if (on) + remote_state[kDaikin128BytePowerSwingSleep] |= kDaikin128BitSleep; + else + remote_state[kDaikin128BytePowerSwingSleep] &= ~kDaikin128BitSleep; +} + +bool IRDaikin128::getSleep(void) { + return remote_state[kDaikin128BytePowerSwingSleep] & kDaikin128BitSleep; +} + +void IRDaikin128::setEcono(const bool on) { + uint8_t mode = getMode(); + if (on && (mode == kDaikin128Cool || mode == kDaikin128Heat)) + remote_state[kDaikin128ByteEconoLight] |= kDaikin128BitEcono; + else + remote_state[kDaikin128ByteEconoLight] &= ~kDaikin128BitEcono; +} + +bool IRDaikin128::getEcono(void) { + return remote_state[kDaikin128ByteEconoLight] & kDaikin128BitEcono; +} + +void IRDaikin128::setQuiet(const bool on) { + uint8_t mode = getMode(); + if (on && (mode == kDaikin128Cool || mode == kDaikin128Heat)) + setFan(kDaikin128FanQuiet); + else if (getFan() == kDaikin128FanQuiet) + setFan(kDaikin128FanAuto); +} + +bool IRDaikin128::getQuiet(void) { + return getFan() == kDaikin128FanQuiet; +} + +void IRDaikin128::setPowerful(const bool on) { + uint8_t mode = getMode(); + if (on && (mode == kDaikin128Cool || mode == kDaikin128Heat)) + setFan(kDaikin128FanPowerful); + else if (getFan() == kDaikin128FanPowerful) + setFan(kDaikin128FanAuto); +} + +bool IRDaikin128::getPowerful(void) { + return getFan() == kDaikin128FanPowerful; +} + +// Set the clock in mins since midnight +void IRDaikin128::setClock(const uint16_t mins_since_midnight) { + uint16_t mins = mins_since_midnight; + if (mins_since_midnight >= 24 * 60) mins = 0; // Bounds check. + // Hours. + remote_state[kDaikin128ByteClockHours] = uint8ToBcd(mins / 60); + // Minutes. + remote_state[kDaikin128ByteClockMins] = uint8ToBcd(mins % 60); +} + +uint16_t IRDaikin128::getClock(void) { + return bcdToUint8(remote_state[kDaikin128ByteClockHours]) * 60 + + bcdToUint8(remote_state[kDaikin128ByteClockMins]); +} + +void IRDaikin128::setOnTimerEnabled(const bool on) { + if (on) + remote_state[kDaikin128ByteOnTimer] |= kDaikin128BitTimerEnabled; + else + remote_state[kDaikin128ByteOnTimer] &= ~kDaikin128BitTimerEnabled; +} + +bool IRDaikin128::getOnTimerEnabled(void) { + return remote_state[kDaikin128ByteOnTimer] & kDaikin128BitTimerEnabled; +} + +// Timer is rounds down to the nearest half hour. +// Args: +// ptr: A PTR to the byte containing the Timer value to be updated. +// mins_since_midnight: The number of minutes the new timer should be set to. +void IRDaikin128::setTimer(uint8_t *ptr, const uint16_t mins_since_midnight) { + uint16_t mins = mins_since_midnight; + if (mins_since_midnight >= 24 * 60) mins = 0; // Bounds check. + // Clear the time component + *ptr &= kDaikin128BitTimerEnabled; + uint8_t bcdhours = uint8ToBcd(mins / 60); + bool addhalf = (mins % 60) >= 30; + *ptr |= ((addhalf << 6) | bcdhours); +} + +// Timer is stored in nr of half hours internally. +// Args: +// ptr: A PTR to the byte containing the Timer value. +// Returns: +// A uint16_t containing the number of minutes since midnight. +uint16_t IRDaikin128::getTimer(const uint8_t *ptr) { + uint8_t bcdhours = *ptr & kDaikin128MaskHours; + bool addhalf = *ptr & kDaikin128BitHalfHour; + return bcdToUint8(bcdhours) * 60 + (addhalf ? 30 : 0); +} + +void IRDaikin128::setOnTimer(const uint16_t mins_since_midnight) { + setTimer(remote_state + kDaikin128ByteOnTimer, mins_since_midnight); +} + +uint16_t IRDaikin128::getOnTimer(void) { + return getTimer(remote_state + kDaikin128ByteOnTimer); +} + +void IRDaikin128::setOffTimerEnabled(const bool on) { + if (on) + remote_state[kDaikin128ByteOffTimer] |= kDaikin128BitTimerEnabled; + else + remote_state[kDaikin128ByteOffTimer] &= ~kDaikin128BitTimerEnabled; +} + +bool IRDaikin128::getOffTimerEnabled(void) { + return remote_state[kDaikin128ByteOffTimer] & kDaikin128BitTimerEnabled; +} + +void IRDaikin128::setOffTimer(const uint16_t mins_since_midnight) { + setTimer(remote_state + kDaikin128ByteOffTimer, mins_since_midnight); +} + +uint16_t IRDaikin128::getOffTimer(void) { + return getTimer(remote_state + kDaikin128ByteOffTimer); +} + +void IRDaikin128::setLightToggle(const uint8_t unit) { + switch (unit) { + case kDaikin128BitCeiling: + case kDaikin128BitWall: + case 0: + remote_state[kDaikin128ByteEconoLight] &= ~kDaikin128MaskLight; + remote_state[kDaikin128ByteEconoLight] |= unit; + break; + default: setLightToggle(0); + } +} + +uint8_t IRDaikin128::getLightToggle(void) { + return remote_state[kDaikin128ByteEconoLight] & kDaikin128MaskLight; +} + +// Convert the internal state into a human readable string. +String IRDaikin128::toString(void) { + String result = ""; + result.reserve(240); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(getPowerToggle(), F("Power Toggle"), false); + result += addModeToString(getMode(), kDaikin128Auto, kDaikin128Cool, + kDaikin128Heat, kDaikin128Dry, kDaikin128Fan); + result += addTempToString(getTemp()); + result += addFanToString(getFan(), kDaikin128FanHigh, kDaikin128FanLow, + kDaikin128FanAuto, kDaikin128FanQuiet, + kDaikin128FanMed); + result += addBoolToString(getPowerful(), F("Powerful")); + result += addBoolToString(getQuiet(), F("Quiet")); + result += addBoolToString(getSwingVertical(), F("Swing (V)")); + result += addBoolToString(getSleep(), F("Sleep")); + result += addBoolToString(getEcono(), F("Econo")); + result += addLabeledString(minsToString(getClock()), F("Clock")); + result += addBoolToString(getOnTimerEnabled(), F("On Timer")); + result += addLabeledString(minsToString(getOnTimer()), F("On Time")); + result += addBoolToString(getOffTimerEnabled(), F("Off Timer")); + result += addLabeledString(minsToString(getOffTimer()), F("Off Time")); + result += addIntToString(getLightToggle(), F("Light Toggle")); + result += F(" ("); + switch (getLightToggle()) { + case kDaikin128BitCeiling: result += F("Ceiling"); break; + case kDaikin128BitWall: result += F("Wall"); break; + case 0: result += F("Off"); break; + default: result += F("UNKNOWN"); + } + result += ')'; + return result; +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRDaikin128::toCommon(const stdAc::state_t *prev) { + stdAc::state_t result; + if (prev != NULL) result = *prev; + result.protocol = decode_type_t::DAIKIN128; + result.model = -1; // No models used. + result.power ^= getPowerToggle(); + result.mode = toCommonMode(getMode()); + result.celsius = true; + result.degrees = getTemp(); + result.fanspeed = toCommonFanSpeed(getFan()); + result.swingv = getSwingVertical() ? stdAc::swingv_t::kAuto + : stdAc::swingv_t::kOff; + result.quiet = getQuiet(); + result.turbo = getPowerful(); + result.econo = getEcono(); + result.light ^= (getLightToggle() != 0); + result.sleep = getSleep() ? 0 : -1; + result.clock = getClock(); + // Not supported. + result.swingh = stdAc::swingh_t::kOff; + result.clean = false; + result.filter = false; + result.beep = false; + return result; +} + +#if DECODE_DAIKIN128 +// Decode the supplied Daikin 128 bit A/C message. +// Args: +// results: Ptr to the data to decode and where to store the decode result. +// nbits: Nr. of bits to expect in the data portion. (kDaikin128Bits) +// strict: Flag to indicate if we strictly adhere to the specification. +// Returns: +// boolean: True if it can decode it, false if it can't. +// +// Supported devices: +// - Daikin BRC52B63 remote. +// +// Status: STABLE / Known Working. +// +// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/827 +bool IRrecv::decodeDaikin128(decode_results *results, const uint16_t nbits, + const bool strict) { + if (results->rawlen < 2 * (nbits + kHeader) + kFooter - 1) + return false; + if (nbits / 8 <= kDaikin128SectionLength) return false; + + // Compliance + if (strict && nbits != kDaikin128Bits) return false; + + uint16_t offset = kStartOffset; + + // Leader + for (uint8_t i = 0; i < 2; i++) { + if (!matchMark(results->rawbuf[offset++], kDaikin128LeaderMark, + kDaikinTolerance, kDaikinMarkExcess)) return false; + if (!matchSpace(results->rawbuf[offset++], kDaikin128LeaderSpace, + kDaikinTolerance, kDaikinMarkExcess)) return false; + } + const uint16_t ksectionSize[kDaikin128Sections] = { + kDaikin128SectionLength, (uint16_t)(nbits / 8 - kDaikin128SectionLength)}; + // Data Sections + uint16_t pos = 0; + for (uint8_t section = 0; section < kDaikin128Sections; section++) { + uint16_t used; + // Section Header (first section only) + Section Data (8 bytes) + + // Section Footer (Not for first section) + used = matchGeneric(results->rawbuf + offset, results->state + pos, + results->rawlen - offset, ksectionSize[section] * 8, + section == 0 ? kDaikin128HdrMark : 0, + section == 0 ? kDaikin128HdrSpace : 0, + kDaikin128BitMark, kDaikin128OneSpace, + kDaikin128BitMark, kDaikin128ZeroSpace, + section > 0 ? kDaikin128FooterMark : kDaikin128BitMark, + kDaikin128Gap, + section > 0, + kDaikinTolerance, kDaikinMarkExcess, false); + if (used == 0) return false; + offset += used; + pos += ksectionSize[section]; + } + // Compliance + if (strict) { + if (!IRDaikin128::validChecksum(results->state)) return false; + } + + // Success + results->decode_type = decode_type_t::DAIKIN128; + results->bits = nbits; + // No need to record the state as we stored it as we decoded it. + // As we use result->state, we don't record value, address, or command as it + // is a union data type. + return true; +} +#endif // DECODE_DAIKIN128 + +#if SEND_DAIKIN152 +// Send a Daikin 152 bit A/C message. +// +// Args: +// data: An array of kDaikin152StateLength bytes containing the IR command. +// +// Supported devices: +// - Daikin ARC480A5 remote. +// +// Status: Beta / Probably working. +// +// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/873 +void IRsend::sendDaikin152(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + for (uint16_t r = 0; r <= repeat; r++) { + // Leader + sendGeneric(0, 0, kDaikin152BitMark, kDaikin152OneSpace, + kDaikin152BitMark, kDaikin152ZeroSpace, + kDaikin152BitMark, kDaikin152Gap, + (uint64_t)0, kDaikin152LeaderBits, + kDaikin152Freq, false, 0, kDutyDefault); + // Header + Data + Footer + sendGeneric(kDaikin152HdrMark, kDaikin152HdrSpace, kDaikin152BitMark, + kDaikin152OneSpace, kDaikin152BitMark, kDaikin152ZeroSpace, + kDaikin152BitMark, kDaikin152Gap, data, + nbytes, kDaikin152Freq, false, 0, kDutyDefault); + } +} +#endif // SEND_DAIKIN152 + +#if DECODE_DAIKIN152 +// Decode the supplied Daikin 152 bit A/C message. +// Args: +// results: Ptr to the data to decode and where to store the decode result. +// nbits: Nr. of bits to expect in the data portion. (kDaikin152Bits) +// strict: Flag to indicate if we strictly adhere to the specification. +// Returns: +// boolean: True if it can decode it, false if it can't. +// +// Supported devices: +// - Daikin ARC480A5 remote. +// +// Status: Beta / Probably working. +// +// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/873 +bool IRrecv::decodeDaikin152(decode_results *results, const uint16_t nbits, + const bool strict) { + if (results->rawlen < 2 * (5 + nbits + kFooter) + kHeader - 1) + return false; + if (nbits / 8 < kDaikin152StateLength) return false; + + // Compliance + if (strict && nbits != kDaikin152Bits) return false; + + uint16_t offset = kStartOffset; + uint16_t used; + + // Leader + uint64_t leader = 0; + used = matchGeneric(results->rawbuf + offset, &leader, + results->rawlen - offset, kDaikin152LeaderBits, + 0, 0, // No Header + kDaikin152BitMark, kDaikin152OneSpace, + kDaikin152BitMark, kDaikin152ZeroSpace, + kDaikin152BitMark, kDaikin152Gap, // Footer gap + false, _tolerance, kMarkExcess, false); + if (used == 0 || leader != 0) return false; + offset += used; + + // Header + Data + Footer + used = matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, nbits, + kDaikin152HdrMark, kDaikin152HdrSpace, + kDaikin152BitMark, kDaikin152OneSpace, + kDaikin152BitMark, kDaikin152ZeroSpace, + kDaikin152BitMark, kDaikin152Gap, + true, _tolerance, kMarkExcess, false); + if (used == 0) return false; + + // Compliance + if (strict) { + if (!IRDaikin152::validChecksum(results->state)) return false; + } + + // Success + results->decode_type = decode_type_t::DAIKIN152; + results->bits = nbits; + // No need to record the state as we stored it as we decoded it. + // As we use result->state, we don't record value, address, or command as it + // is a union data type. + return true; +} +#endif // DECODE_DAIKIN152 + +// Class for handling Daikin 152 bit / 19 byte A/C messages. +// +// Code by crankyoldgit. +// +// Supported Remotes: Daikin ARC480A5 remote +// +// Ref: +// https://github.com/crankyoldgit/IRremoteESP8266/issues/873 +IRDaikin152::IRDaikin152(const uint16_t pin, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { stateReset(); } + +void IRDaikin152::begin() { _irsend.begin(); } + +#if SEND_DAIKIN152 +void IRDaikin152::send(const uint16_t repeat) { + checksum(); + _irsend.sendDaikin152(remote_state, kDaikin152StateLength, repeat); +} +#endif // SEND_DAIKIN152 + +// Verify the checksum is valid for a given state. +// Args: +// state: The array to verify the checksum of. +// length: The size of the state. +// Returns: +// A boolean. +bool IRDaikin152::validChecksum(uint8_t state[], const uint16_t length) { + // Validate the checksum of the given state. + if (length <= 1 || state[length - 1] != sumBytes(state, length - 1)) + return false; + else + return true; +} + +// Calculate and set the checksum values for the internal state. +void IRDaikin152::checksum() { + remote_state[kDaikin152StateLength - 1] = sumBytes( + remote_state, kDaikin152StateLength - 1); +} + +void IRDaikin152::stateReset() { + for (uint8_t i = 3; i < kDaikin152StateLength; i++) remote_state[i] = 0x00; + remote_state[0] = 0x11; + remote_state[1] = 0xDA; + remote_state[2] = 0x27; + // remote_state[19] is a checksum byte, it will be set by checksum(). +} + +uint8_t *IRDaikin152::getRaw() { + checksum(); // Ensure correct settings before sending. + return remote_state; +} + +void IRDaikin152::setRaw(const uint8_t new_code[]) { + for (uint8_t i = 0; i < kDaikin152StateLength; i++) + remote_state[i] = new_code[i]; +} diff --git a/lib/IRremoteESP8266-2.6.5/src/ir_Daikin.h b/lib/IRremoteESP8266-2.6.5/src/ir_Daikin.h new file mode 100644 index 000000000..98a38c640 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.5/src/ir_Daikin.h @@ -0,0 +1,763 @@ +// Copyright 2016 sillyfrog +// Copyright 2017 sillyfrog, crankyoldgit +// Copyright 2018-2019 crankyoldgit + +// Supports: +// Brand: Daikin, Model: ARC433** remote +// Brand: Daikin, Model: ARC477A1 remote +// Brand: Daikin, Model: FTXZ25NV1B A/C +// Brand: Daikin, Model: FTXZ35NV1B A/C +// Brand: Daikin, Model: FTXZ50NV1B A/C +// Brand: Daikin, Model: ARC433B69 remote +// Brand: Daikin, Model: ARC423A5 remote +// Brand: Daikin, Model: FTE12HV2S A/C +// Brand: Daikin, Model: BRC4C153 remote +// Brand: Daikin, Model: 17 Series A/C (DAIKIN128) +// Brand: Daikin, Model: FTXB12AXVJU A/C (DAIKIN128) +// Brand: Daikin, Model: FTXB09AXVJU A/C (DAIKIN128) +// Brand: Daikin, Model: BRC52B63 remote (DAIKIN128) +// Brand: Daikin, Model: ARC480A5 remote (DAIKIN152) + +#ifndef IR_DAIKIN_H_ +#define IR_DAIKIN_H_ + +#ifndef UNIT_TEST +#include +#endif +#include "IRrecv.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif + +/* + Daikin AC map (i.e. DAIKIN, not the other variants) + byte 6= + b4:Comfort + byte 7= checksum of the first part (and last byte before a 29ms pause) + byte 13=Current time, mins past midnight, low bits + byte 14 + b5-b3=Day of the week (SUN=1, MON=2, ..., SAT=7) + b2-b0=Current time, mins past midnight, high bits + byte 15= checksum of the second part (and last byte before a 29ms pause) + byte 21=mode + b7 = 0 + b6+b5+b4 = Mode + Modes: b6+b5+b4 + 011 = Cool + 100 = Heat (temp 23) + 110 = FAN (temp not shown, but 25) + 000 = Fully Automatic (temp 25) + 010 = DRY (temp 0xc0 = 96 degrees c) + b3 = 1 + b2 = OFF timer set + b1 = ON timer set + b0 = Air Conditioner ON + byte 22=temp*2 (Temp should be between 10 - 32) + byte 24=Fan + FAN control + b7+b6+b5+b4 = Fan speed + Fan: b7+b6+b5+b4 + 0×3 = 1 bar + 0×4 = 2 bar + 0×5 = 3 bar + 0×6 = 4 bar + 0×7 = 5 bar + 0xa = Auto + 0xb = Quite + b3+b2+b1+b0 = Swing control up/down + Swing control up/down: + 0000 = Swing up/down off + 1111 = Swing up/down on + byte 25 + Swing control left/right: + 0000 = Swing left/right off + 1111 = Swing left/right on + byte 26=On timer mins past midnight, low bits + byte 27 + b0-b3=On timer mins past midnight, high bits + b4-b7=Off timer mins past midnight, low bits + byte 28=Off timer mins past midnight, high bits + byte 29=Aux -> Powerful (bit 1), Silent (bit 5) + byte 32=Aux2 + b1: Sensor + b2: Econo mode + b7: Intelligent eye on + byte 33=Aux3 + b1: Mold Proof + byte 34= checksum of the third part +*/ + +// Constants +const uint8_t kDaikinAuto = 0b000; +const uint8_t kDaikinDry = 0b010; +const uint8_t kDaikinCool = 0b011; +const uint8_t kDaikinHeat = 0b100; +const uint8_t kDaikinFan = 0b110; +const uint8_t kDaikinMinTemp = 10; // Celsius +const uint8_t kDaikinMaxTemp = 32; // Celsius +const uint8_t kDaikinFanMin = 1; +const uint8_t kDaikinFanMed = 3; +const uint8_t kDaikinFanMax = 5; +const uint8_t kDaikinFanAuto = 0b1010; +const uint8_t kDaikinFanQuiet = 0b1011; +const uint16_t kDaikinHeaderLength = 5; +const uint8_t kDaikinSections = 3; +const uint8_t kDaikinSection1Length = 8; +const uint8_t kDaikinSection2Length = 8; +const uint8_t kDaikinSection3Length = + kDaikinStateLength - kDaikinSection1Length - kDaikinSection2Length; +const uint8_t kDaikinByteComfort = 6; +const uint8_t kDaikinByteChecksum1 = 7; +const uint8_t kDaikinBitComfort = 0b00010000; +const uint8_t kDaikinByteClockMinsLow = 13; +const uint8_t kDaikinByteClockMinsHigh = 14; +const uint8_t kDaikinByteChecksum2 = 15; +const uint8_t kDaikinBytePower = 21; +const uint8_t kDaikinBitPower = 0b00000001; +const uint8_t kDaikinByteTemp = 22; +const uint8_t kDaikinByteFan = 24; +const uint8_t kDaikinByteSwingH = 25; +const uint8_t kDaikinByteOnTimerMinsLow = 26; +const uint8_t kDaikinByteOnTimerMinsHigh = 27; +const uint8_t kDaikinByteOffTimerMinsLow = kDaikinByteOnTimerMinsHigh; +const uint8_t kDaikinByteOffTimerMinsHigh = 28; +const uint8_t kDaikinBytePowerful = 29; +const uint8_t kDaikinBitPowerful = 0b00000001; +const uint8_t kDaikinByteSilent = kDaikinBytePowerful; +const uint8_t kDaikinBitSilent = 0b00100000; +const uint8_t kDaikinByteSensor = 32; +const uint8_t kDaikinBitSensor = 0b00000010; +const uint8_t kDaikinByteEcono = kDaikinByteSensor; +const uint8_t kDaikinBitEcono = 0b00000100; +const uint8_t kDaikinByteEye = kDaikinByteSensor; +const uint8_t kDaikinBitEye = 0b10000000; +const uint8_t kDaikinByteWeeklyTimer = kDaikinByteSensor; +const uint8_t kDaikinBitWeeklyTimer = 0b10000000; +const uint8_t kDaikinByteMold = 33; +const uint8_t kDaikinBitMold = 0b00000010; +const uint8_t kDaikinByteOffTimer = kDaikinBytePower; +const uint8_t kDaikinBitOffTimer = 0b00000100; +const uint8_t kDaikinByteOnTimer = kDaikinByteOffTimer; +const uint8_t kDaikinBitOnTimer = 0b00000010; +const uint8_t kDaikinByteChecksum3 = kDaikinStateLength - 1; +const uint16_t kDaikinUnusedTime = 0x600; +const uint8_t kDaikinBeepQuiet = 1; +const uint8_t kDaikinBeepLoud = 2; +const uint8_t kDaikinBeepOff = 3; +const uint8_t kDaikinLightBright = 1; +const uint8_t kDaikinLightDim = 2; +const uint8_t kDaikinLightOff = 3; +const uint8_t kDaikinCurBit = kDaikinStateLength; +const uint8_t kDaikinCurIndex = kDaikinStateLength + 1; +const uint8_t kDaikinTolerance = 35; +const uint16_t kDaikinMarkExcess = kMarkExcess; +const uint16_t kDaikinHdrMark = 3650; // kDaikinBitMark * 8 +const uint16_t kDaikinHdrSpace = 1623; // kDaikinBitMark * 4 +const uint16_t kDaikinBitMark = 428; +const uint16_t kDaikinZeroSpace = 428; +const uint16_t kDaikinOneSpace = 1280; +const uint16_t kDaikinGap = 29000; +// Note bits in each octet swapped so can be sent as a single value +const uint64_t kDaikinFirstHeader64 = + 0b1101011100000000000000001100010100000000001001111101101000010001; + +// Another variant of the protocol for the Daikin ARC477A1 remote. +const uint16_t kDaikin2Freq = 36700; // Modulation Frequency in Hz. +const uint16_t kDaikin2LeaderMark = 10024; +const uint16_t kDaikin2LeaderSpace = 25180; +const uint16_t kDaikin2Gap = kDaikin2LeaderMark + kDaikin2LeaderSpace; +const uint16_t kDaikin2HdrMark = 3500; +const uint16_t kDaikin2HdrSpace = 1728; +const uint16_t kDaikin2BitMark = 460; +const uint16_t kDaikin2OneSpace = 1270; +const uint16_t kDaikin2ZeroSpace = 420; +const uint16_t kDaikin2Sections = 2; +const uint16_t kDaikin2Section1Length = 20; +const uint16_t kDaikin2Section2Length = 19; +const uint8_t kDaikin2Tolerance = 5; // Extra percentage tolerance + +const uint8_t kDaikin2BitSleepTimer = 0b00100000; +const uint8_t kDaikin2BitPurify = 0b00010000; +const uint8_t kDaikin2BitEye = 0b00000010; +const uint8_t kDaikin2BitEyeAuto = 0b10000000; +const uint8_t kDaikin2BitMold = 0b00001000; +const uint8_t kDaikin2BitClean = 0b00100000; +const uint8_t kDaikin2BitFreshAir = 0b00000001; +const uint8_t kDaikin2BitFreshAirHigh = 0b10000000; +const uint8_t kDaikin2BitPower = 0b10000000; +const uint8_t kDaikin2LightMask = 0b00110000; +const uint8_t kDaikin2BeepMask = 0b11000000; +const uint8_t kDaikin2SwingVHigh = 0x1; +const uint8_t kDaikin2SwingVLow = 0x6; +const uint8_t kDaikin2SwingVBreeze = 0xC; +const uint8_t kDaikin2SwingVCirculate = 0xD; +const uint8_t kDaikin2SwingVAuto = 0xE; +const uint8_t kDaikin2SwingHAuto = 0xBE; +const uint8_t kDaikin2SwingHSwing = 0xBF; +const uint8_t kDaikin2MinCoolTemp = 18; // Min temp (in C) when in Cool mode. + +// Another variant of the protocol for the Daikin ARC433B69 remote. +const uint16_t kDaikin216Freq = 38000; // Modulation Frequency in Hz. +const uint16_t kDaikin216HdrMark = 3440; +const uint16_t kDaikin216HdrSpace = 1750; +const uint16_t kDaikin216BitMark = 420; +const uint16_t kDaikin216OneSpace = 1300; +const uint16_t kDaikin216ZeroSpace = 450; +const uint16_t kDaikin216Gap = 29650; +const uint16_t kDaikin216Sections = 2; +const uint16_t kDaikin216Section1Length = 8; +const uint16_t kDaikin216Section2Length = kDaikin216StateLength - + kDaikin216Section1Length; +const uint8_t kDaikin216BytePower = 13; +const uint8_t kDaikin216ByteMode = kDaikin216BytePower; +const uint8_t kDaikin216MaskMode = 0b01110000; +const uint8_t kDaikin216ByteTemp = 14; +const uint8_t kDaikin216MaskTemp = 0b01111110; +const uint8_t kDaikin216ByteFan = 16; +const uint8_t kDaikin216MaskFan = 0b11110000; +const uint8_t kDaikin216ByteSwingV = 16; +const uint8_t kDaikin216MaskSwingV = 0b00001111; +const uint8_t kDaikin216ByteSwingH = 17; +const uint8_t kDaikin216MaskSwingH = kDaikin216MaskSwingV; +const uint8_t kDaikin216BytePowerful = 21; + +// Another variant of the protocol for the Daikin ARC423A5 remote. +const uint16_t kDaikin160Freq = 38000; // Modulation Frequency in Hz. +const uint16_t kDaikin160HdrMark = 5000; +const uint16_t kDaikin160HdrSpace = 2145; +const uint16_t kDaikin160BitMark = 342; +const uint16_t kDaikin160OneSpace = 1786; +const uint16_t kDaikin160ZeroSpace = 700; +const uint16_t kDaikin160Gap = 29650; +const uint16_t kDaikin160Sections = 2; +const uint16_t kDaikin160Section1Length = 7; +const uint16_t kDaikin160Section2Length = kDaikin160StateLength - + kDaikin160Section1Length; +const uint8_t kDaikin160BytePower = 12; +const uint8_t kDaikin160ByteMode = kDaikin160BytePower; +const uint8_t kDaikin160MaskMode = 0b01110000; +const uint8_t kDaikin160ByteTemp = 16; +const uint8_t kDaikin160MaskTemp = 0b01111110; +const uint8_t kDaikin160ByteFan = 17; +const uint8_t kDaikin160MaskFan = 0b00001111; +const uint8_t kDaikin160ByteSwingV = 13; +const uint8_t kDaikin160MaskSwingV = 0b11110000; +const uint8_t kDaikin160SwingVLowest = 0x1; +const uint8_t kDaikin160SwingVLow = 0x2; +const uint8_t kDaikin160SwingVMiddle = 0x3; +const uint8_t kDaikin160SwingVHigh = 0x4; +const uint8_t kDaikin160SwingVHighest = 0x5; +const uint8_t kDaikin160SwingVAuto = 0xF; + +// Another variant of the protocol for the Daikin BRC4C153 remote. +const uint16_t kDaikin176Freq = 38000; // Modulation Frequency in Hz. +const uint16_t kDaikin176HdrMark = 5070; +const uint16_t kDaikin176HdrSpace = 2140; +const uint16_t kDaikin176BitMark = 370; +const uint16_t kDaikin176OneSpace = 1780; +const uint16_t kDaikin176ZeroSpace = 710; +const uint16_t kDaikin176Gap = 29410; +const uint16_t kDaikin176Sections = 2; +const uint16_t kDaikin176Section1Length = 7; +const uint16_t kDaikin176Section2Length = kDaikin176StateLength - + kDaikin176Section1Length; +const uint8_t kDaikin176Cool = 0b111; // 7 +const uint8_t kDaikin176BytePower = 14; +const uint8_t kDaikin176ByteMode = 12; +const uint8_t kDaikin176MaskMode = 0b01110000; +const uint8_t kDaikin176ByteModeButton = 13; +const uint8_t kDaikin176ModeButton = 0b00000100; +const uint8_t kDaikin176ByteTemp = 17; +const uint8_t kDaikin176MaskTemp = 0b01111110; +const uint8_t kDaikin176DryFanTemp = 17; // Dry/Fan mode is always 17 Celsius. +const uint8_t kDaikin176ByteFan = 18; +const uint8_t kDaikin176MaskFan = 0b11110000; +const uint8_t kDaikin176FanMax = 3; +const uint8_t kDaikin176ByteSwingH = 18; +const uint8_t kDaikin176MaskSwingH = 0b00001111; +const uint8_t kDaikin176SwingHAuto = 0x5; +const uint8_t kDaikin176SwingHOff = 0x6; + +// Another variant of the protocol for the Daikin BRC52B63 remote. +// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/827 +const uint16_t kDaikin128Freq = 38000; // Modulation Frequency in Hz. +const uint16_t kDaikin128LeaderMark = 9800; +const uint16_t kDaikin128LeaderSpace = 9800; +const uint16_t kDaikin128HdrMark = 4600; +const uint16_t kDaikin128HdrSpace = 2500; +const uint16_t kDaikin128BitMark = 350; +const uint16_t kDaikin128OneSpace = 954; +const uint16_t kDaikin128ZeroSpace = 382; +const uint16_t kDaikin128Gap = 20300; +const uint16_t kDaikin128FooterMark = kDaikin128HdrMark; +const uint16_t kDaikin128Sections = 2; +const uint16_t kDaikin128SectionLength = 8; +const uint8_t kDaikin128ByteModeFan = 1; +const uint8_t kDaikin128MaskMode = 0b00001111; +const uint8_t kDaikin128Dry = 0b00000001; +const uint8_t kDaikin128Cool = 0b00000010; +const uint8_t kDaikin128Fan = 0b00000100; +const uint8_t kDaikin128Heat = 0b00001000; +const uint8_t kDaikin128Auto = 0b00001010; +const uint8_t kDaikin128MaskFan = 0b11110000; +const uint8_t kDaikin128FanAuto = 0b0001; +const uint8_t kDaikin128FanHigh = 0b0010; +const uint8_t kDaikin128FanMed = 0b0100; +const uint8_t kDaikin128FanLow = 0b1000; +const uint8_t kDaikin128FanPowerful = 0b0011; +const uint8_t kDaikin128FanQuiet = 0b1001; +const uint8_t kDaikin128ByteClockMins = 2; +const uint8_t kDaikin128ByteClockHours = 3; +const uint8_t kDaikin128ByteOnTimer = 4; +const uint8_t kDaikin128ByteOffTimer = 5; +const uint8_t kDaikin128BitTimerEnabled = 0b10000000; +const uint8_t kDaikin128BitHalfHour = 0b01000000; +const uint8_t kDaikin128MaskHours = 0b00111111; +const uint8_t kDaikin128ByteTemp = 6; +const uint8_t kDaikin128MinTemp = 16; // C +const uint8_t kDaikin128MaxTemp = 30; // C +const uint8_t kDaikin128BytePowerSwingSleep = 7; +const uint8_t kDaikin128BitSwing = 0b00000001; +const uint8_t kDaikin128BitSleep = 0b00000010; +const uint8_t kDaikin128BitPowerToggle = 0b00001000; +const uint8_t kDaikin128ByteEconoLight = 9; +const uint8_t kDaikin128BitEcono = 0b00000100; +const uint8_t kDaikin128BitWall = 0b00001000; +const uint8_t kDaikin128BitCeiling = 0b00000001; +const uint8_t kDaikin128MaskLight = kDaikin128BitWall | kDaikin128BitCeiling; + +// Another variant of the protocol for the Daikin ARC480A5 remote. +// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/873 +const uint16_t kDaikin152Freq = 38000; // Modulation Frequency in Hz. +const uint8_t kDaikin152LeaderBits = 5; +const uint16_t kDaikin152HdrMark = 3492; +const uint16_t kDaikin152HdrSpace = 1718; +const uint16_t kDaikin152BitMark = 433; +const uint16_t kDaikin152OneSpace = 1529; +const uint16_t kDaikin152ZeroSpace = kDaikin152BitMark; +const uint16_t kDaikin152Gap = 25182; + +// Legacy defines. +#define DAIKIN_COOL kDaikinCool +#define DAIKIN_HEAT kDaikinHeat +#define DAIKIN_FAN kDaikinFan +#define DAIKIN_AUTO kDaikinAuto +#define DAIKIN_DRY kDaikinDry +#define DAIKIN_MIN_TEMP kDaikinMinTemp +#define DAIKIN_MAX_TEMP kDaikinMaxTemp +#define DAIKIN_FAN_MIN kDaikinFanMin +#define DAIKIN_FAN_MAX kDaikinFanMax +#define DAIKIN_FAN_AUTO kDaikinFanAuto +#define DAIKIN_FAN_QUIET kDaikinFanQuiet + +class IRDaikinESP { + public: + explicit IRDaikinESP(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); + +#if SEND_DAIKIN + void send(const uint16_t repeat = kDaikinDefaultRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } +#endif + void begin(void); + void on(void); + void off(void); + void setPower(const bool on); + bool getPower(void); + void setTemp(const uint8_t temp); + uint8_t getTemp(); + void setFan(const uint8_t fan); + uint8_t getFan(void); + void setMode(const uint8_t mode); + uint8_t getMode(void); + void setSwingVertical(const bool on); + bool getSwingVertical(void); + void setSwingHorizontal(const bool on); + bool getSwingHorizontal(void); + bool getQuiet(void); + void setQuiet(const bool on); + bool getPowerful(void); + void setPowerful(const bool on); + void setSensor(const bool on); + bool getSensor(void); + void setEcono(const bool on); + bool getEcono(void); + void setMold(const bool on); + bool getMold(void); + void setComfort(const bool on); + bool getComfort(void); + void enableOnTimer(const uint16_t starttime); + void disableOnTimer(void); + uint16_t getOnTime(void); + bool getOnTimerEnabled(); + void enableOffTimer(const uint16_t endtime); + void disableOffTimer(void); + uint16_t getOffTime(void); + bool getOffTimerEnabled(void); + void setCurrentTime(const uint16_t mins_since_midnight); + uint16_t getCurrentTime(void); + void setCurrentDay(const uint8_t day_of_week); + uint8_t getCurrentDay(void); + void setWeeklyTimerEnable(const bool on); + bool getWeeklyTimerEnable(void); + uint8_t* getRaw(void); + void setRaw(const uint8_t new_code[], + const uint16_t length = kDaikinStateLength); + static bool validChecksum(uint8_t state[], + const uint16_t length = kDaikinStateLength); + static uint8_t convertMode(const stdAc::opmode_t mode); + static uint8_t convertFan(const stdAc::fanspeed_t speed); + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + stdAc::state_t toCommon(void); + String toString(void); +#ifndef UNIT_TEST + + private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif + // # of bytes per command + uint8_t remote[kDaikinStateLength]; + void stateReset(void); + void checksum(void); +}; + +// Class to emulate a Daikin ARC477A1 remote. +class IRDaikin2 { + public: + explicit IRDaikin2(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); + +#if SEND_DAIKIN2 + void send(const uint16_t repeat = kDaikin2DefaultRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } +#endif + void begin(); + void on(); + void off(); + void setPower(const bool state); + bool getPower(); + void setTemp(const uint8_t temp); + uint8_t getTemp(); + void setFan(const uint8_t fan); + uint8_t getFan(); + uint8_t getMode(); + void setMode(const uint8_t mode); + void setSwingVertical(const uint8_t position); + uint8_t getSwingVertical(); + void setSwingHorizontal(const uint8_t position); + uint8_t getSwingHorizontal(); + bool getQuiet(); + void setQuiet(const bool on); + bool getPowerful(); + void setPowerful(const bool on); + void setSensor(const bool on); + bool getSensor(); + void setEcono(const bool on); + bool getEcono(); + void setEye(const bool on); + bool getEye(); + void setEyeAuto(const bool on); + bool getEyeAuto(); + void setPurify(const bool on); + bool getPurify(); + void setMold(const bool on); + bool getMold(); + void enableOnTimer(const uint16_t starttime); + void disableOnTimer(); + uint16_t getOnTime(); + bool getOnTimerEnabled(); + void enableSleepTimer(const uint16_t sleeptime); + void disableSleepTimer(); + uint16_t getSleepTime(); + bool getSleepTimerEnabled(); + void enableOffTimer(const uint16_t endtime); + void disableOffTimer(); + uint16_t getOffTime(); + bool getOffTimerEnabled(); + void setCurrentTime(const uint16_t time); + uint16_t getCurrentTime(); + void setBeep(const uint8_t beep); + uint8_t getBeep(); + void setLight(const uint8_t light); + uint8_t getLight(); + void setClean(const bool on); + bool getClean(); + void setFreshAir(const bool on); + bool getFreshAir(); + void setFreshAirHigh(const bool on); + bool getFreshAirHigh(); + uint8_t* getRaw(); + void setRaw(const uint8_t new_code[]); + uint32_t getCommand(); + void setCommand(uint32_t value); + static bool validChecksum(uint8_t state[], + const uint16_t length = kDaikin2StateLength); + static uint8_t convertMode(const stdAc::opmode_t mode); + static uint8_t convertFan(const stdAc::fanspeed_t speed); + uint8_t convertSwingV(const stdAc::swingv_t position); + static stdAc::swingv_t toCommonSwingV(const uint8_t setting); + static stdAc::swingh_t toCommonSwingH(const uint8_t setting); + stdAc::state_t toCommon(void); + String toString(); +#ifndef UNIT_TEST + + private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif + // # of bytes per command + uint8_t remote_state[kDaikin2StateLength]; + void stateReset(); + void checksum(); + void clearOnTimerFlag(); + void clearSleepTimerFlag(); +}; + +// Class to emulate a Daikin ARC433B69 remote. +class IRDaikin216 { + public: + explicit IRDaikin216(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); + +#if SEND_DAIKIN216 + void send(const uint16_t repeat = kDaikin216DefaultRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } +#endif + void begin(); + uint8_t* getRaw(); + void setRaw(const uint8_t new_code[]); + static bool validChecksum(uint8_t state[], + const uint16_t length = kDaikin216StateLength); + void on(void); + void off(void); + void setPower(const bool on); + bool getPower(void); + void setTemp(const uint8_t temp); + uint8_t getTemp(); + void setMode(const uint8_t mode); + uint8_t getMode(void); + static uint8_t convertMode(const stdAc::opmode_t mode); + void setFan(const uint8_t fan); + uint8_t getFan(void); + static uint8_t convertFan(const stdAc::fanspeed_t speed); + void setSwingVertical(const bool on); + bool getSwingVertical(void); + void setSwingHorizontal(const bool on); + bool getSwingHorizontal(void); + void setQuiet(const bool on); + bool getQuiet(void); + void setPowerful(const bool on); + bool getPowerful(void); + stdAc::state_t toCommon(void); + String toString(void); +#ifndef UNIT_TEST + + private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif + // # of bytes per command + uint8_t remote_state[kDaikin216StateLength]; + void stateReset(); + void checksum(); +}; + +// Class to emulate a Daikin ARC423A5 remote. +class IRDaikin160 { + public: + explicit IRDaikin160(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); + +#if SEND_DAIKIN160 + void send(const uint16_t repeat = kDaikin160DefaultRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } +#endif + void begin(); + uint8_t* getRaw(); + void setRaw(const uint8_t new_code[]); + static bool validChecksum(uint8_t state[], + const uint16_t length = kDaikin160StateLength); + void on(void); + void off(void); + void setPower(const bool on); + bool getPower(void); + void setTemp(const uint8_t temp); + uint8_t getTemp(); + void setMode(const uint8_t mode); + uint8_t getMode(void); + static uint8_t convertMode(const stdAc::opmode_t mode); + void setFan(const uint8_t fan); + uint8_t getFan(void); + static uint8_t convertFan(const stdAc::fanspeed_t speed); + void setSwingVertical(const uint8_t position); + uint8_t getSwingVertical(void); + static uint8_t convertSwingV(const stdAc::swingv_t position); + static stdAc::swingv_t toCommonSwingV(const uint8_t setting); + stdAc::state_t toCommon(void); + String toString(void); +#ifndef UNIT_TEST + + private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif + // # of bytes per command + uint8_t remote_state[kDaikin160StateLength]; + void stateReset(); + void checksum(); +}; + +// Class to emulate a Daikin BRC4C153 remote. +class IRDaikin176 { + public: + explicit IRDaikin176(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); + +#if SEND_DAIKIN176 + void send(const uint16_t repeat = kDaikin176DefaultRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } +#endif + void begin(); + uint8_t* getRaw(); + void setRaw(const uint8_t new_code[]); + static bool validChecksum(uint8_t state[], + const uint16_t length = kDaikin176StateLength); + void on(void); + void off(void); + void setPower(const bool on); + bool getPower(void); + void setTemp(const uint8_t temp); + uint8_t getTemp(); + void setMode(const uint8_t mode); + uint8_t getMode(void); + static uint8_t convertMode(const stdAc::opmode_t mode); + void setFan(const uint8_t fan); + uint8_t getFan(void); + static uint8_t convertFan(const stdAc::fanspeed_t speed); + void setSwingHorizontal(const uint8_t position); + uint8_t getSwingHorizontal(void); + static uint8_t convertSwingH(const stdAc::swingh_t position); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::swingh_t toCommonSwingH(const uint8_t setting); + stdAc::state_t toCommon(void); + String toString(void); + +#ifndef UNIT_TEST + + private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif + // # of bytes per command + uint8_t remote_state[kDaikin176StateLength]; + uint8_t _saved_temp; + void stateReset(); + void checksum(); +}; + +// Class to emulate a Daikin BRC52B63 remote / Daikin 17 series A/C. +class IRDaikin128 { + public: + explicit IRDaikin128(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); +#if SEND_DAIKIN128 + void send(const uint16_t repeat = kDaikin128DefaultRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } +#endif // SEND_DAIKIN128 + void begin(); + void setPowerToggle(const bool toggle); + bool getPowerToggle(void); + void setTemp(const uint8_t temp); + uint8_t getTemp(void); + void setFan(const uint8_t fan); + uint8_t getFan(void); + uint8_t getMode(void); + void setMode(const uint8_t mode); + void setSwingVertical(const bool on); + bool getSwingVertical(); + bool getSleep(void); + void setSleep(const bool on); + bool getQuiet(void); + void setQuiet(const bool on); + bool getPowerful(void); + void setPowerful(const bool on); + void setEcono(const bool on); + bool getEcono(void); + void setOnTimer(const uint16_t mins_since_midnight); + uint16_t getOnTimer(void); + bool getOnTimerEnabled(void); + void setOnTimerEnabled(const bool on); + void setOffTimer(const uint16_t mins_since_midnight); + uint16_t getOffTimer(void); + bool getOffTimerEnabled(void); + void setOffTimerEnabled(const bool on); + void setClock(const uint16_t mins_since_midnight); + uint16_t getClock(void); + void setLightToggle(const uint8_t unit_type); + uint8_t getLightToggle(void); + uint8_t* getRaw(void); + void setRaw(const uint8_t new_code[]); + static bool validChecksum(uint8_t state[]); + static uint8_t convertMode(const stdAc::opmode_t mode); + static uint8_t convertFan(const stdAc::fanspeed_t speed); + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + stdAc::state_t toCommon(const stdAc::state_t *prev = NULL); + String toString(void); +#ifndef UNIT_TEST + + private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif + // # of bytes per command + uint8_t remote_state[kDaikin128StateLength]; + void stateReset(void); + static uint8_t calcFirstChecksum(const uint8_t state[]); + static uint8_t calcSecondChecksum(const uint8_t state[]); + static void setTimer(uint8_t *ptr, const uint16_t mins_since_midnight); + static uint16_t getTimer(const uint8_t *ptr); + void checksum(void); + void clearOnTimerFlag(void); + void clearSleepTimerFlag(void); +}; + +// Class to emulate a Daikin ARC480A5 remote. +class IRDaikin152 { + public: + explicit IRDaikin152(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); + +#if SEND_DAIKIN152 + void send(const uint16_t repeat = kDaikin152DefaultRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } +#endif + void begin(); + uint8_t* getRaw(); + void setRaw(const uint8_t new_code[]); + static bool validChecksum(uint8_t state[], + const uint16_t length = kDaikin152StateLength); +#ifndef UNIT_TEST + + private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif + // # of bytes per command + uint8_t remote_state[kDaikin152StateLength]; + void stateReset(); + void checksum(); +}; +#endif // IR_DAIKIN_H_ diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Denon.cpp b/lib/IRremoteESP8266-2.6.5/src/ir_Denon.cpp similarity index 71% rename from lib/IRremoteESP8266-2.6.0/src/ir_Denon.cpp rename to lib/IRremoteESP8266-2.6.5/src/ir_Denon.cpp index 6798e022e..8a32ae261 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Denon.cpp +++ b/lib/IRremoteESP8266-2.6.5/src/ir_Denon.cpp @@ -6,12 +6,6 @@ #include "IRsend.h" #include "IRutils.h" -// DDDD EEEEE N N OOO N N -// D D E NN N O O NN N -// D D EEE N N N O O N N N -// D D E N NN O O N NN -// DDDD EEEEE N N OOO N N - // Original Denon support added by https://github.com/csBlueChip // Ported over by Massimiliano Pinto @@ -43,7 +37,7 @@ const uint64_t kDenonManufacturer = 0x2A4CULL; // // Args: // data: Contents of the message to be sent. -// nbits: Nr. of bits of data to be sent. Typically DENON_BITS. +// nbits: Nr. of bits of data to be sent. Typically kDenonBits. // repeat: Nr. of additional times the message is to be sent. // // Status: BETA / Should be working. @@ -70,7 +64,7 @@ void IRsend::sendDenon(uint64_t data, uint16_t nbits, uint16_t repeat) { // // Args: // results: Ptr to the data to decode and where to store the decode result. -// nbits: Expected nr. of data bits. (Typically DENON_BITS) +// nbits: Expected nr. of data bits. (Typically kDenonBits) // Returns: // boolean: True if it can decode it, false if it can't. // @@ -82,8 +76,8 @@ bool IRrecv::decodeDenon(decode_results *results, uint16_t nbits, bool strict) { // Compliance if (strict) { switch (nbits) { - case DENON_BITS: - case DENON_48_BITS: + case kDenonBits: + case kDenon48Bits: case kDenonLegacyBits: break; default: @@ -103,33 +97,18 @@ bool IRrecv::decodeDenon(decode_results *results, uint16_t nbits, bool strict) { // We couldn't decode it as expected, so try the old legacy method. // NOTE: I don't think this following protocol actually exists. // Looks like a partial version of the Sharp protocol. - // Check we have enough data - if (results->rawlen < 2 * nbits + kHeader + kFooter - 1) return false; if (strict && nbits != kDenonLegacyBits) return false; uint64_t data = 0; uint16_t offset = kStartOffset; - // Header - if (!matchMark(results->rawbuf[offset], kDenonHdrMark)) return false; - // Calculate how long the common tick time is based on the header mark. - uint32_t m_tick = results->rawbuf[offset++] * kRawTick / kDenonHdrMarkTicks; - if (!matchSpace(results->rawbuf[offset], kDenonHdrSpace)) return false; - uint32_t s_tick = - results->rawbuf[offset++] * kRawTick / kDenonHdrSpaceTicks; - - // Data - match_result_t data_result = - matchData(&(results->rawbuf[offset]), nbits, - kDenonBitMarkTicks * m_tick, kDenonOneSpaceTicks * s_tick, - kDenonBitMarkTicks * m_tick, kDenonZeroSpaceTicks * s_tick); - if (data_result.success == false) return false; - data = data_result.data; - offset += data_result.used; - - // Footer - if (!matchMark(results->rawbuf[offset++], kDenonBitMarkTicks * m_tick)) - return false; + // Match Header + Data + Footer + if (!matchGeneric(results->rawbuf + offset, &data, + results->rawlen - offset, nbits, + kDenonHdrMark, kDenonHdrSpace, + kDenonBitMark, kDenonOneSpace, + kDenonBitMark, kDenonZeroSpace, + kDenonBitMark, 0, false)) return false; // Success results->bits = nbits; diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Dish.cpp b/lib/IRremoteESP8266-2.6.5/src/ir_Dish.cpp similarity index 70% rename from lib/IRremoteESP8266-2.6.0/src/ir_Dish.cpp rename to lib/IRremoteESP8266-2.6.5/src/ir_Dish.cpp index 040aa3bf7..b217da763 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Dish.cpp +++ b/lib/IRremoteESP8266-2.6.5/src/ir_Dish.cpp @@ -5,15 +5,12 @@ #include "IRsend.h" #include "IRutils.h" -// DDDD IIIII SSSS H H -// D D I S H H -// D D I SSS HHHHH -// D D I S H H -// DDDD IIIII SSSS H H - // DISH support originally by Todd Treece // http://unionbridge.org/design/ircommand +// Supports: +// Brand: DISH NETWORK, Model: echostar 301 + // Constants // Ref: // https://github.com/marcosamarinho/IRremoteESP8266/blob/master/ir_Dish.cpp @@ -95,35 +92,17 @@ bool IRrecv::decodeDISH(decode_results *results, uint16_t nbits, bool strict) { uint64_t data = 0; uint16_t offset = kStartOffset; - // Header - if (!match(results->rawbuf[offset], kDishHdrMark)) return false; - // Calculate how long the common tick time is based on the header mark. - uint32_t m_tick = results->rawbuf[offset++] * kRawTick / kDishHdrMarkTicks; - if (!matchSpace(results->rawbuf[offset], kDishHdrSpace)) return false; - // Calculate how long the common tick time is based on the header space. - uint32_t s_tick = results->rawbuf[offset++] * kRawTick / kDishHdrSpaceTicks; - - // Data - match_result_t data_result = - matchData(&(results->rawbuf[offset]), nbits, kDishBitMarkTicks * m_tick, - kDishOneSpaceTicks * s_tick, kDishBitMarkTicks * m_tick, - kDishZeroSpaceTicks * s_tick); - if (data_result.success == false) return false; - data = data_result.data; - offset += data_result.used; - - // Footer - if (!matchMark(results->rawbuf[offset++], kDishBitMarkTicks * m_tick)) - return false; - - // Compliance - if (strict) { - // The DISH protocol calls for a repeated message, so strictly speaking - // there should be a code following this. Only require it if we are set to - // strict matching. - if (!matchSpace(results->rawbuf[offset], kDishRptSpaceTicks * s_tick)) - return false; - } + // Match Header + Data + Footer + if (!matchGeneric(results->rawbuf + offset, &data, + results->rawlen - offset, nbits, + kDishHdrMark, kDishHdrSpace, + kDishBitMark, kDishOneSpace, + kDishBitMark, kDishZeroSpace, + kDishBitMark, + // The DISH protocol calls for a repeated message, so + // strictly speaking there should be a code following this. + // Only require it if we are set to strict matching. + strict ? kDishRptSpace : 0, false)) return false; // Success results->decode_type = DISH; diff --git a/lib/IRremoteESP8266-2.6.5/src/ir_Electra.cpp b/lib/IRremoteESP8266-2.6.5/src/ir_Electra.cpp new file mode 100644 index 000000000..6b945aa3f --- /dev/null +++ b/lib/IRremoteESP8266-2.6.5/src/ir_Electra.cpp @@ -0,0 +1,336 @@ +// Copyright 2018, 2019 David Conran + +#include "ir_Electra.h" +#include +#include "IRrecv.h" +#include "IRsend.h" +#include "IRutils.h" + +// Electra A/C added by crankyoldgit +// + +// Ref: +// https://github.com/crankyoldgit/IRremoteESP8266/issues/527 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/642 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/778 +// https://github.com/ToniA/arduino-heatpumpir/blob/master/AUXHeatpumpIR.cpp + +// Constants +const uint16_t kElectraAcHdrMark = 9166; +const uint16_t kElectraAcBitMark = 646; +const uint16_t kElectraAcHdrSpace = 4470; +const uint16_t kElectraAcOneSpace = 1647; +const uint16_t kElectraAcZeroSpace = 547; +const uint32_t kElectraAcMessageGap = kDefaultMessageGap; // Just a guess. + +using irutils::addBoolToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addFanToString; +using irutils::addTempToString; + +#if SEND_ELECTRA_AC +// Send a Electra message +// +// Args: +// data: Contents of the message to be sent. (Guessing MSBF order) +// nbits: Nr. of bits of data to be sent. Typically kElectraAcBits. +// repeat: Nr. of additional times the message is to be sent. +// +// Status: Alpha / Needs testing against a real device. +// +void IRsend::sendElectraAC(const uint8_t data[], const uint16_t nbytes, + const uint16_t repeat) { + for (uint16_t r = 0; r <= repeat; r++) + sendGeneric(kElectraAcHdrMark, kElectraAcHdrSpace, kElectraAcBitMark, + kElectraAcOneSpace, kElectraAcBitMark, kElectraAcZeroSpace, + kElectraAcBitMark, kElectraAcMessageGap, data, nbytes, + 38000, // Complete guess of the modulation frequency. + false, // Send data in LSB order per byte + 0, 50); +} +#endif + + +IRElectraAc::IRElectraAc(const uint16_t pin, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { + this->stateReset(); +} + +void IRElectraAc::stateReset(void) { + for (uint8_t i = 1; i < kElectraAcStateLength - 2; i++) + remote_state[i] = 0; + remote_state[0] = 0xC3; + remote_state[11] = 0x08; + // [12] is the checksum. +} + +void IRElectraAc::begin(void) { _irsend.begin(); } + +uint8_t IRElectraAc::calcChecksum(const uint8_t state[], + const uint16_t length) { + if (length == 0) return state[0]; + return sumBytes(state, length - 1); +} + +bool IRElectraAc::validChecksum(const uint8_t state[], const uint16_t length) { + if (length < 2) + return true; // No checksum to compare with. Assume okay. + return (state[length - 1] == calcChecksum(state, length)); +} + +// Update the checksum for the internal state. +void IRElectraAc::checksum(uint16_t length) { + if (length < 2) return; + remote_state[length - 1] = calcChecksum(remote_state, length); +} + +#if SEND_ELECTRA_AC +void IRElectraAc::send(const uint16_t repeat) { + this->checksum(); + _irsend.sendElectraAC(remote_state, kElectraAcStateLength, repeat); +} +#endif // SEND_ELECTRA_AC + +uint8_t *IRElectraAc::getRaw(void) { + this->checksum(); + return remote_state; +} + +void IRElectraAc::setRaw(const uint8_t new_code[], const uint16_t length) { + for (uint8_t i = 0; i < length && i < kElectraAcStateLength; i++) + remote_state[i] = new_code[i]; +} + +void IRElectraAc::on(void) { this->setPower(true); } + +void IRElectraAc::off(void) { this->setPower(false); } + +void IRElectraAc::setPower(const bool on) { + if (on) + remote_state[9] |= kElectraAcPowerMask; + else + remote_state[9] &= ~kElectraAcPowerMask; +} + +bool IRElectraAc::getPower(void) { + return remote_state[9] & kElectraAcPowerMask; +} + +void IRElectraAc::setMode(const uint8_t mode) { + switch (mode) { + case kElectraAcAuto: + case kElectraAcDry: + case kElectraAcCool: + case kElectraAcHeat: + case kElectraAcFan: + remote_state[6] &= ~kElectraAcModeMask; + remote_state[6] |= (mode << 5); + break; + default: + // If we get an unexpected mode, default to AUTO. + this->setMode(kElectraAcAuto); + } +} + +uint8_t IRElectraAc::getMode(void) { + return (remote_state[6] & kElectraAcModeMask) >> 5; +} + +// Convert a standard A/C mode into its native mode. +uint8_t IRElectraAc::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kElectraAcCool; + case stdAc::opmode_t::kHeat: + return kElectraAcHeat; + case stdAc::opmode_t::kDry: + return kElectraAcDry; + case stdAc::opmode_t::kFan: + return kElectraAcFan; + default: + return kElectraAcAuto; + } +} + +// Convert a native mode to it's common equivalent. +stdAc::opmode_t IRElectraAc::toCommonMode(const uint8_t mode) { + switch (mode) { + case kElectraAcCool: return stdAc::opmode_t::kCool; + case kElectraAcHeat: return stdAc::opmode_t::kHeat; + case kElectraAcDry: return stdAc::opmode_t::kDry; + case kElectraAcFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +// Set the temp. in deg C +void IRElectraAc::setTemp(const uint8_t temp) { + uint8_t newtemp = std::max(kElectraAcMinTemp, temp); + newtemp = std::min(kElectraAcMaxTemp, newtemp); + remote_state[1] = (remote_state[1] & ~kElectraAcTempMask) | + ((newtemp - kElectraAcOffsetTemp) << 3); +} + +// Return the set temp. in deg C +uint8_t IRElectraAc::getTemp(void) { + return ((remote_state[1] & kElectraAcTempMask) >> 3) + kElectraAcOffsetTemp; +} + +// Set the speed of the fan, 0-3, 0 is auto, 1-3 is the speed +void IRElectraAc::setFan(const uint8_t speed) { + switch (speed) { + case kElectraAcFanAuto: + case kElectraAcFanHigh: + case kElectraAcFanMed: + case kElectraAcFanLow: + remote_state[4] &= ~kElectraAcFanMask; + remote_state[4] |= (speed << 5); + break; + default: + // If we get an unexpected speed, default to Auto. + this->setFan(kElectraAcFanAuto); + } +} + +uint8_t IRElectraAc::getFan(void) { + return (remote_state[4] & kElectraAcFanMask) >> 5; +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRElectraAc::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: + return kElectraAcFanLow; + case stdAc::fanspeed_t::kMedium: + return kElectraAcFanMed; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: + return kElectraAcFanHigh; + default: + return kElectraAcFanAuto; + } +} + +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRElectraAc::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kElectraAcFanHigh: return stdAc::fanspeed_t::kMax; + case kElectraAcFanMed: return stdAc::fanspeed_t::kMedium; + case kElectraAcFanLow: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +void IRElectraAc::setSwingV(const bool on) { + if (on) + remote_state[1] &= ~kElectraAcSwingVMask; + else + remote_state[1] |= kElectraAcSwingVMask; +} + +bool IRElectraAc::getSwingV(void) { + return !(remote_state[1] & kElectraAcSwingVMask); +} + +void IRElectraAc::setSwingH(const bool on) { + if (on) + remote_state[2] &= ~kElectraAcSwingHMask; + else + remote_state[2] |= kElectraAcSwingHMask; +} + +bool IRElectraAc::getSwingH(void) { + return !(remote_state[2] & kElectraAcSwingHMask); +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRElectraAc::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::ELECTRA_AC; + result.power = this->getPower(); + result.mode = this->toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(); + result.fanspeed = this->toCommonFanSpeed(this->getFan()); + result.swingv = this->getSwingV() ? stdAc::swingv_t::kAuto + : stdAc::swingv_t::kOff; + result.swingh = this->getSwingH() ? stdAc::swingh_t::kAuto + : stdAc::swingh_t::kOff; + // Not supported. + result.model = -1; // No models used. + result.quiet = false; + result.turbo = false; + result.econo = false; + result.clean = false; + result.light = false; + result.filter = false; + result.beep = false; + result.sleep = -1; + result.clock = -1; + return result; +} + +// Convert the internal state into a human readable string. +String IRElectraAc::toString(void) { + String result = ""; + result.reserve(80); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(getPower(), F("Power"), false); + result += addModeToString(getMode(), kElectraAcAuto, kElectraAcCool, + kElectraAcHeat, kElectraAcDry, kElectraAcFan); + result += addTempToString(getTemp()); + result += addFanToString(getFan(), kElectraAcFanHigh, kElectraAcFanLow, + kElectraAcFanAuto, kElectraAcFanAuto, + kElectraAcFanMed); + result += addBoolToString(getSwingV(), F("Swing(V)")); + result += addBoolToString(getSwingH(), F("Swing(H)")); + return result; +} + +#if DECODE_ELECTRA_AC +// Decode the supplied Electra A/C message. +// +// Args: +// results: Ptr to the data to decode and where to store the decode result. +// nbits: The number of data bits to expect. Typically kElectraAcBits. +// strict: Flag indicating if we should perform strict matching. +// Returns: +// boolean: True if it can decode it, false if it can't. +// +// Status: STABLE / Known working. +// +bool IRrecv::decodeElectraAC(decode_results *results, uint16_t nbits, + bool strict) { + if (strict) { + if (nbits != kElectraAcBits) + return false; // Not strictly a ELECTRA_AC message. + } + + uint16_t offset = kStartOffset; + // Match Header + Data + Footer + if (!matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, nbits, + kElectraAcHdrMark, kElectraAcHdrSpace, + kElectraAcBitMark, kElectraAcOneSpace, + kElectraAcBitMark, kElectraAcZeroSpace, + kElectraAcBitMark, kElectraAcMessageGap, true, + _tolerance, 0, false)) return false; + + // Compliance + if (strict) { + // Verify the checksum. + if (!IRElectraAc::validChecksum(results->state)) return false; + } + + // Success + results->decode_type = decode_type_t::ELECTRA_AC; + results->bits = nbits; + // No need to record the state as we stored it as we decoded it. + // As we use result->state, we don't record value, address, or command as it + // is a union data type. + return true; +} +#endif // DECODE_ELECTRA_AC diff --git a/lib/IRremoteESP8266-2.6.5/src/ir_Electra.h b/lib/IRremoteESP8266-2.6.5/src/ir_Electra.h new file mode 100644 index 000000000..c9c6f018e --- /dev/null +++ b/lib/IRremoteESP8266-2.6.5/src/ir_Electra.h @@ -0,0 +1,102 @@ +// Electra A/C +// +// Copyright 2019 David Conran + +#ifndef IR_ELECTRA_H_ +#define IR_ELECTRA_H_ + +#define __STDC_LIMIT_MACROS +#include +#ifndef UNIT_TEST +#include +#endif +#include "IRremoteESP8266.h" +#include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif + +// Supports: +// Brand: AUX, Model: KFR-35GW/BpNFW=3 A/C +// Brand: AUX, Model: YKR-T/011 remote + +// Ref: +// https://github.com/ToniA/arduino-heatpumpir/blob/master/AUXHeatpumpIR.cpp + +// Constants +// state[1] +const uint8_t kElectraAcTempMask = 0b11111000; +const uint8_t kElectraAcMinTemp = 16; // 16C +const uint8_t kElectraAcMaxTemp = 32; // 32C +const uint8_t kElectraAcOffsetTemp = 8; +const uint8_t kElectraAcSwingVMask = 0b00000111; +// state[2] +const uint8_t kElectraAcSwingHMask = 0b11100000; +// state[4] +const uint8_t kElectraAcFanMask = 0b11100000; +const uint8_t kElectraAcFanAuto = 0b101; +const uint8_t kElectraAcFanLow = 0b011; +const uint8_t kElectraAcFanMed = 0b010; +const uint8_t kElectraAcFanHigh = 0b001; +// state[6] +const uint8_t kElectraAcModeMask = 0b11100000; +const uint8_t kElectraAcAuto = 0b000; +const uint8_t kElectraAcCool = 0b001; +const uint8_t kElectraAcDry = 0b010; +const uint8_t kElectraAcHeat = 0b100; +const uint8_t kElectraAcFan = 0b110; +// state[9] +const uint8_t kElectraAcPowerMask = 0b00100000; + + +// Classes +class IRElectraAc { + public: + explicit IRElectraAc(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); + + void stateReset(void); +#if SEND_ELECTRA_AC + void send(const uint16_t repeat = kElectraAcMinRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } +#endif // SEND_ELECTRA_AC + void begin(void); + void on(void); + void off(void); + void setPower(const bool on); + bool getPower(void); + void setMode(const uint8_t mode); + uint8_t getMode(void); + void setTemp(const uint8_t temp); + uint8_t getTemp(void); + void setFan(const uint8_t speed); + uint8_t getFan(void); + void setSwingV(const bool on); + bool getSwingV(void); + void setSwingH(const bool on); + bool getSwingH(void); + uint8_t* getRaw(void); + void setRaw(const uint8_t new_code[], + const uint16_t length = kElectraAcStateLength); + static bool validChecksum(const uint8_t state[], + const uint16_t length = kElectraAcStateLength); + static uint8_t calcChecksum(const uint8_t state[], + const uint16_t length = kElectraAcStateLength); + String toString(void); + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + stdAc::state_t toCommon(void); +#ifndef UNIT_TEST + + private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif + // The state of the IR remote in IR code form. + uint8_t remote_state[kElectraAcStateLength]; + void checksum(const uint16_t length = kElectraAcStateLength); +}; +#endif // IR_ELECTRA_H_ diff --git a/lib/IRremoteESP8266-2.6.5/src/ir_Fujitsu.cpp b/lib/IRremoteESP8266-2.6.5/src/ir_Fujitsu.cpp new file mode 100644 index 000000000..fa6a0ce8c --- /dev/null +++ b/lib/IRremoteESP8266-2.6.5/src/ir_Fujitsu.cpp @@ -0,0 +1,730 @@ +// Copyright 2017 Jonny Graham +// Copyright 2017-2019 David Conran +#include "ir_Fujitsu.h" +#include +#ifndef ARDUINO +#include +#endif +#include "IRsend.h" +#include "IRutils.h" + +// Fujitsu A/C support added by Jonny Graham & David Conran + +// Equipment it seems compatible with: +// * Fujitsu ASYG30LFCA with remote AR-RAH2E +// * Fujitsu AST9RSGCW with remote AR-DB1 +// * Fujitsu ASYG7LMCA with remote AR-REB1E +// * Fujitsu AR-RAE1E remote. +// * Fujitsu General with remote AR-JW2 +// * + +// Ref: +// These values are based on averages of measurements +const uint16_t kFujitsuAcHdrMark = 3324; +const uint16_t kFujitsuAcHdrSpace = 1574; +const uint16_t kFujitsuAcBitMark = 448; +const uint16_t kFujitsuAcOneSpace = 1182; +const uint16_t kFujitsuAcZeroSpace = 390; +const uint16_t kFujitsuAcMinGap = 8100; + +using irutils::addBoolToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addFanToString; +using irutils::addTempToString; + +#if SEND_FUJITSU_AC +// Send a Fujitsu A/C message. +// +// Args: +// data: An array of bytes containing the IR command. +// nbytes: Nr. of bytes of data in the array. Typically one of: +// kFujitsuAcStateLength +// kFujitsuAcStateLength - 1 +// kFujitsuAcStateLengthShort +// kFujitsuAcStateLengthShort - 1 +// repeat: Nr. of times the message is to be repeated. +// (Default = kFujitsuAcMinRepeat). +// +// Status: STABLE / Known Good. +// +void IRsend::sendFujitsuAC(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + sendGeneric(kFujitsuAcHdrMark, kFujitsuAcHdrSpace, kFujitsuAcBitMark, + kFujitsuAcOneSpace, kFujitsuAcBitMark, kFujitsuAcZeroSpace, + kFujitsuAcBitMark, kFujitsuAcMinGap, data, nbytes, 38, false, + repeat, 50); +} +#endif // SEND_FUJITSU_AC + +// Code to emulate Fujitsu A/C IR remote control unit. + +// Initialise the object. +IRFujitsuAC::IRFujitsuAC(const uint16_t pin, + const fujitsu_ac_remote_model_t model, + const bool inverted, const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { + this->setModel(model); + this->stateReset(); +} + +void IRFujitsuAC::setModel(const fujitsu_ac_remote_model_t model) { + _model = model; + switch (model) { + case ARDB1: + case ARJW2: + _state_length = kFujitsuAcStateLength - 1; + _state_length_short = kFujitsuAcStateLengthShort - 1; + break; + case ARRAH2E: + case ARREB1E: + default: + _state_length = kFujitsuAcStateLength; + _state_length_short = kFujitsuAcStateLengthShort; + } +} + +fujitsu_ac_remote_model_t IRFujitsuAC::getModel(void) { return _model; } + +// Reset the state of the remote to a known good state/sequence. +void IRFujitsuAC::stateReset(void) { + _temp = 24; + _fanSpeed = kFujitsuAcFanHigh; + _mode = kFujitsuAcModeCool; + _swingMode = kFujitsuAcSwingBoth; + _cmd = kFujitsuAcCmdTurnOn; + this->buildState(); +} + +// Configure the pin for output. +void IRFujitsuAC::begin(void) { _irsend.begin(); } + +#if SEND_FUJITSU_AC +// Send the current desired state to the IR LED. +void IRFujitsuAC::send(const uint16_t repeat) { + this->buildState(); + _irsend.sendFujitsuAC(remote_state, getStateLength(), repeat); +} +#endif // SEND_FUJITSU_AC + +void IRFujitsuAC::buildState(void) { + remote_state[0] = 0x14; + remote_state[1] = 0x63; + remote_state[2] = 0x00; + remote_state[3] = 0x10; + remote_state[4] = 0x10; + bool fullCmd = false; + switch (_cmd) { + case kFujitsuAcCmdTurnOff: // 0x02 + case kFujitsuAcCmdEcono: // 0x09 + case kFujitsuAcCmdPowerful: // 0x39 + case kFujitsuAcCmdStepVert: // 0x6C + case kFujitsuAcCmdToggleSwingVert: // 0x6D + case kFujitsuAcCmdStepHoriz: // 0x79 + case kFujitsuAcCmdToggleSwingHoriz: // 0x7A + remote_state[5] = _cmd; + break; + default: + switch (_model) { + case ARRAH2E: + case ARREB1E: + remote_state[5] = 0xFE; + break; + case ARDB1: + case ARJW2: + remote_state[5] = 0xFC; + break; + } + fullCmd = true; + break; + } + if (fullCmd) { // long codes + uint8_t tempByte = _temp - kFujitsuAcMinTemp; + // Nr. of bytes in the message after this byte. + remote_state[6] = _state_length - 7; + + remote_state[7] = 0x30; + remote_state[8] = (_cmd == kFujitsuAcCmdTurnOn) | (tempByte << 4); + remote_state[9] = _mode | 0 << 4; // timer off + remote_state[10] = _fanSpeed; + remote_state[11] = 0; // timerOff values + remote_state[12] = 0; // timerOff/On values + remote_state[13] = 0; // timerOn values + remote_state[14] = 0; + uint8_t checksum = 0; + uint8_t checksum_complement = 0; + switch (_model) { + case ARDB1: + case ARJW2: + checksum = sumBytes(remote_state, _state_length - 1); + checksum_complement = 0x9B; + break; + case ARREB1E: + remote_state[14] |= (_outsideQuiet << 7); + // FALL THRU + case ARRAH2E: + remote_state[14] |= 0x20; + remote_state[10] |= _swingMode << 4; + // FALL THRU + default: + checksum = sumBytes(remote_state + _state_length_short, + _state_length - _state_length_short - 1); + } + // and negate the checksum and store it in the last byte. + remote_state[_state_length - 1] = checksum_complement - checksum; + } else { // short codes + switch (_model) { + case ARRAH2E: + case ARREB1E: + // The last byte is the inverse of penultimate byte + remote_state[_state_length_short - 1] = + ~remote_state[_state_length_short - 2]; + break; + default: + {}; // We don't need to do anything for the others. + } + // Zero the rest of the state. + for (uint8_t i = _state_length_short; i < kFujitsuAcStateLength; i++) + remote_state[i] = 0; + } +} + +uint8_t IRFujitsuAC::getStateLength(void) { + this->buildState(); // Force an update of the internal state. + if (((_model == ARRAH2E || _model == ARREB1E) && remote_state[5] != 0xFE) || + ((_model == ARDB1 || _model == ARJW2) && remote_state[5] != 0xFC)) + return _state_length_short; + else + return _state_length; +} + +// Return a pointer to the internal state date of the remote. +uint8_t* IRFujitsuAC::getRaw(void) { + this->buildState(); + return remote_state; +} + +void IRFujitsuAC::buildFromState(const uint16_t length) { + switch (length) { + case kFujitsuAcStateLength - 1: + case kFujitsuAcStateLengthShort - 1: + this->setModel(ARDB1); + // ARJW2 has horizontal swing. + if (this->getSwing(true) > kFujitsuAcSwingVert) this->setModel(ARJW2); + break; + default: + switch (this->getCmd(true)) { + case kFujitsuAcCmdEcono: + case kFujitsuAcCmdPowerful: + this->setModel(fujitsu_ac_remote_model_t::ARREB1E); + break; + default: + this->setModel(fujitsu_ac_remote_model_t::ARRAH2E); + } + } + switch (remote_state[6]) { + case 8: + if (this->getModel() != fujitsu_ac_remote_model_t::ARJW2) + this->setModel(ARDB1); + break; + case 9: + if (this->getModel() != fujitsu_ac_remote_model_t::ARREB1E) + this->setModel(ARRAH2E); + break; + } + setTemp((remote_state[8] >> 4) + kFujitsuAcMinTemp); + if (remote_state[8] & 0x1) + setCmd(kFujitsuAcCmdTurnOn); + else + setCmd(kFujitsuAcCmdStayOn); + setMode(remote_state[9] & 0b111); + setFanSpeed(remote_state[10] & 0b111); + setSwing(remote_state[10] >> 4); + switch (remote_state[5]) { + case kFujitsuAcCmdTurnOff: + case kFujitsuAcCmdStepHoriz: + case kFujitsuAcCmdToggleSwingHoriz: + case kFujitsuAcCmdStepVert: + case kFujitsuAcCmdToggleSwingVert: + case kFujitsuAcCmdEcono: + case kFujitsuAcCmdPowerful: + setCmd(remote_state[5]); + break; + } + _outsideQuiet = this->getOutsideQuiet(true); +} + +bool IRFujitsuAC::setRaw(const uint8_t newState[], const uint16_t length) { + if (length > kFujitsuAcStateLength) return false; + for (uint16_t i = 0; i < kFujitsuAcStateLength; i++) { + if (i < length) + remote_state[i] = newState[i]; + else + remote_state[i] = 0; + } + buildFromState(length); + return true; +} + +void IRFujitsuAC::stepHoriz(void) { this->setCmd(kFujitsuAcCmdStepHoriz); } + +void IRFujitsuAC::toggleSwingHoriz(const bool update) { + // Toggle the current setting. + if (update) this->setSwing(this->getSwing() ^ kFujitsuAcSwingHoriz); + // and set the appropriate special command. + this->setCmd(kFujitsuAcCmdToggleSwingHoriz); +} + +void IRFujitsuAC::stepVert(void) { this->setCmd(kFujitsuAcCmdStepVert); } + +void IRFujitsuAC::toggleSwingVert(const bool update) { + // Toggle the current setting. + if (update) this->setSwing(this->getSwing() ^ kFujitsuAcSwingVert); + // and set the appropriate special command. + this->setCmd(kFujitsuAcCmdToggleSwingVert); +} + +// Set the requested command of the A/C. +void IRFujitsuAC::setCmd(const uint8_t cmd) { + switch (cmd) { + case kFujitsuAcCmdTurnOff: + case kFujitsuAcCmdTurnOn: + case kFujitsuAcCmdStayOn: + case kFujitsuAcCmdStepVert: + case kFujitsuAcCmdToggleSwingVert: + _cmd = cmd; + break; + case kFujitsuAcCmdStepHoriz: + case kFujitsuAcCmdToggleSwingHoriz: + switch (_model) { + // Only these remotes have step horizontal. + case ARRAH2E: + case ARJW2: + _cmd = cmd; + break; + default: + _cmd = kFujitsuAcCmdStayOn; + } + break; + case kFujitsuAcCmdEcono: + case kFujitsuAcCmdPowerful: + switch (_model) { + // Only these remotes have these commands. + case ARREB1E: + _cmd = cmd; + break; + default: + _cmd = kFujitsuAcCmdStayOn; + } + break; + default: + _cmd = kFujitsuAcCmdStayOn; + } +} + +// Get the special command part of the message. +// Args: +// raw: Do we need to get it from first principles from the raw data? +// Returns: +// A uint8_t containing the contents of the special command byte. +uint8_t IRFujitsuAC::getCmd(const bool raw) { + if (raw) return remote_state[5]; + return _cmd; +} + +// Set the requested power state of the A/C. +void IRFujitsuAC::setPower(const bool on) { + this->setCmd(on ? kFujitsuAcCmdTurnOn : kFujitsuAcCmdTurnOff); +} + +// Set the requested power state of the A/C to off. +void IRFujitsuAC::off(void) { this->setPower(false); } + +// Set the requested power state of the A/C to on. +void IRFujitsuAC::on(void) { this->setPower(true); } + +bool IRFujitsuAC::getPower(void) { return _cmd != kFujitsuAcCmdTurnOff; } + +void IRFujitsuAC::setOutsideQuiet(const bool on) { + _outsideQuiet = on; + this->setCmd(kFujitsuAcCmdStayOn); // No special command involved. +} + +// Get the status of the Outside Quiet setting. +// Args: +// raw: Do we get the result from base data? +// Returns: +// A boolean for if it is set or not. +bool IRFujitsuAC::getOutsideQuiet(const bool raw) { + if (_state_length == kFujitsuAcStateLength && raw) { + _outsideQuiet = remote_state[14] & 0b10000000; + // Only ARREB1E seems to have this mode. + if (_outsideQuiet) this->setModel(fujitsu_ac_remote_model_t::ARREB1E); + } + return _outsideQuiet; +} + +// Set the temp. in deg C +void IRFujitsuAC::setTemp(const uint8_t temp) { + _temp = std::max((uint8_t)kFujitsuAcMinTemp, temp); + _temp = std::min((uint8_t)kFujitsuAcMaxTemp, _temp); + this->setCmd(kFujitsuAcCmdStayOn); // No special command involved. +} + +uint8_t IRFujitsuAC::getTemp(void) { return _temp; } + +// Set the speed of the fan +void IRFujitsuAC::setFanSpeed(const uint8_t fanSpeed) { + if (fanSpeed > kFujitsuAcFanQuiet) + _fanSpeed = kFujitsuAcFanHigh; // Set the fan to maximum if out of range. + else + _fanSpeed = fanSpeed; + this->setCmd(kFujitsuAcCmdStayOn); // No special command involved. +} +uint8_t IRFujitsuAC::getFanSpeed(void) { return _fanSpeed; } + +// Set the requested climate operation mode of the a/c unit. +void IRFujitsuAC::setMode(const uint8_t mode) { + if (mode > kFujitsuAcModeHeat) + _mode = kFujitsuAcModeHeat; // Set the mode to maximum if out of range. + else + _mode = mode; + this->setCmd(kFujitsuAcCmdStayOn); // No special command involved. +} + +uint8_t IRFujitsuAC::getMode(void) { return _mode; } + +// Set the requested swing operation mode of the a/c unit. +void IRFujitsuAC::setSwing(const uint8_t swingMode) { + _swingMode = swingMode; + switch (_model) { + // No Horizontal support. + case ARDB1: + case ARREB1E: + // Set the mode to max if out of range + if (swingMode > kFujitsuAcSwingVert) _swingMode = kFujitsuAcSwingVert; + break; + // Has Horizontal support. + case ARRAH2E: + case ARJW2: + default: + // Set the mode to max if out of range + if (swingMode > kFujitsuAcSwingBoth) _swingMode = kFujitsuAcSwingBoth; + } + this->setCmd(kFujitsuAcCmdStayOn); // No special command involved. +} + +// Get what the swing part of the message should be. +// Args: +// raw: Do we need to get it from first principles from the raw data? +// Returns: +// A uint8_t containing the contents of the swing state. +uint8_t IRFujitsuAC::getSwing(const bool raw) { + if (raw) _swingMode = remote_state[10] >> 4; + return _swingMode; +} + +bool IRFujitsuAC::validChecksum(uint8_t state[], const uint16_t length) { + uint8_t sum = 0; + uint8_t sum_complement = 0; + uint8_t checksum = state[length - 1]; + switch (length) { + case kFujitsuAcStateLengthShort: // ARRAH2E & ARREB1E + return state[length - 1] == (uint8_t)~state[length - 2]; + case kFujitsuAcStateLength - 1: // ARDB1 & ARJW2 + sum = sumBytes(state, length - 1); + sum_complement = 0x9B; + break; + case kFujitsuAcStateLength: // ARRAH2E & ARREB1E + sum = sumBytes(state + kFujitsuAcStateLengthShort, + length - 1 - kFujitsuAcStateLengthShort); + break; + default: // Includes ARDB1 & ARJW2 short. + return true; // Assume the checksum is valid for other lengths. + } + return checksum == (uint8_t)(sum_complement - sum); // Does it match? +} + +// Convert a standard A/C mode into its native mode. +uint8_t IRFujitsuAC::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kFujitsuAcModeCool; + case stdAc::opmode_t::kHeat: + return kFujitsuAcModeHeat; + case stdAc::opmode_t::kDry: + return kFujitsuAcModeDry; + case stdAc::opmode_t::kFan: + return kFujitsuAcModeFan; + default: + return kFujitsuAcModeAuto; + } +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRFujitsuAC::convertFan(stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + return kFujitsuAcFanQuiet; + case stdAc::fanspeed_t::kLow: + return kFujitsuAcFanLow; + case stdAc::fanspeed_t::kMedium: + return kFujitsuAcFanMed; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: + return kFujitsuAcFanHigh; + default: + return kFujitsuAcFanAuto; + } +} + +// Convert a native mode to it's common equivalent. +stdAc::opmode_t IRFujitsuAC::toCommonMode(const uint8_t mode) { + switch (mode) { + case kFujitsuAcModeCool: return stdAc::opmode_t::kCool; + case kFujitsuAcModeHeat: return stdAc::opmode_t::kHeat; + case kFujitsuAcModeDry: return stdAc::opmode_t::kDry; + case kFujitsuAcModeFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRFujitsuAC::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kFujitsuAcFanHigh: return stdAc::fanspeed_t::kMax; + case kFujitsuAcFanMed: return stdAc::fanspeed_t::kMedium; + case kFujitsuAcFanLow: return stdAc::fanspeed_t::kLow; + case kFujitsuAcFanQuiet: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRFujitsuAC::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::FUJITSU_AC; + result.model = this->getModel(); + result.power = this->getPower(); + result.mode = this->toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(); + result.fanspeed = this->toCommonFanSpeed(this->getFanSpeed()); + uint8_t swing = this->getSwing(); + switch (result.model) { + case fujitsu_ac_remote_model_t::ARREB1E: + case fujitsu_ac_remote_model_t::ARRAH2E: + result.swingv = (swing & kFujitsuAcSwingVert) ? stdAc::swingv_t::kAuto : + stdAc::swingv_t::kOff; + result.swingh = (swing & kFujitsuAcSwingHoriz) ? stdAc::swingh_t::kAuto : + stdAc::swingh_t::kOff; + break; + case fujitsu_ac_remote_model_t::ARDB1: + case fujitsu_ac_remote_model_t::ARJW2: + default: + result.swingv = stdAc::swingv_t::kOff; + result.swingh = stdAc::swingh_t::kOff; + } + + result.quiet = (this->getFanSpeed() == kFujitsuAcFanQuiet); + result.turbo = this->getCmd() == kFujitsuAcCmdPowerful; + result.econo = this->getCmd() == kFujitsuAcCmdEcono; + // Not supported. + result.light = false; + result.filter = false; + result.clean = false; + result.beep = false; + result.sleep = -1; + result.clock = -1; + return result; +} + +// Convert the internal state into a human readable string. +String IRFujitsuAC::toString(void) { + String result = ""; + result.reserve(100); // Reserve some heap for the string to reduce fragging. + fujitsu_ac_remote_model_t model = this->getModel(); + result += addIntToString(model, F("Model"), false); + switch (model) { + case fujitsu_ac_remote_model_t::ARRAH2E: result += F(" (ARRAH2E)"); break; + case fujitsu_ac_remote_model_t::ARDB1: result += F(" (ARDB1)"); break; + case fujitsu_ac_remote_model_t::ARREB1E: result += F(" (ARREB1E)"); break; + case fujitsu_ac_remote_model_t::ARJW2: result += F(" (ARJW2)"); break; + default: result += F(" (UNKNOWN)"); + } + result += addBoolToString(getPower(), F("Power")); + result += addModeToString(getMode(), kFujitsuAcModeAuto, kFujitsuAcModeCool, + kFujitsuAcModeHeat, kFujitsuAcModeDry, + kFujitsuAcModeFan); + result += addTempToString(getTemp()); + result += addFanToString(getFanSpeed(), kFujitsuAcFanHigh, kFujitsuAcFanLow, + kFujitsuAcFanAuto, kFujitsuAcFanQuiet, + kFujitsuAcFanMed); + switch (model) { + // These models have no internal swing state. + case fujitsu_ac_remote_model_t::ARDB1: + case fujitsu_ac_remote_model_t::ARJW2: + break; + default: // Assume everything else does. + result += F(", Swing: "); + switch (this->getSwing()) { + case kFujitsuAcSwingOff: + result += F("Off"); + break; + case kFujitsuAcSwingVert: + result += F("Vert"); + break; + case kFujitsuAcSwingHoriz: + result += F("Horiz"); + break; + case kFujitsuAcSwingBoth: + result += F("Vert + Horiz"); + break; + default: + result += F("UNKNOWN"); + } + } + result += F(", Command: "); + switch (this->getCmd()) { + case kFujitsuAcCmdStepHoriz: + result += F("Step vane horizontally"); + break; + case kFujitsuAcCmdStepVert: + result += F("Step vane vertically"); + break; + case kFujitsuAcCmdToggleSwingHoriz: + result += F("Toggle horizontal swing"); + break; + case kFujitsuAcCmdToggleSwingVert: + result += F("Toggle vertically swing"); + break; + case kFujitsuAcCmdEcono: + result += F("Economy"); + break; + case kFujitsuAcCmdPowerful: + result += F("Powerful"); + break; + default: + result += F("N/A"); + } + if (this->getModel() == fujitsu_ac_remote_model_t::ARREB1E) + result += addBoolToString(getOutsideQuiet(), F("Outside Quiet")); + return result; +} + +#if DECODE_FUJITSU_AC +// Decode a Fujitsu AC IR message if possible. +// Places successful decode information in the results pointer. +// Args: +// results: Ptr to the data to decode and where to store the decode result. +// nbits: The number of data bits to expect. Typically kFujitsuAcBits. +// strict: Flag to indicate if we strictly adhere to the specification. +// Returns: +// boolean: True if it can decode it, false if it can't. +// +// Status: ALPHA / Untested. +// +// Ref: +// +bool IRrecv::decodeFujitsuAC(decode_results* results, uint16_t nbits, + bool strict) { + uint16_t offset = kStartOffset; + uint16_t dataBitsSoFar = 0; + + // Have we got enough data to successfully decode? + if (results->rawlen < (2 * kFujitsuAcMinBits) + kHeader + kFooter - 1) + return false; // Can't possibly be a valid message. + + // Compliance + if (strict) { + switch (nbits) { + case kFujitsuAcBits: + case kFujitsuAcBits - 8: + case kFujitsuAcMinBits: + case kFujitsuAcMinBits + 8: + break; + default: + return false; // Must be called with the correct nr. of bits. + } + } + + // Header + if (!matchMark(results->rawbuf[offset++], kFujitsuAcHdrMark)) return false; + if (!matchSpace(results->rawbuf[offset++], kFujitsuAcHdrSpace)) return false; + + // Data (Fixed signature) + match_result_t data_result = + matchData(&(results->rawbuf[offset]), kFujitsuAcMinBits - 8, + kFujitsuAcBitMark, kFujitsuAcOneSpace, kFujitsuAcBitMark, + kFujitsuAcZeroSpace, _tolerance, kMarkExcess, false); + if (data_result.success == false) return false; // Fail + if (data_result.data != 0x1010006314) return false; // Signature failed. + dataBitsSoFar += kFujitsuAcMinBits - 8; + offset += data_result.used; + results->state[0] = 0x14; + results->state[1] = 0x63; + results->state[2] = 0x00; + results->state[3] = 0x10; + results->state[4] = 0x10; + + // Keep reading bytes until we either run out of message or state to fill. + for (uint16_t i = 5; + offset <= results->rawlen - 16 && i < kFujitsuAcStateLength; + i++, dataBitsSoFar += 8, offset += data_result.used) { + data_result = matchData( + &(results->rawbuf[offset]), 8, kFujitsuAcBitMark, kFujitsuAcOneSpace, + kFujitsuAcBitMark, kFujitsuAcZeroSpace, _tolerance, kMarkExcess, false); + if (data_result.success == false) break; // Fail + results->state[i] = data_result.data; + } + + // Footer + if (offset > results->rawlen || + !matchMark(results->rawbuf[offset++], kFujitsuAcBitMark)) + return false; + // The space is optional if we are out of capture. + if (offset < results->rawlen && + !matchAtLeast(results->rawbuf[offset], kFujitsuAcMinGap)) + return false; + + // Compliance + if (strict) { + if (dataBitsSoFar != nbits) return false; + } + + results->decode_type = FUJITSU_AC; + results->bits = dataBitsSoFar; + + // Compliance + switch (dataBitsSoFar) { + case kFujitsuAcMinBits: + // Check if this values indicate that this should have been a long state + // message. + if (results->state[5] == 0xFC) return false; + return true; // Success + case kFujitsuAcMinBits + 8: + // Check if this values indicate that this should have been a long state + // message. + if (results->state[5] == 0xFE) return false; + // The last byte needs to be the inverse of the penultimate byte. + if (results->state[5] != (uint8_t)~results->state[6]) return false; + return true; // Success + case kFujitsuAcBits - 8: + // Long messages of this size require this byte be correct. + if (results->state[5] != 0xFC) return false; + break; + case kFujitsuAcBits: + // Long messages of this size require this byte be correct. + if (results->state[5] != 0xFE) return false; + break; + default: + return false; // Unexpected size. + } + if (!IRFujitsuAC::validChecksum(results->state, dataBitsSoFar / 8)) + return false; + + // Success + return true; // All good. +} +#endif // DECODE_FUJITSU_AC diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Fujitsu.h b/lib/IRremoteESP8266-2.6.5/src/ir_Fujitsu.h similarity index 51% rename from lib/IRremoteESP8266-2.6.0/src/ir_Fujitsu.h rename to lib/IRremoteESP8266-2.6.5/src/ir_Fujitsu.h index 78a4f8951..e953f9058 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Fujitsu.h +++ b/lib/IRremoteESP8266-2.6.5/src/ir_Fujitsu.h @@ -1,5 +1,16 @@ // Copyright 2017 Jonny Graham -// Copyright 2018 David Conran +// Copyright 2018-2019 David Conran + +// Supports: +// Brand: Fujitsu, Model: AR-RAH2E remote +// Brand: Fujitsu, Model: ASYG30LFCA A/C +// Brand: Fujitsu, Model: AR-DB1 remote +// Brand: Fujitsu, Model: AST9RSGCW A/C +// Brand: Fujitsu, Model: AR-REB1E remote +// Brand: Fujitsu, Model: ASYG7LMCA A/C +// Brand: Fujitsu, Model: AR-RAE1E remote +// Brand: Fujitsu General, Model: AR-JW2 remote + #ifndef IR_FUJITSU_H_ #define IR_FUJITSU_H_ @@ -7,8 +18,6 @@ #include #ifdef ARDUINO #include -#else -#include #endif #include "IRrecv.h" #include "IRremoteESP8266.h" @@ -26,11 +35,15 @@ const uint8_t kFujitsuAcModeDry = 0x02; const uint8_t kFujitsuAcModeFan = 0x03; const uint8_t kFujitsuAcModeHeat = 0x04; -const uint8_t kFujitsuAcCmdStayOn = 0x00; -const uint8_t kFujitsuAcCmdTurnOn = 0x01; -const uint8_t kFujitsuAcCmdTurnOff = 0x02; -const uint8_t kFujitsuAcCmdStepHoriz = 0x79; -const uint8_t kFujitsuAcCmdStepVert = 0x6C; +const uint8_t kFujitsuAcCmdStayOn = 0x00; // b00000000 +const uint8_t kFujitsuAcCmdTurnOn = 0x01; // b00000001 +const uint8_t kFujitsuAcCmdTurnOff = 0x02; // b00000010 +const uint8_t kFujitsuAcCmdEcono = 0x09; // b00001001 +const uint8_t kFujitsuAcCmdPowerful = 0x39; // b00111001 +const uint8_t kFujitsuAcCmdStepVert = 0x6C; // b01101100 +const uint8_t kFujitsuAcCmdToggleSwingVert = 0x6D; // b01101101 +const uint8_t kFujitsuAcCmdStepHoriz = 0x79; // b01111001 +const uint8_t kFujitsuAcCmdToggleSwingHoriz = 0x7A; // b01111010 const uint8_t kFujitsuAcFanAuto = 0x00; const uint8_t kFujitsuAcFanHigh = 0x01; @@ -70,45 +83,59 @@ const uint8_t kFujitsuAcSwingBoth = 0x03; #define FUJITSU_AC_SWING_BOTH kFujitsuAcSwingBoth enum fujitsu_ac_remote_model_t { - ARRAH2E = 1, - ARDB1, + ARRAH2E = 1, // (1) AR-RAH2E, AR-RAE1E (Default) + ARDB1, // (2) AR-DB1 + ARREB1E, // (3) AR-REB1E + ARJW2, // (4) AR-JW2 (Same as ARDB1 but with horiz control) }; class IRFujitsuAC { public: - explicit IRFujitsuAC(uint16_t pin, fujitsu_ac_remote_model_t model = ARRAH2E); + explicit IRFujitsuAC(const uint16_t pin, + const fujitsu_ac_remote_model_t model = ARRAH2E, + const bool inverted = false, + const bool use_modulation = true); - void setModel(fujitsu_ac_remote_model_t model); - void stateReset(); + void setModel(const fujitsu_ac_remote_model_t model); + fujitsu_ac_remote_model_t getModel(void); + void stateReset(void); #if SEND_FUJITSU_AC void send(const uint16_t repeat = kFujitsuAcMinRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } #endif // SEND_FUJITSU_AC - void begin(); - void off(); - void stepHoriz(); - void stepVert(); - void setCmd(uint8_t cmd); - uint8_t getCmd(); - void setTemp(uint8_t temp); - uint8_t getTemp(); - void setFanSpeed(uint8_t fan); - uint8_t getFanSpeed(); - void setMode(uint8_t mode); - uint8_t getMode(); - void setSwing(uint8_t mode); - uint8_t getSwing(); - uint8_t* getRaw(); + void begin(void); + void stepHoriz(void); + void toggleSwingHoriz(const bool update = true); + void stepVert(void); + void toggleSwingVert(const bool update = true); + void setCmd(const uint8_t cmd); + uint8_t getCmd(const bool raw = false); + void setTemp(const uint8_t temp); + uint8_t getTemp(void); + void setFanSpeed(const uint8_t fan); + uint8_t getFanSpeed(void); + void setMode(const uint8_t mode); + uint8_t getMode(void); + void setSwing(const uint8_t mode); + uint8_t getSwing(const bool raw = false); + uint8_t* getRaw(void); bool setRaw(const uint8_t newState[], const uint16_t length); - uint8_t getStateLength(); - static bool validChecksum(uint8_t* state, uint16_t length); - bool getPower(); + uint8_t getStateLength(void); + static bool validChecksum(uint8_t* state, const uint16_t length); + void setPower(const bool on); + void off(void); + void on(void); + bool getPower(void); + void setOutsideQuiet(const bool on); + + bool getOutsideQuiet(const bool raw = false); + uint8_t convertMode(const stdAc::opmode_t mode); uint8_t convertFan(stdAc::fanspeed_t speed); -#ifdef ARDUINO - String toString(); -#else - std::string toString(); -#endif + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + stdAc::state_t toCommon(void); + String toString(void); #ifndef UNIT_TEST private: @@ -125,7 +152,8 @@ class IRFujitsuAC { fujitsu_ac_remote_model_t _model; uint8_t _state_length; uint8_t _state_length_short; - void buildState(); + bool _outsideQuiet; + void buildState(void); void buildFromState(const uint16_t length); }; diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_GICable.cpp b/lib/IRremoteESP8266-2.6.5/src/ir_GICable.cpp similarity index 70% rename from lib/IRremoteESP8266-2.6.0/src/ir_GICable.cpp rename to lib/IRremoteESP8266-2.6.5/src/ir_GICable.cpp index 229e4e5bb..6b3849b4c 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_GICable.cpp +++ b/lib/IRremoteESP8266-2.6.5/src/ir_GICable.cpp @@ -1,4 +1,5 @@ // Copyright 2018 David Conran +// G.I. Cable #define __STDC_LIMIT_MACROS #include @@ -7,15 +8,9 @@ #include "IRsend.h" #include "IRutils.h" -// GGGG IIIII CCCCC AAA BBBBB LL EEEEEEE -// GG GG III CC C AAAAA BB B LL EE -// GG III CC AA AA BBBBBB LL EEEEE -// GG GG ... III ... CC C AAAAAAA BB BB LL EE -// GGGGGG ... IIIII ... CCCCC AA AA BBBBBB LLLLLLL EEEEEEE -// // Ref: // https://github.com/cyborg5/IRLib2/blob/master/IRLibProtocols/IRLib_P09_GICable.h -// https://github.com/markszabo/IRremoteESP8266/issues/447 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/447 // Constants const uint16_t kGicableHdrMark = 9000; @@ -71,32 +66,21 @@ void IRsend::sendGICable(uint64_t data, uint16_t nbits, uint16_t repeat) { // Status: Alpha / Not tested against a real device. bool IRrecv::decodeGICable(decode_results *results, uint16_t nbits, bool strict) { - if (results->rawlen < 2 * (nbits + kHeader + kFooter) - 1) - return false; // Can't possibly be a valid GICABLE message. if (strict && nbits != kGicableBits) return false; // Not strictly an GICABLE message. uint64_t data = 0; uint16_t offset = kStartOffset; - - // Header - if (!matchMark(results->rawbuf[offset++], kGicableHdrMark)) return false; - if (!matchSpace(results->rawbuf[offset++], kGicableHdrSpace)) return false; - - // Data - match_result_t data_result = - matchData(&(results->rawbuf[offset]), nbits, kGicableBitMark, - kGicableOneSpace, kGicableBitMark, kGicableZeroSpace); - if (data_result.success == false) return false; - data = data_result.data; - offset += data_result.used; - - // Footer - if (!matchMark(results->rawbuf[offset++], kGicableBitMark)) return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset++], kGicableMinGap)) - return false; - + // Match Header + Data + Footer + uint16_t used; + used = matchGeneric(results->rawbuf + offset, &data, + results->rawlen - offset, nbits, + kGicableHdrMark, kGicableHdrSpace, + kGicableBitMark, kGicableOneSpace, + kGicableBitMark, kGicableZeroSpace, + kGicableBitMark, kGicableMinGap, true); + if (!used) return false; + offset += used; // Compliance if (strict) { // We expect a repeat frame. diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_GlobalCache.cpp b/lib/IRremoteESP8266-2.6.5/src/ir_GlobalCache.cpp similarity index 86% rename from lib/IRremoteESP8266-2.6.0/src/ir_GlobalCache.cpp rename to lib/IRremoteESP8266-2.6.5/src/ir_GlobalCache.cpp index daa9dd22c..8c9646970 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_GlobalCache.cpp +++ b/lib/IRremoteESP8266-2.6.5/src/ir_GlobalCache.cpp @@ -1,18 +1,12 @@ // Copyright 2016 Hisham Khalifa // Copyright 2017 David Conran -#include -#include "IRsend.h" - -// GGG L OOOO BBBB AA L CCCC AA CCCC H H EEEEEE -// G G L O O B B AAAA L C C AAAA C C H H E -// G L O O BBBBB A A L C A A C HHHHHH EEEE -// G GG L O O B BB AAAAAA L C C AAAAAA C C H H E -// GGGGG LLLLLL OOOO BBBBB A A LLLLLL CCCC A A CCCC H H EEEEEE - // Global Cache IR format sender originally added by Hisham Khalifa // (http://www.hishamkhalifa.com) +#include +#include "IRsend.h" + // Constants const uint16_t kGlobalCacheMaxRepeat = 50; const uint32_t kGlobalCacheMinUsec = 80; diff --git a/lib/IRremoteESP8266-2.6.5/src/ir_Goodweather.cpp b/lib/IRremoteESP8266-2.6.5/src/ir_Goodweather.cpp new file mode 100644 index 000000000..d8ac45f1b --- /dev/null +++ b/lib/IRremoteESP8266-2.6.5/src/ir_Goodweather.cpp @@ -0,0 +1,464 @@ +// Copyright 2019 ribeirodanielf +// Copyright 2019 David Conran +// +// Code to emulate Goodweather protocol compatible HVAC devices. +// Should be compatible with: +// * ZH/JT-03 remote control +// + +#include "ir_Goodweather.h" +#include +#ifndef ARDUINO +#include +#endif +#include "IRrecv.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" +#include "IRutils.h" + +using irutils::addBoolToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addFanToString; +using irutils::addTempToString; + +#if SEND_GOODWEATHER +// Send a Goodweather message. +// +// Args: +// data: The raw message to be sent. +// nbits: Nr. of bits of data in the message. (Default is kGoodweatherBits) +// repeat: Nr. of times the message is to be repeated. (Default = 0). +// +// Status: ALPHA / Untested. +// +// Ref: +// https://github.com/crankyoldgit/IRremoteESP8266/issues/697 +void IRsend::sendGoodweather(const uint64_t data, const uint16_t nbits, + const uint16_t repeat) { + if (nbits != kGoodweatherBits) + return; // Wrong nr. of bits to send a proper message. + // Set IR carrier frequency + enableIROut(38); + + for (uint16_t r = 0; r <= repeat; r++) { + // Header + mark(kGoodweatherHdrMark); + space(kGoodweatherHdrSpace); + + // Data + for (int16_t i = 0; i < nbits; i += 8) { + uint16_t chunk = (data >> i) & 0xFF; // Grab a byte at a time. + chunk = (~chunk) << 8 | chunk; // Prepend a inverted copy of the byte. + sendData(kGoodweatherBitMark, kGoodweatherOneSpace, + kGoodweatherBitMark, kGoodweatherZeroSpace, + chunk, 16, false); + } + // Footer + mark(kGoodweatherBitMark); + space(kGoodweatherHdrSpace); + mark(kGoodweatherBitMark); + space(kDefaultMessageGap); + } +} +#endif // SEND_GOODWEATHER + +IRGoodweatherAc::IRGoodweatherAc(const uint16_t pin, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { stateReset(); } + +void IRGoodweatherAc::stateReset(void) { +} + +void IRGoodweatherAc::begin(void) { _irsend.begin(); } + +#if SEND_GOODWEATHER +void IRGoodweatherAc::send(const uint16_t repeat) { + _irsend.sendGoodweather(remote, kGoodweatherBits, repeat); +} +#endif // SEND_GOODWEATHER + +uint64_t IRGoodweatherAc::getRaw(void) { return remote; } + +void IRGoodweatherAc::setRaw(const uint64_t state) { remote = state; } + +void IRGoodweatherAc::on(void) { this->setPower(true); } + +void IRGoodweatherAc::off(void) { this->setPower(false); } + +void IRGoodweatherAc::setPower(const bool on) { + this->setCommand(kGoodweatherCmdPower); + if (on) + remote |= kGoodweatherPowerMask; + else + remote &= ~kGoodweatherPowerMask; +} + +bool IRGoodweatherAc::getPower(void) { return remote & kGoodweatherPowerMask; } + +// Set the temp. in deg C +void IRGoodweatherAc::setTemp(const uint8_t temp) { + uint8_t new_temp = std::max(kGoodweatherTempMin, temp); + new_temp = std::min(kGoodweatherTempMax, new_temp); + if (new_temp > this->getTemp()) this->setCommand(kGoodweatherCmdUpTemp); + if (new_temp < this->getTemp()) this->setCommand(kGoodweatherCmdDownTemp); + remote &= ~kGoodweatherTempMask; + remote |= (uint64_t)(new_temp - kGoodweatherTempMin) << kGoodweatherBitTemp; +} + +// Return the set temp. in deg C +uint8_t IRGoodweatherAc::getTemp(void) { + return ((remote & kGoodweatherTempMask) >> kGoodweatherBitTemp) + + kGoodweatherTempMin; +} + +// Set the speed of the fan +void IRGoodweatherAc::setFan(const uint8_t speed) { + switch (speed) { + case kGoodweatherFanAuto: + case kGoodweatherFanLow: + case kGoodweatherFanMed: + case kGoodweatherFanHigh: + this->setCommand(kGoodweatherCmdFan); + remote &= ~kGoodweatherFanMask; + remote |= ((uint64_t)speed << kGoodweatherBitFan); + break; + default: + this->setFan(kGoodweatherFanAuto); + } +} + +uint8_t IRGoodweatherAc::getFan() { + return (remote & kGoodweatherFanMask) >> kGoodweatherBitFan; +} + +void IRGoodweatherAc::setMode(const uint8_t mode) { + switch (mode) { + case kGoodweatherAuto: + case kGoodweatherDry: + case kGoodweatherCool: + case kGoodweatherFan: + case kGoodweatherHeat: + this->setCommand(kGoodweatherCmdMode); + remote &= ~kGoodweatherModeMask; + remote |= (uint64_t)mode << kGoodweatherBitMode; + break; + default: + // If we get an unexpected mode, default to AUTO. + this->setMode(kGoodweatherAuto); + } +} + +uint8_t IRGoodweatherAc::getMode() { + return (remote & kGoodweatherModeMask) >> kGoodweatherBitMode; +} + +void IRGoodweatherAc::setLight(const bool toggle) { + this->setCommand(kGoodweatherCmdLight); + if (toggle) + remote |= kGoodweatherLightMask; + else + remote &= ~kGoodweatherLightMask; +} + +bool IRGoodweatherAc::getLight() { return remote & kGoodweatherLightMask; } + +void IRGoodweatherAc::setSleep(const bool toggle) { + this->setCommand(kGoodweatherCmdSleep); + if (toggle) + remote |= kGoodweatherSleepMask; + else + remote &= ~kGoodweatherSleepMask; +} + +bool IRGoodweatherAc::getSleep() { return remote & kGoodweatherSleepMask; } + +void IRGoodweatherAc::setTurbo(const bool toggle) { + this->setCommand(kGoodweatherCmdTurbo); + if (toggle) + remote |= kGoodweatherTurboMask; + else + remote &= ~kGoodweatherTurboMask; +} + +bool IRGoodweatherAc::getTurbo() { return remote & kGoodweatherTurboMask; } + +void IRGoodweatherAc::setSwing(const uint8_t speed) { + switch (speed) { + case kGoodweatherSwingOff: + case kGoodweatherSwingSlow: + case kGoodweatherSwingFast: + this->setCommand(kGoodweatherCmdSwing); + remote &= ~kGoodweatherSwingMask; + remote |= ((uint64_t)speed << kGoodweatherBitSwing); + break; + default: + this->setSwing(kGoodweatherSwingOff); + } +} + +uint8_t IRGoodweatherAc::getSwing() { + return (remote & kGoodweatherSwingMask) >> kGoodweatherBitSwing; +} + +void IRGoodweatherAc::setCommand(const uint8_t cmd) { + if (cmd <= kGoodweatherCmdLight) { + remote &= ~kGoodweatherCommandMask; + remote |= (cmd << kGoodweatherBitCommand); + } +} + +uint8_t IRGoodweatherAc::getCommand() { + return (remote & kGoodweatherCommandMask) >> kGoodweatherBitCommand; +} + +// Convert a standard A/C mode into its native mode. +uint8_t IRGoodweatherAc::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kGoodweatherCool; + case stdAc::opmode_t::kHeat: + return kGoodweatherHeat; + case stdAc::opmode_t::kDry: + return kGoodweatherDry; + case stdAc::opmode_t::kFan: + return kGoodweatherFan; + default: + return kGoodweatherAuto; + } +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRGoodweatherAc::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: + return kGoodweatherFanLow; + case stdAc::fanspeed_t::kMedium: + return kGoodweatherFanMed; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: + return kGoodweatherFanHigh; + default: + return kGoodweatherFanAuto; + } +} + +// Convert a standard A/C Vertical Swing into its native version. +uint8_t IRGoodweatherAc::convertSwingV(const stdAc::swingv_t swingv) { + switch (swingv) { + case stdAc::swingv_t::kHighest: + case stdAc::swingv_t::kHigh: + case stdAc::swingv_t::kMiddle: + return kGoodweatherSwingFast; + case stdAc::swingv_t::kLow: + case stdAc::swingv_t::kLowest: + case stdAc::swingv_t::kAuto: + return kGoodweatherSwingSlow; + default: + return kGoodweatherSwingOff; + } +} + +// Convert a native mode to it's common equivalent. +stdAc::opmode_t IRGoodweatherAc::toCommonMode(const uint8_t mode) { + switch (mode) { + case kGoodweatherCool: return stdAc::opmode_t::kCool; + case kGoodweatherHeat: return stdAc::opmode_t::kHeat; + case kGoodweatherDry: return stdAc::opmode_t::kDry; + case kGoodweatherFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRGoodweatherAc::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kGoodweatherFanHigh: return stdAc::fanspeed_t::kMax; + case kGoodweatherFanMed: return stdAc::fanspeed_t::kMedium; + case kGoodweatherFanLow: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRGoodweatherAc::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::GOODWEATHER; + result.power = this->getPower(); + result.mode = this->toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(); + result.fanspeed = this->toCommonFanSpeed(this->getFan()); + result.swingv = this->getSwing() == kGoodweatherSwingOff ? + stdAc::swingv_t::kOff : stdAc::swingv_t::kAuto; + result.turbo = this->getTurbo(); + result.light = this->getLight(); + result.sleep = this->getSleep() ? 0: -1; + // Not supported. + result.model = -1; + result.swingh = stdAc::swingh_t::kOff; + result.quiet = false; + result.econo = false; + result.filter = false; + result.clean = false; + result.beep = false; + result.clock = -1; + return result; +} + +// Convert the internal state into a human readable string. +String IRGoodweatherAc::toString() { + String result = ""; + result.reserve(150); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(getPower(), F("Power"), false); + result += addModeToString(getMode(), kGoodweatherAuto, kGoodweatherCool, + kGoodweatherHeat, kGoodweatherDry, kGoodweatherFan); + result += addTempToString(getTemp()); + result += addFanToString(getFan(), kGoodweatherFanHigh, kGoodweatherFanLow, + kGoodweatherFanAuto, kGoodweatherFanAuto, + kGoodweatherFanMed); + result += addLabeledString(getTurbo() ? F("Toggle") : F("-"), F("Turbo")); + result += addLabeledString(getLight() ? F("Toggle") : F("-"), F("Light")); + result += addLabeledString(getSleep() ? F("Toggle") : F("-"), F("Sleep")); + result += addIntToString(getSwing(), F("Swing")); + switch (this->getSwing()) { + case kGoodweatherSwingFast: + result += F(" (Fast)"); + break; + case kGoodweatherSwingSlow: + result += F(" (Slow)"); + break; + case kGoodweatherSwingOff: + result += F(" (Off)"); + break; + default: + result += F(" (UNKNOWN)"); + } + result += addIntToString(getCommand(), F("Command")); + switch (this->getCommand()) { + case kGoodweatherCmdPower: + result += F(" (Power)"); + break; + case kGoodweatherCmdMode: + result += F(" (Mode)"); + break; + case kGoodweatherCmdUpTemp: + result += F(" (Temp Up)"); + break; + case kGoodweatherCmdDownTemp: + result += F(" (Temp Down)"); + break; + case kGoodweatherCmdSwing: + result += F(" (Swing)"); + break; + case kGoodweatherCmdFan: + result += F(" (Fan)"); + break; + case kGoodweatherCmdTimer: + result += F(" (Timer)"); + break; + case kGoodweatherCmdAirFlow: + result += F(" (Air Flow)"); + break; + case kGoodweatherCmdHold: + result += F(" (Hold)"); + break; + case kGoodweatherCmdSleep: + result += F(" (Sleep)"); + break; + case kGoodweatherCmdTurbo: + result += F(" (Turbo)"); + break; + case kGoodweatherCmdLight: + result += F(" (Light)"); + break; + default: + result += F(" (UNKNOWN)"); + } + return result; +} + +#if DECODE_GOODWEATHER +// Decode the supplied Goodweather message. +// +// Args: +// results: Ptr to the data to decode and where to store the decode result. +// nbits: The number of data bits to expect. Typically kGoodweatherBits. +// strict: Flag indicating if we should perform strict matching. +// Returns: +// boolean: True if it can decode it, false if it can't. +// +// Status: ALPHA / Untested. +bool IRrecv::decodeGoodweather(decode_results* results, + const uint16_t nbits, + const bool strict) { + if (results->rawlen < 2 * (2 * nbits) + kHeader + 2 * kFooter - 1) + return false; // Can't possibly be a valid Goodweather message. + if (strict && nbits != kGoodweatherBits) + return false; // Not strictly a Goodweather message. + + uint64_t dataSoFar = 0; + uint16_t dataBitsSoFar = 0; + uint16_t offset = kStartOffset; + match_result_t data_result; + + // Header + if (!matchMark(results->rawbuf[offset++], kGoodweatherHdrMark)) return false; + if (!matchSpace(results->rawbuf[offset++], kGoodweatherHdrSpace)) + return false; + + // Data + for (; offset <= results->rawlen - 32 && dataBitsSoFar < nbits; + dataBitsSoFar += 8) { + DPRINT("DEBUG: Attempting Byte #"); + DPRINTLN(dataBitsSoFar / 8); + // Read in a byte at a time. + // Normal first. + data_result = matchData(&(results->rawbuf[offset]), 8, + kGoodweatherBitMark, kGoodweatherOneSpace, + kGoodweatherBitMark, kGoodweatherZeroSpace, + _tolerance, kMarkExcess, false); + if (data_result.success == false) return false; + DPRINTLN("DEBUG: Normal byte read okay."); + offset += data_result.used; + uint8_t data = (uint8_t)data_result.data; + // Then inverted. + data_result = matchData(&(results->rawbuf[offset]), 8, + kGoodweatherBitMark, kGoodweatherOneSpace, + kGoodweatherBitMark, kGoodweatherZeroSpace, + _tolerance, kMarkExcess, false); + if (data_result.success == false) return false; + DPRINTLN("DEBUG: Inverted byte read okay."); + offset += data_result.used; + uint8_t inverted = (uint8_t)data_result.data; + DPRINT("DEBUG: data = "); + DPRINTLN((uint16_t)data); + DPRINT("DEBUG: inverted = "); + DPRINTLN((uint16_t)inverted); + if (data != (inverted ^ 0xFF)) return false; // Data integrity failed. + dataSoFar |= (uint64_t)data << dataBitsSoFar; + } + + // Footer. + if (!matchMark(results->rawbuf[offset++], kGoodweatherBitMark)) return false; + if (!matchSpace(results->rawbuf[offset++], kGoodweatherHdrSpace)) + return false; + if (!matchMark(results->rawbuf[offset++], kGoodweatherBitMark)) return false; + if (offset <= results->rawlen && + !matchAtLeast(results->rawbuf[offset], kGoodweatherHdrSpace)) + return false; + + // Compliance + if (strict && (dataBitsSoFar != kGoodweatherBits)) return false; + + // Success + results->decode_type = decode_type_t::GOODWEATHER; + results->bits = dataBitsSoFar; + results->value = dataSoFar; + results->address = 0; + results->command = 0; + return true; +} +#endif // DECODE_GOODWEATHER diff --git a/lib/IRremoteESP8266-2.6.5/src/ir_Goodweather.h b/lib/IRremoteESP8266-2.6.5/src/ir_Goodweather.h new file mode 100644 index 000000000..76d559779 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.5/src/ir_Goodweather.h @@ -0,0 +1,137 @@ +// Goodweather A/C +// +// Copyright 2019 ribeirodanielf +// Copyright 2019 David Conran + +// Supports: +// Brand: Goodweather, Model: ZH/JT-03 remote + +#ifndef IR_GOODWEATHER_H_ +#define IR_GOODWEATHER_H_ + +#define __STDC_LIMIT_MACROS +#include +#ifndef UNIT_TEST +#include +#endif +#include "IRremoteESP8266.h" +#include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif + +// Ref: +// https://github.com/crankyoldgit/IRremoteESP8266/issues/697 + +// Constants + +// Timing +const uint16_t kGoodweatherBitMark = 640; +const uint16_t kGoodweatherOneSpace = 580; +const uint16_t kGoodweatherZeroSpace = 1600; +const uint16_t kGoodweatherHdrMark = 6800; +const uint16_t kGoodweatherHdrSpace = 6800; + +// Masks +const uint8_t kGoodweatherBitLight = 8; +const uint64_t kGoodweatherLightMask = 0x1ULL << kGoodweatherBitLight; +const uint8_t kGoodweatherBitTurbo = kGoodweatherBitLight + 3; // 11 +const uint64_t kGoodweatherTurboMask = 0x1ULL << kGoodweatherBitTurbo; +const uint8_t kGoodweatherBitCommand = kGoodweatherBitTurbo + 5; // 16 +const uint64_t kGoodweatherCommandMask = 0xFULL << kGoodweatherBitCommand; +const uint8_t kGoodweatherBitSleep = kGoodweatherBitCommand + 8; // 24 +const uint64_t kGoodweatherSleepMask = 0x1ULL << kGoodweatherBitSleep; +const uint8_t kGoodweatherBitPower = kGoodweatherBitSleep + 1; // 25 +const uint64_t kGoodweatherPowerMask = 0x1ULL << kGoodweatherBitPower; +const uint8_t kGoodweatherBitSwing = kGoodweatherBitPower + 1; // 26 +const uint64_t kGoodweatherSwingMask = 0x3ULL << kGoodweatherBitSwing; +const uint8_t kGoodweatherBitFan = kGoodweatherBitSwing + 3; // 29 +const uint64_t kGoodweatherFanMask = 0x3ULL << kGoodweatherBitFan; +const uint8_t kGoodweatherBitTemp = kGoodweatherBitFan + 3; // 32 +const uint64_t kGoodweatherTempMask = 0xFULL << kGoodweatherBitTemp; +const uint8_t kGoodweatherBitMode = kGoodweatherBitTemp + 5; // 37 +const uint64_t kGoodweatherModeMask = 0x7ULL << kGoodweatherBitMode; + +// Modes +const uint8_t kGoodweatherAuto = 0b000; +const uint8_t kGoodweatherCool = 0b001; +const uint8_t kGoodweatherDry = 0b010; +const uint8_t kGoodweatherFan = 0b011; +const uint8_t kGoodweatherHeat = 0b100; +const uint8_t kGoodweatherSwingFast = 0b00; +const uint8_t kGoodweatherSwingSlow = 0b01; +const uint8_t kGoodweatherSwingOff = 0b10; +// Fan Control +const uint8_t kGoodweatherFanAuto = 0b00; +const uint8_t kGoodweatherFanHigh = 0b01; +const uint8_t kGoodweatherFanMed = 0b10; +const uint8_t kGoodweatherFanLow = 0b11; +// Temperature +const uint8_t kGoodweatherTempMin = 16; // Celsius +const uint8_t kGoodweatherTempMax = 31; // Celsius +// Commands +const uint8_t kGoodweatherCmdPower = 0x00; +const uint8_t kGoodweatherCmdMode = 0x01; +const uint8_t kGoodweatherCmdUpTemp = 0x02; +const uint8_t kGoodweatherCmdDownTemp = 0x03; +const uint8_t kGoodweatherCmdSwing = 0x04; +const uint8_t kGoodweatherCmdFan = 0x05; +const uint8_t kGoodweatherCmdTimer = 0x06; +const uint8_t kGoodweatherCmdAirFlow = 0x07; +const uint8_t kGoodweatherCmdHold = 0x08; +const uint8_t kGoodweatherCmdSleep = 0x09; +const uint8_t kGoodweatherCmdTurbo = 0x0A; +const uint8_t kGoodweatherCmdLight = 0x0B; + + +// Classes +class IRGoodweatherAc { + public: + explicit IRGoodweatherAc(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); + + void stateReset(void); +#if SEND_GOODWEATHER + void send(const uint16_t repeat = kGoodweatherMinRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } +#endif // SEND_GOODWEATHER + void begin(void); + void on(void); + void off(void); + void setPower(const bool on); + bool getPower(void); + void setTemp(const uint8_t temp); + uint8_t getTemp(void); + void setFan(const uint8_t speed); + uint8_t getFan(void); + void setMode(const uint8_t mode); + uint8_t getMode(); + void setSwing(const uint8_t speed); + uint8_t getSwing(void); + void setSleep(const bool toggle); + bool getSleep(void); + void setTurbo(const bool toggle); + bool getTurbo(void); + void setLight(const bool toggle); + bool getLight(void); + void setCommand(const uint8_t cmd); + uint8_t getCommand(void); + uint64_t getRaw(void); + void setRaw(const uint64_t state); + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); + uint8_t convertSwingV(const stdAc::swingv_t swingv); + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + stdAc::state_t toCommon(void); + String toString(); +#ifndef UNIT_TEST + + private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif + uint64_t remote; // The state of the IR remote in IR code form. +}; +#endif // IR_GOODWEATHER_H_ diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Gree.cpp b/lib/IRremoteESP8266-2.6.5/src/ir_Gree.cpp similarity index 50% rename from lib/IRremoteESP8266-2.6.0/src/ir_Gree.cpp rename to lib/IRremoteESP8266-2.6.5/src/ir_Gree.cpp index 756f008d4..a4d906424 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Gree.cpp +++ b/lib/IRremoteESP8266-2.6.5/src/ir_Gree.cpp @@ -18,12 +18,6 @@ #include "IRutils.h" #include "ir_Kelvinator.h" -// GGGG RRRRRR EEEEEEE EEEEEEE -// GG GG RR RR EE EE -// GG RRRRRR EEEEE EEEEE -// GG GG RR RR EE EE -// GGGGGG RR RR EEEEEEE EEEEEEE - // Constants // Ref: https://github.com/ToniA/arduino-heatpumpir/blob/master/GreeHeatpumpIR.h const uint16_t kGreeHdrMark = 9000; @@ -35,6 +29,14 @@ const uint16_t kGreeMsgSpace = 19000; const uint8_t kGreeBlockFooter = 0b010; const uint8_t kGreeBlockFooterBits = 3; +using irutils::addBoolToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addFanToString; +using irutils::addTempToString; +using irutils::minsToString; + #if SEND_GREE // Send a Gree Heat Pump message. // @@ -47,7 +49,8 @@ const uint8_t kGreeBlockFooterBits = 3; // // Ref: // https://github.com/ToniA/arduino-heatpumpir/blob/master/GreeHeatpumpIR.cpp -void IRsend::sendGree(unsigned char data[], uint16_t nbytes, uint16_t repeat) { +void IRsend::sendGree(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { if (nbytes < kGreeStateLength) return; // Not enough bytes to send a proper message. @@ -80,7 +83,8 @@ void IRsend::sendGree(unsigned char data[], uint16_t nbytes, uint16_t repeat) { // // Ref: // https://github.com/ToniA/arduino-heatpumpir/blob/master/GreeHeatpumpIR.cpp -void IRsend::sendGree(uint64_t data, uint16_t nbits, uint16_t repeat) { +void IRsend::sendGree(const uint64_t data, const uint16_t nbits, + const uint16_t repeat) { if (nbits != kGreeBits) return; // Wrong nr. of bits to send a proper message. // Set IR carrier frequency @@ -110,9 +114,14 @@ void IRsend::sendGree(uint64_t data, uint16_t nbits, uint16_t repeat) { } #endif // SEND_GREE -IRGreeAC::IRGreeAC(uint16_t pin) : _irsend(pin) { stateReset(); } +IRGreeAC::IRGreeAC(const uint16_t pin, const gree_ac_remote_model_t model, + const bool inverted, const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { + stateReset(); + setModel(model); +} -void IRGreeAC::stateReset() { +void IRGreeAC::stateReset(void) { // This resets to a known-good state to Power Off, Fan Auto, Mode Auto, 25C. for (uint8_t i = 0; i < kGreeStateLength; i++) remote_state[i] = 0x0; remote_state[1] = 0x09; @@ -122,11 +131,12 @@ void IRGreeAC::stateReset() { remote_state[7] = 0x50; } -void IRGreeAC::fixup() { +void IRGreeAC::fixup(void) { + setPower(getPower()); // Redo the power bits as they differ between models. checksum(); // Calculate the checksums } -void IRGreeAC::begin() { _irsend.begin(); } +void IRGreeAC::begin(void) { _irsend.begin(); } #if SEND_GREE void IRGreeAC::send(const uint16_t repeat) { @@ -135,15 +145,22 @@ void IRGreeAC::send(const uint16_t repeat) { } #endif // SEND_GREE -uint8_t* IRGreeAC::getRaw() { +uint8_t* IRGreeAC::getRaw(void) { fixup(); // Ensure correct settings before sending. return remote_state; } -void IRGreeAC::setRaw(uint8_t new_code[]) { +void IRGreeAC::setRaw(const uint8_t new_code[]) { for (uint8_t i = 0; i < kGreeStateLength; i++) { remote_state[i] = new_code[i]; } + // We can only detect the difference between models when the power is on. + if (getPower()) { + if (remote_state[2] & kGreePower2Mask) + _model = gree_ac_remote_model_t::YAW1F; + else + _model = gree_ac_remote_model_t::YBOFB; + } } void IRGreeAC::checksum(const uint16_t length) { @@ -160,33 +177,45 @@ void IRGreeAC::checksum(const uint16_t length) { // A boolean. bool IRGreeAC::validChecksum(const uint8_t state[], const uint16_t length) { // Top 4 bits of the last byte in the state is the state's checksum. - if (state[length - 1] >> 4 == - IRKelvinatorAC::calcBlockChecksum(state, length)) - return true; - else - return false; + return (state[length - 1] >> 4 == IRKelvinatorAC::calcBlockChecksum(state, + length)); } -void IRGreeAC::on() { - remote_state[0] |= kGreePower1Mask; - remote_state[2] |= kGreePower2Mask; +void IRGreeAC::setModel(const gree_ac_remote_model_t model) { + switch (model) { + case gree_ac_remote_model_t::YAW1F: + case gree_ac_remote_model_t::YBOFB: + _model = model; break; + default: + setModel(gree_ac_remote_model_t::YAW1F); + } } -void IRGreeAC::off() { - remote_state[0] &= ~kGreePower1Mask; - remote_state[2] &= ~kGreePower2Mask; +gree_ac_remote_model_t IRGreeAC::getModel(void) { + return _model; } -void IRGreeAC::setPower(const bool state) { - if (state) - on(); - else - off(); +void IRGreeAC::on(void) { setPower(true); } + +void IRGreeAC::off(void) { setPower(false); } + +void IRGreeAC::setPower(const bool on) { + if (on) { + remote_state[0] |= kGreePower1Mask; + switch (_model) { + case gree_ac_remote_model_t::YBOFB: break; + default: + remote_state[2] |= kGreePower2Mask; + } + } else { + remote_state[0] &= ~kGreePower1Mask; + remote_state[2] &= ~kGreePower2Mask; // May not be needed. See #814 + } } -bool IRGreeAC::getPower() { - return (remote_state[0] & kGreePower1Mask) && - (remote_state[2] & kGreePower2Mask); +bool IRGreeAC::getPower(void) { + // See #814. Not checking/requiring: (remote_state[2] & kGreePower2Mask) + return remote_state[0] & kGreePower1Mask; } // Set the temp. in deg C @@ -194,12 +223,13 @@ void IRGreeAC::setTemp(const uint8_t temp) { uint8_t new_temp = std::max((uint8_t)kGreeMinTemp, temp); new_temp = std::min((uint8_t)kGreeMaxTemp, new_temp); if (getMode() == kGreeAuto) new_temp = 25; - remote_state[1] = (remote_state[1] & 0xF0U) | (new_temp - kGreeMinTemp); + remote_state[1] = (remote_state[1] & ~kGreeTempMask) | + (new_temp - kGreeMinTemp); } // Return the set temp. in deg C -uint8_t IRGreeAC::getTemp() { - return ((remote_state[1] & 0xFU) + kGreeMinTemp); +uint8_t IRGreeAC::getTemp(void) { + return ((remote_state[1] & kGreeTempMask) + kGreeMinTemp); } // Set the speed of the fan, 0-3, 0 is auto, 1-3 is the speed @@ -212,7 +242,7 @@ void IRGreeAC::setFan(const uint8_t speed) { remote_state[0] |= (fan << 4); } -uint8_t IRGreeAC::getFan() { return ((remote_state[0] & kGreeFanMask) >> 4); } +uint8_t IRGreeAC::getFan(void) { return (remote_state[0] & kGreeFanMask) >> 4; } void IRGreeAC::setMode(const uint8_t new_mode) { uint8_t mode = new_mode; @@ -237,35 +267,61 @@ void IRGreeAC::setMode(const uint8_t new_mode) { remote_state[0] |= mode; } -uint8_t IRGreeAC::getMode() { return (remote_state[0] & kGreeModeMask); } +uint8_t IRGreeAC::getMode(void) { return (remote_state[0] & kGreeModeMask); } -void IRGreeAC::setLight(const bool state) { - remote_state[2] &= ~kGreeLightMask; - remote_state[2] |= (state << 5); +void IRGreeAC::setLight(const bool on) { + if (on) + remote_state[2] |= kGreeLightMask; + else + remote_state[2] &= ~kGreeLightMask; } -bool IRGreeAC::getLight() { return remote_state[2] & kGreeLightMask; } +bool IRGreeAC::getLight(void) { return remote_state[2] & kGreeLightMask; } -void IRGreeAC::setXFan(const bool state) { - remote_state[2] &= ~kGreeXfanMask; - remote_state[2] |= (state << 7); +void IRGreeAC::setIFeel(const bool on) { + if (on) + remote_state[5] |= kGreeIFeelMask; + else + remote_state[5] &= ~kGreeIFeelMask; } -bool IRGreeAC::getXFan() { return remote_state[2] & kGreeXfanMask; } +bool IRGreeAC::getIFeel(void) { return remote_state[5] & kGreeIFeelMask; } -void IRGreeAC::setSleep(const bool state) { - remote_state[0] &= ~kGreeSleepMask; - remote_state[0] |= (state << 7); +void IRGreeAC::setWiFi(const bool on) { + if (on) + remote_state[5] |= kGreeWiFiMask; + else + remote_state[5] &= ~kGreeWiFiMask; } -bool IRGreeAC::getSleep() { return remote_state[0] & kGreeSleepMask; } +bool IRGreeAC::getWiFi(void) { return remote_state[5] & kGreeWiFiMask; } -void IRGreeAC::setTurbo(const bool state) { - remote_state[2] &= ~kGreeTurboMask; - remote_state[2] |= (state << 4); +void IRGreeAC::setXFan(const bool on) { + if (on) + remote_state[2] |= kGreeXfanMask; + else + remote_state[2] &= ~kGreeXfanMask; } -bool IRGreeAC::getTurbo() { return remote_state[2] & kGreeTurboMask; } +bool IRGreeAC::getXFan(void) { return remote_state[2] & kGreeXfanMask; } + +void IRGreeAC::setSleep(const bool on) { + if (on) + remote_state[0] |= kGreeSleepMask; + else + remote_state[0] &= ~kGreeSleepMask; +} + +bool IRGreeAC::getSleep(void) { return remote_state[0] & kGreeSleepMask; } + +void IRGreeAC::setTurbo(const bool on) { + if (on) + remote_state[2] |= kGreeTurboMask; + else + remote_state[2] &= ~kGreeTurboMask; +} + +bool IRGreeAC::getTurbo(void) { return remote_state[2] & kGreeTurboMask; } void IRGreeAC::setSwingVertical(const bool automatic, const uint8_t position) { remote_state[0] &= ~kGreeSwingAutoMask; @@ -297,14 +353,52 @@ void IRGreeAC::setSwingVertical(const bool automatic, const uint8_t position) { remote_state[4] |= new_position; } -bool IRGreeAC::getSwingVerticalAuto() { +bool IRGreeAC::getSwingVerticalAuto(void) { return remote_state[0] & kGreeSwingAutoMask; } -uint8_t IRGreeAC::getSwingVerticalPosition() { +uint8_t IRGreeAC::getSwingVerticalPosition(void) { return remote_state[4] & kGreeSwingPosMask; } +void IRGreeAC::setTimerEnabled(const bool on) { + if (on) + remote_state[1] |= kGreeTimerEnabledBit; + else + remote_state[1] &= ~kGreeTimerEnabledBit; +} + +bool IRGreeAC::getTimerEnabled(void) { + return remote_state[1] & kGreeTimerEnabledBit; +} + +// Returns the number of minutes the timer is set for. +uint16_t IRGreeAC::getTimer(void) { + uint16_t hrs = irutils::bcdToUint8( + (remote_state[2] & kGreeTimerHoursMask) | + ((remote_state[1] & kGreeTimerTensHrMask) >> 1)); + return hrs * 60 + ((remote_state[1] & kGreeTimerHalfHrBit) ? 30 : 0); +} + +// Set the A/C's timer to turn off in X many minutes. +// Stores time internally in 30 min units. +// e.g. 5 mins means 0 (& Off), 95 mins is 90 mins (& On). Max is 24 hours. +// +// Args: +// minutes: The number of minutes the timer should be set for. +void IRGreeAC::setTimer(const uint16_t minutes) { + // Clear the previous settings. + remote_state[1] &= ~kGreeTimer1Mask; + remote_state[2] &= ~kGreeTimerHoursMask; + uint16_t mins = std::min(kGreeTimerMax, minutes); // Bounds check. + setTimerEnabled(mins >= 30); // Timer is enabled when >= 30 mins. + uint8_t hours = mins / 60; + uint8_t halfhour = (mins % 60) < 30 ? 0 : 1; + // Set the "tens" digit of hours & the half hour. + remote_state[1] |= (((hours / 10) << 1) | halfhour) << 4; + // Set the "units" digit of hours. + remote_state[2] |= (hours % 10); +} // Convert a standard A/C mode into its native mode. uint8_t IRGreeAC::convertMode(const stdAc::opmode_t mode) { @@ -356,79 +450,92 @@ uint8_t IRGreeAC::convertSwingV(const stdAc::swingv_t swingv) { } } +// Convert a native mode to it's common equivalent. +stdAc::opmode_t IRGreeAC::toCommonMode(const uint8_t mode) { + switch (mode) { + case kGreeCool: return stdAc::opmode_t::kCool; + case kGreeHeat: return stdAc::opmode_t::kHeat; + case kGreeDry: return stdAc::opmode_t::kDry; + case kGreeFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRGreeAC::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kGreeFanMax: return stdAc::fanspeed_t::kMax; + case kGreeFanMax - 1: return stdAc::fanspeed_t::kMedium; + case kGreeFanMin: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +// Convert a native vertical swing to it's common equivalent. +stdAc::swingv_t IRGreeAC::toCommonSwingV(const uint8_t pos) { + switch (pos) { + case kGreeSwingUp: return stdAc::swingv_t::kHighest; + case kGreeSwingMiddleUp: return stdAc::swingv_t::kHigh; + case kGreeSwingMiddle: return stdAc::swingv_t::kMiddle; + case kGreeSwingMiddleDown: return stdAc::swingv_t::kLow; + case kGreeSwingDown: return stdAc::swingv_t::kLowest; + default: return stdAc::swingv_t::kAuto; + } +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRGreeAC::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::GREE; + result.model = this->getModel(); + result.power = this->getPower(); + result.mode = this->toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(); + result.fanspeed = this->toCommonFanSpeed(this->getFan()); + if (this->getSwingVerticalAuto()) + result.swingv = stdAc::swingv_t::kAuto; + else + result.swingv = this->toCommonSwingV(this->getSwingVerticalPosition()); + result.turbo = this->getTurbo(); + result.light = this->getLight(); + result.clean = this->getXFan(); + result.sleep = this->getSleep() ? 0 : -1; + // Not supported. + result.swingh = stdAc::swingh_t::kOff; + result.quiet = false; + result.econo = false; + result.filter = false; + result.beep = false; + result.clock = -1; + return result; +} + // Convert the internal state into a human readable string. -#ifdef ARDUINO -String IRGreeAC::toString() { +String IRGreeAC::toString(void) { String result = ""; -#else -std::string IRGreeAC::toString() { - std::string result = ""; -#endif // ARDUINO - result += F("Power: "); - if (getPower()) - result += F("On"); - else - result += F("Off"); - result += F(", Mode: "); - result += uint64ToString(getMode()); - switch (getMode()) { - case kGreeAuto: - result += F(" (AUTO)"); - break; - case kGreeCool: - result += F(" (COOL)"); - break; - case kGreeHeat: - result += F(" (HEAT)"); - break; - case kGreeDry: - result += F(" (DRY)"); - break; - case kGreeFan: - result += F(" (FAN)"); - break; - default: - result += F(" (UNKNOWN)"); + result.reserve(150); // Reserve some heap for the string to reduce fragging. + result += addIntToString(getModel(), F("Model"), false); + switch (getModel()) { + case gree_ac_remote_model_t::YAW1F: result += F(" (YAW1F)"); break; + case gree_ac_remote_model_t::YBOFB: result += F(" (YBOFB)"); break; + default: result += F(" (UNKNOWN)"); } - result += F(", Temp: "); - result += uint64ToString(getTemp()); - result += F("C, Fan: "); - result += uint64ToString(getFan()); - switch (getFan()) { - case 0: - result += F(" (AUTO)"); - break; - case kGreeFanMax: - result += F(" (MAX)"); - break; - } - result += F(", Turbo: "); - if (getTurbo()) - result += F("On"); - else - result += F("Off"); - result += F(", XFan: "); - if (getXFan()) - result += F("On"); - else - result += F("Off"); - result += F(", Light: "); - if (getLight()) - result += F("On"); - else - result += F("Off"); - result += F(", Sleep: "); - if (getSleep()) - result += F("On"); - else - result += F("Off"); - result += F(", Swing Vertical Mode: "); - if (getSwingVerticalAuto()) - result += F("Auto"); - else - result += F("Manual"); - result += F(", Swing Vertical Pos: "); - result += uint64ToString(getSwingVerticalPosition()); + result += addBoolToString(getPower(), F("Power")); + result += addModeToString(getMode(), kGreeAuto, kGreeCool, kGreeHeat, + kGreeDry, kGreeFan); + result += addTempToString(getTemp()); + result += addFanToString(getFan(), kGreeFanMax, kGreeFanMin, kGreeFanAuto, + kGreeFanAuto, kGreeFanMed); + result += addBoolToString(getTurbo(), F("Turbo")); + result += addBoolToString(getIFeel(), F("IFeel")); + result += addBoolToString(getWiFi(), F("WiFi")); + result += addBoolToString(getXFan(), F("XFan")); + result += addBoolToString(getLight(), F("Light")); + result += addBoolToString(getSleep(), F("Sleep")); + result += addLabeledString(getSwingVerticalAuto() ? F("Auto") : F("Manual"), + F("Swing Vertical Mode")); + result += addIntToString(getSwingVerticalPosition(), F("Swing Vertical Pos")); switch (getSwingVerticalPosition()) { case kGreeSwingLastPos: result += F(" (Last Pos)"); @@ -437,6 +544,11 @@ std::string IRGreeAC::toString() { result += F(" (Auto)"); break; } + result += F(", Timer: "); + if (getTimerEnabled()) + result += minsToString(getTimer()); + else + result += F("Off"); return result; } @@ -458,72 +570,50 @@ bool IRrecv::decodeGree(decode_results* results, uint16_t nbits, bool strict) { if (strict && nbits != kGreeBits) return false; // Not strictly a Gree message. - uint32_t data; uint16_t offset = kStartOffset; // There are two blocks back-to-back in a full Gree IR message // sequence. - int8_t state_pos = 0; - match_result_t data_result; - // Header - if (!matchMark(results->rawbuf[offset++], kGreeHdrMark)) return false; - if (!matchSpace(results->rawbuf[offset++], kGreeHdrSpace)) return false; - // Data Block #1 (32 bits) - data_result = - matchData(&(results->rawbuf[offset]), 32, kGreeBitMark, kGreeOneSpace, - kGreeBitMark, kGreeZeroSpace, kTolerance, kMarkExcess, false); - if (data_result.success == false) return false; - data = data_result.data; - offset += data_result.used; - - // Record Data Block #1 in the state. - for (uint16_t i = 0; i < 4; i++, data >>= 8) - results->state[state_pos + i] = data & 0xFF; - state_pos += 4; + uint16_t used; + // Header + Data Block #1 (32 bits) + used = matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, nbits / 2, + kGreeHdrMark, kGreeHdrSpace, + kGreeBitMark, kGreeOneSpace, + kGreeBitMark, kGreeZeroSpace, + 0, 0, false, + _tolerance, kMarkExcess, false); + if (used == 0) return false; + offset += used; // Block #1 footer (3 bits, B010) + match_result_t data_result; data_result = matchData(&(results->rawbuf[offset]), kGreeBlockFooterBits, kGreeBitMark, kGreeOneSpace, kGreeBitMark, - kGreeZeroSpace, kTolerance, kMarkExcess, false); + kGreeZeroSpace, _tolerance, kMarkExcess, false); if (data_result.success == false) return false; if (data_result.data != kGreeBlockFooter) return false; offset += data_result.used; - // Inter-block gap. - if (!matchMark(results->rawbuf[offset++], kGreeBitMark)) return false; - if (!matchSpace(results->rawbuf[offset++], kGreeMsgSpace)) return false; - - // Data Block #2 (32 bits) - data_result = - matchData(&(results->rawbuf[offset]), 32, kGreeBitMark, kGreeOneSpace, - kGreeBitMark, kGreeZeroSpace, kTolerance, kMarkExcess, false); - if (data_result.success == false) return false; - data = data_result.data; - offset += data_result.used; - - // Record Data Block #2 in the state. - for (uint16_t i = 0; i < 4; i++, data >>= 8) - results->state[state_pos + i] = data & 0xFF; - state_pos += 4; - - // Footer. - if (!matchMark(results->rawbuf[offset++], kGreeBitMark)) return false; - if (offset <= results->rawlen && - !matchAtLeast(results->rawbuf[offset], kGreeMsgSpace)) - return false; + // Inter-block gap + Data Block #2 (32 bits) + Footer + if (!matchGeneric(results->rawbuf + offset, results->state + 4, + results->rawlen - offset, nbits / 2, + kGreeBitMark, kGreeMsgSpace, + kGreeBitMark, kGreeOneSpace, + kGreeBitMark, kGreeZeroSpace, + kGreeBitMark, kGreeMsgSpace, true, + _tolerance, kMarkExcess, false)) return false; // Compliance if (strict) { - // Correct size/length) - if (state_pos != kGreeStateLength) return false; // Verify the message's checksum is correct. if (!IRGreeAC::validChecksum(results->state)) return false; } // Success results->decode_type = GREE; - results->bits = state_pos * 8; + results->bits = nbits; // No need to record the state as we stored it as we decoded it. // As we use result->state, we don't record value, address, or command as it // is a union data type. diff --git a/lib/IRremoteESP8266-2.6.5/src/ir_Gree.h b/lib/IRremoteESP8266-2.6.5/src/ir_Gree.h new file mode 100644 index 000000000..a399d50d5 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.5/src/ir_Gree.h @@ -0,0 +1,173 @@ +// Copyright 2016 David Conran +// Gree A/C +// +// Supports: +// Brand: Ultimate, Model: Heat Pump +// Brand: EKOKAI, Model: A/C +// Brand: RusClimate, Model: EACS/I-09HAR_X/N3 A/C +// Brand: RusClimate, Model: YAW1F remote +// Brand: Green, Model: YBOFB remote +// Brand: Green, Model: YBOFB2 remote + +#ifndef IR_GREE_H_ +#define IR_GREE_H_ + +#define __STDC_LIMIT_MACROS +#include +#ifndef UNIT_TEST +#include +#endif +#include "IRremoteESP8266.h" +#include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif + +// Constants +enum gree_ac_remote_model_t { + YAW1F = 1, // (1) Ultimate, EKOKAI, RusClimate (Default) + YBOFB, // (2) Green, YBOFB2, YAPOF3 +}; + +const uint8_t kGreeAuto = 0; +const uint8_t kGreeCool = 1; +const uint8_t kGreeDry = 2; +const uint8_t kGreeFan = 3; +const uint8_t kGreeHeat = 4; + +// Byte 0 +const uint8_t kGreeModeMask = 0b00000111; +const uint8_t kGreePower1Mask = 0b00001000; +const uint8_t kGreeFanMask = 0b00110000; +const uint8_t kGreeFanAuto = 0; +const uint8_t kGreeFanMin = 1; +const uint8_t kGreeFanMed = 2; +const uint8_t kGreeFanMax = 3; +const uint8_t kGreeSwingAutoMask = 0b01000000; +const uint8_t kGreeSleepMask = 0b10000000; +// Byte 1 +const uint8_t kGreeTempMask = 0b00001111; +const uint8_t kGreeMinTemp = 16; // Celsius +const uint8_t kGreeMaxTemp = 30; // Celsius +const uint8_t kGreeTimerEnabledBit = 0b10000000; +const uint8_t kGreeTimerHalfHrBit = 0b00010000; +const uint8_t kGreeTimerTensHrMask = 0b01100000; +const uint8_t kGreeTimer1Mask = kGreeTimerTensHrMask | kGreeTimerHalfHrBit; +const uint16_t kGreeTimerMax = 24 * 60; + +// Byte 2 +const uint8_t kGreeTimerHoursMask = 0b00001111; +const uint8_t kGreeTurboMask = 0b00010000; +const uint8_t kGreeLightMask = 0b00100000; +// This might not be used. See #814 +const uint8_t kGreePower2Mask = 0b01000000; +const uint8_t kGreeXfanMask = 0b10000000; +// Byte 4 +const uint8_t kGreeSwingPosMask = 0b00001111; +// byte 5 +const uint8_t kGreeIFeelMask = 0b00000100; +const uint8_t kGreeWiFiMask = 0b01000000; + +const uint8_t kGreeSwingLastPos = 0b00000000; +const uint8_t kGreeSwingAuto = 0b00000001; +const uint8_t kGreeSwingUp = 0b00000010; +const uint8_t kGreeSwingMiddleUp = 0b00000011; +const uint8_t kGreeSwingMiddle = 0b00000100; +const uint8_t kGreeSwingMiddleDown = 0b00000101; +const uint8_t kGreeSwingDown = 0b00000110; +const uint8_t kGreeSwingDownAuto = 0b00000111; +const uint8_t kGreeSwingMiddleAuto = 0b00001001; +const uint8_t kGreeSwingUpAuto = 0b00001011; + +// Legacy defines. +#define GREE_AUTO kGreeAuto +#define GREE_COOL kGreeCool +#define GREE_DRY kGreeDry +#define GREE_FAN kGreeFan +#define GREE_HEAT kGreeHeat +#define GREE_MIN_TEMP kGreeMinTemp +#define GREE_MAX_TEMP kGreeMaxTemp +#define GREE_FAN_MAX kGreeFanMax +#define GREE_SWING_LAST_POS kGreeSwingLastPos +#define GREE_SWING_AUTO kGreeSwingAuto +#define GREE_SWING_UP kGreeSwingUp +#define GREE_SWING_MIDDLE_UP kGreeSwingMiddleUp +#define GREE_SWING_MIDDLE kGreeSwingMiddle +#define GREE_SWING_MIDDLE_DOWN kGreeSwingMiddleDown +#define GREE_SWING_DOWN kGreeSwingDown +#define GREE_SWING_DOWN_AUTO kGreeSwingDownAuto +#define GREE_SWING_MIDDLE_AUTO kGreeSwingMiddleAuto +#define GREE_SWING_UP_AUTO kGreeSwingUpAuto + +// Classes +class IRGreeAC { + public: + explicit IRGreeAC( + const uint16_t pin, + const gree_ac_remote_model_t model = gree_ac_remote_model_t::YAW1F, + const bool inverted = false, const bool use_modulation = true); + + void stateReset(void); +#if SEND_GREE + void send(const uint16_t repeat = kGreeDefaultRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } +#endif // SEND_GREE + void begin(void); + void on(void); + void off(void); + void setModel(const gree_ac_remote_model_t model); + gree_ac_remote_model_t getModel(void); + void setPower(const bool on); + bool getPower(void); + void setTemp(const uint8_t temp); + uint8_t getTemp(void); + void setFan(const uint8_t speed); + uint8_t getFan(void); + void setMode(const uint8_t new_mode); + uint8_t getMode(void); + void setLight(const bool on); + bool getLight(void); + void setXFan(const bool on); + bool getXFan(void); + void setSleep(const bool on); + bool getSleep(void); + void setTurbo(const bool on); + bool getTurbo(void); + void setIFeel(const bool on); + bool getIFeel(void); + void setWiFi(const bool on); + bool getWiFi(void); + void setSwingVertical(const bool automatic, const uint8_t position); + bool getSwingVerticalAuto(void); + uint8_t getSwingVerticalPosition(void); + uint16_t getTimer(void); + void setTimer(const uint16_t minutes); + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); + uint8_t convertSwingV(const stdAc::swingv_t swingv); + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + static stdAc::swingv_t toCommonSwingV(const uint8_t pos); + stdAc::state_t toCommon(void); + uint8_t* getRaw(void); + void setRaw(const uint8_t new_code[]); + static bool validChecksum(const uint8_t state[], + const uint16_t length = kGreeStateLength); + String toString(void); +#ifndef UNIT_TEST + + private: + IRsend _irsend; +#else // UNIT_TEST + IRsendTest _irsend; +#endif // UNIT_TEST + // The state of the IR remote in IR code form. + uint8_t remote_state[kGreeStateLength]; + gree_ac_remote_model_t _model; + void checksum(const uint16_t length = kGreeStateLength); + void fixup(void); + void setTimerEnabled(const bool on); + bool getTimerEnabled(void); +}; + +#endif // IR_GREE_H_ diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Haier.cpp b/lib/IRremoteESP8266-2.6.5/src/ir_Haier.cpp similarity index 66% rename from lib/IRremoteESP8266-2.6.0/src/ir_Haier.cpp rename to lib/IRremoteESP8266-2.6.5/src/ir_Haier.cpp index f76bb3447..d2b947f9e 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Haier.cpp +++ b/lib/IRremoteESP8266-2.6.5/src/ir_Haier.cpp @@ -1,4 +1,5 @@ // Copyright 2018 crankyoldgit +// Code to emulate Haier protocol compatible devices. // The specifics of reverse engineering the protocols details: // * HSU07-HEA03 by kuzin2006. // * YR-W02/HSU-09HMC203 by non7top. @@ -6,27 +7,19 @@ #include "ir_Haier.h" #ifndef UNIT_TEST #include -#else -#include #endif #include "IRremoteESP8266.h" #include "IRutils.h" -// HH HH AAA IIIII EEEEEEE RRRRRR -// HH HH AAAAA III EE RR RR -// HHHHHHH AA AA III EEEEE RRRRRR -// HH HH AAAAAAA III EE RR RR -// HH HH AA AA IIIII EEEEEEE RR RR - // Supported devices: // * Haier HSU07-HEA03 Remote control. // * Haier YR-W02 Remote control // * Haier HSU-09HMC203 A/C unit. // Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/404 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/404 // https://www.dropbox.com/s/mecyib3lhdxc8c6/IR%20data%20reverse%20engineering.xlsx?dl=0 -// https://github.com/markszabo/IRremoteESP8266/issues/485 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/485 // https://www.dropbox.com/sh/w0bt7egp0fjger5/AADRFV6Wg4wZskJVdFvzb8Z0a?dl=0&preview=haer2.ods // Constants @@ -37,6 +30,14 @@ const uint16_t kHaierAcOneSpace = 1650; const uint16_t kHaierAcZeroSpace = 650; const uint32_t kHaierAcMinGap = 150000; // Completely made up value. +using irutils::addBoolToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addFanToString; +using irutils::addTempToString; +using irutils::minsToString; + #if (SEND_HAIER_AC || SEND_HAIER_AC_YRW02) // Send a Haier A/C message. (HSU07-HEA03 remote) // @@ -47,8 +48,8 @@ const uint32_t kHaierAcMinGap = 150000; // Completely made up value. // // Status: STABLE / Known to be working. // -void IRsend::sendHaierAC(unsigned char data[], uint16_t nbytes, - uint16_t repeat) { +void IRsend::sendHaierAC(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { if (nbytes < kHaierACStateLength) return; for (uint16_t r = 0; r <= repeat; r++) { @@ -74,16 +75,18 @@ void IRsend::sendHaierAC(unsigned char data[], uint16_t nbytes, // // Status: Alpha / Untested on a real device. // -void IRsend::sendHaierACYRW02(unsigned char data[], uint16_t nbytes, - uint16_t repeat) { +void IRsend::sendHaierACYRW02(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { if (nbytes >= kHaierACYRW02StateLength) sendHaierAC(data, nbytes, repeat); } #endif // SEND_HAIER_AC_YRW02 // Class for emulating a Haier HSU07-HEA03 remote -IRHaierAC::IRHaierAC(uint16_t pin) : _irsend(pin) { stateReset(); } +IRHaierAC::IRHaierAC(const uint16_t pin, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { stateReset(); } -void IRHaierAC::begin() { _irsend.begin(); } +void IRHaierAC::begin(void) { _irsend.begin(); } #if SEND_HAIER_AC void IRHaierAC::send(const uint16_t repeat) { @@ -92,7 +95,7 @@ void IRHaierAC::send(const uint16_t repeat) { } #endif // SEND_HAIER_AC -void IRHaierAC::checksum() { +void IRHaierAC::checksum(void) { remote_state[8] = sumBytes(remote_state, kHaierACStateLength - 1); } @@ -101,13 +104,12 @@ bool IRHaierAC::validChecksum(uint8_t state[], const uint16_t length) { return (state[length - 1] == sumBytes(state, length - 1)); } -void IRHaierAC::stateReset() { +void IRHaierAC::stateReset(void) { for (uint8_t i = 1; i < kHaierACStateLength; i++) remote_state[i] = 0x0; remote_state[0] = kHaierAcPrefix; remote_state[2] = 0x20; remote_state[4] = 0x0C; remote_state[5] = 0xC0; - remote_state[6] = 0x20; setTemp(kHaierAcDefTemp); setFan(kHaierAcFanAuto); @@ -115,18 +117,18 @@ void IRHaierAC::stateReset() { setCommand(kHaierAcCmdOn); } -uint8_t* IRHaierAC::getRaw() { +uint8_t* IRHaierAC::getRaw(void) { checksum(); return remote_state; } -void IRHaierAC::setRaw(uint8_t new_code[]) { +void IRHaierAC::setRaw(const uint8_t new_code[]) { for (uint8_t i = 0; i < kHaierACStateLength; i++) { remote_state[i] = new_code[i]; } } -void IRHaierAC::setCommand(uint8_t state) { +void IRHaierAC::setCommand(const uint8_t state) { remote_state[1] &= 0b11110000; switch (state) { case kHaierAcCmdOff: @@ -144,9 +146,9 @@ void IRHaierAC::setCommand(uint8_t state) { } } -uint8_t IRHaierAC::getCommand() { return remote_state[1] & (0b00001111); } +uint8_t IRHaierAC::getCommand(void) { return remote_state[1] & (0b00001111); } -void IRHaierAC::setFan(uint8_t speed) { +void IRHaierAC::setFan(const uint8_t speed) { uint8_t new_speed = kHaierAcFanAuto; switch (speed) { case kHaierAcFanLow: @@ -167,7 +169,7 @@ void IRHaierAC::setFan(uint8_t speed) { remote_state[5] |= new_speed; } -uint8_t IRHaierAC::getFan() { +uint8_t IRHaierAC::getFan(void) { switch (remote_state[5] & 0b00000011) { case 1: return kHaierAcFanMed; @@ -185,11 +187,13 @@ void IRHaierAC::setMode(uint8_t mode) { setCommand(kHaierAcCmdMode); if (mode > kHaierAcFan) // If out of range, default to auto mode. new_mode = kHaierAcAuto; - remote_state[7] &= 0b00011111; - remote_state[7] |= (new_mode << 5); + remote_state[6] &= ~kHaierAcModeMask; + remote_state[6] |= (new_mode << 5); } -uint8_t IRHaierAC::getMode() { return (remote_state[7] & 0b11100000) >> 5; } +uint8_t IRHaierAC::getMode(void) { + return (remote_state[6] & kHaierAcModeMask) >> 5; +} void IRHaierAC::setTemp(const uint8_t celsius) { uint8_t temp = celsius; @@ -209,45 +213,47 @@ void IRHaierAC::setTemp(const uint8_t celsius) { remote_state[1] |= ((temp - kHaierAcMinTemp) << 4); } -uint8_t IRHaierAC::getTemp() { +uint8_t IRHaierAC::getTemp(void) { return ((remote_state[1] & 0b11110000) >> 4) + kHaierAcMinTemp; } -void IRHaierAC::setHealth(bool state) { +void IRHaierAC::setHealth(const bool on) { setCommand(kHaierAcCmdHealth); remote_state[4] &= 0b11011111; - remote_state[4] |= (state << 5); + remote_state[4] |= (on << 5); } bool IRHaierAC::getHealth(void) { return remote_state[4] & (1 << 5); } -void IRHaierAC::setSleep(bool state) { +void IRHaierAC::setSleep(const bool on) { setCommand(kHaierAcCmdSleep); - remote_state[7] &= 0b10111111; - remote_state[7] |= (state << 6); + if (on) + remote_state[7] |= kHaierAcSleepBit; + else + remote_state[7] &= ~kHaierAcSleepBit; } -bool IRHaierAC::getSleep(void) { return remote_state[7] & 0b01000000; } +bool IRHaierAC::getSleep(void) { return remote_state[7] & kHaierAcSleepBit; } uint16_t IRHaierAC::getTime(const uint8_t ptr[]) { return (ptr[0] & 0b00011111) * 60 + (ptr[1] & 0b00111111); } -int16_t IRHaierAC::getOnTimer() { +int16_t IRHaierAC::getOnTimer(void) { if (remote_state[3] & 0b10000000) // Check if the timer is turned on. return getTime(remote_state + 6); else return -1; } -int16_t IRHaierAC::getOffTimer() { +int16_t IRHaierAC::getOffTimer(void) { if (remote_state[3] & 0b01000000) // Check if the timer is turned on. return getTime(remote_state + 4); else return -1; } -uint16_t IRHaierAC::getCurrTime() { return getTime(remote_state + 2); } +uint16_t IRHaierAC::getCurrTime(void) { return getTime(remote_state + 2); } void IRHaierAC::setTime(uint8_t ptr[], const uint16_t nr_mins) { uint16_t mins = nr_mins; @@ -273,7 +279,7 @@ void IRHaierAC::setOffTimer(const uint16_t nr_mins) { setTime(remote_state + 4, nr_mins); } -void IRHaierAC::cancelTimers() { +void IRHaierAC::cancelTimers(void) { setCommand(kHaierAcCmdTimerCancel); remote_state[3] &= 0b00111111; } @@ -282,7 +288,9 @@ void IRHaierAC::setCurrTime(const uint16_t nr_mins) { setTime(remote_state + 2, nr_mins); } -uint8_t IRHaierAC::getSwing() { return (remote_state[2] & 0b11000000) >> 6; } +uint8_t IRHaierAC::getSwing(void) { + return (remote_state[2] & 0b11000000) >> 6; +} void IRHaierAC::setSwing(const uint8_t state) { if (state == getSwing()) return; // Nothing to do. @@ -298,23 +306,6 @@ void IRHaierAC::setSwing(const uint8_t state) { } } -// Convert a Haier time into a human readable string. -#ifdef ARDUINO -String IRHaierAC::timeToString(const uint16_t nr_mins) { - String result = ""; -#else -std::string IRHaierAC::timeToString(const uint16_t nr_mins) { - std::string result = ""; -#endif // ARDUINO - - if (nr_mins / 24 < 10) result += '0'; // Zero pad. - result += uint64ToString(nr_mins / 60); - result += ':'; - if (nr_mins % 60 < 10) result += '0'; // Zero pad. - result += uint64ToString(nr_mins % 60); - return result; -} - // Convert a standard A/C mode into its native mode. uint8_t IRHaierAC::convertMode(const stdAc::opmode_t mode) { switch (mode) { @@ -364,17 +355,69 @@ uint8_t IRHaierAC::convertSwingV(const stdAc::swingv_t position) { } } +// Convert a native mode to it's common equivalent. +stdAc::opmode_t IRHaierAC::toCommonMode(const uint8_t mode) { + switch (mode) { + case kHaierAcCool: return stdAc::opmode_t::kCool; + case kHaierAcHeat: return stdAc::opmode_t::kHeat; + case kHaierAcDry: return stdAc::opmode_t::kDry; + case kHaierAcFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRHaierAC::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kHaierAcFanHigh: return stdAc::fanspeed_t::kMax; + case kHaierAcFanMed: return stdAc::fanspeed_t::kMedium; + case kHaierAcFanLow: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +// Convert a native vertical swing to it's common equivalent. +stdAc::swingv_t IRHaierAC::toCommonSwingV(const uint8_t pos) { + switch (pos) { + case kHaierAcSwingUp: return stdAc::swingv_t::kHighest; + case kHaierAcSwingDown: return stdAc::swingv_t::kLowest; + case kHaierAcSwingOff: return stdAc::swingv_t::kOff; + default: return stdAc::swingv_t::kAuto; + } +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRHaierAC::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::HAIER_AC; + result.model = -1; // No models used. + result.power = true; + if (this->getCommand() == kHaierAcCmdOff) result.power = false; + result.mode = this->toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(); + result.fanspeed = this->toCommonFanSpeed(this->getFan()); + result.swingv = this->toCommonSwingV(this->getSwing()); + result.filter = this->getHealth(); + result.sleep = this->getSleep() ? 0 : -1; + // Not supported. + result.swingh = stdAc::swingh_t::kOff; + result.quiet = false; + result.turbo = false; + result.econo = false; + result.light = false; + result.clean = false; + result.beep = false; + result.clock = -1; + return result; +} + // Convert the internal state into a human readable string. -#ifdef ARDUINO -String IRHaierAC::toString() { +String IRHaierAC::toString(void) { String result = ""; -#else -std::string IRHaierAC::toString() { - std::string result = ""; -#endif // ARDUINO + result.reserve(150); // Reserve some heap for the string to reduce fragging. uint8_t cmd = getCommand(); - result += F("Command: "); - result += uint64ToString(cmd); + result += addIntToString(cmd, F("Command"), false); result += F(" ("); switch (cmd) { case kHaierAcCmdOff: @@ -414,41 +457,12 @@ std::string IRHaierAC::toString() { result += F("Unknown"); } result += ')'; - result += F(", Mode: "); - result += uint64ToString(getMode()); - switch (getMode()) { - case kHaierAcAuto: - result += F(" (AUTO)"); - break; - case kHaierAcCool: - result += F(" (COOL)"); - break; - case kHaierAcHeat: - result += F(" (HEAT)"); - break; - case kHaierAcDry: - result += F(" (DRY)"); - break; - case kHaierAcFan: - result += F(" (FAN)"); - break; - default: - result += F(" (UNKNOWN)"); - } - result += F(", Temp: "); - result += uint64ToString(getTemp()); - result += F("C, Fan: "); - result += uint64ToString(getFan()); - switch (getFan()) { - case kHaierAcFanAuto: - result += F(" (AUTO)"); - break; - case kHaierAcFanHigh: - result += F(" (MAX)"); - break; - } - result += F(", Swing: "); - result += uint64ToString(getSwing()); + result += addModeToString(getMode(), kHaierAcAuto, kHaierAcCool, kHaierAcHeat, + kHaierAcDry, kHaierAcFan); + result += addTempToString(getTemp()); + result += addFanToString(getFan(), kHaierAcFanHigh, kHaierAcFanLow, + kHaierAcFanAuto, kHaierAcFanAuto, kHaierAcFanMed); + result += addIntToString(getSwing(), F("Swing")); result += F(" ("); switch (getSwing()) { case kHaierAcSwingOff: @@ -467,37 +481,24 @@ std::string IRHaierAC::toString() { result += F("Unknown"); } result += ')'; - result += F(", Sleep: "); - if (getSleep()) - result += F("On"); - else - result += F("Off"); - result += F(", Health: "); - if (getHealth()) - result += F("On"); - else - result += F("Off"); - result += F(", Current Time: "); - result += timeToString(getCurrTime()); - result += F(", On Timer: "); - if (getOnTimer() >= 0) - result += timeToString(getOnTimer()); - else - result += F("Off"); - result += F(", Off Timer: "); - if (getOffTimer() >= 0) - result += timeToString(getOffTimer()); - else - result += F("Off"); - + result += addBoolToString(getSleep(), F("Sleep")); + result += addBoolToString(getHealth(), F("Health")); + result += addLabeledString(minsToString(getCurrTime()), F("Current Time")); + result += addLabeledString( + getOnTimer() >= 0 ? minsToString(getOnTimer()) : F("Off"), F("On Timer")); + result += addLabeledString( + getOffTimer() >= 0 ? minsToString(getOffTimer()) : F("Off"), + F("Off Timer")); return result; } // End of IRHaierAC class. // Class for emulating a Haier YRW02 remote -IRHaierACYRW02::IRHaierACYRW02(uint16_t pin) : _irsend(pin) { stateReset(); } +IRHaierACYRW02::IRHaierACYRW02(const uint16_t pin, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { stateReset(); } -void IRHaierACYRW02::begin() { _irsend.begin(); } +void IRHaierACYRW02::begin(void) { _irsend.begin(); } #if SEND_HAIER_AC_YRW02 void IRHaierACYRW02::send(const uint16_t repeat) { @@ -506,7 +507,7 @@ void IRHaierACYRW02::send(const uint16_t repeat) { } #endif // SEND_HAIER_AC_YRW02 -void IRHaierACYRW02::checksum() { +void IRHaierACYRW02::checksum(void) { remote_state[kHaierACYRW02StateLength - 1] = sumBytes(remote_state, kHaierACYRW02StateLength - 1); } @@ -516,7 +517,7 @@ bool IRHaierACYRW02::validChecksum(uint8_t state[], const uint16_t length) { return (state[length - 1] == sumBytes(state, length - 1)); } -void IRHaierACYRW02::stateReset() { +void IRHaierACYRW02::stateReset(void) { for (uint8_t i = 1; i < kHaierACYRW02StateLength; i++) remote_state[i] = 0x0; remote_state[0] = kHaierAcYrw02Prefix; @@ -530,12 +531,12 @@ void IRHaierACYRW02::stateReset() { setPower(true); } -uint8_t* IRHaierACYRW02::getRaw() { +uint8_t* IRHaierACYRW02::getRaw(void) { checksum(); return remote_state; } -void IRHaierACYRW02::setRaw(uint8_t new_code[]) { +void IRHaierACYRW02::setRaw(const uint8_t new_code[]) { for (uint8_t i = 0; i < kHaierACYRW02StateLength; i++) { remote_state[i] = new_code[i]; } @@ -557,7 +558,9 @@ void IRHaierACYRW02::setButton(uint8_t button) { } } -uint8_t IRHaierACYRW02::getButton() { return remote_state[12] & (0b00001111); } +uint8_t IRHaierACYRW02::getButton(void) { + return remote_state[12] & 0b00001111; +} void IRHaierACYRW02::setMode(uint8_t mode) { uint8_t new_mode = mode; @@ -576,10 +579,10 @@ void IRHaierACYRW02::setMode(uint8_t mode) { remote_state[7] |= (new_mode << 4); } -uint8_t IRHaierACYRW02::getMode() { return remote_state[7] >> 4; } +uint8_t IRHaierACYRW02::getMode(void) { return remote_state[7] >> 4; } -void IRHaierACYRW02::setTemp(const uint8_t celcius) { - uint8_t temp = celcius; +void IRHaierACYRW02::setTemp(const uint8_t celsius) { + uint8_t temp = celsius; if (temp < kHaierAcMinTemp) temp = kHaierAcMinTemp; else if (temp > kHaierAcMaxTemp) @@ -596,43 +599,47 @@ void IRHaierACYRW02::setTemp(const uint8_t celcius) { remote_state[1] |= ((temp - kHaierAcMinTemp) << 4); } -uint8_t IRHaierACYRW02::getTemp() { +uint8_t IRHaierACYRW02::getTemp(void) { return ((remote_state[1] & 0b11110000) >> 4) + kHaierAcMinTemp; } -void IRHaierACYRW02::setHealth(bool state) { +void IRHaierACYRW02::setHealth(const bool on) { setButton(kHaierAcYrw02ButtonHealth); remote_state[3] &= 0b11111101; - remote_state[3] |= (state << 1); + remote_state[3] |= (on << 1); } bool IRHaierACYRW02::getHealth(void) { return remote_state[3] & 0b00000010; } -bool IRHaierACYRW02::getPower() { return remote_state[4] & kHaierAcYrw02Power; } +bool IRHaierACYRW02::getPower(void) { + return remote_state[4] & kHaierAcYrw02Power; +} -void IRHaierACYRW02::setPower(bool state) { +void IRHaierACYRW02::setPower(const bool on) { setButton(kHaierAcYrw02ButtonPower); - if (state) + if (on) remote_state[4] |= kHaierAcYrw02Power; else remote_state[4] &= ~kHaierAcYrw02Power; } -void IRHaierACYRW02::on() { setPower(true); } +void IRHaierACYRW02::on(void) { setPower(true); } -void IRHaierACYRW02::off() { setPower(false); } +void IRHaierACYRW02::off(void) { setPower(false); } -bool IRHaierACYRW02::getSleep() { return remote_state[8] & kHaierAcYrw02Sleep; } +bool IRHaierACYRW02::getSleep(void) { + return remote_state[8] & kHaierAcYrw02Sleep; +} -void IRHaierACYRW02::setSleep(bool state) { +void IRHaierACYRW02::setSleep(const bool on) { setButton(kHaierAcYrw02ButtonSleep); - if (state) + if (on) remote_state[8] |= kHaierAcYrw02Sleep; else remote_state[8] &= ~kHaierAcYrw02Sleep; } -uint8_t IRHaierACYRW02::getTurbo() { return remote_state[6] >> 6; } +uint8_t IRHaierACYRW02::getTurbo(void) { return remote_state[6] >> 6; } void IRHaierACYRW02::setTurbo(uint8_t speed) { switch (speed) { @@ -645,7 +652,7 @@ void IRHaierACYRW02::setTurbo(uint8_t speed) { } } -uint8_t IRHaierACYRW02::getFan() { return remote_state[5] >> 4; } +uint8_t IRHaierACYRW02::getFan(void) { return remote_state[5] >> 4; } void IRHaierACYRW02::setFan(uint8_t speed) { switch (speed) { @@ -659,7 +666,7 @@ void IRHaierACYRW02::setFan(uint8_t speed) { } } -uint8_t IRHaierACYRW02::getSwing() { return remote_state[1] & 0b00001111; } +uint8_t IRHaierACYRW02::getSwing(void) { return remote_state[1] & 0b00001111; } void IRHaierACYRW02::setSwing(uint8_t state) { uint8_t newstate = state; @@ -739,22 +746,71 @@ uint8_t IRHaierACYRW02::convertSwingV(const stdAc::swingv_t position) { } } +// Convert a native mode to it's common equivalent. +stdAc::opmode_t IRHaierACYRW02::toCommonMode(const uint8_t mode) { + switch (mode) { + case kHaierAcYrw02Cool: return stdAc::opmode_t::kCool; + case kHaierAcYrw02Heat: return stdAc::opmode_t::kHeat; + case kHaierAcYrw02Dry: return stdAc::opmode_t::kDry; + case kHaierAcYrw02Fan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRHaierACYRW02::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kHaierAcYrw02FanHigh: return stdAc::fanspeed_t::kMax; + case kHaierAcYrw02FanMed: return stdAc::fanspeed_t::kMedium; + case kHaierAcYrw02FanLow: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +// Convert a native vertical swing to it's common equivalent. +stdAc::swingv_t IRHaierACYRW02::toCommonSwingV(const uint8_t pos) { + switch (pos) { + case kHaierAcYrw02SwingTop: return stdAc::swingv_t::kHighest; + case kHaierAcYrw02SwingMiddle: return stdAc::swingv_t::kMiddle; + case kHaierAcYrw02SwingDown: return stdAc::swingv_t::kLow; + case kHaierAcYrw02SwingBottom: return stdAc::swingv_t::kLowest; + case kHaierAcYrw02SwingOff: return stdAc::swingv_t::kOff; + default: return stdAc::swingv_t::kAuto; + } +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRHaierACYRW02::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::HAIER_AC_YRW02; + result.model = -1; // No models used. + result.power = this->getPower(); + result.mode = this->toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(); + result.fanspeed = this->toCommonFanSpeed(this->getFan()); + result.swingv = this->toCommonSwingV(this->getSwing()); + result.filter = this->getHealth(); + result.sleep = this->getSleep() ? 0 : -1; + // Not supported. + result.swingh = stdAc::swingh_t::kOff; + result.quiet = false; + result.turbo = false; + result.econo = false; + result.light = false; + result.clean = false; + result.beep = false; + result.clock = -1; + return result; +} + // Convert the internal state into a human readable string. -#ifdef ARDUINO -String IRHaierACYRW02::toString() { +String IRHaierACYRW02::toString(void) { String result = ""; -#else -std::string IRHaierACYRW02::toString() { - std::string result = ""; -#endif // ARDUINO - result += F("Power: "); - if (getPower()) - result += F("On"); - else - result += F("Off"); + result.reserve(130); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(getPower(), F("Power"), false); uint8_t cmd = getButton(); - result += F(", Button: "); - result += uint64ToString(cmd); + result += addIntToString(cmd, F("Button")); result += F(" ("); switch (cmd) { case kHaierAcYrw02ButtonPower: @@ -788,49 +844,14 @@ std::string IRHaierACYRW02::toString() { result += F("Unknown"); } result += ')'; - result += F(", Mode: "); - result += uint64ToString(getMode()); - switch (getMode()) { - case kHaierAcYrw02Auto: - result += F(" (Auto)"); - break; - case kHaierAcYrw02Cool: - result += F(" (Cool)"); - break; - case kHaierAcYrw02Heat: - result += F(" (Heat)"); - break; - case kHaierAcYrw02Dry: - result += F(" (Dry)"); - break; - case kHaierAcYrw02Fan: - result += F(" (Fan)"); - break; - default: - result += F(" (UNKNOWN)"); - } - result += F(", Temp: "); - result += uint64ToString(getTemp()); - result += F("C, Fan: "); - result += uint64ToString(getFan()); - switch (getFan()) { - case kHaierAcYrw02FanAuto: - result += F(" (Auto)"); - break; - case kHaierAcYrw02FanHigh: - result += F(" (High)"); - break; - case kHaierAcYrw02FanLow: - result += F(" (Low)"); - break; - case kHaierAcYrw02FanMed: - result += F(" (Med)"); - break; - default: - result += F(" (Unknown)"); - } - result += F(", Turbo: "); - result += uint64ToString(getTurbo()); + result += addModeToString(getMode(), kHaierAcYrw02Auto, kHaierAcYrw02Cool, + kHaierAcYrw02Heat, kHaierAcYrw02Dry, + kHaierAcYrw02Fan); + result += addTempToString(getTemp()); + result += addFanToString(getFan(), kHaierAcYrw02FanHigh, kHaierAcYrw02FanLow, + kHaierAcYrw02FanAuto, kHaierAcYrw02FanAuto, + kHaierAcYrw02FanMed); + result += addIntToString(getTurbo(), F("Turbo")); result += F(" ("); switch (getTurbo()) { case kHaierAcYrw02TurboOff: @@ -846,8 +867,7 @@ std::string IRHaierACYRW02::toString() { result += F("Unknown"); } result += ')'; - result += F(", Swing: "); - result += uint64ToString(getSwing()); + result += addIntToString(getSwing(), F("Swing")); result += F(" ("); switch (getSwing()) { case kHaierAcYrw02SwingOff: @@ -872,17 +892,8 @@ std::string IRHaierACYRW02::toString() { result += F("Unknown"); } result += ')'; - result += F(", Sleep: "); - if (getSleep()) - result += F("On"); - else - result += F("Off"); - result += F(", Health: "); - if (getHealth()) - result += F("On"); - else - result += F("Off"); - + result += addBoolToString(getSleep(), F("Sleep")); + result += addBoolToString(getHealth(), F("Health")); return result; } // End of IRHaierACYRW02 class. @@ -901,9 +912,6 @@ std::string IRHaierACYRW02::toString() { // bool IRrecv::decodeHaierAC(decode_results* results, uint16_t nbits, bool strict) { - if (nbits % 8 != 0) // nbits has to be a multiple of nr. of bits in a byte. - return false; - if (strict) { if (nbits != kHaierACBits) return false; // Not strictly a HAIER_AC message. @@ -914,27 +922,18 @@ bool IRrecv::decodeHaierAC(decode_results* results, uint16_t nbits, uint16_t offset = kStartOffset; - // Header + // Pre-Header if (!matchMark(results->rawbuf[offset++], kHaierAcHdr)) return false; if (!matchSpace(results->rawbuf[offset++], kHaierAcHdr)) return false; - if (!matchMark(results->rawbuf[offset++], kHaierAcHdr)) return false; - if (!matchSpace(results->rawbuf[offset++], kHaierAcHdrGap)) return false; - // Data - for (uint16_t i = 0; i < nbits / 8; i++) { - match_result_t data_result = - matchData(&(results->rawbuf[offset]), 8, kHaierAcBitMark, - kHaierAcOneSpace, kHaierAcBitMark, kHaierAcZeroSpace); - if (data_result.success == false) return false; - offset += data_result.used; - results->state[i] = (uint8_t)data_result.data; - } - - // Footer - if (!matchMark(results->rawbuf[offset++], kHaierAcBitMark)) return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset++], kHaierAcMinGap)) - return false; + // Match Header + Data + Footer + if (!matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, nbits, + kHaierAcHdr, kHaierAcHdrGap, + kHaierAcBitMark, kHaierAcOneSpace, + kHaierAcBitMark, kHaierAcZeroSpace, + kHaierAcBitMark, kHaierAcMinGap, true, + _tolerance, kMarkExcess)) return false; // Compliance if (strict) { diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Haier.h b/lib/IRremoteESP8266-2.6.5/src/ir_Haier.h similarity index 78% rename from lib/IRremoteESP8266-2.6.0/src/ir_Haier.h rename to lib/IRremoteESP8266-2.6.5/src/ir_Haier.h index 8f7b35196..ea5f67ab3 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Haier.h +++ b/lib/IRremoteESP8266-2.6.5/src/ir_Haier.h @@ -1,13 +1,16 @@ // Copyright 2018 crankyoldgit // The specifics of reverse engineering the protocol details by kuzin2006 +// Supports: +// Brand: Haier, Model: HSU07-HEA03 remote +// Brand: Haier, Model: YR-W02 remote +// Brand: Haier, Model: HSU-09HMC203 A/C + #ifndef IR_HAIER_H_ #define IR_HAIER_H_ #ifndef UNIT_TEST #include -#else -#include #endif #include "IRremoteESP8266.h" #include "IRsend.h" @@ -15,16 +18,10 @@ #include "IRsend_test.h" #endif -// HH HH AAA IIIII EEEEEEE RRRRRR -// HH HH AAAAA III EE RR RR -// HHHHHHH AA AA III EEEEE RRRRRR -// HH HH AAAAAAA III EE RR RR -// HH HH AA AA IIIII EEEEEEE RR RR - // Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/404 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/404 // https://www.dropbox.com/s/mecyib3lhdxc8c6/IR%20data%20reverse%20engineering.xlsx?dl=0 -// https://github.com/markszabo/IRremoteESP8266/issues/485 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/485 // https://www.dropbox.com/sh/w0bt7egp0fjger5/AADRFV6Wg4wZskJVdFvzb8Z0a?dl=0&preview=haer2.ods // Constants @@ -56,6 +53,7 @@ const uint8_t kHaierAcSwingDown = 0b00000010; const uint8_t kHaierAcSwingChg = 0b00000011; // Byte 6 +const uint8_t kHaierAcModeMask = 0b11100000; const uint8_t kHaierAcAuto = 0; const uint8_t kHaierAcCool = 1; const uint8_t kHaierAcDry = 2; @@ -69,6 +67,9 @@ const uint8_t kHaierAcFanHigh = 3; const uint16_t kHaierAcMaxTime = (23 * 60) + 59; +// Byte 7 +const uint8_t kHaierAcSleepBit = 0b01000000; + // Legacy Haier AC defines. #define HAIER_AC_MIN_TEMP kHaierAcMinTemp #define HAIER_AC_DEF_TEMP kHaierAcDefTemp @@ -186,57 +187,56 @@ const uint8_t kHaierAcYrw02ButtonSleep = 0xB; class IRHaierAC { public: - explicit IRHaierAC(uint16_t pin); + explicit IRHaierAC(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); #if SEND_HAIER_AC void send(const uint16_t repeat = kHaierAcDefaultRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } #endif // SEND_HAIER_AC - void begin(); + void begin(void); void setCommand(const uint8_t command); - uint8_t getCommand(); + uint8_t getCommand(void); void setTemp(const uint8_t temp); - uint8_t getTemp(); + uint8_t getTemp(void); void setFan(const uint8_t speed); - uint8_t getFan(); + uint8_t getFan(void); - uint8_t getMode(); + uint8_t getMode(void); void setMode(const uint8_t mode); - bool getSleep(); - void setSleep(const bool state); - bool getHealth(); - void setHealth(const bool state); + bool getSleep(void); + void setSleep(const bool on); + bool getHealth(void); + void setHealth(const bool on); - int16_t getOnTimer(); + int16_t getOnTimer(void); void setOnTimer(const uint16_t mins); - int16_t getOffTimer(); + int16_t getOffTimer(void); void setOffTimer(const uint16_t mins); - void cancelTimers(); + void cancelTimers(void); - uint16_t getCurrTime(); + uint16_t getCurrTime(void); void setCurrTime(const uint16_t mins); - uint8_t getSwing(); + uint8_t getSwing(void); void setSwing(const uint8_t state); - uint8_t* getRaw(); - void setRaw(uint8_t new_code[]); + uint8_t* getRaw(void); + void setRaw(const uint8_t new_code[]); static bool validChecksum(uint8_t state[], const uint16_t length = kHaierACStateLength); uint8_t convertMode(const stdAc::opmode_t mode); uint8_t convertFan(const stdAc::fanspeed_t speed); uint8_t convertSwingV(const stdAc::swingv_t position); - -#ifdef ARDUINO - String toString(); - static String timeToString(const uint16_t nr_mins); -#else - std::string toString(); - static std::string timeToString(const uint16_t nr_mins); -#endif + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + static stdAc::swingv_t toCommonSwingV(const uint8_t pos); + stdAc::state_t toCommon(void); + String toString(void); #ifndef UNIT_TEST private: @@ -245,61 +245,62 @@ class IRHaierAC { IRsendTest _irsend; #endif uint8_t remote_state[kHaierACStateLength]; - void stateReset(); - void checksum(); + void stateReset(void); + void checksum(void); static uint16_t getTime(const uint8_t ptr[]); static void setTime(uint8_t ptr[], const uint16_t nr_mins); }; class IRHaierACYRW02 { public: - explicit IRHaierACYRW02(uint16_t pin); + explicit IRHaierACYRW02(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); #if SEND_HAIER_AC_YRW02 void send(const uint16_t repeat = kHaierAcYrw02DefaultRepeat); #endif // SEND_HAIER_AC_YRW02 - void begin(); + void begin(void); void setButton(const uint8_t button); - uint8_t getButton(); + uint8_t getButton(void); void setTemp(const uint8_t temp); - uint8_t getTemp(); + uint8_t getTemp(void); void setFan(const uint8_t speed); - uint8_t getFan(); + uint8_t getFan(void); - uint8_t getMode(); + uint8_t getMode(void); void setMode(const uint8_t mode); - bool getPower(); - void setPower(const bool state); - void on(); - void off(); + bool getPower(void); + void setPower(const bool on); + void on(void); + void off(void); - bool getSleep(); - void setSleep(const bool state); - bool getHealth(); - void setHealth(const bool state); + bool getSleep(void); + void setSleep(const bool on); + bool getHealth(void); + void setHealth(const bool on); - uint8_t getTurbo(); + uint8_t getTurbo(void); void setTurbo(const uint8_t speed); - uint8_t getSwing(); + uint8_t getSwing(void); void setSwing(const uint8_t state); - uint8_t* getRaw(); - void setRaw(uint8_t new_code[]); + uint8_t* getRaw(void); + void setRaw(const uint8_t new_code[]); static bool validChecksum(uint8_t state[], const uint16_t length = kHaierACYRW02StateLength); uint8_t convertMode(const stdAc::opmode_t mode); uint8_t convertFan(const stdAc::fanspeed_t speed); uint8_t convertSwingV(const stdAc::swingv_t position); -#ifdef ARDUINO - String toString(); -#else - std::string toString(); -#endif + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + static stdAc::swingv_t toCommonSwingV(const uint8_t pos); + stdAc::state_t toCommon(void); + String toString(void); #ifndef UNIT_TEST private: @@ -308,8 +309,8 @@ class IRHaierACYRW02 { IRsendTest _irsend; #endif uint8_t remote_state[kHaierACYRW02StateLength]; - void stateReset(); - void checksum(); + void stateReset(void); + void checksum(void); }; #endif // IR_HAIER_H_ diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Hitachi.cpp b/lib/IRremoteESP8266-2.6.5/src/ir_Hitachi.cpp similarity index 63% rename from lib/IRremoteESP8266-2.6.0/src/ir_Hitachi.cpp rename to lib/IRremoteESP8266-2.6.5/src/ir_Hitachi.cpp index b88189f4a..0550816a9 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Hitachi.cpp +++ b/lib/IRremoteESP8266-2.6.5/src/ir_Hitachi.cpp @@ -15,14 +15,8 @@ #include "IRsend.h" #include "IRutils.h" -// HH HH IIIII TTTTTTT AAA CCCCC HH HH IIIII -// HH HH III TTT AAAAA CC C HH HH III -// HHHHHHH III TTT AA AA CC HHHHHHH III -// HH HH III TTT AAAAAAA CC C HH HH III -// HH HH IIIII TTT AA AA CCCCC HH HH IIIII - // Constants -// Ref: https://github.com/markszabo/IRremoteESP8266/issues/417 +// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/417 const uint16_t kHitachiAcHdrMark = 3300; const uint16_t kHitachiAcHdrSpace = 1700; const uint16_t kHitachiAc1HdrMark = 3400; @@ -32,6 +26,13 @@ const uint16_t kHitachiAcOneSpace = 1250; const uint16_t kHitachiAcZeroSpace = 500; const uint32_t kHitachiAcMinGap = kDefaultMessageGap; // Just a guess. +using irutils::addBoolToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addFanToString; +using irutils::addTempToString; + #if (SEND_HITACHI_AC || SEND_HITACHI_AC2) // Send a Hitachi A/C message. // @@ -43,9 +44,9 @@ const uint32_t kHitachiAcMinGap = kDefaultMessageGap; // Just a guess. // Status: ALPHA / Untested. // // Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/417 -void IRsend::sendHitachiAC(unsigned char data[], uint16_t nbytes, - uint16_t repeat) { +// https://github.com/crankyoldgit/IRremoteESP8266/issues/417 +void IRsend::sendHitachiAC(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { if (nbytes < kHitachiAcStateLength) return; // Not enough bytes to send a proper message. sendGeneric(kHitachiAcHdrMark, kHitachiAcHdrSpace, kHitachiAcBitMark, @@ -69,10 +70,10 @@ void IRsend::sendHitachiAC(unsigned char data[], uint16_t nbytes, // Status: BETA / Appears to work. // // Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/453 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/453 // Basically the same as sendHitatchiAC() except different size and header. -void IRsend::sendHitachiAC1(unsigned char data[], uint16_t nbytes, - uint16_t repeat) { +void IRsend::sendHitachiAC1(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { if (nbytes < kHitachiAc1StateLength) return; // Not enough bytes to send a proper message. sendGeneric(kHitachiAc1HdrMark, kHitachiAc1HdrSpace, kHitachiAcBitMark, @@ -96,10 +97,10 @@ void IRsend::sendHitachiAC1(unsigned char data[], uint16_t nbytes, // Status: BETA / Appears to work. // // Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/417 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/417 // Basically the same as sendHitatchiAC() except different size. -void IRsend::sendHitachiAC2(unsigned char data[], uint16_t nbytes, - uint16_t repeat) { +void IRsend::sendHitachiAC2(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { if (nbytes < kHitachiAc2StateLength) return; // Not enough bytes to send a proper message. sendHitachiAC(data, nbytes, repeat); @@ -110,9 +111,11 @@ void IRsend::sendHitachiAC2(unsigned char data[], uint16_t nbytes, // Inspired by: // https://github.com/ToniA/arduino-heatpumpir/blob/master/HitachiHeatpumpIR.cpp -IRHitachiAc::IRHitachiAc(uint16_t pin) : _irsend(pin) { stateReset(); } +IRHitachiAc::IRHitachiAc(const uint16_t pin, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { stateReset(); } -void IRHitachiAc::stateReset() { +void IRHitachiAc::stateReset(void) { remote_state[0] = 0x80; remote_state[1] = 0x08; remote_state[2] = 0x0C; @@ -130,7 +133,7 @@ void IRHitachiAc::stateReset() { setTemp(23); } -void IRHitachiAc::begin() { _irsend.begin(); } +void IRHitachiAc::begin(void) { _irsend.begin(); } uint8_t IRHitachiAc::calcChecksum(const uint8_t state[], const uint16_t length) { @@ -148,7 +151,7 @@ bool IRHitachiAc::validChecksum(const uint8_t state[], const uint16_t length) { return (state[length - 1] == calcChecksum(state, length)); } -uint8_t *IRHitachiAc::getRaw() { +uint8_t *IRHitachiAc::getRaw(void) { checksum(); return remote_state; } @@ -165,7 +168,7 @@ void IRHitachiAc::send(const uint16_t repeat) { } #endif // SEND_HITACHI_AC -bool IRHitachiAc::getPower() { return (remote_state[17] & 0x01); } +bool IRHitachiAc::getPower(void) { return (remote_state[17] & 0x01); } void IRHitachiAc::setPower(const bool on) { if (on) @@ -174,11 +177,11 @@ void IRHitachiAc::setPower(const bool on) { remote_state[17] &= 0xFE; } -void IRHitachiAc::on() { setPower(true); } +void IRHitachiAc::on(void) { setPower(true); } -void IRHitachiAc::off() { setPower(false); } +void IRHitachiAc::off(void) { setPower(false); } -uint8_t IRHitachiAc::getMode() { return reverseBits(remote_state[10], 8); } +uint8_t IRHitachiAc::getMode(void) { return reverseBits(remote_state[10], 8); } void IRHitachiAc::setMode(const uint8_t mode) { uint8_t newmode = mode; @@ -200,7 +203,9 @@ void IRHitachiAc::setMode(const uint8_t mode) { setFan(getFan()); // Reset the fan speed after the mode change. } -uint8_t IRHitachiAc::getTemp() { return reverseBits(remote_state[11], 8) >> 1; } +uint8_t IRHitachiAc::getTemp(void) { + return reverseBits(remote_state[11], 8) >> 1; +} void IRHitachiAc::setTemp(const uint8_t celsius) { uint8_t temp; @@ -220,7 +225,7 @@ void IRHitachiAc::setTemp(const uint8_t celsius) { remote_state[9] = 0x10; } -uint8_t IRHitachiAc::getFan() { return reverseBits(remote_state[13], 8); } +uint8_t IRHitachiAc::getFan(void) { return reverseBits(remote_state[13], 8); } void IRHitachiAc::setFan(const uint8_t speed) { uint8_t fanmin = kHitachiAcFanAuto; @@ -239,7 +244,7 @@ void IRHitachiAc::setFan(const uint8_t speed) { remote_state[13] = reverseBits(newspeed, 8); } -bool IRHitachiAc::getSwingVertical() { return remote_state[14] & 0x80; } +bool IRHitachiAc::getSwingVertical(void) { return remote_state[14] & 0x80; } void IRHitachiAc::setSwingVertical(const bool on) { if (on) @@ -248,7 +253,7 @@ void IRHitachiAc::setSwingVertical(const bool on) { remote_state[14] &= 0x7F; } -bool IRHitachiAc::getSwingHorizontal() { return remote_state[15] & 0x80; } +bool IRHitachiAc::getSwingHorizontal(void) { return remote_state[15] & 0x80; } void IRHitachiAc::setSwingHorizontal(const bool on) { if (on) @@ -291,68 +296,68 @@ uint8_t IRHitachiAc::convertFan(const stdAc::fanspeed_t speed) { } } +// Convert a native mode to it's common equivalent. +stdAc::opmode_t IRHitachiAc::toCommonMode(const uint8_t mode) { + switch (mode) { + case kHitachiAcCool: return stdAc::opmode_t::kCool; + case kHitachiAcHeat: return stdAc::opmode_t::kHeat; + case kHitachiAcDry: return stdAc::opmode_t::kDry; + case kHitachiAcFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRHitachiAc::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kHitachiAcFanHigh: return stdAc::fanspeed_t::kMax; + case kHitachiAcFanHigh - 1: return stdAc::fanspeed_t::kHigh; + case kHitachiAcFanLow + 1: return stdAc::fanspeed_t::kMedium; + case kHitachiAcFanLow: return stdAc::fanspeed_t::kLow; + default: return stdAc::fanspeed_t::kAuto; + } +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRHitachiAc::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::HITACHI_AC; + result.model = -1; // No models used. + result.power = this->getPower(); + result.mode = this->toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(); + result.fanspeed = this->toCommonFanSpeed(this->getFan()); + result.swingv = this->getSwingVertical() ? stdAc::swingv_t::kAuto : + stdAc::swingv_t::kOff; + result.swingh = this->getSwingHorizontal() ? stdAc::swingh_t::kAuto : + stdAc::swingh_t::kOff; + // Not supported. + result.quiet = false; + result.turbo = false; + result.clean = false; + result.econo = false; + result.filter = false; + result.light = false; + result.beep = false; + result.sleep = -1; + result.clock = -1; + return result; +} + // Convert the internal state into a human readable string. -#ifdef ARDUINO -String IRHitachiAc::toString() { +String IRHitachiAc::toString(void) { String result = ""; -#else -std::string IRHitachiAc::toString() { - std::string result = ""; -#endif // ARDUINO - result += F("Power: "); - if (getPower()) - result += F("On"); - else - result += F("Off"); - result += F(", Mode: "); - result += uint64ToString(getMode()); - switch (getMode()) { - case kHitachiAcAuto: - result += F(" (AUTO)"); - break; - case kHitachiAcCool: - result += F(" (COOL)"); - break; - case kHitachiAcHeat: - result += F(" (HEAT)"); - break; - case kHitachiAcDry: - result += F(" (DRY)"); - break; - case kHitachiAcFan: - result += F(" (FAN)"); - break; - default: - result += F(" (UNKNOWN)"); - } - result += F(", Temp: "); - result += uint64ToString(getTemp()); - result += F("C, Fan: "); - result += uint64ToString(getFan()); - switch (getFan()) { - case kHitachiAcFanAuto: - result += F(" (AUTO)"); - break; - case kHitachiAcFanLow: - result += F(" (LOW)"); - break; - case kHitachiAcFanHigh: - result += F(" (HIGH)"); - break; - default: - result += F(" (UNKNOWN)"); - break; - } - result += F(", Swing (Vertical): "); - if (getSwingVertical()) - result += F("On"); - else - result += F("Off"); - result += F(", Swing (Horizontal): "); - if (getSwingHorizontal()) - result += F("On"); - else - result += F("Off"); + result.reserve(110); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(getPower(), F("Power"), false); + result += addModeToString(getMode(), kHitachiAcAuto, kHitachiAcCool, + kHitachiAcHeat, kHitachiAcDry, kHitachiAcFan); + result += addTempToString(getTemp()); + result += addFanToString(getFan(), kHitachiAcFanHigh, kHitachiAcFanLow, + kHitachiAcFanAuto, kHitachiAcFanAuto, + kHitachiAcFanMed); + result += addBoolToString(getSwingVertical(), F("Swing (Vertical)")); + result += addBoolToString(getSwingHorizontal(), F("Swing (Horizontal)")); return result; } @@ -373,11 +378,11 @@ std::string IRHitachiAc::toString() { // Hitachi A/C Series VI (Circa 2007) / Remote: LT0541-HTA // // Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/417 -// https://github.com/markszabo/IRremoteESP8266/issues/453 -bool IRrecv::decodeHitachiAC(decode_results *results, uint16_t nbits, - bool strict) { - const uint8_t kTolerance = 30; +// https://github.com/crankyoldgit/IRremoteESP8266/issues/417 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/453 +bool IRrecv::decodeHitachiAC(decode_results *results, const uint16_t nbits, + const bool strict) { + const uint8_t k_tolerance = _tolerance + 5; if (results->rawlen < 2 * nbits + kHeader + kFooter - 1) return false; // Can't possibly be a valid HitachiAC message. if (strict) { @@ -391,57 +396,33 @@ bool IRrecv::decodeHitachiAC(decode_results *results, uint16_t nbits, } } uint16_t offset = kStartOffset; - uint16_t dataBitsSoFar = 0; - match_result_t data_result; - - // Header + uint16_t hmark; + uint32_t hspace; if (nbits == kHitachiAc1Bits) { - if (!matchMark(results->rawbuf[offset++], kHitachiAc1HdrMark, kTolerance)) - return false; - if (!matchSpace(results->rawbuf[offset++], kHitachiAc1HdrSpace, kTolerance)) - return false; - } else { // Everything else. - if (!matchMark(results->rawbuf[offset++], kHitachiAcHdrMark, kTolerance)) - return false; - if (!matchSpace(results->rawbuf[offset++], kHitachiAcHdrSpace, kTolerance)) - return false; + hmark = kHitachiAc1HdrMark; + hspace = kHitachiAc1HdrSpace; + } else { + hmark = kHitachiAcHdrMark; + hspace = kHitachiAcHdrSpace; } - // Data - // Keep reading bytes until we either run out of message or state to fill. - for (uint16_t i = 0; offset <= results->rawlen - 16 && i < nbits / 8; - i++, dataBitsSoFar += 8, offset += data_result.used) { - data_result = matchData(&(results->rawbuf[offset]), 8, kHitachiAcBitMark, - kHitachiAcOneSpace, kHitachiAcBitMark, - kHitachiAcZeroSpace, kTolerance); - if (data_result.success == false) break; // Fail - results->state[i] = (uint8_t)data_result.data; - } - - // Footer - if (!matchMark(results->rawbuf[offset++], kHitachiAcBitMark, kTolerance)) - return false; - if (offset <= results->rawlen && - !matchAtLeast(results->rawbuf[offset], kHitachiAcMinGap, kTolerance)) - return false; + // Match Header + Data + Footer + if (!matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, nbits, + hmark, hspace, + kHitachiAcBitMark, kHitachiAcOneSpace, + kHitachiAcBitMark, kHitachiAcZeroSpace, + kHitachiAcBitMark, kHitachiAcMinGap, true, + k_tolerance)) return false; // Compliance if (strict) { - // Re-check we got the correct size/length due to the way we read the data. - switch (dataBitsSoFar / 8) { - case kHitachiAcStateLength: - case kHitachiAc1StateLength: - case kHitachiAc2StateLength: - break; // Continue - default: - return false; - } - if (dataBitsSoFar / 8 == kHitachiAcStateLength && + if (nbits / 8 == kHitachiAcStateLength && !IRHitachiAc::validChecksum(results->state, kHitachiAcStateLength)) return false; } // Success - switch (dataBitsSoFar) { + switch (nbits) { case kHitachiAc1Bits: results->decode_type = HITACHI_AC1; break; @@ -452,7 +433,7 @@ bool IRrecv::decodeHitachiAC(decode_results *results, uint16_t nbits, default: results->decode_type = HITACHI_AC; } - results->bits = dataBitsSoFar; + results->bits = nbits; // No need to record the state as we stored it as we decoded it. // As we use result->state, we don't record value, address, or command as it // is a union data type. diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Hitachi.h b/lib/IRremoteESP8266-2.6.5/src/ir_Hitachi.h similarity index 68% rename from lib/IRremoteESP8266-2.6.0/src/ir_Hitachi.h rename to lib/IRremoteESP8266-2.6.5/src/ir_Hitachi.h index 532717447..b9bfd391c 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Hitachi.h +++ b/lib/IRremoteESP8266-2.6.5/src/ir_Hitachi.h @@ -2,6 +2,11 @@ // // Copyright 2018 David Conran +// Supports: +// Brand: Hitachi, Model: RAS-35THA6 remote +// Brand: Hitachi, Model: LT0541-HTA remote +// Brand: Hitachi, Model: Series VI A/C (Circa 2007) + #ifndef IR_HITACHI_H_ #define IR_HITACHI_H_ @@ -9,8 +14,6 @@ #include #ifndef UNIT_TEST #include -#else -#include #endif #include "IRremoteESP8266.h" #include "IRsend.h" @@ -26,6 +29,7 @@ const uint8_t kHitachiAcDry = 5; const uint8_t kHitachiAcFan = 0xC; const uint8_t kHitachiAcFanAuto = 1; const uint8_t kHitachiAcFanLow = 2; +const uint8_t kHitachiAcFanMed = 3; const uint8_t kHitachiAcFanHigh = 5; const uint8_t kHitachiAcMinTemp = 16; // 16C const uint8_t kHitachiAcMaxTemp = 32; // 32C @@ -34,28 +38,30 @@ const uint8_t kHitachiAcAutoTemp = 23; // 23C // Classes class IRHitachiAc { public: - explicit IRHitachiAc(uint16_t pin); + explicit IRHitachiAc(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); - void stateReset(); + void stateReset(void); #if SEND_HITACHI_AC void send(const uint16_t repeat = kHitachiAcDefaultRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } #endif // SEND_HITACHI_AC - void begin(); - void on(); - void off(); + void begin(void); + void on(void); + void off(void); void setPower(const bool on); - bool getPower(); + bool getPower(void); void setTemp(const uint8_t temp); - uint8_t getTemp(); + uint8_t getTemp(void); void setFan(const uint8_t speed); - uint8_t getFan(); + uint8_t getFan(void); void setMode(const uint8_t mode); - uint8_t getMode(); + uint8_t getMode(void); void setSwingVertical(const bool on); - bool getSwingVertical(); + bool getSwingVertical(void); void setSwingHorizontal(const bool on); - bool getSwingHorizontal(); - uint8_t* getRaw(); + bool getSwingHorizontal(void); + uint8_t* getRaw(void); void setRaw(const uint8_t new_code[], const uint16_t length = kHitachiAcStateLength); static bool validChecksum(const uint8_t state[], @@ -64,11 +70,10 @@ class IRHitachiAc { const uint16_t length = kHitachiAcStateLength); uint8_t convertMode(const stdAc::opmode_t mode); uint8_t convertFan(const stdAc::fanspeed_t speed); -#ifdef ARDUINO - String toString(); -#else - std::string toString(); -#endif + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + stdAc::state_t toCommon(void); + String toString(void); #ifndef UNIT_TEST private: diff --git a/lib/IRremoteESP8266-2.6.5/src/ir_Inax.cpp b/lib/IRremoteESP8266-2.6.5/src/ir_Inax.cpp new file mode 100644 index 000000000..073580fae --- /dev/null +++ b/lib/IRremoteESP8266-2.6.5/src/ir_Inax.cpp @@ -0,0 +1,83 @@ +// Copyright 2019 David Conran (crankyoldgit) +// Support for an IR controlled Robot Toilet + +#include +#include "IRrecv.h" +#include "IRsend.h" +#include "IRutils.h" + +// Supports: +// Brand: Lixil, Model: Inax DT-BA283 Toilet + +// Documentation: +// https://www.lixil-manual.com/GCW-1365-16050/GCW-1365-16050.pdf + +// Constants +// Ref: +// https://github.com/crankyoldgit/IRremoteESP8266/issues/706 +const uint16_t kInaxTick = 500; +const uint16_t kInaxHdrMark = 9000; +const uint16_t kInaxHdrSpace = 4500; +const uint16_t kInaxBitMark = 560; +const uint16_t kInaxOneSpace = 1675; +const uint16_t kInaxZeroSpace = kInaxBitMark; +const uint16_t kInaxMinGap = 40000; + +#if SEND_INAX +// Send a Inax Toilet formatted message. +// +// Args: +// data: The message to be sent. +// nbits: The bit size of the message being sent. typically kInaxBits. +// repeat: The number of times the message is to be repeated. +// +// Status: BETA / Should be working. +// +// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/706 +void IRsend::sendInax(const uint64_t data, const uint16_t nbits, + const uint16_t repeat) { + sendGeneric(kInaxHdrMark, kInaxHdrSpace, + kInaxBitMark, kInaxOneSpace, + kInaxBitMark, kInaxZeroSpace, + kInaxBitMark, kInaxMinGap, + data, nbits, 38, true, repeat, kDutyDefault); +} +#endif + +#if DECODE_INAX +// Decode the supplied Inax Toilet message. +// +// Args: +// results: Ptr to the data to decode and where to store the decode result. +// nbits: Nr. of bits to expect in the data portion. +// Typically kInaxBits. +// strict: Flag to indicate if we strictly adhere to the specification. +// Returns: +// boolean: True if it can decode it, false if it can't. +// +// Status: Stable / Known working. +// +bool IRrecv::decodeInax(decode_results *results, const uint16_t nbits, + const bool strict) { + if (strict && nbits != kInaxBits) + return false; // We expect Inax to be a certain sized message. + + uint64_t data = 0; + uint16_t offset = kStartOffset; + + // Match Header + Data + Footer + if (!matchGeneric(results->rawbuf + offset, &data, + results->rawlen - offset, nbits, + kInaxHdrMark, kInaxHdrSpace, + kInaxBitMark, kInaxOneSpace, + kInaxBitMark, kInaxZeroSpace, + kInaxBitMark, kInaxMinGap, true)) return false; + // Success + results->bits = nbits; + results->value = data; + results->decode_type = INAX; + results->command = 0; + results->address = 0; + return true; +} +#endif diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_JVC.cpp b/lib/IRremoteESP8266-2.6.5/src/ir_JVC.cpp similarity index 78% rename from lib/IRremoteESP8266-2.6.0/src/ir_JVC.cpp rename to lib/IRremoteESP8266-2.6.5/src/ir_JVC.cpp index 47df29dc4..7038e9d3e 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_JVC.cpp +++ b/lib/IRremoteESP8266-2.6.5/src/ir_JVC.cpp @@ -7,12 +7,6 @@ #include "IRtimer.h" #include "IRutils.h" -// JJJJJ V V CCCC -// J V V C -// J V V C -// J J V V C -// J V CCCC - // JVC originally added by Kristian Lauszus // (Thanks to zenwheel and other people at the original blog post) @@ -119,40 +113,23 @@ bool IRrecv::decodeJVC(decode_results *results, uint16_t nbits, bool strict) { uint16_t offset = kStartOffset; bool isRepeat = true; - uint32_t m_tick; - uint32_t s_tick; // Header // (Optional as repeat codes don't have the header) if (matchMark(results->rawbuf[offset], kJvcHdrMark)) { isRepeat = false; - m_tick = results->rawbuf[offset++] * kRawTick / kJvcHdrMarkTicks; + offset++; if (results->rawlen < 2 * nbits + 4) return false; // Can't possibly be a valid JVC message with a header. - if (!matchSpace(results->rawbuf[offset], kJvcHdrSpace)) return false; - s_tick = results->rawbuf[offset++] * kRawTick / kJvcHdrSpaceTicks; - } else { - // We can't easily auto-calibrate as there is no header, so assume - // the default tick time. - m_tick = kJvcTick; - s_tick = kJvcTick; + if (!matchSpace(results->rawbuf[offset++], kJvcHdrSpace)) return false; } - // Data - match_result_t data_result = - matchData(&(results->rawbuf[offset]), nbits, kJvcBitMarkTicks * m_tick, - kJvcOneSpaceTicks * s_tick, kJvcBitMarkTicks * m_tick, - kJvcZeroSpaceTicks * s_tick); - if (data_result.success == false) return false; - data = data_result.data; - offset += data_result.used; - - // Footer - if (!matchMark(results->rawbuf[offset++], kJvcBitMarkTicks * m_tick)) - return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset], kJvcMinGapTicks * s_tick)) - return false; - + // Data + Footer + if (!matchGeneric(results->rawbuf + offset, &data, + results->rawlen - offset, nbits, + 0, 0, + kJvcBitMark, kJvcOneSpace, + kJvcBitMark, kJvcZeroSpace, + kJvcBitMark, kJvcMinGap, true)) return false; // Success results->decode_type = JVC; results->bits = nbits; diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Kelvinator.cpp b/lib/IRremoteESP8266-2.6.5/src/ir_Kelvinator.cpp similarity index 58% rename from lib/IRremoteESP8266-2.6.0/src/ir_Kelvinator.cpp rename to lib/IRremoteESP8266-2.6.5/src/ir_Kelvinator.cpp index c69f4cb8a..0af521b15 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Kelvinator.cpp +++ b/lib/IRremoteESP8266-2.6.5/src/ir_Kelvinator.cpp @@ -24,12 +24,6 @@ #include "IRsend.h" #include "IRutils.h" -// KK KK EEEEEEE LL VV VV IIIII NN NN AAA TTTTTTT OOOOO RRRRRR -// KK KK EE LL VV VV III NNN NN AAAAA TTT OO OO RR RR -// KKKK EEEEE LL VV VV III NN N NN AA AA TTT OO OO RRRRRR -// KK KK EE LL VV VV III NN NNN AAAAAAA TTT OO OO RR RR -// KK KK EEEEEEE LLLLLLL VVV IIIII NN NN AA AA TTT OOOO0 RR RR - // Constants const uint16_t kKelvinatorTick = 85; @@ -72,6 +66,13 @@ const uint8_t kKelvinatorXfan = 1 << kKelvinatorXfanOffset; const uint8_t kKelvinatorTurboOffset = 4; const uint8_t kKelvinatorTurbo = 1 << kKelvinatorTurboOffset; +using irutils::addBoolToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addFanToString; +using irutils::addTempToString; + #if SEND_KELVINATOR // Send a Kelvinator A/C message. // @@ -82,8 +83,8 @@ const uint8_t kKelvinatorTurbo = 1 << kKelvinatorTurboOffset; // // Status: STABLE / Known working. // -void IRsend::sendKelvinator(unsigned char data[], uint16_t nbytes, - uint16_t repeat) { +void IRsend::sendKelvinator(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { if (nbytes < kKelvinatorStateLength) return; // Not enough bytes to send a proper message. @@ -124,36 +125,38 @@ void IRsend::sendKelvinator(unsigned char data[], uint16_t nbytes, } #endif // SEND_KELVINATOR -IRKelvinatorAC::IRKelvinatorAC(uint16_t pin) : _irsend(pin) { stateReset(); } +IRKelvinatorAC::IRKelvinatorAC(const uint16_t pin, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { this->stateReset(); } -void IRKelvinatorAC::stateReset() { +void IRKelvinatorAC::stateReset(void) { for (uint8_t i = 0; i < kKelvinatorStateLength; i++) remote_state[i] = 0x0; remote_state[3] = 0x50; remote_state[11] = 0x70; } -void IRKelvinatorAC::begin() { _irsend.begin(); } +void IRKelvinatorAC::begin(void) { _irsend.begin(); } -void IRKelvinatorAC::fixup() { +void IRKelvinatorAC::fixup(void) { // X-Fan mode is only valid in COOL or DRY modes. - if (getMode() != kKelvinatorCool && getMode() != kKelvinatorDry) - setXFan(false); - checksum(); // Calculate the checksums + if (this->getMode() != kKelvinatorCool && this->getMode() != kKelvinatorDry) + this->setXFan(false); + this->checksum(); // Calculate the checksums } #if SEND_KELVINATOR void IRKelvinatorAC::send(const uint16_t repeat) { - fixup(); // Ensure correct settings before sending. + this->fixup(); // Ensure correct settings before sending. _irsend.sendKelvinator(remote_state, kKelvinatorStateLength, repeat); } #endif // SEND_KELVINATOR -uint8_t *IRKelvinatorAC::getRaw() { - fixup(); // Ensure correct settings before sending. +uint8_t *IRKelvinatorAC::getRaw(void) { + this->fixup(); // Ensure correct settings before sending. return remote_state; } -void IRKelvinatorAC::setRaw(uint8_t new_code[]) { +void IRKelvinatorAC::setRaw(const uint8_t new_code[]) { for (uint8_t i = 0; i < kKelvinatorStateLength; i++) { remote_state[i] = new_code[i]; } @@ -196,46 +199,46 @@ bool IRKelvinatorAC::validChecksum(const uint8_t state[], return true; } -void IRKelvinatorAC::on() { +void IRKelvinatorAC::on(void) { remote_state[0] |= kKelvinatorPower; remote_state[8] = remote_state[0]; // Duplicate to the 2nd command chunk. } -void IRKelvinatorAC::off() { +void IRKelvinatorAC::off(void) { remote_state[0] &= ~kKelvinatorPower; remote_state[8] = remote_state[0]; // Duplicate to the 2nd command chunk. } -void IRKelvinatorAC::setPower(bool state) { - if (state) - on(); +void IRKelvinatorAC::setPower(const bool on) { + if (on) + this->on(); else - off(); + this->off(); } -bool IRKelvinatorAC::getPower() { - return ((remote_state[0] & kKelvinatorPower) != 0); +bool IRKelvinatorAC::getPower(void) { + return remote_state[0] & kKelvinatorPower; } // Set the temp. in deg C -void IRKelvinatorAC::setTemp(uint8_t temp) { - temp = std::max(kKelvinatorMinTemp, temp); +void IRKelvinatorAC::setTemp(const uint8_t degrees) { + uint8_t temp = std::max(kKelvinatorMinTemp, degrees); temp = std::min(kKelvinatorMaxTemp, temp); remote_state[1] = (remote_state[1] & 0xF0U) | (temp - kKelvinatorMinTemp); remote_state[9] = remote_state[1]; // Duplicate to the 2nd command chunk. } // Return the set temp. in deg C -uint8_t IRKelvinatorAC::getTemp() { +uint8_t IRKelvinatorAC::getTemp(void) { return ((remote_state[1] & 0xFU) + kKelvinatorMinTemp); } // Set the speed of the fan, 0-5, 0 is auto, 1-5 is the speed -void IRKelvinatorAC::setFan(uint8_t fan) { - fan = std::min(kKelvinatorFanMax, fan); // Bounds check +void IRKelvinatorAC::setFan(const uint8_t speed) { + uint8_t fan = std::min(kKelvinatorFanMax, speed); // Bounds check // Only change things if we need to. - if (fan != getFan()) { + if (fan != this->getFan()) { // Set the basic fan values. uint8_t fan_basic = std::min(kKelvinatorBasicFanMax, fan); remote_state[0] = (remote_state[0] & kKelvinatorBasicFanMask) | @@ -244,108 +247,117 @@ void IRKelvinatorAC::setFan(uint8_t fan) { // Set the advanced(?) fan value. remote_state[14] = (remote_state[14] & kKelvinatorFanMask) | (fan << kKelvinatorFanOffset); - setTurbo(false); // Turbo mode is turned off if we change the fan settings. + // Turbo mode is turned off if we change the fan settings. + this->setTurbo(false); } } -uint8_t IRKelvinatorAC::getFan() { +uint8_t IRKelvinatorAC::getFan(void) { return ((remote_state[14] & ~kKelvinatorFanMask) >> kKelvinatorFanOffset); } -uint8_t IRKelvinatorAC::getMode() { +uint8_t IRKelvinatorAC::getMode(void) { return (remote_state[0] & ~kKelvinatorModeMask); } -void IRKelvinatorAC::setMode(uint8_t mode) { - // If we get an unexpected mode, default to AUTO. - if (mode > kKelvinatorHeat) mode = kKelvinatorAuto; - remote_state[0] = (remote_state[0] & kKelvinatorModeMask) | mode; - remote_state[8] = remote_state[0]; // Duplicate to the 2nd command chunk. - if (mode == kKelvinatorAuto || kKelvinatorDry) - // When the remote is set to Auto or Dry, it defaults to 25C and doesn't - // show it. - setTemp(kKelvinatorAutoTemp); +void IRKelvinatorAC::setMode(const uint8_t mode) { + switch (mode) { + case kKelvinatorAuto: + case kKelvinatorDry: + // When the remote is set to Auto or Dry, it defaults to 25C and doesn't + // show it. + this->setTemp(kKelvinatorAutoTemp); + // FALL-THRU + case kKelvinatorHeat: + case kKelvinatorCool: + case kKelvinatorFan: + remote_state[0] = (remote_state[0] & kKelvinatorModeMask) | mode; + remote_state[8] = remote_state[0]; // Duplicate to the 2nd command chunk. + break; + default: // If we get an unexpected mode, default to AUTO. + this->setMode(kKelvinatorAuto); + } } -void IRKelvinatorAC::setSwingVertical(bool state) { - if (state) { +void IRKelvinatorAC::setSwingVertical(const bool on) { + if (on) { remote_state[0] |= kKelvinatorVentSwing; remote_state[4] |= kKelvinatorVentSwingV; } else { remote_state[4] &= ~kKelvinatorVentSwingV; - if (!getSwingHorizontal()) remote_state[0] &= ~kKelvinatorVentSwing; + if (!this->getSwingHorizontal()) remote_state[0] &= ~kKelvinatorVentSwing; } remote_state[8] = remote_state[0]; // Duplicate to the 2nd command chunk. } -bool IRKelvinatorAC::getSwingVertical() { - return ((remote_state[4] & kKelvinatorVentSwingV) != 0); +bool IRKelvinatorAC::getSwingVertical(void) { + return remote_state[4] & kKelvinatorVentSwingV; } -void IRKelvinatorAC::setSwingHorizontal(bool state) { - if (state) { +void IRKelvinatorAC::setSwingHorizontal(const bool on) { + if (on) { remote_state[0] |= kKelvinatorVentSwing; remote_state[4] |= kKelvinatorVentSwingH; } else { remote_state[4] &= ~kKelvinatorVentSwingH; - if (!getSwingVertical()) remote_state[0] &= ~kKelvinatorVentSwing; + if (!this->getSwingVertical()) remote_state[0] &= ~kKelvinatorVentSwing; } remote_state[8] = remote_state[0]; // Duplicate to the 2nd command chunk. } -bool IRKelvinatorAC::getSwingHorizontal() { - return ((remote_state[4] & kKelvinatorVentSwingH) != 0); +bool IRKelvinatorAC::getSwingHorizontal(void) { + return remote_state[4] & kKelvinatorVentSwingH; } -void IRKelvinatorAC::setQuiet(bool state) { +void IRKelvinatorAC::setQuiet(const bool on) { remote_state[12] &= ~kKelvinatorQuiet; - remote_state[12] |= (state << kKelvinatorQuietOffset); + remote_state[12] |= (on << kKelvinatorQuietOffset); } -bool IRKelvinatorAC::getQuiet() { - return ((remote_state[12] & kKelvinatorQuiet) != 0); +bool IRKelvinatorAC::getQuiet(void) { + return remote_state[12] & kKelvinatorQuiet; } -void IRKelvinatorAC::setIonFilter(bool state) { +void IRKelvinatorAC::setIonFilter(const bool on) { remote_state[2] &= ~kKelvinatorIonFilter; - remote_state[2] |= (state << kKelvinatorIonFilterOffset); + remote_state[2] |= (on << kKelvinatorIonFilterOffset); remote_state[10] = remote_state[2]; // Duplicate to the 2nd command chunk. } -bool IRKelvinatorAC::getIonFilter() { - return ((remote_state[2] & kKelvinatorIonFilter) != 0); +bool IRKelvinatorAC::getIonFilter(void) { + return remote_state[2] & kKelvinatorIonFilter; } -void IRKelvinatorAC::setLight(bool state) { +void IRKelvinatorAC::setLight(const bool on) { remote_state[2] &= ~kKelvinatorLight; - remote_state[2] |= (state << kKelvinatorLightOffset); + remote_state[2] |= (on << kKelvinatorLightOffset); remote_state[10] = remote_state[2]; // Duplicate to the 2nd command chunk. } -bool IRKelvinatorAC::getLight() { - return ((remote_state[2] & kKelvinatorLight) != 0); +bool IRKelvinatorAC::getLight(void) { + return remote_state[2] & kKelvinatorLight; } // Note: XFan mode is only valid in Cool or Dry mode. -void IRKelvinatorAC::setXFan(bool state) { +void IRKelvinatorAC::setXFan(const bool on) { remote_state[2] &= ~kKelvinatorXfan; - remote_state[2] |= (state << kKelvinatorXfanOffset); + remote_state[2] |= (on << kKelvinatorXfanOffset); remote_state[10] = remote_state[2]; // Duplicate to the 2nd command chunk. } -bool IRKelvinatorAC::getXFan() { - return ((remote_state[2] & kKelvinatorXfan) != 0); +bool IRKelvinatorAC::getXFan(void) { + return remote_state[2] & kKelvinatorXfan; } // Note: Turbo mode is turned off if the fan speed is changed. -void IRKelvinatorAC::setTurbo(bool state) { +void IRKelvinatorAC::setTurbo(const bool on) { remote_state[2] &= ~kKelvinatorTurbo; - remote_state[2] |= (state << kKelvinatorTurboOffset); + remote_state[2] |= (on << kKelvinatorTurboOffset); remote_state[10] = remote_state[2]; // Duplicate to the 2nd command chunk. } -bool IRKelvinatorAC::getTurbo() { - return ((remote_state[2] & kKelvinatorTurbo) != 0); +bool IRKelvinatorAC::getTurbo(void) { + return remote_state[2] & kKelvinatorTurbo; } // Convert a standard A/C mode into its native mode. @@ -364,87 +376,67 @@ uint8_t IRKelvinatorAC::convertMode(const stdAc::opmode_t mode) { } } +// Convert a native mode to it's common equivalent. +stdAc::opmode_t IRKelvinatorAC::toCommonMode(const uint8_t mode) { + switch (mode) { + case kKelvinatorCool: return stdAc::opmode_t::kCool; + case kKelvinatorHeat: return stdAc::opmode_t::kHeat; + case kKelvinatorDry: return stdAc::opmode_t::kDry; + case kKelvinatorFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRKelvinatorAC::toCommonFanSpeed(const uint8_t speed) { + return (stdAc::fanspeed_t)speed; +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRKelvinatorAC::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::KELVINATOR; + result.model = -1; // Unused. + result.power = this->getPower(); + result.mode = this->toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(); + result.fanspeed = this->toCommonFanSpeed(this->getFan()); + result.swingv = this->getSwingVertical() ? stdAc::swingv_t::kAuto : + stdAc::swingv_t::kOff; + result.swingh = this->getSwingHorizontal() ? stdAc::swingh_t::kAuto : + stdAc::swingh_t::kOff; + result.quiet = this->getQuiet(); + result.turbo = this->getTurbo(); + result.light = this->getLight(); + result.filter = this->getIonFilter(); + result.clean = this->getXFan(); + // Not supported. + result.econo = false; + result.beep = false; + result.sleep = -1; + result.clock = -1; + return result; +} + // Convert the internal state into a human readable string. -#ifdef ARDUINO -String IRKelvinatorAC::toString() { +String IRKelvinatorAC::toString(void) { String result = ""; -#else -std::string IRKelvinatorAC::toString() { - std::string result = ""; -#endif // ARDUINO - result += F("Power: "); - if (getPower()) - result += F("On"); - else - result += F("Off"); - result += F(", Mode: "); - result += uint64ToString(getMode()); - switch (getMode()) { - case kKelvinatorAuto: - result += F(" (AUTO)"); - break; - case kKelvinatorCool: - result += F(" (COOL)"); - break; - case kKelvinatorHeat: - result += F(" (HEAT)"); - break; - case kKelvinatorDry: - result += F(" (DRY)"); - break; - case kKelvinatorFan: - result += F(" (FAN)"); - break; - default: - result += F(" (UNKNOWN)"); - } - result += F(", Temp: "); - result += uint64ToString(getTemp()); - result += F("C, Fan: "); - result += uint64ToString(getFan()); - switch (getFan()) { - case kKelvinatorFanAuto: - result += F(" (AUTO)"); - break; - case kKelvinatorFanMax: - result += F(" (MAX)"); - break; - } - result += F(", Turbo: "); - if (getTurbo()) - result += F("On"); - else - result += F("Off"); - result += F(", Quiet: "); - if (getQuiet()) - result += F("On"); - else - result += F("Off"); - result += F(", XFan: "); - if (getXFan()) - result += F("On"); - else - result += F("Off"); - result += F(", IonFilter: "); - if (getIonFilter()) - result += F("On"); - else - result += F("Off"); - result += F(", Light: "); - if (getLight()) - result += F("On"); - else - result += F("Off"); - result += F(", Swing (Horizontal): "); - if (getSwingHorizontal()) - result += F("On"); - else - result += F("Off"); - result += F(", Swing (Vertical): "); - if (getSwingVertical()) - result += F("On"); - else - result += F("Off"); + result.reserve(160); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(getPower(), F("Power"), false); + result += addModeToString(getMode(), kKelvinatorAuto, kKelvinatorCool, + kKelvinatorHeat, kKelvinatorDry, kKelvinatorFan); + result += addTempToString(getTemp()); + result += addFanToString(getFan(), kKelvinatorFanMax, kKelvinatorFanMin, + kKelvinatorFanAuto, kKelvinatorFanAuto, + kKelvinatorBasicFanMax); + result += addBoolToString(getTurbo(), F("Turbo")); + result += addBoolToString(getQuiet(), F("Quiet")); + result += addBoolToString(getXFan(), F("XFan")); + result += addBoolToString(getIonFilter(), F("IonFilter")); + result += addBoolToString(getLight(), F("Light")); + result += addBoolToString(getSwingHorizontal(), F("Swing (Horizontal)")); + result += addBoolToString(getSwingVertical(), F("Swing (Vertical)")); return result; } @@ -458,7 +450,7 @@ std::string IRKelvinatorAC::toString() { // Returns: // boolean: True if it can decode it, false if it can't. // -// Status: ALPHA / Untested. +// Status: STABLE / Known working. bool IRrecv::decodeKelvinator(decode_results *results, uint16_t nbits, bool strict) { if (results->rawlen < @@ -467,101 +459,60 @@ bool IRrecv::decodeKelvinator(decode_results *results, uint16_t nbits, if (strict && nbits != kKelvinatorBits) return false; // Not strictly a Kelvinator message. - uint32_t data; uint16_t offset = kStartOffset; // There are two messages back-to-back in a full Kelvinator IR message // sequence. - int8_t state_pos = 0; + int8_t pos = 0; for (uint8_t s = 0; s < 2; s++) { match_result_t data_result; - // Header - if (!matchMark(results->rawbuf[offset], kKelvinatorHdrMark)) return false; - // Calculate how long the lowest tick time is based on the header mark. - uint32_t mark_tick = - results->rawbuf[offset++] * kRawTick / kKelvinatorHdrMarkTicks; - if (!matchSpace(results->rawbuf[offset], kKelvinatorHdrSpace)) return false; - // Calculate how long the common tick time is based on the header space. - uint32_t space_tick = - results->rawbuf[offset++] * kRawTick / kKelvinatorHdrSpaceTicks; - - // Data (Command) (32 bits) - data_result = matchData( - &(results->rawbuf[offset]), 32, kKelvinatorBitMarkTicks * mark_tick, - kKelvinatorOneSpaceTicks * space_tick, - kKelvinatorBitMarkTicks * mark_tick, - kKelvinatorZeroSpaceTicks * space_tick, kTolerance, kMarkExcess, false); - if (data_result.success == false) return false; - data = data_result.data; - offset += data_result.used; - - // Record command data in the state. - for (uint16_t i = 0; i < 4; i++, data >>= 8) - results->state[state_pos + i] = data & 0xFF; - state_pos += 4; + uint16_t used; + // Header + Data Block #1 (32 bits) + used = matchGeneric(results->rawbuf + offset, results->state + pos, + results->rawlen - offset, 32, + kKelvinatorHdrMark, kKelvinatorHdrSpace, + kKelvinatorBitMark, kKelvinatorOneSpace, + kKelvinatorBitMark, kKelvinatorZeroSpace, + 0, 0, false, + _tolerance, kMarkExcess, false); + if (used == 0) return false; + offset += used; + pos += 4; // Command data footer (3 bits, B010) data_result = matchData( &(results->rawbuf[offset]), kKelvinatorCmdFooterBits, - kKelvinatorBitMarkTicks * mark_tick, - kKelvinatorOneSpaceTicks * space_tick, - kKelvinatorBitMarkTicks * mark_tick, - kKelvinatorZeroSpaceTicks * space_tick, kTolerance, kMarkExcess, false); + kKelvinatorBitMark, kKelvinatorOneSpace, + kKelvinatorBitMark, kKelvinatorZeroSpace, + _tolerance, kMarkExcess, false); if (data_result.success == false) return false; if (data_result.data != kKelvinatorCmdFooter) return false; offset += data_result.used; - // Interdata gap. - if (!matchMark(results->rawbuf[offset++], - kKelvinatorBitMarkTicks * mark_tick)) - return false; - if (!matchSpace(results->rawbuf[offset++], - kKelvinatorGapSpaceTicks * space_tick)) - return false; - - // Data (Options) (32 bits) - data_result = matchData( - &(results->rawbuf[offset]), 32, kKelvinatorBitMarkTicks * mark_tick, - kKelvinatorOneSpaceTicks * space_tick, - kKelvinatorBitMarkTicks * mark_tick, - kKelvinatorZeroSpaceTicks * space_tick, kTolerance, kMarkExcess, false); - if (data_result.success == false) return false; - data = data_result.data; - offset += data_result.used; - - // Record option data in the state. - for (uint16_t i = 0; i < 4; i++, data >>= 8) - results->state[state_pos + i] = data & 0xFF; - state_pos += 4; - - // Inter-sequence gap. (Double length gap) - if (!matchMark(results->rawbuf[offset++], - kKelvinatorBitMarkTicks * mark_tick)) - return false; - if (s == 0) { - if (!matchSpace(results->rawbuf[offset++], - kKelvinatorGapSpaceTicks * space_tick * 2)) - return false; - } else { - if (offset <= results->rawlen && - !matchAtLeast(results->rawbuf[offset], - kKelvinatorGapSpaceTicks * 2 * space_tick)) - return false; - } + // Gap + Data (Options) (32 bits) + used = matchGeneric(results->rawbuf + offset, results->state + pos, + results->rawlen - offset, 32, + kKelvinatorBitMark, kKelvinatorGapSpace, + kKelvinatorBitMark, kKelvinatorOneSpace, + kKelvinatorBitMark, kKelvinatorZeroSpace, + kKelvinatorBitMark, kKelvinatorGapSpace * 2, + s > 0, + _tolerance, kMarkExcess, false); + if (used == 0) return false; + offset += used; + pos += 4; } // Compliance if (strict) { - // Correct size/length) - if (state_pos != kKelvinatorStateLength) return false; // Verify the message's checksum is correct. if (!IRKelvinatorAC::validChecksum(results->state)) return false; } // Success - results->decode_type = KELVINATOR; - results->bits = state_pos * 8; + results->decode_type = decode_type_t::KELVINATOR; + results->bits = nbits; // No need to record the state as we stored it as we decoded it. // As we use result->state, we don't record value, address, or command as it // is a union data type. diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Kelvinator.h b/lib/IRremoteESP8266-2.6.5/src/ir_Kelvinator.h similarity index 71% rename from lib/IRremoteESP8266-2.6.0/src/ir_Kelvinator.h rename to lib/IRremoteESP8266-2.6.5/src/ir_Kelvinator.h index ce830c70a..fac26bf2e 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Kelvinator.h +++ b/lib/IRremoteESP8266-2.6.5/src/ir_Kelvinator.h @@ -2,6 +2,19 @@ // // Copyright 2016 David Conran +// Supports: +// Brand: Kelvinator, Model: YALIF Remote +// Brand: Kelvinator, Model: KSV26CRC A/C +// Brand: Kelvinator, Model: KSV26HRC A/C +// Brand: Kelvinator, Model: KSV35CRC A/C +// Brand: Kelvinator, Model: KSV35HRC A/C +// Brand: Kelvinator, Model: KSV53HRC A/C +// Brand: Kelvinator, Model: KSV62HRC A/C +// Brand: Kelvinator, Model: KSV70CRC A/C +// Brand: Kelvinator, Model: KSV70HRC A/C +// Brand: Kelvinator, Model: KSV80HRC A/C +// Brand: Green, Model: YAPOF3 remote + #ifndef IR_KELVINATOR_H_ #define IR_KELVINATOR_H_ @@ -9,8 +22,6 @@ #include #ifndef UNIT_TEST #include -#else -#include #endif #include "IRremoteESP8266.h" #include "IRsend.h" @@ -18,12 +29,6 @@ #include "IRsend_test.h" #endif -// KK KK EEEEEEE LL VV VV IIIII NN NN AAA TTTTTTT OOOOO RRRRRR -// KK KK EE LL VV VV III NNN NN AAAAA TTT OO OO RR RR -// KKKK EEEEE LL VV VV III NN N NN AA AA TTT OO OO RRRRRR -// KK KK EE LL VV VV III NN NNN AAAAAAA TTT OO OO RR RR -// KK KK EEEEEEE LLLLLLL VVV IIIII NN NN AA AA TTT OOOO0 RR RR - // Constants const uint8_t kKelvinatorAuto = 0; const uint8_t kKelvinatorCool = 1; @@ -32,6 +37,7 @@ const uint8_t kKelvinatorFan = 3; const uint8_t kKelvinatorHeat = 4; const uint8_t kKelvinatorBasicFanMax = 3; const uint8_t kKelvinatorFanAuto = 0; +const uint8_t kKelvinatorFanMin = 1; const uint8_t kKelvinatorFanMax = 5; const uint8_t kKelvinatorMinTemp = 16; // 16C const uint8_t kKelvinatorMaxTemp = 30; // 30C @@ -129,49 +135,50 @@ const uint8_t kKelvinatorAutoTemp = 25; // 25C // Classes class IRKelvinatorAC { public: - explicit IRKelvinatorAC(uint16_t pin); + explicit IRKelvinatorAC(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); - void stateReset(); + void stateReset(void); #if SEND_KELVINATOR void send(const uint16_t repeat = kKelvinatorDefaultRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } #endif // SEND_KELVINATOR - void begin(); - void on(); - void off(); - void setPower(bool state); - bool getPower(); - void setTemp(uint8_t temp); - uint8_t getTemp(); - void setFan(uint8_t fan); - uint8_t getFan(); - void setMode(uint8_t mode); - uint8_t getMode(); - void setSwingVertical(bool state); - bool getSwingVertical(); - void setSwingHorizontal(bool state); - bool getSwingHorizontal(); - void setQuiet(bool state); - bool getQuiet(); - void setIonFilter(bool state); - bool getIonFilter(); - void setLight(bool state); - bool getLight(); - void setXFan(bool state); - bool getXFan(); - void setTurbo(bool state); - bool getTurbo(); - uint8_t* getRaw(); - void setRaw(uint8_t new_code[]); + void begin(void); + void on(void); + void off(void); + void setPower(const bool on); + bool getPower(void); + void setTemp(const uint8_t degrees); + uint8_t getTemp(void); + void setFan(const uint8_t speed); + uint8_t getFan(void); + void setMode(const uint8_t mode); + uint8_t getMode(void); + void setSwingVertical(const bool on); + bool getSwingVertical(void); + void setSwingHorizontal(const bool on); + bool getSwingHorizontal(void); + void setQuiet(const bool on); + bool getQuiet(void); + void setIonFilter(const bool on); + bool getIonFilter(void); + void setLight(const bool on); + bool getLight(void); + void setXFan(const bool on); + bool getXFan(void); + void setTurbo(const bool on); + bool getTurbo(void); + uint8_t* getRaw(void); + void setRaw(const uint8_t new_code[]); static uint8_t calcBlockChecksum( const uint8_t* block, const uint16_t length = kKelvinatorStateLength / 2); static bool validChecksum(const uint8_t state[], const uint16_t length = kKelvinatorStateLength); uint8_t convertMode(const stdAc::opmode_t mode); -#ifdef ARDUINO - String toString(); -#else - std::string toString(); -#endif + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + stdAc::state_t toCommon(void); + String toString(void); #ifndef UNIT_TEST private: @@ -182,7 +189,7 @@ class IRKelvinatorAC { // The state of the IR remote in IR code form. uint8_t remote_state[kKelvinatorStateLength]; void checksum(const uint16_t length = kKelvinatorStateLength); - void fixup(); + void fixup(void); }; #endif // IR_KELVINATOR_H_ diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_LG.cpp b/lib/IRremoteESP8266-2.6.5/src/ir_LG.cpp similarity index 96% rename from lib/IRremoteESP8266-2.6.0/src/ir_LG.cpp rename to lib/IRremoteESP8266-2.6.5/src/ir_LG.cpp index 36c85ff15..124256e9f 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_LG.cpp +++ b/lib/IRremoteESP8266-2.6.5/src/ir_LG.cpp @@ -2,25 +2,18 @@ // Copyright 2015 cheaplin // Copyright 2017, 2018 David Conran +// Supports: +// Brand: LG, Model: 6711A20083V remote +// Brand: LG, Model: AKB74395308 remote + #include "ir_LG.h" #include #include "IRrecv.h" #include "IRsend.h" #include "IRutils.h" -// L GGGG -// L G -// L G GG -// L G G -// LLLLL GGG - // LG decode originally added by Darryl Smith (based on the JVC protocol) // LG send originally added by https://github.com/chaeplin -// -// Known supported devices: -// IR Remotes: -// 6711A20083V -// AKB74395308 // Constants const uint16_t kLgTick = 50; @@ -244,7 +237,7 @@ bool IRrecv::decodeLG(decode_results *results, uint16_t nbits, bool strict) { match_result_t data_result = matchData(&(results->rawbuf[offset]), nbits, bitmarkticks * m_tick, kLgOneSpaceTicks * s_tick, bitmarkticks * m_tick, - kLgZeroSpaceTicks * s_tick, kTolerance, 0); + kLgZeroSpaceTicks * s_tick, _tolerance, 0); if (data_result.success == false) return false; data = data_result.data; offset += data_result.used; diff --git a/lib/IRremoteESP8266-2.6.5/src/ir_LG.h b/lib/IRremoteESP8266-2.6.5/src/ir_LG.h new file mode 100644 index 000000000..01f81e2c1 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.5/src/ir_LG.h @@ -0,0 +1,15 @@ +// Copyright 2017 David Conran + +// Supports: +// Brand: LG, Model: 6711A20083V remote +// Brand: LG, Model: AKB74395308 remote + +#ifndef IR_LG_H_ +#define IR_LG_H_ + +#define __STDC_LIMIT_MACROS +#include + +uint8_t calcLGChecksum(uint16_t data); + +#endif // IR_LG_H_ diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Lasertag.cpp b/lib/IRremoteESP8266-2.6.5/src/ir_Lasertag.cpp similarity index 91% rename from lib/IRremoteESP8266-2.6.0/src/ir_Lasertag.cpp rename to lib/IRremoteESP8266-2.6.5/src/ir_Lasertag.cpp index b1cbdc9b1..f52b7477a 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Lasertag.cpp +++ b/lib/IRremoteESP8266-2.6.5/src/ir_Lasertag.cpp @@ -1,16 +1,11 @@ // Copyright 2017 David Conran +// Lasertag #include #include "IRrecv.h" #include "IRsend.h" #include "IRutils.h" -// LL AAA SSSSS EEEEEEE RRRRRR TTTTTTT AAA GGGG -// LL AAAAA SS EE RR RR TTT AAAAA GG GG -// LL AA AA SSSSS EEEEE RRRRRR TTT AA AA GG -// LL AAAAAAA SS EE RR RR TTT AAAAAAA GG GG -// LLLLLLL AA AA SSSSS EEEEEEE RR RR TTT AA AA GGGGGG - // Constants const uint16_t kLasertagMinSamples = 13; const uint16_t kLasertagTick = 333; diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Lego.cpp b/lib/IRremoteESP8266-2.6.5/src/ir_Lego.cpp similarity index 74% rename from lib/IRremoteESP8266-2.6.0/src/ir_Lego.cpp rename to lib/IRremoteESP8266-2.6.5/src/ir_Lego.cpp index b051aba51..932a568d0 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Lego.cpp +++ b/lib/IRremoteESP8266-2.6.5/src/ir_Lego.cpp @@ -8,12 +8,12 @@ // LEGO // (LEGO is a Registrated Trademark of the Lego Group.) // -// Supported Devices: -// - LEGO Power Functions IR Receiver +// Supports: +// Brand: LEGO Power Functions, Model: IR Receiver // // Ref: -// - https://github.com/markszabo/IRremoteESP8266/issues/641 -// - https://github.com/markszabo/IRremoteESP8266/files/2974525/LEGO_Power_Functions_RC_v120.pdf +// - https://github.com/crankyoldgit/IRremoteESP8266/issues/641 +// - https://github.com/crankyoldgit/IRremoteESP8266/files/2974525/LEGO_Power_Functions_RC_v120.pdf // Constants const uint16_t kLegoPfBitMark = 158; @@ -81,32 +81,17 @@ bool IRrecv::decodeLegoPf(decode_results* results, uint64_t data = 0; uint16_t offset = kStartOffset; - match_result_t data_result; - - // Header - if (!matchMark(results->rawbuf[offset++], kLegoPfBitMark)) return false; - if (!matchSpace(results->rawbuf[offset++], kLegoPfHdrSpace)) return false; - // Data (Typically 16 bits) - data_result = - matchData(&(results->rawbuf[offset]), nbits, - kLegoPfBitMark, kLegoPfOneSpace, - kLegoPfBitMark, kLegoPfZeroSpace, - kTolerance, kMarkExcess, true); - if (data_result.success == false) return false; - data = data_result.data; - offset += data_result.used; - uint16_t actualBits = data_result.used / 2; - - // Footer. - if (!matchMark(results->rawbuf[offset++], kLegoPfBitMark)) return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset], kLegoPfMinCommandLength)) - return false; + // Match Header + Data + Footer + if (!matchGeneric(results->rawbuf + offset, &data, + results->rawlen - offset, nbits, + kLegoPfBitMark, kLegoPfHdrSpace, + kLegoPfBitMark, kLegoPfOneSpace, + kLegoPfBitMark, kLegoPfZeroSpace, + kLegoPfBitMark, kLegoPfMinCommandLength, + true)) return false; // Compliance - if (actualBits < nbits) return false; if (strict) { - if (actualBits != nbits) return false; // Not as we expected. // Verify the Longitudinal Redundancy Check (LRC) uint16_t lrc_data = data; uint8_t lrc = 0xF; @@ -119,7 +104,7 @@ bool IRrecv::decodeLegoPf(decode_results* results, // Success results->decode_type = LEGOPF; - results->bits = actualBits; + results->bits = nbits; results->value = data; results->address = ((data >> (nbits - 4)) & 0b11) + 1; // Channel Id results->command = (data >> 4) & 0xFF; // Stuff between Channel Id and LRC. diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Lutron.cpp b/lib/IRremoteESP8266-2.6.5/src/ir_Lutron.cpp similarity index 90% rename from lib/IRremoteESP8266-2.6.0/src/ir_Lutron.cpp rename to lib/IRremoteESP8266-2.6.5/src/ir_Lutron.cpp index 00eb9383b..7c9835047 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Lutron.cpp +++ b/lib/IRremoteESP8266-2.6.5/src/ir_Lutron.cpp @@ -1,4 +1,5 @@ // Copyright 2018 David Conran +// Lutron #define __STDC_LIMIT_MACROS #include @@ -7,12 +8,6 @@ #include "IRsend.h" #include "IRutils.h" -// LL UU UU TTTTTTT RRRRRR OOOOO NN NN -// LL UU UU TTT RR RR OO OO NNN NN -// LL UU UU TTT RRRRRR OO OO NN N NN -// LL UU UU TTT RR RR OO OO NN NNN -// LLLLLLL UUUUU TTT RR RR OOOO0 NN NN - // Notes: // The Lutron protocol uses a sort of Run Length encoding to encode // its data. There is no header or footer per-se. @@ -22,7 +17,7 @@ // Constants // Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/515 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/515 const uint16_t kLutronTick = 2288; const uint32_t kLutronGap = 150000; // Completely made up value. const uint16_t kLutronDelta = 400; // +/- 300 usecs. @@ -42,7 +37,7 @@ const uint16_t kLutronDelta = 400; // +/- 300 usecs. // So, assume the 1 and only have a normal payload of 35 bits. // // Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/515 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/515 void IRsend::sendLutron(uint64_t data, uint16_t nbits, uint16_t repeat) { enableIROut(40000, 40); // 40Khz & 40% dutycycle. for (uint16_t r = 0; r <= repeat; r++) { @@ -73,7 +68,7 @@ void IRsend::sendLutron(uint64_t data, uint16_t nbits, uint16_t repeat) { // Notes: // // Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/515 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/515 bool IRrecv::decodeLutron(decode_results *results, uint16_t nbits, bool strict) { // Technically the smallest number of entries for the smallest message is '1'. diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_MWM.cpp b/lib/IRremoteESP8266-2.6.5/src/ir_MWM.cpp similarity index 94% rename from lib/IRremoteESP8266-2.6.0/src/ir_MWM.cpp rename to lib/IRremoteESP8266-2.6.5/src/ir_MWM.cpp index 61eac49e2..b747b224b 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_MWM.cpp +++ b/lib/IRremoteESP8266-2.6.5/src/ir_MWM.cpp @@ -1,4 +1,7 @@ // Copyright 2018 Brett T. Warden + +// MWM + // derived from ir_Lasertag.cpp, Copyright 2017 David Conran #include @@ -6,12 +9,6 @@ #include "IRsend.h" #include "IRutils.h" -// MM MM WW WW MM MM -// MMM MMM WW WW MMM MMM -// MM M MM WW W WW MM M MM -// MM MM WWW WWW MM MM -// MM MM WW WW MM MM - // Constants const uint16_t kMWMMinSamples = 6; // Msgs are >=3 bytes, bytes have >=2 // samples @@ -37,7 +34,8 @@ const int16_t kMark = 0; // // Status: Implemented. // -void IRsend::sendMWM(uint8_t data[], uint16_t nbytes, uint16_t repeat) { +void IRsend::sendMWM(const uint8_t data[], const uint16_t nbytes, + const uint16_t repeat) { if (nbytes < 3) return; // Shortest possible message is 3 bytes // Set 38kHz IR carrier frequency & a 1/4 (25%) duty cycle. diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Magiquest.cpp b/lib/IRremoteESP8266-2.6.5/src/ir_Magiquest.cpp similarity index 100% rename from lib/IRremoteESP8266-2.6.0/src/ir_Magiquest.cpp rename to lib/IRremoteESP8266-2.6.5/src/ir_Magiquest.cpp diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Magiquest.h b/lib/IRremoteESP8266-2.6.5/src/ir_Magiquest.h similarity index 100% rename from lib/IRremoteESP8266-2.6.0/src/ir_Magiquest.h rename to lib/IRremoteESP8266-2.6.5/src/ir_Magiquest.h diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Midea.cpp b/lib/IRremoteESP8266-2.6.5/src/ir_Midea.cpp similarity index 56% rename from lib/IRremoteESP8266-2.6.0/src/ir_Midea.cpp rename to lib/IRremoteESP8266-2.6.5/src/ir_Midea.cpp index 8d5d9494f..80744c9cb 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Midea.cpp +++ b/lib/IRremoteESP8266-2.6.5/src/ir_Midea.cpp @@ -1,4 +1,5 @@ // Copyright 2017 bwze, crankyoldgit +// Midea #include "ir_Midea.h" #include @@ -9,12 +10,6 @@ #include "IRsend.h" #include "IRutils.h" -// MM MM IIIII DDDDD EEEEEEE AAA -// MMM MMM III DD DD EE AAAAA -// MM MM MM III DD DD EEEEE AA AA -// MM MM III DD DD EE AAAAAAA -// MM MM IIIII DDDDDD EEEEEEE AA AA - // Midea A/C added by (send) bwze/crankyoldgit & (decode) crankyoldgit // // Equipment it seems compatible with: @@ -42,6 +37,13 @@ const uint16_t kMideaMinGapTicks = const uint16_t kMideaMinGap = kMideaMinGapTicks * kMideaTick; const uint8_t kMideaTolerance = 30; // Percent +using irutils::addBoolToString; +using irutils::addFanToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addTempToString; + #if SEND_MIDEA // Send a Midea message // @@ -91,81 +93,116 @@ void IRsend::sendMidea(uint64_t data, uint16_t nbits, uint16_t repeat) { // Warning: Consider this very alpha code. // Initialise the object. -IRMideaAC::IRMideaAC(uint16_t pin) : _irsend(pin) { stateReset(); } +IRMideaAC::IRMideaAC(const uint16_t pin, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { this->stateReset(); } // Reset the state of the remote to a known good state/sequence. -void IRMideaAC::stateReset() { +void IRMideaAC::stateReset(void) { // Power On, Mode Auto, Fan Auto, Temp = 25C/77F remote_state = 0xA1826FFFFF62; + _SwingVToggle = false; } // Configure the pin for output. -void IRMideaAC::begin() { _irsend.begin(); } +void IRMideaAC::begin(void) { _irsend.begin(); } #if SEND_MIDEA // Send the current desired state to the IR LED. void IRMideaAC::send(const uint16_t repeat) { - checksum(); // Ensure correct checksum before sending. + this->checksum(); // Ensure correct checksum before sending. _irsend.sendMidea(remote_state, kMideaBits, repeat); + // Handle toggling the swing if we need to. + if (_SwingVToggle && !isSwingVToggle()) { + _irsend.sendMidea(kMideaACToggleSwingV, kMideaBits, repeat); + } + _SwingVToggle = false; // The toggle message has been sent, so reset. } #endif // SEND_MIDEA // Return a pointer to the internal state date of the remote. -uint64_t IRMideaAC::getRaw() { - checksum(); +uint64_t IRMideaAC::getRaw(void) { + this->checksum(); return remote_state & kMideaACStateMask; } // Override the internal state with the new state. -void IRMideaAC::setRaw(uint64_t newState) { +void IRMideaAC::setRaw(const uint64_t newState) { remote_state = newState & kMideaACStateMask; } // Set the requested power state of the A/C to off. -void IRMideaAC::on() { remote_state |= kMideaACPower; } +void IRMideaAC::on(void) { remote_state |= kMideaACPower; } // Set the requested power state of the A/C to off. -void IRMideaAC::off() { remote_state &= (kMideaACStateMask ^ kMideaACPower); } +void IRMideaAC::off(void) { + remote_state &= (kMideaACStateMask ^ kMideaACPower); +} // Set the requested power state of the A/C. -void IRMideaAC::setPower(const bool state) { - if (state) - on(); +void IRMideaAC::setPower(const bool on) { + if (on) + this->on(); else - off(); + this->off(); } // Return the requested power state of the A/C. -bool IRMideaAC::getPower() { return (remote_state & kMideaACPower); } +bool IRMideaAC::getPower(void) { return (remote_state & kMideaACPower); } + +// Returns true if we want the A/C unit to work natively in Celsius. +bool IRMideaAC::getUseCelsius(void) { + return !(remote_state & kMideaACCelsiusBit); +} + +// Set the A/C unit to use Celsius natively. +void IRMideaAC::setUseCelsius(const bool on) { + if (on != getUseCelsius()) { // We need to change. + uint8_t native_temp = getTemp(!on); // Get the old native temp. + if (on) + remote_state &= ~kMideaACCelsiusBit; // Clear the bit + else + remote_state |= kMideaACCelsiusBit; // Set the bit + setTemp(native_temp, !on); // Reset temp using the old native temp. + } +} // Set the temperature. // Args: // temp: Temp. in degrees. // useCelsius: Degree type to use. Celsius (true) or Fahrenheit (false) void IRMideaAC::setTemp(const uint8_t temp, const bool useCelsius) { - uint8_t new_temp = temp; + uint8_t max_temp = kMideaACMaxTempF; + uint8_t min_temp = kMideaACMinTempF; if (useCelsius) { - new_temp = std::max(kMideaACMinTempC, new_temp); - new_temp = std::min(kMideaACMaxTempC, new_temp); - new_temp = (uint8_t)((new_temp * 1.8) + 32.5); // 0.5 so we rounding. + max_temp = kMideaACMaxTempC; + min_temp = kMideaACMinTempC; } - new_temp = std::max(kMideaACMinTempF, new_temp); - new_temp = std::min(kMideaACMaxTempF, new_temp); - new_temp -= kMideaACMinTempF; + uint8_t new_temp = std::min(max_temp, std::max(min_temp, temp)); + if (getUseCelsius() && !useCelsius) // Native is in C, new_temp is in F + new_temp = fahrenheitToCelsius(new_temp) - kMideaACMinTempC; + else if (!getUseCelsius() && useCelsius) // Native is in F, new_temp is in C + new_temp = celsiusToFahrenheit(new_temp) - kMideaACMinTempF; + else // Native and desired are the same units. + new_temp -= min_temp; + // Set the actual data. remote_state &= kMideaACTempMask; remote_state |= ((uint64_t)new_temp << 24); } // Return the set temp. // Args: -// useCelsius: Flag indicating if the results are in Celsius or Fahrenheit. +// celsius: Flag indicating if the results are in Celsius or Fahrenheit. // Returns: // A uint8_t containing the temperature. -uint8_t IRMideaAC::getTemp(const bool useCelsius) { - uint8_t temp = ((remote_state >> 24) & 0x1F) + kMideaACMinTempF; - if (useCelsius) { - temp = (uint8_t)((temp - 32) / 1.8); - } +uint8_t IRMideaAC::getTemp(const bool celsius) { + uint8_t temp = ((remote_state >> 24) & 0x1F); + if (getUseCelsius()) + temp += kMideaACMinTempC; + else + temp += kMideaACMinTempF; + if (celsius && !getUseCelsius()) temp = fahrenheitToCelsius(temp) + 0.5; + if (!celsius && getUseCelsius()) temp = celsiusToFahrenheit(temp); return temp; } @@ -187,42 +224,54 @@ void IRMideaAC::setFan(const uint8_t fan) { } // Return the requested state of the unit's fan. -uint8_t IRMideaAC::getFan() { return (remote_state >> 35) & 0b111; } +uint8_t IRMideaAC::getFan(void) { return (remote_state >> 35) & 0b111; } // Get the requested climate operation mode of the a/c unit. // Returns: // A uint8_t containing the A/C mode. -uint8_t IRMideaAC::getMode() { return ((remote_state >> 32) & 0b111); } +uint8_t IRMideaAC::getMode(void) { return ((remote_state >> 32) & 0b111); } // Set the requested climate operation mode of the a/c unit. void IRMideaAC::setMode(const uint8_t mode) { - // If we get an unexpected mode, default to AUTO. - uint64_t new_mode; switch (mode) { case kMideaACAuto: case kMideaACCool: case kMideaACHeat: case kMideaACDry: case kMideaACFan: - new_mode = mode; - break; + remote_state &= kMideaACModeMask; + remote_state |= ((uint64_t)mode << 32); + return; default: - new_mode = kMideaACAuto; + this->setMode(kMideaACAuto); } - remote_state &= kMideaACModeMask; - remote_state |= (new_mode << 32); } // Set the Sleep state of the A/C. -void IRMideaAC::setSleep(const bool state) { - if (state) +void IRMideaAC::setSleep(const bool on) { + if (on) remote_state |= kMideaACSleep; else remote_state &= (kMideaACStateMask ^ kMideaACSleep); } // Return the Sleep state of the A/C. -bool IRMideaAC::getSleep() { return (remote_state & kMideaACSleep); } +bool IRMideaAC::getSleep(void) { return (remote_state & kMideaACSleep); } + +// Set the A/C to toggle the vertical swing toggle for the next send. +void IRMideaAC::setSwingVToggle(const bool on) { + _SwingVToggle = on; +} + +// Return if the message/state is just a Swing V toggle message/command. +bool IRMideaAC::isSwingVToggle(void) { + return remote_state == kMideaACToggleSwingV; +} +// Return the Swing V toggle state of the A/C. +bool IRMideaAC::getSwingVToggle(void) { + _SwingVToggle |= isSwingVToggle(); + return _SwingVToggle; +} // Calculate the checksum for a given array. // Args: @@ -251,7 +300,7 @@ bool IRMideaAC::validChecksum(const uint64_t state) { } // Calculate & set the checksum for the current internal state of the remote. -void IRMideaAC::checksum() { +void IRMideaAC::checksum(void) { // Stored the checksum value in the last byte. remote_state &= kMideaACChecksumMask; remote_state |= calcChecksum(remote_state); @@ -290,65 +339,81 @@ uint8_t IRMideaAC::convertFan(const stdAc::fanspeed_t speed) { } } +// Convert a native mode to it's common equivalent. +stdAc::opmode_t IRMideaAC::toCommonMode(const uint8_t mode) { + switch (mode) { + case kMideaACCool: return stdAc::opmode_t::kCool; + case kMideaACHeat: return stdAc::opmode_t::kHeat; + case kMideaACDry: return stdAc::opmode_t::kDry; + case kMideaACFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRMideaAC::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kMideaACFanHigh: return stdAc::fanspeed_t::kMax; + case kMideaACFanMed: return stdAc::fanspeed_t::kMedium; + case kMideaACFanLow: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRMideaAC::toCommon(const stdAc::state_t *prev) { + stdAc::state_t result; + if (prev != NULL) { + result = *prev; + } else { + // Fixed/Not supported/Non-zero defaults. + result.protocol = decode_type_t::MIDEA; + result.model = -1; // No models used. + result.swingh = stdAc::swingh_t::kOff; + result.swingv = stdAc::swingv_t::kOff; + result.quiet = false; + result.turbo = false; + result.clean = false; + result.econo = false; + result.filter = false; + result.light = false; + result.beep = false; + result.sleep = -1; + result.clock = -1; + } + if (this->isSwingVToggle()) { + result.swingv = result.swingv != stdAc::swingv_t::kOff ? + stdAc::swingv_t::kAuto : stdAc::swingv_t::kOff; + return result; + } + result.power = this->getPower(); + result.mode = this->toCommonMode(this->getMode()); + result.celsius = this->getUseCelsius(); + result.degrees = this->getTemp(result.celsius); + result.fanspeed = this->toCommonFanSpeed(this->getFan()); + result.sleep = this->getSleep() ? 0 : -1; + return result; +} + // Convert the internal state into a human readable string. -#ifdef ARDUINO -String IRMideaAC::toString() { +String IRMideaAC::toString(void) { String result = ""; -#else -std::string IRMideaAC::toString() { - std::string result = ""; -#endif // ARDUINO - result += F("Power: "); - if (getPower()) - result += F("On"); - else - result += F("Off"); - result += F(", Mode: "); - result += uint64ToString(getMode()); - switch (getMode()) { - case kMideaACAuto: - result += F(" (AUTO)"); - break; - case kMideaACCool: - result += F(" (COOL)"); - break; - case kMideaACHeat: - result += F(" (HEAT)"); - break; - case kMideaACDry: - result += F(" (DRY)"); - break; - case kMideaACFan: - result += F(" (FAN)"); - break; - default: - result += F(" (UNKNOWN)"); + result.reserve(100); // Reserve some heap for the string to reduce fragging. + if (!isSwingVToggle()) { + result += addBoolToString(getPower(), F("Power"), false); + result += addModeToString(getMode(), kMideaACAuto, kMideaACCool, + kMideaACHeat, kMideaACDry, kMideaACFan); + result += addBoolToString(getUseCelsius(), F("Celsius")); + result += addTempToString(getTemp(true)); + result += '/'; + result += uint64ToString(getTemp(false)); + result += 'F'; + result += addFanToString(getFan(), kMideaACFanHigh, kMideaACFanLow, + kMideaACFanAuto, kMideaACFanAuto, kMideaACFanMed); + result += addBoolToString(getSleep(), F("Sleep")); } - result += F(", Temp: "); - result += uint64ToString(getTemp(true)); - result += F("C/"); - result += uint64ToString(getTemp(false)); - result += F("F, Fan: "); - result += uint64ToString(getFan()); - switch (getFan()) { - case kMideaACFanAuto: - result += F(" (AUTO)"); - break; - case kMideaACFanLow: - result += F(" (LOW)"); - break; - case kMideaACFanMed: - result += F(" (MED)"); - break; - case kMideaACFanHigh: - result += F(" (HI)"); - break; - } - result += F(", Sleep: "); - if (getSleep()) - result += F("On"); - else - result += F("Off"); + result += addBoolToString(getSwingVToggle(), F("Swing(V) Toggle"), + !isSwingVToggle()); return result; } @@ -365,9 +430,6 @@ std::string IRMideaAC::toString() { // Status: Alpha / Needs testing against a real device. // bool IRrecv::decodeMidea(decode_results *results, uint16_t nbits, bool strict) { - if (nbits % 8 != 0) // nbits has to be a multiple of nr. of bits in a byte. - return false; - uint8_t min_nr_of_messages = 1; if (strict) { if (nbits != kMideaBits) return false; // Not strictly a MIDEA message. @@ -388,35 +450,16 @@ bool IRrecv::decodeMidea(decode_results *results, uint16_t nbits, bool strict) { return false; // We can't possibly capture a Midea packet that big. for (uint8_t i = 0; i < min_nr_of_messages; i++) { - // Header - if (!matchMark(results->rawbuf[offset], kMideaHdrMark)) return false; - // Calculate how long the common tick time is based on the header mark. - uint32_t m_tick = results->rawbuf[offset++] * kRawTick / kMideaHdrMarkTicks; - if (!matchSpace(results->rawbuf[offset], kMideaHdrSpace)) return false; - // Calculate how long the common tick time is based on the header space. - uint32_t s_tick = - results->rawbuf[offset++] * kRawTick / kMideaHdrSpaceTicks; - - // Data (Normal) - match_result_t data_result = matchData( - &(results->rawbuf[offset]), nbits, kMideaBitMarkTicks * m_tick, - kMideaOneSpaceTicks * s_tick, kMideaBitMarkTicks * m_tick, - kMideaZeroSpaceTicks * s_tick, kMideaTolerance); - if (data_result.success == false) return false; - offset += data_result.used; - if (i % 2 == 0) - data = data_result.data; - else - inverted = data_result.data; - - // Footer - if (!matchMark(results->rawbuf[offset++], kMideaBitMarkTicks * m_tick, - kMideaTolerance)) - return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset++], kMideaMinGapTicks * s_tick, - kMideaTolerance)) - return false; + // Match Header + Data + Footer + uint16_t used; + used = matchGeneric(results->rawbuf + offset, i % 2 ? &inverted : &data, + results->rawlen - offset, nbits, + kMideaHdrMark, kMideaHdrSpace, + kMideaBitMark, kMideaOneSpace, + kMideaBitMark, kMideaZeroSpace, + kMideaBitMark, kMideaMinGap, false, kMideaTolerance); + if (!used) return false; + offset += used; } // Compliance diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Midea.h b/lib/IRremoteESP8266-2.6.5/src/ir_Midea.h similarity index 60% rename from lib/IRremoteESP8266-2.6.0/src/ir_Midea.h rename to lib/IRremoteESP8266-2.6.5/src/ir_Midea.h index ab14eb252..289821778 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Midea.h +++ b/lib/IRremoteESP8266-2.6.5/src/ir_Midea.h @@ -1,4 +1,12 @@ // Copyright 2017 David Conran +// Midea + +// Supports: +// Brand: Pioneer System, Model: RYBO12GMFILCAD A/C (12K BTU) +// Brand: Pioneer System, Model: RUBO18GMFILCAD A/C (18K BTU) +// Brand: Comfee, Model: MPD1-12CRN7 A/C +// Brand: Keystone, Model: RG57H4(B)BGEF remote + #ifndef IR_MIDEA_H_ #define IR_MIDEA_H_ @@ -6,8 +14,6 @@ #include #ifdef ARDUINO #include -#else -#include #endif #include "IRremoteESP8266.h" #include "IRsend.h" @@ -15,12 +21,6 @@ #include "IRsend_test.h" #endif -// MM MM IIIII DDDDD EEEEEEE AAA -// MMM MMM III DD DD EE AAAAA -// MM MM MM III DD DD EEEEE AA AA -// MM MM III DD DD EE AAAAAAA -// MM MM IIIII DDDDDD EEEEEEE AA AA - // Midea added by crankyoldgit & bwze // Ref: // https://docs.google.com/spreadsheets/d/1TZh4jWrx4h9zzpYUI9aYXMl1fYOiqu-xVuOOMqagxrs/edit?usp=sharing @@ -39,13 +39,15 @@ const uint64_t kMideaACPower = 1ULL << 39; const uint64_t kMideaACSleep = 1ULL << 38; const uint8_t kMideaACMinTempF = 62; // Fahrenheit const uint8_t kMideaACMaxTempF = 86; // Fahrenheit -const uint8_t kMideaACMinTempC = 16; // Celsius +const uint8_t kMideaACMinTempC = 17; // Celsius const uint8_t kMideaACMaxTempC = 30; // Celsius -const uint64_t kMideaACStateMask = 0x0000FFFFFFFFFFFF; -const uint64_t kMideaACTempMask = 0x0000FFFFE0FFFFFF; -const uint64_t kMideaACFanMask = 0x0000FFC7FFFFFFFF; -const uint64_t kMideaACModeMask = 0x0000FFF8FFFFFFFF; +const uint64_t kMideaACStateMask = 0x0000FFFFFFFFFFFF; +const uint64_t kMideaACCelsiusBit = 0x0000000020000000; +const uint64_t kMideaACTempMask = 0x0000FFFFE0FFFFFF; +const uint64_t kMideaACFanMask = 0x0000FFC7FFFFFFFF; +const uint64_t kMideaACModeMask = 0x0000FFF8FFFFFFFF; const uint64_t kMideaACChecksumMask = 0x0000FFFFFFFFFF00; +const uint64_t kMideaACToggleSwingV = 0x0000A201FFFFFF7C; // Legacy defines. (Deprecated) #define MIDEA_AC_COOL kMideaACCool @@ -66,35 +68,41 @@ const uint64_t kMideaACChecksumMask = 0x0000FFFFFFFFFF00; class IRMideaAC { public: - explicit IRMideaAC(uint16_t pin); + explicit IRMideaAC(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); - void stateReset(); + void stateReset(void); #if SEND_MIDEA void send(const uint16_t repeat = kMideaMinRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } #endif // SEND_MIDEA - void begin(); - void on(); - void off(); - void setPower(const bool state); - bool getPower(); + void begin(void); + void on(void); + void off(void); + void setPower(const bool on); + bool getPower(void); + bool getUseCelsius(void); + void setUseCelsius(const bool celsius); void setTemp(const uint8_t temp, const bool useCelsius = false); uint8_t getTemp(const bool useCelsius = false); void setFan(const uint8_t fan); - uint8_t getFan(); + uint8_t getFan(void); void setMode(const uint8_t mode); - uint8_t getMode(); - void setRaw(uint64_t newState); - uint64_t getRaw(); + uint8_t getMode(void); + void setRaw(const uint64_t newState); + uint64_t getRaw(void); static bool validChecksum(const uint64_t state); - void setSleep(const bool state); - bool getSleep(); + void setSleep(const bool on); + bool getSleep(void); + bool isSwingVToggle(void); + void setSwingVToggle(const bool on); + bool getSwingVToggle(void); uint8_t convertMode(const stdAc::opmode_t mode); uint8_t convertFan(const stdAc::fanspeed_t speed); -#ifdef ARDUINO - String toString(); -#else - std::string toString(); -#endif + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + stdAc::state_t toCommon(const stdAc::state_t *prev = NULL); + String toString(void); #ifndef UNIT_TEST private: @@ -103,7 +111,8 @@ class IRMideaAC { IRsendTest _irsend; #endif uint64_t remote_state; - void checksum(); + bool _SwingVToggle; + void checksum(void); static uint8_t calcChecksum(const uint64_t state); }; diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Mitsubishi.cpp b/lib/IRremoteESP8266-2.6.5/src/ir_Mitsubishi.cpp similarity index 66% rename from lib/IRremoteESP8266-2.6.0/src/ir_Mitsubishi.cpp rename to lib/IRremoteESP8266-2.6.5/src/ir_Mitsubishi.cpp index ca9bef5d9..c78b1d21a 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Mitsubishi.cpp +++ b/lib/IRremoteESP8266-2.6.5/src/ir_Mitsubishi.cpp @@ -2,6 +2,8 @@ // Copyright 2017-2018 David Conran // Copyright 2018 Denes Varga +// Mitsubishi + #include "ir_Mitsubishi.h" #include #ifndef ARDUINO @@ -11,12 +13,6 @@ #include "IRsend.h" #include "IRutils.h" -// MMMMM IIIII TTTTT SSSS U U BBBB IIIII SSSS H H IIIII -// M M M I T S U U B B I S H H I -// M M M I T SSS U U BBBB I SSS HHHHH I -// M M I T S U U B B I S H H I -// M M IIIII T SSSS UUU BBBBB IIIII SSSS H H IIIII - // Mitsubishi (TV) decoding added from https://github.com/z3t0/Arduino-IRremote // Mitsubishi (TV) sending & Mitsubishi A/C support added by David Conran @@ -42,7 +38,7 @@ const uint16_t kMitsubishiMinGap = kMitsubishiMinGapTicks * kMitsubishiTick; // Mitsubishi Projector (HC3000) // Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/441 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/441 const uint16_t kMitsubishi2HdrMark = 8400; const uint16_t kMitsubishi2HdrSpace = kMitsubishi2HdrMark / 2; @@ -63,6 +59,14 @@ const uint16_t kMitsubishiAcZeroSpace = 420; const uint16_t kMitsubishiAcRptMark = 440; const uint16_t kMitsubishiAcRptSpace = 17100; +using irutils::addBoolToString; +using irutils::addFanToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addTempToString; +using irutils::minsToString; + #if SEND_MITSUBISHI // Send a Mitsubishi message // @@ -105,44 +109,23 @@ void IRsend::sendMitsubishi(uint64_t data, uint16_t nbits, uint16_t repeat) { // GlobalCache's Control Tower's Mitsubishi TV data. bool IRrecv::decodeMitsubishi(decode_results *results, uint16_t nbits, bool strict) { - if (results->rawlen < 2 * nbits + kFooter - 1) - return false; // Shorter than shortest possibly expected. if (strict && nbits != kMitsubishiBits) return false; // Request is out of spec. uint16_t offset = kStartOffset; uint64_t data = 0; - // No Header - // But try to auto-calibrate off the initial mark signal. - if (!matchMark(results->rawbuf[offset], kMitsubishiBitMark, 30)) return false; - // Calculate how long the common tick time is based on the initial mark. - uint32_t tick = results->rawbuf[offset] * kRawTick / kMitsubishiBitMarkTicks; - - // Data - match_result_t data_result = matchData( - &(results->rawbuf[offset]), nbits, kMitsubishiBitMarkTicks * tick, - kMitsubishiOneSpaceTicks * tick, kMitsubishiBitMarkTicks * tick, - kMitsubishiZeroSpaceTicks * tick); - if (data_result.success == false) return false; - data = data_result.data; - offset += data_result.used; - uint16_t actualBits = data_result.used / 2; - - // Footer - if (!matchMark(results->rawbuf[offset++], kMitsubishiBitMarkTicks * tick, 30)) - return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset], kMitsubishiMinGapTicks * tick)) - return false; - - // Compliance - if (actualBits < nbits) return false; - if (strict && actualBits != nbits) return false; // Not as we expected. - + // Match Data + Footer + if (!matchGeneric(results->rawbuf + offset, &data, + results->rawlen - offset, nbits, + 0, 0, // No header + kMitsubishiBitMark, kMitsubishiOneSpace, + kMitsubishiBitMark, kMitsubishiZeroSpace, + kMitsubishiBitMark, kMitsubishiMinGap, + true, 30)) return false; // Success results->decode_type = MITSUBISHI; - results->bits = actualBits; + results->bits = nbits; results->value = data; results->address = 0; results->command = 0; @@ -168,7 +151,7 @@ bool IRrecv::decodeMitsubishi(decode_results *results, uint16_t nbits, // i.e. Allegedly, the real remote requires the "Off" button pressed twice. // You will need to add a suitable gap yourself. // Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/441 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/441 void IRsend::sendMitsubishi2(uint64_t data, uint16_t nbits, uint16_t repeat) { for (uint16_t i = 0; i <= repeat; i++) { // First half of the data. @@ -203,7 +186,7 @@ void IRsend::sendMitsubishi2(uint64_t data, uint16_t nbits, uint16_t repeat) { // * Mitsubishi HC3000 projector's remote. // // Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/441 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/441 bool IRrecv::decodeMitsubishi2(decode_results *results, uint16_t nbits, bool strict) { if (results->rawlen < 2 * nbits + kHeader + (kFooter * 2) - 1) @@ -212,47 +195,34 @@ bool IRrecv::decodeMitsubishi2(decode_results *results, uint16_t nbits, return false; // Request is out of spec. uint16_t offset = kStartOffset; - uint64_t data = 0; - uint16_t actualBits = 0; + results->value = 0; // Header if (!matchMark(results->rawbuf[offset++], kMitsubishi2HdrMark)) return false; if (!matchSpace(results->rawbuf[offset++], kMitsubishi2HdrSpace)) return false; - for (uint8_t i = 1; i <= 2; i++) { - // Data - match_result_t data_result = matchData( - &(results->rawbuf[offset]), nbits / 2, kMitsubishi2BitMark, - kMitsubishi2OneSpace, kMitsubishi2BitMark, kMitsubishi2ZeroSpace); - if (data_result.success == false) return false; - data <<= nbits / 2; - data += data_result.data; - offset += data_result.used; - actualBits += data_result.used / 2; - - // Footer - if (!matchMark(results->rawbuf[offset++], kMitsubishi2BitMark)) - return false; - if (i % 2) { // Every odd data block, we expect a HDR space. - if (!matchSpace(results->rawbuf[offset++], kMitsubishi2HdrSpace)) - return false; - } else { // Every even data block, we expect Min Gap or end of the message. - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset++], kMitsubishi2MinGap)) - return false; - } + for (uint8_t i = 0; i < 2; i++) { + // Match Data + Footer + uint16_t used; + uint64_t data = 0; + used = matchGeneric(results->rawbuf + offset, &data, + results->rawlen - offset, nbits / 2, + 0, 0, // No header + kMitsubishi2BitMark, kMitsubishi2OneSpace, + kMitsubishi2BitMark, kMitsubishi2ZeroSpace, + kMitsubishi2BitMark, kMitsubishi2HdrSpace, + i % 2); + if (!used) return false; + offset += used; + results->value <<= (nbits / 2); + results->value += data; } - // Compliance - if (actualBits < nbits) return false; - if (strict && actualBits != nbits) return false; // Not as we expected. - // Success results->decode_type = MITSUBISHI2; - results->bits = actualBits; - results->value = data; - results->address = data >> actualBits / 2; - results->command = data & ((1 << (actualBits / 2)) - 1); + results->bits = nbits; + results->address = results->value >> (nbits / 2); + results->command = results->value & ((1 << (nbits / 2)) - 1); return true; } #endif // DECODE_MITSUBISHI2 @@ -268,8 +238,8 @@ bool IRrecv::decodeMitsubishi2(decode_results *results, uint16_t nbits, // // Status: BETA / Appears to be working. // -void IRsend::sendMitsubishiAC(unsigned char data[], uint16_t nbytes, - uint16_t repeat) { +void IRsend::sendMitsubishiAC(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { if (nbytes < kMitsubishiACStateLength) return; // Not enough bytes to send a proper message. @@ -313,7 +283,7 @@ bool IRrecv::decodeMitsubishiAC(decode_results *results, uint16_t nbits, do { failure = false; // Header: - // Somtime happens that junk signals arrives before the real message + // Sometime happens that junk signals arrives before the real message bool headerFound = false; while (!headerFound && offset < (results->rawlen - (kMitsubishiACBits * 2 + 2))) { @@ -332,7 +302,7 @@ bool IRrecv::decodeMitsubishiAC(decode_results *results, uint16_t nbits, data_result = matchData(&(results->rawbuf[offset]), 8, kMitsubishiAcBitMark, kMitsubishiAcOneSpace, kMitsubishiAcBitMark, - kMitsubishiAcZeroSpace, kTolerance, kMarkExcess, false); + kMitsubishiAcZeroSpace, _tolerance, kMarkExcess, false); if (data_result.success == false) { failure = true; DPRINT("Byte decode failed at #"); @@ -395,7 +365,7 @@ bool IRrecv::decodeMitsubishiAC(decode_results *results, uint16_t nbits, data_result = matchData(&(results->rawbuf[offset]), 8, kMitsubishiAcBitMark, kMitsubishiAcOneSpace, kMitsubishiAcBitMark, - kMitsubishiAcZeroSpace, kTolerance, kMarkExcess, false); + kMitsubishiAcZeroSpace, _tolerance, kMarkExcess, false); if (data_result.success == false || data_result.data != results->state[i]) { DPRINTLN("Repeat payload error."); @@ -420,10 +390,12 @@ bool IRrecv::decodeMitsubishiAC(decode_results *results, uint16_t nbits, // Equipment it seems compatible with: // * // Initialise the object. -IRMitsubishiAC::IRMitsubishiAC(uint16_t pin) : _irsend(pin) { stateReset(); } +IRMitsubishiAC::IRMitsubishiAC(const uint16_t pin, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { this->stateReset(); } // Reset the state of the remote to a known good state/sequence. -void IRMitsubishiAC::stateReset() { +void IRMitsubishiAC::stateReset(void) { // The state of the IR remote in IR code form. // Known good state obtained from: // https://github.com/r45635/HVAC-IR-Control/blob/master/HVAC_ESP8266/HVAC_ESP8266.ino#L108 @@ -445,39 +417,39 @@ void IRMitsubishiAC::stateReset() { for (uint8_t i = 11; i < kMitsubishiACStateLength - 1; i++) remote_state[i] = 0; remote_state[kMitsubishiACStateLength - 1] = 0x1F; - checksum(); // Calculate the checksum + this->checksum(); // Calculate the checksum } // Configure the pin for output. -void IRMitsubishiAC::begin() { _irsend.begin(); } +void IRMitsubishiAC::begin(void) { _irsend.begin(); } #if SEND_MITSUBISHI_AC // Send the current desired state to the IR LED. void IRMitsubishiAC::send(const uint16_t repeat) { - checksum(); // Ensure correct checksum before sending. + this->checksum(); // Ensure correct checksum before sending. _irsend.sendMitsubishiAC(remote_state, kMitsubishiACStateLength, repeat); } #endif // SEND_MITSUBISHI_AC // Return a pointer to the internal state date of the remote. -uint8_t *IRMitsubishiAC::getRaw() { - checksum(); +uint8_t *IRMitsubishiAC::getRaw(void) { + this->checksum(); return remote_state; } -void IRMitsubishiAC::setRaw(uint8_t *data) { +void IRMitsubishiAC::setRaw(const uint8_t *data) { for (uint8_t i = 0; i < (kMitsubishiACStateLength - 1); i++) { remote_state[i] = data[i]; } - checksum(); + this->checksum(); } // Calculate the checksum for the current internal state of the remote. -void IRMitsubishiAC::checksum() { - remote_state[17] = calculateChecksum(remote_state); +void IRMitsubishiAC::checksum(void) { + remote_state[17] = this->calculateChecksum(remote_state); } -uint8_t IRMitsubishiAC::calculateChecksum(uint8_t *data) { +uint8_t IRMitsubishiAC::calculateChecksum(const uint8_t *data) { uint8_t sum = 0; // Checksum is simple addition of all previous bytes stored // as an 8 bit value. @@ -486,45 +458,46 @@ uint8_t IRMitsubishiAC::calculateChecksum(uint8_t *data) { } // Set the requested power state of the A/C to off. -void IRMitsubishiAC::on() { +void IRMitsubishiAC::on(void) { // state = ON; remote_state[5] |= kMitsubishiAcPower; } // Set the requested power state of the A/C to off. -void IRMitsubishiAC::off() { +void IRMitsubishiAC::off(void) { // state = OFF; remote_state[5] &= ~kMitsubishiAcPower; } // Set the requested power state of the A/C. -void IRMitsubishiAC::setPower(bool state) { - if (state) - on(); +void IRMitsubishiAC::setPower(bool on) { + if (on) + this->on(); else - off(); + this->off(); } // Return the requested power state of the A/C. -bool IRMitsubishiAC::getPower() { +bool IRMitsubishiAC::getPower(void) { return ((remote_state[5] & kMitsubishiAcPower) != 0); } // Set the temp. in deg C -void IRMitsubishiAC::setTemp(uint8_t temp) { - temp = std::max((uint8_t)kMitsubishiAcMinTemp, temp); +void IRMitsubishiAC::setTemp(const uint8_t degrees) { + uint8_t temp = std::max((uint8_t)kMitsubishiAcMinTemp, degrees); temp = std::min((uint8_t)kMitsubishiAcMaxTemp, temp); remote_state[7] = temp - kMitsubishiAcMinTemp; } // Return the set temp. in deg C -uint8_t IRMitsubishiAC::getTemp() { +uint8_t IRMitsubishiAC::getTemp(void) { return (remote_state[7] + kMitsubishiAcMinTemp); } // Set the speed of the fan, 0-6. // 0 is auto, 1-5 is the speed, 6 is silent. -void IRMitsubishiAC::setFan(uint8_t fan) { +void IRMitsubishiAC::setFan(const uint8_t speed) { + uint8_t fan = speed; // Bounds check if (fan > kMitsubishiAcFanSilent) fan = kMitsubishiAcFanMax; // Set the fan to maximum if out of range. @@ -539,17 +512,17 @@ void IRMitsubishiAC::setFan(uint8_t fan) { } // Return the requested state of the unit's fan. -uint8_t IRMitsubishiAC::getFan() { +uint8_t IRMitsubishiAC::getFan(void) { uint8_t fan = remote_state[9] & 0b111; if (fan == kMitsubishiAcFanMax) return kMitsubishiAcFanSilent; return fan; } // Return the requested climate operation mode of the a/c unit. -uint8_t IRMitsubishiAC::getMode() { return (remote_state[6]); } +uint8_t IRMitsubishiAC::getMode(void) { return (remote_state[6]); } // Set the requested climate operation mode of the a/c unit. -void IRMitsubishiAC::setMode(uint8_t mode) { +void IRMitsubishiAC::setMode(const uint8_t mode) { // If we get an unexpected mode, default to AUTO. switch (mode) { case kMitsubishiAcAuto: @@ -565,48 +538,67 @@ void IRMitsubishiAC::setMode(uint8_t mode) { remote_state[8] = 0b00110000; break; default: - mode = kMitsubishiAcAuto; - remote_state[8] = 0b00110000; + this->setMode(kMitsubishiAcAuto); + return; } remote_state[6] = mode; } // Set the requested vane operation mode of the a/c unit. -void IRMitsubishiAC::setVane(uint8_t mode) { - mode = std::min(mode, (uint8_t)0b111); // bounds check - mode |= 0b1000; - mode <<= 3; +void IRMitsubishiAC::setVane(const uint8_t position) { + uint8_t pos = std::min(position, (uint8_t)0b111); // bounds check + pos |= 0b1000; + pos <<= 3; remote_state[9] &= 0b11000111; // Clear the previous setting. - remote_state[9] |= mode; + remote_state[9] |= pos; +} + +// Set the requested wide-vane operation mode of the a/c unit. +void IRMitsubishiAC::setWideVane(const uint8_t position) { + uint8_t pos = std::min(position, kMitsubishiAcWideVaneAuto); // bounds check + pos <<= 4; + remote_state[8] &= 0b00001111; // Clear the previous setting. + remote_state[8] |= pos; } // Return the requested vane operation mode of the a/c unit. -uint8_t IRMitsubishiAC::getVane() { +uint8_t IRMitsubishiAC::getVane(void) { return ((remote_state[9] & 0b00111000) >> 3); } +// Return the requested wide vane operation mode of the a/c unit. +uint8_t IRMitsubishiAC::getWideVane(void) { + return (remote_state[8] >> 4); +} + // Return the clock setting of the message. 1=1/6 hour. e.g. 4pm = 48 -uint8_t IRMitsubishiAC::getClock() { return remote_state[10]; } +uint8_t IRMitsubishiAC::getClock(void) { return remote_state[10]; } // Set the current time. 1 = 1/6 hour. e.g. 6am = 36. -void IRMitsubishiAC::setClock(uint8_t clock) { remote_state[10] = clock; } +void IRMitsubishiAC::setClock(const uint8_t clock) { + remote_state[10] = clock; +} // Return the desired start time. 1 = 1/6 hour. e.g. 1am = 6 -uint8_t IRMitsubishiAC::getStartClock() { return remote_state[12]; } +uint8_t IRMitsubishiAC::getStartClock(void) { return remote_state[12]; } -// Set the desired start tiem of the AC. 1 = 1/6 hour. e.g. 8pm = 120 -void IRMitsubishiAC::setStartClock(uint8_t clock) { remote_state[12] = clock; } +// Set the desired start time of the AC. 1 = 1/6 hour. e.g. 8pm = 120 +void IRMitsubishiAC::setStartClock(const uint8_t clock) { + remote_state[12] = clock; +} // Return the desired stop time of the AC. 1 = 1/6 hour. e.g 10pm = 132 -uint8_t IRMitsubishiAC::getStopClock() { return remote_state[11]; } +uint8_t IRMitsubishiAC::getStopClock(void) { return remote_state[11]; } // Set the desired stop time of the AC. 1 = 1/6 hour. e.g 10pm = 132 -void IRMitsubishiAC::setStopClock(uint8_t clock) { remote_state[11] = clock; } +void IRMitsubishiAC::setStopClock(const uint8_t clock) { + remote_state[11] = clock; +} // Return the timer setting. Possible values: kMitsubishiAcNoTimer, // kMitsubishiAcStartTimer, kMitsubishiAcStopTimer, // kMitsubishiAcStartStopTimer -uint8_t IRMitsubishiAC::getTimer() { return remote_state[13] & 0b111; } +uint8_t IRMitsubishiAC::getTimer(void) { return remote_state[13] & 0b111; } // Set the timer setting. Possible values: kMitsubishiAcNoTimer, // kMitsubishiAcStartTimer, kMitsubishiAcStopTimer, @@ -651,78 +643,131 @@ uint8_t IRMitsubishiAC::convertFan(const stdAc::fanspeed_t speed) { uint8_t IRMitsubishiAC::convertSwingV(const stdAc::swingv_t position) { switch (position) { case stdAc::swingv_t::kHighest: + return kMitsubishiAcVaneAutoMove - 6; case stdAc::swingv_t::kHigh: + return kMitsubishiAcVaneAutoMove - 5; case stdAc::swingv_t::kMiddle: + return kMitsubishiAcVaneAutoMove - 4; case stdAc::swingv_t::kLow: + return kMitsubishiAcVaneAutoMove - 3; case stdAc::swingv_t::kLowest: - return kMitsubishiAcVaneAutoMove; + return kMitsubishiAcVaneAutoMove - 2; + case stdAc::swingv_t::kAuto: + return kMitsubishiAcVaneAutoMove; default: - return kMitsubishiAcVaneAuto; + return kMitsubishiAcVaneAuto; } } -#ifdef ARDUINO -String IRMitsubishiAC::timeToString(uint64_t time) { - String result = ""; -#else -std::string IRMitsubishiAC::timeToString(uint64_t time) { - std::string result = ""; -#endif // ARDUINO - if (time / 6 < 10) result += '0'; - result += uint64ToString(time / 6); - result += ':'; - if (time * 10 % 60 < 10) result += '0'; - result += uint64ToString(time * 10 % 60); +// Convert a standard A/C wide wane swing into its native setting. +uint8_t IRMitsubishiAC::convertSwingH(const stdAc::swingh_t position) { + switch (position) { + case stdAc::swingh_t::kLeftMax: + return kMitsubishiAcWideVaneAuto - 7; + case stdAc::swingh_t::kLeft: + return kMitsubishiAcWideVaneAuto - 6; + case stdAc::swingh_t::kMiddle: + return kMitsubishiAcWideVaneAuto - 5; + case stdAc::swingh_t::kRight: + return kMitsubishiAcWideVaneAuto - 4; + case stdAc::swingh_t::kRightMax: + return kMitsubishiAcWideVaneAuto - 3; + case stdAc::swingh_t::kWide: + return kMitsubishiAcWideVaneAuto - 2; + case stdAc::swingh_t::kAuto: + return kMitsubishiAcWideVaneAuto; + default: + return kMitsubishiAcWideVaneAuto - 5; + } +} + +// Convert a native mode to it's common equivalent. +stdAc::opmode_t IRMitsubishiAC::toCommonMode(const uint8_t mode) { + switch (mode) { + case kMitsubishiAcCool: return stdAc::opmode_t::kCool; + case kMitsubishiAcHeat: return stdAc::opmode_t::kHeat; + case kMitsubishiAcDry: return stdAc::opmode_t::kDry; + default: return stdAc::opmode_t::kAuto; + } +} + +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRMitsubishiAC::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kMitsubishiAcFanRealMax: return stdAc::fanspeed_t::kMax; + case kMitsubishiAcFanRealMax - 1: return stdAc::fanspeed_t::kHigh; + case kMitsubishiAcFanRealMax - 2: return stdAc::fanspeed_t::kMedium; + case kMitsubishiAcFanRealMax - 3: return stdAc::fanspeed_t::kLow; + case kMitsubishiAcFanSilent: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +// Convert a native vertical swing to it's common equivalent. +stdAc::swingv_t IRMitsubishiAC::toCommonSwingV(const uint8_t pos) { + switch (pos) { + case 1: return stdAc::swingv_t::kHighest; + case 2: return stdAc::swingv_t::kHigh; + case 3: return stdAc::swingv_t::kMiddle; + case 4: return stdAc::swingv_t::kLow; + case 5: return stdAc::swingv_t::kLowest; + default: return stdAc::swingv_t::kAuto; + } +} + +// Convert a native horizontal swing to it's common equivalent. +stdAc::swingh_t IRMitsubishiAC::toCommonSwingH(const uint8_t pos) { + switch (pos) { + case 1: return stdAc::swingh_t::kLeftMax; + case 2: return stdAc::swingh_t::kLeft; + case 3: return stdAc::swingh_t::kMiddle; + case 4: return stdAc::swingh_t::kRight; + case 5: return stdAc::swingh_t::kRightMax; + case 6: return stdAc::swingh_t::kWide; + default: return stdAc::swingh_t::kAuto; + } +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRMitsubishiAC::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::MITSUBISHI_AC; + result.model = -1; // No models used. + result.power = this->getPower(); + result.mode = this->toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(); + result.fanspeed = this->toCommonFanSpeed(this->getFan()); + result.swingv = this->toCommonSwingV(this->getVane()); + result.swingh = this->toCommonSwingH(this->getWideVane()); + result.quiet = this->getFan() == kMitsubishiAcFanSilent; + // Not supported. + result.turbo = false; + result.clean = false; + result.econo = false; + result.filter = false; + result.light = false; + result.beep = false; + result.sleep = -1; + result.clock = -1; return result; } // Convert the internal state into a human readable string. -#ifdef ARDUINO -String IRMitsubishiAC::toString() { +String IRMitsubishiAC::toString(void) { String result = ""; -#else -std::string IRMitsubishiAC::toString() { - std::string result = ""; -#endif // ARDUINO - result += F("Power: "); - if (getPower()) - result += F("On"); - else - result += F("Off"); - switch (getMode()) { - case MITSUBISHI_AC_AUTO: - result += F(" (AUTO)"); - break; - case MITSUBISHI_AC_COOL: - result += F(" (COOL)"); - break; - case MITSUBISHI_AC_DRY: - result += F(" (DRY)"); - break; - case MITSUBISHI_AC_HEAT: - result += F(" (HEAT)"); - break; - default: - result += F(" (UNKNOWN)"); - } - result += F(", Temp: "); - result += uint64ToString(getTemp()); - result += F("C, FAN: "); - switch (getFan()) { - case MITSUBISHI_AC_FAN_AUTO: - result += F("AUTO"); - break; - case MITSUBISHI_AC_FAN_MAX: - result += F("MAX"); - break; - case MITSUBISHI_AC_FAN_SILENT: - result += F("SILENT"); - break; - default: - result += uint64ToString(getFan()); - } - result += F(", VANE: "); - switch (getVane()) { + result.reserve(110); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(getPower(), F("Power"), false); + result += addModeToString(getMode(), kMitsubishiAcAuto, kMitsubishiAcCool, + kMitsubishiAcHeat, kMitsubishiAcDry, + kMitsubishiAcAuto); + result += addTempToString(getTemp()); + result += addFanToString(getFan(), kMitsubishiAcFanRealMax, + kMitsubishiAcFanRealMax - 3, + kMitsubishiAcFanAuto, kMitsubishiAcFanQuiet, + kMitsubishiAcFanRealMax - 2); + result += F(", Vane: "); + switch (this->getVane()) { case MITSUBISHI_AC_VANE_AUTO: result += F("AUTO"); break; @@ -730,16 +775,21 @@ std::string IRMitsubishiAC::toString() { result += F("AUTO MOVE"); break; default: - result += uint64ToString(getVane()); + result += uint64ToString(this->getVane()); } - result += F(", Time: "); - result += timeToString(getClock()); - result += F(", On timer: "); - result += timeToString(getStartClock()); - result += F(", Off timer: "); - result += timeToString(getStopClock()); + result += F(", Wide Vane: "); + switch (this->getWideVane()) { + case kMitsubishiAcWideVaneAuto: + result += F("AUTO"); + break; + default: + result += uint64ToString(this->getWideVane()); + } + result += addLabeledString(minsToString(getClock() * 10), F("Time")); + result += addLabeledString(minsToString(getStartClock() * 10), F("On timer")); + result += addLabeledString(minsToString(getStopClock() * 10), F("Off timer")); result += F(", Timer: "); - switch (getTimer()) { + switch (this->getTimer()) { case kMitsubishiAcNoTimer: result += '-'; break; @@ -754,7 +804,7 @@ std::string IRMitsubishiAC::toString() { break; default: result += F("? ("); - result += getTimer(); + result += this->getTimer(); result += F(")\n"); } return result; diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Mitsubishi.h b/lib/IRremoteESP8266-2.6.5/src/ir_Mitsubishi.h similarity index 58% rename from lib/IRremoteESP8266-2.6.0/src/ir_Mitsubishi.h rename to lib/IRremoteESP8266-2.6.5/src/ir_Mitsubishi.h index c8dca5dbc..ac67082dc 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Mitsubishi.h +++ b/lib/IRremoteESP8266-2.6.5/src/ir_Mitsubishi.h @@ -1,5 +1,12 @@ // Copyright 2009 Ken Shirriff // Copyright 2017 David Conran + +// Mitsubishi + +// Supports: +// Brand: Mitsubishi, Model: TV +// Brand: Mitsubishi, Model: HC3000 Projector + #ifndef IR_MITSUBISHI_H_ #define IR_MITSUBISHI_H_ @@ -7,8 +14,6 @@ #include #ifndef UNIT_TEST #include -#else -#include #endif #include "IRremoteESP8266.h" #include "IRsend.h" @@ -16,12 +21,6 @@ #include "IRsend_test.h" #endif -// MMMMM IIIII TTTTT SSSS U U BBBB IIIII SSSS H H IIIII -// M M M I T S U U B B I S H H I -// M M M I T SSS U U BBBB I SSS HHHHH I -// M M I T S U U B B I S H H I -// M M IIIII T SSSS UUU BBBBB IIIII SSSS H H IIIII - // Mitsubishi (TV) decoding added from https://github.com/z3t0/Arduino-IRremote // Mitsubishi (TV) sending & Mitsubishi A/C support added by David Conran @@ -35,6 +34,7 @@ const uint8_t kMitsubishiAcFanAuto = 0; const uint8_t kMitsubishiAcFanMax = 5; const uint8_t kMitsubishiAcFanRealMax = 4; const uint8_t kMitsubishiAcFanSilent = 6; +const uint8_t kMitsubishiAcFanQuiet = kMitsubishiAcFanSilent; const uint8_t kMitsubishiAcMinTemp = 16; // 16C const uint8_t kMitsubishiAcMaxTemp = 31; // 31C const uint8_t kMitsubishiAcVaneAuto = 0; @@ -43,6 +43,7 @@ const uint8_t kMitsubishiAcNoTimer = 0; const uint8_t kMitsubishiAcStartTimer = 5; const uint8_t kMitsubishiAcStopTimer = 3; const uint8_t kMitsubishiAcStartStopTimer = 7; +const uint8_t kMitsubishiAcWideVaneAuto = 8; // Legacy defines (Deprecated) #define MITSUBISHI_AC_VANE_AUTO_MOVE kMitsubishiAcVaneAutoMove @@ -61,59 +62,60 @@ const uint8_t kMitsubishiAcStartStopTimer = 7; class IRMitsubishiAC { public: - explicit IRMitsubishiAC(uint16_t pin); + explicit IRMitsubishiAC(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); - static uint8_t calculateChecksum(uint8_t* data); + static uint8_t calculateChecksum(const uint8_t* data); - void stateReset(); + void stateReset(void); #if SEND_MITSUBISHI_AC void send(const uint16_t repeat = kMitsubishiACMinRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } #endif // SEND_MITSUBISHI_AC - void begin(); - void on(); - void off(); - void setPower(bool state); - bool getPower(); - void setTemp(uint8_t temp); - uint8_t getTemp(); - void setFan(uint8_t fan); - uint8_t getFan(); - void setMode(uint8_t mode); - uint8_t getMode(); - void setVane(uint8_t mode); - uint8_t getVane(); - uint8_t* getRaw(); - void setRaw(uint8_t* data); - uint8_t getClock(); - void setClock(uint8_t clock); - uint8_t getStartClock(); - void setStartClock(uint8_t clock); - uint8_t getStopClock(); - void setStopClock(uint8_t clock); - uint8_t getTimer(); - void setTimer(uint8_t timer); + void begin(void); + void on(void); + void off(void); + void setPower(const bool on); + bool getPower(void); + void setTemp(const uint8_t degrees); + uint8_t getTemp(void); + void setFan(const uint8_t speed); + uint8_t getFan(void); + void setMode(const uint8_t mode); + uint8_t getMode(void); + void setVane(const uint8_t position); + void setWideVane(const uint8_t position); + uint8_t getVane(void); + uint8_t getWideVane(void); + uint8_t* getRaw(void); + void setRaw(const uint8_t* data); + uint8_t getClock(void); + void setClock(const uint8_t clock); + uint8_t getStartClock(void); + void setStartClock(const uint8_t clock); + uint8_t getStopClock(void); + void setStopClock(const uint8_t clock); + uint8_t getTimer(void); + void setTimer(const uint8_t timer); uint8_t convertMode(const stdAc::opmode_t mode); uint8_t convertFan(const stdAc::fanspeed_t speed); uint8_t convertSwingV(const stdAc::swingv_t position); -#ifdef ARDUINO - String toString(); -#else - std::string toString(); -#endif + uint8_t convertSwingH(const stdAc::swingh_t position); + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + static stdAc::swingv_t toCommonSwingV(const uint8_t pos); + static stdAc::swingh_t toCommonSwingH(const uint8_t pos); + stdAc::state_t toCommon(void); + String toString(void); #ifndef UNIT_TEST private: IRsend _irsend; #else IRsendTest _irsend; -#endif -#ifdef ARDUINO - String timeToString(uint64_t time); -#else - std::string timeToString(uint64_t time); #endif uint8_t remote_state[kMitsubishiACStateLength]; - void checksum(); + void checksum(void); }; #endif // IR_MITSUBISHI_H_ diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_MitsubishiHeavy.cpp b/lib/IRremoteESP8266-2.6.5/src/ir_MitsubishiHeavy.cpp similarity index 73% rename from lib/IRremoteESP8266-2.6.0/src/ir_MitsubishiHeavy.cpp rename to lib/IRremoteESP8266-2.6.5/src/ir_MitsubishiHeavy.cpp index 9048124d4..6b4295991 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_MitsubishiHeavy.cpp +++ b/lib/IRremoteESP8266-2.6.5/src/ir_MitsubishiHeavy.cpp @@ -22,7 +22,7 @@ #endif // Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/660 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/660 // https://github.com/ToniA/Raw-IR-decoder-for-Arduino/blob/master/MitsubishiHeavy.cpp // https://github.com/ToniA/arduino-heatpumpir/blob/master/MitsubishiHeavyHeatpumpIR.cpp @@ -34,6 +34,12 @@ const uint16_t kMitsubishiHeavyOneSpace = 420; const uint16_t kMitsubishiHeavyZeroSpace = 1220; const uint32_t kMitsubishiHeavyGap = kDefaultMessageGap; // Just a guess. +using irutils::addBoolToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addTempToString; + #if SEND_MITSUBISHIHEAVY // Send a MitsubishiHeavy 88 bit A/C message. // @@ -73,10 +79,12 @@ void IRsend::sendMitsubishiHeavy152(const unsigned char data[], #endif // SEND_MITSUBISHIHEAVY // Class for decoding and constructing MitsubishiHeavy152 AC messages. -IRMitsubishiHeavy152Ac::IRMitsubishiHeavy152Ac( - const uint16_t pin) : _irsend(pin) { stateReset(); } +IRMitsubishiHeavy152Ac::IRMitsubishiHeavy152Ac(const uint16_t pin, + const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { stateReset(); } -void IRMitsubishiHeavy152Ac::begin() { _irsend.begin(); } +void IRMitsubishiHeavy152Ac::begin(void) { _irsend.begin(); } #if SEND_MITSUBISHIHEAVY void IRMitsubishiHeavy152Ac::send(const uint16_t repeat) { @@ -303,7 +311,6 @@ bool IRMitsubishiHeavy152Ac::validChecksum(const uint8_t *state, return true; } - // Convert a standard A/C mode into its native mode. uint8_t IRMitsubishiHeavy152Ac::convertMode(const stdAc::opmode_t mode) { switch (mode) { @@ -378,68 +385,118 @@ uint8_t IRMitsubishiHeavy152Ac::convertSwingH(const stdAc::swingh_t position) { } } +// Convert a native mode to it's common equivalent. +stdAc::opmode_t IRMitsubishiHeavy152Ac::toCommonMode(const uint8_t mode) { + switch (mode) { + case kMitsubishiHeavyCool: return stdAc::opmode_t::kCool; + case kMitsubishiHeavyHeat: return stdAc::opmode_t::kHeat; + case kMitsubishiHeavyDry: return stdAc::opmode_t::kDry; + case kMitsubishiHeavyFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRMitsubishiHeavy152Ac::toCommonFanSpeed(const uint8_t spd) { + switch (spd) { + case kMitsubishiHeavy152FanMax: return stdAc::fanspeed_t::kMax; + case kMitsubishiHeavy152FanHigh: return stdAc::fanspeed_t::kHigh; + case kMitsubishiHeavy152FanMed: return stdAc::fanspeed_t::kMedium; + case kMitsubishiHeavy152FanLow: return stdAc::fanspeed_t::kLow; + case kMitsubishiHeavy152FanEcono: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +// Convert a native vertical swing to it's common equivalent. +stdAc::swingh_t IRMitsubishiHeavy152Ac::toCommonSwingH(const uint8_t pos) { + switch (pos) { + case kMitsubishiHeavy152SwingHLeftMax: return stdAc::swingh_t::kLeftMax; + case kMitsubishiHeavy152SwingHLeft: return stdAc::swingh_t::kLeft; + case kMitsubishiHeavy152SwingHMiddle: return stdAc::swingh_t::kMiddle; + case kMitsubishiHeavy152SwingHRight: return stdAc::swingh_t::kRight; + case kMitsubishiHeavy152SwingHRightMax: return stdAc::swingh_t::kRightMax; + case kMitsubishiHeavy152SwingHOff: return stdAc::swingh_t::kOff; + default: return stdAc::swingh_t::kAuto; + } +} + +// Convert a native vertical swing to it's common equivalent. +stdAc::swingv_t IRMitsubishiHeavy152Ac::toCommonSwingV(const uint8_t pos) { + switch (pos) { + case kMitsubishiHeavy152SwingVHighest: return stdAc::swingv_t::kHighest; + case kMitsubishiHeavy152SwingVHigh: return stdAc::swingv_t::kHigh; + case kMitsubishiHeavy152SwingVMiddle: return stdAc::swingv_t::kMiddle; + case kMitsubishiHeavy152SwingVLow: return stdAc::swingv_t::kLow; + case kMitsubishiHeavy152SwingVLowest: return stdAc::swingv_t::kLowest; + case kMitsubishiHeavy152SwingVOff: return stdAc::swingv_t::kOff; + default: return stdAc::swingv_t::kAuto; + } +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRMitsubishiHeavy152Ac::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::MITSUBISHI_HEAVY_152; + result.model = -1; // No models used. + result.power = this->getPower(); + result.mode = this->toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(); + result.fanspeed = this->toCommonFanSpeed(this->getFan()); + result.swingv = this->toCommonSwingV(this->getSwingVertical()); + result.swingh = this->toCommonSwingH(this->getSwingHorizontal()); + result.turbo = this->getTurbo(); + result.econo = this->getEcono(); + result.clean = this->getClean(); + result.quiet = this->getSilent(); + result.filter = this->getFilter(); + result.sleep = this->getNight() ? 0 : -1; + // Not supported. + result.light = false; + result.beep = false; + result.clock = -1; + return result; +} + // Convert the internal state into a human readable string. -#ifdef ARDUINO String IRMitsubishiHeavy152Ac::toString(void) { String result = ""; -#else -std::string IRMitsubishiHeavy152Ac::toString(void) { - std::string result = ""; -#endif // ARDUINO - result += F("Power: "); - result += (this->getPower() ? F("On") : F("Off")); - result += F(", Mode: "); - result += uint64ToString(this->getMode()); - switch (this->getMode()) { - case kMitsubishiHeavyAuto: - result += F(" (Auto)"); - break; - case kMitsubishiHeavyCool: - result += F(" (Cool)"); - break; - case kMitsubishiHeavyHeat: - result += F(" (Heat)"); - break; - case kMitsubishiHeavyDry: - result += F(" (Dry)"); - break; - case kMitsubishiHeavyFan: - result += F(" (Fan)"); - break; - default: - result += F(" (UNKNOWN)"); - } - result += F(", Temp: "); - result += uint64ToString(this->getTemp()) + 'C'; - result += F(", Fan: "); - result += uint64ToString(this->getFan()); + result.reserve(180); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(getPower(), F("Power"), false); + result += addModeToString(getMode(), kMitsubishiHeavyAuto, + kMitsubishiHeavyCool, kMitsubishiHeavyHeat, + kMitsubishiHeavyDry, kMitsubishiHeavyFan); + result += addTempToString(getTemp()); + result += addIntToString(getFan(), F("Fan")); + result += F(" ("); switch (this->getFan()) { case kMitsubishiHeavy152FanAuto: - result += F(" (Auto)"); + result += F("Auto"); break; case kMitsubishiHeavy152FanHigh: - result += F(" (High)"); + result += F("High"); break; case kMitsubishiHeavy152FanLow: - result += F(" (Low)"); + result += F("Low"); break; case kMitsubishiHeavy152FanMed: - result += F(" (Med)"); + result += F("Medium"); break; case kMitsubishiHeavy152FanMax: - result += F(" (Max)"); + result += F("Max"); break; case kMitsubishiHeavy152FanEcono: - result += F(" (Econo)"); + result += F("Econo"); break; case kMitsubishiHeavy152FanTurbo: - result += F(" (Turbo)"); + result += F("Turbo"); break; default: - result += F(" (UNKNOWN)"); + result += F("UNKNOWN"); } - result += F(", Swing (V): "); - result += uint64ToString(this->getSwingVertical()); + result += ')'; + result += addIntToString(getSwingVertical(), F("Swing (V)")); switch (this->getSwingVertical()) { case kMitsubishiHeavy152SwingVAuto: result += F(" (Auto)"); @@ -465,8 +522,7 @@ std::string IRMitsubishiHeavy152Ac::toString(void) { default: result += F(" (UNKNOWN)"); } - result += F(", Swing (H): "); - result += uint64ToString(this->getSwingHorizontal()); + result += addIntToString(getSwingHorizontal(), F("Swing (H)")); switch (this->getSwingHorizontal()) { case kMitsubishiHeavy152SwingHAuto: result += F(" (Auto)"); @@ -498,29 +554,24 @@ std::string IRMitsubishiHeavy152Ac::toString(void) { default: result += F(" (UNKNOWN)"); } - result += F(", Silent: "); - result += (this->getSilent() ? F("On") : F("Off")); - result += F(", Turbo: "); - result += (this->getTurbo() ? F("On") : F("Off")); - result += F(", Econo: "); - result += (this->getEcono() ? F("On") : F("Off")); - result += F(", Night: "); - result += (this->getNight() ? F("On") : F("Off")); - result += F(", Filter: "); - result += (this->getFilter() ? F("On") : F("Off")); - result += F(", 3D: "); - result += (this->get3D() ? F("On") : F("Off")); - result += F(", Clean: "); - result += (this->getClean() ? F("On") : F("Off")); + result += addBoolToString(getSilent(), F("Silent")); + result += addBoolToString(getTurbo(), F("Turbo")); + result += addBoolToString(getEcono(), F("Econo")); + result += addBoolToString(getNight(), F("Night")); + result += addBoolToString(getFilter(), F("Filter")); + result += addBoolToString(get3D(), F("3D")); + result += addBoolToString(getClean(), F("Clean")); return result; } // Class for decoding and constructing MitsubishiHeavy88 AC messages. -IRMitsubishiHeavy88Ac::IRMitsubishiHeavy88Ac( - const uint16_t pin) : _irsend(pin) { stateReset(); } +IRMitsubishiHeavy88Ac::IRMitsubishiHeavy88Ac(const uint16_t pin, + const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { stateReset(); } -void IRMitsubishiHeavy88Ac::begin() { _irsend.begin(); } +void IRMitsubishiHeavy88Ac::begin(void) { _irsend.begin(); } #if SEND_MITSUBISHIHEAVY void IRMitsubishiHeavy88Ac::send(const uint16_t repeat) { @@ -737,7 +788,6 @@ uint8_t IRMitsubishiHeavy88Ac::convertMode(const stdAc::opmode_t mode) { return IRMitsubishiHeavy152Ac::convertMode(mode); } - // Convert a standard A/C Fan speed into its native fan speed. uint8_t IRMitsubishiHeavy88Ac::convertFan(const stdAc::fanspeed_t speed) { switch (speed) { @@ -796,65 +846,104 @@ uint8_t IRMitsubishiHeavy88Ac::convertSwingH(const stdAc::swingh_t position) { } } +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRMitsubishiHeavy88Ac::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kMitsubishiHeavy88FanTurbo: return stdAc::fanspeed_t::kMax; + case kMitsubishiHeavy88FanHigh: return stdAc::fanspeed_t::kHigh; + case kMitsubishiHeavy88FanMed: return stdAc::fanspeed_t::kMedium; + case kMitsubishiHeavy88FanLow: return stdAc::fanspeed_t::kLow; + case kMitsubishiHeavy88FanEcono: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +// Convert a native vertical swing to it's common equivalent. +stdAc::swingh_t IRMitsubishiHeavy88Ac::toCommonSwingH(const uint8_t pos) { + switch (pos) { + case kMitsubishiHeavy88SwingHLeftMax: return stdAc::swingh_t::kLeftMax; + case kMitsubishiHeavy88SwingHLeft: return stdAc::swingh_t::kLeft; + case kMitsubishiHeavy88SwingHMiddle: return stdAc::swingh_t::kMiddle; + case kMitsubishiHeavy88SwingHRight: return stdAc::swingh_t::kRight; + case kMitsubishiHeavy88SwingHRightMax: return stdAc::swingh_t::kRightMax; + case kMitsubishiHeavy88SwingHOff: return stdAc::swingh_t::kOff; + default: return stdAc::swingh_t::kAuto; + } +} + +// Convert a native vertical swing to it's common equivalent. +stdAc::swingv_t IRMitsubishiHeavy88Ac::toCommonSwingV(const uint8_t pos) { + switch (pos) { + case kMitsubishiHeavy88SwingVHighest: return stdAc::swingv_t::kHighest; + case kMitsubishiHeavy88SwingVHigh: return stdAc::swingv_t::kHigh; + case kMitsubishiHeavy88SwingVMiddle: return stdAc::swingv_t::kMiddle; + case kMitsubishiHeavy88SwingVLow: return stdAc::swingv_t::kLow; + case kMitsubishiHeavy88SwingVLowest: return stdAc::swingv_t::kLowest; + case kMitsubishiHeavy88SwingVOff: return stdAc::swingv_t::kOff; + default: return stdAc::swingv_t::kAuto; + } +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRMitsubishiHeavy88Ac::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::MITSUBISHI_HEAVY_88; + result.model = -1; // No models used. + result.power = this->getPower(); + result.mode = IRMitsubishiHeavy152Ac::toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(); + result.fanspeed = this->toCommonFanSpeed(this->getFan()); + result.swingv = this->toCommonSwingV(this->getSwingVertical()); + result.swingh = this->toCommonSwingH(this->getSwingHorizontal()); + result.turbo = this->getTurbo(); + result.econo = this->getEcono(); + result.clean = this->getClean(); + // Not supported. + result.quiet = false; + result.filter = false; + result.light = false; + result.beep = false; + result.sleep = -1; + result.clock = -1; + return result; +} + // Convert the internal state into a human readable string. -#ifdef ARDUINO String IRMitsubishiHeavy88Ac::toString(void) { String result = ""; -#else -std::string IRMitsubishiHeavy88Ac::toString(void) { - std::string result = ""; -#endif // ARDUINO - result += F("Power: "); - result += (this->getPower() ? F("On") : F("Off")); - result += F(", Mode: "); - result += uint64ToString(this->getMode()); - switch (this->getMode()) { - case kMitsubishiHeavyAuto: - result += F(" (Auto)"); - break; - case kMitsubishiHeavyCool: - result += F(" (Cool)"); - break; - case kMitsubishiHeavyHeat: - result += F(" (Heat)"); - break; - case kMitsubishiHeavyDry: - result += F(" (Dry)"); - break; - case kMitsubishiHeavyFan: - result += F(" (Fan)"); - break; - default: - result += F(" (UNKNOWN)"); - } - result += F(", Temp: "); - result += uint64ToString(this->getTemp()) + 'C'; - result += F(", Fan: "); - result += uint64ToString(this->getFan()); + result.reserve(140); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(getPower(), F("Power"), false); + result += addModeToString(getMode(), kMitsubishiHeavyAuto, + kMitsubishiHeavyCool, kMitsubishiHeavyHeat, + kMitsubishiHeavyDry, kMitsubishiHeavyFan); + result += addTempToString(getTemp()); + result += addIntToString(getFan(), F("Fan")); + result += F(" ("); switch (this->getFan()) { case kMitsubishiHeavy88FanAuto: - result += F(" (Auto)"); + result += F("Auto"); break; case kMitsubishiHeavy88FanHigh: - result += F(" (High)"); + result += F("High"); break; case kMitsubishiHeavy88FanLow: - result += F(" (Low)"); + result += F("Low"); break; case kMitsubishiHeavy88FanMed: - result += F(" (Med)"); + result += F("Medium"); break; case kMitsubishiHeavy88FanEcono: - result += F(" (Econo)"); + result += F("Econo"); break; case kMitsubishiHeavy88FanTurbo: - result += F(" (Turbo)"); + result += F("Turbo"); break; default: - result += F(" (UNKNOWN)"); + result += F("UNKNOWN"); } - result += F(", Swing (V): "); - result += uint64ToString(this->getSwingVertical()); + result += ')'; + result += addIntToString(getSwingVertical(), F("Swing (V)")); switch (this->getSwingVertical()) { case kMitsubishiHeavy88SwingVAuto: result += F(" (Auto)"); @@ -880,8 +969,7 @@ std::string IRMitsubishiHeavy88Ac::toString(void) { default: result += F(" (UNKNOWN)"); } - result += F(", Swing (H): "); - result += uint64ToString(this->getSwingHorizontal()); + result += addIntToString(getSwingHorizontal(), F("Swing (H)")); switch (this->getSwingHorizontal()) { case kMitsubishiHeavy88SwingHAuto: result += F(" (Auto)"); @@ -916,14 +1004,10 @@ std::string IRMitsubishiHeavy88Ac::toString(void) { default: result += F(" (UNKNOWN)"); } - result += F(", Turbo: "); - result += (this->getTurbo() ? F("On") : F("Off")); - result += F(", Econo: "); - result += (this->getEcono() ? F("On") : F("Off")); - result += F(", 3D: "); - result += (this->get3D() ? F("On") : F("Off")); - result += F(", Clean: "); - result += (this->getClean() ? F("On") : F("Off")); + result += addBoolToString(getTurbo(), F("Turbo")); + result += addBoolToString(getEcono(), F("Econo")); + result += addBoolToString(get3D(), F("3D")); + result += addBoolToString(getClean(), F("Clean")); return result; } @@ -953,41 +1037,19 @@ bool IRrecv::decodeMitsubishiHeavy(decode_results* results, } } - uint16_t actualBits = 0; uint16_t offset = kStartOffset; - match_result_t data_result; - - // Header - if (!matchMark(results->rawbuf[offset++], kMitsubishiHeavyHdrMark)) - return false; - if (!matchSpace(results->rawbuf[offset++], kMitsubishiHeavyHdrSpace)) - return false; - // Data - // Keep reading bytes until we either run out of section or state to fill. - for (uint16_t i = 0; - offset <= results->rawlen - 16 && actualBits < nbits; - i++, actualBits += 8, offset += data_result.used) { - data_result = matchData(&(results->rawbuf[offset]), 8, - kMitsubishiHeavyBitMark, kMitsubishiHeavyOneSpace, - kMitsubishiHeavyBitMark, kMitsubishiHeavyZeroSpace, - kTolerance, 0, false); - if (data_result.success == false) { - DPRINT("DEBUG: offset = "); - DPRINTLN(offset + data_result.used); - return false; // Fail - } - results->state[i] = data_result.data; - } - // Footer. - if (!matchMark(results->rawbuf[offset++], kMitsubishiHeavyBitMark)) - return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset], kMitsubishiHeavyGap)) return false; - + uint16_t used; + used = matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, nbits, + kMitsubishiHeavyHdrMark, kMitsubishiHeavyHdrSpace, + kMitsubishiHeavyBitMark, kMitsubishiHeavyOneSpace, + kMitsubishiHeavyBitMark, kMitsubishiHeavyZeroSpace, + kMitsubishiHeavyBitMark, kMitsubishiHeavyGap, true, + _tolerance, 0, false); + if (used == 0) return false; + offset += used; // Compliance - if (actualBits < nbits) return false; - if (strict && actualBits != nbits) return false; // Not as we expected. - switch (actualBits) { + switch (nbits) { case kMitsubishiHeavy88Bits: if (strict && !(IRMitsubishiHeavy88Ac::checkZjsSig(results->state) && IRMitsubishiHeavy88Ac::validChecksum(results->state))) @@ -1005,7 +1067,7 @@ bool IRrecv::decodeMitsubishiHeavy(decode_results* results, } // Success - results->bits = actualBits; + results->bits = nbits; // No need to record the state as we stored it as we decoded it. // As we use result->state, we don't record value, address, or command as it // is a union data type. diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_MitsubishiHeavy.h b/lib/IRremoteESP8266-2.6.5/src/ir_MitsubishiHeavy.h similarity index 86% rename from lib/IRremoteESP8266-2.6.0/src/ir_MitsubishiHeavy.h rename to lib/IRremoteESP8266-2.6.5/src/ir_MitsubishiHeavy.h index bcd85c6e0..c52eeb951 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_MitsubishiHeavy.h +++ b/lib/IRremoteESP8266-2.6.5/src/ir_MitsubishiHeavy.h @@ -1,12 +1,17 @@ // Copyright 2019 David Conran +// Supports: +// Brand: Mitsubishi Heavy Industries, Model: RLA502A700B remote +// Brand: Mitsubishi Heavy Industries, Model: SRKxxZM-S A/C +// Brand: Mitsubishi Heavy Industries, Model: SRKxxZMXA-S A/C +// Brand: Mitsubishi Heavy Industries, Model: RKX502A001C remote +// Brand: Mitsubishi Heavy Industries, Model: SRKxxZJ-S A/C + #ifndef IR_MITSUBISHIHEAVY_H_ #define IR_MITSUBISHIHEAVY_H_ #ifndef UNIT_TEST #include -#else -#include #endif #include "IRremoteESP8266.h" #include "IRsend.h" @@ -15,7 +20,7 @@ #endif // Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/660 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/660 // https://github.com/ToniA/Raw-IR-decoder-for-Arduino/blob/master/MitsubishiHeavy.cpp // https://github.com/ToniA/arduino-heatpumpir/blob/master/MitsubishiHeavyHeatpumpIR.cpp @@ -117,11 +122,14 @@ const uint8_t kMitsubishiHeavy88SwingVOff = 0b00000000; // 0x00 // Classes class IRMitsubishiHeavy152Ac { public: - explicit IRMitsubishiHeavy152Ac(const uint16_t pin); + explicit IRMitsubishiHeavy152Ac(const uint16_t pin, + const bool inverted = false, + const bool use_modulation = true); void stateReset(void); #if SEND_MITSUBISHIHEAVY void send(const uint16_t repeat = kMitsubishiHeavy152MinRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } #endif // SEND_MITSUBISHIHEAVY void begin(void); void on(void); @@ -176,11 +184,12 @@ class IRMitsubishiHeavy152Ac { static uint8_t convertFan(const stdAc::fanspeed_t speed); static uint8_t convertSwingV(const stdAc::swingv_t position); static uint8_t convertSwingH(const stdAc::swingh_t position); -#ifdef ARDUINO + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + static stdAc::swingv_t toCommonSwingV(const uint8_t pos); + static stdAc::swingh_t toCommonSwingH(const uint8_t pos); + stdAc::state_t toCommon(void); String toString(void); -#else // ARDUINO - std::string toString(void); -#endif // ARDUINO #ifndef UNIT_TEST private: @@ -190,12 +199,14 @@ class IRMitsubishiHeavy152Ac { #endif // UNIT_TEST // The state of the IR remote in IR code form. uint8_t remote_state[kMitsubishiHeavy152StateLength]; - void checksum(); + void checksum(void); }; class IRMitsubishiHeavy88Ac { public: - explicit IRMitsubishiHeavy88Ac(const uint16_t pin); + explicit IRMitsubishiHeavy88Ac(const uint16_t pin, + const bool inverted = false, + const bool use_modulation = true); void stateReset(void); #if SEND_MITSUBISHIHEAVY @@ -245,11 +256,11 @@ class IRMitsubishiHeavy88Ac { static uint8_t convertFan(const stdAc::fanspeed_t speed); static uint8_t convertSwingV(const stdAc::swingv_t position); static uint8_t convertSwingH(const stdAc::swingh_t position); -#ifdef ARDUINO + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + static stdAc::swingv_t toCommonSwingV(const uint8_t pos); + static stdAc::swingh_t toCommonSwingH(const uint8_t pos); + stdAc::state_t toCommon(void); String toString(void); -#else // ARDUINO - std::string toString(void); -#endif // ARDUINO #ifndef UNIT_TEST private: @@ -259,6 +270,6 @@ class IRMitsubishiHeavy88Ac { #endif // UNIT_TEST // The state of the IR remote in IR code form. uint8_t remote_state[kMitsubishiHeavy152StateLength]; - void checksum(); + void checksum(void); }; #endif // IR_MITSUBISHIHEAVY_H_ diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_NEC.cpp b/lib/IRremoteESP8266-2.6.5/src/ir_NEC.cpp similarity index 78% rename from lib/IRremoteESP8266-2.6.0/src/ir_NEC.cpp rename to lib/IRremoteESP8266-2.6.5/src/ir_NEC.cpp index 660b51109..03f68c1d9 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_NEC.cpp +++ b/lib/IRremoteESP8266-2.6.5/src/ir_NEC.cpp @@ -1,6 +1,8 @@ // Copyright 2009 Ken Shirriff // Copyright 2017 David Conran +// NEC originally added from https://github.com/shirriff/Arduino-IRremote/ + #define __STDC_LIMIT_MACROS #include "ir_NEC.h" #include @@ -9,16 +11,7 @@ #include "IRsend.h" #include "IRutils.h" -// N N EEEEE CCCC -// NN N E C -// N N N EEE C -// N NN E C -// N N EEEEE CCCC - -// NEC originally added from https://github.com/shirriff/Arduino-IRremote/ - -#if (SEND_NEC || SEND_SHERWOOD || SEND_AIWA_RC_T501 || SEND_SANYO || \ - SEND_PIONEER) +#if (SEND_NEC || SEND_SHERWOOD || SEND_AIWA_RC_T501 || SEND_SANYO) // Send a raw NEC(Renesas) formatted message. // // Args: @@ -68,7 +61,7 @@ uint32_t IRsend::encodeNEC(uint16_t address, uint16_t command) { return (address << 24) + ((address ^ 0xFF) << 16) + command; // Normal. } } -#endif +#endif // (SEND_NEC || SEND_SHERWOOD || SEND_AIWA_RC_T501 || SEND_SANYO ) #if (DECODE_NEC || DECODE_SHERWOOD || DECODE_AIWA_RC_T501 || DECODE_SANYO) // Decode the supplied NEC message. @@ -103,13 +96,11 @@ bool IRrecv::decodeNEC(decode_results *results, uint16_t nbits, bool strict) { uint16_t offset = kStartOffset; // Header - if (!matchMark(results->rawbuf[offset], kNecHdrMark)) return false; - // Calculate how long the lowest tick time is based on the header mark. - uint32_t mark_tick = results->rawbuf[offset++] * kRawTick / kNecHdrMarkTicks; + if (!matchMark(results->rawbuf[offset++], kNecHdrMark)) return false; // Check if it is a repeat code. if (results->rawlen == kNecRptLength && matchSpace(results->rawbuf[offset], kNecRptSpace) && - matchMark(results->rawbuf[offset + 1], kNecBitMarkTicks * mark_tick)) { + matchMark(results->rawbuf[offset + 1], kNecBitMark)) { results->value = kRepeat; results->decode_type = NEC; results->bits = 0; @@ -119,27 +110,13 @@ bool IRrecv::decodeNEC(decode_results *results, uint16_t nbits, bool strict) { return true; } - // Header (cont.) - if (!matchSpace(results->rawbuf[offset], kNecHdrSpace)) return false; - // Calculate how long the common tick time is based on the header space. - uint32_t space_tick = - results->rawbuf[offset++] * kRawTick / kNecHdrSpaceTicks; - // Data - match_result_t data_result = - matchData(&(results->rawbuf[offset]), nbits, kNecBitMarkTicks * mark_tick, - kNecOneSpaceTicks * space_tick, kNecBitMarkTicks * mark_tick, - kNecZeroSpaceTicks * space_tick); - if (data_result.success == false) return false; - data = data_result.data; - offset += data_result.used; - - // Footer - if (!matchMark(results->rawbuf[offset++], kNecBitMarkTicks * mark_tick)) - return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset], kNecMinGapTicks * space_tick)) - return false; - + // Match Header (cont.) + Data + Footer + if (!matchGeneric(results->rawbuf + offset, &data, + results->rawlen - offset, nbits, + 0, kNecHdrSpace, + kNecBitMark, kNecOneSpace, + kNecBitMark, kNecZeroSpace, + kNecBitMark, kNecMinGap, true)) return false; // Compliance // Calculate command and optionally enforce integrity checking. uint8_t command = (data & 0xFF00) >> 8; @@ -166,4 +143,4 @@ bool IRrecv::decodeNEC(decode_results *results, uint16_t nbits, bool strict) { results->address = reverseBits((data >> 16) & UINT16_MAX, 16); return true; } -#endif +#endif // DECODE_NEC || DECODE_SHERWOOD || DECODE_AIWA_RC_T501 || DECODE_SANYO diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_NEC.h b/lib/IRremoteESP8266-2.6.5/src/ir_NEC.h similarity index 86% rename from lib/IRremoteESP8266-2.6.0/src/ir_NEC.h rename to lib/IRremoteESP8266-2.6.5/src/ir_NEC.h index c274c104e..e45ff702c 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_NEC.h +++ b/lib/IRremoteESP8266-2.6.5/src/ir_NEC.h @@ -1,19 +1,17 @@ // Copyright 2009 Ken Shirriff // Copyright 2017, 2018 David Conran +// NEC originally added from https://github.com/shirriff/Arduino-IRremote/ + #ifndef IR_NEC_H_ #define IR_NEC_H_ #include #include "IRremoteESP8266.h" -// N N EEEEE CCCC -// NN N E C -// N N N EEE C -// N NN E C -// N N EEEEE CCCC - -// NEC originally added from https://github.com/shirriff/Arduino-IRremote/ +// Supports: +// Brand: Yamaha, Model: RAV561 remote +// Brand: Yamaha, Model: RXV585B A/V Receiver // Constants // Ref: diff --git a/lib/IRremoteESP8266-2.6.5/src/ir_Neoclima.cpp b/lib/IRremoteESP8266-2.6.5/src/ir_Neoclima.cpp new file mode 100644 index 000000000..353d43b14 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.5/src/ir_Neoclima.cpp @@ -0,0 +1,554 @@ +// Copyright 2019 David Conran + +// Neoclima A/C support + +// Analysis by crankyoldgit & AndreyShpilevoy +// Code by crankyoldgit +// Ref: +// https://github.com/crankyoldgit/IRremoteESP8266/issues/764 +// https://drive.google.com/file/d/1kjYk4zS9NQcMQhFkak-L4mp4UuaAIesW/view + + +// Supports: +// Brand: Neoclima, Model: NS-09AHTI A/C +// Brand: Neoclima, Model: ZH/TY-01 remote + +#include "ir_Neoclima.h" +#include +#include "IRrecv.h" +#include "IRsend.h" +#include "IRutils.h" + +// Constants + +const uint16_t kNeoclimaHdrMark = 6112; +const uint16_t kNeoclimaHdrSpace = 7391; +const uint16_t kNeoclimaBitMark = 537; +const uint16_t kNeoclimaOneSpace = 1651; +const uint16_t kNeoclimaZeroSpace = 571; +const uint32_t kNeoclimaMinGap = kDefaultMessageGap; + +using irutils::addBoolToString; +using irutils::addFanToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addTempToString; + +#if SEND_NEOCLIMA +// Send a Neoclima message. +// +// Args: +// data: message to be sent. +// nbytes: Nr. of bytes of the message to be sent. +// repeat: Nr. of additional times the message is to be sent. +// +// Status: Beta / Known to be working. +// +// Ref: +// https://github.com/crankyoldgit/IRremoteESP8266/issues/764 +void IRsend::sendNeoclima(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + // Set IR carrier frequency + enableIROut(38); + + for (uint16_t i = 0; i <= repeat; i++) { + sendGeneric(kNeoclimaHdrMark, kNeoclimaHdrSpace, + kNeoclimaBitMark, kNeoclimaOneSpace, + kNeoclimaBitMark, kNeoclimaZeroSpace, + kNeoclimaBitMark, kNeoclimaHdrSpace, + data, nbytes, 38000, false, 0, // Repeats are already handled. + 50); + // Extra footer. + mark(kNeoclimaBitMark); + space(kNeoclimaMinGap); + } +} +#endif // SEND_NEOCLIMA + +IRNeoclimaAc::IRNeoclimaAc(const uint16_t pin, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { + this->stateReset(); +} + +void IRNeoclimaAc::stateReset(void) { + for (uint8_t i = 0; i < kNeoclimaStateLength; i++) + remote_state[i] = 0x0; + remote_state[7] = 0x6A; + remote_state[8] = 0x00; + remote_state[9] = 0x2A; + remote_state[10] = 0xA5; + // [11] is the checksum. +} + +void IRNeoclimaAc::begin(void) { _irsend.begin(); } + +uint8_t IRNeoclimaAc::calcChecksum(const uint8_t state[], + const uint16_t length) { + if (length == 0) return state[0]; + return sumBytes(state, length - 1); +} + +bool IRNeoclimaAc::validChecksum(const uint8_t state[], const uint16_t length) { + if (length < 2) + return true; // No checksum to compare with. Assume okay. + return (state[length - 1] == calcChecksum(state, length)); +} + +// Update the checksum for the internal state. +void IRNeoclimaAc::checksum(uint16_t length) { + if (length < 2) return; + remote_state[length - 1] = calcChecksum(remote_state, length); +} + +#if SEND_NEOCLIMA +void IRNeoclimaAc::send(const uint16_t repeat) { + this->checksum(); + _irsend.sendNeoclima(remote_state, kNeoclimaStateLength, repeat); +} +#endif // SEND_NEOCLIMA + +uint8_t *IRNeoclimaAc::getRaw(void) { + this->checksum(); + return remote_state; +} + +void IRNeoclimaAc::setRaw(const uint8_t new_code[], const uint16_t length) { + for (uint8_t i = 0; i < length && i < kNeoclimaStateLength; i++) + remote_state[i] = new_code[i]; +} + + +void IRNeoclimaAc::setButton(const uint8_t button) { + switch (button) { + case kNeoclimaButtonPower: + case kNeoclimaButtonMode: + case kNeoclimaButtonTempUp: + case kNeoclimaButtonTempDown: + case kNeoclimaButtonSwing: + case kNeoclimaButtonFanSpeed: + case kNeoclimaButtonAirFlow: + case kNeoclimaButtonHold: + case kNeoclimaButtonSleep: + case kNeoclimaButtonLight: + case kNeoclimaButtonEye: + case kNeoclimaButtonFollow: + case kNeoclimaButtonIon: + case kNeoclimaButtonFresh: + case kNeoclimaButton8CHeat: + case kNeoclimaButtonTurbo: + remote_state[5] &= ~kNeoclimaButtonMask; + remote_state[5] |= button; + break; + default: + this->setButton(kNeoclimaButtonPower); + } +} + +uint8_t IRNeoclimaAc::getButton(void) { + return remote_state[5] & kNeoclimaButtonMask; +} + +void IRNeoclimaAc::on(void) { this->setPower(true); } + +void IRNeoclimaAc::off(void) { this->setPower(false); } + +void IRNeoclimaAc::setPower(const bool on) { + this->setButton(kNeoclimaButtonPower); + if (on) + remote_state[7] |= kNeoclimaPowerMask; + else + remote_state[7] &= ~kNeoclimaPowerMask; +} + +bool IRNeoclimaAc::getPower(void) { + return remote_state[7] & kNeoclimaPowerMask; +} + +void IRNeoclimaAc::setMode(const uint8_t mode) { + switch (mode) { + case kNeoclimaDry: + // In this mode fan speed always LOW + this->setFan(kNeoclimaFanLow); + // FALL THRU + case kNeoclimaAuto: + case kNeoclimaCool: + case kNeoclimaFan: + case kNeoclimaHeat: + remote_state[9] &= ~kNeoclimaModeMask; + remote_state[9] |= (mode << 5); + this->setButton(kNeoclimaButtonMode); + break; + default: + // If we get an unexpected mode, default to AUTO. + this->setMode(kNeoclimaAuto); + } +} + +uint8_t IRNeoclimaAc::getMode(void) { + return (remote_state[9] & kNeoclimaModeMask) >> 5; +} + +// Convert a standard A/C mode into its native mode. +uint8_t IRNeoclimaAc::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kNeoclimaCool; + case stdAc::opmode_t::kHeat: + return kNeoclimaHeat; + case stdAc::opmode_t::kDry: + return kNeoclimaDry; + case stdAc::opmode_t::kFan: + return kNeoclimaFan; + default: + return kNeoclimaAuto; + } +} + +// Convert a native mode to it's common equivalent. +stdAc::opmode_t IRNeoclimaAc::toCommonMode(const uint8_t mode) { + switch (mode) { + case kNeoclimaCool: return stdAc::opmode_t::kCool; + case kNeoclimaHeat: return stdAc::opmode_t::kHeat; + case kNeoclimaDry: return stdAc::opmode_t::kDry; + case kNeoclimaFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +// Set the temp. in deg C +void IRNeoclimaAc::setTemp(const uint8_t temp) { + uint8_t oldtemp = this->getTemp(); + uint8_t newtemp = std::max(kNeoclimaMinTemp, temp); + newtemp = std::min(kNeoclimaMaxTemp, newtemp); + if (oldtemp > newtemp) + this->setButton(kNeoclimaButtonTempDown); + else if (newtemp > oldtemp) + this->setButton(kNeoclimaButtonTempUp); + remote_state[9] = (remote_state[9] & ~kNeoclimaTempMask) | + (newtemp - kNeoclimaMinTemp); +} + +// Return the set temp. in deg C +uint8_t IRNeoclimaAc::getTemp(void) { + return (remote_state[9] & kNeoclimaTempMask) + kNeoclimaMinTemp; +} + +// Set the speed of the fan, 0-3, 0 is auto, 1-3 is the speed +void IRNeoclimaAc::setFan(const uint8_t speed) { + switch (speed) { + case kNeoclimaFanAuto: + case kNeoclimaFanHigh: + case kNeoclimaFanMed: + if (this->getMode() == kNeoclimaDry) { // Dry mode only allows low speed. + this->setFan(kNeoclimaFanLow); + return; + } + // FALL-THRU + case kNeoclimaFanLow: + remote_state[7] &= ~kNeoclimaFanMask; + remote_state[7] |= (speed << 5); + this->setButton(kNeoclimaButtonFanSpeed); + break; + default: + // If we get an unexpected speed, default to Auto. + this->setFan(kNeoclimaFanAuto); + } +} + +uint8_t IRNeoclimaAc::getFan(void) { + return (remote_state[7] & kNeoclimaFanMask) >> 5; +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRNeoclimaAc::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: + return kNeoclimaFanLow; + case stdAc::fanspeed_t::kMedium: + return kNeoclimaFanMed; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: + return kNeoclimaFanHigh; + default: + return kNeoclimaFanAuto; + } +} + +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRNeoclimaAc::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kNeoclimaFanHigh: return stdAc::fanspeed_t::kMax; + case kNeoclimaFanMed: return stdAc::fanspeed_t::kMedium; + case kNeoclimaFanLow: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +void IRNeoclimaAc::setSleep(const bool on) { + this->setButton(kNeoclimaButtonSleep); + if (on) + remote_state[7] |= kNeoclimaSleepMask; + else + remote_state[7] &= ~kNeoclimaSleepMask; +} + +bool IRNeoclimaAc::getSleep(void) { + return remote_state[7] & kNeoclimaSleepMask; +} + +// A.k.a. Swing +void IRNeoclimaAc::setSwingV(const bool on) { + this->setButton(kNeoclimaButtonSwing); + remote_state[7] &= ~kNeoclimaSwingVMask; + remote_state[7] |= on ? kNeoclimaSwingVOn : kNeoclimaSwingVOff; +} + +bool IRNeoclimaAc::getSwingV(void) { + return (remote_state[7] & kNeoclimaSwingVMask) == kNeoclimaSwingVOn; +} + +// A.k.a. Air Flow +void IRNeoclimaAc::setSwingH(const bool on) { + this->setButton(kNeoclimaButtonAirFlow); + if (on) + remote_state[7] &= ~kNeoclimaSwingHMask; + else + remote_state[7] |= kNeoclimaSwingHMask; +} + +bool IRNeoclimaAc::getSwingH(void) { + return !(remote_state[7] & kNeoclimaSwingHMask); +} + +void IRNeoclimaAc::setTurbo(const bool on) { + this->setButton(kNeoclimaButtonTurbo); + if (on) + remote_state[3] |= kNeoclimaTurboMask; + else + remote_state[3] &= ~kNeoclimaTurboMask; +} + +bool IRNeoclimaAc::getTurbo(void) { + return remote_state[3] & kNeoclimaTurboMask; +} + +void IRNeoclimaAc::setFresh(const bool on) { + this->setButton(kNeoclimaButtonFresh); + if (on) + remote_state[5] |= kNeoclimaFreshMask; + else + remote_state[5] &= ~kNeoclimaFreshMask; +} + +bool IRNeoclimaAc::getFresh(void) { + return remote_state[5] & kNeoclimaFreshMask; +} + +void IRNeoclimaAc::setHold(const bool on) { + this->setButton(kNeoclimaButtonHold); + if (on) + remote_state[3] |= kNeoclimaHoldMask; + else + remote_state[3] &= ~kNeoclimaHoldMask; +} + +bool IRNeoclimaAc::getHold(void) { + return remote_state[3] & kNeoclimaHoldMask; +} + +void IRNeoclimaAc::setIon(const bool on) { + this->setButton(kNeoclimaButtonIon); + if (on) + remote_state[1] |= kNeoclimaIonMask; + else + remote_state[1] &= ~kNeoclimaIonMask; +} + +bool IRNeoclimaAc::getIon(void) { + return remote_state[1] & kNeoclimaIonMask; +} + +void IRNeoclimaAc::setLight(const bool on) { + this->setButton(kNeoclimaButtonLight); + if (on) + remote_state[3] |= kNeoclimaLightMask; + else + remote_state[3] &= ~kNeoclimaLightMask; +} + +bool IRNeoclimaAc::getLight(void) { + return remote_state[3] & kNeoclimaLightMask; +} + +// This feature maintains the room temperature steadily at 8°C and prevents the +// room from freezing by activating the heating operation automatically when +// nobody is at home over a longer period during severe winter. +void IRNeoclimaAc::set8CHeat(const bool on) { + this->setButton(kNeoclimaButton8CHeat); + if (on) + remote_state[1] |= kNeoclima8CHeatMask; + else + remote_state[1] &= ~kNeoclima8CHeatMask; +} + +bool IRNeoclimaAc::get8CHeat(void) { + return remote_state[1] & kNeoclima8CHeatMask; +} + +void IRNeoclimaAc::setEye(const bool on) { + this->setButton(kNeoclimaButtonEye); + if (on) + remote_state[3] |= kNeoclimaEyeMask; + else + remote_state[3] &= ~kNeoclimaEyeMask; +} + +bool IRNeoclimaAc::getEye(void) { + return remote_state[3] & kNeoclimaEyeMask; +} + +/* DISABLED + TODO(someone): Work out why "on" is either 0x5D or 0x5F +void IRNeoclimaAc::setFollow(const bool on) { + this->setButton(kNeoclimaButtonFollow); + if (on) + remote_state[8] = kNeoclimaFollowMe; + else + remote_state[8] = 0; +} +*/ + +bool IRNeoclimaAc::getFollow(void) { + return (remote_state[8] & kNeoclimaFollowMe) == kNeoclimaFollowMe; +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRNeoclimaAc::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::NEOCLIMA; + result.model = -1; // No models used. + result.power = this->getPower(); + result.mode = this->toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(); + result.fanspeed = this->toCommonFanSpeed(this->getFan()); + result.swingv = this->getSwingV() ? stdAc::swingv_t::kAuto + : stdAc::swingv_t::kOff; + result.swingh = this->getSwingH() ? stdAc::swingh_t::kAuto + : stdAc::swingh_t::kOff; + result.turbo = this->getTurbo(); + result.light = this->getLight(); + result.filter = this->getIon(); + result.sleep = this->getSleep() ? 0 : -1; + // Not supported. + result.quiet = false; + result.econo = false; + result.clean = false; + result.beep = false; + result.clock = -1; + return result; +} + +// Convert the internal state into a human readable string. +String IRNeoclimaAc::toString(void) { + String result = ""; + result.reserve(100); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(getPower(), F("Power"), false); + result += addModeToString(getMode(), kNeoclimaAuto, kNeoclimaCool, + kNeoclimaHeat, kNeoclimaDry, kNeoclimaFan); + result += addTempToString(getTemp()); + result += addFanToString(getFan(), kNeoclimaFanHigh, kNeoclimaFanLow, + kNeoclimaFanAuto, kNeoclimaFanAuto, kNeoclimaFanMed); + result += addBoolToString(getSwingV(), F("Swing(V)")); + result += addBoolToString(getSwingH(), F("Swing(H)")); + result += addBoolToString(getSleep(), F("Sleep")); + result += addBoolToString(getTurbo(), F("Turbo")); + result += addBoolToString(getHold(), F("Hold")); + result += addBoolToString(getIon(), F("Ion")); + result += addBoolToString(getEye(), F("Eye")); + result += addBoolToString(getLight(), F("Light")); + result += addBoolToString(getFollow(), F("Follow")); + result += addBoolToString(get8CHeat(), F("8C Heat")); + result += addBoolToString(getFresh(), F("Fresh")); + result += addIntToString(getButton(), F("Button")); + result += F(" ("); + switch (this->getButton()) { + case kNeoclimaButtonPower: result += F("Power"); break; + case kNeoclimaButtonMode: result += F("Mode"); break; + case kNeoclimaButtonTempUp: result += F("Temp Up"); break; + case kNeoclimaButtonTempDown: result += F("Temp Down"); break; + case kNeoclimaButtonSwing: result += F("Swing"); break; + case kNeoclimaButtonFanSpeed: result += F("Speed"); break; + case kNeoclimaButtonAirFlow: result += F("Air Flow"); break; + case kNeoclimaButtonHold: result += F("Hold"); break; + case kNeoclimaButtonSleep: result += F("Sleep"); break; + case kNeoclimaButtonLight: result += F("Light"); break; + case kNeoclimaButtonEye: result += F("Eye"); break; + case kNeoclimaButtonFollow: result += F("Follow"); break; + case kNeoclimaButtonIon: result += F("Ion"); break; + case kNeoclimaButtonFresh: result += F("Fresh"); break; + case kNeoclimaButton8CHeat: result += F("8C Heat"); break; + case kNeoclimaButtonTurbo: result += F("Turbo"); break; + default: + result += F("Unknown"); + } + result += ')'; + return result; +} + +#if DECODE_NEOCLIMA +// Decode the supplied Neoclima message. +// +// Args: +// results: Ptr to the data to decode and where to store the decode result. +// nbits: Nr. of data bits to expect. Typically kNeoclimaBits. +// strict: Flag indicating if we should perform strict matching. +// Returns: +// boolean: True if it can decode it, false if it can't. +// +// Status: BETA / Known working +// +// Ref: +// https://github.com/crankyoldgit/IRremoteESP8266/issues/764 +bool IRrecv::decodeNeoclima(decode_results *results, const uint16_t nbits, + const bool strict) { + // Compliance + if (strict && nbits != kNeoclimaBits) + return false; // Incorrect nr. of bits per spec. + + uint16_t offset = kStartOffset; + // Match Main Header + Data + Footer + uint16_t used; + used = matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, nbits, + kNeoclimaHdrMark, kNeoclimaHdrSpace, + kNeoclimaBitMark, kNeoclimaOneSpace, + kNeoclimaBitMark, kNeoclimaZeroSpace, + kNeoclimaBitMark, kNeoclimaHdrSpace, false, + _tolerance, 0, false); + if (!used) return false; + offset += used; + // Extra footer. + uint64_t unused; + if (!matchGeneric(results->rawbuf + offset, &unused, + results->rawlen - offset, 0, 0, 0, 0, 0, 0, 0, + kNeoclimaBitMark, kNeoclimaHdrSpace, true)) return false; + + // Compliance + if (strict) { + // Check we got a valid checksum. + if (!IRNeoclimaAc::validChecksum(results->state, nbits / 8)) return false; + } + + // Success + results->decode_type = decode_type_t::NEOCLIMA; + results->bits = nbits; + // No need to record the state as we stored it as we decoded it. + // As we use result->state, we don't record value, address, or command as it + // is a union data type. + return true; +} +#endif // DECODE_NEOCLIMA diff --git a/lib/IRremoteESP8266-2.6.5/src/ir_Neoclima.h b/lib/IRremoteESP8266-2.6.5/src/ir_Neoclima.h new file mode 100644 index 000000000..9e99c8a9e --- /dev/null +++ b/lib/IRremoteESP8266-2.6.5/src/ir_Neoclima.h @@ -0,0 +1,154 @@ +// Neoclima A/C +// +// Copyright 2019 David Conran + +// Analysis by crankyoldgit & AndreyShpilevoy + +#ifndef IR_NEOCLIMA_H_ +#define IR_NEOCLIMA_H_ + +#define __STDC_LIMIT_MACROS +#include +#ifndef UNIT_TEST +#include +#endif +#include "IRremoteESP8266.h" +#include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif + +// Supports: +// Brand: Neoclima, Model: NS-09AHTI A/C +// Brand: Neoclima, Model: ZH/TY-01 remote + +// Ref: +// https://github.com/crankyoldgit/IRremoteESP8266/issues/764 +// https://drive.google.com/file/d/1kjYk4zS9NQcMQhFkak-L4mp4UuaAIesW/view + +// Constants +// state[1] +const uint8_t kNeoclima8CHeatMask = 0b00000010; +const uint8_t kNeoclimaIonMask = 0b00000100; +// state[3] +const uint8_t kNeoclimaLightMask = 0b00000001; +const uint8_t kNeoclimaHoldMask = 0b00000100; +const uint8_t kNeoclimaTurboMask = 0b00001000; +const uint8_t kNeoclimaEyeMask = 0b01000000; +// state[5] +const uint8_t kNeoclimaFreshMask = 0b10000000; +const uint8_t kNeoclimaButtonMask = 0b00011111; +const uint8_t kNeoclimaButtonPower = 0x00; +const uint8_t kNeoclimaButtonMode = 0x01; +const uint8_t kNeoclimaButtonTempUp = 0x02; +const uint8_t kNeoclimaButtonTempDown = 0x03; +const uint8_t kNeoclimaButtonSwing = 0x04; +const uint8_t kNeoclimaButtonFanSpeed = 0x05; +const uint8_t kNeoclimaButtonAirFlow = 0x07; +const uint8_t kNeoclimaButtonHold = 0x08; +const uint8_t kNeoclimaButtonSleep = 0x09; +const uint8_t kNeoclimaButtonTurbo = 0x0A; +const uint8_t kNeoclimaButtonLight = 0x0B; +const uint8_t kNeoclimaButtonEye = 0x0E; +const uint8_t kNeoclimaButtonFollow = 0x13; +const uint8_t kNeoclimaButtonIon = 0x14; +const uint8_t kNeoclimaButtonFresh = 0x15; +const uint8_t kNeoclimaButton8CHeat = 0x1D; +// state[7] +const uint8_t kNeoclimaSleepMask = 0b00000001; +const uint8_t kNeoclimaPowerMask = 0b00000010; +const uint8_t kNeoclimaSwingVMask = 0b00001100; +const uint8_t kNeoclimaSwingVOn = 0b00000100; +const uint8_t kNeoclimaSwingVOff = 0b00001000; +const uint8_t kNeoclimaSwingHMask = 0b00010000; +const uint8_t kNeoclimaFanMask = 0b01100000; +const uint8_t kNeoclimaFanAuto = 0b00; +const uint8_t kNeoclimaFanHigh = 0b01; +const uint8_t kNeoclimaFanMed = 0b10; +const uint8_t kNeoclimaFanLow = 0b11; +// state[8] +const uint8_t kNeoclimaFollowMe = 0x5D; // Also 0x5F +// state[9] +const uint8_t kNeoclimaTempMask = 0b00011111; +const uint8_t kNeoclimaMinTemp = 16; // 16C +const uint8_t kNeoclimaMaxTemp = 32; // 32C +const uint8_t kNeoclimaModeMask = 0b11100000; +const uint8_t kNeoclimaAuto = 0b000; +const uint8_t kNeoclimaCool = 0b001; +const uint8_t kNeoclimaDry = 0b010; +const uint8_t kNeoclimaFan = 0b011; +const uint8_t kNeoclimaHeat = 0b100; + +// Classes +class IRNeoclimaAc { + public: + explicit IRNeoclimaAc(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); + + void stateReset(void); +#if SEND_NEOCLIMA + void send(const uint16_t repeat = kNeoclimaMinRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } +#endif // SEND_NEOCLIMA + void begin(void); + void setButton(const uint8_t button); + uint8_t getButton(void); + void on(void); + void off(void); + void setPower(const bool on); + bool getPower(void); + void setMode(const uint8_t mode); + uint8_t getMode(void); + void setTemp(const uint8_t temp); + uint8_t getTemp(void); + void setFan(const uint8_t speed); + uint8_t getFan(void); + void setSwingV(const bool on); + bool getSwingV(void); + void setSwingH(const bool on); + bool getSwingH(void); + void setSleep(const bool on); + bool getSleep(void); + void setTurbo(const bool on); + bool getTurbo(void); + void setFresh(const bool on); + bool getFresh(void); + void setHold(const bool on); + bool getHold(void); + void setIon(const bool on); + bool getIon(void); + void setLight(const bool on); + bool getLight(void); + void set8CHeat(const bool on); + bool get8CHeat(void); + void setEye(const bool on); + bool getEye(void); + // DISABLED: See TODO in ir_Neoclima.cpp + // void setFollow(const bool on); + bool getFollow(void); + uint8_t* getRaw(void); + void setRaw(const uint8_t new_code[], + const uint16_t length = kNeoclimaStateLength); + static bool validChecksum(const uint8_t state[], + const uint16_t length = kNeoclimaStateLength); + static uint8_t calcChecksum(const uint8_t state[], + const uint16_t length = kNeoclimaStateLength); + String toString(void); + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + stdAc::state_t toCommon(void); +#ifndef UNIT_TEST + + private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif + // The state of the IR remote in IR code form. + uint8_t remote_state[kNeoclimaStateLength]; + void checksum(const uint16_t length = kNeoclimaStateLength); +}; + +#endif // IR_NEOCLIMA_H_ diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Nikai.cpp b/lib/IRremoteESP8266-2.6.5/src/ir_Nikai.cpp similarity index 58% rename from lib/IRremoteESP8266-2.6.0/src/ir_Nikai.cpp rename to lib/IRremoteESP8266-2.6.5/src/ir_Nikai.cpp index 9ac22a849..12e99c278 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Nikai.cpp +++ b/lib/IRremoteESP8266-2.6.5/src/ir_Nikai.cpp @@ -1,20 +1,16 @@ // Copyright 2009 Ken Shirriff // Copyright 2017 David Conran +// Nikai + #include #include "IRrecv.h" #include "IRsend.h" #include "IRutils.h" -// NN NN IIIII KK KK AAA IIIII -// NNN NN III KK KK AAAAA III -// NN N NN III KKKK AA AA III -// NN NNN III KK KK AAAAAAA III -// NN NN IIIII KK KK AA AA IIIII - // Constants // Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/309 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/309 const uint16_t kNikaiTick = 500; const uint16_t kNikaiHdrMarkTicks = 8; const uint16_t kNikaiHdrMark = kNikaiHdrMarkTicks * kNikaiTick; @@ -39,7 +35,7 @@ const uint16_t kNikaiMinGap = kNikaiMinGapTicks * kNikaiTick; // // Status: STABLE / Working. // -// Ref: https://github.com/markszabo/IRremoteESP8266/issues/309 +// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/309 void IRsend::sendNikai(uint64_t data, uint16_t nbits, uint16_t repeat) { sendGeneric(kNikaiHdrMark, kNikaiHdrSpace, kNikaiBitMark, kNikaiOneSpace, kNikaiBitMark, kNikaiZeroSpace, kNikaiBitMark, kNikaiMinGap, data, @@ -61,38 +57,19 @@ void IRsend::sendNikai(uint64_t data, uint16_t nbits, uint16_t repeat) { // Status: STABLE / Working. // bool IRrecv::decodeNikai(decode_results *results, uint16_t nbits, bool strict) { - if (results->rawlen < 2 * nbits + kHeader + kFooter - 1) - return false; // Can't possibly be a valid Nikai message. if (strict && nbits != kNikaiBits) return false; // We expect Nikai to be a certain sized message. uint64_t data = 0; uint16_t offset = kStartOffset; - // Header - if (!matchMark(results->rawbuf[offset], kNikaiHdrMark)) return false; - // Calculate how long the common tick time is based on the header mark. - uint32_t m_tick = results->rawbuf[offset++] * kRawTick / kNikaiHdrMarkTicks; - if (!matchSpace(results->rawbuf[offset], kNikaiHdrSpace)) return false; - // Calculate how long the common tick time is based on the header space. - uint32_t s_tick = results->rawbuf[offset++] * kRawTick / kNikaiHdrSpaceTicks; - // Data - match_result_t data_result = - matchData(&(results->rawbuf[offset]), nbits, kNikaiBitMarkTicks * m_tick, - kNikaiOneSpaceTicks * s_tick, kNikaiBitMarkTicks * m_tick, - kNikaiZeroSpaceTicks * s_tick); - if (data_result.success == false) return false; - data = data_result.data; - offset += data_result.used; - // Footer - if (!matchMark(results->rawbuf[offset++], kNikaiBitMarkTicks * m_tick)) - return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset], kNikaiMinGapTicks * s_tick)) - return false; - - // Compliance - + // Match Header + Data + Footer + if (!matchGeneric(results->rawbuf + offset, &data, + results->rawlen - offset, nbits, + kNikaiHdrMark, kNikaiHdrSpace, + kNikaiBitMark, kNikaiOneSpace, + kNikaiBitMark, kNikaiZeroSpace, + kNikaiBitMark, kNikaiMinGap, true)) return false; // Success results->bits = nbits; results->value = data; diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Panasonic.cpp b/lib/IRremoteESP8266-2.6.5/src/ir_Panasonic.cpp similarity index 69% rename from lib/IRremoteESP8266-2.6.0/src/ir_Panasonic.cpp rename to lib/IRremoteESP8266-2.6.5/src/ir_Panasonic.cpp index 47aa51c96..1a24ac41f 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Panasonic.cpp +++ b/lib/IRremoteESP8266-2.6.5/src/ir_Panasonic.cpp @@ -1,6 +1,8 @@ // Copyright 2015 Kristian Lauszus // Copyright 2017, 2018 David Conran +// Panasonic devices + #include "ir_Panasonic.h" #include #ifndef ARDUINO @@ -10,12 +12,6 @@ #include "IRsend.h" #include "IRutils.h" -// PPPP AAA N N AAA SSSS OOO N N IIIII CCCC -// P P A A NN N A A S O O NN N I C -// PPPP AAAAA N N N AAAAA SSS O O N N N I C -// P A A N NN A A S O O N NN I C -// P A A N N A A SSSS OOO N N IIIII CCCC - // Panasonic protocol originally added by Kristian Lauszus from: // https://github.com/z3t0/Arduino-IRremote // (Thanks to zenwheel and other people at the original blog post) @@ -65,6 +61,14 @@ const uint16_t kPanasonicAcSectionGap = 10000; const uint16_t kPanasonicAcSection1Length = 8; const uint32_t kPanasonicAcMessageGap = kDefaultMessageGap; // Just a guess. +using irutils::addBoolToString; +using irutils::addFanToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addTempToString; +using irutils::minsToString; + #if (SEND_PANASONIC || SEND_DENON) // Send a Panasonic formatted message. // @@ -77,7 +81,8 @@ const uint32_t kPanasonicAcMessageGap = kDefaultMessageGap; // Just a guess. // // Note: // This protocol is a modified version of Kaseikyo. -void IRsend::sendPanasonic64(uint64_t data, uint16_t nbits, uint16_t repeat) { +void IRsend::sendPanasonic64(const uint64_t data, const uint16_t nbits, + const uint16_t repeat) { sendGeneric(kPanasonicHdrMark, kPanasonicHdrSpace, kPanasonicBitMark, kPanasonicOneSpace, kPanasonicBitMark, kPanasonicZeroSpace, kPanasonicBitMark, kPanasonicMinGap, kPanasonicMinCommandLength, @@ -96,8 +101,8 @@ void IRsend::sendPanasonic64(uint64_t data, uint16_t nbits, uint16_t repeat) { // // Note: // This protocol is a modified version of Kaseikyo. -void IRsend::sendPanasonic(uint16_t address, uint32_t data, uint16_t nbits, - uint16_t repeat) { +void IRsend::sendPanasonic(const uint16_t address, const uint32_t data, + const uint16_t nbits, const uint16_t repeat) { sendPanasonic64(((uint64_t)address << 32) | (uint64_t)data, nbits, repeat); } @@ -117,8 +122,10 @@ void IRsend::sendPanasonic(uint16_t address, uint32_t data, uint16_t nbits, // Panasonic 48-bit protocol is a modified version of Kaseikyo. // Ref: // http://www.remotecentral.com/cgi-bin/mboard/rc-pronto/thread.cgi?2615 -uint64_t IRsend::encodePanasonic(uint16_t manufacturer, uint8_t device, - uint8_t subdevice, uint8_t function) { +uint64_t IRsend::encodePanasonic(const uint16_t manufacturer, + const uint8_t device, + const uint8_t subdevice, + const uint8_t function) { uint8_t checksum = device ^ subdevice ^ function; return (((uint64_t)manufacturer << 32) | ((uint64_t)device << 24) | ((uint64_t)subdevice << 16) | ((uint64_t)function << 8) | checksum); @@ -141,42 +148,21 @@ uint64_t IRsend::encodePanasonic(uint16_t manufacturer, uint8_t device, // Ref: // http://www.remotecentral.com/cgi-bin/mboard/rc-pronto/thread.cgi?26152 // http://www.hifi-remote.com/wiki/index.php?title=Panasonic -bool IRrecv::decodePanasonic(decode_results *results, uint16_t nbits, - bool strict, uint32_t manufacturer) { - if (results->rawlen < 2 * nbits + kHeader + kFooter - 1) - return false; // Not enough entries to be a Panasonic message. +bool IRrecv::decodePanasonic(decode_results *results, const uint16_t nbits, + const bool strict, const uint32_t manufacturer) { if (strict && nbits != kPanasonicBits) return false; // Request is out of spec. uint64_t data = 0; uint16_t offset = kStartOffset; - // Header - if (!matchMark(results->rawbuf[offset], kPanasonicHdrMark)) return false; - // Calculate how long the common tick time is based on the header mark. - uint32_t m_tick = - results->rawbuf[offset++] * kRawTick / kPanasonicHdrMarkTicks; - if (!matchSpace(results->rawbuf[offset], kPanasonicHdrSpace)) return false; - // Calculate how long the common tick time is based on the header space. - uint32_t s_tick = - results->rawbuf[offset++] * kRawTick / kPanasonicHdrSpaceTicks; - - // Data - match_result_t data_result = matchData( - &(results->rawbuf[offset]), nbits, kPanasonicBitMarkTicks * m_tick, - kPanasonicOneSpaceTicks * s_tick, kPanasonicBitMarkTicks * m_tick, - kPanasonicZeroSpaceTicks * s_tick); - if (data_result.success == false) return false; - data = data_result.data; - offset += data_result.used; - - // Footer - if (!match(results->rawbuf[offset++], kPanasonicBitMarkTicks * m_tick)) - return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset], kPanasonicEndGap)) - return false; - + // Match Header + Data + Footer + if (!matchGeneric(results->rawbuf + offset, &data, + results->rawlen - offset, nbits, + kPanasonicHdrMark, kPanasonicHdrSpace, + kPanasonicBitMark, kPanasonicOneSpace, + kPanasonicBitMark, kPanasonicZeroSpace, + kPanasonicBitMark, kPanasonicEndGap, true)) return false; // Compliance uint32_t address = data >> 32; uint32_t command = data & 0xFFFFFFFF; @@ -217,7 +203,8 @@ bool IRrecv::decodePanasonic(decode_results *results, uint16_t nbits, // A75C3747 // A75C3704 // -void IRsend::sendPanasonicAC(uint8_t data[], uint16_t nbytes, uint16_t repeat) { +void IRsend::sendPanasonicAC(const uint8_t data[], const uint16_t nbytes, + const uint16_t repeat) { if (nbytes < kPanasonicAcSection1Length) return; for (uint16_t r = 0; r <= repeat; r++) { // First section. (8 bytes) @@ -236,16 +223,18 @@ void IRsend::sendPanasonicAC(uint8_t data[], uint16_t nbytes, uint16_t repeat) { } #endif // SEND_PANASONIC_AC -IRPanasonicAc::IRPanasonicAc(uint16_t pin) : _irsend(pin) { stateReset(); } +IRPanasonicAc::IRPanasonicAc(const uint16_t pin, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { this->stateReset(); } -void IRPanasonicAc::stateReset() { +void IRPanasonicAc::stateReset(void) { for (uint8_t i = 0; i < kPanasonicAcStateLength; i++) remote_state[i] = kPanasonicKnownGoodState[i]; _temp = 25; // An initial saved desired temp. Completely made up. _swingh = kPanasonicAcSwingHMiddle; // A similar made up value for H Swing. } -void IRPanasonicAc::begin() { _irsend.begin(); } +void IRPanasonicAc::begin(void) { _irsend.begin(); } // Verify the checksum is valid for a given state. // Args: @@ -264,12 +253,12 @@ uint8_t IRPanasonicAc::calcChecksum(uint8_t state[], const uint16_t length) { } void IRPanasonicAc::fixChecksum(const uint16_t length) { - remote_state[length - 1] = calcChecksum(remote_state, length); + remote_state[length - 1] = this->calcChecksum(remote_state, length); } #if SEND_PANASONIC_AC void IRPanasonicAc::send(const uint16_t repeat) { - fixChecksum(); + this->fixChecksum(); _irsend.sendPanasonicAC(remote_state, kPanasonicAcStateLength, repeat); } #endif // SEND_PANASONIC_AC @@ -302,7 +291,7 @@ void IRPanasonicAc::setModel(const panasonic_ac_remote_model_t model) { remote_state[23] = 0x01; remote_state[25] = 0x06; // Has to be done last as setSwingHorizontal has model check built-in - setSwingHorizontal(_swingh); + this->setSwingHorizontal(_swingh); break; case kPanasonicNke: remote_state[17] = 0x06; @@ -321,7 +310,7 @@ void IRPanasonicAc::setModel(const panasonic_ac_remote_model_t model) { } } -panasonic_ac_remote_model_t IRPanasonicAc::getModel() { +panasonic_ac_remote_model_t IRPanasonicAc::getModel(void) { if (remote_state[23] == 0x89) return kPanasonicRkr; if (remote_state[17] == 0x00) { if ((remote_state[21] & 0x10) && (remote_state[23] & 0x01)) @@ -335,8 +324,8 @@ panasonic_ac_remote_model_t IRPanasonicAc::getModel() { return kPanasonicUnknown; } -uint8_t *IRPanasonicAc::getRaw() { - fixChecksum(); +uint8_t *IRPanasonicAc::getRaw(void) { + this->fixChecksum(); return remote_state; } @@ -357,32 +346,32 @@ void IRPanasonicAc::setRaw(const uint8_t state[]) { // // For all other models, setPower(true) should set the internal state to // turn it on, and setPower(false) should turn it off. -void IRPanasonicAc::setPower(const bool state) { - if (state) - on(); +void IRPanasonicAc::setPower(const bool on) { + if (on) + this->on(); else - off(); + this->off(); } // Return the A/C power state of the remote. // Except for CKP models, where it returns if the power state will be toggled // on the A/C unit when the next message is sent. -bool IRPanasonicAc::getPower() { +bool IRPanasonicAc::getPower(void) { return (remote_state[13] & kPanasonicAcPower) == kPanasonicAcPower; } -void IRPanasonicAc::on() { remote_state[13] |= kPanasonicAcPower; } +void IRPanasonicAc::on(void) { remote_state[13] |= kPanasonicAcPower; } -void IRPanasonicAc::off() { remote_state[13] &= ~kPanasonicAcPower; } +void IRPanasonicAc::off(void) { remote_state[13] &= ~kPanasonicAcPower; } -uint8_t IRPanasonicAc::getMode() { return remote_state[13] >> 4; } +uint8_t IRPanasonicAc::getMode(void) { return remote_state[13] >> 4; } void IRPanasonicAc::setMode(const uint8_t desired) { uint8_t mode = kPanasonicAcAuto; // Default to Auto mode. switch (desired) { case kPanasonicAcFan: // Allegedly Fan mode has a temperature of 27. - setTemp(kPanasonicAcFanModeTemp, false); + this->setTemp(kPanasonicAcFanModeTemp, false); mode = desired; break; case kPanasonicAcAuto: @@ -391,16 +380,16 @@ void IRPanasonicAc::setMode(const uint8_t desired) { case kPanasonicAcDry: mode = desired; // Set the temp to the saved temp, just incase our previous mode was Fan. - setTemp(_temp); + this->setTemp(_temp); break; } remote_state[13] &= 0x0F; // Clear the previous mode bits. remote_state[13] |= mode << 4; } -uint8_t IRPanasonicAc::getTemp() { return remote_state[14] >> 1; } +uint8_t IRPanasonicAc::getTemp(void) { return remote_state[14] >> 1; } -// Set the desitred temperature in Celcius. +// Set the desitred temperature in Celsius. // Args: // celsius: The temperature to set the A/C unit to. // remember: A boolean flag for the class to remember the temperature. @@ -414,7 +403,9 @@ void IRPanasonicAc::setTemp(const uint8_t celsius, const bool remember) { if (remember) _temp = temperature; } -uint8_t IRPanasonicAc::getSwingVertical() { return remote_state[16] & 0x0F; } +uint8_t IRPanasonicAc::getSwingVertical(void) { + return remote_state[16] & 0x0F; +} void IRPanasonicAc::setSwingVertical(const uint8_t desired_elevation) { uint8_t elevation = desired_elevation; @@ -426,7 +417,7 @@ void IRPanasonicAc::setSwingVertical(const uint8_t desired_elevation) { remote_state[16] |= elevation; } -uint8_t IRPanasonicAc::getSwingHorizontal() { return remote_state[17]; } +uint8_t IRPanasonicAc::getSwingHorizontal(void) { return remote_state[17]; } void IRPanasonicAc::setSwingHorizontal(const uint8_t desired_direction) { switch (desired_direction) { @@ -442,7 +433,7 @@ void IRPanasonicAc::setSwingHorizontal(const uint8_t desired_direction) { } _swingh = desired_direction; // Store the direction for later. uint8_t direction = desired_direction; - switch (getModel()) { + switch (this->getModel()) { case kPanasonicDke: case kPanasonicRkr: break; @@ -462,12 +453,12 @@ void IRPanasonicAc::setFan(const uint8_t speed) { (remote_state[16] & 0x0F) | ((speed + kPanasonicAcFanOffset) << 4); } -uint8_t IRPanasonicAc::getFan() { +uint8_t IRPanasonicAc::getFan(void) { return (remote_state[16] >> 4) - kPanasonicAcFanOffset; } -bool IRPanasonicAc::getQuiet() { - switch (getModel()) { +bool IRPanasonicAc::getQuiet(void) { + switch (this->getModel()) { case kPanasonicRkr: case kPanasonicCkp: return remote_state[21] & kPanasonicAcQuietCkp; @@ -476,9 +467,9 @@ bool IRPanasonicAc::getQuiet() { } } -void IRPanasonicAc::setQuiet(const bool state) { +void IRPanasonicAc::setQuiet(const bool on) { uint8_t quiet; - switch (getModel()) { + switch (this->getModel()) { case kPanasonicRkr: case kPanasonicCkp: quiet = kPanasonicAcQuietCkp; @@ -487,16 +478,16 @@ void IRPanasonicAc::setQuiet(const bool state) { quiet = kPanasonicAcQuiet; } - if (state) { - setPowerful(false); // Powerful is mutually exclusive. + if (on) { + this->setPowerful(false); // Powerful is mutually exclusive. remote_state[21] |= quiet; } else { remote_state[21] &= ~quiet; } } -bool IRPanasonicAc::getPowerful() { - switch (getModel()) { +bool IRPanasonicAc::getPowerful(void) { + switch (this->getModel()) { case kPanasonicRkr: case kPanasonicCkp: return remote_state[21] & kPanasonicAcPowerfulCkp; @@ -505,9 +496,9 @@ bool IRPanasonicAc::getPowerful() { } } -void IRPanasonicAc::setPowerful(const bool state) { +void IRPanasonicAc::setPowerful(const bool on) { uint8_t powerful; - switch (getModel()) { + switch (this->getModel()) { case kPanasonicRkr: case kPanasonicCkp: powerful = kPanasonicAcPowerfulCkp; @@ -516,8 +507,8 @@ void IRPanasonicAc::setPowerful(const bool state) { powerful = kPanasonicAcPowerful; } - if (state) { - setQuiet(false); // Quiet is mutually exclusive. + if (on) { + this->setQuiet(false); // Quiet is mutually exclusive. remote_state[21] |= powerful; } else { remote_state[21] &= ~powerful; @@ -528,7 +519,7 @@ uint16_t IRPanasonicAc::encodeTime(const uint8_t hours, const uint8_t mins) { return std::min(hours, (uint8_t)23) * 60 + std::min(mins, (uint8_t)59); } -uint16_t IRPanasonicAc::getClock() { +uint16_t IRPanasonicAc::getClock(void) { uint16_t result = ((remote_state[25] & 0b00000111) << 8) + remote_state[24]; if (result == kPanasonicAcTimeSpecial) return 0; return result; @@ -543,7 +534,7 @@ void IRPanasonicAc::setClock(const uint16_t mins_since_midnight) { remote_state[25] |= (corrected >> 8); } -uint16_t IRPanasonicAc::getOnTimer() { +uint16_t IRPanasonicAc::getOnTimer(void) { uint16_t result = ((remote_state[19] & 0b00000111) << 8) + remote_state[18]; if (result == kPanasonicAcTimeSpecial) return 0; return result; @@ -567,13 +558,13 @@ void IRPanasonicAc::setOnTimer(const uint16_t mins_since_midnight, remote_state[19] |= (corrected >> 8); } -void IRPanasonicAc::cancelOnTimer() { setOnTimer(0, false); } +void IRPanasonicAc::cancelOnTimer(void) { this->setOnTimer(0, false); } -bool IRPanasonicAc::isOnTimerEnabled() { +bool IRPanasonicAc::isOnTimerEnabled(void) { return remote_state[13] & kPanasonicAcOnTimer; } -uint16_t IRPanasonicAc::getOffTimer() { +uint16_t IRPanasonicAc::getOffTimer(void) { uint16_t result = ((remote_state[20] & 0b01111111) << 4) + (remote_state[19] >> 4); if (result == kPanasonicAcTimeSpecial) return 0; @@ -599,25 +590,12 @@ void IRPanasonicAc::setOffTimer(const uint16_t mins_since_midnight, remote_state[20] |= corrected >> 4; } -void IRPanasonicAc::cancelOffTimer() { setOffTimer(0, false); } +void IRPanasonicAc::cancelOffTimer(void) { this->setOffTimer(0, false); } -bool IRPanasonicAc::isOffTimerEnabled() { +bool IRPanasonicAc::isOffTimerEnabled(void) { return remote_state[13] & kPanasonicAcOffTimer; } -#ifdef ARDUINO -String IRPanasonicAc::timeToString(const uint16_t mins_since_midnight) { - String result = ""; -#else -std::string IRPanasonicAc::timeToString(const uint16_t mins_since_midnight) { - std::string result = ""; -#endif // ARDUINO - result += uint64ToString(mins_since_midnight / 60) + ':'; - uint8_t mins = mins_since_midnight % 60; - if (mins < 10) result += '0'; // Zero pad the minutes. - return result + uint64ToString(mins); -} - // Convert a standard A/C mode into its native mode. uint8_t IRPanasonicAc::convertMode(const stdAc::opmode_t mode) { switch (mode) { @@ -685,14 +663,79 @@ uint8_t IRPanasonicAc::convertSwingH(const stdAc::swingh_t position) { } } +// Convert a native mode to it's common equivalent. +stdAc::opmode_t IRPanasonicAc::toCommonMode(const uint8_t mode) { + switch (mode) { + case kPanasonicAcCool: return stdAc::opmode_t::kCool; + case kPanasonicAcHeat: return stdAc::opmode_t::kHeat; + case kPanasonicAcDry: return stdAc::opmode_t::kDry; + case kPanasonicAcFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRPanasonicAc::toCommonFanSpeed(const uint8_t spd) { + switch (spd) { + case kPanasonicAcFanMax: return stdAc::fanspeed_t::kMax; + case kPanasonicAcFanMin + 3: return stdAc::fanspeed_t::kHigh; + case kPanasonicAcFanMin + 2: return stdAc::fanspeed_t::kMedium; + case kPanasonicAcFanMin + 1: return stdAc::fanspeed_t::kLow; + case kPanasonicAcFanMin: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +// Convert a native vertical swing to it's common equivalent. +stdAc::swingh_t IRPanasonicAc::toCommonSwingH(const uint8_t pos) { + switch (pos) { + case kPanasonicAcSwingHFullLeft: return stdAc::swingh_t::kLeftMax; + case kPanasonicAcSwingHLeft: return stdAc::swingh_t::kLeft; + case kPanasonicAcSwingHMiddle: return stdAc::swingh_t::kMiddle; + case kPanasonicAcSwingHRight: return stdAc::swingh_t::kRight; + case kPanasonicAcSwingHFullRight: return stdAc::swingh_t::kRightMax; + default: return stdAc::swingh_t::kAuto; + } +} + +// Convert a native vertical swing to it's common equivalent. +stdAc::swingv_t IRPanasonicAc::toCommonSwingV(const uint8_t pos) { + switch (pos) { + case kPanasonicAcSwingVUp: return stdAc::swingv_t::kHighest; + case kPanasonicAcSwingVDown: return stdAc::swingv_t::kLowest; + default: return stdAc::swingv_t::kAuto; + } +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRPanasonicAc::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::PANASONIC_AC; + result.model = this->getModel(); + result.power = this->getPower(); + result.mode = this->toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(); + result.fanspeed = this->toCommonFanSpeed(this->getFan()); + result.swingv = this->toCommonSwingV(this->getSwingVertical()); + result.swingh = this->toCommonSwingH(this->getSwingHorizontal()); + result.quiet = this->getQuiet(); + result.turbo = this->getPowerful(); + // Not supported. + result.econo = false; + result.clean = false; + result.filter = false; + result.light = false; + result.beep = false; + result.sleep = -1; + result.clock = -1; + return result; +} + // Convert the internal state into a human readable string. -#ifdef ARDUINO -String IRPanasonicAc::toString() { +String IRPanasonicAc::toString(void) { String result = ""; -#else -std::string IRPanasonicAc::toString() { - std::string result = ""; -#endif // ARDUINO + result.reserve(180); // Reserve some heap for the string to reduce fragging. result += F("Model: "); result += uint64ToString(getModel()); switch (getModel()) { @@ -717,52 +760,14 @@ std::string IRPanasonicAc::toString() { default: result += F(" (UNKNOWN)"); } - result += F(", Power: "); - if (getPower()) - result += F("On"); - else - result += F("Off"); - result += F(", Mode: "); - result += uint64ToString(getMode()); - switch (getMode()) { - case kPanasonicAcAuto: - result += F(" (AUTO)"); - break; - case kPanasonicAcCool: - result += F(" (COOL)"); - break; - case kPanasonicAcHeat: - result += F(" (HEAT)"); - break; - case kPanasonicAcDry: - result += F(" (DRY)"); - break; - case kPanasonicAcFan: - result += F(" (FAN)"); - break; - default: - result += F(" (UNKNOWN)"); - } - result += F(", Temp: "); - result += uint64ToString(getTemp()); - result += F("C, Fan: "); - result += uint64ToString(getFan()); - switch (getFan()) { - case kPanasonicAcFanAuto: - result += F(" (AUTO)"); - break; - case kPanasonicAcFanMax: - result += F(" (MAX)"); - break; - case kPanasonicAcFanMin: - result += F(" (MIN)"); - break; - default: - result += F(" (UNKNOWN)"); - break; - } - result += F(", Swing (Vertical): "); - result += uint64ToString(getSwingVertical()); + result += addBoolToString(getPower(), F("Power")); + result += addModeToString(getMode(), kPanasonicAcAuto, kPanasonicAcCool, + kPanasonicAcHeat, kPanasonicAcDry, kPanasonicAcFan); + result += addTempToString(getTemp()); + result += addFanToString(getFan(), kPanasonicAcFanMax, kPanasonicAcFanMin, + kPanasonicAcFanAuto, kPanasonicAcFanAuto, + kPanasonicAcFanMed); + result += addIntToString(getSwingVertical(), F("Swing (Vertical)")); switch (getSwingVertical()) { case kPanasonicAcSwingVAuto: result += F(" (AUTO)"); @@ -786,8 +791,7 @@ std::string IRPanasonicAc::toString() { case kPanasonicCkp: break; // No Horizontal Swing support. default: - result += F(", Swing (Horizontal): "); - result += uint64ToString(getSwingHorizontal()); + result += addIntToString(getSwingHorizontal(), F("Swing (Horizontal)")); switch (getSwingHorizontal()) { case kPanasonicAcSwingHAuto: result += F(" (AUTO)"); @@ -812,28 +816,15 @@ std::string IRPanasonicAc::toString() { break; } } - result += F(", Quiet: "); - if (getQuiet()) - result += F("On"); - else - result += F("Off"); - result += F(", Powerful: "); - if (getPowerful()) - result += F("On"); - else - result += F("Off"); - result += F(", Clock: "); - result += timeToString(getClock()); - result += F(", On Timer: "); - if (isOnTimerEnabled()) - result += timeToString(getOnTimer()); - else - result += F("Off"); - result += F(", Off Timer: "); - if (isOffTimerEnabled()) - result += timeToString(getOffTimer()); - else - result += F("Off"); + result += addBoolToString(getQuiet(), F("Quiet")); + result += addBoolToString(getPowerful(), F("Powerful")); + result += addLabeledString(minsToString(getClock()), F("Clock")); + result += addLabeledString( + isOnTimerEnabled() ? minsToString(getOnTimer()) : F("Off"), + F("On Timer")); + result += addLabeledString( + isOffTimerEnabled() ? minsToString(getOffTimer()) : F("Off"), + F("Off Timer")); return result; } @@ -856,11 +847,8 @@ std::string IRPanasonicAc::toString() { // A/C Remotes: // A75C3747 (Confirmed) // A75C3704 -bool IRrecv::decodePanasonicAC(decode_results *results, uint16_t nbits, - bool strict) { - if (nbits % 8 != 0) // nbits has to be a multiple of nr. of bits in a byte. - return false; - +bool IRrecv::decodePanasonicAC(decode_results *results, const uint16_t nbits, + const bool strict) { uint8_t min_nr_of_messages = 1; if (strict) { if (nbits != kPanasonicAcBits && nbits != kPanasonicAcShortBits) @@ -871,79 +859,31 @@ bool IRrecv::decodePanasonicAC(decode_results *results, uint16_t nbits, min_nr_of_messages * (2 * nbits + kHeader + kFooter) - 1) return false; // Can't possibly be a valid PANASONIC_AC message. - uint16_t dataBitsSoFar = 0; uint16_t offset = kStartOffset; - match_result_t data_result; - // Header - if (!matchMark(results->rawbuf[offset], kPanasonicHdrMark, - kPanasonicAcTolerance, kPanasonicAcExcess)) - return false; - // Calculate how long the common tick time is based on the header mark. - uint32_t m_tick = - results->rawbuf[offset++] * kRawTick / kPanasonicHdrMarkTicks; - if (!matchSpace(results->rawbuf[offset], kPanasonicHdrSpace, - kPanasonicAcTolerance, kPanasonicAcExcess)) - return false; - // Calculate how long the common tick time is based on the header space. - uint32_t s_tick = - results->rawbuf[offset++] * kRawTick / kPanasonicHdrSpaceTicks; + // Match Header + Data #1 + Footer + uint16_t used; + used = matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, kPanasonicAcSection1Length * 8, + kPanasonicHdrMark, kPanasonicHdrSpace, + kPanasonicBitMark, kPanasonicOneSpace, + kPanasonicBitMark, kPanasonicZeroSpace, + kPanasonicBitMark, kPanasonicAcSectionGap, false, + kPanasonicAcTolerance, kPanasonicAcExcess, false); + if (!used) return false; + offset += used; - uint16_t i = 0; - // Data (Section #1) - // Keep reading bytes until we either run out of section or state to fill. - for (; offset <= results->rawlen - 16 && i < kPanasonicAcSection1Length; - i++, dataBitsSoFar += 8, offset += data_result.used) { - data_result = matchData( - &(results->rawbuf[offset]), 8, kPanasonicBitMarkTicks * m_tick, - kPanasonicOneSpaceTicks * s_tick, kPanasonicBitMarkTicks * m_tick, - kPanasonicZeroSpaceTicks * s_tick, kPanasonicAcTolerance, - kPanasonicAcExcess, false); - if (data_result.success == false) { - DPRINT("DEBUG: offset = "); - DPRINTLN(offset + data_result.used); - return false; // Fail - } - results->state[i] = data_result.data; - } - // Section footer. - if (!matchMark(results->rawbuf[offset++], kPanasonicBitMarkTicks * m_tick, - kPanasonicAcTolerance, kPanasonicAcExcess)) + // Match Header + Data #2 + Footer + if (!matchGeneric(results->rawbuf + offset, + results->state + kPanasonicAcSection1Length, + results->rawlen - offset, + nbits - kPanasonicAcSection1Length * 8, + kPanasonicHdrMark, kPanasonicHdrSpace, + kPanasonicBitMark, kPanasonicOneSpace, + kPanasonicBitMark, kPanasonicZeroSpace, + kPanasonicBitMark, kPanasonicAcMessageGap, true, + kPanasonicAcTolerance, kPanasonicAcExcess, false)) return false; - if (!matchSpace(results->rawbuf[offset++], kPanasonicAcSectionGap, - kPanasonicAcTolerance, kPanasonicAcExcess)) - return false; - // Header. - if (!matchMark(results->rawbuf[offset++], kPanasonicHdrMarkTicks * m_tick, - kPanasonicAcTolerance, kPanasonicAcExcess)) - return false; - if (!matchSpace(results->rawbuf[offset++], kPanasonicHdrSpaceTicks * s_tick, - kPanasonicAcTolerance, kPanasonicAcExcess)) - return false; - // Data (Section #2) - // Keep reading bytes until we either run out of data. - for (; offset <= results->rawlen - 16 && i < nbits / 8; - i++, dataBitsSoFar += 8, offset += data_result.used) { - data_result = matchData( - &(results->rawbuf[offset]), 8, kPanasonicBitMarkTicks * m_tick, - kPanasonicOneSpaceTicks * s_tick, kPanasonicBitMarkTicks * m_tick, - kPanasonicZeroSpaceTicks * s_tick, kPanasonicAcTolerance, - kPanasonicAcExcess, false); - if (data_result.success == false) { - DPRINT("DEBUG: offset = "); - DPRINTLN(offset + data_result.used); - return false; // Fail - } - results->state[i] = data_result.data; - } - // Message Footer. - if (!matchMark(results->rawbuf[offset++], kPanasonicBitMarkTicks * m_tick, - kPanasonicAcTolerance, kPanasonicAcExcess)) - return false; - if (offset <= results->rawlen && - !matchAtLeast(results->rawbuf[offset++], kPanasonicAcMessageGap)) - return false; - // Compliance if (strict) { // Check the signatures of the section blocks. They start with 0x02& 0x20. diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Panasonic.h b/lib/IRremoteESP8266-2.6.5/src/ir_Panasonic.h similarity index 67% rename from lib/IRremoteESP8266-2.6.0/src/ir_Panasonic.h rename to lib/IRremoteESP8266-2.6.5/src/ir_Panasonic.h index 1a7b4e114..32899db9b 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Panasonic.h +++ b/lib/IRremoteESP8266-2.6.5/src/ir_Panasonic.h @@ -1,5 +1,24 @@ // Copyright 2018 David Conran +// Supports: +// Brand: Panasonic, Model: TV +// Brand: Panasonic, Model: JKE series A/C +// Brand: Panasonic, Model: DKE series A/C +// Brand: Panasonic, Model: CKP series A/C +// Brand: Panasonic, Model: CS-ME10CKPG A/C +// Brand: Panasonic, Model: CS-ME12CKPG A/C +// Brand: Panasonic, Model: CS-ME14CKPG A/C +// Brand: Panasonic, Model: RKR series A/C +// Brand: Panasonic, Model: CS-Z9RKR A/C +// Brand: Panasonic, Model: NKE series A/C +// Brand: Panasonic, Model: CS-YW9MKD A/C +// Brand: Panasonic, Model: A75C3747 remote +// Brand: Panasonic, Model: A75C3704 remote +// Brand: Panasonic, Model: A75C2311 remote (CKP) +// Brand: Panasonic, Model: A75C3747 remote +// Brand: Panasonic, Model: A75C3747 remote +// Brand: Panasonic, Model: A75C3747 remote + #ifndef IR_PANASONIC_H_ #define IR_PANASONIC_H_ @@ -7,8 +26,6 @@ #include #ifdef ARDUINO #include -#else -#include #endif #include "IRremoteESP8266.h" #include "IRsend.h" @@ -16,12 +33,6 @@ #include "IRsend_test.h" #endif -// PPPP AAA N N AAA SSSS OOO N N IIIII CCCC -// P P A A NN N A A S O O NN N I C -// PPPP AAAAA N N N AAAAA SSS O O N N N I C -// P A A N NN A A S O O N NN I C -// P A A N N A A SSSS OOO N N IIIII CCCC - // Panasonic A/C support heavily influenced by: // https://github.com/ToniA/ESPEasy/blob/HeatpumpIR/lib/HeatpumpIR/PanasonicHeatpumpIR.cpp @@ -37,6 +48,7 @@ const uint8_t kPanasonicAcCool = 3; // 0b0011 const uint8_t kPanasonicAcHeat = 4; // 0b0010 const uint8_t kPanasonicAcFan = 6; // 0b0110 const uint8_t kPanasonicAcFanMin = 0; +const uint8_t kPanasonicAcFanMed = 2; const uint8_t kPanasonicAcFanMax = 4; const uint8_t kPanasonicAcFanAuto = 7; const uint8_t kPanasonicAcFanOffset = 3; @@ -81,62 +93,63 @@ enum panasonic_ac_remote_model_t { class IRPanasonicAc { public: - explicit IRPanasonicAc(uint16_t pin); + explicit IRPanasonicAc(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); - void stateReset(); + void stateReset(void); #if SEND_PANASONIC void send(const uint16_t repeat = kPanasonicAcDefaultRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } #endif // SEND_PANASONIC - void begin(); - void on(); - void off(); - void setPower(const bool state); - bool getPower(); + void begin(void); + void on(void); + void off(void); + void setPower(const bool on); + bool getPower(void); void setTemp(const uint8_t temp, const bool remember = true); - uint8_t getTemp(); + uint8_t getTemp(void); void setFan(const uint8_t fan); - uint8_t getFan(); + uint8_t getFan(void); void setMode(const uint8_t mode); - uint8_t getMode(); + uint8_t getMode(void); void setRaw(const uint8_t state[]); - uint8_t *getRaw(); + uint8_t *getRaw(void); static bool validChecksum(uint8_t *state, const uint16_t length = kPanasonicAcStateLength); static uint8_t calcChecksum(uint8_t *state, const uint16_t length = kPanasonicAcStateLength); - void setQuiet(const bool state); - bool getQuiet(); - void setPowerful(const bool state); - bool getPowerful(); + void setQuiet(const bool on); + bool getQuiet(void); + void setPowerful(const bool on); + bool getPowerful(void); void setModel(const panasonic_ac_remote_model_t model); - panasonic_ac_remote_model_t getModel(); + panasonic_ac_remote_model_t getModel(void); void setSwingVertical(const uint8_t elevation); - uint8_t getSwingVertical(); + uint8_t getSwingVertical(void); void setSwingHorizontal(const uint8_t direction); - uint8_t getSwingHorizontal(); + uint8_t getSwingHorizontal(void); static uint16_t encodeTime(const uint8_t hours, const uint8_t mins); - uint16_t getClock(); + uint16_t getClock(void); void setClock(const uint16_t mins_since_midnight); - uint16_t getOnTimer(); + uint16_t getOnTimer(void); void setOnTimer(const uint16_t mins_since_midnight, const bool enable = true); - void cancelOnTimer(); - bool isOnTimerEnabled(); - uint16_t getOffTimer(); + void cancelOnTimer(void); + bool isOnTimerEnabled(void); + uint16_t getOffTimer(void); void setOffTimer(const uint16_t mins_since_midnight, const bool enable = true); - void cancelOffTimer(); - bool isOffTimerEnabled(); + void cancelOffTimer(void); + bool isOffTimerEnabled(void); uint8_t convertMode(const stdAc::opmode_t mode); uint8_t convertFan(const stdAc::fanspeed_t speed); uint8_t convertSwingV(const stdAc::swingv_t position); uint8_t convertSwingH(const stdAc::swingh_t position); -#ifdef ARDUINO - String toString(); - static String timeToString(const uint16_t mins_since_midnight); -#else - std::string toString(); - static std::string timeToString(const uint16_t mins_since_midnight); -#endif + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + static stdAc::swingv_t toCommonSwingV(const uint8_t pos); + static stdAc::swingh_t toCommonSwingH(const uint8_t pos); + stdAc::state_t toCommon(void); + String toString(void); #ifndef UNIT_TEST private: diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Pioneer.cpp b/lib/IRremoteESP8266-2.6.5/src/ir_Pioneer.cpp similarity index 55% rename from lib/IRremoteESP8266-2.6.0/src/ir_Pioneer.cpp rename to lib/IRremoteESP8266-2.6.5/src/ir_Pioneer.cpp index 9134e3696..490ea9440 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Pioneer.cpp +++ b/lib/IRremoteESP8266-2.6.5/src/ir_Pioneer.cpp @@ -1,6 +1,9 @@ // Copyright 2009 Ken Shirriff // Copyright 2017, 2018 David Conran // Copyright 2018 Kamil Palczewski +// Copyright 2019 s-hadinger + +// Pioneer remote emulation #define __STDC_LIMIT_MACROS #include @@ -8,16 +11,26 @@ #include "IRrecv.h" #include "IRsend.h" #include "IRutils.h" -#include "ir_NEC.h" - -// PPPP III OOO N N EEEE EEEE RRRR -// P P I O O NN N E E R R -// PPPP I O O N N N EEE EEE RRRR -// P I O O N NN E E R R -// P III OOO N N EEEE EEEE R RR +// Constants // Ref: -// http://adrian-kingston.com/IRFormatPioneer.htm +// http://www.adrian-kingston.com/IRFormatPioneer.htm +const uint16_t kPioneerTick = 534; +const uint16_t kPioneerHdrMarkTicks = 16; +const uint16_t kPioneerHdrMark = kPioneerHdrMarkTicks * kPioneerTick; +const uint16_t kPioneerHdrSpaceTicks = 8; +const uint16_t kPioneerHdrSpace = kPioneerHdrSpaceTicks * kPioneerTick; +const uint16_t kPioneerBitMarkTicks = 1; +const uint16_t kPioneerBitMark = kPioneerBitMarkTicks * kPioneerTick; +const uint16_t kPioneerOneSpaceTicks = 3; +const uint16_t kPioneerOneSpace = kPioneerOneSpaceTicks * kPioneerTick; +const uint16_t kPioneerZeroSpaceTicks = 1; +const uint16_t kPioneerZeroSpace = kPioneerZeroSpaceTicks * kPioneerTick; +const uint16_t kPioneerMinCommandLengthTicks = 159; +const uint32_t kPioneerMinCommandLength = kPioneerMinCommandLengthTicks * + kPioneerTick; +const uint16_t kPioneerMinGapTicks = 47; +const uint32_t kPioneerMinGap = kPioneerMinGapTicks * kPioneerTick; #if SEND_PIONEER // Send a raw Pioneer formatted message. @@ -34,13 +47,25 @@ // http://adrian-kingston.com/IRFormatPioneer.htm void IRsend::sendPioneer(const uint64_t data, const uint16_t nbits, const uint16_t repeat) { - // If nbits is to big, or is odd, abort. - if (nbits > sizeof(data) * 8 || nbits % 2 == 1) return; - - // send 1st part of the code - sendNEC(data >> (nbits / 2), nbits / 2, 0); - // send 2nd part of the code - sendNEC(data & (((uint64_t)1 << (nbits / 2)) - 1), nbits / 2, repeat); + // If nbits is to big, abort. + if (nbits > sizeof(data) * 8) return; + for (uint16_t r = 0; r <= repeat; r++) { + // don't use NEC repeat but repeat the whole sequence + if (nbits > 32) { + sendGeneric(kPioneerHdrMark, kPioneerHdrSpace, + kPioneerBitMark, kPioneerOneSpace, + kPioneerBitMark, kPioneerZeroSpace, + kPioneerBitMark, kPioneerMinGap, + kPioneerMinCommandLength, + data >> 32, nbits - 32, 40, true, 0, 33); + } + sendGeneric(kPioneerHdrMark, kPioneerHdrSpace, + kPioneerBitMark, kPioneerOneSpace, + kPioneerBitMark, kPioneerZeroSpace, + kPioneerBitMark, kPioneerMinGap, + kPioneerMinCommandLength, + data, nbits > 32 ? 32 : nbits, 40, true, 0, 33); + } } // Calculate the raw Pioneer data code based on two NEC sub-codes @@ -87,28 +112,22 @@ bool IRrecv::decodePioneer(decode_results *results, const uint16_t nbits, uint64_t data = 0; uint16_t offset = kStartOffset; - + results->value = 0; for (uint16_t section = 0; section < 2; section++) { - // Header - if (!matchMark(results->rawbuf[offset], kNecHdrMark)) return false; - // Calculate how long the lowest tick time is based on the header mark. - uint32_t mark_tick = - results->rawbuf[offset++] * kRawTick / kNecHdrMarkTicks; - if (!matchSpace(results->rawbuf[offset], kNecHdrSpace)) return false; - // Calculate how long the common tick time is based on the header space. - uint32_t space_tick = - results->rawbuf[offset++] * kRawTick / kNecHdrSpaceTicks; - // - // Data - match_result_t data_result = matchData( - &(results->rawbuf[offset]), nbits / 2, kNecBitMarkTicks * mark_tick, - kNecOneSpaceTicks * space_tick, kNecBitMarkTicks * mark_tick, - kNecZeroSpaceTicks * space_tick); - if (data_result.success == false) return false; - uint8_t command = data_result.data >> 8; - uint8_t command_inverted = data_result.data; - uint8_t address = data_result.data >> 24; - uint8_t address_inverted = data_result.data >> 16; + // Match Header + Data + Footer + uint16_t used; + used = matchGeneric(results->rawbuf + offset, &data, + results->rawlen - offset, nbits / 2, + kPioneerHdrMark, kPioneerHdrSpace, + kPioneerBitMark, kPioneerOneSpace, + kPioneerBitMark, kPioneerZeroSpace, + kPioneerBitMark, kPioneerMinGap, true); + if (!used) return false; + offset += used; + uint8_t command = data >> 8; + uint8_t command_inverted = data; + uint8_t address = data >> 24; + uint8_t address_inverted = data >> 16; // Compliance if (strict) { if (command != (command_inverted ^ 0xFF)) @@ -116,8 +135,7 @@ bool IRrecv::decodePioneer(decode_results *results, const uint16_t nbits, if (address != (address_inverted ^ 0xFF)) return false; // Address integrity failed. } - data = (data << (nbits / 2)) + data_result.data; - offset += data_result.used; + results->value = (results->value << (nbits / 2)) + data; // NEC-like commands and addresses are technically in LSB first order so the // final versions have to be reversed. uint16_t code = reverseBits((command << 8) + address, 16); @@ -125,18 +143,10 @@ bool IRrecv::decodePioneer(decode_results *results, const uint16_t nbits, results->command = code; else results->address = code; - - // Footer - if (!matchMark(results->rawbuf[offset++], kNecBitMarkTicks * mark_tick)) - return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset++], kNecMinGapTicks * space_tick)) - return false; } // Success results->bits = nbits; - results->value = data; results->decode_type = PIONEER; return true; } diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Pronto.cpp b/lib/IRremoteESP8266-2.6.5/src/ir_Pronto.cpp similarity index 92% rename from lib/IRremoteESP8266-2.6.0/src/ir_Pronto.cpp rename to lib/IRremoteESP8266-2.6.5/src/ir_Pronto.cpp index 9ab5c76d0..a408afed5 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Pronto.cpp +++ b/lib/IRremoteESP8266-2.6.5/src/ir_Pronto.cpp @@ -1,14 +1,10 @@ // Copyright 2017 David Conran +// Pronto code message generation + #include #include "IRsend.h" -// PPPPPP tt -// PP PP rr rr oooo nn nnn tt oooo -// PPPPPP rrr r oo oo nnn nn tttt oo oo -// PP rr oo oo nn nn tt oo oo -// PP rr oooo nn nn tttt oooo - // Constants const float kProntoFreqFactor = 0.241246; const uint16_t kProntoTypeOffset = 0; diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_RC5_RC6.cpp b/lib/IRremoteESP8266-2.6.5/src/ir_RC5_RC6.cpp similarity index 97% rename from lib/IRremoteESP8266-2.6.0/src/ir_RC5_RC6.cpp rename to lib/IRremoteESP8266-2.6.5/src/ir_RC5_RC6.cpp index ef1500d60..b79416692 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_RC5_RC6.cpp +++ b/lib/IRremoteESP8266-2.6.5/src/ir_RC5_RC6.cpp @@ -1,21 +1,15 @@ // Copyright 2009 Ken Shirriff // Copyright 2017 David Conran +// RC-5 & RC-6 support added from https://github.com/z3t0/Arduino-IRremote +// RC-5X support added by David Conran + #include #include "IRrecv.h" #include "IRsend.h" #include "IRtimer.h" #include "IRutils.h" -// RRRRRR CCCCC 555555 XX XX RRRRRR CCCCC 666 -// RR RR CC C 55 XX XX RR RR CC C 66 -// RRRRRR CC _____ 555555 XXXX RRRRRR CC _____ 666666 -// RR RR CC C 5555 XX XX RR RR CC C 66 66 -// RR RR CCCCC 555555 XX XX RR RR CCCCC 66666 - -// RC-5 & RC-6 support added from https://github.com/z3t0/Arduino-IRremote -// RC-5X support added by David Conran - // Constants // RC-5/RC-5X // Ref: diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_RCMM.cpp b/lib/IRremoteESP8266-2.6.5/src/ir_RCMM.cpp similarity index 93% rename from lib/IRremoteESP8266-2.6.0/src/ir_RCMM.cpp rename to lib/IRremoteESP8266-2.6.5/src/ir_RCMM.cpp index 1b03d2c07..4e8f43891 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_RCMM.cpp +++ b/lib/IRremoteESP8266-2.6.5/src/ir_RCMM.cpp @@ -1,19 +1,16 @@ // Copyright 2017 David Conran +// Send & decode support for Phillips RC-MM added by David Conran + +// Supports: +// Brand: Microsoft, Model: XBOX 360 + #include #include "IRrecv.h" #include "IRsend.h" #include "IRtimer.h" #include "IRutils.h" -// RRRRRR CCCCC MM MM MM MM -// RR RR CC C MMM MMM MMM MMM -// RRRRRR CC _____ MM MM MM MM MM MM -// RR RR CC C MM MM MM MM -// RR RR CCCCC MM MM MM MM - -// Send & decode support for RC-MM added by David Conran - // Constants // Ref: // http://www.sbprojects.com/knowledge/ir/rcmm.php @@ -144,11 +141,9 @@ bool IRrecv::decodeRCMM(decode_results *results, uint16_t nbits, bool strict) { data <<= 2; // Use non-default tolerance & excess for matching some of the spaces as the // defaults are too generous and causes mis-matches in some cases. - if (match(results->rawbuf[offset], kRcmmBitSpace0Ticks * s_tick, - kTolerance)) + if (match(results->rawbuf[offset], kRcmmBitSpace0Ticks * s_tick)) data += 0; - else if (match(results->rawbuf[offset], kRcmmBitSpace1Ticks * s_tick, - kTolerance)) + else if (match(results->rawbuf[offset], kRcmmBitSpace1Ticks * s_tick)) data += 1; else if (match(results->rawbuf[offset], kRcmmBitSpace2Ticks * s_tick, kRcmmTolerance)) diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Samsung.cpp b/lib/IRremoteESP8266-2.6.5/src/ir_Samsung.cpp similarity index 64% rename from lib/IRremoteESP8266-2.6.0/src/ir_Samsung.cpp rename to lib/IRremoteESP8266-2.6.5/src/ir_Samsung.cpp index 7e54d17df..77985b1c4 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Samsung.cpp +++ b/lib/IRremoteESP8266-2.6.5/src/ir_Samsung.cpp @@ -1,6 +1,8 @@ // Copyright 2009 Ken Shirriff // Copyright 2017, 2018, 2019 David Conran +// Samsung remote emulation + #include "ir_Samsung.h" #include #ifndef ARDUINO @@ -10,12 +12,6 @@ #include "IRsend.h" #include "IRutils.h" -// SSSS AAA MMM SSSS U U N N GGGG -// S A A M M M S U U NN N G -// SSS AAAAA M M M SSS U U N N N G GG -// S A A M M S U U N NN G G -// SSSS A A M M SSSS UUU N N GGG - // Samsung originally added from https://github.com/shirriff/Arduino-IRremote/ // Constants @@ -54,6 +50,13 @@ const uint16_t kSamsungAcBitMark = 586; const uint16_t kSamsungAcOneSpace = 1432; const uint16_t kSamsungAcZeroSpace = 436; +using irutils::addBoolToString; +using irutils::addFanToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addTempToString; + #if SEND_SAMSUNG // Send a Samsung formatted message. // Samsung has a separate message to indicate a repeat, like NEC does. @@ -68,7 +71,8 @@ const uint16_t kSamsungAcZeroSpace = 436; // Status: BETA / Should be working. // // Ref: http://elektrolab.wz.cz/katalog/samsung_protocol.pdf -void IRsend::sendSAMSUNG(uint64_t data, uint16_t nbits, uint16_t repeat) { +void IRsend::sendSAMSUNG(const uint64_t data, const uint16_t nbits, + const uint16_t repeat) { sendGeneric(kSamsungHdrMark, kSamsungHdrSpace, kSamsungBitMark, kSamsungOneSpace, kSamsungBitMark, kSamsungZeroSpace, kSamsungBitMark, kSamsungMinGap, kSamsungMinMessageLength, data, @@ -85,11 +89,11 @@ void IRsend::sendSAMSUNG(uint64_t data, uint16_t nbits, uint16_t repeat) { // A raw 32-bit Samsung message suitable for sendSAMSUNG(). // // Status: BETA / Should be working. -uint32_t IRsend::encodeSAMSUNG(uint8_t customer, uint8_t command) { - customer = reverseBits(customer, sizeof(customer) * 8); - command = reverseBits(command, sizeof(command) * 8); - return ((command ^ 0xFF) | (command << 8) | (customer << 16) | - (customer << 24)); +uint32_t IRsend::encodeSAMSUNG(const uint8_t customer, const uint8_t command) { + uint8_t revcustomer = reverseBits(customer, sizeof(customer) * 8); + uint8_t revcommand = reverseBits(command, sizeof(command) * 8); + return ((revcommand ^ 0xFF) | (revcommand << 8) | (revcustomer << 16) | + (revcustomer << 24)); } #endif @@ -113,8 +117,8 @@ uint32_t IRsend::encodeSAMSUNG(uint8_t customer, uint8_t command) { // They differ on their compliance criteria and how they repeat. // Ref: // http://elektrolab.wz.cz/katalog/samsung_protocol.pdf -bool IRrecv::decodeSAMSUNG(decode_results *results, uint16_t nbits, - bool strict) { +bool IRrecv::decodeSAMSUNG(decode_results *results, const uint16_t nbits, + const bool strict) { if (results->rawlen < 2 * nbits + kHeader + kFooter - 1) return false; // Can't possibly be a valid Samsung message. if (strict && nbits != kSamsungBits) @@ -123,31 +127,14 @@ bool IRrecv::decodeSAMSUNG(decode_results *results, uint16_t nbits, uint64_t data = 0; uint16_t offset = kStartOffset; - // Header - if (!matchMark(results->rawbuf[offset], kSamsungHdrMark)) return false; - // Calculate how long the common tick time is based on the header mark. - uint32_t m_tick = results->rawbuf[offset++] * kRawTick / kSamsungHdrMarkTicks; - if (!matchSpace(results->rawbuf[offset], kSamsungHdrSpace)) return false; - // Calculate how long the common tick time is based on the header space. - uint32_t s_tick = - results->rawbuf[offset++] * kRawTick / kSamsungHdrSpaceTicks; - // Data - match_result_t data_result = - matchData(&(results->rawbuf[offset]), nbits, - kSamsungBitMarkTicks * m_tick, kSamsungOneSpaceTicks * s_tick, - kSamsungBitMarkTicks * m_tick, kSamsungZeroSpaceTicks * s_tick); - if (data_result.success == false) return false; - data = data_result.data; - offset += data_result.used; - // Footer - if (!matchMark(results->rawbuf[offset++], kSamsungBitMarkTicks * m_tick)) - return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset], kSamsungMinGapTicks * s_tick)) - return false; - + // Match Header + Data + Footer + if (!matchGeneric(results->rawbuf + offset, &data, + results->rawlen - offset, nbits, + kSamsungHdrMark, kSamsungHdrSpace, + kSamsungBitMark, kSamsungOneSpace, + kSamsungBitMark, kSamsungZeroSpace, + kSamsungBitMark, kSamsungMinGap, true)) return false; // Compliance - // According to the spec, the customer (address) code is the first 8 // transmitted bits. It's then repeated. Check for that. uint8_t address = data >> 24; @@ -182,7 +169,7 @@ bool IRrecv::decodeSAMSUNG(decode_results *results, uint16_t nbits, // Protocol is used by Samsung Bluray Remote: ak59-00167a // // Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/621 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/621 void IRsend::sendSamsung36(const uint64_t data, const uint16_t nbits, const uint16_t repeat) { if (nbits < 16) return; // To small to send. @@ -222,7 +209,7 @@ void IRsend::sendSamsung36(const uint64_t data, const uint16_t nbits, // Protocol is used by Samsung Bluray Remote: ak59-00167a // // Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/621 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/621 bool IRrecv::decodeSamsung36(decode_results *results, const uint16_t nbits, const bool strict) { if (results->rawlen < 2 * nbits + kHeader + kFooter * 2 - 1) @@ -235,52 +222,29 @@ bool IRrecv::decodeSamsung36(decode_results *results, const uint16_t nbits, uint64_t data = 0; uint16_t offset = kStartOffset; - // Header - if (!matchMark(results->rawbuf[offset], kSamsungHdrMark)) return false; - // Calculate how long the common tick time is based on the header mark. - uint32_t m_tick = results->rawbuf[offset++] * kRawTick / kSamsungHdrMarkTicks; - if (!matchSpace(results->rawbuf[offset], kSamsungHdrSpace)) return false; - // Calculate how long the common tick time is based on the header space. - uint32_t s_tick = - results->rawbuf[offset++] * kRawTick / kSamsungHdrSpaceTicks; - // Data (Block #1) - match_result_t data_result = - matchData(&(results->rawbuf[offset]), 16, - kSamsungBitMarkTicks * m_tick, kSamsungOneSpaceTicks * s_tick, - kSamsungBitMarkTicks * m_tick, kSamsungZeroSpaceTicks * s_tick); - if (data_result.success == false) return false; - data = data_result.data; - offset += data_result.used; - uint16_t bitsSoFar = data_result.used / 2; - // Footer (Block #1) - if (!matchMark(results->rawbuf[offset++], kSamsungBitMarkTicks * m_tick)) - return false; - if (!matchSpace(results->rawbuf[offset++], kSamsungHdrSpaceTicks * s_tick)) - return false; + // Match Header + Data + Footer + uint16_t used; + used = matchGeneric(results->rawbuf + offset, &data, + results->rawlen - offset, 16, + kSamsungHdrMark, kSamsungHdrSpace, + kSamsungBitMark, kSamsungOneSpace, + kSamsungBitMark, kSamsungZeroSpace, + kSamsungBitMark, kSamsungHdrSpace, false); + if (!used) return false; + offset += used; // Data (Block #2) - data_result = matchData(&(results->rawbuf[offset]), - nbits - 16, - kSamsungBitMarkTicks * m_tick, - kSamsungOneSpaceTicks * s_tick, - kSamsungBitMarkTicks * m_tick, - kSamsungZeroSpaceTicks * s_tick); - if (data_result.success == false) return false; + uint64_t data2 = 0; + if (!matchGeneric(results->rawbuf + offset, &data2, + results->rawlen - offset, nbits - 16, + 0, 0, + kSamsungBitMark, kSamsungOneSpace, + kSamsungBitMark, kSamsungZeroSpace, + kSamsungBitMark, kSamsungMinGap, true)) return false; data <<= (nbits - 16); - data += data_result.data; - offset += data_result.used; - bitsSoFar += data_result.used / 2; - // Footer (Block #2) - if (!matchMark(results->rawbuf[offset++], kSamsungBitMarkTicks * m_tick)) - return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset], kSamsungMinGapTicks * s_tick)) - return false; - - // Compliance - if (nbits != bitsSoFar) return false; + data += data2; // Success - results->bits = bitsSoFar; + results->bits = nbits; results->value = data; results->decode_type = SAMSUNG36; results->command = data & ((1ULL << (nbits - 16)) - 1); @@ -297,10 +261,10 @@ bool IRrecv::decodeSamsung36(decode_results *results, const uint16_t nbits, // nbytes: Nr. of bytes of data in the array. (>=kSamsungAcStateLength) // repeat: Nr. of times the message is to be repeated. (Default = 0). // -// Status: ALPHA / Untested. +// Status: Stable / Known working. // // Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/505 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/505 void IRsend::sendSamsungAC(const uint8_t data[], const uint16_t nbytes, const uint16_t repeat) { if (nbytes < kSamsungAcStateLength && nbytes % kSamsungACSectionLength) @@ -326,9 +290,13 @@ void IRsend::sendSamsungAC(const uint8_t data[], const uint16_t nbytes, } #endif // SEND_SAMSUNG_AC -IRSamsungAc::IRSamsungAc(uint16_t pin) : _irsend(pin) { stateReset(); } +IRSamsungAc::IRSamsungAc(const uint16_t pin, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { + this->stateReset(); +} -void IRSamsungAc::stateReset() { +void IRSamsungAc::stateReset(void) { for (uint8_t i = 0; i < kSamsungAcExtendedStateLength; i++) remote_state[i] = 0x0; remote_state[0] = 0x02; @@ -341,9 +309,10 @@ void IRSamsungAc::stateReset() { remote_state[10] = 0x71; remote_state[12] = 0x15; remote_state[13] = 0xF0; + _sendpower = false; } -void IRSamsungAc::begin() { _irsend.begin(); } +void IRSamsungAc::begin(void) { _irsend.begin(); } uint8_t IRSamsungAc::calcChecksum(const uint8_t state[], const uint16_t length) { @@ -365,25 +334,35 @@ bool IRSamsungAc::validChecksum(const uint8_t state[], const uint16_t length) { return true; // No checksum to compare with. Assume okay. uint8_t offset = 0; if (length >= kSamsungAcExtendedStateLength) offset = 7; - return ((state[length - 6] >> 4) == calcChecksum(state, length) && - (state[length - (13 + offset)] >> 4) == calcChecksum(state, length - - (7 + offset))); + return ((state[length - 6] >> 4) == IRSamsungAc::calcChecksum(state, length) + && (state[length - (13 + offset)] >> 4) == IRSamsungAc::calcChecksum( + state, length - (7 + offset))); } // Update the checksum for the internal state. void IRSamsungAc::checksum(uint16_t length) { if (length < 13) return; remote_state[length - 6] &= 0x0F; - remote_state[length - 6] |= (calcChecksum(remote_state, length) << 4); + remote_state[length - 6] |= (this->calcChecksum(remote_state, length) << 4); remote_state[length - 13] &= 0x0F; - remote_state[length - 13] |= (calcChecksum(remote_state, length - 7) << 4); + remote_state[length - 13] |= (this->calcChecksum(remote_state, + length - 7) << 4); } #if SEND_SAMSUNG_AC // Use for most function/mode/settings changes to the unit. // i.e. When the device is already running. void IRSamsungAc::send(const uint16_t repeat, const bool calcchecksum) { - if (calcchecksum) checksum(); + if (calcchecksum) this->checksum(); + if (_sendpower) { // Do we need to send a the special power on/off message? + _sendpower = false; // It will now been sent. + if (this->getPower()) { + this->sendOn(); + } else { + this->sendOff(); + return; // No point sending anything else if we are turning the unit off. + } + } _irsend.sendSamsungAC(remote_state, kSamsungAcStateLength, repeat); } @@ -391,7 +370,7 @@ void IRSamsungAc::send(const uint16_t repeat, const bool calcchecksum) { // Samsung A/C requires an extended length message when you want to // change the power operating mode of the A/C unit. void IRSamsungAc::sendExtended(const uint16_t repeat, const bool calcchecksum) { - if (calcchecksum) checksum(); + if (calcchecksum) this->checksum(); uint8_t extended_state[kSamsungAcExtendedStateLength] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xD2, 0x0F, 0x00, 0x00, 0x00, 0x00, @@ -409,7 +388,7 @@ void IRSamsungAc::sendExtended(const uint16_t repeat, const bool calcchecksum) { // Send the special extended "On" message as the library can't seem to reproduce // this message automatically. -// See: https://github.com/markszabo/IRremoteESP8266/issues/604#issuecomment-475020036 +// See: https://github.com/crankyoldgit/IRremoteESP8266/issues/604#issuecomment-475020036 void IRSamsungAc::sendOn(const uint16_t repeat) { const uint8_t extended_state[21] = { 0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0, @@ -420,7 +399,7 @@ void IRSamsungAc::sendOn(const uint16_t repeat) { // Send the special extended "Off" message as the library can't seem to // reproduce this message automatically. -// See: https://github.com/markszabo/IRremoteESP8266/issues/604#issuecomment-475020036 +// See: https://github.com/crankyoldgit/IRremoteESP8266/issues/604#issuecomment-475020036 void IRSamsungAc::sendOff(const uint16_t repeat) { const uint8_t extended_state[21] = { 0x02, 0xB2, 0x0F, 0x00, 0x00, 0x00, 0xC0, @@ -430,8 +409,8 @@ void IRSamsungAc::sendOff(const uint16_t repeat) { } #endif // SEND_SAMSUNG_AC -uint8_t *IRSamsungAc::getRaw() { - checksum(); +uint8_t *IRSamsungAc::getRaw(void) { + this->checksum(); return remote_state; } @@ -446,26 +425,28 @@ void IRSamsungAc::setRaw(const uint8_t new_code[], const uint16_t length) { } } -void IRSamsungAc::on() { - remote_state[1] &= ~kSamsungAcPowerMask1; - remote_state[6] |= kSamsungAcPowerMask2; +void IRSamsungAc::on(void) { + remote_state[1] &= ~kSamsungAcPowerMask1; // Bit needs to be cleared. + remote_state[6] |= kSamsungAcPowerMask6; // Bit needs to be set. + _sendpower = true; // Flag that we need to send the special power message(s). } -void IRSamsungAc::off() { - remote_state[1] |= kSamsungAcPowerMask1; - remote_state[6] &= ~kSamsungAcPowerMask2; +void IRSamsungAc::off(void) { + remote_state[1] |= kSamsungAcPowerMask1; // Bit needs to be set. + remote_state[6] &= ~kSamsungAcPowerMask6; // Bit needs to be cleared. + _sendpower = true; // Flag that we need to send the special power message(s). } -void IRSamsungAc::setPower(const bool state) { - if (state) - on(); +void IRSamsungAc::setPower(const bool on) { + if (on) + this->on(); else - off(); + this->off(); } -bool IRSamsungAc::getPower() { - return ((remote_state[6] & kSamsungAcPowerMask2) != 0) && - ((remote_state[1] & kSamsungAcPowerMask1) == 0); +bool IRSamsungAc::getPower(void) { + return (remote_state[6] & kSamsungAcPowerMask6) && + !(remote_state[1] & kSamsungAcPowerMask1); } // Set the temp. in deg C @@ -477,7 +458,7 @@ void IRSamsungAc::setTemp(const uint8_t temp) { } // Return the set temp. in deg C -uint8_t IRSamsungAc::getTemp() { +uint8_t IRSamsungAc::getTemp(void) { return ((remote_state[11] & kSamsungAcTempMask) >> 4) + kSamsungAcMinTemp; } @@ -489,14 +470,15 @@ void IRSamsungAc::setMode(const uint8_t mode) { // Auto mode has a special fan setting valid only in auto mode. if (newmode == kSamsungAcAuto) { - setFan(kSamsungAcFanAuto2); + this->setFan(kSamsungAcFanAuto2); } else { - if (getFan() == kSamsungAcFanAuto2) // Non-Auto can't have this fan setting - setFan(kSamsungAcFanAuto); // Default to something safe. + // Non-Auto can't have this fan setting + if (this->getFan() == kSamsungAcFanAuto2) + this->setFan(kSamsungAcFanAuto); // Default to something safe. } } -uint8_t IRSamsungAc::getMode() { +uint8_t IRSamsungAc::getMode(void) { return (remote_state[12] & kSamsungAcModeMask) >> 4; } @@ -507,10 +489,10 @@ void IRSamsungAc::setFan(const uint8_t speed) { case kSamsungAcFanMed: case kSamsungAcFanHigh: case kSamsungAcFanTurbo: - if (getMode() == kSamsungAcAuto) return; // Not valid in Auto mode. + if (this->getMode() == kSamsungAcAuto) return; // Not valid in Auto mode. break; case kSamsungAcFanAuto2: // Special fan setting for when in Auto mode. - if (getMode() != kSamsungAcAuto) return; + if (this->getMode() != kSamsungAcAuto) return; break; default: return; @@ -518,42 +500,44 @@ void IRSamsungAc::setFan(const uint8_t speed) { remote_state[12] = (remote_state[12] & ~kSamsungAcFanMask) | (speed << 1); } -uint8_t IRSamsungAc::getFan() { +uint8_t IRSamsungAc::getFan(void) { return ((remote_state[12] & kSamsungAcFanMask) >> 1); } -bool IRSamsungAc::getSwing() { +bool IRSamsungAc::getSwing(void) { // TODO(Hollako): Explain why sometimes the LSB of remote_state[9] is a 1. // e.g. 0xAE or 0XAF for swing move. return ((remote_state[9] & kSamsungAcSwingMask) >> 4) == kSamsungAcSwingMove; } -void IRSamsungAc::setSwing(const bool state) { +void IRSamsungAc::setSwing(const bool on) { // TODO(Hollako): Explain why sometimes the LSB of remote_state[9] is a 1. // e.g. 0xAE or 0XAF for swing move. remote_state[9] &= ~kSamsungAcSwingMask; // Clear the previous swing state. - if (state) + if (on) remote_state[9] |= (kSamsungAcSwingMove << 4); else remote_state[9] |= (kSamsungAcSwingStop << 4); } -bool IRSamsungAc::getBeep() { return remote_state[13] & kSamsungAcBeepMask; } +bool IRSamsungAc::getBeep(void) { + return remote_state[13] & kSamsungAcBeepMask; +} -void IRSamsungAc::setBeep(const bool state) { - if (state) +void IRSamsungAc::setBeep(const bool on) { + if (on) remote_state[13] |= kSamsungAcBeepMask; else remote_state[13] &= ~kSamsungAcBeepMask; } -bool IRSamsungAc::getClean() { +bool IRSamsungAc::getClean(void) { return (remote_state[10] & kSamsungAcCleanMask10) && (remote_state[11] & kSamsungAcCleanMask11); } -void IRSamsungAc::setClean(const bool state) { - if (state) { +void IRSamsungAc::setClean(const bool on) { + if (on) { remote_state[10] |= kSamsungAcCleanMask10; remote_state[11] |= kSamsungAcCleanMask11; } else { @@ -562,18 +546,42 @@ void IRSamsungAc::setClean(const bool state) { } } -// Very unsure this is correct. -bool IRSamsungAc::getQuiet() { - return remote_state[11] & kSamsungAcQuietMask11; +bool IRSamsungAc::getQuiet(void) { + return !(remote_state[1] & kSamsungAcQuietMask1) && + (remote_state[5] & kSamsungAcQuietMask5); } -// Very unsure this is correct. -void IRSamsungAc::setQuiet(const bool state) { - if (state) { - remote_state[11] |= kSamsungAcQuietMask11; - setFan(kSamsungAcFanAuto); // Quiet mode seems to set fan speed to auto. +void IRSamsungAc::setQuiet(const bool on) { + if (on) { + remote_state[1] &= ~kSamsungAcQuietMask1; // Bit needs to be cleared. + remote_state[5] |= kSamsungAcQuietMask5; // Bit needs to be set. + // Quiet mode seems to set fan speed to auto. + this->setFan(kSamsungAcFanAuto); + this->setPowerful(false); // Quiet 'on' is mutually exclusive to Powerful. } else { - remote_state[11] &= ~kSamsungAcQuietMask11; + remote_state[1] |= kSamsungAcQuietMask1; // Bit needs to be set. + remote_state[5] &= ~kSamsungAcQuietMask5; // Bit needs to be cleared. + } +} + +bool IRSamsungAc::getPowerful(void) { + return !(remote_state[8] & kSamsungAcPowerfulMask8) && + (remote_state[10] & kSamsungAcPowerfulMask10) && + this->getFan() == kSamsungAcFanTurbo; +} + +void IRSamsungAc::setPowerful(const bool on) { + if (on) { + remote_state[8] &= ~kSamsungAcPowerfulMask8; // Bit needs to be cleared. + remote_state[10] |= kSamsungAcPowerfulMask10; // Bit needs to be set. + // Powerful mode sets fan speed to Turbo. + this->setFan(kSamsungAcFanTurbo); + this->setQuiet(false); // Powerful 'on' is mutually exclusive to Quiet. + } else { + remote_state[8] |= kSamsungAcPowerfulMask8; // Bit needs to be set. + remote_state[10] &= ~kSamsungAcPowerfulMask10; // Bit needs to be cleared. + // Turning off Powerful mode sets fan speed to Auto if we were in Turbo mode + if (this->getFan() == kSamsungAcFanTurbo) this->setFan(kSamsungAcFanAuto); } } @@ -610,85 +618,90 @@ uint8_t IRSamsungAc::convertFan(const stdAc::fanspeed_t speed) { } } -// Convert the internal state into a human readable string. -#ifdef ARDUINO -String IRSamsungAc::toString() { - String result = ""; -#else -std::string IRSamsungAc::toString() { - std::string result = ""; -#endif // ARDUINO - result += F("Power: "); - if (getPower()) - result += F("On"); - else - result += F("Off"); - result += F(", Mode: "); - result += uint64ToString(getMode()); - switch (getMode()) { - case kSamsungAcAuto: - result += F(" (AUTO)"); - break; - case kSamsungAcCool: - result += F(" (COOL)"); - break; - case kSamsungAcHeat: - result += F(" (HEAT)"); - break; - case kSamsungAcDry: - result += F(" (DRY)"); - break; - case kSamsungAcFan: - result += F(" (FAN)"); - break; - default: - result += F(" (UNKNOWN)"); +// Convert a native mode to it's common equivalent. +stdAc::opmode_t IRSamsungAc::toCommonMode(const uint8_t mode) { + switch (mode) { + case kSamsungAcCool: return stdAc::opmode_t::kCool; + case kSamsungAcHeat: return stdAc::opmode_t::kHeat; + case kSamsungAcDry: return stdAc::opmode_t::kDry; + case kSamsungAcFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; } - result += F(", Temp: "); - result += uint64ToString(getTemp()); - result += F("C, Fan: "); - result += uint64ToString(getFan()); +} + +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRSamsungAc::toCommonFanSpeed(const uint8_t spd) { + switch (spd) { + case kSamsungAcFanTurbo: return stdAc::fanspeed_t::kMax; + case kSamsungAcFanHigh: return stdAc::fanspeed_t::kHigh; + case kSamsungAcFanMed: return stdAc::fanspeed_t::kMedium; + case kSamsungAcFanLow: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRSamsungAc::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::SAMSUNG_AC; + result.model = -1; // Not supported. + result.power = this->getPower(); + result.mode = this->toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(); + result.fanspeed = this->toCommonFanSpeed(this->getFan()); + result.swingv = this->getSwing() ? stdAc::swingv_t::kAuto : + stdAc::swingv_t::kOff; + result.quiet = this->getQuiet(); + result.turbo = this->getPowerful(); + result.clean = this->getClean(); + result.beep = this->getBeep(); + // Not supported. + result.swingh = stdAc::swingh_t::kOff; + result.econo = false; + result.filter = false; + result.light = false; + result.sleep = -1; + result.clock = -1; + return result; +} + +// Convert the internal state into a human readable string. +String IRSamsungAc::toString(void) { + String result = ""; + result.reserve(100); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(getPower(), F("Power"), false); + result += addModeToString(getMode(), kSamsungAcAuto, kSamsungAcCool, + kSamsungAcHeat, kSamsungAcDry, + kSamsungAcFan); + result += addTempToString(getTemp()); + result += addIntToString(getFan(), F("Fan")); switch (getFan()) { case kSamsungAcFanAuto: case kSamsungAcFanAuto2: - result += F(" (AUTO)"); + result += F(" (Auto)"); break; case kSamsungAcFanLow: - result += F(" (LOW)"); + result += F(" (Low)"); break; case kSamsungAcFanMed: - result += F(" (MED)"); + result += F(" (Medium)"); break; case kSamsungAcFanHigh: - result += F(" (HIGH)"); + result += F(" (High)"); break; case kSamsungAcFanTurbo: - result += F(" (TURBO)"); + result += F(" (Turbo)"); break; default: result += F(" (UNKNOWN)"); break; } - result += F(", Swing: "); - if (getSwing()) - result += F("On"); - else - result += F("Off"); - result += F(", Beep: "); - if (getBeep()) - result += F("On"); - else - result += F("Off"); - result += F(", Clean: "); - if (getBeep()) - result += F("On"); - else - result += F("Off"); - result += F(", Quiet: "); - if (getQuiet()) - result += F("On"); - else - result += F("Off"); + result += addBoolToString(getSwing(), F("Swing")); + result += addBoolToString(getBeep(), F("Beep")); + result += addBoolToString(getClean(), F("Clean")); + result += addBoolToString(getQuiet(), F("Quiet")); + result += addBoolToString(getPowerful(), F("Powerful")); return result; } @@ -702,62 +715,38 @@ std::string IRSamsungAc::toString() { // Returns: // boolean: True if it can decode it, false if it can't. // -// Status: BETA / Appears to mostly work. +// Status: Stable / Known to be working. // // Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/505 -bool IRrecv::decodeSamsungAC(decode_results *results, uint16_t nbits, - bool strict) { +// https://github.com/crankyoldgit/IRremoteESP8266/issues/505 +bool IRrecv::decodeSamsungAC(decode_results *results, const uint16_t nbits, + const bool strict) { if (results->rawlen < 2 * nbits + kHeader * 3 + kFooter * 2 - 1) return false; // Can't possibly be a valid Samsung A/C message. if (nbits != kSamsungAcBits && nbits != kSamsungAcExtendedBits) return false; uint16_t offset = kStartOffset; - uint16_t dataBitsSoFar = 0; - match_result_t data_result; // Message Header if (!matchMark(results->rawbuf[offset++], kSamsungAcBitMark)) return false; if (!matchSpace(results->rawbuf[offset++], kSamsungAcHdrSpace)) return false; // Section(s) - for (uint16_t pos = kSamsungACSectionLength, i = 0; pos <= nbits / 8; + for (uint16_t pos = 0; pos <= (nbits / 8) - kSamsungACSectionLength; pos += kSamsungACSectionLength) { - uint64_t sectiondata = 0; - // Section Header - if (!matchMark(results->rawbuf[offset++], kSamsungAcSectionMark)) - return false; - if (!matchSpace(results->rawbuf[offset++], kSamsungAcSectionSpace)) - return false; - // Section Data - // Keep reading bytes until we either run out of section or state to fill. - for (; offset <= results->rawlen - 16 && i < pos; - i++, dataBitsSoFar += 8, offset += data_result.used) { - data_result = matchData(&(results->rawbuf[offset]), 8, kSamsungAcBitMark, - kSamsungAcOneSpace, kSamsungAcBitMark, - kSamsungAcZeroSpace, kTolerance, 0, false); - if (data_result.success == false) { - DPRINT("DEBUG: offset = "); - DPRINTLN(offset + data_result.used); - return false; // Fail - } - results->state[i] = data_result.data; - sectiondata = (sectiondata << 8) + data_result.data; - } - DPRINTLN("DEBUG: sectiondata = 0x" + uint64ToString(sectiondata, 16)); - // Section Footer - if (!matchMark(results->rawbuf[offset++], kSamsungAcBitMark)) return false; - if (pos < nbits / 8) { // Inter-section gap. - if (!matchSpace(results->rawbuf[offset++], kSamsungAcSectionGap)) - return false; - } else { // Last section / End of message gap. - if (offset <= results->rawlen && - !matchAtLeast(results->rawbuf[offset++], kSamsungAcSectionGap)) - return false; - } + uint16_t used; + // Section Header + Section Data (7 bytes) + Section Footer + used = matchGeneric(results->rawbuf + offset, results->state + pos, + results->rawlen - offset, kSamsungACSectionLength * 8, + kSamsungAcSectionMark, kSamsungAcSectionSpace, + kSamsungAcBitMark, kSamsungAcOneSpace, + kSamsungAcBitMark, kSamsungAcZeroSpace, + kSamsungAcBitMark, kSamsungAcSectionGap, + pos + kSamsungACSectionLength >= nbits / 8, + _tolerance, 0, false); + if (used == 0) return false; + offset += used; } // Compliance - // Re-check we got the correct size/length due to the way we read the data. - if (dataBitsSoFar != nbits) return false; // Is the signature correct? DPRINTLN("DEBUG: Checking signature."); if (results->state[0] != 0x02 || results->state[2] != 0x0F) return false; @@ -770,7 +759,7 @@ bool IRrecv::decodeSamsungAC(decode_results *results, uint16_t nbits, } // Success results->decode_type = SAMSUNG_AC; - results->bits = dataBitsSoFar; + results->bits = nbits; // No need to record the state as we stored it as we decoded it. // As we use result->state, we don't record value, address, or command as it // is a union data type. diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Samsung.h b/lib/IRremoteESP8266-2.6.5/src/ir_Samsung.h similarity index 53% rename from lib/IRremoteESP8266-2.6.0/src/ir_Samsung.h rename to lib/IRremoteESP8266-2.6.5/src/ir_Samsung.h index 9df427c6f..1a2be79bb 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Samsung.h +++ b/lib/IRremoteESP8266-2.6.5/src/ir_Samsung.h @@ -9,8 +9,6 @@ #include #ifndef UNIT_TEST #include -#else -#include #endif #include "IRremoteESP8266.h" #include "IRsend.h" @@ -18,14 +16,14 @@ #include "IRsend_test.h" #endif -// SSSS AAA MMM SSSS U U N N GGGG -// S A A M M M S U U NN N G -// SSS AAAAA M M M SSS U U N N N G GG -// S A A M M S U U N NN G G -// SSSS A A M M SSSS UUU N N GGG +// Supports: +// Brand: Samsung, Model: UA55H6300 TV +// Brand: Samsung, Model: IEC-R03 remote +// Brand: Samsung, Model: AR12KSFPEWQNET A/C +// Brand: Samsung, Model: AR12HSSDBWKNEU A/C // Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/505 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/505 // Constants const uint8_t kSamsungAcAuto = 0; @@ -33,7 +31,7 @@ const uint8_t kSamsungAcCool = 1; const uint8_t kSamsungAcDry = 2; const uint8_t kSamsungAcFan = 3; const uint8_t kSamsungAcHeat = 4; -const uint8_t kSamsungAcModeMask = 0x70; +const uint8_t kSamsungAcModeMask = 0x70; // 0b01110000 const uint8_t kSamsungAcFanAuto = 0; const uint8_t kSamsungAcFanLow = 2; const uint8_t kSamsungAcFanMed = 4; @@ -43,17 +41,20 @@ const uint8_t kSamsungAcFanTurbo = 7; const uint8_t kSamsungAcMinTemp = 16; // 16C const uint8_t kSamsungAcMaxTemp = 30; // 30C const uint8_t kSamsungAcAutoTemp = 25; // 25C -const uint8_t kSamsungAcTempMask = 0xF0; -const uint8_t kSamsungAcPowerMask1 = 0x20; -const uint8_t kSamsungAcPowerMask2 = 0x30; -const uint8_t kSamsungAcFanMask = 0x0E; -const uint8_t kSamsungAcSwingMask = 0x70; +const uint8_t kSamsungAcTempMask = 0xF0; // 0b11110000 +const uint8_t kSamsungAcPowerMask1 = 0x20; // 0b00100000 +const uint8_t kSamsungAcPowerMask6 = 0x30; // 0b00110000 +const uint8_t kSamsungAcFanMask = 0x0E; // 0b00001110 +const uint8_t kSamsungAcSwingMask = 0x70; // 0b01110000 const uint8_t kSamsungAcSwingMove = 0b010; const uint8_t kSamsungAcSwingStop = 0b111; -const uint8_t kSamsungAcBeepMask = 0x02; -const uint8_t kSamsungAcCleanMask10 = 0x80; -const uint8_t kSamsungAcCleanMask11 = 0x02; -const uint8_t kSamsungAcQuietMask11 = 0x01; +const uint8_t kSamsungAcBeepMask = 0x02; // 0b00000010 +const uint8_t kSamsungAcCleanMask10 = 0x80; // 0b10000000 +const uint8_t kSamsungAcCleanMask11 = 0x02; // 0b00000010 +const uint8_t kSamsungAcQuietMask1 = 0x10; // 0b00010000 +const uint8_t kSamsungAcQuietMask5 = 0x20; // 0b00100000 +const uint8_t kSamsungAcPowerfulMask8 = 0x50; // 0b01010000 +const uint8_t kSamsungAcPowerfulMask10 = 0x06; // 0b00000110 const uint16_t kSamsungACSectionLength = 7; const uint64_t kSamsungAcPowerSection = 0x1D20F00000000; @@ -61,9 +62,10 @@ const uint64_t kSamsungAcPowerSection = 0x1D20F00000000; // Classes class IRSamsungAc { public: - explicit IRSamsungAc(uint16_t pin); + explicit IRSamsungAc(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); - void stateReset(); + void stateReset(void); #if SEND_SAMSUNG_AC void send(const uint16_t repeat = kSamsungAcDefaultRepeat, const bool calcchecksum = true); @@ -71,27 +73,30 @@ class IRSamsungAc { const bool calcchecksum = true); void sendOn(const uint16_t repeat = kSamsungAcDefaultRepeat); void sendOff(const uint16_t repeat = kSamsungAcDefaultRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } #endif // SEND_SAMSUNG_AC - void begin(); - void on(); - void off(); - void setPower(const bool state); - bool getPower(); + void begin(void); + void on(void); + void off(void); + void setPower(const bool on); + bool getPower(void); void setTemp(const uint8_t temp); - uint8_t getTemp(); + uint8_t getTemp(void); void setFan(const uint8_t speed); - uint8_t getFan(); + uint8_t getFan(void); void setMode(const uint8_t mode); - uint8_t getMode(); - void setSwing(const bool state); - bool getSwing(); - void setBeep(const bool state); - bool getBeep(); - void setClean(const bool state); - bool getClean(); - void setQuiet(const bool state); - bool getQuiet(); - uint8_t* getRaw(); + uint8_t getMode(void); + void setSwing(const bool on); + bool getSwing(void); + void setBeep(const bool on); + bool getBeep(void); + void setClean(const bool on); + bool getClean(void); + void setQuiet(const bool on); + bool getQuiet(void); + void setPowerful(const bool on); + bool getPowerful(void); + uint8_t* getRaw(void); void setRaw(const uint8_t new_code[], const uint16_t length = kSamsungAcStateLength); static bool validChecksum(const uint8_t state[], @@ -100,11 +105,10 @@ class IRSamsungAc { const uint16_t length = kSamsungAcStateLength); uint8_t convertMode(const stdAc::opmode_t mode); uint8_t convertFan(const stdAc::fanspeed_t speed); -#ifdef ARDUINO - String toString(); -#else - std::string toString(); -#endif + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + stdAc::state_t toCommon(void); + String toString(void); #ifndef UNIT_TEST private: @@ -114,6 +118,7 @@ class IRSamsungAc { #endif // The state of the IR remote in IR code form. uint8_t remote_state[kSamsungAcExtendedStateLength]; + bool _sendpower; // Hack to know when we need to send a special power mesg. void checksum(const uint16_t length = kSamsungAcStateLength); }; diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Sanyo.cpp b/lib/IRremoteESP8266-2.6.5/src/ir_Sanyo.cpp similarity index 96% rename from lib/IRremoteESP8266-2.6.0/src/ir_Sanyo.cpp rename to lib/IRremoteESP8266-2.6.5/src/ir_Sanyo.cpp index b2b4d7830..b05e76766 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Sanyo.cpp +++ b/lib/IRremoteESP8266-2.6.5/src/ir_Sanyo.cpp @@ -6,12 +6,6 @@ #include "IRrecv.h" #include "IRsend.h" -// SSSS AAA N N Y Y OOO -// S A A NN N Y Y O O -// SSS AAAAA N N N Y O O -// S A A N NN Y O O -// SSSS A A N N Y OOO - // Sanyo SA 8650B originally added from: // https://github.com/shirriff/Arduino-IRremote/ // Sanyo LC7461 support originally by marcosamarinho diff --git a/lib/IRremoteESP8266-2.6.5/src/ir_Sharp.cpp b/lib/IRremoteESP8266-2.6.5/src/ir_Sharp.cpp new file mode 100644 index 000000000..250248554 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.5/src/ir_Sharp.cpp @@ -0,0 +1,557 @@ +// Copyright 2009 Ken Shirriff +// Copyright 2017, 2019 David Conran + +// Sharp remote emulation + +#include "ir_Sharp.h" +#include +#ifndef ARDUINO +#include +#endif +#include "IRrecv.h" +#include "IRsend.h" +#include "IRutils.h" + +// Equipment it seems compatible with: +// * Sharp LC-52D62U +// * +// + +// Constants +// period time = 1/38000Hz = 26.316 microseconds. +// Ref: +// GlobalCache's IR Control Tower data. +// http://www.sbprojects.com/knowledge/ir/sharp.php +const uint16_t kSharpTick = 26; +const uint16_t kSharpBitMarkTicks = 10; +const uint16_t kSharpBitMark = kSharpBitMarkTicks * kSharpTick; +const uint16_t kSharpOneSpaceTicks = 70; +const uint16_t kSharpOneSpace = kSharpOneSpaceTicks * kSharpTick; +const uint16_t kSharpZeroSpaceTicks = 30; +const uint16_t kSharpZeroSpace = kSharpZeroSpaceTicks * kSharpTick; +const uint16_t kSharpGapTicks = 1677; +const uint16_t kSharpGap = kSharpGapTicks * kSharpTick; +// Address(5) + Command(8) + Expansion(1) + Check(1) +const uint64_t kSharpToggleMask = + ((uint64_t)1 << (kSharpBits - kSharpAddressBits)) - 1; +const uint64_t kSharpAddressMask = ((uint64_t)1 << kSharpAddressBits) - 1; +const uint64_t kSharpCommandMask = ((uint64_t)1 << kSharpCommandBits) - 1; + +using irutils::addBoolToString; +using irutils::addFanToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addTempToString; + +#if (SEND_SHARP || SEND_DENON) +// Send a (raw) Sharp message +// +// Args: +// data: Contents of the message to be sent. +// nbits: Nr. of bits of data to be sent. Typically kSharpBits. +// repeat: Nr. of additional times the message is to be sent. +// +// Status: BETA / Previously working fine. +// +// Notes: +// This procedure handles the inversion of bits required per protocol. +// The protocol spec says to send the LSB first, but legacy code & usage +// has us sending the MSB first. Grrrr. Normal invocation of encodeSharp() +// handles this for you, assuming you are using the correct/standard values. +// e.g. sendSharpRaw(encodeSharp(address, command)); +// +// Ref: +// http://www.sbprojects.com/knowledge/ir/sharp.htm +// http://lirc.sourceforge.net/remotes/sharp/GA538WJSA +// http://www.mwftr.com/ucF08/LEC14%20PIC%20IR.pdf +// http://www.hifi-remote.com/johnsfine/DecodeIR.html#Sharp +void IRsend::sendSharpRaw(const uint64_t data, const uint16_t nbits, + const uint16_t repeat) { + uint64_t tempdata = data; + for (uint16_t i = 0; i <= repeat; i++) { + // Protocol demands that the data be sent twice; once normally, + // then with all but the address bits inverted. + // Note: Previously this used to be performed 3 times (normal, inverted, + // normal), however all data points to that being incorrect. + for (uint8_t n = 0; n < 2; n++) { + sendGeneric(0, 0, // No Header + kSharpBitMark, kSharpOneSpace, kSharpBitMark, kSharpZeroSpace, + kSharpBitMark, kSharpGap, tempdata, nbits, 38, true, + 0, // Repeats are handled already. + 33); + // Invert the data per protocol. This is always called twice, so it's + // retured to original upon exiting the inner loop. + tempdata ^= kSharpToggleMask; + } + } +} + +// Encode a (raw) Sharp message from it's components. +// +// Args: +// address: The value of the address to be sent. +// command: The value of the address to be sent. (8 bits) +// expansion: The value of the expansion bit to use. (0 or 1, typically 1) +// check: The value of the check bit to use. (0 or 1, typically 0) +// MSBfirst: Flag indicating MSB first or LSB first order. (Default: false) +// Returns: +// An uint32_t containing the raw Sharp message for sendSharpRaw(). +// +// Status: BETA / Should work okay. +// +// Notes: +// Assumes the standard Sharp bit sizes. +// Historically sendSharp() sends address & command in +// MSB first order. This is actually incorrect. It should be sent in LSB +// order. The behaviour of sendSharp() hasn't been changed to maintain +// backward compatibility. +// +// Ref: +// http://www.sbprojects.com/knowledge/ir/sharp.htm +// http://lirc.sourceforge.net/remotes/sharp/GA538WJSA +// http://www.mwftr.com/ucF08/LEC14%20PIC%20IR.pdf +uint32_t IRsend::encodeSharp(const uint16_t address, const uint16_t command, + const uint16_t expansion, const uint16_t check, + const bool MSBfirst) { + // Mask any unexpected bits. + uint16_t tempaddress = address & ((1 << kSharpAddressBits) - 1); + uint16_t tempcommand = command & ((1 << kSharpCommandBits) - 1); + uint16_t tempexpansion = expansion & 1; + uint16_t tempcheck = check & 1; + + if (!MSBfirst) { // Correct bit order if needed. + tempaddress = reverseBits(tempaddress, kSharpAddressBits); + tempcommand = reverseBits(tempcommand, kSharpCommandBits); + } + // Concatinate all the bits. + return (tempaddress << (kSharpCommandBits + 2)) | (tempcommand << 2) | + (tempexpansion << 1) | tempcheck; +} + +// Send a Sharp message +// +// Args: +// address: Address value to be sent. +// command: Command value to be sent. +// nbits: Nr. of bits of data to be sent. Typically kSharpBits. +// repeat: Nr. of additional times the message is to be sent. +// +// Status: DEPRICATED / Previously working fine. +// +// Notes: +// This procedure has a non-standard invocation style compared to similar +// sendProtocol() routines. This is due to legacy, compatibility, & historic +// reasons. Normally the calling syntax version is like sendSharpRaw(). +// This procedure transmits the address & command in MSB first order, which is +// incorrect. This behaviour is left as-is to maintain backward +// compatibility with legacy code. +// In short, you should use sendSharpRaw(), encodeSharp(), and the correct +// values of address & command instead of using this, & the wrong values. +// +// Ref: +// http://www.sbprojects.com/knowledge/ir/sharp.htm +// http://lirc.sourceforge.net/remotes/sharp/GA538WJSA +// http://www.mwftr.com/ucF08/LEC14%20PIC%20IR.pdf +void IRsend::sendSharp(const uint16_t address, uint16_t const command, + const uint16_t nbits, const uint16_t repeat) { + sendSharpRaw(encodeSharp(address, command, 1, 0, true), nbits, repeat); +} +#endif // (SEND_SHARP || SEND_DENON) + +#if (DECODE_SHARP || DECODE_DENON) +// Decode the supplied Sharp message. +// +// Args: +// results: Ptr to the data to decode and where to store the decode result. +// nbits: Nr. of data bits to expect. Typically kSharpBits. +// strict: Flag indicating if we should perform strict matching. +// expansion: Should we expect the expansion bit to be set. Default is true. +// Returns: +// boolean: True if it can decode it, false if it can't. +// +// Status: STABLE / Working fine. +// +// Note: +// This procedure returns a value suitable for use in sendSharpRaw(). +// TODO(crankyoldgit): Need to ensure capture of the inverted message as it can +// be missed due to the interrupt timeout used to detect an end of message. +// Several compliance checks are disabled until that is resolved. +// Ref: +// http://www.sbprojects.com/knowledge/ir/sharp.php +// http://www.mwftr.com/ucF08/LEC14%20PIC%20IR.pdf +// http://www.hifi-remote.com/johnsfine/DecodeIR.html#Sharp +bool IRrecv::decodeSharp(decode_results *results, const uint16_t nbits, + const bool strict, const bool expansion) { + if (results->rawlen < 2 * nbits + kFooter - 1) + return false; // Not enough entries to be a Sharp message. + // Compliance + if (strict) { + if (nbits != kSharpBits) return false; // Request is out of spec. + // DISABLED - See TODO +#ifdef UNIT_TEST + // An in spec message has the data sent normally, then inverted. So we + // expect twice as many entries than to just get the results. + if (results->rawlen < 2 * (2 * nbits + kFooter)) return false; +#endif + } + + uint64_t data = 0; + uint16_t offset = kStartOffset; + + // Match Data + Footer + uint16_t used; + used = matchGeneric(results->rawbuf + offset, &data, + results->rawlen - offset, nbits, + 0, 0, // No Header + kSharpBitMark, kSharpOneSpace, + kSharpBitMark, kSharpZeroSpace, + kSharpBitMark, kSharpGap, true, 35); + if (!used) return false; + offset += used; + // Compliance + if (strict) { + // Check the state of the expansion bit is what we expect. + if ((data & 0b10) >> 1 != expansion) return false; + // The check bit should be cleared in a normal message. + if (data & 0b1) return false; + // DISABLED - See TODO +#ifdef UNIT_TEST + // Grab the second copy of the data (i.e. inverted) + uint64_t second_data = 0; + // Match Data + Footer + if (!matchGeneric(results->rawbuf + offset, &second_data, + results->rawlen - offset, nbits, + 0, 0, + kSharpBitMark, kSharpOneSpace, + kSharpBitMark, kSharpZeroSpace, + kSharpBitMark, kSharpGap, true, 35)) return false; + // Check that second_data has been inverted correctly. + if (data != (second_data ^ kSharpToggleMask)) return false; +#endif // UNIT_TEST + } + + // Success + results->decode_type = SHARP; + results->bits = nbits; + results->value = data; + // Address & command are actually transmitted in LSB first order. + results->address = reverseBits(data, nbits) & kSharpAddressMask; + results->command = + reverseBits((data >> 2) & kSharpCommandMask, kSharpCommandBits); + return true; +} +#endif // (DECODE_SHARP || DECODE_DENON) + +#if SEND_SHARP_AC +// Send a Sharp A/C message. +// +// Args: +// data: An array of kSharpAcStateLength bytes containing the IR command. +// nbytes: Nr. of bytes of data to send. i.e. length of `data`. +// repeat: Nr. of times the message should be repeated. +// +// Status: Alpha / Untested. +// +// Ref: +// https://github.com/crankyoldgit/IRremoteESP8266/issues/638 +// https://github.com/ToniA/arduino-heatpumpir/blob/master/SharpHeatpumpIR.cpp +void IRsend::sendSharpAc(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes < kSharpAcStateLength) + return; // Not enough bytes to send a proper message. + + sendGeneric(kSharpAcHdrMark, kSharpAcHdrSpace, + kSharpAcBitMark, kSharpAcOneSpace, + kSharpAcBitMark, kSharpAcZeroSpace, + kSharpAcBitMark, kSharpAcGap, + data, nbytes, 38000, false, repeat, 50); +} +#endif // SEND_SHARP_AC + +IRSharpAc::IRSharpAc(const uint16_t pin, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { this->stateReset(); } + +void IRSharpAc::begin(void) { _irsend.begin(); } + +#if SEND_SHARP_AC +void IRSharpAc::send(const uint16_t repeat) { + this->checksum(); + _irsend.sendSharpAc(remote, kSharpAcStateLength, repeat); +} +#endif // SEND_SHARP_AC + +// Calculate the checksum for a given state. +// Args: +// state: The array to verify the checksums of. +// length: The size of the state. +// Returns: +// The 4 bit checksum. +uint8_t IRSharpAc::calcChecksum(uint8_t state[], const uint16_t length) { + uint8_t xorsum = xorBytes(state, length - 1); + xorsum ^= (state[length - 1] & 0xF); + xorsum ^= xorsum >> 4; + return xorsum & 0xF; +} + +// Verify the checksums are valid for a given state. +// Args: +// state: The array to verify the checksums of. +// length: The size of the state. +// Returns: +// A boolean. +bool IRSharpAc::validChecksum(uint8_t state[], const uint16_t length) { + return (state[length - 1] >> 4) == IRSharpAc::calcChecksum(state, length); +} + +// Calculate and set the checksum values for the internal state. +void IRSharpAc::checksum(void) { + remote[kSharpAcStateLength - 1] &= 0x0F; + remote[kSharpAcStateLength - 1] |= this->calcChecksum(remote) << 4; +} + +void IRSharpAc::stateReset(void) { + static const uint8_t reset[kSharpAcStateLength] = { + 0xAA, 0x5A, 0xCF, 0x10, 0x00, 0x01, 0x00, 0x00, 0x08, 0x80, 0x00, 0xE0, + 0x01}; + for (uint8_t i = 0; i < kSharpAcStateLength; i++) remote[i] = reset[i]; +} + +uint8_t *IRSharpAc::getRaw(void) { + this->checksum(); // Ensure correct settings before sending. + return remote; +} + +void IRSharpAc::setRaw(const uint8_t new_code[], const uint16_t length) { + for (uint8_t i = 0; i < length && i < kSharpAcStateLength; i++) + remote[i] = new_code[i]; +} + +void IRSharpAc::on(void) { remote[kSharpAcBytePower] |= kSharpAcBitPower; } + +void IRSharpAc::off(void) { remote[kSharpAcBytePower] &= ~kSharpAcBitPower; } + +void IRSharpAc::setPower(const bool on) { + if (on) + this->on(); + else + this->off(); +} + +bool IRSharpAc::getPower(void) { + return remote[kSharpAcBytePower] & kSharpAcBitPower; +} + +// Set the temp in deg C +void IRSharpAc::setTemp(const uint8_t temp) { + switch (this->getMode()) { + // Auto & Dry don't allow temp changes and have a special temp. + case kSharpAcAuto: + case kSharpAcDry: + remote[kSharpAcByteTemp] = 0; + remote[kSharpAcByteManual] = 0; // When in Dry/Auto this byte is 0. + return; + default: + remote[kSharpAcByteTemp] = 0xC0; + remote[kSharpAcByteManual] |= kSharpAcBitTempManual; + } + uint8_t degrees = std::max(temp, kSharpAcMinTemp); + degrees = std::min(degrees, kSharpAcMaxTemp); + remote[kSharpAcByteTemp] &= ~kSharpAcMaskTemp; + remote[kSharpAcByteTemp] |= (degrees - kSharpAcMinTemp); +} + +uint8_t IRSharpAc::getTemp(void) { + return (remote[kSharpAcByteTemp] & kSharpAcMaskTemp) + kSharpAcMinTemp; +} + +uint8_t IRSharpAc::getMode(void) { + return remote[kSharpAcByteMode] & kSharpAcMaskMode; +} + +void IRSharpAc::setMode(const uint8_t mode) { + const uint8_t special = 0x20; // Non-auto modes have this bit set. + remote[kSharpAcBytePower] |= special; + switch (mode) { + case kSharpAcAuto: + remote[kSharpAcBytePower] &= ~special; // Auto has this bit cleared. + // FALLTHRU + case kSharpAcDry: + this->setTemp(0); // Dry/Auto have no temp setting. + // FALLTHRU + case kSharpAcCool: + case kSharpAcHeat: + remote[kSharpAcByteMode] &= ~kSharpAcMaskMode; + remote[kSharpAcByteMode] |= mode; + + break; + default: + this->setMode(kSharpAcAuto); + } +} + +// Set the speed of the fan +void IRSharpAc::setFan(const uint8_t speed) { + remote[kSharpAcByteManual] |= kSharpAcBitFanManual; // Manual fan mode. + switch (speed) { + case kSharpAcFanAuto: + // Clear the manual fan bit. + remote[kSharpAcByteManual] &= ~kSharpAcBitFanManual; + // FALLTHRU + case kSharpAcFanMin: + case kSharpAcFanMed: + case kSharpAcFanHigh: + case kSharpAcFanMax: + remote[kSharpAcByteFan] &= ~kSharpAcMaskFan; + remote[kSharpAcByteFan] |= (speed << 4); + break; + default: + this->setFan(kSharpAcFanAuto); + } +} + +uint8_t IRSharpAc::getFan(void) { + return (remote[kSharpAcByteFan] & kSharpAcMaskFan) >> 4; +} + +// Convert a standard A/C mode into its native mode. +uint8_t IRSharpAc::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kSharpAcCool; + case stdAc::opmode_t::kHeat: + return kSharpAcHeat; + case stdAc::opmode_t::kDry: + return kSharpAcDry; + // No Fan mode. + default: + return kSharpAcAuto; + } +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRSharpAc::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: + return kSharpAcFanMin; + case stdAc::fanspeed_t::kMedium: + return kSharpAcFanMed; + case stdAc::fanspeed_t::kHigh: + return kSharpAcFanHigh; + case stdAc::fanspeed_t::kMax: + return kSharpAcFanMax; + default: + return kSharpAcFanAuto; + } +} + +// Convert a native mode to it's common equivalent. +stdAc::opmode_t IRSharpAc::toCommonMode(const uint8_t mode) { + switch (mode) { + case kSharpAcCool: return stdAc::opmode_t::kCool; + case kSharpAcHeat: return stdAc::opmode_t::kHeat; + case kSharpAcDry: return stdAc::opmode_t::kDry; + default: return stdAc::opmode_t::kAuto; + } +} + +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRSharpAc::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kSharpAcFanMax: return stdAc::fanspeed_t::kMax; + case kSharpAcFanHigh: return stdAc::fanspeed_t::kHigh; + case kSharpAcFanMed: return stdAc::fanspeed_t::kMedium; + case kSharpAcFanMin: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRSharpAc::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::SHARP_AC; + result.model = -1; // Not supported. + result.power = this->getPower(); + result.mode = this->toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(); + result.fanspeed = this->toCommonFanSpeed(this->getFan()); + // Not supported. + result.swingv = stdAc::swingv_t::kOff; + result.swingh = stdAc::swingh_t::kOff; + result.quiet = false; + result.turbo = false; + result.clean = false; + result.beep = false; + result.econo = false; + result.filter = false; + result.light = false; + result.sleep = -1; + result.clock = -1; + return result; +} + +// Convert the internal state into a human readable string. +String IRSharpAc::toString(void) { + String result = ""; + result.reserve(60); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(getPower(), F("Power"), false); + result += addModeToString(getMode(), kSharpAcAuto, kSharpAcCool, kSharpAcHeat, + kSharpAcDry, kSharpAcAuto); + result += addTempToString(getTemp()); + result += addFanToString(getFan(), kSharpAcFanMax, kSharpAcFanMin, + kSharpAcFanAuto, kSharpAcFanAuto, kSharpAcFanMed); + return result; +} + +#if DECODE_SHARP_AC +// Decode the supplied Sharp A/C message. +// Args: +// results: Ptr to the data to decode and where to store the decode result. +// nbits: Nr. of bits to expect in the data portion. (kSharpAcBits) +// strict: Flag to indicate if we strictly adhere to the specification. +// Returns: +// boolean: True if it can decode it, false if it can't. +// +// Status: BETA / Should be working. +// +// Ref: +// https://github.com/crankyoldgit/IRremoteESP8266/issues/638 +// https://github.com/ToniA/arduino-heatpumpir/blob/master/SharpHeatpumpIR.cpp +bool IRrecv::decodeSharpAc(decode_results *results, const uint16_t nbits, + const bool strict) { + // Is there enough data to match successfully? + if (results->rawlen < 2 * nbits + kHeader + kFooter - 1) + return false; + + // Compliance + if (strict && nbits != kSharpAcBits) return false; + + uint16_t offset = kStartOffset; + // Match Header + Data + Footer + uint16_t used; + used = matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, nbits, + kSharpAcHdrMark, kSharpAcHdrSpace, + kSharpAcBitMark, kSharpAcOneSpace, + kSharpAcBitMark, kSharpAcZeroSpace, + kSharpAcBitMark, kSharpAcGap, true, + _tolerance, kMarkExcess, false); + if (used == 0) return false; + offset += used; + // Compliance + if (strict) { + if (!IRSharpAc::validChecksum(results->state)) return false; + } + + // Success + results->decode_type = SHARP_AC; + results->bits = nbits; + // No need to record the state as we stored it as we decoded it. + // As we use result->state, we don't record value, address, or command as it + // is a union data type. + return true; +} +#endif // DECODE_SHARP_AC diff --git a/lib/IRremoteESP8266-2.6.5/src/ir_Sharp.h b/lib/IRremoteESP8266-2.6.5/src/ir_Sharp.h new file mode 100644 index 000000000..e80c7cdde --- /dev/null +++ b/lib/IRremoteESP8266-2.6.5/src/ir_Sharp.h @@ -0,0 +1,98 @@ +// Copyright 2019 crankyoldgit + +// Supports: +// Brand: Sharp, Model: LC-52D62U TV +// Brand: Sharp, Model: AY-ZP40KR A/C + +#ifndef IR_SHARP_H_ +#define IR_SHARP_H_ + +#ifndef UNIT_TEST +#include +#endif +#include "IRrecv.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif + +// Constants +const uint16_t kSharpAcHdrMark = 3800; +const uint16_t kSharpAcHdrSpace = 1900; +const uint16_t kSharpAcBitMark = 470; +const uint16_t kSharpAcZeroSpace = 500; +const uint16_t kSharpAcOneSpace = 1400; +const uint32_t kSharpAcGap = kDefaultMessageGap; + +const uint8_t kSharpAcAuto = 0b000; +const uint8_t kSharpAcDry = 0b011; +const uint8_t kSharpAcCool = 0b010; +const uint8_t kSharpAcHeat = 0b001; +const uint8_t kSharpAcMinTemp = 15; // Celsius +const uint8_t kSharpAcMaxTemp = 30; // Celsius +const uint8_t kSharpAcFanAuto = 0b010; // 2 +const uint8_t kSharpAcFanMin = 0b100; // 4 (FAN1) +const uint8_t kSharpAcFanMed = 0b011; // 3 (FAN2) +const uint8_t kSharpAcFanHigh = 0b101; // 5 (FAN3) +const uint8_t kSharpAcFanMax = 0b111; // 7 (FAN4) +const uint8_t kSharpAcByteTemp = 4; +const uint8_t kSharpAcMaskTemp = 0b00001111; +const uint8_t kSharpAcBytePower = 5; +const uint8_t kSharpAcBitPower = 0b00010000; +const uint8_t kSharpAcByteMode = 6; +const uint8_t kSharpAcMaskMode = 0b00000011; +const uint8_t kSharpAcByteFan = kSharpAcByteMode; +const uint8_t kSharpAcMaskFan = 0b01110000; +const uint8_t kSharpAcByteManual = 10; +const uint8_t kSharpAcBitFanManual = 0b00000001; +const uint8_t kSharpAcBitTempManual = 0b00000100; + + +class IRSharpAc { + public: + explicit IRSharpAc(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); + +#if SEND_SHARP_AC + void send(const uint16_t repeat = kSharpAcDefaultRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } +#endif // SEND_SHARP_AC + void begin(void); + void on(void); + void off(void); + void setPower(const bool on); + bool getPower(void); + void setTemp(const uint8_t temp); + uint8_t getTemp(void); + void setFan(const uint8_t fan); + uint8_t getFan(void); + void setMode(const uint8_t mode); + uint8_t getMode(void); + uint8_t* getRaw(void); + void setRaw(const uint8_t new_code[], + const uint16_t length = kSharpAcStateLength); + static bool validChecksum(uint8_t state[], + const uint16_t length = kSharpAcStateLength); + static uint8_t convertMode(const stdAc::opmode_t mode); + static uint8_t convertFan(const stdAc::fanspeed_t speed); + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + stdAc::state_t toCommon(void); + String toString(void); +#ifndef UNIT_TEST + + private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif + // # of bytes per command + uint8_t remote[kSharpAcStateLength]; + void stateReset(void); + void checksum(void); + static uint8_t calcChecksum(uint8_t state[], + const uint16_t length = kSharpAcStateLength); +}; + +#endif // IR_SHARP_H_ diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Sherwood.cpp b/lib/IRremoteESP8266-2.6.5/src/ir_Sherwood.cpp similarity index 66% rename from lib/IRremoteESP8266-2.6.0/src/ir_Sherwood.cpp rename to lib/IRremoteESP8266-2.6.5/src/ir_Sherwood.cpp index 8af7dfb34..47c6790de 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Sherwood.cpp +++ b/lib/IRremoteESP8266-2.6.5/src/ir_Sherwood.cpp @@ -1,14 +1,14 @@ // Copyright 2017 David Conran +// Sherwood IR remote emulation + +// Supports: +// Brand: Sherwood, Model: RC-138 remote +// Brand: Sherwood, Model: RD6505(B) Receiver + #include #include "IRsend.h" -// SSSSS HH HH EEEEEEE RRRRRR WW WW OOOOO OOOOO DDDDD -// SS HH HH EE RR RR WW WW OO OO OO OO DD DD -// SSSSS HHHHHHH EEEEE RRRRRR WW W WW OO OO OO OO DD DD -// SS HH HH EE RR RR WW WWW WW OO OO OO OO DD DD -// SSSSS HH HH EEEEEEE RR RR WW WW OOOO0 OOOO0 DDDDDD - #if SEND_SHERWOOD // Send an IR command to a Sherwood device. // diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Sony.cpp b/lib/IRremoteESP8266-2.6.5/src/ir_Sony.cpp similarity index 88% rename from lib/IRremoteESP8266-2.6.0/src/ir_Sony.cpp rename to lib/IRremoteESP8266-2.6.5/src/ir_Sony.cpp index efa6e6a46..6fc39b7b5 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Sony.cpp +++ b/lib/IRremoteESP8266-2.6.5/src/ir_Sony.cpp @@ -2,17 +2,13 @@ // Copyright 2016 marcosamarinho // Copyright 2017 David Conran +// Sony Remote Emulation + #include #include "IRrecv.h" #include "IRsend.h" #include "IRutils.h" -// SSSS OOO N N Y Y -// S O O NN N Y Y -// SSS O O N N N Y -// S O O N NN Y -// SSSS OOO N N Y - // Sony originally added from https://github.com/shirriff/Arduino-IRremote/ // Updates from marcosamarinho @@ -123,25 +119,20 @@ bool IRrecv::decodeSony(decode_results *results, uint16_t nbits, bool strict) { uint64_t data = 0; uint16_t offset = kStartOffset; uint16_t actualBits; - uint32_t timeSoFar = 0; // Time in uSecs of the message length. // Header - timeSoFar += results->rawbuf[offset] * kRawTick; if (!matchMark(results->rawbuf[offset], kSonyHdrMark)) return false; // Calculate how long the common tick time is based on the header mark. uint32_t tick = results->rawbuf[offset++] * kRawTick / kSonyHdrMarkTicks; // Data for (actualBits = 0; offset < results->rawlen - 1; actualBits++, offset++) { - // The gap after a Sony packet for a repeat should be kSonyMinGap or - // (kSonyRptLength - timeSoFar) according to the spec. - if (matchSpace(results->rawbuf[offset], kSonyMinGapTicks * tick) || - matchAtLeast(results->rawbuf[offset], kSonyRptLength - timeSoFar)) + // The gap after a Sony packet for a repeat should be kSonyMinGap according + // to the spec. + if (matchAtLeast(results->rawbuf[offset], kSonyMinGapTicks * tick)) break; // Found a repeat space. - timeSoFar += results->rawbuf[offset] * kRawTick; if (!matchSpace(results->rawbuf[offset++], kSonySpaceTicks * tick)) return false; - timeSoFar += results->rawbuf[offset] * kRawTick; if (matchMark(results->rawbuf[offset], kSonyOneMarkTicks * tick)) data = (data << 1) | 1; else if (matchMark(results->rawbuf[offset], kSonyZeroMarkTicks * tick)) diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Tcl.cpp b/lib/IRremoteESP8266-2.6.5/src/ir_Tcl.cpp similarity index 69% rename from lib/IRremoteESP8266-2.6.0/src/ir_Tcl.cpp rename to lib/IRremoteESP8266-2.6.5/src/ir_Tcl.cpp index 79fb23cf1..0186f43e5 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Tcl.cpp +++ b/lib/IRremoteESP8266-2.6.5/src/ir_Tcl.cpp @@ -10,6 +10,12 @@ // Constants +using irutils::addBoolToString; +using irutils::addFanToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addTempToString; #if SEND_TCL112AC void IRsend::sendTcl112Ac(const unsigned char data[], const uint16_t nbytes, @@ -22,9 +28,11 @@ void IRsend::sendTcl112Ac(const unsigned char data[], const uint16_t nbytes, } #endif // SEND_TCL112AC -IRTcl112Ac::IRTcl112Ac(uint16_t pin) : _irsend(pin) { stateReset(); } +IRTcl112Ac::IRTcl112Ac(const uint16_t pin, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { stateReset(); } -void IRTcl112Ac::begin() { this->_irsend.begin(); } +void IRTcl112Ac::begin(void) { this->_irsend.begin(); } #if SEND_TCL112AC void IRTcl112Ac::send(const uint16_t repeat) { @@ -64,7 +72,7 @@ bool IRTcl112Ac::validChecksum(uint8_t state[], const uint16_t length) { return (length > 1 && state[length - 1] == calcChecksum(state, length)); } -void IRTcl112Ac::stateReset() { +void IRTcl112Ac::stateReset(void) { for (uint8_t i = 0; i < kTcl112AcStateLength; i++) remote_state[i] = 0x0; // A known good state. (On, Cool, 24C) @@ -79,7 +87,7 @@ void IRTcl112Ac::stateReset() { remote_state[13] = 0x03; } -uint8_t* IRTcl112Ac::getRaw() { +uint8_t* IRTcl112Ac::getRaw(void) { this->checksum(); return remote_state; } @@ -112,7 +120,7 @@ bool IRTcl112Ac::getPower(void) { // Get the requested climate operation mode of the a/c unit. // Returns: // A uint8_t containing the A/C mode. -uint8_t IRTcl112Ac::getMode() { +uint8_t IRTcl112Ac::getMode(void) { return remote_state[6] & 0xF; } @@ -151,7 +159,7 @@ void IRTcl112Ac::setTemp(const float celsius) { remote_state[7] |= ((uint8_t)kTcl112AcTempMax - nrHalfDegrees / 2); } -float IRTcl112Ac::getTemp() { +float IRTcl112Ac::getTemp(void) { float result = kTcl112AcTempMax - (remote_state[7] & 0xF); if (remote_state[12] & kTcl112AcHalfDegree) result += 0.5; return result; @@ -174,7 +182,7 @@ void IRTcl112Ac::setFan(const uint8_t speed) { } // Return the currect fan speed. -uint8_t IRTcl112Ac::getFan() { +uint8_t IRTcl112Ac::getFan(void) { return remote_state[8] & kTcl112AcFanMask; } @@ -291,69 +299,74 @@ uint8_t IRTcl112Ac::convertFan(const stdAc::fanspeed_t speed) { } } -// Convert the internal state into a human readable string. -#ifdef ARDUINO -String IRTcl112Ac::toString() { - String result = ""; -#else -std::string IRTcl112Ac::toString() { - std::string result = ""; -#endif // ARDUINO - result += F("Power: "); - result += (this->getPower() ? F("On") : F("Off")); - result += F(", Mode: "); - result += uint64ToString(getMode()); - switch (this->getMode()) { - case kTcl112AcAuto: - result += F(" (AUTO)"); - break; - case kTcl112AcCool: - result += F(" (COOL)"); - break; - case kTcl112AcHeat: - result += F(" (HEAT)"); - break; - case kTcl112AcDry: - result += F(" (DRY)"); - break; - case kTcl112AcFan: - result += F(" (FAN)"); - break; - default: - result += F(" (UNKNOWN)"); +// Convert a native mode to it's common equivalent. +stdAc::opmode_t IRTcl112Ac::toCommonMode(const uint8_t mode) { + switch (mode) { + case kTcl112AcCool: return stdAc::opmode_t::kCool; + case kTcl112AcHeat: return stdAc::opmode_t::kHeat; + case kTcl112AcDry: return stdAc::opmode_t::kDry; + case kTcl112AcFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; } +} + +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRTcl112Ac::toCommonFanSpeed(const uint8_t spd) { + switch (spd) { + case kTcl112AcFanHigh: return stdAc::fanspeed_t::kMax; + case kTcl112AcFanMed: return stdAc::fanspeed_t::kMedium; + case kTcl112AcFanLow: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRTcl112Ac::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::TCL112AC; + result.model = -1; // Not supported. + result.power = this->getPower(); + result.mode = this->toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(); + result.fanspeed = this->toCommonFanSpeed(this->getFan()); + result.swingv = this->getSwingVertical() ? stdAc::swingv_t::kAuto : + stdAc::swingv_t::kOff; + result.swingh = this->getSwingHorizontal() ? stdAc::swingh_t::kAuto : + stdAc::swingh_t::kOff; + result.turbo = this->getTurbo(); + result.light = this->getLight(); + result.filter = this->getHealth(); + result.econo = this->getEcono(); + // Not supported. + result.quiet = false; + result.clean = false; + result.beep = false; + result.sleep = -1; + result.clock = -1; + return result; +} + +// Convert the internal state into a human readable string. +String IRTcl112Ac::toString(void) { + String result = ""; + result.reserve(140); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(getPower(), F("Power"), false); + result += addModeToString(getMode(), kTcl112AcAuto, kTcl112AcCool, + kTcl112AcHeat, kTcl112AcDry, kTcl112AcFan); uint16_t nrHalfDegrees = this->getTemp() * 2; result += F(", Temp: "); result += uint64ToString(nrHalfDegrees / 2); if (nrHalfDegrees & 1) result += F(".5"); - result += F("C, Fan: "); - result += uint64ToString(getFan()); - switch (getFan()) { - case kTcl112AcFanAuto: - result += F(" (Auto)"); - break; - case kTcl112AcFanLow: - result += F(" (Low)"); - break; - case kTcl112AcFanMed: - result += F(" (Med)"); - break; - case kTcl112AcFanHigh: - result += F(" (High)"); - break; - } - result += F(", Econo: "); - result += (this->getEcono() ? F("On") : F("Off")); - result += ", Health: "; - result += (this->getHealth() ? F("On") : F("Off")); - result += F(", Light: "); - result += (this->getLight() ? F("On") : F("Off")); - result += F(", Turbo: "); - result += (this->getTurbo() ? F("On") : F("Off")); - result += ", Swing (H): "; - result += (this->getSwingHorizontal() ? F("On") : F("Off")); - result += F(", Swing (V): "); - result += (this->getSwingVertical() ? F("On") : F("Off")); + result += 'C'; + result += addFanToString(getFan(), kTcl112AcFanHigh, kTcl112AcFanLow, + kTcl112AcFanAuto, kTcl112AcFanAuto, kTcl112AcFanMed); + result += addBoolToString(getEcono(), F("Econo")); + result += addBoolToString(getHealth(), F("Health")); + result += addBoolToString(getLight(), F("Light")); + result += addBoolToString(getTurbo(), F("Turbo")); + result += addBoolToString(getSwingHorizontal(), F("Swing (H)")); + result += addBoolToString(getSwingVertical(), F("Swing (V)")); return result; } @@ -370,48 +383,26 @@ std::string IRTcl112Ac::toString() { // Status: BETA / Appears to mostly work. // // Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/619 -bool IRrecv::decodeTcl112Ac(decode_results *results, uint16_t nbits, - bool strict) { - if (results->rawlen < 2 * nbits + kHeader + kFooter - 1) - return false; // Can't possibly be a valid Samsung A/C message. +// https://github.com/crankyoldgit/IRremoteESP8266/issues/619 +bool IRrecv::decodeTcl112Ac(decode_results *results, const uint16_t nbits, + const bool strict) { if (strict && nbits != kTcl112AcBits) return false; uint16_t offset = kStartOffset; - uint16_t dataBitsSoFar = 0; - match_result_t data_result; - - // Message Header - if (!matchMark(results->rawbuf[offset++], kTcl112AcHdrMark)) return false; - if (!matchSpace(results->rawbuf[offset++], kTcl112AcHdrSpace)) return false; - - // Data - // Keep reading bytes until we either run out of section or state to fill. - for (uint16_t i = 0; offset <= results->rawlen - 16 && i < nbits / 8; - i++, dataBitsSoFar += 8, offset += data_result.used) { - data_result = matchData(&(results->rawbuf[offset]), 8, kTcl112AcBitMark, - kTcl112AcOneSpace, kTcl112AcBitMark, - kTcl112AcZeroSpace, kTolerance, 0, false); - if (data_result.success == false) { - DPRINT("DEBUG: offset = "); - DPRINTLN(offset + data_result.used); - return false; // Fail - } - results->state[i] = data_result.data; - } - - // Footer - if (!matchMark(results->rawbuf[offset++], kTcl112AcBitMark)) return false; - if (offset <= results->rawlen && - !matchAtLeast(results->rawbuf[offset++], kTcl112AcGap)) return false; + // Match Header + Data + Footer + if (!matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, nbits, + kTcl112AcHdrMark, kTcl112AcHdrSpace, + kTcl112AcBitMark, kTcl112AcOneSpace, + kTcl112AcBitMark, kTcl112AcZeroSpace, + kTcl112AcBitMark, kTcl112AcGap, true, + _tolerance + kTcl112AcTolerance, 0, false)) return false; // Compliance - // Re-check we got the correct size/length due to the way we read the data. - if (dataBitsSoFar != nbits) return false; // Verify we got a valid checksum. if (strict && !IRTcl112Ac::validChecksum(results->state)) return false; // Success results->decode_type = TCL112AC; - results->bits = dataBitsSoFar; + results->bits = nbits; // No need to record the state as we stored it as we decoded it. // As we use result->state, we don't record value, address, or command as it // is a union data type. diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Tcl.h b/lib/IRremoteESP8266-2.6.5/src/ir_Tcl.h similarity index 84% rename from lib/IRremoteESP8266-2.6.0/src/ir_Tcl.h rename to lib/IRremoteESP8266-2.6.5/src/ir_Tcl.h index a1595451d..1a1bc1d6b 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Tcl.h +++ b/lib/IRremoteESP8266-2.6.5/src/ir_Tcl.h @@ -1,15 +1,17 @@ // Copyright 2019 David Conran +// Supports: +// Brand: Leberg, Model: LBS-TOR07 A/C + #ifndef IR_TCL_H_ #define IR_TCL_H_ #ifndef UNIT_TEST #include -#else -#include #endif #include "IRremoteESP8266.h" #include "IRsend.h" +#include "IRrecv.h" #ifdef UNIT_TEST #include "IRsend_test.h" #endif @@ -21,6 +23,7 @@ const uint16_t kTcl112AcBitMark = 500; const uint16_t kTcl112AcOneSpace = 1050; const uint16_t kTcl112AcZeroSpace = 325; const uint32_t kTcl112AcGap = kDefaultMessageGap; // Just a guess. +const uint8_t kTcl112AcTolerance = 5; // Extra Percent const uint8_t kTcl112AcHeat = 1; const uint8_t kTcl112AcDry = 2; @@ -48,10 +51,12 @@ const uint8_t kTcl112AcBitTurbo = 0b01000000; class IRTcl112Ac { public: - explicit IRTcl112Ac(uint16_t pin); + explicit IRTcl112Ac(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); #if SEND_TCL112AC void send(const uint16_t repeat = kTcl112AcDefaultRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } #endif // SEND_TCL void begin(void); uint8_t* getRaw(void); @@ -85,11 +90,10 @@ class IRTcl112Ac { bool getTurbo(void); uint8_t convertMode(const stdAc::opmode_t mode); uint8_t convertFan(const stdAc::fanspeed_t speed); -#ifdef ARDUINO - String toString(); -#else - std::string toString(); -#endif + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + stdAc::state_t toCommon(void); + String toString(void); #ifndef UNIT_TEST private: @@ -98,7 +102,7 @@ class IRTcl112Ac { IRsendTest _irsend; #endif uint8_t remote_state[kTcl112AcStateLength]; - void stateReset(); + void stateReset(void); void checksum(const uint16_t length = kTcl112AcStateLength); }; diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Teco.cpp b/lib/IRremoteESP8266-2.6.5/src/ir_Teco.cpp similarity index 53% rename from lib/IRremoteESP8266-2.6.0/src/ir_Teco.cpp rename to lib/IRremoteESP8266-2.6.5/src/ir_Teco.cpp index 779bf8f8f..9967ccee1 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Teco.cpp +++ b/lib/IRremoteESP8266-2.6.5/src/ir_Teco.cpp @@ -20,6 +20,13 @@ const uint16_t kTecoOneSpace = 1650; const uint16_t kTecoZeroSpace = 580; const uint32_t kTecoGap = kDefaultMessageGap; // Made-up value. Just a guess. +using irutils::addBoolToString; +using irutils::addFanToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addTempToString; + #if SEND_TECO // Send a Teco A/C message. // @@ -27,7 +34,8 @@ const uint32_t kTecoGap = kDefaultMessageGap; // Made-up value. Just a guess. // data: Contents of the message to be sent. // nbits: Nr. of bits of data to be sent. Typically kTecoBits. // repeat: Nr. of additional times the message is to be sent. -void IRsend::sendTeco(uint64_t data, uint16_t nbits, uint16_t repeat) { +void IRsend::sendTeco(const uint64_t data, const uint16_t nbits, + const uint16_t repeat) { sendGeneric(kTecoHdrMark, kTecoHdrSpace, kTecoBitMark, kTecoOneSpace, kTecoBitMark, kTecoZeroSpace, kTecoBitMark, kTecoGap, data, nbits, 38000, false, repeat, kDutyDefault); @@ -35,9 +43,11 @@ void IRsend::sendTeco(uint64_t data, uint16_t nbits, uint16_t repeat) { #endif // SEND_TECO // Class for decoding and constructing Teco AC messages. -IRTecoAc::IRTecoAc(const uint16_t pin) : _irsend(pin) { stateReset(); } +IRTecoAc::IRTecoAc(const uint16_t pin, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { this->stateReset(); } -void IRTecoAc::begin() { _irsend.begin(); } +void IRTecoAc::begin(void) { _irsend.begin(); } #if SEND_TECO void IRTecoAc::send(const uint16_t repeat) { @@ -54,19 +64,18 @@ uint64_t IRTecoAc::getRaw(void) { return remote_state; } void IRTecoAc::setRaw(const uint64_t new_code) { remote_state = new_code; } -void IRTecoAc::on(void) { remote_state |= kTecoPower; } +void IRTecoAc::on(void) { setPower(true); } -void IRTecoAc::off(void) { remote_state &= ~kTecoPower; } +void IRTecoAc::off(void) { setPower(false); } void IRTecoAc::setPower(const bool on) { if (on) - this->on(); + remote_state |= kTecoPower; else - this->off(); + remote_state &= ~kTecoPower; } -bool IRTecoAc::getPower(void) { - return (remote_state & kTecoPower) == kTecoPower; } +bool IRTecoAc::getPower(void) { return remote_state & kTecoPower; } void IRTecoAc::setTemp(const uint8_t temp) { uint8_t newtemp = temp; @@ -136,6 +145,33 @@ void IRTecoAc::setSleep(const bool on) { bool IRTecoAc::getSleep(void) { return remote_state & kTecoSleep; } +bool IRTecoAc::getLight(void) { return remote_state & kTecoLight; } + +void IRTecoAc::setLight(const bool on) { + if (on) + remote_state |= kTecoLight; + else + remote_state &= ~kTecoLight; +} + +bool IRTecoAc::getHumid(void) { return remote_state & kTecoHumid; } + +void IRTecoAc::setHumid(const bool on) { + if (on) + remote_state |= kTecoHumid; + else + remote_state &= ~kTecoHumid; +} + +bool IRTecoAc::getSave(void) { return remote_state & kTecoSave; } + +void IRTecoAc::setSave(const bool on) { + if (on) + remote_state |= kTecoSave; + else + remote_state &= ~kTecoSave; +} + // Convert a standard A/C mode into its native mode. uint8_t IRTecoAc::convertMode(const stdAc::opmode_t mode) { switch (mode) { @@ -168,61 +204,69 @@ uint8_t IRTecoAc::convertFan(const stdAc::fanspeed_t speed) { } } +// Convert a native mode to it's common equivalent. +stdAc::opmode_t IRTecoAc::toCommonMode(const uint8_t mode) { + switch (mode) { + case kTecoCool: return stdAc::opmode_t::kCool; + case kTecoHeat: return stdAc::opmode_t::kHeat; + case kTecoDry: return stdAc::opmode_t::kDry; + case kTecoFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRTecoAc::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kTecoFanHigh: return stdAc::fanspeed_t::kMax; + case kTecoFanMed: return stdAc::fanspeed_t::kMedium; + case kTecoFanLow: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRTecoAc::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::TECO; + result.model = -1; // Not supported. + result.power = this->getPower(); + result.mode = this->toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(); + result.fanspeed = this->toCommonFanSpeed(this->getFan()); + result.swingv = this->getSwing() ? stdAc::swingv_t::kAuto : + stdAc::swingv_t::kOff; + result.sleep = this->getSleep() ? 0 : -1; + result.light = this->getLight(); + // Not supported. + result.swingh = stdAc::swingh_t::kOff; + result.turbo = false; + result.filter = false; + result.econo = false; + result.quiet = false; + result.clean = false; + result.beep = false; + result.clock = -1; + return result; +} + // Convert the internal state into a human readable string. -#ifdef ARDUINO String IRTecoAc::toString(void) { String result = ""; -#else -std::string IRTecoAc::toString(void) { - std::string result = ""; -#endif // ARDUINO - result += F("Power: "); - result += (this->getPower() ? F("On") : F("Off")); - result += F(", Mode: "); - result += uint64ToString(this->getMode()); - switch (this->getMode()) { - case kTecoAuto: - result += F(" (AUTO)"); - break; - case kTecoCool: - result += F(" (COOL)"); - break; - case kTecoHeat: - result += F(" (HEAT)"); - break; - case kTecoDry: - result += F(" (DRY)"); - break; - case kTecoFan: - result += F(" (FAN)"); - break; - default: - result += F(" (UNKNOWN)"); - } - result += F(", Temp: "); - result += uint64ToString(getTemp()); - result += F("C, Fan: "); - result += uint64ToString(getFan()); - switch (this->getFan()) { - case kTecoFanAuto: - result += F(" (Auto)"); - break; - case kTecoFanHigh: - result += F(" (High)"); - break; - case kTecoFanLow: - result += F(" (Low)"); - break; - case kTecoFanMed: - result += F(" (Med)"); - break; - default: - result += F(" (UNKNOWN)"); - } - result += F(", Sleep: "); - result += (this->getSleep() ? F("On") : F("Off")); - result += F(", Swing: "); - result += (this->getSwing() ? F("On") : F("Off")); + result.reserve(100); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(getPower(), F("Power"), false); + result += addModeToString(getMode(), kTecoAuto, kTecoCool, kTecoHeat, + kTecoDry, kTecoFan); + result += addTempToString(getTemp()); + result += addFanToString(getFan(), kTecoFanHigh, kTecoFanLow, + kTecoFanAuto, kTecoFanAuto, kTecoFanMed); + result += addBoolToString(getSleep(), F("Sleep")); + result += addBoolToString(getSwing(), F("Swing")); + result += addBoolToString(getLight(), F("Light")); + result += addBoolToString(getHumid(), F("Humid")); + result += addBoolToString(getSave(), F("Save")); + return result; } @@ -237,39 +281,24 @@ std::string IRTecoAc::toString(void) { // boolean: True if it can decode it, false if it can't. // // Status: STABLE / Tested. -bool IRrecv::decodeTeco(decode_results* results, uint16_t nbits, bool strict) { - // Check if can possibly be a valid Teco message. - if (results->rawlen < 2 * nbits + kHeader + kFooter - 1) return false; +bool IRrecv::decodeTeco(decode_results* results, + const uint16_t nbits, const bool strict) { if (strict && nbits != kTecoBits) return false; // Not what is expected uint64_t data = 0; uint16_t offset = kStartOffset; - match_result_t data_result; - - // Header - if (!matchMark(results->rawbuf[offset++], kTecoHdrMark)) return false; - if (!matchSpace(results->rawbuf[offset++], kTecoHdrSpace)) return false; - // Data (35 bits) - data_result = - matchData(&(results->rawbuf[offset]), 35, kTecoBitMark, kTecoOneSpace, - kTecoBitMark, kTecoZeroSpace, kTolerance, kMarkExcess, false); - if (data_result.success == false) return false; - data = data_result.data; - offset += data_result.used; - uint16_t actualBits = data_result.used / 2; - - // Footer. - if (!matchMark(results->rawbuf[offset++], kTecoBitMark)) return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset], kTecoGap)) return false; - - // Compliance - if (actualBits < nbits) return false; - if (strict && actualBits != nbits) return false; // Not as we expected. + // Match Header + Data + Footer + if (!matchGeneric(results->rawbuf + offset, &data, + results->rawlen - offset, nbits, + kTecoHdrMark, kTecoHdrSpace, + kTecoBitMark, kTecoOneSpace, + kTecoBitMark, kTecoZeroSpace, + kTecoBitMark, kTecoGap, true, + _tolerance, kMarkExcess, false)) return false; // Success results->decode_type = TECO; - results->bits = actualBits; + results->bits = nbits; results->value = data; results->address = 0; results->command = 0; diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Teco.h b/lib/IRremoteESP8266-2.6.5/src/ir_Teco.h similarity index 78% rename from lib/IRremoteESP8266-2.6.0/src/ir_Teco.h rename to lib/IRremoteESP8266-2.6.5/src/ir_Teco.h index 65a0050ae..616fc5dfb 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Teco.h +++ b/lib/IRremoteESP8266-2.6.5/src/ir_Teco.h @@ -5,8 +5,6 @@ #ifndef UNIT_TEST #include -#else -#include #endif #include "IRremoteESP8266.h" #include "IRsend.h" @@ -14,6 +12,10 @@ #include "IRsend_test.h" #endif +// Supports: +// Brand: Alaska, Model: SAC9010QC A/C +// Brand: Alaska, Model: SAC9010QC remote + // Constants. Using LSB to be able to send only 35bits. const uint8_t kTecoAuto = 0; // 0b000 const uint8_t kTecoCool = 1; // 0b001 @@ -37,6 +39,9 @@ const uint64_t kTecoTimerHalfH = 0b00000000000000000000001000000000000; const uint64_t kTecoTimerTenHr = 0b00000000000000000000110000000000000; const uint64_t kTecoTimerOn = 0b00000000000000000001000000000000000; const uint64_t kTecoTimerUniHr = 0b00000000000000011110000000000000000; +const uint64_t kTecoHumid = 0b00000000000000100000000000000000000; +const uint64_t kTecoLight = 0b00000000000001000000000000000000000; +const uint64_t kTecoSave = 0b00000000000100000000000000000000000; const uint64_t kTecoReset = 0b01001010000000000000010000000000000; /* (header mark and space) @@ -44,8 +49,12 @@ const uint64_t kTecoReset = 0b01001010000000000000010000000000000; byte 0 = Cst 0x02 byte 1 = Cst 0x50 + b6-7 = "AIR" 0, 1, 2 (Not Implemented) byte 2: - b0-3 = 0b0000 + b0 = Save + b1 = "Tree with bubbles" / Filter?? (Not Implemented) + b2 = Light/LED. + b3 = Humid b4-7 = Timer hours (unit, not thenth) hours: 0000 (0) = +0 hour @@ -89,7 +98,8 @@ const uint64_t kTecoReset = 0b01001010000000000000010000000000000; // Classes class IRTecoAc { public: - explicit IRTecoAc(const uint16_t pin); + explicit IRTecoAc(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); void stateReset(void); #if SEND_TECO @@ -111,12 +121,21 @@ class IRTecoAc { void setMode(const uint8_t mode); uint8_t getMode(void); - void setSwing(const bool state); + void setSwing(const bool on); bool getSwing(void); - void setSleep(const bool state); + void setSleep(const bool on); bool getSleep(void); + void setLight(const bool on); + bool getLight(void); + + void setHumid(const bool on); + bool getHumid(void); + + void setSave(const bool on); + bool getSave(void); + // void setTimer(uint8_t time); // To check unit // uint8_t getTimer(uint8_t); @@ -125,11 +144,10 @@ class IRTecoAc { uint8_t convertMode(const stdAc::opmode_t mode); uint8_t convertFan(const stdAc::fanspeed_t speed); -#ifdef ARDUINO + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + stdAc::state_t toCommon(void); String toString(void); -#else - std::string toString(void); -#endif #ifndef UNIT_TEST private: diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Toshiba.cpp b/lib/IRremoteESP8266-2.6.5/src/ir_Toshiba.cpp similarity index 62% rename from lib/IRremoteESP8266-2.6.0/src/ir_Toshiba.cpp rename to lib/IRremoteESP8266-2.6.5/src/ir_Toshiba.cpp index a82a2fb24..4fa4c1075 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Toshiba.cpp +++ b/lib/IRremoteESP8266-2.6.5/src/ir_Toshiba.cpp @@ -1,5 +1,8 @@ // Copyright 2017 David Conran +// Toshiba A/C support added by David Conran + + #include "ir_Toshiba.h" #include #ifndef ARDUINO @@ -9,13 +12,6 @@ #include "IRsend.h" #include "IRutils.h" -// TTTTTTT OOOOO SSSSS HH HH IIIII BBBBB AAA -// TTT OO OO SS HH HH III BB B AAAAA -// TTT OO OO SSSSS HHHHHHH III BBBBBB AA AA -// TTT OO OO SS HH HH III BB BB AAAAAAA -// TTT OOOO0 SSSSS HH HH IIIII BBBBBB AA AA - -// Toshiba A/C support added by David Conran // // Equipment it seems compatible with: // * Toshiba RAS-B13N3KV2 / Akita EVO II @@ -35,6 +31,13 @@ const uint16_t kToshibaAcOneSpace = 1623; const uint16_t kToshibaAcZeroSpace = 472; const uint16_t kToshibaAcMinGap = 7048; +using irutils::addBoolToString; +using irutils::addFanToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addTempToString; + #if SEND_TOSHIBA_AC // Send a Toshiba A/C message. // @@ -46,8 +49,8 @@ const uint16_t kToshibaAcMinGap = 7048; // // Status: StABLE / Working. // -void IRsend::sendToshibaAC(unsigned char data[], uint16_t nbytes, - uint16_t repeat) { +void IRsend::sendToshibaAC(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { if (nbytes < kToshibaACStateLength) return; // Not enough bytes to send a proper message. sendGeneric(kToshibaAcHdrMark, kToshibaAcHdrSpace, kToshibaAcBitMark, @@ -64,10 +67,12 @@ void IRsend::sendToshibaAC(unsigned char data[], uint16_t nbytes, // Status: STABLE / Working. // // Initialise the object. -IRToshibaAC::IRToshibaAC(uint16_t pin) : _irsend(pin) { stateReset(); } +IRToshibaAC::IRToshibaAC(const uint16_t pin, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { this->stateReset(); } // Reset the state of the remote to a known good state/sequence. -void IRToshibaAC::stateReset() { +void IRToshibaAC::stateReset(void) { // The state of the IR remote in IR code form. // Known good state obtained from: // https://github.com/r45635/HVAC-IR-Control/blob/master/HVAC_ESP8266/HVAC_ESP8266T.ino#L103 @@ -81,32 +86,32 @@ void IRToshibaAC::stateReset() { remote_state[4] = 0x01; for (uint8_t i = 5; i < kToshibaACStateLength; i++) remote_state[i] = 0; mode_state = remote_state[6] & 0b00000011; - checksum(); // Calculate the checksum + this->checksum(); // Calculate the checksum } // Configure the pin for output. -void IRToshibaAC::begin() { _irsend.begin(); } +void IRToshibaAC::begin(void) { _irsend.begin(); } #if SEND_TOSHIBA_AC // Send the current desired state to the IR LED. void IRToshibaAC::send(const uint16_t repeat) { - checksum(); // Ensure correct checksum before sending. + this->checksum(); // Ensure correct checksum before sending. _irsend.sendToshibaAC(remote_state, kToshibaACStateLength, repeat); } #endif // SEND_TOSHIBA_AC // Return a pointer to the internal state date of the remote. -uint8_t* IRToshibaAC::getRaw() { - checksum(); +uint8_t* IRToshibaAC::getRaw(void) { + this->checksum(); return remote_state; } // Override the internal state with the new state. -void IRToshibaAC::setRaw(uint8_t newState[]) { +void IRToshibaAC::setRaw(const uint8_t newState[]) { for (uint8_t i = 0; i < kToshibaACStateLength; i++) { remote_state[i] = newState[i]; } - mode_state = getMode(true); + mode_state = this->getMode(true); } // Calculate the checksum for a given array. @@ -133,56 +138,59 @@ uint8_t IRToshibaAC::calcChecksum(const uint8_t state[], // Returns: // A boolean. bool IRToshibaAC::validChecksum(const uint8_t state[], const uint16_t length) { - return (length > 1 && state[length - 1] == calcChecksum(state, length)); + return (length > 1 && state[length - 1] == IRToshibaAC::calcChecksum(state, + length)); } // Calculate & set the checksum for the current internal state of the remote. void IRToshibaAC::checksum(const uint16_t length) { // Stored the checksum value in the last byte. - if (length > 1) remote_state[length - 1] = calcChecksum(remote_state, length); + if (length > 1) remote_state[length - 1] = this->calcChecksum(remote_state, + length); } // Set the requested power state of the A/C to off. -void IRToshibaAC::on() { +void IRToshibaAC::on(void) { // state = ON; remote_state[6] &= ~kToshibaAcPower; setMode(mode_state); } // Set the requested power state of the A/C to off. -void IRToshibaAC::off() { +void IRToshibaAC::off(void) { // state = OFF; remote_state[6] |= (kToshibaAcPower | 0b00000011); } // Set the requested power state of the A/C. -void IRToshibaAC::setPower(bool state) { - if (state) - on(); +void IRToshibaAC::setPower(const bool on) { + if (on) + this->on(); else - off(); + this->off(); } // Return the requested power state of the A/C. -bool IRToshibaAC::getPower() { +bool IRToshibaAC::getPower(void) { return ((remote_state[6] & kToshibaAcPower) == 0); } // Set the temp. in deg C -void IRToshibaAC::setTemp(uint8_t temp) { - temp = std::max((uint8_t)kToshibaAcMinTemp, temp); +void IRToshibaAC::setTemp(const uint8_t degrees) { + uint8_t temp = std::max((uint8_t)kToshibaAcMinTemp, degrees); temp = std::min((uint8_t)kToshibaAcMaxTemp, temp); remote_state[5] = (temp - kToshibaAcMinTemp) << 4; } // Return the set temp. in deg C -uint8_t IRToshibaAC::getTemp() { +uint8_t IRToshibaAC::getTemp(void) { return ((remote_state[5] >> 4) + kToshibaAcMinTemp); } // Set the speed of the fan, 0-5. // 0 is auto, 1-5 is the speed, 5 is Max. -void IRToshibaAC::setFan(uint8_t fan) { +void IRToshibaAC::setFan(const uint8_t speed) { + uint8_t fan = speed; // Bounds check if (fan > kToshibaAcFanMax) fan = kToshibaAcFanMax; // Set the fan to maximum if out of range. @@ -192,7 +200,7 @@ void IRToshibaAC::setFan(uint8_t fan) { } // Return the requested state of the unit's fan. -uint8_t IRToshibaAC::getFan() { +uint8_t IRToshibaAC::getFan(void) { uint8_t fan = remote_state[6] >> 5; if (fan == kToshibaAcFanAuto) return kToshibaAcFanAuto; return --fan; @@ -203,7 +211,7 @@ uint8_t IRToshibaAC::getFan() { // useRaw: Indicate to get the mode from the state array. (Default: false) // Returns: // A uint8_t containing the A/C mode. -uint8_t IRToshibaAC::getMode(bool useRaw) { +uint8_t IRToshibaAC::getMode(const bool useRaw) { if (useRaw) return (remote_state[6] & 0b00000011); else @@ -211,25 +219,23 @@ uint8_t IRToshibaAC::getMode(bool useRaw) { } // Set the requested climate operation mode of the a/c unit. -void IRToshibaAC::setMode(uint8_t mode) { +void IRToshibaAC::setMode(const uint8_t mode) { // If we get an unexpected mode, default to AUTO. switch (mode) { case kToshibaAcAuto: - break; case kToshibaAcCool: - break; case kToshibaAcDry: - break; case kToshibaAcHeat: - break; + mode_state = mode; + // Only adjust the remote_state if we have power set to on. + if (getPower()) { + remote_state[6] &= 0b11111100; // Clear the previous mode. + remote_state[6] |= mode_state; + } + return; default: - mode = kToshibaAcAuto; - } - mode_state = mode; - // Only adjust the remote_state if we have power set to on. - if (getPower()) { - remote_state[6] &= 0b11111100; // Clear the previous mode. - remote_state[6] |= mode_state; + // THere is no Fan mode. + this->setMode(kToshibaAcAuto); } } @@ -266,49 +272,64 @@ uint8_t IRToshibaAC::convertFan(const stdAc::fanspeed_t speed) { } } +// Convert a native mode to it's common equivalent. +stdAc::opmode_t IRToshibaAC::toCommonMode(const uint8_t mode) { + switch (mode) { + case kToshibaAcCool: return stdAc::opmode_t::kCool; + case kToshibaAcHeat: return stdAc::opmode_t::kHeat; + case kToshibaAcDry: return stdAc::opmode_t::kDry; + default: return stdAc::opmode_t::kAuto; + } +} + +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRToshibaAC::toCommonFanSpeed(const uint8_t spd) { + switch (spd) { + case kToshibaAcFanMax: return stdAc::fanspeed_t::kMax; + case kToshibaAcFanMax - 1: return stdAc::fanspeed_t::kHigh; + case kToshibaAcFanMax - 2: return stdAc::fanspeed_t::kMedium; + case kToshibaAcFanMax - 3: return stdAc::fanspeed_t::kLow; + case kToshibaAcFanMax - 4: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRToshibaAC::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::TOSHIBA_AC; + result.model = -1; // Not supported. + result.power = this->getPower(); + result.mode = this->toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(); + result.fanspeed = this->toCommonFanSpeed(this->getFan()); + // Not supported. + result.turbo = false; + result.light = false; + result.filter = false; + result.econo = false; + result.swingv = stdAc::swingv_t::kOff; + result.swingh = stdAc::swingh_t::kOff; + result.quiet = false; + result.clean = false; + result.beep = false; + result.sleep = -1; + result.clock = -1; + return result; +} + // Convert the internal state into a human readable string. -#ifdef ARDUINO -String IRToshibaAC::toString() { +String IRToshibaAC::toString(void) { String result = ""; -#else -std::string IRToshibaAC::toString() { - std::string result = ""; -#endif // ARDUINO - result += F("Power: "); - if (getPower()) - result += F("On"); - else - result += F("Off"); - result += F(", Mode: "); - result += uint64ToString(getMode()); - switch (getMode()) { - case kToshibaAcAuto: - result += F(" (AUTO)"); - break; - case kToshibaAcCool: - result += F(" (COOL)"); - break; - case kToshibaAcHeat: - result += F(" (HEAT)"); - break; - case kToshibaAcDry: - result += F(" (DRY)"); - break; - default: - result += F(" (UNKNOWN)"); - } - result += F(", Temp: "); - result += uint64ToString(getTemp()); - result += F("C, Fan: "); - result += uint64ToString(getFan()); - switch (getFan()) { - case kToshibaAcFanAuto: - result += F(" (AUTO)"); - break; - case kToshibaAcFanMax: - result += F(" (MAX)"); - break; - } + result.reserve(40); + result += addBoolToString(getPower(), F("Power"), false); + result += addModeToString(getMode(), kToshibaAcAuto, kToshibaAcCool, + kToshibaAcHeat, kToshibaAcDry, kToshibaAcAuto); + result += addTempToString(getTemp()); + result += addFanToString(getFan(), kToshibaAcFanMax, kToshibaAcFanMin, + kToshibaAcFanAuto, kToshibaAcFanAuto, + kToshibaAcFanMed); return result; } @@ -326,39 +347,22 @@ std::string IRToshibaAC::toString() { // // Ref: // -bool IRrecv::decodeToshibaAC(decode_results* results, uint16_t nbits, - bool strict) { +bool IRrecv::decodeToshibaAC(decode_results* results, const uint16_t nbits, + const bool strict) { uint16_t offset = kStartOffset; - uint16_t dataBitsSoFar = 0; - - // Have we got enough data to successfully decode? - if (results->rawlen < kToshibaACBits + kHeader + kFooter - 1) - return false; // Can't possibly be a valid message. // Compliance if (strict && nbits != kToshibaACBits) return false; // Must be called with the correct nr. of bytes. - // Header - if (!matchMark(results->rawbuf[offset++], kToshibaAcHdrMark)) return false; - if (!matchSpace(results->rawbuf[offset++], kToshibaAcHdrSpace)) return false; - - // Data - for (uint8_t i = 0; i < kToshibaACStateLength; i++) { - // Read a byte's worth of data. - match_result_t data_result = - matchData(&(results->rawbuf[offset]), 8, kToshibaAcBitMark, - kToshibaAcOneSpace, kToshibaAcBitMark, kToshibaAcZeroSpace); - if (data_result.success == false) return false; // Fail - dataBitsSoFar += 8; - results->state[i] = (uint8_t)data_result.data; - offset += data_result.used; - } - - // Footer - if (!matchMark(results->rawbuf[offset++], kToshibaAcBitMark)) return false; - if (!matchSpace(results->rawbuf[offset++], kToshibaAcMinGap)) return false; - + // Match Header + Data + Footer + if (!matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, nbits, + kToshibaAcHdrMark, kToshibaAcHdrSpace, + kToshibaAcBitMark, kToshibaAcOneSpace, + kToshibaAcBitMark, kToshibaAcZeroSpace, + kToshibaAcBitMark, kToshibaAcMinGap, true, + _tolerance, kMarkExcess)) return false; // Compliance if (strict) { // Check that the checksum of the message is correct. @@ -367,7 +371,7 @@ bool IRrecv::decodeToshibaAC(decode_results* results, uint16_t nbits, // Success results->decode_type = TOSHIBA_AC; - results->bits = dataBitsSoFar; + results->bits = nbits; // No need to record the state as we stored it as we decoded it. // As we use result->state, we don't record value, address, or command as it // is a union data type. diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Toshiba.h b/lib/IRremoteESP8266-2.6.5/src/ir_Toshiba.h similarity index 62% rename from lib/IRremoteESP8266-2.6.0/src/ir_Toshiba.h rename to lib/IRremoteESP8266-2.6.5/src/ir_Toshiba.h index 03b461add..6b06855bf 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Toshiba.h +++ b/lib/IRremoteESP8266-2.6.5/src/ir_Toshiba.h @@ -1,4 +1,15 @@ // Copyright 2017 David Conran + +// Toshiba A/C support added by David Conran + +// Supports: +// Brand: Toshiba, Model: RAS-B13N3KV2 +// Brand: Toshiba, Model: Akita EVO II +// Brand: Toshiba, Model: RAS-B13N3KVP-E +// Brand: Toshiba, Model: RAS 18SKP-ES +// Brand: Toshiba, Model: WH-TA04NE +// Brand: Toshiba, Model: WC-L03SE + #ifndef IR_TOSHIBA_H_ #define IR_TOSHIBA_H_ @@ -6,8 +17,6 @@ #include #ifdef ARDUINO #include -#else -#include #endif #include "IRremoteESP8266.h" #include "IRsend.h" @@ -15,14 +24,6 @@ #include "IRsend_test.h" #endif -// TTTTTTT OOOOO SSSSS HH HH IIIII BBBBB AAA -// TTT OO OO SS HH HH III BB B AAAAA -// TTT OO OO SSSSS HHHHHHH III BBBBBB AA AA -// TTT OO OO SS HH HH III BB BB AAAAAAA -// TTT OOOO0 SSSSS HH HH IIIII BBBBBB AA AA - -// Toshiba A/C support added by David Conran - // Constants const uint8_t kToshibaAcAuto = 0; const uint8_t kToshibaAcCool = 1; @@ -30,6 +31,8 @@ const uint8_t kToshibaAcDry = 2; const uint8_t kToshibaAcHeat = 3; const uint8_t kToshibaAcPower = 4; const uint8_t kToshibaAcFanAuto = 0; +const uint8_t kToshibaAcFanMin = 1; +const uint8_t kToshibaAcFanMed = 3; const uint8_t kToshibaAcFanMax = 5; const uint8_t kToshibaAcMinTemp = 17; // 17C const uint8_t kToshibaAcMaxTemp = 30; // 30C @@ -47,34 +50,35 @@ const uint8_t kToshibaAcMaxTemp = 30; // 30C class IRToshibaAC { public: - explicit IRToshibaAC(uint16_t pin); + explicit IRToshibaAC(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); - void stateReset(); + void stateReset(void); #if SEND_TOSHIBA_AC void send(const uint16_t repeat = kToshibaACMinRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } #endif // SEND_TOSHIBA_AC - void begin(); - void on(); - void off(); - void setPower(bool state); - bool getPower(); - void setTemp(uint8_t temp); - uint8_t getTemp(); - void setFan(uint8_t fan); - uint8_t getFan(); - void setMode(uint8_t mode); - uint8_t getMode(bool useRaw = false); - void setRaw(uint8_t newState[]); - uint8_t* getRaw(); + void begin(void); + void on(void); + void off(void); + void setPower(const bool on); + bool getPower(void); + void setTemp(const uint8_t degrees); + uint8_t getTemp(void); + void setFan(const uint8_t speed); + uint8_t getFan(void); + void setMode(const uint8_t mode); + uint8_t getMode(const bool useRaw = false); + void setRaw(const uint8_t newState[]); + uint8_t* getRaw(void); static bool validChecksum(const uint8_t state[], const uint16_t length = kToshibaACStateLength); uint8_t convertMode(const stdAc::opmode_t mode); uint8_t convertFan(const stdAc::fanspeed_t speed); -#ifdef ARDUINO - String toString(); -#else - std::string toString(); -#endif + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + stdAc::state_t toCommon(void); + String toString(void); #ifndef UNIT_TEST private: diff --git a/lib/IRremoteESP8266-2.6.5/src/ir_Trotec.cpp b/lib/IRremoteESP8266-2.6.5/src/ir_Trotec.cpp new file mode 100644 index 000000000..281779f62 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.5/src/ir_Trotec.cpp @@ -0,0 +1,296 @@ +// Copyright 2017 stufisher +// Copyright 2019 crankyoldgit + +#include "ir_Trotec.h" +#include +#ifndef UNIT_TEST +#include +#endif +#include "IRremoteESP8266.h" +#include "IRutils.h" + +// Constants +const uint16_t kTrotecHdrMark = 5952; +const uint16_t kTrotecHdrSpace = 7364; +const uint16_t kTrotecBitMark = 592; +const uint16_t kTrotecOneSpace = 1560; +const uint16_t kTrotecZeroSpace = 592; +const uint16_t kTrotecGap = 6184; +const uint16_t kTrotecGapEnd = 1500; // made up value + +using irutils::addBoolToString; +using irutils::addFanToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addTempToString; + +#if SEND_TROTEC + +void IRsend::sendTrotec(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes < kTrotecStateLength) return; + + for (uint16_t r = 0; r <= repeat; r++) { + sendGeneric(kTrotecHdrMark, kTrotecHdrSpace, kTrotecBitMark, + kTrotecOneSpace, kTrotecBitMark, kTrotecZeroSpace, + kTrotecBitMark, kTrotecGap, data, nbytes, 36, false, + 0, // Repeats handled elsewhere + 50); + // More footer + enableIROut(36); + mark(kTrotecBitMark); + space(kTrotecGapEnd); + } +} +#endif // SEND_TROTEC + +IRTrotecESP::IRTrotecESP(const uint16_t pin, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { this->stateReset(); } + +void IRTrotecESP::begin(void) { _irsend.begin(); } + +#if SEND_TROTEC +void IRTrotecESP::send(const uint16_t repeat) { + this->checksum(); + _irsend.sendTrotec(remote_state, kTrotecStateLength, repeat); +} +#endif // SEND_TROTEC + +uint8_t IRTrotecESP::calcChecksum(const uint8_t state[], + const uint16_t length) { + return sumBytes(state + 2, length - 3); +} + +bool IRTrotecESP::validChecksum(const uint8_t state[], const uint16_t length) { + return state[length - 1] == calcChecksum(state, length); +} + +void IRTrotecESP::checksum(void) { + remote_state[kTrotecStateLength - 1] = sumBytes(remote_state + 2, + kTrotecStateLength - 3); +} + +void IRTrotecESP::stateReset(void) { + for (uint8_t i = 2; i < kTrotecStateLength; i++) remote_state[i] = 0x0; + + remote_state[0] = kTrotecIntro1; + remote_state[1] = kTrotecIntro2; + + this->setPower(false); + this->setTemp(kTrotecDefTemp); + this->setSpeed(kTrotecFanMed); + this->setMode(kTrotecAuto); +} + +uint8_t* IRTrotecESP::getRaw(void) { + this->checksum(); + return remote_state; +} + +void IRTrotecESP::setRaw(const uint8_t state[]) { + for (uint16_t i = 0; i < kTrotecStateLength; i++) remote_state[i] = state[i]; +} + +void IRTrotecESP::setPower(const bool on) { + if (on) + remote_state[2] |= kTrotecPowerBit; + else + remote_state[2] &= ~kTrotecPowerBit; +} + +bool IRTrotecESP::getPower(void) { return remote_state[2] & kTrotecPowerBit; } + +void IRTrotecESP::setSpeed(const uint8_t fan) { + uint8_t speed = std::min(fan, kTrotecFanHigh); + remote_state[2] = (remote_state[2] & 0b11001111) | (speed << 4); +} + +uint8_t IRTrotecESP::getSpeed(void) { + return (remote_state[2] & 0b00110000) >> 4; +} + +void IRTrotecESP::setMode(const uint8_t mode) { + switch (mode) { + case kTrotecAuto: + case kTrotecCool: + case kTrotecDry: + case kTrotecFan: + remote_state[2] = (remote_state[2] & 0b11111100) | mode; + return; + default: + this->setMode(kTrotecAuto); + } +} + +uint8_t IRTrotecESP::getMode(void) { return remote_state[2] & 0b00000011; } + +void IRTrotecESP::setTemp(const uint8_t celsius) { + uint8_t temp = std::max(celsius, kTrotecMinTemp); + temp = std::min(temp, kTrotecMaxTemp); + remote_state[3] = (remote_state[3] & 0x80) | (temp - kTrotecMinTemp); +} + +uint8_t IRTrotecESP::getTemp(void) { + return (remote_state[3] & 0b01111111) + kTrotecMinTemp; +} + +void IRTrotecESP::setSleep(const bool on) { + if (on) + remote_state[3] |= kTrotecSleepBit; + else + remote_state[3] &= ~kTrotecSleepBit; +} + +bool IRTrotecESP::getSleep(void) { return remote_state[3] & kTrotecSleepBit; } + +void IRTrotecESP::setTimer(const uint8_t timer) { + if (timer) + remote_state[5] |= kTrotecTimerBit; + else + remote_state[5] &= ~kTrotecTimerBit; + remote_state[6] = (timer > kTrotecMaxTimer) ? kTrotecMaxTimer : timer; +} + +uint8_t IRTrotecESP::getTimer(void) { return remote_state[6]; } + +// Convert a standard A/C mode into its native mode. +uint8_t IRTrotecESP::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kTrotecCool; + case stdAc::opmode_t::kDry: + return kTrotecDry; + case stdAc::opmode_t::kFan: + return kTrotecFan; + // Note: No Heat mode. + default: + return kTrotecAuto; + } +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRTrotecESP::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: + return kTrotecFanLow; + case stdAc::fanspeed_t::kMedium: + return kTrotecFanMed; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: + return kTrotecFanHigh; + default: + return kTrotecFanMed; + } +} + + +// Convert a native mode to it's common equivalent. +stdAc::opmode_t IRTrotecESP::toCommonMode(const uint8_t mode) { + switch (mode) { + case kTrotecCool: return stdAc::opmode_t::kCool; + case kTrotecDry: return stdAc::opmode_t::kDry; + case kTrotecFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRTrotecESP::toCommonFanSpeed(const uint8_t spd) { + switch (spd) { + case kTrotecFanHigh: return stdAc::fanspeed_t::kMax; + case kTrotecFanMed: return stdAc::fanspeed_t::kMedium; + case kTrotecFanLow: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRTrotecESP::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::TROTEC; + result.power = this->getPower(); + result.mode = this->toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(); + result.fanspeed = this->toCommonFanSpeed(this->getSpeed()); + result.sleep = this->getSleep() ? 0 : -1; + // Not supported. + result.model = -1; // Not supported. + result.swingv = stdAc::swingv_t::kOff; + result.swingh = stdAc::swingh_t::kOff; + result.turbo = false; + result.light = false; + result.filter = false; + result.econo = false; + result.quiet = false; + result.clean = false; + result.beep = false; + result.clock = -1; + return result; +} + +// Convert the internal state into a human readable string. +String IRTrotecESP::toString(void) { + String result = ""; + result.reserve(100); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(getPower(), F("Power"), false); + result += addModeToString(getMode(), kTrotecAuto, kTrotecCool, kTrotecAuto, + kTrotecDry, kTrotecFan); + result += addTempToString(getTemp()); + result += addFanToString(getSpeed(), kTrotecFanHigh, kTrotecFanLow, + kTrotecFanHigh, kTrotecFanHigh, kTrotecFanMed); + result += addBoolToString(getSleep(), F("Sleep")); + return result; +} + +#if DECODE_TROTEC +// Decode the supplied Trotec message. +// +// Args: +// results: Ptr to the data to decode and where to store the decode result. +// nbits: The number of data bits to expect. Typically kTrotecBits. +// strict: Flag indicating if we should perform strict matching. +// Returns: +// boolean: True if it can decode it, false if it can't. +// +// Status: BETA / Probably works. Untested on real devices. +// +// Ref: +bool IRrecv::decodeTrotec(decode_results *results, const uint16_t nbits, + const bool strict) { + if (results->rawlen < 2 * nbits + kHeader + 2 * kFooter - 1) + return false; // Can't possibly be a valid Samsung A/C message. + if (strict && nbits != kTrotecBits) return false; + + uint16_t offset = kStartOffset; + uint16_t used; + // Header + Data + Footer #1 + used = matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, nbits, + kTrotecHdrMark, kTrotecHdrSpace, + kTrotecBitMark, kTrotecOneSpace, + kTrotecBitMark, kTrotecZeroSpace, + kTrotecBitMark, kTrotecGap, true, + _tolerance, 0, false); + if (used == 0) return false; + offset += used; + + // Footer #2 + if (!matchMark(results->rawbuf[offset++], kTrotecBitMark)) return false; + if (offset <= results->rawlen && + !matchAtLeast(results->rawbuf[offset++], kTrotecGapEnd)) return false; + // Compliance + // Verify we got a valid checksum. + if (strict && !IRTrotecESP::validChecksum(results->state)) return false; + // Success + results->decode_type = TROTEC; + results->bits = nbits; + // No need to record the state as we stored it as we decoded it. + // As we use result->state, we don't record value, address, or command as it + // is a union data type. + return true; +} +#endif // DECODE_TROTEC diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Trotec.h b/lib/IRremoteESP8266-2.6.5/src/ir_Trotec.h similarity index 63% rename from lib/IRremoteESP8266-2.6.0/src/ir_Trotec.h rename to lib/IRremoteESP8266-2.6.5/src/ir_Trotec.h index dfbc26c07..98bf3795c 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Trotec.h +++ b/lib/IRremoteESP8266-2.6.5/src/ir_Trotec.h @@ -1,8 +1,12 @@ // Copyright 2017 stufisher +// Copyright 2019 crankyoldgit #ifndef IR_TROTEC_H_ #define IR_TROTEC_H_ +#ifndef UNIT_TEST +#include +#endif #include "IRremoteESP8266.h" #include "IRsend.h" #ifdef UNIT_TEST @@ -55,35 +59,43 @@ const uint8_t kTrotecMaxTimer = 23; class IRTrotecESP { public: - explicit IRTrotecESP(uint16_t pin); + explicit IRTrotecESP(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); #if SEND_TROTEC void send(const uint16_t repeat = kTrotecDefaultRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } #endif // SEND_TROTEC - void begin(); + void begin(void); void setPower(const bool state); - bool getPower(); + bool getPower(void); void setTemp(const uint8_t celsius); - uint8_t getTemp(); + uint8_t getTemp(void); void setSpeed(const uint8_t fan); - uint8_t getSpeed(); + uint8_t getSpeed(void); - uint8_t getMode(); + uint8_t getMode(void); void setMode(const uint8_t mode); - bool getSleep(); - void setSleep(bool sleep); + bool getSleep(void); + void setSleep(const bool on); - uint8_t getTimer(); + uint8_t getTimer(void); void setTimer(const uint8_t timer); - uint8_t* getRaw(); - + uint8_t* getRaw(void); + void setRaw(const uint8_t state[]); + static bool validChecksum(const uint8_t state[], + const uint16_t length = kTrotecStateLength); uint8_t convertMode(const stdAc::opmode_t mode); uint8_t convertFan(const stdAc::fanspeed_t speed); + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + stdAc::state_t toCommon(void); + String toString(void); #ifndef UNIT_TEST private: @@ -92,8 +104,10 @@ class IRTrotecESP { IRsendTest _irsend; #endif uint8_t remote_state[kTrotecStateLength]; - void stateReset(); - void checksum(); + static uint8_t calcChecksum(const uint8_t state[], + const uint16_t length = kTrotecStateLength); + void stateReset(void); + void checksum(void); }; #endif // IR_TROTEC_H_ diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Vestel.cpp b/lib/IRremoteESP8266-2.6.5/src/ir_Vestel.cpp similarity index 71% rename from lib/IRremoteESP8266-2.6.0/src/ir_Vestel.cpp rename to lib/IRremoteESP8266-2.6.5/src/ir_Vestel.cpp index 1fbb822cf..1374c6b59 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Vestel.cpp +++ b/lib/IRremoteESP8266-2.6.5/src/ir_Vestel.cpp @@ -1,12 +1,12 @@ // Copyright 2018 Erdem U. Altinyurt // Copyright 2019 David Conran +// Vestel added by Erdem U. Altinyurt + #include "ir_Vestel.h" #include #ifndef UNIT_TEST #include -#else -#include #endif #include "IRrecv.h" #include "IRremoteESP8266.h" @@ -14,14 +14,6 @@ #include "IRutils.h" #include "ir_Haier.h" -// VV VV EEEEEEE SSSSS TTTTTTTT EEEEEEE LL -// VV VV EE S TT EE LL -// VV VV EEEEE SSSS TT EEEEE LL -// VV VV EE S TT EE LL -// VVV EEEEEEE SSSSS TT EEEEEEE LLLLLLL - -// Vestel added by Erdem U. Altinyurt - // Equipment it seems compatible with: // * Vestel AC Model BIOX CXP-9 (9K BTU) // * @@ -29,6 +21,13 @@ // Ref: // None. Totally reverse engineered. +using irutils::addBoolToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addTempToString; +using irutils::minsToString; + #if SEND_VESTEL_AC // Send a Vestel message // @@ -53,10 +52,12 @@ void IRsend::sendVestelAc(const uint64_t data, const uint16_t nbits, // Code to emulate Vestel A/C IR remote control unit. // Initialise the object. -IRVestelAc::IRVestelAc(uint16_t pin) : _irsend(pin) { stateReset(); } +IRVestelAc::IRVestelAc(const uint16_t pin, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { this->stateReset(); } // Reset the state of the remote to a known good state/sequence. -void IRVestelAc::stateReset() { +void IRVestelAc::stateReset(void) { // Power On, Mode Auto, Fan Auto, Temp = 25C/77F remote_state = kVestelAcStateDefault; remote_time_state = kVestelAcTimeStateDefault; @@ -64,14 +65,14 @@ void IRVestelAc::stateReset() { } // Configure the pin for output. -void IRVestelAc::begin() { +void IRVestelAc::begin(void) { _irsend.begin(); } #if SEND_VESTEL_AC // Send the current desired state to the IR LED. -void IRVestelAc::send() { - checksum(); // Ensure correct checksum before sending. +void IRVestelAc::send(void) { + this->checksum(); // Ensure correct checksum before sending. uint64_t code_to_send; if (use_time_state) code_to_send = remote_time_state; @@ -82,14 +83,14 @@ void IRVestelAc::send() { #endif // SEND_VESTEL_AC // Return the internal state date of the remote. -uint64_t IRVestelAc::getRaw() { - checksum(); +uint64_t IRVestelAc::getRaw(void) { + this->checksum(); if (use_time_state) return remote_time_state; return remote_state; } // Override the internal state with the new state. -void IRVestelAc::setRaw(uint8_t* newState) { +void IRVestelAc::setRaw(const uint8_t* newState) { uint64_t upState = 0; for (int i = 0; i < 7; i++) upState |= static_cast(newState[i]) << (i * 8); @@ -109,15 +110,15 @@ void IRVestelAc::setRaw(const uint64_t newState) { } // Set the requested power state of the A/C to on. -void IRVestelAc::on() { setPower(true); } +void IRVestelAc::on(void) { setPower(true); } // Set the requested power state of the A/C to off. -void IRVestelAc::off() { setPower(false); } +void IRVestelAc::off(void) { setPower(false); } // Set the requested power state of the A/C. -void IRVestelAc::setPower(const bool state) { +void IRVestelAc::setPower(const bool on) { remote_state &= ~((uint64_t)0xF << kVestelAcPowerOffset); - if (state) + if (on) remote_state |= ((uint64_t)0xF << kVestelAcPowerOffset); else remote_state |= ((uint64_t)0xC << kVestelAcPowerOffset); @@ -125,7 +126,7 @@ void IRVestelAc::setPower(const bool state) { } // Return the requested power state of the A/C. -bool IRVestelAc::getPower() { +bool IRVestelAc::getPower(void) { return (remote_state >> kVestelAcPowerOffset == 0xF); } @@ -165,14 +166,14 @@ void IRVestelAc::setFan(const uint8_t fan) { } // Return the requested state of the unit's fan. -uint8_t IRVestelAc::getFan() { +uint8_t IRVestelAc::getFan(void) { return (remote_state >> kVestelAcFanOffset) & 0xF; } // Get the requested climate operation mode of the a/c unit. // Returns: // A uint8_t containing the A/C mode. -uint8_t IRVestelAc::getMode() { +uint8_t IRVestelAc::getMode(void) { return (remote_state >> kVestelAcModeOffset) & 0xF; } @@ -313,53 +314,54 @@ uint16_t IRVestelAc::getOffTimer(void) { } // Set the Sleep state of the A/C. -void IRVestelAc::setSleep(const bool state) { +void IRVestelAc::setSleep(const bool on) { remote_state &= ~((uint64_t)0xF << kVestelAcTurboSleepOffset); - remote_state |= (uint64_t)(state ? kVestelAcSleep : kVestelAcNormal) + remote_state |= (uint64_t)(on ? kVestelAcSleep : kVestelAcNormal) << kVestelAcTurboSleepOffset; use_time_state = false; } // Return the Sleep state of the A/C. -bool IRVestelAc::getSleep() { +bool IRVestelAc::getSleep(void) { return ((remote_state >> kVestelAcTurboSleepOffset) & 0xF) == kVestelAcSleep; } // Set the Turbo state of the A/C. -void IRVestelAc::setTurbo(const bool state) { +void IRVestelAc::setTurbo(const bool on) { remote_state &= ~((uint64_t)0xF << kVestelAcTurboSleepOffset); - remote_state |= (uint64_t)(state ? kVestelAcTurbo : kVestelAcNormal) + remote_state |= (uint64_t)(on ? kVestelAcTurbo : kVestelAcNormal) << kVestelAcTurboSleepOffset; use_time_state = false; } // Return the Turbo state of the A/C. -bool IRVestelAc::getTurbo() { +bool IRVestelAc::getTurbo(void) { return ((remote_state >> kVestelAcTurboSleepOffset) & 0xF) == kVestelAcTurbo; } // Set the Ion state of the A/C. -void IRVestelAc::setIon(const bool state) { +void IRVestelAc::setIon(const bool on) { remote_state &= ~((uint64_t)0x1 << kVestelAcIonOffset); - remote_state |= (uint64_t)(state ? 1 : 0) << kVestelAcIonOffset; + remote_state |= (uint64_t)(on ? 1 : 0) << kVestelAcIonOffset; use_time_state = false; } // Return the Ion state of the A/C. -bool IRVestelAc::getIon() { return (remote_state >> kVestelAcIonOffset) & 1; } +bool IRVestelAc::getIon(void) { + return (remote_state >> kVestelAcIonOffset) & 1; +} // Set the Swing Roaming state of the A/C. -void IRVestelAc::setSwing(const bool state) { +void IRVestelAc::setSwing(const bool on) { remote_state &= ~((uint64_t)0xF << kVestelAcSwingOffset); - remote_state |= (uint64_t)(state ? kVestelAcSwing : 0xF) - << kVestelAcSwingOffset; + remote_state |= (uint64_t)(on ? kVestelAcSwing : 0xF) << kVestelAcSwingOffset; use_time_state = false; } // Return the Swing Roaming state of the A/C. -bool IRVestelAc::getSwing() { +bool IRVestelAc::getSwing(void) { return ((remote_state >> kVestelAcSwingOffset) & 0xF) == kVestelAcSwing; } @@ -385,22 +387,23 @@ uint8_t IRVestelAc::calcChecksum(const uint64_t state) { // Returns: // A boolean. bool IRVestelAc::validChecksum(const uint64_t state) { - return (((state >> kVestelAcChecksumOffset) & 0xFF) == calcChecksum(state)); + return (((state >> kVestelAcChecksumOffset) & 0xFF) == + IRVestelAc::calcChecksum(state)); } // Calculate & set the checksum for the current internal state of the remote. -void IRVestelAc::checksum() { +void IRVestelAc::checksum(void) { // Stored the checksum value in the last byte. remote_state &= ~((uint64_t)0xFF << kVestelAcChecksumOffset); - remote_state |= (uint64_t)calcChecksum(remote_state) + remote_state |= (uint64_t)this->calcChecksum(remote_state) << kVestelAcChecksumOffset; remote_time_state &= ~((uint64_t)0xFF << kVestelAcChecksumOffset); - remote_time_state |= (uint64_t)calcChecksum(remote_time_state) + remote_time_state |= (uint64_t)this->calcChecksum(remote_time_state) << kVestelAcChecksumOffset; } -bool IRVestelAc::isTimeCommand() { +bool IRVestelAc::isTimeCommand(void) { return (remote_state >> kVestelAcPowerOffset == 0x00 || use_time_state); } @@ -437,89 +440,103 @@ uint8_t IRVestelAc::convertFan(const stdAc::fanspeed_t speed) { } } +// Convert a native mode to it's common equivalent. +stdAc::opmode_t IRVestelAc::toCommonMode(const uint8_t mode) { + switch (mode) { + case kVestelAcCool: return stdAc::opmode_t::kCool; + case kVestelAcHeat: return stdAc::opmode_t::kHeat; + case kVestelAcDry: return stdAc::opmode_t::kDry; + case kVestelAcFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRVestelAc::toCommonFanSpeed(const uint8_t spd) { + switch (spd) { + case kVestelAcFanHigh: return stdAc::fanspeed_t::kMax; + case kVestelAcFanMed: return stdAc::fanspeed_t::kMedium; + case kVestelAcFanLow: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRVestelAc::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::VESTEL_AC; + result.model = -1; // Not supported. + result.power = this->getPower(); + result.mode = this->toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(); + result.fanspeed = this->toCommonFanSpeed(this->getFan()); + result.swingv = this->getSwing() ? stdAc::swingv_t::kAuto : + stdAc::swingv_t::kOff; + result.turbo = this->getTurbo(); + result.filter = this->getIon(); + result.sleep = this->getSleep() ? 0 : -1; + // Not supported. + result.swingh = stdAc::swingh_t::kOff; + result.light = false; + result.econo = false; + result.quiet = false; + result.clean = false; + result.beep = false; + result.clock = -1; + return result; +} + // Convert the internal state into a human readable string. -#ifdef ARDUINO -String IRVestelAc::toString() { +String IRVestelAc::toString(void) { String result = ""; -#else -std::string IRVestelAc::toString() { - std::string result = ""; -#endif // ARDUINO - if (isTimeCommand()) { - result += F("Time: "); - result += IRHaierAC::timeToString(getTime()); - - result += F(", Timer: "); - result += isTimerActive() ? IRHaierAC::timeToString(getTimer()) : F("Off"); - - result += F(", On Timer: "); - result += (isOnTimerActive() && !isTimerActive()) - ? IRHaierAC::timeToString(getOnTimer()) - : F("Off"); - - result += F(", Off Timer: "); - result += - isOffTimerActive() ? IRHaierAC::timeToString(getOffTimer()) : F("Off"); + result.reserve(100); // Reserve some heap for the string to reduce fragging. + if (this->isTimeCommand()) { + result += addLabeledString(minsToString(getTime()), F("Time"), false); + result += addLabeledString( + isTimerActive() ? minsToString(getTimer()) : F("Off"), + F("Timer")); + result += addLabeledString( + (isOnTimerActive() && !isTimerActive()) ? + minsToString(this->getOnTimer()) : F("Off"), + F("On Timer")); + result += addLabeledString( + isOffTimerActive() ? minsToString(getOffTimer()) : F("Off"), + F("Off Timer")); return result; } // Not a time command, it's a normal command. - result += F("Power: "); - result += (getPower() ? F("On") : F("Off")); - result += F(", Mode: "); - result += uint64ToString(getMode()); - switch (getMode()) { - case kVestelAcAuto: - result += F(" (AUTO)"); - break; - case kVestelAcCool: - result += F(" (COOL)"); - break; - case kVestelAcHeat: - result += F(" (HEAT)"); - break; - case kVestelAcDry: - result += F(" (DRY)"); - break; - case kVestelAcFan: - result += F(" (FAN)"); - break; - default: - result += F(" (UNKNOWN)"); - } - result += F(", Temp: "); - result += uint64ToString(getTemp()); - result += F("C, Fan: "); - result += uint64ToString(getFan()); - switch (getFan()) { + result += addBoolToString(getPower(), F("Power"), false); + result += addModeToString(getMode(), kVestelAcAuto, kVestelAcCool, + kVestelAcHeat, kVestelAcDry, kVestelAcFan); + result += addTempToString(getTemp()); + result += addIntToString(getFan(), F("Fan")); + switch (this->getFan()) { case kVestelAcFanAuto: - result += F(" (AUTO)"); + result += F(" (Auto)"); break; case kVestelAcFanLow: - result += F(" (LOW)"); + result += F(" (Low)"); break; case kVestelAcFanMed: - result += F(" (MEDIUM)"); + result += F(" (Medium)"); break; case kVestelAcFanHigh: - result += F(" (HIGH)"); + result += F(" (High)"); break; case kVestelAcFanAutoCool: - result += F(" (AUTO COOL)"); + result += F(" (Auto Cool)"); break; case kVestelAcFanAutoHot: - result += F(" (AUTO HOT)"); + result += F(" (Auto Hot)"); break; default: result += F(" (UNKNOWN)"); } - result += F(", Sleep: "); - result += (getSleep() ? F("On") : F("Off")); - result += F(", Turbo: "); - result += (getTurbo() ? F("On") : F("Off")); - result += F(", Ion: "); - result += (getIon() ? F("On") : F("Off")); - result += F(", Swing: "); - result += (getSwing() ? F("On") : F("Off")); + result += addBoolToString(getSleep(), F("Sleep")); + result += addBoolToString(getTurbo(), F("Turbo")); + result += addBoolToString(getIon(), F("Ion")); + result += addBoolToString(getSwing(), F("Swing")); return result; } @@ -535,8 +552,8 @@ std::string IRVestelAc::toString() { // // Status: Alpha / Needs testing against a real device. // -bool IRrecv::decodeVestelAc(decode_results* results, uint16_t nbits, - bool strict) { +bool IRrecv::decodeVestelAc(decode_results* results, const uint16_t nbits, + const bool strict) { if (nbits % 8 != 0) // nbits has to be a multiple of nr. of bits in a byte. return false; @@ -550,26 +567,17 @@ bool IRrecv::decodeVestelAc(decode_results* results, uint16_t nbits, if (nbits > sizeof(data) * 8) return false; // We can't possibly capture a Vestel packet that big. - // Header - if (!matchMark(results->rawbuf[offset++], kVestelAcHdrMark)) return false; - if (!matchSpace(results->rawbuf[offset++], kVestelAcHdrSpace)) return false; - - // Data (Normal) - match_result_t data_result = - matchData(&(results->rawbuf[offset]), nbits, kVestelAcBitMark, - kVestelAcOneSpace, kVestelAcBitMark, kVestelAcZeroSpace, - kVestelAcTolerance, kMarkExcess, false); - - if (data_result.success == false) return false; - offset += data_result.used; - data = data_result.data; - - // Footer - if (!matchMark(results->rawbuf[offset++], kVestelAcBitMark)) return false; - + // Match Header + Data + Footer + if (!matchGeneric(results->rawbuf + offset, &data, + results->rawlen - offset, nbits, + kVestelAcHdrMark, kVestelAcHdrSpace, + kVestelAcBitMark, kVestelAcOneSpace, + kVestelAcBitMark, kVestelAcZeroSpace, + kVestelAcBitMark, 0, false, + kVestelAcTolerance, kMarkExcess, false)) return false; // Compliance if (strict) - if (!IRVestelAc::validChecksum(data_result.data)) return false; + if (!IRVestelAc::validChecksum(data)) return false; // Success results->decode_type = VESTEL_AC; diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Vestel.h b/lib/IRremoteESP8266-2.6.5/src/ir_Vestel.h similarity index 83% rename from lib/IRremoteESP8266-2.6.0/src/ir_Vestel.h rename to lib/IRremoteESP8266-2.6.5/src/ir_Vestel.h index ab04e8b35..f60c031aa 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Vestel.h +++ b/lib/IRremoteESP8266-2.6.5/src/ir_Vestel.h @@ -1,6 +1,9 @@ // Copyright 2018 Erdem U. Altinyurt // Copyright 2019 David Conran +// Supports: +// Brand: Vestel, Model: BIOX CXP-9 A/C (9K BTU) + #ifndef IR_VESTEL_H_ #define IR_VESTEL_H_ @@ -8,8 +11,6 @@ #include #ifdef ARDUINO #include -#else -#include #endif #include "IRremoteESP8266.h" #include "IRsend.h" @@ -17,12 +18,6 @@ #include "IRsend_test.h" #endif -// VV VV EEEEEEE SSSSS TTTTTTTT EEEEEEE LL -// VV VV EE S TT EE LL -// VV VV EEEEE SSSS TT EEEEE LL -// VV VV EE S TT EE LL -// VVV EEEEEEE SSSSS TT EEEEEEE LLLLLLL - // Vestel added by Erdem U. Altinyurt // Structure of a Command message (56 bits) @@ -31,7 +26,7 @@ // Swing: 4 bits. (auto 0xA, stop 0xF) // turbo_sleep_normal: 4bits. (normal 0x1, sleep 0x3, turbo 0x7) // Unused: 8 bits. (0x00) -// Temperature: 4 bits. (Celcius, but offset by -16 degrees. e.g. 0x0 = 16C) +// Temperature: 4 bits. (Celsius, but offset by -16 degrees. e.g. 0x0 = 16C) // Fan Speed: 4 bits (auto 0x1, low 0x5, mid 0x9, high 0xB, 0xD auto hot, // 0xC auto cool) // Mode: 3 bits. (auto 0x0, cold 0x1, dry 0x2, fan 0x3, hot 0x4) @@ -108,17 +103,19 @@ const uint64_t kVestelAcTimeStateDefault = 0x201ULL; class IRVestelAc { public: - explicit IRVestelAc(uint16_t pin); + explicit IRVestelAc(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); - void stateReset(); + void stateReset(void); #if SEND_VESTEL_AC - void send(); + void send(void); + uint8_t calibrate(void) { return _irsend.calibrate(); } #endif // SEND_VESTEL_AC void begin(void); void on(void); void off(void); - void setPower(const bool state); - bool getPower(); + void setPower(const bool on); + bool getPower(void); void setAuto(const int8_t autoLevel); void setTimer(const uint16_t minutes); uint16_t getTimer(void); @@ -134,17 +131,17 @@ class IRVestelAc { uint8_t getFan(void); void setMode(const uint8_t mode); uint8_t getMode(void); - void setRaw(uint8_t* newState); + void setRaw(const uint8_t* newState); void setRaw(const uint64_t newState); uint64_t getRaw(void); static bool validChecksum(const uint64_t state); - void setSwing(const bool state); + void setSwing(const bool on); bool getSwing(void); - void setSleep(const bool state); + void setSleep(const bool on); bool getSleep(void); - void setTurbo(const bool state); + void setTurbo(const bool on); bool getTurbo(void); - void setIon(const bool state); + void setIon(const bool on); bool getIon(void); bool isTimeCommand(void); bool isOnTimerActive(void); @@ -154,13 +151,12 @@ class IRVestelAc { bool isTimerActive(void); void setTimerActive(const bool on); static uint8_t calcChecksum(const uint64_t state); - uint8_t convertMode(const stdAc::opmode_t mode); - uint8_t convertFan(const stdAc::fanspeed_t speed); -#ifdef ARDUINO - String toString(); -#else - std::string toString(); -#endif + static uint8_t convertMode(const stdAc::opmode_t mode); + static uint8_t convertFan(const stdAc::fanspeed_t speed); + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + stdAc::state_t toCommon(void); + String toString(void); #ifndef UNIT_TEST private: @@ -171,7 +167,7 @@ class IRVestelAc { uint64_t remote_state; uint64_t remote_time_state; bool use_time_state; - void checksum(); + void checksum(void); }; #endif // IR_VESTEL_H_ diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Whirlpool.cpp b/lib/IRremoteESP8266-2.6.5/src/ir_Whirlpool.cpp similarity index 62% rename from lib/IRremoteESP8266-2.6.0/src/ir_Whirlpool.cpp rename to lib/IRremoteESP8266-2.6.5/src/ir_Whirlpool.cpp index 048c1a1eb..92a9b2bb3 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Whirlpool.cpp +++ b/lib/IRremoteESP8266-2.6.5/src/ir_Whirlpool.cpp @@ -24,14 +24,8 @@ #include "IRsend.h" #include "IRutils.h" -// WW WW HH HH IIIII RRRRRR LL PPPPPP OOOOO OOOOO LL -// WW WW HH HH III RR RR LL PP PP OO OO OO OO LL -// WW W WW HHHHHHH III RRRRRR LL PPPPPP OO OO OO OO LL -// WW WWW WW HH HH III RR RR LL PP OO OO OO OO LL -// WW WW HH HH IIIII RR RR LLLLLLL PP OOOO0 OOOO0 LLLLLLL - // Constants -// Ref: https://github.com/markszabo/IRremoteESP8266/issues/509 +// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/509 const uint16_t kWhirlpoolAcHdrMark = 8950; const uint16_t kWhirlpoolAcHdrSpace = 4484; const uint16_t kWhirlpoolAcBitMark = 597; @@ -41,6 +35,14 @@ const uint16_t kWhirlpoolAcGap = 7920; const uint32_t kWhirlpoolAcMinGap = kDefaultMessageGap; // Just a guess. const uint8_t kWhirlpoolAcSections = 3; +using irutils::addBoolToString; +using irutils::addFanToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addTempToString; +using irutils::minsToString; + #if SEND_WHIRLPOOL_AC // Send a Whirlpool A/C message. // @@ -52,9 +54,9 @@ const uint8_t kWhirlpoolAcSections = 3; // Status: ALPHA / Untested. // // Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/509 -void IRsend::sendWhirlpoolAC(unsigned char data[], uint16_t nbytes, - uint16_t repeat) { +// https://github.com/crankyoldgit/IRremoteESP8266/issues/509 +void IRsend::sendWhirlpoolAC(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { if (nbytes < kWhirlpoolAcStateLength) return; // Not enough bytes to send a proper message. for (uint16_t r = 0; r <= repeat; r++) { @@ -85,17 +87,19 @@ void IRsend::sendWhirlpoolAC(unsigned char data[], uint16_t nbytes, // Decoding help from: // @redmusicxd, @josh929800, @raducostea -IRWhirlpoolAc::IRWhirlpoolAc(uint16_t pin) : _irsend(pin) { stateReset(); } +IRWhirlpoolAc::IRWhirlpoolAc(const uint16_t pin, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { this->stateReset(); } -void IRWhirlpoolAc::stateReset() { +void IRWhirlpoolAc::stateReset(void) { for (uint8_t i = 2; i < kWhirlpoolAcStateLength; i++) remote_state[i] = 0x0; remote_state[0] = 0x83; remote_state[1] = 0x06; remote_state[6] = 0x80; - _setTemp(kWhirlpoolAcAutoTemp); // Default to a sane value. + this->_setTemp(kWhirlpoolAcAutoTemp); // Default to a sane value. } -void IRWhirlpoolAc::begin() { _irsend.begin(); } +void IRWhirlpoolAc::begin(void) { _irsend.begin(); } bool IRWhirlpoolAc::validChecksum(uint8_t state[], const uint16_t length) { if (length > kWhirlpoolAcChecksumByte1 && @@ -128,13 +132,13 @@ void IRWhirlpoolAc::checksum(uint16_t length) { #if SEND_WHIRLPOOL_AC void IRWhirlpoolAc::send(const uint16_t repeat, const bool calcchecksum) { - if (calcchecksum) checksum(); + if (calcchecksum) this->checksum(); _irsend.sendWhirlpoolAC(remote_state, kWhirlpoolAcStateLength, repeat); } #endif // SEND_WHIRLPOOL_AC uint8_t *IRWhirlpoolAc::getRaw(const bool calcchecksum) { - if (calcchecksum) checksum(); + if (calcchecksum) this->checksum(); return remote_state; } @@ -143,7 +147,7 @@ void IRWhirlpoolAc::setRaw(const uint8_t new_code[], const uint16_t length) { remote_state[i] = new_code[i]; } -whirlpool_ac_remote_model_t IRWhirlpoolAc::getModel() { +whirlpool_ac_remote_model_t IRWhirlpoolAc::getModel(void) { if (remote_state[kWhirlpoolAcAltTempPos] & kWhirlpoolAcAltTempMask) return DG11J191; else @@ -160,13 +164,13 @@ void IRWhirlpoolAc::setModel(const whirlpool_ac_remote_model_t model) { default: remote_state[kWhirlpoolAcAltTempPos] &= ~kWhirlpoolAcAltTempMask; } - _setTemp(_desiredtemp); // Different models have different temp values. + this->_setTemp(_desiredtemp); // Different models have different temp values. } // Return the temp. offset in deg C for the current model. -int8_t IRWhirlpoolAc::getTempOffset() { - switch (getModel()) { - case DG11J191: +int8_t IRWhirlpoolAc::getTempOffset(void) { + switch (this->getModel()) { + case whirlpool_ac_remote_model_t::DG11J191: return -2; break; default: @@ -177,7 +181,7 @@ int8_t IRWhirlpoolAc::getTempOffset() { // Set the temp. in deg C void IRWhirlpoolAc::_setTemp(const uint8_t temp, const bool remember) { if (remember) _desiredtemp = temp; - int8_t offset = getTempOffset(); // Cache the min temp for the model. + int8_t offset = this->getTempOffset(); // Cache the min temp for the model. uint8_t newtemp = std::max((uint8_t)(kWhirlpoolAcMinTemp + offset), temp); newtemp = std::min((uint8_t)(kWhirlpoolAcMaxTemp + offset), newtemp); remote_state[kWhirlpoolAcTempPos] = @@ -187,23 +191,23 @@ void IRWhirlpoolAc::_setTemp(const uint8_t temp, const bool remember) { // Set the temp. in deg C void IRWhirlpoolAc::setTemp(const uint8_t temp) { - _setTemp(temp); - setSuper(false); // Changing temp cancels Super/Jet mode. - setCommand(kWhirlpoolAcCommandTemp); + this->_setTemp(temp); + this->setSuper(false); // Changing temp cancels Super/Jet mode. + this->setCommand(kWhirlpoolAcCommandTemp); } // Return the set temp. in deg C -uint8_t IRWhirlpoolAc::getTemp() { +uint8_t IRWhirlpoolAc::getTemp(void) { return ((remote_state[kWhirlpoolAcTempPos] & kWhirlpoolAcTempMask) >> 4) + - + kWhirlpoolAcMinTemp + getTempOffset(); + + kWhirlpoolAcMinTemp + this->getTempOffset(); } void IRWhirlpoolAc::_setMode(const uint8_t mode) { switch (mode) { case kWhirlpoolAcAuto: - setFan(kWhirlpoolAcFanAuto); - _setTemp(kWhirlpoolAcAutoTemp, false); - setSleep(false); // Cancel sleep mode when in auto/6thsense mode. + this->setFan(kWhirlpoolAcFanAuto); + this->_setTemp(kWhirlpoolAcAutoTemp, false); + this->setSleep(false); // Cancel sleep mode when in auto/6thsense mode. // FALL THRU case kWhirlpoolAcHeat: case kWhirlpoolAcCool: @@ -211,20 +215,20 @@ void IRWhirlpoolAc::_setMode(const uint8_t mode) { case kWhirlpoolAcFan: remote_state[kWhirlpoolAcModePos] &= ~kWhirlpoolAcModeMask; remote_state[kWhirlpoolAcModePos] |= mode; - setCommand(kWhirlpoolAcCommandMode); + this->setCommand(kWhirlpoolAcCommandMode); break; default: return; } - if (mode == kWhirlpoolAcAuto) setCommand(kWhirlpoolAcCommand6thSense); + if (mode == kWhirlpoolAcAuto) this->setCommand(kWhirlpoolAcCommand6thSense); } void IRWhirlpoolAc::setMode(const uint8_t mode) { - setSuper(false); // Changing mode cancels Super/Jet mode. - _setMode(mode); + this->setSuper(false); // Changing mode cancels Super/Jet mode. + this->_setMode(mode); } -uint8_t IRWhirlpoolAc::getMode() { +uint8_t IRWhirlpoolAc::getMode(void) { return remote_state[kWhirlpoolAcModePos] & kWhirlpoolAcModeMask; } @@ -236,13 +240,13 @@ void IRWhirlpoolAc::setFan(const uint8_t speed) { case kWhirlpoolAcFanHigh: remote_state[kWhirlpoolAcFanPos] = (remote_state[kWhirlpoolAcFanPos] & ~kWhirlpoolAcFanMask) | speed; - setSuper(false); // Changing fan speed cancels Super/Jet mode. - setCommand(kWhirlpoolAcCommandFanSpeed); + this->setSuper(false); // Changing fan speed cancels Super/Jet mode. + this->setCommand(kWhirlpoolAcCommandFanSpeed); break; } } -uint8_t IRWhirlpoolAc::getFan() { +uint8_t IRWhirlpoolAc::getFan(void) { return remote_state[kWhirlpoolAcFanPos] & kWhirlpoolAcFanMask; } @@ -257,7 +261,7 @@ void IRWhirlpoolAc::setSwing(const bool on) { setCommand(kWhirlpoolAcCommandSwing); } -bool IRWhirlpoolAc::getSwing() { +bool IRWhirlpoolAc::getSwing(void) { return (remote_state[kWhirlpoolAcFanPos] & kWhirlpoolAcSwing1Mask) && (remote_state[kWhirlpoolAcOffTimerPos] & kWhirlpoolAcSwing2Mask); } @@ -269,7 +273,7 @@ void IRWhirlpoolAc::setLight(const bool on) { remote_state[kWhirlpoolAcClockPos] |= kWhirlpoolAcLightMask; } -bool IRWhirlpoolAc::getLight() { +bool IRWhirlpoolAc::getLight(void) { return !(remote_state[kWhirlpoolAcClockPos] & kWhirlpoolAcLightMask); } @@ -292,49 +296,53 @@ bool IRWhirlpoolAc::isTimerEnabled(const uint16_t pos) { return remote_state[pos - 1] & kWhirlpoolAcTimerEnableMask; } -void IRWhirlpoolAc::enableTimer(const uint16_t pos, const bool state) { - if (state) +void IRWhirlpoolAc::enableTimer(const uint16_t pos, const bool on) { + if (on) remote_state[pos - 1] |= kWhirlpoolAcTimerEnableMask; else remote_state[pos - 1] &= ~kWhirlpoolAcTimerEnableMask; } void IRWhirlpoolAc::setClock(const uint16_t minspastmidnight) { - setTime(kWhirlpoolAcClockPos, minspastmidnight); + this->setTime(kWhirlpoolAcClockPos, minspastmidnight); } -uint16_t IRWhirlpoolAc::getClock() { return getTime(kWhirlpoolAcClockPos); } +uint16_t IRWhirlpoolAc::getClock(void) { + return this->getTime(kWhirlpoolAcClockPos); +} void IRWhirlpoolAc::setOffTimer(const uint16_t minspastmidnight) { - setTime(kWhirlpoolAcOffTimerPos, minspastmidnight); + this->setTime(kWhirlpoolAcOffTimerPos, minspastmidnight); } -uint16_t IRWhirlpoolAc::getOffTimer() { - return getTime(kWhirlpoolAcOffTimerPos); +uint16_t IRWhirlpoolAc::getOffTimer(void) { + return this->getTime(kWhirlpoolAcOffTimerPos); } -bool IRWhirlpoolAc::isOffTimerEnabled() { - return isTimerEnabled(kWhirlpoolAcOffTimerPos); +bool IRWhirlpoolAc::isOffTimerEnabled(void) { + return this->isTimerEnabled(kWhirlpoolAcOffTimerPos); } -void IRWhirlpoolAc::enableOffTimer(const bool state) { - enableTimer(kWhirlpoolAcOffTimerPos, state); - setCommand(kWhirlpoolAcCommandOffTimer); +void IRWhirlpoolAc::enableOffTimer(const bool on) { + this->enableTimer(kWhirlpoolAcOffTimerPos, on); + this->setCommand(kWhirlpoolAcCommandOffTimer); } void IRWhirlpoolAc::setOnTimer(const uint16_t minspastmidnight) { - setTime(kWhirlpoolAcOnTimerPos, minspastmidnight); + this->setTime(kWhirlpoolAcOnTimerPos, minspastmidnight); } -uint16_t IRWhirlpoolAc::getOnTimer() { return getTime(kWhirlpoolAcOnTimerPos); } - -bool IRWhirlpoolAc::isOnTimerEnabled() { - return isTimerEnabled(kWhirlpoolAcOnTimerPos); +uint16_t IRWhirlpoolAc::getOnTimer(void) { + return this->getTime(kWhirlpoolAcOnTimerPos); } -void IRWhirlpoolAc::enableOnTimer(const bool state) { - enableTimer(kWhirlpoolAcOnTimerPos, state); - setCommand(kWhirlpoolAcCommandOnTimer); +bool IRWhirlpoolAc::isOnTimerEnabled(void) { + return this->isTimerEnabled(kWhirlpoolAcOnTimerPos); +} + +void IRWhirlpoolAc::enableOnTimer(const bool on) { + this->enableTimer(kWhirlpoolAcOnTimerPos, on); + this->setCommand(kWhirlpoolAcCommandOnTimer); } void IRWhirlpoolAc::setPowerToggle(const bool on) { @@ -342,54 +350,54 @@ void IRWhirlpoolAc::setPowerToggle(const bool on) { remote_state[kWhirlpoolAcPowerTogglePos] |= kWhirlpoolAcPowerToggleMask; else remote_state[kWhirlpoolAcPowerTogglePos] &= ~kWhirlpoolAcPowerToggleMask; - setSuper(false); // Changing power cancels Super/Jet mode. - setCommand(kWhirlpoolAcCommandPower); + this->setSuper(false); // Changing power cancels Super/Jet mode. + this->setCommand(kWhirlpoolAcCommandPower); } -bool IRWhirlpoolAc::getPowerToggle() { +bool IRWhirlpoolAc::getPowerToggle(void) { return remote_state[kWhirlpoolAcPowerTogglePos] & kWhirlpoolAcPowerToggleMask; } -uint8_t IRWhirlpoolAc::getCommand() { +uint8_t IRWhirlpoolAc::getCommand(void) { return remote_state[kWhirlpoolAcCommandPos]; } void IRWhirlpoolAc::setSleep(const bool on) { if (on) { remote_state[kWhirlpoolAcSleepPos] |= kWhirlpoolAcSleepMask; - setFan(kWhirlpoolAcFanLow); + this->setFan(kWhirlpoolAcFanLow); } else { remote_state[kWhirlpoolAcSleepPos] &= ~kWhirlpoolAcSleepMask; } - setCommand(kWhirlpoolAcCommandSleep); + this->setCommand(kWhirlpoolAcCommandSleep); } -bool IRWhirlpoolAc::getSleep() { +bool IRWhirlpoolAc::getSleep(void) { return remote_state[kWhirlpoolAcSleepPos] & kWhirlpoolAcSleepMask; } // AKA Jet/Turbo mode. void IRWhirlpoolAc::setSuper(const bool on) { if (on) { - setFan(kWhirlpoolAcFanHigh); - switch (getMode()) { + this->setFan(kWhirlpoolAcFanHigh); + switch (this->getMode()) { case kWhirlpoolAcHeat: - setTemp(kWhirlpoolAcMaxTemp + getTempOffset()); + this->setTemp(kWhirlpoolAcMaxTemp + this->getTempOffset()); break; case kWhirlpoolAcCool: default: - setTemp(kWhirlpoolAcMinTemp + getTempOffset()); - setMode(kWhirlpoolAcCool); + this->setTemp(kWhirlpoolAcMinTemp + this->getTempOffset()); + this->setMode(kWhirlpoolAcCool); break; } remote_state[kWhirlpoolAcSuperPos] |= kWhirlpoolAcSuperMask; } else { remote_state[kWhirlpoolAcSuperPos] &= ~kWhirlpoolAcSuperMask; } - setCommand(kWhirlpoolAcCommandSuper); + this->setCommand(kWhirlpoolAcCommandSuper); } -bool IRWhirlpoolAc::getSuper() { +bool IRWhirlpoolAc::getSuper(void) { return remote_state[kWhirlpoolAcSuperPos] & kWhirlpoolAcSuperMask; } @@ -429,34 +437,60 @@ uint8_t IRWhirlpoolAc::convertFan(const stdAc::fanspeed_t speed) { } } -#ifdef ARDUINO -String IRWhirlpoolAc::timeToString(const uint16_t minspastmidnight) { - String result = ""; -#else -std::string IRWhirlpoolAc::timeToString(const uint16_t minspastmidnight) { - std::string result = ""; -#endif // ARDUINO - uint8_t hours = minspastmidnight / 60; - if (hours < 10) result += '0'; - result += uint64ToString(hours); - result += ':'; - uint8_t mins = minspastmidnight % 60; - if (mins < 10) result += '0'; - result += uint64ToString(mins); +// Convert a native mode to it's common equivalent. +stdAc::opmode_t IRWhirlpoolAc::toCommonMode(const uint8_t mode) { + switch (mode) { + case kWhirlpoolAcCool: return stdAc::opmode_t::kCool; + case kWhirlpoolAcHeat: return stdAc::opmode_t::kHeat; + case kWhirlpoolAcDry: return stdAc::opmode_t::kDry; + case kWhirlpoolAcFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRWhirlpoolAc::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kWhirlpoolAcFanHigh: return stdAc::fanspeed_t::kMax; + case kWhirlpoolAcFanMedium: return stdAc::fanspeed_t::kMedium; + case kWhirlpoolAcFanLow: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRWhirlpoolAc::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::WHIRLPOOL_AC; + result.model = this->getModel(); + result.power = this->getPowerToggle(); + result.mode = this->toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(); + result.fanspeed = this->toCommonFanSpeed(this->getFan()); + result.swingv = this->getSwing() ? stdAc::swingv_t::kAuto : + stdAc::swingv_t::kOff; + result.turbo = this->getSuper(); + result.light = this->getLight(); + result.sleep = this->getSleep() ? 0 : -1; + // Not supported. + result.swingh = stdAc::swingh_t::kOff; + result.quiet = false; + result.filter = false; + result.econo = false; + result.clean = false; + result.beep = false; + result.clock = -1; return result; } // Convert the internal state into a human readable string. -#ifdef ARDUINO -String IRWhirlpoolAc::toString() { +String IRWhirlpoolAc::toString(void) { String result = ""; -#else -std::string IRWhirlpoolAc::toString() { - std::string result = ""; -#endif // ARDUINO + result.reserve(200); // Reserve some heap for the string to reduce fragging. result += F("Model: "); - result += uint64ToString(getModel()); - switch (getModel()) { + result += uint64ToString(this->getModel()); + switch (this->getModel()) { case DG11J191: result += F(" (DG11J191)"); break; @@ -466,88 +500,26 @@ std::string IRWhirlpoolAc::toString() { default: result += F(" (UNKNOWN)"); } - result += F(", Power toggle: "); - if (getPowerToggle()) - result += F("On"); - else - result += F("Off"); - result += F(", Mode: "); - result += uint64ToString(getMode()); - switch (getMode()) { - case kWhirlpoolAcHeat: - result += F(" (HEAT)"); - break; - case kWhirlpoolAcAuto: - result += F(" (AUTO)"); - break; - case kWhirlpoolAcCool: - result += F(" (COOL)"); - break; - case kWhirlpoolAcDry: - result += F(" (DRY)"); - break; - case kWhirlpoolAcFan: - result += F(" (FAN)"); - break; - default: - result += F(" (UNKNOWN)"); - } - result += F(", Temp: "); - result += uint64ToString(getTemp()); - result += F("C, Fan: "); - result += uint64ToString(getFan()); - switch (getFan()) { - case kWhirlpoolAcFanAuto: - result += F(" (AUTO)"); - break; - case kWhirlpoolAcFanHigh: - result += F(" (HIGH)"); - break; - case kWhirlpoolAcFanMedium: - result += F(" (MEDIUM)"); - break; - case kWhirlpoolAcFanLow: - result += F(" (LOW)"); - break; - default: - result += F(" (UNKNOWN)"); - break; - } - result += F(", Swing: "); - if (getSwing()) - result += F("On"); - else - result += F("Off"); - result += F(", Light: "); - if (getLight()) - result += F("On"); - else - result += F("Off"); - result += F(", Clock: "); - result += timeToString(getClock()); - result += F(", On Timer: "); - if (isOnTimerEnabled()) - result += timeToString(getOnTimer()); - else - result += F("Off"); - result += F(", Off Timer: "); - if (isOffTimerEnabled()) - result += timeToString(getOffTimer()); - else - result += F("Off"); - result += F(", Sleep: "); - if (getSleep()) - result += F("On"); - else - result += F("Off"); - result += F(", Super: "); - if (getSuper()) - result += F("On"); - else - result += F("Off"); - result += F(", Command: "); - result += uint64ToString(getCommand()); - switch (getCommand()) { + result += addBoolToString(getPowerToggle(), F("Power toggle")); + result += addModeToString(getMode(), kWhirlpoolAcAuto, kWhirlpoolAcCool, + kWhirlpoolAcHeat, kWhirlpoolAcDry, kWhirlpoolAcFan); + result += addTempToString(getTemp()); + result += addFanToString(getFan(), kWhirlpoolAcFanHigh, kWhirlpoolAcFanLow, + kWhirlpoolAcFanAuto, kWhirlpoolAcFanAuto, + kWhirlpoolAcFanMedium); + result += addBoolToString(getSwing(), F("Swing")); + result += addBoolToString(getLight(), F("Light")); + result += addLabeledString(minsToString(getClock()), F("Clock")); + result += addLabeledString( + isOnTimerEnabled() ? minsToString(getOnTimer()) : F("Off"), + F("On Timer")); + result += addLabeledString( + isOffTimerEnabled() ? minsToString(getOffTimer()) : F("Off"), + F("Off Timer")); + result += addBoolToString(getSleep(), F("Sleep")); + result += addBoolToString(getSuper(), F("Super")); + result += addIntToString(getCommand(), F("Command")); + switch (this->getCommand()) { case kWhirlpoolAcCommandLight: result += F(" (LIGHT)"); break; @@ -605,9 +577,9 @@ std::string IRWhirlpoolAc::toString() { // // // Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/509 -bool IRrecv::decodeWhirlpoolAC(decode_results *results, uint16_t nbits, - bool strict) { +// https://github.com/crankyoldgit/IRremoteESP8266/issues/509 +bool IRrecv::decodeWhirlpoolAC(decode_results *results, const uint16_t nbits, + const bool strict) { if (results->rawlen < 2 * nbits + 4 + kHeader + kFooter - 1) return false; // Can't possibly be a valid Whirlpool A/C message. if (strict) { @@ -615,9 +587,6 @@ bool IRrecv::decodeWhirlpoolAC(decode_results *results, uint16_t nbits, } uint16_t offset = kStartOffset; - uint16_t dataBitsSoFar = 0; - uint16_t i = 0; - match_result_t data_result; uint8_t sectionSize[kWhirlpoolAcSections] = {6, 8, 7}; // Header @@ -625,44 +594,36 @@ bool IRrecv::decodeWhirlpoolAC(decode_results *results, uint16_t nbits, if (!matchSpace(results->rawbuf[offset++], kWhirlpoolAcHdrSpace)) return false; - // Data Section - // Keep reading bytes until we either run out of section or state to fill. - for (uint8_t section = 0, pos = 0; section < kWhirlpoolAcSections; + // Data Sections + uint16_t pos = 0; + for (uint8_t section = 0; section < kWhirlpoolAcSections; section++) { + uint16_t used; + // Section Data + used = matchGeneric(results->rawbuf + offset, results->state + pos, + results->rawlen - offset, sectionSize[section] * 8, + 0, 0, + kWhirlpoolAcBitMark, kWhirlpoolAcOneSpace, + kWhirlpoolAcBitMark, kWhirlpoolAcZeroSpace, + kWhirlpoolAcBitMark, kWhirlpoolAcGap, + section >= kWhirlpoolAcSections - 1, + _tolerance, kMarkExcess, false); + if (used == 0) return false; + offset += used; pos += sectionSize[section]; - for (; offset <= results->rawlen - 16 && i < pos; - i++, dataBitsSoFar += 8, offset += data_result.used) { - data_result = - matchData(&(results->rawbuf[offset]), 8, kWhirlpoolAcBitMark, - kWhirlpoolAcOneSpace, kWhirlpoolAcBitMark, - kWhirlpoolAcZeroSpace, kTolerance, kMarkExcess, false); - if (data_result.success == false) break; // Fail - // Data is in LSB order. We need to reverse it. - results->state[i] = (uint8_t)data_result.data; - } - // Section Footer - if (!matchMark(results->rawbuf[offset++], kWhirlpoolAcBitMark)) - return false; - if (section < kWhirlpoolAcSections - 1) { // Inter-section gaps. - if (!matchSpace(results->rawbuf[offset++], kWhirlpoolAcGap)) return false; - } else { // Last section / End of message gap. - if (offset <= results->rawlen && - !matchAtLeast(results->rawbuf[offset++], kWhirlpoolAcGap)) - return false; - } } // Compliance if (strict) { // Re-check we got the correct size/length due to the way we read the data. - if (dataBitsSoFar != kWhirlpoolAcBits) return false; - if (!IRWhirlpoolAc::validChecksum(results->state, dataBitsSoFar / 8)) + if (pos * 8 != nbits) return false; + if (!IRWhirlpoolAc::validChecksum(results->state, nbits / 8)) return false; } // Success results->decode_type = WHIRLPOOL_AC; - results->bits = dataBitsSoFar; + results->bits = nbits; // No need to record the state as we stored it as we decoded it. // As we use result->state, we don't record value, address, or command as it // is a union data type. diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Whirlpool.h b/lib/IRremoteESP8266-2.6.5/src/ir_Whirlpool.h similarity index 75% rename from lib/IRremoteESP8266-2.6.0/src/ir_Whirlpool.h rename to lib/IRremoteESP8266-2.6.5/src/ir_Whirlpool.h index 9604d025c..156c4b631 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Whirlpool.h +++ b/lib/IRremoteESP8266-2.6.5/src/ir_Whirlpool.h @@ -2,6 +2,16 @@ // // Copyright 2018 David Conran +// Supports: +// Brand: Whirlpool, Model: DG11J1-3A remote +// Brand: Whirlpool, Model: DG11J1-04 remote +// Brand: Whirlpool, Model: DG11J1-91 remote +// Brand: Whirlpool, Model: SPIS409L A/C +// Brand: Whirlpool, Model: SPIS412L A/C +// Brand: Whirlpool, Model: SPIW409L A/C +// Brand: Whirlpool, Model: SPIW412L A/C +// Brand: Whirlpool, Model: SPIW418L A/C + #ifndef IR_WHIRLPOOL_H_ #define IR_WHIRLPOOL_H_ @@ -9,8 +19,6 @@ #include #ifndef UNIT_TEST #include -#else -#include #endif #include "IRremoteESP8266.h" #include "IRsend.h" @@ -18,14 +26,8 @@ #include "IRsend_test.h" #endif -// WW WW HH HH IIIII RRRRRR LL PPPPPP OOOOO OOOOO LL -// WW WW HH HH III RR RR LL PP PP OO OO OO OO LL -// WW W WW HHHHHHH III RRRRRR LL PPPPPP OO OO OO OO LL -// WW WWW WW HH HH III RR RR LL PP OO OO OO OO LL -// WW WW HH HH IIIII RR RR LLLLLLL PP OOOO0 OOOO0 LLLLLLL - // Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/509 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/509 // Constants const uint8_t kWhirlpoolAcChecksumByte1 = 13; @@ -87,45 +89,47 @@ enum whirlpool_ac_remote_model_t { // Classes class IRWhirlpoolAc { public: - explicit IRWhirlpoolAc(uint16_t pin); + explicit IRWhirlpoolAc(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); - void stateReset(); + void stateReset(void); #if SEND_WHIRLPOOL_AC void send(const uint16_t repeat = kWhirlpoolAcDefaultRepeat, const bool calcchecksum = true); + uint8_t calibrate(void) { return _irsend.calibrate(); } #endif // SEND_WHIRLPOOL_AC - void begin(); - void on(); - void off(); + void begin(void); + void on(void); + void off(void); void setPowerToggle(const bool on); - bool getPowerToggle(); + bool getPowerToggle(void); void setSleep(const bool on); - bool getSleep(); + bool getSleep(void); void setSuper(const bool on); - bool getSuper(); + bool getSuper(void); void setTemp(const uint8_t temp); - uint8_t getTemp(); + uint8_t getTemp(void); void setFan(const uint8_t speed); - uint8_t getFan(); + uint8_t getFan(void); void setMode(const uint8_t mode); - uint8_t getMode(); + uint8_t getMode(void); void setSwing(const bool on); - bool getSwing(); + bool getSwing(void); void setLight(const bool on); - bool getLight(); - uint16_t getClock(); + bool getLight(void); + uint16_t getClock(void); void setClock(const uint16_t minspastmidnight); - uint16_t getOnTimer(); + uint16_t getOnTimer(void); void setOnTimer(const uint16_t minspastmidnight); - void enableOnTimer(const bool state); - bool isOnTimerEnabled(); - uint16_t getOffTimer(); + void enableOnTimer(const bool on); + bool isOnTimerEnabled(void); + uint16_t getOffTimer(void); void setOffTimer(const uint16_t minspastmidnight); - void enableOffTimer(const bool state); - bool isOffTimerEnabled(); + void enableOffTimer(const bool on); + bool isOffTimerEnabled(void); void setCommand(const uint8_t code); - uint8_t getCommand(); - whirlpool_ac_remote_model_t getModel(); + uint8_t getCommand(void); + whirlpool_ac_remote_model_t getModel(void); void setModel(const whirlpool_ac_remote_model_t model); uint8_t* getRaw(const bool calcchecksum = true); void setRaw(const uint8_t new_code[], @@ -134,11 +138,10 @@ class IRWhirlpoolAc { const uint16_t length = kWhirlpoolAcStateLength); uint8_t convertMode(const stdAc::opmode_t mode); uint8_t convertFan(const stdAc::fanspeed_t speed); -#ifdef ARDUINO - String toString(); -#else - std::string toString(); -#endif + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + stdAc::state_t toCommon(void); + String toString(void); #ifndef UNIT_TEST private: @@ -156,12 +159,7 @@ class IRWhirlpoolAc { void enableTimer(const uint16_t pos, const bool state); void _setTemp(const uint8_t temp, const bool remember = true); void _setMode(const uint8_t mode); - int8_t getTempOffset(); -#ifdef ARDUINO - String timeToString(uint16_t minspastmidnight); -#else - std::string timeToString(uint16_t minspastmidnight); -#endif + int8_t getTempOffset(void); }; #endif // IR_WHIRLPOOL_H_ diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Whynter.cpp b/lib/IRremoteESP8266-2.6.5/src/ir_Whynter.cpp similarity index 69% rename from lib/IRremoteESP8266-2.6.0/src/ir_Whynter.cpp rename to lib/IRremoteESP8266-2.6.5/src/ir_Whynter.cpp index 555c50788..c5634f381 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Whynter.cpp +++ b/lib/IRremoteESP8266-2.6.5/src/ir_Whynter.cpp @@ -1,20 +1,17 @@ // Copyright 2009 Ken Shirriff // Copyright 2017 David Conran +// Whynter A/C ARC-110WD added by Francesco Meschia +// Whynter originally added from https://github.com/shirriff/Arduino-IRremote/ + +// Supports: +// Brand: Whynter, Model: ARC-110WD A/C + #include #include "IRrecv.h" #include "IRsend.h" #include "IRutils.h" -// W W H H Y Y N N TTTTT EEEEE RRRRR -// W W H H Y Y NN N T E R R -// W W W HHHHH Y N N N T EEE RRRR -// W W W H H Y N NN T E R R -// WWW H H Y N N T EEEEE R R - -// Whynter A/C ARC-110WD added by Francesco Meschia -// Whynter originally added from https://github.com/shirriff/Arduino-IRremote/ - // Constants const uint16_t kWhynterTick = 50; @@ -91,39 +88,18 @@ bool IRrecv::decodeWhynter(decode_results *results, uint16_t nbits, return false; // Incorrect nr. of bits per spec. uint16_t offset = kStartOffset; - - // Header + uint64_t data = 0; + // Pre-Header // Sequence begins with a bit mark and a zero space. - // These are typically small, so we'll prefer to do the calibration - // on the much larger header mark & space that are next. if (!matchMark(results->rawbuf[offset++], kWhynterBitMark)) return false; if (!matchSpace(results->rawbuf[offset++], kWhynterZeroSpace)) return false; - // Main header mark and space - if (!matchMark(results->rawbuf[offset], kWhynterHdrMark)) return false; - // Calculate how long the common tick time is based on the header mark. - uint32_t m_tick = results->rawbuf[offset++] * kRawTick / kWhynterHdrMarkTicks; - if (!matchSpace(results->rawbuf[offset], kWhynterHdrSpace)) return false; - // Calculate how long the common tick time is based on the header space. - uint32_t s_tick = - results->rawbuf[offset++] * kRawTick / kWhynterHdrSpaceTicks; - - // Data - uint64_t data = 0; - match_result_t data_result = - matchData(&(results->rawbuf[offset]), nbits, - kWhynterBitMarkTicks * m_tick, kWhynterOneSpaceTicks * s_tick, - kWhynterBitMarkTicks * m_tick, kWhynterZeroSpaceTicks * s_tick); - if (data_result.success == false) return false; - data = data_result.data; - offset += data_result.used; - - // Footer - if (!matchMark(results->rawbuf[offset++], kWhynterBitMarkTicks * m_tick)) - return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset], kWhynterMinGapTicks * s_tick)) - return false; - + // Match Main Header + Data + Footer + if (!matchGeneric(results->rawbuf + offset, &data, + results->rawlen - offset, nbits, + kWhynterHdrMark, kWhynterHdrSpace, + kWhynterBitMark, kWhynterOneSpace, + kWhynterBitMark, kWhynterZeroSpace, + kWhynterBitMark, kWhynterMinGap, true)) return false; // Success results->decode_type = WHYNTER; results->bits = nbits; diff --git a/lib/IRremoteESP8266-2.6.0/test/IRac_test.cpp b/lib/IRremoteESP8266-2.6.5/test/IRac_test.cpp similarity index 53% rename from lib/IRremoteESP8266-2.6.0/test/IRac_test.cpp rename to lib/IRremoteESP8266-2.6.5/test/IRac_test.cpp index 39c17a84b..3afc89c6e 100644 --- a/lib/IRremoteESP8266-2.6.0/test/IRac_test.cpp +++ b/lib/IRremoteESP8266-2.6.5/test/IRac_test.cpp @@ -1,8 +1,12 @@ // Copyright 2019 David Conran +#include +#include "ir_Amcor.h" #include "ir_Argo.h" #include "ir_Daikin.h" +#include "ir_Electra.h" #include "ir_Fujitsu.h" +#include "ir_Goodweather.h" #include "ir_Gree.h" #include "ir_Haier.h" #include "ir_Hitachi.h" @@ -10,8 +14,10 @@ #include "ir_Midea.h" #include "ir_Mitsubishi.h" #include "ir_MitsubishiHeavy.h" +#include "ir_Neoclima.h" #include "ir_Panasonic.h" #include "ir_Samsung.h" +#include "ir_Sharp.h" #include "ir_Tcl.h" #include "ir_Teco.h" #include "ir_Toshiba.h" @@ -28,6 +34,27 @@ // Tests for IRac class. +TEST(TestIRac, Amcor) { + IRAmcorAc ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On, Mode: 5 (AUTO), Fan: 3 (High), Temp: 19C, Max: Off"; + + ac.begin(); + irac.amcor(&ac, + true, // Power + stdAc::opmode_t::kAuto, // Mode + 19, // Celsius + stdAc::fanspeed_t::kHigh); // Fan speed + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(AMCOR, ac._irsend.capture.decode_type); + ASSERT_EQ(kAmcorBits, ac._irsend.capture.bits); + ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); +} + TEST(TestIRac, Argo) { IRArgoAC ac(0); IRac irac(0); @@ -42,7 +69,7 @@ TEST(TestIRac, Argo) { false, // Turbo -1); // Sleep EXPECT_TRUE(ac.getPower()); - EXPECT_EQ(1, ac.getMode()); + EXPECT_EQ(kArgoHeat, ac.getMode()); EXPECT_EQ(21, ac.getTemp()); EXPECT_EQ(kArgoFlapAuto, ac.getFlap()); EXPECT_FALSE(ac.getMax()); // Turbo @@ -74,25 +101,57 @@ TEST(TestIRac, Coolix) { EXPECT_TRUE(capture.decode(&ac._irsend.capture)); ASSERT_EQ(COOLIX, ac._irsend.capture.decode_type); ASSERT_EQ(kCoolixBits, ac._irsend.capture.bits); - ac.setRaw(ac._irsend.capture.value); - ASSERT_EQ(expected, ac.toString()); + ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); + // Confirm we are sending with a repeat of 1. i.e. two messages. + EXPECT_EQ( + "f38000d50" // 38kHz Frequency and 50% duty-cycle. + // Start of message #1 (i.e. Repeat '0') + // Header + "m4480s4480" + // Data + "m560s1680m560s560m560s1680m560s1680m560s560m560s560m560s1680m560s560" + "m560s560m560s1680m560s560m560s560m560s1680m560s1680m560s560m560s1680" + "m560s560m560s560m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" + "m560s1680m560s1680m560s560m560s560m560s560m560s560m560s560m560s560" + "m560s560m560s1680m560s1680m560s560m560s1680m560s1680m560s560m560s560" + "m560s1680m560s560m560s560m560s1680m560s560m560s560m560s1680m560s1680" + // Footer + "m560s5040" + // End of message #1 (i.e. Repeat '0') + // Start of message #2 (i.e. Repeat '1') + // Header + "m4480s4480" + // Data + "m560s1680m560s560m560s1680m560s1680m560s560m560s560m560s1680m560s560" + "m560s560m560s1680m560s560m560s560m560s1680m560s1680m560s560m560s1680" + "m560s560m560s560m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" + "m560s1680m560s1680m560s560m560s560m560s560m560s560m560s560m560s560" + "m560s560m560s1680m560s1680m560s560m560s1680m560s1680m560s560m560s560" + "m560s1680m560s560m560s560m560s1680m560s560m560s560m560s1680m560s1680" + // Footer + "m560s105040", + // End of message #2 (i.e. Repeat '1') + // Note: the two messages (#1 & #2) are identical. + ac._irsend.outputStr()); } TEST(TestIRac, Daikin) { IRDaikinESP ac(0); IRac irac(0); + IRrecv capture(0); char expected[] = - "Power: On, Mode: 3 (COOL), Temp: 19C, Fan: 2, Powerful: Off, " - "Quiet: Off, Sensor: Off, Eye: Off, Mold: On, Comfort: Off, " + "Power: On, Mode: 3 (COOL), Temp: 19C, Fan: 5 (High), Powerful: Off, " + "Quiet: Off, Sensor: Off, Mold: On, Comfort: Off, " "Swing (Horizontal): Off, Swing (Vertical): Off, " - "Current Time: 0:00, On Time: Off, Off Time: Off"; + "Current Time: 00:00, Current Day: (UNKNOWN), On Time: Off, " + "Off Time: Off, Weekly Timer: On"; ac.begin(); irac.daikin(&ac, true, // Power stdAc::opmode_t::kCool, // Mode 19, // Celsius - stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::fanspeed_t::kMax, // Fan speed stdAc::swingv_t::kOff, // Veritcal swing stdAc::swingh_t::kOff, // Horizontal swing false, // Quiet @@ -100,6 +159,87 @@ TEST(TestIRac, Daikin) { true, // Filter true); // Clean ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(DAIKIN, ac._irsend.capture.decode_type); + ASSERT_EQ(kDaikinBits, ac._irsend.capture.bits); + ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); +} + +TEST(TestIRac, Daikin128) { + IRDaikin128 ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power Toggle: On, Mode: 8 (HEAT), Temp: 27C, Fan: 9 (Quiet), " + "Powerful: Off, Quiet: On, Swing (V): On, Sleep: On, " + "Econo: Off, Clock: 21:57, On Timer: Off, On Time: 00:00, " + "Off Timer: Off, Off Time: 00:00, Light Toggle: 8 (Wall)"; + + ac.begin(); + irac.daikin128(&ac, + true, // Power + stdAc::opmode_t::kHeat, // Mode + 27, // Celsius + stdAc::fanspeed_t::kMin, // Fan speed + stdAc::swingv_t::kAuto, // Veritcal swing + true, // Quiet + false, // Turbo + true, // Light + false, // Econo + 18 * 60, // Sleep + 21 * 60 + 57); // Clock + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(DAIKIN128, ac._irsend.capture.decode_type); + ASSERT_EQ(kDaikin128Bits, ac._irsend.capture.bits); + ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); +} + +TEST(TestIRac, Daikin160) { + IRDaikin160 ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On, Mode: 2 (DRY), Temp: 23C, Fan: 1 (Low), " + "Vent Position (V): 3 (Middle)"; + + ac.begin(); + irac.daikin160(&ac, + true, // Power + stdAc::opmode_t::kDry, // Mode + 23, // Celsius + stdAc::fanspeed_t::kMin, // Fan speed + stdAc::swingv_t::kMiddle); // Veritcal swing + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(DAIKIN160, ac._irsend.capture.decode_type); + ASSERT_EQ(kDaikin160Bits, ac._irsend.capture.bits); + ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); +} + +TEST(TestIRac, Daikin176) { + IRDaikin176 ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On, Mode: 7 (COOL), Temp: 26C, Fan: 1 (Low), Swing (H): 5 (Auto)"; + + ac.begin(); + irac.daikin176(&ac, + true, // Power + stdAc::opmode_t::kCool, // Mode + 26, // Celsius + stdAc::fanspeed_t::kLow, // Fan speed + stdAc::swingh_t::kAuto); // Horizontal swing + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(DAIKIN176, ac._irsend.capture.decode_type); + ASSERT_EQ(kDaikin176Bits, ac._irsend.capture.bits); + ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); } TEST(TestIRac, Daikin2) { @@ -107,18 +247,18 @@ TEST(TestIRac, Daikin2) { IRac irac(0); IRrecv capture(0); char expected[] = - "Power: On, Mode: 3 (COOL), Temp: 19C, Fan: 2, Swing (V): 14 (Auto), " - "Swing (H): 0, Clock: 0:00, On Time: Off, Off Time: Off, " - "Sleep Time: Off, Beep: 1 (Quiet), Light: 1 (Bright), Mold: On, " - "Clean: Off, Fresh Air: Off, Eye: Off, Eye Auto: Off, Quiet: Off, " - "Powerful: Off, Purify: On, Econo: Off"; + "Power: On, Mode: 3 (COOL), Temp: 19C, Fan: 1 (Low), " + "Swing (V): 14 (Auto), Swing (H): 0, Clock: 00:00, On Time: Off, " + "Off Time: Off, Sleep Time: Off, Beep: 1 (Quiet), Light: 1 (Bright), " + "Mold: On, Clean: Off, Fresh Air: Off, Eye: Off, Eye Auto: Off, " + "Quiet: Off, Powerful: Off, Purify: On, Econo: Off"; ac.begin(); irac.daikin2(&ac, true, // Power stdAc::opmode_t::kCool, // Mode 19, // Celsius - stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::fanspeed_t::kLow, // Fan speed stdAc::swingv_t::kOff, // Veritcal swing stdAc::swingh_t::kOff, // Horizontal swing false, // Quiet @@ -134,8 +274,7 @@ TEST(TestIRac, Daikin2) { EXPECT_TRUE(capture.decode(&ac._irsend.capture)); ASSERT_EQ(DAIKIN2, ac._irsend.capture.decode_type); ASSERT_EQ(kDaikin2Bits, ac._irsend.capture.bits); - ac.setRaw(ac._irsend.capture.state); - ASSERT_EQ(expected, ac.toString()); + ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); } TEST(TestIRac, Daikin216) { @@ -143,8 +282,8 @@ TEST(TestIRac, Daikin216) { IRac irac(0); IRrecv capture(0); char expected[] = - "Power: On, Mode: 4 (HEAT), Temp: 31C, Fan: 11 (QUIET), " - "Swing (Horizontal): On, Swing (Vertical): On, Quiet: On"; + "Power: On, Mode: 4 (HEAT), Temp: 31C, Fan: 11 (Quiet), " + "Swing (Horizontal): On, Swing (Vertical): On, Quiet: On, Powerful: Off"; ac.begin(); irac.daikin216(&ac, @@ -154,12 +293,37 @@ TEST(TestIRac, Daikin216) { stdAc::fanspeed_t::kMedium, // Fan speed stdAc::swingv_t::kAuto, // Veritcal swing stdAc::swingh_t::kLeft, // Horizontal swing - true); // Quiet + true, // Quiet + false); // Turbo (Powerful) ASSERT_EQ(expected, ac.toString()); ac._irsend.makeDecodeResult(); EXPECT_TRUE(capture.decode(&ac._irsend.capture)); ASSERT_EQ(DAIKIN216, ac._irsend.capture.decode_type); ASSERT_EQ(kDaikin216Bits, ac._irsend.capture.bits); + ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); +} + +TEST(TestIRac, Electra) { + IRElectraAc ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On, Mode: 6 (FAN), Temp: 26C, Fan: 1 (High), " + "Swing(V): On, Swing(H): On"; + + ac.begin(); + irac.electra(&ac, + true, // Power + stdAc::opmode_t::kFan, // Mode + 26, // Celsius + stdAc::fanspeed_t::kHigh, // Fan speed + stdAc::swingv_t::kAuto, // Veritcal swing + stdAc::swingh_t::kLeft); // Horizontal swing + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(ELECTRA_AC, ac._irsend.capture.decode_type); + ASSERT_EQ(kElectraAcBits, ac._irsend.capture.bits); ac.setRaw(ac._irsend.capture.state); ASSERT_EQ(expected, ac.toString()); } @@ -168,9 +332,12 @@ TEST(TestIRac, Fujitsu) { IRFujitsuAC ac(0); IRac irac(0); IRrecv capture(0); - char expected[] = - "Power: On, Mode: 1 (COOL), Temp: 19C, Fan: 2 (MED), " - "Swing: Off, Command: N/A"; + std::string ardb1_expected = + "Model: 2 (ARDB1), Power: On, Mode: 1 (COOL), Temp: 19C, " + "Fan: 2 (Medium), Command: N/A"; + std::string arrah2e_expected = + "Model: 1 (ARRAH2E), Power: On, Mode: 1 (COOL), Temp: 19C, " + "Fan: 2 (Medium), Swing: Off, Command: N/A"; ac.begin(); irac.fujitsu(&ac, @@ -181,14 +348,15 @@ TEST(TestIRac, Fujitsu) { stdAc::fanspeed_t::kMedium, // Fan speed stdAc::swingv_t::kOff, // Veritcal swing stdAc::swingh_t::kOff, // Horizontal swing - false); // Quiet - ASSERT_EQ(expected, ac.toString()); + false, // Quiet + false, // Turbo (Powerful) + false); // Econo + ASSERT_EQ(ardb1_expected, ac.toString()); ac._irsend.makeDecodeResult(); EXPECT_TRUE(capture.decode(&ac._irsend.capture)); ASSERT_EQ(FUJITSU_AC, ac._irsend.capture.decode_type); ASSERT_EQ(kFujitsuAcBits - 8, ac._irsend.capture.bits); - ac.setRaw(ac._irsend.capture.state, ac._irsend.capture.bits / 8); - ASSERT_EQ(expected, ac.toString()); + ASSERT_EQ(ardb1_expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); ac._irsend.reset(); irac.fujitsu(&ac, @@ -199,14 +367,41 @@ TEST(TestIRac, Fujitsu) { stdAc::fanspeed_t::kMedium, // Fan speed stdAc::swingv_t::kOff, // Veritcal swing stdAc::swingh_t::kOff, // Horizontal swing - false); // Quiet - ASSERT_EQ(expected, ac.toString()); + false, // Quiet + false, // Turbo (Powerful) + false); // Econo + ASSERT_EQ(arrah2e_expected, ac.toString()); ac._irsend.makeDecodeResult(); EXPECT_TRUE(capture.decode(&ac._irsend.capture)); ASSERT_EQ(FUJITSU_AC, ac._irsend.capture.decode_type); ASSERT_EQ(kFujitsuAcBits, ac._irsend.capture.bits); - ac.setRaw(ac._irsend.capture.state, ac._irsend.capture.bits / 8); + ASSERT_EQ(arrah2e_expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); +} + +TEST(TestIRac, Goodweather) { + IRGoodweatherAc ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On, Mode: 1 (COOL), Temp: 19C, Fan: 2 (Medium), Turbo: Toggle, " + "Light: Toggle, Sleep: Toggle, Swing: 1 (Slow), Command: 0 (Power)"; + + ac.begin(); + irac.goodweather(&ac, + true, // Power + stdAc::opmode_t::kCool, // Mode + 19, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kHigh, // Veritcal swing + true, // Turbo + true, // Light + 8 * 60 + 0); // Sleep time ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(GOODWEATHER, ac._irsend.capture.decode_type); + ASSERT_EQ(kGoodweatherBits, ac._irsend.capture.bits); + ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); } TEST(TestIRac, Gree) { @@ -214,28 +409,29 @@ TEST(TestIRac, Gree) { IRac irac(0); IRrecv capture(0); char expected[] = - "Power: On, Mode: 1 (COOL), Temp: 22C, Fan: 2, Turbo: Off, XFan: On, " + "Model: 1 (YAW1F), Power: On, Mode: 1 (COOL), Temp: 22C, " + "Fan: 2 (Medium), Turbo: Off, IFeel: Off, WiFi: Off, XFan: On, " "Light: On, Sleep: On, Swing Vertical Mode: Manual, " - "Swing Vertical Pos: 3"; + "Swing Vertical Pos: 3, Timer: Off"; ac.begin(); irac.gree(&ac, - true, // Power - stdAc::opmode_t::kCool, // Mode - 22, // Celsius - stdAc::fanspeed_t::kMedium, // Fan speed - stdAc::swingv_t::kHigh, // Veritcal swing - false, // Turbo - true, // Light - true, // Clean (aka Mold/XFan) - 8 * 60 + 0); // Sleep time + gree_ac_remote_model_t::YAW1F, // Model + true, // Power + stdAc::opmode_t::kCool, // Mode + 22, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kHigh, // Veritcal swing + false, // Turbo + true, // Light + true, // Clean (aka Mold/XFan) + 8 * 60 + 0); // Sleep time ASSERT_EQ(expected, ac.toString()); ac._irsend.makeDecodeResult(); EXPECT_TRUE(capture.decode(&ac._irsend.capture)); ASSERT_EQ(GREE, ac._irsend.capture.decode_type); ASSERT_EQ(kGreeBits, ac._irsend.capture.bits); - ac.setRaw(ac._irsend.capture.state); - ASSERT_EQ(expected, ac.toString()); + ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); } TEST(TestIRac, Haier) { @@ -243,9 +439,9 @@ TEST(TestIRac, Haier) { IRac irac(0); IRrecv capture(0); char expected[] = - "Command: 1 (On), Mode: 3 (HEAT), Temp: 24C, Fan: 2, Swing: 1 (Up), " - "Sleep: On, Health: On, Current Time: 13:45, On Timer: Off, " - "Off Timer: Off"; + "Command: 1 (On), Mode: 1 (COOL), Temp: 24C, Fan: 2 (Medium), " + "Swing: 1 (Up), Sleep: On, Health: On, Current Time: 13:45, " + "On Timer: Off, Off Timer: Off"; ac.begin(); irac.haier(&ac, @@ -262,8 +458,7 @@ TEST(TestIRac, Haier) { EXPECT_TRUE(capture.decode(&ac._irsend.capture)); ASSERT_EQ(HAIER_AC, ac._irsend.capture.decode_type); ASSERT_EQ(kHaierACBits, ac._irsend.capture.bits); - ac.setRaw(ac._irsend.capture.state); - ASSERT_EQ(expected, ac.toString()); + ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); } @@ -272,8 +467,8 @@ TEST(TestIRac, HaierYrwo2) { IRac irac(0); IRrecv capture(0); char expected[] = - "Power: On, Button: 5 (Power), Mode: 2 (Cool), Temp: 23C, Fan: 4 (Med), " - "Turbo: 1 (High), Swing: 1 (Top), Sleep: On, Health: On"; + "Power: On, Button: 5 (Power), Mode: 2 (COOL), Temp: 23C, " + "Fan: 4 (Medium), Turbo: 1 (High), Swing: 1 (Top), Sleep: On, Health: On"; ac.begin(); irac.haierYrwo2(&ac, @@ -290,8 +485,7 @@ TEST(TestIRac, HaierYrwo2) { EXPECT_TRUE(capture.decode(&ac._irsend.capture)); ASSERT_EQ(HAIER_AC_YRW02, ac._irsend.capture.decode_type); ASSERT_EQ(kHaierACYRW02Bits, ac._irsend.capture.bits); - ac.setRaw(ac._irsend.capture.state); - ASSERT_EQ(expected, ac.toString()); + ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); } TEST(TestIRac, Hitachi) { @@ -299,7 +493,7 @@ TEST(TestIRac, Hitachi) { IRac irac(0); IRrecv capture(0); char expected[] = - "Power: On, Mode: 2 (AUTO), Temp: 22C, Fan: 3 (UNKNOWN), " + "Power: On, Mode: 2 (AUTO), Temp: 22C, Fan: 3 (Medium), " "Swing (Vertical): Off, Swing (Horizontal): On"; ac.begin(); @@ -316,8 +510,7 @@ TEST(TestIRac, Hitachi) { EXPECT_TRUE(capture.decode(&ac._irsend.capture)); ASSERT_EQ(HITACHI_AC, ac._irsend.capture.decode_type); ASSERT_EQ(kHitachiAcBits, ac._irsend.capture.bits); - ac.setRaw(ac._irsend.capture.state); - ASSERT_EQ(expected, ac.toString()); + ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); } TEST(TestIRac, Kelvinator) { @@ -325,9 +518,9 @@ TEST(TestIRac, Kelvinator) { IRac irac(0); IRrecv capture(0); char expected[] = - "Power: On, Mode: 1 (COOL), Temp: 19C, Fan: 3, Turbo: Off, Quiet: Off, " - "XFan: On, IonFilter: On, Light: On, Swing (Horizontal): Off, " - "Swing (Vertical): Off"; + "Power: On, Mode: 1 (COOL), Temp: 19C, Fan: 3 (Medium), Turbo: Off, " + "Quiet: Off, XFan: On, IonFilter: On, Light: On, " + "Swing (Horizontal): Off, Swing (Vertical): Off"; ac.begin(); irac.kelvinator(&ac, @@ -348,8 +541,7 @@ TEST(TestIRac, Kelvinator) { EXPECT_TRUE(capture.decode(&ac._irsend.capture)); ASSERT_EQ(KELVINATOR, ac._irsend.capture.decode_type); ASSERT_EQ(kKelvinatorBits, ac._irsend.capture.bits); - ac.setRaw(ac._irsend.capture.state); - ASSERT_EQ(expected, ac.toString()); + ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); } TEST(TestIRac, Midea) { @@ -357,14 +549,17 @@ TEST(TestIRac, Midea) { IRac irac(0); IRrecv capture(0); char expected[] = - "Power: On, Mode: 1 (DRY), Temp: 27C/81F, Fan: 2 (MED), Sleep: On"; + "Power: On, Mode: 1 (DRY), Celsius: On, Temp: 27C/80F, Fan: 2 (Medium), " + "Sleep: On, Swing(V) Toggle: Off"; ac.begin(); irac.midea(&ac, true, // Power stdAc::opmode_t::kDry, // Mode - 27, // Celsius + true, // Celsius + 27, // Degrees stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kOff, // Swing (V) 8 * 60 + 0); // Sleep time ASSERT_EQ(expected, ac.toString()); @@ -372,8 +567,7 @@ TEST(TestIRac, Midea) { EXPECT_TRUE(capture.decode(&ac._irsend.capture)); ASSERT_EQ(MIDEA, ac._irsend.capture.decode_type); ASSERT_EQ(kMideaBits, ac._irsend.capture.bits); - ac.setRaw(ac._irsend.capture.value); - ASSERT_EQ(expected, ac.toString()); + ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); } TEST(TestIRac, Mitsubishi) { @@ -381,8 +575,8 @@ TEST(TestIRac, Mitsubishi) { IRac irac(0); IRrecv capture(0); char expected[] = - "Power: On (COOL), Temp: 20C, FAN: 2, VANE: AUTO, Time: 14:30, " - "On timer: 00:00, Off timer: 00:00, Timer: -"; + "Power: On, Mode: 24 (COOL), Temp: 20C, Fan: 2 (Medium), Vane: AUTO, " + "Wide Vane: 3, Time: 14:30, On timer: 00:00, Off timer: 00:00, Timer: -"; ac.begin(); irac.mitsubishi(&ac, @@ -391,6 +585,7 @@ TEST(TestIRac, Mitsubishi) { 20, // Celsius stdAc::fanspeed_t::kMedium, // Fan speed stdAc::swingv_t::kOff, // Veritcal swing + stdAc::swingh_t::kOff, // Horizontal swing false, // Silent 14 * 60 + 35); // Clock ASSERT_EQ(expected, ac.toString()); @@ -398,8 +593,7 @@ TEST(TestIRac, Mitsubishi) { EXPECT_TRUE(capture.decode(&ac._irsend.capture)); ASSERT_EQ(MITSUBISHI_AC, ac._irsend.capture.decode_type); ASSERT_EQ(kMitsubishiACBits, ac._irsend.capture.bits); - ac.setRaw(ac._irsend.capture.state); - ASSERT_EQ(expected, ac.toString()); + ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); } TEST(TestIRac, MitsubishiHeavy88) { @@ -407,7 +601,7 @@ TEST(TestIRac, MitsubishiHeavy88) { IRac irac(0); IRrecv capture(0); char expected[] = - "Power: On, Mode: 1 (Cool), Temp: 21C, Fan: 3 (Med), " + "Power: On, Mode: 1 (COOL), Temp: 21C, Fan: 3 (Medium), " "Swing (V): 16 (Auto), Swing (H): 0 (Off), Turbo: Off, Econo: Off, " "3D: Off, Clean: On"; @@ -427,8 +621,7 @@ TEST(TestIRac, MitsubishiHeavy88) { EXPECT_TRUE(capture.decode(&ac._irsend.capture)); ASSERT_EQ(MITSUBISHI_HEAVY_88, ac._irsend.capture.decode_type); ASSERT_EQ(kMitsubishiHeavy88Bits, ac._irsend.capture.bits); - ac.setRaw(ac._irsend.capture.state); - ASSERT_EQ(expected, ac.toString()); + ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); } TEST(TestIRac, MitsubishiHeavy152) { @@ -436,7 +629,7 @@ TEST(TestIRac, MitsubishiHeavy152) { IRac irac(0); IRrecv capture(0); char expected[] = - "Power: On, Mode: 1 (Cool), Temp: 20C, Fan: 6 (Econo), " + "Power: On, Mode: 1 (COOL), Temp: 20C, Fan: 6 (Econo), " "Swing (V): 6 (Off), Swing (H): 0 (Auto), Silent: On, Turbo: Off, " "Econo: On, Night: On, Filter: On, 3D: Off, Clean: Off"; @@ -459,8 +652,37 @@ TEST(TestIRac, MitsubishiHeavy152) { EXPECT_TRUE(capture.decode(&ac._irsend.capture)); ASSERT_EQ(MITSUBISHI_HEAVY_152, ac._irsend.capture.decode_type); ASSERT_EQ(kMitsubishiHeavy152Bits, ac._irsend.capture.bits); - ac.setRaw(ac._irsend.capture.state); + ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); +} + +TEST(TestIRac, Neoclima) { + IRNeoclimaAc ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On, Mode: 1 (COOL), Temp: 20C, Fan: 3 (Low), " + "Swing(V): Off, Swing(H): On, Sleep: On, Turbo: Off, Hold: Off, Ion: On, " + "Eye: Off, Light: On, Follow: Off, 8C Heat: Off, Fresh: Off, " + "Button: 0 (Power)"; + + ac.begin(); + irac.neoclima(&ac, + true, // Power + stdAc::opmode_t::kCool, // Mode + 20, // Celsius + stdAc::fanspeed_t::kLow, // Fan speed + stdAc::swingv_t::kOff, // Veritcal swing + stdAc::swingh_t::kAuto, // Horizontal swing + false, // Turbo + true, // Light + true, // Filter + 8 * 60); // Sleep ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(decode_type_t::NEOCLIMA, ac._irsend.capture.decode_type); + ASSERT_EQ(kNeoclimaBits, ac._irsend.capture.bits); + ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); } TEST(TestIRac, Panasonic) { @@ -468,7 +690,7 @@ TEST(TestIRac, Panasonic) { IRac irac(0); IRrecv capture(0); char expected_nke[] = - "Model: 2 (NKE), Power: On, Mode: 4 (HEAT), Temp: 28C, Fan: 2 (UNKNOWN), " + "Model: 2 (NKE), Power: On, Mode: 4 (HEAT), Temp: 28C, Fan: 2 (Medium), " "Swing (Vertical): 15 (AUTO), Swing (Horizontal): 6 (Middle), Quiet: On, " "Powerful: Off, Clock: 19:17, On Timer: Off, Off Timer: Off"; @@ -489,11 +711,10 @@ TEST(TestIRac, Panasonic) { EXPECT_TRUE(capture.decode(&ac._irsend.capture)); ASSERT_EQ(PANASONIC_AC, ac._irsend.capture.decode_type); ASSERT_EQ(kPanasonicAcBits, ac._irsend.capture.bits); - ac.setRaw(ac._irsend.capture.state); - ASSERT_EQ(expected_nke, ac.toString()); + ASSERT_EQ(expected_nke, IRAcUtils::resultAcToString(&ac._irsend.capture)); char expected_dke[] = - "Model: 3 (DKE), Power: On, Mode: 3 (COOL), Temp: 18C, Fan: 4 (MAX), " + "Model: 3 (DKE), Power: On, Mode: 3 (COOL), Temp: 18C, Fan: 4 (High), " "Swing (Vertical): 1 (Full Up), Swing (Horizontal): 6 (Middle), " "Quiet: Off, Powerful: On, Clock: 19:17, On Timer: Off, Off Timer: Off"; ac._irsend.reset(); @@ -513,8 +734,7 @@ TEST(TestIRac, Panasonic) { EXPECT_TRUE(capture.decode(&ac._irsend.capture)); ASSERT_EQ(PANASONIC_AC, ac._irsend.capture.decode_type); ASSERT_EQ(kPanasonicAcBits, ac._irsend.capture.bits); - ac.setRaw(ac._irsend.capture.state); - ASSERT_EQ(expected_dke, ac.toString()); + ASSERT_EQ(expected_dke, IRAcUtils::resultAcToString(&ac._irsend.capture)); } TEST(TestIRac, Samsung) { @@ -522,8 +742,8 @@ TEST(TestIRac, Samsung) { IRac irac(0); IRrecv capture(0); char expected[] = - "Power: On, Mode: 0 (AUTO), Temp: 28C, Fan: 6 (AUTO), Swing: On, " - "Beep: On, Clean: On, Quiet: On"; + "Power: On, Mode: 0 (AUTO), Temp: 28C, Fan: 6 (Auto), Swing: On, " + "Beep: On, Clean: On, Quiet: On, Powerful: Off"; ac.begin(); irac.samsung(&ac, @@ -536,14 +756,13 @@ TEST(TestIRac, Samsung) { false, // Turbo true, // Clean true, // Beep - false); // with the Hack Off + false); // with dopower Off ASSERT_EQ(expected, ac.toString()); ac._irsend.makeDecodeResult(); EXPECT_TRUE(capture.decode(&ac._irsend.capture)); ASSERT_EQ(SAMSUNG_AC, ac._irsend.capture.decode_type); ASSERT_EQ(kSamsungAcBits, ac._irsend.capture.bits); - ac.setRaw(ac._irsend.capture.state); - ASSERT_EQ(expected, ac.toString()); + ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); ac._irsend.reset(); irac.samsung(&ac, @@ -556,19 +775,39 @@ TEST(TestIRac, Samsung) { false, // Turbo true, // Clean true, // Beep - true); // with the Hack On + true); // with dopower On ASSERT_EQ(expected, ac.toString()); // Class should be in the desired mode. ac._irsend.makeDecodeResult(); EXPECT_TRUE(capture.decode(&ac._irsend.capture)); ASSERT_EQ(SAMSUNG_AC, ac._irsend.capture.decode_type); ASSERT_EQ(kSamsungAcExtendedBits, ac._irsend.capture.bits); - ac.setRaw(ac._irsend.capture.state); // However, we expect a plain "on" state as it should be sent before the // desired state. char expected_on[] = - "Power: On, Mode: 0 (AUTO), Temp: 16C, Fan: 0 (AUTO), Swing: Off, " - "Beep: Off, Clean: Off, Quiet: Off"; - ASSERT_EQ(expected_on, ac.toString()); + "Power: On, Mode: 1 (COOL), Temp: 24C, Fan: 0 (Auto), Swing: Off, " + "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off"; + ASSERT_EQ(expected_on, IRAcUtils::resultAcToString(&ac._irsend.capture)); +} + +TEST(TestIRac, Sharp) { + IRSharpAc ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On, Mode: 2 (COOL), Temp: 28C, Fan: 3 (Medium)"; + + ac.begin(); + irac.sharp(&ac, + true, // Power + stdAc::opmode_t::kCool, // Mode + 28, // Celsius + stdAc::fanspeed_t::kMedium); // Fan speed + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(SHARP_AC, ac._irsend.capture.decode_type); + ASSERT_EQ(kSharpAcBits, ac._irsend.capture.bits); + ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); } TEST(TestIRac, Tcl112) { @@ -576,7 +815,7 @@ TEST(TestIRac, Tcl112) { IRac irac(0); IRrecv capture(0); char expected[] = - "Power: On, Mode: 3 (COOL), Temp: 20C, Fan: 3 (Med), Econo: On, " + "Power: On, Mode: 3 (COOL), Temp: 20C, Fan: 3 (Medium), Econo: On, " "Health: On, Light: On, Turbo: Off, Swing (H): On, Swing (V): Off"; ac.begin(); @@ -596,8 +835,7 @@ TEST(TestIRac, Tcl112) { EXPECT_TRUE(capture.decode(&ac._irsend.capture)); ASSERT_EQ(TCL112AC, ac._irsend.capture.decode_type); ASSERT_EQ(kTcl112AcBits, ac._irsend.capture.bits); - ac.setRaw(ac._irsend.capture.state); - ASSERT_EQ(expected, ac.toString()); + ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); } TEST(TestIRac, Teco) { @@ -605,8 +843,8 @@ TEST(TestIRac, Teco) { IRac irac(0); IRrecv capture(0); char expected[] = - "Power: On, Mode: 0 (AUTO), Temp: 21C, Fan: 2 (Med), Sleep: On, " - "Swing: On"; + "Power: On, Mode: 0 (AUTO), Temp: 21C, Fan: 2 (Medium), Sleep: On, " + "Swing: On, Light: On, Humid: Off, Save: Off"; ac.begin(); irac.teco(&ac, @@ -615,21 +853,21 @@ TEST(TestIRac, Teco) { 21, // Celsius stdAc::fanspeed_t::kMedium, // Fan speed stdAc::swingv_t::kAuto, // Veritcal swing + true, // Light 8 * 60 + 30); // Sleep ASSERT_EQ(expected, ac.toString()); ac._irsend.makeDecodeResult(); EXPECT_TRUE(capture.decode(&ac._irsend.capture)); ASSERT_EQ(TECO, ac._irsend.capture.decode_type); ASSERT_EQ(kTecoBits, ac._irsend.capture.bits); - ac.setRaw(ac._irsend.capture.value); - ASSERT_EQ(expected, ac.toString()); + ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); } TEST(TestIRac, Toshiba) { IRToshibaAC ac(0); IRac irac(0); IRrecv capture(0); - char expected[] = "Power: On, Mode: 2 (DRY), Temp: 29C, Fan: 2"; + char expected[] = "Power: On, Mode: 2 (DRY), Temp: 29C, Fan: 2 (UNKNOWN)"; ac.begin(); irac.toshiba(&ac, @@ -642,13 +880,15 @@ TEST(TestIRac, Toshiba) { EXPECT_TRUE(capture.decode(&ac._irsend.capture)); ASSERT_EQ(TOSHIBA_AC, ac._irsend.capture.decode_type); ASSERT_EQ(kToshibaACBits, ac._irsend.capture.bits); - ac.setRaw(ac._irsend.capture.state); - ASSERT_EQ(expected, ac.toString()); + ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); } TEST(TestIRac, Trotec) { IRTrotecESP ac(0); IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On, Mode: 1 (COOL), Temp: 18C, Fan: 3 (High), Sleep: On"; ac.begin(); irac.trotec(&ac, @@ -662,6 +902,12 @@ TEST(TestIRac, Trotec) { EXPECT_EQ(18, ac.getTemp()); EXPECT_EQ(kTrotecFanHigh, ac.getSpeed()); EXPECT_TRUE(ac.getSleep()); + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(TROTEC, ac._irsend.capture.decode_type); + ASSERT_EQ(kTrotecBits, ac._irsend.capture.bits); + ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); } TEST(TestIRac, Vestel) { @@ -669,7 +915,7 @@ TEST(TestIRac, Vestel) { IRac irac(0); IRrecv capture(0); char expected[] = - "Power: On, Mode: 0 (AUTO), Temp: 22C, Fan: 5 (LOW), Sleep: On, " + "Power: On, Mode: 0 (AUTO), Temp: 22C, Fan: 5 (Low), Sleep: On, " "Turbo: Off, Ion: On, Swing: On"; ac.begin(); @@ -688,8 +934,7 @@ TEST(TestIRac, Vestel) { EXPECT_TRUE(capture.decode(&ac._irsend.capture)); ASSERT_EQ(VESTEL_AC, ac._irsend.capture.decode_type); ASSERT_EQ(kVestelAcBits, ac._irsend.capture.bits); - ac.setRaw(ac._irsend.capture.state); - ASSERT_EQ(expected, ac.toString()); + ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); ac._irsend.reset(); char expected_clocks[] = @@ -713,8 +958,7 @@ TEST(TestIRac, Vestel) { EXPECT_TRUE(capture.decode(&ac._irsend.capture)); ASSERT_EQ(VESTEL_AC, ac._irsend.capture.decode_type); ASSERT_EQ(kVestelAcBits, ac._irsend.capture.bits); - ac.setRaw(ac._irsend.capture.state); - ASSERT_EQ(expected_clocks, ac.toString()); + ASSERT_EQ(expected_clocks, IRAcUtils::resultAcToString(&ac._irsend.capture)); // Now check it sends both messages during normal operation when the // clock is set. @@ -760,7 +1004,7 @@ TEST(TestIRac, Whirlpool) { IRrecv capture(0); char expected[] = "Model: 1 (DG11J13A), Power toggle: On, Mode: 1 (AUTO), Temp: 21C, " - "Fan: 3 (LOW), Swing: On, Light: On, Clock: 23:58, On Timer: Off, " + "Fan: 3 (Low), Swing: On, Light: On, Clock: 23:58, On Timer: Off, " "Off Timer: Off, Sleep: On, Super: Off, Command: 1 (POWER)"; ac.begin(); @@ -780,8 +1024,114 @@ TEST(TestIRac, Whirlpool) { EXPECT_TRUE(capture.decode(&ac._irsend.capture)); ASSERT_EQ(WHIRLPOOL_AC, ac._irsend.capture.decode_type); ASSERT_EQ(kWhirlpoolAcBits, ac._irsend.capture.bits); - ac.setRaw(ac._irsend.capture.state); - ASSERT_EQ(expected, ac.toString()); + ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); +} + +TEST(TestIRac, cmpStates) { + stdAc::state_t a, b; + a.protocol = decode_type_t::COOLIX; + a.model = -1; + a.power = true; + a.celsius = true; + a.degrees = 25; + a.mode = stdAc::opmode_t::kAuto; + a.fanspeed = stdAc::fanspeed_t::kAuto; + a.swingh = stdAc::swingh_t::kOff; + a.swingv = stdAc::swingv_t::kOff; + a.quiet = false; + a.turbo = false; + a.light = false; + a.econo = false; + a.beep = false; + a.filter = false; + a.clean = false; + a.quiet = false; + a.sleep = -1; + a.clock = -1; + + ASSERT_FALSE(IRac::cmpStates(a, a)); + ASSERT_TRUE(IRac::cmpStates(a, b)); + + b = a; + ASSERT_FALSE(IRac::cmpStates(a, b)); + + // Check we don't compare the clock. + b.clock = 1234; + ASSERT_FALSE(IRac::cmpStates(a, b)); + + // Now make them different. + b.power = false; + ASSERT_TRUE(IRac::cmpStates(a, b)); +} + +TEST(TestIRac, handleToggles) { + stdAc::state_t desired, prev, result; + desired.protocol = decode_type_t::COOLIX; + desired.model = -1; + desired.power = true; + desired.celsius = true; + desired.degrees = 25; + desired.mode = stdAc::opmode_t::kAuto; + desired.fanspeed = stdAc::fanspeed_t::kAuto; + desired.swingh = stdAc::swingh_t::kOff; + desired.swingv = stdAc::swingv_t::kOff; + desired.quiet = false; + desired.turbo = false; + desired.light = false; + desired.econo = false; + desired.beep = false; + desired.filter = false; + desired.clean = false; + desired.quiet = false; + desired.sleep = -1; + desired.clock = -1; + + // The states should be the same as we gave no previous state. + EXPECT_FALSE(IRac::cmpStates(desired, IRac::handleToggles(desired))); + // The states should be the same as we gave no settings that changed. + prev = desired; + EXPECT_FALSE(IRac::cmpStates(desired, IRac::handleToggles(desired, &prev))); + // Change something that isn't a toggle. + desired.degrees = 26; + ASSERT_TRUE(IRac::cmpStates(desired, prev)); + // Still shouldn't change. + EXPECT_FALSE(IRac::cmpStates(desired, IRac::handleToggles(desired, &prev))); + prev.turbo = true; // This requires a toggle. + result = IRac::handleToggles(desired, &prev); + EXPECT_TRUE(IRac::cmpStates(desired, result)); + EXPECT_TRUE(result.turbo); + desired.turbo = true; // As the desired setting hasn't changed from previous + // the result should not have turbo set, as it is + // a toggle setting. + result = IRac::handleToggles(desired, &prev); + EXPECT_TRUE(IRac::cmpStates(desired, result)); + EXPECT_FALSE(result.turbo); + + // Go back to the same states. + prev = desired; + ASSERT_FALSE(IRac::cmpStates(desired, prev)); + // Test swing, as it is more complicated. + result = IRac::handleToggles(desired, &prev); + EXPECT_EQ(stdAc::swingv_t::kOff, result.swingv); + desired.swingv = stdAc::swingv_t::kAuto; + result = IRac::handleToggles(desired, &prev); + EXPECT_NE(stdAc::swingv_t::kOff, result.swingv); + + prev = desired; // Pretend it was sent and time has passed. + ASSERT_FALSE(IRac::cmpStates(desired, prev)); + ASSERT_NE(stdAc::swingv_t::kOff, desired.swingv); + + // User changes setting but it's still an "on" setting, as this device + // only has a binary on/off for swingv. Nothing should change. + desired.swingv = stdAc::swingv_t::kHigh; + result = IRac::handleToggles(desired, &prev); + ASSERT_EQ(stdAc::swingv_t::kOff, result.swingv); // i.e No toggle. + + prev = desired; // Pretend it was sent and time has passed. + // User changes setting to off. i.e. It is no longer on, so it should toggle. + desired.swingv = stdAc::swingv_t::kOff; + result = IRac::handleToggles(desired, &prev); + ASSERT_NE(stdAc::swingv_t::kOff, result.swingv); // i.e A toggle. } TEST(TestIRac, strToBool) { @@ -863,3 +1213,152 @@ TEST(TestIRac, strToModel) { EXPECT_EQ(-1, IRac::strToModel("FOOBAR")); EXPECT_EQ(0, IRac::strToModel("FOOBAR", 0)); } + +TEST(TestIRac, boolToString) { + EXPECT_EQ("on", IRac::boolToString(true)); + EXPECT_EQ("off", IRac::boolToString(false)); +} + +TEST(TestIRac, opmodeToString) { + EXPECT_EQ("off", IRac::opmodeToString(stdAc::opmode_t::kOff)); + EXPECT_EQ("auto", IRac::opmodeToString(stdAc::opmode_t::kAuto)); + EXPECT_EQ("cool", IRac::opmodeToString(stdAc::opmode_t::kCool)); + EXPECT_EQ("unknown", IRac::opmodeToString((stdAc::opmode_t)500)); +} + +TEST(TestIRac, fanspeedToString) { + EXPECT_EQ("low", IRac::fanspeedToString(stdAc::fanspeed_t::kLow)); + EXPECT_EQ("auto", IRac::fanspeedToString(stdAc::fanspeed_t::kAuto)); + EXPECT_EQ("unknown", IRac::fanspeedToString((stdAc::fanspeed_t)500)); +} + +TEST(TestIRac, swingvToString) { + EXPECT_EQ("off", IRac::swingvToString(stdAc::swingv_t::kOff)); + EXPECT_EQ("low", IRac::swingvToString(stdAc::swingv_t::kLow)); + EXPECT_EQ("auto", IRac::swingvToString(stdAc::swingv_t::kAuto)); + EXPECT_EQ("unknown", IRac::swingvToString((stdAc::swingv_t)500)); +} + +TEST(TestIRac, swinghToString) { + EXPECT_EQ("off", IRac::swinghToString(stdAc::swingh_t::kOff)); + EXPECT_EQ("left", IRac::swinghToString(stdAc::swingh_t::kLeft)); + EXPECT_EQ("auto", IRac::swinghToString(stdAc::swingh_t::kAuto)); + EXPECT_EQ("wide", IRac::swinghToString(stdAc::swingh_t::kWide)); + EXPECT_EQ("unknown", IRac::swinghToString((stdAc::swingh_t)500)); +} + +// Check that we keep the previous state info if the message is a special +// state-less command. +TEST(TestIRac, CoolixDecodeToState) { + stdAc::state_t prev; + prev.mode = stdAc::opmode_t::kHeat; + prev.power = true; + prev.celsius = true; + prev.degrees = 20; + prev.fanspeed = stdAc::fanspeed_t::kLow; + + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + irsend.sendCOOLIX(kCoolixOff); // Special state-less "off" message. + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + stdAc::state_t result; + ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &result, &prev)); + ASSERT_EQ(decode_type_t::COOLIX, result.protocol); + ASSERT_FALSE(result.power); + ASSERT_EQ(stdAc::opmode_t::kHeat, result.mode); + ASSERT_TRUE(result.celsius); + ASSERT_EQ(20, result.degrees); + ASSERT_EQ(stdAc::fanspeed_t::kLow, result.fanspeed); +} + +// Check light on/off functionality in Coolix common a/c handling. +TEST(TestIRac, Issue821) { + stdAc::state_t prev; + stdAc::state_t next; + stdAc::state_t result; + // state info from: + // https://github.com/crankyoldgit/IRremoteESP8266/issues/821#issuecomment-513708970 + prev.protocol = decode_type_t::COOLIX; + prev.model = -1; + prev.power = true; + prev.mode = stdAc::opmode_t::kAuto; + prev.degrees = 24; + prev.celsius = true; + prev.fanspeed = stdAc::fanspeed_t::kAuto; + prev.swingv = stdAc::swingv_t::kOff; + prev.swingh = stdAc::swingh_t::kOff; + prev.quiet = false; + prev.turbo = false; + prev.econo = false; + prev.light = false; + prev.filter = false; + prev.clean = false; + prev.beep = false; + + next = prev; + next.light = true; + + IRac irac(0); + IRrecv capture(0); + IRCoolixAC ac(0); + + ac.begin(); + result = irac.handleToggles(next, &prev); + ASSERT_TRUE(result.light); + irac.sendAc(next, &prev); + ASSERT_TRUE(next.light); + irac.coolix(&ac, + result.power, // Power + result.mode, // Mode + result.degrees, // Celsius + result.fanspeed, // Fan speed + result.swingv, // Veritcal swing + result.swingh, // Horizontal swing + result.turbo, // Turbo + result.light, // Light + result.clean, // Clean + -1); // Sleep + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(COOLIX, ac._irsend.capture.decode_type); + ASSERT_EQ(kCoolixBits, ac._irsend.capture.bits); + ASSERT_EQ("Power: On, Led: Toggle", + IRAcUtils::resultAcToString(&ac._irsend.capture)); + EXPECT_EQ( + "f38000d50m" + "4480s4480" + "m560s1680m560s560m560s1680m560s1680m560s560m560s1680m560s560m560s1680" + "m560s560m560s1680m560s560m560s560m560s1680m560s560m560s1680m560s560" + "m560s1680m560s1680m560s1680m560s1680m560s560m560s1680m560s560m560s1680" + "m560s560m560s560m560s560m560s560m560s1680m560s560m560s1680m560s560" + "m560s1680m560s560m560s1680m560s560m560s560m560s1680m560s560m560s1680" + "m560s560m560s1680m560s560m560s1680m560s1680m560s560m560s1680m560s560" + "m560s5040" + "m4480s4480" + "m560s1680m560s560m560s1680m560s1680m560s560m560s1680m560s560m560s1680" + "m560s560m560s1680m560s560m560s560m560s1680m560s560m560s1680m560s560" + "m560s1680m560s1680m560s1680m560s1680m560s560m560s1680m560s560m560s1680" + "m560s560m560s560m560s560m560s560m560s1680m560s560m560s1680m560s560" + "m560s1680m560s560m560s1680m560s560m560s560m560s1680m560s560m560s1680" + "m560s560m560s1680m560s560m560s1680m560s1680m560s560m560s1680m560s560" + "m560s105040" + "m4480s4480" + "m560s1680m560s560m560s1680m560s1680m560s560m560s560m560s1680m560s560" + "m560s560m560s1680m560s560m560s560m560s1680m560s1680m560s560m560s1680" + "m560s560m560s560m560s560m560s1680m560s1680m560s1680m560s1680m560s1680" + "m560s1680m560s1680m560s1680m560s560m560s560m560s560m560s560m560s560" + "m560s560m560s1680m560s560m560s560m560s1680m560s560m560s560m560s560" + "m560s1680m560s560m560s1680m560s1680m560s560m560s1680m560s1680m560s1680" + "m560s5040" + "m4480s4480" + "m560s1680m560s560m560s1680m560s1680m560s560m560s560m560s1680m560s560" + "m560s560m560s1680m560s560m560s560m560s1680m560s1680m560s560m560s1680" + "m560s560m560s560m560s560m560s1680m560s1680m560s1680m560s1680m560s1680" + "m560s1680m560s1680m560s1680m560s560m560s560m560s560m560s560m560s560" + "m560s560m560s1680m560s560m560s560m560s1680m560s560m560s560m560s560" + "m560s1680m560s560m560s1680m560s1680m560s560m560s1680m560s1680m560s1680" + "m560s105040", + ac._irsend.outputStr()); +} diff --git a/lib/IRremoteESP8266-2.6.5/test/IRrecv_test.cpp b/lib/IRremoteESP8266-2.6.5/test/IRrecv_test.cpp new file mode 100644 index 000000000..cda7b747f --- /dev/null +++ b/lib/IRremoteESP8266-2.6.5/test/IRrecv_test.cpp @@ -0,0 +1,1246 @@ +// Copyright 2017 David Conran + +#include "IRrecv_test.h" +#include "IRrecv.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "gtest/gtest.h" + +// Tests for the IRrecv object. +TEST(TestIRrecv, DefaultBufferSize) { + IRrecv irrecv_default(1); + EXPECT_EQ(kRawBuf, irrecv_default.getBufSize()); +} + +TEST(TestIRrecv, LargeBufferSize) { + IRrecv irrecv_large(3, 1024); + EXPECT_EQ(1024, irrecv_large.getBufSize()); +} + +TEST(TestIRrecv, SmallBufferSize) { + IRrecv irrecv_small(4, 80); + EXPECT_EQ(80, irrecv_small.getBufSize()); +} + +TEST(TestIRrecv, MediumBufferSize) { + IRrecv irrecv_medium(4, 512); + EXPECT_EQ(512, irrecv_medium.getBufSize()); +} + +TEST(TestIRrecv, IRrecvDestructor) { + IRrecv *irrecv_ptr = new IRrecv(1); + EXPECT_EQ(kRawBuf, irrecv_ptr->getBufSize()); + + delete irrecv_ptr; + irrecv_ptr = new IRrecv(1, 1234); + EXPECT_EQ(1234, irrecv_ptr->getBufSize()); + delete irrecv_ptr; + + irrecv_ptr = new IRrecv(1, 123); + EXPECT_EQ(123, irrecv_ptr->getBufSize()); + delete irrecv_ptr; +} + +// Tests for copyIrParams() + +TEST(TestCopyIrParams, CopyEmpty) { + irparams_t src; + irparams_t dst; + uint16_t test_size = 1234; + src.bufsize = test_size; + src.rawlen = 0; + src.rawbuf = new uint16_t[test_size]; + src.overflow = false; + dst.bufsize = 4567; + dst.rawlen = 123; + dst.rawbuf = new uint16_t[test_size]; + dst.overflow = true; + // Confirm we are looking at different memory for the buffers. + ASSERT_NE(src.rawbuf, dst.rawbuf); + + IRrecv irrecv(4); + irrecv.copyIrParams(&src, &dst); + + ASSERT_EQ(src.bufsize, dst.bufsize); + ASSERT_EQ(src.rawlen, dst.rawlen); + ASSERT_NE(src.rawbuf, dst.rawbuf); // Pointers, not content. + ASSERT_EQ(src.overflow, dst.overflow); + // Contents of the buffers needs to match. + EXPECT_EQ(0, memcmp(src.rawbuf, dst.rawbuf, src.bufsize * sizeof(uint16_t))); +} + +TEST(TestCopyIrParams, CopyNonEmpty) { + irparams_t src; + irparams_t dst; + uint16_t test_size = 1234; + src.bufsize = test_size; + src.rawlen = 67; + src.rawbuf = new uint16_t[test_size]; + src.rawbuf[0] = 0xF00D; + src.rawbuf[1] = 0xBEEF; + src.rawbuf[test_size - 1] = 0xDEAD; + src.overflow = true; + dst.bufsize = 0; + dst.rawlen = 0; + dst.rawbuf = new uint16_t[test_size]; + dst.overflow = false; + // Confirm we are looking at different memory for the buffers. + ASSERT_NE(src.rawbuf, dst.rawbuf); + // and that they differ before we test. + EXPECT_NE(0, memcmp(src.rawbuf, dst.rawbuf, src.bufsize * sizeof(uint16_t))); + + IRrecv irrecv(4); + irrecv.copyIrParams(&src, &dst); + + ASSERT_EQ(src.bufsize, dst.bufsize); + EXPECT_EQ(test_size, dst.bufsize); + ASSERT_EQ(src.rawlen, dst.rawlen); + EXPECT_EQ(67, dst.rawlen); + ASSERT_EQ(src.overflow, dst.overflow); + EXPECT_TRUE(dst.overflow); + ASSERT_NE(src.rawbuf, dst.rawbuf); // Pointers, not content. + // Contents of the buffers needs to match. + EXPECT_EQ(0, memcmp(src.rawbuf, dst.rawbuf, src.bufsize * sizeof(uint16_t))); + // Check the canary values. + EXPECT_EQ(0xF00D, dst.rawbuf[0]); + EXPECT_EQ(0xBEEF, dst.rawbuf[1]); + EXPECT_EQ(0xDEAD, dst.rawbuf[test_size - 1]); +} + +// Tests for decode(). + +// Test decode of a NEC message. +TEST(TestDecode, DecodeNEC) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + irsend.reset(); + irsend.sendNEC(0x807F40BF); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(NEC, irsend.capture.decode_type); + EXPECT_EQ(kNECBits, irsend.capture.bits); + EXPECT_EQ(0x807F40BF, irsend.capture.value); +} + +// Test decode of a JVC message. +TEST(TestDecode, DecodeJVC) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + irsend.reset(); + irsend.sendJVC(0xC2B8); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(JVC, irsend.capture.decode_type); + EXPECT_EQ(kJvcBits, irsend.capture.bits); + EXPECT_EQ(0xC2B8, irsend.capture.value); +} + +// Test decode of a LG message. +TEST(TestDecode, DecodeLG) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + irsend.reset(); + irsend.sendLG(0x4B4AE51); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(LG, irsend.capture.decode_type); + EXPECT_EQ(kLgBits, irsend.capture.bits); + EXPECT_EQ(0x4B4AE51, irsend.capture.value); + + irsend.reset(); + irsend.sendLG(0xB4B4AE51, kLg32Bits); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(LG, irsend.capture.decode_type); + EXPECT_EQ(kLg32Bits, irsend.capture.bits); + EXPECT_EQ(0xB4B4AE51, irsend.capture.value); +} + +// Test decode of a Panasonic message. +TEST(TestDecode, DecodePanasonic) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + irsend.reset(); + irsend.sendPanasonic64(0x40040190ED7C); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decodePanasonic(&irsend.capture, kPanasonicBits, true)); + EXPECT_EQ(PANASONIC, irsend.capture.decode_type); + EXPECT_EQ(kPanasonicBits, irsend.capture.bits); + EXPECT_EQ(0x40040190ED7C, irsend.capture.value); +} + +// Test decode of a Samsun message. +TEST(TestDecode, DecodeSamsung) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + irsend.reset(); + irsend.sendSAMSUNG(0xE0E09966); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(SAMSUNG, irsend.capture.decode_type); + EXPECT_EQ(kSamsungBits, irsend.capture.bits); + EXPECT_EQ(0xE0E09966, irsend.capture.value); +} + +// Test decode of a Sherwood message. +TEST(TestDecode, DecodeSherwood) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + irsend.reset(); + irsend.sendSherwood(0x807F40BF); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + // Sherwood codes are really NEC codes. + EXPECT_EQ(NEC, irsend.capture.decode_type); + EXPECT_EQ(kNECBits, irsend.capture.bits); + EXPECT_EQ(0x807F40BF, irsend.capture.value); +} + +// Test decode of a Whynter message. +TEST(TestDecode, DecodeWhynter) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + irsend.reset(); + irsend.sendWhynter(0x87654321); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(WHYNTER, irsend.capture.decode_type); + EXPECT_EQ(kWhynterBits, irsend.capture.bits); + EXPECT_EQ(0x87654321, irsend.capture.value); +} + +// Test decode of Sony messages. +TEST(TestDecode, DecodeSony) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + + // Synthesised Normal Sony 20-bit message. + irsend.reset(); + irsend.sendSony(irsend.encodeSony(kSony20Bits, 0x1, 0x1, 0x1)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(SONY, irsend.capture.decode_type); + EXPECT_EQ(kSony20Bits, irsend.capture.bits); + EXPECT_EQ(0x81080, irsend.capture.value); + + // Synthesised Normal Sony 15-bit message. + irsend.reset(); + irsend.sendSony(irsend.encodeSony(kSony15Bits, 21, 1), kSony15Bits); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(SONY, irsend.capture.decode_type); + EXPECT_EQ(kSony15Bits, irsend.capture.bits); + EXPECT_EQ(0x5480, irsend.capture.value); + + // Synthesised Normal Sony 12-bit message. + irsend.reset(); + irsend.sendSony(irsend.encodeSony(kSony12Bits, 21, 1), kSony12Bits); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(SONY, irsend.capture.decode_type); + EXPECT_EQ(kSony12Bits, irsend.capture.bits); + EXPECT_EQ(0xA90, irsend.capture.value); +} + +// Test decode of Sharp messages. +TEST(TestDecode, DecodeSharp) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + irsend.reset(); + irsend.sendSharpRaw(0x454A); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(SHARP, irsend.capture.decode_type); + EXPECT_EQ(kSharpBits, irsend.capture.bits); + EXPECT_EQ(0x454A, irsend.capture.value); +} + +// Test decode of Sanyo messages. +TEST(TestDecode, DecodeSanyo) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + irsend.reset(); + irsend.sendSanyoLC7461(0x2468DCB56A9); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(SANYO_LC7461, irsend.capture.decode_type); + EXPECT_EQ(kSanyoLC7461Bits, irsend.capture.bits); + EXPECT_EQ(0x2468DCB56A9, irsend.capture.value); +} + +// Test decode of RC-MM messages. +TEST(TestDecode, DecodeRCMM) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + + // Normal RCMM 24-bit message. + irsend.reset(); + irsend.sendRCMM(0xe0a600); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(RCMM, irsend.capture.decode_type); + EXPECT_EQ(kRCMMBits, irsend.capture.bits); + EXPECT_EQ(0xe0a600, irsend.capture.value); + + // Normal RCMM 12-bit message. + irsend.reset(); + irsend.sendRCMM(0x600, 12); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(RCMM, irsend.capture.decode_type); + EXPECT_EQ(12, irsend.capture.bits); + EXPECT_EQ(0x600, irsend.capture.value); + + // Normal RCMM 32-bit message. + irsend.reset(); + irsend.sendRCMM(0x28e0a600, 32); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(RCMM, irsend.capture.decode_type); + EXPECT_EQ(32, irsend.capture.bits); + EXPECT_EQ(0x28e0a600, irsend.capture.value); +} + +// Test decode of Mitsubishi messages. +TEST(TestDecode, DecodeMitsubishi) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + irsend.reset(); + irsend.sendMitsubishi(0xC2B8); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(MITSUBISHI, irsend.capture.decode_type); + EXPECT_EQ(kMitsubishiBits, irsend.capture.bits); + EXPECT_EQ(0xC2B8, irsend.capture.value); +} + +// Test decode of RC-5/RC-5X messages. +TEST(TestDecode, DecodeRC5) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + // Normal RC-5 12-bit message. + irsend.reset(); + irsend.sendRC5(0x175); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(RC5, irsend.capture.decode_type); + EXPECT_EQ(kRC5Bits, irsend.capture.bits); + EXPECT_EQ(0x175, irsend.capture.value); + // Synthesised Normal RC-5X 13-bit message. + irsend.reset(); + irsend.sendRC5(irsend.encodeRC5X(0x02, 0x41, true), kRC5XBits); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(RC5X, irsend.capture.decode_type); + EXPECT_EQ(kRC5XBits, irsend.capture.bits); + EXPECT_EQ(0x1881, irsend.capture.value); +} + +// Test decode of RC-6 messages. +TEST(TestDecode, DecodeRC6) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + // Normal RC-6 Mode 0 (20-bit) message. + irsend.reset(); + irsend.sendRC6(0x175); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(RC6, irsend.capture.decode_type); + EXPECT_EQ(kRC6Mode0Bits, irsend.capture.bits); + EXPECT_EQ(0x175, irsend.capture.value); + + // Normal RC-6 36-bit message. + irsend.reset(); + irsend.sendRC6(0xC800F742A, kRC6_36Bits); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(RC6, irsend.capture.decode_type); + EXPECT_EQ(kRC6_36Bits, irsend.capture.bits); + EXPECT_EQ(0xC800F742A, irsend.capture.value); +} + +// Test decode of Dish messages. +TEST(TestDecode, DecodeDish) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + irsend.reset(); + irsend.sendDISH(0x9C00); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(DISH, irsend.capture.decode_type); + EXPECT_EQ(kDishBits, irsend.capture.bits); + EXPECT_EQ(0x9C00, irsend.capture.value); +} + +// Test decode of Denon messages. +TEST(TestDecode, DecodeDenon) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + // Normal Denon 15-bit message. (Sharp) + irsend.reset(); + irsend.sendDenon(0x2278); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(DENON, irsend.capture.decode_type); + EXPECT_EQ(kDenonBits, irsend.capture.bits); + EXPECT_EQ(0x2278, irsend.capture.value); + // Legacy Denon 14-bit message. + irsend.reset(); + irsend.sendDenon(0x1278, kDenonLegacyBits); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(DENON, irsend.capture.decode_type); + EXPECT_EQ(kDenonBits, irsend.capture.bits); + EXPECT_EQ(0x1278, irsend.capture.value); + // Normal Denon 48-bit message. (Panasonic/Kaseikyo) + irsend.reset(); + irsend.sendDenon(0x2A4C028D6CE3, kDenon48Bits); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(DENON, irsend.capture.decode_type); + EXPECT_EQ(kDenon48Bits, irsend.capture.bits); + EXPECT_EQ(0x2A4C028D6CE3, irsend.capture.value); +} + +// Test decode of Coolix messages. +TEST(TestDecode, DecodeCoolix) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + irsend.reset(); + irsend.sendCOOLIX(0x123456); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(COOLIX, irsend.capture.decode_type); + EXPECT_EQ(kCoolixBits, irsend.capture.bits); + EXPECT_EQ(0x123456, irsend.capture.value); +} + +// Test decode of Aiwa messages. +TEST(TestDecode, DecodeAiwa) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + irsend.reset(); + irsend.sendAiwaRCT501(0x7F); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(AIWA_RC_T501, irsend.capture.decode_type); + EXPECT_EQ(kAiwaRcT501Bits, irsend.capture.bits); + EXPECT_EQ(0x7F, irsend.capture.value); +} + +// Test matchData() on space encoded data. +TEST(TestMatchData, SpaceEncoded) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + + uint16_t space_encoded_raw[11] = {500, 500, 500, 1500, 499, 499, + 501, 1501, 499, 1490, 500}; + match_result_t result; + + irsend.reset(); + irsend.sendRaw(space_encoded_raw, 11, 38000); + irsend.makeDecodeResult(); + result = irrecv.matchData(irsend.capture.rawbuf + 1, 5, 500, 1500, 500, 500); + ASSERT_TRUE(result.success); + EXPECT_EQ(0b01011, result.data); + EXPECT_EQ(10, result.used); + + irsend.reset(); + irsend.sendRaw(space_encoded_raw, 11, 38000); + irsend.makeDecodeResult(); + result = irrecv.matchData(irsend.capture.rawbuf + 1, 5, 500, 1000, 500, 500); + ASSERT_FALSE(result.success); +} + +// Test matchData() on mark encoded data. +TEST(TestMatchData, MarkEncoded) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + + uint16_t mark_encoded_raw[11] = {500, 500, 1500, 500, 499, 499, + 1501, 501, 1499, 490, 500}; + match_result_t result; + + irsend.reset(); + irsend.sendRaw(mark_encoded_raw, 11, 38000); + irsend.makeDecodeResult(); + // MSBF order. + result = irrecv.matchData(irsend.capture.rawbuf + 1, 5, 1500, 500, 500, 500); + ASSERT_TRUE(result.success); + EXPECT_EQ(0b01011, result.data); + EXPECT_EQ(10, result.used); + // LSBF order. + result = irrecv.matchData(irsend.capture.rawbuf + 1, 5, 1500, 500, 500, 500, + kTolerance, kMarkExcess, false); + ASSERT_TRUE(result.success); + EXPECT_EQ(0b11010, result.data); // Bits reversed of the previous test. + EXPECT_EQ(10, result.used); + + irsend.reset(); + irsend.sendRaw(mark_encoded_raw, 11, 38000); + irsend.makeDecodeResult(); + // MSBF order. + result = irrecv.matchData(irsend.capture.rawbuf + 1, 5, 1000, 500, 500, 500); + ASSERT_FALSE(result.success); + // LSBF order. + result = irrecv.matchData(irsend.capture.rawbuf + 1, 5, 1000, 500, 500, 500, + kTolerance, kMarkExcess, false); + ASSERT_FALSE(result.success); +} + +// Test matchData() on "equal total bit time" encoded data. +TEST(TestMatchData, EqualTotalBitTimeEncoded) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + + uint16_t equal_encoded_raw[11] = {500, 1500, 1500, 500, 499, 1499, + 1501, 501, 1499, 490, 500}; + match_result_t result; + + irsend.reset(); + irsend.sendRaw(equal_encoded_raw, 11, 38000); + irsend.makeDecodeResult(); + result = irrecv.matchData(irsend.capture.rawbuf + 1, 5, 1500, 500, 500, 1500); + ASSERT_TRUE(result.success); + EXPECT_EQ(0b01011, result.data); + EXPECT_EQ(10, result.used); + + irsend.reset(); + irsend.sendRaw(equal_encoded_raw, 11, 38000); + irsend.makeDecodeResult(); + result = irrecv.matchData(irsend.capture.rawbuf + 1, 5, 1000, 500, 500, 1000); + ASSERT_FALSE(result.success); +} + +// Test matchData() on arbitrary encoded data. +TEST(TestMatchData, ArbitraryEncoded) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + + uint16_t arbitrary_encoded_raw[11] = {500, 1500, 3000, 1000, 499, 1499, + 3001, 1001, 2999, 990, 500}; + match_result_t result; + + irsend.reset(); + irsend.sendRaw(arbitrary_encoded_raw, 11, 38000); + irsend.makeDecodeResult(); + result = + irrecv.matchData(irsend.capture.rawbuf + 1, 5, 3000, 1000, 500, 1500); + ASSERT_TRUE(result.success); + EXPECT_EQ(0b01011, result.data); + EXPECT_EQ(10, result.used); + + irsend.reset(); + irsend.sendRaw(arbitrary_encoded_raw, 11, 38000); + irsend.makeDecodeResult(); + result = irrecv.matchData(irsend.capture.rawbuf + 1, 5, 1000, 500, 500, 1000); + ASSERT_FALSE(result.success); +} + +TEST(TestMatchGeneric, NormalWithNoAtleast) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + + uint16_t good_entries_trailing_space = 12; + uint16_t good_trailing_space_data[good_entries_trailing_space] = { + 8000, // Header mark + 4000, // Header space + 500, 2000, // Bit #0 (1) + 500, 1000, // Bit #1 (0) + 500, 2000, // Bit #2 (1) + 500, 1000, // Bit #3 (0) + 3000, // Footer mark + 15000}; // Footer space + + uint16_t good_entries_no_trailing_space = 11; + uint16_t good_no_trailing_space_data[good_entries_no_trailing_space] = { + 8000, // Header mark + 4000, // Header space + 500, 2000, // Bit #0 (1) + 500, 1000, // Bit #1 (0) + 500, 2000, // Bit #2 (1) + 500, 1000, // Bit #3 (0) + 3000}; // Footer mark (No Footer space.) + + uint16_t offset = kStartOffset; + irsend.reset(); + irsend.sendRaw(good_trailing_space_data, good_entries_trailing_space, 38000); + irsend.makeDecodeResult(); + uint64_t result_data = 0; + uint16_t entries_used = 0; + entries_used = irrecv.matchGeneric( + irsend.capture.rawbuf + offset, &result_data, + irsend.capture.rawlen - offset, + 4, // nbits + 8000, 4000, // Header + 500, 2000, // one mark & space + 500, 1000, // zero mark & space + 3000, 15000, // Footer + false, // No atleast on the footer space. + 1, // 1% Tolerance + 0, // No excess margin + true); // MSB first. + ASSERT_NE(0, entries_used); + EXPECT_EQ(0b1010, result_data); + EXPECT_EQ(irsend.capture.rawlen - kStartOffset, entries_used); + EXPECT_EQ(good_entries_trailing_space, entries_used); + + // Same again but with a footer space mis-match, which should fail. + result_data = 0; + entries_used = irrecv.matchGeneric( + irsend.capture.rawbuf + offset, &result_data, + irsend.capture.rawlen - offset, + 4, // nbits + 8000, 4000, // Header + 500, 2000, // one mark & space + 500, 1000, // zero mark & space + 3000, 30000, // Footer + false, // no atleast on the footer space. + 1, // 1% Tolerance + 0, // No excess margin + true); // MSB first. + ASSERT_EQ(0, entries_used); + + // Same again as first part but with no footer space data as the last entry. + irsend.reset(); + result_data = 0; + irsend.sendRaw(good_no_trailing_space_data, good_entries_no_trailing_space, + 38000); + irsend.makeDecodeResult(); + entries_used = irrecv.matchGeneric( + irsend.capture.rawbuf + offset, &result_data, + irsend.capture.rawlen - offset, + 4, // nbits + 8000, 4000, // Header + 500, 2000, // one mark & space + 500, 1000, // zero mark & space + 3000, 15000, // Footer + false, // No atleast on the footer space. + 1, // 1% Tolerance + 0, // No excess margin + true); // MSB first. + ASSERT_NE(0, entries_used); + EXPECT_EQ(0b1010, result_data); + EXPECT_EQ(irsend.capture.rawlen - kStartOffset, entries_used); + EXPECT_EQ(good_entries_no_trailing_space, entries_used); +} + + +TEST(TestMatchGeneric, NormalWithAtleast) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + + uint16_t good_entries_trailing_space = 12; + uint16_t good_trailing_space_data[good_entries_trailing_space] = { + 8000, // Header mark + 4000, // Header space + 500, 2000, // Bit #0 (1) + 500, 1000, // Bit #1 (0) + 500, 2000, // Bit #2 (1) + 500, 1000, // Bit #3 (0) + 3000, // Footer mark + 15000}; // Footer space + + uint16_t good_entries_no_trailing_space = 11; + uint16_t good_no_trailing_space_data[good_entries_no_trailing_space] = { + 8000, // Header mark + 4000, // Header space + 500, 2000, // Bit #0 (1) + 500, 1000, // Bit #1 (0) + 500, 2000, // Bit #2 (1) + 500, 1000, // Bit #3 (0) + 3000}; // Footer mark (No Footer space.) + + uint16_t offset = kStartOffset; + irsend.reset(); + irsend.sendRaw(good_trailing_space_data, good_entries_trailing_space, 38000); + irsend.makeDecodeResult(); + uint64_t result_data = 0; + uint16_t entries_used = 0; + entries_used = irrecv.matchGeneric( + irsend.capture.rawbuf + offset, &result_data, + irsend.capture.rawlen - offset, + 4, // nbits + 8000, 4000, // Header + 500, 2000, // one mark & space + 500, 1000, // zero mark & space + 3000, 15000, // Footer + true, // atleast on the footer space. + 1, // 1% Tolerance + 0, // No excess margin + true); // MSB first. + ASSERT_NE(0, entries_used); + EXPECT_EQ(0b1010, result_data); + EXPECT_EQ(irsend.capture.rawlen - kStartOffset, entries_used); + EXPECT_EQ(good_entries_trailing_space, entries_used); + + // Same again but with a footer space under-match. + result_data = 0; + entries_used = irrecv.matchGeneric( + irsend.capture.rawbuf + offset, &result_data, + irsend.capture.rawlen - offset, + 4, // nbits + 8000, 4000, // Header + 500, 2000, // one mark & space + 500, 1000, // zero mark & space + 3000, 1500, // Footer + true, // atleast on the footer space. + 1, // 1% Tolerance + 0, // No excess margin + true); // MSB first. + ASSERT_NE(0, entries_used); + EXPECT_EQ(0b1010, result_data); + EXPECT_EQ(irsend.capture.rawlen - kStartOffset, entries_used); + EXPECT_EQ(good_entries_trailing_space, entries_used); + + // Same again but with a footer space under-match using less bits so the + // atleast footer isn't the last entry in the buffer. + result_data = 0; + entries_used = irrecv.matchGeneric( + irsend.capture.rawbuf + offset, &result_data, + irsend.capture.rawlen - offset, + 3, // nbits (1 less than normal) + 8000, 4000, // Header + 500, 2000, // one mark & space + 500, 1000, // zero mark & space + 500, 500, // Footer + true, // atleast on the footer space. + 1, // 1% Tolerance + 0, // No excess margin + true); // MSB first. + ASSERT_NE(0, entries_used); + EXPECT_EQ(0b101, result_data); + // -2 because we reduced nbits by 1. + EXPECT_EQ(irsend.capture.rawlen - kStartOffset - 2, entries_used); + EXPECT_EQ(good_entries_trailing_space - 2, entries_used); + + // Same again but with a footer space over-match, which should fail. + result_data = 0; + entries_used = irrecv.matchGeneric( + irsend.capture.rawbuf + offset, &result_data, + irsend.capture.rawlen - offset, + 3, // nbits + 8000, 4000, // Header + 500, 2000, // one mark & space + 500, 1000, // zero mark & space + 500, 10000, // Footer + true, // atleast on the footer space. + 1, // 1% Tolerance + 0, // No excess margin + true); // MSB first. + ASSERT_EQ(0, entries_used); + + // Same as first part but with no footer space data as the last entry. + irsend.reset(); + result_data = 0; + irsend.sendRaw(good_no_trailing_space_data, good_entries_no_trailing_space, + 38000); + irsend.makeDecodeResult(); + entries_used = irrecv.matchGeneric( + irsend.capture.rawbuf + offset, &result_data, + irsend.capture.rawlen - offset, + 4, // nbits + 8000, 4000, // Header + 500, 2000, // one mark & space + 500, 1000, // zero mark & space + 3000, 15000, // Footer + true, // atleast on the footer space. + 1, // 1% Tolerance + 0, // No excess margin + true); // MSB first. + ASSERT_NE(0, entries_used); + EXPECT_EQ(0b1010, result_data); + EXPECT_EQ(irsend.capture.rawlen - kStartOffset, entries_used); + EXPECT_EQ(good_entries_no_trailing_space, entries_used); +} + +TEST(TestMatchGeneric, FailureCases) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + + uint16_t entries = 11; + uint16_t data[entries] = { + 8000, // Header mark + 4000, // Header space + 500, 2000, // Bit #0 (1) + 500, 1000, // Bit #1 (0) + 500, 2000, // Bit #2 (1) + 500, 1000, // Bit #3 (0) + 3000}; // Footer mark (No Footer space.) + + uint16_t offset = kStartOffset; + irsend.reset(); + irsend.sendRaw(data, entries, 38000); + irsend.makeDecodeResult(); + uint16_t entries_used = 0; + + // Wanting too many bits should fail. + uint64_t result_data = 0; + entries_used = irrecv.matchGeneric( + irsend.capture.rawbuf + offset, &result_data, + irsend.capture.rawlen - offset, + 5, // nbits + 8000, 4000, // Header + 500, 2000, // one mark & space + 500, 1000, // zero mark & space + 3000, 15000, // Footer + true, // atleast on the footer space. + 1, // 1% Tolerance + 0, // No excess margin + true); // MSB first. + ASSERT_EQ(0, entries_used); + + // Bad header mark. + entries_used = irrecv.matchGeneric( + irsend.capture.rawbuf + offset, &result_data, + irsend.capture.rawlen - offset, + 4, // nbits + 2000, 4000, // Header + 500, 2000, // one mark & space + 500, 1000, // zero mark & space + 3000, 15000, // Footer + true, // atleast on the footer space. + 1, // 1% Tolerance + 0, // No excess margin + true); // MSB first. + ASSERT_EQ(0, entries_used); + + // Bad header space. + entries_used = irrecv.matchGeneric( + irsend.capture.rawbuf + offset, &result_data, + irsend.capture.rawlen - offset, + 4, // nbits + 8000, 2000, // Header + 500, 2000, // one mark & space + 500, 1000, // zero mark & space + 3000, 15000, // Footer + true, // atleast on the footer space. + 1, // 1% Tolerance + 0, // No excess margin + true); // MSB first. + ASSERT_EQ(0, entries_used); + + // Bad one mark. + entries_used = irrecv.matchGeneric( + irsend.capture.rawbuf + offset, &result_data, + irsend.capture.rawlen - offset, + 4, // nbits + 8000, 4000, // Header + 600, 2000, // one mark & space + 500, 1000, // zero mark & space + 3000, 15000, // Footer + true, // atleast on the footer space. + 1, // 1% Tolerance + 0, // No excess margin + true); // MSB first. + ASSERT_EQ(0, entries_used); + + // Bad one space. + entries_used = irrecv.matchGeneric( + irsend.capture.rawbuf + offset, &result_data, + irsend.capture.rawlen - offset, + 4, // nbits + 8000, 4000, // Header + 500, 2500, // one mark & space + 500, 1000, // zero mark & space + 3000, 15000, // Footer + true, // atleast on the footer space. + 1, // 1% Tolerance + 0, // No excess margin + true); // MSB first. + ASSERT_EQ(0, entries_used); + + // Bad zero space. + entries_used = irrecv.matchGeneric( + irsend.capture.rawbuf + offset, &result_data, + irsend.capture.rawlen - offset, + 4, // nbits + 8000, 4000, // Header + 500, 2000, // one mark & space + 500, 1500, // zero mark & space + 3000, 15000, // Footer + true, // atleast on the footer space. + 1, // 1% Tolerance + 0, // No excess margin + true); // MSB first. + ASSERT_EQ(0, entries_used); + + // Bad zero mark. + entries_used = irrecv.matchGeneric( + irsend.capture.rawbuf + offset, &result_data, + irsend.capture.rawlen - offset, + 4, // nbits + 8000, 4000, // Header + 500, 2000, // one mark & space + 900, 1000, // zero mark & space + 3000, 15000, // Footer + true, // atleast on the footer space. + 1, // 1% Tolerance + 0, // No excess margin + true); // MSB first. + ASSERT_EQ(0, entries_used); + + // Bad Footer mark. + entries_used = irrecv.matchGeneric( + irsend.capture.rawbuf + offset, &result_data, + irsend.capture.rawlen - offset, + 4, // nbits + 8000, 4000, // Header + 500, 2000, // one mark & space + 500, 1000, // zero mark & space + 1000, 15000, // Footer + true, // atleast on the footer space. + 1, // 1% Tolerance + 0, // No excess margin + true); // MSB first. + ASSERT_EQ(0, entries_used); + + // Confirm it really does match as expected.. + entries_used = irrecv.matchGeneric( + irsend.capture.rawbuf + offset, &result_data, + irsend.capture.rawlen - offset, + 4, // nbits + 8000, 4000, // Header + 500, 2000, // one mark & space + 500, 1000, // zero mark & space + 3000, 15000, // Footer + true, // atleast on the footer space. + 1, // 1% Tolerance + 0, // No excess margin + true); // MSB first. + ASSERT_NE(0, entries_used); +} + +TEST(TestMatchGeneric, MissingHeaderFooter) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + + uint16_t entries = 11; + uint16_t data[entries] = { + 8000, // Header mark + 4000, // Header space + 500, 2000, // Bit #0 (1) + 500, 1000, // Bit #1 (0) + 500, 2000, // Bit #2 (1) + 500, 1000, // Bit #3 (0) + 3000}; // Footer mark (No Footer space.) + + uint16_t offset = kStartOffset; + irsend.reset(); + irsend.sendRaw(data, entries, 38000); + irsend.makeDecodeResult(); + uint16_t entries_used = 0; + + uint64_t result_data = 0; + + // No footer match + entries_used = irrecv.matchGeneric( + irsend.capture.rawbuf + offset, &result_data, + irsend.capture.rawlen - offset, + 4, // nbits + 8000, 4000, // Header + 500, 2000, // one mark & space + 500, 1000, // zero mark & space + 0, 0, // NO Footer + true, // atleast on the footer space. + 1, // 1% Tolerance + 0, // No excess margin + true); // MSB first. + ASSERT_NE(0, entries_used); + EXPECT_EQ(0b1010, result_data); + EXPECT_EQ(irsend.capture.rawlen - kStartOffset - 1, entries_used); + EXPECT_EQ(entries - 1, entries_used); + + // No header match (should fail) + entries_used = irrecv.matchGeneric( + irsend.capture.rawbuf + offset, &result_data, + irsend.capture.rawlen - offset, + 4, // nbits + 0, 0, // NO Header + 500, 2000, // one mark & space + 500, 1000, // zero mark & space + 0, 0, // NO Footer + true, // atleast on the footer space. + 1, // 1% Tolerance + 0, // No excess margin + true); // MSB first. + ASSERT_EQ(0, entries_used); + + // No header match but starting after header + offset += 2; + entries_used = irrecv.matchGeneric( + irsend.capture.rawbuf + offset, &result_data, + irsend.capture.rawlen - offset, + 4, // nbits + 0, 0, // NO Header + 500, 2000, // one mark & space + 500, 1000, // zero mark & space + 3000, 15000, // Footer + true, // atleast on the footer space. + 1, // 1% Tolerance + 0, // No excess margin + true); // MSB first. + ASSERT_NE(0, entries_used); + EXPECT_EQ(0b1010, result_data); + EXPECT_EQ(irsend.capture.rawlen - offset, entries_used); + EXPECT_EQ(entries - 2, entries_used); +} + +TEST(TestMatchGeneric, BitOrdering) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + + uint16_t entries = 11; + uint16_t data[entries] = { + 8000, // Header mark + 4000, // Header space + 500, 2000, // Bit #0 (1) + 500, 1000, // Bit #1 (0) + 500, 2000, // Bit #2 (1) + 500, 1000, // Bit #3 (0) + 3000}; // Footer mark (No Footer space.) + + uint16_t offset = kStartOffset; + irsend.reset(); + irsend.sendRaw(data, entries, 38000); + irsend.makeDecodeResult(); + uint16_t entries_used = 0; + + uint64_t result_data = 0; + + // MSB order + entries_used = irrecv.matchGeneric( + irsend.capture.rawbuf + offset, &result_data, + irsend.capture.rawlen - offset, + 4, // nbits + 8000, 4000, // Header + 500, 2000, // one mark & space + 500, 1000, // zero mark & space + 3000, 15000, // Footer + true, // atleast on the footer space. + 1, // 1% Tolerance + 0, // No excess margin + true); // MSB first. + ASSERT_NE(0, entries_used); + EXPECT_EQ(0b1010, result_data); + EXPECT_EQ(irsend.capture.rawlen - kStartOffset, entries_used); + EXPECT_EQ(entries, entries_used); + + // LSB order + entries_used = irrecv.matchGeneric( + irsend.capture.rawbuf + offset, &result_data, + irsend.capture.rawlen - offset, + 4, // nbits + 8000, 4000, // Header + 500, 2000, // one mark & space + 500, 1000, // zero mark & space + 3000, 15000, // Footer + true, // atleast on the footer space. + 1, // 1% Tolerance + 0, // No excess margin + false); // LSB first. + ASSERT_NE(0, entries_used); + EXPECT_EQ(0b0101, result_data); + EXPECT_EQ(irsend.capture.rawlen - kStartOffset, entries_used); + EXPECT_EQ(entries, entries_used); +} + +TEST(TestMatchGeneric, UsingBytes) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + + uint16_t entries = 32; + uint16_t data[entries] = { + // No header + 500, 2000, // Byte #0 Bit #0 (1) + 500, 1000, // Byte #0 Bit #1 (0) + 500, 2000, // Byte #0 Bit #2 (1) + 500, 1000, // Byte #0 Bit #3 (0) + 500, 2000, // Byte #0 Bit #4 (1) + 500, 1000, // Byte #0 Bit #5 (0) + 500, 2000, // Byte #0 Bit #6 (1) + 500, 1000, // Byte #0 Bit #7 (0) + 500, 2000, // Byte #1 Bit #0 (1) + 500, 2000, // Byte #1 Bit #1 (1) + 500, 2000, // Byte #1 Bit #2 (1) + 500, 2000, // Byte #1 Bit #3 (1) + 500, 1000, // Byte #1 Bit #4 (0) + 500, 1000, // Byte #1 Bit #5 (0) + 500, 1000, // Byte #1 Bit #6 (0) + 500, 1000}; // Byte #1 Bit #7 (0) & No footer + + uint16_t offset = kStartOffset; + irsend.reset(); + irsend.sendRaw(data, entries, 38000); + irsend.makeDecodeResult(); + uint16_t entries_used = 0; + + uint8_t result_data[4] = {}; // Bigger than we need. + + // MSB order + entries_used = irrecv.matchGeneric( + irsend.capture.rawbuf + offset, result_data, + irsend.capture.rawlen - offset, + 2 * 8, // nbits + 0, 0, // No Header + 500, 2000, // one mark & space + 500, 1000, // zero mark & space + 0, 0, // No Footer + false, // atleast on the footer space. + 1, // 1% Tolerance + 0, // No excess margin + true); // MSB first. + ASSERT_NE(0, entries_used); + EXPECT_EQ(0b10101010, result_data[0]); + EXPECT_EQ(0b11110000, result_data[1]); + EXPECT_EQ(irsend.capture.rawlen - kStartOffset, entries_used); + EXPECT_EQ(entries, entries_used); + + // LSB order + entries_used = irrecv.matchGeneric( + irsend.capture.rawbuf + offset, result_data, + irsend.capture.rawlen - offset, + 2 * 8, // nbits + 0, 0, // No Header + 500, 2000, // one mark & space + 500, 1000, // zero mark & space + 0, 0, // No Footer + false, // atleast on the footer space. + 1, // 1% Tolerance + 0, // No excess margin + false); // MSB first. + ASSERT_NE(0, entries_used); + EXPECT_EQ(0b01010101, result_data[0]); + EXPECT_EQ(0b00001111, result_data[1]); + EXPECT_EQ(irsend.capture.rawlen - kStartOffset, entries_used); + EXPECT_EQ(entries, entries_used); + + // Asking for too much. + entries_used = irrecv.matchGeneric( + irsend.capture.rawbuf + offset, result_data, + irsend.capture.rawlen - offset, + 3 * 8, // nbits + 0, 0, // No Header + 500, 2000, // one mark & space + 500, 1000, // zero mark & space + 0, 0, // No Footer + false, // atleast on the footer space. + 1, // 1% Tolerance + 0, // No excess margin + true); // MSB first. + ASSERT_EQ(0, entries_used); + + // Asking for less than what is there. + entries_used = irrecv.matchGeneric( + irsend.capture.rawbuf + offset, result_data, + irsend.capture.rawlen - offset, + 1 * 8, // nbits + 0, 0, // No Header + 500, 2000, // one mark & space + 500, 1000, // zero mark & space + 0, 0, // No Footer + false, // atleast on the footer space. + 1, // 1% Tolerance + 0, // No excess margin + true); // MSB first. + ASSERT_NE(0, entries_used); + EXPECT_EQ(0b10101010, result_data[0]); + EXPECT_GT(irsend.capture.rawlen - kStartOffset, entries_used); + EXPECT_EQ(16, entries_used); + + // Asking for non mod-8 size should fail. + entries_used = irrecv.matchGeneric( + irsend.capture.rawbuf + offset, result_data, + irsend.capture.rawlen - offset, + 9, // nbits + 0, 0, // No Header + 500, 2000, // one mark & space + 500, 1000, // zero mark & space + 0, 0, // No Footer + false, // atleast on the footer space. + 1, // 1% Tolerance + 0, // No excess margin + true); // MSB first. + ASSERT_EQ(0, entries_used); + + // Expecting different timings should fail. + entries_used = irrecv.matchGeneric( + irsend.capture.rawbuf + offset, result_data, + irsend.capture.rawlen - offset, + 8, // nbits + 0, 0, // No Header + 500, 900, // one mark & space + 500, 1000, // zero mark & space + 0, 0, // No Footer + false, // atleast on the footer space. + 1, // 1% Tolerance + 0, // No excess margin + true); // MSB first. + ASSERT_EQ(0, entries_used); +} + +TEST(TestIRrecv, Tolerance) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + + uint16_t equal_encoded_raw[11] = {500, 1500, 1500, 500, 499, 1499, + 1501, 501, 1499, 490, 500}; + match_result_t result; + + ASSERT_EQ(kTolerance, irrecv.getTolerance()); + irrecv.setTolerance(); + ASSERT_EQ(kTolerance, irrecv.getTolerance()); + irrecv.setTolerance(kTolerance + 1); + ASSERT_EQ(kTolerance + 1, irrecv.getTolerance()); + irrecv.setTolerance(kTolerance - 1); + ASSERT_EQ(kTolerance - 1, irrecv.getTolerance()); + + irrecv.setTolerance(); + ASSERT_EQ(kTolerance, irrecv.getTolerance()); + + irsend.reset(); + irsend.sendRaw(equal_encoded_raw, 11, 38000); + irsend.makeDecodeResult(); + result = irrecv.matchData(irsend.capture.rawbuf + 1, 5, 1500, 500, 500, 1500); + ASSERT_TRUE(result.success); + EXPECT_EQ(0b01011, result.data); + EXPECT_EQ(10, result.used); + + irrecv.setTolerance(0); + ASSERT_EQ(0, irrecv.getTolerance()); + irsend.reset(); + irsend.sendRaw(equal_encoded_raw, 11, 38000); + irsend.makeDecodeResult(); + result = irrecv.matchData(irsend.capture.rawbuf + 1, 5, 1500, 500, 500, 1500); + ASSERT_FALSE(result.success); +} diff --git a/lib/IRremoteESP8266-2.6.0/test/IRrecv_test.h b/lib/IRremoteESP8266-2.6.5/test/IRrecv_test.h similarity index 100% rename from lib/IRremoteESP8266-2.6.0/test/IRrecv_test.h rename to lib/IRremoteESP8266-2.6.5/test/IRrecv_test.h diff --git a/lib/IRremoteESP8266-2.6.0/test/IRsend_test.cpp b/lib/IRremoteESP8266-2.6.5/test/IRsend_test.cpp similarity index 84% rename from lib/IRremoteESP8266-2.6.0/test/IRsend_test.cpp rename to lib/IRremoteESP8266-2.6.5/test/IRsend_test.cpp index ffd69cf71..fbf8ba229 100644 --- a/lib/IRremoteESP8266-2.6.0/test/IRsend_test.cpp +++ b/lib/IRremoteESP8266-2.6.5/test/IRsend_test.cpp @@ -1,6 +1,7 @@ // Copyright 2017,2019 David Conran #include "IRsend_test.h" +#include "IRrecv_test.h" #include "IRsend.h" #include "IRutils.h" #include "gtest/gtest.h" @@ -378,7 +379,7 @@ TEST(TestLowLevelSend, SpaceNoModulation) { EXPECT_EQ("[Off]1000usecs", irsend.low_level_sequence); } -// Test expected to work/produce a message for irsend:send() +// Test expected to work/produce a message for simple irsend:send() TEST(TestSend, GenericSimpleSendMethod) { IRsendTest irsend(0); IRrecv irrecv(0); @@ -657,7 +658,7 @@ TEST(TestSend, GenericSimpleSendMethod) { EXPECT_EQ(0x1234, irsend.capture.value); } -// Test some expected types to NOT work/produce a message via irsend:send() +// Test some expected types to NOT work/produce a message via irsend:send() TEST(TestSend, GenericSimpleSendMethodFailure) { IRsendTest irsend(0); IRrecv irrecv(0); @@ -670,17 +671,118 @@ TEST(TestSend, GenericSimpleSendMethodFailure) { ASSERT_FALSE(irrecv.decode(&irsend.capture)); // For every A/C protocol which decodes to having a state[]. - for (int i = 0; i < 200; i++) { + for (int i = 0; i <= kLastDecodeType; i++) { if (hasACState((decode_type_t)i) && i != GREE) { // Gree is an exception. // Check it fails. - ASSERT_FALSE(irsend.send((decode_type_t)i, 0, 0)); + ASSERT_FALSE(irsend.send((decode_type_t)i, (uint64_t)0, 0)); } } // Test some other special cases. - ASSERT_FALSE(irsend.send(UNKNOWN, 0, 0)); - ASSERT_FALSE(irsend.send(UNUSED, 0, 0)); - ASSERT_FALSE(irsend.send(RAW, 0, 0)); - ASSERT_FALSE(irsend.send(PRONTO, 0, 0)); - ASSERT_FALSE(irsend.send(GLOBALCACHE, 0, 0)); + ASSERT_FALSE(irsend.send(UNKNOWN, (uint64_t)0, 0)); + ASSERT_FALSE(irsend.send(UNUSED, (uint64_t)0, 0)); + ASSERT_FALSE(irsend.send(RAW, (uint64_t)0, 0)); + ASSERT_FALSE(irsend.send(PRONTO, (uint64_t)0, 0)); + ASSERT_FALSE(irsend.send(GLOBALCACHE, (uint64_t)0, 0)); +} + +// Test expected to work/produce a message for complex irsend:send() +TEST(TestSend, GenericComplexSendMethod) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + irsend.reset(); + uint8_t kelvinator[kKelvinatorStateLength] = { + 0x19, 0x0B, 0x80, 0x50, 0x00, 0x00, 0x00, 0xE0, + 0x19, 0x0B, 0x80, 0x70, 0x00, 0x00, 0x10, 0xF0}; + ASSERT_TRUE(irsend.send(KELVINATOR, kelvinator, kKelvinatorStateLength)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(KELVINATOR, irsend.capture.decode_type); + EXPECT_EQ(kKelvinatorStateLength * 8, irsend.capture.bits); + EXPECT_STATE_EQ(kelvinator, irsend.capture.state, irsend.capture.bits / 8); + + irsend.reset(); + uint8_t panasonic[kPanasonicAcStateLength] = { + 0x02, 0x20, 0xE0, 0x04, 0x00, 0x00, 0x00, 0x06, 0x02, + 0x20, 0xE0, 0x04, 0x00, 0x4E, 0x2E, 0x80, 0xAF, 0x00, + 0x00, 0x0E, 0xE0, 0x11, 0x00, 0x01, 0x00, 0x06, 0xB7}; + ASSERT_TRUE(irsend.send(PANASONIC_AC, panasonic, kPanasonicAcStateLength)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(PANASONIC_AC, irsend.capture.decode_type); + EXPECT_EQ(kPanasonicAcStateLength * 8, irsend.capture.bits); + EXPECT_STATE_EQ(panasonic, irsend.capture.state, irsend.capture.bits / 8); +} + +// Test some expected types to NOT work/produce a complex message via +// irsend:send() +TEST(TestSend, GenericComplexSendMethodFailure) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + // Check nothing is sent for unexpected protocols + uint8_t state[kStateSizeMax] = {}; + irsend.reset(); + ASSERT_FALSE(irsend.send(NEC, state, kNECBits)); + irsend.makeDecodeResult(); + ASSERT_FALSE(irrecv.decode(&irsend.capture)); + + // For every A/C protocol which DOESN'T decode to having a state[]. + for (int i = -1; i <= kLastDecodeType; i++) { + if (!hasACState((decode_type_t)i)) + // Check it fails. + ASSERT_FALSE(irsend.send((decode_type_t)i, state, 0)); + else // Or if it is okay. + ASSERT_TRUE(irsend.send((decode_type_t)i, state, 0)); + } +} + +TEST(TestSend, GenericSendExistsForEveryRealProtocol) { + IRsendTest irsend(0); + irsend.begin(); + + uint8_t state[kStateSizeMax] = {}; + uint64_t value = 0; + for (int i = 1; i <= kLastDecodeType; i++) { + switch (i) { + // Protocols that don't have a generic send equiv. + case PRONTO: + case RAW: + case GLOBALCACHE: + // Protocols that are disabled because they don't work. + case SANYO: + break; + default: + EXPECT_TRUE(irsend.send((decode_type_t)i, state, 0) || + irsend.send((decode_type_t)i, value, 0)) << + "Protocol " << typeToString((decode_type_t)i) << "(" << i << + ") doesn't have a generic send option for it."; + } + } +} + +TEST(TestSend, defaultBits) { + for (int i = 1; i <= kLastDecodeType; i++) { + switch (i) { + // Protocols that don't have have a default bit size. + case PRONTO: + case RAW: + case GLOBALCACHE: + case SANYO: // Not implemented / disabled. + // Deliberate no default size. + case FUJITSU_AC: + case MWM: + EXPECT_EQ(IRsend::defaultBits((decode_type_t)i), 0) << + "Protocol " << typeToString((decode_type_t)i) << "(" << i << + ") doesn't have a correct value for it."; + break; + default: + EXPECT_GT(IRsend::defaultBits((decode_type_t)i), 0) << + "Protocol " << typeToString((decode_type_t)i) << "(" << i << + ") doesn't have a correct value for it."; + } + } } diff --git a/lib/IRremoteESP8266-2.6.0/test/IRsend_test.h b/lib/IRremoteESP8266-2.6.5/test/IRsend_test.h similarity index 100% rename from lib/IRremoteESP8266-2.6.0/test/IRsend_test.h rename to lib/IRremoteESP8266-2.6.5/test/IRsend_test.h diff --git a/lib/IRremoteESP8266-2.6.0/test/IRutils_test.cpp b/lib/IRremoteESP8266-2.6.5/test/IRutils_test.cpp similarity index 74% rename from lib/IRremoteESP8266-2.6.0/test/IRutils_test.cpp rename to lib/IRremoteESP8266-2.6.5/test/IRutils_test.cpp index 4a4907649..366deba32 100644 --- a/lib/IRremoteESP8266-2.6.0/test/IRutils_test.cpp +++ b/lib/IRremoteESP8266-2.6.5/test/IRutils_test.cpp @@ -1,8 +1,9 @@ -// Copyright 2017 David Conran +// Copyright 2017-2019 David Conran #include "IRutils.h" #include #include "IRrecv.h" +#include "IRrecv_test.h" #include "IRsend.h" #include "IRsend_test.h" #include "gtest/gtest.h" @@ -403,15 +404,139 @@ TEST(TestStrToDecodeType, strToDecodeType) { } TEST(TestUtils, htmlEscape) { - EXPECT_EQ("", htmlEscape("")); - EXPECT_EQ("No Changes", htmlEscape("No Changes")); - EXPECT_EQ("No\tChanges+_%^$@~`\n:\\", htmlEscape("No\tChanges+_%^$@~`\n:\\")); - EXPECT_EQ(""With Changes"", htmlEscape("\"With Changes\"")); + EXPECT_EQ("", irutils::htmlEscape("")); + EXPECT_EQ("No Changes", irutils::htmlEscape("No Changes")); + EXPECT_EQ("No\tChanges+_%^$@~`\n:\\", + irutils::htmlEscape("No\tChanges+_%^$@~`\n:\\")); + EXPECT_EQ(""With Changes"", + irutils::htmlEscape("\"With Changes\"")); EXPECT_EQ( "';!‐"<>&#equals;&#{}" - "()", htmlEscape("';!-\"<>=&#{}()")); - EXPECT_EQ("""", htmlEscape("\"\"")); + "()", irutils::htmlEscape("';!-\"<>=&#{}()")); + EXPECT_EQ("""", irutils::htmlEscape("\"\"")); EXPECT_EQ( "&quot;&lt;&apos;&gt;&amp;", - htmlEscape(""<'>&")); + irutils::htmlEscape(""<'>&")); +} + +TEST(TestUtils, TemperatureConversion) { + // Freezing point of water. + ASSERT_EQ(32.0, celsiusToFahrenheit(0.0)); + ASSERT_EQ(0.0, fahrenheitToCelsius(32.0)); + // Boiling point of water. + ASSERT_EQ(212.0, celsiusToFahrenheit(100.0)); + ASSERT_EQ(100.0, fahrenheitToCelsius(212.0)); + // Room Temp. (RTP) + ASSERT_EQ(77.0, celsiusToFahrenheit(25.0)); + ASSERT_EQ(25.0, fahrenheitToCelsius(77.0)); + // Misc + ASSERT_EQ(-40.0, fahrenheitToCelsius(-40.0)); +} + +TEST(TestResultToRawArray, TypicalCase) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + + // Generate a known message. + irsend.reset(); + irsend.sendNikai(0xD0F2F); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(NIKAI, irsend.capture.decode_type); + ASSERT_EQ(kNikaiBits, irsend.capture.bits); + EXPECT_EQ( + "uint16_t rawData[52] = {4000, 4000, 500, 2000, 500, 2000, " + "500, 2000, 500, 2000, 500, 1000, 500, 1000, 500, 2000, 500, 1000, " + "500, 2000, 500, 2000, 500, 2000, 500, 2000, 500, 1000, 500, 1000, " + "500, 1000, 500, 1000, 500, 2000, 500, 2000, 500, 1000, 500, 2000, " + "500, 1000, 500, 1000, 500, 1000, 500, 1000, 500, 8500 };" + " // NIKAI D0F2F\n" + "uint64_t data = 0xD0F2F;\n", + resultToSourceCode(&irsend.capture)); + uint16_t rawData[52] = { // Data taken from above. + 4000, 4000, 500, 2000, 500, 2000, 500, 2000, 500, 2000, 500, 1000, 500, + 1000, 500, 2000, 500, 1000, 500, 2000, 500, 2000, 500, 2000, 500, 2000, + 500, 1000, 500, 1000, 500, 1000, 500, 1000, 500, 2000, 500, 2000, 500, + 1000, 500, 2000, 500, 1000, 500, 1000, 500, 1000, 500, 1000, 500, 8500}; + uint16_t * result = resultToRawArray(&irsend.capture); + ASSERT_EQ(52, getCorrectedRawLength(&irsend.capture)); + EXPECT_STATE_EQ(rawData, result, getCorrectedRawLength(&irsend.capture)); + if (result != NULL) delete[] result; +} + +TEST(TestResultToRawArray, LargeValues) { + IRsendTest irsend(0); + IRrecv irrecv(1); + uint16_t test_data[9] = {10, 20, 30, 40, 50, 60, 70, 80, 90}; + irsend.begin(); + irsend.reset(); + irsend.sendRaw(test_data, 9, 38000); + irsend.makeDecodeResult(); + irrecv.decode(&irsend.capture); + uint16_t * result = resultToRawArray(&irsend.capture); + ASSERT_EQ(9, getCorrectedRawLength(&irsend.capture)); + EXPECT_STATE_EQ(test_data, result, 9); + if (result != NULL) delete[] result; + // Stick in some large values. + irsend.capture.rawbuf[3] = 60000; + EXPECT_EQ( + "uint16_t rawData[11] = {10, 20, 65535, 0, 54465, 40, 50, 60, 70, " + "80, 90}; // UNKNOWN 54051FFD\n", + resultToSourceCode(&irsend.capture)); + uint16_t large_test_data[11] = { + 10, 20, 65535, 0, 54465, 40, 50, 60, 70, 80, 90}; + ASSERT_EQ(11, getCorrectedRawLength(&irsend.capture)); + result = resultToRawArray(&irsend.capture); + EXPECT_STATE_EQ(large_test_data, result, 11); + if (result != NULL) delete[] result; +} + +TEST(TestUtils, TypeStringConversionRangeTests) { + ASSERT_EQ("UNKNOWN", typeToString((decode_type_t)(kLastDecodeType + 1))); + ASSERT_EQ("UNKNOWN", typeToString(decode_type_t::UNKNOWN)); + for (int i = 0; i <= kLastDecodeType; i++) { + EXPECT_NE("UNKNOWN", typeToString((decode_type_t)i)) << "Protocol " << i << + " doesn't have a valid string for it."; + EXPECT_EQ(i, strToDecodeType(typeToString((decode_type_t)i).c_str())) << + "Protocol " << typeToString((decode_type_t)i) << + " doesn't decode from a string correctly"; + } +} + +TEST(TestUtils, MinsToString) { + EXPECT_EQ("00:00", irutils::minsToString(0)); + EXPECT_EQ("00:01", irutils::minsToString(1)); + EXPECT_EQ("00:10", irutils::minsToString(10)); + EXPECT_EQ("00:59", irutils::minsToString(59)); + + EXPECT_EQ("01:00", irutils::minsToString(60)); + EXPECT_EQ("01:01", irutils::minsToString(61)); + EXPECT_EQ("01:59", irutils::minsToString(60 + 59)); + EXPECT_EQ("18:59", irutils::minsToString(18 * 60 + 59)); + EXPECT_EQ("23:59", irutils::minsToString(23 * 60 + 59)); +} + +TEST(TestUtils, sumNibbles) { + uint8_t testdata[] = {0x01, 0x23, 0x45}; + EXPECT_EQ(0, irutils::sumNibbles(testdata, 0)); + EXPECT_EQ(1, irutils::sumNibbles(testdata, 0, 1)); + EXPECT_EQ(1, irutils::sumNibbles(testdata, 1)); + EXPECT_EQ(2, irutils::sumNibbles(testdata, 1, 1)); + EXPECT_EQ(15, irutils::sumNibbles(testdata, 3)); + EXPECT_EQ(115, irutils::sumNibbles(testdata, 3, 100)); +} + +TEST(TestUtils, BCD) { + EXPECT_EQ(0, irutils::uint8ToBcd(0)); + EXPECT_EQ(0, irutils::bcdToUint8(0)); + EXPECT_EQ(1, irutils::uint8ToBcd(1)); + EXPECT_EQ(10, irutils::bcdToUint8(0x10)); + EXPECT_EQ(0x10, irutils::uint8ToBcd(10)); + EXPECT_EQ(11, irutils::bcdToUint8(0x11)); + EXPECT_EQ(0x11, irutils::uint8ToBcd(11)); + EXPECT_EQ(99, irutils::bcdToUint8(0x99)); + EXPECT_EQ(0x99, irutils::uint8ToBcd(99)); + EXPECT_EQ(255, irutils::bcdToUint8(0x9A)); + EXPECT_EQ(255, irutils::uint8ToBcd(100)); } diff --git a/lib/IRremoteESP8266-2.6.0/test/Makefile b/lib/IRremoteESP8266-2.6.5/test/Makefile similarity index 89% rename from lib/IRremoteESP8266-2.6.0/test/Makefile rename to lib/IRremoteESP8266-2.6.5/test/Makefile index 9a64aaaaa..0e721d58c 100644 --- a/lib/IRremoteESP8266-2.6.0/test/Makefile +++ b/lib/IRremoteESP8266-2.6.5/test/Makefile @@ -38,7 +38,8 @@ TESTS = IRutils_test IRsend_test ir_NEC_test ir_GlobalCache_test \ ir_Carrier_test ir_Haier_test ir_Hitachi_test ir_GICable_test \ ir_Whirlpool_test ir_Lutron_test ir_Electra_test ir_Pioneer_test \ ir_MWM_test ir_Vestel_test ir_Teco_test ir_Tcl_test ir_Lego_test IRac_test \ - ir_MitsubishiHeavy_test + ir_MitsubishiHeavy_test ir_Trotec_test ir_Argo_test ir_Goodweather_test \ + ir_Inax_test ir_Neoclima_test ir_Amcor_test # All Google Test headers. Usually you shouldn't change this # definition. @@ -83,21 +84,27 @@ PROTOCOLS = ir_NEC.o ir_Sony.o ir_Samsung.o ir_JVC.o ir_RCMM.o ir_RC5_RC6.o \ ir_Midea.o ir_Magiquest.o ir_Lasertag.o ir_Carrier.o ir_Haier.o \ ir_Hitachi.o ir_GICable.o ir_Whirlpool.o ir_Lutron.o ir_Electra.o \ ir_Pioneer.o ir_MWM.o ir_Vestel.o ir_Teco.o ir_Tcl.o ir_Lego.o ir_Argo.o \ - ir_Trotec.o ir_MitsubishiHeavy.o + ir_Trotec.o ir_MitsubishiHeavy.o ir_Goodweather.o ir_Inax.o ir_Neoclima.o \ + ir_Amcor.o # All the IR Protocol header files. -PROTOCOLS_H = $(USER_DIR)/ir_Argo.h \ +PROTOCOLS_H = $(USER_DIR)/ir_Amcor.h \ + $(USER_DIR)/ir_Argo.h \ $(USER_DIR)/ir_Gree.h \ $(USER_DIR)/ir_Magiquest.h \ $(USER_DIR)/ir_Coolix.h \ + $(USER_DIR)/ir_Electra.h \ $(USER_DIR)/ir_Haier.h \ $(USER_DIR)/ir_Midea.h \ $(USER_DIR)/ir_Toshiba.h \ $(USER_DIR)/ir_Daikin.h \ + $(USER_DIR)/ir_Goodweather.h \ $(USER_DIR)/ir_Kelvinator.h \ $(USER_DIR)/ir_Mitsubishi.h \ $(USER_DIR)/ir_MitsubishiHeavy.h \ $(USER_DIR)/ir_NEC.h \ + $(USER_DIR)/ir_Neoclima.h \ + $(USER_DIR)/ir_Sharp.h \ $(USER_DIR)/ir_Samsung.h \ $(USER_DIR)/ir_Trotec.h \ $(USER_DIR)/ir_Fujitsu.h \ @@ -106,7 +113,8 @@ PROTOCOLS_H = $(USER_DIR)/ir_Argo.h \ $(USER_DIR)/ir_Whirlpool.h \ $(USER_DIR)/ir_Vestel.h \ $(USER_DIR)/ir_Tcl.h \ - $(USER_DIR)/ir_Teco.h + $(USER_DIR)/ir_Teco.h \ + $(USER_DIR)/ir_Trotec.h # Common object files COMMON_OBJ = IRutils.o IRtimer.o IRsend.o IRrecv.o IRac.o ir_GlobalCache.o \ $(PROTOCOLS) gtest_main.a @@ -288,7 +296,7 @@ ir_Fujitsu_test : $(COMMON_OBJ) ir_Fujitsu_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ ir_Sharp.o : $(USER_DIR)/ir_Sharp.cpp $(COMMON_DEPS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Sharp.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Sharp.cpp ir_Sharp_test.o : ir_Sharp_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Sharp_test.cpp @@ -494,8 +502,8 @@ ir_Lutron_test.o : ir_Lutron_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) ir_Lutron_test : $(COMMON_OBJ) ir_Lutron_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ -ir_Electra.o : $(USER_DIR)/ir_Electra.cpp $(COMMON_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Electra.cpp +ir_Electra.o : $(USER_DIR)/ir_Electra.h $(USER_DIR)/ir_Electra.cpp $(COMMON_DEPS) $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Electra.cpp ir_Electra_test.o : ir_Electra_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Electra_test.cpp @@ -560,5 +568,53 @@ ir_Lego_test : $(COMMON_OBJ) ir_Lego_test.o ir_Argo.o : $(USER_DIR)/ir_Argo.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Argo.cpp +ir_Argo_test.o : ir_Argo_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Argo_test.cpp + +ir_Argo_test : $(COMMON_OBJ) ir_Argo_test.o + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ + ir_Trotec.o : $(USER_DIR)/ir_Trotec.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Trotec.cpp + +ir_Trotec_test.o : ir_Trotec_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Trotec_test.cpp + +ir_Trotec_test : $(COMMON_OBJ) ir_Trotec_test.o + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ + +ir_Goodweather.o : $(USER_DIR)/ir_Goodweather.cpp $(COMMON_DEPS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Goodweather.cpp + +ir_Goodweather_test.o : ir_Goodweather_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Goodweather_test.cpp + +ir_Goodweather_test : $(COMMON_OBJ) ir_Goodweather_test.o + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ + +ir_Inax.o : $(USER_DIR)/ir_Inax.cpp $(COMMON_DEPS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Inax.cpp + +ir_Inax_test.o : ir_Inax_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Inax_test.cpp + +ir_Inax_test : $(COMMON_OBJ) ir_Inax_test.o + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ + +ir_Neoclima.o : $(USER_DIR)/ir_Neoclima.h $(USER_DIR)/ir_Neoclima.cpp $(COMMON_DEPS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Neoclima.cpp + +ir_Neoclima_test.o : ir_Neoclima_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Neoclima_test.cpp + +ir_Neoclima_test : $(COMMON_OBJ) ir_Neoclima_test.o + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ + +ir_Amcor.o : $(USER_DIR)/ir_Amcor.h $(USER_DIR)/ir_Amcor.cpp $(COMMON_DEPS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Amcor.cpp + +ir_Amcor_test.o : ir_Amcor_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Amcor_test.cpp + +ir_Amcor_test : $(COMMON_OBJ) ir_Amcor_test.o + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Aiwa_test.cpp b/lib/IRremoteESP8266-2.6.5/test/ir_Aiwa_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.6.0/test/ir_Aiwa_test.cpp rename to lib/IRremoteESP8266-2.6.5/test/ir_Aiwa_test.cpp diff --git a/lib/IRremoteESP8266-2.6.5/test/ir_Amcor_test.cpp b/lib/IRremoteESP8266-2.6.5/test/ir_Amcor_test.cpp new file mode 100644 index 000000000..265c61500 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.5/test/ir_Amcor_test.cpp @@ -0,0 +1,351 @@ +// Copyright 2019 David Conran + +#include "IRac.h" +#include "ir_Amcor.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "IRutils.h" +#include "gtest/gtest.h" + +TEST(TestUtils, Housekeeping) { + ASSERT_EQ("AMCOR", typeToString(decode_type_t::AMCOR)); + ASSERT_EQ(decode_type_t::AMCOR, strToDecodeType("AMCOR")); + ASSERT_TRUE(hasACState(decode_type_t::AMCOR)); + ASSERT_TRUE(IRac::isProtocolSupported(decode_type_t::AMCOR)); +} + +// Test sending typical data only. +TEST(TestSendAmcor, SendDataOnly) { + IRsendTest irsend(0); + irsend.begin(); + + uint8_t expectedState[kAmcorStateLength] = { + 0x01, 0x41, 0x36, 0x00, 0x00, 0x30, 0x00, 0x12}; + + irsend.reset(); + irsend.sendAmcor(expectedState); + EXPECT_EQ( + "f38000d50" + "m8200s4200" + "m1500s600m600s1500m600s1500m600s1500m600s1500m600s1500m600s1500m600s1500" + "m1500s600m600s1500m600s1500m600s1500m600s1500m600s1500m1500s600m600s1500" + "m600s1500m1500s600m1500s600m600s1500m1500s600m1500s600m600s1500m600s1500" + "m600s1500m600s1500m600s1500m600s1500m600s1500m600s1500m600s1500m600s1500" + "m600s1500m600s1500m600s1500m600s1500m600s1500m600s1500m600s1500m600s1500" + "m600s1500m600s1500m600s1500m600s1500m1500s600m1500s600m600s1500m600s1500" + "m600s1500m600s1500m600s1500m600s1500m600s1500m600s1500m600s1500m600s1500" + "m600s1500m1500s600m600s1500m600s1500m1500s600m600s1500m600s1500m600s1500" + "m1900s34300" + "m8200s4200" + "m1500s600m600s1500m600s1500m600s1500m600s1500m600s1500m600s1500m600s1500" + "m1500s600m600s1500m600s1500m600s1500m600s1500m600s1500m1500s600m600s1500" + "m600s1500m1500s600m1500s600m600s1500m1500s600m1500s600m600s1500m600s1500" + "m600s1500m600s1500m600s1500m600s1500m600s1500m600s1500m600s1500m600s1500" + "m600s1500m600s1500m600s1500m600s1500m600s1500m600s1500m600s1500m600s1500" + "m600s1500m600s1500m600s1500m600s1500m1500s600m1500s600m600s1500m600s1500" + "m600s1500m600s1500m600s1500m600s1500m600s1500m600s1500m600s1500m600s1500" + "m600s1500m1500s600m600s1500m600s1500m1500s600m600s1500m600s1500m600s1500" + "m1900s34300", + irsend.outputStr()); +} + +TEST(TestDecodeAmcor, SyntheticSelfDecode) { + IRsendTest irsend(0); + IRrecv irrecv(0); + IRAmcorAc ac(0); + + uint8_t expectedState[kAmcorStateLength] = { + 0x01, 0x41, 0x30, 0x00, 0x00, 0x30, 0x00, 0x0C}; + + irsend.begin(); + irsend.reset(); + irsend.sendAmcor(expectedState); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(AMCOR, irsend.capture.decode_type); + EXPECT_EQ(kAmcorBits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + ac.setRaw(irsend.capture.state); + EXPECT_EQ( + "Power: On, Mode: 1 (COOL), Fan: 4 (Auto), Temp: 24C, Max: Off", + ac.toString()); +} + +TEST(TestDecodeAmcor, RealExample) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + // Data from Issue #834 captured by ldellus + // Turn on, cooling, 27 deg C. + uint16_t rawData[263] = { + 8210, 4276, 1544, 480, 596, 1510, 596, 1510, 596, 1692, 388, 1534, 596, + 1510, 596, 1510, 596, 1684, 1450, 480, 596, 1510, 570, 1534, 570, 1718, 386, + 1536, 594, 1500, 1632, 482, 596, 1694, 362, 1550, 1632, 472, 1658, 456, 596, + 1684, 1474, 446, 1634, 480, 572, 1534, 572, 1718, 362, 1558, 572, 1534, 570, + 1534, 570, 1720, 360, 1558, 572, 1534, 570, 1534, 570, 1718, 360, 1560, 572, + 1534, 570, 1534, 570, 1718, 362, 1560, 572, 1532, 572, 1534, 570, 1718, 362, + 1558, 572, 1532, 572, 1534, 570, 1710, 1448, 472, 1634, 480, 572, 1534, 570, + 1718, 362, 1558, 572, 1534, 570, 1534, 572, 1716, 362, 1560, 572, 1534, 572, + 1534, 570, 1718, 362, 1550, 1634, 480, 570, 1536, 570, 1710, 1448, 482, 570, + 1534, 570, 1536, 570, 1508, 1856, 34298, + // rawData[132] is here. (8218) + 8218, 4314, 1502, 522, 530, 1576, 504, 1602, 504, 1786, 392, 1528, 504, + 1600, 504, 1600, 504, 1770, 1414, 522, 528, 1578, 502, 1602, 504, 1784, 394, + 1528, 504, 1584, 1574, 548, 528, 1762, 392, 1512, 1572, 530, 1600, 524, 528, + 1744, 1390, 530, 1574, 546, 506, 1600, 504, 1784, 394, 1528, 504, 1600, 578, + 1528, 504, 1784, 394, 1526, 504, 1600, 504, 1600, 506, 1784, 394, 1528, 504, + 1602, 504, 1602, 504, 1784, 394, 1526, 506, 1600, 504, 1600, 506, 1784, 392, + 1526, 506, 1600, 504, 1602, 502, 1768, 1390, 530, 1574, 548, 504, 1600, 504, + 1786, 392, 1530, 504, 1600, 504, 1600, 504, 1786, 392, 1528, 504, 1600, 504, + 1600, 506, 1784, 394, 1512, 1574, 548, 504, 1602, 504, 1768, 1388, 548, 504, + 1602, 504, 1602, 502, 1574, 1792}; // UNKNOWN D510A6EF + + uint8_t expectedState[kAmcorStateLength] = { + 0x01, 0x41, 0x36, 0x00, 0x00, 0x30, 0x00, 0x12}; + + irsend.reset(); + irsend.sendRaw(rawData, 263, 38000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(AMCOR, irsend.capture.decode_type); + EXPECT_EQ(kAmcorBits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + + // Verify the repeat is the same decode. + irsend.reset(); + irsend.sendRaw(rawData + 132, 131, 38000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(AMCOR, irsend.capture.decode_type); + EXPECT_EQ(kAmcorBits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + + // https://github.com/crankyoldgit/IRremoteESP8266/issues/834#issuecomment-515700254 + uint16_t rawData2[263] = {8252, 4294, 1518, 508, 544, 1560, 546, 1560, 570, + 1718, 416, 1504, 546, 1560, 570, 1532, 572, 1718, 1414, 506, 544, 1560, 570, + 1534, 570, 1718, 416, 1506, 544, 1558, 1598, 508, 544, 1746, 416, 1504, 546, + 1560, 570, 1534, 1598, 690, 1414, 508, 544, 1560, 546, 1560, 544, 1746, 416, + 1504, 546, 1560, 546, 1560, 570, 1718, 416, 1504, 544, 1560, 570, 1536, 544, + 1744, 416, 1506, 570, 1534, 546, 1558, 546, 1744, 418, 1502, 572, 1534, 544, + 1560, 570, 1720, 416, 1506, 544, 1560, 546, 1560, 544, 1744, 1414, 506, + 1572, 534, 544, 1560, 570, 1720, 416, 1504, 570, 1536, 544, 1560, 572, 1718, + 416, 1504, 570, 1542, 592, 1504, 570, 1720, 416, 1502, 1572, 534, 544, 1560, + 572, 1718, 1414, 508, 544, 1560, 570, 1534, 570, 1508, 1840, 34174, 8230, + 4292, 1546, 480, 546, 1560, 572, 1534, 570, 1718, 416, 1502, 572, 1532, 572, + 1532, 572, 1718, 1440, 480, 570, 1534, 572, 1534, 572, 1716, 418, 1504, 572, + 1532, 1626, 480, 572, 1718, 418, 1502, 574, 1534, 572, 1530, 1626, 662, + 1442, 480, 572, 1534, 572, 1534, 572, 1716, 418, 1502, 574, 1542, 592, 1504, + 598, 1692, 418, 1504, 572, 1532, 574, 1530, 574, 1716, 418, 1502, 598, 1508, + 572, 1532, 598, 1692, 418, 1502, 598, 1508, 572, 1532, 574, 1716, 418, 1504, + 598, 1508, 572, 1532, 574, 1716, 1442, 478, 1626, 480, 572, 1534, 572, 1718, + 392, 1526, 574, 1532, 572, 1532, 572, 1716, 418, 1502, 598, 1508, 574, 1532, + 598, 1700, 408, 1504, 1624, 480, 572, 1532, 574, 1716, 1440, 480, 572, 1532, + 572, 1532, 572, 1506, 1814}; // UNKNOWN ADA838FB + + uint8_t expectedState2[kAmcorStateLength] = { + 0x01, 0x41, 0x18, 0x00, 0x00, 0x30, 0x00, 0x12}; + + irsend.reset(); + irsend.sendRaw(rawData2, 263, 38000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(AMCOR, irsend.capture.decode_type); + EXPECT_EQ(kAmcorBits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState2, irsend.capture.state, irsend.capture.bits); +} + +// Tests for IRAmcorAc class. + +TEST(TestAmcorAcClass, Power) { + IRAmcorAc ac(0); + ac.begin(); + + ac.on(); + EXPECT_TRUE(ac.getPower()); + + ac.off(); + EXPECT_FALSE(ac.getPower()); + + ac.setPower(true); + EXPECT_TRUE(ac.getPower()); + + ac.setPower(false); + EXPECT_FALSE(ac.getPower()); +} + +TEST(TestAmcorAcClass, Temperature) { + IRAmcorAc ac(0); + ac.begin(); + + ac.setTemp(0); + EXPECT_EQ(kAmcorMinTemp, ac.getTemp()); + + ac.setTemp(255); + EXPECT_EQ(kAmcorMaxTemp, ac.getTemp()); + + ac.setTemp(kAmcorMinTemp); + EXPECT_EQ(kAmcorMinTemp, ac.getTemp()); + + ac.setTemp(kAmcorMaxTemp); + EXPECT_EQ(kAmcorMaxTemp, ac.getTemp()); + + ac.setTemp(kAmcorMinTemp - 1); + EXPECT_EQ(kAmcorMinTemp, ac.getTemp()); + + ac.setTemp(kAmcorMaxTemp + 1); + EXPECT_EQ(kAmcorMaxTemp, ac.getTemp()); + + ac.setTemp(17); + EXPECT_EQ(17, ac.getTemp()); + + ac.setTemp(21); + EXPECT_EQ(21, ac.getTemp()); + + ac.setTemp(25); + EXPECT_EQ(25, ac.getTemp()); + + ac.setTemp(29); + EXPECT_EQ(29, ac.getTemp()); +} + +TEST(TestAmcorAcClass, OperatingMode) { + IRAmcorAc ac(0); + ac.begin(); + + ac.setMode(kAmcorAuto); + EXPECT_EQ(kAmcorAuto, ac.getMode()); + + ac.setMode(kAmcorCool); + EXPECT_EQ(kAmcorCool, ac.getMode()); + + ac.setMode(kAmcorHeat); + EXPECT_EQ(kAmcorHeat, ac.getMode()); + + ac.setMode(kAmcorDry); + EXPECT_EQ(kAmcorDry, ac.getMode()); + + ac.setMode(kAmcorFan); + EXPECT_EQ(kAmcorFan, ac.getMode()); + + ac.setMode(kAmcorAuto + 1); + EXPECT_EQ(kAmcorAuto, ac.getMode()); + + ac.setMode(255); + EXPECT_EQ(kAmcorAuto, ac.getMode()); +} + +TEST(TestAmcorAcClass, FanSpeed) { + IRAmcorAc ac(0); + ac.begin(); + + ac.setFan(0); + EXPECT_EQ(kAmcorFanAuto, ac.getFan()); + + ac.setFan(255); + EXPECT_EQ(kAmcorFanAuto, ac.getFan()); + + ac.setFan(kAmcorFanMax); + EXPECT_EQ(kAmcorFanMax, ac.getFan()); + + ac.setFan(kAmcorFanMax + 1); + EXPECT_EQ(kAmcorFanAuto, ac.getFan()); + + ac.setFan(kAmcorFanMax - 1); + EXPECT_EQ(kAmcorFanMax - 1, ac.getFan()); + + ac.setFan(1); + EXPECT_EQ(1, ac.getFan()); + + ac.setFan(1); + EXPECT_EQ(1, ac.getFan()); + + ac.setFan(3); + EXPECT_EQ(3, ac.getFan()); +} + +TEST(TestAmcorAcClass, Checksums) { + uint8_t state[kAmcorStateLength] = { + 0x01, 0x41, 0x30, 0x00, 0x00, 0x30, 0x00, 0x0C}; + + ASSERT_EQ(0x0C, IRAmcorAc::calcChecksum(state)); + EXPECT_TRUE(IRAmcorAc::validChecksum(state)); + // Change the array so the checksum is invalid. + state[0] ^= 0xFF; + EXPECT_FALSE(IRAmcorAc::validChecksum(state)); + // Restore the previous change, and change another byte. + state[0] ^= 0xFF; + state[4] ^= 0xFF; + EXPECT_FALSE(IRAmcorAc::validChecksum(state)); + state[4] ^= 0xFF; + EXPECT_TRUE(IRAmcorAc::validChecksum(state)); + + // Additional known good states. + uint8_t knownGood1[kAmcorStateLength] = { + 0x01, 0x11, 0x3E, 0x00, 0x00, 0x30, 0x00, 0x17}; + EXPECT_TRUE(IRAmcorAc::validChecksum(knownGood1)); + ASSERT_EQ(0x17, IRAmcorAc::calcChecksum(knownGood1)); + uint8_t knownGood2[kAmcorStateLength] = { + 0x01, 0x22, 0x26, 0x00, 0x00, 0x30, 0x00, 0x10}; + EXPECT_TRUE(IRAmcorAc::validChecksum(knownGood2)); + ASSERT_EQ(0x10, IRAmcorAc::calcChecksum(knownGood2)); + uint8_t knownGood3[kAmcorStateLength] = { + 0x01, 0x41, 0x24, 0x00, 0x00, 0xC0, 0x00, 0x18}; + EXPECT_TRUE(IRAmcorAc::validChecksum(knownGood3)); + ASSERT_EQ(0x18, IRAmcorAc::calcChecksum(knownGood3)); + + // For a recalculation. + uint8_t knownBad[kAmcorStateLength] = { + // Same as knownGood3 except for the checksum. + 0x01, 0x41, 0x24, 0x00, 0x00, 0xC0, 0x00, 0x00}; + EXPECT_FALSE(IRAmcorAc::validChecksum(knownBad)); + IRAmcorAc ac(0); + ac.setRaw(knownBad); + EXPECT_STATE_EQ(knownGood3, ac.getRaw(), kAmcorBits); +} + +TEST(TestAmcorAcClass, Max) { + IRAmcorAc ac(0); + ac.begin(); + + ac.setMode(kAmcorCool); + ac.setMax(true); + EXPECT_EQ(kAmcorCool, ac.getMode()); + EXPECT_EQ(kAmcorMinTemp, ac.getTemp()); + EXPECT_TRUE(ac.getMax()); + ac.setMax(false); + EXPECT_EQ(kAmcorCool, ac.getMode()); + EXPECT_EQ(kAmcorMinTemp, ac.getTemp()); + EXPECT_FALSE(ac.getMax()); + + ac.setMode(kAmcorHeat); + ac.setMax(true); + EXPECT_EQ(kAmcorHeat, ac.getMode()); + EXPECT_EQ(kAmcorMaxTemp, ac.getTemp()); + EXPECT_TRUE(ac.getMax()); + ac.setMax(false); + EXPECT_EQ(kAmcorHeat, ac.getMode()); + EXPECT_EQ(kAmcorMaxTemp, ac.getTemp()); + EXPECT_FALSE(ac.getMax()); + + ac.setMode(kAmcorAuto); + ac.setTemp(25); + ac.setMax(true); + EXPECT_EQ(kAmcorAuto, ac.getMode()); + EXPECT_EQ(25, ac.getTemp()); + EXPECT_FALSE(ac.getMax()); + + // Test known real data. + uint8_t lo[kAmcorStateLength] = { + 0x01, 0x41, 0x18, 0x00, 0x00, 0x30, 0x03, 0x15}; + uint8_t hi[kAmcorStateLength] = { + 0x01, 0x12, 0x40, 0x00, 0x00, 0x30, 0x03, 0x0E}; + ac.setRaw(lo); + EXPECT_EQ("Power: On, Mode: 1 (COOL), Fan: 4 (Auto), Temp: 12C, Max: On", + ac.toString()); + ac.setRaw(hi); + EXPECT_EQ("Power: On, Mode: 2 (HEAT), Fan: 1 (Low), Temp: 32C, Max: On", + ac.toString()); +} diff --git a/lib/IRremoteESP8266-2.6.5/test/ir_Argo_test.cpp b/lib/IRremoteESP8266-2.6.5/test/ir_Argo_test.cpp new file mode 100644 index 000000000..7932416e0 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.5/test/ir_Argo_test.cpp @@ -0,0 +1,221 @@ +// Copyright 2019 David Conran +#include "ir_Argo.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "gtest/gtest.h" + + +TEST(TestArgoACClass, toCommon) { + IRArgoAC ac(0); + ac.setPower(true); + ac.setMode(kArgoCool); + ac.setTemp(20); + ac.setFan(kArgoFan3); + ac.setMax(true); + ac.setNight(true); + // Now test it. + ASSERT_EQ(decode_type_t::ARGO, ac.toCommon().protocol); + ASSERT_TRUE(ac.toCommon().power); + ASSERT_TRUE(ac.toCommon().celsius); + ASSERT_EQ(20, ac.toCommon().degrees); + ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode); + ASSERT_EQ(stdAc::fanspeed_t::kMax, ac.toCommon().fanspeed); + ASSERT_EQ(0, ac.toCommon().sleep); + ASSERT_TRUE(ac.toCommon().turbo); + // Unsupported. + ASSERT_EQ(-1, ac.toCommon().model); + ASSERT_EQ(stdAc::swingv_t::kOff, ac.toCommon().swingv); + ASSERT_EQ(stdAc::swingh_t::kOff, ac.toCommon().swingh); + ASSERT_FALSE(ac.toCommon().econo); + ASSERT_FALSE(ac.toCommon().light); + ASSERT_FALSE(ac.toCommon().filter); + ASSERT_FALSE(ac.toCommon().clean); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_FALSE(ac.toCommon().quiet); + ASSERT_EQ(-1, ac.toCommon().clock); +} + +TEST(TestArgoACClass, MessageConstructon) { + IRArgoAC ac(0); + ac.setPower(true); + ac.setTemp(20); + ac.setMode(kArgoCool); + ac.setFan(kArgoFanAuto); + ac.setRoomTemp(21); + ac.setiFeel(true); + ac.setMax(true); + ac.setNight(true); + + // Don't implicitly trust this. It's just a guess. + uint8_t expected[kArgoStateLength] = { + 0xAC, 0xF5, 0x00, 0x24, 0x02, 0x00, 0x00, 0x00, 0x00, 0xAC, 0xD6, 0x01}; + EXPECT_STATE_EQ(expected, ac.getRaw(), kArgoBits); + EXPECT_EQ( + "Power: On, Mode: 0 (COOL), Fan: 0 (AUTO), Temp: 20C, Room Temp: 21C, " + "Max: On, iFeel: On, Night: On", + ac.toString()); +} + +// Tests for sendArgo(). + +// Test sending typical data only. +TEST(TestSendArgo, SendDataOnly) { + IRsendTest irsend(0); + irsend.begin(); + uint8_t data[kArgoStateLength] = { + 0xAC, 0xF5, 0x00, 0x24, 0x02, 0x00, 0x00, 0x00, 0x00, 0xAC, 0xD6, 0x01}; + + irsend.sendArgo(data); + EXPECT_EQ( + "f38000d50" + "m6400s3300" + "m400s900m400s900m400s2200m400s2200m400s900m400s2200m400s900m400s2200" + "m400s2200m400s900m400s2200m400s900m400s2200m400s2200m400s2200m400s2200" + "m400s900m400s900m400s900m400s900m400s900m400s900m400s900m400s900" + "m400s900m400s900m400s2200m400s900m400s900m400s2200m400s900m400s900" + "m400s900m400s2200m400s900m400s900m400s900m400s900m400s900m400s900" + "m400s900m400s900m400s900m400s900m400s900m400s900m400s900m400s900" + "m400s900m400s900m400s900m400s900m400s900m400s900m400s900m400s900" + "m400s900m400s900m400s900m400s900m400s900m400s900m400s900m400s900" + "m400s900m400s900m400s900m400s900m400s900m400s900m400s900m400s900" + "m400s900m400s900m400s2200m400s2200m400s900m400s2200m400s900m400s2200" + "m400s900m400s2200m400s2200m400s900m400s2200m400s900m400s2200m400s2200" + "m400s2200m400s900m400s900m400s900m400s900m400s900m400s900" + "m400s900", + irsend.outputStr()); +} + +// Tests for decodeArgo(). +// Decode normal Argo messages. + +TEST(TestDecodeArgo, SyntheticDecode) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + // Synthesised Normal Argo message. + irsend.reset(); + uint8_t expectedState[kArgoStateLength] = { + 0xAC, 0xF5, 0x00, 0x24, 0x02, 0x00, 0x00, 0x00, 0x00, 0xAC, 0xD6, 0x01}; + irsend.sendArgo(expectedState); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(decode_type_t::ARGO, irsend.capture.decode_type); + EXPECT_EQ(kArgoBits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); +} + + +TEST(TestArgoACClass, SetAndGetTemp) { + IRArgoAC ac(0); + + ac.setTemp(25); + EXPECT_EQ(25, ac.getTemp()); + ac.setTemp(kArgoMinTemp); + EXPECT_EQ(kArgoMinTemp, ac.getTemp()); + ac.setTemp(kArgoMaxTemp); + EXPECT_EQ(kArgoMaxTemp, ac.getTemp()); + ac.setTemp(kArgoMinTemp - 1); + EXPECT_EQ(kArgoMinTemp, ac.getTemp()); + ac.setTemp(kArgoMaxTemp + 1); + EXPECT_EQ(kArgoMaxTemp, ac.getTemp()); +} + +TEST(TestArgoACClass, SetAndGetRoomTemp) { + IRArgoAC ac(0); + + ac.setRoomTemp(25); + EXPECT_EQ(25, ac.getRoomTemp()); + ac.setRoomTemp(kArgoTempOffset); + EXPECT_EQ(kArgoTempOffset, ac.getRoomTemp()); + ac.setRoomTemp(kArgoMaxRoomTemp); + EXPECT_EQ(kArgoMaxRoomTemp, ac.getRoomTemp()); + ac.setRoomTemp(kArgoTempOffset - 1); + EXPECT_EQ(kArgoTempOffset, ac.getRoomTemp()); + ac.setRoomTemp(kArgoMaxRoomTemp + 1); + EXPECT_EQ(kArgoMaxRoomTemp, ac.getRoomTemp()); +} + +TEST(TestArgoACClass, SetAndGetMode) { + IRArgoAC ac(0); + + ac.setMode(kArgoHeat); + EXPECT_EQ(kArgoHeat, ac.getMode()); + ac.setMode(kArgoCool); + EXPECT_EQ(kArgoCool, ac.getMode()); + ac.setMode(kArgoDry); + EXPECT_EQ(kArgoDry, ac.getMode()); + ac.setMode(kArgoAuto); + EXPECT_EQ(kArgoAuto, ac.getMode()); + ac.setMode(kArgoHeatAuto); + EXPECT_EQ(kArgoHeatAuto, ac.getMode()); + ac.setMode(kArgoOff); + EXPECT_EQ(kArgoOff, ac.getMode()); + ac.setMode(255); + EXPECT_EQ(kArgoAuto, ac.getMode()); +} + +TEST(TestArgoACClass, SetAndGetFan) { + IRArgoAC ac(0); + + ac.setFan(kArgoFan3); + EXPECT_EQ(kArgoFan3, ac.getFan()); + ac.setFan(kArgoFan1); + EXPECT_EQ(kArgoFan1, ac.getFan()); + ac.setFan(kArgoFanAuto); + EXPECT_EQ(kArgoFanAuto, ac.getFan()); + ac.setFan(kArgoFan3); + EXPECT_EQ(kArgoFan3, ac.getFan()); + ASSERT_NE(7, kArgoFan3); + // Now try some unexpected value. + ac.setFan(7); + EXPECT_EQ(kArgoFan3, ac.getFan()); +} + +TEST(TestArgoACClass, Night) { + IRArgoAC ac(0); + ac.setNight(false); + ASSERT_FALSE(ac.getNight()); + ac.setNight(true); + ASSERT_TRUE(ac.getNight()); + ac.setNight(false); + ASSERT_FALSE(ac.getNight()); +} + +TEST(TestArgoACClass, iFeel) { + IRArgoAC ac(0); + ac.setiFeel(false); + ASSERT_FALSE(ac.getiFeel()); + ac.setiFeel(true); + ASSERT_TRUE(ac.getiFeel()); + ac.setiFeel(false); + ASSERT_FALSE(ac.getiFeel()); +} + +TEST(TestArgoACClass, Power) { + IRArgoAC ac(0); + ac.setPower(false); + ASSERT_FALSE(ac.getPower()); + ac.setPower(true); + ASSERT_TRUE(ac.getPower()); + ac.setPower(false); + ASSERT_FALSE(ac.getPower()); +} + +TEST(TestArgoACClass, Max) { + IRArgoAC ac(0); + ac.setMax(false); + ASSERT_FALSE(ac.getMax()); + ac.setMax(true); + ASSERT_TRUE(ac.getMax()); + ac.setMax(false); + ASSERT_FALSE(ac.getMax()); +} + +TEST(TestUtils, Housekeeping) { + ASSERT_EQ("ARGO", typeToString(decode_type_t::ARGO)); + ASSERT_EQ(decode_type_t::ARGO, strToDecodeType("ARGO")); + ASSERT_TRUE(hasACState(decode_type_t::ARGO)); +} diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Carrier_test.cpp b/lib/IRremoteESP8266-2.6.5/test/ir_Carrier_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.6.0/test/ir_Carrier_test.cpp rename to lib/IRremoteESP8266-2.6.5/test/ir_Carrier_test.cpp diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Coolix_test.cpp b/lib/IRremoteESP8266-2.6.5/test/ir_Coolix_test.cpp similarity index 79% rename from lib/IRremoteESP8266-2.6.0/test/ir_Coolix_test.cpp rename to lib/IRremoteESP8266-2.6.5/test/ir_Coolix_test.cpp index 0f97c5ead..618d825a0 100644 --- a/lib/IRremoteESP8266-2.6.0/test/ir_Coolix_test.cpp +++ b/lib/IRremoteESP8266-2.6.5/test/ir_Coolix_test.cpp @@ -31,7 +31,7 @@ TEST(TestSendCoolix, SendDataOnly) { "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" - "m560s5040", + "m560s105040", irsend.outputStr()); irsend.reset(); @@ -53,7 +53,7 @@ TEST(TestSendCoolix, SendDataOnly) { "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" - "m560s5040", + "m560s105040", irsend.outputStr()); irsend.reset(); @@ -75,7 +75,7 @@ TEST(TestSendCoolix, SendDataOnly) { "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" - "m560s5040", + "m560s105040", irsend.outputStr()); } @@ -103,7 +103,7 @@ TEST(TestSendCoolix, SendWithRepeats) { "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" - "m560s5040", + "m560s105040", irsend.outputStr()); irsend.sendCOOLIX(0xAA55AA, kCoolixBits, 2); // 2 repeats. EXPECT_EQ( @@ -131,7 +131,7 @@ TEST(TestSendCoolix, SendWithRepeats) { "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" - "m560s5040", + "m560s105040", irsend.outputStr()); } @@ -151,7 +151,7 @@ TEST(TestSendCoolix, SendUnusualSize) { "m4480s4480" "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" - "m560s5040", + "m560s105040", irsend.outputStr()); irsend.reset(); @@ -193,7 +193,7 @@ TEST(TestSendCoolix, SendUnusualSize) { "m560s560m560s560m560s1680m560s1680m560s560m560s560m560s1680m560s560" "m560s1680m560s1680m560s1680m560s560m560s1680m560s1680m560s1680m560s1680" "m560s560m560s560m560s560m560s1680m560s560m560s560m560s560m560s560" - "m560s5040", + "m560s105040", irsend.outputStr()); // Bit sizes must be a multiple of 8. @@ -395,6 +395,8 @@ TEST(TestCoolixACClass, SetAndGetMode) { TEST(TestCoolixACClass, SetAndGetFan) { IRCoolixAC ircoolix(0); + // This mode allows pretty much everything except Auto0 speed. + ircoolix.setMode(kCoolixCool); ircoolix.setFan(kCoolixFanMax); EXPECT_EQ(kCoolixFanMax, ircoolix.getFan()); ircoolix.setFan(kCoolixFanMin); @@ -403,12 +405,29 @@ TEST(TestCoolixACClass, SetAndGetFan) { EXPECT_EQ(kCoolixFanZoneFollow, ircoolix.getFan()); ircoolix.setFan(kCoolixFanAuto); EXPECT_EQ(kCoolixFanAuto, ircoolix.getFan()); + ircoolix.setFan(kCoolixFanAuto0); + EXPECT_EQ(kCoolixFanAuto, ircoolix.getFan()); ircoolix.setFan(kCoolixFanMax); EXPECT_EQ(kCoolixFanMax, ircoolix.getFan()); ASSERT_NE(3, kCoolixFanAuto); // Now try some unexpected value. ircoolix.setFan(3); EXPECT_EQ(kCoolixFanAuto, ircoolix.getFan()); + + // These modes allows pretty much everything except Auto speed. + ircoolix.setMode(kCoolixDry); + EXPECT_EQ(kCoolixFanAuto0, ircoolix.getFan()); + ircoolix.setFan(kCoolixFanMax); + EXPECT_EQ(kCoolixFanMax, ircoolix.getFan()); + ircoolix.setFan(kCoolixFanAuto); + EXPECT_EQ(kCoolixFanAuto0, ircoolix.getFan()); + + ircoolix.setMode(kCoolixAuto); + EXPECT_EQ(kCoolixFanAuto0, ircoolix.getFan()); + ircoolix.setFan(kCoolixFanMax); + EXPECT_EQ(kCoolixFanMax, ircoolix.getFan()); + ircoolix.setFan(kCoolixFanAuto0); + EXPECT_EQ(kCoolixFanAuto0, ircoolix.getFan()); } TEST(TestCoolixACClass, SetGetClearSensorTempAndZoneFollow) { @@ -547,7 +566,7 @@ TEST(TestCoolixACClass, RealCaptureExample) { // Tests to debug/fix: -// https://github.com/markszabo/IRremoteESP8266/issues/624 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/624 TEST(TestCoolixACClass, Issue624HandleSpecialStatesBetter) { IRCoolixAC ac(0); ac.begin(); @@ -597,3 +616,132 @@ TEST(TestCoolixACClass, Issue624HandleSpecialStatesBetter) { ac.toString()); EXPECT_EQ(0xB2BF40, ac.getRaw()); } + +TEST(TestCoolixACClass, toCommon) { + IRCoolixAC ac(0); + ac.setPower(true); + ac.setMode(kCoolixCool); + ac.setTemp(20); + ac.setFan(kCoolixFanMax); + + // Now test it. + ASSERT_EQ(decode_type_t::COOLIX, ac.toCommon().protocol); + ASSERT_EQ(-1, ac.toCommon().model); + ASSERT_TRUE(ac.toCommon().power); + ASSERT_TRUE(ac.toCommon().celsius); + ASSERT_EQ(20, ac.toCommon().degrees); + ASSERT_FALSE(ac.toCommon().turbo); + ASSERT_FALSE(ac.toCommon().clean); + ASSERT_FALSE(ac.toCommon().light); + ASSERT_EQ(-1, ac.toCommon().sleep); + ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode); + ASSERT_EQ(stdAc::fanspeed_t::kMax, ac.toCommon().fanspeed); + ASSERT_EQ(stdAc::swingv_t::kOff, ac.toCommon().swingv); + ASSERT_EQ(stdAc::swingh_t::kOff, ac.toCommon().swingh); + // Unsupported. + ASSERT_FALSE(ac.toCommon().quiet); + ASSERT_FALSE(ac.toCommon().econo); + ASSERT_FALSE(ac.toCommon().filter); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_EQ(-1, ac.toCommon().clock); +} + +TEST(TestCoolixACClass, Issue722) { + IRrecv irrecv(0); + IRCoolixAC ac(0); + + // Auto 17C ON pressed + uint32_t on_auto_17c_fan_auto0 = 0xB21F08; + ac.begin(); + ac.setPower(true); + ac.setMode(kCoolixAuto); + ac.setFan(kCoolixFanAuto); + ac.setTemp(17); + EXPECT_EQ(on_auto_17c_fan_auto0, ac.getRaw()); + + // Off + uint32_t off = 0xB27BE0; + ac.off(); + EXPECT_EQ(off, ac.getRaw()); + + // ON Auto Temp 18C + uint32_t on_auto_18c_fan_auto0 = 0xB21F18; + ac.setTemp(18); + EXPECT_EQ(on_auto_18c_fan_auto0, ac.getRaw()); + + // Set Mode Cool 18C + uint32_t on_cool_18c_fan_auto = 0xB2BF10; + ac.setMode(kCoolixCool); + EXPECT_EQ(on_cool_18c_fan_auto, ac.getRaw()); + + // Set Mode DRY 18C + uint32_t on_dry_18c_fan_auto0 = 0xB21F14; + ac.setMode(kCoolixDry); + EXPECT_EQ(on_dry_18c_fan_auto0, ac.getRaw()); + + // Set Mode HEAT 18C + uint32_t on_heat_18c_fan_auto = 0xB2BF1C; + ac.setMode(kCoolixHeat); + EXPECT_EQ(on_heat_18c_fan_auto, ac.getRaw()); + + // Set mode FAN + uint32_t on_fan_18c_fan_auto = 0xB2BFE4; + ac.setMode(kCoolixFan); + EXPECT_EQ(on_fan_18c_fan_auto, ac.getRaw()); + + // Fan level 2 (initial was auto) + uint32_t on_fan_18c_fan_min = 0xB29FE4; + ac.setFan(kCoolixFanMin); + EXPECT_EQ(on_fan_18c_fan_min, ac.getRaw()); + + // Fan level 3 + uint32_t on_fan_18c_fan_med = 0xB25FE4; + ac.setFan(kCoolixFanMed); + EXPECT_EQ(on_fan_18c_fan_med, ac.getRaw()); + + // Fan level 4 + uint32_t on_fan_18c_fan_max = 0xB23FE4; + ac.setFan(kCoolixFanMax); + EXPECT_EQ(on_fan_18c_fan_max, ac.getRaw()); + + // Test sending the last message to verify the class send() method works. + ac.send(); + ac._irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&ac._irsend.capture)); + EXPECT_EQ(COOLIX, ac._irsend.capture.decode_type); + EXPECT_EQ(kCoolixBits, ac._irsend.capture.bits); + EXPECT_EQ(on_fan_18c_fan_max, ac._irsend.capture.value); + EXPECT_EQ(0x0, ac._irsend.capture.address); + EXPECT_EQ(0x0, ac._irsend.capture.command); + EXPECT_EQ( + // Raw data supplied by @mariusmotea + "f38000d50" + // 4434,4376, + "m4480s4480" + // 566,1614,592,504,566,1618,566,1616,568,528,564,532,564,1616,568,532, + "m560s1680m560s560m560s1680m560s1680m560s560m560s560m560s1680m560s560" + // 566,530,566,1620,568,528,566,530,566,1618,564,1618,566,530,564,1624, + "m560s560m560s1680m560s560m560s560m560s1680m560s1680m560s560m560s1680" + // 538,560,566,530,564,1620,566,1618,566,1618,566,1616,566,1616,566,1620, + "m560s560m560s560m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" + // 568,1620,566,1616,566,530,566,530,564,530,562,532,564,530,566,530, + "m560s1680m560s1680m560s560m560s560m560s560m560s560m560s560m560s560" + // 566,1622,566,1616,540,1642,566,528,566,530,566,1616,566,530,566,532, + "m560s1680m560s1680m560s1680m560s560m560s560m560s1680m560s560m560s560" + // 564,532,564,530,566,530,566,1614,566,1616,562,532,564,1620,566,1618, + "m560s560m560s560m560s560m560s1680m560s1680m560s560m560s1680m560s1680" + // 538,5254,4432,4364,566,1616,568,530,564,1620,568,1616,564,532,564,530, + "m560s5040m4480s4480m560s1680m560s560m560s1680m560s1680m560s560m560s560" + // 566,1616,566,532,564,532,566,1620,568,528,566,530,566,1616,564,1618, + "m560s1680m560s560m560s560m560s1680m560s560m560s560m560s1680m560s1680" + // 566,530,566,1622,566,532,566,528,566,1620,568,1614,566,1618,566,1618, + "m560s560m560s1680m560s560m560s560m560s1680m560s1680m560s1680m560s1680" + // 566,1614,568,1618,566,1622,568,1616,566,530,564,530,566,530,566,528, + "m560s1680m560s1680m560s1680m560s1680m560s560m560s560m560s560m560s560" + // 564,530,566,532,566,1622,564,1616,566,1616,564,532,564,530,564,1616, + "m560s560m560s560m560s1680m560s1680m560s1680m560s560m560s560m560s1680" + // 564,530,564,532,566,530,564,530,566,528,564,1618,564,1618,564,532, + "m560s560m560s560m560s560m560s560m560s560m560s1680m560s1680m560s560" + // 564,1620,566,1618,562 // Raw data matches what is expected. + "m560s1680m560s1680m560s105040", ac._irsend.outputStr()); +} diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Daikin_test.cpp b/lib/IRremoteESP8266-2.6.5/test/ir_Daikin_test.cpp similarity index 62% rename from lib/IRremoteESP8266-2.6.0/test/ir_Daikin_test.cpp rename to lib/IRremoteESP8266-2.6.5/test/ir_Daikin_test.cpp index 67d144d54..774109c3a 100644 --- a/lib/IRremoteESP8266-2.6.0/test/ir_Daikin_test.cpp +++ b/lib/IRremoteESP8266-2.6.5/test/ir_Daikin_test.cpp @@ -1,5 +1,6 @@ -// Copyright 2017 David Conran +// Copyright 2017-2019 David Conran #include "ir_Daikin.h" +#include "IRac.h" #include "IRrecv.h" #include "IRrecv_test.h" #include "IRsend.h" @@ -540,33 +541,47 @@ TEST(TestDaikinClass, OnOffTimers) { ASSERT_FALSE(ac.getPowerful()); } -// Test Eye mode. -TEST(TestDaikinClass, EyeSetting) { +TEST(TestDaikinClass, WeeklyTimerEnable) { IRDaikinESP ac(0); ac.begin(); - // The Eye setting is stored in the same byte as Econo mode. + // The Weekly Timer Enabled flag is stored in the same byte as Econo mode. // Econo mode tests are there to make sure it isn't harmed and vice-versa. ac.setEcono(false); - ac.setEye(false); - ASSERT_FALSE(ac.getEye()); + ac.setWeeklyTimerEnable(false); + ASSERT_FALSE(ac.getWeeklyTimerEnable()); EXPECT_FALSE(ac.getEcono()); - ac.setEye(true); - ASSERT_TRUE(ac.getEye()); + ac.setWeeklyTimerEnable(true); + ASSERT_TRUE(ac.getWeeklyTimerEnable()); EXPECT_FALSE(ac.getEcono()); ac.setEcono(false); - ASSERT_TRUE(ac.getEye()); + ASSERT_TRUE(ac.getWeeklyTimerEnable()); EXPECT_FALSE(ac.getEcono()); ac.setEcono(true); - ASSERT_TRUE(ac.getEye()); + ASSERT_TRUE(ac.getWeeklyTimerEnable()); EXPECT_TRUE(ac.getEcono()); - ac.setEye(false); - ASSERT_FALSE(ac.getEye()); + ac.setWeeklyTimerEnable(false); + ASSERT_FALSE(ac.getWeeklyTimerEnable()); EXPECT_TRUE(ac.getEcono()); + + // Tests with real data from: + // https://github.com/crankyoldgit/IRremoteESP8266/issues/704#issuecomment-493731421 + uint8_t on[kDaikinStateLength] = { + 0x11, 0xDA, 0x27, 0x00, 0xC5, 0x00, 0x00, 0xD7, 0x11, 0xDA, 0x27, 0x00, + 0x42, 0xE3, 0x0B, 0x42, 0x11, 0xDA, 0x27, 0x00, 0x00, 0x68, 0x32, 0x00, + 0x30, 0x00, 0x00, 0x06, 0x60, 0x00, 0x00, 0xC1, 0x00, 0x00, 0x03}; + uint8_t off[kDaikinStateLength] = { + 0x11, 0xDA, 0x27, 0x00, 0xC5, 0x00, 0x00, 0xD7, 0x11, 0xDA, 0x27, 0x00, + 0x42, 0xE3, 0x0B, 0x42, 0x11, 0xDA, 0x27, 0x00, 0x00, 0x68, 0x32, 0x00, + 0x30, 0x00, 0x00, 0x06, 0x60, 0x00, 0x00, 0xC1, 0x80, 0x00, 0x83}; + ac.setRaw(on); + EXPECT_TRUE(ac.getWeeklyTimerEnable()); + ac.setRaw(off); + EXPECT_FALSE(ac.getWeeklyTimerEnable()); } // Test Mold mode. @@ -614,13 +629,6 @@ TEST(TestDaikinClass, SensorSetting) { ASSERT_FALSE(ac.getSensor()); } -TEST(TestDaikinClass, RenderTime) { - EXPECT_EQ("0:00", IRDaikinESP::renderTime(0)); - EXPECT_EQ("0:10", IRDaikinESP::renderTime(10)); - EXPECT_EQ("1:00", IRDaikinESP::renderTime(1 * 60 + 0)); - EXPECT_EQ("23:59", IRDaikinESP::renderTime(23 * 60 + 59)); -} - TEST(TestDaikinClass, SetAndGetRaw) { IRDaikinESP ac(0); uint8_t shortState[kDaikinStateLengthShort] = { @@ -683,30 +691,33 @@ TEST(TestDaikinClass, HumanReadable) { IRDaikinESP ac(0); EXPECT_EQ( - "Power: On, Mode: 4 (HEAT), Temp: 15C, Fan: 11 (QUIET), " - "Powerful: Off, Quiet: Off, Sensor: Off, Eye: Off, Mold: Off, " + "Power: On, Mode: 4 (HEAT), Temp: 15C, Fan: 11 (Quiet), " + "Powerful: Off, Quiet: Off, Sensor: Off, Mold: Off, " "Comfort: Off, Swing (Horizontal): Off, Swing (Vertical): Off, " - "Current Time: 0:00, On Time: Off, Off Time: Off", + "Current Time: 00:00, Current Day: (UNKNOWN), On Time: Off, " + "Off Time: Off, Weekly Timer: On", ac.toString()); ac.setMode(kDaikinAuto); ac.setTemp(25); ac.setFan(kDaikinFanAuto); ac.setQuiet(true); ac.setSensor(true); - ac.setEye(true); ac.setMold(true); ac.setSwingVertical(true); ac.setSwingHorizontal(true); ac.setCurrentTime(9 * 60 + 15); + ac.setCurrentDay(4); ac.enableOnTimer(8 * 60 + 0); ac.enableOffTimer(17 * 60 + 30); ac.setComfort(true); + ac.setWeeklyTimerEnable(false); ac.off(); EXPECT_EQ( - "Power: Off, Mode: 0 (AUTO), Temp: 25C, Fan: 10 (AUTO), " - "Powerful: Off, Quiet: On, Sensor: On, Eye: On, Mold: On, Comfort: On, " + "Power: Off, Mode: 0 (AUTO), Temp: 25C, Fan: 10 (Auto), " + "Powerful: Off, Quiet: On, Sensor: On, Mold: On, Comfort: On, " "Swing (Horizontal): On, Swing (Vertical): On, " - "Current Time: 9:15, On Time: 8:00, Off Time: 17:30", + "Current Time: 09:15, Current Day: WED, On Time: 08:00, Off Time: 17:30, " + "Weekly Timer: Off", ac.toString()); } @@ -855,10 +866,11 @@ TEST(TestDecodeDaikin, RealExample) { EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); ac.setRaw(irsend.capture.state); EXPECT_EQ( - "Power: On, Mode: 3 (COOL), Temp: 29C, Fan: 10 (AUTO), Powerful: On, " - "Quiet: Off, Sensor: Off, Eye: Off, Mold: Off, Comfort: Off, " + "Power: On, Mode: 3 (COOL), Temp: 29C, Fan: 10 (Auto), Powerful: On, " + "Quiet: Off, Sensor: Off, Mold: Off, Comfort: Off, " "Swing (Horizontal): Off, Swing (Vertical): Off, " - "Current Time: 22:18, On Time: 21:30, Off Time: 6:10", ac.toString()); + "Current Time: 22:18, Current Day: (UNKNOWN), " + "On Time: 21:30, Off Time: 06:10, Weekly Timer: On", ac.toString()); } // Decoding a message we entirely constructed based solely on a given state. @@ -887,10 +899,11 @@ TEST(TestDecodeDaikin, ShortSyntheticExample) { EXPECT_STATE_EQ(longState, irsend.capture.state, irsend.capture.bits); ac.setRaw(irsend.capture.state); EXPECT_EQ( - "Power: On, Mode: 3 (COOL), Temp: 29C, Fan: 10 (AUTO), Powerful: On, " - "Quiet: Off, Sensor: Off, Eye: Off, Mold: Off, Comfort: Off, " + "Power: On, Mode: 3 (COOL), Temp: 29C, Fan: 10 (Auto), Powerful: On, " + "Quiet: Off, Sensor: Off, Mold: Off, Comfort: Off, " "Swing (Horizontal): Off, Swing (Vertical): Off, " - "Current Time: 22:18, On Time: 21:30, Off Time: 6:10", ac.toString()); + "Current Time: 22:18, Current Day: (UNKNOWN), " + "On Time: 21:30, Off Time: 06:10, Weekly Timer: On", ac.toString()); } // Decoding a message we entirely constructed based solely on a given state. @@ -915,10 +928,11 @@ TEST(TestDecodeDaikin, LongSyntheticExample) { EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); ac.setRaw(irsend.capture.state); EXPECT_EQ( - "Power: On, Mode: 3 (COOL), Temp: 29C, Fan: 10 (AUTO), Powerful: On, " - "Quiet: Off, Sensor: Off, Eye: Off, Mold: Off, Comfort: Off, " + "Power: On, Mode: 3 (COOL), Temp: 29C, Fan: 10 (Auto), Powerful: On, " + "Quiet: Off, Sensor: Off, Mold: Off, Comfort: Off, " "Swing (Horizontal): Off, Swing (Vertical): Off, " - "Current Time: 22:18, On Time: 21:30, Off Time: 6:10", ac.toString()); + "Current Time: 22:18, Current Day: (UNKNOWN), " + "On Time: 21:30, Off Time: 06:10, Weekly Timer: On", ac.toString()); } // Test decoding a message captured from a real IR remote. @@ -932,7 +946,7 @@ TEST(TestDecodeDaikin2, RealExample) { 0x80, 0x04, 0xB0, 0x16, 0x24, 0x00, 0x00, 0xBE, 0xD5, 0xF5, 0x11, 0xDA, 0x27, 0x00, 0x00, 0x08, 0x26, 0x00, 0xA0, 0x00, 0x00, 0x06, 0x60, 0x00, 0x00, 0xC1, 0x80, 0x60, 0xE7}; - // "Off" Data from https://github.com/markszabo/IRremoteESP8266/issues/582 + // "Off" Data from https://github.com/crankyoldgit/IRremoteESP8266/issues/582 uint16_t rawData[633] = { // Data supplied by @sheppy99 10024, 25180, 3494, 1732, 436, 1300, 436, 436, 432, 438, 430, 438, 426, 1306, 430, 442, 430, 438, 428, 440, 430, 440, 430, 1304, @@ -1428,9 +1442,9 @@ TEST(TestDaikin2Class, HumanReadable) { ac.setPurify(true); ac.setEcono(false); EXPECT_EQ( - "Power: On, Mode: 3 (COOL), Temp: 21C, Fan: 5 (Max), " + "Power: On, Mode: 3 (COOL), Temp: 21C, Fan: 5 (High), " "Swing (V): 14 (Auto), Swing (H): 191 (Swing), Clock: 12:34, " - "On Time: Off, Off Time: 20:00, Sleep Time: 4:00, Beep: 2 (Loud), " + "On Time: Off, Off Time: 20:00, Sleep Time: 04:00, Beep: 2 (Loud), " "Light: 2 (Dim), Mold: On, Clean: Off, Fresh Air: On, Eye: On, " "Eye Auto: On, Quiet: Off, Powerful: On, Purify: On, Econo: Off", ac.toString()); @@ -1443,9 +1457,9 @@ TEST(TestDaikin2Class, HumanReadable) { ac.setCurrentTime(23 * 60 + 45); // 23:45 ac.enableOnTimer(9 * 60 + 11); // 9:11 EXPECT_EQ( - "Power: On, Mode: 4 (HEAT), Temp: 32C, Fan: 1 (Min), " + "Power: On, Mode: 4 (HEAT), Temp: 32C, Fan: 1 (Low), " "Swing (V): 14 (Auto), Swing (H): 191 (Swing), Clock: 23:45, " - "On Time: 9:11, Off Time: 20:00, Sleep Time: Off, Beep: 1 (Quiet), " + "On Time: 09:11, Off Time: 20:00, Sleep Time: Off, Beep: 1 (Quiet), " "Light: 1 (Bright), Mold: On, Clean: Off, Fresh Air: On, Eye: On, " "Eye Auto: On, Quiet: On, Powerful: Off, Purify: On, Econo: Off", ac.toString()); @@ -1498,17 +1512,40 @@ TEST(TestUtils, Housekeeping) { ASSERT_EQ("DAIKIN", typeToString(decode_type_t::DAIKIN)); ASSERT_EQ(decode_type_t::DAIKIN, strToDecodeType("DAIKIN")); ASSERT_TRUE(hasACState(decode_type_t::DAIKIN)); + ASSERT_TRUE(IRac::isProtocolSupported(decode_type_t::DAIKIN)); + + ASSERT_EQ("DAIKIN128", typeToString(decode_type_t::DAIKIN128)); + ASSERT_EQ(decode_type_t::DAIKIN128, strToDecodeType("DAIKIN128")); + ASSERT_TRUE(hasACState(decode_type_t::DAIKIN128)); + ASSERT_TRUE(IRac::isProtocolSupported(decode_type_t::DAIKIN128)); + + ASSERT_EQ("DAIKIN152", typeToString(decode_type_t::DAIKIN152)); + ASSERT_EQ(decode_type_t::DAIKIN152, strToDecodeType("DAIKIN152")); + ASSERT_TRUE(hasACState(decode_type_t::DAIKIN152)); + ASSERT_FALSE(IRac::isProtocolSupported(decode_type_t::DAIKIN152)); + + ASSERT_EQ("DAIKIN160", typeToString(decode_type_t::DAIKIN160)); + ASSERT_EQ(decode_type_t::DAIKIN160, strToDecodeType("DAIKIN160")); + ASSERT_TRUE(hasACState(decode_type_t::DAIKIN160)); + ASSERT_TRUE(IRac::isProtocolSupported(decode_type_t::DAIKIN160)); + + ASSERT_EQ("DAIKIN176", typeToString(decode_type_t::DAIKIN176)); + ASSERT_EQ(decode_type_t::DAIKIN176, strToDecodeType("DAIKIN176")); + ASSERT_TRUE(hasACState(decode_type_t::DAIKIN176)); + ASSERT_TRUE(IRac::isProtocolSupported(decode_type_t::DAIKIN176)); ASSERT_EQ("DAIKIN2", typeToString(decode_type_t::DAIKIN2)); ASSERT_EQ(decode_type_t::DAIKIN2, strToDecodeType("DAIKIN2")); ASSERT_TRUE(hasACState(decode_type_t::DAIKIN2)); + ASSERT_TRUE(IRac::isProtocolSupported(decode_type_t::DAIKIN2)); ASSERT_EQ("DAIKIN216", typeToString(decode_type_t::DAIKIN216)); ASSERT_EQ(decode_type_t::DAIKIN216, strToDecodeType("DAIKIN216")); ASSERT_TRUE(hasACState(decode_type_t::DAIKIN216)); + ASSERT_TRUE(IRac::isProtocolSupported(decode_type_t::DAIKIN216)); } -// https://github.com/markszabo/IRremoteESP8266/issues/582#issuecomment-453863879 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/582#issuecomment-453863879 TEST(TestDecodeDaikin2, Issue582DeepDecodeExample) { IRDaikin2 ac(0); @@ -1524,10 +1561,10 @@ TEST(TestDecodeDaikin2, Issue582DeepDecodeExample) { ASSERT_TRUE(ac.getPurify()); EXPECT_EQ( "Power: On, Mode: 0 (AUTO), Temp: 19C, Fan: 10 (Auto), " - "Swing (V): 14 (Auto), Swing (H): 190 (Auto), Clock: 9:20, On Time: Off, " - "Off Time: Off, Sleep Time: Off, Beep: 3 (Off), Light: 3 (Off), " - "Mold: On, Clean: On, Fresh Air: Off, Eye: On, Eye Auto: Off, " - "Quiet: Off, Powerful: Off, Purify: On, Econo: Off", + "Swing (V): 14 (Auto), Swing (H): 190 (Auto), Clock: 09:20, " + "On Time: Off, Off Time: Off, Sleep Time: Off, Beep: 3 (Off), " + "Light: 3 (Off), Mold: On, Clean: On, Fresh Air: Off, Eye: On, " + "Eye Auto: Off, Quiet: Off, Powerful: Off, Purify: On, Econo: Off", ac.toString()); } @@ -1677,7 +1714,6 @@ TEST(TestDaikin216Class, OperatingMode) { EXPECT_EQ(kDaikinAuto, ac.getMode()); } - TEST(TestDaikin216Class, VaneSwing) { IRDaikin216 ac(0); ac.begin(); @@ -1744,15 +1780,34 @@ TEST(TestDaikin216Class, FanSpeed) { EXPECT_EQ(kDaikinFanQuiet, ac.getFan()); } -TEST(TestDaikin216Class, Quiet) { +TEST(TestDaikin216Class, QuietAndPowerful) { IRDaikin216 ac(0); ac.begin(); + ac.setQuiet(false); + ac.setPowerful(false); + EXPECT_FALSE(ac.getQuiet()); + EXPECT_FALSE(ac.getPowerful()); + ac.setQuiet(true); EXPECT_TRUE(ac.getQuiet()); + EXPECT_FALSE(ac.getPowerful()); + + ac.setPowerful(true); + EXPECT_FALSE(ac.getQuiet()); + EXPECT_TRUE(ac.getPowerful()); + + ac.setQuiet(true); + EXPECT_TRUE(ac.getQuiet()); + EXPECT_FALSE(ac.getPowerful()); ac.setQuiet(false); EXPECT_FALSE(ac.getQuiet()); + EXPECT_FALSE(ac.getPowerful()); + + ac.setPowerful(true); + EXPECT_FALSE(ac.getQuiet()); + EXPECT_TRUE(ac.getPowerful()); ac.setQuiet(true); EXPECT_TRUE(ac.getQuiet()); @@ -1761,22 +1816,23 @@ TEST(TestDaikin216Class, Quiet) { TEST(TestDaikin216Class, ExampleStates) { IRDaikin216 ac(0); ac.begin(); - // https://github.com/markszabo/IRremoteESP8266/pull/690#issuecomment-487770194 + // https://github.com/crankyoldgit/IRremoteESP8266/pull/690#issuecomment-487770194 uint8_t state[kDaikin216StateLength] = { 0x11, 0xDA, 0x27, 0xF0, 0x00, 0x00, 0x00, 0x02, 0x11, 0xDA, 0x27, 0x00, 0x00, 0x21, 0xC0, 0x00, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x53}; ac.setRaw(state); EXPECT_EQ( - "Power: On, Mode: 2 (DRY), Temp: 32C, Fan: 10 (AUTO), " - "Swing (Horizontal): Off, Swing (Vertical): Off, Quiet: Off", + "Power: On, Mode: 2 (DRY), Temp: 32C, Fan: 10 (Auto), " + "Swing (Horizontal): Off, Swing (Vertical): Off, " + "Quiet: Off, Powerful: Off", ac.toString()); } TEST(TestDaikin216Class, ReconstructKnownState) { IRDaikin216 ac(0); ac.begin(); - // https://github.com/markszabo/IRremoteESP8266/issues/689#issue-438086949 + // https://github.com/crankyoldgit/IRremoteESP8266/issues/689#issue-438086949 uint8_t expectedState[kDaikin216StateLength] = { 0x11, 0xDA, 0x27, 0xF0, 0x00, 0x00, 0x00, 0x02, 0x11, 0xDA, 0x27, 0x00, 0x00, 0x00, 0x26, 0x00, 0xA0, 0x00, @@ -1789,18 +1845,19 @@ TEST(TestDaikin216Class, ReconstructKnownState) { ac.setSwingVertical(false); ac.setQuiet(false); EXPECT_EQ( - "Power: Off, Mode: 0 (AUTO), Temp: 19C, Fan: 10 (AUTO), " - "Swing (Horizontal): Off, Swing (Vertical): Off, Quiet: Off", + "Power: Off, Mode: 0 (AUTO), Temp: 19C, Fan: 10 (Auto), " + "Swing (Horizontal): Off, Swing (Vertical): Off, " + "Quiet: Off, Powerful: Off", ac.toString()); EXPECT_STATE_EQ(expectedState, ac.getRaw(), kDaikin216Bits); } -// https://github.com/markszabo/IRremoteESP8266/issues/689 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/689 TEST(TestDecodeDaikin216, RealExample) { IRsendTest irsend(0); IRrecv irrecv(0); - // https://github.com/markszabo/IRremoteESP8266/issues/689#issue-438086949 + // https://github.com/crankyoldgit/IRremoteESP8266/issues/689#issue-438086949 uint16_t rawData[439] = { 3402, 1770, 382, 1340, 382, 480, 382, 478, 382, 480, 380, 1342, 382, 478, 356, 504, 382, 480, 380, 478, 384, 1342, 380, 480, 380, 1342, 382, 1342, @@ -1852,16 +1909,17 @@ TEST(TestDecodeDaikin216, RealExample) { IRDaikin216 ac(0); ac.setRaw(irsend.capture.state); EXPECT_EQ( - "Power: Off, Mode: 0 (AUTO), Temp: 19C, Fan: 10 (AUTO), " - "Swing (Horizontal): Off, Swing (Vertical): Off, Quiet: Off", + "Power: Off, Mode: 0 (AUTO), Temp: 19C, Fan: 10 (Auto), " + "Swing (Horizontal): Off, Swing (Vertical): Off, " + "Quiet: Off, Powerful: Off", ac.toString()); } -// https://github.com/markszabo/IRremoteESP8266/issues/689 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/689 TEST(TestDecodeDaikin216, SyntheticExample) { IRsendTest irsend(0); IRrecv irrecv(0); - // https://github.com/markszabo/IRremoteESP8266/issues/689#issue-438086949 + // https://github.com/crankyoldgit/IRremoteESP8266/issues/689#issue-438086949 uint8_t expectedState[kDaikin216StateLength] = { // 8 bytes 0x11, 0xDA, 0x27, 0xF0, 0x00, 0x00, 0x00, 0x02, @@ -1878,3 +1936,1051 @@ TEST(TestDecodeDaikin216, SyntheticExample) { ASSERT_EQ(kDaikin216Bits, irsend.capture.bits); EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); } + +TEST(TestDaikinClass, toCommon) { + IRDaikinESP ac(0); + ac.setPower(true); + ac.setMode(kDaikinCool); + ac.setTemp(20); + ac.setFan(kDaikinFanMax); + ac.setSwingVertical(true); + ac.setSwingHorizontal(true); + ac.setQuiet(false); + ac.setPowerful(true); + ac.setEcono(false); + ac.setMold(true); + // Now test it. + ASSERT_EQ(decode_type_t::DAIKIN, ac.toCommon().protocol); + ASSERT_EQ(-1, ac.toCommon().model); + ASSERT_TRUE(ac.toCommon().power); + ASSERT_TRUE(ac.toCommon().celsius); + ASSERT_EQ(20, ac.toCommon().degrees); + ASSERT_TRUE(ac.toCommon().turbo); + ASSERT_TRUE(ac.toCommon().clean); + ASSERT_FALSE(ac.toCommon().quiet); + ASSERT_FALSE(ac.toCommon().econo); + ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode); + ASSERT_EQ(stdAc::fanspeed_t::kMax, ac.toCommon().fanspeed); + ASSERT_EQ(stdAc::swingv_t::kAuto, ac.toCommon().swingv); + ASSERT_EQ(stdAc::swingh_t::kAuto, ac.toCommon().swingh); + // Unsupported. + ASSERT_FALSE(ac.toCommon().filter); + ASSERT_FALSE(ac.toCommon().light); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_EQ(-1, ac.toCommon().sleep); + ASSERT_EQ(-1, ac.toCommon().clock); +} + +TEST(TestDaikin2Class, toCommon) { + IRDaikin2 ac(0); + ac.setPower(true); + ac.setMode(kDaikinCool); + ac.setTemp(20); + ac.setFan(kDaikinFanMax); + ac.setSwingVertical(kDaikin2SwingVHigh + 3); + ac.setSwingHorizontal(kDaikin2SwingHAuto); + ac.setQuiet(false); + ac.setPowerful(true); + ac.setEcono(false); + ac.setMold(true); + ac.setLight(true); + ac.setPurify(true); + ac.setBeep(true); + ac.enableSleepTimer(6 * 60); + // Now test it. + ASSERT_EQ(decode_type_t::DAIKIN2, ac.toCommon().protocol); + ASSERT_EQ(-1, ac.toCommon().model); + ASSERT_TRUE(ac.toCommon().power); + ASSERT_TRUE(ac.toCommon().celsius); + ASSERT_EQ(20, ac.toCommon().degrees); + ASSERT_TRUE(ac.toCommon().turbo); + ASSERT_TRUE(ac.toCommon().clean); + ASSERT_FALSE(ac.toCommon().quiet); + ASSERT_FALSE(ac.toCommon().econo); + ASSERT_TRUE(ac.toCommon().light); + ASSERT_TRUE(ac.toCommon().filter); + ASSERT_TRUE(ac.toCommon().beep); + ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode); + ASSERT_EQ(stdAc::fanspeed_t::kMax, ac.toCommon().fanspeed); + ASSERT_EQ(stdAc::swingv_t::kMiddle, ac.toCommon().swingv); + ASSERT_EQ(stdAc::swingh_t::kAuto, ac.toCommon().swingh); + ASSERT_EQ(6 * 60, ac.toCommon().sleep); + // Unsupported. + ASSERT_EQ(-1, ac.toCommon().clock); +} + +TEST(TestDaikin216Class, toCommon) { + IRDaikin216 ac(0); + ac.setPower(true); + ac.setMode(kDaikinCool); + ac.setTemp(20); + ac.setFan(kDaikinFanMax); + ac.setSwingVertical(true); + ac.setSwingHorizontal(true); + ac.setQuiet(false); + ac.setPowerful(true); + // Now test it. + ASSERT_EQ(decode_type_t::DAIKIN216, ac.toCommon().protocol); + ASSERT_EQ(-1, ac.toCommon().model); + ASSERT_TRUE(ac.toCommon().power); + ASSERT_TRUE(ac.toCommon().celsius); + ASSERT_EQ(20, ac.toCommon().degrees); + ASSERT_TRUE(ac.toCommon().turbo); + ASSERT_FALSE(ac.toCommon().quiet); + ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode); + ASSERT_EQ(stdAc::fanspeed_t::kMax, ac.toCommon().fanspeed); + ASSERT_EQ(stdAc::swingv_t::kAuto, ac.toCommon().swingv); + ASSERT_EQ(stdAc::swingh_t::kAuto, ac.toCommon().swingh); + // Unsupported. + ASSERT_FALSE(ac.toCommon().clean); + ASSERT_FALSE(ac.toCommon().econo); + ASSERT_FALSE(ac.toCommon().light); + ASSERT_FALSE(ac.toCommon().filter); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_EQ(-1, ac.toCommon().sleep); + ASSERT_EQ(-1, ac.toCommon().clock); +} + +// https://github.com/crankyoldgit/IRremoteESP8266/issues/731 +TEST(TestDecodeDaikin160, RealExample) { + IRsendTest irsend(0); + IRrecv irrecv(0); + uint16_t rawData[327] = { + 5024, 2144, 342, 1786, 344, 706, 342, 706, 344, 706, 342, 1786, 342, 706, + 342, 708, 342, 708, 342, 708, 342, 1786, 342, 708, 342, 1786, 342, 1788, + 342, 708, 342, 1786, 344, 1786, 342, 1786, 342, 1786, 342, 1786, 342, 708, + 342, 708, 340, 1786, 344, 706, 342, 708, 342, 706, 342, 708, 342, 708, + 342, 706, 342, 1786, 342, 1788, 342, 1786, 342, 1786, 342, 1788, 342, 706, + 342, 1788, 342, 1786, 342, 706, 342, 706, 342, 708, 342, 708, 342, 708, + 342, 708, 342, 706, 342, 708, 342, 708, 342, 706, 342, 706, 342, 708, 342, + 1786, 342, 1786, 342, 1786, 342, 1788, 342, 706, 342, 706, 342, 706, 342, + 708, 342, 29442, 5022, 2146, 342, 1786, 342, 706, 342, 708, 342, 708, 342, + 1788, 342, 706, 342, 706, 342, 706, 342, 706, 342, 1788, 342, 708, 342, + 1786, 342, 1786, 342, 708, 342, 1786, 342, 1788, 342, 1786, 342, 1786, + 342, 1786, 342, 706, 342, 706, 342, 1786, 344, 706, 342, 706, 344, 706, + 342, 706, 342, 706, 342, 708, 342, 706, 342, 706, 342, 706, 342, 706, 342, + 1786, 342, 1786, 342, 706, 342, 706, 342, 1788, 342, 708, 342, 1786, 342, + 1786, 342, 706, 342, 706, 342, 706, 342, 708, 342, 1786, 342, 1786, 342, + 708, 342, 708, 342, 1786, 342, 706, 342, 706, 344, 706, 342, 1786, 342, + 708, 342, 706, 342, 706, 342, 708, 342, 706, 342, 708, 342, 706, 342, 708, + 342, 706, 342, 704, 344, 706, 342, 706, 344, 706, 342, 706, 342, 708, 342, + 706, 342, 706, 344, 706, 342, 706, 342, 706, 344, 1786, 342, 1786, 342, + 1786, 342, 1786, 342, 706, 342, 706, 344, 706, 342, 706, 342, 1786, 344, + 706, 342, 1786, 342, 706, 342, 708, 342, 706, 342, 706, 344, 706, 342, + 706, 342, 706, 342, 1786, 342, 706, 344, 706, 342, 706, 342, 708, 342, + 706, 342, 1788, 342, 1786, 342, 706, 344, 1786, 344, 706, 344, 1786, 342, + 708, 342}; // UNKNOWN 99CC993 + + uint8_t expectedState[kDaikin160StateLength] = { + // 7 bytes + 0x11, 0xDA, 0x27, 0xF0, 0x0D, 0x00, 0x0F, + // 13 bytes + 0x11, 0xDA, 0x27, 0x00, 0xD3, 0x30, 0x11, + 0x00, 0x00, 0x1E, 0x0A, 0x08, 0x56}; + + irsend.begin(); + irsend.reset(); + irsend.sendRaw(rawData, 327, 38000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(DAIKIN160, irsend.capture.decode_type); + ASSERT_EQ(kDaikin160Bits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + IRDaikin160 ac(0); + ac.setRaw(irsend.capture.state); + EXPECT_EQ("Power: Off, Mode: 3 (COOL), Temp: 25C, Fan: 10 (Auto), " + "Vent Position (V): 1 (Lowest)", ac.toString()); +} + +TEST(TestDecodeDaikin160, SyntheticExample) { + IRsendTest irsend(0); + IRrecv irrecv(0); + + uint8_t expectedState[kDaikin160StateLength] = { + // 7 bytes + 0x11, 0xDA, 0x27, 0xF0, 0x0D, 0x00, 0x0F, + // 13 bytes + 0x11, 0xDA, 0x27, 0x00, 0xD3, 0x30, 0x11, + 0x00, 0x00, 0x1E, 0x0A, 0x08, 0x56}; + + irsend.begin(); + irsend.reset(); + irsend.sendDaikin160(expectedState); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(DAIKIN160, irsend.capture.decode_type); + ASSERT_EQ(kDaikin160Bits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); +} + +TEST(TestDaikin160Class, toCommon) { + IRDaikin160 ac(0); + ac.setPower(true); + ac.setMode(kDaikinCool); + ac.setTemp(20); + ac.setFan(kDaikinFanMax); + ac.setSwingVertical(kDaikin160SwingVAuto); + // Now test it. + ASSERT_EQ(decode_type_t::DAIKIN160, ac.toCommon().protocol); + ASSERT_EQ(-1, ac.toCommon().model); + ASSERT_TRUE(ac.toCommon().power); + ASSERT_TRUE(ac.toCommon().celsius); + ASSERT_EQ(20, ac.toCommon().degrees); + ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode); + ASSERT_EQ(stdAc::fanspeed_t::kMax, ac.toCommon().fanspeed); + ASSERT_EQ(stdAc::swingv_t::kAuto, ac.toCommon().swingv); + // Unsupported. + ASSERT_EQ(stdAc::swingh_t::kOff, ac.toCommon().swingh); + ASSERT_FALSE(ac.toCommon().turbo); + ASSERT_FALSE(ac.toCommon().quiet); + ASSERT_FALSE(ac.toCommon().clean); + ASSERT_FALSE(ac.toCommon().econo); + ASSERT_FALSE(ac.toCommon().light); + ASSERT_FALSE(ac.toCommon().filter); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_EQ(-1, ac.toCommon().sleep); + ASSERT_EQ(-1, ac.toCommon().clock); +} + +TEST(TestDaikin160Class, FanSpeed) { + IRDaikin160 ac(0); + ac.begin(); + + // Unexpected value should default to Auto. + ac.setFan(0); + EXPECT_EQ(kDaikinFanAuto, ac.getFan()); + + // Unexpected value should default to Auto. + ac.setFan(255); + EXPECT_EQ(kDaikinFanAuto, ac.getFan()); + + ac.setFan(kDaikinFanMax); + EXPECT_EQ(kDaikinFanMax, ac.getFan()); + + // Beyond Max should default to Auto. + ac.setFan(kDaikinFanMax + 1); + EXPECT_EQ(kDaikinFanAuto, ac.getFan()); + + ac.setFan(kDaikinFanMax - 1); + EXPECT_EQ(kDaikinFanMax - 1, ac.getFan()); + + ac.setFan(kDaikinFanMin); + EXPECT_EQ(kDaikinFanMin, ac.getFan()); + + ac.setFan(kDaikinFanMin + 1); + EXPECT_EQ(kDaikinFanMin + 1, ac.getFan()); + + // Beyond Min should default to Auto. + ac.setFan(kDaikinFanMin - 1); + EXPECT_EQ(kDaikinFanAuto, ac.getFan()); + + ac.setFan(3); + EXPECT_EQ(3, ac.getFan()); + + ac.setFan(kDaikinFanAuto); + EXPECT_EQ(kDaikinFanAuto, ac.getFan()); + + ac.setFan(kDaikinFanQuiet); + EXPECT_EQ(kDaikinFanQuiet, ac.getFan()); +} + +TEST(TestDaikin160Class, VaneSwing) { + IRDaikin160 ac(0); + ac.begin(); + + ac.setSwingVertical(kDaikin160SwingVAuto); + EXPECT_EQ(kDaikin160SwingVAuto, ac.getSwingVertical()); + + ac.setSwingVertical(kDaikin160SwingVHigh); + EXPECT_EQ(kDaikin160SwingVHigh, ac.getSwingVertical()); + + ac.setSwingVertical(255); + EXPECT_EQ(kDaikin160SwingVAuto, ac.getSwingVertical()); + + EXPECT_EQ(kDaikin160SwingVHighest, + IRDaikin160::convertSwingV(stdAc::swingv_t::kHighest)); + EXPECT_EQ(kDaikin160SwingVLowest, + IRDaikin160::convertSwingV(stdAc::swingv_t::kLowest)); + EXPECT_EQ(kDaikin160SwingVMiddle, + IRDaikin160::convertSwingV(stdAc::swingv_t::kMiddle)); +} + +TEST(TestDaikin160Class, Power) { + IRDaikin160 ac(0); + ac.begin(); + + ac.on(); + EXPECT_TRUE(ac.getPower()); + + ac.off(); + EXPECT_FALSE(ac.getPower()); + + ac.setPower(true); + EXPECT_TRUE(ac.getPower()); + + ac.setPower(false); + EXPECT_FALSE(ac.getPower()); +} + +TEST(TestDaikin160Class, Temperature) { + IRDaikin160 ac(0); + ac.begin(); + + ac.setTemp(0); + EXPECT_EQ(kDaikinMinTemp, ac.getTemp()); + + ac.setTemp(255); + EXPECT_EQ(kDaikinMaxTemp, ac.getTemp()); + + ac.setTemp(kDaikinMinTemp); + EXPECT_EQ(kDaikinMinTemp, ac.getTemp()); + + ac.setTemp(kDaikinMaxTemp); + EXPECT_EQ(kDaikinMaxTemp, ac.getTemp()); + + ac.setTemp(kDaikinMinTemp - 1); + EXPECT_EQ(kDaikinMinTemp, ac.getTemp()); + + ac.setTemp(kDaikinMaxTemp + 1); + EXPECT_EQ(kDaikinMaxTemp, ac.getTemp()); + + ac.setTemp(kDaikinMinTemp + 1); + EXPECT_EQ(kDaikinMinTemp + 1, ac.getTemp()); + + ac.setTemp(21); + EXPECT_EQ(21, ac.getTemp()); + + ac.setTemp(25); + EXPECT_EQ(25, ac.getTemp()); + + ac.setTemp(29); + EXPECT_EQ(29, ac.getTemp()); +} + +TEST(TestDaikin160Class, OperatingMode) { + IRDaikin160 ac(0); + ac.begin(); + + ac.setMode(kDaikinAuto); + EXPECT_EQ(kDaikinAuto, ac.getMode()); + + ac.setMode(kDaikinCool); + EXPECT_EQ(kDaikinCool, ac.getMode()); + + ac.setMode(kDaikinHeat); + EXPECT_EQ(kDaikinHeat, ac.getMode()); + + ac.setMode(kDaikinDry); + EXPECT_EQ(kDaikinDry, ac.getMode()); + + ac.setMode(kDaikinFan); + EXPECT_EQ(kDaikinFan, ac.getMode()); + + ac.setMode(kDaikinFan + 1); + EXPECT_EQ(kDaikinAuto, ac.getMode()); + + ac.setMode(kDaikinAuto + 1); + EXPECT_EQ(kDaikinAuto, ac.getMode()); + + ac.setMode(255); + EXPECT_EQ(kDaikinAuto, ac.getMode()); +} + +TEST(TestDaikin160Class, HumanReadable) { + IRDaikin160 ac(0); + + EXPECT_EQ( + "Power: Off, Mode: 3 (COOL), Temp: 25C, Fan: 10 (Auto), " + "Vent Position (V): 1 (Lowest)", + ac.toString()); + ac.setMode(kDaikinAuto); + ac.setTemp(19); + ac.setFan(kDaikinFanMin); + ac.setSwingVertical(kDaikin160SwingVAuto); + ac.setPower(true); + EXPECT_EQ( + "Power: On, Mode: 0 (AUTO), Temp: 19C, Fan: 1 (Low), " + "Vent Position (V): 15 (Auto)", + ac.toString()); +} + +TEST(TestDaikin176Class, FanControl) { + IRDaikin176 ac(0); + + EXPECT_EQ( + "Power: Off, Mode: 7 (COOL), Temp: 9C, Fan: 1 (Low), Swing (H): 6 (Off)", + ac.toString()); + ac.setFan(kDaikinFanMin); + ac.setPower(true); + EXPECT_EQ( + "Power: On, Mode: 7 (COOL), Temp: 9C, Fan: 1 (Low), Swing (H): 6 (Off)", + ac.toString()); + ac.setFan(kDaikinFanMin + 1); + EXPECT_EQ( + "Power: On, Mode: 7 (COOL), Temp: 9C, Fan: 3 (High), Swing (H): 6 (Off)", + ac.toString()); + ac.setFan(kDaikin176FanMax); + EXPECT_EQ( + "Power: On, Mode: 7 (COOL), Temp: 9C, Fan: 3 (High), Swing (H): 6 (Off)", + ac.toString()); + + // Real state from remote + // https://github.com/crankyoldgit/IRremoteESP8266/pull/826#issuecomment-513168270 + uint8_t state[kDaikin176StateLength] = { + 0x11, 0xDA, 0x17, 0x18, 0x04, 0x00, 0x1E, + 0x11, 0xDA, 0x17, 0x18, 0x00, 0x73, 0x00, 0x21, 0x00, 0x00, 0x22, 0x35, + 0x00, 0x20, 0x25}; + ac.setRaw(state); + EXPECT_EQ( + "Power: On, Mode: 7 (COOL), Temp: 26C, Fan: 3 (High), " + "Swing (H): 5 (Auto)", + ac.toString()); +} + +TEST(TestDaikin176Class, convertFan) { + EXPECT_EQ(kDaikinFanMin, IRDaikin176::convertFan(stdAc::fanspeed_t::kMin)); + EXPECT_EQ(kDaikinFanMin, IRDaikin176::convertFan(stdAc::fanspeed_t::kLow)); + EXPECT_EQ(kDaikin176FanMax, + IRDaikin176::convertFan(stdAc::fanspeed_t::kMedium)); + EXPECT_EQ(kDaikin176FanMax, + IRDaikin176::convertFan(stdAc::fanspeed_t::kHigh)); + EXPECT_EQ(kDaikin176FanMax, IRDaikin176::convertFan(stdAc::fanspeed_t::kMax)); + EXPECT_EQ(kDaikin176FanMax, + IRDaikin176::convertFan(stdAc::fanspeed_t::kAuto)); +} + +TEST(TestDaikin176Class, SimulateIRacDaikin176) { + IRDaikin176 ac(0); + + ac.setPower(true); + ac.setMode(ac.convertMode(stdAc::opmode_t::kCool)); + ac.setTemp(26); + ac.setFan(ac.convertFan(stdAc::fanspeed_t::kMax)); + ac.setSwingHorizontal(kDaikin176SwingHOff); + EXPECT_EQ( + "Power: On, Mode: 7 (COOL), Temp: 26C, Fan: 3 (High), Swing (H): 6 (Off)", + ac.toString()); + ac.setSwingHorizontal(ac.convertSwingH(stdAc::swingh_t::kAuto)); + EXPECT_EQ( + "Power: On, Mode: 7 (COOL), Temp: 26C, Fan: 3 (High), " + "Swing (H): 5 (Auto)", + ac.toString()); +} + +TEST(TestDaikin176Class, OperatingMode) { + IRDaikin176 ac(0); + ac.begin(); + + ac.setMode(kDaikinAuto); + EXPECT_EQ(kDaikin176Cool, ac.getMode()); + + ac.setMode(kDaikin176Cool); + EXPECT_EQ(kDaikin176Cool, ac.getMode()); + + ac.setMode(kDaikinDry); + EXPECT_EQ(kDaikinDry, ac.getMode()); + + ac.setMode(kDaikinHeat); + EXPECT_EQ(kDaikin176Cool, ac.getMode()); + + ac.setMode(kDaikinFan); + EXPECT_EQ(kDaikinFan, ac.getMode()); + + ac.setMode(kDaikin176Cool + 1); + EXPECT_EQ(kDaikin176Cool, ac.getMode()); + + ac.setMode(kDaikinAuto + 1); + EXPECT_EQ(kDaikin176Cool, ac.getMode()); + + ac.setMode(255); + EXPECT_EQ(kDaikin176Cool, ac.getMode()); +} + +TEST(TestDaikin176Class, Temperature) { + IRDaikin176 ac(0); + ac.begin(); + ac.setMode(kDaikinAuto); + ac.setTemp(0); + EXPECT_EQ(kDaikinMinTemp, ac.getTemp()); + + ac.setTemp(255); + EXPECT_EQ(kDaikinMaxTemp, ac.getTemp()); + + ac.setTemp(kDaikinMinTemp); + EXPECT_EQ(kDaikinMinTemp, ac.getTemp()); + + ac.setTemp(kDaikinMaxTemp); + EXPECT_EQ(kDaikinMaxTemp, ac.getTemp()); + + ac.setTemp(kDaikinMinTemp - 1); + EXPECT_EQ(kDaikinMinTemp, ac.getTemp()); + + ac.setTemp(kDaikinMaxTemp + 1); + EXPECT_EQ(kDaikinMaxTemp, ac.getTemp()); + + ac.setTemp(kDaikinMinTemp + 1); + EXPECT_EQ(kDaikinMinTemp + 1, ac.getTemp()); + + ac.setTemp(21); + EXPECT_EQ(21, ac.getTemp()); + + ac.setTemp(25); + EXPECT_EQ(25, ac.getTemp()); + + ac.setTemp(29); + EXPECT_EQ(29, ac.getTemp()); + + // Temp should be locked to kDaikin176DryFanTemp when in Dry or Fan Mode. + ac.setMode(kDaikinFan); + EXPECT_EQ(kDaikin176DryFanTemp, ac.getTemp()); + ac.setMode(kDaikin176Cool); + EXPECT_EQ(29, ac.getTemp()); + ac.setMode(kDaikinDry); + EXPECT_EQ(kDaikinDry, ac.getMode()); + EXPECT_EQ(kDaikin176DryFanTemp, ac.getTemp()); + ac.setMode(kDaikin176Cool); + EXPECT_EQ(29, ac.getTemp()); + ac.setMode(kDaikinFan); + ac.setTemp(25); + EXPECT_EQ(kDaikin176DryFanTemp, ac.getTemp()); + ac.setMode(kDaikinHeat); + EXPECT_EQ(25, ac.getTemp()); +} + +TEST(TestDaikin176Class, Power) { + IRDaikin176 ac(0); + ac.begin(); + + ac.on(); + EXPECT_TRUE(ac.getPower()); + + ac.off(); + EXPECT_FALSE(ac.getPower()); + + ac.setPower(true); + EXPECT_TRUE(ac.getPower()); + + ac.setPower(false); + EXPECT_FALSE(ac.getPower()); +} + +TEST(TestDaikin176Class, VaneSwing) { + IRDaikin176 ac(0); + ac.begin(); + + ac.setSwingHorizontal(kDaikin176SwingHAuto); + EXPECT_EQ(kDaikin176SwingHAuto, ac.getSwingHorizontal()); + + ac.setSwingHorizontal(0); + EXPECT_EQ(kDaikin176SwingHAuto, ac.getSwingHorizontal()); + + ac.setSwingHorizontal(kDaikin176SwingHOff); + EXPECT_EQ(kDaikin176SwingHOff, ac.getSwingHorizontal()); + + ac.setSwingHorizontal(255); + EXPECT_EQ(kDaikin176SwingHAuto, ac.getSwingHorizontal()); + + EXPECT_EQ(kDaikin176SwingHAuto, + IRDaikin176::convertSwingH(stdAc::swingh_t::kAuto)); + EXPECT_EQ(kDaikin176SwingHOff, + IRDaikin176::convertSwingH(stdAc::swingh_t::kOff)); + EXPECT_EQ(kDaikin176SwingHAuto, + IRDaikin176::convertSwingH(stdAc::swingh_t::kLeft)); +} + +TEST(TestDaikin176Class, ReconstructKnownStates) { + IRDaikin176 ac(0); + ac.begin(); + // Data from: + // https://github.com/crankyoldgit/IRremoteESP8266/pull/826#issuecomment-513531138 + + // Power: On, Mode: 7 (COOL), Temp: 25C, Fan: 3 (MAX), Swing (H): 5 (Auto) + // 11DA171804001E11DA17180073002100002035002023 + uint8_t on_cool_25_max_auto[22] = { + 0x11, 0xDA, 0x17, 0x18, 0x04, 0x00, 0x1E, + 0x11, 0xDA, 0x17, 0x18, 0x00, 0x73, 0x00, 0x21, 0x00, 0x00, 0x20, 0x35, + 0x00, 0x20, 0x23}; + // Power: On, Mode: 6 (FAN), Temp: 17C, Fan: 3 (MAX), Swing (H): 5 (Auto) + // 11DA171804001E11DA171800630401000010350020E7 + uint8_t on_fan_17_max_auto[22] = { + 0x11, 0xDA, 0x17, 0x18, 0x04, 0x00, 0x1E, + 0x11, 0xDA, 0x17, 0x18, 0x00, 0x63, 0x04, 0x01, 0x00, 0x00, 0x10, 0x35, + 0x00, 0x20, 0xE7}; + // Power: On, Mode: 2 (DRY), Temp: 17C, Fan: 3 (MAX), Swing (H): 5 (Auto) + // 11DA171804001E11DA17180023047100001035002017 + uint8_t on_dry_17_max_auto[22] = { + 0x11, 0xDA, 0x17, 0x18, 0x04, 0x00, 0x1E, + 0x11, 0xDA, 0x17, 0x18, 0x00, 0x23, 0x04, 0x71, 0x00, 0x00, 0x10, 0x35, + 0x00, 0x20, 0x17}; + // Power: On, Mode: 7 (COOL), Temp: 25C, Fan: 3 (MAX), Swing (H): 5 (Auto) + // 11DA171804001E11DA17180073042100002035002027 + uint8_t on_cool_25_max_auto_v2[22] = { + 0x11, 0xDA, 0x17, 0x18, 0x04, 0x00, 0x1E, + 0x11, 0xDA, 0x17, 0x18, 0x00, 0x73, 0x04, 0x21, 0x00, 0x00, 0x20, 0x35, + 0x00, 0x20, 0x27}; + ac.setMode(kDaikin176Cool); + ac.setPower(true); + ac.setTemp(25); + ac.setFan(kDaikin176FanMax); + ac.setSwingHorizontal(true); + EXPECT_STATE_EQ(on_cool_25_max_auto, ac.getRaw(), kDaikin176Bits); + ac.setMode(kDaikinFan); + EXPECT_STATE_EQ(on_fan_17_max_auto, ac.getRaw(), kDaikin176Bits); + ac.setMode(kDaikinDry); + EXPECT_STATE_EQ(on_dry_17_max_auto, ac.getRaw(), kDaikin176Bits); + ac.setMode(kDaikin176Cool); + EXPECT_STATE_EQ(on_cool_25_max_auto_v2, ac.getRaw(), kDaikin176Bits); +} + +// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/827 +// Data from: +// https://docs.google.com/spreadsheets/d/1-YJnHyzy6bId5QmjTEZuw8_wSufESoIl-L_VEF-o8lM/edit?usp=sharing +TEST(TestDecodeDaikin128, RealExample) { + IRsendTest irsend(0); + IRrecv irrecv(0); + uint16_t rawData[265] = { + 9846, 9794, 9848, 9796, 4638, 2512, 348, 382, 352, 954, 352, 956, 352, + 382, 352, 956, 352, 384, 352, 382, 352, 386, 352, 382, 352, 954, 352, 384, + 352, 382, 352, 954, 352, 384, 352, 382, 352, 386, 352, 382, 352, 382, 354, + 382, 354, 382, 352, 382, 352, 954, 352, 382, 352, 384, 352, 954, 352, 382, + 352, 382, 352, 954, 352, 954, 354, 382, 352, 382, 352, 386, 352, 954, 354, + 954, 352, 954, 352, 384, 352, 382, 352, 382, 352, 954, 352, 384, 354, 382, + 352, 954, 352, 382, 352, 382, 352, 382, 352, 956, 352, 382, 354, 384, 354, + 382, 354, 954, 352, 954, 352, 382, 352, 382, 352, 954, 352, 382, 352, 384, + 354, 954, 352, 382, 352, 954, 352, 954, 352, 382, 352, 954, 352, 382, 352, + 956, 352, 20306, 376, 954, 352, 384, 352, 382, 352, 382, 354, 382, 352, + 954, 352, 382, 352, 958, 352, 384, 352, 382, 352, 382, 352, 382, 352, 382, + 354, 382, 352, 382, 352, 386, 352, 382, 352, 382, 354, 382, 352, 384, 352, + 382, 352, 382, 352, 382, 354, 384, 354, 382, 354, 382, 354, 382, 352, 382, + 352, 382, 352, 382, 352, 382, 352, 386, 354, 382, 352, 382, 352, 382, 352, + 382, 352, 382, 352, 382, 354, 382, 352, 384, 352, 382, 354, 382, 354, 382, + 354, 382, 352, 382, 354, 382, 354, 382, 354, 384, 354, 382, 354, 382, 352, + 382, 352, 382, 354, 382, 352, 382, 352, 382, 352, 386, 354, 952, 354, 954, + 352, 382, 352, 954, 354, 382, 352, 382, 354, 382, 354, 382, 4618 + }; // UNKNOWN DBA1F5E3 + uint8_t expectedState[kDaikin128StateLength] = { + // 8 bytes + 0x16, 0x12, 0x20, 0x19, 0x47, 0x22, 0x26, 0xAD, + // 8 bytes + 0xA1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B}; + + irsend.begin(); + irsend.reset(); + irsend.sendRaw(rawData, 265, 38000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(DAIKIN128, irsend.capture.decode_type); + ASSERT_EQ(kDaikin128Bits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + EXPECT_EQ( + "Power Toggle: On, Mode: 2 (COOL), Temp: 26C, Fan: 1 (Auto), " + "Powerful: Off, Quiet: Off, Swing (V): On, Sleep: Off, " + "Econo: Off, Clock: 19:20, " + "On Timer: Off, On Time: 07:30, Off Timer: Off, Off Time: 22:00, " + "Light Toggle: 0 (Off)", + IRAcUtils::resultAcToString(&irsend.capture)); +} + +// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/827 +// Data from: +// https://docs.google.com/spreadsheets/d/1-YJnHyzy6bId5QmjTEZuw8_wSufESoIl-L_VEF-o8lM/edit?usp=sharing +TEST(TestDecodeDaikin128, SyntheticSelfDecode) { + IRsendTest irsend(0); + IRrecv irrecv(0); + uint8_t expectedState[kDaikin128StateLength] = { + // 8 bytes + 0x16, 0x12, 0x20, 0x19, 0x47, 0x22, 0x26, 0xAD, + // 8 bytes + 0xA1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B}; + + irsend.begin(); + irsend.reset(); + irsend.sendDaikin128(expectedState); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(DAIKIN128, irsend.capture.decode_type); + ASSERT_EQ(kDaikin128Bits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); +} + +TEST(TestDaikin128Class, Checksums) { + IRDaikin128 ac(0); + + uint8_t knownGood[kDaikin128StateLength] = { + 0x16, 0x12, 0x20, 0x19, 0x47, 0x22, 0x26, 0xAD, + 0xA1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B}; + uint8_t knownBad[kDaikin128StateLength] = { + 0x16, 0x12, 0x20, 0x19, 0x47, 0x22, 0x26, 0x0D, + 0xA1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + EXPECT_EQ(0xA, ac.calcFirstChecksum(knownGood)); + EXPECT_EQ(0x0B, ac.calcSecondChecksum(knownGood)); + EXPECT_TRUE(ac.validChecksum(knownGood)); + ac.setRaw(knownBad); + EXPECT_STATE_EQ(knownGood, ac.getRaw(), kDaikin128Bits); +} + +TEST(TestDaikin128Class, PowerToggle) { + IRDaikin128 ac(0); + ac.begin(); + + ac.setPowerToggle(true); + EXPECT_TRUE(ac.getPowerToggle()); + ac.setPowerToggle(false); + EXPECT_FALSE(ac.getPowerToggle()); + ac.setPowerToggle(true); + EXPECT_TRUE(ac.getPowerToggle()); +} + +TEST(TestDaikin128Class, SwingVertical) { + IRDaikin128 ac(0); + ac.begin(); + + ac.setSwingVertical(true); + EXPECT_TRUE(ac.getSwingVertical()); + ac.setSwingVertical(false); + EXPECT_FALSE(ac.getSwingVertical()); + ac.setSwingVertical(true); + EXPECT_TRUE(ac.getSwingVertical()); +} + +TEST(TestDaikin128Class, Sleep) { + IRDaikin128 ac(0); + ac.begin(); + + ac.setSleep(true); + EXPECT_TRUE(ac.getSleep()); + ac.setSleep(false); + EXPECT_FALSE(ac.getSleep()); + ac.setSleep(true); + EXPECT_TRUE(ac.getSleep()); +} + +TEST(TestDaikin128Class, Econo) { + IRDaikin128 ac(0); + ac.begin(); + + // Econo works in some modes + ac.setMode(kDaikin128Heat); + ac.setEcono(true); + EXPECT_TRUE(ac.getEcono()); + ac.setEcono(false); + EXPECT_FALSE(ac.getEcono()); + ac.setEcono(true); + EXPECT_TRUE(ac.getEcono()); + // But not some some modes + ac.setMode(kDaikin128Auto); + EXPECT_FALSE(ac.getEcono()); + ac.setEcono(true); + EXPECT_FALSE(ac.getEcono()); +} + +TEST(TestDaikin128Class, FanSpeed) { + IRDaikin128 ac(0); + ac.begin(); + + ac.setMode(kDaikin128Cool); + // Unexpected value should default to Auto. + ac.setFan(0); + EXPECT_EQ(kDaikin128FanAuto, ac.getFan()); + ac.setFan(255); + EXPECT_EQ(kDaikin128FanAuto, ac.getFan()); + ac.setFan(5); + EXPECT_EQ(kDaikin128FanAuto, ac.getFan()); + + ac.setFan(kDaikin128FanHigh); + EXPECT_EQ(kDaikin128FanHigh, ac.getFan()); + + // Beyond Quiet should default to Auto. + ac.setFan(kDaikin128FanQuiet + 1); + EXPECT_EQ(kDaikin128FanAuto, ac.getFan()); + + ac.setFan(kDaikin128FanMed); + EXPECT_EQ(kDaikin128FanMed, ac.getFan()); + + ac.setFan(kDaikin128FanLow); + EXPECT_EQ(kDaikin128FanLow, ac.getFan()); + + ac.setFan(kDaikin128FanPowerful); + EXPECT_EQ(kDaikin128FanPowerful, ac.getFan()); + + ac.setFan(kDaikin128FanAuto); + EXPECT_EQ(kDaikin128FanAuto, ac.getFan()); + + ac.setFan(kDaikin128FanQuiet); + EXPECT_EQ(kDaikin128FanQuiet, ac.getFan()); +} + +TEST(TestDaikin128Class, OperatingMode) { + IRDaikin128 ac(0); + ac.begin(); + + ac.setMode(0); + EXPECT_EQ(kDaikin128Auto, ac.getMode()); + ac.setMode(kDaikin128Cool); + EXPECT_EQ(kDaikin128Cool, ac.getMode()); + ac.setMode(kDaikin128Auto); + EXPECT_EQ(kDaikin128Auto, ac.getMode()); + ac.setMode(kDaikin128Heat); + EXPECT_EQ(kDaikin128Heat, ac.getMode()); + ac.setMode(kDaikin128Dry); + EXPECT_EQ(kDaikin128Dry, ac.getMode()); + ac.setMode(kDaikin128Fan); + EXPECT_EQ(kDaikin128Fan, ac.getMode()); + ac.setMode(3); + EXPECT_EQ(kDaikin128Auto, ac.getMode()); + ac.setMode(kDaikin128Auto + 1); + EXPECT_EQ(kDaikin128Auto, ac.getMode()); + ac.setMode(255); + EXPECT_EQ(kDaikin128Auto, ac.getMode()); +} + +TEST(TestDaikin128Class, Quiet) { + IRDaikin128 ac(0); + ac.begin(); + + // Quiet works in some modes + ac.setMode(kDaikin128Cool); + ac.setQuiet(true); + EXPECT_TRUE(ac.getQuiet()); + ac.setQuiet(false); + EXPECT_FALSE(ac.getQuiet()); + ac.setQuiet(true); + EXPECT_TRUE(ac.getQuiet()); + // But not some some modes + ac.setMode(kDaikin128Auto); + EXPECT_FALSE(ac.getQuiet()); + ac.setQuiet(true); + EXPECT_FALSE(ac.getQuiet()); +} + +TEST(TestDaikin128Class, Powerful) { + IRDaikin128 ac(0); + ac.begin(); + + // Powerful works in some modes + ac.setMode(kDaikin128Cool); + ac.setPowerful(true); + EXPECT_TRUE(ac.getPowerful()); + ac.setPowerful(false); + EXPECT_FALSE(ac.getPowerful()); + ac.setPowerful(true); + EXPECT_TRUE(ac.getPowerful()); + // But not some some modes + ac.setMode(kDaikin128Auto); + EXPECT_FALSE(ac.getPowerful()); + ac.setPowerful(true); + EXPECT_FALSE(ac.getPowerful()); +} + +TEST(TestDaikin128Class, Temperature) { + IRDaikin128 ac(0); + ac.begin(); + + ac.setTemp(0); + EXPECT_EQ(kDaikin128MinTemp, ac.getTemp()); + + ac.setTemp(255); + EXPECT_EQ(kDaikin128MaxTemp, ac.getTemp()); + + ac.setTemp(kDaikin128MinTemp); + EXPECT_EQ(kDaikin128MinTemp, ac.getTemp()); + + ac.setTemp(kDaikin128MaxTemp); + EXPECT_EQ(kDaikin128MaxTemp, ac.getTemp()); + + ac.setTemp(kDaikin128MinTemp - 1); + EXPECT_EQ(kDaikin128MinTemp, ac.getTemp()); + + ac.setTemp(kDaikin128MaxTemp + 1); + EXPECT_EQ(kDaikin128MaxTemp, ac.getTemp()); + + ac.setTemp(kDaikin128MinTemp + 1); + EXPECT_EQ(kDaikin128MinTemp + 1, ac.getTemp()); + + ac.setTemp(21); + EXPECT_EQ(21, ac.getTemp()); + + ac.setTemp(25); + EXPECT_EQ(25, ac.getTemp()); + + ac.setTemp(29); + EXPECT_EQ(29, ac.getTemp()); +} + +// Test human readable output. +TEST(TestDaikin128Class, HumanReadable) { + IRDaikin128 ac(0); + + ac.setPowerToggle(false); + ac.setMode(kDaikin128Auto); + ac.setTemp(25); + ac.setFan(kDaikin128FanAuto); + ac.setQuiet(false); + ac.setPowerful(false); + ac.setSleep(false); + ac.setEcono(false); + ac.setSwingVertical(true); + EXPECT_EQ( + "Power Toggle: Off, Mode: 10 (AUTO), Temp: 25C, Fan: 1 (Auto), " + "Powerful: Off, Quiet: Off, Swing (V): On, " + "Sleep: Off, Econo: Off, Clock: 00:00, " + "On Timer: Off, On Time: 00:00, Off Timer: Off, Off Time: 00:00, " + "Light Toggle: 0 (Off)", + ac.toString()); + ac.setMode(kDaikin128Cool); + ac.setTemp(16); + ac.setQuiet(true); + ac.setSwingVertical(false); + ac.setPowerToggle(true); + ac.setSleep(true); + ac.setEcono(true); + ac.setClock(18 * 60 + 33); // 18:33 + ac.setOnTimer(10 * 60); // 10am + ac.setOnTimerEnabled(true); + ac.setOffTimer(21 * 60 + 30); // 9:30pm + ac.setOffTimerEnabled(true); + ac.setLightToggle(kDaikin128BitWall); + EXPECT_EQ( + "Power Toggle: On, Mode: 2 (COOL), Temp: 16C, Fan: 9 (Quiet), " + "Powerful: Off, Quiet: On, Swing (V): Off, " + "Sleep: On, Econo: On, Clock: 18:33, " + "On Timer: On, On Time: 10:00, Off Timer: On, Off Time: 21:30, " + "Light Toggle: 8 (Wall)", + ac.toString()); +} + +TEST(TestDaikin128Class, Clock) { + IRDaikin128 ac(0); + ac.begin(); + + ac.setClock(0); + EXPECT_EQ(0, ac.getClock()); + ac.setClock(23 * 60 + 59); + EXPECT_EQ(23 * 60 + 59, ac.getClock()); + ac.setClock(23 * 60 + 59 + 1); + EXPECT_EQ(0, ac.getClock()); + ac.setClock(24 * 60 + 99); + EXPECT_EQ(0, ac.getClock()); +} + +TEST(TestDaikin128Class, Timers) { + IRDaikin128 ac(0); + ac.begin(); + + ac.setOnTimerEnabled(false); + EXPECT_FALSE(ac.getOnTimerEnabled()); + ac.setOnTimerEnabled(true); + EXPECT_TRUE(ac.getOnTimerEnabled()); + ac.setOnTimer(13 * 60 + 30); + EXPECT_EQ("13:30", irutils::minsToString(ac.getOnTimer())); + ac.setOnTimer(13 * 60 + 31); + EXPECT_EQ("13:30", irutils::minsToString(ac.getOnTimer())); + ac.setOnTimer(13 * 60 + 29); + EXPECT_EQ("13:00", irutils::minsToString(ac.getOnTimer())); + EXPECT_TRUE(ac.getOnTimerEnabled()); + ac.setOnTimerEnabled(false); + EXPECT_FALSE(ac.getOnTimerEnabled()); + + ac.setOffTimerEnabled(false); + EXPECT_FALSE(ac.getOffTimerEnabled()); + ac.setOffTimerEnabled(true); + EXPECT_TRUE(ac.getOffTimerEnabled()); + ac.setOffTimer(1 * 60 + 30); + EXPECT_EQ("01:30", irutils::minsToString(ac.getOffTimer())); + ac.setOffTimer(23 * 60 + 31); + EXPECT_EQ("23:30", irutils::minsToString(ac.getOffTimer())); + ac.setOffTimer(24 * 60 + 29); + EXPECT_EQ("00:00", irutils::minsToString(ac.getOffTimer())); + EXPECT_TRUE(ac.getOffTimerEnabled()); + ac.setOffTimerEnabled(false); + EXPECT_FALSE(ac.getOffTimerEnabled()); +} + +TEST(TestDaikin128Class, ReconstructKnownState) { + IRDaikin128 ac(0); + + uint8_t expectedState[kDaikin128StateLength] = { + 0x16, 0x12, 0x20, 0x19, 0x47, 0x22, 0x26, 0xAD, + 0xA1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B}; + + ac.begin(); + ac.setPowerToggle(true); + ac.setMode(kDaikin128Cool); + ac.setTemp(26); + ac.setFan(kDaikin128FanAuto); + ac.setPowerful(false); + ac.setQuiet(false); + ac.setSwingVertical(true); + ac.setSleep(false); + ac.setEcono(false); + ac.setClock(19 * 60 + 20); + ac.setOnTimerEnabled(false); + ac.setOnTimer(7 * 60 + 30); + ac.setOffTimerEnabled(false); + ac.setOffTimer(22 * 60 + 0); + ac.setLightToggle(0); + + EXPECT_STATE_EQ(expectedState, ac.getRaw(), kDaikin128Bits); +} + +// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/873 +// Data from: +// https://github.com/crankyoldgit/IRremoteESP8266/issues/873#issue-485088080 +TEST(TestDecodeDaikin152, RealExample) { + IRsendTest irsend(0); + IRrecv irrecv(0); + uint16_t rawData[319] = { + 450, 420, 448, 446, 422, 444, 422, 446, 422, 446, 422, 25182, 3492, 1718, + 450, 1288, 448, 422, 446, 448, 420, 446, 422, 1290, 448, 422, 446, 446, + 422, 446, 424, 420, 448, 1290, 448, 446, 422, 1288, 448, 1288, 450, 420, + 448, 1288, 448, 1288, 450, 1288, 448, 1288, 448, 1290, 448, 446, 422, 446, + 422, 1288, 450, 446, 422, 420, 446, 446, 422, 422, 446, 446, 422, 420, + 448, 422, 446, 446, 422, 446, 422, 446, 422, 420, 446, 446, 422, 446, 422, + 422, 446, 446, 422, 422, 446, 446, 422, 446, 422, 446, 422, 446, 422, 446, + 424, 444, 424, 446, 420, 446, 422, 446, 422, 424, 444, 444, 422, 424, 444, + 1288, 450, 444, 422, 1288, 450, 1288, 450, 444, 422, 422, 446, 446, 422, + 446, 422, 446, 422, 446, 422, 422, 446, 420, 448, 444, 422, 446, 422, 446, + 422, 420, 448, 446, 422, 446, 422, 446, 422, 422, 446, 1286, 450, 422, + 448, 446, 422, 446, 422, 422, 446, 420, 446, 422, 446, 446, 422, 422, 446, + 446, 422, 422, 446, 446, 424, 444, 422, 420, 448, 446, 422, 420, 446, 446, + 422, 446, 422, 420, 448, 444, 422, 422, 448, 444, 424, 420, 446, 446, 422, + 446, 422, 422, 446, 444, 422, 446, 422, 444, 422, 446, 422, 420, 448, 446, + 422, 420, 448, 446, 422, 446, 422, 446, 422, 446, 422, 446, 422, 444, 422, + 1288, 450, 420, 448, 446, 420, 446, 422, 446, 422, 446, 424, 420, 448, + 444, 422, 422, 446, 446, 424, 420, 448, 1312, 424, 420, 448, 1288, 448, + 446, 422, 446, 424, 420, 446, 1288, 450, 1288, 450, 444, 422, 446, 422, + 422, 448, 444, 422, 420, 448, 446, 422, 1288, 448, 446, 422, 446, 422, + 444, 424, 444, 422, 446, 422, 446, 422, 420, 448, 446, 422, 420, 446, + 1290, 448, 1288, 448, 420, 446, 1288, 448, 420, 446, 1288, 450, 444, 424, + 1286, 450}; // UNKNOWN 2B9504D3 + uint8_t expectedState[kDaikin152StateLength] = { + 0x11, 0xDA, 0x27, 0x00, 0x00, 0x00, 0x34, 0x00, 0x40, 0x00, + 0x00, 0x00, 0x00, 0x20, 0x00, 0xC5, 0x40, 0x00, 0xAB}; + + irsend.begin(); + irsend.reset(); + irsend.sendRaw(rawData, 319, 38000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(DAIKIN152, irsend.capture.decode_type); + ASSERT_EQ(kDaikin152Bits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); +} + +// https://github.com/crankyoldgit/IRremoteESP8266/issues/873 +TEST(TestDecodeDaikin152, SyntheticExample) { + IRsendTest irsend(0); + IRrecv irrecv(0); + uint8_t expectedState[kDaikin152StateLength] = { + 0x11, 0xDA, 0x27, 0x00, 0x00, 0x00, 0x34, 0x00, 0x40, 0x00, + 0x00, 0x00, 0x00, 0x20, 0x00, 0xC5, 0x40, 0x00, 0xAB}; + + irsend.begin(); + irsend.reset(); + irsend.sendDaikin152(expectedState); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(DAIKIN152, irsend.capture.decode_type); + ASSERT_EQ(kDaikin152Bits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); +} diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Denon_test.cpp b/lib/IRremoteESP8266-2.6.5/test/ir_Denon_test.cpp similarity index 91% rename from lib/IRremoteESP8266-2.6.0/test/ir_Denon_test.cpp rename to lib/IRremoteESP8266-2.6.5/test/ir_Denon_test.cpp index 6b80eae02..bfb554bb5 100644 --- a/lib/IRremoteESP8266-2.6.0/test/ir_Denon_test.cpp +++ b/lib/IRremoteESP8266-2.6.5/test/ir_Denon_test.cpp @@ -25,7 +25,7 @@ TEST(TestSendDenon, SendDataOnly) { irsend.reset(); // Denon Eco Mode On. (Panasonic/Kaseikyo) - irsend.sendDenon(0x2A4C028D6CE3, DENON_48_BITS); + irsend.sendDenon(0x2A4C028D6CE3, kDenon48Bits); EXPECT_EQ( "f36700d50" "m3456s1728" @@ -45,7 +45,7 @@ TEST(TestSendDenon, SendNormalWithRepeats) { irsend.begin(); irsend.reset(); - irsend.sendDenon(0x2278, DENON_BITS, 1); // 1 repeat. + irsend.sendDenon(0x2278, kDenonBits, 1); // 1 repeat. EXPECT_EQ( "f38000d33" "m260s780m260s1820m260s780m260s780m260s780m260s1820m260s780m260s780" @@ -61,7 +61,7 @@ TEST(TestSendDenon, SendNormalWithRepeats) { "m260s780m260s780m260s780m260s780m260s1820m260s1820m260s1820" "m260s43602", irsend.outputStr()); - irsend.sendDenon(0x2278, DENON_BITS, 2); // 2 repeats. + irsend.sendDenon(0x2278, kDenonBits, 2); // 2 repeats. EXPECT_EQ( "f38000d33" "m260s780m260s1820m260s780m260s780m260s780m260s1820m260s780m260s780" @@ -90,7 +90,7 @@ TEST(TestSendDenon, Send48BitWithRepeats) { irsend.begin(); irsend.reset(); - irsend.sendDenon(0x2A4C028D6CE3, DENON_48_BITS, 1); // 1 repeat. + irsend.sendDenon(0x2A4C028D6CE3, kDenon48Bits, 1); // 1 repeat. EXPECT_EQ( "f36700d50" "m3456s1728" @@ -110,7 +110,7 @@ TEST(TestSendDenon, Send48BitWithRepeats) { "m432s1296m432s1296m432s1296m432s432m432s432m432s432m432s1296m432s1296" "m432s98928", irsend.outputStr()); - irsend.sendDenon(0x2A4C028D6CE3, DENON_48_BITS, 2); // 2 repeats. + irsend.sendDenon(0x2A4C028D6CE3, kDenon48Bits, 2); // 2 repeats. EXPECT_EQ( "f36700d50" "m3456s1728" @@ -185,9 +185,9 @@ TEST(TestDecodeDenon, NormalDecodeWithStrict) { irsend.sendDenon(0x2278); irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decodeDenon(&irsend.capture, DENON_BITS, true)); + ASSERT_TRUE(irrecv.decodeDenon(&irsend.capture, kDenonBits, true)); EXPECT_EQ(DENON, irsend.capture.decode_type); - EXPECT_EQ(DENON_BITS, irsend.capture.bits); + EXPECT_EQ(kDenonBits, irsend.capture.bits); EXPECT_EQ(0x2278, irsend.capture.value); EXPECT_EQ(0x2, irsend.capture.address); EXPECT_EQ(0x79, irsend.capture.command); @@ -208,11 +208,11 @@ TEST(TestDecodeDenon, NormalDecodeWithStrict) { // Normal Denon 48-bit message. (Panasonic/Kaseikyo) irsend.reset(); - irsend.sendDenon(0x2A4C028D6CE3, DENON_48_BITS); + irsend.sendDenon(0x2A4C028D6CE3, kDenon48Bits); irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decodeDenon(&irsend.capture, DENON_48_BITS, true)); + ASSERT_TRUE(irrecv.decodeDenon(&irsend.capture, kDenon48Bits, true)); EXPECT_EQ(DENON, irsend.capture.decode_type); - EXPECT_EQ(DENON_48_BITS, irsend.capture.bits); + EXPECT_EQ(kDenon48Bits, irsend.capture.bits); EXPECT_EQ(0x2A4C028D6CE3, irsend.capture.value); EXPECT_EQ(0x2A4C, irsend.capture.address); EXPECT_EQ(0x028D6CE3, irsend.capture.command); @@ -235,9 +235,9 @@ TEST(TestDecodeDenon, DecodeGlobalCacheExample) { irsend.sendGC(gc_test_power, 67); irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decodeDenon(&irsend.capture, DENON_BITS, true)); + ASSERT_TRUE(irrecv.decodeDenon(&irsend.capture, kDenonBits, true)); EXPECT_EQ(DENON, irsend.capture.decode_type); - EXPECT_EQ(DENON_BITS, irsend.capture.bits); + EXPECT_EQ(kDenonBits, irsend.capture.bits); EXPECT_EQ(0x2278, irsend.capture.value); EXPECT_EQ(0x2, irsend.capture.address); EXPECT_EQ(0x79, irsend.capture.command); @@ -256,9 +256,9 @@ TEST(TestDecodeDenon, DecodeGlobalCacheExample) { irsend.sendGC(gc_test_eco, 103); irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decodeDenon(&irsend.capture, DENON_48_BITS, true)); + ASSERT_TRUE(irrecv.decodeDenon(&irsend.capture, kDenon48Bits, true)); EXPECT_EQ(DENON, irsend.capture.decode_type); - EXPECT_EQ(DENON_48_BITS, irsend.capture.bits); + EXPECT_EQ(kDenon48Bits, irsend.capture.bits); EXPECT_EQ(0x2A4C028D6CE3, irsend.capture.value); EXPECT_EQ(0x2A4C, irsend.capture.address); EXPECT_EQ(0x028D6CE3, irsend.capture.command); @@ -281,6 +281,6 @@ TEST(TestDecodeDenon, FailToDecodeNonDenonExample) { ASSERT_FALSE(irrecv.decodeDenon(&irsend.capture)); ASSERT_FALSE(irrecv.decodeDenon(&irsend.capture, kDenonLegacyBits, false)); - ASSERT_FALSE(irrecv.decodeDenon(&irsend.capture, DENON_BITS, false)); - ASSERT_FALSE(irrecv.decodeDenon(&irsend.capture, DENON_48_BITS, false)); + ASSERT_FALSE(irrecv.decodeDenon(&irsend.capture, kDenonBits, false)); + ASSERT_FALSE(irrecv.decodeDenon(&irsend.capture, kDenon48Bits, false)); } diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Dish_test.cpp b/lib/IRremoteESP8266-2.6.5/test/ir_Dish_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.6.0/test/ir_Dish_test.cpp rename to lib/IRremoteESP8266-2.6.5/test/ir_Dish_test.cpp diff --git a/lib/IRremoteESP8266-2.6.5/test/ir_Electra_test.cpp b/lib/IRremoteESP8266-2.6.5/test/ir_Electra_test.cpp new file mode 100644 index 000000000..581d40561 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.5/test/ir_Electra_test.cpp @@ -0,0 +1,250 @@ +// Copyright 2018, 2019 David Conran + +#include "ir_Electra.h" +#include +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "gtest/gtest.h" + +// Tests for sendElectraAC(). + +// Test sending typical data only. +TEST(TestSendElectraAC, SendDataOnly) { + IRsendTest irsend(0); + irsend.begin(); + uint8_t data[kElectraAcStateLength] = {0xC3, 0x87, 0xF6, 0x28, 0x60, + 0x00, 0x20, 0x00, 0x00, 0x20, + 0x00, 0x05, 0x0D}; + + irsend.sendElectraAC(data); + EXPECT_EQ( + "f38000d50" + "m9166s4470" + "m646s1647m646s1647m646s547m646s547m646s547m646s547m646s1647m646s1647" + "m646s1647m646s1647m646s1647m646s547m646s547m646s547m646s547m646s1647" + "m646s547m646s1647m646s1647m646s547m646s1647m646s1647m646s1647m646s1647" + "m646s547m646s547m646s547m646s1647m646s547m646s1647m646s547m646s547" + "m646s547m646s547m646s547m646s547m646s547m646s1647m646s1647m646s547" + "m646s547m646s547m646s547m646s547m646s547m646s547m646s547m646s547" + "m646s547m646s547m646s547m646s547m646s547m646s1647m646s547m646s547" + "m646s547m646s547m646s547m646s547m646s547m646s547m646s547m646s547" + "m646s547m646s547m646s547m646s547m646s547m646s547m646s547m646s547" + "m646s547m646s547m646s547m646s547m646s547m646s1647m646s547m646s547" + "m646s547m646s547m646s547m646s547m646s547m646s547m646s547m646s547" + "m646s1647m646s547m646s1647m646s547m646s547m646s547m646s547m646s547" + "m646s1647m646s547m646s1647m646s1647m646s547m646s547m646s547m646s547" + "m646s100000", + irsend.outputStr()); +} + +// Tests for decodeElectraAC(). +// Decode normal ElectraAC messages. + +TEST(TestDecodeElectraAC, SyntheticDecode) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + // Synthesised Normal ElectraAC message. + irsend.reset(); + uint8_t expectedState[kElectraAcStateLength] = {0xC3, 0x87, 0xF6, 0x28, 0x60, + 0x00, 0x20, 0x00, 0x00, 0x20, + 0x00, 0x05, 0x0D}; + irsend.sendElectraAC(expectedState); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(ELECTRA_AC, irsend.capture.decode_type); + EXPECT_EQ(kElectraAcBits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); +} + +// Decode a recorded example +TEST(TestDecodeElectraAC, RealExampleDecode) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + // Real ElectraAC message. + // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/527 + uint16_t rawData[211] = { + 9166, 4470, 642, 1632, 642, 1632, 668, 534, 666, 534, 668, 534, + 614, 536, 640, 1636, 640, 1646, 694, 1662, 612, 1628, 642, 1666, + 664, 532, 668, 534, 666, 534, 666, 532, 666, 1644, 642, 532, + 640, 1634, 668, 1632, 642, 538, 666, 1660, 610, 1666, 664, 1632, + 642, 1672, 610, 536, 666, 534, 694, 532, 666, 1636, 614, 538, + 666, 1632, 642, 536, 666, 544, 692, 534, 640, 558, 640, 534, + 640, 540, 666, 534, 638, 1666, 638, 1636, 640, 550, 666, 534, + 640, 540, 666, 534, 640, 540, 666, 536, 638, 540, 666, 536, + 638, 550, 664, 536, 638, 540, 664, 536, 638, 540, 666, 534, + 638, 1640, 664, 536, 692, 546, 664, 536, 664, 536, 664, 536, + 664, 546, 612, 532, 636, 538, 664, 536, 664, 546, 612, 538, + 638, 538, 638, 538, 664, 536, 690, 538, 662, 538, 664, 538, + 662, 548, 664, 536, 662, 538, 662, 562, 638, 564, 636, 564, + 636, 1668, 582, 556, 652, 572, 612, 568, 636, 564, 610, 570, + 636, 556, 616, 550, 656, 566, 610, 570, 632, 578, 608, 1640, + 662, 562, 642, 1686, 582, 570, 634, 566, 604, 576, 636, 566, + 610, 578, 634, 1664, 584, 590, 660, 1636, 610, 1642, 664, 590, + 610, 590, 636, 566, 634, 568, 686}; // UNKNOWN 9AD8CDB5 + uint8_t expectedState[kElectraAcStateLength] = {0xC3, 0x87, 0xF6, 0x28, 0x60, + 0x00, 0x20, 0x00, 0x00, 0x20, + 0x00, 0x05, 0x0D}; + + irsend.reset(); + irsend.sendRaw(rawData, 211, 38000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(ELECTRA_AC, irsend.capture.decode_type); + ASSERT_EQ(kElectraAcBits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + EXPECT_EQ( + "Power: On, Mode: 1 (COOL), Temp: 24C, Fan: 3 (Low), " + "Swing(V): Off, Swing(H): Off", + IRAcUtils::resultAcToString(&irsend.capture)); +} + +TEST(TestIRElectraAcClass, Power) { + IRElectraAc ac(0); + ac.begin(); + + ac.on(); + EXPECT_TRUE(ac.getPower()); + + ac.off(); + EXPECT_FALSE(ac.getPower()); + + ac.setPower(true); + EXPECT_TRUE(ac.getPower()); + + ac.setPower(false); + EXPECT_FALSE(ac.getPower()); +} + +TEST(TestIRElectraAcClass, OperatingMode) { + IRElectraAc ac(0); + ac.begin(); + + ac.setMode(kElectraAcAuto); + EXPECT_EQ(kElectraAcAuto, ac.getMode()); + + ac.setMode(kElectraAcCool); + EXPECT_EQ(kElectraAcCool, ac.getMode()); + + ac.setMode(kElectraAcHeat); + EXPECT_EQ(kElectraAcHeat, ac.getMode()); + + ac.setMode(kElectraAcDry); + EXPECT_EQ(kElectraAcDry, ac.getMode()); + + ac.setMode(kElectraAcFan); + EXPECT_EQ(kElectraAcFan, ac.getMode()); + + ac.setMode(kElectraAcHeat + 1); + EXPECT_EQ(kElectraAcAuto, ac.getMode()); + + ac.setMode(255); + EXPECT_EQ(kElectraAcAuto, ac.getMode()); +} + +TEST(TestIRElectraAcClass, SetAndGetTemp) { + IRElectraAc ac(0); + ac.setTemp(25); + EXPECT_EQ(25, ac.getTemp()); + ac.setTemp(kElectraAcMinTemp); + EXPECT_EQ(kElectraAcMinTemp, ac.getTemp()); + ac.setTemp(kElectraAcMinTemp - 1); + EXPECT_EQ(kElectraAcMinTemp, ac.getTemp()); + ac.setTemp(kElectraAcMaxTemp); + EXPECT_EQ(kElectraAcMaxTemp, ac.getTemp()); + ac.setTemp(kElectraAcMaxTemp + 1); + EXPECT_EQ(kElectraAcMaxTemp, ac.getTemp()); +} + +TEST(TestIRElectraAcClass, FanSpeed) { + IRElectraAc ac(0); + ac.begin(); + + ac.setFan(0); + EXPECT_EQ(kElectraAcFanAuto, ac.getFan()); + + ac.setFan(255); + EXPECT_EQ(kElectraAcFanAuto, ac.getFan()); + + ac.setFan(kElectraAcFanHigh); + EXPECT_EQ(kElectraAcFanHigh, ac.getFan()); + + ac.setFan(std::max(kElectraAcFanHigh, kElectraAcFanLow) + 1); + EXPECT_EQ(kElectraAcFanAuto, ac.getFan()); + + ac.setFan(kElectraAcFanHigh - 1); + EXPECT_EQ(kElectraAcFanAuto, ac.getFan()); + + ac.setFan(1); + EXPECT_EQ(1, ac.getFan()); + + ac.setFan(1); + EXPECT_EQ(1, ac.getFan()); + + ac.setFan(3); + EXPECT_EQ(3, ac.getFan()); +} + +TEST(TestIRElectraAcClass, toCommon) { + IRElectraAc ac(0); + ac.setPower(true); + ac.setMode(kElectraAcCool); + ac.setTemp(20); + ac.setFan(kElectraAcFanHigh); + ac.setSwingV(true); + ac.setSwingH(true); + // Now test it. + ASSERT_EQ(decode_type_t::ELECTRA_AC, ac.toCommon().protocol); + ASSERT_EQ(-1, ac.toCommon().model); + ASSERT_TRUE(ac.toCommon().power); + ASSERT_TRUE(ac.toCommon().celsius); + ASSERT_EQ(20, ac.toCommon().degrees); + + ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode); + ASSERT_EQ(stdAc::fanspeed_t::kMax, ac.toCommon().fanspeed); + ASSERT_EQ(stdAc::swingv_t::kAuto, ac.toCommon().swingv); + ASSERT_EQ(stdAc::swingh_t::kAuto, ac.toCommon().swingh); + // Unsupported. + ASSERT_FALSE(ac.toCommon().turbo); + ASSERT_FALSE(ac.toCommon().filter); + ASSERT_FALSE(ac.toCommon().light); + ASSERT_FALSE(ac.toCommon().quiet); + ASSERT_FALSE(ac.toCommon().econo); + ASSERT_FALSE(ac.toCommon().clean); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_EQ(-1, ac.toCommon().sleep); + ASSERT_EQ(-1, ac.toCommon().clock); +} + +TEST(TestIRElectraAcClass, HumanReadable) { + IRElectraAc ac(0); + // Data from: + // https://github.com/crankyoldgit/IRremoteESP8266/issues/778#issue-460052080 + uint8_t on_cool_32C_auto_voff[13] = { + 0xC3, 0xC7, 0xE0, 0x00, 0xA0, 0x00, 0x20, + 0x00, 0x00, 0x20, 0x00, 0x40, 0x8A}; + ac.setRaw(on_cool_32C_auto_voff); + EXPECT_EQ( + "Power: On, Mode: 1 (COOL), Temp: 32C, Fan: 5 (Auto), " + "Swing(V): Off, Swing(H): Off", ac.toString()); + uint8_t on_cool_16C_auto_voff[13] = { + 0xC3, 0x47, 0xE0, 0x00, 0xA0, 0x00, 0x20, + 0x00, 0x00, 0x20, 0x00, 0x41, 0x0B}; + ac.setRaw(on_cool_16C_auto_voff); + EXPECT_EQ( + "Power: On, Mode: 1 (COOL), Temp: 16C, Fan: 5 (Auto), " + "Swing(V): Off, Swing(H): Off", ac.toString()); + uint8_t on_cool_16C_low_voff[13] = { + 0xC3, 0x47, 0xE0, 0x00, 0x60, 0x00, 0x20, + 0x00, 0x00, 0x20, 0x00, 0x41, 0xCB}; + ac.setRaw(on_cool_16C_low_voff); + EXPECT_EQ( + "Power: On, Mode: 1 (COOL), Temp: 16C, Fan: 3 (Low), " + "Swing(V): Off, Swing(H): Off", ac.toString()); +} diff --git a/lib/IRremoteESP8266-2.6.5/test/ir_Fujitsu_test.cpp b/lib/IRremoteESP8266-2.6.5/test/ir_Fujitsu_test.cpp new file mode 100644 index 000000000..b6613906f --- /dev/null +++ b/lib/IRremoteESP8266-2.6.5/test/ir_Fujitsu_test.cpp @@ -0,0 +1,798 @@ +// Copyright 2017 Jonny Graham, David Conran + +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "ir_Fujitsu.h" +#include "gtest/gtest.h" + +// Tests for Fujitsu A/C methods. + +// Test sending typical data only. +TEST(TestIRFujitsuACClass, GetRawDefault) { + IRFujitsuAC ac(0); // AR-RAH2E + ac.setSwing(kFujitsuAcSwingBoth); + ac.setMode(kFujitsuAcModeCool); + ac.setFanSpeed(kFujitsuAcFanHigh); + ac.setTemp(24); + ac.setCmd(kFujitsuAcCmdTurnOn); + uint8_t expected_arrah2e[16] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, + 0x81, 0x01, 0x31, 0x00, 0x00, 0x00, 0x20, 0xFD}; + EXPECT_STATE_EQ(expected_arrah2e, ac.getRaw(), 16 * 8); + EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); + EXPECT_EQ("Model: 1 (ARRAH2E), Power: On, Mode: 1 (COOL), Temp: 24C, " + "Fan: 1 (High), Swing: Vert + Horiz, Command: N/A", + ac.toString()); + + uint8_t expected_ardb1[15] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFC, 0x08, 0x30, + 0x81, 0x01, 0x01, 0x00, 0x00, 0x00, 0x4D}; + ac.setModel(ARDB1); + EXPECT_STATE_EQ(expected_ardb1, ac.getRaw(), 15 * 8); + EXPECT_EQ(kFujitsuAcStateLength - 1, ac.getStateLength()); + EXPECT_EQ("Model: 2 (ARDB1), Power: On, Mode: 1 (COOL), Temp: 24C, " + "Fan: 1 (High), Command: N/A", + ac.toString()); +} + +TEST(TestIRFujitsuACClass, GetRawTurnOff) { + IRFujitsuAC ac(0); + ac.setModel(ARRAH2E); + ac.off(); + uint8_t expected_arrah2e[7] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x02, 0xFD}; + EXPECT_STATE_EQ(expected_arrah2e, ac.getRaw(), 7 * 8); + EXPECT_EQ(kFujitsuAcStateLengthShort, ac.getStateLength()); + EXPECT_EQ("Model: 1 (ARRAH2E), Power: Off, Mode: 1 (COOL), Temp: 24C, " + "Fan: 1 (High), Swing: Vert + Horiz, Command: N/A", + ac.toString()); + + ac.setModel(ARDB1); + uint8_t expected_ardb1[6] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x02}; + EXPECT_STATE_EQ(expected_ardb1, ac.getRaw(), 6 * 8); + EXPECT_EQ(kFujitsuAcStateLengthShort - 1, ac.getStateLength()); + EXPECT_EQ("Model: 2 (ARDB1), Power: Off, Mode: 1 (COOL), Temp: 24C, " + "Fan: 1 (High), Command: N/A", + ac.toString()); +} + +TEST(TestIRFujitsuACClass, GetRawStepHoriz) { + IRFujitsuAC ac(0); + ac.stepHoriz(); + uint8_t expected[7] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x79, 0x86}; + EXPECT_STATE_EQ(expected, ac.getRaw(), 7 * 8); + EXPECT_EQ(kFujitsuAcStateLengthShort, ac.getStateLength()); + EXPECT_EQ( + "Model: 1 (ARRAH2E), Power: On, Mode: 1 (COOL), Temp: 24C, " + "Fan: 1 (High), Swing: Vert + Horiz, Command: Step vane horizontally", + ac.toString()); +} + +TEST(TestIRFujitsuACClass, GetRawStepVert) { + IRFujitsuAC ac(0); + ac.setModel(ARRAH2E); + ac.stepVert(); + uint8_t expected_arrah2e[7] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x6C, 0x93}; + EXPECT_STATE_EQ(expected_arrah2e, ac.getRaw(), 7 * 8); + EXPECT_EQ(kFujitsuAcStateLengthShort, ac.getStateLength()); + EXPECT_EQ("Model: 1 (ARRAH2E), Power: On, Mode: 1 (COOL), Temp: 24C, " + "Fan: 1 (High), Swing: Vert + Horiz, Command: Step vane vertically", + ac.toString()); + + ac.setModel(ARDB1); + ac.stepVert(); + uint8_t expected_ardb1[6] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x6C}; + EXPECT_STATE_EQ(expected_ardb1, ac.getRaw(), 6 * 8); + EXPECT_EQ(kFujitsuAcStateLengthShort - 1, + ac.getStateLength()); + EXPECT_EQ("Model: 2 (ARDB1), Power: On, Mode: 1 (COOL), Temp: 24C, " + "Fan: 1 (High), Command: Step vane vertically", + ac.toString()); +} + +TEST(TestIRFujitsuACClass, GetRawWithSwingHoriz) { + IRFujitsuAC ac(0); + ac.setCmd(kFujitsuAcCmdStayOn); + ac.setSwing(kFujitsuAcSwingHoriz); + ac.setMode(kFujitsuAcModeCool); + ac.setFanSpeed(kFujitsuAcFanQuiet); + ac.setTemp(25); + uint8_t expected[16] = {0x14, 0x63, 0x0, 0x10, 0x10, 0xFE, 0x9, 0x30, + 0x90, 0x1, 0x24, 0x0, 0x0, 0x0, 0x20, 0xFB}; + EXPECT_STATE_EQ(expected, ac.getRaw(), 16 * 8); + EXPECT_EQ("Model: 1 (ARRAH2E), Power: On, Mode: 1 (COOL), Temp: 25C, " + "Fan: 4 (Quiet), Swing: Horiz, Command: N/A", + ac.toString()); +} + +TEST(TestIRFujitsuACClass, GetRawWithFan) { + IRFujitsuAC ac(0); + ac.setCmd(kFujitsuAcCmdStayOn); + ac.setSwing(kFujitsuAcSwingHoriz); + ac.setMode(kFujitsuAcModeFan); + ac.setFanSpeed(kFujitsuAcFanMed); + ac.setTemp(20); // temp doesn't matter for fan + // but it is sent by the RC anyway + ac.setModel(ARRAH2E); + uint8_t expected_arrah2e[16] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, + 0x40, 0x03, 0x22, 0x00, 0x00, 0x00, 0x20, 0x4B}; + EXPECT_STATE_EQ(expected_arrah2e, ac.getRaw(), 16 * 8); + EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); + EXPECT_EQ("Model: 1 (ARRAH2E), Power: On, Mode: 3 (FAN), Temp: 20C, " + "Fan: 2 (Medium), Swing: Horiz, Command: N/A", ac.toString()); + + ac.setModel(ARDB1); + uint8_t expected_ardb1[15] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFC, 0x08, 0x30, + 0x40, 0x03, 0x02, 0x00, 0x00, 0x00, 0x8B}; + EXPECT_EQ(kFujitsuAcStateLength - 1, ac.getStateLength()); + EXPECT_STATE_EQ(expected_ardb1, ac.getRaw(), ac.getStateLength() * 8); + EXPECT_EQ("Model: 2 (ARDB1), Power: On, Mode: 3 (FAN), Temp: 20C, " + "Fan: 2 (Medium), Command: N/A", ac.toString()); +} + +TEST(TestIRFujitsuACClass, SetRaw) { + IRFujitsuAC ac(0); + EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); + uint8_t expected_default_arrah2e[kFujitsuAcStateLength] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, + 0x81, 0x01, 0x31, 0x00, 0x00, 0x00, 0x20, 0xFD}; + EXPECT_STATE_EQ(expected_default_arrah2e, ac.getRaw(), + ac.getStateLength() * 8); + EXPECT_EQ("Model: 1 (ARRAH2E), Power: On, Mode: 1 (COOL), Temp: 24C, " + "Fan: 1 (High), Swing: Vert + Horiz, Command: N/A", + ac.toString()); + // Now set a new state via setRaw(); + // This state is a real state from an AR-DB1 remote. + uint8_t new_state1[kFujitsuAcStateLength - 1] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFC, 0x08, 0x30, + 0x30, 0x01, 0x00, 0x00, 0x00, 0x00, 0x9F}; + ac.setRaw(new_state1, kFujitsuAcStateLength - 1); + EXPECT_EQ(kFujitsuAcStateLength - 1, ac.getStateLength()); + EXPECT_STATE_EQ(new_state1, ac.getRaw(), ac.getStateLength() * 8); + EXPECT_EQ("Model: 2 (ARDB1), Power: On, Mode: 1 (COOL), Temp: 19C, " + "Fan: 0 (Auto), Command: N/A", ac.toString()); +} + +TEST(TestSendFujitsuAC, GenerateMessage) { + IRFujitsuAC ac(0); + IRsendTest irsend(0); + ac.begin(); + irsend.begin(); + + ac.setCmd(kFujitsuAcCmdStayOn); + ac.setSwing(kFujitsuAcSwingBoth); + ac.setMode(kFujitsuAcModeCool); + ac.setFanSpeed(kFujitsuAcFanHigh); + ac.setTemp(24); + + EXPECT_EQ(kFujitsuAcFanHigh, ac.getFanSpeed()); + EXPECT_EQ(kFujitsuAcModeCool, ac.getMode()); + EXPECT_EQ(24, ac.getTemp()); + EXPECT_EQ(kFujitsuAcSwingBoth, ac.getSwing()); + EXPECT_EQ(kFujitsuAcCmdStayOn, ac.getCmd()); + + irsend.reset(); + irsend.sendFujitsuAC(ac.getRaw(), kFujitsuAcStateLength); + EXPECT_EQ( + "f38000d50" + "m3324s1574" + "m448s390m448s390m448s1182m448s390m448s1182m448s390m448s390m448s390" + "m448s1182m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" + "m448s390m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182" + "m448s1182m448s390m448s390m448s1182m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s1182m448s1182m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s1182" + "m448s1182m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s1182m448s390m448s390" + "m448s390m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182" + "m448s8100", + irsend.outputStr()); +} + +TEST(TestSendFujitsuAC, GenerateShortMessage) { + IRFujitsuAC ac(0); + IRsendTest irsend(0); + ac.begin(); + irsend.begin(); + + ac.off(); + + EXPECT_EQ(kFujitsuAcCmdTurnOff, ac.getCmd()); + + irsend.reset(); + irsend.sendFujitsuAC(ac.getRaw(), kFujitsuAcStateLengthShort); + EXPECT_EQ( + "f38000d50" + "m3324s1574m448s390m448s390m448s1182m448s390m448s1182m448s390m448s390m448" + "s390m448s1182m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s1182m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s1182m448s390m448s390m448s390m448s390m448s1182m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s1182m448s390m448s1182m448" + "s1182m448s1182m448s1182m448s1182m448s1182m448s8100", + irsend.outputStr()); +} + +// Issue #275 +TEST(TestSendFujitsuAC, Issue275) { + IRFujitsuAC ac(0); + IRsendTest irsend(0); + ac.begin(); + irsend.begin(); + irsend.reset(); + + ac.setCmd(kFujitsuAcCmdTurnOff); + irsend.sendFujitsuAC(ac.getRaw(), kFujitsuAcStateLengthShort); + EXPECT_EQ( + "f38000d50" + // Header + "m3324s1574" + // 0 0 1 0 1 0 0 0 (0x28) + "m448s390m448s390m448s1182m448s390m448s1182m448s390m448s390m448s390" + // 1 1 0 0 0 1 1 0 (0xC6) + "m448s1182m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390" + // 0 0 0 0 0 0 0 0 (0x00) + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + // 0 0 0 0 1 0 0 0 (0x08) + "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" + // 0 0 0 0 1 0 0 0 (0x08) + "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" + // 0 1 0 0 0 0 0 0 (0x40) + "m448s390m448s1182m448s390m448s390m448s390m448s390m448s390m448s390" + // 1 0 1 1 1 1 1 1 (0xBF) + "m448s1182m448s390m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182" + // Footer + "m448s8100", irsend.outputStr()); + + irsend.reset(); + // Per report in Issue #275 + uint16_t off[115] = { + 3350, 1650, + 450, 400, 450, 450, 450, 1250, 450, 400, 450, 1250, 450, 400, 450, 400, + 450, 400, 450, 1250, 450, 1250, 450, 400, 450, 400, 450, 400, 450, 1250, + 450, 1250, 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, + 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, + 450, 1250, 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, + 450, 400, 450, 1250, 450, 400, 450, 400, 450, 400, 450, 400, 450, 1250, + 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, 450, 1250, + 450, 400, 450, 1250, 450, 1250, 450, 1250, 450, 1250, 450, 1250, + 450, 1250, 450}; + irsend.sendRaw(off, 115, 38); + EXPECT_EQ( + "f38000d50" + // Header + "m3350s1650" + // 0 0 1 0 1 0 0 0 (0x28) + "m450s400m450s450m450s1250m450s400m450s1250m450s400m450s400m450s400" + // 1 1 0 0 0 1 1 0 (0xC6) + "m450s1250m450s1250m450s400m450s400m450s400m450s1250m450s1250m450s400" + // 0 0 0 0 0 0 0 0 (0x00) + "m450s400m450s400m450s400m450s400m450s400m450s400m450s400m450s400" + // 0 0 0 0 1 0 0 0 (0x08) + "m450s400m450s400m450s400m450s400m450s1250m450s400m450s400m450s400" + // 0 0 0 0 1 0 0 0 (0x08) + "m450s400m450s400m450s400m450s400m450s1250m450s400m450s400m450s400" + // 0 1 0 0 0 0 0 0 (0x40) + "m450s400m450s1250m450s400m450s400m450s400m450s400m450s400m450s400" + // 1 0 1 1 1 1 1 1 (0xBF) + "m450s1250m450s400m450s1250m450s1250m450s1250m450s1250m450s1250m450s1250" + // Footer + "m450", + irsend.outputStr()); +} + +TEST(TestDecodeFujitsuAC, SyntheticShortMessages) { + IRsendTest irsend(0); + IRFujitsuAC ac(0); + IRrecv irrecv(0); + + irsend.begin(); + irsend.reset(); + + ac.setModel(ARRAH2E); + ac.setCmd(kFujitsuAcCmdTurnOff); + irsend.sendFujitsuAC(ac.getRaw(), ac.getStateLength()); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); + ASSERT_EQ(kFujitsuAcMinBits + 8, irsend.capture.bits); + uint8_t expected_arrah2e[7] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x02, 0xFD}; + EXPECT_STATE_EQ(expected_arrah2e, irsend.capture.state, irsend.capture.bits); + + irsend.reset(); + + ac.setModel(ARDB1); + ac.setCmd(kFujitsuAcCmdTurnOff); + irsend.sendFujitsuAC(ac.getRaw(), ac.getStateLength()); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); + ASSERT_EQ(kFujitsuAcMinBits, irsend.capture.bits); + uint8_t expected_ardb1[6] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x02}; + EXPECT_STATE_EQ(expected_ardb1, irsend.capture.state, irsend.capture.bits); +} + +TEST(TestDecodeFujitsuAC, SyntheticLongMessages) { + IRsendTest irsend(0); + IRFujitsuAC ac(0); + IRrecv irrecv(0); + irsend.begin(); + + irsend.reset(); + + ac.setModel(ARRAH2E); + ac.setCmd(kFujitsuAcCmdStayOn); + ac.setSwing(kFujitsuAcSwingVert); + ac.setMode(kFujitsuAcModeCool); + ac.setFanSpeed(kFujitsuAcFanQuiet); + ac.setTemp(18); + irsend.sendFujitsuAC(ac.getRaw(), ac.getStateLength()); + ASSERT_EQ(kFujitsuAcStateLength, ac.getStateLength()); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decodeFujitsuAC(&irsend.capture)); + ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); + ASSERT_EQ(kFujitsuAcBits, irsend.capture.bits); + uint8_t expected_arrah2e[kFujitsuAcStateLength] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, + 0x20, 0x01, 0x14, 0x00, 0x00, 0x00, 0x20, 0x7B}; + EXPECT_STATE_EQ(expected_arrah2e, irsend.capture.state, irsend.capture.bits); + ac.setRaw(irsend.capture.state, irsend.capture.bits / 8); + EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); + EXPECT_EQ("Model: 1 (ARRAH2E), Power: On, Mode: 1 (COOL), Temp: 18C, " + "Fan: 4 (Quiet), Swing: Vert, Command: N/A", ac.toString()); + + irsend.reset(); + + ac.setModel(ARDB1); + irsend.sendFujitsuAC(ac.getRaw(), ac.getStateLength()); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); + ASSERT_EQ(kFujitsuAcBits - 8, irsend.capture.bits); + uint8_t expected_ardb1[kFujitsuAcStateLength - 1] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFC, 0x08, 0x30, + 0x20, 0x01, 0x04, 0x00, 0x00, 0x00, 0xAB}; + EXPECT_STATE_EQ(expected_ardb1, irsend.capture.state, irsend.capture.bits); + ac.setRaw(irsend.capture.state, irsend.capture.bits / 8); + EXPECT_EQ(kFujitsuAcStateLength - 1, ac.getStateLength()); + EXPECT_EQ("Model: 2 (ARDB1), Power: On, Mode: 1 (COOL), Temp: 18C, " + "Fan: 4 (Quiet), Command: N/A", ac.toString()); +} + +TEST(TestDecodeFujitsuAC, RealShortARDB1OffExample) { + IRsendTest irsend(0); + IRrecv irrecv(0); + IRFujitsuAC ac(0); + + irsend.begin(); + + irsend.reset(); + // "Off" Message recorded from an AR-DB1 remote. + uint16_t rawData[99] = { + 3310, 1636, 440, 386, 440, 394, 442, 1210, 442, 390, 414, 1220, + 444, 390, 446, 380, 446, 380, 436, 1216, 438, 1214, 438, 388, + 438, 386, 438, 396, 410, 1222, 440, 1220, 442, 384, 442, 384, + 442, 384, 442, 382, 444, 382, 442, 382, 444, 380, 446, 380, + 446, 380, 444, 380, 436, 390, 436, 388, 436, 388, 438, 1214, + 438, 386, 438, 388, 438, 386, 440, 386, 440, 384, 442, 384, + 442, 384, 442, 1210, 444, 382, 444, 382, 444, 382, 444, 380, + 446, 1206, 436, 390, 436, 388, 436, 388, 438, 388, 438, 396, + 420, 388, 436}; + irsend.sendRaw(rawData, 99, 38000); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); + ASSERT_EQ(kFujitsuAcMinBits, irsend.capture.bits); + uint8_t expected[6] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x02}; + EXPECT_STATE_EQ(expected, irsend.capture.state, irsend.capture.bits); + ac.setRaw(irsend.capture.state, irsend.capture.bits / 8); + EXPECT_EQ(kFujitsuAcStateLengthShort - 1, ac.getStateLength()); + EXPECT_EQ("Model: 2 (ARDB1), Power: Off, Mode: 0 (AUTO), Temp: 16C, " + "Fan: 0 (Auto), Command: N/A", ac.toString()); +} + +TEST(TestDecodeFujitsuAC, RealLongARDB1Example) { + IRsendTest irsend(0); + IRrecv irrecv(0); + IRFujitsuAC ac(0); + + irsend.begin(); + irsend.reset(); + uint16_t rawData1[243] = { + 3316, 1632, 444, 390, 438, 388, 436, 1216, 438, 388, 438, 1214, + 438, 388, 438, 386, 440, 386, 440, 1212, 440, 1210, 442, 392, + 412, 396, 442, 392, 444, 1208, 444, 1208, 444, 380, 444, 380, + 446, 380, 436, 390, 436, 390, 436, 390, 436, 388, 438, 388, + 438, 388, 438, 388, 438, 386, 438, 386, 440, 384, 440, 1210, + 442, 384, 442, 382, 442, 384, 442, 384, 442, 382, 442, 382, + 444, 382, 444, 1208, 444, 382, 444, 380, 446, 380, 436, 390, + 436, 390, 436, 1214, 438, 1214, 438, 1212, 440, 1212, 440, 1220, + 412, 1222, 440, 394, 442, 382, 442, 382, 444, 1208, 444, 382, + 444, 380, 446, 380, 446, 380, 434, 390, 436, 388, 438, 388, + 438, 388, 438, 1214, 438, 1212, 440, 386, 440, 394, 412, 1222, + 440, 394, 442, 384, 442, 384, 442, 382, 442, 1208, 444, 390, + 414, 394, 442, 1216, 446, 380, 436, 390, 436, 390, 436, 388, + 436, 390, 436, 388, 438, 386, 440, 386, 440, 386, 438, 1212, + 440, 386, 440, 384, 440, 384, 442, 392, 412, 396, 440, 394, + 442, 382, 444, 382, 444, 382, 444, 380, 444, 380, 444, 382, + 444, 380, 446, 380, 436, 388, 436, 390, 436, 388, 438, 388, + 438, 388, 438, 388, 438, 386, 440, 386, 440, 386, 442, 384, + 440, 386, 442, 384, 440, 384, 442, 384, 442, 382, 442, 382, + 444, 1208, 444, 382, 444, 1208, 444, 380, 446, 1206, 436, 390, + 436, 1216, 436}; + irsend.sendRaw(rawData1, 243, 38000); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); + ASSERT_EQ(kFujitsuAcBits - 8, irsend.capture.bits); + uint8_t expected1[kFujitsuAcStateLength - 1] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFC, 0x08, 0x30, + 0x21, 0x01, 0x04, 0x00, 0x00, 0x00, 0xAA}; + EXPECT_STATE_EQ(expected1, irsend.capture.state, irsend.capture.bits); + ac.setRaw(irsend.capture.state, irsend.capture.bits / 8); + EXPECT_EQ(kFujitsuAcStateLength - 1, ac.getStateLength()); + EXPECT_EQ("Model: 2 (ARDB1), Power: On, Mode: 1 (COOL), Temp: 18C, " + "Fan: 4 (Quiet), Command: N/A", ac.toString()); + + irsend.reset(); + uint16_t rawData2[243] = { + 3316, 1630, 436, 398, 438, 386, 438, 1212, 440, 384, 440, 1212, + 442, 384, 442, 392, 414, 394, 442, 1218, 446, 1206, 436, 390, + 436, 388, 438, 388, 438, 1214, 440, 1212, 440, 384, 442, 384, + 442, 384, 442, 382, 444, 382, 444, 382, 444, 380, 446, 380, + 444, 380, 436, 390, 436, 388, 438, 396, 418, 388, 438, 1232, + 410, 396, 440, 394, 442, 384, 442, 384, 442, 382, 442, 392, + 414, 392, 444, 1216, 446, 380, 436, 390, 436, 396, 418, 390, + 436, 398, 438, 1214, 440, 1212, 440, 1210, 442, 1208, 444, 1216, + 416, 1218, 444, 388, 436, 390, 436, 388, 438, 1214, 440, 386, + 438, 386, 440, 386, 440, 384, 442, 384, 442, 384, 442, 382, + 444, 382, 444, 1206, 446, 1206, 436, 390, 436, 388, 438, 388, + 438, 386, 440, 394, 410, 396, 440, 1220, 442, 1210, 442, 392, + 414, 394, 442, 1218, 446, 406, 410, 388, 436, 390, 436, 390, + 436, 388, 438, 386, 440, 386, 440, 386, 440, 386, 440, 384, + 442, 384, 442, 384, 442, 382, 444, 382, 444, 380, 446, 380, + 446, 380, 436, 390, 436, 390, 436, 388, 438, 386, 438, 388, + 438, 386, 440, 386, 440, 384, 442, 384, 442, 384, 442, 384, + 442, 382, 444, 382, 444, 380, 446, 380, 446, 380, 436, 390, + 436, 388, 436, 388, 438, 386, 438, 386, 440, 386, 440, 1212, + 440, 1210, 442, 1210, 442, 1208, 444, 1208, 436, 390, 436, 388, + 436, 1214, 440}; + irsend.sendRaw(rawData2, 243, 38000); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); + ASSERT_EQ(kFujitsuAcBits - 8, irsend.capture.bits); + uint8_t expected2[kFujitsuAcStateLength - 1] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFC, 0x08, 0x30, + 0x30, 0x01, 0x00, 0x00, 0x00, 0x00, 0x9F}; + EXPECT_STATE_EQ(expected2, irsend.capture.state, irsend.capture.bits); + ac.setRaw(irsend.capture.state, irsend.capture.bits / 8); + EXPECT_EQ(kFujitsuAcStateLength - 1, ac.getStateLength()); + EXPECT_EQ("Model: 2 (ARDB1), Power: On, Mode: 1 (COOL), Temp: 19C, " + "Fan: 0 (Auto), Command: N/A", ac.toString()); +} + +TEST(TestDecodeFujitsuAC, Issue414) { + IRsendTest irsend(0); + IRrecv irrecv(0); + IRFujitsuAC ac(0); + + // Capture as supplied by arpmota + uint16_t rawData[259] = {3352, 1574, 480, 350, 480, 346, 480, 1190, 458, 346, + 508, 1140, 480, 346, 506, 346, 458, 346, 480, 1168, 480, 1192, 452, 374, + 458, 346, 480, 346, 508, 1168, 480, 1140, 480, 346, 506, 346, 458, 346, + 480, 346, 480, 346, 480, 346, 484, 372, 454, 374, 456, 346, 508, 318, + 480, 374, 458, 374, 480, 318, 480, 1196, 452, 346, 480, 346, 484, 342, + 484, 346, 480, 374, 458, 346, 506, 318, 508, 1170, 452, 346, 480, 374, + 458, 346, 506, 318, 480, 1196, 452, 1190, 458, 1162, 480, 1196, 452, + 1170, 480, 1190, 458, 1164, 480, 1196, 480, 318, 508, 346, 456, 1192, + 480, 346, 456, 374, 452, 346, 480, 374, 458, 342, 484, 346, 508, 346, + 456, 342, 512, 1164, 458, 1164, 508, 346, 456, 346, 480, 1190, 456, 342, + 484, 346, 506, 346, 456, 374, 452, 346, 508, 346, 458, 1164, 508, 346, + 458, 374, 452, 1168, 480, 374, 480, 318, 480, 374, 456, 346, 508, 318, + 480, 346, 484, 374, 480, 318, 484, 342, 484, 374, 480, 318, 484, 342, + 484, 346, 508, 318, 508, 346, 458, 346, 506, 318, 480, 374, 458, 346, + 506, 318, 480, 346, 484, 374, 480, 318, 482, 372, 456, 346, 508, 318, + 506, 348, 456, 342, 484, 346, 508, 318, 484, 374, 480, 318, 508, 318, + 484, 346, 508, 318, 480, 374, 456, 346, 508, 346, 480, 318, 480, 346, + 484, 374, 480, 320, 484, 1164, 508, 346, 458, 342, 512, 1164, 458, 1190, + 454, 346, 484, 1164, 508, 346, 458, 1164, 480, 350, 480, 374, 480}; + uint8_t state[16] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, 0x81, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x20, 0x2B}; + irsend.begin(); + irsend.reset(); + irsend.sendRaw(rawData, 259, 38000); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); + ASSERT_EQ(kFujitsuAcBits, irsend.capture.bits); + EXPECT_STATE_EQ(state, irsend.capture.state, irsend.capture.bits); + ac.setRaw(irsend.capture.state, irsend.capture.bits / 8); + EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); + EXPECT_EQ("Model: 1 (ARRAH2E), Power: On, Mode: 4 (HEAT), Temp: 24C, " + "Fan: 0 (Auto), Swing: Off, Command: N/A", ac.toString()); + + // Resend it using the state this time. + irsend.reset(); + irsend.sendFujitsuAC(state, 16); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); + ASSERT_EQ(kFujitsuAcBits, irsend.capture.bits); + EXPECT_STATE_EQ(state, irsend.capture.state, irsend.capture.bits); + EXPECT_EQ( + "f38000d50" + "m3324s1574" + "m448s390m448s390m448s1182m448s390m448s1182m448s390m448s390m448s390" + "m448s1182m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" + "m448s390m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182" + "m448s1182m448s390m448s390m448s1182m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s1182m448s1182m448s390m448s390" + "m448s1182m448s390m448s390m448s390m448s390m448s390m448s390m448s1182" + "m448s390m448s390m448s1182m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s1182m448s390m448s390" + "m448s1182m448s1182m448s390m448s1182m448s390m448s1182m448s390m448s390" + "m448s8100", irsend.outputStr()); +} + +TEST(TestIRFujitsuACClass, toCommon) { + IRFujitsuAC ac(0); + ac.setMode(kFujitsuAcModeCool); + ac.setTemp(20); + ac.setFanSpeed(kFujitsuAcFanQuiet); + ac.setSwing(kFujitsuAcSwingBoth); + + // Now test it. + ASSERT_EQ(decode_type_t::FUJITSU_AC, ac.toCommon().protocol); + ASSERT_EQ(fujitsu_ac_remote_model_t::ARRAH2E, ac.toCommon().model); + ASSERT_TRUE(ac.toCommon().power); + ASSERT_TRUE(ac.toCommon().celsius); + ASSERT_EQ(20, ac.toCommon().degrees); + ASSERT_TRUE(ac.toCommon().quiet); + + ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode); + ASSERT_EQ(stdAc::fanspeed_t::kMin, ac.toCommon().fanspeed); + ASSERT_EQ(stdAc::swingv_t::kAuto, ac.toCommon().swingv); + ASSERT_EQ(stdAc::swingh_t::kAuto, ac.toCommon().swingh); + // Unsupported. + ASSERT_FALSE(ac.toCommon().filter); + ASSERT_FALSE(ac.toCommon().clean); + ASSERT_FALSE(ac.toCommon().turbo); + ASSERT_FALSE(ac.toCommon().light); + ASSERT_FALSE(ac.toCommon().econo); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_EQ(-1, ac.toCommon().sleep); + ASSERT_EQ(-1, ac.toCommon().clock); + + // Check off mode which is special. + ac.off(); + ASSERT_FALSE(ac.toCommon().power); + ac.send(); + ac.stateReset(); + IRrecv irrecv(0); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&ac._irsend.capture)); + ASSERT_EQ(FUJITSU_AC, ac._irsend.capture.decode_type); + ac.setRaw(ac._irsend.capture.state, ac._irsend.capture.bits / 8); + + // Now test it. + EXPECT_EQ( // Off mode technically has no temp, mode, fan, etc. + "Model: 1 (ARRAH2E), Power: Off, Mode: 0 (AUTO), Temp: 16C, " + "Fan: 0 (Auto), Swing: Off, Command: N/A", ac.toString()); + ASSERT_EQ(decode_type_t::FUJITSU_AC, ac.toCommon().protocol); + ASSERT_EQ(fujitsu_ac_remote_model_t::ARRAH2E, ac.toCommon().model); + ASSERT_FALSE(ac.toCommon().power); + ASSERT_TRUE(ac.toCommon().celsius); + ASSERT_EQ(16, ac.toCommon().degrees); + ASSERT_FALSE(ac.toCommon().quiet); + + ASSERT_EQ(stdAc::opmode_t::kAuto, ac.toCommon().mode); + ASSERT_EQ(stdAc::fanspeed_t::kAuto, ac.toCommon().fanspeed); + ASSERT_EQ(stdAc::swingv_t::kOff, ac.toCommon().swingv); + ASSERT_EQ(stdAc::swingh_t::kOff, ac.toCommon().swingh); + // Unsupported. + ASSERT_FALSE(ac.toCommon().filter); + ASSERT_FALSE(ac.toCommon().clean); + ASSERT_FALSE(ac.toCommon().turbo); + ASSERT_FALSE(ac.toCommon().light); + ASSERT_FALSE(ac.toCommon().econo); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_EQ(-1, ac.toCommon().sleep); + ASSERT_EQ(-1, ac.toCommon().clock); +} + +TEST(TestDecodeFujitsuAC, Issue716) { + IRsendTest irsend(0); + IRrecv irrecv(0); + IRFujitsuAC ac(0); + + // Powerful command from a raw data capture. + // Capture as supplied by u4mzu4 + uint16_t rawData[115] = { + 3320, 1610, 432, 406, 432, 406, 432, 1220, 432, 406, 432, 1192, 458, 406, + 432, 406, 432, 406, 432, 1218, 432, 1220, 432, 406, 432, 406, 432, 406, + 432, 1192, 458, 1192, 460, 406, 432, 406, 432, 406, 432, 406, 432, 406, + 432, 406, 432, 406, 432, 408, 432, 406, 432, 406, 430, 406, 432, 406, 432, + 406, 432, 1190, 460, 406, 432, 408, 430, 406, 432, 406, 432, 406, 432, + 406, 432, 406, 434, 1192, 458, 406, 432, 406, 432, 406, 432, 1194, 458, + 406, 432, 406, 432, 1194, 456, 1196, 454, 1220, 432, 406, 432, 406, 432, + 408, 430, 1194, 458, 1194, 456, 406, 432, 406, 430, 406, 432, 1194, 458, + 1194, 458}; // FUJITSU_AC + uint8_t powerful[kFujitsuAcStateLengthShort] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0x39, 0xC6}; + irsend.begin(); + irsend.reset(); + irsend.sendRaw(rawData, 115, 38000); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); + ASSERT_EQ(kFujitsuAcStateLengthShort * 8, irsend.capture.bits); + EXPECT_STATE_EQ(powerful, irsend.capture.state, irsend.capture.bits); + ac.setRaw(irsend.capture.state, irsend.capture.bits / 8); + EXPECT_EQ(fujitsu_ac_remote_model_t::ARREB1E, ac.getModel()); + EXPECT_EQ(kFujitsuAcStateLengthShort, ac.getStateLength()); + EXPECT_EQ("Model: 3 (ARREB1E), Power: On, Mode: 0 (AUTO), Temp: 16C, " + "Fan: 0 (Auto), Swing: Off, Command: Powerful, Outside Quiet: Off", + ac.toString()); + + // Economy (just from the state) + uint8_t econo[kFujitsuAcStateLengthShort] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0x09, 0xF6}; + // Make sure we can't accidentally inherit the correct model. + ASSERT_NE(fujitsu_ac_remote_model_t::ARDB1, + fujitsu_ac_remote_model_t::ARREB1E); + ac.setModel(fujitsu_ac_remote_model_t::ARDB1); + ac.setRaw(econo, kFujitsuAcStateLengthShort); + EXPECT_EQ(fujitsu_ac_remote_model_t::ARREB1E, ac.getModel()); + EXPECT_EQ(kFujitsuAcStateLengthShort, ac.getStateLength()); + EXPECT_EQ("Model: 3 (ARREB1E), Power: On, Mode: 0 (AUTO), Temp: 16C, " + "Fan: 0 (Auto), Swing: Off, Command: Economy, Outside Quiet: Off", + ac.toString()); +} + +TEST(TestIRFujitsuACClass, OutsideQuiet) { + IRsendTest irsend(0); + IRrecv irrecv(0); + IRFujitsuAC ac(0); + + ASSERT_NE(fujitsu_ac_remote_model_t::ARDB1, + fujitsu_ac_remote_model_t::ARREB1E); + ASSERT_NE(fujitsu_ac_remote_model_t::ARRAH2E, + fujitsu_ac_remote_model_t::ARREB1E); + // States as supplied by u4mzu4 + // https://github.com/crankyoldgit/IRremoteESP8266/issues/716#issuecomment-495852309 + uint8_t off[kFujitsuAcStateLength] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, + 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x20, 0x2F}; + uint8_t on[kFujitsuAcStateLength] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, + 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0xA0, 0xAF}; + // Make sure we can't accidentally inherit the correct model. + ac.setModel(fujitsu_ac_remote_model_t::ARDB1); + ac.setRaw(off, kFujitsuAcStateLength); + EXPECT_EQ(fujitsu_ac_remote_model_t::ARRAH2E, ac.getModel()); + EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); + EXPECT_FALSE(ac.getOutsideQuiet()); + // We can really only tell the difference between ARRAH2E & ARREB1E if + // the option is set. Otheriwse they appear the same. + EXPECT_EQ( + "Model: 1 (ARRAH2E), Power: On, Mode: 1 (COOL), Temp: 24C, " + "Fan: 0 (Auto), Swing: Off, Command: N/A", ac.toString()); + ac.setModel(fujitsu_ac_remote_model_t::ARREB1E); + EXPECT_EQ( + "Model: 3 (ARREB1E), Power: On, Mode: 1 (COOL), Temp: 24C, " + "Fan: 0 (Auto), Swing: Off, Command: N/A, Outside Quiet: Off", + ac.toString()); + + // Make sure we can't accidentally inherit the correct model. + ac.setModel(fujitsu_ac_remote_model_t::ARDB1); + ac.setRaw(on, kFujitsuAcStateLength); + EXPECT_EQ(fujitsu_ac_remote_model_t::ARREB1E, ac.getModel()); + EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); + EXPECT_TRUE(ac.getOutsideQuiet()); + EXPECT_EQ( + "Model: 3 (ARREB1E), Power: On, Mode: 1 (COOL), Temp: 24C, " + "Fan: 0 (Auto), Swing: Off, Command: N/A, Outside Quiet: On", + ac.toString()); + + ac.setOutsideQuiet(false); + EXPECT_FALSE(ac.getOutsideQuiet()); + ac.setOutsideQuiet(true); + EXPECT_TRUE(ac.getOutsideQuiet()); + ac.setOutsideQuiet(false); + EXPECT_FALSE(ac.getOutsideQuiet()); +} + +TEST(TestIRFujitsuACClass, toggleSwing) { + IRsendTest irsend(0); + IRrecv irrecv(0); + IRFujitsuAC ac(0); + + ac.begin(); + ac.setModel(ARJW2); + ac.setSwing(kFujitsuAcSwingOff); + ac.setCmd(kFujitsuAcCmdStayOn); + ASSERT_EQ(kFujitsuAcSwingOff, ac.getSwing()); + ac.toggleSwingHoriz(); + EXPECT_EQ(kFujitsuAcCmdToggleSwingHoriz, ac.getCmd()); + EXPECT_EQ(kFujitsuAcSwingHoriz, ac.getSwing()); + ac.toggleSwingHoriz(); + EXPECT_EQ(kFujitsuAcCmdToggleSwingHoriz, ac.getCmd()); + EXPECT_EQ(kFujitsuAcSwingOff, ac.getSwing()); + ac.toggleSwingVert(); + EXPECT_EQ(kFujitsuAcCmdToggleSwingVert, ac.getCmd()); + EXPECT_EQ(kFujitsuAcSwingVert, ac.getSwing()); + ac.toggleSwingVert(); + EXPECT_EQ(kFujitsuAcCmdToggleSwingVert, ac.getCmd()); + EXPECT_EQ(kFujitsuAcSwingOff, ac.getSwing()); + + // Both + ac.toggleSwingHoriz(); + EXPECT_EQ(kFujitsuAcCmdToggleSwingHoriz, ac.getCmd()); + ac.toggleSwingVert(); + EXPECT_EQ(kFujitsuAcCmdToggleSwingVert, ac.getCmd()); + EXPECT_EQ(kFujitsuAcSwingBoth, ac.getSwing()); + ac.toggleSwingHoriz(); + EXPECT_EQ(kFujitsuAcCmdToggleSwingHoriz, ac.getCmd()); + EXPECT_EQ(kFujitsuAcSwingVert, ac.getSwing()); + ac.toggleSwingHoriz(); + EXPECT_EQ(kFujitsuAcSwingBoth, ac.getSwing()); + + EXPECT_EQ( + "Model: 4 (ARJW2), Power: On, Mode: 1 (COOL), Temp: 24C, Fan: 1 (High), " + "Command: Toggle horizontal swing", + ac.toString()); + + // Test without the update set. + ac.toggleSwingHoriz(false); + EXPECT_EQ(kFujitsuAcSwingBoth, ac.getSwing()); + EXPECT_EQ(kFujitsuAcCmdToggleSwingHoriz, ac.getCmd()); + ac.toggleSwingVert(false); + EXPECT_EQ(kFujitsuAcSwingBoth, ac.getSwing()); + EXPECT_EQ(kFujitsuAcCmdToggleSwingVert, ac.getCmd()); +} + +TEST(TestDecodeFujitsuAC, Issue726) { + IRsendTest irsend(0); + IRrecv irrecv(0); + IRFujitsuAC ac(0); + + // fan:auto mode:auto temp:24 power:on + // Capture as supplied by huexpub + // Rawdata was very messy. Had to use `./auto_analyse_raw_data.py -r 250` to + // get it to parse due to timings being above tolerances. + uint8_t auto_auto_on_24[kFujitsuAcStateLength] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, + 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x2F}; + irsend.begin(); + irsend.reset(); + irsend.sendFujitsuAC(auto_auto_on_24, 16); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); + ASSERT_EQ(kFujitsuAcStateLength * 8, irsend.capture.bits); + EXPECT_STATE_EQ(auto_auto_on_24, irsend.capture.state, irsend.capture.bits); + ac.setRaw(irsend.capture.state, irsend.capture.bits / 8); + EXPECT_EQ(fujitsu_ac_remote_model_t::ARRAH2E, ac.getModel()); + EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); + EXPECT_EQ("Model: 1 (ARRAH2E), Power: On, Mode: 0 (AUTO), Temp: 24C, " + "Fan: 0 (Auto), Swing: Off, Command: N/A", + ac.toString()); +} diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_GICable_test.cpp b/lib/IRremoteESP8266-2.6.5/test/ir_GICable_test.cpp similarity index 96% rename from lib/IRremoteESP8266-2.6.0/test/ir_GICable_test.cpp rename to lib/IRremoteESP8266-2.6.5/test/ir_GICable_test.cpp index bad9bbded..234a748b5 100644 --- a/lib/IRremoteESP8266-2.6.0/test/ir_GICable_test.cpp +++ b/lib/IRremoteESP8266-2.6.5/test/ir_GICable_test.cpp @@ -102,7 +102,7 @@ TEST(TestDecodeGICable, RealExampleDecodeOK) { irsend.begin(); // Real GICable "OK/Select" message. - // Ref: https://github.com/markszabo/IRremoteESP8266/issues/447 + // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/447 uint16_t rawData[39] = {9064, 4408, 580, 4408, 580, 2152, 578, 2150, 580, 2150, 580, 4408, 580, 2150, 580, 2150, 580, 2150, 580, 2150, 580, 2150, 580, 2150, @@ -125,7 +125,7 @@ TEST(TestDecodeGICable, RealExampleDecodeLEFT) { irsend.begin(); // Real GICable "LEFT" message. - // Ref: https://github.com/markszabo/IRremoteESP8266/issues/447 + // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/447 uint16_t rawData[39] = {9040, 4434, 554, 2176, 580, 4408, 554, 4434, 582, 2148, 554, 4434, 580, 4408, 556, 2174, 580, 2150, 580, 2150, 582, 2148, 556, 2176, @@ -149,7 +149,7 @@ TEST(TestDecodeGICable, RealExampleDecodeZEROKey) { // Real GICable "Zero Key" message. // Note: Zero key looks similar to a JVC message, hence this test. - // Ref: https://github.com/markszabo/IRremoteESP8266/issues/447 + // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/447 uint16_t rawData[39] = {9036, 4434, 552, 2178, 552, 2178, 552, 2180, 550, 2178, 552, 2178, 550, 2180, 552, 2178, 552, 2178, 550, 2180, 552, 2178, 526, 2204, diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_GlobalCache_test.cpp b/lib/IRremoteESP8266-2.6.5/test/ir_GlobalCache_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.6.0/test/ir_GlobalCache_test.cpp rename to lib/IRremoteESP8266-2.6.5/test/ir_GlobalCache_test.cpp diff --git a/lib/IRremoteESP8266-2.6.5/test/ir_Goodweather_test.cpp b/lib/IRremoteESP8266-2.6.5/test/ir_Goodweather_test.cpp new file mode 100644 index 000000000..58f42f0fe --- /dev/null +++ b/lib/IRremoteESP8266-2.6.5/test/ir_Goodweather_test.cpp @@ -0,0 +1,511 @@ +// Copyright 2019 David Conran + +#include "ir_Goodweather.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "gtest/gtest.h" + +TEST(TestIRUtils, Goodweather) { + ASSERT_EQ("GOODWEATHER", typeToString(decode_type_t::GOODWEATHER)); + ASSERT_EQ(decode_type_t::GOODWEATHER, strToDecodeType("GOODWEATHER")); + ASSERT_FALSE(hasACState(decode_type_t::GOODWEATHER)); +} + +// Tests for sendGoodweather(). + +// Test sending typical data only. +TEST(TestSendGoodweather, SendDataOnly) { + IRsendTest irsend(0); + irsend.begin(); + + irsend.reset(); + irsend.sendGoodweather(0x0); + EXPECT_EQ( + "f38000d50" + "m6800s6800" + "m640s1600m640s1600m640s1600m640s1600m640s1600m640s1600m640s1600m640s1600" + "m640s580m640s580m640s580m640s580m640s580m640s580m640s580m640s580" + "m640s1600m640s1600m640s1600m640s1600m640s1600m640s1600m640s1600m640s1600" + "m640s580m640s580m640s580m640s580m640s580m640s580m640s580m640s580" + "m640s1600m640s1600m640s1600m640s1600m640s1600m640s1600m640s1600m640s1600" + "m640s580m640s580m640s580m640s580m640s580m640s580m640s580m640s580" + "m640s1600m640s1600m640s1600m640s1600m640s1600m640s1600m640s1600m640s1600" + "m640s580m640s580m640s580m640s580m640s580m640s580m640s580m640s580" + "m640s1600m640s1600m640s1600m640s1600m640s1600m640s1600m640s1600m640s1600" + "m640s580m640s580m640s580m640s580m640s580m640s580m640s580m640s580" + "m640s1600m640s1600m640s1600m640s1600m640s1600m640s1600m640s1600m640s1600" + "m640s580m640s580m640s580m640s580m640s580m640s580m640s580m640s580" + "m640s6800m640s100000", + irsend.outputStr()); + + irsend.reset(); +} + +// Tests for decodeGoodweather(). + +// Decode normal Goodweather messages. +TEST(TestDecodeGoodweather, SyntheticDecode) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + // Normal (made-up value) Goodweather 48-bit message. + irsend.reset(); + irsend.sendGoodweather(0x1234567890AB); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(decode_type_t::GOODWEATHER, irsend.capture.decode_type); + EXPECT_EQ(kGoodweatherBits, irsend.capture.bits); + EXPECT_EQ(0x1234567890AB, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); + EXPECT_FALSE(irsend.capture.repeat); + // Normal (Real) Goodweather 48-bit message. + irsend.reset(); + irsend.sendGoodweather(0xD5276A030000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(decode_type_t::GOODWEATHER, irsend.capture.decode_type); + EXPECT_EQ(kGoodweatherBits, irsend.capture.bits); + EXPECT_EQ(0xD5276A030000, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); + EXPECT_FALSE(irsend.capture.repeat); +} + +// Decode a real example of a Goodweather message. +// https://github.com/crankyoldgit/IRremoteESP8266/issues/697#issuecomment-490209819 +TEST(TestDecodeGoodweather, RealExampleDecode) { + IRsendTest irsend(0); + IRrecv irrecv(0); + IRGoodweatherAc ac(0); + irsend.begin(); + ac.begin(); + + irsend.reset(); + // Raw Goodweather 48-bit message. + uint16_t rawData_4624AB[197] = { + 6828, 6828, 732, 1780, 652, 1830, 652, 1806, 678, 1830, 652, 1806, 678, + 1830, 652, 1830, 652, 1834, 706, 518, 734, 508, 734, 514, 734, 510, 732, + 510, 732, 510, 732, 510, 732, 514, 732, 1776, 706, 1780, 628, 1854, 628, + 1832, 654, 1832, 654, 1856, 628, 1832, 634, 1876, 680, 536, 708, 536, 708, + 536, 706, 538, 706, 538, 706, 538, 706, 536, 680, 564, 680, 1828, 708, + 1758, 680, 1804, 680, 1828, 708, 1778, 732, 1754, 732, 1754, 732, 1756, + 732, 490, 658, 586, 658, 586, 658, 586, 658, 586, 658, 584, 658, 586, 658, + 586, 660, 1850, 704, 520, 658, 1828, 658, 1826, 658, 1826, 658, 586, 660, + 584, 684, 1826, 730, 490, 686, 1824, 660, 560, 710, 532, 710, 534, 712, + 1776, 712, 1774, 686, 560, 712, 1774, 712, 1798, 730, 492, 712, 1798, 684, + 1798, 678, 568, 730, 1756, 686, 1796, 686, 532, 712, 532, 712, 1796, 728, + 494, 712, 532, 738, 1772, 730, 492, 712, 532, 738, 506, 738, 1772, 660, + 582, 728, 1736, 712, 558, 710, 1750, 710, 558, 710, 510, 738, 1748, 738, + 508, 736, 1772, 684, 534, 736, 1772, 704, 518, 738, 1772, 660, 1824, 678, + 6770, 684}; // COOLIX 4624AB + irsend.sendRaw(rawData_4624AB, 197, 38000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(decode_type_t::GOODWEATHER, irsend.capture.decode_type); + EXPECT_EQ(kGoodweatherBits, irsend.capture.bits); + EXPECT_EQ(0xD52462000000, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); + EXPECT_FALSE(irsend.capture.repeat); + ac.setRaw(irsend.capture.value); + EXPECT_EQ( + "Power: On, Mode: 1 (COOL), Temp: 20C, Fan: 3 (Low), " + "Turbo: -, Light: -, Sleep: -, Swing: 0 (Fast), Command: 0 (Power)", + ac.toString()); + +uint16_t rawData_FAD2BE31[197] = { + 6142, 7348, 570, 1612, 638, 1562, 620, 1584, 670, 1538, 566, 1638, 564, + 1610, 618, 1582, 642, 1542, 638, 498, 622, 518, 618, 496, 622, 518, 596, + 522, 596, 542, 618, 498, 618, 520, 594, 1590, 614, 1586, 618, 1588, 640, + 1592, 538, 1614, 612, 1584, 620, 1584, 616, 1592, 564, 546, 596, 540, 596, + 520, 620, 520, 594, 524, 618, 522, 650, 466, 616, 522, 670, 1532, 618, 1568, + 590, 1610, 618, 1612, 640, 1530, 594, 1586, 618, 1616, 590, 1586, 640, 472, + 618, 520, 672, 446, 618, 520, 646, 474, 616, 520, 622, 500, 614, 518, 624, + 1612, 560, 1616, 590, 1584, 620, 520, 646, 1540, 612, 518, 622, 516, 596, + 1586, 618, 518, 622, 498, 616, 520, 622, 1582, 616, 498, 620, 1582, 622, + 1586, 586, 528, 616, 1582, 622, 498, 616, 518, 624, 1582, 614, 1592, 568, + 544, 620, 1580, 648, 1542, 610, 520, 622, 1586, 666, 1536, 592, 518, 600, + 542, 594, 1592, 590, 544, 620, 498, 616, 518, 622, 1580, 620, 496, 620, + 1586, 618, 502, 610, 1584, 620, 518, 672, 446, 620, 1612, 592, 504, 608, + 1586, 618, 518, 646, 1540, 612, 520, 600, 1604, 622, 1582, 596, 7382, 566}; + // UNKNOWN FAD2BE31 + + irsend.reset(); + irsend.sendRaw(rawData_FAD2BE31, 197, 38000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(decode_type_t::GOODWEATHER, irsend.capture.decode_type); + EXPECT_EQ(kGoodweatherBits, irsend.capture.bits); + EXPECT_EQ(0xD52668000000, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); + EXPECT_FALSE(irsend.capture.repeat); + ac.setRaw(irsend.capture.value); + EXPECT_EQ( + "Power: Off, Mode: 1 (COOL), Temp: 22C, Fan: 3 (Low), Turbo: -, " + "Light: -, Sleep: -, Swing: 2 (Off), Command: 0 (Power)", + ac.toString()); + + uint16_t rawData_5453D3AD[197] = { + 6190, 7298, 668, 1542, 614, 1590, 590, 1582, 620, 1584, 566, 1624, 632, + 1592, 616, 1588, 638, 1538, 594, 520, 620, 520, 594, 522, 620, 520, 586, + 530, 618, 520, 640, 480, 616, 520, 642, 1544, 612, 1588, 622, 1576, 668, + 1540, 564, 1640, 592, 1582, 646, 1558, 670, 1536, 594, 518, 622, 520, 594, + 522, 620, 520, 566, 552, 618, 520, 614, 504, 618, 518, 666, 1520, 610, + 1586, 618, 1612, 636, 1568, 564, 1590, 614, 1584, 620, 1582, 666, 1542, + 614, 526, 590, 520, 596, 520, 622, 520, 566, 550, 620, 520, 588, 530, 618, + 520, 668, 1536, 594, 520, 646, 1558, 668, 452, 616, 1584, 642, 498, 566, + 550, 618, 1582, 668, 454, 612, 1582, 646, 496, 594, 1614, 666, 450, 662, + 1536, 584, 1600, 612, 520, 642, 1590, 588, 502, 616, 520, 588, 1600, 612, + 1586, 616, 520, 612, 1574, 610, 1584, 644, 496, 564, 1620, 636, 1562, 640, + 524, 560, 530, 616, 1582, 644, 498, 620, 494, 670, 472, 622, 1558, 616, + 520, 642, 1564, 594, 518, 646, 1558, 668, 454, 638, 494, 668, 1538, 616, + 498, 642, 1558, 670, 454, 636, 1560, 642, 496, 614, 1592, 616, 1584, 620, + 7350, 668}; // UNKNOWN 5453D3AD + + irsend.reset(); + irsend.sendRaw(rawData_5453D3AD, 197, 38000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(decode_type_t::GOODWEATHER, irsend.capture.decode_type); + EXPECT_EQ(kGoodweatherBits, irsend.capture.bits); + EXPECT_EQ(0xD5266A000000, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); + EXPECT_FALSE(irsend.capture.repeat); + ac.setRaw(irsend.capture.value); + EXPECT_EQ( + "Power: On, Mode: 1 (COOL), Temp: 22C, Fan: 3 (Low), Turbo: -, " + "Light: -, Sleep: -, Swing: 2 (Off), Command: 0 (Power)", + ac.toString()); + + uint16_t rawData_B2354FBB[197] = { + 6192, 7298, 592, 1616, 618, 1584, 620, 1558, 668, 1520, 636, 1562, 642, + 1584, 590, 1614, 542, 1634, 622, 494, 668, 454, 638, 494, 670, 454, 638, + 492, 646, 480, 636, 494, 672, 470, 622, 1560, 642, 1556, 672, 1534, 614, + 1572, 636, 1584, 622, 1582, 644, 1534, 596, 1586, 642, 494, 666, 454, 640, + 494, 668, 452, 640, 494, 668, 454, 638, 494, 670, 470, 620, 1562, 666, + 470, 644, 1546, 634, 1584, 618, 1584, 644, 1534, 640, 1548, 636, 1560, + 644, 520, 542, 1618, 638, 494, 670, 454, 636, 496, 670, 454, 634, 494, + 672, 470, 620, 1564, 640, 496, 642, 1562, 616, 520, 622, 1558, 668, 450, + 640, 494, 694, 1536, 566, 524, 644, 1558, 666, 456, 638, 1558, 644, 520, + 572, 1588, 638, 1558, 644, 494, 590, 1596, 638, 1584, 620, 1584, 644, 454, + 638, 1556, 672, 472, 620, 1562, 640, 1558, 646, 494, 644, 470, 646, 496, + 566, 1618, 638, 494, 668, 1534, 646, 468, 674, 468, 568, 550, 670, 1530, + 670, 454, 638, 1560, 644, 494, 622, 1582, 620, 494, 646, 496, 620, 1560, + 644, 494, 668, 1522, 610, 518, 674, 1532, 614, 504, 640, 1584, 642, 1562, + 616, 7332, 594}; // UNKNOWN B2354FBB + + irsend.reset(); + irsend.sendRaw(rawData_B2354FBB, 197, 38000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(decode_type_t::GOODWEATHER, irsend.capture.decode_type); + EXPECT_EQ(kGoodweatherBits, irsend.capture.bits); + EXPECT_EQ(0xD5286A020000, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); + EXPECT_FALSE(irsend.capture.repeat); + ac.setRaw(irsend.capture.value); + EXPECT_EQ( + "Power: On, Mode: 1 (COOL), Temp: 24C, Fan: 3 (Low), Turbo: -, " + "Light: -, Sleep: -, Swing: 2 (Off), Command: 2 (Temp Up)", + ac.toString()); + + uint16_t rawData_71DD9105[197] = { + 6190, 7296, 696, 1496, 634, 1562, 642, 1582, 640, 1564, 564, 1598, 638, + 1558, 646, 1560, 588, 1616, 618, 520, 620, 494, 622, 494, 646, 494, 620, + 496, 644, 494, 590, 528, 642, 494, 642, 1544, 638, 1584, 618, 1564, 804, + 1394, 620, 1564, 640, 1558, 644, 1586, 562, 1616, 620, 492, 672, 470, 622, + 494, 646, 494, 622, 494, 646, 494, 620, 498, 644, 492, 596, 520, 644, 494, + 592, 1596, 612, 1584, 642, 1560, 614, 1612, 594, 1584, 620, 1558, 646, + 1556, 644, 1562, 618, 520, 620, 494, 620, 494, 646, 494, 568, 548, 644, + 494, 616, 1570, 638, 494, 670, 1534, 568, 550, 646, 1556, 616, 526, 618, + 492, 672, 1532, 568, 550, 646, 1558, 640, 500, 618, 1560, 668, 470, 642, + 1548, 658, 1536, 642, 520, 588, 504, 644, 492, 644, 478, 642, 1582, 618, + 1586, 590, 506, 640, 1556, 646, 1584, 562, 1616, 620, 1558, 646, 1556, + 670, 454, 638, 492, 648, 1558, 642, 478, 644, 492, 590, 530, 858, 1342, + 642, 496, 618, 1564, 642, 492, 642, 1548, 636, 492, 648, 494, 622, 1562, + 642, 492, 644, 1562, 618, 520, 620, 1558, 644, 476, 640, 1558, 646, 1558, + 612, 7382, 594}; // UNKNOWN 71DD9105 + + irsend.reset(); + irsend.sendRaw(rawData_71DD9105, 197, 38000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(decode_type_t::GOODWEATHER, irsend.capture.decode_type); + EXPECT_EQ(kGoodweatherBits, irsend.capture.bits); + EXPECT_EQ(0xD5276A030000, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); + EXPECT_FALSE(irsend.capture.repeat); + ac.setRaw(irsend.capture.value); + EXPECT_EQ( + "Power: On, Mode: 1 (COOL), Temp: 23C, Fan: 3 (Low), " + "Turbo: -, Light: -, Sleep: -, Swing: 2 (Off), Command: 3 (Temp Down)", + ac.toString()); + + uint16_t rawData_C4F9E573[199] = { + 6186, 7296, 648, 1558, 670, 1542, 612, 1584, 618, 1560, 668, 1534, 622, + 1566, 638, 1558, 646, 1584, 590, 506, 640, 492, 642, 480, 640, 494, 644, + 478, 640, 494, 668, 454, 614, 516, 648, 1560, 566, 1638, 618, 1584, 620, + 1556, 672, 1534, 620, 1564, 640, 1584, 618, 1586, 564, 528, 670, 468, 640, + 478, 642, 494, 644, 478, 640, 492, 670, 454, 638, 494, 670, 1560, 542, + 1636, 644, 468, 670, 1534, 620, 1586, 618, 1558, 646, 1556, 670, 1534, + 622, 492, 648, 494, 620, 1562, 642, 496, 642, 476, 642, 494, 696, 426, + 642, 492, 646, 1558, 568, 548, 644, 494, 642, 1564, 618, 1584, 620, 494, + 568, 548, 644, 1558, 644, 480, 636, 1584, 620, 1584, 644, 456, 634, 494, + 672, 1560, 540, 1638, 618, 494, 728, 1476, 592, 524, 646, 492, 616, 1572, + 638, 1560, 644, 492, 668, 1520, 638, 1562, 642, 494, 588, 1598, 638, 1560, + 642, 494, 622, 498, 642, 1556, 646, 478, 638, 492, 646, 494, 620, 1584, + 618, 522, 616, 1546, 612, 516, 648, 1556, 644, 476, 668, 468, 670, 1534, + 620, 494, 648, 1556, 670, 452, 640, 1558, 644, 496, 646, 1536, 616, 1582, + 646, 7326, 616, 41598, 230}; // UNKNOWN C4F9E573 + + irsend.reset(); + irsend.sendRaw(rawData_C4F9E573, 199, 38000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(decode_type_t::GOODWEATHER, irsend.capture.decode_type); + EXPECT_EQ(kGoodweatherBits, irsend.capture.bits); + EXPECT_EQ(0xD52666040000, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); + EXPECT_FALSE(irsend.capture.repeat); + ac.setRaw(irsend.capture.value); + EXPECT_EQ( + "Power: On, Mode: 1 (COOL), Temp: 22C, Fan: 3 (Low), Turbo: -, Light: -, " + "Sleep: -, Swing: 1 (Slow), Command: 4 (Swing)", + ac.toString()); +} + + +TEST(TestGoodweatherAcClass, toCommon) { + IRGoodweatherAc ac(0); + ac.setPower(true); + ac.setMode(kGoodweatherCool); + ac.setTemp(20); + ac.setFan(kGoodweatherFanHigh); + ac.setSwing(kGoodweatherSwingFast); + ac.setTurbo(true); + ac.setLight(true); + ac.setSleep(true); + // Now test it. + ASSERT_EQ(decode_type_t::GOODWEATHER, ac.toCommon().protocol); + ASSERT_TRUE(ac.toCommon().power); + ASSERT_TRUE(ac.toCommon().celsius); + ASSERT_EQ(20, ac.toCommon().degrees); + ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode); + ASSERT_EQ(stdAc::fanspeed_t::kMax, ac.toCommon().fanspeed); + ASSERT_EQ(stdAc::swingv_t::kAuto, ac.toCommon().swingv); + ASSERT_TRUE(ac.toCommon().turbo); + ASSERT_TRUE(ac.toCommon().light); + ASSERT_EQ(0, ac.toCommon().sleep); + // Unsupported. + ASSERT_EQ(-1, ac.toCommon().model); + ASSERT_EQ(stdAc::swingh_t::kOff, ac.toCommon().swingh); + ASSERT_FALSE(ac.toCommon().quiet); + ASSERT_FALSE(ac.toCommon().econo); + ASSERT_FALSE(ac.toCommon().filter); + ASSERT_FALSE(ac.toCommon().clean); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_EQ(-1, ac.toCommon().clock); +} + + +TEST(TestGoodweatherAcClass, Temperature) { + IRGoodweatherAc ac(0); + + ac.setTemp(kGoodweatherTempMin); + EXPECT_EQ(kGoodweatherTempMin, ac.getTemp()); + + ac.setTemp(kGoodweatherTempMin + 1); + EXPECT_EQ(kGoodweatherTempMin + 1, ac.getTemp()); + + ac.setTemp(kGoodweatherTempMax); + EXPECT_EQ(kGoodweatherTempMax, ac.getTemp()); + + ac.setTemp(kGoodweatherTempMin - 1); + EXPECT_EQ(kGoodweatherTempMin, ac.getTemp()); + + ac.setTemp(kGoodweatherTempMax + 1); + EXPECT_EQ(kGoodweatherTempMax, ac.getTemp()); + + ac.setTemp(23); + EXPECT_EQ(23, ac.getTemp()); + + ac.setTemp(27); + EXPECT_EQ(27, ac.getTemp()); + + ac.setTemp(22); + EXPECT_EQ(22, ac.getTemp()); + + ac.setTemp(25); + EXPECT_EQ(25, ac.getTemp()); + + ac.setTemp(0); + EXPECT_EQ(kGoodweatherTempMin, ac.getTemp()); + + ac.setTemp(255); + EXPECT_EQ(kGoodweatherTempMax, ac.getTemp()); +} + +TEST(TestGoodweatherAcClass, OperatingMode) { + IRGoodweatherAc ac(0); + ac.begin(); + + ac.setMode(kGoodweatherAuto); + EXPECT_EQ(kGoodweatherAuto, ac.getMode()); + + ac.setMode(kGoodweatherCool); + EXPECT_EQ(kGoodweatherCool, ac.getMode()); + + ac.setMode(kGoodweatherHeat); + EXPECT_EQ(kGoodweatherHeat, ac.getMode()); + + ac.setMode(kGoodweatherFan); // Should set fan speed to High. + EXPECT_EQ(kGoodweatherFan, ac.getMode()); + + ac.setMode(kGoodweatherDry); + EXPECT_EQ(kGoodweatherDry, ac.getMode()); + + ac.setMode(kGoodweatherHeat + 1); + EXPECT_EQ(kGoodweatherAuto, ac.getMode()); + + ac.setMode(kGoodweatherCool); + EXPECT_EQ(kGoodweatherCool, ac.getMode()); + + ac.setMode(kGoodweatherAuto - 1); + EXPECT_EQ(kGoodweatherAuto, ac.getMode()); + + ac.setMode(kGoodweatherCool); + ac.setMode(255); + EXPECT_EQ(kGoodweatherAuto, ac.getMode()); + + ac.setMode(kGoodweatherCool); + ac.setMode(0); + EXPECT_EQ(kGoodweatherAuto, ac.getMode()); +} + +TEST(TestGoodweatherAcClass, Power) { + IRGoodweatherAc ac(0); + ac.begin(); + + ac.setPower(true); + EXPECT_TRUE(ac.getPower()); + + ac.setPower(false); + EXPECT_EQ(false, ac.getPower()); + + ac.setPower(true); + EXPECT_TRUE(ac.getPower()); + + ac.off(); + EXPECT_EQ(false, ac.getPower()); + + ac.on(); + EXPECT_TRUE(ac.getPower()); +} + +TEST(TestGoodweatherAcClass, Light) { + IRGoodweatherAc ac(0); + ac.begin(); + + ac.setLight(true); + EXPECT_TRUE(ac.getLight()); + ac.setLight(false); + EXPECT_EQ(false, ac.getLight()); + ac.setLight(true); + EXPECT_TRUE(ac.getLight()); +} + +TEST(TestGoodweatherAcClass, Turbo) { + IRGoodweatherAc ac(0); + ac.begin(); + + ac.setTurbo(true); + EXPECT_TRUE(ac.getTurbo()); + ac.setTurbo(false); + EXPECT_EQ(false, ac.getTurbo()); + ac.setTurbo(true); + EXPECT_TRUE(ac.getTurbo()); +} + +TEST(TestGoodweatherAcClass, Sleep) { + IRGoodweatherAc ac(0); + ac.begin(); + + ac.setSleep(true); + EXPECT_TRUE(ac.getSleep()); + ac.setSleep(false); + EXPECT_EQ(false, ac.getSleep()); + ac.setSleep(true); + EXPECT_TRUE(ac.getSleep()); +} + +TEST(TestGoodweatherAcClass, FanSpeed) { + IRGoodweatherAc ac(0); + ac.begin(); + + // Unexpected value should default to Auto. + ac.setFan(255); + EXPECT_EQ(kGoodweatherFanAuto, ac.getFan()); + + ac.setFan(kGoodweatherFanLow); + EXPECT_EQ(kGoodweatherFanLow, ac.getFan()); + ac.setFan(kGoodweatherFanMed); + EXPECT_EQ(kGoodweatherFanMed, ac.getFan()); + ac.setFan(kGoodweatherFanHigh); + EXPECT_EQ(kGoodweatherFanHigh, ac.getFan()); + ac.setFan(kGoodweatherFanAuto); + EXPECT_EQ(kGoodweatherFanAuto, ac.getFan()); + + // Beyond Low should default to Auto. + ac.setFan(kGoodweatherFanLow + 1); + EXPECT_EQ(kGoodweatherFanAuto, ac.getFan()); +} + + +TEST(TestGoodweatherAcClass, SwingSpeed) { + IRGoodweatherAc ac(0); + ac.begin(); + + // Unexpected value should default to Off. + ac.setSwing(255); + EXPECT_EQ(kGoodweatherSwingOff, ac.getSwing()); + + ac.setSwing(kGoodweatherSwingSlow); + EXPECT_EQ(kGoodweatherSwingSlow, ac.getSwing()); + ac.setSwing(kGoodweatherSwingOff); + EXPECT_EQ(kGoodweatherSwingOff, ac.getSwing()); + ac.setSwing(kGoodweatherSwingFast); + EXPECT_EQ(kGoodweatherSwingFast, ac.getSwing()); + + // Beyond Off should default to Off. + ac.setSwing(kGoodweatherSwingOff + 1); + EXPECT_EQ(kGoodweatherSwingOff, ac.getSwing()); +} + +TEST(TestGoodweatherAcClass, Command) { + IRGoodweatherAc ac(0); + ac.begin(); + + ac.setCommand(kGoodweatherCmdMode); + EXPECT_EQ(kGoodweatherCmdMode, ac.getCommand()); + // Unexpected value should not change anything. + ac.setCommand(255); + EXPECT_EQ(kGoodweatherCmdMode, ac.getCommand()); + + ac.setCommand(kGoodweatherCmdPower); + EXPECT_EQ(kGoodweatherCmdPower, ac.getCommand()); + ac.setCommand(kGoodweatherCmdLight); + EXPECT_EQ(kGoodweatherCmdLight, ac.getCommand()); + + // Beyond Light should be ignored. + ac.setCommand(kGoodweatherCmdLight + 1); + EXPECT_EQ(kGoodweatherCmdLight, ac.getCommand()); +} diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Gree_test.cpp b/lib/IRremoteESP8266-2.6.5/test/ir_Gree_test.cpp similarity index 76% rename from lib/IRremoteESP8266-2.6.0/test/ir_Gree_test.cpp rename to lib/IRremoteESP8266-2.6.5/test/ir_Gree_test.cpp index a05b06391..d85df72b8 100644 --- a/lib/IRremoteESP8266-2.6.0/test/ir_Gree_test.cpp +++ b/lib/IRremoteESP8266-2.6.5/test/ir_Gree_test.cpp @@ -338,6 +338,50 @@ TEST(TestGreeClass, Turbo) { EXPECT_TRUE(irgree.getTurbo()); } +TEST(TestGreeClass, IFeel) { + IRGreeAC ac(0); + ac.begin(); + + ac.setIFeel(true); + EXPECT_TRUE(ac.getIFeel()); + + ac.setIFeel(false); + EXPECT_FALSE(ac.getIFeel()); + + ac.setIFeel(true); + EXPECT_TRUE(ac.getIFeel()); + + // https://github.com/crankyoldgit/IRremoteESP8266/pull/770#issuecomment-504992209 + uint8_t on[8] = {0x08, 0x09, 0x60, 0x50, 0x00, 0x44, 0x00, 0xF0}; + uint8_t off[8] = {0x08, 0x09, 0x60, 0x50, 0x00, 0x40, 0x00, 0xF0}; + ac.setRaw(off); + EXPECT_FALSE(ac.getIFeel()); + ac.setRaw(on); + EXPECT_TRUE(ac.getIFeel()); +} + +TEST(TestGreeClass, WiFi) { + IRGreeAC ac(0); + ac.begin(); + + ac.setWiFi(true); + EXPECT_TRUE(ac.getWiFi()); + + ac.setWiFi(false); + EXPECT_FALSE(ac.getWiFi()); + + ac.setWiFi(true); + EXPECT_TRUE(ac.getWiFi()); + + // https://github.com/crankyoldgit/IRremoteESP8266/pull/770#issuecomment-504992209 + uint8_t on[8] = {0x09, 0x09, 0x60, 0x50, 0x00, 0x40, 0x00, 0x00}; + uint8_t off[8] = {0x09, 0x09, 0x60, 0x50, 0x00, 0x00, 0x00, 0xC0}; + ac.setRaw(off); + EXPECT_FALSE(ac.getWiFi()); + ac.setRaw(on); + EXPECT_TRUE(ac.getWiFi()); +} + TEST(TestGreeClass, Sleep) { IRGreeAC irgree(0); irgree.begin(); @@ -451,9 +495,10 @@ TEST(TestGreeClass, HumanReadable) { IRGreeAC irgree(0); EXPECT_EQ( - "Power: Off, Mode: 0 (AUTO), Temp: 25C, Fan: 0 (AUTO), Turbo: Off, " - "XFan: Off, Light: On, Sleep: Off, Swing Vertical Mode: Manual, " - "Swing Vertical Pos: 0 (Last Pos)", + "Model: 1 (YAW1F), Power: Off, Mode: 0 (AUTO), Temp: 25C, Fan: 0 (Auto), " + "Turbo: Off, IFeel: Off, WiFi: Off, XFan: Off, Light: On, Sleep: Off, " + "Swing Vertical Mode: Manual, Swing Vertical Pos: 0 (Last Pos), " + "Timer: Off", irgree.toString()); irgree.on(); irgree.setMode(kGreeCool); @@ -463,11 +508,14 @@ TEST(TestGreeClass, HumanReadable) { irgree.setSleep(true); irgree.setLight(false); irgree.setTurbo(true); + irgree.setIFeel(true); + irgree.setWiFi(true); irgree.setSwingVertical(true, kGreeSwingAuto); + irgree.setTimer(12 * 60 + 30); EXPECT_EQ( - "Power: On, Mode: 1 (COOL), Temp: 16C, Fan: 3 (MAX), Turbo: On, " - "XFan: On, Light: Off, Sleep: On, Swing Vertical Mode: Auto, " - "Swing Vertical Pos: 1 (Auto)", + "Model: 1 (YAW1F), Power: On, Mode: 1 (COOL), Temp: 16C, Fan: 3 (High), " + "Turbo: On, IFeel: On, WiFi: On, XFan: On, Light: Off, Sleep: On, " + "Swing Vertical Mode: Auto, Swing Vertical Pos: 1 (Auto), Timer: 12:30", irgree.toString()); } @@ -501,7 +549,7 @@ TEST(TestDecodeGree, NormalRealExample) { uint8_t gree_code[kGreeStateLength] = {0x19, 0x0A, 0x60, 0x50, 0x02, 0x23, 0x00, 0xF0}; - // Ref: https://github.com/markszabo/IRremoteESP8266/issues/386 + // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/386 uint16_t rawData[139] = { 9008, 4496, 644, 1660, 676, 530, 648, 558, 672, 1636, 646, 1660, 644, 556, 650, 584, 626, 560, 644, 580, 628, 1680, 624, 560, @@ -525,8 +573,116 @@ TEST(TestDecodeGree, NormalRealExample) { EXPECT_STATE_EQ(gree_code, irsend.capture.state, kGreeBits); irgree.setRaw(irsend.capture.state); EXPECT_EQ( - "Power: On, Mode: 1 (COOL), Temp: 26C, Fan: 1, Turbo: Off, " - "XFan: Off, Light: On, Sleep: Off, Swing Vertical Mode: Manual, " - "Swing Vertical Pos: 2", + "Model: 1 (YAW1F), Power: On, Mode: 1 (COOL), Temp: 26C, Fan: 1 (Low), " + "Turbo: Off, IFeel: Off, WiFi: Off, XFan: Off, Light: On, Sleep: Off, " + "Swing Vertical Mode: Manual, Swing Vertical Pos: 2, Timer: Off", irgree.toString()); } + +TEST(TestGreeClass, toCommon) { + IRGreeAC ac(0); + ac.setPower(true); + ac.setMode(kGreeCool); + ac.setTemp(20); + ac.setFan(kGreeFanMax); + ac.setSwingVertical(false, kGreeSwingUp); + ac.setTurbo(true); + ac.setXFan(true); + ac.setLight(true); + ac.setSleep(true); + // Now test it. + ASSERT_EQ(decode_type_t::GREE, ac.toCommon().protocol); + ASSERT_EQ(gree_ac_remote_model_t::YAW1F, ac.toCommon().model); + ASSERT_TRUE(ac.toCommon().power); + ASSERT_TRUE(ac.toCommon().celsius); + ASSERT_EQ(20, ac.toCommon().degrees); + ASSERT_TRUE(ac.toCommon().turbo); + ASSERT_TRUE(ac.toCommon().clean); + ASSERT_TRUE(ac.toCommon().light); + ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode); + ASSERT_EQ(stdAc::fanspeed_t::kMax, ac.toCommon().fanspeed); + ASSERT_EQ(stdAc::swingv_t::kHighest, ac.toCommon().swingv); + ASSERT_EQ(0, ac.toCommon().sleep); + // Unsupported. + ASSERT_EQ(stdAc::swingh_t::kOff, ac.toCommon().swingh); + ASSERT_FALSE(ac.toCommon().quiet); + ASSERT_FALSE(ac.toCommon().econo); + ASSERT_FALSE(ac.toCommon().filter); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_EQ(-1, ac.toCommon().clock); +} + +TEST(TestGreeClass, Issue814Power) { + IRGreeAC ac(0); + ac.begin(); + + // https://github.com/crankyoldgit/IRremoteESP8266/issues/814#issuecomment-511263921 + uint8_t YBOFB_on[8] = {0x59, 0x07, 0x20, 0x50, 0x01, 0x20, 0x00, 0xC0}; + uint8_t off[8] = {0x51, 0x07, 0x20, 0x50, 0x01, 0x20, 0x00, 0x40}; + + ac.on(); + EXPECT_EQ(gree_ac_remote_model_t::YAW1F, ac.getModel()); + ac.setRaw(off); + EXPECT_FALSE(ac.getPower()); + ac.setRaw(YBOFB_on); + EXPECT_TRUE(ac.getPower()); + EXPECT_EQ(gree_ac_remote_model_t::YBOFB, ac.getModel()); + EXPECT_EQ( + "Model: 2 (YBOFB), Power: On, Mode: 1 (COOL), Temp: 23C, Fan: 1 (Low), " + "Turbo: Off, IFeel: Off, WiFi: Off, XFan: Off, Light: On, Sleep: Off, " + "Swing Vertical Mode: Auto, Swing Vertical Pos: 1 (Auto), Timer: Off", + ac.toString()); + ac.off(); + EXPECT_STATE_EQ(off, ac.getRaw(), kGreeBits); + ac.on(); + EXPECT_STATE_EQ(YBOFB_on, ac.getRaw(), kGreeBits); + uint8_t YAW1F_on[8] = {0x59, 0x07, 0x60, 0x50, 0x01, 0x20, 0x00, 0xC0}; + ac.setModel(gree_ac_remote_model_t::YAW1F); + EXPECT_STATE_EQ(YAW1F_on, ac.getRaw(), kGreeBits); + ac.off(); + EXPECT_STATE_EQ(off, ac.getRaw(), kGreeBits); + ac.setModel(gree_ac_remote_model_t::YBOFB); + ac.on(); + EXPECT_STATE_EQ(YBOFB_on, ac.getRaw(), kGreeBits); +} + +TEST(TestGreeClass, Timer) { + IRGreeAC ac(0); + ac.begin(); + + ac.setTimer(0); + EXPECT_FALSE(ac.getTimerEnabled()); + EXPECT_EQ(0, ac.getTimer()); + + ac.setTimer(29); + EXPECT_FALSE(ac.getTimerEnabled()); + EXPECT_EQ(0, ac.getTimer()); + + ac.setTimer(30); + EXPECT_TRUE(ac.getTimerEnabled()); + EXPECT_EQ(30, ac.getTimer()); + + ac.setTimer(60); + EXPECT_TRUE(ac.getTimerEnabled()); + EXPECT_EQ(60, ac.getTimer()); + + ac.setTimer(90); + EXPECT_TRUE(ac.getTimerEnabled()); + EXPECT_EQ(90, ac.getTimer()); + + ac.setTimer(10 * 60); + EXPECT_TRUE(ac.getTimerEnabled()); + EXPECT_EQ(10 * 60, ac.getTimer()); + + ac.setTimer(23 * 60 + 59); + EXPECT_TRUE(ac.getTimerEnabled()); + EXPECT_EQ(23 * 60 + 30, ac.getTimer()); + + ac.setTimer(24 * 60 + 1); + EXPECT_TRUE(ac.getTimerEnabled()); + EXPECT_EQ(24 * 60, ac.getTimer()); + + ac.setTimer(24 * 60 + 30); + EXPECT_TRUE(ac.getTimerEnabled()); + EXPECT_EQ(24 * 60, ac.getTimer()); +} diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Haier_test.cpp b/lib/IRremoteESP8266-2.6.5/test/ir_Haier_test.cpp similarity index 90% rename from lib/IRremoteESP8266-2.6.0/test/ir_Haier_test.cpp rename to lib/IRremoteESP8266-2.6.5/test/ir_Haier_test.cpp index 8d306cb0b..03cc63f93 100644 --- a/lib/IRremoteESP8266-2.6.0/test/ir_Haier_test.cpp +++ b/lib/IRremoteESP8266-2.6.5/test/ir_Haier_test.cpp @@ -343,24 +343,11 @@ TEST(TestHaierACClass, Timers) { EXPECT_EQ(kHaierAcCmdTimerCancel, haier.getCommand()); } -TEST(TestHaierACClass, TimeToString) { - EXPECT_EQ("00:00", IRHaierAC::timeToString(0)); - EXPECT_EQ("00:01", IRHaierAC::timeToString(1)); - EXPECT_EQ("00:10", IRHaierAC::timeToString(10)); - EXPECT_EQ("00:59", IRHaierAC::timeToString(59)); - - EXPECT_EQ("01:00", IRHaierAC::timeToString(60)); - EXPECT_EQ("01:01", IRHaierAC::timeToString(61)); - EXPECT_EQ("01:59", IRHaierAC::timeToString(60 + 59)); - EXPECT_EQ("18:59", IRHaierAC::timeToString(18 * 60 + 59)); - EXPECT_EQ("23:59", IRHaierAC::timeToString(23 * 60 + 59)); -} - TEST(TestHaierACClass, MessageConstuction) { IRHaierAC haier(0); EXPECT_EQ( - "Command: 1 (On), Mode: 0 (AUTO), Temp: 25C, Fan: 0 (AUTO), " + "Command: 1 (On), Mode: 0 (AUTO), Temp: 25C, Fan: 0 (Auto), " "Swing: 0 (Off), Sleep: Off, Health: Off, " "Current Time: 00:00, On Timer: Off, Off Timer: Off", haier.toString()); @@ -368,7 +355,7 @@ TEST(TestHaierACClass, MessageConstuction) { haier.setTemp(21); haier.setFan(kHaierAcFanHigh); EXPECT_EQ( - "Command: 3 (Fan), Mode: 1 (COOL), Temp: 21C, Fan: 3 (MAX), " + "Command: 3 (Fan), Mode: 1 (COOL), Temp: 21C, Fan: 3 (High), " "Swing: 0 (Off), Sleep: Off, Health: Off, " "Current Time: 00:00, On Timer: Off, Off Timer: Off", haier.toString()); @@ -377,7 +364,7 @@ TEST(TestHaierACClass, MessageConstuction) { haier.setSleep(true); haier.setCurrTime(615); // 10:15am EXPECT_EQ( - "Command: 8 (Sleep), Mode: 3 (HEAT), Temp: 21C, Fan: 3 (MAX), " + "Command: 8 (Sleep), Mode: 1 (COOL), Temp: 21C, Fan: 3 (High), " "Swing: 3 (Chg), Sleep: On, Health: On, " "Current Time: 10:15, On Timer: Off, Off Timer: Off", haier.toString()); @@ -386,7 +373,7 @@ TEST(TestHaierACClass, MessageConstuction) { haier.setCommand(kHaierAcCmdOn); EXPECT_EQ( - "Command: 1 (On), Mode: 2 (DRY), Temp: 21C, Fan: 2, " + "Command: 1 (On), Mode: 1 (COOL), Temp: 21C, Fan: 2 (Medium), " "Swing: 3 (Chg), Sleep: On, Health: On, " "Current Time: 10:15, On Timer: 13:20, Off Timer: 18:45", haier.toString()); @@ -394,20 +381,20 @@ TEST(TestHaierACClass, MessageConstuction) { // Now change a few already set things. haier.setMode(kHaierAcHeat); EXPECT_EQ( - "Command: 2 (Mode), Mode: 3 (HEAT), Temp: 21C, Fan: 2, " + "Command: 2 (Mode), Mode: 3 (HEAT), Temp: 21C, Fan: 2 (Medium), " "Swing: 3 (Chg), Sleep: On, Health: On, " - "Current Time: 10:15, On Timer: 13:52, Off Timer: 18:45", + "Current Time: 10:15, On Timer: 13:20, Off Timer: 18:45", haier.toString()); haier.setTemp(25); EXPECT_EQ( - "Command: 6 (Temp Up), Mode: 3 (HEAT), Temp: 25C, Fan: 2, " + "Command: 6 (Temp Up), Mode: 3 (HEAT), Temp: 25C, Fan: 2 (Medium), " "Swing: 3 (Chg), Sleep: On, Health: On, " - "Current Time: 10:15, On Timer: 13:52, Off Timer: 18:45", + "Current Time: 10:15, On Timer: 13:20, Off Timer: 18:45", haier.toString()); uint8_t expectedState[kHaierACStateLength] = {0xA5, 0x96, 0xEA, 0xCF, 0x32, - 0xED, 0x2D, 0x74, 0xB4}; + 0xED, 0x6D, 0x54, 0xD4}; EXPECT_STATE_EQ(expectedState, haier.getRaw(), kHaierACBits); // Check that the checksum is valid. @@ -419,7 +406,7 @@ TEST(TestHaierACClass, MessageConstuction) { EXPECT_FALSE(IRHaierAC::validChecksum(randomState)); haier.setRaw(randomState); EXPECT_EQ( - "Command: 9 (Timer Set), Mode: 3 (HEAT), Temp: 20C, Fan: 2, " + "Command: 9 (Timer Set), Mode: 3 (HEAT), Temp: 20C, Fan: 2 (Medium), " "Swing: 1 (Up), Sleep: On, Health: Off, " "Current Time: 16:32, On Timer: Off, Off Timer: Off", haier.toString()); @@ -683,7 +670,7 @@ TEST(TestHaierACYRW02Class, MessageConstuction) { IRHaierACYRW02 haier(0); EXPECT_EQ( - "Power: On, Button: 5 (Power), Mode: 0 (Auto), Temp: 25C," + "Power: On, Button: 5 (Power), Mode: 0 (AUTO), Temp: 25C," " Fan: 10 (Auto), Turbo: 0 (Off), Swing: 0 (Off), Sleep: Off," " Health: On", haier.toString()); @@ -691,7 +678,7 @@ TEST(TestHaierACYRW02Class, MessageConstuction) { haier.setTemp(21); haier.setFan(kHaierAcYrw02FanHigh); EXPECT_EQ( - "Power: On, Button: 4 (Fan), Mode: 2 (Cool), Temp: 21C," + "Power: On, Button: 4 (Fan), Mode: 2 (COOL), Temp: 21C," " Fan: 2 (High), Turbo: 0 (Off), Swing: 0 (Off), Sleep: Off," " Health: On", haier.toString()); @@ -701,7 +688,7 @@ TEST(TestHaierACYRW02Class, MessageConstuction) { haier.setSleep(true); haier.setTurbo(kHaierAcYrw02TurboHigh); EXPECT_EQ( - "Power: On, Button: 8 (Turbo), Mode: 2 (Cool), Temp: 21C," + "Power: On, Button: 8 (Turbo), Mode: 2 (COOL), Temp: 21C," " Fan: 2 (High), Turbo: 1 (High), Swing: 2 (Middle)," " Sleep: On, Health: Off", haier.toString()); @@ -716,7 +703,7 @@ TEST(TestHaierACYRW02Class, RealStates) { IRHaierACYRW02 haier(0); haier.setRaw(expectedState1); EXPECT_EQ( - "Power: On, Button: 7 (Health), Mode: 8 (Heat), Temp: 30C," + "Power: On, Button: 7 (Health), Mode: 8 (HEAT), Temp: 30C," " Fan: 2 (High), Turbo: 0 (Off), Swing: 1 (Top), Sleep: Off," " Health: Off", haier.toString()); @@ -726,7 +713,7 @@ TEST(TestHaierACYRW02Class, RealStates) { 0x80, 0x00, 0x00, 0x00, 0x00, 0x05, 0x75}; haier.setRaw(expectedState2); EXPECT_EQ( - "Power: Off, Button: 5 (Power), Mode: 8 (Heat), Temp: 30C," + "Power: Off, Button: 5 (Power), Mode: 8 (HEAT), Temp: 30C," " Fan: 2 (High), Turbo: 0 (Off), Swing: 0 (Off), Sleep: Off," " Health: Off", haier.toString()); @@ -736,7 +723,7 @@ TEST(TestHaierACYRW02Class, RealStates) { 0x20, 0x00, 0x00, 0x00, 0x00, 0x01, 0x2B}; haier.setRaw(expectedState3); EXPECT_EQ( - "Power: On, Button: 1 (Temp Down), Mode: 2 (Cool), Temp: 16C," + "Power: On, Button: 1 (Temp Down), Mode: 2 (COOL), Temp: 16C," " Fan: 2 (High), Turbo: 0 (Off), Swing: 2 (Middle), Sleep: Off," " Health: On", haier.toString()); @@ -747,7 +734,7 @@ TEST(TestHaierACYRW02Class, RealStates) { 0x20, 0x80, 0x00, 0x00, 0x00, 0x0B, 0xD7}; haier.setRaw(expectedState4); EXPECT_EQ( - "Power: On, Button: 11 (Sleep), Mode: 2 (Cool), Temp: 25C," + "Power: On, Button: 11 (Sleep), Mode: 2 (COOL), Temp: 25C," " Fan: 10 (Auto), Turbo: 0 (Off), Swing: 12 (Auto), Sleep: On," " Health: On", haier.toString()); @@ -758,7 +745,7 @@ TEST(TestHaierACYRW02Class, RealStates) { 0x20, 0x80, 0x00, 0x00, 0x00, 0x04, 0x85}; haier.setRaw(expectedState5); EXPECT_EQ( - "Power: On, Button: 4 (Fan), Mode: 2 (Cool), Temp: 25C," + "Power: On, Button: 4 (Fan), Mode: 2 (COOL), Temp: 25C," " Fan: 2 (High), Turbo: 0 (Off), Swing: 12 (Auto), Sleep: On," " Health: On", haier.toString()); @@ -831,7 +818,7 @@ TEST(TestDecodeHaierAC, RealExample1) { IRHaierAC haier(0); haier.setRaw(irsend.capture.state); EXPECT_EQ( - "Command: 1 (On), Mode: 0 (AUTO), Temp: 16C, Fan: 0 (AUTO), " + "Command: 1 (On), Mode: 1 (COOL), Temp: 16C, Fan: 0 (Auto), " "Swing: 0 (Off), Sleep: Off, Health: Off, " "Current Time: 00:01, On Timer: Off, Off Timer: Off", haier.toString()); @@ -873,7 +860,7 @@ TEST(TestDecodeHaierAC, RealExample2) { IRHaierAC haier(0); haier.setRaw(irsend.capture.state); EXPECT_EQ( - "Command: 6 (Temp Up), Mode: 0 (AUTO), Temp: 22C, Fan: 0 (AUTO), " + "Command: 6 (Temp Up), Mode: 1 (COOL), Temp: 22C, Fan: 0 (Auto), " "Swing: 0 (Off), Sleep: Off, Health: Off, " "Current Time: 00:01, On Timer: Off, Off Timer: Off", haier.toString()); @@ -915,7 +902,7 @@ TEST(TestDecodeHaierAC, RealExample3) { IRHaierAC haier(0); haier.setRaw(irsend.capture.state); EXPECT_EQ( - "Command: 12 (Health), Mode: 0 (AUTO), Temp: 30C, Fan: 0 (AUTO), " + "Command: 12 (Health), Mode: 1 (COOL), Temp: 30C, Fan: 0 (Auto), " "Swing: 0 (Off), Sleep: Off, Health: On, " "Current Time: 00:09, On Timer: Off, Off Timer: Off", haier.toString()); @@ -985,14 +972,14 @@ TEST(TestDecodeHaierAC_YRW02, RealExample) { IRHaierACYRW02 haier(0); haier.setRaw(irsend.capture.state); EXPECT_EQ( - "Power: On, Button: 5 (Power), Mode: 2 (Cool), Temp: 17C," + "Power: On, Button: 5 (Power), Mode: 2 (COOL), Temp: 17C," " Fan: 2 (High), Turbo: 0 (Off), Swing: 2 (Middle), Sleep: Off," " Health: On", haier.toString()); } // Default state of the remote needed to include hidden data. -// Ref: https://github.com/markszabo/IRremoteESP8266/issues/668 +// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/668 TEST(TestHaierAcIssues, Issue668) { IRHaierAC ac(0); IRHaierAC acText(1); @@ -1002,13 +989,14 @@ TEST(TestHaierAcIssues, Issue668) { // Turn on the AC. ac._irsend.reset(); char expected_on[] = - "Command: 1 (On), Mode: 0 (AUTO), Temp: 25C, Fan: 0 (AUTO), " + "Command: 1 (On), Mode: 1 (COOL), Temp: 25C, Fan: 0 (Auto), " "Swing: 0 (Off), Sleep: Off, Health: Off, Current Time: 00:00, " "On Timer: Off, Off Timer: Off"; // State taken from real capture: - // https://github.com/markszabo/IRremoteESP8266/issues/668#issuecomment-483531895 + // https://github.com/crankyoldgit/IRremoteESP8266/issues/668#issuecomment-483531895 uint8_t expected_on_state[9] = { 0xA5, 0x91, 0x20, 0x00, 0x0C, 0xC0, 0x20, 0x00, 0x42}; + ac.setMode(kHaierAcCool); ac.setCommand(kHaierAcCmdOn); EXPECT_EQ(expected_on, ac.toString()); ac.send(); @@ -1039,11 +1027,11 @@ TEST(TestHaierAcIssues, Issue668) { // Increase the temp by 1. ac._irsend.reset(); char expected_temp_plus_one[] = - "Command: 6 (Temp Up), Mode: 0 (AUTO), Temp: 26C, Fan: 0 (AUTO), " + "Command: 6 (Temp Up), Mode: 1 (COOL), Temp: 26C, Fan: 0 (Auto), " "Swing: 0 (Off), Sleep: Off, Health: Off, Current Time: 00:00, " "On Timer: Off, Off Timer: Off"; // State taken from real capture: - // https://github.com/markszabo/IRremoteESP8266/issues/668#issuecomment-483531895 + // https://github.com/crankyoldgit/IRremoteESP8266/issues/668#issuecomment-483531895 uint8_t expected_temp_plus_one_state[9] = { 0xA5, 0xA6, 0x20, 0x00, 0x0C, 0xC0, 0x20, 0x00, 0x57}; ASSERT_EQ(25, ac.getTemp()); @@ -1063,7 +1051,7 @@ TEST(TestHaierAcIssues, Issue668) { // Decrease the temp by 1. ac._irsend.reset(); char expected_temp_minus_one[] = - "Command: 7 (Temp Down), Mode: 0 (AUTO), Temp: 25C, Fan: 0 (AUTO), " + "Command: 7 (Temp Down), Mode: 1 (COOL), Temp: 25C, Fan: 0 (Auto), " "Swing: 0 (Off), Sleep: Off, Health: Off, Current Time: 00:00, " "On Timer: Off, Off Timer: Off"; ASSERT_EQ(26, ac.getTemp()); @@ -1078,3 +1066,65 @@ TEST(TestHaierAcIssues, Issue668) { acText.setRaw(ac._irsend.capture.state); EXPECT_EQ(expected_temp_minus_one, acText.toString()); } + +TEST(TestHaierACClass, toCommon) { + IRHaierAC ac(0); + ac.setCommand(kHaierAcCmdOn); + ac.setMode(kHaierAcCool); + ac.setTemp(20); + ac.setFan(kHaierAcFanHigh); + ac.setSwing(kHaierAcSwingChg); + ac.setHealth(true); + ac.setSleep(true); + // Now test it. + ASSERT_EQ(decode_type_t::HAIER_AC, ac.toCommon().protocol); + ASSERT_EQ(-1, ac.toCommon().model); + ASSERT_TRUE(ac.toCommon().power); + ASSERT_TRUE(ac.toCommon().celsius); + ASSERT_EQ(20, ac.toCommon().degrees); + ASSERT_TRUE(ac.toCommon().filter); + ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode); + ASSERT_EQ(stdAc::fanspeed_t::kMax, ac.toCommon().fanspeed); + ASSERT_EQ(stdAc::swingv_t::kAuto, ac.toCommon().swingv); + ASSERT_EQ(0, ac.toCommon().sleep); + // Unsupported. + ASSERT_EQ(stdAc::swingh_t::kOff, ac.toCommon().swingh); + ASSERT_FALSE(ac.toCommon().turbo); + ASSERT_FALSE(ac.toCommon().light); + ASSERT_FALSE(ac.toCommon().quiet); + ASSERT_FALSE(ac.toCommon().econo); + ASSERT_FALSE(ac.toCommon().clean); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_EQ(-1, ac.toCommon().clock); +} + +TEST(TestHaierACYRW02Class, toCommon) { + IRHaierACYRW02 ac(0); + ac.setPower(true); + ac.setMode(kHaierAcYrw02Cool); + ac.setTemp(20); + ac.setFan(kHaierAcYrw02FanHigh); + ac.setSwing(kHaierAcYrw02SwingTop); + ac.setHealth(true); + ac.setSleep(true); + // Now test it. + ASSERT_EQ(decode_type_t::HAIER_AC_YRW02, ac.toCommon().protocol); + ASSERT_EQ(-1, ac.toCommon().model); + ASSERT_TRUE(ac.toCommon().power); + ASSERT_TRUE(ac.toCommon().celsius); + ASSERT_EQ(20, ac.toCommon().degrees); + ASSERT_TRUE(ac.toCommon().filter); + ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode); + ASSERT_EQ(stdAc::fanspeed_t::kMax, ac.toCommon().fanspeed); + ASSERT_EQ(stdAc::swingv_t::kHighest, ac.toCommon().swingv); + ASSERT_EQ(0, ac.toCommon().sleep); + // Unsupported. + ASSERT_EQ(stdAc::swingh_t::kOff, ac.toCommon().swingh); + ASSERT_FALSE(ac.toCommon().turbo); + ASSERT_FALSE(ac.toCommon().light); + ASSERT_FALSE(ac.toCommon().quiet); + ASSERT_FALSE(ac.toCommon().econo); + ASSERT_FALSE(ac.toCommon().clean); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_EQ(-1, ac.toCommon().clock); +} diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Hitachi_test.cpp b/lib/IRremoteESP8266-2.6.5/test/ir_Hitachi_test.cpp similarity index 96% rename from lib/IRremoteESP8266-2.6.0/test/ir_Hitachi_test.cpp rename to lib/IRremoteESP8266-2.6.5/test/ir_Hitachi_test.cpp index a2471c4aa..7e7935638 100644 --- a/lib/IRremoteESP8266-2.6.0/test/ir_Hitachi_test.cpp +++ b/lib/IRremoteESP8266-2.6.5/test/ir_Hitachi_test.cpp @@ -300,7 +300,7 @@ TEST(TestIRHitachiAcClass, HumanReadable) { ac.setFan(kHitachiAcFanHigh); ac.setSwingVertical(true); EXPECT_EQ( - "Power: On, Mode: 3 (HEAT), Temp: 32C, Fan: 5 (HIGH), " + "Power: On, Mode: 3 (HEAT), Temp: 32C, Fan: 5 (High), " "Swing (Vertical): On, Swing (Horizontal): Off", ac.toString()); ac.setMode(kHitachiAcCool); @@ -309,7 +309,7 @@ TEST(TestIRHitachiAcClass, HumanReadable) { ac.setSwingVertical(false); ac.setSwingHorizontal(true); EXPECT_EQ( - "Power: On, Mode: 4 (COOL), Temp: 16C, Fan: 2 (LOW), " + "Power: On, Mode: 4 (COOL), Temp: 16C, Fan: 2 (Low), " "Swing (Vertical): Off, Swing (Horizontal): On", ac.toString()); } @@ -376,7 +376,7 @@ TEST(TestDecodeHitachiAC, NormalRealExample1) { 0x20, 0x04, 0x00, 0x80, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0xAC}; - // Ref: https://github.com/markszabo/IRremoteESP8266/issues/417 + // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/417 // 'On' '16c' 'auto fan' 'cooling mode' uint16_t rawData[451] = { 3318, 1720, 400, 1276, 400, 432, 398, 434, 398, 434, 400, 432, @@ -428,7 +428,7 @@ TEST(TestDecodeHitachiAC, NormalRealExample1) { IRHitachiAc ac(0); ac.setRaw(irsend.capture.state); EXPECT_EQ( - "Power: On, Mode: 4 (COOL), Temp: 16C, Fan: 1 (AUTO), " + "Power: On, Mode: 4 (COOL), Temp: 16C, Fan: 1 (Auto), " "Swing (Vertical): Off, Swing (Horizontal): Off", ac.toString()); } @@ -444,7 +444,7 @@ TEST(TestDecodeHitachiAC, NormalRealExample2) { 0xC0, 0x02, 0x00, 0xA0, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0xD0}; - // Ref: https://github.com/markszabo/IRremoteESP8266/issues/417 + // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/417 // 'On' '32c' 'auto fan' 'heating mode' uint16_t rawData[451] = { 3322, 1718, 400, 1278, 398, 432, 402, 430, 400, 430, 402, 430, @@ -496,7 +496,7 @@ TEST(TestDecodeHitachiAC, NormalRealExample2) { IRHitachiAc ac(0); ac.setRaw(irsend.capture.state); EXPECT_EQ( - "Power: On, Mode: 3 (HEAT), Temp: 32C, Fan: 5 (HIGH), " + "Power: On, Mode: 3 (HEAT), Temp: 32C, Fan: 5 (High), " "Swing (Vertical): Off, Swing (Horizontal): Off", ac.toString()); } @@ -543,7 +543,7 @@ TEST(TestDecodeHitachiAC1, NormalRealExample) { 0x61, 0x84, 0x00, 0x00, 0x00, 0x00, 0x10, 0x98}; - // Ref: https://github.com/markszabo/IRremoteESP8266/issues/453 + // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/453 uint16_t rawData[211] = { 3400, 3350, 450, 1250, 450, 400, 400, 1300, 400, 1300, 400, 400, 450, 400, 400, 1300, 400, 400, 400, 1300, 400, 400, 450, 1250, @@ -685,7 +685,7 @@ TEST(TestDecodeHitachiAC2, NormalRealExample) { 0x01, 0xFE, 0xC0, 0x3F, 0x80, 0x7F, 0x11, 0xEE, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00}; - // Ref: https://github.com/markszabo/IRremoteESP8266/issues/417 + // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/417 uint16_t rawData[851] = { // ON - 32c cool (fan auto) 3432, 1654, 492, 1180, 488, 360, 486, 360, 486, 360, 486, 362, @@ -768,3 +768,33 @@ TEST(TestDecodeHitachiAC2, NormalRealExample) { ASSERT_EQ(kHitachiAc2Bits, irsend.capture.bits); EXPECT_STATE_EQ(hitachi_code, irsend.capture.state, kHitachiAc2Bits); } + +TEST(TestIRHitachiAcClass, toCommon) { + IRHitachiAc ac(0); + ac.setPower(true); + ac.setMode(kHitachiAcCool); + ac.setTemp(20); + ac.setFan(kHitachiAcFanHigh); + ac.setSwingVertical(true); + ac.setSwingHorizontal(true); + // Now test it. + ASSERT_EQ(decode_type_t::HITACHI_AC, ac.toCommon().protocol); + ASSERT_EQ(-1, ac.toCommon().model); + ASSERT_TRUE(ac.toCommon().power); + ASSERT_TRUE(ac.toCommon().celsius); + ASSERT_EQ(20, ac.toCommon().degrees); + ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode); + ASSERT_EQ(stdAc::fanspeed_t::kMax, ac.toCommon().fanspeed); + ASSERT_EQ(stdAc::swingv_t::kAuto, ac.toCommon().swingv); + ASSERT_EQ(stdAc::swingh_t::kAuto, ac.toCommon().swingh); + // Unsupported. + ASSERT_FALSE(ac.toCommon().turbo); + ASSERT_FALSE(ac.toCommon().clean); + ASSERT_FALSE(ac.toCommon().light); + ASSERT_FALSE(ac.toCommon().quiet); + ASSERT_FALSE(ac.toCommon().econo); + ASSERT_FALSE(ac.toCommon().filter); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_EQ(-1, ac.toCommon().sleep); + ASSERT_EQ(-1, ac.toCommon().clock); +} diff --git a/lib/IRremoteESP8266-2.6.5/test/ir_Inax_test.cpp b/lib/IRremoteESP8266-2.6.5/test/ir_Inax_test.cpp new file mode 100644 index 000000000..b182cce32 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.5/test/ir_Inax_test.cpp @@ -0,0 +1,119 @@ +// Copyright 2019 crankyoldgit (David Conran) + +#include "IRsend.h" +#include "IRsend_test.h" +#include "IRutils.h" +#include "gtest/gtest.h" + + +// General housekeeping +TEST(TestInax, Housekeeping) { + ASSERT_EQ("INAX", typeToString(INAX)); + ASSERT_FALSE(hasACState(INAX)); +} + +// Tests for sendInax(). +// Test sending typical data only. +TEST(TestSendInax, SendDataOnly) { + IRsendTest irsend(0); + irsend.begin(); + + irsend.reset(); + irsend.sendInax(0x5C32CD); // Small flush. + EXPECT_EQ( + "f38000d50" + "m9000s4500" + "m560s560m560s1675m560s560m560s1675m560s1675m560s1675m560s560m560s560" + "m560s560m560s560m560s1675m560s1675m560s560m560s560m560s1675m560s560" + "m560s1675m560s1675m560s560m560s560m560s1675m560s1675m560s560m560s1675" + "m560s40000" + "m9000s4500" + "m560s560m560s1675m560s560m560s1675m560s1675m560s1675m560s560m560s560" + "m560s560m560s560m560s1675m560s1675m560s560m560s560m560s1675m560s560" + "m560s1675m560s1675m560s560m560s560m560s1675m560s1675m560s560m560s1675" + "m560s40000", + irsend.outputStr()); + + irsend.reset(); +} + +// Test sending with different repeats. +TEST(TestSendInax, SendWithRepeats) { + IRsendTest irsend(0); + irsend.begin(); + + irsend.reset(); + irsend.sendInax(0x5C32CD, kInaxBits, 0); // 0 repeats. + EXPECT_EQ( + "f38000d50" + "m9000s4500" + "m560s560m560s1675m560s560m560s1675m560s1675m560s1675m560s560m560s560" + "m560s560m560s560m560s1675m560s1675m560s560m560s560m560s1675m560s560" + "m560s1675m560s1675m560s560m560s560m560s1675m560s1675m560s560m560s1675" + "m560s40000", + irsend.outputStr()); + irsend.sendInax(0x5C32CD, kInaxBits, 2); // 2 repeats. + EXPECT_EQ( + "f38000d50" + "m9000s4500" + "m560s560m560s1675m560s560m560s1675m560s1675m560s1675m560s560m560s560" + "m560s560m560s560m560s1675m560s1675m560s560m560s560m560s1675m560s560" + "m560s1675m560s1675m560s560m560s560m560s1675m560s1675m560s560m560s1675" + "m560s40000" + "m9000s4500" + "m560s560m560s1675m560s560m560s1675m560s1675m560s1675m560s560m560s560" + "m560s560m560s560m560s1675m560s1675m560s560m560s560m560s1675m560s560" + "m560s1675m560s1675m560s560m560s560m560s1675m560s1675m560s560m560s1675" + "m560s40000" + "m9000s4500" + "m560s560m560s1675m560s560m560s1675m560s1675m560s1675m560s560m560s560" + "m560s560m560s560m560s1675m560s1675m560s560m560s560m560s1675m560s560" + "m560s1675m560s1675m560s560m560s560m560s1675m560s1675m560s560m560s1675" + "m560s40000", + irsend.outputStr()); +} + +// Tests for decodeInax(). + +// Decode normal Inax messages. +TEST(TestDecodeInax, SyntheticDecode) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + // Normal Inax 24-bit message. + irsend.reset(); + irsend.sendInax(0x5C32CD); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(INAX, irsend.capture.decode_type); + EXPECT_EQ(kInaxBits, irsend.capture.bits); + EXPECT_EQ(0x5C32CD, irsend.capture.value); + EXPECT_EQ(0, irsend.capture.address); + EXPECT_EQ(0, irsend.capture.command); +} + +// Decode real example via Issue #704 +TEST(TestDecodeInax, DecodeExamples) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + irsend.reset(); + // Inax Small Flush from Issue #309 + uint16_t smallFlushRawData[51] = { + 8996, 4474, 568, 556, 560, 1676, 568, 556, 562, 1676, 562, 1678, 566, + 1674, 566, 558, 560, 560, 566, 556, 566, 556, 560, 1678, 562, 1676, 566, + 556, 562, 560, 564, 1672, 566, 556, 562, 1676, 562, 1678, 562, 560, 564, + 558, 564, 1674, 560, 1678, 564, 560, 566, 1670, 562}; + + irsend.sendRaw(smallFlushRawData, 51, 38); + irsend.makeDecodeResult(); + + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(INAX, irsend.capture.decode_type); + EXPECT_EQ(kInaxBits, irsend.capture.bits); + EXPECT_EQ(0x5C32CD, irsend.capture.value); + EXPECT_EQ(0, irsend.capture.address); + EXPECT_EQ(0, irsend.capture.command); +} diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_JVC_test.cpp b/lib/IRremoteESP8266-2.6.5/test/ir_JVC_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.6.0/test/ir_JVC_test.cpp rename to lib/IRremoteESP8266-2.6.5/test/ir_JVC_test.cpp diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Kelvinator_test.cpp b/lib/IRremoteESP8266-2.6.5/test/ir_Kelvinator_test.cpp similarity index 93% rename from lib/IRremoteESP8266-2.6.0/test/ir_Kelvinator_test.cpp rename to lib/IRremoteESP8266-2.6.5/test/ir_Kelvinator_test.cpp index 38a298e58..436336c04 100644 --- a/lib/IRremoteESP8266-2.6.0/test/ir_Kelvinator_test.cpp +++ b/lib/IRremoteESP8266-2.6.5/test/ir_Kelvinator_test.cpp @@ -422,7 +422,7 @@ TEST(TestKelvinatorClass, HumanReadable) { IRKelvinatorAC irkelv(0); EXPECT_EQ( - "Power: Off, Mode: 0 (AUTO), Temp: 16C, Fan: 0 (AUTO), Turbo: Off, " + "Power: Off, Mode: 0 (AUTO), Temp: 16C, Fan: 0 (Auto), Turbo: Off, " "Quiet: Off, XFan: Off, IonFilter: Off, Light: Off, " "Swing (Horizontal): Off, Swing (Vertical): Off", irkelv.toString()); @@ -435,7 +435,7 @@ TEST(TestKelvinatorClass, HumanReadable) { irkelv.setLight(true); irkelv.setSwingHorizontal(true); EXPECT_EQ( - "Power: On, Mode: 1 (COOL), Temp: 25C, Fan: 5 (MAX), Turbo: Off, " + "Power: On, Mode: 1 (COOL), Temp: 25C, Fan: 5 (High), Turbo: Off, " "Quiet: Off, XFan: On, IonFilter: On, Light: On, " "Swing (Horizontal): On, Swing (Vertical): Off", irkelv.toString()); @@ -520,3 +520,39 @@ TEST(TestDecodeKelvinator, NormalSynthetic) { ASSERT_EQ(kKelvinatorBits, irsend.capture.bits); EXPECT_STATE_EQ(kelv_code, irsend.capture.state, kKelvinatorBits); } + +TEST(TestKelvinatorClass, toCommon) { + IRKelvinatorAC ac(0); + ac.setPower(true); + ac.setMode(kKelvinatorCool); + ac.setTemp(20); + ac.setFan(kKelvinatorFanMax); + ac.setIonFilter(true); + ac.setXFan(true); + ac.setQuiet(false); + ac.setTurbo(true); + ac.setLight(true); + ac.setSwingHorizontal(false); + ac.setSwingVertical(true); + + // Now test it. + ASSERT_EQ(decode_type_t::KELVINATOR, ac.toCommon().protocol); + ASSERT_EQ(-1, ac.toCommon().model); + ASSERT_TRUE(ac.toCommon().power); + ASSERT_TRUE(ac.toCommon().celsius); + ASSERT_EQ(20, ac.toCommon().degrees); + ASSERT_TRUE(ac.toCommon().filter); + ASSERT_TRUE(ac.toCommon().clean); + ASSERT_FALSE(ac.toCommon().quiet); + ASSERT_TRUE(ac.toCommon().turbo); + ASSERT_TRUE(ac.toCommon().light); + ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode); + ASSERT_EQ(stdAc::fanspeed_t::kMax, ac.toCommon().fanspeed); + ASSERT_EQ(stdAc::swingv_t::kAuto, ac.toCommon().swingv); + ASSERT_EQ(stdAc::swingh_t::kOff, ac.toCommon().swingh); + // Unsupported. + ASSERT_FALSE(ac.toCommon().econo); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_EQ(-1, ac.toCommon().sleep); + ASSERT_EQ(-1, ac.toCommon().clock); +} diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_LG_test.cpp b/lib/IRremoteESP8266-2.6.5/test/ir_LG_test.cpp similarity index 99% rename from lib/IRremoteESP8266-2.6.0/test/ir_LG_test.cpp rename to lib/IRremoteESP8266-2.6.5/test/ir_LG_test.cpp index 2925494b9..d1e1b8659 100644 --- a/lib/IRremoteESP8266-2.6.0/test/ir_LG_test.cpp +++ b/lib/IRremoteESP8266-2.6.5/test/ir_LG_test.cpp @@ -429,7 +429,7 @@ TEST(TestDecodeLG2, RealLG2Example) { } // Tests for issue reported in -// https://github.com/markszabo/IRremoteESP8266/issues/620 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/620 TEST(TestDecodeLG, Issue620) { IRsendTest irsend(0); IRrecv irrecv(0); diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Lasertag_test.cpp b/lib/IRremoteESP8266-2.6.5/test/ir_Lasertag_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.6.0/test/ir_Lasertag_test.cpp rename to lib/IRremoteESP8266-2.6.5/test/ir_Lasertag_test.cpp diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Lego_test.cpp b/lib/IRremoteESP8266-2.6.5/test/ir_Lego_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.6.0/test/ir_Lego_test.cpp rename to lib/IRremoteESP8266-2.6.5/test/ir_Lego_test.cpp diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Lutron_test.cpp b/lib/IRremoteESP8266-2.6.5/test/ir_Lutron_test.cpp similarity index 98% rename from lib/IRremoteESP8266-2.6.0/test/ir_Lutron_test.cpp rename to lib/IRremoteESP8266-2.6.5/test/ir_Lutron_test.cpp index d682967ca..81cf0df9c 100644 --- a/lib/IRremoteESP8266-2.6.0/test/ir_Lutron_test.cpp +++ b/lib/IRremoteESP8266-2.6.5/test/ir_Lutron_test.cpp @@ -120,7 +120,7 @@ TEST(TestDecodeLutron, DocumentedExampleFullOff) { irsend.begin(); // Full Off code. - // Ref: https://github.com/markszabo/IRremoteESP8266/issues/515 + // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/515 uint16_t rawData[14] = {20518, 6839, 2280, 6839, 2280, 2280, 9119, 2280, 2280, 6839, 2280, 4560, 2280, 11399}; irsend.reset(); diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_MWM_test.cpp b/lib/IRremoteESP8266-2.6.5/test/ir_MWM_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.6.0/test/ir_MWM_test.cpp rename to lib/IRremoteESP8266-2.6.5/test/ir_MWM_test.cpp diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Magiquest_test.cpp b/lib/IRremoteESP8266-2.6.5/test/ir_Magiquest_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.6.0/test/ir_Magiquest_test.cpp rename to lib/IRremoteESP8266-2.6.5/test/ir_Magiquest_test.cpp diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Midea_test.cpp b/lib/IRremoteESP8266-2.6.5/test/ir_Midea_test.cpp similarity index 86% rename from lib/IRremoteESP8266-2.6.0/test/ir_Midea_test.cpp rename to lib/IRremoteESP8266-2.6.5/test/ir_Midea_test.cpp index ced3ea10c..319528e9c 100644 --- a/lib/IRremoteESP8266-2.6.0/test/ir_Midea_test.cpp +++ b/lib/IRremoteESP8266-2.6.5/test/ir_Midea_test.cpp @@ -397,13 +397,13 @@ TEST(TestMideaACClass, Temperature) { EXPECT_EQ(kMideaACMaxTempF, midea.getTemp(false)); // General changes. - midea.setTemp(17, true); // C - EXPECT_EQ(17, midea.getTemp(true)); // C - EXPECT_EQ(63, midea.getTemp(false)); // F + midea.setTemp(18, true); // C + EXPECT_EQ(18, midea.getTemp(true)); // C + EXPECT_EQ(64, midea.getTemp(false)); // F midea.setTemp(21, true); // C EXPECT_EQ(21, midea.getTemp(true)); // C - EXPECT_EQ(70, midea.getTemp(false)); // F + EXPECT_EQ(69, midea.getTemp(false)); // F midea.setTemp(25, true); // C EXPECT_EQ(25, midea.getTemp(true)); // C @@ -414,7 +414,7 @@ TEST(TestMideaACClass, Temperature) { EXPECT_EQ(86, midea.getTemp(false)); // F midea.setTemp(80, false); // F - EXPECT_EQ(26, midea.getTemp(true)); // C + EXPECT_EQ(27, midea.getTemp(true)); // C EXPECT_EQ(80, midea.getTemp(false)); // F midea.setTemp(70); // F @@ -440,25 +440,30 @@ TEST(TestMideaACClass, Sleep) { } TEST(TestMideaACClass, HumanReadableOutput) { - IRMideaAC midea(0); - midea.begin(); + IRMideaAC ac(0); + ac.begin(); - midea.setRaw(0xA1826FFFFF62); + ac.setRaw(0xA1826FFFFF62); EXPECT_EQ( - "Power: On, Mode: 2 (AUTO), Temp: 25C/77F, Fan: 0 (AUTO), " - "Sleep: Off", - midea.toString()); - midea.off(); - midea.setTemp(25); - midea.setFan(kMideaACFanHigh); - midea.setMode(kMideaACDry); - midea.setSleep(true); - EXPECT_EQ("Power: Off, Mode: 1 (DRY), Temp: 16C/62F, Fan: 3 (HI), Sleep: On", - midea.toString()); + "Power: On, Mode: 2 (AUTO), Celsius: Off, Temp: 25C/77F, Fan: 0 (Auto), " + "Sleep: Off, Swing(V) Toggle: Off", ac.toString()); + ac.off(); + ac.setTemp(25, true); + ac.setFan(kMideaACFanHigh); + ac.setMode(kMideaACDry); + ac.setSleep(true); + EXPECT_EQ( + "Power: Off, Mode: 1 (DRY), Celsius: Off, Temp: 25C/77F, Fan: 3 (High), " + "Sleep: On, Swing(V) Toggle: Off", ac.toString()); + ac.setUseCelsius(true); + EXPECT_EQ( + "Power: Off, Mode: 1 (DRY), Celsius: On, Temp: 25C/77F, Fan: 3 (High), " + "Sleep: On, Swing(V) Toggle: Off", ac.toString()); - midea.setRaw(0xA19867FFFF7E); - EXPECT_EQ("Power: On, Mode: 0 (COOL), Temp: 20C/69F, Fan: 3 (HI), Sleep: Off", - midea.toString()); + ac.setRaw(0xA19867FFFF7E); + EXPECT_EQ( + "Power: On, Mode: 0 (COOL), Celsius: Off, Temp: 21C/69F, Fan: 3 (High), " + "Sleep: Off, Swing(V) Toggle: Off", ac.toString()); } // Tests for decodeMidea(). @@ -656,3 +661,84 @@ TEST(TestDecodeMidea, DecodeRealExample) { EXPECT_EQ(kMideaBits, irsend.capture.bits); EXPECT_EQ(0xA18263FFFF6E, irsend.capture.value); } + +TEST(TestMideaACClass, toCommon) { + IRMideaAC ac(0); + ac.setPower(true); + ac.setMode(kMideaACCool); + ac.setUseCelsius(true); + ac.setTemp(20, true); + ac.setFan(kMideaACFanHigh); + // Now test it. + ASSERT_EQ(decode_type_t::MIDEA, ac.toCommon().protocol); + ASSERT_EQ(-1, ac.toCommon().model); + ASSERT_TRUE(ac.toCommon().power); + ASSERT_TRUE(ac.toCommon().celsius); + ASSERT_EQ(20, ac.toCommon().degrees); + ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode); + ASSERT_EQ(stdAc::fanspeed_t::kMax, ac.toCommon().fanspeed); + ASSERT_EQ(stdAc::swingv_t::kOff, ac.toCommon().swingv); + // Unsupported. + ASSERT_EQ(stdAc::swingh_t::kOff, ac.toCommon().swingh); + ASSERT_FALSE(ac.toCommon().turbo); + ASSERT_FALSE(ac.toCommon().clean); + ASSERT_FALSE(ac.toCommon().light); + ASSERT_FALSE(ac.toCommon().quiet); + ASSERT_FALSE(ac.toCommon().econo); + ASSERT_FALSE(ac.toCommon().filter); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_EQ(-1, ac.toCommon().sleep); + ASSERT_EQ(-1, ac.toCommon().clock); +} + +// https://github.com/crankyoldgit/IRremoteESP8266/issues/819 +TEST(TestMideaACClass, CelsiusRemoteTemp) { + IRMideaAC ac(0); + uint64_t on_cool_low_17c = 0xA18840FFFF56; + uint64_t on_cool_low_30c = 0xA1884DFFFF5D; + ac.on(); + ac.setMode(kMideaACCool); + ac.setFan(kMideaACFanLow); + ac.setTemp(17, true); + EXPECT_FALSE(ac.getUseCelsius()); + ac.setUseCelsius(true); + EXPECT_TRUE(ac.getUseCelsius()); + EXPECT_EQ(on_cool_low_17c, ac.getRaw()); + EXPECT_EQ( + "Power: On, Mode: 0 (COOL), Celsius: On, Temp: 17C/62F, Fan: 1 (Low), " + "Sleep: Off, Swing(V) Toggle: Off", ac.toString()); + ac.setRaw(on_cool_low_17c); + EXPECT_EQ(17, ac.getTemp(true)); + EXPECT_EQ(62, ac.getTemp(false)); + EXPECT_EQ( + "Power: On, Mode: 0 (COOL), Celsius: On, Temp: 17C/62F, Fan: 1 (Low), " + "Sleep: Off, Swing(V) Toggle: Off", ac.toString()); + ac.setTemp(17, true); + EXPECT_EQ(17, ac.getTemp(true)); + EXPECT_EQ(62, ac.getTemp(false)); + EXPECT_EQ(on_cool_low_17c, ac.getRaw()); + + ac.setRaw(on_cool_low_30c); + EXPECT_EQ( + "Power: On, Mode: 0 (COOL), Celsius: On, Temp: 30C/86F, Fan: 1 (Low), " + "Sleep: Off, Swing(V) Toggle: Off", ac.toString()); +} + +// https://github.com/crankyoldgit/IRremoteESP8266/issues/819 +TEST(TestMideaACClass, SwingV) { + IRMideaAC ac(0); + ac.setSwingVToggle(false); + ASSERT_FALSE(ac.getSwingVToggle()); + ac.setSwingVToggle(true); + ASSERT_TRUE(ac.getSwingVToggle()); + EXPECT_EQ( + "Power: On, Mode: 2 (AUTO), Celsius: Off, Temp: 25C/77F, Fan: 0 (Auto), " + "Sleep: Off, Swing(V) Toggle: On", ac.toString()); + ac.setSwingVToggle(false); + ASSERT_FALSE(ac.getSwingVToggle()); + EXPECT_EQ( + "Power: On, Mode: 2 (AUTO), Celsius: Off, Temp: 25C/77F, Fan: 0 (Auto), " + "Sleep: Off, Swing(V) Toggle: Off", ac.toString()); + ac.setRaw(kMideaACToggleSwingV); + EXPECT_EQ("Swing(V) Toggle: On", ac.toString()); +} diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_MitsubishiHeavy_test.cpp b/lib/IRremoteESP8266-2.6.5/test/ir_MitsubishiHeavy_test.cpp similarity index 87% rename from lib/IRremoteESP8266-2.6.0/test/ir_MitsubishiHeavy_test.cpp rename to lib/IRremoteESP8266-2.6.5/test/ir_MitsubishiHeavy_test.cpp index 340a04078..0af6b5d07 100644 --- a/lib/IRremoteESP8266-2.6.0/test/ir_MitsubishiHeavy_test.cpp +++ b/lib/IRremoteESP8266-2.6.5/test/ir_MitsubishiHeavy_test.cpp @@ -293,7 +293,7 @@ TEST(TestMitsubishiHeavy152AcClass, HumanReadable) { IRMitsubishiHeavy152Ac ac(0); EXPECT_EQ( - "Power: Off, Mode: 0 (Auto), Temp: 17C, Fan: 0 (Auto), " + "Power: Off, Mode: 0 (AUTO), Temp: 17C, Fan: 0 (Auto), " "Swing (V): 0 (Auto), Swing (H): 0 (Auto), Silent: Off, Turbo: Off, " "Econo: Off, Night: Off, Filter: Off, 3D: Off, Clean: Off", ac.toString()); @@ -310,7 +310,7 @@ TEST(TestMitsubishiHeavy152AcClass, HumanReadable) { ac.setSwingVertical(kMitsubishiHeavy152SwingVAuto); ac.setSwingHorizontal(kMitsubishiHeavy152SwingHAuto); EXPECT_EQ( - "Power: On, Mode: 1 (Cool), Temp: 17C, Fan: 4 (Max), " + "Power: On, Mode: 1 (COOL), Temp: 17C, Fan: 4 (Max), " "Swing (V): 0 (Auto), Swing (H): 0 (Auto), Silent: On, Turbo: Off, " "Econo: Off, Night: On, Filter: On, 3D: On, Clean: Off", ac.toString()); @@ -327,7 +327,7 @@ TEST(TestMitsubishiHeavy152AcClass, HumanReadable) { ac.setSwingHorizontal(kMitsubishiHeavy152SwingHLeftMax); EXPECT_EQ( - "Power: On, Mode: 4 (Heat), Temp: 31C, Fan: 8 (Turbo), " + "Power: On, Mode: 4 (HEAT), Temp: 31C, Fan: 8 (Turbo), " "Swing (V): 5 (Lowest), Swing (H): 1 (Max Left), Silent: Off, Turbo: On, " "Econo: Off, Night: Off, Filter: On, 3D: Off, Clean: Off", ac.toString()); @@ -338,7 +338,7 @@ TEST(TestMitsubishiHeavy152AcClass, HumanReadable) { ac.setSwingVertical(kMitsubishiHeavy152SwingVOff); EXPECT_EQ( - "Power: On, Mode: 0 (Auto), Temp: 31C, Fan: 6 (Econo), " + "Power: On, Mode: 0 (AUTO), Temp: 31C, Fan: 6 (Econo), " "Swing (V): 6 (Off), Swing (H): 1 (Max Left), Silent: Off, " "Turbo: Off, Econo: On, Night: Off, Filter: On, 3D: Off, Clean: On", ac.toString()); @@ -349,7 +349,7 @@ TEST(TestMitsubishiHeavy152AcClass, HumanReadable) { ac.setMode(kMitsubishiHeavyDry); ac.setSwingHorizontal(kMitsubishiHeavy152SwingHLeftRight); EXPECT_EQ( - "Power: On, Mode: 2 (Dry), Temp: 25C, Fan: 0 (Auto), " + "Power: On, Mode: 2 (DRY), Temp: 25C, Fan: 0 (Auto), " "Swing (V): 6 (Off), Swing (H): 7 (Left Right), Silent: Off, " "Turbo: Off, Econo: Off, Night: Off, Filter: Off, 3D: Off, Clean: Off", ac.toString()); @@ -359,7 +359,7 @@ TEST(TestMitsubishiHeavy152AcClass, ReconstructKnownExample) { IRMitsubishiHeavy152Ac ac(0); EXPECT_EQ( - "Power: Off, Mode: 0 (Auto), Temp: 17C, Fan: 0 (Auto), " + "Power: Off, Mode: 0 (AUTO), Temp: 17C, Fan: 0 (Auto), " "Swing (V): 0 (Auto), Swing (H): 0 (Auto), Silent: Off, Turbo: Off, " "Econo: Off, Night: Off, Filter: Off, 3D: Off, Clean: Off", ac.toString()); @@ -377,7 +377,7 @@ TEST(TestMitsubishiHeavy152AcClass, ReconstructKnownExample) { ac.setSwingVertical(kMitsubishiHeavy152SwingVAuto); ac.setSwingHorizontal(kMitsubishiHeavy152SwingHAuto); EXPECT_EQ( - "Power: On, Mode: 4 (Heat), Temp: 24C, Fan: 4 (Max), " + "Power: On, Mode: 4 (HEAT), Temp: 24C, Fan: 4 (Max), " "Swing (V): 0 (Auto), Swing (H): 0 (Auto), Silent: Off, Turbo: Off, " "Econo: Off, Night: Off, Filter: Off, 3D: Off, Clean: Off", ac.toString()); @@ -635,7 +635,7 @@ TEST(TestMitsubishiHeavy88AcClass, HumanReadable) { IRMitsubishiHeavy88Ac ac(0); EXPECT_EQ( - "Power: Off, Mode: 0 (Auto), Temp: 17C, Fan: 0 (Auto), " + "Power: Off, Mode: 0 (AUTO), Temp: 17C, Fan: 0 (Auto), " "Swing (V): 0 (Off), Swing (H): 0 (Off), " "Turbo: Off, Econo: Off, 3D: Off, Clean: Off", ac.toString()); @@ -648,7 +648,7 @@ TEST(TestMitsubishiHeavy88AcClass, HumanReadable) { ac.set3D(true); ac.setSwingVertical(kMitsubishiHeavy88SwingVAuto); EXPECT_EQ( - "Power: On, Mode: 1 (Cool), Temp: 17C, Fan: 4 (High), " + "Power: On, Mode: 1 (COOL), Temp: 17C, Fan: 4 (High), " "Swing (V): 16 (Auto), Swing (H): 200 (3D), " "Turbo: Off, Econo: Off, 3D: On, Clean: Off", ac.toString()); @@ -662,7 +662,7 @@ TEST(TestMitsubishiHeavy88AcClass, HumanReadable) { ac.setSwingHorizontal(kMitsubishiHeavy88SwingHLeftMax); EXPECT_EQ( - "Power: On, Mode: 4 (Heat), Temp: 31C, Fan: 6 (Turbo), " + "Power: On, Mode: 4 (HEAT), Temp: 31C, Fan: 6 (Turbo), " "Swing (V): 26 (Lowest), Swing (H): 4 (Max Left), Turbo: On, Econo: Off, " "3D: Off, Clean: Off", ac.toString()); @@ -673,7 +673,7 @@ TEST(TestMitsubishiHeavy88AcClass, HumanReadable) { ac.setSwingVertical(kMitsubishiHeavy88SwingVOff); EXPECT_EQ( - "Power: On, Mode: 0 (Auto), Temp: 31C, Fan: 7 (Econo), " + "Power: On, Mode: 0 (AUTO), Temp: 31C, Fan: 7 (Econo), " "Swing (V): 0 (Off), Swing (H): 4 (Max Left), Turbo: Off, Econo: On, " "3D: Off, Clean: On", ac.toString()); @@ -684,7 +684,7 @@ TEST(TestMitsubishiHeavy88AcClass, HumanReadable) { ac.setMode(kMitsubishiHeavyDry); ac.setSwingHorizontal(kMitsubishiHeavy88SwingHLeftRight); EXPECT_EQ( - "Power: On, Mode: 2 (Dry), Temp: 25C, Fan: 0 (Auto), " + "Power: On, Mode: 2 (DRY), Temp: 25C, Fan: 0 (Auto), " "Swing (V): 0 (Off), Swing (H): 72 (Left Right), Turbo: Off, Econo: Off, " "3D: Off, Clean: Off", ac.toString()); @@ -704,7 +704,7 @@ TEST(TestDecodeMitsubishiHeavy, ZmsRealExample) { 0xF8, 0x04, 0xFB, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x80, 0x7F}; - // Ref: https://github.com/markszabo/IRremoteESP8266/issues/660#issuecomment-480571466 + // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/660#issuecomment-480571466 uint16_t rawData[307] = { 3136, 1638, 364, 428, 366, 1224, 362, 432, 364, 430, 364, 1226, 362, 432, 364, 1224, 366, 428, 366, 430, 366, 1224, 362, 1228, 362, 1228, 362, 432, @@ -739,7 +739,7 @@ TEST(TestDecodeMitsubishiHeavy, ZmsRealExample) { EXPECT_STATE_EQ(expected, irsend.capture.state, irsend.capture.bits); ac.setRaw(irsend.capture.state); EXPECT_EQ( - "Power: On, Mode: 4 (Heat), Temp: 24C, Fan: 4 (Max), " + "Power: On, Mode: 4 (HEAT), Temp: 24C, Fan: 4 (Max), " "Swing (V): 0 (Auto), Swing (H): 0 (Auto), Silent: Off, Turbo: Off, " "Econo: Off, Night: Off, Filter: Off, 3D: Off, Clean: Off", ac.toString()); @@ -766,7 +766,7 @@ TEST(TestDecodeMitsubishiHeavy, ZmsSyntheticExample) { EXPECT_STATE_EQ(expected, irsend.capture.state, irsend.capture.bits); ac.setRaw(irsend.capture.state); EXPECT_EQ( - "Power: On, Mode: 4 (Heat), Temp: 24C, Fan: 4 (Max), " + "Power: On, Mode: 4 (HEAT), Temp: 24C, Fan: 4 (Max), " "Swing (V): 0 (Auto), Swing (H): 0 (Auto), Silent: Off, Turbo: Off, " "Econo: Off, Night: Off, Filter: Off, 3D: Off, Clean: Off", ac.toString()); @@ -784,7 +784,7 @@ TEST(TestDecodeMitsubishiHeavy, ZmsRealExample2) { 0xF8, 0x04, 0xFB, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x80, 0x7F}; - // Ref: https://github.com/markszabo/IRremoteESP8266/issues/660#issuecomment-480571466 + // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/660#issuecomment-480571466 uint16_t rawData[307] = { 3196, 1580, 398, 390, 404, 1190, 400, 390, 402, 390, 402, 1192, 402, 388, 402, 1192, 400, 390, 402, 392, 402, 1192, 400, 1188, 400, 1188, 400, 390, @@ -819,7 +819,7 @@ TEST(TestDecodeMitsubishiHeavy, ZmsRealExample2) { EXPECT_STATE_EQ(expected, irsend.capture.state, irsend.capture.bits); ac.setRaw(irsend.capture.state); EXPECT_EQ( - "Power: Off, Mode: 4 (Heat), Temp: 24C, Fan: 4 (Max), " + "Power: Off, Mode: 4 (HEAT), Temp: 24C, Fan: 4 (Max), " "Swing (V): 0 (Auto), Swing (H): 0 (Auto), Silent: Off, Turbo: Off, " "Econo: Off, Night: Off, Filter: Off, 3D: Off, Clean: Off", ac.toString()); @@ -844,8 +844,77 @@ TEST(TestDecodeMitsubishiHeavy, ZjsSyntheticExample) { EXPECT_STATE_EQ(expected, irsend.capture.state, irsend.capture.bits); ac.setRaw(irsend.capture.state); EXPECT_EQ( - "Power: On, Mode: 2 (Dry), Temp: 25C, Fan: 0 (Auto), " + "Power: On, Mode: 2 (DRY), Temp: 25C, Fan: 0 (Auto), " "Swing (V): 0 (Off), Swing (H): 72 (Left Right), Turbo: Off, Econo: Off, " "3D: Off, Clean: Off", ac.toString()); } + +TEST(TestMitsubishiHeavy152AcClass, toCommon) { + IRMitsubishiHeavy152Ac ac(0); + ac.setPower(true); + ac.setMode(kMitsubishiHeavyCool); + ac.setTemp(20); + ac.setFan(kMitsubishiHeavy152FanLow); + ac.setSwingVertical(kMitsubishiHeavy152SwingVHighest); + ac.setSwingHorizontal(kMitsubishiHeavy152SwingHRightMax); + ac.setTurbo(false); + ac.setEcono(true); + ac.setClean(true); + ac.setFilter(true); + ac.setSilent(true); + ac.setNight(true); + // Now test it. + ASSERT_EQ(decode_type_t::MITSUBISHI_HEAVY_152, ac.toCommon().protocol); + ASSERT_EQ(-1, ac.toCommon().model); + ASSERT_TRUE(ac.toCommon().power); + ASSERT_TRUE(ac.toCommon().celsius); + ASSERT_EQ(20, ac.toCommon().degrees); + ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode); + ASSERT_EQ(stdAc::fanspeed_t::kMin, ac.toCommon().fanspeed); + ASSERT_EQ(stdAc::swingv_t::kHighest, ac.toCommon().swingv); + ASSERT_EQ(stdAc::swingh_t::kRightMax, ac.toCommon().swingh); + ASSERT_FALSE(ac.toCommon().turbo); + ASSERT_TRUE(ac.toCommon().econo); + ASSERT_TRUE(ac.toCommon().clean); + ASSERT_TRUE(ac.toCommon().quiet); + ASSERT_TRUE(ac.toCommon().filter); + ASSERT_EQ(0, ac.toCommon().sleep); + // Unsupported. + ASSERT_FALSE(ac.toCommon().light); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_EQ(-1, ac.toCommon().clock); +} + +TEST(TestMitsubishiHeavy88AcClass, toCommon) { + IRMitsubishiHeavy88Ac ac(0); + ac.setPower(true); + ac.setMode(kMitsubishiHeavyCool); + ac.setTemp(20); + ac.setFan(kMitsubishiHeavy88FanLow); + ac.setSwingVertical(kMitsubishiHeavy88SwingVHighest); + ac.setSwingHorizontal(kMitsubishiHeavy88SwingHRightMax); + ac.setTurbo(false); + ac.setEcono(true); + ac.setClean(true); + // Now test it. + ASSERT_EQ(decode_type_t::MITSUBISHI_HEAVY_88, ac.toCommon().protocol); + ASSERT_EQ(-1, ac.toCommon().model); + ASSERT_TRUE(ac.toCommon().power); + ASSERT_TRUE(ac.toCommon().celsius); + ASSERT_EQ(20, ac.toCommon().degrees); + ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode); + ASSERT_EQ(stdAc::fanspeed_t::kMin, ac.toCommon().fanspeed); + ASSERT_EQ(stdAc::swingv_t::kHighest, ac.toCommon().swingv); + ASSERT_EQ(stdAc::swingh_t::kRightMax, ac.toCommon().swingh); + ASSERT_FALSE(ac.toCommon().turbo); + ASSERT_TRUE(ac.toCommon().econo); + ASSERT_TRUE(ac.toCommon().clean); + // Unsupported. + ASSERT_FALSE(ac.toCommon().quiet); + ASSERT_FALSE(ac.toCommon().light); + ASSERT_FALSE(ac.toCommon().filter); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_EQ(-1, ac.toCommon().sleep); + ASSERT_EQ(-1, ac.toCommon().clock); +} diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Mitsubishi_test.cpp b/lib/IRremoteESP8266-2.6.5/test/ir_Mitsubishi_test.cpp similarity index 97% rename from lib/IRremoteESP8266-2.6.0/test/ir_Mitsubishi_test.cpp rename to lib/IRremoteESP8266-2.6.5/test/ir_Mitsubishi_test.cpp index 6c9480b31..7d16ed91e 100644 --- a/lib/IRremoteESP8266-2.6.0/test/ir_Mitsubishi_test.cpp +++ b/lib/IRremoteESP8266-2.6.5/test/ir_Mitsubishi_test.cpp @@ -984,8 +984,8 @@ TEST(TestDecodeMitsubishiAC, DecodeRealExampleRepeatNeededButError) { TEST(TestMitsubishiACClass, HumanReadable) { IRMitsubishiAC irMitsu(0); EXPECT_EQ( - "Power: On (HEAT), Temp: 22C, FAN: SILENT, VANE: AUTO, " - "Time: 17:10, On timer: 00:00, Off timer: 00:00, Timer: -", + "Power: On, Mode: 8 (HEAT), Temp: 22C, Fan: 6 (Quiet), Vane: AUTO, " + "Wide Vane: 3, Time: 17:10, On timer: 00:00, Off timer: 00:00, Timer: -", irMitsu.toString()); } @@ -1133,3 +1133,33 @@ TEST(TestDecodeMitsubishi2, DecodeRealExample) { EXPECT_EQ(0xF, irsend.capture.address); EXPECT_EQ(0x82, irsend.capture.command); } + +TEST(TestMitsubishiACClass, toCommon) { + IRMitsubishiAC ac(0); + ac.setPower(true); + ac.setMode(kMitsubishiAcCool); + ac.setTemp(20); + ac.setFan(kMitsubishiAcFanSilent); + ac.setVane(kMitsubishiAcVaneAuto); + ac.setWideVane(kMitsubishiAcWideVaneAuto); + // Now test it. + ASSERT_EQ(decode_type_t::MITSUBISHI_AC, ac.toCommon().protocol); + ASSERT_EQ(-1, ac.toCommon().model); + ASSERT_TRUE(ac.toCommon().power); + ASSERT_TRUE(ac.toCommon().celsius); + ASSERT_EQ(20, ac.toCommon().degrees); + ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode); + ASSERT_EQ(stdAc::fanspeed_t::kMin, ac.toCommon().fanspeed); + ASSERT_EQ(stdAc::swingv_t::kAuto, ac.toCommon().swingv); + ASSERT_EQ(stdAc::swingh_t::kAuto, ac.toCommon().swingh); + ASSERT_TRUE(ac.toCommon().quiet); + // Unsupported. + ASSERT_FALSE(ac.toCommon().turbo); + ASSERT_FALSE(ac.toCommon().clean); + ASSERT_FALSE(ac.toCommon().light); + ASSERT_FALSE(ac.toCommon().econo); + ASSERT_FALSE(ac.toCommon().filter); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_EQ(-1, ac.toCommon().sleep); + ASSERT_EQ(-1, ac.toCommon().clock); +} diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_NEC_test.cpp b/lib/IRremoteESP8266-2.6.5/test/ir_NEC_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.6.0/test/ir_NEC_test.cpp rename to lib/IRremoteESP8266-2.6.5/test/ir_NEC_test.cpp diff --git a/lib/IRremoteESP8266-2.6.5/test/ir_Neoclima_test.cpp b/lib/IRremoteESP8266-2.6.5/test/ir_Neoclima_test.cpp new file mode 100644 index 000000000..38747f276 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.5/test/ir_Neoclima_test.cpp @@ -0,0 +1,434 @@ +// Copyright 2019 David Conran (crankyoldgit) + +#include "ir_Neoclima.h" +#include +#include "IRsend.h" +#include "IRsend_test.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "gtest/gtest.h" + +TEST(TestUtils, Housekeeping) { + ASSERT_EQ("NEOCLIMA", typeToString(decode_type_t::NEOCLIMA)); + ASSERT_EQ(decode_type_t::NEOCLIMA, strToDecodeType("NEOCLIMA")); + ASSERT_TRUE(hasACState(decode_type_t::NEOCLIMA)); +} + +// Test sending typical data only. +TEST(TestSendNeoclima, SendDataOnly) { + IRsendTest irsend(0); + irsend.begin(); + + uint8_t state[kNeoclimaStateLength] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x6A, 0x00, 0x2A, 0xA5, 0x39}; + irsend.reset(); + irsend.sendNeoclima(state); + EXPECT_EQ( + "f38000d50" + "m6112s7391" + "m537s571m537s571m537s571m537s571m537s571m537s571m537s571m537s571" + "m537s571m537s571m537s571m537s571m537s571m537s571m537s571m537s571" + "m537s571m537s571m537s571m537s571m537s571m537s571m537s571m537s571" + "m537s571m537s571m537s571m537s571m537s571m537s571m537s571m537s571" + "m537s571m537s571m537s571m537s571m537s571m537s571m537s571m537s571" + "m537s571m537s571m537s571m537s571m537s571m537s571m537s571m537s571" + "m537s571m537s571m537s571m537s571m537s571m537s571m537s571m537s571" + "m537s571m537s1651m537s571m537s1651m537s571m537s1651m537s1651m537s571" + "m537s571m537s571m537s571m537s571m537s571m537s571m537s571m537s571" + "m537s571m537s1651m537s571m537s1651m537s571m537s1651m537s571m537s571" + "m537s1651m537s571m537s1651m537s571m537s571m537s1651m537s571m537s1651" + "m537s1651m537s571m537s571m537s1651m537s1651m537s1651m537s571m537s571" + "m537s7391" + "m537s100000", + irsend.outputStr()); +} + +// https://github.com/crankyoldgit/IRremoteESP8266/issues/764#issuecomment-503755096 +TEST(TestDecodeNeoclima, RealExample) { + IRsendTest irsend(0); + IRrecv irrecv(0); + uint16_t rawData[197] = { + 6112, 7392, 540, 602, 516, 578, 522, 604, 540, 554, 540, 554, 540, 576, + 518, 576, 516, 554, 540, 608, 542, 554, 540, 554, 540, 576, 518, 604, 516, + 556, 540, 576, 546, 580, 542, 578, 542, 602, 518, 554, 542, 554, 568, 582, + 540, 554, 540, 582, 540, 578, 518, 582, 542, 576, 544, 530, 566, 534, 562, + 534, 562, 552, 542, 582, 540, 604, 518, 608, 542, 554, 540, 582, 540, 604, + 518, 580, 540, 606, 544, 554, 542, 554, 542, 580, 542, 576, 520, 554, 540, + 578, 518, 578, 518, 582, 544, 552, 570, 580, 544, 580, 542, 554, 542, 604, + 520, 576, 520, 580, 540, 556, 540, 556, 542, 584, 566, 580, 542, 1622, + 542, 554, 542, 1620, 544, 604, 520, 1642, 518, 1674, 548, 560, 564, 580, + 544, 554, 544, 552, 544, 554, 542, 556, 542, 576, 522, 554, 542, 556, 542, + 580, 542, 1670, 520, 578, 520, 1622, 542, 580, 518, 1646, 520, 558, 568, + 552, 546, 1628, 566, 580, 544, 1668, 522, 576, 520, 578, 520, 1670, 522, + 576, 522, 1670, 496, 1676, 570, 560, 566, 532, 564, 1648, 544, 1670, 522, + 1650, 544, 552, 544, 576, 520, 7390, 544}; // UNKNOWN EE182D95 + + uint8_t expectedState[kNeoclimaStateLength] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x6A, 0x00, 0x2A, 0xA5, 0x39}; + + irsend.begin(); + irsend.reset(); + irsend.sendRaw(rawData, 197, 38000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(decode_type_t::NEOCLIMA, irsend.capture.decode_type); + ASSERT_EQ(kNeoclimaBits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + IRNeoclimaAc ac(0); + ac.setRaw(irsend.capture.state); + EXPECT_EQ( + "Power: On, Mode: 1 (COOL), Temp: 26C, Fan: 3 (Low), " + "Swing(V): Off, Swing(H): On, Sleep: Off, Turbo: Off, Hold: Off, " + "Ion: Off, Eye: Off, Light: Off, Follow: Off, 8C Heat: Off, Fresh: Off, " + "Button: 0 (Power)", + ac.toString()); +} + +// Self decode. +TEST(TestDecodeNeoclima, SyntheticExample) { + IRsendTest irsend(0); + IRrecv irrecv(0); + + uint8_t expectedState[kNeoclimaStateLength] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x6A, 0x00, 0x2A, 0xA5, 0x39}; + + irsend.begin(); + irsend.reset(); + irsend.sendNeoclima(expectedState); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(decode_type_t::NEOCLIMA, irsend.capture.decode_type); + ASSERT_EQ(kNeoclimaBits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); +} + +TEST(TestIRNeoclimaAcClass, Power) { + IRNeoclimaAc ac(0); + ac.begin(); + + ac.on(); + EXPECT_TRUE(ac.getPower()); + + ac.off(); + EXPECT_FALSE(ac.getPower()); + + ac.setPower(true); + EXPECT_TRUE(ac.getPower()); + + ac.setPower(false); + EXPECT_FALSE(ac.getPower()); + + EXPECT_EQ(kNeoclimaButtonPower, ac.getButton()); +} + +TEST(TestIRNeoclimaAcClass, OperatingMode) { + IRNeoclimaAc ac(0); + ac.begin(); + + ac.setMode(kNeoclimaAuto); + EXPECT_EQ(kNeoclimaAuto, ac.getMode()); + EXPECT_EQ(kNeoclimaButtonMode, ac.getButton()); + + + ac.setMode(kNeoclimaCool); + EXPECT_EQ(kNeoclimaCool, ac.getMode()); + + ac.setMode(kNeoclimaHeat); + EXPECT_EQ(kNeoclimaHeat, ac.getMode()); + + ASSERT_NE(kNeoclimaFanHigh, kNeoclimaFanLow); + ac.setFan(kNeoclimaFanHigh); + ac.setMode(kNeoclimaDry); // Dry should lock the fan to speed LOW. + EXPECT_EQ(kNeoclimaDry, ac.getMode()); + EXPECT_EQ(kNeoclimaFanLow, ac.getFan()); + ac.setFan(kNeoclimaFanHigh); + EXPECT_EQ(kNeoclimaFanLow, ac.getFan()); + + ac.setMode(kNeoclimaFan); + EXPECT_EQ(kNeoclimaFan, ac.getMode()); + + ac.setMode(kNeoclimaHeat + 1); + EXPECT_EQ(kNeoclimaAuto, ac.getMode()); + + ac.setMode(255); + EXPECT_EQ(kNeoclimaAuto, ac.getMode()); +} + +TEST(TestIRNeoclimaAcClass, SetAndGetTemp) { + IRNeoclimaAc ac(0); + ac.setTemp(25); + EXPECT_EQ(25, ac.getTemp()); + ac.setTemp(kNeoclimaMinTemp); + EXPECT_EQ(kNeoclimaMinTemp, ac.getTemp()); + EXPECT_EQ(kNeoclimaButtonTempDown, ac.getButton()); + ac.setTemp(kNeoclimaMinTemp - 1); + EXPECT_EQ(kNeoclimaMinTemp, ac.getTemp()); + ac.setTemp(kNeoclimaMaxTemp); + EXPECT_EQ(kNeoclimaMaxTemp, ac.getTemp()); + EXPECT_EQ(kNeoclimaButtonTempUp, ac.getButton()); + ac.setTemp(kNeoclimaMaxTemp + 1); + EXPECT_EQ(kNeoclimaMaxTemp, ac.getTemp()); +} + +TEST(TestIRNeoclimaAcClass, FanSpeed) { + IRNeoclimaAc ac(0); + ac.begin(); + + ac.setFan(0); + EXPECT_EQ(0, ac.getFan()); + + ac.setFan(255); + EXPECT_EQ(kNeoclimaFanAuto, ac.getFan()); + + ac.setFan(kNeoclimaFanHigh); + EXPECT_EQ(kNeoclimaFanHigh, ac.getFan()); + + ac.setFan(std::max(kNeoclimaFanHigh, kNeoclimaFanLow) + 1); + EXPECT_EQ(kNeoclimaFanAuto, ac.getFan()); + + ac.setFan(kNeoclimaFanHigh - 1); + EXPECT_EQ(kNeoclimaFanHigh - 1, ac.getFan()); + + ac.setFan(1); + EXPECT_EQ(1, ac.getFan()); + + ac.setFan(1); + EXPECT_EQ(1, ac.getFan()); + + ac.setFan(3); + EXPECT_EQ(3, ac.getFan()); + EXPECT_EQ(kNeoclimaButtonFanSpeed, ac.getButton()); + + // Data from: + // https://drive.google.com/file/d/1kjYk4zS9NQcMQhFkak-L4mp4UuaAIesW/view + uint8_t fan_low[12] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x6A, 0x00, 0x29, 0xA5, 0x3D}; + uint8_t fan_medium[12] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x4A, 0x00, 0x29, 0xA5, 0x1D}; + uint8_t fan_high[12] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x2A, 0x00, 0x29, 0xA5, 0xFD}; + uint8_t fan_auto[12] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0A, 0x00, 0x29, 0xA5, 0xDD}; + ac.setRaw(fan_low); + EXPECT_EQ(kNeoclimaFanLow, ac.getFan()); + EXPECT_EQ(kNeoclimaButtonFanSpeed, ac.getButton()); + ac.setRaw(fan_medium); + EXPECT_EQ(kNeoclimaFanMed, ac.getFan()); + EXPECT_EQ(kNeoclimaButtonFanSpeed, ac.getButton()); + ac.setRaw(fan_high); + EXPECT_EQ(kNeoclimaFanHigh, ac.getFan()); + EXPECT_EQ(kNeoclimaButtonFanSpeed, ac.getButton()); + ac.setRaw(fan_auto); + EXPECT_EQ(kNeoclimaFanAuto, ac.getFan()); + EXPECT_EQ(kNeoclimaButtonFanSpeed, ac.getButton()); +} + +TEST(TestIRNeoclimaAcClass, Sleep) { + IRNeoclimaAc ac(0); + ac.begin(); + ac.setSleep(true); + EXPECT_TRUE(ac.getSleep()); + ac.setSleep(false); + EXPECT_FALSE(ac.getSleep()); + ac.setSleep(true); + EXPECT_TRUE(ac.getSleep()); + EXPECT_EQ(kNeoclimaButtonSleep, ac.getButton()); +} + +TEST(TestIRNeoclimaAcClass, Turbo) { + IRNeoclimaAc ac(0); + ac.begin(); + ac.setTurbo(true); + EXPECT_TRUE(ac.getTurbo()); + ac.setTurbo(false); + EXPECT_FALSE(ac.getTurbo()); + ac.setTurbo(true); + EXPECT_TRUE(ac.getTurbo()); + EXPECT_EQ(kNeoclimaButtonTurbo, ac.getButton()); + // Data from: + // https://drive.google.com/file/d/1tA09Gu_ZqDcHucscnqzv0V3cIUWOE0d1/view + uint8_t turbo_on[12] = { + 0x00, 0x00, 0x00, 0x08, 0x00, 0x0A, 0x00, 0x6A, 0x00, 0x88, 0xA5, 0xA9}; + uint8_t turbo_off[12] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x6A, 0x00, 0x88, 0xA5, 0xA1}; + ac.setRaw(turbo_on); + EXPECT_TRUE(ac.getTurbo()); + EXPECT_EQ(kNeoclimaButtonTurbo, ac.getButton()); + ac.setRaw(turbo_off); + EXPECT_EQ(kNeoclimaButtonTurbo, ac.getButton()); + EXPECT_FALSE(ac.getTurbo()); +} + +TEST(TestIRNeoclimaAcClass, Fresh) { + IRNeoclimaAc ac(0); + ac.begin(); + ac.setFresh(true); + EXPECT_TRUE(ac.getFresh()); + ac.setFresh(false); + EXPECT_FALSE(ac.getFresh()); + ac.setFresh(true); + EXPECT_TRUE(ac.getFresh()); + EXPECT_EQ(kNeoclimaButtonFresh, ac.getButton()); + // Data from: + // https://drive.google.com/file/d/1kjYk4zS9NQcMQhFkak-L4mp4UuaAIesW/view + uint8_t on[12] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x95, 0x00, 0x6A, 0x00, 0x29, 0xA5, 0xCD}; + uint8_t off[12] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x6A, 0x00, 0x29, 0xA5, 0x4D}; + ac.setRaw(on); + EXPECT_TRUE(ac.getFresh()); + EXPECT_EQ(kNeoclimaButtonFresh, ac.getButton()); + ac.setRaw(off); + EXPECT_EQ(kNeoclimaButtonFresh, ac.getButton()); + EXPECT_FALSE(ac.getFresh()); +} + +TEST(TestIRNeoclimaAcClass, Hold) { + IRNeoclimaAc ac(0); + ac.begin(); + ac.setHold(true); + EXPECT_TRUE(ac.getHold()); + ac.setHold(false); + EXPECT_FALSE(ac.getHold()); + ac.setHold(true); + EXPECT_TRUE(ac.getHold()); + EXPECT_EQ(kNeoclimaButtonHold, ac.getButton()); +} + +TEST(TestIRNeoclimaAcClass, 8CHeat) { + IRNeoclimaAc ac(0); + ac.begin(); + ac.set8CHeat(true); + EXPECT_TRUE(ac.get8CHeat()); + ac.set8CHeat(false); + EXPECT_FALSE(ac.get8CHeat()); + ac.set8CHeat(true); + EXPECT_TRUE(ac.get8CHeat()); + EXPECT_EQ(kNeoclimaButton8CHeat, ac.getButton()); +} + +TEST(TestIRNeoclimaAcClass, Light) { + IRNeoclimaAc ac(0); + ac.begin(); + ac.setLight(true); + EXPECT_TRUE(ac.getLight()); + ac.setLight(false); + EXPECT_FALSE(ac.getLight()); + ac.setLight(true); + EXPECT_TRUE(ac.getLight()); + EXPECT_EQ(kNeoclimaButtonLight, ac.getButton()); +} + +TEST(TestIRNeoclimaAcClass, Ion) { + IRNeoclimaAc ac(0); + ac.begin(); + ac.setIon(true); + EXPECT_TRUE(ac.getIon()); + ac.setIon(false); + EXPECT_FALSE(ac.getIon()); + ac.setIon(true); + EXPECT_TRUE(ac.getIon()); + EXPECT_EQ(kNeoclimaButtonIon, ac.getButton()); +} + +TEST(TestIRNeoclimaAcClass, Eye) { + IRNeoclimaAc ac(0); + ac.begin(); + ac.setEye(true); + EXPECT_TRUE(ac.getEye()); + ac.setEye(false); + EXPECT_FALSE(ac.getEye()); + ac.setEye(true); + EXPECT_TRUE(ac.getEye()); + EXPECT_EQ(kNeoclimaButtonEye, ac.getButton()); +} + +TEST(TestIRNeoclimaAcClass, Follow) { + IRNeoclimaAc ac(0); + ac.begin(); + /* DISABLED: See TODO in ir_Neoclima.cpp + ac.setFollow(true); + EXPECT_TRUE(ac.getFollow()); + ac.setFollow(false); + EXPECT_FALSE(ac.getFollow()); + ac.setFollow(true); + EXPECT_TRUE(ac.getFollow()); + EXPECT_EQ(kNeoclimaButtonFollow, ac.getButton()); + */ + uint8_t on_5F[12] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x0A, 0x5F, 0x89, 0xA5, 0xAA}; + uint8_t on_5D[12] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x6A, 0x5D, 0x29, 0xA5, 0xA8}; + uint8_t off[12] = { + 0x00, 0x04, 0x00, 0x40, 0x00, 0x13, 0x00, 0x6B, 0x00, 0x29, 0xA5, 0x90}; + ac.setRaw(on_5F); + EXPECT_TRUE(ac.getFollow()); + ac.setRaw(off); + EXPECT_FALSE(ac.getFollow()); + ac.setRaw(on_5D); + EXPECT_TRUE(ac.getFollow()); +} + +TEST(TestIRNeoclimaAcClass, ChecksumCalculation) { + uint8_t examplestate[kNeoclimaStateLength] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x6A, 0x00, 0x2A, 0xA5, 0x39}; + const uint8_t originalstate[kNeoclimaStateLength] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x6A, 0x00, 0x2A, 0xA5, 0x39}; + + EXPECT_TRUE(IRNeoclimaAc::validChecksum(examplestate)); + EXPECT_EQ(0x39, IRNeoclimaAc::calcChecksum(examplestate)); + + examplestate[11] = 0x12; // Set an incorrect checksum. + EXPECT_FALSE(IRNeoclimaAc::validChecksum(examplestate)); + EXPECT_EQ(0x39, IRNeoclimaAc::calcChecksum(examplestate)); + IRNeoclimaAc ac(0); + ac.setRaw(examplestate); + // Extracting the state from the object should have a correct checksum. + EXPECT_TRUE(IRNeoclimaAc::validChecksum(ac.getRaw())); + EXPECT_STATE_EQ(originalstate, ac.getRaw(), kNeoclimaBits); + examplestate[11] = 0x39; // Restore old checksum value. + + // Change the state to force a different checksum. + examplestate[8] = 0x01; + EXPECT_FALSE(IRNeoclimaAc::validChecksum(examplestate)); + EXPECT_EQ(0x3A, IRNeoclimaAc::calcChecksum(examplestate)); +} + +TEST(TestIRNeoclimaAcClass, toCommon) { + IRNeoclimaAc ac(0); + ac.setPower(true); + ac.setMode(kNeoclimaCool); + ac.setTemp(20); + ac.setFan(kNeoclimaFanHigh); + ac.setSwingV(true); + ac.setSwingH(true); + ac.setTurbo(false); + ac.setIon(true); + ac.setLight(true); + ac.setSleep(true); + // Now test it. + ASSERT_EQ(decode_type_t::NEOCLIMA, ac.toCommon().protocol); + ASSERT_EQ(-1, ac.toCommon().model); + ASSERT_TRUE(ac.toCommon().power); + ASSERT_TRUE(ac.toCommon().celsius); + ASSERT_EQ(20, ac.toCommon().degrees); + ASSERT_FALSE(ac.toCommon().turbo); + ASSERT_TRUE(ac.toCommon().filter); + ASSERT_TRUE(ac.toCommon().light); + ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode); + ASSERT_EQ(stdAc::fanspeed_t::kMax, ac.toCommon().fanspeed); + ASSERT_EQ(stdAc::swingv_t::kAuto, ac.toCommon().swingv); + ASSERT_EQ(stdAc::swingh_t::kAuto, ac.toCommon().swingh); + ASSERT_EQ(0, ac.toCommon().sleep); + // Unsupported. + ASSERT_FALSE(ac.toCommon().quiet); + ASSERT_FALSE(ac.toCommon().econo); + ASSERT_FALSE(ac.toCommon().clean); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_EQ(-1, ac.toCommon().clock); +} diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Nikai_test.cpp b/lib/IRremoteESP8266-2.6.5/test/ir_Nikai_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.6.0/test/ir_Nikai_test.cpp rename to lib/IRremoteESP8266-2.6.5/test/ir_Nikai_test.cpp diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Panasonic_test.cpp b/lib/IRremoteESP8266-2.6.5/test/ir_Panasonic_test.cpp similarity index 95% rename from lib/IRremoteESP8266-2.6.0/test/ir_Panasonic_test.cpp rename to lib/IRremoteESP8266-2.6.5/test/ir_Panasonic_test.cpp index 4d10f8fb1..7f84c2a8a 100644 --- a/lib/IRremoteESP8266-2.6.0/test/ir_Panasonic_test.cpp +++ b/lib/IRremoteESP8266-2.6.5/test/ir_Panasonic_test.cpp @@ -543,7 +543,7 @@ TEST(TestIRPanasonicAcClass, ChecksumCalculation) { EXPECT_TRUE(IRPanasonicAc::validChecksum(examplestate)); EXPECT_EQ(0x83, IRPanasonicAc::calcChecksum(examplestate)); - examplestate[kPanasonicAcStateLength - 1] = 0x0; // Set incoorect checksum. + examplestate[kPanasonicAcStateLength - 1] = 0x0; // Set incorrect checksum. EXPECT_FALSE(IRPanasonicAc::validChecksum(examplestate)); EXPECT_EQ(0x83, IRPanasonicAc::calcChecksum(examplestate)); pana.setRaw(examplestate); @@ -739,7 +739,7 @@ TEST(TestIRPanasonicAcClass, HumanReadable) { EXPECT_EQ( "Model: 4 (JKE), Power: Off, Mode: 0 (AUTO), Temp: 0C, " "Fan: 253 (UNKNOWN), Swing (Vertical): 0 (UNKNOWN), Quiet: Off, " - "Powerful: Off, Clock: 0:00, On Timer: Off, Off Timer: Off", + "Powerful: Off, Clock: 00:00, On Timer: Off, Off Timer: Off", pana.toString()); pana.setPower(true); pana.setTemp(kPanasonicAcMaxTemp); @@ -749,24 +749,24 @@ TEST(TestIRPanasonicAcClass, HumanReadable) { pana.setPowerful(true); EXPECT_EQ( "Model: 4 (JKE), Power: On, Mode: 4 (HEAT), Temp: 30C, " - "Fan: 4 (MAX), Swing (Vertical): 15 (AUTO), Quiet: Off, " - "Powerful: On, Clock: 0:00, On Timer: Off, Off Timer: Off", + "Fan: 4 (High), Swing (Vertical): 15 (AUTO), Quiet: Off, " + "Powerful: On, Clock: 00:00, On Timer: Off, Off Timer: Off", pana.toString()); pana.setQuiet(true); pana.setModel(kPanasonicLke); EXPECT_EQ( "Model: 1 (LKE), Power: Off, Mode: 4 (HEAT), Temp: 30C, " - "Fan: 4 (MAX), Swing (Vertical): 15 (AUTO), " + "Fan: 4 (High), Swing (Vertical): 15 (AUTO), " "Swing (Horizontal): 6 (Middle), Quiet: On, Powerful: Off, " - "Clock: 0:00, On Timer: 0:00, Off Timer: Off", + "Clock: 00:00, On Timer: 00:00, Off Timer: Off", pana.toString()); pana.setModel(kPanasonicDke); pana.setSwingHorizontal(kPanasonicAcSwingHRight); EXPECT_EQ( "Model: 3 (DKE), Power: Off, Mode: 4 (HEAT), Temp: 30C, " - "Fan: 4 (MAX), Swing (Vertical): 15 (AUTO), " + "Fan: 4 (High), Swing (Vertical): 15 (AUTO), " "Swing (Horizontal): 11 (Right), Quiet: On, Powerful: Off, " - "Clock: 0:00, On Timer: Off, Off Timer: Off", + "Clock: 00:00, On Timer: Off, Off Timer: Off", pana.toString()); } @@ -857,8 +857,8 @@ TEST(TestDecodePanasonicAC, SyntheticExample) { pana.setRaw(irsend.capture.state); EXPECT_EQ( "Model: 4 (JKE), Power: Off, Mode: 3 (COOL), Temp: 25C, " - "Fan: 7 (AUTO), Swing (Vertical): 15 (AUTO), Quiet: Off, " - "Powerful: Off, Clock: 0:00, On Timer: Off, Off Timer: Off", + "Fan: 7 (Auto), Swing (Vertical): 15 (AUTO), Quiet: Off, " + "Powerful: Off, Clock: 00:00, On Timer: Off, Off Timer: Off", pana.toString()); } @@ -936,9 +936,9 @@ TEST(TestDecodePanasonicAC, Issue540) { // TODO(crankyoldgit): Try to figure out what model this should be. EXPECT_EQ( "Model: 0 (UNKNOWN), Power: On, Mode: 3 (COOL), Temp: 26C, " - "Fan: 7 (AUTO), Swing (Vertical): 15 (AUTO), " + "Fan: 7 (Auto), Swing (Vertical): 15 (AUTO), " "Swing (Horizontal): 13 (AUTO), Quiet: Off, Powerful: Off, " - "Clock: 0:00, On Timer: Off, Off Timer: Off", + "Clock: 00:00, On Timer: Off, Off Timer: Off", pana.toString()); } @@ -948,20 +948,10 @@ TEST(TestIRPanasonicAcClass, TimeBasics) { EXPECT_EQ(0x448, IRPanasonicAc::encodeTime(18, 16)); EXPECT_EQ(0, IRPanasonicAc::encodeTime(0, 0)); EXPECT_EQ(kPanasonicAcTimeMax, IRPanasonicAc::encodeTime(23, 59)); - EXPECT_EQ("16:10", - IRPanasonicAc::timeToString(IRPanasonicAc::encodeTime(16, 10))); - EXPECT_EQ("6:30", - IRPanasonicAc::timeToString(IRPanasonicAc::encodeTime(6, 30))); - EXPECT_EQ("18:16", - IRPanasonicAc::timeToString(IRPanasonicAc::encodeTime(18, 16))); - EXPECT_EQ("1:01", - IRPanasonicAc::timeToString(IRPanasonicAc::encodeTime(1, 1))); EXPECT_EQ(kPanasonicAcTimeMax, IRPanasonicAc::encodeTime(23, 59)); EXPECT_EQ(kPanasonicAcTimeMax, IRPanasonicAc::encodeTime(25, 72)); EXPECT_EQ(59, IRPanasonicAc::encodeTime(0, 72)); EXPECT_EQ(23 * 60, IRPanasonicAc::encodeTime(27, 0)); - EXPECT_EQ("0:00", IRPanasonicAc::timeToString(0)); - EXPECT_EQ("23:59", IRPanasonicAc::timeToString(kPanasonicAcTimeMax)); } TEST(TestIRPanasonicAcClass, TimersAndClock) { @@ -1126,8 +1116,8 @@ TEST(TestDecodePanasonicAC, CkpModelSpecifics) { pana.setRaw(irsend.capture.state); EXPECT_EQ( "Model: 5 (CKP), Power: Off, Mode: 4 (HEAT), Temp: 23C, " - "Fan: 7 (AUTO), Swing (Vertical): 15 (AUTO), Quiet: Off, " - "Powerful: On, Clock: 0:00, On Timer: 0:00, Off Timer: 0:00", + "Fan: 7 (Auto), Swing (Vertical): 15 (AUTO), Quiet: Off, " + "Powerful: On, Clock: 00:00, On Timer: 00:00, Off Timer: 00:00", pana.toString()); pana.setQuiet(true); @@ -1142,3 +1132,36 @@ TEST(TestDecodePanasonicAC, CkpModelSpecifics) { EXPECT_EQ(kPanasonicCkp, pana.getModel()); EXPECT_STATE_EQ(ckpPowerfulOn, pana.getRaw(), kPanasonicAcBits); } + +TEST(TestIRPanasonicAcClass, toCommon) { + IRPanasonicAc ac(0); + ac.setModel(panasonic_ac_remote_model_t::kPanasonicDke); + ac.setPower(true); + ac.setMode(kPanasonicAcCool); + ac.setTemp(20); + ac.setFan(kPanasonicAcFanMax); + ac.setSwingVertical(kPanasonicAcSwingVAuto); + ac.setSwingHorizontal(kPanasonicAcSwingHMiddle); + ac.setPowerful(true); + ac.setQuiet(false); + // Now test it. + ASSERT_EQ(decode_type_t::PANASONIC_AC, ac.toCommon().protocol); + ASSERT_EQ(panasonic_ac_remote_model_t::kPanasonicDke, ac.toCommon().model); + ASSERT_TRUE(ac.toCommon().power); + ASSERT_TRUE(ac.toCommon().celsius); + ASSERT_EQ(20, ac.toCommon().degrees); + ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode); + ASSERT_EQ(stdAc::fanspeed_t::kMax, ac.toCommon().fanspeed); + ASSERT_EQ(stdAc::swingv_t::kAuto, ac.toCommon().swingv); + ASSERT_EQ(stdAc::swingh_t::kMiddle, ac.toCommon().swingh); + ASSERT_TRUE(ac.toCommon().turbo); + ASSERT_FALSE(ac.toCommon().quiet); + // Unsupported. + ASSERT_FALSE(ac.toCommon().econo); + ASSERT_FALSE(ac.toCommon().clean); + ASSERT_FALSE(ac.toCommon().light); + ASSERT_FALSE(ac.toCommon().filter); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_EQ(-1, ac.toCommon().sleep); + ASSERT_EQ(-1, ac.toCommon().clock); +} diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Pioneer_test.cpp b/lib/IRremoteESP8266-2.6.5/test/ir_Pioneer_test.cpp similarity index 60% rename from lib/IRremoteESP8266-2.6.0/test/ir_Pioneer_test.cpp rename to lib/IRremoteESP8266-2.6.5/test/ir_Pioneer_test.cpp index 36d61c706..37cb56fa2 100644 --- a/lib/IRremoteESP8266-2.6.0/test/ir_Pioneer_test.cpp +++ b/lib/IRremoteESP8266-2.6.5/test/ir_Pioneer_test.cpp @@ -13,35 +13,35 @@ TEST(TestSendPioneer, SendDataOnly) { irsend.begin(); irsend.sendPioneer(0); EXPECT_EQ( - "f38000d33" - "m8960s4480" - "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" - "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" - "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" - "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" - "m560s58240" - "m8960s4480" - "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" - "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" - "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" - "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" - "m560s58240", + "f40000d33" + "m8544s4272" + "m534s534m534s534m534s534m534s534m534s534m534s534m534s534m534s534" + "m534s534m534s534m534s534m534s534m534s534m534s534m534s534m534s534" + "m534s534m534s534m534s534m534s534m534s534m534s534m534s534m534s534" + "m534s534m534s534m534s534m534s534m534s534m534s534m534s534m534s534" + "m534s37380" + "m8544s4272" + "m534s534m534s534m534s534m534s534m534s534m534s534m534s534m534s534" + "m534s534m534s534m534s534m534s534m534s534m534s534m534s534m534s534" + "m534s534m534s534m534s534m534s534m534s534m534s534m534s534m534s534" + "m534s534m534s534m534s534m534s534m534s534m534s534m534s534m534s534" + "m534s37380", irsend.outputStr()); irsend.sendPioneer(0x55FF00AAAA00FF55); EXPECT_EQ( - "f38000d33" - "m8960s4480" - "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" - "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" - "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" - "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" - "m560s40320" - "m8960s4480" - "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" - "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" - "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" - "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" - "m560s40320", + "f40000d33" + "m8544s4272" + "m534s534m534s1602m534s534m534s1602m534s534m534s1602m534s534m534s1602" + "m534s1602m534s1602m534s1602m534s1602m534s1602m534s1602m534s1602m534s1602" + "m534s534m534s534m534s534m534s534m534s534m534s534m534s534m534s534" + "m534s1602m534s534m534s1602m534s534m534s1602m534s534m534s1602m534s534" + "m534s25098" + "m8544s4272" + "m534s1602m534s534m534s1602m534s534m534s1602m534s534m534s1602m534s534" + "m534s534m534s534m534s534m534s534m534s534m534s534m534s534m534s534" + "m534s1602m534s1602m534s1602m534s1602m534s1602m534s1602m534s1602m534s1602" + "m534s534m534s1602m534s534m534s1602m534s534m534s1602m534s534m534s1602" + "m534s25098", irsend.outputStr()); } @@ -61,7 +61,7 @@ TEST(TestEncodePioneer, SimpleEncoding) { EXPECT_EQ(0xA55A6A95F50A04FB, irsend.encodePioneer(0xA556, 0xAF20)); // "Source" from - // https://github.com/markszabo/IRremoteESP8266/pull/547#issuecomment-429616582 + // https://github.com/crankyoldgit/IRremoteESP8266/pull/547#issuecomment-429616582 EXPECT_EQ(0x659A05FAF50AC53A, irsend.encodePioneer(0xA6A0, 0xAFA3)); } @@ -92,7 +92,7 @@ TEST(TestDecodePioneer, RealExampleLongDecodeSourceButton) { irsend.reset(); // "Source" button. - // https://github.com/markszabo/IRremoteESP8266/pull/547#issuecomment-429616582 + // https://github.com/crankyoldgit/IRremoteESP8266/pull/547#issuecomment-429616582 uint16_t rawData[135] = { 8552, 4184, 596, 472, 592, 1524, 594, 1524, 594, 472, 592, 472, 598, 1520, 596, 472, 594, 1524, 592, 1524, 592, 472, 592, 472, @@ -119,7 +119,7 @@ TEST(TestDecodePioneer, RealExampleLongDecodeSourceButton) { // Synthetic Pioneer message. // For: -// https://github.com/markszabo/IRremoteESP8266/pull/547#issuecomment-430800734 +// https://github.com/crankyoldgit/IRremoteESP8266/pull/547#issuecomment-430800734 TEST(TestDecodePioneer, SyntheticPioneerMessage) { IRsendTest irsend(0); IRrecv irrecv(0); @@ -138,18 +138,18 @@ TEST(TestDecodePioneer, SyntheticPioneerMessage) { irsend.reset(); irsend.sendPioneer(0x659A857AF50A3DC2, 64, 0); EXPECT_EQ( - "f38000d33" - "m8960s4480" - "m560s560m560s1680m560s1680m560s560m560s560m560s1680m560s560m560s1680" - "m560s1680m560s560m560s560m560s1680m560s1680m560s560m560s1680m560s560" - "m560s1680m560s560m560s560m560s560m560s560m560s1680m560s560m560s1680" - "m560s560m560s1680m560s1680m560s1680m560s1680m560s560m560s1680m560s560" - "m560s40320" - "m8960s4480" - "m560s1680m560s1680m560s1680m560s1680m560s560m560s1680m560s560m560s1680" - "m560s560m560s560m560s560m560s560m560s1680m560s560m560s1680m560s560" - "m560s560m560s560m560s1680m560s1680m560s1680m560s1680m560s560m560s1680" - "m560s1680m560s1680m560s560m560s560m560s560m560s560m560s1680m560s560" - "m560s40320", + "f40000d33" + "m8544s4272" + "m534s534m534s1602m534s1602m534s534m534s534m534s1602m534s534m534s1602" + "m534s1602m534s534m534s534m534s1602m534s1602m534s534m534s1602m534s534" + "m534s1602m534s534m534s534m534s534m534s534m534s1602m534s534m534s1602" + "m534s534m534s1602m534s1602m534s1602m534s1602m534s534m534s1602m534s534" + "m534s25098" + "m8544s4272" + "m534s1602m534s1602m534s1602m534s1602m534s534m534s1602m534s534m534s1602" + "m534s534m534s534m534s534m534s534m534s1602m534s534m534s1602m534s534" + "m534s534m534s534m534s1602m534s1602m534s1602m534s1602m534s534m534s1602" + "m534s1602m534s1602m534s534m534s534m534s534m534s534m534s1602m534s534" + "m534s25098", irsend.outputStr()); } diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Pronto_test.cpp b/lib/IRremoteESP8266-2.6.5/test/ir_Pronto_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.6.0/test/ir_Pronto_test.cpp rename to lib/IRremoteESP8266-2.6.5/test/ir_Pronto_test.cpp diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_RC5_RC6_test.cpp b/lib/IRremoteESP8266-2.6.5/test/ir_RC5_RC6_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.6.0/test/ir_RC5_RC6_test.cpp rename to lib/IRremoteESP8266-2.6.5/test/ir_RC5_RC6_test.cpp diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_RCMM_test.cpp b/lib/IRremoteESP8266-2.6.5/test/ir_RCMM_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.6.0/test/ir_RCMM_test.cpp rename to lib/IRremoteESP8266-2.6.5/test/ir_RCMM_test.cpp diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Samsung_test.cpp b/lib/IRremoteESP8266-2.6.5/test/ir_Samsung_test.cpp similarity index 75% rename from lib/IRremoteESP8266-2.6.0/test/ir_Samsung_test.cpp rename to lib/IRremoteESP8266-2.6.5/test/ir_Samsung_test.cpp index 8670ac4ab..b928350b6 100644 --- a/lib/IRremoteESP8266-2.6.0/test/ir_Samsung_test.cpp +++ b/lib/IRremoteESP8266-2.6.5/test/ir_Samsung_test.cpp @@ -1,5 +1,6 @@ // Copyright 2017, 2018, 2019 David Conran +#include #include "ir_Samsung.h" #include "IRrecv.h" #include "IRrecv_test.h" @@ -414,7 +415,7 @@ TEST(TestIRSamsungAcClass, SetAndGetSwing) { EXPECT_TRUE(samsung.getSwing()); // Real examples from: - // https://github.com/markszabo/IRremoteESP8266/issues/505#issuecomment-424036602 + // https://github.com/crankyoldgit/IRremoteESP8266/issues/505#issuecomment-424036602 // TODO(Hollako): Explain why state[9] lowest bit changes between on and off. const uint8_t expected_off[kSamsungAcStateLength] = { 0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0, @@ -499,15 +500,87 @@ TEST(TestIRSamsungAcClass, SetAndGetFan) { } TEST(TestIRSamsungAcClass, SetAndGetQuiet) { - IRSamsungAc samsung(0); - samsung.setQuiet(false); - EXPECT_FALSE(samsung.getQuiet()); - samsung.setFan(kSamsungAcFanHigh); - samsung.setQuiet(true); - EXPECT_TRUE(samsung.getQuiet()); - EXPECT_EQ(kSamsungAcFanAuto, samsung.getFan()); - samsung.setQuiet(false); - EXPECT_FALSE(samsung.getQuiet()); + IRSamsungAc ac(0); + ac.setQuiet(false); + EXPECT_FALSE(ac.getQuiet()); + ac.setFan(kSamsungAcFanHigh); + ac.setQuiet(true); + EXPECT_TRUE(ac.getQuiet()); + EXPECT_EQ(kSamsungAcFanAuto, ac.getFan()); + ac.setQuiet(false); + EXPECT_FALSE(ac.getQuiet()); + + // Actual quiet on & off states from: + // https://github.com/crankyoldgit/IRremoteESP8266/issues/734#issuecomment-500071419 + uint8_t on[14] = { + 0x02, 0x82, 0x0F, 0x00, 0x00, 0x20, 0xF0, + 0x01, 0xF2, 0xFE, 0x71, 0x00, 0x11, 0xF0}; + ac.setRaw(on, 14); + EXPECT_TRUE(ac.getQuiet()); + EXPECT_EQ(kSamsungAcFanAuto, ac.getFan()); + uint8_t off[14] = { + 0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0, + 0x01, 0xF2, 0xFE, 0x71, 0x00, 0x11, 0xF0}; + ac.setRaw(off, 14); + EXPECT_FALSE(ac.getQuiet()); +} + + +TEST(TestIRSamsungAcClass, SetAndGetPowerful) { + IRSamsungAc ac(0); + ac.setFan(kSamsungAcFanMed); + ac.setPowerful(false); + EXPECT_FALSE(ac.getPowerful()); + EXPECT_EQ(kSamsungAcFanMed, ac.getFan()); + ac.setPowerful(true); + EXPECT_TRUE(ac.getPowerful()); + EXPECT_EQ(kSamsungAcFanTurbo, ac.getFan()); + ac.setPowerful(false); + EXPECT_FALSE(ac.getPowerful()); + EXPECT_EQ(kSamsungAcFanAuto, ac.getFan()); + + // Actual powerful on & off states from: + // https://github.com/crankyoldgit/IRremoteESP8266/issues/734#issuecomment-500120270 + uint8_t on[kSamsungAcStateLength] = { + 0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0, + 0x01, 0xA2, 0xFE, 0x77, 0x00, 0x1F, 0xF0}; + ac.setRaw(on, kSamsungAcStateLength); + EXPECT_TRUE(ac.getPowerful()); + EXPECT_EQ(kSamsungAcFanTurbo, ac.getFan()); + EXPECT_EQ( + "Power: On, Mode: 1 (COOL), Temp: 16C, Fan: 7 (Turbo), Swing: Off, " + "Beep: Off, Clean: Off, Quiet: Off, Powerful: On", ac.toString()); + + uint8_t off[kSamsungAcStateLength] = { + 0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0, + 0x01, 0xF2, 0xFE, 0x71, 0x00, 0x11, 0xF0}; + ac.setRaw(off, kSamsungAcStateLength); + EXPECT_FALSE(ac.getPowerful()); + EXPECT_NE(kSamsungAcFanTurbo, ac.getFan()); + EXPECT_EQ( + "Power: On, Mode: 1 (COOL), Temp: 16C, Fan: 0 (Auto), Swing: Off, " + "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off", ac.toString()); +} + +TEST(TestIRSamsungAcClass, QuietAndPowerfulAreMutuallyExclusive) { + IRSamsungAc ac(0); + ac.setQuiet(false); + ac.setPowerful(false); + EXPECT_FALSE(ac.getQuiet()); + EXPECT_FALSE(ac.getPowerful()); + EXPECT_NE(kSamsungAcFanTurbo, ac.getFan()); + ac.setQuiet(true); + EXPECT_TRUE(ac.getQuiet()); + EXPECT_FALSE(ac.getPowerful()); + EXPECT_EQ(kSamsungAcFanAuto, ac.getFan()); + ac.setPowerful(true); + EXPECT_FALSE(ac.getQuiet()); + EXPECT_TRUE(ac.getPowerful()); + EXPECT_EQ(kSamsungAcFanTurbo, ac.getFan()); + ac.setQuiet(true); + EXPECT_TRUE(ac.getQuiet()); + EXPECT_FALSE(ac.getPowerful()); + EXPECT_NE(kSamsungAcFanTurbo, ac.getFan()); } TEST(TestIRSamsungAcClass, ChecksumCalculation) { @@ -529,7 +602,7 @@ TEST(TestIRSamsungAcClass, ChecksumCalculation) { EXPECT_TRUE(IRSamsungAc::validChecksum(examplestate)); EXPECT_EQ(0, IRSamsungAc::calcChecksum(examplestate)); - examplestate[8] = 0x12; // Set an incoorect checksum. + examplestate[8] = 0x12; // Set an incorrect checksum. EXPECT_FALSE(IRSamsungAc::validChecksum(examplestate)); EXPECT_EQ(0, IRSamsungAc::calcChecksum(examplestate)); samsung.setRaw(examplestate); @@ -550,8 +623,8 @@ TEST(TestIRSamsungAcClass, ChecksumCalculation) { TEST(TestIRSamsungAcClass, HumanReadable) { IRSamsungAc samsung(0); EXPECT_EQ( - "Power: On, Mode: 1 (COOL), Temp: 16C, Fan: 2 (LOW), Swing: On, " - "Beep: Off, Clean: Off, Quiet: Off", + "Power: On, Mode: 1 (COOL), Temp: 16C, Fan: 2 (Low), Swing: On, " + "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off", samsung.toString()); samsung.setTemp(kSamsungAcMaxTemp); samsung.setMode(kSamsungAcHeat); @@ -561,13 +634,19 @@ TEST(TestIRSamsungAcClass, HumanReadable) { samsung.setBeep(true); samsung.setClean(true); EXPECT_EQ( - "Power: Off, Mode: 4 (HEAT), Temp: 30C, Fan: 5 (HIGH), Swing: Off, " - "Beep: On, Clean: On, Quiet: Off", + "Power: Off, Mode: 4 (HEAT), Temp: 30C, Fan: 5 (High), Swing: Off, " + "Beep: On, Clean: On, Quiet: Off, Powerful: Off", samsung.toString()); samsung.setQuiet(true); EXPECT_EQ( - "Power: Off, Mode: 4 (HEAT), Temp: 30C, Fan: 0 (AUTO), Swing: Off, " - "Beep: On, Clean: On, Quiet: On", + "Power: Off, Mode: 4 (HEAT), Temp: 30C, Fan: 0 (Auto), Swing: Off, " + "Beep: On, Clean: On, Quiet: On, Powerful: Off", + samsung.toString()); + samsung.setQuiet(false); + samsung.setPowerful(true); + EXPECT_EQ( + "Power: Off, Mode: 4 (HEAT), Temp: 30C, Fan: 7 (Turbo), Swing: Off, " + "Beep: On, Clean: On, Quiet: Off, Powerful: On", samsung.toString()); } @@ -670,8 +749,8 @@ TEST(TestDecodeSamsungAC, DecodeRealExample) { IRSamsungAc samsung(0); samsung.setRaw(irsend.capture.state); EXPECT_EQ( - "Power: On, Mode: 1 (COOL), Temp: 16C, Fan: 2 (LOW), Swing: On, " - "Beep: Off, Clean: Off, Quiet: Off", + "Power: On, Mode: 1 (COOL), Temp: 16C, Fan: 2 (Low), Swing: On, " + "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off", samsung.toString()); } @@ -719,13 +798,13 @@ TEST(TestDecodeSamsungAC, DecodeRealExample2) { IRSamsungAc samsung(0); samsung.setRaw(irsend.capture.state); EXPECT_EQ( - "Power: On, Mode: 1 (COOL), Temp: 24C, Fan: 0 (AUTO), Swing: Off, " - "Beep: Off, Clean: Off, Quiet: Off", + "Power: On, Mode: 1 (COOL), Temp: 24C, Fan: 0 (Auto), Swing: Off, " + "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off", samsung.toString()); } // Decode a real Samsung A/C example from: -// https://github.com/markszabo/IRremoteESP8266/issues/505#issuecomment-424036602 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/505#issuecomment-424036602 TEST(TestDecodeSamsungAC, DecodePowerOnSample) { IRsendTest irsend(0); IRrecv irrecv(0); @@ -778,13 +857,13 @@ TEST(TestDecodeSamsungAC, DecodePowerOnSample) { IRSamsungAc samsung(0); samsung.setRaw(irsend.capture.state, kSamsungAcExtendedStateLength); EXPECT_EQ( - "Power: On, Mode: 1 (COOL), Temp: 24C, Fan: 0 (AUTO), Swing: Off, " - "Beep: Off, Clean: Off, Quiet: Off", + "Power: On, Mode: 1 (COOL), Temp: 24C, Fan: 0 (Auto), Swing: Off, " + "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off", samsung.toString()); } // Decode a real Samsung A/C example from: -// https://github.com/markszabo/IRremoteESP8266/issues/505#issuecomment-424036602 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/505#issuecomment-424036602 TEST(TestDecodeSamsungAC, DecodePowerOffSample) { IRsendTest irsend(0); IRrecv irrecv(0); @@ -838,8 +917,8 @@ TEST(TestDecodeSamsungAC, DecodePowerOffSample) { IRSamsungAc samsung(0); samsung.setRaw(irsend.capture.state, kSamsungAcExtendedStateLength); EXPECT_EQ( - "Power: Off, Mode: 1 (COOL), Temp: 24C, Fan: 0 (AUTO), Swing: Off, " - "Beep: Off, Clean: Off, Quiet: Off", + "Power: Off, Mode: 1 (COOL), Temp: 24C, Fan: 0 (Auto), Swing: Off, " + "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off", samsung.toString()); } @@ -885,8 +964,8 @@ TEST(TestDecodeSamsungAC, DecodeHeatSample) { IRSamsungAc samsung(0); samsung.setRaw(irsend.capture.state); EXPECT_EQ( - "Power: On, Mode: 4 (HEAT), Temp: 17C, Fan: 0 (AUTO), Swing: On, " - "Beep: Off, Clean: Off, Quiet: Off", + "Power: On, Mode: 4 (HEAT), Temp: 17C, Fan: 0 (Auto), Swing: On, " + "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off", samsung.toString()); } @@ -932,8 +1011,8 @@ TEST(TestDecodeSamsungAC, DecodeCoolSample) { IRSamsungAc samsung(0); samsung.setRaw(irsend.capture.state); EXPECT_EQ( - "Power: On, Mode: 1 (COOL), Temp: 20C, Fan: 0 (AUTO), Swing: Off, " - "Beep: Off, Clean: Off, Quiet: Off", + "Power: On, Mode: 1 (COOL), Temp: 20C, Fan: 0 (Auto), Swing: Off, " + "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off", samsung.toString()); } @@ -990,8 +1069,8 @@ TEST(TestDecodeSamsungAC, Issue604DecodeExtended) { IRSamsungAc samsung(0); samsung.setRaw(irsend.capture.state, irsend.capture.bits / 8); EXPECT_EQ( - "Power: Off, Mode: 4 (HEAT), Temp: 30C, Fan: 0 (AUTO), Swing: Off, " - "Beep: Off, Clean: Off, Quiet: Off", + "Power: Off, Mode: 4 (HEAT), Temp: 30C, Fan: 0 (Auto), Swing: Off, " + "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off", samsung.toString()); } @@ -1128,3 +1207,239 @@ TEST(TestDecodeSamsung36, SyntheticExample) { EXPECT_EQ(0xE00FF, irsend.capture.command); EXPECT_EQ(0x400, irsend.capture.address); } + +// https://github.com/crankyoldgit/IRremoteESP8266/issues/604 +TEST(TestIRSamsungAcClass, Issue604SendPowerHack) { + IRSamsungAc ac(0); + ac.begin(); + + std::string freqduty = "f38000d50"; + + std::string poweron = + "m690s17844" + "m3086s8864" + "m586s436m586s1432m586s436m586s436m586s436m586s436m586s436m586s436" + "m586s436m586s1432m586s436m586s436m586s1432m586s436m586s436m586s1432" + "m586s1432m586s1432m586s1432m586s1432m586s436m586s436m586s436m586s436" + "m586s436m586s436m586s436m586s436m586s436m586s436m586s436m586s436" + "m586s436m586s436m586s436m586s436m586s436m586s436m586s436m586s436" + "m586s436m586s436m586s436m586s436m586s436m586s436m586s436m586s436" + "m586s436m586s436m586s436m586s436m586s1432m586s1432m586s1432m586s1432" + "m586s2886" + "m3086s8864" + "m586s1432m586s436m586s436m586s436m586s436m586s436m586s436m586s436" + "m586s436m586s1432m586s436m586s436m586s1432m586s436m586s1432m586s1432" + "m586s1432m586s1432m586s1432m586s1432m586s436m586s436m586s436m586s436" + "m586s436m586s436m586s436m586s436m586s436m586s436m586s436m586s436" + "m586s436m586s436m586s436m586s436m586s436m586s436m586s436m586s436" + "m586s436m586s436m586s436m586s436m586s436m586s436m586s436m586s436" + "m586s436m586s436m586s436m586s436m586s436m586s436m586s436m586s436" + "m586s2886" + "m3086s8864" + "m586s1432m586s436m586s436m586s436m586s436m586s436m586s436m586s436" + "m586s436m586s1432m586s436m586s436m586s436m586s1432m586s1432m586s1432" + "m586s436m586s1432m586s1432m586s1432m586s1432m586s1432m586s1432m586s1432" + "m586s1432m586s436m586s436m586s436m586s1432m586s1432m586s1432m586s436" + "m586s436m586s436m586s436m586s436m586s436m586s436m586s436m586s1432" + "m586s1432m586s436m586s436m586s436m586s1432m586s436m586s436m586s436" + "m586s436m586s436m586s436m586s436m586s1432m586s1432m586s1432m586s1432" + "m586s100000"; + std::string settings = + "m690s17844" + "m3086s8864" + "m586s436m586s1432m586s436m586s436m586s436m586s436m586s436m586s436" + "m586s436m586s1432m586s436m586s436m586s1432m586s436m586s436m586s1432" + "m586s1432m586s1432m586s1432m586s1432m586s436m586s436m586s436m586s436" + "m586s436m586s436m586s436m586s436m586s436m586s436m586s436m586s436" + "m586s436m586s436m586s436m586s436m586s436m586s436m586s436m586s436" + "m586s436m586s436m586s436m586s436m586s436m586s436m586s436m586s436" + "m586s436m586s436m586s436m586s436m586s1432m586s1432m586s1432m586s1432" + "m586s2886" + "m3086s8864" + "m586s1432m586s436m586s436m586s436m586s436m586s436m586s436m586s436" + "m586s436m586s1432m586s436m586s436m586s1432m586s436m586s1432m586s1432" + "m586s436m586s1432m586s1432m586s1432m586s436m586s1432m586s436m586s1432" + "m586s1432m586s436m586s436m586s436m586s1432m586s1432m586s1432m586s436" + "m586s436m586s436m586s436m586s436m586s1432m586s1432m586s1432m586s436" + "m586s1432m586s436m586s436m586s1432m586s1432m586s436m586s436m586s436" + "m586s436m586s436m586s436m586s436m586s1432m586s1432m586s1432m586s1432" + "m586s100000"; + std::string text = "Power: On, Mode: 1 (COOL), Temp: 23C, Fan: 4 (Medium), " + "Swing: On, Beep: Off, Clean: Off, Quiet: Off, " + "Powerful: Off"; + // Don't do a setPower()/on()/off() as that will trigger the special message. + // So it should only be the normal "settings" message. + ac.setTemp(23); + ac.setMode(kSamsungAcCool); + ac.setFan(kSamsungAcFanMed); + ac.send(); + EXPECT_EQ(text, ac.toString()); + EXPECT_EQ(freqduty + settings, ac._irsend.outputStr()); + ac._irsend.reset(); + // Now trigger a special power message by using a power method. + ac.on(); + ac.send(); // This should result in two messages. 1 x extended + 1 x normal. + EXPECT_EQ(text, ac.toString()); + EXPECT_EQ(freqduty + poweron + settings, ac._irsend.outputStr()); + ac._irsend.reset(); + // Subsequent sending should be just the "settings" message. + ac.send(); + EXPECT_EQ(text, ac.toString()); + EXPECT_EQ(freqduty + settings, ac._irsend.outputStr()); + ac._irsend.reset(); + // Now trigger a special power message by using a power method (again). + ac.setPower(true); + ac.send(); // This should result in two messages. 1 x extended + 1 x normal. + EXPECT_EQ(text, ac.toString()); + EXPECT_EQ(freqduty + poweron + settings, ac._irsend.outputStr()); +} + +TEST(TestIRSamsungAcClass, toCommon) { + IRSamsungAc ac(0); + ac.setPower(true); + ac.setMode(kSamsungAcCool); + ac.setTemp(20); + ac.setFan(kSamsungAcFanAuto); + ac.setSwing(true); + ac.setBeep(true); + ac.setClean(true); + ac.setQuiet(true); + // Now test it. + ASSERT_EQ(decode_type_t::SAMSUNG_AC, ac.toCommon().protocol); + ASSERT_EQ(-1, ac.toCommon().model); + ASSERT_TRUE(ac.toCommon().power); + ASSERT_TRUE(ac.toCommon().celsius); + ASSERT_EQ(20, ac.toCommon().degrees); + ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode); + ASSERT_EQ(stdAc::fanspeed_t::kAuto, ac.toCommon().fanspeed); + ASSERT_EQ(stdAc::swingv_t::kAuto, ac.toCommon().swingv); + ASSERT_FALSE(ac.toCommon().turbo); + ASSERT_TRUE(ac.toCommon().quiet); + ASSERT_TRUE(ac.toCommon().clean); + ASSERT_TRUE(ac.toCommon().beep); + // Unsupported. + ASSERT_EQ(stdAc::swingh_t::kOff, ac.toCommon().swingh); + ASSERT_FALSE(ac.toCommon().econo); + ASSERT_FALSE(ac.toCommon().light); + ASSERT_FALSE(ac.toCommon().filter); + ASSERT_EQ(-1, ac.toCommon().sleep); + ASSERT_EQ(-1, ac.toCommon().clock); +} + +TEST(TestDecodeSamsungAC, Issue734QuietSetting) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + irsend.reset(); + // QUIET MODE ON data from: + // https://github.com/crankyoldgit/IRremoteESP8266/issues/734#issuecomment-499791618 + uint16_t quietOn[233] = { + 624, 17360, 3076, 8902, 520, 476, 520, 1472, 520, 476, 520, 474, 520, 476, + 520, 476, 520, 474, 522, 476, 520, 478, 518, 1476, 516, 500, 494, 502, + 548, 448, 546, 450, 544, 452, 522, 1468, 520, 1474, 520, 1472, 520, 1472, + 520, 1472, 520, 476, 520, 476, 518, 478, 516, 480, 516, 500, 496, 500, + 494, 502, 550, 446, 546, 450, 544, 452, 524, 472, 522, 474, 518, 476, 520, + 476, 520, 474, 522, 474, 520, 474, 520, 476, 520, 474, 520, 476, 518, 478, + 518, 480, 516, 480, 516, 502, 494, 502, 548, 1444, 524, 472, 522, 472, + 520, 474, 518, 478, 518, 476, 520, 476, 520, 1472, 520, 1470, 520, 1472, + 520, 1474, 516, 2980, 2998, 8980, 498, 1498, 548, 448, 526, 470, 544, 452, + 524, 472, 520, 474, 520, 476, 520, 476, 520, 476, 520, 1472, 520, 474, + 520, 476, 520, 1474, 518, 1476, 516, 1496, 496, 1498, 548, 446, 546, 1446, + 524, 1468, 518, 1474, 520, 1472, 520, 1472, 520, 1472, 520, 1474, 518, + 1476, 518, 480, 516, 500, 496, 528, 520, 1446, 544, 1446, 524, 1470, 518, + 476, 520, 476, 520, 474, 520, 476, 520, 474, 520, 476, 520, 474, 520, 476, + 520, 476, 518, 1476, 516, 482, 514, 502, 548, 448, 548, 1442, 544, 452, + 522, 474, 518, 476, 518, 476, 520, 476, 520, 474, 520, 476, 520, 1472, + 520, 1470, 522, 1474, 518, 1476, 536}; + + uint8_t expectedState[kSamsungAcStateLength] = { + 0x02, 0x82, 0x0F, 0x00, 0x00, 0x20, 0xF0, + 0x01, 0xF2, 0xFE, 0x71, 0x00, 0x11, 0xF0}; + + irsend.sendRaw(quietOn, 233, 38000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(SAMSUNG_AC, irsend.capture.decode_type); + EXPECT_EQ(kSamsungAcBits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + + IRSamsungAc ac(0); + ac.setRaw(irsend.capture.state, irsend.capture.bits / 8); + EXPECT_EQ( + "Power: On, Mode: 1 (COOL), Temp: 16C, Fan: 0 (Auto), Swing: Off, " + "Beep: Off, Clean: Off, Quiet: On, Powerful: Off", + ac.toString()); + + // Make sure the ac class state is in something wildly different first. + ac.stateReset(); + ac.setPower(false); + ac.setMode(kSamsungAcAuto); + ac.setTemp(30); + ac.setSwing(true); + ac.setBeep(true); + ac.setClean(true); + ac.setQuiet(false); + // See if we can build the state from scratch. + ac.setPower(true); + ac.setMode(kSamsungAcCool); + ac.setTemp(16); + ac.setSwing(false); + ac.setBeep(false); + ac.setClean(false); + ac.setQuiet(true); + EXPECT_EQ( + "Power: On, Mode: 1 (COOL), Temp: 16C, Fan: 0 (Auto), Swing: Off, " + "Beep: Off, Clean: Off, Quiet: On, Powerful: Off", + ac.toString()); + // Check it matches the known good/expected state. + EXPECT_STATE_EQ(expectedState, ac.getRaw(), kSamsungAcBits); +} + +TEST(TestDecodeSamsungAC, Issue734PowerfulOff) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + irsend.reset(); + // 1st Powerful off data from: + // https://github.com/crankyoldgit/IRremoteESP8266/issues/734#issuecomment-500114580 + uint16_t powerfulOff[233] = { + 652, 17336, 3078, 8910, 562, 456, 546, 1448, 550, 446, 552, 444, 552, 444, + 550, 446, 550, 446, 552, 446, 550, 446, 552, 1440, 550, 446, 550, 446, + 550, 1470, 478, 518, 502, 492, 536, 1458, 542, 1450, 552, 1440, 552, 1442, + 552, 1442, 550, 446, 550, 446, 550, 446, 552, 444, 550, 446, 550, 446, + 550, 472, 524, 472, 480, 516, 510, 488, 538, 458, 542, 452, 548, 448, 550, + 446, 550, 446, 550, 444, 552, 444, 552, 444, 552, 444, 552, 444, 552, 444, + 552, 444, 550, 446, 550, 446, 550, 472, 524, 472, 482, 514, 510, 486, 536, + 460, 542, 454, 546, 450, 550, 446, 552, 1442, 552, 1442, 550, 1442, 552, + 1440, 508, 2994, 3030, 8932, 552, 1638, 450, // <= (was 356) + // Above hack due to poor data. + 470, 526, 470, 506, 492, 510, + 486, 542, 454, 544, 450, 550, 446, 554, 444, 550, 1442, 550, 444, 550, + 446, 550, 1442, 552, 1440, 550, 1442, 550, 1470, 524, 470, 480, 1512, 512, + 1480, 546, 1448, 550, 1442, 552, 1442, 552, 1442, 550, 1440, 552, 1440, + 552, 446, 550, 444, 552, 444, 550, 1468, 484, 1510, 512, 1482, 544, 452, + 550, 446, 552, 442, 554, 444, 552, 444, 554, 442, 554, 442, 552, 444, 554, + 442, 554, 1440, 552, 444, 554, 442, 554, 468, 528, 1466, 508, 488, 512, + 484, 544, 450, 550, 446, 554, 442, 556, 442, 554, 442, 554, 1438, 554, + 1438, 554, 1438, 554, 1438, 562}; // UNKNOWN 7B551B62}; + + uint8_t expectedState[kSamsungAcStateLength] = { + 0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0, + 0x01, 0xF2, 0xFE, 0x71, 0x00, 0x11, 0xF0}; + + irsend.sendRaw(powerfulOff, 233, 38000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decodeSamsungAC(&irsend.capture)); + ASSERT_EQ(SAMSUNG_AC, irsend.capture.decode_type); + EXPECT_EQ(kSamsungAcBits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + + IRSamsungAc ac(0); + ac.setRaw(irsend.capture.state, irsend.capture.bits / 8); + EXPECT_EQ( + "Power: On, Mode: 1 (COOL), Temp: 16C, Fan: 0 (Auto), Swing: Off, " + "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off", + ac.toString()); +} diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Sanyo_test.cpp b/lib/IRremoteESP8266-2.6.5/test/ir_Sanyo_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.6.0/test/ir_Sanyo_test.cpp rename to lib/IRremoteESP8266-2.6.5/test/ir_Sanyo_test.cpp diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Sharp_test.cpp b/lib/IRremoteESP8266-2.6.5/test/ir_Sharp_test.cpp similarity index 52% rename from lib/IRremoteESP8266-2.6.0/test/ir_Sharp_test.cpp rename to lib/IRremoteESP8266-2.6.5/test/ir_Sharp_test.cpp index c9d3e851b..2949580ed 100644 --- a/lib/IRremoteESP8266-2.6.0/test/ir_Sharp_test.cpp +++ b/lib/IRremoteESP8266-2.6.5/test/ir_Sharp_test.cpp @@ -1,5 +1,8 @@ // Copyright 2017 David Conran +#include "ir_Sharp.h" +#include "IRrecv.h" +#include "IRrecv_test.h" #include "IRsend.h" #include "IRsend_test.h" #include "gtest/gtest.h" @@ -360,3 +363,345 @@ TEST(TestDecodeSharp, FailToDecodeNonSharpExample) { ASSERT_FALSE(irrecv.decodeSharp(&irsend.capture)); ASSERT_FALSE(irrecv.decodeSharp(&irsend.capture, kSharpBits, false)); } + +// https://github.com/crankyoldgit/IRremoteESP8266/issues/638#issue-421064165 +TEST(TestDecodeSharpAc, RealExample) { + IRsendTest irsend(0); + IRrecv irrecv(0); + // cool-auto-27.txt + uint16_t rawData[211] = { + 3804, 1892, 466, 486, 466, 1388, 466, 486, 466, 1386, 468, 486, 468, 1388, + 466, 486, 466, 1386, 468, 488, 466, 1388, 466, 488, 466, 1386, 468, 1388, + 466, 486, 466, 1388, 466, 486, 468, 1384, 468, 1388, 468, 1388, 466, 1388, + 466, 486, 468, 484, 468, 1386, 468, 1386, 468, 486, 466, 486, 468, 486, + 466, 488, 466, 1388, 466, 486, 466, 486, 468, 486, 466, 488, 466, 488, + 466, 1386, 468, 1388, 466, 486, 468, 486, 466, 1388, 464, 1388, 466, 1386, + 468, 486, 466, 486, 468, 486, 466, 1388, 468, 1384, 470, 486, 466, 486, + 468, 486, 468, 1386, 468, 486, 468, 486, 468, 486, 468, 1388, 466, 486, + 466, 486, 466, 486, 466, 488, 466, 486, 468, 486, 468, 486, 468, 486, 466, + 486, 466, 486, 466, 488, 466, 486, 466, 486, 466, 1388, 466, 486, 468, + 486, 466, 486, 468, 486, 468, 486, 466, 486, 466, 488, 466, 486, 466, 486, + 466, 488, 466, 486, 468, 1386, 468, 486, 466, 486, 466, 1390, 464, 488, + 466, 486, 468, 486, 468, 486, 466, 486, 466, 486, 466, 486, 468, 486, 468, + 486, 466, 486, 466, 1386, 468, 1390, 466, 1388, 466, 1388, 468, 486, 466, + 486, 468, 486, 466, 486, 466, 486, 466, 1390, 464, 486, 414}; + // UNKNOWN F2B82C78 + uint8_t expectedState[kSharpAcStateLength] = { + 0xAA, 0x5A, 0xCF, 0x10, 0xCC, 0x31, 0x22, 0x00, 0x08, 0x80, 0x04, 0xE0, + 0x41}; + + irsend.begin(); + irsend.reset(); + irsend.sendRaw(rawData, 211, 38000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(SHARP_AC, irsend.capture.decode_type); + ASSERT_EQ(kSharpAcBits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + + IRSharpAc ac(0); + ac.begin(); + ac.setRaw(irsend.capture.state); + EXPECT_EQ("Power: On, Mode: 2 (COOL), Temp: 27C, Fan: 2 (Auto)", + ac.toString()); +} + +// https://github.com/crankyoldgit/IRremoteESP8266/issues/638#issue-421064165 +TEST(TestDecodeSharpAc, SyntheticExample) { + IRsendTest irsend(0); + IRrecv irrecv(0); + // cool-auto-27.txt + uint8_t expectedState[kSharpAcStateLength] = { + 0xAA, 0x5A, 0xCF, 0x10, 0xCC, 0x31, 0x22, 0x00, 0x08, 0x80, 0x04, 0xE0, + 0x41}; + + irsend.begin(); + irsend.reset(); + irsend.sendSharpAc(expectedState); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(SHARP_AC, irsend.capture.decode_type); + ASSERT_EQ(kSharpAcBits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); +} + +TEST(TestIRUtils, Sharp) { + ASSERT_EQ("SHARP", typeToString(decode_type_t::SHARP)); + ASSERT_EQ(decode_type_t::SHARP, strToDecodeType("SHARP")); + ASSERT_FALSE(hasACState(decode_type_t::SHARP)); +} + +TEST(TestIRUtils, SharpAc) { + ASSERT_EQ("SHARP_AC", typeToString(decode_type_t::SHARP_AC)); + ASSERT_EQ(decode_type_t::SHARP_AC, strToDecodeType("SHARP_AC")); + ASSERT_TRUE(hasACState(decode_type_t::SHARP_AC)); +} + +// Tests for IRSharpAc class. + +TEST(TestSharpAcClass, Power) { + IRSharpAc ac(0); + ac.begin(); + + ac.on(); + EXPECT_TRUE(ac.getPower()); + + ac.off(); + EXPECT_FALSE(ac.getPower()); + + ac.setPower(true); + EXPECT_TRUE(ac.getPower()); + + ac.setPower(false); + EXPECT_FALSE(ac.getPower()); +} + +TEST(TestSharpAcClass, Checksum) { + uint8_t state[kSharpAcStateLength] = { + 0xAA, 0x5A, 0xCF, 0x10, 0xCC, 0x31, 0x22, 0x00, 0x08, 0x80, 0x04, 0xE0, + 0x41}; + EXPECT_EQ(0x4, IRSharpAc::calcChecksum(state)); + EXPECT_TRUE(IRSharpAc::validChecksum(state)); + // Change the state so it is not valid. + state[3] = 0; + EXPECT_FALSE(IRSharpAc::validChecksum(state)); +} + +TEST(TestSharpAcClass, Temperature) { + IRSharpAc ac(0); + ac.begin(); + ac.setMode(kSharpAcCool); // Cool mode doesn't have temp restrictions. + + ac.setTemp(0); + EXPECT_EQ(kSharpAcMinTemp, ac.getTemp()); + + ac.setTemp(255); + EXPECT_EQ(kSharpAcMaxTemp, ac.getTemp()); + + ac.setTemp(kSharpAcMinTemp); + EXPECT_EQ(kSharpAcMinTemp, ac.getTemp()); + + ac.setTemp(kSharpAcMaxTemp); + EXPECT_EQ(kSharpAcMaxTemp, ac.getTemp()); + + ac.setTemp(kSharpAcMinTemp - 1); + EXPECT_EQ(kSharpAcMinTemp, ac.getTemp()); + + ac.setTemp(kSharpAcMaxTemp + 1); + EXPECT_EQ(kSharpAcMaxTemp, ac.getTemp()); + + ac.setTemp(kSharpAcMinTemp + 1); + EXPECT_EQ(kSharpAcMinTemp + 1, ac.getTemp()); + + ac.setTemp(21); + EXPECT_EQ(21, ac.getTemp()); + + ac.setTemp(25); + EXPECT_EQ(25, ac.getTemp()); + + ac.setTemp(29); + EXPECT_EQ(29, ac.getTemp()); +} + +TEST(TestSharpAcClass, OperatingMode) { + IRSharpAc ac(0); + ac.begin(); + + ac.setTemp(25); + ac.setMode(kSharpAcAuto); + EXPECT_EQ(kSharpAcAuto, ac.getMode()); + + ac.setMode(kSharpAcCool); + EXPECT_EQ(kSharpAcCool, ac.getMode()); + + ac.setMode(kSharpAcHeat); + EXPECT_EQ(kSharpAcHeat, ac.getMode()); + + ac.setMode(kSharpAcDry); + EXPECT_EQ(kSharpAcDry, ac.getMode()); + ASSERT_EQ(kSharpAcMinTemp, ac.getTemp()); // Dry mode restricts the temp. + ac.setTemp(25); + ASSERT_EQ(kSharpAcMinTemp, ac.getTemp()); + + ac.setMode(kSharpAcDry + 1); + EXPECT_EQ(kSharpAcAuto, ac.getMode()); + + ac.setMode(kSharpAcCool); + EXPECT_EQ(kSharpAcCool, ac.getMode()); + // We are no longer restricted. + ac.setTemp(25); + ASSERT_EQ(25, ac.getTemp()); + + ac.setMode(255); + EXPECT_EQ(kSharpAcAuto, ac.getMode()); +} + + +TEST(TestSharpAcClass, FanSpeed) { + IRSharpAc ac(0); + ac.begin(); + + // Unexpected value should default to Auto. + ac.setFan(0); + EXPECT_EQ(kSharpAcFanAuto, ac.getFan()); + + // Unexpected value should default to Auto. + ac.setFan(255); + EXPECT_EQ(kSharpAcFanAuto, ac.getFan()); + + ac.setFan(kSharpAcFanMax); + EXPECT_EQ(kSharpAcFanMax, ac.getFan()); + + // Beyond Max should default to Auto. + ac.setFan(kSharpAcFanMax + 1); + EXPECT_EQ(kSharpAcFanAuto, ac.getFan()); + + ac.setFan(kSharpAcFanMed); + EXPECT_EQ(kSharpAcFanMed, ac.getFan()); + + ac.setFan(kSharpAcFanMin); + EXPECT_EQ(kSharpAcFanMin, ac.getFan()); + + ac.setFan(kSharpAcFanAuto - 1); + EXPECT_EQ(kSharpAcFanAuto, ac.getFan()); + + ac.setFan(kSharpAcFanMax + 1); + EXPECT_EQ(kSharpAcFanAuto, ac.getFan()); + + ac.setFan(kSharpAcFanAuto); + EXPECT_EQ(kSharpAcFanAuto, ac.getFan()); +} + +TEST(TestSharpAcClass, ReconstructKnownState) { + IRSharpAc ac(0); + ac.begin(); + + uint8_t on_auto_auto[kSharpAcStateLength] = { + 0xAA, 0x5A, 0xCF, 0x10, 0x00, 0x11, 0x20, 0x00, 0x08, 0x80, 0x00, 0xE0, + 0x01}; + ac.on(); + ac.setMode(kSharpAcAuto); + ac.setTemp(kSharpAcMinTemp); + ac.setFan(kSharpAcFanAuto); + EXPECT_STATE_EQ(on_auto_auto, ac.getRaw(), kSharpAcBits); + EXPECT_EQ("Power: On, Mode: 0 (AUTO), Temp: 15C, Fan: 2 (Auto)", + ac.toString()); + + uint8_t cool_auto_28[kSharpAcStateLength] = { + 0xAA, 0x5A, 0xCF, 0x10, 0xCD, 0x31, 0x22, 0x00, 0x08, 0x80, 0x04, 0xE0, + 0x51}; + ac.stateReset(); + ac.on(); + ac.setMode(kSharpAcCool); + ac.setTemp(28); + ac.setFan(kSharpAcFanAuto); + EXPECT_EQ("Power: On, Mode: 2 (COOL), Temp: 28C, Fan: 2 (Auto)", + ac.toString()); + EXPECT_STATE_EQ(cool_auto_28, ac.getRaw(), kSharpAcBits); +} + +// https://github.com/crankyoldgit/IRremoteESP8266/issues/638#issue-421064165 +TEST(TestSharpAcClass, KnownStates) { + IRSharpAc ac(0); + ac.begin(); + + uint8_t off_auto_auto[kSharpAcStateLength] = { + 0xAA, 0x5A, 0xCF, 0x10, 0x00, 0x21, 0x20, 0x00, 0x08, 0x80, 0x00, 0xE0, + 0x31}; + ASSERT_TRUE(ac.validChecksum(off_auto_auto)); + ac.setRaw(off_auto_auto); + EXPECT_EQ("Power: Off, Mode: 0 (AUTO), Temp: 15C, Fan: 2 (Auto)", + ac.toString()); + uint8_t on_auto_auto[kSharpAcStateLength] = { + 0xAA, 0x5A, 0xCF, 0x10, 0x00, 0x11, 0x20, 0x00, 0x08, 0x80, 0x00, 0xE0, + 0x01}; + ASSERT_TRUE(ac.validChecksum(on_auto_auto)); + ac.setRaw(on_auto_auto); + EXPECT_EQ("Power: On, Mode: 0 (AUTO), Temp: 15C, Fan: 2 (Auto)", + ac.toString()); + uint8_t cool_auto_28[kSharpAcStateLength] = { + 0xAA, 0x5A, 0xCF, 0x10, 0xCD, 0x31, 0x22, 0x00, 0x08, 0x80, 0x04, 0xE0, + 0x51}; + ASSERT_TRUE(ac.validChecksum(cool_auto_28)); + ac.setRaw(cool_auto_28); + EXPECT_EQ("Power: On, Mode: 2 (COOL), Temp: 28C, Fan: 2 (Auto)", + ac.toString()); + uint8_t cool_fan1_28[kSharpAcStateLength] = { + 0xAA, 0x5A, 0xCF, 0x10, 0xCD, 0x31, 0x42, 0x00, 0x08, 0x80, 0x05, 0xE0, + 0x21}; + ASSERT_TRUE(ac.validChecksum(cool_fan1_28)); + ac.setRaw(cool_fan1_28); + EXPECT_EQ("Power: On, Mode: 2 (COOL), Temp: 28C, Fan: 4 (Low)", + ac.toString()); + uint8_t cool_fan2_28[kSharpAcStateLength] = { + 0xAA, 0x5A, 0xCF, 0x10, 0xCD, 0x31, 0x32, 0x00, 0x08, 0x80, 0x05, 0xE0, + 0x51}; + ASSERT_TRUE(ac.validChecksum(cool_fan2_28)); + ac.setRaw(cool_fan2_28); + EXPECT_EQ("Power: On, Mode: 2 (COOL), Temp: 28C, Fan: 3 (Medium)", + ac.toString()); + uint8_t cool_fan3_28[kSharpAcStateLength] = { + 0xAA, 0x5A, 0xCF, 0x10, 0xCD, 0x31, 0x52, 0x00, 0x08, 0x80, 0x05, 0xE0, + 0x31}; + ASSERT_TRUE(ac.validChecksum(cool_fan3_28)); + ac.setRaw(cool_fan3_28); + EXPECT_EQ("Power: On, Mode: 2 (COOL), Temp: 28C, Fan: 5 (UNKNOWN)", + ac.toString()); + uint8_t cool_fan4_28[kSharpAcStateLength] = { + 0xAA, 0x5A, 0xCF, 0x10, 0xCD, 0x31, 0x72, 0x00, 0x08, 0x80, 0x05, 0xE0, + 0x11}; + ASSERT_TRUE(ac.validChecksum(cool_fan4_28)); + ac.setRaw(cool_fan4_28); + EXPECT_EQ("Power: On, Mode: 2 (COOL), Temp: 28C, Fan: 7 (High)", + ac.toString()); + /* Unsupported / Not yet reverse engineered. + uint8_t cool_fan4_28_ion_on[kSharpAcStateLength] = { + 0xAA, 0x5A, 0xCF, 0x10, 0xCD, 0x61, 0x72, 0x08, 0x08, 0x80, 0x00, 0xE4, + 0xD1}; + ASSERT_TRUE(ac.validChecksum(cool_fan4_28_ion_on)); + ac.setRaw(cool_fan4_28_ion_on); + EXPECT_EQ("Power: On, Mode: 2 (COOL), Temp: 28C, Fan: 7 (MAX)", + ac.toString()); + uint8_t cool_fan4_28_eco1[kSharpAcStateLength] = { + 0xAA, 0x5A, 0xCF, 0x10, 0xCD, 0x61, 0x72, 0x18, 0x08, 0x80, 0x00, 0xE8, + 0x01}; + ASSERT_TRUE(ac.validChecksum(cool_fan4_28_eco1)); + ac.setRaw(cool_fan4_28_eco1); + EXPECT_EQ("Power: On, Mode: 2 (COOL), Temp: 28C, Fan: 7 (MAX)", + ac.toString()); */ + uint8_t dry_auto[kSharpAcStateLength] = { + 0xAA, 0x5A, 0xCF, 0x10, 0x00, 0x31, 0x23, 0x00, 0x08, 0x80, 0x00, 0xE0, + 0x11}; + ASSERT_TRUE(ac.validChecksum(dry_auto)); + ac.setRaw(dry_auto); + EXPECT_EQ("Power: On, Mode: 3 (DRY), Temp: 15C, Fan: 2 (Auto)", + ac.toString()); +} + +TEST(TestSharpAcClass, toCommon) { + IRSharpAc ac(0); + ac.setPower(true); + ac.setMode(kSharpAcCool); + ac.setTemp(20); + ac.setFan(kSharpAcFanMax); + // Now test it. + ASSERT_EQ(decode_type_t::SHARP_AC, ac.toCommon().protocol); + ASSERT_TRUE(ac.toCommon().power); + ASSERT_TRUE(ac.toCommon().celsius); + ASSERT_EQ(20, ac.toCommon().degrees); + ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode); + ASSERT_EQ(stdAc::fanspeed_t::kMax, ac.toCommon().fanspeed); + // Unsupported. + ASSERT_EQ(-1, ac.toCommon().model); + ASSERT_EQ(stdAc::swingv_t::kOff, ac.toCommon().swingv); + ASSERT_EQ(stdAc::swingh_t::kOff, ac.toCommon().swingh); + ASSERT_FALSE(ac.toCommon().turbo); + ASSERT_FALSE(ac.toCommon().quiet); + ASSERT_FALSE(ac.toCommon().clean); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_FALSE(ac.toCommon().econo); + ASSERT_FALSE(ac.toCommon().light); + ASSERT_FALSE(ac.toCommon().filter); + ASSERT_EQ(-1, ac.toCommon().sleep); + ASSERT_EQ(-1, ac.toCommon().clock); +} diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Sherwood_test.cpp b/lib/IRremoteESP8266-2.6.5/test/ir_Sherwood_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.6.0/test/ir_Sherwood_test.cpp rename to lib/IRremoteESP8266-2.6.5/test/ir_Sherwood_test.cpp diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Sony_test.cpp b/lib/IRremoteESP8266-2.6.5/test/ir_Sony_test.cpp similarity index 96% rename from lib/IRremoteESP8266-2.6.0/test/ir_Sony_test.cpp rename to lib/IRremoteESP8266-2.6.5/test/ir_Sony_test.cpp index 35c3287b0..51bacbd6d 100644 --- a/lib/IRremoteESP8266-2.6.0/test/ir_Sony_test.cpp +++ b/lib/IRremoteESP8266-2.6.5/test/ir_Sony_test.cpp @@ -153,7 +153,7 @@ TEST(TestDecodeSony, NormalSonyDecodeWithStrict) { irsend.reset(); irsend.sendSony(irsend.encodeSony(kSony20Bits, 0x1, 0x1, 0x1)); irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decodeSony(&irsend.capture, kSony20Bits, true)); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); EXPECT_EQ(SONY, irsend.capture.decode_type); EXPECT_EQ(kSony20Bits, irsend.capture.bits); EXPECT_EQ(0x81080, irsend.capture.value); @@ -164,7 +164,7 @@ TEST(TestDecodeSony, NormalSonyDecodeWithStrict) { irsend.reset(); irsend.sendSony(irsend.encodeSony(kSony15Bits, 21, 1), kSony15Bits); irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decodeSony(&irsend.capture, kSony15Bits, true)); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); EXPECT_EQ(SONY, irsend.capture.decode_type); EXPECT_EQ(kSony15Bits, irsend.capture.bits); EXPECT_EQ(0x5480, irsend.capture.value); @@ -175,7 +175,7 @@ TEST(TestDecodeSony, NormalSonyDecodeWithStrict) { irsend.reset(); irsend.sendSony(irsend.encodeSony(kSony12Bits, 21, 1), kSony12Bits); irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decodeSony(&irsend.capture, kSony12Bits, true)); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); EXPECT_EQ(SONY, irsend.capture.decode_type); EXPECT_EQ(kSony12Bits, irsend.capture.bits); EXPECT_EQ(0xA90, irsend.capture.value); @@ -248,7 +248,7 @@ TEST(TestDecodeSony, SonyDecodeWithIllegalSize) { EXPECT_FALSE(irrecv.decodeSony(&irsend.capture, kSony15Bits, true)); EXPECT_FALSE(irrecv.decodeSony(&irsend.capture, kSony20Bits, true)); // Should work with a 'normal' match (not strict) - ASSERT_TRUE(irrecv.decodeSony(&irsend.capture)); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); EXPECT_EQ(SONY, irsend.capture.decode_type); EXPECT_EQ(8, irsend.capture.bits); EXPECT_EQ(0xFF, irsend.capture.value); @@ -264,7 +264,7 @@ TEST(TestDecodeSony, SonyDecodeWithIllegalSize) { EXPECT_FALSE(irrecv.decodeSony(&irsend.capture, kSony15Bits, true)); EXPECT_FALSE(irrecv.decodeSony(&irsend.capture, kSony20Bits, true)); // Should work with a 'normal' match (not strict) - ASSERT_TRUE(irrecv.decodeSony(&irsend.capture)); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); EXPECT_EQ(SONY, irsend.capture.decode_type); EXPECT_EQ(13, irsend.capture.bits); EXPECT_EQ(0x1FFF, irsend.capture.value); @@ -280,7 +280,7 @@ TEST(TestDecodeSony, SonyDecodeWithIllegalSize) { EXPECT_FALSE(irrecv.decodeSony(&irsend.capture, kSony15Bits, true)); EXPECT_FALSE(irrecv.decodeSony(&irsend.capture, kSony20Bits, true)); // Should work with a 'normal' match (not strict) - ASSERT_TRUE(irrecv.decodeSony(&irsend.capture)); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); EXPECT_EQ(SONY, irsend.capture.decode_type); EXPECT_EQ(17, irsend.capture.bits); EXPECT_EQ(0x1FFFF, irsend.capture.value); @@ -296,19 +296,18 @@ TEST(TestDecodeSony, SonyDecodeWithIllegalSize) { EXPECT_FALSE(irrecv.decodeSony(&irsend.capture, kSony15Bits, true)); EXPECT_FALSE(irrecv.decodeSony(&irsend.capture, kSony20Bits, true)); // Should work with a 'normal' match (not strict) - ASSERT_TRUE(irrecv.decodeSony(&irsend.capture)); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); EXPECT_EQ(SONY, irsend.capture.decode_type); EXPECT_EQ(21, irsend.capture.bits); EXPECT_EQ(0x1FFFFF, irsend.capture.value); EXPECT_EQ(0x0, irsend.capture.address); EXPECT_EQ(0x0, irsend.capture.command); - irsend.reset(); // Illegal 64-bit (max) Sony-like message. irsend.sendSony(0xFFFFFFFFFFFFFFFF, 64, 0); irsend.makeDecodeResult(); // Should work with a 'normal' match (not strict) - ASSERT_TRUE(irrecv.decodeSony(&irsend.capture)); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); EXPECT_EQ(SONY, irsend.capture.decode_type); EXPECT_EQ(64, irsend.capture.bits); EXPECT_EQ(0xFFFFFFFFFFFFFFFF, irsend.capture.value); @@ -316,7 +315,6 @@ TEST(TestDecodeSony, SonyDecodeWithIllegalSize) { EXPECT_EQ(0x0, irsend.capture.command); } -// Decode unsupported Sony messages. i.e non-standard sizes. TEST(TestDecodeSony, DecodeGlobalCacheExample) { IRsendTest irsend(4); IRrecv irrecv(4); @@ -331,7 +329,7 @@ TEST(TestDecodeSony, DecodeGlobalCacheExample) { irsend.makeDecodeResult(); // Without strict. - ASSERT_TRUE(irrecv.decodeSony(&irsend.capture)); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); EXPECT_EQ(SONY, irsend.capture.decode_type); EXPECT_EQ(12, irsend.capture.bits); EXPECT_EQ(0x750, irsend.capture.value); diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Tcl_test.cpp b/lib/IRremoteESP8266-2.6.5/test/ir_Tcl_test.cpp similarity index 78% rename from lib/IRremoteESP8266-2.6.0/test/ir_Tcl_test.cpp rename to lib/IRremoteESP8266-2.6.5/test/ir_Tcl_test.cpp index 249dcc637..8432cf9ac 100644 --- a/lib/IRremoteESP8266-2.6.0/test/ir_Tcl_test.cpp +++ b/lib/IRremoteESP8266-2.6.5/test/ir_Tcl_test.cpp @@ -246,7 +246,6 @@ TEST(TestTcl112AcClass, Power) { ac.toString()); } - TEST(TestTcl112AcClass, Checksum) { uint8_t temp16C[kTcl112AcStateLength] = { 0x23, 0xCB, 0x26, 0x01, 0x00, 0x24, 0x03, @@ -382,3 +381,82 @@ TEST(TestTcl112AcClass, FanSpeed) { ac.setFan(kTcl112AcFanHigh + 1); EXPECT_EQ(kTcl112AcFanAuto, ac.getFan()); } + + +TEST(TestTcl112AcClass, toCommon) { + IRTcl112Ac ac(0); + ac.setPower(true); + ac.setMode(kTcl112AcCool); + ac.setTemp(20); + ac.setFan(kTcl112AcFanHigh); + ac.setSwingVertical(true); + ac.setSwingHorizontal(true); + ac.setTurbo(true); + ac.setHealth(true); + ac.setEcono(true); + ac.setLight(true); + // Now test it. + ASSERT_EQ(decode_type_t::TCL112AC, ac.toCommon().protocol); + ASSERT_EQ(-1, ac.toCommon().model); + ASSERT_TRUE(ac.toCommon().power); + ASSERT_TRUE(ac.toCommon().celsius); + ASSERT_EQ(20, ac.toCommon().degrees); + ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode); + ASSERT_EQ(stdAc::fanspeed_t::kMax, ac.toCommon().fanspeed); + ASSERT_EQ(stdAc::swingv_t::kAuto, ac.toCommon().swingv); + ASSERT_EQ(stdAc::swingh_t::kAuto, ac.toCommon().swingh); + ASSERT_TRUE(ac.toCommon().turbo); + ASSERT_TRUE(ac.toCommon().econo); + ASSERT_TRUE(ac.toCommon().light); + ASSERT_TRUE(ac.toCommon().filter); + // Unsupported. + ASSERT_FALSE(ac.toCommon().clean); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_FALSE(ac.toCommon().quiet); + ASSERT_EQ(-1, ac.toCommon().sleep); + ASSERT_EQ(-1, ac.toCommon().clock); +} + +TEST(TestDecodeTcl112Ac, Issue744) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + irsend.reset(); + uint16_t rawData[227] = { + 3164, 1532, 584, 1082, 472, 1068, 580, 244, 602, 264, 542, 328, 530, 1034, + 586, 262, 540, 326, 508, 1064, 582, 1082, 490, 328, 532, 1032, 586, 262, + 544, 352, 478, 1060, 584, 1082, 486, 328, 502, 1058, 588, 1084, 472, 344, + 530, 250, 600, 1086, 492, 322, 530, 258, 594, 1082, 494, 318, 510, 344, + 530, 248, 600, 262, 544, 326, 504, 296, 578, 252, 598, 260, 550, 318, 506, + 344, 530, 250, 600, 258, 546, 318, 508, 342, 532, 254, 596, 236, 606, 266, + 524, 1066, 580, 242, 602, 266, 542, 1054, 574, 246, 604, 262, 550, 1088, + 530, 1034, 588, 262, 542, 328, 504, 296, 582, 238, 606, 262, 546, 322, + 508, 342, 530, 250, 602, 260, 544, 1052, 572, 252, 600, 260, 546, 320, + 506, 344, 530, 254, 596, 264, 578, 268, 552, 316, 528, 256, 598, 260, 578, + 272, 520, 372, 476, 294, 582, 240, 604, 266, 542, 328, 502, 294, 582, 238, + 604, 268, 540, 322, 506, 346, 530, 244, 604, 260, 542, 354, 478, 298, 580, + 240, 604, 262, 542, 326, 506, 342, 530, 250, 600, 260, 548, 318, 506, 344, + 530, 250, 600, 260, 546, 320, 528, 322, 530, 254, 598, 262, 548, 316, 468, + 380, 532, 250, 600, 260, 546, 1092, 500, 300, 578, 246, 602, 1082, 474, + 346, 530, 248, 602, 260, 542, 1054, 570, 1090, 524}; // UNKNOWN 3338FACE + + uint8_t expectedState[kTcl112AcStateLength] = { + 0x23, 0xCB, 0x26, 0x01, 0x00, 0x24, 0x03, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x80, 0xC4}; + + irsend.sendRaw(rawData, 227, 38000); + irsend.makeDecodeResult(); + + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(TCL112AC, irsend.capture.decode_type); + EXPECT_EQ(kTcl112AcBits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + + IRTcl112Ac ac(0); + ac.setRaw(irsend.capture.state); + EXPECT_EQ( + "Power: On, Mode: 3 (COOL), Temp: 23C, Fan: 0 (Auto), Econo: Off, " + "Health: Off, Light: On, Turbo: Off, Swing (H): Off, Swing (V): Off", + ac.toString()); +} diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Teco_test.cpp b/lib/IRremoteESP8266-2.6.5/test/ir_Teco_test.cpp similarity index 77% rename from lib/IRremoteESP8266-2.6.0/test/ir_Teco_test.cpp rename to lib/IRremoteESP8266-2.6.5/test/ir_Teco_test.cpp index 6b03a671d..4ed7fbd9f 100644 --- a/lib/IRremoteESP8266-2.6.0/test/ir_Teco_test.cpp +++ b/lib/IRremoteESP8266-2.6.5/test/ir_Teco_test.cpp @@ -198,26 +198,72 @@ TEST(TestTecoACClass, Sleep) { EXPECT_TRUE(ac.getSleep()); } +TEST(TestTecoACClass, Light) { + IRTecoAc ac(0); + ac.begin(); + + ac.setLight(true); + EXPECT_TRUE(ac.getLight()); + ac.setLight(false); + EXPECT_EQ(false, ac.getLight()); + ac.setLight(true); + EXPECT_TRUE(ac.getLight()); + // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/870#issue-484797174 + ac.setRaw(0x250200A09); + EXPECT_TRUE(ac.getLight()); +} + +TEST(TestTecoACClass, Humid) { + IRTecoAc ac(0); + ac.begin(); + + ac.setHumid(true); + EXPECT_TRUE(ac.getHumid()); + ac.setHumid(false); + EXPECT_EQ(false, ac.getHumid()); + ac.setHumid(true); + EXPECT_TRUE(ac.getHumid()); + // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/870#issuecomment-524536810 + ac.setRaw(0x250100A09); + EXPECT_TRUE(ac.getHumid()); +} + +TEST(TestTecoACClass, Save) { + IRTecoAc ac(0); + ac.begin(); + + ac.setSave(true); + EXPECT_TRUE(ac.getSave()); + ac.setSave(false); + EXPECT_EQ(false, ac.getSave()); + ac.setSave(true); + EXPECT_TRUE(ac.getSave()); + // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/870#issuecomment-524536810 + ac.setRaw(0x250800A09); + EXPECT_TRUE(ac.getSave()); +} + TEST(TestTecoACClass, MessageConstuction) { IRTecoAc ac(0); EXPECT_EQ( "Power: Off, Mode: 0 (AUTO), Temp: 16C, Fan: 0 (Auto), Sleep: Off, " - "Swing: Off", + "Swing: Off, Light: Off, Humid: Off, Save: Off", ac.toString()); ac.setPower(true); ac.setMode(kTecoCool); ac.setTemp(21); ac.setFan(kTecoFanHigh); ac.setSwing(false); + ac.setLight(false); EXPECT_EQ( "Power: On, Mode: 1 (COOL), Temp: 21C, Fan: 3 (High), Sleep: Off, " - "Swing: Off", + "Swing: Off, Light: Off, Humid: Off, Save: Off", ac.toString()); ac.setSwing(true); EXPECT_EQ( "Power: On, Mode: 1 (COOL), Temp: 21C, Fan: 3 (High), Sleep: Off, " - "Swing: On", + "Swing: On, Light: Off, Humid: Off, Save: Off", ac.toString()); ac.setSwing(false); ac.setFan(kTecoFanLow); @@ -225,17 +271,20 @@ TEST(TestTecoACClass, MessageConstuction) { ac.setMode(kTecoHeat); EXPECT_EQ( "Power: On, Mode: 4 (HEAT), Temp: 21C, Fan: 1 (Low), Sleep: On, " - "Swing: Off", + "Swing: Off, Light: Off, Humid: Off, Save: Off", ac.toString()); ac.setSleep(false); EXPECT_EQ( "Power: On, Mode: 4 (HEAT), Temp: 21C, Fan: 1 (Low), Sleep: Off, " - "Swing: Off", + "Swing: Off, Light: Off, Humid: Off, Save: Off", ac.toString()); ac.setTemp(25); + ac.setLight(true); + ac.setSave(true); + ac.setHumid(true); EXPECT_EQ( "Power: On, Mode: 4 (HEAT), Temp: 25C, Fan: 1 (Low), Sleep: Off, " - "Swing: Off", + "Swing: Off, Light: On, Humid: On, Save: On", ac.toString()); } @@ -253,7 +302,7 @@ TEST(TestTecoACClass, ReconstructKnownMessage) { EXPECT_EQ(expected, ac.getRaw()); EXPECT_EQ( "Power: On, Mode: 1 (COOL), Temp: 27C, Fan: 0 (Auto), Sleep: On, " - "Swing: On", + "Swing: On, Light: Off, Humid: Off, Save: Off", ac.toString()); } @@ -295,7 +344,7 @@ TEST(TestDecodeTeco, NormalDecodeWithStrict) { ac.setRaw(irsend.capture.value); EXPECT_EQ( "Power: Off, Mode: 0 (AUTO), Temp: 16C, Fan: 0 (Auto), Sleep: Off, " - "Swing: Off", + "Swing: Off, Light: Off, Humid: Off, Save: Off", ac.toString()); } @@ -328,7 +377,7 @@ TEST(TestDecodeTeco, RealNormalExample) { ac.setRaw(irsend.capture.value); EXPECT_EQ( "Power: On, Mode: 1 (COOL), Temp: 27C, Fan: 0 (Auto), Sleep: On, " - "Swing: On", + "Swing: On, Light: Off, Humid: Off, Save: Off", ac.toString()); uint16_t rawData2[73] = { @@ -352,7 +401,38 @@ TEST(TestDecodeTeco, RealNormalExample) { ac.begin(); ac.setRaw(irsend.capture.value); EXPECT_EQ( - "Power: On, Mode: 2 (DRY), Temp: 21C, Fan: 2 (Med), Sleep: Off, " - "Swing: On", + "Power: On, Mode: 2 (DRY), Temp: 21C, Fan: 2 (Medium), Sleep: Off, " + "Swing: On, Light: Off, Humid: Off, Save: Off", ac.toString()); } + + +TEST(TestTecoACClass, toCommon) { + IRTecoAc ac(0); + ac.setPower(true); + ac.setMode(kTecoCool); + ac.setTemp(20); + ac.setFan(kTecoFanHigh); + ac.setSwing(true); + ac.setSleep(true); + // Now test it. + ASSERT_EQ(decode_type_t::TECO, ac.toCommon().protocol); + ASSERT_TRUE(ac.toCommon().power); + ASSERT_TRUE(ac.toCommon().celsius); + ASSERT_EQ(20, ac.toCommon().degrees); + ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode); + ASSERT_EQ(stdAc::fanspeed_t::kMax, ac.toCommon().fanspeed); + ASSERT_EQ(stdAc::swingv_t::kAuto, ac.toCommon().swingv); + ASSERT_EQ(0, ac.toCommon().sleep); + // Unsupported. + ASSERT_EQ(-1, ac.toCommon().model); + ASSERT_EQ(stdAc::swingh_t::kOff, ac.toCommon().swingh); + ASSERT_FALSE(ac.toCommon().turbo); + ASSERT_FALSE(ac.toCommon().econo); + ASSERT_FALSE(ac.toCommon().light); + ASSERT_FALSE(ac.toCommon().filter); + ASSERT_FALSE(ac.toCommon().clean); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_FALSE(ac.toCommon().quiet); + ASSERT_EQ(-1, ac.toCommon().clock); +} diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Toshiba_test.cpp b/lib/IRremoteESP8266-2.6.5/test/ir_Toshiba_test.cpp similarity index 96% rename from lib/IRremoteESP8266-2.6.0/test/ir_Toshiba_test.cpp rename to lib/IRremoteESP8266-2.6.5/test/ir_Toshiba_test.cpp index d74866f92..15f8e6b90 100644 --- a/lib/IRremoteESP8266-2.6.0/test/ir_Toshiba_test.cpp +++ b/lib/IRremoteESP8266-2.6.5/test/ir_Toshiba_test.cpp @@ -352,16 +352,17 @@ TEST(TestToshibaACClass, HumanReadableOutput) { 0x00, 0xC1, 0x00, 0xC0}; toshiba.setRaw(initial_state); - EXPECT_EQ("Power: On, Mode: 0 (AUTO), Temp: 17C, Fan: 0 (AUTO)", + EXPECT_EQ("Power: On, Mode: 0 (AUTO), Temp: 17C, Fan: 0 (Auto)", toshiba.toString()); toshiba.setRaw(modified_state); - EXPECT_EQ("Power: On, Mode: 1 (COOL), Temp: 17C, Fan: 5 (MAX)", + EXPECT_EQ("Power: On, Mode: 1 (COOL), Temp: 17C, Fan: 5 (High)", toshiba.toString()); toshiba.off(); toshiba.setTemp(25); toshiba.setFan(3); toshiba.setMode(kToshibaAcDry); - EXPECT_EQ("Power: Off, Mode: 2 (DRY), Temp: 25C, Fan: 3", toshiba.toString()); + EXPECT_EQ("Power: Off, Mode: 2 (DRY), Temp: 25C, Fan: 3 (Medium)", + toshiba.toString()); } TEST(TestToshibaACClass, MessageConstuction) { @@ -669,3 +670,31 @@ TEST(TestDecodeToshibaAC, RealExamples) { // sending the power off message. EXPECT_EQ(kToshibaAcHeat, toshiba.getMode()); } + +TEST(TestToshibaACClass, toCommon) { + IRToshibaAC ac(0); + ac.setPower(true); + ac.setMode(kToshibaAcCool); + ac.setTemp(20); + ac.setFan(kToshibaAcFanMax); + // Now test it. + ASSERT_EQ(decode_type_t::TOSHIBA_AC, ac.toCommon().protocol); + ASSERT_EQ(-1, ac.toCommon().model); + ASSERT_TRUE(ac.toCommon().power); + ASSERT_TRUE(ac.toCommon().celsius); + ASSERT_EQ(20, ac.toCommon().degrees); + ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode); + ASSERT_EQ(stdAc::fanspeed_t::kMax, ac.toCommon().fanspeed); + // Unsupported. + ASSERT_EQ(stdAc::swingv_t::kOff, ac.toCommon().swingv); + ASSERT_EQ(stdAc::swingh_t::kOff, ac.toCommon().swingh); + ASSERT_FALSE(ac.toCommon().turbo); + ASSERT_FALSE(ac.toCommon().econo); + ASSERT_FALSE(ac.toCommon().light); + ASSERT_FALSE(ac.toCommon().filter); + ASSERT_FALSE(ac.toCommon().clean); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_FALSE(ac.toCommon().quiet); + ASSERT_EQ(-1, ac.toCommon().sleep); + ASSERT_EQ(-1, ac.toCommon().clock); +} diff --git a/lib/IRremoteESP8266-2.6.5/test/ir_Trotec_test.cpp b/lib/IRremoteESP8266-2.6.5/test/ir_Trotec_test.cpp new file mode 100644 index 000000000..f9272f384 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.5/test/ir_Trotec_test.cpp @@ -0,0 +1,179 @@ +// Copyright 2019 David Conran +#include "ir_Trotec.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "gtest/gtest.h" + + +TEST(TestTrotecESPClass, toCommon) { + IRTrotecESP ac(0); + ac.setPower(true); + ac.setMode(kTrotecCool); + ac.setTemp(20); + ac.setSpeed(kTrotecFanHigh); + ac.setSleep(true); + // Now test it. + ASSERT_EQ(decode_type_t::TROTEC, ac.toCommon().protocol); + ASSERT_TRUE(ac.toCommon().power); + ASSERT_TRUE(ac.toCommon().celsius); + ASSERT_EQ(20, ac.toCommon().degrees); + ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode); + ASSERT_EQ(stdAc::fanspeed_t::kMax, ac.toCommon().fanspeed); + ASSERT_EQ(0, ac.toCommon().sleep); + // Unsupported. + ASSERT_EQ(-1, ac.toCommon().model); + ASSERT_EQ(stdAc::swingv_t::kOff, ac.toCommon().swingv); + ASSERT_EQ(stdAc::swingh_t::kOff, ac.toCommon().swingh); + ASSERT_FALSE(ac.toCommon().turbo); + ASSERT_FALSE(ac.toCommon().econo); + ASSERT_FALSE(ac.toCommon().light); + ASSERT_FALSE(ac.toCommon().filter); + ASSERT_FALSE(ac.toCommon().clean); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_FALSE(ac.toCommon().quiet); + ASSERT_EQ(-1, ac.toCommon().clock); +} + +TEST(TestTrotecESPClass, MessageConstructon) { + IRTrotecESP ac(0); + ac.setPower(true); + ac.setTemp(20); + ac.setMode(kTrotecCool); + ac.setSpeed(kTrotecFanMed); + ac.setSleep(true); + + uint8_t expected[kTrotecStateLength] = { + 0x12, 0x34, 0x29, 0x82, 0x00, 0x00, 0x00, 0x00, 0xAB}; + EXPECT_STATE_EQ(expected, ac.getRaw(), kTrotecBits); + EXPECT_EQ( + "Power: On, Mode: 1 (COOL), Temp: 20C, Fan: 2 (Medium), Sleep: On", + ac.toString()); +} + +// Tests for sendTrotec(). + +// Test sending typical data only. +TEST(TestSendTrotec, SendDataOnly) { + IRsendTest irsend(0); + irsend.begin(); + uint8_t data[kTrotecStateLength] = { + 0x12, 0x34, 0x29, 0x82, 0x00, 0x00, 0x00, 0x00, 0xAB}; + + irsend.sendTrotec(data); + EXPECT_EQ( + "f36000d50" + "m5952s7364" + "m592s592m592s1560m592s592m592s592m592s1560m592s592m592s592m592s592" + "m592s592m592s592m592s1560m592s592m592s1560m592s1560m592s592m592s592" + "m592s1560m592s592m592s592m592s1560m592s592m592s1560m592s592m592s592" + "m592s592m592s1560m592s592m592s592m592s592m592s592m592s592m592s1560" + "m592s592m592s592m592s592m592s592m592s592m592s592m592s592m592s592" + "m592s592m592s592m592s592m592s592m592s592m592s592m592s592m592s592" + "m592s592m592s592m592s592m592s592m592s592m592s592m592s592m592s592" + "m592s592m592s592m592s592m592s592m592s592m592s592m592s592m592s592" + "m592s1560m592s1560m592s592m592s1560m592s592m592s1560m592s592m592s1560" + "m592s6184" + "m592s1500", + irsend.outputStr()); +} + +// Tests for decodeTrotec(). +// Decode normal Trotec messages. + +TEST(TestDecodeTrotec, SyntheticDecode) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + // Synthesised Normal Trotec message. + irsend.reset(); + uint8_t expectedState[kTrotecStateLength] = { + 0x12, 0x34, 0x29, 0x82, 0x00, 0x00, 0x00, 0x00, 0xAB}; + irsend.sendTrotec(expectedState); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(decode_type_t::TROTEC, irsend.capture.decode_type); + EXPECT_EQ(kTrotecBits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + IRTrotecESP ac(0); + ac.setRaw(irsend.capture.state); + EXPECT_EQ( + "Power: On, Mode: 1 (COOL), Temp: 20C, Fan: 2 (Medium), Sleep: On", + ac.toString()); +} + + +TEST(TestTrotecESPClass, SetAndGetTemp) { + IRTrotecESP ac(0); + + ac.setTemp(25); + EXPECT_EQ(25, ac.getTemp()); + ac.setTemp(kTrotecMinTemp); + EXPECT_EQ(kTrotecMinTemp, ac.getTemp()); + ac.setTemp(kTrotecMaxTemp); + EXPECT_EQ(kTrotecMaxTemp, ac.getTemp()); + ac.setTemp(kTrotecMinTemp - 1); + EXPECT_EQ(kTrotecMinTemp, ac.getTemp()); + ac.setTemp(kTrotecMaxTemp + 1); + EXPECT_EQ(kTrotecMaxTemp, ac.getTemp()); +} + +TEST(TestTrotecESPClass, SetAndGetMode) { + IRTrotecESP ac(0); + + ac.setMode(kTrotecFan); + EXPECT_EQ(kTrotecFan, ac.getMode()); + ac.setMode(kTrotecCool); + EXPECT_EQ(kTrotecCool, ac.getMode()); + ac.setMode(kTrotecAuto); + EXPECT_EQ(kTrotecAuto, ac.getMode()); + ac.setMode(kTrotecDry); + EXPECT_EQ(kTrotecDry, ac.getMode()); + ac.setMode(255); + EXPECT_EQ(kTrotecAuto, ac.getMode()); +} + +TEST(TestTrotecESPClass, SetAndGetFan) { + IRTrotecESP ac(0); + + ac.setSpeed(kTrotecFanHigh); + EXPECT_EQ(kTrotecFanHigh, ac.getSpeed()); + ac.setSpeed(kTrotecFanLow); + EXPECT_EQ(kTrotecFanLow, ac.getSpeed()); + ac.setSpeed(kTrotecFanMed); + EXPECT_EQ(kTrotecFanMed, ac.getSpeed()); + ac.setSpeed(kTrotecFanHigh); + EXPECT_EQ(kTrotecFanHigh, ac.getSpeed()); + ASSERT_NE(7, kTrotecFanHigh); + // Now try some unexpected value. + ac.setSpeed(7); + EXPECT_EQ(kTrotecFanHigh, ac.getSpeed()); +} + +TEST(TestTrotecESPClass, Sleep) { + IRTrotecESP ac(0); + ac.setSleep(false); + ASSERT_FALSE(ac.getSleep()); + ac.setSleep(true); + ASSERT_TRUE(ac.getSleep()); + ac.setSleep(false); + ASSERT_FALSE(ac.getSleep()); +} + +TEST(TestTrotecESPClass, Power) { + IRTrotecESP ac(0); + ac.setPower(false); + ASSERT_FALSE(ac.getPower()); + ac.setPower(true); + ASSERT_TRUE(ac.getPower()); + ac.setPower(false); + ASSERT_FALSE(ac.getPower()); +} + +TEST(TestUtils, Housekeeping) { + ASSERT_EQ("TROTEC", typeToString(decode_type_t::TROTEC)); + ASSERT_EQ(decode_type_t::TROTEC, strToDecodeType("TROTEC")); + ASSERT_TRUE(hasACState(decode_type_t::TROTEC)); +} diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Vestel_test.cpp b/lib/IRremoteESP8266-2.6.5/test/ir_Vestel_test.cpp similarity index 88% rename from lib/IRremoteESP8266-2.6.0/test/ir_Vestel_test.cpp rename to lib/IRremoteESP8266-2.6.5/test/ir_Vestel_test.cpp index 077a0a25e..da95b57c4 100644 --- a/lib/IRremoteESP8266-2.6.0/test/ir_Vestel_test.cpp +++ b/lib/IRremoteESP8266-2.6.5/test/ir_Vestel_test.cpp @@ -321,7 +321,7 @@ TEST(TestVestelAcClass, MessageConstuction) { IRVestelAc ac(0); EXPECT_EQ( - "Power: On, Mode: 0 (AUTO), Temp: 25C, Fan: 13 (AUTO HOT), Sleep: Off, " + "Power: On, Mode: 0 (AUTO), Temp: 25C, Fan: 13 (Auto Hot), Sleep: Off, " "Turbo: Off, Ion: Off, Swing: Off", ac.toString()); ac.setMode(kVestelAcCool); @@ -329,7 +329,7 @@ TEST(TestVestelAcClass, MessageConstuction) { ac.setFan(kVestelAcFanHigh); EXPECT_FALSE(ac.isTimeCommand()); EXPECT_EQ( - "Power: On, Mode: 1 (COOL), Temp: 21C, Fan: 11 (HIGH), Sleep: Off, " + "Power: On, Mode: 1 (COOL), Temp: 21C, Fan: 11 (High), Sleep: Off, " "Turbo: Off, Ion: Off, Swing: Off", ac.toString()); ac.setSwing(true); @@ -337,7 +337,7 @@ TEST(TestVestelAcClass, MessageConstuction) { ac.setTurbo(true); EXPECT_FALSE(ac.isTimeCommand()); EXPECT_EQ( - "Power: On, Mode: 1 (COOL), Temp: 21C, Fan: 11 (HIGH), Sleep: Off, " + "Power: On, Mode: 1 (COOL), Temp: 21C, Fan: 11 (High), Sleep: Off, " "Turbo: On, Ion: On, Swing: On", ac.toString()); @@ -345,7 +345,7 @@ TEST(TestVestelAcClass, MessageConstuction) { ac.setSleep(true); ac.setMode(kVestelAcHeat); EXPECT_EQ( - "Power: On, Mode: 4 (HEAT), Temp: 21C, Fan: 11 (HIGH), Sleep: On, " + "Power: On, Mode: 4 (HEAT), Temp: 21C, Fan: 11 (High), Sleep: On, " "Turbo: Off, Ion: On, Swing: On", ac.toString()); EXPECT_FALSE(ac.isTimeCommand()); @@ -353,7 +353,7 @@ TEST(TestVestelAcClass, MessageConstuction) { ac.setTemp(25); ac.setPower(false); EXPECT_EQ( - "Power: Off, Mode: 4 (HEAT), Temp: 25C, Fan: 11 (HIGH), Sleep: On, " + "Power: Off, Mode: 4 (HEAT), Temp: 25C, Fan: 11 (High), Sleep: On, " "Turbo: Off, Ion: On, Swing: On", ac.toString()); EXPECT_FALSE(ac.isTimeCommand()); @@ -368,19 +368,19 @@ TEST(TestVestelAcClass, MessageConstuction) { ac.setTimer(8 * 60 + 0); EXPECT_TRUE(ac.isTimeCommand()); EXPECT_EQ( - "Time: 23:59, Timer: 8:00, On Timer: Off, Off Timer: Off", + "Time: 23:59, Timer: 08:00, On Timer: Off, Off Timer: Off", ac.toString()); ac.setOnTimer(7 * 60 + 40); EXPECT_EQ( - "Time: 23:59, Timer: Off, On Timer: 7:40, Off Timer: Off", + "Time: 23:59, Timer: Off, On Timer: 07:40, Off Timer: Off", ac.toString()); ac.setOffTimer(17 * 60 + 10); EXPECT_EQ( - "Time: 23:59, Timer: Off, On Timer: 7:40, Off Timer: 17:10", + "Time: 23:59, Timer: Off, On Timer: 07:40, Off Timer: 17:10", ac.toString()); ac.setTimer(8 * 60 + 0); EXPECT_EQ( - "Time: 23:59, Timer: 8:00, On Timer: Off, Off Timer: Off", + "Time: 23:59, Timer: 08:00, On Timer: Off, Off Timer: Off", ac.toString()); ac.setTimer(0); EXPECT_EQ( @@ -389,7 +389,7 @@ TEST(TestVestelAcClass, MessageConstuction) { ac.on(); EXPECT_FALSE(ac.isTimeCommand()); EXPECT_EQ( - "Power: On, Mode: 4 (HEAT), Temp: 25C, Fan: 11 (HIGH), Sleep: On, " + "Power: On, Mode: 4 (HEAT), Temp: 25C, Fan: 11 (High), Sleep: On, " "Turbo: Off, Ion: On, Swing: On", ac.toString()); } @@ -431,7 +431,7 @@ TEST(TestDecodeVestelAc, NormalDecodeWithStrict) { ac.begin(); ac.setRaw(irsend.capture.value); EXPECT_EQ( - "Power: On, Mode: 0 (AUTO), Temp: 25C, Fan: 13 (AUTO HOT), Sleep: Off, " + "Power: On, Mode: 0 (AUTO), Temp: 25C, Fan: 13 (Auto Hot), Sleep: Off, " "Turbo: Off, Ion: Off, Swing: Off", ac.toString()); } @@ -467,7 +467,7 @@ TEST(TestDecodeVestelAc, RealNormalExample) { ac.begin(); ac.setRaw(irsend.capture.value); EXPECT_EQ( - "Power: On, Mode: 4 (HEAT), Temp: 16C, Fan: 1 (AUTO), Sleep: Off, " + "Power: On, Mode: 4 (HEAT), Temp: 16C, Fan: 1 (Auto), Sleep: Off, " "Turbo: Off, Ion: On, Swing: Off", ac.toString()); } @@ -502,7 +502,7 @@ TEST(TestDecodeVestelAc, RealTimerExample) { ac.begin(); ac.setRaw(irsend.capture.value); EXPECT_EQ( - "Time: 5:45, Timer: Off, On Timer: 14:00, Off Timer: 23:00", + "Time: 05:45, Timer: Off, On Timer: 14:00, Off Timer: 23:00", ac.toString()); } @@ -511,3 +511,34 @@ TEST(TestDecodeVestelAc, Housekeeping) { ASSERT_EQ("VESTEL_AC", typeToString(VESTEL_AC)); ASSERT_FALSE(hasACState(VESTEL_AC)); // Uses uint64_t, not uint8_t*. } + +TEST(TestVestelAcClass, toCommon) { + IRVestelAc ac(0); + ac.setPower(true); + ac.setMode(kVestelAcCool); + ac.setTemp(20); + ac.setFan(kVestelAcFanHigh); + ac.setSwing(true); + ac.setTurbo(true); + ac.setIon(true); + // Now test it. + ASSERT_EQ(decode_type_t::VESTEL_AC, ac.toCommon().protocol); + ASSERT_EQ(-1, ac.toCommon().model); + ASSERT_TRUE(ac.toCommon().power); + ASSERT_TRUE(ac.toCommon().celsius); + ASSERT_EQ(20, ac.toCommon().degrees); + ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode); + ASSERT_EQ(stdAc::fanspeed_t::kMax, ac.toCommon().fanspeed); + ASSERT_EQ(stdAc::swingv_t::kAuto, ac.toCommon().swingv); + ASSERT_TRUE(ac.toCommon().turbo); + ASSERT_TRUE(ac.toCommon().filter); + // Unsupported. + ASSERT_EQ(stdAc::swingh_t::kOff, ac.toCommon().swingh); + ASSERT_FALSE(ac.toCommon().econo); + ASSERT_FALSE(ac.toCommon().light); + ASSERT_FALSE(ac.toCommon().clean); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_FALSE(ac.toCommon().quiet); + ASSERT_EQ(-1, ac.toCommon().sleep); + ASSERT_EQ(-1, ac.toCommon().clock); +} diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Whirlpool_test.cpp b/lib/IRremoteESP8266-2.6.5/test/ir_Whirlpool_test.cpp similarity index 93% rename from lib/IRremoteESP8266-2.6.0/test/ir_Whirlpool_test.cpp rename to lib/IRremoteESP8266-2.6.5/test/ir_Whirlpool_test.cpp index e282989f0..6349a8154 100644 --- a/lib/IRremoteESP8266-2.6.0/test/ir_Whirlpool_test.cpp +++ b/lib/IRremoteESP8266-2.6.5/test/ir_Whirlpool_test.cpp @@ -70,7 +70,7 @@ TEST(TestDecodeWhirlpoolAC, SyntheticDecode) { ac.setRaw(irsend.capture.state); EXPECT_EQ( "Model: 1 (DG11J13A), Power toggle: Off, Mode: 1 (AUTO), Temp: 25C, " - "Fan: 0 (AUTO), Swing: Off, Light: On, Clock: 17:31, On Timer: Off, " + "Fan: 0 (Auto), Swing: Off, Light: On, Clock: 17:31, On Timer: Off, " "Off Timer: Off, Sleep: Off, Super: Off, Command: 2 (TEMP)", ac.toString()); } @@ -94,7 +94,7 @@ TEST(TestDecodeWhirlpoolAC, Real26CFanAutoCoolingSwingOnClock1918) { ac.setRaw(irsend.capture.state); EXPECT_EQ( "Model: 1 (DG11J13A), Power toggle: Off, Mode: 2 (COOL), Temp: 26C, " - "Fan: 0 (AUTO), Swing: On, Light: On, Clock: 19:18, On Timer: Off, " + "Fan: 0 (Auto), Swing: On, Light: On, Clock: 19:18, On Timer: Off, " "Off Timer: Off, Sleep: Off, Super: Off, Command: 7 (SWING)", ac.toString()); } @@ -149,7 +149,7 @@ TEST(TestDecodeWhirlpoolAC, RealTimerExample) { ac.setRaw(irsend.capture.state); EXPECT_EQ( "Model: 1 (DG11J13A), Power toggle: Off, Mode: 3 (DRY), Temp: 25C, " - "Fan: 0 (AUTO), Swing: Off, Light: On, Clock: 07:35, On Timer: 07:40, " + "Fan: 0 (Auto), Swing: Off, Light: On, Clock: 07:35, On Timer: 07:40, " "Off Timer: 08:05, Sleep: Off, Super: Off, Command: 5 (ONTIMER)", ac.toString()); } @@ -161,7 +161,7 @@ TEST(TestDecodeWhirlpoolAC, RealExampleDecode) { irsend.begin(); // Real WhirlpoolAC message. - // Ref: https://github.com/markszabo/IRremoteESP8266/issues/509 + // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/509 uint16_t rawData[343] = { 8950, 4484, 598, 1642, 598, 1646, 594, 534, 594, 538, 602, 532, 598, 540, 600, 542, 598, 1650, 600, 522, 598, 1644, 596, 1650, @@ -207,7 +207,7 @@ TEST(TestDecodeWhirlpoolAC, RealExampleDecode) { ac.setRaw(irsend.capture.state); EXPECT_EQ( "Model: 1 (DG11J13A), Power toggle: Off, Mode: 1 (AUTO), Temp: 25C, " - "Fan: 0 (AUTO), Swing: Off, Light: On, Clock: 17:31, On Timer: Off, " + "Fan: 0 (Auto), Swing: Off, Light: On, Clock: 17:31, On Timer: Off, " "Off Timer: Off, Sleep: Off, Super: Off, Command: 2 (TEMP)", ac.toString()); } @@ -324,25 +324,18 @@ TEST(TestIRWhirlpoolAcClass, SetAndGetClock) { IRWhirlpoolAc ac(0); ac.setClock(0); EXPECT_EQ(0, ac.getClock()); - EXPECT_EQ("00:00", ac.timeToString(ac.getClock())); ac.setClock(1); EXPECT_EQ(1, ac.getClock()); - EXPECT_EQ("00:01", ac.timeToString(ac.getClock())); ac.setClock(12 * 60 + 34); EXPECT_EQ(12 * 60 + 34, ac.getClock()); - EXPECT_EQ("12:34", ac.timeToString(ac.getClock())); ac.setClock(7 * 60 + 5); EXPECT_EQ(7 * 60 + 5, ac.getClock()); - EXPECT_EQ("07:05", ac.timeToString(ac.getClock())); ac.setClock(23 * 60 + 59); EXPECT_EQ(23 * 60 + 59, ac.getClock()); - EXPECT_EQ("23:59", ac.timeToString(ac.getClock())); ac.setClock(24 * 60 + 0); EXPECT_EQ(0, ac.getClock()); - EXPECT_EQ("00:00", ac.timeToString(ac.getClock())); ac.setClock(25 * 60 + 23); EXPECT_EQ(1 * 60 + 23, ac.getClock()); - EXPECT_EQ("01:23", ac.timeToString(ac.getClock())); } TEST(TestIRWhirlpoolAcClass, OnOffTimers) { @@ -353,56 +346,42 @@ TEST(TestIRWhirlpoolAcClass, OnOffTimers) { ac.enableOnTimer(false); ac.setOnTimer(0); EXPECT_EQ(0, ac.getOnTimer()); - EXPECT_EQ("00:00", ac.timeToString(ac.getOnTimer())); EXPECT_FALSE(ac.isOnTimerEnabled()); EXPECT_EQ(kWhirlpoolAcCommandOnTimer, ac.getCommand()); ac.setOnTimer(1); EXPECT_EQ(1, ac.getOnTimer()); - EXPECT_EQ("00:01", ac.timeToString(ac.getOnTimer())); ac.enableOnTimer(true); ac.setOnTimer(12 * 60 + 34); EXPECT_EQ(12 * 60 + 34, ac.getOnTimer()); - EXPECT_EQ("12:34", ac.timeToString(ac.getOnTimer())); EXPECT_TRUE(ac.isOnTimerEnabled()); ac.setOnTimer(7 * 60 + 5); EXPECT_EQ(7 * 60 + 5, ac.getOnTimer()); - EXPECT_EQ("07:05", ac.timeToString(ac.getOnTimer())); ac.setOnTimer(23 * 60 + 59); EXPECT_EQ(23 * 60 + 59, ac.getOnTimer()); - EXPECT_EQ("23:59", ac.timeToString(ac.getOnTimer())); ac.setOnTimer(24 * 60 + 0); EXPECT_EQ(0, ac.getOnTimer()); - EXPECT_EQ("00:00", ac.timeToString(ac.getOnTimer())); ac.setOnTimer(25 * 60 + 23); EXPECT_EQ(1 * 60 + 23, ac.getOnTimer()); - EXPECT_EQ("01:23", ac.timeToString(ac.getOnTimer())); // Off Timer ac.enableOffTimer(false); ac.setOffTimer(0); EXPECT_EQ(0, ac.getOffTimer()); - EXPECT_EQ("00:00", ac.timeToString(ac.getOffTimer())); EXPECT_FALSE(ac.isOffTimerEnabled()); EXPECT_EQ(kWhirlpoolAcCommandOffTimer, ac.getCommand()); ac.setOffTimer(1); EXPECT_EQ(1, ac.getOffTimer()); - EXPECT_EQ("00:01", ac.timeToString(ac.getOffTimer())); ac.enableOffTimer(true); ac.setOffTimer(12 * 60 + 34); EXPECT_EQ(12 * 60 + 34, ac.getOffTimer()); - EXPECT_EQ("12:34", ac.timeToString(ac.getOffTimer())); EXPECT_TRUE(ac.isOffTimerEnabled()); ac.setOffTimer(7 * 60 + 5); EXPECT_EQ(7 * 60 + 5, ac.getOffTimer()); - EXPECT_EQ("07:05", ac.timeToString(ac.getOffTimer())); ac.setOffTimer(23 * 60 + 59); EXPECT_EQ(23 * 60 + 59, ac.getOffTimer()); - EXPECT_EQ("23:59", ac.timeToString(ac.getOffTimer())); ac.setOffTimer(24 * 60 + 0); EXPECT_EQ(0, ac.getOffTimer()); - EXPECT_EQ("00:00", ac.timeToString(ac.getOffTimer())); ac.setOffTimer(25 * 60 + 23); EXPECT_EQ(1 * 60 + 23, ac.getOffTimer()); - EXPECT_EQ("01:23", ac.timeToString(ac.getOffTimer())); } TEST(TestIRWhirlpoolAcClass, SetAndGetCommand) { @@ -578,8 +557,41 @@ TEST(TestIRWhirlpoolAcClass, MessageConstruction) { EXPECT_EQ( "Model: 1 (DG11J13A), Power toggle: Off, Mode: 3 (DRY), Temp: 25C, " - "Fan: 0 (AUTO), Swing: Off, Light: On, Clock: 07:35, On Timer: 07:40, " + "Fan: 0 (Auto), Swing: Off, Light: On, Clock: 07:35, On Timer: 07:40, " "Off Timer: 08:05, Sleep: Off, Super: Off, Command: 5 (ONTIMER)", ac.toString()); EXPECT_STATE_EQ(expectedState, ac.getRaw(), kWhirlpoolAcBits); } + +TEST(TestIRWhirlpoolAcClass, toCommon) { + IRWhirlpoolAc ac(0); + ac.setModel(whirlpool_ac_remote_model_t::DG11J13A); + ac.setPowerToggle(true); + ac.setMode(kWhirlpoolAcCool); + ac.setTemp(18); + ac.setFan(kWhirlpoolAcFanHigh); + ac.setSwing(true); + ac.setSuper(true); + ac.setLight(true); + ac.setSleep(false); + // Now test it. + ASSERT_EQ(decode_type_t::WHIRLPOOL_AC, ac.toCommon().protocol); + ASSERT_EQ(whirlpool_ac_remote_model_t::DG11J13A, ac.toCommon().model); + ASSERT_TRUE(ac.toCommon().power); + ASSERT_TRUE(ac.toCommon().celsius); + ASSERT_EQ(18, ac.toCommon().degrees); + ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode); + ASSERT_EQ(stdAc::fanspeed_t::kMax, ac.toCommon().fanspeed); + ASSERT_EQ(stdAc::swingv_t::kAuto, ac.toCommon().swingv); + ASSERT_TRUE(ac.toCommon().turbo); + ASSERT_TRUE(ac.toCommon().light); + ASSERT_EQ(-1, ac.toCommon().sleep); + // Unsupported. + ASSERT_EQ(stdAc::swingh_t::kOff, ac.toCommon().swingh); + ASSERT_FALSE(ac.toCommon().econo); + ASSERT_FALSE(ac.toCommon().filter); + ASSERT_FALSE(ac.toCommon().clean); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_FALSE(ac.toCommon().quiet); + ASSERT_EQ(-1, ac.toCommon().clock); +} diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Whynter_test.cpp b/lib/IRremoteESP8266-2.6.5/test/ir_Whynter_test.cpp similarity index 97% rename from lib/IRremoteESP8266-2.6.0/test/ir_Whynter_test.cpp rename to lib/IRremoteESP8266-2.6.5/test/ir_Whynter_test.cpp index 92ced5cf6..0c45f2654 100644 --- a/lib/IRremoteESP8266-2.6.0/test/ir_Whynter_test.cpp +++ b/lib/IRremoteESP8266-2.6.5/test/ir_Whynter_test.cpp @@ -151,7 +151,7 @@ TEST(TestDecodeWhynter, NormalDecodeWithStrict) { irsend.reset(); irsend.sendWhynter(0x87654321); irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decodeWhynter(&irsend.capture, kWhynterBits, true)); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); EXPECT_EQ(WHYNTER, irsend.capture.decode_type); EXPECT_EQ(kWhynterBits, irsend.capture.bits); EXPECT_EQ(0x87654321, irsend.capture.value); @@ -170,20 +170,20 @@ TEST(TestDecodeWhynter, NormalDecodeWithRepeatAndStrict) { irsend.reset(); irsend.sendWhynter(0x87654321, kWhynterBits, 2); irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decodeWhynter(&irsend.capture, kWhynterBits, true)); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); EXPECT_EQ(WHYNTER, irsend.capture.decode_type); EXPECT_EQ(kWhynterBits, irsend.capture.bits); EXPECT_EQ(0x87654321, irsend.capture.value); EXPECT_FALSE(irsend.capture.repeat); irsend.makeDecodeResult(2 * kWhynterBits + 6); - ASSERT_TRUE(irrecv.decodeWhynter(&irsend.capture, kWhynterBits, true)); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); EXPECT_EQ(WHYNTER, irsend.capture.decode_type); EXPECT_EQ(kWhynterBits, irsend.capture.bits); EXPECT_EQ(0x87654321, irsend.capture.value); irsend.makeDecodeResult(2 * (2 * kWhynterBits + 6)); - ASSERT_TRUE(irrecv.decodeWhynter(&irsend.capture, kWhynterBits, true)); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); EXPECT_EQ(WHYNTER, irsend.capture.decode_type); EXPECT_EQ(kWhynterBits, irsend.capture.bits); EXPECT_EQ(0x87654321, irsend.capture.value); diff --git a/lib/IRremoteESP8266-2.6.0/tools/Makefile b/lib/IRremoteESP8266-2.6.5/tools/Makefile similarity index 86% rename from lib/IRremoteESP8266-2.6.0/tools/Makefile rename to lib/IRremoteESP8266-2.6.5/tools/Makefile index 08488949c..d2da05eb3 100644 --- a/lib/IRremoteESP8266-2.6.0/tools/Makefile +++ b/lib/IRremoteESP8266-2.6.5/tools/Makefile @@ -29,7 +29,7 @@ run_tests : all failed=""; \ for py_unittest in *_test.py; do \ echo "RUNNING: $${py_unittest}"; \ - python ./$${py_unittest} || failed="$${failed} $${py_unittest}"; \ + python3 ./$${py_unittest} || failed="$${failed} $${py_unittest}"; \ done; \ if [ -n "$${failed}" ]; then \ echo "FAIL: :-( :-( Unit test(s)$${failed} failed! :-( :-("; exit 1; \ @@ -50,7 +50,8 @@ PROTOCOLS = ir_NEC.o ir_Sony.o ir_Samsung.o ir_JVC.o ir_RCMM.o ir_RC5_RC6.o \ ir_Magiquest.o ir_Lasertag.o ir_Carrier.o ir_Haier.o ir_Hitachi.o \ ir_GICable.o ir_Whirlpool.o ir_Lutron.o ir_Electra.o ir_Pioneer.o \ ir_MWM.o ir_Vestel.o ir_Teco.o ir_Tcl.o ir_Lego.o \ - ir_MitsubishiHeavy.o + ir_MitsubishiHeavy.o ir_Goodweather.o ir_Inax.o ir_Argo.o \ + ir_Trotec.o ir_Neoclima.o ir_Amcor.o # Common object files COMMON_OBJ = IRutils.o IRtimer.o IRsend.o IRrecv.o $(PROTOCOLS) @@ -104,6 +105,9 @@ ir_Samsung.o : $(USER_DIR)/ir_Samsung.cpp $(USER_DIR)/ir_Samsung.h $(COMMON_DEPS ir_Kelvinator.o : $(USER_DIR)/ir_Kelvinator.cpp $(USER_DIR)/ir_Kelvinator.h $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Kelvinator.cpp +ir_Inax.o : $(USER_DIR)/ir_Inax.cpp $(COMMON_DEPS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Inax.cpp + ir_JVC.o : $(USER_DIR)/ir_JVC.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_JVC.cpp @@ -122,8 +126,8 @@ ir_MitsubishiHeavy.o : $(USER_DIR)/ir_MitsubishiHeavy.h $(USER_DIR)/ir_Mitsubish ir_Fujitsu.o : $(USER_DIR)/ir_Fujitsu.h $(USER_DIR)/ir_Fujitsu.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Fujitsu.cpp -ir_Sharp.o : $(USER_DIR)/ir_Sharp.cpp $(COMMON_DEPS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Sharp.cpp +ir_Sharp.o : $(USER_DIR)/ir_Sharp.h $(USER_DIR)/ir_Sharp.cpp $(COMMON_DEPS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Sharp.cpp ir_RC5_RC6.o : $(USER_DIR)/ir_RC5_RC6.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_RC5_RC6.cpp @@ -192,7 +196,7 @@ ir_Lutron.o : $(USER_DIR)/ir_Lutron.cpp $(GTEST_HEADERS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Lutron.cpp ir_Electra.o : $(USER_DIR)/ir_Electra.cpp $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Electra.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Electra.cpp ir_Pioneer.o : $(USER_DIR)/ir_Pioneer.cpp $(GTEST_HEADERS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Pioneer.cpp @@ -209,5 +213,20 @@ ir_Teco.o : $(USER_DIR)/ir_Teco.cpp $(GTEST_HEADERS) ir_Tcl.o : $(USER_DIR)/ir_Tcl.cpp $(USER_DIR)/ir_Tcl.h $(GTEST_HEADERS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Tcl.cpp +ir_Trotec.o : $(USER_DIR)/ir_Trotec.cpp $(USER_DIR)/ir_Trotec.h $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Trotec.cpp + ir_Lego.o : $(USER_DIR)/ir_Lego.cpp $(GTEST_HEADERS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Lego.cpp + +ir_Argo.o : $(USER_DIR)/ir_Argo.cpp $(USER_DIR)/ir_Argo.h $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Argo.cpp + +ir_Goodweather.o : $(USER_DIR)/ir_Goodweather.cpp $(USER_DIR)/ir_Goodweather.h $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Goodweather.cpp + +ir_Neoclima.o : $(USER_DIR)/ir_Neoclima.cpp $(USER_DIR)/ir_Neoclima.h $(COMMON_DEPS) $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Neoclima.cpp + +ir_Amcor.o : $(USER_DIR)/ir_Amcor.cpp $(USER_DIR)/ir_Amcor.h $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Amcor.cpp diff --git a/lib/IRremoteESP8266-2.6.0/tools/RawToGlobalCache.sh b/lib/IRremoteESP8266-2.6.5/tools/RawToGlobalCache.sh similarity index 100% rename from lib/IRremoteESP8266-2.6.0/tools/RawToGlobalCache.sh rename to lib/IRremoteESP8266-2.6.5/tools/RawToGlobalCache.sh diff --git a/lib/IRremoteESP8266-2.6.0/tools/auto_analyse_raw_data.py b/lib/IRremoteESP8266-2.6.5/tools/auto_analyse_raw_data.py similarity index 98% rename from lib/IRremoteESP8266-2.6.0/tools/auto_analyse_raw_data.py rename to lib/IRremoteESP8266-2.6.5/tools/auto_analyse_raw_data.py index b23cdb46f..8a2e45794 100644 --- a/lib/IRremoteESP8266-2.6.0/tools/auto_analyse_raw_data.py +++ b/lib/IRremoteESP8266-2.6.5/tools/auto_analyse_raw_data.py @@ -8,7 +8,7 @@ import argparse import sys -class RawIRMessage(object): +class RawIRMessage(): """Basic analyse functions & structure for raw IR messages.""" # pylint: disable=too-many-instance-attributes @@ -62,7 +62,7 @@ class RawIRMessage(object): def _usec_compare(self, seen, expected): """Compare two usec values and see if they match within a subtractive margin.""" - return seen <= expected and seen > expected - self.margin + return expected - self.margin < seen <= expected def _usec_compares(self, usecs, expecteds): """Compare a usec value to a list of values and return True @@ -160,7 +160,7 @@ class RawIRMessage(object): def avg_list(items): """Return the average of a list of numbers.""" if items: - return sum(items) / len(items) + return int(sum(items) / len(items)) return 0 @@ -293,7 +293,7 @@ def decode_data(message, defines, function_code, output=sys.stdout): output.write("kHdrSpace+") function_code.append(" space(kHdrSpace);") elif message.is_bit_mark(usec) and count % 2: - if state != "HS" and state != "BS": + if state not in ("HS", "BS"): output.write("kBitMark(UNEXPECTED)") state = "BM" elif message.is_zero_space(usec): diff --git a/lib/IRremoteESP8266-2.6.0/tools/auto_analyse_raw_data_test.py b/lib/IRremoteESP8266-2.6.5/tools/auto_analyse_raw_data_test.py similarity index 97% rename from lib/IRremoteESP8266-2.6.0/tools/auto_analyse_raw_data_test.py rename to lib/IRremoteESP8266-2.6.5/tools/auto_analyse_raw_data_test.py index 681ff5520..5d5504ffc 100644 --- a/lib/IRremoteESP8266-2.6.0/tools/auto_analyse_raw_data_test.py +++ b/lib/IRremoteESP8266-2.6.5/tools/auto_analyse_raw_data_test.py @@ -1,6 +1,6 @@ -#!/usr/bin/python +#!/usr/bin/python3 """Unit tests for auto_analyse_raw_data.py""" -import StringIO +from io import StringIO import unittest import auto_analyse_raw_data as analyse @@ -12,7 +12,7 @@ class TestRawIRMessage(unittest.TestCase): def test_display_binary(self): """Test the display_binary() method.""" - output = StringIO.StringIO() + output = StringIO() message = analyse.RawIRMessage(100, [8000, 4000, 500, 500, 500], output, False) self.assertEqual(output.getvalue(), '') @@ -52,8 +52,8 @@ class TestAutoAnalyseRawData(unittest.TestCase): def test_dump_constants_simple(self): """Simple tests for the dump_constants() function.""" - ignore = StringIO.StringIO() - output = StringIO.StringIO() + ignore = StringIO() + output = StringIO() defs = [] message = analyse.RawIRMessage(200, [ 7930, 3952, 494, 1482, 520, 1482, 494, 1508, 494, 520, 494, 1482, 494, @@ -75,8 +75,8 @@ class TestAutoAnalyseRawData(unittest.TestCase): def test_dump_constants_aircon(self): """More complex tests for the dump_constants() function.""" - ignore = StringIO.StringIO() - output = StringIO.StringIO() + ignore = StringIO() + output = StringIO() defs = [] message = analyse.RawIRMessage(200, [ 9008, 4496, 644, 1660, 676, 530, 648, 558, 672, 1636, 646, 1660, 644, @@ -111,7 +111,7 @@ class TestAutoAnalyseRawData(unittest.TestCase): self.assertEqual(analyse.convert_rawdata("0"), [0]) with self.assertRaises(ValueError) as context: analyse.convert_rawdata("") - self.assertEqual(context.exception.message, + self.assertEqual(str(context.exception), "Raw Data contains a non-numeric value of ''.") # Single parenthesis @@ -132,13 +132,13 @@ class TestAutoAnalyseRawData(unittest.TestCase): # Bad parentheses with self.assertRaises(ValueError) as context: analyse.convert_rawdata("}10{") - self.assertEqual(context.exception.message, + self.assertEqual(str(context.exception), "Raw Data not parsible due to parentheses placement.") # Non base-10 values with self.assertRaises(ValueError) as context: analyse.convert_rawdata("10, 20, foo, bar, 30") - self.assertEqual(context.exception.message, + self.assertEqual(str(context.exception), "Raw Data contains a non-numeric value of 'foo'.") # A messy usual "good" case. @@ -155,7 +155,7 @@ class TestAutoAnalyseRawData(unittest.TestCase): """Tests for the parse_and_report() function.""" # Without code generation. - output = StringIO.StringIO() + output = StringIO() input_str = """ uint16_t rawbuf[139] = {9008, 4496, 644, 1660, 676, 530, 648, 558, 672, 1636, 646, 1660, 644, 556, 650, 584, 626, 560, 644, 580, 628, 1680, @@ -210,7 +210,7 @@ class TestAutoAnalyseRawData(unittest.TestCase): 'Total Nr. of suspected bits: 67\n') # With code generation. - output = StringIO.StringIO() + output = StringIO() input_str = """ uint16_t rawbuf[37] = {7930, 3952, 494, 1482, 520, 1482, 494, 1508, 494, 520, 494, 1482, 494, 520, 494, 1482, 494, 1482, 494, @@ -294,7 +294,7 @@ class TestAutoAnalyseRawData(unittest.TestCase): """Tests for unusual Space Gaps in parse_and_report() function.""" # Tests for unusual Gaps. (Issue #482) - output = StringIO.StringIO() + output = StringIO() input_str = """ uint16_t rawbuf[272] = {3485, 3512, 864, 864, 864, 2620, 864, 864, 864, 2620, 864, 2620, 864, 2620, 864, 2620, 864, 2620, 864, 864, @@ -466,7 +466,7 @@ class TestAutoAnalyseRawData(unittest.TestCase): def test_reduce_list(self): """Tests for the reduce_list method.""" - ignore = StringIO.StringIO() + ignore = StringIO() message = analyse.RawIRMessage(200, [ 7930, 3952, 494, 1482, 520, 1482, 494, 1508, 494, 520, 494, 1482, 494, 520, 494, 1482, 494, 1482, 494, 3978, 494, 520, 494, 520, 494, 520, 494, diff --git a/lib/IRremoteESP8266-2.6.0/tools/gc_decode.cpp b/lib/IRremoteESP8266-2.6.5/tools/gc_decode.cpp similarity index 100% rename from lib/IRremoteESP8266-2.6.0/tools/gc_decode.cpp rename to lib/IRremoteESP8266-2.6.5/tools/gc_decode.cpp diff --git a/lib/IRremoteESP8266-2.6.0/tools/mkkeywords b/lib/IRremoteESP8266-2.6.5/tools/mkkeywords similarity index 82% rename from lib/IRremoteESP8266-2.6.0/tools/mkkeywords rename to lib/IRremoteESP8266-2.6.5/tools/mkkeywords index a2cdccd9b..3172383ad 100644 --- a/lib/IRremoteESP8266-2.6.0/tools/mkkeywords +++ b/lib/IRremoteESP8266-2.6.5/tools/mkkeywords @@ -30,9 +30,8 @@ cat << EndOfTextEndOfTextEndOfText EndOfTextEndOfTextEndOfText -CLASSES=$(grep "^class " src/*.h | cut -d' ' -f2 | sort -u) -# Manually add typedefs as they are hard to parse out. -CLASSES="${CLASSES} ir_params_t match_result_t" +CLASSES=$(egrep -h "^ *((enum|class) |} [a-zA-Z0-9_]+_t;$)" src/*.h | + sed 's/^ *//;s/enum class//;s/\;$//' | cut -d' ' -f2 | sort -u) for i in ${CLASSES}; do echo -e "${i}\tKEYWORD1" done | sort -du @@ -44,10 +43,11 @@ cat << EndOfTextEndOfTextEndOfText ####################################### EndOfTextEndOfTextEndOfText - -METHODS=$(egrep "^(u?int|void|bool|String|static|match_result_t).*\(" src/*.cpp| - cut -d' ' -f2- | sed 's/^.*:://' | cut -d'(' -f1 | sort -u | - grep -v ICACHE_RAM_ATTR) +CTYPES="u?int(8|16|32|64)?(_t)?|void|bool|char|float|long|double|String|static" +OURTYPES="match_result_t|state_t|decode_type_t" +METHODS=$(egrep -h "^[ ]{0,2}(${CTYPES}|${OURTYPES})\*? [^ ]*\(" src/*.cpp | + sed 's/^ //' | cut -d' ' -f2 | sed 's/^\([^:]*::\| *\* *\)//' | + cut -d'(' -f1 | sort -u | grep -v RAM_ATTR) for i in ${METHODS}; do echo -e "${i}\tKEYWORD2" done | sort -u diff --git a/lib/IRremoteESP8266-2.6.0/tools/mode2_decode.cpp b/lib/IRremoteESP8266-2.6.5/tools/mode2_decode.cpp similarity index 100% rename from lib/IRremoteESP8266-2.6.0/tools/mode2_decode.cpp rename to lib/IRremoteESP8266-2.6.5/tools/mode2_decode.cpp diff --git a/lib/IRremoteESP8266-2.6.5/tools/scrape_supported_devices.py b/lib/IRremoteESP8266-2.6.5/tools/scrape_supported_devices.py new file mode 100644 index 000000000..574eac351 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.5/tools/scrape_supported_devices.py @@ -0,0 +1,255 @@ +#!/usr/bin/env python3 +"""Generate SupportedProtocols.md by scraping source code files""" +import pathlib +import argparse +import sys +import re +import time + +CODE_URL = "https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_" + +BRAND_MODEL = re.compile(r"Brand: *(?P.+), *Model: *(?P.+)") +ENUMS = re.compile(r"enum \w+ {(.+?)};", re.DOTALL) +ENUM_ENTRY = re.compile(r"^\s+(\w+)", re.MULTILINE) +DECODED_PROTOCOLS = re.compile(r".*results->decode_type *=.*?(\w+);") +AC_FN = re.compile(r"ir_(.+).h") + +ALL_FN = re.compile(r"ir_(.+).(h|cpp)") + +EXCLUDED_PROTOCOLS = ["UNKNOWN", "UNUSED", "kLastDecodeType"] + +MARKDOWN_HEADER = """""".format(time.asctime()) + + +def getallprotocols(): + """Return all protocls configured in IRremoteESP8266.h + """ + irremote = ARGS.directory / "IRremoteESP8266.h" + enums = getenums(irremote) + if not enums: + errorexit("Error getting ENUMS from IRremoteESP8266.h") + return enums + + +def getdecodedprotocols(): + """All protocols that include decoding support""" + ret = set() + for path in ARGS.directory.iterdir(): + if path.suffix != ".cpp": + continue + matches = DECODED_PROTOCOLS.finditer(path.open().read()) + for match in matches: + protocol = match.group(1) + if protocol not in EXCLUDED_PROTOCOLS: + ret.add(protocol) + return ret + + +def getallacs(): + """All supported A/C codes""" + ret = {} + for path in ARGS.directory.iterdir(): + match = AC_FN.match(path.name) + if not match: + continue + acprotocol = match.group(1) + rawmodels = getenums(path) + models = set() + for model in rawmodels: + model = model.upper() + model = model.replace("K{}".format(acprotocol.upper()), "") + if model and model not in EXCLUDED_PROTOCOLS: + models.add(model) + ret[acprotocol] = models + return ret + + +def getalldevices(): + """All devices and associated branding and model information (if available) + """ + allcodes = {} + fnnomatch = set() + fnmatch = set() + for path in ARGS.directory.iterdir(): + match = ALL_FN.match(path.name) + if not match: + continue + supports = extractsupports(path) + if supports: + fnmatch.add(path.stem) + else: + fnnomatch.add(path.stem) + protocol = match.group(1) + for brand, model in supports: + protocolbrand = (protocol, brand) + allcodes[protocolbrand] = allcodes.get(protocolbrand, list()) + [model] + nosupports = fnnomatch - fnmatch + for fnprotocol in nosupports: + allcodes[(fnprotocol[3:], "Unknown")] = [] + return allcodes, nosupports + + +def getenums(path): + """Returns the keys for the first enum type in path + """ + enums = ENUMS.search(path.open().read()) + ret = set() + if not enums: + return ret + for enum in ENUM_ENTRY.finditer(enums.group(1)): + enum = enum.group(1) + if enum in EXCLUDED_PROTOCOLS: + continue + ret.add(enum) + return ret + + +ARGS = None + + +def initargs(): + """Init the command line arguments""" + global ARGS # pylint: disable=global-statement + parser = argparse.ArgumentParser() + parser.add_argument( + "-s", + "--stdout", + help="output to stdout rather than SupportedProtocols.md", + action="store_true", + ) + parser.add_argument("-v", + "--verbose", + help="increase output verbosity", + action="store_true") + parser.add_argument( + "-a", + "--alert", + help="alert if a file does not have a supports section", + action="store_true", + ) + parser.add_argument( + "directory", + nargs="?", + help="directory of the source git checkout", + default=None, + ) + ARGS = parser.parse_args() + if ARGS.directory is None: + src = pathlib.Path("../src") + if not src.is_dir(): + src = pathlib.Path("./src") + else: + src = pathlib.Path(ARGS.directory) / "src" + if not src.is_dir(): + errorexit("Directory not valid: {}".format(str(src))) + ARGS.directory = src + return ARGS + + +def errorexit(msg): + """Print an error and exit on critical error""" + sys.stderr.write("{}\n".format(msg)) + sys.exit(1) + + +def extractsupports(path): + """Extract all of the Supports: sections and associated brands and models + """ + supports = [] + insupports = False + for line in path.open(): + if not line.startswith("//"): + continue + line = line[2:].strip() + if line == "Supports:": + insupports = True + continue + if insupports: + match = BRAND_MODEL.match(line) + if match: + supports.append((match.group("brand"), match.group("model"))) + else: + insupports = False + continue + return supports + + +def makeurl(txt, path): + """Make a Markup URL from given filename""" + return "[{}]({})".format(txt, CODE_URL + path) + + +def outputprotocols(fout, protocols): + """For a given protocol set, sort and output the markdown""" + protocols = list(protocols) + protocols.sort() + for protocol in protocols: + fout.write("- {}\n".format(protocol)) + + +def main(): + """Standard boiler plate""" + initargs() + if ARGS.verbose: + print("Looking for files in: {}".format(str(ARGS.directory.resolve()))) + if ARGS.stdout: + fout = sys.stdout + else: + foutpath = ARGS.directory / "../SupportedProtocols.md" + foutpath = foutpath.resolve() + if ARGS.verbose: + print("Output path: {}".format(str(foutpath))) + fout = foutpath.open("w") + decodedprotocols = getdecodedprotocols() + sendonly = getallprotocols() - decodedprotocols + allacs = getallacs() + + allcodes, nosupports = getalldevices() + allbrands = list(allcodes.keys()) + allbrands.sort() + + fout.write(MARKDOWN_HEADER) + fout.write("\n# IR Protocols supported by this library\n\n") + fout.write( + "| Protocol | Brand | Model | A/C Model | Detailed A/C Support |\n") + fout.write("| --- | --- | --- | --- | --- |\n") + + for protocolbrand in allbrands: + protocol, brand = protocolbrand + codes = allcodes[protocolbrand] + codes.sort() + if protocol in allacs: + acmodels = list(allacs[protocol]) + acmodels.sort() + acsupport = "Yes" + brand = makeurl(brand, protocol + ".h") + else: + acmodels = [] + acsupport = "-" + + fout.write("| {} | **{}** | {} | {} | {} |\n".format( + makeurl(protocol, protocol + ".cpp"), + brand, + "
".join(codes), + "
".join(acmodels), + acsupport, + )) + + fout.write("\n\n## Send only protocols:\n\n") + outputprotocols(fout, sendonly) + + fout.write("\n\n## Send & decodable protocols:\n\n") + outputprotocols(fout, decodedprotocols) + + if ARGS.alert: + nosupports = list(nosupports) + nosupports.sort() + print("The following files had no supports section:") + for path in nosupports: + print("\t{}".format(path)) + + +if __name__ == "__main__": + main() diff --git a/lib/JaretBurkett_ILI9488-gemu-1.0/ILI9488.cpp b/lib/JaretBurkett_ILI9488-gemu-1.0/ILI9488.cpp new file mode 100644 index 000000000..3e0aca9ee --- /dev/null +++ b/lib/JaretBurkett_ILI9488-gemu-1.0/ILI9488.cpp @@ -0,0 +1,1104 @@ +/*************************************************** + STM32 Support added by Jaret Burkett at OSHlab.com + + This is our library for the Adafruit ILI9488 Breakout and Shield + ----> http://www.adafruit.com/products/1651 + + Check out the links above for our tutorials and wiring diagrams + These displays use SPI to communicate, 4 or 5 pins are required to + interface (RST is optional) + Adafruit invests time and resources providing this open source code, + please support Adafruit and open-source hardware by purchasing + products from Adafruit! + + Written by Limor Fried/Ladyada for Adafruit Industries. + MIT license, all text above must be included in any redistribution + ****************************************************/ +#include +#include "ILI9488.h" +#include + +// if using software spi this optimizes the code +#define SWSPI_OPTMODE + +#define ILI9488_START start(); +#define ILI9488_STOP stop(); + +const uint16_t ili9488_colors[]={ILI9488_BLACK,ILI9488_WHITE,ILI9488_RED,ILI9488_GREEN,ILI9488_BLUE,ILI9488_CYAN,ILI9488_MAGENTA,\ + ILI9488_YELLOW,ILI9488_NAVY,ILI9488_DARKGREEN,ILI9488_DARKCYAN,ILI9488_MAROON,ILI9488_PURPLE,ILI9488_OLIVE,\ +ILI9488_LIGHTGREY,ILI9488_DARKGREY,ILI9488_ORANGE,ILI9488_GREENYELLOW,ILI9488_PINK}; + +// Constructor when using software SPI. All output pins are configurable. +ILI9488::ILI9488(int8_t cs,int8_t mosi,int8_t sclk,int8_t bp) : Renderer(ILI9488_TFTWIDTH, ILI9488_TFTHEIGHT) { + _cs = cs; + _mosi = mosi; + _sclk = sclk; + _bp = bp; + _hwspi = 0; +} + + +#include "spi_register.h" + +/* + +CPU Clock = 80 Mhz +max clock of display is 15 Mhz (66ns sclk cycle) +so cpu/8 => 10 Mhz should be ok + +HSPI CLK 5 GPIO14 +HSPI /CS 8 GPIO15 +HSPI MOSI 7 GPIO13 +HSPI MISO 6 GPIO12 + + +GPIO names for your easy reference: +GPIO0: PERIPHS_IO_MUX_GPIO0_U +GPIO1: PERIPHS_IO_MUX_U0TXD_U +GPIO2: PERIPHS_IO_MUX_GPIO2_U +GPIO3: PERIPHS_IO_MUX_U0RXD_U +GPIO4: PERIPHS_IO_MUX_GPIO4_U +GPIO5: PERIPHS_IO_MUX_GPIO5_U +GPIO6: PERIPHS_IO_MUX_SD_CLK_U +GPIO7: PERIPHS_IO_MUX_SD_DATA0_U +GPIO8: PERIPHS_IO_MUX_SD_DATA1_U +GPIO9: PERIPHS_IO_MUX_SD_DATA2_U +GPIO10: PERIPHS_IO_MUX_SD_DATA3_U +GPIO11: PERIPHS_IO_MUX_SD_CMD_U +GPIO12: PERIPHS_IO_MUX_MTDI_U +GPIO13: PERIPHS_IO_MUX_MTCK_U +GPIO14: PERIPHS_IO_MUX_MTMS_U +GPIO15: PERIPHS_IO_MUX_MTDO_U +*/ + +uint8_t ili9488_start; + +uint32_t ili9488_clock; +uint32_t ili9488_usr; +uint32_t ili9488_usr1; +uint32_t ili9488_usr2; +uint32_t ili9488_spi1c; +uint32_t ili9488_spi1c1; +uint32_t ili9488_spi1p; +uint32_t ili9488_gpmux; +uint32_t ili9488_mtdo; + + +uint32_t ili9488_clock_prev; +uint32_t ili9488_usr_prev; +uint32_t ili9488_usr1_prev; +uint32_t ili9488_usr2_prev; +uint32_t ili9488_spi1c_prev; +uint32_t ili9488_spi1c1_prev; +uint32_t ili9488_spi1p_prev; +uint32_t ili9488_gpmux_prev; +uint32_t ili9488_mtdo_prev; + +// code from espressif SDK +/****************************************************************************** + * FunctionName : spi_lcd_mode_init + * Description : SPI master initial function for driving LCD 3 wire spi +*******************************************************************************/ +void ILI9488::spi_lcd_mode_init(void) { + + ili9488_clock_prev=SPI1CLK; + ili9488_usr_prev=SPI1U; + ili9488_usr1_prev=SPI1U1; + ili9488_usr2_prev=SPI1U2; + ili9488_spi1c_prev=SPI1C; + ili9488_spi1c1_prev=SPI1C1; + ili9488_spi1p_prev=SPI1P; + ili9488_gpmux_prev=GPMUX; + ili9488_mtdo_prev=READ_PERI_REG(PERIPHS_IO_MUX_MTDO_U); + + SPI1U = SPIUMOSI | SPIUDUPLEX | SPIUSSE; + SPI1U1=0; + SPI1C = 0; + SPI1C1 = 0; + + //bit9 of PERIPHS_IO_MUX should be cleared when HSPI clock doesn't equal CPU clock + //bit8 of PERIPHS_IO_MUX should be cleared when SPI clock doesn't equal CPU clock + + WRITE_PERI_REG(PERIPHS_IO_MUX, 0x105); //clear bit9 + //PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDI_U, 2);//configure miso to spi mode + PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, 2);//configure mosi to spi mode + PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTMS_U, 2);//configure sclk to spi mode + PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U, 2);//configure cs to spi mode + +// the current implementation leaves about 1 us between transfers ???? +// due to lack of documentation i could not find the reason +// skipping this would double the speed !!! + + //SET_PERI_REG_MASK(SPI_USER(1), SPI_CS_SETUP|SPI_CS_HOLD|SPI_USR_COMMAND); + + SET_PERI_REG_MASK(SPI_USER(1), SPI_USR_COMMAND); + + CLEAR_PERI_REG_MASK(SPI_USER(1), SPI_FLASH_MODE); + // SPI clock=CPU clock/8 => 10 Mhz + /* + WRITE_PERI_REG(SPI_CLOCK(1), + ((1&SPI_CLKDIV_PRE)<>1)|0x80; + else bytetemp=(low_8bit>>1)&0x7f; + + regvalue= ((8&SPI_USR_COMMAND_BITLEN)<>1)&0x7f; + + ILI9488_START + +//#define SPI_USR_COMMAND_BITLEN 0x0000000F +//#define SPI_USR_COMMAND_BITLEN_S 28 + + regvalue= ((8&SPI_USR_COMMAND_BITLEN)<>1)|0x80; + + ILI9488_START + + regvalue= ((8&SPI_USR_COMMAND_BITLEN)<=sizeof(ili9488_colors)/2) index=0; + return ili9488_colors[index]; +} + +void ILI9488::DisplayInit(int8_t p,int8_t size,int8_t rot,int8_t font) { + setRotation(rot); + invertDisplay(false); + setTextWrap(false); // Allow text to run off edges + cp437(true); + setTextFont(font&3); + setTextSize(size&7); + setTextColor(ILI9488_WHITE,ILI9488_BLACK); + setCursor(0,0); + fillScreen(ILI9488_BLACK); + ILI9488_STOP +} + +void ILI9488::DisplayOnff(int8_t on) { + if (on) { + writecommand(ILI9488_DISPON); //Display on + if (_bp>=0) { + digitalWrite(_bp,HIGH); + /* + writecommand(ILI9488_WRCTRLD); + writedata(0x0c); + writecommand(ILI9488_CAPC9); + writedata(0x3f);*/ + } + } else { + writecommand(ILI9488_DISPOFF); + if (_bp>=0) { + digitalWrite(_bp,LOW); + //writecommand(ILI9488_WRCTRLD); + //writedata(0x04); + } + } + ILI9488_STOP +} + + +void ILI9488::begin(void) { + pinMode(_cs, OUTPUT); + digitalWrite(_cs,HIGH); + pinMode(_sclk, OUTPUT); + pinMode(_mosi, OUTPUT); + if (_bp>=0) { + pinMode(_bp, OUTPUT); + digitalWrite(_bp,HIGH); + } + if ((_sclk==14) && (_mosi==13) && (_cs==15)) { + // we use hardware spi + _hwspi=1; + spi_lcd_mode_init(); + } else { + // we must use software spi + _hwspi=0; + } + ILI9488_START + delay(1); + + writecommand(0xE0); + writedata(0x00); + writedata(0x03); + writedata(0x09); + writedata(0x08); + writedata(0x16); + writedata(0x0A); + writedata(0x3F); + writedata(0x78); + writedata(0x4C); + writedata(0x09); + writedata(0x0A); + writedata(0x08); + writedata(0x16); + writedata(0x1A); + writedata(0x0F); + + + writecommand(0XE1); + writedata(0x00); + writedata(0x16); + writedata(0x19); + writedata(0x03); + writedata(0x0F); + writedata(0x05); + writedata(0x32); + writedata(0x45); + writedata(0x46); + writedata(0x04); + writedata(0x0E); + writedata(0x0D); + writedata(0x35); + writedata(0x37); + writedata(0x0F); + + writecommand(0XC0); //Power Control 1 + writedata(0x17); //Vreg1out + writedata(0x15); //Verg2out + + writecommand(0xC1); //Power Control 2 + writedata(0x41); //VGH,VGL + + writecommand(0xC5); //Power Control 3 + writedata(0x00); + writedata(0x12); //Vcom + writedata(0x80); + + writecommand(0x36); //Memory Access + writedata(0x48); + + writecommand(0x3A); // Interface Pixel Format + writedata(0x66); //18 bit + + writecommand(0XB0); // Interface Mode Control + writedata(0x80); //SDO NOT USE + + writecommand(0xB1); //Frame rate + writedata(0xA0); //60Hz + + writecommand(0xB4); //Display Inversion Control + writedata(0x02); //2-dot + + writecommand(0XB6); //Display Function Control RGB/MCU Interface Control + + writedata(0x02); //MCU + writedata(0x02); //Source,Gate scan dieection + + writecommand(0XE9); // Set Image Functio + writedata(0x00); // Disable 24 bit data + + writecommand(0xF7); // Adjust Control + writedata(0xA9); + writedata(0x51); + writedata(0x2C); + writedata(0x82); // D7 stream, loose + + writecommand(ILI9488_SLPOUT); //Exit Sleep + delay(120); + writecommand(ILI9488_DISPON); //Display on + + ILI9488_STOP +} +/* +void ILI9488::setScrollArea(uint16_t topFixedArea, uint16_t bottomFixedArea){ + writecommand(0x33); // Vertical scroll definition + writedata(topFixedArea >> 8); + writedata(topFixedArea); + writedata((_height - topFixedArea - bottomFixedArea) >> 8); + writedata(_height - topFixedArea - bottomFixedArea); + writedata(bottomFixedArea >> 8); + writedata(bottomFixedArea); + ILI9488_STOP +} +void ILI9488::scroll(uint16_t pixels){ + writecommand(0x37); // Vertical scrolling start address + writedata(pixels >> 8); + writedata(pixels); + ILI9488_STOP +}*/ + +void ILI9488::setAddrWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) { +uint8_t flag=0; + + if (!x0 && !y0 && !x1 && !y1) { + x0=0; + y0=0; + x1=_width; + y1=_height; + flag=1; + } + + if (x1>_width) x1=_width; + if (y1>_height) y1=_height; + + writecommand(ILI9488_CASET); // Column addr set + writedata(x0 >> 8); + writedata(x0 & 0xFF); // XSTART + writedata(x1 >> 8); + writedata(x1 & 0xFF); // XEND + + writecommand(ILI9488_PASET); // Row addr set + writedata(y0>>8); + writedata(y0 &0xff); // YSTART + writedata(y1>>8); + writedata(y1 &0xff); // YEND + + writecommand(ILI9488_RAMWR); // write to RAM + if (flag) ILI9488_STOP +} + +/* +void ILI9488::drawImage(const uint8_t* img, uint16_t x, uint16_t y, uint16_t w, uint16_t h){ +return; + // rudimentary clipping (drawChar w/big text requires this) + if((x >= _width) || (y >= _height)) return; + if((x + w - 1) >= _width) w = _width - x; + if((y + h - 1) >= _height) h = _height - y; + + setAddrWindow(x, y, x+w-1, y+h-1); + + // uint8_t hi = color >> 8, lo = color; + + #if defined(USE_FAST_PINIO) && !defined (_VARIANT_ARDUINO_STM32_) + *dcport |= dcpinmask; + *csport &= ~cspinmask; + #else + digitalWrite(_dc, HIGH); + digitalWrite(_cs, LOW); + #endif + uint8_t linebuff[w*3+1]; + uint16_t pixels = w*h; + // uint16_t count = 0; + uint32_t count = 0; + for (uint16_t i = 0; i < h; i++) { + uint16_t pixcount = 0; + for (uint16_t o = 0; o < w; o++) { + uint8_t b1 = img[count]; + count++; + uint8_t b2 = img[count]; + count++; + uint16_t color = b1 << 8 | b2; + linebuff[pixcount] = (((color & 0xF800) >> 11)* 255) / 31; + pixcount++; + linebuff[pixcount] = (((color & 0x07E0) >> 5) * 255) / 63; + pixcount++; + linebuff[pixcount] = ((color & 0x001F)* 255) / 31; + pixcount++; + } // for row + #if defined (__STM32F1__) + SPI.dmaSend(linebuff, w*3); + #else + for(uint16_t b = 0; b < w*3; b++){ + spiwrite(linebuff[b]); + } + #endif + + }// for col + #if defined(USE_FAST_PINIO) && !defined (_VARIANT_ARDUINO_STM32_) + *csport |= cspinmask; + #else + digitalWrite(_cs, HIGH); + #endif + + if (hwSPI) spi_end(); +} +*/ + +void ILI9488::pushColor(uint16_t color) { + write16BitColor(color); + ILI9488_STOP +} + +void ILI9488::pushColors(uint16_t *data, uint8_t len, boolean first) { + uint16_t color; + uint8_t buff[len*3+1]; + uint16_t count = 0; + uint8_t lencount = len; + while(lencount--) { + color = *data++; + buff[count] = (((color & 0xF800) >> 11)* 255) / 31; + count++; + buff[count] = (((color & 0x07E0) >> 5) * 255) / 63; + count++; + buff[count] = ((color & 0x001F)* 255) / 31; + count++; + } + + for(uint16_t b = 0; b < len*3; b++){ + writedata(buff[b]); + } + + ILI9488_STOP + +} + +void ILI9488::write16BitColor(uint16_t color){ + // #if (__STM32F1__) + // uint8_t buff[4] = { + // (((color & 0xF800) >> 11)* 255) / 31, + // (((color & 0x07E0) >> 5) * 255) / 63, + // ((color & 0x001F)* 255) / 31 + // }; + // SPI.dmaSend(buff, 3); + // #else + uint8_t r = (color & 0xF800) >> 11; + uint8_t g = (color & 0x07E0) >> 5; + uint8_t b = color & 0x001F; + + r = (r * 255) / 31; + g = (g * 255) / 63; + b = (b * 255) / 31; + + #ifndef SWSPI_OPTMODE + writedata(r); + writedata(g); + writedata(b); + #else + if (_hwspi) { + writedata(r); + writedata(g); + writedata(b); + } else { + WRITE_PERI_REG( PIN_OUT_CLEAR, 1<<_cs); + WRITE_PERI_REG( PIN_OUT_CLEAR, 1<<_sclk); + WRITE_PERI_REG( PIN_OUT_SET, 1<<_mosi); + WRITE_PERI_REG( PIN_OUT_SET, 1<<_sclk); + + for(uint8_t bit = 0x80; bit; bit >>= 1) { + WRITE_PERI_REG( PIN_OUT_CLEAR, 1<<_sclk); + if(r&bit) WRITE_PERI_REG( PIN_OUT_SET, 1<<_mosi); + else WRITE_PERI_REG( PIN_OUT_CLEAR, 1<<_mosi); + WRITE_PERI_REG( PIN_OUT_SET, 1<<_sclk); + } + + WRITE_PERI_REG( PIN_OUT_CLEAR, 1<<_sclk); + WRITE_PERI_REG( PIN_OUT_SET, 1<<_mosi); + WRITE_PERI_REG( PIN_OUT_SET, 1<<_sclk); + + for(uint8_t bit = 0x80; bit; bit >>= 1) { + WRITE_PERI_REG( PIN_OUT_CLEAR, 1<<_sclk); + if(g&bit) WRITE_PERI_REG( PIN_OUT_SET, 1<<_mosi); + else WRITE_PERI_REG( PIN_OUT_CLEAR, 1<<_mosi); + WRITE_PERI_REG( PIN_OUT_SET, 1<<_sclk); + } + + WRITE_PERI_REG( PIN_OUT_CLEAR, 1<<_sclk); + WRITE_PERI_REG( PIN_OUT_SET, 1<<_mosi); + WRITE_PERI_REG( PIN_OUT_SET, 1<<_sclk); + + for(uint8_t bit = 0x80; bit; bit >>= 1) { + WRITE_PERI_REG( PIN_OUT_CLEAR, 1<<_sclk); + if(b&bit) WRITE_PERI_REG( PIN_OUT_SET, 1<<_mosi); + else WRITE_PERI_REG( PIN_OUT_CLEAR, 1<<_mosi); + WRITE_PERI_REG( PIN_OUT_SET, 1<<_sclk); + } + WRITE_PERI_REG( PIN_OUT_SET, 1<<_cs); + } + + #endif + ILI9488_STOP +} + +void ILI9488::drawPixel(int16_t x, int16_t y, uint16_t color) { + + if((x < 0) ||(x >= _width) || (y < 0) || (y >= _height)) return; + + setAddrWindow(x,y,x+1,y+1); + write16BitColor(color); + ILI9488_STOP +} + +void ILI9488::drawFastVLine(int16_t x, int16_t y, int16_t h, + uint16_t color) { + + // Rudimentary clipping + if((x >= _width) || (y >= _height)) return; + + if((y+h-1) >= _height) + h = _height-y; + + setAddrWindow(x, y, x, y+h-1); + + uint8_t r = (color & 0xF800) >> 11; + uint8_t g = (color & 0x07E0) >> 5; + uint8_t b = color & 0x001F; + + r = (r * 255) / 31; + g = (g * 255) / 63; + b = (b * 255) / 31; + + while (h--) { + #ifndef SWSPI_OPTMODE + writedata(r); + writedata(g); + writedata(b); + #else + if (_hwspi) { + writedata(r); + writedata(g); + writedata(b); + } else { + WRITE_PERI_REG( PIN_OUT_CLEAR, 1<<_cs); + WRITE_PERI_REG( PIN_OUT_CLEAR, 1<<_sclk); + WRITE_PERI_REG( PIN_OUT_SET, 1<<_mosi); + WRITE_PERI_REG( PIN_OUT_SET, 1<<_sclk); + + for(uint8_t bit = 0x80; bit; bit >>= 1) { + WRITE_PERI_REG( PIN_OUT_CLEAR, 1<<_sclk); + if(r&bit) WRITE_PERI_REG( PIN_OUT_SET, 1<<_mosi); + else WRITE_PERI_REG( PIN_OUT_CLEAR, 1<<_mosi); + WRITE_PERI_REG( PIN_OUT_SET, 1<<_sclk); + } + + WRITE_PERI_REG( PIN_OUT_CLEAR, 1<<_sclk); + WRITE_PERI_REG( PIN_OUT_SET, 1<<_mosi); + WRITE_PERI_REG( PIN_OUT_SET, 1<<_sclk); + + for(uint8_t bit = 0x80; bit; bit >>= 1) { + WRITE_PERI_REG( PIN_OUT_CLEAR, 1<<_sclk); + if(g&bit) WRITE_PERI_REG( PIN_OUT_SET, 1<<_mosi); + else WRITE_PERI_REG( PIN_OUT_CLEAR, 1<<_mosi); + WRITE_PERI_REG( PIN_OUT_SET, 1<<_sclk); + } + + WRITE_PERI_REG( PIN_OUT_CLEAR, 1<<_sclk); + WRITE_PERI_REG( PIN_OUT_SET, 1<<_mosi); + WRITE_PERI_REG( PIN_OUT_SET, 1<<_sclk); + + for(uint8_t bit = 0x80; bit; bit >>= 1) { + WRITE_PERI_REG( PIN_OUT_CLEAR, 1<<_sclk); + if(b&bit) WRITE_PERI_REG( PIN_OUT_SET, 1<<_mosi); + else WRITE_PERI_REG( PIN_OUT_CLEAR, 1<<_mosi); + WRITE_PERI_REG( PIN_OUT_SET, 1<<_sclk); + } + WRITE_PERI_REG( PIN_OUT_SET, 1<<_cs); + } + #endif + } + ILI9488_STOP +} + +void ILI9488::drawFastHLine(int16_t x, int16_t y, int16_t w, + uint16_t color) { + + // Rudimentary clipping + if((x >= _width) || (y >= _height)) return; + if((x+w-1) >= _width) w = _width-x; + + setAddrWindow(x, y, x+w-1, y); + + uint8_t r = (color & 0xF800) >> 11; + uint8_t g = (color & 0x07E0) >> 5; + uint8_t b = color & 0x001F; + + r = (r * 255) / 31; + g = (g * 255) / 63; + b = (b * 255) / 31; + + while (w--) { +#ifndef SWSPI_OPTMODE + writedata(r); + writedata(g); + writedata(b); +#else + if (_hwspi) { + writedata(r); + writedata(g); + writedata(b); + } else { + WRITE_PERI_REG( PIN_OUT_CLEAR, 1<<_cs); + WRITE_PERI_REG( PIN_OUT_CLEAR, 1<<_sclk); + WRITE_PERI_REG( PIN_OUT_SET, 1<<_mosi); + WRITE_PERI_REG( PIN_OUT_SET, 1<<_sclk); + + for(uint8_t bit = 0x80; bit; bit >>= 1) { + WRITE_PERI_REG( PIN_OUT_CLEAR, 1<<_sclk); + if(r&bit) WRITE_PERI_REG( PIN_OUT_SET, 1<<_mosi); + else WRITE_PERI_REG( PIN_OUT_CLEAR, 1<<_mosi); + WRITE_PERI_REG( PIN_OUT_SET, 1<<_sclk); + } + + WRITE_PERI_REG( PIN_OUT_CLEAR, 1<<_sclk); + WRITE_PERI_REG( PIN_OUT_SET, 1<<_mosi); + WRITE_PERI_REG( PIN_OUT_SET, 1<<_sclk); + + for(uint8_t bit = 0x80; bit; bit >>= 1) { + WRITE_PERI_REG( PIN_OUT_CLEAR, 1<<_sclk); + if(g&bit) WRITE_PERI_REG( PIN_OUT_SET, 1<<_mosi); + else WRITE_PERI_REG( PIN_OUT_CLEAR, 1<<_mosi); + WRITE_PERI_REG( PIN_OUT_SET, 1<<_sclk); + } + + WRITE_PERI_REG( PIN_OUT_CLEAR, 1<<_sclk); + WRITE_PERI_REG( PIN_OUT_SET, 1<<_mosi); + WRITE_PERI_REG( PIN_OUT_SET, 1<<_sclk); + + for(uint8_t bit = 0x80; bit; bit >>= 1) { + WRITE_PERI_REG( PIN_OUT_CLEAR, 1<<_sclk); + if(b&bit) WRITE_PERI_REG( PIN_OUT_SET, 1<<_mosi); + else WRITE_PERI_REG( PIN_OUT_CLEAR, 1<<_mosi); + WRITE_PERI_REG( PIN_OUT_SET, 1<<_sclk); + } + WRITE_PERI_REG( PIN_OUT_SET, 1<<_cs); + } +#endif + } + ILI9488_STOP +} + +// this moves 460 kbytes +// now at 475 ms with 13,3 Mhz clock +void ILI9488::fillScreen(uint16_t color) { + //uint32_t time=millis(); + fillRect(0, 0, _width, _height, color); + //time=millis()-time; + //Serial.printf("time %d ms\n",time); + ILI9488_STOP +} + + +//#define WAIT_9BITS asm_nop_9bits(); +#define WAIT_9BITS +//#define WAIT_BEFORE while(*((uint32_t *)0x60000100)&SPI_USR); //waiting for spi module available +#define WAIT_SPI_READY while(READ_PERI_REG(SPI_CMD(1))&SPI_USR); + +//#define WAIT_SPI_READY + +//#define DIAG_PIN_SET WRITE_PERI_REG( PIN_OUT_SET, 1<<2); +//#define DIAG_PIN_CLR WRITE_PERI_REG( PIN_OUT_CLEAR, 1<<2); +#define DIAG_PIN_SET +#define DIAG_PIN_CLR + +//#define WRITE_PERI_REG(addr, val) (*((volatile uint32_t *)ETS_UNCACHED_ADDR(addr))) = (uint32_t)(val) + + +// CMD 0x60000200-1*0x100 +// SPI_USER2 0x60000200-1*0x100 + 0x24 + +//#define WRITE_SPI_REG WRITE_PERI_REG(0x60000124, regvalue_r);SET_PERI_REG_MASK(0x60000100, SPI_USR); +// THIS TAKES 1 us => 80 cpu clock cycles !!!!!!!!!!!!!!!!!!!!!!!!!! +// probably the memw causes this delay +#define WRITE_SPI_REG(A) WRITE_PERI_REG(SPI_USER2(1), A); SET_PERI_REG_MASK(SPI_CMD(1), SPI_USR); + +//#define WRITE_SPI_REG(A) *((uint32_t *)0x60000124)=A; *((uint32_t *)0x60000100)|=SPI_USR; + +//#define WRITE_SPI_REG + +// this enables the 27 bit packed mode +#define RGB_PACK_MODE + +// extremely strange => if this code is merged into pack_rgb() the software crashes +// swap bytes +uint32_t ulswap(uint32_t data) { +union { + uint32_t l; + uint8_t b[4]; +} data_; + + data_.l = data; + // MSBFIRST Byte first + data = (data_.b[3] | (data_.b[2] << 8) | (data_.b[1] << 16) | (data_.b[0] << 24)); + return data; +} + +// pack RGB into uint32 +uint32_t pack_rgb(uint32_t r, uint32_t g, uint32_t b) { + uint32_t data; + data=r<<23; + data|=g<<14; + data|=b<<5; + data|=0b10000000010000000010000000000000; + return ulswap(data); +} + +// fill a rectangle +void ILI9488::fillRect(int16_t x, int16_t y, int16_t w, int16_t h, + uint16_t color) { + + ILI9488_START + // rudimentary clipping (drawChar w/big text requires this) + if((x >= _width) || (y >= _height)) return; + if((x + w - 1) >= _width) w = _width - x; + if((y + h - 1) >= _height) h = _height - y; + + setAddrWindow(x, y, x+w-1, y+h-1); + //ILI9488_START + + uint8_t r = (color & 0xF800) >> 11; + uint8_t g = (color & 0x07E0) >> 5; + uint8_t b = color & 0x001F; + + r = (r * 255) / 31; + g = (g * 255) / 63; + b = (b * 255) / 31; + + uint32_t regvalue_r,regvalue_g,regvalue_b; + uint32_t data; + + if (_hwspi) { + // precalculate the register values for rgb + +#ifndef RGB_PACK_MODE + uint8_t bytetemp; + bytetemp=(r>>1)|0x80; + regvalue_r= ((8&SPI_USR_COMMAND_BITLEN)<>1)|0x80; + regvalue_g= ((8&SPI_USR_COMMAND_BITLEN)<>1)|0x80; + regvalue_b= ((8&SPI_USR_COMMAND_BITLEN)< MSBFIRST + SPI1U = SPIUMOSI; + SPI1C1 = 0; + data=pack_rgb(r,g,b); + +#endif + } + + for(y=h; y>0; y--) { + delay(0); + for(x=w; x>0; x--) { + +#ifndef SWSPI_OPTMODE + writedata(r); + writedata(g); + writedata(b); +#else + if (_hwspi) { + //noInterrupts(); +#ifdef RGB_PACK_MODE + while(SPI1CMD & SPIBUSY) {} + SPI1W0 = data; + SPI1CMD |= SPIBUSY; +#else + DIAG_PIN_CLR + WAIT_SPI_READY + DIAG_PIN_SET + + WRITE_SPI_REG(regvalue_r) + WAIT_9BITS + + DIAG_PIN_CLR + WAIT_SPI_READY + DIAG_PIN_SET + + WRITE_SPI_REG(regvalue_g) + WAIT_9BITS + + DIAG_PIN_CLR + WAIT_SPI_READY + DIAG_PIN_SET + + WRITE_SPI_REG(regvalue_b) + WAIT_9BITS + + //interrupts(); +#endif + } else { + WRITE_PERI_REG( PIN_OUT_CLEAR, 1<<_cs); + + WRITE_PERI_REG( PIN_OUT_CLEAR, 1<<_sclk); + WRITE_PERI_REG( PIN_OUT_SET, 1<<_mosi); + WRITE_PERI_REG( PIN_OUT_SET, 1<<_sclk); + + for(uint8_t bit = 0x80; bit; bit >>= 1) { + WRITE_PERI_REG( PIN_OUT_CLEAR, 1<<_sclk); + if(r&bit) WRITE_PERI_REG( PIN_OUT_SET, 1<<_mosi); + else WRITE_PERI_REG( PIN_OUT_CLEAR, 1<<_mosi); + WRITE_PERI_REG( PIN_OUT_SET, 1<<_sclk); + } + + WRITE_PERI_REG( PIN_OUT_CLEAR, 1<<_sclk); + WRITE_PERI_REG( PIN_OUT_SET, 1<<_mosi); + WRITE_PERI_REG( PIN_OUT_SET, 1<<_sclk); + + for(uint8_t bit = 0x80; bit; bit >>= 1) { + WRITE_PERI_REG( PIN_OUT_CLEAR, 1<<_sclk); + if(g&bit) WRITE_PERI_REG( PIN_OUT_SET, 1<<_mosi); + else WRITE_PERI_REG( PIN_OUT_CLEAR, 1<<_mosi); + WRITE_PERI_REG( PIN_OUT_SET, 1<<_sclk); + } + + WRITE_PERI_REG( PIN_OUT_CLEAR, 1<<_sclk); + WRITE_PERI_REG( PIN_OUT_SET, 1<<_mosi); + WRITE_PERI_REG( PIN_OUT_SET, 1<<_sclk); + + for(uint8_t bit = 0x80; bit; bit >>= 1) { + WRITE_PERI_REG( PIN_OUT_CLEAR, 1<<_sclk); + if(b&bit) WRITE_PERI_REG( PIN_OUT_SET, 1<<_mosi); + else WRITE_PERI_REG( PIN_OUT_CLEAR, 1<<_mosi); + WRITE_PERI_REG( PIN_OUT_SET, 1<<_sclk); + } + WRITE_PERI_REG( PIN_OUT_SET, 1<<_cs); + } + #endif + + } + + } +#ifdef RGB_PACK_MODE + // reinit old mode + while(SPI1CMD & SPIBUSY) {} + ILI9488_STOP + //spi_lcd_mode_init(); +#endif + +} + + +// Pass 8-bit (each) R,G,B, get back 16-bit packed color +uint16_t ILI9488::color565(uint8_t r, uint8_t g, uint8_t b) { + return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3); +} + + +#define MADCTL_MY 0x80 +#define MADCTL_MX 0x40 +#define MADCTL_MV 0x20 +#define MADCTL_ML 0x10 +#define MADCTL_RGB 0x00 +#define MADCTL_BGR 0x08 +#define MADCTL_MH 0x04 + +void ILI9488::setRotation(uint8_t m) { + + writecommand(ILI9488_MADCTL); + rotation = m % 4; // can't be higher than 3 + switch (rotation) { + case 0: + writedata(MADCTL_MX | MADCTL_BGR); + _width = ILI9488_TFTWIDTH; + _height = ILI9488_TFTHEIGHT; + break; + case 1: + writedata(MADCTL_MV | MADCTL_BGR); + _width = ILI9488_TFTHEIGHT; + _height = ILI9488_TFTWIDTH; + break; + case 2: + writedata(MADCTL_MY | MADCTL_BGR); + _width = ILI9488_TFTWIDTH; + _height = ILI9488_TFTHEIGHT; + break; + case 3: + writedata(MADCTL_MX | MADCTL_MY | MADCTL_MV | MADCTL_BGR); + _width = ILI9488_TFTHEIGHT; + _height = ILI9488_TFTWIDTH; + break; + } + ILI9488_STOP +} + + +void ILI9488::invertDisplay(boolean i) { + writecommand(i ? ILI9488_INVON : ILI9488_INVOFF); + ILI9488_STOP +} + +void ICACHE_RAM_ATTR ILI9488::fastSPIwrite(uint8_t d,uint8_t dc) { + + WRITE_PERI_REG( PIN_OUT_CLEAR, 1<<_cs); + WRITE_PERI_REG( PIN_OUT_CLEAR, 1<<_sclk); + if(dc) WRITE_PERI_REG( PIN_OUT_SET, 1<<_mosi); + else WRITE_PERI_REG( PIN_OUT_CLEAR, 1<<_mosi); + WRITE_PERI_REG( PIN_OUT_SET, 1<<_sclk); + + for(uint8_t bit = 0x80; bit; bit >>= 1) { + WRITE_PERI_REG( PIN_OUT_CLEAR, 1<<_sclk); + if(d&bit) WRITE_PERI_REG( PIN_OUT_SET, 1<<_mosi); + else WRITE_PERI_REG( PIN_OUT_CLEAR, 1<<_mosi); + WRITE_PERI_REG( PIN_OUT_SET, 1<<_sclk); + } + WRITE_PERI_REG( PIN_OUT_SET, 1<<_cs); +} + +/* + + uint16_t ILI9488::readcommand16(uint8_t c) { + digitalWrite(_dc, LOW); + if (_cs) + digitalWrite(_cs, LOW); + + spiwrite(c); + pinMode(_sid, INPUT); // input! + uint16_t r = spiread(); + r <<= 8; + r |= spiread(); + if (_cs) + digitalWrite(_cs, HIGH); + + pinMode(_sid, OUTPUT); // back to output + return r; + } + + uint32_t ILI9488::readcommand32(uint8_t c) { + digitalWrite(_dc, LOW); + if (_cs) + digitalWrite(_cs, LOW); + spiwrite(c); + pinMode(_sid, INPUT); // input! + + dummyclock(); + dummyclock(); + + uint32_t r = spiread(); + r <<= 8; + r |= spiread(); + r <<= 8; + r |= spiread(); + r <<= 8; + r |= spiread(); + if (_cs) + digitalWrite(_cs, HIGH); + + pinMode(_sid, OUTPUT); // back to output + return r; + } + + */ diff --git a/lib/JaretBurkett_ILI9488-gemu-1.0/ILI9488.h b/lib/JaretBurkett_ILI9488-gemu-1.0/ILI9488.h new file mode 100644 index 000000000..89c7a909b --- /dev/null +++ b/lib/JaretBurkett_ILI9488-gemu-1.0/ILI9488.h @@ -0,0 +1,172 @@ +/*************************************************** + STM32 Support added by Jaret Burkett at OSHlab.com + + This is our library for the Adafruit ILI9488 Breakout and Shield + ----> http://www.adafruit.com/products/1651 + + Check out the links above for our tutorials and wiring diagrams + These displays use SPI to communicate, 4 or 5 pins are required to + interface (RST is optional) + Adafruit invests time and resources providing this open source code, + please support Adafruit and open-source hardware by purchasing + products from Adafruit! + + Written by Limor Fried/Ladyada for Adafruit Industries. + MIT license, all text above must be included in any redistribution + ****************************************************/ + +#ifndef _ILI9488H_ +#define _ILI9488H_ + +#if ARDUINO >= 100 + #include "Arduino.h" + #include "Print.h" +#else + #include "WProgram.h" +#endif +#include +#ifdef __AVR + #include +#elif defined(ESP8266) + #include +#endif + +#ifdef ARDUINO_STM32_FEATHER +typedef volatile uint32 RwReg; +#endif +#if defined (__AVR__) || defined(TEENSYDUINO) || defined (__arm__) || defined (__STM32F1__) +#define USE_FAST_PINIO +#endif + +#define ILI9488_TFTWIDTH 320 +#define ILI9488_TFTHEIGHT 480 + +#define ILI9488_NOP 0x00 +#define ILI9488_SWRESET 0x01 +#define ILI9488_RDDID 0x04 +#define ILI9488_RDDST 0x09 + +#define ILI9488_SLPIN 0x10 +#define ILI9488_SLPOUT 0x11 +#define ILI9488_PTLON 0x12 +#define ILI9488_NORON 0x13 + +#define ILI9488_RDMODE 0x0A +#define ILI9488_RDMADCTL 0x0B +#define ILI9488_RDPIXFMT 0x0C +#define ILI9488_RDIMGFMT 0x0D +#define ILI9488_RDSELFDIAG 0x0F + +#define ILI9488_INVOFF 0x20 +#define ILI9488_INVON 0x21 +#define ILI9488_GAMMASET 0x26 +#define ILI9488_DISPOFF 0x28 +#define ILI9488_DISPON 0x29 + +#define ILI9488_CASET 0x2A +#define ILI9488_PASET 0x2B +#define ILI9488_RAMWR 0x2C +#define ILI9488_RAMRD 0x2E + +#define ILI9488_PTLAR 0x30 +#define ILI9488_MADCTL 0x36 +#define ILI9488_PIXFMT 0x3A + +#define ILI9488_WRCTRLD 0x53 +#define ILI9488_CAPC9 0xCF + +#define ILI9488_FRMCTR1 0xB1 +#define ILI9488_FRMCTR2 0xB2 +#define ILI9488_FRMCTR3 0xB3 +#define ILI9488_INVCTR 0xB4 +#define ILI9488_DFUNCTR 0xB6 + +#define ILI9488_PWCTR1 0xC0 +#define ILI9488_PWCTR2 0xC1 +#define ILI9488_PWCTR3 0xC2 +#define ILI9488_PWCTR4 0xC3 +#define ILI9488_PWCTR5 0xC4 +#define ILI9488_VMCTR1 0xC5 +#define ILI9488_VMCTR2 0xC7 + +#define ILI9488_RDID1 0xDA +#define ILI9488_RDID2 0xDB +#define ILI9488_RDID3 0xDC +#define ILI9488_RDID4 0xDD + +#define ILI9488_GMCTRP1 0xE0 +#define ILI9488_GMCTRN1 0xE1 +/* +#define ILI9488_PWCTR6 0xFC + +*/ + +#define PIN_OUT_SET 0x60000304 +#define PIN_OUT_CLEAR 0x60000308 + +// Color definitions +#define ILI9488_BLACK 0x0000 /* 0, 0, 0 */ +#define ILI9488_NAVY 0x000F /* 0, 0, 128 */ +#define ILI9488_DARKGREEN 0x03E0 /* 0, 128, 0 */ +#define ILI9488_DARKCYAN 0x03EF /* 0, 128, 128 */ +#define ILI9488_MAROON 0x7800 /* 128, 0, 0 */ +#define ILI9488_PURPLE 0x780F /* 128, 0, 128 */ +#define ILI9488_OLIVE 0x7BE0 /* 128, 128, 0 */ +#define ILI9488_LIGHTGREY 0xC618 /* 192, 192, 192 */ +#define ILI9488_DARKGREY 0x7BEF /* 128, 128, 128 */ +#define ILI9488_BLUE 0x001F /* 0, 0, 255 */ +#define ILI9488_GREEN 0x07E0 /* 0, 255, 0 */ +#define ILI9488_CYAN 0x07FF /* 0, 255, 255 */ +#define ILI9488_RED 0xF800 /* 255, 0, 0 */ +#define ILI9488_MAGENTA 0xF81F /* 255, 0, 255 */ +#define ILI9488_YELLOW 0xFFE0 /* 255, 255, 0 */ +#define ILI9488_WHITE 0xFFFF /* 255, 255, 255 */ +#define ILI9488_ORANGE 0xFD20 /* 255, 165, 0 */ +#define ILI9488_GREENYELLOW 0xAFE5 /* 173, 255, 47 */ +#define ILI9488_PINK 0xF81F + + + + +class ILI9488 : public Renderer { + + public: + + ILI9488(int8_t cs,int8_t mosi,int8_t sclk,int8_t bp); + + void begin(void); + void DisplayInit(int8_t p,int8_t size,int8_t rot,int8_t font); + void setAddrWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1); + void setScrollArea(uint16_t topFixedArea, uint16_t bottomFixedArea); + void scroll(uint16_t pixels); + void pushColor(uint16_t color); + void pushColors(uint16_t *data, uint8_t len, boolean first); + //void drawImage(const uint8_t* img, uint16_t x, uint16_t y, uint16_t w, uint16_t h); + void fillScreen(uint16_t color); + void drawPixel(int16_t x, int16_t y, uint16_t color); + void drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color); + void drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color); + void fillRect(int16_t x, int16_t y, int16_t w, int16_t h,uint16_t color); + void setRotation(uint8_t r); + void invertDisplay(boolean i); + uint16_t color565(uint8_t r, uint8_t g, uint8_t b); + uint16_t GetColorFromIndex(uint8_t index); + void DisplayOnff(int8_t on); + void writecommand(uint8_t c); + void writedata(uint8_t d); + void write16BitColor(uint16_t color); + void commandList(uint8_t *addr); + void hw_spi_init(); + + + private: + uint8_t tabcolor; + void fastSPIwrite(uint8_t d,uint8_t dc); + void spi_lcd_mode_init(void); + void start(void); + void stop(void); + int8_t _cs, _mosi, _sclk, _bp, _hwspi; + +}; + +#endif diff --git a/lib/JaretBurkett_ILI9488-gemu-1.0/README.md b/lib/JaretBurkett_ILI9488-gemu-1.0/README.md new file mode 100644 index 000000000..fd203455b --- /dev/null +++ b/lib/JaretBurkett_ILI9488-gemu-1.0/README.md @@ -0,0 +1,8 @@ +### ILI9488 Arduino Library +This library is for support for the 320x480 tft controller over 4 wire SPI. It is based heavily on the [Adafruit_ILI9341](https://github.com/adafruit/Adafruit_ILI9341) library and is designed to work with the [Adafruit_GFX library](https://github.com/adafruit/Adafruit-GFX-Library). + +I have made some heavy modifications, as the typical Adafruit TFT libraries are designed to work with 16bit color (RGB565), and the ILI9488 can only do 24bit (RGB888) color in 4 wire SPI mode. You can still use the library EXACTLY like you would for 16bit mode color, the colors are converted before sending to the display. What this means is, things will be slower than normal. Not only do you have to write twice as many pixels as a normal 240x320 display, 153,600px (320x480) vs 76,800px (240x320), but you also have to do a lightweight conversion on each color, and write 3 bytes vs 2bytes per pixel. + +For this reason, I do not recommend an AVR based Arduino for this library, although it will still work. I highly recommend a faster microcontroller based on ARM such as the Teensy, [STM32duino](https://github.com/rogerclarkmelbourne/Arduino_STM32), Arduino Zero, or the Arduing Due. + +On the STM32duino, DMA is supported and is therefore much faster. \ No newline at end of file diff --git a/lib/JaretBurkett_ILI9488-gemu-1.0/examples/graphicstest/graphicstest.ino b/lib/JaretBurkett_ILI9488-gemu-1.0/examples/graphicstest/graphicstest.ino new file mode 100644 index 000000000..30b0cbd9a --- /dev/null +++ b/lib/JaretBurkett_ILI9488-gemu-1.0/examples/graphicstest/graphicstest.ino @@ -0,0 +1,350 @@ +/*************************************************** + This is our GFX example for the Adafruit ILI9488 Breakout and Shield + ----> http://www.adafruit.com/products/1651 + + Check out the links above for our tutorials and wiring diagrams + These displays use SPI to communicate, 4 or 5 pins are required to + interface (RST is optional) + Adafruit invests time and resources providing this open source code, + please support Adafruit and open-source hardware by purchasing + products from Adafruit! + + Written by Limor Fried/Ladyada for Adafruit Industries. + MIT license, all text above must be included in any redistribution + ****************************************************/ + + +#include "SPI.h" +#include +#include + +#define TFT_CS PA1 +#define TFT_DC PB3 +#define TFT_LED PB0 +#define TFT_RST PB4 + +// Use hardware SPI (on Uno, #13, #12, #11) and the above for CS/DC +ILI9488 tft = ILI9488(TFT_CS, TFT_DC, TFT_RST); +// If using the breakout, change pins as desired +//Adafruit_ILI9488 tft = Adafruit_ILI9488(TFT_CS, TFT_DC, TFT_MOSI, TFT_CLK, TFT_RST, TFT_MISO); + +void setup() { + Serial.begin(9600); + Serial.println("ILI9488 Test!"); + + tft.begin(); + + // read diagnostics (optional but can help debug problems) + uint8_t x = tft.readcommand8(ILI9488_RDMODE); + Serial.print("Display Power Mode: 0x"); Serial.println(x, HEX); + x = tft.readcommand8(ILI9488_RDMADCTL); + Serial.print("MADCTL Mode: 0x"); Serial.println(x, HEX); + x = tft.readcommand8(ILI9488_RDPIXFMT); + Serial.print("Pixel Format: 0x"); Serial.println(x, HEX); + x = tft.readcommand8(ILI9488_RDIMGFMT); + Serial.print("Image Format: 0x"); Serial.println(x, HEX); + x = tft.readcommand8(ILI9488_RDSELFDIAG); + Serial.print("Self Diagnostic: 0x"); Serial.println(x, HEX); + + Serial.println(F("Benchmark Time (microseconds)")); + + Serial.print(F("Screen fill ")); + Serial.println(testFillScreen()); + delay(500); + + Serial.print(F("Text ")); + Serial.println(testText()); + delay(3000); + + Serial.print(F("Lines ")); + Serial.println(testLines(ILI9488_CYAN)); + delay(500); + + Serial.print(F("Horiz/Vert Lines ")); + Serial.println(testFastLines(ILI9488_RED, ILI9488_BLUE)); + delay(500); + + Serial.print(F("Rectangles (outline) ")); + Serial.println(testRects(ILI9488_GREEN)); + delay(500); + + Serial.print(F("Rectangles (filled) ")); + Serial.println(testFilledRects(ILI9488_YELLOW, ILI9488_MAGENTA)); + delay(500); + + Serial.print(F("Circles (filled) ")); + Serial.println(testFilledCircles(10, ILI9488_MAGENTA)); + + Serial.print(F("Circles (outline) ")); + Serial.println(testCircles(10, ILI9488_WHITE)); + delay(500); + + Serial.print(F("Triangles (outline) ")); + Serial.println(testTriangles()); + delay(500); + + Serial.print(F("Triangles (filled) ")); + Serial.println(testFilledTriangles()); + delay(500); + + Serial.print(F("Rounded rects (outline) ")); + Serial.println(testRoundRects()); + delay(500); + + Serial.print(F("Rounded rects (filled) ")); + Serial.println(testFilledRoundRects()); + delay(500); + + Serial.println(F("Done!")); + +} + + +void loop(void) { + for(uint8_t rotation=0; rotation<4; rotation++) { + tft.setRotation(rotation); + testText(); + delay(1000); + } +} + +unsigned long testFillScreen() { + unsigned long start = micros(); + tft.fillScreen(ILI9488_BLACK); + tft.fillScreen(ILI9488_RED); + tft.fillScreen(ILI9488_GREEN); + tft.fillScreen(ILI9488_BLUE); + tft.fillScreen(ILI9488_BLACK); + return micros() - start; +} + +unsigned long testText() { + tft.fillScreen(ILI9488_BLACK); + unsigned long start = micros(); + tft.setCursor(0, 0); + tft.setTextColor(ILI9488_WHITE); tft.setTextSize(1); + tft.println("Hello World!"); + tft.setTextColor(ILI9488_YELLOW); tft.setTextSize(2); + tft.println(1234.56); + tft.setTextColor(ILI9488_RED); tft.setTextSize(3); + tft.println(0xDEADBEEF, HEX); + tft.println(); + tft.setTextColor(ILI9488_GREEN); + tft.setTextSize(5); + tft.println("Groop"); + tft.setTextSize(2); + tft.println("I implore thee,"); + tft.setTextSize(1); + tft.println("my foonting turlingdromes."); + tft.println("And hooptiously drangle me"); + tft.println("with crinkly bindlewurdles,"); + tft.println("Or I will rend thee"); + tft.println("in the gobberwarts"); + tft.println("with my blurglecruncheon,"); + tft.println("see if I don't!"); + return micros() - start; +} + +unsigned long testLines(uint16_t color) { + unsigned long start, t; + int x1, y1, x2, y2, + w = tft.width(), + h = tft.height(); + + tft.fillScreen(ILI9488_BLACK); + + x1 = y1 = 0; + y2 = h - 1; + start = micros(); + for(x2=0; x20; i-=6) { + i2 = i / 2; + start = micros(); + tft.fillRect(cx-i2, cy-i2, i, i, color1); + t += micros() - start; + // Outlines are not included in timing results + tft.drawRect(cx-i2, cy-i2, i, i, color2); + } + + return t; +} + +unsigned long testFilledCircles(uint8_t radius, uint16_t color) { + unsigned long start; + int x, y, w = tft.width(), h = tft.height(), r2 = radius * 2; + + tft.fillScreen(ILI9488_BLACK); + start = micros(); + for(x=radius; x10; i-=5) { + start = micros(); + tft.fillTriangle(cx, cy - i, cx - i, cy + i, cx + i, cy + i, + tft.color565(0, i, i)); + t += micros() - start; + tft.drawTriangle(cx, cy - i, cx - i, cy + i, cx + i, cy + i, + tft.color565(i, i, 0)); + } + + return t; +} + +unsigned long testRoundRects() { + unsigned long start; + int w, i, i2, + cx = tft.width() / 2 - 1, + cy = tft.height() / 2 - 1; + + tft.fillScreen(ILI9488_BLACK); + w = min(tft.width(), tft.height()); + start = micros(); + for(i=0; i20; i-=6) { + i2 = i / 2; + tft.fillRoundRect(cx-i2, cy-i2, i, i, i/8, tft.color565(0, i, 0)); + } + + return micros() - start; +} diff --git a/lib/JaretBurkett_ILI9488-gemu-1.0/keywords.txt b/lib/JaretBurkett_ILI9488-gemu-1.0/keywords.txt new file mode 100644 index 000000000..44c83b06f --- /dev/null +++ b/lib/JaretBurkett_ILI9488-gemu-1.0/keywords.txt @@ -0,0 +1,30 @@ +####################################### +# Syntax Coloring Map +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +ILI9488 KEYWORD1 + + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +setRotation KEYWORD2 +setAddrWindow KEYWORD2 +pushColor KEYWORD2 +drawPixel KEYWORD2 +drawFastVLine KEYWORD2 +drawFastHLine KEYWORD2 +fillRect KEYWORD2 +setRotation KEYWORD2 +setRotation KEYWORD2 +height KEYWORD2 +width KEYWORD2 +invertDisplay KEYWORD2 +drawImage KEYWORD2 +setScrollArea KEYWORD2 +scroll KEYWORD2 diff --git a/lib/JaretBurkett_ILI9488-gemu-1.0/library.properties b/lib/JaretBurkett_ILI9488-gemu-1.0/library.properties new file mode 100644 index 000000000..a8ae043a7 --- /dev/null +++ b/lib/JaretBurkett_ILI9488-gemu-1.0/library.properties @@ -0,0 +1,9 @@ +name=ILI9488 +version=1.0.2 +author=Jaret Burkett +maintainer=Jaret Burkett +sentence=Library for ILI9488 displays +paragraph=Library for ILI9488 displays +category=Display +url=https://github.com/jaretburkett/ILI9488 +architectures=* diff --git a/lib/JaretBurkett_ILI9488-gemu-1.0/spi_register.h b/lib/JaretBurkett_ILI9488-gemu-1.0/spi_register.h new file mode 100644 index 000000000..340559ae1 --- /dev/null +++ b/lib/JaretBurkett_ILI9488-gemu-1.0/spi_register.h @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2010 - 2011 Espressif System + * + */ + +#ifndef SPI_REGISTER_H_INCLUDED +#define SPI_REGISTER_H_INCLUDED + +#define REG_SPI_BASE(i) (0x60000200-i*0x100) +#define SPI_CMD(i) (REG_SPI_BASE(i) + 0x0) +#define SPI_USR (BIT(18)) + +#define SPI_ADDR(i) (REG_SPI_BASE(i) + 0x4) + +#define SPI_CTRL(i) (REG_SPI_BASE(i) + 0x8) +#define SPI_WR_BIT_ORDER (BIT(26)) +#define SPI_RD_BIT_ORDER (BIT(25)) +#define SPI_QIO_MODE (BIT(24)) +#define SPI_DIO_MODE (BIT(23)) +#define SPI_QOUT_MODE (BIT(20)) +#define SPI_DOUT_MODE (BIT(14)) +#define SPI_FASTRD_MODE (BIT(13)) + + + +#define SPI_RD_STATUS(i) (REG_SPI_BASE(i) + 0x10) + +#define SPI_CTRL2(i) (REG_SPI_BASE(i) + 0x14) + +#define SPI_CS_DELAY_NUM 0x0000000F +#define SPI_CS_DELAY_NUM_S 28 +#define SPI_CS_DELAY_MODE 0x00000003 +#define SPI_CS_DELAY_MODE_S 26 +#define SPI_MOSI_DELAY_NUM 0x00000007 +#define SPI_MOSI_DELAY_NUM_S 23 +#define SPI_MOSI_DELAY_MODE 0x00000003 +#define SPI_MOSI_DELAY_MODE_S 21 +#define SPI_MISO_DELAY_NUM 0x00000007 +#define SPI_MISO_DELAY_NUM_S 18 +#define SPI_MISO_DELAY_MODE 0x00000003 +#define SPI_MISO_DELAY_MODE_S 16 +#define SPI_CK_OUT_HIGH_MODE 0x0000000F +#define SPI_CK_OUT_HIGH_MODE_S 12 +#define SPI_CK_OUT_LOW_MODE 0x0000000F +#define SPI_CK_OUT_LOW_MODE_S 8 + +#define SPI_CLOCK(i) (REG_SPI_BASE(i) + 0x18) +#define SPI_CLK_EQU_SYSCLK (BIT(31)) +#define SPI_CLKDIV_PRE 0x00001FFF +#define SPI_CLKDIV_PRE_S 18 +#define SPI_CLKCNT_N 0x0000003F +#define SPI_CLKCNT_N_S 12 +#define SPI_CLKCNT_H 0x0000003F +#define SPI_CLKCNT_H_S 6 +#define SPI_CLKCNT_L 0x0000003F +#define SPI_CLKCNT_L_S 0 + +#define SPI_USER(i) (REG_SPI_BASE(i) + 0x1C) +#define SPI_USR_COMMAND (BIT(31)) +#define SPI_USR_ADDR (BIT(30)) +#define SPI_USR_DUMMY (BIT(29)) +#define SPI_USR_MISO (BIT(28)) +#define SPI_USR_MOSI (BIT(27)) + +#define SPI_USR_MOSI_HIGHPART (BIT(25)) +#define SPI_USR_MISO_HIGHPART (BIT(24)) + + +#define SPI_SIO (BIT(16)) +#define SPI_FWRITE_QIO (BIT(15)) +#define SPI_FWRITE_DIO (BIT(14)) +#define SPI_FWRITE_QUAD (BIT(13)) +#define SPI_FWRITE_DUAL (BIT(12)) +#define SPI_WR_BYTE_ORDER (BIT(11)) +#define SPI_RD_BYTE_ORDER (BIT(10)) +#define SPI_CK_OUT_EDGE (BIT(7)) +#define SPI_CK_I_EDGE (BIT(6)) +#define SPI_CS_SETUP (BIT(5)) +#define SPI_CS_HOLD (BIT(4)) +#define SPI_FLASH_MODE (BIT(2)) +#define SPI_DOUTDIN (BIT(0)) + +#define SPI_USER1(i) (REG_SPI_BASE(i) + 0x20) +#define SPI_USR_ADDR_BITLEN 0x0000003F +#define SPI_USR_ADDR_BITLEN_S 26 +#define SPI_USR_MOSI_BITLEN 0x000001FF +#define SPI_USR_MOSI_BITLEN_S 17 +#define SPI_USR_MISO_BITLEN 0x000001FF +#define SPI_USR_MISO_BITLEN_S 8 + +#define SPI_USR_DUMMY_CYCLELEN 0x000000FF +#define SPI_USR_DUMMY_CYCLELEN_S 0 + +#define SPI_USER2(i) (REG_SPI_BASE(i) + 0x24) +#define SPI_USR_COMMAND_BITLEN 0x0000000F +#define SPI_USR_COMMAND_BITLEN_S 28 +#define SPI_USR_COMMAND_VALUE 0x0000FFFF +#define SPI_USR_COMMAND_VALUE_S 0 + +#define SPI_WR_STATUS(i) (REG_SPI_BASE(i) + 0x28) +#define SPI_PIN(i) (REG_SPI_BASE(i) + 0x2C) +#define SPI_CS2_DIS (BIT(2)) +#define SPI_CS1_DIS (BIT(1)) +#define SPI_CS0_DIS (BIT(0)) +#define SPI_IDLE_EDGE (BIT(29)) + +#define SPI_SLAVE(i) (REG_SPI_BASE(i) + 0x30) +#define SPI_SYNC_RESET (BIT(31)) +#define SPI_SLAVE_MODE (BIT(30)) +#define SPI_SLV_WR_RD_BUF_EN (BIT(29)) +#define SPI_SLV_WR_RD_STA_EN (BIT(28)) +#define SPI_SLV_CMD_DEFINE (BIT(27)) +#define SPI_TRANS_CNT 0x0000000F +#define SPI_TRANS_CNT_S 23 +#define SPI_TRANS_DONE_EN (BIT(9)) +#define SPI_SLV_WR_STA_DONE_EN (BIT(8)) +#define SPI_SLV_RD_STA_DONE_EN (BIT(7)) +#define SPI_SLV_WR_BUF_DONE_EN (BIT(6)) +#define SPI_SLV_RD_BUF_DONE_EN (BIT(5)) + + + +#define SLV_SPI_INT_EN 0x0000001f +#define SLV_SPI_INT_EN_S 5 + +#define SPI_TRANS_DONE (BIT(4)) +#define SPI_SLV_WR_STA_DONE (BIT(3)) +#define SPI_SLV_RD_STA_DONE (BIT(2)) +#define SPI_SLV_WR_BUF_DONE (BIT(1)) +#define SPI_SLV_RD_BUF_DONE (BIT(0)) + +#define SPI_SLAVE1(i) (REG_SPI_BASE(i) + 0x34) +#define SPI_SLV_STATUS_BITLEN 0x0000001F +#define SPI_SLV_STATUS_BITLEN_S 27 +#define SPI_SLV_BUF_BITLEN 0x000001FF +#define SPI_SLV_BUF_BITLEN_S 16 +#define SPI_SLV_RD_ADDR_BITLEN 0x0000003F +#define SPI_SLV_RD_ADDR_BITLEN_S 10 +#define SPI_SLV_WR_ADDR_BITLEN 0x0000003F +#define SPI_SLV_WR_ADDR_BITLEN_S 4 + +#define SPI_SLV_WRSTA_DUMMY_EN (BIT(3)) +#define SPI_SLV_RDSTA_DUMMY_EN (BIT(2)) +#define SPI_SLV_WRBUF_DUMMY_EN (BIT(1)) +#define SPI_SLV_RDBUF_DUMMY_EN (BIT(0)) + + + +#define SPI_SLAVE2(i) (REG_SPI_BASE(i) + 0x38) +#define SPI_SLV_WRBUF_DUMMY_CYCLELEN 0X000000FF +#define SPI_SLV_WRBUF_DUMMY_CYCLELEN_S 24 +#define SPI_SLV_RDBUF_DUMMY_CYCLELEN 0X000000FF +#define SPI_SLV_RDBUF_DUMMY_CYCLELEN_S 16 +#define SPI_SLV_WRSTR_DUMMY_CYCLELEN 0X000000FF +#define SPI_SLV_WRSTR_DUMMY_CYCLELEN_S 8 +#define SPI_SLV_RDSTR_DUMMY_CYCLELEN 0x000000FF +#define SPI_SLV_RDSTR_DUMMY_CYCLELEN_S 0 + +#define SPI_SLAVE3(i) (REG_SPI_BASE(i) + 0x3C) +#define SPI_SLV_WRSTA_CMD_VALUE 0x000000FF +#define SPI_SLV_WRSTA_CMD_VALUE_S 24 +#define SPI_SLV_RDSTA_CMD_VALUE 0x000000FF +#define SPI_SLV_RDSTA_CMD_VALUE_S 16 +#define SPI_SLV_WRBUF_CMD_VALUE 0x000000FF +#define SPI_SLV_WRBUF_CMD_VALUE_S 8 +#define SPI_SLV_RDBUF_CMD_VALUE 0x000000FF +#define SPI_SLV_RDBUF_CMD_VALUE_S 0 + +#define SPI_W0(i) (REG_SPI_BASE(i) +0x40) +#define SPI_W1(i) (REG_SPI_BASE(i) +0x44) +#define SPI_W2(i) (REG_SPI_BASE(i) +0x48) +#define SPI_W3(i) (REG_SPI_BASE(i) +0x4C) +#define SPI_W4(i) (REG_SPI_BASE(i) +0x50) +#define SPI_W5(i) (REG_SPI_BASE(i) +0x54) +#define SPI_W6(i) (REG_SPI_BASE(i) +0x58) +#define SPI_W7(i) (REG_SPI_BASE(i) +0x5C) +#define SPI_W8(i) (REG_SPI_BASE(i) +0x60) +#define SPI_W9(i) (REG_SPI_BASE(i) +0x64) +#define SPI_W10(i) (REG_SPI_BASE(i) +0x68) +#define SPI_W11(i) (REG_SPI_BASE(i) +0x6C) +#define SPI_W12(i) (REG_SPI_BASE(i) +0x70) +#define SPI_W13(i) (REG_SPI_BASE(i) +0x74) +#define SPI_W14(i) (REG_SPI_BASE(i) +0x78) +#define SPI_W15(i) (REG_SPI_BASE(i) +0x7C) + +#define SPI_EXT3(i) (REG_SPI_BASE(i) + 0xFC) +#define SPI_INT_HOLD_ENA 0x00000003 +#define SPI_INT_HOLD_ENA_S 0 +#endif // SPI_REGISTER_H_INCLUDED diff --git a/lib/NeoPixelBus-2.2.9/COPYING b/lib/NeoPixelBus-2.2.9/COPYING deleted file mode 100644 index 10926e87f..000000000 --- a/lib/NeoPixelBus-2.2.9/COPYING +++ /dev/null @@ -1,675 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. - diff --git a/lib/NeoPixelBus-2.2.9/library.json b/lib/NeoPixelBus-2.2.9/library.json deleted file mode 100644 index 35e8ea0c2..000000000 --- a/lib/NeoPixelBus-2.2.9/library.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "NeoPixelBus", - "keywords": "NeoPixel, WS2811, WS2812, WS2813, SK6812, DotStar, APA102, RGB, RGBW", - "description": "A library that makes controlling NeoPixels (WS2811, WS2812, WS2813 & SK6812) and DotStars (APA102) easy. Supports most Arduino platforms. Support for RGBW pixels. Includes seperate RgbColor, RgbwColor, HslColor, and HsbColor objects. Includes an animator class that helps create asyncronous animations. For Esp8266 it has three methods of sending NeoPixel data, DMA, UART, and Bit Bang; and two methods of sending DotStar data, hardware SPI and software SPI.", - "homepage": "https://github.com/Makuna/NeoPixelBus/wiki", - "repository": - { - "type": "git", - "url": "https://github.com/Makuna/NeoPixelBus" - }, - "version": "2.2.9", - "frameworks": "arduino", - "platforms": "*" -} - diff --git a/lib/NeoPixelBus-2.2.9/src/internal/NeoEsp8266UartMethod.cpp b/lib/NeoPixelBus-2.2.9/src/internal/NeoEsp8266UartMethod.cpp deleted file mode 100644 index 7bfc3e0d2..000000000 --- a/lib/NeoPixelBus-2.2.9/src/internal/NeoEsp8266UartMethod.cpp +++ /dev/null @@ -1,216 +0,0 @@ -/*------------------------------------------------------------------------- -NeoPixel library helper functions for Esp8266 UART hardware - -Written by Michael C. Miller. - -I invest time and resources providing this open source code, -please support me by dontating (see https://github.com/Makuna/NeoPixelBus) - -------------------------------------------------------------------------- -This file is part of the Makuna/NeoPixelBus library. - -NeoPixelBus is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as -published by the Free Software Foundation, either version 3 of -the License, or (at your option) any later version. - -NeoPixelBus is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with NeoPixel. If not, see -. --------------------------------------------------------------------------*/ - -#ifdef ARDUINO_ARCH_ESP8266 -#include "NeoEsp8266UartMethod.h" -#include -extern "C" -{ - #include - #include - #include - #include -} - -#define UART1 1 -#define UART1_INV_MASK (0x3f << 19) - -// Gets the number of bytes waiting in the TX FIFO of UART1 -static inline uint8_t getUartTxFifoLength() -{ - return (U1S >> USTXC) & 0xff; -} - -// Append a byte to the TX FIFO of UART1 -// You must ensure the TX FIFO isn't full -static inline void enqueue(uint8_t byte) -{ - U1F = byte; -} - -static const uint8_t* esp8266_uart1_async_buf; -static const uint8_t* esp8266_uart1_async_buf_end; - -NeoEsp8266Uart::NeoEsp8266Uart(uint16_t pixelCount, size_t elementSize) -{ - _sizePixels = pixelCount * elementSize; - _pixels = (uint8_t*)malloc(_sizePixels); - memset(_pixels, 0x00, _sizePixels); -} - -NeoEsp8266Uart::~NeoEsp8266Uart() -{ - // Wait until the TX fifo is empty. This way we avoid broken frames - // when destroying & creating a NeoPixelBus to change its length. - while (getUartTxFifoLength() > 0) - { - yield(); - } - - free(_pixels); -} - -void NeoEsp8266Uart::InitializeUart(uint32_t uartBaud) -{ - // Configure the serial line with 1 start bit (0), 6 data bits and 1 stop bit (1) - Serial1.begin(uartBaud, SERIAL_6N1, SERIAL_TX_ONLY); - - // Invert the TX voltage associated with logic level so: - // - A logic level 0 will generate a Vcc signal - // - A logic level 1 will generate a Gnd signal - CLEAR_PERI_REG_MASK(UART_CONF0(UART1), UART1_INV_MASK); - SET_PERI_REG_MASK(UART_CONF0(UART1), (BIT(22))); -} - -void NeoEsp8266Uart::UpdateUart() -{ - // Since the UART can finish sending queued bytes in the FIFO in - // the background, instead of waiting for the FIFO to flush - // we annotate the start time of the frame so we can calculate - // when it will finish. - _startTime = micros(); - - // Then keep filling the FIFO until done - const uint8_t* ptr = _pixels; - const uint8_t* end = ptr + _sizePixels; - while (ptr != end) - { - ptr = FillUartFifo(ptr, end); - } -} - -const uint8_t* ICACHE_RAM_ATTR NeoEsp8266Uart::FillUartFifo(const uint8_t* pixels, const uint8_t* end) -{ - // Remember: UARTs send less significant bit (LSB) first so - // pushing ABCDEF byte will generate a 0FEDCBA1 signal, - // including a LOW(0) start & a HIGH(1) stop bits. - // Also, we have configured UART to invert logic levels, so: - const uint8_t _uartData[4] = { - 0b110111, // On wire: 1 000 100 0 [Neopixel reads 00] - 0b000111, // On wire: 1 000 111 0 [Neopixel reads 01] - 0b110100, // On wire: 1 110 100 0 [Neopixel reads 10] - 0b000100, // On wire: 1 110 111 0 [NeoPixel reads 11] - }; - uint8_t avail = (UART_TX_FIFO_SIZE - getUartTxFifoLength()) / 4; - if (end - pixels > avail) - { - end = pixels + avail; - } - while (pixels < end) - { - uint8_t subpix = *pixels++; - enqueue(_uartData[(subpix >> 6) & 0x3]); - enqueue(_uartData[(subpix >> 4) & 0x3]); - enqueue(_uartData[(subpix >> 2) & 0x3]); - enqueue(_uartData[ subpix & 0x3]); - } - return pixels; -} - -NeoEsp8266AsyncUart::NeoEsp8266AsyncUart(uint16_t pixelCount, size_t elementSize) - : NeoEsp8266Uart(pixelCount, elementSize) -{ - _asyncPixels = (uint8_t*)malloc(_sizePixels); -} - -NeoEsp8266AsyncUart::~NeoEsp8266AsyncUart() -{ - // Remember: the UART interrupt can be sending data from _asyncPixels in the background - while (esp8266_uart1_async_buf != esp8266_uart1_async_buf_end) - { - yield(); - } - free(_asyncPixels); -} - -void ICACHE_RAM_ATTR NeoEsp8266AsyncUart::InitializeUart(uint32_t uartBaud) -{ - NeoEsp8266Uart::InitializeUart(uartBaud); - - // Disable all interrupts - ETS_UART_INTR_DISABLE(); - - // Clear the RX & TX FIFOS - SET_PERI_REG_MASK(UART_CONF0(UART1), UART_RXFIFO_RST | UART_TXFIFO_RST); - CLEAR_PERI_REG_MASK(UART_CONF0(UART1), UART_RXFIFO_RST | UART_TXFIFO_RST); - - // Set the interrupt handler - ETS_UART_INTR_ATTACH(IntrHandler, NULL); - - // Set tx fifo trigger. 80 bytes gives us 200 microsecs to refill the FIFO - WRITE_PERI_REG(UART_CONF1(UART1), 80 << UART_TXFIFO_EMPTY_THRHD_S); - - // Disable RX & TX interrupts. It is enabled by uart.c in the SDK - CLEAR_PERI_REG_MASK(UART_INT_ENA(UART1), UART_RXFIFO_FULL_INT_ENA | UART_TXFIFO_EMPTY_INT_ENA); - - // Clear all pending interrupts in UART1 - WRITE_PERI_REG(UART_INT_CLR(UART1), 0xffff); - - // Reenable interrupts - ETS_UART_INTR_ENABLE(); -} - -void NeoEsp8266AsyncUart::UpdateUart() -{ - // Instruct ESP8266 hardware uart1 to send the pixels asynchronously - esp8266_uart1_async_buf = _pixels; - esp8266_uart1_async_buf_end = _pixels + _sizePixels; - SET_PERI_REG_MASK(UART_INT_ENA(1), UART_TXFIFO_EMPTY_INT_ENA); - - // Annotate when we started to send bytes, so we can calculate when we are ready to send again - _startTime = micros(); - - // Copy the pixels to the idle buffer and swap them - memcpy(_asyncPixels, _pixels, _sizePixels); - std::swap(_asyncPixels, _pixels); -} - -void ICACHE_RAM_ATTR NeoEsp8266AsyncUart::IntrHandler(void* param) -{ - // Interrupt handler is shared between UART0 & UART1 - if (READ_PERI_REG(UART_INT_ST(UART1))) //any UART1 stuff - { - // Fill the FIFO with new data - esp8266_uart1_async_buf = FillUartFifo(esp8266_uart1_async_buf, esp8266_uart1_async_buf_end); - // Disable TX interrupt when done - if (esp8266_uart1_async_buf == esp8266_uart1_async_buf_end) - { - CLEAR_PERI_REG_MASK(UART_INT_ENA(UART1), UART_TXFIFO_EMPTY_INT_ENA); - } - // Clear all interrupts flags (just in case) - WRITE_PERI_REG(UART_INT_CLR(UART1), 0xffff); - } - - if (READ_PERI_REG(UART_INT_ST(UART0))) - { - // TODO: gdbstub uses the interrupt of UART0, but there is no way to call its - // interrupt handler gdbstub_uart_hdlr since it's static. - WRITE_PERI_REG(UART_INT_CLR(UART0), 0xffff); - } -} - -#endif - diff --git a/lib/NeoPixelBus-2.2.9/src/internal/NeoEsp8266UartMethod.h b/lib/NeoPixelBus-2.2.9/src/internal/NeoEsp8266UartMethod.h deleted file mode 100644 index a92d56631..000000000 --- a/lib/NeoPixelBus-2.2.9/src/internal/NeoEsp8266UartMethod.h +++ /dev/null @@ -1,178 +0,0 @@ -/*------------------------------------------------------------------------- -NeoPixel library helper functions for Esp8266 UART hardware - -Written by Michael C. Miller. - -I invest time and resources providing this open source code, -please support me by dontating (see https://github.com/Makuna/NeoPixelBus) - -------------------------------------------------------------------------- -This file is part of the Makuna/NeoPixelBus library. - -NeoPixelBus is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as -published by the Free Software Foundation, either version 3 of -the License, or (at your option) any later version. - -NeoPixelBus is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with NeoPixel. If not, see -. --------------------------------------------------------------------------*/ - -#pragma once - -#ifdef ARDUINO_ARCH_ESP8266 -#include - -// NeoEsp8266Uart contains all the low level details that doesn't -// depend on the transmission speed, and therefore, it isn't a template -class NeoEsp8266Uart -{ -protected: - NeoEsp8266Uart(uint16_t pixelCount, size_t elementSize); - - ~NeoEsp8266Uart(); - - void InitializeUart(uint32_t uartBaud); - - void UpdateUart(); - - static const uint8_t* ICACHE_RAM_ATTR FillUartFifo(const uint8_t* pixels, const uint8_t* end); - - size_t _sizePixels; // Size of '_pixels' buffer below - uint8_t* _pixels; // Holds LED color values - uint32_t _startTime; // Microsecond count when last update started -}; - -// NeoEsp8266AsyncUart handles all transmission asynchronously using interrupts -// -// This UART controller uses two buffers that are swapped in every call to -// NeoPixelBus.Show(). One buffer contains the data that is being sent -// asynchronosly and another buffer contains the data that will be send -// in the next call to NeoPixelBus.Show(). -// -// Therefore, the result of NeoPixelBus.Pixels() is invalidated after -// every call to NeoPixelBus.Show() and must not be cached. -class NeoEsp8266AsyncUart: public NeoEsp8266Uart -{ -protected: - NeoEsp8266AsyncUart(uint16_t pixelCount, size_t elementSize); - - ~NeoEsp8266AsyncUart(); - - void InitializeUart(uint32_t uartBaud); - - void UpdateUart(); - -private: - static void ICACHE_RAM_ATTR IntrHandler(void* param); - - uint8_t* _asyncPixels; // Holds a copy of LED color values taken when UpdateUart began -}; - -// NeoEsp8266UartSpeedWs2813 contains the timing constants used to get NeoPixelBus running with the Ws2813 -class NeoEsp8266UartSpeedWs2813 -{ -public: - static const uint32_t ByteSendTimeUs = 10; // us it takes to send a single pixel element at 800khz speed - static const uint32_t UartBaud = 3200000; // 800mhz, 4 serial bytes per NeoByte - static const uint32_t ResetTimeUs = 250; // us between data send bursts to reset for next update -}; - -// NeoEsp8266UartSpeed800Kbps contains the timing constant used to get NeoPixelBus running at 800Khz -class NeoEsp8266UartSpeed800Kbps -{ -public: - static const uint32_t ByteSendTimeUs = 10; // us it takes to send a single pixel element at 800khz speed - static const uint32_t UartBaud = 3200000; // 800mhz, 4 serial bytes per NeoByte - static const uint32_t ResetTimeUs = 50; // us between data send bursts to reset for next update -}; - -// NeoEsp8266UartSpeed800Kbps contains the timing constant used to get NeoPixelBus running at 400Khz -class NeoEsp8266UartSpeed400Kbps -{ -public: - static const uint32_t ByteSendTimeUs = 20; // us it takes to send a single pixel element at 400khz speed - static const uint32_t UartBaud = 1600000; // 400mhz, 4 serial bytes per NeoByte - static const uint32_t ResetTimeUs = 50; // us between data send bursts to reset for next update -}; - -// NeoEsp8266UartMethodBase is a light shell arround NeoEsp8266Uart or NeoEsp8266AsyncUart that -// implements the methods needed to operate as a NeoPixelBus method. -template -class NeoEsp8266UartMethodBase: public T_BASE -{ -public: - NeoEsp8266UartMethodBase(uint16_t pixelCount, size_t elementSize) - : T_BASE(pixelCount, elementSize) - { - } - NeoEsp8266UartMethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize) - : T_BASE(pixelCount, elementSize) - { - } - - bool IsReadyToUpdate() const - { - uint32_t delta = micros() - this->_startTime; - return delta >= getPixelTime() + T_SPEED::ResetTimeUs; - } - - void Initialize() - { - this->InitializeUart(T_SPEED::UartBaud); - - // Inverting logic levels can generate a phantom bit in the led strip bus - // We need to delay 50+ microseconds the output stream to force a data - // latch and discard this bit. Otherwise, that bit would be prepended to - // the first frame corrupting it. - this->_startTime = micros() - getPixelTime(); - } - - void Update() - { - // Data latch = 50+ microsecond pause in the output stream. Rather than - // put a delay at the end of the function, the ending time is noted and - // the function will simply hold off (if needed) on issuing the - // subsequent round of data until the latch time has elapsed. This - // allows the mainline code to start generating the next frame of data - // rather than stalling for the latch. - while (!this->IsReadyToUpdate()) - { - yield(); - } - this->UpdateUart(); - } - - uint8_t* getPixels() const - { - return this->_pixels; - }; - - size_t getPixelsSize() const - { - return this->_sizePixels; - }; - -private: - uint32_t getPixelTime() const - { - return (T_SPEED::ByteSendTimeUs * this->_sizePixels); - }; -}; - -typedef NeoEsp8266UartMethodBase NeoEsp8266UartWs2813Method; -typedef NeoEsp8266UartMethodBase NeoEsp8266Uart800KbpsMethod; -typedef NeoEsp8266UartMethodBase NeoEsp8266Uart400KbpsMethod; - -typedef NeoEsp8266UartMethodBase NeoEsp8266AsyncUartWs2813Method; -typedef NeoEsp8266UartMethodBase NeoEsp8266AsyncUart800KbpsMethod; -typedef NeoEsp8266UartMethodBase NeoEsp8266AsyncUart400KbpsMethod; - -#endif - diff --git a/lib/NeoPixelBus-2.2.9/src/internal/NeoPixelEsp.c b/lib/NeoPixelBus-2.2.9/src/internal/NeoPixelEsp.c deleted file mode 100644 index 52415ff42..000000000 --- a/lib/NeoPixelBus-2.2.9/src/internal/NeoPixelEsp.c +++ /dev/null @@ -1,151 +0,0 @@ -/*------------------------------------------------------------------------- -NeoPixel library helper functions for Esp8266 and Esp32. - -Written by Michael C. Miller. - -I invest time and resources providing this open source code, -please support me by dontating (see https://github.com/Makuna/NeoPixelBus) - -------------------------------------------------------------------------- -This file is part of the Makuna/NeoPixelBus library. - -NeoPixelBus is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as -published by the Free Software Foundation, either version 3 of -the License, or (at your option) any later version. - -NeoPixelBus is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with NeoPixel. If not, see -. --------------------------------------------------------------------------*/ - -#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) - -#include -#if defined(ARDUINO_ARCH_ESP8266) -#include -#endif - -// ESP32 doesn't define ICACHE_RAM_ATTR -#ifndef ICACHE_RAM_ATTR -#define ICACHE_RAM_ATTR IRAM_ATTR -#endif - -inline uint32_t _getCycleCount() -{ - uint32_t ccount; - __asm__ __volatile__("rsr %0,ccount":"=a" (ccount)); - return ccount; -} - -#define CYCLES_800_T0H (F_CPU / 2500000) // 0.4us -#define CYCLES_800_T1H (F_CPU / 1250000) // 0.8us -#define CYCLES_800 (F_CPU / 800000) // 1.25us per bit -#define CYCLES_400_T0H (F_CPU / 2000000) -#define CYCLES_400_T1H (F_CPU / 833333) -#define CYCLES_400 (F_CPU / 400000) - -void ICACHE_RAM_ATTR bitbang_send_pixels_800(uint8_t* pixels, uint8_t* end, uint8_t pin) -{ - const uint32_t pinRegister = _BV(pin); - uint8_t mask; - uint8_t subpix; - uint32_t cyclesStart; - - // trigger emediately - cyclesStart = _getCycleCount() - CYCLES_800; - do - { - subpix = *pixels++; - for (mask = 0x80; mask != 0; mask >>= 1) - { - // do the checks here while we are waiting on time to pass - uint32_t cyclesBit = ((subpix & mask)) ? CYCLES_800_T1H : CYCLES_800_T0H; - uint32_t cyclesNext = cyclesStart; - - // after we have done as much work as needed for this next bit - // now wait for the HIGH - do - { - // cache and use this count so we don't incur another - // instruction before we turn the bit high - cyclesStart = _getCycleCount(); - } while ((cyclesStart - cyclesNext) < CYCLES_800); - - // set high -#if defined(ARDUINO_ARCH_ESP32) - GPIO.out_w1ts = pinRegister; -#else - GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, pinRegister); -#endif - - // wait for the LOW - do - { - cyclesNext = _getCycleCount(); - } while ((cyclesNext - cyclesStart) < cyclesBit); - - // set low -#if defined(ARDUINO_ARCH_ESP32) - GPIO.out_w1tc = pinRegister; -#else - GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, pinRegister); -#endif - } - } while (pixels < end); -} - -void ICACHE_RAM_ATTR bitbang_send_pixels_400(uint8_t* pixels, uint8_t* end, uint8_t pin) -{ - const uint32_t pinRegister = _BV(pin); - uint8_t mask; - uint8_t subpix; - uint32_t cyclesStart; - - // trigger emediately - cyclesStart = _getCycleCount() - CYCLES_400; - do - { - subpix = *pixels++; - for (mask = 0x80; mask; mask >>= 1) - { - uint32_t cyclesBit = ((subpix & mask)) ? CYCLES_400_T1H : CYCLES_400_T0H; - uint32_t cyclesNext = cyclesStart; - - // after we have done as much work as needed for this next bit - // now wait for the HIGH - do - { - // cache and use this count so we don't incur another - // instruction before we turn the bit high - cyclesStart = _getCycleCount(); - } while ((cyclesStart - cyclesNext) < CYCLES_400); - -#if defined(ARDUINO_ARCH_ESP32) - GPIO.out_w1ts = pinRegister; -#else - GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, pinRegister); -#endif - - // wait for the LOW - do - { - cyclesNext = _getCycleCount(); - } while ((cyclesNext - cyclesStart) < cyclesBit); - - // set low -#if defined(ARDUINO_ARCH_ESP32) - GPIO.out_w1tc = pinRegister; -#else - GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, pinRegister); -#endif - } - } while (pixels < end); -} - -#endif diff --git a/lib/NeoPixelBus-2.2.9/.gitattributes b/lib/NeoPixelBus-2.5.0.09/.gitattributes similarity index 100% rename from lib/NeoPixelBus-2.2.9/.gitattributes rename to lib/NeoPixelBus-2.5.0.09/.gitattributes diff --git a/lib/NeoPixelBus-2.2.9/.gitignore b/lib/NeoPixelBus-2.5.0.09/.gitignore similarity index 100% rename from lib/NeoPixelBus-2.2.9/.gitignore rename to lib/NeoPixelBus-2.5.0.09/.gitignore diff --git a/lib/NeoPixelBus-2.5.0.09/COPYING b/lib/NeoPixelBus-2.5.0.09/COPYING new file mode 100644 index 000000000..153d416dc --- /dev/null +++ b/lib/NeoPixelBus-2.5.0.09/COPYING @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. \ No newline at end of file diff --git a/lib/NeoPixelBus-2.2.9/ReadMe.md b/lib/NeoPixelBus-2.5.0.09/ReadMe.md similarity index 70% rename from lib/NeoPixelBus-2.2.9/ReadMe.md rename to lib/NeoPixelBus-2.5.0.09/ReadMe.md index 580e72426..a3ff660be 100644 --- a/lib/NeoPixelBus-2.2.9/ReadMe.md +++ b/lib/NeoPixelBus-2.5.0.09/ReadMe.md @@ -1,15 +1,15 @@ # NeoPixelBus -[![Donate](http://img.shields.io/paypal/donate.png?color=yellow)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=6AA97KE54UJR4) +[![Donate](https://img.shields.io/badge/paypal-donate-yellow.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=6AA97KE54UJR4) Arduino NeoPixel library A library to control one wire protocol RGB and RGBW leds like SK6812, WS2811, WS2812 and WS2813 that are commonly refered to as NeoPixels and two wire protocol RGB like APA102 commonly refered to as DotStars. Supports most Arduino platforms. -This is the most funtional library for the Esp8266 as it provides solutions for all Esp8266 module types even when WiFi is used. +This is the most functional library for the Esp8266 as it provides solutions for all Esp8266 module types even when WiFi is used. -Please read this best practices link before connecting your NeoPixels, it will save you alot of time and effort. +Please read this best practices link before connecting your NeoPixels, it will save you a lot of time and effort. [Adafruit NeoPixel Best Practices](https://learn.adafruit.com/adafruit-neopixel-uberguide/best-practices) For quick questions jump on Gitter and ask away. @@ -17,6 +17,9 @@ For quick questions jump on Gitter and ask away. For bugs, make sure there isn't an active issue and then create one. +## Why this library and not FastLED or some other library? +See [Why this Library in the Wiki](https://github.com/Makuna/NeoPixelBus/wiki/Library-Comparisons). + ## Documentation [See Wiki](https://github.com/Makuna/NeoPixelBus/wiki) diff --git a/lib/NeoPixelBus-2.2.9/examples/DotStarTest/DotStarTest.ino b/lib/NeoPixelBus-2.5.0.09/examples/DotStarTest/DotStarTest.ino similarity index 100% rename from lib/NeoPixelBus-2.2.9/examples/DotStarTest/DotStarTest.ino rename to lib/NeoPixelBus-2.5.0.09/examples/DotStarTest/DotStarTest.ino diff --git a/lib/NeoPixelBus-2.2.9/examples/NeoPixelBrightness/NeoPixelBrightness.ino b/lib/NeoPixelBus-2.5.0.09/examples/NeoPixelBrightness/NeoPixelBrightness.ino similarity index 100% rename from lib/NeoPixelBus-2.2.9/examples/NeoPixelBrightness/NeoPixelBrightness.ino rename to lib/NeoPixelBus-2.5.0.09/examples/NeoPixelBrightness/NeoPixelBrightness.ino diff --git a/lib/NeoPixelBus-2.2.9/examples/NeoPixelGamma/NeoPixelGamma.ino b/lib/NeoPixelBus-2.5.0.09/examples/NeoPixelGamma/NeoPixelGamma.ino similarity index 100% rename from lib/NeoPixelBus-2.2.9/examples/NeoPixelGamma/NeoPixelGamma.ino rename to lib/NeoPixelBus-2.5.0.09/examples/NeoPixelGamma/NeoPixelGamma.ino diff --git a/lib/NeoPixelBus-2.2.9/examples/NeoPixelTest/NeoPixelTest.ino b/lib/NeoPixelBus-2.5.0.09/examples/NeoPixelTest/NeoPixelTest.ino similarity index 91% rename from lib/NeoPixelBus-2.2.9/examples/NeoPixelTest/NeoPixelTest.ino rename to lib/NeoPixelBus-2.5.0.09/examples/NeoPixelTest/NeoPixelTest.ino index 6d323b884..415c8538c 100644 --- a/lib/NeoPixelBus-2.2.9/examples/NeoPixelTest/NeoPixelTest.ino +++ b/lib/NeoPixelBus-2.5.0.09/examples/NeoPixelTest/NeoPixelTest.ino @@ -25,9 +25,7 @@ NeoPixelBus strip(PixelCount, PixelPin); // For Esp8266, the Pin is omitted and it uses GPIO3 due to DMA hardware use. // There are other Esp8266 alternative methods that provide more pin options, but also have // other side effects. -//NeoPixelBus strip(PixelCount); -// -// NeoEsp8266Uart800KbpsMethod uses GPI02 instead +// for details see wiki linked here https://github.com/Makuna/NeoPixelBus/wiki/ESP8266-NeoMethods // You can also use one of these for Esp8266, // each having their own restrictions @@ -38,9 +36,10 @@ NeoPixelBus strip(PixelCount, PixelPin); //NeoPixelBus strip(PixelCount, PixelPin); // Uart method is good for the Esp-01 or other pin restricted modules +// for details see wiki linked here https://github.com/Makuna/NeoPixelBus/wiki/ESP8266-NeoMethods // NOTE: These will ignore the PIN and use GPI02 pin -//NeoPixelBus strip(PixelCount, PixelPin); -//NeoPixelBus strip(PixelCount, PixelPin); +//NeoPixelBus strip(PixelCount, PixelPin); +//NeoPixelBus strip(PixelCount, PixelPin); // The bitbang method is really only good if you are not using WiFi features of the ESP // It works with all but pin 16 diff --git a/lib/NeoPixelBus-2.2.9/examples/animations/NeoPixelAnimation/NeoPixelAnimation.ino b/lib/NeoPixelBus-2.5.0.09/examples/animations/NeoPixelAnimation/NeoPixelAnimation.ino similarity index 98% rename from lib/NeoPixelBus-2.2.9/examples/animations/NeoPixelAnimation/NeoPixelAnimation.ino rename to lib/NeoPixelBus-2.5.0.09/examples/animations/NeoPixelAnimation/NeoPixelAnimation.ino index 66b4d4aca..4f0386abe 100644 --- a/lib/NeoPixelBus-2.2.9/examples/animations/NeoPixelAnimation/NeoPixelAnimation.ino +++ b/lib/NeoPixelBus-2.5.0.09/examples/animations/NeoPixelAnimation/NeoPixelAnimation.ino @@ -29,10 +29,7 @@ NeoPixelBus strip(PixelCount, PixelPin); // For Esp8266, the Pin is omitted and it uses GPIO3 due to DMA hardware use. // There are other Esp8266 alternative methods that provide more pin options, but also have // other side effects. -//NeoPixelBus strip(PixelCount); -// -// NeoEsp8266Uart800KbpsMethod uses GPI02 instead - +// for details see wiki linked here https://github.com/Makuna/NeoPixelBus/wiki/ESP8266-NeoMethods // NeoPixel animation time management object NeoPixelAnimator animations(PixelCount, NEO_CENTISECONDS); diff --git a/lib/NeoPixelBus-2.2.9/examples/animations/NeoPixelCylon/NeoPixelCylon.ino b/lib/NeoPixelBus-2.5.0.09/examples/animations/NeoPixelCylon/NeoPixelCylon.ino similarity index 100% rename from lib/NeoPixelBus-2.2.9/examples/animations/NeoPixelCylon/NeoPixelCylon.ino rename to lib/NeoPixelBus-2.5.0.09/examples/animations/NeoPixelCylon/NeoPixelCylon.ino diff --git a/lib/NeoPixelBus-2.2.9/examples/animations/NeoPixelFunFadeInOut/NeoPixelFunFadeInOut.ino b/lib/NeoPixelBus-2.5.0.09/examples/animations/NeoPixelFunFadeInOut/NeoPixelFunFadeInOut.ino similarity index 92% rename from lib/NeoPixelBus-2.2.9/examples/animations/NeoPixelFunFadeInOut/NeoPixelFunFadeInOut.ino rename to lib/NeoPixelBus-2.5.0.09/examples/animations/NeoPixelFunFadeInOut/NeoPixelFunFadeInOut.ino index 45e66ec38..f6c065c2c 100644 --- a/lib/NeoPixelBus-2.2.9/examples/animations/NeoPixelFunFadeInOut/NeoPixelFunFadeInOut.ino +++ b/lib/NeoPixelBus-2.5.0.09/examples/animations/NeoPixelFunFadeInOut/NeoPixelFunFadeInOut.ino @@ -16,13 +16,11 @@ NeoPixelBus strip(PixelCount, PixelPin); // For Esp8266, the Pin is omitted and it uses GPIO3 due to DMA hardware use. // There are other Esp8266 alternative methods that provide more pin options, but also have // other side effects. -//NeoPixelBus strip(PixelCount); -// -// NeoEsp8266Uart800KbpsMethod uses GPI02 instead +// for details see wiki linked here https://github.com/Makuna/NeoPixelBus/wiki/ESP8266-NeoMethods NeoPixelAnimator animations(AnimationChannels); // NeoPixel animation management object -uint16_t effectState = 0; // general purpose variable used to store effect state +boolean fadeToColor = true; // general purpose variable used to store effect state // what is stored for state is specific to the need, in this case, the colors. @@ -75,7 +73,7 @@ void BlendAnimUpdate(const AnimationParam& param) void FadeInFadeOutRinseRepeat(float luminance) { - if (effectState == 0) + if (fadeToColor) { // Fade upto a random color // we use HslColor object as it allows us to easily pick a hue @@ -89,7 +87,7 @@ void FadeInFadeOutRinseRepeat(float luminance) animations.StartAnimation(0, time, BlendAnimUpdate); } - else if (effectState == 1) + else { // fade to black uint16_t time = random(600, 700); @@ -101,7 +99,7 @@ void FadeInFadeOutRinseRepeat(float luminance) } // toggle to the next effect state - effectState = (effectState + 1) % 2; + fadeToColor = !fadeToColor; } void setup() diff --git a/lib/NeoPixelBus-2.2.9/examples/animations/NeoPixelFunLoop/NeoPixelFunLoop.ino b/lib/NeoPixelBus-2.5.0.09/examples/animations/NeoPixelFunLoop/NeoPixelFunLoop.ino similarity index 97% rename from lib/NeoPixelBus-2.2.9/examples/animations/NeoPixelFunLoop/NeoPixelFunLoop.ino rename to lib/NeoPixelBus-2.5.0.09/examples/animations/NeoPixelFunLoop/NeoPixelFunLoop.ino index 3dea4c0e6..c8a7788de 100644 --- a/lib/NeoPixelBus-2.2.9/examples/animations/NeoPixelFunLoop/NeoPixelFunLoop.ino +++ b/lib/NeoPixelBus-2.5.0.09/examples/animations/NeoPixelFunLoop/NeoPixelFunLoop.ino @@ -29,9 +29,7 @@ NeoPixelBus strip(PixelCount, PixelPin); // For Esp8266, the Pin is omitted and it uses GPIO3 due to DMA hardware use. // There are other Esp8266 alternative methods that provide more pin options, but also have // other side effects. -//NeoPixelBus strip(PixelCount); -// -// NeoEsp8266Uart800KbpsMethod uses GPI02 instead +// for details see wiki linked here https://github.com/Makuna/NeoPixelBus/wiki/ESP8266-NeoMethods // what is stored for state is specific to the need, in this case, the colors and // the pixel to animate; diff --git a/lib/NeoPixelBus-2.2.9/examples/animations/NeoPixelFunRandomChange/NeoPixelFunRandomChange.ino b/lib/NeoPixelBus-2.5.0.09/examples/animations/NeoPixelFunRandomChange/NeoPixelFunRandomChange.ino similarity index 96% rename from lib/NeoPixelBus-2.2.9/examples/animations/NeoPixelFunRandomChange/NeoPixelFunRandomChange.ino rename to lib/NeoPixelBus-2.5.0.09/examples/animations/NeoPixelFunRandomChange/NeoPixelFunRandomChange.ino index 17d6b6a48..8e8866775 100644 --- a/lib/NeoPixelBus-2.2.9/examples/animations/NeoPixelFunRandomChange/NeoPixelFunRandomChange.ino +++ b/lib/NeoPixelBus-2.5.0.09/examples/animations/NeoPixelFunRandomChange/NeoPixelFunRandomChange.ino @@ -14,9 +14,7 @@ NeoPixelBus strip(PixelCount, PixelPin); // For Esp8266, the Pin is omitted and it uses GPIO3 due to DMA hardware use. // There are other Esp8266 alternative methods that provide more pin options, but also have // other side effects. -//NeoPixelBus strip(PixelCount); -// -// NeoEsp8266Uart800KbpsMethod uses GPI02 instead +// for details see wiki linked here https://github.com/Makuna/NeoPixelBus/wiki/ESP8266-NeoMethods NeoPixelAnimator animations(PixelCount); // NeoPixel animation management object diff --git a/lib/NeoPixelBus-2.2.9/examples/animations/NeoPixelRotateLoop/NeoPixelRotateLoop.ino b/lib/NeoPixelBus-2.5.0.09/examples/animations/NeoPixelRotateLoop/NeoPixelRotateLoop.ino similarity index 100% rename from lib/NeoPixelBus-2.2.9/examples/animations/NeoPixelRotateLoop/NeoPixelRotateLoop.ino rename to lib/NeoPixelBus-2.5.0.09/examples/animations/NeoPixelRotateLoop/NeoPixelRotateLoop.ino diff --git a/lib/NeoPixelBus-2.2.9/examples/bitmaps/NeoPixelBitmap/NeoPixelBitmap.ino b/lib/NeoPixelBus-2.5.0.09/examples/bitmaps/NeoPixelBitmap/NeoPixelBitmap.ino similarity index 100% rename from lib/NeoPixelBus-2.2.9/examples/bitmaps/NeoPixelBitmap/NeoPixelBitmap.ino rename to lib/NeoPixelBus-2.5.0.09/examples/bitmaps/NeoPixelBitmap/NeoPixelBitmap.ino diff --git a/lib/NeoPixelBus-2.2.9/examples/bitmaps/NeoPixelBitmap/Strings.bmp b/lib/NeoPixelBus-2.5.0.09/examples/bitmaps/NeoPixelBitmap/Strings.bmp similarity index 100% rename from lib/NeoPixelBus-2.2.9/examples/bitmaps/NeoPixelBitmap/Strings.bmp rename to lib/NeoPixelBus-2.5.0.09/examples/bitmaps/NeoPixelBitmap/Strings.bmp diff --git a/lib/NeoPixelBus-2.2.9/examples/bitmaps/NeoPixelBitmap/StringsW.bmp b/lib/NeoPixelBus-2.5.0.09/examples/bitmaps/NeoPixelBitmap/StringsW.bmp similarity index 100% rename from lib/NeoPixelBus-2.2.9/examples/bitmaps/NeoPixelBitmap/StringsW.bmp rename to lib/NeoPixelBus-2.5.0.09/examples/bitmaps/NeoPixelBitmap/StringsW.bmp diff --git a/lib/NeoPixelBus-2.2.9/examples/bitmaps/NeoPixelBufferCylon/Cylon.pdn b/lib/NeoPixelBus-2.5.0.09/examples/bitmaps/NeoPixelBufferCylon/Cylon.pdn similarity index 100% rename from lib/NeoPixelBus-2.2.9/examples/bitmaps/NeoPixelBufferCylon/Cylon.pdn rename to lib/NeoPixelBus-2.5.0.09/examples/bitmaps/NeoPixelBufferCylon/Cylon.pdn diff --git a/lib/NeoPixelBus-2.2.9/examples/bitmaps/NeoPixelBufferCylon/CylonGrb.h b/lib/NeoPixelBus-2.5.0.09/examples/bitmaps/NeoPixelBufferCylon/CylonGrb.h similarity index 100% rename from lib/NeoPixelBus-2.2.9/examples/bitmaps/NeoPixelBufferCylon/CylonGrb.h rename to lib/NeoPixelBus-2.5.0.09/examples/bitmaps/NeoPixelBufferCylon/CylonGrb.h diff --git a/lib/NeoPixelBus-2.2.9/examples/bitmaps/NeoPixelBufferCylon/CylonGrbw.h b/lib/NeoPixelBus-2.5.0.09/examples/bitmaps/NeoPixelBufferCylon/CylonGrbw.h similarity index 100% rename from lib/NeoPixelBus-2.2.9/examples/bitmaps/NeoPixelBufferCylon/CylonGrbw.h rename to lib/NeoPixelBus-2.5.0.09/examples/bitmaps/NeoPixelBufferCylon/CylonGrbw.h diff --git a/lib/NeoPixelBus-2.2.9/examples/bitmaps/NeoPixelBufferCylon/NeoPixelBufferCylon.ino b/lib/NeoPixelBus-2.5.0.09/examples/bitmaps/NeoPixelBufferCylon/NeoPixelBufferCylon.ino similarity index 100% rename from lib/NeoPixelBus-2.2.9/examples/bitmaps/NeoPixelBufferCylon/NeoPixelBufferCylon.ino rename to lib/NeoPixelBus-2.5.0.09/examples/bitmaps/NeoPixelBufferCylon/NeoPixelBufferCylon.ino diff --git a/lib/NeoPixelBus-2.2.9/examples/bitmaps/NeoPixelBufferShader/NeoPixelBufferShader.ino b/lib/NeoPixelBus-2.5.0.09/examples/bitmaps/NeoPixelBufferShader/NeoPixelBufferShader.ino similarity index 98% rename from lib/NeoPixelBus-2.2.9/examples/bitmaps/NeoPixelBufferShader/NeoPixelBufferShader.ino rename to lib/NeoPixelBus-2.5.0.09/examples/bitmaps/NeoPixelBufferShader/NeoPixelBufferShader.ino index 37a109f46..c2c8e74b2 100644 --- a/lib/NeoPixelBus-2.2.9/examples/bitmaps/NeoPixelBufferShader/NeoPixelBufferShader.ino +++ b/lib/NeoPixelBus-2.5.0.09/examples/bitmaps/NeoPixelBufferShader/NeoPixelBufferShader.ino @@ -36,12 +36,12 @@ const RgbColor White(255); const RgbColor Black(0); // define a custom shader object that provides brightness support -// based upon the NeoBitsBase -template class BrightnessShader : public NeoBitsBase +// based upon the NeoShaderBase +template class BrightnessShader : public NeoShaderBase { public: BrightnessShader(): - NeoBitsBase(), + NeoShaderBase(), _brightness(255) // default to full bright {} diff --git a/lib/NeoPixelBus-2.2.9/examples/bitmaps/NeoPixelDibTest/NeoPixelDibTest.ino b/lib/NeoPixelBus-2.5.0.09/examples/bitmaps/NeoPixelDibTest/NeoPixelDibTest.ino similarity index 97% rename from lib/NeoPixelBus-2.2.9/examples/bitmaps/NeoPixelDibTest/NeoPixelDibTest.ino rename to lib/NeoPixelBus-2.5.0.09/examples/bitmaps/NeoPixelDibTest/NeoPixelDibTest.ino index c421b5b1b..1a13c135c 100644 --- a/lib/NeoPixelBus-2.2.9/examples/bitmaps/NeoPixelDibTest/NeoPixelDibTest.ino +++ b/lib/NeoPixelBus-2.5.0.09/examples/bitmaps/NeoPixelDibTest/NeoPixelDibTest.ino @@ -34,12 +34,12 @@ const RgbColor White(255); const RgbColor Black(0); // define a custom shader object that provides brightness support -// based upon the NeoBitsBase -class BrightnessShader : public NeoBitsBase +// based upon the NeoShaderBase +class BrightnessShader : public NeoShaderBase { public: BrightnessShader(): - NeoBitsBase(), + NeoShaderBase(), _brightness(255) // default to full bright {} diff --git a/lib/NeoPixelBus-2.2.9/examples/topologies/NeoPixelMosaicDump/NeoPixelMosaicDump.ino b/lib/NeoPixelBus-2.5.0.09/examples/topologies/NeoPixelMosaicDump/NeoPixelMosaicDump.ino similarity index 100% rename from lib/NeoPixelBus-2.2.9/examples/topologies/NeoPixelMosaicDump/NeoPixelMosaicDump.ino rename to lib/NeoPixelBus-2.5.0.09/examples/topologies/NeoPixelMosaicDump/NeoPixelMosaicDump.ino diff --git a/lib/NeoPixelBus-2.2.9/examples/topologies/NeoPixelMosaicTest/NeoPixelMosaicTest.ino b/lib/NeoPixelBus-2.5.0.09/examples/topologies/NeoPixelMosaicTest/NeoPixelMosaicTest.ino similarity index 100% rename from lib/NeoPixelBus-2.2.9/examples/topologies/NeoPixelMosaicTest/NeoPixelMosaicTest.ino rename to lib/NeoPixelBus-2.5.0.09/examples/topologies/NeoPixelMosaicTest/NeoPixelMosaicTest.ino diff --git a/lib/NeoPixelBus-2.2.9/examples/topologies/NeoPixelRingTopologyTest/NeoPixelRingTopologyTest.ino b/lib/NeoPixelBus-2.5.0.09/examples/topologies/NeoPixelRingTopologyTest/NeoPixelRingTopologyTest.ino similarity index 100% rename from lib/NeoPixelBus-2.2.9/examples/topologies/NeoPixelRingTopologyTest/NeoPixelRingTopologyTest.ino rename to lib/NeoPixelBus-2.5.0.09/examples/topologies/NeoPixelRingTopologyTest/NeoPixelRingTopologyTest.ino diff --git a/lib/NeoPixelBus-2.2.9/examples/topologies/NeoPixelTilesDump/NeoPixelTilesDump.ino b/lib/NeoPixelBus-2.5.0.09/examples/topologies/NeoPixelTilesDump/NeoPixelTilesDump.ino similarity index 100% rename from lib/NeoPixelBus-2.2.9/examples/topologies/NeoPixelTilesDump/NeoPixelTilesDump.ino rename to lib/NeoPixelBus-2.5.0.09/examples/topologies/NeoPixelTilesDump/NeoPixelTilesDump.ino diff --git a/lib/NeoPixelBus-2.2.9/examples/topologies/NeoPixelTilesTest/NeoPixelTilesTest.ino b/lib/NeoPixelBus-2.5.0.09/examples/topologies/NeoPixelTilesTest/NeoPixelTilesTest.ino similarity index 100% rename from lib/NeoPixelBus-2.2.9/examples/topologies/NeoPixelTilesTest/NeoPixelTilesTest.ino rename to lib/NeoPixelBus-2.5.0.09/examples/topologies/NeoPixelTilesTest/NeoPixelTilesTest.ino diff --git a/lib/NeoPixelBus-2.2.9/examples/topologies/NeoPixelTopologyDump/NeoPixelTopologyDump.ino b/lib/NeoPixelBus-2.5.0.09/examples/topologies/NeoPixelTopologyDump/NeoPixelTopologyDump.ino similarity index 100% rename from lib/NeoPixelBus-2.2.9/examples/topologies/NeoPixelTopologyDump/NeoPixelTopologyDump.ino rename to lib/NeoPixelBus-2.5.0.09/examples/topologies/NeoPixelTopologyDump/NeoPixelTopologyDump.ino diff --git a/lib/NeoPixelBus-2.2.9/examples/topologies/NeoPixelTopologyTest/NeoPixelTopologyTest.ino b/lib/NeoPixelBus-2.5.0.09/examples/topologies/NeoPixelTopologyTest/NeoPixelTopologyTest.ino similarity index 100% rename from lib/NeoPixelBus-2.2.9/examples/topologies/NeoPixelTopologyTest/NeoPixelTopologyTest.ino rename to lib/NeoPixelBus-2.5.0.09/examples/topologies/NeoPixelTopologyTest/NeoPixelTopologyTest.ino diff --git a/lib/NeoPixelBus-2.5.0.09/extras/curves/circular.png b/lib/NeoPixelBus-2.5.0.09/extras/curves/circular.png new file mode 100644 index 0000000000000000000000000000000000000000..e4204dbbdf02c42801b16afa87c30edb075ca324 GIT binary patch literal 44762 zcmb@uWmptk)HaHM!k~nNC_ONUpnyuZ4mAuVBOz_j-6EYKr7!3}gfZ1msExITQf_(G~E; zMoI{NbHUl^+4%=b5l)cb#k2%|AU1!f`jCL2F!J*8vy0$ohyy~`nSg+z5&w1Jr~PLW z@XPBi@=shI+nc$#KXWoAaCl~E=fY=a>cS?-caKjX9Cl((K#;GjB==C$!)W!y-GkOL z@urvG*=pB^;o^mkKWk#<=G|Q`k)sJIAC?l=mY1@HO{-pNxzoj52+K()yKQsB2BPml zH9^nzjsQwU8t6`apY$0q98Qh7O9)F2R5Y6L?%Cd;4tu8}Z>C_PU@}pEn=ikGi=Bgm zJ=~6cCVc%Jc`W5x^l( z$T!vB_F^aUS@kPh6?xS3^jwtRLy8#ku3r@O9McXB4UHn92nGU802wPTkzBQfJH6@X zP%(rP61*V*tH^)&@CSq>T(YUTxoYO)RRVuwu%@wB{PJ6lCBcybreP!lTglbv#;252!W*3*Q;heQ zUz#ZQO2wOLD=+Y7UR|ZOF!kVxLNSg%_vE~xp6*wg#N9XY_lZ7%!yTX<_bG=)zhC5@ zUEk>Fu6{W7u}cm6%U>mFA^W)h9<+;0Qq84XOO(}u<~7%N`d`=noGncuq5|Q(U@tKS zoyQQVU)mlQHMc%ySr7JYSJpUqjwu|uc;zP|b&>sII=%JBkM`4#(VtHqT-Iox4#eK< zRsL*CLck@9Aao#!pcr}0E-v|cub^O!TTsm-c{9~*Hzc)3_Ef(s;X$=vH`%tqn}}%| z?gyW5$#Mu1Mbbpjj(p6}CzR5Wkm==p*uGI&HIAU8bGNu8l%JUrV(HGmPCVhNa#SU$ zl}AKSaS=}KFX3Uv#@v0Q#IFew?tOYN7)*Cs-L>79EZF7R?6+je;(1IvSbFf_r?ZBf zK9=rd|CFObbV+X8?N3H})m^n7GJA>codf zP9wI9VRi@aSWr>%Uzp90X>Eg8?z0||5inf@cEv2-!T!Oor85v_bmxp$ha>PcVWj&T z3`rCT%dHp>K4kUrlq|+g*|XJ3#^tX;y0=(!Wr&VSIn{za;(3i%$@i`61WZ@JI6|Vf zpL;SI{bEa@N+L4h-U=7s#TqGR9m=g1meLLin^%b6Wi4Nmr=!!h_%Vr=qx4L+jQ{xd z;fL;iHPfp2$D%zwq>4)8l9I9R1qWz3N&1TfU4dxgshd0du@=tMC#^buj+PiCfna)4 zLNoc9@OG+mA~q?fot=qx?iGoznC;DvHrRm6syOL*bM4y-l>N!?K3VA~(2IIBk;=?v zFArVePyXqd61rCjHukn4Q9Y9>^J=1n`KKEt?Y*_1*U@s_yZ5*VM6ZEOY~L`pmOt6$ zpfBWwLSAX+=aa1-}lrl3j4Xa#2-@c9h2^tWrJ<~xTm6U*M35|?Y zJa>5_Mf)y{=YFn&_CI$=5``SMAx_IKS`Kp?P=hPV094^49ll*5kd@iE;;z&8(DxZV9=@MBO9g+iu*l z#y9lmX)Y!uysIVR<;|nd$!8VUqN+dYsn7~dRZlsa(RtzfD~~mkb$nyd@bFYC%jj$+ zh5vMVS3T7`dvu%srH$v#J(-&A+-^}AZ)%xg8SK%yH`n-4@cV^mARpz)Ova$m(afxR z-GSAQcLy@CPo^I#&Neiho!>4#NX$>pG2GqEkS~bq_}vV(Jo)7@yzpR{4ue9Wsw{eB z6Ywh9v$nOZeOWBN_@`1eB(h^T`EYfn->@!qH}~P>Ns!|2&3i*pA<|yO-Ge^OUC9+5 zjmt6bWEI=n`TF~Xe#0p#i#Trz#O+H{^M6DDGum@ zOB#*q2e(;`Pj=|f4h#9l{<4T3{!lf*RETqKKUxba3JX3A9*EQKeyBYnu9}6>gy$F! zb6czVhosZTUYIXaX z`HwetC#rU4#jY}1@$^(3(aK< zw;H;aQ+j%V?s5E0XM}i#4NLd6yZCF zZ$P)~hu*BZ!roc-RIb-GbF)7e!@3Ieqa?9|zv=x(l;V9~Qr=4qe`B2d#G^{?+MLZu z;IH=sYbAO!V<}7+GPk^V)W`GN|BI$W+H;MzHNMGiGHJ?IYKg6>)5a2?;l55o4Y}8i z>gld6if}7SyXn9Z-?fIY7uA^zx6T*!blh9Bnq%?DMy9o&*i$Uui3df)95l~aevxK` zf??@CnnApN!1>!^#?6Stlg4A$?AD`*#pJq|*+ut04mwe=LP8}g^xL7_&-7-(F)vW$ z{}#ror>EELoh;Ya#3uA4HYA3ElHp|9)ZIXX_WC3wbgaTFL>4!)d3nvSnyvo<6mL)0 z1v+GzKgb;|O3T*kRX8a(_v*W^w*~|ZOh3#xO$~SHrIUYU*`YTl0gZagIVc9pV-YKx z?)AyO^l!(%Nn)AB!xN;-md5h4fLUH@CP5EATEkg%iPw+cTa8Ir24cK%I$Yb{=Oa5Y9h@-8`>c20yi=n7 zKr=oZzjOkIB2OMkw@_KsG1^y0of%_Ag+B*u$H!If8o&9g_nJrNhYC(+W35p1&yn)8K455fjoenl*V_wa)&)Ht!>v{` z#1k0BaX=&d(d5HdGVQw;(Iv;llhR-EWB!{DD=uj+*6iVU`KybmQ!if36s z(@xI+M3GV8EpBLj>0yRYzJ$dO2eC3$Jswx`VeR%nHbEg_XSkk>dG{fU-|hY>JnHvv zBIDD%X@zhZepk1Ew%YshlP{5d(z4i~^OFyS|SqwUYbj4SR;g3m>ZuRR|LoU>0Y zUC6YR0OoX%RnIq3jLK;MScHI}mQ=z5d;Np1H=X&wAKJt~Ik)`wdGt(7W;1fNfIeeoHvr0V)I%wqxxwb8>gKrNT5{k&HHT+O~j0!}O2i0NEvctWuX zedwq_Obk#)s7L;WZh!pTQsN_J~Uid*J5{G&y&fq%IB(OcsbsnI(c~^ zN0jvfRz-Yty*+L{7MYBc4BVMbi(I}UiojgWcug3JKe*~!yTjStb6K4b$oECTaMbt- zLjo0k$^M_6H%G#`SIUlGbQib|xAc>Q(>8hp0(JJ`u&ZUl#E?);LocwE_Z*cck2$h0 zk>lmn^QdMzvX^ESwbOd;*rb)vU3z8qYS)(_)Jm=OYIPpDJe++#C>EGGC1d5`tW303 z4}Kfi6M%X9?XUi&tEQ05$&IfxY`@DAe&FWSu=G5E=+7X^)Tg}kW{Q~CE3khbjTpVp zGBjV~BR8s7e?RvAq^T*JK65(!?!%?lK4#}uySz~1c$q5ld)FsBYgKWtkMCu1Z4-V& z!aH>TNeO>%-FPB+}%5V3^PNK%$GqN}q(1o8PnbeO4tQ z3%udwm!Gt3eh|2}#2&@KKI>$YB9(1vIJuPdRx#*};`B$`7Bf#E;_4Ic7+G8q<*E1! zBfo!>eapMG|1MPhILm1AH|i?+)t=zVq&}`L-+u2e!YoZMh1o?U)Njh(Dz$GhQv@S$ z@=A9ABak>7EYy0D$smX~zWD5%x>}Q?2@yq=Ul%zT*tb4L&3GPYWKH^1+V&YGTuGbn zMf0lN3^b568WzO)Il; znM`TlUbE#tsRGuknQ9rh%bSIX;#tL_UI4G#HmvglYAIjf&J7dlmx_Tr#776D0`~DQ zwyExchK=p>icx-Z9n;BuzyUV!W&wPAHW%OScgC7&brEmw8yjw7!sOk%HxBy)T58&( zL@JJ33Yq%RosX6i@!sG#9y0ECN9#7;vjrxWw--*b3fS&_dQ0NT7`Rz}i3t-| zcakbh`8P;25AVaN#DmQTPWOr}Dw#%UfEMnP2m>wXjDCYy8jkI87Mvw9XG~xq+IR@? zHKkm@x`2YY{`p|Ttx6_+60kgj7X!~@(Ri`@R+IL{V4Ny?51~unO1ez~@WCyC%`Z1I z-r?@o#pZ4W>a(n3Ku6E``! zxnWuhtZ@)+Qc0Kw52{y@@%?p9m{2Ss4aLn159S&YN-3C!VmJ2+s&fN(wAKsbd%Wv8 zDg~iYtGUj=ELL_n$drWQ$xH+22q~3DuE!)KWM5W_3Ddj_2f-%kYXNKKts42cHhD%9 z2GMtAqk8U~*iX%7Y6!tQJ}WYh5MC}vRVu{+hS1QBi%K0Th@AZVPq7yW9Ex@&_Fd1U z)#AfXtB(Ak&T)TQTjCPZjmGBW`K1SynldOSf0w%UQ6yYaL+Kf47JI%&BFa(CZN%{{ zosyjp%hi2lZfNf#lmVd)t(59b1+ldGW=)WfjMN8UW6?5%4 zt{M?fxS}@GYnzu80WF-hToi>hQLf2Cxa+q4q9NBHKsRf>Mir~PmjI9DwuF;sAt0zA zPt`qwbbWCpE`Q@fzv!-St+32c$!6(U4)%6rV}7!g_$@IDhSZcpI;lA%3WRl}-ScP{m!?|@Hdn#He`5r0^!Fzf^4L*9`t>~h!%s*=xMhC5=QAyN zaxQENq*5`Cd=c&972Pz74-0!q%?AjD?GK&kA}zl0vZttfhlK|(1lZnh=-j&BE(%ML zH1M)mh-bne_jJb;D1rY?Z|~RdmyQ22V9JrKE09rG=(r=&rzN?(XLt5OQLRIv2|O!MvI!(m$*FOwHQt}* zflgnG>dKO7aNjk}q!;6%AT=m;OVE0dR5Y1%$TFA6i{m*#PPLW~y#zBQd}Oc~MN zj2C2mr?oi6cr}|Eb&QcqAW+L(jR+5J5pDVuL$3|c!06uG?V7rulMaX~ecdjBCxG@M zU?1yE$K}(QThD4_-Rq-VOpR^ri6W}LuPK>+A#W%-KQr60o0u+=eTg;+Q(BFdMLABG zdR1(l$jqaEZER3Uzf0;L5ZVe@{0qBPo=ut4{qSUkBlPJK=jLC5|8d2}qW+%d5EC znEg=}Z-8c}|G6YpB)t7j+2MC%S4&@*40d1oX0vzL(RAukKq;qF0sNZFc$AB0AqGgMbpsjEEB^2GNQ$ zqRrs$o7?Bjb>NM@)}Z-duZW3q5+@D&NbiX}h`S>V=h`+)CjmWBXC|q02M~b-e8o~n z9^E(I0-GT}di|SI&WByB(<6yrX zi^|~OlZFT89Cox)5aLDq!=byst`;~Wsxa;kkOO#t8gk#JU%m16$gk`g(D72kW9RA(1D3 zV$Asd7xi{S{Dq-lapgTXv9~Oj*OgMNCF0Niyy@-4MJJ<$Uv9diittoJVCv$$qD8+w zi7kBjbb=r|pco2`3fW&41^#>g^TV7-R{C|_R%mO)s4WdR5u^g$?U6k>lN2L?f-k0muY$M4p1JCtMVuMy*rN$JwXSi79+72q9>P~q> zu3AfQui#hKQhG4&p2cfJUI~Ss9Q1E?LU zyD3+wKe*-uA1RUZt2t;Xdd7r_C`e)B%msMUzt-VEVE^fReI%mUFVY_6LZmDQRp4B&9mpeT`UIEGR%p>J>EHd+7q*}EEFs1Q z58AFNODAtL8u-#ujM!OE9=Psj{4N_f7!fh?#@X5$y144E=&v|f`&hIz$jf_{f&v1qTCcjsO%aRToe6l}x%&!=^^RinIkM0^E9uo<^sWSS5G z3;6nAbY9UKoKmn2xWCf^T5x!IY5r6%+BRIpKPx|G671cJRSj;-Kks}(WKjpxs;Ht1 zU}40$t6Le@ZQ%egLmxY@?0w{ap#@Kr_DxeEAd!_77AV?Jo=3-|IvXJwtgDVGDU{p` zmb-uGl7cNK3~7)2p8I9)JD1<}kJPxZpcPX>(d!IF3B6;#Fcoa!vba_KLyineJA;M5 zlA=DpD2x=|{;0tA|8Xkbns}L(ghc4mI0}Bh2=uZN9&9sVaI3BvVyQmk$ZcuRG28{U zJo>7WX+jCsw?PT89-f-a@z~jt@k9Ggy-^|fi4!ql1bUHXQj=fA}VBzT;+Uu2ySRAzH7+y z9z7QT24;dJzCZw6nUj|n4VlukXjv#22_av;i_!|JyG@PqJ`(4`&aa;;at~hulGRvQ z+A`NV)e5srZ)Mya$*iQvKJq5YB%>I}hmMBr_o6%B9dj&-L02bjUbg>j=h|~Zj2!mc zV@()=I_|oCsrVB#3Vgo-l%FDotO!< zmA5Bj*7+5HK%R&9L~qMhd-2Gf)+n=ye!^>-MhyYh+N3MI*mn~qXd+PX+vFk_)^Mek z#dTDsk}(83Gk!bsd~S=y<-BG7EPl<+-ylwZ?{Q1{(?e0s$*S>_C1ws8k39PPUv717 zKhXnzP_4UA&QsD5?ACWAx91Rmf53r*pzT})e{)oklOJx|Iu>1}n)sRsG2K5hlHgpxy(l$Ik>6M7=B!GT z%Il2;5uYGnhdnEAW}(oF8RBRMI2*?7WKIr4b>4wAhm*btgod%=4bbdMAr!dT^VT^T%OQfW@c(>P>eB=I9<>I#NRI2=w9F}(50oPcSMJA4)q4>jV9_z^R{0*=JkFeg@y8)iz_?*r z9E05p4shi1_G9khJD<_C^z?(7swyh)Vg#SRc;TaR(Z8pWmz|lST+Zn#fX}fU-rY{{ z^sC&S8tsie_YM&N(uG%+#VI0qJD`&<{o?sj~&_ zLg!3?d*gAplX?^q1tf^hpbxkp**$PJ?>J(e~-{Dl~S1_ixR+#wUGy1YtIx1WvF_Mqd1qN>E*QfytIOSDEs$B$O!N4mkzG=vnKPlKqtn+$ z;ID<*XZEseDfVWoz58@7*zgEIzSb>yYrMyMR;StU4CKHDY*;=~<5I$3x07v~Fc6!p zFJvU6-8fl*aAKn~Vo=VP2b8G$;e^3*P zOCzmhRs`SBpi=D`_4M_DJB2V54L20zMzxgL8!w<;q&akvdzU3)SHq-V_Fpys7!yOw zu&5Nsb&vakf2TuuS@k=JXN1=DUJTY;rPl<&X|cj9 z_tN;^>h?rdVMloSvopT2&9P9>^d@Lp<>Vv-?EjujGztNT3OPcT*>&**%Ss-6(5{^` z1p_Oh@uTF3`f7ld1I9hZD@FvyQlF2tUO(LvP(yI^NF)&niCB4?SR+83A<$ObPefpFuS zkDi|X3!RUQe&vZ0wqjs!)tEZtc|?hTQh5Cv5z|#{q4>g!D^;8BrN8EUc%Yn|Hvh-p zVFvUte1InMP~q!LsG>&sP*M;|HFMS{O-KF0GTcs+>Z zPe7*bqOS9wx8mEFyB1|Ub1iC3*lwu!NFi>vE=nz36#+Dn`dWWW6l%K47lVO3t2viO zbi6`~Q454K;814l?T1U=(Q@$r&l`T#I~eA1y}mTDs)!PTzWXQLVvfDd9|`NwFhy|S zm8a{XA~M0)%l4@Y3vn^A!^!9^=Pu>`PG<_9i*zivb8qAKVt+Pn6gt0fa=dsFz+2l^p_+7E@X5PlHR#%fg!IdHhpM zSCPq)5nbeI;o{&@)``U>!U!h|`#2<`!XE2vQ4KIy(P-?pNdGgezLtBY)&KNTb3AHj zjB2OWgnRi61vb3ch<1tUa?<#_328%&LfzHJ&Fh60oPari4SE_MV-b z({N$ie9HZ-&+5L((tKUAZ9c=w&YSnyfx3m{Od0P`r~6i zF6=lX;jth5Ke4By+;ScbCY+U!>-Q$<5MReBj~wrxn7of+dpY8&UwteE{1}5)ycK}= z4!hrZvkw~~=f>gx2;{o|fdtnR-@WXjxA$+1-SKr~2SRs!Xeh9LFJyM-5hPo>kiII9 z{Dy0-+uny_)K)ajKl_Hde!;4E6D+Muv({LtC#n#|w@bJwaAJ2i^)tz)A9#L-dVWPB z$g?>B%hEEpXfPERQ}TBsgJc)A`#(dZ6a>Da27j~M6 z3rY7qtSws%3 ztE2+nTlduv1!qOp^K@s2Mg*5F)eC!j48E$~e1KpAVH>ctm~Zy?CO0-%z*4_^oZIJD zL!|badfl-eC)SR}JYHue(n7AaRk8t~I74odOtQ30i6-s(LiP4i_Jm7N4X5QW@pmpq z3q;w61$S%#ugxp#%b9r>IH9kD|IX;MftxPsS6_L#uzZJD;@q9F#A)-6VYUmi3pc?4 zn^Q)HGw{lLsE2lscr_^0&XQ$up{~(b+MB?2xLI1*ET5lRJd)s$`4-GFWg^pI{1dX& zNnbTyrOTm3n%4Zqe7vyN|FO-psM1_C4erAO`*+wMejAgbu<<)m-2Y^eZ~E^*<9eEM ztyfO!Zp(-o_a>mg>9ZLvQOu#b&#OAJHslb-Yrq$0uox)USlV8m$$-xFBoTw=t#68; z$XJ1sTVdwqIg#&hh(#~hOh9SaX0EJpmq#!SoOyFU{9H0oJ&-RUoc#Q9baU`R-t8hW zXo{!-PyYjEUGW%Mln3LP02tzJrpbJcha8b!q5M~D=_ok3+nMoLGH5SK%YSW%X~ttj zObszQ6DcTCiV%7fDs?7u9xx)gUxJI^OUeJ?3@)1z$|dK<7t|gXQ;I_K4xUA8LSa43 zNzR%v<5OS-nYgD(D>}xaAbM)@_G8cd3a-Gf@5-Q~D~&gks|~nm*r-AB2Eb+M-x<6m z_>RB&g>7^{G(nbHq%Y@RYFADM!NFOKI+j)&Y{s=7A(Nw*%g5f7tgV7?x+t>k~DDxsyv%z;HFmP??d;U&`7T6I3DOQBenRujC4n7ihFVM?*1}Hr$ zT0xVyaDjT`4oG(Us1GA!Z!(YE2(zr-S&$}FWoe@z!C zagPDD5rzcYX#D=QNeBkVuC~YV45=ic8T!nW>AoxO_Lgzo8`4+g@#cEj0_Bz&=C;2F zyqJFvnpd3s^nM%-E-~4q zY`tkO4Gp3B!fDIk1x1&D`gJ0&clmjLnBYsPY+$*oOm&CVbku>V2Ss7-1XlE z&RvO{G|*oeS}tV5;LLa9FaiKjxI7pgCM7QxwvC5S2~n`NXoN&{EzFAaDFBRHc&EOy zY+&I+Hz3JUE9H@0AlRRZ%`c9kuV;vMY@Rujd3Af|w`3pd z&%FcquboaAE32 zJ?XG_rqCtce~wU2SAbTDe%lZabu#wXd!KSpC?ySMBI&+-vTXs)Q=0M3SC7|hDU{6J z??hu>fW`apCidM67P_CQ<@*@dLyfBS8_ALUoF}~7ua9|-NKxcY@*SN+d z(549&cRvi%T#e~DQDnmKu}bAdanb~qRaN7j7Aau)ZM3`@dVaZOdEA=z2TW->P z^X#J6$_{jK@hjA{jg!t|1&m~qv2v>!_5EWIhNTl=YaK>6aDVs=t4u&CHj)3v!}8J| zs6n_11k|orn-Gesx7dYZX(kWq0duJRo|7{RXyiNj0)8A#+t{7&2)(nYq5JG(e<{}UGREFhU*5yt{Q+uD55(s2`AVB1; zS>8;bpi>Ww`&H_BqGNpiR%kY` z{twHuczK>Tsw8pQI==SgCle;8U0u1qfeOR+d^&jKDQ;m`LKNzAFs*|k^aBUU%fz{k zYLZ%MW$YEP8X}407>?`$9BUK=0_ixa>3a%_7e&_Ms6i#(b=)C8mQ1bGyU{Tg4R;1& zjlY-#IkWO!=1vYUJRubN%JT+Eq!Zk0Q6yDZL`0dcqxUAZxkMLrF$*^#xV00tjUtZ? zDbEL#!nB=frDh5!Bw#0}rv7XC#IN$z9Imp$s#6AmSObBSB4uD2WHCFuPYjwF@lqBU z5rQVZdJ!nd7}*tv2b0UouGs+)(3pBJP)>O7fD3SSd({gvFj6Hd=rXu&ZF&D%HwOY1 zp^jBZANiD!8dEI_g&In^zM;Xjshwq(HVG}3Ud6Cce;m~#{kFO+0j|F#nhHp@Zg@iX zIa@5v1%qL(4MxkN7QQ#2oB*ZjPzxf}?w?n5AeLPV%=a&R%j$2(h(gH^XC$~^+4HJ~ zd*1{U`WsnlRAYuWC}44C!r0L+BKygyFyF>eMLXjIZN&Z&FE_mSDU^=fSmLNX2EEqx zs_m?!&`jM>NGPoxfD4u&!G@iqCRLhx4s3_8%zG3H0^<69MdXyBa{mp$O!IXqAxu`# z#?AbGHsV5?W`M&Ph=QDE#EWwqL^=hNLdk&Kq5qt5ul3du$fgzvgWo{ zJg5oK!QF(p=wCh+Pw)<=!6|pU*16OBSo{sJvo`#x0SPtXJp@UTv>R*HPzgA7MI=a1 z;B^EjWHDH0VvQ#9soW@kQ=gV{3>|E`XPHxH0yKF+kG?T<+B5 zrksCs*X56bZNNQA#}tnilp3-7b3(&iNd^XnP*W4z&yLuFJz5e_L-w!$7ZgQALp^+N zS(QS`Gqevo#{<;BfIvJAl^zcUDS$%L`j`hqWYEi6I4Ek<;iBZm1Sg;|lq(!Lo@$HW-t8s0{E+sL51hYbh{PT{QX|MLfS2IHhR| z1Gd=;xu4GQGBima#Cc|GJPy8a_Ly){7@dv&EzUsz(@AvS7coL0f4*8RDX1X|jn!jO zCAgbdE^v_oU0h@t%N>n(`A!E}0>x^{Xl=Jc{?71Slq)Y#aTvD5CKhdUa4E&=Rjjm+ zJzEG?*1H%>8~YzrZWy-Ho>0|I$fL$3WJE}Bm0Rzui^1mauyS*XfZU=YLPF~!HLCkY zz6|_wgwwJIlnd+byPX}AjsZs7tyf;*vQ`B``xrXSHe$spEw?aWlkT_MGndVPpYRQ$ z=RaHL4|aebp141@T46LH^rxG-$v2G_^xi^o%d^5@B0SQ%X*0c_IB&$#=b8pQHtksH zhMY$kb539cGZDKEFiW?Z9{MkJhxD%hcBjd;+%pDnbVB>Db17r_dd>>k;(Ab$LV&HAD#p#Wo&HNS ziuOTysofk1we(^SmhilHEu%)bJ-AM=Wc)_HOP2b=3^SOrR$>B17ay)*S>U3BP+=~> zn}mbR%VS6@9mpe7s4R6smNv#tQDL+|8{(E(WWP?(7Eg-l2cfUR`QnTh0(S-q*yrMS znXLwGS3x4hHg9|UOI5XZY-#;NL;zLqp7>UL>W`1Sj6=44 zRhkq^FG9M|C^_qnC&1c#p4$V@`cj_jL(21v26^(~5~sLGkV~`PsYZpd0RhNHK3Jb+ zo*~94A@HX^zJ)Sj%Iqg@XOhbQFT0WfX!%B|Dr0xi-FIP%p_g%Et_fv0+^?@7CkAle zCW6>V8hG_G#(J*|Bx2oHBy*;jH-X@3(!#=mj_)Zn1e=pPn8^^D_;!DQ*Yhza$j&&| zDKF;R8^vW)<6h0xR0~m>CXUkD=>LF#Y6JC5T3v9mjYeJ|jJuw^@f~Ip1lcxG=PxVdQR3gyC!Z@Rloh^|UnPgAvgB&-R zFxR@?#iLAmQ=aEe4rR^j#q6mpUaRU-C@bj7uF~Y_8~fj*-nY=~m920s5cXAhB%3f6 zU6*_{;jES5=i$uj0#O)qr#N>eX*#Ghaafo&ix&zF6Q&$>7seD9d@fAW{Zu)S-&+qL zM)6B*3_B=t9ZKJrG3_5Y8(;r4b%!w!)!)C844lU>NoQyN*<)(MlTbJcg{*U7w^-}*SSoxDPv34Mczk~w(NzKX18i2+zI*8VJKw>af))rr7 zRkM5l9l|}oGKu7d1Fs(2oag1H4BHVWeCBv42V7eI`c5>aJxxM*5CmhGh3{PoFO`3v zzQ9ACf>=}_aKY_gB+zNpCt|G7BhCNB`m3177r7Q;>t zr+zZ^&~_ZuSIh?PgbZb)E|OLzE=zz@>#_4yJ7*2lO8k!KJYdfTOXArE(5?58x5$=0 z9|!E!#a~S$en-O}o#Qz+{M4qd9l2qh;S4zIl9;jV@q5@^V+36?(mPz3+-}|GM+nj5 zDrdO3WO;oII|H1W*xG1I4NrY_H(#WoQj1pX1{Ha32WH%rXyD`<^+Ng#Gng;nRS#<1 z0lk-dhlhcmgbp(^GgDOp9!*7|yd`6?*r^X+qb%18AM-#v5kIQ0JR4Z*=NE&~(Z?yZ z+5-P7=N|560%~g-(-NdWhV{nTs~C2}DQKuuWSc@^VIl2%4}*uoVIJ}MlgB?l7(2t4 zLnnsW#%^7AgnOO28zb>!jIks0dk1hV3OPJ27%MDD%79A28wgM-sC7%MmFUeQR@FB^ zbKp&q)?}krCY2fFwy-lIkx4=anI8~1cTK}7ysRrN!B}DR8H^glB3H0*l$qHlJ{YLP zdmPkV8im855(uK6s~lef>M)^?L(s9k4BlaAgj!Bg?2or-j^axqUPyU8O7;Hw1h6by z&gnCCS9(3KhYnrG{DO!wh&XFzfBF<$L|gGaa2^6uQu@)iwEVw%Emd|gVbqu;z^!`g zHg{DmzD%&6dlqRVkP4uv%!V9u$h}OmlG$!}a`QtEaIA*a` z$QE(mZ#j={?CA?V!1&{-GoA-e#w&c^(%`)L_t6-}lCeCSL0j9AJD_yIDebS`@-h>k zkwv%W7*=m4FfG}obo;qoUsCy`X($g5J_B`uGMH}6iKMgPhgeZ(!sQkCag2l9TDwo;kliryk0-n{QRRt@XQjd}t$^3mb6 za>U;U8X$K`rVtnq<;ElD|NU+J+38zoGJ4{~4r%N2T7`hLeCI?FR5kjpMtIkOTPkCdm$JFW9n-MS;2-niv!bjbWkFBn7VU71#bseR)>=BfFn3%6AvbZtJ_RP7b z;D(w8#ZxK<8-KPq7}QK-MT8W#q|h~?3ylbn$5OGhv^{Y+R#8Lv9VnfF!)QwAFK{RM zMmN;5F$cg;9)K7Qe;9#LGyub^ZrHcBl>sVedV9W4K8j&g0GX<%VaqAu!Q+)0jv`(T z@rAPHpeE|8ge;Qc*Vs^ndBA@hexCEx7Kgfq?{$zBd-$_CZ{#yIF41SLnuuKP^?P+i z2Si)4Kz%QPo<#A21Py~V-IgrBA7l`F}8O?a5{2%i=~$iY;p5fdg~ z3+#NfhoFf*FAdw6UFmvST=3`GV+2V^6v(Ljb(%4#L?RmF^J97Z8v$Z;064O8*wJr7 zlA&ld20~>g&o#RHh_N%Cb!kk}o$0)j$L`0nwOIRRNBE)a7l}kIS(hqp&uAJH0N=>g zdm}<7`lm+9^7$&jRChia4a(YjJ`3D`v=U!SGk0y&yZ#J;)BrEDYf$vo_`>oo!{%-h z(ZPfob_%6?sS#+504VMdFcf#^(Cw351$EjE1WGGyp(h~X12tBta3s*C&HwrsRfBK6 z6l+~`XhFB%SGa==ooN6Q)dL9jVmEp~h0Dh5fQeTdeSbo@ebkJ(lf3CC;YQ77!aGq zmEDNg&zJWOkV6pGDi7M9f~OH;&|SH8o2g6NpLoIbS0a2&epf$-41j4dma#mk6vKR} z%4XTFI(ETl0wvj#GS2WeU1<~m^-#!;bRrcm71Y+pQgm!?I%bu+@3g#J|uC+GBijl?AH5q(gZiH3$DR2Z z1o?#f;l%M)HWmO2T}Stw_1-Oi9bZa*=y6WExb`H60ru0X3`QJlXP!v%(%$ z(Uv`XkqL8Y?>cAZqldC(UvEpgIW`6e{>WfEXJ+MO@g-59j-PGhNWrerhoiw3(9DZy z@K)gtcMvg191k|lSZC)tQ$^m1lCGxY!mc(NM^l$PM1;A49%?^9k05Gom@`68Qo@DA z32@%;Kr|y(vKu4JvO4aOzV&=4f7rb`M<4-MB0hW$5++sBQ1zZ{;Rh*Mhpge~cu+HI zR4*l+cNxF3ftH47 zF_!Q?g`d#mq;#jjB{Tt>_aRn9l|oBL!DVMeR#jJkKU2+EfZ zHbs~)VVB!MNf98KWXq^F2C@+YYw(>>)|!RpLlE?W3LsFqfS?lu^U)C&&>AlP&26qTnozw!*a z#Kt!u<_oS4ALv}3?$Gl{nV|i7o~uIP>nIvX;i!^tU|{ynW8Zrr*cdDzd;-o}zn$vP z82VyNY|I|ehfsKB{)OhJ7s0u#XaOA!SXcavQ^MT~qjWJ(f zvy>i2$SGzxO4Gu9vW0L=gqmG3k}}u^@oA7xN(VnEG;pol6TH8gfRYD8;q(hYk(ane2!qkI zg_=ozEZqGSId2LmSF)NEbMJMFjLs;t58Z&A_SYp_5pX^i2cFVr}k zX8oB5K}C~@&re5$c%mx`x$qIH9|9gvmblM`9w%QGxxL;-^DW9Dofko>6!j^)^1yvE zVli;1Y3CwXl3Z$?2(ZhbA~J5*mSU*B6hje&Nigg#m_srUi=e4~F2#kdU%&TvD_kGn z-2S)-UzEDfifrW)G}&(6XpXe{_K-v5d#JE78%#cCH8IKB9x21F<&SXL@A3iJq?2VD z?gRWgsG9M=QQ#G;lSHybF)=Wj3P(nf!QfjcZ71+VU@?V6xm5yC-qj}Xk}bDcMIgi# zE%W;T)ce!eHatLtF;!Fw%f)hpL^>lF@F`_a;Mw=r2eb`y#Nz$t=caKxs|VTEk;_F_ z?T)WME2H#|rNN!Hj$?^000&8xyT>#G>ecM>$$Qy2Eh$5=H)yUYwIa^nC8g&;ldL|J zJ@ZZEqqZa&`+IMTc0E)FRInNFMez`WV@XY`W1s+!GOZ(u;5@D z+s;0_zZM@pF+0rueDiDUN&Q}Q{Zycf&F+(IICw$MegVv>Vd3-#RPj}#D3IAw;2pfm z+o52aPE}q|Z=uF2R`0j11RlxDwzd5M#XPqp=~6yd82D~?<&ku%c5c0Vb4O7J-2Rs1 z=6?Mn0q$Z?NR{$KlB{K2Ks<9L1UoC1m5J^B#Ru&KF~D7#cY>XL40D!LOICa-phB;) z^29om47|;DD!(&kz?iO8?*0NKI1MUzGPBt4K$rNdjWX*0=t+F^)Ds*P*?ggfkWi?D za*|TOF>j|AMyNV{_m4qt1y65gtD3-I(eZ}cUjdKNrXJsJKQJiVwhcWr=$QCK!C@%z z_GnI*2-@1@;B5OwPpIEr6k2;U+Kv6y+z)9jXwSd;?t%H^dcTFhn0tO7W7xopL_62J z>sLS~4c|eS)4W{_=5x?Hi1IqFkj_qhON}{*wy5~SAnxMzn28HZw|=6u$}Ff?as`gj zuNBu+EAgqlWrQTq;MnfbpilxuuU=afAR()!jQHUBx$(SfhqtD)?ZNsPM+o+Kt`c<- zJbbIf?fl3~MxRc%7ubz8&?Qp2VnqG`@MVodfzHoV5f|p5tN!izP=b&}Jc%ng<;nd@ zK7dTg(b3UxCGe81o8D`tuWaZ&Qu$&CMO5I`BqepJrB6ZsF$_}?He z=@EuNb%*r)`;Xkyo>vk@`FzhT@6_G~DE8x3Ao0XP=I5jrGaJ#J;p5j4&r4jCiNE_x zOEZ7$!4|XkU?e?phPWj<^o+n_iRAeU{;0(5{QFh1&vYD0aoLFLrh{w)U=`T6zDN20 zzFu7DqQYHG;ZBVvAK_3A`nXrJ`}x$rYe38?gaQ^7dmu{9QG@jfnK;Ys8V0HccitS@ zvp7smA9eEEHvaqsf{|nV6o&Zu{^8kIKQ7#)uT^F;4j^fgkz9+lE~F6l{}cSH&EgT_K_f-t*qC-WLCLIZl{U z(c0KWS_0|07u)-+LcmdMd09JISTHjMd*D4A;>(cE_qhzaT{+mJS*rvgx62gDX@My@ zG+6QB5&NTjs6JNB{(>j2iLK8e1{lKJ4bh=M5AVKIhg9mXmz55@ufn3M6^S`N74b=N zp80?OglNY7fEoAd*XazV^k2yC&3J zk~NFxyeqfj=Iw#IP((2KfXzloQonDG9a{MF^EcMnG#jK;+TNxr-X8+}kXAg)Ry!0s zGVk7bKJT>b#_^yB!TTk|KLpOsON^+Ze|5a>HxXxOnx(bL6tv)YN1MT(T5e`(uhy&YH=E%XaeOj^DmGlTRZt- zi7d3I-ba(yl#yw^5R?!?hq6eWY|4;C+0QVZvbW}1dNm(0+jOP=%Ej5mk;b>H zqj(+X)sNALv`?Addp~KWKK<6t0L%%-K^*Rv+^v=ABy^xaT+K@u(X6KZBI~1y~VIv2jWK!XFYjJg>-{8Yy_HZ6XZjR;HVVKo5fa^ zuHYe|?B-H)^U)A((0(F}`4X|reeNKbDKeZ|sFM*{m)djyE51B~O4vVKh`VtjWa<6z{QQc^swdFu4U zx}EGvl0dWU@94r?e8kPK{kd&y573^19IlY30R{wIr8_=%8DLNbV_6xrNI(3jN+RO~ zY;WYmx&BwRM0&N4Cmu+bY@+mo3TQ}3XC12@ulT{ci+|c6BQH{uJAqv4t8!Tka@~bv zoP~x35Nc2GqtcUpsQ08FdCn&>aw?tJ0tO3U;CG)Rd0c=H6iS7zsTBe$mB8%+ii`Xp z8}11c8sm_(rhu2XT01iLaJ*l&Oo~&Uc77LT6P{5ZM1yO~kNuChguBk(VKBsR||0gO)yjZD%Hi`cZp#gwis5!VSUxB?RjV;6i)epiOVTpsKp5imwrO- zS32Onp{{R7VK=&a5Ok2EOoXJgqROF(oj^x)LWQ7i_MlsCZtlM@dIpH^(&nRAy>L}< zjXG&JAjt>&MS)3s1Vz_ue|EABL}rlYuH#aCfM5z{N8fOj_%6`R8E3&%bSA`~)wM?f z3*64|g*Wd`FYpN;C@#F3FKYkguQIM>05G(X1RES>@HwhXCjj{Tne{R8;wp|f0)RU8 z$y3EISX#_(jALn%zOIjT%C&O|HX*WhvI$Rtx9)35vaH-^PC8wO1=Wfh$@q?QSdDS3IuvxwgMSGBBfR`vJ zGWO>hpk0e~vZgd@b#5~kJ~H&$upK#dKQ`)3dU60(-I||a1-IWV6U05iFT0r1$K9t& zUp3Yx%XFOTdX- z4Bmv+lZbvSKsC*c{i=8?MgM_}NT%za3_`z_lWlwYfkm)N=mr2W=s zG-1*xo4yhQ4TMOskAsvJ2I%{}3runHvJ7aP8)yAl;5vkg7)1(o{+x*~JP2YMfdOzc zuU5!&5kZIvZua5J?+kLV_TqyPKE*;RchZ=(qd$EKv=01O$pY-9 z7WeHcO2hfJjI@8pta>a%jF9vZH133T!%y9yNFlp64C^01kk4r9eQj5JlTAliQdv}? zBVHNjr7(BxP+nH|wjI|3ouJ<`Jj2g#2I~7xo1ro>G&zA{R>=Dh(tsR_ojrPbMMzea zszWaFd?Y-qj$didPffsfDb)33fDpaLctAeqO_b0rDwtheCRsrtg4G_wzOZpdkfP+q z!`-_v_$B5?N_6V5Pe|X#$0xRc+gxzYIYs9)uXbb3?01O4-@Qd9W&tD@!ZkQ2qY=HY z3nhRC3s^0nxE=dUVj`d|ZI>7ziadD{P(b2Ix9R8%KnX(so%w+vA}d{UJB8snt05*W zj8gd)_nfhCl#nzl8aLVd(Q^+dN3}=`$U5sFsUMpPpM+`V30l3=z_>s}BtBIp1Kyjh z0uLXeSg`*C0ao~}OizOqwDJU<_;aws{{C8+C+iE|ea#2HeY7TvkpLJ?QUZqN*PH#6!vSJ7F|7im7CiZq$eXtRKS;^?GL~tYrOqu;S%UfO$@f* z=yofRno?=Yu#7*lMyH47Wo65=$4oD|D8Y*Y@lA2?dZW1v1$6BX_!F_nTeuZ!Vrh^F9OJg9h0;TH|B8i-V+aB%0 z9V)(zd9QiHarrG(4P-^igsx}bIY__tlK%n7xvb3DDUINDyk?4`L3VLH{MiX=tV{fR zk$9Gt<(L>`(0UI@Iq8Q8U`xHnWkq41=$FqKjf_cAt zWAiBK$@ker#)v;>TfV2~ns?44$j13!+M7D%J%Nsspg1pnO9R0Gv=udgOd2>PiU+Z@ zu(h&bXp)znsfz=ZoMk^x1K58(t5x@Srki9q7KZ(TJpool3v)bAjcwQL^tlBf%e+si zk!Fy?jSAe6Fz@8Ho#l8hL5w(Z6|@il*#&1<<8N+wfWX{%Fba?1Rzn5VUgxrv7Ulz? zNK<0^K$dNVXrS#RT9VWPs$I1A1D@6CfX*kS+>V*Bbu>_S1MT zPK1*%H&AWV@i|0`uK^g@|CS%y%%vvlE|R_XV++3cq2FRcTzn!|L`8F=Ee4M?TGx(~ z#@kP6Vdl5TZ@{$bAX?W&Yb3TAWHSje5zalW#{6k1ok@%{eGTE>50WR8+1aK2o`Rtg z`YAeu{|!oCVB?>i62XXh6XtDJ$h}{hflN%zoBs;( z5KgRp_NLJ!2*G6hHJq)fU{$~&|At3?Bw&_q081ocwZh;I;T$}-(IfZl; z6Xe1vDyS$B_4z`*MyRdbe_4CKk_i^J_WV`8u(QnQerZ6}bWM~!mAPfEWEXd+Cfyf# zy;{57AH|*;B;R=EM4?g3&a*2Eq&6E`5qY_1PEgO%Io;hg5?8Qz#>#c5FChrDVTfb4 z8F|RWEB4P<4yXf?Vn)$W12J^tyIXIY^Sz+|i%UZ?0 zX^`6H$;=!0LPMJQ+6CyD z615)ZZa!iG=I@np(=l*9hMywg;<-1lVmj#haBgAU;EU8)Z_jH6>s5-}pw9H`-3?s$ zv5!^-%a6i~MpTW_ymTYOGN|U@Xast)cehXonb9x}qZDD3LG=4XmKM2meoaB%$-L(N z2&;lV9$psasm0Gs3|RFKO%FNz*=<7((xM3i-bclflyNwZBEU;^n3r8A@{=JqGDtGE zFfnmK#vh-p_}qZ^vlumY_jZ}+OO_V9!^Ja!-ztD|so5s58dmGC0;r!Fqy+$gU}Nv4TRz}I%Bih*;ffr(+hgt6vc6B0ndkP~PE zH*liCf);d0U2VuyqO%{iCqYO&$Zn%&UZK~P*V7%Wi5bl`@%na5b3e0Weh(9$d|Lr|v&J@KE%d za3rW_6K+#2Hv%N?QSpm}Ek@!rnsKguuFnn4hXOw8ajw4hIRZ`O!;LSioCMRWp`D&x zPg|2T5=cNk3^@o zs<~0?8NV+WZvncLZm?)hDn3o8vT+D~IP zFDwk5CQ)`CE>@u&U?N;ODQzbM91hK14^~WbotY~dL=n=baR4nBu(AMU2C~3{7iysJ z;Xs6w7Dm}4Jd)YhCi~aK2;3FIlNu4DDiT;$L;Hu09Av28jKjQ6`pYUhGDoTsnK)CH zJKHqcyey64JEz{mo6iO-HRVA$TaSMyV^?3yRq`_ef(-~SQop4@dM7_N`$@1J93`#u zm5?eiyv5Il@PtqA&SAKhjuNWOVHp6vFakst27 zwL6NfuNgN~8CYXNP(o zpv9-Q>drG@<%;IOU3SLfdB9UK=KG@r$)$#)EP$tg#i)I}VZ9mxibiET5e9jq*6_;X z_BNh^CKb%=APo(ac}Cl~?`1h)^h>v1MJLh1tS~;Ivx7ftgs*#y+Wimk>#GWP0DtW} zP+=X=n{Tu?mF(AiY@n(gUI6Sork7cr(Tv73-;ja=%fx6z4j_v!qcIoPW`_YXtR`$C zz^6_|7F!P7wTVu?cO!yR5%7oAQVUW?a(*i!SE0J8#6&ovGf?&mLWCO+8x@?LnE`K$ zNyMjZ980|T=(jZk7IM>Ik(L=W{?Vl>e<}EFtQj z7)@V`>&{GEk@u<N75BUK2%~#mIslLe{3{-jkhd8 zB1^SeNklUmiw#d<)|v;D=KqkpAGDr=6ZL#)g{)5zsev(b-Q{6HoD0CChF!Ub{eQdv z0dxrm`R?S}>b}n|VT-h}-be4=6m@L6ZeSm_-S9zIUVmdZMQ3#Qj&SE8B({93?-&CX zR5&#SbKoImdMOtL{*KIDU4O`2;F^&}1~}`T<#bJ_x0klLu@_$9gY!}};YV~LkW(R1 z6OE3r-RE|=+uAfG9>Xj7mX7QJIF{P+Ul58~e0_z*QJ#kr+vS6MV}bE{iBa}Xl5f^&=`;2-@V)G3Wq^l zM22I$;?de)J^%+_R+jYPdpq!%X6grH6@l58X4dw#!kc0p3P=F2NDC^!(acd@6^DC~ zq-2o6+ydB=3|g|n4aML8(NHd_!-s3gMBs9LDV?YcfLSjbZ4k(s2_a8ueai^e0_G$| zt+Z1#obpv01tik#Y=j~bdHIB@Cq}5$kj!gKbe+?X7_}WJud=`bV@n~VCXsz{z54SC zeU}Mev#100wyXS>s!woy(h8~Kh(m})n0@aRzZ$Ku_ufZMa`R6cYw^<=8-9$#H zeE&Jm2JgfB*yd(H1R(iYRGcy0&MPPYmC4G@ZM6>Oj+CfAa2@D7+&PCgG1NadPp zF6#Lz1JIgT;Yj;djKIDm4BgMU*&v`3ZQtY;q&NZQCM%>LPc~tk1oS|c~g6?<5=Og>YLqQ7j0&%I? z#NUT#Xp(^a7EK_ey+1mJG-9s*`STfBuB4tUv@bu$WcNG?97!wF$&Z+7P!XY|sS|q0 z$3{l4-h4vWe|dg)RnwqqQ-YaU`X&^ga7V{bTpfq`Plc)~X@8(6#d|0AnLj76@uiF?FVZ zAt!5R(Q36g6ldRc1oF~D%7fVjqffx;PjS7OjmXV~oQp1v#r^q|ULhr67KHWb{YVH^ zuh30b?0g}Z%hc3#NxH#w9D_4|W>6TZ-12&ChWH>W>SThyQ~oW;iURDBp@f}AehLtS zCss}SDlFp<4`Td+P-%Ix!_}B*fAb`Pm?ERM4Oh6-Xu~JAu*G|~4;85&N1-MFI^y!F z4Ks-CzzYlK#a%(z=f6S|W{59^2K`sQe7cT%PtQzP20l~b;CbfBS4fT$9j5@|UCnlT zOhR-%NU2;xJ5+xEsR{(~9(}r@|NKVpSO5{&4^?~jK)I;=BF7#tFH*(`ST@N3qJbbo9oy2h`elO1)r~`ok7k1lmUC!mO288TqsnKRfTIVveZafG9Gagaso~D z#@%^HgH`vH33?G0RU~Pz_aPJ^thHhYZS}N5k@%#XjOinwY(g6p>6?n=Pzf}r!7^IT z?50^M0ropjN23i_oIRKAAN`Tv$>@t87UV~yKokcYFs2J>d?>z*hmL3uyA>V5+u83@ zEo$_q`hDiVvp~pu36Nbd0UjIrF7JI@nco{#P0aKK8}Qs8%B^ARaJS2d%iZB&E%Q1` z_v7c3ULT+tuE zd5D-4j3p@9ON&2v8hnfuX)##N4e>D$$2E>aw$NqyJ-}wCdKS|Md0@;%`c?XuCNp&a z#rm>}3wRAE6sFH)5Hq<@VLoR_R7hRJzaR~u??0KX0KO=@=1Up9x z#K6PdAfARE`(_z~*U&PGg{lW&?mB_;3?Xb$O1RW}Dk^a3f)7V>6TB6qW0i3?{bGtg z^~d9XkmU;sTwDUj)!;;U_w1=U6nDOCGn3HAF2Mliqg(`VGhpv(S5!t4SOJ{^{G(RF zv+kghV}jcNDLe;qa6m2pQ}+WNGXj?3q2h)~h${!GzaHI4#<7BFt95u$*KYc@Hx9vT zx%*Dh`>X&_%4E`IeNBOcRsl)3U`e=5KZgS7v#tFZ!ObjHzZfhZ>sXtY+9N$3}VcPjaLqAHjLH=j44 zLBq?={{FI4=qNrqe-AW5eq}-(#C~odw=bUu6cC2qY2pITTkMOjd)*U-8^)81wy?EO z2l`~x6=4&+W`$JrkCSx3X#nz9#^8gwbA$f=-n-RWpo@T0PeU4TgG$l-*!j zA2mE4rZHYg)NEq|L@#Tv>u|JaWv|xQvD-;_Ez@W$NSk<}(Kq(+LLy|om9j>QEeVZ+ z7mHwihqUMtb`S$E~V0h4QKMQwWUQ!m%3C9Ma(CIgZ`s45J^qng}C)KkeIT1 z>N`^K?MitxWyY-Es6+fAvpJsg(t*c}6Pa^nCtY2{so6Yp72~U6N?8Sc`3(Bkb@ylq+0Ql)EMG`kN&Mo|u z>{GIklyb(w{fj#BilHhLtwEG=N8pKN*dsaxyuZ;FnT`78-aI){7Kh{bv~KXZ+hGt5 z!ZD6GN2!eKYO~r;{!@S$JdU2)gNH^GKrsgyx#g!<%0y+54T+WLY^KY3DGgn{d+ucQ zB>GDspFpiXVuzCeRd?PD-&a(G1rKr-!e(Dy$Ny$8kuM1keMRh)6&j`06Iv68$?C7L z?gHNPDCd1&@4NDC+|s~~9F)qF0frpyQI5_M z$lYAnyG~=V)ils%crMxkv7iGNPjQzuLlK<~^8=L&%(kH&*9FkEi zE$Vh_?tcd(bv{Fm#=I3@X8G(2!gq~V7)AQN$I@X-Afy01p|ilQw!PhERw4c%vej_+?6t)`Zimh@$)+Er6M20Jfcm`=}BUQ2_$Y>amjdXvP`0!VlqWlzCP6Z6iC zxAu8#!7?{ufmE|&^3ky3@zv}>G~DD9b0iBP*$BVVcT6zeWupB6tk3XVsfcdAy5nO1 z#|*n{^7dqiOelX223oF|3olecpVxdi|JtZO@s0?lzVi`CjdT|w>^Kqp`?^Ap`BnKZ z-Fn%el*Qcg``vN|t?hlMaLMjhh=U>yXxt|@vhpb8gmg`+k6M&XYQ1X{ShFn+OtDjc zotwLr(+c=kvNYoC=ib*tLoWgH0Q+_51l*BY-+%v(-TS6p#e)?6$#V0CHIp3kHA_ep5 z2oYRoK%mogghtRW1B3~(I+HS|r&kB6{+<*d9wlo$^$yn8?U2<167de+Cz@M&QBf41 zc%gzrS;jNfUg$}VhRSaV>*i~f`A16{WDlOmep&=tBfh|;Oy_eKTwn40YAcl#C^#cJPyhS*?7(9JOUpau z$pc2!uTNz15q-9uJXxHt#S`cQ>V&jXq_rlN)J|RNi9WcK^>oY{!o3FK}gZxJy zfP&e~q+OcA5h-}dr#ga&2oVg<^|`#)!M;X=tkQQNQVkv+umt+}teW+NxSD+v%@6p$ zm{BHFv7uhdc?GbvjAxt-r*FUo43gPg#WrJfhu-s<_YnmIz0y) zRsb~?pojR9&p3FwNpkhA_MbL=ugm<7^ z$hz&j`z0Wy3K?tpAfj%ME4D67EGrbg34CI_s4g|;Hnw^8zX~E7T{kY$bDH))dkAuc zNfl^_F1A3BW38S0ja$KH6!#brDu0MOhiFeQOKd;5ib*@_Q9Giqvoe!Cd(-E)6r?!Q zh8lN*aI3s#DgH!d18pn~B}6`uby0qFks`@#y-4}L%8n;Mrr7T9(mdG|43fXa{-`>{ zouf1-cgak^&FkvVk5rQ@Upk@ht4Z(afVsNkQXXAi;<^Cr;$?i4fkS;mfG{ zEAk;z-Icd_@9y1Yz$zcli6*eQ$3FyEOIC6u^WV3n;#cAA0*k1wzR~FMR!1KM&vToz z>FR|SDL5~=hSh>r@$I3tZ(sN$B?`QAD8rGUz)@+ zd;ale|0OL92)?^#P}<16M7xGz*ntdfoukU#gJ2i~HR!EQ5+~t3ZD_^=;^^;(T9~qq zk$Df_{bLj?cB2%r1LPh7vLdMT76Y#NM+23)#NVtH`4rMgVahEg-YqH0g!vxK97Y<- z^B}k-=;sdFXIx$2_Z78|G*(4O0viWTgMFba|CkA3-d`Zgh_D@O$2+YJ!wsuW0iHxn zB#x|h?0(zyd`Gc*R=a116MYwaEp1Ouo`w2ufBubZrkk4vDh#SpyNJA@c5+s=0V!y@ zc2V%8OteFSGPCUVIvZrHBP5YK`FCFGARj-oJsM)WP0{-`7irO(tpbFvA`hGUx?VeE zbvmy8w^2ebd;_iX01eVwBMN}2Uv>01Vk>t39FLq)+DoK$8etriw57*#KT8DXwuwY3 z=w7RE4KfL}#+6np;g$wa z`w576r7qb4VgnHZ#lf6iyT>D++J|8fY>p2xLSMFmy>nj0uwNAfK&KmxbD#_lqjR<4(qnP<54B5LQxTRUhqP-@Y{ z)&u-I8KliEeEYQllIT%>D2|2KtZ60>uObkxxip&t^iGsG=^d*qdOwV>}7UEm=_$8rYHMryj76dP@T; z3%yADHecl7gh(auwUh83t~+bZ>H6`A)F%cilq#(20mOKY;zyW-kKSR?;-OkM?a{K) z4*D8{qrY8v;?P>l~pUBJ%oZK+07^!=T z9H~$uWIst+vQ3RBFg#8U1t2}YJj5xe5K}R6E{NFLHHM>v7GxacA+UI0Xt@Ch6p&Pq^^8A=6F!d;c{@%#y0#aTMeubqZ11?paS%VQ8`OMSd%d|`0;x)9Q<$8Ni#tiu4vVNJ*u) zp-+HGl|v#6Zt1;CP+VF#fN^ev`M=^Cnp;DqOsgGNE1u6lUd*QweiR5uLP=_^7K^it z^2`Sw*`iYiC-VnRvQW?5v7YD)%N!gW57}>xPLq_kvL3_L$m{v(coj-;Gf?8WtC`H_ z3yCx0w;&l*UrzbiwlhybL`U4`y9O}7KM6|yV4XF7_i&-ga^xsIdFb|V0)Az`%yx4Y zLj9WOqGWx`0!O2~=rqWLl}7AKR(@!&uwru*M`uK}kAy$v;_lyLx<*O7Aq~F121{ zLHH`cllqrjdpilTVGBgr`C3;AVMZ}NC9)^ug1nzEsUp>nvovrp8>VE*Up4;?<+aUH*S)D z22O4@g5c1_lL#K_Icn@!Ooue8VrBJ~pn~^f-W^W(D%2Y_1!bWar!_o%jP*HaM(8%> zb0hwYN8p`uh|iR96Ys=ey^9h4Kxpa^8Hfc*6|}=C8z|D$n1%qV2%6WC!qv??84)h_ z>X>fNKgWWI{bFOT3!f|WwJ;oPnZ-p8!b=iR#tO}YZ|^=`vKcw{-lF)#4-?{GFa<45=6uB{=#@Ff#DA#6*2cRu+`Qo$vOBXq)WAtCVhnh?Qy@P@&NX~+Px-8Yld7_E1SF-&W}X~^4PvtSIQmz~G8b}o~{O*-FVEmfxH z2%dAjM+x404iYJPcV5dmzhrdT#^92kOzHfMTB1If(f3 z1Kv3ht^p1BXapKz&Hxk?T7>kyjgsM|16vkH|E7qnq00V#gWR)FmX!h_0B>S*i|eTj zO5t_xo9x%X>aj9XxVi|a=a-?-2Cak#)g~bh zS^Sx~1Cv9f90vxhfS87RU@bJXB-_rCr=AxFc4p0lkn~d$oA=yxdhxuN$vkdE@0UYJ zQuZb&Q29(LJ>i<$KYgYH4CIv`K(5iX**N`b_0pl4hm?Q)=Qb%msY0QRj19oAj0cA} zKW7IAyXkl5ImZ$ckRc;ioBJfXp<7*CVvIoRCpo7MMnvAo6MVFLCLqWJqQ)UUhpPA` zhtciZkRfAhHcD4;U+Gn6w0g+y=10DM23)`BAvct3?E9C}sX~FRz!ipcE}-)qOjJw7A*g0-hL) zD;z=P;>ww&dSYNS4(ByX{xYccCQs3J6v(GQq6{MXhdVIV{WQ{+6?pV^Km9yT3+N;H z-isX2M(Y1J8!J?Iwu|~17XJZ0wwCTA0IoO7s~2a3MHQbfQQJBSS0CJo?fTFRpQWMj zze*G3HEZj0hxob7pu}`6xnTKV&X4sEfFBWwK8+=#TrF(D4(1KIhr3u_1kI3Y;SUr% z!~aC_4-Y@GyL(1VOzbayr8}P{8gKkZEP(|FVj1y+lCbS)4|kZS)JSv#u7Mq)5(4x! zYwH|ZZ2uQLA98BKE9g3ATE-gk`1gY}9z1y?ZYl!UNBS;KO`q^Jv|)N5Lt_pF_y^Z8 z*(8#n#8SHL&KcCzZ z=S%qnbu(2fwv&|0MmFD0ZVxyunz!nSn%U{2#Gtny>!Bkd?B4JBQO(oCP}{ly0Qgiz2e^3voZe&3Ix9(X<8vMRVgq4@YOCR4TetUJ}Z_FU!< zwIBVvBhL;!wT*d6fHu!bSO2MkEG{kzA+Dp-g5>j9x2KBebfwOqc3LMulcoD3uK|CF z-#*%SAJyTfA1th*ba&0`h!{IE~B8u&|o(DUf4dO zP|4Z@^0=?vRz?-OyRLh2+a%pSfH*(8f+tHT(_#;(iHzZOk|riUAiMo_QaU*xBV#(y zSl#D606;=^{^!Yh9J%YwYu=`_M&yw^T8!8z7J?wkOUjl$hzNq77eBlIOXQ4SF>VHK zvc<^tfw^kOi`w<0XQ2BW0$~cX^eS_c2p~>HNkLBz-La;pd$Y{UvN_Jo&`GG@vsAtQ zd*4Nwdh)jOb%z_6XqjN3RQ}WGpRuFSY6Ln>lzQpWzBula#u2%P$rD#$FZ;04U%O+88f$spw~PcoKvQLl2smo(22$}W8^w+w zNB1zc&OhH0(0F)xYMMHkw6*=Q>TfqOJ~A##uS~cMBr>P3Wd5W=ASfO=VT7_v&`67g z*HG-!&@zdn8EVBjXL>v9#o!Yna${+j7@DCG=jw)72`2}F=OE`P!h5<HyEA;Du?hal0Y>fx>ug^eh zq@C~3(T!;AjdHE|KGumq42@6X#reIfe^s8S2hiv=Cn*pQ&Zji2z=>DNXFFKhMn1Is z$8%67$V=+=v0m-88K}(pp|-ysLs-w@#;u{4NqFfj z%<>ib?VLWb==f#mxK|7uep-_~pEK=Awww zQhQ2*%O~>mv<4^fE8iF9t4_#*qd)bh$>=P!9~v6&%1;Rd$+e**!5{S+BDGgUzV49R ztv>y#&U}#q)VS%#Y7T(7UJ^$jsL!eg5ly}x*_$pKgEzbUR-?@{{5BLDxwP5Wis$u4 zAtJOj{4hK8RQ+NQ9Vn4DsUPq@cp8BaaCE|uQ8_hNZ`B~D;6TO|GQwm-z`GneQ29&R zO^3e>_vZQGZQzr^5Cmev(H8S@@AcZRvXz$#&cOfgk(&Q;&=a1>Ew{C^EpN9L(rs-! z=RZ*Fb!JnF4^koQ=_dk-pe=*L=*w7%1`#*A)#C3w1%bwP7=y`}S7P2;6}OT1m>(0x z5qi7x2cV_2HpfB=VFTJ3DQ}54EU%+9 z=WZ4B+wEar_=P4Jhi9n z!dW)E*Ci*?)WR-L=hG{R)EtI3o{jX_AGmd|^KMW+fd(?TLde#D3%RP#oy9*t0{}xK zNwVGuxsClIr>oQZ=Ap~~w)1CLA)t_396KRq*pN9sfVq}P?)ssxJr}1SIaH8HF|xbf zUDD zve|0^MhP0MMO)hA@i}AAn>n9ifLrm!X)zf_26kwKeG)Dy>Lp>?_Q#8pz&=gxB#uJ8 ziB2gixvLvGr`07cz44QM|Cd#;JlOHnS{Q7Ya7okyvOOf;`K_2O_xQYk?Jxn_WW4#I zCN2&4fVc`iK9d(8M*$*QR#RE^u0z`?5(7j_SVX_HI7*_pVEcWRKeVv?+&B1cKijdp zPr=awKeV;0^SxRC=iJA)u|x*^U~Aq>y<%}AQE2ioQ8I7Vg3(pT3Njr z8tW7<-|Ep8c=n>uv*$AunIFuZcXwf)6&r~%??$>eRSmy7ia@-WXaY@shp`%EQxL0C zxOGlf$sJn0sqy=2_lwkIekyo8w7DH+_ONncYN39nuy6NqPm^AY*?L`%^v+|oB?#<} zWsTM$;B~>|$C4B}%>JSou!l<7GNu`y+giES9b8D8b@-DTQNdYO{Z!3?*@jyy4W6z9 zCp*D~OF_oT33ANKW^ag=2|&#RbYHK&L~7Qy>aF&7jhu`)szI|~#%B{8)pgrMMFM#3JyEt+GxI-) zuP3%%9i_ip%E_T;d;sBlAATEdQ23}J=>R7ofju!}I*N?FGuToA&Mw+TX06&lFOStU zj?2);p*ng+#>@5DW5?<^pRBOUmY-reps~3NOmCpk1fYG@Y2tz#D@0_^zw9znNM_-u*jRrt2-Q?k3Sxm7xx&39g&d4$LIdS7`#B8P4l=Y355=Ly$MfO%B z2`P9JjoZw7EO;`c8*m6R=A-dJO-JaVYwL>}+Qs4e(CNyl(Oqy##RRYxT;jAmj22W$ zm*F{34}Qh3$u(9jjcg#X0{=oiY!PA`ysSA+00Wjj_1njTksZ_Ox__Rlex|6*tXTfKZ1P=;qu9PLbQEOe;QY9jM93(ZaIgFW3~=pV zm)V~F_LYY(>rO3PV&e`2vjyjEn&hrI<*4{?fRh|xE3p{mi7K(bdH{5o-UopXR z7TynnU-|;-o3IvWX(%EqE;tT-AlLN9^HV0iDU(|cfK-Iv?}Z>XrI{|7@t$8);qxqF z=>ulV9+efZXSCU>c2b-=XqsPX%2!fV%cHDd28h`AaK*1*oh(?`CI*I5rW^?}D4W|w z)3?zYDW{`y$K!)!%iU`2-Im$7S3I|N7UwI@uZQnxve{Cezz4zjFF*3|DxWdX&6&(> zlDGZ-ES{OL)_B6w2K`=$(IRNwXBW4}wmY;Hw`*rqe?f_d5uTAW-Uunk&!1UP5M5K~ zSNE(`Y}CnMxYj#lXCOh$Z7}$96eti6Mu{0J?cwh=oK>3Hu2*vP*eG2$*`4wf-C1Ge zr!p#8R48BUP2`r}>k;+d_;9&uvuxgbODU^-DT~f{OX+yUntJk6k;R3D00!0Q?r*`dY$$o%mVHL%e(@$t{SnmaFooKkCQd`B+9 zFOSw;Mv^{$Ot_hubKb5Y2TVMD)+K<Tzd)E;*>f$=mu^Zonx`WLL`drfa@pW#n0ok$N8H>pe_ab>bOT{BMP!i5X%bDc~+ zsgvni21nW0%x030?68F@Ie4zmWxT|mE(mYmoeS8dQ=J^C=|QI`urI!0ydd`F{q^Gx zH!*3hVLqo%?BLuyt0&9U#ac}yRvWU4{S-gT&(V949zV`(*weWl8LO;Bu`(?$mTDJq zrTvx6l&W*(?xxkI-gwh1-rT1yQ5J?_wFeki1pL*_Cl-t|B=qF2h8YL*FQ&Pf z+6-LOFBmGQ5EpAmUy})64rUOJjV1LNdxQ=dx1|Me%<-b%d==F&_&oERTNDO=EW~=5h$O&t7xyy*X5TmCLFr$-mzy!EXRn?fN9Ne8Bn0fLQi&aQEtp-}$5{)2PEyj4&A3Qr5T_VRd3(Zi+YtT-O|r)XB=;E0JN%ao=T^Ylr+zduG~ z1gd0bvAcmyAI!D7)p#Avo07zp!WUZpa;SbQ3Yl%9Bxz};>rFhwMXJe5rG$!S3H*{t z`4d^Rn#Ijy%%6K?_JUp6@1JVTjvKS-4_neoF!O>60X2o@*UM!J>0P&4p@J2+=d|+xubdZiWOR{Rer^3_p9Zi8E*D?;lL4vjh_MYbl>I~ z?2g&dRk#KwYTs}VbCVQlNmyQ24IA*%=dsfduDa%L9Nm8^K}D=dy5Z-1Fzm06Vd0FU zc}%k<=(6DH0*0086;UP4Sr%cTmZW6)38KX4YVjJK>J1Bj(^mnlEt5!}Jo(Zwx(t_N zRa+m^7=)urhUqejqQ6^snR6vXjZdU~nXil&jot{yZGG()Jto}FlIweA4c-f`r@wuk zy_(nN{OoD2te2W)A&T@mD~}n?m0EN*mzI^Q-H&yLY@ckIyIQUACUnJ?6u{AleUo)l zcj#A|yj!Sblwy~>*1UobExlBV(n(92z~%D^K}yQq^isVv-Mn76Ht6n13p9ImnHPo( zjkmULP(5kVXsK^vAz?Jf)6h3ad6n_xAyGc7$<}MF^c@jr$E8A*RK?R?YHW2mYM~A4 zXX*ybygSNm6!*rLU^*un>XdZF(QFla%xeG^QKc(%2lnvDJUTyt!)U=iyjr zo*F8^zq*iKG=F*znCdt08F;Lnq5;&v;t}&y$~F8dhB3`D^#`hPOr0?Op3^ zp`n2f-luRLBTVKl^-6U2r7qNHaTRzKP9iIO0zG3hTh>GQo8=}(`gjWt?lLvC{MDS9 z%?hnT7Ccxj%-X1^peoq?c=grlkm!r!4b@7z&rAmcJiQOnRB+l*?-`%)zQU?cJ$uVo zH?Qe~+snegW9E|Gss+yFUjpZ;Q{zTXWi(O3v!C9+@y*cOOG+O*MChBj-lr#MV zvS7A+&!VoJy`WpX%2t4-N-{K5Jmk36UUuljMH=DISV6|erYdO7Ydtd~2KAnS$2I2} z-uiZ5NxqzVXsJhi{z-!0%}+IEz8!v&H9hhLObM5{{72|i4ZfBo#D3rM`#x2%ynSRr zAe=-hzbK#AL|OeefOmUtAxZu~1#PaRc3YbJWSfO|LR53fCZ}bygW+O8+DZ8^|5%>; z>;-4^Gv)0U*g|8~617D&G{=oD_h1+mlsv^_#v`d3chT1aB>l|NPnKX#Cv&LMiSGqm zth!#n0yR8NMtYX1FOk=gbtF4KAK z0;5kfjSXGHPlGHk)&LiV3Qiv;_jmo01Ne!K=L@zSl~2oOj+pU_3-}b8^Hnq`o9DVY znqLzdv1?!dRB~Hr>C*wPC^*vu6~~bUL*fUj)XOb&A}!drWrk!n z8pad`H_|iVD?aqtT3T2|F2*DvbgA<|)rXs2spb~qAw^Ga61>+fwDhHS&PK`I`|Cb_ zwMi@Okw*yLD|LG(W>87s*-hi@*=Ce}giE#C z>0YV*l6I3egYJ_i@Z~oVEPkj+V9((OWaY$>2DN6(+81p9*WH!> zv$eJRo(`vKs5%s-kg8$%6A zC$xqp5!$y=R8@qY5E4TO?xN@3_kQjla6g}W^UKah_O_nk`#kHrp1t>a1T899JCsyJ z%7fXZBHu4E`Kc7mY5Wo7>2RWOq#n8>y5P*`V~E#{Qr%wl*LE4hNUCHVEAO+2r?bZU z6cH7(9sO<+R3}z$sSit_g*O=+q1%G+9T78!?ntBmkrRVgta&)AVt0yeS0I!aY&_@4 zQ@(1wdK;>t{b@;6Oj4VY1kTjan-$VOJqGVk;xaE6eQ;M@zJY~*<-T@+_zb7*<;EbM8qt3@ z)#x)1_KPV3l)wp(mg}>LwB+7GTuB_gVCi@=Dul$`9`TEIvdJF7%|V{jX;TgS0+Dt+ zjb3Q7PGYc`Mr|0V#+XJvel2@0A;+;NYilt2M~lugW<3K~$ukzKFN5p+nzF*GnmY1v znM(#~)kU-1NMg=e8X+u`T}F#-3LDKF6Q?UX_*)+qOpBgbnj*azCrvd13}X%>hml8c zqQVIDa0PZB^5^2r;%m1{$&rpJ3fI^w`apC`&w%FkGlJE#kqPUkH$EUyrbY-b zCO@vVaNSDB4+KY%1Y6=Y?9P zCk(wOT5cJiBQ%g=6c*2((2h$!L%u*e4~$iV2h-!|oz3q=eKO9+{{EuTAWBWpPKb#} zI}k__8ldt4KHe1gd;&CLWq5+mPNHD;NpkxMzlMA|8BX0X*JJYWHuR0rs(b_WroAAf`=)}9@= z@j{WZnlQj@7|7}0J{3)r!C3nfuqkr&O<41xx+GO@veyVT91fOYG zR$OcD+1>isWH2Gp1$eQ2U(NnCyHm^ulkQs3>=C(jJqGnC)k@sB= z<7H?*F7fQLDyT-yo|;pA_FBEvK?HkJo$L-0m`03K#1IOb_lL4af;!8Aoc8Ud>pA(P zu{xJ@SiDJT0=mr%B8RxALFzf^2u#rER?%>?+3^MQcLB# zX#*S`XEf5?5LX)@$apaK#hev-WT%Lj@5YOf^~F65LP+UH0!|gnxvl_YpsOQJVnVRU z=COzF#lG&iBT7r{ui|_wRJ5cY8VS#mxw0#{ONw~o3irz=O^K|_O8B)GFbtK`{v}Ae zphfvB=OEoMPKyJ_U*=)=Qx8pqo#ycF&ug1y0 zg}4Q5TEn@((X-5?kW^W&hx2uXz|TW(uqDbVDt<(U?%-^;Ssej<_e5eBBFmrPGAn0g z(wJ0%+=2ue0z9%_!-xhk@pFf$FfLS|dnbaPU;-7~i72@WQW*+Eol-f^IdygUVwnM| zzNIc`$jW)c)dwKNleD_8nnrj8VrQ-D#1J!~xO(W_!)cLS-!d_kv#7cWy6EXvTci&X zO=lYG zxWKW}`uo|sx5ypFF$~2TMeVrDMJ662BVoQ^c>iiJ)5WGFY;jd$5Eo9zd*}<}{f3Mk z7n772sam8;%RA%AtjpX4f566v?;N%;tbMX!Hn((dbURi{sPR5n`U%c;a7y-VOd;nY z*+g`i!?SweTOw0SpGO5mc=d=Bk}J9|D39QvRHF|dKM$fvM#v${^?!v2Qf5*s97+?( z_OJUUZ5$hksZBa^?cxpu7~@POj8RB-H@XizXm*M&YU4-Xn4t_mo||?)iDC<&5roMJ z#r>XtV_pI#@hxpPz9}P&l^H!bcX}-KYtV`!uaQ^K0o!zj9YeqL$$*Z2qj z49+m&k|kspCXpjmx|D5tR`=p-QK5iQW(-F=4y~?#qc#fYJj2Zy2$tWC*2ahBn)s2o zi>e0k(upf_x`>LgYkf2u9gBkBq*?Sv<#hx=x^HOS?G0;3B8>! zshR>G=OX8Ysw?>$^mZ+NqDTh2Sf zBgP*W7>rdR{P?J!U+sUL2HnX18liX|2@ilJ9%cB^m6Rw2ATyNp;Ukcybk&>;1+tnb zno-l3Bh-hG*u8xWdp#|^4k`)7UsbFCaV_jyimIDObpgck}$>g8&U3fZF z6}T=j5ujEK%;S$-Po23%`?Im`5d_e|y#k%J`Y$`LycHF)8p=N*ebEIrAqyt`*wSq1 zXM4c^+Au6wRW8q>+A5edL3eGd0S1ULA6UnKlGN`;oY-qqd-iVbh#SZ!BOc;Z6BEz( zR^RJZ#Tjg;&fMK6EJmS-m|^`)>|Rb?gV8Ou70o$OfrY2V&=F}bG4Ep@FBH1956w&X z)PR>d1RwIFyn*0%OfQ=#zJF&L>fNLQC(;f@mvZo9^%mv=PxEhg9odtTSSv=_t~LE! ztPV0oER}AaJ!5FmQ+A|h2dFc3sL7(x7;se9;X+13b)Cm%m*v zMQVi`#5KPEmW$i9VK8^&9h*6sO8jNzAZFbbGk)D_3Z&_sRN>5;nGCuhO($zF#c96e zFj!f}NAq{ESJEvE2flR;fZ!k26GrQq5+leN2@oG(0rk+oj`5K?d7@!`Q=&rwx>yA# zTc9^;WN4xh`UfR~9hT+YI`F7ZpC)?rbV}9-%kMiyE$yYDnCya^SgTKSNxD=a`;)++ z{yJY9Jgsbj((m+Ibpgn$C2rH=H{>B{DoyQpYk0MWo#4}zAyMJhc(^xtlpXip86>Iv z*NAOAEMCNl;=fu2YcL@@Z`4^Yn!t?JaG$XoZzyG1&{3S-JT* z=A*u)6riRdP?K-iEh>ETLqFXg1`w+FbZI0Pr|4uI0NM-xyNn3HCIz+OGx@)!#Eobl zV2V9Osz(22WP0G7d;rUt&0wO`=D$?^nv&q@cLC}Z^P1rO*K*;%>bJCEr)&*#3%Bzk z8TG@83Q|o2ea#{d$FJZ7>s~aa!RtWAF*8iUHQR1k z<4qkhIoEXYZg!5jy8)|&ruCC2HST}N1SL}3J*-G_>$f>q_9Xc_ zf9+b=bKmT{C2q%h6mt{w!@C~wA1D7B#4Ouv#lvcFMtve8U_CY zxU+Zm7;wT56JWV6`F_GJ;N;<$6jsIG<$OBRW zDX5`Un_vUpWomQ-wE=qn7UB_Y6NYLEiRzmL=T*>_7#tKqb4qad9kl5Hhb!CuXB8U! zaoY~i0vLQiFNw{A2~iB+ugS7y~2ujO>eI~h5@!P zkC1Ex-Kvt((sFliZnyjM08J`-19cDwk6}RER@{5;m+ab5V6hg0b`jfmm0TC1?b6jW zV;5c$U+o5~@tAMt==(BWfz5qmSDueS_8vSxudsFpvHTrnWBv42wAV|P!`_d$*J38_ zS2wS;leiLnFRJ71`wQgY-yWWRByK}K>bwvG2m8=`SPws!&VzjT6Kv>A*|*^bs>fR0 z_hY|A2B?CaN7d91(APh6I;T7eOdwD~&8hPmE|`^#Qx$8ll0?z&*tmIO-(Eq{n3S4m z$$ph@HU6-IM3^P+!&FidTWp{wXagc))cp2<%F3cV=S%w|^cuDopK5)~AKm^c#yxK) zUeY^Pp}ctwES(?&oqqbsoyIZyqDTGpf}e7&CS^)gzmYR<`PnBbueLDtW2=TiUG5@beIfx=-* z9uTpWgO$?IXeHz?sXWL13VTwkrNvWaFK5;C`z~CbU!m4Vm2}L{4|e{-I=IqE7vqYn zg1%MfRwG$su42Yzmq?w1>PpVZ>IY9uPRq#(1CyS}*BvxN;F=V;iv_R^d@Ps@+0 z!vc2$TgZW|o1bp5ns-sxh)aLV69YlUh5Z_kKZch0&eM!jB{|mR%kgM?9dx}Q$3eyZ z(wAJP9tNdAl!MA04~R#Vl4n~Tz)O9XO=r_pc4+5$-)l4r5DQR2|H_qZD2LV#NvoK| zBTculY{Y-p-x_?LDmWxk%DTAm)U_M9{U{YBLj*^fC#3`MvYoRZZm+ z#x0D$(e^3i1;QI-)(4lG~y)4$YQf9uPHa^Qo-$+XUf(PRB%t`8P3w%CYsuQm)B zd&BHnchVGey_)0ijCH=cn>(A_6nhkq+0|#?$3r6YxVW2!oL8ywJ9KbM8I$f#&TF>x z=Fp8N5#kIJJAE5`v|VdfS4bV9^wPaa2^6H?EUL|l6-Ey>@Y{6!N5M!uj~89v`(2TGm@}s2q+zQJa*P{!K$V^nwrTT*qh@!-&Xmy*`dIalInTm#Qu^9)C87F5h z50zC$S4OtvjA`9=?ixDd?~yPqNbg!hJg^(fSKlGfV@A$K7FIgmUoYmLxTIw7tC7pJA%(oC4r*c>V-#X(hpOR`tB)jM4>Cv{U#RHJog}Ig%YS)Pg z?$X`lX{)n_@Lr?MIQJM-C+%E(R$4SY9uMeYpQUR31dU3)UENMas`Jg>5G zeL@<#5e4o+)tcOFfjw6UkU{^3uM?l$$@TCsd@qT6wVF)*qpfaW$*FZGiElz|w;hlB zUR+0*xE>9BsFq7`a9Vv?kN!!=X-%{I2vcM`zm0K{^b0I-=$KN=9@cTK0$iGOAIa^>(s;da@Bg z`DTJ6zA0%@S^;%DwOSvpeXcc9nCjj$7quTKStdUC+!~b3r3O!=s*IZ72**#`Dci@ T*$=g0o1@#n;1;!JSML1>@>3jo literal 0 HcmV?d00001 diff --git a/lib/NeoPixelBus-2.5.0.09/extras/curves/cubic.png b/lib/NeoPixelBus-2.5.0.09/extras/curves/cubic.png new file mode 100644 index 0000000000000000000000000000000000000000..a2d7c14682717aea33a88da5ec4cbdda1884a449 GIT binary patch literal 42669 zcmb@tWn9!<)HaHNFd!Y$I53iu(y1^5L*F3M2uMhG2nZ++T|-NW4ugP7OQ|$S3CPeL z5+WrbAbIw1-_LoU^Wl6szw<%=3cJ@{*Sc2hsDHFo$w-(;2nYzs)R1sJ0)i{o!JnJN zm%&did3jo0eDqY62uk``R>6O+KE9`QkAR>&f%L?R5d0tFjx_cnARuqW|GCuRR%io$ z$l#4I^1kn8=j~_ZX-nX4<>2Zq=xXbIQ%q1q@J<};^f3X!TQ)WLJ^d#SH%|S%X-8ji zA2leh`Q!|jS{VAxzS|rP`uxe_pg6$1^xb>I*^=W=zkH_y-*+o?bSS)vPau0$UVcx) z@U`w^;w;yP48n6$7uhdXA zI2Zn33}NW2;Pd(l6aqeXiIk|p=NUN)20jF=#A^7D3}q7j<9!o$34DIuMdAn?O`_G&P0ub+B4*DD09HgosKUIu2J?kLH42B9LNLqJEbA4`q3) z8mGm;z(Bl|!&L%H7Vtys-}%T(eIvj9-=?I{ZjY^eqv^d5x#1~&`u=Z5lm$W%B|2_D z*>SRT=V4#%s+KveZhypp4CO=eyi}~`L&mq&1$R}*hW0ZJSMHc5E!Pbh31-x!*bUwK z-t8_xg~$_&SxU7rL!7_M;{ByUXkf&^{759kKpYDfYss=5#yVlTV%x>I@|2hTjgOg;aMd(!MKKp$zYGQ3QPY^s1qM zDJ-TVZ>V6<%T~aa*SN1P`fMp8^TtvHe3Qr|(u|xNW{ePm*02&dlR_a)B2qGi+z7GS z=SQQ4yR@0ya{Y%opHEtKenQU$Ry1D=_ST^O3c{5SqG)g0q`YBtY}!R}hB0}IX}n%} zw-IFr;hSD@^yN(LtW_pk{G#sCIyBuQ``nN9zD2szk_^E!PH+vR!riXne!ILrqvICa zI$p6L<3I7snY^!&x#IaXWVI;n$;s99_kZuU`su<2uyn77=loS3{Cc+&-IZ=Inn5@w z{X-$WP^g3dZG+6fv~Sh-noEr(X16j8*)Kmk#Y^?t`M~f=j)B%~m2{Ka{lDtyYW#$f zmX#7TDd4Y^_0;W!B$pis$!9EssCtb`wz;^(>;Ohe94!WZJEfc5F4Z zLfzXV(kK7rQD#_~r84!8;XFF}giB2&V4Q5!VEA=;rV~SBGPTTou`OmoV(~oBvpYi3 zduj9pk7;M`Hq{zb^6({Eh1rFYsqLXxGCp0^y1o-sV$vff#O?9=4P$&#ri59~=g}uf zj)s`a1k6gv%kB(E$Ld4b@Q|G)3$u4gkY{#caSjg;-0qx97w~BnbH%v-LrL-UcTZ8q zvx>y)g8KOi>C8QJc@Y)2@=Sbe#h$+~jQy=#lDcB|WL~a+O><~P@5-z6!b^=TwyZu{ zLtlsPn#V6Au0+5BT%V;8Sn2?!?rRMxzV&xBy;JjUVfMLp0&`O5&pY0I%pP-ScoF-F zXA)OTVGIt=@eY^r53|pM;ih|1pWP=cGr!#Y zzy9?3X&!~7fra0mT0)TA6>-2}U%q76W&powf@)q@^YHM{)zvL8FTX&#IjOhjQ{fl5rj7a+-dZ0*i}Lfs z46cD5q4yseWc%`-e~bSxJ&P^&KX=@lwTM4TSeZRkwbNXgIaDQ5IH53AIG&^%NM0#D z6#doVlkCSDbY}l+S9LH}vr)l!dVMu)j6eCzid@U>i*O_61Im|z@H&-v=<_>xs2yQu zWZQgKTAS-PKk}Dd%Cq$rY>4+fneyP@(Ga1wt|xl~FluJ)Qh0|aGGME> zwEZlH5}YdmK_x?ig5yJxKwPhKqXK>1-tO7oDZ;LHX0P}0(_j2fANCXzQ5;_*icnVm z9Psnuy{!_-013rAf5QHeiZd>d*`lQ&+mn!6uNxQ&{%i1~A!$NqM?T##@-|9!U!GoIj6O zEY2I9E~^{8E$3Yk%g>&FK9fjA!u~@?xmEdqZQn-nW6rw|vzZpVK8~wHhhJ8LTD0cC zk(6gOpKxZQ?T_|z{8^IdpMJTguS1JBXHC`t{y!DNUPZ*JvX`^{c=MKP3A%k*`_9Wx z2rg_xD|_QEKULB~LVgrI&4n?1MH9eA61c}`Xlpu}xDyEWMU)ybBb~mB&>Q&mEqc1W z>V_lJF4H8hCE4}KEwk_NB1qBsdT8x*x)ikj;6!PF<0}3(&Dnu@%Dt0)$gXqEx)|r) z|Fc=gqII;L)7JGyPBNQC)c%_^_R9T2o6Qt)mL!93rx}x|%XlknW@UpdST%6!s`_kr zpwD6+V7)sO>z1o_v=`!`&c-xyOr=HhVsWuqZc#%Ec}2~s5c?;Xh#QuF#Qg7wsFSQX;Ug zgW*}p`X3t?wH-7Ho`@c*As3TJ8-Jib`m~Ga(lX&MSXmCyu>S*YL))jvn*aFocpNoq zoFARv9y{FZePJxVCi>E;pnQh6eh==jTT`EGk z-^Ma6yw>j3*b{w?^&UE4&t<#`z4fYV8MQYe)JTn(e}=~fE|OoWAJ<~esuzCZs@|kXM$ojvw{kW?21`CF|rm;-Q2o5}S2HXENXgBHaCg zJMLwp#fZDK?^xVqqLy_pral{Or)eS>-9m()_cG&8NA?x$fa<24L)*z|MR-qnuO*f< z>uX@Y&>Y&^-Si6&2|IqM@7u3sMzgr%u*cIuYo71K#)%t0WOP`nN(>hTsb!;Q{9VsF zJ0p3>E*ewH1>=xDulF`dI|hfJpe#l-^L=gBrnf5^?9u80)xi z42gC8RznM9wSI+ue}69q=dKKd;aAuvl=UTt`U}6ql_%QB z`>!m92JAMA`>DXLvSGig#>AFBJgpNsj(=Gqb*JS z8{Vo7Jx3_9SC8aQWCmy>#aO55qco}+f$Z6PZEPf)X!>>yMuXn_ifus8mbG8dEAcH2{>ElM% z7l1C4kI220GWU1wl@Y5}eR4~es7*`!?D#|v1a{B1f zG6aw&B4-*#_gy z!G#i}c+Q0O9O7r<>a*xL+qn`GP85LPB-T?l+*)r_%0Ponn7^C_=(``tF9i!a`#KR{ z?W)Uk4}V>0=1Z+Z&W`*({ra6RZjP=SeCItKO@fTdW0UcLrlc^|TS~HP?4(v@A1i~HK@E3t0H2(Cho@@oeoUisz zQz+>_O@^SI!}|H^b#5$0$nHiejXy?g0l(9)&p}h1w~c9v-ceb|KSR&+uj$ovqHKHt zp3F6UPq}f!9eMO<=j-tw3OM3o@WZ9Rzf>~&5$SI^Cy%oEbT1j)r_s9#ma*_d4^P&n zTI!ujBlY<4dj1o@FEQ(JD=F)^v(p7NgF9fq<$TIi#(fwarF}e2oo|4*gx`1mZAT8; z{Y$_=JDUQ!e@{%rf`u-h9wH8FZ)?+JqNAg`6-^5w5oQ{_a4Ldlaq|FX?y5>mR!Be- zQ%S46kVlKdscHoz@FIi~3EmqKW!7cG!QOxX3E!f>qe`4V8$naJ`(NF**PK}{#;?$h ze4Cz17>D`5$w|}O4V5ZLXp~-Jtb(KEqZG7_03lbl=4FD(mL*lHJT7eMKw_`zg^+C&ZxP_L`Vu84$wHq4nqI|H2l)xd< zn$z~zDJdyx$y`xH1O#bdtS=Ctztv+531ILQ9MIU=|5WpNY4Xda``+d{@K?Q6i0}K|(ggn#b z!Xn8{nVZ75_C1v{&$%5WTgHTi*`tstttJ5^2N9$zd?AJ(%cwdMLl z<9DB$kzC+!?P05YoZlooOdcrGGmXO;j*5j&YF#3#=O`P=iQF;jkSAi!rOi9b+UC7b zhmkd>{gQePbr*chaq6yDOegSgKLF3+zYf3LoyEpixFvXHiz8`pS^a&d%P*$ejcedx zF8JXq|AOJ&cZc3IxayMOXAo+Aab;V>My_H~#~Ig6I0?40^Q&9jD1k zoUeiN$bPf)Q&gPI{CK^e>CO*0E1ZQoskjh8jVOz$Wv=$aZm7dP_D@!Wf`Ix4o=mTA z1_wXwo;rV^gNx77Yo!71 zlcj2=$O)-L!|bHvpoVNcXTckDE^NDlEr#~P+-qP#nHwg1APz_TvqER?{W;xS|Me1* zKX9|Y9>V<2bt_;)pNku743P{VihvWsvnv6LJHB6Aa=`j;IZu|Kgcg?Z;}=I2JMX6B zQh$ZLeZ@;DV8u-PRCy}3Tl7Vdsg05+99W992Sni9gFLzxC=M363?oTC;|-SS?|Kzx z>%++oL&F$pXuZ2!J+^%_r@%~83mN~j^R1o^XV9PhG`c(FWE7M234Gwn80mP$NqqfX zlS;=ZDGeP+2`ELRD7D2&M|fLC^Z1bWI$#f#$1yEm@z>>1rqf4}@W$f8D+$>}j+FV7 z9*#<$>V_ey%VN}F*sq^mFnt&8M1h@O9x~oDFORPF^xltvE&9MgNuDbZzRQv%RC6KhLj! zd*54O+JKwoxv19ON?KBp`IRH(Tr4;{h*Yh9NaLf82eY@W;uG4>O*xGF-T)TIsl%xi zXv>(Z^6s`L?q)1r{v}Cc)@KPX?!|rGSB1ajoC&uAKN1oOX)3@#S zm|PxDv9!Ms91`nBp}?Rmb^i|9oBbtJhts^#h~pOvp6zK4OdI7?&$-zs;m^ci1jkE* zlL6Zxaa5J)ATxdczNA{si)nSTC@}ET5uyq|&E3PQG%jweGTeK5FZMp}HlZnOW-!4BU114nsD@`_f`OCqTToswBwNHg!ct?e}@>9wSuorLc z`Nz7Ks$oJS!K*f3i;1KrZ|J)I>U`_ywt+54TyCh!P}Xy?O)m7k=v46QzGCMSZ~t!g z>HbC@zNR$t)afM@{5~$nPM3sgz5?*UR>j;K1&K2jz?JOSA;t9R;)Uw0{zx$$!n@RS zKH*)ur{GG+PUB{OOTl~Pav?e?F}RN{acX-fjmmm{6s23f82OY>4P z5!wCzyNt8c7jWKZ|2W80(79v@@-=;3HmC6$3Eq0@YP$7|hj2G;qjNuz6mYetN6UKT zYVR8Yfd3e+m?H2i8;@j4IFrzL=e|nu#&CePf3+y!yf1t6fujjo!AV1dMAc%4RPO(n zmeAe6$AN@Bes(UyyZnHb${6bksW;oW0=m0Btg7gaG@+?apuwH}fnfTcgCnsF{hrtx zH4#8GBbVF@anSP3=OIk4tL4n%J9Sjx#_|dYoNNxBvLtoyvOc}y2L$$#*xSada7kw~ zdJn`yH=CilBzB(TkaFvL4JbO*^!4(iw6J5&* z$cU^20DR=zuz(j5^Y{|jritV^Lj?BQ)yIC>aBxUB0Xy@W4)}h0l8pLT`Ma8j@DABq zSN}}()L5ccRltC@HqxoVpC!TGMC8Z!?{j5^zFc5k!X&KYEL!`2&u_o^hxU2K5NfZM z)3kIF02`l%ThAbJvOyLE33dj|bT^FfIz%i}AwAKMbP=9#3*u347W>aPo^~3HKFvl4 z8`1Uv;2q)WSWyDQGq8j9Cie!fWT4~IMx*qEWn)tH4d_fF1stDFo)6EROX#C8g^-6u62#}a_ z)=pb*l|@`n;npv@d`of)E?Cw5$?e=^Dd>h7mC!p@a&Qy&-f(7-7D|pbc)wRm36++6 ziSA62&XR_@Sa{iNO#NU96dkW||)p=H>=Reo+Q7|Y4?5>BE@tKP@GNxd2b&_WyFk79w*GA{>&a$dcDk>@pIbTwgaYf3Ij_O-pNSR7W<&W-RXeB)}vusVp zln5T-ZMi%XK;JW?;^LP43Cf^gei|_U^7(^0O51S-qN5(t5KF@OcFS=L4UTrYi;8GwB?aghl zvv+^;H}(b4lbx+ddRHq~B!)U#l_iyrj}KlHA|N2}QIvpEq)v6T`w9I1{_`cwWtPGZ zC(A^S?*Q%f5K&E0I@C`|{dmFekLQh3+x;TW`oQ$$X>Y(>#fxpRysKiG*oK(7lG zvtQ(ZhQX8uB1&ZzuG8MQp}K2^sEv-|ASk;e4!yXL=^Vv9Mb!C}Tx=kcm1I3$VqTHO zwrQH%1YN}qn7Pi0z6l3eD;9C6`SD3Vf>+8wL+3nj#S82{Q6Hub)s&k_)XI;yHNEdq zbLPtrz``U;2EpkEnALB3aVXs>cEkCSHf@d`h~C8w8iUM5q#iL5K-#Wy=MWPw6x*xu zMJQxLeOa#tZ;LDkDKF;x!TO}{y<#Kd05caDVEH6u%9bKSTL zq1I>;29!&KwWr!?k9)exE5QrNSNpKn#SFxmnohwuL^qtWuCk6ljbrTDpZoD#m4^WY zb_?5Frbs}8B{bR$0vcRm9rrG>9{23}A07Kk+zqC14M#1igDI|G@GK=&3AH!VvR1=ss#Q_xXo|;@r{V3tnJ%|MQGxe<LSRdJ+q0xI>hN8344VTi|p*G`v4(r&-NLlc&wlw8?IfurkL>&MT46k zBQ-Nh3yGk?fklO1i>Ukl2W)wY0Ma$xpwrj|O!w9F+0Xsn))B=BC7h3Chp{&D&WwqK z-0hw3z;v=C39|MsQ40a5&|MA&^V2A-?+3Ob^eqvFHZ;P@DX!M*{c~^>qk^_xR;L>V zAhcH{;s29M4+ysp2-ns8in3d7?>y>b@mv~D*xcXirk_pqn~auM_~@AU#3-9<9cj~w(1P+WchVq>0Azqu|7cFNniR!;p!RgkDPuNr4n?eb>JZobT1vsBHAxFSW1pPT%kZ z?b-j|Hix4hKO-9qCKOrfkW!GiDZfsYZ^^Lq=IlqMjnXg0JM66^*S?S7wl_;xJdiSe z>zSZ?WGEFkELt7YsR7QP1-M$IN8#bK3fv{5Go}ks+w3h)h3k*DMpkT;n2@nlbaNd# zPVoShgo88-8guvs-I3}FYweRdfQw2&sFPGr%$@+>EQPd=lM_RET`%NOogLVh0Q)8S05T2QjiyQY^*-l0Mh{OAtS@1=yVYY1@0 zt}xRe5)Z&e#>|JiQjy3RDxacffJr^|HjuP?@(*h~nPNF1f_zZoKI7Xs@9KljLF9vW zAIwl7SdH_BE6z0oouqh9s!Jm1^YMg@8#}6P>$^8!fZ2}o7K>%P=&cMUOLON)pwV0J zexkdm25!~9kD}s3E^Kxx2;8|BrM)^!*S2v-M!)}XBY$qzJ%}pYnly~%?Z$CwbreST z%IS+9Mfpx>k<@WD&mS02%dB68U5$G)iYurTiStH^KRz)e?nQs3AAk(ittm>x96RWr zxGC&%`;QXPbAg!tNRCqAAeQAdR3ACzo-%S{+X_Qwr+?NB5PVT#eJk+(jaEu1J43vH zx97|1+*GTHu}tnC=tZu;AUPH=-7zC$-{JC>G}Qd%Nlf+58ir+xwDlvSBpM{2Vb?n( zM9#t;IHgDVq@cy8-7!DV_W0w}o|(YAltXBLy_C>EtCtc#hES;aF|Mz(!YsW=W#fUXI~# z!}kdT#}M1TAOp*EA4DcJ3A~oK2JAkY| zjOr$bY*e0?Y9gf~7FikI_7cOe?#2q|+}g;>S&MD@vq%zm+3SOLSE==CStf%L!Qk!w6WS?$@3#Og?ImNy4B`8Hz{bmXTI~oi ziGEM-RTkEqtR1p)N<=H^q91V4;JQu?;YACF`?T6fZeMIP2T=Vow2Y_AqoKx*Q?wjm z>&H0jbv)+2`58QRXKiB(h$MQ3SDmi+55=MbHc-45LyMn8?~_x!CemT|Tz0c)gO}m0 zfbhmeukpiE1rUL&Kl150_0@eRRil_pgL@tqg&|e}VluibACXeIllrDXQ1{ZzGpmWq zO=VT{bRnW)U^A8%dDWiIosR_2xU>rtBcz2x5{wf>HZktFFs!4$IyPUSxt65@454P zTJ~CaG1=#^+z(5ry+0qwF!mpwpEWP#vMr+Zvx7`iT4;b`B@O~Ht=5P1L3mxxEG{(T zcWidJFECi|SQRT3KsvRxtjdb&_y@$FY1KFNY|FmrMl_)D#!ppfp96pVrqa>GuhO9X z$JuD=yfgWdq5ZdYIx5GrFJrFEMS+yDu(OL%VbGaS?)IdYZ!ezsdnQ-;XCWkzqIh*nRqg^Q%H#9WOtzNG+nJ6HlqJP(ZIe701HhZ9L?p14WzHp^iqMibq46DXFn~CL!Dop1jU_-D7Yaw@~fOAPwlPvZ~J@%|d>0T(W zz(ZM_fG|+b!PK_CG|1|HdohMqZW-_KVP|u?*V!1T^V!1Vow;-aKbp(raH$7@=>G!h z{R z>fwwAM!)|#86+Fsx7AOJc#Y?ZxBZ>Cy_7y3?klc-5TMVWlb>{(T0n{IMi&r4@2N^u z7Mp$omMFCFZr=2Caf6T!r?{kdq~3QLM`UzVRE8Mv3)E@lq3?~ny+0Nb{C?JLJqvhP zk>DRR58+nbK^Yz}iVjTRMF+qc9@i^bbO*oX5&Eu`5bvflkMe$``i|$aBH;7?W;w25 z^xJkD3i%`?4CDyhKY#sjUlckmSB6Xp>^ zT%&LAN-`FG9_9cn_`L(zg(}E^U?`SguMDpws~`~VSheJ~D+D!bu7#gYn%O;+T7EjS z6#$n0eeu9od=p5Os?6(D*|UGb*+AC{S?sWjBRksQB;uxMIVRsm%_P)^#2h8ssm z5)rtjmmjiAL2m^eu^+RGMd|&9#oESzLC_w>&+wXSc;(MZsbFz>LEx>ol8yi7}@H(?}LQ8(RX1M(yh1ngo?t-!NQ*hTCXeM+QWe<2UQi@by(7k0n3 zk}qZ#!HUv(sNFndm?sS_HmZG)a#U{Xz`~7{UlWSt=zskTict}6p--|`Il!%tjg7t5 z=8PkdG8m3Z6@?CGPu^5-e!u)s09;g&8g*L#ufJaeXsJr*UYf5C9%sXE*lc8YTtNL# z%HmX7L81whEWGG(B$^6~R-z`n`KQHYTBDL%D>Ef2iGz)xc61tR4sI$4{D4uv$fXtQ zs8-8kF1KvpR>c7CSTbW@QQ%Y*kne!-p1D`^gl+@>mDo0~OrC!U2W^(O zj?ksrZ>L|LfP7@R1Z98x#o2is__k*C2RB0;wnhdqLkV9})U}a4Z@0F7tpnow>K|ol z0`H+ki-4_MFD3P?^HJrXY0Tv>-Idnk7*11|z_DKJ0R7=eqH}9#GD}j)tNlGztrs9i zNtyKjG|@%^d`<7=WYab*f6-q!@y;FlW{V&x-cD~L)(UOImPf|!({ ziSw~;XCNvP*41dw-zeDK-1sl<-*QL13u^-9(&iXjxgc(vQhPav6*HTPEJm*f|6x`AI-kHzbtS?m$i}1H=<{L z;wzJzuKq6!`qKNtdi)*8=s3iI=u4ZcTr*^KdSyj?iutiwVoCf={@}g`vRkK^8jw`b z6+Tt>1%%O~TVoKsPqK(QDiBK1WvE?75T8>2f`+P}#we!rLRJfGSKw4v0gl|Xcaa>l zRpOgED(4~W-<>e3Mk1*Ui~~%$umbNXqV&$eod85rKXa|vR2M}GjEVcTK$&Df(f$W` zR)&4OfyI5u&_fU5Z8-`!Zai_PcuG=_PbIWvLVDw2+%x(d9?n^OM^aadRA zYvx$9cHRLezy6_M^pw-hrB%G)Oe~-O4C;esf;Zn!4*!@n!EC!`0T;~NL`WTq1ZL@8 zq1?VaP@i9V9g5RrYy-GK08LAuk3n(G+}#su7?xPJ?pzc!&?{m1RMpm%Qv_Tn7jq|U6;NVyZDAz<1Rp-*>Un(j{ zbmY&3^IpY!W$4ZDcRlPUfbPfkU3)jv{KoKys5Gpu_Wq=M5H!3v?#tWok%Zu5ac*oX zX-6YJb*lr`9dKu1nDw@zyOR3Rp5ne@)A_f#BF?1)DQK_zR(+x{NUWv>L$BE%d+*@( zN62-VD787msDLmJ)q~Gs0ip;XqA;|UDJ(@B3j%&;_gc!T492TgVR-!TiapE!>VOpn zbd(_?V7AkAMIu`7neNIvW@%`h4_2azlNqcE_NO6nCElDa|3|X}_BUuteDxp>)r61h zp}jFJK3C2XHaf$toH%*Mj#bp9;%Fa&Lk^a2a9ebqlB7QQ6i@)XhP8sqYvB=yB7T77 zVO;LB$nZg=RRnOf(peVS58|$xTfpc%kQ1h!sy&Ibou1?0W-&fI2pA$Gk{lgmlW*M zqqzIS9EvvCoHv?<3?$7gSJUs!UJRBD28(@utkP2ZZt^prbJ5iin4$8gG8OHSB$2PD zX+F8ZS{-1!pp~4@$__cet6Ajlm52`jnP&6TV>miMOhZ=6hewwS_TwNf#hs~34s7-E zCNo5(upm+0B>OarR;FUSxk_VP6zXz3U&bSTy&zE&z|wC9(CAtn$n@ZUAGhwAfi@7F zf~voGK8Sn^#Pj4jI8RP9FD3_JpBPOfIDpj2VITG{v>>*9wkKXc9fWBS&Q=j+2BB&E zbag+(k}}b3el}|eRS8l#`?c&lwzs&kg^~>}wWGKBLcy?hfT@?Z25~mu^vhOZ@*-bt zq6ImI8k;`5i=tc1hcc6tYgY;qIS8N-IWjQ)4>NZrH7z7V<%p7}w?w9B30)-a=De8R z)tj*A1-4IPj#WaTf>LoPlnqT_bz2vTHbfMWat#7T0I>1}ocz@v*}+T@NEa7KUa=ne zFC3UPIbb^h#ZGvqBg;WU4Ho`f`c#aXI!O!lL<5HJ31IU}VQKMM_t5zbZY*9#VT^N&R?`NrUtVB*L;)qJazeJFRa56v%Y% z_uj}S2p*DO_gQd&T{m^&|8GiSaC&ZCMz@bn3Iy5+T!b5P!fF6$Y4g({szId0<<{*Q zc#=C4ZwqBolQLiYQxK4=GJGVZ!=QTp{kIz)B5X%$qVxip?>KMt83K*3YVrJc%K%iq zVDiBo!u}c*!O%7}-cS7l3Nz|ienFmfGF(&(G$^xMfr5vywKOEPHYWmP=bp55ez)Ao zZd^pA)GyfTl2Cr%(;#F?>N60Ju^gvb}PV~ANbfhkICXdeU~W{-{-NuETS=%vWEv3I!F{)qpaZ^7f1g1u z3Q|0g(E{)25Y^bTHkHADtg;IUVo=)`OEV^fLd;4^i!VuzhFd=PC=U$lePu(2v(Air zyELY=^9d2m>Tp&g*q4jL>f!^k@vN#K*l#g|G+J~fpeZ@mL5W;^9M?&k9nF0G#Zu{k=8U%VR#`m);j$%^*nwj? zPesBTk=F8gZi(ZUia~Lj$baPtbArO2JIyao2#ajJ>X}EU7zJ9;j<+IJPhZZD{iexs zSs(XQ=fajv;qTi;W7!8R#(-afE|ybElLsIj9HL-IJANhHx>7(K>JkHKy|_yizN@g@L#?DFOhsg&59mg_Kt#006wV9BFOLW;@6DJoKE$-}QB>Ea zNMr>~t_gx%R{|wEGBVMfCBrwQp*b@-g3(PRKtycTt|iki&b^c=k0Qb?VtN+QDY~;< z>cx2=SYhPEuKYrIEp1&w7D{J3lT?s6v1y%6z#qnx^ytweuDEFMGz($vArKYQDsR;c z2f{`z=-bM3V|4^0HdMN3!a-q<7Fn@H|CFV#5{D^$E^rF|zO3I=p1%ho_1*HK%m`K0 z`<4s-ka1#;yvk}MsI4$#=CVA>zf7+7_&pOm5rIap7kwyP9_-SvRg(E6`%oWcYJk%M zaUz9bcI)GESNRNzH)U>IHjUbme) z9BczA?tLHDP#~)_@=d-E%DeP5k%w>%pNzTEg$7(01HtDX^jjLM5~E47$lsD_M`SrE zM>c5lE}`*B*ysQ-*#GqS`;OlhcEU1cF?gzzI&#WMla1hU~uhmRZF|hS_m@Kaog;uo*(9>B~{h8eY zl@%khJxR$pFle-EieOelt}^Z~CohJ!1@%99m7P-$Ei$#QApy*ngq@$b-}K)PS;nKq zEr1rWWOX36WngO%iRokt{zno-Q?cTh%eTdag=-B!_3h~wmmG>u@EmDDwk{Ce_31L! zy*kgSq=E*X+Q|#1{`=Q35pw-{dV0XCY1`Fb3pAFz8wu_!v2Nh(Gr-e^fGOi7ZXKDp z54=x~D$Yp?J0F{5kO~0-tc%z}=r9-wg`=?WY~zkzSXi(T=AexoGI=J<|bkBHn{a6L?sca9VK= z0F&0avtz~l4RJmTX3PsZY%fA&2heepusQMGz$w_1MvC$-P=WEF|^ z_{0q*YN_i$8Li4WesD!#wfu)c;jY3(g?E|$BFJEYv3tST$roeCnFj$9|IRg`P5Mf1 zyz>g2(CFQVGMSx!7rqP@E@Bc*ZL9Q7A#Rq}a$!GhY96)cqYf+i#yXhj($Ujel>tO- ztEMXPK;mmf6-Y<8hbO*iUX%{$g75~m_W*{eG~Ce?}|wn4*Fp+9edrO*bNMAYlV}=j|I;i8{(4x7{N6T|Z`RcQ-nTG^ z(co}G60`NwJik7;^U;HSgEE1!%r8DR`#U!fR84U6LWe~ZtxwTi+Aumm^0Y==F)Ya@QAhc%S zYLnidd$z}h6x)~$wtMmfu5U^Pjt2NLV|{lG16x$j?{H&r(-y!*p=4<^?trYuV9Vmp z!HoL@o{gskFIyeuOhg}hA&Tk)My+p~asz=*@*eR<1E!Y|A|oetQ~i>X@WvyB2y9GE zCS2G4qSzOOcO%NTc1#;j^uFf#<${-T@QT99xZAO|YoHY0Qb?;#NA;#dXA@e5@|rQ3 zOE&tUVyd_tbvU~Ph@rMAucr+=5P62Lq$DB!b$vGa|= z#tPvf|M(Wuzd*B0+kHNkcn(UB);=ofN{8OwV5r%4sou@<=d8->M4bjt27JMJ%*e|lAC#(Rp07m*awv3bp zgn-iO{9F`)hbas$!D!kE6S{1`LxP-4 zBx@bXEc#wx-PlWHLE4?x{#Xi{Go22NZt9{;0;z@-b2Oxg&oz~YkT`iF9z+Yps`g?P z-`$X@K!m$?1ZT2QG>kl6YaWf&hyfFPJQ;jpo+EW{^BS=lrS269(`1Fe3E5~*gL8wF z%c%Z~Aj@oi!g((~-1^;U)YHAg7`$tUXL#CM+eaini`ZB}rv&p34Wu-K0-c+fozZ%b zMDA8S|5P*VFCW-^HY121-|Fo1OP$hKp)W zdR!k|Rsd@EX%8RA)N2m+Lr~qVU6Pasrs9Y&v8O!m@dw3< zH6{Ni@3m=}?;}K)y4G$}*mG-tF zjqVWYV7n3SuzMK=6V@GX_?wL`={q@>3qV<{HQ#>x4y3w!)mXg5V>8j$lD;RpJOvp) zDX=X6WP`LR&a6t5fM0s2jc(2K27B4Rdnu1sC zmeGsS=V3H@UH!SB-iYRULhKh`KENyP4gSbt<%BYxQ*9)7X`~02XS580+5}_Xu&Uud zr$JX9@y-!}gP}#ih#P;^4rikUb^f+K@AFW3L`2+vL1ncX_3u&~$7>!~Dq(EAI6t3! z?_aqQa>f}9zr*}r&&rv!z-_lPYjU-kzH=V+OW(?G)`6#R0~NBMprk|x=_rhAyEzR} z#Do!o9^h3kab-&}I|#I!{vO2;H?dT)%7Y_zlsZd|p~3wWvbSBh@iFXq@Vz|HCF5%A zNB@5cp}bsNcDTaALLS{cICwr`C=vh)TI(}C`$0lzHF$i{ zu<5!4HVmv3TlD_P5o1L9Wh%Om3Bc~;m+jt5H8aj%}MsLr&0yTxibR9a{T>}zClDax)0WCs7ue|wLC zTgwJAie(N0wK-3@x7nT{zFr?Rh_afHYimPeG4H^Op6#Ya;QvJ%%)!+xPC?Z8eazWI z+UGPjSliqLjj_+CZAV*vLA!sCbY{(tq+AX)GaENwhUO@oO^=c)ME$kej?a~3jgs{g zxy4(Lt=0cRWEr@&9J5w+aQ;M>_W9sHO_aKv3|6FSVl;E!RJUNgbQ))|&#nw-8-oCq z&k%i-UYT}l{qbL8+imXqzHTygq96G~MYh`>2L+pWE$+Q(1MIKn{Tc}B6A@h_(_V@8?Eja z!mAFonKrLy93=@Bm9YEl%0^A<8TLG-7gT|#py~S|tHqV$7{X`LzQqk7QuEW`gyMT1 zevIhg=mnIw&vGs~#eR$)v5MDyQ)4TiouzD=V?mb~=`YTkBmFa{5Ao0WkL0l8a)sm_ zBYuZJ=D*_Vz%vBchEV(YdNUj>h**hQ^B;|E@b*tk+$Nhc-;&@ww0gZqz)e9mcr-x7 z?l^ya-@q+yTmd)mJ;ZEEHS6v3Hg=vnbTeD{0z6Zp&9}sM&B88CU{Rr>4S!{|k%Y`M zbcnfGpy86$rP885inl}VqIr2UJCxWwJXqD;q7bY~TfX#we`G43g$A zBV`SJK_w3$kr>*ioyjVh*I@;#r9uzIyjsC4C$=e%{hf?8LSx$l$1Jlm&WR`CeAIw! ztC4s3mhP^;Jb$pkP<;JcsMW+>C1mJp@LZVt>4}>(6d!U^pO5m;VG7#&HL56hupnfw za($T{)KGsV@2EcgDgXlUZ&UGj3QQFmum*eE<7JS}mf6)oQ2k$I{dXXh?;k&oA1zcy zR%LcLc4!#c9P1#8LXl|NBgxDtB>Rw&&2el}vLd^TBr|1`P4?cu=dIWK^Zoq3-@g?1 zbzkGY#&bL#kL%18GvTch`kzfa}Pb6?a4o?@fmPWx=-3t3*cvCa1iQfh+`@ ziiYx(LR~kvA6bpnuymU#5l(rh%{SO{kWaZgCQ1c)GvQvU(p-ZNwzc=>FiY;0??b%a zm@`J(%@Cs&2FCd5RBNE@v)=;*4hap7_#Fp;eAyotBp$ud*>64!b6EHOC!V8-TGdF? z?NvT*;?6T0e|AUmohE%7DxeZG!GSM4sm*HUFtrSo2 zVlD)OIVGft``Rl@uk`Nv+ow;Zq3Age00&aP(LuZ?lDYS&;a>wgwo?TVxOtaQ7vCoj zeE1{UQFd}rr5Uh!;w@LA3ReJu)A5C4C;xHQ#oDg9=SnQV^n3TBdO`&p33@9kC0@A= z=5CiD+V-NbY|BgU7&HUKwY2aJ>!!TK$d-U-tF6N%a;VXs*z7_ib3t1)8 zln7!S3Fbmx128c!j1F=k2<3!wE%2;W=k1u-o}#bJT@O>ZD|3dM+O8@*kjURe=!p~m z94xWd?4B2{Wt{yD=s%CrF^xAlsYlqPNWqAxL~W5ixQ|0C>Pm8(rc2PzBf z4~Qu;89hc9=`%1#V17w6gZph%=#u`Wf}@1GIMx z#N>1o^w?lOopS(?yJss5J=swxxW?8bDsa(en*W+_y5ZVbq-$Xifs!3a)3kG-9Z@_= z$xY0=q5fZ0i1-cbjk^3@4yNEoGp<2vp@xg_w5A3w893(nD9iLdE3g8SdA~btDKJ4_ z$st=+7V;?jlk)bmW7J?no#D{=2in+w3GD5XhfFoZ*}Ynz!`fjbK3&!cKZIZ)ETtoJ z#~Eh<@38jgm{-?DUUcXr-IJ)<-BiR?G6iC44Y}D!P03z`JosBDy)k*NQjxR0_s{p1 zIWRUc0F5rlnLKk`{xs5C$8|plZeikqF@yNr)!t}~bG&qY{#Rh5J-CZy|1TQ(VxFDM z>l;BL8via{fG!nP4d$sZbTR;h05V{cT&8IsT4=C%5)FVj#$F}fD#g{cmApj9m;Xpl z5UZsQlbWqdqY_7VKRj;$L_A4Ac~iJz*+Ga%9Q|+dqOa`T05)#KM@m`KS(;Q7?MwWq zp##%FlQqu3#P$d_BKmqAOz`_Fo4$BxbCg>h(q>V*0oV$8{})(%N3tUO z5)a#NC}4c91E-0M1RJJ;umd6^9(4a)(sYw zWCfL3(jyc(x-rG_Qlu5|=>C!aW{tnUFObmwgFnv&d}RG!4fyN^WrM( zy2(ZGrM!=!8w37ch_teLw)^X{Sc4OynQ;HyGuZUpeA9vpZU!8u{a;LxUF*mNG-r45 zBkVWL8Y5vcCU1-osE9x~`L;;p?9YvyVh<5LE)0y?i3i}_Cq}bH!oK;|ffw>9Ukx+} zWapd=S;}R}Z`a*e9_Rz35epEaXCf26k?0x2n&YXz@<%qXj*~zdmLlOycNa(5~ z%>9wy$;|*uUf#bk&)`jq4J*DS36qPvC{Jc9nXpm>NzD!WhrI%P-&a5fiBh%KCMi+$ z>K$EeahN;bmgixmT#&~tF`TH+|GD#}=-Fy=HBi$?yZmm!dg`yeiyVw|lMJaSdGjm_ zqop?Z#c8UP7FlmLY?yDxgYE&pzqg9Y#Nxw2iUG-to|=J0m9ikpeL$>*Zk2hI0N=j1 z9p~-3aLyY;_2J?9-WM;b3kT=I7Rh8kbgN(>?{MtZE067m?~4MrL}9`cv(s&0lbX`} z=?4GPB~!h7uklt<(5gVd;H6 zLHI-3VkA~a-zG9A7l@8WYL zQt|44NkYO|mxkrQmu-`r{nz~bJ(bVpKzd<}l);akHhdrh6gXJ=S%mniv}>uRL3v2g zhn_P0Ut&EJ!X!CUcVj<)a@ZNR1mFJcKR@tmUEQ}EisAT4bc@B%glM+n6}l5 z_#NuiFl65&01Eh97sA{9<>H!LM4|L|6<&}&Fb3!kvqJ?|Lk(Cm)j45@^Sy~m zX16lQ&0wrc;k~T?6hiL7iUK0!zd+AlHrRuY1qMn1eBhwi!!>9Jwuj*0 z?nAdipS)3MPm`LVWx8?#@^)QVoE`dOeZCHDI|S+!d2eZG;6m9#n-l*oYxL z&4og&KVWkKO%pr%H<}z@Sw6q?eyHH4lGY2_Bq<1D&CO-=6?}W78Qbs_6+C4a)B*|V zAK~Ep<%kGhpsxG^hEl^5+#ib=#Zy&INbl*rG3ysbgtgsuqp#w!36B2S?>iB&1!P{M z@gPhFIv6jX;HoY_Uj8p^^PJzW0Q+ZY0MuI`VZPDWQ*qb+!iRy8dlu`zO@Hfq5CrLM zaS~5HYSNJ$c@=BnwR;ZpN%1#0QpFD6-93&1x`0c-ET`V5xN8|sqt6-dqYo06ey1W?nz-Z(>1WnSwAbH2W|Ypwe}bgx2H02KB_ z1}ju4|2|0au?@{V|Ye&9{OjW2F)T>4lH0$vj6FQa%s8 zyNZhn8Ftq4*y$}G?U59fOM%bM?EROGddMH3T9%Srj&ledr{VOq0Jv>axfnmAs@Zh^8QgniRS6JJ|aWIZb*ADByXCIH0-un`{P@6I=4~0P=4nY~Q zmY1OMe~A!sG39HiFL572!Kb0!G9#|4e{Hp#-*Fj;XS^d~M)B;5Xbkf@Z7igmJr&v^ zTmgaG`rXnsPPW-GZ~H~SV;_Al=8f@%THF2Hq5#ty9)ssrL+95~-kNDh{QbL7viV

Q%tmg;4`ppsVnz+at)vLkb$eM_q5d!wYo7C2nZ`LxCC|pg>U% zfVvtEY6b|(#uhRF+~kYG?FVG=W~Zm!UZVAVsjV7);rIrz%ty>&MdOkqMMK6fLRApr z2D#Z_DL;X_3Jr^j<4Xq)A{t0=OY1It3o^UgSib+(gXT$ok}aS;faI8A=U!JGBu<8? z$y9?+eKR}2#)cTtrc%obs^cE&Jcj+_ZH^jPkh<1(N8u>9d-EjD6HuVy3Owndo!iRR z2$Iu>Y+CYC|0D9u(sFemEp~5R4?n~+rz_UvO}G9u208)}dy}^du-e4h?;nWK+=;~F zuIfVM8d6J2r#$cfUkf|Wb!^XjV+>;db&&6HX6z&Ew50Ge;#VEV z+h5EuWLGI$i^iKC#8cU>pSK0jwL$FfgRx-zXK%~4PZXU2R4R3qkJoJ^K!H%4P@1ps z4~=LggS@wu^=r~OdKtBb*{=j0A<0+|`h2iVuP|f7+%$#`x=jus7*|D~GAU3k3GTpM zI0kr9$jd#vK?|T%=83U#F08Tt~pG;L$1nMiXc{uv1j3ZVpL3xWZJ=FL-z?<)! zL^lR=*&^c;DCOw*@W!w(&4w4iTg%XxZmsv}p|*n!xDJPh216CWmV9x_hYU+>FeR+m zuvz;7Uh-+AF=y}H1wj~q3by({xzpz`-I|MQ*Tm6ddkZl<$I0Num=Lg1;l-1HS*|`o z9ric1cq{?POxoc*YP$f-S7?U!UhzOK#RQ?M0ybe(|5GtpXb&h;ckU zv`SB2KjJFx$%kKHqN+nDaX%Y%;_%v)Tl`w1gQc8h;^^k*+hqd@GFr#EuG5CifOQYU zBb?N=jpjWwP_`iZT$WK@0A3SdciH`SAOIN!f(6%1KnkSlBe@@Z+>BBQ7mq-h zujIUsf3!uyS|u4W|9?v{#8_lT19b=jVJ4KSA|}5A*kvCkdYo*1%qPSRnBGG{hS@^E z`F5Bd=%v?yGhz1skc<#1HIN5`+)F}w+)b?)l+*4%Q&}M-zv(5*$oltms!Q-(FmQMH zn#`4Wy<>sGRBeX2&a$l<_(S=|lQk>YIY_{ARZ|Lo<8+teDkEJ855|u)X<~~|IflRa z=<=-|_7+np_lMd%}#RO%S>+$?*Zp=N-2|%4beY&V{ zXbp33yc&$6+b-pCwCziQejePO9YLQs+fSEp(m3g7pvkdXpLcI);vj_2<^TL<?!PPJ-RTXhT)y0}y>unts#NcPIz&8Y9);7fmfISb8w zco2OZI18~`Bu#_1`ku?6G$aibc^-#Spu=T{{kE}oR%>*m??X12_f{I^JU*=yRFq$Y zLdhF%KxZLHbvr@Xe7er5ku`OIhW9c_vEm^)T>k(6>Xy)bV7MGxxf4d)?zbBSAHb5B zlDxn1HrSF-Q;QhJSbu>Ut3K)DP-D^)WV%z`*Xc~6lp=;!Ktlb(47@aR7t zU5`3W1(CaF@Ba#dD$nVi0q;Y1PC`q`Q0T@(jZgQ&6ttVWSAmK;YY_rY!Ril3ufwo~ zVF4jlDzSC!U)rW3O)LDR$xtK?^#YAi(l?w&LvBNMBW1YST`Q}hi3dsX)fF-k=(sE! zKwej9j#I$0@(HA_zw=*d|4fEdcM{Fp{!VC1_6i0`GWpllkYVl#w`+w2TkiG!15tOn zg3y4}VR-n=X{>g$ zq@bB>ZZCY!_Y5GNQy_Ea37Kl>n@U8KMoiR$$Da=By#oMdmK*1-Oac zW*$h8iWXET zYL4OYFNu^LkvagOuP%qV;0gS&OX@(lKR|eyXssMkJ{Nr{nYahg6%PqDQr+5P8F5Sc7Xp<;A9H;>7fP!J8h8Rj3k6%1&sV*yn{sis| zhZ?`EAQ(QnoQt&u5s)Wc?=2e5xr+O*LpAfm<#tF0`ElZ@)D(DHG4OzEgTQNer^PYI zFrB9ktC@#T^M3#tMxRqSE>vxo<{5x!1Vb{@nT19p-66U5yqwcHjL&Voehv7Ng`+V% zEb!#ZRj1!Q(ttB5Q#yJk3E&TW{#8MY^F@snmcoYB;uy~bdED`j`O+Z7GM3|_L>E*V zY6$TIu0Xl_-ayjYH%@Qo~b|wHAK2PE>xCK`q0R9rdE}wt__UC?wA}{LgVKlE4>&-{7Kr<&Q zN}k&}nL2ez0yEoxck#&SmqLKJI_+G(Ms*u-5k8O(ibazsqOjUdbk^~8jRC*As!Fb8HJAleK{{tCW>AZu^QcL+#n9k+hreT3SXO!Z#tuK|z=DU3|DyQkwU z06W^dI+G`8)03Ee9t0ic^XD-FrxK3OR#xFImd74?e*~+2<|eBlY(Ebc2LnJOrXscs z@Q&YnBo$BbDC?VVUHcwJPR`Pe6+Y0_$$R2q^$RI?9LixprjJq;gh)S)0;B-;z}aaD z!6Rmfyh;(st6)`%sN-`oz9RV^lU|*Yy9U>3Y25FhNMU=LEsoasgqK7d{OVyS^!G3n`nL9c%sc$!G`-;M9R#uI zkRw~inZ$+J`NE(K+IvW#z^+&+{ADc~P4fFVw*3g;GmM_EQ(eJ`jLEPnbBLhzz-qCs zd4D@3NEN4Q0ro*ouH#ViM$-I=QSf3dhixl>*`Z&v0DofrYAd;@%Zmg zi?R@9qO)hF2Z6bR`b&;BV`sdiX@>=ZemFwXHsfq;qppZ-l>mfK>GVgjJ<% z^UGArKxv_QgVv>F%OVdFa1N{UNSq-Rv_CcqM#g)rwG7${9Oj-x<7q~gl#Zf)>tL{9 zi!e^Px%8ruS0*Zf9pAV4^V1tWusBA)*tgYO=4(>#4Q`Gg&)i+e$qIX}MS8Dr!4B-v z;%g>-JY~FU)nI-C$bGb%qyGYu6^bUr3EIp)q*VPXY2xgBzV{%i17Ts%IjEUkY%WX$h|ZnfPk*SS zXQwO8+A?2Bo>4tkbrB&_<+qIp?!Dn!G{g8S3b*KlN0<1UZB>Y?!$}Mq@3R5H703!I z(5oW()?mxD7@(_3#(~HuAk53y?i;J1xIvuUS+P6e69U*Sx4S3^+p({Zs|Aw02|%iy zg7gn^xEw`YA;q?D0H{}}Vw;pXZ2Du1^Hazmb~ax^1oXOXpIW77=YpgzN=y#NIQV}S z{9WcI2T*@PR8)6DW#WEcXlrQTfcLif=Ucl;J_8jXalOT(Cx&j4sg9^8>?Vq1BEDFy zff@k^3q=gLHixcyL7RJ412`|S27%^`pB#!*0mrBU@s0!AKgv}!5%fW-m=5$HM&onc zdnVY#emOAPKRj%E)|>d`-%2mYJpm{7sLuz>workk<1q<${R;%s)xuxWCUG!%@}n_F zmy-BmVPj#6`a9%}JV$yE2Lp)oDFNx-`S{afzxLQOasL%CfCAbj&p_WGpeYSh=iae3 zF>vJHW|v3}KcLJ1(++c;Y&eDlpxpm0V2km*5$^&AzoxtLMf_#QittiNfiY`3_<8F5MKGzto_Wzv=5#RbosFM0iwHfmQ*a=C{T)P%===t zAFR+m-96|!EZhz;)=|GXR|n64LKe^xMSVu0RR{ZtK%NTL+~pUFL*n^I1iKvAVPqCS zh1Ts?%08(tkv>teY!BgL>)3uM8Yw77#>4jX3KAyyJe?DiTo7c?qutEmYL! zV`x>Cfu2T?m)*84D!az9dO#HM+lWy{bUlhVkQb1mF=jy459q+aC@NhzER-m!RpKX7p{sd*wEExZyPqX|IIG zIdudses}?5s{(VI^M5*U!1UASzXYeA%ao#WGa{*1WS>60SHx-Z2y6jSRGvJJF-35d zI#F4FWt!<7fX?n&#+_f)w#CK;X(bK3Zka|m)F43t<4`tZV4^;S*)`2?+gF?~JU#&kS zr|dyn%SO9Nk@xqtCtbM^I*lafb@=%;t<*t_6vgU=U%3fNqG~u&_wBqs2SzhX6;Xu9 z;`|Hblgb@sigO+1!e2ILcm=hF+tN5<4=o6LW9~U6&9|5B2V&dBTK2h6dbG& zqNO>uS^bl(A97eiJKL$%a)j7Yn*xT~`4?a9vn`b%>thc}C(mxmi#v;e+T2Hld zYXAibT(pq>-%=6S_N>7jz&dYs{Ca)d1QAilu%6%Q&%6qPG3ybH15Ybv59iBwO8yNCCIcpVT6Nr~UJ@mbpj7onmv zp7<4>&E?7@yzp$)jVqXVNe7LaSB^tP_20NtKK^F~NgKoKFZ`GRuVbB=lc*uIFV0QO z%oAj^8!F@gKbB3Fz7Jw3_JCzL%2Z{{fucU22ja_be}O@`^D~w?41$XgdVc{a>*N6% zGsla{CLcrDk+G~^w#Q9d=cIWT>_z}n07UNHyEl{6JH@}q{iju<4^rD!@v?Ycxbo?Y zzDeR;ORAD*6b(+9?B0 z9Mzo^3RK$C9h|v<#@X^0qw_%j?Qzz;`J3idZXpPR#+rWaby%7ZhD0dxHfo226RKjO z*ZP;I_rHTxxE*9gsB!-XY$mVm(x)VEqL{G(vAfP7z0a?Si`D&m zntsjep2aLkMcP#WRtKqjd{GydB}4yor_ckDSfuZ^zw#TwlcS-5lD~n4$J_5@KUq)g z@#ktggsM6+a9kAxzyjziHwQtABKo6hc+WQgO~RH)2^)3lCs|B=O7CkGl(7tz4>7Ip zDO{mfPIa(^z+}Jq`xwk@YML+~;dNg{sY9M*%hz$=f}Gt()PWCd*3~=cr`>zzoe&)5 z$l&0|_wb~=to@dB&rqmS!w~=hyY+N$bue}@yBL;=MEE(rzaRr^32Li+C9)XQZ*M(p zJ8bHIpO1m;jmu1wkpt*x(u+bIPZShG==<1cP=9Or(Y*J1*jlz%1DM0y2b}JN_n`hY zic_Me^xC{;6pRe=WX_o1!L)3(*+-;Vff+-n(N}=grUI--oQy%q%81(~*5N%o%9u2P zOf|a69Zo{UG|9;s;lTcR{g}O!+sKrnyxiLGP$5ml@`{zFOG&=Ph7r zwsm&4^6{}-yhq$~$ifTb&v5WnnO}a_hbK2P)QsTqdGFHN^UP)anFwp{-VW~Z{ZARk zP_M~OU3y8Ujq?u*iXeGSLj#G32p`$v{@**pgb5E&!Utt@f^h!7`%0uZ0iWbm%Q2Wf z7*0-JnIC;5ijBlG<=?d~6-QBt%5e2DD34{GneFESt2jEhGG%kaV*e(@WBL78j>>Te zi0pF9yjjecZ>_JT=o)lG;U4G>!hJ~?%ng9RqHJGWR|vDxN%X(H7`qSA0mw`%nh{S% zgzWkW+*?gS@62~I_{T~DJrU#=L5UsA^&^h5hfu$4t#GAln~5F^e}ySkGs!;@gxQbX zC~g1-`1Rxx%M<4vLCz3zu|O23qo<~XYob(&IkOkZkg_UIO>pMlq>C3*LE=RibSD+f z6f+R*T9pmIW|`o@oHvV;j_H>^GWCErbzbb#{cXpIpH7Ojf>1Uc$|4K7E^puu-hTS} zOCFKL7(9rY+MZqVU<#ldILNUp45UAwqcQ*6(jzw1+eZW)*gxWMK?6#P$Nl*OmGg+u{y=wMe|l{{tl-kwkh@Z$!A%7UdT+HpKC!IdbDMb=KHlYTjzT#; zF~FG%mh9}>>_z^I8)Zh@QZ~MN*Kg)NVh_?ca6jiS2gOM}y<%*=J8I!vu@CVj+hF1n zal%7+i9*Xfrh=m*6#w_JL3Ia-g;~#yKuK6lX$8Y)B-Rdla)08(P)J$!t}KDp+}27^ zo>Hdjt7FIhL_t^lAHNT!5*i&7CWQR_lKemxg2zSM{2PHnPIM~9(?5=*wvcIt3(BWM$GB(_ERg0txY`rj_YUP)X94su`Bq;k5 zU@V@bu!K7Zq04{zZ2&MwL9~R8V7|38G}0VOES|L_s^9dyh`gWLQytq5K1Jf*v=yc5 zL-M>yE$_0zY9KV2)Ut`Qsw>k6MgAQru4qSEkt@jh6 zvPhp=tYp>z+Z%|}m?d~aUnbzbJq@IJ7X-z;iG?;&APqU($qBc6!uHMIEWOKQmyHC_ zyn@ao!=v!2QD;Us@SXT4e@2k)Hk^(vf#)*bAC4%yjK6aM-0h$aA@v(~*q!zG7r3bJ zpyzb;zC@0vO6f(sEglyyU1C?`MjDIxu(sE^A%&O*#vCNV16DwZ9-zIPrY@`TC#q0? zC;u2+mS*BhOc?LUbKkw>OZ+Y3syL+x9axRL0`u@|PORKAv_Jk3nNI>)Gh? zvoquItq(b3Hlhj#65QJu;jZD~zxeRN86av2_m{J13#Heybg#j|vI=wrm*uN#-P6ak zD6UQ34Hn=&s8IrH{Q1TRtT+rGQN0Lm{^AmjVryG@Iy^g2eNT%b#a^gB&1@`3fi8CG zMz%z6nbHjuitL4#g7$Jy(?4UI1JHgHtZ)LofP)JhtuBY5klh=5f-up#KY72|EVlXM zu7yI~8wVXZ`|1IH0NOBCvkRu5^JpP>okv>$0}pBekwJg^(FJnWSK_lgw> zNT-u43lAeLY7)PMnT=jQ3wI7x?%tt0`^uZn87?u=ecY!g6~;I}X%wK*@#-iy?a1*M zCf3(!aFH#`gbxu!R?fDRLp1VtTB1_sMX)0nj5;=)vFJai$e#YOIiulxItNPZq4@8ABr_V|(zlSooEgRi#ri!U;0^0T_f4MM!u5zF z2CUJZHQ-JgOdzuZ#~t5?qT81<8h&M_nyvSFTsDmphU-W#`uY&3THN4<-IcCcLW3+K z@*Z%UEHR8BUS27`PbPy*laU1F^x_RxyJn6BXeirb-$O%BX-)e=*5@|7HF;eA7X&(D zoJQy5m2BI|P*oOjbYT_lPbGYbK_4do@NrBV;8wy1h)YlEm6MdI{T90yi(NoHSA=PC zwClf8ADL;xrumx?os!UaUNtx5iW*2sRcLA*rHvR|G4^TcKQ}g7@$YHW>j&8Oxv%O# zIN$CU)*6NDSs^Jy(fB&COul`kZYl?^RE@F`DO|bYetxTHn=0FXCO_=k2$c!j@3$Kx zgc~t93^&sK1-g1~VziLv2;TmFygS<-tW<~W17#nwgvv5`ZR4IB&(j+=#k|912?D!L zi9jt+tLF}KuTkYdp@i&|;Ti_JP-G4~?^?b-Ayfdwe%R$-CcBD@YRr`|jh4s~GJzsU zg7UnKZ7+?6DEZdj;rLz-SRg!yjDlzT%H$dMPYH|%q~J{`m9u*IZA;*Q~e=gM--G;8tl2JTzFrugPJRaQPbDro%&Z@E9uVq|HW?nm#OY zz^-uy3ELTCMAk0KEU&MZOT%;&5*ZjBpNEcjR3c?8%?4fZR zM6C}>+(B3=aYfoCY82{LQy1E7rVm62Dx4+F&yAVsc>s88xmiId0rv}oZhqLP$}x&Y zp=>z~e2F0*0`Nd{xJEw@Q(R)m`TjhM`FSQfA#&IvxYsCDGwb29P?!DgYYALL%e`ZR8{9KhN?XAPyjQ-R zrYfFRczq>G*paJCl#Q>f3|(w~&#n;;tT#*7zYs#^yrqtyQ12ez z#}SM%A5V=TX#4~*LS^y#tjS{tpTFIwT2sYUOuV=&5jres_UA`Qhe7&BlZ2K4nKxyB zapV58zbf|k`ic{oI~AEd-f-o6TeJtsfG%9co8Ka^H_hF@j{_mNtc;8yND`cVVm@+A z6PLKRwF~+`JuX_qzSV!R9dO(lNhR#XKKNzkGR&i4Cd7PE!Yr)$OIp-=`qiFV^ecajezo7q844y^@Q z6ZjQs`|?E$=-f4ZRH1tdLIX~$@Vlx#@_(NJjL-Coz2rchD;(bPmsmGjZEJO9Ev<-J4Q&fI(u-lq=)nm%#TAQ)<3Cp#25}_JWO9G=}FXQd(F=(kjiD-dwXe z%u+2iPvw?zh`FP#GAjcgsbEC1GTryKm0RT|e+FlHM%~};k5fi%Sb3iU%4x2S&s*0| zOZ&Vgm6^Oo*3$Z8xLnOb&@smU?&~Zkt-f0<2FglIn!V~lnL&LM!Y!8>3vTJBm6os2 z^_OQ6d}ij@vDsgQ)C{U;lHR>~RUJf7YGY{I#ugUTlS%ui**nUJB-~Oa>YaWnGdVQ; ziy*BqBPXz1E|E-;9L2OA8`Fi)dcHW&`hM-OlF+xZcysc3lKxd<)W~5k-G~999&&pS z26^`r`7;w%N4bL26fMQU)OviQ_d@|sR^cY8|J{HA7IS3-Mc40x>X%&%Rh}nJP8INM zMTrIt=(;cz|8lz)zx0BD-D7T3@3=H&^YXWOmmrgMUG}t!eQjo2EBo zQ+yhMBAJhajmNNn_RF0Mf9bjy_v@C&=tUq$4eI;01puAf6S|X|WHGYmP`W1V3C5piyg3onvfrs`@Uy`p0$(OuP zdQRn!SpuOXT5W&Smyuz>O6M zyF9zgMc*DcKYjyUxH8tew8(na&|~Y+pGWQAT3MHi)q^bQq@xC9^tlB5PfSUtrZ0T< zXZRYca3U&z;GCc(J%ExX=*z2*tfuRXJ!)5Qp&xj_*Tbx@uob2Iw#%FG29t{f+Xp@w zt}*M-ZH-?e4}O$V(N#)**7_{4GB9N=^K*&e$z?&J$V5wk|Dc=xIU9X%n2&eE2UxNQ z$Aqs1ZL*yDdE7-{Ks#obhVVnZARr^I>&4ypy)lzUHv$1`KGjsrnTyWzaFV@#Q~h$M zCxfPXitqC~n|9ww)#iBI*F}673$FfoO%>j!9h1JAEZP~?^ECO(d_^c#_+n7lHs&)M zkA9AjK8Y5~Nr<|%mFM=0l=>7@7lb$^I0(*iNib%#u1hEF;tE%WCwxn-Fgaf^;BA*q?j5jz35zNcJwT;6QJtG8O!W>ByxOuw3$J5q?J z)ZIf(7GYwjm~uti`k%W<_c}qJFDmhBzi6QTQt`k@vwQyV^yi1q zQ#N=AXA2|OKH8{b4Zh7ZCYrea+Od-Hm{?LccvO$Rxq(7&g7Z^q!H)v17%YYUwNI3L zJ=8Ar7P8mv)Xf?T)J1&wZfA5&7ZHQTieD-29ab8R4Bi&FxJBafIjg`{roc2}G&Y-X z+U^VKw_?0*?7*ST@hsECD}N4iQfX#|#a^?R=?%O4m6wetD?)`Xmt1V2IAtRj{X+J7 zofsS$oGlHadp%78sm~~PU@aIDB+jfIS6$KFoMU|Ehzz_M&PU{PH*+!U7xgZRUzZh#*7MSh?XhsUL~fpvfzL7>UdcPZXvmeU)OP8sm*?5g zR+`bZV;kX13CUmE%WC=0iM?UM709`7Pa5JWOphL7KKz-oKzy&K6`S$G$@nX&f6;44 z5rLrf!_6J86Rsy0LhBw1aS)>TXI%p z2ri%N=h07EL}o3)rBosiFH5GkTuC&_2ngsuB7-M zzA_^$r-$N`*8LciTt(%W^vQmkkGbR&_8v;wyH~&n?~GwW<&t3}GAY2U6eX8G9yL^F}rS zeN_)DvRrx_N&$by<;v-79PI#)eoQ6(kk<{VGhg%ASvQidT`Cup=9FW?GHFt5NdM7D zq0veo8u2z*ifwYOT36RlDl9leA{xg)=vvCYp6vA3Yp~naEy>jErjmg?zv|$2XBdO# zzW?;%Bx}oOa&!7-xMS(>Vfw$tNA|_<6)X|#&u0`ru3NSi;pye?7%BHV6sL*%XhLXJR5~${8 zqsMoWezC0IP_t+-P&`yQ-|17UsE!RK3_eeUE zdv%CKonkUIXvc@KKdF8rs5P~Mxsb?7I&hd)q3Pu3TVBwTZw88rp{%Q8-?!y5|DFju z`N#hIr?BfgE9;_XY*goI-KG`Gv{sr4+pK47G6NE<-(O@oq9{ey;H2*IOy6Mf+_@0} zze=xI%16ynl(QcC`A$g}2Eux-tFQJ(kN@V-3>XGsUkv#fdNwLJnv2Ex|GTbknM=bv&%$+Zw1-{`kr9GdT6Pc1vNqTIpQ zezIJ`Be(4|W0WcMrT0f7nZB5QW4)2do@;TV&=K=0fhpc&Vru=ae0N`mI`%oK!*QQm zR?fJxE9d%ZPh_tvqti=8Nl0TxE+Ex;K(9asT{Nl?T&Xa5yP!vL{o^%?$$0h^x*Aec zrS~bsYLU-V;+GTpW~1mCudJS=H}NZ8dh15Y9K)#*fvLib$esm3g2`EN8tiB%UQsf@ z;msCF5{t(6kM(mfWY@)6QkD$Ui~R>^B_e=BPJ?c6w%jcV*QfhmEc7V>1V@Z)B^ZeoeHOy*Yu z#%etoZ~9fu6mzzbat%igoF?Mn67p9s%>N%U?83 zf9b`yUd>shLh(4pFjKKM85*htvJd!4cYdE+JZ%@Vw(HdGlUiw>Yke|Z1WELQRD$*S zemC~%y7qo88mm)QQLr;42vXhGm)=vQH`h4BuBmqf8}Q_)#1PFBLd_)ZT@Z9%L@GzCIw5Kw^-GM*_?dHMbcRKStp&J z%LUHS!i+hSEX}2N_tJp2wNP`b`w-TpatI8988YuDZYUbT?V=WS^(`m`w7~r59NYFcdVOHVc%NhQfXLK5ceh zWsS?f9tGCfj(tPCa#Q%A9!?LrY_{scb z=~gE!yRO7Dwh*`7iaz z4cZ7R^DCc7)=MAxHn{yGuozQNWXErMwB-8f!sN)#gz=y8N%DEt3x`EYU0LU8#Z#iA z9wx`NeOI@0{Ry_6PJwmISulO;WUudh8Y=c2RNM1k?mt{-@Xb!*&}Im&bU>Hy;0X#D z?+lV=TXKQKuE+@;c=jg$xbos2aqZ8C8YgwWbp1tRd#26zi~Y}=nKWXRsm!8FV_S4W zicP{R;p{uE(?&358HKGnmtG7HE$sB{S^hEMrrE2MU0ukR6~V>{lOiD@b&gh9=a}sE z0y!Bevhs+}9jx<`i6P`RbS)kCs(Nc|7?7z=)APLNZcOsCwAE3p_yZbJqc@8446~Oc z=q6bRVu1sCGM(tc_}O#vSWJ9wcp#mlWu8xkYrvB1IF|=pzE^ufc1q zCZDEOcnQN^7(^XHU!Xf}Jv_1XjxWMuKwh6KH)QToCf>0kC-rQ9B`pkNbNaKP6d$$8 zE5rH`eb3G1N)*@ekGt`0ILgU0?6N3AM&5SeeSUvN9;Sx4v4KQ2N-z zH^RmvK;%bQMjQI)nDDZB}f-12-{$ z-A2dUjA*GkVv@}DmA0h9RQ&;4eKsgVc)b|8B$ul1!k>8&I`=^(8w};hn@!oxK}v-$ zBNg&YM-MA;-uz*KQy+&WJkyKlX>H2v#qLkP%UXPB7}^nCL4u)w zR4uV0G*IcoR}dEs?=2sqF`iwvbmL{TywqDk312&%ocMO>Yez~1zb$&+*b-$&sFA_= zK7DG(A*b`|uXB!CV1kE+2=F^TH+Kz#p-5^Q%{sg}>8yiaR$nyoMKY-33!?q?PvK7F z6ojw&wc~hOS5A8!u-Jn)+qP7p=w+(;D#^PLFKcIBX~zd1-`Zo`_Bm%fkSk53R%Rif z#!?9*q1&ek&H~el?(vyqQJQ!9GE;+oln&?gB(Ef6cjGF`dW&E+C$L`Ui0$JaHvzDr z;o`(9CiA;LV{*z}o(AVxxbxiB(L}i5nc9rC)qvjva@E}6-JMbtQ=!E^1v6Layv`zq zjO2saNLIUe0(bs=W&4AvBlM#dMdu|Ymb7JNB8j?v)Tf^)va2obJOuSZ`l}GZq&rZC zhT07aIn#Af`zRwAOgoTol@4J=QXrI7DnZfvz6Sjg*&qXWgk013{K3K3;{zgL55Ezx@{xAb_d zK5w5hglwZ{$D&jOi70{WWydfu=4_a_Pl*+2-PG3*CsCL-=dzj0hSeqVhAis=z&fKJ ze*pSOXp=}R|Ec{{_C=3%w=Wo$d)xcV;ObuAhPgQRn{5;V8UDGVm`DR zqbL8l)UhaOkM%Wg>bs-b209jYx2Z9(qADfcrXlR&&kn~EEAG;9tcGnXB_@uQwX9u` zt&OV?U*$t6bgHB}c+b!gE%!#-Q*Xgkck|>kzD1duvMK0`_s8o;A|00ZTY~TqrUKKw zX7g0WGze$d2?ScyW`!bXH}Yx;0qi>ja1NATNz;b40n6ARnER6ebeoMZ*7(=X!_!jsOSUfobS?SDNTFP76W?q3{`i+^#&o}ncP{UJIjAKyCAG)fQN@0f#g;ru34+_%;Ok-yr#eoDref6> ziweSVg>D2O)gk>{dDMZYsXQAYmaRtqrLpgVgRX@aa-;BS`oAND9`(1X|6AvrVxbi@ zRkbkJ6BSL2HKge|T|e&)aiu&a_NH=*J?$mz-rxKXdDcfkVjXzbNS11dN~2<9xld@1 z(55m%xd8VCe-qYON|rhW2zV9eDAtOi{`!|svHmp3Uwi`1z~3E2f=sc9Ca9YCXT}(i z3Q+b85GzoS5Rf}y9WWpwpiZ%nn1VyNz$XeEdai_qQx)= zR>EzP&eU8qQeF9opp2nPYR+70SV^RgAcW-5CI-|f5p;ATQ&GB~L0?(fR^ zD_`r<_HBg+ZT(U1JJUH;(WCbEGb8PS0x7f|vKP5ihZ7rnxI5NgeU}OpeD))U09 z8}FOcps~teW5`Nlc%=wa(;Dpmdq=mt&hMDtZ_wZ@{*{nBCCxF8-HRmkuC>a89epz3 z=Fy07X50=wjQpGZ{9`0wJZbsFhDlx~8%xEaAGDPi72Hh!UH2YT#>CZ&=QX$(Vlu76 zv5IH{;A(l7d7ixXWc`6i-JLyGa8Y49@f>I|*RvX0gLXMJwCdmRfFwm7oF|(Oo#W*y zcgU!wc0Iy+4`-3y^4y?h=<3_U*UH(QK1KA0zt5`{E+(v>dAWZq0}^Z*RI)Z|Hak|O zM2kO^Fc>N7?GbYjKm6Cjxq^>!{X+7 zSNB@}i~M-jY})=3+w=2UKWxb$n0SnfdgE^$I=Nx^WFw#=)$ZN<8+ok@W?paON;v)v zPT@U+voVed3Jo3|Y!>(V*MMKOeDx57HW#Qb53vlP<6hXN#q(CXNS(7?0zF25T}Sd( z{93yTk!;$nzJSNhHV)Pb)|&pnIJaC$+(%bV`4q<8M;*49R&tPJ=Q(p*{5pzb@~+of zpkkVIS`82dl8N+ba;N{ejIUnnrwtKZtGP;axDI}(nbHP3=qgAx`TiuvGK?OKwalf( z4D>xe{hO8kXjkJFX$!+C)n*0?_nEkg!~2TRiutFeHM3+9f!pI>jGo7xgOr)|rah^) z3y;e2t`E&WAFJg{?xuQnE9^Dbzy>rD8TP1;ahF1_EY}j^s3%WLs=LbG=r!i7hW(OY zWa48nPS0tJO}td-^&g7@JLPkwNaaC< zRxQB_RKYs!wIFjv*k4WcR`a+Xmy>x(@W^P>S!Pj%8R0<v17W`C3Z9YIIG{k<&wlTGv>)3Y!0UN1w;6{nR zz318wA>uN&urxhNW2k7DVOVfKOY0PY^csdn+}HS-_HOzYkLry->?La-l}&8@P=Io) zk5Z^#4L>5u2l~A~v@1z64YL15lA?Jbr0$86|J{bwcAp|rnF!)(oB|HtkT|zXFO@Q{ zHq=?vy-W~c8kXop?`x(ogN`xwBonIR7_W6_Ej5(TX`4rc{K=UH{mmb5%!-SCMhr$l z3+)nLgvzroF2Fs)ETVHPI}*v~M^$x&im=yb<6AM0asvM|riR?EItJoE?b(YL`k4=L zH|xAenO zwkC7f9r<2?KawPNSpuWyc%MUPh_yVFp`b(-8p{g%Ek_pn7^8ztm>?gquE< zi_gTk!yRBJHmZ@E88On!^7MVDhC1yNqdMUNVh)C%jIda= zqTkxNvjWeL_z8ocb$yuhJT@4uP(Kph^Su9l3cZG49j(U8T`^12hzn2W-0zSC!I`g| zEGeDDU9H@jIl^<3v9EGe#G6@|Ic|D*RASthG}lF>?VZ#Xr3AZ2Y%je#hOXa8`4Ng$ zKALf8)FZK!Z;_VALAz|v1tBcxK~Nuwin#ti2NVBWxW@u&F8C_ymyj?`5jQarf-a=Ji`_Gu%Xj|^ DttD3w literal 0 HcmV?d00001 diff --git a/lib/NeoPixelBus-2.5.0.09/extras/curves/different.png b/lib/NeoPixelBus-2.5.0.09/extras/curves/different.png new file mode 100644 index 0000000000000000000000000000000000000000..ae85ce2fe9e18eacc5130c043524c5b2c4d90432 GIT binary patch literal 37163 zcmcG$c|4R|_y;^%)KqvP6(vn3#YDqzN=KW zY-7vVlWZY-vdnwkqv!Ygz5l=O=kw%w@|^qJ=UnHU>-v7b*LCjuT9;MXcJS^%p-^lD z`~__kYV$7mb9nnE_zjDTvnlgm+Nw&Z?8bv*@Ru!?=QYlwP;bL`u9|LzzoQ-T`YtFG zdlmABrP?9Q9Dcdq_2M!b7_O7D#7OsaSL{Eu|hho<(Q7G~=!iDoX zp2m}F9?uRgd5x?*XLKymON~udzAin>KXa|3x>VkHI=i!)o-1Ek(OJ;3BPQzxfqcO* zg7dsu)bR+;^eZv=*dtHS|6sSnKSwx~w{S`~93I)V%lY!%gSSd(J(fSbw9?YEr_wLC z8ucbDWg2_xDlANlXN-?OKbbM1w@^7Rc`shpgMLlQ!f*NIwa1@7mPiP8q?~#y8fyK! zb2y-D8wwRDr{Ro6qfm(@+X={I|Nr@^7?W3682;}QG1EI5X@%L@qK2a?yuSXTzTeG9PK!sCfBS8T^~@=&}y7<3Ja z?aUw5b65N~Q-gLVF{bARbdm#FCf}o;@*d)%WhD+BS?nSIxNCK@W?x)%g?&+)jHGSi zLy?EOE1o?Hxq46T5z&?9!*`5kxTxj2i`$PyPsRDM*&fFX^Yk~_#cJ#AA#eA!haBl) zKON~PA4~o~nB^QS*=*$)ggv}jLgoU;gO65sM@{;_UNyfyT0~Qr9OU=Ma4a|!-?}r} zGm8@r5Il8XGYYj^g@-4CRYp`o`@-GJw(PS#RzADqly$zjn8jTYHtmUE9rV=63w>a3 z+`GMGOlRKB&8BD<>U0o!i@Bmd>w{W{f7WoK=5mat0Ew*8emA?5A{N&t)EG4N13tbF zJ6z> zr94vQ-&C^$S+75;XBItVGRnxwcCOc71+ysSDpjm*B`>R=3usJQ-Gy~smofIj(^v;f zCe$u+du5YZTOL2mE+yWa)0@?LATt7UPQ8Ge)#C0VqIlV0H3BiY_;vnS2@Jt9GoZd| zchdRu)gjng<*#?;IMrq?x#I(dF+wG;WJVlicP(Gv?IWF1th(=n6ql|IUc1CI=iTCd zMKb{`OIM<`z^-$r{^&{bQf^LK$SLgDZY}7xChm}uT+E%sb5!A<m3N$DacV3qxMQ= zpWxoJuJ427+tLT$^28U1O`Lu>>XLFhEF}4M#WVBxQObn_>UT$dZ=N)e!`8Yy%-hmj zvz?$AGioxS^}ACq=&2&!h;f;Nhw|~AIA_=a>w)e1`6$0)trHI~SaLtSa9W5MA=Pp* zl$AZH>tbv00}FQR8(geJwe`?*dezFb!@km8_M4j~_8De;-Jxo6RhN28XdMHJnPEfu_p%P2{6NSX5OodCOsSa5KTMJ}A^-mj6$Go~J5l z>*;m=yIVjdl-DAh1%*1DB8W*ZFZTln!Xz6cK5v2A{Q?kw%r^Uj2HdqNwIJ?tdxav{UT6S=9u1{z8 zUwg3bA>iNV&lQh($f_TTo4?;rBfAdIW8#R+2d!K$kBq{zzqiSPHJxG<7G}2vTvXPc z`mFn3iyO~}zA&63g0Ma9Cx>tKcIN&bi6tBVi3_;JB@DX>;<<2~#!t%44 zwgwi64HE}Fjnu0=v3Cn6?BdAP-KRy+b<6(A<)!RBH_H=8EeCs7gh=CLOHfqY0vj9QWX1Gj3GA|cBYmTvU|nO?f-6!=;$)=SovFyTVG|+j3*fE&U z#n&|}?NUj?Sl3IS3CD~pHI->`OY2Yn>n21^Cg;3S1=1e~COgaI z3_Hby(a7KVu3^xG;D(nfR(OLsS|RnyC+2PnX5~|kUG>T&9v&@zfhMpr+Su6$nfz<8 z0}NlS_t15jBeyvkOV;kob;qwqzIHKt0{M@3{D=PIZe_9tFm6N>9G+~J*Wll z8^adPG*X&varb0yAu*hLP8HZgqI2#>?2TVtY`le2M_ap*57C_xiEySpW^ z#c`=y&WM74=nPBv^|rr_+~%@yN0Pen&im6U4u*ycLD**(!>#bMn)Y+hJ-*H^J{@iS zbH%@fuDcmoM4j`F!}9~(9Yh7p*3Kr9QeDIEj4e5DNZt7&le`o`|Jg#SwIuUwrL10U zFSfIDTE895OX&$~nQiV~CA<NxV8beWT}o|VZ7%|YjXFPjr!BXEX)Px5%v zsze)Xk2jlYCFl8dh|`ONnR{hRRcnNZV3Dsjm$rR}pMg_hAqhX*G-GW{$wG^r4kfydAt-OV4m*xM*kFhyR39q&_y!15XnHB|(U zGIeEId#>uX=URM-E7fXy11@AH+^#w3`-$aZ-OIKuV0|MjN44Lm9*pQ*Xa4z(*PnMA ztJyo0?Q;=K)P4E(?4k&q6aN06^Vd6ruvZAF#GT0UU^CTK3w(m$# zGtHo(!^AE)CNb6qX#4AJLCKFdpDi%G9h{7q=ztFMy}Q1-hM~D=jvnUqLtiT0k|tYA z^S&ia^yFdFM+x)asixr)k^QungxU{&eTUPa%=N+q$Au`4g6(qd+n zX=ztps_fb??VKEagifcsW=P~Bu||jO3Hi?R`dG60Xnk*qTJqyoSe}5AliQQu3>-^n zCcC~ddnJ_~b6Q#3$H(VMD3;?Br8x-Am7F$2yTm(j7J5*G9)bd_q6M)YpTvpd3hHCY z!B~$fEVayyVFJ;<*zO)NBjpPjn|}O}e9OCJEau+Tqhd~CXjEWu`x7*wDAS}3c7ox} zqlC-HkgdD;rL(S0mhmz;d8ewoO^QL?E<(%xww9LNI|-BrA8uTLBb;b@(ZR4=3!Q%Q zm$UeEJM4gSW}hn7)vw#&XJLocebii6XCs^u?XxFxOQH!%ns{w%C2bFnC3&#%^^(P( z6=FlT#W3*>jN2MkyQs#5$OIe)iqDCJ_@82t_ zrgx8w6&U2fFJmzpPsH9I0yM@-_UNXo$OKEg>y^-l?b?~7lh2P=*y0+>$t~Zt} z==-xU?>u)j4+Xn)L9<76&s`ZA8I?!-O*z0bZ?+xwvCom+TLO6Yv2#JNWR1rPne)LxPyxVpJn??$28jPCXn6ci<{kOi@p zjdzK>10MWfPW$}6^;C~+8bHJY3}xuTA8~wLw6jd_Bdw~e{k6$tS+&y|%zd)6Zm>O9 z5?el36>gLA0NJ*ATP%lNOqDWL5I?#1Tn46A;q522&1FUous)iG4vcftk2=YDtM_H| zwJ##4JN7!`wVp%Y0m0-FaQ{WH|1|K$JSLNK2Q%3nJ1aB5fqEO@3}4B=!dOq%b69V@ zMnc@7>CW@F(>q}sXSJL(9-TCKg{htNeXmxxB!Q`I9w{|2T>DliiM5M2URyW<(X%=4 zg~Hp~fl1GLK?yOBVHaM?>Rmn}??2biy+jMq`5Yk5Hk0!_Ocz#0k=SRY^fuwEcdCDv zLT>jf$($ZG4g6^cNCr-U0p5>;u@0SbxP^uCwYAE2va9*z$jXjR-+1JS$h*q#wZs!O7x29R}&%h`A)9M<>6U*L>8p#dtKFMdIvM zV#%joDe%|Jn~CESajV7sfR&cN|M!Ny6%&9>_lKXJ<1(xX?`d1C6TF+woFf`3;9tb##%XK79gLXm)@C z1{B1O#Pb^nePH^k0OvRu_hnoC42<<+VZYhmf#|k`*|Pb|7GMAhfc@-y2Nmjie}j`8 zm?ePi`L4-pDAPV9`29DMyR07O6P0|14nuumB5aoSap2c_CE~&B*DH2yDly%OzxezZ4wx1mqU5l7aSL|z$NwKh`etG^AoJ_^k3uQ zf7|CIO9PNWyP^wo38=qJ!vBK>8}wF#J&E0%fY}}%wtZ6jqp}| z^A~_yB)m+8*W2if2jI6AS&QP$J_JQAKwMzrv74y}?>&od0-Rjm`-T=cCV-&t<;{kF zEzWBMYSnp&P5``N!<)rr?b&^Ox+Ca&wpW*0VcFtXz)Q>+eR=5bnAz-_PbJqT6x!r& z&wkzx@YzW-;c>1_P{6Hl*bCLRIJWJ+0RXTF%9O_wZ*6p$2jzN8P}>%xwK=bqy(GM| zUT8k;MjryBR;x%^5(5w{HSi9;>l|4+OylGqHl^FOi7!OXmcSaP~j$)4FpX<`?=7^V6$hiz|Y2FDp>{QqU2n1q0Ein7&Yv6N? zs&M^uR%Cu{CRh6k3eIkv`sa6!aZ4l7Y)?;_c?wIWiLatEEx6PFW(qHY6}2oIyu0Z) zeNUd5GiD>akG@Jgcw)3X13AKGDTcQKRsw7gOy0~bmq*OvZAULa)`pmK?Hl z4j{)q*f7%#@SEl|EeIf~GVZ8TComwPqCUG6mI*;$U`!=5cEk)e#O75QTM@>|Jn;pM zdD<0-<87=P$#SkQE2wNCyWY6CdOc+v@l;~I-}t)R3l&uh~5KF9uin1`M3RQ`3QKw@nqL{&7yNA&@6N#KVZo(Ud?KR)t3h(u!O8US|FIjT8Wp@ z(x0c@fH=;E5a4}=8dWpd z9`rrS6Tnl}*&FNO5bM%OGZVxNCOytqLf~bm9I((5y{sr~k?VD3?R8D($o2IlnBZBt zQOhDk?m}sCQ9V@)lFmNl2YSNaZJvS!h6_vT_YhX)$0zYv@@KzadQ=^CWK}Xw(j%x# z!{K?iR12OAjO!y&cWK^BI#Y0F@kBFh+MZh}EY(t?%}gLrrIyb`Pjx+wrwX~WcOrm% zQ3PCDHZLh=o^sP=q7`76yrN=iN5E{MBZkfNs3_d z+v&AW@0O?r8*>i~(s|ZBQwC0r;f&)Rm^91+oFYH!*iuWam&7)djr0;TP9ejOK7wJx zco=a}cD)bXNj$*>Yub)uMbWIbK6BP=g!+Wl2hlbNrB=L%$1BfrP-#ID^NVO-%jH&H z%AX!%nBkwfCK8@5?epkw!{ukdC#Xj;+~3^8z=D8ZQ#Refr2_MtIA$;wV{NkB1k?Lz zV3xv*Fk`K~4$XEX0)Feo08(k6-KS0V3HjwKd^k2o9|vGh=RZ>o%#a0D`)o&Q#SIop z5uW?(L(r^40WDCxw8Kg%c5#?tn?lDaU_*H&Z9WD@2&ftey`WO5H99C%&cp7G80?yZ z-G^Q=0)U9+plH^g&%{-fX_RVtOL$5gn_pNMGNw99-Xh4ScUt#>jHmzzn$1q|Y#2QI zDypCUM@|(?^p<3IGGH9XODSmZty9a_yM?FdU~H1fLhu-SJ9` z=3vJpHa%>S;FP|+UI)$KSRuR9(-dK0aqPvHnyRX*>kc-9;Z6rtHlZZ8r4G5PO4%F6 z(I)%pCBf^|289X)HhV-wvw9XE0&q5OTx#A<&(y_J9()uCRWmX&B8p^{miq96BTpwa zW$zR}w4IBVlD;(6U)=R7>CIYcp|uiaa3GA0DDv}Li8U|fvV%VOUQ%CdBHEg^Z~bnx zc4VOGm6xu%+X*fFojO0Oi@t)rP6vhATHwYCr3SMT(hzV;SKAEqYBLCIFDp$`Ot0L+ z$BLOM&3~Ny>R_Xd)*Ox@!0ITfv_w!jkFIrej4UFer$X<(EyRPrIttg7lxgpy#ieQ2ltE!Y(&5B3D>NP5NG!9O98`SwG}G$byK&r9>iYn z)M(bWi6AWE84rJOc>$!f!Rz>Fu}vh|_8sh?7JLEF6c3YogXVuK~kygQB$5_cZVay?q*=E=(jOR#d-{;9q+hS z1}^E&a~zTHLFuFh5_Q&yHi3U;k^Q5ea}-;r>)bazn58KOnLN)Q@8>#g({XR?1%$W;Z9G9>bU z8nWDVFjQB{*y=EH9rgtMF*WKA#A|?}?^9LIVhG5ZYryGb=dgUQ3AU|1V<@!Mqh5lI z&{FRy_2KW^Zg~|_YlAt6WY?*2r#QNtdJJ?-`g6j!An*9J?ZY~h!TGdA{D^=XzgE`(+EY6C8Y#Q{~cA0 z{_^!FWbahp`$$uOmas5W%0#v52?K;Jmb_PEbJ^HCv1*LkQ{vNXV2MVAt9JB+MoMD6 z3{wq-_&iz<@KWwPRHe3w{k5)_r_bK`vR@Tqz`9hswzO$aQCB3fTSqvq35CA%h=HX| zZ<>62v+D1(1b469*I;${^$C4NVEsN7w)C<>j<(GE=tu}53$G0bvmFaK2vYxTlA5xw znxL##{!HkzYt=HFqN5?Q^5s&a!UBAAA&^Wf0Vx4YuggV?GhqZ|=_nw_zF>Im%JfW`xQT2J2zle^11iXt#|FKKWo}9{4;Od9)wkPoP4I+I@2`W2?{{G zX-Y*!yBYkq&Qc=-;T<*mK}NQ(?%2M%83(MkJ|R!rrtke_Hp1zf&{W7|RT)9rQSR3> zRFLtU7<9J{tuk8A+X%2JKkVa^FtwF#)+tlU-IAE{)j^X^wO$02KYc&>l~?tDPnCv9 z*>XU$j|MI-RSpP$i$FrlM#HWFL_FgxG|QBAm_DL`w;!>>rR=?aE)!jAB>Nti>16*Z z1Ckeoo|NeEP-Mt~R!ZMs81Me)z@_|1=mC+Sd#!+IfBkFtUfQHpI6x=HPAW#@J>PY=nWX>}%+{KFTZcFJz#bg_xRma#ax^g>C*Q+C7#@7!5o zo1zW55=2vyPKn;Cunv;Co%n%K5kc^Sy!+7Ur$FR=ff171qqAw`6|BvtR=?XPxAwc3 zKx*j~W{mc>oSw1K_k*CeCx0A{WmV?q2h^*jY!^KUK*9cbMsyYF2NKVt6*|=JBJP!U zH#*(a%g_?ueuVOz^OgtbmlS)((b8!iuRVyQb04ACbYUg_Z2`@LDU}FS!8Dfzq2EW zW0pGB;P4tQUB}&rKm?m)t3n*9q0^rYIMzpQb0T9RrGRAnvF2MIv_w#OhU%H zuxGLcpwfZ=dmdvxx&XyHJf!_(sTZ)p;@S z?UV+7>T~DtV1xPyJm)xR%ebAQf}{r9&X);v)2>Ldgw{>^o(F(pc{^I|JHOFuOznih z5e|<=uk&0q!zD(Fl$8Ydhj)$;mw#+Ir-u;C>#~;ZoL;gcR(M*bR^$4j4`>9Qq`bc^ z-$1n6TU!dc4Vq3B?%a+NUQs2gEReVox-{G z%t`RoGA85@!&m3B(ag+~l$9dVR19gl^>LUrZ1|#7@Qyb3S95(pQt!5`t$J#*;s7e9 zsBI6wAsQ?}ub-~z0OkD4uL6YBX2;z~p?R%523e2&b+MyW2!gR!j*k40B(-=VmZQUf zJ9;Z78R>jP;rLQ7gbaNcWP81iSEjuPQLu|VpuLQ-ZcDo&!20N*G)=8AC{oAJFirzB z#Qick7V)60Ad$g?s>9mf3LYw@~_ zfBa^YxgKQq3hg%KR3ZEe>tMKiCb8U)rS!&Ck!j zhk@drms)triK9PRP>B}J5mf2lAw;nCUdGGlx^MPASHZ)(PbbUz>p_x)&BD0Y=WCl3 zHgx}~7nzy$hxWAh_Lg&?$bLpX?j}!W6rYeqHOf60jUrNP`Yyxqs&FEwp508z`A(F} z#8|Ioy+tJ9Z-sUSp@esD(yo*U2Vr>#a*{qDg3w3#RG`d^XT(IFd!ptR7y0~pdoFB> zUk=xEY{pjWk0I2;Sbx3Eh?gQ$-D4R^`l6(w4|tO^@WnCvW6xvH-TVk8P3%6PV7hzp zbs6WQYXlvZx*-tiu65Rk#s%2E1WQhTK=UJUC)FdyYj|pKLtFl{veZ95*x%PDIwV8D z+p5Wx0iIHGH7Ch8-bwgsnrwC;OJ^?}NjAc^5qU6w-9AS1E6IXcWlj|8L6~jBv9pyX zT-w=dakDiU7EUsV2K(DGp^cok9zETi_gMwbl+MS=gIt1GnH)(rW~v53z&bGlbfB!~ zaH^HRZ<}<0f^;KO!`>DNxyvum_hoY=Bd@Q3lUa31_fFsUE#d16jCE5cxQ$q+rcyhD zDuh_oNieGu47s~T_`B24wNDvlxaj(BNf0Dt`bb^JoMJtcx1d#%|8@+-UEw~G@+)E$TJ98#hG z{#q@8Y4G}0E+w)PX+|THW8*sWb+{;{tJd&Zg?vb?w>4Ej>Z{`7>#n>Lh*winRI>4I zq$I!@;~;vMt2#7oBH&exi&dGq>esiY|F)w!%h>l9#00fP-BE|v4BzT$9a+4Ve|@S1 z%}I&rVFW-n&vV^4j=UQ&&<;dGK7~#n^Fks3#n&vwdFwenEXK&>*XY+J1Zd~_badrC zLPih_qSm=lBy{{3-UuBaWjZfgF5>a@#gE4%Fy&s;30*-yk+u(JH0?$T@4N6l5NrNZNVO9JF$17VcVC|NR?rXPeTU*epQu>c z=hAtyd!s{&Q#ZZi+y6O@^)aP`CjxASJs6d|l$dq7$bII=;KiI5WOJY?ae&_-1p!Op z@Ztdb%G{r|GXX20X@xl$+j5;o@?QIG9T67UeWZsQ%3L5n_*Q#s*=C%(!qQSK`BmSt zDzSkV;J|>COiunQW-0oSNbT(N^#kg+dI z2VJ<-^Cg5C4tkt2Es-Ll=I}6E>KFZAApWesWg})NHv3sMg!g5lt>1?$*thROWUSGe z4%(f$6-fkt3x#JHuuN->koXxuEka~0^(F7ZW8d1nIAk^gLQP*Sk#t4vdG%;*`|^2yTHf z%BoUn1E04dsSv%UYx$chCLS@m5z%x&w!J7;(Fo}zzd3>p18Nmm1!Oy&r+kx5)=IJ7 z)s=`sZF380a*ruuUr?1gJ4Z**m6SpH>DG$*5D*T|ElJ}npqde7a8UxY0D?Y5DD}B; z2O8vWYt^Eqq3}R3d4~VbNb$Ly)L0AH&Q|EZna`FFJz*oTqe_23cMbv5%Dfcf+Q7N? z;#kFUio6&lNPbfke(@ znNpYK`@>!Xzq4{Cp!mQseaRtYHNN2S_Klc2H0>X0mc;9=Ltn7Str-|8)s8vhnO5S@W-BVI}tScN+S&_ zt3X0GOg|kt<@q=`xwokogGrbEJ1iL{-BZ$O_R|73^yi#k@dT0juHsW9oWRAI{-~ff zi47Vg5nYBbNU?uQXWW1o29GB{U0hke4r+3U!f{Nx_22#u^%U$?uIa65D1wBZ<9Z-B z&e6Hs85Z`s--=~}E0cdIMh+C@@29{KhB!P1If5_WRHvGQ@_x!Pj5xK$B$kIW0jql(dzw5UkJbbqxkDQHDMp(wy<2}Q%iuPjLuf~k#AH2#02}_l=?Pv8 zxEcAryU(605ZhN^c!o3qq`mIaN?kw)Id3gA!A8j@XM@OEJ4|gv5O;;pjqVYgs)`ETqDP%80|efPIq8+y#c{?zP<^tOJn~U z^29IJ-mp+;`8>e(E}u&58Xr#g*08t-Nyz~6Nj3DF6^bsgQz3l%{@pzi_SL~^TSiI{JUV~9z-Fsd`OLq9$;cKu zBsKH5*N5G2la!|Dz=wK5U;odB^?PVM!Qd|P+K@}Ew0gqWxOrV!YJsC0-1d8T4AizX zEXUduz*XS!`R5}+30D+wX39L!p`31Ip!telKsLs@_4~Ry+H;$|5hk5C{s-?r;0|~% zOJJO2rJ~a5`^a*|cEw;1dt>=y+)yn5wB()u{Z%bs`^2|@#pA7NItx4TqW<66HSoCS z6^4gc5t&FD_k3!noL#Fqa*7oJCLHK^!P#COXugy*IcSv z`k<~fDpg}sWLQWIRD^SKa%QBJ>?1!qq#~35^6JB>SSO%AnU;ApUjSiHnXqdQ+a)z& z$}_-`H;!YmI#=3H9xk4rIr))YU@KE;A%?cAT?N=lVBMjt4a(>VEYuO47ye@yH#%|Xu9Z(eYm%il7dQ1`PpB( z^6k;K4?%6c>&kJcsEKBF*#m@Nfo>UCs=^9}lLlU#-}7~JNlA8=pOG?cjH;g1mWI7? zt?jrGx03dHIH`pkwVqmtLK)l-SpJ^a-687pK@i35Sl9qL9#=VslpW6$iY6CLlPCFz zY?~~Hp-gc#?lJUn!aNEJI+7LA#)`}jL$z|o%a>*!|J^MkdUZ~7f;Dh&wH@gSS4A9& z5d-JzyA+Oim|=_E4OKb-SC+Z~w3FvwrXC!-{Q|-M`_X8XmUL7gz=?~Yo-2>LO_T2_ z}Y_mxpw7 z`PGV#(C*@TtsRuyO!kk@;mS4fNmY2iMh!$_E-ok9G8^w@R){_!JG&hEUXY(AT4F~_Hx2@la_AZ)H3K6DZ0Opdb|&x zk)3S&C7RE+kPT%;4|*b5j#|?2&3N&rUM9qP=#zGmued& z?`46?K1c=W3P2td3}(4_ExilD1v?;I4DRbG`sa|!4pF{b8?QSzXnd7Gx#rrcv-S@U zgDY%)Tx$P`IU+THNN$6Zv6dkbM97Xf+xZV&(ZC~_xGZKanM->(Bn!E)fa3l&Ruf!O z+&jnks)CD7SsK6hn6cOilsRH$+BP9mWCx^I0VyuP9`23#+WOhgd%-U$j3+<-Lz$#FC_*p;wL~4OY zP)G!Vn%m`aAa^y}G?lMS0?4a~21(l#boT?}0>4})p|BdHBUeyINA@4b-{15#CB5}O*n{&}9T z*v~stcj3&oPN#pb>m*eN=yA~|0vJ9OBL3C5DEQln-|67x zv{Iwh&a7oKUZAaqb4~KKSfYPGn(Dx%9f_mdAqHxQgB%-7_nonNO+`ISs*(`J|6g@{9aDa*a z4EGkemusNLMMjGc9#WZY`U&}@vg=LXo7QAC@al(FvUDFJHDyR?vvo=b!Z_qpU0k1# zi|j;rLkrCc0ETvP5rwZ*yO^{F&DR}UU&1tX5~oV;|IGv-6;%e^P};s$NSz!m$hX28 zi*~MS$UbuQjpSC}y_RIx|E-g1pyOC>$VF*AMOPDnjB}#fu$%c1G(OwccM;0Lp*~9Z zB9B5InqUU5#b_?8je>N$hp&r_ZY-0MeWcT|6wFb(9i6KlcSJ%y4A-xV<$Xc-zKZHoY?PDd>&ccUQF z7M!eYQYZutOhQ~H2lKGU?X{H>Wj8KGYUTEl6`;{ysnjR~$3ei4r;Rt-i7YEPG4Xj8 z6c|c>a@cwK3>k7B0 z^(FHMX3>a`1TZoS>lz!n%8;dypT7|%Z|w^MB(K)%cJg2sk{kd$n+yB%E+1?IKfpiC z<~z`6o*%;i4f3k+)o1ii#otp{OBRGG--=GPqt2b0ZRC$#J{TCukMl}Oue^Nrbpa;f zFcgZf%Iw?0ArRlE+qe*Vhhzz0lR^Ic7Z0M%7pPw(+%?b&8Ql;Q$# zK?%0nB{)nV#iuwr4qt*zb5LKlGb6>N@DiMF$Uhw2b$p|+;6J2{V<~r5ByAmYCTC;>)o3T56B;ZD)F7^i8o0%vYdu+^8 zLQDvz5&I2}v!MORe%VKPUK!Ea%o-DnM#+#-d8ep;zAF>n_1HNWpbG)5LrL=oG873% z?59NKV=9|e9ntAFIHFTA)zq?*&D$f^dp`@I zj%&^zlj14kbLiS~xE_ubNW<}FFs9H*{|wmlvr~$k!&9E>%G&9L4@kQ$g0Yyc9tWvm(Jn1^ZmbZAlb{pO7}^S_=v&%bQ+){ zt35zFa`>3C; z4Ps!LLZQngBsbnuJNdQw<1-6QaLs(m{VsMZS(f0?MmTgP`)pP_VvuMOe-X)xE1#d% zjYCQBG3M=MA4#lDK^27V&LZzWztjWfAbl-$`A@nouG+ya9jw`QRRS!Ye2A={Qw3b9 z;VM<kRcy|sA4B)n5GfG{Om=%%H|+Fk2a$P;vt4#ll#|n- zArY+bus3e;=W{nxC4)b|_fZW*pv@6D_JgOH4lge(DkP%k`GMAo)dVJV=>?rUNO-WY zKpO_WE95s)Ol8G0#vvfW)CW%b59pO}5GQF77#I(v#}ylvQwvpW8-60TnaNMB{2=8l|07hO&qkTT|p7NsM&( zt(&ZTsVsLIjMB}lgFP1ZH5NQc8a6t`DDpWY+V zIw{7f>Y@(#Sjz&6MP1D|(+;Z06gl5|LWasLT1LAdKLMe$!}iy>ghpy-19tC%AFC~tI76V9t^b*XbFspD9GF%QC= z67r7FK+#D9g2Ix7;SiX7Hg^XM$Nu`MT|4hDRN$Vb_n1wcC@4Mvh3Fnj-6wY&HH)-8MH>~ZbE0sfT$ z3ELkMm4v|7b*idThd(Riu7Jk|&58G|6HODCkK?UQ-pr2R(sCof6;{#FT1cQf4Zx;m zA5rbZ9hx-te=T&D$q=pXs@q)Oqnm!g{`9rD<$VQx;sHpV(PEi+UxwsR zLJySsB6T;%`CvF`CZl?1cYXkFe}L+c`|d&Ev))qqOL%pD#tU6iEt739KJLXOxx%Vh zgY3=M6?oiX$ro@>*|h%H-omBNc_$f`Ao5Ikc?|eSCEa4GhHz190&ZQrK%Pn`lLPNK zce>zj-^0YDXg* z7me=3VUYiM^<|-bM}gg|Xf07#tDAVdqSI6`0gx-VCUVb4TvgEjn~9B5Z0F+n%#P=! zgOZS50jQ@`47*^*c)a^QH2U}v?~{g5zL3ej-JyArZn-`Q&?n2l?0t$E$RCX@e&=uK zMuFgRxEAgQQpkZ9AcX^}>wr&Jw)Y?;qRh`$SV!oRnz~E1lqsE`gt~QM=l%og)NP3! zz4cPwy)|%;Id5F-mviF-sO8_PciC_^H^hSdLERmjgpT&ryZ?TDfCp|S!lfwt*qcX@ zo2LT>Ak90QTso}(t=u+*Q4(=w>PI@l!K$1jUlF1I8z7~-|5a)N5$XqA?^ga@r^k1Ky38a0dk`?ldplWdF-qtWR&KxZp2s9qlaAy)%K zzDb#~Fnm0rq9Pq{CV{=EiBY`=7xLBXCu*eX1X0HiGmHxsO6<*Rpv;Vm^PETq*d;H! zEX(=?=eiE#@SN-u&{o3{~i-2lap7!%^S6Ox)fN@6u+gHE% zd9dMX#XLVGQ~sm}_$^;}EXnJBz6oL#a_8q7cl*^_cpC=?j#f=5P=G4faGDGTohA!U zb5r)*gdkBNzjC`0@Z4f~rK3%V(bhy!1iE9uW_g?$8E}JbOx2kZrn>nm{sHaP%a;Hy z+V;Vr+;MT*qI5KspBe3<#j`HP_sCnU{FKKfWdNjw@06J0)h;PPZ2~y}O;Gd$rIPr)*B;-f-g6T5&@oqIzY7JIMyDJ>*<=RQ$ambB+- z$zvfTj>yv(xeHh{;n29}E`>stD#(N?=6m}5nYQp5P3O3WP7iy0;lV6>R&&q=g-5X< zZKG>dyrb|O0#Ix`!hZ@pW7Q*gqezBs}O!X0C0N7>5^w9|Wotv24bKrw1?gcAS z|H{h-wiKQ%DEC@!Z>pRjlv{&h(Wp!~n%dlu@Y zl1^>hJeN^j+FGG|1xnu!`*^4JUWCRIKT>k9R@~_AGG42xEGqIZ(XiUjyif$$@T?%D z?)>RD>Bd$5NOE9C+}b;8X>R$_zA(){CAtSx;GP+uiov=LRPE_rfztEC;!wQ~t}7;J zi%u=12;T(FcQVYU&8F?%k=}Z5lSR(KJERru;!ugFp=t^CYJ2~BYfsk7oxb4G8|48vGa>MS zt|WE>n!W^5Ss71;o=f`#QE6YGegZnK?OYc-vu6LAbYFvnh4A6Nf9xX>XL{`)N?`Eb z$#Dh8Zo0A*i=suC-Sz!I;7xoVW3iSkFQupvzk9byViuNGm(FG0M4GD%mqKm;G$p$- z6PCb7q+4aQF22#FTE4#N_F9dn3TJhn-< zHGq(Oa&4Oi{Kgv2I1atgu*VvAazcoWo8onYfWiB)GylXR)zg5G(y~8=vzK7C1C&6zyK7uU zFCIHl>DI(hucrQM3fzcyJ9HY0RKEaJb!)2zl7&miuVquIV1f0COEo}Of!mkZkvg<2 zhCv)n2^4^GKt#U6K=kF{bNHcbKd86|xhh($45qiXbszgk->Nqv=dkL)Ju?xXpZv&` z+8`+RwG`ofv_$|0`ojWe`gJ!YP-bpX^e4~C`rp1m9a{ew&k0*Y^2fc1D9DvSrXiUI{6aSC(SA~HWRsDIapiju;N8aYd@tbHU z@OAp(ug66xw1}0ok?WfRD2wFO(FmChR=TF(>B!H-+9=_v%JP0?Mfv`*mqpjCIg6O*4V`u_!;Kgu*^ee@(eeiyx z`mCVOuuC#N_nw|~SK?hVWsG`*`|)N!kst+O9ejk*oN*3%_xgK80{xv@V3?1A*@T2d z+&Qo>>jCrGsco`8U3MHb0H>d&GIQSe)aAy`h4Kl`X z@Zo-vQWd~PZuq`~$eEG%tO^(R)TCMIG15KOufX+LDa?`4;%{YZegNHR)l_=gorw_# zew7kudWO}0nB@_u)_eX2jmi*)+GP3p3MgonG8HQv0tVCOA0W_QLxfsz&=BBKUyurH z#Jb0w5tE=7BxXRip)2jAzGQfzTkz53F^`pFTw&spQRhP+d33EpIoRpx zS$#vt{X)vy3GbT!{tx)z4?l8w6-rNot4))4LF`n9{Ggbvni`(-G$=k0Ai$B*zn*;* zB?9Kz|DT*ffT-^djoyEH8AE>6KgoB^v>oZe7JAsaL6BmFh2UE!pIU)tz}=SgL8tvf zE-=j;W-)R}c|`FN~DBYU~VPN3afT`xVkQGV*^F*i52v9ry;;b{LV^8Qhi<*>S>%|!`tEl1o_t`C*|3%Hy; zY!UB{3@cC2?+6zuk*;#QPi|rl5%4+N6srU1)lZS{ z065gU;%?n8bqj{@TKHA1<_>(l!O+lfKLtKLFsrQn+vPw=1ev?Mb?OLoGVpHJ+N^)& zp{Wx6-1N~nu|04n0}ho}Kf)rVbC0{E& znm+Mx1pPnlefK{V?Ek-REi0K7p~JCP$tYRb$H?AE3E88JWYukDb{yhH*&{nAD~h`^ z4w6j>w>=6u_Tf0+*Kxl;l>izUactW ze(y9$d-{izFNFK+KRM�h@5TTek0uCML}8Hop(utBNv1PW3+`xIo#7&;TwWEPtaN zQ{Os$Da0E?A@jrJ-QSA!P+7yay)eQE^jM_=uQAKJkCR3cR+9heDEbjw;jD~@Bv@y@ zYx1|+Hb8-y^%dqeI*oEvyVbUM83CoUd^=Wgtki40$z|Fa)NB7}f%sqd5k|N`p({sB z0Y{E({TpKrau-^2*SYq#a(`+as*QnIgbf2RQ<&X<=6NAUGcw_um*A^XZe(`&ak7v; zvkXXL3gfZjN{HF}aEa{Q=5A-b2hv*Pt+V>$qriqMQT6#JXo=;pXa&r{h@0UYBkokM z!WF^j^_6>29Sc}Hi1eN~_rj9pf~uynHukI+L~=|D#LBaH*EMP;!y=9m<-=?f@{tvtG3`1{+WZDv$~`Hh?1AJT%8Y3j$@0d) zUqhPZ_O*+AdB2DvoR1-1?t5O)W&O+^^z6gyLY>z~qAB@3IVr ziR@c2ZnGC(M?dp$A*i~FNPl>fhoLqH%}%Xo%x&kS*600pjiV8&eJGxy&D&aIp48N*p3X z4wc9f1YB^qYfuqn_Cfc{;$4O6CSIo#0Z0mo>84%Gz&NF=35uRDVjsr9N$!(IwJbK= zeKN~!)h{$Xr7Vr(urki~Jc{woYJ^EbP#9A3L^oK=m*t9w5>{B@6qB#02fkKPDi4QW zlTne70A|6U`wSiSHJ2Lj!bg*8LBAx1l*t|5K*v2iU=ddId8 z&#B|*0QbDC7z_FN#g43DNW1e#JsyvD<(nCfGhU6b0{tYoP0NuRK4T$&#;}HyuFQ#d z&DODzG4zi#12=R0Lg<}up{~kM1asu;0?!563R5qhNYJ$>)+cb+J!xeG9T zn#$X%M;Tu*Zs)vZj*+Agp)qo6L#ZH}$x$2KNOV{%8I(98Q1Zu{nqogYBVEsN&8f`q zj7JLGJE62Q2r3JSYlz-Kt~XJZ-gY!};NQ^OE@u~n)V*&V5PsX!7_wyPdJYrf3I3&( z!91QZ(bOjQS&Dz5tF7&(%8A@a1pBp!SDkcU~4gJQs-k0FtF&o|3r zSBecaqz8`=$NgHE`yn+$RytEG7y!@{rapY+x7K%&g5#7nU7zq7vfRKZ-FTR}2JJ(M zlhQ|C>nwFyjSzVqO%pgB&VT+?rv@DFkx1V!jRqKxawx>K{=R(eol94214&Xxb8rP$ zaMD@^@5oodKGOYu3erKCFr^7By#R!9R0U%tND3$eqCmE8xxQ8q0wY0$-J~ntwfH(r z!iRAW7v*JAcGpU^P#X9Y>G;=bsyAH!cq;Th_;B}tD+r7V@kUan>5in^;KkuM=A($2 z-S2L$w?dYnn8ooz`BeF9^peGtorNFq>m*Sf&39|05}HWA(W>I3{zp9_d&Uqzeqzp2 z1{?1rupgaa>H{1WJ1R(06Y$4CIq$4OUUd)pu8fB!w%a8M{(lb z!b2J7Ez(&kkXH|7J~#~00ybB~(A32h9;xdZRwq*QW+&=xAX#%UL7}#d&kkK1k_tBP-fc)xH|Yhy z#T&9KNlMCf!LcN3r}!#~KN&qQcmDPb7bXeEo|bC86__|UUt0*$b($l8>@wT8M^!4T z4}bm^SMdyPGI{qr#$^VWoFc4xf&_NvKOUiM^sT@6$NS=}Zyr0#NNdfbghs2UufX8C{5G7eVg4N3`v{0sOKsHWNP5Wi{^cu`?0fN zurEuS8yIkkN19uz8B{bsdp;*N58ar%McVWX45BQ%guG)1AM=$=c*k%v2Y!{NcP)ee_dNoY~pCmJ$zIpL)-tpwaXiY{M1sN@B$q+3PeU> z>zXHnZ7GZArW35mxmTcy-&^Wd%xv%)YR!o9^m0SOWfKA3Y%rgMMkV*A9Ocpu z-iP>k&e5iu29PUkfePV>TEbaBs%*DFjqI|PM3nh2-Dq4GoxRs9m!it{cT2UwX#|zj z6#+0p`-&-qm>QmMLy}cXsklKW_r?z5v(+P{gIwKR^n-OaR|UjJ0aOdTvO_$}fX->DaU-j3zN~7!WN0&rl#e z^YHlxmaI4m93R758~nECdMD&X&h>s9wo6fE1nUzt(&z_(L@uRPg*_$puij+B-1K~# z@{2daN}#SU3ONNXRVt@FBdo9KiIY6YY&N+zNJN1iszE+E1BAjOR=7v^(tcqyKd&h; z^O5}vCeP%;kjDSvphP0GEWiEY^p4-2?LIuLoT`8B+c@8VH0*iQVzHIOhb)f{oBqF$ ztkI4xF8xK2!T**qEg{4?V6EiHz&a$G1tM03ibrj3aF0fj+nnB}8y$+Jc{p4~jRdzI znF#3??-Rq<0etN^!YZr?`g1!AEY?jL<;%lTO zSsx}RU7;lOv(d=JBO9Sw|wZZY` zt3&oFo%(p0?3lA;5h$)f2omodIY|*T+iT-NUage(lp{HYEwf&&eG9?DR6}r(#5!S3>?oK7IA!J%HK6?c<}tACVL1;xs%a_9x@Z zU7iQi_u7nr3@c4@`bGy#E})8RX@Ba9F+F7nKnrlL60Hw3Vtc-5GDd-VigM^f2@UPg zSpZWL4V-q6b{$L&s?V=~+;i3kQ`0^-) zfl3xIXVgi5qQgE1$~@7i`SI#h=){vdd*^UD2ig#Z+2Q5Bp9UwKmWdp$GkL}c_pC_W zL)oE(EYj)$y)Mphp+AXAu^-oqJo#n{W5WSm z1@>H2R9kAA&bR)}iKK9E-(1q@P{fMu%nyTDp#uiy0wrbTaj1rD6HH@+2?^1M&3vJt z9q2kzueP=$kpmovq7m@V3gl4TQe*^9Ip_dgJ^g4E-`QPoxC2D<4`o9ma->UjNKera z_~V9}M&Xhb-3Lt-;)77&w6ftl^0_l-dKH`p67BP2q)H;Onf129Qn~-0ud?iiQ9cTA zGdiFA*BhkN=@Q@nf=i$@wvydpeSw z_UoHGEWn)h2udH!qm569e2m<14LML=yZ<_6^21J(8b}HYIilu?h4gJ-SE12C z8@vth1^}Wn4xMxlPFzm;D}^d|yB~Bk>HKBo9O$8q!tRgLvY`=;6Hnf=K!+ zYY2o&jjVh>4hT+`usrf9&heMsUZ5)XqhH1i-LAdQM1~BW#bCoglyHqULgIz`jDhcs z?FE`|Tu^ZjcrH_?FzB99JWK%8lt~U|O#-IK$U(a zIfAkXXBY5EL1Mj=Xxg8(_LfJ!a>+BwhWFV6A!nY^QsYop&AlbJfWRQMs4y$?1QdIi zMmB0hkr$}5RRzuI(O*fuZD}H$R34K#mpQli$-;#$0zHrg$IVhVJ3U&`UXI%R4)Csh ze2sPptRA`5pM1J5#Nr5L8Ox27;s&=B89@b*IQOWgQmIe6YSc?>E!#tI=V53WZeO?W zPy4epzQGH>LY_N2Rz%Sh0Qjonn{9^DW(v5&Rl9qWKaE`(mOavmDn zq8AK~-zG!_-Fl(xLQVYm-87DloHOwR^70CfrNW^*4T~b|2qn)AJ1;nP{=;Eu&-65w z2RWD-;(P=4&5yKgU!sar+@mTR`k90V-$2i)x4M+r7ii-L@Xbdq)6hoI+_&C|t4-Zq zBa&GFbXf*@?`R8KD1FJ7*y0s0-E;9Y$_G#_k|oXY#4hiK!|62A#g%uja^p;IY8Ib@ zWp-2!m)({bSs}zY5*}6U(_bQs8J-K*G}yc1(2_qO@C6uJzJBMFueSy^7aYM(xgnfE zH^Z>tn9K5i0VE~=8lcg2wt%iB3I?|S;!kh5A7V^82J$)*^a8#c8e@tLZPq(l_!QR8Io~BAMAX&qiEiz+R)4xB)-n&07>8;aFy)^ z@aKB|!E31j+Jj+&-6alj<_K`yokk%5;D7yhr{x>cLZM$UGYlZ@*TogM#$TJVgiSNXHqckr7E*6s8 z5}zga3LwaNMb=%5zvt&tY)B7}^4KBVTqaG2`_s)Ox$)}qYKr2vRpnzzB6~=sGr`ei zK&w9G^fTL3#AY3VC3Om&s`?cn|rqh{qm6al7yg1AmVex8`)i&?w1~TeORn>Bhh}s_qzGuJy#HZLP@+W?IpcufdDEWIKFRwL9z@CPcDy1E+i_7#<8ytxlr`8FV$t1zW{kTBM+|`!Y6( zpR8qi6>BQHSE^Yl!0a&jloi%Y=hFLT*S@TdukomwA1m(;Hul{lp8jzf_3|M=`CJdM z(nugg_FLJ#x$d~_2eD$;hsjUFA_tix&&-ffx6&{v2|1T z?b`e6V1hD?gb;c=ZWeNpTcQoillV7(qBY^MtnW1_?wEC-G;1#10qi;r{#ua3A=QCB z%sex!dy)BtZ`aJ|h!Plm`5PA>9KHbH-&xAS`y>kymiEh2Yr)#K{SzmcD*Q653Pe4i znvcDYXTOpXYCXI%AN5<>T`1%_W}=}fqzmkG2Zf1YVfF0SIQ9c70=a`_x4%{pCm|;V ze4n~Lld&DV-oMr!Pe5W6--A~n<(*^K`Pp7vBc6Ukt!zm3-%kM$&Ge2%nC*O8c@=Ms z`RCx1!^(HA+wvB@b6if(7%qVSeOzR_m*bpO6P$HtYS1QOd3D z@P`j+m(Q;shKqUD|KceSA?8oJr~2MN#O=Qg@qVpK;T+Xp*?G06lC;nZY66q@FM6>S zkKu~Y2KPVx9{T_nK{cg5Z&+S7z|X&g4cwU1zPWQVonnBt@P0~m)Q#<($?}kmtg)>f zdf^P<0X!|nS*cD@9EUa@`1i$tlk#S+jQW(mKCZXg#0=i~{z?&l)jYPX=GBQrzBj~5N?a1@~%6`X=cw&1~saHer-Ny_V3}_cpCdl(k ze=mJ^lC*C-ggfV?`*2g#*cRoNSai};$Q(*g-Z6?f^D6$e`i9Rh@!d|g+MlO@CW#r) ze#R*idS&{jf;kFa93+10oxE1}a2{CER-6R>R%{%xCQfHPPMV7+e%jgqZ)v5l{SN&B z_M{|*a^Iy}{2U|tQv5X{f_=|2?;2wOOXBg zAw??7;4H=+w6tPAfxc%U>pXrv&lEo%a%FFeda;u3i?Fw&TjGh~V%~X_O{K@;;C_a? z<4tr9$8Lxxg!6a%0U!B$TlAqBt?W&+1Qgn`&wMn6;O}B{cK@G zK^Kaa?13m9$n3!ITWyVl&zexHIhK#yD**P;utc}8vex#e0qOFF3p`3dyxLIarOqtJ z5zxhUUcToUiAwf&VNyt9_2NHbA)|^_#oe72KSjC{i6+=mKbp{tDi(enDKDI_cSjK{#ki`H6y7K3g`~C^r z%rog7z2<0qBRtrm^Q-RAHOJ#sZ-=N+NMsoN)#!w{cT$24`L9Y<`kLH`o1Q&yBBJjs zI{uSV-9ks~WIGi{K|3Ubhzc9%AeUDwk1taRC1G1{@=%0VscIfU28(=dUk_RY57Ui> z4{x9RB9gsUMopU~t2xQ|2^zBwZtTLH$JR)VUV~{#x*W;-&%J9H)_EMF~Aom3pMvLC&)%D*AEA zu)>82QH?oswP|}2x>n%IDM?*EPGE3rxmt;2=7J$*JuRR^0Z&`dAm62#2)PYHwH_(G zc9qmQrx)xGDT-&xU-=*YZ5m~?ZI_qa4il@Gvm(CDV`=vfbQB@=fZWy!_+&;TXK(WC zGq@oXb4>V*PQ_{MGPreVuO=@++S6h0*5Ui|e@5TCj*FdB`Y3tt=u1!1pT-IR>qoF^ z$)6_`9ho=p(1>MjcK=#`Q4Z;gzO^5h$^*i=dMRi_9cW-*fXPqyqh=eet8`y@@2krh zHj_7%^lUFZ)qDSW2BnM*n8NeG3ILeS81PGHBfRFfe!DD&s^`BlK!!FrTvW_C_NIuB z3I#??otxWzvL|}!bxD1)&ctRb4jjezrROTSOtQQ|j`r3oyR+x)OCNXZl%nDjnqlTb0N#{1rAF|&SKyvNKlIaa!uu22#X{eL;Xa`bAKYzL(DJ+RNWOqpu>b9Sp-EFG5{e6Y}SGt zBOPYJPa&uB8Z5Au7pFdz6fPAI3Mv<0={$H753^dSE1ZB!;rj zDmSAj{ZA|PcbaPganqdEvN!9Q*=(((N1l$q(YrjBqGwLw-+wzzu^Cd z@k>giv$p_lhw&tZBPx{&B#(9yDPRP$54!R25Eb<)ygskcSCwx+)?~Fq#Pqy}KTf+p z)_wS*!QuA7CG;8py@G*fDmN+gBV5CfN6~uA8Xl|g1^VzWKynw-zf@|>JDV-jlo9ar z@W+qHSAM~ZA_uAOX0JRLN&NQk0|4OwHl5OwV+a^4be z2ui=&z0}3^Ys*pzzx4IVfkR6_G4he5e=|PrOu}u$je9=NtS~KS{#XKvU|%{8%&YhX za0b}xSyoT+{`k7J5pwnhN;A@YT2KKwj|gc0sx_uq(u3v-T9!w0;1N3%N_>Mt$heEw zA2vTV$nB6jnDLhLy8+&oj$*ok&ofHl5%)S(UOFUEM}7$FJ^KaAPapxU{rI)_O(7#i z96=w4r}qkQK-9zvuM;rFes)D(hexKY+xj1wo-tDH(7Rq4DxWZAMu?=x{qCgvq8P`U zj*l=?+-6_#J8a0GJPeUmAWQ|_Z}NUZ@N*XlmgD4emQ+o=d6iC^%a2ul8o1@%Cw3mb zYkHmTV*~t?udh$~5cUU~=2tS-xW)u}--`R?l7cz*DB&eTXGV;A{Kb*M!Tg!tzmpU%S1e z;ji6IF7RfM-5~wMyOk)ti-CK>3l4IRANAgs*XHczaxZf&IfHt}Co_|gr1EfceLM`_ z!C@Rkl3BF4X;I+C77#>=2L020GN)BMHmHKOX?J?xZa4qjBuTrMx$l|^$tz2o*eJ5m zs-aM|7B2oOgYb7;IO%-%u{b&7WCI~C%vvj3r;OT3NIfesIC|)zF=&5#?Z-uJL^UU) z(XC&1U(m&HuB4I@GyE0nuJ323ziae))cR4TFg}*p-aT=)p;a&HOV{?P@(;w{;k@0M zOOJ`og!hrPzN?e(-np!;ZiaqbBc;4I@rzKw?5N4y4+z*_P}q47Pn!Wz z8@8U^_b)|#Y)PkOESpqufz`uZMg96tQS-M-(gm?NS<{^W5BQ6+ucGz2ksZ6d*nsCO zW+^X=%TH3H@a{`~VHl5SWskoCxAQ6wOlTDM@X|QW(wT5vWHCr zRaxP>^mS#+j+oXTev?7?it6X+91#`om-cdm%L#I7@Q@y!Usc0yX3_K|FTIRq(g(Co zpM}%R;z#=&yB7PP{K_h-+V`0S7m_5_C$p9|8ev`1Ume_SUpYj#OOc+bpKcf!;^xkfKg?;ZXbCMP(4Hs3x2 zUm>SG^o@_6&nQ30`)bGI&3if1!o1>zJoPuIoG!Xo30N?sCe2g6)Rg`+?q#`wGN!bw zG{RLM9hp?WLh@wRuTf9}onCd(qG@Z?raga<{#`114 zad%^qvGJcRRW-Gp#Cc?8h^i>ByVI8>REj!(%ntfw7$zkg45s8%eNCCfhQ!c{GzA`8 zlC!^O7sE1|=VYptQR0#L)yN@V&0lX$P?nSTUEg3rYUDeN7e~~x%`+q{{(M2Fcues| zQY2oez9q-b$Bt^w#^SC8qmzi+>{5N3`jWy0_hioo-sQ=K<(+4Jei~+4!)g-^&4VdL z_3EQKG1ysxe^j5Jjl-J~>91S@2yvmccpc1139+~>j>>ihyk&iXL=FoB2Yovqrd}lU zL;XQXH+3y4i8dsqPBbl0U#M%UKB)VwAkzO(fu zT-Bk>8kI1T{J~u!7*G@6 z8Bh^$Z+iJ>2E#W*WgJGjo?gvN&WlGi2K#RL)TPS&=Rx1?D%GZ!JNvh2lyS-(pK(n2 z0*{7lH4C)l5A67A4EhY4wg_hXL(v*-DobWsJKPtdZoOQ2HED=TTYk8_U@=*(Tv9Rl z9(ogpNhX-^@=TFSPNj87bZWIkU%|ebl3;e`nKfD7e?~oQJTHw+& z**k4mb(Gl6QSusnk=IIAWoI7MCk#EG=6bU!%$t~(A0OsMT}?l1octb+O>~s&!zDIe zWV_YNCa0Wqp1g+F^5=IQ+g1EYjJ2gYFS}prVEoE)4ZL<|>X~pqHdU+^OSBXb5(~fY zi?7gpJcT`|G(pex(wiq$^;QV+(s|GCHDsYv5?OlJVw%S#kyjJwN@9vx{FB8VB{ja1 zOw>iX6w_8Gk!)wY|Z*mAuAw_x^VgP;$uw*^EUdV z9zP8$XG(t-3)k0dyl%6D`5&u{B{)|j4J0yQOXl4SHo#qU6;XEJ-KaW6ckm4JzJ0kf zrsI{55l*(_%R!J{ffjo}&v!0?BAk%pRi`Yq3*|G{tl}$TBRxE0UA@P*Ub@$DuZ4ZklMo)ks*U1w?211yNeJ`K z^qNt%2VAncjWhPw_|j6-Zr^Lt^WqK0%Rh>Hau|Ca--rFGHo90YVrQLKS#G^)qKx&L zo%7Zh7-6*}s_tB5~0; zh(&O8Mvm~!^d}|nqeK*z6^q7yglA;u@fEt9TGNN6C1+nFXYh<)wx0=YSt*lV30OQX zIm4G|r_fszxAzRQz~`Q*IzmtiPd8x0Va|;fOC**CW_hdGX;8!Cm^-)35VEM&Io{|% z^S;s~_ea?tiRISQeuj%EL*}7CpQ5gB2g&p>Bp1{ajmi)}Os9?jXI+UPa=; z2sSD+QDEdgpKh9D5Mk5vTU1^u+AJx={(5eJIm;5<-be|PZA^1FDoLcUG#+cn=)~%- zW08w^V3qOPggPPo>txHu((?0Z(Z~n+c2`_WDg4I~iuoYM)N^+C#pt|M)7trY1I<~m z=j(f;J6qtJ{_)9Nyfm?4d$P#w)ZJejBAjdn7mSTng$` z#cw9X+4G;>&U&*cIyY$Vv~ts6$tqg;M(wYTN^y>nLx~3gXA>Ips}hoXJswijzcEGKCW~xn{R>JDSup4iu(LL?) zxy>7@8N4x~jEehxN)v(zJ%)P9MeRI<9_j}h-V#fJ&mpKYk2p%4^7jcE-#;1btaFFwY-7JlhZywNCh=Cmz4$t#>CKWAEBDw)E4N*zwzUIyCMYPi=F`iC9)cy} zqI06??kKd3aWr;yuSC2mPBM99+5OVhq><%;Y3k~v9Jtj5_Mfh5<11W4(l0{KbTAHm z=9V4c%NIWbdWLp>iTFnEb($iaLnW(P`>SyII<TbDVTJh3L!2E6yNNTjNBM|&kPGYCAhJ;OlqQ&K(5#;{NSLksW?rxs9FdC1=n(@20 z>%(qT#v0=*B$E>ks?W3=`x|ZcBS{4Z7sF?lKEp+PwO8$B9M8r;ne1;=oJRMiXn1MN zz}#G3Wo+jo`$CNFy+j^wQwgh7As%?*j=ym~YF036t?}#4c|o3fqCLtE8Ioa`kSt+h z*@3}HXy3QEODvBkZ#~2IXl?Cr1E2BykGkdgzrv9tI@q{hpF~+$)uw9=!&}h5a-%!~ zHGP>=RTPS>Vys}NNmV7TZ979R`TcDayX5#Hh{mmqn1=b7qM1V&VF zBjv?Y*S+S#JdX?IMF+xb@vgoWZOv}% zf5Xg%ul?e$Pk;L+M&f;uBQ%@R-&!WI#prhSIVTI^vk&wWaxKU;_0#D4O|WEN5+ zaRJ%hz?-h)d%{0*mM(F}#P8CP{P~rJZJArS@OW~cS1ScAgs=b?1aqA54SDPdbM&sX0YyH_p^l24~H|klyTO(^EUCt$T3uO2(I$mNvQy z7mdx3527^weRN}}gqY}D+}k!#RC>E|y1Q(Qi`ph`rWT6mP0QuKVOM-5#1j?5-&ad( zW|Mq#hqVY7oMV*xc`)UqvT#%bP+j*)YYFv9SVkqFu;;O1QELt$nz6Ed>NTOR_k>w4 z3Fl;{e7DueCn+r*=H`qklI~*~^H-gOZhKh_e2+9}H?-)-MFXnSJj}H{3G5tE`v^R9 zYSV%6jA5lyXugM_3tLUX@LOc7f2Qpu&NkMPmneukWx0|rmYE9xe~9g@Kvq-}9{ zV67?bdZ^XTJtzKBl4hq;AH^lG=ZNd;Ft!FW=R)^nt|}pUDjw{{vZPk5T-1Js2Hixc#&yy-1i#RNvoe6ZIzpbCN{oIJ!uud0DTjDj=j;6U=pxV?rdRzhK4v zZ-mJUXgT~va%dkzctp$B@mBt&X<2al8=NqNQF(%-_79?Wch#v{Bx|nSdfv_KrUFnP zsQ0F|o36}tt535X6R1Z6wSBj@Y6z;n-y^h_^Vmim>lH81vKRTX2MOG&Ak7Q zbnE3MMg4FGj5tiq=S`1Iw3RwtptshpQh4VjQU@1}E=5#dZLCWy16a)UOB^&G9^5zkGDsV zx?Zl)Snwct@RxOVR51P>uZ|Qf(?$Zm0B6PHfi6wp>`0Cjlu3HysfaQLnfd4oB_3-C zdcQXBoE(kYn?O*!_AAo^PU8cbG=DdRb_FVysJ*B~* zE#ZU}G?HC%&ONO$Kd{CqIHSK-M-LXn>|07>{+pHvE#L#~SR-zVx>OOC25l9KdY22$ zVu*RHC7E{!bS8a#Wi88vDZwg(jxV&D2ut~s`b$QFP3Nl20o$z- z-bBD>uHw9j+OVf}#g7e*((O9AmDgE6SAA$9mwvzZ%ou#rRwbW*%+7dO_)?y$pmXK_ zG&xCcjDjY?Nwj39_;HiHSUrH5>rIm70j0_0U`0Jy0T9DJ%kg;OsxU$|D_Yf7o97{z zC+4A7*UMYd5zMz1tq`jqyAPJL&-*X~(J83yLhp{6#HzCk$)gCBFVG*tT;)e@ zZ|^39#z$$M3Q@84yI50fPPZ()8N$3lWfNBQOYXBBm? zGkytRU9nt?hoYp8I%C@#Xt>n!VChBs`(K+o@Pd}x@pUfy16mhuJFGfXF1nrgU&BoP z&rD;ai*(8eM|&yX!y@!*SRut0Cr{x%=*987q@V}3_jYbTBCXY z=?2@1!M_{qKMP*{T@U7a@Z}(oN>b<>0nZD?)8qe7{>&pRE-$lPu~pU3h?GD1blC|| zIUeT&p3-7!^(IsUo)QqqK@CY{ds85%gd6ZTL+a)BrD{xu$%J;6AelS4@yhItW=?)V zW=x;lIs2Xp?~xDNb+#;r25$)XDiLMFpxH=dypVkg@-KM&dL;C-ZAh)5g7(XS^SXT7 zJBFe0@&Z|pA)WFKCsC^cIn-=?t+rTPuao>bls#4BubF?8Pge5rLLoU$Qx|xd{G9&2c#%bHW zpnBy+1Iy8odlih)toFApBWTc*=flrpFz}$(R&21|;HlKIZ;CpXqh^%VXS0KeTZ;Fd z#zt}>_Ue=5@2GnZ59cUYD^gTiNFDS0lJ{e?!Q~3>)fTT^Ne638e54IZg7FZ=gum>C ziS2y1=@#9{p{)KnHD>Xh9Wsz9-(oY|xFZE83-4$>*fTQWva68|A;#onN+HH>*A_QQ zS(3YYj-byLIU`%**{Q+5^zO5a@q~ExOv|}T>w??_Wo7oCbt2An_Z_Vj*LtN5?pbS4 zf3z(S4VeCR?vRja9E2VjrJ+6Cvr@Oa-|^g4kE7R%p|RH%S9se0{A$1yk{Ih^&^-El zu|%~!uX!#ZCsw9wGTf2}$r}@%E_6mVWPHshMO2bAKp?<(H;&I%RHd&#kQ5kgjxAcv z5@#5*?QOa!!5G{YrJR^E!%fg{O834Ume}{fNYIA+r|nchbm%u5X=86IwMmAN*m~aF zOzd|n)}X??{Ho!442sYjBQH$w{AY`aH4A4H7OUODIjc+8(kR9_7Pe&`s=V?}fYrVD z+TwwaRr}CTBtj6}r*g3p++9Yhn--NQ#JIMP+_un_-s`hX`qN(_2`8zvQ3LaNF_+%Q zBbh!TwoSF^tV%DM`CBp707296kd=M6!NN(CQMTM8F+S#P{)e1_xxwlT0oRM{=ZNB$ zcPo5+2ML;(bOYy+#64J*U>0KdqR-)vgJU<@Woe3ngld=ty#8X{hjUXGRjcx2BP)bG{JhTy4b}B%XI%@T z`3yezqFgTS3sf={lIX}OWKpDtk*jGhiXg%Pr)+XZnMQwBP0>cAEc zSNc7H(sLnd%=}|QKTvk6H5sfvNa=FMOzt)7y0OKvbB(=I7iVoQ4A-jr4R|WtOIPhJ zssw_(dW~$d1jKQQHxvcGW?`e}YV}=3zdWhnWO-rtGh}9nwZODuf=#I4Xvn)M+ui6y z?qyK(w`=bHJC5(6xQnyn3G3$CuaWSAwc9I_>OK0)%zLWeO&eJr-OrS`kPu1uMAf!$ zMc*S0iFTv`s-R?fTqoBa&828XH7;tRV;kE~B*JKJauMSa`sOXCOBVeXvu3p2qoUus zNnYd~k^O9*n8Rf*O!vVHdv3|9bj$M$-hWuSPpS3Gi;yba#>D}1ZU6I)XQP1|=R~`o z#pX}iGWEl9TqaWhMHalY{`5~s)&NS9D#+~BKZ_`80oD^%ATg()&;(kE|NoPM0@pvh bLps&;p2GF5<)$G8{6neTQY}}$`{(}wSz}?G literal 0 HcmV?d00001 diff --git a/lib/NeoPixelBus-2.5.0.09/extras/curves/exponential.png b/lib/NeoPixelBus-2.5.0.09/extras/curves/exponential.png new file mode 100644 index 0000000000000000000000000000000000000000..25031713c74aa1083cade3de9eb5dd6cebada4aa GIT binary patch literal 43532 zcmce;Wmpu>7eBg$;-Y{OD$Pm>QqtYX3Ie(k(kk68-75$PyM!Q(vb2CoNJxo*ba!`m z_nqbYd+z_meeTP9U*G~xoH;Xd&gXp28A6_`DiRS=5kepkqNi|q1O$S26MQk;zy-gV zEws1~K5(26in5S`4w_}~1HPGziVOtu19kK4%{A~d)E=(w1c4COW4~~k?6Qr)FYh}m zymEeNXX@hUUPj%`VEug9X>=%->tv#4 zW6X0!ykAmn!mjc`E_*EfyX5TP&}_oVvc)+atHOc!az~SqVX<{*(eaFz8=kEzr`74% zv6c@*NSR-7QbQ`_sN{NY58YtGA#FdriNvFU$Uv<3C~N)zsUA_UnfjmQ_ zu^-?5fBEpr{PJZwpt;0q&{(!gA^nAR7FisZeiU?~F>gJtL0Z^tsK zLR>OOui8tSu8-uJfX-tjyPYG9lafMwGdQ#gD&?5J-(2 z=uV^rGZ5B`+3d+st`^qAhrFf-4W+i`6`3P?x9()d#@=;La%~G~%=x2G&|4E*qzm>z^~Fzb7LM7%yKgNVa~5_q6_d9*S(ug> zPavosbNg!sWs$Naf9HtRirQv68T=j3yDlG6at&f93nU>>6+F9Fm3Bs1_I0Ojn|`q< z^223PYakiF1})J{TJ7xL9liJU{vup+MI51qTKVj18hl%py_VT==?(&)y5yYRYe_c{ zdsMhf?54KfjB8?TQ_EV+%^}CAC(BWsw~}yjw~&O4kQCo8p@~qmB&|Y@yn}3UFfl=V zr^a4O(!8zX`+T{llOj8dY@)o>*Ye$mO3X>k(cEZLZY}(N_Wl*Q*Mkg#Pe$fQ(Pwa^hh8ry-F=$+*vk5n63$E10R$#IR$&t<_wOuMWF%kS)opMbwnVO7)n2VrEJS zZnDT#BB3GQnJ_@OuApFA6K@1vluhsWo|BdOG+S$dgLo#zRbV^LeVZ7Wm zCrGfb20uqgfN-x2A{>^cDT+9dpAymc3+eK|i(iKnV)vCOf{K0n)mEyplkCqnwb^44 zzb<3Fb=^KF&M z@@=)g=z3fj;%=Hy_nW3QELIi1H%97&>n8+*;T+w%%+c&iHTa5)^F{PBQc?&O8Er-` z{PwSfT%_flGsjTo51;RuG|TZfBfhw| zSUjnk;@b98GnR;dv*2v3o-fhxaPDbSn;j*@?Jg-P$mLLrpdqjg6_?oSRahuT%AsEL1Q zEJXd~@UM@Y-kiT>_)fk?B1Bj!Sw4y2gYX8)pzr!Da&P$b&9RsI-+W|WSC@SqlCA|G z0=Xen|D7(d{Kfv``^sDno?D(9?rxQ*-Sz(W3Y!we%&xP}+aR&S!bkO+d0wL`>o?iks=B z?rB1%?jMw(rYM*=ua^`}rBwNu zqiw#Ch)6|BXa;dqZ)NqwR{li-py@w}rJsjB+6CrbHh%N_Hzr2eLhU0O0NUJgk9gIc&qaPeI!k>TG`;Wq-OP7=l+ zhUwdlLmrna2WNg0EgGYX^GABpe`ut+L)^=biBwOC9>C?8fU7Az ztS5Sd{Cl88f2(Qn{Bd~QLA=o6DMvZ0ubX#G;r_9?Z)>0Yr7TB9><7Ee3}TdSqnuw% zgyLNE0ZbJh5}r%-^A5N*eh5p-pgCr_&9Qm~VGeTD;ekgPv)wY9R6oxPY}VkNPfHNM zZ9~4E@E`-FDZ~!#yYnim&huY}@vSb`e-kJ8BiCwb=%p@77kR8<@$W#xvoIzX_!-EX zIjlwlZ5HXcEWIT~Mas(Jhay>}kM8HVEyP{A+loJscmdig_y4yTe40P#!72MYLs?D1 zy@QvPp^D`j)VyENW`T}E>kHGCc0uxB+Zcpz2Z?k2_&q+#Z_zAgSv&pUd)L=Zb-}MP z)SfJ4p=A^OoluLnrg};mz;Qq(bw||>3#=tR>y`dZ?!gQG;(9}FgYTA+k7LPk^LJ*_ zZohhk=WX#9<~65w9&2Z0W^toIA~0qlO^o_Atd+|s&Qug~o$O#O2>;8`&FWhl`Nd;+ zRD@2>JxTlnfJHNq$}B$%}hNMw%?-zv3c;tbNU?^Q&Rh024vf(ix1_1Fap8 zJ+{@6Jac!hsbx!?=2#Vba+Q>G^Wn^ijaqZ=9@Tu5(|zi3Pz)OtT9-iN5`(>Ud!fzR zQB9rTEAZ>?(~XVA+?5vUT&*?a$f;sr(d;(O1jEB-pP~{k{Vbwks)|aCIxSY<(4VSF z-S*r+$0#QChQ(qQ<@71Uz6_jm{jB)T#)m_$r3reV)Tn=!h<0BcIBD9L{-tlP=#77J zWdHiRb5}K6=2MGJY3!y>+@3advP;_4D*Q&YfgXp%T!YBWCW6TUeQcJPe_oVqYITgT0DGNPI?dP~FQy2N zy5FM?2|eT!Gau*gadg(HrTC0V&jSgyLG!G3&^%KM0~RWkdYmQ3AXL zYb2TFb0Y8I9_#grXW4!A{^0o2G7|5tbb_(dNQL#tNvZR4BDBmzC+xf>6JAY3HACj} zs)u!|?SQUS#QOfz6{UUIL-8GM5fAS|J0C|47CiO8kwDaoA;A`?g;{TWkTurQcf7f@ zzoWB2@Z^G7k?j1jO0Nm8v;wec8sfQSV8ndqpxJK2k)R~Ye^0Qp^U!b+W}PWVoFt@I zjuP-vrXd#G)K!39qaA?47ezt&D9GHK_-EXS;FO2q@rTcQe#TyNbybdGi^(U3laTa7uOnwZHywCS% zmp={L>aBD$OwF3k1A6)|iNh=x-Ri-o}(R@mcr}y zsqfcF;bx%FUrZ3Fb%SPB#&W$1jjTx~syx2lg71UfW{gL3|U8B~J z{xnG&FRQ$pe!cwG>{5EPn|%G3^eaXA)qranv|1>mouIEZ{2^$FeB_&dz1|AyK(Dnj zm;gA?q` zi(tEsNK~`^RTfOGHtC7htzn{p=^cW)PGD7Hbf|x9rLom_*+dAS&{`pTS)!J=5sJxn zEI;FuoQ z&5A&)y7JqoT8(|n(%3>*kviZpS3TK82!TvtEQ0R}QrG2(fE6MB%V@7bo0vcEDK6O# z&B<8JkD&oxb&6BDGMMD6gOgyzJ?)5$uDpjyB-=Oi)HlJUtlprUMeqhVRpksC*a7>RY*_|>RqXE`ePzDiz2*4MW|!yDE>290=XT*s_;V{D%8&cz4QT0JFSHi# z_d|0;fq|`6XzYGrWIdh~-sMN1%Rjlce*NBE*>(4?k5 zXSyq|YuQE;yjh5IldoPzIm#xicsyjj2l_uG%^5RNRsJ{=nV@6=4US|GN5m%`4-9Et zH*FHMkH4Y*D6XH1p4d^`(p~fcIJ8F(>5|Kzd5+}=Sz~sP#vRg}oSd;p`AB%i116>$ zD7fR{qu%i!zTu4yOE+k~Y$TlTWfkbUnIXrlB^4z4<<=;M^%m-Os(`9f!x z#sR?+)KOxvaVrbG`?jAfpOW)1S;o}IBjp)aa?`!YQ3d*yEF_SCe~$G3=oRnX?fOhl z^YbTKecSx26pMym?)<*u>VCT7TJs(uD`sIK_=$&BE?2fV?8VY!!yJ7MyprN#y)t_l zItae%g*U@w8>^!z)i-~>gLlM+6U)3z8sHXh9fF!r6WMszf#*J%? zk|B)*SG#imYwh1?I6u|=18MY>B>FktdRA3W^>K!jx1ZHWEoI6&Fk|5KxDw3C$13M4 z!6DPgQ)yY7kIwB5;$PoLRC8+ht+<2hgF&G1Dqc2Q7WJb@CW zOpzu!ywIs3zVq6*c#(HfMc0t>Q?3{|gnhFIvbs3D%!Pd7P1R19p?3I@MXUu*NaZPOPQ`U7G%e;V z>}Tc7QP?%Ek>L@oZS&vvT%`hXUv+%T*gVUt7Uk;oT52;HF;dG|p#uKuE5|*PS*)^h z-F;j6oU5;E{7BLEJkZwRjPL4d!LQcZ4J1Iu#Tb(3qYF*Xa!7r|>f$)Aly6_)>?d z6Iv{q>Zrvw40`k1mubBBtLvTY&n0;Rm#Nw>`Rk{EPf}eSM7WTVlGYNa+$?)Yy<$}6 z6g%6!t2=%w7L2&|P|-lkO2}B2q0MH~NxY(GB$~SHkp`BMpy_p+3FS{Mm5 zY<~!Pzl>DJX_=lZ7_hd~9-Bh$WzUz~_5NRbo2(*NmRNOhtlUPq=Q@UbC&xibM-_Pj ziMHX~j-Dm{5@#OY zznTXlU_2u5fxLByItZcy6iZ0f-+667VE!(Q$DGv!d-Q#ob;vO{U$&}1aP#!8t+9DP{u=^Kgww*q5a6wF@#W`JZU2=RXly^M1y1|Pz{@1GZE-1`VP^7h9F$8=HsHH- zk-%0&PNYeMVyEv$2L`W)4Zp0N28BCj%{jR{tf30G(@mKZt&Yw;1>=)@bc4}n?WK12 zvba>?MY9e$xG~QE;ZjRsFNue=1FC}O+2P_*SIv*GvY34L4w`+j+xvzMC&oBJvCg(X zl{=Mo!MwjtfdMAOa}60n9A+tXxNJg#d=!vX_Cc% zrCv)Y!D1||W&8tW8{RXqJ<`e0Ek32E`pa@x_E^^b(z1Z)fDB*-AE7~u;I)-Mr?2GDS8$} z%rmiK2srIf+`X!Odn*#a%Md$>%YsdbDgQ@!h4mb@RdOL_@AHAJ91rJkkX^C7m_OoO z4@?lel$Q&xLtsi357tZEvx3dhJxrDe%xlHlG3JzX_xBk24`I z>Qp_wAEGdbA$m#vsocA5GSaKU`$ZFtuAKy+Znn{5L4-~~N`{^h zy=Yxj(l&cZs0NI;2Y=NcXig6B>n0<4WdF5qO1KMp^aAb%9EJ}~Q9x{Sb0GJW)(6EYZ#+VdS`J%)GI!2C;M@z3SJ{Cr^ko582?Bn0RgVL$vCurgnnqLR1O z^eKx-{sFI5(Do?%SvHJ`X^ZSH<5pP6#r>xAgE%8Z09vj{VDx829k7e*hguQby%Rb0 z$-F>aLSXUSj@s+T`L?QX^FP<|+Xq^BOUj$Jms$sGLluWJYiKEhp57#XXS$=8DX{%|)$@hTTR!`()N!ZvSR)*5W5Er#-}U(x_FxVy0af=Il|`WvLc|1Qs_-& z+riG?TQ_kMdU8*gPMEQMk+)`EdC)X48Olf(SYf53WmHiFn`juheG=b$2`aZFBvtrgSN1d11Qwio(`n4}b;= zKWx(Fy6wG_glRyeK2sUYxb+=-=qcSM9RYOL9aL`juZjlf@<8o@x>CCa;{9BR5Bh_hlemXNrJ~Yp+$Ge21O4GM2U(pppIV@cf<@oYkJR8qL`3W- z&+mUN{yY0I6A{^DF*j0TB~FYk6sv9qH>FucuO3H|uS>KY3JpK2$2;#9wRMpK{*GH8 zm}&mLRojtoI{=n{czYX_*h^Z(#3aD#*U&bQ5~@hs`ZNdkNM0}+dz=SEcf22dK24X9 z_bny{1Uv|ng?Jmb9}YUb%!%Mk_^n9^PL4gtO-ZV>Jb1AT*XReZS z$YS0tolK2z+#vaIN8xC6+hX76d72~>c8I%Og~;4Yhrc?`nURxs+-!F=y_gg53b*CW zOBZr8#_=K)Qx8-ZE9MO%8 lH~>@!F@k9|V$_j6oPA})CIMW^yW@}O3p(iZMq}Ci z3IPmvl%=w+_bv_uo_B1ZqPPy4^Tsi9_Qk>ZWW+5wmO-GMkmnoIkHUo^0{r}4!fFr1 zL!LsXkjE3ln{UfRn2w^}+y{tZ0a;uf5JUgUyi^lUDx4GCna|17$e7hr?aY2P4FHgF zdovrY)XX$92&aU?dZ7`DfR=a)#U*GTu$(=ly`u_mjyF(+lRq=jIS0;Ig>#e6XM%fs z>0<7u!De7KgksfApaW`QX{@bun*&Tj4bm|*sHMU{!4o7|R5dSq^eP}TEX{U#PRF;z zo6pFx%hbUHA9oEiXW$ z)9xCy738?_$IEx#nzGw|6*b$PXt*-GEi6n7G-78g(XhQVI_++wTCq2nnP}{&4@}!? z!d(y${mDl$Ks=}b^`~-+0}eeSX&+dbk#UYEL-fvg*vfBY<`=5`o2vl1Nmm6SR}FF5 zrsCU>kvz%Lc-v2qKb`h9PAe_ZJPZYp6-o1@)LT6u;?&m%$5MpAo?=Fhhh)^6< zG}6|OWA{r1xeLI-+^aiR4V-H0PS?F;(#Q^pbFxZ8r*syqgaBJU)`b_ZX9sM3?3cfS zK)Qv}LvT3EJjJj$08Ve3L0ApYsCmDkXX;_6y==nBNUy1m2+RUNs`3foI4M>MC@D}Q zvG&Jz@c;Jx17>@}^$wfA9gMDjaK#{DwwqoQQK%n>>BJBO4_<({ z@7!K$Zdc_DUS09Jwf*mAL3o>!ER~Wq_36_H#o+@xwoNqVNQ7%rY!*S* z^StR}Os&+;NqQ9*SsI;l{gJmKlC0-MCWsxtD&U$;4&3OtfpaliGbypNKefKW)gSNs zmeTv?$uk4rVl@-ozNbv(t=mg0wcB?XjfCoec|DfsoVo+#yEP_Bjmz=lH#pSo=aOvx zYDvqegHc~#jSaSpJ>HX%Ub^ZSQqChW;S$r7;@o}U_GNs2( z6sVqGmz3w-9)DRSKmhYLXSU{^&K@n;-i7?IGjOML%De^fC&EU@lhA@>WwjmjfC}(A z7NS7$qAtm>{bo9Fn$lax9XHqXCOebRAKPc&*-ZE&@%#U*7r};Ret^M!7pH8d()f7QGr4x zx>C+1Pw+avx!*mn0b%B5XwtiXEh^J$P=yhdrumn=`y(YCTR1 z1q1e`?X3qtslbIrEOTgl+~aCWx87Y8M`eM@n0=^p=iK%@I-GLoYuS{GR%uW=sVF7r z@4xW;q-9GYvw`3FW(ul5&Uqf>pX~1gELC22s<%zf#Pm@2)DhS~tw5(!Y?$NUJ1O1( z%>8}WpIbB%k^uz+XY(iKAIT7|UeS+6o;_k^qM8oH&giarKnz6X`I!>ME`3|#Qrnh1 zUdba{NQGg^vfWjXqzKrOq4t(9)nilM;KH2nYl&ZjLR-=|NRG`3*9RH?k+zP~pe6)v z!J)r#^z)Bjj3R&-`MsLf@-EAE7}tzBW;pzL&_zGVd7%xBF^2HAJV-Ajp`UmzVDRbC zHdVPZcPeytBkh9bo_pkOG`lM}W#sGv*${;jded8i~Q)#J;QQFK6(Z{aUY_gg(*5 zoH}H4UvaY!G`2zjyaoO%ej_z*wYm=43{Q4`vnm0H+gH~oU95Kj?-|=bl!J?}wYm#P z6|jqlfD;+Rr? z<)YiCh>J^5?>q=8Mi-0Y^5JT+HF);}@ea*qUT;?33xE^39S<$$iy(@51 zn9cV$9dd+mO(5%uS?2yAI9LaLn&`pJyeT6a;FL|x(0z%A!M|-j9^Rw7zE-Bljk*DK z@-60%E4w6u!8<`5<3lO9~Y*b6{1^TmE?{(MpdoFh< zCtaDd#`e6}cLOGxHHO?0D^rzx3aBwgrxBSI}(>5AnYM~>+I!Ub?+EzpQ~QN(g87B8#e3^8X-QqMPy&^(zx!> zHE2TqwO6Y>K>PH&39?q{C=uB5BO}BPJ2+vp?Z2Jt-U%v1YiYT zCyvLaJh>PWu~=Rm6CRpzi?KOJDA9pT4ZXOe&GhBlyO+U1_S(85b8dZS&aafFi1~GI z{tn6!X2qECnR!sbPIQpK;U8(Sx!)%x{ZPK{4QSnS2(}aDYVsnW_7S*E^Fq4H>BBl* z7=iO&S-)g-Mvl9BZ}etPILY=bh+^gdfyl6tdN4hCUpJ0a+MHjC7J%=H4_r6&0c`^7 zuKL*wpyLc6s73{R-!&%$V5N*tgJ-0P;wJDT#_VUQ*&9k>*SB z>jRR<1BJBMCAWuB z1G?l{8tTrCoVrpARrpKBqr1Wkg~KC6@euz~A3*HM%dsba$_=URYQ0Lp< z*@3ur@7dF34kOp_*%t+~x@^ZdqzHIblJ2Zq%w!RRwA-G5A|Osa&cZF{L6B0F|GsPd zA1N#5*Df%Z8QesGwP-wdo}qxUeQYB@YaV#nuew6wepd{(LM_BXl(*Uc$6d1{gN~_e zPG%qU7Zv}=0!y2RT}^d=8bS(rYO?{NxFja#htVQ*Ce%`~>>!Cr3xGDr1IO&ljnqsd zjn__c%$%7pU3EB25J54lks7pKEhv5M?}lF)pCsz1{!OL$N9r6f8>dQ2lKx0?Q9w7; zdQdSYp%+-U^&e8t+(El$g`X9M|HZ^8qEJBaC-lnAtZ4L~YXoRW%%YU5lkAAf7ZEe} z+bBO{mswPDGyo%mFA?qOD!=ED8193o|99&l)3<;l7$rDI;ClY#q(4bl5GO1u%>Yg; z4==-Z1HD?&2%LXD9NR$_A3J)s~^WY;^@1!uY3_j4XTb4X)riYnbGGZ^eWE-?en=c{0g$o0$V zcc(Ndoqw)2OpVZG{cm$mSK1{12U_$)u}dHXOL#qS@D>{a`^YIh|4=?m1!fSe0x;r3 z(D*ae-1kc%R|wS;u|vUxnHm-THw7W}`)VZ$93pS9c1}RUmD{@;liGAogf98i(AK6R zge*#Ml!e()MwSqOB9{n7H3tZ^*7**i*GS6ctG2Rv>GcMVbs8)$O+cA*H*)nfo-8Ud zDLSf<0d{T7mL-drfpcT3u@0ix{RS|q_N*P06n>^w&;JB2fdJPKkoxQ_aSOmdMZQyw z-C<$E_*v;HIf|bMPyt_1=2DeB1$JaM%eK)9uYDj5IX^B39E`@?|VQT27b_WEh_22-}o?0_z#)V6Fk{ z`I+*GGeYqfYsV!&9b4q@#Nkj7xeR4-)5KtPHoh+cKmf6r~?2|+OcejkF;zZr7-E65Ulo0bp#A8tY# z__l(y#V|D0g>NGYc#=31T)&Wt5J93u!Hvi_){OvWd)O&82cyxrs|!Q8tQh|i8&Fs< zkw|c!gf11bH=zS*AAQ4=gFgozJ}OGg7Dekx=;E9wY{$1r5j-j^Ajjm*$6rXDA=-{} z19d*m-;};6HA`g-vM7}vnt~#9=Rcfwc(R_BV8&`CRN)?fnzD)_(E-Z_#(-<$wK;vrn}2O1(djKK9vPtRlp zY{kIrbd_)?4JslrpZ4o86}iG?(}mn3NF9Mv9iPP8usQIy3zhu<$2+88b7R5-KY}!DX=*G0WVBb;E_}aDneEwaRGJO#2*x&`>&?=H2te;-l^EX)usT)hq4Ke z#75Dee8L+u@FA>%WMcotb8Iwi=iLwJ{8i4abirUJG9s7hH@dDmFz_b$xM1O*vxuTR z&Y#z&9DrK<-d;HVF>d`TBzPU2Nu9d)kMqG~LyOfm8RkkV>Ai2(4&oIOFDCB1@Z(u> zmud!ZvZOyRW)f6dgt_6r1I17F;wQzf6=SM-0nS%S`taWk7`2Or-lhV@c#g6o!-N;i z9)P)LFD*^QUgX!uRVf6^510awVIv8ngFXr6hnWEgUc;g(g@ATdORiNYIc%-zEJ}@JZzPBR0I(JVU3J)?bQ}k z3&oFtGU)a$ukyu@VAUWWOV+cu^fM;l;pka;sN(YIWx$2`7d=DbHb-#}-l7K?j`(ZX zx#m0%AG87_IFpFLg+v2_Bncg5Rrgy7_2>CQobO9eWPMcAMb8W2wk=u%&qJA2kx)+>hp^vfYrt!ucmZ+wgUf7lyfT zux9AQ*6Jfc$?w_vbRV{QrJ>6O19`0|9t*;Y3&&?lIBzA3o&*YxF_1x8wn6wd`$F6M zDDm3nmAFRR;{bZ_TW1v^u0f)IOt`DU$=mjI%lK>Zc+SF1p@dCRN_Hz0^CgTp!A9 zN;LCdH__OP)RCs%PyDU;Hv;p+^&=F+i2@Nj0_)GTtUxD?742aVhsa@icCDDuSbM-! zgBbsqma60T_U(&uEn~x?P1G56&QUy;k|D~C9#Xq3)gLLWO3KTv`6F$B)t*<#AK>#= z89}01I%T>;TNUn8qs3IAgV!{EKO7`YJ+l8CSSc}ZV=rj+9w4xxIiA;CL+~Hw?#Jt8 zCcipx2o_(mB%&EC{*L9g2}SRV$hCs99T@ydp3A3eSpXw`PbB)+kOi@IJ9&AJ71Q>` zP!0YiPr!a`?4UYSkyE*ULRoF{^SoQkY87)vnzZZ=eai(n*d3ZK3-=j@5Q%`spO9RN zi<+F%Da>_477WzhY3%Oq@4XnbjS#Vq=K0U3_CC1kOQ|AMP4jscS&Yuz=z1uf#tEn} z*#7Us`NAX-I_#`1S1feR<_9JcK19B3!6FMQ6QctQ`L_6 zczw%D71ZkVJJJHWfcKqd206ngR;EySc1V0LX*yv8i#tSB_@4z>@vCt;MQca~N2iUw z?23mNjJXpOF$w_rheb{ME2dqs*30JHyO z4S^c4>{IIjg~^q>0I0B>d%M2&qq@Ht<^Whx;jWbO>;IYKm&5|+d3AtO^j%8&xC!30 zur7sp1HZiedZMAP0g36T>@G{5ecEgs6+lkDd+tXgZAp_DMG7I`-gv_e07Syy=5{m-UM7=8 z`Fv+)E{sCI$ZR1aO;wzAAe7!iH)$&?x4yr5&n+o)!LW(Q#2wIe&>WYXD27|N*-tA0 ztH-_RR^d$qAmyQ#w})eR(1FR~rTzWHo1m!XtUO;Fb|+NQEB+4s150!B=Q$sqv-7IG z4_!BP1p8q>cQ+eSS4yf3%AnjNxB&O%TRfElk|*qRz{kM*UyN8&vof79DYMywxc)iy zdDfu0kgzj~+wXPQEiNo&zHcsc1NfLR;Js0Lr+Ja9HSK^BPK`oLF~E*%*LzRRQFduY zEPlH#HDUI$ItlwT$@OjR1OM^s0fClesED7y{g?~4Tszp{gToDeRRGlKoZ&lAH|m9h zKeT!Z_Uv7$#6V?HY=A$u()86BxY!Aj0wGAD1CSePLX*+hQ> zVhZD_ssqgc{v>AA?H>ZSDo>HwRhECL1(lZP^>Ra*H=DWUQH; z{JN(H^!u;k{_At6J1$rlX2FQUkUxjRCq4~e*`ROqQ-W|v>F;wFKeC>8j%rH#fuN)( zcch8&D@b2}5R3@UmWD?Ewz^8<8Q|;p;|@hVT@ zov0Cn51E)YJ$TC7pcdfNfrHuO;gtv?|K2-n{eh7>BE8n@v;fQ2uTY>qswaU6cvp<= z+C9=JMVqT2-5%YGA%rlipc+H~jn@XxS=c+e0ovz*_8%lC8?It~ggwx!x|vt>EH+Wc zhEwZ)qMIoUYgZds3`eQlO9hId$!<#{itJJ6ug2y0X@m-Ai1sW}`C=_w!3&b)+0Sy95V*^ zy{XbyD^t-E78EEdBbU=uCqNpg!k^?y-pOwSU0I_55^}ckdZ7e)UH|%&AA}j(6l~=n z1zu~DzM6wlExJQgm8Z-5W#MBoNNNrr6lRb+gHyJPWr`7^{@n^E0MoT3n zTTrKE4-yy%6KrVuL}<0^Li)I?!*?blCV&{)to!aNhjN8S{C4jLVQYZ&2KIG7fQqxS z_2D+ab7-BIX&v_PeoaFgV*1I6)b{E@87T{;0vGQvgQuBzprUFH z%DCEe8J{+70O}bauSem7G_08A7mL+0W~YFq30Rpoble-<$N^=h@oP`9c_GZ0ePE0% z9<-T^?im+IID_&HQpnK2g(G-yGZMv@pPr8mc4YcF;%K_JJbXCwJhzJz;ssAW0IBCr zs$NxHhvr=Tmk!Im41bB^wKShhrwS)f-D~r^Ft5`-j-we*)Z0-vy7+pFarDep72fEd zg>os!0o_Dr6M@Zc|E2_mSc{Eyg8a0@sK4hh2r}%^HC!SsW=z1i&3pL7AU-p|`suxW zg<AU+UoK*iC_|HorzCT5Vcz~L^XWlpu*ONn3H0YVkYY?=Wj znPPb>GJ>NX3Jr{ViZ}bWmYEfg9rXu+p-g=9`vpBIBDpGz8I)8RzKSD-#2BU7fJBiV z0Lgveu^Bc_`i$ZhX)#)}u@`SC)r&;@0}@5s(s+yTG(p*_Js)a79x8$wR8=^29vj=I z`VniQ^KMy%GoR)clHNa1WC-)ZMmgEsEVv_J&hobvfLQS<7kv#-w#1I7&4T>8f$aHf zw541&WB0@3iz4a4TyfMMwcyI;91u#S%7U&S~PONeotK7J_?#c{%*l zJ#ZcHKpf!7#^lO?kepL2T)sZF@@vgAE|LP9P2(2H=2erCAu>R3wE2&IJSHbajEg6Q zVk;WbvhMg1?pLRR5(H3$gs?aU@Br8x1>keQTY-nywFA0;$vJJl4S8j0xN!5n+@<}a z^>Yk)J&;4PEo4`-6j>2EHown!rkYyGOJj|# zd;4sF+@46qkSoFA`~EMjA^pAl$-t#l*uqP00G; z8;M?lWDP-Re((UUtZ$I~69NV#yPH9Bulsi2YT-Ih)GjcFpst$@{B@20nm!&7>jZpF zF;j;4Udy<~?fitZ8W^)Z(sR8>?qFT1X%|i#-5oL;{fiCRJVZe2JjV`ZwS^Dz)7%%>= zdU>bnG1cj}LVNbFIKg3ryrpr#h>X@mB0hjsQ-M_rKX3wZ)!v$BT+A~^P|(|ic%KHR zGX;f+6|;%lrdoF4+&&XhyDusQ@a9Q`^_;nEGT#F_?^sg_7KBu{5^lIY^<`4={1czDkMu6zSWQCPH_*w*=CUQd#j|f&`wIf`x&unKWNC52D5t^) zyd-gzS=H1jHQ?>HsbqK2Pl1y1Uq0yO6xVc)=O4#G&S*IH5rR;N36t;#s3q%`OB<-- zp}gF(krm$wJL@&Bu|k~*+I=@>5b)~ZbMfYX(wyUBVdQ0G<|0Ll+}K-HNnD*mVpM*k z5oKV-zZ86+T2~ik&3>OS2&T=q_3?^SJZbqHBU`wG zec_)L(4CZJZP$g=&}V^JgOMdvN&|xe;Gg-2N1D2YA_I~Jpq(}^elv8De;3~xT+*& z!tZhV<4o2ahsD2kqbnk>F0LEd8y_h*{f!%sO!STn4ve-_WT|XoWo1c^G%vxEhW18+ zVmtme)mu*mUOjvb?|T@`2Mu|mMDy^gn{l~P&V9zT6RlsubOQ3TpsIRU_}?>|jdu;v zohXuD95J3{4Lh#nav{@4Zq4wuZs)T(gMge*RrMb;Eh;v`WF)yD0+}?2H&roAT48v{f6w{w)P?EOi^W}+-!OpGsPPHyw zsaR^xFC|ZY1O`>dvbBoy|MSFy9LznTyQ;y!;Io1RDKa4PScx%IhQA~FA1MDn@3ari559jFj;WDJ?Poi7x$QcaYJ+z3^ zJt5hu2WVgi8)01066v`}{CCBi%px$5_lhR%lk;{`+dqCH$mmu4ueF~0Ql=q&t+GZ? zigE^7{39wV9+LLpqy-cggOV?{IyNSz!E+u`P?1*|Ub!#`_qm^2y&4!0cs|Bxb=?Gb z$``{QuG`GP)(wH`=YA%%5kM&F*h)#!=rIcAbtiG~a?(I)DjWCBL++*+F@{68Kf?tB zJ+2oThO}8b11F#cs4>_)_SSRSq_>Zt&}MzR1v0>b-xb2(YlLlL{pIryY>#fi4v#*R zZsDVuGie3B+Aw>Op){v5%tYf6{UjJq9M)^vd1xtQp#4c*Rex5DalH* zvWsjYGyB-<_qud{KA+$Bue$H9>s>)YD~I5G z32yFFs*&YT0cNv^ru!A;H~&5I(U0494wXUY#vhsz6Pwf|#@*j{BjGxoNY|0hot=6)Ya4{n7s8eMi@ zDjw9tNDoyo!MMAu1a$Q*_hyS2wi0PFZ_XMY=+o+cy?I5+`%#CbT0pwaO@y2K1F_KF zSs#?gTrm{?F+F5SyH`M8-t!U|KQ8wa}VkbKBFS8N0ijB5!Osbsd7o{fmv zq0~nh;=w~#K;f*<Zmme-om<#LnmE(k)lY*13vtp- z(t*E)Dh5kx)F!P1l}VDUU#$>c<5hGg;X$3lF=I_{?%R2NO$dQcZ~D5PmBhd(bCZKT zSW&*fgZYq3_-A4qpnPyo1fDSt;!40zxLiP=!9*7sf$ZF;D| zR*>ys^cHNC2d^ygr=yJj4_95SD|B>`QT&n~>9Yb(r-d>fG` zJzJJEKpQ}p0H4TtP|7BRXG;1wG$P&z7ptFiFWaB8=bHF{;TU<0g1w}!sK#GwJ}PNI z0dx9hFNriy-%gr+sW%|EplOXOInlQmz{QF!eNBrzB^8UQQIEiFObi)iBhx*Wi>zVH zY3BQi_OqQ&!N*IjC|#EWmbN3vndlYFl0z#ICdld|+}zf3$+w_OTs1RT9&Mjh0y@WB zTVi>r#=c-*&Leul+xNYFsrQOl{v9J3=~QoPhafYn5b~Z;bwxFT% zJag^)Un3NdFdW>epAFRDf$y~H#~)tfK+D{|gV?vipb4`a38qe#Q{DHOm0VdL{&xl6 zf0@OQYAh&TplLs0<_S-v{XjF1y`!R)bf=6z{+biBd;O81j~)Nr&^MB+<}@53@~`NA z!DrzQx^gINeW-9{5ouQKr8aaVTqDi;_`|m45OV5zJ;lNn4J7#S&kQw^+;|(i4CvVw z5ZjoO%1ADVG#xSdgVe~!Z~u4q?`|z~faq*NSKyLNcz7=NP$jtJxviS$tLT1)$T8h)HFb}VA7rF(f4-tMN; z@h}O4?l+9^)+m)#L&Lb^*x1V6ztJzsCUM+ltRTnQ*OcdGG<9pP=8RSH2F^)F=Gj`{JHke!Rp`+Wi{D8__zh{;obkBI=A&hZVkI+&V!857<&`s z_>lZZ8_0jpT2q{l1v9$5z58LkJfHuLfcs0}DowS(AN~V@ThnUCX+C^LBB{X7fkm9w zeJN5^D9snWpScg=Avq5fIX$HPY4YYfDqhGijOtH^vhsgf_8LE_<+tHm>s|_Rfl}9)C&%g&mxA)+xFgO0_Rf$o!mp9AZ_8`5nzu3h- z_hCP~{*$G03-W-Sm@b0#t9W7GExPtq&QC~O+gpmr^faj6+f0TbU z%2j_DjhixbE1=M$`#GA<2yz%Y0cXEG1Lqzp=$D%$0yf5TC%FK^U6F%xv7H6W=r=1J ztt({Qc&(TD3l7RS3F8-ec(aq>gGB0OH~(QKe36SRBmRWmf-BY(#NrySJkFOxJJZkA zOsjzDdjGYhsTX^=q*Z^VCcwSi;{Cx0qXWOmKV5&fFi$oOodHkfnYhWoXopQ(6BlQb zy7W)(p;R?v2%iP{^&2mDbs?-dJ&jA^q^0A=7w6AG-^xc(PL#*}>pIQ;FBxcC#;2CO z4Xxa&(H#13tcq5Rw;S`ohbX+JGoq%uWN^RkT89ZVkfsck7r>!J~U}Kq^A!hj;prlX*n5;5c|3 zG(f+X_Md+5Nw9>zwzhUSUFY0$kPwyhZyPwZerbIatR9xRSY_?-GA2I%1~P3Hdw*2( zqVD-F4Zba;pYkwXw%r1sG%s(1T$J&f z;Fk}F_GuZzO6Pa_iNE}HR&wW0%G7le-&#PO{c`RYAHc9G`3xQ_|6i&xg$@(fQswJ$ z@Z+G@68k*Yg7n&uxt#`UdL815T8F!4@YC%dVuV||EgyfNPBPDwmoGgFt%u9Qw_t++ zUw=i`42=eU!#|YMU}&2eIbO$wYZyCuNw#^dRs&@0-V0Z>^xkO9Xa6(l`3r6!loa@K zZbwNDeqCy4rWW6N zz$*hr{)Z4A)K=WTCqjOW@*8MI@i1PRc9~Lx|uwP^l*gb!buVPlqX;hO?cq?q8vVeG%0v9QOhaS*lpJ7e$ZLuCQ#NQZhj z(1{CcUqQZz6v73?$}6&dUtPUJ^+g*&tuegwz#k~kkdf`ux0+H`CER{a(WFiZnxBMn znsr+S`G#aFz6>oqvp)@e1sS$M}oAETj#a(FG-1&?0p z?BKV6Jh-Y8gNQ<;+0r;fFu4_=7h5Wmc*FICz;`n9VQWuC!9dvqMk_viDoyerIctjA zF=4O16Y-S_!m1ARbe%HH_n>Bk$V3E(uiTA?(;$5S-fMtH_z~Iw8sQ?PCXE)>;lUq9 z@osgduqw@LUey{AEt9PMcwxGL3V`=si4_&qQcjQl2##*Dv>#IZaLd?G znu1H-+P>6nfZeXo59aL$cVufQbGC0?hTS7s?4c~xLeQWb2nsZ6n%xAMBgn4=UU7XY z%L5ip=Tjq+zvm5vAimsu4`zL8_wPWWVRjtJe2XHv_qU*t1izllO#W);{NpA#`Z6*g zbT*lrYL%WDCqT9f!ZW!?6T4T~puuMhgw?`U*~xWlYBhClprh%AELTTFRX4 zmRgX~6p~NkjY7Ahnt>G6cV-Zga^DU(X=el^DgO&*B@G81<#q{T8p`3?8prk?fp1@U z$GSW-JUp0L6EBUHPrUIe(k^ic$ripz9r75Z_{g^ikce(5-h<0XRVo2>jYx%v`Tf+j z5Es5`1xUpLC9Y4Ld3n+uTyD4vK||ZA$9i9jLYc}E#+0Z4h;h|E3CUciI&NFm3iZ+H zJgC)}UQ!mE=}!eU4oDvbdRJCQSnJalf;zkuf8FPv6rcNJ-Dgh`OaE?{%W5Xjm; zjqGsUI@t&m*>1`BOAPbyfF&RQLPo_Duw z&MB5b|Nn!4An50ij+h5tw=#kb4EY9kET~(d&a2fw7c}gR!lriW4RFZ4weB9;Th*W) zyoEl}mx*YXQ=66jX(3WU@+s99$y-Xwf*h4z4NFtsrXEMzf_(N}+W_c^Qe{i5x__Ok zM&#J>cTwIcsw|Mfe%TsBjDTc_L`@lYF*&@`6V$VRFZR>hO|`xiIM7`2NnHei9Cyk3 zD9V7&E^QV96#u7~1bPGRGNFtWkhcB;EXSx^N#@(L+~D-W;@XC!8M5nNY zhrb0^V45EY=L#x@ltD>-62z^+7Y|x{HrGSe>!i^gKu}O>yKhzq85R8o=`HB$U8A}P zQUd#s{y7qI@^FOb^#X_p3E|i7YJyxdsj^*&JRE1QML7d%vD|wy5Po25y3hE+Pybbt zYg#8-@$unn#U7f`9=}}{+GVfydn)H$E1V0oL7bK2hWermG(pNcsVvv@p{x@8hhVZY zI7A_jyg8JEFFjCnolG)=2;1h4UX+a^vZ>baF7@xLmEg7o(P*~-oP~M8WG1VU>C`K_ zAGEOj3@1K$?p9zRsm7Jyq~^*EaPp^sQ=boC)&Tm&FS`0V(0wd5r*sxF0Y{! z<#a{Ji*UH`s$H=yT!xYc=1Zs@cCZ`lY^8X zI17Q(3qR?TReXXPGOt)v766k#KKGesdiN{IStl;qKPUsjRjn)Fv1Pvkc{j1PVROCE zORVC{(p2kV8jc$BvPb1Gydrsl!%Mt>$2_>RB5K4wDIBH{R?(-1?e*%e^nS_9Z16%>~b!fB(GREZ_r)*B2<7ONd>naA+feAU9$j6 z$FN4vS%6`n<^go{lvHCVDA2{$%`17VqoScW`sk*;M-qKxqIo|@c)M-B)&g+Nlua#a z?t34%k@g?=(a@;xi;05r>?tXHG~Xx0vJ0L3-LnPB6hM&4>#Rv~sb3qn%6gRk4!Rls z<5aMNZ!82-5*5aTs_OWARG{YhUZ6$!1yCiqAEcw8jrM&07sr4Ds_|5Kc<7X0u?4Zs zNmX}dZkSEKmGwW+fCL60 z0tM3x|J-H!FS#6!o5T47jZs}6!El5yPFv=065=E1$YVLe56tYj5(mTf!NfiqK&qke zIM9U%7DzsPOYPg})~y|)NW|pYCheui9^-EhMu1qygxsg$Ag6OgHLCwV*_T+E%QT&S zMlZMySr8}RF!ibV%g#6?Ux$vX!Q4sOjg?)wTcY zsybc#H3fW34|)ogq@Un1Khl@(y2t~oqgQ8Ca)&m}DMXTaGl=|m#MnVR|dmkz5~Uqw4-4>CtKZM2H=Ncwq?P^|BjwlrvrM;fC4!fX6+m zqnvgE@~BNvRlY6I6l}@PpPCTi!;?of8;IS|K>8H7ytorV4Rs0suwHz+1aJdY)?~X z&6E~fHl^H(a>P-y0_m`92Gchrutm50(S1@4-MkC!o45q}&fhwt{oSO8->}V2fJAmj zPiutUn1|byZBhzgCI!}LunuCxTOhhx z=M#A37bu0!p?NS|_f~zc2kBISX&M5_N2!y=q6!9JL+Le#ysfxc-(aW}>ahg67s_UxzFs272 z@cyLlQ5%=kfYyn@pyC~_08$hHz?J6oWynnH z)BEYk=8LHnV6b(K(VB-u$&uw$oyzs8*)HUkSDZCo%sX{%!$RWEXYL z3 zL2A32&1tY7>pye3YTMJ}M`9~%n;bC(HVfEgx+AbGiW>E8&Wpg8+bAwvkOd`&zV{0 zzffYW25@aS|B>^EBqWrU!|uBV%nqA8A@mFi(-+1M(6@sxB%P3#S|=E!Z14**wj<#(>%_ zM8!UzNpf7S@0TH>7cw(=Tupfu?DuO>;s7At6R|FJzNKeJoN2j7bft0Qjt34@eS5c= zsZZpOwJj1M6bUHWOdX^>tOExFk^~@grbpAjHU^V5O^Y*`9Mw|8wpINY=(m``h)uyJ z95uV~dMrZnC%-C1+HoE{C=fvl*?6m9N20QtGRKF>`mmg^&$`qz^`V|67|jjN23w^b zzYb6mXv@^QtIK{mbF3Kl1Dd1^8k#x8MUE^eLp>52Q9Tg)Ex#pRdJTza&^8nB7G5Mh z1KZXm3#w*|w)Ugi%1~SeXP^Cby=-cpafbf)Clcyl%UblGt`TpbbY&u-L=Fidb zE!j~{R-x*(%2?6}m9{dj>K%AMtna@Nno*0$!_-a4Lw`Zx73GMg*Tv4@kf%g+>kSpu zbh8Rf5bZr3s>b|*Jm^7$E<#L}7^Oc4_i|arnQi z2(j(5hI5_f%~5WD!imNn;P75%8vZ#Tz{O0xXt@WBLL)iZ>9CV0y2;~`X`Gc!6D|!? z9am!CvZ&of*K#u;GCs(PvzAl~yt}2qQw%sFp(}n+ZAm1m$46?8+{tP20pF#)n@!o+ z{v*`k=L*BX5<9}h7$X>{#81~b(kBVDxt03;nv80Ag@GKJL^2~%h`2vjLmK>vb=^HL zMHvo<6ur;>9lovcavb!oO$Sr}yU&S!;xA!q*%{3A2^=<9*;Q0eUw)KL%)*!N|8a zARX@3ew?jFUxUdoX7AG-Xm`Us)=0kjp4t`Uy1r6<;y{$jChhs5l9Ev7=FD85F0>2Z2{Kr9mY<&AF?)cZar*bu3RCDk z*k80kY49L=@!ua^>rFWiwEnn$xNU6R| z_8j;;BL`kJj)*zSeTjX~F^z-u5+vk<3%oceVB>~UpstK+gp;swu;1mVU77+E+Tb}$ z8i%e}a6Laas@Y$2N20M50IY7j2F%$K#fP>dQ5<~u*nIG$R;i86HERNimb2cy`^X10 z8`*WsE3zvKIB~FX4nvY+B-hDnuEogli!$e+vSKITc~1gqt}zDb-wN_zu+7bi_=ALH z8_`M4t2^*ree|q))G9j~0EY-NbcCobODY(DM=C0+afIe;`fanMekiX_#Q4H4uO;_x z(r{exvHJU4!P7wv2n$jwkWz2;;6L;|eCi-%=MDw#BTM|hWG%v=Qp0K*4NH1W7ui2o z2aGH9Wsf=tzN5qnCBn2e*S^z?d|-u>J(gn9dPt!=5YF!4OO{~$JIZXgc;4sUwK^`l zOx0Wz?;~A}j+R?`8n6`4L3{tQYV5I4F@vS=qWknq{ELXUi*5CSYR2Iam2Ybq^*3Op8l=&Ny_IN)MrcuPD#6eXAaN|YSjY8MjLCWk-16X~i(|Er6C*ngJv@^SuZ zFR1Ke9f?zvz8Rv#To1e`5!`IyP4sYqF-FR1D5JCJ4m>p38Ujo~FXtflNs<0y_o4N1V;cS&MFDYG6T!3EwP8zh?DvPB%3w~dP zUR#c`^p)!H?5i}^baWx@Dr{2ecW=aCS!g_wd1tz33R(+LUJ#?sO$Q?#2%U>{{71Cd z4@xOF`@ucnH&y=Jb~Xm|xfO~`Wdt36hwy){ELC>bf$ZCKK<{`QeC2|hXz^Fd=`5t5 zov%H`RzrgG=JpvJKo$V;SBhtQ0dg!K1t`?AH_lIfdI)-pwTCYQbaP0f0j$Bv@G{hF z#o}W%q;j;7${~33HyIi6{RaRVqGn;jeX{|XaO@QU;l6CByn$+$?V!REtUH>NH=+oc zG+o$`pN(z$iqpHi+`w_&2MFQ`)IgA(8lIZBHxULTO#b@}@Y0LguN!&4V@@$wAbwuk zGn79;sB#~H*Bc+P3Io|}x>N96>*2kwE3Xck@t`EO#SQC2Z*I0O@m{qxVLFp`34>M2 zMXen^hpq?#RlJWlcVdXkH^=TW16=Dszv;x1AM^B?*u&^|F|r8MruF1}42f@=u2h}} zzNJ)?vAPrvhR_SpyE#IzjtS!}+iQU8x{f4m6d~Z84OPGOkPm-&gxvb~345QzjFhIz zoyUM|#3v76;~E>R@V5d!fdU3+i!L(!}GY4pp>u-KIEGlcnZcY$4#YyY6w`nkm$ zEBX6`kpKoT4m$IhyW64gwnM_GL(0Wd<-CNf+aNIcXP0J0zkSg z4>+rzBscGSD}3d8_lySSB^$3tNjo)NFI#>Ec}NU!z@h|)1<|`=Wwr`+YqlqKqubz4 zZ<-G;fEiZ0M1%*maDJ`3B;P_yZf19yKggxuRbdKBJ7z-xYSPPUcaEIBMQko|AJ>g9)PJZm&H8f(Xz35v7vfQza1)r-R3c0}NVTvveP zhV-#+z<9Mjeq=!#dWbniA#H*mIi-=b{Xr$gG?ZFnQPwvOJS>I)UjB#O)l7dy>Jbf- zMID#?iG(hWkqdB?eC_%etlvip?2%JwMzEly1|F2xM)e3zs4%hNEe#9ia6x~n$5G+T zX^vVN`+4^w={wKO2S$^FVT00awj74)UwZTK4r{j#FgcYP=c48*^pv-(rZ&XvT>0?% zF4uUgDZxULu_^(IfK^FkHsJZ_DEZ^M5?!a8toD+IYyH{>bC-HIwbIq#V0ExdE`wnO zD)^s|Em7Era!_KSc1PD~@#zmIp1k8eT-brbgg<-k4?vya!3&Uh9Ql3XhpG~tLA_HX zT_?LNY&p+3z%q8-yyOBLnhac5KsEq3D7;6cu&Fj2I(?c^OQXsz#!I!}Z>rStjuim) z4^`g)9IB%n^TG>F%4!xMD1;u03OgrggB@gWEkfEnfkQKe^ob#(*WG z9Cwcw)e^-FKLsULDt_B*_`5|pkYB;_&=mL%6gKY~&mH?o)lcLv7$R*u=VJF)0M5YV zI9=z!ngS1AG08me$LEh|dSyz`5mipTa~a!TBf;GvEj&kK2#7WyEx2FO&=_3Og|od_ zK>_oS4h0LCF`O(r%d9_ql1A61Juo247P{vp)3Y=P?`RJ=Gi^jDD5s~t&sbNm%f4-y zeyT$e1>_FIRm=GeH;E9znO@T=-I{*h2-a!{PGXiC193-{AylL@Jk#wfB`5mx9QXwJ zd(3B$2Hnyo-iTyI-QurdFMq!inF)o`;HCl^+N4$MEj!bM+CCHK)QdL|KFT8?9)+r( zDFg<@6~&2byF@=Z$Vcf>Gxh6~ShTAW<@EX)D4K1keJuD42gt4l-yYBrEP7!WxRn6v zB^I1Rw0yWG%Jt87;ExZVR4Jds@qyU`T|rP(Af6BEzYi4W?%@lWq-X;%qxAnso{-9~Z2B zVd|g&U9F(o^;4LW3;cCt*)lMcF08mKH(|Dhx2^Wt(d)r~EvV`k8R20REmgu>2c9@; zu9xd|C{cvhPv!^ix$@}eCrKdZhzZRSMQ;0wZ{*n3$p?hRARO{*jPPqcE0l04i4BoPq({}mQkaE`@WA+p zt#;kL@&GQPa%>?G!5z)KEA^3SSSQm%D!?a<#txPls7?GhL25${*B<7b5H3->QJz!V zSC{C|mM`DWE)1L9#<|2EyuRW9&pGr1;O&p}H-WJ*m$mPPJJGptk<)f^9s=xLi|kdrFxq<#ag%fU19uCJ%e zx$mgrdVO2uBtbOkUwaI}Z5(jPF4xZd&NL#qX@&m{1q5~kyz+<^&H>B>xwU;$69q=3 zB)c7txjKV-P1bQTY}5ojZN}>RP@V!2c}g$yMsyf`=Z9J-2VtbYV+~fT{;R7B8>JTt zG#!kpy*dt+Tq8GNwG|d0BAV^OE_KiS0Kd%(=(+pIYx3$*isds{{Q(SdM)yRDn;kdj%ZxM_%8Cp6ZY;qN{lEU||Ji%!$sz4i1Sn#6H{J+Pw$NXBoDTRfrU; zFHF2B#l|@P49Q zYOr6e)m;z-)<{C;$C_EFJO0V{3RoRLw)zNcGl=zmdJ@89BzA|DjkWY9JXwhjHc#N& z-}|J)0{4*GRL=|OaR)0`)P~C{t-hT51H?FI$)?ramoIn^RCEl_`B6(AMch9+JAbPw zLoOFO)-k-j;}R72OG zXC@>R%`+2$vV|n#q~xkW;Q8u!Cs8f}tK?jtC)Es)>~g~2hehQmJowQ(QH|NuiS&ju zVsrH>Q3k8;%J$a#6?gOgCk(%mNn(^$^}whuy;N%B<|Fa$EsHlL9oKpi%a#;iku#P5 z1JeP{pruLvspT)|^X`s{&CSW@;Ee4L*2aOLzSkYlOG-cgZ($%e4C^Hk&A7yp#p5)5NzCka) z&$h+ttMhOHflw8PagO^ph>WrUIGGZnXb8`C0G39`4KzA+(7%Pn!6GB;F%cz*-r|o& zGrakHFIT_kc}R#)wDmUyNKGw8+#I%Zmi*!a@US+z&TF!>OXb)}&{-pi+SwQz66reD zup|9G1ET035sn}8TS{Zwa+YA>p+o!%+8SMEUgnkY&{BaeI92Q0M`JK|Yi*v%J436R zjO}p$X0L1oD^c>`VSNwi=X`dHg-u7lGk+w30oZc-ddSn8J+3~f>#+Ka3~D4Aj(>pp z(Z^M|#;(PJwhvQ71|xdNPUugk+NzmZLop`Q^2S-TPlqB%mXJ1&r_xDSqU@>1VMdjD z8`P^ML*=HKPoH$k2Vv!-v8fXush21O4M!rmT;3kJ9iL3s*)){eBhvM)gUbO}b4C*z zk?9lSF|4WgF8CL32%xF?;QTs9o-`A>5P#j_0%f_!N2eY{-0WEu@5THTm=C4CBBIEH zuQzlAZ0-7w?HOg^c%P*c5F;O5qG)=e9g1Qd&k6)`C>WZ-l#Vx|RTE6w{No3aaUR^7 zvpCM&5CutVuA5GWukgSRRk^5wBnQmA1})&`Ag=BsvGMjNz%S7JKJm-)?^}%F0@VOB zOAITCQHvBMmM(aW*N)}sB?_%`rEe1#R{(;S+47!c+x%}nU3Hds6m&&+KtKAv zoF)cqjrcaAD|g$z9ochvRO?o{W|kZVChPO{iHqyW(}DXQm@S=n53Fr#vkq#6b(k9u zs2yN1zx)c|0tgoGv+ZX&zKPUC(cDPTliSUKU~RdBRMf-G$ zgSKLIATD|!2E41qaP6&_^#qu3zU#ehI85>33*V1+N=2po4KDQb(^QeJq~5-$+-4ZC z2drdD*7iEut?sI4UV6qTm+IC@>BlZRN}kQ+$Blo#D*>;Zjq|}M)PL5x8-zx#gW=fY zTB`clsr0`7xJ7m48E}QjZQZjL?mh{FogLRLKo5QSW!7^FC%pbefeqFMOexrjfO4mn zAD0RF%_<+?QbyQN6_&Yhj`B^oK_wuZSE&D>;{)it@@t^=P-dTSg*00cj$cTgQ5E48 zabKVeAxqqc1!=&&?=SuUZS?o4n%qbrv%foB(e8V*Ef%bq)2w+B1Q-RfzuG`1Xe5bq zDsfi$B-q8ldUUw`+0$-O0db(Z!lY_NhN4}(q})k8_5l_@eeF)0F_S$8F*I^Jise2m zE$f8^tIvtnyOqv#In(?w@0cyz_RaiMRKdMM;$OePfv#IefYC}=O<0W(=7+XXU#skU zYG57!#(^01p}Ehqg%8{3JS-Z5~*ZEo}KuLCg zYu5axAN9y71q}^wRXZzhIkk^Iwe9WB6Fi_LV6bkxw`%?2X&xi!{V9&qd3sv__i@Ul zdbJ1TJ01{7{E1bO4AwGmdi;;Bz*AP*wTT-@2r*3x8ELFUsS!Cjilp52N1W2cPEJzPO>7kD-L-)@liBB%HeM{E|^)6X6 zkGcfJjf9)RxCW9Quvsbbd&;AQ=QUJHfy)XD$RgYVShUnfw<9s1H1=5KlUh|2dU6$L zzOW?3wCEs>ge9c0bN7+&vQX0+qsD$cI-9E5H68WpZ9o1ZQ#$!t4&zCjii8%B*UxghlB zf4oYWWgy=}#$e0up`2fI#{qXXsbwS~&g4w;#dty2`R@|Q9?>JykTr2o_lmg3_|)!w z-oqp}9`=t6PP{UU6F(6qV)p}asVI3}1@LfV>0;?E_Q7{2Gy7oPv{=g_SUSnTuS ze3)bfR}^Y_Brt9=+L;m9zM&5t=Z~Ro{vHg(GksH^L#iD4wQr4Hz)HFRnNEH8V`7gu zf2V7aXf;Wpm;Ez*H}6@oodr8P6SG%t3!!~S&8hWQe~L5@)ghF_BX|1FziR*{ z8Sce2xFHp`vfe(|Sd;^2OxLx$jiar-kg*8YK#Y6E+3eA|z`SRu>i*C1;Ri}#g?X7Q zv>CL=v&2Z03xADrUVsxF6QoqhBr!Qb;;-%@h2E7DfmcklHylYT(ukv+%;p2r(+@&L zbogl29b2@FbE#G4Fpp?aLw^PXiJtq%`0#vPsw~C8DfzpQN#ES>9PzU#X>;B^0GUE0cT4ulTlWxxVhzwEV?jRjEXUazK62B$;rFXY(>jli zCrwnHI=xJ%|D5Cw1?q84I_307biXEa+WDfQW&|FTymv>O`;$o*#ld7BUDy<$L`EKn zMp2v#Ft`rJFe`M;|bm8CKF0_MaGxwMxyL*~ZKMOe~!vG%No1*JKT zktqH0kU&=?>N_ZJ;oVKO_@LPs9b|6(}8ZiPN` zv3&fEKU)7`b|)MeSx*)pt5k5mRw8a8F~V^i<*zLl7ry7v7jDPx=v~dxznp5Ui1gAq zYQqPg89Fpjo!6t5rhw(mwas-jWDZsj^wk6XKIj!f4wX%273|bRx@ytF(u}2YEYlBy zL)Ystjj$HeMKf|!y_gu6XelneN2wJW?)P5TD$1cyO4Od`(f^FR!zUU`y_X<)^5t|^ zQXOcJCkra3p)!7Db(H2cgTYE_dktG48w!=|cpiQ0v&Yhx=gl$A7Z+D>_9670S#!0Z zY24Po@ARzqr8F72_A^IV3eM1ZcF<&(mQ9^)*uultd91}fXrb^rm-chCsJX`-elD=K zqfpiRuv}+!Fsw8h{-eFUddzJ{+6}(EIVxDbH!A(iN|yRXw&jk{Id_YC3-e@Oob|`l z)RnyzcSR|M-J-#e8amyZXUunh{J;e{wWT+tms7Rb+kW2KdbhMN{jF`8-1*&NtN$}n z6w2612s!Wb%puDkWrTZ|K72a?t4jIJc{!!;dNjPCT0PyjOs@TIabu)qz|byEKpKTY z8P(?px&R{k6|$tywQI^-UE2_AxX-(o?R^XKe#hB1lWu$at$e<<-^)^C6dmkv8cZ@?MhkTb_3Tvkt(-zG+C z=#%R#^kXJ^D&RmbWge~3Rs_psQ}&(gA2%5XLvJ7&Ae&{U|=6k%(*hCOnhv7511{FU=k3ct?1!Gg^_g&woo?z%igRLdq&)idE(d*Y1?~=1g z?vf6>-hLMwJ297$#W0X(MOb`X{;XYa=|$(YMcFqdwO=OGI?^yNrhHO!2=x^5PXx)_ z6k%)T9_^tiNzO{TRd7a9sx8RE!~(1@o|JEb3^L_42R4 z32tI{=dm3cMsJqvhKqw~BP?pJY*)7v6{yK3@iCbnqB1+nwa19}Q#P^pEsH7T6D>M8 z{n9aLqpT+hwz1yx(>xSyDOaAHx*MUG)yGGW=;Oyx?oEuhH5Tf5MOLv9&&EZSMl`U( zlWNiD`Yf?0dv(e3HxofAx2tpNr)R7YK4PaOi?bzG)US0+-l=$xI9uC{f0ZhK%{Hd5 zmmeomdR%VIIGt}mB`VFApJZzMMDf}32!kE3&s)!gA&)(Z zlLOv+*Ry#T5`sG9VEcz2HHo)HI{98H?vhP%zkbM^FDdYGljySN%0Y-Eh!HOfc1D(E zw;Sc=72dD$Jl)c$=vg1{^J#R#v2IUoDeIZqcSEe=WSb^N+f#nC`TK%tQOhLzP2cMd zY--NS3k)II)KXzWHrm)kZ2DPMDpURUG<6ebqTuQ-a@jmxBJUYmj5_4rk0cO^cDlp+ z{8FFwbvpa;=v6A!=V?(*hMmlh#5uQ2J@H<8(j?kx(`A-2bW^;K54ml$e7Xkl`VZn2 z`6_}fDz8Ww`=nuXJ)YjZ7(C2uu!Y=23|&hjmrc^$l|11LSCQQ(X?HJ)m9esEX%H3u zESy@Yys{zAN2^z9LJS}J{J8v8W@k(=Z%6;N-}#(`!+VF@J2zO&{<;TOu@Nj+6SFp7 zRFDw%UhAf;jy{ZGYN@R zFEFznPpB%n0?k9+qm`-7e%v&g92QWc!ux1X8RIRLRpY*j>K2+guqpSqZYRI~o_8rv zcg4(2OyEI!wnh|YlvtQME*jOIhKXHHu~gzF9DZNHQEV4sKDQN@W_qBwu-jp!zuvi~Y=wNf;{MX+!ySzKLrRsvSEj{4Ugj z|H}eRTcdb;Uc1eTk$7B?dE!Kt3{yy6TPl-qJ&Y=G=qe2irI?lSi?sVqq_lpi7%X9v zm`1Q1VkCqR&+V6}8)Df!N9IHsj4|{cl5vk-NJLg$B`9rM@NcuIQl&~gTNgITg#jyf zIaEw&2`f;eLixNjVVJv5vTvFw8j|PU^LS0#Z=Px4J>5gn3QYokUp~)7>XUXO91G^^ z%Wo}(Y3(dZeA8aq{{6G-gvnLR zWP9^cR`@A1+ZC#&{rM9^a})02?zd`UDO(zcg-jdot{gWN8e%nNX!*V$XQ7(ok&eOE zUGrB=SROfC6$}%C#Xa0DoYr$s3OnhR>V*>znT;*uDE;(2^n9H?X~*}|VmpjXAJMVs zQJvTSW0IOC*IK*q*3g3Alvy_|Kjjsk-6t;Uq~3nh_zJ_0+zRg-KIB|mDzE(6(^?#+ zQxg*XS%RSxu9y264@0(njgLWiRpk{Msm^1BiMlmG;jf|={6ojLIC9)Sk9gI`-%h$b zMx^7ZJnJSIye>2P;rWMxk)zS8Pts1kKa2k=8$FUQ^q#UJkx zApfXvtlNVc(dSG)H|>W$doB#gzaM{$kyI|!*zd%}DVo##Hi;mIo4oamVJGrU2d;DC zoB*S8Nlv1snaliLp>*?%)259(ZSD#~tkAjcvt`JC`jYWdq1U`gba(iNXVJdH*dvfm zPsOLP$v(ao;BGp7Mq9l7!}LH%+6B4Zl-CQD+o4IEKjkK3(nUFh%ZO~*$An9Wcd@wv z7W^yC9b&h(UR_CEyo=#kdSdYK@+ApU=;ws`rJl~$Irt&%{!J>^;!E`#A^Z(HP7-|o z_X~FR4Ecf&Ped4?Ct{ozsP0dc1zOTi-qni^d~98`UcHw<d>t|SU6FqtJ>QkVvc+}nbRmJkQJ+MN zUZ1#4P}iZh-A`gTts0XoS>bTLjNbYa(g$6FK;!>fikdkQA!^_VT)wEMgCVO_*qr_j5 zeR%dOIm-M+W~bce7gIOf6ex|M36LhQy4|odomHW8QDmR~wx6D|>z04+hlC+MoZ^&o zHEqk^wj0M=V)wf{m9sIIUbI*kZ##D~H(AneU1~%Aeo|%o#y1^In_=QfWedTrnDy&5 zEguy5>=arwACmZJQdFcG;qBUny}zuqeiQx<*u zV!dDY5+t5mrN?6Rm{Wsx^3YMbn0${FtjuRiWyU^XF?CIXB`2-klGw_hhn2fB&vlx) zV4|KDn>&k>iB0Fm;r0@2w5REnF9r|!Y{_+rt++?;%1j&oc)i?zS-h+NE4wh2>7lT8 zGtG@c?Hczgj#U@&_Zb8w+%Ptfturorn6nOkU}bY#PrUtUm)cTSOZJ!ewu`~Y32p8W zopZ9x+Fvi*rf z^QxIH7lGf{XpF4m*o^p<@V=`AKI|ruF-6{DQDAM-$V_X$vs|S-v9xHU(%4MPa9TG@ zX4=Bn{yZ@_!#X9$Cb|94W`K=|APfgKE7%u5f(#j3eoS=P&eY}vll4M;_b{jrOVC;M z%4pBahQFVTv)DCO z7TX)-y@?yL#a(3FQexQQ=u9A7Z+71pcgE}{!h=c`e((MGCwa%0y#&IZQOumorMCBf zE!AB2Vy$+{)y{f_!R|u}tExN%F@n_8^k%$%6oztn#ImYG1$XhNJwt`8uzG3Mc<=Sq z=V`qkm1%!|jNO&_InnhgM^~g`bHY7A5O&Gdm{lrlz>u!^wVuvDEgWc`s9;hzm`{VewsL&6Smy1LhDvs)=sdmU_t9B$1 zuE_=5g>w{d-}$`ancU^pVY3L~O>U*u&iNEa+-uL|b8(g{RH{_tVdSYFBHjCzDx0_C z7S-ULKIUPz(egVD**jjpUz>~2N=UkC#3iyc>#9M#*0Gn?Mp7Y9wC{*-N@k0-h0V5Z zcFt$6PmJp-?&%B5pWPsP2eBxp?w|En?Pl7^;-(~f4W{jUDn5sqgiA*5&b4WZUXDLY ztJhw7Y9|LCV~d*Wd&-?zmH0;57K}XBYUNw!L|0|nCpm@tetL01l|~T1e8cMDxWSae=7c{TIeJ^Z!itf51!(RgBEW~WAtA|1T3@%ppX$tQE$Cj+zMa$2&OC`^v} z;y3jf+G?|vo#TW~N<6<7(EVk?IJj1oo4Fp9bqiN+O8C5ombL4}C3 zwP&5R&hNk9zh|AlzO}x~-tYT9&wloP_xkqU8h8>YlO_?VsdMT?DwxV_h7%%h{jQEr zbm9BTbJu`?{EdzFM{X%#K!Y|dLlDNSb4jnL6MpGVNlIuSIG)$Zu0Q1U|Mp&y7gbn> z4)Zi~JAq0ddfjlusz#a!oBA0LXEx~K4F`}&RNvW!^EZkQO%KPLzZZ6@1)+ljBvNjG zLcbIpxcX|)ovwT})Hd?8kb#1vn4-O)2MlyvV0Ts>CBwDf19clcH+{~E_C#!yGPWnu zVfraJS}4xnHg5row{^41b2o2L6tD5M98~jlT@c0W;seQ>I@=Og=yhh}be6<--(^-{ortL;KgyjQJZ9!gB2I)-*6_!V!L3z!X1t*x62{CG@g72#(n8DvpbLh=xVIneamVd>va$Gk08)c z#~PHfeiji1wXR3`t>oDS%e0+b{$@alFBcf;A8K(=7o`Ppd?Z@-2s=bJ1f66^#fXP* z-C;tXB)_DGMJ>WJ&X-JZr(_9GFKyfnRYp|de7lGsZ3~7X1hpku(1dhCsz!@O@|R-< z)8F#p<4HLW7w+|S*?l%=@ZMp}xQywWo)7p3()xqkXB!MT2xNnHyRaH2wO_Xl$zx;l`B5xc^w$n z(%|G7qKd)r3*N+nH*A{liV3halNWw?+sW><`t;RImRj^i=wq5_^%37B1S{l)PK8c( zb4UH~7KBEdwv&tEo7vGcyJMs6uVYyzNh;uL6^cTR z;duLcOkocvYGZyQp`IaHeLZe}EV^hZp_>(XZSlRr*+t4{ z(Lskr1`nxHv#r%mMB1zsf?k0)1x+@|b4V~fCc?f2hVryG{NV#Z_Be{?jMKe`C9AW) zUlB4VyJtI7SA248PYSZk<b>rm9_>|!BGiADw~!vq)`UgFrV+INGt^Ip?` zao}U-Gb-?{o14>>+V|Orw~X@u$?b^W*<>{Xu0|D3KTVnk;2N1~g>9h=n6<;|16sFl z?&evnAM)TYooVD1+eufeM1Q;X_b*#7jU8bWRnVPy2nKn(fiRQ7M-%=;qBe;e>LQ}xm$ypa<|Kx*K1xlv@ zBq(f>%$bSAw(68`ziq!dkUTK|9BpH$+4u#hE?{8BIKEO&~MGvPmDDX3D zZN9MfGrHSG0H?td2k_?^m~;NhBQE}HaJdXlX8$j{DE4k|7JTFLVVOJP!e33fxw*aH z4VD}T_KUmAYe#0ltoEhH8qgPprxfe{Ib^)6}eLeQte@sHd3qQkGs`MrJ+q`PJ~Sx{Xn zvr|SslRC7Z_Y)p67hH%ey9m*PTKl+m@}kqgPWW8-BcJ@3P?1G`NC9bqTWhp6n^bNG zg(oZ9wzyb8oX3zaDtqvcBW6T+#k=Ge4RWCcmW@Si&L4CF7(bFJDac(qO`~(_>3QX+ zQnOv2pE|MVxT12dvZ5PTFz0pB_NZ2an!~NzRx9zzN5wFqY`Im*gv`EMGQ)~wFIJRb z$RN0==SLzA7UAm~qka;FD#7E-`5muh`!yd!L}__0b%CiSp&YvYJRd47=hh%saOw zFw0Fuy;?4GrUb{Fif$Teal43hn|55?q^3b>E}k)UgjvG$snjy~@nLBc0?)?TBX*$> zcJ+bv1#QlgI-O#4ydRhUvbA8rx8lXoF3-CAV5?B@@2s(Dzc)@KzGc494Q0ELJ~eT$ z6}AzTBKU!HVbPE~mRv&a%JRnspt3z<-^L`&!pU7a1_3)mvWFlbkMLEcI?ibBt*H!mVqJMSkG$|fQCi>i7tPrqc)CJNndx_2Kf=-tUC9>hmu+Kb zATMH!M8(W0L`T^AF#Y*OExLfnqP`viOGHlLGU;8!OZMIn%cs?GMMLR5&X$ z@X(layhqiT#eb#3$^oPJdw$ckSPqQ_{Sgg za%)M@>|;`eyRWt(BjHQ&SgG7ky4Pi^74Z@Ni~;v6672Wf1N+ZRhsR1zG6?3Ioi1db z7?(hZH8No-nQvV8^U2~;AL1iId3?=hrgp7GJ8`!lN!48wLU36_>^B*_U)<;~(rK&`i zSZ_OZNO&vGH^J9ItvXJ4K4G5A{voZ(aV}v*{ION{?z4=D{3WxPsktlGQ%XpcwsOJR z*LVe3iY|+H#ZuFWjNkaS){yCiTPgMxg&m#WaCkbV$KQvZy7(@{oAjvaZOuIKz*^x< zYe64)p9aOAg!OL3_E!kZI#7qWY^TLI(d^v;Gs!?5GDS@gqt~=klZ=L`eZ%ZU%th!> zBR2Jg+FeeqRf^hA!S`LJ@8X@vY8C7sh<)nzqhNkFU^aVgS;;+C9&piCQB{jbn#0T$ zb}R&wEbPOW---JLdnyu3>Uo;mPvP5T5c^CV^t#7kOutd&u0qXz+vDll+%uPPN@u1w zoxCAK-xeo|_jQcJRsFam(e(>M>w_)0SQx%B>F6~dy!wj#iG7=F4V;YWqj9UV1}MQ%=9JV+1|Te+H{C zeNHvkd;$CVDl@lIa~j@5mxA>bfx4}$⪙g#71aVFU4iWeC6g|=1$kAiZ`xSe}I>G zW@JpuS_S8c3LM^VIxN-{jNf~#-6V*AMKR6R!2dn-bnd48y>6j$=?|{tE~w378-OI> zc`q{bQR^kPzQK~&O7aSNgE0c#v3QE(B8(K+-N!Y~sV`=TG3C)dI3*Ma|7X(QABT98 zNv}x6=V9nD4jbX^`rtxfBhxMR)MT@jYCcBK%H6`BRhs?Dq-1wVB0WX$uPWJlnvp4l z2pM}DUoMHYL3BKhC@!Kb>{U+SfXq(9f0EjZ7M6=MdT z{lv=`CoHL4^oAs#Nw}ZWJJgFv7cngx26~Y}uHS>{P@oZ;2}82-ne;P6JCoF<#6QPu zSJGZmles#Zr!OdXIguGpJWAwNPEHVU_HnrN~t07-wnZEb%mr3oKgEOH+7z-j|g&o z;O)9Y7%=!PL#EFRP~|ZHCf`v0;5$XV7WMdX*OSlbt+136c9VX5KvB9u%4agU)pKS$ zkp?{Xjwyr+8`tcvR8|(5;4M3W9_t{ARt+2sK+^qUgXCKrB7w;04No0u}sy0kUk<$&6kE$h6A!Zs^)yVLe6m17WeSR)T%t5-`#PkH!pF?>_MWw6W78ANb|L}u?nI6pfJ*C*zbk+6>J ziwB#lOw!$##GQ0DyH>HTAtO@S7ajNVeU=3Uh5KskipXI{yFy3p{%8+u77H&hV;;r7 zEsV6Z{1qH_7Gh}`qufzD(m3h-_Rrn&7cPIVo**Z`z5kQwaJ%=ff5z|IZ)v$bLUsDz n<^LaoXVTK!u`|7&FW%ZkTPirQ{`J{_?K@T&Tk{(9<+y(Wp5m8b literal 0 HcmV?d00001 diff --git a/lib/NeoPixelBus-2.5.0.09/extras/curves/gamma.png b/lib/NeoPixelBus-2.5.0.09/extras/curves/gamma.png new file mode 100644 index 0000000000000000000000000000000000000000..33a84d6140d9f6304f450496ee4ab24c4d31e5d4 GIT binary patch literal 22467 zcmb@ucUV(R*Ebrlh9VL`5k!rIB8Z?;6r_j(p-GXVbd(MOl_niEpeQ9EAcFKFy(cs& zilTxjy(A(xDo97ANk3~N_wzpQIp?46y1qZu?AbGG*37K+TWz)>+Lu(A8MqiwC=|0Q z{-Q1lwT%h>9NA3|Ep)E8EI0qrRZ&D0empn^58G|eYo14;N+K9nEqA~(`Ud{0D+;xz z5&1*+)+O5t8adsRuDD%xv2pXXyk(8LVQKH|cEZ`(?TGk^lP6Ax;nr+XC_{18i|6%j zn@tT&JU;j)ab{y}V&P?y$`_UTe%p!Nw3p8w3=?_%!;3#{m*kvjJ#^;Fo2WHoTG>CQ z2Jb9bcJbmuaTi$3PAJoxw(q9d-Y8)G=x$Z`o0W(|p#pa+ zVo)fB?N~H)zx#jCJv*wqyE~|%x%pvUT*dsndxZuqHa6BosjB~)p|w5T2Tm+Hn`up? z*W7K*)bmq?dhC-Y1CN(<3_9-~YpD7`<9qbz(X?s^YZNL}0Z*U%@-9A(q?Xq0z=y%0 zy0*dCojW2=nTV?y8yV%kR^ml*?uRzi%CU|Ic@iFpIo%FbKXQb%;tfJLjH=c(eOs+& zzS^c~PzF3&ihXTn`z30F%A$UH$Hks^l2c~Pxn<1BMln2c`ChT#*MX_#*eACOxK_^6 z;vJ4YySJ~&;SC2t{X@^bjw>|_R?jrGjL{%!}qZN^R_!2vRwD@N^|#jp1@2(Z+i zGx<2@NEn%3qWm28E-2EE{XHC;H&-O9`65S_qn<~cP5!VtzD~_$sFz3KJ!@1TL-FWd zGj`SDokQk>uhqpxBngvRH5-kh-VOmRB5Xug^(S$28kjUuy}+M3%4`l6o!26dGD(M= z9VJ`(sD8k*`w_04&VBusN8)E^YH=7^s)(?i=Em=9o;TluN>SXcs<0#Dw65D*MnniR@nE{zH$e@z~X#TtQo4+ ze0c1v&lnRaNV)x()6Hj5)4}+sQ41|wElc#vaQ#kGE>+2U85tyNh51-+YU#48b(AsukoUtg^mdI*Rx}nC#k{5+ zXpyB@gglMAc_FIgO(%WL4^(p)oZ`Jq=EL5Y$)a1FQJ5)-#BlmF8=(sM*+N0d3fAN4 z8QboZP+#ZkeQkWm-^8ry?a;~p?84EVndaO*@vmuK`|QLmbxyT6IS^)TSX85uI08G4 z;^<;h>x(cWxV86-0XOxB*#h}7VG=xRG^c=%`Z3v>R;-K+)2a_^P25Tz-mt{nY22qVnbW=Ocv{jmfnpu|lMJVr#MMVTHbGR!NE+O`Ar~UW!xJ?} zWfh@|J^+G`S#g|Ns;~Qc`cC%e^-D8cQC2G)krT@M&YUxpCe|iCd3SUu9}|h!55bVp z`z4qTUh99;5v*#pN8$ztO*k`vt%z)|#k@+huOZYst6$5(2y;3Yy3Lg|4XrrW+o@N! zprQ|A(F!pa@9jac-yv;JQ3HUm3I6~4@qZak9;7LDEH5v=diCn}?^2&VD3tcGAYuj} z%bPcEVq!QDY|752uXpAJ@CqDgwvfZI=yUCQ{z?@{qC5x^6_6x#-C41F8%!q5w~|1&6Wf_zM3+ zCd$imeulC^1Pt@<5Se}bb zBrQvwm@!eVPWoYu!y=1hzzbv)ypozWQT7)3(Slb*CbjN0qdmTaUrQLW&Uj=H$BVEi zSmIpc_p{DTG3q0CeU{kyJ;N;@!BcMTRWjPySdci($h{;N%f!qyfI%qQTJ77 zI_~8bJ&b*Yhjb}na>aXNoL2ZXyx&9_v&qx_m*GwcT7J(1CbIGvK8*a`kcQQ;(Rcge z`?m(45ii?X+N^jrud!Y1lSm?Btw#7K-+4^@Fjw`oVr>1rHQblU!)nC{>DIK> z&E`aZv8Y4Zdvati*WxWlDpV zll{e3wk9tVGf<&u{~)UjSZ7>cF=^M>JgFgRM;|{$Dw)?fd-45JVhcXmZ&b~8)0#fy z;LkYD)2}%7#DDArZ=!DpM=Jv|8@t^^Kx z7c>MX-%0iqlXYAVwn#oi7TF}#7fJYx^f|NDCsd$2lF+ZOVOe**Um0Pvf%DcRNXN_- ziq7Vm^uu9)T@P;&IQ71Qmllo*wdUU3g_@QMI2ERUhvIj*S`-XTcaqE*bNfe-r9TS5fpG!I3AgS4L_a^ggFT0=)iRCn%E zVMLE5%7{2V*c^vf2r`YAMgG=4iY~~{&u+`=)(9hTfjYJwXVX3VwlpkW40nuUX_Rdz ze>(BIBS&0i+;)_Led{S~jK^_WHu{1t=RKnWgQG|%i;Ey3Ddn#$9c{(xoa|w+5Dpiz zcI3SkTGg<~Om-IvWn;ZI`P4BYphX}<%b-+KcYItE`>leLeP^hpNE{ey#Uiap6v4k!nR<%lCb!efOv&(_JZ{NPpY=EEi zN-W0Q`oBw@()pQG)10gZv-+(8^YxSOM?cM1>*?(+*bC}7z!Y2%CbR^b_4q!BuC~Z& zPKu<%jpH=N!tyx;w!{@zg6{h&3OR$E{f_v1-YFx|_ay^dE41)$m|>0(m;>qBb#-wv z)mVN-IGIFMvGtpMrM4v>DU?@5eRHE9&t>UZAEyZ-kR%cP=ON)+iOZav^tW-FY@sdD z$xirg=%nKBR?~aa{d8B&pNBP}ddvTkS37es;ZWJe!`7Uxt-~Ep>$N}p;?vu7FTXc7 zxm)rcD2?CqR`PjK-=_KvxL|V@zvLAbJ0L9TnK_)HsF=KYrsurHNVjCsP+#y^z}4EV zY4rMD1YhS)efTd&xy8J+(EHc9Z7hz{@?y3GR$<hZT)0oN(Qvu_Ua`Yf8uSK#uOe zL*lGdC>?VpMbles=4Lp7@^th36?O&d=mOA|j(>XxV?3-F^}V2NxDnp1w8b7r~ z=Tz=R#qj{IR!m2ab%xNthv_C1Ny?Di;zsa^hofyD^^`z{bl4UN@tyYzL|nn1Ic^M} z;MEV&ORCtQ>TOG6z43GTluZFm;r1!A+jMad#bO%5<@ZA)3j zq%2h^m^~msEn92&&}gATDX?^Oi9t{;erpXDJ-OYHB6_|5cGAn`rSTS~t&YoVEvrAr z1dt^R^b0{2F<$-2cO%_CTTGRS^+iri#DJyCUEJC|hS3yBVHR*Z8+wPmgca>D;z zytX@7B5D7107j=8&QWi+%tw5#5~Jw_6N_&{H9W_Et7 zrx?rMd=hy{cK9I|IFcxZJq}>Z_8U!~>L`;Rb@3lk<3^er%At9gTS|&$z!sac^6{{# z{VcM$IJH(xl-ngadvp#qeT@ZT0hGn9DRQEv<3uEBXRv+E?<`>g4&jh(Y!8#MaRd?9 zdN4}_F(D0o<0qX6M%VA#KZ-wx6A={^buu$Xx5E=KUsRXrnfbI;7iE$20} z+<#$?{I-6Wt>bybfZux8Nkc=%nZrIG&6T|u!z2ui7#TQEIE0GcC$ali*v5EJh;dJ?(ZJLsDE0P$tH9Ja*D9eC_V zdTO#GJ?B|~$ty5RV3WO#;2Px;d5=cOfOw8ETSZo$=j_^zOTnf!|JXB}b!U%e& zO~@(g1M@_?>E7B|zYwmD2Yw|G60fL^GL0o;?VpSKrbl1QsjK4?0Yq5&kt5W*T*S8L zZO_l5K8=~R-c{?2_5I0}x*D6Zl(Zh!vaPX1VntX|CFj?>h~rrIPdpw$arDS+Cu`LO zxMdI0_6|rTlZoXLcU|gd)J|^hj~ZvVNN&LFLq43W_u+${nQ?oN0SB1gN^1P*lT*i= z7`Ct?EzFzFlZ4wD3U*5llIwn`o!sP^9(>I{uzvOx{t%rddN*@mS&-xDXEwRuJ(pL= zPWu;2DqGjcuElv)9@*0(6R2}eKA61KFLpI?B9-r>Mi6^xSmko)l z2*~g$DRL1&e1(6Q9Q%zxOf)On-jJ!&KpYw22Hue>UI*lB8#41#rai{$iVs0Qo+1+7 zeDti72-<#kuzdnE(wiNe6JB^Y01p=H@F4T=!^f=$KIBc(t8gbK;~HUjx%qr)r%dTd zr>R2DEJ70^AZPI&Rx+iNLSow+%Uch@E{Qq1StX$UeYgw{3Nzo_)$!h-@>py;1kHqNVT}^RIo9yMZ2S zkR5m=TBG{`N&RUbmoC*zpZ)z1v_v?f^5}==?^t^;FE2~l*RN^9)s2QvGc)-hB(~#3 z`TvoV4If^8Duyd4EL?WVV9L<5>a@=_x_b4d7HDnM_&bjV{Ui6*pY%$xd~rnQ z_^f9OzhTnkjAHkTe(WU8q=BB8m|$dNoJONkR7c-QW_~PrdmKwP|K5b3J!y$IGBWy& zg)AR4qiVD6*CZ5*>t49i<#E41)reFXidO1=Fet&=TUuKB`3=Y(7#r`-`vIYLh5y2b z|Ay3?hWDL5CdY(?4jb@y8GS2a{08+vx4hON~)sTojPR*83 zq=kobRX#(n8Ud|=NUKR4i8GR*7OJ2t3BuPv+*yc~UDh{z7$Qcmibf9e@oqNVmC};0Xqi!MIMU%2(^L}1ZG#er{hcLQzFkd+$QzuEJTRr`Dhu2g2GN#9#n_`;8<};;GZYmQW6wK^p26I-zLS! zvUM|dUTYOKIo6C|TnaDk_?(op^Py$%Os8c6+%g4ELU(Z<=$`KSTU8wo7*)7?7))Q_ z&U9W{{+ygy1u;Wqra7UIeX})K&E3k_gF~?4;|$q6;4?=m5}LGKg!9atu4)8tCv|0| z!nsm68c}Ah`{7PyT^p0zo{;u@?yUV&Dy+l-I-GJ96ySQ|6Y#xmRu$et2pma70?T$- zN%hQXySeiCVCAHm4wA9Adnkq_xLegIsZg%X*T1Oq`e~+)ADC4!J(hb9Rum?7gL$mI zs9Kjj$93`~@V`G`{Gs+S7?@ozhCOLBKtz9}qH-dKBBLH1qoy01)IY+%mI|z;<EK2ES*~eG&4Eo zfM`z)yZ@UkmBrV&c@0eSa%P?goYpRyyJlu1R$EWwt1u<(ZC!hfFSXl}0>XF&oagmQ zVZ|`yxYeS}OL*4Xr<`f6yXmAvGOdWamR6Npr)=`zHtO;Jo}w17F6r zMK{qpIDb*(?cM2y8&1A&Bt6%AB0u-MK+J}=#~SyOox#_?2~)f#>Sk2}4&(%U1L2xN zX6xb%FK}8mHyb>6y9fYaDZP;09o(b9-`CDiFyjY(K437{%exWrRC_qorAnG#zyjq>HhF4?tm;fsFAp}4&wzP{l!+t-N*nlS~FT9bN znOPsDvU2p{&Z`Cag}1b>p!#^wIpxxCXXfl=s(ou*TVm3t0gIbu<+w#eM2?B|m#aw9 z2bPdXYI*-9NOpmM#JZ866f7-H3F+Lr1s16Wi;Xe(-%@p2gEK1njxXMUY!pH&FD*^O ztQ2ZO_Q7-q>ImOKfJQw@|4>P1o_+=J-#2zV%PKR`ulAUVFJ(uGH!n$jG6`5UmcyEw zt|jFuoHg+py9sh^a_(1}uu`TVl7g-~sHnT#LKeeWZ;ThPpfo#HCL8ZqI!4I-Bc#2j zHqT{+*yT5|!|-A5P3-lf;IFSJwr0iLnd{2Z%V|YgtbKoXf*rBG7B~iFOA%B9K;-XaE z1X?<_6fOR$R>$v}9;@Am7gj=|EDI0tMnFfJipj%W&TFqRxWt;qZTZ{K*_KHBjTI2mPChZqYOL~qttvvnVU>WAN#PxMV z(_VyMXE0J7kp;J;pC^?PyX2M|roiISgM`0R_gu?{g!T>1G>rEXt*eS17Y_8s(9;ll z+h7)*fZ0b2Lje>pJ>SrV44=X2543NOEJ$M~h+qkR?LAe)YIkIyw^-02p8nii&;Nlm z1;m=Of%SKvm#;5f`rwJI>yTS!1A?0xu&$Vqw|0*8?+SQNA_>tZWfh9rM3TG%S~X(* zIasvK3huX5UgOO9!rCb|-Njdoh3rVE)WAtaa?U_`$2{#XzJO@#WI}k$e?^3ZrqKBR zh(b}7t_4!)wF1?H1+_P{k_Q7?W9S<}hb=s}p~pt=6|#4~Y$czhgI<$&iF(^Un{cy% zquU^e%i#KFgc`>L>;)gicB7pK&cdU>>h&WxsMVYuXDkk za*{SR=xVLw%pcM~d4b#|TV0z|b0OXiWkBP}CVghZrC*M&doThfdn=1j|W*6Fbf%mxa$EA=x zZa0*&Tr;H+S7O-R%%y%Ld6*fr;~cMgrCGO!9Y;a*H@CPye{U zYKh09VoMDDWmvi0LFv+N7Wp1!R#jZX)#!^^E-x(OL zUg3TNq_gE=i2h8i{1WkX1UXU$`o^d88k^3``wa?!Rjj|6so)RP#zVuM$lU`@^*@VZ zNmNJq+JTv>TSw$!z2t5)$gzPN>Y)6q7i`XLmyITCLx%6*tfVAu9&7wq-TaG9J@)%q zExhA5xF*^cED=Fy1tU6-&H=ZokX;aRK?(zIJ24&LB9hw>opRl<1E?J@GC8IVshvP0 zEVkSD$1n%D2)y!Nh4t70np5QP`1u|{cf`p~e&(5}U=X(5N_pEe%JyDK&1Be4GPZ;%0AWr{YFF+{1eDhN(&%*h%u zd}xN;iM{>%`xyp#9L%c9BBsYs-%|&?-wEJ?3kpiUkUe3gX18C)(4W@B;~8=stgYY3 z9+;+*9UmH?#9qm~UBB^7zJ6#kZL`)h)@#Z$KJt10Cs_;hSeg84R}9^^%MCYluKS)P z;xt1mO)`klf8KP6`Jp%qT^HI4lGkIjV9Q$y>D{}|&dYcn7;9VfF*k9~Z1`jHF76#9 z%-fNK`2$BKhmmjWC=LGAhnf-7?*&YEjAt)?Nj6cGzM%!7y2&vyS0u+H$q?Y4fy=PZ z)3qrTdjH#$8^r|C|H25H=gRH!Uwu*#S>JTeP6sdnbK~_*3iRXt`wM`n1uHh-q#PcY zb~oX1$f@b~A2J6pn_rq)G&w7V#M*q_&QrgB;aTdgInv9oz5`5NkvdVMYjFt z8A*8YVA}Xo4bknNq%Zf2;1wd<&3tDMa+{Y(T$pLTn!H)QQoBCvfK9g; z%81gCmr%Jq_->jeTCJOcL@gu7HSkbKyzgQhe{V%O?K{$ROania&Qhr>e*Sk|z1@HO(EO1_~dy4Ws+`<{(~g2NvCsA4!T21v+qH0%hZH zeZv%&!T+r;G8@tVs|OHl>r(hEh3SS`ml)%FBva7Z2sGi$Q9%=?(l3k2%x~dY37+3v z>oam1l==-tV;I2!BeYY%4+!|x3zDgUG(q}8JKDoBLJJQqR}ivDOW?GEiezvB;W)w{ zY}C7}8%=!DrROVMaJk{6?MJlStnMLmxWgP(sl43hru<;HZomS-lqkrJzS|iA3pfM| zxCMl616eXy2eCwF8K{NstCzDE1HxPYTX6?@`biByNdPE;vEs(S6b_&}78p0H3yKiz z$YEh$kZU;&eWUM!x5GFiI8sint=RS65bO-(GXhT%(IMeZVlb6Etbn8+M*=R8eYp;l zpOQ!TszR*i+Qfn0D#h*crI`Nxk} z93Vr{j)lIZs5{+eiE zHA6}cV6M8lx*SgFaoej`ch~v0diMH{J&7erviMd6teF}zh+n~C-<#VuABrk8t)c8W zBL8FXoa@>zmnGud*mci(_vOmGZMuc^|FCt)9zM8IE4(9Di>^^A?AP$#DDl+ik_7fp zNKGq)SbhjO#<-KxrIyD(W6+$IzrhkXf$WytP{&z7 zeovciGyB*c$y_r&2qYR#!1?lk__FpF3pHcYZ#YOK$K0}5hpgVdTX!JKQBbEZ#n=qvm z5tkB$$8+}nBNF+UghnZY=yaDME)=Hel}KbD8iYe_j6&Nr!;7^%Zd`G3S+?RLeG^!B zq4a=n9gtzh;j9{nNJH||@a(Slqc5M;1hvVR^jvmL?God%0y<8Y(FsP0-L4v%kef`#>?*)ZlMmZxwy z3maf0lnIxSj<_EOp&=F;j+01YwbMcS(C9e8JMA+VOja*c%fO=l2$)o(V-F#1X9vU@ zo0mH~!>0SA+q_B_wF9tw2emI?KN@`*@;EP+R_Z-_Nj(c+rkyGoHs8J(b*F9~yxLmW zCmQ%M>iGKseQ?(z52bxh5ksmk*jSeT<%z>-6wP*sf|-_^9atxOnjQ;8a?xkO-c(@G?g1xqx%4O`0nz5UD;`fdu4poebDcH41F9HsB^E?2MA!-R)uRH z6;(1=Xh1md*|m_p!3iYIx4d`Z53#&t#&InVkr$262Hb*Z*mCmUdpkAtH#_y|t{=tH z4THjDLE3jAFb~zFqDYTqH#JUlM^3QI$2yV-Ik zUj}5FX!Q$)!aKAfIcX8SneG5_N(^_3OS)F^KRX%Cf<#IPsq?6a+cgyB`Uw zu=}@mPT?vnX*ULQ>n(wNr}^pss|d-Lr+?aTyWVy*x{4P|r!Nl4Zd23we!Q1pB+;m# z2QJiV3JVkz716}C{c*VO;l%BF$Tf-_{Mq+ejSbN$w=)peH&aDXaT<2d9a8FW?{+hb zF-H1c@BHl)CifcRui(Bvf}ut_*!Pz&{T5NFgv`ZPcCq~c&OV#q?B+i2Q%E28N+4iW zG?`RbAPe&QA(lS)5!bgCEY4n0iy%$#^6KA^voDtuW2yagf*>a+=2Vb}%4$1M89Zlf z$Y3$D<*_1HJUou>9J9~sF143vxy6T$V+<}(V*L}#oQ)*K|CsZ@T`oSXrg8W3!VFon zsRiFKdD2Pbp@Jg4!*zgWJ+8){wB0yd3OJW`XGf zy$gEZWY_zk{C*Ws<-;Cpm| zx3k#}-bR<96iDbkkV+N|rVU(H zuL`0fNW_O|q=rxmK_r|5q)1ZLcCuki*CGWln859d7@>4cMMX=n1PXwh-3<~rc5BBz z>RUV>)d6#&Fc^kXS*NM`ND}HIEW=|nAWYr8HnE?k66=Q?779(oMfd%Kn6B^Ho@3uf z?#wI`Ul)^+@q))dKw#PRqu*E?d+0fXNyJlBuxgQT1r2sg46#vgF-+TcGath{ zdqX-60<4|Mn9%}i|IK`m0Dw3uj{48kQRs)5aKXq0>Y-#3<$gi!KSF^yH!l!S-(X<} zq0B~LG4^THr@IVF7YEVt?33|#kobDXhDJA7?N*ID4ST4K1OJy7XPE>?s0i{IXumf~ zObX8D4(vh$RB+=W78qHUAiE;@{gI_3-S@jB;?)UrKc87C&z1ja4I&Yrjwi{wP1pu( zS~rqtaKR1|+`+UFK+M+(^Seh3VGT#jiMS7nNT%4&e8yC6YCP~9(R2-2FtQMn-@=qe z`4iTEs%r2TBYRd{M+kAIe?iX=+u zC=RQI!?8bKeEF;O-r_V45ROg|ZuM97Z%lR)O_!BJn7${+iD>sR|z+!(FHy3I88#G=&19Qg-^6 zOg8R_)i5oH6Ayvw(p7D{=g;koUFIcpaV6*r;NazcQf|J$pdm>xDu}gChwl%Dachpl z*-xjJSr13djV3{oHSieop0`bgPNmr&znA3>*Q^wDzBT0$OJs?VodBok$@*M*9x71v*j7?FiXU>k z7laN@7}ETr&1TBLlB&caB0n8OP9)AvvUnSQtYSdL-DNz$2J9SL`;({AE>>P#{Lf|f z@l?>Grbh765G9d7_)y$T15n4m))!kmiDVBEC+Ih87+7Qc2DOz2=F{a4jhzQ^%?wzp zT|lm3aE3E0c){(Dwu2zo=Xcg!yM9Iz#diV!>cnO&1kvB`j(Yd>+2<;rn2ytf6Sa%E zdD{YS9l=>vnh6}-42zI3UC4p~kW4CIBSH?Uw#(KVv%-aYP-^!`#7fA(g1mxWURwNc z8m4cTT?9-EjKbouknj||ST7~k_wMkaNxu(qN~q>XBqB%_;D&sF^qIbQY!x!x2%!)X zkq)jYcw#_6Nsrg(A2CVNr$A|}@8tt0!B;(>%2|AS1#sRablD7wm}g>$Yw0xx>8S`6 z<9lJgz8@it1Q#oiE0(ZKGft=N3Yn}?VsWH!xly${$He+}2XY<3N$`LXzFy3VnpeO( zh+{Cn-&pXX#)+gZ6U2JHR7Q0%VRKR?7Y3CS6~kW*+D*KK_YQ%iu5+8oPO;3_F69S+ zG+}R4JRfo&0Nts&uAG1j+>OmS51?`Os}vRG5JBoS&0gj*OmsNYax<@{!z4WOur;?~g$OUIK4Np#3Bd_{(j66HBeO)APR`4BBmL z*iFn3!eTA2!F7DQjiI+&ogqTgwFX!FDI7c?z@Be9D{qqL<>Y?k(xEN@dv}P$_c&+P ze*fuY&|eRCR3M-XNAcfqDyC8?fh zgvK|;EPiGNR6RGEFDXPks*-!jcKyjJR`f$M=_{}%6Oq2u-!FtpCT%iaL6YW;sKy7uxp_h zdHIfe2$}P{Rb9b4Td^-Ku_8Ls z&4}x*5z;QJHb)L2J>&8=D`uxink5NJ5ZZZ-)4T4xo2h8Hjynaxe7v)^`9_j!CXM5RdR zP}CQn57}C(==$+cC@f9zqO$iX>dI}J*W=P!VZgU2$L*+WcEyfzDlVf*-E-1KenWJi!u-WR)vyr?CG>zK z&QXY!_-ycY{l_kuh5F^ZFDkYs5~!5s-Kvkjap^v8FFA}rka%j?nkQs6L&DckZF`9s zzuak4_ZA~bTn~^2L1=(${%|{O29FOb?Jn6k$g@J1a&x!p_0j+O6vWc-PJG&Rc@!Yc zG59*>udVf`>ZfE;q!$inAaC|#k3EJDZDi16Kg@>=tLK&9`lAB(MtSriI%9u*uy+}* z?=W=j0rIFrSo@5vKr&z=I8U**4R|Ff;JJi0@BF8jjrOsi9vsEJfoj_V@*4n${k#gr zTRotRGJoc_i)Y^T4eQ?BsJ7e03@{_zSLPDPGCV6%CcG#Vg*9qtc`uY_E?lU;nhva4 zoKfuHakI)xw;aqT44z7G=|Q2=_eN2mR_L$nU)Cg2MhA&W9KqAdhZyc8@lwhW75GA2 z76+3bT9y*^6wP-xTVK2=S>0(H_R@|Hb=}bo4|Nx^yOhu2#Ia|q^^k1lFJm3dr$c4+ zd?rE8ZL^e<%eU@2DHBoP(sLr&Lz4~_r+49kE|M8Fz_uq}cainqU`hN|KV2ITYKn$h zB-_?YbfbTGK|`19qR9rerjQ4PTCz96b7|lq9TDB}00s_@!r$9a3|;x8i;B4@|8QoM z;QgIJIE?Q5U#?Clg&c7{TqLPE6zQav&5E_(%wl7{{^$d}$n(K-Az5qyrb{!AE28gE zMc0l-hW$A!iAvEMKNz)nJ7dJ^q8QcxCqfL}Es4tvF7SJ;Z($JZ@ej+P)q~t9ZKHW6 z)f`JlmP!Wpf|{JR)A`%#Wxo$c;#W9f~7~T-9<6D zc6ez(F>0N2g;)*|1g((1iZ-9d%JYW~rEic<5{Kj~1R7xpg$zsmYk%9x+K)-mn+|&w z*S9p*m+UBQHuv4C#ytIp_Q;gV!iBlSb>pOL2@lh@1EUe)vSwkPh|9|rf;E)S8QWcq&6H)P>MZ3r&f7a7}T z(ai*7xebGT&Mj~DBaH`Tzzup{H!F3nZ|dcZ@=o=0k3NQUPaBIm4UGM2%A_S~O6^CX z;tQ@}0n8hJ^1%lhOS!s;m|)D)=up~m&4|163u!OY3nO)HUcH8Ev`WACBJahFId-c~ z^;|OCSa!E6JJ-b{%(8=}?&3C7$`NZ8u(Y|}Diqbitu~8THJ&H{ueSPm7T#o+C!4U=ZHu%y+RO|NA^8s%=Nc{|L zRCX+dnCz0iZT>w{?Ogid?^qP7Ifq2bu=rjUc?)s3HEnzSEtSzZDWhzDr2BAnn53xK z@%8K1<>hM4`*)7<^2LDbx*B0>zo77VEwcG!)j8^?waY6J@)HLG@>fQ%B7~W5u4|u) zK2Jv>qoZInq?B1yA*FjgD5G_7V@+Y?_K1&hy|dhzCYEQ6-mimhc!bKgFFT5k+XXEAk@U2RvdU;2kUSnv8ZbJkX?sNPF*t?es(#p9r} zS%eF^kK@A)2+D|%U$0F0awC4Wm9jLi;ym;IJq zm#FZytrVWxPYZ<_XTVBDKuvLRab;!POmN~abmj$9$C4K>Oh#j$t)Z>&e@?w{zx<>` zs{4mUSC<8W=+U2L?(HSdL(oloxd|tPV~Q|AxG9m9P_g9N0NLdo)E+4r?s2Ry4XrA2O|m&O za4}7(RI1yk?-cP-BPYUBUbGSJb)BtE5wn39o#$1PdPI zminsM@ko%pd8_^{YG}!6O&ORjcbQlTh5K+STV$PBOTtfAvzfAYr?d z1c)E5pupe)cbDwoiIRblq+MOQ<(}OQ9=dK?*=-abMYrUf&-|-dy>zUEoQj@9`UM`v zS?+S#wfma%cg@}Mc}tvAv*#D;)u8l#;92%&EZLc)=ERR4i}g>ib2d6w8rjMFNJ!KX`doN0 zkj+;y%V*_de!5&g;TM|E`|=cze$Jh&?!JWwd3#1{oirsZ`7oD!s27ELPmK;hX203x zF^Qr3l*oXtx?M~13_8%bJc}UCJWe zUl?R*E3ogA!owA+bpp{g30XUCbbk|8$5X#h-)pjobTK$`_m@i+NtwCl_7(-D-euS-hvlULldrrR9G-%;$Rd}J8X^ITMdlP{Eq z>2S*gzv75+CKhY&%1cDG$3~s$FYm*c`qh4xJ93c`HkDC7`>b*5bEBA*)2FFT*_nRh zgo1@c4wmP&lfu?2k^HZW2tVA^+vBOXs7p>hguZc_ZJ}%!<)nlCVg2}@!iO&T_-m3s z80B2Fyc^po+E^@^=J|96J>0A3+QYBO9_(v*g`1EQLpJ3tlTUqJG*+05p$WA0q&bXu z*x5&}eq`kMtSIacM=mC%Njq*_REpy^j1Wxt@ZO} z)%2;ZybG~*dp!2qIeT>MS;;OvebIH{c`cQNw##V!wUC|H+dHkn@40=%cw?DqKk zsXWW1Sc@A^@ff4(<9ix9nFx#aqJMT81+u;k6uD<$dxg98BJ=0RUeC0Rcxhen34~pm zj)R#IbTS>GMdY9od#%&YzM=Ujp81&)Z8df!1(F$(-kxMb^>q7ygTdm-YR|MCNGI4^ zUuy0U7jEs8Jq8e4Wj{Zk6<e!c+TobB(*P7t%|5ta3j{Os zsok!A3OQ;_OHd@2klpqm-P+I^t!m^&(=w7Jc&_Ey*WY6+Z=Y(Dq6{fdqNfB})r>tZ z0>fk~TYOBr+n*Cj3wJ1$*#?Ked4djLVmO}^&H(01yk0kez%P*@lp3M)l%W-lS7nYy|Gz0PcAKnLiEKhIY($QlV`@;>k2)iVk>y_f8Gi3`G$EpXe2?*mNg~(cjV^s(nv$ly z|KoW;nenOI>rN+kE4P;Z{OEm+n#`$-1M|nbF;uSiqicmbB~xvrFRyl**qwAr5BpN= zkWyyf8`FVtF4CUZU;ih_?5o=4R~4P;?6VPH=pVPAQ~6{-KAWUHM6*xY;ivY8*OyZS zClEczs^eh6<`BDrwk{V-m&)fFEht+JqMD_OP2c=4LB}{(u#jI;vUb$4a?%Rgq+T?wvs3|*< zRQ|c?g_0~5abeYWUvwv&ZHcj(j8*eDC0}?ZY$#}NbIf^SWs-AaBFKsV>cZ`XScfcf zc%jQ(xx?kwb1n;}Yz!<(q%TG-Dg6#qN=&S6bF9a^e%l9TnQvl8tBQ6~FQ+|uz=K-Y zcXgDny&!e5gML(#pc2|=7k}@|z?a1Ynz@8>FTS**%O}Tx;KVkje>I$Fq9=;8zixjz zNFY$MoMO6s1o+Y`yxukAJ|#4T;bB-1kFK?jHp*Hov`Krly;ZU{yWiLWZR)O&JT|7( z`sEfU!B&c+E!MEgCT&)2gSzigq5ndxksrD1M4Q)9YN}9I^>ah>{4g#@iU+4%aC|{` zxMY4NgM?PA_S--n^<_iap#HAX3cZ;4oDw4kMXjlOjrzOF^(f2n5v^DC(ptY7g=U<6 zuHhN<$mLbdFnU2>1scBGk4Ic_VJDlgHxdCPPDsVKQnQ==UgYZ5S;%b^UE|% zhx`Gn`v!MMEZC?q*8(ikeB_(!&wcOJJKg2qOH6W5UA2vTQk!Y+Co|ZsAY2$z`sBXe zwLBMYwbrgHvlUNfp!0c0hXfrJcQo|+r;I0Q+$u3ySDvha28wD%Jd8PyEMYmedN_dL#F*& z&(C%d2>~J@{2jA;Ax60dB4nJAjYJPU&DLEI2F(I0XY5J#>{iM(!*|$pNZ!(# zjw{tpOIEwQ&s$nKUFJ*YowTzVIiJauygup$IeFad1N?=~+HM!TjCc#4_+ZkXtlYcu z$ksNRVcx@F)Y>J-DDjeH!R+ggf*cPyyuAw?2u6g-P~D-?7~Okw_J=!cbhfo>vr(44 zbM5n(p}SH?b+!THWW~LGXR4PM=p2%B4eSmlN+xFYXZ>L7C>^VvYrn3g;*>TiqNHym zR$C(*R#@5oDz8L7R$SKmj#3!ea^P`X!M1BY38Ut;YiS}Q`@gh&=_o4VFXsW5OMT^4 z)&+mRDX}(U?9~8FzL=WhcPYi_FQ#upH2-L_518~DFO$D~VVV|XcrIJt6W^Bao0=T1 zyDfm};u3SFNY2s(ds}Qo9nMF+K5N;~MnRBqI0#6;6czTU0AD&U)%yFY5OjQ^%HQRN;8jGe1vdW`_ht!;F86H;o)3l$65 #KAxLt zlW-=ig5}M375#s?GFBmN!&l zOfN`%0y?4}f6u41c3@7ghPPVR+q3m;!KZgc)@!VpHU??W{iOkL%>6_X%!k|a4o}~V z$uKn6%eHQl;VQ6LW^1*3Db7|~6P{MnOCMf|G56;{4w z7}|R|ndADf89U>PF|Fq6j-9EK0?`fwU6QmUhnXZqbE_N%9#6TKH}cmmhwv2WcV2Ei zUA=5TCd<)+6Sckf>)RU`?UWZLcj;c;ChDWEvyEI4=0Y=>$b3^wB{{6+Slz#qHJ^JU zfm7f5T&nm5nR6FwIwYw*6!k=YimqfqV?)myk5~@m_zcV^^3Wn>v7HQS7gL3n5+GzF zoZ?}iUZ7f1i#$+O;|dG0(&FSdWFN}nN>2b`Ufg?+l}nDNUcr=6`r-uy+kRd3qxrCeZn{DOGKu->;D*5qZXfsp5?88|?K zn^};LcBexQ&rlMe(~wO`Z`OYO;SkwuF9)x*S-n&O{AW=iqk|!nB5UK02PW-wLc-bd zn3uW@x0OVVVhI76`C&RCM!|gO+>UE@=yadcaGVAzgmRwpAk+4}MyxXCm`6O#{)+v- zuD_Z}meVY)DCcec!_k|@e@Qt#^Ooj4j^5m=oIr|I6PsF>?&&Ud@c{6fVk)c80~%}k zan}~DC49+96G_qE>sS;Zktnw?XVi-OhR%$279u4*hiw?vI;==79*gNXn)yVnb@k*( zn7iUwS6fGJkAX=``q&&RA1K^5eKIx1A%8gJkdG0r^`eNr<0C>CVEt^njLq3py|i1# zimm--X-g{m^$BwxL)D{$qiHrSIRhDzJzu!&4x|ak)TjmH+OgJeE9~mCBJAP7A?R`R z{&DE1&H-bGh%BvCJIkO^rkj`V*jYwX^p}}JVdrZue104$-Z=NMW4i6qO|9t%`;>F6 zn}$Dp(PeykGpy^l_n-dAv82zeB24zbx-9z4JGDW(Sn4LZF9%4bIpQuSX;TdjR0M#v zjJ>I)>Q>t%n>rV6WPWgA#G>j7^$`J_oP5q@zV^zHogya>7E0pY*xAs zd?WsDNeTFWy1CMzrmiqbtJWf;lz=E$ie)hn4OIlBNQ{c?0?L*UM1&F~B2X5ifB~x@ zh-`)gODJHFEr5h2DFc*1feHqZC9)(?P?pN_Kx^SejM3fzf6-Fdl_``vTy zyt|$6EVZ{F4!gpcPy0%<)D=A)=n`qkd{KOI#cy~d4Sm{l_d8V*!JNDJN3Z>L^f@^5 zN39r1iAO|X7_L9vResY?BG$qKlX+bHm{Pm@GHF@4INjY^Z4ATOnL`2_gDh6sIw_az zgtF1pooTXN`93=z`QZe;S>A0c*PI!K%SbQq$_{Pv57ED&$X3cn&$ljQ58oDC*fX2l zCz$58=}`s}qYa(mOhJeq1wY+X_xh(E^&8+?v{>Rr%4O87{>J8!lgKIL^nnfo;vDJz zW?h#rkSAR#c&F#S>If`cWEZ(nLvveATnTWd;nZwdWj&I1440>jN>S!xgbr?wC3Wn# zM&}SzPF3GNESO2vLVFMT;O*zbD5F7?S3w6=-k3+?V1(GO078cgPnhk4>dPL)9KZQn z*6$sUM*CD-F>0hXI}-Z*p?q%VrI*<=X&IbT?%wU${Yj~t8ZATX5+6`_!iM{u#~;NVeoM@>-xp)UlVHP5T5edws5hM@_nYiU|0u4Iv&8dh9W zyC-0&X7{+M{@TNLsiu?1ye}%SERP`z54aw&c7^-94J6n3qt$m-3aIf!KJT?~Ys3#K zRj(siem2e`uZIIoM$z^`Q5*1s!NbyDMna>vTZW-?vnM*X3=2I~tC-aS6OTuiH^wES z3lsahD-tIPz@G*4kdN_YdB0u;8=JB`FL<+CkJ2yl^pmYlCCe2t3t+0?cyYdK`SosB z@wLMZJ(wmiHJ_{_r;SbXOU)i}a1co^x)Xx%^2+sg5p4zgqv6aAK_w7}&hfZBJJ?`u zRuD^qGcC*C;*(4rlkifgI-~?z3E&_7=?natS>3^7%8+r#HfW`0Ty@7@>{qZA?VfSi zPhPuTCLyaUl z?YYy^S6!pdh#E{GQ z!QivS>nuBqCYn=2WFJJGEju5KI#;v78dNAeQtm+lD^CB4;wWvLhOF)PJg%Y%8UzLP zz>HE8@!vc?9%J9 zbJe3D<>N+V4+u5+`4qq9f!wBtQl?P!W>><8YWH(#6-kdA%7jrXli4?uaJiUKiHDD7JTBYIXV2ctrwS8M~b*hSU38mi-PEWA65<}D|+IQlB<}c}mcJvTu$VHR7ND7wO>zUlk zWbD#mq9DDc%SBaB#`De*(8jmGr4?*-7$m^=s64fXD@j$wusn~`#kcUeXBD4`b+1No_roL_oWJA8kXO-tm#HEW7rTlSc9uH=KFh2670HfY7s(EWMAh#cWu~ zM7&nM@nV}Q0>b*^)rYh8jaYbew2W!pwHOXA|lVys|t^##fFp_{&bqi|Q9?Xx@kKTQk`Of2VWA>bcO+ zFq9$xY^!v5Wd{H8sOzO`u38Qjt{x`N<}{8bR`#xf_U5i9Ld4JwKk zwQn0ubqBeySjC?Dvm9*ES>Wzf=wX|Zmu^w$?d9&3Ti}rroXeUumG(6amzkNFmU(sp zpE!8YLf+Z`o3^SM#kUdK>v;PI6;^q4R+SS0{MA@PM!&V+}Rg^M*ezaj|%cb@Bk6{as32( z8~n(>fJN-#c$csfe*D~yqJtkFcm2P-cmh{bQxo06a^y(uJ)eo`>F6bqlvl5?hXdG4 zr(P8q&{PQrpzl;Y_pco@c3tr>Jl=3s=dFCLNGrz%@XxV zOifN!I?j{e`O-7ZF=ArgxUxZQjn|r+R6X4J6&KZTTP8SJd^PRSy3f&K+Rqav^QTh3 zIy~&?(mIi}dvP&LcjTPGwC_;BVJ_Y zFwN)vD7q8dC4T&p8_yuJJ6^gJ(Bc2=Nb<>zBZkN8UQov44_NL>hirK8tU;(<@d?zu+wLHk!%n*4tm~m#gn5+WSANZWxNH9gtj1X?AJB*WlB>et5i5 z%XmzvhB2sVIZ1nNW z6E+1#)NU9XI6vTi`{mO@5X%W|(hFAWk8=TB4`fCqS}!!@8g#b7jiWi)BJ7eu{Z0g2;^O=>~TzvO=GsDH(T4nCK z<(iO8PDJHf$L>T%4x;X*$xB{4=EZI&HlAEH7!pr5>fA`wi%rNHbqQJ@`V-n=oRDu{ zSp4M29b?^M?$|uJop)X#8%eXF-1d7W+t3{q)b&eR;Oe$}S@M~K;uj`Tyk=vg2KOu8 zUY9PNCw!s*o;#=1LGcuPv3oHl%Rx$)W5RNt@VLlgReB($oo}8%>CmM)AO>gV;JaIg zRW<^DsABtDW=n^xA39$CxqRu$?1|YEbPtGwS3BBZJhNWBQC25ouxyNkQJB+opZM@K zx>^ymu%Gw*!y#ds0CwuR>%9D;^6uu<+(5LlcdTaE_JG(c37b!2h+UmcKSPKddyWdZ zy?M;b`{tWkGAii!taR4fPY3T4pV=7Ka0sP1K7YX)Mhp`xB6+-BA}A7dchS5+PATjB z4Bz_^^a_vhda!D+_V(wj%BHa%>v>N%B~vbiQNs8?U%Yr6-4~N(xxGe-H~-toRhJL% zy-epX=a^-G->~4gd%yQ+Utp1TLR??eoOG$>O@BerpPmoce$`%SeP7D#ZEAhPIDFtz zb0FHwJ~o=>fCbp(%I8gezHwvYmuI)VIKL@Xd<%QhrNL3s;mcix2IbJOiM{%P*p%fT7!vKkkHoIq&{I{OSJ{UjA=!X%8>j&BH^;AlG(b zyk+(%ymkV=oleEv+`KkMNJvmn2?4x2kM{l=5u@VbBf-D$XhNsL#l>X^FVJ+1mvnoL z#VzI3$bK5` zpfR)7_u%cOW%u21NwiGyyDQNG7f+y4-@LJXN-sk~A`4B)*#aSTyA7MzjaAxbVF{#j zD*6*DdPO_8C8{83yoc1|^N6{rv1M~M{Fz^Bl>cW`HOD03u<9KuN=J~}?E3-~oBa)b z55-K#ei!tuMpei9oD7Yo0h`m%ysS(lxI1hP_8v}uEw~}TgO0UOQ|J`fMg?`n5E+zA z@6y1G|NC|Cne+tNMH>z5O3n3)q;_Va&hLfR#}k{mx4LJAL^2D4A7jWVdPn zb=fG3Tb&=mBbo67>&o$jTh{Gsl>y_O?!G+Y*UnStwFhHUBY$C)LGQ^2b&TUGHI6C@>WbkpL|I`4YW>YV&-U z{~7NI^H1?PLpkw;lFg1h&b5i@)h_?S=Qdpda)w=dwuXMBbFMeSu3(U-wMs_sOp2vr z05g-i|JwMdZmq{<`Mob~*e_DgsN=Qv2mz;A#@6ov{9Z$CtC}wID~)*+&W(xbrlrzE zdX+iDuE&%FPHI3uvDxHmrX|>2lBB^kb^e+vD%Gi*HVcKhlVOLrBMr?gzq+zm;ilz) ztOoX3e16f``Tz@2X=%fH@gBM*XDY+t-~0x(V#93R%SA)u2^%-dqleiUPowPC7otDx zBldg~%orpLn*Ot=r|C?USk5|aSeR|iw=Z*YaqLsilDl>7Iaa6lJ|NF(>f*3zogd+F z9wqVoL7)MqVQQkB+s1LMV^Ws4@C)78GcnF5iA}OA)yMa$ba(j)p{xW^jUuMEW_QYM zoyV_|QkmU*E^t}k#9D;P_-P^rrY0?{rX z8~)kY`ID)#n3SkmS8oQdn0hcTpZ^tky0N%CaToa}jSUTl_A*p;@4tt)r?lB$p~Ltp ziU66nz5 zdY@3$`C8?4)l2^~Z1zu%rVUONH({7>toLf_*&mxB%uTuuzp6H~B0G9{QPng5p1pTd z6Psb$nhd55<%*|@_3Lxf2N_h7 zEXAF%XU{jVPo+qrpYE9YQKw8b)z8v5%tX^CVuJ2JMh61s7o;db15L}lUMr3zHeG*c z^5zdxo!}4uM1UfkfGX+NOk|dmhl7Z+Ac`BKG_N2hKy;F^u$!ULch4t5L zXZgYH8d{%~ftB9U=F$(wt|J=?@DA>d% zlAE!)A~_{H+%Gdwp(&|QlS#R!pJ1jg`+{=!ABYmTy_Lnn%UTO?$CE=_jHd?!=BH9D z>o&wRu=>k9*;QV$b#$0VSGtZ;4Q*y`qAWI3)_SV&`!q+gddEfw%&|?#gRMSL0?jIJ zd7rdQzOO)R^t#8sio|iC)REsK>~i?4aIr>qD#XYZ|Gviml2NOk) zNTRQDIJ^jK{eDgZD^ib6bcti-`b4Ml`ntHOq<6(KUoAmfM<+S+;1EigiC0iC`DBCE zIqSMP80CQcMIQ7`_GG(K(qT3qi3*?_DO_0*RYe{Tn2AO?iCqUKI!qZ- z6jQO6IK6M&BoV&j_o_)e1w1&QFNwvD1zMSo;MgQk$2#%lhl;1!8B~U=46=(0G4q{onT!VPm*r1d!LBD;rLSG03nVADDra%mVmUm6;gASR zv{^?t`oan9XgfjqxRZ&v$C&V?D1aOh5rO3SRZ-JnT}hOgv;dLoi+A~dbwxMNDzK}^ zOgLcOUYrZGkwB#{cbjBL+e*~ZSQOE0jPlbf-n(^;yoKtkx^)q&F@$9Hsf;&^?oUxe?z4PVY zq&k6nMcLu^;-gVPm&N*xoBxR5_4JkS)G~m1xcgWF0gdf@ zEb1K2Omv@Jx^6ODWe6Kndl0h=vq}Z#My4O{ax(mUPjmwc4b6Q~emm7i_}(@b5UkXt z!k`l7w_!2k2HT-YA>E_W$;$__$3uBh%d&vxs(D9P0ZOCtT;w}9mh~`=2F34)3dH4C zdN>y#fE>iSyJDDKbO<`07`E^)KED!;!hz|izpmLM`y3y0mIu|{7QC5dO&VL;>f{Y;3vT(Ir%znN5hOGa?ty+L!Z#x0vefn*o%Igf|mUn4PirWI=y zCMsM-hxs;r+ntIh!h?T;>{fni)_)?c*RJJ^f=$M%(2xl1<_uF2{f$J=)?nRo$>9r9 z02=Grr0ow8D;^O4`m9~>ZB6}*7CCoy8$6Gv{|^SdB%P*{$*-VODV$u>`v#-~(9Y2i zw@m>Fx{nU6I@*d14fBW%af(+hbe2kGSJ7nK^vHZhrKWF`kSr|bUw28OSrdlvUYANe z73G_L9mDLRvgZ?mYKBrv?;k6FSPKfZE4o%mH|D>7d+z7$0uzPPN1fzXRt&rBQ0iUJ za|gZ_?!M*Li^K@oNc_hwyMC19FktY%X=)d~8<0)gc`8|3s~r$4koJ61m!ot)j1+u?agA@ala(K0(k3%+*n`(&+yq z#X&sHq%w9W>um(Y4__7Br!6DB<6x{D9&c>_vn1PIMQ1fJbg3ZmX>!i&5r_p|`kX3J z<2$C}3E5ekxax}0(<15qz%MbnaR(yJZU?$_uPx(nDPm13@huLP8y;rA|<%oN&C2$OqrvmpB&9v01 zKWqGf}TrEp5)qptbv6I<1qZt>wKe z{kze|8!&7^I%1q->PIEG=7tWY<7oFG_*2ialcNhY1wO&{`CCN&wDMc~ka>-uls7uM zJzwdpR!1x;B9&g9m2?=VNjr=HxmThJg;{Q;xl84Zk7$bBgK=2T$*N=yJ6o-kh-rF( zqZx8h^)t_Y625B}|IChT^ov!%r>TYkbZ`(Yn1slG7ia<@6L}BqIw0r!;XZTI!+rZQ z1aXvq<^xI<7S+L*An6fyL-jS1ND!Ns_!SN3=Fd$Gja1 zn9qA%a_;b{L~RDU^nQYChS?{#ORZm+5omeVaCC0Ho5kfFLfHy_mf$3*lE49@#Q;)e z#)iAheJt~Q zJ$|Wz7WIY766l7jDx7``M~);HS8d#_$1^7)j_p* z{r$VRG_WjD8CH1T=Wp8{kLUnQIxz6^QWdaE#I=m6DCZ}D^FifPkX-RfU1-E?FQBtveB zbrb&Q<7Rz~)}8xcvU);Flzf8I%wEX)jM7QYd#F&)^5*vjVZXt|cv)sJQcGSMcYj4ykDiXfGo>S(p|v&zUxT7j1L=J&5X`~K0si$2k^MOfwr&;=65M58!mKmM!{7g5N)V5gU zGtJ?kNR4L*7@~*o+W`NQ`a1E{c?Z={oQK`b;*RCI^EN4h^;;O4rpDo)TkPQf)Y^Abr z$mS0N4|-~FhB8<&Y4?+$_#Tgw3`9aJVc$NGG0*|b@RnJ%QEE!7R>1KW67bHZGQI$%>zd9Nsn*@27yutsX72)g@Kz9%i>7$2<7s)w z^_*z>Rx#zmd|+1{ZD?-s;WBnON*Vj`HMG>+V^aV$nE}qvKO)psMl1YRoZ%(B30`rZGGzWz&zTZ{zSP-1dJD^}{ls)<}RNbG@qY)_Xo#Te^$r-v59p z{?v-MGLmnteBnN#FZ9e={-u?L7e3AY&2K*qtXJ7_%n-FkcSjD?oi)StDZ!lHD=87N z-dW2JCI+Wp#}ja+snJdH!wJ^#_`+@s<%Vntq?@A>JQQi_*8aNa%FMJ}NxuqNqw~o) z;R{A;YN^fGB`I2R0l_FM2*N~Gl|WHwMlgVW;F#CPAsWQvKjJT)M;&Stah6{RK4hTu ztdwMZDowK+PO_=_V;8xg5x^`+#BsvAiml`V(Gz0|e`orUsCXVluP(j0#A)A8$X>0X zPqnzU1^(Z=qhO}ReDQ?Cy>ij1S2G3(B1xL6!kSdi$xLba3JK2#P<{BfR3A%E2b;Z0 z@r2X7DwrUnLL}23I;#_uAS(NjPUUScH)V~w!m7vbZ?t!wK1GhrSfwIq-K>R%luqIh zLECpcn#3_#nj^y*adYpSu3fJz-@lix4L(eFTL@=ggcWwb;9rgCp5gczFyCiWXso8D z;`U;{pH0$Whi4+N{4=$^8$KjotbVcALZ&1X31W!qSFT)%r9cwWc&Hpi8N2+;KuR0r zdZZ;1rzVG}!;F;(MNsXP7t)G%gGN{dC?^}5 z)tsD!8+OppC>pW^rmMHEI1(T#bGK!2aN5mR2gEC_v~{l+V^-dn&l+@vyZ{%)lrD8z z=siT^E|CIm_Z)xngbSS-4U>sD43jZjr%Ky$T~F6b8%G$$o#Z?71cxv$L;P&AkQALP zJ{g5KGG`@DHl`#epIR0dT`=X);MzuWLHd^H+_8?6B9QR>2W~B@zSTGMN`FM9CVkLM zQyKQRnJ`+*6PxR!hmbn53o~@}GAf9dC0v3RiYlt2Ia|5soy+6Xw>SlSb-BqYw7uh2 zg{az>$pxv)n!UXc9GlB5$*~_IX$P&zIi-A2cw%CgI5>f8Ue3j|)KEjYFDR?$%lAmp zA=@7+hSn_~AWq6<2v>$Xg&`I{$P^A-v5Qs%#O}V~9r&7>VMYzmiTqL(a4Kh4(nCiV zAc@suo;HmeycUg0&ChT5>&qPw7fpMm`oW4SE-GqE1N!j9fnML}I%QweTXndl@kb>9 zZXEYRdz`3>QfW^8s1^|I8n<)N+~q|zXVC{A%lfn&w0xeKc{Xm3}nNKIAoy6 z<{&7RBrrGCGzW*AjqB+_upmJ$-fMqn$VyIq-8Iu<2&T#03H`HI7OC;D;%0M%x}MY8 zS*UXuUX@O_?EO@-`RO#|Q%cX&u%ar-r8$kqhY5-u+PGzE;`Ngy4i=pntpQvl!N#yi z;VK)Chw)H=_KJEA>4({(L04T!qx)k8qU5(z6dx+iy%QE$ePkOH>HjVnpU5BdI54RJ z)cV6fk))lC()2uqg((_b-7{*wo^B6Ia}KU|dTSSGc$8}KVmu=AJjUUjqm~`)wge=ptN6X!5Y#{I`g@|P z;#6|-=G^#A4Xj+Ri+q~uJJTUO29=f+dzEVS3b2>*H?MebR`gIh#)T2F^4?e~(BFot zhoAQ-KM#5HPe<-uOV#4FHNQ$4ur(l|N{c$!`rsD73KqsZpn@NZptt;EWo0q*-WITz zNR&`|gB)M?srxwzKYXv^?B9&h!GHB!ORtY*8v!nAAAy%Zg5ia``i=}%o8e@(eL$10 z%CC&86%?|1imaH)Cn$~29E(b0pVJTpY0Ej3bl8-^%>~5i<57R#MP>4k+g%rQ{ql!s zV5%Rc2&CxYwZ2mt*wt2n*?qA_AHo3t2HXaj2kZs5Sm3NNHig)iL_ z))nxE+ZDuhOY|VIPLk|a-}z>w3<2xp3QEDz_2DZBaS12=eOI-J8L!^v3(Gl{rrAbY zUpNtSUIJyeaU(*|8N3@}%9I7h6BNQe!k~=q5LM1&A>8Ic&;CPBSC21W_n31rsF0$` zqk*GGpv;meE4N-8Mg-aK=a<_9(?iYWI~`wyF*iN)FYH*3VqwxS$sN*NZUGi zfCmlEhu_PL9Q8*jyUJSaH!s>gOjloAyxaUYk6*j&JARmle2lLFhY_7}1)1IDxgqZO zlO1-zjuy54y^KotO2T2Ax!;yswd)7|W1-^R3CGXn40m&J5TSmA!|a0W5EPGbM*M8W zXh|J#LOTNf3H1cOsWakm85D>jF!A_vrkL0qJC(xRyLnRzcu`6b!;b!4e-koNY)}3( zIFHWPa6O~yw1Bs!#s4P3n3UvWJ<*#QKsGcI*mu*q{C6!R9PU?tXJ@FkF#Zk9*1x;l zdBRG8!?KldaSQQly}n+FvQm0Ryty?IwRS|)+S6N4w{FgPcksGRp7y$9sp%bvj6$al z2MG-)foDRsmpm9My72^3!%_nr>OMlGBS!v8TX(xM`AhB{y&Dkar&UikHaK1G@avT^ zA85zff@d@1?l1q^4cgXR5lJ5DM4)*oFvgR6_1wc}a?%g94sa3P^=(+f#{18_($*4Y z3gRjS`B}#I^d*OoE=x$|!hH+n@=KAEed06Jp8DEvJzm$e<$rz<+dk&oCW)Hrb=1S9 zK4r{+a+P2PiD(H@H<&3>lgx6KdQ8-l?K#8JFZkUwy%=)g^QpuVZ4eZ)WumREy{2>^ zwkm%h6}?a90Uv5%r?ALU-;rtk^YLC^``!T_vBTf?peLIe+2aY1ORue+=RuM^5bXvD z+Qq_wpn!}CQ1E*>sYmK)R%y5nxIU^8ui#IVy{9n23vU)}*?GtUoFM4xdkvk3iD_0Wz5i~2gQ6a5xBga=cH-<_ zO9?UEPoWn+m-vY?sAw(pdl+UWGP*!rC6+BF&C>dpzfEUDh9|Wibm!JL>%3P1EHi!W z*LR6;r?Yu84jR>PUU0aAe8|^*pbT}^<7ev-8n05S!%w-4B>H#uyyGF?W~_|HIl|u5 zbH@K9xDLgqn5^Zf8Y3E~kYAY!?c8rD3sg*uYOu0>#-q(1dEeda8z?!F9wjWn*9jo-38-yaIF{dH}IVFc`tH=KkuOn`r&zYmS-k?Bw)U! z!#Z_kyssMt4GgzGYH9i<4ef!-wbNVDS(6{7H7HDQA>RbSvz_ja#B}u&$7OhA&8tDzrHhmy;$lN0wI>Hg)^Zl3MQG3?Va)(SOY=hfdEA< zRfiKtBp8?EKp8vUJtwk1Ua}ohYN-WUj5+pocMS?kY-<1B?BmGIy~B)OFAn5I*}b(U zr1fgd6hg|o`4D8EkS%cWxrTfCE-~CuzGU$*WbXL}e?Q1` z4a#<;3M!TA0F2&}K~eUPg|32|Yp!!XZ==>I37U&xw!0*NQ~UpT7tY)?!{90NDe^&Q zmR&p{*JpIuQiv-;Vs;;-n>J2K9R!&>Q&pKKF8>=I_IE+H;HBP9^J3S+PrbkZ0>_?CUX07!uf7Iushr}{< zuv0yC_w)B@am-Dzy~iVnw10sndZ6-Y@d5;rX;u7Qal~<|L|x5X76y(vPgFgdl0HSb zVY~5jXCRqxq^O6!2_Zd-k)o35mi?@DJd$X0k0Uid5XyNcUS3&D*L$3*)NIpSa;wuY zSO;#w*UR51s*o1^LNgSA`hy1UY4do=5TGe%1U{d-tJBZ9;$8=u4)m_x;Nl^t$H;`9 zjmw$QNgN=!j$O+lNq-acgf-?ST?cL^jI=&{}FDU@(bY6f5G5?)DHt|^>sN4kXXt^Y)!)+FxDn&6_mB*ENL1+Q9anh! zm(8+Yq1&W_5YY*nK@wQ*15 zN<+<{a(U?@6xm)f4xtnCXio)BPfwfIMpr{NdKx`i72PvBcf>zu)&Q~w{`%R)ke*{d zu0!{f1$pp+Y{F^ZNTgKFS6&BU&I$YS2S4R>)mikRLx)lc+FDwXyyz>@CubpL+q?UG zroeatQkB@SXz_^DYR1!7ba$--9U53={gJd)P<8*k4`U9cAJP`YTsdHi)z7un^TL8O2_+*b##>GiXL6Ib(SdxR`Wg}%=Wn52cJ-gVgkPFc>21~SZMowR#Y;x ztZ3molQVnBnJrZ)%XR2Rb|NqphHMqg$xKE75gI6xNT-!9fkJ-lsw(dI@$}6ysHa5n z*yF0%ZSn)bLniIK&@FFnxAiP!~(xJGKXct;iwIt0#;B@Z7o#&syi zc`1YG&JwlJENB@(h9zoqQ8 zS32e56RlvA*Pyi zfrAw0;A^6{l*@KB1xwYr-e$)_i&F%lsOoopPW8_+VK=t1>8TJ09V?lsfdJC!_2rPn zj(VO*KrC3*zz+Ol76i+Gef*x|!pVg)M_0nSp!LRCp0`6AMNy-Q$uE7lD7Jc91E6)p zs^cG1+cU2Ph|0dZ(XNk1Gdlab*xi&1{ob{%nD+FW#t|q1t{Q;Jsr4nli6^9&RPNj) zyy^he0kx-+r60r$`mNAer2zHrtn8!yl)ND}{7)U-p zh4Q|_3!3ply(uhu$TuGBfp28?|1pOMQS9_? z9^4Q5-12x~b*feBaDY%s#3T2DM@V7fG=&n>yh{b?n+08ixye>J7vpMHYgC;RhbYja z`Fva7Sbi78%yfwxBcK;Ah&@TQsmWF)m)DG9={Ts_W6(fn)zWdjSE%uAGuEqK3G%B| z%zr)q8w$rqv)sqNAGMP#aYN6+#1|%ofvvn5;ldlfylqHOL=M7D2D)nFe>B{lfTdP?}z6WmX4d&35sS z(UCqBkl38UUBo_G? zR4g_HFe~VczZN*{wDHB=swLz__1Rz55fx(KXG257oplY(Si>Vosv#Rg^zZljYx(`@ z?tT(tvA>uN4%o|ZT&5c<<{F{IGL|GLz2q9140L82vkxV$G4=kP2+PrZ4^7K7ZMS)* zTFG4#iaELNIJd1i)9m8c<=br4lQx8AERP=#_vV*B_s|Gh9dK{Gy$Om3jDMp?=5{o5 z6DVr!l4tl^e16S|;D^|@{AHP^;_vJ)mpR%>&e@sEfQxA4xMD>)eWXLp=-sGFgx9I7 z{~6inXqbQ{@A>J}fb8TS4JfJ1p95Bi5C^ob!WTaq@d94K`28K0@EIBQmhGC5+$M1I z+3re8EKm3}{i-`#wcn2F_eQBJl^h=`=~Ntk2vKe$Ua^OZ+AIPexwW~Hu>ad)oAK1q z#M^=`(wGiyNI}0v<1prY`Rv%U0~&_{(Pc{-E`3O=#)0#T0ResO_i|@l55XG7z1HNq zi4jtE;ZXkGpyk5XYY@Z z6Er3Zcqz8KymqKScN4g7T5J8ahuQ;3kNXM5Njkb6t-I~aX3LG!*TA@(xLQiATzcCK zbS;@GMB?jp?uZlOO&PNPO4XBQoJ6>jS8V%rcBqz;eL#rY+&rI$pXOsff#5v(KLno{ z2!8!D(2NEHTwi3X;5zos?px1jgwFWJH7PER$E&eDS{m`1(i|$-6;U9uOm?p?X+?(~UrKEHbB7Zu)CN4HweWEg3}Jra5YgJ&ngb~kJ>gw5 zU>}ob(tMzeO46CesB?zfiKu*sOmLBJbbeO6FTXEo*En_t4y&W`;m%qa$B$i8xU zcgn>F=!G2|_1==NMb4kc>FuR?{(RoG=Qtly{ffwBG~c#!EV9fQ!=%ph_PeU6zrI7y z#^8c42k|RMT7-4(bWTkRRzxOWkMC+}sBsslpwEYhq|cB4)xL=S*RulAGjhdx5G}v> zEZ+X~F6WSO?e;xQ3x&R0;)%6U6sI+GH!KWj0pVI77KSQ){VB|@3$J^Sj^kx*oC!CI ziw`P>8y7=IFgqF*5-qnnG6y{MV}10*A$FI&%keb%58S^PHH5kaA}QL@tCnylrSjDH zfmrL~qxIM?s{&e(%Qy1tp(=)x^Qk`g>@+VQpVp^Gm(OGhK~4(__Hgb0CjD>2X5I`0 zWZSfOVg&9sYGG0$zWVc0XpTZO!M^0u!wEs{5*Z6HTdsdma`##e)+JgS=Yvntfd0}JJ&m$m)QUz^jCaFWru@z8&_s>S4_D8 zk+1!GFTYBEKf=AIWWc*)|kTN{yGWaOx ze}NZO^g+y1SaQ9CeN_@IsZakYK&%k971D#}KlsqFhC$ z`JBw=DjS96Xwt;?K(hSNP(>tIR56{;d{Fu2`@DZ|_A}-t<2UV18-IfM_s}ArvM^=_ zmZJSrW;4jo4cc*Sr1_Ty>yX0e9a}K@t2NgVI3yx1?yD~9+2N0UkJUzxwXlP|>^59U zs%5c92eap zX|n)>X_rVFQka@DI@$5`%+J74OtVSQBm34q22U7$f;6Cxk?`2rL(c#k4GpV-a}wi@cL4OqOt;0>|@!XL8DEP-Q@-p(x;0yA5;~Z=U+7fN)#w2c*gqzpSS0E+* zhfwn(F3h8#fT#bSv=`~rxo{qfHPLDmmOI}SJFB1iQ0;(e$5-5cQPGQMr&~Y_C=gOj zB_I|c2L!N)F;*t7S2mRN1!VgyIi)@fpE2z?`#^=^MCFpg++hc0!bF@6`K4BRk++X5hg4PN~-!(Q#$ zec2C}sDbxP8N5`W>q z#F!vg#A$B>4K5j5;?0kcq#?peJ)Rc}lf0Y{w+|s!9SExNpN_i+8-#~+GG*j2ay{CTS z(?#SH$b^CnbtO@Db8gxG$qAbv!Zr@gmH)JQLiImra7?|7%*f>5<4=8`Cc+5szm$=_iZ3bLn5z)lQapH{TV zddA+oQ8Q0^`SRpmHR%QkG*mZcKxeAN}fWnw%hF#?zT&;YvBX3BgsZSj4E2R!U z7L|d*NHXgbMcV|12*MxSs;D6CTX#Rd%ZV^8br|$M_~GwDsb>~x8xE|i6o&vO9*(0k zs{T*$WPpu$G>61XbB8lK7H%N{YG5DrgOlDf>GWEI%edE)mPRzW)c4~=1Gies!}ZBK z-@+BJ$y-;PoZd@g?Oa_+FAvdNU|Vv~4x3p`#bGrWOWab5FGXK`R9v!t0F)YE|FY$Y zP<-gUoYI(yoFzN{7-DaQp#9GP#dG+Y7g7mO+PYQH=A@POTYj6uOQm6-Jt`uR!>$Lp z(Upf(Q?Jp`q>ddM(lXf8;06)JT92l0QN7#^k>zZs3Dzv%6QfKzWmlK)R{xH{FGNYo zJ_q%PYl$4IpybcDMX+xw%PZ-?Eio2f zefsCAJ6!0;aLOf*{&TH~Q?G_Euy4)#2GWJU2a|4*^;lV?k0HgWd0Da6DIIkpsH+(|`LIN121&aUcU1@+ATh zb>s?jH=J({xL*0!!~8NVD;?~NkVhWfkjQ-%JPqq$cxc5n+^sJe>bfF>8RGD>nL~a# z&*$xB@5?_H(l$V^LJuvw6zxL_+d*U*RwHsyb9LP%`n~$(FM~L71fTYTJh8hZ^$iqs zn^U71Qk9gELI0OiJ?QO}M&`fPYy&+TB8Icf`zsG$Y6Lt-)}Z$KkG=+1P9g#SwJ%RB z96^W%=u}32C&7FG|DDm>5@H)M&y*Jc*J}fzmnEUoTOGIU46QxLju8E|2JrR5Ypuh` zuT&U}ltB$mM0>q+MFuZ)w)cCIiv&A*nq$8JWhyRitr59`JiD6?bNr-lFZQgsS{K|q z{6ycZ1bhHM_|$GntjnMDdZdN1iV;n|-F~I}6%!uuca2E~A3zwWe>IVI;ds%uqIb%PmDDzFE<3Se+CLwUKTY?%Zn;L zSRb4dRA97PB@Q=TA3_I`2A3gRpz-)msKf(`lfYGW84r1aUIm1Np*3Y%!5P6FI@ z7@4^7HhFKY6&gMOzafZbPke{}zOvSehinO<$)KgDD%I!g%L9e!ZpoF@-CSrWAs}}! zMgHySH6S_~TbaPdYXN*{Y)V-qpI>eaGkz#24o|OiTZ4-gWO~#XB+zt+kq)A5`kDJR-v6%Z>EtN8UzV{e@f~TZ5qf`@coTdy`y*>{eRY zGg!nD2}dMQtO*A<5=4Z+AwG%&S&@VC;8l5jEhD~`TQwbf&}eDUm9t~ULCxn+6VItR zg3N{Xd}et=()-_~YisNp*jg^B^X#?oEI&L;y>Xsj>wXv+kQTZ^is8cC?_Y0wkM#x2 zQ%!gP@T@>bfVj+~HA7k# z$?etrwMa(>6zE8*!u}u@1LIY$$KR_1!gWWN%J_tnlq{1{i*gLoBHSiA*BI?2p$+k9 zUIWt00UUgdS_Ff>K1-l21jHeK?WDt0lt>_{Hr2u2ynj)Rblua9wTH#;rx0%^QkDAQ z?vItydZb1_sMa)Up}4E|f+hJG;9YlZ@mB%a8}bR-Nwy8K!SMvKwfFqO>8hgGiuxpg z&(9bn zD)5!7)xn(LW(yqdav?@pc3;7kJ7c;fK=YR&QD;pU0d=E0->i!kKJ*g`%BDidSK5T! zpg$`$9i=S(grVhHZdOQu4pPGvl|6Ck+IFz=H5$M?BN$upcrWJ_y0Pmj_qFdINra4f z`Ohf`?Wu~mcOo$486jw&4GCry7cS&xTD2&L5^a~96d&d118aVRWaLt4wGT_Arq~Qv zC|q26!E8< zDd<>_PCcC!bmJ!*4caHki$)8aqlK#vFH#;L{i}O*x75m;W>5z34~F5fzL(%vyu_Z5 zTP4TtK*k1k3dGubg0j;O8T|5SmClKN4^{N+;P2*e|4e8hS^o56VVUOzTWz$SPz5MF zX6gfZ&T_D$EGJ#f_zwIAJ9g!a|9{NA_dnJD|39v2Wz(<^M@FQDvK{Lnn}kqO*_7;A zS&f~ve#dCE)14Jf0&9f$buN$3R=V z-?g8-e19RqB9DLo^eID|DNoFH$}=HE?;D$VTdhC`VblAxBzm#M2pBv%xGvq<&8p@_ zq(6l&Nd9;#6kRxjWFOGY;8MR$hPP!4&v3=6{2;^kNAbhqxbf#uPYz zSxG=Ie3$+3ftcO49zh!k0Uk;MQjB)C{|NFwM;vOM|I6qR6cWOG{IcqHOBH>%Sqgg1 z4!rM@T0Ta|XV>IP_MaNlp@Iec>ol~zUIaNb!2UCC2!U+GY{(MkaePO~&o9RopRpeWEGPV!mC1t+2G`l`O|X z-=*8n3|zilS-d-_e(~t{77&pEWL26P%TIK3v-$yT=Vl5ICQymabM%m0)uBn)j#!dJ zI}GvznX^rKN_<9cC7!3$X2j{w3z6(NK0 zbr$ZgqNOt7bSjjwa}yr6a6wabfQ1m&IFPGgtrJzIkSB3-J|Bl0G=UWXSGJ?B!aChH zh45^xCiCbgSo@)rTXGtt3_t<>7b%2s-*vc5^Yb;xp*yhyl7$#FAMus6`q*LjP_a69 zbXg0HjDjM-FLuZ;{@#U=O~PeisSX|`?Tz{zY9GW+q|B|mm?qJGwoj)6f|AY`_u7E) z_$KA;3~NhhVNp8z8Ebq>G{En5n5frrq9s|1_jC%7#5J&{NcU3s76qr z;QkyPO{KZO#7HV%Kld!sN>M2Vub7qgPa|+iwjvk>6-as$7%Ix#&V?Mu7rv-GQl(8v z|A9-IflEuzG@f(N3l)~0e!zMpPN&yc9D*G<6p6&5Tn^miz4;`MQSQex%lfV^i!0A) z^x8#!-#EjM9js9`$uemC#9G$@$%+y7%Jq{*q?fSL+W&GPwUX$)uT9{Vxal zi^eF-*mqSCfc8`1zuNvR0HZMmxh@1bX;kjG4GfK(uB;s2u|Cj;JMg0~F0Q2DpFO4*W~A* z8L||-1_d~ag<4(2&SE@F;tg){TEOmvbwh@$~_ie)s?q=s!lhEp(Pz))_ zO67~(BVj0f)$;COfV1HDpTh70GsRtZE$Zoe2cO+|MnzGef9O9-3ayE6q@DVie zZJFrd8*eJFYn+@+xYh{@duPk`6AT)a8VtWd-qe(rubBmUe4+FkIdHn6Xc&Z%XJi!g z729bJOIzfaVT-f<(QZ-XTX0kTj-{q77`%A9g2d&8;nO6w$@II#Dh#jb9icaY2EJP- z!L~S>x10KVXDnhN*+)D#uRQwHgjHpo4}w~@5wR6;wz+S;SfXNyvW86PXWgrmxn)!7 z>gZGI$=D>9zMDlzh#~_CoP9$Y1uMO;_O-dx8A#lGZYB6aZ?kBjydRpwC=pFY~ZZ)W!k&LWTGUmm-s>U#GV5?e+-N8&MN*IDFUdmzK5 z20A@VvAEQ3TM=_mspJQiM z>hu3`3JMHXKP>26A6di@ZK5tjeB3r=^}1Q&a_t|88}hhR_3LP-VeQsJxp!)%XW)I7&`o-cOg zozYLcr$eT4)?%m1wdNYa7eLWlK?jxx@S3yhq)Z*+Zry1}$iHjh z*9-CitJ*9^(uRRV%3(srK~1B@dp?;{I>Gijjf~&cvNq8fB_ub%-Rn~v>_u`eh+^tu zJ}@s@Xvw{+lWfou@$?af*Daea2?S-^1jx;twWwJjp;&tzpZSJ$A1))#DjSEaYiW z;L&5RqLcDqCU89lBfP!RxcU02K&(+gqMm^6h1hQN=@2A|l6UyZfnh#u6hJxqe{F{J zMXrjRZggL>S#EIbXt9wNZ_ozuXb=>0xKC#lrfi?4%>Br68`Z_Qm9;*l2mYLvaTXu- zijvg+>o+R4LNR&*uNBRp=rPnjv(Cy1i)63t8#$w@-;+RtajFZ5m@cEnsCOHKqfhED z=Kgt(^HhK``gVnw45C*@uW^udeZ^;qW&z&L=RUDY3%~UsKAN!gif)m%km@qRFs=S8 z%uce=>dtV2*qZP|LgGKx0u3K1N4!zVyCoIpao&)ER2!8$bC9rYv6kbdejCkvA2nz1 z34pDjuCuv+qJUol@YFk*FF&h0$(+xQsXm82NNgV@nF)Ee-PMX-OUJw3WywWyq4m9)U$B-K^9@ z=3vpFFS)#sX|Vc*eeMPTga&wfi!Vw3fI@nti{8^tSpGLl0ftH<+^uLRA-InvNB`i= zA!X!JDx37$ne1$DNv+Br;M8)1gQ2hYYA|=R20rbLv1wL1+VG-)LADQh@RG^{q9H%ElIs2Cl3VnJs z`Ny(jfTJ!HNOH3iI%8;VemaXS9+BwsE2+eJn&%;c!FvU*ifEe0R!43WQ$7!ztB=7| z-%K9AqKAa<$qIa}0G3B6szG1gjK?M7jW$>`>EjM8;~nNYu*dMX5no3F;1B~)ye^TX4pJ7N$x+p%4zpJEUVYm2xbwOTETLrkQ#`^%}z-B}RJNNAR|{xDikE#wlkvH+sHdegl)F!Pmpe`PRwFs5KuXzy-CS zh}}%U6rVl*5N?1Bb-q!Nx%h+4l_|Yah&LuOd#_trwtN2(S=k>7U4_%0nJLp1|=?4F+bCq58I5+7g};M7IOl9Q!} z1RkYgB?=ff(goT9}p!0HbL3wR% zTKtX5)0~5n5#t3U62JrJIqDF-KHbT*PL#AQRx4UV0=nJQ!sx%uB(h}<&#fent&NVJV{SI*e6EiycF_+ zu-R)d>S*tN^H|$a%?N(2y;P=HKi_QTjE^2Z`dYlb_Lb#=xhPt-gIMPIqp01_4yq?G zApp9P)^4wTqjtEN`h+R9+eW-dV3m?3i5iNq$qv6?E*+JZOsUZz1R1~4-(j{-m$cKeMigN%=)ogZs4 zjNl7>xuD1>kG(TsQoM~%CZ6&@@gPV6>9=jBOs7YvOD2(MF~)MJW5m5t@$%6KqZg!5 z(+P!o^+Cbua5mc?kXhhG1pQ4wUys0@EHG7N{Owo)_T(o&Uno!o1TK(vgqwWG6Q-u) zoDmWGQ4ZuW(F;{d)qaBPRZ2;B%hN!HI!^47QJ(JYIv)rhiC4Pl9fOz*p&j{m%TK<< zKfz{Z9ZYHsCU^nl_>ATsnC zSSm0GTeCmzc9>spd3u;>2vQMtgnD%}Zrk|hB`5(yPPAu&1F499x^W4>0h%Olu#6wO z9%UYFt3Wj^5_A6yWx)>6j>>%ZMD*tv?)=Q33cTXI-kekCiH6eR@)hqRlF$s>PaRXr zMUj|LR=>dd1`?Gpf?|G?0=zE(bmXF5B9ICv(s|jgaw7x}-dgg98VZW*o8#368I9I* zeAzLMm|_3x+(?C=Z~xb2Tw{8aE$>~g=i%1?bUO^D?!(7>vZyRG`)4koo&m#mp3H^jtH|$7xSj-k@BCywwhwL-w9E1Z=%C z7q(fat}yb|exc4q5#aTNPqTv2=LjySpoS|$aXzFP%%5?GTov@hhZ|)elebx{7Di7E zj|BP!>5+ZszeBc-xUIr!7PEt$ad1I>w%%Wc^a&1f=t3xX8cQwl{@ap$Yv`5K_%P1}EJrno}=mD6+PwQZG4b-#MohN?Q$DJ>y0 zg@G;VomFt%Ej-VE2u#v)UGO`7XrdxNhkMM0&p5 zc<_)GqJfXm*mXGdQV@$->pAhLpsZ&)KC6_JlpA?60xtIfvvPI{pOWPMM7RO1Oy(XMWb6#z8ct@ zKP4U|#B>%pP6t{M4A2ZD)a2)X5T)De`<4Sk$YEL8Oh!El7~QCC6~+Tv)b3m)L=8e; zPN5hB2R@TnO>cn?;qT`{LD}-jk5&+@BF{wt{~UnpNc&H$&=){J1~La191Hm^;kYl# zWv_%P^_IPuS*QpaOg}my&nf)g$;t@{@Bj_(bf_#*KCeWF(F2l?UIk+8rAj)Io`Zb) z){a3Ylx-Jl!r0h5PRO$B`wm)HpKf(QFO%AH1dDZLTQONy#4r?!h!iQPJ~_^piZxlxHDA8AZq>0XEQL=oce}b4nnQ% z-!ZF{BhmMcy~|uG3{&N*JNd2NUidhGbePx5^VT4u0>DTR37&_*kkCjrjsrtA;M8>` zX6KVoj&dazx0@^S$7FZj+UcAOyeLT< zFAr1Qc&>TPy;y+{>%f&p^-HGWHF$7**qKq7O5kb&EuXbX;ifZ~wf%pXq^CpxxBJ^m zwQ);Dg$edpV}+78TzPUaZQr%cDz;xh~aTS3d`MW z$hNzE^CWX^4#fCI>LLC~z?8~WVQ?&@DFV}1Xw19Ls0t>Jlm~c|!8=U)Ft>O`58 zUGjmCv9Z146NtSWo+icHMweC4PEW}{l)U8~<_9L4{7Xi?7f{o1c5xtW37@)bt8zS) znEWh{Jhsx{MNtZB!N&xxyoV2)3>;bbgQ{v}8c>{2y6WhQ)&Cw3(dN;wsnLI#*dP<{ z1??hbVF!PfY^Nv$Ur^{V%3R9`gLEC(pu89~4(_^0K7>U4`ZEN}mU0K2KR4)@c{w8~ z|E%=x`5_pA$Va({Yr$uiRYV0J zQfof!EBKzFVAg7b&08ahS{$;ckNqgo9b&GEI@|}4+%btoBhoy@DhngN&L^P1z`_hP z_>fSkl}AR6KtACj;k++0G8Tg>4uxSr8*659|1*(7z)g&}jMN zX_|YMcr})&J;F1Z_t&M2zcA`8I6k-1jIc8TXl7sykAm0#D>&UXF<~jF!2Wu-D$9Mp zOdajD>u5|CG`&zz2y#Q>@zH*@znfF|GfGdG4-%JiDfp0#Wnfb`4-FMMRsqtOY8-^4 z|EoVuf*XNPew7WRwDh$w`;Ry*L7?Rwd?_FtPkJi#@NM2J4)5uo`0u%Vzw%wtG><1C zdcsu$X-O~ueq9TzH@9H`Gf8(jOn`s6R^|d9j{11WA{_P)sYnUk9Qyo8 zWv{JY%C2VFDXP2sSIc5rTEq=h7?k!x2m5mvV+T)#d?#0cP~m{et#n4f5RFkj&~K*a zB^K@h;vT*faNABd-RAWdmf81_M6`{^k1p|poG*s4YpV3VF}h0*Z;caQs(mMHt$-M6+H7=f7S$UT28Lhbv0d>;^GMLT=HnKYM$fiSWer2 z8Tk;qS-6)xDka+~ah4kOeUJ1|qr8}`y3TobK)X~s6(lljFmp>Elqqobr}po)ZCqhg z<^eS^lB%@}!S|WKrT@a(sRlvO2|}2Le*Em0+q`P*AY|$9`|ImuxiEFf>9g2D+WpMf zXj0OfQ;p?gGPwE7v|y0?N}Y>i(kzP^BmlU7@2ioO!uzk65baSvZhYv29F$PN0}8cb z3gCv3OyZ5^3j?92>SS>9%_3{u5NnUd7(}!ARf|+ zugD%}d)RN{zYp4*Ie|Sl&qU~-x~Pfn6+)a@$2`15hl0H8Pe_SC%dmI7xLX{gS2$j%XXjX}iChLOSCO>onBg$?+ ziN7#_reat;_`^x?IH+gZL;jEuk}P@HPb#f&TYjjU6>mv{9vda44{R-~7kJpB|L=a} z0sB!v5YJFd1UKT42Y>-Sr;@5Rw^QnRE1ZiN2$nAWC)*?Kv1E*lj#`K6!6h?3sN5g3 zdmwV0Z5fH-7^GlilvkmTy?y6juI~a&a>hZM*U|Saco`Xwk|T0E5r{)@xj;3?AX=KK zYaCESBw1ssaR?lq1_vG`-IL9$3;nQs9sRCkbTjaA{`EYvdK6md7StzbpGA0_8i}+= zra}&&_(qK(qu%fOGaOYp0rK2PmYi9|B3=Z`r4!;4DWI@ySS1|#^yXRYnZ=+3tWmG8 zU0vw+dw1xsQK$tyIDmB@Yxau}*F0^HjF^GJ848NOcpZKDvX@3K65CqQS0>=gb8zw3 zYa}G)lZUr5B0i~fNtHE#Yhdkam0WUtcaC`TEb=$eI6nG&dh^dzDjq6CZUC@y4o|;V zVS5&V#koJQ9&XAuK!x2<6$k2so2Q{PQ|w{_`cH;{*_q=ELD2>RK(RzLiouybj|w%x zyT!=Xtyo@E%@>>S#eBk zed^r1Y`S|21O!5X6M_5+{2g%!Y2}?GD%~SLsr2nGS!>(9^olqAy-Qex9A44rh08L6 zZ5TRA$`xvaU@Ev!iwVc*-QTN7@xt|yjzuY{T~@}-;A-(l?md!4h%Lk%D*>O`GbEZ> z5x%@)=Lk0y9+?4(lKn3#ae(BYwp4KX8TIc1zzf|dmX&&LSALm(_LB;`T#3z88jut2 z8K1bBh4W*jr2I^V5WE4a`GVMQ*2NRbUd0U4+V*QT4_*lDD!ob-6Du6M8X9;M@g$o0 z6g(>j8p~pT7I8wwx;W}(3E;-OT5*iZ$O5PPJlQWs(E!%*V$n`<^JW<5o?vwf?i zG{=P4zYFODu>$|Oa1R*fUK^r9bN$v#APg*7dMJ<_rDrvRMh6|L3jMA{x*1$o(&2Tu z02lPJ!|+SgiNb%sL{X?QTsX+!56rWkJ}>#m9H!Md94*S@A3m zOOv5E8*(>~t)7LV&{Vt`+oce20fhOg8fWUs<>rh@4`!TQ`2+6~4u2y)WGj82M}u(q zu_5-lhx`7Bt=)qIacGOD`$r zWeYBdpgEzPEY`G(xW*|HdRv<3hh6!+d%LxMsqo4B-X&&(S&XrxJ_BB|JZ)q=@Vzs` z5-PkP?cm0~9`fR`v{1QSxy_{|vzTLKkQ#aE4|M9sR^eR#c_f{V^c?HUd%F6k`>pZ% z%jgDTida|~mY$13%@gtzB?L3E;Our4=_R^c=AU9i9Mkt7|D2$M}R(d<&(T><$JYyWn^&n zTFzEa0gK#7{)Da)p033b*2V%QwlEw%kwCk0#3+62qjpw2!~?G${^Vh7?Dl#*KUBBo zFRZ^Dnw25NR$m7O}`Ml39fn=+i_WV(INwem-^uIG9v>3=W8iKjiD>37eA;G_^2d>GVjNVL&I zIXO9@hnYIcAO}sQO%#`h4r)1vH!mP9{0O~8v~YmY%dZSd4-s&1n-J&M1y%<*bOm%Q zy`{U7X~z$%3$Xu7MnCb8H9QR|Wd*bzJV88GRPM6~kIbW6eoEb&jhfGG%ucGdFLY=> zkkf;%y)w&Kgm1y^Uh?L9<%C1b!NQ7qy1Fq`YPp~3QL&i+6oYw0JOfQgU&Lfv6iDr} zhB?yh|B?ckWoY}nuMM%PWn#wVCr|9z-;V7sp8RH~(z=NvL2KOEM;dfD?Z>nYQC?nu z3#1lBN>A|I+y2nnusLDsExk2JR7{+6YPLlGYMsg|NMo;R%Amn>6iAzjhVxouC)m8D zy*w9Eo>c3>n$>oEQ`VG`-A^~#FoJ~j_y0*%*P3NLh#e4*w!pj>Kd193Tc#=koW34Ck*E-Y84A zO@@b~JS%g=_iKNfM0gIxLed%(LFq(%}IFa#O8n1OpM&H<1ta z)SpE^+?CS=m9fTbej&-N5G(`MdEIUQmqRbngL$onE9UeVPgj!H{hu|K`wt`d;fwfq z2+<-zJixv0d^w3|;n?2yTU<)1(2UdHAiv9?**h}k=k<*ftl-v^DzdOD^g5@HgIk`7 z(_@Ruv&(Wv$h0Wb_KNwD2RHu?6CN{kE7^?{4rcxqd{&h6!!m#qBqwxxMCEuc)+=N0 z{uN6i%-4Lc{|0)5CS)JM#>?J}j=pf)LFx9bm<43iIaGI8+LBD&`j)&!x#)1%vItKc zxka}V$K?=jtBYjVfar1qRz|%ZxcVKR`@)!xfm>#+@7BF?somGwl52KjBB84Meh+@; z#(vce{?t#Fa2R2u=1eR2=t3dd&C#r;>du{}y*fM5QOCLXuQB&VYcOV8UC_Hrj-272 zM0L(5a^3kW7H|TAyhk@3jp5r|;eEWY&Xd5gT`=9KGQa%g`55%Z-jU&&_onXJ)3&G! z8?5qxSuXuoYR-98vorJRVA+T+jc^J?JZRe6jkJFW`ggl3&({d)SHemM(`bwECiGE5 z+P_B!$p#hBt#8t8re#%7VS#q;Tc82a#~J0k8FQ*+X<$D=h7J#@*x|8 zSNq0_&)%O^LQ#GA@KAYZ&-2_jmX_}We<#0YNYwB2B2=Zrpxq?RKN@Y;Iqj`A z%in$}Ylo-R5BUT~mtO@A%7NgkG5CkTvdpvSyHFTW=)RO9ns$V=*Q+$8ls@3!DLqbqm_m)(x&pxTRPXP(x#{heo_rR$3mO<1 zQ^g5AE+VlU+cfR$^>-gzbQSqde^-gOTk|5_c9KG^2vS;C#}fvPPy1YTo4Pf;)Jt7W z=tefYTT}69Xy2pgbycg`2;ybKF@KHR&2@bIg~`wO2nAap))-nlF6j8Wj)7y_j4x|# zShJ3Q{g&x=O-7`>64QRE_}ZmBnLVhAfVVy-qqTZ_SNthYEZIuoSbBbzb@^I(B$Rw zj27F*8Wmo~o4vk>VNN_%!wzoHkpSm&GtrQI{UA{=?wwc5l zB;NI3ZJE_*O5%E~yb!`K)vwTZKl?Xc$Nm!DqQYEdT1zY3B61|Ot>h3U(^-*pstC!y zqc%vo{iU0PNa zWj}qs&}K(%Syxs3v#1@djTY-N2y1L+&2fsPw=rS(c1KEVs5T=ap(A9{q26edtE6b( z%^X=CzUVG1>o+S`bRMz;Nq^gtV)w8NnijRL7rOc_MZLPvj*pp3hYuU;cf2bxhl&O$ z)6~h-`BhzJtV<$2ZDE2v7I@amsMhOwaVoaZx;-vV1y_GkDv3YhU5k+%#<0L0iI@(I zQz45+*(>V(BY$J#NzLqUXAgt<&Lz?m2F-V?P@NZJ(Cvep^un} zGo%J&c&WK7@@?>uyB((Lpv?w~x-^3E!mL3Hp()Cmj6Ua8AQl^G4{U%#gkSDr;#WMg7a#g6Pk(quvn_FJbYt|P zDpC)6x%p$8MR_RR>3#=YMVk0hpN=Y0lKWM%M^_Ss%)*n@_?gTpBH?jOp8asYU z8S-~&#?mj``6-(%vD7UX`DWL7S=uVOc0aW8i@ri3(+WM=7xOPDJp@?Q$=3Pll{X%0 zKNNNM8AOuf6sh^)HJ)B&nUo|oeJ4y09d=K(@_JFNV_1KbwecikoB1;r?ondMVM!J% z*7Hnc@(N+s(P5AfXi@C{v#N8}$Xs-?9({F;XPVlWMtS``sH z3=Ypd(Nb%wiz`M1B{-M|Xf#utyK`>SYTT)@E1FqMTE`BNSs_>vBuAD}56;ceoR`eR z(43mvyXQTN@obh=-ooM-wP|t8i_!pGc>WuY)Y?^$+D>=d(_idLH2KO}5PO#D+36Py`|i?$_u0Z%CEq-X`ojh`K6q^$T_xnKxacd!4Z!Q6pA%XJ#&P z@QVKOntki?Rlcdy4QJm_0YA5{I0Wg9q5@LNv4*#cYVOJmnz}8DEx75pv_;dHE7vBp zwr2A!xVDJhmqV&eeI1j`(`k|>3K?@ zJdKOU*T3QIK=#-romS1HJ!>=2phP9_lSZWMep?7z9KY)EM=5)05!I$ER$`*F-w4`4SPh(#hk~`P?lHGSj=0nPg!qOqKQ0!Y&7bsOFwj0+;wsp&yw10zV zez=k6r}0DajG6SL4|-Lx9hqZB`>r7*Z#8of{{FlrL;whzA?$mGPB<$Y|et{E2xDq9^;LV z5a0)()pD)7NyR>;&2!HgIsa-Xkn|M{R69;DRh*(LT9m9KZcbH*o-gt>E^!RYYF+C# ze971+BWvRo=xMgl8=L&Yg3EN)~o6^ke-cBmq&G#-fJ^J z!|A9$1d!ME8NM>ll$XCKCA9cnxuIIGb{URBIeJzYVPj>5&Ck!zUOc}0Y;h+<8mV8u zs>B`ln^W$LmT223hurGmOs3{Dv2D)YwCXXv;ci^ti0AOO5|<>LV(Bb!8{N}M zkrAl#q@zMZ!{kw93aH?d00F@AksK7}qfDV8k z0n`Bfgnv!}Xm7b+MwJtOC6P|8rl_cRXe~HHZ0!9*Mb60%{xqM7-^0zja(?A(>-aH? zTes}}M}Y7DdiBU6mlum?G*h`qxh}!N%WNya+QkR&96ctRT7IqGNq4D1hj`@f>YsMP zMO?@fep%aY)jcR2(-Gu;*VuTlVW+J@*tF6f0D5~%1ML3kGdkG?H|xAy-+EtKw-At> zY;9|^dC+yjA)~>6{n369&*P2d%FYti%f>Gd3FhYFtriF>HG<9Z7=?3VgddH7`7_xCApvpsyzD6wGZ5zM^cN6vQ+GY^1wXeQ7 zQjpMRGiL4;Hg8^$5nr>!gK~Oq@v`#i*t1=Ss~$fEOjsNke9xD^B%V&s*7@o>t^a%B z5>H+AcfRyzAr|4p6LsXaujhIx9VqI4V*EiEpbKMFE5W|99fUbGHKL_Ki9gi8t|r<< ze!R|btJa#o6Ng^(%vJPj&?!fhH@8_(noxG+948#tTfAw)B0{ryD$G1wmcv%XR0&kw zH7*Xt-w}iz@msG|rl^&b_c(DH-IB8n1mPh`<63%=Od@dvy-Exz z>~dnRWAFI;ODU%Wx_!{fsaBT7qp#a;-9#}uh&UWy)bW%y`W2&) z_m&6mPTt?~Voo~4O@@QMP-Hf9mMJrnaHw0_mn%o6?u@a$qV}}WWAvzi31zPhpNr|) z5ZSt;zm8KGdhpS0>ON5v(V%Pa z#`HZ|LG)a?xR`;U6$7T|Y#T)I5(oE_BxacsZAipzr2px*c1P z8xNNvv2M2dfnkiSNX4W61Ol>YkziHsCwt8C-+3*G17XP*T z65GAO!_zEywy&xT?E{Hjep-%d)*73>E9DQ4M)$xlCx!a2L9r>h^p zFcw)e_&&0c6+zjJKMUylW-(;opSQt=vb$m7ZJRcC`RzJ>;Ha0}&atu#@ty22-6Q6n zD+u(=l(pgpK4|_s5XPyBz^vme#*YV zMH9rLDXNHvk4>nH$Ir>Y18^GG$$^;Pu$?ItN?)KnpS>0>dMMiSPC|D`6L#|WW*R7?AjLYV_$dp9mDpkRSC<}qlX|z?7#|c7&O3z%5igcrFVoxlt zR+4tnS|(-#rTc6WnrnVTQxMiCNQc@9!c^e?bcc5(c^Y+!cQsDljY`mC^Q2!AWj3^C zdVfZ2oTnY>#9Yw1eK79oGe;(T+u}39#uuzamTI_^3kwM&o{Oi-@efH4;RzN=MqI^5 z79YzJ&fwo)NjvJRRW3ACW-|l>DCo;rE~K(bG}4{9{8mN_{vR{v&^Gbz@7cdbFLBa! zN=Xg<1o4?nm{6O>kW1+f>ECKy;cDyY&bH+z&H0)Yn}x9reu3^ayG)a2(?hRxH1=$9 z$=6}k)l9Uf8|vQOE7~=D`A#CNFJX@6?VsSxqm3SsO?UUZqy*vUJmYjf|Y&P-w`SCsXp}2EMJ$8ajiWw-sLkFlT*q(Tk{OHvgq7RwWRgp z9D1|&nlnt9DLz;$3N$&(;i|!%O}xueewDbM%gHtV@)WxGx@sgm$>CW(F75f zGF^hW)%jyyBgze#1X|B<^j1d->)#V%8BSqdGt^#ewcTC#o3`>}S;`V&qrZM0?_C%u zd6vr4izFsyh;kQP+vRMgEx5dEYm*DhZMzL8d1UKcdgNb&8O;gmOj!z}o`45AukjWF zDo$s2BQuW^y3F^2l7`gqd`u>Po%2@qR`k0jGbPYm81y7rFW6;ZsbG$b`#k+Nw3l=V{Bmo@aYYmo$oK z@62CK6We!};=;W}#jQDjz-Q=+wgeZhmzT=pSeB=?L% z#XM26bBuiGGQk4R6_K(#UXqcRwJx8Z={agd@JkqorMtGrhU&mnY2Iqs?g@|;L}?>@ z$o^`0C}=+%VI!Cjj?XA>K0}vvThkEccB&gvw{vl>;i2mZ1e5ZYLx0a>9(Z13^kJxr zR+0*Li`%7^mr5drLw=GWZQ zhSxbc+&M;=teUI4Csdwx??*6E8XN*b9cF)p6ZTf<G8YI_cXRGP7ZrGsfFj95z9)rqDZ$%7b0Al?*27d zd;XUgQ(mWI%t;$u(sOB#p#Imwo~tlhrNYnQitM)1pqVMGvI&7OnE=}WMLLIank+|0 z`HxACsXD&8upzy@S2-7bulrbE>tyu^mR0|}|M9)wR$))~Vow4z?~;AS3yoLW7167$ zg1v3b-BzAWi|wm+RR)!lEMCA?CBkNRhoGFBE!CN@;)u-{a@7_ZHSMCt)nNAZ?k1^? z+6EM2xc*s+;L`hjytQWX-q(pKT$Iq&|9pDZ`!oE8@w^b> zmPzYsPcV3D*Bf+uxHs*bRBM7*JE?4fCDJ&%q(v)MM`K4B9jHYzCEgK_)ZOWCpO$;O zV-z30lN~o5hqI0AzpILmFc04&{B68a)9kH*3ZIF<<*zFC(>x&j$t{sgu$J9rJKOWh z?VYsqWpDZKi%eUh_=_m->vf)Ay)`Q8t|)Fl2`6~s9(-DLv-RG*@b-*X2SNMrDKp&3 z+?4S!^OQ)YRNb0pS9edR+)6J;iDq4^G^3sy-6smvS$b>F*}jcDWMr#~OU15r8wC(+ z=k;M~P1W~dPj!=W$e6D;G*#D_Rm@HxSok>#5yEEKI`t~Ft%H2t4uyxUCn!0{I>bm` zHN`{X*XYg_E<(?nD9FjUe(DGt#CoyR`O?+a&`-`{6)<%zed}nfcor1RZsE>(~ zT{OANb_8cv)30h>!dyA$U~0KsTOMy%(!(ImJWnaSNbjF+Z~f#qeyfXVR}Et&(sW`* zkJRyG&|KdO{|57+KGQq&nOdyM`~Ld# zq=(lOGUkw{F-v-BUai;Ft{ZL=?<-MQ@{4xKdJGv`r?oW7Y8JhR6;>u=r z|33KjF@#?1?U};Pt=W;4+LU!sW|8<0*J|Z*>!d4TEau7$IKMmaa*4Mj<2T;gbk86P zx?NX2aSO!nYHL9lqjcqRAul>x!=Bht773l&Dr8T<(CUwAU#($fr)P;nIK4H)iN=6G zv&KZ<&(<+bqRa)+baW^#K79G0Zz4hLSHy&;ishkmY0Z_k?2xB77o zj%_@n?+GofEAu6j?0MU|D(oqYW&*j3^|0B#iKmlVdrqPFlYKJW{S`$iue6?l`jYa^ z@}ivcQ5Nv%q0aTzJ<2ZW3M?IpbGWmn6o1msam6&V`Zp6;!R?1;{4Ac+`I%RKZ{Y*i z6Kq#!W&Yw&ERtFtxhb$0$-kfQLa#RH{S4L(G8sBYTOK-kyiT!SyZeW?#t^$Ld@jV| zmwIn$e&NG^I}Cg7qn4K&7q;a#?V#`A6*^q4Gi*wX!PUfORnlZ@#$96@isNk12{Zxw zCs^*cCEBj93|69!&mq@Vv8Zidb2KxMNak?(7ZE%;ZCT>XhjK>P;%x<8#9QW(FeuyF zvVqo3XCcVUoOD-i@41*|MF_$a9j`MrX=T&Ghy00)5}2sVl+*#A`(@9o(Wv|k7AodF!u{XxVT<}x$ehs)XCUJ&spfh zS3|k`YqkFJY#L3!2#>}N3Rl1Kejld#KBt`EM77y#zw{Xv8*k&SMB->Hy5_4_6@x9e zrv8Fkp7%Z~(?snOUSyWTYya)3k<0c=`Ej34sn?NjiSFM0MENm(J%Q-1(Vp`#uN_;x z#ctDpOMX1^{YAab4sju0Pc-JmC})Mie0nz4D}U5)@-#G>9p2VyEh+ zy4;y}OWy9cXEvNFR}Q>CldirVj&bcuno=+sH9haUI;)r-6)ej=#3LK8Nhh-;Q2OQf zmtIDRwA6g9=57_7zfGzTXa8$kRX*4$gdeWvo*ofpemDXVOYARdKDv`k@uCK!68?gg zjIq`a_?moRwqmdF*Kz1oV58pbjkfPCC+j=bNPFt;FndNPtS8hj2y9vY^)&jk`|51D zR+MVxdcux4OscXf8KgZ2%p@*44MCOMH$X4SS#+szt;!7HSjZ<)ts#@U~Uu^1n}0P zo;x-8x_k1;cr*GYwg3E}PsXRLbHwA$(^#(7t=4B8mc-OL)w_?nzg$l^+S33-~L91wy$d-0Yp7AHM*4uZc|O_;dkZ9t7;eAe`*EsT&t5YzB@!aJZgHpB%?ON zw{&QggI>B4sbF94uE5SudfMhL>R$Qg=+rv(l<$9=`x~^9^RYo^bHry%Srm!$OXslK z_pn~aDadoBOZ&kNigMlZ=+Vl4r`~kNS7@gbj0Aq;W(oh}yiSI=H-mLmC#+RHRlT3P zg{$upgtqkwqHCjK;}_jJGq|MaTX z;h}%8kR4lV2R;bFrsYjLWB4fUPK=M0aLMV;$d$-ct#dKtMZzUB)tQCL{p)y>@Bh0Q zH0eRN)>|0E{|(*Cbt zDTyk0N4X`E#;J{4T#gsUnKayBGd;(A@Q||O8I>55@<}JH6qkhh&Cz2I-%RdOPw66y z)G*Hih_s99lFZ^E8o%723i|Nn1zFAZgu_rO{qb#e64+yCc3p!fe-MrN>#(s!Gs!gYw)>q6AA z2hQ@7D{H?ahhGb=|7_@i?zp{lCw!Y(Y*Ftf!j#4yEjf7mqp*IU6Qm!WW4a3Lx}*-` zrGzrfSik)7p7vEe_E?G3_gcIFUgXHo!NkI}_$!MYB$tmKoUTds@!0>9V}?}odEjxw zKevDZ4?jriTkymA-~cqTagrh@+5hivoK%C{HDvfF4^18=dRi~5c8`pRL>=N*0W!+p zW1uP_U9$ho?AEOpM+C^b0;PHr01KfQmq)7{CUGfEc4lSTd4fH8CWF5Qu?ckelM z-mUwcJCJ@1#bOKkf8n?Ii84k0~Xfkn*f^1tbqC6l_5gI4R3l z-6yF~iB`wFgUWcbtmSeH*zU!wx5n)KqeLIbx;$Xb;Z;N8mbo}JEYVoVPu#}yw; zdsmHxP6lxt5gDwrGpJnE#ufI@Pdph#TkDoeQ3r>;ot6==5Kn-RjF-Vo=CKq~JlVZ~|G@Mrsfe||CK?$?>9b`X)1BD?k-*9WG^A%y!_4&bs@aLsa zW7raA2)eIuX3peQV_kFD`gsFdTkgr$TEIpCyX)`G#1Ld0E8)eqDfR>7z`+}@IuUJ4r&^r$!fDC6&Y|ohL#b5r z(SQIC{P*uA4#M4!mxK3v0hp_~K2+63%u>v_q=CJR*ENDxJ1#5RyPl!sY*+n~Jb#;e zAPzMmEh({0K&|veILlrCk$!Tfc=?Kz<52k>%}yc?F1@j6nL1AvBho0tm_f_FUsfL5 zNd1&)=Q8U}#OWtLSvO0;RlpaG2v1zoTO6epgKS%|YC}RqvDD^>6=ndO1nH1_&*@km zO`!~W{o8O_>7GVYJjt?&--Kb7KS1!LG}f)Rp0CZfP^W>0LfHj}`oHGZa8*f|Z+$iK zdM&RD&jNPGP}b3j#-{Tt!hl0`&73jnw-AD@;028{p?`<{=Abu%3M9ceHoiE4nSYm3 zWqMIWe)iq9Sia-{udk91tB(3J(f5f$dU}#~o_Zz`?$F;^{;UK0#-)c1GPVAY_^6+} zjM%xd?nZ5(8@*L)oF5Wl?auPu>%*Px7!Pe}K0t*RLh3d;{@IbQvyF?zD)9uRco=N8 zP^|hm`)Y#wlv;WgxZBOfT@!k;`deAIg^P8bihU<=c+ z!3_?f)Bctn1mwSyGJSw54Z>rwOCXPaX)C$gK8^B&+sY?RlWxzL-m(0Gondm+IZMBH zWwUN!nf_**ZnLc!xy24Qvkvew+KTtSYtHu2jxf3tf87UMY5xnFRl^mVOuFPI;oZ1mi33*Ax^d)|uxq9(|IOjORqbSr>+8D>Ga+NrojJiFjk-2Gjx4KT2~dWhcct2 z``lYzLmxyH{0HG+4&}UQ%-7#@SuD)rY}_2C>cN(V?+%`cBn?+HH^{5!LuDJ^0I)*| z7JPF8S&^zwQF;cijql%3$7Z9Rbxh;J*ZL}V8g~I|adxjmS{xy}UcQiW>3&o-KE!;n zP&_yzW;0QF4%5DPT@g5l5R46?RwKqkcS0R~4cG&NjlKj&6T{URM>)<-(-ks{?Y=4R z_S=}xv%)*f{E?L@9;VA1&nf|Jh%GNrDTfQ4(-C$_Ot}UER!D+A?VNHC*~9?!4;mWl zoho0@GY<)we|rl$nY(l4mA(;X2$jDj8j;5vWN}Vx(+q+M&hI}CX2m&%Y5P;k%RWF5 zyM4^T%#d*HQGRh0=u_C@si*5d=G4e~^^Zw7hTXc5i-5Q$im2J4DgD)#cm2_*?ITxX z0^9CoPheEH zX2yGtcPTU{c;%OfEl9&?-t&S1NOf4LqnE06+Y%$3H+_0vD={&{js^%e<9LHPOoho`~cVRqR2jwB=(zCiz- zXt2#R0snZ(N$#GLmhEFF*M|@?Pf^2t^+}At6ayR+PP^?Pj<% z?CwU>o5Z)jJ?hEs=osr7BeCuE>-$iJKoeR(kxh-?{&K^r=xU6ctJMUn%SWq|MGR#3 z7WY_O&@Kh2i_iP&*iJ;$)sW(ZFCy`{i};&w3_eKDxr!#{W!u~-tE{x#@qX>Gubu2z zS(#m_`*he~Ch=&7z^(tmP2-(dWaqY#uWgoI0ln{WwDz0sSDm^8aRH zBAS&oEtuu{bq#G~WLF_t1vZ)oajl--p0 zQX)%J(rFdr8eva+pd+QC(aZaBe;$8nLoi{yH@fOuN#TiQ;U&MPV!E*6^EsG#cm4&zgz2C22%g=6o<-OY8P}d_qml zzm0Xic{MC6UyUr>fTv4j44hwfVP1yBjucqi*)bm{p(C{%%z4Ejzuv67XU?^fdh8#1m znPHgyub+rcE8J%>Mg z%M*6;?PpKcm&B`T1d?$V!>zDH{|~KQJZ6vZhZPn)ogWgvKKHvTs$?->$CGThjtra? zIZaXuS?8sVWo`{e_(3G!QC%8W9Eq7+=c^v_BkO6O)+{BhoF2K8n|*Rx($i;sK?_Z5bzORfvu zE=zH5|A-8H&G?3uq>mmvoXC~ho{0V4*hZC`nTbb9zpkMPTTd=Mwf*{B2A<}<(ySPN zm1v_x9hj-4g|Y53xO3|4&&AK?FU|SS3Q^K7os~Q4mW%~kTrmo3)!SPiFxygcd^SJV zp}=hOuEu6Bav;-|o}}+Gzz@;Dhd)k`ygv#35eEJzOZNZpr~hw(=KqdR5AI;UwY4pG zq$%s`CxYuFAsOR{!KFq;MZJ3U>g!k8W014sIPYU4bBu*Em_LyCAQrEE|9;$AFyWW8 zn25+yUsH)^=VPlrVYl{piVRjs=|}U@+w?mx+x(?BoAAlCA}jqOy9W*2*ZGbV!h2uO z4g6fIvo)&fI*@L6C|oELy7OVus797g>4c1I=-AH$2wTq?C|fRc57PScPUkAMG1NYM zvc1D?bXh8x81B8Jrm|S;!nidNUc0|tD!EbT^I>Svgo$6j)M@AJ1x3rzy}b0G7@bi@ zmaTm1V{HCN^F?xF_|-in=@T)`hjTd@qllHy%GE+*Y`C-q37MK-B3_5Z(VjD5GdS7i zFXM~pzeOn80h zPn@8=LZw-+*hTM1$jxjitfmOK2gJ~wQQfuc3mN(Hc#~PM+ID-@$Ky!=+nJ`cK?#{h zd#Xm)tfyj}vlJ+jE4-&3%gChkQFLa9=g}IWHh&jYhWIApOTQijQg=`&wDROs@Ws1a z02W5_jLzU{WeLaT3VTt^>1hAg;Yq9giBl;(*1>ww{O%Ux&zPtU_tJ!6<}u+uqtZ4* zbIUCyUjtG>$%-A}7`O%2M_W&&M z%~fR_7d5(FYmc!|Nv^ndAj+Pq^CXXQ>GMAjDfsy5#qR0QnC%R{_^S1>IBvP|9q}h& zMu~97{6JZUf?2Ra{PLa2ulueS6sIP9KEmeImcAs18GJ-??sUnu3#uHSG4a!T#(1_< zO;b9kbmzDNH@orrxWO&kM#;E1ExRQbIT@{@z8Ez}$L80$bEm4px>H$niEC*D!+yWF ziFnezqtonYuEd(XbL^?x6TOZ_*N&g+d!cn{;_1WFEy;Ryrh%&ol^@o8*%;+kl~L7x zgQpYmW?6|F#%fr(Xjd_0d*Sk<-`mv3CvthQOO^Y@=m*(O6=E;xXtu(vXTZ|8j+i7x zqwW<(-O+vPD3TgZUt%iFp8Bh~%uewSXX^9a@4!;OoP?J%>iVA?4I7?G>5EGBqG0X{ z>=AAsx>q#qxFe*wxqCzZ>HL7|j!;LzXGZ-?hR2uC-t{w9zV_Ulf?z0WFFc%CTqiPU zlHaB=(O981Al;=ySE~a*v^@W@b{gko{JBlGE=6HDhSd?nQ24NngH!~9s%IRtE6OR* zJ<#okn=y#dkzTCNom(s1@v-S0ad>vkGIh5@GH<|Pp1992r(1{M zC6hQ_;#}bOSozfPt)xkzsy#1pUFs0Cg6^fh6M{1t-F3CU?LOJAJv!uOPaWhmF3*0O zwNus^V~>fat5L*m9AD!*+Bm+@SG5{p9?tXy-aDlc%xvbL zQ2NzGyv5@iUtobTRrf8+OW(YU0MBu-^`58xu?l}i#cBPpZSqh>M<`#a6@2t43N>t? zfI9nK^gVac^;b2v?Y%>Nuf)T^B)OEsXzGs5xLRs*CMkwu8#?w>ra(P8g6SVI-c+QS z9uTN))dPEhoqgU*BWN4m6?@X*pekHA!=dolPC+;h|LdJc$I{WQpBgrf*D`Kgneoy4qrO!}*Xfa~v`e3PtlLC}NB5LM z6-p6Pjp$#EY_ZeIRoq$=qDX_oalV9KckGJ3hG-??Uo$1!Wo#kobV-j5`l`FcmZLma z%w~Ux_H3>D?a?c`{)2Tz?nc`QdG#qsDfZVlVtBHX6kSX#^O;X#iNbQ=bokM!4?e>h<>vy_4yH9 z_h?e(yLEP$O8!H08~OV5%ab^hI;*3@;~?QT?apAqokQ`he3ibT2mRZ3sKy5gxepsw zdMZ7&_pdCUV@K=$N*b-R?lvE|FjaawtMND*xked1SitIo*vkkvy!Ui?&CuX(r;~}L zN;oyG)4-DtUtw8p?d(Jjmd;}OAFbcFYIJpm6D#tK7DWRdkK>ro<)Yq5#8d~pm-E)>G_2ZV^(cM-Z5w--P8EUBsxdg?xV z?WA;0iz4Ieh>prUqPG3=cVU1%#p&h($EIa7y&PMj({Nux+=XO!Fg(2itcnyu3Z=Kv zq}425`>P2%cAwgJa%(PDBhe&iWPNCGaK?lNHY=-<~f3D-xVm& ztV2+visgn2SFCNJnp7|>-s$ENA7Pvk|IT{CU9DGSVpN|HdC{zpibrGLRCVBkM;SwS zdZ@$Fl7go3bfmfEDRk;yzbAll_~yU&qP4?Fx;9l%G_$N!WoXDbAgAblf2#bgqlu)) zLa)@8jr7&|q3krpq*CFV7#3gSuLlDnu}|58h$8=L;F1rb$6NHlVy|T>Q%g0=fL!-m zuZGGGLpp4!`L(ZHCgPiGZ(8JXZh{qVV=9yQ{ir*pYL09tk`nYU)%`1l@$ZHAJXadqU{w(2z^-Gc#LncgQKzQeN2dMt^JuJNVQ0qTD(Y|-X0uh{38fM%$I z#`6303}zOG9$cC;#e!Uk=jqp_ck|~zN&-_D4qey3^q-!*tfgGGL{c7SuQpyl^azId z1tgA*BU$cPC>x|J+j)_>`01DELWlN7;SZv+sVseAR+>%m-ii1%f3J*^ zg0S;T|4m`Ya9WJrkdgJ6Igf^0DF`bjJx`sUWYZWBRvo;hhSf+;Z=r;qnXErbd`tRm zmBQe88QJU?OO5H3{5=oO!1YxBUI9xB6=LsByl1iJ2kFJ{tHH$9^B5rNuo`_c3@nP1 zj7#U?-2HpDXN~sq4c)Pyihcrk%Bug?`@{*>W2#k1!1WMRO+`-y zlK*QY7ai_4ct>wBEqQlJIfo0wDo_a~qNiX5KLxG*AZPXw-a6lxU*NVSP9JWzIv_QG zo&*X=Nv6jxiv3m9Ie`Y}MI9j_VO6GroAs!+;gsRL>jJmfQXc?7g5>c07rZ_0>9JN4|E@;?*6S^I6J9P7IopgYb}DmHXZK+b%H?8z0R%j@)(+p|bcUfw+A zs#mWrX`}{(hEC~Pumyp0Id7CP_tyd}2f+qElh=yosFng>04X0#2jhWv#Q`TjPlgr- zS9Uu9$;rwp_Zam}CLRqUJFM>qwU*55`KL&pCCu7N7y2}K^Ncph?|SpE={_vTT#K+{ zMLT%aD<|TE=)72h1~@y5bE)M@=^kN;+US&UhF6zZ(f5^~!;IKYkd%iUZlM&M7JGlF z2=jaV_`WApLTpw3{0jf;G5T9RVP1J%Hj5fyF3lsXrtxigc|SI^23N!<$P;{2)wkQD z>ICy#9f+ALmz)O%)Ywvc>MQr(9s!Q*^6iqwKbuebY0&c9k5;K#u{^)`wD)e`+;2{HaFVo0xD*AG3>q*-(LV)u0 z4tdKmFeb3Mdlypm?3i0_{z~d+tCjb-?WP?iZUJ_D>knMC9k*+UH~L{ARn~fE4?`vY zRz@OTsb_;)Zs|;}>-T?6-`~!|q>Wc{~WbO8sm_vS~ucURZVdro};;W;2WR}CIKDdqh|Duu3@bTt7Pu*>Y zi8`hrETWhh^JUGEEj!|#NWLhybopQZp>AnyjfgBZ2vrEHIl$_QI)Eh9qj0qr@Hmdejl;?3LXG>X{r^Q+pZn^=~r3`EpQ4JYi>S-~W2I z_U#kp`=WcXLfu2HjQg&McpKhjHl;x^`@gf9O3aoY3NeWtY@u8p=+x`AImg19iXubJ zd>$z$tY)%)?=N#}Z}@|NI}b}Zyf!!lj>Ligx%{s-+A8iimp$3HpbtMR8J!IsO@uqE zr5}EkZ*HA@N&IZwHP>ebTUNJPRNG}amc2|Xs?_tz>vAK{zIttq$$Jnh7%80ix|8fJ z27p9;=4!3x{BJ(Ipmc%PYJ)}v-FdmBjAB8d^svZPjz#H$f?d5>-p&1s?5TyJMu8nJ z>l7yTnB8srRa2pDxh!LdV!8Qx2Q-RFTnG?n@7(_?{A7GBJ^iu94C}yL-__rNp$5sT z{OqYY)UT=JmUu_5PT*vfI||l`T}==OO5PHXoeWNSVmeI&O;>4{?oaM>>MY2G)Be0$ z<8=}ooykxPsL)w22Kdxo(pZ%sf55=U9Cnc`{<%uteLpy|5y}FH1XrI&MUTaWWlAdi zO)=Sh0y0shdXdg|y+flG_L=QK8ja+cWrafkHa#w^rAWGfDmdL(;KzWb=rlWklj#pg z(!t>mTI*f2JBj#<=?J>C*5hq3HVBr8hvLGDON|%n=laN^^B4gHI3ypn+YQbrSA9~f z5<_}X)*8}OKuNP_gXLfcSZ4vO2GKu|Zk$8W=49pf>&fyINZt&G&=QM>TODHo4RUz5 z;>Y298sfA%_EVGuc)GFiK$sQ4S6@@ohW#k&j+V6KK%z^@COMyq!8e%sw^kWq7Z0Q{ zeT=m1B%2Ny6(>(zks%wJPrcz{L6}v?Qq=WCJn!~FaJ__xoO#viV5cauTGH6vD^j^8 zHwv0L^f4MZ#V*`r|6CvTEAV6#`AOqIu%rz+AXt{A2oNm7;}bdJq#!graj7FT0?94h z#h>P>?aEr`+Y_t z)!9HUYcq(>yAd`Qw0zmxq-R7$4a?VdZ)B*0OUd9YBqIIBgiNI<6BV>m>N%&)SdgHC zta53}Nn;@I-`I6BKvM(?RqRL(e&cz=6#jt)%5fu0B;Pgdtu2fF;WW&Qfc0jexP z8F~tRNspgmT^mp@a9@ehcx=-=pD1#QV$a6XIElmBbw%?ni0Q2~0y&&HA^GglQ1fWS@j!z{?D z>=+$rt$(oqN8LAEQg66AhA*B^5FmDSLzliN=NMkfeY_h>t`I8~V5JlP10^?v*``R2 z6FGq_Wl-w%Jyna;K;C6$O$u1xYWiTv+Ra3f6Rf+e`qB zhDV!Q3Vkb6AA>TWpN+wi?q>@{kq1xppWbir(wIL18BqIVi)S%C9IhczC}%aG8#*xb zg&%omF^*S>xw+Gs^k$daI+L{@E_JT4N7&lh0G*!OZDGxMd7T<$ zoE%3eeS>V8BgBxKx&)RW$x}e$E8-!)+HEMEZTxXGn}DRvRi1#}+Rie0*?GA+=V8V;!3S&G1{`DgyrD8%=4!6LQeJ#}8RNewVoyM_x77JJaa;d~m)jci$7hnrx|i$W#f zHmxKgfnem2U}P?dqDKOwp2g>(2@>zQwb)WCBFPW0yNLYd&FyzRckn9MGPq}erqQY1 zrAb^D%08H5O%mF$HU7PQMuFV87gDm0p>Y z8dy{_b-f}>$>e-LP#@ekM zs!kJ3{7gZX#irkO+&9=<=o9H$rxCp`f`h`yED=kc#X#z`ns3Y9hfd#5UQo=b5ko1x zML<&g0IKAhpyFW^P!A%_j~C{84nl#^RL=z~!N)h^N_+0Yn!dLTn5cX^p@v;<$fuTD z0lb&wrd`ZpGk$e8!$?i?X?tg+9-*$Nw+UD5SQJ~MVY&6+JTz*V0U+pwsewn=*!7?X zaZOpEztNIB;%}XUVXHZg$|Y!XW;> zA)MVxCwL0#!UZHpc6d{YV0Xz-uRy zqPF6a>~T3}N6a^1a`DXC!J0|kqlwW*sHvE{0h&JwxX>D~I#ig_&JM!$AoJb3@25!K zNAl6-L$9ozTO^3IZDG_|kUdr8^-@gb`Wp`ClKtk`Y+;IM*xWC_!_#za0pf$@A0A!? z_u$~l15XoIP)j~o&2&%1v)K&rC=Can^b274bY2Fl>7#EP&paVmLP8=8qL`Dn{bj#x z)C1KlH`dSF8dP>ow1KpN^}bRR1N{eYkOQU|?+s>IL4C!8FfntaJaHn}0TDgVC^3C)$|*j+JDNPMoTscDv!b57^F#y=zvJlRQsij9~q z!<(i7aI7s+iz3_C-mheGbXORSu6*uFOT?!yildc2)&7tHsMu`A&UlMY*!QfzY@xe? zaF+1)dlf8gRYtHTYN9O79^>II|Hw-#lH>FdsgEbtgiiy!8EeOdN|<6ko1hq8Gs3efG?Jj?YXC^T62EyX z^}b_W71>0m!Ud7U}c|jr$810qH(g*?@Eq2(3K6?5RxQHb55KNPKy1)GV=s5GwV- zL7y#EFHlN0i}3qp15nBfChJ?!y9&oBlwNSRz@jspOpJLpOMFv&zQg9`aZi{^N&`rf zfWPNQ^4u`#^lY;}503P~tU+-!%qniSF?}ivNdRlBavAQ!*i#cnj!N==zW{pH(K%Am zXJJpR$tergyawT}gW0k8-20-Y@Q>tbKTeD(7;fr#~M|j{4 zpXt0oCD6ZSfH(c+j-f)@`pt;nNA?)5_`P%vp$M-c6}Hs5s_+2Kb8rAhZYRYH_b^#O#{1%WM4e2_i__G77>1PyJ;XCVzpm zzrri^QcOc!Gwl?yt8p^PAMluKE^0Q46&$`}fa^VLo)a&u_`vPi_(1?2K}{VYZeuc4 zBS!4P%8v*^o=Ng&*#zS&$h+G3g;}w=15{vVL$6QBN4QSsf}*XJaW=<`+t5muZ&3+~ zBlK{PLha#%DI!GHls?*{nm!!dPsDrQ;H`N#UBlsfJn}pk>7F%sp&2(hjU+tH$aPl2 zu&_0RqWkOAJ&IWo5`0Q;p=$L<>mP)c56%6){K%HLV*RMfu%h(bJ8{e+5l(-jGyV0S zkZ5@ha74dKj^{n*b&ad+skesfOG-h{O zBecHM4+0N|mO@ZM$*6bDOvy=CwF~(Pr7p3V@1>vXsH2az8K4!m=wBpTgJgB4$t61RtD`0$-DQz(HqTiP4+Swq91dO zn1AVh)xx0BNLKd6G@EuPS@HQeSrJT4dx5H}oM5tFDhP!w*mt%T@H9#`zy!v$UlDlz z34WXl#ghHhv9eiu|D`HKWp7W&!=2AWbXg7I=)9rP*xAO5C!L2Hqf)DhU9=#&@3x)o z*NjKaC(6)^tPMVI2sL^2{Hp~tR8kqJ-R;JNyqZ&YIiWJS@3EwX%Ls+~tx!hAYo`?d zfu6~i5Y+)Bb;mOMcR66sUG}K6m6k(|_21b$3);uall$&L0(`#-r-v52R3V}~nox>m zLpzxWv?Wc*mkMTe7Km-I3&X+L$OQ1RXTVDmm2v2qTlTrG^k-N!7}gWD3o-!Tll%H2 z?NR3-jF5j=An9xXcSEQZXOqjUlaY<=aw9N6+UQE zx5`&f^!deAPb1t6Ruy*@7p{I&51khs0bPB~fX^dF9_o&jZoT;U1Uba3u^M|6LJE-} z|NhS*pvJZbTGS#-5vu_g_rG4ex*f{c{|X9YC(?~OSHMf6LxdgcW)j*Kkp+WE7x;ek znvlrHE3?n=MODQ(A zIf9zj!5XawDHZf^OP}Go+<6&AI=n#g*dC?$bLks%Xks(WeD^hqNW%hoD5CA>`Xnqi zX|fOqXKp!6z;kVV$HST?%L$X~Q;))g$q@9kK!KF1zid!hpG@0RjKZXo#IU zqDk?vDKMuFI2Ng=no$@Np!b_ji5%ryivi{UjRxIsYv06oC6pkfY*G`_bED^N65E!x zdqIgK#HX@u=+Y|?iP*O3eSu|dEA6+*bOD>bNwX;GZdi}f74~*i0<$%>E;Qi;LCTlI z`HG0D9=A|;6m7^pl0EeU@+jk{%k#|H##F={z)oS8TN+aaKx5GL5=u>s{6Z9@4;;n` z%9n14%7XTqW}M*9oC;#s^C3a&-1&hGIx(cd{OS{mHIP16;9tuJIyY_33MSGJPXL9o zC@sw&4dC3}%f88;{%5XZXJ5ppJBhjj|C*YSkwMP4GZWFDc*8nZskK#Jxh2a)!{YNX zy+3BIx1ykvOlHF7)_1rqnIvgwDN_^V(voYg2T*HCPUdG)1Ft zIBn-^hEeg?Y<)mZFF8Dz547PZA(bHgW4dq~Yxf!`CaLANm%F$2oB-7Wv4++wR|9R) zpi-2cZeph04QFiXpq@goCUv1L=rAC%&h@!#khVjQl<@3C$rp~&BQoHq5<%5k2~ih~ z=zq10$5zW`i4DgDzD0TF_HhMW$%c&40gHVo==Gf?4&R|wB%75|UWj5`N5JQJG%Q-@ z-xYyIh@lQr4N?t=;`LxKq$$U|fAR;CFf>32#R;R~+%^B3u%onwo-GocdhMI28-^`4 z8kQgjRxLp>!rZZFVYdTbHSDXDmb=j2TwrgfjO^XWQHn8~pyg*xjrl9PVOE>GbB*;F zMtIf3`@$(|BXL>V^ zrVEd?vURxu*tcfUb?NXW+@%PP-P>y8&O+DBCue3{=Q`vhNk}MeVo0l~yfW-D3>!Oc z8k3Z~zZ&a*c^4^xvXZ|n%YX3Fn}F!oY2U<DO~?Ap@JRu)9`%lZZj-i^(cVd;*g4Iu3#{l?`j=)lYVhv&WbJ4cU% zT}Nw>Xxa`5e-Nrtj^(ANrymDJB|2}@BFyKI?nM0IO2P=Mh;6W@XQ#7?9xK;Snt%+; zbuk4-zp&y8N*VZDtFE5fBYTRYs3;oU87@pg)5-!Xi5)!}&7Jwua(Q$v?dG%fVR zAqn>|1J|J^Q>DS8^eF~I0m6PP_O9Ld*gzhLhs4Fua54Lmdu*wu{^~)R@3{d9IY$ML zkh?5W?xOV=RQvK4|BN!Xw)XNIVNkx`o&NFWdtHl?@Z&gGZD>48vCqb?Anz;C z>w6}06kh+^jgY71I`Pz|7V3L4-RIMt$LwUkTf1?l*9A%rojVDSa848|FI9kRYB|U8 zqdTMphj=MzZ|%ZgT1rp@|NqJtXOgs@!3XM)?xDjHZJuZ(fnA1Wlgu8Aj&NF(1ZLd9 zXGE1Y_7C*_NWpTty1K8)@(!l1YFOxuC}NYC;ej+mc~LJd+xi@3qh82BP}qRSU)?mZ zTcTUMQSM?~4|)%VlOQLc%a;1XPxjFz{lRts0B=rVu`v$c#|C&*L$f9%BkAip>y|39!MO*MM`s*?1sCj+`|=u|y!nal9N2X@wUP{&Ix9{+9&?Blv9QSPGL zbec~^ulaK{UiVsgDK|uzuA|X8Vvp>s!~gvIIk4g#I9^v#0PH zTWZL0g7nkaKp4bmb+K5g6|k_4FhXB%sd0MT-17bNjD^ZT z)q-9-2bCTgq`V*!A44|X&oBkafJG|}(w_G}#|A7g77LAcXV${3zEt0*Img z??B`05wA{>+czOlKuA_yWc5ePn$+dGl<1fcZ5hO$a0p0w%KRw`jr}MU2j0| z-AG)-%A1;7+Rud49vgr(c`)Kk?_KJMg_(Dzz~$>k;R6y=$Ym5N8NsZ*Xq{0{^Y2z_ z*tXDDceM>!xjyoHNmEG|*M%Bf4I(zY1g%=D>^-chf9aWP3V2$KfNz!STUstC2*f&I zb`3{0GS24*lfM6+E!PCL-?LURtuxSIU^P_6stTF*rq`MoV8s6qX4ygIj#$R`f`P!P zWJOWlzd<>9O`;Gjq>53?pVrAk&}B>TwGcsWlPSUI>Oy9)^z~h z?2r{8o*+;Yb5e1{P;IWq3sjB1i&>r3442Lki@wVB zSplK%+06Nra{&fs{=z*hLFI2e-btvPZ-sGk+E;OeiVhc-{8Go#dOmK%MTtxvNjA38 z(Ed!hkar+-rgw`BoR|)S2?}a5;sfnybz%dOqSm1@YvC! zQ!_KGk5)h@*CejkWJ_Fw;r@-z9$wGE&CVWSZT%|o9kDpPP3}2I9UmAldgwa>Pw?8C zUzl@)lI7vAANBQY8Vt8W6{eeU+R0sCz_c5UZQq%9eM~W2qmO?sC#aw4Rr?4YF7!zT z9E)vt;9r11ng4?P?jGYe8ST)p4fpgCo>@ot4{Oo&3o!Qt+quhj8)Mlk#`pV&gN3N}9^iAM>^ z^7Qc)&hXjU7xU^?#ezn5KB9Le3TeX*?G08>YdkzbmPZbHoImzoF!~+|%K`;)z{d%$ zbEH5(7O_)-)9j_YblUY?hAhy_DB}e8v3x zQ+@;^kKyk?9?)ai=R2+$hnHttrcN7E1f~<#H88N-{vBiyYkFO~<~}bFPqGlL&qU0y zNSsnE0`*}qFH{zAU(de2-#z;F+#P3E7xZ-X zMN8E{FpKHWtOP-)FHCA!kQPaX(kP#DfEp8@{GW${N&Ve=GXkZ=Gw}%&eW5-^#m5Z?Fpen%|A%GWzIw&W7 z!S<72`(!>k>f&lqulse?x*DXfK?pTQb_w2eVOQSb3|t854r=Z29Z=2&1uLi=edjh> z=>%jNCuCUqZ+(4yWp(F*xq*(xyhUZ0RTlBH2Z)_bF}iW?@=+&?fxCGxF^tFlmLBE^ z=p#d0VAek4O3+-OtTuIUwoU#1XPEid>1ra)Siuh&O?`d+SeNV9uM->TdLyD}RLIX! zijxJJoF&9P|7r>2)CI|4LgedQiV&pU*Fmj`^9=9QZPM)>5p^Z$P{QjKRI zvAuVKI8NV&_&046S?U=!dDf`Juct`P(fY|2zU)XSLtO(phHKJD6c)S~{9j;y29o%2 z(<}55AdN4G_-1nEtCA~D z`*>KMe&!NuGZZ1iQZ2=Lc+0Cg^I2o1&+>EeQ-K`h$uaYK0E3}pQ>dW&nW_p`rMvy{&sT%-+TX0pORaW#b%lFsb|u_)@m89 z=VqRiVUcS&$^GQY>OgS`1!-Yk$ewE)9x}U<|8Wvk=fo)=VOgTQf5)H>i5#GR=?n_< z37dPGL}1p(;~F|b^&SweH$68O8yX4f8I7(ck!(xY8Qexu!TBIAp377;L&yz4z#e9R zN)a8{Gul}I?`APDP@g@{xIP-a%8}7qmg0~gtOXHc(qz6_P@Z~b@BoY6dv9|FUV_r| z(sXrI*VFTQD)Z}EEde&S8xp%7?v71Vh!3h%ehuAj04fY6$?Cmk!?``|J%t#Z}+ZpyS>IwaZu1U3L2Lh z$o?NzaPk%K43r2g)+MrZh7k&2q-p(p#;Q9Bs;=Q%mo%uX+4C*}cE7lys6qOG0Rz8D za|PM2y5t2~9A(WL;}ax12raWr)>RvMwsm6zKg0P+zprZdT73KPyhaR6_vqlq4?t5jGGQq!f|X3Bry?fjWuKRn

WqB+4Ue}Rq>7H-m7pvOvI7a$%k443$sYjon9bdj zRUhitr@WvVEdmv3Q1p!eW%sGD%{zba9T?1 z!1Ka@h3!)qL231lH^t?(D80>g=hH?ZNEa%QJaknCyM!IIC9z~!YbHNn$9*?B3Hp52M-eNvNMpOO>l?JO;E zM|-fTx^j2>;yWPP#&4W*nN2Y1Gc{nE?|Yu5DMBR9^Gvmsl}X16=$(=$Cu1EduR$d| z4ru)F$;Xx5i;!8A{F=KIYMvK3s^mC5dl|fh^QwL2ppp*db2Z;Te@a?Ai3WqQA#I1L-ZdG;H1D3vn+i2Zr_K;B2Z>)UP}ux zMBW%02jfRpyh9}%_y=@p`OC&vZ!>C;9w*&@`XdR8?+W)@2ivwM@x|6(ZfID9#pCsR z2rY*)vhzu`8la2Q%E8W-^%<1;e&~lJSlRrVlvTr?stTo+o6dwasUrF>UzbfC`Qle! zHk~&OiV*v|F)?^~JCbvt+4M?cu?$PJF&6D97U%pLaKB<+CDj#0>>S95t&2Shz{w~>M&K|Ub1s69kvx{mu18O~g8bu9EdH{fZ0g&MX7$HU5=un0qE6lec6LP)KxE65 zLFH#aHiq>&WX%=yWALDpcZ&Q_PlAFfOXq3Eu1@4dM@-QD5>+q<^u=5u19J|@mnA@5 zarTygoQA@@$d-#1rUP_}2DkVJ2zFWD2d#f}8GPKeI^0Sp-T@2zaUY@(;J|wSL%~n2 zfEv{tOwnhL0Pa9g&~yd~EYgH_?6vfJ@LlT73Sy^r9(hki*naTm< zu)CzdG$B9!oKQ9UXyCJ@h!ijutqDngeY~tXzrdTOFT3?HhdNV_(Lc zb!xe*u+F|17Te^C`E*p9{j|*5x66*rXm=!OHz=?n@U7-PN zh00=CdVgZ!lo*odb+ua5?%RaYEnKEufZrN-S){S!*?m4$Nrzb=Su&_TT?bo%aP1}s zpjnzgG6SAhr4iH}Gs_pOt*qc_@(cnKrWl8z;u2?()=~YQJCUKFp})#$_*v!nc_P91 z;Se@}1&4n2<{=iX&{+N3YfHz?OV!5dxk4cdz9=8<3R|^cZnn|7ass6R zFvd@c-$X&lW^oCCT3;LP;kqbNH87M;P8=|&Fwiz}jfu9!Ykh=%J0w5A9)+9-hV{a3 zT8W^7McuWfN_!xk5oU-7O*%~iHOAlHBKpZD+CzU+Fws0k>6V|a55;8yx^*q5H1Dw6 z-VwA8NeG=lSS?!J`%a^VUC+p)k*i7UBdCt%jOMNeJQ%{hKLuWW(XMpO{k^jX4U;2R zslg8ZTZl*ith`y2X{7$nChML5jfR2IS{E19pk!Yd^$a+H=BT>714ftdUXh0Lau=b+ zDBqh7G}_stKHUJ4iAq17IwRpq6t5 zZ_nrgB}o??Knsmtc8%xqFr9}zR%zJfUerJpO3$FZKS)X-uc7)~le}2C;^?5qGYGR=!9!#`f_fqaN)# z*X-mkV5&5jT7S(ZwP@&r2GsWUeIru8@3?q{5HF-33FDztKPXIp4)pdK=#30qO+%cN zJw1DB-=RRctSVOHSL5`IiPjF&L;EiIS6t9v3t^;#u`j z$@qOQb2J^_Xc*!ZsGptDT09{O+KWC?1F`JyezgT<-W_H?;m5KZ$kT@%0T+kDhLItR zurSbAcm+Cah2cFYzf^7P36eYvHgqGk<@sDZZw2R?i50fAUk`6m2Tr~iEKvPiP8g#B zy0ivtBL)Vf6|YOB0(H7N?+*44u(_siGpR$BNSxsL8bR;h<(6mi2|Xz3VjiY*z&#Qg{*^z1o*4VtjnOvN$*}kV-CmYG%ffHpQQK z@}u{0L<OcKwWcjbFq`O-&= zJJMv{bI~uas}En^875{O-D+pg==Evmi{GGj`xI{0qtOt0IS`l{@_(ubm;BK3JaU}R zl0b^bTTsjC_mMZ0PFiiLy)#hL*ff&kO-O0F{)v~a%5_BwA9q@sC)hU4o4(H)^S)xn(A?DsdF}H}yN z)P=1$Q?a635Xy(eCuP6=hT3v;9cME+d>?eh(hfC`=s91jaXNSqDYA=m6&s%5%WH9) z>-qL7NXlL)qpWAR{D=P0o3yN3wE+J6;~egYA2Wc%A;0&&Il0n&hrpyoW7a` z35YYPY8a5MgMDLqqp;eeO4O-M9LKydFHYdiaj-MA|}A%y$E<}Y9Od* z>hLj#v!^m|Fh_b8sYH2#%3QNKR8m zeB}Ng%+Jzs{+*_=XBLz^+!EAb7Id7eH^F>+HIxK&`TK1PRRr*@<)HE2?en3d7(cQu z&CT^n75jSWye1W&4*LPudOzJXGGvDis)#$G^Rx4Hj?e|I|< z;VI>mWiu8kK8?eUMjd5EmxFHlvLzpa!|*qO4c=7eXT)HBoNNKdujAkzUW(1#FAH~WufP}V6GN%%=6vHvTo2SqPvAn+y?mrKF$NFn>+#W^lbH5p& zjB+peJMNk*3}U!iT=8F^XQ}d`J_A602z3AIH-Z+C-5t|hyRLAq?H2Pa#}OWhL%61l z%tVGQ**LBk`W}gDzg`j!YiFf1c@GK|jwa4rde#q>eaWAL`V}XPHd`oAY>h9V84pw#qAHi07Y1 z`WUdK-YCD%I0Trv{#4}sW>KV==Mc6RILz$PzCj}lOQihw8JwWS%|woqdZ6Lk?eS;K zakFZEt}P`k36%N&^%HOsgPrC9>YC1;d1}p`x1FGr-Q3U1ywlkZ6&1X;fHi@yI&`(& zb+k#!&x6B!&C{YY!8nYU6NK1>hhH6?RTXyl#Ag}tctF<+!{Q}PUkBqvwysJ?SL*ee zCV=Xl+2l}XuWg_vNiXm^A{_ZO+Z#HOVn}km=&MReV-J58bU43s_(MY*_$)O+eX6Cn zgor6{Zvj0a%e8$O=>yzai(&-Ow-i&|zXMD&6qqLYF3)}NBz+0?CC;0~u6l;$p=^~T zchjLZq5go2vHOMTKh*WOxoDzkT}Rz(*!@$j8HfHavfeuo%lGXcze^EWnPqloD-@#4 zy6rtOiqKFtWpBwQGb3c(Hlbt-A@A%Rxvi94_TIna();s#p6C1fOLSe=d0po?&f`3e z*Xwn3bMzEsEr?v9?5#?lk>+{3o51zdp#w|6!P zNuF5m9oeRsS;WIgGBG&+yH501SxqTdDt+_jkv^p`ibzB$`_OfM}yEwr?HN6B+PYbcu~ z@om`vd6wD7x&V?;n+}6U!a1Mq4W9Fpl%L=k43>+XJ_uyRsQ}!!cAx$B-vUP3zk0s4 zbd7e6UCtZLDb31g46~%jGP677@^SM{`pgUhtVJPIiENPsHyQQ}w!N@GP0^ll&*-hG z@zI+lceSdcK*}#S2EImOs=E)lWr#Y)Z&}wsQXBCKW ze5yt|&x>@TPdWp7>qa}si2#)SZjDT|Y@)D@Bt-&!DPaDHslwplHm!wH&`&MgoD`WT zLs%55n)LYtV@&*Bk_%{&xJeme#}Z3=m$o8Ujt8PeA8kcwE0|j|4SuoSS+cU*issEj#`~ z%f&cxA++m|ouoXFwGsQUJZej@+03>FCGCP(B=et~!_@fS?mUw4ogovt)AW!p`># z)LU4)vmsw?B%}I$jiJ|(Prdj5EMGF17}fOU4++2u74yoa72U8tz8vBw$RH^Lq(!NHb}4(!s^SLEV-`d667Nu!J>&OtzQMTrG!DsHbswB&C}BB#2qu3s^OrJv%)@Z zW+A02e+qhr++X_SRfsA4VdMTKYrfdSyS;0)XtciGaRIHSpC167K`3?U1Oj~BqySvz z^)1tJy|J3iTAh9u&1NvPFd}XZ5%MT@us@;F&Li?8=>^%x*wHhD&^{X4e*oN{?@h<1 zHW&^a6>|DrCOBNCO(_*4Ej9XPj03_)fRATKJ{~}#pJ`h2m&6ag!Q2PhM2Keo4<;+T z(D)U8^fwL%n}Sc5xG~?hUPr!-`1y~k{VUt*dT72La&!8#<;LO=_K^K&v?!l8ljgHH z4j)K4u+uXA0oH9ktrY}x#%vyQw=D%~1|}=RtC0g)B(U~dKo}AwTX^Y*r*E;;qq@E; zj)PDx53!H>~EYzVqxf4vdr+(vw#c<44aOP9S==uBh3B! zviqz3Cc%~|{YOAyN0)JgzyQpsO+l|vAoB&%NaT!<2W%ilZ-e)BG$)55bK-?t$w7~! zvC>(Q<;!ly|540C6K`jI>w-D1!!R|y^Fdi@SVB2MMq@f)lgccKCY z#M3K4f}){L>QndJn9ne3@kQ*f4t162l2Zn2_^!ZV%IY5eHX(eAqC-g_Ff5(JuL z2u87~ffbN_-N#3yl6NCs(qQ|U{*wxlz6>dw3#ZdJ^{Z0IsZW%?bQ>-3Sl;~tFV28gOm;xgmNaU>dm?EwzWT=pJ{6IC}rzoLBdwY^LHS!gY?~- zvb{iMd1T(cf>V`m@i{N0FGeiD$eFFX2FsD(bVoKmqAcl5*ZaCm1vL>!BD>@j-q6=_ zP6k#gYx}K7l;#+^nvEBnr(Zlf0|%cs*B<`jjgQb_*q4oVe3`|f-}7fM(c<~X_vGHN zQ9r%-0gsgxh4?vsj!9J)DSO+k_N2`?Oht$I`;go3OEq?3h z@}%X2j@&6UcL6GO0btn<^71GIM?Deel>etXoU*r{Cy7K57qhkq!{zFPb@^vnUMO!ro#qCj((ApZQoR3XwMK z)JSPJ!TEj%g+j$B3*DKjwH=WEf8y#S!Lq_CSjRuh7KsoYF6SxfgfY zL!|iNx=3-Zo`s@2Fln_g4aBS(S}sTR^maOsD|tBIi-6)zqTay~b|Q_mFzKy>UNk-- zv}g)@i8ewMg+bEjcR=FWfQ)wA-R03BLY;$c>0$RyeQ8f5&sBhhRLl5n0CJv+Wb}Oi zm^8>y%W$Eji7*ttw>by~W#!5~Cv;l7Mn&M@9Y)B*0GJ;9zkFzvG-#RF>->9Vf8pUm z-+&=C^*OZ3#kozuZOFnX+?-F1{JVt*Uh!p}jsB$n1e6gVgC|may0j$+eLaY6IZ~_ zcsDM%64CKYJL3X4srg=uKCjCN)51&|;)FynT1*raFGyf`I5G71s~7*NeEFTZ+1K9f z_j!#I3v>cB;H3cqAZm%2wFyi0&KKTI4c@J!8-K7irDn^vt}co&mz!snzQ*8P>AYTJ z>k9{!t8JohcBrxW&}gC?OZz6orqfL%79|5LPX_sCyxJw~si}}+v6WE|Y7LX_J zi@X-f5gL1XTC}1p0f0S!coge_*`SBrqoi-U;rtTuUA$ROR`Du9jwwCTPR*zej!>VU zf*%w8fB@iNnk{+fHHdJ87U}zx^ws*>Yb_RdH_#mk@3o>|KJmByj;OaCp9N8Wz^@je z`SSxOFMkLbEK*Q`$YlXqoP(RmtPd^#2IZR7;KTTc0Y-5SXd;$8DiyDuv@q0nGF%O# z2q(96E0a}_jrOu%LGyclp{Mv~~=pEc`=gHndLy z&-H#-dV&F3KS^l*%!-xO>rZ?{b0A+9K$U&dSa|$>PyLVv7hCt=`-dT0a}PoM*z!Hv z=RS)@pSVGVq1b-nd3-PAqRLD#B3`@2%JPYWe`qovoB+FxD20M910%3#lz_4v8S$zV z5xe+kf-yHUo7-!VSCxN^KWzGSgn*FnGeAW@zkp>O89`in97lr7E)hP@hZFdO-(UFe z8DV&6Hn{P4Ap6)#h>))3<^3Tc{ae?QlTx#f&`o8bkhHMI#>Ah~@0k?lq`q>!bgJoWgx`#-6}qGAO9KFb z8Uou&1-KPr4{ec8mHJ;!DL1zzo{xnb3YJ*nMb>)Cr}%@i82%ssYPjtJjZg?$N)D(m z<$+cIga3@e#SD`fFyc+)NPDi390C!F4rnAF`m+Lv;Ad6?%0LDp=#tUj|7KJ}F;D}* z%N!OO66&#fYCZgwa4z@uFB@I@eXR^24PK6nzMoE?H^JKUAFq_u`q= zZ|H01Qc12=FRo=NJ<8lLB( zzSaJo3#hMFt2-QZxm6_>Hwh4Bm&0*L6aGIpP{9p!l|p`$y*9E<*M)>bG<$KbRydoR z=&AMn0iTywG#0gw51-#SHs0IG2?3%{{}fD63-YK>rh~a>5%*TyP9wVPvf(-s(K1)H zq0ThSWM@qTlnWpXWxz`iJA-bqYqeMu7q^Ga>tTKrsD$)x_y7P%v2Uz(;kr2n;j{kd z$%*7qV%g(S8*d{F&tK5h*X2_Ll_5}3lZ|$bp`@h1A1pfqQ_SSQS&VDvA72#`inYj% z=R8S&*JG9w39NmzlgS182cRWrG7YD`_)5bBc6<12;Th6P#pF_&ii*MQve5y83Y;t~ zEP!?z?pOhTd_}z6FE|At4?A3{VsIkdKlqpRiy}d{e!a3Su1lrH^>OM9Amu zhSns#i2#%~sVGdfi#mR+LAFtr7j6NHs)_4;^R$ngF(%=Ybv|cDSKEa>B~|%2SI^6Q z9zL9#$uCFRoDd3hF8`BPInQ2x@r6`fiREYoAWk@5-7QyP&+xt+1^fRE%Kzg!Z$S2E z@<746^yPJ6p?Up{@|@KP>K)KYK}W)Nl{wa@Y0&$A2+-#m6Q>4=LSpJmUrTe@H zwkq&vxbJomzLQ|T6(5xchm}Yt#+5<&XkVa2uMRH_J_SvulOca?0>pv@L0yY=x&jn9 zU~M_W+Ik^73sINbhuuQPP3*jtdmY?>gBD{2YFxoLK#NXnjJ-s>%~bD*$)^J43xF8` zh;n)U@HAs%g#5c}EFf8vt*oC9OXXpI{|G;{7Y&r^KZ{G2D65Zlg zd-oG))mGmb#=iwI3ssPxP+?@tVs_cvNggC>8sR6u0`7#qo$gGGP9W4nFgl}P{c+>_ z1!PBUs}2SpF&wRN?;jyiBhrPYQ313jrDhkkem=w}xx00y-Ky(0ue0td*+DsJ6^CD|yYjY`BD^CIL#l=c-ezeOo`HX=C3R>WJ zXO_20x|Tu=_JdI;;GtNFb4ZXthi$k)HZMdBr+(BL^N|Hjh5>9du=df*vO*g#m+U;f zXv|d@s*6D1;MFknSei?N2z97IRePJ%rAjze^Uz%i1z}hFR_WV~J#U!YMO5%O6ewm+Gpuv3kF+ zubIV(i?7I|-kMC8dF)@;wB>vL(ed#8lHj)yns0^Ei4h|pXp+lf(UtU;Q?dROGED|< z%iF#=Uf3#2J7;0I`=>L4%ylj7%`hUa+pm=6f7K;`i+BobzeiJ2e$t%4xkU!8L(oF4GCUy~g& zu_68G+yATPA$57a^xx|*q0lYYf?M+egsl}f#@ym--$*ZPF=+E=q1D&^Xw1hmB^3PT z!w?T5J0Ld9HZvpRD-HYpWA_ivqir>AZ|4uKRONL!#QHRifSc9|qyb?zR({)Ch}wlJ zbK~dkEUIX2&thj5C~hH*kBJhn6nC7SxrVp3wGH4%$mQBd?NDpXXcmI}N5ZwIAC%?I za_?cOPU0NdXE@SlSkNbKeStZ9tHYne=!!GmTlG6NsGYj2d zBHg?ECN)UBqa~w7lnj|vILN$!WdVs~h6j_l=q7P51Tmh_2cjcNfPNZf?RcMZ_U;d8pnAZnTayVh|bwK?C&LruY$A_DETerCB|t3!tkH}C0!$76#X(% zV9*9Y>G8_)XGs~T!O8=-*Z7V?qHc?C^y}}C;ti_?+isRl0ShoH-cb|o5T{-ByrKC8 z_JyR+=M#bM3>~SZWmuX!Bl#R7BD5ZW?ROY~y#zssv1c$QUfX_n@iMNc;BtDWyU zgW5*)cThLD44R0dHx4xH(sS&RKx)0!x8&s7;4Y%~01_N0UZ_A57^TqzN<5JAV5~Xp zS}@-tE*u4<6G#m49tMR*k8vN(egiq#tD=dLv(5M@RqqJnvqJ*oz*j8WOR*U(GmI8V zNr)IZ0G@S^6CFp!fA+10#|dGU04OFw{UJef?Q%P+B8Qe&D;|K!1j&z4I1kHw1c4>& z)E~mQul3}qEcB=wq!`WUQPFD1Ag_q0wAI{W=Vs{SKxcwl)ncjH2E-E~${wug4EhhK z&q3r-*xa;zMb)Fa3Ez^uuUo&iGD3&~*7PtU{|Zb|%5aH+z6a`u`Wb~Dd#l}$w-KZp z+xcDWDwLU|Go`-vyLZc!ULnO}H)YRl)MoUS_5=;eL zjQfdi0y`xn@_K*5bX(O}A`5u;9ZC-5W=>)b^8P|N6tyaFQA59tF}M#qFE}?oA&d9A z89*IDsh@VNkir3lA_XN_MFoY(p}fH2Zn>gN^lN7W5@lB{ znRo@UWwD_d$;_|gUL5R>2a&(94c>plfXA}#7_ACAf9YO}^(4zlCjniEg3`iUsnYzL z6A0pusEd%+HmI^cMF39tHUgfSNk)SXydV|#wwDk^4Zl#aYh@9dyD<2G$uHC*oHPPe+#oDJ*>QX_dJEd zV;O(`p=M`gp_8aZ7(x)R#Z_%P%{3UYXs}`(3bE~9w3Yj|IxJunBg#QbhBlrY>;pl- zOPa}U18GY+k>YE_K^fOvX3+5wV{fZPXkUk-UMRuA_VX*zfVfs)!R(nXPV~*^_!Ksd z;QCQ0ICMG{M<&0qJcYVXf^NZVby`9^@xx4=CLlP7S&w5eF4-*f`u}dzgvj#-FS6d7 zx?L{}i2t{gy|GM3%Y4@wxma&n;zz}@JIIy+0Yv{dquBbB_n^b0DUfXlcyH zPJaSLhLI=A?#-x2l2qTx>!Ee-_IY=R7Ek%KWyqRtWqa|zt%9aW*1hG2XE+dR>O7@t`$JB$c_ zuGL*DL(Q)HR6KuFROAhRH_MX#X@yA7U#(Id<448kP%k*hqZ-nkCjvmVk#y%Pn4BqJ zK{l80oHF4Q-)N9mas6j59Z}|EM3ni+kdNrTB~Rp&S8@tWDlz>~9HnO5g%Fw28W%CN zK?@&XizVJh;5#zvZ@o9vawbG>fJkuvy$#`yd=5ulP~}F|8L});YAD|AqgKiLYvCLj zqA~wk)uBWmCB&-o)x!XzpzMOfKJT0|jJKO&=@Za$ zTYS?6_rMBhWGgioKFNDGIT8*UUoPvg_7sb*I#`D z{9Wv-n^^b&vTc&s@pf+*cAS+5n$d5@a7N{*((#jaCP^gKQ5ym=zpnIF_)k&E9 zXfXO*emUSOP0U}KKBS**{zvRh#ItZA)UESq)_Co=%O?=MaCO>z&1?p!X%MWVJXkE?H~jHXGc5^%DnI1o0w5khijyo zk{O#clL414N00ifc}9UT=jI25$bCJ#%w5ca3kvO={nKj%kIOhr30+bB`$Qcnd7_Nm zDnXm>Bk;E_f7n~aiw7JWcE6$7M_rz@|b5|G{bBj9bV0 zy{1@<2<1!{36s2E=mQo_+^H#A_kBqbz2?;lNwOI=DfNi=#{lj*p!oL{30d2MzMN zcgOR4sW1@6G#yCEL`_8YhDe(*Exi;EmdN>s$BHknbHS1NiuAPxqMIdmq z_+C4v2S|dpESh!4)XI#T;Q%SOWH*kD3V#~x9f{8~-fV@0 z67?NU#Nkn;2cT%QxJOGer7LWl{I6M|lfkQ%dQ`})PJetM0j8I7i|-HOIghr-IFN+a zF-U+pd&@{5lWv+b{+*sMs6rGy6{GWoeF=cGG{ol!Q3!8Et(h5T&HG!CEZa%}Gzk+m zo{wB40!=y@jO`Ol?^hP&x(5Vj6dD$I(m^$KaZ4BI2_QIzgbo+B@AYsNU$rWj9PrHm zLFos9@+c(nX}-4H`W&Y8$juU8#oa4-FH5XwoY;hfYXmdNVJ1tPC1lEE8oAtiwXmvvjb4U)N0KHBBz&-YoS~#PX}qgL|bSCnN{F*x)&{ z6R44fbhLs1UXX~&@JQ0Rge(#eh)NVGeu&&Eyz)qX6)Jo9Dqa#-(H9UYCM=w5&SJhq zas0YTGngn(e2Q^1T<3Fv-z5XlVC(GQ(H-QGZ<1+HpBX_o=lS#J2Dqj#Uji=~X)P9U z2MY>BKzy|iPEvmkYY5I}6-5!YR5A{&OG!UM8XWvrxfMl4Pp{-1GYmjp@az=_kw>|Mm#~2hYMRG>JEZs96a*(2e|NK}%Tq5!z??}_BBf}4X| zkbLiBzAhXj()DannWahlo86|zTE0Ta^_4RM{U=e*5p7bvfMWulD#?@6T94)gt&oHq zat6FTQZ~g!nq($D{vjn67`%xU;s>@0m8PdqD8rxA5&gZ(SC`uAW_-13tr%$ap#DSx zC``IKFLMa}?2ea(Dpp1*HRdwYb9)ZwMPY}n9$|=T6$<^~t9A9KTR3jXrrbI~2)2$j z4xwUTx+=@7#+yk?7Rk4_M&rd1A)W6024uEC76UAt7PG-uKN^+3>DLirHy>Jc`n`m} zW-MXLIf2jzhUZYIGwr45xqn^)qlmi0GgRqHLRLY~8X*_p!eLYUaHP!gMi*YY{h5h; z-pv|q&(L)+oV+EE5`@CN0<@l2oA2w|J%IvEm5ZBoD)ei?P})vmkyybmJp}}9tE?}^ z-Myxg__a<-c{)kQABb_Z;pN0O4e7fL*D3jvIkKcv{p|)xLrM;f7qg_U^@R(Dh3Ye&Y8p+Z$+yH4<*$THE`!eqSqsFZ69@ zL(fgb`~o+-($18S77Vq+d}kUkZ;sVX%XJLTQCpWkYziw+{>}R@4U-h5eubyopKC1%`?~pktaJY_b{RUcnuC45|Vu(vtVDncT%)&Fw z!oC@;9(J%oHiVmW8Iw}7Q~i~+Ab+DxbW7V_GEPgkawMrdtQpqB89ZZLe z#PL>%iC<5kK3gzch+1U02N_!P3WLvnX|T>g*b6G5n8-Q=$`i+vpiqv}pZ<@<5u^2o z>UmBg#X}~6&AVAy?_OeE_C6k#*necFU*WTeTWO;MtL?Lf(HzP;ckKSFQTYj~=s!k; z!a!+`{dX;AJ57Ga7(3afdQ01oRrL zC9#mVZely&3V$}>(m$QaCU3CYh z^Uc128_PCzw4(Z6d_sI)Z|WRZJEit`X|%@3TyxaG)&xG+{2#kCPsmH`m>@LnE2tD* z6__#{eo0W;;kou+&G*+OTaQ@I!&LrtgNv0KIY&al25&59vl(tADWWs!lb}^U<~oxM z6trtJ8o(_!jfJ^=fZmN{pp4?)RG#sT9zOCC8Vrt%^GV3+C?L!&ExqnAJaIcETo;ZC zpuI34Ub4T*0*h{?=KIz&KA1l@Vg)doP+9I~OSzLQj|90bx}- z{n^@9X5{_9?YtRgif%QWz)tAraQn{=sRILs3=vsbQZtSt`O-3m{Y}xQqT+u=ixa^mES#NoDzj za>?KQrHNRu9xsOBo){~Je+YAb{9X#VMA=sYsVnM6RS~7#vGfG=$6>E~qC9zx*`?o> zX1{Ouf|2{wUJEmD7RkTmg%`{;kiyz&e+Af44;&5xp*LUow=>x^MGPJ8F8@*jCmQu= z9OmC)_1}6w!6()FrU4dZ1@cUQiXsH~JCsf1D63NwD~9E}J^@9fuQq0(bEhYtLaepX zf}`>(xG-=Y?tD|WInh`Jy+xy}zq3;<_095&()oaPS!Pf8jt4`6FzNQs!HGMg9@~5* z2xX>*bn+O7?2Y(`!Y524YzYS=hGrYdVDa;d`ey_u-GLf0r29r4WQ(?GZ?hJ%YvI#H ziWexT9ZQ||ChDttE}HEu$M9u^8#+JGeX8wuW3oGyOcD+%cfa$pUe~!_*T1IE5S7>a z#hv94m`CbzW&D!qw@G*1_vf4Rd6U;9O`UInKXdQ24Cwu;jeAUnTpu2P`%&*N(fI@l z_3}}5guUI)48(g>Jyof8z~mn?4$;xkburY*UoZtqzlzX&yJ^I#TnTe{a<&2{0hr8i zpKrS(nydWiXfne7sMnMCrrQQSjKvp0E1|_^nv3UGdaK(43w~=dpSnqB93Y9!1*-H( z6lzv%*bh;V)lkJA1%C}^+EBdPNND!Cm(?|Ap*i+xT$tsI>h!4!+Qe9LS)WP1(5k(< zwOzklrK1x2H6ZiFF-cS|Jvy9B93 znjBw3sgS_JZ6#?s%NMZf~2?hBcP%U%K--nuk)lq%1od%Mgvv1#!)eRV@Z@PUL zg!k2-Ona=hs(#hU@OZYp2rPDxcXb$HAXN~cQ3i2I&<2~-Lfh=BHWl527c>~}m(DdE zJA5a}Gdl8+Nt4#y3_LG#hfNYV)gk3=_C9N9uNh7FQUU7RJDaHI%C6goCs%hZ=boyp zU%UTo23a)XFxjpPUKbazBnrro`E}S#*b*FO$#@vZ9;Y^bUhrL4^v&0?HhQI(p%YZ6qYDzL*s$76GlwMCE;=KA<^ zi5IiPZ;r>)7BnjO{P)L_{#xbYwaZJA@rtM6A%D8^BB&@S`!Wsg-|sd$8Y!&ZI^g@l z%BnyqX|5z6@HC`K{qzP4PN``9;9EeW7tSe)edW!4@>g){{m)Sm()IFC0Z@U9i|dnh z?>31jLeg`-1z!7++t6^`Vw#cQHF@5mL%FW;!^M!9@q^VNwc10M@DGv~u1?7{nwVkK zUYxE=h3Qxk!-nQ!XJ?m`Od0(4*;v-IWnl?QO%7T&dhV~piC1sTuk3VhNiZDZE_-+# zSqdLu6~x%%#t*9YygrG~(vHd=4M$u^=xz*oxNc@@KEU@V8y@=E;R>MwWL+W=F|KPl z5$k(o_~!g76*PzE7EEd-u@m_h;XAGx|1CTp7pMDdrG4q;zGw6DRxxwfn*yWc#ENAf zmwbYE^2t%3OqyyNbLG^M>)uH55y5@dGRG3O_n4wM(HbDs3x7gozh`A_`sbfkn{dvn zAiBFO0)KmP=Pm_8@b)i?gx@{~fzB6l>9Jra_)kbxCHON6k$0Rbrv%gTnE@8r>Z)ys zYhzDr*V~&cOwwJxgp>^x^pQtj!&QG(Is68$#kaxG&`@fNiCZ%PhB9J-XGCa3ICuGK zcx!Ik-*~w6jC@og8U7)OZT3p-Kg%K^kvzuk`mw?7pl_g|x}LOWuOa&y8{3qc*X|t< z`F-^0QG?qfmVIxmQc~C@Uf#}LfeSliR7SRLTZ@^m6)xMqnmDMYg}wj~o{W`_nm423J+QDm9iuy-_&{1|Uk(uZ^ z#|y(Gddl6&K=ae38MNQd3!0K2=$5FfqZfQ!n@b8M1f|u`KfOs0C>t)@+it(>O>TRg zg{8%Ek5LKh%*1N+c=xSnJ~n6Rx>>LmjBgcNmdnH^-n9HK&-9mnc3?N}N=UE9vP4dG z+QZdU(>wOk>$U1UN!KuHX%vmG)k3A(0@vpssRZycdGd$|l#uoJj^|T~kh@lr9%=S5 zX~r<6kc7^9rq0Ujl-%K&5O`3LvTp1Cu)DB#Plm(0P}aPwl&tZxq`Nq(Z@7ItnIf4b((CCgSb^!K%nLPzEE4ML_Bg78IMUED9aoxiVRyBpRc|cj zkfBGZPiU@g{fdZ)#zKgBZjCFQnSqBX=ko2IwiO~HRbM*foAJ`P1(-Rd>?ywJE&rj2 znl+ufI_n$!b%AZaEa)qdxx(ydCjLJAg<49j<{5*=W$|=d!^rGp-ZPD{>%Hq?nmjf2 zC8{X|=4zrWdDQFno-!l{w|iuB-U{`48vpFgj=MN=3rjH;=wF*D#!+%zgwvJqaCugL zHNP)@Qtga*h+gkCiXO~uo?P*Q<$-NV8NY=X2~!^}RsD3`87ob6!7Nz{QEp~(?UUgP zX5(JFcwdGylG5PAqZ4j&y7Ja}l&IjSXnWlh)g~OMt%At{gm{Hzet*K6i=tStdbjm| z9b_9!S@lY}QcFDM7Gc0i?eZ;LT_C)kCA4l|`$*eerM2%;S7KV31+ObJr>E0)>h|5) z8UZo)yeXESPw&gSb*9i#*X@Xopy1;-W?Minc{xj}Ybjmse<1`P6yRV}JgI zK#_^7pzlzf-dtg!QvIjXcsoJs{_Pe!iZL=OE1hu1TUrENyy)BmOAEEohiJyDNi2rh zua&Zc3h}x7>j>6fCP+qWj=*b$j|!6_HXQ; ztclcYxl_|INf=$QJ?n3EzI4JuUi?|(>r|TFrdDQDW32Sr!}L*h{{S_$8A)u8xUGvl!0Ru|FL_>DVj4G@n2)8kg+4NpuU4_~cDK{ES7li7j-utzZfVS} z`Bt8x`@EGP*nEmw#;t*5KZ(=RLjDI?^o|df+q~s+acJEpw3LG9m73EXkwcnnfT5_- z-o2BaOd7-|x{`X3E<-kVARzWpODW}Y9@dHXbG3}=S@(0@1KU!+m(-}eFs5y*kJT0q zY6wb>5~NZOCdl3ddtMO?az15AP7s+1J%a`2t+?zFs z0JCuNKHeuX`x}S2ZrL1fXYx=oD#mju<~X_(0=?`lRWFj|n176UmWYd#O6oa1Q_s|i zjzrOS=XYD%6$InD^Y6Hsx0o6j25{rTz>M_YGLknv`$*+>BGxLHKE%O0#$kqiyiG8 zb+Lg7C!zbyoH#n|9*u6*-f<6e1^Vox;Q6rghF$Jw%&80dl%)UQQU5U_Je=|E=?u{{ z(vlh0uvpmf#?H_sQ%ZI2-fHQSCo%0hNYcgy-jPsuzi5wcaT-~!`6Z>EZ<=Iy>2p9+ z`|XvR9Q#5mJr@>lsWWrBYIMk+{38`$Y5z+o%_&+{B;5`@F61?9^+Rags8Qf@PwZSf zou{gnebc*pAD{MRso1vp=UhnB%>YL`PxtQmmHa>g*__xyibk+Yo3BX&f-bT2D<><9 zU`d7P>2|meyH)4;o{LK`P*glICJlC!dbe}CXm}z!dNU$C@Ra9x?ujCGf!&PefQPrM z>p6IqgEH~g&E}U=P05DII3EucKUt^J$V;zf@IaZhd3W71HBIyuRw{kCe=C^jwJg3M%bYMU?ZqSm!+*VEI5#0*Jx_imZ^A6gt1Xynp{2OB1AdJ8H2b}uyb5*do( zyLm{D>B52)Pw_CnwM3PcO77_Zzonu z25NklW>C8}d)<&OeYqJ-mQvQJ&#r~ZmMJ+CSf>2N=c{v~>u#g)cvPSWqeek$c8G}4 zx-C}b*1Elil1qlYUw07JDW%W^mvtx|4B@jPP(!{zeGo_27krq*mX|5)C#kY-^g4wlC|MQW(3Is z!d#)WKWfu=GoG%GbZy)`1_os_nDnjQs$c6@)diW-n76c1vw}T0Dae?98>6r7=l8c@ zf(kVng$=Q1{mN41rm#(qVV*?>ix~OONcZpXi)m0Wo@0!2e&CzT%l8N;ekiShu3?~6 zoYvRm@p$W;B*C#CgvB)YQHaNxyCmV3If9Asn5nLJd;QV2*UN!H)7f60Ll@W;w7M(d z*hFaZG8toMydA$UFYl0X9%)Zu4*ObuyTa^n)GD!A*IUmHV8*{814}9o%LtBLfImT} z=}hj2PqC)r%cPX%${adkS9=|Am!wec@}*qEm?$rPG6sjyP%mxmv){UBk6Zn3ZkJE) z7rKcS1P)$*_Vb?&oK(0fU8FilSk!#S*tx2`JX&tllkSUlMxoMbj+vhj+u5lqwE4xN z6H=?vJ@FX}eR3~4KTX_0u2Ve9bP7-vWlW!_jFD3;UC+9sw96+s$n4tmnIuOinG$!= zD91bv1~xK4O^%gM^j)KA~B%+d8hj6|Obhk=->94(2sftWfhIX;yo;hJ;`14{qF zOQjhd<`Vxb#9=qpkZ}#uvOCviq0tEa>cvg_wJ$6=|7Odp)*ZgE&BRb|_4y*V>c4qKUzye*hY?7g_jl{|vN!hF+l!OP(MwtJ;++tu5gsEgU>Q1!xx zE}pVKy(+1?eg((u$tSo?fyDSr8bW5Y)uEp1x*SL_cL~A^`Z_S&0HXl#6 z$$EjhPXcKuQ?C#SSxP=GEIX-=jH-r6yC=((c`_@9ZGl=i0(!as1#Ycn#!mICOm%%J zcU}xLTP?9^QgUW8rK~o)q0H+2sVnszgMyQ$1lxJvQbVK+{a%%sRoq+Sne&^CX!3Ei|Ksmf&|aSLREw9)`v}$Z za8-(Wf#bRkSuANvCv{B7*KjaFR^G-3X*Ox9@z25x# zEK#eO!)Ty4c%qJSTEDe1+v7*RpW>{@gkFr8yZNn(FUwKWUYQjn=@jOC*uQlSe3*>v?#;Qn5>+;AXWUM#eMT`!4eybzmm*=(36WC`3-Disb(^!5 zCFfoJmm*88~-xr`e84;rJ!}%4{m~`~3}7#c6hn zCXh|VhO%^GbL7Ql{c>tUXCykPo2_Y3IBiSYOGOFH6!mp-73T00HqFLLu5ug9=tc}d znp)u9JLhTWuy&b8r|6(l#z-$Qosy>Sue-PI*qwNZN$lxe(?Gq15jA*w4i%RHsl^Eyh%gDJKi(6!%^;Uz>I9%#RcCo|JJxdU5FLcDu+;KcLOd)`>x zj9*W@jd1Qtmv9dK)uHNmJ^fu~*|uxUoL=-ju!=Uj_vuF`;vX(BCtZ|Kw`HEKTMs|Y zfs&fm&vR1(D__@`VY?&I>yn-%DAt}Tq5jEjK5J5G7G6QAlF>D|&fxlZ6q$4~v-wkJ z^@lV45tC=Aoaa=i$#6Y;iW9=0OP_ebi6oqyk@SiC%4hG{*Rp#>JI1{!c)NpoV$Uw_ z-oYLT2ZaTODZwx9sSGEWIkTTl5pe&x4~P4L>pQj3w}M`uPF}Ta@%n>b>XoEAc*z4h zn>OJTJL1z=?@pc$ooY z(azoHvfWd$p4&+i5Y^*QVqPPGwQ9$s)Asn~brlWn1!hCRcyqgH8XjKrSNkn%Q$;=eIQ9l>oIt`ay~+(hV=R z1HxxmK^V{^Nq40Ay2h^rig?VNE2%81JGW#GmPH$f&_Sv>Ep7eoO1qYc`(lYUW=sni z6PfQ!YS}*gl#Z7(X_*}NSmWUlabJ5^p?c--k9RlR*V(6>M+#0=-lU^VXSq5IM8W_?P-}UIAa|@`$`pd*_*%R z7jw=mljrxFe5^e#X_OhY;`Y0b9! zl2p2gmJ@_ZmYTkXYYv$=rxK@B9I!02l(JC4RLZMnsWd2-+9a7cQfeBQreKNSEW+F5 zz3<-7=luciz4OaD=kqydpS8Ygt<7Sewbw!nPtCl!jyjrCVT$vdBZFUQ;hYy{3oM7J zRMa*;;_Vedc~|rZS!!PrkIRXSUm)fI2H3JMGkDDzO_psU0`Q(A=jKLk!75rgq%p(jo4q6NKT4FV- zH~;`^z0?$vwObS7gqYv-K3apIdDr$QcengPo?j4Mr@i?U;$CjPv6}unn%UKkYH>^9 z(%pI`;NpWFQf74GS`VcACcRf98Bfmb78;fB75&ML{n8JFFMXHIxCxIH@)H|novZ^` z^8k+wo;Fuq|N8c5^yXtS5mBQa9mShMhaj}pBKee=%!<`?`j6=|PhJ{N1I5qhj!fFS z+@>gW_y-P6W%(ZTFjzK?M;utyvW0+asWb_cBv`QrbmkAxJ1GBgBGm^)susn42SB>Z z*K6plKPL-i%3yE3cv;$ z{DT0E0!5A}N+I1zbq{*UzI|GZJQ`tt3&{MgK>y#y{`+1LKP(6|umahFpjZMR7s1{h zKvqcG+<{{)s0s{@4WK6tApZar!T{L?>dFD~w}obK^}qe#^GniQ-xkKkDC-plU{5*d zey0PPLRhB;6wM|LKyZG|Cm+}>#Mn2le%AQf+Hm`%Z%ii3&dtSTgRv72Zy@b=cXJSR zD6?MI=f~2&VPE3L8p*S5sSTK4mdfSt@AmWHtc*Y`daI7js;sg*nDK(<)FWr5eQyH7 z?zfXDU0uEYeiaM9J=<(TD@}Xy`JO!G>5|$6i)4u`THPmU!CTqmWNWFp%tHNi3AK*J z=JZ%X;Izb)My2eZedZL8ezkKug&em3R9I{~M zbT#HG=95ao88h$TO33Z*P-Gm8Ou`g6^DYMy#8dY(qmO+mc?#*{2w#2CcoTNkZcf7! zB92O%v^vf0=_+Ny8dyFrGpC`L0@NHn{P?12FLgo-?raw)pRV5>lBVuDPRs!x!Ogw9muEPv8~DNYzbl9Vvuvt15ijl%O@^g$0QeG*y0aE+AK1Wnkq zPj6g4&&&?gp>Q|@M)u*h(f+)8aN-^7E!Y-z=38Sw_sJY z6NvM%Vlhr;K*h#G@?fqneWZEw{p=Wofgr9lm#em?W_LM5xtNyP(`}hjTl8IV;=u)` zc;o#(j9*M8@CB$}GJuo)M0GV+*Yms#F#TVAEm(6SZ2*0D~3ZNo6) zmKvvGHUBj4PB`fvRHGX8Et*7)>4++qt?M;k+&}sxRr#_%(ac0e#r2`wM577$BYGOo z%)sfCVRuef9{61THqs_iD{qVQD4|YI^nR^v)x#=(5>;_bQ0RJ(nB~u?gZA%lP;KNb z)GK7_e#D<=6Peo!aX(daCRSi;ag|Hvg^1~iyZwbe$GLV(XMqW^5sOWhj<0xkzlEGh z(3)3FOg*zhjdtC$+~1vaeA?2Ho0dr}*yc<0d@?fBtS(2WwM!YHz1m=jFw;8}XPhHk z1C%qwaQ>&auf%CA4K38PXQ%8LYuPwHU zvURTh8b)FeyzJ4}uw2aTL6u1w1i|adXAu^BrZY|NhW&^oh&{q`Wz+BrRb??6a=aPy$Ux^njzUp;Pk;3C&~ z6eyxRa3)b*@I!`Ne|Yy$vC_0tE;iysw^nhdu>^N>(CcDmfK&&==PmdI?6L@864LPtttP9S|MpLxM}Um{Nj&gwV$Six_N`?U?=Rh$}PBq7f3DLw1Sqg zY^=#DdeSh~S>a9?gpYZXpb|)WV{rS*!F-vTjNVQdQTUc^B=bSVvz@@%$qCgG9ikg! z3d*A8;!Q4)#-NO~A|^jPSiL$XtUwx^1hkEjapQSAVTyvw?av&B>rz&D58bcTvmTK? znjcQF^^B9FeAH#Liv2}d13vSPa=#&g)z#Wd|0(&s6_|C18Cq^-bI<4R@Dmx*HMdVeyeROtpp#8{Ep`u# z#KF#UK&d&< z!2-04czdpnuc)V^-QvheOp9jM&+Nr(^TD2F%XN-dk`wmUub!ykfrbhH*HPJDnF{}% f0P=m&0SVlltfYEj{jOfX!U{)w6tV*G&!m3=&fYeb literal 0 HcmV?d00001 diff --git a/lib/NeoPixelBus-2.5.0.09/extras/curves/quintic.png b/lib/NeoPixelBus-2.5.0.09/extras/curves/quintic.png new file mode 100644 index 0000000000000000000000000000000000000000..30dab932aa8870d1609e56ace7d7ee1b2a59876c GIT binary patch literal 42362 zcmbrmc{tTw`v;0hyG$9%%+8iJnJQDJY{f>X9m$*wWtJf#p$wU~Z7O52C39p*LXs&- z2pKYu88gq$z4iQl=bS&zb*}52Kc08LJYWFS~8F5DKvXYL6 z$=vQ_bpOpbk)4$4`EZWq$M3}_JH|I3oW5g~)+3)+nsA-IA~T$LQzPvo_Bx|(8jE~r z%9Wx=EX`gEmFo)U&Hm(Pm%Kj~eA((%`)9f8OJewAvQY?PR?sDf*?kF+1O(jcRv`VN zE@4S~a`MGf!p{&H5&=J-$T5uY<9o;-1wUleN7P6^bPNR24<|2*9Dbz#KYh5NcK`lg zbZA=IDgA5ro<4oLlth;-9_ zsl&<0w2mSXpQtx7vFM1d#rv=S{IMxdb{Y$w$ohOGzoWXe}RVUtn$>KXUtYmS>Lq-_4{t{rXTKFD^;E3GYpxl`gtqwMOlnhuI)>KT8-cs zx}KZO+JwKSwz@yOD4M+%a`zaSNg$qb=n$vFM&yU|FNck;A0HlCaCsy+Qa$B7${?)U z;4dw-C|CGfZ}`z|O`2f4oRJ1y?b!J2!#l;()^CE+AC|=Y(6kLm^;PF~b)Q+j`cbsN z$bqM5AbCC~4i|@W`>wkpI#?`!W)Nkg_(T1tr!+F_$_j%`ANi+IIHpIw=}%W*KBJX- zySG~MJ75CQdVa4Xe{YfLLz7Rl4AZUdxLanyLWTD~@=+aOvAh?^z9=`0HKV;)D)GZR zZ!XxL_GZL-$(%~|oA=7JKR?En(7u~D89;oTdA@r8VU{Ny*$^M-Spomt&yJDf)_w?6 zg7(g`BdySmdvf0mjfBaIqtz=vRUE2o+-w!zsu{5qo|s1b=oosa5}Kn#uGWNr?SIbJ=58F7+XH|SGiqU=J1XaMR-OB@1M1~9!h6A)t@HG1UBcrM zIlE4?-obOjtdrc2@07;AjSfWBYe%NM?LSQBs7$(bS{#BazZOj`ZLviqnVVnETzbRS zx!L+LCaEiq;%>~BOP@Tl>~(dnn*~^Du@k0Fx1OJ2w-Z0}x2h}S+nUo+YW9TTGuF1X z8V}b$(5s>D>yi!WgF>s6d`yES1lm#`t)7fOkwtmwd};lKg^jWo`$-+A`f!FPhF|hZ zF)r5XpC`BEV==lHwOZAGOJ4j#mccm#8}DBU%=}K1@kHUpNrYzW5PC zyD@xb=2mKOiMYhu{D!rcS;~yRrLJg@EuI6FC=OS5g>21e=$z)&nXK+;2t+x1&ywgng;VeC(r-$%7B#~vZPlgQOR&Vx+P25C z9|y_%-WA!ubaUrd;c>9@?yr^QdRt{dHgq30HzW|DL`}wf5^m=wZTKHv$Nz`__y5<$ z{J;68r$zz9nwpv$nOe#6UN;>M!T1ezEesAE({x8>Xh;ai1uiht+2aH4fj8Va_RlH$ z>0#QJmX_c@UP7+W(_C0tsh*mu_}#wkt-RjTo3E(*U@yBk-M7PVK2wjY+NXbM(4BqH zxnyl&P%mXSd!;#2UF75T*yq8|(#Bg2=6TacHTwz*=+;($gldp0r`0zNW5MZeB&zLq z4209=IY@lVSKvsl?vmZKoM|+**?z3sHSAo}Kqq?jnS!-ufylrl{=$vN$^$%X2cB5; zT+41iGPZTeYJ99c+3$s8uj-)pZ^vU&ay_E~@dBn-Qf*CRTXCz4rxf@0*S_y67PiyNpr+^CE%XmNGZI2VL$&pPXzIhphbMZb zmg9S?3Jg^p*0kd_&pr@w_~x)sH$}fRJUF^?dMmW5v{+vJ{f{28o6EVA)2sCyrh30{ z*wWWeFSxAowz&Qb4_j?5IP37Ix8_|d{-ae&CVd=EZbU6--%pvb?zDcAA1AjO1(`4Y zWp!pixPnji^lt9M4d?YQqfzDE=Y@8K5?3D4xnPW()Wj{D+h;uuTNcxsS4%mg>&Fzf z8qI?UT@N()iBoy0_c3X2P@(6alERw6C7&p!WxQ~ag{`xXYv$WDufnL5p0BFO-3p>5 zRLNfuZ33ai3Cn`WD-L@VT}#pa#CnbPQ|IB*k`0CJU7b8dzsbKPohI<1>Z81P@sCc} z+s^aodl3(@H!0cPohD9=Dn5@A&Kp*xQ}f(EW*x&u3Y*ec6x#&SR5jJSTrv2;W^}w| z-Q3gUj*7nH<>$vP^@|f{_MNZw_Tg)>ntI>z0%~u*z(6EEaxw&6-l$x(MkdpyRy>l{ z-4gqB`9_ZO-9~;}Oh~NH=PTZCFqrn4h4%y*SjG2K+SC&{S6l=4h0kimpPjE_FWZ)X zN2)niqBFvf5T_}XM3(R;R^*7BUO$cqw8@>)$DA@4<)>4t*9na|2O$dVlG%{6K*U}{ zE-O=P5}9L8&pwPw$RuL1^^X(Sh+R3X?g>NN|P{|z!TRMqH&U>yn=Zk`0@1^54Y*M+8mAM548Pki_fWkr{-g-WM{By|Bwp8?r)khP z?aBA)Tpv{xwd*-T9lrDV`CORMmZBi?`e6dq->@hfgj&fU7Og=t`K3RX1= zDwbC9xgT~9thk#pXjf#J9?BzE;63oJ=q)<@Q{PI>IhM$K?OrKgpHQl%M_n+D#>J6D zAFC~-4&O8CYpy;cSSK3NE}>Xru$(LV_eWcvqoC}Js`+)}UcX8GpuBLSox%Fc>1VMP z>;}<0yiKf*_&fh*_zE*PF$xQnO0D&pM)q%(_Qr+Z^L&tQZFk$G*H8OPcI3sPD-Jt} z<#K|@l2}8ZIVApq^efwb9=!!>H6f#ucG2ZruRFdgriWz z*Fv7%%4kb^IM+*CPCw4H65MWiIp@8q_Ezr}4@@vEpKY`vTGa%y=}r(r{11m z?TWDT8r*luj<7=%cO^0s*hSUQIi#~8d{#<-!ZG!HZ#u`>Ru1Ol4PIzmE69>9>X{Z- z0}BdkV@F%DknSEA9s9$Y*)Lzyh3=0opFCNa!f6syH{`w+VYk592Qug+@7sP)teC;I z2$Brw5Zqy|U`53bOVc?6aR_~yz4#wdbGc`w5tW82dy$<<(YR7Dq)We6^X&G1NH~OE zwgFk>GJmg8k_t6^BgN`ah?% zYoyKJ<@BK@B3~kHfG|QWHdO;nO`85JVV85;ecafc+RCc5NbV2Qd%skkfS{W$Cj3Mu zcMFY|B}Vc1vH7CtX(cR$FR#X~%WQQwz0OsUD^4#aC~8_GKi4MU+owo}m#dC}!+UN= zu%Uw+aY-y=PiC!*%iLIlqn&w)PEqk_?0a);_x_b^HnG!*GN?L1g}%s%#T{vonDT3( zVuQn%*1}^)VH}4o##zQ{uCRDb))|#G@iE5v5F9kcsnym()$XzkE@rK{Wo}!!&#jRZ zq9+m7ilst`tm%+}b)%4SKe)hElO9&IiVbVb#!@Ez4uz*tJ3D69S76!MOiTg_O!i%XEce@r{ z?C2AcZ)Z%Xi-reF>%_`FG zCiIn!&kJ(5{p`MoKF0forsblYeH;^`C9wpphAlZ6_JS^Jf`%l@r>U*AIlpQ5?Af3Q zC##lbFx@vEIxB6*T+NQjxtNPQ{?&U{0F_3s>x2!d+`?15&~Z5oYWZj@%;+UiwPE&D z+2?VR{_^`jnZE^*zxQ6-SR&Iysh_N~z3X8sl`Kg=X5Vds$BDo#^gWq3d!=AqtM|!q z&4tcYMV@5S{&1}9VoG^aXWz+e9gL@ErFhZFXiJ7`J?y2D5-zsa8F9PxTh{_~ToACN zT+p2#1z$&s+7^U9JpPl&ZgeI7Oo43<*Ui7rUPCw}DJHbG>$TO@visPO*i?zK6T}1! zQtpNff0yIq{rhgX>SV79hn1d0ZOn-wUBj7*R)-5&9xJ=st&8X9#NIxzXnypo_vg=m zQkTEB0;;4h5z4lGrX>-#M%yx5l)Z&EkfDk+ZY?_-ZO3Bq+{RfAvG^yVReXjMq-9kk zWsugI@v9MDxE66Zy)$pW%vkOhzAk&WM|wLTFG`Dao%IR?-?%^CC5_9n15Su zG9!K!4GU1nnjnQ9W|g8DOGWmS(BZKJpIc1r+NOF}!_PKR9Le8twRqR&m4d~3{;i5} zrUz{-3HPiPiMnZ@oqgGtkP3st3xtZFi@@wl9Z@Y>k!G*PaX{#;0DYB$ny!*?<5vGjKjmAt$TT#DxRBu~)0l)M+O4tw5GfaEOeiV+I=fi#-Qp)^c60D~iNiL>OZ+bvf8S;# zaHsY}I%vRDG-`Eqb%Gq%xr_K;0fZsABqxzxFGo*S^Fyx8iYsMprt3K8rK5zsHFPZA zAteVdNSQ#*L@0mv8KGvoaXn`Tlf4;hg-z2TrbSAKiK}C6&{U^6uTmTd`=W7Kfh#%Q zlHmyKT@Wc>(T37Ja)JId>drUUMl)iF_NpH)YeFvp*~I=dJ155;8H|fMssiWZXI61} ztio+~SIDr4h5`_!^r>1#E$n(#LGTqf*n=fkX_qV_cuHOyg_7TmF4up)A2yCc9p6)8 zbYUXI#m7^Veahi3UEM1zx>3iM@V(o8eWe+9BrrcBMJTm$$bV(Dfqzc;cM%zd&D#R^ z@$Y?tNMV-!$g_*SZ3;4BLo&!je{bYJB4(Xk>1!abE;3iNS)A}FVvob2&ajmv=z zwHj_o_$u#(>He{79pp>Kz`WHT;;^w**x1+BRjnqD&3I5szxnR)gCbTPV`J|>Khf}Y z)f4#oYjZ_j68;|hTynG^Wz5OIJWb67(J1`NZ0kfjUT0aP1%LSr$KRd{3Sl}%M!8E2 zmZwcFAEv%f?nSm=!+%OSqsv*1IRE;77Ut4Ji(kDuX|hRpI!t>sF*AFKcNgxfQUELx zvP!^)j~~JX_%1cREy-dFJw(7~SG6bPtCui6_^vlgVMo!ql{5z zZ{M+$-_L)-)ZW>uE_Ku0f}Ap9m6=1+v-^<#kS^35bZXS#n6onsc;QaGvf2(`TODY6 zs*n#L~k*D0y}-_I9fEln&{O2@=-9kElDrY~fv9=m|Y@MnIU&Se0Cv;NX9 zjP?HLc(3SNZA3m&f7LQKcQKRdIDzc*k?J-&VbWZ=wS{eCyEp7Y2DWuD?e7$4cKLC_ zoc<_Zry9rN+T1cr3_l9SJ*N>~+bIy_-W*|0yfdGFAJd*7qPnot-v2z7Dgdf7hEp5i z51zMZ=Vklr{C%sO&A_`h{Ogf(h^Dwz@3Ss5x3;&d6%F+l|L9U8jTKlU z=AxDmKk#V1{hfggavHmO+CtLM?LN(6L&m69ZA; z$m{23CbOJ+e@Qya4DU?V;He(-UzrsM`+nl=U#=dyK=aUz)yvuC;>`%PoNCs}T^I8< zA!AWcGsQh?DbG@CEI(>TMYfpRIDgXLbuP5%w|$ZoEuEKY>5{gEpCa3+8_DUW|4Mf! z{vjs`c)qxFH#5N`7N0WAYp}jYRvmQ5@bk zipVh5mS)SJD+u^MCTnUek3{~9BfL|}7@9 zOyBmEJVI%US@P2JshEo3RI&NM2%CZM`%@m$*iZ3dzmA}6@>$0~n9nN-IyJ3Nl1Qm( zxr0oY&T3~(d1+yH(WmopV|S>ZhF?m1QD_;9@3mDW9({ab*8lR#yj1O;;`aFf;_kCa z6)Ms;nP?;_BCghcT-D?kAl_A^dSxh0sepXJ;gW>+_~n!LM?-&zAC9-muSSLU1!RXw zcqq^wnY*cG4;dEzNjI}S^aB4(!9XY|N?kDxPo&pvq!5KtJ zM{mgS)4#Z&p?D&PlaNNgAB`cDkJddFfVmZXNpo8_YexxkkBs#0Svh<=Z2ha3IV^x2 z6x{Mq9a~rwY3bJh3ie3$ejCLCQ{VA&S?Ha#Ou~nR%5|SVqJ{E+yIz#&8wu5N18Pu0;71eUm-KF9} z&2o?zf2znuAfAT%YC@@?g}o3?KO|+NaH7%7_Qp%vm|E<3Drb_d*WaqMi`MUy6>gh^^6OAdV+h{}^{go(nY zB@t>t@v3P}w>a^ZDgJQxSoZI+Ucx+KwyGCa{x#CjyT{xeHFswb@~V4W>(Z#Yl%2mr zMt$k-7Jhn=Ein(QbPWi^DfQs-pW14hA*h=CwvA@oP=BOa>T^t4RZ_^s9tS0;>3A*H zpxI4-LKlvJ>ZW4F(oB&SY(1~Q4acSOSvzV+AcnNDVReL>uIbnE(jJk2V*^|(dC_6dMaPW#EJDvNi-E&>^loiA6Os9gw}fCQ zoP5a5&xwa;~G`f?FyqVX=%RAnm) zwJJC4Pt^5BH_Jhp!aIq_^U}sfUX(%p-mX$nAF{_{gTKx3gL!u7=528Xp#Tv);l&Mm zbwDDO*mG-(HEBe!EOIo6rYP~YGeJ>-LNdrUi}~EfJFTSEk#HAUYc*J3w`k+>1hEnl z&~n)1K4z(VkF=fGljg^;So~p{jtSAi1aLA@_*`(eYFK0Xk_4&A-oxsag*@O6#xC#9 zhNxo~x>q$=hNP)$i@mo(m>2`CmoLgY{4zkHIJ}2gf>7q!u}qAb6Q!vlsiAmEytjOF zq9D?{&+oCC69q^hDrRSIGcj;cfcO-@GC{3Y)u%BXM0^Jc`$gBS6>GG9l8npgI-DZBPT?re=(X;qC_voLnl>J&qaquoRCkbY!F(38HwiSe{r%(O{C(`XlhmN9 zu>u!+6eN~e&j<{yhKHEm1)w?^d4GM|-EB1tltJ!($aQh$QY;T(`Xpvwb(^d7foMO; zo8@adW7;pyv!|CN;deY#&b8nz!WEz|89e#(XOlFi%9F)2e@Kz+#`eeu9rFJ2U0P7q zhg+-HRV8T*6Q3v;H6f>mh=~`wC8yqj0D8z^LFPzlL0URGb&2xv@o{o8#qFwBhWx|| z;Yu8p)8zP5Pc7_8MUSrE!RwKeG;6E5{SV>4mLxjTx|MxB_e`p{8yystkRRqQ<%CJI zCg!LajGd&S31%0F55QZoiTZ<+BbXW%M)jW%iRl-=sbMsu{{a;Qi&KZ{NrOdpPH6A7ShYUzb*k|X%b)=dkOy+`1xCxB77K&|vgti8QI zP^ae5k0@bURe4ClIFsC$H_Ign|=dVM;hMbJt$)8aGFNPb^zUDEP6@v<4f zb($rVLq;5M>!rnEXb2>MZmiZW-g?>Bu1z(@NxIym$%a4_ zVT|RAAO}=G7=|$an8HL*z-dYya>@?}%SZx1s1z)tbC{lkSWsb|1dc&fH0`)%_BCmk z_PDUKE3Bfxdax5;L$42iA1Bk8Y^!zPb;z~8U-*`j8taU;Dd36{WTv}VWZC$9j!=vB zZl!lPJaQnclLl?eTE$X+&zyV~MUs(5CxZZS#xza?aa5I5m;*GDF?sLX{`k`$JYg>O zln_#5C(W0^1KGT2I5LN?s!X1$qfc?c{;^Gt5oCTQfqcbh=y`^f)G={&I$zX;2tG|Z zbch}eGq;TN5Yug9fmTn$K0a=1MoiDUv-%T1jYJ@g_fKYhMofFR%AnZZQR@|n1?EU2 z0U8`Xycd9{bPCJ(JteDq?(=>b7waSDb467jTOiGDGohTvK+JYs)lMvzfs;?jpS7i~ zR{kNVFiA>aoH+KO3nE>AQn)q=zlZB9W4R)VYQ%2V(rIBM3na0KqvQy*;YyT^OxOhz zZK)QL8@%wstE&2$99#-P_rtH@7F~GzfnV3R_-w1=n_6s#`QBb$U;z?o8Oqk96BP9= znI(H_R}_FIiOKij1rS$Jt9R^__=zD+{8<#loZR=xOzok4!nh|Nl`&sWnP&&U+1{#; zy)_-gNeC;vn7_de8Nj=(9+p_VlcjxQ(yCbdL19~+TibOCY5Da+aTkX(tM%N=)Ys(- zEeN#dq~co1k+LNTNLQ#2+~06RFP=p@Oy~CBgD<|{nu@Q*{*HW~=B@~>kWSOFkU}#*#t8%K%ul4kkOLUc!!7 z7B+M3j;?8zKXdut0@hN=(6GPY&$h57IuqM)k+}E$glb6ZyaezMKpDPe991JL9pCt1 zO{DpJRR$w&rA-z-ODwKTuf--V*=I?Z!WAkToKP178=Q^Icg*o$VG;?Z>;dsMGuLBh zy_dBGk#%3cf2C+-G%wYl<6D)FZ{K^oQnMg~dZAH^J4)$~I#MCu;<2ykUiwZ`85TUu zsq*#w3|cZ_C7-&8tVng{y+BY@oa*~KSIb;5A@cjj^eCV-_^bs5CqVJ<45^iG9kQST zDWm6@4aX*`Ib14>Xo`ZK92Y=^sN>Rp>6m^&szJ$`@$wS($u%zp$yP9miA{+t3X4PV zmF8!3Y2><_W}MQ^{|w96QN0cNUC!8Z-A~qBs6fDih&nfbPH9`!Sa>x0SN-x)b$A{Z4k$b#dks^Vt`}Akn~zA2<;g+o(__@WW=VUbhn<3pkWx z2r4Dy2a0Q9%S3h55Jg{~&wX~Xd-yuC3qog*An~&37bg}0;;p(&+|d)TSFRa1KB3d` z#G`omvZ|~x{LkMPMED!IX&T~u?gvGcM$kpru%xXm|e zt|8KdH4v@a$Vk(0lOY03|37cRfuki&e%wfkacO9JRfSHCHe(m}^^a~8~jUgc`2W6<6&D2$i6w|!5aKUU2zO=5!R`O zc7nKgHYIZ8LloX6Hd0mw89U$@&1j)~(C$5@+gNDRpI3;%F(6S&i^8P2PYUZQC*iq`j?VT3 z?I>deU&6H5+%ZS+P+2$8RL3!llsuq^&HJm%I65`xI^&*v<3+OEEULv)*CLk7OGgIP z(15M*4mthCHb(lbp-CzG8IbfaBI_-VQ>DFc!5P~Q!1TeWM$)K6Xhw4q3~U24DB0f9 z8HW1nFf`HeMg59C5381Qqp6r-)wtCUQQh}pbjEa{r51LuYTMOON>CT8z4NvNi=~Ph z5hI8XHXzVz8}zuNB5(+=+t*P)cOOpe1cLQ7@ST zER|nchQ@G)3;H0OD(v|&?Hu*4vI4|k%hg!)575Fr(*6XD&p+OH>#y9t;k&!vKSY8l z>0Hd$ro`GKb`#zhcpnc!RnEMMp@3F61yy&rzub%s>!`o$HYPS^$Um2>2OM+F^scBh zQYNhNwQ9P%PZP4q=-3rUR@fyo>8j0kNyTy!J|xy>P|nx6ThzWSp}LzN#>J%)@=NsW z-7rYfpgVL)T?x7bHGZ(wl<>8d`90N+K#?xqL&L6D!$(;t49m z^ z2EwQH?ALM9$hM#F!ZbFO6lNV%p(yJ?cu84f4|Da%sfR+B0vbAXN@#=Uw$VxWH224a z1p!k_ACIUFIi5H40%)8yAy)8GM2QaVeT=6Q`c>iuY4zF06H2O*1!AWSp%Rdf%wX1D z_}Ge6vpqw=N%NxosuppWEn|1&)`lo?bJp@7bEVl2^VCDe>a_ZOen# zF{-7_MPwfZR&SMFUGV-2Zhluq9T{zfJrxs9x3yk9J#6}~Y`JFVEccNTk1#UgIC{^< zBABT?lBW{Scnhw0B$2!MW^n9|CX^A9YG@1SGm$+E47F7@i&eZgiPzcwsH(nr_+ruK zS8?;6b1v4;m~h1#Wv&|8NI<`<{9qvYm9;yARbF`fJ%c3uXhF_)o9{XP{%~~evmlpk zWer^6O*Qpl7{BLBn%GS$;yy-j@6}Cpr^W=l*r`N%PN}#&M&UjAAmY!KC*YYLk)7-g zd{W_47^jSLk6s?SIn3-%Grd&}aKP-ZLc15@J>S6j?iApT^82F}FOs(9VCEUl)LynA zAFgsCD=%Q#xnti|)=q(1|FO`U}|@@nnbEs|FSI5K{omJx+gJg27;Nyo>})*Lte z6hOq{kk$-lXnhV@|GC`PnWBa!AM3u%FAs&2V92qz%joj6*~#@XbZ5dk>x4&c(^)(4W$8aN_(Y3GKf7KNg9HNLxPGmRRl2M{OuOEbP?Mcnqtxq& zCaB(QoC^i+N~6lHIXz&>E0#>8vUT>sugG-_x$*AD80Gd`J|5c}tnZRpOS=kI-V8iU zB^4c~>&C)*9Dw)GurHk91kg>Zet-DRhuml=PztHQ*nZy|?UhkMTp@yB5pBq!>TeTWgBFWW?_GmIPYN$WE*k zsblLc-s#*pr(!Du$>uKUUf?OYi_)j>W_w8^fAb_o30^`LchR_DJe~8D)Q67`M$y)G zZAA@i$1o9q`k~&?CRjZL#b1m%dIeThc39Dbu-7-E@cTl>(Q8MfTJ`?Nz>HypIHG!+l5sJj(PXyIot}|VGxWTf%$wi{yPB7gIuNrWya02|ZV--q9%s{G` zfN2R*y0w1c#LD?<1Xm9fIg7~3eSQH{*)w;U%7=pb+clS9s!_unFA9mRd&vXu*CX zHE-ZH@&`t+LOh6>aIE{@e#y^z1kMR6%NJ$#T zyS5K{22o-dnGdV*k{9S3_m4o-S+f$`W&r92;Iq;DnpE1s74O&DgMEr`Uo8Q--k|uO z!EL1aQTYBlBt5We?zU^at%&Kmxf5tAjsLRp#R6B+{JeXeruVV!Ufddp5zuPY6}M5* z67M8@{^}hNFyvq6Fhf7os+WgAn0Fmk_B`2r^V=#L$v5=94=I3m3yo!oyUuBIk)85= z;>OuzR1ybONH63GM;TDWA+_z1-VmsvnFu%6D`R-!iN#p#>Se>_odg~7qj_UD&j%6f zoy*c+Z&NYi7A>6b+ncesjSPabo6vgD>3a=|ke!-f$;H=|$!8$mb;Xrsuphx<{bJVY zN?6Q`E0p7KlaJcbX>$G$57N<1V@OwO`4Pc=9)Ml#zV0&pAaAb zLaW&Cu6*S{BQooq!kLq_Av~og5NNS;UEc07t$ERvrGo*MEDOCAjbK^gm`|1<2ih&S0b zl%sTQWWWAxiP9YhQRvkacH=ZPV6${;Dh4N~={R6M;e+|Um6zV?)FaV&cQ^v`XqlB| zx6ruO=v|t_6&EJpg#?&ond8W@P4L2a<)=A@;P^>vyd+DqO_yV?+>TSn0zL?!GI@HW z4wTUowiDm8bvn{IjmJpHLKGtr!l2B`!)7r7V)Jh%9K~W7eA= zA<+@f$z8hVApF&g*rzDfotbeI4Oc4v=l5Iyq(WtnXHj^lIL-jICpE?PUCNzwwS}{B zR1D1E48WP{^+*!v|l}2U-DWJ?EAhL-z3td^(%?OI>lG+Z^~J zmuY{zcI*EgX}NWxlNwq8A(cB7F`ocsXsnvfGbRP`7R#0M%D$X`r$MzXZBBL%0q6c57A*5EqVrLs3EejJ&$7aZCEMYm^R`FG(Se~Vy?nZyc+nnO7i)VR~ZEKzEhRx=4ZSth}k*jh|T{W1zJ!_P?ET7CDx#rW>76xGLv==;igQ9vDEM|(P@1MjQ)m`z{hL=c16P^V$ z9S=&;yT`N1om)gFdk>tLnRMx=j%`yc#^%Ev2Vty)`FmfBK(G(l`Y!i>HPEZ{PZf{u z=eIyI$7wjk1&A8L+RZ5YMJ*NTJaDsn{u&3lZtoW!Q+2H&jT8RYRRSW6_(NAUl=&;u z>>l4^SSzs2qL+6c-T?eyV5y@n6lwR-=U~#?_Cs~XjMg7CfglXyM0WA6YqLE!f=F+u zGCV%UFrOSvcuStCvR&O78I@7fA@?|+7J=4rzORO&I*I=l0xD#hPpvfH`kr~WEVvuQ*D)jIA&zT*mU&2;Tfl5{c`<+uLlsK`+Z;D(2IO@K~8|5 zOnYgmToOgR*3`xy#cXuxoFQ~-V*GTl+4=dU0i@nS_F}o@Ice!L$9SCtGa+_Y-ss^X z6_y@^yR9X_nFfMdRsVnM#2E#k+$bU@esk&?=T{vx~12!|`XqxN;fr_~tqvurebe6-M^RU%aYjULf}fD ze&k1)zkW(8VcfR^MRXgwYN%#L`|f-fn!kbZlUZRcdhX*=A51YxDO!Z7;4}GzVV`Jnr@#pTugqF zYO68T_(1K%rI+L9k;n7K1;9Rc>2XisRl~m_@&AUbU4|iS(VClAvD;C3Q*)+H*mEb^ zMx|{T2t$-&al#u~X00W4ZWh~nso}wg^~uPs*teW4O@PPycVLnfZKR_UHIIi=>gC0pqo`_mqVIUxdyh&ez7P3UtaRktzTXp zdw#Bnx8V#cRlP7iX_TmND}Us;CM21R@W?S;CkG+!KaagcX(us z%;1EkAH&6|I?{t4RONm^x#4r=*?Nm(fZLg`?6k7@qp(!wJx{bd<5rxoX>4dq4$^tJ zy*-`)Obh*MM+`dk034fdz&){pWP(WAZZ{BorBRyUV;0h`u-)X18kLpr+Y?)1X9X!e zT@Nz3W?aDk4*%KSe^>yhnkHCu4%`YmyQks67ufdnqb~T_w*WcppQ?DqF|Y(_(#s00>s4v{zx5MU6d{oE6W)BCj+o4!Go}Ji_Aay~6uQZbTqnrXkjnyHcCN5a^KVO#+ z$89lyM~Iyny3L1XJwRIc7t|3X<~%3K6bk0T|^hwZENqdpc)Ckg-03x<|67BE)ZIGUE2AIn-*5xQG2o?1eOj&JzHeE}Xs62;}=`E*IkLnd*zE_St4q zfyXx4XM?spj6kEl<59@{VgrjF)2RWW;*u4(2nYt)mU8JUvs!^iYWdh{HHWg<%{LW2CGZ&7tKacp3_1jNu-U z3U>~a!tQ2NGI#ii{hOpNIQIAw1rj~$#EPKcWp5)nm&$Sh-IKEisP?tzLZg73OK@*a zkwo6tkO^BE@kT3E@uC8_==hY#Xx6_uV?OLF=fwbGTskCP79SR@k~eqLNN%aKHIlLJ zz=uNaGk`<|u)_ypI&!l^_@74852U%k3}b%fi+3xFrU`RE{~zRM<&yx$RTEDJIQ6N#b1X3i+Tv7C3#H$zE0kFB<&A$3UrQiFUs#ecFA894uaQQ1 zUd_hCi-q9;P%aI;65%8WazDVLUwnzMJML<+%;wn?meuvelxzBsXn4l3H{UhB1d@_# zfftiOxfy_G#(|`M_xFPg=+tWEvm$r^BOH`T)wiv(x_Ti&_C@&JS+#nj0#20=yUS!r zQY&bGBK|w1F6@jZT8X5)w%IjqlKNK4 zv;q5e%`j0%z^3G5sfO&;T61aRUf2qJw0JZ4T*zECC`z(}GnC<65T02@YCBT~={+cO znxD+a)2%F48Mi%i99;*5u|aY7n_=Y+mHQdO98??M3{JrlMsPKJ{AVcri9hs1zW_Mz zM+)#8H-Op1Nr;=|di;dzKaRn}1n%>C0%3nI=8dMva|8^1O9(HQaR+7t)qbon3ci0R zEeCvooST#T4-akBt^7JCKuoO?j3)$;2nUH${POo!td7>cD7g$)01QeO6ZLDrULG#W z3oUtM7ew53Vwr$(r+y{JQx=-yEC7c z1HG+=)|b%L`aC|qK_NhlWd9v801vC7)p#Gv&SQjcM{ezx@gu!Y>7IErO3%KkaQ<~) zOWynVbN(y&K}q<$fjUumIV$RKE%iv1 zP#>UNzBV8p0U|T=sm>07-uJdXXseS|b^gBa9XZ_-ej1HLvc$0UTn=>*)O#5yNAj9!pa>@R!;V z^1)Uw;Kii8IZ*}btjRvG)vKf4NDZLB_!pm{)fnK4oezXueo>XQP*5Fy~43UJ2t0AW$EBJ(St}0toP3@Jy#br6jAWjdhO?{j!bQh>F}BwC6g~xuh05YY`*pe z^s};t4V2xZZ5j(9v0{-U7GGT7T*P`dDXmo^s44C-P77vqi&+3$fTxIl)By35l3~e< z@R)<@bjn>$!q{Qv-o`CSu%oyKe4vrM@lEioR~80tAkap&T`FZQEmSTKzdWD2Q2%Ge zFW&QRq5R5X4Bk;oZWv`S&*oAI9%rq!rII%532F8-EZ4P09@^f%b3`p=$$XuX)E#?O z@j;Q2WIA+e{QDa#yvR+bUj}8W?wkUK&zeU3<#c#~E-MrE)ANS<5G@dK zSP85pz_a;0yOwe>>_T}k4=tssijTMVxjOcO%k-JIMlHi%Pvn#yHc7Q>Lv=@+uZYtI zA&%AlHc9+ z3S6uD?|BvXIm>prc8*)@&pboFyg}Toe=xJVx|Z3ToZ<3YVkMAmW`UJ`iie#On=}?q zWm2;BN8`Ze8wHQDGD>KP976_M;W$Oo{4KWYaOX~2^!+i>;Zpfy+7lx-LMdktwausmfPtlISAmr_9~z?aB%-n$6}Wcz(O!TE;{fP$-^B$H&&3jtj^o z;G^Q8n!LgTB(fWvLTOflT1qMMoGCy;(4Bb}!`qM~A{ae*NN&D0Z^I@;*QdmNZ(q41 z+UL>k?+WdEf3kTBOa69HI$<3v51R6#{EGyb?v0L`yB+&22G1+m%9pR4SYjaZ-}Kf3I_;T6g?|9sV;y$#jhO#S&;`0P z`Z=&|0iIa}c;-|1(@pZi3#cpppoijNMah^`^f#h!t+9vcqN z%a&Bpega4o)em^Z$4N+u7#YcX;7G?_S*)5S#iTNSme>33RoE*$4jDG$pj4Z!^Mb6& zFCIssjlmvGNFjBEszqk{Lxy91AUQ8A9r9#}V8j7`Lc05~pV`&YgVZddDw%i`h|$a! zfcx)^{U9aHm+|(q5+6?+sWApLA;*cLU4QoPB}%uy{BPtnfyjG?Z>A5hJRrDWC3L{$ z1p%3MYlQ-})nP#| zl;`>3mrf?Zzd!Eg$)peXnoG@91`&x^kEt>5Se^@xZ&l)dM> zYag#VRiC$#jixnBq#$i(NQ~fvL~RaiuO9k$yT`QYxVHNiSVIpLu|5*2gC|;~OGZY= zx5#t8Ayc?sP?BykMVr}YXAneTj z7X)|R|8_}nZ9DdiwAHXgu082g70tcV3CXlGXQ^aBqe&PTJsh9{f%f7tZ184+l5OF2=i_TZ9*=TwM) z>?h{onc0bxq_>(*?K6#WZ{U3&om#UC2UH*|oGM3wzgPX?r+f`!+MqxYQ(qH{%=`N$ z=a*439)MwMcZ+xMmjR6UyN=HMA5^_}Al3gHHh#23sLZktM^=)|WOb}#myGNRW$(R} zC{o9tl*?HaW{?!WCaO>OsQH-QXFYf)hETJ3}JOVbT^ZSN zC7h1n%{IucE`57koaB(tEzDjHP1lsp=P6zCJQ{(|I{nq8D)>jdD>Um^7;69VS=H)Z zFJa&H-^^<$^x+HXaxMsR=hUAf6*b-@Ja5yg`0;0Mw7R%aAc-FwnF2_VCb-Hd&hmp@7*N`f)2&|2j;)d772QbwtdEgn1h2gyi6KOZjqH(F34u41>Y-?eG zE;Pi#bC-_8O(_Rt{U2~=9LWz4yXyGO`^ks^7{fazJCvelV5W{|Rsc53(C@1&S>sf( zoL*2IHtFNpsXxpf-C=RQsuC<_|CxTP6OLe~-?yz}-~Y_2GNV}kBLt>Wbo9HoG~Che z8TJ+;N-n)E8q}izT-)Qrm{1_g8o@w|cFRLL?C0k$=uL|9^~(@liIrEU*g1)8l>8U` zOtZeJ{?JkrXx>V(A_F9LwA(8z2H-w4YSGraaNENvua&)zfCA=zwDBzzJI(^4Efd&I znaXn+_b++|e$lPvE-(1iuOL4++^mS^GL-YLMcO*K>8m4xUVHaLfKYh<0?%>4UIxEQ zH3O@rr|02Uo1(M5o&Kp1jA$nYDgvL~bW;uUf82CL&@Cq+U}sA`5$od8Ke%-RZ~I7* zkh64@f8te13B=^|A@e?sJ$lLpZp7!kPgNu#lwh8Pc6-D_25z$?j*|~&!RugaAQgZu z>s|QWG+=7@9?^NU{VEzQTzluddpw(KCxBc+7M+5L;&M^yL=INTyhI^t;`c^7D!f^ulra% zr6W9C6XBnT&gJgAHC-_o-yEPgKW+nTdv*&)@TU9jSBed@oN1O=JcawJy}ILU(J-6A z?8w#&m}MhGXu?H@IXWY(hX0l8u(dckGY-4(D==S7a6H3_@-<%GVdllltnbK!bN%$G z!yCE-Ft3B=xb36fL4&iMYa={Xu8Y*rnqy!QuX)ZMhZg4Ttgb`a)g`j)C0N;RV-_tF zr;V`wIv*w7FyCJn4>LhyARYMC-p>61HxTn&&z*{`_d6fZ{Q94&HEr6lbrvvob_aPco;qU zD9D|V9w7{m01(L6DdPoH1U`B9hHeuzQ2P08U`@WXKO79vOk@~#{IbdQXgEa1ST29v zIYz7d>(`b1_=z&m^7vU4T6yby-zoL~ziY&QbtYqYs%wLb7Y|gEjJU%f1TgjxIGLnF z=zg9tFOo;N#=tzqQ%VunhzKC9pL_ui)O-k3z%%*nque%in|s86mEU zbqmJgS;(W10!LD-U~g|fo8f#_h0`X%6Yic zUzzk<9viXq-1(e5qATntTV2tmZE^w`=OgEB%xXzE#1AE(`8|(wSq9d1$7nJ7C^|Q{ zmq@^Zj=g=eK0lsoBk~_`41BCkB$qnIBlfqWOX-}_KMN=DwwZcw`g$TH@+$0{gwz@C zH#~q70;bvydI3VeZxRlGV;g*@IaC1ir!8{_2HyN&O;uL>+!JqFi4he{QYMQ3C7+X zQoMLo!Y82%8;|5l`|R-)ZqXe@To5Y6RS z1cZ^4AsZZRNY5E!dlxt8QJwxyiOj0{u&NEdM6V=I#*bkf202m|rZgX9QH`{HZE38I zU{CjEbVo~n@x$9<7)aM+i_@4Pj8G@(g-;>_9y^NbbI@d2SfNZMm{?`&DRy~(SjCPu zTyVDxhjm>iwZ(`)>s~Eqwg9k&Z}X?POSjI`upcns-yKL?1twxKmYWE$So}#A%bB@V zZ@|1Y3FD89gk$wc^f`A-cB)qJlRI~vgxC+j4G(XLNZuU-{IL$2OR^PIW0>cXV`)PY z+xCSZ2;6EO#qgI3nRTl)Q35bdZ?a9)Z96PMqe&Y>&bEhq1j)DZKhr>$eV%Lz-Ze;H zffXkLY7v9oql0Wdz}< zteEp=R8*Y)Kgw4@ZNMEvAlJyC48iL0@mQV!w5n~Z%@ZBqtU_wBR38KYx%Dl`Z9uaR`~}=d&YWV75ecd^<&s%tCA#fWnORMyw;4uV5s7`OMKg z^^(mexZ`n<4^eqWUmt>)IEJW~1;&7QqK2Go6mfaTsEG8AE71OBYRo zKeTb^WuZCG6qxPJfgMG-0*r&nO?9|0d~g@IWdr*~B@a%W^g6D#SxnbK!qJtK2zJp$ zmN|{fXx}~(`7cdZ0cP;>3<-1lHj)hPeE?8h3J685-VXBtP1ZPPhFD05$=Pc!3-o<) zvI;p0A!7aW*}>cMH)zxZPfQt552on{{R9N-maaNt=@jno+*sXJ8a45bW?gAuI9&Py zN$H%h@eVjd4$s|qiJdol53mWzsRt7z7+u1vrCOv9+-2tx7(qt>&;z%h^q9t2X-ufq zhbPJlyXriseJ`s5-?bZb5%{&9Gvj13HT~vK2Lq!3ytk~BjQ|+k-x-5_u?YNZ`Seab zAmgbZTUR^yaNm#zWdZ&Naj<_3fLfEpr+Q>E3OQvB6ckJ<_dDS>05E?RER{l0=Q^o% zi9=hl?=yed5X#sa4(-prV}Z%LVOI{LSoPcn7{al6ZM^yj;H+=nkS_z`83#4*{N#gK zm1ECHRGYd=^7rYZAP&Kb+ajLJIC9N}X#Qs@*d0yib5c9aNfCzM+06C~JIKbynO1vp zAFd0E%v~8H8JD4-q{@CCEbh8Apfgcw0rVD7vg6OTAt!)P<3vp~{^@3iO+g}!ciRg1 z7i5-fPov**zPfV*6b4)|0)kG7vH zxMrLk0*S$4cv&4elN9pyui!-*%R&P-l}nPp;Z3w?UAaP`_h9H~%r*Myp6PIe^KP{+ z=zNQaW)=4+ElCQCIt|<%_a#On)D`pfOD)83Uxm1)~?2 zuIJTNn+|fHuQ?={NLDmi*i6^1Y`d8~tG%0suTsCHo&{MzEcEUoK>fv6=#+ zT7Va2S$rdVSTV*#{)9f-*aT8$N*zDDDGnMcGVpE#BBtZ~K*=))-x;;sP2hY}xs8DFyoUY>ZB}Bf$kwb*8gpgqWS##2RGYTIVOWop?;;naD4tYN$Psn#j@ujZ^)gmbdEI4J4 zt6lcU^~Na@KtfSh`l*O{EBI|+p9N(H;ZG)zJh4H#G`hF?Izp;3L`cxKuxMO z58~trp@YGRZ+TY-)G|`;@vRkGXbr>z1U4eNy=M84f%~l_^}*w09sR-w(Cj}okud9P z&SXI^(v?;!5TOGmYJWirMHq}w57zSS7*>_<$5C?hIhP@w3~Mc^d1MA5-zf{GdN*my zqkja6HCo!hbajT|anju%KWBI@NHpxaje61f0 z9JR>cXEfij$T0j%sUe9b(0_T9^Ndf0b14hZ^NH418~!ic814Yz9?*1t1D*z6@(>#r z*pXaDiWc@psf%BR*4?;0-*DG``Ku|r?I(9I34HJE=1J_f-pEFPuxeAqmBSsNyWz<( zq(}{o(t8fxIw*>{qQ6hXybf8kbZj^phCd%QQ@7Am23y&C2epHTsv;L4iek|J6PdN< z`z#Le_k?_RI4ex%{H8|Wta3=Q?{C|~%jF>!5`28~CV5Qxug?qYC|FPT`e{a}ux1;1 z6643wx(U1Kw?hU5h!tf3=W`kgXq{f?- z-c}cRtwVA4cws71+p?XS@LO+{K-L6tiQ1k(@{}Zdv2o2L>k^f zpu{|7eA^?13|t@Vuu+N?P+*6QD$G0}($FdZXvDh37)6>@B|tnr_vU0gSQIe={Go<` z*1aU&S_*hMnHqr6BMv4E$z+f}>PCj-w^!$7yo`Kh&l00c>w36%{tI*A5lZC)4KbKY z55IzjhV64ZrM~IBv7k_LtmEWN7z%)sJrN>zILD8mb-M#Pr2wEBwDug?wpsz|R&_q* zaaFM(69U%pG3mE}1>8EOs`v&`*whNxZW0286QmsSE$>+VJJZtrvMp{WH>U&YK8QZu z)EC0N(B!mw!`aIRpkoS73n03Km1&xc6Hq8dqO9w8zn!H~GxR@~5f?VNdku2rpVcV7 z0$``;e-r`sE{hxXI@`uQ1OTQ~cCM*4#@O1-c>^_q^_a^d9||S;A;_Q!Y5ShTP%`}MrE|<+ zb|~%7#R(`l{r|FB`EPubkZMjxdNSrIXu#6@jUTUhK0O}h;j)TZM8QKqD+2%#+i9BW zh|SY|QL=l70Y*MoOi$)^t_AA>-^G9f5i&YOob&+WMm44^3Bc8F0Wg|%E^^LRs0Z*| z1T6hG4X~WU!^Tw_bMHc^2;{X6HJmuWapmy_*9bFmo^)ft(Ko4OTT_yfTA4dj<~cje z(+gc@x^DS|O#q^K&C2qJ-lJMw&mANQ8%&JKHy^^ZwG&`Fe!Dj5lyzu)&|{1aJcSl9 zxdeeKSH4@APN|afVW^}x$ug-^e|igM*446J?5!v_UyJ~Lw=zEu(F}knE@x~#(UHT{ zbz2TA+K{x&N4)I(M~BTk$4@r?ZBPZm6JVr=b7UMJzW*PkX#HmJ7ssfp9tEYPNx!pR zBy+3DiC40Lb=h%OZ2Fh}mW>d=+exr$7KpSwo=RG(mLC`B8#}zwC^7zw1iga5n&@`k zxV!_RAsbRx*FhYi8WR_0lfp#JzGn`Su205q=&1g_j5PbBDSf20L?j(fvE+@iQ6%uN z@ukdlFwHAMavT)*&Q$A@42`=NuW^p+Y#}KcK>v*uM`Y~Ih*Mbb<%)<=vBw`_54mmM z;WC>H=hT3f+P)zIYc^Tuu<|n)a7%p)z|0Xog(FCQDFu-JEAan$F5WEzFRa;<3O&k} zH{&h7DKzsYsX$wdJoBU6(+SyZ`|A zQa-e{$@?9w4=md5()ATT?g)IiuIaavf0Y(|n@FrfU$7WKCot|@wb;-HNb-y2PX;`~ zaC{gK!>`LG($dF)C0@?b13r3{>wje1yi#uc5YtPH@2lm@ zt1Ca^MLf??RQ9YLvMi=!>m?X$eu<^ioere>LA80i4iF(=8}F&xApoKg_tyDJofInm zi4dl-NerN0ko1qbB@Cby&HtE)ODn?7$A(h|y?`A)upM7Au_Atggt(dh^{9pfiz#G@ z&weP<1$|6h7$yRy{i(xQyDa9>u$5r|+9TW=LE)Bk*oX-OZA}rQt~$|4%ZvYL!WM+F zaS~QTOP_h`cr*JNCm`lZa#EY0?>~6;GmsvU-(Y(71_ecC?VC@~w{OC^vR;@c;2z3x zRu@h`IKy53dIb(Pf51cDIk4eBSpEoZI4sL7=$i=fk<{Q0th54>6ZPuX=+E|Rs6LVH zYpA->kOL~VI>X=j;X@wqKI&R!2+|gH4}antf5{vaCRa%!pO8@=K~C40KOtp3KUiw2 zM;2!m4UC6bX@1r?{g;NVH{aUN;RT5vCO6=|waKQy2)uM25GgGY8RXOt`F<56o%f8H z{4z?srS~K~2$O+i$cG4$c7kg0c|Ttg*krZi0xYUaY zXYH5$oAqoeL}SQiN3%mK|A2pT1JE2Q2{5$|`}L7BFSnm-EP}5~cjh3}5D|8J-}OvZ z2te6wo!Xvl^rmz;ZEdyR&t2pKk;! zMz|uVAL0Nm$G0)kmCs12;w{5syvOl~7|Nq;y>9qiuP{0S;KQKRp1|MrVMpUkKq&%H zC8mA68Hykk!3P6)#f}wMVX)E>W(r*NZ`-GE{iTCfAZX*mHxEooZicXfY(hhHurl=h zjN^;pFppi0CyIwk?$4^+7v@Bj+?ijfB!ziw9RgLe;h~(}2Vh;080ndF&Xz)Xb|U@I z+Gh%&2^)|l5_CK<3r#SW=Oi^DE*P#Blo!-*z>UlU>qS0j1>LjAa=JkdxXl4akd5(a zA6#kFT2+2$gn=BysZ?xRm;5&S4qtVzM11{jFoM%$^aD#t1hgd+iDGkxEWPJ*tWDCu zV5|TrAD98eul`&F6KsDz>y5yL&qnlub9cXz6UFv1$O|A^ydcN`39_%%B6t~}19z18 zTNcg~cGCOFk<&g0Nn?RmQk10D>R>Rw7v+ltB4YN|tO9+(bTlG$0|&g?lAD&~kT#je zMNQ5F9&JJk(xHI2dA$nX0sN7Y zQ}QH2&HVcdj*uOW614~7G%cwalyHYdAr(Y)VEyWP{08!P-Aj689{~R3z)puq^3L6Y zI7B{bzBGo8)XfxVT>&73z?^fe7rOwNU2xv^c?>ycNl_dJw&FG1`4HA$|IJX64gxjl zE@rq(xDK2_kSadMBm1^!2L+gXUi@*R_=7M%qNM#=H>7n&CZzevtPX<`0Aw%YnpC3! z)Rr`9P;%#o7es}3CVobPIEbr@+~>mQd)4ylQmE*a(G?LZy8mwI?3}#7iyunxuA_>L zGTsKhO&#H)A!pP9+)$decx|fwFP3|*h#R5`0VYB`Ar4hQXFlKpI;h? zG9DYhNc;2sJu^@#tG;b7X?1!WL@YHfqgIQwz<#v^v=PZQ#w-el;qimHv%j&OC49lT zs~;lV&QR$PWZdMz&V6Zk|4E};A9PeXc2ekc|4a<67p0&6Mz(Vt+1Sfr4^H~( zM~8N;t#J}fC}*2lMYWa2!%FZyxqQVWwi7gpc|xe3$Fu+@uyV zaIB^auST(YGGZE%ExF17HS&14ms8`&={WhA4?v5&nnI z=6l!2WQ8vdCj{}6y<`QUnvqbf$9O>UOZ%Lc#zaZa+|^@1nHw9%;fC31apazo(e7x0 ztxqWCc08pEn)C)rH11Q1y~#k-KY@qsyh4OG7Aw>6KMARv+T$0o=S*oSz8^!X{Q}BD zd8-ua>~df~qF(|1_ie%qh^qHg)=X4zYLOlXYY84FDhW2IZA;IhyXE>n3;wGAm3F9r z04uXknqNO8wu&c)^W`ZV&9By?=<)=hN30SK>2_Fx;in8w?Gg3^rMg`jvs0XGq^B~X z387!y9f>IrUI8AIu-XaC&;IApgS$MmILX|9Y94rR?QFz{5gg{!-Ty~`fh7MHp2*0+ zpw?IoyhFgMYGeE?9-j{fV)W03iGP6riI{DB^J>YfPI)1hiLA^PMdk^gwfY#oLDp7XxgsT|y z0Z0qE+=0(mNJ38kcp)l)x_(TZ5ogn$bt7{;+|a1C2o&o`8{9qhMq-!zLGlz7n^M|) zq_b$abA>=7vx=>JcOG^u=dWBpTxk%Yg7lzz2A^<2ihTzWH9f|27=2DiJkka z=aYe7kd3$vlGr5Oh0={w>Dq`GKH7E};j|w$QO|Q1lD%(k7df_2H{Qno+y+4C$~QMC zy@6Jh5Ed-yf*5dP(J_9qFCk$^Zp8*+C=vFR68}U8-`-%XW`XzzcY2^ZIQ=`F*?JJ| z3_F75>qM%(vbSeHaNJ0klIk}%TP~CK-j;1Ai+7`8db{sU)DiVp(T7~<2SFvTr&!b& zA|M>!h7B0Q@3O@RZB4O3pf9Q_mT0F6L72ds`Jd@PkVGAWK{SR*qO#l_e;Y@D1Q;t0 z5b8#(ev@@CbGehxBR+zixxKdl3(&KBEQI6BeP|8Z2VCeLiHmJurj&9t=eGeSTRuCW zvk_$0*2Q%XVJIjlIE%31c%kN!V$3=n9KZpo=V>GXj?O)M(UI=KnM}5)5n)GM-orJe zFJSBQHXbLb<~UkBYaBh*L3Co{f&BUM$>xiWkbJ_>P*7Aw!=(!(FG$Nf9=df2l=bhN ziD}8asLAs3I4y06#%(G!;G63AUPW&y8i9`b!J=&({X|l2vsylF*6}gGpotkjPg zYMe;2FHCjVPU*_Jh?pi85SpA0ym9gT!Z^X`R9Eds`uPzK_$xGybFSp_{MNSV6Q}N)R3C>6%*-Wkb+N_MEev!y0 ze|g0<`(&v)jy=^>%-Lp#aa0_kR0Cd@Dl)5Z?JS_F3k?{<4g*mxJSN5Hz~_*_fOJ+( zlbj0rP=o0sG-0ETCe_}=@#{Cx5lW4*MuJ2C;I{V_x6N3?m)ItbLXh30cK1aAvXeye z;)e1}yi94>6)UT!w1m$ye=%(9R#%VT`bDhr0Y41Dj>1i8F)pvphl9Yi5J+HLqi8&n zm2eb+O*r)sA1p?74PzIu`v_=CT>>ZWUoddj=0SnNgZ+Tk7z7)bIi4p9R3Rb25vdBK zbmjuHd<25Pkbo8YT&_wnE#GVm@WT!UcitmRLs1J2AgCGWXzjY(=bh??&i3yG2S^cI zt9a}ko?zHIt)S`*=XvBb!1Q6bxYIt2TBWTep1`W1%8mxySjIsqCy zTX>xt^z9Ls=n}9iw#(4qy)8*@;)RvQ`yoIG@q!~jUfhrd!o(EZ*pNFNm&L1pTjrAY;5FfQB~MAI~-BbApQ&^5U$80Yz4>L_nhaL)1a;DWb_5+NQ>b z)&fr547EE%9h5L~6g?0F4DY0;nxIzyE__K>vW8)6(Ax_E@+@|JzA50Ef^L65~N# zr?A*0pYs;HdGw*ZYX6_pBp}IQdMzHh^9XepE$;BKU zzW3#eM9pK4r_`Ch5Pvy0kki{w^DGjJoh8l3uvSh-9p4KA!A1aNUg)<;5^cw@Ok^hy zmbX!TMz*>(<{L{L7|Qo4nR<@ZngSdb@$mE_X*I7I*s7`yfIbgdnYKI-XBaMgppF#j zeKPV3LCG@=ru}*iM{^QH7wPdsm+Jr1tsz^Ux&+seUu0!bxP4THw2(c^N~u1j1q5Mpibvz3GqH6L0Zq{`a}`);0Q%sL{QIrB=V>+fC=G5a&et=fl~$Xk-~ul zJ6^u6EDM{@Cb8m8^P%AHBKTXgj<576vPQ3G>LE4TVyLSg9Abeg52XvfS`@I9Yle2; zag&0b^tUW`0gNGA*$d?x=*s6nP++Rz*f85SDm73?b~cLGwOF2Oj5^p!?%#8ATP<6M zLS-cC)Z5K%Ebq&>5*cRy&Cq{$@xxs@DwA>_eOf3QE35Y`VJVwf^j1J>{eVd`WIPmq z^c2ljl?KBz8WIQ!4(f}F1bOCTP4=l^&4Q5Mj39}a#^s-PWykU z!M2DA_K8>fRjq|&GJGL`$n#&wB|MFQu;tGW+Q zNln$R4U?8KL+T5b0$7w^Q6Z$I{C%^D8OUt)vRPXo%SQbGQjP*hYAM$QyXYixps;z{kBK+C}K2?fB*r1&+Mhl_W+du37q%8YN(Z%yP4v zj>r}Olkv0jQ_FO26AYDyW)_qicn9SMY&N1LLV}M9p#lda2s>LykSea@2o8JFs|_pW z0RRv{On)~lN+tYIP&)OneJG_`pUCc9e(|r{Aa?=*@e?wDWkcWb4f!b zWZU%(e;^yTEz|&~#(q+`2PAOT1~T301feR$cjrQ~L)_Pp)57s!oq>?F--i6GG(55) z$3K^}J5SdxT)$6M*?#Q!^WiZ3;6#i4jHyZRW4-eF{^t-4TKr^Ao0a3L31A`88yA7; z@P88(^v`A*87IC&#lW>va#oy-{|9RLu!+?uC?~+&4EUNVX{nIT04>;x7FiraZh?## z0~1pYf|LiK)RbujK{9?BqrUNPLl*0-tM+|;*z80g2EV&Wr(2t@orNBr3Ty>6ysXjF z(2=K5BpjM@T&Rzn(HS8B|1`lQ_4f;9XOD-Cw=9MN^zeQd{jnAPr?T5oF!`W91A+`> zxq#^Ngi@{X`!zX5!gAhbc+WE_lxIpij;Ccx+UukJd~%Hegwi}Q zWIWbBzdO`gUcudrsPg}RvI@?g1a7svr3PN>?E+MfZa zXxIZAyOm}Url{@UAiBcQaOgPSOY0L7+dN#e6{;tLL#i}HQKy?!0w@p%#ZEv~*=F&R zck-Rt?F7#uuP3<@do{?$eq*fN$n)m(cK=G~#I z%V{zO6|A)~sM}F4zejH}t-388eu-KNg-2BLaFKogu})&olpokBmT6bBkEZ?eqRVdt z6(1#oZBFlt5(=>)hXM`vB9X1?0O{;eI z(By$8m3ZYH;2gWJm)W84oFa@{MOz%BqwUrEga|=v5ky!#JkFbX4k-OCJ!@Ku*}h?x znA?_TCZnzhflAKpC*5`G@`ZKWNh?pcw~dvI5eknsrBE#kO6;9q$FX|Y{pFD}y1yJg z%nk03*mRyoq4puS2db~_5&O1Ls-*9|3D?JKou}y;0_yRFXBcsd1NWF-z&%#bP+nO7 zz$Ru4`N-<^r_!le1*#f!8aqF?zd<;T7{)$RBxH1(gPk`wdp&D1ls!szUyxS!9hG$$h~KhN0r z;6grBR~>j1sS5OZO)7?*%e$)DvCsv+@Qh!jp5Nbcu$ROByNu{mGIGNy4?BZSSXyP9qz6@44X1xp; z6w-K6hl(%CwBV(QmP#N~-fte#{+{(w(3 zkk$*hy57T1^65pYanVzwC{W>p?nGe8_fDt@UopOnbSxZ<)irlvex;$DYx#hZ)LGroD0} zGJkc*_ug;+=CPh=JZ25tbLPW{@sw0FVN~0yV&ojyJ%K9OF8~34EpJz)?9WyI^J$Hj z_+Hjr?S3B%a8D8AvXAMNNW^$@Dq6RB3M7-}-lpQ?&O%F1?x^%7vyvNKdCtC*awk^A z;2zYEynzV$x_kp;=ae;!73(uQmqAtYpI!Mae8|RMEB|WuosV*zW$Z~&iUb(>Ev zII-%N4qt%ZVJX+4V0i%XzSYO#50rd;m~oZu7ta3{wZGIr#ETt5EFK#Q!+-0w)yBlU z&Dnm7>?*uTN1}w^v|nxWUXZ^*D%Nx1e7&fB#Dn+cHJtW{VXX=ZwYgVCthl$*>iD>X zjj)w0J><<}WpAAW%-+Dln1BLCp{Oeb;a9s6Rol>-I+g`@Oj0tY^5jKY-QgCk4{{~l zKq~<$w>ua2cQpLoaKAJt7sy)U%%TQyPMw->aHqmUg^fXo8a!36gKp$GbDebdbxy z4*yo&^!{^|yQHn1_JRi##u|Qr6_f66rm)SPu88YbJ3|r%<9w@{Xg=UrR7a||7~#@( z9Pz!09BKP$mmvZlI&aKhf`$BpH7xt?9%;W*(T z>DqZ;X_Rd#qhl#RDgs5jg748u=YxZ~|LsMQ>9?!gh>$`bX;ichl^Z(Au~d+Umz)hs zsa(l7!yO%_lPXA1218Y3$ezj&Dk&EwIfL(8Kw?XhIs-pBjTLJ%`UWWE0`&tLf5v4e zigAvg@2#7m?r<ArEppjMRn?(1sSt0mvg>mOY>01Dn^C(z^ga5>Ge@A?kaiLX}>Z z>Qj^oG^K2y1njYeaQuY$+0Ee3(@AloupL_9kE_Ux{2=COyU@L6pdwe zmuYgE6puX_A|!Uv3B%0lFo6{#;#`iwPPuan>{=)iJ(lBIozr{nrIE%sHo}FL8u9wn zR~DR#aSA9hvf3mNkWi8esy{4aZ|Y^X-B`uXZY5e5%i!OFyU`=JaCZ9DzyQtaX&Oqg zNx~=PQQN`uW5SZEW*Z2y|+epF@q}-HoLv!*$|TUnVfM#^sP9GDi@n&ahLf9X9J? z#T~oS^aVt49~R}6ii+oVc8Kk5DK9LuXn~j{5eg6k2-fyI$MaV=WBiX9hWqlZEmnfn zXy?ha`g6O^B}1go(dh@*Niu{_gsLJzvqP3f&64i0$BqA#41g#VD)fL&0r) z9Wr4L6cGry@mg$QMG*-LV{@J{M{Z{3*;9w!cop`1?NAw;FlZm*M-)sufF}@qmbnj9 zUo@?rM|=oan;FR{jZ|fG6t4!V6ma|U-lbl!AEyr-bbC&V)`_I>J&xe~_4N!c+(bhP zwc0ZXK_Y^7 z&W2{fRhi1)$j|)suIi628NI+pAP^xFDAe_fy1kFwAY3leS|%bY-$-FwGLqSL4qr31 z40SzedF}fzdo?MuH}Q?O#htq5CV5$vL(%;?6a%M$n_tNV`nkAZf9HlaMu(>KK+g*V zLHWw~)aLvBE@xG>4|4z>r#Qyf*QoJ6Q!NxaBpi8ulp9%DG-c=p7YG(CK@roK6^x4`|pYB=i>Uqn;@vNwa zYSr`P+Inf~lrl7|f$bkr{x|Y5E$sv<1%8ECjCvV%!1zyBrGF0CC|`E?WtA`#Pd{$W z9bS!rpN3USD4|L<0#QM#x(z>r;uic<1v-!li`48m7WmcE-*CE)-g)X*-JWD(VltsJ z@^yQ@%qW0)bAL>=Y-wwCR#{oOyIvODp&! zms|?`XDDqa`1O0|C+@$0wl^yY8+Z-v7Z(5duy}K&bkXVjg$sL?`|JBDpydo@F?$E{ zeD@UEO;KYn< z&Dbf`R_vC%j{p|PO889R+B=b5{TrzPerKoHek?b(MF`aT6L$?67;I%%7!;oUjHU>5 zv&uVKgeNcBY;g`!+*dzwLV)1@$>~yyiL-QC*{PD4kiD=)syWudoRuG4+68IkY06&( zoE+Fp>LMjx7AG>_U@2;~O}Xb_Q&gxuyGAf$UC(C7?RRB5?Wex;x{zUq^}SAY-GoYe zwoTNO=bMbe`r`u+1)g3>WzI}I4LO|)WaYjac{Z(W?ynOX*kbclCasEt;LC9{Gi1Pcpx5tS)@9 zrQ>%fu_HF7meIpINGZ7{IxYU%D+9ML)_=P*BRKmN-cWw5IhU6~-r~+6fN;aMn;?%YopB4woh06?Wr4cmsT=0vh&jQJIxPd z-8OcnPdw^T(+OX@nQu08H+`yN=Wn~Tt-dL~CvN`xvO%H1AgghSF+8 z54?Uo+L)=<;3;b*sE?mudy~$w~+ z&T*$?zS1arJV>pl89;3#Pte_3T-K_Wpqx_*I%%~2b}U6-*{`UxiOq!2aYILcWIe6l z=Yd4*V6TkMN7>2~Y?7%%XYHt;I-V&SS;u--vw7by!Z;i3A*6~?#V1*09Y?=xdU)^M z=UhQeH@2~5VeBW<&>C4E zn9cmcNhDY>7wzoUFVGai!xST-UxpE zZVGeIm$c=2kIU*xuh0keHAYuIV;sQuw7RxJKbp34`Az74H4f{paz13t5^NJ2sK3M; z*8N^?ty8YoUZHiUm|`Vhxa#KM@_J8)$QSn>kr5J^xnj`J)OvGVkl*j zzb6r!hhF)QFENe<8ZQB~v{?`^?6)hg&8Q%#b74KOw7_lVKQ<8bZ`` zdfi63IgxI7Rs1zCc2u^iHB^Gvh{c#f9Lw@>L!q5<>BHNirJ&coesi=#hj+9s?fB)- zrWo2hJi4QBI>_@2kK}9BH<>LOx$mfDrHe8Xo=kRaw?%f3MV+J<$DF4%e(=MnE!*am zTXi)DpD~N0oGf4N3-6&2+L(?c74@ssUGsVAvWzSH$uTV0?XG~G9 z@YT5lw@DtEBCx=hL%)z<>C1y^uUno~N><%|wU^<$dZ@2W`}b$LF}B3f_}4v;4V3*Q zl%eH98HV=_YHbF$_$bSd71on}){}KKddWC=Wbe_Xq|6A+QlOU31NNj8_ehBygL8#K zozFl2gsbORVGHH{YZvQSkzVtRV`RW7@ThOkWr=g0ICA3>c!DYoi99Z3z3Ogjl_mMk*aWh?)9gVDs3^v=i2V>$)r zH;7CB-5z-|Dmi*IPnlIz4USm(uNhlW{4%ftt54Zjk3`DxIJo zoZOn~+V5TvUlc0We~^%!xsH3o`(lGprv{|DKmTY|Y45<6pr%}(?Zy#A$A!4EEWGWi zL_43z@k4s3tz-CAf?vJv;Vm(?vg<2UCneu)jn+naFlC+lB*z+vux-g&SMalWm1U@* zQLqu3FUOiIsyakF!FB7rZ&%DGgGpU2v72G*#mZI_H13|1-Ylv79Gi z{#0OrRK>A*6FA<>95kH|aY^dB4dK{drR4Tw8Urp~Q=!G0hAQWa?~ z8AeN_5*Itt6T%jz!dFesoi&!vz2Sr(ui$y}?C}q_h;G|J7S^aW#ZmJjC;kP3RgUKn zZvAp=&a2cv?wQ>R>uH9u#sq)P-kj8iC5pBaU=!S(YTvYvu4wymXRLz9@k%h)R7rcR z%!ahaV3}4R^4ei1HE-9F>)qw|Y6V@FBg>rCT5NLIKKF!IaDahPId8!%IGbD_3CrSe zZo;ImMf-B&C!WfQjVodE$~wtXL2FrL<+@?>=nd+slOx9lYF(Y%EZgZ`lTR>l)oQ1U zx1}$S?M*J~rH?V5UKg6qv*9lqF;iN1Th?x%+<%B78WkqL=RgqK3m0gIj?A0TW_S4O zmCvl`wrilBk5<@DRAg}5z1Wx`sK2vO%+U(So;M+g#?>;eyh_>78~CglzE%J`NeXmm zn4IB?@PbHvi}w&tBGs6np^!?47UpBH?W2=Sbz!m7%$fL1mWwrL*0`b3jf*VQHb$0{ zZ_>5iK7_a25Q<$!@$iaqM zMp2k&ME>H_j}_dW-hxFLg=oJ4+i)j>==CAx$n6BX<;`VnYMW}?^o@A~y^)sRY|1U~ zL~>IXg3fz-vk-L$qgOt7^~b`MD+(*`r!4F=MIW2ww(0s^-{?@|iG*wj1k_mrx8hYV+J(KwxNy=R0>8^}g7UT(RuX;>>ZkL*1z!AkhhgLt?S%}`p6U(S;oNC#TNRq zhCmH;)YRklbTDG5@2!ky<@pJ>2h8wlz7v_dSu!#{OzrwbXUbyGGHmU+-(S)o_}hlB z9cG$dP3yV;JGXP~Z(qpWsmmRyM=nUG3F%(>c3M`tT*pUcj;nqECMI#FdVq6*YxZke zanfBI{p@#>Y}XI3vb*5D=;zdo zg%AE;{E(QlAPCP*lT($^*(aEFN(N|e3Zs_1qvr~42TJn6Xb}erJ0|LoejRNC6X6eG z&a|!gbqHp+`F~$3#|YUL6+c@VF=^k^pMY&esHpsLmw$U*poZU`UgF$krS+mYD;3YZ zw|95CR2=-DeQ{P3PThJ>>vyS8a%%o@vQCr@wcD6WWxJN&Wmd^Z_E+89eX)NF-}|Xv z7yZztCGJe>ngBm1yVx7f6{`%H>Z6l%;2phIwJHpKJY@4ER32Msx2;c>TSc z;`=EGCbsJ=c4~9BgQav1Q|)F!uW7tnzsX$}E%sGr=z8L4e!b6rz-9I;gREp9Ro+j* zdHHKQCBum!7V3ME8vTdoO|qrS4{tuB8;D9dS;;7|!s zN9(*riG8rpbzdbkMOQnHf7TkE>@&9^ij}I(dh`dFdwi0Ozy-dZx^8niH!|gcF+&ml zUM)2}tRV5%YhRarQ#-ucJBBk13iSfxHXId}J%nfFZWlI8kVhIcIG;od^DGoT2uxfQ z8HpbC^-1@euDqP~b-Ug&9w!_etMrj^?vkHio1l{Lneq=i$8ZPR zk4fyBRJlyV$L(*yj?-_)?p48fC>b{i3%xN+9JE_GvSDVg=uESE!KzTZzN^$(?W9uj zc}be~;wwQWu-UY{`ydfo8Db;4`IbRe$IfBkCNY3$sF&F148!L*&^o9~^>M$Orplz_**9bvQ{JX&89 zs1cMYQZ%xn=g6U=(yE?hEb&r8Yx2B}PAM3Pc4~t7vbL>hi=@oR^HIOyXv(rveAc{w zZeADdA0iRz;=h$RNjK#0VPX8Z`6PW&+~uemPg+?1o4gedZR*cC(aUDk+*5Ap^w+%H zP97=8yy9TX?<^(Dk)#u#3P(*BOMC)BLoFJ<544r|+tuIk2u)*2vwYCKrf5mlD1()icUSW(-;%c3tx@+%(dpLkF~%4w z`D=wAP3MExg|c2W8%9bw^{B~TTX|GietuOEPHFsrYkA*saqL?)uPa>x!LM<-s2Eu2 zR0EZ5_48-FHrg+LBUhIt-%8xO>2kDQIrZq*S}IjALP+Jmir5gG70fQ*B$^7uHp!0aJf+*F3= zW+_Zu2Mcq4sp4_rvLRM>?aXzqz_h8iV|hxdD`tw&W%DL?E7GT$@9!mR3^5fdhuxie ztG-^J=1EAJ`1i-nm9#buMd4G9r#YJ5CSUNTZ(Ix<<*RPV+|EAymlQy1xlubI9Xt0< zY2E}!1up*HuEOd1mU?o5({k6`#-xHa6&M1RMNea~N>>C$Ca-`MhnJdijD62jKE4v@ zy*T0huIBIZxaE@Bzt&i*wv#`2R(5H9!4^I1vH8<$2$ve(kKwOXX*XE(JMQ?Z`J{aF zP9Tm?Fj;3o(p-Hi@cD+&3(ZJ2YcRJO10eJm%GD^r!NJE8@Or$?6rn{{?b*tQ*FB!T%Z!&4~?^jC)znYi|#6`8V{(s$Fdpy(YAE%2_ zDVHKiP9l{?MV3qWVKKs?WbUQeF~*|YLl>1b_ghC|8Om8M(JaQOaGYqzeQL9jFzlGb zT(;=-_Wk^Y?oFe$PMO*LQoK@ALj#UeEXQeD>_~R`dbq6zZR{1-c+m3_raQ zfsUA94|;|eE8icTpGPJiHpbLrbMdNU`=&ErR8-y=uGSP!@&LaGPcayxf$g*dd6~a! z0qlDJn()o0_L8*t$6M-`-MCF@R-!xMX@aprwl#KLwDi~lc=3ZEuA?a2$j>F3(EaQU zw0!X8wbUVPMuNkx=r#>h_#)&gxoEJ6W>pU>iHpwS!G#Z~00P}C-Lt_lwCYc3GCVPj zG5m-uQI1&l!I@Ie29T9mGOzm>CyuVf{l-ROGm7Y)Mj(!ic9D;;zYDXd%z5XAL|jYd zDB@<4w&4w#PW)~*TJw9HlHxIixOROtrB&f=t4KpBZ>B(`I@V|-ev%|k+-=##DPn8B9J(@eLMbm^y)Ke43e2g! zw5fe%yxtY0??Ql0Kd!&CBWv(f*7BM)UYq@>v-|}LpFjARW?7%X*}^rs-Ws$xk!;bK z%-QsH^+`Os;0OgO8#0tpJ9K>!!u|w3UVi_A&9jp1Ui;`D+%O*-{lpd(>lj#c&12S_ z$boE!Ml+y8rx%UeV>|W+{X_4E0C2Ic*WAenxRZq_JwmbY4|wi*MGdggQFL05eweHs zOgN9@t(E{c^tG-;_In#CeIS5n$rjm5(LM`!N%ZYF{kScSy- zW<$W8`OkbaxVy0)&=G^S2@nT^wjGdLgSHni=Yq>Az|{*bp#I4J!objQz-c0;X_S$W zh`imXGO6MmEU^JphSUqRA?AV~L*`77s?#j}FB3R^HpepI_3-d8IEF8-r%N%OWN+;; z_pu^B8@n5*MIC^v-SSxhwXy1C(D0egEvvi7)WCB*Y@FxP!sc_I(jd?bZ5mZ98=5Uo%+3amz4OJ zUThV!E|@&W5u zzF;fUz;VL(7Uy`&f@Z~3i|`vJ;Gj!~D9II8y8CA{sQ^24y+5{eny5_rY*Lt)g7+`r zw~7*6iBq1J=5aL~d}+PxVYk|-#EZkp>hlYqATD7%m5g8`9by#o<-BVe3KOPXfp9J5 zTkJSe5_z60)v|DQFX8>A<^v6VGyc6GMH4aWqw4ZdbR>M@L5cbDfLVeE2`cyjp`obk&2X@BVKXSdquJ)VwL@)j`OIe~7|-D1Rt545Y_ z92r{fkV=C0Wu8aNLU>{P@>hQax!A(v>3Ba%o`pK-VKtZVE#=F0SD!NS`u=uli1Q+`t-4VWC+ZcpEMZ9kx$^1B zC(*tX>vuKx@2M)SE@R8=X2EtG%8{(T1NZCJ&@tKogD zg#$wKcVm{vSa@IdAq#xjvuabt%3N+xr@;VV6m{>-7K8CN!|s{6RN$%-%<{U{8zM*V zrA_!W!sp2Q3b>tHD<2_d{MaRZ?}X+@-PQ-@`)pm^MvdRoGG@E(RptJ-iow;VW`*T5 zt;o5S=uFPaSDj0goTR36kp+7YO;(}w989(GtYvAZbP?S(<_5|?CaOTg1DUVI$YXd} z_o?%^ddd%Wq@SZ)$!x_95SAckp#2e#f|N8pvfiI%7t^dn_qjNCkO^oIgapYGc9+}I z<`mSNP9J9-7=H*EM23tASR6E?>9{_2<%kXCP*pDVEnywA|9USq5#`*}>}9&)1R(GRQB%>}H&&>4vJtD@Q}du0>Fl=*0+^ zkqDRr7?BaA)g`(RR6Bk85yt60iZTQ3|MEWe=Q7EGpvZha;jSoqgG5~B=sTgUR-V>W zP}T^x2B{1ygsR(Z9%%7nMi!u z-01Uc{TF;AqWhP9l-P5u(qAc2p`RsgM()B4`6dLDpv7yjl&AH1Db5hE!+B|1Veh-1 zM;bIE4s}xyLy_cMa#etLY;xL~&N~qvJxPKPg_1?tca+e;yv&OBCV${Mqi4RVZTt%~ zAu!O6(2s+M{?#V1IG)MbDoyP>Gql1#b+kCn(d&Uh&Y8NtR2P$QL6p|yjJB#FoL zs<@3^IV2e}e=ke@VWI5^SXdLdK?LVOyN9l+cA`P56e{r1AN#+iETl5^nftGui^X>v zki3v!xj6C!!Nt`m(@RgvvS@vudXmCqlK7?JCz?{Gb|CL-p<=9>is*&qpP3apPP@p!R;sn59_ zVC}Y;3W)XWrnPwh8%ddl43KFF*A>+Ey`oCxlu4Q?2)u0ROEu3c9(B=?j<;();S%6= z^P;im?YK0X92fFgbhq0?X);-}q*;BXLoiIs-8?M=@@ruXnc}FL->B32mQV$*G+QII zxmrqy>}e?1)Kzz`es18Cs#3dKuUr(8;SSV?MC$^9esX1tV^9)6(HB&KkmVWSD)-+t9qPZ z{jSCMH6>y+iqvag@N(p*4oqcZv?uF~T+MGnoiDmfbI#sq94<_aq?C^;tA7woV*BN5 zy=9nJdimOUTb=i=vSpZ0#}mC7E!kQzdptv4h;4n#voO{6l__M^cV&ECB}gWQHqTAW zsGs&|Gi$zhxkGU{0&u=*gQA9a(nkGOx4>T;o1_71I!VC0TL-EmB0vGw@yfIffTCsN wHVb(W82ujwPyg*WZ^>spP@u#`ykJC9h_x8Q+Yx!KC>$(M8y`s*thjR}T6BCODRz-)2 zY1=;d=fv)<@RKc$4#u1R(ox4SWqdw90)N?Vc?oxki76{&@2c?*_&f7m>@7zoru`q0 zf3{THzczy(9(7Xv!wGM1;pA%UV9s>c*qZ1hLNs?eAt53rA|8ZZvt(k5b=6S0q2L^;*eYW<3Io2DJ)pje*)DwseTi+m5*u3SfXCJ- zPu}lEZ{PawVp-Af@KB$K~tN8OuRJLwF^#?ELHw=prDU_mi6F}>UM z|IeTPp@D&c8o|@k4)|+!b8~YRagT9P=PPx6DU&X`^M{ol9t?d@L-MH`G;&&U?UCaV z5juTZoHbBAJ9<2F`-PeNg=W(OwL_Qu4~8;5eMs7_@@VHlY1CgT*RMZ2iQ1whcOdkE z73l=qW6>ZwEGxif#JNR@wp&AK`;T<2si%HIpZN9b*Q2Vf`{Zhi!>!^Y>U`uC#Ya** zg0O;IM2~8#C9MK+qPwHCK=;%~GhSb!dhHd>qRFQYU6)UU?Ie0MrjAMLM+_IyJBYVJ zbZ>N@t@&(~eopnH9HlizS5IKWnYFovh0D}_i|;WMvp*Ay^m0gX=Il;A6aEh|K5^;n=%ddtC= zP4CxqDwMejHaPlGRmtz?I zzqp*_^hv|T6MtFm73mvxzmgE%QOM6SUR*~==x$Cuc}Y9Q!RV&#gaVWQ0Sw!LEhEQj z_)8zIWEPU>|HFCE$<>^ZtBr5#G_J!ZwYre}&+Q~rUSxIWK z>3m|3*A3GbFYd*i?tb3P?Pz4M$Wx*p*{fvKJ{g);!n#Fe2O_vN_4b*m+|wl_D#6LX zhVbA^8cD=!m)VL=Y?=9K`EATSWf|IXXzOhrt;U|a1(i=w02VGRHu6~)jr!jqC=n^NhzzD^mK=Bu8 zfG3&Ai1mY`$%!UOEc^&|lX`WYMMKk7+oeNp=K-QG`UIy0q2o(`{DyD|slbeJ<*T+6uHwb_4BEX8PdX+(%+RMR z8C}T^E;&gwwf;)1o_S^JhjyDhdiJ+wvaPjuW^XL-p)mV~kCiv~KSqnK?u(lWlDPW^ z&52{#A>!ndLdvuXU-EP~mdo{K&pVVviB;#-*m@L;R^`u2wLbEVtkR@MlMR)w^!7A_1DPdY|s0%x7VWZ)34ahX|>cG??Z24 zdT{(QP3K6L{lvw-x3k!u*$F=$qtgCV-n6hEGbcwbGro&z=`R}5>Nir8hPvZ4cr=Z? zS9`Mm^5c>~H{34zIR0t-0p!&4d!qcxHX7P_+|1hW<$wJA)OF~I0z=5Hre}7jN|Bxrghn@7Cq@X{8#GQ8gmXWKTE6q7-cOn+PwUWFZyzG zR4I*UGQEgngXLb2Pm>JRXXjM=?k|1hRgkowE=cmAYcEf)#>nMb!CDlBrEx1~!c3)Y zz{5*9y#2@Iif^;&jIH!#Gmm_+9MkIYMXut(y`j;d>9r*9LQmVDa_BlAO1Z`KCqky3 z{5i{>vc9sFkrqpZpA{!cw9u@HT;1a8Y@uhK$+lZzIu~#4C#q(Ho-u{zW~^646hFaB zPi;^Zgylv~dS$u&NKj64m6C`dl(@}UG{(y(9WE=KWK5M02B;Lv&Gkt4`XqeM;g*cy z?imtrd@o2w{HZTvy`C zo0=_S`+rPKp8oV)jYkSK_j#$j-7EhxJ3YVj8BN9&`H~9U=={VGrN>Xiz`%97`>mH` zdCKazUtPg^&#GiB*6jJN=XTz%Mh(A)q-Itnl7e4&EaW{ye*V_mdcc@H^h_KekHndrcL!0)vpXM6%|70at>Y+(hkCtS_Kelk0-#KroDrJX$++x1#-2@D5@=cKX`E2t|o{JgG$u?8&&3x_1 zxm6mYPWN}atlgYZSX!qy;1zmSy5y@BlP~If#m||nq*q*}%lU95^aQUZl>3<1zWr1e zbQ4$d=jHJz0=te$HCM?z@r!Tn@|XCBvHP1({a*58`ufAHa*5)Vy?Xse%U>pPt++=(1Gf-k*E*in-c} zno`qLzLK|VV~Ogiwzy_K4d?uQY0Ci=Qm1I#Ezi+E1*zLs9Ha{KMKPe+@nK1#LAS@A zPp*Gk!^^*aeX77TM3AA=T>evE3dO>+cy*8TRM#tJYvnl+LWCkBGR#2E-t@-wRB)js zW+3RXoQZ0mUp2BsDdF!E9SO%$`iTAHGs;N{&psryj)Axx23HRfqBiFSibiRs%k=(? z2{|pFjExBJu{LXsd%IY*m)GrkQ|(7foU_%B+`Qu1@{QJk%56FwI^}(W)c%#3)`SLC zM9CsWoV%m*R(|+ImD0n49HtY=&UY4SU1=hY{Y{PQE0hu`9;sI2(AUN7o9M|gvP*$ zlH3C48yUR|)7rys+2k`*4S)1>hEwd598Q<=Q<4k4Ry-T_}g<%sgeYlY4_tpAI?R>>FPGKF z7e;-?r;MJ}I}P2RJn7Sb>I=5JVZ5__9ud-sg*}e4`OkV&HyVy8_Lj_b+!679TX(vE zU%zj4C6AxtJoH5kIfnD!$8e}!c%IkY8$1!HndxoBFPkk-5fl<4*wi3(0;Ut+@wZriUQ07-)7?i@%vSUC z%U!&Lj}~ldFLl{4#9_HoToZMaX|L`hU-9&6g;86}_ad&I`y7>BA!npfMUHK>oaT@- z7w^qiCE{thuaRNh*ScZ3k0X~w6%|ZI*H86&o9)oXRxf0>`=aX@rusPREaWm1GYZSo zZgJh5aPGgRVy~F}=Sk|5B$qBD=Gh07{}j>)IHuHP%jd(WvcAqkL!i;y2loAu8E^<|K17ZFgu-R?=_oDAq6TV`)B5#=FVIl-$o%lo@%-hS^NWHswuk; z4IGvg%WsFZ`^k=7SBif9ID(4Qt+=^X`=n9sLbD(>?}=+v;2(E?kFEK8?5VOg{T^-I z@$6qe6wC5*zgOJCA349`8j}k#pkvkq&tImjxB613;VQ@9o{rVo7%9G6u#3n0apFKrO_D?&b9Ln`5B_%};rJ+M+Q3KU^aOfF|Fa5r&8T({z z9=itCWI?PsflyoPNJz?$qMxd}jAe6I4&B2RnqgFKxQod@v$Xn(T&YPISIGL9{muH` z>}p*awuNSwOf>B~_bY{!ncqGv~M-^;$lPLF7K@>oQd z68NL;Jes*yZP_;3I|z~s;~A2fU^*KxJ9@jBP6?4q*07aj{9P4E>2n=A`kBnubMLps z$okl7nxa;~_#R}wuD?o|)U&OZ%^o!C)N6m=<^rE{`;Vo!LP2{GNAP^I{_2YHc8k}z zBOAeXC4E`ULSxpp21jH)>@YI(P3*P#fAr)FAWrB-z^8qABlA--#(=HFC}iC9mv^Jp z1k5M*PQ9CbU%#Z_EhSMChAS>68(Z-Q`4Pk3uVVGM)9Ke_jg*rGzOTf`928TXxKp&- zz;)pUbz7^!X>SwF{=eX$k*{|*T&0Ngax=Q)I?RsAWL4>EY4zlBf$zMy@TD71mK65kV6tm_;Jito-zAk-Ds&vWk0!B+3o^_x+E$19~{qM}p{EqpqHJG%xRjv)&CE=9#c! zIXH5#-BPv}o7GEU@ zYcj(k2jV<(RfV5LOM6{E!>+SMCi|AY&6p%AUE%&q$6;AoZUtoTFR7IyJcN5WrMAnB zN2;Jn?J+>TUB0?mR({F;3eu>-Onypn2g8(| zo)r>{&9Af^iqL`szEs&9{{1JT`eZ4Qoj%X93#U38w7B2;*CU#Q;_^+sQ3|TWj7w)iBtyP&p__U3H!-ND;Z+23eTWT>4;0 z6kP|P^8-3tl1FZ9)iW?Z_o4SuEJ>nKfj>^)#iZCz(GldvTJ~Hx@poi;sk_*nP%8Ri z!RoZ@IjXhx=kiX6kGd*G583Gr>lNRdk4U1fi~6|z9B{{9`v5n3mCeK@B;SFZ-c@ec z*(ZHR_*LdRW(~V^`xdzQg{t+u#IDU8=fUFTa`h6?Pq3~us?yG6L#4IU+IDXk$)-~V zOspL0D+wN{M|JqpY!4%_Nn(#&Wi+sF&q6^tT%-e?77%XhuDMYu*qrf1*_CT zL8`9hWn)^pb0V?N&}S%3kb0J)$kUdhp2w|=mm!PJVzxHC?QOz- z4~K#~oP@YfUQElOItu|;>l0mfClIzkDw#BegmggZTr zGcs_B*2L4%q=*+6x~A6)?2|%iTla&9t$6RZOS|j-tk0UuU~g3oD~EOEdDNjqNP_6c z4vo~mokCOkXM#+Lh&(%q^DJ6j*^qGBdl$yXr+;iwH3BuylF3e{>6d~r8!gwp|9Dwz z2OYHc#0TLkFV-?0Z!mmk!&+r!OeG}M&#;FU@OO=Db^dKWNe$^(eQ8n7j&qf|?^xk7 z_>$w(`;+3jxis}7k|_(6;y?V&F`P~0iM0>TW;yvUB=H>?3WeosA2*1aVte59Dvcg- zF?ai$+CQ}u>E;ZPj=4JOjF;-x7{?Kkj*WwTNN2%S0 zA#l&@3*~x?!FJ!<-hDke*APt-o}VIk4ECWjTPZTz!LvR18Tdft11Ec>i96t9&bX|B?`$zs@dPR|5K^h73|s*UWvpV1qDmRIBe3e_Y+E9jxy|i z_oDk!XMw_Uiy$?!LE?L7$wVX$ySLBKQZu0*hgBUJ&D?J=@&-cCpS*KrYe#5Xk3mAJ z>p-Ftggp#^Z&e0q`EE{VcB$?|Z78 zWz@aWqd#C8)jv_|y+=l^1G%42Y?oew47mY6)_60CEH>~BF4`H`-E~oKc z&ir=7qt?iU1=R=dJ$mieA4#V8v`=C?UmT6UGs zKJN|;Kh5x?W@l$>d=%7L+khvJQ9)wn}<7Rb|iJ(jIwXqYj^#Plu3jw+a6+L3mF zC4l2G04{ye+pQ1^zOIjnu0Dy4GCFrC@(ti!DxU_Xcyg!UAhSlk07M``u6sep*{lPE zf&^VVJInZR`eT1v;c(w-CR~lQQ+#KO@rUev8pPS;loZVkT*La&>UJ#Cog!|Ai5$bf zP8O{buFk5h+cQ>kBMo+*EF@PSUd6J}-{l{p?Jpu_lF$NdAriu^?IDV7!*gTpV@xN? zuEZwCyj|=yY(wcr8pwEa?Xp0*1bL2L?#$( z066)30o~<7vO(ioj@jkp)d%|bLA`3k8lT!J-YQKH|FqUC&gO?AKW3YJYPW67J9f0h zJSDI}oJjShf)mGaZ*r$a10yx_W1%B8f+pwCI=dl79qCuwZ+HKF3rZ1UHc4Edi;U~G zn?isc4hxCoj7=R2{vh|a@!#i**|?7PdkZx<$O7$B`tkX8;qW%`)(^vDY0j%^0mAvOr6m;o#Gnl(V8&@vc@{Wel# zvGkOy$TlKbITmqm9*Ny#oT?WV4FzEkwkEH1He!$5(;?tRZV3;*u-s)lE2od}$WjRE;3`Cz{$_ER}Q;O$HU% z=p5GK-h(6vQ9{3h?My02*uE#dB91~6#0s} z9hUm@Bo*I2)svRqulDIYScPuF1K8$3IECbW+Z_osX!^I;ECB;o^tnF!I!z9hlJio9 zds}=Z-Vh!qs~ID!9*{amzQvbKWD7RA8tk zCEq1Jd3RXZ`*>$deF?y^t#=0pHw?!`1rgAq{Uz`%#w{}*>PCc0*v2X;QqL&2`OO^=0pL1vX#;Y|~oirne(Qfot#0N9NGHV?o; zp_5dYBZ1_A-R0TLLRqEty1v?*P|QP{yMWpH&PR^st(6GE^9T-K1?h44sW|wUgqi=S z;KUzPcvRc6?3f-+l9IYT?ml82sQ&b}=B>t1&ALj5ENyOw0N~0zW4C93Zc|)i&n3o(1J5bO zt^dQv1g}38oVKQ+>{v~&jV2W*tr@3eHs>lu!imF0cDA^1r)Ger&V9JrcRbAQ;=y%D z^tg-Z89+(XwjrVN{QZ=cW;ZL)^YBw_p<{ZsD`6WKl-YG^EG?gWaY1&#htA%sEYZU!w05V~i*voaRqP2~r zuao7UQoct~%u2N=l-rr8o5`?ob2Ub$ItsfNeL zY|6EkZDNFp-5v$puvyh#&#SowbQ(U%m!{?P{Rh$)O)NATA?I7kU zd=vT{flwUg*9c6^5GAk2H@xE)O(^Kkv#zqMq6%IkdfGl4qewE^xG`hab+LIj3Y7JL zkGe=mWWI|bv$npifsD`KFO5uFO!V@=>iV1g@yB|6%`7d$22CurUln&Fc5xX@niJIx zwrcWOoIt1ypvUC575@h=E%nx?o}HlanZ2TrHep?IMyc(O?^`yF(}WW>3lqQBixVcw z*kbeBB>RVHA2(R1;yUjxhYGPo{SZXEVyUL>MB2_`588(QKSs}u0rz|&zdK`HK!K26 z_xZD6-M3Oy%y@lFzLXwwtvEU~eD>^e#uDcUqfbL>25%n@`cn}`ApsK7TkaKv3&>=J z1P{Dl&<)J=z5ukqHhm*;=8=(=oveQDryQwJhw?{Bqr^Z_J(>?vNWzCF&LtXuhxK)k z_33xcOWt=V#bLLNB}KaULrV0Z&{&CJG;Bk$!|0u}O&4Mb^S`2>`c|2bWuKtEgiHCN zM>NTOxX->5rGv=oFW-UGa{I59)k9X8==qjaU14)!4F4;^&YX9bBE%9v{?}NYf()1c z*e~I+KWMPO92?y#BQIZZDMVZX0JuZpa-4^3HbGSUP!m}J9r|PChwMLAi{E?j8aSpV zDLxcI5EXQ;|A>Xz#BkZhjp3i7mrw^sAhJT(C3mc3fE(DUEjF)$WZx>06cR0zq^5U1 z6Dh|gh5YqHE$%f?p(DzC8phdky8p4;PBt>4uGi62vBG{e;w?NuQ~AUwH$!Eagw-Ri ztoB8{WXS?&dO!n4__}o0z$$oHCE#I0GvX+mP%6nB2;e^;j|_0?u9*$CTRVs#BtI24 zWlL8E1Wm8UhxvBGeeqdT1uG-N?snH41*8P)^pG{@>lYyoArX^S%gr<);cJNlJ_z~ zXvx6Bn)7IlS@H|&81c^Z>&(nJ3F}Z~c1mgIdfv3N6sm#Y(XIP6<3KRiu}8snpIotM zF{fX^8LVCA6-)GoqB0#AbUQ4t(41${8WC(mTbRtu?M>_A%5y#lQU}$?a6670b4j1) z;b!<+VyP7X9QY)^^oXzZ*p7M$Rnj08wjaSl$(WSYs`!`QNZw()pTWc7}KB$P=I&;>!NO-?SGV9nEMc zGGeEvTW*Xjd5viaeNf-}IDj*%?h-ciP@jHIU0t2{GN;fECjMtrijc1vHKty0Uy4qC zV7ivwv%scPX-Uh)hDz%Lrq)uGC{pG#n3xn#Om2A>(GDsQdLg$XoG)CfrbZKB^;VKc zfBsZDBTNtnayZY*bV7OZ4z_QtFguxF1t0QP_2j3I5M4bzVua3y<_06|rqwhzo5sG_ z+xG1AckadZ)wd9IE>+xIzCmQ&fF$WwyD%!Nc416_Y4-;l7V06RxX38puQJoO(s9`G znEZN>f}KpZ&<2W(-rZ|8?)_^)0vbBX`@;EXpiWGrllxwCt3bIk#M?Pqka{ytNSi}a zy^6+9;Ur+u#wV@DVwZO%f4+eaj0EG{eS}Ttz)cY&pCJ!%Nz~eRvTJ9T3{F*S-y}j6 zQUL}xJyu~`JERR&5cAi>R3TMQFgV}c%tHRL8D-CXWVG4o{b7dX_cJ21ip8^U zC9njb)i&|6kZKw-dIz)6OKG1XEZFABkQb7PmFK?8QY%@b={3la-WQS+0U@2>z+$&F zh^|x!Qb)NZX}L$ckA4r&CUnY5Xibu36=*%tf5|!$hJyu9n3)7iR>kL1v zT+bGI*AKv4v%RL@+*$W`o1 zhr4}u&HIn>ksPc$z!_fELQ@}nZk1yA=H6-3!|Uw>f+Q}s+=M+;79D!0yM_%Q{8b4U z1BFvVPrG12ARHUwzH04dC%-P1CJ?~g8C!nAHj=O(`_;;>J-ZbgGT2z=zr(Y=|Q z02-F7Zf%MzPA}oj!Ls3hg8DsA+BW$gUahp$r)?zz0CK)AJFCrlo%1c z9Y91pne?Zuo)@6A>wBL#=D1`h3SMb$OUYtq>sP~ov^hAKg$ixcC$znjw1D6gJ(6T} zy`GkS-Si~7D##bX2N%%Pr{Qbgl_81p+DQLQF3s4(Z}HC(ACM6zV8nlbO5i#3v@Y`5 zotq;-0N6tKm^}}D!^w}XC)r_yKh$(WpF-uGPCe&Q_u&rafP4H1uqU~~B2F$&@@wy) zcJawFLoD$xwmM*#x*H}p56cI%y^=SXbbfAf?6f3Ge&b78FFDMAQ2&f2Izsl{fg|Iu83p+o*VGp=2zEwh8QK{G9Rg_O@)c(1I)igQDqVHjlnmpx?eG zv(Py10;(C%(s!K#BGVePJmz)?$E_Nv!5?_Iu?KjkEc>_a>3KcjLwHyrpzJ-$gE#a{ z8AL#{vE+<6uPf|2NG2+Djtx#hq!RnQA=2QmJ~EEASr&0kRwN=A7?i3QIfJi-8j=b( zuZX$pIWD59Oqs3JQ7djkmOXb%0uabGLBs9P352f8U%pJ^FqOI4WyVhb^Oh~nT7hNL z`#`BD&uNbFi)40eLIExI#w{8I`dH-499sf-u9}C(WcP!Ou3BC4Q+k?F*8A`XuXq6; z&cnuAcu>5?m{|0ieq3}ibF}2XC8ud5CF$hy9MD&nsdJ``12B+xUUgsMA0C$nhBwx& z`KC1{dUdW(Lefwy(M|ks5B0}g%M1g9zHwQe(Eo()nR1go53hk3wRV5Bv9$a+{U2kU zsls8iqH=CPc45p;2WakD-IBk2Q_;9g&3!%YWY^~?olR<(!H+T@wsHoCo_d%lp6JHj z?i;R^=pvm5-_uW*>~mwJsW=yDV72?6ca(n#-G9H@a)*^z`>ug1(UFV_# zn^^3;8h+=*w_n~eI=~9Ej-BfB3G@w*`7?+sN{T@61jm`g-vtRSw-+;BtoVJ{7y-li zhhk;;*7qRo?RoP97b&DN!?NCRz6a}NaNm>!W}(b}wW|A_deSID`TcYstJRgjP-)H* zqg8nTSu<18iAsp;e>hIC%VHi02#ld|yyvh0JO*VA=tHp7*BZG$zMas6-#F3Au+e<@ z4otJ^rbpgdW8bZK=8+QKoAZKIoSCdVe;t!{sL0Km@{t5@Tg+??X~Yem%4SgYV<}xQ zQ43D^G$FlY!FCYZ?AGSq2GW>f0`6?4ozf(xW;U1L%x0hfmJ ziAs-=JwuDVR=%0*Lku3N7pRWTjM}sPF7QG=FXEYnwENXGc{u-=D@0NUZ=NJ;+nmeT z$(qi!I$p^{r6FYc^y;!aVs;3Se`0%eAG6 zip7LeTTH=je+~nKVL5dVA9!c~jTp!bhWpjry-mi9=CRpx;mJ?R%!IGEI>|rr4Y#xX zu|Bs!V@@7^<`Xk&b^ef2KAhFFP?=B6%8Vu>TO8~Cl?8|8t-o!m=~%JC-j#o>z^{rI zFD85*@uUJWNd^VFeBS@ilONz_XUJ!xf&TIGmk9!SueV-(U+Hs&G-ns;FNif26&Gl~<*OEjwwCUV>q2ifd&#r94fh>dmJY(Vbp z1=$}24(Sn!ENC?mYJe2=Z&D~3*TrIkarb4icf81N<1BsyWv}(bkVu^qNEa;tN-A?o zIuzKG9a;_XGGfSBvmP=oaWlWo5~Ko2UKsP~guxbs0PGg&y4N9yeys8db3V#_q}ovu z)$pllmj98*5Ku!oBOMhgP(*{6Vhc`b(lxX}63!%qVY@oo?F*zLr)&suimsB!I@IM}3V3+C9;J;Z05kWc` z$MPHHJ{E!MOEmu&F=z6RmwRdTO zqH{v=f0CmhYTm0?d^b!#Z2hH#i7x!d{70m!47i6Xw1xWB%55nGtGs}6lgajqFou@K z^6%Dw!1lEg#1oZze|PNc8-R`-fzTKA+n7+wm`?#Lbt#Y!=#yTzAu`9))y6^u>>xoY1f%b{We) zRYL;^s7lTxnfW{jn>*1b+*5c@Ih*6bTX*{|Ktt<25?v+lw zW&K*dr)(PZFyrQ1Ghq{B*9kYSkcikar#F*n$u(C$CiinUue0K?6ParoUIyvOOj}70 zj9;k@IUES3``mU^*7+INtHU&H-MC}{eDHGZ6?|ZQL!s=YE=R>k=bX~Ia0?N)95--% zvc2aHW!-(9*CR;%c8sx8OLeIG9=~672wc@s_Vd}H=2$v!i<7~5l3a^3ynRBtdkmg; z&UO`V;rL^sbzafO@-IQ^l=p20<*3A=nlGVl&k*hRI=d_nRc zK9uvy{KvX{8_aTZ)a77FBk@BuS1r}sn_1t2V}BH7t7Sc8@_XN8a=)5)Yxve3P-Qil zT$=NMd(BQ?4;4}!>hLGMz?lBRv|v%Vm4;9eDwt_y#Y!yPww$wbArb@pa11a zqq?%by#$;R%v&?6wO@eknP?YK0+UM@aQd>pjxprmij9F_FhTsm=iYuAM^uUDw^4qh zs1#=F%3biJ6xCq=6UPM5}3eEEEtAjNa`Vzb1ruH0wh!~w<2Qh*Fz0vWHAhRhTQONOqx zo9y(o=i)q7=Q6k%B2M#ZuS)=jwC%lVe`Qe+U0F!p%>_@CcwHZ>=zS((FXr5zgwE&? zul`nB-~uK5a%TyAx)00gyn&?%*iXn65BIC_<-!*Yh@o|e=F){Okf#hnU#aaKk0>O` z3qam~HC2(YMbCP^c4m#&M3j5vroVsm#11oRH2+#?2L>J=sT_s1Z&y9CJ!#rOKAX8< zrLUZ)Z-@DALGXpjlfN*y?INvR?ZM{IdC`}U4&J#(bTtE9ytHE6=L9L}Xa^r~eMSG4(!H+Frq}Ve1pNls5J-&eQ{f{DNdb+dy$|hO8 zPbT{iG$*C{G(cxyZInc!(jRc7Q^s(lvGE_?!q8$45#y42{)g@afUhKwl?q`YE4U%g-3o>C{(yvWcuw43Z`Jkx z73elbT!gKKAKV2%eDEC!yL5NyL*9c&w$*~DIrQTcePYLb^ku`Tru=1vO(wUWp^F_GJs|&crR8L00?3%~UtlV?{&SDgeGnZo)N?lsg+aOf*sRh(Aoqu^=L0t4F30nkq2S^m@bxPK$b#H4KtPmFe3z8wxh`dBEmCs? z%jcGmwNlyF>{~Hz=g$YT3{!16?J#gZw>5SgyX`TMn|DnMJ5s=#o?@S2FZ!d$uGDc| zhh1l(MG@O$0}#&%&P0;w?Y3a2*HKb%LV|YP@AFEV+tkjX+=z;ygFjk>KdhEn%>6^_ z65wDUxOAKo7uOmucn!^5V&a^?2i8l**fafr1Z0s6Y3cU`_)9uyRj;8tGi>CT2CU8 z&~4~Xp~{Nc|EUeRmB&7T_y%dZSA%NVS`B#Ufg@nN2o3wee3${OdT+9fg{GMC3@wxH zE3q_|2?>~FqN~7seLk}grn!w)&3|i;&k7lw?ZK0vu`1Cr#bp;f5(Uu~mUZ#}3c}aH zYpR-e>h;~%4etuM4F(wrpMsjM=^^KBxU5HW$*UGp&=J8m+MyNK{hL4W20Wd(2IRgp z&jit?L~3aK?T&K}-8Fz#{Lh2%4rkgcBF`4qjvUh!ltu{bUz-HByn`;a#@WnvVluVZ zQ{ML%7Tg9>;`7BsiD%D$|Ck3Nlj@(`PH0^FntsL7D?ORQ_l@|&wQ-`cj)su=VEp_PS>o5(IzfU6bOyya`Xqu!b_Z&R#@!P%zwFn}Ch}P?9WwXx z49T8R$yU@yIEFkN<2ahS%J?VFy$%cg>1wvOk;?b}A`o8tVfzT!{ve4oJq&%+A)e+b zg49F2c4UMAjkBpEVW&EbDl}C8H0{QQZ-v#Y(A2lWC0)Zlo%oqSV=LM0pth{?!mz;Y z6@}ze2jO5ZH!;&-W2Zm~=$S*tyn{*OaSG`JJX^^PUQ>!C*Q{e?#s5#t!>GRCE5=M{g1+!Rop+~!pFnn9e$FJe{RB)o0Ycg9Vx>!ZKQkX}y zEk!Q2c{b>tPzUGGx3M&~;|jLl+gg`#e{eC65+7}IcJDv2CLC%{B%{X6z68*3!rXH$ zA&2Vy8Ef6HH0S!|x~*txhmZ9D*)C%wcDe-8zur~Pl(l4Zaee9f9|EcAe9Sq2O3(A0=P zCBJ(0;XipqBC1x7NizhB*)us2lDW>2Is?J5R&kvCDQ2Mrc(b#1u)#oc;4*&*5IK;x za~VORA5aS=7va#&l8cTnIz0Pdj#G7#8k&5B*WG}fUO%Py?TS8S-GdP-jvIM$awlUj z?D0_gk&H?EqI;vu>6Z)KWtZ8NCsu@2$ge#c#~+FXzY(TF0NP(Xml{6?A&DG!p`S6wpdy zri0%S9{bi8{akmmu|J{YUjJj2(s;-`&<( z#bPOvCgXSyo+HDdO9niWFE|>jPb__Owrnu`o?+`)21?qNvyuBNXAWHKng2c^s9G$> zvV{%@CpJ+Z_2_bTGAdKLFFplcCt&R!0_k>@kh5vTua|lX?Jl!vysf`9+{b`SjudjdYKF^qMgSFUnl>BamdUZnuaN) zoltEmW@SL{q{Wv)Jm+v3W86oRP ze`ZZ{e``Pgy!ZZ34bYUNFvAkMXOAww`hOUz`p-LGk1Mk={ie2P#9n*N{vVI`3vql^ z&%NElBClJLY(HtFfzg}oS4W6%J4N=l1BRPj1uaOew?Ok(>eUlKq(_oUgU(8!uaGT<6p^N&WBSGy1VU?*yrzT356r9g_Z?)YbxmVx&T?Z+I&0i|=et$Y~GuNATuF zuVf`f#ApE?9}l=<<2PIUaac)wIq;bXH@j`%^Sk-;zdJj0m>?Y65_(FFP8%gLk7N#C zmvDB93iJ=7HgCqdL)yZjiRYB}B+BUTy0NChPJbr7F~snZz;~1_pOL;W)cf_lDlk%_ z9xdTpk*4PC*`Gc8H#Vu{AzNtDB>s+osy*h~Qo;jokJm=9uk9^IXm;tiNXUQTQQhZ; z&WqBzC6$6`V%ttSfIPbnX!Y4-!PLTHzO{x0MZ{XmbaeJMJwG=wZU5T4BuG8`jQ4~A zMBBSLE}sP`A+Fj>@HcsTZ-rLguuROuIlr|}umh#zeIV-Fkz7-9fOJ8J%>wvxRo-c}@@xTc^AcM!<8C;MH zjm^_E8$9O@)pBl+eMDeI7iP*R(*G(yrESHw{||OYe!I%<6w5n5x%Re!y?*Wg^m?_K z`*BxR^;aaaNmI=>=?_7lwP&=d)XsfH^i4L4&0{E1hshiEy*h6((b%Zh_NC2OQ@I|V zrGDrOmVZ18UEu#T;e4^EIRvd14}(ObbeIrI|6zTm0@B^`N&JYG^{JjE^YotK=XtNX zH|IH}6@M0_w(dQ)aIu;Vy143&u`wUzMKK@k`+(g&K`Pd zrP2>tZ&%*y$J{$sCWi)^u4X?>s^)i9Z(HwLvL(*WM$yk*Y-%f7toOgqk#E#?8}K_$ z8~Y#fdq&Uy#T_i@xG(z7ndX8%E@_92${X+mbJ*S`^P;SYM9qQqY?lymLOS%RAXKPz zUGyoIv;)lh0=An$NAc57K#6bqh5a`@mJ1Bu4pk*UCq{J1heg%r4oT_G>^hbn?f5<~ zWp?^5@pdbe!(S-!g8&fZI!2ag1`lLY$0%c6%3jYkvJihj)UA$y8UWuzvlT(t$;gt=&Yzc z*LVK<$2O7Pf9htC)d2q}4q6cjzxd^@_Uo#kr{5>kgRV z7ktgSYi6WN+SPu}^Gm8htE`rw(eDaaq&GSVxp4TUCwF{}yK8>*dQ+=@)L3}QE#Utq z71AdFr$e@`^cmcS?4}P-KGm<5zaTI#sXjBGP=bsvLU;xDmT;2~$ZEUbSRR{IKU~k- zzVO4E?(m*cxPbSTDRhZUe(fCGmk}ciJwXLXLihk2Nqox$!7d-EI~YLq(X;=WFH!OQ zV>ZY;y!hc24ooS9Df|JWtVlM9K2fk+s}EnFK*y`VQxxy}Q6oP$lEj}Mw=J^}o)+@@ zk>Fp&e-Pe(!e_0O34N$QcX1Z`td&D=p4*^Nl1#MLjtAW+n2dB+Zo1qd-0~OorXY1b zEO2A*^BhH5J#vPFHeLcTabyFZ9pL?^k|CTm&|qHa|LkAT7iC4f7^jCZV`LKfmPHDF>M4$WA}X&ENoODF2JQC+z}Y6%idF zMIjmX>Jl_(zUpajBn|8L_;M7*htP|dt)KoDVYmM!epe{JFik%q`LfZ&j;>bygA9Eh z4z|o)k>*jg|G}>a#kPZwZ0*0%qK+T!FoX>J-Qy!#LJy$PINle!s}vP&Zz=2h2{j~7 z{+9=9xk@7Mm_7>$|5u1l_kZ#uooixN=&QToaDfY{+E83eoUY%2kH!bZ$e$h+>GI8; z`_M{h+SsaL7ERg?-_&+>0{B?>VISZnYs4{Znjik_C5OV{{a8O#b_4zE_)ubnF2w&w z9P^tJe26`PMy*E^UA*qiLw!=)GrKu_C>Z8J%}cM?^!)t2jcIjp92O+5|9``Fl8-OC zqL06>p{CPE)~6vT_IULXcx1j3ZNE=2q38cl_ucs& zWYqwl7#&C~D;m(=ck^J^_PorwqGdVC9s&{7FIv(y!Ba{{bkN4&>jPb#?vF4CB;mfQOY=E}+XuSH>C&t=l>3#ik16=Xg@)&StY-cwfAQcWGk z1=Lnm84v`}1G~(reF*m?&hr^72}pF5^eJ0k>Y4S{%Q=xTK{x*wKcXh@d0^ld#Rk~G z6Or%)etlhtHB_hF$ZjU6Sruyqgy7hqk0YukDb!XAT_#p{%kCFuc#>UuvCU@OJt9V|CzWDL#^PYDI@|cfLn@jX zQqj8w2ce+K-vwb$g@ho527b4+b)M{64-me_H&}+Y$`QTY+}9x{4E-<8fFXr-RWyBK zPaYp6VFcoC^FrSFpuKaA`Gu$?$n)$tP-+TeUE45N6xLs-AY5aSb9`{Rwm>R3#HO?L z?_|JK8N}SN*ekW>9K`zidS2Q%Nqi1U>7t7efuWd1>_Sa=@RL9=wg=x`gH*Y>sxY4i z>Jk#kxd|)}CMG8ImP*V8`Mu#;rFkz1er2(V5HuQ+B0YKTriktUcxk)qz;{1wX~*C{ zUqMpxG+WqftN3xlpv;y|?x+HS`6WUqFUuV?9bqrmsG{ ztQB-5jPT%4oia9O;NpHdoS0qjn}ZkT`}`>>Qttbg~07s zua=!Q>MbCKd}Dc)q@}28G_pJ`y;`fQf{9(Jaez1GpY`eIO@22E1l_&0eE*%Q$gi>l zsTDpKIt`c#)I{vv>*3WyA#iJ(SYAqrxu=NH*Kl2P(9KSN;!ke8qo#<~3d=lIdCqIw z`4QTT>;6x$S?&2=m7ECjn{{4b? z1*t7?Sh@bKBsSR0QFY~TrFDg@`{H7{_v3+mNfa74qjB@cp95+D*?|Y2tD?_1{0LM1 zH@k2)m7`8+qBAm~YX=L_(}v1K8wqOxgdaN>wZ`qQ!JC+oA%&$462se=A-r>gRqs{XSteS$z2cxSa>nl>bs)0AYqhYVYc|!zgQR4 zg_d2nn=A!8Q4&~}L(glA!hk2vtAI>qvyW{J9G_)%oBtplOUP@kOuurIs;9vQ!}5b- z?~bA1BOkdNfP%LU{%qEY*_;$1j_|81OqI;d75ERLg_7NTuDK8Gb}x()X+NpM`a}Rj z|H*EBS(btr@cGaYaR814kW65}D`8oq00%=%aUZ4D3i=36&l6e-;^rD|=1XjD^MJQ|@5FrKpS=_Yee&Tts4!6OM5$f%FYu)&OA=>~}+YvyW-`=$Y zL}>iA_{k?Z?dQN~%lIMvS~e#bpzhStcI33tA<5y|cq?m}Z^L={S$`)5d52ETLwexK zS=c!)>pf!~k10TVQ=?$h04%I9pweVOhD($yvKf#E#~~%N=e|4^Q)C7lN@^8@(qG1y z5`TdHdcSOn zI0MO&y}yD9e#h`qpYL;}Btublt@1*Lx=p!(@d8^uuB z+P=+o21;o!l+?wkH)tVVt1&|`*k4+89 z+M##beFV;SUbEZzs#yF)ZR&pj%pe482-M(ATRsY9JvHP<-u}Yd@I6qniKcZxZ0?t- zqbRU5?|R?ul_1||rl#gsg*GY9L4U>)c3Ci9yRrxkq4@0o5)9{4PSQb9K#?+5ZFKNK zlFFmEwul!wt7g-4r}x^?*pkg3?=zGjn$#ho_FVH>fZx6d$O+IivgvtDjBKU0s~G|u z6woI4tw2?_%q&Q{c6@@Y%McgW9-a__-=pq9Ckv0ejlxtDQ%&ZgjfARuX&gTHB6tDTJU2T6d$AMMXd8Sc@_B9Vwk z1i!t--}~31XT6iC$G7P^jfy)=1zIEIFx^1VXw2nV6reuO-6?4z*y@8e-?uXdiQO?RgK?@Z-qrrLbX|h4Y@b0=e zHSV09St_&+f-wZeIBOBW)YNR|&#PeTsz5SL9tVlinDmF!=FlP#^r?-W?DN`L>FtOm z7{A^87Lgigj!|+hIGVAJ)1W6x*pctn1RuFM_wx*8GuqgJFavnCJ_!buiVUp-jV5hU zgmWp}z#``{xV9v+8KsK#JqTXXrfF}j3T=no2kLa!ZCzCq;s`8RktmK#;2a()P4j2S;d!UDp)AV zA>f89v7J}JLYfP_&5DmQy%an4AG-GBe_?RbcPc2vGuUH=*~*-%d6WGQdW{r@nqZVB zYnLCV^S^YR1@)#)*<5#zbybwY;L*T{H?g)#dnh3z@rw~ETc1DC0x*Vx1(|sA=c!ll z)c^W18b#DrKieR2fLP^1a#ZSriT%)2o z;x;39K!-Pk13vOYhXg}Z46LS}$HEI#x!@nhdqxf_^>3qhixDPB%o!xt!4R=RO7x4@ zzU%ED#(VC+2!?H6jjJ%z%^ni`0w+#kH+Mt(ts9$Pg(%t^TV*%6k9GcwS@7qer++YY zjCf9?c_7}KeE3h;zV#MgwrdTF%l{SdIC-^1w%j1uuBRe%ffq_=94DURT70JlyZr8H z?edhNVxcN_vZ>&CD>%yF@yz!ILq~1WWgA=Dilty>F?-K}Nj0-qlnqzK&T6w=O!*vJ zx()z!e0pPqg3d`e;+BfBwZM*+AhexZaRnBc3PiH>?oN!Yi%=X#_uJ$*Scqx^Y|gZ><#e*l*v>U(kuI+^&3<8~?H`hot-_I?$ekH10;SGi>Uz=5Hm;CrIgPQH>OCcRU1g9ipKu^ z*kkkSFv{uu_)ihHkg$~I+zuQsekv6b-i(ZqtpKcHT9JQI6I`O#N3wE1gw)gHbhrBLGvi-LcP>GhelRu;!(^;tr_Ox&OJ!32++1?GxbIuL4LeG-OjZqIH zffaj+Nxq}2XBMJ`D!i)uXB-rtO&L%CDi6Z`rnNYP0DWFP|D zo&3AW4VkPC^n5UIAAXmuap15B8qN)Z-;dJ%zKs-dU|^POuf?25m>h$nb06sh)b!ut zLhcXz_PsoJ2zRo>aT4%$TYa0C`lzT;Rx=d7ttr!3!$DdY0$^F0ELt?|cUtC=jh((H7o^A0x;353F7@hTd$@*3v(%>$Ioh}qtS;q^+3@2K4 zL$e_bisgp`1Z*0T+{h}}8!s(eCo>3jr6f-*IKw7jSO(&oNj!N#+KwYgds@V!s;7H) zj|3CmYbyawvzM3fD@T8P%2g#)q>UZ@R z@m{m~h2J2Y37xwX9~KojH#SLUsjX z*veTx#O2HOhpA5WJNfyMpTdHNP)mxP_it*-sW_qd`=xV5IZfZB=!;1t43vBE&Nhd7 z++PtS#Hzk*BQSvk?N*lr%5U*+O+me@Gt1I)hV^voo#W>s4#DSJ{?{87U}~(Jb+F@a zSs5&Z`Hp!)&@TA{T#4jO=6kFt4@oJC%hfp}ZF|Cd5U`)Sk)82B)abV;A=^4uX24yG zLxTXhrW76ja@u}0ySSyzbz!|cF2A|=P+!-%*VH~hQTaC?b1o&tc5JhM#$=}wJlZ## zlzDWI6B+qtyoZiLD&DE?bY$y8bIDuw)&gWh>ko;u97vo(w!VtG6pYia+!au4_B+6p z&bYgRTLI6~XygUYjNO^0Jo6s|!KeI7L{!0-rWiE4v)Y=^(S$5fCd4sbzhok0Zda5Y zzhz=YK+&#W?0s3MlSR+&UdH0eK)1{7fcP4Q5-9p7Nt4lZl$VpWrK8efy;2pxgEWXk zr!bmzcsnELugbW}5e565?p&c*f^<5Nz|L8+_{Tw3wo{7Z6YNd@LP^VTSvPzA+9hTy zQ%bo1;^TZkyowbNY6$719BC(d&3guY=3|t*+}BlK-*coWu*INgd0cyIg{)^(mNkDV z9APeo=rH0_2tgb3&!X3mkcc}QZ6|L!6Bb5|N;cZ+DDH0fn=v1C?m+v2W8!|v^tU%Q zW!}5By%Jq|%nu!)q1%;oUc$f+2r;k` zLM-VCKSmSxm)m#{VBy(p-&3OmWY5S80?{a$GD}Mi;pW96RJ=?F52#{A?4)lpXlg%} zBl_rZM-W1$fg|AYyx_!@n=G?4UC)yyau*W>=R6Hi^aNOv007k>GXTeeCzmmrFFNw_DAQ)UB{!CE zA~6IG)UK(STi+oO1xgL)Yl5}mzwz~X`G{W+2#f}RHQQa7nxu+w*z=1|u5*R8re^lt z6u{Vkwhx*sa-k^9S25akYij2f1ASZP-Y23MO&h67?@&P4qNfX~QJb=7QdA8}2ekcu z^xJ*~g7NC|Wi5SoO2xk&BmNrSnt(U68-4%?@*G_tnAje`B3oHQoc5Rmelm<^JzFPZ38_-zm6i4% zwIfbah}%LBesChIRl%x>G#T?ykIOXaFK>Tv1UV$u?B^5e;6I<30L_G^kSu22=1#w* zZ@w3^70k?%t;Hs-0TLy5Ha%bQCUf-5n}eC3fRLfQs|;=e%ycb_6-@h0_?wjAJVDP658iw%;TUIag6O>gzU)cod`PDmn}O`^1z_s37C zNFyn8g#W5vvSnOpwgP?YsD+Tn#+6LoWS}IEZw1=+EcM6^vK7|p2GRurJ{_UNz=isB z)UYf`Ca=6`>&b4p7}$nE04nJ>L!7|;pQ_cJLCSah+47KF!6(HQ?i9iP%b-d2zOBV0 zjnv$Ug=p*TZhb_`V*=y#Y&$^ojK^^V0JL9>OMadY4Pp%JaRenhSX^wR$7z}6c5e|B z+|n5-g^sN(^tE{kKduD z3zSntBkmn1CXr!v5P>Cd5Li<*9YtY(z^L>Wy|-cBX|mD>f@S*&1Nbc7BJNMaJuShF zQ$hj*6+Ykc=kt*HZ~5+&O9^A+urv^>Pj8BPcwHtCN;Li{@w!eL85tR=%7Vr(m}!xp zLlC?(3gkb}s64p#{lusH+~Go(wm7^Sc$2HBfbR@6z(3EK0^uBjzZ3ygEQILafp6KU z0P>y0aB`)7$dt-}H_Rt*MX*efsRl-36Pao z$5EZ+_aIm4@_UwY3}44<9lsRaR2cOW^ljkqcpjersOt^%i0m-C-Xe`qQ5pRl4H%I~ z@U11aT|3*EaI(|myKZ{>hpGA8w2;`6H{$L^BmXKvo&Mqa1ypZkr_&2aJ`gzl9b+PC z;P|n9&y~}-kLNVF9mNmfa?a2>_wo3j>dTM+gl!0E$6;8`+mU}8q-FsHUD3s?$M}vw zk`<^oLW~zQ;V0FML{sJ?%e*TrI?Ex8+~92~0Gd>PyX~!^Tg$a#M^s})>r(m6Ks8U6 zUK6tDDg`gbOvYTy3@inb(%T>J&LlGdz(yfi`^JCCSP{L0kzv@*e5DKFIw*)>@0+h( z$b7{j=J3Wl07Bk!g&_W&|7Q&KYNa9m(%+}M$af2Zf@nCFf^0Bc}PWdmuBWYmYdii3!=EMi>4Ke^!}oZKbl_q-#9IVn zmF{AbU)4+`i0!;q#eRd*5C-sXc-M2ZWJqC#AK+U+UZY2uW!*zLZ8K^8+03pl z!Rh2rO5us1KP7y%KRg(5W&0R0XgqA#;Hk77k{Xrt+-4tQA()^o+E&Q1o?Xo@M)YIU zfNY&U@!S3H68k|2MAwtDE<0sRaJt&7`cK5@j{&N+_Y7r`DMhF^T&t_lOIzAWo{II=1u+t1CS`CI##y3D093{0ivIWaQG#1Y@cJO zs3k_-;wI@u4~_e05PQqQ~xqOi5jTe3wq7BLk0h#N;yVY_Gi$PLN5QTMo`J#i_tu~ zbOgz+hZo`U>U{AvE+2;t`&q8&QAc-s*_HzV`4-Kio11N3_8b`R{20B{ zLwILe4m@zqwe_&&sLi+I9-Y4w}3v~;&orN~;b*e^d1 zG8~dfCMY`_g1O-ativFs82IALaCl^gsEefg@6`0#{O#dTBH@$G%}Hyc=4~z3&?4xh zUk?cp<%v2|?mMe^RcstMtgXEuMJq&&vN=E}GLHWZ$Xo0QO0~epH6MqF->&=7nsZUr z%U3q_jO#BcSW-?HmdlleD}9yshcsple9&$st=d$GBp86;#(d_)^xo`0r&g^niTQ*A zoz3;MMNnyyl$CR-ToPgM7>$7`Z+MJ&!3EBMgV!Z&F_#^=S1L1)bl4XHmFl^wfd^QBC@Uv5cWR5D!jrA-(>D4gTtH^vx>g*jG2Tthpwzx+zRqMv zRgQt^`lL3rLYB@4@dU>BP83^a?&~u`5{{@amDr%uIPFGYjCi+o3diVL0oJ0YOObcy zyc`k{!HLm40LM9Y{bE_j=!hqLa)BfpRAq84a9URezcLlWtHtbobv^KnA$9)X1EM^ZW&&a}1EYf ziW3=Z&mXhi!dD8uy|a`>Ax(7=w?R*2h_3hYLsjhY^}t|{yglF=idN_rvb}JAsY6j0 z$V_#jyfSmUz1o7q6a1}9PhDe!c4yWDc`VD{cMIFFz<}0d{b|qdHkz4Z2IF& zIHWytfCr>?+)ibP4lxH`A9i=0HMO4kZ7IcvZeKaQKx&8*T5F-4?)nvuD`}pv+EA z2(D5P8VI;8kAl#62Xt6!HM=-|eV4qMY2Oi@WF`yYm{w~3_z;_mChJ8TI@c7tzflcX zZ3aBA;XvHe-Sz&=IE^@jjzGj!UJMDS`yb5QhX;;sIN1P^>U^*a zg3~C9%L-!0Xk_q-$7k4xsne2gDMze02u~XXz8&cH10dqBgq>3&O7A$YO>i4MW-Y*{LmI3<6$jv{I$G!c}P-;9)@8tZ??sQ$@#22PuU zjYVg&w}fSm+$>K&lvw51-xKQIzn)vw#;SVhUmq{MtgQC{=Pm}rNMp0umOmyof9Dx* z^1{&Q#(`W8WH;m^qh7^cg105LhS{zzzpk^BobVnuxxaB7!TAKDwFH}FS;SZu76v0H z>-z7{~}eb@`jsdVCd&_ zXTHe;c{$;(Xv!&;w`^-b+#jbGgl>@sG3VwnMGmh3Y6S$v%RAK=-?yxjEPfDFJsuxe z`St;9z$+HV9vw;WLMcUaw{6Cbyyn=0xS)W&aAzR?lS_X~t{B`%V#N!A95@dCwe^Mf zlMFX&A(U}=N^ts0jD_R48L9WXnbJjoQ#}9k_qc&+MXjQ(LkP&fB=>wZu>S$MIrVr{ z!I)xTWDK+z7OMZMnp5Vc@mJ}~3hXCOBy4TyO2KEYq4-YdDhH}(rut{xjNHw4Opih1M3&X z*y}z~YW_CGf*VBBO&E@!RCY9Hc?Mx-1z))m47 zdM*j}=TlxkHBl^p3j^YwI)xV4OHSaYL+K?3f~wSF5D)$e^ug-8jwFGiP-v*nqUz@5 z9Dg#q?SNam#LjjVgSEaC#h-32ikK&ZAn<$|?N7-=H zZw7F6K#86M?Yy6;r0w&4qtl0%eGei@uz+jaZIU*QFcK=hRSV{nqb3a&sZ=-fVb{>* zd)eD26h*=>FlO!5Xe3E`$5ie6iL?-#Y1?>?irjwqE)86dGb>{KxqRIxw#@UJDBOQ_ z`PZp`mr?M#loE)M*xoSyJ=W!II;62@wq<$8+_p$}=bQF`Y8wsW!JEKa*zS%Ww;5U$ zzbequ1X8Y$3UX2`n25im<-KYE9?_`^MFja{KC?vXokZJ zba-1rDEkIQtV%*g%Ixi;zey(o!GqM{s)HT0BV9r3a%-*M5-4FZHcu<&Waz(lekpV7 z?c#&oV*JvSA+rE#+v@}Z+-4HHvi>&8v*}Zfh&9}a!jwo51*mt}HLQQd7JoJ6H%;d7 z-QQYNvHL5wo={$*e*X||=xA0Sz^I<^A_sax*Ebw|HZ*dI@7(+vS3bKP%*Q(4z=C*b zR?USBzEreaYh?`qU|64bo5rUV)3>v#VuQ9LWoFhI;o?Yz-FD)`=y0ZhkOgfWQF`L+ zwBjZ4@~zq(m#Najl^J^woJnlNnv;eSfQo{{%aeF%7jK}1*h$vUPba4R+IK~L1&faOxhGVi zoNXg>NAmr=uI%XYa~HUBGwV=5a?7759>DenypDG=z1x5%m7E`InpR|Xyti?@&=jmk z{+q|i&iUKGkf$(!tsq{z2uh%>K6>HiaNicF0@Nsv8mPX`xF>-|AjFT?5y&#o8I)T& zGN&ue-(pPjOgg>A@yjD>(v#CM-`lr(Luldt75NLu6uPYV&M+aTw$6~{hVBd#A?XFu zsWyIz-2&`#B1k~{+q1A6Zh&gNKS<*HcV9profenc5NiO&C|_m9eaa`-7(Ngpq>Ef2 z*+tu(GuG5)WW3&=x|AInTN2sd_n7@Uhli znABCy*)|XE`LGAU|DN9;+5EE`^tD|^iqrxw>7{PY6mq{?1|fev15iQP$aj!DC?BWW#3Q zpwGbKdgVM^C%D_e{*1wdr5P?q!PZ9%!D%OUzVRj}Ihk2s_Azgp@JbN3G^g&rHQgU- z38xS>`h~Ne6odFdjuV+F;b?zvdiY&`K6N^QjRa8VkSU2=%jL(T9PT~g!DiI0w`_Yk zT6+COf)NP*lJ^104e@<0_ohh(v&+jO+KL4&6{0y$?E~`L8ympl(Xty~c>g{+TBdKcdKmFHRvE!=(ji`oMl>oo_Y| zQq5NDn3!6n$p=ftt@NiO)Tg!|@g9~*0*k{nNQ+Z6$m;Q1>aWQs5*+%&gkFmTKXR3w z1ZV4a(|Rr_>|A`m%^X<1__#@%H~YmkTBkT&xC~X}SUVG@SZ2Hfy71ihia|WXr|3@04{Fp?NwRR|c0gT2Oq_2p;*?`tOpGlE>kS(vo*ewkqdZzb1?gm9CDwimTj=7OUTG12{xS z*%V2c)yKnopnE-BaZ)W2YeJUa1831cp;kN{jz|D#$7T4gHr&xmxeDCd1No40F+kTI z1S07SbPE=(=^4Ba{=;tqD)0!-R#0C;Af*5K_sV7m-9p=9Ae-5|N_ z^VRzsZuNuY%_lTF4;@6Fk#a2>dXV-ycG*58eLJzggz+XzqB`2zp0geUaiUhOFyxLN zw-0YVBq|$)BxPir&9=yyla3p%+3*WjbRzpxLysKd`03k20`*si zb)H~|Wd`eFm^zc7mi}{VQ#jK#-A3<)VOxUWjV?uQj<@Ht#((;}O;#`9_SsAB$p1i4 z3rTmuD&sXm`4T-fx2^U6N>*R4k}}hCmD}DE16oEHWz111_jvY6pXWHOIELBl$Z@wq+x4?$Dh_~j7b-j4j!L-X{O~M zw@`u2d~u#jEsTC3TqHQR;mN|p9id=D$#s2)J7yfo!iK3GkjJ<%qeSHu?m0k>YG0fg+(^I`XZ_F;VfM3E##`YPQ*`=o>P0vE=0(S! z?>LGtsYp^>zgnk|Y>2+#X*SXt9eu3(Bay!EiK4r*$2Bu+2lWN|^+B9{dbY5IrVQ7@ zSu506ewD)kc@fJwNd|3d5sXK5LpWI@97wTcC3adg<~&<-DeC&E%J%rtXGdl$H}|yK z);HCjVx5jSAkL4^s_@EDB}JU5_YhEPagMf8qEkfMiSbO9{3>xoc&p^g7Nl;7l)mkW z2v`tsuQ7D^D<@#VgmjC zJJOlNVZ|+!Qhq{zedgfWmHjgO9@iW;9ugZ!~4zzv&`)puL(igd$uU?0O4RrV{$DD`(3u{uCBhDb8^= z>h~z8@uD-yK2o}W0Ik$Lc8SbhG>f};c=&b)-2;2&!L|2wc83Q(N2g749O9BdNpP!0 zU`RP9^fd*Cm&3<<6mZI0?_5XU&$1Vl+Cde|gUqTkBNt!vYu1KMcu$P#4dk~RHQ5=N zkcGGFPb3QHeKCc{%=)$33%}u@@wDB-UQNSQdYXap&!W4EErku{G zxReAd{#K|{ZzcU_j+YXp0<2xi$}V*k#lrYkSkITOhs_Izh2Gm|DL3Je((22n1Ywg4{cOs@?LMHx2ip{A$d=w zY+24eoT?l>X6Ca*H9|zMrzzp6`rOue_{qcMD@(Kfv*vf}6v$sDvhD^-DzA`IMEDxX zjCC#JM>i_CF~--_B6wJrq&!hwGS`*R{G&Rx@1CCtU{s2%$ScdTXXW~uHEL3jQOZ>+ zG%dxaXXd1?$$vfP{#v#WZXlJQT8D1*J%2HYAoOF1ZAj~_g#ub|He}|3l#1=mx&yzb z!(FS;1h$hyEZ@BdpHF25+sLszKUlqbrSAB6dQ|2(p5Tn1ZOz_O-WrkF5#QphjK1-O zF5Ju6DoJ-=K zcnwlc0*$x={<-%wE;H{u-6!Z%dhva>ytH}hI<0g{=#!gNOq(ywxEBtL7k3Z?eO{XB z{+27QDblZDiXtEnX3vJ7PI%$EmYvZf_CxksiSfFUTJOnOzcRHPlz6UbURW?Tvy34J z#WOkc?#2Rp?I~tW!Pz%`wp#H_Qv0@^2H^?OQ))&5-*$0V4H5AmGE#$=B{vhB=qn{ zb8frLiXg_=uU1R(jU5M-3V5$OFy@3WxSHtK*9=#tx=U7c<+49A;F(;Y-@kFid|el_ zBd(FNnCgT~Cr%gT4{lzmbM$Hs%(nD;Ib{`bAZPS-{B;r*Otm|w0~g7)ccr{d_TD=) zBjel8=#&Y*t&YM=wym!h`yM`%R$oX;2!8f~ZiuEOxChg(AwI6u4L)}nL8@fT6>PD_%!piahngJqsZzp|uF-Q~9OPYzgU=^S^yL*%OWHN%`lhrj9> z*TU{Bo7Eo=E+tL<&^;AAWwy?faE(s!Szpe%gFL0TIu*FGC1%pJm(@eD&$cR*sf@zz zEZ{!SIbvL7)w+N1>9EwMS@rr7rA|w~e7iE>W0G)NsjWzakK+ny;fDpML{8%eF~NNC z%`3$sd_CXVOsGBlLls8B^|wY#O5HRqKIDf#m-N2Q=F>r* zO9`Gy$YWgp!r5DS;dR(_17(H6C&;F^J8QUmoJ4%@*Ws};?`Fa#P;9ojRR$B{UCtg0 ziW|Koq1s$>UqqAx?X%^Xw8g$X_}Aui=JLiyI~jExqC)eB`ZYt0WRB<9=Y+d%zXKh1{_P#(`X}Y#s?tk+=3=PV%W1aAs#^ zZ;k9AbOv9=xy=cO7TGIv0hhA7;s)Eh)Rc z5LKJKAUADM9DPu&+Vq1PQ254L%$x-!7`^)ySY440z`URgbt&^+HUJV!xfzU=3D9%H{MDoCn7{gdX!ab zfLh`>Ip8`F?N!pio^GSw@2VFTki)lZAI4OcBIUVNrc5P%Pn_SYL|JKR?zbH7q7@`& z(n1@g`(#73)m|HRR9Ljq^lKVpmzA~!gTZlmBa|K~CNHD9Fz_IeTRA}(s%XYt^3K7* zEY4jgKqkF^>{M9_!8FVPFK%^qa>20p4Ots1Zl{D61dpLH`NM4Kta8GyjhTiMW^p-5 zqbAF8I<18$o>tAfDJfHdQ@Ns1Y@L-ISwDsRry@@m<+UHa9%|GbH{ls%j3u@qM{6aE ztRMM*TFGwRcUY)c!E3-xS(uT8xr`DMnt_Ujw6_P7J+7(rVJrCRiQL4Y3v`>cZPW5+ zOVbkhQQ(kb1fkm_L&LKgM&)EQH2L$s6GxRYvy@r4dql8O&6IBkn_oEq0*;^7-7vM zE4io`8*!|&7ZKy5YnJtizdyL%z?zDG7&_bIDB3*LTqmGis$9WT^GWltXG0_L(e9IK+qI1Oty`O+ovTyoj9%M@8=J zzg(!&^GD^-Z51{;o(p0=GRnHp5S=yAUrV{#RIk+xfEu{Lens zpXL?%WM{ybY@Hr20q6Vn&Zp7qOBmvI=Da7m_gxkAeC>EtxE=eE@TUq1cI^qxHuV z(Szb1XWW$W8&ek7Yny6GgtRn{3hlZR-+TubdFh6y=cM@Y&ZH3Wg!4X8`MMFJZS;7{ zE?BeOpQF8U10hh%441G>d+Amg(2xu~7;~tg zgS)ZHRw9!#xH*C@-ReUYX(`vHK%@B#1L#+`P9cx7nTMs5!X&ZgpnF}JKRNif495&! z+NW#>D$7v82OWy{{^sDrM@@xRFtfW;773ZQf zh4V+M=FU%;hkLIki0L0yWv@qr*ZOYebo2Wb*arnPdE$s(@8gJ3IlA2+p);{mR@reau57oIJTfOfh%>KX_e-6S1%Ece#ri`LQkJE zqU5<@H*@)Ay=AS#(@Mtj3kgVkAvvNhg>#lrc59(xV!zp2HZWKzC=0{>gu#`qrk(D3 z*@5OOb5RRHn(yp039h=rmq>fc?@Ez>z1Mw3v|YDD!+>y!n=Rgae!6tlxq3OW}>9LM8JPz@enGNg}qQ~@qdEh21HM9=s*033?{A#eT`Is9ifpviPhjpVDO4bLKv;@dUYPk;CitI!J^j|GO zkd=D%Au;MsV_1bMWrq5!^C0=qTQAL`TUV4~N!H)$$EbKXXh{a?9aPa zq42~L^DXvs;rHkl@0QeFW`jpCeQc!axtZ^{eN-9SLhAE_u4^Y?3){1D(M_3D_dpTZ z=qVyoexcE~)bp@Wjz{awJRemUCYrU+%$sKQGOVyo@%`-83>JpJqkuQN;@t_+wWWaN%1ko)?CjA8uOP zvda-RQMes3TnIL#LGlTww0hXbg8~A#*B;gvKlel8t*I)qp^eQP+2>aa+GSkSj{3w4 zcO`iykzdWa23C;mgU`{`FqRE}QH$98K-g)FtYmhO!|0G-sjdtWZR?|U{U7o9&(UJ? z{VaH2@{^@^Hu#fsa)H#wd=U z4Q`$1%WuB(a>G>Dmp&&xfjWunjh`RTor}8tOr%^&!5U>>yjfnl_|^Ug@8~l5{oH;n~Ogq*~(I89)st1{-{z2rjVOFw%2>L<4bn9leoBNaetmp zt@HD3T?bLq7yO|=g{r^M$&_&LpYI-Ae7NwF%AEZfp8&_3B#v!4ISFJPHLY9B0ee*9 z^n=9FpF+Oj@k`|qZ7|6e?k7D8&t;sO^`48uU*7z=a6g_rKIhx;)tMOjnND~$uzoCJ zWN6O!uy@jell*>}&7Y4F-~1F>bXu+2FQc(ml=O(h`yRgJ+QR+SV){A)(#qB0{dNkh zvGiz!bvJEXY~t}kMeZ;|mu8#$-;VZF8H^0c?dKa!K09~l&>n*z&(q%b){3qz!0VV+ zs3Kbi59;>9dH2tk+nA~k3cRG>xx%zE=P2yXg=>2Hf^%g$tc{QPVVVD~y`e!x7k*7} zs(b4YUssw$ao`T5bp^$JPm#-dzVwZ~%skGOY!G7Nm8rj$73y(-RJPCy>NAjPPxnGoAqDe9&? z7+8nV%#`etytmK()QjZtB|+I`t>sS1B0;wX$@n)vk>eiHm<4)=WwKMuS666V=;vmv zbXGW}&*gU@EVKC&Vi)vPbTWTQt5YWIEe7}rCII6W6_Yx07Ds$~r77fvQZeJ50JksG zKgpOdukgt$+4gYgRSq28OJ8LW^fQ2?%>17F@rG10-yA(skz@q(g^rLv^J94Ea$Va$ zJ^SxuQnGELlPUViCjgaX^S)Q5ru}E){h3zKLBr#h>%I<-?%Kn}H+;(2L)!h-Hb&(R&!Bne`!>~{yq~E973XWNEqcK99_7CqcTOFhJ2Li3~%)`)PN>Z zzW7=z_U~%6F)?la8G*uYP({k{as(tAXwXu^%6Pal0D+i;n{xiY`&UDMHwghj?vXv8 z-XNjgabfL9I5(J%XEfAWf8Go z_h(_LT!bB#*nF`oNO_0pv_vfV`$-Y8FoO2a*R^8Ba$a;7)oR?tr9O;Q@h8)A;c9Uw zR|ejmW*Hiu*^G;su8fUgm#^FQQI_ntdzP9kOEd~Nw2*!CXz;uP108PU>QY_e#bV1w z-%Zvbh`-;k3jbUldH9O)hqrI*B}0yiQ~PhaT0V=8tEIfB+Omh5zvPr4yS;1H7ZJfl zM^=o*87^u(1{;+0XzQK$yx3EFyvd^Kxyl;gc(=e5?7#e)K^zHF>EpiuJ zb>8{S_Pd)c1-Voc1G+hFdWSB^bMLSbok@6)l3s_E0RcR}Q=e&!QFr!k^k;+e zmF(0PcQffz`KQOD41^~u6mQ{d*Einu)+BTh12DQOled=PZswDfeVBSx>$GwM%K>Im zEr3gS>I*nbxz`y>{g2UTV3lk{CgZkJUWnxAE$;oy99$+?F}WZ;F7wg7uEak1yYtox zZfCwg5R+gxVCt%+2iJjo&PtnYSUVHfG2PJL?YnR~zEt>Y5?w%ZRs0=0*1?2h1GW=& zc^0xV0wptc^|q;#7_oO8)q^_R#!YaR6zclD^t!eQX1OI^Br%U<7n$L? z>FtJ+(;=BTy74^M!T0RJ;^KYQK*%DHCfRU!G!8^=9;V! z8AZ1_m+FZ(&lucnFt3*G8;)uyu@TtfKT9g=q;ci&%P8%+Z=%djW7PFjt}Ay@uOT>8 z&7}V88H3K~_XpW43k_(d`81Xldy4#XE|Dtiw9scJE4q9$ei&(9MbqEmqQNEH3;$eQ z_OVuN!E*AGbbF!6_PgOvZJKrRH$|>my^QXXbNi%vl)0~BcJxbmkrZ=@*O)=p=vUXc znHY^$W3`v1D7A~Oxx!u&5ML!RGzhKaotj4N!T)Xg*i9D z#Z@cej!Q1PX<18@!Hm@qe$1Ha%+D+um~2LIz};i8BR80UQi#9_o|f2OEJD4<&`THSAVUd64xqo_tTbk5p&@vrYpdq z_H|@K$t=39`SA<>m0ow6$WlG7>z`TCON)QEaNY*3wYJTRb5$$yQ72E;iL02vds!xf z?2G66QY9lC-~{0n*Q-jx4BP3{8lcN=$u_^;aEcZ^v=H>ki;0w)QRlW{ZxMy`Nl@nd3V5cfQX#lLLh#w|X3pX*h{OAF21N$Ewm zwPpD*N_yNb9!@~d+>uod)oqHCj>sJ?6<*S&v~?I7(vbe$ndkb#S-tyW=7*u9@Rc?f zSDN2#IO%@WWt@3eWbayA$5orvZT+J>L8_}jlCk8Mfn9NrlUk00T1emjTgWfTV)NJU z7FOSSd)f9*k^gLK>C$gu+j8#TZ`i%+&f5yRwTWW;w_E=eZOvc!DJSJe>figvA4qfR zI!!8%J>gvY`3rB^dZ(v5`IctZvT%idDpFY|M`DbE?cE{u7xc;ePM-RZ zo_^#$Q}olkQ?`wV?yjk}Tk3M^>)V3)?}|>_=H5BmHnVz0(VHT+Eq6f4qd#v;(e&NE znQ#8(O1*vhM7D7D&e_1hi$od3?%PxHKTIoPD+HLi?_p z8z%1kG)MBTzvb0!5TBm2Jqa{GK6m!6b;|>Hq~G{lG;cY_+DT^Lm=`^LS^Zk-v8em) z%GA||Jinct$Nv4+w8h2$8*Y>u*1Rb1yKB4mlEbyFHCuACq+`W5=|x{Ctz1;wwWfOB zqS;f$CzaaHtU6cw{o2!;Yi_&AaqWHwoLyX3X;2jIZk@HTdTHoGk>A^IZ`)Y?^d@iS z;`vGE-JUGG@b$}g!v}6o-4j5$^g=P~iRv|9?uu4j)Q;Q^O0y4_T;F(!f6@fe${G9g zzATLUzDw_k$KCVI@6B4H_52N&X6h%NU%us{|Bk;iGIw4u`z2N}FAtP*e_i9OD())GfSDU-0 zx3s%1mc`xB(lmEfO!l8>0IcDD?SAb9^vpW{DTk++ZMy@k;B==ipK)o+^a9@I`K=r9@?vsTRME45a@%HZBw%Tc4#_6Rq^W1N)x=`nF7g$>!^qaEowXiMw zyWPGIr|o_oFz>}amv#5@62Aw`STu2WqJ7)E-V@G=au&BYzE7Ue+Y*-9ssdb0v*Z6S zrj5W0M;KC^g(f{w`OMIv47|pPp@DytL<)ia`XF{-)3$%tuK)IrTm}BmE}tI+ L`njxgN@xNA*unU# literal 0 HcmV?d00001 diff --git a/lib/NeoPixelBus-2.2.9/keywords.txt b/lib/NeoPixelBus-2.5.0.09/keywords.txt similarity index 54% rename from lib/NeoPixelBus-2.2.9/keywords.txt rename to lib/NeoPixelBus-2.5.0.09/keywords.txt index 75771ef97..4734aca32 100644 --- a/lib/NeoPixelBus-2.2.9/keywords.txt +++ b/lib/NeoPixelBus-2.5.0.09/keywords.txt @@ -20,25 +20,105 @@ NeoBrgFeature KEYWORD1 NeoRbgFeature KEYWORD1 DotStarBgrFeature KEYWORD1 DotStarLbgrFeature KEYWORD1 -NeoWs2813Method KEYWORD1 Neo800KbpsMethod KEYWORD1 Neo400KbpsMethod KEYWORD1 -NeoAvrWs2813Method KEYWORD1 -NeoAvr800KbpsMethod KEYWORD1 -NeoAvr400KbpsMethod KEYWORD1 -NeoEsp8266DmaWs2813Method KEYWORD1 +NeoWs2813Method KEYWORD1 +NeoWs2812xMethod KEYWORD1 +NeoWs2812Method KEYWORD1 +NeoSk6812Method KEYWORD1 +NeoLc8812Method KEYWORD1 +NeoApa106Method KEYWORD1 +NeoEsp8266DmaWs2812xMethod KEYWORD1 +NeoEsp8266DmaSk6812Method KEYWORD1 +NeoEsp8266DmaApa106Method KEYWORD1 NeoEsp8266Dma800KbpsMethod KEYWORD1 NeoEsp8266Dma400KbpsMethod KEYWORD1 -NeoEsp8266UartWs2813Method KEYWORD1 -NeoEsp8266Uart800KbpsMethod KEYWORD1 -NeoEsp8266Uart400KbpsMethod KEYWORD1 -NeoEsp8266AsyncUartWs2813Method KEYWORD1 -NeoEsp8266AsyncUart800KbpsMethod KEYWORD1 -NeoEsp8266AsyncUart400KbpsMethod KEYWORD1 +NeoEsp8266Uart0Ws2813Method KEYWORD1 +NeoEsp8266Uart0Ws2812xMethod KEYWORD1 +NeoEsp8266Uart0Ws2812Method KEYWORD1 +NeoEsp8266Uart0Sk6812Method KEYWORD1 +NeoEsp8266Uart0Lc8812Method KEYWORD1 +NeoEsp8266Uart0Apa106Method KEYWORD1 +NeoEsp8266Uart0800KbpsMethod KEYWORD1 +NeoEsp8266Uart0400KbpsMethod KEYWORD1 +NeoEsp8266AsyncUart0Ws2813Method KEYWORD1 +NeoEsp8266AsyncUart0Ws2812xMethod KEYWORD1 +NeoEsp8266AsyncUart0Ws2812Method KEYWORD1 +NeoEsp8266AsyncUart0Sk6812Method KEYWORD1 +NeoEsp8266AsyncUart0Lc8812Method KEYWORD1 +NeoEsp8266AsyncUart0Apa106Method KEYWORD1 +NeoEsp8266AsyncUart0800KbpsMethod KEYWORD1 +NeoEsp8266AsyncUart0400KbpsMethod KEYWORD1 +NeoEsp8266Uart1Ws2813Method KEYWORD1 +NeoEsp8266Uart1Ws2812xMethod KEYWORD1 +NeoEsp8266Uart1Ws2812Method KEYWORD1 +NeoEsp8266Uart1Sk6812Method KEYWORD1 +NeoEsp8266Uart1Lc8812Method KEYWORD1 +NeoEsp8266Uart1Apa106Method KEYWORD1 +NeoEsp8266Uart1800KbpsMethod KEYWORD1 +NeoEsp8266Uart1400KbpsMethod KEYWORD1 +NeoEsp8266AsyncUart1Ws2813Method KEYWORD1 +NeoEsp8266AsyncUart1Ws2812xMethod KEYWORD1 +NeoEsp8266AsyncUart1Ws2812Method KEYWORD1 +NeoEsp8266AsyncUart1Sk6812Method KEYWORD1 +NeoEsp8266AsyncUart1Lc8812Method KEYWORD1 +NeoEsp8266AsyncUart1Apa106Method KEYWORD1 +NeoEsp8266AsyncUart1800KbpsMethod KEYWORD1 +NeoEsp8266AsyncUart1400KbpsMethod KEYWORD1 NeoEsp8266BitBangWs2813Method KEYWORD1 +NeoEsp8266BitBangWs2812xMethod KEYWORD1 +NeoEsp8266BitBangWs2812Method KEYWORD1 +NeoEsp8266BitBangSk6812Method KEYWORD1 +NeoEsp8266BitBangLc8812Method KEYWORD1 +NeoEsp8266BitBangApa106Method KEYWORD1 NeoEsp8266BitBang800KbpsMethod KEYWORD1 NeoEsp8266BitBang400KbpsMethod KEYWORD1 +NeoEsp32Rmt0Ws2812xMethod KEYWORD1 +NeoEsp32Rmt0Sk6812Method KEYWORD1 +NeoEsp32Rmt0Apa106Method KEYWORD1 +NeoEsp32Rmt0800KbpsMethod KEYWORD1 +NeoEsp32Rmt0400KbpsMethod KEYWORD1 +NeoEsp32Rmt1Ws2812xMethod KEYWORD1 +NeoEsp32Rmt1Sk6812Method KEYWORD1 +NeoEsp32Rmt1Apa106Method KEYWORD1 +NeoEsp32Rmt1800KbpsMethod KEYWORD1 +NeoEsp32Rmt1400KbpsMethod KEYWORD1 +NeoEsp32Rmt2Ws2812xMethod KEYWORD1 +NeoEsp32Rmt2Sk6812Method KEYWORD1 +NeoEsp32Rmt2Apa106Method KEYWORD1 +NeoEsp32Rmt2800KbpsMethod KEYWORD1 +NeoEsp32Rmt2400KbpsMethod KEYWORD1 +NeoEsp32Rmt3Ws2812xMethod KEYWORD1 +NeoEsp32Rmt3Sk6812Method KEYWORD1 +NeoEsp32Rmt3Apa106Method KEYWORD1 +NeoEsp32Rmt3800KbpsMethod KEYWORD1 +NeoEsp32Rmt3400KbpsMethod KEYWORD1 +NeoEsp32Rmt4Ws2812xMethod KEYWORD1 +NeoEsp32Rmt4Sk6812Method KEYWORD1 +NeoEsp32Rmt4Apa106Method KEYWORD1 +NeoEsp32Rmt4800KbpsMethod KEYWORD1 +NeoEsp32Rmt4400KbpsMethod KEYWORD1 +NeoEsp32Rmt5Ws2812xMethod KEYWORD1 +NeoEsp32Rmt5Sk6812Method KEYWORD1 +NeoEsp32Rmt5Apa106Method KEYWORD1 +NeoEsp32Rmt5800KbpsMethod KEYWORD1 +NeoEsp32Rmt5400KbpsMethod KEYWORD1 +NeoEsp32Rmt6Ws2812xMethod KEYWORD1 +NeoEsp32Rmt6Sk6812Method KEYWORD1 +NeoEsp32Rmt6Apa106Method KEYWORD1 +NeoEsp32Rmt6800KbpsMethod KEYWORD1 +NeoEsp32Rmt6400KbpsMethod KEYWORD1 +NeoEsp32Rmt7Ws2812xMethod KEYWORD1 +NeoEsp32Rmt7Sk6812Method KEYWORD1 +NeoEsp32Rmt7Apa106Method KEYWORD1 +NeoEsp32Rmt7800KbpsMethod KEYWORD1 +NeoEsp32Rmt7400KbpsMethod KEYWORD1 NeoEsp32BitBangWs2813Method KEYWORD1 +NeoEsp32BitBangWs2812xMethod KEYWORD1 +NeoEsp32BitBangWs2812Method KEYWORD1 +NeoEsp32BitBangSk6812Method KEYWORD1 +NeoEsp32BitBangLc8812Method KEYWORD1 +NeoEsp32BitBangApa106Method KEYWORD1 NeoEsp32BitBang800KbpsMethod KEYWORD1 NeoEsp32BitBang400KbpsMethod KEYWORD1 DotStarMethod KEYWORD1 @@ -105,6 +185,7 @@ PixelsSize KEYWORD2 PixelCount KEYWORD2 SetPixelColor KEYWORD2 GetPixelColor KEYWORD2 +SwapPixelColor KEYWORD2 CalculateBrightness KEYWORD2 Darken KEYWORD2 Lighten KEYWORD2 @@ -117,6 +198,7 @@ StopAnimation KEYWORD2 RestartAnimation KEYWORD2 IsAnimationActive KEYWORD2 AnimationDuration KEYWORD2 +ChangeAnimationDuration KEYWORD2 UpdateAnimations KEYWORD2 IsPaused KEYWORD2 Pause KEYWORD2 @@ -126,29 +208,38 @@ setTimeScale KEYWORD2 QuadraticIn KEYWORD2 QuadraticOut KEYWORD2 QuadraticInOut KEYWORD2 +QuadraticCenter KEYWORD2 CubicIn KEYWORD2 CubicOut KEYWORD2 CubicInOut KEYWORD2 +CubicCenter KEYWORD2 QuarticIn KEYWORD2 QuarticOut KEYWORD2 QuarticInOut KEYWORD2 +QuarticCenter KEYWORD2 QuinticIn KEYWORD2 QuinticOut KEYWORD2 QuinticInOut KEYWORD2 +QuinticCenter KEYWORD2 SinusoidalIn KEYWORD2 SinusoidalOut KEYWORD2 SinusoidalInOut KEYWORD2 +SinusoidalCenter KEYWORD2 ExponentialIn KEYWORD2 ExponentialOut KEYWORD2 ExponentialInOut KEYWORD2 +ExponentialCenter KEYWORD2 CircularIn KEYWORD2 CircularOut KEYWORD2 CircularInOut KEYWORD2 +CircularCenter KEYWORD2 Gamma KEYWORD2 Map KEYWORD2 MapProbe KEYWORD2 getWidth KEYWORD2 getHeight KEYWORD2 +RingPixelShift KEYWORD2 +RingPixelRotate KEYWORD2 getCountOfRings KEYWORD2 getPixelCountAtRing KEYWORD2 getPixelCount KEYWORD2 diff --git a/lib/NeoPixelBus-2.5.0.09/library.json b/lib/NeoPixelBus-2.5.0.09/library.json new file mode 100644 index 000000000..76cb53848 --- /dev/null +++ b/lib/NeoPixelBus-2.5.0.09/library.json @@ -0,0 +1,14 @@ +{ + "name": "NeoPixelBus", + "keywords": "NeoPixel, WS2811, WS2812, WS2813, SK6812, DotStar, APA102, RGB, RGBW", + "description": "A library that makes controlling NeoPixels (WS2811, WS2812, WS2813 & SK6812) and DotStars (APA102) easy. Supports most Arduino platforms. Support for RGBW pixels. Includes seperate RgbColor, RgbwColor, HslColor, and HsbColor objects. Includes an animator class that helps create asyncronous animations. For Esp8266 it has three methods of sending NeoPixel data, DMA, UART, and Bit Bang. For Esp32 it has two base methods of sending NeoPixel data, i2s and RMT. For all platforms, there are two methods of sending DotStar data, hardware SPI and software SPI.", + "homepage": "https://github.com/Makuna/NeoPixelBus/wiki", + "repository": { + "type": "git", + "url": "https://github.com/Makuna/NeoPixelBus" + }, + "version": "2.5.0", + "frameworks": "arduino", + "platforms": "*" +} + diff --git a/lib/NeoPixelBus-2.2.9/library.properties b/lib/NeoPixelBus-2.5.0.09/library.properties similarity index 77% rename from lib/NeoPixelBus-2.2.9/library.properties rename to lib/NeoPixelBus-2.5.0.09/library.properties index 029a6db39..5cac2ca18 100644 --- a/lib/NeoPixelBus-2.2.9/library.properties +++ b/lib/NeoPixelBus-2.5.0.09/library.properties @@ -1,9 +1,9 @@ name=NeoPixelBus by Makuna -version=2.2.9 +version=2.5.0 author=Michael C. Miller (makuna@live.com) maintainer=Michael C. Miller (makuna@live.com) sentence=A library that makes controlling NeoPixels (WS2811, WS2812, WS2813 & SK6812) and DotStars (APA102) easy. -paragraph=Supports most Arduino platforms, including Esp8266 and Esp32. Support for RGBW pixels. Includes seperate RgbColor, RgbwColor, HslColor, and HsbColor objects. Includes an animator class that helps create asyncronous animations. Supports Matrix layout of pixels. Includes Gamma corretion object. For Esp8266 it has three methods of sending NeoPixel data, DMA, UART, and Bit Bang; and two methods of sending DotStar data, hardware SPI and software SPI. +paragraph=Supports most Arduino platforms, including Esp8266 and Esp32. Support for RGBW pixels. Includes seperate RgbColor, RgbwColor, HslColor, and HsbColor objects. Includes an animator class that helps create asyncronous animations. Supports Matrix layout of pixels. Includes Gamma corretion object. For Esp8266 it has three methods of sending NeoPixel data, DMA, UART, and Bit Bang. For Esp32 it has two base methods of sending NeoPixel data, i2s and RMT. For all platforms, there are two methods of sending DotStar data, hardware SPI and software SPI. category=Display url=https://github.com/Makuna/NeoPixelBus/wiki architectures=* \ No newline at end of file diff --git a/lib/NeoPixelBus-2.2.9/src/NeoPixelAnimator.h b/lib/NeoPixelBus-2.5.0.09/src/NeoPixelAnimator.h similarity index 95% rename from lib/NeoPixelBus-2.2.9/src/NeoPixelAnimator.h rename to lib/NeoPixelBus-2.5.0.09/src/NeoPixelAnimator.h index 21eb2ad8b..c49d9ec48 100644 --- a/lib/NeoPixelBus-2.2.9/src/NeoPixelAnimator.h +++ b/lib/NeoPixelBus-2.5.0.09/src/NeoPixelAnimator.h @@ -109,6 +109,8 @@ public: return _animations[indexAnimation]._duration; } + void ChangeAnimationDuration(uint16_t indexAnimation, uint16_t newDuration); + void UpdateAnimations(); bool IsPaused() @@ -159,6 +161,11 @@ private: _remaining = 0; } + float CurrentProgress() + { + return (float)(_duration - _remaining) / (float)_duration; + } + uint16_t _duration; uint16_t _remaining; diff --git a/lib/NeoPixelBus-2.2.9/src/NeoPixelBrightnessBus.h b/lib/NeoPixelBus-2.5.0.09/src/NeoPixelBrightnessBus.h similarity index 100% rename from lib/NeoPixelBus-2.2.9/src/NeoPixelBrightnessBus.h rename to lib/NeoPixelBus-2.5.0.09/src/NeoPixelBrightnessBus.h diff --git a/lib/NeoPixelBus-2.2.9/src/NeoPixelBus.h b/lib/NeoPixelBus-2.5.0.09/src/NeoPixelBus.h similarity index 94% rename from lib/NeoPixelBus-2.2.9/src/NeoPixelBus.h rename to lib/NeoPixelBus-2.5.0.09/src/NeoPixelBus.h index f6a6b7bab..3c08f83f6 100644 --- a/lib/NeoPixelBus-2.2.9/src/NeoPixelBus.h +++ b/lib/NeoPixelBus-2.5.0.09/src/NeoPixelBus.h @@ -57,8 +57,9 @@ License along with NeoPixel. If not, see #include "internal/NeoBufferMethods.h" #include "internal/NeoBuffer.h" #include "internal/NeoSpriteSheet.h" -#include "internal/NeoBitmapFile.h" #include "internal/NeoDib.h" +#include "internal/NeoBitmapFile.h" + #include "internal/NeoEase.h" #include "internal/NeoGamma.h" @@ -71,6 +72,8 @@ License along with NeoPixel. If not, see #elif defined(ARDUINO_ARCH_ESP32) +#include "internal/NeoEsp32I2sMethod.h" +#include "internal/NeoEsp32RmtMethod.h" #include "internal/NeoEspBitBangMethod.h" #include "internal/DotStarGenericMethod.h" @@ -136,14 +139,21 @@ public: Dirty(); } - void Show() + // used by DotStartSpiMethod if pins can be configured + void Begin(int8_t sck, int8_t miso, int8_t mosi, int8_t ss) + { + _method.Initialize(sck, miso, mosi, ss); + Dirty(); + } + + void Show(bool maintainBufferConsistency = true) { if (!IsDirty()) { return; } - _method.Update(); + _method.Update(maintainBufferConsistency); ResetDirty(); } @@ -321,7 +331,14 @@ public: } } + void SwapPixelColor(uint16_t indexPixelOne, uint16_t indexPixelTwo) + { + auto colorOne = GetPixelColor(indexPixelOne); + auto colorTwo = GetPixelColor(indexPixelTwo); + SetPixelColor(indexPixelOne, colorTwo); + SetPixelColor(indexPixelTwo, colorOne); + }; protected: const uint16_t _countPixels; // Number of RGB LEDs in strip diff --git a/lib/NeoPixelBus-2.2.9/src/internal/DotStarAvrMethod.h b/lib/NeoPixelBus-2.5.0.09/src/internal/DotStarAvrMethod.h similarity index 99% rename from lib/NeoPixelBus-2.2.9/src/internal/DotStarAvrMethod.h rename to lib/NeoPixelBus-2.5.0.09/src/internal/DotStarAvrMethod.h index 66bd00b0b..a627d0a68 100644 --- a/lib/NeoPixelBus-2.2.9/src/internal/DotStarAvrMethod.h +++ b/lib/NeoPixelBus-2.5.0.09/src/internal/DotStarAvrMethod.h @@ -68,7 +68,7 @@ public: digitalWrite(_pinData, LOW); } - void Update() + void Update(bool) { // start frame for (int startFrameByte = 0; startFrameByte < 4; startFrameByte++) diff --git a/lib/NeoPixelBus-2.2.9/src/internal/DotStarColorFeatures.h b/lib/NeoPixelBus-2.5.0.09/src/internal/DotStarColorFeatures.h similarity index 52% rename from lib/NeoPixelBus-2.2.9/src/internal/DotStarColorFeatures.h rename to lib/NeoPixelBus-2.5.0.09/src/internal/DotStarColorFeatures.h index 3719d6fc1..50a33e248 100644 --- a/lib/NeoPixelBus-2.2.9/src/internal/DotStarColorFeatures.h +++ b/lib/NeoPixelBus-2.5.0.09/src/internal/DotStarColorFeatures.h @@ -324,3 +324,332 @@ public: }; +/* RGB Feature -- Some APA102s ship in RGB order */ +class DotStarRgbFeature : public DotStar3Elements +{ +public: + static void applyPixelColor(uint8_t* pPixels, uint16_t indexPixel, ColorObject color) + { + uint8_t* p = getPixelAddress(pPixels, indexPixel); + + *p++ = 0xff; // upper three bits are always 111 and brightness at max + *p++ = color.R; + *p++ = color.G; + *p = color.B; + } + + static ColorObject retrievePixelColor(uint8_t* pPixels, uint16_t indexPixel) + { + ColorObject color; + uint8_t* p = getPixelAddress(pPixels, indexPixel); + + p++; // ignore the first byte + color.R = *p++; + color.G = *p++; + color.B = *p; + + return color; + } + + static ColorObject retrievePixelColor_P(PGM_VOID_P pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress((const uint8_t*)pPixels, indexPixel); + + pgm_read_byte(p++); // ignore the first byte + color.R = pgm_read_byte(p++); + color.G = pgm_read_byte(p++); + color.B = pgm_read_byte(p); + + return color; + } + +}; + +class DotStarLrgbFeature : public DotStar4Elements +{ +public: + static void applyPixelColor(uint8_t* pPixels, uint16_t indexPixel, ColorObject color) + { + uint8_t* p = getPixelAddress(pPixels, indexPixel); + + *p++ = 0xE0 | (color.W < 31 ? color.W : 31); // upper three bits are always 111 + *p++ = color.R; + *p++ = color.G; + *p = color.B; + } + + static ColorObject retrievePixelColor(uint8_t* pPixels, uint16_t indexPixel) + { + ColorObject color; + uint8_t* p = getPixelAddress(pPixels, indexPixel); + + color.W = (*p++) & 0x1F; // mask out upper three bits + color.R = *p++; + color.G = *p++; + color.B = *p; + + return color; + } + + static ColorObject retrievePixelColor_P(PGM_VOID_P pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress((const uint8_t*)pPixels, indexPixel); + + color.W = pgm_read_byte(p++) & 0x1F; // mask out upper three bits + color.R = pgm_read_byte(p++); + color.G = pgm_read_byte(p++); + color.B = pgm_read_byte(p); + + return color; + } + +}; +/* RBG Feature -- Some APA102s ship in RBG order */ +class DotStarRbgFeature : public DotStar3Elements +{ +public: + static void applyPixelColor(uint8_t* pPixels, uint16_t indexPixel, ColorObject color) + { + uint8_t* p = getPixelAddress(pPixels, indexPixel); + + *p++ = 0xff; // upper three bits are always 111 and brightness at max + *p++ = color.R; + *p++ = color.B; + *p = color.G; + } + + static ColorObject retrievePixelColor(uint8_t* pPixels, uint16_t indexPixel) + { + ColorObject color; + uint8_t* p = getPixelAddress(pPixels, indexPixel); + + p++; // ignore the first byte + color.R = *p++; + color.B = *p++; + color.G = *p; + + return color; + } + + static ColorObject retrievePixelColor_P(PGM_VOID_P pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress((const uint8_t*)pPixels, indexPixel); + + pgm_read_byte(p++); // ignore the first byte + color.R = pgm_read_byte(p++); + color.B = pgm_read_byte(p++); + color.G = pgm_read_byte(p); + + return color; + } + +}; + +class DotStarLrbgFeature : public DotStar4Elements +{ +public: + static void applyPixelColor(uint8_t* pPixels, uint16_t indexPixel, ColorObject color) + { + uint8_t* p = getPixelAddress(pPixels, indexPixel); + + *p++ = 0xE0 | (color.W < 31 ? color.W : 31); // upper three bits are always 111 + *p++ = color.R; + *p++ = color.B; + *p = color.G; + } + + static ColorObject retrievePixelColor(uint8_t* pPixels, uint16_t indexPixel) + { + ColorObject color; + uint8_t* p = getPixelAddress(pPixels, indexPixel); + + color.W = (*p++) & 0x1F; // mask out upper three bits + color.R = *p++; + color.B = *p++; + color.G = *p; + + return color; + } + + static ColorObject retrievePixelColor_P(PGM_VOID_P pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress((const uint8_t*)pPixels, indexPixel); + + color.W = pgm_read_byte(p++) & 0x1F; // mask out upper three bits + color.R = pgm_read_byte(p++); + color.B = pgm_read_byte(p++); + color.G = pgm_read_byte(p); + + return color; + } + +}; + +/* GBR Feature -- Some APA102s ship in GBR order */ +class DotStarGbrFeature : public DotStar3Elements +{ +public: + static void applyPixelColor(uint8_t* pPixels, uint16_t indexPixel, ColorObject color) + { + uint8_t* p = getPixelAddress(pPixels, indexPixel); + + *p++ = 0xff; // upper three bits are always 111 and brightness at max + *p++ = color.G; + *p++ = color.B; + *p = color.R; + } + + static ColorObject retrievePixelColor(uint8_t* pPixels, uint16_t indexPixel) + { + ColorObject color; + uint8_t* p = getPixelAddress(pPixels, indexPixel); + + p++; // ignore the first byte + color.G = *p++; + color.B = *p++; + color.R = *p; + + return color; + } + + static ColorObject retrievePixelColor_P(PGM_VOID_P pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress((const uint8_t*)pPixels, indexPixel); + + pgm_read_byte(p++); // ignore the first byte + color.G = pgm_read_byte(p++); + color.B = pgm_read_byte(p++); + color.R = pgm_read_byte(p); + + return color; + } + +}; + +class DotStarLgbrFeature : public DotStar4Elements +{ +public: + static void applyPixelColor(uint8_t* pPixels, uint16_t indexPixel, ColorObject color) + { + uint8_t* p = getPixelAddress(pPixels, indexPixel); + + *p++ = 0xE0 | (color.W < 31 ? color.W : 31); // upper three bits are always 111 + *p++ = color.G; + *p++ = color.B; + *p = color.R; + } + + static ColorObject retrievePixelColor(uint8_t* pPixels, uint16_t indexPixel) + { + ColorObject color; + uint8_t* p = getPixelAddress(pPixels, indexPixel); + + color.W = (*p++) & 0x1F; // mask out upper three bits + color.G = *p++; + color.B = *p++; + color.R = *p; + + return color; + } + + static ColorObject retrievePixelColor_P(PGM_VOID_P pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress((const uint8_t*)pPixels, indexPixel); + + color.W = pgm_read_byte(p++) & 0x1F; // mask out upper three bits + color.G = pgm_read_byte(p++); + color.B = pgm_read_byte(p++); + color.R = pgm_read_byte(p); + + return color; + } + +}; +/* BRG Feature -- Some APA102s ship in BRG order */ +class DotStarBrgFeature : public DotStar3Elements +{ +public: + static void applyPixelColor(uint8_t* pPixels, uint16_t indexPixel, ColorObject color) + { + uint8_t* p = getPixelAddress(pPixels, indexPixel); + + *p++ = 0xff; // upper three bits are always 111 and brightness at max + *p++ = color.B; + *p++ = color.R; + *p = color.G; + } + + static ColorObject retrievePixelColor(uint8_t* pPixels, uint16_t indexPixel) + { + ColorObject color; + uint8_t* p = getPixelAddress(pPixels, indexPixel); + + p++; // ignore the first byte + color.B = *p++; + color.R = *p++; + color.G = *p; + + return color; + } + + static ColorObject retrievePixelColor_P(PGM_VOID_P pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress((const uint8_t*)pPixels, indexPixel); + + pgm_read_byte(p++); // ignore the first byte + color.B = pgm_read_byte(p++); + color.R = pgm_read_byte(p++); + color.G = pgm_read_byte(p); + + return color; + } + +}; + +class DotStarLbrgFeature : public DotStar4Elements +{ +public: + static void applyPixelColor(uint8_t* pPixels, uint16_t indexPixel, ColorObject color) + { + uint8_t* p = getPixelAddress(pPixels, indexPixel); + + *p++ = 0xE0 | (color.W < 31 ? color.W : 31); // upper three bits are always 111 + *p++ = color.B; + *p++ = color.R; + *p = color.G; + } + + static ColorObject retrievePixelColor(uint8_t* pPixels, uint16_t indexPixel) + { + ColorObject color; + uint8_t* p = getPixelAddress(pPixels, indexPixel); + + color.W = (*p++) & 0x1F; // mask out upper three bits + color.B = *p++; + color.R = *p++; + color.G = *p; + + return color; + } + + static ColorObject retrievePixelColor_P(PGM_VOID_P pPixels, uint16_t indexPixel) + { + ColorObject color; + const uint8_t* p = getPixelAddress((const uint8_t*)pPixels, indexPixel); + + color.W = pgm_read_byte(p++) & 0x1F; // mask out upper three bits + color.B = pgm_read_byte(p++); + color.R = pgm_read_byte(p++); + color.G = pgm_read_byte(p); + + return color; + } + +}; diff --git a/lib/NeoPixelBus-2.2.9/src/internal/DotStarGenericMethod.h b/lib/NeoPixelBus-2.5.0.09/src/internal/DotStarGenericMethod.h similarity index 99% rename from lib/NeoPixelBus-2.2.9/src/internal/DotStarGenericMethod.h rename to lib/NeoPixelBus-2.5.0.09/src/internal/DotStarGenericMethod.h index d59d0e0dd..8b3fe3350 100644 --- a/lib/NeoPixelBus-2.2.9/src/internal/DotStarGenericMethod.h +++ b/lib/NeoPixelBus-2.5.0.09/src/internal/DotStarGenericMethod.h @@ -60,7 +60,7 @@ public: digitalWrite(_pinData, LOW); } - void Update() + void Update(bool) { // start frame for (int startFrameByte = 0; startFrameByte < 4; startFrameByte++) diff --git a/lib/NeoPixelBus-2.2.9/src/internal/DotStarSpiMethod.h b/lib/NeoPixelBus-2.5.0.09/src/internal/DotStarSpiMethod.h similarity index 81% rename from lib/NeoPixelBus-2.2.9/src/internal/DotStarSpiMethod.h rename to lib/NeoPixelBus-2.5.0.09/src/internal/DotStarSpiMethod.h index 4aabc29c2..7c51816a1 100644 --- a/lib/NeoPixelBus-2.2.9/src/internal/DotStarSpiMethod.h +++ b/lib/NeoPixelBus-2.5.0.09/src/internal/DotStarSpiMethod.h @@ -51,29 +51,37 @@ public: return true; // dot stars don't have a required delay } +#if defined(ARDUINO_ARCH_ESP32) + void Initialize(int8_t sck, int8_t miso, int8_t mosi, int8_t ss) + { + SPI.begin(sck, miso, mosi, ss); + } +#endif + void Initialize() { SPI.begin(); - -#if defined(ARDUINO_ARCH_ESP8266) - SPI.setFrequency(20000000L); -#elif defined(ARDUINO_ARCH_AVR) - SPI.setClockDivider(SPI_CLOCK_DIV2); // 8 MHz (6 MHz on Pro Trinket 3V) -#else - SPI.setClockDivider((F_CPU + 4000000L) / 8000000L); // 8-ish MHz on Due -#endif - SPI.setBitOrder(MSBFIRST); - SPI.setDataMode(SPI_MODE0); } - void Update() + void Update(bool) { - // due to API inconsistencies need to call different methods on SPI + SPI.beginTransaction(SPISettings(20000000L, MSBFIRST, SPI_MODE0)); #if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) + // ESPs have a method to write without inplace overwriting the send buffer + // since we don't care what gets received, use it for performance SPI.writeBytes(_sendBuffer, _sizeSendBuffer); + #else - SPI.transfer(_sendBuffer, _sizeSendBuffer); + // default ARDUINO transfer inplace overwrites the send buffer + // which is bad, so we have to send one byte at a time + uint8_t* out = _sendBuffer; + uint8_t* end = out + _sizeSendBuffer; + while (out < end) + { + SPI.transfer(*out++); + } #endif + SPI.endTransaction(); } uint8_t* getPixels() const diff --git a/lib/NeoPixelBus-2.5.0.09/src/internal/Esp32_i2s.c b/lib/NeoPixelBus-2.5.0.09/src/internal/Esp32_i2s.c new file mode 100644 index 000000000..092170e20 --- /dev/null +++ b/lib/NeoPixelBus-2.5.0.09/src/internal/Esp32_i2s.c @@ -0,0 +1,484 @@ +// WARNING: This file contains code that is more than likely already +// exposed from the Esp32 Arduino API. It will be removed once integration is complete. +// +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#if defined(ARDUINO_ARCH_ESP32) + +#include +#include +#include "stdlib.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "freertos/queue.h" + +#include "esp_intr.h" +#include "rom/ets_sys.h" +#include "soc/gpio_reg.h" +#include "soc/gpio_sig_map.h" +#include "soc/io_mux_reg.h" +#include "soc/rtc_cntl_reg.h" +#include "soc/i2s_struct.h" +#include "soc/dport_reg.h" +#include "soc/sens_reg.h" +#include "driver/gpio.h" +#include "driver/i2s.h" +#include "driver/dac.h" +#include "Esp32_i2s.h" +#include "esp32-hal.h" + +#define I2S_BASE_CLK (160000000L) +#define ESP32_REG(addr) (*((volatile uint32_t*)(0x3FF00000+(addr)))) + +#define I2S_DMA_QUEUE_SIZE 16 + +#define I2S_DMA_SILENCE_LEN 256 // bytes + +typedef struct i2s_dma_item_s { + uint32_t blocksize: 12; // datalen + uint32_t datalen : 12; // len*(bits_per_sample/8)*2 => max 2047*8bit/1023*16bit samples + uint32_t unused : 5; // 0 + uint32_t sub_sof : 1; // 0 + uint32_t eof : 1; // 1 => last? + uint32_t owner : 1; // 1 + + void* data; // malloc(datalen) + struct i2s_dma_item_s* next; + + // if this pointer is not null, it will be freed + void* free_ptr; + + // if DMA buffers are preallocated + uint8_t* buf; +} i2s_dma_item_t; + +typedef struct { + i2s_dev_t* bus; + int8_t ws; + int8_t bck; + int8_t out; + int8_t in; + uint32_t rate; + intr_handle_t isr_handle; + xQueueHandle tx_queue; + + uint8_t* silence_buf; + size_t silence_len; + + i2s_dma_item_t* dma_items; + size_t dma_count; + uint32_t dma_buf_len :12; + uint32_t unused :20; +} i2s_bus_t; + +static uint8_t i2s_silence_buf[I2S_DMA_SILENCE_LEN]; + +static i2s_bus_t I2S[2] = { + {&I2S0, -1, -1, -1, -1, 0, NULL, NULL, i2s_silence_buf, I2S_DMA_SILENCE_LEN, NULL, I2S_DMA_QUEUE_SIZE, 0, 0}, + {&I2S1, -1, -1, -1, -1, 0, NULL, NULL, i2s_silence_buf, I2S_DMA_SILENCE_LEN, NULL, I2S_DMA_QUEUE_SIZE, 0, 0} +}; + +void IRAM_ATTR i2sDmaISR(void* arg); +bool i2sInitDmaItems(uint8_t bus_num); + +bool i2sInitDmaItems(uint8_t bus_num) { + if (bus_num > 1) { + return false; + } + if (I2S[bus_num].tx_queue) {// already set + return true; + } + + if (I2S[bus_num].dma_items == NULL) { + I2S[bus_num].dma_items = (i2s_dma_item_t*)(malloc(I2S[bus_num].dma_count* sizeof(i2s_dma_item_t))); + if (I2S[bus_num].dma_items == NULL) { + log_e("MEM ERROR!"); + return false; + } + } + + int i, i2, a; + i2s_dma_item_t* item; + + for(i=0; ieof = 1; + item->owner = 1; + item->sub_sof = 0; + item->unused = 0; + item->data = I2S[bus_num].silence_buf; + item->blocksize = I2S[bus_num].silence_len; + item->datalen = I2S[bus_num].silence_len; + item->next = &I2S[bus_num].dma_items[i2]; + item->free_ptr = NULL; + if (I2S[bus_num].dma_buf_len) { + item->buf = (uint8_t*)(malloc(I2S[bus_num].dma_buf_len)); + if (item->buf == NULL) { + log_e("MEM ERROR!"); + for(a=0; abuf = NULL; + } + } + + I2S[bus_num].tx_queue = xQueueCreate(I2S[bus_num].dma_count, sizeof(i2s_dma_item_t*)); + if (I2S[bus_num].tx_queue == NULL) {// memory error + log_e("MEM ERROR!"); + free(I2S[bus_num].dma_items); + I2S[bus_num].dma_items = NULL; + return false; + } + return true; +} + +void i2sSetSilenceBuf(uint8_t bus_num, uint8_t* data, size_t len) { + if (bus_num > 1 || !data || !len) { + return; + } + I2S[bus_num].silence_buf = data; + I2S[bus_num].silence_len = len; +} + +esp_err_t i2sSetClock(uint8_t bus_num, uint8_t div_num, uint8_t div_b, uint8_t div_a, uint8_t bck, uint8_t bits) { + if (bus_num > 1 || div_a > 63 || div_b > 63 || bck > 63) { + return ESP_FAIL; + } + i2s_dev_t* i2s = I2S[bus_num].bus; + i2s->clkm_conf.clka_en = 0; + i2s->clkm_conf.clkm_div_a = div_a; + i2s->clkm_conf.clkm_div_b = div_b; + i2s->clkm_conf.clkm_div_num = div_num; + i2s->sample_rate_conf.tx_bck_div_num = bck; + i2s->sample_rate_conf.rx_bck_div_num = bck; + i2s->sample_rate_conf.tx_bits_mod = bits; + i2s->sample_rate_conf.rx_bits_mod = bits; + return ESP_OK; +} + +void i2sSetTxDataMode(uint8_t bus_num, i2s_tx_chan_mod_t chan_mod, i2s_tx_fifo_mod_t fifo_mod) { + if (bus_num > 1) { + return; + } + + I2S[bus_num].bus->conf_chan.tx_chan_mod = chan_mod; // 0:dual channel; 1:right channel; 2:left channel; 3:left channel constant; 4:right channel constant; (channels flipped if tx_msb_right == 1) + I2S[bus_num].bus->fifo_conf.tx_fifo_mod = fifo_mod; // 0:16-bit dual channel; 1:16-bit single channel; 2:32-bit dual channel; 3:32-bit single channel data +} + +void i2sSetDac(uint8_t bus_num, bool right, bool left) { + if (bus_num > 1) { + return; + } + + if (!right && !left) { + dac_output_disable(1); + dac_output_disable(2); + dac_i2s_disable(); + I2S[bus_num].bus->conf2.lcd_en = 0; + I2S[bus_num].bus->conf.tx_right_first = 0; + I2S[bus_num].bus->conf2.camera_en = 0; + I2S[bus_num].bus->conf.tx_msb_shift = 1;// I2S signaling + return; + } + + i2sSetPins(bus_num, -1, -1, -1, -1); + I2S[bus_num].bus->conf2.lcd_en = 1; + I2S[bus_num].bus->conf.tx_right_first = 0; + I2S[bus_num].bus->conf2.camera_en = 0; + I2S[bus_num].bus->conf.tx_msb_shift = 0; + dac_i2s_enable(); + + if (right) {// DAC1, right channel, GPIO25 + dac_output_enable(1); + } + if (left) { // DAC2, left channel, GPIO26 + dac_output_enable(2); + } +} + +void i2sSetPins(uint8_t bus_num, int8_t out, int8_t ws, int8_t bck, int8_t in) { + if (bus_num > 1) { + return; + } + + if ((ws >= 0 && I2S[bus_num].ws == -1) || (bck >= 0 && I2S[bus_num].bck == -1) || (out >= 0 && I2S[bus_num].out == -1)) { + i2sSetDac(bus_num, false, false); + } + + if (ws >= 0) { + if (I2S[bus_num].ws != ws) { + if (I2S[bus_num].ws >= 0) { + gpio_matrix_out(I2S[bus_num].ws, 0x100, false, false); + } + I2S[bus_num].ws = ws; + pinMode(ws, OUTPUT); + gpio_matrix_out(ws, bus_num?I2S1O_WS_OUT_IDX:I2S0O_WS_OUT_IDX, false, false); + } + } else if (I2S[bus_num].ws >= 0) { + gpio_matrix_out(I2S[bus_num].ws, 0x100, false, false); + I2S[bus_num].ws = -1; + } + + if (bck >= 0) { + if (I2S[bus_num].bck != bck) { + if (I2S[bus_num].bck >= 0) { + gpio_matrix_out(I2S[bus_num].bck, 0x100, false, false); + } + I2S[bus_num].bck = bck; + pinMode(bck, OUTPUT); + gpio_matrix_out(bck, bus_num?I2S1O_BCK_OUT_IDX:I2S0O_BCK_OUT_IDX, false, false); + } + } else if (I2S[bus_num].bck >= 0) { + gpio_matrix_out(I2S[bus_num].bck, 0x100, false, false); + I2S[bus_num].bck = -1; + } + + if (out >= 0) { + if (I2S[bus_num].out != out) { + if (I2S[bus_num].out >= 0) { + gpio_matrix_out(I2S[bus_num].out, 0x100, false, false); + } + I2S[bus_num].out = out; + pinMode(out, OUTPUT); + gpio_matrix_out(out, bus_num?I2S1O_DATA_OUT23_IDX:I2S0O_DATA_OUT23_IDX, false, false); + } + } else if (I2S[bus_num].out >= 0) { + gpio_matrix_out(I2S[bus_num].out, 0x100, false, false); + I2S[bus_num].out = -1; + } + +} + +bool i2sWriteDone(uint8_t bus_num) { + if (bus_num > 1) { + return false; + } + return (I2S[bus_num].dma_items[I2S[bus_num].dma_count - 1].data == I2S[bus_num].silence_buf); +} + +void i2sInit(uint8_t bus_num, uint32_t bits_per_sample, uint32_t sample_rate, i2s_tx_chan_mod_t chan_mod, i2s_tx_fifo_mod_t fifo_mod, size_t dma_count, size_t dma_len) { + if (bus_num > 1) { + return; + } + + I2S[bus_num].dma_count = dma_count; + I2S[bus_num].dma_buf_len = dma_len & 0xFFF; + + if (!i2sInitDmaItems(bus_num)) { + return; + } + + if (bus_num) { + periph_module_enable(PERIPH_I2S1_MODULE); + } else { + periph_module_enable(PERIPH_I2S0_MODULE); + } + + esp_intr_disable(I2S[bus_num].isr_handle); + i2s_dev_t* i2s = I2S[bus_num].bus; + i2s->out_link.stop = 1; + i2s->conf.tx_start = 0; + i2s->int_ena.val = 0; + i2s->int_clr.val = 0xFFFFFFFF; + i2s->fifo_conf.dscr_en = 0; + + // reset fifo + i2s->conf.rx_fifo_reset = 1; + i2s->conf.rx_fifo_reset = 0; + i2s->conf.tx_fifo_reset = 1; + i2s->conf.tx_fifo_reset = 0; + + // reset i2s + i2s->conf.tx_reset = 1; + i2s->conf.tx_reset = 0; + i2s->conf.rx_reset = 1; + i2s->conf.rx_reset = 0; + + // reset dma + i2s->lc_conf.in_rst = 1; + i2s->lc_conf.in_rst = 0; + i2s->lc_conf.out_rst = 1; + i2s->lc_conf.out_rst = 0; + + // Enable and configure DMA + i2s->lc_conf.check_owner = 0; + i2s->lc_conf.out_loop_test = 0; + i2s->lc_conf.out_auto_wrback = 0; + i2s->lc_conf.out_data_burst_en = 0; + i2s->lc_conf.outdscr_burst_en = 0; + i2s->lc_conf.out_no_restart_clr = 0; + i2s->lc_conf.indscr_burst_en = 0; + i2s->lc_conf.out_eof_mode = 1; + + i2s->pdm_conf.pcm2pdm_conv_en = 0; + i2s->pdm_conf.pdm2pcm_conv_en = 0; + // SET_PERI_REG_BITS(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL, 0x1, RTC_CNTL_SOC_CLK_SEL_S); + + + i2s->conf_chan.tx_chan_mod = chan_mod; // 0-two channel;1-right;2-left;3-righ;4-left + i2s->conf_chan.rx_chan_mod = chan_mod; // 0-two channel;1-right;2-left;3-righ;4-left + i2s->fifo_conf.tx_fifo_mod = fifo_mod; // 0-right&left channel;1-one channel + i2s->fifo_conf.rx_fifo_mod = fifo_mod; // 0-right&left channel;1-one channel + + i2s->conf.tx_mono = 0; + i2s->conf.rx_mono = 0; + + i2s->conf.tx_start = 0; + i2s->conf.rx_start = 0; + + i2s->conf.tx_short_sync = 0; + i2s->conf.rx_short_sync = 0; + i2s->conf.tx_msb_shift = (bits_per_sample != 8);// 0:DAC/PCM, 1:I2S + i2s->conf.rx_msb_shift = 0; + + i2s->conf.tx_slave_mod = 0; // Master + + i2s->conf.tx_msb_right = 0; + i2s->conf.tx_right_first = (bits_per_sample == 8); + i2s->conf2.lcd_en = (bits_per_sample == 8); + i2s->conf2.camera_en = 0; + + i2s->fifo_conf.tx_fifo_mod_force_en = 1; + + i2s->pdm_conf.rx_pdm_en = 0; + i2s->pdm_conf.tx_pdm_en = 0; + + i2sSetSampleRate(bus_num, sample_rate, bits_per_sample); + + // enable intr in cpu // + esp_intr_alloc(bus_num?ETS_I2S1_INTR_SOURCE:ETS_I2S0_INTR_SOURCE, ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_LEVEL1, &i2sDmaISR, &I2S[bus_num], &I2S[bus_num].isr_handle); + // enable send intr + i2s->int_ena.out_eof = 1; + i2s->int_ena.out_dscr_err = 1; + + i2s->fifo_conf.dscr_en = 1;// enable dma + i2s->out_link.start = 0; + i2s->out_link.addr = (uint32_t)(&I2S[bus_num].dma_items[0]); // loads dma_struct to dma + i2s->out_link.start = 1; // starts dma + i2s->conf.tx_start = 1;// Start I2s module + + esp_intr_enable(I2S[bus_num].isr_handle); +} + +esp_err_t i2sSetSampleRate(uint8_t bus_num, uint32_t rate, uint8_t bits) { + if (bus_num > 1) { + return ESP_FAIL; + } + + if (I2S[bus_num].rate == rate) { + return ESP_OK; + } + + int clkmInteger, clkmDecimals, bck = 0; + double denom = (double)1 / 63; + int channel = 2; + +// double mclk; + double clkmdiv; + + int factor; + + if (bits == 8) { + factor = 120; + } else { + factor = (256 % bits) ? 384 : 256; + } + + clkmdiv = (double)I2S_BASE_CLK / (rate* factor); + if (clkmdiv > 256) { + log_e("rate is too low"); + return ESP_FAIL; + } + I2S[bus_num].rate = rate; + + clkmInteger = clkmdiv; + clkmDecimals = ((clkmdiv - clkmInteger) / denom); + + if (bits == 8) { +// mclk = rate* factor; + bck = 60; + bits = 16; + } else { +// mclk = (double)clkmInteger + (denom* clkmDecimals); + bck = factor/(bits* channel); + } + + i2sSetClock(bus_num, clkmInteger, clkmDecimals, 63, bck, bits); + + return ESP_OK; +} + +void IRAM_ATTR i2sDmaISR(void* arg) +{ + i2s_dma_item_t* dummy = NULL; + i2s_bus_t* dev = (i2s_bus_t*)(arg); + portBASE_TYPE hpTaskAwoken = 0; + + if (dev->bus->int_st.out_eof) { + i2s_dma_item_t* item = (i2s_dma_item_t*)(dev->bus->out_eof_des_addr); + item->data = dev->silence_buf; + item->blocksize = dev->silence_len; + item->datalen = dev->silence_len; + if (xQueueIsQueueFullFromISR(dev->tx_queue) == pdTRUE) { + xQueueReceiveFromISR(dev->tx_queue, &dummy, &hpTaskAwoken); + } + xQueueSendFromISR(dev->tx_queue, (void*)&item, &hpTaskAwoken); + } + dev->bus->int_clr.val = dev->bus->int_st.val; + if (hpTaskAwoken == pdTRUE) { + portYIELD_FROM_ISR(); + } +} + +size_t i2sWrite(uint8_t bus_num, uint8_t* data, size_t len, bool copy, bool free_when_sent) { + if (bus_num > 1 || !I2S[bus_num].tx_queue) { + return 0; + } + size_t index = 0; + size_t toSend = len; + size_t limit = I2S_DMA_MAX_DATA_LEN; + i2s_dma_item_t* item = NULL; + + while (len) { + toSend = len; + if (toSend > limit) { + toSend = limit; + } + + if (xQueueReceive(I2S[bus_num].tx_queue, &item, portMAX_DELAY) == pdFALSE) { + log_e("xQueueReceive failed\n"); + break; + } + // data is constant. no need to copy + item->data = data + index; + item->blocksize = toSend; + item->datalen = toSend; + + len -= toSend; + index += toSend; + } + return index; +} + + +#endif diff --git a/lib/NeoPixelBus-2.5.0.09/src/internal/Esp32_i2s.h b/lib/NeoPixelBus-2.5.0.09/src/internal/Esp32_i2s.h new file mode 100644 index 000000000..0027369cf --- /dev/null +++ b/lib/NeoPixelBus-2.5.0.09/src/internal/Esp32_i2s.h @@ -0,0 +1,40 @@ +#pragma once + +#if defined(ARDUINO_ARCH_ESP32) + +#ifdef __cplusplus +extern "C" { +#endif + +#include "esp_err.h" + +#define I2S_DMA_MAX_DATA_LEN 4092// maximum bytes in one dma item + +typedef enum { + I2S_CHAN_STEREO, I2S_CHAN_RIGHT_TO_LEFT, I2S_CHAN_LEFT_TO_RIGHT, I2S_CHAN_RIGHT_ONLY, I2S_CHAN_LEFT_ONLY +} i2s_tx_chan_mod_t; + +typedef enum { + I2S_FIFO_16BIT_DUAL, I2S_FIFO_16BIT_SINGLE, I2S_FIFO_32BIT_DUAL, I2S_FIFO_32BIT_SINGLE +} i2s_tx_fifo_mod_t; + +void i2sInit(uint8_t bus_num, uint32_t bits_per_sample, uint32_t sample_rate, i2s_tx_chan_mod_t chan_mod, i2s_tx_fifo_mod_t fifo_mod, size_t dma_count, size_t dma_len); + +void i2sSetPins(uint8_t bus_num, int8_t out, int8_t ws, int8_t bck, int8_t in); +void i2sSetDac(uint8_t bus_num, bool right, bool left); + +esp_err_t i2sSetClock(uint8_t bus_num, uint8_t div_num, uint8_t div_b, uint8_t div_a, uint8_t bck, uint8_t bits_per_sample); +esp_err_t i2sSetSampleRate(uint8_t bus_num, uint32_t sample_rate, uint8_t bits_per_sample); + +void i2sSetTxDataMode(uint8_t bus_num, i2s_tx_chan_mod_t chan_mod, i2s_tx_fifo_mod_t fifo_mod); + +void i2sSetSilenceBuf(uint8_t bus_num, uint8_t* data, size_t len); + +size_t i2sWrite(uint8_t bus_num, uint8_t* data, size_t len, bool copy, bool free_when_sent); +bool i2sWriteDone(uint8_t bus_num); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/NeoPixelBus-2.2.9/src/internal/HsbColor.cpp b/lib/NeoPixelBus-2.5.0.09/src/internal/HsbColor.cpp similarity index 100% rename from lib/NeoPixelBus-2.2.9/src/internal/HsbColor.cpp rename to lib/NeoPixelBus-2.5.0.09/src/internal/HsbColor.cpp diff --git a/lib/NeoPixelBus-2.2.9/src/internal/HsbColor.h b/lib/NeoPixelBus-2.5.0.09/src/internal/HsbColor.h similarity index 100% rename from lib/NeoPixelBus-2.2.9/src/internal/HsbColor.h rename to lib/NeoPixelBus-2.5.0.09/src/internal/HsbColor.h diff --git a/lib/NeoPixelBus-2.2.9/src/internal/HslColor.cpp b/lib/NeoPixelBus-2.5.0.09/src/internal/HslColor.cpp similarity index 100% rename from lib/NeoPixelBus-2.2.9/src/internal/HslColor.cpp rename to lib/NeoPixelBus-2.5.0.09/src/internal/HslColor.cpp diff --git a/lib/NeoPixelBus-2.2.9/src/internal/HslColor.h b/lib/NeoPixelBus-2.5.0.09/src/internal/HslColor.h similarity index 100% rename from lib/NeoPixelBus-2.2.9/src/internal/HslColor.h rename to lib/NeoPixelBus-2.5.0.09/src/internal/HslColor.h diff --git a/lib/NeoPixelBus-2.2.9/src/internal/HtmlColor.cpp b/lib/NeoPixelBus-2.5.0.09/src/internal/HtmlColor.cpp similarity index 100% rename from lib/NeoPixelBus-2.2.9/src/internal/HtmlColor.cpp rename to lib/NeoPixelBus-2.5.0.09/src/internal/HtmlColor.cpp diff --git a/lib/NeoPixelBus-2.2.9/src/internal/HtmlColor.h b/lib/NeoPixelBus-2.5.0.09/src/internal/HtmlColor.h similarity index 99% rename from lib/NeoPixelBus-2.2.9/src/internal/HtmlColor.h rename to lib/NeoPixelBus-2.5.0.09/src/internal/HtmlColor.h index a40d6223c..238d4acdb 100644 --- a/lib/NeoPixelBus-2.2.9/src/internal/HtmlColor.h +++ b/lib/NeoPixelBus-2.5.0.09/src/internal/HtmlColor.h @@ -212,7 +212,7 @@ struct HtmlColor for (uint8_t indexName = 0; indexName < T_HTMLCOLORNAMES::Count(); ++indexName) { const HtmlColorPair* colorPair = T_HTMLCOLORNAMES::Pair(indexName); - PGM_P searchName = (PGM_P)pgm_read_ptr(&colorPair->Name); + PGM_P searchName = reinterpret_cast(pgm_read_ptr(&(colorPair->Name))); size_t str1Size = nameSize; const char* str1 = name; const char* str2P = searchName; diff --git a/lib/NeoPixelBus-2.2.9/src/internal/HtmlColorNameStrings.cpp b/lib/NeoPixelBus-2.5.0.09/src/internal/HtmlColorNameStrings.cpp similarity index 100% rename from lib/NeoPixelBus-2.2.9/src/internal/HtmlColorNameStrings.cpp rename to lib/NeoPixelBus-2.5.0.09/src/internal/HtmlColorNameStrings.cpp diff --git a/lib/NeoPixelBus-2.2.9/src/internal/HtmlColorNameStrings.h b/lib/NeoPixelBus-2.5.0.09/src/internal/HtmlColorNameStrings.h similarity index 100% rename from lib/NeoPixelBus-2.2.9/src/internal/HtmlColorNameStrings.h rename to lib/NeoPixelBus-2.5.0.09/src/internal/HtmlColorNameStrings.h diff --git a/lib/NeoPixelBus-2.2.9/src/internal/HtmlColorNames.cpp b/lib/NeoPixelBus-2.5.0.09/src/internal/HtmlColorNames.cpp similarity index 100% rename from lib/NeoPixelBus-2.2.9/src/internal/HtmlColorNames.cpp rename to lib/NeoPixelBus-2.5.0.09/src/internal/HtmlColorNames.cpp diff --git a/lib/NeoPixelBus-2.2.9/src/internal/HtmlColorShortNames.cpp b/lib/NeoPixelBus-2.5.0.09/src/internal/HtmlColorShortNames.cpp similarity index 100% rename from lib/NeoPixelBus-2.2.9/src/internal/HtmlColorShortNames.cpp rename to lib/NeoPixelBus-2.5.0.09/src/internal/HtmlColorShortNames.cpp diff --git a/lib/NeoPixelBus-2.2.9/src/internal/Layouts.h b/lib/NeoPixelBus-2.5.0.09/src/internal/Layouts.h similarity index 93% rename from lib/NeoPixelBus-2.2.9/src/internal/Layouts.h rename to lib/NeoPixelBus-2.5.0.09/src/internal/Layouts.h index 0df0049d7..5b1016ea9 100644 --- a/lib/NeoPixelBus-2.2.9/src/internal/Layouts.h +++ b/lib/NeoPixelBus-2.5.0.09/src/internal/Layouts.h @@ -57,7 +57,7 @@ public: class RowMajorLayout : public RowMajorTilePreference { public: - static uint16_t Map(uint16_t width, uint16_t height, uint16_t x, uint16_t y) + static uint16_t Map(uint16_t width, uint16_t /* height */, uint16_t x, uint16_t y) { return x + y * width; } @@ -102,7 +102,7 @@ public: class RowMajor270Layout : public RowMajorTilePreference { public: - static uint16_t Map(uint16_t width, uint16_t height, uint16_t x, uint16_t y) + static uint16_t Map(uint16_t /* width */, uint16_t height, uint16_t x, uint16_t y) { return x * height + (height - 1 - y); } @@ -136,7 +136,7 @@ public: class ColumnMajorLayout : public ColumnMajorTilePreference { public: - static uint16_t Map(uint16_t width, uint16_t height, uint16_t x, uint16_t y) + static uint16_t Map(uint16_t /* width */, uint16_t height, uint16_t x, uint16_t y) { return x * height + y; } @@ -151,7 +151,7 @@ public: class ColumnMajor90Layout : public ColumnMajorTilePreference { public: - static uint16_t Map(uint16_t width, uint16_t height, uint16_t x, uint16_t y) + static uint16_t Map(uint16_t width, uint16_t /* height */, uint16_t x, uint16_t y) { return (width - 1 - x) + y * width; } @@ -213,7 +213,7 @@ public: class RowMajorAlternatingLayout : public RowMajorAlternatingTilePreference { public: - static uint16_t Map(uint16_t width, uint16_t height, uint16_t x, uint16_t y) + static uint16_t Map(uint16_t width, uint16_t /* height */, uint16_t x, uint16_t y) { uint16_t index = y * width; @@ -290,7 +290,7 @@ public: class RowMajorAlternating270Layout : public RowMajorAlternatingTilePreference { public: - static uint16_t Map(uint16_t width, uint16_t height, uint16_t x, uint16_t y) + static uint16_t Map(uint16_t /* width */, uint16_t height, uint16_t x, uint16_t y) { uint16_t index = x * height; @@ -332,7 +332,7 @@ public: class ColumnMajorAlternatingLayout : public ColumnMajorAlternatingTilePreference { public: - static uint16_t Map(uint16_t width, uint16_t height, uint16_t x, uint16_t y) + static uint16_t Map(uint16_t /* width */, uint16_t height, uint16_t x, uint16_t y) { uint16_t index = x * height; @@ -357,7 +357,7 @@ public: class ColumnMajorAlternating90Layout : public ColumnMajorAlternatingTilePreference { public: - static uint16_t Map(uint16_t width, uint16_t height, uint16_t x, uint16_t y) + static uint16_t Map(uint16_t width, uint16_t /* height */, uint16_t x, uint16_t y) { uint16_t index = y * width; diff --git a/lib/NeoPixelBus-2.2.9/src/internal/NeoArmMethod.h b/lib/NeoPixelBus-2.5.0.09/src/internal/NeoArmMethod.h similarity index 82% rename from lib/NeoPixelBus-2.2.9/src/internal/NeoArmMethod.h rename to lib/NeoPixelBus-2.5.0.09/src/internal/NeoArmMethod.h index 34b42a853..9cfce8874 100644 --- a/lib/NeoPixelBus-2.2.9/src/internal/NeoArmMethod.h +++ b/lib/NeoPixelBus-2.5.0.09/src/internal/NeoArmMethod.h @@ -66,7 +66,7 @@ public: _endTime = micros(); } - void Update() + void Update(bool) { // Data latch = 50+ microsecond pause in the output stream. Rather than // put a delay at the end of the function, the ending time is noted and @@ -106,24 +106,32 @@ private: uint8_t _pin; // output pin number }; +// Teensy 3.0 or 3.1 (3.2) or 3.5 or 3.6 +#if defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__) -#if defined(__MK20DX128__) || defined(__MK20DX256__) // Teensy 3.0 & 3.1 - -class NeoArmMk20dxSpeedPropsWs2813 +class NeoArmMk20dxSpeedProps800KbpsBase { public: static const uint32_t CyclesT0h = (F_CPU / 4000000); static const uint32_t CyclesT1h = (F_CPU / 1250000); static const uint32_t Cycles = (F_CPU / 800000); - static const uint32_t ResetTimeUs = 250; }; -class NeoArmMk20dxSpeedProps800Kbps +class NeoArmMk20dxSpeedPropsWs2812x : public NeoArmMk20dxSpeedProps800KbpsBase +{ +public: + static const uint32_t ResetTimeUs = 300; +}; + +class NeoArmMk20dxSpeedPropsSk6812 : public NeoArmMk20dxSpeedProps800KbpsBase +{ +public: + static const uint32_t ResetTimeUs = 80; +}; + +class NeoArmMk20dxSpeedProps800Kbps : public NeoArmMk20dxSpeedProps800KbpsBase { public: - static const uint32_t CyclesT0h = (F_CPU / 4000000); - static const uint32_t CyclesT1h = (F_CPU / 1250000); - static const uint32_t Cycles = (F_CPU / 800000); static const uint32_t ResetTimeUs = 50; }; @@ -136,6 +144,15 @@ public: static const uint32_t ResetTimeUs = 50; }; +class NeoArmMk20dxSpeedPropsApa106 +{ +public: + static const uint32_t CyclesT0h = (F_CPU / 4000000); + static const uint32_t CyclesT1h = (F_CPU / 913750); + static const uint32_t Cycles = (F_CPU / 584800); + static const uint32_t ResetTimeUs = 50; +}; + template class NeoArmMk20dxSpeedBase { public: @@ -180,16 +197,17 @@ public: } }; -typedef NeoArmMethodBase> NeoArmWs2813Method; +typedef NeoArmMethodBase> NeoArmWs2812xMethod; +typedef NeoArmMethodBase> NeoArmSk6812Method; +typedef NeoArmMethodBase> NeoArmApa106Method; typedef NeoArmMethodBase> NeoArm800KbpsMethod; typedef NeoArmMethodBase> NeoArm400KbpsMethod; + #elif defined(__MKL26Z64__) // Teensy-LC #if F_CPU == 48000000 - - class NeoArmMk26z64Speed800KbpsBase { public: @@ -280,20 +298,28 @@ public: } }; -class NeoArmMk26z64SpeedWs2813 : public NeoArmMk26z64Speed800KbpsBase +class NeoArmMk26z64SpeedWs2812x : public NeoArmMk26z64Speed800KbpsBase { public: - const static uint32_t ResetTimeUs = 250; + const static uint32_t ResetTimeUs = 300; +}; + +class NeoArmMk26z64SpeedSk6812 : public NeoArmMk26z64Speed800KbpsBase +{ +public: + const static uint32_t ResetTimeUs = 80; }; class NeoArmMk26z64Speed800Kbps : public NeoArmMk26z64Speed800KbpsBase { public: const static uint32_t ResetTimeUs = 50; -} +}; -typedef NeoArmMethodBase NeoArmWs2813Method; +typedef NeoArmMethodBase NeoArmWs2812xMethod; +typedef NeoArmMethodBase NeoArmSk6812Method; typedef NeoArmMethodBase NeoArm800KbpsMethod; +typedef NeoArm800KbpsMethod NeoArmApa106Method #else #error "Teensy-LC: Sorry, only 48 MHz is supported, please set Tools > CPU Speed to 48 MHz" @@ -327,10 +353,16 @@ public: } }; -class NeoArmSamd21g18aSpeedPropsWs2813 : public NeoArmSamd21g18aSpeedProps800KbpsBase +class NeoArmSamd21g18aSpeedPropsWs2812x : public NeoArmSamd21g18aSpeedProps800KbpsBase { public: - static const uint32_t ResetTimeUs = 250; + static const uint32_t ResetTimeUs = 300; +}; + +class NeoArmSamd21g18aSpeedPropsSk6812 : public NeoArmSamd21g18aSpeedProps800KbpsBase +{ +public: + static const uint32_t ResetTimeUs = 80; }; class NeoArmSamd21g18aSpeedProps800Kbps : public NeoArmSamd21g18aSpeedProps800KbpsBase @@ -419,11 +451,13 @@ public: } }; -typedef NeoArmMethodBase> NeoArmWs2813Method; +typedef NeoArmMethodBase> NeoArmWs2812xMethod; +typedef NeoArmMethodBase> NeoArmSk6812Method; typedef NeoArmMethodBase> NeoArm800KbpsMethod; typedef NeoArmMethodBase> NeoArm400KbpsMethod; +typedef NeoArm400KbpsMethod NeoArmApa106Method -#elif defined (ARDUINO_STM32_FEATHER) // FEATHER WICED (120MHz) +#elif defined(ARDUINO_STM32_FEATHER) || defined(ARDUINO_ARCH_STM32L4) || defined(ARDUINO_ARCH_STM32F4) || defined(ARDUINO_ARCH_STM32F1)// FEATHER WICED (120MHz) class NeoArmStm32SpeedProps800KbpsBase { @@ -477,18 +511,24 @@ public: } }; +class NeoArmStm32SpeedPropsWs2812x : public NeoArmStm32SpeedProps800KbpsBase +{ +public: + static const uint32_t ResetTimeUs = 300; +}; + +class NeoArmStm32SpeedPropsSk6812 : public NeoArmStm32SpeedProps800KbpsBase +{ +public: + static const uint32_t ResetTimeUs = 80; +}; + class NeoArmStm32SpeedProps800Kbps : public NeoArmStm32SpeedProps800KbpsBase { public: static const uint32_t ResetTimeUs = 50; }; -class NeoArmStm32SpeedPropsWs2813 : public NeoArmStm32SpeedProps800KbpsBase -{ -public: - static const uint32_t ResetTimeUs = 250; -}; - /* TODO - not found in Adafruit library class NeoArmStm32SpeedProps400Kbps { @@ -521,11 +561,36 @@ public: uint8_t* end = ptr + sizePixels; uint8_t p = *ptr++; uint8_t bitMask = 0x80; - uint32_t pinMask = BIT(PIN_MAP[pin].gpio_bit); - volatile uint16_t* set = &(PIN_MAP[pin].gpio_device->regs->BSRRL); - volatile uint16_t* clr = &(PIN_MAP[pin].gpio_device->regs->BSRRH); +#if defined(ARDUINO_STM32_FEATHER) + uint32_t pinMask = BIT(PIN_MAP[pin].gpio_bit); + volatile uint16_t* set = &(PIN_MAP[pin].gpio_device->regs->BSRRL); + volatile uint16_t* clr = &(PIN_MAP[pin].gpio_device->regs->BSRRH); + +#elif defined(ARDUINO_ARCH_STM32F4) + uint32_t pinMask = BIT(pin & 0x0f); + + volatile uint16_t* set = &(PIN_MAP[pin].gpio_device->regs->BSRRL); + volatile uint16_t* clr = &(PIN_MAP[pin].gpio_device->regs->BSRRH); + +#elif defined(ARDUINO_ARCH_STM32F1) + + uint32_t pinMask = BIT(PIN_MAP[pin].gpio_bit); + + volatile uint32_t* set = &(PIN_MAP[pin].gpio_device->regs->BRR); + volatile uint32_t* clr = &(PIN_MAP[pin].gpio_device->regs->BSRR); + +#elif defined(ARDUINO_ARCH_STM32L4) + + uint32_t pinMask = g_APinDescription[pin].bit; + + GPIO_TypeDef* GPIO = static_cast(g_APinDescription[pin].GPIO); + + volatile uint32_t* set = &(GPIO->BRR); + volatile uint32_t* clr = &(GPIO->BSRR); + +#endif for (;;) { if (p & bitMask) @@ -567,8 +632,10 @@ public: } }; -typedef NeoArmMethodBase> NeoArmWs2813Method; +typedef NeoArmMethodBase> NeoArmWs2812xMethod; +typedef NeoArmMethodBase> NeoArmSk6812Method; typedef NeoArmMethodBase> NeoArm800KbpsMethod; +typedef NeoArm800KbpsMethod NeoArmApa106Method; #else // Other ARM architecture -- Presumed Arduino Due @@ -576,21 +643,29 @@ typedef NeoArmMethodBase> Neo #define ARM_OTHER_SCALE VARIANT_MCK / 2UL / 1000000UL #define ARM_OTHER_INST (2UL * F_CPU / VARIANT_MCK) -class NeoArmOtherSpeedPropsWs2813 +class NeoArmOtherSpeedProps800KbpsBase { public: static const uint32_t CyclesT0h = ((uint32_t)(0.40 * ARM_OTHER_SCALE + 0.5) - (5 * ARM_OTHER_INST)); static const uint32_t CyclesT1h = ((uint32_t)(0.80 * ARM_OTHER_SCALE + 0.5) - (5 * ARM_OTHER_INST)); static const uint32_t Cycles = ((uint32_t)(1.25 * ARM_OTHER_SCALE + 0.5) - (5 * ARM_OTHER_INST)); - static const uint32_t ResetTimeUs = 250; }; -class NeoArmOtherSpeedProps800Kbps +class NeoArmOtherSpeedPropsWs2812x : public NeoArmOtherSpeedProps800KbpsBase +{ +public: + static const uint32_t ResetTimeUs = 300; +}; + +class NeoArmOtherSpeedPropsSk6812 : public NeoArmOtherSpeedProps800KbpsBase +{ +public: + static const uint32_t ResetTimeUs = 80; +}; + +class NeoArmOtherSpeedProps800Kbps : public NeoArmOtherSpeedProps800KbpsBase { public: - static const uint32_t CyclesT0h = ((uint32_t)(0.40 * ARM_OTHER_SCALE + 0.5) - (5 * ARM_OTHER_INST)); - static const uint32_t CyclesT1h = ((uint32_t)(0.80 * ARM_OTHER_SCALE + 0.5) - (5 * ARM_OTHER_INST)); - static const uint32_t Cycles = ((uint32_t)(1.25 * ARM_OTHER_SCALE + 0.5) - (5 * ARM_OTHER_INST)); static const uint32_t ResetTimeUs = 50; }; @@ -679,16 +754,23 @@ public: } }; -typedef NeoArmMethodBase> NeoArmWs2813Method; +typedef NeoArmMethodBase> NeoArmWs2812xMethod; +typedef NeoArmMethodBase> NeoArmSk6812Method; typedef NeoArmMethodBase> NeoArm800KbpsMethod; typedef NeoArmMethodBase> NeoArm400KbpsMethod; +typedef NeoArm400KbpsMethod NeoArmApa106Method; #endif // Arm doesn't have alternatives methods yet, so only one to make the default -typedef NeoArmWs2813Method NeoWs2813Method; -typedef NeoArm800KbpsMethod Neo800KbpsMethod; +typedef NeoArmWs2812xMethod NeoWs2813Method; +typedef NeoArmWs2812xMethod NeoWs2812xMethod; +typedef NeoArmSk6812Method NeoSk6812Method; +typedef NeoArmSk6812Method NeoLc8812Method; +typedef NeoArm800KbpsMethod NeoWs2812Method; +typedef NeoArmApa106Method NeoApa106Method; +typedef NeoArmWs2812xMethod Neo800KbpsMethod; #ifdef NeoArm400KbpsMethod // this is needed due to missing 400Kbps for some platforms typedef NeoArm400KbpsMethod Neo400KbpsMethod; #endif diff --git a/lib/NeoPixelBus-2.2.9/src/internal/NeoAvrMethod.h b/lib/NeoPixelBus-2.5.0.09/src/internal/NeoAvrMethod.h similarity index 90% rename from lib/NeoPixelBus-2.2.9/src/internal/NeoAvrMethod.h rename to lib/NeoPixelBus-2.5.0.09/src/internal/NeoAvrMethod.h index 9843816f8..b292cf48c 100644 --- a/lib/NeoPixelBus-2.2.9/src/internal/NeoAvrMethod.h +++ b/lib/NeoPixelBus-2.5.0.09/src/internal/NeoAvrMethod.h @@ -71,10 +71,16 @@ public: }; -class NeoAvrSpeedWs2813 : public NeoAvrSpeed800KbpsBase +class NeoAvrSpeedWs2812x : public NeoAvrSpeed800KbpsBase { public: - static const uint32_t ResetTimeUs = 250; + static const uint32_t ResetTimeUs = 300; +}; + +class NeoAvrSpeedSk6812 : public NeoAvrSpeed800KbpsBase +{ +public: + static const uint32_t ResetTimeUs = 80; }; class NeoAvrSpeed800Kbps: public NeoAvrSpeed800KbpsBase @@ -142,7 +148,7 @@ public: _endTime = micros(); } - void Update() + void Update(bool) { // Data latch = 50+ microsecond pause in the output stream. Rather than // put a delay at the end of the function, the ending time is noted and @@ -187,13 +193,21 @@ private: uint8_t _pinMask; // Output PORT bitmask }; -typedef NeoAvrMethodBase NeoAvrWs2813Method; + +typedef NeoAvrMethodBase NeoAvrWs2812xMethod; +typedef NeoAvrMethodBase NeoAvrSk6812Method; typedef NeoAvrMethodBase NeoAvr800KbpsMethod; typedef NeoAvrMethodBase NeoAvr400KbpsMethod; + // AVR doesn't have alternatives yet, so there is just the default -typedef NeoAvrWs2813Method NeoWs2813Method; -typedef NeoAvr800KbpsMethod Neo800KbpsMethod; +typedef NeoAvrWs2812xMethod NeoWs2813Method; +typedef NeoAvrWs2812xMethod NeoWs2812xMethod; +typedef NeoAvr800KbpsMethod NeoWs2812Method; +typedef NeoAvrSk6812Method NeoSk6812Method; +typedef NeoAvrSk6812Method NeoLc8812Method; +typedef NeoAvr400KbpsMethod NeoApa106Method; +typedef NeoAvrWs2812xMethod Neo800KbpsMethod; typedef NeoAvr400KbpsMethod Neo400KbpsMethod; #endif diff --git a/lib/NeoPixelBus-2.2.9/src/internal/NeoBitmapFile.h b/lib/NeoPixelBus-2.5.0.09/src/internal/NeoBitmapFile.h similarity index 84% rename from lib/NeoPixelBus-2.2.9/src/internal/NeoBitmapFile.h rename to lib/NeoPixelBus-2.5.0.09/src/internal/NeoBitmapFile.h index 697967858..870696f48 100644 --- a/lib/NeoPixelBus-2.2.9/src/internal/NeoBitmapFile.h +++ b/lib/NeoPixelBus-2.5.0.09/src/internal/NeoBitmapFile.h @@ -75,12 +75,11 @@ public: _width(0), _height(0), _sizeRow(0), - _bottomToTop(true), - _bytesPerPixel(0) + _bytesPerPixel(0), + _bottomToTop(true) { } - - + ~NeoBitmapFile() { _file.close(); @@ -172,7 +171,7 @@ public: return _height; }; - typename T_COLOR_FEATURE::ColorObject GetPixelColor(int16_t x, int16_t y) const + typename T_COLOR_FEATURE::ColorObject GetPixelColor(int16_t x, int16_t y) { if (x < 0 || x >= _width || y < 0 || y >= _height) { @@ -190,7 +189,9 @@ public: return color; }; - void Blt(NeoBufferContext destBuffer, + + template void Render(NeoBufferContext destBuffer, + T_SHADER& shader, uint16_t indexPixel, int16_t xSrc, int16_t ySrc, @@ -205,10 +206,11 @@ public: { for (int16_t x = 0; x < wSrc && indexPixel < destPixelCount; x++, indexPixel++) { - if (xSrc < _width) + if ((uint16_t)xSrc < _width) { if (readPixel(&color)) { + color = shader.Apply(indexPixel, color); xSrc++; } } @@ -217,8 +219,20 @@ public: } } } - + void Blt(NeoBufferContext destBuffer, + uint16_t indexPixel, + int16_t xSrc, + int16_t ySrc, + int16_t wSrc) + { + NeoShaderNop shaderNop; + + Render>(destBuffer, shaderNop, indexPixel, xSrc, ySrc, wSrc); + }; + + template void Render(NeoBufferContext destBuffer, + T_SHADER& shader, int16_t xDest, int16_t yDest, int16_t xSrc, @@ -239,15 +253,16 @@ public: { for (int16_t x = 0; x < wSrc; x++) { - if (xFile < _width) + uint16_t indexDest = layoutMap(xDest + x, yDest + y); + + if ((uint16_t)xFile < _width) { if (readPixel(&color)) { + color = shader.Apply(indexDest, color); xFile++; } } - - uint16_t indexDest = layoutMap(xDest + x, yDest + y); if (indexDest < destPixelCount) { @@ -258,6 +273,28 @@ public: } }; + void Blt(NeoBufferContext destBuffer, + int16_t xDest, + int16_t yDest, + int16_t xSrc, + int16_t ySrc, + int16_t wSrc, + int16_t hSrc, + LayoutMapCallback layoutMap) + { + NeoShaderNop shaderNop; + + Render>(destBuffer, + shaderNop, + xDest, + yDest, + xSrc, + ySrc, + wSrc, + hSrc, + layoutMap); + }; + private: T_FILE_METHOD _file; @@ -268,26 +305,26 @@ private: uint8_t _bytesPerPixel; bool _bottomToTop; - int16_t constrainX(int16_t x) + int16_t constrainX(int16_t x) const { if (x < 0) { x = 0; } - else if (x >= _width) + else if ((uint16_t)x >= _width) { x = _width - 1; } return x; }; - int16_t constrainY(int16_t y) + int16_t constrainY(int16_t y) const { if (y < 0) { y = 0; } - else if (y >= _height) + else if ((uint16_t)y >= _height) { y = _height - 1; } diff --git a/lib/NeoPixelBus-2.2.9/src/internal/NeoBuffer.h b/lib/NeoPixelBus-2.5.0.09/src/internal/NeoBuffer.h similarity index 84% rename from lib/NeoPixelBus-2.2.9/src/internal/NeoBuffer.h rename to lib/NeoPixelBus-2.5.0.09/src/internal/NeoBuffer.h index 86176c9b0..996a77820 100644 --- a/lib/NeoPixelBus-2.2.9/src/internal/NeoBuffer.h +++ b/lib/NeoPixelBus-2.5.0.09/src/internal/NeoBuffer.h @@ -133,19 +133,38 @@ public: Blt(destBuffer, xDest, yDest, 0, 0, Width(), Height(), layoutMap); } + template void Render(NeoBufferContext destBuffer, T_SHADER& shader) + { + uint16_t countPixels = destBuffer.PixelCount(); + + if (countPixels > _method.PixelCount()) + { + countPixels = _method.PixelCount(); + } + + for (uint16_t indexPixel = 0; indexPixel < countPixels; indexPixel++) + { + typename T_BUFFER_METHOD::ColorObject color; + + shader.Apply(indexPixel, (uint8_t*)(&color), _method.Pixels() + (indexPixel * _method.PixelSize())); + + T_BUFFER_METHOD::ColorFeature::applyPixelColor(destBuffer.Pixels, indexPixel, color); + } + } + private: T_BUFFER_METHOD _method; uint16_t pixelIndex( int16_t x, - int16_t y) + int16_t y) const { uint16_t result = PixelIndex_OutOfBounds; if (x >= 0 && - x < Width() && + (uint16_t)x < Width() && y >= 0 && - y < Height()) + (uint16_t)y < Height()) { result = x + y * Width(); } diff --git a/lib/NeoPixelBus-2.2.9/src/internal/NeoBufferContext.h b/lib/NeoPixelBus-2.5.0.09/src/internal/NeoBufferContext.h similarity index 100% rename from lib/NeoPixelBus-2.2.9/src/internal/NeoBufferContext.h rename to lib/NeoPixelBus-2.5.0.09/src/internal/NeoBufferContext.h diff --git a/lib/NeoPixelBus-2.2.9/src/internal/NeoBufferMethods.h b/lib/NeoPixelBus-2.5.0.09/src/internal/NeoBufferMethods.h similarity index 100% rename from lib/NeoPixelBus-2.2.9/src/internal/NeoBufferMethods.h rename to lib/NeoPixelBus-2.5.0.09/src/internal/NeoBufferMethods.h diff --git a/lib/NeoPixelBus-2.2.9/src/internal/NeoColorFeatures.h b/lib/NeoPixelBus-2.5.0.09/src/internal/NeoColorFeatures.h similarity index 100% rename from lib/NeoPixelBus-2.2.9/src/internal/NeoColorFeatures.h rename to lib/NeoPixelBus-2.5.0.09/src/internal/NeoColorFeatures.h diff --git a/lib/NeoPixelBus-2.2.9/src/internal/NeoDib.h b/lib/NeoPixelBus-2.5.0.09/src/internal/NeoDib.h similarity index 90% rename from lib/NeoPixelBus-2.2.9/src/internal/NeoDib.h rename to lib/NeoPixelBus-2.5.0.09/src/internal/NeoDib.h index ef69ce6f7..2ec93eb7b 100644 --- a/lib/NeoPixelBus-2.2.9/src/internal/NeoDib.h +++ b/lib/NeoPixelBus-2.5.0.09/src/internal/NeoDib.h @@ -25,6 +25,32 @@ License along with NeoPixel. If not, see -------------------------------------------------------------------------*/ #pragma once +template class NeoShaderNop +{ +public: + NeoShaderNop() + { + } + + bool IsDirty() const + { + return true; + }; + + void Dirty() + { + }; + + void ResetDirty() + { + }; + + T_COLOR_OBJECT Apply(uint16_t, T_COLOR_OBJECT color) + { + return color; + }; +}; + class NeoShaderBase { public: @@ -118,11 +144,13 @@ public: Dirty(); }; - template void Render(NeoBufferContext destBuffer, T_SHADER& shader) + template void Render(NeoBufferContext destBuffer, + T_SHADER& shader) { if (IsDirty() || shader.IsDirty()) { uint16_t countPixels = destBuffer.PixelCount(); + if (countPixels > _countPixels) { countPixels = _countPixels; diff --git a/lib/NeoPixelBus-2.2.9/src/internal/NeoEase.h b/lib/NeoPixelBus-2.5.0.09/src/internal/NeoEase.h similarity index 72% rename from lib/NeoPixelBus-2.2.9/src/internal/NeoEase.h rename to lib/NeoPixelBus-2.5.0.09/src/internal/NeoEase.h index eaa50239f..b7b5df964 100644 --- a/lib/NeoPixelBus-2.2.9/src/internal/NeoEase.h +++ b/lib/NeoPixelBus-2.5.0.09/src/internal/NeoEase.h @@ -71,6 +71,20 @@ public: } } + static float QuadraticCenter(float unitValue) + { + unitValue *= 2.0f; + if (unitValue < 1.0f) + { + return (-0.5f * (unitValue * unitValue - 2.0f)); + } + else + { + unitValue -= 1.0f; + return (0.5f * (unitValue * unitValue + 1.0f)); + } + } + static float CubicIn(float unitValue) { return (unitValue * unitValue * unitValue); @@ -96,6 +110,13 @@ public: } } + static float CubicCenter(float unitValue) + { + unitValue *= 2.0f; + unitValue -= 1.0f; + return (0.5f * (unitValue * unitValue * unitValue) + 1); + } + static float QuarticIn(float unitValue) { return (unitValue * unitValue * unitValue * unitValue); @@ -121,6 +142,20 @@ public: } } + static float QuarticCenter(float unitValue) + { + unitValue *= 2.0f; + unitValue -= 1.0f; + if (unitValue < 0.0f) + { + return (-0.5f * (unitValue * unitValue * unitValue * unitValue - 1.0f)); + } + else + { + return (0.5f * (unitValue * unitValue * unitValue * unitValue + 1.0f)); + } + } + static float QuinticIn(float unitValue) { return (unitValue * unitValue * unitValue * unitValue * unitValue); @@ -146,6 +181,13 @@ public: } } + static float QuinticCenter(float unitValue) + { + unitValue *= 2.0f; + unitValue -= 1.0f; + return (0.5f * (unitValue * unitValue * unitValue * unitValue * unitValue + 1.0f)); + } + static float SinusoidalIn(float unitValue) { return (-cos(unitValue * HALF_PI) + 1.0f); @@ -161,6 +203,19 @@ public: return -0.5 * (cos(PI * unitValue) - 1.0f); } + static float SinusoidalCenter(float unitValue) + { + if (unitValue < 0.5f) + { + return (0.5 * sin(PI * unitValue)); + } + else + { + return (-0.5 * (cos(PI * (unitValue-0.5f)) + 1.0f)); + } + + } + static float ExponentialIn(float unitValue) { return (pow(2, 10.0f * (unitValue - 1.0f))); @@ -185,6 +240,20 @@ public: } } + static float ExponentialCenter(float unitValue) + { + unitValue *= 2.0f; + if (unitValue < 1.0f) + { + return (0.5f * (-pow(2, -10.0f * unitValue) + 1.0f)); + } + else + { + unitValue -= 2.0f; + return (0.5f * (pow(2, 10.0f * unitValue) + 1.0f)); + } + } + static float CircularIn(float unitValue) { if (unitValue == 1.0f) @@ -217,6 +286,25 @@ public: } } + static float CircularCenter(float unitValue) + { + unitValue *= 2.0f; + unitValue -= 1.0f; + if (unitValue == 0.0f) + { + return 1.0f; + } + else if (unitValue < 0.0f) + { + return (0.5f * sqrt(1.0f - unitValue * unitValue)); + } + else + { + unitValue -= 2.0f; + return (-0.5f * (sqrt(1.0f - unitValue * unitValue) - 1.0f ) + 0.5f); + } + } + static float Gamma(float unitValue) { return pow(unitValue, 1.0f / 0.45f); diff --git a/lib/NeoPixelBus-2.5.0.09/src/internal/NeoEsp32I2sMethod.h b/lib/NeoPixelBus-2.5.0.09/src/internal/NeoEsp32I2sMethod.h new file mode 100644 index 000000000..d7856a1ef --- /dev/null +++ b/lib/NeoPixelBus-2.5.0.09/src/internal/NeoEsp32I2sMethod.h @@ -0,0 +1,217 @@ +/*------------------------------------------------------------------------- +NeoPixel library helper functions for Esp32. + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +#pragma once + +#ifdef ARDUINO_ARCH_ESP32 + +extern "C" +{ +#include +#include "Esp32_i2s.h" +} + +const uint16_t c_dmaBytesPerPixelBytes = 4; + +class NeoEsp32I2sSpeedWs2812x +{ +public: + const static uint32_t I2sSampleRate = 100000; + const static uint16_t ByteSendTimeUs = 10; + const static uint16_t ResetTimeUs = 300; +}; + +class NeoEsp32I2sSpeedSk6812 +{ +public: + const static uint32_t I2sSampleRate = 100000; + const static uint16_t ByteSendTimeUs = 10; + const static uint16_t ResetTimeUs = 80; +}; + +class NeoEsp32I2sSpeed800Kbps +{ +public: + const static uint32_t I2sSampleRate = 100000; + const static uint16_t ByteSendTimeUs = 10; + const static uint16_t ResetTimeUs = 50; +}; + +class NeoEsp32I2sSpeed400Kbps +{ +public: + const static uint32_t I2sSampleRate = 50000; + const static uint16_t ByteSendTimeUs = 20; + const static uint16_t ResetTimeUs = 50; +}; + +class NeoEsp32I2sSpeedApa106 +{ +public: + const static uint32_t I2sSampleRate = 76000; + const static uint16_t ByteSendTimeUs = 14; + const static uint16_t ResetTimeUs = 50; +}; + +class NeoEsp32I2sBusZero +{ +public: + const static uint8_t I2sBusNumber = 0; +}; + +class NeoEsp32I2sBusOne +{ +public: + const static uint8_t I2sBusNumber = 1; +}; + +template class NeoEsp32I2sMethodBase +{ +public: + NeoEsp32I2sMethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize) : + _pin(pin) + { + uint16_t dmaPixelSize = c_dmaBytesPerPixelBytes * elementSize; + uint16_t resetSize = c_dmaBytesPerPixelBytes * T_SPEED::ResetTimeUs / T_SPEED::ByteSendTimeUs; + + _pixelsSize = pixelCount * elementSize; + _i2sBufferSize = pixelCount * dmaPixelSize + resetSize; + + // must have a 4 byte aligned buffer for i2s + uint32_t alignment = _i2sBufferSize % 4; + if (alignment) + { + _i2sBufferSize += 4 - alignment; + } + + _pixels = static_cast(malloc(_pixelsSize)); + memset(_pixels, 0x00, _pixelsSize); + + _i2sBuffer = static_cast(malloc(_i2sBufferSize)); + memset(_i2sBuffer, 0x00, _i2sBufferSize); + } + + ~NeoEsp32I2sMethodBase() + { + while (!IsReadyToUpdate()) + { + yield(); + } + + pinMode(_pin, INPUT); + + free(_pixels); + free(_i2sBuffer); + } + + bool IsReadyToUpdate() const + { + return (i2sWriteDone(T_BUS::I2sBusNumber)); + } + + void Initialize() + { + size_t dmaCount = (_i2sBufferSize + I2S_DMA_MAX_DATA_LEN - 1) / I2S_DMA_MAX_DATA_LEN; + i2sInit(T_BUS::I2sBusNumber, 16, T_SPEED::I2sSampleRate, I2S_CHAN_STEREO, I2S_FIFO_16BIT_DUAL, dmaCount, 0); + i2sSetPins(T_BUS::I2sBusNumber, _pin, -1, -1, -1); + } + + void Update(bool) + { + // wait for not actively sending data + while (!IsReadyToUpdate()) + { + yield(); + } + + FillBuffers(); + + i2sWrite(T_BUS::I2sBusNumber, _i2sBuffer, _i2sBufferSize, false, false); + } + + uint8_t* getPixels() const + { + return _pixels; + }; + + size_t getPixelsSize() const + { + return _pixelsSize; + } + +private: + const uint8_t _pin; // output pin number + + size_t _pixelsSize; // Size of '_pixels' buffer + uint8_t* _pixels; // Holds LED color values + + uint32_t _i2sBufferSize; // total size of _i2sBuffer + uint8_t* _i2sBuffer; // holds the DMA buffer that is referenced by _i2sBufDesc + + void FillBuffers() + { + const uint16_t bitpatterns[16] = + { + 0b1000100010001000, 0b1000100010001110, 0b1000100011101000, 0b1000100011101110, + 0b1000111010001000, 0b1000111010001110, 0b1000111011101000, 0b1000111011101110, + 0b1110100010001000, 0b1110100010001110, 0b1110100011101000, 0b1110100011101110, + 0b1110111010001000, 0b1110111010001110, 0b1110111011101000, 0b1110111011101110, + }; + + uint16_t* pDma = reinterpret_cast(_i2sBuffer); + uint8_t* pPixelsEnd = _pixels + _pixelsSize; + for (uint8_t* pPixel = _pixels; pPixel < pPixelsEnd; pPixel++) + { + *(pDma++) = bitpatterns[((*pPixel) & 0x0f)]; + *(pDma++) = bitpatterns[((*pPixel) >> 4) & 0x0f]; + } + } +}; + +typedef NeoEsp32I2sMethodBase NeoEsp32I2s0Ws2812xMethod; +typedef NeoEsp32I2sMethodBase NeoEsp32I2s0Sk6812Method; +typedef NeoEsp32I2sMethodBase NeoEsp32I2s0800KbpsMethod; +typedef NeoEsp32I2sMethodBase NeoEsp32I2s0400KbpsMethod; +typedef NeoEsp32I2sMethodBase NeoEsp32I2s0Apa106Method; + +typedef NeoEsp32I2sMethodBase NeoEsp32I2s1Ws2812xMethod; +typedef NeoEsp32I2sMethodBase NeoEsp32I2s1Sk6812Method; +typedef NeoEsp32I2sMethodBase NeoEsp32I2s1800KbpsMethod; +typedef NeoEsp32I2sMethodBase NeoEsp32I2s1400KbpsMethod; +typedef NeoEsp32I2sMethodBase NeoEsp32I2s1Apa106Method; + +// I2s Bus 1 method is the default method for Esp32 +typedef NeoEsp32I2s1Ws2812xMethod NeoWs2813Method; +typedef NeoEsp32I2s1Ws2812xMethod NeoWs2812xMethod; +typedef NeoEsp32I2s1800KbpsMethod NeoWs2812Method; +typedef NeoEsp32I2s1Sk6812Method NeoSk6812Method; +typedef NeoEsp32I2s1Sk6812Method NeoLc8812Method; +typedef NeoEsp32I2s1Apa106Method NeoApa106Method; + +typedef NeoEsp32I2s1Ws2812xMethod Neo800KbpsMethod; +typedef NeoEsp32I2s1400KbpsMethod Neo400KbpsMethod; + +#endif \ No newline at end of file diff --git a/lib/NeoPixelBus-2.5.0.09/src/internal/NeoEsp32RmtMethod.h b/lib/NeoPixelBus-2.5.0.09/src/internal/NeoEsp32RmtMethod.h new file mode 100644 index 000000000..86669e509 --- /dev/null +++ b/lib/NeoPixelBus-2.5.0.09/src/internal/NeoEsp32RmtMethod.h @@ -0,0 +1,369 @@ +/*------------------------------------------------------------------------- +NeoPixel library helper functions for Esp32. + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +#pragma once + +#ifdef ARDUINO_ARCH_ESP32 + +/* General Reference documentation for the APIs used in this implementation +LOW LEVEL: (what is actually used) +DOCS: https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/peripherals/rmt.html +EXAMPLE: https://github.com/espressif/esp-idf/blob/826ff7186ae07dc81e960a8ea09ebfc5304bfb3b/examples/peripherals/rmt_tx/main/rmt_tx_main.c +HIGHER LEVEL: +NO TRANSLATE SUPPORT so this was not used +NOTE: https://github.com/espressif/arduino-esp32/commit/50d142950d229b8fabca9b749dc4a5f2533bc426 +Esp32-hal-rmt.h +Esp32-hal-rmt.c +*/ + +extern "C" +{ +#include +#include +} + +class NeoEsp32RmtSpeedBase +{ +public: + // ClkDiv of 2 provides for good resolution and plenty of reset resolution; but + // a ClkDiv of 1 will provide enough space for the longest reset and does show + // little better pulse accuracy + const static uint8_t RmtClockDivider = 2; + + inline constexpr static uint32_t FromNs(uint32_t ns) + { + return ns / NsPerRmtTick; + } + // this is used rather than the rmt_item32_t as you can't correctly initialize + // it as a static constexpr within the template + inline constexpr static uint32_t Item32Val(uint16_t nsHigh, uint16_t nsLow) + { + return (FromNs(nsLow) << 16) | (1 << 15) | (FromNs(nsHigh)); + } + +public: + const static uint32_t RmtCpu = 80000000L; // 80 mhz RMT clock + const static uint32_t NsPerSecond = 1000000000L; + const static uint32_t RmtTicksPerSecond = (RmtCpu / RmtClockDivider); + const static uint32_t NsPerRmtTick = (NsPerSecond / RmtTicksPerSecond); // about 25 +}; + +class NeoEsp32RmtSpeedWs2812x : public NeoEsp32RmtSpeedBase +{ +public: + const static uint32_t RmtBit0 = Item32Val(400, 850); + const static uint32_t RmtBit1 = Item32Val(800, 450); + const static uint16_t RmtDurationReset = FromNs(300000); // 300us +}; + +class NeoEsp32RmtSpeedSk6812 : public NeoEsp32RmtSpeedBase +{ +public: + const static uint32_t RmtBit0 = Item32Val(400, 850); + const static uint32_t RmtBit1 = Item32Val(800, 450); + const static uint16_t RmtDurationReset = FromNs(80000); // 80us +}; + +class NeoEsp32RmtSpeed800Kbps : public NeoEsp32RmtSpeedBase +{ +public: + const static uint32_t RmtBit0 = Item32Val(400, 850); + const static uint32_t RmtBit1 = Item32Val(800, 450); + const static uint16_t RmtDurationReset = FromNs(50000); // 50us +}; + +class NeoEsp32RmtSpeed400Kbps : public NeoEsp32RmtSpeedBase +{ +public: + const static uint32_t RmtBit0 = Item32Val(800, 1700); + const static uint32_t RmtBit1 = Item32Val(1600, 900); + const static uint16_t RmtDurationReset = FromNs(50000); // 50us +}; + +class NeoEsp32RmtSpeedApa106 : public NeoEsp32RmtSpeedBase +{ +public: + const static uint32_t RmtBit0 = Item32Val(400, 1250); + const static uint32_t RmtBit1 = Item32Val(1250, 400); + const static uint16_t RmtDurationReset = FromNs(50000); // 50us +}; + +class NeoEsp32RmtChannel0 +{ +public: + const static rmt_channel_t RmtChannelNumber = RMT_CHANNEL_0; +}; + +class NeoEsp32RmtChannel1 +{ +public: + const static rmt_channel_t RmtChannelNumber = RMT_CHANNEL_1; +}; + +class NeoEsp32RmtChannel2 +{ +public: + const static rmt_channel_t RmtChannelNumber = RMT_CHANNEL_2; +}; + +class NeoEsp32RmtChannel3 +{ +public: + const static rmt_channel_t RmtChannelNumber = RMT_CHANNEL_3; +}; + +class NeoEsp32RmtChannel4 +{ +public: + const static rmt_channel_t RmtChannelNumber = RMT_CHANNEL_4; +}; + +class NeoEsp32RmtChannel5 +{ +public: + const static rmt_channel_t RmtChannelNumber = RMT_CHANNEL_5; +}; + +class NeoEsp32RmtChannel6 +{ +public: + const static rmt_channel_t RmtChannelNumber = RMT_CHANNEL_6; +}; + +class NeoEsp32RmtChannel7 +{ +public: + const static rmt_channel_t RmtChannelNumber = RMT_CHANNEL_7; +}; + +template class NeoEsp32RmtMethodBase +{ +public: + NeoEsp32RmtMethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize) : + _pin(pin) + { + _pixelsSize = pixelCount * elementSize; + + _pixelsEditing = static_cast(malloc(_pixelsSize)); + memset(_pixelsEditing, 0x00, _pixelsSize); + + _pixelsSending = static_cast(malloc(_pixelsSize)); + // no need to initialize it, it gets overwritten on every send + } + + ~NeoEsp32RmtMethodBase() + { + // wait until the last send finishes before destructing everything + // arbitrary time out of 10 seconds + rmt_wait_tx_done(T_CHANNEL::RmtChannelNumber, 10000 / portTICK_PERIOD_MS); + + rmt_driver_uninstall(T_CHANNEL::RmtChannelNumber); + + free(_pixelsEditing); + free(_pixelsSending); + } + + + bool IsReadyToUpdate() const + { + return (ESP_OK == rmt_wait_tx_done(T_CHANNEL::RmtChannelNumber, 0)); + } + + void Initialize() + { + rmt_config_t config; + + config.rmt_mode = RMT_MODE_TX; + config.channel = T_CHANNEL::RmtChannelNumber; + config.gpio_num = static_cast(_pin); + config.mem_block_num = 1; + config.tx_config.loop_en = false; + + config.tx_config.idle_output_en = true; + config.tx_config.idle_level = RMT_IDLE_LEVEL_LOW; + + config.tx_config.carrier_en = false; + config.tx_config.carrier_level = RMT_CARRIER_LEVEL_LOW; + + config.clk_div = T_SPEED::RmtClockDivider; + + rmt_config(&config); + rmt_driver_install(T_CHANNEL::RmtChannelNumber, 0, 0); + rmt_translator_init(T_CHANNEL::RmtChannelNumber, _translate); + } + + void Update(bool maintainBufferConsistency) + { + // wait for not actively sending data + // this will time out at 10 seconds, an arbitrarily long period of time + // and do nothing if this happens + if (ESP_OK == rmt_wait_tx_done(T_CHANNEL::RmtChannelNumber, 10000 / portTICK_PERIOD_MS)) + { + // now start the RMT transmit with the editing buffer before we swap + rmt_write_sample(T_CHANNEL::RmtChannelNumber, _pixelsEditing, _pixelsSize, false); + + if (maintainBufferConsistency) + { + // copy editing to sending, + // this maintains the contract that "colors present before will + // be the same after", otherwise GetPixelColor will be inconsistent + memcpy(_pixelsSending, _pixelsEditing, _pixelsSize); + } + + // swap so the user can modify without affecting the async operation + std::swap(_pixelsSending, _pixelsEditing); + } + } + + uint8_t* getPixels() const + { + return _pixelsEditing; + }; + + size_t getPixelsSize() const + { + return _pixelsSize; + } + +private: + const uint8_t _pin; // output pin number + + size_t _pixelsSize; // Size of '_pixels' buffer + uint8_t* _pixelsEditing; // Holds LED color values exposed for get and set + uint8_t* _pixelsSending; // Holds LED color values used to async send using RMT + + + // stranslate NeoPixelBuffer into RMT buffer + // this is done on the fly so we don't require a send buffer in raw RMT format + // which would be 32x larger than the primary buffer + static void IRAM_ATTR _translate(const void* src, + rmt_item32_t* dest, + size_t src_size, + size_t wanted_num, + size_t* translated_size, + size_t* item_num) + { + if (src == NULL || dest == NULL) + { + *translated_size = 0; + *item_num = 0; + return; + } + + size_t size = 0; + size_t num = 0; + const uint8_t* psrc = static_cast(src); + rmt_item32_t* pdest = dest; + + for (;;) + { + uint8_t data = *psrc; + + for (uint8_t bit = 0; bit < 8; bit++) + { + pdest->val = (data & 0x80) ? T_SPEED::RmtBit1 : T_SPEED::RmtBit0; + pdest++; + data <<= 1; + } + num += 8; + size++; + + // if this is the last byte we need to adjust the length of the last pulse + if (size >= src_size) + { + // extend the last bits LOW value to include the full reset signal length + pdest--; + pdest->duration1 = T_SPEED::RmtDurationReset; + // and stop updating data to send + break; + } + + if (num >= wanted_num) + { + // stop updating data to send + break; + } + + psrc++; + } + + *translated_size = size; + *item_num = num; + } +}; + +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt0Ws2812xMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt0Sk6812Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt0Apa106Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt0800KbpsMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt0400KbpsMethod; + +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt1Ws2812xMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt1Sk6812Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt1Apa106Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt1800KbpsMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt1400KbpsMethod; + +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt2Ws2812xMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt2Sk6812Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt2Apa106Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt2800KbpsMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt2400KbpsMethod; + +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt3Ws2812xMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt3Sk6812Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt3Apa106Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt3800KbpsMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt3400KbpsMethod; + +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt4Ws2812xMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt4Sk6812Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt4Apa106Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt4800KbpsMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt4400KbpsMethod; + +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt5Ws2812xMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt5Sk6812Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt5Apa106Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt5800KbpsMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt5400KbpsMethod; + +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt6Ws2812xMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt6Sk6812Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt6Apa106Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt6800KbpsMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt6400KbpsMethod; + +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt7Ws2812xMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt7Sk6812Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt7Apa106Method; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt7800KbpsMethod; +typedef NeoEsp32RmtMethodBase NeoEsp32Rmt7400KbpsMethod; + +// RMT is NOT the default method for Esp32, +// you are required to use a specific channel listed above + +#endif \ No newline at end of file diff --git a/lib/NeoPixelBus-2.2.9/src/internal/NeoEsp8266DmaMethod.h b/lib/NeoPixelBus-2.5.0.09/src/internal/NeoEsp8266DmaMethod.h similarity index 75% rename from lib/NeoPixelBus-2.2.9/src/internal/NeoEsp8266DmaMethod.h rename to lib/NeoPixelBus-2.5.0.09/src/internal/NeoEsp8266DmaMethod.h index dc4dd2723..402c9ae85 100644 --- a/lib/NeoPixelBus-2.2.9/src/internal/NeoEsp8266DmaMethod.h +++ b/lib/NeoPixelBus-2.5.0.09/src/internal/NeoEsp8266DmaMethod.h @@ -48,9 +48,7 @@ extern "C" #include "ets_sys.h" #include "user_interface.h" -// Workaround STAGE compile error -#include -#if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_1) || defined(ARDUINO_ESP8266_RELEASE_2_4_2) || defined(ARDUINO_ESP8266_RELEASE_2_5_0) +#if !defined(__CORE_ESP8266_VERSION_H) || defined(ARDUINO_ESP8266_RELEASE_2_5_0) void rom_i2c_writeReg_Mask(uint32_t block, uint32_t host_id, uint32_t reg_add, uint32_t Msb, uint32_t Lsb, uint32_t indata); #endif } @@ -67,19 +65,29 @@ struct slc_queue_item uint32 next_link_ptr; }; -class NeoEsp8266DmaSpeedWs2813 +class NeoEsp8266DmaSpeed800KbpsBase { public: const static uint32_t I2sClockDivisor = 3; const static uint32_t I2sBaseClockDivisor = 16; - const static uint32_t ResetTimeUs = 250; + const static uint32_t ByteSendTimeUs = 10; // us it takes to send a single pixel element at 800khz speed }; -class NeoEsp8266DmaSpeed800Kbps +class NeoEsp8266DmaSpeedWs2812x : public NeoEsp8266DmaSpeed800KbpsBase +{ +public: + const static uint32_t ResetTimeUs = 300; +}; + +class NeoEsp8266DmaSpeedSk6812 : public NeoEsp8266DmaSpeed800KbpsBase +{ +public: + const static uint32_t ResetTimeUs = 80; +}; + +class NeoEsp8266DmaSpeed800Kbps : public NeoEsp8266DmaSpeed800KbpsBase { public: - const static uint32_t I2sClockDivisor = 3; - const static uint32_t I2sBaseClockDivisor = 16; const static uint32_t ResetTimeUs = 50; }; @@ -88,14 +96,25 @@ class NeoEsp8266DmaSpeed400Kbps public: const static uint32_t I2sClockDivisor = 6; const static uint32_t I2sBaseClockDivisor = 16; + const static uint32_t ByteSendTimeUs = 20; // us it takes to send a single pixel element at 400khz speed const static uint32_t ResetTimeUs = 50; }; +class NeoEsp8266DmaSpeedApa106 +{ +public: + const static uint32_t I2sClockDivisor = 4; + const static uint32_t I2sBaseClockDivisor = 16; + const static uint32_t ByteSendTimeUs = 17; // us it takes to send a single pixel element + const static uint32_t ResetTimeUs = 50; +}; + enum NeoDmaState { NeoDmaState_Idle, NeoDmaState_Pending, NeoDmaState_Sending, + NeoDmaState_Zeroing, }; const uint16_t c_maxDmaBlockSize = 4095; const uint16_t c_dmaBytesPerPixelBytes = 4; @@ -117,6 +136,8 @@ public: _i2sBuffer = (uint8_t*)malloc(_i2sBufferSize); memset(_i2sBuffer, 0x00, _i2sBufferSize); + // _i2sBuffer[0] = 0b11101000; // debug, 1 bit then 0 bit + memset(_i2sZeroes, 0x00, sizeof(_i2sZeroes)); _is2BufMaxBlockSize = (c_maxDmaBlockSize / dmaPixelSize) * dmaPixelSize; @@ -133,8 +154,26 @@ public: ~NeoEsp8266DmaMethodBase() { + uint8_t waits = 1; + while (!IsReadyToUpdate()) + { + waits = 2; + yield(); + } + + // wait for any pending sends to complete + // due to internal i2s caching/send delays, this can more that once the data size + uint32_t time = micros(); + while ((micros() - time) < ((getPixelTime() + T_SPEED::ResetTimeUs) * waits)) + { + yield(); + } + StopDma(); + s_this = nullptr; + pinMode(c_I2sPin, INPUT); + free(_pixels); free(_i2sBuffer); free(_i2sBufDesc); @@ -148,7 +187,8 @@ public: void Initialize() { StopDma(); - _dmaState = NeoDmaState_Sending; // start off sending empty buffer + + pinMode(c_I2sPin, FUNCTION_1); // I2S0_DATA uint8_t* is2Buffer = _i2sBuffer; uint32_t is2BufferSize = _i2sBufferSize; @@ -193,6 +233,11 @@ public: // setup the rest of i2s DMA // ETS_SLC_INTR_DISABLE(); + + // start off in sending state as that is what it will be all setup to be + // for the interrupt + _dmaState = NeoDmaState_Sending; + SLCC0 |= SLCRXLR | SLCTXLR; SLCC0 &= ~(SLCRXLR | SLCTXLR); SLCIC = 0xFFFFFFFF; @@ -208,9 +253,11 @@ public: // expect. The TXLINK part still needs a valid DMA descriptor, even if it's unused: the DMA engine will throw // an error at us otherwise. Just feed it any random descriptor. SLCTXL &= ~(SLCTXLAM << SLCTXLA); // clear TX descriptor address - SLCTXL |= (uint32)&(_i2sBufDesc[_i2sBufDescCount-1]) << SLCTXLA; // set TX descriptor address. any random desc is OK, we don't use TX but it needs to be valid + // set TX descriptor address. any random desc is OK, we don't use TX but it needs to be valid + SLCTXL |= (uint32)&(_i2sBufDesc[_i2sBufDescCount-1]) << SLCTXLA; SLCRXL &= ~(SLCRXLAM << SLCRXLA); // clear RX descriptor address - SLCRXL |= (uint32)_i2sBufDesc << SLCRXLA; // set RX descriptor address + // set RX descriptor address. use first of the data addresses + SLCRXL |= (uint32)&(_i2sBufDesc[0]) << SLCRXLA; ETS_SLC_INTR_ATTACH(i2s_slc_isr, NULL); SLCIE = SLCIRXEOF; // Enable only for RX EOF interrupt @@ -221,8 +268,6 @@ public: SLCTXL |= SLCTXLS; SLCRXL |= SLCRXLS; - pinMode(c_I2sPin, FUNCTION_1); // I2S0_DATA - I2S_CLK_ENABLE(); I2SIC = 0x3F; I2SIE = 0; @@ -232,30 +277,32 @@ public: I2SC |= I2SRST; I2SC &= ~(I2SRST); - I2SFC &= ~(I2SDE | (I2STXFMM << I2STXFM) | (I2SRXFMM << I2SRXFM)); // Set RX/TX FIFO_MOD=0 and disable DMA (FIFO only) + // Set RX/TX FIFO_MOD=0 and disable DMA (FIFO only) + I2SFC &= ~(I2SDE | (I2STXFMM << I2STXFM) | (I2SRXFMM << I2SRXFM)); I2SFC |= I2SDE; //Enable DMA - I2SCC &= ~((I2STXCMM << I2STXCM) | (I2SRXCMM << I2SRXCM)); // Set RX/TX CHAN_MOD=0 + // Set RX/TX CHAN_MOD=0 + I2SCC &= ~((I2STXCMM << I2STXCM) | (I2SRXCMM << I2SRXCM)); // set the rate uint32_t i2s_clock_div = T_SPEED::I2sClockDivisor & I2SCDM; uint8_t i2s_bck_div = T_SPEED::I2sBaseClockDivisor & I2SBDM; //!trans master, !bits mod, rece slave mod, rece msb shift, right first, msb right - I2SC &= ~(I2STSM | (I2SBMM << I2SBM) | (I2SBDM << I2SBD) | (I2SCDM << I2SCD)); + I2SC &= ~(I2STSM | I2SRSM | (I2SBMM << I2SBM) | (I2SBDM << I2SBD) | (I2SCDM << I2SCD)); I2SC |= I2SRF | I2SMR | I2SRSM | I2SRMS | (i2s_bck_div << I2SBD) | (i2s_clock_div << I2SCD); I2SC |= I2STXS; // Start transmission } - void ICACHE_RAM_ATTR Update() + void ICACHE_RAM_ATTR Update(bool) { // wait for not actively sending data - while (_dmaState != NeoDmaState_Idle) + while (!IsReadyToUpdate()) { yield(); } FillBuffers(); - + // toggle state so the ISR reacts _dmaState = NeoDmaState_Pending; } @@ -273,16 +320,16 @@ public: private: static NeoEsp8266DmaMethodBase* s_this; // for the ISR - size_t _pixelsSize; // Size of '_pixels' buffer + size_t _pixelsSize; // Size of '_pixels' buffer uint8_t* _pixels; // Holds LED color values uint32_t _i2sBufferSize; // total size of _i2sBuffer uint8_t* _i2sBuffer; // holds the DMA buffer that is referenced by _i2sBufDesc // normally 24 bytes creates the minimum 50us latch per spec, but - // with the new logic, this latch is used to space between three states - // buffer size = (24 * (speed / 50)) / 3 - uint8_t _i2sZeroes[(24L * (T_SPEED::ResetTimeUs / 50L)) / 3L]; + // with the new logic, this latch is used to space between mulitple states + // buffer size = (24 * (reset time / 50)) / 6 + uint8_t _i2sZeroes[(24L * (T_SPEED::ResetTimeUs / 50L)) / 6L]; slc_queue_item* _i2sBufDesc; // dma block descriptors uint16_t _i2sBufDescCount; // count of block descriptors in _i2sBufDesc @@ -296,15 +343,15 @@ private: // in the case of this code, the second to last state descriptor volatile static void ICACHE_RAM_ATTR i2s_slc_isr(void) { + ETS_SLC_INTR_DISABLE(); + uint32_t slc_intr_status = SLCIS; SLCIC = 0xFFFFFFFF; - if (slc_intr_status & SLCIRXEOF) + if ((slc_intr_status & SLCIRXEOF) && s_this) { - ETS_SLC_INTR_DISABLE(); - - switch (s_this->_dmaState) + switch (s_this->_dmaState) { case NeoDmaState_Idle: break; @@ -314,7 +361,7 @@ private: slc_queue_item* finished_item = (slc_queue_item*)SLCRXEDA; // data block has pending data waiting to send, prepare it - // point last state block to top + // point last state block to top (finished_item + 1)->next_link_ptr = (uint32_t)(s_this->_i2sBufDesc); s_this->_dmaState = NeoDmaState_Sending; @@ -330,14 +377,17 @@ private: // just looping and not sending the data blocks (finished_item + 1)->next_link_ptr = (uint32_t)(finished_item); - s_this->_dmaState = NeoDmaState_Idle; + s_this->_dmaState = NeoDmaState_Zeroing; } break; + + case NeoDmaState_Zeroing: + s_this->_dmaState = NeoDmaState_Idle; + break; } - - - ETS_SLC_INTR_ENABLE(); } + + ETS_SLC_INTR_ENABLE(); } void FillBuffers() @@ -362,6 +412,16 @@ private: void StopDma() { ETS_SLC_INTR_DISABLE(); + + // Disable any I2S send or receive + I2SC &= ~(I2STXS | I2SRXS); + + // Reset I2S + I2SC &= ~(I2SRST); + I2SC |= I2SRST; + I2SC &= ~(I2SRST); + + SLCIC = 0xFFFFFFFF; SLCIE = 0; SLCTXL &= ~(SLCTXLAM << SLCTXLA); // clear TX descriptor address @@ -369,18 +429,32 @@ private: pinMode(c_I2sPin, INPUT); } + + uint32_t getPixelTime() const + { + return (T_SPEED::ByteSendTimeUs * this->_pixelsSize); + }; + }; -template +template NeoEsp8266DmaMethodBase* NeoEsp8266DmaMethodBase::s_this; -typedef NeoEsp8266DmaMethodBase NeoEsp8266DmaWs2813Method; +typedef NeoEsp8266DmaMethodBase NeoEsp8266DmaWs2812xMethod; +typedef NeoEsp8266DmaMethodBase NeoEsp8266DmaSk6812Method; typedef NeoEsp8266DmaMethodBase NeoEsp8266Dma800KbpsMethod; typedef NeoEsp8266DmaMethodBase NeoEsp8266Dma400KbpsMethod; +typedef NeoEsp8266DmaMethodBase NeoEsp8266DmaApa106Method; // Dma method is the default method for Esp8266 -typedef NeoEsp8266DmaWs2813Method NeoWs2813Method; -typedef NeoEsp8266Dma800KbpsMethod Neo800KbpsMethod; +typedef NeoEsp8266DmaWs2812xMethod NeoWs2813Method; +typedef NeoEsp8266DmaWs2812xMethod NeoWs2812xMethod; +typedef NeoEsp8266Dma800KbpsMethod NeoWs2812Method; +typedef NeoEsp8266DmaSk6812Method NeoSk6812Method; +typedef NeoEsp8266DmaSk6812Method NeoLc8812Method; +typedef NeoEsp8266DmaApa106Method NeoApa106Method; + +typedef NeoEsp8266DmaWs2812xMethod Neo800KbpsMethod; typedef NeoEsp8266Dma400KbpsMethod Neo400KbpsMethod; -#endif \ No newline at end of file +#endif diff --git a/lib/NeoPixelBus-2.5.0.09/src/internal/NeoEsp8266UartMethod.cpp b/lib/NeoPixelBus-2.5.0.09/src/internal/NeoEsp8266UartMethod.cpp new file mode 100644 index 000000000..eb06812e6 --- /dev/null +++ b/lib/NeoPixelBus-2.5.0.09/src/internal/NeoEsp8266UartMethod.cpp @@ -0,0 +1,171 @@ +/*------------------------------------------------------------------------- +NeoPixel library helper functions for Esp8266 UART hardware + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +#ifdef ARDUINO_ARCH_ESP8266 +#include "NeoEsp8266UartMethod.h" +#include +extern "C" +{ + #include +} + +const volatile uint8_t* ICACHE_RAM_ATTR NeoEsp8266UartContext::FillUartFifo(uint8_t uartNum, + const volatile uint8_t* pixels, + const volatile uint8_t* end) +{ + // Remember: UARTs send less significant bit (LSB) first so + // pushing ABCDEF byte will generate a 0FEDCBA1 signal, + // including a LOW(0) start & a HIGH(1) stop bits. + // Also, we have configured UART to invert logic levels, so: + const uint8_t _uartData[4] = { + 0b110111, // On wire: 1 000 100 0 [Neopixel reads 00] + 0b000111, // On wire: 1 000 111 0 [Neopixel reads 01] + 0b110100, // On wire: 1 110 100 0 [Neopixel reads 10] + 0b000100, // On wire: 1 110 111 0 [NeoPixel reads 11] + }; + uint8_t avail = (UART_TX_FIFO_SIZE - GetTxFifoLength(uartNum)) / 4; + if (end - pixels > avail) + { + end = pixels + avail; + } + while (pixels < end) + { + uint8_t subpix = *pixels++; + Enqueue(uartNum, _uartData[(subpix >> 6) & 0x3]); + Enqueue(uartNum, _uartData[(subpix >> 4) & 0x3]); + Enqueue(uartNum, _uartData[(subpix >> 2) & 0x3]); + Enqueue(uartNum, _uartData[subpix & 0x3]); + } + return pixels; +} + +volatile NeoEsp8266UartInterruptContext* NeoEsp8266UartInterruptContext::s_uartInteruptContext[] = { nullptr, nullptr }; + +void NeoEsp8266UartInterruptContext::StartSending(uint8_t uartNum, uint8_t* start, uint8_t* end) +{ + // send the pixels asynchronously + _asyncBuff = start; + _asyncBuffEnd = end; + + // enable the transmit interrupt + USIE(uartNum) |= (1 << UIFE); +} + +void NeoEsp8266UartInterruptContext::Attach(uint8_t uartNum) +{ + // Disable all interrupts + ETS_UART_INTR_DISABLE(); + + // Clear the RX & TX FIFOS + const uint32_t fifoResetFlags = (1 << UCTXRST) | (1 << UCRXRST); + USC0(uartNum) |= fifoResetFlags; + USC0(uartNum) &= ~(fifoResetFlags); + + // attach the ISR if needed + if (s_uartInteruptContext[0] == nullptr && + s_uartInteruptContext[1] == nullptr) + { + ETS_UART_INTR_ATTACH(Isr, s_uartInteruptContext); + } + + // attach the context + s_uartInteruptContext[uartNum] = this; + + // Set tx fifo trigger. 80 bytes gives us 200 microsecs to refill the FIFO + USC1(uartNum) = (80 << UCFET); + + // Disable RX & TX interrupts. It maybe still enabled by uart.c in the SDK + USIE(uartNum) &= ~((1 << UIFF) | (1 << UIFE)); + + // Clear all pending interrupts in UART1 + USIC(uartNum) = 0xffff; + + // Reenable interrupts + ETS_UART_INTR_ENABLE(); +} + +void NeoEsp8266UartInterruptContext::Detach(uint8_t uartNum) +{ + // Disable interrupts + ETS_UART_INTR_DISABLE(); + + if (s_uartInteruptContext[uartNum] != nullptr) + { + // turn off uart + USC1(uartNum) = 0; + USIC(uartNum) = 0xffff; + USIE(uartNum) = 0; + + s_uartInteruptContext[uartNum] = nullptr; + + if (s_uartInteruptContext[0] == nullptr && + s_uartInteruptContext[1] == nullptr) + { + // detach our ISR + ETS_UART_INTR_ATTACH(NULL, NULL); + + // return so we don't enable interrupts since there is no ISR anymore + return; + } + } + + // Reenable interrupts + ETS_UART_INTR_ENABLE(); +} + +void ICACHE_RAM_ATTR NeoEsp8266UartInterruptContext::Isr(void* param) +{ + // make sure this is for us + if (param == s_uartInteruptContext) + { + // Interrupt handler is shared between UART0 & UART1 + // so we need to test for both + for (uint8_t uartNum = 0; uartNum < 2; uartNum++) + { + if (USIS(uartNum) && s_uartInteruptContext[uartNum] != nullptr) + { + // Fill the FIFO with new data + s_uartInteruptContext[uartNum]->_asyncBuff = FillUartFifo( + uartNum, + s_uartInteruptContext[uartNum]->_asyncBuff, + s_uartInteruptContext[uartNum]->_asyncBuffEnd); + + // Disable TX interrupt when done + if (s_uartInteruptContext[uartNum]->_asyncBuff == s_uartInteruptContext[uartNum]->_asyncBuffEnd) + { + // clear the TX FIFO Empty + USIE(uartNum) &= ~(1 << UIFE); + } + + // Clear all interrupts flags (just in case) + USIC(uartNum) = 0xffff; + } + } + } +} + +#endif + diff --git a/lib/NeoPixelBus-2.5.0.09/src/internal/NeoEsp8266UartMethod.h b/lib/NeoPixelBus-2.5.0.09/src/internal/NeoEsp8266UartMethod.h new file mode 100644 index 000000000..42c4c63bb --- /dev/null +++ b/lib/NeoPixelBus-2.5.0.09/src/internal/NeoEsp8266UartMethod.h @@ -0,0 +1,434 @@ +/*------------------------------------------------------------------------- +NeoPixel library helper functions for Esp8266 UART hardware + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +#pragma once + +#ifdef ARDUINO_ARCH_ESP8266 +#include + +// this template method class is used to track the data being sent on the uart +// when using the default serial ISR installed by the core +// used with NeoEsp8266Uart and NeoEsp8266AsyncUart classes +// +class NeoEsp8266UartContext +{ +public: + // Gets the number of bytes waiting in the TX FIFO + static inline uint8_t ICACHE_RAM_ATTR GetTxFifoLength(uint8_t uartNum) + { + return (USS(uartNum) >> USTXC) & 0xff; + } + // Append a byte to the TX FIFO + static inline void ICACHE_RAM_ATTR Enqueue(uint8_t uartNum, uint8_t value) + { + USF(uartNum) = value; + } + + static const volatile uint8_t* ICACHE_RAM_ATTR FillUartFifo(uint8_t uartNum, + const volatile uint8_t* pixels, + const volatile uint8_t* end); +}; + +// this template method class is used to track the data being sent on the uart +// when using our own UART ISR +// used with NeoEsp8266Uart and NeoEsp8266AsyncUart classes +// +class NeoEsp8266UartInterruptContext : NeoEsp8266UartContext +{ +public: + NeoEsp8266UartInterruptContext() : + _asyncBuff(nullptr), + _asyncBuffEnd(nullptr) + { + } + + bool IsSending() + { + return (_asyncBuff != _asyncBuffEnd); + } + + void StartSending(uint8_t uartNum, uint8_t* start, uint8_t* end); + void Attach(uint8_t uartNum); + void Detach(uint8_t uartNum); + +private: + volatile const uint8_t* _asyncBuff; + volatile const uint8_t* _asyncBuffEnd; + volatile static NeoEsp8266UartInterruptContext* s_uartInteruptContext[2]; + + static void ICACHE_RAM_ATTR Isr(void* param); +}; + +// this template feature class is used a base for all others and contains +// common methods +// +class UartFeatureBase +{ +protected: + static void ConfigUart(uint8_t uartNum) + { + // clear all invert bits + USC0(uartNum) &= ~((1 << UCDTRI) | (1 << UCRTSI) | (1 << UCTXI) | (1 << UCDSRI) | (1 << UCCTSI) | (1 << UCRXI)); + // Invert the TX voltage associated with logic level so: + // - A logic level 0 will generate a Vcc signal + // - A logic level 1 will generate a Gnd signal + USC0(uartNum) |= (1 << UCTXI); + } +}; + +// this template feature class is used to define the specifics for uart0 +// used with NeoEsp8266Uart and NeoEsp8266AsyncUart classes +// +class UartFeature0 : UartFeatureBase +{ +public: + static const uint32_t Index = 0; + static void Init(uint32_t baud) + { + // Configure the serial line with 1 start bit (0), 6 data bits and 1 stop bit (1) + Serial.begin(baud, SERIAL_6N1, SERIAL_TX_ONLY); + ConfigUart(Index); + } +}; + +// this template feature class is used to define the specifics for uart1 +// used with NeoEsp8266Uart and NeoEsp8266AsyncUart classes +// +class UartFeature1 : UartFeatureBase +{ +public: + static const uint32_t Index = 1; + static void Init(uint32_t baud) + { + // Configure the serial line with 1 start bit (0), 6 data bits and 1 stop bit (1) + Serial1.begin(baud, SERIAL_6N1, SERIAL_TX_ONLY); + ConfigUart(Index); + } +}; + +// this template method class is used a base for all others and contains +// common properties and methods +// +// used by NeoEsp8266Uart and NeoEsp8266AsyncUart +// +class NeoEsp8266UartBase +{ +protected: + size_t _sizePixels; // Size of '_pixels' buffer below + uint8_t* _pixels; // Holds LED color values + uint32_t _startTime; // Microsecond count when last update started + + NeoEsp8266UartBase(uint16_t pixelCount, size_t elementSize) + { + _sizePixels = pixelCount * elementSize; + _pixels = (uint8_t*)malloc(_sizePixels); + memset(_pixels, 0x00, _sizePixels); + } + + ~NeoEsp8266UartBase() + { + free(_pixels); + } + +}; + +// this template method class is used to glue uart feature and context for +// synchronous uart method +// +// used by NeoEsp8266UartMethodBase +// +template class NeoEsp8266Uart : public NeoEsp8266UartBase +{ +protected: + + NeoEsp8266Uart(uint16_t pixelCount, size_t elementSize) : + NeoEsp8266UartBase(pixelCount, elementSize) + { + } + + ~NeoEsp8266Uart() + { + // Wait until the TX fifo is empty. This way we avoid broken frames + // when destroying & creating a NeoPixelBus to change its length. + while (T_UARTCONTEXT::GetTxFifoLength(T_UARTFEATURE::Index) > 0) + { + yield(); + } + } + + void InitializeUart(uint32_t uartBaud) + { + T_UARTFEATURE::Init(uartBaud); + } + + void UpdateUart(bool) + { + // Since the UART can finish sending queued bytes in the FIFO in + // the background, instead of waiting for the FIFO to flush + // we annotate the start time of the frame so we can calculate + // when it will finish. + _startTime = micros(); + + // Then keep filling the FIFO until done + const uint8_t* ptr = _pixels; + const uint8_t* end = ptr + _sizePixels; + while (ptr != end) + { + ptr = const_cast(T_UARTCONTEXT::FillUartFifo(T_UARTFEATURE::Index, ptr, end)); + } + } +}; + +// this template method class is used to glue uart feature and context for +// asynchronously uart method +// +// This UART controller uses two buffers that are swapped in every call to +// NeoPixelBus.Show(). One buffer contains the data that is being sent +// asynchronosly and another buffer contains the data that will be send +// in the next call to NeoPixelBus.Show(). +// +// Therefore, the result of NeoPixelBus.Pixels() is invalidated after +// every call to NeoPixelBus.Show() and must not be cached. +// +// used by NeoEsp8266UartMethodBase +// +template class NeoEsp8266AsyncUart : public NeoEsp8266UartBase +{ +protected: + NeoEsp8266AsyncUart(uint16_t pixelCount, size_t elementSize) : + NeoEsp8266UartBase(pixelCount, elementSize) + { + _pixelsSending = (uint8_t*)malloc(_sizePixels); + } + + ~NeoEsp8266AsyncUart() + { + // Remember: the UART interrupt can be sending data from _pixelsSending in the background + while (_context.IsSending()) + { + yield(); + } + // detach context, which will disable intr, may disable ISR + _context.Detach(T_UARTFEATURE::Index); + + free(_pixelsSending); + } + + void ICACHE_RAM_ATTR InitializeUart(uint32_t uartBaud) + { + T_UARTFEATURE::Init(uartBaud); + + // attach the context, which will enable the ISR + _context.Attach(T_UARTFEATURE::Index); + } + + void UpdateUart(bool maintainBufferConsistency) + { + // Instruct ESP8266 hardware uart to send the pixels asynchronously + _context.StartSending(T_UARTFEATURE::Index, + _pixels, + _pixels + _sizePixels); + + // Annotate when we started to send bytes, so we can calculate when we are ready to send again + _startTime = micros(); + + if (maintainBufferConsistency) + { + // copy editing to sending, + // this maintains the contract that "colors present before will + // be the same after", otherwise GetPixelColor will be inconsistent + memcpy(_pixelsSending, _pixels, _sizePixels); + } + + // swap so the user can modify without affecting the async operation +// std::swap(_pixelsSending, _pixels); + uint8_t *temp = _pixelsSending; + _pixelsSending = _pixels; + _pixels = temp; + } + +private: + T_UARTCONTEXT _context; + + uint8_t* _pixelsSending; // Holds a copy of LED color values taken when UpdateUart began +}; + +class NeoEsp8266UartSpeed800KbpsBase +{ +public: + static const uint32_t ByteSendTimeUs = 10; // us it takes to send a single pixel element at 800khz speed + static const uint32_t UartBaud = 3200000; // 800mhz, 4 serial bytes per NeoByte +}; + +// NeoEsp8266UartSpeedWs2813 contains the timing constants used to get NeoPixelBus running with the Ws2813 +class NeoEsp8266UartSpeedWs2812x : public NeoEsp8266UartSpeed800KbpsBase +{ +public: + static const uint32_t ResetTimeUs = 300; // us between data send bursts to reset for next update +}; + +class NeoEsp8266UartSpeedSk6812 : public NeoEsp8266UartSpeed800KbpsBase +{ +public: + static const uint32_t ResetTimeUs = 80; // us between data send bursts to reset for next update +}; + +// NeoEsp8266UartSpeed800Kbps contains the timing constant used to get NeoPixelBus running at 800Khz +class NeoEsp8266UartSpeed800Kbps : public NeoEsp8266UartSpeed800KbpsBase +{ +public: + static const uint32_t ResetTimeUs = 50; // us between data send bursts to reset for next update +}; + +// NeoEsp8266UartSpeed400Kbps contains the timing constant used to get NeoPixelBus running at 400Khz +class NeoEsp8266UartSpeed400Kbps +{ +public: + static const uint32_t ByteSendTimeUs = 20; // us it takes to send a single pixel element at 400khz speed + static const uint32_t UartBaud = 1600000; // 400mhz, 4 serial bytes per NeoByte + static const uint32_t ResetTimeUs = 50; // us between data send bursts to reset for next update +}; + +// NeoEsp8266UartSpeedApa106 contains the timing constant used to get NeoPixelBus running for Apa106 +// Pulse cycle = 1.71 = 1.368 longer than normal, 0.731 slower, NeoEsp8266UartSpeedApa1066 +class NeoEsp8266UartSpeedApa106 +{ +public: + static const uint32_t ByteSendTimeUs = 14; // us it takes to send a single pixel element at 400khz speed + static const uint32_t UartBaud = 2339181; // APA106 pulse cycle of 1.71us, 4 serial bytes per NeoByte + static const uint32_t ResetTimeUs = 50; // us between data send bursts to reset for next update +}; + +// NeoEsp8266UartMethodBase is a light shell arround NeoEsp8266Uart or NeoEsp8266AsyncUart that +// implements the methods needed to operate as a NeoPixelBus method. +template +class NeoEsp8266UartMethodBase: public T_BASE +{ +public: + NeoEsp8266UartMethodBase(uint16_t pixelCount, size_t elementSize) + : T_BASE(pixelCount, elementSize) + { + } + NeoEsp8266UartMethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize) + : T_BASE(pixelCount, elementSize) + { + } + + bool IsReadyToUpdate() const + { + uint32_t delta = micros() - this->_startTime; + return delta >= getPixelTime() + T_SPEED::ResetTimeUs; + } + + void Initialize() + { + this->InitializeUart(T_SPEED::UartBaud); + + // Inverting logic levels can generate a phantom bit in the led strip bus + // We need to delay 50+ microseconds the output stream to force a data + // latch and discard this bit. Otherwise, that bit would be prepended to + // the first frame corrupting it. + this->_startTime = micros() - getPixelTime(); + } + + void Update(bool maintainBufferConsistency) + { + // Data latch = 50+ microsecond pause in the output stream. Rather than + // put a delay at the end of the function, the ending time is noted and + // the function will simply hold off (if needed) on issuing the + // subsequent round of data until the latch time has elapsed. This + // allows the mainline code to start generating the next frame of data + // rather than stalling for the latch. + while (!this->IsReadyToUpdate()) + { + yield(); + } + this->UpdateUart(maintainBufferConsistency); + } + + uint8_t* getPixels() const + { + return this->_pixels; + }; + + size_t getPixelsSize() const + { + return this->_sizePixels; + }; + +private: + uint32_t getPixelTime() const + { + return (T_SPEED::ByteSendTimeUs * this->_sizePixels); + }; +}; + +// uart 0 +typedef NeoEsp8266UartMethodBase> NeoEsp8266Uart0Ws2812xMethod; +typedef NeoEsp8266UartMethodBase> NeoEsp8266Uart0Sk6812Method; +typedef NeoEsp8266UartMethodBase> NeoEsp8266Uart0Apa106Method; +typedef NeoEsp8266UartMethodBase> NeoEsp8266Uart0800KbpsMethod; +typedef NeoEsp8266UartMethodBase> NeoEsp8266Uart0400KbpsMethod; + +typedef NeoEsp8266Uart0Ws2812xMethod NeoEsp8266Uart0Ws2813Method; +typedef NeoEsp8266Uart0800KbpsMethod NeoEsp8266Uart0Ws2812Method; +typedef NeoEsp8266Uart0Sk6812Method NeoEsp8266Uart0Lc8812Method; + +// uart 1 +typedef NeoEsp8266UartMethodBase> NeoEsp8266Uart1Ws2812xMethod; +typedef NeoEsp8266UartMethodBase> NeoEsp8266Uart1Sk6812Method; +typedef NeoEsp8266UartMethodBase> NeoEsp8266Uart1Apa106Method; +typedef NeoEsp8266UartMethodBase> NeoEsp8266Uart1800KbpsMethod; +typedef NeoEsp8266UartMethodBase> NeoEsp8266Uart1400KbpsMethod; + +typedef NeoEsp8266Uart1Ws2812xMethod NeoEsp8266Uart1Ws2813Method; +typedef NeoEsp8266Uart1800KbpsMethod NeoEsp8266Uart1Ws2812Method; +typedef NeoEsp8266Uart1Sk6812Method NeoEsp8266Uart1Lc8812Method; + +// uart 0 async +typedef NeoEsp8266UartMethodBase> NeoEsp8266AsyncUart0Ws2812xMethod; +typedef NeoEsp8266UartMethodBase> NeoEsp8266AsyncUart0Sk6812Method; +typedef NeoEsp8266UartMethodBase> NeoEsp8266AsyncUart0Apa106Method; +typedef NeoEsp8266UartMethodBase> NeoEsp8266AsyncUart0800KbpsMethod; +typedef NeoEsp8266UartMethodBase> NeoEsp8266AsyncUart0400KbpsMethod; + +typedef NeoEsp8266AsyncUart0Ws2812xMethod NeoEsp8266AsyncUart0Ws2813Method; +typedef NeoEsp8266AsyncUart0800KbpsMethod NeoEsp8266AsyncUart0Ws2812Method; +typedef NeoEsp8266AsyncUart0Sk6812Method NeoEsp8266AsyncUart0Lc8812Method; + +// uart 1 async +typedef NeoEsp8266UartMethodBase> NeoEsp8266AsyncUart1Ws2812xMethod; +typedef NeoEsp8266UartMethodBase> NeoEsp8266AsyncUart1Sk6812Method; +typedef NeoEsp8266UartMethodBase> NeoEsp8266AsyncUart1Apa106Method; +typedef NeoEsp8266UartMethodBase> NeoEsp8266AsyncUart1800KbpsMethod; +typedef NeoEsp8266UartMethodBase> NeoEsp8266AsyncUart1400KbpsMethod; + +typedef NeoEsp8266AsyncUart1Ws2812xMethod NeoEsp8266AsyncUart1Ws2813Method; +typedef NeoEsp8266AsyncUart1800KbpsMethod NeoEsp8266AsyncUart1Ws2812Method; +typedef NeoEsp8266AsyncUart1Sk6812Method NeoEsp8266AsyncUart1Lc8812Method; + +#endif + diff --git a/lib/NeoPixelBus-2.2.9/src/internal/NeoEspBitBangMethod.h b/lib/NeoPixelBus-2.5.0.09/src/internal/NeoEspBitBangMethod.h similarity index 73% rename from lib/NeoPixelBus-2.2.9/src/internal/NeoEspBitBangMethod.h rename to lib/NeoPixelBus-2.5.0.09/src/internal/NeoEspBitBangMethod.h index 499f1c3da..361560213 100644 --- a/lib/NeoPixelBus-2.2.9/src/internal/NeoEspBitBangMethod.h +++ b/lib/NeoPixelBus-2.5.0.09/src/internal/NeoEspBitBangMethod.h @@ -39,14 +39,24 @@ License along with NeoPixel. If not, see extern "C" void ICACHE_RAM_ATTR bitbang_send_pixels_800(uint8_t* pixels, uint8_t* end, uint8_t pin); extern "C" void ICACHE_RAM_ATTR bitbang_send_pixels_400(uint8_t* pixels, uint8_t* end, uint8_t pin); -class NeoEspBitBangSpeedWs2813 +class NeoEspBitBangSpeedWs2812x { public: static void send_pixels(uint8_t* pixels, uint8_t* end, uint8_t pin) { bitbang_send_pixels_800(pixels, end, pin); } - static const uint32_t ResetTimeUs = 250; + static const uint32_t ResetTimeUs = 300; +}; + +class NeoEspBitBangSpeedSk6812 +{ +public: + static void send_pixels(uint8_t* pixels, uint8_t* end, uint8_t pin) + { + bitbang_send_pixels_800(pixels, end, pin); + } + static const uint32_t ResetTimeUs = 80; }; class NeoEspBitBangSpeed800Kbps @@ -103,7 +113,7 @@ public: _endTime = micros(); } - void Update() + void Update(bool) { // Data latch = 50+ microsecond pause in the output stream. Rather than // put a delay at the end of the function, the ending time is noted and @@ -116,11 +126,23 @@ public: yield(); // allows for system yield if needed } - noInterrupts(); // Need 100% focus on instruction timing + // Need 100% focus on instruction timing +#if defined(ARDUINO_ARCH_ESP32) + delay(1); // required + portMUX_TYPE updateMux = portMUX_INITIALIZER_UNLOCKED; + + portENTER_CRITICAL(&updateMux); +#else + noInterrupts(); +#endif T_SPEED::send_pixels(_pixels, _pixels + _sizePixels, _pin); - + +#if defined(ARDUINO_ARCH_ESP32) + portEXIT_CRITICAL(&updateMux); +#else interrupts(); +#endif // save EOD time for latch on next call _endTime = micros(); @@ -146,21 +168,28 @@ private: #if defined(ARDUINO_ARCH_ESP32) -typedef NeoEspBitBangMethodBase NeoEsp32BitBangWs2813Method; +typedef NeoEspBitBangMethodBase NeoEsp32BitBangWs2812xMethod; +typedef NeoEspBitBangMethodBase NeoEsp32BitBangSk6812Method; typedef NeoEspBitBangMethodBase NeoEsp32BitBang800KbpsMethod; typedef NeoEspBitBangMethodBase NeoEsp32BitBang400KbpsMethod; -// Bitbang method is the default method for Esp32 -typedef NeoEsp32BitBangWs2813Method NeoWs2813Method; -typedef NeoEsp32BitBang800KbpsMethod Neo800KbpsMethod; -typedef NeoEsp32BitBang400KbpsMethod Neo400KbpsMethod; +typedef NeoEsp32BitBangWs2812xMethod NeoEsp32BitBangWs2813Method; +typedef NeoEsp32BitBang800KbpsMethod NeoEsp32BitBangWs2812Method; +typedef NeoEsp32BitBangSk6812Method NeoEsp32BitBangLc8812Method; +typedef NeoEsp32BitBang400KbpsMethod NeoEsp32BitBangApa106Method; #else -typedef NeoEspBitBangMethodBase NeoEsp8266BitBangWs2813Method; +typedef NeoEspBitBangMethodBase NeoEsp8266BitBangWs2812xMethod; +typedef NeoEspBitBangMethodBase NeoEsp8266BitBangSk6812Method; typedef NeoEspBitBangMethodBase NeoEsp8266BitBang800KbpsMethod; typedef NeoEspBitBangMethodBase NeoEsp8266BitBang400KbpsMethod; +typedef NeoEsp8266BitBangWs2812xMethod NeoEsp8266BitBangWs2813Method; +typedef NeoEsp8266BitBang800KbpsMethod NeoEsp8266BitBangWs2812Method; +typedef NeoEsp8266BitBangSk6812Method NeoEsp8266BitBangLc8812Method; +typedef NeoEsp8266BitBang400KbpsMethod NeoEsp8266BitBangApa106Method; #endif +// ESP bitbang doesn't have defaults and should avoided except for testing #endif \ No newline at end of file diff --git a/lib/NeoPixelBus-2.2.9/src/internal/NeoGamma.cpp b/lib/NeoPixelBus-2.5.0.09/src/internal/NeoGamma.cpp similarity index 100% rename from lib/NeoPixelBus-2.2.9/src/internal/NeoGamma.cpp rename to lib/NeoPixelBus-2.5.0.09/src/internal/NeoGamma.cpp diff --git a/lib/NeoPixelBus-2.2.9/src/internal/NeoGamma.h b/lib/NeoPixelBus-2.5.0.09/src/internal/NeoGamma.h similarity index 95% rename from lib/NeoPixelBus-2.2.9/src/internal/NeoGamma.h rename to lib/NeoPixelBus-2.5.0.09/src/internal/NeoGamma.h index 9b94e2419..4aaf68e4a 100644 --- a/lib/NeoPixelBus-2.2.9/src/internal/NeoGamma.h +++ b/lib/NeoPixelBus-2.5.0.09/src/internal/NeoGamma.h @@ -1,5 +1,6 @@ /*------------------------------------------------------------------------- -NeoPixelGamma class is used to correct RGB colors for human eye gamma levels +NeoGamma class is used to correct RGB colors for human eye gamma levels equally +across all color channels Written by Michael C. Miller. diff --git a/lib/NeoPixelBus-2.2.9/src/internal/NeoHueBlend.h b/lib/NeoPixelBus-2.5.0.09/src/internal/NeoHueBlend.h similarity index 100% rename from lib/NeoPixelBus-2.2.9/src/internal/NeoHueBlend.h rename to lib/NeoPixelBus-2.5.0.09/src/internal/NeoHueBlend.h diff --git a/lib/NeoPixelBus-2.2.9/src/internal/NeoMosaic.h b/lib/NeoPixelBus-2.5.0.09/src/internal/NeoMosaic.h similarity index 100% rename from lib/NeoPixelBus-2.2.9/src/internal/NeoMosaic.h rename to lib/NeoPixelBus-2.5.0.09/src/internal/NeoMosaic.h diff --git a/lib/NeoPixelBus-2.2.9/src/internal/NeoPixelAnimator.cpp b/lib/NeoPixelBus-2.5.0.09/src/internal/NeoPixelAnimator.cpp similarity index 84% rename from lib/NeoPixelBus-2.2.9/src/internal/NeoPixelAnimator.cpp rename to lib/NeoPixelBus-2.5.0.09/src/internal/NeoPixelAnimator.cpp index db51a5d4a..d535316f6 100644 --- a/lib/NeoPixelBus-2.2.9/src/internal/NeoPixelAnimator.cpp +++ b/lib/NeoPixelBus-2.5.0.09/src/internal/NeoPixelAnimator.cpp @@ -142,7 +142,7 @@ void NeoPixelAnimator::UpdateAnimations() if (pAnim->_remaining > delta) { param.state = (pAnim->_remaining == pAnim->_duration) ? AnimationState_Started : AnimationState_Progress; - param.progress = (float)(pAnim->_duration - pAnim->_remaining) / (float)pAnim->_duration; + param.progress = pAnim->CurrentProgress(); fnUpdate(param); @@ -164,3 +164,33 @@ void NeoPixelAnimator::UpdateAnimations() } } } + +void NeoPixelAnimator::ChangeAnimationDuration(uint16_t indexAnimation, uint16_t newDuration) +{ + if (indexAnimation >= _countAnimations) + { + return; + } + + AnimationContext* pAnim = &_animations[indexAnimation]; + + // calc the current animation progress + float progress = pAnim->CurrentProgress(); + + // keep progress in range just in case + if (progress < 0.0f) + { + progress = 0.0f; + } + else if (progress > 1.0f) + { + progress = 1.0f; + } + + // change the duration + pAnim->_duration = newDuration; + + // _remaining time must also be reset after a duration change; + // use the progress to recalculate it + pAnim->_remaining = uint16_t(pAnim->_duration * (1.0f - progress)); +} \ No newline at end of file diff --git a/lib/NeoPixelBus-2.2.9/src/internal/NeoPixelAvr.c b/lib/NeoPixelBus-2.5.0.09/src/internal/NeoPixelAvr.c similarity index 99% rename from lib/NeoPixelBus-2.2.9/src/internal/NeoPixelAvr.c rename to lib/NeoPixelBus-2.5.0.09/src/internal/NeoPixelAvr.c index 3d057e27a..761fa7eb1 100644 --- a/lib/NeoPixelBus-2.2.9/src/internal/NeoPixelAvr.c +++ b/lib/NeoPixelBus-2.5.0.09/src/internal/NeoPixelAvr.c @@ -64,7 +64,7 @@ void send_pixels_8mhz_800_PortD(uint8_t* pixels, size_t sizePixels, uint8_t pinM volatile uint8_t lo; // PORT w/output bit set low volatile uint8_t n1; - volatile n2 = 0; // First, next bits out + volatile uint8_t n2 = 0; // First, next bits out // Squeezing an 800 KHz stream out of an 8 MHz chip requires code // specific to each PORT register. At present this is only written @@ -189,7 +189,7 @@ void send_pixels_8mhz_800_PortB(uint8_t* pixels, size_t sizePixels, uint8_t pinM volatile uint8_t lo; // PORT w/output bit set low volatile uint8_t n1; - volatile n2 = 0; // First, next bits out + volatile uint8_t n2 = 0; // First, next bits out // Same as above, just switched to PORTB and stripped of comments. hi = PORTB | pinMask; @@ -645,4 +645,4 @@ void send_pixels_16mhz_400(uint8_t* pixels, size_t sizePixels, volatile uint8_t* #error "CPU SPEED NOT SUPPORTED" #endif -#endif \ No newline at end of file +#endif diff --git a/lib/NeoPixelBus-2.5.0.09/src/internal/NeoPixelEsp.c b/lib/NeoPixelBus-2.5.0.09/src/internal/NeoPixelEsp.c new file mode 100644 index 000000000..f28a5755e --- /dev/null +++ b/lib/NeoPixelBus-2.5.0.09/src/internal/NeoPixelEsp.c @@ -0,0 +1,169 @@ +/*------------------------------------------------------------------------- +NeoPixel library helper functions for Esp8266 and Esp32. + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) + +#include +#if defined(ARDUINO_ARCH_ESP8266) +#include +#endif + +// ESP32 doesn't define ICACHE_RAM_ATTR +#ifndef ICACHE_RAM_ATTR +#define ICACHE_RAM_ATTR IRAM_ATTR +#endif + +static uint32_t _getCycleCount(void) __attribute__((always_inline)); + +static inline uint32_t _getCycleCount(void) +{ + uint32_t ccount; + __asm__ __volatile__("rsr %0,ccount":"=a" (ccount)); + return ccount; +} + +#define CYCLES_800_T0H (F_CPU / 2500000) // 0.4us +#define CYCLES_800_T1H (F_CPU / 1250000) // 0.8us +#define CYCLES_800 (F_CPU / 800000) // 1.25us per bit +#define CYCLES_400_T0H (F_CPU / 2000000) +#define CYCLES_400_T1H (F_CPU / 833333) +#define CYCLES_400 (F_CPU / 400000) + +void ICACHE_RAM_ATTR bitbang_send_pixels_800(uint8_t* pixels, uint8_t* end, uint8_t pin) +{ + const uint32_t pinRegister = _BV(pin); + uint8_t mask = 0x80; + uint8_t subpix = *pixels++; + uint32_t cyclesStart = 0; // trigger emediately + uint32_t cyclesNext = 0; + + for (;;) + { + // do the checks here while we are waiting on time to pass + uint32_t cyclesBit = CYCLES_800_T0H; + if (subpix & mask) + { + cyclesBit = CYCLES_800_T1H; + } + + // after we have done as much work as needed for this next bit + // now wait for the HIGH + while (((cyclesStart = _getCycleCount()) - cyclesNext) < CYCLES_800); + + // set high +#if defined(ARDUINO_ARCH_ESP32) + GPIO.out_w1ts = pinRegister; +#else + GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, pinRegister); +#endif + + // wait for the LOW + while ((_getCycleCount() - cyclesStart) < cyclesBit); + + // set low +#if defined(ARDUINO_ARCH_ESP32) + GPIO.out_w1tc = pinRegister; +#else + GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, pinRegister); +#endif + cyclesNext = cyclesStart; + + // next bit + mask >>= 1; + if (mask == 0) + { + // no more bits to send in this byte + // check for another byte + if (pixels >= end) + { + // no more bytes to send so stop + break; + } + // reset mask to first bit and get the next byte + mask = 0x80; + subpix = *pixels++; + } + } +} + +void ICACHE_RAM_ATTR bitbang_send_pixels_400(uint8_t* pixels, uint8_t* end, uint8_t pin) +{ + const uint32_t pinRegister = _BV(pin); + uint8_t mask = 0x80; + uint8_t subpix = *pixels++; + uint32_t cyclesStart = 0; // trigger emediately + uint32_t cyclesNext = 0; + + for (;;) + { + // do the checks here while we are waiting on time to pass + uint32_t cyclesBit = CYCLES_400_T0H; + if (subpix & mask) + { + cyclesBit = CYCLES_400_T1H; + } + + // after we have done as much work as needed for this next bit + // now wait for the HIGH + while (((cyclesStart = _getCycleCount()) - cyclesNext) < CYCLES_400); + + // set high +#if defined(ARDUINO_ARCH_ESP32) + GPIO.out_w1ts = pinRegister; +#else + GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, pinRegister); +#endif + + // wait for the LOW + while ((_getCycleCount() - cyclesStart) < cyclesBit); + + // set low +#if defined(ARDUINO_ARCH_ESP32) + GPIO.out_w1tc = pinRegister; +#else + GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, pinRegister); +#endif + cyclesNext = cyclesStart; + + // next bit + mask >>= 1; + if (mask == 0) + { + // no more bits to send in this byte + // check for another byte + if (pixels >= end) + { + // no more bytes to send so stop + break; + } + // reset mask to first bit and get the next byte + mask = 0x80; + subpix = *pixels++; + } + } +} + +#endif diff --git a/lib/NeoPixelBus-2.2.9/src/internal/NeoRingTopology.h b/lib/NeoPixelBus-2.5.0.09/src/internal/NeoRingTopology.h similarity index 80% rename from lib/NeoPixelBus-2.2.9/src/internal/NeoRingTopology.h rename to lib/NeoPixelBus-2.5.0.09/src/internal/NeoRingTopology.h index 8e152f8c5..e598fa462 100644 --- a/lib/NeoPixelBus-2.2.9/src/internal/NeoRingTopology.h +++ b/lib/NeoPixelBus-2.5.0.09/src/internal/NeoRingTopology.h @@ -56,6 +56,33 @@ public: return _map(ring, pixel); } + uint16_t RingPixelShift(uint8_t ring, uint16_t pixel, int16_t shift) + { + int32_t ringPixel = pixel; + ringPixel += shift; + + if (ringPixel < 0) + { + ringPixel = 0; + } + else + { + uint16_t count = getPixelCountAtRing(ring); + if (ringPixel >= count) + { + ringPixel = count - 1; + } + } + return ringPixel; + } + + uint16_t RingPixelRotate(uint8_t ring, uint16_t pixel, int16_t rotate) + { + int32_t ringPixel = pixel; + ringPixel += rotate; + return ringPixel % getPixelCountAtRing(ring); + } + uint8_t getCountOfRings() const { return _ringCount() - 1; // minus one as the Rings includes the extra value diff --git a/lib/NeoPixelBus-2.2.9/src/internal/NeoSpriteSheet.h b/lib/NeoPixelBus-2.5.0.09/src/internal/NeoSpriteSheet.h similarity index 97% rename from lib/NeoPixelBus-2.2.9/src/internal/NeoSpriteSheet.h rename to lib/NeoPixelBus-2.5.0.09/src/internal/NeoSpriteSheet.h index a0f277eb9..83fb978d3 100644 --- a/lib/NeoPixelBus-2.2.9/src/internal/NeoSpriteSheet.h +++ b/lib/NeoPixelBus-2.5.0.09/src/internal/NeoSpriteSheet.h @@ -146,15 +146,15 @@ private: uint16_t pixelIndex(uint16_t indexSprite, int16_t x, - int16_t y) + int16_t y) const { uint16_t result = PixelIndex_OutOfBounds; if (indexSprite < _spriteCount && x >= 0 && - x < SpriteWidth() && + (uint16_t)x < SpriteWidth() && y >= 0 && - y < SpriteHeight()) + (uint16_t)y < SpriteHeight()) { result = x + y * SpriteWidth() + indexSprite * _spriteHeight * SpriteWidth(); } diff --git a/lib/NeoPixelBus-2.2.9/src/internal/NeoTiles.h b/lib/NeoPixelBus-2.5.0.09/src/internal/NeoTiles.h similarity index 100% rename from lib/NeoPixelBus-2.2.9/src/internal/NeoTiles.h rename to lib/NeoPixelBus-2.5.0.09/src/internal/NeoTiles.h diff --git a/lib/NeoPixelBus-2.2.9/src/internal/NeoTopology.h b/lib/NeoPixelBus-2.5.0.09/src/internal/NeoTopology.h similarity index 100% rename from lib/NeoPixelBus-2.2.9/src/internal/NeoTopology.h rename to lib/NeoPixelBus-2.5.0.09/src/internal/NeoTopology.h diff --git a/lib/NeoPixelBus-2.2.9/src/internal/RgbColor.cpp b/lib/NeoPixelBus-2.5.0.09/src/internal/RgbColor.cpp similarity index 100% rename from lib/NeoPixelBus-2.2.9/src/internal/RgbColor.cpp rename to lib/NeoPixelBus-2.5.0.09/src/internal/RgbColor.cpp diff --git a/lib/NeoPixelBus-2.2.9/src/internal/RgbColor.h b/lib/NeoPixelBus-2.5.0.09/src/internal/RgbColor.h similarity index 100% rename from lib/NeoPixelBus-2.2.9/src/internal/RgbColor.h rename to lib/NeoPixelBus-2.5.0.09/src/internal/RgbColor.h diff --git a/lib/NeoPixelBus-2.2.9/src/internal/RgbwColor.cpp b/lib/NeoPixelBus-2.5.0.09/src/internal/RgbwColor.cpp similarity index 100% rename from lib/NeoPixelBus-2.2.9/src/internal/RgbwColor.cpp rename to lib/NeoPixelBus-2.5.0.09/src/internal/RgbwColor.cpp diff --git a/lib/NeoPixelBus-2.2.9/src/internal/RgbwColor.h b/lib/NeoPixelBus-2.5.0.09/src/internal/RgbwColor.h similarity index 100% rename from lib/NeoPixelBus-2.2.9/src/internal/RgbwColor.h rename to lib/NeoPixelBus-2.5.0.09/src/internal/RgbwColor.h diff --git a/lib/TasmotaModbus-1.1.0/README.md b/lib/TasmotaModbus-1.2.0/README.md similarity index 100% rename from lib/TasmotaModbus-1.1.0/README.md rename to lib/TasmotaModbus-1.2.0/README.md diff --git a/lib/TasmotaModbus-1.1.0/examples/modbustest/modbustest.ino b/lib/TasmotaModbus-1.2.0/examples/modbustest/modbustest.ino similarity index 100% rename from lib/TasmotaModbus-1.1.0/examples/modbustest/modbustest.ino rename to lib/TasmotaModbus-1.2.0/examples/modbustest/modbustest.ino diff --git a/lib/TasmotaModbus-1.1.0/keywords.txt b/lib/TasmotaModbus-1.2.0/keywords.txt similarity index 100% rename from lib/TasmotaModbus-1.1.0/keywords.txt rename to lib/TasmotaModbus-1.2.0/keywords.txt diff --git a/lib/TasmotaModbus-1.1.0/library.json b/lib/TasmotaModbus-1.2.0/library.json similarity index 93% rename from lib/TasmotaModbus-1.1.0/library.json rename to lib/TasmotaModbus-1.2.0/library.json index d983bec32..c9639e164 100644 --- a/lib/TasmotaModbus-1.1.0/library.json +++ b/lib/TasmotaModbus-1.2.0/library.json @@ -1,6 +1,6 @@ { "name": "TasmotaModbus", - "version": "1.1.0", + "version": "1.2.0", "keywords": [ "serial", "io", "TasmotaModbus" ], diff --git a/lib/TasmotaModbus-1.1.0/library.properties b/lib/TasmotaModbus-1.2.0/library.properties similarity index 93% rename from lib/TasmotaModbus-1.1.0/library.properties rename to lib/TasmotaModbus-1.2.0/library.properties index bb42fb372..9197e7cfe 100644 --- a/lib/TasmotaModbus-1.1.0/library.properties +++ b/lib/TasmotaModbus-1.2.0/library.properties @@ -1,5 +1,5 @@ name=TasmotaModbus -version=1.1.0 +version=1.2.0 author=Theo Arends maintainer=Theo Arends sentence=Basic modbus wrapper for TasmotaSerial for ESP8266. diff --git a/lib/TasmotaModbus-1.1.0/src/TasmotaModbus.cpp b/lib/TasmotaModbus-1.2.0/src/TasmotaModbus.cpp similarity index 67% rename from lib/TasmotaModbus-1.1.0/src/TasmotaModbus.cpp rename to lib/TasmotaModbus-1.2.0/src/TasmotaModbus.cpp index 207fc07f8..dde74fd7c 100644 --- a/lib/TasmotaModbus-1.1.0/src/TasmotaModbus.cpp +++ b/lib/TasmotaModbus-1.2.0/src/TasmotaModbus.cpp @@ -61,10 +61,10 @@ void TasmotaModbus::Send(uint8_t device_address, uint8_t function_code, uint16_t frame[0] = mb_address; // 0xFE default device address or dedicated like 0x01 frame[1] = function_code; - frame[2] = (uint8_t)(start_address >> 8); - frame[3] = (uint8_t)(start_address); - frame[4] = (uint8_t)(register_count >> 8); - frame[5] = (uint8_t)(register_count); + frame[2] = (uint8_t)(start_address >> 8); // MSB + frame[3] = (uint8_t)(start_address); // LSB + frame[4] = (uint8_t)(register_count >> 8); // MSB + frame[5] = (uint8_t)(register_count); // LSB uint16_t crc = CalculateCRC(frame, 6); frame[6] = (uint8_t)(crc); frame[7] = (uint8_t)(crc >> 8); @@ -80,32 +80,48 @@ bool TasmotaModbus::ReceiveReady() uint8_t TasmotaModbus::ReceiveBuffer(uint8_t *buffer, uint8_t register_count) { - uint8_t len = 0; + mb_len = 0; uint32_t last = millis(); - while ((available() > 0) && (len < (register_count *2) + 5) && (millis() - last < 10)) { + while ((available() > 0) && (mb_len < (register_count *2) + 5) && (millis() - last < 10)) { uint8_t data = (uint8_t)read(); - if (!len) { // Skip leading data as provided by hardware serial + if (!mb_len) { // Skip leading data as provided by hardware serial if (mb_address == data) { - buffer[len++] = data; + buffer[mb_len++] = data; } } else { - buffer[len++] = data; - if (3 == len) { + buffer[mb_len++] = data; + if (3 == mb_len) { if (buffer[1] & 0x80) { // 01 84 02 f2 f1 - return buffer[2]; // 1 = Illegal Function, 2 = Illegal Address, 3 = Illegal Data, 4 = Slave Error + return buffer[2]; // 1 = Illegal Function, + // 2 = Illegal Data Address, + // 3 = Illegal Data Value, + // 4 = Slave Error + // 5 = Acknowledge but not finished (no error) + // 6 = Slave Busy + // 8 = Memory Parity error + // 10 = Gateway Path Unavailable + // 11 = Gateway Target device failed to respond } } } last = millis(); } - if (len < 7) { return 7; } // 7 = Not enough data - if (len != buffer[2] + 5) { return 8; } // 8 = Unexpected result + if (mb_len < 7) { return 7; } // 7 = Not enough data - uint16_t crc = (buffer[len -1] << 8) | buffer[len -2]; - if (CalculateCRC(buffer, len -2) != crc) { return 9; } // 9 = crc error +/* + if (mb_len != buffer[2] + 5) { + buffer[2] = mb_len - 5; // As it's wrong anyway let's store actual number received in here (5 will be added by client) + return 3; // 3 = Unexpected result + } +*/ - return 0; // 0 = No error + uint16_t crc = (buffer[mb_len -1] << 8) | buffer[mb_len -2]; + if (CalculateCRC(buffer, mb_len -2) != crc) { + return 9; // 9 = crc error + } + + return 0; // 0 = No error } uint8_t TasmotaModbus::Receive16BitRegister(uint16_t *value) diff --git a/lib/TasmotaModbus-1.1.0/src/TasmotaModbus.h b/lib/TasmotaModbus-1.2.0/src/TasmotaModbus.h similarity index 76% rename from lib/TasmotaModbus-1.1.0/src/TasmotaModbus.h rename to lib/TasmotaModbus-1.2.0/src/TasmotaModbus.h index 5176aa89e..2138e0c9d 100644 --- a/lib/TasmotaModbus-1.1.0/src/TasmotaModbus.h +++ b/lib/TasmotaModbus-1.2.0/src/TasmotaModbus.h @@ -37,21 +37,28 @@ class TasmotaModbus : public TasmotaSerial { bool ReceiveReady(); /* Return codes: - * 0 - No error - * 1 - Illegal function - * 2 - Illegal address - * 3 - Illegal data - * 4 - Slave error - * 7 - Not enough minimal data received - * 8 - Not enough data receieved - * 9 - Crc error + * 0 = No error + * 1 = Illegal Function, + * 2 = Illegal Data Address, + * 3 = Illegal Data Value, + * 4 = Slave Error + * 5 = Acknowledge but not finished (no error) + * 6 = Slave Busy + * 7 = Not enough minimal data received + * 8 = Memory Parity error + * 9 = Crc error + * 10 = Gateway Path Unavailable + * 11 = Gateway Target device failed to respond */ uint8_t ReceiveBuffer(uint8_t *buffer, uint8_t register_count); uint8_t Receive16BitRegister(uint16_t *value); uint8_t Receive32BitRegister(float *value); + uint8_t ReceiveCount(void) { return mb_len; } + private: uint8_t mb_address; + uint8_t mb_len; }; #endif // TasmotaModbus_h diff --git a/lib/TasmotaSerial-2.3.1/src/TasmotaSerial.cpp b/lib/TasmotaSerial-2.3.1/src/TasmotaSerial.cpp deleted file mode 100644 index d99137edc..000000000 --- a/lib/TasmotaSerial-2.3.1/src/TasmotaSerial.cpp +++ /dev/null @@ -1,268 +0,0 @@ -/* - TasmotaSerial.cpp - Minimal implementation of software serial for Tasmota - - Copyright (C) 2019 Theo Arends - - This library is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#include - -// The Arduino standard GPIO routines are not enough, -// must use some from the Espressif SDK as well -extern "C" { -#include "gpio.h" -} - -#include - -// As the Arduino attachInterrupt has no parameter, lists of objects -// and callbacks corresponding to each possible GPIO pins have to be defined -TasmotaSerial *tms_obj_list[16]; - -#ifdef TM_SERIAL_USE_IRAM -void ICACHE_RAM_ATTR tms_isr_0() { tms_obj_list[0]->rxRead(); }; -void ICACHE_RAM_ATTR tms_isr_1() { tms_obj_list[1]->rxRead(); }; -void ICACHE_RAM_ATTR tms_isr_2() { tms_obj_list[2]->rxRead(); }; -void ICACHE_RAM_ATTR tms_isr_3() { tms_obj_list[3]->rxRead(); }; -void ICACHE_RAM_ATTR tms_isr_4() { tms_obj_list[4]->rxRead(); }; -void ICACHE_RAM_ATTR tms_isr_5() { tms_obj_list[5]->rxRead(); }; -// Pin 6 to 11 can not be used -void ICACHE_RAM_ATTR tms_isr_12() { tms_obj_list[12]->rxRead(); }; -void ICACHE_RAM_ATTR tms_isr_13() { tms_obj_list[13]->rxRead(); }; -void ICACHE_RAM_ATTR tms_isr_14() { tms_obj_list[14]->rxRead(); }; -void ICACHE_RAM_ATTR tms_isr_15() { tms_obj_list[15]->rxRead(); }; -#else -void tms_isr_0() { tms_obj_list[0]->rxRead(); }; -void tms_isr_1() { tms_obj_list[1]->rxRead(); }; -void tms_isr_2() { tms_obj_list[2]->rxRead(); }; -void tms_isr_3() { tms_obj_list[3]->rxRead(); }; -void tms_isr_4() { tms_obj_list[4]->rxRead(); }; -void tms_isr_5() { tms_obj_list[5]->rxRead(); }; -// Pin 6 to 11 can not be used -void tms_isr_12() { tms_obj_list[12]->rxRead(); }; -void tms_isr_13() { tms_obj_list[13]->rxRead(); }; -void tms_isr_14() { tms_obj_list[14]->rxRead(); }; -void tms_isr_15() { tms_obj_list[15]->rxRead(); }; -#endif // TM_SERIAL_USE_IRAM - -static void (*ISRList[16])() = { - tms_isr_0, - tms_isr_1, - tms_isr_2, - tms_isr_3, - tms_isr_4, - tms_isr_5, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - tms_isr_12, - tms_isr_13, - tms_isr_14, - tms_isr_15 -}; - -TasmotaSerial::TasmotaSerial(int receive_pin, int transmit_pin, int hardware_fallback) -{ - m_valid = false; - m_hardserial = 0; - m_hardswap = 0; - m_stop_bits = 1; - if (!((isValidGPIOpin(receive_pin)) && (isValidGPIOpin(transmit_pin) || transmit_pin == 16))) { - return; - } - m_rx_pin = receive_pin; - m_tx_pin = transmit_pin; - m_in_pos = m_out_pos = 0; - if (hardware_fallback && (((3 == m_rx_pin) && (1 == m_tx_pin)) || ((3 == m_rx_pin) && (-1 == m_tx_pin)) || ((-1 == m_rx_pin) && (1 == m_tx_pin)))) { - m_hardserial = 1; - } - else if ((2 == hardware_fallback) && (((13 == m_rx_pin) && (15 == m_tx_pin)) || ((13 == m_rx_pin) && (-1 == m_tx_pin)) || ((-1 == m_rx_pin) && (15 == m_tx_pin)))) { - m_hardserial = 1; - m_hardswap = 1; - } - else { - if (m_rx_pin > -1) { - m_buffer = (uint8_t*)malloc(TM_SERIAL_BUFFER_SIZE); - if (m_buffer == NULL) return; - // Use getCycleCount() loop to get as exact timing as possible - m_bit_time = ESP.getCpuFreqMHz() * 1000000 / TM_SERIAL_BAUDRATE; - pinMode(m_rx_pin, INPUT); - tms_obj_list[m_rx_pin] = this; - attachInterrupt(m_rx_pin, ISRList[m_rx_pin], FALLING); - } - if (m_tx_pin > -1) { - pinMode(m_tx_pin, OUTPUT); - digitalWrite(m_tx_pin, HIGH); - } - } - m_valid = true; -} - -TasmotaSerial::~TasmotaSerial() -{ - if (!m_hardserial) { - if (m_rx_pin > -1) { - detachInterrupt(m_rx_pin); - tms_obj_list[m_rx_pin] = NULL; - if (m_buffer) { - free(m_buffer); - } - } - } -} - -bool TasmotaSerial::isValidGPIOpin(int pin) -{ - return (pin >= -1 && pin <= 5) || (pin >= 12 && pin <= 15); -} - -bool TasmotaSerial::begin(long speed, int stop_bits) { - m_stop_bits = ((stop_bits -1) &1) +1; - if (m_hardserial) { - Serial.flush(); - if (2 == m_stop_bits) { - Serial.begin(speed, SERIAL_8N2); - } else { - Serial.begin(speed, SERIAL_8N1); - } - if (m_hardswap) { - Serial.swap(); - } - } else { - // Use getCycleCount() loop to get as exact timing as possible - m_bit_time = ESP.getCpuFreqMHz() * 1000000 / speed; - m_high_speed = (speed >= 9600); - } - return m_valid; -} - -bool TasmotaSerial::begin() { - return begin(TM_SERIAL_BAUDRATE); -} - -bool TasmotaSerial::hardwareSerial() { - return m_hardserial; -} - -void TasmotaSerial::flush() { - if (m_hardserial) { - Serial.flush(); - } else { - m_in_pos = m_out_pos = 0; - } -} - -int TasmotaSerial::peek() { - if (m_hardserial) { - return Serial.peek(); - } else { - if ((-1 == m_rx_pin) || (m_in_pos == m_out_pos)) return -1; - return m_buffer[m_out_pos]; - } -} - -int TasmotaSerial::read() -{ - if (m_hardserial) { - return Serial.read(); - } else { - if ((-1 == m_rx_pin) || (m_in_pos == m_out_pos)) return -1; - uint8_t ch = m_buffer[m_out_pos]; - m_out_pos = (m_out_pos +1) % TM_SERIAL_BUFFER_SIZE; - return ch; - } -} - -int TasmotaSerial::available() -{ - if (m_hardserial) { - return Serial.available(); - } else { - int avail = m_in_pos - m_out_pos; - if (avail < 0) avail += TM_SERIAL_BUFFER_SIZE; - return avail; - } -} - -#ifdef TM_SERIAL_USE_IRAM -#define TM_SERIAL_WAIT { while (ESP.getCycleCount()-start < wait) if (!m_high_speed) optimistic_yield(1); wait += m_bit_time; } // Watchdog timeouts -#else -#define TM_SERIAL_WAIT { while (ESP.getCycleCount()-start < wait); wait += m_bit_time; } -#endif - -size_t TasmotaSerial::write(uint8_t b) -{ - if (m_hardserial) { - return Serial.write(b); - } else { - if (-1 == m_tx_pin) return 0; - if (m_high_speed) cli(); // Disable interrupts in order to get a clean transmit - unsigned long wait = m_bit_time; - digitalWrite(m_tx_pin, HIGH); - unsigned long start = ESP.getCycleCount(); - // Start bit; - digitalWrite(m_tx_pin, LOW); - TM_SERIAL_WAIT; - for (int i = 0; i < 8; i++) { - digitalWrite(m_tx_pin, (b & 1) ? HIGH : LOW); - TM_SERIAL_WAIT; - b >>= 1; - } - // Stop bit(s) - for (int i = 0; i < m_stop_bits; i++) { - digitalWrite(m_tx_pin, HIGH); - TM_SERIAL_WAIT; - } - if (m_high_speed) sei(); - return 1; - } -} - -#ifdef TM_SERIAL_USE_IRAM -void ICACHE_RAM_ATTR TasmotaSerial::rxRead() -{ -#else -void TasmotaSerial::rxRead() -{ -#endif - // Advance the starting point for the samples but compensate for the - // initial delay which occurs before the interrupt is delivered - unsigned long wait = m_bit_time + m_bit_time/3 - 500; - unsigned long start = ESP.getCycleCount(); - uint8_t rec = 0; - for (int i = 0; i < 8; i++) { - TM_SERIAL_WAIT; - rec >>= 1; - if (digitalRead(m_rx_pin)) rec |= 0x80; - } - // Stop bit(s) - TM_SERIAL_WAIT; - if (2 == m_stop_bits) { - digitalRead(m_rx_pin); - TM_SERIAL_WAIT; - } - // Store the received value in the buffer unless we have an overflow - unsigned int next = (m_in_pos+1) % TM_SERIAL_BUFFER_SIZE; - if (next != (int)m_out_pos) { - m_buffer[m_in_pos] = rec; - m_in_pos = next; - } - // Must clear this bit in the interrupt register, - // it gets set even when interrupts are disabled - GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, 1 << m_rx_pin); -} diff --git a/lib/TasmotaSerial-2.3.1/README.md b/lib/TasmotaSerial-2.4.1/README.md similarity index 100% rename from lib/TasmotaSerial-2.3.1/README.md rename to lib/TasmotaSerial-2.4.1/README.md diff --git a/lib/TasmotaSerial-2.3.1/examples/swsertest/swsertest.ino b/lib/TasmotaSerial-2.4.1/examples/swsertest/swsertest.ino similarity index 100% rename from lib/TasmotaSerial-2.3.1/examples/swsertest/swsertest.ino rename to lib/TasmotaSerial-2.4.1/examples/swsertest/swsertest.ino diff --git a/lib/TasmotaSerial-2.3.1/keywords.txt b/lib/TasmotaSerial-2.4.1/keywords.txt similarity index 100% rename from lib/TasmotaSerial-2.3.1/keywords.txt rename to lib/TasmotaSerial-2.4.1/keywords.txt diff --git a/lib/TasmotaSerial-2.3.1/library.json b/lib/TasmotaSerial-2.4.1/library.json similarity index 94% rename from lib/TasmotaSerial-2.3.1/library.json rename to lib/TasmotaSerial-2.4.1/library.json index fad36bcc6..554d9ea9e 100644 --- a/lib/TasmotaSerial-2.3.1/library.json +++ b/lib/TasmotaSerial-2.4.1/library.json @@ -1,6 +1,6 @@ { "name": "TasmotaSerial", - "version": "2.3.0", + "version": "2.4.1", "keywords": [ "serial", "io", "TasmotaSerial" ], diff --git a/lib/TasmotaSerial-2.3.1/library.properties b/lib/TasmotaSerial-2.4.1/library.properties similarity index 94% rename from lib/TasmotaSerial-2.3.1/library.properties rename to lib/TasmotaSerial-2.4.1/library.properties index 095077d8e..b326d7404 100644 --- a/lib/TasmotaSerial-2.3.1/library.properties +++ b/lib/TasmotaSerial-2.4.1/library.properties @@ -1,5 +1,5 @@ name=TasmotaSerial -version=2.3.0 +version=2.4.1 author=Theo Arends maintainer=Theo Arends sentence=Implementation of software serial with hardware serial fallback for ESP8266. diff --git a/lib/TasmotaSerial-2.4.1/src/TasmotaSerial.cpp b/lib/TasmotaSerial-2.4.1/src/TasmotaSerial.cpp new file mode 100644 index 000000000..45a1d47af --- /dev/null +++ b/lib/TasmotaSerial-2.4.1/src/TasmotaSerial.cpp @@ -0,0 +1,397 @@ +/* + TasmotaSerial.cpp - Minimal implementation of software serial for Tasmota + + Copyright (C) 2019 Theo Arends + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include + +// The Arduino standard GPIO routines are not enough, +// must use some from the Espressif SDK as well +extern "C" { +#include "gpio.h" +} + +#include + +// for STAGE and pre-2.6, we can have a single wrapper using attachInterruptArg() +void ICACHE_RAM_ATTR callRxRead(void *self) { ((TasmotaSerial*)self)->rxRead(); }; + +// As the Arduino attachInterrupt has no parameter, lists of objects +// and callbacks corresponding to each possible GPIO pins have to be defined +TasmotaSerial *tms_obj_list[16]; + +#ifdef TM_SERIAL_USE_IRAM +void ICACHE_RAM_ATTR tms_isr_0() { tms_obj_list[0]->rxRead(); }; +void ICACHE_RAM_ATTR tms_isr_1() { tms_obj_list[1]->rxRead(); }; +void ICACHE_RAM_ATTR tms_isr_2() { tms_obj_list[2]->rxRead(); }; +void ICACHE_RAM_ATTR tms_isr_3() { tms_obj_list[3]->rxRead(); }; +void ICACHE_RAM_ATTR tms_isr_4() { tms_obj_list[4]->rxRead(); }; +void ICACHE_RAM_ATTR tms_isr_5() { tms_obj_list[5]->rxRead(); }; +// Pin 6 to 11 can not be used +void ICACHE_RAM_ATTR tms_isr_12() { tms_obj_list[12]->rxRead(); }; +void ICACHE_RAM_ATTR tms_isr_13() { tms_obj_list[13]->rxRead(); }; +void ICACHE_RAM_ATTR tms_isr_14() { tms_obj_list[14]->rxRead(); }; +void ICACHE_RAM_ATTR tms_isr_15() { tms_obj_list[15]->rxRead(); }; +#else +void tms_isr_0() { tms_obj_list[0]->rxRead(); }; +void tms_isr_1() { tms_obj_list[1]->rxRead(); }; +void tms_isr_2() { tms_obj_list[2]->rxRead(); }; +void tms_isr_3() { tms_obj_list[3]->rxRead(); }; +void tms_isr_4() { tms_obj_list[4]->rxRead(); }; +void tms_isr_5() { tms_obj_list[5]->rxRead(); }; +// Pin 6 to 11 can not be used +void tms_isr_12() { tms_obj_list[12]->rxRead(); }; +void tms_isr_13() { tms_obj_list[13]->rxRead(); }; +void tms_isr_14() { tms_obj_list[14]->rxRead(); }; +void tms_isr_15() { tms_obj_list[15]->rxRead(); }; +#endif // TM_SERIAL_USE_IRAM + +static void (*ISRList[16])() = { + tms_isr_0, + tms_isr_1, + tms_isr_2, + tms_isr_3, + tms_isr_4, + tms_isr_5, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + tms_isr_12, + tms_isr_13, + tms_isr_14, + tms_isr_15 +}; + +TasmotaSerial::TasmotaSerial(int receive_pin, int transmit_pin, int hardware_fallback, int nwmode, int buffer_size) +{ + m_valid = false; + m_hardserial = false; + m_hardswap = false; + m_stop_bits = 1; + m_nwmode = nwmode; + serial_buffer_size = buffer_size; + if (!((isValidGPIOpin(receive_pin)) && (isValidGPIOpin(transmit_pin) || transmit_pin == 16))) { + return; + } + m_rx_pin = receive_pin; + m_tx_pin = transmit_pin; + m_in_pos = m_out_pos = 0; + if (hardware_fallback && (((3 == m_rx_pin) && (1 == m_tx_pin)) || ((3 == m_rx_pin) && (-1 == m_tx_pin)) || ((-1 == m_rx_pin) && (1 == m_tx_pin)))) { + m_hardserial = true; + } + else if ((2 == hardware_fallback) && (((13 == m_rx_pin) && (15 == m_tx_pin)) || ((13 == m_rx_pin) && (-1 == m_tx_pin)) || ((-1 == m_rx_pin) && (15 == m_tx_pin)))) { + m_hardserial = true; + m_hardswap = true; + } + else { + if (m_rx_pin > -1) { + m_buffer = (uint8_t*)malloc(serial_buffer_size); + if (m_buffer == NULL) return; + // Use getCycleCount() loop to get as exact timing as possible + m_bit_time = ESP.getCpuFreqMHz() * 1000000 / TM_SERIAL_BAUDRATE; + m_bit_start_time = m_bit_time + m_bit_time/3 - 500; // pre-compute first wait + pinMode(m_rx_pin, INPUT); + tms_obj_list[m_rx_pin] = this; +#if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_2) || defined(ARDUINO_ESP8266_RELEASE_2_5_2) + attachInterrupt(m_rx_pin, ISRList[m_rx_pin], (m_nwmode) ? CHANGE : FALLING); +#else + attachInterruptArg(m_rx_pin, callRxRead, this, (m_nwmode) ? CHANGE : FALLING); +#endif + } + if (m_tx_pin > -1) { + pinMode(m_tx_pin, OUTPUT); + digitalWrite(m_tx_pin, HIGH); + } + } + m_valid = true; +} + +TasmotaSerial::~TasmotaSerial() +{ + if (!m_hardserial) { + if (m_rx_pin > -1) { + detachInterrupt(m_rx_pin); + tms_obj_list[m_rx_pin] = NULL; + if (m_buffer) { + free(m_buffer); + } + } + } +} + +bool TasmotaSerial::isValidGPIOpin(int pin) +{ + return (pin >= -1 && pin <= 5) || (pin >= 12 && pin <= 15); +} + +bool TasmotaSerial::begin(long speed, int stop_bits) { + m_stop_bits = ((stop_bits -1) &1) +1; + if (m_hardserial) { + Serial.flush(); + if (2 == m_stop_bits) { + Serial.begin(speed, SERIAL_8N2); + } else { + Serial.begin(speed, SERIAL_8N1); + } + if (m_hardswap) { + Serial.swap(); + } + } else { + // Use getCycleCount() loop to get as exact timing as possible + m_bit_time = ESP.getCpuFreqMHz() * 1000000 / speed; + m_bit_start_time = m_bit_time + m_bit_time/3 - (ESP.getCpuFreqMHz() > 120 ? 700 : 500); // pre-compute first wait + m_high_speed = (speed >= 9600); + m_very_high_speed = (speed >= 50000); + } + return m_valid; +} + +bool TasmotaSerial::begin() { + return begin(TM_SERIAL_BAUDRATE); +} + +bool TasmotaSerial::hardwareSerial() { + return m_hardserial; +} + +void TasmotaSerial::flush() { + if (m_hardserial) { + Serial.flush(); + } else { + m_in_pos = m_out_pos = 0; + } +} + +int TasmotaSerial::peek() { + if (m_hardserial) { + return Serial.peek(); + } else { + if ((-1 == m_rx_pin) || (m_in_pos == m_out_pos)) return -1; + return m_buffer[m_out_pos]; + } +} + +int TasmotaSerial::read() +{ + if (m_hardserial) { + return Serial.read(); + } else { + if ((-1 == m_rx_pin) || (m_in_pos == m_out_pos)) return -1; + uint32_t ch = m_buffer[m_out_pos]; + m_out_pos = (m_out_pos +1) % serial_buffer_size; + return ch; + } +} + +int TasmotaSerial::available() +{ + if (m_hardserial) { + return Serial.available(); + } else { + int avail = m_in_pos - m_out_pos; + if (avail < 0) avail += serial_buffer_size; + return avail; + } +} + +#ifdef TM_SERIAL_USE_IRAM +#define TM_SERIAL_WAIT_SND { while (ESP.getCycleCount() < (wait + start)) if (!m_high_speed) optimistic_yield(1); wait += m_bit_time; } // Watchdog timeouts +#define TM_SERIAL_WAIT_SND_FAST { while (ESP.getCycleCount() < (wait + start)); wait += m_bit_time; } +#define TM_SERIAL_WAIT_RCV { while (ESP.getCycleCount() < (wait + start)); wait += m_bit_time; } +#define TM_SERIAL_WAIT_RCV_LOOP { while (ESP.getCycleCount() < (wait + start)); } +#else +#define TM_SERIAL_WAIT_SND { while (ESP.getCycleCount() < (wait + start)); wait += m_bit_time; } +#define TM_SERIAL_WAIT_SND_FAST { while (ESP.getCycleCount() < (wait + start)); wait += m_bit_time; } +#define TM_SERIAL_WAIT_RCV { while (ESP.getCycleCount() < (wait + start)); wait += m_bit_time; } +#define TM_SERIAL_WAIT_RCV_LOOP { while (ESP.getCycleCount() < (wait + start)); } +#endif + +#ifdef TM_SERIAL_USE_IRAM +void ICACHE_RAM_ATTR TasmotaSerial::_fast_write(uint8_t b) { +#else +void TasmotaSerial::_fast_write(uint8_t b) { +#endif + uint32_t wait = m_bit_time; + uint32_t start = ESP.getCycleCount(); + // Start bit; + digitalWrite(m_tx_pin, LOW); + TM_SERIAL_WAIT_SND_FAST; + for (uint32_t i = 0; i < 8; i++) { + digitalWrite(m_tx_pin, (b & 1) ? HIGH : LOW); + TM_SERIAL_WAIT_SND_FAST; + b >>= 1; + } + // Stop bit(s) + digitalWrite(m_tx_pin, HIGH); + for (uint32_t i = 0; i < m_stop_bits; i++) { + TM_SERIAL_WAIT_SND_FAST; + } +} + +size_t TasmotaSerial::write(uint8_t b) +{ + if (m_hardserial) { + return Serial.write(b); + } else { + if (-1 == m_tx_pin) return 0; + if (m_high_speed) { + cli(); // Disable interrupts in order to get a clean transmit + _fast_write(b); + sei(); + } else { + uint32_t wait = m_bit_time; + //digitalWrite(m_tx_pin, HIGH); // already in HIGH mode + uint32_t start = ESP.getCycleCount(); + // Start bit; + digitalWrite(m_tx_pin, LOW); + TM_SERIAL_WAIT_SND; + for (uint32_t i = 0; i < 8; i++) { + digitalWrite(m_tx_pin, (b & 1) ? HIGH : LOW); + TM_SERIAL_WAIT_SND; + b >>= 1; + } + // Stop bit(s) + digitalWrite(m_tx_pin, HIGH); + // re-enable interrupts during stop bits, it's not an issue if they are longer than expected + for (uint32_t i = 0; i < m_stop_bits; i++) { + TM_SERIAL_WAIT_SND; + } + } + + return 1; + } +} + +#ifdef TM_SERIAL_USE_IRAM +void ICACHE_RAM_ATTR TasmotaSerial::rxRead() +{ +#else +void TasmotaSerial::rxRead() +{ +#endif + if (!m_nwmode) { + int32_t loop_read = m_very_high_speed ? serial_buffer_size : 1; + // Advance the starting point for the samples but compensate for the + // initial delay which occurs before the interrupt is delivered + uint32_t wait = m_bit_start_time; + uint32_t start = ESP.getCycleCount(); + while (loop_read-- > 0) { // try to receveive all consecutive bytes in a raw + uint32_t rec = 0; + for (uint32_t i = 0; i < 8; i++) { + TM_SERIAL_WAIT_RCV; + rec >>= 1; + if (digitalRead(m_rx_pin)) rec |= 0x80; + } + // Store the received value in the buffer unless we have an overflow + uint32_t next = (m_in_pos+1) % serial_buffer_size; + if (next != (int)m_out_pos) { + m_buffer[m_in_pos] = rec; + m_in_pos = next; + } + + TM_SERIAL_WAIT_RCV_LOOP; // wait for stop bit + if (2 == m_stop_bits) { + wait += m_bit_time; + TM_SERIAL_WAIT_RCV_LOOP; + } + wait += m_bit_time / 4; + + if (loop_read <= 0) { break; } // exit now if not very high speed or buffer full + + bool start_of_next_byte = false; + for (uint32_t i = 0; i < 12; i++) { + TM_SERIAL_WAIT_RCV_LOOP; // wait for 1/4 bits + wait += m_bit_time / 4; + if (!digitalRead(m_rx_pin)) { + // this is the start bit of the next byte + wait += m_bit_time; // we have advanced in the first 1/4 of bit, and already added 1/4 of bit so we're roughly centered. Just skip start bit. + start_of_next_byte = true; + m_bit_follow_metric++; + break; // exit loop + } + } + + if (!start_of_next_byte) { + break; // exit now if no sign of next byte + } + } + // Must clear this bit in the interrupt register, + // it gets set even when interrupts are disabled + GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, 1 << m_rx_pin); + } else { + uint32_t diff; + uint32_t level; + + #define LASTBIT 9 + + GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, 1 << m_rx_pin); + + level = digitalRead(m_rx_pin); + + if (!level && !ss_index) { + // start condition + ss_bstart = ESP.getCycleCount() - (m_bit_time / 4); + ss_byte = 0; + ss_index++; + //digitalWrite(1, LOW); + } else { + // now any bit changes go here + // calc bit number + diff = (ESP.getCycleCount() - ss_bstart) / m_bit_time; + //digitalWrite(1, level); + + if (!level && diff > LASTBIT) { + // start bit of next byte, store and restart + // leave irq at change + for (uint32_t i = ss_index; i <= LASTBIT; i++) { + ss_byte |= (1 << i); + } + //stobyte(0,ssp->ss_byte>>1); + uint32_t next = (m_in_pos + 1) % serial_buffer_size; + if (next != (uint32_t)m_out_pos) { + m_buffer[m_in_pos] = ss_byte >> 1; + m_in_pos = next; + } + + ss_bstart = ESP.getCycleCount() - (m_bit_time / 4); + ss_byte = 0; + ss_index = 1; + return; + } + if (diff >= LASTBIT) { + // bit zero was 0, + //stobyte(0,ssp->ss_byte>>1); + uint32_t next = (m_in_pos + 1) % serial_buffer_size; + if (next != (uint32_t)m_out_pos) { + m_buffer[m_in_pos] = ss_byte >> 1; + m_in_pos = next; + } + ss_byte = 0; + ss_index = 0; + } else { + // shift in + for (uint32_t i = ss_index; i < diff; i++) { + if (!level) ss_byte |= (1 << i); + } + ss_index = diff; + } + } + } +} diff --git a/lib/TasmotaSerial-2.3.1/src/TasmotaSerial.h b/lib/TasmotaSerial-2.4.1/src/TasmotaSerial.h similarity index 75% rename from lib/TasmotaSerial-2.3.1/src/TasmotaSerial.h rename to lib/TasmotaSerial-2.4.1/src/TasmotaSerial.h index 9481ef370..81545f522 100644 --- a/lib/TasmotaSerial-2.3.1/src/TasmotaSerial.h +++ b/lib/TasmotaSerial-2.4.1/src/TasmotaSerial.h @@ -20,7 +20,7 @@ #ifndef TasmotaSerial_h #define TasmotaSerial_h /*********************************************************************************************\ - * TasmotaSerial supports up to 115200 baud with fixed buffer size of 64 bytes using optional no iram + * TasmotaSerial supports up to 115200 baud with default buffer size of 64 bytes using optional no iram * * Based on EspSoftwareSerial v3.4.3 by Peter Lerup (https://github.com/plerup/espsoftwareserial) \*********************************************************************************************/ @@ -38,7 +38,7 @@ class TasmotaSerial : public Stream { public: - TasmotaSerial(int receive_pin, int transmit_pin, int hardware_fallback = 0); + TasmotaSerial(int receive_pin, int transmit_pin, int hardware_fallback = 0, int nwmode = 0, int buffer_size = TM_SERIAL_BUFFER_SIZE); virtual ~TasmotaSerial(); bool begin(long speed, int stop_bits = 1); @@ -53,6 +53,8 @@ class TasmotaSerial : public Stream { void rxRead(); + uint32_t getLoopReadMetric(void) const { return m_bit_follow_metric; } + using Print::write; private: @@ -60,17 +62,27 @@ class TasmotaSerial : public Stream { size_t txWrite(uint8_t byte); // Member variables - bool m_valid; - bool m_hardserial; - bool m_hardswap; - bool m_high_speed; int m_rx_pin; int m_tx_pin; - int m_stop_bits; - unsigned long m_bit_time; - unsigned int m_in_pos; - unsigned int m_out_pos; + uint32_t m_stop_bits; + uint32_t ss_byte; + uint32_t ss_bstart; + uint32_t ss_index; + uint32_t m_bit_time; + uint32_t m_bit_start_time; + uint32_t m_bit_follow_metric = 0; + uint32_t m_in_pos; + uint32_t m_out_pos; + uint32_t serial_buffer_size; + bool m_valid; + bool m_nwmode; + bool m_hardserial; + bool m_hardswap; + bool m_high_speed = false; + bool m_very_high_speed = false; // above 100000 bauds uint8_t *m_buffer; + + void _fast_write(uint8_t b); // IRAM minimized version }; #endif // TasmotaSerial_h diff --git a/lib/Xlatb_RA8876-gemu-1.0/RA8876.cpp b/lib/Xlatb_RA8876-gemu-1.0/RA8876.cpp new file mode 100644 index 000000000..a3c73f3fb --- /dev/null +++ b/lib/Xlatb_RA8876-gemu-1.0/RA8876.cpp @@ -0,0 +1,1403 @@ +/*MIT License + +Copyright (c) 2017 xlatb + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include +#include "RA8876.h" +#include + +/* TODO + +font 0 x and y size with line,col cmd +support for rotation +fast picture write + +*/ + +//void SHOW_STAGE(uint8_t stage) { +// Serial.printf(">%d,\n", stage); +// + +const uint16_t RA8876_colors[]={RA8876_BLACK,RA8876_WHITE,RA8876_RED,RA8876_GREEN,RA8876_BLUE,RA8876_CYAN,RA8876_MAGENTA,\ + RA8876_YELLOW,RA8876_NAVY,RA8876_DARKGREEN,RA8876_DARKCYAN,RA8876_MAROON,RA8876_PURPLE,RA8876_OLIVE,\ +RA8876_LIGHTGREY,RA8876_DARKGREY,RA8876_ORANGE,RA8876_GREENYELLOW,RA8876_PINK}; + +uint16_t RA8876::GetColorFromIndex(uint8_t index) { + if (index>=sizeof(RA8876_colors)/2) index=0; + return RA8876_colors[index]; +} + + +// Constructor when using software SPI. All output pins are configurable. +RA8876::RA8876(int8_t cs,int8_t mosi,int8_t miso,int8_t sclk,int8_t bp) : Renderer(RA8876_TFTWIDTH, RA8876_TFTHEIGHT) { + m_csPin = cs; + _mosi = mosi; + _miso = miso; + _sclk = sclk; + /* + _bp = bp; + if (_bp<99) { + pinMode(_bp, OUTPUT); + digitalWrite(_bp,HIGH); + }*/ + _hwspi = 1; +} + + +//#define RA8876_CS_LOW digitalWrite(m_csPin, LOW) +//#define RA8876_CS_HIGH digitalWrite(m_csPin, HIGH) + +#define RA8876_CS_LOW GPOC=(1<> 8); +} + +uint8_t RA8876::readReg(uint8_t reg) { + writeCmd(reg); + return readData(); +} + +// Like readReg(), but does two successive register reads of a 16-bit value, low byte first. +uint16_t RA8876::readReg16(uint8_t reg) { + uint16_t v; + + writeCmd(reg); + v = readData(); + writeCmd(reg + 1); + v |= readData() << 8; + + return v; +} + +// Trigger a soft reset. Note that the data sheet section 19.2 says that this only resets the +// "internal state machine", not any configuration registers. +void RA8876::softReset(void) { + SPI.beginTransaction(m_spiSettings); + + // Trigger soft reset + writeReg(RA8876_REG_SRR, 0x01); + delay(5); + + // Wait for status register to show "normal operation". + uint8_t status; + for (int i = 0; i < 250; i++) { + delay(1); + if (((status = readStatus()) & 0x02) == 0) + break; + } + SPI.endTransaction(); + return; +} + +void RA8876::DisplayOnff(int8_t on) { + uint8_t dpcr; + SPI.beginTransaction(m_spiSettings); + dpcr = readReg(RA8876_REG_DPCR); + + if (on) { + dpcr |= 0x40; // Display on + dim(dimmer); + } else { + dpcr &= 0x40^0xff; // Display off + // backlight off + writeReg(RA8876_REG_TCMPB0L,0); + writeReg(RA8876_REG_TCMPB0H,0); + } + writeReg(RA8876_REG_DPCR, dpcr); + SPI.endTransaction(); +} + +// 0-15 +void RA8876::dim(uint8_t contrast) { + SPI.beginTransaction(m_spiSettings); + + dimmer=contrast; + // pwm0 duty + uint32_t duty=(contrast*1024)/15; + writeReg(RA8876_REG_TCMPB0L,duty); + writeReg(RA8876_REG_TCMPB0H,duty>>8); + SPI.endTransaction(); +} + +// pwm needed for backlight dimmer +void RA8876::PWM_init(void) { + SPI.beginTransaction(m_spiSettings); + writeReg(RA8876_REG_PSCLR,3); + + uint8_t val=RA8876_PWM_TIMER_DIV4<<6|RA8876_PWM_TIMER_DIV4<<4|RA8876_XPWM1_OUTPUT_PWM_TIMER1<<2|RA8876_XPWM0_OUTPUT_PWM_TIMER0; + writeReg(RA8876_REG_PMUXR,val); + + val=RA8876_PWM_TIMER1_INVERTER_ON<<6|RA8876_PWM_TIMER1_AUTO_RELOAD<<5|RA8876_PWM_TIMER1_START<<4|RA8876_PWM_TIMER0_DEAD_ZONE_DISABLE<<3| + RA8876_PWM_TIMER0_INVERTER_OFF<<2|RA8876_PWM_TIMER0_AUTO_RELOAD<<1|RA8876_PWM_TIMER0_START; + writeReg(RA8876_REG_PCFGR,val); + + uint16_t duty=0x00ff; + // pwm0 duty for backlight + writeReg(RA8876_REG_TCMPB0L,duty); + writeReg(RA8876_REG_TCMPB0H,duty>>8); + + // pwm1 duty not needed + duty=0x00ff; + writeReg(RA8876_REG_TCMPB1L,duty); + writeReg(RA8876_REG_TCMPB1H,duty>>8); + + // clocksperiod + uint16_t clocks_per_period=1024; + + writeReg(RA8876_REG_TCNTB0L,clocks_per_period); + writeReg(RA8876_REG_TCNTB0H,clocks_per_period>>8); + + writeReg(RA8876_REG_TCNTB1L,clocks_per_period); + writeReg(RA8876_REG_TCNTB1H,clocks_per_period>>8); + SPI.endTransaction(); +} + +// Given a target frequency in kHz, finds PLL parameters k and n to reach as +// close as possible to the target frequency without exceeding it. +// The k parameter will be constrained to the range 1..kMax. +// Returns true iff PLL params were found, even if not an exact match. +bool RA8876::calcPllParams(uint32_t targetFreq, int kMax, PllParams *pll) { + bool found = false; + int foundk, foundn; + uint32_t foundFreq; + uint32_t foundError; // Amount lower than requested frequency + + // k of 0 (i.e. 2 ** 0 = 1) is possible, but not sure if it's a good idea. + for (int testk = 1; testk <= kMax; testk++) + { + if (m_oscClock % (1 << testk)) + continue; // Step size with this k would be fractional + + int testn = (targetFreq / (m_oscClock / (1 << testk))) - 1; + if ((testn < 1) || (testn > 63)) + continue; // Param n out of range for this k + + // Fvco constraint found in data sheet section 6.1.2 + uint32_t fvco = m_oscClock * (testn + 1); + if ((fvco < 100000) && (fvco > 600000)) + continue; // Fvco out of range + + // Found some usable params, but perhaps at a lower frequency than requested. + uint32_t freq = (m_oscClock * (testn + 1)) / (1 << testk); + uint32_t error = targetFreq - freq; + if ((!found) || (found && (foundError > error))) + { + found = true; + foundk = testk; + foundn = testn; + foundFreq = freq; + foundError = error; + + // No need to keep searching if the frequency match was exact + if (foundError == 0) + break; + } + } + + if (found) + { + pll->freq = foundFreq; + pll->k = foundk; + pll->n = foundn; + } + + return found; +} + +// Calculates the clock frequencies and their PLL parameters. +bool RA8876::calcClocks(void) { + // Data sheet section 5.2 gives max clocks: + // memClock : 166 MHz + // coreClock: 120 MHz (133MHz if not using internal font) + // scanClock: 100 MHz + + // Mem clock target is the same as SDRAM speed, but capped at 166 MHz + uint32_t memClock = m_sdramInfo->speed * 1000; + if (memClock > 166000) + memClock = 166000; + + if (!calcPllParams(memClock, 3, &m_memPll)) + return false; + + // Core clock target will be the same as the mem clock, but capped to + // 120 MHz, because that is the max frequency if we want to use the + // internal font. + uint32_t coreClock = m_memPll.freq; + if (coreClock > 120000) + coreClock = 120000; + + if (!calcPllParams(coreClock, 3, &m_corePll)) + return false; + + // Scan clock target will be the display's dot clock, but capped at 100 MHz + uint32_t scanClock = m_displayInfo->dotClock; + if (scanClock > 100000) + scanClock = 100000; + + if (!calcPllParams(scanClock, 7, &m_scanPll)) + return false; + + //dumpClocks(); + + // Data sheet section 6.1.1 rules: + // 1. Core clock must be less than or equal to mem clock + if (m_corePll.freq > m_memPll.freq) + return false; + + // 2. Core clock must be greater than half mem clock + if ((m_corePll.freq * 2) <= m_memPll.freq) + return false; + + // 3. Core clock must be greater than (scan clock * 1.5) + if (m_corePll.freq <= (m_scanPll.freq + (m_scanPll.freq >> 1))) + return false; + + return true; +} + +// Dump clock info to serial monitor. +/* +void RA8876::dumpClocks(void) +{ + Serial.println("\nMem\n---"); + Serial.print("Requested kHz: "); Serial.println(m_sdramInfo->speed * 1000); + Serial.print("Actual kHz : "); Serial.println(m_memPll.freq); + Serial.print("PLL k : "); Serial.println(m_memPll.k); + Serial.print("PLL n : "); Serial.println(m_memPll.n); + + Serial.println("\nCore\n----"); + Serial.print("kHz : "); Serial.println(m_corePll.freq); + Serial.print("PLL k : "); Serial.println(m_corePll.k); + Serial.print("PLL n : "); Serial.println(m_corePll.n); + + Serial.println("\nScan\n----"); + Serial.print("Requested kHz: "); Serial.println(m_displayInfo->dotClock); + Serial.print("Actual kHz : "); Serial.println(m_scanPll.freq); + Serial.print("PLL k : "); Serial.println(m_scanPll.k); + Serial.print("PLL n : "); Serial.println(m_scanPll.n); + + // TODO: Frame rate? + + return; +} +*/ + +bool RA8876::initPLL(void) { + //Serial.println("init PLL"); + + SPI.beginTransaction(m_spiSettings); + + //Serial.print("DRAM_FREQ "); Serial.println(m_memPll.freq); + //Serial.print("7: "); Serial.println(m_memPll.k << 1); + //Serial.print("8: "); Serial.println(m_memPll.n); + writeReg(RA8876_REG_MPLLC1, m_memPll.k << 1); + writeReg(RA8876_REG_MPLLC2, m_memPll.n); + + //Serial.print("CORE_FREQ "); Serial.println(m_corePll.freq); + //Serial.print("9: "); Serial.println(m_corePll.k << 1); + //Serial.print("A: "); Serial.println(m_corePll.n); + writeReg(RA8876_REG_SPLLC1, m_corePll.k << 1); + writeReg(RA8876_REG_SPLLC2, m_corePll.n); + + // Per the data sheet, there are two divider fields for the scan clock, but the math seems + // to work out if we treat k as a single 3-bit number in bits 3..1. + //Serial.print("SCAN_FREQ "); Serial.println(m_scanPll.freq); + //Serial.print("5: "); Serial.println(m_scanPll.k << 1); + //Serial.print("6: "); Serial.println(m_scanPll.n); + writeReg(RA8876_REG_PPLLC1, m_scanPll.k << 1); + writeReg(RA8876_REG_PPLLC2, m_scanPll.n); + + // Toggle bit 7 of the CCR register to trigger a reconfiguration of the PLLs + writeReg(RA8876_REG_CCR, 0x00); + delay(2); + writeReg(RA8876_REG_CCR, 0x80); + delay(2); + + uint8_t ccr = readReg(RA8876_REG_CCR); + + SPI.endTransaction(); + + return (ccr & 0x80) ? true : false; +} + +// Initialize SDRAM interface. +bool RA8876::initMemory(SdramInfo *info) { + //Serial.println("init memory"); + + uint32_t sdramRefreshRate; + uint8_t sdrar = 0x00; + uint8_t sdrmd = 0x00; + + // Refresh rate + sdramRefreshRate = ((uint32_t) info->refresh * info->speed * 1000) >> info->rowBits; + + // Number of banks + if (info->banks == 2) + ; // NOP + else if (info->banks == 4) + sdrar |= 0x20; + else + return false; // Unsupported number of banks + + // Number of row bits + if ((info->rowBits < 11) || (info->rowBits > 13)) + return false; // Unsupported number of row bits + else + sdrar |= ((info->rowBits - 11) & 0x03) << 3; + + // Number of column bits + if ((info->colBits < 8) || (info->colBits > 12)) + return false; // Unsupported number of column bits + else + sdrar |= info->colBits & 0x03; + + // CAS latency + if ((info->casLatency < 2) || (info->casLatency > 3)) + return false; // Unsupported CAS latency + else + sdrmd |= info->casLatency & 0x03; + + SPI.beginTransaction(m_spiSettings); + + //Serial.print("SDRAR: "); Serial.println(sdrar); // Expected: 0x29 (41 decimal) + writeReg(RA8876_REG_SDRAR, sdrar); + + //Serial.print("SDRMD: "); Serial.println(sdrmd); + writeReg(RA8876_REG_SDRMD, sdrmd); + + //Serial.print("sdramRefreshRate: "); Serial.println(sdramRefreshRate); + writeReg(RA8876_REG_SDR_REF_ITVL0, sdramRefreshRate & 0xFF); + writeReg(RA8876_REG_SDR_REF_ITVL1, sdramRefreshRate >> 8); + + // Trigger SDRAM initialization + writeReg(RA8876_REG_SDRCR, 0x01); + + // Wait for SDRAM to be ready + uint8_t status; + for (int i = 0; i < 250; i++) { + delay(1); + if ((status = readStatus()) & 0x40) + break; + } + SPI.endTransaction(); + + //Serial.println(status); + + return (status & 0x40) ? true : false; +} + +void RA8876::DisplayInit(int8_t p,int8_t size,int8_t rot,int8_t font) { + setRotation(rot); + setTextWrap(false); // Allow text to run off edges + cp437(true); + setTextFont(font&3); + setTextSize(size&7); + setTextColor(RA8876_WHITE,RA8876_BLACK); + setCursor(0,0); + fillScreen(RA8876_BLACK); + setDrawMode(drawmode); + PWM_init(); +} + +bool RA8876::initDisplay() { + SPI.beginTransaction(m_spiSettings); + + // Set chip config register + uint8_t ccr = readReg(RA8876_REG_CCR); + ccr &= 0xE7; // 24-bit LCD output + ccr &= 0xFE; // 8-bit host data bus + writeReg(RA8876_REG_CCR, ccr); + + writeReg(RA8876_REG_MACR, 0x00); // Direct write, left-to-right-top-to-bottom memory + + writeReg(RA8876_REG_ICR, 0x00); // Graphics mode, memory is SDRAM + + uint8_t dpcr = readReg(RA8876_REG_DPCR); + dpcr &= 0xFB; // Vertical scan top to bottom + dpcr &= 0xF8; // Colour order RGB + dpcr |= 0x80; // Panel fetches PDAT at PCLK falling edge + writeReg(RA8876_REG_DPCR, dpcr); + + uint8_t pcsr = readReg(RA8876_REG_PCSR); + pcsr |= 0x80; // XHSYNC polarity high + pcsr |= 0x40; // XVSYNC polarity high + pcsr &= 0xDF; // XDE polarity high + writeReg(RA8876_REG_PCSR, pcsr); + + // Set display width + writeReg(RA8876_REG_HDWR, (m_displayInfo->width / 8) - 1); + writeReg(RA8876_REG_HDWFTR, (m_displayInfo->width % 8)); + + // Set display height + writeReg(RA8876_REG_VDHR0, (m_displayInfo->height - 1) & 0xFF); + writeReg(RA8876_REG_VDHR1, (m_displayInfo->height - 1) >> 8); + + // Set horizontal non-display (back porch) + writeReg(RA8876_REG_HNDR, (m_displayInfo->hBackPorch / 8) - 1); + writeReg(RA8876_REG_HNDFTR, (m_displayInfo->hBackPorch % 8)); + + // Set horizontal start position (front porch) + writeReg(RA8876_REG_HSTR, ((m_displayInfo->hFrontPorch + 4) / 8) - 1); + + // Set HSYNC pulse width + writeReg(RA8876_REG_HPWR, ((m_displayInfo->hPulseWidth + 4) / 8) - 1); + + // Set vertical non-display (back porch) + writeReg(RA8876_REG_VNDR0, (m_displayInfo->vBackPorch - 1) & 0xFF); + writeReg(RA8876_REG_VNDR1, (m_displayInfo->vBackPorch - 1) >> 8); + + // Set vertical start position (front porch) + writeReg(RA8876_REG_VSTR, m_displayInfo->vFrontPorch - 1); + + // Set VSYNC pulse width + writeReg(RA8876_REG_VPWR, m_displayInfo->vPulseWidth - 1); + + // Set main window to 16 bits per pixel + writeReg(RA8876_REG_MPWCTR, 0x04); // PIP windows disabled, 16-bpp, enable sync signals + + // Set main window start address to 0 + writeReg(RA8876_REG_MISA0, 0); + writeReg(RA8876_REG_MISA1, 0); + writeReg(RA8876_REG_MISA2, 0); + writeReg(RA8876_REG_MISA3, 0); + + // Set main window image width + writeReg(RA8876_REG_MIW0, m_width & 0xFF); + writeReg(RA8876_REG_MIW1, m_width >> 8); + + // Set main window start coordinates + writeReg(RA8876_REG_MWULX0, 0); + writeReg(RA8876_REG_MWULX1, 0); + writeReg(RA8876_REG_MWULY0, 0); + writeReg(RA8876_REG_MWULY1, 0); + + // Set canvas start address + writeReg(RA8876_REG_CVSSA0, 0); + writeReg(RA8876_REG_CVSSA1, 0); + writeReg(RA8876_REG_CVSSA2, 0); + writeReg(RA8876_REG_CVSSA3, 0); + + // Set canvas width + writeReg(RA8876_REG_CVS_IMWTH0, m_width & 0xFF); + writeReg(RA8876_REG_CVS_IMWTH1, m_width >> 8); + + // Set active window start coordinates + writeReg(RA8876_REG_AWUL_X0, 0); + writeReg(RA8876_REG_AWUL_X1, 0); + writeReg(RA8876_REG_AWUL_Y0, 0); + writeReg(RA8876_REG_AWUL_Y1, 0); + + // Set active window dimensions + writeReg(RA8876_REG_AW_WTH0, m_width & 0xFF); + writeReg(RA8876_REG_AW_WTH1, m_width >> 8); + writeReg(RA8876_REG_AW_HT0, m_height & 0xFF); + writeReg(RA8876_REG_AW_HT1, m_height >> 8); + + // Set canvas addressing mode/colour depth + uint8_t aw_color = 0x00; // 2d addressing mode + if (m_depth == 16) + aw_color |= 0x01; + else if (m_depth == 24) + aw_color |= 0x02; + writeReg(RA8876_REG_AW_COLOR, aw_color); + + // Turn on display + dpcr = readReg(RA8876_REG_DPCR); + dpcr |= 0x40; // Display on + writeReg(RA8876_REG_DPCR, dpcr); + + // TODO: Track backlight pin and turn on backlight + + SPI.endTransaction(); + + return true; +} + +void RA8876::setRotation(uint8_t m) { +return; +/* + SPI.beginTransaction(m_spiSettings); + + rotation = m % 4; // can't be higher than 3 + switch (rotation) { + case 0: + writeReg(RA8876_REG_MACR, 0x00); + _width = RA8876_TFTWIDTH; + _height = RA8876_TFTHEIGHT; + break; + case 1: + writeReg(RA8876_REG_MACR, 0x02); + _width = RA8876_TFTHEIGHT; + _height = RA8876_TFTWIDTH; + break; + case 2: + writeReg(RA8876_REG_MACR, 0x01); + _width = RA8876_TFTWIDTH; + _height = RA8876_TFTHEIGHT; + break; + case 3: + writeReg(RA8876_REG_MACR, 0x03); + _width = RA8876_TFTHEIGHT; + _height = RA8876_TFTWIDTH; + break; + } + m_width = _width; + m_height = _height; + SPI.endTransaction();*/ +} + +SdramInfo defaultSdramInfo = +{ + 120, // 120 MHz + 3, // CAS latency 3 + 4, // 4 banks + 12, // 12-bit row addresses + 9, // 9-bit column addresses + 64 // 64 millisecond refresh time +}; + +DisplayInfo defaultDisplayInfo = +{ + 1024, // Display width + 600, // Display height + 50000, // Pixel clock in kHz + + 160, // Horizontal front porch + 160, // Horizontal back porch + 70, // HSYNC pulse width + + 12, // Vertical front porch + 23, // Vertical back porch + 10 // VSYNC pulse width +}; + +bool RA8876::begin(void) { + m_sdramInfo = &defaultSdramInfo; + m_displayInfo = &defaultDisplayInfo; + m_width = m_displayInfo->width; + m_height = m_displayInfo->height; + m_depth = 16; + m_oscClock = 10000; // 10000kHz or 10MHz + textcolor = 0xFFFF; // White + textbgcolor = 0; // black + m_fontRomInfo.present = false; // No external font ROM chip + + // Set up chip select pin + pinMode(m_csPin, OUTPUT); + digitalWrite(m_csPin, HIGH); + + if (!calcClocks()) { + //Serial.println("calcClocks failed"); + return false; + } + + SPI.begin(); + + m_spiSettings = SPISettings(RA8876_SPI_SPEED, MSBFIRST, SPI_MODE3); + + softReset(); + + if (!initPLL()) { + //Serial.println("initPLL failed"); + return false; + } + + if (!initMemory(m_sdramInfo)) { + //Serial.println("initMemory failed"); + return false; + } + + if (!initDisplay()) { + //Serial.println("initDisplay failed"); + return false; + } + + // Set default font + selectInternalFont(RA8876_FONT_SIZE_16); + setTextScale(1); + + setRotation(0); + + clearScreen(0); + + return true; +} + +void RA8876::fillScreen(uint16_t color) { + clearScreen(color); +} + +// Show colour bars of 8 colours in repeating horizontal bars. +// This does not alter video memory, but rather instructs the video controller to display +// the pattern rather than the contents of memory. +void RA8876::colorBarTest(bool enabled) { + SPI.beginTransaction(m_spiSettings); + + uint8_t dpcr = readReg(RA8876_REG_DPCR); + + if (enabled) + dpcr = dpcr | 0x20; + else + dpcr = dpcr & ~0x20; + + writeReg(RA8876_REG_DPCR, dpcr); + + SPI.endTransaction(); +} + + +void RA8876::drawLine(int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color) { + drawTwoPointShape(x1, y1, x2, y2, color, RA8876_REG_DCR0, 0x80); +}; +void RA8876::drawRect(int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color) { + drawTwoPointShape(x1, y1, x1+x2, y1+y2, color, RA8876_REG_DCR1, 0xA0); +}; +void RA8876::fillRect(int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color) { + drawTwoPointShape(x1, y1, x1+x2, y1+y2, color, RA8876_REG_DCR1, 0xE0); +}; +void RA8876::drawTriangle(int16_t x1, int16_t y1, int16_t x2, int16_t y2, int16_t x3, int16_t y3, uint16_t color) { + drawThreePointShape(x1, y1, x2, y2, x3, y3, color, RA8876_REG_DCR0, 0xA2); +}; +void RA8876::fillTriangle(int16_t x1, int16_t y1, int16_t x2, int16_t y2, int16_t x3, int16_t y3, uint16_t color) { + drawThreePointShape(x1, y1, x2, y2, x3, y3, color, RA8876_REG_DCR0, 0xE2); +}; +void RA8876::drawCircle(int16_t x, int16_t y, int16_t radius, uint16_t color) { + drawEllipseShape(x, y, radius, radius, color, 0x80); +}; +void RA8876::fillCircle(int16_t x, int16_t y, int16_t radius, uint16_t color) { + drawEllipseShape(x, y, radius, radius, color, 0xC0); +}; + +void RA8876::drawRoundRect(int16_t x0, int16_t y0, int16_t w, int16_t h, int16_t radius, uint16_t color) { + drawThreePointShape1(x0, y0, x0+w, y0+h, radius, radius, color, RA8876_REG_DCR1, 0xB0); +} +void RA8876::fillRoundRect(int16_t x0, int16_t y0, int16_t w, int16_t h, int16_t radius, uint16_t color) { + drawThreePointShape1(x0, y0, x0+w, y0+h, radius, radius, color, RA8876_REG_DCR1, 0xF0); +} + +void RA8876::clearScreen(uint16_t color) { + setCursor(0, 0); fillRect(0, 0, m_width, m_height, color); +}; + +void RA8876::drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color) { + drawTwoPointShape(x, y, x, y+h, color, RA8876_REG_DCR1, 0xE0); +} +void RA8876::drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color) { + drawTwoPointShape(x, y, x+w, y, color, RA8876_REG_DCR1, 0xE0); +} + +void RA8876::setTextSize(uint8_t s) { + setTextScale(s, s); + textsize_x=s; + textsize_y=s; + //Serial.printf("scale %d\n",s); +} + +// Picture16bppBteMpuWriteColorExpansionWithChromaKey +// PAGE1_START_ADDR,SCREEN_WIDTH, 50, 50+128+50+10, 128, 128,COLOR65K_WHITE,COLOR65K_BLACK,"sun.bin"); +// ra8876lite.bteMpuWriteWithROP(s1_addr, s1_image_width, s1_x, s1_y, des_addr, des_image_width, des_x, des_y, width, height, rop_code); + + +//dCardShowPicture16bppBteMpuWriteWithROP(PAGE1_START_ADDR, SCREEN_WIDTH, 50+128, 50, PAGE1_START_ADDR, SCREEN_WIDTH, 50+128, 50, +// 128, 128,RA8876_BTE_ROP_CODE_6,"appli.bin"); + +/*bte_Source1_MemoryStartAddr(s1_addr); + bte_Source1_ImageWidth(s1_image_width); + + bte_Source1_WindowStartXY(s1_x,s1_y); + bte_DestinationMemoryStartAddr(des_addr); + + bte_DestinationImageWidth(des_image_width); + + bte_DestinationWindowStartXY(des_x,des_y); + bte_WindowSize(width,height); + + lcdRegDataWrite(RA8876_BTE_CTRL1,rop_code<<4|RA8876_BTE_MPU_WRITE_WITH_ROP);//91h + lcdRegDataWrite(RA8876_BTE_COLR,RA8876_S0_COLOR_DEPTH_16BPP<<5|RA8876_S1_COLOR_DEPTH_16BPP<<2|RA8876_DESTINATION_COLOR_DEPTH_16BPP);//92h + lcdRegDataWrite(RA8876_BTE_CTRL0,RA8876_BTE_ENABLE<<4);//90h + ramAccessPrepare(); + +// put picturess + activeWindowXY(x,y); +activeWindowWH(width,height); +setPixelCursor(x,y); + + */ +void RA8876::setAddrWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) { + uint16_t xs=x1-x0; + uint16_t ys=y1-y0; + cursor_x=x0; + cursor_y=y0; + addrw_x1=x0; + addrw_x2=x1; + uint8_t flag=0; + + if (!x0 && !y0 && !x1 && !y1) { + x0=0; + y0=0; + x1=_width; + y1=_height; + flag=1; + } + + if (x1>_width) x1=_width; + if (y1>_height) y1=_height; + + SPI.beginTransaction(m_spiSettings); + +#if 1 +// activeWindowXY(x,y); + writeReg(RA8876_REG_AWUL_X0,x0);//56h + writeReg(RA8876_REG_AWUL_X1,x0>>8);//57h + writeReg(RA8876_REG_AWUL_Y0,y0);//58h + writeReg(RA8876_REG_AWUL_Y1,y0>>8);//59h + + //activeWindowWH(width,height); + writeReg(RA8876_REG_AW_WTH0,xs);//5ah + writeReg(RA8876_REG_AW_WTH1,xs>>8);//5bh + writeReg(RA8876_REG_AW_HT0,ys);//5ch + writeReg(RA8876_REG_AW_HT1,ys>>8);//5dh + + //setPixelCursor(x,y); + writeReg(RA8876_REG_CURH0,x0); //5fh + writeReg(RA8876_REG_CURH1,x0>>8);//60h + writeReg(RA8876_REG_CURV0,y0);//61h + writeReg(RA8876_REG_CURV1,y0>>8);//62h + + //ramAccessPrepare(); + + #else + // source address, page1=0 + // bte_Source1_MemoryStartAddr + uint32_t addr=0; + writeReg(RA8876_REG_S1_STR0,addr);//9dh + writeReg(RA8876_REG_S1_STR1,addr>>8);//9eh + writeReg(RA8876_REG_S1_STR2,addr>>16);//9fh + writeReg(RA8876_REG_S1_STR3,addr>>24);//a0h + // screen width + // bte_Source1_ImageWidth + uint16_t width=_width; + writeReg(RA8876_REG_S1_WTH0,width);//abh + writeReg(RA8876_REG_S1_WTH1,width>>8);//ach + + // source window + // bte_Source1_WindowStartXY + writeReg(RA8876_REG_S1_X0,x0);//adh + writeReg(RA8876_REG_S1_X1,x0>>8);//aeh + writeReg(RA8876_REG_S1_Y0,y0);//afh + writeReg(RA8876_REG_S1_Y1,y0>>8);//b0h + + // dest mem + // bte_DestinationMemoryStartAddr + writeReg(RA8876_REG_DT_STR0,addr);//a7h + writeReg(RA8876_REG_DT_STR1,addr>>8);//a8h + writeReg(RA8876_REG_DT_STR2,addr>>16);//a9h + writeReg(RA8876_REG_DT_STR3,addr>>24);//aah + + // bte_DestinationImageWidth + writeReg(RA8876_REG_DT_WTH0,width);//abh + writeReg(RA8876_REG_DT_WTH1,width>>8);//ach + + //bte_DestinationWindowStartXY(des_x,des_y); + writeReg(RA8876_REG_DT_X0,x0);//adh + writeReg(RA8876_REG_DT_X1,x0>>8);//aeh + writeReg(RA8876_REG_DT_Y0,y0);//afh + writeReg(RA8876_REG_DT_Y1,y0>>8);//b0h + + // bte_WindowSize + writeReg(RA8876_REG_BTE_WTH0,xs);//b1h + writeReg(RA8876_REG_BTE_WTH1,xs>>8);//b2h + writeReg(RA8876_REG_BTE_HIG0,ys);//b3h + writeReg(RA8876_REG_BTE_HIG1,ys>>8);//b4h + + + writeReg(RA8876_BTE_CTRL1,RA8876_BTE_ROP_CODE_12<<4|RA8876_BTE_MPU_WRITE_WITH_ROP);//91h + writeReg(RA8876_BTE_COLR,RA8876_S0_COLOR_DEPTH_16BPP<<5|RA8876_S1_COLOR_DEPTH_16BPP<<2|RA8876_DESTINATION_COLOR_DEPTH_16BPP);//92h + writeReg(RA8876_BTE_CTRL0,RA8876_BTE_ENABLE<<4);//90h + + #endif + + writeCmd(RA8876_REG_MRWDP); //04h(); + + if (flag) SPI.endTransaction(); +} + +void RA8876::pushColors(uint16_t *data, uint8_t len, boolean first) { + SPI.beginTransaction(m_spiSettings); + while (len--) { + + //uint16_t color=RA8876_WHITE; + uint16_t color=*data++; + //waitWriteFifo(); + writeData(color&0xff); + //waitWriteFifo(); + writeData(color>>8); + + } + SPI.endTransaction(); +} + +void RA8876::drawPixel(int16_t x, int16_t y, uint16_t color) { + //Serial.println("drawPixel"); + //Serial.println(readStatus()); + + SPI.beginTransaction(m_spiSettings); + + writeReg(RA8876_REG_CURH0, x & 0xFF); + writeReg(RA8876_REG_CURH1, x >> 8); + + writeReg(RA8876_REG_CURV0, y & 0xFF); + writeReg(RA8876_REG_CURV1, y >> 8); + + writeReg(RA8876_REG_MRWDP, color & 0xFF); + writeReg(RA8876_REG_MRWDP, color >> 8); + + SPI.endTransaction(); +} + +void RA8876::drawTwoPointShape(int x1, int y1, int x2, int y2, uint16_t color, uint8_t reg, uint8_t cmd) { + //Serial.println("drawTwoPointShape"); + + SPI.beginTransaction(m_spiSettings); + + // First point + writeReg(RA8876_REG_DLHSR0, x1 & 0xFF); + writeReg(RA8876_REG_DLHSR1, x1 >> 8); + writeReg(RA8876_REG_DLVSR0, y1 & 0xFF); + writeReg(RA8876_REG_DLVSR1, y1 >> 8); + + // Second point + writeReg(RA8876_REG_DLHER0, x2 & 0xFF); + writeReg(RA8876_REG_DLHER1, x2 >> 8); + writeReg(RA8876_REG_DLVER0, y2 & 0xFF); + writeReg(RA8876_REG_DLVER1, y2 >> 8); + + // Colour + writeReg(RA8876_REG_FGCR, color >> 11 << 3); + writeReg(RA8876_REG_FGCG, ((color >> 5) & 0x3F) << 2); + writeReg(RA8876_REG_FGCB, (color & 0x1F) << 3); + + // Draw + writeReg(reg, cmd); // Start drawing + + // Wait for completion + wait_ready(); + + SPI.endTransaction(); +} + + +// 1 us => 10 ms +#define RA8876_TIMEOUT 10000 + +void RA8876::waitWriteFifo(void) { + uint8_t status = readStatus(); + uint32_t iter = 0; + while (status & 0x80) { + status = readStatus(); + iter++; + if (iter>RA8876_TIMEOUT) { + // timeout, soft reset + softReset(); + SPI.beginTransaction(m_spiSettings); + Serial.printf("iter timeout fifo\n"); + return; + } + } +}; + +void RA8876::waitTaskBusy(void) { + //while (readStatus() & 0x08); + wait_ready(); +}; + +void RA8876::wait_ready(void) { + uint8_t status = readStatus(); + uint32_t iter = 0; + while (status & 0x08) { + status = readStatus(); + iter++; + if (iter>RA8876_TIMEOUT) { + // timeout, soft reset + softReset(); + SPI.beginTransaction(m_spiSettings); + Serial.printf("iter timeout cmd\n"); + return; + } + } + // got at max 1800 + // Serial.printf("iter: %d\n",iter); +} + +void RA8876::drawThreePointShape(int x1, int y1, int x2, int y2, int x3, int y3, uint16_t color, uint8_t reg, uint8_t cmd) { + //Serial.println("drawThreePointShape"); + + SPI.beginTransaction(m_spiSettings); + + // First point + writeReg(RA8876_REG_DLHSR0, x1 & 0xFF); + writeReg(RA8876_REG_DLHSR1, x1 >> 8); + writeReg(RA8876_REG_DLVSR0, y1 & 0xFF); + writeReg(RA8876_REG_DLVSR1, y1 >> 8); + + // Second point + writeReg(RA8876_REG_DLHER0, x2 & 0xFF); + writeReg(RA8876_REG_DLHER1, x2 >> 8); + writeReg(RA8876_REG_DLVER0, y2 & 0xFF); + writeReg(RA8876_REG_DLVER1, y2 >> 8); + + // Third point + writeReg(RA8876_REG_DTPH0, x3 & 0xFF); + writeReg(RA8876_REG_DTPH1, x3 >> 8); + writeReg(RA8876_REG_DTPV0, y3 & 0xFF); + writeReg(RA8876_REG_DTPV1, y3 >> 8); + + // Colour + writeReg(RA8876_REG_FGCR, color >> 11 << 3); + writeReg(RA8876_REG_FGCG, ((color >> 5) & 0x3F) << 2); + writeReg(RA8876_REG_FGCB, (color & 0x1F) << 3); + + // Draw + writeReg(reg, cmd); // Start drawing + + // Wait for completion + wait_ready(); + + SPI.endTransaction(); +} + +void RA8876::drawThreePointShape1(int x1, int y1, int x2, int y2, int x3, int y3, uint16_t color, uint8_t reg, uint8_t cmd) { + //Serial.println("drawThreePointShape"); + + SPI.beginTransaction(m_spiSettings); + + // First point + writeReg(RA8876_REG_DLHSR0, x1 & 0xFF); + writeReg(RA8876_REG_DLHSR1, x1 >> 8); + writeReg(RA8876_REG_DLVSR0, y1 & 0xFF); + writeReg(RA8876_REG_DLVSR1, y1 >> 8); + + // Second point + writeReg(RA8876_REG_DLHER0, x2 & 0xFF); + writeReg(RA8876_REG_DLHER1, x2 >> 8); + writeReg(RA8876_REG_DLVER0, y2 & 0xFF); + writeReg(RA8876_REG_DLVER1, y2 >> 8); + + // corner radius + writeReg(RA8876_REG_ELL_A0, x3 & 0xFF); + writeReg(RA8876_REG_ELL_A1, x3 >> 8); + writeReg(RA8876_REG_ELL_B0, y3 & 0xFF); + writeReg(RA8876_REG_ELL_B1, y3 >> 8); + + #define RA8876_REG_ELL_A0 0x77 // Draw ellipse major radius 0 + #define RA8876_REG_ELL_A1 0x78 // Draw ellipse major radius 1 + #define RA8876_REG_ELL_B0 0x79 // Draw ellipse minor radius 0 + #define RA8876_REG_ELL_B1 0x7A // Draw ellipse minor radius 1 + + // Colour + writeReg(RA8876_REG_FGCR, color >> 11 << 3); + writeReg(RA8876_REG_FGCG, ((color >> 5) & 0x3F) << 2); + writeReg(RA8876_REG_FGCB, (color & 0x1F) << 3); + + // Draw + writeReg(reg, cmd); // Start drawing + + // Wait for completion + wait_ready(); + + SPI.endTransaction(); +} + +void RA8876::drawEllipseShape(int x, int y, int xrad, int yrad, uint16_t color, uint8_t cmd) { + //Serial.println("drawEllipseShape"); + + SPI.beginTransaction(m_spiSettings); + + // First point + writeReg16(RA8876_REG_DEHR0, x); + writeReg16(RA8876_REG_DEVR0, y); + + // Radii + writeReg16(RA8876_REG_ELL_A0, xrad); + writeReg16(RA8876_REG_ELL_B0, yrad); + + // Colour + writeReg(RA8876_REG_FGCR, color >> 11 << 3); + writeReg(RA8876_REG_FGCG, ((color >> 5) & 0x3F) << 2); + writeReg(RA8876_REG_FGCB, (color & 0x1F) << 3); + + // Draw + writeReg(RA8876_REG_DCR1, cmd); // Start drawing + + // Wait for completion + wait_ready(); + + SPI.endTransaction(); +} + +void RA8876::setCursor(int16_t x, int16_t y) { + SPI.beginTransaction(m_spiSettings); + + writeReg16(RA8876_REG_F_CURX0, x); + writeReg16(RA8876_REG_F_CURY0, y); + + SPI.endTransaction(); + //Serial.printf("curs %d:%d\n",x,y); + + cursor_x = x; + cursor_y = y; +} + +int RA8876::getCursorX(void) { + SPI.beginTransaction(m_spiSettings); + + int x = readReg16(RA8876_REG_F_CURX0); + + SPI.endTransaction(); + + return x; +} + +int RA8876::getCursorY(void) { + SPI.beginTransaction(m_spiSettings); + + int y = readReg16(RA8876_REG_F_CURY0); + + SPI.endTransaction(); + + return y; +} + +// Given a font encoding value, returns the corresponding bit pattern for +// use by internal fonts. +uint8_t RA8876::internalFontEncoding(enum FontEncoding enc) { + uint8_t e; + switch (enc) + { + case RA8876_FONT_ENCODING_8859_2: + e = 0x01; + break; + case RA8876_FONT_ENCODING_8859_4: + e = 0x02; + break; + case RA8876_FONT_ENCODING_8859_5: + e = 0x03; + break; + default: + e = 0x00; // ISO-8859-1 + break; + } + + return e; +} + +void RA8876::setTextMode(void) { + // Restore text colour + textcolor=textcolor; + writeReg(RA8876_REG_FGCR, textcolor >> 11 << 3); + writeReg(RA8876_REG_FGCG, ((textcolor >> 5) & 0x3F) << 2); + writeReg(RA8876_REG_FGCB, (textcolor & 0x1F) << 3); + + writeReg(RA8876_REG_BGCR, textbgcolor >> 11 << 3); + writeReg(RA8876_REG_BGCG, ((textbgcolor >> 5) & 0x3F) << 2); + writeReg(RA8876_REG_BGCB, (textbgcolor & 0x1F) << 3); + + waitTaskBusy(); + + // Enable text mode + uint8_t icr = readReg(RA8876_REG_ICR); + writeReg(RA8876_REG_ICR, icr | 0x04); + + if (textcolor==textbgcolor) { + setDrawMode_reg(1); + } else { + setDrawMode_reg(0); + } + +} + +void RA8876::setGraphicsMode(void) { + waitTaskBusy(); + + // Disable text mode + uint8_t icr = readReg(RA8876_REG_ICR); + writeReg(RA8876_REG_ICR, icr & ~0x04); +} + +void RA8876::selectInternalFont(enum FontSize size, enum FontEncoding enc) { + m_fontSource = RA8876_FONT_SOURCE_INTERNAL; + m_fontSize = size; + m_fontFlags = 0; + + SPI.beginTransaction(m_spiSettings); + + writeReg(RA8876_REG_CCR0, 0x00 | ((size & 0x03) << 4) | internalFontEncoding(enc)); + + uint8_t ccr1 = readReg(RA8876_REG_CCR1); + //ccr1 |= 0x40; // Transparent background + ccr1 &=0x40^0xff; + writeReg(RA8876_REG_CCR1, ccr1); + + SPI.endTransaction(); +} + +void RA8876::setDrawMode(uint8_t mode) { + drawmode=mode; + setDrawMode_reg(mode); +} + +void RA8876::setDrawMode_reg(uint8_t mode) { + SPI.beginTransaction(m_spiSettings); + uint8_t ccr1 = readReg(RA8876_REG_CCR1); + if (mode) { + ccr1 |= 0x40; // Transparent background + } else { + ccr1 &=0x40^0xff; // opaque background + } + writeReg(RA8876_REG_CCR1, ccr1); + SPI.endTransaction(); +} + +void RA8876::selectExternalFont(enum ExternalFontFamily family, enum FontSize size, enum FontEncoding enc, FontFlags flags) { + m_fontSource = RA8876_FONT_SOURCE_EXT_ROM; + m_fontSize = size; + m_fontFlags = flags; + + SPI.beginTransaction(m_spiSettings); + + //Serial.print("CCR0: "); Serial.println(0x40 | ((size & 0x03) << 4), HEX); + writeReg(RA8876_REG_CCR0, 0x40 | ((size & 0x03) << 4)); // Select external font ROM and size + + uint8_t ccr1 = readReg(RA8876_REG_CCR1); + ccr1 |= 0x40; // Transparent background + //Serial.print("CCR1: "); Serial.println(ccr1, HEX); + writeReg(RA8876_REG_CCR1, ccr1); + + //Serial.print("GTFNT_CR: "); Serial.println((enc << 3) | (family & 0x03), HEX); + writeReg(RA8876_REG_GTFNT_CR, (enc << 3) | (family & 0x03)); // Character encoding and family + + SPI.endTransaction(); +} + +/* +void RA8876::setTextColor(uint16_t color) { + textcolor = color; + textbgcolor = color; + }; + + void RA8876::setTextColor(uint16_t c, uint16_t bg) { + textcolor = c; + textbgcolor=bg; + } + */ + +void RA8876::setTextScale(int scale) { + setTextScale(scale, scale); + }; + +int RA8876::getTextSizeY(void) { + return ((m_fontSize + 2) * 8) * m_textScaleY; +} + +void RA8876::setTextScale(int xScale, int yScale) { + xScale = constrain(xScale, 1, 4); + yScale = constrain(yScale, 1, 4); + + m_textScaleX = xScale; + m_textScaleY = yScale; + + SPI.beginTransaction(m_spiSettings); + + uint8_t ccr1 = readReg(RA8876_REG_CCR1); + ccr1 = (ccr1 & 0xF0) | ((xScale - 1) << 2) | (yScale - 1); + //Serial.println(ccr1, HEX); + writeReg(RA8876_REG_CCR1, ccr1); + + SPI.endTransaction(); +} + +// Similar to write(), but does no special handling of control characters. +void RA8876::putChars(const char *buffer, size_t size) { + SPI.beginTransaction(m_spiSettings); + + setTextMode(); + + // Write characters + writeCmd(RA8876_REG_MRWDP); + for (unsigned int i = 0; i < size; i++) + { + waitWriteFifo(); + writeData(buffer[i]); + } + + setGraphicsMode(); + + SPI.endTransaction(); +} + +void RA8876::putChars16(const uint16_t *buffer, unsigned int count) { + SPI.beginTransaction(m_spiSettings); + + setTextMode(); + + // Write characters + writeCmd(RA8876_REG_MRWDP); + for (unsigned int i = 0; i < count; i++) + { + waitWriteFifo(); + writeData(buffer[i] >> 8); + + waitWriteFifo(); + writeData(buffer[i] & 0xFF); + } + + setGraphicsMode(); + + SPI.endTransaction(); +} + + +extern uint8_t wr_redir; + +size_t RA8876::xwrite(uint8_t c) { + return xwrite(&c, 1); +}; + +//#define RA8876_DEBUG + +size_t RA8876::xwrite(const uint8_t *buffer, size_t size) { + +#ifdef RA8876_DEBUG + char buff[128]; + memcpy(buff,buffer,size); + buff[size]=0; + Serial.printf("write start: %s\n",buff); +#endif + SPI.beginTransaction(m_spiSettings); + + setTextMode(); + + writeCmd(RA8876_REG_MRWDP); // Set current register for writing to memory + for (unsigned int i = 0; i < size; i++) + { + char c = buffer[i]; + + if (!c) continue; + + if (c == '\r') + ; // Ignored + else if (c == '\n') + { + setCursor(0, getCursorY() + getTextSizeY()); + writeCmd(RA8876_REG_MRWDP); // Reset current register for writing to memory + } + else if ((m_fontFlags & RA8876_FONT_FLAG_XLAT_FULLWIDTH) && ((c >= 0x21) || (c <= 0x7F))) + { + // Translate ASCII to Unicode fullwidth form (for Chinese fonts that lack ASCII) + uint16_t fwc = c - 0x21 + 0xFF01; + + waitWriteFifo(); + writeData(fwc >> 8); + + waitWriteFifo(); + writeData(fwc & 0xFF); + } + else + { + waitWriteFifo(); + writeData(c); + } + } + + setGraphicsMode(); + + SPI.endTransaction(); + + #ifdef RA8876_DEBUG + Serial.printf("write end:\n"); + #endif + + return size; +} + +void RA8876::FastString(uint16_t x,uint16_t y,uint16_t tcolor, const char* str) { + setCursor(x,y); + setTextColor(tcolor,textbgcolor); + xwrite((uint8_t*)str,strlen(str)); +} diff --git a/lib/Xlatb_RA8876-gemu-1.0/RA8876.h b/lib/Xlatb_RA8876-gemu-1.0/RA8876.h new file mode 100644 index 000000000..b27698027 --- /dev/null +++ b/lib/Xlatb_RA8876-gemu-1.0/RA8876.h @@ -0,0 +1,569 @@ +/*************************************************** + STM32 Support added by Jaret Burkett at OSHlab.com + + This is our library for the Adafruit RA8876 Breakout and Shield + ----> http://www.adafruit.com/products/1651 + + Check out the links above for our tutorials and wiring diagrams + These displays use SPI to communicate, 4 or 5 pins are required to + interface (RST is optional) + Adafruit invests time and resources providing this open source code, + please support Adafruit and open-source hardware by purchasing + products from Adafruit! + + Written by Limor Fried/Ladyada for Adafruit Industries. + MIT license, all text above must be included in any redistribution + ****************************************************/ + +#ifndef _RA8876H_ +#define _RA8876H_ + + +#include "Arduino.h" +#include +#include + +#define SPRINT(A) {char str[32];sprintf(str,"val: %d ",A);Serial.println((char*)str);} + + +#define RA8876_TFTWIDTH 1024 +#define RA8876_TFTHEIGHT 600 + +struct SdramInfo +{ + int speed; // MHz + int casLatency; // CAS latency (2 or 3) + int banks; // Banks (2 or 4) + int rowBits; // Row addressing bits (11-13) + int colBits; // Column addressing bits (8-12) + int refresh; // Refresh time in microseconds +}; + +struct DisplayInfo +{ + int width; // Display width + int height; // Display height + + uint32_t dotClock; // Pixel clock in kHz + + int hFrontPorch; // Will be rounded to the nearest multiple of 8 + int hBackPorch; + int hPulseWidth; // Will be rounded to the nearest multiple of 8 + + int vFrontPorch; + int vBackPorch; + int vPulseWidth; +}; + +// Data sheet section 6.1. +// Output frequency is: (m_oscClock * (n + 1)) / (2 ** k) +// There is also a PLL parameter named 'm', but it's unclear how its value could ever be non-zero. +// When it is zero, the divisor is (2 ** 0) = 1, so we simply ignore it. +struct PllParams +{ + uint32_t freq; // Frequency in kHz + int n; // Multiplier less 1 (range 1..63) + int k; // Divisor power of 2 (range 0..3 for CCLK/MCLK; range 0..7 for SCLK) +}; + +#define RGB332(r, g, b) (((r) & 0xE0) | (((g) & 0xE0) >> 3) | (((b) & 0xE0) >> 6)) +#define RGB565(r, g, b) ((((r) & 0xF8) << 8) | (((g) & 0xFC) << 3) | (((b) & 0xF8) >> 3)) + +enum FontSource +{ + RA8876_FONT_SOURCE_INTERNAL, // CGROM with four 8-bit ISO Latin variants + RA8876_FONT_SOURCE_EXT_ROM // External font ROM chip +}; + +enum FontSize +{ + RA8876_FONT_SIZE_16 = 0x00, + RA8876_FONT_SIZE_24 = 0x01, + RA8876_FONT_SIZE_32 = 0x02 +}; + +enum FontEncoding +{ + RA8876_FONT_ENCODING_GB2312 = 0x00, // GB2312 (Simplified Chinese) + RA8876_FONT_ENCODING_GB18030 = 0x01, // GB12345/GB18030 (Chinese) + RA8876_FONT_ENCODING_BIG5 = 0x02, // Big5 (Traditional Chinese) + + RA8876_FONT_ENCODING_UNICODE = 0x03, // Unicode (UCS-2?) + + RA8876_FONT_ENCODING_ASCII = 0x04, // ASCII + + RA8876_FONT_ENCODING_UNIJAPAN = 0x05, // Uni-Japanese (?) + RA8876_FONT_ENCODING_JIS0208 = 0x06, // JIS X 0208 (Shift JIS?) + + RA8876_FONT_ENCODING_LGCATH = 0x07, // Latin/Greek/Cyrillic/Arabic/Thai/Hebrew (?) + + RA8876_FONT_ENCODING_8859_1 = 0x11, // ISO 8859-1 (Latin 1) + RA8876_FONT_ENCODING_8859_2 = 0x12, // ISO 8859-2 (Latin 2: Eastern European) + RA8876_FONT_ENCODING_8859_3 = 0x13, // ISO 8859-3 (Latin 3: South European) + RA8876_FONT_ENCODING_8859_4 = 0x14, // ISO 8859-4 (Latin 4: Northern European) + RA8876_FONT_ENCODING_8859_5 = 0x15, // ISO 8859-5 (Latin/Cyrillic) + RA8876_FONT_ENCODING_8859_7 = 0x16, // ISO 8859-7 (Latin/Greek) + RA8876_FONT_ENCODING_8859_8 = 0x17, // ISO 8859-8 (Latin/Hebrew) + RA8876_FONT_ENCODING_8859_9 = 0x18, // ISO 8859-9 (Latin 5: Turkish) + RA8876_FONT_ENCODING_8859_10 = 0x19, // ISO 8859-10 (Latin 6: Nordic) + RA8876_FONT_ENCODING_8859_11 = 0x1A, // ISO 8859-11 (Latin/Thai) + RA8876_FONT_ENCODING_8859_13 = 0x1B, // ISO 8859-13 (Latin 7: Baltic Rim) + RA8876_FONT_ENCODING_8859_14 = 0x1C, // ISO 8859-14 (Latin 8: Celtic) + RA8876_FONT_ENCODING_8859_15 = 0x1D, // ISO 8859-15 (Latin 9: Western European) + RA8876_FONT_ENCODING_8859_16 = 0x1E // ISO 8859-16 (Latin 10: South-Eastern European) +}; + +enum ExternalFontRom +{ + RA8876_FONT_ROM_GT21L16T1W = 0, + RA8876_FONT_ROM_GT30L16U2W = 1, + RA8876_FONT_ROM_GT30L24T3Y = 2, + RA8876_FONT_ROM_GT30L24M1Z = 3, + RA8876_FONT_ROM_GT30L32S4W = 4, + RA8876_FONT_ROM_GT20L24F6Y = 5, + RA8876_FONT_ROM_GT21L24S1W = 6 +}; + +struct ExternalFontRomInfo +{ + bool present; + int spiInterface; // SPI interface that font ROM is connected to (0 or 1) + int spiClockDivisor; // SPI interface clock divisor (2..512 in steps of 2) + enum ExternalFontRom chip; // Chip type +}; + +enum ExternalFontFamily +{ + RA8876_FONT_FAMILY_FIXED = 0, + RA8876_FONT_FAMILY_ARIAL = 1, + RA8876_FONT_FAMILY_TIMES = 2, + RA8876_FONT_FAMILY_FIXED_BOLD = 3 +}; + +typedef uint8_t FontFlags; +#define RA8876_FONT_FLAG_XLAT_FULLWIDTH 0x01 // Translate ASCII to Unicode fullwidth forms + +// 1MHz. TODO: Figure out actual speed to use +// Data sheet section 5.2 says maximum SPI clock is 50MHz. +//#define RA8876_SPI_SPEED 10000000 +#define RA8876_SPI_SPEED 20000000 + +// With SPI, the RA8876 expects an initial byte where the top two bits are meaningful. Bit 7 +// is A0, bit 6 is WR#. See data sheet section 7.3.2 and section 19. +// A0: 0 for command/status, 1 for data +// WR#: 0 for write, 1 for read +#define RA8876_DATA_WRITE 0x80 +#define RA8876_DATA_READ 0xC0 +#define RA8876_CMD_WRITE 0x00 +#define RA8876_STATUS_READ 0x40 + +// Data sheet 19.2: Chip configuration registers +#define RA8876_REG_SRR 0x00 // Software Reset Register +#define RA8876_REG_CCR 0x01 // Chip Configuration Register +#define RA8876_REG_MACR 0x02 // Memory Access Control Register +#define RA8876_REG_ICR 0x03 // Input Control Register +#define RA8876_REG_MRWDP 0x04 // Memory Read/Write Data Port + +// Data sheet 19.3: PLL setting registers +#define RA8876_REG_PPLLC1 0x05 // SCLK PLL control register 1 +#define RA8876_REG_PPLLC2 0x06 // SCLK PLL control register 2 +#define RA8876_REG_MPLLC1 0x07 // MCLK PLL control register 1 +#define RA8876_REG_MPLLC2 0x08 // MCLK PLL control register 2 +#define RA8876_REG_SPLLC1 0x09 // CCLK PLL control register 1 +#define RA8876_REG_SPLLC2 0x0A // CCLK PLL control register 2 + +// Data sheet 19.5: LCD display control registers +#define RA8876_REG_MPWCTR 0x10 // Main/PIP Window Control Register +#define RA8876_REG_PIPCDEP 0x11 // PIP Window Color Depth register +#define RA8876_REG_DPCR 0x12 // Display configuration register +#define RA8876_REG_PCSR 0x13 // Panel scan clock and data setting register +#define RA8876_REG_HDWR 0x14 // Horizontal Display Width Register +#define RA8876_REG_HDWFTR 0x15 // Horizontal Display Width Fine Tuning Register +#define RA8876_REG_HNDR 0x16 // Horizontal Non-Display Period Register +#define RA8876_REG_HNDFTR 0x17 // Horizontal Non-Display Period Fine Tuning Register +#define RA8876_REG_HSTR 0x18 // HSYNC start position register +#define RA8876_REG_HPWR 0x19 // HSYNC Pulse Width Register +#define RA8876_REG_VDHR0 0x1A // Vertical Display Height Register 0 +#define RA8876_REG_VDHR1 0x1B // Vertical Display Height Register 1 +#define RA8876_REG_VNDR0 0x1C // Vertical Non-Display Period Register 0 +#define RA8876_REG_VNDR1 0x1D // Vertical Non-Display Period Register 1 +#define RA8876_REG_VSTR 0x1E // VSYNC start position register +#define RA8876_REG_VPWR 0x1F // VSYNC pulse width register +#define RA8876_REG_MISA0 0x20 // Main Image Start Address 0 +#define RA8876_REG_MISA1 0x21 // Main Image Start Address 1 +#define RA8876_REG_MISA2 0x22 // Main Image Start Address 2 +#define RA8876_REG_MISA3 0x23 // Main Image Start Address 3 +#define RA8876_REG_MIW0 0x24 // Main Image Width 0 +#define RA8876_REG_MIW1 0x25 // Main Image Width 1 +#define RA8876_REG_MWULX0 0x26 // Main Window Upper-Left X coordinate 0 +#define RA8876_REG_MWULX1 0x27 // Main Window Upper-Left X coordinate 1 +#define RA8876_REG_MWULY0 0x28 // Main Window Upper-Left Y coordinate 0 +#define RA8876_REG_MWULY1 0x29 // Main Window Upper-Left Y coordinate 1 + +// Data sheet 19.6: Geometric engine control registers +#define RA8876_REG_CVSSA0 0x50 // Canvas Start Address 0 +#define RA8876_REG_CVSSA1 0x51 // Canvas Start Address 1 +#define RA8876_REG_CVSSA2 0x52 // Canvas Start Address 2 +#define RA8876_REG_CVSSA3 0x53 // Canvas Start Address 3 +#define RA8876_REG_CVS_IMWTH0 0x54 // Canvas image width 0 +#define RA8876_REG_CVS_IMWTH1 0x55 // Canvas image width 1 +#define RA8876_REG_AWUL_X0 0x56 // Active Window Upper-Left X coordinate 0 +#define RA8876_REG_AWUL_X1 0x57 // Active Window Upper-Left X coordinate 1 +#define RA8876_REG_AWUL_Y0 0x58 // Active Window Upper-Left Y coordinate 0 +#define RA8876_REG_AWUL_Y1 0x59 // Active Window Upper-Left Y coordinate 1 +#define RA8876_REG_AW_WTH0 0x5A // Active Window Width 0 +#define RA8876_REG_AW_WTH1 0x5B // Active Window Width 1 +#define RA8876_REG_AW_HT0 0x5C // Active Window Height 0 +#define RA8876_REG_AW_HT1 0x5D // Active Window Height 1 +#define RA8876_REG_AW_COLOR 0x5E // Color Depth of canvas & active window +#define RA8876_REG_CURH0 0x5F // Graphic read/write horizontal position 0 +#define RA8876_REG_CURH1 0x60 // Graphic read/write horizontal position 1 +#define RA8876_REG_CURV0 0x61 // Graphic read/write vertical position 0 +#define RA8876_REG_CURV1 0x62 // Graphic read/write vertical position 1 +#define RA8876_REG_F_CURX0 0x63 // Text cursor X-coordinate register 0 +#define RA8876_REG_F_CURX1 0x64 // Text cursor X-coordinate register 1 +#define RA8876_REG_F_CURY0 0x65 // Text cursor Y-coordinate register 0 +#define RA8876_REG_F_CURY1 0x66 // Text cursor Y-coordinate register 1 + +#define RA8876_REG_DCR0 0x67 // Draw shape control register 0 + +#define RA8876_REG_DLHSR0 0x68 // Draw shape point 1 X coordinate register 0 +#define RA8876_REG_DLHSR1 0x69 // Draw shape point 1 X coordinate register 1 +#define RA8876_REG_DLVSR0 0x6A // Draw shape point 1 Y coordinate register 0 +#define RA8876_REG_DLVSR1 0x6B // Draw shape point 1 Y coordinate register 1 + +#define RA8876_REG_DLHER0 0x6C // Draw shape point 2 X coordinate register 0 +#define RA8876_REG_DLHER1 0x6D // Draw shape point 2 X coordinate register 1 +#define RA8876_REG_DLVER0 0x6E // Draw shape point 2 Y coordinate register 0 +#define RA8876_REG_DLVER1 0x6F // Draw shape point 2 Y coordinate register 1 + +#define RA8876_REG_DTPH0 0x70 // Draw shape point 3 X coordinate register 0 +#define RA8876_REG_DTPH1 0x71 // Draw shape point 3 X coordinate register 1 +#define RA8876_REG_DTPV0 0x72 // Draw shape point 3 Y coordinate register 0 +#define RA8876_REG_DTPV1 0x73 // Draw shape point 3 Y coordinate register 1 + +#define RA8876_REG_DCR1 0x76 // Draw shape control register 1 + +#define RA8876_REG_ELL_A0 0x77 // Draw ellipse major radius 0 +#define RA8876_REG_ELL_A1 0x78 // Draw ellipse major radius 1 +#define RA8876_REG_ELL_B0 0x79 // Draw ellipse minor radius 0 +#define RA8876_REG_ELL_B1 0x7A // Draw ellipse minor radius 1 + +#define RA8876_REG_DEHR0 0x7B // Draw ellipse centre X coordinate register 0 +#define RA8876_REG_DEHR1 0x7C // Draw ellipse centre X coordinate register 1 +#define RA8876_REG_DEVR0 0x7D // Draw ellipse centre Y coordinate register 0 +#define RA8876_REG_DEVR1 0x7E // Draw ellipse centre Y coordinate register 1 + +// Data sheet 19.7: PWM timer control registers +#define RA8876_REG_PSCLR 0x84 // PWM prescaler register +#define RA8876_REG_PMUXR 0x85 // PWM clock mux register +#define RA8876_REG_PCFGR 0x86 // PWM configuration register + +#define RA8876_REG_TCMPB0L 0x88 +#define RA8876_REG_TCMPB0H 0x89 +#define RA8876_REG_TCNTB0L 0x8A +#define RA8876_REG_TCNTB0H 0x8B +#define RA8876_REG_TCMPB1L 0x8C +#define RA8876_REG_TCMPB1H 0x8D +#define RA8876_REG_TCNTB1L 0x8E +#define RA8876_REG_TCNTB1H 0x8F + +#define RA8876_REG_BTE_CTRL0 0x90 +#define RA8876_REG_BTE_CTRL1 0x91 +#define RA8876_REG_BTE_COLR 0x92 + +#define RA8876_REG_S1_STR0 0x9D +#define RA8876_REG_S1_STR1 0x9E +#define RA8876_REG_S1_STR2 0x9F +#define RA8876_REG_S1_STR3 0xA0 + +#define RA8876_REG_S1_WTH0 0xA1 +#define RA8876_REG_S1_WTH1 0xA2 + +#define RA8876_REG_S1_X0 0xA3 +#define RA8876_REG_S1_X1 0xA4 +#define RA8876_REG_S1_Y0 0xA5 +#define RA8876_REG_S1_Y1 0xA6 + +#define RA8876_REG_DT_STR0 0xA7 +#define RA8876_REG_DT_STR1 0xA8 +#define RA8876_REG_DT_STR2 0xA9 +#define RA8876_REG_DT_STR3 0xAA + +#define RA8876_REG_DT_WTH0 0xAB +#define RA8876_REG_DT_WTH1 0xAC + +#define RA8876_REG_DT_X0 0xAD +#define RA8876_REG_DT_X1 0xAE +#define RA8876_REG_DT_Y0 0xAF +#define RA8876_REG_DT_Y1 0xB0 + +#define RA8876_REG_BTE_WTH0 0xB1 +#define RA8876_REG_BTE_WTH1 0xB2 +#define RA8876_REG_BTE_HIG0 0xB3 +#define RA8876_REG_BTE_HIG1 0xB4 + + +// Data sheet 19.9: Serial flash & SPI master control registers +#define RA8876_REG_SFL_CTRL 0xB7 // Serial flash/ROM control register +#define RA8876_REG_SPI_DIVSOR 0xBB // SPI clock period + +// Data sheet 19.10: Text engine +#define RA8876_REG_CCR0 0xCC // Character Control Register 0 +#define RA8876_REG_CCR1 0xCD // Character Control Register 1 +#define RA8876_REG_GTFNT_SEL 0xCE // Genitop character ROM select +#define RA8876_REG_GTFNT_CR 0xCF // Genitop character ROM control register + +#define RA8876_REG_FLDR 0xD0 // Chracter line gap register +#define RA8876_REG_F2FSSR 0xD1 // Chracter to character space setting register +#define RA8876_REG_FGCR 0xD2 // Foreground colour register - red +#define RA8876_REG_FGCG 0xD3 // Foreground colour register - green +#define RA8876_REG_FGCB 0xD4 // Foreground colour register - blue + +#define RA8876_REG_BGCR 0xD5 // background colour register - red +#define RA8876_REG_BGCG 0xD6 // background colour register - green +#define RA8876_REG_BGCB 0xD7 // background colour register - blue + +// Data sheet 19.12: SDRAM control registers +#define RA8876_REG_SDRAR 0xE0 // SDRAM attribute register +#define RA8876_REG_SDRMD 0xE1 // SDRAM mode & extended mode register +#define RA8876_REG_SDR_REF_ITVL0 0xE2 // SDRAM auto refresh interval 0 +#define RA8876_REG_SDR_REF_ITVL1 0xE3 // SDRAM auto refresh interval 1 +#define RA8876_REG_SDRCR 0xE4 // SDRAM control register + + +// Color definitions +#define RA8876_BLACK 0x0000 /* 0, 0, 0 */ +#define RA8876_NAVY 0x000F /* 0, 0, 128 */ +#define RA8876_DARKGREEN 0x03E0 /* 0, 128, 0 */ +#define RA8876_DARKCYAN 0x03EF /* 0, 128, 128 */ +#define RA8876_MAROON 0x7800 /* 128, 0, 0 */ +#define RA8876_PURPLE 0x780F /* 128, 0, 128 */ +#define RA8876_OLIVE 0x7BE0 /* 128, 128, 0 */ +#define RA8876_LIGHTGREY 0xC618 /* 192, 192, 192 */ +#define RA8876_DARKGREY 0x7BEF /* 128, 128, 128 */ +#define RA8876_BLUE 0x001F /* 0, 0, 255 */ +#define RA8876_GREEN 0x07E0 /* 0, 255, 0 */ +#define RA8876_CYAN 0x07FF /* 0, 255, 255 */ +#define RA8876_RED 0xF800 /* 255, 0, 0 */ +#define RA8876_MAGENTA 0xF81F /* 255, 0, 255 */ +#define RA8876_YELLOW 0xFFE0 /* 255, 255, 0 */ +#define RA8876_WHITE 0xFFFF /* 255, 255, 255 */ +#define RA8876_ORANGE 0xFD20 /* 255, 165, 0 */ +#define RA8876_GREENYELLOW 0xAFE5 /* 173, 255, 47 */ +#define RA8876_PINK 0xF81F + + +#define RA8876_PSCLR 0x84 +#define RA8876_PRESCALER 0x03 +#define RA8876_PMUXR 0x85 +#define RA8876_PWM_TIMER_DIV1 0 +#define RA8876_PWM_TIMER_DIV2 1 +#define RA8876_PWM_TIMER_DIV4 2 +#define RA8876_PWM_TIMER_DIV8 3 +#define RA8876_XPWM1_OUTPUT_ERROR_FLAG 0 +#define RA8876_XPWM1_OUTPUT_PWM_TIMER1 2 +#define RA8876_XPWM1_OUTPUT_OSC_CLK 3 +#define RA8876_XPWM0_GPIO_C7 0 +#define RA8876_XPWM0_OUTPUT_PWM_TIMER0 2 +#define RA8876_XPWM0_OUTPUT_CORE_CLK 3 + +#define RA8876_PCFGR 0x86 +#define RA8876_PWM_TIMER1_INVERTER_OFF 0 +#define RA8876_PWM_TIMER1_INVERTER_ON 1 +#define RA8876_PWM_TIMER1_ONE_SHOT 0 +#define RA8876_PWM_TIMER1_AUTO_RELOAD 1 +#define RA8876_PWM_TIMER1_STOP 0 +#define RA8876_PWM_TIMER1_START 1 +#define RA8876_PWM_TIMER0_DEAD_ZONE_DISABLE 0 +#define RA8876_PWM_TIMER0_DEAD_ZONE_ENABLE 1 +#define RA8876_PWM_TIMER0_INVERTER_OFF 0 +#define RA8876_PWM_TIMER0_INVERTER_ON 1 +#define RA8876_PWM_TIMER0_ONE_SHOT 0 +#define RA8876_PWM_TIMER0_AUTO_RELOAD 1 +#define RA8876_PWM_TIMER0_STOP 0 +#define RA8876_PWM_TIMER0_START 1 + +#define RA8876_BTE_CTRL0 0x90 +#define RA8876_BTE_ENABLE 1 +#define RA8876_PATTERN_FORMAT8X8 0 +#define RA8876_PATTERN_FORMAT16X16 1 +#define RA8876_BTE_CTRL1 0x91 +#define RA8876_DESTINATION_COLOR_DEPTH_16BPP 1 +#define RA8876_S0_COLOR_DEPTH_16BPP 1 +#define RA8876_S1_COLOR_DEPTH_16BPP 1 + +#define RA8876_BTE_ROP_CODE_0 0 //0 ( Blackness ) +#define RA8876_BTE_ROP_CODE_1 1 //~S0・~S1 or ~ ( S0+S1 ) +#define RA8876_BTE_ROP_CODE_2 2 //~S0・S1 +#define RA8876_BTE_ROP_CODE_3 3 //~S0 +#define RA8876_BTE_ROP_CODE_4 4 //S0・~S1 +#define RA8876_BTE_ROP_CODE_5 5 //~S1 +#define RA8876_BTE_ROP_CODE_6 6 //S0^S1 +#define RA8876_BTE_ROP_CODE_7 7 //~S0+~S1 or ~ ( S0・S1 ) +#define RA8876_BTE_ROP_CODE_8 8 //S0・S1 +#define RA8876_BTE_ROP_CODE_9 9 //~ ( S0^S1 ) +#define RA8876_BTE_ROP_CODE_10 10 //S1 +#define RA8876_BTE_ROP_CODE_11 11 //~S0+S1 +#define RA8876_BTE_ROP_CODE_12 12 //S0 +#define RA8876_BTE_ROP_CODE_13 13 //S0+~S1 +#define RA8876_BTE_ROP_CODE_14 14 //S0+S1 +#define RA8876_BTE_ROP_CODE_15 15 //1 ( Whiteness ) +#define RA8876_BTE_ROP_BUS_WIDTH8 7 +#define RA8876_BTE_ROP_BUS_WIDTH16 15 + +#define RA8876_BTE_MPU_WRITE_WITH_ROP 0 +#define RA8876_BTE_MEMORY_COPY_WITH_ROP 2 +#define RA8876_BTE_MPU_WRITE_WITH_CHROMA 4 +#define RA8876_BTE_MEMORY_COPY_WITH_CHROMA 5 +#define RA8876_BTE_PATTERN_FILL_WITH_ROP 6 +#define RA8876_BTE_PATTERN_FILL_WITH_CHROMA 7 +#define RA8876_BTE_MPU_WRITE_COLOR_EXPANSION 8 +#define RA8876_BTE_MPU_WRITE_COLOR_EXPANSION_WITH_CHROMA 9 +#define RA8876_BTE_MEMORY_COPY_WITH_OPACITY 10 +#define RA8876_BTE_MPU_WRITE_WITH_OPACITY 11 +#define RA8876_BTE_SOLID_FILL 12 +#define RA8876_BTE_MEMORY_COPY_WITH_COLOR_EXPANSION 14 +#define RA8876_BTE_MEMORY_COPY_WITH_COLOR_EXPANSION_CHROMA 15 + +#define RA8876_BTE_COLR 0x92 + +class RA8876 : public Renderer { + + public: + + RA8876(int8_t cs,int8_t mosi,int8_t miso,int8_t sclk,int8_t bp); + + bool begin(void); + + // Dimensions + int getWidth() { return m_width; }; + int getHeight() { return m_height; }; + + void DisplayInit(int8_t p,int8_t size,int8_t rot,int8_t font); + + // Test + void colorBarTest(bool enabled); + + // Drawing + void drawPixel(int16_t x, int16_t y, uint16_t color); + void drawLine(int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color); + void drawRect(int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color); + void fillRect(int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color); + void fillScreen(uint16_t color); + void drawTriangle(int16_t x1, int16_t y1, int16_t x2, int16_t y2, int16_t x3, int16_t y3, uint16_t color); + void fillTriangle(int16_t x1, int16_t y1, int16_t x2, int16_t y2, int16_t x3, int16_t y3, uint16_t color); + + void drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color); + void drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color); + + + void drawCircle(int16_t x, int16_t y, int16_t radius, uint16_t color); + void fillCircle(int16_t x, int16_t y, int16_t radius, uint16_t color); + void drawRoundRect(int16_t x0, int16_t y0, int16_t w, int16_t h, int16_t radius, uint16_t color); + void fillRoundRect(int16_t x0, int16_t y0, int16_t w, int16_t h, int16_t radius, uint16_t color); + + void clearScreen(uint16_t color); + + // Text cursor + void setCursor(int16_t x, int16_t y); + int getCursorX(void); + int getCursorY(void); + + void setAddrWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1); + void pushColors(uint16_t *data, uint8_t len, boolean first); + + // Text + void selectInternalFont(enum FontSize size, enum FontEncoding enc = RA8876_FONT_ENCODING_8859_1); + void selectExternalFont(enum ExternalFontFamily family, enum FontSize size, enum FontEncoding enc, FontFlags flags = 0); + int getTextSizeY(void); + //void setTextColor(uint16_t color); + //void setTextColor(uint16_t c, uint16_t bg); + void setTextScale(int scale); + void setTextSize(uint8_t s); + void setTextScale(int xScale, int yScale); + void putChar(char c) { putChars(&c, 1); }; + void putChars(const char *buffer, size_t size); + void putChar16(uint16_t c) { putChars16(&c, 1); }; + void putChars16(const uint16_t *buffer, unsigned int count); + + // Internal for Print class + size_t xwrite(uint8_t c); + size_t xwrite(const uint8_t *buffer, size_t size); + + uint16_t GetColorFromIndex(uint8_t index); + void DisplayOnff(int8_t on); + void setRotation(uint8_t m); + void setDrawMode(uint8_t mode); + void setDrawMode_reg(uint8_t mode); + void dim(uint8_t contrast); + void FastString(uint16_t x,uint16_t y,uint16_t tcolor, const char* str); + + private: + uint8_t tabcolor; + void PWM_init(void); + void wait_ready(void); + void softReset(void); + void writeCmd(uint8_t x); + void writeData(uint8_t x); + uint8_t readData(void); + uint8_t readStatus(void); + void writeReg(uint8_t reg, uint8_t x); + void writeReg16(uint8_t reg, uint16_t x); + uint8_t readReg(uint8_t reg); + uint16_t readReg16(uint8_t reg); + void waitWriteFifo(void); + void waitTaskBusy(void); + + bool calcPllParams(uint32_t targetFreq, int kMax, PllParams *pll); + bool calcClocks(void); + void dumpClocks(void); + + bool initPLL(void); + bool initMemory(SdramInfo *info); + bool initDisplay(void); + + // Font utils + uint8_t internalFontEncoding(enum FontEncoding enc); + + // Text/graphics mode + void setTextMode(void); + void setGraphicsMode(void); + + // Low-level shapes + void drawTwoPointShape(int x1, int y1, int x2, int y2, uint16_t color, uint8_t reg, uint8_t cmd); // drawLine, drawRect, fillRect + void drawThreePointShape(int x1, int y1, int x2, int y2, int x3, int y3, uint16_t color, uint8_t reg, uint8_t cmd); // drawTriangle, fillTriangle + void drawEllipseShape(int x, int y, int xrad, int yrad, uint16_t color, uint8_t cmd); // drawCircle, fillCircle + void drawThreePointShape1(int x1, int y1, int x2, int y2, int x3, int y3, uint16_t color, uint8_t reg, uint8_t cmd); + + int8_t m_csPin, _mosi, _miso, _sclk, dimmer, _hwspi; + uint16_t m_width; + uint16_t m_height; + uint16_t m_depth; + uint32_t m_oscClock; // OSC clock (external crystal) frequency in kHz + + PllParams m_memPll; // MCLK (memory) PLL parameters + PllParams m_corePll; // CCLK (core) PLL parameters + PllParams m_scanPll; // SCLK (LCD panel scan) PLL parameters + + SPISettings m_spiSettings; + + SdramInfo *m_sdramInfo; + + DisplayInfo *m_displayInfo; + + ExternalFontRomInfo m_fontRomInfo; + + uint16_t addrw_x1; + uint16_t addrw_x2; + + //uint16_t m_textColor; + int m_textScaleX; + int m_textScaleY; + + enum FontSource m_fontSource; + enum FontSize m_fontSize; + FontFlags m_fontFlags; +}; + +#endif diff --git a/lib/Xlatb_RA8876-gemu-1.0/README.md b/lib/Xlatb_RA8876-gemu-1.0/README.md new file mode 100644 index 000000000..2f20c4106 --- /dev/null +++ b/lib/Xlatb_RA8876-gemu-1.0/README.md @@ -0,0 +1,8 @@ +### RA8876 Arduino Library +This library is for support for the 1024x600 tft controller over 3 wire SPI. It is based heavily on the [Adafruit_ILI9341](https://github.com/adafruit/Adafruit_ILI9341) library and is designed to work with the [Adafruit_GFX library](https://github.com/adafruit/Adafruit-GFX-Library). + +I have made some heavy modifications, as the typical Adafruit TFT libraries are designed to work with 16bit color (RGB565), and the RA8876 can only do 24bit (RGB888) color in 4 wire SPI mode. You can still use the library EXACTLY like you would for 16bit mode color, the colors are converted before sending to the display. What this means is, things will be slower than normal. Not only do you have to write twice as many pixels as a normal 240x320 display, 153,600px (320x480) vs 76,800px (240x320), but you also have to do a lightweight conversion on each color, and write 3 bytes vs 2bytes per pixel. + +For this reason, I do not recommend an AVR based Arduino for this library, although it will still work. I highly recommend a faster microcontroller based on ARM such as the Teensy, [STM32duino](https://github.com/rogerclarkmelbourne/Arduino_STM32), Arduino Zero, or the Arduing Due. + +On the STM32duino, DMA is supported and is therefore much faster. diff --git a/lib/Xlatb_RA8876-gemu-1.0/keywords.txt b/lib/Xlatb_RA8876-gemu-1.0/keywords.txt new file mode 100644 index 000000000..3496214f1 --- /dev/null +++ b/lib/Xlatb_RA8876-gemu-1.0/keywords.txt @@ -0,0 +1,30 @@ +####################################### +# Syntax Coloring Map +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +RA8876 KEYWORD1 + + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +setRotation KEYWORD2 +setAddrWindow KEYWORD2 +pushColor KEYWORD2 +drawPixel KEYWORD2 +drawFastVLine KEYWORD2 +drawFastHLine KEYWORD2 +fillRect KEYWORD2 +setRotation KEYWORD2 +setRotation KEYWORD2 +height KEYWORD2 +width KEYWORD2 +invertDisplay KEYWORD2 +drawImage KEYWORD2 +setScrollArea KEYWORD2 +scroll KEYWORD2 diff --git a/lib/Xlatb_RA8876-gemu-1.0/library.properties b/lib/Xlatb_RA8876-gemu-1.0/library.properties new file mode 100644 index 000000000..6e478adf0 --- /dev/null +++ b/lib/Xlatb_RA8876-gemu-1.0/library.properties @@ -0,0 +1,9 @@ +name=RA8876 +version=1.0.2 +author=Jaret Burkett +maintainer=Jaret Burkett +sentence=Library for RA8876 displays +paragraph=Library for RA8876 displays +category=Display +url=https://github.com/jaretburkett/ILI9488 +architectures=* diff --git a/lib/Xlatb_RA8876-gemu-1.0/spi_register.h b/lib/Xlatb_RA8876-gemu-1.0/spi_register.h new file mode 100644 index 000000000..340559ae1 --- /dev/null +++ b/lib/Xlatb_RA8876-gemu-1.0/spi_register.h @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2010 - 2011 Espressif System + * + */ + +#ifndef SPI_REGISTER_H_INCLUDED +#define SPI_REGISTER_H_INCLUDED + +#define REG_SPI_BASE(i) (0x60000200-i*0x100) +#define SPI_CMD(i) (REG_SPI_BASE(i) + 0x0) +#define SPI_USR (BIT(18)) + +#define SPI_ADDR(i) (REG_SPI_BASE(i) + 0x4) + +#define SPI_CTRL(i) (REG_SPI_BASE(i) + 0x8) +#define SPI_WR_BIT_ORDER (BIT(26)) +#define SPI_RD_BIT_ORDER (BIT(25)) +#define SPI_QIO_MODE (BIT(24)) +#define SPI_DIO_MODE (BIT(23)) +#define SPI_QOUT_MODE (BIT(20)) +#define SPI_DOUT_MODE (BIT(14)) +#define SPI_FASTRD_MODE (BIT(13)) + + + +#define SPI_RD_STATUS(i) (REG_SPI_BASE(i) + 0x10) + +#define SPI_CTRL2(i) (REG_SPI_BASE(i) + 0x14) + +#define SPI_CS_DELAY_NUM 0x0000000F +#define SPI_CS_DELAY_NUM_S 28 +#define SPI_CS_DELAY_MODE 0x00000003 +#define SPI_CS_DELAY_MODE_S 26 +#define SPI_MOSI_DELAY_NUM 0x00000007 +#define SPI_MOSI_DELAY_NUM_S 23 +#define SPI_MOSI_DELAY_MODE 0x00000003 +#define SPI_MOSI_DELAY_MODE_S 21 +#define SPI_MISO_DELAY_NUM 0x00000007 +#define SPI_MISO_DELAY_NUM_S 18 +#define SPI_MISO_DELAY_MODE 0x00000003 +#define SPI_MISO_DELAY_MODE_S 16 +#define SPI_CK_OUT_HIGH_MODE 0x0000000F +#define SPI_CK_OUT_HIGH_MODE_S 12 +#define SPI_CK_OUT_LOW_MODE 0x0000000F +#define SPI_CK_OUT_LOW_MODE_S 8 + +#define SPI_CLOCK(i) (REG_SPI_BASE(i) + 0x18) +#define SPI_CLK_EQU_SYSCLK (BIT(31)) +#define SPI_CLKDIV_PRE 0x00001FFF +#define SPI_CLKDIV_PRE_S 18 +#define SPI_CLKCNT_N 0x0000003F +#define SPI_CLKCNT_N_S 12 +#define SPI_CLKCNT_H 0x0000003F +#define SPI_CLKCNT_H_S 6 +#define SPI_CLKCNT_L 0x0000003F +#define SPI_CLKCNT_L_S 0 + +#define SPI_USER(i) (REG_SPI_BASE(i) + 0x1C) +#define SPI_USR_COMMAND (BIT(31)) +#define SPI_USR_ADDR (BIT(30)) +#define SPI_USR_DUMMY (BIT(29)) +#define SPI_USR_MISO (BIT(28)) +#define SPI_USR_MOSI (BIT(27)) + +#define SPI_USR_MOSI_HIGHPART (BIT(25)) +#define SPI_USR_MISO_HIGHPART (BIT(24)) + + +#define SPI_SIO (BIT(16)) +#define SPI_FWRITE_QIO (BIT(15)) +#define SPI_FWRITE_DIO (BIT(14)) +#define SPI_FWRITE_QUAD (BIT(13)) +#define SPI_FWRITE_DUAL (BIT(12)) +#define SPI_WR_BYTE_ORDER (BIT(11)) +#define SPI_RD_BYTE_ORDER (BIT(10)) +#define SPI_CK_OUT_EDGE (BIT(7)) +#define SPI_CK_I_EDGE (BIT(6)) +#define SPI_CS_SETUP (BIT(5)) +#define SPI_CS_HOLD (BIT(4)) +#define SPI_FLASH_MODE (BIT(2)) +#define SPI_DOUTDIN (BIT(0)) + +#define SPI_USER1(i) (REG_SPI_BASE(i) + 0x20) +#define SPI_USR_ADDR_BITLEN 0x0000003F +#define SPI_USR_ADDR_BITLEN_S 26 +#define SPI_USR_MOSI_BITLEN 0x000001FF +#define SPI_USR_MOSI_BITLEN_S 17 +#define SPI_USR_MISO_BITLEN 0x000001FF +#define SPI_USR_MISO_BITLEN_S 8 + +#define SPI_USR_DUMMY_CYCLELEN 0x000000FF +#define SPI_USR_DUMMY_CYCLELEN_S 0 + +#define SPI_USER2(i) (REG_SPI_BASE(i) + 0x24) +#define SPI_USR_COMMAND_BITLEN 0x0000000F +#define SPI_USR_COMMAND_BITLEN_S 28 +#define SPI_USR_COMMAND_VALUE 0x0000FFFF +#define SPI_USR_COMMAND_VALUE_S 0 + +#define SPI_WR_STATUS(i) (REG_SPI_BASE(i) + 0x28) +#define SPI_PIN(i) (REG_SPI_BASE(i) + 0x2C) +#define SPI_CS2_DIS (BIT(2)) +#define SPI_CS1_DIS (BIT(1)) +#define SPI_CS0_DIS (BIT(0)) +#define SPI_IDLE_EDGE (BIT(29)) + +#define SPI_SLAVE(i) (REG_SPI_BASE(i) + 0x30) +#define SPI_SYNC_RESET (BIT(31)) +#define SPI_SLAVE_MODE (BIT(30)) +#define SPI_SLV_WR_RD_BUF_EN (BIT(29)) +#define SPI_SLV_WR_RD_STA_EN (BIT(28)) +#define SPI_SLV_CMD_DEFINE (BIT(27)) +#define SPI_TRANS_CNT 0x0000000F +#define SPI_TRANS_CNT_S 23 +#define SPI_TRANS_DONE_EN (BIT(9)) +#define SPI_SLV_WR_STA_DONE_EN (BIT(8)) +#define SPI_SLV_RD_STA_DONE_EN (BIT(7)) +#define SPI_SLV_WR_BUF_DONE_EN (BIT(6)) +#define SPI_SLV_RD_BUF_DONE_EN (BIT(5)) + + + +#define SLV_SPI_INT_EN 0x0000001f +#define SLV_SPI_INT_EN_S 5 + +#define SPI_TRANS_DONE (BIT(4)) +#define SPI_SLV_WR_STA_DONE (BIT(3)) +#define SPI_SLV_RD_STA_DONE (BIT(2)) +#define SPI_SLV_WR_BUF_DONE (BIT(1)) +#define SPI_SLV_RD_BUF_DONE (BIT(0)) + +#define SPI_SLAVE1(i) (REG_SPI_BASE(i) + 0x34) +#define SPI_SLV_STATUS_BITLEN 0x0000001F +#define SPI_SLV_STATUS_BITLEN_S 27 +#define SPI_SLV_BUF_BITLEN 0x000001FF +#define SPI_SLV_BUF_BITLEN_S 16 +#define SPI_SLV_RD_ADDR_BITLEN 0x0000003F +#define SPI_SLV_RD_ADDR_BITLEN_S 10 +#define SPI_SLV_WR_ADDR_BITLEN 0x0000003F +#define SPI_SLV_WR_ADDR_BITLEN_S 4 + +#define SPI_SLV_WRSTA_DUMMY_EN (BIT(3)) +#define SPI_SLV_RDSTA_DUMMY_EN (BIT(2)) +#define SPI_SLV_WRBUF_DUMMY_EN (BIT(1)) +#define SPI_SLV_RDBUF_DUMMY_EN (BIT(0)) + + + +#define SPI_SLAVE2(i) (REG_SPI_BASE(i) + 0x38) +#define SPI_SLV_WRBUF_DUMMY_CYCLELEN 0X000000FF +#define SPI_SLV_WRBUF_DUMMY_CYCLELEN_S 24 +#define SPI_SLV_RDBUF_DUMMY_CYCLELEN 0X000000FF +#define SPI_SLV_RDBUF_DUMMY_CYCLELEN_S 16 +#define SPI_SLV_WRSTR_DUMMY_CYCLELEN 0X000000FF +#define SPI_SLV_WRSTR_DUMMY_CYCLELEN_S 8 +#define SPI_SLV_RDSTR_DUMMY_CYCLELEN 0x000000FF +#define SPI_SLV_RDSTR_DUMMY_CYCLELEN_S 0 + +#define SPI_SLAVE3(i) (REG_SPI_BASE(i) + 0x3C) +#define SPI_SLV_WRSTA_CMD_VALUE 0x000000FF +#define SPI_SLV_WRSTA_CMD_VALUE_S 24 +#define SPI_SLV_RDSTA_CMD_VALUE 0x000000FF +#define SPI_SLV_RDSTA_CMD_VALUE_S 16 +#define SPI_SLV_WRBUF_CMD_VALUE 0x000000FF +#define SPI_SLV_WRBUF_CMD_VALUE_S 8 +#define SPI_SLV_RDBUF_CMD_VALUE 0x000000FF +#define SPI_SLV_RDBUF_CMD_VALUE_S 0 + +#define SPI_W0(i) (REG_SPI_BASE(i) +0x40) +#define SPI_W1(i) (REG_SPI_BASE(i) +0x44) +#define SPI_W2(i) (REG_SPI_BASE(i) +0x48) +#define SPI_W3(i) (REG_SPI_BASE(i) +0x4C) +#define SPI_W4(i) (REG_SPI_BASE(i) +0x50) +#define SPI_W5(i) (REG_SPI_BASE(i) +0x54) +#define SPI_W6(i) (REG_SPI_BASE(i) +0x58) +#define SPI_W7(i) (REG_SPI_BASE(i) +0x5C) +#define SPI_W8(i) (REG_SPI_BASE(i) +0x60) +#define SPI_W9(i) (REG_SPI_BASE(i) +0x64) +#define SPI_W10(i) (REG_SPI_BASE(i) +0x68) +#define SPI_W11(i) (REG_SPI_BASE(i) +0x6C) +#define SPI_W12(i) (REG_SPI_BASE(i) +0x70) +#define SPI_W13(i) (REG_SPI_BASE(i) +0x74) +#define SPI_W14(i) (REG_SPI_BASE(i) +0x78) +#define SPI_W15(i) (REG_SPI_BASE(i) +0x7C) + +#define SPI_EXT3(i) (REG_SPI_BASE(i) + 0xFC) +#define SPI_INT_HOLD_ENA 0x00000003 +#define SPI_INT_HOLD_ENA_S 0 +#endif // SPI_REGISTER_H_INCLUDED diff --git a/lib/base64-1.1.1/LICENSE b/lib/base64-1.1.1/LICENSE new file mode 100644 index 000000000..1cef10b50 --- /dev/null +++ b/lib/base64-1.1.1/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Densaugeo + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/lib/base64-1.1.1/Makefile b/lib/base64-1.1.1/Makefile new file mode 100644 index 000000000..b7d209d6f --- /dev/null +++ b/lib/base64-1.1.1/Makefile @@ -0,0 +1,9 @@ +CXX ?= g++ +CFLAGS ?= -Wall -I src + +test: catch.cpp catch.hpp src/base64.hpp + $(CXX) $(CFLAGS) catch.cpp -o catch + ./catch + +clean: + rm catch diff --git a/lib/base64-1.1.1/README.md b/lib/base64-1.1.1/README.md new file mode 100644 index 000000000..c97623848 --- /dev/null +++ b/lib/base64-1.1.1/README.md @@ -0,0 +1,44 @@ +# base64_arduino + +Base64 encoder/decoder for arduino repo + +[![npm](https://img.shields.io/npm/l/express.svg)]() +[![Build Status](https://travis-ci.org/Densaugeo/base64_arduino.svg?branch=master)](https://travis-ci.org/Densaugeo/base64_arduino) + +## Installation + +Add base64.cpp and base64.hpp to your project folder or library search path, put `#include "base64.hpp"` in your source, and pass base64.cpp to your compiler + +## Usage + +~~~ +unsigned char binary[] = {133, 244, 117, 206, 178, 195}; +unsigned char base64[9]; + +unsigned int base64_length = encode_base64(binary, 6, base64); + +printf("%d\n", base64_length); // Prints "8" +printf((char *) base64); // Prints "hfR1zrLD" +~~~ + +~~~ +unsigned char base64[] = "hfR1zrLD"; +unsigned char binary[6]; + +unsigned int binary_length = decode_base64(base64, binary); + +printf("[%d, %d, %d, %d, %d, %d]\n", // Prints "[133, 244, 117, 206, 178, 195]" + binary[0], binary[1], binary[2], + binary[3], binary[4], binary[5]); +printf("%d\n", binary_length); // Prints "6" +~~~ + +## Details + +Uses common web conventions - '+' for 62, '/' for 63, '=' for padding. Note that invalid base64 characters are interpreted as padding. + +Can be compiled as C, uses .*pp extensions because it is usually used in C++ projects and is tested for C++. + +## License + +MIT diff --git a/lib/base64-1.1.1/catch.cpp b/lib/base64-1.1.1/catch.cpp new file mode 100644 index 000000000..e1c686819 --- /dev/null +++ b/lib/base64-1.1.1/catch.cpp @@ -0,0 +1,465 @@ +#define CATCH_CONFIG_MAIN +#include "catch.hpp" +#include "base64.hpp" + +TEST_CASE("encode_base64_length()", "[]") { + SECTION("Zero length") { + REQUIRE(encode_base64_length(0) == 0); + } + + SECTION("Divisible by 3 (no padding)") { + REQUIRE(encode_base64_length(3) == 4); + REQUIRE(encode_base64_length(6) == 8); + REQUIRE(encode_base64_length(60) == 80); + } + + SECTION("Not divisible by 3 (padded)") { + REQUIRE(encode_base64_length(1) == 4); + REQUIRE(encode_base64_length(2) == 4); + REQUIRE(encode_base64_length(4) == 8); + REQUIRE(encode_base64_length(5) == 8); + REQUIRE(encode_base64_length(7) == 12); + REQUIRE(encode_base64_length(256) == 344); + } + + SECTION("Large") { + REQUIRE(encode_base64_length(65536) == 87384); + } +} + +TEST_CASE("encode_base64()", "[]") { + unsigned char actual_base64[100]; + + SECTION("Zero length") { + unsigned char binary_0[] = {}; + encode_base64(binary_0, 0, actual_base64); // Should give 'AQIDBUNDQQgEIIY4' + REQUIRE(memcmp(actual_base64, "", 1) == 0); + } + + SECTION("Length 1 (single section padded)") { + unsigned char binary_0[] = {0}; + encode_base64(binary_0, 1, actual_base64); + REQUIRE(memcmp(actual_base64, "AA==", 5) == 0); + + unsigned char binary_1[] = {3}; + encode_base64(binary_1, 1, actual_base64); + REQUIRE(memcmp(actual_base64, "Aw==", 5) == 0); + + unsigned char binary_2[] = {8}; + encode_base64(binary_2, 1, actual_base64); + REQUIRE(memcmp(actual_base64, "CA==", 5) == 0); + + unsigned char binary_3[] = {145}; + encode_base64(binary_3, 1, actual_base64); + REQUIRE(memcmp(actual_base64, "kQ==", 5) == 0); + + unsigned char binary_4[] = {56}; + encode_base64(binary_4, 1, actual_base64); + REQUIRE(memcmp(actual_base64, "OA==", 5) == 0); + + unsigned char binary_5[] = {54}; + encode_base64(binary_5, 1, actual_base64); + REQUIRE(memcmp(actual_base64, "Ng==", 5) == 0); + + unsigned char binary_6[] = {181}; + encode_base64(binary_6, 1, actual_base64); + REQUIRE(memcmp(actual_base64, "tQ==", 5) == 0); + + unsigned char binary_7[] = {79}; + encode_base64(binary_7, 1, actual_base64); + REQUIRE(memcmp(actual_base64, "Tw==", 5) == 0); + + unsigned char binary_8[] = {115}; + encode_base64(binary_8, 1, actual_base64); + REQUIRE(memcmp(actual_base64, "cw==", 5) == 0); + + unsigned char binary_9[] = {255}; + encode_base64(binary_9, 1, actual_base64); + REQUIRE(memcmp(actual_base64, "/w==", 5) == 0); + } + + SECTION("Length 2 (single section padded)") { + unsigned char binary_0[] = {0, 0}; + encode_base64(binary_0, 2, actual_base64); + REQUIRE(memcmp(actual_base64, "AAA=", 5) == 0); + + unsigned char binary_1[] = {49, 42}; + encode_base64(binary_1, 2, actual_base64); + REQUIRE(memcmp(actual_base64, "MSo=", 5) == 0); + + unsigned char binary_2[] = {133, 38}; + encode_base64(binary_2, 2, actual_base64); + REQUIRE(memcmp(actual_base64, "hSY=", 5) == 0); + + unsigned char binary_3[] = {61, 127}; + encode_base64(binary_3, 2, actual_base64); + REQUIRE(memcmp(actual_base64, "PX8=", 5) == 0); + + unsigned char binary_4[] = {109, 80}; + encode_base64(binary_4, 2, actual_base64); + REQUIRE(memcmp(actual_base64, "bVA=", 5) == 0); + + unsigned char binary_5[] = {47, 213}; + encode_base64(binary_5, 2, actual_base64); + REQUIRE(memcmp(actual_base64, "L9U=", 5) == 0); + + unsigned char binary_6[] = {172, 205}; + encode_base64(binary_6, 2, actual_base64); + REQUIRE(memcmp(actual_base64, "rM0=", 5) == 0); + + unsigned char binary_7[] = {191, 240}; + encode_base64(binary_7, 2, actual_base64); + REQUIRE(memcmp(actual_base64, "v/A=", 5) == 0); + + unsigned char binary_8[] = {107, 248}; + encode_base64(binary_8, 2, actual_base64); + REQUIRE(memcmp(actual_base64, "a/g=", 5) == 0); + + unsigned char binary_9[] = {255, 255}; + encode_base64(binary_9, 2, actual_base64); + REQUIRE(memcmp(actual_base64, "//8=", 5) == 0); + } + + SECTION("Length 3 (single section no padding)") { + unsigned char binary_0[] = {0, 0, 0}; + encode_base64(binary_0, 3, actual_base64); + REQUIRE(memcmp(actual_base64, "AAAA", 5) == 0); + + unsigned char binary_1[] = {151, 167, 18}; + encode_base64(binary_1, 3, actual_base64); + REQUIRE(memcmp(actual_base64, "l6cS", 5) == 0); + + unsigned char binary_2[] = {23, 174, 50}; + encode_base64(binary_2, 3, actual_base64); + REQUIRE(memcmp(actual_base64, "F64y", 5) == 0); + + unsigned char binary_3[] = {143, 205, 227}; + encode_base64(binary_3, 3, actual_base64); + REQUIRE(memcmp(actual_base64, "j83j", 5) == 0); + + unsigned char binary_4[] = {60, 18, 186}; + encode_base64(binary_4, 3, actual_base64); + REQUIRE(memcmp(actual_base64, "PBK6", 5) == 0); + + unsigned char binary_5[] = {100, 34, 201}; + encode_base64(binary_5, 3, actual_base64); + REQUIRE(memcmp(actual_base64, "ZCLJ", 5) == 0); + + unsigned char binary_6[] = {52, 83, 129}; + encode_base64(binary_6, 3, actual_base64); + REQUIRE(memcmp(actual_base64, "NFOB", 5) == 0); + + unsigned char binary_7[] = {241, 202, 185}; + encode_base64(binary_7, 3, actual_base64); + REQUIRE(memcmp(actual_base64, "8cq5", 5) == 0); + + unsigned char binary_8[] = {149, 190, 208}; + encode_base64(binary_8, 3, actual_base64); + REQUIRE(memcmp(actual_base64, "lb7Q", 5) == 0); + + unsigned char binary_9[] = {255, 255, 255}; + encode_base64(binary_9, 3, actual_base64); + REQUIRE(memcmp(actual_base64, "////", 5) == 0); + } + + SECTION("Length divisible by 3 (no padding)") { + unsigned char binary_0[] = {117, 213, 35, 151, 133, 255}; + encode_base64(binary_0, 6, actual_base64); + REQUIRE(memcmp(actual_base64, "ddUjl4X/", 9) == 0); + + unsigned char binary_1[] = {90, 95, 209, 235, 58, 255}; + encode_base64(binary_1, 6, actual_base64); + REQUIRE(memcmp(actual_base64, "Wl/R6zr/", 9) == 0); + + unsigned char binary_2[] = {133, 244, 117, 206, 178, 195, 249, 84, 248}; + encode_base64(binary_2, 9, actual_base64); + REQUIRE(memcmp(actual_base64, "hfR1zrLD+VT4", 13) == 0); + + unsigned char binary_3[] = {7, 27, 226, 144, 59, 237, 79, 62, 191}; + encode_base64(binary_3, 9, actual_base64); + REQUIRE(memcmp(actual_base64, "BxvikDvtTz6/", 13) == 0); + + unsigned char binary_4[] = {99, 225, 39, 195, 8, 43, 209, 151, 8, 43, 195, 183}; + encode_base64(binary_4, 12, actual_base64); + REQUIRE(memcmp(actual_base64, "Y+Enwwgr0ZcIK8O3", 17) == 0); + + unsigned char binary_5[] = {171, 65, 164, 64, 60, 221, 46, 226, 252, 167, 250, 252}; + encode_base64(binary_5, 12, actual_base64); + REQUIRE(memcmp(actual_base64, "q0GkQDzdLuL8p/r8", 17) == 0); + + unsigned char binary_6[] = {248, 127, 14, 241, 93, 177, 152, 46, 255, 127, 92, 84, 56, 59, 152, 132, 113, 115, 252, 70, 190, 224, 130, 155, 86, 172, 159, 162, 30, 127}; + encode_base64(binary_6, 30, actual_base64); + REQUIRE(memcmp(actual_base64, "+H8O8V2xmC7/f1xUODuYhHFz/Ea+4IKbVqyfoh5/", 41) == 0); + + unsigned char binary_7[] = {157, 12, 248, 83, 148, 156, 196, 30, 186, 28, 52, 192, 171, 142, 6, 105, 128, 131, 89, 5, 3, 131, 215, 192, 87, 215, 244, 141, 127, 17}; + encode_base64(binary_7, 30, actual_base64); + REQUIRE(memcmp(actual_base64, "nQz4U5ScxB66HDTAq44GaYCDWQUDg9fAV9f0jX8R", 41) == 0); + + unsigned char binary_8[] = {180, 179, 175, 132, 162, 219, 3, 18, 96, 162, 214, 232, 49, 120, 59, 133, 102, 93, 67, 34, 186, 28, 6, 28, 195, 69, 249, 44, 140, 115, 55, 215, 68, 99, 130, 160, 32, 181, 172, 125, 144, 8, 21, 119, 60, 213, 156, 230, 243, 87, 101, 167, 136, 94, 242, 174, 239, 81, 67, 101}; + encode_base64(binary_8, 60, actual_base64); + REQUIRE(memcmp(actual_base64, "tLOvhKLbAxJgotboMXg7hWZdQyK6HAYcw0X5LIxzN9dEY4KgILWsfZAIFXc81Zzm81dlp4he8q7vUUNl", 81) == 0); + + unsigned char binary_9[] = {165, 186, 12, 82, 241, 34, 63, 218, 215, 28, 105, 126, 37, 69, 255, 36, 235, 103, 194, 236, 81, 115, 192, 61, 247, 128, 43, 38, 58, 140, 208, 9, 34, 145, 252, 209, 150, 227, 35, 241, 46, 25, 170, 198, 191, 87, 43, 206, 250, 199, 158, 193, 96, 249, 79, 142, 39, 216, 36, 236}; + encode_base64(binary_9, 60, actual_base64); + REQUIRE(memcmp(actual_base64, "pboMUvEiP9rXHGl+JUX/JOtnwuxRc8A994ArJjqM0AkikfzRluMj8S4Zqsa/VyvO+seewWD5T44n2CTs", 81) == 0); + } + + SECTION("Length not divisible by 3 (padded)") { + unsigned char binary_0[] = {216, 183, 235, 10}; + encode_base64(binary_0, 4, actual_base64); + REQUIRE(memcmp(actual_base64, "2LfrCg==", 9) == 0); + + unsigned char binary_1[] = {7, 254, 182, 49, 158}; + encode_base64(binary_1, 5, actual_base64); + REQUIRE(memcmp(actual_base64, "B/62MZ4=", 9) == 0); + + unsigned char binary_2[] = {71, 58, 223, 154, 93, 69, 18}; + encode_base64(binary_2, 7, actual_base64); + REQUIRE(memcmp(actual_base64, "Rzrfml1FEg==", 13) == 0); + + unsigned char binary_3[] = {226, 127, 31, 206, 19, 75, 35, 80}; + encode_base64(binary_3, 8, actual_base64); + REQUIRE(memcmp(actual_base64, "4n8fzhNLI1A=", 13) == 0); + + unsigned char binary_4[] = {5, 36, 50, 78, 218, 198, 242, 85, 235, 72, 78, 139, 103}; + encode_base64(binary_4, 13, actual_base64); + REQUIRE(memcmp(actual_base64, "BSQyTtrG8lXrSE6LZw==", 21) == 0); + + unsigned char binary_5[] = {161, 176, 49, 33, 148, 150, 94, 252, 21, 249, 106, 49, 216, 124, 219, 233, 133, 102, 32, 182, 193}; + encode_base64(binary_5, 21, actual_base64); + REQUIRE(memcmp(actual_base64, "obAxIZSWXvwV+Wox2Hzb6YVmILbB", 29) == 0); + + unsigned char binary_6[] = {136, 116, 151, 174, 215, 54, 64, 218, 197, 148, 149, 17, 183, 59, 177, 54, 172, 135, 192, 202, 183, 3, 254, 51, 83, 217}; + encode_base64(binary_6, 26, actual_base64); + REQUIRE(memcmp(actual_base64, "iHSXrtc2QNrFlJURtzuxNqyHwMq3A/4zU9k=", 37) == 0); + + unsigned char binary_7[] = {181, 16, 71, 30, 145, 101, 21, 170, 45, 24, 201, 78, 83, 31, 175, 132, 127, 108, 88, 7, 37, 154, 196, 139, 87, 68, 243, 6, 180, 36, 89, 10, 67, 73}; + encode_base64(binary_7, 34, actual_base64); + REQUIRE(memcmp(actual_base64, "tRBHHpFlFaotGMlOUx+vhH9sWAclmsSLV0TzBrQkWQpDSQ==", 49) == 0); + + unsigned char binary_8[] = {24, 6, 234, 175, 34, 198, 47, 173, 234, 158, 106, 203, 80, 171, 218, 163, 60, 105, 183, 152, 73, 142, 190, 107, 189, 223, 215, 169, 63, 169, 163, 29, 9, 134, 235, 107, 35, 5, 16, 50, 7}; + encode_base64(binary_8, 41, actual_base64); + REQUIRE(memcmp(actual_base64, "GAbqryLGL63qnmrLUKvaozxpt5hJjr5rvd/XqT+pox0JhutrIwUQMgc=", 57) == 0); + + unsigned char binary_9[] = {220, 92, 67, 95, 157, 76, 162, 210, 224, 202, 136, 157, 104, 178, 103, 81, 35, 103, 244, 71, 92, 25, 69, 64, 61, 232, 198, 108, 217, 106, 63, 103, 234, 39, 156, 108, 4, 101, 212, 198, 57, 223, 75, 132, 160, 26, 193, 139, 16, 89, 12, 45, 183, 133, 33}; + encode_base64(binary_9, 55, actual_base64); + REQUIRE(memcmp(actual_base64, "3FxDX51MotLgyoidaLJnUSNn9EdcGUVAPejGbNlqP2fqJ5xsBGXUxjnfS4SgGsGLEFkMLbeFIQ==", 77) == 0); + } +} + +TEST_CASE("decode_base64_length()", "[]") { + SECTION("Zero length") { + REQUIRE(decode_base64_length((unsigned char*) "") == 0); + } + + SECTION("Divisible by 4 (no padding)") { + REQUIRE(decode_base64_length((unsigned char*) "AAAA") == 3); + REQUIRE(decode_base64_length((unsigned char*) "////") == 3); + REQUIRE(decode_base64_length((unsigned char*) "Y+Enwwgr0ZcIK8O3") == 12); + REQUIRE(decode_base64_length((unsigned char*) "tLOvhKLbAxJgotboMXg7hWZdQyK6HAYcw0X5LIxzN9dEY4KgILWsfZAIFXc81Zzm81dlp4he8q7vUUNl") == 60); + } + + SECTION("Not divisible by 4 (padded)") { + REQUIRE(decode_base64_length((unsigned char*) "AA==") == 1); + REQUIRE(decode_base64_length((unsigned char*) "Aw==") == 1); + REQUIRE(decode_base64_length((unsigned char*) "a/g=") == 2); + REQUIRE(decode_base64_length((unsigned char*) "//8=") == 2); + REQUIRE(decode_base64_length((unsigned char*) "4n8fzhNLI1A=") == 8); + REQUIRE(decode_base64_length((unsigned char*) "3FxDX51MotLgyoidaLJnUSNn9EdcGUVAPejGbNlqP2fqJ5xsBGXUxjnfS4SgGsGLEFkMLbeFIQ==") == 55); + } + + SECTION("Not divisible by 4 (padding missing)") { + REQUIRE(decode_base64_length((unsigned char*) "AA") == 1); + REQUIRE(decode_base64_length((unsigned char*) "Aw") == 1); + REQUIRE(decode_base64_length((unsigned char*) "a/g") == 2); + REQUIRE(decode_base64_length((unsigned char*) "//8") == 2); + REQUIRE(decode_base64_length((unsigned char*) "4n8fzhNLI1A") == 8); + REQUIRE(decode_base64_length((unsigned char*) "3FxDX51MotLgyoidaLJnUSNn9EdcGUVAPejGbNlqP2fqJ5xsBGXUxjnfS4SgGsGLEFkMLbeFIQ") == 55); + } + + SECTION("Padding in middle cuts off string") { + REQUIRE(decode_base64_length((unsigned char*) "AA==4n8fzhNL") == 1); + REQUIRE(decode_base64_length((unsigned char*) "Aw=4n8fzhNL") == 1); + REQUIRE(decode_base64_length((unsigned char*) "a/g=4n8fzhNL==") == 2); + REQUIRE(decode_base64_length((unsigned char*) "//8=4n8fzhNL") == 2); + REQUIRE(decode_base64_length((unsigned char*) "4n8fzhNLI1A=4n8fzhNL====") == 8); + REQUIRE(decode_base64_length((unsigned char*) "3FxDX51MotLgyoidaLJnUSNn9EdcGUVAPejGbNlqP2fqJ5xsBGXUxjnfS4SgGsGLEFkMLbeFIQ==4n8fzhNL") == 55); + } + + SECTION("Extra padding is ignored") { + REQUIRE(decode_base64_length((unsigned char*) "Aw========") == 1); + REQUIRE(decode_base64_length((unsigned char*) "a/g=======") == 2); + REQUIRE(decode_base64_length((unsigned char*) "Aw========") == 1); + REQUIRE(decode_base64_length((unsigned char*) "a/g==========") == 2); + REQUIRE(decode_base64_length((unsigned char*) "4n8fzhNLI1A===========") == 8); + REQUIRE(decode_base64_length((unsigned char*) "3FxDX51MotLgyoidaLJnUSNn9EdcGUVAPejGbNlqP2fqJ5xsBGXUxjnfS4SgGsGLEFkMLbeFIQ=========") == 55); + } + + SECTION("Non-base64 characcters are interpreted as padding") { + REQUIRE(decode_base64_length((unsigned char*) "Aw:;") == 1); + REQUIRE(decode_base64_length((unsigned char*) "Aw`'@") == 1); + REQUIRE(decode_base64_length((unsigned char*) "a/g~") == 2); + REQUIRE(decode_base64_length((unsigned char*) "a/g[|") == 2); + REQUIRE(decode_base64_length((unsigned char*) "4n8fzhNLI1A]") == 8); + REQUIRE(decode_base64_length((unsigned char*) "Y+Enwwgr0ZcIK8O3{}") == 12); + REQUIRE(decode_base64_length((unsigned char*) "AA-^4n8fzhNL") == 1); + } +} + +TEST_CASE("decode_base64()", "[]") { + unsigned char actual_binary[100]; + + // Zero length case ignored, because it is verified to return no data in decode_base64_length() + + SECTION("Divisible by 4 (no padding)") { + unsigned char expected_binary_0[] = {0, 0, 0}; + decode_base64((unsigned char*) "AAAA", actual_binary); + REQUIRE(memcmp(actual_binary, expected_binary_0, 3) == 0); + + unsigned char expected_binary_1[] = {255, 255, 255}; + decode_base64((unsigned char*) "////", actual_binary); + REQUIRE(memcmp(actual_binary, expected_binary_1, 3) == 0); + + unsigned char expected_binary_2[] = {99, 225, 39, 195, 8, 43, 209, 151, 8, 43, 195, 183}; + decode_base64((unsigned char*) "Y+Enwwgr0ZcIK8O3", actual_binary); + REQUIRE(memcmp(actual_binary, expected_binary_2, 12) == 0); + + unsigned char expected_binary_3[] = {180, 179, 175, 132, 162, 219, 3, 18, 96, 162, 214, 232, 49, 120, 59, 133, 102, 93, 67, 34, 186, 28, 6, 28, 195, 69, 249, 44, 140, 115, 55, 215, 68, 99, 130, 160, 32, 181, 172, 125, 144, 8, 21, 119, 60, 213, 156, 230, 243, 87, 101, 167, 136, 94, 242, 174, 239, 81, 67, 101}; + decode_base64((unsigned char*) "tLOvhKLbAxJgotboMXg7hWZdQyK6HAYcw0X5LIxzN9dEY4KgILWsfZAIFXc81Zzm81dlp4he8q7vUUNl", actual_binary); + REQUIRE(memcmp(actual_binary, expected_binary_3, 60) == 0); + } + + SECTION("Not divisible by 4 (padded)") { + unsigned char expected_binary_0[] = {0}; + decode_base64((unsigned char*) "AA==", actual_binary); + REQUIRE(memcmp(actual_binary, expected_binary_0, 1) == 0); + + unsigned char expected_binary_1[] = {3}; + decode_base64((unsigned char*) "Aw==", actual_binary); + REQUIRE(memcmp(actual_binary, expected_binary_1, 1) == 0); + + unsigned char expected_binary_2[] = {107, 248}; + decode_base64((unsigned char*) "a/g=", actual_binary); + REQUIRE(memcmp(actual_binary, expected_binary_2, 2) == 0); + + unsigned char expected_binary_3[] = {255, 255}; + decode_base64((unsigned char*) "//8=", actual_binary); + REQUIRE(memcmp(actual_binary, expected_binary_3, 2) == 0); + + unsigned char expected_binary_4[] = {226, 127, 31, 206, 19, 75, 35, 80}; + decode_base64((unsigned char*) "4n8fzhNLI1A=", actual_binary); + REQUIRE(memcmp(actual_binary, expected_binary_4, 8) == 0); + + unsigned char expected_binary_5[] = {220, 92, 67, 95, 157, 76, 162, 210, 224, 202, 136, 157, 104, 178, 103, 81, 35, 103, 244, 71, 92, 25, 69, 64, 61, 232, 198, 108, 217, 106, 63, 103, 234, 39, 156, 108, 4, 101, 212, 198, 57, 223, 75, 132, 160, 26, 193, 139, 16, 89, 12, 45, 183, 133, 33}; + decode_base64((unsigned char*) "3FxDX51MotLgyoidaLJnUSNn9EdcGUVAPejGbNlqP2fqJ5xsBGXUxjnfS4SgGsGLEFkMLbeFIQ==", actual_binary); + REQUIRE(memcmp(actual_binary, expected_binary_5, 55) == 0); + } + + SECTION("Not divisible by 4 (padding missing)") { + unsigned char expected_binary_0[] = {0}; + decode_base64((unsigned char*) "AA", actual_binary); + REQUIRE(memcmp(actual_binary, expected_binary_0, 1) == 0); + + unsigned char expected_binary_1[] = {3}; + decode_base64((unsigned char*) "Aw", actual_binary); + REQUIRE(memcmp(actual_binary, expected_binary_1, 1) == 0); + + unsigned char expected_binary_2[] = {107, 248}; + decode_base64((unsigned char*) "a/g", actual_binary); + REQUIRE(memcmp(actual_binary, expected_binary_2, 2) == 0); + + unsigned char expected_binary_3[] = {255, 255}; + decode_base64((unsigned char*) "//8", actual_binary); + REQUIRE(memcmp(actual_binary, expected_binary_3, 2) == 0); + + unsigned char expected_binary_4[] = {226, 127, 31, 206, 19, 75, 35, 80}; + decode_base64((unsigned char*) "4n8fzhNLI1A", actual_binary); + REQUIRE(memcmp(actual_binary, expected_binary_4, 8) == 0); + + unsigned char expected_binary_5[] = {220, 92, 67, 95, 157, 76, 162, 210, 224, 202, 136, 157, 104, 178, 103, 81, 35, 103, 244, 71, 92, 25, 69, 64, 61, 232, 198, 108, 217, 106, 63, 103, 234, 39, 156, 108, 4, 101, 212, 198, 57, 223, 75, 132, 160, 26, 193, 139, 16, 89, 12, 45, 183, 133, 33}; + decode_base64((unsigned char*) "3FxDX51MotLgyoidaLJnUSNn9EdcGUVAPejGbNlqP2fqJ5xsBGXUxjnfS4SgGsGLEFkMLbeFIQ", actual_binary); + REQUIRE(memcmp(actual_binary, expected_binary_5, 55) == 0); + } + + SECTION("Padding in middle cuts off string") { + unsigned char expected_binary_0[] = {0}; + decode_base64((unsigned char*) "AA==4n8fzhNL", actual_binary); + REQUIRE(memcmp(actual_binary, expected_binary_0, 1) == 0); + + unsigned char expected_binary_1[] = {3}; + decode_base64((unsigned char*) "Aw=4n8fzhNL", actual_binary); + REQUIRE(memcmp(actual_binary, expected_binary_1, 1) == 0); + + unsigned char expected_binary_2[] = {107, 248}; + decode_base64((unsigned char*) "a/g=4n8fzhNL==", actual_binary); + REQUIRE(memcmp(actual_binary, expected_binary_2, 2) == 0); + + unsigned char expected_binary_3[] = {255, 255}; + decode_base64((unsigned char*) "//8=4n8fzhNL", actual_binary); + REQUIRE(memcmp(actual_binary, expected_binary_3, 2) == 0); + + unsigned char expected_binary_4[] = {226, 127, 31, 206, 19, 75, 35, 80}; + decode_base64((unsigned char*) "4n8fzhNLI1A=4n8fzhNL====", actual_binary); + REQUIRE(memcmp(actual_binary, expected_binary_4, 8) == 0); + + unsigned char expected_binary_5[] = {220, 92, 67, 95, 157, 76, 162, 210, 224, 202, 136, 157, 104, 178, 103, 81, 35, 103, 244, 71, 92, 25, 69, 64, 61, 232, 198, 108, 217, 106, 63, 103, 234, 39, 156, 108, 4, 101, 212, 198, 57, 223, 75, 132, 160, 26, 193, 139, 16, 89, 12, 45, 183, 133, 33}; + decode_base64((unsigned char*) "3FxDX51MotLgyoidaLJnUSNn9EdcGUVAPejGbNlqP2fqJ5xsBGXUxjnfS4SgGsGLEFkMLbeFIQ==4n8fzhNL", actual_binary); + REQUIRE(memcmp(actual_binary, expected_binary_5, 55) == 0); + } + + SECTION("Extra padding is ignored") { + unsigned char expected_binary_0[] = {3}; + decode_base64((unsigned char*) "Aw========", actual_binary); + REQUIRE(memcmp(actual_binary, expected_binary_0, 1) == 0); + + unsigned char expected_binary_1[] = {107, 248}; + decode_base64((unsigned char*) "a/g=======", actual_binary); + REQUIRE(memcmp(actual_binary, expected_binary_1, 2) == 0); + + unsigned char expected_binary_2[] = {3}; + decode_base64((unsigned char*) "Aw========", actual_binary); + REQUIRE(memcmp(actual_binary, expected_binary_2, 1) == 0); + + unsigned char expected_binary_3[] = {107, 248}; + decode_base64((unsigned char*) "a/g==========", actual_binary); + REQUIRE(memcmp(actual_binary, expected_binary_3, 2) == 0); + + unsigned char expected_binary_4[] = {226, 127, 31, 206, 19, 75, 35, 80}; + decode_base64((unsigned char*) "4n8fzhNLI1A===========", actual_binary); + REQUIRE(memcmp(actual_binary, expected_binary_4, 8) == 0); + + unsigned char expected_binary_5[] = {220, 92, 67, 95, 157, 76, 162, 210, 224, 202, 136, 157, 104, 178, 103, 81, 35, 103, 244, 71, 92, 25, 69, 64, 61, 232, 198, 108, 217, 106, 63, 103, 234, 39, 156, 108, 4, 101, 212, 198, 57, 223, 75, 132, 160, 26, 193, 139, 16, 89, 12, 45, 183, 133, 33}; + decode_base64((unsigned char*) "3FxDX51MotLgyoidaLJnUSNn9EdcGUVAPejGbNlqP2fqJ5xsBGXUxjnfS4SgGsGLEFkMLbeFIQ=========", actual_binary); + REQUIRE(memcmp(actual_binary, expected_binary_5, 55) == 0); + } + + SECTION("Non-base64 characcters are interpreted as padding") { + unsigned char expected_binary_0[] = {3}; + decode_base64((unsigned char*) "Aw==", actual_binary); + REQUIRE(memcmp(actual_binary, expected_binary_0, 1) == 0); + + unsigned char expected_binary_1[] = {3}; + decode_base64((unsigned char*) "Aw===", actual_binary); + REQUIRE(memcmp(actual_binary, expected_binary_1, 1) == 0); + + unsigned char expected_binary_2[] = {107, 248}; + decode_base64((unsigned char*) "a/g=", actual_binary); + REQUIRE(memcmp(actual_binary, expected_binary_2, 2) == 0); + + unsigned char expected_binary_3[] = {107, 248}; + decode_base64((unsigned char*) "a/g==", actual_binary); + REQUIRE(memcmp(actual_binary, expected_binary_3, 2) == 0); + + unsigned char expected_binary_4[] = {226, 127, 31, 206, 19, 75, 35, 80}; + decode_base64((unsigned char*) "4n8fzhNLI1A=", actual_binary); + REQUIRE(memcmp(actual_binary, expected_binary_4, 8) == 0); + + unsigned char expected_binary_5[] = {99, 225, 39, 195, 8, 43, 209, 151, 8, 43, 195, 183}; + decode_base64((unsigned char*) "Y+Enwwgr0ZcIK8O3==", actual_binary); + REQUIRE(memcmp(actual_binary, expected_binary_5, 12) == 0); + + unsigned char expected_binary_6[] = {0}; + decode_base64((unsigned char*) "AA==4n8fzhNL", actual_binary); + REQUIRE(memcmp(actual_binary, expected_binary_6, 1) == 0); + } +} \ No newline at end of file diff --git a/lib/base64-1.1.1/catch.hpp b/lib/base64-1.1.1/catch.hpp new file mode 100644 index 000000000..5cc33a838 --- /dev/null +++ b/lib/base64-1.1.1/catch.hpp @@ -0,0 +1,10359 @@ +/* + * Catch v1.3.4 + * Generated: 2016-02-10 19:24:03.089683 + * ---------------------------------------------------------- + * This file has been merged from multiple headers. Please don't edit it directly + * Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED +#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED + +#define TWOBLUECUBES_CATCH_HPP_INCLUDED + +#ifdef __clang__ +# pragma clang system_header +#elif defined __GNUC__ +# pragma GCC system_header +#endif + +// #included from: internal/catch_suppress_warnings.h + +#ifdef __clang__ +# ifdef __ICC // icpc defines the __clang__ macro +# pragma warning(push) +# pragma warning(disable: 161 1682) +# else // __ICC +# pragma clang diagnostic ignored "-Wglobal-constructors" +# pragma clang diagnostic ignored "-Wvariadic-macros" +# pragma clang diagnostic ignored "-Wc99-extensions" +# pragma clang diagnostic ignored "-Wunused-variable" +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wpadded" +# pragma clang diagnostic ignored "-Wc++98-compat" +# pragma clang diagnostic ignored "-Wc++98-compat-pedantic" +# pragma clang diagnostic ignored "-Wswitch-enum" +# pragma clang diagnostic ignored "-Wcovered-switch-default" +# endif +#elif defined __GNUC__ +# pragma GCC diagnostic ignored "-Wvariadic-macros" +# pragma GCC diagnostic ignored "-Wunused-variable" +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wpadded" +#endif +#if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER) +# define CATCH_IMPL +#endif + +#ifdef CATCH_IMPL +# ifndef CLARA_CONFIG_MAIN +# define CLARA_CONFIG_MAIN_NOT_DEFINED +# define CLARA_CONFIG_MAIN +# endif +#endif + +// #included from: internal/catch_notimplemented_exception.h +#define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_H_INCLUDED + +// #included from: catch_common.h +#define TWOBLUECUBES_CATCH_COMMON_H_INCLUDED + +#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line +#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) +#define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) + +#define INTERNAL_CATCH_STRINGIFY2( expr ) #expr +#define INTERNAL_CATCH_STRINGIFY( expr ) INTERNAL_CATCH_STRINGIFY2( expr ) + +#include +#include +#include + +// #included from: catch_compiler_capabilities.h +#define TWOBLUECUBES_CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED + +// Detect a number of compiler features - mostly C++11/14 conformance - by compiler +// The following features are defined: +// +// CATCH_CONFIG_CPP11_NULLPTR : is nullptr supported? +// CATCH_CONFIG_CPP11_NOEXCEPT : is noexcept supported? +// CATCH_CONFIG_CPP11_GENERATED_METHODS : The delete and default keywords for compiler generated methods +// CATCH_CONFIG_CPP11_IS_ENUM : std::is_enum is supported? +// CATCH_CONFIG_CPP11_TUPLE : std::tuple is supported +// CATCH_CONFIG_CPP11_LONG_LONG : is long long supported? +// CATCH_CONFIG_CPP11_OVERRIDE : is override supported? +// CATCH_CONFIG_CPP11_UNIQUE_PTR : is unique_ptr supported (otherwise use auto_ptr) + +// CATCH_CONFIG_CPP11_OR_GREATER : Is C++11 supported? + +// CATCH_CONFIG_VARIADIC_MACROS : are variadic macros supported? + +// **************** +// Note to maintainers: if new toggles are added please document them +// in configuration.md, too +// **************** + +// In general each macro has a _NO_ form +// (e.g. CATCH_CONFIG_CPP11_NO_NULLPTR) which disables the feature. +// Many features, at point of detection, define an _INTERNAL_ macro, so they +// can be combined, en-mass, with the _NO_ forms later. + +// All the C++11 features can be disabled with CATCH_CONFIG_NO_CPP11 + +#ifdef __clang__ + +# if __has_feature(cxx_nullptr) +# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR +# endif + +# if __has_feature(cxx_noexcept) +# define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT +# endif + +#endif // __clang__ + +//////////////////////////////////////////////////////////////////////////////// +// Borland +#ifdef __BORLANDC__ + +#endif // __BORLANDC__ + +//////////////////////////////////////////////////////////////////////////////// +// EDG +#ifdef __EDG_VERSION__ + +#endif // __EDG_VERSION__ + +//////////////////////////////////////////////////////////////////////////////// +// Digital Mars +#ifdef __DMC__ + +#endif // __DMC__ + +//////////////////////////////////////////////////////////////////////////////// +// GCC +#ifdef __GNUC__ + +#if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__) +# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR +#endif + +// - otherwise more recent versions define __cplusplus >= 201103L +// and will get picked up below + +#endif // __GNUC__ + +//////////////////////////////////////////////////////////////////////////////// +// Visual C++ +#ifdef _MSC_VER + +#if (_MSC_VER >= 1600) +# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR +# define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR +#endif + +#if (_MSC_VER >= 1900 ) // (VC++ 13 (VS2015)) +#define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT +#define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS +#endif + +#endif // _MSC_VER + +//////////////////////////////////////////////////////////////////////////////// + +// Use variadic macros if the compiler supports them +#if ( defined _MSC_VER && _MSC_VER > 1400 && !defined __EDGE__) || \ + ( defined __WAVE__ && __WAVE_HAS_VARIADICS ) || \ + ( defined __GNUC__ && __GNUC__ >= 3 ) || \ + ( !defined __cplusplus && __STDC_VERSION__ >= 199901L || __cplusplus >= 201103L ) + +#define CATCH_INTERNAL_CONFIG_VARIADIC_MACROS + +#endif + +//////////////////////////////////////////////////////////////////////////////// +// C++ language feature support + +// catch all support for C++11 +#if defined(__cplusplus) && __cplusplus >= 201103L + +# define CATCH_CPP11_OR_GREATER + +# if !defined(CATCH_INTERNAL_CONFIG_CPP11_NULLPTR) +# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR +# endif + +# ifndef CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT +# define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT +# endif + +# ifndef CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS +# define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS +# endif + +# ifndef CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM +# define CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM +# endif + +# ifndef CATCH_INTERNAL_CONFIG_CPP11_TUPLE +# define CATCH_INTERNAL_CONFIG_CPP11_TUPLE +# endif + +# ifndef CATCH_INTERNAL_CONFIG_VARIADIC_MACROS +# define CATCH_INTERNAL_CONFIG_VARIADIC_MACROS +# endif + +# if !defined(CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG) +# define CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG +# endif + +# if !defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE) +# define CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE +# endif +# if !defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) +# define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR +# endif + +#endif // __cplusplus >= 201103L + +// Now set the actual defines based on the above + anything the user has configured +#if defined(CATCH_INTERNAL_CONFIG_CPP11_NULLPTR) && !defined(CATCH_CONFIG_CPP11_NO_NULLPTR) && !defined(CATCH_CONFIG_CPP11_NULLPTR) && !defined(CATCH_CONFIG_NO_CPP11) +# define CATCH_CONFIG_CPP11_NULLPTR +#endif +#if defined(CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_CONFIG_CPP11_NO_NOEXCEPT) && !defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_CONFIG_NO_CPP11) +# define CATCH_CONFIG_CPP11_NOEXCEPT +#endif +#if defined(CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS) && !defined(CATCH_CONFIG_CPP11_NO_GENERATED_METHODS) && !defined(CATCH_CONFIG_CPP11_GENERATED_METHODS) && !defined(CATCH_CONFIG_NO_CPP11) +# define CATCH_CONFIG_CPP11_GENERATED_METHODS +#endif +#if defined(CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM) && !defined(CATCH_CONFIG_CPP11_NO_IS_ENUM) && !defined(CATCH_CONFIG_CPP11_IS_ENUM) && !defined(CATCH_CONFIG_NO_CPP11) +# define CATCH_CONFIG_CPP11_IS_ENUM +#endif +#if defined(CATCH_INTERNAL_CONFIG_CPP11_TUPLE) && !defined(CATCH_CONFIG_CPP11_NO_TUPLE) && !defined(CATCH_CONFIG_CPP11_TUPLE) && !defined(CATCH_CONFIG_NO_CPP11) +# define CATCH_CONFIG_CPP11_TUPLE +#endif +#if defined(CATCH_INTERNAL_CONFIG_VARIADIC_MACROS) && !defined(CATCH_CONFIG_NO_VARIADIC_MACROS) && !defined(CATCH_CONFIG_VARIADIC_MACROS) +# define CATCH_CONFIG_VARIADIC_MACROS +#endif +#if defined(CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_NO_LONG_LONG) && !defined(CATCH_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_NO_CPP11) +# define CATCH_CONFIG_CPP11_LONG_LONG +#endif +#if defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_NO_OVERRIDE) && !defined(CATCH_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_NO_CPP11) +# define CATCH_CONFIG_CPP11_OVERRIDE +#endif +#if defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_NO_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_NO_CPP11) +# define CATCH_CONFIG_CPP11_UNIQUE_PTR +#endif + +// noexcept support: +#if defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_NOEXCEPT) +# define CATCH_NOEXCEPT noexcept +# define CATCH_NOEXCEPT_IS(x) noexcept(x) +#else +# define CATCH_NOEXCEPT throw() +# define CATCH_NOEXCEPT_IS(x) +#endif + +// nullptr support +#ifdef CATCH_CONFIG_CPP11_NULLPTR +# define CATCH_NULL nullptr +#else +# define CATCH_NULL NULL +#endif + +// override support +#ifdef CATCH_CONFIG_CPP11_OVERRIDE +# define CATCH_OVERRIDE override +#else +# define CATCH_OVERRIDE +#endif + +// unique_ptr support +#ifdef CATCH_CONFIG_CPP11_UNIQUE_PTR +# define CATCH_AUTO_PTR( T ) std::unique_ptr +#else +# define CATCH_AUTO_PTR( T ) std::auto_ptr +#endif + +namespace Catch { + + struct IConfig; + + struct CaseSensitive { enum Choice { + Yes, + No + }; }; + + class NonCopyable { +#ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS + NonCopyable( NonCopyable const& ) = delete; + NonCopyable( NonCopyable && ) = delete; + NonCopyable& operator = ( NonCopyable const& ) = delete; + NonCopyable& operator = ( NonCopyable && ) = delete; +#else + NonCopyable( NonCopyable const& info ); + NonCopyable& operator = ( NonCopyable const& ); +#endif + + protected: + NonCopyable() {} + virtual ~NonCopyable(); + }; + + class SafeBool { + public: + typedef void (SafeBool::*type)() const; + + static type makeSafe( bool value ) { + return value ? &SafeBool::trueValue : 0; + } + private: + void trueValue() const {} + }; + + template + inline void deleteAll( ContainerT& container ) { + typename ContainerT::const_iterator it = container.begin(); + typename ContainerT::const_iterator itEnd = container.end(); + for(; it != itEnd; ++it ) + delete *it; + } + template + inline void deleteAllValues( AssociativeContainerT& container ) { + typename AssociativeContainerT::const_iterator it = container.begin(); + typename AssociativeContainerT::const_iterator itEnd = container.end(); + for(; it != itEnd; ++it ) + delete it->second; + } + + bool startsWith( std::string const& s, std::string const& prefix ); + bool endsWith( std::string const& s, std::string const& suffix ); + bool contains( std::string const& s, std::string const& infix ); + void toLowerInPlace( std::string& s ); + std::string toLower( std::string const& s ); + std::string trim( std::string const& str ); + bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ); + + struct pluralise { + pluralise( std::size_t count, std::string const& label ); + + friend std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ); + + std::size_t m_count; + std::string m_label; + }; + + struct SourceLineInfo { + + SourceLineInfo(); + SourceLineInfo( char const* _file, std::size_t _line ); + SourceLineInfo( SourceLineInfo const& other ); +# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS + SourceLineInfo( SourceLineInfo && ) = default; + SourceLineInfo& operator = ( SourceLineInfo const& ) = default; + SourceLineInfo& operator = ( SourceLineInfo && ) = default; +# endif + bool empty() const; + bool operator == ( SourceLineInfo const& other ) const; + bool operator < ( SourceLineInfo const& other ) const; + + std::string file; + std::size_t line; + }; + + std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ); + + // This is just here to avoid compiler warnings with macro constants and boolean literals + inline bool isTrue( bool value ){ return value; } + inline bool alwaysTrue() { return true; } + inline bool alwaysFalse() { return false; } + + void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo ); + + void seedRng( IConfig const& config ); + unsigned int rngSeed(); + + // Use this in variadic streaming macros to allow + // >> +StreamEndStop + // as well as + // >> stuff +StreamEndStop + struct StreamEndStop { + std::string operator+() { + return std::string(); + } + }; + template + T const& operator + ( T const& value, StreamEndStop ) { + return value; + } +} + +#define CATCH_INTERNAL_LINEINFO ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) +#define CATCH_INTERNAL_ERROR( msg ) ::Catch::throwLogicError( msg, CATCH_INTERNAL_LINEINFO ); + +#include + +namespace Catch { + + class NotImplementedException : public std::exception + { + public: + NotImplementedException( SourceLineInfo const& lineInfo ); + NotImplementedException( NotImplementedException const& ) {} + + virtual ~NotImplementedException() CATCH_NOEXCEPT {} + + virtual const char* what() const CATCH_NOEXCEPT; + + private: + std::string m_what; + SourceLineInfo m_lineInfo; + }; + +} // end namespace Catch + +/////////////////////////////////////////////////////////////////////////////// +#define CATCH_NOT_IMPLEMENTED throw Catch::NotImplementedException( CATCH_INTERNAL_LINEINFO ) + +// #included from: internal/catch_context.h +#define TWOBLUECUBES_CATCH_CONTEXT_H_INCLUDED + +// #included from: catch_interfaces_generators.h +#define TWOBLUECUBES_CATCH_INTERFACES_GENERATORS_H_INCLUDED + +#include + +namespace Catch { + + struct IGeneratorInfo { + virtual ~IGeneratorInfo(); + virtual bool moveNext() = 0; + virtual std::size_t getCurrentIndex() const = 0; + }; + + struct IGeneratorsForTest { + virtual ~IGeneratorsForTest(); + + virtual IGeneratorInfo& getGeneratorInfo( std::string const& fileInfo, std::size_t size ) = 0; + virtual bool moveNext() = 0; + }; + + IGeneratorsForTest* createGeneratorsForTest(); + +} // end namespace Catch + +// #included from: catch_ptr.hpp +#define TWOBLUECUBES_CATCH_PTR_HPP_INCLUDED + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +#endif + +namespace Catch { + + // An intrusive reference counting smart pointer. + // T must implement addRef() and release() methods + // typically implementing the IShared interface + template + class Ptr { + public: + Ptr() : m_p( CATCH_NULL ){} + Ptr( T* p ) : m_p( p ){ + if( m_p ) + m_p->addRef(); + } + Ptr( Ptr const& other ) : m_p( other.m_p ){ + if( m_p ) + m_p->addRef(); + } + ~Ptr(){ + if( m_p ) + m_p->release(); + } + void reset() { + if( m_p ) + m_p->release(); + m_p = CATCH_NULL; + } + Ptr& operator = ( T* p ){ + Ptr temp( p ); + swap( temp ); + return *this; + } + Ptr& operator = ( Ptr const& other ){ + Ptr temp( other ); + swap( temp ); + return *this; + } + void swap( Ptr& other ) { std::swap( m_p, other.m_p ); } + T* get() const{ return m_p; } + T& operator*() const { return *m_p; } + T* operator->() const { return m_p; } + bool operator !() const { return m_p == CATCH_NULL; } + operator SafeBool::type() const { return SafeBool::makeSafe( m_p != CATCH_NULL ); } + + private: + T* m_p; + }; + + struct IShared : NonCopyable { + virtual ~IShared(); + virtual void addRef() const = 0; + virtual void release() const = 0; + }; + + template + struct SharedImpl : T { + + SharedImpl() : m_rc( 0 ){} + + virtual void addRef() const { + ++m_rc; + } + virtual void release() const { + if( --m_rc == 0 ) + delete this; + } + + mutable unsigned int m_rc; + }; + +} // end namespace Catch + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#include +#include +#include + +namespace Catch { + + class TestCase; + class Stream; + struct IResultCapture; + struct IRunner; + struct IGeneratorsForTest; + struct IConfig; + + struct IContext + { + virtual ~IContext(); + + virtual IResultCapture* getResultCapture() = 0; + virtual IRunner* getRunner() = 0; + virtual size_t getGeneratorIndex( std::string const& fileInfo, size_t totalSize ) = 0; + virtual bool advanceGeneratorsForCurrentTest() = 0; + virtual Ptr getConfig() const = 0; + }; + + struct IMutableContext : IContext + { + virtual ~IMutableContext(); + virtual void setResultCapture( IResultCapture* resultCapture ) = 0; + virtual void setRunner( IRunner* runner ) = 0; + virtual void setConfig( Ptr const& config ) = 0; + }; + + IContext& getCurrentContext(); + IMutableContext& getCurrentMutableContext(); + void cleanUpContext(); + Stream createStream( std::string const& streamName ); + +} + +// #included from: internal/catch_test_registry.hpp +#define TWOBLUECUBES_CATCH_TEST_REGISTRY_HPP_INCLUDED + +// #included from: catch_interfaces_testcase.h +#define TWOBLUECUBES_CATCH_INTERFACES_TESTCASE_H_INCLUDED + +#include + +namespace Catch { + + class TestSpec; + + struct ITestCase : IShared { + virtual void invoke () const = 0; + protected: + virtual ~ITestCase(); + }; + + class TestCase; + struct IConfig; + + struct ITestCaseRegistry { + virtual ~ITestCaseRegistry(); + virtual std::vector const& getAllTests() const = 0; + virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0; + }; + + bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); + std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); + std::vector const& getAllTestCasesSorted( IConfig const& config ); + +} + +namespace Catch { + +template +class MethodTestCase : public SharedImpl { + +public: + MethodTestCase( void (C::*method)() ) : m_method( method ) {} + + virtual void invoke() const { + C obj; + (obj.*m_method)(); + } + +private: + virtual ~MethodTestCase() {} + + void (C::*m_method)(); +}; + +typedef void(*TestFunction)(); + +struct NameAndDesc { + NameAndDesc( const char* _name = "", const char* _description= "" ) + : name( _name ), description( _description ) + {} + + const char* name; + const char* description; +}; + +void registerTestCase + ( ITestCase* testCase, + char const* className, + NameAndDesc const& nameAndDesc, + SourceLineInfo const& lineInfo ); + +struct AutoReg { + + AutoReg + ( TestFunction function, + SourceLineInfo const& lineInfo, + NameAndDesc const& nameAndDesc ); + + template + AutoReg + ( void (C::*method)(), + char const* className, + NameAndDesc const& nameAndDesc, + SourceLineInfo const& lineInfo ) { + + registerTestCase + ( new MethodTestCase( method ), + className, + nameAndDesc, + lineInfo ); + } + + ~AutoReg(); + +private: + AutoReg( AutoReg const& ); + void operator= ( AutoReg const& ); +}; + +void registerTestCaseFunction + ( TestFunction function, + SourceLineInfo const& lineInfo, + NameAndDesc const& nameAndDesc ); + +} // end namespace Catch + +#ifdef CATCH_CONFIG_VARIADIC_MACROS + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_TESTCASE( ... ) \ + static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )(); \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); }\ + static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )() + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); } + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... )\ + namespace{ \ + struct INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) : ClassName{ \ + void test(); \ + }; \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test, #ClassName, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); \ + } \ + void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test() + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \ + Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); + +#else + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_TESTCASE( Name, Desc ) \ + static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )(); \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); }\ + static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )() + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, Name, Desc ) \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( Name, Desc ), CATCH_INTERNAL_LINEINFO ); } + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, TestName, Desc )\ + namespace{ \ + struct INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) : ClassName{ \ + void test(); \ + }; \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test, #ClassName, Catch::NameAndDesc( TestName, Desc ), CATCH_INTERNAL_LINEINFO ); \ + } \ + void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test() + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, Name, Desc ) \ + Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); +#endif + +// #included from: internal/catch_capture.hpp +#define TWOBLUECUBES_CATCH_CAPTURE_HPP_INCLUDED + +// #included from: catch_result_builder.h +#define TWOBLUECUBES_CATCH_RESULT_BUILDER_H_INCLUDED + +// #included from: catch_result_type.h +#define TWOBLUECUBES_CATCH_RESULT_TYPE_H_INCLUDED + +namespace Catch { + + // ResultWas::OfType enum + struct ResultWas { enum OfType { + Unknown = -1, + Ok = 0, + Info = 1, + Warning = 2, + + FailureBit = 0x10, + + ExpressionFailed = FailureBit | 1, + ExplicitFailure = FailureBit | 2, + + Exception = 0x100 | FailureBit, + + ThrewException = Exception | 1, + DidntThrowException = Exception | 2, + + FatalErrorCondition = 0x200 | FailureBit + + }; }; + + inline bool isOk( ResultWas::OfType resultType ) { + return ( resultType & ResultWas::FailureBit ) == 0; + } + inline bool isJustInfo( int flags ) { + return flags == ResultWas::Info; + } + + // ResultDisposition::Flags enum + struct ResultDisposition { enum Flags { + Normal = 0x01, + + ContinueOnFailure = 0x02, // Failures fail test, but execution continues + FalseTest = 0x04, // Prefix expression with ! + SuppressFail = 0x08 // Failures are reported but do not fail the test + }; }; + + inline ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ) { + return static_cast( static_cast( lhs ) | static_cast( rhs ) ); + } + + inline bool shouldContinueOnFailure( int flags ) { return ( flags & ResultDisposition::ContinueOnFailure ) != 0; } + inline bool isFalseTest( int flags ) { return ( flags & ResultDisposition::FalseTest ) != 0; } + inline bool shouldSuppressFailure( int flags ) { return ( flags & ResultDisposition::SuppressFail ) != 0; } + +} // end namespace Catch + +// #included from: catch_assertionresult.h +#define TWOBLUECUBES_CATCH_ASSERTIONRESULT_H_INCLUDED + +#include + +namespace Catch { + + struct AssertionInfo + { + AssertionInfo() {} + AssertionInfo( std::string const& _macroName, + SourceLineInfo const& _lineInfo, + std::string const& _capturedExpression, + ResultDisposition::Flags _resultDisposition ); + + std::string macroName; + SourceLineInfo lineInfo; + std::string capturedExpression; + ResultDisposition::Flags resultDisposition; + }; + + struct AssertionResultData + { + AssertionResultData() : resultType( ResultWas::Unknown ) {} + + std::string reconstructedExpression; + std::string message; + ResultWas::OfType resultType; + }; + + class AssertionResult { + public: + AssertionResult(); + AssertionResult( AssertionInfo const& info, AssertionResultData const& data ); + ~AssertionResult(); +# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS + AssertionResult( AssertionResult const& ) = default; + AssertionResult( AssertionResult && ) = default; + AssertionResult& operator = ( AssertionResult const& ) = default; + AssertionResult& operator = ( AssertionResult && ) = default; +# endif + + bool isOk() const; + bool succeeded() const; + ResultWas::OfType getResultType() const; + bool hasExpression() const; + bool hasMessage() const; + std::string getExpression() const; + std::string getExpressionInMacro() const; + bool hasExpandedExpression() const; + std::string getExpandedExpression() const; + std::string getMessage() const; + SourceLineInfo getSourceInfo() const; + std::string getTestMacroName() const; + + protected: + AssertionInfo m_info; + AssertionResultData m_resultData; + }; + +} // end namespace Catch + +// #included from: catch_matchers.hpp +#define TWOBLUECUBES_CATCH_MATCHERS_HPP_INCLUDED + +namespace Catch { +namespace Matchers { + namespace Impl { + + namespace Generic { + template class AllOf; + template class AnyOf; + template class Not; + } + + template + struct Matcher : SharedImpl + { + typedef ExpressionT ExpressionType; + + virtual ~Matcher() {} + virtual Ptr clone() const = 0; + virtual bool match( ExpressionT const& expr ) const = 0; + virtual std::string toString() const = 0; + + Generic::AllOf operator && ( Matcher const& other ) const; + Generic::AnyOf operator || ( Matcher const& other ) const; + Generic::Not operator ! () const; + }; + + template + struct MatcherImpl : Matcher { + + virtual Ptr > clone() const { + return Ptr >( new DerivedT( static_cast( *this ) ) ); + } + }; + + namespace Generic { + template + class Not : public MatcherImpl, ExpressionT> { + public: + explicit Not( Matcher const& matcher ) : m_matcher(matcher.clone()) {} + Not( Not const& other ) : m_matcher( other.m_matcher ) {} + + virtual bool match( ExpressionT const& expr ) const CATCH_OVERRIDE { + return !m_matcher->match( expr ); + } + + virtual std::string toString() const CATCH_OVERRIDE { + return "not " + m_matcher->toString(); + } + private: + Ptr< Matcher > m_matcher; + }; + + template + class AllOf : public MatcherImpl, ExpressionT> { + public: + + AllOf() {} + AllOf( AllOf const& other ) : m_matchers( other.m_matchers ) {} + + AllOf& add( Matcher const& matcher ) { + m_matchers.push_back( matcher.clone() ); + return *this; + } + virtual bool match( ExpressionT const& expr ) const + { + for( std::size_t i = 0; i < m_matchers.size(); ++i ) + if( !m_matchers[i]->match( expr ) ) + return false; + return true; + } + virtual std::string toString() const { + std::ostringstream oss; + oss << "( "; + for( std::size_t i = 0; i < m_matchers.size(); ++i ) { + if( i != 0 ) + oss << " and "; + oss << m_matchers[i]->toString(); + } + oss << " )"; + return oss.str(); + } + + AllOf operator && ( Matcher const& other ) const { + AllOf allOfExpr( *this ); + allOfExpr.add( other ); + return allOfExpr; + } + + private: + std::vector > > m_matchers; + }; + + template + class AnyOf : public MatcherImpl, ExpressionT> { + public: + + AnyOf() {} + AnyOf( AnyOf const& other ) : m_matchers( other.m_matchers ) {} + + AnyOf& add( Matcher const& matcher ) { + m_matchers.push_back( matcher.clone() ); + return *this; + } + virtual bool match( ExpressionT const& expr ) const + { + for( std::size_t i = 0; i < m_matchers.size(); ++i ) + if( m_matchers[i]->match( expr ) ) + return true; + return false; + } + virtual std::string toString() const { + std::ostringstream oss; + oss << "( "; + for( std::size_t i = 0; i < m_matchers.size(); ++i ) { + if( i != 0 ) + oss << " or "; + oss << m_matchers[i]->toString(); + } + oss << " )"; + return oss.str(); + } + + AnyOf operator || ( Matcher const& other ) const { + AnyOf anyOfExpr( *this ); + anyOfExpr.add( other ); + return anyOfExpr; + } + + private: + std::vector > > m_matchers; + }; + + } // namespace Generic + + template + Generic::AllOf Matcher::operator && ( Matcher const& other ) const { + Generic::AllOf allOfExpr; + allOfExpr.add( *this ); + allOfExpr.add( other ); + return allOfExpr; + } + + template + Generic::AnyOf Matcher::operator || ( Matcher const& other ) const { + Generic::AnyOf anyOfExpr; + anyOfExpr.add( *this ); + anyOfExpr.add( other ); + return anyOfExpr; + } + + template + Generic::Not Matcher::operator ! () const { + return Generic::Not( *this ); + } + + namespace StdString { + + inline std::string makeString( std::string const& str ) { return str; } + inline std::string makeString( const char* str ) { return str ? std::string( str ) : std::string(); } + + struct CasedString + { + CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity ) + : m_caseSensitivity( caseSensitivity ), + m_str( adjustString( str ) ) + {} + std::string adjustString( std::string const& str ) const { + return m_caseSensitivity == CaseSensitive::No + ? toLower( str ) + : str; + + } + std::string toStringSuffix() const + { + return m_caseSensitivity == CaseSensitive::No + ? " (case insensitive)" + : ""; + } + CaseSensitive::Choice m_caseSensitivity; + std::string m_str; + }; + + struct Equals : MatcherImpl { + Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) + : m_data( str, caseSensitivity ) + {} + Equals( Equals const& other ) : m_data( other.m_data ){} + + virtual ~Equals(); + + virtual bool match( std::string const& expr ) const { + return m_data.m_str == m_data.adjustString( expr );; + } + virtual std::string toString() const { + return "equals: \"" + m_data.m_str + "\"" + m_data.toStringSuffix(); + } + + CasedString m_data; + }; + + struct Contains : MatcherImpl { + Contains( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) + : m_data( substr, caseSensitivity ){} + Contains( Contains const& other ) : m_data( other.m_data ){} + + virtual ~Contains(); + + virtual bool match( std::string const& expr ) const { + return m_data.adjustString( expr ).find( m_data.m_str ) != std::string::npos; + } + virtual std::string toString() const { + return "contains: \"" + m_data.m_str + "\"" + m_data.toStringSuffix(); + } + + CasedString m_data; + }; + + struct StartsWith : MatcherImpl { + StartsWith( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) + : m_data( substr, caseSensitivity ){} + + StartsWith( StartsWith const& other ) : m_data( other.m_data ){} + + virtual ~StartsWith(); + + virtual bool match( std::string const& expr ) const { + return startsWith( m_data.adjustString( expr ), m_data.m_str ); + } + virtual std::string toString() const { + return "starts with: \"" + m_data.m_str + "\"" + m_data.toStringSuffix(); + } + + CasedString m_data; + }; + + struct EndsWith : MatcherImpl { + EndsWith( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) + : m_data( substr, caseSensitivity ){} + EndsWith( EndsWith const& other ) : m_data( other.m_data ){} + + virtual ~EndsWith(); + + virtual bool match( std::string const& expr ) const { + return endsWith( m_data.adjustString( expr ), m_data.m_str ); + } + virtual std::string toString() const { + return "ends with: \"" + m_data.m_str + "\"" + m_data.toStringSuffix(); + } + + CasedString m_data; + }; + } // namespace StdString + } // namespace Impl + + // The following functions create the actual matcher objects. + // This allows the types to be inferred + template + inline Impl::Generic::Not Not( Impl::Matcher const& m ) { + return Impl::Generic::Not( m ); + } + + template + inline Impl::Generic::AllOf AllOf( Impl::Matcher const& m1, + Impl::Matcher const& m2 ) { + return Impl::Generic::AllOf().add( m1 ).add( m2 ); + } + template + inline Impl::Generic::AllOf AllOf( Impl::Matcher const& m1, + Impl::Matcher const& m2, + Impl::Matcher const& m3 ) { + return Impl::Generic::AllOf().add( m1 ).add( m2 ).add( m3 ); + } + template + inline Impl::Generic::AnyOf AnyOf( Impl::Matcher const& m1, + Impl::Matcher const& m2 ) { + return Impl::Generic::AnyOf().add( m1 ).add( m2 ); + } + template + inline Impl::Generic::AnyOf AnyOf( Impl::Matcher const& m1, + Impl::Matcher const& m2, + Impl::Matcher const& m3 ) { + return Impl::Generic::AnyOf().add( m1 ).add( m2 ).add( m3 ); + } + + inline Impl::StdString::Equals Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) { + return Impl::StdString::Equals( str, caseSensitivity ); + } + inline Impl::StdString::Equals Equals( const char* str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) { + return Impl::StdString::Equals( Impl::StdString::makeString( str ), caseSensitivity ); + } + inline Impl::StdString::Contains Contains( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) { + return Impl::StdString::Contains( substr, caseSensitivity ); + } + inline Impl::StdString::Contains Contains( const char* substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) { + return Impl::StdString::Contains( Impl::StdString::makeString( substr ), caseSensitivity ); + } + inline Impl::StdString::StartsWith StartsWith( std::string const& substr ) { + return Impl::StdString::StartsWith( substr ); + } + inline Impl::StdString::StartsWith StartsWith( const char* substr ) { + return Impl::StdString::StartsWith( Impl::StdString::makeString( substr ) ); + } + inline Impl::StdString::EndsWith EndsWith( std::string const& substr ) { + return Impl::StdString::EndsWith( substr ); + } + inline Impl::StdString::EndsWith EndsWith( const char* substr ) { + return Impl::StdString::EndsWith( Impl::StdString::makeString( substr ) ); + } + +} // namespace Matchers + +using namespace Matchers; + +} // namespace Catch + +namespace Catch { + + struct TestFailureException{}; + + template class ExpressionLhs; + + struct STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison; + + struct CopyableStream { + CopyableStream() {} + CopyableStream( CopyableStream const& other ) { + oss << other.oss.str(); + } + CopyableStream& operator=( CopyableStream const& other ) { + oss.str(""); + oss << other.oss.str(); + return *this; + } + std::ostringstream oss; + }; + + class ResultBuilder { + public: + ResultBuilder( char const* macroName, + SourceLineInfo const& lineInfo, + char const* capturedExpression, + ResultDisposition::Flags resultDisposition, + char const* secondArg = "" ); + + template + ExpressionLhs operator <= ( T const& operand ); + ExpressionLhs operator <= ( bool value ); + + template + ResultBuilder& operator << ( T const& value ) { + m_stream.oss << value; + return *this; + } + + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( RhsT const& ); + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( RhsT const& ); + + ResultBuilder& setResultType( ResultWas::OfType result ); + ResultBuilder& setResultType( bool result ); + ResultBuilder& setLhs( std::string const& lhs ); + ResultBuilder& setRhs( std::string const& rhs ); + ResultBuilder& setOp( std::string const& op ); + + void endExpression(); + + std::string reconstructExpression() const; + AssertionResult build() const; + + void useActiveException( ResultDisposition::Flags resultDisposition = ResultDisposition::Normal ); + void captureResult( ResultWas::OfType resultType ); + void captureExpression(); + void captureExpectedException( std::string const& expectedMessage ); + void captureExpectedException( Matchers::Impl::Matcher const& matcher ); + void handleResult( AssertionResult const& result ); + void react(); + bool shouldDebugBreak() const; + bool allowThrows() const; + + private: + AssertionInfo m_assertionInfo; + AssertionResultData m_data; + struct ExprComponents { + ExprComponents() : testFalse( false ) {} + bool testFalse; + std::string lhs, rhs, op; + } m_exprComponents; + CopyableStream m_stream; + + bool m_shouldDebugBreak; + bool m_shouldThrow; + }; + +} // namespace Catch + +// Include after due to circular dependency: +// #included from: catch_expression_lhs.hpp +#define TWOBLUECUBES_CATCH_EXPRESSION_LHS_HPP_INCLUDED + +// #included from: catch_evaluate.hpp +#define TWOBLUECUBES_CATCH_EVALUATE_HPP_INCLUDED + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4389) // '==' : signed/unsigned mismatch +#endif + +#include + +namespace Catch { +namespace Internal { + + enum Operator { + IsEqualTo, + IsNotEqualTo, + IsLessThan, + IsGreaterThan, + IsLessThanOrEqualTo, + IsGreaterThanOrEqualTo + }; + + template struct OperatorTraits { static const char* getName(){ return "*error*"; } }; + template<> struct OperatorTraits { static const char* getName(){ return "=="; } }; + template<> struct OperatorTraits { static const char* getName(){ return "!="; } }; + template<> struct OperatorTraits { static const char* getName(){ return "<"; } }; + template<> struct OperatorTraits { static const char* getName(){ return ">"; } }; + template<> struct OperatorTraits { static const char* getName(){ return "<="; } }; + template<> struct OperatorTraits{ static const char* getName(){ return ">="; } }; + + template + inline T& opCast(T const& t) { return const_cast(t); } + +// nullptr_t support based on pull request #154 from Konstantin Baumann +#ifdef CATCH_CONFIG_CPP11_NULLPTR + inline std::nullptr_t opCast(std::nullptr_t) { return nullptr; } +#endif // CATCH_CONFIG_CPP11_NULLPTR + + // So the compare overloads can be operator agnostic we convey the operator as a template + // enum, which is used to specialise an Evaluator for doing the comparison. + template + class Evaluator{}; + + template + struct Evaluator { + static bool evaluate( T1 const& lhs, T2 const& rhs) { + return opCast( lhs ) == opCast( rhs ); + } + }; + template + struct Evaluator { + static bool evaluate( T1 const& lhs, T2 const& rhs ) { + return opCast( lhs ) != opCast( rhs ); + } + }; + template + struct Evaluator { + static bool evaluate( T1 const& lhs, T2 const& rhs ) { + return opCast( lhs ) < opCast( rhs ); + } + }; + template + struct Evaluator { + static bool evaluate( T1 const& lhs, T2 const& rhs ) { + return opCast( lhs ) > opCast( rhs ); + } + }; + template + struct Evaluator { + static bool evaluate( T1 const& lhs, T2 const& rhs ) { + return opCast( lhs ) >= opCast( rhs ); + } + }; + template + struct Evaluator { + static bool evaluate( T1 const& lhs, T2 const& rhs ) { + return opCast( lhs ) <= opCast( rhs ); + } + }; + + template + bool applyEvaluator( T1 const& lhs, T2 const& rhs ) { + return Evaluator::evaluate( lhs, rhs ); + } + + // This level of indirection allows us to specialise for integer types + // to avoid signed/ unsigned warnings + + // "base" overload + template + bool compare( T1 const& lhs, T2 const& rhs ) { + return Evaluator::evaluate( lhs, rhs ); + } + + // unsigned X to int + template bool compare( unsigned int lhs, int rhs ) { + return applyEvaluator( lhs, static_cast( rhs ) ); + } + template bool compare( unsigned long lhs, int rhs ) { + return applyEvaluator( lhs, static_cast( rhs ) ); + } + template bool compare( unsigned char lhs, int rhs ) { + return applyEvaluator( lhs, static_cast( rhs ) ); + } + + // unsigned X to long + template bool compare( unsigned int lhs, long rhs ) { + return applyEvaluator( lhs, static_cast( rhs ) ); + } + template bool compare( unsigned long lhs, long rhs ) { + return applyEvaluator( lhs, static_cast( rhs ) ); + } + template bool compare( unsigned char lhs, long rhs ) { + return applyEvaluator( lhs, static_cast( rhs ) ); + } + + // int to unsigned X + template bool compare( int lhs, unsigned int rhs ) { + return applyEvaluator( static_cast( lhs ), rhs ); + } + template bool compare( int lhs, unsigned long rhs ) { + return applyEvaluator( static_cast( lhs ), rhs ); + } + template bool compare( int lhs, unsigned char rhs ) { + return applyEvaluator( static_cast( lhs ), rhs ); + } + + // long to unsigned X + template bool compare( long lhs, unsigned int rhs ) { + return applyEvaluator( static_cast( lhs ), rhs ); + } + template bool compare( long lhs, unsigned long rhs ) { + return applyEvaluator( static_cast( lhs ), rhs ); + } + template bool compare( long lhs, unsigned char rhs ) { + return applyEvaluator( static_cast( lhs ), rhs ); + } + + // pointer to long (when comparing against NULL) + template bool compare( long lhs, T* rhs ) { + return Evaluator::evaluate( reinterpret_cast( lhs ), rhs ); + } + template bool compare( T* lhs, long rhs ) { + return Evaluator::evaluate( lhs, reinterpret_cast( rhs ) ); + } + + // pointer to int (when comparing against NULL) + template bool compare( int lhs, T* rhs ) { + return Evaluator::evaluate( reinterpret_cast( lhs ), rhs ); + } + template bool compare( T* lhs, int rhs ) { + return Evaluator::evaluate( lhs, reinterpret_cast( rhs ) ); + } + +#ifdef CATCH_CONFIG_CPP11_LONG_LONG + // long long to unsigned X + template bool compare( long long lhs, unsigned int rhs ) { + return applyEvaluator( static_cast( lhs ), rhs ); + } + template bool compare( long long lhs, unsigned long rhs ) { + return applyEvaluator( static_cast( lhs ), rhs ); + } + template bool compare( long long lhs, unsigned long long rhs ) { + return applyEvaluator( static_cast( lhs ), rhs ); + } + template bool compare( long long lhs, unsigned char rhs ) { + return applyEvaluator( static_cast( lhs ), rhs ); + } + + // unsigned long long to X + template bool compare( unsigned long long lhs, int rhs ) { + return applyEvaluator( static_cast( lhs ), rhs ); + } + template bool compare( unsigned long long lhs, long rhs ) { + return applyEvaluator( static_cast( lhs ), rhs ); + } + template bool compare( unsigned long long lhs, long long rhs ) { + return applyEvaluator( static_cast( lhs ), rhs ); + } + template bool compare( unsigned long long lhs, char rhs ) { + return applyEvaluator( static_cast( lhs ), rhs ); + } + + // pointer to long long (when comparing against NULL) + template bool compare( long long lhs, T* rhs ) { + return Evaluator::evaluate( reinterpret_cast( lhs ), rhs ); + } + template bool compare( T* lhs, long long rhs ) { + return Evaluator::evaluate( lhs, reinterpret_cast( rhs ) ); + } +#endif // CATCH_CONFIG_CPP11_LONG_LONG + +#ifdef CATCH_CONFIG_CPP11_NULLPTR + // pointer to nullptr_t (when comparing against nullptr) + template bool compare( std::nullptr_t, T* rhs ) { + return Evaluator::evaluate( nullptr, rhs ); + } + template bool compare( T* lhs, std::nullptr_t ) { + return Evaluator::evaluate( lhs, nullptr ); + } +#endif // CATCH_CONFIG_CPP11_NULLPTR + +} // end of namespace Internal +} // end of namespace Catch + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +// #included from: catch_tostring.h +#define TWOBLUECUBES_CATCH_TOSTRING_H_INCLUDED + +#include +#include +#include +#include +#include + +#ifdef __OBJC__ +// #included from: catch_objc_arc.hpp +#define TWOBLUECUBES_CATCH_OBJC_ARC_HPP_INCLUDED + +#import + +#ifdef __has_feature +#define CATCH_ARC_ENABLED __has_feature(objc_arc) +#else +#define CATCH_ARC_ENABLED 0 +#endif + +void arcSafeRelease( NSObject* obj ); +id performOptionalSelector( id obj, SEL sel ); + +#if !CATCH_ARC_ENABLED +inline void arcSafeRelease( NSObject* obj ) { + [obj release]; +} +inline id performOptionalSelector( id obj, SEL sel ) { + if( [obj respondsToSelector: sel] ) + return [obj performSelector: sel]; + return nil; +} +#define CATCH_UNSAFE_UNRETAINED +#define CATCH_ARC_STRONG +#else +inline void arcSafeRelease( NSObject* ){} +inline id performOptionalSelector( id obj, SEL sel ) { +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" +#endif + if( [obj respondsToSelector: sel] ) + return [obj performSelector: sel]; +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + return nil; +} +#define CATCH_UNSAFE_UNRETAINED __unsafe_unretained +#define CATCH_ARC_STRONG __strong +#endif + +#endif + +#ifdef CATCH_CONFIG_CPP11_TUPLE +#include +#endif + +#ifdef CATCH_CONFIG_CPP11_IS_ENUM +#include +#endif + +namespace Catch { + +// Why we're here. +template +std::string toString( T const& value ); + +// Built in overloads + +std::string toString( std::string const& value ); +std::string toString( std::wstring const& value ); +std::string toString( const char* const value ); +std::string toString( char* const value ); +std::string toString( const wchar_t* const value ); +std::string toString( wchar_t* const value ); +std::string toString( int value ); +std::string toString( unsigned long value ); +std::string toString( unsigned int value ); +std::string toString( const double value ); +std::string toString( const float value ); +std::string toString( bool value ); +std::string toString( char value ); +std::string toString( signed char value ); +std::string toString( unsigned char value ); + +#ifdef CATCH_CONFIG_CPP11_LONG_LONG +std::string toString( long long value ); +std::string toString( unsigned long long value ); +#endif + +#ifdef CATCH_CONFIG_CPP11_NULLPTR +std::string toString( std::nullptr_t ); +#endif + +#ifdef __OBJC__ + std::string toString( NSString const * const& nsstring ); + std::string toString( NSString * CATCH_ARC_STRONG const& nsstring ); + std::string toString( NSObject* const& nsObject ); +#endif + +namespace Detail { + + extern const std::string unprintableString; + + struct BorgType { + template BorgType( T const& ); + }; + + struct TrueType { char sizer[1]; }; + struct FalseType { char sizer[2]; }; + + TrueType& testStreamable( std::ostream& ); + FalseType testStreamable( FalseType ); + + FalseType operator<<( std::ostream const&, BorgType const& ); + + template + struct IsStreamInsertable { + static std::ostream &s; + static T const&t; + enum { value = sizeof( testStreamable(s << t) ) == sizeof( TrueType ) }; + }; + +#if defined(CATCH_CONFIG_CPP11_IS_ENUM) + template::value + > + struct EnumStringMaker + { + static std::string convert( T const& ) { return unprintableString; } + }; + + template + struct EnumStringMaker + { + static std::string convert( T const& v ) + { + return ::Catch::toString( + static_cast::type>(v) + ); + } + }; +#endif + template + struct StringMakerBase { +#if defined(CATCH_CONFIG_CPP11_IS_ENUM) + template + static std::string convert( T const& v ) + { + return EnumStringMaker::convert( v ); + } +#else + template + static std::string convert( T const& ) { return unprintableString; } +#endif + }; + + template<> + struct StringMakerBase { + template + static std::string convert( T const& _value ) { + std::ostringstream oss; + oss << _value; + return oss.str(); + } + }; + + std::string rawMemoryToString( const void *object, std::size_t size ); + + template + inline std::string rawMemoryToString( const T& object ) { + return rawMemoryToString( &object, sizeof(object) ); + } + +} // end namespace Detail + +template +struct StringMaker : + Detail::StringMakerBase::value> {}; + +template +struct StringMaker { + template + static std::string convert( U* p ) { + if( !p ) + return "NULL"; + else + return Detail::rawMemoryToString( p ); + } +}; + +template +struct StringMaker { + static std::string convert( R C::* p ) { + if( !p ) + return "NULL"; + else + return Detail::rawMemoryToString( p ); + } +}; + +namespace Detail { + template + std::string rangeToString( InputIterator first, InputIterator last ); +} + +//template +//struct StringMaker > { +// static std::string convert( std::vector const& v ) { +// return Detail::rangeToString( v.begin(), v.end() ); +// } +//}; + +template +std::string toString( std::vector const& v ) { + return Detail::rangeToString( v.begin(), v.end() ); +} + +#ifdef CATCH_CONFIG_CPP11_TUPLE + +// toString for tuples +namespace TupleDetail { + template< + typename Tuple, + std::size_t N = 0, + bool = (N < std::tuple_size::value) + > + struct ElementPrinter { + static void print( const Tuple& tuple, std::ostream& os ) + { + os << ( N ? ", " : " " ) + << Catch::toString(std::get(tuple)); + ElementPrinter::print(tuple,os); + } + }; + + template< + typename Tuple, + std::size_t N + > + struct ElementPrinter { + static void print( const Tuple&, std::ostream& ) {} + }; + +} + +template +struct StringMaker> { + + static std::string convert( const std::tuple& tuple ) + { + std::ostringstream os; + os << '{'; + TupleDetail::ElementPrinter>::print( tuple, os ); + os << " }"; + return os.str(); + } +}; +#endif // CATCH_CONFIG_CPP11_TUPLE + +namespace Detail { + template + std::string makeString( T const& value ) { + return StringMaker::convert( value ); + } +} // end namespace Detail + +/// \brief converts any type to a string +/// +/// The default template forwards on to ostringstream - except when an +/// ostringstream overload does not exist - in which case it attempts to detect +/// that and writes {?}. +/// Overload (not specialise) this template for custom typs that you don't want +/// to provide an ostream overload for. +template +std::string toString( T const& value ) { + return StringMaker::convert( value ); +} + + namespace Detail { + template + std::string rangeToString( InputIterator first, InputIterator last ) { + std::ostringstream oss; + oss << "{ "; + if( first != last ) { + oss << Catch::toString( *first ); + for( ++first ; first != last ; ++first ) + oss << ", " << Catch::toString( *first ); + } + oss << " }"; + return oss.str(); + } +} + +} // end namespace Catch + +namespace Catch { + +// Wraps the LHS of an expression and captures the operator and RHS (if any) - +// wrapping them all in a ResultBuilder object +template +class ExpressionLhs { + ExpressionLhs& operator = ( ExpressionLhs const& ); +# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS + ExpressionLhs& operator = ( ExpressionLhs && ) = delete; +# endif + +public: + ExpressionLhs( ResultBuilder& rb, T lhs ) : m_rb( rb ), m_lhs( lhs ) {} +# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS + ExpressionLhs( ExpressionLhs const& ) = default; + ExpressionLhs( ExpressionLhs && ) = default; +# endif + + template + ResultBuilder& operator == ( RhsT const& rhs ) { + return captureExpression( rhs ); + } + + template + ResultBuilder& operator != ( RhsT const& rhs ) { + return captureExpression( rhs ); + } + + template + ResultBuilder& operator < ( RhsT const& rhs ) { + return captureExpression( rhs ); + } + + template + ResultBuilder& operator > ( RhsT const& rhs ) { + return captureExpression( rhs ); + } + + template + ResultBuilder& operator <= ( RhsT const& rhs ) { + return captureExpression( rhs ); + } + + template + ResultBuilder& operator >= ( RhsT const& rhs ) { + return captureExpression( rhs ); + } + + ResultBuilder& operator == ( bool rhs ) { + return captureExpression( rhs ); + } + + ResultBuilder& operator != ( bool rhs ) { + return captureExpression( rhs ); + } + + void endExpression() { + bool value = m_lhs ? true : false; + m_rb + .setLhs( Catch::toString( value ) ) + .setResultType( value ) + .endExpression(); + } + + // Only simple binary expressions are allowed on the LHS. + // If more complex compositions are required then place the sub expression in parentheses + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator + ( RhsT const& ); + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator - ( RhsT const& ); + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator / ( RhsT const& ); + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator * ( RhsT const& ); + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( RhsT const& ); + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( RhsT const& ); + +private: + template + ResultBuilder& captureExpression( RhsT const& rhs ) { + return m_rb + .setResultType( Internal::compare( m_lhs, rhs ) ) + .setLhs( Catch::toString( m_lhs ) ) + .setRhs( Catch::toString( rhs ) ) + .setOp( Internal::OperatorTraits::getName() ); + } + +private: + ResultBuilder& m_rb; + T m_lhs; +}; + +} // end namespace Catch + + +namespace Catch { + + template + inline ExpressionLhs ResultBuilder::operator <= ( T const& operand ) { + return ExpressionLhs( *this, operand ); + } + + inline ExpressionLhs ResultBuilder::operator <= ( bool value ) { + return ExpressionLhs( *this, value ); + } + +} // namespace Catch + +// #included from: catch_message.h +#define TWOBLUECUBES_CATCH_MESSAGE_H_INCLUDED + +#include + +namespace Catch { + + struct MessageInfo { + MessageInfo( std::string const& _macroName, + SourceLineInfo const& _lineInfo, + ResultWas::OfType _type ); + + std::string macroName; + SourceLineInfo lineInfo; + ResultWas::OfType type; + std::string message; + unsigned int sequence; + + bool operator == ( MessageInfo const& other ) const { + return sequence == other.sequence; + } + bool operator < ( MessageInfo const& other ) const { + return sequence < other.sequence; + } + private: + static unsigned int globalCount; + }; + + struct MessageBuilder { + MessageBuilder( std::string const& macroName, + SourceLineInfo const& lineInfo, + ResultWas::OfType type ) + : m_info( macroName, lineInfo, type ) + {} + + template + MessageBuilder& operator << ( T const& value ) { + m_stream << value; + return *this; + } + + MessageInfo m_info; + std::ostringstream m_stream; + }; + + class ScopedMessage { + public: + ScopedMessage( MessageBuilder const& builder ); + ScopedMessage( ScopedMessage const& other ); + ~ScopedMessage(); + + MessageInfo m_info; + }; + +} // end namespace Catch + +// #included from: catch_interfaces_capture.h +#define TWOBLUECUBES_CATCH_INTERFACES_CAPTURE_H_INCLUDED + +#include + +namespace Catch { + + class TestCase; + class AssertionResult; + struct AssertionInfo; + struct SectionInfo; + struct SectionEndInfo; + struct MessageInfo; + class ScopedMessageBuilder; + struct Counts; + + struct IResultCapture { + + virtual ~IResultCapture(); + + virtual void assertionEnded( AssertionResult const& result ) = 0; + virtual bool sectionStarted( SectionInfo const& sectionInfo, + Counts& assertions ) = 0; + virtual void sectionEnded( SectionEndInfo const& endInfo ) = 0; + virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) = 0; + virtual void pushScopedMessage( MessageInfo const& message ) = 0; + virtual void popScopedMessage( MessageInfo const& message ) = 0; + + virtual std::string getCurrentTestName() const = 0; + virtual const AssertionResult* getLastResult() const = 0; + + virtual void handleFatalErrorCondition( std::string const& message ) = 0; + }; + + IResultCapture& getResultCapture(); +} + +// #included from: catch_debugger.h +#define TWOBLUECUBES_CATCH_DEBUGGER_H_INCLUDED + +// #included from: catch_platform.h +#define TWOBLUECUBES_CATCH_PLATFORM_H_INCLUDED + +#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) +#define CATCH_PLATFORM_MAC +#elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED) +#define CATCH_PLATFORM_IPHONE +#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) +#define CATCH_PLATFORM_WINDOWS +#endif + +#include + +namespace Catch{ + + bool isDebuggerActive(); + void writeToDebugConsole( std::string const& text ); +} + +#ifdef CATCH_PLATFORM_MAC + + // The following code snippet based on: + // http://cocoawithlove.com/2008/03/break-into-debugger.html + #ifdef DEBUG + #if defined(__ppc64__) || defined(__ppc__) + #define CATCH_BREAK_INTO_DEBUGGER() \ + if( Catch::isDebuggerActive() ) { \ + __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n" \ + : : : "memory","r0","r3","r4" ); \ + } + #else + #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) {__asm__("int $3\n" : : );} + #endif + #endif + +#elif defined(_MSC_VER) + #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { __debugbreak(); } +#elif defined(__MINGW32__) + extern "C" __declspec(dllimport) void __stdcall DebugBreak(); + #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { DebugBreak(); } +#endif + +#ifndef CATCH_BREAK_INTO_DEBUGGER +#define CATCH_BREAK_INTO_DEBUGGER() Catch::alwaysTrue(); +#endif + +// #included from: catch_interfaces_runner.h +#define TWOBLUECUBES_CATCH_INTERFACES_RUNNER_H_INCLUDED + +namespace Catch { + class TestCase; + + struct IRunner { + virtual ~IRunner(); + virtual bool aborting() const = 0; + }; +} + +/////////////////////////////////////////////////////////////////////////////// +// In the event of a failure works out if the debugger needs to be invoked +// and/or an exception thrown and takes appropriate action. +// This needs to be done as a macro so the debugger will stop in the user +// source code rather than in Catch library code +#define INTERNAL_CATCH_REACT( resultBuilder ) \ + if( resultBuilder.shouldDebugBreak() ) CATCH_BREAK_INTO_DEBUGGER(); \ + resultBuilder.react(); + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ) \ + do { \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ + try { \ + ( __catchResult <= expr ).endExpression(); \ + } \ + catch( ... ) { \ + __catchResult.useActiveException( Catch::ResultDisposition::Normal ); \ + } \ + INTERNAL_CATCH_REACT( __catchResult ) \ + } while( Catch::isTrue( false && static_cast(expr) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_IF( expr, resultDisposition, macroName ) \ + INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ); \ + if( Catch::getResultCapture().getLastResult()->succeeded() ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_ELSE( expr, resultDisposition, macroName ) \ + INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ); \ + if( !Catch::getResultCapture().getLastResult()->succeeded() ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_NO_THROW( expr, resultDisposition, macroName ) \ + do { \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ + try { \ + expr; \ + __catchResult.captureResult( Catch::ResultWas::Ok ); \ + } \ + catch( ... ) { \ + __catchResult.useActiveException( resultDisposition ); \ + } \ + INTERNAL_CATCH_REACT( __catchResult ) \ + } while( Catch::alwaysFalse() ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_THROWS( expr, resultDisposition, matcher, macroName ) \ + do { \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition, #matcher ); \ + if( __catchResult.allowThrows() ) \ + try { \ + expr; \ + __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \ + } \ + catch( ... ) { \ + __catchResult.captureExpectedException( matcher ); \ + } \ + else \ + __catchResult.captureResult( Catch::ResultWas::Ok ); \ + INTERNAL_CATCH_REACT( __catchResult ) \ + } while( Catch::alwaysFalse() ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_THROWS_AS( expr, exceptionType, resultDisposition, macroName ) \ + do { \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ + if( __catchResult.allowThrows() ) \ + try { \ + expr; \ + __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \ + } \ + catch( exceptionType ) { \ + __catchResult.captureResult( Catch::ResultWas::Ok ); \ + } \ + catch( ... ) { \ + __catchResult.useActiveException( resultDisposition ); \ + } \ + else \ + __catchResult.captureResult( Catch::ResultWas::Ok ); \ + INTERNAL_CATCH_REACT( __catchResult ) \ + } while( Catch::alwaysFalse() ) + +/////////////////////////////////////////////////////////////////////////////// +#ifdef CATCH_CONFIG_VARIADIC_MACROS + #define INTERNAL_CATCH_MSG( messageType, resultDisposition, macroName, ... ) \ + do { \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \ + __catchResult << __VA_ARGS__ + ::Catch::StreamEndStop(); \ + __catchResult.captureResult( messageType ); \ + INTERNAL_CATCH_REACT( __catchResult ) \ + } while( Catch::alwaysFalse() ) +#else + #define INTERNAL_CATCH_MSG( messageType, resultDisposition, macroName, log ) \ + do { \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \ + __catchResult << log + ::Catch::StreamEndStop(); \ + __catchResult.captureResult( messageType ); \ + INTERNAL_CATCH_REACT( __catchResult ) \ + } while( Catch::alwaysFalse() ) +#endif + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_INFO( log, macroName ) \ + Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage ) = Catch::MessageBuilder( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log; + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CHECK_THAT( arg, matcher, resultDisposition, macroName ) \ + do { \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #arg ", " #matcher, resultDisposition ); \ + try { \ + std::string matcherAsString = (matcher).toString(); \ + __catchResult \ + .setLhs( Catch::toString( arg ) ) \ + .setRhs( matcherAsString == Catch::Detail::unprintableString ? #matcher : matcherAsString ) \ + .setOp( "matches" ) \ + .setResultType( (matcher).match( arg ) ); \ + __catchResult.captureExpression(); \ + } catch( ... ) { \ + __catchResult.useActiveException( resultDisposition | Catch::ResultDisposition::ContinueOnFailure ); \ + } \ + INTERNAL_CATCH_REACT( __catchResult ) \ + } while( Catch::alwaysFalse() ) + +// #included from: internal/catch_section.h +#define TWOBLUECUBES_CATCH_SECTION_H_INCLUDED + +// #included from: catch_section_info.h +#define TWOBLUECUBES_CATCH_SECTION_INFO_H_INCLUDED + +// #included from: catch_totals.hpp +#define TWOBLUECUBES_CATCH_TOTALS_HPP_INCLUDED + +#include + +namespace Catch { + + struct Counts { + Counts() : passed( 0 ), failed( 0 ), failedButOk( 0 ) {} + + Counts operator - ( Counts const& other ) const { + Counts diff; + diff.passed = passed - other.passed; + diff.failed = failed - other.failed; + diff.failedButOk = failedButOk - other.failedButOk; + return diff; + } + Counts& operator += ( Counts const& other ) { + passed += other.passed; + failed += other.failed; + failedButOk += other.failedButOk; + return *this; + } + + std::size_t total() const { + return passed + failed + failedButOk; + } + bool allPassed() const { + return failed == 0 && failedButOk == 0; + } + bool allOk() const { + return failed == 0; + } + + std::size_t passed; + std::size_t failed; + std::size_t failedButOk; + }; + + struct Totals { + + Totals operator - ( Totals const& other ) const { + Totals diff; + diff.assertions = assertions - other.assertions; + diff.testCases = testCases - other.testCases; + return diff; + } + + Totals delta( Totals const& prevTotals ) const { + Totals diff = *this - prevTotals; + if( diff.assertions.failed > 0 ) + ++diff.testCases.failed; + else if( diff.assertions.failedButOk > 0 ) + ++diff.testCases.failedButOk; + else + ++diff.testCases.passed; + return diff; + } + + Totals& operator += ( Totals const& other ) { + assertions += other.assertions; + testCases += other.testCases; + return *this; + } + + Counts assertions; + Counts testCases; + }; +} + +namespace Catch { + + struct SectionInfo { + SectionInfo + ( SourceLineInfo const& _lineInfo, + std::string const& _name, + std::string const& _description = std::string() ); + + std::string name; + std::string description; + SourceLineInfo lineInfo; + }; + + struct SectionEndInfo { + SectionEndInfo( SectionInfo const& _sectionInfo, Counts const& _prevAssertions, double _durationInSeconds ) + : sectionInfo( _sectionInfo ), prevAssertions( _prevAssertions ), durationInSeconds( _durationInSeconds ) + {} + + SectionInfo sectionInfo; + Counts prevAssertions; + double durationInSeconds; + }; + +} // end namespace Catch + +// #included from: catch_timer.h +#define TWOBLUECUBES_CATCH_TIMER_H_INCLUDED + +#ifdef CATCH_PLATFORM_WINDOWS +typedef unsigned long long uint64_t; +#else +#include +#endif + +namespace Catch { + + class Timer { + public: + Timer() : m_ticks( 0 ) {} + void start(); + unsigned int getElapsedMicroseconds() const; + unsigned int getElapsedMilliseconds() const; + double getElapsedSeconds() const; + + private: + uint64_t m_ticks; + }; + +} // namespace Catch + +#include + +namespace Catch { + + class Section : NonCopyable { + public: + Section( SectionInfo const& info ); + ~Section(); + + // This indicates whether the section should be executed or not + operator bool() const; + + private: + SectionInfo m_info; + + std::string m_name; + Counts m_assertions; + bool m_sectionIncluded; + Timer m_timer; + }; + +} // end namespace Catch + +#ifdef CATCH_CONFIG_VARIADIC_MACROS + #define INTERNAL_CATCH_SECTION( ... ) \ + if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) +#else + #define INTERNAL_CATCH_SECTION( name, desc ) \ + if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, name, desc ) ) +#endif + +// #included from: internal/catch_generators.hpp +#define TWOBLUECUBES_CATCH_GENERATORS_HPP_INCLUDED + +#include +#include +#include +#include + +namespace Catch { + +template +struct IGenerator { + virtual ~IGenerator() {} + virtual T getValue( std::size_t index ) const = 0; + virtual std::size_t size () const = 0; +}; + +template +class BetweenGenerator : public IGenerator { +public: + BetweenGenerator( T from, T to ) : m_from( from ), m_to( to ){} + + virtual T getValue( std::size_t index ) const { + return m_from+static_cast( index ); + } + + virtual std::size_t size() const { + return static_cast( 1+m_to-m_from ); + } + +private: + + T m_from; + T m_to; +}; + +template +class ValuesGenerator : public IGenerator { +public: + ValuesGenerator(){} + + void add( T value ) { + m_values.push_back( value ); + } + + virtual T getValue( std::size_t index ) const { + return m_values[index]; + } + + virtual std::size_t size() const { + return m_values.size(); + } + +private: + std::vector m_values; +}; + +template +class CompositeGenerator { +public: + CompositeGenerator() : m_totalSize( 0 ) {} + + // *** Move semantics, similar to auto_ptr *** + CompositeGenerator( CompositeGenerator& other ) + : m_fileInfo( other.m_fileInfo ), + m_totalSize( 0 ) + { + move( other ); + } + + CompositeGenerator& setFileInfo( const char* fileInfo ) { + m_fileInfo = fileInfo; + return *this; + } + + ~CompositeGenerator() { + deleteAll( m_composed ); + } + + operator T () const { + size_t overallIndex = getCurrentContext().getGeneratorIndex( m_fileInfo, m_totalSize ); + + typename std::vector*>::const_iterator it = m_composed.begin(); + typename std::vector*>::const_iterator itEnd = m_composed.end(); + for( size_t index = 0; it != itEnd; ++it ) + { + const IGenerator* generator = *it; + if( overallIndex >= index && overallIndex < index + generator->size() ) + { + return generator->getValue( overallIndex-index ); + } + index += generator->size(); + } + CATCH_INTERNAL_ERROR( "Indexed past end of generated range" ); + return T(); // Suppress spurious "not all control paths return a value" warning in Visual Studio - if you know how to fix this please do so + } + + void add( const IGenerator* generator ) { + m_totalSize += generator->size(); + m_composed.push_back( generator ); + } + + CompositeGenerator& then( CompositeGenerator& other ) { + move( other ); + return *this; + } + + CompositeGenerator& then( T value ) { + ValuesGenerator* valuesGen = new ValuesGenerator(); + valuesGen->add( value ); + add( valuesGen ); + return *this; + } + +private: + + void move( CompositeGenerator& other ) { + std::copy( other.m_composed.begin(), other.m_composed.end(), std::back_inserter( m_composed ) ); + m_totalSize += other.m_totalSize; + other.m_composed.clear(); + } + + std::vector*> m_composed; + std::string m_fileInfo; + size_t m_totalSize; +}; + +namespace Generators +{ + template + CompositeGenerator between( T from, T to ) { + CompositeGenerator generators; + generators.add( new BetweenGenerator( from, to ) ); + return generators; + } + + template + CompositeGenerator values( T val1, T val2 ) { + CompositeGenerator generators; + ValuesGenerator* valuesGen = new ValuesGenerator(); + valuesGen->add( val1 ); + valuesGen->add( val2 ); + generators.add( valuesGen ); + return generators; + } + + template + CompositeGenerator values( T val1, T val2, T val3 ){ + CompositeGenerator generators; + ValuesGenerator* valuesGen = new ValuesGenerator(); + valuesGen->add( val1 ); + valuesGen->add( val2 ); + valuesGen->add( val3 ); + generators.add( valuesGen ); + return generators; + } + + template + CompositeGenerator values( T val1, T val2, T val3, T val4 ) { + CompositeGenerator generators; + ValuesGenerator* valuesGen = new ValuesGenerator(); + valuesGen->add( val1 ); + valuesGen->add( val2 ); + valuesGen->add( val3 ); + valuesGen->add( val4 ); + generators.add( valuesGen ); + return generators; + } + +} // end namespace Generators + +using namespace Generators; + +} // end namespace Catch + +#define INTERNAL_CATCH_LINESTR2( line ) #line +#define INTERNAL_CATCH_LINESTR( line ) INTERNAL_CATCH_LINESTR2( line ) + +#define INTERNAL_CATCH_GENERATE( expr ) expr.setFileInfo( __FILE__ "(" INTERNAL_CATCH_LINESTR( __LINE__ ) ")" ) + +// #included from: internal/catch_interfaces_exception.h +#define TWOBLUECUBES_CATCH_INTERFACES_EXCEPTION_H_INCLUDED + +#include +#include + +// #included from: catch_interfaces_registry_hub.h +#define TWOBLUECUBES_CATCH_INTERFACES_REGISTRY_HUB_H_INCLUDED + +#include + +namespace Catch { + + class TestCase; + struct ITestCaseRegistry; + struct IExceptionTranslatorRegistry; + struct IExceptionTranslator; + struct IReporterRegistry; + struct IReporterFactory; + + struct IRegistryHub { + virtual ~IRegistryHub(); + + virtual IReporterRegistry const& getReporterRegistry() const = 0; + virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0; + virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() = 0; + }; + + struct IMutableRegistryHub { + virtual ~IMutableRegistryHub(); + virtual void registerReporter( std::string const& name, Ptr const& factory ) = 0; + virtual void registerListener( Ptr const& factory ) = 0; + virtual void registerTest( TestCase const& testInfo ) = 0; + virtual void registerTranslator( const IExceptionTranslator* translator ) = 0; + }; + + IRegistryHub& getRegistryHub(); + IMutableRegistryHub& getMutableRegistryHub(); + void cleanUp(); + std::string translateActiveException(); + +} + +namespace Catch { + + typedef std::string(*exceptionTranslateFunction)(); + + struct IExceptionTranslator; + typedef std::vector ExceptionTranslators; + + struct IExceptionTranslator { + virtual ~IExceptionTranslator(); + virtual std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const = 0; + }; + + struct IExceptionTranslatorRegistry { + virtual ~IExceptionTranslatorRegistry(); + + virtual std::string translateActiveException() const = 0; + }; + + class ExceptionTranslatorRegistrar { + template + class ExceptionTranslator : public IExceptionTranslator { + public: + + ExceptionTranslator( std::string(*translateFunction)( T& ) ) + : m_translateFunction( translateFunction ) + {} + + virtual std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const CATCH_OVERRIDE { + try { + if( it == itEnd ) + throw; + else + return (*it)->translate( it+1, itEnd ); + } + catch( T& ex ) { + return m_translateFunction( ex ); + } + } + + protected: + std::string(*m_translateFunction)( T& ); + }; + + public: + template + ExceptionTranslatorRegistrar( std::string(*translateFunction)( T& ) ) { + getMutableRegistryHub().registerTranslator + ( new ExceptionTranslator( translateFunction ) ); + } + }; +} + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) \ + static std::string INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator )( signature ); \ + namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ) ); }\ + static std::string INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator )( signature ) + +// #included from: internal/catch_approx.hpp +#define TWOBLUECUBES_CATCH_APPROX_HPP_INCLUDED + +#include +#include + +namespace Catch { +namespace Detail { + + class Approx { + public: + explicit Approx ( double value ) + : m_epsilon( std::numeric_limits::epsilon()*100 ), + m_scale( 1.0 ), + m_value( value ) + {} + + Approx( Approx const& other ) + : m_epsilon( other.m_epsilon ), + m_scale( other.m_scale ), + m_value( other.m_value ) + {} + + static Approx custom() { + return Approx( 0 ); + } + + Approx operator()( double value ) { + Approx approx( value ); + approx.epsilon( m_epsilon ); + approx.scale( m_scale ); + return approx; + } + + friend bool operator == ( double lhs, Approx const& rhs ) { + // Thanks to Richard Harris for his help refining this formula + return fabs( lhs - rhs.m_value ) < rhs.m_epsilon * (rhs.m_scale + (std::max)( fabs(lhs), fabs(rhs.m_value) ) ); + } + + friend bool operator == ( Approx const& lhs, double rhs ) { + return operator==( rhs, lhs ); + } + + friend bool operator != ( double lhs, Approx const& rhs ) { + return !operator==( lhs, rhs ); + } + + friend bool operator != ( Approx const& lhs, double rhs ) { + return !operator==( rhs, lhs ); + } + + Approx& epsilon( double newEpsilon ) { + m_epsilon = newEpsilon; + return *this; + } + + Approx& scale( double newScale ) { + m_scale = newScale; + return *this; + } + + std::string toString() const { + std::ostringstream oss; + oss << "Approx( " << Catch::toString( m_value ) << " )"; + return oss.str(); + } + + private: + double m_epsilon; + double m_scale; + double m_value; + }; +} + +template<> +inline std::string toString( Detail::Approx const& value ) { + return value.toString(); +} + +} // end namespace Catch + +// #included from: internal/catch_interfaces_tag_alias_registry.h +#define TWOBLUECUBES_CATCH_INTERFACES_TAG_ALIAS_REGISTRY_H_INCLUDED + +// #included from: catch_tag_alias.h +#define TWOBLUECUBES_CATCH_TAG_ALIAS_H_INCLUDED + +#include + +namespace Catch { + + struct TagAlias { + TagAlias( std::string _tag, SourceLineInfo _lineInfo ) : tag( _tag ), lineInfo( _lineInfo ) {} + + std::string tag; + SourceLineInfo lineInfo; + }; + + struct RegistrarForTagAliases { + RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); + }; + +} // end namespace Catch + +#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } +// #included from: catch_option.hpp +#define TWOBLUECUBES_CATCH_OPTION_HPP_INCLUDED + +namespace Catch { + + // An optional type + template + class Option { + public: + Option() : nullableValue( CATCH_NULL ) {} + Option( T const& _value ) + : nullableValue( new( storage ) T( _value ) ) + {} + Option( Option const& _other ) + : nullableValue( _other ? new( storage ) T( *_other ) : CATCH_NULL ) + {} + + ~Option() { + reset(); + } + + Option& operator= ( Option const& _other ) { + if( &_other != this ) { + reset(); + if( _other ) + nullableValue = new( storage ) T( *_other ); + } + return *this; + } + Option& operator = ( T const& _value ) { + reset(); + nullableValue = new( storage ) T( _value ); + return *this; + } + + void reset() { + if( nullableValue ) + nullableValue->~T(); + nullableValue = CATCH_NULL; + } + + T& operator*() { return *nullableValue; } + T const& operator*() const { return *nullableValue; } + T* operator->() { return nullableValue; } + const T* operator->() const { return nullableValue; } + + T valueOr( T const& defaultValue ) const { + return nullableValue ? *nullableValue : defaultValue; + } + + bool some() const { return nullableValue != CATCH_NULL; } + bool none() const { return nullableValue == CATCH_NULL; } + + bool operator !() const { return nullableValue == CATCH_NULL; } + operator SafeBool::type() const { + return SafeBool::makeSafe( some() ); + } + + private: + T* nullableValue; + char storage[sizeof(T)]; + }; + +} // end namespace Catch + +namespace Catch { + + struct ITagAliasRegistry { + virtual ~ITagAliasRegistry(); + virtual Option find( std::string const& alias ) const = 0; + virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const = 0; + + static ITagAliasRegistry const& get(); + }; + +} // end namespace Catch + +// These files are included here so the single_include script doesn't put them +// in the conditionally compiled sections +// #included from: internal/catch_test_case_info.h +#define TWOBLUECUBES_CATCH_TEST_CASE_INFO_H_INCLUDED + +#include +#include + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +#endif + +namespace Catch { + + struct ITestCase; + + struct TestCaseInfo { + enum SpecialProperties{ + None = 0, + IsHidden = 1 << 1, + ShouldFail = 1 << 2, + MayFail = 1 << 3, + Throws = 1 << 4 + }; + + TestCaseInfo( std::string const& _name, + std::string const& _className, + std::string const& _description, + std::set const& _tags, + SourceLineInfo const& _lineInfo ); + + TestCaseInfo( TestCaseInfo const& other ); + + friend void setTags( TestCaseInfo& testCaseInfo, std::set const& tags ); + + bool isHidden() const; + bool throws() const; + bool okToFail() const; + bool expectedToFail() const; + + std::string name; + std::string className; + std::string description; + std::set tags; + std::set lcaseTags; + std::string tagsAsString; + SourceLineInfo lineInfo; + SpecialProperties properties; + }; + + class TestCase : public TestCaseInfo { + public: + + TestCase( ITestCase* testCase, TestCaseInfo const& info ); + TestCase( TestCase const& other ); + + TestCase withName( std::string const& _newName ) const; + + void invoke() const; + + TestCaseInfo const& getTestCaseInfo() const; + + void swap( TestCase& other ); + bool operator == ( TestCase const& other ) const; + bool operator < ( TestCase const& other ) const; + TestCase& operator = ( TestCase const& other ); + + private: + Ptr test; + }; + + TestCase makeTestCase( ITestCase* testCase, + std::string const& className, + std::string const& name, + std::string const& description, + SourceLineInfo const& lineInfo ); +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + + +#ifdef __OBJC__ +// #included from: internal/catch_objc.hpp +#define TWOBLUECUBES_CATCH_OBJC_HPP_INCLUDED + +#import + +#include + +// NB. Any general catch headers included here must be included +// in catch.hpp first to make sure they are included by the single +// header for non obj-usage + +/////////////////////////////////////////////////////////////////////////////// +// This protocol is really only here for (self) documenting purposes, since +// all its methods are optional. +@protocol OcFixture + +@optional + +-(void) setUp; +-(void) tearDown; + +@end + +namespace Catch { + + class OcMethod : public SharedImpl { + + public: + OcMethod( Class cls, SEL sel ) : m_cls( cls ), m_sel( sel ) {} + + virtual void invoke() const { + id obj = [[m_cls alloc] init]; + + performOptionalSelector( obj, @selector(setUp) ); + performOptionalSelector( obj, m_sel ); + performOptionalSelector( obj, @selector(tearDown) ); + + arcSafeRelease( obj ); + } + private: + virtual ~OcMethod() {} + + Class m_cls; + SEL m_sel; + }; + + namespace Detail{ + + inline std::string getAnnotation( Class cls, + std::string const& annotationName, + std::string const& testCaseName ) { + NSString* selStr = [[NSString alloc] initWithFormat:@"Catch_%s_%s", annotationName.c_str(), testCaseName.c_str()]; + SEL sel = NSSelectorFromString( selStr ); + arcSafeRelease( selStr ); + id value = performOptionalSelector( cls, sel ); + if( value ) + return [(NSString*)value UTF8String]; + return ""; + } + } + + inline size_t registerTestMethods() { + size_t noTestMethods = 0; + int noClasses = objc_getClassList( CATCH_NULL, 0 ); + + Class* classes = (CATCH_UNSAFE_UNRETAINED Class *)malloc( sizeof(Class) * noClasses); + objc_getClassList( classes, noClasses ); + + for( int c = 0; c < noClasses; c++ ) { + Class cls = classes[c]; + { + u_int count; + Method* methods = class_copyMethodList( cls, &count ); + for( u_int m = 0; m < count ; m++ ) { + SEL selector = method_getName(methods[m]); + std::string methodName = sel_getName(selector); + if( startsWith( methodName, "Catch_TestCase_" ) ) { + std::string testCaseName = methodName.substr( 15 ); + std::string name = Detail::getAnnotation( cls, "Name", testCaseName ); + std::string desc = Detail::getAnnotation( cls, "Description", testCaseName ); + const char* className = class_getName( cls ); + + getMutableRegistryHub().registerTest( makeTestCase( new OcMethod( cls, selector ), className, name.c_str(), desc.c_str(), SourceLineInfo() ) ); + noTestMethods++; + } + } + free(methods); + } + } + return noTestMethods; + } + + namespace Matchers { + namespace Impl { + namespace NSStringMatchers { + + template + struct StringHolder : MatcherImpl{ + StringHolder( NSString* substr ) : m_substr( [substr copy] ){} + StringHolder( StringHolder const& other ) : m_substr( [other.m_substr copy] ){} + StringHolder() { + arcSafeRelease( m_substr ); + } + + NSString* m_substr; + }; + + struct Equals : StringHolder { + Equals( NSString* substr ) : StringHolder( substr ){} + + virtual bool match( ExpressionType const& str ) const { + return (str != nil || m_substr == nil ) && + [str isEqualToString:m_substr]; + } + + virtual std::string toString() const { + return "equals string: " + Catch::toString( m_substr ); + } + }; + + struct Contains : StringHolder { + Contains( NSString* substr ) : StringHolder( substr ){} + + virtual bool match( ExpressionType const& str ) const { + return (str != nil || m_substr == nil ) && + [str rangeOfString:m_substr].location != NSNotFound; + } + + virtual std::string toString() const { + return "contains string: " + Catch::toString( m_substr ); + } + }; + + struct StartsWith : StringHolder { + StartsWith( NSString* substr ) : StringHolder( substr ){} + + virtual bool match( ExpressionType const& str ) const { + return (str != nil || m_substr == nil ) && + [str rangeOfString:m_substr].location == 0; + } + + virtual std::string toString() const { + return "starts with: " + Catch::toString( m_substr ); + } + }; + struct EndsWith : StringHolder { + EndsWith( NSString* substr ) : StringHolder( substr ){} + + virtual bool match( ExpressionType const& str ) const { + return (str != nil || m_substr == nil ) && + [str rangeOfString:m_substr].location == [str length] - [m_substr length]; + } + + virtual std::string toString() const { + return "ends with: " + Catch::toString( m_substr ); + } + }; + + } // namespace NSStringMatchers + } // namespace Impl + + inline Impl::NSStringMatchers::Equals + Equals( NSString* substr ){ return Impl::NSStringMatchers::Equals( substr ); } + + inline Impl::NSStringMatchers::Contains + Contains( NSString* substr ){ return Impl::NSStringMatchers::Contains( substr ); } + + inline Impl::NSStringMatchers::StartsWith + StartsWith( NSString* substr ){ return Impl::NSStringMatchers::StartsWith( substr ); } + + inline Impl::NSStringMatchers::EndsWith + EndsWith( NSString* substr ){ return Impl::NSStringMatchers::EndsWith( substr ); } + + } // namespace Matchers + + using namespace Matchers; + +} // namespace Catch + +/////////////////////////////////////////////////////////////////////////////// +#define OC_TEST_CASE( name, desc )\ ++(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Name_test ) \ +{\ +return @ name; \ +}\ ++(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Description_test ) \ +{ \ +return @ desc; \ +} \ +-(void) INTERNAL_CATCH_UNIQUE_NAME( Catch_TestCase_test ) + +#endif + +#ifdef CATCH_IMPL +// #included from: internal/catch_impl.hpp +#define TWOBLUECUBES_CATCH_IMPL_HPP_INCLUDED + +// Collect all the implementation files together here +// These are the equivalent of what would usually be cpp files + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wweak-vtables" +#endif + +// #included from: ../catch_session.hpp +#define TWOBLUECUBES_CATCH_RUNNER_HPP_INCLUDED + +// #included from: internal/catch_commandline.hpp +#define TWOBLUECUBES_CATCH_COMMANDLINE_HPP_INCLUDED + +// #included from: catch_config.hpp +#define TWOBLUECUBES_CATCH_CONFIG_HPP_INCLUDED + +// #included from: catch_test_spec_parser.hpp +#define TWOBLUECUBES_CATCH_TEST_SPEC_PARSER_HPP_INCLUDED + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +#endif + +// #included from: catch_test_spec.hpp +#define TWOBLUECUBES_CATCH_TEST_SPEC_HPP_INCLUDED + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +#endif + +// #included from: catch_wildcard_pattern.hpp +#define TWOBLUECUBES_CATCH_WILDCARD_PATTERN_HPP_INCLUDED + +namespace Catch +{ + class WildcardPattern { + enum WildcardPosition { + NoWildcard = 0, + WildcardAtStart = 1, + WildcardAtEnd = 2, + WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd + }; + + public: + + WildcardPattern( std::string const& pattern, CaseSensitive::Choice caseSensitivity ) + : m_caseSensitivity( caseSensitivity ), + m_wildcard( NoWildcard ), + m_pattern( adjustCase( pattern ) ) + { + if( startsWith( m_pattern, "*" ) ) { + m_pattern = m_pattern.substr( 1 ); + m_wildcard = WildcardAtStart; + } + if( endsWith( m_pattern, "*" ) ) { + m_pattern = m_pattern.substr( 0, m_pattern.size()-1 ); + m_wildcard = static_cast( m_wildcard | WildcardAtEnd ); + } + } + virtual ~WildcardPattern(); + virtual bool matches( std::string const& str ) const { + switch( m_wildcard ) { + case NoWildcard: + return m_pattern == adjustCase( str ); + case WildcardAtStart: + return endsWith( adjustCase( str ), m_pattern ); + case WildcardAtEnd: + return startsWith( adjustCase( str ), m_pattern ); + case WildcardAtBothEnds: + return contains( adjustCase( str ), m_pattern ); + } + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunreachable-code" +#endif + throw std::logic_error( "Unknown enum" ); +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + } + private: + std::string adjustCase( std::string const& str ) const { + return m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str; + } + CaseSensitive::Choice m_caseSensitivity; + WildcardPosition m_wildcard; + std::string m_pattern; + }; +} + +#include +#include + +namespace Catch { + + class TestSpec { + struct Pattern : SharedImpl<> { + virtual ~Pattern(); + virtual bool matches( TestCaseInfo const& testCase ) const = 0; + }; + class NamePattern : public Pattern { + public: + NamePattern( std::string const& name ) + : m_wildcardPattern( toLower( name ), CaseSensitive::No ) + {} + virtual ~NamePattern(); + virtual bool matches( TestCaseInfo const& testCase ) const { + return m_wildcardPattern.matches( toLower( testCase.name ) ); + } + private: + WildcardPattern m_wildcardPattern; + }; + + class TagPattern : public Pattern { + public: + TagPattern( std::string const& tag ) : m_tag( toLower( tag ) ) {} + virtual ~TagPattern(); + virtual bool matches( TestCaseInfo const& testCase ) const { + return testCase.lcaseTags.find( m_tag ) != testCase.lcaseTags.end(); + } + private: + std::string m_tag; + }; + + class ExcludedPattern : public Pattern { + public: + ExcludedPattern( Ptr const& underlyingPattern ) : m_underlyingPattern( underlyingPattern ) {} + virtual ~ExcludedPattern(); + virtual bool matches( TestCaseInfo const& testCase ) const { return !m_underlyingPattern->matches( testCase ); } + private: + Ptr m_underlyingPattern; + }; + + struct Filter { + std::vector > m_patterns; + + bool matches( TestCaseInfo const& testCase ) const { + // All patterns in a filter must match for the filter to be a match + for( std::vector >::const_iterator it = m_patterns.begin(), itEnd = m_patterns.end(); it != itEnd; ++it ) + if( !(*it)->matches( testCase ) ) + return false; + return true; + } + }; + + public: + bool hasFilters() const { + return !m_filters.empty(); + } + bool matches( TestCaseInfo const& testCase ) const { + // A TestSpec matches if any filter matches + for( std::vector::const_iterator it = m_filters.begin(), itEnd = m_filters.end(); it != itEnd; ++it ) + if( it->matches( testCase ) ) + return true; + return false; + } + + private: + std::vector m_filters; + + friend class TestSpecParser; + }; +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +namespace Catch { + + class TestSpecParser { + enum Mode{ None, Name, QuotedName, Tag }; + Mode m_mode; + bool m_exclusion; + std::size_t m_start, m_pos; + std::string m_arg; + TestSpec::Filter m_currentFilter; + TestSpec m_testSpec; + ITagAliasRegistry const* m_tagAliases; + + public: + TestSpecParser( ITagAliasRegistry const& tagAliases ) : m_tagAliases( &tagAliases ) {} + + TestSpecParser& parse( std::string const& arg ) { + m_mode = None; + m_exclusion = false; + m_start = std::string::npos; + m_arg = m_tagAliases->expandAliases( arg ); + for( m_pos = 0; m_pos < m_arg.size(); ++m_pos ) + visitChar( m_arg[m_pos] ); + if( m_mode == Name ) + addPattern(); + return *this; + } + TestSpec testSpec() { + addFilter(); + return m_testSpec; + } + private: + void visitChar( char c ) { + if( m_mode == None ) { + switch( c ) { + case ' ': return; + case '~': m_exclusion = true; return; + case '[': return startNewMode( Tag, ++m_pos ); + case '"': return startNewMode( QuotedName, ++m_pos ); + default: startNewMode( Name, m_pos ); break; + } + } + if( m_mode == Name ) { + if( c == ',' ) { + addPattern(); + addFilter(); + } + else if( c == '[' ) { + if( subString() == "exclude:" ) + m_exclusion = true; + else + addPattern(); + startNewMode( Tag, ++m_pos ); + } + } + else if( m_mode == QuotedName && c == '"' ) + addPattern(); + else if( m_mode == Tag && c == ']' ) + addPattern(); + } + void startNewMode( Mode mode, std::size_t start ) { + m_mode = mode; + m_start = start; + } + std::string subString() const { return m_arg.substr( m_start, m_pos - m_start ); } + template + void addPattern() { + std::string token = subString(); + if( startsWith( token, "exclude:" ) ) { + m_exclusion = true; + token = token.substr( 8 ); + } + if( !token.empty() ) { + Ptr pattern = new T( token ); + if( m_exclusion ) + pattern = new TestSpec::ExcludedPattern( pattern ); + m_currentFilter.m_patterns.push_back( pattern ); + } + m_exclusion = false; + m_mode = None; + } + void addFilter() { + if( !m_currentFilter.m_patterns.empty() ) { + m_testSpec.m_filters.push_back( m_currentFilter ); + m_currentFilter = TestSpec::Filter(); + } + } + }; + inline TestSpec parseTestSpec( std::string const& arg ) { + return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec(); + } + +} // namespace Catch + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +// #included from: catch_interfaces_config.h +#define TWOBLUECUBES_CATCH_INTERFACES_CONFIG_H_INCLUDED + +#include +#include +#include + +namespace Catch { + + struct Verbosity { enum Level { + NoOutput = 0, + Quiet, + Normal + }; }; + + struct WarnAbout { enum What { + Nothing = 0x00, + NoAssertions = 0x01 + }; }; + + struct ShowDurations { enum OrNot { + DefaultForReporter, + Always, + Never + }; }; + struct RunTests { enum InWhatOrder { + InDeclarationOrder, + InLexicographicalOrder, + InRandomOrder + }; }; + + class TestSpec; + + struct IConfig : IShared { + + virtual ~IConfig(); + + virtual bool allowThrows() const = 0; + virtual std::ostream& stream() const = 0; + virtual std::string name() const = 0; + virtual bool includeSuccessfulResults() const = 0; + virtual bool shouldDebugBreak() const = 0; + virtual bool warnAboutMissingAssertions() const = 0; + virtual int abortAfter() const = 0; + virtual bool showInvisibles() const = 0; + virtual ShowDurations::OrNot showDurations() const = 0; + virtual TestSpec const& testSpec() const = 0; + virtual RunTests::InWhatOrder runOrder() const = 0; + virtual unsigned int rngSeed() const = 0; + virtual bool forceColour() const = 0; + }; +} + +// #included from: catch_stream.h +#define TWOBLUECUBES_CATCH_STREAM_H_INCLUDED + +// #included from: catch_streambuf.h +#define TWOBLUECUBES_CATCH_STREAMBUF_H_INCLUDED + +#include + +namespace Catch { + + class StreamBufBase : public std::streambuf { + public: + virtual ~StreamBufBase() CATCH_NOEXCEPT; + }; +} + +#include +#include +#include + +namespace Catch { + + std::ostream& cout(); + std::ostream& cerr(); + + struct IStream { + virtual ~IStream() CATCH_NOEXCEPT; + virtual std::ostream& stream() const = 0; + }; + + class FileStream : public IStream { + mutable std::ofstream m_ofs; + public: + FileStream( std::string const& filename ); + virtual ~FileStream() CATCH_NOEXCEPT; + public: // IStream + virtual std::ostream& stream() const CATCH_OVERRIDE; + }; + + class CoutStream : public IStream { + mutable std::ostream m_os; + public: + CoutStream(); + virtual ~CoutStream() CATCH_NOEXCEPT; + + public: // IStream + virtual std::ostream& stream() const CATCH_OVERRIDE; + }; + + class DebugOutStream : public IStream { + std::auto_ptr m_streamBuf; + mutable std::ostream m_os; + public: + DebugOutStream(); + virtual ~DebugOutStream() CATCH_NOEXCEPT; + + public: // IStream + virtual std::ostream& stream() const CATCH_OVERRIDE; + }; +} + +#include +#include +#include +#include +#include + +#ifndef CATCH_CONFIG_CONSOLE_WIDTH +#define CATCH_CONFIG_CONSOLE_WIDTH 80 +#endif + +namespace Catch { + + struct ConfigData { + + ConfigData() + : listTests( false ), + listTags( false ), + listReporters( false ), + listTestNamesOnly( false ), + showSuccessfulTests( false ), + shouldDebugBreak( false ), + noThrow( false ), + showHelp( false ), + showInvisibles( false ), + forceColour( false ), + filenamesAsTags( false ), + abortAfter( -1 ), + rngSeed( 0 ), + verbosity( Verbosity::Normal ), + warnings( WarnAbout::Nothing ), + showDurations( ShowDurations::DefaultForReporter ), + runOrder( RunTests::InDeclarationOrder ) + {} + + bool listTests; + bool listTags; + bool listReporters; + bool listTestNamesOnly; + + bool showSuccessfulTests; + bool shouldDebugBreak; + bool noThrow; + bool showHelp; + bool showInvisibles; + bool forceColour; + bool filenamesAsTags; + + int abortAfter; + unsigned int rngSeed; + + Verbosity::Level verbosity; + WarnAbout::What warnings; + ShowDurations::OrNot showDurations; + RunTests::InWhatOrder runOrder; + + std::string outputFilename; + std::string name; + std::string processName; + + std::vector reporterNames; + std::vector testsOrTags; + }; + + class Config : public SharedImpl { + private: + Config( Config const& other ); + Config& operator = ( Config const& other ); + virtual void dummy(); + public: + + Config() + {} + + Config( ConfigData const& data ) + : m_data( data ), + m_stream( openStream() ) + { + if( !data.testsOrTags.empty() ) { + TestSpecParser parser( ITagAliasRegistry::get() ); + for( std::size_t i = 0; i < data.testsOrTags.size(); ++i ) + parser.parse( data.testsOrTags[i] ); + m_testSpec = parser.testSpec(); + } + } + + virtual ~Config() { + } + + std::string const& getFilename() const { + return m_data.outputFilename ; + } + + bool listTests() const { return m_data.listTests; } + bool listTestNamesOnly() const { return m_data.listTestNamesOnly; } + bool listTags() const { return m_data.listTags; } + bool listReporters() const { return m_data.listReporters; } + + std::string getProcessName() const { return m_data.processName; } + + bool shouldDebugBreak() const { return m_data.shouldDebugBreak; } + + std::vector getReporterNames() const { return m_data.reporterNames; } + + int abortAfter() const { return m_data.abortAfter; } + + TestSpec const& testSpec() const { return m_testSpec; } + + bool showHelp() const { return m_data.showHelp; } + bool showInvisibles() const { return m_data.showInvisibles; } + + // IConfig interface + virtual bool allowThrows() const { return !m_data.noThrow; } + virtual std::ostream& stream() const { return m_stream->stream(); } + virtual std::string name() const { return m_data.name.empty() ? m_data.processName : m_data.name; } + virtual bool includeSuccessfulResults() const { return m_data.showSuccessfulTests; } + virtual bool warnAboutMissingAssertions() const { return m_data.warnings & WarnAbout::NoAssertions; } + virtual ShowDurations::OrNot showDurations() const { return m_data.showDurations; } + virtual RunTests::InWhatOrder runOrder() const { return m_data.runOrder; } + virtual unsigned int rngSeed() const { return m_data.rngSeed; } + virtual bool forceColour() const { return m_data.forceColour; } + + private: + + IStream const* openStream() { + if( m_data.outputFilename.empty() ) + return new CoutStream(); + else if( m_data.outputFilename[0] == '%' ) { + if( m_data.outputFilename == "%debug" ) + return new DebugOutStream(); + else + throw std::domain_error( "Unrecognised stream: " + m_data.outputFilename ); + } + else + return new FileStream( m_data.outputFilename ); + } + ConfigData m_data; + + std::auto_ptr m_stream; + TestSpec m_testSpec; + }; + +} // end namespace Catch + +// #included from: catch_clara.h +#define TWOBLUECUBES_CATCH_CLARA_H_INCLUDED + +// Use Catch's value for console width (store Clara's off to the side, if present) +#ifdef CLARA_CONFIG_CONSOLE_WIDTH +#define CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH CLARA_CONFIG_CONSOLE_WIDTH +#undef CLARA_CONFIG_CONSOLE_WIDTH +#endif +#define CLARA_CONFIG_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH + +// Declare Clara inside the Catch namespace +#define STITCH_CLARA_OPEN_NAMESPACE namespace Catch { +// #included from: ../external/clara.h + +// Version 0.0.1.1 + +// Only use header guard if we are not using an outer namespace +#if !defined(TWOBLUECUBES_CLARA_H_INCLUDED) || defined(STITCH_CLARA_OPEN_NAMESPACE) + +#ifndef STITCH_CLARA_OPEN_NAMESPACE +#define TWOBLUECUBES_CLARA_H_INCLUDED +#define STITCH_CLARA_OPEN_NAMESPACE +#define STITCH_CLARA_CLOSE_NAMESPACE +#else +#define STITCH_CLARA_CLOSE_NAMESPACE } +#endif + +#define STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE STITCH_CLARA_OPEN_NAMESPACE + +// ----------- #included from tbc_text_format.h ----------- + +// Only use header guard if we are not using an outer namespace +#if !defined(TBC_TEXT_FORMAT_H_INCLUDED) || defined(STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE) +#ifndef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE +#define TBC_TEXT_FORMAT_H_INCLUDED +#endif + +#include +#include +#include +#include + +// Use optional outer namespace +#ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE +namespace STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE { +#endif + +namespace Tbc { + +#ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH + const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH; +#else + const unsigned int consoleWidth = 80; +#endif + + struct TextAttributes { + TextAttributes() + : initialIndent( std::string::npos ), + indent( 0 ), + width( consoleWidth-1 ), + tabChar( '\t' ) + {} + + TextAttributes& setInitialIndent( std::size_t _value ) { initialIndent = _value; return *this; } + TextAttributes& setIndent( std::size_t _value ) { indent = _value; return *this; } + TextAttributes& setWidth( std::size_t _value ) { width = _value; return *this; } + TextAttributes& setTabChar( char _value ) { tabChar = _value; return *this; } + + std::size_t initialIndent; // indent of first line, or npos + std::size_t indent; // indent of subsequent lines, or all if initialIndent is npos + std::size_t width; // maximum width of text, including indent. Longer text will wrap + char tabChar; // If this char is seen the indent is changed to current pos + }; + + class Text { + public: + Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() ) + : attr( _attr ) + { + std::string wrappableChars = " [({.,/|\\-"; + std::size_t indent = _attr.initialIndent != std::string::npos + ? _attr.initialIndent + : _attr.indent; + std::string remainder = _str; + + while( !remainder.empty() ) { + if( lines.size() >= 1000 ) { + lines.push_back( "... message truncated due to excessive size" ); + return; + } + std::size_t tabPos = std::string::npos; + std::size_t width = (std::min)( remainder.size(), _attr.width - indent ); + std::size_t pos = remainder.find_first_of( '\n' ); + if( pos <= width ) { + width = pos; + } + pos = remainder.find_last_of( _attr.tabChar, width ); + if( pos != std::string::npos ) { + tabPos = pos; + if( remainder[width] == '\n' ) + width--; + remainder = remainder.substr( 0, tabPos ) + remainder.substr( tabPos+1 ); + } + + if( width == remainder.size() ) { + spliceLine( indent, remainder, width ); + } + else if( remainder[width] == '\n' ) { + spliceLine( indent, remainder, width ); + if( width <= 1 || remainder.size() != 1 ) + remainder = remainder.substr( 1 ); + indent = _attr.indent; + } + else { + pos = remainder.find_last_of( wrappableChars, width ); + if( pos != std::string::npos && pos > 0 ) { + spliceLine( indent, remainder, pos ); + if( remainder[0] == ' ' ) + remainder = remainder.substr( 1 ); + } + else { + spliceLine( indent, remainder, width-1 ); + lines.back() += "-"; + } + if( lines.size() == 1 ) + indent = _attr.indent; + if( tabPos != std::string::npos ) + indent += tabPos; + } + } + } + + void spliceLine( std::size_t _indent, std::string& _remainder, std::size_t _pos ) { + lines.push_back( std::string( _indent, ' ' ) + _remainder.substr( 0, _pos ) ); + _remainder = _remainder.substr( _pos ); + } + + typedef std::vector::const_iterator const_iterator; + + const_iterator begin() const { return lines.begin(); } + const_iterator end() const { return lines.end(); } + std::string const& last() const { return lines.back(); } + std::size_t size() const { return lines.size(); } + std::string const& operator[]( std::size_t _index ) const { return lines[_index]; } + std::string toString() const { + std::ostringstream oss; + oss << *this; + return oss.str(); + } + + inline friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) { + for( Text::const_iterator it = _text.begin(), itEnd = _text.end(); + it != itEnd; ++it ) { + if( it != _text.begin() ) + _stream << "\n"; + _stream << *it; + } + return _stream; + } + + private: + std::string str; + TextAttributes attr; + std::vector lines; + }; + +} // end namespace Tbc + +#ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE +} // end outer namespace +#endif + +#endif // TBC_TEXT_FORMAT_H_INCLUDED + +// ----------- end of #include from tbc_text_format.h ----------- +// ........... back in clara.h + +#undef STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE + +// ----------- #included from clara_compilers.h ----------- + +#ifndef TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED +#define TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED + +// Detect a number of compiler features - mostly C++11/14 conformance - by compiler +// The following features are defined: +// +// CLARA_CONFIG_CPP11_NULLPTR : is nullptr supported? +// CLARA_CONFIG_CPP11_NOEXCEPT : is noexcept supported? +// CLARA_CONFIG_CPP11_GENERATED_METHODS : The delete and default keywords for compiler generated methods +// CLARA_CONFIG_CPP11_OVERRIDE : is override supported? +// CLARA_CONFIG_CPP11_UNIQUE_PTR : is unique_ptr supported (otherwise use auto_ptr) + +// CLARA_CONFIG_CPP11_OR_GREATER : Is C++11 supported? + +// CLARA_CONFIG_VARIADIC_MACROS : are variadic macros supported? + +// In general each macro has a _NO_ form +// (e.g. CLARA_CONFIG_CPP11_NO_NULLPTR) which disables the feature. +// Many features, at point of detection, define an _INTERNAL_ macro, so they +// can be combined, en-mass, with the _NO_ forms later. + +// All the C++11 features can be disabled with CLARA_CONFIG_NO_CPP11 + +#ifdef __clang__ + +#if __has_feature(cxx_nullptr) +#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR +#endif + +#if __has_feature(cxx_noexcept) +#define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT +#endif + +#endif // __clang__ + +//////////////////////////////////////////////////////////////////////////////// +// GCC +#ifdef __GNUC__ + +#if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__) +#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR +#endif + +// - otherwise more recent versions define __cplusplus >= 201103L +// and will get picked up below + +#endif // __GNUC__ + +//////////////////////////////////////////////////////////////////////////////// +// Visual C++ +#ifdef _MSC_VER + +#if (_MSC_VER >= 1600) +#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR +#define CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR +#endif + +#if (_MSC_VER >= 1900 ) // (VC++ 13 (VS2015)) +#define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT +#define CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS +#endif + +#endif // _MSC_VER + +//////////////////////////////////////////////////////////////////////////////// +// C++ language feature support + +// catch all support for C++11 +#if defined(__cplusplus) && __cplusplus >= 201103L + +#define CLARA_CPP11_OR_GREATER + +#if !defined(CLARA_INTERNAL_CONFIG_CPP11_NULLPTR) +#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR +#endif + +#ifndef CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT +#define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT +#endif + +#ifndef CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS +#define CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS +#endif + +#if !defined(CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE) +#define CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE +#endif +#if !defined(CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) +#define CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR +#endif + +#endif // __cplusplus >= 201103L + +// Now set the actual defines based on the above + anything the user has configured +#if defined(CLARA_INTERNAL_CONFIG_CPP11_NULLPTR) && !defined(CLARA_CONFIG_CPP11_NO_NULLPTR) && !defined(CLARA_CONFIG_CPP11_NULLPTR) && !defined(CLARA_CONFIG_NO_CPP11) +#define CLARA_CONFIG_CPP11_NULLPTR +#endif +#if defined(CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_CONFIG_CPP11_NO_NOEXCEPT) && !defined(CLARA_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_CONFIG_NO_CPP11) +#define CLARA_CONFIG_CPP11_NOEXCEPT +#endif +#if defined(CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS) && !defined(CLARA_CONFIG_CPP11_NO_GENERATED_METHODS) && !defined(CLARA_CONFIG_CPP11_GENERATED_METHODS) && !defined(CLARA_CONFIG_NO_CPP11) +#define CLARA_CONFIG_CPP11_GENERATED_METHODS +#endif +#if defined(CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE) && !defined(CLARA_CONFIG_NO_OVERRIDE) && !defined(CLARA_CONFIG_CPP11_OVERRIDE) && !defined(CLARA_CONFIG_NO_CPP11) +#define CLARA_CONFIG_CPP11_OVERRIDE +#endif +#if defined(CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CLARA_CONFIG_NO_UNIQUE_PTR) && !defined(CLARA_CONFIG_CPP11_UNIQUE_PTR) && !defined(CLARA_CONFIG_NO_CPP11) +#define CLARA_CONFIG_CPP11_UNIQUE_PTR +#endif + +// noexcept support: +#if defined(CLARA_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_NOEXCEPT) +#define CLARA_NOEXCEPT noexcept +# define CLARA_NOEXCEPT_IS(x) noexcept(x) +#else +#define CLARA_NOEXCEPT throw() +# define CLARA_NOEXCEPT_IS(x) +#endif + +// nullptr support +#ifdef CLARA_CONFIG_CPP11_NULLPTR +#define CLARA_NULL nullptr +#else +#define CLARA_NULL NULL +#endif + +// override support +#ifdef CLARA_CONFIG_CPP11_OVERRIDE +#define CLARA_OVERRIDE override +#else +#define CLARA_OVERRIDE +#endif + +// unique_ptr support +#ifdef CLARA_CONFIG_CPP11_UNIQUE_PTR +# define CLARA_AUTO_PTR( T ) std::unique_ptr +#else +# define CLARA_AUTO_PTR( T ) std::auto_ptr +#endif + +#endif // TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED + +// ----------- end of #include from clara_compilers.h ----------- +// ........... back in clara.h + +#include +#include +#include + +// Use optional outer namespace +#ifdef STITCH_CLARA_OPEN_NAMESPACE +STITCH_CLARA_OPEN_NAMESPACE +#endif + +namespace Clara { + + struct UnpositionalTag {}; + + extern UnpositionalTag _; + +#ifdef CLARA_CONFIG_MAIN + UnpositionalTag _; +#endif + + namespace Detail { + +#ifdef CLARA_CONSOLE_WIDTH + const unsigned int consoleWidth = CLARA_CONFIG_CONSOLE_WIDTH; +#else + const unsigned int consoleWidth = 80; +#endif + + // Use this to try and stop compiler from warning about unreachable code + inline bool isTrue( bool value ) { return value; } + + using namespace Tbc; + + inline bool startsWith( std::string const& str, std::string const& prefix ) { + return str.size() >= prefix.size() && str.substr( 0, prefix.size() ) == prefix; + } + + template struct RemoveConstRef{ typedef T type; }; + template struct RemoveConstRef{ typedef T type; }; + template struct RemoveConstRef{ typedef T type; }; + template struct RemoveConstRef{ typedef T type; }; + + template struct IsBool { static const bool value = false; }; + template<> struct IsBool { static const bool value = true; }; + + template + void convertInto( std::string const& _source, T& _dest ) { + std::stringstream ss; + ss << _source; + ss >> _dest; + if( ss.fail() ) + throw std::runtime_error( "Unable to convert " + _source + " to destination type" ); + } + inline void convertInto( std::string const& _source, std::string& _dest ) { + _dest = _source; + } + inline void convertInto( std::string const& _source, bool& _dest ) { + std::string sourceLC = _source; + std::transform( sourceLC.begin(), sourceLC.end(), sourceLC.begin(), ::tolower ); + if( sourceLC == "y" || sourceLC == "1" || sourceLC == "true" || sourceLC == "yes" || sourceLC == "on" ) + _dest = true; + else if( sourceLC == "n" || sourceLC == "0" || sourceLC == "false" || sourceLC == "no" || sourceLC == "off" ) + _dest = false; + else + throw std::runtime_error( "Expected a boolean value but did not recognise:\n '" + _source + "'" ); + } + inline void convertInto( bool _source, bool& _dest ) { + _dest = _source; + } + template + inline void convertInto( bool, T& ) { + if( isTrue( true ) ) + throw std::runtime_error( "Invalid conversion" ); + } + + template + struct IArgFunction { + virtual ~IArgFunction() {} +#ifdef CLARA_CONFIG_CPP11_GENERATED_METHODS + IArgFunction() = default; + IArgFunction( IArgFunction const& ) = default; +#endif + virtual void set( ConfigT& config, std::string const& value ) const = 0; + virtual void setFlag( ConfigT& config ) const = 0; + virtual bool takesArg() const = 0; + virtual IArgFunction* clone() const = 0; + }; + + template + class BoundArgFunction { + public: + BoundArgFunction() : functionObj( CLARA_NULL ) {} + BoundArgFunction( IArgFunction* _functionObj ) : functionObj( _functionObj ) {} + BoundArgFunction( BoundArgFunction const& other ) : functionObj( other.functionObj ? other.functionObj->clone() : CLARA_NULL ) {} + BoundArgFunction& operator = ( BoundArgFunction const& other ) { + IArgFunction* newFunctionObj = other.functionObj ? other.functionObj->clone() : CLARA_NULL; + delete functionObj; + functionObj = newFunctionObj; + return *this; + } + ~BoundArgFunction() { delete functionObj; } + + void set( ConfigT& config, std::string const& value ) const { + functionObj->set( config, value ); + } + void setFlag( ConfigT& config ) const { + functionObj->setFlag( config ); + } + bool takesArg() const { return functionObj->takesArg(); } + + bool isSet() const { + return functionObj != CLARA_NULL; + } + private: + IArgFunction* functionObj; + }; + + template + struct NullBinder : IArgFunction{ + virtual void set( C&, std::string const& ) const {} + virtual void setFlag( C& ) const {} + virtual bool takesArg() const { return true; } + virtual IArgFunction* clone() const { return new NullBinder( *this ); } + }; + + template + struct BoundDataMember : IArgFunction{ + BoundDataMember( M C::* _member ) : member( _member ) {} + virtual void set( C& p, std::string const& stringValue ) const { + convertInto( stringValue, p.*member ); + } + virtual void setFlag( C& p ) const { + convertInto( true, p.*member ); + } + virtual bool takesArg() const { return !IsBool::value; } + virtual IArgFunction* clone() const { return new BoundDataMember( *this ); } + M C::* member; + }; + template + struct BoundUnaryMethod : IArgFunction{ + BoundUnaryMethod( void (C::*_member)( M ) ) : member( _member ) {} + virtual void set( C& p, std::string const& stringValue ) const { + typename RemoveConstRef::type value; + convertInto( stringValue, value ); + (p.*member)( value ); + } + virtual void setFlag( C& p ) const { + typename RemoveConstRef::type value; + convertInto( true, value ); + (p.*member)( value ); + } + virtual bool takesArg() const { return !IsBool::value; } + virtual IArgFunction* clone() const { return new BoundUnaryMethod( *this ); } + void (C::*member)( M ); + }; + template + struct BoundNullaryMethod : IArgFunction{ + BoundNullaryMethod( void (C::*_member)() ) : member( _member ) {} + virtual void set( C& p, std::string const& stringValue ) const { + bool value; + convertInto( stringValue, value ); + if( value ) + (p.*member)(); + } + virtual void setFlag( C& p ) const { + (p.*member)(); + } + virtual bool takesArg() const { return false; } + virtual IArgFunction* clone() const { return new BoundNullaryMethod( *this ); } + void (C::*member)(); + }; + + template + struct BoundUnaryFunction : IArgFunction{ + BoundUnaryFunction( void (*_function)( C& ) ) : function( _function ) {} + virtual void set( C& obj, std::string const& stringValue ) const { + bool value; + convertInto( stringValue, value ); + if( value ) + function( obj ); + } + virtual void setFlag( C& p ) const { + function( p ); + } + virtual bool takesArg() const { return false; } + virtual IArgFunction* clone() const { return new BoundUnaryFunction( *this ); } + void (*function)( C& ); + }; + + template + struct BoundBinaryFunction : IArgFunction{ + BoundBinaryFunction( void (*_function)( C&, T ) ) : function( _function ) {} + virtual void set( C& obj, std::string const& stringValue ) const { + typename RemoveConstRef::type value; + convertInto( stringValue, value ); + function( obj, value ); + } + virtual void setFlag( C& obj ) const { + typename RemoveConstRef::type value; + convertInto( true, value ); + function( obj, value ); + } + virtual bool takesArg() const { return !IsBool::value; } + virtual IArgFunction* clone() const { return new BoundBinaryFunction( *this ); } + void (*function)( C&, T ); + }; + + } // namespace Detail + + struct Parser { + Parser() : separators( " \t=:" ) {} + + struct Token { + enum Type { Positional, ShortOpt, LongOpt }; + Token( Type _type, std::string const& _data ) : type( _type ), data( _data ) {} + Type type; + std::string data; + }; + + void parseIntoTokens( int argc, char const* const argv[], std::vector& tokens ) const { + const std::string doubleDash = "--"; + for( int i = 1; i < argc && argv[i] != doubleDash; ++i ) + parseIntoTokens( argv[i] , tokens); + } + void parseIntoTokens( std::string arg, std::vector& tokens ) const { + while( !arg.empty() ) { + Parser::Token token( Parser::Token::Positional, arg ); + arg = ""; + if( token.data[0] == '-' ) { + if( token.data.size() > 1 && token.data[1] == '-' ) { + token = Parser::Token( Parser::Token::LongOpt, token.data.substr( 2 ) ); + } + else { + token = Parser::Token( Parser::Token::ShortOpt, token.data.substr( 1 ) ); + if( token.data.size() > 1 && separators.find( token.data[1] ) == std::string::npos ) { + arg = "-" + token.data.substr( 1 ); + token.data = token.data.substr( 0, 1 ); + } + } + } + if( token.type != Parser::Token::Positional ) { + std::size_t pos = token.data.find_first_of( separators ); + if( pos != std::string::npos ) { + arg = token.data.substr( pos+1 ); + token.data = token.data.substr( 0, pos ); + } + } + tokens.push_back( token ); + } + } + std::string separators; + }; + + template + struct CommonArgProperties { + CommonArgProperties() {} + CommonArgProperties( Detail::BoundArgFunction const& _boundField ) : boundField( _boundField ) {} + + Detail::BoundArgFunction boundField; + std::string description; + std::string detail; + std::string placeholder; // Only value if boundField takes an arg + + bool takesArg() const { + return !placeholder.empty(); + } + void validate() const { + if( !boundField.isSet() ) + throw std::logic_error( "option not bound" ); + } + }; + struct OptionArgProperties { + std::vector shortNames; + std::string longName; + + bool hasShortName( std::string const& shortName ) const { + return std::find( shortNames.begin(), shortNames.end(), shortName ) != shortNames.end(); + } + bool hasLongName( std::string const& _longName ) const { + return _longName == longName; + } + }; + struct PositionalArgProperties { + PositionalArgProperties() : position( -1 ) {} + int position; // -1 means non-positional (floating) + + bool isFixedPositional() const { + return position != -1; + } + }; + + template + class CommandLine { + + struct Arg : CommonArgProperties, OptionArgProperties, PositionalArgProperties { + Arg() {} + Arg( Detail::BoundArgFunction const& _boundField ) : CommonArgProperties( _boundField ) {} + + using CommonArgProperties::placeholder; // !TBD + + std::string dbgName() const { + if( !longName.empty() ) + return "--" + longName; + if( !shortNames.empty() ) + return "-" + shortNames[0]; + return "positional args"; + } + std::string commands() const { + std::ostringstream oss; + bool first = true; + std::vector::const_iterator it = shortNames.begin(), itEnd = shortNames.end(); + for(; it != itEnd; ++it ) { + if( first ) + first = false; + else + oss << ", "; + oss << "-" << *it; + } + if( !longName.empty() ) { + if( !first ) + oss << ", "; + oss << "--" << longName; + } + if( !placeholder.empty() ) + oss << " <" << placeholder << ">"; + return oss.str(); + } + }; + + typedef CLARA_AUTO_PTR( Arg ) ArgAutoPtr; + + friend void addOptName( Arg& arg, std::string const& optName ) + { + if( optName.empty() ) + return; + if( Detail::startsWith( optName, "--" ) ) { + if( !arg.longName.empty() ) + throw std::logic_error( "Only one long opt may be specified. '" + + arg.longName + + "' already specified, now attempting to add '" + + optName + "'" ); + arg.longName = optName.substr( 2 ); + } + else if( Detail::startsWith( optName, "-" ) ) + arg.shortNames.push_back( optName.substr( 1 ) ); + else + throw std::logic_error( "option must begin with - or --. Option was: '" + optName + "'" ); + } + friend void setPositionalArg( Arg& arg, int position ) + { + arg.position = position; + } + + class ArgBuilder { + public: + ArgBuilder( Arg* arg ) : m_arg( arg ) {} + + // Bind a non-boolean data member (requires placeholder string) + template + void bind( M C::* field, std::string const& placeholder ) { + m_arg->boundField = new Detail::BoundDataMember( field ); + m_arg->placeholder = placeholder; + } + // Bind a boolean data member (no placeholder required) + template + void bind( bool C::* field ) { + m_arg->boundField = new Detail::BoundDataMember( field ); + } + + // Bind a method taking a single, non-boolean argument (requires a placeholder string) + template + void bind( void (C::* unaryMethod)( M ), std::string const& placeholder ) { + m_arg->boundField = new Detail::BoundUnaryMethod( unaryMethod ); + m_arg->placeholder = placeholder; + } + + // Bind a method taking a single, boolean argument (no placeholder string required) + template + void bind( void (C::* unaryMethod)( bool ) ) { + m_arg->boundField = new Detail::BoundUnaryMethod( unaryMethod ); + } + + // Bind a method that takes no arguments (will be called if opt is present) + template + void bind( void (C::* nullaryMethod)() ) { + m_arg->boundField = new Detail::BoundNullaryMethod( nullaryMethod ); + } + + // Bind a free function taking a single argument - the object to operate on (no placeholder string required) + template + void bind( void (* unaryFunction)( C& ) ) { + m_arg->boundField = new Detail::BoundUnaryFunction( unaryFunction ); + } + + // Bind a free function taking a single argument - the object to operate on (requires a placeholder string) + template + void bind( void (* binaryFunction)( C&, T ), std::string const& placeholder ) { + m_arg->boundField = new Detail::BoundBinaryFunction( binaryFunction ); + m_arg->placeholder = placeholder; + } + + ArgBuilder& describe( std::string const& description ) { + m_arg->description = description; + return *this; + } + ArgBuilder& detail( std::string const& detail ) { + m_arg->detail = detail; + return *this; + } + + protected: + Arg* m_arg; + }; + + class OptBuilder : public ArgBuilder { + public: + OptBuilder( Arg* arg ) : ArgBuilder( arg ) {} + OptBuilder( OptBuilder& other ) : ArgBuilder( other ) {} + + OptBuilder& operator[]( std::string const& optName ) { + addOptName( *ArgBuilder::m_arg, optName ); + return *this; + } + }; + + public: + + CommandLine() + : m_boundProcessName( new Detail::NullBinder() ), + m_highestSpecifiedArgPosition( 0 ), + m_throwOnUnrecognisedTokens( false ) + {} + CommandLine( CommandLine const& other ) + : m_boundProcessName( other.m_boundProcessName ), + m_options ( other.m_options ), + m_positionalArgs( other.m_positionalArgs ), + m_highestSpecifiedArgPosition( other.m_highestSpecifiedArgPosition ), + m_throwOnUnrecognisedTokens( other.m_throwOnUnrecognisedTokens ) + { + if( other.m_floatingArg.get() ) + m_floatingArg.reset( new Arg( *other.m_floatingArg ) ); + } + + CommandLine& setThrowOnUnrecognisedTokens( bool shouldThrow = true ) { + m_throwOnUnrecognisedTokens = shouldThrow; + return *this; + } + + OptBuilder operator[]( std::string const& optName ) { + m_options.push_back( Arg() ); + addOptName( m_options.back(), optName ); + OptBuilder builder( &m_options.back() ); + return builder; + } + + ArgBuilder operator[]( int position ) { + m_positionalArgs.insert( std::make_pair( position, Arg() ) ); + if( position > m_highestSpecifiedArgPosition ) + m_highestSpecifiedArgPosition = position; + setPositionalArg( m_positionalArgs[position], position ); + ArgBuilder builder( &m_positionalArgs[position] ); + return builder; + } + + // Invoke this with the _ instance + ArgBuilder operator[]( UnpositionalTag ) { + if( m_floatingArg.get() ) + throw std::logic_error( "Only one unpositional argument can be added" ); + m_floatingArg.reset( new Arg() ); + ArgBuilder builder( m_floatingArg.get() ); + return builder; + } + + template + void bindProcessName( M C::* field ) { + m_boundProcessName = new Detail::BoundDataMember( field ); + } + template + void bindProcessName( void (C::*_unaryMethod)( M ) ) { + m_boundProcessName = new Detail::BoundUnaryMethod( _unaryMethod ); + } + + void optUsage( std::ostream& os, std::size_t indent = 0, std::size_t width = Detail::consoleWidth ) const { + typename std::vector::const_iterator itBegin = m_options.begin(), itEnd = m_options.end(), it; + std::size_t maxWidth = 0; + for( it = itBegin; it != itEnd; ++it ) + maxWidth = (std::max)( maxWidth, it->commands().size() ); + + for( it = itBegin; it != itEnd; ++it ) { + Detail::Text usage( it->commands(), Detail::TextAttributes() + .setWidth( maxWidth+indent ) + .setIndent( indent ) ); + Detail::Text desc( it->description, Detail::TextAttributes() + .setWidth( width - maxWidth - 3 ) ); + + for( std::size_t i = 0; i < (std::max)( usage.size(), desc.size() ); ++i ) { + std::string usageCol = i < usage.size() ? usage[i] : ""; + os << usageCol; + + if( i < desc.size() && !desc[i].empty() ) + os << std::string( indent + 2 + maxWidth - usageCol.size(), ' ' ) + << desc[i]; + os << "\n"; + } + } + } + std::string optUsage() const { + std::ostringstream oss; + optUsage( oss ); + return oss.str(); + } + + void argSynopsis( std::ostream& os ) const { + for( int i = 1; i <= m_highestSpecifiedArgPosition; ++i ) { + if( i > 1 ) + os << " "; + typename std::map::const_iterator it = m_positionalArgs.find( i ); + if( it != m_positionalArgs.end() ) + os << "<" << it->second.placeholder << ">"; + else if( m_floatingArg.get() ) + os << "<" << m_floatingArg->placeholder << ">"; + else + throw std::logic_error( "non consecutive positional arguments with no floating args" ); + } + // !TBD No indication of mandatory args + if( m_floatingArg.get() ) { + if( m_highestSpecifiedArgPosition > 1 ) + os << " "; + os << "[<" << m_floatingArg->placeholder << "> ...]"; + } + } + std::string argSynopsis() const { + std::ostringstream oss; + argSynopsis( oss ); + return oss.str(); + } + + void usage( std::ostream& os, std::string const& procName ) const { + validate(); + os << "usage:\n " << procName << " "; + argSynopsis( os ); + if( !m_options.empty() ) { + os << " [options]\n\nwhere options are: \n"; + optUsage( os, 2 ); + } + os << "\n"; + } + std::string usage( std::string const& procName ) const { + std::ostringstream oss; + usage( oss, procName ); + return oss.str(); + } + + ConfigT parse( int argc, char const* const argv[] ) const { + ConfigT config; + parseInto( argc, argv, config ); + return config; + } + + std::vector parseInto( int argc, char const* argv[], ConfigT& config ) const { + std::string processName = argv[0]; + std::size_t lastSlash = processName.find_last_of( "/\\" ); + if( lastSlash != std::string::npos ) + processName = processName.substr( lastSlash+1 ); + m_boundProcessName.set( config, processName ); + std::vector tokens; + Parser parser; + parser.parseIntoTokens( argc, argv, tokens ); + return populate( tokens, config ); + } + + std::vector populate( std::vector const& tokens, ConfigT& config ) const { + validate(); + std::vector unusedTokens = populateOptions( tokens, config ); + unusedTokens = populateFixedArgs( unusedTokens, config ); + unusedTokens = populateFloatingArgs( unusedTokens, config ); + return unusedTokens; + } + + std::vector populateOptions( std::vector const& tokens, ConfigT& config ) const { + std::vector unusedTokens; + std::vector errors; + for( std::size_t i = 0; i < tokens.size(); ++i ) { + Parser::Token const& token = tokens[i]; + typename std::vector::const_iterator it = m_options.begin(), itEnd = m_options.end(); + for(; it != itEnd; ++it ) { + Arg const& arg = *it; + + try { + if( ( token.type == Parser::Token::ShortOpt && arg.hasShortName( token.data ) ) || + ( token.type == Parser::Token::LongOpt && arg.hasLongName( token.data ) ) ) { + if( arg.takesArg() ) { + if( i == tokens.size()-1 || tokens[i+1].type != Parser::Token::Positional ) + errors.push_back( "Expected argument to option: " + token.data ); + else + arg.boundField.set( config, tokens[++i].data ); + } + else { + arg.boundField.setFlag( config ); + } + break; + } + } + catch( std::exception& ex ) { + errors.push_back( std::string( ex.what() ) + "\n- while parsing: (" + arg.commands() + ")" ); + } + } + if( it == itEnd ) { + if( token.type == Parser::Token::Positional || !m_throwOnUnrecognisedTokens ) + unusedTokens.push_back( token ); + else if( errors.empty() && m_throwOnUnrecognisedTokens ) + errors.push_back( "unrecognised option: " + token.data ); + } + } + if( !errors.empty() ) { + std::ostringstream oss; + for( std::vector::const_iterator it = errors.begin(), itEnd = errors.end(); + it != itEnd; + ++it ) { + if( it != errors.begin() ) + oss << "\n"; + oss << *it; + } + throw std::runtime_error( oss.str() ); + } + return unusedTokens; + } + std::vector populateFixedArgs( std::vector const& tokens, ConfigT& config ) const { + std::vector unusedTokens; + int position = 1; + for( std::size_t i = 0; i < tokens.size(); ++i ) { + Parser::Token const& token = tokens[i]; + typename std::map::const_iterator it = m_positionalArgs.find( position ); + if( it != m_positionalArgs.end() ) + it->second.boundField.set( config, token.data ); + else + unusedTokens.push_back( token ); + if( token.type == Parser::Token::Positional ) + position++; + } + return unusedTokens; + } + std::vector populateFloatingArgs( std::vector const& tokens, ConfigT& config ) const { + if( !m_floatingArg.get() ) + return tokens; + std::vector unusedTokens; + for( std::size_t i = 0; i < tokens.size(); ++i ) { + Parser::Token const& token = tokens[i]; + if( token.type == Parser::Token::Positional ) + m_floatingArg->boundField.set( config, token.data ); + else + unusedTokens.push_back( token ); + } + return unusedTokens; + } + + void validate() const + { + if( m_options.empty() && m_positionalArgs.empty() && !m_floatingArg.get() ) + throw std::logic_error( "No options or arguments specified" ); + + for( typename std::vector::const_iterator it = m_options.begin(), + itEnd = m_options.end(); + it != itEnd; ++it ) + it->validate(); + } + + private: + Detail::BoundArgFunction m_boundProcessName; + std::vector m_options; + std::map m_positionalArgs; + ArgAutoPtr m_floatingArg; + int m_highestSpecifiedArgPosition; + bool m_throwOnUnrecognisedTokens; + }; + +} // end namespace Clara + +STITCH_CLARA_CLOSE_NAMESPACE +#undef STITCH_CLARA_OPEN_NAMESPACE +#undef STITCH_CLARA_CLOSE_NAMESPACE + +#endif // TWOBLUECUBES_CLARA_H_INCLUDED +#undef STITCH_CLARA_OPEN_NAMESPACE + +// Restore Clara's value for console width, if present +#ifdef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH +#define CLARA_CONFIG_CONSOLE_WIDTH CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH +#undef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH +#endif + +#include + +namespace Catch { + + inline void abortAfterFirst( ConfigData& config ) { config.abortAfter = 1; } + inline void abortAfterX( ConfigData& config, int x ) { + if( x < 1 ) + throw std::runtime_error( "Value after -x or --abortAfter must be greater than zero" ); + config.abortAfter = x; + } + inline void addTestOrTags( ConfigData& config, std::string const& _testSpec ) { config.testsOrTags.push_back( _testSpec ); } + inline void addReporterName( ConfigData& config, std::string const& _reporterName ) { config.reporterNames.push_back( _reporterName ); } + + inline void addWarning( ConfigData& config, std::string const& _warning ) { + if( _warning == "NoAssertions" ) + config.warnings = static_cast( config.warnings | WarnAbout::NoAssertions ); + else + throw std::runtime_error( "Unrecognised warning: '" + _warning + "'" ); + } + inline void setOrder( ConfigData& config, std::string const& order ) { + if( startsWith( "declared", order ) ) + config.runOrder = RunTests::InDeclarationOrder; + else if( startsWith( "lexical", order ) ) + config.runOrder = RunTests::InLexicographicalOrder; + else if( startsWith( "random", order ) ) + config.runOrder = RunTests::InRandomOrder; + else + throw std::runtime_error( "Unrecognised ordering: '" + order + "'" ); + } + inline void setRngSeed( ConfigData& config, std::string const& seed ) { + if( seed == "time" ) { + config.rngSeed = static_cast( std::time(0) ); + } + else { + std::stringstream ss; + ss << seed; + ss >> config.rngSeed; + if( ss.fail() ) + throw std::runtime_error( "Argment to --rng-seed should be the word 'time' or a number" ); + } + } + inline void setVerbosity( ConfigData& config, int level ) { + // !TBD: accept strings? + config.verbosity = static_cast( level ); + } + inline void setShowDurations( ConfigData& config, bool _showDurations ) { + config.showDurations = _showDurations + ? ShowDurations::Always + : ShowDurations::Never; + } + inline void loadTestNamesFromFile( ConfigData& config, std::string const& _filename ) { + std::ifstream f( _filename.c_str() ); + if( !f.is_open() ) + throw std::domain_error( "Unable to load input file: " + _filename ); + + std::string line; + while( std::getline( f, line ) ) { + line = trim(line); + if( !line.empty() && !startsWith( line, "#" ) ) + addTestOrTags( config, "\"" + line + "\"," ); + } + } + + inline Clara::CommandLine makeCommandLineParser() { + + using namespace Clara; + CommandLine cli; + + cli.bindProcessName( &ConfigData::processName ); + + cli["-?"]["-h"]["--help"] + .describe( "display usage information" ) + .bind( &ConfigData::showHelp ); + + cli["-l"]["--list-tests"] + .describe( "list all/matching test cases" ) + .bind( &ConfigData::listTests ); + + cli["-t"]["--list-tags"] + .describe( "list all/matching tags" ) + .bind( &ConfigData::listTags ); + + cli["-s"]["--success"] + .describe( "include successful tests in output" ) + .bind( &ConfigData::showSuccessfulTests ); + + cli["-b"]["--break"] + .describe( "break into debugger on failure" ) + .bind( &ConfigData::shouldDebugBreak ); + + cli["-e"]["--nothrow"] + .describe( "skip exception tests" ) + .bind( &ConfigData::noThrow ); + + cli["-i"]["--invisibles"] + .describe( "show invisibles (tabs, newlines)" ) + .bind( &ConfigData::showInvisibles ); + + cli["-o"]["--out"] + .describe( "output filename" ) + .bind( &ConfigData::outputFilename, "filename" ); + + cli["-r"]["--reporter"] +// .placeholder( "name[:filename]" ) + .describe( "reporter to use (defaults to console)" ) + .bind( &addReporterName, "name" ); + + cli["-n"]["--name"] + .describe( "suite name" ) + .bind( &ConfigData::name, "name" ); + + cli["-a"]["--abort"] + .describe( "abort at first failure" ) + .bind( &abortAfterFirst ); + + cli["-x"]["--abortx"] + .describe( "abort after x failures" ) + .bind( &abortAfterX, "no. failures" ); + + cli["-w"]["--warn"] + .describe( "enable warnings" ) + .bind( &addWarning, "warning name" ); + +// - needs updating if reinstated +// cli.into( &setVerbosity ) +// .describe( "level of verbosity (0=no output)" ) +// .shortOpt( "v") +// .longOpt( "verbosity" ) +// .placeholder( "level" ); + + cli[_] + .describe( "which test or tests to use" ) + .bind( &addTestOrTags, "test name, pattern or tags" ); + + cli["-d"]["--durations"] + .describe( "show test durations" ) + .bind( &setShowDurations, "yes/no" ); + + cli["-f"]["--input-file"] + .describe( "load test names to run from a file" ) + .bind( &loadTestNamesFromFile, "filename" ); + + cli["-#"]["--filenames-as-tags"] + .describe( "adds a tag for the filename" ) + .bind( &ConfigData::filenamesAsTags ); + + // Less common commands which don't have a short form + cli["--list-test-names-only"] + .describe( "list all/matching test cases names only" ) + .bind( &ConfigData::listTestNamesOnly ); + + cli["--list-reporters"] + .describe( "list all reporters" ) + .bind( &ConfigData::listReporters ); + + cli["--order"] + .describe( "test case order (defaults to decl)" ) + .bind( &setOrder, "decl|lex|rand" ); + + cli["--rng-seed"] + .describe( "set a specific seed for random numbers" ) + .bind( &setRngSeed, "'time'|number" ); + + cli["--force-colour"] + .describe( "force colourised output" ) + .bind( &ConfigData::forceColour ); + + return cli; + } + +} // end namespace Catch + +// #included from: internal/catch_list.hpp +#define TWOBLUECUBES_CATCH_LIST_HPP_INCLUDED + +// #included from: catch_text.h +#define TWOBLUECUBES_CATCH_TEXT_H_INCLUDED + +#define TBC_TEXT_FORMAT_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH + +#define CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE Catch +// #included from: ../external/tbc_text_format.h +// Only use header guard if we are not using an outer namespace +#ifndef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE +# ifdef TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED +# ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED +# define TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED +# endif +# else +# define TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED +# endif +#endif +#ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED +#include +#include +#include + +// Use optional outer namespace +#ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE +namespace CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE { +#endif + +namespace Tbc { + +#ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH + const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH; +#else + const unsigned int consoleWidth = 80; +#endif + + struct TextAttributes { + TextAttributes() + : initialIndent( std::string::npos ), + indent( 0 ), + width( consoleWidth-1 ), + tabChar( '\t' ) + {} + + TextAttributes& setInitialIndent( std::size_t _value ) { initialIndent = _value; return *this; } + TextAttributes& setIndent( std::size_t _value ) { indent = _value; return *this; } + TextAttributes& setWidth( std::size_t _value ) { width = _value; return *this; } + TextAttributes& setTabChar( char _value ) { tabChar = _value; return *this; } + + std::size_t initialIndent; // indent of first line, or npos + std::size_t indent; // indent of subsequent lines, or all if initialIndent is npos + std::size_t width; // maximum width of text, including indent. Longer text will wrap + char tabChar; // If this char is seen the indent is changed to current pos + }; + + class Text { + public: + Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() ) + : attr( _attr ) + { + std::string wrappableChars = " [({.,/|\\-"; + std::size_t indent = _attr.initialIndent != std::string::npos + ? _attr.initialIndent + : _attr.indent; + std::string remainder = _str; + + while( !remainder.empty() ) { + if( lines.size() >= 1000 ) { + lines.push_back( "... message truncated due to excessive size" ); + return; + } + std::size_t tabPos = std::string::npos; + std::size_t width = (std::min)( remainder.size(), _attr.width - indent ); + std::size_t pos = remainder.find_first_of( '\n' ); + if( pos <= width ) { + width = pos; + } + pos = remainder.find_last_of( _attr.tabChar, width ); + if( pos != std::string::npos ) { + tabPos = pos; + if( remainder[width] == '\n' ) + width--; + remainder = remainder.substr( 0, tabPos ) + remainder.substr( tabPos+1 ); + } + + if( width == remainder.size() ) { + spliceLine( indent, remainder, width ); + } + else if( remainder[width] == '\n' ) { + spliceLine( indent, remainder, width ); + if( width <= 1 || remainder.size() != 1 ) + remainder = remainder.substr( 1 ); + indent = _attr.indent; + } + else { + pos = remainder.find_last_of( wrappableChars, width ); + if( pos != std::string::npos && pos > 0 ) { + spliceLine( indent, remainder, pos ); + if( remainder[0] == ' ' ) + remainder = remainder.substr( 1 ); + } + else { + spliceLine( indent, remainder, width-1 ); + lines.back() += "-"; + } + if( lines.size() == 1 ) + indent = _attr.indent; + if( tabPos != std::string::npos ) + indent += tabPos; + } + } + } + + void spliceLine( std::size_t _indent, std::string& _remainder, std::size_t _pos ) { + lines.push_back( std::string( _indent, ' ' ) + _remainder.substr( 0, _pos ) ); + _remainder = _remainder.substr( _pos ); + } + + typedef std::vector::const_iterator const_iterator; + + const_iterator begin() const { return lines.begin(); } + const_iterator end() const { return lines.end(); } + std::string const& last() const { return lines.back(); } + std::size_t size() const { return lines.size(); } + std::string const& operator[]( std::size_t _index ) const { return lines[_index]; } + std::string toString() const { + std::ostringstream oss; + oss << *this; + return oss.str(); + } + + inline friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) { + for( Text::const_iterator it = _text.begin(), itEnd = _text.end(); + it != itEnd; ++it ) { + if( it != _text.begin() ) + _stream << "\n"; + _stream << *it; + } + return _stream; + } + + private: + std::string str; + TextAttributes attr; + std::vector lines; + }; + +} // end namespace Tbc + +#ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE +} // end outer namespace +#endif + +#endif // TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED +#undef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE + +namespace Catch { + using Tbc::Text; + using Tbc::TextAttributes; +} + +// #included from: catch_console_colour.hpp +#define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_HPP_INCLUDED + +namespace Catch { + + struct Colour { + enum Code { + None = 0, + + White, + Red, + Green, + Blue, + Cyan, + Yellow, + Grey, + + Bright = 0x10, + + BrightRed = Bright | Red, + BrightGreen = Bright | Green, + LightGrey = Bright | Grey, + BrightWhite = Bright | White, + + // By intention + FileName = LightGrey, + Warning = Yellow, + ResultError = BrightRed, + ResultSuccess = BrightGreen, + ResultExpectedFailure = Warning, + + Error = BrightRed, + Success = Green, + + OriginalExpression = Cyan, + ReconstructedExpression = Yellow, + + SecondaryText = LightGrey, + Headers = White + }; + + // Use constructed object for RAII guard + Colour( Code _colourCode ); + Colour( Colour const& other ); + ~Colour(); + + // Use static method for one-shot changes + static void use( Code _colourCode ); + + private: + bool m_moved; + }; + + inline std::ostream& operator << ( std::ostream& os, Colour const& ) { return os; } + +} // end namespace Catch + +// #included from: catch_interfaces_reporter.h +#define TWOBLUECUBES_CATCH_INTERFACES_REPORTER_H_INCLUDED + +#include +#include +#include +#include + +namespace Catch +{ + struct ReporterConfig { + explicit ReporterConfig( Ptr const& _fullConfig ) + : m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {} + + ReporterConfig( Ptr const& _fullConfig, std::ostream& _stream ) + : m_stream( &_stream ), m_fullConfig( _fullConfig ) {} + + std::ostream& stream() const { return *m_stream; } + Ptr fullConfig() const { return m_fullConfig; } + + private: + std::ostream* m_stream; + Ptr m_fullConfig; + }; + + struct ReporterPreferences { + ReporterPreferences() + : shouldRedirectStdOut( false ) + {} + + bool shouldRedirectStdOut; + }; + + template + struct LazyStat : Option { + LazyStat() : used( false ) {} + LazyStat& operator=( T const& _value ) { + Option::operator=( _value ); + used = false; + return *this; + } + void reset() { + Option::reset(); + used = false; + } + bool used; + }; + + struct TestRunInfo { + TestRunInfo( std::string const& _name ) : name( _name ) {} + std::string name; + }; + struct GroupInfo { + GroupInfo( std::string const& _name, + std::size_t _groupIndex, + std::size_t _groupsCount ) + : name( _name ), + groupIndex( _groupIndex ), + groupsCounts( _groupsCount ) + {} + + std::string name; + std::size_t groupIndex; + std::size_t groupsCounts; + }; + + struct AssertionStats { + AssertionStats( AssertionResult const& _assertionResult, + std::vector const& _infoMessages, + Totals const& _totals ) + : assertionResult( _assertionResult ), + infoMessages( _infoMessages ), + totals( _totals ) + { + if( assertionResult.hasMessage() ) { + // Copy message into messages list. + // !TBD This should have been done earlier, somewhere + MessageBuilder builder( assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType() ); + builder << assertionResult.getMessage(); + builder.m_info.message = builder.m_stream.str(); + + infoMessages.push_back( builder.m_info ); + } + } + virtual ~AssertionStats(); + +# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS + AssertionStats( AssertionStats const& ) = default; + AssertionStats( AssertionStats && ) = default; + AssertionStats& operator = ( AssertionStats const& ) = default; + AssertionStats& operator = ( AssertionStats && ) = default; +# endif + + AssertionResult assertionResult; + std::vector infoMessages; + Totals totals; + }; + + struct SectionStats { + SectionStats( SectionInfo const& _sectionInfo, + Counts const& _assertions, + double _durationInSeconds, + bool _missingAssertions ) + : sectionInfo( _sectionInfo ), + assertions( _assertions ), + durationInSeconds( _durationInSeconds ), + missingAssertions( _missingAssertions ) + {} + virtual ~SectionStats(); +# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS + SectionStats( SectionStats const& ) = default; + SectionStats( SectionStats && ) = default; + SectionStats& operator = ( SectionStats const& ) = default; + SectionStats& operator = ( SectionStats && ) = default; +# endif + + SectionInfo sectionInfo; + Counts assertions; + double durationInSeconds; + bool missingAssertions; + }; + + struct TestCaseStats { + TestCaseStats( TestCaseInfo const& _testInfo, + Totals const& _totals, + std::string const& _stdOut, + std::string const& _stdErr, + bool _aborting ) + : testInfo( _testInfo ), + totals( _totals ), + stdOut( _stdOut ), + stdErr( _stdErr ), + aborting( _aborting ) + {} + virtual ~TestCaseStats(); + +# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS + TestCaseStats( TestCaseStats const& ) = default; + TestCaseStats( TestCaseStats && ) = default; + TestCaseStats& operator = ( TestCaseStats const& ) = default; + TestCaseStats& operator = ( TestCaseStats && ) = default; +# endif + + TestCaseInfo testInfo; + Totals totals; + std::string stdOut; + std::string stdErr; + bool aborting; + }; + + struct TestGroupStats { + TestGroupStats( GroupInfo const& _groupInfo, + Totals const& _totals, + bool _aborting ) + : groupInfo( _groupInfo ), + totals( _totals ), + aborting( _aborting ) + {} + TestGroupStats( GroupInfo const& _groupInfo ) + : groupInfo( _groupInfo ), + aborting( false ) + {} + virtual ~TestGroupStats(); + +# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS + TestGroupStats( TestGroupStats const& ) = default; + TestGroupStats( TestGroupStats && ) = default; + TestGroupStats& operator = ( TestGroupStats const& ) = default; + TestGroupStats& operator = ( TestGroupStats && ) = default; +# endif + + GroupInfo groupInfo; + Totals totals; + bool aborting; + }; + + struct TestRunStats { + TestRunStats( TestRunInfo const& _runInfo, + Totals const& _totals, + bool _aborting ) + : runInfo( _runInfo ), + totals( _totals ), + aborting( _aborting ) + {} + virtual ~TestRunStats(); + +# ifndef CATCH_CONFIG_CPP11_GENERATED_METHODS + TestRunStats( TestRunStats const& _other ) + : runInfo( _other.runInfo ), + totals( _other.totals ), + aborting( _other.aborting ) + {} +# else + TestRunStats( TestRunStats const& ) = default; + TestRunStats( TestRunStats && ) = default; + TestRunStats& operator = ( TestRunStats const& ) = default; + TestRunStats& operator = ( TestRunStats && ) = default; +# endif + + TestRunInfo runInfo; + Totals totals; + bool aborting; + }; + + struct IStreamingReporter : IShared { + virtual ~IStreamingReporter(); + + // Implementing class must also provide the following static method: + // static std::string getDescription(); + + virtual ReporterPreferences getPreferences() const = 0; + + virtual void noMatchingTestCases( std::string const& spec ) = 0; + + virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0; + virtual void testGroupStarting( GroupInfo const& groupInfo ) = 0; + + virtual void testCaseStarting( TestCaseInfo const& testInfo ) = 0; + virtual void sectionStarting( SectionInfo const& sectionInfo ) = 0; + + virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0; + + // The return value indicates if the messages buffer should be cleared: + virtual bool assertionEnded( AssertionStats const& assertionStats ) = 0; + + virtual void sectionEnded( SectionStats const& sectionStats ) = 0; + virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0; + virtual void testGroupEnded( TestGroupStats const& testGroupStats ) = 0; + virtual void testRunEnded( TestRunStats const& testRunStats ) = 0; + + virtual void skipTest( TestCaseInfo const& testInfo ) = 0; + }; + + struct IReporterFactory : IShared { + virtual ~IReporterFactory(); + virtual IStreamingReporter* create( ReporterConfig const& config ) const = 0; + virtual std::string getDescription() const = 0; + }; + + struct IReporterRegistry { + typedef std::map > FactoryMap; + typedef std::vector > Listeners; + + virtual ~IReporterRegistry(); + virtual IStreamingReporter* create( std::string const& name, Ptr const& config ) const = 0; + virtual FactoryMap const& getFactories() const = 0; + virtual Listeners const& getListeners() const = 0; + }; + + Ptr addReporter( Ptr const& existingReporter, Ptr const& additionalReporter ); + +} + +#include +#include + +namespace Catch { + + inline std::size_t listTests( Config const& config ) { + + TestSpec testSpec = config.testSpec(); + if( config.testSpec().hasFilters() ) + Catch::cout() << "Matching test cases:\n"; + else { + Catch::cout() << "All available test cases:\n"; + testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); + } + + std::size_t matchedTests = 0; + TextAttributes nameAttr, tagsAttr; + nameAttr.setInitialIndent( 2 ).setIndent( 4 ); + tagsAttr.setIndent( 6 ); + + std::vector matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); + for( std::vector::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); + it != itEnd; + ++it ) { + matchedTests++; + TestCaseInfo const& testCaseInfo = it->getTestCaseInfo(); + Colour::Code colour = testCaseInfo.isHidden() + ? Colour::SecondaryText + : Colour::None; + Colour colourGuard( colour ); + + Catch::cout() << Text( testCaseInfo.name, nameAttr ) << std::endl; + if( !testCaseInfo.tags.empty() ) + Catch::cout() << Text( testCaseInfo.tagsAsString, tagsAttr ) << std::endl; + } + + if( !config.testSpec().hasFilters() ) + Catch::cout() << pluralise( matchedTests, "test case" ) << "\n" << std::endl; + else + Catch::cout() << pluralise( matchedTests, "matching test case" ) << "\n" << std::endl; + return matchedTests; + } + + inline std::size_t listTestsNamesOnly( Config const& config ) { + TestSpec testSpec = config.testSpec(); + if( !config.testSpec().hasFilters() ) + testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); + std::size_t matchedTests = 0; + std::vector matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); + for( std::vector::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); + it != itEnd; + ++it ) { + matchedTests++; + TestCaseInfo const& testCaseInfo = it->getTestCaseInfo(); + Catch::cout() << testCaseInfo.name << std::endl; + } + return matchedTests; + } + + struct TagInfo { + TagInfo() : count ( 0 ) {} + void add( std::string const& spelling ) { + ++count; + spellings.insert( spelling ); + } + std::string all() const { + std::string out; + for( std::set::const_iterator it = spellings.begin(), itEnd = spellings.end(); + it != itEnd; + ++it ) + out += "[" + *it + "]"; + return out; + } + std::set spellings; + std::size_t count; + }; + + inline std::size_t listTags( Config const& config ) { + TestSpec testSpec = config.testSpec(); + if( config.testSpec().hasFilters() ) + Catch::cout() << "Tags for matching test cases:\n"; + else { + Catch::cout() << "All available tags:\n"; + testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); + } + + std::map tagCounts; + + std::vector matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); + for( std::vector::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); + it != itEnd; + ++it ) { + for( std::set::const_iterator tagIt = it->getTestCaseInfo().tags.begin(), + tagItEnd = it->getTestCaseInfo().tags.end(); + tagIt != tagItEnd; + ++tagIt ) { + std::string tagName = *tagIt; + std::string lcaseTagName = toLower( tagName ); + std::map::iterator countIt = tagCounts.find( lcaseTagName ); + if( countIt == tagCounts.end() ) + countIt = tagCounts.insert( std::make_pair( lcaseTagName, TagInfo() ) ).first; + countIt->second.add( tagName ); + } + } + + for( std::map::const_iterator countIt = tagCounts.begin(), + countItEnd = tagCounts.end(); + countIt != countItEnd; + ++countIt ) { + std::ostringstream oss; + oss << " " << std::setw(2) << countIt->second.count << " "; + Text wrapper( countIt->second.all(), TextAttributes() + .setInitialIndent( 0 ) + .setIndent( oss.str().size() ) + .setWidth( CATCH_CONFIG_CONSOLE_WIDTH-10 ) ); + Catch::cout() << oss.str() << wrapper << "\n"; + } + Catch::cout() << pluralise( tagCounts.size(), "tag" ) << "\n" << std::endl; + return tagCounts.size(); + } + + inline std::size_t listReporters( Config const& /*config*/ ) { + Catch::cout() << "Available reporters:\n"; + IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories(); + IReporterRegistry::FactoryMap::const_iterator itBegin = factories.begin(), itEnd = factories.end(), it; + std::size_t maxNameLen = 0; + for(it = itBegin; it != itEnd; ++it ) + maxNameLen = (std::max)( maxNameLen, it->first.size() ); + + for(it = itBegin; it != itEnd; ++it ) { + Text wrapper( it->second->getDescription(), TextAttributes() + .setInitialIndent( 0 ) + .setIndent( 7+maxNameLen ) + .setWidth( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen-8 ) ); + Catch::cout() << " " + << it->first + << ":" + << std::string( maxNameLen - it->first.size() + 2, ' ' ) + << wrapper << "\n"; + } + Catch::cout() << std::endl; + return factories.size(); + } + + inline Option list( Config const& config ) { + Option listedCount; + if( config.listTests() ) + listedCount = listedCount.valueOr(0) + listTests( config ); + if( config.listTestNamesOnly() ) + listedCount = listedCount.valueOr(0) + listTestsNamesOnly( config ); + if( config.listTags() ) + listedCount = listedCount.valueOr(0) + listTags( config ); + if( config.listReporters() ) + listedCount = listedCount.valueOr(0) + listReporters( config ); + return listedCount; + } + +} // end namespace Catch + +// #included from: internal/catch_run_context.hpp +#define TWOBLUECUBES_CATCH_RUNNER_IMPL_HPP_INCLUDED + +// #included from: catch_test_case_tracker.hpp +#define TWOBLUECUBES_CATCH_TEST_CASE_TRACKER_HPP_INCLUDED + +#include +#include +#include +#include + +namespace Catch { +namespace TestCaseTracking { + + struct ITracker : SharedImpl<> { + virtual ~ITracker(); + + // static queries + virtual std::string name() const = 0; + + // dynamic queries + virtual bool isComplete() const = 0; // Successfully completed or failed + virtual bool isSuccessfullyCompleted() const = 0; + virtual bool isOpen() const = 0; // Started but not complete + virtual bool hasChildren() const = 0; + + virtual ITracker& parent() = 0; + + // actions + virtual void close() = 0; // Successfully complete + virtual void fail() = 0; + virtual void markAsNeedingAnotherRun() = 0; + + virtual void addChild( Ptr const& child ) = 0; + virtual ITracker* findChild( std::string const& name ) = 0; + virtual void openChild() = 0; + }; + + class TrackerContext { + + enum RunState { + NotStarted, + Executing, + CompletedCycle + }; + + Ptr m_rootTracker; + ITracker* m_currentTracker; + RunState m_runState; + + public: + + static TrackerContext& instance() { + static TrackerContext s_instance; + return s_instance; + } + + TrackerContext() + : m_currentTracker( CATCH_NULL ), + m_runState( NotStarted ) + {} + + ITracker& startRun(); + + void endRun() { + m_rootTracker.reset(); + m_currentTracker = CATCH_NULL; + m_runState = NotStarted; + } + + void startCycle() { + m_currentTracker = m_rootTracker.get(); + m_runState = Executing; + } + void completeCycle() { + m_runState = CompletedCycle; + } + + bool completedCycle() const { + return m_runState == CompletedCycle; + } + ITracker& currentTracker() { + return *m_currentTracker; + } + void setCurrentTracker( ITracker* tracker ) { + m_currentTracker = tracker; + } + }; + + class TrackerBase : public ITracker { + protected: + enum CycleState { + NotStarted, + Executing, + ExecutingChildren, + NeedsAnotherRun, + CompletedSuccessfully, + Failed + }; + class TrackerHasName { + std::string m_name; + public: + TrackerHasName( std::string const& name ) : m_name( name ) {} + bool operator ()( Ptr const& tracker ) { + return tracker->name() == m_name; + } + }; + typedef std::vector > Children; + std::string m_name; + TrackerContext& m_ctx; + ITracker* m_parent; + Children m_children; + CycleState m_runState; + public: + TrackerBase( std::string const& name, TrackerContext& ctx, ITracker* parent ) + : m_name( name ), + m_ctx( ctx ), + m_parent( parent ), + m_runState( NotStarted ) + {} + virtual ~TrackerBase(); + + virtual std::string name() const CATCH_OVERRIDE { + return m_name; + } + virtual bool isComplete() const CATCH_OVERRIDE { + return m_runState == CompletedSuccessfully || m_runState == Failed; + } + virtual bool isSuccessfullyCompleted() const CATCH_OVERRIDE { + return m_runState == CompletedSuccessfully; + } + virtual bool isOpen() const CATCH_OVERRIDE { + return m_runState != NotStarted && !isComplete(); + } + virtual bool hasChildren() const CATCH_OVERRIDE { + return !m_children.empty(); + } + + virtual void addChild( Ptr const& child ) CATCH_OVERRIDE { + m_children.push_back( child ); + } + + virtual ITracker* findChild( std::string const& name ) CATCH_OVERRIDE { + Children::const_iterator it = std::find_if( m_children.begin(), m_children.end(), TrackerHasName( name ) ); + return( it != m_children.end() ) + ? it->get() + : CATCH_NULL; + } + virtual ITracker& parent() CATCH_OVERRIDE { + assert( m_parent ); // Should always be non-null except for root + return *m_parent; + } + + virtual void openChild() CATCH_OVERRIDE { + if( m_runState != ExecutingChildren ) { + m_runState = ExecutingChildren; + if( m_parent ) + m_parent->openChild(); + } + } + void open() { + m_runState = Executing; + moveToThis(); + if( m_parent ) + m_parent->openChild(); + } + + virtual void close() CATCH_OVERRIDE { + + // Close any still open children (e.g. generators) + while( &m_ctx.currentTracker() != this ) + m_ctx.currentTracker().close(); + + switch( m_runState ) { + case NotStarted: + case CompletedSuccessfully: + case Failed: + throw std::logic_error( "Illogical state" ); + + case NeedsAnotherRun: + break;; + + case Executing: + m_runState = CompletedSuccessfully; + break; + case ExecutingChildren: + if( m_children.empty() || m_children.back()->isComplete() ) + m_runState = CompletedSuccessfully; + break; + + default: + throw std::logic_error( "Unexpected state" ); + } + moveToParent(); + m_ctx.completeCycle(); + } + virtual void fail() CATCH_OVERRIDE { + m_runState = Failed; + if( m_parent ) + m_parent->markAsNeedingAnotherRun(); + moveToParent(); + m_ctx.completeCycle(); + } + virtual void markAsNeedingAnotherRun() CATCH_OVERRIDE { + m_runState = NeedsAnotherRun; + } + private: + void moveToParent() { + assert( m_parent ); + m_ctx.setCurrentTracker( m_parent ); + } + void moveToThis() { + m_ctx.setCurrentTracker( this ); + } + }; + + class SectionTracker : public TrackerBase { + public: + SectionTracker( std::string const& name, TrackerContext& ctx, ITracker* parent ) + : TrackerBase( name, ctx, parent ) + {} + virtual ~SectionTracker(); + + static SectionTracker& acquire( TrackerContext& ctx, std::string const& name ) { + SectionTracker* section = CATCH_NULL; + + ITracker& currentTracker = ctx.currentTracker(); + if( ITracker* childTracker = currentTracker.findChild( name ) ) { + section = dynamic_cast( childTracker ); + assert( section ); + } + else { + section = new SectionTracker( name, ctx, ¤tTracker ); + currentTracker.addChild( section ); + } + if( !ctx.completedCycle() && !section->isComplete() ) { + + section->open(); + } + return *section; + } + }; + + class IndexTracker : public TrackerBase { + int m_size; + int m_index; + public: + IndexTracker( std::string const& name, TrackerContext& ctx, ITracker* parent, int size ) + : TrackerBase( name, ctx, parent ), + m_size( size ), + m_index( -1 ) + {} + virtual ~IndexTracker(); + + static IndexTracker& acquire( TrackerContext& ctx, std::string const& name, int size ) { + IndexTracker* tracker = CATCH_NULL; + + ITracker& currentTracker = ctx.currentTracker(); + if( ITracker* childTracker = currentTracker.findChild( name ) ) { + tracker = dynamic_cast( childTracker ); + assert( tracker ); + } + else { + tracker = new IndexTracker( name, ctx, ¤tTracker, size ); + currentTracker.addChild( tracker ); + } + + if( !ctx.completedCycle() && !tracker->isComplete() ) { + if( tracker->m_runState != ExecutingChildren && tracker->m_runState != NeedsAnotherRun ) + tracker->moveNext(); + tracker->open(); + } + + return *tracker; + } + + int index() const { return m_index; } + + void moveNext() { + m_index++; + m_children.clear(); + } + + virtual void close() CATCH_OVERRIDE { + TrackerBase::close(); + if( m_runState == CompletedSuccessfully && m_index < m_size-1 ) + m_runState = Executing; + } + }; + + inline ITracker& TrackerContext::startRun() { + m_rootTracker = new SectionTracker( "{root}", *this, CATCH_NULL ); + m_currentTracker = CATCH_NULL; + m_runState = Executing; + return *m_rootTracker; + } + +} // namespace TestCaseTracking + +using TestCaseTracking::ITracker; +using TestCaseTracking::TrackerContext; +using TestCaseTracking::SectionTracker; +using TestCaseTracking::IndexTracker; + +} // namespace Catch + +// #included from: catch_fatal_condition.hpp +#define TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED + +namespace Catch { + + // Report the error condition then exit the process + inline void fatal( std::string const& message, int exitCode ) { + IContext& context = Catch::getCurrentContext(); + IResultCapture* resultCapture = context.getResultCapture(); + resultCapture->handleFatalErrorCondition( message ); + + if( Catch::alwaysTrue() ) // avoids "no return" warnings + exit( exitCode ); + } + +} // namespace Catch + +#if defined ( CATCH_PLATFORM_WINDOWS ) ///////////////////////////////////////// + +namespace Catch { + + struct FatalConditionHandler { + void reset() {} + }; + +} // namespace Catch + +#else // Not Windows - assumed to be POSIX compatible ////////////////////////// + +#include + +namespace Catch { + + struct SignalDefs { int id; const char* name; }; + extern SignalDefs signalDefs[]; + SignalDefs signalDefs[] = { + { SIGINT, "SIGINT - Terminal interrupt signal" }, + { SIGILL, "SIGILL - Illegal instruction signal" }, + { SIGFPE, "SIGFPE - Floating point error signal" }, + { SIGSEGV, "SIGSEGV - Segmentation violation signal" }, + { SIGTERM, "SIGTERM - Termination request signal" }, + { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" } + }; + + struct FatalConditionHandler { + + static void handleSignal( int sig ) { + for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) + if( sig == signalDefs[i].id ) + fatal( signalDefs[i].name, -sig ); + fatal( "", -sig ); + } + + FatalConditionHandler() : m_isSet( true ) { + for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) + signal( signalDefs[i].id, handleSignal ); + } + ~FatalConditionHandler() { + reset(); + } + void reset() { + if( m_isSet ) { + for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) + signal( signalDefs[i].id, SIG_DFL ); + m_isSet = false; + } + } + + bool m_isSet; + }; + +} // namespace Catch + +#endif // not Windows + +#include +#include + +namespace Catch { + + class StreamRedirect { + + public: + StreamRedirect( std::ostream& stream, std::string& targetString ) + : m_stream( stream ), + m_prevBuf( stream.rdbuf() ), + m_targetString( targetString ) + { + stream.rdbuf( m_oss.rdbuf() ); + } + + ~StreamRedirect() { + m_targetString += m_oss.str(); + m_stream.rdbuf( m_prevBuf ); + } + + private: + std::ostream& m_stream; + std::streambuf* m_prevBuf; + std::ostringstream m_oss; + std::string& m_targetString; + }; + + /////////////////////////////////////////////////////////////////////////// + + class RunContext : public IResultCapture, public IRunner { + + RunContext( RunContext const& ); + void operator =( RunContext const& ); + + public: + + explicit RunContext( Ptr const& _config, Ptr const& reporter ) + : m_runInfo( _config->name() ), + m_context( getCurrentMutableContext() ), + m_activeTestCase( CATCH_NULL ), + m_config( _config ), + m_reporter( reporter ) + { + m_context.setRunner( this ); + m_context.setConfig( m_config ); + m_context.setResultCapture( this ); + m_reporter->testRunStarting( m_runInfo ); + } + + virtual ~RunContext() { + m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, aborting() ) ); + } + + void testGroupStarting( std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount ) { + m_reporter->testGroupStarting( GroupInfo( testSpec, groupIndex, groupsCount ) ); + } + void testGroupEnded( std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount ) { + m_reporter->testGroupEnded( TestGroupStats( GroupInfo( testSpec, groupIndex, groupsCount ), totals, aborting() ) ); + } + + Totals runTest( TestCase const& testCase ) { + Totals prevTotals = m_totals; + + std::string redirectedCout; + std::string redirectedCerr; + + TestCaseInfo testInfo = testCase.getTestCaseInfo(); + + m_reporter->testCaseStarting( testInfo ); + + m_activeTestCase = &testCase; + + do { + m_trackerContext.startRun(); + do { + m_trackerContext.startCycle(); + m_testCaseTracker = &SectionTracker::acquire( m_trackerContext, testInfo.name ); + runCurrentTest( redirectedCout, redirectedCerr ); + } + while( !m_testCaseTracker->isSuccessfullyCompleted() && !aborting() ); + } + // !TBD: deprecated - this will be replaced by indexed trackers + while( getCurrentContext().advanceGeneratorsForCurrentTest() && !aborting() ); + + Totals deltaTotals = m_totals.delta( prevTotals ); + m_totals.testCases += deltaTotals.testCases; + m_reporter->testCaseEnded( TestCaseStats( testInfo, + deltaTotals, + redirectedCout, + redirectedCerr, + aborting() ) ); + + m_activeTestCase = CATCH_NULL; + m_testCaseTracker = CATCH_NULL; + + return deltaTotals; + } + + Ptr config() const { + return m_config; + } + + private: // IResultCapture + + virtual void assertionEnded( AssertionResult const& result ) { + if( result.getResultType() == ResultWas::Ok ) { + m_totals.assertions.passed++; + } + else if( !result.isOk() ) { + m_totals.assertions.failed++; + } + + if( m_reporter->assertionEnded( AssertionStats( result, m_messages, m_totals ) ) ) + m_messages.clear(); + + // Reset working state + m_lastAssertionInfo = AssertionInfo( "", m_lastAssertionInfo.lineInfo, "{Unknown expression after the reported line}" , m_lastAssertionInfo.resultDisposition ); + m_lastResult = result; + } + + virtual bool sectionStarted ( + SectionInfo const& sectionInfo, + Counts& assertions + ) + { + std::ostringstream oss; + oss << sectionInfo.name << "@" << sectionInfo.lineInfo; + + ITracker& sectionTracker = SectionTracker::acquire( m_trackerContext, oss.str() ); + if( !sectionTracker.isOpen() ) + return false; + m_activeSections.push_back( §ionTracker ); + + m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo; + + m_reporter->sectionStarting( sectionInfo ); + + assertions = m_totals.assertions; + + return true; + } + bool testForMissingAssertions( Counts& assertions ) { + if( assertions.total() != 0 ) + return false; + if( !m_config->warnAboutMissingAssertions() ) + return false; + if( m_trackerContext.currentTracker().hasChildren() ) + return false; + m_totals.assertions.failed++; + assertions.failed++; + return true; + } + + virtual void sectionEnded( SectionEndInfo const& endInfo ) { + Counts assertions = m_totals.assertions - endInfo.prevAssertions; + bool missingAssertions = testForMissingAssertions( assertions ); + + if( !m_activeSections.empty() ) { + m_activeSections.back()->close(); + m_activeSections.pop_back(); + } + + m_reporter->sectionEnded( SectionStats( endInfo.sectionInfo, assertions, endInfo.durationInSeconds, missingAssertions ) ); + m_messages.clear(); + } + + virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) { + if( m_unfinishedSections.empty() ) + m_activeSections.back()->fail(); + else + m_activeSections.back()->close(); + m_activeSections.pop_back(); + + m_unfinishedSections.push_back( endInfo ); + } + + virtual void pushScopedMessage( MessageInfo const& message ) { + m_messages.push_back( message ); + } + + virtual void popScopedMessage( MessageInfo const& message ) { + m_messages.erase( std::remove( m_messages.begin(), m_messages.end(), message ), m_messages.end() ); + } + + virtual std::string getCurrentTestName() const { + return m_activeTestCase + ? m_activeTestCase->getTestCaseInfo().name + : ""; + } + + virtual const AssertionResult* getLastResult() const { + return &m_lastResult; + } + + virtual void handleFatalErrorCondition( std::string const& message ) { + ResultBuilder resultBuilder = makeUnexpectedResultBuilder(); + resultBuilder.setResultType( ResultWas::FatalErrorCondition ); + resultBuilder << message; + resultBuilder.captureExpression(); + + handleUnfinishedSections(); + + // Recreate section for test case (as we will lose the one that was in scope) + TestCaseInfo const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); + SectionInfo testCaseSection( testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description ); + + Counts assertions; + assertions.failed = 1; + SectionStats testCaseSectionStats( testCaseSection, assertions, 0, false ); + m_reporter->sectionEnded( testCaseSectionStats ); + + TestCaseInfo testInfo = m_activeTestCase->getTestCaseInfo(); + + Totals deltaTotals; + deltaTotals.testCases.failed = 1; + m_reporter->testCaseEnded( TestCaseStats( testInfo, + deltaTotals, + "", + "", + false ) ); + m_totals.testCases.failed++; + testGroupEnded( "", m_totals, 1, 1 ); + m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, false ) ); + } + + public: + // !TBD We need to do this another way! + bool aborting() const { + return m_totals.assertions.failed == static_cast( m_config->abortAfter() ); + } + + private: + + void runCurrentTest( std::string& redirectedCout, std::string& redirectedCerr ) { + TestCaseInfo const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); + SectionInfo testCaseSection( testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description ); + m_reporter->sectionStarting( testCaseSection ); + Counts prevAssertions = m_totals.assertions; + double duration = 0; + try { + m_lastAssertionInfo = AssertionInfo( "TEST_CASE", testCaseInfo.lineInfo, "", ResultDisposition::Normal ); + + seedRng( *m_config ); + + Timer timer; + timer.start(); + if( m_reporter->getPreferences().shouldRedirectStdOut ) { + StreamRedirect coutRedir( Catch::cout(), redirectedCout ); + StreamRedirect cerrRedir( Catch::cerr(), redirectedCerr ); + invokeActiveTestCase(); + } + else { + invokeActiveTestCase(); + } + duration = timer.getElapsedSeconds(); + } + catch( TestFailureException& ) { + // This just means the test was aborted due to failure + } + catch(...) { + makeUnexpectedResultBuilder().useActiveException(); + } + m_testCaseTracker->close(); + handleUnfinishedSections(); + m_messages.clear(); + + Counts assertions = m_totals.assertions - prevAssertions; + bool missingAssertions = testForMissingAssertions( assertions ); + + if( testCaseInfo.okToFail() ) { + std::swap( assertions.failedButOk, assertions.failed ); + m_totals.assertions.failed -= assertions.failedButOk; + m_totals.assertions.failedButOk += assertions.failedButOk; + } + + SectionStats testCaseSectionStats( testCaseSection, assertions, duration, missingAssertions ); + m_reporter->sectionEnded( testCaseSectionStats ); + } + + void invokeActiveTestCase() { + FatalConditionHandler fatalConditionHandler; // Handle signals + m_activeTestCase->invoke(); + fatalConditionHandler.reset(); + } + + private: + + ResultBuilder makeUnexpectedResultBuilder() const { + return ResultBuilder( m_lastAssertionInfo.macroName.c_str(), + m_lastAssertionInfo.lineInfo, + m_lastAssertionInfo.capturedExpression.c_str(), + m_lastAssertionInfo.resultDisposition ); + } + + void handleUnfinishedSections() { + // If sections ended prematurely due to an exception we stored their + // infos here so we can tear them down outside the unwind process. + for( std::vector::const_reverse_iterator it = m_unfinishedSections.rbegin(), + itEnd = m_unfinishedSections.rend(); + it != itEnd; + ++it ) + sectionEnded( *it ); + m_unfinishedSections.clear(); + } + + TestRunInfo m_runInfo; + IMutableContext& m_context; + TestCase const* m_activeTestCase; + ITracker* m_testCaseTracker; + ITracker* m_currentSectionTracker; + AssertionResult m_lastResult; + + Ptr m_config; + Totals m_totals; + Ptr m_reporter; + std::vector m_messages; + AssertionInfo m_lastAssertionInfo; + std::vector m_unfinishedSections; + std::vector m_activeSections; + TrackerContext m_trackerContext; + }; + + IResultCapture& getResultCapture() { + if( IResultCapture* capture = getCurrentContext().getResultCapture() ) + return *capture; + else + throw std::logic_error( "No result capture instance" ); + } + +} // end namespace Catch + +// #included from: internal/catch_version.h +#define TWOBLUECUBES_CATCH_VERSION_H_INCLUDED + +namespace Catch { + + // Versioning information + struct Version { + Version( unsigned int _majorVersion, + unsigned int _minorVersion, + unsigned int _patchNumber, + std::string const& _branchName, + unsigned int _buildNumber ); + + unsigned int const majorVersion; + unsigned int const minorVersion; + unsigned int const patchNumber; + + // buildNumber is only used if branchName is not null + std::string const branchName; + unsigned int const buildNumber; + + friend std::ostream& operator << ( std::ostream& os, Version const& version ); + + private: + void operator=( Version const& ); + }; + + extern Version libraryVersion; +} + +#include +#include +#include + +namespace Catch { + + Ptr createReporter( std::string const& reporterName, Ptr const& config ) { + Ptr reporter = getRegistryHub().getReporterRegistry().create( reporterName, config.get() ); + if( !reporter ) { + std::ostringstream oss; + oss << "No reporter registered with name: '" << reporterName << "'"; + throw std::domain_error( oss.str() ); + } + return reporter; + } + + Ptr makeReporter( Ptr const& config ) { + std::vector reporters = config->getReporterNames(); + if( reporters.empty() ) + reporters.push_back( "console" ); + + Ptr reporter; + for( std::vector::const_iterator it = reporters.begin(), itEnd = reporters.end(); + it != itEnd; + ++it ) + reporter = addReporter( reporter, createReporter( *it, config ) ); + return reporter; + } + Ptr addListeners( Ptr const& config, Ptr reporters ) { + IReporterRegistry::Listeners listeners = getRegistryHub().getReporterRegistry().getListeners(); + for( IReporterRegistry::Listeners::const_iterator it = listeners.begin(), itEnd = listeners.end(); + it != itEnd; + ++it ) + reporters = addReporter(reporters, (*it)->create( ReporterConfig( config ) ) ); + return reporters; + } + + Totals runTests( Ptr const& config ) { + + Ptr iconfig = config.get(); + + Ptr reporter = makeReporter( config ); + reporter = addListeners( iconfig, reporter ); + + RunContext context( iconfig, reporter ); + + Totals totals; + + context.testGroupStarting( config->name(), 1, 1 ); + + TestSpec testSpec = config->testSpec(); + if( !testSpec.hasFilters() ) + testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "~[.]" ).testSpec(); // All not hidden tests + + std::vector const& allTestCases = getAllTestCasesSorted( *iconfig ); + for( std::vector::const_iterator it = allTestCases.begin(), itEnd = allTestCases.end(); + it != itEnd; + ++it ) { + if( !context.aborting() && matchTest( *it, testSpec, *iconfig ) ) + totals += context.runTest( *it ); + else + reporter->skipTest( *it ); + } + + context.testGroupEnded( iconfig->name(), totals, 1, 1 ); + return totals; + } + + void applyFilenamesAsTags( IConfig const& config ) { + std::vector const& tests = getAllTestCasesSorted( config ); + for(std::size_t i = 0; i < tests.size(); ++i ) { + TestCase& test = const_cast( tests[i] ); + std::set tags = test.tags; + + std::string filename = test.lineInfo.file; + std::string::size_type lastSlash = filename.find_last_of( "\\/" ); + if( lastSlash != std::string::npos ) + filename = filename.substr( lastSlash+1 ); + + std::string::size_type lastDot = filename.find_last_of( "." ); + if( lastDot != std::string::npos ) + filename = filename.substr( 0, lastDot ); + + tags.insert( "#" + filename ); + setTags( test, tags ); + } + } + + class Session : NonCopyable { + static bool alreadyInstantiated; + + public: + + struct OnUnusedOptions { enum DoWhat { Ignore, Fail }; }; + + Session() + : m_cli( makeCommandLineParser() ) { + if( alreadyInstantiated ) { + std::string msg = "Only one instance of Catch::Session can ever be used"; + Catch::cerr() << msg << std::endl; + throw std::logic_error( msg ); + } + alreadyInstantiated = true; + } + ~Session() { + Catch::cleanUp(); + } + + void showHelp( std::string const& processName ) { + Catch::cout() << "\nCatch v" << libraryVersion << "\n"; + + m_cli.usage( Catch::cout(), processName ); + Catch::cout() << "For more detail usage please see the project docs\n" << std::endl; + } + + int applyCommandLine( int argc, char const* argv[], OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail ) { + try { + m_cli.setThrowOnUnrecognisedTokens( unusedOptionBehaviour == OnUnusedOptions::Fail ); + m_unusedTokens = m_cli.parseInto( argc, argv, m_configData ); + if( m_configData.showHelp ) + showHelp( m_configData.processName ); + m_config.reset(); + } + catch( std::exception& ex ) { + { + Colour colourGuard( Colour::Red ); + Catch::cerr() + << "\nError(s) in input:\n" + << Text( ex.what(), TextAttributes().setIndent(2) ) + << "\n\n"; + } + m_cli.usage( Catch::cout(), m_configData.processName ); + return (std::numeric_limits::max)(); + } + return 0; + } + + void useConfigData( ConfigData const& _configData ) { + m_configData = _configData; + m_config.reset(); + } + + int run( int argc, char const* argv[] ) { + + int returnCode = applyCommandLine( argc, argv ); + if( returnCode == 0 ) + returnCode = run(); + return returnCode; + } + int run( int argc, char* argv[] ) { + return run( argc, const_cast( argv ) ); + } + + int run() { + if( m_configData.showHelp ) + return 0; + + try + { + config(); // Force config to be constructed + + seedRng( *m_config ); + + if( m_configData.filenamesAsTags ) + applyFilenamesAsTags( *m_config ); + + // Handle list request + if( Option listed = list( config() ) ) + return static_cast( *listed ); + + return static_cast( runTests( m_config ).assertions.failed ); + } + catch( std::exception& ex ) { + Catch::cerr() << ex.what() << std::endl; + return (std::numeric_limits::max)(); + } + } + + Clara::CommandLine const& cli() const { + return m_cli; + } + std::vector const& unusedTokens() const { + return m_unusedTokens; + } + ConfigData& configData() { + return m_configData; + } + Config& config() { + if( !m_config ) + m_config = new Config( m_configData ); + return *m_config; + } + private: + Clara::CommandLine m_cli; + std::vector m_unusedTokens; + ConfigData m_configData; + Ptr m_config; + }; + + bool Session::alreadyInstantiated = false; + +} // end namespace Catch + +// #included from: catch_registry_hub.hpp +#define TWOBLUECUBES_CATCH_REGISTRY_HUB_HPP_INCLUDED + +// #included from: catch_test_case_registry_impl.hpp +#define TWOBLUECUBES_CATCH_TEST_CASE_REGISTRY_IMPL_HPP_INCLUDED + +#include +#include +#include +#include +#include + +namespace Catch { + + struct LexSort { + bool operator() (TestCase i,TestCase j) const { return (i sortTests( IConfig const& config, std::vector const& unsortedTestCases ) { + + std::vector sorted = unsortedTestCases; + + switch( config.runOrder() ) { + case RunTests::InLexicographicalOrder: + std::sort( sorted.begin(), sorted.end(), LexSort() ); + break; + case RunTests::InRandomOrder: + { + seedRng( config ); + + RandomNumberGenerator rng; + std::random_shuffle( sorted.begin(), sorted.end(), rng ); + } + break; + case RunTests::InDeclarationOrder: + // already in declaration order + break; + } + return sorted; + } + bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ) { + return testSpec.matches( testCase ) && ( config.allowThrows() || !testCase.throws() ); + } + + void enforceNoDuplicateTestCases( std::vector const& functions ) { + std::set seenFunctions; + for( std::vector::const_iterator it = functions.begin(), itEnd = functions.end(); + it != itEnd; + ++it ) { + std::pair::const_iterator, bool> prev = seenFunctions.insert( *it ); + if( !prev.second ){ + Catch::cerr() + << Colour( Colour::Red ) + << "error: TEST_CASE( \"" << it->name << "\" ) already defined.\n" + << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << "\n" + << "\tRedefined at " << it->getTestCaseInfo().lineInfo << std::endl; + exit(1); + } + } + } + + std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ) { + std::vector filtered; + filtered.reserve( testCases.size() ); + for( std::vector::const_iterator it = testCases.begin(), itEnd = testCases.end(); + it != itEnd; + ++it ) + if( matchTest( *it, testSpec, config ) ) + filtered.push_back( *it ); + return filtered; + } + std::vector const& getAllTestCasesSorted( IConfig const& config ) { + return getRegistryHub().getTestCaseRegistry().getAllTestsSorted( config ); + } + + class TestRegistry : public ITestCaseRegistry { + public: + TestRegistry() + : m_currentSortOrder( RunTests::InDeclarationOrder ), + m_unnamedCount( 0 ) + {} + virtual ~TestRegistry(); + + virtual void registerTest( TestCase const& testCase ) { + std::string name = testCase.getTestCaseInfo().name; + if( name == "" ) { + std::ostringstream oss; + oss << "Anonymous test case " << ++m_unnamedCount; + return registerTest( testCase.withName( oss.str() ) ); + } + m_functions.push_back( testCase ); + } + + virtual std::vector const& getAllTests() const { + return m_functions; + } + virtual std::vector const& getAllTestsSorted( IConfig const& config ) const { + if( m_sortedFunctions.empty() ) + enforceNoDuplicateTestCases( m_functions ); + + if( m_currentSortOrder != config.runOrder() || m_sortedFunctions.empty() ) { + m_sortedFunctions = sortTests( config, m_functions ); + m_currentSortOrder = config.runOrder(); + } + return m_sortedFunctions; + } + + private: + std::vector m_functions; + mutable RunTests::InWhatOrder m_currentSortOrder; + mutable std::vector m_sortedFunctions; + size_t m_unnamedCount; + std::ios_base::Init m_ostreamInit; // Forces cout/ cerr to be initialised + }; + + /////////////////////////////////////////////////////////////////////////// + + class FreeFunctionTestCase : public SharedImpl { + public: + + FreeFunctionTestCase( TestFunction fun ) : m_fun( fun ) {} + + virtual void invoke() const { + m_fun(); + } + + private: + virtual ~FreeFunctionTestCase(); + + TestFunction m_fun; + }; + + inline std::string extractClassName( std::string const& classOrQualifiedMethodName ) { + std::string className = classOrQualifiedMethodName; + if( startsWith( className, "&" ) ) + { + std::size_t lastColons = className.rfind( "::" ); + std::size_t penultimateColons = className.rfind( "::", lastColons-1 ); + if( penultimateColons == std::string::npos ) + penultimateColons = 1; + className = className.substr( penultimateColons, lastColons-penultimateColons ); + } + return className; + } + + void registerTestCase + ( ITestCase* testCase, + char const* classOrQualifiedMethodName, + NameAndDesc const& nameAndDesc, + SourceLineInfo const& lineInfo ) { + + getMutableRegistryHub().registerTest + ( makeTestCase + ( testCase, + extractClassName( classOrQualifiedMethodName ), + nameAndDesc.name, + nameAndDesc.description, + lineInfo ) ); + } + void registerTestCaseFunction + ( TestFunction function, + SourceLineInfo const& lineInfo, + NameAndDesc const& nameAndDesc ) { + registerTestCase( new FreeFunctionTestCase( function ), "", nameAndDesc, lineInfo ); + } + + /////////////////////////////////////////////////////////////////////////// + + AutoReg::AutoReg + ( TestFunction function, + SourceLineInfo const& lineInfo, + NameAndDesc const& nameAndDesc ) { + registerTestCaseFunction( function, lineInfo, nameAndDesc ); + } + + AutoReg::~AutoReg() {} + +} // end namespace Catch + +// #included from: catch_reporter_registry.hpp +#define TWOBLUECUBES_CATCH_REPORTER_REGISTRY_HPP_INCLUDED + +#include + +namespace Catch { + + class ReporterRegistry : public IReporterRegistry { + + public: + + virtual ~ReporterRegistry() CATCH_OVERRIDE {} + + virtual IStreamingReporter* create( std::string const& name, Ptr const& config ) const CATCH_OVERRIDE { + FactoryMap::const_iterator it = m_factories.find( name ); + if( it == m_factories.end() ) + return CATCH_NULL; + return it->second->create( ReporterConfig( config ) ); + } + + void registerReporter( std::string const& name, Ptr const& factory ) { + m_factories.insert( std::make_pair( name, factory ) ); + } + void registerListener( Ptr const& factory ) { + m_listeners.push_back( factory ); + } + + virtual FactoryMap const& getFactories() const CATCH_OVERRIDE { + return m_factories; + } + virtual Listeners const& getListeners() const CATCH_OVERRIDE { + return m_listeners; + } + + private: + FactoryMap m_factories; + Listeners m_listeners; + }; +} + +// #included from: catch_exception_translator_registry.hpp +#define TWOBLUECUBES_CATCH_EXCEPTION_TRANSLATOR_REGISTRY_HPP_INCLUDED + +#ifdef __OBJC__ +#import "Foundation/Foundation.h" +#endif + +namespace Catch { + + class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry { + public: + ~ExceptionTranslatorRegistry() { + deleteAll( m_translators ); + } + + virtual void registerTranslator( const IExceptionTranslator* translator ) { + m_translators.push_back( translator ); + } + + virtual std::string translateActiveException() const { + try { +#ifdef __OBJC__ + // In Objective-C try objective-c exceptions first + @try { + return tryTranslators(); + } + @catch (NSException *exception) { + return Catch::toString( [exception description] ); + } +#else + return tryTranslators(); +#endif + } + catch( TestFailureException& ) { + throw; + } + catch( std::exception& ex ) { + return ex.what(); + } + catch( std::string& msg ) { + return msg; + } + catch( const char* msg ) { + return msg; + } + catch(...) { + return "Unknown exception"; + } + } + + std::string tryTranslators() const { + if( m_translators.empty() ) + throw; + else + return m_translators[0]->translate( m_translators.begin()+1, m_translators.end() ); + } + + private: + std::vector m_translators; + }; +} + +namespace Catch { + + namespace { + + class RegistryHub : public IRegistryHub, public IMutableRegistryHub { + + RegistryHub( RegistryHub const& ); + void operator=( RegistryHub const& ); + + public: // IRegistryHub + RegistryHub() { + } + virtual IReporterRegistry const& getReporterRegistry() const CATCH_OVERRIDE { + return m_reporterRegistry; + } + virtual ITestCaseRegistry const& getTestCaseRegistry() const CATCH_OVERRIDE { + return m_testCaseRegistry; + } + virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() CATCH_OVERRIDE { + return m_exceptionTranslatorRegistry; + } + + public: // IMutableRegistryHub + virtual void registerReporter( std::string const& name, Ptr const& factory ) CATCH_OVERRIDE { + m_reporterRegistry.registerReporter( name, factory ); + } + virtual void registerListener( Ptr const& factory ) CATCH_OVERRIDE { + m_reporterRegistry.registerListener( factory ); + } + virtual void registerTest( TestCase const& testInfo ) CATCH_OVERRIDE { + m_testCaseRegistry.registerTest( testInfo ); + } + virtual void registerTranslator( const IExceptionTranslator* translator ) CATCH_OVERRIDE { + m_exceptionTranslatorRegistry.registerTranslator( translator ); + } + + private: + TestRegistry m_testCaseRegistry; + ReporterRegistry m_reporterRegistry; + ExceptionTranslatorRegistry m_exceptionTranslatorRegistry; + }; + + // Single, global, instance + inline RegistryHub*& getTheRegistryHub() { + static RegistryHub* theRegistryHub = CATCH_NULL; + if( !theRegistryHub ) + theRegistryHub = new RegistryHub(); + return theRegistryHub; + } + } + + IRegistryHub& getRegistryHub() { + return *getTheRegistryHub(); + } + IMutableRegistryHub& getMutableRegistryHub() { + return *getTheRegistryHub(); + } + void cleanUp() { + delete getTheRegistryHub(); + getTheRegistryHub() = CATCH_NULL; + cleanUpContext(); + } + std::string translateActiveException() { + return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException(); + } + +} // end namespace Catch + +// #included from: catch_notimplemented_exception.hpp +#define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_HPP_INCLUDED + +#include + +namespace Catch { + + NotImplementedException::NotImplementedException( SourceLineInfo const& lineInfo ) + : m_lineInfo( lineInfo ) { + std::ostringstream oss; + oss << lineInfo << ": function "; + oss << "not implemented"; + m_what = oss.str(); + } + + const char* NotImplementedException::what() const CATCH_NOEXCEPT { + return m_what.c_str(); + } + +} // end namespace Catch + +// #included from: catch_context_impl.hpp +#define TWOBLUECUBES_CATCH_CONTEXT_IMPL_HPP_INCLUDED + +// #included from: catch_stream.hpp +#define TWOBLUECUBES_CATCH_STREAM_HPP_INCLUDED + +#include +#include +#include + +namespace Catch { + + template + class StreamBufImpl : public StreamBufBase { + char data[bufferSize]; + WriterF m_writer; + + public: + StreamBufImpl() { + setp( data, data + sizeof(data) ); + } + + ~StreamBufImpl() CATCH_NOEXCEPT { + sync(); + } + + private: + int overflow( int c ) { + sync(); + + if( c != EOF ) { + if( pbase() == epptr() ) + m_writer( std::string( 1, static_cast( c ) ) ); + else + sputc( static_cast( c ) ); + } + return 0; + } + + int sync() { + if( pbase() != pptr() ) { + m_writer( std::string( pbase(), static_cast( pptr() - pbase() ) ) ); + setp( pbase(), epptr() ); + } + return 0; + } + }; + + /////////////////////////////////////////////////////////////////////////// + + FileStream::FileStream( std::string const& filename ) { + m_ofs.open( filename.c_str() ); + if( m_ofs.fail() ) { + std::ostringstream oss; + oss << "Unable to open file: '" << filename << "'"; + throw std::domain_error( oss.str() ); + } + } + + std::ostream& FileStream::stream() const { + return m_ofs; + } + + struct OutputDebugWriter { + + void operator()( std::string const&str ) { + writeToDebugConsole( str ); + } + }; + + DebugOutStream::DebugOutStream() + : m_streamBuf( new StreamBufImpl() ), + m_os( m_streamBuf.get() ) + {} + + std::ostream& DebugOutStream::stream() const { + return m_os; + } + + // Store the streambuf from cout up-front because + // cout may get redirected when running tests + CoutStream::CoutStream() + : m_os( Catch::cout().rdbuf() ) + {} + + std::ostream& CoutStream::stream() const { + return m_os; + } + +#ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement these functions + std::ostream& cout() { + return std::cout; + } + std::ostream& cerr() { + return std::cerr; + } +#endif +} + +namespace Catch { + + class Context : public IMutableContext { + + Context() : m_config( CATCH_NULL ), m_runner( CATCH_NULL ), m_resultCapture( CATCH_NULL ) {} + Context( Context const& ); + void operator=( Context const& ); + + public: // IContext + virtual IResultCapture* getResultCapture() { + return m_resultCapture; + } + virtual IRunner* getRunner() { + return m_runner; + } + virtual size_t getGeneratorIndex( std::string const& fileInfo, size_t totalSize ) { + return getGeneratorsForCurrentTest() + .getGeneratorInfo( fileInfo, totalSize ) + .getCurrentIndex(); + } + virtual bool advanceGeneratorsForCurrentTest() { + IGeneratorsForTest* generators = findGeneratorsForCurrentTest(); + return generators && generators->moveNext(); + } + + virtual Ptr getConfig() const { + return m_config; + } + + public: // IMutableContext + virtual void setResultCapture( IResultCapture* resultCapture ) { + m_resultCapture = resultCapture; + } + virtual void setRunner( IRunner* runner ) { + m_runner = runner; + } + virtual void setConfig( Ptr const& config ) { + m_config = config; + } + + friend IMutableContext& getCurrentMutableContext(); + + private: + IGeneratorsForTest* findGeneratorsForCurrentTest() { + std::string testName = getResultCapture()->getCurrentTestName(); + + std::map::const_iterator it = + m_generatorsByTestName.find( testName ); + return it != m_generatorsByTestName.end() + ? it->second + : CATCH_NULL; + } + + IGeneratorsForTest& getGeneratorsForCurrentTest() { + IGeneratorsForTest* generators = findGeneratorsForCurrentTest(); + if( !generators ) { + std::string testName = getResultCapture()->getCurrentTestName(); + generators = createGeneratorsForTest(); + m_generatorsByTestName.insert( std::make_pair( testName, generators ) ); + } + return *generators; + } + + private: + Ptr m_config; + IRunner* m_runner; + IResultCapture* m_resultCapture; + std::map m_generatorsByTestName; + }; + + namespace { + Context* currentContext = CATCH_NULL; + } + IMutableContext& getCurrentMutableContext() { + if( !currentContext ) + currentContext = new Context(); + return *currentContext; + } + IContext& getCurrentContext() { + return getCurrentMutableContext(); + } + + void cleanUpContext() { + delete currentContext; + currentContext = CATCH_NULL; + } +} + +// #included from: catch_console_colour_impl.hpp +#define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_IMPL_HPP_INCLUDED + +namespace Catch { + namespace { + + struct IColourImpl { + virtual ~IColourImpl() {} + virtual void use( Colour::Code _colourCode ) = 0; + }; + + struct NoColourImpl : IColourImpl { + void use( Colour::Code ) {} + + static IColourImpl* instance() { + static NoColourImpl s_instance; + return &s_instance; + } + }; + + } // anon namespace +} // namespace Catch + +#if !defined( CATCH_CONFIG_COLOUR_NONE ) && !defined( CATCH_CONFIG_COLOUR_WINDOWS ) && !defined( CATCH_CONFIG_COLOUR_ANSI ) +# ifdef CATCH_PLATFORM_WINDOWS +# define CATCH_CONFIG_COLOUR_WINDOWS +# else +# define CATCH_CONFIG_COLOUR_ANSI +# endif +#endif + +#if defined ( CATCH_CONFIG_COLOUR_WINDOWS ) ///////////////////////////////////////// + +#ifndef NOMINMAX +#define NOMINMAX +#endif + +#ifdef __AFXDLL +#include +#else +#include +#endif + +namespace Catch { +namespace { + + class Win32ColourImpl : public IColourImpl { + public: + Win32ColourImpl() : stdoutHandle( GetStdHandle(STD_OUTPUT_HANDLE) ) + { + CONSOLE_SCREEN_BUFFER_INFO csbiInfo; + GetConsoleScreenBufferInfo( stdoutHandle, &csbiInfo ); + originalForegroundAttributes = csbiInfo.wAttributes & ~( BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY ); + originalBackgroundAttributes = csbiInfo.wAttributes & ~( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY ); + } + + virtual void use( Colour::Code _colourCode ) { + switch( _colourCode ) { + case Colour::None: return setTextAttribute( originalForegroundAttributes ); + case Colour::White: return setTextAttribute( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); + case Colour::Red: return setTextAttribute( FOREGROUND_RED ); + case Colour::Green: return setTextAttribute( FOREGROUND_GREEN ); + case Colour::Blue: return setTextAttribute( FOREGROUND_BLUE ); + case Colour::Cyan: return setTextAttribute( FOREGROUND_BLUE | FOREGROUND_GREEN ); + case Colour::Yellow: return setTextAttribute( FOREGROUND_RED | FOREGROUND_GREEN ); + case Colour::Grey: return setTextAttribute( 0 ); + + case Colour::LightGrey: return setTextAttribute( FOREGROUND_INTENSITY ); + case Colour::BrightRed: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED ); + case Colour::BrightGreen: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN ); + case Colour::BrightWhite: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); + + case Colour::Bright: throw std::logic_error( "not a colour" ); + } + } + + private: + void setTextAttribute( WORD _textAttribute ) { + SetConsoleTextAttribute( stdoutHandle, _textAttribute | originalBackgroundAttributes ); + } + HANDLE stdoutHandle; + WORD originalForegroundAttributes; + WORD originalBackgroundAttributes; + }; + + IColourImpl* platformColourInstance() { + static Win32ColourImpl s_instance; + return &s_instance; + } + +} // end anon namespace +} // end namespace Catch + +#elif defined( CATCH_CONFIG_COLOUR_ANSI ) ////////////////////////////////////// + +#include + +namespace Catch { +namespace { + + // use POSIX/ ANSI console terminal codes + // Thanks to Adam Strzelecki for original contribution + // (http://github.com/nanoant) + // https://github.com/philsquared/Catch/pull/131 + class PosixColourImpl : public IColourImpl { + public: + virtual void use( Colour::Code _colourCode ) { + switch( _colourCode ) { + case Colour::None: + case Colour::White: return setColour( "[0m" ); + case Colour::Red: return setColour( "[0;31m" ); + case Colour::Green: return setColour( "[0;32m" ); + case Colour::Blue: return setColour( "[0:34m" ); + case Colour::Cyan: return setColour( "[0;36m" ); + case Colour::Yellow: return setColour( "[0;33m" ); + case Colour::Grey: return setColour( "[1;30m" ); + + case Colour::LightGrey: return setColour( "[0;37m" ); + case Colour::BrightRed: return setColour( "[1;31m" ); + case Colour::BrightGreen: return setColour( "[1;32m" ); + case Colour::BrightWhite: return setColour( "[1;37m" ); + + case Colour::Bright: throw std::logic_error( "not a colour" ); + } + } + static IColourImpl* instance() { + static PosixColourImpl s_instance; + return &s_instance; + } + + private: + void setColour( const char* _escapeCode ) { + Catch::cout() << '\033' << _escapeCode; + } + }; + + IColourImpl* platformColourInstance() { + Ptr config = getCurrentContext().getConfig(); + return (config && config->forceColour()) || isatty(STDOUT_FILENO) + ? PosixColourImpl::instance() + : NoColourImpl::instance(); + } + +} // end anon namespace +} // end namespace Catch + +#else // not Windows or ANSI /////////////////////////////////////////////// + +namespace Catch { + + static IColourImpl* platformColourInstance() { return NoColourImpl::instance(); } + +} // end namespace Catch + +#endif // Windows/ ANSI/ None + +namespace Catch { + + Colour::Colour( Code _colourCode ) : m_moved( false ) { use( _colourCode ); } + Colour::Colour( Colour const& _other ) : m_moved( false ) { const_cast( _other ).m_moved = true; } + Colour::~Colour(){ if( !m_moved ) use( None ); } + + void Colour::use( Code _colourCode ) { + static IColourImpl* impl = isDebuggerActive() + ? NoColourImpl::instance() + : platformColourInstance(); + impl->use( _colourCode ); + } + +} // end namespace Catch + +// #included from: catch_generators_impl.hpp +#define TWOBLUECUBES_CATCH_GENERATORS_IMPL_HPP_INCLUDED + +#include +#include +#include + +namespace Catch { + + struct GeneratorInfo : IGeneratorInfo { + + GeneratorInfo( std::size_t size ) + : m_size( size ), + m_currentIndex( 0 ) + {} + + bool moveNext() { + if( ++m_currentIndex == m_size ) { + m_currentIndex = 0; + return false; + } + return true; + } + + std::size_t getCurrentIndex() const { + return m_currentIndex; + } + + std::size_t m_size; + std::size_t m_currentIndex; + }; + + /////////////////////////////////////////////////////////////////////////// + + class GeneratorsForTest : public IGeneratorsForTest { + + public: + ~GeneratorsForTest() { + deleteAll( m_generatorsInOrder ); + } + + IGeneratorInfo& getGeneratorInfo( std::string const& fileInfo, std::size_t size ) { + std::map::const_iterator it = m_generatorsByName.find( fileInfo ); + if( it == m_generatorsByName.end() ) { + IGeneratorInfo* info = new GeneratorInfo( size ); + m_generatorsByName.insert( std::make_pair( fileInfo, info ) ); + m_generatorsInOrder.push_back( info ); + return *info; + } + return *it->second; + } + + bool moveNext() { + std::vector::const_iterator it = m_generatorsInOrder.begin(); + std::vector::const_iterator itEnd = m_generatorsInOrder.end(); + for(; it != itEnd; ++it ) { + if( (*it)->moveNext() ) + return true; + } + return false; + } + + private: + std::map m_generatorsByName; + std::vector m_generatorsInOrder; + }; + + IGeneratorsForTest* createGeneratorsForTest() + { + return new GeneratorsForTest(); + } + +} // end namespace Catch + +// #included from: catch_assertionresult.hpp +#define TWOBLUECUBES_CATCH_ASSERTIONRESULT_HPP_INCLUDED + +namespace Catch { + + AssertionInfo::AssertionInfo( std::string const& _macroName, + SourceLineInfo const& _lineInfo, + std::string const& _capturedExpression, + ResultDisposition::Flags _resultDisposition ) + : macroName( _macroName ), + lineInfo( _lineInfo ), + capturedExpression( _capturedExpression ), + resultDisposition( _resultDisposition ) + {} + + AssertionResult::AssertionResult() {} + + AssertionResult::AssertionResult( AssertionInfo const& info, AssertionResultData const& data ) + : m_info( info ), + m_resultData( data ) + {} + + AssertionResult::~AssertionResult() {} + + // Result was a success + bool AssertionResult::succeeded() const { + return Catch::isOk( m_resultData.resultType ); + } + + // Result was a success, or failure is suppressed + bool AssertionResult::isOk() const { + return Catch::isOk( m_resultData.resultType ) || shouldSuppressFailure( m_info.resultDisposition ); + } + + ResultWas::OfType AssertionResult::getResultType() const { + return m_resultData.resultType; + } + + bool AssertionResult::hasExpression() const { + return !m_info.capturedExpression.empty(); + } + + bool AssertionResult::hasMessage() const { + return !m_resultData.message.empty(); + } + + std::string AssertionResult::getExpression() const { + if( isFalseTest( m_info.resultDisposition ) ) + return "!" + m_info.capturedExpression; + else + return m_info.capturedExpression; + } + std::string AssertionResult::getExpressionInMacro() const { + if( m_info.macroName.empty() ) + return m_info.capturedExpression; + else + return m_info.macroName + "( " + m_info.capturedExpression + " )"; + } + + bool AssertionResult::hasExpandedExpression() const { + return hasExpression() && getExpandedExpression() != getExpression(); + } + + std::string AssertionResult::getExpandedExpression() const { + return m_resultData.reconstructedExpression; + } + + std::string AssertionResult::getMessage() const { + return m_resultData.message; + } + SourceLineInfo AssertionResult::getSourceInfo() const { + return m_info.lineInfo; + } + + std::string AssertionResult::getTestMacroName() const { + return m_info.macroName; + } + +} // end namespace Catch + +// #included from: catch_test_case_info.hpp +#define TWOBLUECUBES_CATCH_TEST_CASE_INFO_HPP_INCLUDED + +namespace Catch { + + inline TestCaseInfo::SpecialProperties parseSpecialTag( std::string const& tag ) { + if( startsWith( tag, "." ) || + tag == "hide" || + tag == "!hide" ) + return TestCaseInfo::IsHidden; + else if( tag == "!throws" ) + return TestCaseInfo::Throws; + else if( tag == "!shouldfail" ) + return TestCaseInfo::ShouldFail; + else if( tag == "!mayfail" ) + return TestCaseInfo::MayFail; + else + return TestCaseInfo::None; + } + inline bool isReservedTag( std::string const& tag ) { + return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !isalnum( tag[0] ); + } + inline void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) { + if( isReservedTag( tag ) ) { + { + Colour colourGuard( Colour::Red ); + Catch::cerr() + << "Tag name [" << tag << "] not allowed.\n" + << "Tag names starting with non alpha-numeric characters are reserved\n"; + } + { + Colour colourGuard( Colour::FileName ); + Catch::cerr() << _lineInfo << std::endl; + } + exit(1); + } + } + + TestCase makeTestCase( ITestCase* _testCase, + std::string const& _className, + std::string const& _name, + std::string const& _descOrTags, + SourceLineInfo const& _lineInfo ) + { + bool isHidden( startsWith( _name, "./" ) ); // Legacy support + + // Parse out tags + std::set tags; + std::string desc, tag; + bool inTag = false; + for( std::size_t i = 0; i < _descOrTags.size(); ++i ) { + char c = _descOrTags[i]; + if( !inTag ) { + if( c == '[' ) + inTag = true; + else + desc += c; + } + else { + if( c == ']' ) { + TestCaseInfo::SpecialProperties prop = parseSpecialTag( tag ); + if( prop == TestCaseInfo::IsHidden ) + isHidden = true; + else if( prop == TestCaseInfo::None ) + enforceNotReservedTag( tag, _lineInfo ); + + tags.insert( tag ); + tag.clear(); + inTag = false; + } + else + tag += c; + } + } + if( isHidden ) { + tags.insert( "hide" ); + tags.insert( "." ); + } + + TestCaseInfo info( _name, _className, desc, tags, _lineInfo ); + return TestCase( _testCase, info ); + } + + void setTags( TestCaseInfo& testCaseInfo, std::set const& tags ) + { + testCaseInfo.tags = tags; + testCaseInfo.lcaseTags.clear(); + + std::ostringstream oss; + for( std::set::const_iterator it = tags.begin(), itEnd = tags.end(); it != itEnd; ++it ) { + oss << "[" << *it << "]"; + std::string lcaseTag = toLower( *it ); + testCaseInfo.properties = static_cast( testCaseInfo.properties | parseSpecialTag( lcaseTag ) ); + testCaseInfo.lcaseTags.insert( lcaseTag ); + } + testCaseInfo.tagsAsString = oss.str(); + } + + TestCaseInfo::TestCaseInfo( std::string const& _name, + std::string const& _className, + std::string const& _description, + std::set const& _tags, + SourceLineInfo const& _lineInfo ) + : name( _name ), + className( _className ), + description( _description ), + lineInfo( _lineInfo ), + properties( None ) + { + setTags( *this, _tags ); + } + + TestCaseInfo::TestCaseInfo( TestCaseInfo const& other ) + : name( other.name ), + className( other.className ), + description( other.description ), + tags( other.tags ), + lcaseTags( other.lcaseTags ), + tagsAsString( other.tagsAsString ), + lineInfo( other.lineInfo ), + properties( other.properties ) + {} + + bool TestCaseInfo::isHidden() const { + return ( properties & IsHidden ) != 0; + } + bool TestCaseInfo::throws() const { + return ( properties & Throws ) != 0; + } + bool TestCaseInfo::okToFail() const { + return ( properties & (ShouldFail | MayFail ) ) != 0; + } + bool TestCaseInfo::expectedToFail() const { + return ( properties & (ShouldFail ) ) != 0; + } + + TestCase::TestCase( ITestCase* testCase, TestCaseInfo const& info ) : TestCaseInfo( info ), test( testCase ) {} + + TestCase::TestCase( TestCase const& other ) + : TestCaseInfo( other ), + test( other.test ) + {} + + TestCase TestCase::withName( std::string const& _newName ) const { + TestCase other( *this ); + other.name = _newName; + return other; + } + + void TestCase::swap( TestCase& other ) { + test.swap( other.test ); + name.swap( other.name ); + className.swap( other.className ); + description.swap( other.description ); + tags.swap( other.tags ); + lcaseTags.swap( other.lcaseTags ); + tagsAsString.swap( other.tagsAsString ); + std::swap( TestCaseInfo::properties, static_cast( other ).properties ); + std::swap( lineInfo, other.lineInfo ); + } + + void TestCase::invoke() const { + test->invoke(); + } + + bool TestCase::operator == ( TestCase const& other ) const { + return test.get() == other.test.get() && + name == other.name && + className == other.className; + } + + bool TestCase::operator < ( TestCase const& other ) const { + return name < other.name; + } + TestCase& TestCase::operator = ( TestCase const& other ) { + TestCase temp( other ); + swap( temp ); + return *this; + } + + TestCaseInfo const& TestCase::getTestCaseInfo() const + { + return *this; + } + +} // end namespace Catch + +// #included from: catch_version.hpp +#define TWOBLUECUBES_CATCH_VERSION_HPP_INCLUDED + +namespace Catch { + + Version::Version + ( unsigned int _majorVersion, + unsigned int _minorVersion, + unsigned int _patchNumber, + std::string const& _branchName, + unsigned int _buildNumber ) + : majorVersion( _majorVersion ), + minorVersion( _minorVersion ), + patchNumber( _patchNumber ), + branchName( _branchName ), + buildNumber( _buildNumber ) + {} + + std::ostream& operator << ( std::ostream& os, Version const& version ) { + os << version.majorVersion << "." + << version.minorVersion << "." + << version.patchNumber; + + if( !version.branchName.empty() ) { + os << "-" << version.branchName + << "." << version.buildNumber; + } + return os; + } + + Version libraryVersion( 1, 3, 4, "", 0 ); + +} + +// #included from: catch_message.hpp +#define TWOBLUECUBES_CATCH_MESSAGE_HPP_INCLUDED + +namespace Catch { + + MessageInfo::MessageInfo( std::string const& _macroName, + SourceLineInfo const& _lineInfo, + ResultWas::OfType _type ) + : macroName( _macroName ), + lineInfo( _lineInfo ), + type( _type ), + sequence( ++globalCount ) + {} + + // This may need protecting if threading support is added + unsigned int MessageInfo::globalCount = 0; + + //////////////////////////////////////////////////////////////////////////// + + ScopedMessage::ScopedMessage( MessageBuilder const& builder ) + : m_info( builder.m_info ) + { + m_info.message = builder.m_stream.str(); + getResultCapture().pushScopedMessage( m_info ); + } + ScopedMessage::ScopedMessage( ScopedMessage const& other ) + : m_info( other.m_info ) + {} + + ScopedMessage::~ScopedMessage() { + getResultCapture().popScopedMessage( m_info ); + } + +} // end namespace Catch + +// #included from: catch_legacy_reporter_adapter.hpp +#define TWOBLUECUBES_CATCH_LEGACY_REPORTER_ADAPTER_HPP_INCLUDED + +// #included from: catch_legacy_reporter_adapter.h +#define TWOBLUECUBES_CATCH_LEGACY_REPORTER_ADAPTER_H_INCLUDED + +namespace Catch +{ + // Deprecated + struct IReporter : IShared { + virtual ~IReporter(); + + virtual bool shouldRedirectStdout() const = 0; + + virtual void StartTesting() = 0; + virtual void EndTesting( Totals const& totals ) = 0; + virtual void StartGroup( std::string const& groupName ) = 0; + virtual void EndGroup( std::string const& groupName, Totals const& totals ) = 0; + virtual void StartTestCase( TestCaseInfo const& testInfo ) = 0; + virtual void EndTestCase( TestCaseInfo const& testInfo, Totals const& totals, std::string const& stdOut, std::string const& stdErr ) = 0; + virtual void StartSection( std::string const& sectionName, std::string const& description ) = 0; + virtual void EndSection( std::string const& sectionName, Counts const& assertions ) = 0; + virtual void NoAssertionsInSection( std::string const& sectionName ) = 0; + virtual void NoAssertionsInTestCase( std::string const& testName ) = 0; + virtual void Aborted() = 0; + virtual void Result( AssertionResult const& result ) = 0; + }; + + class LegacyReporterAdapter : public SharedImpl + { + public: + LegacyReporterAdapter( Ptr const& legacyReporter ); + virtual ~LegacyReporterAdapter(); + + virtual ReporterPreferences getPreferences() const; + virtual void noMatchingTestCases( std::string const& ); + virtual void testRunStarting( TestRunInfo const& ); + virtual void testGroupStarting( GroupInfo const& groupInfo ); + virtual void testCaseStarting( TestCaseInfo const& testInfo ); + virtual void sectionStarting( SectionInfo const& sectionInfo ); + virtual void assertionStarting( AssertionInfo const& ); + virtual bool assertionEnded( AssertionStats const& assertionStats ); + virtual void sectionEnded( SectionStats const& sectionStats ); + virtual void testCaseEnded( TestCaseStats const& testCaseStats ); + virtual void testGroupEnded( TestGroupStats const& testGroupStats ); + virtual void testRunEnded( TestRunStats const& testRunStats ); + virtual void skipTest( TestCaseInfo const& ); + + private: + Ptr m_legacyReporter; + }; +} + +namespace Catch +{ + LegacyReporterAdapter::LegacyReporterAdapter( Ptr const& legacyReporter ) + : m_legacyReporter( legacyReporter ) + {} + LegacyReporterAdapter::~LegacyReporterAdapter() {} + + ReporterPreferences LegacyReporterAdapter::getPreferences() const { + ReporterPreferences prefs; + prefs.shouldRedirectStdOut = m_legacyReporter->shouldRedirectStdout(); + return prefs; + } + + void LegacyReporterAdapter::noMatchingTestCases( std::string const& ) {} + void LegacyReporterAdapter::testRunStarting( TestRunInfo const& ) { + m_legacyReporter->StartTesting(); + } + void LegacyReporterAdapter::testGroupStarting( GroupInfo const& groupInfo ) { + m_legacyReporter->StartGroup( groupInfo.name ); + } + void LegacyReporterAdapter::testCaseStarting( TestCaseInfo const& testInfo ) { + m_legacyReporter->StartTestCase( testInfo ); + } + void LegacyReporterAdapter::sectionStarting( SectionInfo const& sectionInfo ) { + m_legacyReporter->StartSection( sectionInfo.name, sectionInfo.description ); + } + void LegacyReporterAdapter::assertionStarting( AssertionInfo const& ) { + // Not on legacy interface + } + + bool LegacyReporterAdapter::assertionEnded( AssertionStats const& assertionStats ) { + if( assertionStats.assertionResult.getResultType() != ResultWas::Ok ) { + for( std::vector::const_iterator it = assertionStats.infoMessages.begin(), itEnd = assertionStats.infoMessages.end(); + it != itEnd; + ++it ) { + if( it->type == ResultWas::Info ) { + ResultBuilder rb( it->macroName.c_str(), it->lineInfo, "", ResultDisposition::Normal ); + rb << it->message; + rb.setResultType( ResultWas::Info ); + AssertionResult result = rb.build(); + m_legacyReporter->Result( result ); + } + } + } + m_legacyReporter->Result( assertionStats.assertionResult ); + return true; + } + void LegacyReporterAdapter::sectionEnded( SectionStats const& sectionStats ) { + if( sectionStats.missingAssertions ) + m_legacyReporter->NoAssertionsInSection( sectionStats.sectionInfo.name ); + m_legacyReporter->EndSection( sectionStats.sectionInfo.name, sectionStats.assertions ); + } + void LegacyReporterAdapter::testCaseEnded( TestCaseStats const& testCaseStats ) { + m_legacyReporter->EndTestCase + ( testCaseStats.testInfo, + testCaseStats.totals, + testCaseStats.stdOut, + testCaseStats.stdErr ); + } + void LegacyReporterAdapter::testGroupEnded( TestGroupStats const& testGroupStats ) { + if( testGroupStats.aborting ) + m_legacyReporter->Aborted(); + m_legacyReporter->EndGroup( testGroupStats.groupInfo.name, testGroupStats.totals ); + } + void LegacyReporterAdapter::testRunEnded( TestRunStats const& testRunStats ) { + m_legacyReporter->EndTesting( testRunStats.totals ); + } + void LegacyReporterAdapter::skipTest( TestCaseInfo const& ) { + } +} + +// #included from: catch_timer.hpp + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wc++11-long-long" +#endif + +#ifdef CATCH_PLATFORM_WINDOWS +#include +#else +#include +#endif + +namespace Catch { + + namespace { +#ifdef CATCH_PLATFORM_WINDOWS + uint64_t getCurrentTicks() { + static uint64_t hz=0, hzo=0; + if (!hz) { + QueryPerformanceFrequency( reinterpret_cast( &hz ) ); + QueryPerformanceCounter( reinterpret_cast( &hzo ) ); + } + uint64_t t; + QueryPerformanceCounter( reinterpret_cast( &t ) ); + return ((t-hzo)*1000000)/hz; + } +#else + uint64_t getCurrentTicks() { + timeval t; + gettimeofday(&t,CATCH_NULL); + return static_cast( t.tv_sec ) * 1000000ull + static_cast( t.tv_usec ); + } +#endif + } + + void Timer::start() { + m_ticks = getCurrentTicks(); + } + unsigned int Timer::getElapsedMicroseconds() const { + return static_cast(getCurrentTicks() - m_ticks); + } + unsigned int Timer::getElapsedMilliseconds() const { + return static_cast(getElapsedMicroseconds()/1000); + } + double Timer::getElapsedSeconds() const { + return getElapsedMicroseconds()/1000000.0; + } + +} // namespace Catch + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +// #included from: catch_common.hpp +#define TWOBLUECUBES_CATCH_COMMON_HPP_INCLUDED + +namespace Catch { + + bool startsWith( std::string const& s, std::string const& prefix ) { + return s.size() >= prefix.size() && s.substr( 0, prefix.size() ) == prefix; + } + bool endsWith( std::string const& s, std::string const& suffix ) { + return s.size() >= suffix.size() && s.substr( s.size()-suffix.size(), suffix.size() ) == suffix; + } + bool contains( std::string const& s, std::string const& infix ) { + return s.find( infix ) != std::string::npos; + } + void toLowerInPlace( std::string& s ) { + std::transform( s.begin(), s.end(), s.begin(), ::tolower ); + } + std::string toLower( std::string const& s ) { + std::string lc = s; + toLowerInPlace( lc ); + return lc; + } + std::string trim( std::string const& str ) { + static char const* whitespaceChars = "\n\r\t "; + std::string::size_type start = str.find_first_not_of( whitespaceChars ); + std::string::size_type end = str.find_last_not_of( whitespaceChars ); + + return start != std::string::npos ? str.substr( start, 1+end-start ) : ""; + } + + bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ) { + bool replaced = false; + std::size_t i = str.find( replaceThis ); + while( i != std::string::npos ) { + replaced = true; + str = str.substr( 0, i ) + withThis + str.substr( i+replaceThis.size() ); + if( i < str.size()-withThis.size() ) + i = str.find( replaceThis, i+withThis.size() ); + else + i = std::string::npos; + } + return replaced; + } + + pluralise::pluralise( std::size_t count, std::string const& label ) + : m_count( count ), + m_label( label ) + {} + + std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ) { + os << pluraliser.m_count << " " << pluraliser.m_label; + if( pluraliser.m_count != 1 ) + os << "s"; + return os; + } + + SourceLineInfo::SourceLineInfo() : line( 0 ){} + SourceLineInfo::SourceLineInfo( char const* _file, std::size_t _line ) + : file( _file ), + line( _line ) + {} + SourceLineInfo::SourceLineInfo( SourceLineInfo const& other ) + : file( other.file ), + line( other.line ) + {} + bool SourceLineInfo::empty() const { + return file.empty(); + } + bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const { + return line == other.line && file == other.file; + } + bool SourceLineInfo::operator < ( SourceLineInfo const& other ) const { + return line < other.line || ( line == other.line && file < other.file ); + } + + void seedRng( IConfig const& config ) { + if( config.rngSeed() != 0 ) + std::srand( config.rngSeed() ); + } + unsigned int rngSeed() { + return getCurrentContext().getConfig()->rngSeed(); + } + + std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) { +#ifndef __GNUG__ + os << info.file << "(" << info.line << ")"; +#else + os << info.file << ":" << info.line; +#endif + return os; + } + + void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo ) { + std::ostringstream oss; + oss << locationInfo << ": Internal Catch error: '" << message << "'"; + if( alwaysTrue() ) + throw std::logic_error( oss.str() ); + } +} + +// #included from: catch_section.hpp +#define TWOBLUECUBES_CATCH_SECTION_HPP_INCLUDED + +namespace Catch { + + SectionInfo::SectionInfo + ( SourceLineInfo const& _lineInfo, + std::string const& _name, + std::string const& _description ) + : name( _name ), + description( _description ), + lineInfo( _lineInfo ) + {} + + Section::Section( SectionInfo const& info ) + : m_info( info ), + m_sectionIncluded( getResultCapture().sectionStarted( m_info, m_assertions ) ) + { + m_timer.start(); + } + + Section::~Section() { + if( m_sectionIncluded ) { + SectionEndInfo endInfo( m_info, m_assertions, m_timer.getElapsedSeconds() ); + if( std::uncaught_exception() ) + getResultCapture().sectionEndedEarly( endInfo ); + else + getResultCapture().sectionEnded( endInfo ); + } + } + + // This indicates whether the section should be executed or not + Section::operator bool() const { + return m_sectionIncluded; + } + +} // end namespace Catch + +// #included from: catch_debugger.hpp +#define TWOBLUECUBES_CATCH_DEBUGGER_HPP_INCLUDED + +#include + +#ifdef CATCH_PLATFORM_MAC + + #include + #include + #include + #include + #include + + namespace Catch{ + + // The following function is taken directly from the following technical note: + // http://developer.apple.com/library/mac/#qa/qa2004/qa1361.html + + // Returns true if the current process is being debugged (either + // running under the debugger or has a debugger attached post facto). + bool isDebuggerActive(){ + + int mib[4]; + struct kinfo_proc info; + size_t size; + + // Initialize the flags so that, if sysctl fails for some bizarre + // reason, we get a predictable result. + + info.kp_proc.p_flag = 0; + + // Initialize mib, which tells sysctl the info we want, in this case + // we're looking for information about a specific process ID. + + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID; + mib[3] = getpid(); + + // Call sysctl. + + size = sizeof(info); + if( sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, CATCH_NULL, 0) != 0 ) { + Catch::cerr() << "\n** Call to sysctl failed - unable to determine if debugger is active **\n" << std::endl; + return false; + } + + // We're being debugged if the P_TRACED flag is set. + + return ( (info.kp_proc.p_flag & P_TRACED) != 0 ); + } + } // namespace Catch + +#elif defined(_MSC_VER) + extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); + namespace Catch { + bool isDebuggerActive() { + return IsDebuggerPresent() != 0; + } + } +#elif defined(__MINGW32__) + extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); + namespace Catch { + bool isDebuggerActive() { + return IsDebuggerPresent() != 0; + } + } +#else + namespace Catch { + inline bool isDebuggerActive() { return false; } + } +#endif // Platform + +#ifdef CATCH_PLATFORM_WINDOWS + extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA( const char* ); + namespace Catch { + void writeToDebugConsole( std::string const& text ) { + ::OutputDebugStringA( text.c_str() ); + } + } +#else + namespace Catch { + void writeToDebugConsole( std::string const& text ) { + // !TBD: Need a version for Mac/ XCode and other IDEs + Catch::cout() << text; + } + } +#endif // Platform + +// #included from: catch_tostring.hpp +#define TWOBLUECUBES_CATCH_TOSTRING_HPP_INCLUDED + +namespace Catch { + +namespace Detail { + + const std::string unprintableString = "{?}"; + + namespace { + const int hexThreshold = 255; + + struct Endianness { + enum Arch { Big, Little }; + + static Arch which() { + union _{ + int asInt; + char asChar[sizeof (int)]; + } u; + + u.asInt = 1; + return ( u.asChar[sizeof(int)-1] == 1 ) ? Big : Little; + } + }; + } + + std::string rawMemoryToString( const void *object, std::size_t size ) + { + // Reverse order for little endian architectures + int i = 0, end = static_cast( size ), inc = 1; + if( Endianness::which() == Endianness::Little ) { + i = end-1; + end = inc = -1; + } + + unsigned char const *bytes = static_cast(object); + std::ostringstream os; + os << "0x" << std::setfill('0') << std::hex; + for( ; i != end; i += inc ) + os << std::setw(2) << static_cast(bytes[i]); + return os.str(); + } +} + +std::string toString( std::string const& value ) { + std::string s = value; + if( getCurrentContext().getConfig()->showInvisibles() ) { + for(size_t i = 0; i < s.size(); ++i ) { + std::string subs; + switch( s[i] ) { + case '\n': subs = "\\n"; break; + case '\t': subs = "\\t"; break; + default: break; + } + if( !subs.empty() ) { + s = s.substr( 0, i ) + subs + s.substr( i+1 ); + ++i; + } + } + } + return "\"" + s + "\""; +} +std::string toString( std::wstring const& value ) { + + std::string s; + s.reserve( value.size() ); + for(size_t i = 0; i < value.size(); ++i ) + s += value[i] <= 0xff ? static_cast( value[i] ) : '?'; + return Catch::toString( s ); +} + +std::string toString( const char* const value ) { + return value ? Catch::toString( std::string( value ) ) : std::string( "{null string}" ); +} + +std::string toString( char* const value ) { + return Catch::toString( static_cast( value ) ); +} + +std::string toString( const wchar_t* const value ) +{ + return value ? Catch::toString( std::wstring(value) ) : std::string( "{null string}" ); +} + +std::string toString( wchar_t* const value ) +{ + return Catch::toString( static_cast( value ) ); +} + +std::string toString( int value ) { + std::ostringstream oss; + oss << value; + if( value > Detail::hexThreshold ) + oss << " (0x" << std::hex << value << ")"; + return oss.str(); +} + +std::string toString( unsigned long value ) { + std::ostringstream oss; + oss << value; + if( value > Detail::hexThreshold ) + oss << " (0x" << std::hex << value << ")"; + return oss.str(); +} + +std::string toString( unsigned int value ) { + return Catch::toString( static_cast( value ) ); +} + +template +std::string fpToString( T value, int precision ) { + std::ostringstream oss; + oss << std::setprecision( precision ) + << std::fixed + << value; + std::string d = oss.str(); + std::size_t i = d.find_last_not_of( '0' ); + if( i != std::string::npos && i != d.size()-1 ) { + if( d[i] == '.' ) + i++; + d = d.substr( 0, i+1 ); + } + return d; +} + +std::string toString( const double value ) { + return fpToString( value, 10 ); +} +std::string toString( const float value ) { + return fpToString( value, 5 ) + "f"; +} + +std::string toString( bool value ) { + return value ? "true" : "false"; +} + +std::string toString( char value ) { + return value < ' ' + ? toString( static_cast( value ) ) + : Detail::makeString( value ); +} + +std::string toString( signed char value ) { + return toString( static_cast( value ) ); +} + +std::string toString( unsigned char value ) { + return toString( static_cast( value ) ); +} + +#ifdef CATCH_CONFIG_CPP11_LONG_LONG +std::string toString( long long value ) { + std::ostringstream oss; + oss << value; + if( value > Detail::hexThreshold ) + oss << " (0x" << std::hex << value << ")"; + return oss.str(); +} +std::string toString( unsigned long long value ) { + std::ostringstream oss; + oss << value; + if( value > Detail::hexThreshold ) + oss << " (0x" << std::hex << value << ")"; + return oss.str(); +} +#endif + +#ifdef CATCH_CONFIG_CPP11_NULLPTR +std::string toString( std::nullptr_t ) { + return "nullptr"; +} +#endif + +#ifdef __OBJC__ + std::string toString( NSString const * const& nsstring ) { + if( !nsstring ) + return "nil"; + return "@" + toString([nsstring UTF8String]); + } + std::string toString( NSString * CATCH_ARC_STRONG const& nsstring ) { + if( !nsstring ) + return "nil"; + return "@" + toString([nsstring UTF8String]); + } + std::string toString( NSObject* const& nsObject ) { + return toString( [nsObject description] ); + } +#endif + +} // end namespace Catch + +// #included from: catch_result_builder.hpp +#define TWOBLUECUBES_CATCH_RESULT_BUILDER_HPP_INCLUDED + +namespace Catch { + + std::string capturedExpressionWithSecondArgument( std::string const& capturedExpression, std::string const& secondArg ) { + return secondArg.empty() || secondArg == "\"\"" + ? capturedExpression + : capturedExpression + ", " + secondArg; + } + ResultBuilder::ResultBuilder( char const* macroName, + SourceLineInfo const& lineInfo, + char const* capturedExpression, + ResultDisposition::Flags resultDisposition, + char const* secondArg ) + : m_assertionInfo( macroName, lineInfo, capturedExpressionWithSecondArgument( capturedExpression, secondArg ), resultDisposition ), + m_shouldDebugBreak( false ), + m_shouldThrow( false ) + {} + + ResultBuilder& ResultBuilder::setResultType( ResultWas::OfType result ) { + m_data.resultType = result; + return *this; + } + ResultBuilder& ResultBuilder::setResultType( bool result ) { + m_data.resultType = result ? ResultWas::Ok : ResultWas::ExpressionFailed; + return *this; + } + ResultBuilder& ResultBuilder::setLhs( std::string const& lhs ) { + m_exprComponents.lhs = lhs; + return *this; + } + ResultBuilder& ResultBuilder::setRhs( std::string const& rhs ) { + m_exprComponents.rhs = rhs; + return *this; + } + ResultBuilder& ResultBuilder::setOp( std::string const& op ) { + m_exprComponents.op = op; + return *this; + } + + void ResultBuilder::endExpression() { + m_exprComponents.testFalse = isFalseTest( m_assertionInfo.resultDisposition ); + captureExpression(); + } + + void ResultBuilder::useActiveException( ResultDisposition::Flags resultDisposition ) { + m_assertionInfo.resultDisposition = resultDisposition; + m_stream.oss << Catch::translateActiveException(); + captureResult( ResultWas::ThrewException ); + } + + void ResultBuilder::captureResult( ResultWas::OfType resultType ) { + setResultType( resultType ); + captureExpression(); + } + void ResultBuilder::captureExpectedException( std::string const& expectedMessage ) { + if( expectedMessage.empty() ) + captureExpectedException( Matchers::Impl::Generic::AllOf() ); + else + captureExpectedException( Matchers::Equals( expectedMessage ) ); + } + + void ResultBuilder::captureExpectedException( Matchers::Impl::Matcher const& matcher ) { + + assert( m_exprComponents.testFalse == false ); + AssertionResultData data = m_data; + data.resultType = ResultWas::Ok; + data.reconstructedExpression = m_assertionInfo.capturedExpression; + + std::string actualMessage = Catch::translateActiveException(); + if( !matcher.match( actualMessage ) ) { + data.resultType = ResultWas::ExpressionFailed; + data.reconstructedExpression = actualMessage; + } + AssertionResult result( m_assertionInfo, data ); + handleResult( result ); + } + + void ResultBuilder::captureExpression() { + AssertionResult result = build(); + handleResult( result ); + } + void ResultBuilder::handleResult( AssertionResult const& result ) + { + getResultCapture().assertionEnded( result ); + + if( !result.isOk() ) { + if( getCurrentContext().getConfig()->shouldDebugBreak() ) + m_shouldDebugBreak = true; + if( getCurrentContext().getRunner()->aborting() || (m_assertionInfo.resultDisposition & ResultDisposition::Normal) ) + m_shouldThrow = true; + } + } + void ResultBuilder::react() { + if( m_shouldThrow ) + throw Catch::TestFailureException(); + } + + bool ResultBuilder::shouldDebugBreak() const { return m_shouldDebugBreak; } + bool ResultBuilder::allowThrows() const { return getCurrentContext().getConfig()->allowThrows(); } + + AssertionResult ResultBuilder::build() const + { + assert( m_data.resultType != ResultWas::Unknown ); + + AssertionResultData data = m_data; + + // Flip bool results if testFalse is set + if( m_exprComponents.testFalse ) { + if( data.resultType == ResultWas::Ok ) + data.resultType = ResultWas::ExpressionFailed; + else if( data.resultType == ResultWas::ExpressionFailed ) + data.resultType = ResultWas::Ok; + } + + data.message = m_stream.oss.str(); + data.reconstructedExpression = reconstructExpression(); + if( m_exprComponents.testFalse ) { + if( m_exprComponents.op == "" ) + data.reconstructedExpression = "!" + data.reconstructedExpression; + else + data.reconstructedExpression = "!(" + data.reconstructedExpression + ")"; + } + return AssertionResult( m_assertionInfo, data ); + } + std::string ResultBuilder::reconstructExpression() const { + if( m_exprComponents.op == "" ) + return m_exprComponents.lhs.empty() ? m_assertionInfo.capturedExpression : m_exprComponents.op + m_exprComponents.lhs; + else if( m_exprComponents.op == "matches" ) + return m_exprComponents.lhs + " " + m_exprComponents.rhs; + else if( m_exprComponents.op != "!" ) { + if( m_exprComponents.lhs.size() + m_exprComponents.rhs.size() < 40 && + m_exprComponents.lhs.find("\n") == std::string::npos && + m_exprComponents.rhs.find("\n") == std::string::npos ) + return m_exprComponents.lhs + " " + m_exprComponents.op + " " + m_exprComponents.rhs; + else + return m_exprComponents.lhs + "\n" + m_exprComponents.op + "\n" + m_exprComponents.rhs; + } + else + return "{can't expand - use " + m_assertionInfo.macroName + "_FALSE( " + m_assertionInfo.capturedExpression.substr(1) + " ) instead of " + m_assertionInfo.macroName + "( " + m_assertionInfo.capturedExpression + " ) for better diagnostics}"; + } + +} // end namespace Catch + +// #included from: catch_tag_alias_registry.hpp +#define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_HPP_INCLUDED + +// #included from: catch_tag_alias_registry.h +#define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_H_INCLUDED + +#include + +namespace Catch { + + class TagAliasRegistry : public ITagAliasRegistry { + public: + virtual ~TagAliasRegistry(); + virtual Option find( std::string const& alias ) const; + virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const; + void add( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); + static TagAliasRegistry& get(); + + private: + std::map m_registry; + }; + +} // end namespace Catch + +#include +#include + +namespace Catch { + + TagAliasRegistry::~TagAliasRegistry() {} + + Option TagAliasRegistry::find( std::string const& alias ) const { + std::map::const_iterator it = m_registry.find( alias ); + if( it != m_registry.end() ) + return it->second; + else + return Option(); + } + + std::string TagAliasRegistry::expandAliases( std::string const& unexpandedTestSpec ) const { + std::string expandedTestSpec = unexpandedTestSpec; + for( std::map::const_iterator it = m_registry.begin(), itEnd = m_registry.end(); + it != itEnd; + ++it ) { + std::size_t pos = expandedTestSpec.find( it->first ); + if( pos != std::string::npos ) { + expandedTestSpec = expandedTestSpec.substr( 0, pos ) + + it->second.tag + + expandedTestSpec.substr( pos + it->first.size() ); + } + } + return expandedTestSpec; + } + + void TagAliasRegistry::add( char const* alias, char const* tag, SourceLineInfo const& lineInfo ) { + + if( !startsWith( alias, "[@" ) || !endsWith( alias, "]" ) ) { + std::ostringstream oss; + oss << "error: tag alias, \"" << alias << "\" is not of the form [@alias name].\n" << lineInfo; + throw std::domain_error( oss.str().c_str() ); + } + if( !m_registry.insert( std::make_pair( alias, TagAlias( tag, lineInfo ) ) ).second ) { + std::ostringstream oss; + oss << "error: tag alias, \"" << alias << "\" already registered.\n" + << "\tFirst seen at " << find(alias)->lineInfo << "\n" + << "\tRedefined at " << lineInfo; + throw std::domain_error( oss.str().c_str() ); + } + } + + TagAliasRegistry& TagAliasRegistry::get() { + static TagAliasRegistry instance; + return instance; + + } + + ITagAliasRegistry::~ITagAliasRegistry() {} + ITagAliasRegistry const& ITagAliasRegistry::get() { return TagAliasRegistry::get(); } + + RegistrarForTagAliases::RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ) { + try { + TagAliasRegistry::get().add( alias, tag, lineInfo ); + } + catch( std::exception& ex ) { + Colour colourGuard( Colour::Red ); + Catch::cerr() << ex.what() << std::endl; + exit(1); + } + } + +} // end namespace Catch + +// #included from: ../reporters/catch_reporter_multi.hpp +#define TWOBLUECUBES_CATCH_REPORTER_MULTI_HPP_INCLUDED + +namespace Catch { + +class MultipleReporters : public SharedImpl { + typedef std::vector > Reporters; + Reporters m_reporters; + +public: + void add( Ptr const& reporter ) { + m_reporters.push_back( reporter ); + } + +public: // IStreamingReporter + + virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE { + return m_reporters[0]->getPreferences(); + } + + virtual void noMatchingTestCases( std::string const& spec ) CATCH_OVERRIDE { + for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); + it != itEnd; + ++it ) + (*it)->noMatchingTestCases( spec ); + } + + virtual void testRunStarting( TestRunInfo const& testRunInfo ) CATCH_OVERRIDE { + for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); + it != itEnd; + ++it ) + (*it)->testRunStarting( testRunInfo ); + } + + virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE { + for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); + it != itEnd; + ++it ) + (*it)->testGroupStarting( groupInfo ); + } + + virtual void testCaseStarting( TestCaseInfo const& testInfo ) CATCH_OVERRIDE { + for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); + it != itEnd; + ++it ) + (*it)->testCaseStarting( testInfo ); + } + + virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE { + for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); + it != itEnd; + ++it ) + (*it)->sectionStarting( sectionInfo ); + } + + virtual void assertionStarting( AssertionInfo const& assertionInfo ) CATCH_OVERRIDE { + for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); + it != itEnd; + ++it ) + (*it)->assertionStarting( assertionInfo ); + } + + // The return value indicates if the messages buffer should be cleared: + virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE { + bool clearBuffer = false; + for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); + it != itEnd; + ++it ) + clearBuffer |= (*it)->assertionEnded( assertionStats ); + return clearBuffer; + } + + virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE { + for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); + it != itEnd; + ++it ) + (*it)->sectionEnded( sectionStats ); + } + + virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE { + for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); + it != itEnd; + ++it ) + (*it)->testCaseEnded( testCaseStats ); + } + + virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE { + for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); + it != itEnd; + ++it ) + (*it)->testGroupEnded( testGroupStats ); + } + + virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE { + for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); + it != itEnd; + ++it ) + (*it)->testRunEnded( testRunStats ); + } + + virtual void skipTest( TestCaseInfo const& testInfo ) CATCH_OVERRIDE { + for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); + it != itEnd; + ++it ) + (*it)->skipTest( testInfo ); + } +}; + +Ptr addReporter( Ptr const& existingReporter, Ptr const& additionalReporter ) { + Ptr resultingReporter; + + if( existingReporter ) { + MultipleReporters* multi = dynamic_cast( existingReporter.get() ); + if( !multi ) { + multi = new MultipleReporters; + resultingReporter = Ptr( multi ); + if( existingReporter ) + multi->add( existingReporter ); + } + else + resultingReporter = existingReporter; + multi->add( additionalReporter ); + } + else + resultingReporter = additionalReporter; + + return resultingReporter; +} + +} // end namespace Catch + +// #included from: ../reporters/catch_reporter_xml.hpp +#define TWOBLUECUBES_CATCH_REPORTER_XML_HPP_INCLUDED + +// #included from: catch_reporter_bases.hpp +#define TWOBLUECUBES_CATCH_REPORTER_BASES_HPP_INCLUDED + +#include + +namespace Catch { + + struct StreamingReporterBase : SharedImpl { + + StreamingReporterBase( ReporterConfig const& _config ) + : m_config( _config.fullConfig() ), + stream( _config.stream() ) + { + m_reporterPrefs.shouldRedirectStdOut = false; + } + + virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE { + return m_reporterPrefs; + } + + virtual ~StreamingReporterBase() CATCH_OVERRIDE; + + virtual void noMatchingTestCases( std::string const& ) CATCH_OVERRIDE {} + + virtual void testRunStarting( TestRunInfo const& _testRunInfo ) CATCH_OVERRIDE { + currentTestRunInfo = _testRunInfo; + } + virtual void testGroupStarting( GroupInfo const& _groupInfo ) CATCH_OVERRIDE { + currentGroupInfo = _groupInfo; + } + + virtual void testCaseStarting( TestCaseInfo const& _testInfo ) CATCH_OVERRIDE { + currentTestCaseInfo = _testInfo; + } + virtual void sectionStarting( SectionInfo const& _sectionInfo ) CATCH_OVERRIDE { + m_sectionStack.push_back( _sectionInfo ); + } + + virtual void sectionEnded( SectionStats const& /* _sectionStats */ ) CATCH_OVERRIDE { + m_sectionStack.pop_back(); + } + virtual void testCaseEnded( TestCaseStats const& /* _testCaseStats */ ) CATCH_OVERRIDE { + currentTestCaseInfo.reset(); + } + virtual void testGroupEnded( TestGroupStats const& /* _testGroupStats */ ) CATCH_OVERRIDE { + currentGroupInfo.reset(); + } + virtual void testRunEnded( TestRunStats const& /* _testRunStats */ ) CATCH_OVERRIDE { + currentTestCaseInfo.reset(); + currentGroupInfo.reset(); + currentTestRunInfo.reset(); + } + + virtual void skipTest( TestCaseInfo const& ) CATCH_OVERRIDE { + // Don't do anything with this by default. + // It can optionally be overridden in the derived class. + } + + Ptr m_config; + std::ostream& stream; + + LazyStat currentTestRunInfo; + LazyStat currentGroupInfo; + LazyStat currentTestCaseInfo; + + std::vector m_sectionStack; + ReporterPreferences m_reporterPrefs; + }; + + struct CumulativeReporterBase : SharedImpl { + template + struct Node : SharedImpl<> { + explicit Node( T const& _value ) : value( _value ) {} + virtual ~Node() {} + + typedef std::vector > ChildNodes; + T value; + ChildNodes children; + }; + struct SectionNode : SharedImpl<> { + explicit SectionNode( SectionStats const& _stats ) : stats( _stats ) {} + virtual ~SectionNode(); + + bool operator == ( SectionNode const& other ) const { + return stats.sectionInfo.lineInfo == other.stats.sectionInfo.lineInfo; + } + bool operator == ( Ptr const& other ) const { + return operator==( *other ); + } + + SectionStats stats; + typedef std::vector > ChildSections; + typedef std::vector Assertions; + ChildSections childSections; + Assertions assertions; + std::string stdOut; + std::string stdErr; + }; + + struct BySectionInfo { + BySectionInfo( SectionInfo const& other ) : m_other( other ) {} + BySectionInfo( BySectionInfo const& other ) : m_other( other.m_other ) {} + bool operator() ( Ptr const& node ) const { + return node->stats.sectionInfo.lineInfo == m_other.lineInfo; + } + private: + void operator=( BySectionInfo const& ); + SectionInfo const& m_other; + }; + + typedef Node TestCaseNode; + typedef Node TestGroupNode; + typedef Node TestRunNode; + + CumulativeReporterBase( ReporterConfig const& _config ) + : m_config( _config.fullConfig() ), + stream( _config.stream() ) + { + m_reporterPrefs.shouldRedirectStdOut = false; + } + ~CumulativeReporterBase(); + + virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE { + return m_reporterPrefs; + } + + virtual void testRunStarting( TestRunInfo const& ) CATCH_OVERRIDE {} + virtual void testGroupStarting( GroupInfo const& ) CATCH_OVERRIDE {} + + virtual void testCaseStarting( TestCaseInfo const& ) CATCH_OVERRIDE {} + + virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE { + SectionStats incompleteStats( sectionInfo, Counts(), 0, false ); + Ptr node; + if( m_sectionStack.empty() ) { + if( !m_rootSection ) + m_rootSection = new SectionNode( incompleteStats ); + node = m_rootSection; + } + else { + SectionNode& parentNode = *m_sectionStack.back(); + SectionNode::ChildSections::const_iterator it = + std::find_if( parentNode.childSections.begin(), + parentNode.childSections.end(), + BySectionInfo( sectionInfo ) ); + if( it == parentNode.childSections.end() ) { + node = new SectionNode( incompleteStats ); + parentNode.childSections.push_back( node ); + } + else + node = *it; + } + m_sectionStack.push_back( node ); + m_deepestSection = node; + } + + virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE {} + + virtual bool assertionEnded( AssertionStats const& assertionStats ) { + assert( !m_sectionStack.empty() ); + SectionNode& sectionNode = *m_sectionStack.back(); + sectionNode.assertions.push_back( assertionStats ); + return true; + } + virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE { + assert( !m_sectionStack.empty() ); + SectionNode& node = *m_sectionStack.back(); + node.stats = sectionStats; + m_sectionStack.pop_back(); + } + virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE { + Ptr node = new TestCaseNode( testCaseStats ); + assert( m_sectionStack.size() == 0 ); + node->children.push_back( m_rootSection ); + m_testCases.push_back( node ); + m_rootSection.reset(); + + assert( m_deepestSection ); + m_deepestSection->stdOut = testCaseStats.stdOut; + m_deepestSection->stdErr = testCaseStats.stdErr; + } + virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE { + Ptr node = new TestGroupNode( testGroupStats ); + node->children.swap( m_testCases ); + m_testGroups.push_back( node ); + } + virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE { + Ptr node = new TestRunNode( testRunStats ); + node->children.swap( m_testGroups ); + m_testRuns.push_back( node ); + testRunEndedCumulative(); + } + virtual void testRunEndedCumulative() = 0; + + virtual void skipTest( TestCaseInfo const& ) CATCH_OVERRIDE {} + + Ptr m_config; + std::ostream& stream; + std::vector m_assertions; + std::vector > > m_sections; + std::vector > m_testCases; + std::vector > m_testGroups; + + std::vector > m_testRuns; + + Ptr m_rootSection; + Ptr m_deepestSection; + std::vector > m_sectionStack; + ReporterPreferences m_reporterPrefs; + + }; + + template + char const* getLineOfChars() { + static char line[CATCH_CONFIG_CONSOLE_WIDTH] = {0}; + if( !*line ) { + memset( line, C, CATCH_CONFIG_CONSOLE_WIDTH-1 ); + line[CATCH_CONFIG_CONSOLE_WIDTH-1] = 0; + } + return line; + } + + struct TestEventListenerBase : StreamingReporterBase { + TestEventListenerBase( ReporterConfig const& _config ) + : StreamingReporterBase( _config ) + {} + + virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE {} + virtual bool assertionEnded( AssertionStats const& ) CATCH_OVERRIDE { + return false; + } + }; + +} // end namespace Catch + +// #included from: ../internal/catch_reporter_registrars.hpp +#define TWOBLUECUBES_CATCH_REPORTER_REGISTRARS_HPP_INCLUDED + +namespace Catch { + + template + class LegacyReporterRegistrar { + + class ReporterFactory : public IReporterFactory { + virtual IStreamingReporter* create( ReporterConfig const& config ) const { + return new LegacyReporterAdapter( new T( config ) ); + } + + virtual std::string getDescription() const { + return T::getDescription(); + } + }; + + public: + + LegacyReporterRegistrar( std::string const& name ) { + getMutableRegistryHub().registerReporter( name, new ReporterFactory() ); + } + }; + + template + class ReporterRegistrar { + + class ReporterFactory : public SharedImpl { + + // *** Please Note ***: + // - If you end up here looking at a compiler error because it's trying to register + // your custom reporter class be aware that the native reporter interface has changed + // to IStreamingReporter. The "legacy" interface, IReporter, is still supported via + // an adapter. Just use REGISTER_LEGACY_REPORTER to take advantage of the adapter. + // However please consider updating to the new interface as the old one is now + // deprecated and will probably be removed quite soon! + // Please contact me via github if you have any questions at all about this. + // In fact, ideally, please contact me anyway to let me know you've hit this - as I have + // no idea who is actually using custom reporters at all (possibly no-one!). + // The new interface is designed to minimise exposure to interface changes in the future. + virtual IStreamingReporter* create( ReporterConfig const& config ) const { + return new T( config ); + } + + virtual std::string getDescription() const { + return T::getDescription(); + } + }; + + public: + + ReporterRegistrar( std::string const& name ) { + getMutableRegistryHub().registerReporter( name, new ReporterFactory() ); + } + }; + + template + class ListenerRegistrar { + + class ListenerFactory : public SharedImpl { + + virtual IStreamingReporter* create( ReporterConfig const& config ) const { + return new T( config ); + } + virtual std::string getDescription() const { + return ""; + } + }; + + public: + + ListenerRegistrar() { + getMutableRegistryHub().registerListener( new ListenerFactory() ); + } + }; +} + +#define INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) \ + namespace{ Catch::LegacyReporterRegistrar catch_internal_RegistrarFor##reporterType( name ); } + +#define INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) \ + namespace{ Catch::ReporterRegistrar catch_internal_RegistrarFor##reporterType( name ); } + +#define INTERNAL_CATCH_REGISTER_LISTENER( listenerType ) \ + namespace{ Catch::ListenerRegistrar catch_internal_RegistrarFor##listenerType; } + +// #included from: ../internal/catch_xmlwriter.hpp +#define TWOBLUECUBES_CATCH_XMLWRITER_HPP_INCLUDED + +#include +#include +#include +#include + +namespace Catch { + + class XmlEncode { + public: + enum ForWhat { ForTextNodes, ForAttributes }; + + XmlEncode( std::string const& str, ForWhat forWhat = ForTextNodes ) + : m_str( str ), + m_forWhat( forWhat ) + {} + + void encodeTo( std::ostream& os ) const { + + // Apostrophe escaping not necessary if we always use " to write attributes + // (see: http://www.w3.org/TR/xml/#syntax) + + for( std::size_t i = 0; i < m_str.size(); ++ i ) { + char c = m_str[i]; + switch( c ) { + case '<': os << "<"; break; + case '&': os << "&"; break; + + case '>': + // See: http://www.w3.org/TR/xml/#syntax + if( i > 2 && m_str[i-1] == ']' && m_str[i-2] == ']' ) + os << ">"; + else + os << c; + break; + + case '\"': + if( m_forWhat == ForAttributes ) + os << """; + else + os << c; + break; + + default: + // Escape control chars - based on contribution by @espenalb in PR #465 + if ( ( c < '\x09' ) || ( c > '\x0D' && c < '\x20') || c=='\x7F' ) + os << "&#x" << std::uppercase << std::hex << static_cast( c ); + else + os << c; + } + } + } + + friend std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ) { + xmlEncode.encodeTo( os ); + return os; + } + + private: + std::string m_str; + ForWhat m_forWhat; + }; + + class XmlWriter { + public: + + class ScopedElement { + public: + ScopedElement( XmlWriter* writer ) + : m_writer( writer ) + {} + + ScopedElement( ScopedElement const& other ) + : m_writer( other.m_writer ){ + other.m_writer = CATCH_NULL; + } + + ~ScopedElement() { + if( m_writer ) + m_writer->endElement(); + } + + ScopedElement& writeText( std::string const& text, bool indent = true ) { + m_writer->writeText( text, indent ); + return *this; + } + + template + ScopedElement& writeAttribute( std::string const& name, T const& attribute ) { + m_writer->writeAttribute( name, attribute ); + return *this; + } + + private: + mutable XmlWriter* m_writer; + }; + + XmlWriter() + : m_tagIsOpen( false ), + m_needsNewline( false ), + m_os( &Catch::cout() ) + {} + + XmlWriter( std::ostream& os ) + : m_tagIsOpen( false ), + m_needsNewline( false ), + m_os( &os ) + {} + + ~XmlWriter() { + while( !m_tags.empty() ) + endElement(); + } + + XmlWriter& startElement( std::string const& name ) { + ensureTagClosed(); + newlineIfNecessary(); + stream() << m_indent << "<" << name; + m_tags.push_back( name ); + m_indent += " "; + m_tagIsOpen = true; + return *this; + } + + ScopedElement scopedElement( std::string const& name ) { + ScopedElement scoped( this ); + startElement( name ); + return scoped; + } + + XmlWriter& endElement() { + newlineIfNecessary(); + m_indent = m_indent.substr( 0, m_indent.size()-2 ); + if( m_tagIsOpen ) { + stream() << "/>\n"; + m_tagIsOpen = false; + } + else { + stream() << m_indent << "\n"; + } + m_tags.pop_back(); + return *this; + } + + XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ) { + if( !name.empty() && !attribute.empty() ) + stream() << " " << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << "\""; + return *this; + } + + XmlWriter& writeAttribute( std::string const& name, bool attribute ) { + stream() << " " << name << "=\"" << ( attribute ? "true" : "false" ) << "\""; + return *this; + } + + template + XmlWriter& writeAttribute( std::string const& name, T const& attribute ) { + std::ostringstream oss; + oss << attribute; + return writeAttribute( name, oss.str() ); + } + + XmlWriter& writeText( std::string const& text, bool indent = true ) { + if( !text.empty() ){ + bool tagWasOpen = m_tagIsOpen; + ensureTagClosed(); + if( tagWasOpen && indent ) + stream() << m_indent; + stream() << XmlEncode( text ); + m_needsNewline = true; + } + return *this; + } + + XmlWriter& writeComment( std::string const& text ) { + ensureTagClosed(); + stream() << m_indent << ""; + m_needsNewline = true; + return *this; + } + + XmlWriter& writeBlankLine() { + ensureTagClosed(); + stream() << "\n"; + return *this; + } + + void setStream( std::ostream& os ) { + m_os = &os; + } + + private: + XmlWriter( XmlWriter const& ); + void operator=( XmlWriter const& ); + + std::ostream& stream() { + return *m_os; + } + + void ensureTagClosed() { + if( m_tagIsOpen ) { + stream() << ">\n"; + m_tagIsOpen = false; + } + } + + void newlineIfNecessary() { + if( m_needsNewline ) { + stream() << "\n"; + m_needsNewline = false; + } + } + + bool m_tagIsOpen; + bool m_needsNewline; + std::vector m_tags; + std::string m_indent; + std::ostream* m_os; + }; + +} +// #included from: catch_reenable_warnings.h + +#define TWOBLUECUBES_CATCH_REENABLE_WARNINGS_H_INCLUDED + +#ifdef __clang__ +# ifdef __ICC // icpc defines the __clang__ macro +# pragma warning(pop) +# else +# pragma clang diagnostic pop +# endif +#elif defined __GNUC__ +# pragma GCC diagnostic pop +#endif + + +namespace Catch { + class XmlReporter : public StreamingReporterBase { + public: + XmlReporter( ReporterConfig const& _config ) + : StreamingReporterBase( _config ), + m_sectionDepth( 0 ) + { + m_reporterPrefs.shouldRedirectStdOut = true; + } + + virtual ~XmlReporter() CATCH_OVERRIDE; + + static std::string getDescription() { + return "Reports test results as an XML document"; + } + + public: // StreamingReporterBase + + virtual void noMatchingTestCases( std::string const& s ) CATCH_OVERRIDE { + StreamingReporterBase::noMatchingTestCases( s ); + } + + virtual void testRunStarting( TestRunInfo const& testInfo ) CATCH_OVERRIDE { + StreamingReporterBase::testRunStarting( testInfo ); + m_xml.setStream( stream ); + m_xml.startElement( "Catch" ); + if( !m_config->name().empty() ) + m_xml.writeAttribute( "name", m_config->name() ); + } + + virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE { + StreamingReporterBase::testGroupStarting( groupInfo ); + m_xml.startElement( "Group" ) + .writeAttribute( "name", groupInfo.name ); + } + + virtual void testCaseStarting( TestCaseInfo const& testInfo ) CATCH_OVERRIDE { + StreamingReporterBase::testCaseStarting(testInfo); + m_xml.startElement( "TestCase" ).writeAttribute( "name", trim( testInfo.name ) ); + + if ( m_config->showDurations() == ShowDurations::Always ) + m_testCaseTimer.start(); + } + + virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE { + StreamingReporterBase::sectionStarting( sectionInfo ); + if( m_sectionDepth++ > 0 ) { + m_xml.startElement( "Section" ) + .writeAttribute( "name", trim( sectionInfo.name ) ) + .writeAttribute( "description", sectionInfo.description ); + } + } + + virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE { } + + virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE { + const AssertionResult& assertionResult = assertionStats.assertionResult; + + // Print any info messages in tags. + if( assertionStats.assertionResult.getResultType() != ResultWas::Ok ) { + for( std::vector::const_iterator it = assertionStats.infoMessages.begin(), itEnd = assertionStats.infoMessages.end(); + it != itEnd; + ++it ) { + if( it->type == ResultWas::Info ) { + m_xml.scopedElement( "Info" ) + .writeText( it->message ); + } else if ( it->type == ResultWas::Warning ) { + m_xml.scopedElement( "Warning" ) + .writeText( it->message ); + } + } + } + + // Drop out if result was successful but we're not printing them. + if( !m_config->includeSuccessfulResults() && isOk(assertionResult.getResultType()) ) + return true; + + // Print the expression if there is one. + if( assertionResult.hasExpression() ) { + m_xml.startElement( "Expression" ) + .writeAttribute( "success", assertionResult.succeeded() ) + .writeAttribute( "type", assertionResult.getTestMacroName() ) + .writeAttribute( "filename", assertionResult.getSourceInfo().file ) + .writeAttribute( "line", assertionResult.getSourceInfo().line ); + + m_xml.scopedElement( "Original" ) + .writeText( assertionResult.getExpression() ); + m_xml.scopedElement( "Expanded" ) + .writeText( assertionResult.getExpandedExpression() ); + } + + // And... Print a result applicable to each result type. + switch( assertionResult.getResultType() ) { + case ResultWas::ThrewException: + m_xml.scopedElement( "Exception" ) + .writeAttribute( "filename", assertionResult.getSourceInfo().file ) + .writeAttribute( "line", assertionResult.getSourceInfo().line ) + .writeText( assertionResult.getMessage() ); + break; + case ResultWas::FatalErrorCondition: + m_xml.scopedElement( "Fatal Error Condition" ) + .writeAttribute( "filename", assertionResult.getSourceInfo().file ) + .writeAttribute( "line", assertionResult.getSourceInfo().line ) + .writeText( assertionResult.getMessage() ); + break; + case ResultWas::Info: + m_xml.scopedElement( "Info" ) + .writeText( assertionResult.getMessage() ); + break; + case ResultWas::Warning: + // Warning will already have been written + break; + case ResultWas::ExplicitFailure: + m_xml.scopedElement( "Failure" ) + .writeText( assertionResult.getMessage() ); + break; + default: + break; + } + + if( assertionResult.hasExpression() ) + m_xml.endElement(); + + return true; + } + + virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE { + StreamingReporterBase::sectionEnded( sectionStats ); + if( --m_sectionDepth > 0 ) { + XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResults" ); + e.writeAttribute( "successes", sectionStats.assertions.passed ); + e.writeAttribute( "failures", sectionStats.assertions.failed ); + e.writeAttribute( "expectedFailures", sectionStats.assertions.failedButOk ); + + if ( m_config->showDurations() == ShowDurations::Always ) + e.writeAttribute( "durationInSeconds", sectionStats.durationInSeconds ); + + m_xml.endElement(); + } + } + + virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE { + StreamingReporterBase::testCaseEnded( testCaseStats ); + XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResult" ); + e.writeAttribute( "success", testCaseStats.totals.assertions.allOk() ); + + if ( m_config->showDurations() == ShowDurations::Always ) + e.writeAttribute( "durationInSeconds", m_testCaseTimer.getElapsedSeconds() ); + + m_xml.endElement(); + } + + virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE { + StreamingReporterBase::testGroupEnded( testGroupStats ); + // TODO: Check testGroupStats.aborting and act accordingly. + m_xml.scopedElement( "OverallResults" ) + .writeAttribute( "successes", testGroupStats.totals.assertions.passed ) + .writeAttribute( "failures", testGroupStats.totals.assertions.failed ) + .writeAttribute( "expectedFailures", testGroupStats.totals.assertions.failedButOk ); + m_xml.endElement(); + } + + virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE { + StreamingReporterBase::testRunEnded( testRunStats ); + m_xml.scopedElement( "OverallResults" ) + .writeAttribute( "successes", testRunStats.totals.assertions.passed ) + .writeAttribute( "failures", testRunStats.totals.assertions.failed ) + .writeAttribute( "expectedFailures", testRunStats.totals.assertions.failedButOk ); + m_xml.endElement(); + } + + private: + Timer m_testCaseTimer; + XmlWriter m_xml; + int m_sectionDepth; + }; + + INTERNAL_CATCH_REGISTER_REPORTER( "xml", XmlReporter ) + +} // end namespace Catch + +// #included from: ../reporters/catch_reporter_junit.hpp +#define TWOBLUECUBES_CATCH_REPORTER_JUNIT_HPP_INCLUDED + +#include + +namespace Catch { + + class JunitReporter : public CumulativeReporterBase { + public: + JunitReporter( ReporterConfig const& _config ) + : CumulativeReporterBase( _config ), + xml( _config.stream() ) + { + m_reporterPrefs.shouldRedirectStdOut = true; + } + + virtual ~JunitReporter() CATCH_OVERRIDE; + + static std::string getDescription() { + return "Reports test results in an XML format that looks like Ant's junitreport target"; + } + + virtual void noMatchingTestCases( std::string const& /*spec*/ ) CATCH_OVERRIDE {} + + virtual void testRunStarting( TestRunInfo const& runInfo ) CATCH_OVERRIDE { + CumulativeReporterBase::testRunStarting( runInfo ); + xml.startElement( "testsuites" ); + } + + virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE { + suiteTimer.start(); + stdOutForSuite.str(""); + stdErrForSuite.str(""); + unexpectedExceptions = 0; + CumulativeReporterBase::testGroupStarting( groupInfo ); + } + + virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE { + if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException ) + unexpectedExceptions++; + return CumulativeReporterBase::assertionEnded( assertionStats ); + } + + virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE { + stdOutForSuite << testCaseStats.stdOut; + stdErrForSuite << testCaseStats.stdErr; + CumulativeReporterBase::testCaseEnded( testCaseStats ); + } + + virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE { + double suiteTime = suiteTimer.getElapsedSeconds(); + CumulativeReporterBase::testGroupEnded( testGroupStats ); + writeGroup( *m_testGroups.back(), suiteTime ); + } + + virtual void testRunEndedCumulative() CATCH_OVERRIDE { + xml.endElement(); + } + + void writeGroup( TestGroupNode const& groupNode, double suiteTime ) { + XmlWriter::ScopedElement e = xml.scopedElement( "testsuite" ); + TestGroupStats const& stats = groupNode.value; + xml.writeAttribute( "name", stats.groupInfo.name ); + xml.writeAttribute( "errors", unexpectedExceptions ); + xml.writeAttribute( "failures", stats.totals.assertions.failed-unexpectedExceptions ); + xml.writeAttribute( "tests", stats.totals.assertions.total() ); + xml.writeAttribute( "hostname", "tbd" ); // !TBD + if( m_config->showDurations() == ShowDurations::Never ) + xml.writeAttribute( "time", "" ); + else + xml.writeAttribute( "time", suiteTime ); + xml.writeAttribute( "timestamp", "tbd" ); // !TBD + + // Write test cases + for( TestGroupNode::ChildNodes::const_iterator + it = groupNode.children.begin(), itEnd = groupNode.children.end(); + it != itEnd; + ++it ) + writeTestCase( **it ); + + xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite.str() ), false ); + xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite.str() ), false ); + } + + void writeTestCase( TestCaseNode const& testCaseNode ) { + TestCaseStats const& stats = testCaseNode.value; + + // All test cases have exactly one section - which represents the + // test case itself. That section may have 0-n nested sections + assert( testCaseNode.children.size() == 1 ); + SectionNode const& rootSection = *testCaseNode.children.front(); + + std::string className = stats.testInfo.className; + + if( className.empty() ) { + if( rootSection.childSections.empty() ) + className = "global"; + } + writeSection( className, "", rootSection ); + } + + void writeSection( std::string const& className, + std::string const& rootName, + SectionNode const& sectionNode ) { + std::string name = trim( sectionNode.stats.sectionInfo.name ); + if( !rootName.empty() ) + name = rootName + "/" + name; + + if( !sectionNode.assertions.empty() || + !sectionNode.stdOut.empty() || + !sectionNode.stdErr.empty() ) { + XmlWriter::ScopedElement e = xml.scopedElement( "testcase" ); + if( className.empty() ) { + xml.writeAttribute( "classname", name ); + xml.writeAttribute( "name", "root" ); + } + else { + xml.writeAttribute( "classname", className ); + xml.writeAttribute( "name", name ); + } + xml.writeAttribute( "time", Catch::toString( sectionNode.stats.durationInSeconds ) ); + + writeAssertions( sectionNode ); + + if( !sectionNode.stdOut.empty() ) + xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), false ); + if( !sectionNode.stdErr.empty() ) + xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), false ); + } + for( SectionNode::ChildSections::const_iterator + it = sectionNode.childSections.begin(), + itEnd = sectionNode.childSections.end(); + it != itEnd; + ++it ) + if( className.empty() ) + writeSection( name, "", **it ); + else + writeSection( className, name, **it ); + } + + void writeAssertions( SectionNode const& sectionNode ) { + for( SectionNode::Assertions::const_iterator + it = sectionNode.assertions.begin(), itEnd = sectionNode.assertions.end(); + it != itEnd; + ++it ) + writeAssertion( *it ); + } + void writeAssertion( AssertionStats const& stats ) { + AssertionResult const& result = stats.assertionResult; + if( !result.isOk() ) { + std::string elementName; + switch( result.getResultType() ) { + case ResultWas::ThrewException: + case ResultWas::FatalErrorCondition: + elementName = "error"; + break; + case ResultWas::ExplicitFailure: + elementName = "failure"; + break; + case ResultWas::ExpressionFailed: + elementName = "failure"; + break; + case ResultWas::DidntThrowException: + elementName = "failure"; + break; + + // We should never see these here: + case ResultWas::Info: + case ResultWas::Warning: + case ResultWas::Ok: + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + elementName = "internalError"; + break; + } + + XmlWriter::ScopedElement e = xml.scopedElement( elementName ); + + xml.writeAttribute( "message", result.getExpandedExpression() ); + xml.writeAttribute( "type", result.getTestMacroName() ); + + std::ostringstream oss; + if( !result.getMessage().empty() ) + oss << result.getMessage() << "\n"; + for( std::vector::const_iterator + it = stats.infoMessages.begin(), + itEnd = stats.infoMessages.end(); + it != itEnd; + ++it ) + if( it->type == ResultWas::Info ) + oss << it->message << "\n"; + + oss << "at " << result.getSourceInfo(); + xml.writeText( oss.str(), false ); + } + } + + XmlWriter xml; + Timer suiteTimer; + std::ostringstream stdOutForSuite; + std::ostringstream stdErrForSuite; + unsigned int unexpectedExceptions; + }; + + INTERNAL_CATCH_REGISTER_REPORTER( "junit", JunitReporter ) + +} // end namespace Catch + +// #included from: ../reporters/catch_reporter_console.hpp +#define TWOBLUECUBES_CATCH_REPORTER_CONSOLE_HPP_INCLUDED + +namespace Catch { + + struct ConsoleReporter : StreamingReporterBase { + ConsoleReporter( ReporterConfig const& _config ) + : StreamingReporterBase( _config ), + m_headerPrinted( false ) + {} + + virtual ~ConsoleReporter() CATCH_OVERRIDE; + static std::string getDescription() { + return "Reports test results as plain lines of text"; + } + + virtual void noMatchingTestCases( std::string const& spec ) CATCH_OVERRIDE { + stream << "No test cases matched '" << spec << "'" << std::endl; + } + + virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE { + } + + virtual bool assertionEnded( AssertionStats const& _assertionStats ) CATCH_OVERRIDE { + AssertionResult const& result = _assertionStats.assertionResult; + + bool printInfoMessages = true; + + // Drop out if result was successful and we're not printing those + if( !m_config->includeSuccessfulResults() && result.isOk() ) { + if( result.getResultType() != ResultWas::Warning ) + return false; + printInfoMessages = false; + } + + lazyPrint(); + + AssertionPrinter printer( stream, _assertionStats, printInfoMessages ); + printer.print(); + stream << std::endl; + return true; + } + + virtual void sectionStarting( SectionInfo const& _sectionInfo ) CATCH_OVERRIDE { + m_headerPrinted = false; + StreamingReporterBase::sectionStarting( _sectionInfo ); + } + virtual void sectionEnded( SectionStats const& _sectionStats ) CATCH_OVERRIDE { + if( _sectionStats.missingAssertions ) { + lazyPrint(); + Colour colour( Colour::ResultError ); + if( m_sectionStack.size() > 1 ) + stream << "\nNo assertions in section"; + else + stream << "\nNo assertions in test case"; + stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl; + } + if( m_headerPrinted ) { + if( m_config->showDurations() == ShowDurations::Always ) + stream << "Completed in " << _sectionStats.durationInSeconds << "s" << std::endl; + m_headerPrinted = false; + } + else { + if( m_config->showDurations() == ShowDurations::Always ) + stream << _sectionStats.sectionInfo.name << " completed in " << _sectionStats.durationInSeconds << "s" << std::endl; + } + StreamingReporterBase::sectionEnded( _sectionStats ); + } + + virtual void testCaseEnded( TestCaseStats const& _testCaseStats ) CATCH_OVERRIDE { + StreamingReporterBase::testCaseEnded( _testCaseStats ); + m_headerPrinted = false; + } + virtual void testGroupEnded( TestGroupStats const& _testGroupStats ) CATCH_OVERRIDE { + if( currentGroupInfo.used ) { + printSummaryDivider(); + stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n"; + printTotals( _testGroupStats.totals ); + stream << "\n" << std::endl; + } + StreamingReporterBase::testGroupEnded( _testGroupStats ); + } + virtual void testRunEnded( TestRunStats const& _testRunStats ) CATCH_OVERRIDE { + printTotalsDivider( _testRunStats.totals ); + printTotals( _testRunStats.totals ); + stream << std::endl; + StreamingReporterBase::testRunEnded( _testRunStats ); + } + + private: + + class AssertionPrinter { + void operator= ( AssertionPrinter const& ); + public: + AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages ) + : stream( _stream ), + stats( _stats ), + result( _stats.assertionResult ), + colour( Colour::None ), + message( result.getMessage() ), + messages( _stats.infoMessages ), + printInfoMessages( _printInfoMessages ) + { + switch( result.getResultType() ) { + case ResultWas::Ok: + colour = Colour::Success; + passOrFail = "PASSED"; + //if( result.hasMessage() ) + if( _stats.infoMessages.size() == 1 ) + messageLabel = "with message"; + if( _stats.infoMessages.size() > 1 ) + messageLabel = "with messages"; + break; + case ResultWas::ExpressionFailed: + if( result.isOk() ) { + colour = Colour::Success; + passOrFail = "FAILED - but was ok"; + } + else { + colour = Colour::Error; + passOrFail = "FAILED"; + } + if( _stats.infoMessages.size() == 1 ) + messageLabel = "with message"; + if( _stats.infoMessages.size() > 1 ) + messageLabel = "with messages"; + break; + case ResultWas::ThrewException: + colour = Colour::Error; + passOrFail = "FAILED"; + messageLabel = "due to unexpected exception with message"; + break; + case ResultWas::FatalErrorCondition: + colour = Colour::Error; + passOrFail = "FAILED"; + messageLabel = "due to a fatal error condition"; + break; + case ResultWas::DidntThrowException: + colour = Colour::Error; + passOrFail = "FAILED"; + messageLabel = "because no exception was thrown where one was expected"; + break; + case ResultWas::Info: + messageLabel = "info"; + break; + case ResultWas::Warning: + messageLabel = "warning"; + break; + case ResultWas::ExplicitFailure: + passOrFail = "FAILED"; + colour = Colour::Error; + if( _stats.infoMessages.size() == 1 ) + messageLabel = "explicitly with message"; + if( _stats.infoMessages.size() > 1 ) + messageLabel = "explicitly with messages"; + break; + // These cases are here to prevent compiler warnings + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + passOrFail = "** internal error **"; + colour = Colour::Error; + break; + } + } + + void print() const { + printSourceInfo(); + if( stats.totals.assertions.total() > 0 ) { + if( result.isOk() ) + stream << "\n"; + printResultType(); + printOriginalExpression(); + printReconstructedExpression(); + } + else { + stream << "\n"; + } + printMessage(); + } + + private: + void printResultType() const { + if( !passOrFail.empty() ) { + Colour colourGuard( colour ); + stream << passOrFail << ":\n"; + } + } + void printOriginalExpression() const { + if( result.hasExpression() ) { + Colour colourGuard( Colour::OriginalExpression ); + stream << " "; + stream << result.getExpressionInMacro(); + stream << "\n"; + } + } + void printReconstructedExpression() const { + if( result.hasExpandedExpression() ) { + stream << "with expansion:\n"; + Colour colourGuard( Colour::ReconstructedExpression ); + stream << Text( result.getExpandedExpression(), TextAttributes().setIndent(2) ) << "\n"; + } + } + void printMessage() const { + if( !messageLabel.empty() ) + stream << messageLabel << ":" << "\n"; + for( std::vector::const_iterator it = messages.begin(), itEnd = messages.end(); + it != itEnd; + ++it ) { + // If this assertion is a warning ignore any INFO messages + if( printInfoMessages || it->type != ResultWas::Info ) + stream << Text( it->message, TextAttributes().setIndent(2) ) << "\n"; + } + } + void printSourceInfo() const { + Colour colourGuard( Colour::FileName ); + stream << result.getSourceInfo() << ": "; + } + + std::ostream& stream; + AssertionStats const& stats; + AssertionResult const& result; + Colour::Code colour; + std::string passOrFail; + std::string messageLabel; + std::string message; + std::vector messages; + bool printInfoMessages; + }; + + void lazyPrint() { + + if( !currentTestRunInfo.used ) + lazyPrintRunInfo(); + if( !currentGroupInfo.used ) + lazyPrintGroupInfo(); + + if( !m_headerPrinted ) { + printTestCaseAndSectionHeader(); + m_headerPrinted = true; + } + } + void lazyPrintRunInfo() { + stream << "\n" << getLineOfChars<'~'>() << "\n"; + Colour colour( Colour::SecondaryText ); + stream << currentTestRunInfo->name + << " is a Catch v" << libraryVersion << " host application.\n" + << "Run with -? for options\n\n"; + + if( m_config->rngSeed() != 0 ) + stream << "Randomness seeded to: " << m_config->rngSeed() << "\n\n"; + + currentTestRunInfo.used = true; + } + void lazyPrintGroupInfo() { + if( !currentGroupInfo->name.empty() && currentGroupInfo->groupsCounts > 1 ) { + printClosedHeader( "Group: " + currentGroupInfo->name ); + currentGroupInfo.used = true; + } + } + void printTestCaseAndSectionHeader() { + assert( !m_sectionStack.empty() ); + printOpenHeader( currentTestCaseInfo->name ); + + if( m_sectionStack.size() > 1 ) { + Colour colourGuard( Colour::Headers ); + + std::vector::const_iterator + it = m_sectionStack.begin()+1, // Skip first section (test case) + itEnd = m_sectionStack.end(); + for( ; it != itEnd; ++it ) + printHeaderString( it->name, 2 ); + } + + SourceLineInfo lineInfo = m_sectionStack.front().lineInfo; + + if( !lineInfo.empty() ){ + stream << getLineOfChars<'-'>() << "\n"; + Colour colourGuard( Colour::FileName ); + stream << lineInfo << "\n"; + } + stream << getLineOfChars<'.'>() << "\n" << std::endl; + } + + void printClosedHeader( std::string const& _name ) { + printOpenHeader( _name ); + stream << getLineOfChars<'.'>() << "\n"; + } + void printOpenHeader( std::string const& _name ) { + stream << getLineOfChars<'-'>() << "\n"; + { + Colour colourGuard( Colour::Headers ); + printHeaderString( _name ); + } + } + + // if string has a : in first line will set indent to follow it on + // subsequent lines + void printHeaderString( std::string const& _string, std::size_t indent = 0 ) { + std::size_t i = _string.find( ": " ); + if( i != std::string::npos ) + i+=2; + else + i = 0; + stream << Text( _string, TextAttributes() + .setIndent( indent+i) + .setInitialIndent( indent ) ) << "\n"; + } + + struct SummaryColumn { + + SummaryColumn( std::string const& _label, Colour::Code _colour ) + : label( _label ), + colour( _colour ) + {} + SummaryColumn addRow( std::size_t count ) { + std::ostringstream oss; + oss << count; + std::string row = oss.str(); + for( std::vector::iterator it = rows.begin(); it != rows.end(); ++it ) { + while( it->size() < row.size() ) + *it = " " + *it; + while( it->size() > row.size() ) + row = " " + row; + } + rows.push_back( row ); + return *this; + } + + std::string label; + Colour::Code colour; + std::vector rows; + + }; + + void printTotals( Totals const& totals ) { + if( totals.testCases.total() == 0 ) { + stream << Colour( Colour::Warning ) << "No tests ran\n"; + } + else if( totals.assertions.total() > 0 && totals.assertions.allPassed() ) { + stream << Colour( Colour::ResultSuccess ) << "All tests passed"; + stream << " (" + << pluralise( totals.assertions.passed, "assertion" ) << " in " + << pluralise( totals.testCases.passed, "test case" ) << ")" + << "\n"; + } + else { + + std::vector columns; + columns.push_back( SummaryColumn( "", Colour::None ) + .addRow( totals.testCases.total() ) + .addRow( totals.assertions.total() ) ); + columns.push_back( SummaryColumn( "passed", Colour::Success ) + .addRow( totals.testCases.passed ) + .addRow( totals.assertions.passed ) ); + columns.push_back( SummaryColumn( "failed", Colour::ResultError ) + .addRow( totals.testCases.failed ) + .addRow( totals.assertions.failed ) ); + columns.push_back( SummaryColumn( "failed as expected", Colour::ResultExpectedFailure ) + .addRow( totals.testCases.failedButOk ) + .addRow( totals.assertions.failedButOk ) ); + + printSummaryRow( "test cases", columns, 0 ); + printSummaryRow( "assertions", columns, 1 ); + } + } + void printSummaryRow( std::string const& label, std::vector const& cols, std::size_t row ) { + for( std::vector::const_iterator it = cols.begin(); it != cols.end(); ++it ) { + std::string value = it->rows[row]; + if( it->label.empty() ) { + stream << label << ": "; + if( value != "0" ) + stream << value; + else + stream << Colour( Colour::Warning ) << "- none -"; + } + else if( value != "0" ) { + stream << Colour( Colour::LightGrey ) << " | "; + stream << Colour( it->colour ) + << value << " " << it->label; + } + } + stream << "\n"; + } + + static std::size_t makeRatio( std::size_t number, std::size_t total ) { + std::size_t ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number/ total : 0; + return ( ratio == 0 && number > 0 ) ? 1 : ratio; + } + static std::size_t& findMax( std::size_t& i, std::size_t& j, std::size_t& k ) { + if( i > j && i > k ) + return i; + else if( j > k ) + return j; + else + return k; + } + + void printTotalsDivider( Totals const& totals ) { + if( totals.testCases.total() > 0 ) { + std::size_t failedRatio = makeRatio( totals.testCases.failed, totals.testCases.total() ); + std::size_t failedButOkRatio = makeRatio( totals.testCases.failedButOk, totals.testCases.total() ); + std::size_t passedRatio = makeRatio( totals.testCases.passed, totals.testCases.total() ); + while( failedRatio + failedButOkRatio + passedRatio < CATCH_CONFIG_CONSOLE_WIDTH-1 ) + findMax( failedRatio, failedButOkRatio, passedRatio )++; + while( failedRatio + failedButOkRatio + passedRatio > CATCH_CONFIG_CONSOLE_WIDTH-1 ) + findMax( failedRatio, failedButOkRatio, passedRatio )--; + + stream << Colour( Colour::Error ) << std::string( failedRatio, '=' ); + stream << Colour( Colour::ResultExpectedFailure ) << std::string( failedButOkRatio, '=' ); + if( totals.testCases.allPassed() ) + stream << Colour( Colour::ResultSuccess ) << std::string( passedRatio, '=' ); + else + stream << Colour( Colour::Success ) << std::string( passedRatio, '=' ); + } + else { + stream << Colour( Colour::Warning ) << std::string( CATCH_CONFIG_CONSOLE_WIDTH-1, '=' ); + } + stream << "\n"; + } + void printSummaryDivider() { + stream << getLineOfChars<'-'>() << "\n"; + } + + private: + bool m_headerPrinted; + }; + + INTERNAL_CATCH_REGISTER_REPORTER( "console", ConsoleReporter ) + +} // end namespace Catch + +// #included from: ../reporters/catch_reporter_compact.hpp +#define TWOBLUECUBES_CATCH_REPORTER_COMPACT_HPP_INCLUDED + +namespace Catch { + + struct CompactReporter : StreamingReporterBase { + + CompactReporter( ReporterConfig const& _config ) + : StreamingReporterBase( _config ) + {} + + virtual ~CompactReporter(); + + static std::string getDescription() { + return "Reports test results on a single line, suitable for IDEs"; + } + + virtual ReporterPreferences getPreferences() const { + ReporterPreferences prefs; + prefs.shouldRedirectStdOut = false; + return prefs; + } + + virtual void noMatchingTestCases( std::string const& spec ) { + stream << "No test cases matched '" << spec << "'" << std::endl; + } + + virtual void assertionStarting( AssertionInfo const& ) { + } + + virtual bool assertionEnded( AssertionStats const& _assertionStats ) { + AssertionResult const& result = _assertionStats.assertionResult; + + bool printInfoMessages = true; + + // Drop out if result was successful and we're not printing those + if( !m_config->includeSuccessfulResults() && result.isOk() ) { + if( result.getResultType() != ResultWas::Warning ) + return false; + printInfoMessages = false; + } + + AssertionPrinter printer( stream, _assertionStats, printInfoMessages ); + printer.print(); + + stream << std::endl; + return true; + } + + virtual void testRunEnded( TestRunStats const& _testRunStats ) { + printTotals( _testRunStats.totals ); + stream << "\n" << std::endl; + StreamingReporterBase::testRunEnded( _testRunStats ); + } + + private: + class AssertionPrinter { + void operator= ( AssertionPrinter const& ); + public: + AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages ) + : stream( _stream ) + , stats( _stats ) + , result( _stats.assertionResult ) + , messages( _stats.infoMessages ) + , itMessage( _stats.infoMessages.begin() ) + , printInfoMessages( _printInfoMessages ) + {} + + void print() { + printSourceInfo(); + + itMessage = messages.begin(); + + switch( result.getResultType() ) { + case ResultWas::Ok: + printResultType( Colour::ResultSuccess, passedString() ); + printOriginalExpression(); + printReconstructedExpression(); + if ( ! result.hasExpression() ) + printRemainingMessages( Colour::None ); + else + printRemainingMessages(); + break; + case ResultWas::ExpressionFailed: + if( result.isOk() ) + printResultType( Colour::ResultSuccess, failedString() + std::string( " - but was ok" ) ); + else + printResultType( Colour::Error, failedString() ); + printOriginalExpression(); + printReconstructedExpression(); + printRemainingMessages(); + break; + case ResultWas::ThrewException: + printResultType( Colour::Error, failedString() ); + printIssue( "unexpected exception with message:" ); + printMessage(); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::FatalErrorCondition: + printResultType( Colour::Error, failedString() ); + printIssue( "fatal error condition with message:" ); + printMessage(); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::DidntThrowException: + printResultType( Colour::Error, failedString() ); + printIssue( "expected exception, got none" ); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::Info: + printResultType( Colour::None, "info" ); + printMessage(); + printRemainingMessages(); + break; + case ResultWas::Warning: + printResultType( Colour::None, "warning" ); + printMessage(); + printRemainingMessages(); + break; + case ResultWas::ExplicitFailure: + printResultType( Colour::Error, failedString() ); + printIssue( "explicitly" ); + printRemainingMessages( Colour::None ); + break; + // These cases are here to prevent compiler warnings + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + printResultType( Colour::Error, "** internal error **" ); + break; + } + } + + private: + // Colour::LightGrey + + static Colour::Code dimColour() { return Colour::FileName; } + +#ifdef CATCH_PLATFORM_MAC + static const char* failedString() { return "FAILED"; } + static const char* passedString() { return "PASSED"; } +#else + static const char* failedString() { return "failed"; } + static const char* passedString() { return "passed"; } +#endif + + void printSourceInfo() const { + Colour colourGuard( Colour::FileName ); + stream << result.getSourceInfo() << ":"; + } + + void printResultType( Colour::Code colour, std::string passOrFail ) const { + if( !passOrFail.empty() ) { + { + Colour colourGuard( colour ); + stream << " " << passOrFail; + } + stream << ":"; + } + } + + void printIssue( std::string issue ) const { + stream << " " << issue; + } + + void printExpressionWas() { + if( result.hasExpression() ) { + stream << ";"; + { + Colour colour( dimColour() ); + stream << " expression was:"; + } + printOriginalExpression(); + } + } + + void printOriginalExpression() const { + if( result.hasExpression() ) { + stream << " " << result.getExpression(); + } + } + + void printReconstructedExpression() const { + if( result.hasExpandedExpression() ) { + { + Colour colour( dimColour() ); + stream << " for: "; + } + stream << result.getExpandedExpression(); + } + } + + void printMessage() { + if ( itMessage != messages.end() ) { + stream << " '" << itMessage->message << "'"; + ++itMessage; + } + } + + void printRemainingMessages( Colour::Code colour = dimColour() ) { + if ( itMessage == messages.end() ) + return; + + // using messages.end() directly yields compilation error: + std::vector::const_iterator itEnd = messages.end(); + const std::size_t N = static_cast( std::distance( itMessage, itEnd ) ); + + { + Colour colourGuard( colour ); + stream << " with " << pluralise( N, "message" ) << ":"; + } + + for(; itMessage != itEnd; ) { + // If this assertion is a warning ignore any INFO messages + if( printInfoMessages || itMessage->type != ResultWas::Info ) { + stream << " '" << itMessage->message << "'"; + if ( ++itMessage != itEnd ) { + Colour colourGuard( dimColour() ); + stream << " and"; + } + } + } + } + + private: + std::ostream& stream; + AssertionStats const& stats; + AssertionResult const& result; + std::vector messages; + std::vector::const_iterator itMessage; + bool printInfoMessages; + }; + + // Colour, message variants: + // - white: No tests ran. + // - red: Failed [both/all] N test cases, failed [both/all] M assertions. + // - white: Passed [both/all] N test cases (no assertions). + // - red: Failed N tests cases, failed M assertions. + // - green: Passed [both/all] N tests cases with M assertions. + + std::string bothOrAll( std::size_t count ) const { + return count == 1 ? "" : count == 2 ? "both " : "all " ; + } + + void printTotals( const Totals& totals ) const { + if( totals.testCases.total() == 0 ) { + stream << "No tests ran."; + } + else if( totals.testCases.failed == totals.testCases.total() ) { + Colour colour( Colour::ResultError ); + const std::string qualify_assertions_failed = + totals.assertions.failed == totals.assertions.total() ? + bothOrAll( totals.assertions.failed ) : ""; + stream << + "Failed " << bothOrAll( totals.testCases.failed ) + << pluralise( totals.testCases.failed, "test case" ) << ", " + "failed " << qualify_assertions_failed << + pluralise( totals.assertions.failed, "assertion" ) << "."; + } + else if( totals.assertions.total() == 0 ) { + stream << + "Passed " << bothOrAll( totals.testCases.total() ) + << pluralise( totals.testCases.total(), "test case" ) + << " (no assertions)."; + } + else if( totals.assertions.failed ) { + Colour colour( Colour::ResultError ); + stream << + "Failed " << pluralise( totals.testCases.failed, "test case" ) << ", " + "failed " << pluralise( totals.assertions.failed, "assertion" ) << "."; + } + else { + Colour colour( Colour::ResultSuccess ); + stream << + "Passed " << bothOrAll( totals.testCases.passed ) + << pluralise( totals.testCases.passed, "test case" ) << + " with " << pluralise( totals.assertions.passed, "assertion" ) << "."; + } + } + }; + + INTERNAL_CATCH_REGISTER_REPORTER( "compact", CompactReporter ) + +} // end namespace Catch + +namespace Catch { + // These are all here to avoid warnings about not having any out of line + // virtual methods + NonCopyable::~NonCopyable() {} + IShared::~IShared() {} + IStream::~IStream() CATCH_NOEXCEPT {} + FileStream::~FileStream() CATCH_NOEXCEPT {} + CoutStream::~CoutStream() CATCH_NOEXCEPT {} + DebugOutStream::~DebugOutStream() CATCH_NOEXCEPT {} + StreamBufBase::~StreamBufBase() CATCH_NOEXCEPT {} + IContext::~IContext() {} + IResultCapture::~IResultCapture() {} + ITestCase::~ITestCase() {} + ITestCaseRegistry::~ITestCaseRegistry() {} + IRegistryHub::~IRegistryHub() {} + IMutableRegistryHub::~IMutableRegistryHub() {} + IExceptionTranslator::~IExceptionTranslator() {} + IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() {} + IReporter::~IReporter() {} + IReporterFactory::~IReporterFactory() {} + IReporterRegistry::~IReporterRegistry() {} + IStreamingReporter::~IStreamingReporter() {} + AssertionStats::~AssertionStats() {} + SectionStats::~SectionStats() {} + TestCaseStats::~TestCaseStats() {} + TestGroupStats::~TestGroupStats() {} + TestRunStats::~TestRunStats() {} + CumulativeReporterBase::SectionNode::~SectionNode() {} + CumulativeReporterBase::~CumulativeReporterBase() {} + + StreamingReporterBase::~StreamingReporterBase() {} + ConsoleReporter::~ConsoleReporter() {} + CompactReporter::~CompactReporter() {} + IRunner::~IRunner() {} + IMutableContext::~IMutableContext() {} + IConfig::~IConfig() {} + XmlReporter::~XmlReporter() {} + JunitReporter::~JunitReporter() {} + TestRegistry::~TestRegistry() {} + FreeFunctionTestCase::~FreeFunctionTestCase() {} + IGeneratorInfo::~IGeneratorInfo() {} + IGeneratorsForTest::~IGeneratorsForTest() {} + WildcardPattern::~WildcardPattern() {} + TestSpec::Pattern::~Pattern() {} + TestSpec::NamePattern::~NamePattern() {} + TestSpec::TagPattern::~TagPattern() {} + TestSpec::ExcludedPattern::~ExcludedPattern() {} + + Matchers::Impl::StdString::Equals::~Equals() {} + Matchers::Impl::StdString::Contains::~Contains() {} + Matchers::Impl::StdString::StartsWith::~StartsWith() {} + Matchers::Impl::StdString::EndsWith::~EndsWith() {} + + void Config::dummy() {} + + namespace TestCaseTracking { + ITracker::~ITracker() {} + TrackerBase::~TrackerBase() {} + SectionTracker::~SectionTracker() {} + IndexTracker::~IndexTracker() {} + } +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#endif + +#ifdef CATCH_CONFIG_MAIN +// #included from: internal/catch_default_main.hpp +#define TWOBLUECUBES_CATCH_DEFAULT_MAIN_HPP_INCLUDED + +#ifndef __OBJC__ + +// Standard C/C++ main entry point +int main (int argc, char * argv[]) { + return Catch::Session().run( argc, argv ); +} + +#else // __OBJC__ + +// Objective-C entry point +int main (int argc, char * const argv[]) { +#if !CATCH_ARC_ENABLED + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; +#endif + + Catch::registerTestMethods(); + int result = Catch::Session().run( argc, (char* const*)argv ); + +#if !CATCH_ARC_ENABLED + [pool drain]; +#endif + + return result; +} + +#endif // __OBJC__ + +#endif + +#ifdef CLARA_CONFIG_MAIN_NOT_DEFINED +# undef CLARA_CONFIG_MAIN +#endif + +////// + +// If this config identifier is defined then all CATCH macros are prefixed with CATCH_ +#ifdef CATCH_CONFIG_PREFIX_ALL + +#define CATCH_REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE" ) +#define CATCH_REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, "CATCH_REQUIRE_FALSE" ) + +#define CATCH_REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, "", "CATCH_REQUIRE_THROWS" ) +#define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THROWS_AS" ) +#define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, matcher, "CATCH_REQUIRE_THROWS_WITH" ) +#define CATCH_REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_NOTHROW" ) + +#define CATCH_CHECK( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK" ) +#define CATCH_CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, "CATCH_CHECK_FALSE" ) +#define CATCH_CHECKED_IF( expr ) INTERNAL_CATCH_IF( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECKED_IF" ) +#define CATCH_CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECKED_ELSE" ) +#define CATCH_CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, "CATCH_CHECK_NOFAIL" ) + +#define CATCH_CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THROWS" ) +#define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THROWS_AS" ) +#define CATCH_CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, matcher, "CATCH_CHECK_THROWS_WITH" ) +#define CATCH_CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_NOTHROW" ) + +#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THAT" ) +#define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THAT" ) + +#define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( msg, "CATCH_INFO" ) +#define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, "CATCH_WARN", msg ) +#define CATCH_SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( msg, "CATCH_INFO" ) +#define CATCH_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CATCH_CAPTURE" ) +#define CATCH_SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CATCH_CAPTURE" ) + +#ifdef CATCH_CONFIG_VARIADIC_MACROS + #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) + #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) + #define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) + #define CATCH_REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) + #define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) + #define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL", __VA_ARGS__ ) + #define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "CATCH_SUCCEED", __VA_ARGS__ ) +#else + #define CATCH_TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description ) + #define CATCH_TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description ) + #define CATCH_METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description ) + #define CATCH_REGISTER_TEST_CASE( function, name, description ) INTERNAL_CATCH_REGISTER_TESTCASE( function, name, description ) + #define CATCH_SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description ) + #define CATCH_FAIL( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL", msg ) + #define CATCH_SUCCEED( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "CATCH_SUCCEED", msg ) +#endif +#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" ) + +#define CATCH_REGISTER_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) +#define CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) + +#define CATCH_GENERATE( expr) INTERNAL_CATCH_GENERATE( expr ) + +// "BDD-style" convenience wrappers +#ifdef CATCH_CONFIG_VARIADIC_MACROS +#define CATCH_SCENARIO( ... ) CATCH_TEST_CASE( "Scenario: " __VA_ARGS__ ) +#define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ ) +#else +#define CATCH_SCENARIO( name, tags ) CATCH_TEST_CASE( "Scenario: " name, tags ) +#define CATCH_SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags ) +#endif +#define CATCH_GIVEN( desc ) CATCH_SECTION( std::string( "Given: ") + desc, "" ) +#define CATCH_WHEN( desc ) CATCH_SECTION( std::string( " When: ") + desc, "" ) +#define CATCH_AND_WHEN( desc ) CATCH_SECTION( std::string( " And: ") + desc, "" ) +#define CATCH_THEN( desc ) CATCH_SECTION( std::string( " Then: ") + desc, "" ) +#define CATCH_AND_THEN( desc ) CATCH_SECTION( std::string( " And: ") + desc, "" ) + +// If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required +#else + +#define REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "REQUIRE" ) +#define REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, "REQUIRE_FALSE" ) + +#define REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, "", "REQUIRE_THROWS" ) +#define REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::Normal, "REQUIRE_THROWS_AS" ) +#define REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, matcher, "REQUIRE_THROWS_WITH" ) +#define REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::Normal, "REQUIRE_NOTHROW" ) + +#define CHECK( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK" ) +#define CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, "CHECK_FALSE" ) +#define CHECKED_IF( expr ) INTERNAL_CATCH_IF( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECKED_IF" ) +#define CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECKED_ELSE" ) +#define CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, "CHECK_NOFAIL" ) + +#define CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, "", "CHECK_THROWS" ) +#define CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THROWS_AS" ) +#define CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, matcher, "CHECK_THROWS_WITH" ) +#define CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK_NOTHROW" ) + +#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THAT" ) +#define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::Normal, "REQUIRE_THAT" ) + +#define INFO( msg ) INTERNAL_CATCH_INFO( msg, "INFO" ) +#define WARN( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, "WARN", msg ) +#define SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( msg, "INFO" ) +#define CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CAPTURE" ) +#define SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CAPTURE" ) + +#ifdef CATCH_CONFIG_VARIADIC_MACROS + #define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) + #define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) + #define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) + #define REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) + #define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) + #define FAIL( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "FAIL", __VA_ARGS__ ) + #define SUCCEED( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED", __VA_ARGS__ ) +#else + #define TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description ) + #define TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description ) + #define METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description ) + #define REGISTER_TEST_CASE( method, name, description ) INTERNAL_CATCH_REGISTER_TESTCASE( method, name, description ) + #define SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description ) + #define FAIL( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "FAIL", msg ) + #define SUCCEED( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED", msg ) +#endif +#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" ) + +#define REGISTER_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) +#define REGISTER_LEGACY_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) + +#define GENERATE( expr) INTERNAL_CATCH_GENERATE( expr ) + +#endif + +#define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) + +// "BDD-style" convenience wrappers +#ifdef CATCH_CONFIG_VARIADIC_MACROS +#define SCENARIO( ... ) TEST_CASE( "Scenario: " __VA_ARGS__ ) +#define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ ) +#else +#define SCENARIO( name, tags ) TEST_CASE( "Scenario: " name, tags ) +#define SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags ) +#endif +#define GIVEN( desc ) SECTION( std::string(" Given: ") + desc, "" ) +#define WHEN( desc ) SECTION( std::string(" When: ") + desc, "" ) +#define AND_WHEN( desc ) SECTION( std::string("And when: ") + desc, "" ) +#define THEN( desc ) SECTION( std::string(" Then: ") + desc, "" ) +#define AND_THEN( desc ) SECTION( std::string(" And: ") + desc, "" ) + +using Catch::Detail::Approx; + +#endif // TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED + diff --git a/lib/base64-1.1.1/library.properties b/lib/base64-1.1.1/library.properties new file mode 100644 index 000000000..245115fdc --- /dev/null +++ b/lib/base64-1.1.1/library.properties @@ -0,0 +1,9 @@ +name=base64 +version=1.1.1 +author=Densaugeo +maintainer=Densaugeo +sentence=Base64 encoder/decoder for arduino repo +paragraph=Uses common web conventions - '+' for 62, '/' for 63, '=' for padding. Note that invalid base64 characters are interpreted as padding. +category=Communication +url=https://github.com/Densaugeo/base64_arduino +architectures=* diff --git a/lib/base64-1.1.1/src/base64.hpp b/lib/base64-1.1.1/src/base64.hpp new file mode 100644 index 000000000..8ca581e07 --- /dev/null +++ b/lib/base64-1.1.1/src/base64.hpp @@ -0,0 +1,195 @@ +/** + * Base64 encoding and decoding of strings. Uses '+' for 62, '/' for 63, '=' for padding + */ + +#ifndef BASE64_H_INCLUDED +#define BASE64_H_INCLUDED + +/* binary_to_base64: + * Description: + * Converts a single byte from a binary value to the corresponding base64 character + * Parameters: + * v - Byte to convert + * Returns: + * ascii code of base64 character. If byte is >= 64, then there is not corresponding base64 character + * and 255 is returned + */ +unsigned char binary_to_base64(unsigned char v); + +/* base64_to_binary: + * Description: + * Converts a single byte from a base64 character to the corresponding binary value + * Parameters: + * c - Base64 character (as ascii code) + * Returns: + * 6-bit binary value + */ +unsigned char base64_to_binary(unsigned char c); + +/* encode_base64_length: + * Description: + * Calculates length of base64 string needed for a given number of binary bytes + * Parameters: + * input_length - Amount of binary data in bytes + * Returns: + * Number of base64 characters needed to encode input_length bytes of binary data + */ +unsigned int encode_base64_length(unsigned int input_length); + +/* decode_base64_length: + * Description: + * Calculates number of bytes of binary data in a base64 string + * Parameters: + * input - Base64-encoded null-terminated string + * Returns: + * Number of bytes of binary data in input + */ +unsigned int decode_base64_length(unsigned char input[]); + +/* encode_base64: + * Description: + * Converts an array of bytes to a base64 null-terminated string + * Parameters: + * input - Pointer to input data + * input_length - Number of bytes to read from input pointer + * output - Pointer to output string. Null terminator will be added automatically + * Returns: + * Length of encoded string in bytes (not including null terminator) + */ +unsigned int encode_base64(unsigned char input[], unsigned int input_length, unsigned char output[]); + +/* decode_base64: + * Description: + * Converts a base64 null-terminated string to an array of bytes + * Parameters: + * input - Pointer to input string + * output - Pointer to output array + * Returns: + * Number of bytes in the decoded binary + */ +unsigned int decode_base64(unsigned char input[], unsigned char output[]); + +unsigned char binary_to_base64(unsigned char v) { + // Capital letters - 'A' is ascii 65 and base64 0 + if(v < 26) return v + 'A'; + + // Lowercase letters - 'a' is ascii 97 and base64 26 + if(v < 52) return v + 71; + + // Digits - '0' is ascii 48 and base64 52 + if(v < 62) return v - 4; + + // '+' is ascii 43 and base64 62 + if(v == 62) return '+'; + + // '/' is ascii 47 and base64 63 + if(v == 63) return '/'; + + return 64; +} + +unsigned char base64_to_binary(unsigned char c) { + // Capital letters - 'A' is ascii 65 and base64 0 + if('A' <= c && c <= 'Z') return c - 'A'; + + // Lowercase letters - 'a' is ascii 97 and base64 26 + if('a' <= c && c <= 'z') return c - 71; + + // Digits - '0' is ascii 48 and base64 52 + if('0' <= c && c <= '9') return c + 4; + + // '+' is ascii 43 and base64 62 + if(c == '+') return 62; + + // '/' is ascii 47 and base64 63 + if(c == '/') return 63; + + return 255; +} + +unsigned int encode_base64_length(unsigned int input_length) { + return (input_length + 2)/3*4; +} + +unsigned int decode_base64_length(unsigned char input[]) { + unsigned char *start = input; + + while(base64_to_binary(input[0]) < 64) { + ++input; + } + + unsigned int input_length = input - start; + + unsigned int output_length = input_length/4*3; + + switch(input_length % 4) { + default: return output_length; + case 2: return output_length + 1; + case 3: return output_length + 2; + } +} + +unsigned int encode_base64(unsigned char input[], unsigned int input_length, unsigned char output[]) { + unsigned int full_sets = input_length/3; + + // While there are still full sets of 24 bits... + for(unsigned int i = 0; i < full_sets; ++i) { + output[0] = binary_to_base64( input[0] >> 2); + output[1] = binary_to_base64((input[0] & 0x03) << 4 | input[1] >> 4); + output[2] = binary_to_base64((input[1] & 0x0F) << 2 | input[2] >> 6); + output[3] = binary_to_base64( input[2] & 0x3F); + + input += 3; + output += 4; + } + + switch(input_length % 3) { + case 0: + output[0] = '\0'; + break; + case 1: + output[0] = binary_to_base64( input[0] >> 2); + output[1] = binary_to_base64((input[0] & 0x03) << 4); + output[2] = '='; + output[3] = '='; + output[4] = '\0'; + break; + case 2: + output[0] = binary_to_base64( input[0] >> 2); + output[1] = binary_to_base64((input[0] & 0x03) << 4 | input[1] >> 4); + output[2] = binary_to_base64((input[1] & 0x0F) << 2); + output[3] = '='; + output[4] = '\0'; + break; + } + + return encode_base64_length(input_length); +} + +unsigned int decode_base64(unsigned char input[], unsigned char output[]) { + unsigned int output_length = decode_base64_length(input); + + // While there are still full sets of 24 bits... + for(unsigned int i = 2; i < output_length; i += 3) { + output[0] = base64_to_binary(input[0]) << 2 | base64_to_binary(input[1]) >> 4; + output[1] = base64_to_binary(input[1]) << 4 | base64_to_binary(input[2]) >> 2; + output[2] = base64_to_binary(input[2]) << 6 | base64_to_binary(input[3]); + + input += 4; + output += 3; + } + + switch(output_length % 3) { + case 1: + output[0] = base64_to_binary(input[0]) << 2 | base64_to_binary(input[1]) >> 4; + break; + case 2: + output[0] = base64_to_binary(input[0]) << 2 | base64_to_binary(input[1]) >> 4; + output[1] = base64_to_binary(input[1]) << 4 | base64_to_binary(input[2]) >> 2; + break; + } + + return output_length; +} + +#endif // ifndef diff --git a/lib/bearssl-esp8266/src/int/i15_montmul.c b/lib/bearssl-esp8266/src/int/i15_montmul.c index 9d6f70a49..b2206f587 100644 --- a/lib/bearssl-esp8266/src/int/i15_montmul.c +++ b/lib/bearssl-esp8266/src/int/i15_montmul.c @@ -141,8 +141,8 @@ loop%=: \n\ py = &y[0]; // addresses of both arrays that will be scanned as uint16_t pm = &m[0]; - int py_unaligned = (((int)py) & 2) != 0; - int pm_unaligned = (((int)pm) & 2) != 0; + int py_unaligned = (((intptr_t)py) & 2) != 0; + int pm_unaligned = (((intptr_t)pm) & 2) != 0; uint32_t ty, tm; // 32 bits buffers if (!py_unaligned && !pm_unaligned) { // both are aligned to 32 bits, we always skip the first 16 bits diff --git a/lib/bearssl-esp8266/src/x509/skey_decoder.c b/lib/bearssl-esp8266/src/x509/skey_decoder.c index 192abcaa0..47adab0eb 100644 --- a/lib/bearssl-esp8266/src/x509/skey_decoder.c +++ b/lib/bearssl-esp8266/src/x509/skey_decoder.c @@ -158,7 +158,7 @@ static const unsigned char t0_codeblock[] PROGMEM = { 0x02, 0x06, 0x1E, 0x00, 0x00, 0x19, 0x19, 0x00, 0x00, 0x01, 0x0B, 0x00, 0x00, 0x01, 0x00, 0x20, 0x14, 0x06, 0x08, 0x01, 0x01, 0x21, 0x20, 0x22, 0x20, 0x04, 0x75, 0x13, 0x00, 0x00, 0x01, - T0_INT2(3 * BR_X509_BUFSIZE_KEY), 0x00, 0x01, 0x01, 0x87, 0xFF, 0xFF, + T0_INT2(3 * BR_X509_BUFSIZE_SIG), 0x00, 0x01, 0x01, 0x87, 0xFF, 0xFF, 0x7F, 0x54, 0x57, 0x01, 0x02, 0x3E, 0x55, 0x01, 0x01, 0x0E, 0x06, 0x02, 0x30, 0x16, 0x57, 0x01, 0x02, 0x19, 0x0D, 0x06, 0x06, 0x13, 0x3B, 0x44, 0x32, 0x04, 0x1C, 0x01, 0x04, 0x19, 0x0D, 0x06, 0x08, 0x13, 0x3B, 0x01, diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.0/src/epdpaint.cpp b/lib/esp-epaper-29-ws-20171230-gemu-1.0/src/epdpaint.cpp deleted file mode 100644 index cd04dc8db..000000000 --- a/lib/esp-epaper-29-ws-20171230-gemu-1.0/src/epdpaint.cpp +++ /dev/null @@ -1,322 +0,0 @@ -/** - * @filename : epdpaint.cpp - * @brief : Paint tools - * @author : Yehui from Waveshare - * - * Copyright (C) Waveshare September 9 2017 - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documnetation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include -#include "epdpaint.h" - -Paint::Paint(unsigned char* image, int width, int height) { - this->rotate = ROTATE_0; - this->image = image; - /* 1 byte = 8 pixels, so the width should be the multiple of 8 */ - this->width = width % 8 ? width + 8 - (width % 8) : width; - this->height = height; -} - -Paint::~Paint() { -} - -/** - * @brief: clear the image - */ -void Paint::Clear(int colored) { - for (int x = 0; x < this->width; x++) { - for (int y = 0; y < this->height; y++) { - DrawAbsolutePixel(x, y, colored); - } - } -} - -/** - * @brief: this draws a pixel by absolute coordinates. - * this function won't be affected by the rotate parameter. - */ -void Paint::DrawAbsolutePixel(int x, int y, int colored) { - if (x < 0 || x >= this->width || y < 0 || y >= this->height) { - return; - } - if (IF_INVERT_COLOR) { - if (colored) { - image[(x + y * this->width) / 8] |= 0x80 >> (x % 8); - } else { - image[(x + y * this->width) / 8] &= ~(0x80 >> (x % 8)); - } - } else { - if (colored) { - image[(x + y * this->width) / 8] &= ~(0x80 >> (x % 8)); - } else { - image[(x + y * this->width) / 8] |= 0x80 >> (x % 8); - } - } -} - -/** - * @brief: Getters and Setters - */ -unsigned char* Paint::GetImage(void) { - return this->image; -} - -int Paint::GetWidth(void) { - return this->width; -} - -void Paint::SetWidth(int width) { - this->width = width % 8 ? width + 8 - (width % 8) : width; -} - -int Paint::GetHeight(void) { - return this->height; -} - -void Paint::SetHeight(int height) { - this->height = height; -} - -int Paint::GetRotate(void) { - return this->rotate; -} - -void Paint::SetRotate(int rotate){ - this->rotate = rotate; -} - -/** - * @brief: this draws a pixel by the coordinates - */ -void Paint::DrawPixel(int x, int y, int colored) { - int point_temp; - if (this->rotate == ROTATE_0) { - if(x < 0 || x >= this->width || y < 0 || y >= this->height) { - return; - } - DrawAbsolutePixel(x, y, colored); - } else if (this->rotate == ROTATE_90) { - if(x < 0 || x >= this->height || y < 0 || y >= this->width) { - return; - } - point_temp = x; - x = this->width - y; - y = point_temp; - DrawAbsolutePixel(x, y, colored); - } else if (this->rotate == ROTATE_180) { - if(x < 0 || x >= this->width || y < 0 || y >= this->height) { - return; - } - x = this->width - x; - y = this->height - y; - DrawAbsolutePixel(x, y, colored); - } else if (this->rotate == ROTATE_270) { - if(x < 0 || x >= this->height || y < 0 || y >= this->width) { - return; - } - point_temp = x; - x = y; - y = this->height - point_temp; - DrawAbsolutePixel(x, y, colored); - } -} - -/** - * @brief: this draws a charactor on the frame buffer but not refresh - */ -void Paint::DrawCharAt(int x, int y, char ascii_char, sFONT* font, int colored) { - int i, j; - unsigned int char_offset = (ascii_char - ' ') * font->Height * (font->Width / 8 + (font->Width % 8 ? 1 : 0)); - const unsigned char* ptr = &font->table[char_offset]; - - for (j = 0; j < font->Height; j++) { - for (i = 0; i < font->Width; i++) { - if (pgm_read_byte(ptr) & (0x80 >> (i % 8))) { - DrawPixel(x + i, y + j, colored); - } else { - // fill background - DrawPixel(x + i, y + j, 1); - } - if (i % 8 == 7) { - ptr++; - } - } - if (font->Width % 8 != 0) { - ptr++; - } - } -} - -/** -* @brief: this displays a string on the frame buffer but not refresh -*/ -void Paint::DrawStringAt(int x, int y, const char* text, sFONT* font, int colored) { - const char* p_text = text; - unsigned int counter = 0; - int refcolumn = x; - - /* Send the string character by character on EPD */ - while (*p_text != 0) { - /* Display one character on EPD */ - DrawCharAt(refcolumn, y, *p_text, font, colored); - /* Decrement the column position by 16 */ - refcolumn += font->Width; - /* Point on the next character */ - p_text++; - counter++; - } -} - -/** -* @brief: this draws a line on the frame buffer -*/ -void Paint::DrawLine(int x0, int y0, int x1, int y1, int colored) { - /* Bresenham algorithm */ - int dx = x1 - x0 >= 0 ? x1 - x0 : x0 - x1; - int sx = x0 < x1 ? 1 : -1; - int dy = y1 - y0 <= 0 ? y1 - y0 : y0 - y1; - int sy = y0 < y1 ? 1 : -1; - int err = dx + dy; - - while((x0 != x1) && (y0 != y1)) { - DrawPixel(x0, y0 , colored); - if (2 * err >= dy) { - err += dy; - x0 += sx; - } - if (2 * err <= dx) { - err += dx; - y0 += sy; - } - } -} - -/** -* @brief: this draws a horizontal line on the frame buffer -*/ -void Paint::DrawHorizontalLine(int x, int y, int line_width, int colored) { - int i; - for (i = x; i < x + line_width; i++) { - DrawPixel(i, y, colored); - } -} - -/** -* @brief: this draws a vertical line on the frame buffer -*/ -void Paint::DrawVerticalLine(int x, int y, int line_height, int colored) { - int i; - for (i = y; i < y + line_height; i++) { - DrawPixel(x, i, colored); - } -} - -/** -* @brief: this draws a rectangle -*/ -void Paint::DrawRectangle(int x0, int y0, int x1, int y1, int colored) { - int min_x, min_y, max_x, max_y; - min_x = x1 > x0 ? x0 : x1; - max_x = x1 > x0 ? x1 : x0; - min_y = y1 > y0 ? y0 : y1; - max_y = y1 > y0 ? y1 : y0; - - DrawHorizontalLine(min_x, min_y, max_x - min_x + 1, colored); - DrawHorizontalLine(min_x, max_y, max_x - min_x + 1, colored); - DrawVerticalLine(min_x, min_y, max_y - min_y + 1, colored); - DrawVerticalLine(max_x, min_y, max_y - min_y + 1, colored); -} - -/** -* @brief: this draws a filled rectangle -*/ -void Paint::DrawFilledRectangle(int x0, int y0, int x1, int y1, int colored) { - int min_x, min_y, max_x, max_y; - int i; - min_x = x1 > x0 ? x0 : x1; - max_x = x1 > x0 ? x1 : x0; - min_y = y1 > y0 ? y0 : y1; - max_y = y1 > y0 ? y1 : y0; - - for (i = min_x; i <= max_x; i++) { - DrawVerticalLine(i, min_y, max_y - min_y + 1, colored); - } -} - -/** -* @brief: this draws a circle -*/ -void Paint::DrawCircle(int x, int y, int radius, int colored) { - /* Bresenham algorithm */ - int x_pos = -radius; - int y_pos = 0; - int err = 2 - 2 * radius; - int e2; - - do { - DrawPixel(x - x_pos, y + y_pos, colored); - DrawPixel(x + x_pos, y + y_pos, colored); - DrawPixel(x + x_pos, y - y_pos, colored); - DrawPixel(x - x_pos, y - y_pos, colored); - e2 = err; - if (e2 <= y_pos) { - err += ++y_pos * 2 + 1; - if(-x_pos == y_pos && e2 <= x_pos) { - e2 = 0; - } - } - if (e2 > x_pos) { - err += ++x_pos * 2 + 1; - } - } while (x_pos <= 0); -} - -/** -* @brief: this draws a filled circle -*/ -void Paint::DrawFilledCircle(int x, int y, int radius, int colored) { - /* Bresenham algorithm */ - int x_pos = -radius; - int y_pos = 0; - int err = 2 - 2 * radius; - int e2; - - do { - DrawPixel(x - x_pos, y + y_pos, colored); - DrawPixel(x + x_pos, y + y_pos, colored); - DrawPixel(x + x_pos, y - y_pos, colored); - DrawPixel(x - x_pos, y - y_pos, colored); - DrawHorizontalLine(x + x_pos, y + y_pos, 2 * (-x_pos) + 1, colored); - DrawHorizontalLine(x + x_pos, y - y_pos, 2 * (-x_pos) + 1, colored); - e2 = err; - if (e2 <= y_pos) { - err += ++y_pos * 2 + 1; - if(-x_pos == y_pos && e2 <= x_pos) { - e2 = 0; - } - } - if(e2 > x_pos) { - err += ++x_pos * 2 + 1; - } - } while(x_pos <= 0); -} - -/* END OF FILE */ diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.0/src/font12.c b/lib/esp-epaper-29-ws-20171230-gemu-1.0/src/font12.c deleted file mode 100644 index dfaed8be9..000000000 --- a/lib/esp-epaper-29-ws-20171230-gemu-1.0/src/font12.c +++ /dev/null @@ -1,1385 +0,0 @@ -/** - ****************************************************************************** - * @file Font12.c - * @author MCD Application Team - * @version V1.0.0 - * @date 18-February-2014 - * @brief This file provides text Font12 for STM32xx-EVAL's LCD driver. - ****************************************************************************** - * @attention - * - *

© COPYRIGHT(c) 2014 STMicroelectronics

- * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. 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. - * 3. Neither the name of STMicroelectronics nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * 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. - * - ****************************************************************************** - */ - -/* Includes ------------------------------------------------------------------*/ -#include "fonts.h" -#include - -// -// Font data for Courier New 12pt -// - -const uint8_t Font12_Table[] PROGMEM = -{ - // @0 ' ' (7 pixels wide) - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x00, // - - // @12 '!' (7 pixels wide) - 0x00, // - 0x10, // # - 0x10, // # - 0x10, // # - 0x10, // # - 0x10, // # - 0x00, // - 0x00, // - 0x10, // # - 0x00, // - 0x00, // - 0x00, // - - // @24 '"' (7 pixels wide) - 0x00, // - 0x6C, // ## ## - 0x48, // # # - 0x48, // # # - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x00, // - - // @36 '#' (7 pixels wide) - 0x00, // - 0x14, // # # - 0x14, // # # - 0x28, // # # - 0x7C, // ##### - 0x28, // # # - 0x7C, // ##### - 0x28, // # # - 0x50, // # # - 0x50, // # # - 0x00, // - 0x00, // - - // @48 '$' (7 pixels wide) - 0x00, // - 0x10, // # - 0x38, // ### - 0x40, // # - 0x40, // # - 0x38, // ### - 0x48, // # # - 0x70, // ### - 0x10, // # - 0x10, // # - 0x00, // - 0x00, // - - // @60 '%' (7 pixels wide) - 0x00, // - 0x20, // # - 0x50, // # # - 0x20, // # - 0x0C, // ## - 0x70, // ### - 0x08, // # - 0x14, // # # - 0x08, // # - 0x00, // - 0x00, // - 0x00, // - - // @72 '&' (7 pixels wide) - 0x00, // - 0x00, // - 0x00, // - 0x18, // ## - 0x20, // # - 0x20, // # - 0x54, // # # # - 0x48, // # # - 0x34, // ## # - 0x00, // - 0x00, // - 0x00, // - - // @84 ''' (7 pixels wide) - 0x00, // - 0x10, // # - 0x10, // # - 0x10, // # - 0x10, // # - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x00, // - - // @96 '(' (7 pixels wide) - 0x00, // - 0x08, // # - 0x08, // # - 0x10, // # - 0x10, // # - 0x10, // # - 0x10, // # - 0x10, // # - 0x10, // # - 0x08, // # - 0x08, // # - 0x00, // - - // @108 ')' (7 pixels wide) - 0x00, // - 0x20, // # - 0x20, // # - 0x10, // # - 0x10, // # - 0x10, // # - 0x10, // # - 0x10, // # - 0x10, // # - 0x20, // # - 0x20, // # - 0x00, // - - // @120 '*' (7 pixels wide) - 0x00, // - 0x10, // # - 0x7C, // ##### - 0x10, // # - 0x28, // # # - 0x28, // # # - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x00, // - - // @132 '+' (7 pixels wide) - 0x00, // - 0x00, // - 0x10, // # - 0x10, // # - 0x10, // # - 0xFE, // ####### - 0x10, // # - 0x10, // # - 0x10, // # - 0x00, // - 0x00, // - 0x00, // - - // @144 ',' (7 pixels wide) - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x18, // ## - 0x10, // # - 0x30, // ## - 0x20, // # - 0x00, // - - // @156 '-' (7 pixels wide) - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x7C, // ##### - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x00, // - - // @168 '.' (7 pixels wide) - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x30, // ## - 0x30, // ## - 0x00, // - 0x00, // - 0x00, // - - // @180 '/' (7 pixels wide) - 0x00, // - 0x04, // # - 0x04, // # - 0x08, // # - 0x08, // # - 0x10, // # - 0x10, // # - 0x20, // # - 0x20, // # - 0x40, // # - 0x00, // - 0x00, // - - // @192 '0' (7 pixels wide) - 0x00, // - 0x38, // ### - 0x44, // # # - 0x44, // # # - 0x44, // # # - 0x44, // # # - 0x44, // # # - 0x44, // # # - 0x38, // ### - 0x00, // - 0x00, // - 0x00, // - - // @204 '1' (7 pixels wide) - 0x00, // - 0x30, // ## - 0x10, // # - 0x10, // # - 0x10, // # - 0x10, // # - 0x10, // # - 0x10, // # - 0x7C, // ##### - 0x00, // - 0x00, // - 0x00, // - - // @216 '2' (7 pixels wide) - 0x00, // - 0x38, // ### - 0x44, // # # - 0x04, // # - 0x08, // # - 0x10, // # - 0x20, // # - 0x44, // # # - 0x7C, // ##### - 0x00, // - 0x00, // - 0x00, // - - // @228 '3' (7 pixels wide) - 0x00, // - 0x38, // ### - 0x44, // # # - 0x04, // # - 0x18, // ## - 0x04, // # - 0x04, // # - 0x44, // # # - 0x38, // ### - 0x00, // - 0x00, // - 0x00, // - - // @240 '4' (7 pixels wide) - 0x00, // - 0x0C, // ## - 0x14, // # # - 0x14, // # # - 0x24, // # # - 0x44, // # # - 0x7E, // ###### - 0x04, // # - 0x0E, // ### - 0x00, // - 0x00, // - 0x00, // - - // @252 '5' (7 pixels wide) - 0x00, // - 0x3C, // #### - 0x20, // # - 0x20, // # - 0x38, // ### - 0x04, // # - 0x04, // # - 0x44, // # # - 0x38, // ### - 0x00, // - 0x00, // - 0x00, // - - // @264 '6' (7 pixels wide) - 0x00, // - 0x1C, // ### - 0x20, // # - 0x40, // # - 0x78, // #### - 0x44, // # # - 0x44, // # # - 0x44, // # # - 0x38, // ### - 0x00, // - 0x00, // - 0x00, // - - // @276 '7' (7 pixels wide) - 0x00, // - 0x7C, // ##### - 0x44, // # # - 0x04, // # - 0x08, // # - 0x08, // # - 0x08, // # - 0x10, // # - 0x10, // # - 0x00, // - 0x00, // - 0x00, // - - // @288 '8' (7 pixels wide) - 0x00, // - 0x38, // ### - 0x44, // # # - 0x44, // # # - 0x38, // ### - 0x44, // # # - 0x44, // # # - 0x44, // # # - 0x38, // ### - 0x00, // - 0x00, // - 0x00, // - - // @300 '9' (7 pixels wide) - 0x00, // - 0x38, // ### - 0x44, // # # - 0x44, // # # - 0x44, // # # - 0x3C, // #### - 0x04, // # - 0x08, // # - 0x70, // ### - 0x00, // - 0x00, // - 0x00, // - - // @312 ':' (7 pixels wide) - 0x00, // - 0x00, // - 0x00, // - 0x30, // ## - 0x30, // ## - 0x00, // - 0x00, // - 0x30, // ## - 0x30, // ## - 0x00, // - 0x00, // - 0x00, // - - // @324 ';' (7 pixels wide) - 0x00, // - 0x00, // - 0x00, // - 0x18, // ## - 0x18, // ## - 0x00, // - 0x00, // - 0x18, // ## - 0x30, // ## - 0x20, // # - 0x00, // - 0x00, // - - // @336 '<' (7 pixels wide) - 0x00, // - 0x00, // - 0x0C, // ## - 0x10, // # - 0x60, // ## - 0x80, // # - 0x60, // ## - 0x10, // # - 0x0C, // ## - 0x00, // - 0x00, // - 0x00, // - - // @348 '=' (7 pixels wide) - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x7C, // ##### - 0x00, // - 0x7C, // ##### - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x00, // - - // @360 '>' (7 pixels wide) - 0x00, // - 0x00, // - 0xC0, // ## - 0x20, // # - 0x18, // ## - 0x04, // # - 0x18, // ## - 0x20, // # - 0xC0, // ## - 0x00, // - 0x00, // - 0x00, // - - // @372 '?' (7 pixels wide) - 0x00, // - 0x00, // - 0x18, // ## - 0x24, // # # - 0x04, // # - 0x08, // # - 0x10, // # - 0x00, // - 0x30, // ## - 0x00, // - 0x00, // - 0x00, // - - // @384 '@' (7 pixels wide) - 0x38, // ### - 0x44, // # # - 0x44, // # # - 0x4C, // # ## - 0x54, // # # # - 0x54, // # # # - 0x4C, // # ## - 0x40, // # - 0x44, // # # - 0x38, // ### - 0x00, // - 0x00, // - - // @396 'A' (7 pixels wide) - 0x00, // - 0x30, // ## - 0x10, // # - 0x28, // # # - 0x28, // # # - 0x28, // # # - 0x7C, // ##### - 0x44, // # # - 0xEE, // ### ### - 0x00, // - 0x00, // - 0x00, // - - // @408 'B' (7 pixels wide) - 0x00, // - 0xF8, // ##### - 0x44, // # # - 0x44, // # # - 0x78, // #### - 0x44, // # # - 0x44, // # # - 0x44, // # # - 0xF8, // ##### - 0x00, // - 0x00, // - 0x00, // - - // @420 'C' (7 pixels wide) - 0x00, // - 0x3C, // #### - 0x44, // # # - 0x40, // # - 0x40, // # - 0x40, // # - 0x40, // # - 0x44, // # # - 0x38, // ### - 0x00, // - 0x00, // - 0x00, // - - // @432 'D' (7 pixels wide) - 0x00, // - 0xF0, // #### - 0x48, // # # - 0x44, // # # - 0x44, // # # - 0x44, // # # - 0x44, // # # - 0x48, // # # - 0xF0, // #### - 0x00, // - 0x00, // - 0x00, // - - // @444 'E' (7 pixels wide) - 0x00, // - 0xFC, // ###### - 0x44, // # # - 0x50, // # # - 0x70, // ### - 0x50, // # # - 0x40, // # - 0x44, // # # - 0xFC, // ###### - 0x00, // - 0x00, // - 0x00, // - - // @456 'F' (7 pixels wide) - 0x00, // - 0x7E, // ###### - 0x22, // # # - 0x28, // # # - 0x38, // ### - 0x28, // # # - 0x20, // # - 0x20, // # - 0x70, // ### - 0x00, // - 0x00, // - 0x00, // - - // @468 'G' (7 pixels wide) - 0x00, // - 0x3C, // #### - 0x44, // # # - 0x40, // # - 0x40, // # - 0x4E, // # ### - 0x44, // # # - 0x44, // # # - 0x38, // ### - 0x00, // - 0x00, // - 0x00, // - - // @480 'H' (7 pixels wide) - 0x00, // - 0xEE, // ### ### - 0x44, // # # - 0x44, // # # - 0x7C, // ##### - 0x44, // # # - 0x44, // # # - 0x44, // # # - 0xEE, // ### ### - 0x00, // - 0x00, // - 0x00, // - - // @492 'I' (7 pixels wide) - 0x00, // - 0x7C, // ##### - 0x10, // # - 0x10, // # - 0x10, // # - 0x10, // # - 0x10, // # - 0x10, // # - 0x7C, // ##### - 0x00, // - 0x00, // - 0x00, // - - // @504 'J' (7 pixels wide) - 0x00, // - 0x3C, // #### - 0x08, // # - 0x08, // # - 0x08, // # - 0x48, // # # - 0x48, // # # - 0x48, // # # - 0x30, // ## - 0x00, // - 0x00, // - 0x00, // - - // @516 'K' (7 pixels wide) - 0x00, // - 0xEE, // ### ### - 0x44, // # # - 0x48, // # # - 0x50, // # # - 0x70, // ### - 0x48, // # # - 0x44, // # # - 0xE6, // ### ## - 0x00, // - 0x00, // - 0x00, // - - // @528 'L' (7 pixels wide) - 0x00, // - 0x70, // ### - 0x20, // # - 0x20, // # - 0x20, // # - 0x20, // # - 0x24, // # # - 0x24, // # # - 0x7C, // ##### - 0x00, // - 0x00, // - 0x00, // - - // @540 'M' (7 pixels wide) - 0x00, // - 0xEE, // ### ### - 0x6C, // ## ## - 0x6C, // ## ## - 0x54, // # # # - 0x54, // # # # - 0x44, // # # - 0x44, // # # - 0xEE, // ### ### - 0x00, // - 0x00, // - 0x00, // - - // @552 'N' (7 pixels wide) - 0x00, // - 0xEE, // ### ### - 0x64, // ## # - 0x64, // ## # - 0x54, // # # # - 0x54, // # # # - 0x54, // # # # - 0x4C, // # ## - 0xEC, // ### ## - 0x00, // - 0x00, // - 0x00, // - - // @564 'O' (7 pixels wide) - 0x00, // - 0x38, // ### - 0x44, // # # - 0x44, // # # - 0x44, // # # - 0x44, // # # - 0x44, // # # - 0x44, // # # - 0x38, // ### - 0x00, // - 0x00, // - 0x00, // - - // @576 'P' (7 pixels wide) - 0x00, // - 0x78, // #### - 0x24, // # # - 0x24, // # # - 0x24, // # # - 0x38, // ### - 0x20, // # - 0x20, // # - 0x70, // ### - 0x00, // - 0x00, // - 0x00, // - - // @588 'Q' (7 pixels wide) - 0x00, // - 0x38, // ### - 0x44, // # # - 0x44, // # # - 0x44, // # # - 0x44, // # # - 0x44, // # # - 0x44, // # # - 0x38, // ### - 0x1C, // ### - 0x00, // - 0x00, // - - // @600 'R' (7 pixels wide) - 0x00, // - 0xF8, // ##### - 0x44, // # # - 0x44, // # # - 0x44, // # # - 0x78, // #### - 0x48, // # # - 0x44, // # # - 0xE2, // ### # - 0x00, // - 0x00, // - 0x00, // - - // @612 'S' (7 pixels wide) - 0x00, // - 0x34, // ## # - 0x4C, // # ## - 0x40, // # - 0x38, // ### - 0x04, // # - 0x04, // # - 0x64, // ## # - 0x58, // # ## - 0x00, // - 0x00, // - 0x00, // - - // @624 'T' (7 pixels wide) - 0x00, // - 0xFE, // ####### - 0x92, // # # # - 0x10, // # - 0x10, // # - 0x10, // # - 0x10, // # - 0x10, // # - 0x38, // ### - 0x00, // - 0x00, // - 0x00, // - - // @636 'U' (7 pixels wide) - 0x00, // - 0xEE, // ### ### - 0x44, // # # - 0x44, // # # - 0x44, // # # - 0x44, // # # - 0x44, // # # - 0x44, // # # - 0x38, // ### - 0x00, // - 0x00, // - 0x00, // - - // @648 'V' (7 pixels wide) - 0x00, // - 0xEE, // ### ### - 0x44, // # # - 0x44, // # # - 0x28, // # # - 0x28, // # # - 0x28, // # # - 0x10, // # - 0x10, // # - 0x00, // - 0x00, // - 0x00, // - - // @660 'W' (7 pixels wide) - 0x00, // - 0xEE, // ### ### - 0x44, // # # - 0x44, // # # - 0x54, // # # # - 0x54, // # # # - 0x54, // # # # - 0x54, // # # # - 0x28, // # # - 0x00, // - 0x00, // - 0x00, // - - // @672 'X' (7 pixels wide) - 0x00, // - 0xC6, // ## ## - 0x44, // # # - 0x28, // # # - 0x10, // # - 0x10, // # - 0x28, // # # - 0x44, // # # - 0xC6, // ## ## - 0x00, // - 0x00, // - 0x00, // - - // @684 'Y' (7 pixels wide) - 0x00, // - 0xEE, // ### ### - 0x44, // # # - 0x28, // # # - 0x28, // # # - 0x10, // # - 0x10, // # - 0x10, // # - 0x38, // ### - 0x00, // - 0x00, // - 0x00, // - - // @696 'Z' (7 pixels wide) - 0x00, // - 0x7C, // ##### - 0x44, // # # - 0x08, // # - 0x10, // # - 0x10, // # - 0x20, // # - 0x44, // # # - 0x7C, // ##### - 0x00, // - 0x00, // - 0x00, // - - // @708 '[' (7 pixels wide) - 0x00, // - 0x38, // ### - 0x20, // # - 0x20, // # - 0x20, // # - 0x20, // # - 0x20, // # - 0x20, // # - 0x20, // # - 0x20, // # - 0x38, // ### - 0x00, // - - // @720 '\' (7 pixels wide) - 0x00, // - 0x40, // # - 0x20, // # - 0x20, // # - 0x20, // # - 0x10, // # - 0x10, // # - 0x08, // # - 0x08, // # - 0x08, // # - 0x00, // - 0x00, // - - // @732 ']' (7 pixels wide) - 0x00, // - 0x38, // ### - 0x08, // # - 0x08, // # - 0x08, // # - 0x08, // # - 0x08, // # - 0x08, // # - 0x08, // # - 0x08, // # - 0x38, // ### - 0x00, // - - // @744 '^' (7 pixels wide) - 0x00, // - 0x10, // # - 0x10, // # - 0x28, // # # - 0x44, // # # - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x00, // - - // @756 '_' (7 pixels wide) - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0xFE, // ####### - - // @768 '`' (7 pixels wide) - 0x00, // - 0x10, // # - 0x08, // # - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x00, // - - // @780 'a' (7 pixels wide) - 0x00, // - 0x00, // - 0x00, // - 0x38, // ### - 0x44, // # # - 0x3C, // #### - 0x44, // # # - 0x44, // # # - 0x3E, // ##### - 0x00, // - 0x00, // - 0x00, // - - // @792 'b' (7 pixels wide) - 0x00, // - 0xC0, // ## - 0x40, // # - 0x58, // # ## - 0x64, // ## # - 0x44, // # # - 0x44, // # # - 0x44, // # # - 0xF8, // ##### - 0x00, // - 0x00, // - 0x00, // - - // @804 'c' (7 pixels wide) - 0x00, // - 0x00, // - 0x00, // - 0x3C, // #### - 0x44, // # # - 0x40, // # - 0x40, // # - 0x44, // # # - 0x38, // ### - 0x00, // - 0x00, // - 0x00, // - - // @816 'd' (7 pixels wide) - 0x00, // - 0x0C, // ## - 0x04, // # - 0x34, // ## # - 0x4C, // # ## - 0x44, // # # - 0x44, // # # - 0x44, // # # - 0x3E, // ##### - 0x00, // - 0x00, // - 0x00, // - - // @828 'e' (7 pixels wide) - 0x00, // - 0x00, // - 0x00, // - 0x38, // ### - 0x44, // # # - 0x7C, // ##### - 0x40, // # - 0x40, // # - 0x3C, // #### - 0x00, // - 0x00, // - 0x00, // - - // @840 'f' (7 pixels wide) - 0x00, // - 0x1C, // ### - 0x20, // # - 0x7C, // ##### - 0x20, // # - 0x20, // # - 0x20, // # - 0x20, // # - 0x7C, // ##### - 0x00, // - 0x00, // - 0x00, // - - // @852 'g' (7 pixels wide) - 0x00, // - 0x00, // - 0x00, // - 0x36, // ## ## - 0x4C, // # ## - 0x44, // # # - 0x44, // # # - 0x44, // # # - 0x3C, // #### - 0x04, // # - 0x38, // ### - 0x00, // - - // @864 'h' (7 pixels wide) - 0x00, // - 0xC0, // ## - 0x40, // # - 0x58, // # ## - 0x64, // ## # - 0x44, // # # - 0x44, // # # - 0x44, // # # - 0xEE, // ### ### - 0x00, // - 0x00, // - 0x00, // - - // @876 'i' (7 pixels wide) - 0x00, // - 0x10, // # - 0x00, // - 0x70, // ### - 0x10, // # - 0x10, // # - 0x10, // # - 0x10, // # - 0x7C, // ##### - 0x00, // - 0x00, // - 0x00, // - - // @888 'j' (7 pixels wide) - 0x00, // - 0x10, // # - 0x00, // - 0x78, // #### - 0x08, // # - 0x08, // # - 0x08, // # - 0x08, // # - 0x08, // # - 0x08, // # - 0x70, // ### - 0x00, // - - // @900 'k' (7 pixels wide) - 0x00, // - 0xC0, // ## - 0x40, // # - 0x5C, // # ### - 0x48, // # # - 0x70, // ### - 0x50, // # # - 0x48, // # # - 0xDC, // ## ### - 0x00, // - 0x00, // - 0x00, // - - // @912 'l' (7 pixels wide) - 0x00, // - 0x30, // ## - 0x10, // # - 0x10, // # - 0x10, // # - 0x10, // # - 0x10, // # - 0x10, // # - 0x7C, // ##### - 0x00, // - 0x00, // - 0x00, // - - // @924 'm' (7 pixels wide) - 0x00, // - 0x00, // - 0x00, // - 0xE8, // ### # - 0x54, // # # # - 0x54, // # # # - 0x54, // # # # - 0x54, // # # # - 0xFE, // ####### - 0x00, // - 0x00, // - 0x00, // - - // @936 'n' (7 pixels wide) - 0x00, // - 0x00, // - 0x00, // - 0xD8, // ## ## - 0x64, // ## # - 0x44, // # # - 0x44, // # # - 0x44, // # # - 0xEE, // ### ### - 0x00, // - 0x00, // - 0x00, // - - // @948 'o' (7 pixels wide) - 0x00, // - 0x00, // - 0x00, // - 0x38, // ### - 0x44, // # # - 0x44, // # # - 0x44, // # # - 0x44, // # # - 0x38, // ### - 0x00, // - 0x00, // - 0x00, // - - // @960 'p' (7 pixels wide) - 0x00, // - 0x00, // - 0x00, // - 0xD8, // ## ## - 0x64, // ## # - 0x44, // # # - 0x44, // # # - 0x44, // # # - 0x78, // #### - 0x40, // # - 0xE0, // ### - 0x00, // - - // @972 'q' (7 pixels wide) - 0x00, // - 0x00, // - 0x00, // - 0x36, // ## ## - 0x4C, // # ## - 0x44, // # # - 0x44, // # # - 0x44, // # # - 0x3C, // #### - 0x04, // # - 0x0E, // ### - 0x00, // - - // @984 'r' (7 pixels wide) - 0x00, // - 0x00, // - 0x00, // - 0x6C, // ## ## - 0x30, // ## - 0x20, // # - 0x20, // # - 0x20, // # - 0x7C, // ##### - 0x00, // - 0x00, // - 0x00, // - - // @996 's' (7 pixels wide) - 0x00, // - 0x00, // - 0x00, // - 0x3C, // #### - 0x44, // # # - 0x38, // ### - 0x04, // # - 0x44, // # # - 0x78, // #### - 0x00, // - 0x00, // - 0x00, // - - // @1008 't' (7 pixels wide) - 0x00, // - 0x00, // - 0x20, // # - 0x7C, // ##### - 0x20, // # - 0x20, // # - 0x20, // # - 0x22, // # # - 0x1C, // ### - 0x00, // - 0x00, // - 0x00, // - - // @1020 'u' (7 pixels wide) - 0x00, // - 0x00, // - 0x00, // - 0xCC, // ## ## - 0x44, // # # - 0x44, // # # - 0x44, // # # - 0x4C, // # ## - 0x36, // ## ## - 0x00, // - 0x00, // - 0x00, // - - // @1032 'v' (7 pixels wide) - 0x00, // - 0x00, // - 0x00, // - 0xEE, // ### ### - 0x44, // # # - 0x44, // # # - 0x28, // # # - 0x28, // # # - 0x10, // # - 0x00, // - 0x00, // - 0x00, // - - // @1044 'w' (7 pixels wide) - 0x00, // - 0x00, // - 0x00, // - 0xEE, // ### ### - 0x44, // # # - 0x54, // # # # - 0x54, // # # # - 0x54, // # # # - 0x28, // # # - 0x00, // - 0x00, // - 0x00, // - - // @1056 'x' (7 pixels wide) - 0x00, // - 0x00, // - 0x00, // - 0xCC, // ## ## - 0x48, // # # - 0x30, // ## - 0x30, // ## - 0x48, // # # - 0xCC, // ## ## - 0x00, // - 0x00, // - 0x00, // - - // @1068 'y' (7 pixels wide) - 0x00, // - 0x00, // - 0x00, // - 0xEE, // ### ### - 0x44, // # # - 0x24, // # # - 0x28, // # # - 0x18, // ## - 0x10, // # - 0x10, // # - 0x78, // #### - 0x00, // - - // @1080 'z' (7 pixels wide) - 0x00, // - 0x00, // - 0x00, // - 0x7C, // ##### - 0x48, // # # - 0x10, // # - 0x20, // # - 0x44, // # # - 0x7C, // ##### - 0x00, // - 0x00, // - 0x00, // - - // @1092 '{' (7 pixels wide) - 0x00, // - 0x08, // # - 0x10, // # - 0x10, // # - 0x10, // # - 0x10, // # - 0x20, // # - 0x10, // # - 0x10, // # - 0x10, // # - 0x08, // # - 0x00, // - - // @1104 '|' (7 pixels wide) - 0x00, // - 0x10, // # - 0x10, // # - 0x10, // # - 0x10, // # - 0x10, // # - 0x10, // # - 0x10, // # - 0x10, // # - 0x10, // # - 0x00, // - 0x00, // - - // @1116 '}' (7 pixels wide) - 0x00, // - 0x20, // # - 0x10, // # - 0x10, // # - 0x10, // # - 0x10, // # - 0x08, // # - 0x10, // # - 0x10, // # - 0x10, // # - 0x20, // # - 0x00, // - - // @1128 '~' (7 pixels wide) - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x24, // # # - 0x58, // # ## - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x00, // -}; - -sFONT Font12 = { - Font12_Table, - 7, /* Width */ - 12, /* Height */ -}; - -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.0/src/font16.c b/lib/esp-epaper-29-ws-20171230-gemu-1.0/src/font16.c deleted file mode 100644 index e9276914d..000000000 --- a/lib/esp-epaper-29-ws-20171230-gemu-1.0/src/font16.c +++ /dev/null @@ -1,1765 +0,0 @@ -/** - ****************************************************************************** - * @file font16.c - * @author MCD Application Team - * @version V1.0.0 - * @date 18-February-2014 - * @brief This file provides text font16 for STM32xx-EVAL's LCD driver. - ****************************************************************************** - * @attention - * - *

© COPYRIGHT(c) 2014 STMicroelectronics

- * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. 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. - * 3. Neither the name of STMicroelectronics nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * 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. - * - ****************************************************************************** - */ - -/* Includes ------------------------------------------------------------------*/ -#include "fonts.h" -#include - -// -// Font data for Courier New 12pt -// - -const uint8_t Font16_Table[] PROGMEM = -{ - // @0 ' ' (11 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @32 '!' (11 pixels wide) - 0x00, 0x00, // - 0x0C, 0x00, // ## - 0x0C, 0x00, // ## - 0x0C, 0x00, // ## - 0x0C, 0x00, // ## - 0x0C, 0x00, // ## - 0x0C, 0x00, // ## - 0x0C, 0x00, // ## - 0x0C, 0x00, // ## - 0x00, 0x00, // - 0x0C, 0x00, // ## - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @64 '"' (11 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x1D, 0xC0, // ### ### - 0x1D, 0xC0, // ### ### - 0x08, 0x80, // # # - 0x08, 0x80, // # # - 0x08, 0x80, // # # - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @96 '#' (11 pixels wide) - 0x00, 0x00, // - 0x0D, 0x80, // ## ## - 0x0D, 0x80, // ## ## - 0x0D, 0x80, // ## ## - 0x0D, 0x80, // ## ## - 0x3F, 0xC0, // ######## - 0x1B, 0x00, // ## ## - 0x3F, 0xC0, // ######## - 0x1B, 0x00, // ## ## - 0x1B, 0x00, // ## ## - 0x1B, 0x00, // ## ## - 0x1B, 0x00, // ## ## - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @128 '$' (11 pixels wide) - 0x04, 0x00, // # - 0x1F, 0x80, // ###### - 0x31, 0x80, // ## ## - 0x31, 0x80, // ## ## - 0x38, 0x00, // ### - 0x1E, 0x00, // #### - 0x0F, 0x00, // #### - 0x03, 0x80, // ### - 0x31, 0x80, // ## ## - 0x31, 0x80, // ## ## - 0x3F, 0x00, // ###### - 0x04, 0x00, // # - 0x04, 0x00, // # - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @160 '%' (11 pixels wide) - 0x00, 0x00, // - 0x18, 0x00, // ## - 0x24, 0x00, // # # - 0x24, 0x00, // # # - 0x18, 0xC0, // ## ## - 0x07, 0x80, // #### - 0x1E, 0x00, // #### - 0x31, 0x80, // ## ## - 0x02, 0x40, // # # - 0x02, 0x40, // # # - 0x01, 0x80, // ## - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @192 '&' (11 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x0F, 0x00, // #### - 0x18, 0x00, // ## - 0x18, 0x00, // ## - 0x18, 0x00, // ## - 0x0C, 0x00, // ## - 0x1D, 0x80, // ### ## - 0x37, 0x00, // ## ### - 0x33, 0x00, // ## ## - 0x1D, 0x80, // ### ## - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @224 ''' (11 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x07, 0x00, // ### - 0x07, 0x00, // ### - 0x02, 0x00, // # - 0x02, 0x00, // # - 0x02, 0x00, // # - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @256 '(' (11 pixels wide) - 0x00, 0x00, // - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x06, 0x00, // ## - 0x0E, 0x00, // ### - 0x0C, 0x00, // ## - 0x0C, 0x00, // ## - 0x0C, 0x00, // ## - 0x0C, 0x00, // ## - 0x0E, 0x00, // ### - 0x06, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @288 ')' (11 pixels wide) - 0x00, 0x00, // - 0x18, 0x00, // ## - 0x18, 0x00, // ## - 0x0C, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x0C, 0x00, // ## - 0x1C, 0x00, // ### - 0x18, 0x00, // ## - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @320 '*' (11 pixels wide) - 0x00, 0x00, // - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x3F, 0xC0, // ######## - 0x3F, 0xC0, // ######## - 0x0F, 0x00, // #### - 0x1F, 0x80, // ###### - 0x19, 0x80, // ## ## - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @352 '+' (11 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x04, 0x00, // # - 0x04, 0x00, // # - 0x04, 0x00, // # - 0x3F, 0x80, // ####### - 0x04, 0x00, // # - 0x04, 0x00, // # - 0x04, 0x00, // # - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @384 ',' (11 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x06, 0x00, // ## - 0x04, 0x00, // # - 0x0C, 0x00, // ## - 0x08, 0x00, // # - 0x08, 0x00, // # - 0x00, 0x00, // - 0x00, 0x00, // - - // @416 '-' (11 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x3F, 0x80, // ####### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @448 '.' (11 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x0C, 0x00, // ## - 0x0C, 0x00, // ## - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @480 '/' (11 pixels wide) - 0x00, 0xC0, // ## - 0x00, 0xC0, // ## - 0x01, 0x80, // ## - 0x01, 0x80, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x06, 0x00, // ## - 0x0C, 0x00, // ## - 0x0C, 0x00, // ## - 0x18, 0x00, // ## - 0x18, 0x00, // ## - 0x30, 0x00, // ## - 0x30, 0x00, // ## - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @512 '0' (11 pixels wide) - 0x00, 0x00, // - 0x0E, 0x00, // ### - 0x1B, 0x00, // ## ## - 0x31, 0x80, // ## ## - 0x31, 0x80, // ## ## - 0x31, 0x80, // ## ## - 0x31, 0x80, // ## ## - 0x31, 0x80, // ## ## - 0x31, 0x80, // ## ## - 0x1B, 0x00, // ## ## - 0x0E, 0x00, // ### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @544 '1' (11 pixels wide) - 0x00, 0x00, // - 0x06, 0x00, // ## - 0x3E, 0x00, // ##### - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x3F, 0xC0, // ######## - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @576 '2' (11 pixels wide) - 0x00, 0x00, // - 0x0F, 0x00, // #### - 0x19, 0x80, // ## ## - 0x31, 0x80, // ## ## - 0x31, 0x80, // ## ## - 0x03, 0x00, // ## - 0x06, 0x00, // ## - 0x0C, 0x00, // ## - 0x18, 0x00, // ## - 0x30, 0x00, // ## - 0x3F, 0x80, // ####### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @608 '3' (11 pixels wide) - 0x00, 0x00, // - 0x3F, 0x00, // ###### - 0x61, 0x80, // ## ## - 0x01, 0x80, // ## - 0x03, 0x00, // ## - 0x1F, 0x00, // ##### - 0x03, 0x80, // ### - 0x01, 0x80, // ## - 0x01, 0x80, // ## - 0x61, 0x80, // ## ## - 0x3F, 0x00, // ###### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @640 '4' (11 pixels wide) - 0x00, 0x00, // - 0x07, 0x00, // ### - 0x07, 0x00, // ### - 0x0F, 0x00, // #### - 0x0B, 0x00, // # ## - 0x1B, 0x00, // ## ## - 0x13, 0x00, // # ## - 0x33, 0x00, // ## ## - 0x3F, 0x80, // ####### - 0x03, 0x00, // ## - 0x0F, 0x80, // ##### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @672 '5' (11 pixels wide) - 0x00, 0x00, // - 0x1F, 0x80, // ###### - 0x18, 0x00, // ## - 0x18, 0x00, // ## - 0x18, 0x00, // ## - 0x1F, 0x00, // ##### - 0x11, 0x80, // # ## - 0x01, 0x80, // ## - 0x01, 0x80, // ## - 0x21, 0x80, // # ## - 0x1F, 0x00, // ##### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @704 '6' (11 pixels wide) - 0x00, 0x00, // - 0x07, 0x80, // #### - 0x1C, 0x00, // ### - 0x18, 0x00, // ## - 0x30, 0x00, // ## - 0x37, 0x00, // ## ### - 0x39, 0x80, // ### ## - 0x31, 0x80, // ## ## - 0x31, 0x80, // ## ## - 0x19, 0x80, // ## ## - 0x0F, 0x00, // #### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @736 '7' (11 pixels wide) - 0x00, 0x00, // - 0x7F, 0x00, // ####### - 0x43, 0x00, // # ## - 0x03, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x0C, 0x00, // ## - 0x0C, 0x00, // ## - 0x0C, 0x00, // ## - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @768 '8' (11 pixels wide) - 0x00, 0x00, // - 0x1F, 0x00, // ##### - 0x31, 0x80, // ## ## - 0x31, 0x80, // ## ## - 0x31, 0x80, // ## ## - 0x1F, 0x00, // ##### - 0x31, 0x80, // ## ## - 0x31, 0x80, // ## ## - 0x31, 0x80, // ## ## - 0x31, 0x80, // ## ## - 0x1F, 0x00, // ##### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @800 '9' (11 pixels wide) - 0x00, 0x00, // - 0x1E, 0x00, // #### - 0x33, 0x00, // ## ## - 0x31, 0x80, // ## ## - 0x31, 0x80, // ## ## - 0x33, 0x80, // ## ### - 0x1D, 0x80, // ### ## - 0x01, 0x80, // ## - 0x03, 0x00, // ## - 0x07, 0x00, // ### - 0x3C, 0x00, // #### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @832 ':' (11 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x0C, 0x00, // ## - 0x0C, 0x00, // ## - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x0C, 0x00, // ## - 0x0C, 0x00, // ## - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @864 ';' (11 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x06, 0x00, // ## - 0x04, 0x00, // # - 0x08, 0x00, // # - 0x08, 0x00, // # - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @896 '<' (11 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0xC0, // ## - 0x03, 0x00, // ## - 0x04, 0x00, // # - 0x18, 0x00, // ## - 0x60, 0x00, // ## - 0x18, 0x00, // ## - 0x04, 0x00, // # - 0x03, 0x00, // ## - 0x00, 0xC0, // ## - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @928 '=' (11 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x7F, 0xC0, // ######### - 0x00, 0x00, // - 0x7F, 0xC0, // ######### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @960 '>' (11 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x60, 0x00, // ## - 0x18, 0x00, // ## - 0x04, 0x00, // # - 0x03, 0x00, // ## - 0x00, 0xC0, // ## - 0x03, 0x00, // ## - 0x04, 0x00, // # - 0x18, 0x00, // ## - 0x60, 0x00, // ## - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @992 '?' (11 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x1F, 0x00, // ##### - 0x31, 0x80, // ## ## - 0x31, 0x80, // ## ## - 0x01, 0x80, // ## - 0x07, 0x00, // ### - 0x0C, 0x00, // ## - 0x0C, 0x00, // ## - 0x00, 0x00, // - 0x0C, 0x00, // ## - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @1024 '@' (11 pixels wide) - 0x00, 0x00, // - 0x0E, 0x00, // ### - 0x11, 0x00, // # # - 0x21, 0x00, // # # - 0x21, 0x00, // # # - 0x27, 0x00, // # ### - 0x29, 0x00, // # # # - 0x29, 0x00, // # # # - 0x27, 0x00, // # ### - 0x20, 0x00, // # - 0x11, 0x00, // # # - 0x0E, 0x00, // ### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @1056 'A' (11 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x3F, 0x00, // ###### - 0x0F, 0x00, // #### - 0x09, 0x00, // # # - 0x19, 0x80, // ## ## - 0x19, 0x80, // ## ## - 0x1F, 0x80, // ###### - 0x30, 0xC0, // ## ## - 0x30, 0xC0, // ## ## - 0x79, 0xE0, // #### #### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @1088 'B' (11 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x7F, 0x00, // ####### - 0x31, 0x80, // ## ## - 0x31, 0x80, // ## ## - 0x31, 0x80, // ## ## - 0x3F, 0x00, // ###### - 0x31, 0x80, // ## ## - 0x31, 0x80, // ## ## - 0x31, 0x80, // ## ## - 0x7F, 0x00, // ####### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @1120 'C' (11 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x1F, 0x40, // ##### # - 0x30, 0xC0, // ## ## - 0x60, 0x40, // ## # - 0x60, 0x00, // ## - 0x60, 0x00, // ## - 0x60, 0x00, // ## - 0x60, 0x40, // ## # - 0x30, 0x80, // ## # - 0x1F, 0x00, // ##### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @1152 'D' (11 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x7F, 0x00, // ####### - 0x31, 0x80, // ## ## - 0x30, 0xC0, // ## ## - 0x30, 0xC0, // ## ## - 0x30, 0xC0, // ## ## - 0x30, 0xC0, // ## ## - 0x30, 0xC0, // ## ## - 0x31, 0x80, // ## ## - 0x7F, 0x00, // ####### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @1184 'E' (11 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x7F, 0x80, // ######## - 0x30, 0x80, // ## # - 0x30, 0x80, // ## # - 0x32, 0x00, // ## # - 0x3E, 0x00, // ##### - 0x32, 0x00, // ## # - 0x30, 0x80, // ## # - 0x30, 0x80, // ## # - 0x7F, 0x80, // ######## - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @1216 'F' (11 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x7F, 0xC0, // ######### - 0x30, 0x40, // ## # - 0x30, 0x40, // ## # - 0x32, 0x00, // ## # - 0x3E, 0x00, // ##### - 0x32, 0x00, // ## # - 0x30, 0x00, // ## - 0x30, 0x00, // ## - 0x7C, 0x00, // ##### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @1248 'G' (11 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x1E, 0x80, // #### # - 0x31, 0x80, // ## ## - 0x60, 0x80, // ## # - 0x60, 0x00, // ## - 0x60, 0x00, // ## - 0x67, 0xC0, // ## ##### - 0x61, 0x80, // ## ## - 0x31, 0x80, // ## ## - 0x1F, 0x00, // ##### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @1280 'H' (11 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x7B, 0xC0, // #### #### - 0x31, 0x80, // ## ## - 0x31, 0x80, // ## ## - 0x31, 0x80, // ## ## - 0x3F, 0x80, // ####### - 0x31, 0x80, // ## ## - 0x31, 0x80, // ## ## - 0x31, 0x80, // ## ## - 0x7B, 0xC0, // #### #### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @1312 'I' (11 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x3F, 0xC0, // ######## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x3F, 0xC0, // ######## - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @1344 'J' (11 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x1F, 0xC0, // ####### - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x63, 0x00, // ## ## - 0x63, 0x00, // ## ## - 0x63, 0x00, // ## ## - 0x3E, 0x00, // ##### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @1376 'K' (11 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x7B, 0xC0, // #### #### - 0x31, 0x80, // ## ## - 0x33, 0x00, // ## ## - 0x36, 0x00, // ## ## - 0x3C, 0x00, // #### - 0x3E, 0x00, // ##### - 0x33, 0x00, // ## ## - 0x31, 0x80, // ## ## - 0x79, 0xC0, // #### ### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @1408 'L' (11 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x7E, 0x00, // ###### - 0x18, 0x00, // ## - 0x18, 0x00, // ## - 0x18, 0x00, // ## - 0x18, 0x00, // ## - 0x18, 0x40, // ## # - 0x18, 0x40, // ## # - 0x18, 0x40, // ## # - 0x7F, 0xC0, // ######### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @1440 'M' (11 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0xE0, 0xE0, // ### ### - 0x60, 0xC0, // ## ## - 0x71, 0xC0, // ### ### - 0x7B, 0xC0, // #### #### - 0x6A, 0xC0, // ## # # ## - 0x6E, 0xC0, // ## ### ## - 0x64, 0xC0, // ## # ## - 0x60, 0xC0, // ## ## - 0xFB, 0xE0, // ##### ##### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @1472 'N' (11 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x73, 0xC0, // ### #### - 0x31, 0x80, // ## ## - 0x39, 0x80, // ### ## - 0x3D, 0x80, // #### ## - 0x35, 0x80, // ## # ## - 0x37, 0x80, // ## #### - 0x33, 0x80, // ## ### - 0x31, 0x80, // ## ## - 0x79, 0x80, // #### ## - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @1504 'O' (11 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x1F, 0x00, // ##### - 0x31, 0x80, // ## ## - 0x60, 0xC0, // ## ## - 0x60, 0xC0, // ## ## - 0x60, 0xC0, // ## ## - 0x60, 0xC0, // ## ## - 0x60, 0xC0, // ## ## - 0x31, 0x80, // ## ## - 0x1F, 0x00, // ##### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @1536 'P' (11 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x7F, 0x00, // ####### - 0x31, 0x80, // ## ## - 0x31, 0x80, // ## ## - 0x31, 0x80, // ## ## - 0x31, 0x80, // ## ## - 0x3F, 0x00, // ###### - 0x30, 0x00, // ## - 0x30, 0x00, // ## - 0x7E, 0x00, // ###### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @1568 'Q' (11 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x1F, 0x00, // ##### - 0x31, 0x80, // ## ## - 0x60, 0xC0, // ## ## - 0x60, 0xC0, // ## ## - 0x60, 0xC0, // ## ## - 0x60, 0xC0, // ## ## - 0x60, 0xC0, // ## ## - 0x31, 0x80, // ## ## - 0x1F, 0x00, // ##### - 0x0C, 0xC0, // ## ## - 0x1F, 0x80, // ###### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @1600 'R' (11 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x7F, 0x00, // ####### - 0x31, 0x80, // ## ## - 0x31, 0x80, // ## ## - 0x31, 0x80, // ## ## - 0x3E, 0x00, // ##### - 0x33, 0x00, // ## ## - 0x31, 0x80, // ## ## - 0x31, 0x80, // ## ## - 0x7C, 0xE0, // ##### ### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @1632 'S' (11 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x1F, 0x80, // ###### - 0x31, 0x80, // ## ## - 0x31, 0x80, // ## ## - 0x38, 0x00, // ### - 0x1F, 0x00, // ##### - 0x03, 0x80, // ### - 0x31, 0x80, // ## ## - 0x31, 0x80, // ## ## - 0x3F, 0x00, // ###### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @1664 'T' (11 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x7F, 0x80, // ######## - 0x4C, 0x80, // # ## # - 0x4C, 0x80, // # ## # - 0x4C, 0x80, // # ## # - 0x0C, 0x00, // ## - 0x0C, 0x00, // ## - 0x0C, 0x00, // ## - 0x0C, 0x00, // ## - 0x3F, 0x00, // ###### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @1696 'U' (11 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x7B, 0xC0, // #### #### - 0x31, 0x80, // ## ## - 0x31, 0x80, // ## ## - 0x31, 0x80, // ## ## - 0x31, 0x80, // ## ## - 0x31, 0x80, // ## ## - 0x31, 0x80, // ## ## - 0x31, 0x80, // ## ## - 0x1F, 0x00, // ##### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @1728 'V' (11 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x7B, 0xC0, // #### #### - 0x31, 0x80, // ## ## - 0x31, 0x80, // ## ## - 0x1B, 0x00, // ## ## - 0x1B, 0x00, // ## ## - 0x1B, 0x00, // ## ## - 0x0A, 0x00, // # # - 0x0E, 0x00, // ### - 0x0E, 0x00, // ### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @1760 'W' (11 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0xFB, 0xE0, // ##### ##### - 0x60, 0xC0, // ## ## - 0x64, 0xC0, // ## # ## - 0x6E, 0xC0, // ## ### ## - 0x6E, 0xC0, // ## ### ## - 0x2A, 0x80, // # # # # - 0x3B, 0x80, // ### ### - 0x3B, 0x80, // ### ### - 0x31, 0x80, // ## ## - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @1792 'X' (11 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x7B, 0xC0, // #### #### - 0x31, 0x80, // ## ## - 0x1B, 0x00, // ## ## - 0x0E, 0x00, // ### - 0x0E, 0x00, // ### - 0x0E, 0x00, // ### - 0x1B, 0x00, // ## ## - 0x31, 0x80, // ## ## - 0x7B, 0xC0, // #### #### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @1824 'Y' (11 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x79, 0xE0, // #### #### - 0x30, 0xC0, // ## ## - 0x19, 0x80, // ## ## - 0x0F, 0x00, // #### - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x1F, 0x80, // ###### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @1856 'Z' (11 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x3F, 0x80, // ####### - 0x21, 0x80, // # ## - 0x23, 0x00, // # ## - 0x06, 0x00, // ## - 0x04, 0x00, // # - 0x0C, 0x00, // ## - 0x18, 0x80, // ## # - 0x30, 0x80, // ## # - 0x3F, 0x80, // ####### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @1888 '[' (11 pixels wide) - 0x00, 0x00, // - 0x07, 0x80, // #### - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x07, 0x80, // #### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @1920 '\' (11 pixels wide) - 0x30, 0x00, // ## - 0x30, 0x00, // ## - 0x18, 0x00, // ## - 0x18, 0x00, // ## - 0x0C, 0x00, // ## - 0x0C, 0x00, // ## - 0x06, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x01, 0x80, // ## - 0x01, 0x80, // ## - 0x00, 0xC0, // ## - 0x00, 0xC0, // ## - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @1952 ']' (11 pixels wide) - 0x00, 0x00, // - 0x1E, 0x00, // #### - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x1E, 0x00, // #### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @1984 '^' (11 pixels wide) - 0x04, 0x00, // # - 0x0A, 0x00, // # # - 0x0A, 0x00, // # # - 0x11, 0x00, // # # - 0x20, 0x80, // # # - 0x20, 0x80, // # # - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @2016 '_' (11 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0xFF, 0xE0, // ########### - - // @2048 '`' (11 pixels wide) - 0x08, 0x00, // # - 0x04, 0x00, // # - 0x02, 0x00, // # - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @2080 'a' (11 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x1F, 0x00, // ##### - 0x01, 0x80, // ## - 0x01, 0x80, // ## - 0x1F, 0x80, // ###### - 0x31, 0x80, // ## ## - 0x33, 0x80, // ## ### - 0x1D, 0xC0, // ### ### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @2112 'b' (11 pixels wide) - 0x00, 0x00, // - 0x70, 0x00, // ### - 0x30, 0x00, // ## - 0x30, 0x00, // ## - 0x37, 0x00, // ## ### - 0x39, 0x80, // ### ## - 0x30, 0xC0, // ## ## - 0x30, 0xC0, // ## ## - 0x30, 0xC0, // ## ## - 0x39, 0x80, // ### ## - 0x77, 0x00, // ### ### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @2144 'c' (11 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x1E, 0x80, // #### # - 0x31, 0x80, // ## ## - 0x60, 0x80, // ## # - 0x60, 0x00, // ## - 0x60, 0x80, // ## # - 0x31, 0x80, // ## ## - 0x1F, 0x00, // ##### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @2176 'd' (11 pixels wide) - 0x00, 0x00, // - 0x03, 0x80, // ### - 0x01, 0x80, // ## - 0x01, 0x80, // ## - 0x1D, 0x80, // ### ## - 0x33, 0x80, // ## ### - 0x61, 0x80, // ## ## - 0x61, 0x80, // ## ## - 0x61, 0x80, // ## ## - 0x33, 0x80, // ## ### - 0x1D, 0xC0, // ### ### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @2208 'e' (11 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x1F, 0x00, // ##### - 0x31, 0x80, // ## ## - 0x60, 0xC0, // ## ## - 0x7F, 0xC0, // ######### - 0x60, 0x00, // ## - 0x30, 0xC0, // ## ## - 0x1F, 0x80, // ###### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @2240 'f' (11 pixels wide) - 0x00, 0x00, // - 0x07, 0xE0, // ###### - 0x0C, 0x00, // ## - 0x0C, 0x00, // ## - 0x3F, 0x80, // ####### - 0x0C, 0x00, // ## - 0x0C, 0x00, // ## - 0x0C, 0x00, // ## - 0x0C, 0x00, // ## - 0x0C, 0x00, // ## - 0x3F, 0x80, // ####### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @2272 'g' (11 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x1D, 0xC0, // ### ### - 0x33, 0x80, // ## ### - 0x61, 0x80, // ## ## - 0x61, 0x80, // ## ## - 0x61, 0x80, // ## ## - 0x33, 0x80, // ## ### - 0x1D, 0x80, // ### ## - 0x01, 0x80, // ## - 0x01, 0x80, // ## - 0x1F, 0x00, // ##### - 0x00, 0x00, // - 0x00, 0x00, // - - // @2304 'h' (11 pixels wide) - 0x00, 0x00, // - 0x70, 0x00, // ### - 0x30, 0x00, // ## - 0x30, 0x00, // ## - 0x37, 0x00, // ## ### - 0x39, 0x80, // ### ## - 0x31, 0x80, // ## ## - 0x31, 0x80, // ## ## - 0x31, 0x80, // ## ## - 0x31, 0x80, // ## ## - 0x7B, 0xC0, // #### #### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @2336 'i' (11 pixels wide) - 0x00, 0x00, // - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x00, 0x00, // - 0x1E, 0x00, // #### - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x3F, 0xC0, // ######## - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @2368 'j' (11 pixels wide) - 0x00, 0x00, // - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x00, 0x00, // - 0x3F, 0x00, // ###### - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x3E, 0x00, // ##### - 0x00, 0x00, // - 0x00, 0x00, // - - // @2400 'k' (11 pixels wide) - 0x00, 0x00, // - 0x70, 0x00, // ### - 0x30, 0x00, // ## - 0x30, 0x00, // ## - 0x37, 0x80, // ## #### - 0x36, 0x00, // ## ## - 0x3C, 0x00, // #### - 0x3C, 0x00, // #### - 0x36, 0x00, // ## ## - 0x33, 0x00, // ## ## - 0x77, 0xC0, // ### ##### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @2432 'l' (11 pixels wide) - 0x00, 0x00, // - 0x1E, 0x00, // #### - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x3F, 0xC0, // ######## - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @2464 'm' (11 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x7F, 0x80, // ######## - 0x36, 0xC0, // ## ## ## - 0x36, 0xC0, // ## ## ## - 0x36, 0xC0, // ## ## ## - 0x36, 0xC0, // ## ## ## - 0x36, 0xC0, // ## ## ## - 0x76, 0xE0, // ### ## ### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @2496 'n' (11 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x77, 0x00, // ### ### - 0x39, 0x80, // ### ## - 0x31, 0x80, // ## ## - 0x31, 0x80, // ## ## - 0x31, 0x80, // ## ## - 0x31, 0x80, // ## ## - 0x7B, 0xC0, // #### #### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @2528 'o' (11 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x1F, 0x00, // ##### - 0x31, 0x80, // ## ## - 0x60, 0xC0, // ## ## - 0x60, 0xC0, // ## ## - 0x60, 0xC0, // ## ## - 0x31, 0x80, // ## ## - 0x1F, 0x00, // ##### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @2560 'p' (11 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x77, 0x00, // ### ### - 0x39, 0x80, // ### ## - 0x30, 0xC0, // ## ## - 0x30, 0xC0, // ## ## - 0x30, 0xC0, // ## ## - 0x39, 0x80, // ### ## - 0x37, 0x00, // ## ### - 0x30, 0x00, // ## - 0x30, 0x00, // ## - 0x7C, 0x00, // ##### - 0x00, 0x00, // - 0x00, 0x00, // - - // @2592 'q' (11 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x1D, 0xC0, // ### ### - 0x33, 0x80, // ## ### - 0x61, 0x80, // ## ## - 0x61, 0x80, // ## ## - 0x61, 0x80, // ## ## - 0x33, 0x80, // ## ### - 0x1D, 0x80, // ### ## - 0x01, 0x80, // ## - 0x01, 0x80, // ## - 0x07, 0xC0, // ##### - 0x00, 0x00, // - 0x00, 0x00, // - - // @2624 'r' (11 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x7B, 0x80, // #### ### - 0x1C, 0xC0, // ### ## - 0x18, 0x00, // ## - 0x18, 0x00, // ## - 0x18, 0x00, // ## - 0x18, 0x00, // ## - 0x7F, 0x00, // ####### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @2656 's' (11 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x1F, 0x80, // ###### - 0x31, 0x80, // ## ## - 0x3C, 0x00, // #### - 0x1F, 0x00, // ##### - 0x03, 0x80, // ### - 0x31, 0x80, // ## ## - 0x3F, 0x00, // ###### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @2688 't' (11 pixels wide) - 0x00, 0x00, // - 0x18, 0x00, // ## - 0x18, 0x00, // ## - 0x18, 0x00, // ## - 0x7F, 0x00, // ####### - 0x18, 0x00, // ## - 0x18, 0x00, // ## - 0x18, 0x00, // ## - 0x18, 0x00, // ## - 0x18, 0x80, // ## # - 0x0F, 0x00, // #### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @2720 'u' (11 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x73, 0x80, // ### ### - 0x31, 0x80, // ## ## - 0x31, 0x80, // ## ## - 0x31, 0x80, // ## ## - 0x31, 0x80, // ## ## - 0x33, 0x80, // ## ### - 0x1D, 0xC0, // ### ### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @2752 'v' (11 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x7B, 0xC0, // #### #### - 0x31, 0x80, // ## ## - 0x31, 0x80, // ## ## - 0x1B, 0x00, // ## ## - 0x1B, 0x00, // ## ## - 0x0E, 0x00, // ### - 0x0E, 0x00, // ### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @2784 'w' (11 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0xF1, 0xE0, // #### #### - 0x60, 0xC0, // ## ## - 0x64, 0xC0, // ## # ## - 0x6E, 0xC0, // ## ### ## - 0x3B, 0x80, // ### ### - 0x3B, 0x80, // ### ### - 0x31, 0x80, // ## ## - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @2816 'x' (11 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x7B, 0xC0, // #### #### - 0x1B, 0x00, // ## ## - 0x0E, 0x00, // ### - 0x0E, 0x00, // ### - 0x0E, 0x00, // ### - 0x1B, 0x00, // ## ## - 0x7B, 0xC0, // #### #### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @2848 'y' (11 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x79, 0xE0, // #### #### - 0x30, 0xC0, // ## ## - 0x19, 0x80, // ## ## - 0x19, 0x80, // ## ## - 0x0B, 0x00, // # ## - 0x0F, 0x00, // #### - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x0C, 0x00, // ## - 0x3E, 0x00, // ##### - 0x00, 0x00, // - 0x00, 0x00, // - - // @2880 'z' (11 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x3F, 0x80, // ####### - 0x21, 0x80, // # ## - 0x03, 0x00, // ## - 0x0E, 0x00, // ### - 0x18, 0x00, // ## - 0x30, 0x80, // ## # - 0x3F, 0x80, // ####### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @2912 '{' (11 pixels wide) - 0x00, 0x00, // - 0x06, 0x00, // ## - 0x0C, 0x00, // ## - 0x0C, 0x00, // ## - 0x0C, 0x00, // ## - 0x0C, 0x00, // ## - 0x0C, 0x00, // ## - 0x18, 0x00, // ## - 0x0C, 0x00, // ## - 0x0C, 0x00, // ## - 0x0C, 0x00, // ## - 0x0C, 0x00, // ## - 0x06, 0x00, // ## - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @2944 '|' (11 pixels wide) - 0x00, 0x00, // - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @2976 '}' (11 pixels wide) - 0x00, 0x00, // - 0x0C, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x03, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x0C, 0x00, // ## - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @3008 '~' (11 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x18, 0x00, // ## - 0x24, 0x80, // # # # - 0x03, 0x00, // ## - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // -}; - -sFONT Font16 = { - Font16_Table, - 11, /* Width */ - 16, /* Height */ -}; - -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.0/src/font20.c b/lib/esp-epaper-29-ws-20171230-gemu-1.0/src/font20.c deleted file mode 100644 index 17329b06f..000000000 --- a/lib/esp-epaper-29-ws-20171230-gemu-1.0/src/font20.c +++ /dev/null @@ -1,2143 +0,0 @@ -/** - ****************************************************************************** - * @file font20.c - * @author MCD Application Team - * @version V1.0.0 - * @date 18-February-2014 - * @brief This file provides text font20 for STM32xx-EVAL's LCD driver. - ****************************************************************************** - * @attention - * - *

© COPYRIGHT(c) 2014 STMicroelectronics

- * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. 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. - * 3. Neither the name of STMicroelectronics nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * 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. - * - ****************************************************************************** - */ - -/* Includes ------------------------------------------------------------------*/ -#include "fonts.h" -#include - -// Character bitmaps for Courier New 15pt -const uint8_t Font20_Table[] PROGMEM = -{ - // @0 ' ' (14 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @40 '!' (14 pixels wide) - 0x00, 0x00, // - 0x07, 0x00, // ### - 0x07, 0x00, // ### - 0x07, 0x00, // ### - 0x07, 0x00, // ### - 0x07, 0x00, // ### - 0x07, 0x00, // ### - 0x07, 0x00, // ### - 0x02, 0x00, // # - 0x02, 0x00, // # - 0x00, 0x00, // - 0x00, 0x00, // - 0x07, 0x00, // ### - 0x07, 0x00, // ### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @80 '"' (14 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x1C, 0xE0, // ### ### - 0x1C, 0xE0, // ### ### - 0x1C, 0xE0, // ### ### - 0x08, 0x40, // # # - 0x08, 0x40, // # # - 0x08, 0x40, // # # - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @120 '#' (14 pixels wide) - 0x0C, 0xC0, // ## ## - 0x0C, 0xC0, // ## ## - 0x0C, 0xC0, // ## ## - 0x0C, 0xC0, // ## ## - 0x0C, 0xC0, // ## ## - 0x3F, 0xF0, // ########## - 0x3F, 0xF0, // ########## - 0x0C, 0xC0, // ## ## - 0x0C, 0xC0, // ## ## - 0x3F, 0xF0, // ########## - 0x3F, 0xF0, // ########## - 0x0C, 0xC0, // ## ## - 0x0C, 0xC0, // ## ## - 0x0C, 0xC0, // ## ## - 0x0C, 0xC0, // ## ## - 0x0C, 0xC0, // ## ## - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @160 '$' (14 pixels wide) - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x07, 0xE0, // ###### - 0x0F, 0xE0, // ####### - 0x18, 0x60, // ## ## - 0x18, 0x00, // ## - 0x1F, 0x00, // ##### - 0x0F, 0xC0, // ###### - 0x00, 0xE0, // ### - 0x18, 0x60, // ## ## - 0x18, 0x60, // ## ## - 0x1F, 0xC0, // ####### - 0x1F, 0x80, // ###### - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @200 '%' (14 pixels wide) - 0x00, 0x00, // - 0x1C, 0x00, // ### - 0x22, 0x00, // # # - 0x22, 0x00, // # # - 0x22, 0x00, // # # - 0x1C, 0x60, // ### ## - 0x01, 0xE0, // #### - 0x0F, 0x80, // ##### - 0x3C, 0x00, // #### - 0x31, 0xC0, // ## ### - 0x02, 0x20, // # # - 0x02, 0x20, // # # - 0x02, 0x20, // # # - 0x01, 0xC0, // ### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @240 '&' (14 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x03, 0xE0, // ##### - 0x0F, 0xE0, // ####### - 0x0C, 0x00, // ## - 0x0C, 0x00, // ## - 0x06, 0x00, // ## - 0x0F, 0x30, // #### ## - 0x1F, 0xF0, // ######### - 0x19, 0xE0, // ## #### - 0x18, 0xC0, // ## ## - 0x1F, 0xF0, // ######### - 0x07, 0xB0, // #### ## - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @280 ''' (14 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x03, 0x80, // ### - 0x03, 0x80, // ### - 0x03, 0x80, // ### - 0x01, 0x00, // # - 0x01, 0x00, // # - 0x01, 0x00, // # - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @320 '(' (14 pixels wide) - 0x00, 0x00, // - 0x00, 0xC0, // ## - 0x00, 0xC0, // ## - 0x01, 0x80, // ## - 0x01, 0x80, // ## - 0x01, 0x80, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x01, 0x80, // ## - 0x01, 0x80, // ## - 0x01, 0x80, // ## - 0x00, 0xC0, // ## - 0x00, 0xC0, // ## - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @360 ')' (14 pixels wide) - 0x00, 0x00, // - 0x0C, 0x00, // ## - 0x0C, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x0C, 0x00, // ## - 0x0C, 0x00, // ## - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @400 '*' (14 pixels wide) - 0x00, 0x00, // - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x1B, 0x60, // ## ## ## - 0x1F, 0xE0, // ######## - 0x07, 0x80, // #### - 0x07, 0x80, // #### - 0x0F, 0xC0, // ###### - 0x0C, 0xC0, // ## ## - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @440 '+' (14 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x3F, 0xF0, // ########## - 0x3F, 0xF0, // ########## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @480 ',' (14 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x03, 0x80, // ### - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x04, 0x00, // # - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @520 '-' (14 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x3F, 0xE0, // ######### - 0x3F, 0xE0, // ######### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @560 '.' (14 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x03, 0x80, // ### - 0x03, 0x80, // ### - 0x03, 0x80, // ### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @600 '/' (14 pixels wide) - 0x00, 0x60, // ## - 0x00, 0x60, // ## - 0x00, 0xC0, // ## - 0x00, 0xC0, // ## - 0x00, 0xC0, // ## - 0x01, 0x80, // ## - 0x01, 0x80, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x0C, 0x00, // ## - 0x0C, 0x00, // ## - 0x0C, 0x00, // ## - 0x18, 0x00, // ## - 0x18, 0x00, // ## - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @640 '0' (14 pixels wide) - 0x00, 0x00, // - 0x0F, 0x80, // ##### - 0x1F, 0xC0, // ####### - 0x18, 0xC0, // ## ## - 0x30, 0x60, // ## ## - 0x30, 0x60, // ## ## - 0x30, 0x60, // ## ## - 0x30, 0x60, // ## ## - 0x30, 0x60, // ## ## - 0x30, 0x60, // ## ## - 0x30, 0x60, // ## ## - 0x18, 0xC0, // ## ## - 0x1F, 0xC0, // ####### - 0x0F, 0x80, // ##### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @680 '1' (14 pixels wide) - 0x00, 0x00, // - 0x03, 0x00, // ## - 0x1F, 0x00, // ##### - 0x1F, 0x00, // ##### - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x1F, 0xE0, // ######## - 0x1F, 0xE0, // ######## - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @720 '2' (14 pixels wide) - 0x00, 0x00, // - 0x0F, 0x80, // ##### - 0x1F, 0xC0, // ####### - 0x38, 0xE0, // ### ### - 0x30, 0x60, // ## ## - 0x00, 0x60, // ## - 0x00, 0xC0, // ## - 0x01, 0x80, // ## - 0x03, 0x00, // ## - 0x06, 0x00, // ## - 0x0C, 0x00, // ## - 0x18, 0x00, // ## - 0x3F, 0xE0, // ######### - 0x3F, 0xE0, // ######### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @760 '3' (14 pixels wide) - 0x00, 0x00, // - 0x0F, 0x80, // ##### - 0x3F, 0xC0, // ######## - 0x30, 0xE0, // ## ### - 0x00, 0x60, // ## - 0x00, 0xE0, // ### - 0x07, 0xC0, // ##### - 0x07, 0xC0, // ##### - 0x00, 0xE0, // ### - 0x00, 0x60, // ## - 0x00, 0x60, // ## - 0x60, 0xE0, // ## ### - 0x7F, 0xC0, // ######### - 0x3F, 0x80, // ####### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @800 '4' (14 pixels wide) - 0x00, 0x00, // - 0x01, 0xC0, // ### - 0x03, 0xC0, // #### - 0x03, 0xC0, // #### - 0x06, 0xC0, // ## ## - 0x0C, 0xC0, // ## ## - 0x0C, 0xC0, // ## ## - 0x18, 0xC0, // ## ## - 0x30, 0xC0, // ## ## - 0x3F, 0xE0, // ######### - 0x3F, 0xE0, // ######### - 0x00, 0xC0, // ## - 0x03, 0xE0, // ##### - 0x03, 0xE0, // ##### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @840 '5' (14 pixels wide) - 0x00, 0x00, // - 0x1F, 0xC0, // ####### - 0x1F, 0xC0, // ####### - 0x18, 0x00, // ## - 0x18, 0x00, // ## - 0x1F, 0x80, // ###### - 0x1F, 0xC0, // ####### - 0x18, 0xE0, // ## ### - 0x00, 0x60, // ## - 0x00, 0x60, // ## - 0x00, 0x60, // ## - 0x30, 0xE0, // ## ### - 0x3F, 0xC0, // ######## - 0x1F, 0x80, // ###### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @880 '6' (14 pixels wide) - 0x00, 0x00, // - 0x03, 0xE0, // ##### - 0x0F, 0xE0, // ####### - 0x1E, 0x00, // #### - 0x18, 0x00, // ## - 0x38, 0x00, // ### - 0x37, 0x80, // ## #### - 0x3F, 0xC0, // ######## - 0x38, 0xE0, // ### ### - 0x30, 0x60, // ## ## - 0x30, 0x60, // ## ## - 0x18, 0xE0, // ## ### - 0x1F, 0xC0, // ####### - 0x07, 0x80, // #### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @920 '7' (14 pixels wide) - 0x00, 0x00, // - 0x3F, 0xE0, // ######### - 0x3F, 0xE0, // ######### - 0x30, 0x60, // ## ## - 0x00, 0x60, // ## - 0x00, 0xC0, // ## - 0x00, 0xC0, // ## - 0x00, 0xC0, // ## - 0x01, 0x80, // ## - 0x01, 0x80, // ## - 0x01, 0x80, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @960 '8' (14 pixels wide) - 0x00, 0x00, // - 0x0F, 0x80, // ##### - 0x1F, 0xC0, // ####### - 0x38, 0xE0, // ### ### - 0x30, 0x60, // ## ## - 0x38, 0xE0, // ### ### - 0x1F, 0xC0, // ####### - 0x1F, 0xC0, // ####### - 0x38, 0xE0, // ### ### - 0x30, 0x60, // ## ## - 0x30, 0x60, // ## ## - 0x38, 0xE0, // ### ### - 0x1F, 0xC0, // ####### - 0x0F, 0x80, // ##### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @1000 '9' (14 pixels wide) - 0x00, 0x00, // - 0x0F, 0x00, // #### - 0x1F, 0xC0, // ####### - 0x38, 0xC0, // ### ## - 0x30, 0x60, // ## ## - 0x30, 0x60, // ## ## - 0x38, 0xE0, // ### ### - 0x1F, 0xE0, // ######## - 0x0F, 0x60, // #### ## - 0x00, 0xE0, // ### - 0x00, 0xC0, // ## - 0x03, 0xC0, // #### - 0x3F, 0x80, // ####### - 0x3E, 0x00, // ##### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @1040 ':' (14 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x03, 0x80, // ### - 0x03, 0x80, // ### - 0x03, 0x80, // ### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x03, 0x80, // ### - 0x03, 0x80, // ### - 0x03, 0x80, // ### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @1080 ';' (14 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x01, 0xC0, // ### - 0x01, 0xC0, // ### - 0x01, 0xC0, // ### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x03, 0x80, // ### - 0x03, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x04, 0x00, // # - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @1120 '<' (14 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x30, // ## - 0x00, 0xF0, // #### - 0x03, 0xC0, // #### - 0x07, 0x00, // ### - 0x1C, 0x00, // ### - 0x78, 0x00, // #### - 0x1C, 0x00, // ### - 0x07, 0x00, // ### - 0x03, 0xC0, // #### - 0x00, 0xF0, // #### - 0x00, 0x30, // ## - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @1160 '=' (14 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x7F, 0xF0, // ########### - 0x7F, 0xF0, // ########### - 0x00, 0x00, // - 0x00, 0x00, // - 0x7F, 0xF0, // ########### - 0x7F, 0xF0, // ########### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @1200 '>' (14 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x30, 0x00, // ## - 0x3C, 0x00, // #### - 0x0F, 0x00, // #### - 0x03, 0x80, // ### - 0x00, 0xE0, // ### - 0x00, 0x78, // #### - 0x00, 0xE0, // ### - 0x03, 0x80, // ### - 0x0F, 0x00, // #### - 0x3C, 0x00, // #### - 0x30, 0x00, // ## - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @1240 '?' (14 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x0F, 0x80, // ##### - 0x1F, 0xC0, // ####### - 0x18, 0x60, // ## ## - 0x18, 0x60, // ## ## - 0x00, 0x60, // ## - 0x01, 0xC0, // ### - 0x03, 0x80, // ### - 0x03, 0x00, // ## - 0x00, 0x00, // - 0x00, 0x00, // - 0x07, 0x00, // ### - 0x07, 0x00, // ### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @1280 '@' (14 pixels wide) - 0x00, 0x00, // - 0x03, 0x80, // ### - 0x0C, 0x80, // ## # - 0x08, 0x40, // # # - 0x10, 0x40, // # # - 0x10, 0x40, // # # - 0x11, 0xC0, // # ### - 0x12, 0x40, // # # # - 0x12, 0x40, // # # # - 0x12, 0x40, // # # # - 0x11, 0xC0, // # ### - 0x10, 0x00, // # - 0x08, 0x00, // # - 0x08, 0x40, // # # - 0x07, 0x80, // #### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @1320 'A' (14 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x1F, 0x80, // ###### - 0x1F, 0x80, // ###### - 0x03, 0x80, // ### - 0x06, 0xC0, // ## ## - 0x06, 0xC0, // ## ## - 0x0C, 0xC0, // ## ## - 0x0C, 0x60, // ## ## - 0x1F, 0xE0, // ######## - 0x1F, 0xE0, // ######## - 0x30, 0x30, // ## ## - 0x78, 0x78, // #### #### - 0x78, 0x78, // #### #### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @1360 'B' (14 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x3F, 0x80, // ####### - 0x3F, 0xC0, // ######## - 0x18, 0x60, // ## ## - 0x18, 0x60, // ## ## - 0x18, 0xE0, // ## ### - 0x1F, 0xC0, // ####### - 0x1F, 0xE0, // ######## - 0x18, 0x70, // ## ### - 0x18, 0x30, // ## ## - 0x18, 0x30, // ## ## - 0x3F, 0xF0, // ########## - 0x3F, 0xE0, // ######### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @1400 'C' (14 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x07, 0xB0, // #### ## - 0x0F, 0xF0, // ######## - 0x1C, 0x70, // ### ### - 0x38, 0x30, // ### ## - 0x30, 0x00, // ## - 0x30, 0x00, // ## - 0x30, 0x00, // ## - 0x30, 0x00, // ## - 0x38, 0x30, // ### ## - 0x1C, 0x70, // ### ### - 0x0F, 0xE0, // ####### - 0x07, 0xC0, // ##### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @1440 'D' (14 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x7F, 0x80, // ######## - 0x7F, 0xC0, // ######### - 0x30, 0xE0, // ## ### - 0x30, 0x70, // ## ### - 0x30, 0x30, // ## ## - 0x30, 0x30, // ## ## - 0x30, 0x30, // ## ## - 0x30, 0x30, // ## ## - 0x30, 0x70, // ## ### - 0x30, 0xE0, // ## ### - 0x7F, 0xC0, // ######### - 0x7F, 0x80, // ######## - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @1480 'E' (14 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x3F, 0xF0, // ########## - 0x3F, 0xF0, // ########## - 0x18, 0x30, // ## ## - 0x18, 0x30, // ## ## - 0x19, 0x80, // ## ## - 0x1F, 0x80, // ###### - 0x1F, 0x80, // ###### - 0x19, 0x80, // ## ## - 0x18, 0x30, // ## ## - 0x18, 0x30, // ## ## - 0x3F, 0xF0, // ########## - 0x3F, 0xF0, // ########## - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @1520 'F' (14 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x3F, 0xF0, // ########## - 0x3F, 0xF0, // ########## - 0x18, 0x30, // ## ## - 0x18, 0x30, // ## ## - 0x19, 0x80, // ## ## - 0x1F, 0x80, // ###### - 0x1F, 0x80, // ###### - 0x19, 0x80, // ## ## - 0x18, 0x00, // ## - 0x18, 0x00, // ## - 0x3F, 0x00, // ###### - 0x3F, 0x00, // ###### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @1560 'G' (14 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x07, 0xB0, // #### ## - 0x1F, 0xF0, // ######### - 0x18, 0x70, // ## ### - 0x30, 0x30, // ## ## - 0x30, 0x00, // ## - 0x30, 0x00, // ## - 0x31, 0xF8, // ## ###### - 0x31, 0xF8, // ## ###### - 0x30, 0x30, // ## ## - 0x18, 0x30, // ## ## - 0x1F, 0xF0, // ######### - 0x07, 0xC0, // ##### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @1600 'H' (14 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x3C, 0xF0, // #### #### - 0x3C, 0xF0, // #### #### - 0x18, 0x60, // ## ## - 0x18, 0x60, // ## ## - 0x18, 0x60, // ## ## - 0x1F, 0xE0, // ######## - 0x1F, 0xE0, // ######## - 0x18, 0x60, // ## ## - 0x18, 0x60, // ## ## - 0x18, 0x60, // ## ## - 0x3C, 0xF0, // #### #### - 0x3C, 0xF0, // #### #### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @1640 'I' (14 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x1F, 0xE0, // ######## - 0x1F, 0xE0, // ######## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x1F, 0xE0, // ######## - 0x1F, 0xE0, // ######## - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @1680 'J' (14 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x03, 0xF8, // ####### - 0x03, 0xF8, // ####### - 0x00, 0x60, // ## - 0x00, 0x60, // ## - 0x00, 0x60, // ## - 0x00, 0x60, // ## - 0x30, 0x60, // ## ## - 0x30, 0x60, // ## ## - 0x30, 0x60, // ## ## - 0x30, 0xE0, // ## ### - 0x3F, 0xC0, // ######## - 0x0F, 0x80, // ##### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @1720 'K' (14 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x3E, 0xF8, // ##### ##### - 0x3E, 0xF8, // ##### ##### - 0x18, 0xE0, // ## ### - 0x19, 0x80, // ## ## - 0x1B, 0x00, // ## ## - 0x1F, 0x00, // ##### - 0x1D, 0x80, // ### ## - 0x18, 0xC0, // ## ## - 0x18, 0xC0, // ## ## - 0x18, 0x60, // ## ## - 0x3E, 0x78, // ##### #### - 0x3E, 0x38, // ##### ### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @1760 'L' (14 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x3F, 0x00, // ###### - 0x3F, 0x00, // ###### - 0x0C, 0x00, // ## - 0x0C, 0x00, // ## - 0x0C, 0x00, // ## - 0x0C, 0x00, // ## - 0x0C, 0x00, // ## - 0x0C, 0x30, // ## ## - 0x0C, 0x30, // ## ## - 0x0C, 0x30, // ## ## - 0x3F, 0xF0, // ########## - 0x3F, 0xF0, // ########## - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @1800 'M' (14 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x78, 0x78, // #### #### - 0x78, 0x78, // #### #### - 0x38, 0x70, // ### ### - 0x3C, 0xF0, // #### #### - 0x34, 0xB0, // ## # # ## - 0x37, 0xB0, // ## #### ## - 0x37, 0xB0, // ## #### ## - 0x33, 0x30, // ## ## ## - 0x33, 0x30, // ## ## ## - 0x30, 0x30, // ## ## - 0x7C, 0xF8, // ##### ##### - 0x7C, 0xF8, // ##### ##### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @1840 'N' (14 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x39, 0xF0, // ### ##### - 0x3D, 0xF0, // #### ##### - 0x1C, 0x60, // ### ## - 0x1E, 0x60, // #### ## - 0x1E, 0x60, // #### ## - 0x1B, 0x60, // ## ## ## - 0x1B, 0x60, // ## ## ## - 0x19, 0xE0, // ## #### - 0x19, 0xE0, // ## #### - 0x18, 0xE0, // ## ### - 0x3E, 0xE0, // ##### ### - 0x3E, 0x60, // ##### ## - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @1880 'O' (14 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x07, 0x80, // #### - 0x0F, 0xC0, // ###### - 0x1C, 0xE0, // ### ### - 0x38, 0x70, // ### ### - 0x30, 0x30, // ## ## - 0x30, 0x30, // ## ## - 0x30, 0x30, // ## ## - 0x30, 0x30, // ## ## - 0x38, 0x70, // ### ### - 0x1C, 0xE0, // ### ### - 0x0F, 0xC0, // ###### - 0x07, 0x80, // #### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @1920 'P' (14 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x3F, 0xC0, // ######## - 0x3F, 0xE0, // ######### - 0x18, 0x70, // ## ### - 0x18, 0x30, // ## ## - 0x18, 0x30, // ## ## - 0x18, 0x70, // ## ### - 0x1F, 0xE0, // ######## - 0x1F, 0xC0, // ####### - 0x18, 0x00, // ## - 0x18, 0x00, // ## - 0x3F, 0x00, // ###### - 0x3F, 0x00, // ###### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @1960 'Q' (14 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x07, 0x80, // #### - 0x0F, 0xC0, // ###### - 0x1C, 0xE0, // ### ### - 0x38, 0x70, // ### ### - 0x30, 0x30, // ## ## - 0x30, 0x30, // ## ## - 0x30, 0x30, // ## ## - 0x30, 0x30, // ## ## - 0x38, 0x70, // ### ### - 0x1C, 0xE0, // ### ### - 0x0F, 0xC0, // ###### - 0x07, 0x80, // #### - 0x07, 0xB0, // #### ## - 0x0F, 0xF0, // ######## - 0x0C, 0xE0, // ## ### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @2000 'R' (14 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x3F, 0xC0, // ######## - 0x3F, 0xE0, // ######### - 0x18, 0x70, // ## ### - 0x18, 0x30, // ## ## - 0x18, 0x70, // ## ### - 0x1F, 0xE0, // ######## - 0x1F, 0xC0, // ####### - 0x18, 0xE0, // ## ### - 0x18, 0x60, // ## ## - 0x18, 0x70, // ## ### - 0x3E, 0x38, // ##### ### - 0x3E, 0x18, // ##### ## - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @2040 'S' (14 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x0F, 0xB0, // ##### ## - 0x1F, 0xF0, // ######### - 0x38, 0x70, // ### ### - 0x30, 0x30, // ## ## - 0x38, 0x00, // ### - 0x1F, 0x80, // ###### - 0x07, 0xE0, // ###### - 0x00, 0x70, // ### - 0x30, 0x30, // ## ## - 0x38, 0x70, // ### ### - 0x3F, 0xE0, // ######### - 0x37, 0xC0, // ## ##### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @2080 'T' (14 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x3F, 0xF0, // ########## - 0x3F, 0xF0, // ########## - 0x33, 0x30, // ## ## ## - 0x33, 0x30, // ## ## ## - 0x33, 0x30, // ## ## ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x0F, 0xC0, // ###### - 0x0F, 0xC0, // ###### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @2120 'U' (14 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x3C, 0xF0, // #### #### - 0x3C, 0xF0, // #### #### - 0x18, 0x60, // ## ## - 0x18, 0x60, // ## ## - 0x18, 0x60, // ## ## - 0x18, 0x60, // ## ## - 0x18, 0x60, // ## ## - 0x18, 0x60, // ## ## - 0x18, 0x60, // ## ## - 0x1C, 0xE0, // ### ### - 0x0F, 0xC0, // ###### - 0x07, 0x80, // #### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @2160 'V' (14 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x78, 0xF0, // #### #### - 0x78, 0xF0, // #### #### - 0x30, 0x60, // ## ## - 0x30, 0x60, // ## ## - 0x18, 0xC0, // ## ## - 0x18, 0xC0, // ## ## - 0x0D, 0x80, // ## ## - 0x0D, 0x80, // ## ## - 0x0D, 0x80, // ## ## - 0x07, 0x00, // ### - 0x07, 0x00, // ### - 0x07, 0x00, // ### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @2200 'W' (14 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x7C, 0x7C, // ##### ##### - 0x7C, 0x7C, // ##### ##### - 0x30, 0x18, // ## ## - 0x33, 0x98, // ## ### ## - 0x33, 0x98, // ## ### ## - 0x33, 0x98, // ## ### ## - 0x36, 0xD8, // ## ## ## ## - 0x16, 0xD0, // # ## ## # - 0x1C, 0x70, // ### ### - 0x1C, 0x70, // ### ### - 0x1C, 0x70, // ### ### - 0x18, 0x30, // ## ## - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @2240 'X' (14 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x78, 0xF0, // #### #### - 0x78, 0xF0, // #### #### - 0x30, 0x60, // ## ## - 0x18, 0xC0, // ## ## - 0x0D, 0x80, // ## ## - 0x07, 0x00, // ### - 0x07, 0x00, // ### - 0x0D, 0x80, // ## ## - 0x18, 0xC0, // ## ## - 0x30, 0x60, // ## ## - 0x78, 0xF0, // #### #### - 0x78, 0xF0, // #### #### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @2280 'Y' (14 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x3C, 0xF0, // #### #### - 0x3C, 0xF0, // #### #### - 0x18, 0x60, // ## ## - 0x0C, 0xC0, // ## ## - 0x07, 0x80, // #### - 0x07, 0x80, // #### - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x0F, 0xC0, // ###### - 0x0F, 0xC0, // ###### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @2320 'Z' (14 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x1F, 0xE0, // ######## - 0x1F, 0xE0, // ######## - 0x18, 0x60, // ## ## - 0x18, 0xC0, // ## ## - 0x01, 0x80, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x06, 0x00, // ## - 0x0C, 0x60, // ## ## - 0x18, 0x60, // ## ## - 0x1F, 0xE0, // ######## - 0x1F, 0xE0, // ######## - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @2360 '[' (14 pixels wide) - 0x00, 0x00, // - 0x03, 0xC0, // #### - 0x03, 0xC0, // #### - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0xC0, // #### - 0x03, 0xC0, // #### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @2400 '\' (14 pixels wide) - 0x18, 0x00, // ## - 0x18, 0x00, // ## - 0x0C, 0x00, // ## - 0x0C, 0x00, // ## - 0x0C, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x01, 0x80, // ## - 0x01, 0x80, // ## - 0x00, 0xC0, // ## - 0x00, 0xC0, // ## - 0x00, 0xC0, // ## - 0x00, 0x60, // ## - 0x00, 0x60, // ## - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @2440 ']' (14 pixels wide) - 0x00, 0x00, // - 0x0F, 0x00, // #### - 0x0F, 0x00, // #### - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x0F, 0x00, // #### - 0x0F, 0x00, // #### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @2480 '^' (14 pixels wide) - 0x00, 0x00, // - 0x02, 0x00, // # - 0x07, 0x00, // ### - 0x0D, 0x80, // ## ## - 0x18, 0xC0, // ## ## - 0x30, 0x60, // ## ## - 0x20, 0x20, // # # - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @2520 '_' (14 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0xFF, 0xFC, // ############## - 0xFF, 0xFC, // ############## - - // @2560 '`' (14 pixels wide) - 0x00, 0x00, // - 0x04, 0x00, // # - 0x03, 0x00, // ## - 0x00, 0x80, // # - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @2600 'a' (14 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x0F, 0xC0, // ###### - 0x1F, 0xE0, // ######## - 0x00, 0x60, // ## - 0x0F, 0xE0, // ####### - 0x1F, 0xE0, // ######## - 0x38, 0x60, // ### ## - 0x30, 0xE0, // ## ### - 0x3F, 0xF0, // ########## - 0x1F, 0x70, // ##### ### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @2640 'b' (14 pixels wide) - 0x00, 0x00, // - 0x70, 0x00, // ### - 0x70, 0x00, // ### - 0x30, 0x00, // ## - 0x30, 0x00, // ## - 0x37, 0x80, // ## #### - 0x3F, 0xE0, // ######### - 0x38, 0x60, // ### ## - 0x30, 0x30, // ## ## - 0x30, 0x30, // ## ## - 0x30, 0x30, // ## ## - 0x38, 0x60, // ### ## - 0x7F, 0xE0, // ########## - 0x77, 0x80, // ### #### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @2680 'c' (14 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x07, 0xB0, // #### ## - 0x1F, 0xF0, // ######### - 0x18, 0x30, // ## ## - 0x30, 0x30, // ## ## - 0x30, 0x00, // ## - 0x30, 0x00, // ## - 0x38, 0x30, // ### ## - 0x1F, 0xF0, // ######### - 0x0F, 0xC0, // ###### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @2720 'd' (14 pixels wide) - 0x00, 0x00, // - 0x00, 0x70, // ### - 0x00, 0x70, // ### - 0x00, 0x30, // ## - 0x00, 0x30, // ## - 0x07, 0xB0, // #### ## - 0x1F, 0xF0, // ######### - 0x18, 0x70, // ## ### - 0x30, 0x30, // ## ## - 0x30, 0x30, // ## ## - 0x30, 0x30, // ## ## - 0x38, 0x70, // ### ### - 0x1F, 0xF8, // ########## - 0x07, 0xB8, // #### ### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @2760 'e' (14 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x07, 0x80, // #### - 0x1F, 0xE0, // ######## - 0x18, 0x60, // ## ## - 0x3F, 0xF0, // ########## - 0x3F, 0xF0, // ########## - 0x30, 0x00, // ## - 0x18, 0x30, // ## ## - 0x1F, 0xF0, // ######### - 0x07, 0xC0, // ##### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @2800 'f' (14 pixels wide) - 0x00, 0x00, // - 0x03, 0xF0, // ###### - 0x07, 0xF0, // ####### - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x1F, 0xE0, // ######## - 0x1F, 0xE0, // ######## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x1F, 0xE0, // ######## - 0x1F, 0xE0, // ######## - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @2840 'g' (14 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x07, 0xB8, // #### ### - 0x1F, 0xF8, // ########## - 0x18, 0x70, // ## ### - 0x30, 0x30, // ## ## - 0x30, 0x30, // ## ## - 0x30, 0x30, // ## ## - 0x18, 0x70, // ## ### - 0x1F, 0xF0, // ######### - 0x07, 0xB0, // #### ## - 0x00, 0x30, // ## - 0x00, 0x70, // ### - 0x0F, 0xE0, // ####### - 0x0F, 0xC0, // ###### - 0x00, 0x00, // - 0x00, 0x00, // - - // @2880 'h' (14 pixels wide) - 0x00, 0x00, // - 0x38, 0x00, // ### - 0x38, 0x00, // ### - 0x18, 0x00, // ## - 0x18, 0x00, // ## - 0x1B, 0xC0, // ## #### - 0x1F, 0xE0, // ######## - 0x1C, 0x60, // ### ## - 0x18, 0x60, // ## ## - 0x18, 0x60, // ## ## - 0x18, 0x60, // ## ## - 0x18, 0x60, // ## ## - 0x3C, 0xF0, // #### #### - 0x3C, 0xF0, // #### #### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @2920 'i' (14 pixels wide) - 0x00, 0x00, // - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x00, 0x00, // - 0x00, 0x00, // - 0x1F, 0x00, // ##### - 0x1F, 0x00, // ##### - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x1F, 0xE0, // ######## - 0x1F, 0xE0, // ######## - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @2960 'j' (14 pixels wide) - 0x00, 0x00, // - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x00, 0x00, // - 0x00, 0x00, // - 0x1F, 0xC0, // ####### - 0x1F, 0xC0, // ####### - 0x00, 0xC0, // ## - 0x00, 0xC0, // ## - 0x00, 0xC0, // ## - 0x00, 0xC0, // ## - 0x00, 0xC0, // ## - 0x00, 0xC0, // ## - 0x00, 0xC0, // ## - 0x00, 0xC0, // ## - 0x01, 0xC0, // ### - 0x3F, 0x80, // ####### - 0x3F, 0x00, // ###### - 0x00, 0x00, // - 0x00, 0x00, // - - // @3000 'k' (14 pixels wide) - 0x00, 0x00, // - 0x38, 0x00, // ### - 0x38, 0x00, // ### - 0x18, 0x00, // ## - 0x18, 0x00, // ## - 0x1B, 0xE0, // ## ##### - 0x1B, 0xE0, // ## ##### - 0x1B, 0x00, // ## ## - 0x1E, 0x00, // #### - 0x1E, 0x00, // #### - 0x1B, 0x00, // ## ## - 0x19, 0x80, // ## ## - 0x39, 0xF0, // ### ##### - 0x39, 0xF0, // ### ##### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @3040 'l' (14 pixels wide) - 0x00, 0x00, // - 0x1F, 0x00, // ##### - 0x1F, 0x00, // ##### - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x1F, 0xE0, // ######## - 0x1F, 0xE0, // ######## - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @3080 'm' (14 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x7E, 0xE0, // ###### ### - 0x7F, 0xF0, // ########### - 0x33, 0x30, // ## ## ## - 0x33, 0x30, // ## ## ## - 0x33, 0x30, // ## ## ## - 0x33, 0x30, // ## ## ## - 0x33, 0x30, // ## ## ## - 0x7B, 0xB8, // #### ### ### - 0x7B, 0xB8, // #### ### ### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @3120 'n' (14 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x3B, 0xC0, // ### #### - 0x3F, 0xE0, // ######### - 0x1C, 0x60, // ### ## - 0x18, 0x60, // ## ## - 0x18, 0x60, // ## ## - 0x18, 0x60, // ## ## - 0x18, 0x60, // ## ## - 0x3C, 0xF0, // #### #### - 0x3C, 0xF0, // #### #### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @3160 'o' (14 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x07, 0x80, // #### - 0x1F, 0xE0, // ######## - 0x18, 0x60, // ## ## - 0x30, 0x30, // ## ## - 0x30, 0x30, // ## ## - 0x30, 0x30, // ## ## - 0x18, 0x60, // ## ## - 0x1F, 0xE0, // ######## - 0x07, 0x80, // #### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @3200 'p' (14 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x77, 0x80, // ### #### - 0x7F, 0xE0, // ########## - 0x38, 0x60, // ### ## - 0x30, 0x30, // ## ## - 0x30, 0x30, // ## ## - 0x30, 0x30, // ## ## - 0x38, 0x60, // ### ## - 0x3F, 0xE0, // ######### - 0x37, 0x80, // ## #### - 0x30, 0x00, // ## - 0x30, 0x00, // ## - 0x7C, 0x00, // ##### - 0x7C, 0x00, // ##### - 0x00, 0x00, // - 0x00, 0x00, // - - // @3240 'q' (14 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x07, 0xB8, // #### ### - 0x1F, 0xF8, // ########## - 0x18, 0x70, // ## ### - 0x30, 0x30, // ## ## - 0x30, 0x30, // ## ## - 0x30, 0x30, // ## ## - 0x18, 0x70, // ## ### - 0x1F, 0xF0, // ######### - 0x07, 0xB0, // #### ## - 0x00, 0x30, // ## - 0x00, 0x30, // ## - 0x00, 0xF8, // ##### - 0x00, 0xF8, // ##### - 0x00, 0x00, // - 0x00, 0x00, // - - // @3280 'r' (14 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x3C, 0xE0, // #### ### - 0x3D, 0xF0, // #### ##### - 0x0F, 0x30, // #### ## - 0x0E, 0x00, // ### - 0x0C, 0x00, // ## - 0x0C, 0x00, // ## - 0x0C, 0x00, // ## - 0x3F, 0xC0, // ######## - 0x3F, 0xC0, // ######## - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @3320 's' (14 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x07, 0xE0, // ###### - 0x1F, 0xE0, // ######## - 0x18, 0x60, // ## ## - 0x1E, 0x00, // #### - 0x0F, 0xC0, // ###### - 0x01, 0xE0, // #### - 0x18, 0x60, // ## ## - 0x1F, 0xE0, // ######## - 0x1F, 0x80, // ###### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @3360 't' (14 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x0C, 0x00, // ## - 0x0C, 0x00, // ## - 0x0C, 0x00, // ## - 0x3F, 0xE0, // ######### - 0x3F, 0xE0, // ######### - 0x0C, 0x00, // ## - 0x0C, 0x00, // ## - 0x0C, 0x00, // ## - 0x0C, 0x00, // ## - 0x0C, 0x30, // ## ## - 0x0F, 0xF0, // ######## - 0x07, 0xC0, // ##### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @3400 'u' (14 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x38, 0xE0, // ### ### - 0x38, 0xE0, // ### ### - 0x18, 0x60, // ## ## - 0x18, 0x60, // ## ## - 0x18, 0x60, // ## ## - 0x18, 0x60, // ## ## - 0x18, 0xE0, // ## ### - 0x1F, 0xF0, // ######### - 0x0F, 0x70, // #### ### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @3440 'v' (14 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x78, 0xF0, // #### #### - 0x78, 0xF0, // #### #### - 0x30, 0x60, // ## ## - 0x18, 0xC0, // ## ## - 0x18, 0xC0, // ## ## - 0x0D, 0x80, // ## ## - 0x0D, 0x80, // ## ## - 0x07, 0x00, // ### - 0x07, 0x00, // ### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @3480 'w' (14 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x78, 0xF0, // #### #### - 0x78, 0xF0, // #### #### - 0x32, 0x60, // ## # ## - 0x32, 0x60, // ## # ## - 0x37, 0xE0, // ## ###### - 0x1D, 0xC0, // ### ### - 0x1D, 0xC0, // ### ### - 0x18, 0xC0, // ## ## - 0x18, 0xC0, // ## ## - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @3520 'x' (14 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x3C, 0xF0, // #### #### - 0x3C, 0xF0, // #### #### - 0x0C, 0xC0, // ## ## - 0x07, 0x80, // #### - 0x03, 0x00, // ## - 0x07, 0x80, // #### - 0x0C, 0xC0, // ## ## - 0x3C, 0xF0, // #### #### - 0x3C, 0xF0, // #### #### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @3560 'y' (14 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x78, 0xF0, // #### #### - 0x78, 0xF0, // #### #### - 0x30, 0x60, // ## ## - 0x18, 0xC0, // ## ## - 0x18, 0xC0, // ## ## - 0x0D, 0x80, // ## ## - 0x0F, 0x80, // ##### - 0x07, 0x00, // ### - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x0C, 0x00, // ## - 0x7F, 0x00, // ####### - 0x7F, 0x00, // ####### - 0x00, 0x00, // - 0x00, 0x00, // - - // @3600 'z' (14 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x1F, 0xE0, // ######## - 0x1F, 0xE0, // ######## - 0x18, 0xC0, // ## ## - 0x01, 0x80, // ## - 0x03, 0x00, // ## - 0x06, 0x00, // ## - 0x0C, 0x60, // ## ## - 0x1F, 0xE0, // ######## - 0x1F, 0xE0, // ######## - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @3640 '{' (14 pixels wide) - 0x00, 0x00, // - 0x01, 0xC0, // ### - 0x03, 0xC0, // #### - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x07, 0x00, // ### - 0x0E, 0x00, // ### - 0x07, 0x00, // ### - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0xC0, // #### - 0x01, 0xC0, // ### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @3680 '|' (14 pixels wide) - 0x00, 0x00, // - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x03, 0x00, // ## - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @3720 '}' (14 pixels wide) - 0x00, 0x00, // - 0x1C, 0x00, // ### - 0x1E, 0x00, // #### - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x07, 0x00, // ### - 0x03, 0x80, // ### - 0x07, 0x00, // ### - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x06, 0x00, // ## - 0x1E, 0x00, // #### - 0x1C, 0x00, // ### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - - // @3760 '~' (14 pixels wide) - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x0E, 0x00, // ### - 0x3F, 0x30, // ###### ## - 0x33, 0xF0, // ## ###### - 0x01, 0xE0, // #### - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // - 0x00, 0x00, // -}; - - -sFONT Font20 = { - Font20_Table, - 14, /* Width */ - 20, /* Height */ -}; - -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.0/src/font24.c b/lib/esp-epaper-29-ws-20171230-gemu-1.0/src/font24.c deleted file mode 100644 index 360a204c5..000000000 --- a/lib/esp-epaper-29-ws-20171230-gemu-1.0/src/font24.c +++ /dev/null @@ -1,2521 +0,0 @@ -/** - ****************************************************************************** - * @file font24.c - * @author MCD Application Team - * @version V1.0.0 - * @date 18-February-2014 - * @brief This file provides text font24 for STM32xx-EVAL's LCD driver. - ****************************************************************************** - * @attention - * - *

© COPYRIGHT(c) 2014 STMicroelectronics

- * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. 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. - * 3. Neither the name of STMicroelectronics nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * 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. - * - ****************************************************************************** - */ - -/* Includes ------------------------------------------------------------------*/ -#include "fonts.h" -#include - -const uint8_t Font24_Table [] PROGMEM = -{ - // @0 ' ' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @72 '!' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x03, 0x80, 0x00, // ### - 0x03, 0x80, 0x00, // ### - 0x03, 0x80, 0x00, // ### - 0x03, 0x80, 0x00, // ### - 0x03, 0x80, 0x00, // ### - 0x03, 0x80, 0x00, // ### - 0x03, 0x80, 0x00, // ### - 0x03, 0x80, 0x00, // ### - 0x03, 0x80, 0x00, // ### - 0x01, 0x00, 0x00, // # - 0x01, 0x00, 0x00, // # - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x03, 0x80, 0x00, // ### - 0x03, 0x80, 0x00, // ### - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @144 '"' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x0E, 0x70, 0x00, // ### ### - 0x0E, 0x70, 0x00, // ### ### - 0x0E, 0x70, 0x00, // ### ### - 0x04, 0x20, 0x00, // # # - 0x04, 0x20, 0x00, // # # - 0x04, 0x20, 0x00, // # # - 0x04, 0x20, 0x00, // # # - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @216 '#' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x06, 0x60, 0x00, // ## ## - 0x06, 0x60, 0x00, // ## ## - 0x06, 0x60, 0x00, // ## ## - 0x06, 0x60, 0x00, // ## ## - 0x06, 0x60, 0x00, // ## ## - 0x3F, 0xF8, 0x00, // ########### - 0x3F, 0xF8, 0x00, // ########### - 0x06, 0x60, 0x00, // ## ## - 0x0C, 0xC0, 0x00, // ## ## - 0x3F, 0xF8, 0x00, // ########### - 0x3F, 0xF8, 0x00, // ########### - 0x0C, 0xC0, 0x00, // ## ## - 0x0C, 0xC0, 0x00, // ## ## - 0x0C, 0xC0, 0x00, // ## ## - 0x0C, 0xC0, 0x00, // ## ## - 0x0C, 0xC0, 0x00, // ## ## - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @288 '$' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x07, 0xB0, 0x00, // #### ## - 0x0F, 0xF0, 0x00, // ######## - 0x18, 0x70, 0x00, // ## ### - 0x18, 0x70, 0x00, // ## ### - 0x1C, 0x00, 0x00, // ### - 0x0F, 0x80, 0x00, // ##### - 0x07, 0xE0, 0x00, // ###### - 0x00, 0xF0, 0x00, // #### - 0x18, 0x30, 0x00, // ## ## - 0x1C, 0x30, 0x00, // ### ## - 0x1C, 0x70, 0x00, // ### ### - 0x1F, 0xE0, 0x00, // ######## - 0x1B, 0xC0, 0x00, // ## #### - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @360 '%' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x07, 0x80, 0x00, // #### - 0x0F, 0xC0, 0x00, // ###### - 0x1C, 0xE0, 0x00, // ### ### - 0x18, 0x60, 0x00, // ## ## - 0x18, 0x60, 0x00, // ## ## - 0x1C, 0xE0, 0x00, // ### ### - 0x0F, 0xF8, 0x00, // ######### - 0x07, 0xE0, 0x00, // ###### - 0x1F, 0xF0, 0x00, // ######### - 0x07, 0x38, 0x00, // ### ### - 0x06, 0x18, 0x00, // ## ## - 0x06, 0x18, 0x00, // ## ## - 0x07, 0x38, 0x00, // ### ### - 0x03, 0xF0, 0x00, // ###### - 0x01, 0xE0, 0x00, // #### - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @432 '&' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x03, 0xF0, 0x00, // ###### - 0x07, 0xF0, 0x00, // ####### - 0x0C, 0x60, 0x00, // ## ## - 0x0C, 0x00, 0x00, // ## - 0x0C, 0x00, 0x00, // ## - 0x06, 0x00, 0x00, // ## - 0x07, 0x00, 0x00, // ### - 0x0F, 0x9C, 0x00, // ##### ### - 0x1D, 0xFC, 0x00, // ### ####### - 0x18, 0xF0, 0x00, // ## #### - 0x18, 0x70, 0x00, // ## ### - 0x0F, 0xFC, 0x00, // ########## - 0x07, 0xDC, 0x00, // ##### ### - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @504 ''' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x03, 0x80, 0x00, // ### - 0x03, 0x80, 0x00, // ### - 0x03, 0x80, 0x00, // ### - 0x01, 0x00, 0x00, // # - 0x01, 0x00, 0x00, // # - 0x01, 0x00, 0x00, // # - 0x01, 0x00, 0x00, // # - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @576 '(' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x18, 0x00, // ## - 0x00, 0x38, 0x00, // ### - 0x00, 0x70, 0x00, // ### - 0x00, 0xF0, 0x00, // #### - 0x00, 0xE0, 0x00, // ### - 0x00, 0xE0, 0x00, // ### - 0x01, 0xC0, 0x00, // ### - 0x01, 0xC0, 0x00, // ### - 0x01, 0xC0, 0x00, // ### - 0x01, 0xC0, 0x00, // ### - 0x01, 0xC0, 0x00, // ### - 0x01, 0xC0, 0x00, // ### - 0x00, 0xE0, 0x00, // ### - 0x00, 0xE0, 0x00, // ### - 0x00, 0x70, 0x00, // ### - 0x00, 0x70, 0x00, // ### - 0x00, 0x38, 0x00, // ### - 0x00, 0x18, 0x00, // ## - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @648 ')' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x18, 0x00, 0x00, // ## - 0x1C, 0x00, 0x00, // ### - 0x0E, 0x00, 0x00, // ### - 0x0E, 0x00, 0x00, // ### - 0x07, 0x00, 0x00, // ### - 0x07, 0x00, 0x00, // ### - 0x03, 0x80, 0x00, // ### - 0x03, 0x80, 0x00, // ### - 0x03, 0x80, 0x00, // ### - 0x03, 0x80, 0x00, // ### - 0x03, 0x80, 0x00, // ### - 0x03, 0x80, 0x00, // ### - 0x07, 0x00, 0x00, // ### - 0x07, 0x00, 0x00, // ### - 0x0F, 0x00, 0x00, // #### - 0x0E, 0x00, 0x00, // ### - 0x1C, 0x00, 0x00, // ### - 0x18, 0x00, 0x00, // ## - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @720 '*' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x1D, 0xB8, 0x00, // ### ## ### - 0x1F, 0xF8, 0x00, // ########## - 0x07, 0xE0, 0x00, // ###### - 0x03, 0xC0, 0x00, // #### - 0x03, 0xC0, 0x00, // #### - 0x06, 0x60, 0x00, // ## ## - 0x06, 0x60, 0x00, // ## ## - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @792 '+' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x3F, 0xFC, 0x00, // ############ - 0x3F, 0xFC, 0x00, // ############ - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @864 ',' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0xE0, 0x00, // ### - 0x00, 0xC0, 0x00, // ## - 0x01, 0xC0, 0x00, // ### - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x03, 0x00, 0x00, // ## - 0x03, 0x00, 0x00, // ## - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @936 '-' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x1F, 0xF8, 0x00, // ########## - 0x1F, 0xF8, 0x00, // ########## - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @1008 '.' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x03, 0xC0, 0x00, // #### - 0x03, 0xC0, 0x00, // #### - 0x03, 0xC0, 0x00, // #### - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @1080 '/' (17 pixels wide) - 0x00, 0x18, 0x00, // ## - 0x00, 0x18, 0x00, // ## - 0x00, 0x38, 0x00, // ### - 0x00, 0x30, 0x00, // ## - 0x00, 0x70, 0x00, // ### - 0x00, 0x60, 0x00, // ## - 0x00, 0x60, 0x00, // ## - 0x00, 0xC0, 0x00, // ## - 0x00, 0xC0, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x03, 0x00, 0x00, // ## - 0x03, 0x00, 0x00, // ## - 0x06, 0x00, 0x00, // ## - 0x06, 0x00, 0x00, // ## - 0x0E, 0x00, 0x00, // ### - 0x0C, 0x00, 0x00, // ## - 0x1C, 0x00, 0x00, // ### - 0x18, 0x00, 0x00, // ## - 0x18, 0x00, 0x00, // ## - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @1152 '0' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x03, 0xC0, 0x00, // #### - 0x07, 0xE0, 0x00, // ###### - 0x0C, 0x30, 0x00, // ## ## - 0x0C, 0x30, 0x00, // ## ## - 0x18, 0x18, 0x00, // ## ## - 0x18, 0x18, 0x00, // ## ## - 0x18, 0x18, 0x00, // ## ## - 0x18, 0x18, 0x00, // ## ## - 0x18, 0x18, 0x00, // ## ## - 0x18, 0x18, 0x00, // ## ## - 0x18, 0x18, 0x00, // ## ## - 0x0C, 0x30, 0x00, // ## ## - 0x0C, 0x30, 0x00, // ## ## - 0x07, 0xE0, 0x00, // ###### - 0x03, 0xC0, 0x00, // #### - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @1224 '1' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x80, 0x00, // # - 0x07, 0x80, 0x00, // #### - 0x1F, 0x80, 0x00, // ###### - 0x1D, 0x80, 0x00, // ### ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x1F, 0xF8, 0x00, // ########## - 0x1F, 0xF8, 0x00, // ########## - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @1296 '2' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x07, 0xC0, 0x00, // ##### - 0x1F, 0xF0, 0x00, // ######### - 0x38, 0x30, 0x00, // ### ## - 0x30, 0x18, 0x00, // ## ## - 0x30, 0x18, 0x00, // ## ## - 0x00, 0x18, 0x00, // ## - 0x00, 0x30, 0x00, // ## - 0x00, 0x60, 0x00, // ## - 0x01, 0xC0, 0x00, // ### - 0x03, 0x80, 0x00, // ### - 0x06, 0x00, 0x00, // ## - 0x0C, 0x00, 0x00, // ## - 0x18, 0x00, 0x00, // ## - 0x3F, 0xF8, 0x00, // ########### - 0x3F, 0xF8, 0x00, // ########### - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @1368 '3' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x03, 0xC0, 0x00, // #### - 0x0F, 0xE0, 0x00, // ####### - 0x0C, 0x70, 0x00, // ## ### - 0x00, 0x30, 0x00, // ## - 0x00, 0x30, 0x00, // ## - 0x00, 0x60, 0x00, // ## - 0x03, 0xC0, 0x00, // #### - 0x03, 0xE0, 0x00, // ##### - 0x00, 0x70, 0x00, // ### - 0x00, 0x18, 0x00, // ## - 0x00, 0x18, 0x00, // ## - 0x00, 0x18, 0x00, // ## - 0x18, 0x38, 0x00, // ## ### - 0x1F, 0xF0, 0x00, // ######### - 0x0F, 0xC0, 0x00, // ###### - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @1440 '4' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0xE0, 0x00, // ### - 0x01, 0xE0, 0x00, // #### - 0x01, 0xE0, 0x00, // #### - 0x03, 0x60, 0x00, // ## ## - 0x06, 0x60, 0x00, // ## ## - 0x06, 0x60, 0x00, // ## ## - 0x0C, 0x60, 0x00, // ## ## - 0x0C, 0x60, 0x00, // ## ## - 0x18, 0x60, 0x00, // ## ## - 0x30, 0x60, 0x00, // ## ## - 0x3F, 0xF8, 0x00, // ########### - 0x3F, 0xF8, 0x00, // ########### - 0x00, 0x60, 0x00, // ## - 0x03, 0xF8, 0x00, // ####### - 0x03, 0xF8, 0x00, // ####### - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @1512 '5' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x1F, 0xF0, 0x00, // ######### - 0x1F, 0xF0, 0x00, // ######### - 0x18, 0x00, 0x00, // ## - 0x18, 0x00, 0x00, // ## - 0x18, 0x00, 0x00, // ## - 0x1B, 0xC0, 0x00, // ## #### - 0x1F, 0xF0, 0x00, // ######### - 0x1C, 0x30, 0x00, // ### ## - 0x00, 0x18, 0x00, // ## - 0x00, 0x18, 0x00, // ## - 0x00, 0x18, 0x00, // ## - 0x00, 0x18, 0x00, // ## - 0x30, 0x30, 0x00, // ## ## - 0x3F, 0xF0, 0x00, // ########## - 0x0F, 0xC0, 0x00, // ###### - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @1584 '6' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0xF8, 0x00, // ##### - 0x03, 0xF8, 0x00, // ####### - 0x07, 0x00, 0x00, // ### - 0x0E, 0x00, 0x00, // ### - 0x0C, 0x00, 0x00, // ## - 0x18, 0x00, 0x00, // ## - 0x1B, 0xC0, 0x00, // ## #### - 0x1F, 0xF0, 0x00, // ######### - 0x1C, 0x30, 0x00, // ### ## - 0x18, 0x18, 0x00, // ## ## - 0x18, 0x18, 0x00, // ## ## - 0x18, 0x18, 0x00, // ## ## - 0x0C, 0x38, 0x00, // ## ### - 0x0F, 0xF0, 0x00, // ######## - 0x03, 0xE0, 0x00, // ##### - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @1656 '7' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x1F, 0xF8, 0x00, // ########## - 0x1F, 0xF8, 0x00, // ########## - 0x18, 0x18, 0x00, // ## ## - 0x18, 0x38, 0x00, // ## ### - 0x00, 0x30, 0x00, // ## - 0x00, 0x30, 0x00, // ## - 0x00, 0x70, 0x00, // ### - 0x00, 0x60, 0x00, // ## - 0x00, 0x60, 0x00, // ## - 0x00, 0xE0, 0x00, // ### - 0x00, 0xC0, 0x00, // ## - 0x00, 0xC0, 0x00, // ## - 0x01, 0xC0, 0x00, // ### - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @1728 '8' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x07, 0xE0, 0x00, // ###### - 0x0F, 0xF0, 0x00, // ######## - 0x1C, 0x38, 0x00, // ### ### - 0x18, 0x18, 0x00, // ## ## - 0x18, 0x18, 0x00, // ## ## - 0x0C, 0x30, 0x00, // ## ## - 0x07, 0xE0, 0x00, // ###### - 0x07, 0xE0, 0x00, // ###### - 0x0C, 0x30, 0x00, // ## ## - 0x18, 0x18, 0x00, // ## ## - 0x18, 0x18, 0x00, // ## ## - 0x18, 0x18, 0x00, // ## ## - 0x1C, 0x38, 0x00, // ### ### - 0x0F, 0xF0, 0x00, // ######## - 0x07, 0xE0, 0x00, // ###### - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @1800 '9' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x07, 0xC0, 0x00, // ##### - 0x0F, 0xF0, 0x00, // ######## - 0x1C, 0x30, 0x00, // ### ## - 0x18, 0x18, 0x00, // ## ## - 0x18, 0x18, 0x00, // ## ## - 0x18, 0x18, 0x00, // ## ## - 0x0C, 0x38, 0x00, // ## ### - 0x0F, 0xF8, 0x00, // ######### - 0x03, 0xD8, 0x00, // #### ## - 0x00, 0x18, 0x00, // ## - 0x00, 0x30, 0x00, // ## - 0x00, 0x70, 0x00, // ### - 0x00, 0xE0, 0x00, // ### - 0x1F, 0xC0, 0x00, // ####### - 0x1F, 0x00, 0x00, // ##### - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @1872 ':' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x03, 0xC0, 0x00, // #### - 0x03, 0xC0, 0x00, // #### - 0x03, 0xC0, 0x00, // #### - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x03, 0xC0, 0x00, // #### - 0x03, 0xC0, 0x00, // #### - 0x03, 0xC0, 0x00, // #### - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @1944 ';' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0xF0, 0x00, // #### - 0x00, 0xF0, 0x00, // #### - 0x00, 0xF0, 0x00, // #### - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0xE0, 0x00, // ### - 0x01, 0xC0, 0x00, // ### - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x03, 0x00, 0x00, // ## - 0x02, 0x00, 0x00, // # - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @2016 '<' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x1C, 0x00, // ### - 0x00, 0x3C, 0x00, // #### - 0x00, 0xF0, 0x00, // #### - 0x03, 0xC0, 0x00, // #### - 0x0F, 0x00, 0x00, // #### - 0x3C, 0x00, 0x00, // #### - 0xF0, 0x00, 0x00, // #### - 0x3C, 0x00, 0x00, // #### - 0x0F, 0x00, 0x00, // #### - 0x03, 0xC0, 0x00, // #### - 0x00, 0xF0, 0x00, // #### - 0x00, 0x3C, 0x00, // #### - 0x00, 0x1C, 0x00, // ### - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @2088 '=' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x7F, 0xFC, 0x00, // ############# - 0x7F, 0xFC, 0x00, // ############# - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x7F, 0xFC, 0x00, // ############# - 0x7F, 0xFC, 0x00, // ############# - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @2160 '>' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x70, 0x00, 0x00, // ### - 0x78, 0x00, 0x00, // #### - 0x1E, 0x00, 0x00, // #### - 0x07, 0x80, 0x00, // #### - 0x01, 0xE0, 0x00, // #### - 0x00, 0x78, 0x00, // #### - 0x00, 0x1E, 0x00, // #### - 0x00, 0x78, 0x00, // #### - 0x01, 0xE0, 0x00, // #### - 0x07, 0x80, 0x00, // #### - 0x1E, 0x00, 0x00, // #### - 0x78, 0x00, 0x00, // #### - 0x70, 0x00, 0x00, // ### - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @2232 '?' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x07, 0xC0, 0x00, // ##### - 0x0F, 0xE0, 0x00, // ####### - 0x18, 0x70, 0x00, // ## ### - 0x18, 0x30, 0x00, // ## ## - 0x18, 0x30, 0x00, // ## ## - 0x00, 0x70, 0x00, // ### - 0x00, 0xE0, 0x00, // ### - 0x03, 0xC0, 0x00, // #### - 0x03, 0x80, 0x00, // ### - 0x03, 0x00, 0x00, // ## - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x07, 0x00, 0x00, // ### - 0x07, 0x00, 0x00, // ### - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @2304 '@' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x03, 0xE0, 0x00, // ##### - 0x07, 0xF0, 0x00, // ####### - 0x0E, 0x38, 0x00, // ### ### - 0x0C, 0x18, 0x00, // ## ## - 0x18, 0x78, 0x00, // ## #### - 0x18, 0xF8, 0x00, // ## ##### - 0x19, 0xD8, 0x00, // ## ### ## - 0x19, 0x98, 0x00, // ## ## ## - 0x19, 0x98, 0x00, // ## ## ## - 0x19, 0x98, 0x00, // ## ## ## - 0x18, 0xF8, 0x00, // ## ##### - 0x18, 0x78, 0x00, // ## #### - 0x18, 0x00, 0x00, // ## - 0x0C, 0x00, 0x00, // ## - 0x0E, 0x18, 0x00, // ### ## - 0x07, 0xF8, 0x00, // ######## - 0x03, 0xE0, 0x00, // ##### - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @2376 'A' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x1F, 0x80, 0x00, // ###### - 0x1F, 0xC0, 0x00, // ####### - 0x01, 0xC0, 0x00, // ### - 0x03, 0x60, 0x00, // ## ## - 0x03, 0x60, 0x00, // ## ## - 0x06, 0x30, 0x00, // ## ## - 0x06, 0x30, 0x00, // ## ## - 0x0C, 0x30, 0x00, // ## ## - 0x0F, 0xF8, 0x00, // ######### - 0x1F, 0xF8, 0x00, // ########## - 0x18, 0x0C, 0x00, // ## ## - 0x30, 0x0C, 0x00, // ## ## - 0xFC, 0x7F, 0x00, // ###### ####### - 0xFC, 0x7F, 0x00, // ###### ####### - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @2448 'B' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x7F, 0xE0, 0x00, // ########## - 0x7F, 0xF0, 0x00, // ########### - 0x18, 0x38, 0x00, // ## ### - 0x18, 0x18, 0x00, // ## ## - 0x18, 0x18, 0x00, // ## ## - 0x18, 0x38, 0x00, // ## ### - 0x1F, 0xF0, 0x00, // ######### - 0x1F, 0xF8, 0x00, // ########## - 0x18, 0x1C, 0x00, // ## ### - 0x18, 0x0C, 0x00, // ## ## - 0x18, 0x0C, 0x00, // ## ## - 0x18, 0x0C, 0x00, // ## ## - 0x7F, 0xF8, 0x00, // ############ - 0x7F, 0xF0, 0x00, // ########### - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @2520 'C' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x03, 0xEC, 0x00, // ##### ## - 0x0F, 0xFC, 0x00, // ########## - 0x1C, 0x1C, 0x00, // ### ### - 0x18, 0x0C, 0x00, // ## ## - 0x30, 0x0C, 0x00, // ## ## - 0x30, 0x00, 0x00, // ## - 0x30, 0x00, 0x00, // ## - 0x30, 0x00, 0x00, // ## - 0x30, 0x00, 0x00, // ## - 0x30, 0x00, 0x00, // ## - 0x18, 0x0C, 0x00, // ## ## - 0x1C, 0x1C, 0x00, // ### ### - 0x0F, 0xF8, 0x00, // ######### - 0x03, 0xF0, 0x00, // ###### - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @2592 'D' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x7F, 0xC0, 0x00, // ######### - 0x7F, 0xF0, 0x00, // ########### - 0x18, 0x38, 0x00, // ## ### - 0x18, 0x18, 0x00, // ## ## - 0x18, 0x0C, 0x00, // ## ## - 0x18, 0x0C, 0x00, // ## ## - 0x18, 0x0C, 0x00, // ## ## - 0x18, 0x0C, 0x00, // ## ## - 0x18, 0x0C, 0x00, // ## ## - 0x18, 0x0C, 0x00, // ## ## - 0x18, 0x18, 0x00, // ## ## - 0x18, 0x38, 0x00, // ## ### - 0x7F, 0xF0, 0x00, // ########### - 0x7F, 0xE0, 0x00, // ########## - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @2664 'E' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x7F, 0xF8, 0x00, // ############ - 0x7F, 0xF8, 0x00, // ############ - 0x18, 0x18, 0x00, // ## ## - 0x18, 0x18, 0x00, // ## ## - 0x19, 0x98, 0x00, // ## ## ## - 0x19, 0x80, 0x00, // ## ## - 0x1F, 0x80, 0x00, // ###### - 0x1F, 0x80, 0x00, // ###### - 0x19, 0x80, 0x00, // ## ## - 0x19, 0x98, 0x00, // ## ## ## - 0x18, 0x18, 0x00, // ## ## - 0x18, 0x18, 0x00, // ## ## - 0x7F, 0xF8, 0x00, // ############ - 0x7F, 0xF8, 0x00, // ############ - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @2736 'F' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x3F, 0xFC, 0x00, // ############ - 0x3F, 0xFC, 0x00, // ############ - 0x0C, 0x0C, 0x00, // ## ## - 0x0C, 0x0C, 0x00, // ## ## - 0x0C, 0xCC, 0x00, // ## ## ## - 0x0C, 0xC0, 0x00, // ## ## - 0x0F, 0xC0, 0x00, // ###### - 0x0F, 0xC0, 0x00, // ###### - 0x0C, 0xC0, 0x00, // ## ## - 0x0C, 0xC0, 0x00, // ## ## - 0x0C, 0x00, 0x00, // ## - 0x0C, 0x00, 0x00, // ## - 0x3F, 0xC0, 0x00, // ######## - 0x3F, 0xC0, 0x00, // ######## - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @2808 'G' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x03, 0xEC, 0x00, // ##### ## - 0x0F, 0xFC, 0x00, // ########## - 0x1C, 0x1C, 0x00, // ### ### - 0x18, 0x0C, 0x00, // ## ## - 0x30, 0x0C, 0x00, // ## ## - 0x30, 0x00, 0x00, // ## - 0x30, 0x00, 0x00, // ## - 0x30, 0xFE, 0x00, // ## ####### - 0x30, 0xFE, 0x00, // ## ####### - 0x30, 0x0C, 0x00, // ## ## - 0x38, 0x0C, 0x00, // ### ## - 0x1C, 0x1C, 0x00, // ### ### - 0x0F, 0xFC, 0x00, // ########## - 0x03, 0xF0, 0x00, // ###### - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @2880 'H' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x7E, 0x7E, 0x00, // ###### ###### - 0x7E, 0x7E, 0x00, // ###### ###### - 0x18, 0x18, 0x00, // ## ## - 0x18, 0x18, 0x00, // ## ## - 0x18, 0x18, 0x00, // ## ## - 0x18, 0x18, 0x00, // ## ## - 0x1F, 0xF8, 0x00, // ########## - 0x1F, 0xF8, 0x00, // ########## - 0x18, 0x18, 0x00, // ## ## - 0x18, 0x18, 0x00, // ## ## - 0x18, 0x18, 0x00, // ## ## - 0x18, 0x18, 0x00, // ## ## - 0x7E, 0x7E, 0x00, // ###### ###### - 0x7E, 0x7E, 0x00, // ###### ###### - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @2952 'I' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x1F, 0xF8, 0x00, // ########## - 0x1F, 0xF8, 0x00, // ########## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x1F, 0xF8, 0x00, // ########## - 0x1F, 0xF8, 0x00, // ########## - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @3024 'J' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x07, 0xFE, 0x00, // ########## - 0x07, 0xFE, 0x00, // ########## - 0x00, 0x30, 0x00, // ## - 0x00, 0x30, 0x00, // ## - 0x00, 0x30, 0x00, // ## - 0x00, 0x30, 0x00, // ## - 0x00, 0x30, 0x00, // ## - 0x30, 0x30, 0x00, // ## ## - 0x30, 0x30, 0x00, // ## ## - 0x30, 0x30, 0x00, // ## ## - 0x30, 0x30, 0x00, // ## ## - 0x30, 0x60, 0x00, // ## ## - 0x3F, 0xE0, 0x00, // ######### - 0x0F, 0x80, 0x00, // ##### - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @3096 'K' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x7F, 0x3E, 0x00, // ####### ##### - 0x7F, 0x3E, 0x00, // ####### ##### - 0x18, 0x30, 0x00, // ## ## - 0x18, 0x60, 0x00, // ## ## - 0x18, 0xC0, 0x00, // ## ## - 0x19, 0x80, 0x00, // ## ## - 0x1B, 0x80, 0x00, // ## ### - 0x1F, 0xC0, 0x00, // ####### - 0x1C, 0xE0, 0x00, // ### ### - 0x18, 0x70, 0x00, // ## ### - 0x18, 0x30, 0x00, // ## ## - 0x18, 0x38, 0x00, // ## ### - 0x7F, 0x1F, 0x00, // ####### ##### - 0x7F, 0x1F, 0x00, // ####### ##### - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @3168 'L' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x7F, 0x80, 0x00, // ######## - 0x7F, 0x80, 0x00, // ######## - 0x0C, 0x00, 0x00, // ## - 0x0C, 0x00, 0x00, // ## - 0x0C, 0x00, 0x00, // ## - 0x0C, 0x00, 0x00, // ## - 0x0C, 0x00, 0x00, // ## - 0x0C, 0x00, 0x00, // ## - 0x0C, 0x0C, 0x00, // ## ## - 0x0C, 0x0C, 0x00, // ## ## - 0x0C, 0x0C, 0x00, // ## ## - 0x0C, 0x0C, 0x00, // ## ## - 0x7F, 0xFC, 0x00, // ############# - 0x7F, 0xFC, 0x00, // ############# - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @3240 'M' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0xF0, 0x0F, 0x00, // #### #### - 0xF8, 0x1F, 0x00, // ##### ##### - 0x38, 0x1C, 0x00, // ### ### - 0x3C, 0x3C, 0x00, // #### #### - 0x3C, 0x3C, 0x00, // #### #### - 0x36, 0x6C, 0x00, // ## ## ## ## - 0x36, 0x6C, 0x00, // ## ## ## ## - 0x33, 0xCC, 0x00, // ## #### ## - 0x33, 0xCC, 0x00, // ## #### ## - 0x31, 0x8C, 0x00, // ## ## ## - 0x30, 0x0C, 0x00, // ## ## - 0x30, 0x0C, 0x00, // ## ## - 0xFE, 0x7F, 0x00, // ####### ####### - 0xFE, 0x7F, 0x00, // ####### ####### - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @3312 'N' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x78, 0xFE, 0x00, // #### ####### - 0x78, 0xFE, 0x00, // #### ####### - 0x1C, 0x18, 0x00, // ### ## - 0x1E, 0x18, 0x00, // #### ## - 0x1F, 0x18, 0x00, // ##### ## - 0x1B, 0x18, 0x00, // ## ## ## - 0x1B, 0x98, 0x00, // ## ### ## - 0x19, 0xD8, 0x00, // ## ### ## - 0x18, 0xD8, 0x00, // ## ## ## - 0x18, 0xF8, 0x00, // ## ##### - 0x18, 0x78, 0x00, // ## #### - 0x18, 0x38, 0x00, // ## ### - 0x7F, 0x18, 0x00, // ####### ## - 0x7F, 0x18, 0x00, // ####### ## - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @3384 'O' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x03, 0xC0, 0x00, // #### - 0x0F, 0xF0, 0x00, // ######## - 0x1C, 0x38, 0x00, // ### ### - 0x18, 0x18, 0x00, // ## ## - 0x38, 0x1C, 0x00, // ### ### - 0x30, 0x0C, 0x00, // ## ## - 0x30, 0x0C, 0x00, // ## ## - 0x30, 0x0C, 0x00, // ## ## - 0x30, 0x0C, 0x00, // ## ## - 0x38, 0x1C, 0x00, // ### ### - 0x18, 0x18, 0x00, // ## ## - 0x1C, 0x38, 0x00, // ### ### - 0x0F, 0xF0, 0x00, // ######## - 0x03, 0xC0, 0x00, // #### - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @3456 'P' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x3F, 0xF0, 0x00, // ########## - 0x3F, 0xF8, 0x00, // ########### - 0x0C, 0x1C, 0x00, // ## ### - 0x0C, 0x0C, 0x00, // ## ## - 0x0C, 0x0C, 0x00, // ## ## - 0x0C, 0x0C, 0x00, // ## ## - 0x0C, 0x18, 0x00, // ## ## - 0x0F, 0xF8, 0x00, // ######### - 0x0F, 0xE0, 0x00, // ####### - 0x0C, 0x00, 0x00, // ## - 0x0C, 0x00, 0x00, // ## - 0x0C, 0x00, 0x00, // ## - 0x3F, 0xC0, 0x00, // ######## - 0x3F, 0xC0, 0x00, // ######## - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @3528 'Q' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x03, 0xC0, 0x00, // #### - 0x0F, 0xF0, 0x00, // ######## - 0x1C, 0x38, 0x00, // ### ### - 0x18, 0x18, 0x00, // ## ## - 0x38, 0x1C, 0x00, // ### ### - 0x30, 0x0C, 0x00, // ## ## - 0x30, 0x0C, 0x00, // ## ## - 0x30, 0x0C, 0x00, // ## ## - 0x30, 0x0C, 0x00, // ## ## - 0x38, 0x1C, 0x00, // ### ### - 0x18, 0x18, 0x00, // ## ## - 0x1C, 0x38, 0x00, // ### ### - 0x0F, 0xF0, 0x00, // ######## - 0x07, 0xC0, 0x00, // ##### - 0x07, 0xCC, 0x00, // ##### ## - 0x0F, 0xFC, 0x00, // ########## - 0x0C, 0x38, 0x00, // ## ### - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @3600 'R' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x7F, 0xE0, 0x00, // ########## - 0x7F, 0xF0, 0x00, // ########### - 0x18, 0x38, 0x00, // ## ### - 0x18, 0x18, 0x00, // ## ## - 0x18, 0x18, 0x00, // ## ## - 0x18, 0x38, 0x00, // ## ### - 0x1F, 0xF0, 0x00, // ######### - 0x1F, 0xC0, 0x00, // ####### - 0x18, 0xE0, 0x00, // ## ### - 0x18, 0x70, 0x00, // ## ### - 0x18, 0x30, 0x00, // ## ## - 0x18, 0x38, 0x00, // ## ### - 0x7F, 0x1E, 0x00, // ####### #### - 0x7F, 0x0E, 0x00, // ####### ### - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @3672 'S' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x07, 0xD8, 0x00, // ##### ## - 0x0F, 0xF8, 0x00, // ######### - 0x1C, 0x38, 0x00, // ### ### - 0x18, 0x18, 0x00, // ## ## - 0x18, 0x18, 0x00, // ## ## - 0x1E, 0x00, 0x00, // #### - 0x0F, 0xC0, 0x00, // ###### - 0x03, 0xF0, 0x00, // ###### - 0x00, 0x78, 0x00, // #### - 0x18, 0x18, 0x00, // ## ## - 0x18, 0x18, 0x00, // ## ## - 0x1C, 0x38, 0x00, // ### ### - 0x1F, 0xF0, 0x00, // ######### - 0x1B, 0xE0, 0x00, // ## ##### - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @3744 'T' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x3F, 0xFC, 0x00, // ############ - 0x3F, 0xFC, 0x00, // ############ - 0x31, 0x8C, 0x00, // ## ## ## - 0x31, 0x8C, 0x00, // ## ## ## - 0x31, 0x8C, 0x00, // ## ## ## - 0x31, 0x8C, 0x00, // ## ## ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x0F, 0xF0, 0x00, // ######## - 0x0F, 0xF0, 0x00, // ######## - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @3816 'U' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x7E, 0x7E, 0x00, // ###### ###### - 0x7E, 0x7E, 0x00, // ###### ###### - 0x18, 0x18, 0x00, // ## ## - 0x18, 0x18, 0x00, // ## ## - 0x18, 0x18, 0x00, // ## ## - 0x18, 0x18, 0x00, // ## ## - 0x18, 0x18, 0x00, // ## ## - 0x18, 0x18, 0x00, // ## ## - 0x18, 0x18, 0x00, // ## ## - 0x18, 0x18, 0x00, // ## ## - 0x18, 0x18, 0x00, // ## ## - 0x0C, 0x30, 0x00, // ## ## - 0x0F, 0xF0, 0x00, // ######## - 0x03, 0xC0, 0x00, // #### - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @3888 'V' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x7F, 0x7F, 0x00, // ####### ####### - 0x7F, 0x7F, 0x00, // ####### ####### - 0x18, 0x0C, 0x00, // ## ## - 0x0C, 0x18, 0x00, // ## ## - 0x0C, 0x18, 0x00, // ## ## - 0x0C, 0x18, 0x00, // ## ## - 0x06, 0x30, 0x00, // ## ## - 0x06, 0x30, 0x00, // ## ## - 0x03, 0x60, 0x00, // ## ## - 0x03, 0x60, 0x00, // ## ## - 0x03, 0x60, 0x00, // ## ## - 0x01, 0xC0, 0x00, // ### - 0x01, 0xC0, 0x00, // ### - 0x00, 0x80, 0x00, // # - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @3960 'W' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0xFE, 0x3F, 0x80, // ####### ####### - 0xFE, 0x3F, 0x80, // ####### ####### - 0x30, 0x06, 0x00, // ## ## - 0x30, 0x06, 0x00, // ## ## - 0x30, 0x86, 0x00, // ## # ## - 0x19, 0xCC, 0x00, // ## ### ## - 0x19, 0xCC, 0x00, // ## ### ## - 0x1B, 0x6C, 0x00, // ## ## ## ## - 0x1B, 0x6C, 0x00, // ## ## ## ## - 0x1E, 0x7C, 0x00, // #### ##### - 0x0E, 0x38, 0x00, // ### ### - 0x0E, 0x38, 0x00, // ### ### - 0x0C, 0x18, 0x00, // ## ## - 0x0C, 0x18, 0x00, // ## ## - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @4032 'X' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x7E, 0x7E, 0x00, // ###### ###### - 0x7E, 0x7E, 0x00, // ###### ###### - 0x18, 0x18, 0x00, // ## ## - 0x0C, 0x30, 0x00, // ## ## - 0x06, 0x60, 0x00, // ## ## - 0x03, 0xC0, 0x00, // #### - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x03, 0xC0, 0x00, // #### - 0x06, 0x60, 0x00, // ## ## - 0x0C, 0x30, 0x00, // ## ## - 0x18, 0x18, 0x00, // ## ## - 0x7E, 0x7E, 0x00, // ###### ###### - 0x7E, 0x7E, 0x00, // ###### ###### - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @4104 'Y' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x7C, 0x7E, 0x00, // ##### ###### - 0x7C, 0x7E, 0x00, // ##### ###### - 0x18, 0x18, 0x00, // ## ## - 0x0C, 0x30, 0x00, // ## ## - 0x06, 0x60, 0x00, // ## ## - 0x06, 0x60, 0x00, // ## ## - 0x03, 0xC0, 0x00, // #### - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x0F, 0xF0, 0x00, // ######## - 0x0F, 0xF0, 0x00, // ######## - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @4176 'Z' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x1F, 0xF8, 0x00, // ########## - 0x1F, 0xF8, 0x00, // ########## - 0x18, 0x18, 0x00, // ## ## - 0x18, 0x30, 0x00, // ## ## - 0x18, 0x60, 0x00, // ## ## - 0x18, 0xC0, 0x00, // ## ## - 0x01, 0x80, 0x00, // ## - 0x03, 0x00, 0x00, // ## - 0x06, 0x18, 0x00, // ## ## - 0x0C, 0x18, 0x00, // ## ## - 0x18, 0x18, 0x00, // ## ## - 0x30, 0x18, 0x00, // ## ## - 0x3F, 0xF8, 0x00, // ########### - 0x3F, 0xF8, 0x00, // ########### - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @4248 '[' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x01, 0xF0, 0x00, // ##### - 0x01, 0xF0, 0x00, // ##### - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0xF0, 0x00, // ##### - 0x01, 0xF0, 0x00, // ##### - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @4320 '\' (17 pixels wide) - 0x18, 0x00, 0x00, // ## - 0x18, 0x00, 0x00, // ## - 0x1C, 0x00, 0x00, // ### - 0x0C, 0x00, 0x00, // ## - 0x0E, 0x00, 0x00, // ### - 0x06, 0x00, 0x00, // ## - 0x06, 0x00, 0x00, // ## - 0x03, 0x00, 0x00, // ## - 0x03, 0x00, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x00, 0xC0, 0x00, // ## - 0x00, 0xC0, 0x00, // ## - 0x00, 0x60, 0x00, // ## - 0x00, 0x60, 0x00, // ## - 0x00, 0x70, 0x00, // ### - 0x00, 0x30, 0x00, // ## - 0x00, 0x38, 0x00, // ### - 0x00, 0x18, 0x00, // ## - 0x00, 0x18, 0x00, // ## - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @4392 ']' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x0F, 0x80, 0x00, // ##### - 0x0F, 0x80, 0x00, // ##### - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x0F, 0x80, 0x00, // ##### - 0x0F, 0x80, 0x00, // ##### - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @4464 '^' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x80, 0x00, // # - 0x01, 0xC0, 0x00, // ### - 0x03, 0xE0, 0x00, // ##### - 0x07, 0x70, 0x00, // ### ### - 0x06, 0x30, 0x00, // ## ## - 0x0C, 0x18, 0x00, // ## ## - 0x18, 0x0C, 0x00, // ## ## - 0x10, 0x04, 0x00, // # # - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @4536 '_' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0xFF, 0xFF, 0x00, // ################ - 0xFF, 0xFF, 0x00, // ################ - - // @4608 '`' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x03, 0x00, 0x00, // ## - 0x03, 0x80, 0x00, // ### - 0x00, 0xE0, 0x00, // ### - 0x00, 0x60, 0x00, // ## - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @4680 'a' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x0F, 0xC0, 0x00, // ###### - 0x1F, 0xE0, 0x00, // ######## - 0x00, 0x30, 0x00, // ## - 0x00, 0x30, 0x00, // ## - 0x07, 0xF0, 0x00, // ####### - 0x1F, 0xF0, 0x00, // ######### - 0x38, 0x30, 0x00, // ### ## - 0x30, 0x30, 0x00, // ## ## - 0x30, 0x70, 0x00, // ## ### - 0x1F, 0xFC, 0x00, // ########### - 0x0F, 0xBC, 0x00, // ##### #### - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @4752 'b' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x78, 0x00, 0x00, // #### - 0x78, 0x00, 0x00, // #### - 0x18, 0x00, 0x00, // ## - 0x18, 0x00, 0x00, // ## - 0x1B, 0xE0, 0x00, // ## ##### - 0x1F, 0xF8, 0x00, // ########## - 0x1C, 0x18, 0x00, // ### ## - 0x18, 0x0C, 0x00, // ## ## - 0x18, 0x0C, 0x00, // ## ## - 0x18, 0x0C, 0x00, // ## ## - 0x18, 0x0C, 0x00, // ## ## - 0x18, 0x0C, 0x00, // ## ## - 0x1C, 0x18, 0x00, // ### ## - 0x7F, 0xF8, 0x00, // ############ - 0x7B, 0xE0, 0x00, // #### ##### - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @4824 'c' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x03, 0xEC, 0x00, // ##### ## - 0x0F, 0xFC, 0x00, // ########## - 0x1C, 0x1C, 0x00, // ### ### - 0x38, 0x0C, 0x00, // ### ## - 0x30, 0x0C, 0x00, // ## ## - 0x30, 0x00, 0x00, // ## - 0x30, 0x00, 0x00, // ## - 0x38, 0x0C, 0x00, // ### ## - 0x1C, 0x1C, 0x00, // ### ### - 0x0F, 0xF8, 0x00, // ######### - 0x03, 0xF0, 0x00, // ###### - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @4896 'd' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x78, 0x00, // #### - 0x00, 0x78, 0x00, // #### - 0x00, 0x18, 0x00, // ## - 0x00, 0x18, 0x00, // ## - 0x07, 0xD8, 0x00, // ##### ## - 0x1F, 0xF8, 0x00, // ########## - 0x18, 0x38, 0x00, // ## ### - 0x30, 0x18, 0x00, // ## ## - 0x30, 0x18, 0x00, // ## ## - 0x30, 0x18, 0x00, // ## ## - 0x30, 0x18, 0x00, // ## ## - 0x30, 0x18, 0x00, // ## ## - 0x18, 0x38, 0x00, // ## ### - 0x1F, 0xFE, 0x00, // ############ - 0x07, 0xDE, 0x00, // ##### #### - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @4968 'e' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x07, 0xE0, 0x00, // ###### - 0x1F, 0xF8, 0x00, // ########## - 0x18, 0x18, 0x00, // ## ## - 0x30, 0x0C, 0x00, // ## ## - 0x3F, 0xFC, 0x00, // ############ - 0x3F, 0xFC, 0x00, // ############ - 0x30, 0x00, 0x00, // ## - 0x30, 0x00, 0x00, // ## - 0x18, 0x0C, 0x00, // ## ## - 0x1F, 0xFC, 0x00, // ########### - 0x07, 0xF0, 0x00, // ####### - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @5040 'f' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x01, 0xFC, 0x00, // ####### - 0x03, 0xFC, 0x00, // ######## - 0x06, 0x00, 0x00, // ## - 0x06, 0x00, 0x00, // ## - 0x3F, 0xF8, 0x00, // ########### - 0x3F, 0xF8, 0x00, // ########### - 0x06, 0x00, 0x00, // ## - 0x06, 0x00, 0x00, // ## - 0x06, 0x00, 0x00, // ## - 0x06, 0x00, 0x00, // ## - 0x06, 0x00, 0x00, // ## - 0x06, 0x00, 0x00, // ## - 0x06, 0x00, 0x00, // ## - 0x3F, 0xF0, 0x00, // ########## - 0x3F, 0xF0, 0x00, // ########## - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @5112 'g' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x07, 0xDE, 0x00, // ##### #### - 0x1F, 0xFE, 0x00, // ############ - 0x18, 0x38, 0x00, // ## ### - 0x30, 0x18, 0x00, // ## ## - 0x30, 0x18, 0x00, // ## ## - 0x30, 0x18, 0x00, // ## ## - 0x30, 0x18, 0x00, // ## ## - 0x30, 0x18, 0x00, // ## ## - 0x18, 0x38, 0x00, // ## ### - 0x1F, 0xF8, 0x00, // ########## - 0x07, 0xD8, 0x00, // ##### ## - 0x00, 0x18, 0x00, // ## - 0x00, 0x18, 0x00, // ## - 0x00, 0x38, 0x00, // ### - 0x0F, 0xF0, 0x00, // ######## - 0x0F, 0xC0, 0x00, // ###### - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @5184 'h' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x78, 0x00, 0x00, // #### - 0x78, 0x00, 0x00, // #### - 0x18, 0x00, 0x00, // ## - 0x18, 0x00, 0x00, // ## - 0x1B, 0xE0, 0x00, // ## ##### - 0x1F, 0xF0, 0x00, // ######### - 0x1C, 0x38, 0x00, // ### ### - 0x18, 0x18, 0x00, // ## ## - 0x18, 0x18, 0x00, // ## ## - 0x18, 0x18, 0x00, // ## ## - 0x18, 0x18, 0x00, // ## ## - 0x18, 0x18, 0x00, // ## ## - 0x18, 0x18, 0x00, // ## ## - 0x7E, 0x7E, 0x00, // ###### ###### - 0x7E, 0x7E, 0x00, // ###### ###### - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @5256 'i' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x1F, 0x80, 0x00, // ###### - 0x1F, 0x80, 0x00, // ###### - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x3F, 0xFC, 0x00, // ############ - 0x3F, 0xFC, 0x00, // ############ - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @5328 'j' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0xC0, 0x00, // ## - 0x00, 0xC0, 0x00, // ## - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x1F, 0xF0, 0x00, // ######### - 0x1F, 0xF0, 0x00, // ######### - 0x00, 0x30, 0x00, // ## - 0x00, 0x30, 0x00, // ## - 0x00, 0x30, 0x00, // ## - 0x00, 0x30, 0x00, // ## - 0x00, 0x30, 0x00, // ## - 0x00, 0x30, 0x00, // ## - 0x00, 0x30, 0x00, // ## - 0x00, 0x30, 0x00, // ## - 0x00, 0x30, 0x00, // ## - 0x00, 0x30, 0x00, // ## - 0x00, 0x30, 0x00, // ## - 0x00, 0x70, 0x00, // ### - 0x1F, 0xE0, 0x00, // ######## - 0x1F, 0x80, 0x00, // ###### - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @5400 'k' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x3C, 0x00, 0x00, // #### - 0x3C, 0x00, 0x00, // #### - 0x0C, 0x00, 0x00, // ## - 0x0C, 0x00, 0x00, // ## - 0x0C, 0xF8, 0x00, // ## ##### - 0x0C, 0xF8, 0x00, // ## ##### - 0x0C, 0xC0, 0x00, // ## ## - 0x0D, 0x80, 0x00, // ## ## - 0x0F, 0x80, 0x00, // ##### - 0x0F, 0x00, 0x00, // #### - 0x0F, 0x80, 0x00, // ##### - 0x0D, 0xC0, 0x00, // ## ### - 0x0C, 0xE0, 0x00, // ## ### - 0x3C, 0x7C, 0x00, // #### ##### - 0x3C, 0x7C, 0x00, // #### ##### - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @5472 'l' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x1F, 0x80, 0x00, // ###### - 0x1F, 0x80, 0x00, // ###### - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x3F, 0xFC, 0x00, // ############ - 0x3F, 0xFC, 0x00, // ############ - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @5544 'm' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0xF7, 0x78, 0x00, // #### ### #### - 0xFF, 0xFC, 0x00, // ############## - 0x39, 0xCC, 0x00, // ### ### ## - 0x31, 0x8C, 0x00, // ## ## ## - 0x31, 0x8C, 0x00, // ## ## ## - 0x31, 0x8C, 0x00, // ## ## ## - 0x31, 0x8C, 0x00, // ## ## ## - 0x31, 0x8C, 0x00, // ## ## ## - 0x31, 0x8C, 0x00, // ## ## ## - 0xFD, 0xEF, 0x00, // ###### #### #### - 0xFD, 0xEF, 0x00, // ###### #### #### - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @5616 'n' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x7B, 0xE0, 0x00, // #### ##### - 0x7F, 0xF0, 0x00, // ########### - 0x1C, 0x38, 0x00, // ### ### - 0x18, 0x18, 0x00, // ## ## - 0x18, 0x18, 0x00, // ## ## - 0x18, 0x18, 0x00, // ## ## - 0x18, 0x18, 0x00, // ## ## - 0x18, 0x18, 0x00, // ## ## - 0x18, 0x18, 0x00, // ## ## - 0x7E, 0x7E, 0x00, // ###### ###### - 0x7E, 0x7E, 0x00, // ###### ###### - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @5688 'o' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x03, 0xC0, 0x00, // #### - 0x0F, 0xF0, 0x00, // ######## - 0x1C, 0x38, 0x00, // ### ### - 0x38, 0x1C, 0x00, // ### ### - 0x30, 0x0C, 0x00, // ## ## - 0x30, 0x0C, 0x00, // ## ## - 0x30, 0x0C, 0x00, // ## ## - 0x38, 0x1C, 0x00, // ### ### - 0x1C, 0x38, 0x00, // ### ### - 0x0F, 0xF0, 0x00, // ######## - 0x03, 0xC0, 0x00, // #### - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @5760 'p' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x7B, 0xE0, 0x00, // #### ##### - 0x7F, 0xF8, 0x00, // ############ - 0x1C, 0x18, 0x00, // ### ## - 0x18, 0x0C, 0x00, // ## ## - 0x18, 0x0C, 0x00, // ## ## - 0x18, 0x0C, 0x00, // ## ## - 0x18, 0x0C, 0x00, // ## ## - 0x18, 0x0C, 0x00, // ## ## - 0x1C, 0x18, 0x00, // ### ## - 0x1F, 0xF8, 0x00, // ########## - 0x1B, 0xE0, 0x00, // ## ##### - 0x18, 0x00, 0x00, // ## - 0x18, 0x00, 0x00, // ## - 0x18, 0x00, 0x00, // ## - 0x7F, 0x00, 0x00, // ####### - 0x7F, 0x00, 0x00, // ####### - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @5832 'q' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x07, 0xDE, 0x00, // ##### #### - 0x1F, 0xFE, 0x00, // ############ - 0x18, 0x38, 0x00, // ## ### - 0x30, 0x18, 0x00, // ## ## - 0x30, 0x18, 0x00, // ## ## - 0x30, 0x18, 0x00, // ## ## - 0x30, 0x18, 0x00, // ## ## - 0x30, 0x18, 0x00, // ## ## - 0x18, 0x38, 0x00, // ## ### - 0x1F, 0xF8, 0x00, // ########## - 0x07, 0xD8, 0x00, // ##### ## - 0x00, 0x18, 0x00, // ## - 0x00, 0x18, 0x00, // ## - 0x00, 0x18, 0x00, // ## - 0x00, 0xFE, 0x00, // ####### - 0x00, 0xFE, 0x00, // ####### - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @5904 'r' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x3E, 0x78, 0x00, // ##### #### - 0x3E, 0xFC, 0x00, // ##### ###### - 0x07, 0xCC, 0x00, // ##### ## - 0x07, 0x00, 0x00, // ### - 0x06, 0x00, 0x00, // ## - 0x06, 0x00, 0x00, // ## - 0x06, 0x00, 0x00, // ## - 0x06, 0x00, 0x00, // ## - 0x06, 0x00, 0x00, // ## - 0x3F, 0xF0, 0x00, // ########## - 0x3F, 0xF0, 0x00, // ########## - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @5976 's' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x07, 0xF8, 0x00, // ######## - 0x0F, 0xF8, 0x00, // ######### - 0x18, 0x18, 0x00, // ## ## - 0x18, 0x18, 0x00, // ## ## - 0x1F, 0x80, 0x00, // ###### - 0x0F, 0xF0, 0x00, // ######## - 0x00, 0xF8, 0x00, // ##### - 0x18, 0x18, 0x00, // ## ## - 0x18, 0x38, 0x00, // ## ### - 0x1F, 0xF0, 0x00, // ######### - 0x1F, 0xE0, 0x00, // ######## - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @6048 't' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x0C, 0x00, 0x00, // ## - 0x0C, 0x00, 0x00, // ## - 0x0C, 0x00, 0x00, // ## - 0x0C, 0x00, 0x00, // ## - 0x3F, 0xF0, 0x00, // ########## - 0x3F, 0xF0, 0x00, // ########## - 0x0C, 0x00, 0x00, // ## - 0x0C, 0x00, 0x00, // ## - 0x0C, 0x00, 0x00, // ## - 0x0C, 0x00, 0x00, // ## - 0x0C, 0x00, 0x00, // ## - 0x0C, 0x00, 0x00, // ## - 0x0C, 0x1C, 0x00, // ## ### - 0x07, 0xFC, 0x00, // ######### - 0x03, 0xF0, 0x00, // ###### - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @6120 'u' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x78, 0x78, 0x00, // #### #### - 0x78, 0x78, 0x00, // #### #### - 0x18, 0x18, 0x00, // ## ## - 0x18, 0x18, 0x00, // ## ## - 0x18, 0x18, 0x00, // ## ## - 0x18, 0x18, 0x00, // ## ## - 0x18, 0x18, 0x00, // ## ## - 0x18, 0x18, 0x00, // ## ## - 0x18, 0x38, 0x00, // ## ### - 0x0F, 0xFE, 0x00, // ########### - 0x07, 0xDE, 0x00, // ##### #### - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @6192 'v' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x7C, 0x3E, 0x00, // ##### ##### - 0x7C, 0x3E, 0x00, // ##### ##### - 0x18, 0x18, 0x00, // ## ## - 0x18, 0x18, 0x00, // ## ## - 0x0C, 0x30, 0x00, // ## ## - 0x0C, 0x30, 0x00, // ## ## - 0x06, 0x60, 0x00, // ## ## - 0x06, 0x60, 0x00, // ## ## - 0x07, 0xE0, 0x00, // ###### - 0x03, 0xC0, 0x00, // #### - 0x03, 0xC0, 0x00, // #### - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @6264 'w' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x78, 0x3C, 0x00, // #### #### - 0x78, 0x3C, 0x00, // #### #### - 0x31, 0x18, 0x00, // ## # ## - 0x33, 0x98, 0x00, // ## ### ## - 0x33, 0x98, 0x00, // ## ### ## - 0x1A, 0xB0, 0x00, // ## # # ## - 0x1E, 0xF0, 0x00, // #### #### - 0x1E, 0xF0, 0x00, // #### #### - 0x1C, 0x60, 0x00, // ### ## - 0x0C, 0x60, 0x00, // ## ## - 0x0C, 0x60, 0x00, // ## ## - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @6336 'x' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x3E, 0x7C, 0x00, // ##### ##### - 0x3E, 0x7C, 0x00, // ##### ##### - 0x0C, 0x30, 0x00, // ## ## - 0x06, 0x60, 0x00, // ## ## - 0x03, 0xC0, 0x00, // #### - 0x01, 0x80, 0x00, // ## - 0x03, 0xC0, 0x00, // #### - 0x06, 0x60, 0x00, // ## ## - 0x0C, 0x30, 0x00, // ## ## - 0x3E, 0x7C, 0x00, // ##### ##### - 0x3E, 0x7C, 0x00, // ##### ##### - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @6408 'y' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x7E, 0x1F, 0x00, // ###### ##### - 0x7E, 0x1F, 0x00, // ###### ##### - 0x18, 0x0C, 0x00, // ## ## - 0x0C, 0x18, 0x00, // ## ## - 0x0C, 0x18, 0x00, // ## ## - 0x06, 0x30, 0x00, // ## ## - 0x06, 0x30, 0x00, // ## ## - 0x03, 0x60, 0x00, // ## ## - 0x03, 0xE0, 0x00, // ##### - 0x01, 0xC0, 0x00, // ### - 0x00, 0xC0, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x03, 0x00, 0x00, // ## - 0x3F, 0xC0, 0x00, // ######## - 0x3F, 0xC0, 0x00, // ######## - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @6480 'z' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x1F, 0xF8, 0x00, // ########## - 0x1F, 0xF8, 0x00, // ########## - 0x18, 0x30, 0x00, // ## ## - 0x18, 0x60, 0x00, // ## ## - 0x00, 0xC0, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x03, 0x00, 0x00, // ## - 0x06, 0x18, 0x00, // ## ## - 0x0C, 0x18, 0x00, // ## ## - 0x1F, 0xF8, 0x00, // ########## - 0x1F, 0xF8, 0x00, // ########## - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @6552 '{' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0xE0, 0x00, // ### - 0x01, 0xE0, 0x00, // #### - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x03, 0x80, 0x00, // ### - 0x07, 0x00, 0x00, // ### - 0x03, 0x80, 0x00, // ### - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0xE0, 0x00, // #### - 0x00, 0xE0, 0x00, // ### - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @6624 '|' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @6696 '}' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x07, 0x00, 0x00, // ### - 0x07, 0x80, 0x00, // #### - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0xC0, 0x00, // ### - 0x00, 0xE0, 0x00, // ### - 0x01, 0xC0, 0x00, // ### - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x01, 0x80, 0x00, // ## - 0x07, 0x80, 0x00, // #### - 0x07, 0x00, 0x00, // ### - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - - // @6768 '~' (17 pixels wide) - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x0E, 0x00, 0x00, // ### - 0x1F, 0x18, 0x00, // ##### ## - 0x3B, 0xB8, 0x00, // ### ### ### - 0x31, 0xF0, 0x00, // ## ##### - 0x00, 0xE0, 0x00, // ### - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // - 0x00, 0x00, 0x00, // -}; - -sFONT Font24 = { - Font24_Table, - 17, /* Width */ - 24, /* Height */ -}; - -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.0/src/font8.c b/lib/esp-epaper-29-ws-20171230-gemu-1.0/src/font8.c deleted file mode 100644 index f1d5b4dfc..000000000 --- a/lib/esp-epaper-29-ws-20171230-gemu-1.0/src/font8.c +++ /dev/null @@ -1,1005 +0,0 @@ -/** - ****************************************************************************** - * @file Font8.c - * @author MCD Application Team - * @version V1.0.0 - * @date 18-February-2014 - * @brief This file provides text Font8 for STM32xx-EVAL's LCD driver. - ****************************************************************************** - * @attention - * - *

© COPYRIGHT(c) 2014 STMicroelectronics

- * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. 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. - * 3. Neither the name of STMicroelectronics nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * 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. - * - ****************************************************************************** - */ - -/* Includes ------------------------------------------------------------------*/ -#include "fonts.h" -#include - -// -// Font data for Courier New 12pt -// - -const uint8_t Font8_Table[] PROGMEM = -{ - // @0 ' ' (5 pixels wide) - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x00, // - - // @8 '!' (5 pixels wide) - 0x20, // # - 0x20, // # - 0x20, // # - 0x20, // # - 0x00, // - 0x20, // # - 0x00, // - 0x00, // - - // @16 '"' (5 pixels wide) - 0x50, // # # - 0x50, // # # - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x00, // - - // @24 '#' (5 pixels wide) - 0x28, // # # - 0x50, // # # - 0xF8, // ##### - 0x50, // # # - 0xF8, // ##### - 0x50, // # # - 0xA0, // # # - 0x00, // - - // @32 '$' (5 pixels wide) - 0x20, // # - 0x30, // ## - 0x60, // ## - 0x30, // ## - 0x10, // # - 0x60, // ## - 0x20, // # - 0x00, // - - // @40 '%' (5 pixels wide) - 0x20, // # - 0x20, // # - 0x18, // ## - 0x60, // ## - 0x10, // # - 0x10, // # - 0x00, // - 0x00, // - - // @48 '&' (5 pixels wide) - 0x00, // - 0x38, // ### - 0x20, // # - 0x60, // ## - 0x50, // # # - 0x78, // #### - 0x00, // - 0x00, // - - // @56 ''' (5 pixels wide) - 0x20, // # - 0x20, // # - 0x20, // # - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x00, // - - // @64 '(' (5 pixels wide) - 0x10, // # - 0x20, // # - 0x20, // # - 0x20, // # - 0x20, // # - 0x20, // # - 0x10, // # - 0x00, // - - // @72 ')' (5 pixels wide) - 0x40, // # - 0x20, // # - 0x20, // # - 0x20, // # - 0x20, // # - 0x20, // # - 0x40, // # - 0x00, // - - // @80 '*' (5 pixels wide) - 0x20, // # - 0x70, // ### - 0x20, // # - 0x50, // # # - 0x00, // - 0x00, // - 0x00, // - 0x00, // - - // @88 '+' (5 pixels wide) - 0x00, // - 0x20, // # - 0x20, // # - 0xF8, // ##### - 0x20, // # - 0x20, // # - 0x00, // - 0x00, // - - // @96 ',' (5 pixels wide) - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x10, // # - 0x20, // # - 0x20, // # - 0x00, // - - // @104 '-' (5 pixels wide) - 0x00, // - 0x00, // - 0x00, // - 0x70, // ### - 0x00, // - 0x00, // - 0x00, // - 0x00, // - - // @112 '.' (5 pixels wide) - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x20, // # - 0x00, // - 0x00, // - - // @120 '/' (5 pixels wide) - 0x10, // # - 0x20, // # - 0x20, // # - 0x20, // # - 0x40, // # - 0x40, // # - 0x80, // # - 0x00, // - - // @128 '0' (5 pixels wide) - 0x20, // # - 0x50, // # # - 0x50, // # # - 0x50, // # # - 0x50, // # # - 0x20, // # - 0x00, // - 0x00, // - - // @136 '1' (5 pixels wide) - 0x60, // ## - 0x20, // # - 0x20, // # - 0x20, // # - 0x20, // # - 0xF8, // ##### - 0x00, // - 0x00, // - - // @144 '2' (5 pixels wide) - 0x20, // # - 0x50, // # # - 0x20, // # - 0x20, // # - 0x40, // # - 0x70, // ### - 0x00, // - 0x00, // - - // @152 '3' (5 pixels wide) - 0x20, // # - 0x50, // # # - 0x10, // # - 0x20, // # - 0x10, // # - 0x60, // ## - 0x00, // - 0x00, // - - // @160 '4' (5 pixels wide) - 0x10, // # - 0x30, // ## - 0x50, // # # - 0x78, // #### - 0x10, // # - 0x38, // ### - 0x00, // - 0x00, // - - // @168 '5' (5 pixels wide) - 0x70, // ### - 0x40, // # - 0x60, // ## - 0x10, // # - 0x50, // # # - 0x20, // # - 0x00, // - 0x00, // - - // @176 '6' (5 pixels wide) - 0x30, // ## - 0x40, // # - 0x60, // ## - 0x50, // # # - 0x50, // # # - 0x60, // ## - 0x00, // - 0x00, // - - // @184 '7' (5 pixels wide) - 0x70, // ### - 0x50, // # # - 0x10, // # - 0x20, // # - 0x20, // # - 0x20, // # - 0x00, // - 0x00, // - - // @192 '8' (5 pixels wide) - 0x20, // # - 0x50, // # # - 0x20, // # - 0x50, // # # - 0x50, // # # - 0x20, // # - 0x00, // - 0x00, // - - // @200 '9' (5 pixels wide) - 0x30, // ## - 0x50, // # # - 0x50, // # # - 0x30, // ## - 0x10, // # - 0x60, // ## - 0x00, // - 0x00, // - - // @208 ':' (5 pixels wide) - 0x00, // - 0x00, // - 0x20, // # - 0x00, // - 0x00, // - 0x20, // # - 0x00, // - 0x00, // - - // @216 ';' (5 pixels wide) - 0x00, // - 0x00, // - 0x10, // # - 0x00, // - 0x10, // # - 0x20, // # - 0x00, // - 0x00, // - - // @224 '<' (5 pixels wide) - 0x00, // - 0x10, // # - 0x20, // # - 0xC0, // ## - 0x20, // # - 0x10, // # - 0x00, // - 0x00, // - - // @232 '=' (5 pixels wide) - 0x00, // - 0x70, // ### - 0x00, // - 0x70, // ### - 0x00, // - 0x00, // - 0x00, // - 0x00, // - - // @240 '>' (5 pixels wide) - 0x00, // - 0x40, // # - 0x20, // # - 0x18, // ## - 0x20, // # - 0x40, // # - 0x00, // - 0x00, // - - // @248 '?' (5 pixels wide) - 0x20, // # - 0x50, // # # - 0x10, // # - 0x20, // # - 0x00, // - 0x20, // # - 0x00, // - 0x00, // - - // @256 '@' (5 pixels wide) - 0x30, // ## - 0x48, // # # - 0x48, // # # - 0x58, // # ## - 0x48, // # # - 0x40, // # - 0x38, // ### - 0x00, // - - // @264 'A' (5 pixels wide) - 0x60, // ## - 0x20, // # - 0x50, // # # - 0x70, // ### - 0x88, // # # - 0xD8, // ## ## - 0x00, // - 0x00, // - - // @272 'B' (5 pixels wide) - 0xF0, // #### - 0x48, // # # - 0x70, // ### - 0x48, // # # - 0x48, // # # - 0xF0, // #### - 0x00, // - 0x00, // - - // @280 'C' (5 pixels wide) - 0x70, // ### - 0x50, // # # - 0x40, // # - 0x40, // # - 0x40, // # - 0x30, // ## - 0x00, // - 0x00, // - - // @288 'D' (5 pixels wide) - 0xF0, // #### - 0x48, // # # - 0x48, // # # - 0x48, // # # - 0x48, // # # - 0xF0, // #### - 0x00, // - 0x00, // - - // @296 'E' (5 pixels wide) - 0xF8, // ##### - 0x48, // # # - 0x60, // ## - 0x40, // # - 0x48, // # # - 0xF8, // ##### - 0x00, // - 0x00, // - - // @304 'F' (5 pixels wide) - 0xF8, // ##### - 0x48, // # # - 0x60, // ## - 0x40, // # - 0x40, // # - 0xE0, // ### - 0x00, // - 0x00, // - - // @312 'G' (5 pixels wide) - 0x70, // ### - 0x40, // # - 0x40, // # - 0x58, // # ## - 0x50, // # # - 0x30, // ## - 0x00, // - 0x00, // - - // @320 'H' (5 pixels wide) - 0xE8, // ### # - 0x48, // # # - 0x78, // #### - 0x48, // # # - 0x48, // # # - 0xE8, // ### # - 0x00, // - 0x00, // - - // @328 'I' (5 pixels wide) - 0x70, // ### - 0x20, // # - 0x20, // # - 0x20, // # - 0x20, // # - 0x70, // ### - 0x00, // - 0x00, // - - // @336 'J' (5 pixels wide) - 0x38, // ### - 0x10, // # - 0x10, // # - 0x50, // # # - 0x50, // # # - 0x20, // # - 0x00, // - 0x00, // - - // @344 'K' (5 pixels wide) - 0xD8, // ## ## - 0x50, // # # - 0x60, // ## - 0x70, // ### - 0x50, // # # - 0xD8, // ## ## - 0x00, // - 0x00, // - - // @352 'L' (5 pixels wide) - 0xE0, // ### - 0x40, // # - 0x40, // # - 0x40, // # - 0x48, // # # - 0xF8, // ##### - 0x00, // - 0x00, // - - // @360 'M' (5 pixels wide) - 0xD8, // ## ## - 0xD8, // ## ## - 0xD8, // ## ## - 0xA8, // # # # - 0x88, // # # - 0xD8, // ## ## - 0x00, // - 0x00, // - - // @368 'N' (5 pixels wide) - 0xD8, // ## ## - 0x68, // ## # - 0x68, // ## # - 0x58, // # ## - 0x58, // # ## - 0xE8, // ### # - 0x00, // - 0x00, // - - // @376 'O' (5 pixels wide) - 0x30, // ## - 0x48, // # # - 0x48, // # # - 0x48, // # # - 0x48, // # # - 0x30, // ## - 0x00, // - 0x00, // - - // @384 'P' (5 pixels wide) - 0xF0, // #### - 0x48, // # # - 0x48, // # # - 0x70, // ### - 0x40, // # - 0xE0, // ### - 0x00, // - 0x00, // - - // @392 'Q' (5 pixels wide) - 0x30, // ## - 0x48, // # # - 0x48, // # # - 0x48, // # # - 0x48, // # # - 0x30, // ## - 0x18, // ## - 0x00, // - - // @400 'R' (5 pixels wide) - 0xF0, // #### - 0x48, // # # - 0x48, // # # - 0x70, // ### - 0x48, // # # - 0xE8, // ### # - 0x00, // - 0x00, // - - // @408 'S' (5 pixels wide) - 0x70, // ### - 0x50, // # # - 0x20, // # - 0x10, // # - 0x50, // # # - 0x70, // ### - 0x00, // - 0x00, // - - // @416 'T' (5 pixels wide) - 0xF8, // ##### - 0xA8, // # # # - 0x20, // # - 0x20, // # - 0x20, // # - 0x70, // ### - 0x00, // - 0x00, // - - // @424 'U' (5 pixels wide) - 0xD8, // ## ## - 0x48, // # # - 0x48, // # # - 0x48, // # # - 0x48, // # # - 0x30, // ## - 0x00, // - 0x00, // - - // @432 'V' (5 pixels wide) - 0xD8, // ## ## - 0x88, // # # - 0x48, // # # - 0x50, // # # - 0x50, // # # - 0x30, // ## - 0x00, // - 0x00, // - - // @440 'W' (5 pixels wide) - 0xD8, // ## ## - 0x88, // # # - 0xA8, // # # # - 0xA8, // # # # - 0xA8, // # # # - 0x50, // # # - 0x00, // - 0x00, // - - // @448 'X' (5 pixels wide) - 0xD8, // ## ## - 0x50, // # # - 0x20, // # - 0x20, // # - 0x50, // # # - 0xD8, // ## ## - 0x00, // - 0x00, // - - // @456 'Y' (5 pixels wide) - 0xD8, // ## ## - 0x88, // # # - 0x50, // # # - 0x20, // # - 0x20, // # - 0x70, // ### - 0x00, // - 0x00, // - - // @464 'Z' (5 pixels wide) - 0x78, // #### - 0x48, // # # - 0x10, // # - 0x20, // # - 0x48, // # # - 0x78, // #### - 0x00, // - 0x00, // - - // @472 '[' (5 pixels wide) - 0x30, // ## - 0x20, // # - 0x20, // # - 0x20, // # - 0x20, // # - 0x20, // # - 0x30, // ## - 0x00, // - - // @480 '\' (5 pixels wide) - 0x80, // # - 0x40, // # - 0x40, // # - 0x20, // # - 0x20, // # - 0x20, // # - 0x10, // # - 0x00, // - - // @488 ']' (5 pixels wide) - 0x60, // ## - 0x20, // # - 0x20, // # - 0x20, // # - 0x20, // # - 0x20, // # - 0x60, // ## - 0x00, // - - // @496 '^' (5 pixels wide) - 0x20, // # - 0x20, // # - 0x50, // # # - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x00, // - - // @504 '_' (5 pixels wide) - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0xF8, // ##### - - // @512 '`' (5 pixels wide) - 0x20, // # - 0x10, // # - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x00, // - 0x00, // - - // @520 'a' (5 pixels wide) - 0x00, // - 0x00, // - 0x30, // ## - 0x10, // # - 0x70, // ### - 0x78, // #### - 0x00, // - 0x00, // - - // @528 'b' (5 pixels wide) - 0xC0, // ## - 0x40, // # - 0x70, // ### - 0x48, // # # - 0x48, // # # - 0xF0, // #### - 0x00, // - 0x00, // - - // @536 'c' (5 pixels wide) - 0x00, // - 0x00, // - 0x70, // ### - 0x40, // # - 0x40, // # - 0x70, // ### - 0x00, // - 0x00, // - - // @544 'd' (5 pixels wide) - 0x18, // ## - 0x08, // # - 0x38, // ### - 0x48, // # # - 0x48, // # # - 0x38, // ### - 0x00, // - 0x00, // - - // @552 'e' (5 pixels wide) - 0x00, // - 0x00, // - 0x70, // ### - 0x70, // ### - 0x40, // # - 0x30, // ## - 0x00, // - 0x00, // - - // @560 'f' (5 pixels wide) - 0x10, // # - 0x20, // # - 0x70, // ### - 0x20, // # - 0x20, // # - 0x70, // ### - 0x00, // - 0x00, // - - // @568 'g' (5 pixels wide) - 0x00, // - 0x00, // - 0x38, // ### - 0x48, // # # - 0x48, // # # - 0x38, // ### - 0x08, // # - 0x30, // ## - - // @576 'h' (5 pixels wide) - 0xC0, // ## - 0x40, // # - 0x70, // ### - 0x48, // # # - 0x48, // # # - 0xE8, // ### # - 0x00, // - 0x00, // - - // @584 'i' (5 pixels wide) - 0x20, // # - 0x00, // - 0x60, // ## - 0x20, // # - 0x20, // # - 0x70, // ### - 0x00, // - 0x00, // - - // @592 'j' (5 pixels wide) - 0x20, // # - 0x00, // - 0x70, // ### - 0x10, // # - 0x10, // # - 0x10, // # - 0x10, // # - 0x70, // ### - - // @600 'k' (5 pixels wide) - 0xC0, // ## - 0x40, // # - 0x58, // # ## - 0x70, // ### - 0x50, // # # - 0xD8, // ## ## - 0x00, // - 0x00, // - - // @608 'l' (5 pixels wide) - 0x60, // ## - 0x20, // # - 0x20, // # - 0x20, // # - 0x20, // # - 0x70, // ### - 0x00, // - 0x00, // - - // @616 'm' (5 pixels wide) - 0x00, // - 0x00, // - 0xD0, // ## # - 0xA8, // # # # - 0xA8, // # # # - 0xA8, // # # # - 0x00, // - 0x00, // - - // @624 'n' (5 pixels wide) - 0x00, // - 0x00, // - 0xF0, // #### - 0x48, // # # - 0x48, // # # - 0xC8, // ## # - 0x00, // - 0x00, // - - // @632 'o' (5 pixels wide) - 0x00, // - 0x00, // - 0x30, // ## - 0x48, // # # - 0x48, // # # - 0x30, // ## - 0x00, // - 0x00, // - - // @640 'p' (5 pixels wide) - 0x00, // - 0x00, // - 0xF0, // #### - 0x48, // # # - 0x48, // # # - 0x70, // ### - 0x40, // # - 0xE0, // ### - - // @648 'q' (5 pixels wide) - 0x00, // - 0x00, // - 0x38, // ### - 0x48, // # # - 0x48, // # # - 0x38, // ### - 0x08, // # - 0x18, // ## - - // @656 'r' (5 pixels wide) - 0x00, // - 0x00, // - 0x78, // #### - 0x20, // # - 0x20, // # - 0x70, // ### - 0x00, // - 0x00, // - - // @664 's' (5 pixels wide) - 0x00, // - 0x00, // - 0x30, // ## - 0x20, // # - 0x10, // # - 0x60, // ## - 0x00, // - 0x00, // - - // @672 't' (5 pixels wide) - 0x00, // - 0x40, // # - 0xF0, // #### - 0x40, // # - 0x48, // # # - 0x30, // ## - 0x00, // - 0x00, // - - // @680 'u' (5 pixels wide) - 0x00, // - 0x00, // - 0xD8, // ## ## - 0x48, // # # - 0x48, // # # - 0x38, // ### - 0x00, // - 0x00, // - - // @688 'v' (5 pixels wide) - 0x00, // - 0x00, // - 0xC8, // ## # - 0x48, // # # - 0x30, // ## - 0x30, // ## - 0x00, // - 0x00, // - - // @696 'w' (5 pixels wide) - 0x00, // - 0x00, // - 0xD8, // ## ## - 0xA8, // # # # - 0xA8, // # # # - 0x50, // # # - 0x00, // - 0x00, // - - // @704 'x' (5 pixels wide) - 0x00, // - 0x00, // - 0x48, // # # - 0x30, // ## - 0x30, // ## - 0x48, // # # - 0x00, // - 0x00, // - - // @712 'y' (5 pixels wide) - 0x00, // - 0x00, // - 0xD8, // ## ## - 0x50, // # # - 0x50, // # # - 0x20, // # - 0x20, // # - 0x60, // ## - - // @720 'z' (5 pixels wide) - 0x00, // - 0x00, // - 0x78, // #### - 0x50, // # # - 0x28, // # # - 0x78, // #### - 0x00, // - 0x00, // - - // @728 '{' (5 pixels wide) - 0x10, // # - 0x20, // # - 0x20, // # - 0x60, // ## - 0x20, // # - 0x20, // # - 0x10, // # - 0x00, // - - // @736 '|' (5 pixels wide) - 0x20, // # - 0x20, // # - 0x20, // # - 0x20, // # - 0x20, // # - 0x20, // # - 0x20, // # - 0x00, // - - // @744 '}' (5 pixels wide) - 0x40, // # - 0x20, // # - 0x20, // # - 0x30, // ## - 0x20, // # - 0x20, // # - 0x40, // # - 0x00, // - - // @752 '~' (5 pixels wide) - 0x00, // - 0x00, // - 0x00, // - 0x28, // # # - 0x50, // # # - 0x00, // - 0x00, // - 0x00, // -}; - -sFONT Font8 = { - Font8_Table, - 5, /* Width */ - 8, /* Height */ -}; - -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.0/.gitignore b/lib/esp-epaper-29-ws-20171230-gemu-1.1/.gitignore similarity index 100% rename from lib/esp-epaper-29-ws-20171230-gemu-1.0/.gitignore rename to lib/esp-epaper-29-ws-20171230-gemu-1.1/.gitignore diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.0/.travis.yml b/lib/esp-epaper-29-ws-20171230-gemu-1.1/.travis.yml similarity index 100% rename from lib/esp-epaper-29-ws-20171230-gemu-1.0/.travis.yml rename to lib/esp-epaper-29-ws-20171230-gemu-1.1/.travis.yml diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.0/Arduino/epd2in9-demo/epd2in9-demo.ino b/lib/esp-epaper-29-ws-20171230-gemu-1.1/Arduino/epd2in9-demo/epd2in9-demo.ino similarity index 100% rename from lib/esp-epaper-29-ws-20171230-gemu-1.0/Arduino/epd2in9-demo/epd2in9-demo.ino rename to lib/esp-epaper-29-ws-20171230-gemu-1.1/Arduino/epd2in9-demo/epd2in9-demo.ino diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.0/Arduino/libraries/readme.txt b/lib/esp-epaper-29-ws-20171230-gemu-1.1/Arduino/libraries/readme.txt similarity index 100% rename from lib/esp-epaper-29-ws-20171230-gemu-1.0/Arduino/libraries/readme.txt rename to lib/esp-epaper-29-ws-20171230-gemu-1.1/Arduino/libraries/readme.txt diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.0/LICENSE b/lib/esp-epaper-29-ws-20171230-gemu-1.1/LICENSE similarity index 100% rename from lib/esp-epaper-29-ws-20171230-gemu-1.0/LICENSE rename to lib/esp-epaper-29-ws-20171230-gemu-1.1/LICENSE diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.0/Makefile b/lib/esp-epaper-29-ws-20171230-gemu-1.1/Makefile similarity index 100% rename from lib/esp-epaper-29-ws-20171230-gemu-1.0/Makefile rename to lib/esp-epaper-29-ws-20171230-gemu-1.1/Makefile diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.0/README.md b/lib/esp-epaper-29-ws-20171230-gemu-1.1/README.md similarity index 100% rename from lib/esp-epaper-29-ws-20171230-gemu-1.0/README.md rename to lib/esp-epaper-29-ws-20171230-gemu-1.1/README.md diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.0/components/epaper-29-ws/component.mk b/lib/esp-epaper-29-ws-20171230-gemu-1.1/components/epaper-29-ws/component.mk similarity index 100% rename from lib/esp-epaper-29-ws-20171230-gemu-1.0/components/epaper-29-ws/component.mk rename to lib/esp-epaper-29-ws-20171230-gemu-1.1/components/epaper-29-ws/component.mk diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.0/components/epaper-29-ws/epaper-29-ws.c b/lib/esp-epaper-29-ws-20171230-gemu-1.1/components/epaper-29-ws/epaper-29-ws.c similarity index 100% rename from lib/esp-epaper-29-ws-20171230-gemu-1.0/components/epaper-29-ws/epaper-29-ws.c rename to lib/esp-epaper-29-ws-20171230-gemu-1.1/components/epaper-29-ws/epaper-29-ws.c diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.0/components/epaper-29-ws/epaper-29-ws.h b/lib/esp-epaper-29-ws-20171230-gemu-1.1/components/epaper-29-ws/epaper-29-ws.h similarity index 100% rename from lib/esp-epaper-29-ws-20171230-gemu-1.0/components/epaper-29-ws/epaper-29-ws.h rename to lib/esp-epaper-29-ws-20171230-gemu-1.1/components/epaper-29-ws/epaper-29-ws.h diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.0/components/epaper-29-ws/epaper_font.c b/lib/esp-epaper-29-ws-20171230-gemu-1.1/components/epaper-29-ws/epaper_font.c similarity index 100% rename from lib/esp-epaper-29-ws-20171230-gemu-1.0/components/epaper-29-ws/epaper_font.c rename to lib/esp-epaper-29-ws-20171230-gemu-1.1/components/epaper-29-ws/epaper_font.c diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.0/components/epaper-29-ws/epaper_fonts.h b/lib/esp-epaper-29-ws-20171230-gemu-1.1/components/epaper-29-ws/epaper_fonts.h similarity index 100% rename from lib/esp-epaper-29-ws-20171230-gemu-1.0/components/epaper-29-ws/epaper_fonts.h rename to lib/esp-epaper-29-ws-20171230-gemu-1.1/components/epaper-29-ws/epaper_fonts.h diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.0/components/epaper-29-ws/font16.c b/lib/esp-epaper-29-ws-20171230-gemu-1.1/components/epaper-29-ws/font16.c similarity index 100% rename from lib/esp-epaper-29-ws-20171230-gemu-1.0/components/epaper-29-ws/font16.c rename to lib/esp-epaper-29-ws-20171230-gemu-1.1/components/epaper-29-ws/font16.c diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.0/components/epaper-29-ws/font20.c b/lib/esp-epaper-29-ws-20171230-gemu-1.1/components/epaper-29-ws/font20.c similarity index 100% rename from lib/esp-epaper-29-ws-20171230-gemu-1.0/components/epaper-29-ws/font20.c rename to lib/esp-epaper-29-ws-20171230-gemu-1.1/components/epaper-29-ws/font20.c diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.0/components/epaper-29-ws/font8.c b/lib/esp-epaper-29-ws-20171230-gemu-1.1/components/epaper-29-ws/font8.c similarity index 100% rename from lib/esp-epaper-29-ws-20171230-gemu-1.0/components/epaper-29-ws/font8.c rename to lib/esp-epaper-29-ws-20171230-gemu-1.1/components/epaper-29-ws/font8.c diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.0/components/epaper-29-ws/imagedata.cpp b/lib/esp-epaper-29-ws-20171230-gemu-1.1/components/epaper-29-ws/imagedata.cpp similarity index 100% rename from lib/esp-epaper-29-ws-20171230-gemu-1.0/components/epaper-29-ws/imagedata.cpp rename to lib/esp-epaper-29-ws-20171230-gemu-1.1/components/epaper-29-ws/imagedata.cpp diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.0/components/epaper-29-ws/imagedata.h b/lib/esp-epaper-29-ws-20171230-gemu-1.1/components/epaper-29-ws/imagedata.h similarity index 100% rename from lib/esp-epaper-29-ws-20171230-gemu-1.0/components/epaper-29-ws/imagedata.h rename to lib/esp-epaper-29-ws-20171230-gemu-1.1/components/epaper-29-ws/imagedata.h diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.0/docs/Doxyfile b/lib/esp-epaper-29-ws-20171230-gemu-1.1/docs/Doxyfile similarity index 100% rename from lib/esp-epaper-29-ws-20171230-gemu-1.0/docs/Doxyfile rename to lib/esp-epaper-29-ws-20171230-gemu-1.1/docs/Doxyfile diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.0/docs/Makefile b/lib/esp-epaper-29-ws-20171230-gemu-1.1/docs/Makefile similarity index 100% rename from lib/esp-epaper-29-ws-20171230-gemu-1.0/docs/Makefile rename to lib/esp-epaper-29-ws-20171230-gemu-1.1/docs/Makefile diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.0/docs/README.md b/lib/esp-epaper-29-ws-20171230-gemu-1.1/docs/README.md similarity index 100% rename from lib/esp-epaper-29-ws-20171230-gemu-1.0/docs/README.md rename to lib/esp-epaper-29-ws-20171230-gemu-1.1/docs/README.md diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.0/docs/conf.py b/lib/esp-epaper-29-ws-20171230-gemu-1.1/docs/conf.py similarity index 100% rename from lib/esp-epaper-29-ws-20171230-gemu-1.0/docs/conf.py rename to lib/esp-epaper-29-ws-20171230-gemu-1.1/docs/conf.py diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.0/docs/gen-dxd.py b/lib/esp-epaper-29-ws-20171230-gemu-1.1/docs/gen-dxd.py similarity index 100% rename from lib/esp-epaper-29-ws-20171230-gemu-1.0/docs/gen-dxd.py rename to lib/esp-epaper-29-ws-20171230-gemu-1.1/docs/gen-dxd.py diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.0/docs/index.rst b/lib/esp-epaper-29-ws-20171230-gemu-1.1/docs/index.rst similarity index 100% rename from lib/esp-epaper-29-ws-20171230-gemu-1.0/docs/index.rst rename to lib/esp-epaper-29-ws-20171230-gemu-1.1/docs/index.rst diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.0/docs/link-roles.py b/lib/esp-epaper-29-ws-20171230-gemu-1.1/docs/link-roles.py similarity index 100% rename from lib/esp-epaper-29-ws-20171230-gemu-1.0/docs/link-roles.py rename to lib/esp-epaper-29-ws-20171230-gemu-1.1/docs/link-roles.py diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.0/docs/repo_util.py b/lib/esp-epaper-29-ws-20171230-gemu-1.1/docs/repo_util.py similarity index 100% rename from lib/esp-epaper-29-ws-20171230-gemu-1.0/docs/repo_util.py rename to lib/esp-epaper-29-ws-20171230-gemu-1.1/docs/repo_util.py diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.0/docs/requirements.txt b/lib/esp-epaper-29-ws-20171230-gemu-1.1/docs/requirements.txt similarity index 100% rename from lib/esp-epaper-29-ws-20171230-gemu-1.0/docs/requirements.txt rename to lib/esp-epaper-29-ws-20171230-gemu-1.1/docs/requirements.txt diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.0/library.properties b/lib/esp-epaper-29-ws-20171230-gemu-1.1/library.properties similarity index 85% rename from lib/esp-epaper-29-ws-20171230-gemu-1.0/library.properties rename to lib/esp-epaper-29-ws-20171230-gemu-1.1/library.properties index 07a9e0a07..488cfdda2 100644 --- a/lib/esp-epaper-29-ws-20171230-gemu-1.0/library.properties +++ b/lib/esp-epaper-29-ws-20171230-gemu-1.1/library.properties @@ -1,7 +1,7 @@ name=Waveshare esp 2.9 inch e-paper display driver version=1.0 -author=Gerhard Muntz -maintainer=Gerhard Muntz +author=Gerhard Mutz +maintainer=Gerhard Mutz sentence=ESP8266 library for Waveshare e-paper display. paragraph= category=Display diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.0/main/README.md b/lib/esp-epaper-29-ws-20171230-gemu-1.1/main/README.md similarity index 100% rename from lib/esp-epaper-29-ws-20171230-gemu-1.0/main/README.md rename to lib/esp-epaper-29-ws-20171230-gemu-1.1/main/README.md diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.0/main/component.mk b/lib/esp-epaper-29-ws-20171230-gemu-1.1/main/component.mk similarity index 100% rename from lib/esp-epaper-29-ws-20171230-gemu-1.0/main/component.mk rename to lib/esp-epaper-29-ws-20171230-gemu-1.1/main/component.mk diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.0/main/esp-epaper-29-ws.c b/lib/esp-epaper-29-ws-20171230-gemu-1.1/main/esp-epaper-29-ws.c similarity index 100% rename from lib/esp-epaper-29-ws-20171230-gemu-1.0/main/esp-epaper-29-ws.c rename to lib/esp-epaper-29-ws-20171230-gemu-1.1/main/esp-epaper-29-ws.c diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.0/main/imagedata.c b/lib/esp-epaper-29-ws-20171230-gemu-1.1/main/imagedata.c similarity index 100% rename from lib/esp-epaper-29-ws-20171230-gemu-1.0/main/imagedata.c rename to lib/esp-epaper-29-ws-20171230-gemu-1.1/main/imagedata.c diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.0/main/imagedata.h b/lib/esp-epaper-29-ws-20171230-gemu-1.1/main/imagedata.h similarity index 100% rename from lib/esp-epaper-29-ws-20171230-gemu-1.0/main/imagedata.h rename to lib/esp-epaper-29-ws-20171230-gemu-1.1/main/imagedata.h diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.0/pictures/2.9inch_e-Paper_Datasheet.pdf b/lib/esp-epaper-29-ws-20171230-gemu-1.1/pictures/2.9inch_e-Paper_Datasheet.pdf similarity index 100% rename from lib/esp-epaper-29-ws-20171230-gemu-1.0/pictures/2.9inch_e-Paper_Datasheet.pdf rename to lib/esp-epaper-29-ws-20171230-gemu-1.1/pictures/2.9inch_e-Paper_Datasheet.pdf diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.0/pictures/e-paper-and-esp-sample-image.jpg b/lib/esp-epaper-29-ws-20171230-gemu-1.1/pictures/e-paper-and-esp-sample-image.jpg similarity index 100% rename from lib/esp-epaper-29-ws-20171230-gemu-1.0/pictures/e-paper-and-esp-sample-image.jpg rename to lib/esp-epaper-29-ws-20171230-gemu-1.1/pictures/e-paper-and-esp-sample-image.jpg diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.0/pictures/e-paper-and-esp-sample-text.jpg b/lib/esp-epaper-29-ws-20171230-gemu-1.1/pictures/e-paper-and-esp-sample-text.jpg similarity index 100% rename from lib/esp-epaper-29-ws-20171230-gemu-1.0/pictures/e-paper-and-esp-sample-text.jpg rename to lib/esp-epaper-29-ws-20171230-gemu-1.1/pictures/e-paper-and-esp-sample-text.jpg diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.0/pictures/espresif-logo.bmp b/lib/esp-epaper-29-ws-20171230-gemu-1.1/pictures/espresif-logo.bmp similarity index 100% rename from lib/esp-epaper-29-ws-20171230-gemu-1.0/pictures/espresif-logo.bmp rename to lib/esp-epaper-29-ws-20171230-gemu-1.1/pictures/espresif-logo.bmp diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.0/pictures/image-conversion-setup.png b/lib/esp-epaper-29-ws-20171230-gemu-1.1/pictures/image-conversion-setup.png similarity index 100% rename from lib/esp-epaper-29-ws-20171230-gemu-1.0/pictures/image-conversion-setup.png rename to lib/esp-epaper-29-ws-20171230-gemu-1.1/pictures/image-conversion-setup.png diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.0/src/epd2in9.cpp b/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/epd2in9.cpp similarity index 71% rename from lib/esp-epaper-29-ws-20171230-gemu-1.0/src/epd2in9.cpp rename to lib/esp-epaper-29-ws-20171230-gemu-1.1/src/epd2in9.cpp index 3e9168db3..686b0391e 100644 --- a/lib/esp-epaper-29-ws-20171230-gemu-1.0/src/epd2in9.cpp +++ b/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/epd2in9.cpp @@ -27,20 +27,78 @@ #include #include "epd2in9.h" -Epd::~Epd() { -}; +extern uint8_t *buffer; + +Epd::Epd(int16_t width, int16_t height) : +Paint(width,height) { +} + +void Epd::DisplayOnff(int8_t on) { +} + +void Epd::Updateframe() { + SetFrameMemory(buffer, 0, 0, EPD_WIDTH,EPD_HEIGHT); + DisplayFrame(); + //Serial.printf("update\n"); +} + +#define DISPLAY_INIT_MODE 0 +#define DISPLAY_INIT_PARTIAL 1 +#define DISPLAY_INIT_FULL 2 -Epd::Epd() { - //reset_pin = RST_PIN; - //dc_pin = DC_PIN; - cs_pin = CS_PIN; - mosi_pin = MOSI_PIN; - sclk_pin = SCLK_PIN; - //busy_pin = BUSY_PIN; - width = EPD_WIDTH; - height = EPD_HEIGHT; -}; +void Epd::DisplayInit(int8_t p,int8_t size,int8_t rot,int8_t font) { +// ignore update mode + if (p==DISPLAY_INIT_PARTIAL) { + Init(lut_partial_update); + //ClearFrameMemory(0xFF); // bit set = white, bit reset = black + DisplayFrame(); + delay(500); + return; + //Serial.printf("partial\n"); + } else if (p==DISPLAY_INIT_FULL) { + Init(lut_full_update); + //ClearFrameMemory(0xFF); // bit set = white, bit reset = black + DisplayFrame(); + delay(3500); + //Serial.printf("full\n"); + return; + } else { + Updateframe(); + } + setRotation(rot); + invertDisplay(false); + setTextWrap(false); // Allow text to run off edges + cp437(true); + setTextFont(font&3); + setTextSize(size&7); + setTextColor(WHITE,BLACK); + setCursor(0,0); + fillScreen(BLACK); +} + +int16_t Epd::Begin(int16_t cs,int16_t mosi,int16_t sclk) { + cs_pin=cs; + mosi_pin=mosi; + sclk_pin=sclk; +} + + +void Epd::Init(int8_t p) { + if (p==DISPLAY_INIT_PARTIAL) { + Init(lut_partial_update); + } else { + Init(lut_full_update); + } + ClearFrameMemory(0xFF); + DisplayFrame(); + if (p==DISPLAY_INIT_PARTIAL) { + delay(350); + } else { + delay(3500); + } +} + int Epd::Init(const unsigned char* lut) { /* this calls the peripheral hardware interface, see epdif */ @@ -56,6 +114,13 @@ int Epd::Init(const unsigned char* lut) { pinMode(mosi_pin, OUTPUT); pinMode(sclk_pin, OUTPUT); + digitalWrite(cs_pin,HIGH); + digitalWrite(mosi_pin,LOW); + digitalWrite(sclk_pin,LOW); + + width = EPD_WIDTH; + height = EPD_HEIGHT; + /* EPD hardware init start */ this->lut = lut; Reset(); @@ -103,9 +168,9 @@ void Epd::SendData(unsigned char data) { */ void Epd::WaitUntilIdle(void) { return; - while(DigitalRead(busy_pin) == HIGH) { //LOW: idle, HIGH: busy - DelayMs(100); - } + //while(DigitalRead(busy_pin) == HIGH) { //LOW: idle, HIGH: busy + // DelayMs(100); + //} } /** @@ -115,9 +180,9 @@ void Epd::WaitUntilIdle(void) { */ void Epd::Reset(void) { //DigitalWrite(reset_pin, LOW); //module reset - DelayMs(200); + //delay(200); //DigitalWrite(reset_pin, HIGH); - DelayMs(200); + //delay(200); } /** @@ -179,7 +244,7 @@ void Epd::SetFrameMemory( /* send the image data */ for (uint16_t j = 0; j < y_end - y + 1; j++) { for (uint16_t i = 0; i < (x_end - x + 1) / 8; i++) { - SendData(image_buffer[i + j * (image_width / 8)]); + SendData(image_buffer[i + j * (image_width / 8)]^0xff); } } } @@ -207,7 +272,7 @@ void Epd::SetFrameMemory(const unsigned char* image_buffer) { SendCommand(WRITE_RAM); /* send the image data */ for (int i = 0; i < this->width / 8 * this->height; i++) { - SendData(pgm_read_byte(&image_buffer[i])); + SendData(pgm_read_byte(&image_buffer[i])^0xff); } } @@ -295,23 +360,68 @@ const unsigned char lut_partial_update[] = 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; -void Epd::fastSPIwrite(uint8_t d,uint8_t dc) { - digitalWrite(cs_pin, LOW); +#define PIN_OUT_SET 0x60000304 +#define PIN_OUT_CLEAR 0x60000308 - // transfer dc - digitalWrite(sclk_pin, LOW); - if(dc) digitalWrite(mosi_pin, HIGH); - else digitalWrite(mosi_pin, LOW); - digitalWrite(sclk_pin, HIGH); +#define PWRITE xdigitalWrite + + +#ifndef SSPI_USEANYPIN +// uses about 2.75 usecs, 365 kb /sec +// however does not work with GPIO 16 !!!! +void ICACHE_RAM_ATTR Epd::fastSPIwrite(uint8_t d,uint8_t dc) { + + WRITE_PERI_REG( PIN_OUT_CLEAR, 1<>= 1) { - digitalWrite(sclk_pin, LOW); - if(d & bit) digitalWrite(mosi_pin, HIGH); - else digitalWrite(mosi_pin, LOW); - digitalWrite(sclk_pin, HIGH); + WRITE_PERI_REG( PIN_OUT_CLEAR, 1< 76 kb / sec +// can use any pin +void Epd::fastSPIwrite(uint8_t d,uint8_t dc) { + + PWRITE(cs_pin, LOW); + + // transfer dc + PWRITE(sclk_pin, LOW); + if(dc) PWRITE(mosi_pin, HIGH); + else PWRITE(mosi_pin, LOW); + PWRITE(sclk_pin, HIGH); + + for(uint8_t bit = 0x80; bit; bit >>= 1) { + PWRITE(sclk_pin, LOW); + if(d & bit) PWRITE(mosi_pin, HIGH); + else PWRITE(mosi_pin, LOW); + PWRITE(sclk_pin, HIGH); } - digitalWrite(cs_pin, HIGH); + PWRITE(cs_pin, HIGH); } + + +#endif + /* END OF FILE */ diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.0/src/epd2in9.h b/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/epd2in9.h similarity index 89% rename from lib/esp-epaper-29-ws-20171230-gemu-1.0/src/epd2in9.h rename to lib/esp-epaper-29-ws-20171230-gemu-1.1/src/epd2in9.h index 464fcfab8..99459b198 100644 --- a/lib/esp-epaper-29-ws-20171230-gemu-1.0/src/epd2in9.h +++ b/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/epd2in9.h @@ -27,11 +27,14 @@ #ifndef EPD2IN9_H #define EPD2IN9_H -#include "epdif.h" +#include "epdpaint.h" + // Display resolution #define EPD_WIDTH 128 #define EPD_HEIGHT 296 +//#define EPD_WIDTH 296 +//#define EPD_HEIGHT 128 // EPD2IN9 commands #define DRIVER_OUTPUT_CONTROL 0x01 @@ -59,14 +62,16 @@ extern const unsigned char lut_full_update[]; extern const unsigned char lut_partial_update[]; -class Epd : EpdIf { +class Epd : public Paint { public: - unsigned long width; - unsigned long height; + Epd(int16_t width, int16_t height); + int16_t width; + int16_t height; Epd(); ~Epd(); int Init(const unsigned char* lut); + void Init(int8_t p); void SendCommand(unsigned char command); void SendData(unsigned char data); void WaitUntilIdle(void); @@ -82,23 +87,26 @@ public: void ClearFrameMemory(unsigned char color); void DisplayFrame(void); void Sleep(void); + void fastSPIwrite(uint8_t d,uint8_t dc); + + void DisplayOnff(int8_t on); + void DisplayInit(int8_t p,int8_t size,int8_t rot,int8_t font); + int16_t Begin(int16_t p1,int16_t p2,int16_t p3); + void Updateframe(); - unsigned int cs_pin; - unsigned int mosi_pin; - unsigned int sclk_pin; - private: unsigned int reset_pin; unsigned int dc_pin; unsigned int busy_pin; const unsigned char* lut; - - + unsigned int cs_pin; + unsigned int mosi_pin; + unsigned int sclk_pin; void SetLut(const unsigned char* lut); void SetMemoryArea(int x_start, int y_start, int x_end, int y_end); void SetMemoryPointer(int x, int y); - void fastSPIwrite(uint8_t d,uint8_t dc); + //void fastSPIwrite(uint8_t d,uint8_t dc); }; #endif /* EPD2IN9_H */ diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/epd4in2.cpp b/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/epd4in2.cpp new file mode 100644 index 000000000..e2fe5dd34 --- /dev/null +++ b/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/epd4in2.cpp @@ -0,0 +1,566 @@ +/** + * @filename : epd4in2.cpp + * @brief : Implements for Dual-color e-paper library + * @author : Yehui from Waveshare + * + * Copyright (C) Waveshare August 10 2017 + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documnetation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include + + +//#define SSPI_USEANYPIN + +extern uint8_t *buffer; +uint8_t epd42_mode; + +Epd42::Epd42(int16_t width, int16_t height) : +Paint(width,height) { +} + +void Epd42::DisplayOnff(int8_t on) { +} + + +#define DISPLAY_INIT_MODE 0 +#define DISPLAY_INIT_PARTIAL 1 +#define DISPLAY_INIT_FULL 2 + +void Epd42::Updateframe() { + //SetFrameMemory(buffer, 0, 0, EPD_WIDTH,EPD_HEIGHT); + SetPartialWindow(buffer, 0, 0, width,height,2); + if (epd42_mode==DISPLAY_INIT_PARTIAL) { + DisplayFrameQuick(); + } else { + DisplayFrame(); + } + //Serial.printf("update\n"); +} + +void Epd42::DisplayInit(int8_t p,int8_t size,int8_t rot,int8_t font) { +// ignore update mode + if (p==DISPLAY_INIT_PARTIAL) { + epd42_mode=p; + //Init(lut_partial_update); + //ClearFrameMemory(0xFF); // bit set = white, bit reset = black + DisplayFrameQuick(); + delay(500); + return; + //Serial.printf("partial\n"); + } else if (p==DISPLAY_INIT_FULL) { + epd42_mode=p; + //Init(lut_full_update); + //ClearFrameMemory(0xFF); // bit set = white, bit reset = black + DisplayFrame(); + delay(4500); + return; + //Serial.printf("full\n"); + } else { + epd42_mode=DISPLAY_INIT_FULL; + Updateframe(); + } + + setRotation(rot); + invertDisplay(false); + setTextWrap(false); // Allow text to run off edges + cp437(true); + setTextFont(font); + setTextSize(size); + setTextColor(WHITE,BLACK); + setCursor(0,0); + fillScreen(BLACK); +} + +int16_t Epd42::Begin(int16_t cs,int16_t mosi,int16_t sclk) { + cs_pin=cs; + mosi_pin=mosi; + sclk_pin=sclk; +} + +void Epd42::Init(int8_t p) { + epd42_mode=p; + DisplayFrame(); + delay(4000); +} + +int Epd42::Init(void) { + pinMode(cs_pin, OUTPUT); + pinMode(mosi_pin, OUTPUT); + pinMode(sclk_pin, OUTPUT); + + digitalWrite(cs_pin,HIGH); + digitalWrite(mosi_pin,LOW); + digitalWrite(sclk_pin,LOW); + + width = EPD_WIDTH42; + height = EPD_HEIGHT42; + + Reset(); + SendCommand(POWER_SETTING); + SendData(0x03); // VDS_EN, VDG_EN + SendData(0x00); // VCOM_HV, VGHL_LV[1], VGHL_LV[0] + SendData(0x2b); // VDH + SendData(0x2b); // VDL + SendData(0xff); // VDHR + SendCommand(BOOSTER_SOFT_START); + SendData(0x17); + SendData(0x17); + SendData(0x17); //07 0f 17 1f 27 2F 37 2f + SendCommand(POWER_ON); + WaitUntilIdle(); + SendCommand(PANEL_SETTING); + // SendData(0xbf); // KW-BF KWR-AF BWROTP 0f + // SendData(0x0b); +// SendData(0x0F); //300x400 Red mode, LUT from OTP +// SendData(0x1F); //300x400 B/W mode, LUT from OTP + SendData(0x3F); //300x400 B/W mode, LUT set by register +// SendData(0x2F); //300x400 Red mode, LUT set by register + + SendCommand(PLL_CONTROL); + SendData(0x3C); // 3A 100Hz 29 150Hz 39 200Hz 31 171Hz 3C 50Hz (default) 0B 10Hz + //SendData(0x0B); //0B is 10Hz + /* EPD hardware init end */ + return 0; +} + +/** + * @brief: basic function for sending commands + */ +void Epd42::SendCommand(unsigned char command) { + //DigitalWrite(dc_pin, LOW); + //SpiTransfer(command); + fastSPIwrite(command,0); +} + +/** + * @brief: basic function for sending data + */ +void Epd42::SendData(unsigned char data) { + //DigitalWrite(dc_pin, HIGH); + //SpiTransfer(data); + fastSPIwrite(data,1); +} + +/** + * @brief: Wait until the busy_pin goes HIGH + */ +void Epd42::WaitUntilIdle(void) { + // while(DigitalRead(busy_pin) == 0) { //0: busy, 1: idle + // DelayMs(1); + // } +} + +/** + * @brief: module reset. + * often used to awaken the module in deep sleep, + * see Epd::Sleep(); + */ +void Epd42::Reset(void) { + //DigitalWrite(reset_pin, LOW); + //DelayMs(200); + //DigitalWrite(reset_pin, HIGH); + //DelayMs(200); +} +/** + * @brief: transmit partial data to the SRAM. The final parameter chooses between dtm=1 and dtm=2 + */ +void Epd42::SetPartialWindow(const unsigned char* buffer_black, int x, int y, int w, int l, int dtm) { + SendCommand(PARTIAL_IN); + SendCommand(PARTIAL_WINDOW); + SendData(x >> 8); + SendData(x & 0xf8); // x should be the multiple of 8, the last 3 bit will always be ignored + SendData(((x & 0xf8) + w - 1) >> 8); + SendData(((x & 0xf8) + w - 1) | 0x07); + SendData(y >> 8); + SendData(y & 0xff); + SendData((y + l - 1) >> 8); + SendData((y + l - 1) & 0xff); + SendData(0x01); // Gates scan both inside and outside of the partial window. (default) + // DelayMs(2); + SendCommand((dtm == 1) ? DATA_START_TRANSMISSION_1 : DATA_START_TRANSMISSION_2); + if (buffer_black != NULL) { + for(int i = 0; i < w / 8 * l; i++) { + SendData(buffer_black[i]^0xff); + } + } else { + for(int i = 0; i < w / 8 * l; i++) { + SendData(0x00); + } + } + // DelayMs(2); + SendCommand(PARTIAL_OUT); +} + + + +/** + * @brief: set the look-up table + */ +void Epd42::SetLut(void) { + unsigned int count; + SendCommand(LUT_FOR_VCOM); //vcom + for(count = 0; count < 44; count++) { + SendData(pgm_read_byte(&lut_vcom0[count])); + } + + SendCommand(LUT_WHITE_TO_WHITE); //ww -- + for(count = 0; count < 42; count++) { + SendData(pgm_read_byte(&lut_ww[count])); + } + + SendCommand(LUT_BLACK_TO_WHITE); //bw r + for(count = 0; count < 42; count++) { + SendData(pgm_read_byte(&lut_bw[count])); + } + + SendCommand(LUT_WHITE_TO_BLACK); //wb w + for(count = 0; count < 42; count++) { + SendData(pgm_read_byte(&lut_wb[count])); + } + + SendCommand(LUT_BLACK_TO_BLACK); //bb b + for(count = 0; count < 42; count++) { + SendData(pgm_read_byte(&lut_bb[count])); + } +} + + +/** + * @brief: set the look-up table for quick display (partial refresh) + */ + +void Epd42::SetLutQuick(void) { + unsigned int count; + SendCommand(LUT_FOR_VCOM); //vcom + for(count = 0; count < 44; count++) { + SendData(pgm_read_byte(&lut_vcom0_quick[count])); + } + + SendCommand(LUT_WHITE_TO_WHITE); //ww -- + for(count = 0; count < 42; count++) { + SendData(pgm_read_byte(&lut_ww_quick[count])); + } + + SendCommand(LUT_BLACK_TO_WHITE); //bw r + for(count = 0; count < 42; count++) { + SendData(pgm_read_byte(&lut_bw_quick[count])); + } + + SendCommand(LUT_WHITE_TO_BLACK); //wb w + for(count = 0; count < 42; count++) { + SendData(pgm_read_byte(&lut_wb_quick[count])); + } + + SendCommand(LUT_BLACK_TO_BLACK); //bb b + for(count = 0; count < 42; count++) { + SendData(pgm_read_byte(&lut_bb_quick[count])); + } +} + + +/** + * @brief: refresh and displays the frame + */ +void Epd42::DisplayFrame(const unsigned char* frame_buffer) { + SendCommand(RESOLUTION_SETTING); + SendData(width >> 8); + SendData(width & 0xff); + SendData(height >> 8); + SendData(height & 0xff); + + SendCommand(VCM_DC_SETTING); + SendData(0x12); + + SendCommand(VCOM_AND_DATA_INTERVAL_SETTING); + SendCommand(0x97); //VBDF 17|D7 VBDW 97 VBDB 57 VBDF F7 VBDW 77 VBDB 37 VBDR B7 + + if (frame_buffer != NULL) { + SendCommand(DATA_START_TRANSMISSION_1); + for(int i = 0; i < width / 8 * height; i++) { + SendData(0xFF); // bit set: white, bit reset: black + } + delay(2); + SendCommand(DATA_START_TRANSMISSION_2); + for(int i = 0; i < width / 8 * height; i++) { + SendData(pgm_read_byte(&frame_buffer[i])); + } + delay(2); + } + + SetLut(); + + SendCommand(DISPLAY_REFRESH); + delay(100); + WaitUntilIdle(); +} + + + + +/** + * @brief: clear the frame data from the SRAM, this won't refresh the display + */ +void Epd42::ClearFrame(void) { + SendCommand(RESOLUTION_SETTING); + SendData(width >> 8); + SendData(width & 0xff); + SendData(height >> 8); + SendData(height & 0xff); + + SendCommand(DATA_START_TRANSMISSION_1); + delay(2); + for(int i = 0; i < width / 8 * height; i++) { + SendData(0xFF); + } + delay(2); + SendCommand(DATA_START_TRANSMISSION_2); + delay(2); + for(int i = 0; i < width / 8 * height; i++) { + SendData(0xFF); + } + delay(2); +} + + + +/** + * @brief: This displays the frame data from SRAM + */ +void Epd42::DisplayFrame(void) { + SetLut(); + SendCommand(DISPLAY_REFRESH); + delay(100); + WaitUntilIdle(); +} + +void Epd42::DisplayFrameQuick(void) { + SetLutQuick(); + SendCommand(DISPLAY_REFRESH); + // DelayMs(100); + // WaitUntilIdle(); +} + + +/** + * @brief: After this command is transmitted, the chip would enter the deep-sleep mode to save power. + * The deep sleep mode would return to standby by hardware reset. The only one parameter is a + * check code, the command would be executed if check code = 0xA5. + * You can use Epd::Reset() to awaken and use Epd::Init() to initialize. + */ +void Epd42::Sleep() { + SendCommand(VCOM_AND_DATA_INTERVAL_SETTING); + SendData(0x17); //border floating + SendCommand(VCM_DC_SETTING); //VCOM to 0V + SendCommand(PANEL_SETTING); + delay(100); + + SendCommand(POWER_SETTING); //VG&VS to 0V fast + SendData(0x00); + SendData(0x00); + SendData(0x00); + SendData(0x00); + SendData(0x00); + delay(100); + + SendCommand(POWER_OFF); //power off + WaitUntilIdle(); + SendCommand(DEEP_SLEEP); //deep sleep + SendData(0xA5); +} + +const unsigned char lut_vcom0[] PROGMEM = +{ +0x40, 0x17, 0x00, 0x00, 0x00, 0x02, +0x00, 0x17, 0x17, 0x00, 0x00, 0x02, +0x00, 0x0A, 0x01, 0x00, 0x00, 0x01, +0x00, 0x0E, 0x0E, 0x00, 0x00, 0x02, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const unsigned char lut_vcom0_quick[] PROGMEM = +{ +0x00, 0x0E, 0x00, 0x00, 0x00, 0x01, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + + + +const unsigned char lut_ww[] PROGMEM = +{ +0x40, 0x17, 0x00, 0x00, 0x00, 0x02, +0x90, 0x17, 0x17, 0x00, 0x00, 0x02, +0x40, 0x0A, 0x01, 0x00, 0x00, 0x01, +0xA0, 0x0E, 0x0E, 0x00, 0x00, 0x02, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const unsigned char lut_ww_quick[] PROGMEM = +{ +0xA0, 0x0E, 0x00, 0x00, 0x00, 0x01, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + + +const unsigned char lut_bw[] PROGMEM = +{ +0x40, 0x17, 0x00, 0x00, 0x00, 0x02, +0x90, 0x17, 0x17, 0x00, 0x00, 0x02, +0x40, 0x0A, 0x01, 0x00, 0x00, 0x01, +0xA0, 0x0E, 0x0E, 0x00, 0x00, 0x02, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + + +const unsigned char lut_bw_quick[] PROGMEM = +{ +0xA0, 0x0E, 0x00, 0x00, 0x00, 0x01, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const unsigned char lut_bb[] PROGMEM = +{ +0x80, 0x17, 0x00, 0x00, 0x00, 0x02, +0x90, 0x17, 0x17, 0x00, 0x00, 0x02, +0x80, 0x0A, 0x01, 0x00, 0x00, 0x01, +0x50, 0x0E, 0x0E, 0x00, 0x00, 0x02, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const unsigned char lut_bb_quick[] PROGMEM = +{ +0x50, 0x0E, 0x00, 0x00, 0x00, 0x01, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + + +const unsigned char lut_wb[] PROGMEM = +{ +0x80, 0x17, 0x00, 0x00, 0x00, 0x02, +0x90, 0x17, 0x17, 0x00, 0x00, 0x02, +0x80, 0x0A, 0x01, 0x00, 0x00, 0x01, +0x50, 0x0E, 0x0E, 0x00, 0x00, 0x02, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const unsigned char lut_wb_quick[] PROGMEM = +{ +0x50, 0x0E, 0x00, 0x00, 0x00, 0x01, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + + + +#define PIN_OUT_SET 0x60000304 +#define PIN_OUT_CLEAR 0x60000308 + +#define PWRITE ydigitalWrite + +#ifndef SSPI_USEANYPIN +// uses about 2.75 usecs, 365 kb /sec +// however does not work with GPIO 16 !!!! +void ICACHE_RAM_ATTR Epd42::fastSPIwrite(uint8_t d,uint8_t dc) { + + WRITE_PERI_REG( PIN_OUT_CLEAR, 1<>= 1) { + WRITE_PERI_REG( PIN_OUT_CLEAR, 1< 76 kb / sec +// can use any pin +void Epd42::fastSPIwrite(uint8_t d,uint8_t dc) { + + PWRITE(cs_pin, LOW); + + // transfer dc + PWRITE(sclk_pin, LOW); + if(dc) PWRITE(mosi_pin, HIGH); + else PWRITE(mosi_pin, LOW); + PWRITE(sclk_pin, HIGH); + + for(uint8_t bit = 0x80; bit; bit >>= 1) { + PWRITE(sclk_pin, LOW); + if(d & bit) PWRITE(mosi_pin, HIGH); + else PWRITE(mosi_pin, LOW); + PWRITE(sclk_pin, HIGH); + } + + PWRITE(cs_pin, HIGH); +} +#endif + +/* END OF FILE */ diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/epd4in2.h b/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/epd4in2.h new file mode 100644 index 000000000..9b140c0d8 --- /dev/null +++ b/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/epd4in2.h @@ -0,0 +1,130 @@ +/** + * @filename : epd4in2.h + * @brief : Header file for Dual-color e-paper library epd4in2.cpp + * @author : Yehui from Waveshare + * + * Copyright (C) Waveshare August 10 2017 + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documnetation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef EPD4IN2_H +#define EPD4IN2_H + +#include "epdpaint.h" + +// Display resolution +#define EPD_WIDTH42 400 +#define EPD_HEIGHT42 300 + +// EPD4IN2 commands +#define PANEL_SETTING 0x00 +#define POWER_SETTING 0x01 +#define POWER_OFF 0x02 +#define POWER_OFF_SEQUENCE_SETTING 0x03 +#define POWER_ON 0x04 +#define POWER_ON_MEASURE 0x05 +#define BOOSTER_SOFT_START 0x06 +#define DEEP_SLEEP 0x07 +#define DATA_START_TRANSMISSION_1 0x10 +#define DATA_STOP 0x11 +#define DISPLAY_REFRESH 0x12 +#define DATA_START_TRANSMISSION_2 0x13 +#define LUT_FOR_VCOM 0x20 +#define LUT_WHITE_TO_WHITE 0x21 +#define LUT_BLACK_TO_WHITE 0x22 +#define LUT_WHITE_TO_BLACK 0x23 +#define LUT_BLACK_TO_BLACK 0x24 +#define PLL_CONTROL 0x30 +#define TEMPERATURE_SENSOR_COMMAND 0x40 +#define TEMPERATURE_SENSOR_SELECTION 0x41 +#define TEMPERATURE_SENSOR_WRITE 0x42 +#define TEMPERATURE_SENSOR_READ 0x43 +#define VCOM_AND_DATA_INTERVAL_SETTING 0x50 +#define LOW_POWER_DETECTION 0x51 +#define TCON_SETTING 0x60 +#define RESOLUTION_SETTING 0x61 +#define GSST_SETTING 0x65 +#define GET_STATUS 0x71 +#define AUTO_MEASUREMENT_VCOM 0x80 +#define READ_VCOM_VALUE 0x81 +#define VCM_DC_SETTING 0x82 +#define PARTIAL_WINDOW 0x90 +#define PARTIAL_IN 0x91 +#define PARTIAL_OUT 0x92 +#define PROGRAM_MODE 0xA0 +#define ACTIVE_PROGRAMMING 0xA1 +#define READ_OTP 0xA2 +#define POWER_SAVING 0xE3 + +extern const unsigned char lut_vcom0[]; +extern const unsigned char lut_ww[]; +extern const unsigned char lut_bw[]; +extern const unsigned char lut_bb[]; +extern const unsigned char lut_wb[]; + +extern const unsigned char lut_vcom0_quick[]; +extern const unsigned char lut_ww_quick[]; +extern const unsigned char lut_bw_quick[]; +extern const unsigned char lut_bb_quick[]; +extern const unsigned char lut_wb_quick[]; + + + + +class Epd42 : public Paint { +public: + Epd42(int16_t width, int16_t height); + + int Init(void); + void Init(int8_t p); + void SendCommand(unsigned char command); + void SendData(unsigned char data); + void WaitUntilIdle(void); + void Reset(void); + + void SetPartialWindow(const unsigned char* frame_buffer, int x, int y, int w, int l, int dtm); + + void SetPartialWindowBlack(const unsigned char* buffer_black, int x, int y, int w, int l); + void SetPartialWindowRed(const unsigned char* buffer_red, int x, int y, int w, int l); + void SetLut(void); + void SetLutQuick(void); + void DisplayFrame(const unsigned char* frame_buffer); + void DisplayFrame(void); + void DisplayFrameQuick(void); + void ClearFrame(void); + void Sleep(void); + + void DisplayOnff(int8_t on); + void DisplayInit(int8_t p,int8_t size,int8_t rot,int8_t font); + int16_t Begin(int16_t p1,int16_t p2,int16_t p3); + void Updateframe(); + +private: + void fastSPIwrite(uint8_t d,uint8_t dc); + uint8_t cs_pin; + uint8_t mosi_pin; + uint8_t sclk_pin; + int16_t width; + int16_t height; +}; + +#endif /* EPD4IN2_H */ + +/* END OF FILE */ diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.0/src/epdif.cpp b/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/epdif.cpp similarity index 100% rename from lib/esp-epaper-29-ws-20171230-gemu-1.0/src/epdif.cpp rename to lib/esp-epaper-29-ws-20171230-gemu-1.1/src/epdif.cpp diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.0/src/epdif.h b/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/epdif.h similarity index 100% rename from lib/esp-epaper-29-ws-20171230-gemu-1.0/src/epdif.h rename to lib/esp-epaper-29-ws-20171230-gemu-1.1/src/epdif.h diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/epdpaint.cpp b/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/epdpaint.cpp new file mode 100644 index 000000000..7b6db3206 --- /dev/null +++ b/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/epdpaint.cpp @@ -0,0 +1,177 @@ +/** + * @filename : epdpaint.cpp + * @brief : Paint tools + * @author : Yehui from Waveshare + * + * Copyright (C) Waveshare September 9 2017 + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documnetation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include "epdpaint.h" + +extern uint8_t *buffer; + +Paint::Paint(int16_t width, int16_t height) : +Renderer(width,height) { +} + + +void Paint::DisplayOnff(int8_t on) { +} + +int16_t Paint::Begin(int16_t p1,int16_t p2,int16_t p3) { + +} + +void Paint::Updateframe() { +} + +void Paint::DisplayInit(int8_t p,int8_t size,int8_t rot,int8_t font) { + +} + + +#define renderer_swap(a, b) { int16_t t = a; a = b; b = t; } +/** + * @brief: this draws a pixel by absolute coordinates. + * this function won't be affected by the rotate parameter. + * we must use this for epaper because these displays have a strange and different bit pattern + */ +void Paint::DrawAbsolutePixel(int x, int y, int16_t color) { + + int16_t w=width(),h=height(),rot=getRotation(); + if (rot==1 || rot==3) { + renderer_swap(w,h); + } + + if (x < 0 || x >= w || y < 0 || y >= h) { + return; + } + if (IF_INVERT_COLOR) { + if (color) { + buffer[(x + y * w) / 8] |= 0x80 >> (x % 8); + } else { + buffer[(x + y * w) / 8] &= ~(0x80 >> (x % 8)); + } + } else { + if (color) { + buffer[(x + y * w) / 8] &= ~(0x80 >> (x % 8)); + } else { + buffer[(x + y * w) / 8] |= 0x80 >> (x % 8); + } + } +} + +#if 0 +/** + * @brief: this draws a pixel by the coordinates + */ +void Paint::drawPixel(int16_t x, int16_t y, uint16_t color) { + int16_t point_temp; + int8_t rot=getRotation(); + if (rot == ROTATE_0) { + if(x < 0 || x >= width() || y < 0 || y >= height()) { + return; + } + DrawAbsolutePixel(x, y, color); + } else if (rot== ROTATE_90) { + if(x < 0 || x >= width() || y < 0 || y >=height() ) { + return; + } + point_temp = x; + x = height() - y; + y = point_temp; + DrawAbsolutePixel(x, y, color); + } else if (rot == ROTATE_180) { + if(x < 0 || x >= width() || y < 0 || y >= height()) { + return; + } + x = width() - x; + y = height() - y; + DrawAbsolutePixel(x, y, color); + } else if (rot == ROTATE_270) { + if(x < 0 || x >= width() || y < 0 || y >= height()) { + return; + } + point_temp = x; + x = y; + y = width() - point_temp; + DrawAbsolutePixel(x, y, color); + } +} +#else + + +void Paint::drawPixel(int16_t x, int16_t y, uint16_t color) { + if (!buffer) return; + if ((x < 0) || (x >= width()) || (y < 0) || (y >= height())) + return; + + // check rotation, move pixel around if necessary + switch (getRotation()) { + case 1: + renderer_swap(x, y); + x = WIDTH - x - 1; + break; + case 2: + x = WIDTH - x - 1; + y = HEIGHT - y - 1; + break; + case 3: + renderer_swap(x, y); + y = HEIGHT - y - 1; + break; + } + + // x is which column + DrawAbsolutePixel(x,y,color); + /* + switch (color) + { + case WHITE: buffer[x+ (y/8)*WIDTH] |= (1 << (y&7)); break; + case BLACK: buffer[x+ (y/8)*WIDTH] &= ~(1 << (y&7)); break; + case INVERSE: buffer[x+ (y/8)*WIDTH] ^= (1 << (y&7)); break; + }*/ + +} +#endif +/** +* @brief: this draws a horizontal line on the frame buffer +*/ +void Paint::drawFastHLine(int16_t x, int16_t y, int16_t line_width, uint16_t colored) { + int i; + for (i = x; i < x + line_width; i++) { + drawPixel(i, y, colored); + } +} + +/** +* @brief: this draws a vertical line on the frame buffer +*/ +void Paint::drawFastVLine(int16_t x, int16_t y, int16_t line_height, uint16_t colored) { + int i; + for (i = y; i < y + line_height; i++) { + drawPixel(x, i, colored); + } +} + + +/* END OF FILE */ diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.0/src/epdpaint.h b/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/epdpaint.h similarity index 58% rename from lib/esp-epaper-29-ws-20171230-gemu-1.0/src/epdpaint.h rename to lib/esp-epaper-29-ws-20171230-gemu-1.1/src/epdpaint.h index 77a3b190c..c70d762d1 100644 --- a/lib/esp-epaper-29-ws-20171230-gemu-1.0/src/epdpaint.h +++ b/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/epdpaint.h @@ -37,36 +37,21 @@ #define IF_INVERT_COLOR 1 #include "fonts.h" +#include "renderer.h" -class Paint { +class Paint : public Renderer { public: - Paint(unsigned char* image, int width, int height); - ~Paint(); - void Clear(int colored); - int GetWidth(void); - void SetWidth(int width); - int GetHeight(void); - void SetHeight(int height); - int GetRotate(void); - void SetRotate(int rotate); - unsigned char* GetImage(void); - void DrawAbsolutePixel(int x, int y, int colored); - void DrawPixel(int x, int y, int colored); - void DrawCharAt(int x, int y, char ascii_char, sFONT* font, int colored); - void DrawStringAt(int x, int y, const char* text, sFONT* font, int colored); - void DrawLine(int x0, int y0, int x1, int y1, int colored); - void DrawHorizontalLine(int x, int y, int width, int colored); - void DrawVerticalLine(int x, int y, int height, int colored); - void DrawRectangle(int x0, int y0, int x1, int y1, int colored); - void DrawFilledRectangle(int x0, int y0, int x1, int y1, int colored); - void DrawCircle(int x, int y, int radius, int colored); - void DrawFilledCircle(int x, int y, int radius, int colored); + Paint(int16_t width, int16_t height); + void drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color); + void drawFastVLine(int16_t x, int16_t y, int16_t w, uint16_t color); + void drawPixel(int16_t x, int16_t y, uint16_t color); + void DrawAbsolutePixel(int x, int y, int16_t color); + + void DisplayOnff(int8_t on); + void DisplayInit(int8_t p,int8_t size,int8_t rot,int8_t font); + int16_t Begin(int16_t p1,int16_t p2,int16_t p3); + void Updateframe(); -private: - unsigned char* image; - uint16_t width; - uint16_t height; - uint8_t rotate; }; #endif diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/font12.c b/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/font12.c new file mode 100644 index 000000000..675919df4 --- /dev/null +++ b/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/font12.c @@ -0,0 +1,1500 @@ +/** + ****************************************************************************** + * @file Font12.c + * @author MCD Application Team + * @version V1.0.0 + * @date 18-February-2014 + * @brief This file provides text Font12 for STM32xx-EVAL's LCD driver. + ****************************************************************************** + * @attention + * + *

© COPYRIGHT(c) 2014 STMicroelectronics

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of STMicroelectronics nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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. + * + ****************************************************************************** + */ + +/* Includes ------------------------------------------------------------------*/ +#include "fonts.h" +#include + +// +// Font data for Courier New 12pt +// + +const uint8_t Font12_Table[] PROGMEM = +{ + // @0 ' ' (7 pixels wide) + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + + // @12 '!' (7 pixels wide) + 0x00, // + 0x10, // # + 0x10, // # + 0x10, // # + 0x10, // # + 0x10, // # + 0x00, // + 0x00, // + 0x10, // # + 0x00, // + 0x00, // + 0x00, // + + // @24 '"' (7 pixels wide) + 0x00, // + 0x6c, // ## ## + 0x48, // # # + 0x48, // # # + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + + // @36 '#' (7 pixels wide) + 0x00, // + 0x14, // # # + 0x14, // # # + 0x28, // # # + 0x7c, // ##### + 0x28, // # # + 0x7c, // ##### + 0x28, // # # + 0x50, // # # + 0x50, // # # + 0x00, // + 0x00, // + + // @48 '$' (7 pixels wide) + 0x00, // + 0x10, // # + 0x38, // ### + 0x40, // # + 0x40, // # + 0x38, // ### + 0x48, // # # + 0x70, // ### + 0x10, // # + 0x10, // # + 0x00, // + 0x00, // + + // @60 '%' (7 pixels wide) + 0x00, // + 0x20, // # + 0x50, // # # + 0x20, // # + 0x0c, // ## + 0x70, // ### + 0x08, // # + 0x14, // # # + 0x08, // # + 0x00, // + 0x00, // + 0x00, // + + // @72 '&' (7 pixels wide) + 0x00, // + 0x00, // + 0x00, // + 0x18, // ## + 0x20, // # + 0x20, // # + 0x54, // # # # + 0x48, // # # + 0x34, // ## # + 0x00, // + 0x00, // + 0x00, // + + // @84 ''' (7 pixels wide) + 0x00, // + 0x10, // # + 0x10, // # + 0x10, // # + 0x10, // # + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + + // @96 '(' (7 pixels wide) + 0x00, // + 0x08, // # + 0x08, // # + 0x10, // # + 0x10, // # + 0x10, // # + 0x10, // # + 0x10, // # + 0x10, // # + 0x08, // # + 0x08, // # + 0x00, // + + // @108 ')' (7 pixels wide) + 0x00, // + 0x20, // # + 0x20, // # + 0x10, // # + 0x10, // # + 0x10, // # + 0x10, // # + 0x10, // # + 0x10, // # + 0x20, // # + 0x20, // # + 0x00, // + + // @120 '*' (7 pixels wide) + 0x00, // + 0x10, // # + 0x7c, // ##### + 0x10, // # + 0x28, // # # + 0x28, // # # + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + + // @132 '+' (7 pixels wide) + 0x00, // + 0x00, // + 0x10, // # + 0x10, // # + 0x10, // # + 0xfe, //####### + 0x10, // # + 0x10, // # + 0x10, // # + 0x00, // + 0x00, // + 0x00, // + + // @144 ',' (7 pixels wide) + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x18, // ## + 0x10, // # + 0x30, // ## + 0x20, // # + 0x00, // + + // @156 '-' (7 pixels wide) + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x7c, // ##### + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + + // @168 '.' (7 pixels wide) + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x30, // ## + 0x30, // ## + 0x00, // + 0x00, // + 0x00, // + + // @180 '/' (7 pixels wide) + 0x00, // + 0x04, // # + 0x04, // # + 0x08, // # + 0x08, // # + 0x10, // # + 0x10, // # + 0x20, // # + 0x20, // # + 0x40, // # + 0x00, // + 0x00, // + + // @192 '0' (7 pixels wide) + 0x00, // + 0x38, // ### + 0x44, // # # + 0x44, // # # + 0x44, // # # + 0x44, // # # + 0x44, // # # + 0x44, // # # + 0x38, // ### + 0x00, // + 0x00, // + 0x00, // + + // @204 '1' (7 pixels wide) + 0x00, // + 0x30, // ## + 0x10, // # + 0x10, // # + 0x10, // # + 0x10, // # + 0x10, // # + 0x10, // # + 0x7c, // ##### + 0x00, // + 0x00, // + 0x00, // + + // @216 '2' (7 pixels wide) + 0x00, // + 0x38, // ### + 0x44, // # # + 0x04, // # + 0x08, // # + 0x10, // # + 0x20, // # + 0x44, // # # + 0x7c, // ##### + 0x00, // + 0x00, // + 0x00, // + + // @228 '3' (7 pixels wide) + 0x00, // + 0x38, // ### + 0x44, // # # + 0x04, // # + 0x18, // ## + 0x04, // # + 0x04, // # + 0x44, // # # + 0x38, // ### + 0x00, // + 0x00, // + 0x00, // + + // @240 '4' (7 pixels wide) + 0x00, // + 0x0c, // ## + 0x14, // # # + 0x14, // # # + 0x24, // # # + 0x44, // # # + 0x7e, // ###### + 0x04, // # + 0x0e, // ### + 0x00, // + 0x00, // + 0x00, // + + // @252 '5' (7 pixels wide) + 0x00, // + 0x3c, // #### + 0x20, // # + 0x20, // # + 0x38, // ### + 0x04, // # + 0x04, // # + 0x44, // # # + 0x38, // ### + 0x00, // + 0x00, // + 0x00, // + + // @264 '6' (7 pixels wide) + 0x00, // + 0x1c, // ### + 0x20, // # + 0x40, // # + 0x78, // #### + 0x44, // # # + 0x44, // # # + 0x44, // # # + 0x38, // ### + 0x00, // + 0x00, // + 0x00, // + + // @276 '7' (7 pixels wide) + 0x00, // + 0x7c, // ##### + 0x44, // # # + 0x04, // # + 0x08, // # + 0x08, // # + 0x08, // # + 0x10, // # + 0x10, // # + 0x00, // + 0x00, // + 0x00, // + + // @288 '8' (7 pixels wide) + 0x00, // + 0x38, // ### + 0x44, // # # + 0x44, // # # + 0x38, // ### + 0x44, // # # + 0x44, // # # + 0x44, // # # + 0x38, // ### + 0x00, // + 0x00, // + 0x00, // + + // @300 '9' (7 pixels wide) + 0x00, // + 0x38, // ### + 0x44, // # # + 0x44, // # # + 0x44, // # # + 0x3c, // #### + 0x04, // # + 0x08, // # + 0x70, // ### + 0x00, // + 0x00, // + 0x00, // + + // @312 ':' (7 pixels wide) + 0x00, // + 0x00, // + 0x00, // + 0x30, // ## + 0x30, // ## + 0x00, // + 0x00, // + 0x30, // ## + 0x30, // ## + 0x00, // + 0x00, // + 0x00, // + + // @324 ';' (7 pixels wide) + 0x00, // + 0x00, // + 0x00, // + 0x18, // ## + 0x18, // ## + 0x00, // + 0x00, // + 0x18, // ## + 0x30, // ## + 0x20, // # + 0x00, // + 0x00, // + + // @336 '<' (7 pixels wide) + 0x00, // + 0x00, // + 0x0c, // ## + 0x10, // # + 0x60, // ## + 0x80, //# + 0x60, // ## + 0x10, // # + 0x0c, // ## + 0x00, // + 0x00, // + 0x00, // + + // @348 '=' (7 pixels wide) + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x7c, // ##### + 0x00, // + 0x7c, // ##### + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + + // @360 '>' (7 pixels wide) + 0x00, // + 0x00, // + 0xc0, //## + 0x20, // # + 0x18, // ## + 0x04, // # + 0x18, // ## + 0x20, // # + 0xc0, //## + 0x00, // + 0x00, // + 0x00, // + + // @372 '?' (7 pixels wide) + 0x00, // + 0x00, // + 0x18, // ## + 0x24, // # # + 0x04, // # + 0x08, // # + 0x10, // # + 0x00, // + 0x30, // ## + 0x00, // + 0x00, // + 0x00, // + + // @384 '@' (7 pixels wide) + 0x38, // ### + 0x44, // # # + 0x44, // # # + 0x4c, // # ## + 0x54, // # # # + 0x54, // # # # + 0x4c, // # ## + 0x40, // # + 0x44, // # # + 0x38, // ### + 0x00, // + 0x00, // + + // @396 'A' (7 pixels wide) + 0x00, // + 0x30, // ## + 0x10, // # + 0x28, // # # + 0x28, // # # + 0x28, // # # + 0x7c, // ##### + 0x44, // # # + 0xee, //### ### + 0x00, // + 0x00, // + 0x00, // + + // @408 'B' (7 pixels wide) + 0x00, // + 0xf8, //##### + 0x44, // # # + 0x44, // # # + 0x78, // #### + 0x44, // # # + 0x44, // # # + 0x44, // # # + 0xf8, //##### + 0x00, // + 0x00, // + 0x00, // + + // @420 'C' (7 pixels wide) + 0x00, // + 0x3c, // #### + 0x44, // # # + 0x40, // # + 0x40, // # + 0x40, // # + 0x40, // # + 0x44, // # # + 0x38, // ### + 0x00, // + 0x00, // + 0x00, // + + // @432 'D' (7 pixels wide) + 0x00, // + 0xf0, //#### + 0x48, // # # + 0x44, // # # + 0x44, // # # + 0x44, // # # + 0x44, // # # + 0x48, // # # + 0xf0, //#### + 0x00, // + 0x00, // + 0x00, // + + // @444 'E' (7 pixels wide) + 0x00, // + 0xfc, //###### + 0x44, // # # + 0x50, // # # + 0x70, // ### + 0x50, // # # + 0x40, // # + 0x44, // # # + 0xfc, //###### + 0x00, // + 0x00, // + 0x00, // + + // @456 'F' (7 pixels wide) + 0x00, // + 0x7e, // ###### + 0x22, // # # + 0x28, // # # + 0x38, // ### + 0x28, // # # + 0x20, // # + 0x20, // # + 0x70, // ### + 0x00, // + 0x00, // + 0x00, // + + // @468 'G' (7 pixels wide) + 0x00, // + 0x3c, // #### + 0x44, // # # + 0x40, // # + 0x40, // # + 0x4e, // # ### + 0x44, // # # + 0x44, // # # + 0x38, // ### + 0x00, // + 0x00, // + 0x00, // + + // @480 'H' (7 pixels wide) + 0x00, // + 0xee, //### ### + 0x44, // # # + 0x44, // # # + 0x7c, // ##### + 0x44, // # # + 0x44, // # # + 0x44, // # # + 0xee, //### ### + 0x00, // + 0x00, // + 0x00, // + + // @492 'I' (7 pixels wide) + 0x00, // + 0x7c, // ##### + 0x10, // # + 0x10, // # + 0x10, // # + 0x10, // # + 0x10, // # + 0x10, // # + 0x7c, // ##### + 0x00, // + 0x00, // + 0x00, // + + // @504 'J' (7 pixels wide) + 0x00, // + 0x3c, // #### + 0x08, // # + 0x08, // # + 0x08, // # + 0x48, // # # + 0x48, // # # + 0x48, // # # + 0x30, // ## + 0x00, // + 0x00, // + 0x00, // + + // @516 'K' (7 pixels wide) + 0x00, // + 0xee, //### ### + 0x44, // # # + 0x48, // # # + 0x50, // # # + 0x70, // ### + 0x48, // # # + 0x44, // # # + 0xe6, //### ## + 0x00, // + 0x00, // + 0x00, // + + // @528 'L' (7 pixels wide) + 0x00, // + 0x70, // ### + 0x20, // # + 0x20, // # + 0x20, // # + 0x20, // # + 0x24, // # # + 0x24, // # # + 0x7c, // ##### + 0x00, // + 0x00, // + 0x00, // + + // @540 'M' (7 pixels wide) + 0x00, // + 0xee, //### ### + 0x6c, // ## ## + 0x6c, // ## ## + 0x54, // # # # + 0x54, // # # # + 0x44, // # # + 0x44, // # # + 0xee, //### ### + 0x00, // + 0x00, // + 0x00, // + + // @552 'N' (7 pixels wide) + 0x00, // + 0xee, //### ### + 0x64, // ## # + 0x64, // ## # + 0x54, // # # # + 0x54, // # # # + 0x54, // # # # + 0x4c, // # ## + 0xec, //### ## + 0x00, // + 0x00, // + 0x00, // + + // @564 'O' (7 pixels wide) + 0x00, // + 0x38, // ### + 0x44, // # # + 0x44, // # # + 0x44, // # # + 0x44, // # # + 0x44, // # # + 0x44, // # # + 0x38, // ### + 0x00, // + 0x00, // + 0x00, // + + // @576 'P' (7 pixels wide) + 0x00, // + 0x78, // #### + 0x24, // # # + 0x24, // # # + 0x24, // # # + 0x38, // ### + 0x20, // # + 0x20, // # + 0x70, // ### + 0x00, // + 0x00, // + 0x00, // + + // @588 'Q' (7 pixels wide) + 0x00, // + 0x38, // ### + 0x44, // # # + 0x44, // # # + 0x44, // # # + 0x44, // # # + 0x44, // # # + 0x44, // # # + 0x38, // ### + 0x1c, // ### + 0x00, // + 0x00, // + + // @600 'R' (7 pixels wide) + 0x00, // + 0xf8, //##### + 0x44, // # # + 0x44, // # # + 0x44, // # # + 0x78, // #### + 0x48, // # # + 0x44, // # # + 0xe2, //### # + 0x00, // + 0x00, // + 0x00, // + + // @612 'S' (7 pixels wide) + 0x00, // + 0x34, // ## # + 0x4c, // # ## + 0x40, // # + 0x38, // ### + 0x04, // # + 0x04, // # + 0x64, // ## # + 0x58, // # ## + 0x00, // + 0x00, // + 0x00, // + + // @624 'T' (7 pixels wide) + 0x00, // + 0xfe, //####### + 0x92, //# # # + 0x10, // # + 0x10, // # + 0x10, // # + 0x10, // # + 0x10, // # + 0x38, // ### + 0x00, // + 0x00, // + 0x00, // + + // @636 'U' (7 pixels wide) + 0x00, // + 0xee, //### ### + 0x44, // # # + 0x44, // # # + 0x44, // # # + 0x44, // # # + 0x44, // # # + 0x44, // # # + 0x38, // ### + 0x00, // + 0x00, // + 0x00, // + + // @648 'V' (7 pixels wide) + 0x00, // + 0xee, //### ### + 0x44, // # # + 0x44, // # # + 0x28, // # # + 0x28, // # # + 0x28, // # # + 0x10, // # + 0x10, // # + 0x00, // + 0x00, // + 0x00, // + + // @660 'W' (7 pixels wide) + 0x00, // + 0xee, //### ### + 0x44, // # # + 0x44, // # # + 0x54, // # # # + 0x54, // # # # + 0x54, // # # # + 0x54, // # # # + 0x28, // # # + 0x00, // + 0x00, // + 0x00, // + + // @672 'X' (7 pixels wide) + 0x00, // + 0xc6, //## ## + 0x44, // # # + 0x28, // # # + 0x10, // # + 0x10, // # + 0x28, // # # + 0x44, // # # + 0xc6, //## ## + 0x00, // + 0x00, // + 0x00, // + + // @684 'Y' (7 pixels wide) + 0x00, // + 0xee, //### ### + 0x44, // # # + 0x28, // # # + 0x28, // # # + 0x10, // # + 0x10, // # + 0x10, // # + 0x38, // ### + 0x00, // + 0x00, // + 0x00, // + + // @696 'Z' (7 pixels wide) + 0x00, // + 0x7c, // ##### + 0x44, // # # + 0x08, // # + 0x10, // # + 0x10, // # + 0x20, // # + 0x44, // # # + 0x7c, // ##### + 0x00, // + 0x00, // + 0x00, // + + // @708 '[' (7 pixels wide) + 0x00, // + 0x38, // ### + 0x20, // # + 0x20, // # + 0x20, // # + 0x20, // # + 0x20, // # + 0x20, // # + 0x20, // # + 0x20, // # + 0x38, // ### + 0x00, // + + // @720 '\' (7 pixels wide) + 0x00, // + 0x40, // # + 0x20, // # + 0x20, // # + 0x20, // # + 0x10, // # + 0x10, // # + 0x08, // # + 0x08, // # + 0x08, // # + 0x00, // + 0x00, // + + // @732 ']' (7 pixels wide) + 0x00, // + 0x38, // ### + 0x08, // # + 0x08, // # + 0x08, // # + 0x08, // # + 0x08, // # + 0x08, // # + 0x08, // # + 0x08, // # + 0x38, // ### + 0x00, // + + // @744 '^' (7 pixels wide) + 0x00, // + 0x10, // # + 0x10, // # + 0x28, // # # + 0x44, // # # + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + + // @756 '_' (7 pixels wide) + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0xfe, //####### + + // @768 '`' (7 pixels wide) + 0x00, // + 0x10, // # + 0x08, // # + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + + // @780 'a' (7 pixels wide) + 0x00, // + 0x00, // + 0x00, // + 0x38, // ### + 0x44, // # # + 0x3c, // #### + 0x44, // # # + 0x44, // # # + 0x3e, // ##### + 0x00, // + 0x00, // + 0x00, // + + // @792 'b' (7 pixels wide) + 0x00, // + 0xc0, //## + 0x40, // # + 0x58, // # ## + 0x64, // ## # + 0x44, // # # + 0x44, // # # + 0x44, // # # + 0xf8, //##### + 0x00, // + 0x00, // + 0x00, // + + // @804 'c' (7 pixels wide) + 0x00, // + 0x00, // + 0x00, // + 0x3c, // #### + 0x44, // # # + 0x40, // # + 0x40, // # + 0x44, // # # + 0x38, // ### + 0x00, // + 0x00, // + 0x00, // + + // @816 'd' (7 pixels wide) + 0x00, // + 0x0c, // ## + 0x04, // # + 0x34, // ## # + 0x4c, // # ## + 0x44, // # # + 0x44, // # # + 0x44, // # # + 0x3e, // ##### + 0x00, // + 0x00, // + 0x00, // + + // @828 'e' (7 pixels wide) + 0x00, // + 0x00, // + 0x00, // + 0x38, // ### + 0x44, // # # + 0x7c, // ##### + 0x40, // # + 0x40, // # + 0x3c, // #### + 0x00, // + 0x00, // + 0x00, // + + // @840 'f' (7 pixels wide) + 0x00, // + 0x1c, // ### + 0x20, // # + 0x7c, // ##### + 0x20, // # + 0x20, // # + 0x20, // # + 0x20, // # + 0x7c, // ##### + 0x00, // + 0x00, // + 0x00, // + + // @852 'g' (7 pixels wide) + 0x00, // + 0x00, // + 0x00, // + 0x36, // ## ## + 0x4c, // # ## + 0x44, // # # + 0x44, // # # + 0x44, // # # + 0x3c, // #### + 0x04, // # + 0x38, // ### + 0x00, // + + // @864 'h' (7 pixels wide) + 0x00, // + 0xc0, //## + 0x40, // # + 0x58, // # ## + 0x64, // ## # + 0x44, // # # + 0x44, // # # + 0x44, // # # + 0xee, //### ### + 0x00, // + 0x00, // + 0x00, // + + // @876 'i' (7 pixels wide) + 0x00, // + 0x10, // # + 0x00, // + 0x70, // ### + 0x10, // # + 0x10, // # + 0x10, // # + 0x10, // # + 0x7c, // ##### + 0x00, // + 0x00, // + 0x00, // + + // @888 'j' (7 pixels wide) + 0x00, // + 0x10, // # + 0x00, // + 0x78, // #### + 0x08, // # + 0x08, // # + 0x08, // # + 0x08, // # + 0x08, // # + 0x08, // # + 0x70, // ### + 0x00, // + + // @900 'k' (7 pixels wide) + 0x00, // + 0xc0, //## + 0x40, // # + 0x5c, // # ### + 0x48, // # # + 0x70, // ### + 0x50, // # # + 0x48, // # # + 0xdc, //## ### + 0x00, // + 0x00, // + 0x00, // + + // @912 'l' (7 pixels wide) + 0x00, // + 0x30, // ## + 0x10, // # + 0x10, // # + 0x10, // # + 0x10, // # + 0x10, // # + 0x10, // # + 0x7c, // ##### + 0x00, // + 0x00, // + 0x00, // + + // @924 'm' (7 pixels wide) + 0x00, // + 0x00, // + 0x00, // + 0xe8, //### # + 0x54, // # # # + 0x54, // # # # + 0x54, // # # # + 0x54, // # # # + 0xfe, //####### + 0x00, // + 0x00, // + 0x00, // + + // @936 'n' (7 pixels wide) + 0x00, // + 0x00, // + 0x00, // + 0xd8, //## ## + 0x64, // ## # + 0x44, // # # + 0x44, // # # + 0x44, // # # + 0xee, //### ### + 0x00, // + 0x00, // + 0x00, // + + // @948 'o' (7 pixels wide) + 0x00, // + 0x00, // + 0x00, // + 0x38, // ### + 0x44, // # # + 0x44, // # # + 0x44, // # # + 0x44, // # # + 0x38, // ### + 0x00, // + 0x00, // + 0x00, // + + // @960 'p' (7 pixels wide) + 0x00, // + 0x00, // + 0x00, // + 0xd8, //## ## + 0x64, // ## # + 0x44, // # # + 0x44, // # # + 0x44, // # # + 0x78, // #### + 0x40, // # + 0xe0, //### + 0x00, // + + // @972 'q' (7 pixels wide) + 0x00, // + 0x00, // + 0x00, // + 0x36, // ## ## + 0x4c, // # ## + 0x44, // # # + 0x44, // # # + 0x44, // # # + 0x3c, // #### + 0x04, // # + 0x0e, // ### + 0x00, // + + // @984 'r' (7 pixels wide) + 0x00, // + 0x00, // + 0x00, // + 0x6c, // ## ## + 0x30, // ## + 0x20, // # + 0x20, // # + 0x20, // # + 0x7c, // ##### + 0x00, // + 0x00, // + 0x00, // + + // @996 's' (7 pixels wide) + 0x00, // + 0x00, // + 0x00, // + 0x3c, // #### + 0x44, // # # + 0x38, // ### + 0x04, // # + 0x44, // # # + 0x78, // #### + 0x00, // + 0x00, // + 0x00, // + + // @1008 't' (7 pixels wide) + 0x00, // + 0x00, // + 0x20, // # + 0x7c, // ##### + 0x20, // # + 0x20, // # + 0x20, // # + 0x22, // # # + 0x1c, // ### + 0x00, // + 0x00, // + 0x00, // + + // @1020 'u' (7 pixels wide) + 0x00, // + 0x00, // + 0x00, // + 0xcc, //## ## + 0x44, // # # + 0x44, // # # + 0x44, // # # + 0x4c, // # ## + 0x36, // ## ## + 0x00, // + 0x00, // + 0x00, // + + // @1032 'v' (7 pixels wide) + 0x00, // + 0x00, // + 0x00, // + 0xee, //### ### + 0x44, // # # + 0x44, // # # + 0x28, // # # + 0x28, // # # + 0x10, // # + 0x00, // + 0x00, // + 0x00, // + + // @1044 'w' (7 pixels wide) + 0x00, // + 0x00, // + 0x00, // + 0xee, //### ### + 0x44, // # # + 0x54, // # # # + 0x54, // # # # + 0x54, // # # # + 0x28, // # # + 0x00, // + 0x00, // + 0x00, // + + // @1056 'x' (7 pixels wide) + 0x00, // + 0x00, // + 0x00, // + 0xcc, //## ## + 0x48, // # # + 0x30, // ## + 0x30, // ## + 0x48, // # # + 0xcc, //## ## + 0x00, // + 0x00, // + 0x00, // + + // @1068 'y' (7 pixels wide) + 0x00, // + 0x00, // + 0x00, // + 0xee, //### ### + 0x44, // # # + 0x24, // # # + 0x28, // # # + 0x18, // ## + 0x10, // # + 0x10, // # + 0x78, // #### + 0x00, // + + // @1080 'z' (7 pixels wide) + 0x00, // + 0x00, // + 0x00, // + 0x7c, // ##### + 0x48, // # # + 0x10, // # + 0x20, // # + 0x44, // # # + 0x7c, // ##### + 0x00, // + 0x00, // + 0x00, // + + // @1092 '{' (7 pixels wide) + 0x00, // + 0x08, // # + 0x10, // # + 0x10, // # + 0x10, // # + 0x10, // # + 0x20, // # + 0x10, // # + 0x10, // # + 0x10, // # + 0x08, // # + 0x00, // + + // @1104 '|' (7 pixels wide) + 0x00, // + 0x10, // # + 0x10, // # + 0x10, // # + 0x10, // # + 0x10, // # + 0x10, // # + 0x10, // # + 0x10, // # + 0x10, // # + 0x00, // + 0x00, // + + // @1116 '}' (7 pixels wide) + 0x00, // + 0x20, // # + 0x10, // # + 0x10, // # + 0x10, // # + 0x10, // # + 0x08, // # + 0x10, // # + 0x10, // # + 0x10, // # + 0x20, // # + 0x00, // + + // @1128 '~' (7 pixels wide) + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x24, // # # + 0x58, // # ## + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + + // @0 ' ' (7 pixels wide) + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + + // @0 ' ' (7 pixels wide) + 0x00, // + 0x28, // # # + 0x00, // + 0x38, // ### + 0x44, // # # + 0x3c, // #### + 0x44, // # # + 0x44, // # # + 0x3e, // ##### + 0x00, // + 0x00, // + 0x00, // + + // @0 ' ' (7 pixels wide) + 0x44, // # # + 0x30, // ## + 0x10, // # + 0x28, // # # + 0x28, // # # + 0x28, // # # + 0x7c, // ##### + 0x44, // # # + 0xee, //### ### + 0x00, // + 0x00, // + 0x00, // + + // @0 ' ' (7 pixels wide) + 0x00, // + 0x28, // # # + 0x00, // + 0x38, // ### + 0x44, // # # + 0x44, // # # + 0x44, // # # + 0x44, // # # + 0x38, // ### + 0x00, // + 0x00, // + 0x00, // + + // @0 ' ' (7 pixels wide) + 0x44, // # # + 0x38, // ### + 0x44, // # # + 0x44, // # # + 0x44, // # # + 0x44, // # # + 0x44, // # # + 0x44, // # # + 0x38, // ### + 0x00, // + 0x00, // + 0x00, // + + // @0 ' ' (7 pixels wide) + 0x00, // + 0x44, // # # + 0x00, // + 0xcc, //## ## + 0x44, // # # + 0x44, // # # + 0x44, // # # + 0x4c, // # ## + 0x36, // ## ## + 0x00, // + 0x00, // + 0x00, // + + // @0 ' ' (7 pixels wide) + 0x44, // # # + 0x00, // + 0xee, //### ### + 0x44, // # # + 0x44, // # # + 0x44, // # # + 0x44, // # # + 0x44, // # # + 0x44, // # # + 0x38, // ### + 0x00, // + 0x00, // + + // @0 ' ' (7 pixels wide) + 0x00, // + 0x70, // ### + 0x48, // # # + 0x48, // # # + 0x50, // # # + 0x50, // # # + 0x58, // # ## + 0x4c, // # ## + 0x4c, // # ## + 0x78, // #### + 0x40, // # + 0x40 // # + +}; + +sFONT Font12 = { + Font12_Table, + 7, /* Width */ + 12, /* Height */ +}; + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ + + diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/font16.c b/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/font16.c new file mode 100644 index 000000000..e2bab9130 --- /dev/null +++ b/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/font16.c @@ -0,0 +1,1909 @@ +/** + ****************************************************************************** + * @file font16.c + * @author MCD Application Team + * @version V1.0.0 + * @date 18-February-2014 + * @brief This file provides text font16 for STM32xx-EVAL's LCD driver. + ****************************************************************************** + * @attention + * + *

© COPYRIGHT(c) 2014 STMicroelectronics

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of STMicroelectronics nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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. + * + ****************************************************************************** + */ + +/* Includes ------------------------------------------------------------------*/ +#include "fonts.h" +#include + +// +// Font data for Courier New 12pt +// + +const uint8_t Font16_Table[] PROGMEM = +{ + // @0 ' ' (11 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @32 '!' (11 pixels wide) + 0x00,0x00, // + 0x0c,0x00, // ## + 0x0c,0x00, // ## + 0x0c,0x00, // ## + 0x0c,0x00, // ## + 0x0c,0x00, // ## + 0x0c,0x00, // ## + 0x0c,0x00, // ## + 0x0c,0x00, // ## + 0x00,0x00, // + 0x0c,0x00, // ## + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @64 '"' (11 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x1d,0xc0, // ### ### + 0x1d,0xc0, // ### ### + 0x08,0x80, // # # + 0x08,0x80, // # # + 0x08,0x80, // # # + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @96 '#' (11 pixels wide) + 0x00,0x00, // + 0x0d,0x80, // ## ## + 0x0d,0x80, // ## ## + 0x0d,0x80, // ## ## + 0x0d,0x80, // ## ## + 0x3f,0xc0, // ######## + 0x1b,0x00, // ## ## + 0x3f,0xc0, // ######## + 0x1b,0x00, // ## ## + 0x1b,0x00, // ## ## + 0x1b,0x00, // ## ## + 0x1b,0x00, // ## ## + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @128 '$' (11 pixels wide) + 0x04,0x00, // # + 0x1f,0x80, // ###### + 0x31,0x80, // ## ## + 0x31,0x80, // ## ## + 0x38,0x00, // ### + 0x1e,0x00, // #### + 0x0f,0x00, // #### + 0x03,0x80, // ### + 0x31,0x80, // ## ## + 0x31,0x80, // ## ## + 0x3f,0x00, // ###### + 0x04,0x00, // # + 0x04,0x00, // # + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @160 '%' (11 pixels wide) + 0x00,0x00, // + 0x18,0x00, // ## + 0x24,0x00, // # # + 0x24,0x00, // # # + 0x18,0xc0, // ## ## + 0x07,0x80, // #### + 0x1e,0x00, // #### + 0x31,0x80, // ## ## + 0x02,0x40, // # # + 0x02,0x40, // # # + 0x01,0x80, // ## + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @192 '&' (11 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x0f,0x00, // #### + 0x18,0x00, // ## + 0x18,0x00, // ## + 0x18,0x00, // ## + 0x0c,0x00, // ## + 0x1d,0x80, // ### ## + 0x37,0x00, // ## ### + 0x33,0x00, // ## ## + 0x1d,0x80, // ### ## + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @224 ''' (11 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x07,0x00, // ### + 0x07,0x00, // ### + 0x02,0x00, // # + 0x02,0x00, // # + 0x02,0x00, // # + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @256 '(' (11 pixels wide) + 0x00,0x00, // + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x06,0x00, // ## + 0x0e,0x00, // ### + 0x0c,0x00, // ## + 0x0c,0x00, // ## + 0x0c,0x00, // ## + 0x0c,0x00, // ## + 0x0e,0x00, // ### + 0x06,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @288 ')' (11 pixels wide) + 0x00,0x00, // + 0x18,0x00, // ## + 0x18,0x00, // ## + 0x0c,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x0c,0x00, // ## + 0x1c,0x00, // ### + 0x18,0x00, // ## + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @320 '*' (11 pixels wide) + 0x00,0x00, // + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x3f,0xc0, // ######## + 0x3f,0xc0, // ######## + 0x0f,0x00, // #### + 0x1f,0x80, // ###### + 0x19,0x80, // ## ## + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @352 '+' (11 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x04,0x00, // # + 0x04,0x00, // # + 0x04,0x00, // # + 0x3f,0x80, // ####### + 0x04,0x00, // # + 0x04,0x00, // # + 0x04,0x00, // # + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @384 ',' (11 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x06,0x00, // ## + 0x04,0x00, // # + 0x0c,0x00, // ## + 0x08,0x00, // # + 0x08,0x00, // # + 0x00,0x00, // + 0x00,0x00, // + + // @416 '-' (11 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x3f,0x80, // ####### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @448 '.' (11 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x0c,0x00, // ## + 0x0c,0x00, // ## + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @480 '/' (11 pixels wide) + 0x00,0xc0, // ## + 0x00,0xc0, // ## + 0x01,0x80, // ## + 0x01,0x80, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x06,0x00, // ## + 0x0c,0x00, // ## + 0x0c,0x00, // ## + 0x18,0x00, // ## + 0x18,0x00, // ## + 0x30,0x00, // ## + 0x30,0x00, // ## + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @512 '0' (11 pixels wide) + 0x00,0x00, // + 0x0e,0x00, // ### + 0x1b,0x00, // ## ## + 0x31,0x80, // ## ## + 0x31,0x80, // ## ## + 0x31,0x80, // ## ## + 0x31,0x80, // ## ## + 0x31,0x80, // ## ## + 0x31,0x80, // ## ## + 0x1b,0x00, // ## ## + 0x0e,0x00, // ### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @544 '1' (11 pixels wide) + 0x00,0x00, // + 0x06,0x00, // ## + 0x3e,0x00, // ##### + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x3f,0xc0, // ######## + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @576 '2' (11 pixels wide) + 0x00,0x00, // + 0x0f,0x00, // #### + 0x19,0x80, // ## ## + 0x31,0x80, // ## ## + 0x31,0x80, // ## ## + 0x03,0x00, // ## + 0x06,0x00, // ## + 0x0c,0x00, // ## + 0x18,0x00, // ## + 0x30,0x00, // ## + 0x3f,0x80, // ####### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @608 '3' (11 pixels wide) + 0x00,0x00, // + 0x3f,0x00, // ###### + 0x61,0x80, // ## ## + 0x01,0x80, // ## + 0x03,0x00, // ## + 0x1f,0x00, // ##### + 0x03,0x80, // ### + 0x01,0x80, // ## + 0x01,0x80, // ## + 0x61,0x80, // ## ## + 0x3f,0x00, // ###### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @640 '4' (11 pixels wide) + 0x00,0x00, // + 0x07,0x00, // ### + 0x07,0x00, // ### + 0x0f,0x00, // #### + 0x0b,0x00, // # ## + 0x1b,0x00, // ## ## + 0x13,0x00, // # ## + 0x33,0x00, // ## ## + 0x3f,0x80, // ####### + 0x03,0x00, // ## + 0x0f,0x80, // ##### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @672 '5' (11 pixels wide) + 0x00,0x00, // + 0x1f,0x80, // ###### + 0x18,0x00, // ## + 0x18,0x00, // ## + 0x18,0x00, // ## + 0x1f,0x00, // ##### + 0x11,0x80, // # ## + 0x01,0x80, // ## + 0x01,0x80, // ## + 0x21,0x80, // # ## + 0x1f,0x00, // ##### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @704 '6' (11 pixels wide) + 0x00,0x00, // + 0x07,0x80, // #### + 0x1c,0x00, // ### + 0x18,0x00, // ## + 0x30,0x00, // ## + 0x37,0x00, // ## ### + 0x39,0x80, // ### ## + 0x31,0x80, // ## ## + 0x31,0x80, // ## ## + 0x19,0x80, // ## ## + 0x0f,0x00, // #### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @736 '7' (11 pixels wide) + 0x00,0x00, // + 0x7f,0x00, // ####### + 0x43,0x00, // # ## + 0x03,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x0c,0x00, // ## + 0x0c,0x00, // ## + 0x0c,0x00, // ## + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @768 '8' (11 pixels wide) + 0x00,0x00, // + 0x1f,0x00, // ##### + 0x31,0x80, // ## ## + 0x31,0x80, // ## ## + 0x31,0x80, // ## ## + 0x1f,0x00, // ##### + 0x31,0x80, // ## ## + 0x31,0x80, // ## ## + 0x31,0x80, // ## ## + 0x31,0x80, // ## ## + 0x1f,0x00, // ##### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @800 '9' (11 pixels wide) + 0x00,0x00, // + 0x1e,0x00, // #### + 0x33,0x00, // ## ## + 0x31,0x80, // ## ## + 0x31,0x80, // ## ## + 0x33,0x80, // ## ### + 0x1d,0x80, // ### ## + 0x01,0x80, // ## + 0x03,0x00, // ## + 0x07,0x00, // ### + 0x3c,0x00, // #### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @832 ':' (11 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x0c,0x00, // ## + 0x0c,0x00, // ## + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x0c,0x00, // ## + 0x0c,0x00, // ## + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @864 ';' (11 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x06,0x00, // ## + 0x04,0x00, // # + 0x08,0x00, // # + 0x08,0x00, // # + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @896 '<' (11 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x00,0xc0, // ## + 0x03,0x00, // ## + 0x04,0x00, // # + 0x18,0x00, // ## + 0x60,0x00, // ## + 0x18,0x00, // ## + 0x04,0x00, // # + 0x03,0x00, // ## + 0x00,0xc0, // ## + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @928 '=' (11 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x7f,0xc0, // ######### + 0x00,0x00, // + 0x7f,0xc0, // ######### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @960 '>' (11 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x60,0x00, // ## + 0x18,0x00, // ## + 0x04,0x00, // # + 0x03,0x00, // ## + 0x00,0xc0, // ## + 0x03,0x00, // ## + 0x04,0x00, // # + 0x18,0x00, // ## + 0x60,0x00, // ## + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @992 '?' (11 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x1f,0x00, // ##### + 0x31,0x80, // ## ## + 0x31,0x80, // ## ## + 0x01,0x80, // ## + 0x07,0x00, // ### + 0x0c,0x00, // ## + 0x0c,0x00, // ## + 0x00,0x00, // + 0x0c,0x00, // ## + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @1024 '@' (11 pixels wide) + 0x00,0x00, // + 0x0e,0x00, // ### + 0x11,0x00, // # # + 0x21,0x00, // # # + 0x21,0x00, // # # + 0x27,0x00, // # ### + 0x29,0x00, // # # # + 0x29,0x00, // # # # + 0x27,0x00, // # ### + 0x20,0x00, // # + 0x11,0x00, // # # + 0x0e,0x00, // ### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @1056 'A' (11 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x3f,0x00, // ###### + 0x0f,0x00, // #### + 0x09,0x00, // # # + 0x19,0x80, // ## ## + 0x19,0x80, // ## ## + 0x1f,0x80, // ###### + 0x30,0xc0, // ## ## + 0x30,0xc0, // ## ## + 0x79,0xe0, // #### #### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @1088 'B' (11 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x7f,0x00, // ####### + 0x31,0x80, // ## ## + 0x31,0x80, // ## ## + 0x31,0x80, // ## ## + 0x3f,0x00, // ###### + 0x31,0x80, // ## ## + 0x31,0x80, // ## ## + 0x31,0x80, // ## ## + 0x7f,0x00, // ####### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @1120 'C' (11 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x1f,0x40, // ##### # + 0x30,0xc0, // ## ## + 0x60,0x40, // ## # + 0x60,0x00, // ## + 0x60,0x00, // ## + 0x60,0x00, // ## + 0x60,0x40, // ## # + 0x30,0x80, // ## # + 0x1f,0x00, // ##### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @1152 'D' (11 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x7f,0x00, // ####### + 0x31,0x80, // ## ## + 0x30,0xc0, // ## ## + 0x30,0xc0, // ## ## + 0x30,0xc0, // ## ## + 0x30,0xc0, // ## ## + 0x30,0xc0, // ## ## + 0x31,0x80, // ## ## + 0x7f,0x00, // ####### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @1184 'E' (11 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x7f,0x80, // ######## + 0x30,0x80, // ## # + 0x30,0x80, // ## # + 0x32,0x00, // ## # + 0x3e,0x00, // ##### + 0x32,0x00, // ## # + 0x30,0x80, // ## # + 0x30,0x80, // ## # + 0x7f,0x80, // ######## + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @1216 'F' (11 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x7f,0xc0, // ######### + 0x30,0x40, // ## # + 0x30,0x40, // ## # + 0x32,0x00, // ## # + 0x3e,0x00, // ##### + 0x32,0x00, // ## # + 0x30,0x00, // ## + 0x30,0x00, // ## + 0x7c,0x00, // ##### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @1248 'G' (11 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x1e,0x80, // #### # + 0x31,0x80, // ## ## + 0x60,0x80, // ## # + 0x60,0x00, // ## + 0x60,0x00, // ## + 0x67,0xc0, // ## ##### + 0x61,0x80, // ## ## + 0x31,0x80, // ## ## + 0x1f,0x00, // ##### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @1280 'H' (11 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x7b,0xc0, // #### #### + 0x31,0x80, // ## ## + 0x31,0x80, // ## ## + 0x31,0x80, // ## ## + 0x3f,0x80, // ####### + 0x31,0x80, // ## ## + 0x31,0x80, // ## ## + 0x31,0x80, // ## ## + 0x7b,0xc0, // #### #### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @1312 'I' (11 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x3f,0xc0, // ######## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x3f,0xc0, // ######## + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @1344 'J' (11 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x1f,0xc0, // ####### + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x63,0x00, // ## ## + 0x63,0x00, // ## ## + 0x63,0x00, // ## ## + 0x3e,0x00, // ##### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @1376 'K' (11 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x7b,0xc0, // #### #### + 0x31,0x80, // ## ## + 0x33,0x00, // ## ## + 0x36,0x00, // ## ## + 0x3c,0x00, // #### + 0x3e,0x00, // ##### + 0x33,0x00, // ## ## + 0x31,0x80, // ## ## + 0x79,0xc0, // #### ### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @1408 'L' (11 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x7e,0x00, // ###### + 0x18,0x00, // ## + 0x18,0x00, // ## + 0x18,0x00, // ## + 0x18,0x00, // ## + 0x18,0x40, // ## # + 0x18,0x40, // ## # + 0x18,0x40, // ## # + 0x7f,0xc0, // ######### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @1440 'M' (11 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0xe0,0xe0, //### ### + 0x60,0xc0, // ## ## + 0x71,0xc0, // ### ### + 0x7b,0xc0, // #### #### + 0x6a,0xc0, // ## # # ## + 0x6e,0xc0, // ## ### ## + 0x64,0xc0, // ## # ## + 0x60,0xc0, // ## ## + 0xfb,0xe0, //##### ##### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @1472 'N' (11 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x73,0xc0, // ### #### + 0x31,0x80, // ## ## + 0x39,0x80, // ### ## + 0x3d,0x80, // #### ## + 0x35,0x80, // ## # ## + 0x37,0x80, // ## #### + 0x33,0x80, // ## ### + 0x31,0x80, // ## ## + 0x79,0x80, // #### ## + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @1504 'O' (11 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x1f,0x00, // ##### + 0x31,0x80, // ## ## + 0x60,0xc0, // ## ## + 0x60,0xc0, // ## ## + 0x60,0xc0, // ## ## + 0x60,0xc0, // ## ## + 0x60,0xc0, // ## ## + 0x31,0x80, // ## ## + 0x1f,0x00, // ##### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @1536 'P' (11 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x7f,0x00, // ####### + 0x31,0x80, // ## ## + 0x31,0x80, // ## ## + 0x31,0x80, // ## ## + 0x31,0x80, // ## ## + 0x3f,0x00, // ###### + 0x30,0x00, // ## + 0x30,0x00, // ## + 0x7e,0x00, // ###### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @1568 'Q' (11 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x1f,0x00, // ##### + 0x31,0x80, // ## ## + 0x60,0xc0, // ## ## + 0x60,0xc0, // ## ## + 0x60,0xc0, // ## ## + 0x60,0xc0, // ## ## + 0x60,0xc0, // ## ## + 0x31,0x80, // ## ## + 0x1f,0x00, // ##### + 0x0c,0xc0, // ## ## + 0x1f,0x80, // ###### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @1600 'R' (11 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x7f,0x00, // ####### + 0x31,0x80, // ## ## + 0x31,0x80, // ## ## + 0x31,0x80, // ## ## + 0x3e,0x00, // ##### + 0x33,0x00, // ## ## + 0x31,0x80, // ## ## + 0x31,0x80, // ## ## + 0x7c,0xe0, // ##### ### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @1632 'S' (11 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x1f,0x80, // ###### + 0x31,0x80, // ## ## + 0x31,0x80, // ## ## + 0x38,0x00, // ### + 0x1f,0x00, // ##### + 0x03,0x80, // ### + 0x31,0x80, // ## ## + 0x31,0x80, // ## ## + 0x3f,0x00, // ###### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @1664 'T' (11 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x7f,0x80, // ######## + 0x4c,0x80, // # ## # + 0x4c,0x80, // # ## # + 0x4c,0x80, // # ## # + 0x0c,0x00, // ## + 0x0c,0x00, // ## + 0x0c,0x00, // ## + 0x0c,0x00, // ## + 0x3f,0x00, // ###### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @1696 'U' (11 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x7b,0xc0, // #### #### + 0x31,0x80, // ## ## + 0x31,0x80, // ## ## + 0x31,0x80, // ## ## + 0x31,0x80, // ## ## + 0x31,0x80, // ## ## + 0x31,0x80, // ## ## + 0x31,0x80, // ## ## + 0x1f,0x00, // ##### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @1728 'V' (11 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x7b,0xc0, // #### #### + 0x31,0x80, // ## ## + 0x31,0x80, // ## ## + 0x1b,0x00, // ## ## + 0x1b,0x00, // ## ## + 0x1b,0x00, // ## ## + 0x0a,0x00, // # # + 0x0e,0x00, // ### + 0x0e,0x00, // ### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @1760 'W' (11 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0xfb,0xe0, //##### ##### + 0x60,0xc0, // ## ## + 0x64,0xc0, // ## # ## + 0x6e,0xc0, // ## ### ## + 0x6e,0xc0, // ## ### ## + 0x2a,0x80, // # # # # + 0x3b,0x80, // ### ### + 0x3b,0x80, // ### ### + 0x31,0x80, // ## ## + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @1792 'X' (11 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x7b,0xc0, // #### #### + 0x31,0x80, // ## ## + 0x1b,0x00, // ## ## + 0x0e,0x00, // ### + 0x0e,0x00, // ### + 0x0e,0x00, // ### + 0x1b,0x00, // ## ## + 0x31,0x80, // ## ## + 0x7b,0xc0, // #### #### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @1824 'Y' (11 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x79,0xe0, // #### #### + 0x30,0xc0, // ## ## + 0x19,0x80, // ## ## + 0x0f,0x00, // #### + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x1f,0x80, // ###### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @1856 'Z' (11 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x3f,0x80, // ####### + 0x21,0x80, // # ## + 0x23,0x00, // # ## + 0x06,0x00, // ## + 0x04,0x00, // # + 0x0c,0x00, // ## + 0x18,0x80, // ## # + 0x30,0x80, // ## # + 0x3f,0x80, // ####### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @1888 '[' (11 pixels wide) + 0x00,0x00, // + 0x07,0x80, // #### + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x07,0x80, // #### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @1920 '\' (11 pixels wide) + 0x30,0x00, // ## + 0x30,0x00, // ## + 0x18,0x00, // ## + 0x18,0x00, // ## + 0x0c,0x00, // ## + 0x0c,0x00, // ## + 0x06,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x01,0x80, // ## + 0x01,0x80, // ## + 0x00,0xc0, // ## + 0x00,0xc0, // ## + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @1952 ']' (11 pixels wide) + 0x00,0x00, // + 0x1e,0x00, // #### + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x1e,0x00, // #### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @1984 '^' (11 pixels wide) + 0x04,0x00, // # + 0x0a,0x00, // # # + 0x0a,0x00, // # # + 0x11,0x00, // # # + 0x20,0x80, // # # + 0x20,0x80, // # # + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @2016 '_' (11 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0xff,0xe0, //########### + + // @2048 '`' (11 pixels wide) + 0x08,0x00, // # + 0x04,0x00, // # + 0x02,0x00, // # + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @2080 'a' (11 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x1f,0x00, // ##### + 0x01,0x80, // ## + 0x01,0x80, // ## + 0x1f,0x80, // ###### + 0x31,0x80, // ## ## + 0x33,0x80, // ## ### + 0x1d,0xc0, // ### ### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @2112 'b' (11 pixels wide) + 0x00,0x00, // + 0x70,0x00, // ### + 0x30,0x00, // ## + 0x30,0x00, // ## + 0x37,0x00, // ## ### + 0x39,0x80, // ### ## + 0x30,0xc0, // ## ## + 0x30,0xc0, // ## ## + 0x30,0xc0, // ## ## + 0x39,0x80, // ### ## + 0x77,0x00, // ### ### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @2144 'c' (11 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x1e,0x80, // #### # + 0x31,0x80, // ## ## + 0x60,0x80, // ## # + 0x60,0x00, // ## + 0x60,0x80, // ## # + 0x31,0x80, // ## ## + 0x1f,0x00, // ##### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @2176 'd' (11 pixels wide) + 0x00,0x00, // + 0x03,0x80, // ### + 0x01,0x80, // ## + 0x01,0x80, // ## + 0x1d,0x80, // ### ## + 0x33,0x80, // ## ### + 0x61,0x80, // ## ## + 0x61,0x80, // ## ## + 0x61,0x80, // ## ## + 0x33,0x80, // ## ### + 0x1d,0xc0, // ### ### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @2208 'e' (11 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x1f,0x00, // ##### + 0x31,0x80, // ## ## + 0x60,0xc0, // ## ## + 0x7f,0xc0, // ######### + 0x60,0x00, // ## + 0x30,0xc0, // ## ## + 0x1f,0x80, // ###### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @2240 'f' (11 pixels wide) + 0x00,0x00, // + 0x07,0xe0, // ###### + 0x0c,0x00, // ## + 0x0c,0x00, // ## + 0x3f,0x80, // ####### + 0x0c,0x00, // ## + 0x0c,0x00, // ## + 0x0c,0x00, // ## + 0x0c,0x00, // ## + 0x0c,0x00, // ## + 0x3f,0x80, // ####### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @2272 'g' (11 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x1d,0xc0, // ### ### + 0x33,0x80, // ## ### + 0x61,0x80, // ## ## + 0x61,0x80, // ## ## + 0x61,0x80, // ## ## + 0x33,0x80, // ## ### + 0x1d,0x80, // ### ## + 0x01,0x80, // ## + 0x01,0x80, // ## + 0x1f,0x00, // ##### + 0x00,0x00, // + 0x00,0x00, // + + // @2304 'h' (11 pixels wide) + 0x00,0x00, // + 0x70,0x00, // ### + 0x30,0x00, // ## + 0x30,0x00, // ## + 0x37,0x00, // ## ### + 0x39,0x80, // ### ## + 0x31,0x80, // ## ## + 0x31,0x80, // ## ## + 0x31,0x80, // ## ## + 0x31,0x80, // ## ## + 0x7b,0xc0, // #### #### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @2336 'i' (11 pixels wide) + 0x00,0x00, // + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x00,0x00, // + 0x1e,0x00, // #### + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x3f,0xc0, // ######## + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @2368 'j' (11 pixels wide) + 0x00,0x00, // + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x00,0x00, // + 0x3f,0x00, // ###### + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x3e,0x00, // ##### + 0x00,0x00, // + 0x00,0x00, // + + // @2400 'k' (11 pixels wide) + 0x00,0x00, // + 0x70,0x00, // ### + 0x30,0x00, // ## + 0x30,0x00, // ## + 0x37,0x80, // ## #### + 0x36,0x00, // ## ## + 0x3c,0x00, // #### + 0x3c,0x00, // #### + 0x36,0x00, // ## ## + 0x33,0x00, // ## ## + 0x77,0xc0, // ### ##### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @2432 'l' (11 pixels wide) + 0x00,0x00, // + 0x1e,0x00, // #### + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x3f,0xc0, // ######## + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @2464 'm' (11 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x7f,0x80, // ######## + 0x36,0xc0, // ## ## ## + 0x36,0xc0, // ## ## ## + 0x36,0xc0, // ## ## ## + 0x36,0xc0, // ## ## ## + 0x36,0xc0, // ## ## ## + 0x76,0xe0, // ### ## ### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @2496 'n' (11 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x77,0x00, // ### ### + 0x39,0x80, // ### ## + 0x31,0x80, // ## ## + 0x31,0x80, // ## ## + 0x31,0x80, // ## ## + 0x31,0x80, // ## ## + 0x7b,0xc0, // #### #### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @2528 'o' (11 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x1f,0x00, // ##### + 0x31,0x80, // ## ## + 0x60,0xc0, // ## ## + 0x60,0xc0, // ## ## + 0x60,0xc0, // ## ## + 0x31,0x80, // ## ## + 0x1f,0x00, // ##### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @2560 'p' (11 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x77,0x00, // ### ### + 0x39,0x80, // ### ## + 0x30,0xc0, // ## ## + 0x30,0xc0, // ## ## + 0x30,0xc0, // ## ## + 0x39,0x80, // ### ## + 0x37,0x00, // ## ### + 0x30,0x00, // ## + 0x30,0x00, // ## + 0x7c,0x00, // ##### + 0x00,0x00, // + 0x00,0x00, // + + // @2592 'q' (11 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x1d,0xc0, // ### ### + 0x33,0x80, // ## ### + 0x61,0x80, // ## ## + 0x61,0x80, // ## ## + 0x61,0x80, // ## ## + 0x33,0x80, // ## ### + 0x1d,0x80, // ### ## + 0x01,0x80, // ## + 0x01,0x80, // ## + 0x07,0xc0, // ##### + 0x00,0x00, // + 0x00,0x00, // + + // @2624 'r' (11 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x7b,0x80, // #### ### + 0x1c,0xc0, // ### ## + 0x18,0x00, // ## + 0x18,0x00, // ## + 0x18,0x00, // ## + 0x18,0x00, // ## + 0x7f,0x00, // ####### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @2656 's' (11 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x1f,0x80, // ###### + 0x31,0x80, // ## ## + 0x3c,0x00, // #### + 0x1f,0x00, // ##### + 0x03,0x80, // ### + 0x31,0x80, // ## ## + 0x3f,0x00, // ###### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @2688 't' (11 pixels wide) + 0x00,0x00, // + 0x18,0x00, // ## + 0x18,0x00, // ## + 0x18,0x00, // ## + 0x7f,0x00, // ####### + 0x18,0x00, // ## + 0x18,0x00, // ## + 0x18,0x00, // ## + 0x18,0x00, // ## + 0x18,0x80, // ## # + 0x0f,0x00, // #### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @2720 'u' (11 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x73,0x80, // ### ### + 0x31,0x80, // ## ## + 0x31,0x80, // ## ## + 0x31,0x80, // ## ## + 0x31,0x80, // ## ## + 0x33,0x80, // ## ### + 0x1d,0xc0, // ### ### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @2752 'v' (11 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x7b,0xc0, // #### #### + 0x31,0x80, // ## ## + 0x31,0x80, // ## ## + 0x1b,0x00, // ## ## + 0x1b,0x00, // ## ## + 0x0e,0x00, // ### + 0x0e,0x00, // ### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @2784 'w' (11 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0xf1,0xe0, //#### #### + 0x60,0xc0, // ## ## + 0x64,0xc0, // ## # ## + 0x6e,0xc0, // ## ### ## + 0x3b,0x80, // ### ### + 0x3b,0x80, // ### ### + 0x31,0x80, // ## ## + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @2816 'x' (11 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x7b,0xc0, // #### #### + 0x1b,0x00, // ## ## + 0x0e,0x00, // ### + 0x0e,0x00, // ### + 0x0e,0x00, // ### + 0x1b,0x00, // ## ## + 0x7b,0xc0, // #### #### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @2848 'y' (11 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x79,0xe0, // #### #### + 0x30,0xc0, // ## ## + 0x19,0x80, // ## ## + 0x19,0x80, // ## ## + 0x0b,0x00, // # ## + 0x0f,0x00, // #### + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x0c,0x00, // ## + 0x3e,0x00, // ##### + 0x00,0x00, // + 0x00,0x00, // + + // @2880 'z' (11 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x3f,0x80, // ####### + 0x21,0x80, // # ## + 0x03,0x00, // ## + 0x0e,0x00, // ### + 0x18,0x00, // ## + 0x30,0x80, // ## # + 0x3f,0x80, // ####### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @2912 '{' (11 pixels wide) + 0x00,0x00, // + 0x06,0x00, // ## + 0x0c,0x00, // ## + 0x0c,0x00, // ## + 0x0c,0x00, // ## + 0x0c,0x00, // ## + 0x0c,0x00, // ## + 0x18,0x00, // ## + 0x0c,0x00, // ## + 0x0c,0x00, // ## + 0x0c,0x00, // ## + 0x0c,0x00, // ## + 0x06,0x00, // ## + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @2944 '|' (11 pixels wide) + 0x00,0x00, // + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @2976 '}' (11 pixels wide) + 0x00,0x00, // + 0x0c,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x03,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x0c,0x00, // ## + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @3008 '~' (11 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x18,0x00, // ## + 0x24,0x80, // # # # + 0x03,0x00, // ## + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @0 ' ' (11 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @0 ' ' (11 pixels wide) + 0x00,0x00, // + 0x31,0x80, // ## ## + 0x31,0x80, // ## ## + 0x00,0x00, // + 0x1f,0x00, // ##### + 0x01,0x80, // ## + 0x01,0x80, // ## + 0x1f,0x80, // ###### + 0x31,0x80, // ## ## + 0x33,0x80, // ## ### + 0x1d,0xc0, // ### ### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @0 ' ' (11 pixels wide) + 0x60,0xc0, // ## ## + 0x00,0x00, // + 0x3f,0x00, // ###### + 0x0f,0x00, // #### + 0x09,0x00, // # # + 0x19,0x80, // ## ## + 0x19,0x80, // ## ## + 0x1f,0x80, // ###### + 0x30,0xc0, // ## ## + 0x30,0xc0, // ## ## + 0x79,0xe0, // #### #### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @0 ' ' (11 pixels wide) + 0x00,0x00, // + 0x31,0x80, // ## ## + 0x31,0x80, // ## ## + 0x00,0x00, // + 0x1f,0x00, // ##### + 0x31,0x80, // ## ## + 0x60,0xc0, // ## ## + 0x60,0xc0, // ## ## + 0x60,0xc0, // ## ## + 0x31,0x80, // ## ## + 0x1f,0x00, // ##### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @0 ' ' (11 pixels wide) + 0x31,0x80, // ## ## + 0x00,0x00, // + 0x1f,0x00, // ##### + 0x31,0x80, // ## ## + 0x60,0xc0, // ## ## + 0x60,0xc0, // ## ## + 0x60,0xc0, // ## ## + 0x60,0xc0, // ## ## + 0x60,0xc0, // ## ## + 0x31,0x80, // ## ## + 0x1f,0x00, // ##### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @0 ' ' (11 pixels wide) + 0x00,0x00, // + 0x31,0x80, // ## ## + 0x31,0x80, // ## ## + 0x00,0x00, // + 0x73,0x80, // ### ### + 0x31,0x80, // ## ## + 0x31,0x80, // ## ## + 0x31,0x80, // ## ## + 0x31,0x80, // ## ## + 0x33,0x80, // ## ### + 0x1d,0xc0, // ### ### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @0 ' ' (11 pixels wide) + 0x31,0x80, // ## ## + 0x00,0x00, // + 0x7b,0xc0, // #### #### + 0x31,0x80, // ## ## + 0x31,0x80, // ## ## + 0x31,0x80, // ## ## + 0x31,0x80, // ## ## + 0x31,0x80, // ## ## + 0x31,0x80, // ## ## + 0x31,0x80, // ## ## + 0x1f,0x00, // ##### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @0 ' ' (11 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x1e,0x00, // #### + 0x1b,0x00, // ## ## + 0x11,0x00, // # # + 0x11,0x80, // # ## + 0x11,0x80, // # ## + 0x1f,0x00, // ##### + 0x1e,0x00, // #### + 0x13,0x00, // # ## + 0x11,0x00, // # # + 0x13,0x00, // # ## + 0x1e,0x00, // #### + 0x10,0x00, // # + 0x10,0x00, // # + 0x00,0x00 // +}; + +sFONT Font16 = { + Font16_Table, + 11, /* Width */ + 16, /* Height */ +}; + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/font20.c b/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/font20.c new file mode 100644 index 000000000..d53e31382 --- /dev/null +++ b/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/font20.c @@ -0,0 +1,2319 @@ +/** + ****************************************************************************** + * @file font20.c + * @author MCD Application Team + * @version V1.0.0 + * @date 18-February-2014 + * @brief This file provides text font20 for STM32xx-EVAL's LCD driver. + ****************************************************************************** + * @attention + * + *

© COPYRIGHT(c) 2014 STMicroelectronics

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of STMicroelectronics nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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. + * + ****************************************************************************** + */ + +/* Includes ------------------------------------------------------------------*/ +#include "fonts.h" +#include + +// Character bitmaps for Courier New 15pt +const uint8_t Font20_Table[] PROGMEM = +{ + // @0 ' ' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @40 '!' (14 pixels wide) + 0x00,0x00, // + 0x07,0x00, // ### + 0x07,0x00, // ### + 0x07,0x00, // ### + 0x07,0x00, // ### + 0x07,0x00, // ### + 0x07,0x00, // ### + 0x07,0x00, // ### + 0x02,0x00, // # + 0x02,0x00, // # + 0x00,0x00, // + 0x00,0x00, // + 0x07,0x00, // ### + 0x07,0x00, // ### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @80 '"' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x1c,0xe0, // ### ### + 0x1c,0xe0, // ### ### + 0x1c,0xe0, // ### ### + 0x08,0x40, // # # + 0x08,0x40, // # # + 0x08,0x40, // # # + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @120 '#' (14 pixels wide) + 0x0c,0xc0, // ## ## + 0x0c,0xc0, // ## ## + 0x0c,0xc0, // ## ## + 0x0c,0xc0, // ## ## + 0x0c,0xc0, // ## ## + 0x3f,0xf0, // ########## + 0x3f,0xf0, // ########## + 0x0c,0xc0, // ## ## + 0x0c,0xc0, // ## ## + 0x3f,0xf0, // ########## + 0x3f,0xf0, // ########## + 0x0c,0xc0, // ## ## + 0x0c,0xc0, // ## ## + 0x0c,0xc0, // ## ## + 0x0c,0xc0, // ## ## + 0x0c,0xc0, // ## ## + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @160 '$' (14 pixels wide) + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x07,0xe0, // ###### + 0x0f,0xe0, // ####### + 0x18,0x60, // ## ## + 0x18,0x00, // ## + 0x1f,0x00, // ##### + 0x0f,0xc0, // ###### + 0x00,0xe0, // ### + 0x18,0x60, // ## ## + 0x18,0x60, // ## ## + 0x1f,0xc0, // ####### + 0x1f,0x80, // ###### + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @200 '%' (14 pixels wide) + 0x00,0x00, // + 0x1c,0x00, // ### + 0x22,0x00, // # # + 0x22,0x00, // # # + 0x22,0x00, // # # + 0x1c,0x60, // ### ## + 0x01,0xe0, // #### + 0x0f,0x80, // ##### + 0x3c,0x00, // #### + 0x31,0xc0, // ## ### + 0x02,0x20, // # # + 0x02,0x20, // # # + 0x02,0x20, // # # + 0x01,0xc0, // ### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @240 '&' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x03,0xe0, // ##### + 0x0f,0xe0, // ####### + 0x0c,0x00, // ## + 0x0c,0x00, // ## + 0x06,0x00, // ## + 0x0f,0x30, // #### ## + 0x1f,0xf0, // ######### + 0x19,0xe0, // ## #### + 0x18,0xc0, // ## ## + 0x1f,0xf0, // ######### + 0x07,0xb0, // #### ## + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @280 ''' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x03,0x80, // ### + 0x03,0x80, // ### + 0x03,0x80, // ### + 0x01,0x00, // # + 0x01,0x00, // # + 0x01,0x00, // # + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @320 '(' (14 pixels wide) + 0x00,0x00, // + 0x00,0xc0, // ## + 0x00,0xc0, // ## + 0x01,0x80, // ## + 0x01,0x80, // ## + 0x01,0x80, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x01,0x80, // ## + 0x01,0x80, // ## + 0x01,0x80, // ## + 0x00,0xc0, // ## + 0x00,0xc0, // ## + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @360 ')' (14 pixels wide) + 0x00,0x00, // + 0x0c,0x00, // ## + 0x0c,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x0c,0x00, // ## + 0x0c,0x00, // ## + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @400 '*' (14 pixels wide) + 0x00,0x00, // + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x1b,0x60, // ## ## ## + 0x1f,0xe0, // ######## + 0x07,0x80, // #### + 0x07,0x80, // #### + 0x0f,0xc0, // ###### + 0x0c,0xc0, // ## ## + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @440 '+' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x3f,0xf0, // ########## + 0x3f,0xf0, // ########## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @480 ',' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x03,0x80, // ### + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x04,0x00, // # + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @520 '-' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x3f,0xe0, // ######### + 0x3f,0xe0, // ######### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @560 '.' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x03,0x80, // ### + 0x03,0x80, // ### + 0x03,0x80, // ### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @600 '/' (14 pixels wide) + 0x00,0x60, // ## + 0x00,0x60, // ## + 0x00,0xc0, // ## + 0x00,0xc0, // ## + 0x00,0xc0, // ## + 0x01,0x80, // ## + 0x01,0x80, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x0c,0x00, // ## + 0x0c,0x00, // ## + 0x0c,0x00, // ## + 0x18,0x00, // ## + 0x18,0x00, // ## + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @640 '0' (14 pixels wide) + 0x00,0x00, // + 0x0f,0x80, // ##### + 0x1f,0xc0, // ####### + 0x18,0xc0, // ## ## + 0x30,0x60, // ## ## + 0x30,0x60, // ## ## + 0x30,0x60, // ## ## + 0x30,0x60, // ## ## + 0x30,0x60, // ## ## + 0x30,0x60, // ## ## + 0x30,0x60, // ## ## + 0x18,0xc0, // ## ## + 0x1f,0xc0, // ####### + 0x0f,0x80, // ##### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @680 '1' (14 pixels wide) + 0x00,0x00, // + 0x03,0x00, // ## + 0x1f,0x00, // ##### + 0x1f,0x00, // ##### + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x1f,0xe0, // ######## + 0x1f,0xe0, // ######## + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @720 '2' (14 pixels wide) + 0x00,0x00, // + 0x0f,0x80, // ##### + 0x1f,0xc0, // ####### + 0x38,0xe0, // ### ### + 0x30,0x60, // ## ## + 0x00,0x60, // ## + 0x00,0xc0, // ## + 0x01,0x80, // ## + 0x03,0x00, // ## + 0x06,0x00, // ## + 0x0c,0x00, // ## + 0x18,0x00, // ## + 0x3f,0xe0, // ######### + 0x3f,0xe0, // ######### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @760 '3' (14 pixels wide) + 0x00,0x00, // + 0x0f,0x80, // ##### + 0x3f,0xc0, // ######## + 0x30,0xe0, // ## ### + 0x00,0x60, // ## + 0x00,0xe0, // ### + 0x07,0xc0, // ##### + 0x07,0xc0, // ##### + 0x00,0xe0, // ### + 0x00,0x60, // ## + 0x00,0x60, // ## + 0x60,0xe0, // ## ### + 0x7f,0xc0, // ######### + 0x3f,0x80, // ####### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @800 '4' (14 pixels wide) + 0x00,0x00, // + 0x01,0xc0, // ### + 0x03,0xc0, // #### + 0x03,0xc0, // #### + 0x06,0xc0, // ## ## + 0x0c,0xc0, // ## ## + 0x0c,0xc0, // ## ## + 0x18,0xc0, // ## ## + 0x30,0xc0, // ## ## + 0x3f,0xe0, // ######### + 0x3f,0xe0, // ######### + 0x00,0xc0, // ## + 0x03,0xe0, // ##### + 0x03,0xe0, // ##### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @840 '5' (14 pixels wide) + 0x00,0x00, // + 0x1f,0xc0, // ####### + 0x1f,0xc0, // ####### + 0x18,0x00, // ## + 0x18,0x00, // ## + 0x1f,0x80, // ###### + 0x1f,0xc0, // ####### + 0x18,0xe0, // ## ### + 0x00,0x60, // ## + 0x00,0x60, // ## + 0x00,0x60, // ## + 0x30,0xe0, // ## ### + 0x3f,0xc0, // ######## + 0x1f,0x80, // ###### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @880 '6' (14 pixels wide) + 0x00,0x00, // + 0x03,0xe0, // ##### + 0x0f,0xe0, // ####### + 0x1e,0x00, // #### + 0x18,0x00, // ## + 0x38,0x00, // ### + 0x37,0x80, // ## #### + 0x3f,0xc0, // ######## + 0x38,0xe0, // ### ### + 0x30,0x60, // ## ## + 0x30,0x60, // ## ## + 0x18,0xe0, // ## ### + 0x1f,0xc0, // ####### + 0x07,0x80, // #### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @920 '7' (14 pixels wide) + 0x00,0x00, // + 0x3f,0xe0, // ######### + 0x3f,0xe0, // ######### + 0x30,0x60, // ## ## + 0x00,0x60, // ## + 0x00,0xc0, // ## + 0x00,0xc0, // ## + 0x00,0xc0, // ## + 0x01,0x80, // ## + 0x01,0x80, // ## + 0x01,0x80, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @960 '8' (14 pixels wide) + 0x00,0x00, // + 0x0f,0x80, // ##### + 0x1f,0xc0, // ####### + 0x38,0xe0, // ### ### + 0x30,0x60, // ## ## + 0x38,0xe0, // ### ### + 0x1f,0xc0, // ####### + 0x1f,0xc0, // ####### + 0x38,0xe0, // ### ### + 0x30,0x60, // ## ## + 0x30,0x60, // ## ## + 0x38,0xe0, // ### ### + 0x1f,0xc0, // ####### + 0x0f,0x80, // ##### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @1000 '9' (14 pixels wide) + 0x00,0x00, // + 0x0f,0x00, // #### + 0x1f,0xc0, // ####### + 0x38,0xc0, // ### ## + 0x30,0x60, // ## ## + 0x30,0x60, // ## ## + 0x38,0xe0, // ### ### + 0x1f,0xe0, // ######## + 0x0f,0x60, // #### ## + 0x00,0xe0, // ### + 0x00,0xc0, // ## + 0x03,0xc0, // #### + 0x3f,0x80, // ####### + 0x3e,0x00, // ##### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @1040 ':' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x03,0x80, // ### + 0x03,0x80, // ### + 0x03,0x80, // ### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x03,0x80, // ### + 0x03,0x80, // ### + 0x03,0x80, // ### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @1080 ';' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x01,0xc0, // ### + 0x01,0xc0, // ### + 0x01,0xc0, // ### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x03,0x80, // ### + 0x03,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x04,0x00, // # + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @1120 '<' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x30, // ## + 0x00,0xf0, // #### + 0x03,0xc0, // #### + 0x07,0x00, // ### + 0x1c,0x00, // ### + 0x78,0x00, // #### + 0x1c,0x00, // ### + 0x07,0x00, // ### + 0x03,0xc0, // #### + 0x00,0xf0, // #### + 0x00,0x30, // ## + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @1160 '=' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x7f,0xf0, // ########### + 0x7f,0xf0, // ########### + 0x00,0x00, // + 0x00,0x00, // + 0x7f,0xf0, // ########### + 0x7f,0xf0, // ########### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @1200 '>' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x30,0x00, // ## + 0x3c,0x00, // #### + 0x0f,0x00, // #### + 0x03,0x80, // ### + 0x00,0xe0, // ### + 0x00,0x78, // #### + 0x00,0xe0, // ### + 0x03,0x80, // ### + 0x0f,0x00, // #### + 0x3c,0x00, // #### + 0x30,0x00, // ## + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @1240 '?' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x0f,0x80, // ##### + 0x1f,0xc0, // ####### + 0x18,0x60, // ## ## + 0x18,0x60, // ## ## + 0x00,0x60, // ## + 0x01,0xc0, // ### + 0x03,0x80, // ### + 0x03,0x00, // ## + 0x00,0x00, // + 0x00,0x00, // + 0x07,0x00, // ### + 0x07,0x00, // ### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @1280 '@' (14 pixels wide) + 0x00,0x00, // + 0x03,0x80, // ### + 0x0c,0x80, // ## # + 0x08,0x40, // # # + 0x10,0x40, // # # + 0x10,0x40, // # # + 0x11,0xc0, // # ### + 0x12,0x40, // # # # + 0x12,0x40, // # # # + 0x12,0x40, // # # # + 0x11,0xc0, // # ### + 0x10,0x00, // # + 0x08,0x00, // # + 0x08,0x40, // # # + 0x07,0x80, // #### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @1320 'A' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x1f,0x80, // ###### + 0x1f,0x80, // ###### + 0x03,0x80, // ### + 0x06,0xc0, // ## ## + 0x06,0xc0, // ## ## + 0x0c,0xc0, // ## ## + 0x0c,0x60, // ## ## + 0x1f,0xe0, // ######## + 0x1f,0xe0, // ######## + 0x30,0x30, // ## ## + 0x78,0x78, // #### #### + 0x78,0x78, // #### #### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @1360 'B' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x3f,0x80, // ####### + 0x3f,0xc0, // ######## + 0x18,0x60, // ## ## + 0x18,0x60, // ## ## + 0x18,0xe0, // ## ### + 0x1f,0xc0, // ####### + 0x1f,0xe0, // ######## + 0x18,0x70, // ## ### + 0x18,0x30, // ## ## + 0x18,0x30, // ## ## + 0x3f,0xf0, // ########## + 0x3f,0xe0, // ######### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @1400 'C' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x07,0xb0, // #### ## + 0x0f,0xf0, // ######## + 0x1c,0x70, // ### ### + 0x38,0x30, // ### ## + 0x30,0x00, // ## + 0x30,0x00, // ## + 0x30,0x00, // ## + 0x30,0x00, // ## + 0x38,0x30, // ### ## + 0x1c,0x70, // ### ### + 0x0f,0xe0, // ####### + 0x07,0xc0, // ##### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @1440 'D' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x7f,0x80, // ######## + 0x7f,0xc0, // ######### + 0x30,0xe0, // ## ### + 0x30,0x70, // ## ### + 0x30,0x30, // ## ## + 0x30,0x30, // ## ## + 0x30,0x30, // ## ## + 0x30,0x30, // ## ## + 0x30,0x70, // ## ### + 0x30,0xe0, // ## ### + 0x7f,0xc0, // ######### + 0x7f,0x80, // ######## + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @1480 'E' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x3f,0xf0, // ########## + 0x3f,0xf0, // ########## + 0x18,0x30, // ## ## + 0x18,0x30, // ## ## + 0x19,0x80, // ## ## + 0x1f,0x80, // ###### + 0x1f,0x80, // ###### + 0x19,0x80, // ## ## + 0x18,0x30, // ## ## + 0x18,0x30, // ## ## + 0x3f,0xf0, // ########## + 0x3f,0xf0, // ########## + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @1520 'F' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x3f,0xf0, // ########## + 0x3f,0xf0, // ########## + 0x18,0x30, // ## ## + 0x18,0x30, // ## ## + 0x19,0x80, // ## ## + 0x1f,0x80, // ###### + 0x1f,0x80, // ###### + 0x19,0x80, // ## ## + 0x18,0x00, // ## + 0x18,0x00, // ## + 0x3f,0x00, // ###### + 0x3f,0x00, // ###### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @1560 'G' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x07,0xb0, // #### ## + 0x1f,0xf0, // ######### + 0x18,0x70, // ## ### + 0x30,0x30, // ## ## + 0x30,0x00, // ## + 0x30,0x00, // ## + 0x31,0xf8, // ## ###### + 0x31,0xf8, // ## ###### + 0x30,0x30, // ## ## + 0x18,0x30, // ## ## + 0x1f,0xf0, // ######### + 0x07,0xc0, // ##### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @1600 'H' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x3c,0xf0, // #### #### + 0x3c,0xf0, // #### #### + 0x18,0x60, // ## ## + 0x18,0x60, // ## ## + 0x18,0x60, // ## ## + 0x1f,0xe0, // ######## + 0x1f,0xe0, // ######## + 0x18,0x60, // ## ## + 0x18,0x60, // ## ## + 0x18,0x60, // ## ## + 0x3c,0xf0, // #### #### + 0x3c,0xf0, // #### #### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @1640 'I' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x1f,0xe0, // ######## + 0x1f,0xe0, // ######## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x1f,0xe0, // ######## + 0x1f,0xe0, // ######## + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @1680 'J' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x03,0xf8, // ####### + 0x03,0xf8, // ####### + 0x00,0x60, // ## + 0x00,0x60, // ## + 0x00,0x60, // ## + 0x00,0x60, // ## + 0x30,0x60, // ## ## + 0x30,0x60, // ## ## + 0x30,0x60, // ## ## + 0x30,0xe0, // ## ### + 0x3f,0xc0, // ######## + 0x0f,0x80, // ##### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @1720 'K' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x3e,0xf8, // ##### ##### + 0x3e,0xf8, // ##### ##### + 0x18,0xe0, // ## ### + 0x19,0x80, // ## ## + 0x1b,0x00, // ## ## + 0x1f,0x00, // ##### + 0x1d,0x80, // ### ## + 0x18,0xc0, // ## ## + 0x18,0xc0, // ## ## + 0x18,0x60, // ## ## + 0x3e,0x78, // ##### #### + 0x3e,0x38, // ##### ### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @1760 'L' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x3f,0x00, // ###### + 0x3f,0x00, // ###### + 0x0c,0x00, // ## + 0x0c,0x00, // ## + 0x0c,0x00, // ## + 0x0c,0x00, // ## + 0x0c,0x00, // ## + 0x0c,0x30, // ## ## + 0x0c,0x30, // ## ## + 0x0c,0x30, // ## ## + 0x3f,0xf0, // ########## + 0x3f,0xf0, // ########## + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @1800 'M' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x78,0x78, // #### #### + 0x78,0x78, // #### #### + 0x38,0x70, // ### ### + 0x3c,0xf0, // #### #### + 0x34,0xb0, // ## # # ## + 0x37,0xb0, // ## #### ## + 0x37,0xb0, // ## #### ## + 0x33,0x30, // ## ## ## + 0x33,0x30, // ## ## ## + 0x30,0x30, // ## ## + 0x7c,0xf8, // ##### ##### + 0x7c,0xf8, // ##### ##### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @1840 'N' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x39,0xf0, // ### ##### + 0x3d,0xf0, // #### ##### + 0x1c,0x60, // ### ## + 0x1e,0x60, // #### ## + 0x1e,0x60, // #### ## + 0x1b,0x60, // ## ## ## + 0x1b,0x60, // ## ## ## + 0x19,0xe0, // ## #### + 0x19,0xe0, // ## #### + 0x18,0xe0, // ## ### + 0x3e,0xe0, // ##### ### + 0x3e,0x60, // ##### ## + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @1880 'O' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x07,0x80, // #### + 0x0f,0xc0, // ###### + 0x1c,0xe0, // ### ### + 0x38,0x70, // ### ### + 0x30,0x30, // ## ## + 0x30,0x30, // ## ## + 0x30,0x30, // ## ## + 0x30,0x30, // ## ## + 0x38,0x70, // ### ### + 0x1c,0xe0, // ### ### + 0x0f,0xc0, // ###### + 0x07,0x80, // #### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @1920 'P' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x3f,0xc0, // ######## + 0x3f,0xe0, // ######### + 0x18,0x70, // ## ### + 0x18,0x30, // ## ## + 0x18,0x30, // ## ## + 0x18,0x70, // ## ### + 0x1f,0xe0, // ######## + 0x1f,0xc0, // ####### + 0x18,0x00, // ## + 0x18,0x00, // ## + 0x3f,0x00, // ###### + 0x3f,0x00, // ###### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @1960 'Q' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x07,0x80, // #### + 0x0f,0xc0, // ###### + 0x1c,0xe0, // ### ### + 0x38,0x70, // ### ### + 0x30,0x30, // ## ## + 0x30,0x30, // ## ## + 0x30,0x30, // ## ## + 0x30,0x30, // ## ## + 0x38,0x70, // ### ### + 0x1c,0xe0, // ### ### + 0x0f,0xc0, // ###### + 0x07,0x80, // #### + 0x07,0xb0, // #### ## + 0x0f,0xf0, // ######## + 0x0c,0xe0, // ## ### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @2000 'R' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x3f,0xc0, // ######## + 0x3f,0xe0, // ######### + 0x18,0x70, // ## ### + 0x18,0x30, // ## ## + 0x18,0x70, // ## ### + 0x1f,0xe0, // ######## + 0x1f,0xc0, // ####### + 0x18,0xe0, // ## ### + 0x18,0x60, // ## ## + 0x18,0x70, // ## ### + 0x3e,0x38, // ##### ### + 0x3e,0x18, // ##### ## + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @2040 'S' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x0f,0xb0, // ##### ## + 0x1f,0xf0, // ######### + 0x38,0x70, // ### ### + 0x30,0x30, // ## ## + 0x38,0x00, // ### + 0x1f,0x80, // ###### + 0x07,0xe0, // ###### + 0x00,0x70, // ### + 0x30,0x30, // ## ## + 0x38,0x70, // ### ### + 0x3f,0xe0, // ######### + 0x37,0xc0, // ## ##### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @2080 'T' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x3f,0xf0, // ########## + 0x3f,0xf0, // ########## + 0x33,0x30, // ## ## ## + 0x33,0x30, // ## ## ## + 0x33,0x30, // ## ## ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x0f,0xc0, // ###### + 0x0f,0xc0, // ###### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @2120 'U' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x3c,0xf0, // #### #### + 0x3c,0xf0, // #### #### + 0x18,0x60, // ## ## + 0x18,0x60, // ## ## + 0x18,0x60, // ## ## + 0x18,0x60, // ## ## + 0x18,0x60, // ## ## + 0x18,0x60, // ## ## + 0x18,0x60, // ## ## + 0x1c,0xe0, // ### ### + 0x0f,0xc0, // ###### + 0x07,0x80, // #### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @2160 'V' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x78,0xf0, // #### #### + 0x78,0xf0, // #### #### + 0x30,0x60, // ## ## + 0x30,0x60, // ## ## + 0x18,0xc0, // ## ## + 0x18,0xc0, // ## ## + 0x0d,0x80, // ## ## + 0x0d,0x80, // ## ## + 0x0d,0x80, // ## ## + 0x07,0x00, // ### + 0x07,0x00, // ### + 0x07,0x00, // ### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @2200 'W' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x7c,0x7c, // ##### ##### + 0x7c,0x7c, // ##### ##### + 0x30,0x18, // ## ## + 0x33,0x98, // ## ### ## + 0x33,0x98, // ## ### ## + 0x33,0x98, // ## ### ## + 0x36,0xd8, // ## ## ## ## + 0x16,0xd0, // # ## ## # + 0x1c,0x70, // ### ### + 0x1c,0x70, // ### ### + 0x1c,0x70, // ### ### + 0x18,0x30, // ## ## + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @2240 'X' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x78,0xf0, // #### #### + 0x78,0xf0, // #### #### + 0x30,0x60, // ## ## + 0x18,0xc0, // ## ## + 0x0d,0x80, // ## ## + 0x07,0x00, // ### + 0x07,0x00, // ### + 0x0d,0x80, // ## ## + 0x18,0xc0, // ## ## + 0x30,0x60, // ## ## + 0x78,0xf0, // #### #### + 0x78,0xf0, // #### #### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @2280 'Y' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x3c,0xf0, // #### #### + 0x3c,0xf0, // #### #### + 0x18,0x60, // ## ## + 0x0c,0xc0, // ## ## + 0x07,0x80, // #### + 0x07,0x80, // #### + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x0f,0xc0, // ###### + 0x0f,0xc0, // ###### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @2320 'Z' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x1f,0xe0, // ######## + 0x1f,0xe0, // ######## + 0x18,0x60, // ## ## + 0x18,0xc0, // ## ## + 0x01,0x80, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x06,0x00, // ## + 0x0c,0x60, // ## ## + 0x18,0x60, // ## ## + 0x1f,0xe0, // ######## + 0x1f,0xe0, // ######## + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @2360 '[' (14 pixels wide) + 0x00,0x00, // + 0x03,0xc0, // #### + 0x03,0xc0, // #### + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0xc0, // #### + 0x03,0xc0, // #### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @2400 '\' (14 pixels wide) + 0x18,0x00, // ## + 0x18,0x00, // ## + 0x0c,0x00, // ## + 0x0c,0x00, // ## + 0x0c,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x01,0x80, // ## + 0x01,0x80, // ## + 0x00,0xc0, // ## + 0x00,0xc0, // ## + 0x00,0xc0, // ## + 0x00,0x60, // ## + 0x00,0x60, // ## + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @2440 ']' (14 pixels wide) + 0x00,0x00, // + 0x0f,0x00, // #### + 0x0f,0x00, // #### + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x0f,0x00, // #### + 0x0f,0x00, // #### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @2480 '^' (14 pixels wide) + 0x00,0x00, // + 0x02,0x00, // # + 0x07,0x00, // ### + 0x0d,0x80, // ## ## + 0x18,0xc0, // ## ## + 0x30,0x60, // ## ## + 0x20,0x20, // # # + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @2520 '_' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0xff,0xfc, //############## + 0xff,0xfc, //############## + + // @2560 '`' (14 pixels wide) + 0x00,0x00, // + 0x04,0x00, // # + 0x03,0x00, // ## + 0x00,0x80, // # + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @2600 'a' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x0f,0xc0, // ###### + 0x1f,0xe0, // ######## + 0x00,0x60, // ## + 0x0f,0xe0, // ####### + 0x1f,0xe0, // ######## + 0x38,0x60, // ### ## + 0x30,0xe0, // ## ### + 0x3f,0xf0, // ########## + 0x1f,0x70, // ##### ### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @2640 'b' (14 pixels wide) + 0x00,0x00, // + 0x70,0x00, // ### + 0x70,0x00, // ### + 0x30,0x00, // ## + 0x30,0x00, // ## + 0x37,0x80, // ## #### + 0x3f,0xe0, // ######### + 0x38,0x60, // ### ## + 0x30,0x30, // ## ## + 0x30,0x30, // ## ## + 0x30,0x30, // ## ## + 0x38,0x60, // ### ## + 0x7f,0xe0, // ########## + 0x77,0x80, // ### #### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @2680 'c' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x07,0xb0, // #### ## + 0x1f,0xf0, // ######### + 0x18,0x30, // ## ## + 0x30,0x30, // ## ## + 0x30,0x00, // ## + 0x30,0x00, // ## + 0x38,0x30, // ### ## + 0x1f,0xf0, // ######### + 0x0f,0xc0, // ###### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @2720 'd' (14 pixels wide) + 0x00,0x00, // + 0x00,0x70, // ### + 0x00,0x70, // ### + 0x00,0x30, // ## + 0x00,0x30, // ## + 0x07,0xb0, // #### ## + 0x1f,0xf0, // ######### + 0x18,0x70, // ## ### + 0x30,0x30, // ## ## + 0x30,0x30, // ## ## + 0x30,0x30, // ## ## + 0x38,0x70, // ### ### + 0x1f,0xf8, // ########## + 0x07,0xb8, // #### ### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @2760 'e' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x07,0x80, // #### + 0x1f,0xe0, // ######## + 0x18,0x60, // ## ## + 0x3f,0xf0, // ########## + 0x3f,0xf0, // ########## + 0x30,0x00, // ## + 0x18,0x30, // ## ## + 0x1f,0xf0, // ######### + 0x07,0xc0, // ##### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @2800 'f' (14 pixels wide) + 0x00,0x00, // + 0x03,0xf0, // ###### + 0x07,0xf0, // ####### + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x1f,0xe0, // ######## + 0x1f,0xe0, // ######## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x1f,0xe0, // ######## + 0x1f,0xe0, // ######## + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @2840 'g' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x07,0xb8, // #### ### + 0x1f,0xf8, // ########## + 0x18,0x70, // ## ### + 0x30,0x30, // ## ## + 0x30,0x30, // ## ## + 0x30,0x30, // ## ## + 0x18,0x70, // ## ### + 0x1f,0xf0, // ######### + 0x07,0xb0, // #### ## + 0x00,0x30, // ## + 0x00,0x70, // ### + 0x0f,0xe0, // ####### + 0x0f,0xc0, // ###### + 0x00,0x00, // + 0x00,0x00, // + + // @2880 'h' (14 pixels wide) + 0x00,0x00, // + 0x38,0x00, // ### + 0x38,0x00, // ### + 0x18,0x00, // ## + 0x18,0x00, // ## + 0x1b,0xc0, // ## #### + 0x1f,0xe0, // ######## + 0x1c,0x60, // ### ## + 0x18,0x60, // ## ## + 0x18,0x60, // ## ## + 0x18,0x60, // ## ## + 0x18,0x60, // ## ## + 0x3c,0xf0, // #### #### + 0x3c,0xf0, // #### #### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @2920 'i' (14 pixels wide) + 0x00,0x00, // + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x00,0x00, // + 0x00,0x00, // + 0x1f,0x00, // ##### + 0x1f,0x00, // ##### + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x1f,0xe0, // ######## + 0x1f,0xe0, // ######## + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @2960 'j' (14 pixels wide) + 0x00,0x00, // + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x00,0x00, // + 0x00,0x00, // + 0x1f,0xc0, // ####### + 0x1f,0xc0, // ####### + 0x00,0xc0, // ## + 0x00,0xc0, // ## + 0x00,0xc0, // ## + 0x00,0xc0, // ## + 0x00,0xc0, // ## + 0x00,0xc0, // ## + 0x00,0xc0, // ## + 0x00,0xc0, // ## + 0x01,0xc0, // ### + 0x3f,0x80, // ####### + 0x3f,0x00, // ###### + 0x00,0x00, // + 0x00,0x00, // + + // @3000 'k' (14 pixels wide) + 0x00,0x00, // + 0x38,0x00, // ### + 0x38,0x00, // ### + 0x18,0x00, // ## + 0x18,0x00, // ## + 0x1b,0xe0, // ## ##### + 0x1b,0xe0, // ## ##### + 0x1b,0x00, // ## ## + 0x1e,0x00, // #### + 0x1e,0x00, // #### + 0x1b,0x00, // ## ## + 0x19,0x80, // ## ## + 0x39,0xf0, // ### ##### + 0x39,0xf0, // ### ##### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @3040 'l' (14 pixels wide) + 0x00,0x00, // + 0x1f,0x00, // ##### + 0x1f,0x00, // ##### + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x1f,0xe0, // ######## + 0x1f,0xe0, // ######## + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @3080 'm' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x7e,0xe0, // ###### ### + 0x7f,0xf0, // ########### + 0x33,0x30, // ## ## ## + 0x33,0x30, // ## ## ## + 0x33,0x30, // ## ## ## + 0x33,0x30, // ## ## ## + 0x33,0x30, // ## ## ## + 0x7b,0xb8, // #### ### ### + 0x7b,0xb8, // #### ### ### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @3120 'n' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x3b,0xc0, // ### #### + 0x3f,0xe0, // ######### + 0x1c,0x60, // ### ## + 0x18,0x60, // ## ## + 0x18,0x60, // ## ## + 0x18,0x60, // ## ## + 0x18,0x60, // ## ## + 0x3c,0xf0, // #### #### + 0x3c,0xf0, // #### #### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @3160 'o' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x07,0x80, // #### + 0x1f,0xe0, // ######## + 0x18,0x60, // ## ## + 0x30,0x30, // ## ## + 0x30,0x30, // ## ## + 0x30,0x30, // ## ## + 0x18,0x60, // ## ## + 0x1f,0xe0, // ######## + 0x07,0x80, // #### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @3200 'p' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x77,0x80, // ### #### + 0x7f,0xe0, // ########## + 0x38,0x60, // ### ## + 0x30,0x30, // ## ## + 0x30,0x30, // ## ## + 0x30,0x30, // ## ## + 0x38,0x60, // ### ## + 0x3f,0xe0, // ######### + 0x37,0x80, // ## #### + 0x30,0x00, // ## + 0x30,0x00, // ## + 0x7c,0x00, // ##### + 0x7c,0x00, // ##### + 0x00,0x00, // + 0x00,0x00, // + + // @3240 'q' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x07,0xb8, // #### ### + 0x1f,0xf8, // ########## + 0x18,0x70, // ## ### + 0x30,0x30, // ## ## + 0x30,0x30, // ## ## + 0x30,0x30, // ## ## + 0x18,0x70, // ## ### + 0x1f,0xf0, // ######### + 0x07,0xb0, // #### ## + 0x00,0x30, // ## + 0x00,0x30, // ## + 0x00,0xf8, // ##### + 0x00,0xf8, // ##### + 0x00,0x00, // + 0x00,0x00, // + + // @3280 'r' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x3c,0xe0, // #### ### + 0x3d,0xf0, // #### ##### + 0x0f,0x30, // #### ## + 0x0e,0x00, // ### + 0x0c,0x00, // ## + 0x0c,0x00, // ## + 0x0c,0x00, // ## + 0x3f,0xc0, // ######## + 0x3f,0xc0, // ######## + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @3320 's' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x07,0xe0, // ###### + 0x1f,0xe0, // ######## + 0x18,0x60, // ## ## + 0x1e,0x00, // #### + 0x0f,0xc0, // ###### + 0x01,0xe0, // #### + 0x18,0x60, // ## ## + 0x1f,0xe0, // ######## + 0x1f,0x80, // ###### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @3360 't' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x0c,0x00, // ## + 0x0c,0x00, // ## + 0x0c,0x00, // ## + 0x3f,0xe0, // ######### + 0x3f,0xe0, // ######### + 0x0c,0x00, // ## + 0x0c,0x00, // ## + 0x0c,0x00, // ## + 0x0c,0x00, // ## + 0x0c,0x30, // ## ## + 0x0f,0xf0, // ######## + 0x07,0xc0, // ##### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @3400 'u' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x38,0xe0, // ### ### + 0x38,0xe0, // ### ### + 0x18,0x60, // ## ## + 0x18,0x60, // ## ## + 0x18,0x60, // ## ## + 0x18,0x60, // ## ## + 0x18,0xe0, // ## ### + 0x1f,0xf0, // ######### + 0x0f,0x70, // #### ### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @3440 'v' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x78,0xf0, // #### #### + 0x78,0xf0, // #### #### + 0x30,0x60, // ## ## + 0x18,0xc0, // ## ## + 0x18,0xc0, // ## ## + 0x0d,0x80, // ## ## + 0x0d,0x80, // ## ## + 0x07,0x00, // ### + 0x07,0x00, // ### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @3480 'w' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x78,0xf0, // #### #### + 0x78,0xf0, // #### #### + 0x32,0x60, // ## # ## + 0x32,0x60, // ## # ## + 0x37,0xe0, // ## ###### + 0x1d,0xc0, // ### ### + 0x1d,0xc0, // ### ### + 0x18,0xc0, // ## ## + 0x18,0xc0, // ## ## + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @3520 'x' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x3c,0xf0, // #### #### + 0x3c,0xf0, // #### #### + 0x0c,0xc0, // ## ## + 0x07,0x80, // #### + 0x03,0x00, // ## + 0x07,0x80, // #### + 0x0c,0xc0, // ## ## + 0x3c,0xf0, // #### #### + 0x3c,0xf0, // #### #### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @3560 'y' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x78,0xf0, // #### #### + 0x78,0xf0, // #### #### + 0x30,0x60, // ## ## + 0x18,0xc0, // ## ## + 0x18,0xc0, // ## ## + 0x0d,0x80, // ## ## + 0x0f,0x80, // ##### + 0x07,0x00, // ### + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x0c,0x00, // ## + 0x7f,0x00, // ####### + 0x7f,0x00, // ####### + 0x00,0x00, // + 0x00,0x00, // + + // @3600 'z' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x1f,0xe0, // ######## + 0x1f,0xe0, // ######## + 0x18,0xc0, // ## ## + 0x01,0x80, // ## + 0x03,0x00, // ## + 0x06,0x00, // ## + 0x0c,0x60, // ## ## + 0x1f,0xe0, // ######## + 0x1f,0xe0, // ######## + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @3640 '{' (14 pixels wide) + 0x00,0x00, // + 0x01,0xc0, // ### + 0x03,0xc0, // #### + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x07,0x00, // ### + 0x0e,0x00, // ### + 0x07,0x00, // ### + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0xc0, // #### + 0x01,0xc0, // ### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @3680 '|' (14 pixels wide) + 0x00,0x00, // + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x03,0x00, // ## + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @3720 '}' (14 pixels wide) + 0x00,0x00, // + 0x1c,0x00, // ### + 0x1e,0x00, // #### + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x07,0x00, // ### + 0x03,0x80, // ### + 0x07,0x00, // ### + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x06,0x00, // ## + 0x1e,0x00, // #### + 0x1c,0x00, // ### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @3760 '~' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x0e,0x00, // ### + 0x3f,0x30, // ###### ## + 0x33,0xf0, // ## ###### + 0x01,0xe0, // #### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @0 ' ' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @0 ' ' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x18,0x60, // ## ## + 0x18,0x60, // ## ## + 0x00,0x00, // + 0x0f,0xc0, // ###### + 0x1f,0xe0, // ######## + 0x00,0x60, // ## + 0x0f,0xe0, // ####### + 0x1f,0xe0, // ######## + 0x38,0x60, // ### ## + 0x30,0xe0, // ## ### + 0x3f,0xf0, // ########## + 0x1f,0x70, // ##### ### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @0 ' ' (14 pixels wide) + 0x30,0x30, // ## ## + 0x00,0x00, // + 0x1f,0x80, // ###### + 0x1f,0x80, // ###### + 0x03,0x80, // ### + 0x06,0xc0, // ## ## + 0x06,0xc0, // ## ## + 0x0c,0xc0, // ## ## + 0x0c,0x60, // ## ## + 0x1f,0xe0, // ######## + 0x1f,0xe0, // ######## + 0x30,0x30, // ## ## + 0x78,0x78, // #### #### + 0x78,0x78, // #### #### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @0 ' ' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x18,0x60, // ## ## + 0x18,0x60, // ## ## + 0x00,0x00, // + 0x07,0x80, // #### + 0x1f,0xe0, // ######## + 0x18,0x60, // ## ## + 0x30,0x30, // ## ## + 0x30,0x30, // ## ## + 0x30,0x30, // ## ## + 0x18,0x60, // ## ## + 0x1f,0xe0, // ######## + 0x07,0x80, // #### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @0 ' ' (14 pixels wide) + 0x30,0x30, // ## ## + 0x00,0x00, // + 0x07,0x80, // #### + 0x0f,0xc0, // ###### + 0x1c,0xe0, // ### ### + 0x38,0x70, // ### ### + 0x30,0x30, // ## ## + 0x30,0x30, // ## ## + 0x30,0x30, // ## ## + 0x30,0x30, // ## ## + 0x38,0x70, // ### ### + 0x1c,0xe0, // ### ### + 0x0f,0xc0, // ###### + 0x07,0x80, // #### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @0 ' ' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x18,0x60, // ## ## + 0x18,0x60, // ## ## + 0x00,0x00, // + 0x38,0xe0, // ### ### + 0x38,0xe0, // ### ### + 0x18,0x60, // ## ## + 0x18,0x60, // ## ## + 0x18,0x60, // ## ## + 0x18,0x60, // ## ## + 0x18,0xe0, // ## ### + 0x1f,0xf0, // ######### + 0x0f,0x70, // #### ### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @0 ' ' (14 pixels wide) + 0x18,0x60, // ## ## + 0x00,0x00, // + 0x3c,0xf0, // #### #### + 0x3c,0xf0, // #### #### + 0x18,0x60, // ## ## + 0x18,0x60, // ## ## + 0x18,0x60, // ## ## + 0x18,0x60, // ## ## + 0x18,0x60, // ## ## + 0x18,0x60, // ## ## + 0x18,0x60, // ## ## + 0x1c,0xe0, // ### ### + 0x0f,0xc0, // ###### + 0x07,0x80, // #### + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00, // + + // @0 ' ' (14 pixels wide) + 0x00,0x00, // + 0x00,0x00, // + 0x0f,0x00, // #### + 0x1f,0x80, // ###### + 0x18,0x80, // ## # + 0x18,0x80, // ## # + 0x18,0x80, // ## # + 0x1b,0x00, // ## ## + 0x1e,0x00, // #### + 0x1f,0xc0, // ####### + 0x18,0x60, // ## ## + 0x18,0x60, // ## ## + 0x18,0x60, // ## ## + 0x18,0xc0, // ## ## + 0x1f,0x80, // ###### + 0x18,0x00, // ## + 0x18,0x00, // ## + 0x00,0x00, // + 0x00,0x00, // + 0x00,0x00 // +}; + + +sFONT Font20 = { + Font20_Table, + 14, /* Width */ + 20, /* Height */ +}; + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/font24.c b/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/font24.c new file mode 100644 index 000000000..66fcc495e --- /dev/null +++ b/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/font24.c @@ -0,0 +1,2729 @@ +/** + ****************************************************************************** + * @file font24.c + * @author MCD Application Team + * @version V1.0.0 + * @date 18-February-2014 + * @brief This file provides text font24 for STM32xx-EVAL's LCD driver. + ****************************************************************************** + * @attention + * + *

© COPYRIGHT(c) 2014 STMicroelectronics

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of STMicroelectronics nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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. + * + ****************************************************************************** + */ + +/* Includes ------------------------------------------------------------------*/ +#include "fonts.h" +#include + +const uint8_t Font24_Table [] PROGMEM = +{ + // @0 ' ' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @72 '!' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x03,0x80,0x00, // ### + 0x03,0x80,0x00, // ### + 0x03,0x80,0x00, // ### + 0x03,0x80,0x00, // ### + 0x03,0x80,0x00, // ### + 0x03,0x80,0x00, // ### + 0x03,0x80,0x00, // ### + 0x03,0x80,0x00, // ### + 0x03,0x80,0x00, // ### + 0x01,0x00,0x00, // # + 0x01,0x00,0x00, // # + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x03,0x80,0x00, // ### + 0x03,0x80,0x00, // ### + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @144 '"' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x0e,0x70,0x00, // ### ### + 0x0e,0x70,0x00, // ### ### + 0x0e,0x70,0x00, // ### ### + 0x04,0x20,0x00, // # # + 0x04,0x20,0x00, // # # + 0x04,0x20,0x00, // # # + 0x04,0x20,0x00, // # # + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @216 '#' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x06,0x60,0x00, // ## ## + 0x06,0x60,0x00, // ## ## + 0x06,0x60,0x00, // ## ## + 0x06,0x60,0x00, // ## ## + 0x06,0x60,0x00, // ## ## + 0x3f,0xf8,0x00, // ########### + 0x3f,0xf8,0x00, // ########### + 0x06,0x60,0x00, // ## ## + 0x0c,0xc0,0x00, // ## ## + 0x3f,0xf8,0x00, // ########### + 0x3f,0xf8,0x00, // ########### + 0x0c,0xc0,0x00, // ## ## + 0x0c,0xc0,0x00, // ## ## + 0x0c,0xc0,0x00, // ## ## + 0x0c,0xc0,0x00, // ## ## + 0x0c,0xc0,0x00, // ## ## + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @288 '$' (17 pixels wide) + 0x00,0x00,0x00, // + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x07,0xb0,0x00, // #### ## + 0x0f,0xf0,0x00, // ######## + 0x18,0x70,0x00, // ## ### + 0x18,0x70,0x00, // ## ### + 0x1c,0x00,0x00, // ### + 0x0f,0x80,0x00, // ##### + 0x07,0xe0,0x00, // ###### + 0x00,0xf0,0x00, // #### + 0x18,0x30,0x00, // ## ## + 0x1c,0x30,0x00, // ### ## + 0x1c,0x70,0x00, // ### ### + 0x1f,0xe0,0x00, // ######## + 0x1b,0xc0,0x00, // ## #### + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @360 '%' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x07,0x80,0x00, // #### + 0x0f,0xc0,0x00, // ###### + 0x1c,0xe0,0x00, // ### ### + 0x18,0x60,0x00, // ## ## + 0x18,0x60,0x00, // ## ## + 0x1c,0xe0,0x00, // ### ### + 0x0f,0xf8,0x00, // ######### + 0x07,0xe0,0x00, // ###### + 0x1f,0xf0,0x00, // ######### + 0x07,0x38,0x00, // ### ### + 0x06,0x18,0x00, // ## ## + 0x06,0x18,0x00, // ## ## + 0x07,0x38,0x00, // ### ### + 0x03,0xf0,0x00, // ###### + 0x01,0xe0,0x00, // #### + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @432 '&' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x03,0xf0,0x00, // ###### + 0x07,0xf0,0x00, // ####### + 0x0c,0x60,0x00, // ## ## + 0x0c,0x00,0x00, // ## + 0x0c,0x00,0x00, // ## + 0x06,0x00,0x00, // ## + 0x07,0x00,0x00, // ### + 0x0f,0x9c,0x00, // ##### ### + 0x1d,0xfc,0x00, // ### ####### + 0x18,0xf0,0x00, // ## #### + 0x18,0x70,0x00, // ## ### + 0x0f,0xfc,0x00, // ########## + 0x07,0xdc,0x00, // ##### ### + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @504 ''' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x03,0x80,0x00, // ### + 0x03,0x80,0x00, // ### + 0x03,0x80,0x00, // ### + 0x01,0x00,0x00, // # + 0x01,0x00,0x00, // # + 0x01,0x00,0x00, // # + 0x01,0x00,0x00, // # + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @576 '(' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x18,0x00, // ## + 0x00,0x38,0x00, // ### + 0x00,0x70,0x00, // ### + 0x00,0xf0,0x00, // #### + 0x00,0xe0,0x00, // ### + 0x00,0xe0,0x00, // ### + 0x01,0xc0,0x00, // ### + 0x01,0xc0,0x00, // ### + 0x01,0xc0,0x00, // ### + 0x01,0xc0,0x00, // ### + 0x01,0xc0,0x00, // ### + 0x01,0xc0,0x00, // ### + 0x00,0xe0,0x00, // ### + 0x00,0xe0,0x00, // ### + 0x00,0x70,0x00, // ### + 0x00,0x70,0x00, // ### + 0x00,0x38,0x00, // ### + 0x00,0x18,0x00, // ## + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @648 ')' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x18,0x00,0x00, // ## + 0x1c,0x00,0x00, // ### + 0x0e,0x00,0x00, // ### + 0x0e,0x00,0x00, // ### + 0x07,0x00,0x00, // ### + 0x07,0x00,0x00, // ### + 0x03,0x80,0x00, // ### + 0x03,0x80,0x00, // ### + 0x03,0x80,0x00, // ### + 0x03,0x80,0x00, // ### + 0x03,0x80,0x00, // ### + 0x03,0x80,0x00, // ### + 0x07,0x00,0x00, // ### + 0x07,0x00,0x00, // ### + 0x0f,0x00,0x00, // #### + 0x0e,0x00,0x00, // ### + 0x1c,0x00,0x00, // ### + 0x18,0x00,0x00, // ## + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @720 '*' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x1d,0xb8,0x00, // ### ## ### + 0x1f,0xf8,0x00, // ########## + 0x07,0xe0,0x00, // ###### + 0x03,0xc0,0x00, // #### + 0x03,0xc0,0x00, // #### + 0x06,0x60,0x00, // ## ## + 0x06,0x60,0x00, // ## ## + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @792 '+' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x3f,0xfc,0x00, // ############ + 0x3f,0xfc,0x00, // ############ + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @864 ',' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0xe0,0x00, // ### + 0x00,0xc0,0x00, // ## + 0x01,0xc0,0x00, // ### + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x03,0x00,0x00, // ## + 0x03,0x00,0x00, // ## + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @936 '-' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x1f,0xf8,0x00, // ########## + 0x1f,0xf8,0x00, // ########## + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @1008 '.' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x03,0xc0,0x00, // #### + 0x03,0xc0,0x00, // #### + 0x03,0xc0,0x00, // #### + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @1080 '/' (17 pixels wide) + 0x00,0x18,0x00, // ## + 0x00,0x18,0x00, // ## + 0x00,0x38,0x00, // ### + 0x00,0x30,0x00, // ## + 0x00,0x70,0x00, // ### + 0x00,0x60,0x00, // ## + 0x00,0x60,0x00, // ## + 0x00,0xc0,0x00, // ## + 0x00,0xc0,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x03,0x00,0x00, // ## + 0x03,0x00,0x00, // ## + 0x06,0x00,0x00, // ## + 0x06,0x00,0x00, // ## + 0x0e,0x00,0x00, // ### + 0x0c,0x00,0x00, // ## + 0x1c,0x00,0x00, // ### + 0x18,0x00,0x00, // ## + 0x18,0x00,0x00, // ## + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @1152 '0' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x03,0xc0,0x00, // #### + 0x07,0xe0,0x00, // ###### + 0x0c,0x30,0x00, // ## ## + 0x0c,0x30,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x0c,0x30,0x00, // ## ## + 0x0c,0x30,0x00, // ## ## + 0x07,0xe0,0x00, // ###### + 0x03,0xc0,0x00, // #### + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @1224 '1' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x80,0x00, // # + 0x07,0x80,0x00, // #### + 0x1f,0x80,0x00, // ###### + 0x1d,0x80,0x00, // ### ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x1f,0xf8,0x00, // ########## + 0x1f,0xf8,0x00, // ########## + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @1296 '2' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x07,0xc0,0x00, // ##### + 0x1f,0xf0,0x00, // ######### + 0x38,0x30,0x00, // ### ## + 0x30,0x18,0x00, // ## ## + 0x30,0x18,0x00, // ## ## + 0x00,0x18,0x00, // ## + 0x00,0x30,0x00, // ## + 0x00,0x60,0x00, // ## + 0x01,0xc0,0x00, // ### + 0x03,0x80,0x00, // ### + 0x06,0x00,0x00, // ## + 0x0c,0x00,0x00, // ## + 0x18,0x00,0x00, // ## + 0x3f,0xf8,0x00, // ########### + 0x3f,0xf8,0x00, // ########### + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @1368 '3' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x03,0xc0,0x00, // #### + 0x0f,0xe0,0x00, // ####### + 0x0c,0x70,0x00, // ## ### + 0x00,0x30,0x00, // ## + 0x00,0x30,0x00, // ## + 0x00,0x60,0x00, // ## + 0x03,0xc0,0x00, // #### + 0x03,0xe0,0x00, // ##### + 0x00,0x70,0x00, // ### + 0x00,0x18,0x00, // ## + 0x00,0x18,0x00, // ## + 0x00,0x18,0x00, // ## + 0x18,0x38,0x00, // ## ### + 0x1f,0xf0,0x00, // ######### + 0x0f,0xc0,0x00, // ###### + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @1440 '4' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0xe0,0x00, // ### + 0x01,0xe0,0x00, // #### + 0x01,0xe0,0x00, // #### + 0x03,0x60,0x00, // ## ## + 0x06,0x60,0x00, // ## ## + 0x06,0x60,0x00, // ## ## + 0x0c,0x60,0x00, // ## ## + 0x0c,0x60,0x00, // ## ## + 0x18,0x60,0x00, // ## ## + 0x30,0x60,0x00, // ## ## + 0x3f,0xf8,0x00, // ########### + 0x3f,0xf8,0x00, // ########### + 0x00,0x60,0x00, // ## + 0x03,0xf8,0x00, // ####### + 0x03,0xf8,0x00, // ####### + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @1512 '5' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x1f,0xf0,0x00, // ######### + 0x1f,0xf0,0x00, // ######### + 0x18,0x00,0x00, // ## + 0x18,0x00,0x00, // ## + 0x18,0x00,0x00, // ## + 0x1b,0xc0,0x00, // ## #### + 0x1f,0xf0,0x00, // ######### + 0x1c,0x30,0x00, // ### ## + 0x00,0x18,0x00, // ## + 0x00,0x18,0x00, // ## + 0x00,0x18,0x00, // ## + 0x00,0x18,0x00, // ## + 0x30,0x30,0x00, // ## ## + 0x3f,0xf0,0x00, // ########## + 0x0f,0xc0,0x00, // ###### + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @1584 '6' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0xf8,0x00, // ##### + 0x03,0xf8,0x00, // ####### + 0x07,0x00,0x00, // ### + 0x0e,0x00,0x00, // ### + 0x0c,0x00,0x00, // ## + 0x18,0x00,0x00, // ## + 0x1b,0xc0,0x00, // ## #### + 0x1f,0xf0,0x00, // ######### + 0x1c,0x30,0x00, // ### ## + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x0c,0x38,0x00, // ## ### + 0x0f,0xf0,0x00, // ######## + 0x03,0xe0,0x00, // ##### + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @1656 '7' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x1f,0xf8,0x00, // ########## + 0x1f,0xf8,0x00, // ########## + 0x18,0x18,0x00, // ## ## + 0x18,0x38,0x00, // ## ### + 0x00,0x30,0x00, // ## + 0x00,0x30,0x00, // ## + 0x00,0x70,0x00, // ### + 0x00,0x60,0x00, // ## + 0x00,0x60,0x00, // ## + 0x00,0xe0,0x00, // ### + 0x00,0xc0,0x00, // ## + 0x00,0xc0,0x00, // ## + 0x01,0xc0,0x00, // ### + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @1728 '8' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x07,0xe0,0x00, // ###### + 0x0f,0xf0,0x00, // ######## + 0x1c,0x38,0x00, // ### ### + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x0c,0x30,0x00, // ## ## + 0x07,0xe0,0x00, // ###### + 0x07,0xe0,0x00, // ###### + 0x0c,0x30,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x1c,0x38,0x00, // ### ### + 0x0f,0xf0,0x00, // ######## + 0x07,0xe0,0x00, // ###### + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @1800 '9' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x07,0xc0,0x00, // ##### + 0x0f,0xf0,0x00, // ######## + 0x1c,0x30,0x00, // ### ## + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x0c,0x38,0x00, // ## ### + 0x0f,0xf8,0x00, // ######### + 0x03,0xd8,0x00, // #### ## + 0x00,0x18,0x00, // ## + 0x00,0x30,0x00, // ## + 0x00,0x70,0x00, // ### + 0x00,0xe0,0x00, // ### + 0x1f,0xc0,0x00, // ####### + 0x1f,0x00,0x00, // ##### + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @1872 ':' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x03,0xc0,0x00, // #### + 0x03,0xc0,0x00, // #### + 0x03,0xc0,0x00, // #### + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x03,0xc0,0x00, // #### + 0x03,0xc0,0x00, // #### + 0x03,0xc0,0x00, // #### + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @1944 ';' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0xf0,0x00, // #### + 0x00,0xf0,0x00, // #### + 0x00,0xf0,0x00, // #### + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0xe0,0x00, // ### + 0x01,0xc0,0x00, // ### + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x03,0x00,0x00, // ## + 0x02,0x00,0x00, // # + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @2016 '<' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x1c,0x00, // ### + 0x00,0x3c,0x00, // #### + 0x00,0xf0,0x00, // #### + 0x03,0xc0,0x00, // #### + 0x0f,0x00,0x00, // #### + 0x3c,0x00,0x00, // #### + 0xf0,0x00,0x00, //#### + 0x3c,0x00,0x00, // #### + 0x0f,0x00,0x00, // #### + 0x03,0xc0,0x00, // #### + 0x00,0xf0,0x00, // #### + 0x00,0x3c,0x00, // #### + 0x00,0x1c,0x00, // ### + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @2088 '=' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x7f,0xfc,0x00, // ############# + 0x7f,0xfc,0x00, // ############# + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x7f,0xfc,0x00, // ############# + 0x7f,0xfc,0x00, // ############# + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @2160 '>' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x70,0x00,0x00, // ### + 0x78,0x00,0x00, // #### + 0x1e,0x00,0x00, // #### + 0x07,0x80,0x00, // #### + 0x01,0xe0,0x00, // #### + 0x00,0x78,0x00, // #### + 0x00,0x1e,0x00, // #### + 0x00,0x78,0x00, // #### + 0x01,0xe0,0x00, // #### + 0x07,0x80,0x00, // #### + 0x1e,0x00,0x00, // #### + 0x78,0x00,0x00, // #### + 0x70,0x00,0x00, // ### + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @2232 '?' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x07,0xc0,0x00, // ##### + 0x0f,0xe0,0x00, // ####### + 0x18,0x70,0x00, // ## ### + 0x18,0x30,0x00, // ## ## + 0x18,0x30,0x00, // ## ## + 0x00,0x70,0x00, // ### + 0x00,0xe0,0x00, // ### + 0x03,0xc0,0x00, // #### + 0x03,0x80,0x00, // ### + 0x03,0x00,0x00, // ## + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x07,0x00,0x00, // ### + 0x07,0x00,0x00, // ### + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @2304 '@' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x03,0xe0,0x00, // ##### + 0x07,0xf0,0x00, // ####### + 0x0e,0x38,0x00, // ### ### + 0x0c,0x18,0x00, // ## ## + 0x18,0x78,0x00, // ## #### + 0x18,0xf8,0x00, // ## ##### + 0x19,0xd8,0x00, // ## ### ## + 0x19,0x98,0x00, // ## ## ## + 0x19,0x98,0x00, // ## ## ## + 0x19,0x98,0x00, // ## ## ## + 0x18,0xf8,0x00, // ## ##### + 0x18,0x78,0x00, // ## #### + 0x18,0x00,0x00, // ## + 0x0c,0x00,0x00, // ## + 0x0e,0x18,0x00, // ### ## + 0x07,0xf8,0x00, // ######## + 0x03,0xe0,0x00, // ##### + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @2376 'A' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x1f,0x80,0x00, // ###### + 0x1f,0xc0,0x00, // ####### + 0x01,0xc0,0x00, // ### + 0x03,0x60,0x00, // ## ## + 0x03,0x60,0x00, // ## ## + 0x06,0x30,0x00, // ## ## + 0x06,0x30,0x00, // ## ## + 0x0c,0x30,0x00, // ## ## + 0x0f,0xf8,0x00, // ######### + 0x1f,0xf8,0x00, // ########## + 0x18,0x0c,0x00, // ## ## + 0x30,0x0c,0x00, // ## ## + 0xfc,0x7f,0x00, //###### ####### + 0xfc,0x7f,0x00, //###### ####### + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @2448 'B' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x7f,0xe0,0x00, // ########## + 0x7f,0xf0,0x00, // ########### + 0x18,0x38,0x00, // ## ### + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x18,0x38,0x00, // ## ### + 0x1f,0xf0,0x00, // ######### + 0x1f,0xf8,0x00, // ########## + 0x18,0x1c,0x00, // ## ### + 0x18,0x0c,0x00, // ## ## + 0x18,0x0c,0x00, // ## ## + 0x18,0x0c,0x00, // ## ## + 0x7f,0xf8,0x00, // ############ + 0x7f,0xf0,0x00, // ########### + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @2520 'C' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x03,0xec,0x00, // ##### ## + 0x0f,0xfc,0x00, // ########## + 0x1c,0x1c,0x00, // ### ### + 0x18,0x0c,0x00, // ## ## + 0x30,0x0c,0x00, // ## ## + 0x30,0x00,0x00, // ## + 0x30,0x00,0x00, // ## + 0x30,0x00,0x00, // ## + 0x30,0x00,0x00, // ## + 0x30,0x00,0x00, // ## + 0x18,0x0c,0x00, // ## ## + 0x1c,0x1c,0x00, // ### ### + 0x0f,0xf8,0x00, // ######### + 0x03,0xf0,0x00, // ###### + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @2592 'D' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x7f,0xc0,0x00, // ######### + 0x7f,0xf0,0x00, // ########### + 0x18,0x38,0x00, // ## ### + 0x18,0x18,0x00, // ## ## + 0x18,0x0c,0x00, // ## ## + 0x18,0x0c,0x00, // ## ## + 0x18,0x0c,0x00, // ## ## + 0x18,0x0c,0x00, // ## ## + 0x18,0x0c,0x00, // ## ## + 0x18,0x0c,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x18,0x38,0x00, // ## ### + 0x7f,0xf0,0x00, // ########### + 0x7f,0xe0,0x00, // ########## + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @2664 'E' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x7f,0xf8,0x00, // ############ + 0x7f,0xf8,0x00, // ############ + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x19,0x98,0x00, // ## ## ## + 0x19,0x80,0x00, // ## ## + 0x1f,0x80,0x00, // ###### + 0x1f,0x80,0x00, // ###### + 0x19,0x80,0x00, // ## ## + 0x19,0x98,0x00, // ## ## ## + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x7f,0xf8,0x00, // ############ + 0x7f,0xf8,0x00, // ############ + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @2736 'F' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x3f,0xfc,0x00, // ############ + 0x3f,0xfc,0x00, // ############ + 0x0c,0x0c,0x00, // ## ## + 0x0c,0x0c,0x00, // ## ## + 0x0c,0xcc,0x00, // ## ## ## + 0x0c,0xc0,0x00, // ## ## + 0x0f,0xc0,0x00, // ###### + 0x0f,0xc0,0x00, // ###### + 0x0c,0xc0,0x00, // ## ## + 0x0c,0xc0,0x00, // ## ## + 0x0c,0x00,0x00, // ## + 0x0c,0x00,0x00, // ## + 0x3f,0xc0,0x00, // ######## + 0x3f,0xc0,0x00, // ######## + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @2808 'G' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x03,0xec,0x00, // ##### ## + 0x0f,0xfc,0x00, // ########## + 0x1c,0x1c,0x00, // ### ### + 0x18,0x0c,0x00, // ## ## + 0x30,0x0c,0x00, // ## ## + 0x30,0x00,0x00, // ## + 0x30,0x00,0x00, // ## + 0x30,0xfe,0x00, // ## ####### + 0x30,0xfe,0x00, // ## ####### + 0x30,0x0c,0x00, // ## ## + 0x38,0x0c,0x00, // ### ## + 0x1c,0x1c,0x00, // ### ### + 0x0f,0xfc,0x00, // ########## + 0x03,0xf0,0x00, // ###### + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @2880 'H' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x7e,0x7e,0x00, // ###### ###### + 0x7e,0x7e,0x00, // ###### ###### + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x1f,0xf8,0x00, // ########## + 0x1f,0xf8,0x00, // ########## + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x7e,0x7e,0x00, // ###### ###### + 0x7e,0x7e,0x00, // ###### ###### + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @2952 'I' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x1f,0xf8,0x00, // ########## + 0x1f,0xf8,0x00, // ########## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x1f,0xf8,0x00, // ########## + 0x1f,0xf8,0x00, // ########## + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @3024 'J' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x07,0xfe,0x00, // ########## + 0x07,0xfe,0x00, // ########## + 0x00,0x30,0x00, // ## + 0x00,0x30,0x00, // ## + 0x00,0x30,0x00, // ## + 0x00,0x30,0x00, // ## + 0x00,0x30,0x00, // ## + 0x30,0x30,0x00, // ## ## + 0x30,0x30,0x00, // ## ## + 0x30,0x30,0x00, // ## ## + 0x30,0x30,0x00, // ## ## + 0x30,0x60,0x00, // ## ## + 0x3f,0xe0,0x00, // ######### + 0x0f,0x80,0x00, // ##### + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @3096 'K' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x7f,0x3e,0x00, // ####### ##### + 0x7f,0x3e,0x00, // ####### ##### + 0x18,0x30,0x00, // ## ## + 0x18,0x60,0x00, // ## ## + 0x18,0xc0,0x00, // ## ## + 0x19,0x80,0x00, // ## ## + 0x1b,0x80,0x00, // ## ### + 0x1f,0xc0,0x00, // ####### + 0x1c,0xe0,0x00, // ### ### + 0x18,0x70,0x00, // ## ### + 0x18,0x30,0x00, // ## ## + 0x18,0x38,0x00, // ## ### + 0x7f,0x1f,0x00, // ####### ##### + 0x7f,0x1f,0x00, // ####### ##### + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @3168 'L' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x7f,0x80,0x00, // ######## + 0x7f,0x80,0x00, // ######## + 0x0c,0x00,0x00, // ## + 0x0c,0x00,0x00, // ## + 0x0c,0x00,0x00, // ## + 0x0c,0x00,0x00, // ## + 0x0c,0x00,0x00, // ## + 0x0c,0x00,0x00, // ## + 0x0c,0x0c,0x00, // ## ## + 0x0c,0x0c,0x00, // ## ## + 0x0c,0x0c,0x00, // ## ## + 0x0c,0x0c,0x00, // ## ## + 0x7f,0xfc,0x00, // ############# + 0x7f,0xfc,0x00, // ############# + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @3240 'M' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0xf0,0x0f,0x00, //#### #### + 0xf8,0x1f,0x00, //##### ##### + 0x38,0x1c,0x00, // ### ### + 0x3c,0x3c,0x00, // #### #### + 0x3c,0x3c,0x00, // #### #### + 0x36,0x6c,0x00, // ## ## ## ## + 0x36,0x6c,0x00, // ## ## ## ## + 0x33,0xcc,0x00, // ## #### ## + 0x33,0xcc,0x00, // ## #### ## + 0x31,0x8c,0x00, // ## ## ## + 0x30,0x0c,0x00, // ## ## + 0x30,0x0c,0x00, // ## ## + 0xfe,0x7f,0x00, //####### ####### + 0xfe,0x7f,0x00, //####### ####### + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @3312 'N' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x78,0xfe,0x00, // #### ####### + 0x78,0xfe,0x00, // #### ####### + 0x1c,0x18,0x00, // ### ## + 0x1e,0x18,0x00, // #### ## + 0x1f,0x18,0x00, // ##### ## + 0x1b,0x18,0x00, // ## ## ## + 0x1b,0x98,0x00, // ## ### ## + 0x19,0xd8,0x00, // ## ### ## + 0x18,0xd8,0x00, // ## ## ## + 0x18,0xf8,0x00, // ## ##### + 0x18,0x78,0x00, // ## #### + 0x18,0x38,0x00, // ## ### + 0x7f,0x18,0x00, // ####### ## + 0x7f,0x18,0x00, // ####### ## + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @3384 'O' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x03,0xc0,0x00, // #### + 0x0f,0xf0,0x00, // ######## + 0x1c,0x38,0x00, // ### ### + 0x18,0x18,0x00, // ## ## + 0x38,0x1c,0x00, // ### ### + 0x30,0x0c,0x00, // ## ## + 0x30,0x0c,0x00, // ## ## + 0x30,0x0c,0x00, // ## ## + 0x30,0x0c,0x00, // ## ## + 0x38,0x1c,0x00, // ### ### + 0x18,0x18,0x00, // ## ## + 0x1c,0x38,0x00, // ### ### + 0x0f,0xf0,0x00, // ######## + 0x03,0xc0,0x00, // #### + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @3456 'P' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x3f,0xf0,0x00, // ########## + 0x3f,0xf8,0x00, // ########### + 0x0c,0x1c,0x00, // ## ### + 0x0c,0x0c,0x00, // ## ## + 0x0c,0x0c,0x00, // ## ## + 0x0c,0x0c,0x00, // ## ## + 0x0c,0x18,0x00, // ## ## + 0x0f,0xf8,0x00, // ######### + 0x0f,0xe0,0x00, // ####### + 0x0c,0x00,0x00, // ## + 0x0c,0x00,0x00, // ## + 0x0c,0x00,0x00, // ## + 0x3f,0xc0,0x00, // ######## + 0x3f,0xc0,0x00, // ######## + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @3528 'Q' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x03,0xc0,0x00, // #### + 0x0f,0xf0,0x00, // ######## + 0x1c,0x38,0x00, // ### ### + 0x18,0x18,0x00, // ## ## + 0x38,0x1c,0x00, // ### ### + 0x30,0x0c,0x00, // ## ## + 0x30,0x0c,0x00, // ## ## + 0x30,0x0c,0x00, // ## ## + 0x30,0x0c,0x00, // ## ## + 0x38,0x1c,0x00, // ### ### + 0x18,0x18,0x00, // ## ## + 0x1c,0x38,0x00, // ### ### + 0x0f,0xf0,0x00, // ######## + 0x07,0xc0,0x00, // ##### + 0x07,0xcc,0x00, // ##### ## + 0x0f,0xfc,0x00, // ########## + 0x0c,0x38,0x00, // ## ### + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @3600 'R' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x7f,0xe0,0x00, // ########## + 0x7f,0xf0,0x00, // ########### + 0x18,0x38,0x00, // ## ### + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x18,0x38,0x00, // ## ### + 0x1f,0xf0,0x00, // ######### + 0x1f,0xc0,0x00, // ####### + 0x18,0xe0,0x00, // ## ### + 0x18,0x70,0x00, // ## ### + 0x18,0x30,0x00, // ## ## + 0x18,0x38,0x00, // ## ### + 0x7f,0x1e,0x00, // ####### #### + 0x7f,0x0e,0x00, // ####### ### + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @3672 'S' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x07,0xd8,0x00, // ##### ## + 0x0f,0xf8,0x00, // ######### + 0x1c,0x38,0x00, // ### ### + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x1e,0x00,0x00, // #### + 0x0f,0xc0,0x00, // ###### + 0x03,0xf0,0x00, // ###### + 0x00,0x78,0x00, // #### + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x1c,0x38,0x00, // ### ### + 0x1f,0xf0,0x00, // ######### + 0x1b,0xe0,0x00, // ## ##### + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @3744 'T' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x3f,0xfc,0x00, // ############ + 0x3f,0xfc,0x00, // ############ + 0x31,0x8c,0x00, // ## ## ## + 0x31,0x8c,0x00, // ## ## ## + 0x31,0x8c,0x00, // ## ## ## + 0x31,0x8c,0x00, // ## ## ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x0f,0xf0,0x00, // ######## + 0x0f,0xf0,0x00, // ######## + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @3816 'U' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x7e,0x7e,0x00, // ###### ###### + 0x7e,0x7e,0x00, // ###### ###### + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x0c,0x30,0x00, // ## ## + 0x0f,0xf0,0x00, // ######## + 0x03,0xc0,0x00, // #### + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @3888 'V' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x7f,0x7f,0x00, // ####### ####### + 0x7f,0x7f,0x00, // ####### ####### + 0x18,0x0c,0x00, // ## ## + 0x0c,0x18,0x00, // ## ## + 0x0c,0x18,0x00, // ## ## + 0x0c,0x18,0x00, // ## ## + 0x06,0x30,0x00, // ## ## + 0x06,0x30,0x00, // ## ## + 0x03,0x60,0x00, // ## ## + 0x03,0x60,0x00, // ## ## + 0x03,0x60,0x00, // ## ## + 0x01,0xc0,0x00, // ### + 0x01,0xc0,0x00, // ### + 0x00,0x80,0x00, // # + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @3960 'W' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0xfe,0x3f,0x80, //####### ####### + 0xfe,0x3f,0x80, //####### ####### + 0x30,0x06,0x00, // ## ## + 0x30,0x06,0x00, // ## ## + 0x30,0x86,0x00, // ## # ## + 0x19,0xcc,0x00, // ## ### ## + 0x19,0xcc,0x00, // ## ### ## + 0x1b,0x6c,0x00, // ## ## ## ## + 0x1b,0x6c,0x00, // ## ## ## ## + 0x1e,0x7c,0x00, // #### ##### + 0x0e,0x38,0x00, // ### ### + 0x0e,0x38,0x00, // ### ### + 0x0c,0x18,0x00, // ## ## + 0x0c,0x18,0x00, // ## ## + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @4032 'X' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x7e,0x7e,0x00, // ###### ###### + 0x7e,0x7e,0x00, // ###### ###### + 0x18,0x18,0x00, // ## ## + 0x0c,0x30,0x00, // ## ## + 0x06,0x60,0x00, // ## ## + 0x03,0xc0,0x00, // #### + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x03,0xc0,0x00, // #### + 0x06,0x60,0x00, // ## ## + 0x0c,0x30,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x7e,0x7e,0x00, // ###### ###### + 0x7e,0x7e,0x00, // ###### ###### + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @4104 'Y' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x7c,0x7e,0x00, // ##### ###### + 0x7c,0x7e,0x00, // ##### ###### + 0x18,0x18,0x00, // ## ## + 0x0c,0x30,0x00, // ## ## + 0x06,0x60,0x00, // ## ## + 0x06,0x60,0x00, // ## ## + 0x03,0xc0,0x00, // #### + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x0f,0xf0,0x00, // ######## + 0x0f,0xf0,0x00, // ######## + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @4176 'Z' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x1f,0xf8,0x00, // ########## + 0x1f,0xf8,0x00, // ########## + 0x18,0x18,0x00, // ## ## + 0x18,0x30,0x00, // ## ## + 0x18,0x60,0x00, // ## ## + 0x18,0xc0,0x00, // ## ## + 0x01,0x80,0x00, // ## + 0x03,0x00,0x00, // ## + 0x06,0x18,0x00, // ## ## + 0x0c,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x30,0x18,0x00, // ## ## + 0x3f,0xf8,0x00, // ########### + 0x3f,0xf8,0x00, // ########### + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @4248 '[' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x01,0xf0,0x00, // ##### + 0x01,0xf0,0x00, // ##### + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0xf0,0x00, // ##### + 0x01,0xf0,0x00, // ##### + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @4320 '\' (17 pixels wide) + 0x18,0x00,0x00, // ## + 0x18,0x00,0x00, // ## + 0x1c,0x00,0x00, // ### + 0x0c,0x00,0x00, // ## + 0x0e,0x00,0x00, // ### + 0x06,0x00,0x00, // ## + 0x06,0x00,0x00, // ## + 0x03,0x00,0x00, // ## + 0x03,0x00,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x00,0xc0,0x00, // ## + 0x00,0xc0,0x00, // ## + 0x00,0x60,0x00, // ## + 0x00,0x60,0x00, // ## + 0x00,0x70,0x00, // ### + 0x00,0x30,0x00, // ## + 0x00,0x38,0x00, // ### + 0x00,0x18,0x00, // ## + 0x00,0x18,0x00, // ## + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @4392 ']' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x0f,0x80,0x00, // ##### + 0x0f,0x80,0x00, // ##### + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x0f,0x80,0x00, // ##### + 0x0f,0x80,0x00, // ##### + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @4464 '^' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x80,0x00, // # + 0x01,0xc0,0x00, // ### + 0x03,0xe0,0x00, // ##### + 0x07,0x70,0x00, // ### ### + 0x06,0x30,0x00, // ## ## + 0x0c,0x18,0x00, // ## ## + 0x18,0x0c,0x00, // ## ## + 0x10,0x04,0x00, // # # + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @4536 '_' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0xff,0xff,0x00, //################ + 0xff,0xff,0x00, //################ + + // @4608 '`' (17 pixels wide) + 0x00,0x00,0x00, // + 0x03,0x00,0x00, // ## + 0x03,0x80,0x00, // ### + 0x00,0xe0,0x00, // ### + 0x00,0x60,0x00, // ## + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @4680 'a' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x0f,0xc0,0x00, // ###### + 0x1f,0xe0,0x00, // ######## + 0x00,0x30,0x00, // ## + 0x00,0x30,0x00, // ## + 0x07,0xf0,0x00, // ####### + 0x1f,0xf0,0x00, // ######### + 0x38,0x30,0x00, // ### ## + 0x30,0x30,0x00, // ## ## + 0x30,0x70,0x00, // ## ### + 0x1f,0xfc,0x00, // ########### + 0x0f,0xbc,0x00, // ##### #### + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @4752 'b' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x78,0x00,0x00, // #### + 0x78,0x00,0x00, // #### + 0x18,0x00,0x00, // ## + 0x18,0x00,0x00, // ## + 0x1b,0xe0,0x00, // ## ##### + 0x1f,0xf8,0x00, // ########## + 0x1c,0x18,0x00, // ### ## + 0x18,0x0c,0x00, // ## ## + 0x18,0x0c,0x00, // ## ## + 0x18,0x0c,0x00, // ## ## + 0x18,0x0c,0x00, // ## ## + 0x18,0x0c,0x00, // ## ## + 0x1c,0x18,0x00, // ### ## + 0x7f,0xf8,0x00, // ############ + 0x7b,0xe0,0x00, // #### ##### + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @4824 'c' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x03,0xec,0x00, // ##### ## + 0x0f,0xfc,0x00, // ########## + 0x1c,0x1c,0x00, // ### ### + 0x38,0x0c,0x00, // ### ## + 0x30,0x0c,0x00, // ## ## + 0x30,0x00,0x00, // ## + 0x30,0x00,0x00, // ## + 0x38,0x0c,0x00, // ### ## + 0x1c,0x1c,0x00, // ### ### + 0x0f,0xf8,0x00, // ######### + 0x03,0xf0,0x00, // ###### + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @4896 'd' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x78,0x00, // #### + 0x00,0x78,0x00, // #### + 0x00,0x18,0x00, // ## + 0x00,0x18,0x00, // ## + 0x07,0xd8,0x00, // ##### ## + 0x1f,0xf8,0x00, // ########## + 0x18,0x38,0x00, // ## ### + 0x30,0x18,0x00, // ## ## + 0x30,0x18,0x00, // ## ## + 0x30,0x18,0x00, // ## ## + 0x30,0x18,0x00, // ## ## + 0x30,0x18,0x00, // ## ## + 0x18,0x38,0x00, // ## ### + 0x1f,0xfe,0x00, // ############ + 0x07,0xde,0x00, // ##### #### + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @4968 'e' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x07,0xe0,0x00, // ###### + 0x1f,0xf8,0x00, // ########## + 0x18,0x18,0x00, // ## ## + 0x30,0x0c,0x00, // ## ## + 0x3f,0xfc,0x00, // ############ + 0x3f,0xfc,0x00, // ############ + 0x30,0x00,0x00, // ## + 0x30,0x00,0x00, // ## + 0x18,0x0c,0x00, // ## ## + 0x1f,0xfc,0x00, // ########### + 0x07,0xf0,0x00, // ####### + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @5040 'f' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x01,0xfc,0x00, // ####### + 0x03,0xfc,0x00, // ######## + 0x06,0x00,0x00, // ## + 0x06,0x00,0x00, // ## + 0x3f,0xf8,0x00, // ########### + 0x3f,0xf8,0x00, // ########### + 0x06,0x00,0x00, // ## + 0x06,0x00,0x00, // ## + 0x06,0x00,0x00, // ## + 0x06,0x00,0x00, // ## + 0x06,0x00,0x00, // ## + 0x06,0x00,0x00, // ## + 0x06,0x00,0x00, // ## + 0x3f,0xf0,0x00, // ########## + 0x3f,0xf0,0x00, // ########## + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @5112 'g' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x07,0xde,0x00, // ##### #### + 0x1f,0xfe,0x00, // ############ + 0x18,0x38,0x00, // ## ### + 0x30,0x18,0x00, // ## ## + 0x30,0x18,0x00, // ## ## + 0x30,0x18,0x00, // ## ## + 0x30,0x18,0x00, // ## ## + 0x30,0x18,0x00, // ## ## + 0x18,0x38,0x00, // ## ### + 0x1f,0xf8,0x00, // ########## + 0x07,0xd8,0x00, // ##### ## + 0x00,0x18,0x00, // ## + 0x00,0x18,0x00, // ## + 0x00,0x38,0x00, // ### + 0x0f,0xf0,0x00, // ######## + 0x0f,0xc0,0x00, // ###### + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @5184 'h' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x78,0x00,0x00, // #### + 0x78,0x00,0x00, // #### + 0x18,0x00,0x00, // ## + 0x18,0x00,0x00, // ## + 0x1b,0xe0,0x00, // ## ##### + 0x1f,0xf0,0x00, // ######### + 0x1c,0x38,0x00, // ### ### + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x7e,0x7e,0x00, // ###### ###### + 0x7e,0x7e,0x00, // ###### ###### + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @5256 'i' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x1f,0x80,0x00, // ###### + 0x1f,0x80,0x00, // ###### + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x3f,0xfc,0x00, // ############ + 0x3f,0xfc,0x00, // ############ + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @5328 'j' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0xc0,0x00, // ## + 0x00,0xc0,0x00, // ## + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x1f,0xf0,0x00, // ######### + 0x1f,0xf0,0x00, // ######### + 0x00,0x30,0x00, // ## + 0x00,0x30,0x00, // ## + 0x00,0x30,0x00, // ## + 0x00,0x30,0x00, // ## + 0x00,0x30,0x00, // ## + 0x00,0x30,0x00, // ## + 0x00,0x30,0x00, // ## + 0x00,0x30,0x00, // ## + 0x00,0x30,0x00, // ## + 0x00,0x30,0x00, // ## + 0x00,0x30,0x00, // ## + 0x00,0x70,0x00, // ### + 0x1f,0xe0,0x00, // ######## + 0x1f,0x80,0x00, // ###### + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @5400 'k' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x3c,0x00,0x00, // #### + 0x3c,0x00,0x00, // #### + 0x0c,0x00,0x00, // ## + 0x0c,0x00,0x00, // ## + 0x0c,0xf8,0x00, // ## ##### + 0x0c,0xf8,0x00, // ## ##### + 0x0c,0xc0,0x00, // ## ## + 0x0d,0x80,0x00, // ## ## + 0x0f,0x80,0x00, // ##### + 0x0f,0x00,0x00, // #### + 0x0f,0x80,0x00, // ##### + 0x0d,0xc0,0x00, // ## ### + 0x0c,0xe0,0x00, // ## ### + 0x3c,0x7c,0x00, // #### ##### + 0x3c,0x7c,0x00, // #### ##### + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @5472 'l' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x1f,0x80,0x00, // ###### + 0x1f,0x80,0x00, // ###### + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x3f,0xfc,0x00, // ############ + 0x3f,0xfc,0x00, // ############ + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @5544 'm' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0xf7,0x78,0x00, //#### ### #### + 0xff,0xfc,0x00, //############## + 0x39,0xcc,0x00, // ### ### ## + 0x31,0x8c,0x00, // ## ## ## + 0x31,0x8c,0x00, // ## ## ## + 0x31,0x8c,0x00, // ## ## ## + 0x31,0x8c,0x00, // ## ## ## + 0x31,0x8c,0x00, // ## ## ## + 0x31,0x8c,0x00, // ## ## ## + 0xfd,0xef,0x00, //###### #### #### + 0xfd,0xef,0x00, //###### #### #### + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @5616 'n' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x7b,0xe0,0x00, // #### ##### + 0x7f,0xf0,0x00, // ########### + 0x1c,0x38,0x00, // ### ### + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x7e,0x7e,0x00, // ###### ###### + 0x7e,0x7e,0x00, // ###### ###### + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @5688 'o' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x03,0xc0,0x00, // #### + 0x0f,0xf0,0x00, // ######## + 0x1c,0x38,0x00, // ### ### + 0x38,0x1c,0x00, // ### ### + 0x30,0x0c,0x00, // ## ## + 0x30,0x0c,0x00, // ## ## + 0x30,0x0c,0x00, // ## ## + 0x38,0x1c,0x00, // ### ### + 0x1c,0x38,0x00, // ### ### + 0x0f,0xf0,0x00, // ######## + 0x03,0xc0,0x00, // #### + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @5760 'p' (17 pixels wide) + 0x00, 0x00, 0x00, // + 0x00, 0x00, 0x00, // + 0x00, 0x00, 0x00, // + 0x00, 0x00, 0x00, // + 0x00, 0x00, 0x00, // + 0x00, 0x00, 0x00, // + 0x7B, 0xE0, 0x00, // #### ##### + 0x7F, 0xF8, 0x00, // ############ + 0x1C, 0x18, 0x00, // ### ## + 0x18, 0x0C, 0x00, // ## ## + 0x18, 0x0C, 0x00, // ## ## + 0x18, 0x0C, 0x00, // ## ## + 0x18, 0x0C, 0x00, // ## ## + 0x18, 0x0C, 0x00, // ## ## + 0x1C, 0x18, 0x00, // ### ## + 0x1F, 0xF8, 0x00, // ########## + 0x1B, 0xE0, 0x00, // ## ##### + 0x18, 0x00, 0x00, // ## + 0x18, 0x00, 0x00, // ## + 0x18, 0x00, 0x00, // ## + 0x7F, 0x00, 0x00, // ####### + 0x7F, 0x00, 0x00, // ####### + 0x00, 0x00, 0x00, // + 0x00, 0x00, 0x00, // + + // @5832 'q' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x07,0xde,0x00, // ##### #### + 0x1f,0xfe,0x00, // ############ + 0x18,0x38,0x00, // ## ### + 0x30,0x18,0x00, // ## ## + 0x30,0x18,0x00, // ## ## + 0x30,0x18,0x00, // ## ## + 0x30,0x18,0x00, // ## ## + 0x30,0x18,0x00, // ## ## + 0x18,0x38,0x00, // ## ### + 0x1f,0xf8,0x00, // ########## + 0x07,0xd8,0x00, // ##### ## + 0x00,0x18,0x00, // ## + 0x00,0x18,0x00, // ## + 0x00,0x18,0x00, // ## + 0x00,0xfe,0x00, // ####### + 0x00,0xfe,0x00, // ####### + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @5904 'r' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x3e,0x78,0x00, // ##### #### + 0x3e,0xfc,0x00, // ##### ###### + 0x07,0xcc,0x00, // ##### ## + 0x07,0x00,0x00, // ### + 0x06,0x00,0x00, // ## + 0x06,0x00,0x00, // ## + 0x06,0x00,0x00, // ## + 0x06,0x00,0x00, // ## + 0x06,0x00,0x00, // ## + 0x3f,0xf0,0x00, // ########## + 0x3f,0xf0,0x00, // ########## + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @5976 's' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x07,0xf8,0x00, // ######## + 0x0f,0xf8,0x00, // ######### + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x1f,0x80,0x00, // ###### + 0x0f,0xf0,0x00, // ######## + 0x00,0xf8,0x00, // ##### + 0x18,0x18,0x00, // ## ## + 0x18,0x38,0x00, // ## ### + 0x1f,0xf0,0x00, // ######### + 0x1f,0xe0,0x00, // ######## + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @6048 't' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x0c,0x00,0x00, // ## + 0x0c,0x00,0x00, // ## + 0x0c,0x00,0x00, // ## + 0x0c,0x00,0x00, // ## + 0x3f,0xf0,0x00, // ########## + 0x3f,0xf0,0x00, // ########## + 0x0c,0x00,0x00, // ## + 0x0c,0x00,0x00, // ## + 0x0c,0x00,0x00, // ## + 0x0c,0x00,0x00, // ## + 0x0c,0x00,0x00, // ## + 0x0c,0x00,0x00, // ## + 0x0c,0x1c,0x00, // ## ### + 0x07,0xfc,0x00, // ######### + 0x03,0xf0,0x00, // ###### + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @6120 'u' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x78,0x78,0x00, // #### #### + 0x78,0x78,0x00, // #### #### + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x18,0x38,0x00, // ## ### + 0x0f,0xfe,0x00, // ########### + 0x07,0xde,0x00, // ##### #### + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @6192 'v' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x7c,0x3e,0x00, // ##### ##### + 0x7c,0x3e,0x00, // ##### ##### + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x0c,0x30,0x00, // ## ## + 0x0c,0x30,0x00, // ## ## + 0x06,0x60,0x00, // ## ## + 0x06,0x60,0x00, // ## ## + 0x07,0xe0,0x00, // ###### + 0x03,0xc0,0x00, // #### + 0x03,0xc0,0x00, // #### + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @6264 'w' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x78,0x3c,0x00, // #### #### + 0x78,0x3c,0x00, // #### #### + 0x31,0x18,0x00, // ## # ## + 0x33,0x98,0x00, // ## ### ## + 0x33,0x98,0x00, // ## ### ## + 0x1a,0xb0,0x00, // ## # # ## + 0x1e,0xf0,0x00, // #### #### + 0x1e,0xf0,0x00, // #### #### + 0x1c,0x60,0x00, // ### ## + 0x0c,0x60,0x00, // ## ## + 0x0c,0x60,0x00, // ## ## + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @6336 'x' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x3e,0x7c,0x00, // ##### ##### + 0x3e,0x7c,0x00, // ##### ##### + 0x0c,0x30,0x00, // ## ## + 0x06,0x60,0x00, // ## ## + 0x03,0xc0,0x00, // #### + 0x01,0x80,0x00, // ## + 0x03,0xc0,0x00, // #### + 0x06,0x60,0x00, // ## ## + 0x0c,0x30,0x00, // ## ## + 0x3e,0x7c,0x00, // ##### ##### + 0x3e,0x7c,0x00, // ##### ##### + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @6408 'y' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x7e,0x1f,0x00, // ###### ##### + 0x7e,0x1f,0x00, // ###### ##### + 0x18,0x0c,0x00, // ## ## + 0x0c,0x18,0x00, // ## ## + 0x0c,0x18,0x00, // ## ## + 0x06,0x30,0x00, // ## ## + 0x06,0x30,0x00, // ## ## + 0x03,0x60,0x00, // ## ## + 0x03,0xe0,0x00, // ##### + 0x01,0xc0,0x00, // ### + 0x00,0xc0,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x03,0x00,0x00, // ## + 0x3f,0xc0,0x00, // ######## + 0x3f,0xc0,0x00, // ######## + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @6480 'z' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x1f,0xf8,0x00, // ########## + 0x1f,0xf8,0x00, // ########## + 0x18,0x30,0x00, // ## ## + 0x18,0x60,0x00, // ## ## + 0x00,0xc0,0x00, // ## + 0x01,0x80,0x00, // ## + 0x03,0x00,0x00, // ## + 0x06,0x18,0x00, // ## ## + 0x0c,0x18,0x00, // ## ## + 0x1f,0xf8,0x00, // ########## + 0x1f,0xf8,0x00, // ########## + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @6552 '{' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0xe0,0x00, // ### + 0x01,0xe0,0x00, // #### + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x03,0x80,0x00, // ### + 0x07,0x00,0x00, // ### + 0x03,0x80,0x00, // ### + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0xe0,0x00, // #### + 0x00,0xe0,0x00, // ### + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @6624 '|' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @6696 '}' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x07,0x00,0x00, // ### + 0x07,0x80,0x00, // #### + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0xc0,0x00, // ### + 0x00,0xe0,0x00, // ### + 0x01,0xc0,0x00, // ### + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x01,0x80,0x00, // ## + 0x07,0x80,0x00, // #### + 0x07,0x00,0x00, // ### + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @6768 '~' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x0e,0x00,0x00, // ### + 0x1f,0x18,0x00, // ##### ## + 0x3b,0xb8,0x00, // ### ### ### + 0x31,0xf0,0x00, // ## ##### + 0x00,0xe0,0x00, // ### + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @0 ' ' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @0 ' ' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x0c,0x30,0x00, // ## ## + 0x0c,0x30,0x00, // ## ## + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x0f,0xc0,0x00, // ###### + 0x1f,0xe0,0x00, // ######## + 0x00,0x30,0x00, // ## + 0x00,0x30,0x00, // ## + 0x07,0xf0,0x00, // ####### + 0x1f,0xf0,0x00, // ######### + 0x38,0x30,0x00, // ### ## + 0x30,0x30,0x00, // ## ## + 0x30,0x70,0x00, // ## ### + 0x1f,0xfc,0x00, // ########### + 0x0f,0xbc,0x00, // ##### #### + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @0 ' ' (17 pixels wide) + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x00,0x00,0x00, // + 0x1f,0x80,0x00, // ###### + 0x1f,0xc0,0x00, // ####### + 0x01,0xc0,0x00, // ### + 0x03,0x60,0x00, // ## ## + 0x03,0x60,0x00, // ## ## + 0x06,0x30,0x00, // ## ## + 0x06,0x30,0x00, // ## ## + 0x0c,0x30,0x00, // ## ## + 0x0f,0xf8,0x00, // ######### + 0x1f,0xf8,0x00, // ########## + 0x18,0x0c,0x00, // ## ## + 0x30,0x0c,0x00, // ## ## + 0xfc,0x7f,0x00, //###### ####### + 0xfc,0x7f,0x00, //###### ####### + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @0 ' ' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x0c,0x30,0x00, // ## ## + 0x0c,0x30,0x00, // ## ## + 0x00,0x00,0x00, // + 0x03,0xc0,0x00, // #### + 0x0f,0xf0,0x00, // ######## + 0x1c,0x38,0x00, // ### ### + 0x38,0x1c,0x00, // ### ### + 0x30,0x0c,0x00, // ## ## + 0x30,0x0c,0x00, // ## ## + 0x30,0x0c,0x00, // ## ## + 0x38,0x1c,0x00, // ### ### + 0x1c,0x38,0x00, // ### ### + 0x0f,0xf0,0x00, // ######## + 0x03,0xc0,0x00, // #### + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @0 ' ' (17 pixels wide) + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x00,0x00,0x00, // + 0x03,0xc0,0x00, // #### + 0x0f,0xf0,0x00, // ######## + 0x1c,0x38,0x00, // ### ### + 0x18,0x18,0x00, // ## ## + 0x38,0x1c,0x00, // ### ### + 0x30,0x0c,0x00, // ## ## + 0x30,0x0c,0x00, // ## ## + 0x30,0x0c,0x00, // ## ## + 0x30,0x0c,0x00, // ## ## + 0x38,0x1c,0x00, // ### ### + 0x18,0x18,0x00, // ## ## + 0x1c,0x38,0x00, // ### ### + 0x0f,0xf0,0x00, // ######## + 0x03,0xc0,0x00, // #### + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @0 ' ' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x30,0x30,0x00, // ## ## + 0x30,0x30,0x00, // ## ## + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x78,0x78,0x00, // #### #### + 0x78,0x78,0x00, // #### #### + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x18,0x38,0x00, // ## ### + 0x0f,0xfe,0x00, // ########### + 0x07,0xde,0x00, // ##### #### + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @0 ' ' (17 pixels wide) + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x00,0x00,0x00, // + 0x7e,0x7e,0x00, // ###### ###### + 0x7e,0x7e,0x00, // ###### ###### + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x0c,0x30,0x00, // ## ## + 0x0f,0xf0,0x00, // ######## + 0x03,0xc0,0x00, // #### + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + + // @0 ' ' (17 pixels wide) + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x00,0x00,0x00, // + 0x0f,0x80,0x00, // ##### + 0x1f,0xe0,0x00, // ######## + 0x18,0x70,0x00, // ## ### + 0x18,0x30,0x00, // ## ## + 0x18,0x30,0x00, // ## ## + 0x18,0x60,0x00, // ## ## + 0x1f,0xc0,0x00, // ####### + 0x1f,0x00,0x00, // ##### + 0x19,0xc0,0x00, // ## ### + 0x18,0x70,0x00, // ## ### + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x18,0x18,0x00, // ## ## + 0x18,0x30,0x00, // ## ## + 0x18,0xe0,0x00, // ## ### + 0x1f,0x80,0x00, // ###### + 0x1e,0x00,0x00, // #### + 0x18,0x00,0x00, // ## + 0x18,0x00,0x00, // ## + 0x00,0x00,0x00, // + 0x00,0x00,0x00 // +}; + +sFONT Font24 = { + Font24_Table, + 17, /* Width */ + 24, /* Height */ +}; + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/font8.c b/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/font8.c new file mode 100644 index 000000000..37104ea74 --- /dev/null +++ b/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/font8.c @@ -0,0 +1,1085 @@ +/** + ****************************************************************************** + * @file Font8.c + * @author MCD Application Team + * @version V1.0.0 + * @date 18-February-2014 + * @brief This file provides text Font8 for STM32xx-EVAL's LCD driver. + ****************************************************************************** + * @attention + * + *

© COPYRIGHT(c) 2014 STMicroelectronics

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of STMicroelectronics nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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. + * + ****************************************************************************** + */ + +/* Includes ------------------------------------------------------------------*/ +#include "fonts.h" +#include + +// +// Font data for Courier New 12pt +// + +const uint8_t Font8_Table[] PROGMEM = +{ + // @0 ' ' (5 pixels wide) + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + + // @8 '!' (5 pixels wide) + 0x20, // # + 0x20, // # + 0x20, // # + 0x20, // # + 0x00, // + 0x20, // # + 0x00, // + 0x00, // + + // @16 '"' (5 pixels wide) + 0x50, // # # + 0x50, // # # + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + + // @24 '#' (5 pixels wide) + 0x28, // # # + 0x50, // # # + 0xf8, //##### + 0x50, // # # + 0xf8, //##### + 0x50, // # # + 0xa0, //# # + 0x00, // + + // @32 '$' (5 pixels wide) + 0x20, // # + 0x30, // ## + 0x60, // ## + 0x30, // ## + 0x10, // # + 0x60, // ## + 0x20, // # + 0x00, // + + // @40 '%' (5 pixels wide) + 0x20, // # + 0x20, // # + 0x18, // ## + 0x60, // ## + 0x10, // # + 0x10, // # + 0x00, // + 0x00, // + + // @48 '&' (5 pixels wide) + 0x00, // + 0x38, // ### + 0x20, // # + 0x60, // ## + 0x50, // # # + 0x78, // #### + 0x00, // + 0x00, // + + // @56 ''' (5 pixels wide) + 0x20, // # + 0x20, // # + 0x20, // # + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + + // @64 '(' (5 pixels wide) + 0x10, // # + 0x20, // # + 0x20, // # + 0x20, // # + 0x20, // # + 0x20, // # + 0x10, // # + 0x00, // + + // @72 ')' (5 pixels wide) + 0x40, // # + 0x20, // # + 0x20, // # + 0x20, // # + 0x20, // # + 0x20, // # + 0x40, // # + 0x00, // + + // @80 '*' (5 pixels wide) + 0x20, // # + 0x70, // ### + 0x20, // # + 0x50, // # # + 0x00, // + 0x00, // + 0x00, // + 0x00, // + + // @88 '+' (5 pixels wide) + 0x00, // + 0x20, // # + 0x20, // # + 0xf8, //##### + 0x20, // # + 0x20, // # + 0x00, // + 0x00, // + + // @96 ',' (5 pixels wide) + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x10, // # + 0x20, // # + 0x20, // # + 0x00, // + + // @104 '-' (5 pixels wide) + 0x00, // + 0x00, // + 0x00, // + 0x70, // ### + 0x00, // + 0x00, // + 0x00, // + 0x00, // + + // @112 '.' (5 pixels wide) + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x20, // # + 0x00, // + 0x00, // + + // @120 '/' (5 pixels wide) + 0x10, // # + 0x20, // # + 0x20, // # + 0x20, // # + 0x40, // # + 0x40, // # + 0x80, //# + 0x00, // + + // @128 '0' (5 pixels wide) + 0x20, // # + 0x50, // # # + 0x50, // # # + 0x50, // # # + 0x50, // # # + 0x20, // # + 0x00, // + 0x00, // + + // @136 '1' (5 pixels wide) + 0x60, // ## + 0x20, // # + 0x20, // # + 0x20, // # + 0x20, // # + 0xf8, //##### + 0x00, // + 0x00, // + + // @144 '2' (5 pixels wide) + 0x20, // # + 0x50, // # # + 0x20, // # + 0x20, // # + 0x40, // # + 0x70, // ### + 0x00, // + 0x00, // + + // @152 '3' (5 pixels wide) + 0x20, // # + 0x50, // # # + 0x10, // # + 0x20, // # + 0x10, // # + 0x60, // ## + 0x00, // + 0x00, // + + // @160 '4' (5 pixels wide) + 0x10, // # + 0x30, // ## + 0x50, // # # + 0x78, // #### + 0x10, // # + 0x38, // ### + 0x00, // + 0x00, // + + // @168 '5' (5 pixels wide) + 0x70, // ### + 0x40, // # + 0x60, // ## + 0x10, // # + 0x50, // # # + 0x20, // # + 0x00, // + 0x00, // + + // @176 '6' (5 pixels wide) + 0x30, // ## + 0x40, // # + 0x60, // ## + 0x50, // # # + 0x50, // # # + 0x60, // ## + 0x00, // + 0x00, // + + // @184 '7' (5 pixels wide) + 0x70, // ### + 0x50, // # # + 0x10, // # + 0x20, // # + 0x20, // # + 0x20, // # + 0x00, // + 0x00, // + + // @192 '8' (5 pixels wide) + 0x20, // # + 0x50, // # # + 0x20, // # + 0x50, // # # + 0x50, // # # + 0x20, // # + 0x00, // + 0x00, // + + // @200 '9' (5 pixels wide) + 0x30, // ## + 0x50, // # # + 0x50, // # # + 0x30, // ## + 0x10, // # + 0x60, // ## + 0x00, // + 0x00, // + + // @208 ':' (5 pixels wide) + 0x00, // + 0x00, // + 0x20, // # + 0x00, // + 0x00, // + 0x20, // # + 0x00, // + 0x00, // + + // @216 ';' (5 pixels wide) + 0x00, // + 0x00, // + 0x10, // # + 0x00, // + 0x10, // # + 0x20, // # + 0x00, // + 0x00, // + + // @224 '<' (5 pixels wide) + 0x00, // + 0x10, // # + 0x20, // # + 0xc0, //## + 0x20, // # + 0x10, // # + 0x00, // + 0x00, // + + // @232 '=' (5 pixels wide) + 0x00, // + 0x70, // ### + 0x00, // + 0x70, // ### + 0x00, // + 0x00, // + 0x00, // + 0x00, // + + // @240 '>' (5 pixels wide) + 0x00, // + 0x40, // # + 0x20, // # + 0x18, // ## + 0x20, // # + 0x40, // # + 0x00, // + 0x00, // + + // @248 '?' (5 pixels wide) + 0x20, // # + 0x50, // # # + 0x10, // # + 0x20, // # + 0x00, // + 0x20, // # + 0x00, // + 0x00, // + + // @256 '@' (5 pixels wide) + 0x30, // ## + 0x48, // # # + 0x48, // # # + 0x58, // # ## + 0x48, // # # + 0x40, // # + 0x38, // ### + 0x00, // + + // @264 'A' (5 pixels wide) + 0x60, // ## + 0x20, // # + 0x50, // # # + 0x70, // ### + 0x88, //# # + 0xd8, //## ## + 0x00, // + 0x00, // + + // @272 'B' (5 pixels wide) + 0xf0, //#### + 0x48, // # # + 0x70, // ### + 0x48, // # # + 0x48, // # # + 0xf0, //#### + 0x00, // + 0x00, // + + // @280 'C' (5 pixels wide) + 0x70, // ### + 0x50, // # # + 0x40, // # + 0x40, // # + 0x40, // # + 0x30, // ## + 0x00, // + 0x00, // + + // @288 'D' (5 pixels wide) + 0xf0, //#### + 0x48, // # # + 0x48, // # # + 0x48, // # # + 0x48, // # # + 0xf0, //#### + 0x00, // + 0x00, // + + // @296 'E' (5 pixels wide) + 0xf8, //##### + 0x48, // # # + 0x60, // ## + 0x40, // # + 0x48, // # # + 0xf8, //##### + 0x00, // + 0x00, // + + // @304 'F' (5 pixels wide) + 0xf8, //##### + 0x48, // # # + 0x60, // ## + 0x40, // # + 0x40, // # + 0xe0, //### + 0x00, // + 0x00, // + + // @312 'G' (5 pixels wide) + 0x70, // ### + 0x40, // # + 0x40, // # + 0x58, // # ## + 0x50, // # # + 0x30, // ## + 0x00, // + 0x00, // + + // @320 'H' (5 pixels wide) + 0xe8, //### # + 0x48, // # # + 0x78, // #### + 0x48, // # # + 0x48, // # # + 0xe8, //### # + 0x00, // + 0x00, // + + // @328 'I' (5 pixels wide) + 0x70, // ### + 0x20, // # + 0x20, // # + 0x20, // # + 0x20, // # + 0x70, // ### + 0x00, // + 0x00, // + + // @336 'J' (5 pixels wide) + 0x38, // ### + 0x10, // # + 0x10, // # + 0x50, // # # + 0x50, // # # + 0x20, // # + 0x00, // + 0x00, // + + // @344 'K' (5 pixels wide) + 0xd8, //## ## + 0x50, // # # + 0x60, // ## + 0x70, // ### + 0x50, // # # + 0xd8, //## ## + 0x00, // + 0x00, // + + // @352 'L' (5 pixels wide) + 0xe0, //### + 0x40, // # + 0x40, // # + 0x40, // # + 0x48, // # # + 0xf8, //##### + 0x00, // + 0x00, // + + // @360 'M' (5 pixels wide) + 0xd8, //## ## + 0xd8, //## ## + 0xd8, //## ## + 0xa8, //# # # + 0x88, //# # + 0xd8, //## ## + 0x00, // + 0x00, // + + // @368 'N' (5 pixels wide) + 0xd8, //## ## + 0x68, // ## # + 0x68, // ## # + 0x58, // # ## + 0x58, // # ## + 0xe8, //### # + 0x00, // + 0x00, // + + // @376 'O' (5 pixels wide) + 0x30, // ## + 0x48, // # # + 0x48, // # # + 0x48, // # # + 0x48, // # # + 0x30, // ## + 0x00, // + 0x00, // + + // @384 'P' (5 pixels wide) + 0xf0, //#### + 0x48, // # # + 0x48, // # # + 0x70, // ### + 0x40, // # + 0xe0, //### + 0x00, // + 0x00, // + + // @392 'Q' (5 pixels wide) + 0x30, // ## + 0x48, // # # + 0x48, // # # + 0x48, // # # + 0x48, // # # + 0x30, // ## + 0x18, // ## + 0x00, // + + // @400 'R' (5 pixels wide) + 0xf0, //#### + 0x48, // # # + 0x48, // # # + 0x70, // ### + 0x48, // # # + 0xe8, //### # + 0x00, // + 0x00, // + + // @408 'S' (5 pixels wide) + 0x70, // ### + 0x50, // # # + 0x20, // # + 0x10, // # + 0x50, // # # + 0x70, // ### + 0x00, // + 0x00, // + + // @416 'T' (5 pixels wide) + 0xf8, //##### + 0xa8, //# # # + 0x20, // # + 0x20, // # + 0x20, // # + 0x70, // ### + 0x00, // + 0x00, // + + // @424 'U' (5 pixels wide) + 0xd8, //## ## + 0x48, // # # + 0x48, // # # + 0x48, // # # + 0x48, // # # + 0x30, // ## + 0x00, // + 0x00, // + + // @432 'V' (5 pixels wide) + 0xd8, //## ## + 0x88, //# # + 0x48, // # # + 0x50, // # # + 0x50, // # # + 0x30, // ## + 0x00, // + 0x00, // + + // @440 'W' (5 pixels wide) + 0xd8, //## ## + 0x88, //# # + 0xa8, //# # # + 0xa8, //# # # + 0xa8, //# # # + 0x50, // # # + 0x00, // + 0x00, // + + // @448 'X' (5 pixels wide) + 0xd8, //## ## + 0x50, // # # + 0x20, // # + 0x20, // # + 0x50, // # # + 0xd8, //## ## + 0x00, // + 0x00, // + + // @456 'Y' (5 pixels wide) + 0xd8, //## ## + 0x88, //# # + 0x50, // # # + 0x20, // # + 0x20, // # + 0x70, // ### + 0x00, // + 0x00, // + + // @464 'Z' (5 pixels wide) + 0x78, // #### + 0x48, // # # + 0x10, // # + 0x20, // # + 0x48, // # # + 0x78, // #### + 0x00, // + 0x00, // + + // @472 '[' (5 pixels wide) + 0x30, // ## + 0x20, // # + 0x20, // # + 0x20, // # + 0x20, // # + 0x20, // # + 0x30, // ## + 0x00, // + + // @480 '\' (5 pixels wide) + 0x80, //# + 0x40, // # + 0x40, // # + 0x20, // # + 0x20, // # + 0x20, // # + 0x10, // # + 0x00, // + + // @488 ']' (5 pixels wide) + 0x60, // ## + 0x20, // # + 0x20, // # + 0x20, // # + 0x20, // # + 0x20, // # + 0x60, // ## + 0x00, // + + // @496 '^' (5 pixels wide) + 0x20, // # + 0x20, // # + 0x50, // # # + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + + // @504 '_' (5 pixels wide) + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0xf8, //##### + + // @512 '`' (5 pixels wide) + 0x20, // # + 0x10, // # + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + + // @520 'a' (5 pixels wide) + 0x00, // + 0x00, // + 0x30, // ## + 0x10, // # + 0x70, // ### + 0x78, // #### + 0x00, // + 0x00, // + + // @528 'b' (5 pixels wide) + 0xc0, //## + 0x40, // # + 0x70, // ### + 0x48, // # # + 0x48, // # # + 0xf0, //#### + 0x00, // + 0x00, // + + // @536 'c' (5 pixels wide) + 0x00, // + 0x00, // + 0x70, // ### + 0x40, // # + 0x40, // # + 0x70, // ### + 0x00, // + 0x00, // + + // @544 'd' (5 pixels wide) + 0x18, // ## + 0x08, // # + 0x38, // ### + 0x48, // # # + 0x48, // # # + 0x38, // ### + 0x00, // + 0x00, // + + // @552 'e' (5 pixels wide) + 0x00, // + 0x00, // + 0x70, // ### + 0x70, // ### + 0x40, // # + 0x30, // ## + 0x00, // + 0x00, // + + // @560 'f' (5 pixels wide) + 0x10, // # + 0x20, // # + 0x70, // ### + 0x20, // # + 0x20, // # + 0x70, // ### + 0x00, // + 0x00, // + + // @568 'g' (5 pixels wide) + 0x00, // + 0x00, // + 0x38, // ### + 0x48, // # # + 0x48, // # # + 0x38, // ### + 0x08, // # + 0x30, // ## + + // @576 'h' (5 pixels wide) + 0xc0, //## + 0x40, // # + 0x70, // ### + 0x48, // # # + 0x48, // # # + 0xe8, //### # + 0x00, // + 0x00, // + + // @584 'i' (5 pixels wide) + 0x20, // # + 0x00, // + 0x60, // ## + 0x20, // # + 0x20, // # + 0x70, // ### + 0x00, // + 0x00, // + + // @592 'j' (5 pixels wide) + 0x20, // # + 0x00, // + 0x70, // ### + 0x10, // # + 0x10, // # + 0x10, // # + 0x10, // # + 0x70, // ### + + // @600 'k' (5 pixels wide) + 0xc0, //## + 0x40, // # + 0x58, // # ## + 0x70, // ### + 0x50, // # # + 0xd8, //## ## + 0x00, // + 0x00, // + + // @608 'l' (5 pixels wide) + 0x60, // ## + 0x20, // # + 0x20, // # + 0x20, // # + 0x20, // # + 0x70, // ### + 0x00, // + 0x00, // + + // @616 'm' (5 pixels wide) + 0x00, // + 0x00, // + 0xd0, //## # + 0xa8, //# # # + 0xa8, //# # # + 0xa8, //# # # + 0x00, // + 0x00, // + + // @624 'n' (5 pixels wide) + 0x00, // + 0x00, // + 0xf0, //#### + 0x48, // # # + 0x48, // # # + 0xc8, //## # + 0x00, // + 0x00, // + + // @632 'o' (5 pixels wide) + 0x00, // + 0x00, // + 0x30, // ## + 0x48, // # # + 0x48, // # # + 0x30, // ## + 0x00, // + 0x00, // + + // @640 'p' (5 pixels wide) + 0x00, // + 0x00, // + 0xf0, //#### + 0x48, // # # + 0x48, // # # + 0x70, // ### + 0x40, // # + 0xe0, //### + + // @648 'q' (5 pixels wide) + 0x00, // + 0x00, // + 0x38, // ### + 0x48, // # # + 0x48, // # # + 0x38, // ### + 0x08, // # + 0x18, // ## + + // @656 'r' (5 pixels wide) + 0x00, // + 0x00, // + 0x78, // #### + 0x20, // # + 0x20, // # + 0x70, // ### + 0x00, // + 0x00, // + + // @664 's' (5 pixels wide) + 0x00, // + 0x00, // + 0x30, // ## + 0x20, // # + 0x10, // # + 0x60, // ## + 0x00, // + 0x00, // + + // @672 't' (5 pixels wide) + 0x00, // + 0x40, // # + 0xf0, //#### + 0x40, // # + 0x48, // # # + 0x30, // ## + 0x00, // + 0x00, // + + // @680 'u' (5 pixels wide) + 0x00, // + 0x00, // + 0xd8, //## ## + 0x48, // # # + 0x48, // # # + 0x38, // ### + 0x00, // + 0x00, // + + // @688 'v' (5 pixels wide) + 0x00, // + 0x00, // + 0xc8, //## # + 0x48, // # # + 0x30, // ## + 0x30, // ## + 0x00, // + 0x00, // + + // @696 'w' (5 pixels wide) + 0x00, // + 0x00, // + 0xd8, //## ## + 0xa8, //# # # + 0xa8, //# # # + 0x50, // # # + 0x00, // + 0x00, // + + // @704 'x' (5 pixels wide) + 0x00, // + 0x00, // + 0x48, // # # + 0x30, // ## + 0x30, // ## + 0x48, // # # + 0x00, // + 0x00, // + + // @712 'y' (5 pixels wide) + 0x00, // + 0x00, // + 0xd8, //## ## + 0x50, // # # + 0x50, // # # + 0x20, // # + 0x20, // # + 0x60, // ## + + // @720 'z' (5 pixels wide) + 0x00, // + 0x00, // + 0x78, // #### + 0x50, // # # + 0x28, // # # + 0x78, // #### + 0x00, // + 0x00, // + + // @728 '{' (5 pixels wide) + 0x10, // # + 0x20, // # + 0x20, // # + 0x60, // ## + 0x20, // # + 0x20, // # + 0x10, // # + 0x00, // + + // @736 '|' (5 pixels wide) + 0x20, // # + 0x20, // # + 0x20, // # + 0x20, // # + 0x20, // # + 0x20, // # + 0x20, // # + 0x00, // + + // @744 '}' (5 pixels wide) + 0x40, // # + 0x20, // # + 0x20, // # + 0x30, // ## + 0x20, // # + 0x20, // # + 0x40, // # + 0x00, // + + // @752 '~' (5 pixels wide) + 0x00, // + 0x00, // + 0x00, // + 0x28, // # # + 0x50, // # # + 0x00, // + 0x00, // + 0x00, // + + // @0 ' ' (5 pixels wide) + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + 0x00, // + + // @0 ' ' (5 pixels wide) + 0x50, // # # + 0x00, // + 0x30, // ## + 0x10, // # + 0x70, // ### + 0x78, // #### + 0x00, // + 0x00, // + + // @0 ' ' (5 pixels wide) + 0x88, //# # + 0x60, // ## + 0x20, // # + 0x50, // # # + 0x70, // ### + 0x88, //# # + 0xd8, //## ## + 0x00, // + + // @0 ' ' (5 pixels wide) + 0x48, // # # + 0x00, // + 0x30, // ## + 0x48, // # # + 0x48, // # # + 0x30, // ## + 0x00, // + 0x00, // + + // @0 ' ' (5 pixels wide) + 0x48, // # # + 0x30, // ## + 0x48, // # # + 0x48, // # # + 0x48, // # # + 0x48, // # # + 0x30, // ## + 0x00, // + + // @0 ' ' (5 pixels wide) + 0x48, // # # + 0x00, // + 0xd8, //## ## + 0x48, // # # + 0x48, // # # + 0x38, // ### + 0x00, // + 0x00, // + + // @0 ' ' (5 pixels wide) + 0x48, // # # + 0x00, // + 0xd8, //## ## + 0x48, // # # + 0x48, // # # + 0x48, // # # + 0x48, // # # + 0x30, // ## + + // @0 ' ' (5 pixels wide) + 0x70, // ### + 0x50, // # # + 0x60, // ## + 0x50, // # # + 0x48, // # # + 0x58, // # ## + 0x60, // ## + 0x40 // # +}; + +sFONT Font8 = { + Font8_Table, + 5, /* Width */ + 8, /* Height */ +}; + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.0/src/fonts.h b/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/fonts.h similarity index 99% rename from lib/esp-epaper-29-ws-20171230-gemu-1.0/src/fonts.h rename to lib/esp-epaper-29-ws-20171230-gemu-1.1/src/fonts.h index 4530308b8..a19c1bedc 100644 --- a/lib/esp-epaper-29-ws-20171230-gemu-1.0/src/fonts.h +++ b/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/fonts.h @@ -51,12 +51,14 @@ /* Includes ------------------------------------------------------------------*/ #include +#define USE_TINY_FONT + typedef struct _tFont -{ +{ const uint8_t *table; uint16_t Width; uint16_t Height; - + } sFONT; extern sFONT Font24; @@ -68,8 +70,8 @@ extern sFONT Font8; #ifdef __cplusplus } #endif - + #endif /* __FONTS_H */ - + /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/renderer.cpp b/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/renderer.cpp new file mode 100644 index 000000000..722d5ffb1 --- /dev/null +++ b/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/renderer.cpp @@ -0,0 +1,519 @@ +/** + * @filename : epdpaint.cpp + * @brief : Paint tools + * @author : Yehui from Waveshare + * + * Copyright (C) Waveshare September 9 2017 + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documnetation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include "renderer.h" + +#define USE_EPD_FONTS +//#define USE_ALL_EPD_FONTS +//#define USE_GFX_FONTS +#define USE_TINY_FONT + +uint8_t wr_redir=0; + +uint8_t *buffer; + +#define SPRINT(A) char str[32];sprintf(str,"val: %d ",A);Serial.println((char*)str); + +#define OLED_FONT_WIDTH 6 +#define OLED_FONT_HEIGTH 8 +#define BLACK 0 + +Renderer::Renderer(int16_t x, int16_t y) : +Adafruit_GFX(x, y) { + font=0; +#ifdef USE_EPD_FONTS + selected_font = &Font12; +#endif + +} + +uint16_t Renderer::GetColorFromIndex(uint8_t index) { + if (index>0) return 1; + return 0; +} + +void Renderer::dim(uint8_t contrast) { + +} + +void Renderer::pushColors(uint16_t *data, uint8_t len, boolean first) { + +} + +void Renderer::setAddrWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) { + +} + +void Renderer::DisplayOnff(int8_t on) { + +} +void Renderer::DisplayInit(int8_t p,int8_t size,int8_t rot,int8_t font) { + +} + +int16_t Renderer::Begin(int16_t p1,int16_t p2,int16_t p3) { + return 0; +} + +void Renderer::Updateframe() { + +} + +/** + * @brief: this draws a charactor on the frame buffer but not refresh + */ +void Renderer::DrawCharAt(int16_t x, int16_t y, char ascii_char,int16_t colored) { +#ifdef USE_EPD_FONTS + sFONT *xfont = selected_font; + int i, j; + unsigned int char_offset = (ascii_char - ' ') * xfont->Height * (xfont->Width / 8 + (xfont->Width % 8 ? 1 : 0)); + const unsigned char* ptr = &xfont->table[char_offset]; + + for (j = 0; j < xfont->Height; j++) { + for (i = 0; i < xfont->Width; i++) { + if (pgm_read_byte(ptr) & (0x80 >> (i % 8))) { + writePixel(x + i, y + j, colored); + } else { + // fill background + if (!drawmode) writePixel(x + i, y + j, textbgcolor); + } + if (i % 8 == 7) { + ptr++; + } + } + if (xfont->Width % 8 != 0) { + ptr++; + } + } +#endif +} + +/** +* @brief: this displays a string on the frame buffer but not refresh +*/ +void Renderer::DrawStringAt(int16_t x, int16_t y, const char* text, uint16_t colored, uint8_t flag) { + const char* p_text = text; + unsigned int counter = 0; + int refcolumn = x; + sFONT *xfont = selected_font; + +#ifndef USE_EPD_FONTS + font=0; +#endif + +#ifndef USE_GFX_FONTS + if (!font) { +#endif + if (flag) { + x=(x-1)*OLED_FONT_WIDTH*textsize_x; + y=(y-1)*OLED_FONT_HEIGTH*textsize_y; + } + setCursor(x,y); + setTextColor(colored,textbgcolor); + print(text); + return; +#ifndef USE_GFX_FONTS + } +#endif + + + if (flag) { + x=(x-1)*xfont->Width; + y=(y-1)*xfont->Height; + refcolumn = x; + } + + /* Send the string character by character on EPD */ + if (font==7) { + return FastString(x,y,colored,p_text); + } + while (*p_text != 0) { + /* Display one character on EPD */ + DrawCharAt(refcolumn, y, *p_text, colored); + /* increment the column position */ + refcolumn += xfont->Width; + /* Point on the next character */ + p_text++; + counter++; + } + +} + +void Renderer::FastString(uint16_t x,uint16_t y,uint16_t tcolor, const char* str) { + +} + +#include +#include +#include + +sFONT RAFont = { + 0, + 7, /* Width */ + 12, /* Height */ +}; + +void Renderer::setTextFont(uint8_t f) { + font=f; + +#ifdef USE_GFX_FONTS + switch (f) { + case 0: + setFont(0); + break; + case 1: + setFont(&FreeMono12pt7b); + break; + case 2: + setFont(&FreeMono18pt7b); + break; + case 3: + setFont(&FreeMono24pt7b); + break; + default: + setFont(0); + break; + } + +#endif + +#ifdef USE_ALL_EPD_FONTS + switch (font) { + case 1: + selected_font = &Font12; + break; + case 2: + selected_font = &Font24; + break; + case 3: + selected_font = &Font8; + break; + case 4: + selected_font = &Font16; + break; + case 5: + selected_font = &Font20; + break; + case 7: + selected_font = &RAFont; + default: + font=0; + } +#else +#ifdef USE_EPD_FONTS + if (1 == font) { + selected_font = &Font12; + } else { + #ifdef USE_TINY_FONT + if (2 == font) { + selected_font = &Font24; + } else { + selected_font = &Font8; + } + #else + selected_font = &Font24; + #endif + } +#endif +#endif + +} + + +void Renderer::clearDisplay(void) { + fillScreen(BLACK); +} + +#define renderer_swap(a, b) { int16_t t = a; a = b; b = t; } + +void Renderer::drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color) { + boolean bSwap = false; + if (!buffer) return; + switch(getRotation()) { + case 0: + // 0 degree rotation, do nothing + break; + case 1: + // 90 degree rotation, swap x & y for rotation, then invert x + bSwap = true; + renderer_swap(x, y); + x = WIDTH - x - 1; + break; + case 2: + // 180 degree rotation, invert x and y - then shift y around for height. + x = WIDTH - x - 1; + y = HEIGHT - y - 1; + x -= (w-1); + break; + case 3: + // 270 degree rotation, swap x & y for rotation, then invert y and adjust y for w (not to become h) + bSwap = true; + renderer_swap(x, y); + y = HEIGHT - y - 1; + y -= (w-1); + break; + } + + if(bSwap) { + drawFastVLineInternal(x, y, w, color); + } else { + drawFastHLineInternal(x, y, w, color); + } +} + +void Renderer::drawFastHLineInternal(int16_t x, int16_t y, int16_t w, uint16_t color) { + // Do bounds/limit checks + if(y < 0 || y >= HEIGHT) { return; } + + // make sure we don't try to draw below 0 + if(x < 0) { + w += x; + x = 0; + } + + // make sure we don't go off the edge of the display + if( (x + w) > WIDTH) { + w = (WIDTH - x); + } + + // if our width is now negative, punt + if(w <= 0) { return; } + + // set up the pointer for movement through the buffer + register uint8_t *pBuf = buffer; + // adjust the buffer pointer for the current row + pBuf += ((y/8) * WIDTH); + // and offset x columns in + pBuf += x; + + register uint8_t mask = 1 << (y&7); + + switch (color) + { + case WHITE: while(w--) { *pBuf++ |= mask; }; break; + case BLACK: mask = ~mask; while(w--) { *pBuf++ &= mask; }; break; + case INVERSE: while(w--) { *pBuf++ ^= mask; }; break; + } +} + +void Renderer::drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color) { + if (!buffer) return; + bool bSwap = false; + switch(getRotation()) { + case 0: + break; + case 1: + // 90 degree rotation, swap x & y for rotation, then invert x and adjust x for h (now to become w) + bSwap = true; + renderer_swap(x, y); + x = WIDTH - x - 1; + x -= (h-1); + break; + case 2: + // 180 degree rotation, invert x and y - then shift y around for height. + x = WIDTH - x - 1; + y = HEIGHT - y - 1; + y -= (h-1); + break; + case 3: + // 270 degree rotation, swap x & y for rotation, then invert y + bSwap = true; + renderer_swap(x, y); + y = HEIGHT - y - 1; + break; + } + + if(bSwap) { + drawFastHLineInternal(x, y, h, color); + } else { + drawFastVLineInternal(x, y, h, color); + } +} + + +void Renderer::drawFastVLineInternal(int16_t x, int16_t __y, int16_t __h, uint16_t color) { + + // do nothing if we're off the left or right side of the screen + if(x < 0 || x >= WIDTH) { return; } + + // make sure we don't try to draw below 0 + if(__y < 0) { + // __y is negative, this will subtract enough from __h to account for __y being 0 + __h += __y; + __y = 0; + + } + + // make sure we don't go past the height of the display + if( (__y + __h) > HEIGHT) { + __h = (HEIGHT - __y); + } + + // if our height is now negative, punt + if(__h <= 0) { + return; + } + + // this display doesn't need ints for coordinates, use local byte registers for faster juggling + register uint8_t y = __y; + register uint8_t h = __h; + + + // set up the pointer for fast movement through the buffer + register uint8_t *pBuf = buffer; + // adjust the buffer pointer for the current row + pBuf += ((y/8) * WIDTH); + // and offset x columns in + pBuf += x; + + // do the first partial byte, if necessary - this requires some masking + register uint8_t mod = (y&7); + if(mod) { + // mask off the high n bits we want to set + mod = 8-mod; + + // note - lookup table results in a nearly 10% performance improvement in fill* functions + // register uint8_t mask = ~(0xFF >> (mod)); + static uint8_t premask[8] = {0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE }; + register uint8_t mask = premask[mod]; + + // adjust the mask if we're not going to reach the end of this byte + if( h < mod) { + mask &= (0XFF >> (mod-h)); + } + + switch (color) + { + case WHITE: *pBuf |= mask; break; + case BLACK: *pBuf &= ~mask; break; + case INVERSE: *pBuf ^= mask; break; + } + + // fast exit if we're done here! + if(h= 8) { + if (color == INVERSE) { // separate copy of the code so we don't impact performance of the black/white write version with an extra comparison per loop + do { + *pBuf=~(*pBuf); + + // adjust the buffer forward 8 rows worth of data + pBuf += WIDTH; + + // adjust h & y (there's got to be a faster way for me to do this, but this should still help a fair bit for now) + h -= 8; + } while(h >= 8); + } + else { + // store a local value to work with + register uint8_t val = (color == WHITE) ? 255 : 0; + + do { + // write our value in + *pBuf = val; + + // adjust the buffer forward 8 rows worth of data + pBuf += WIDTH; + + // adjust h & y (there's got to be a faster way for me to do this, but this should still help a fair bit for now) + h -= 8; + } while(h >= 8); + } + } + + // now do the final partial byte, if necessary + if(h) { + mod = h & 7; + // this time we want to mask the low bits of the byte, vs the high bits we did above + // register uint8_t mask = (1 << mod) - 1; + // note - lookup table results in a nearly 10% performance improvement in fill* functions + static uint8_t postmask[8] = {0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F }; + register uint8_t mask = postmask[mod]; + switch (color) + { + case WHITE: *pBuf |= mask; break; + case BLACK: *pBuf &= ~mask; break; + case INVERSE: *pBuf ^= mask; break; + } + } +} +/* +void Renderer::drawPixel(int16_t x, int16_t y, uint16_t color) { + if (jdrawPixel)(*jdrawPixel)(x,y,color); +} +*/ + +// the most basic function, set a single pixel +void Renderer::drawPixel(int16_t x, int16_t y, uint16_t color) { + if (!buffer) return; + if ((x < 0) || (x >= width()) || (y < 0) || (y >= height())) + return; + + // check rotation, move pixel around if necessary + switch (getRotation()) { + case 1: + renderer_swap(x, y); + x = WIDTH - x - 1; + break; + case 2: + x = WIDTH - x - 1; + y = HEIGHT - y - 1; + break; + case 3: + renderer_swap(x, y); + y = HEIGHT - y - 1; + break; + } + + // x is which column + switch (color) + { + case WHITE: buffer[x+ (y/8)*WIDTH] |= (1 << (y&7)); break; + case BLACK: buffer[x+ (y/8)*WIDTH] &= ~(1 << (y&7)); break; + case INVERSE: buffer[x+ (y/8)*WIDTH] ^= (1 << (y&7)); break; + } + +} + +void Renderer::setDrawMode(uint8_t mode) { + drawmode=mode; +} + +void VButton::xdrawButton(bool inverted) { + wr_redir=1; + drawButton(inverted); + wr_redir=0; +} + +/* END OF FILE */ diff --git a/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/renderer.h b/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/renderer.h new file mode 100644 index 000000000..3f87f2a89 --- /dev/null +++ b/lib/esp-epaper-29-ws-20171230-gemu-1.1/src/renderer.h @@ -0,0 +1,58 @@ + +#ifndef RENDERER_H +#define RENDERER_H + +#include +#include "fonts.h" + +#define BLACK 0 +#define WHITE 1 +#define INVERSE 2 + +// depends on GFX driver +// GFX patched +// a. in class GFX setCursor,setTextSize => virtual +// b. textcolor,textbgcolor => public; + + +class Renderer : public Adafruit_GFX { + //Paint(unsigned char* image, int width, int height); + //~Renderer(); +public: + Renderer(int16_t x, int16_t y); + void setTextFont(uint8_t f); + void clearDisplay(void); + virtual void DrawStringAt(int16_t x, int16_t y, const char* text,uint16_t colored,uint8_t flag); + virtual void drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color); + virtual void drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color); + virtual void drawPixel(int16_t x, int16_t y, uint16_t color); + virtual uint16_t GetColorFromIndex(uint8_t index); + + virtual void DisplayOnff(int8_t on); + virtual void DisplayInit(int8_t p,int8_t size,int8_t rot,int8_t font); + virtual int16_t Begin(int16_t p1,int16_t p2,int16_t p3); + virtual void Updateframe(); + virtual void dim(uint8_t contrast); + virtual void pushColors(uint16_t *data, uint8_t len, boolean first); + virtual void setAddrWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1); + void setDrawMode(uint8_t mode); + uint8_t drawmode; + virtual void FastString(uint16_t x,uint16_t y,uint16_t tcolor, const char* str); +private: + void DrawCharAt(int16_t x, int16_t y, char ascii_char,int16_t colored); + inline void drawFastVLineInternal(int16_t x, int16_t y, int16_t h, uint16_t color) __attribute__((always_inline)); + inline void drawFastHLineInternal(int16_t x, int16_t y, int16_t w, uint16_t color) __attribute__((always_inline)); + sFONT *selected_font; + uint8_t font; +}; + +class VButton : public Adafruit_GFX_Button { + public: + uint8_t vpower; + void xdrawButton(bool inverted); +}; + + +#endif + +/* END OF FILE */ diff --git a/lib/esp-knx-ip-0.5.1/DPT.h b/lib/esp-knx-ip-0.5.2/DPT.h similarity index 95% rename from lib/esp-knx-ip-0.5.1/DPT.h rename to lib/esp-knx-ip-0.5.2/DPT.h index 71045b103..3529d51af 100644 --- a/lib/esp-knx-ip-0.5.1/DPT.h +++ b/lib/esp-knx-ip-0.5.2/DPT.h @@ -40,13 +40,14 @@ typedef enum __dpt_3_007 typedef enum __weekday { + DPT_10_001_WEEKDAY_NODAY = 0, DPT_10_001_WEEKDAY_MONDAY = 1, DPT_10_001_WEEKDAY_TUESDAY = 2, DPT_10_001_WEEKDAY_WEDNESDAY = 3, DPT_10_001_WEEKDAY_THURSDAY = 4, DPT_10_001_WEEKDAY_FRIDAY = 5, DPT_10_001_WEEKDAY_SATURDAY = 6, - DPT_10_001_WEEKDAY_SUNDAY = 8, + DPT_10_001_WEEKDAY_SUNDAY = 7, } weekday_t; typedef struct __time_of_day diff --git a/lib/esp-knx-ip-0.5.1/LICENSE b/lib/esp-knx-ip-0.5.2/LICENSE similarity index 100% rename from lib/esp-knx-ip-0.5.1/LICENSE rename to lib/esp-knx-ip-0.5.2/LICENSE diff --git a/lib/esp-knx-ip-0.5.1/README.md b/lib/esp-knx-ip-0.5.2/README.md similarity index 100% rename from lib/esp-knx-ip-0.5.1/README.md rename to lib/esp-knx-ip-0.5.2/README.md diff --git a/lib/esp-knx-ip-0.5.1/esp-knx-ip-config.cpp b/lib/esp-knx-ip-0.5.2/esp-knx-ip-config.cpp similarity index 100% rename from lib/esp-knx-ip-0.5.1/esp-knx-ip-config.cpp rename to lib/esp-knx-ip-0.5.2/esp-knx-ip-config.cpp diff --git a/lib/esp-knx-ip-0.5.1/esp-knx-ip-conversion.cpp b/lib/esp-knx-ip-0.5.2/esp-knx-ip-conversion.cpp similarity index 100% rename from lib/esp-knx-ip-0.5.1/esp-knx-ip-conversion.cpp rename to lib/esp-knx-ip-0.5.2/esp-knx-ip-conversion.cpp diff --git a/lib/esp-knx-ip-0.5.1/esp-knx-ip-send.cpp b/lib/esp-knx-ip-0.5.2/esp-knx-ip-send.cpp similarity index 93% rename from lib/esp-knx-ip-0.5.1/esp-knx-ip-send.cpp rename to lib/esp-knx-ip-0.5.2/esp-knx-ip-send.cpp index ae5e9fabc..e71e5954c 100644 --- a/lib/esp-knx-ip-0.5.1/esp-knx-ip-send.cpp +++ b/lib/esp-knx-ip-0.5.2/esp-knx-ip-send.cpp @@ -185,3 +185,17 @@ void ESPKNXIP::send_4byte_float(address_t const &receiver, knx_command_type_t ct uint8_t buf[] = {0x00, ((uint8_t *)&val)[3], ((uint8_t *)&val)[2], ((uint8_t *)&val)[1], ((uint8_t *)&val)[0]}; send(receiver, ct, 5, buf); } + +void ESPKNXIP::send_14byte_string(address_t const &receiver, knx_command_type_t ct, const char *val) +{ + // DPT16 strings are always 14 bytes long, however the data array is one larger due to the telegram structure. + // The first byte needs to be zero, string start after that. + uint8_t buf[15] = {0x00}; + int len = strlen(val); + if (len > 14) + { + len = 14; + } + memcpy(buf+1, val, len); + send(receiver, ct, 15, buf); +} diff --git a/lib/esp-knx-ip-0.5.1/esp-knx-ip-webserver.cpp b/lib/esp-knx-ip-0.5.2/esp-knx-ip-webserver.cpp similarity index 100% rename from lib/esp-knx-ip-0.5.1/esp-knx-ip-webserver.cpp rename to lib/esp-knx-ip-0.5.2/esp-knx-ip-webserver.cpp diff --git a/lib/esp-knx-ip-0.5.1/esp-knx-ip.cpp b/lib/esp-knx-ip-0.5.2/esp-knx-ip.cpp similarity index 100% rename from lib/esp-knx-ip-0.5.1/esp-knx-ip.cpp rename to lib/esp-knx-ip-0.5.2/esp-knx-ip.cpp diff --git a/lib/esp-knx-ip-0.5.1/esp-knx-ip.h b/lib/esp-knx-ip-0.5.2/esp-knx-ip.h similarity index 98% rename from lib/esp-knx-ip-0.5.1/esp-knx-ip.h rename to lib/esp-knx-ip-0.5.2/esp-knx-ip.h index e2346c5a3..eb5ecf7b0 100644 --- a/lib/esp-knx-ip-0.5.1/esp-knx-ip.h +++ b/lib/esp-knx-ip-0.5.2/esp-knx-ip.h @@ -451,6 +451,7 @@ class ESPKNXIP { void send_4byte_int(address_t const &receiver, knx_command_type_t ct, int32_t val); void send_4byte_uint(address_t const &receiver, knx_command_type_t ct, uint32_t val); void send_4byte_float(address_t const &receiver, knx_command_type_t ct, float val); + void send_14byte_string(address_t const &receiver, knx_command_type_t ct, const char *val); void write_1bit(address_t const &receiver, uint8_t bit) { send_1bit(receiver, KNX_CT_WRITE, bit); } void write_2bit(address_t const &receiver, uint8_t twobit) { send_2bit(receiver, KNX_CT_WRITE, twobit); } @@ -469,6 +470,7 @@ class ESPKNXIP { void write_4byte_int(address_t const &receiver, int32_t val) { send_4byte_int(receiver, KNX_CT_WRITE, val); } void write_4byte_uint(address_t const &receiver, uint32_t val) { send_4byte_uint(receiver, KNX_CT_WRITE, val); } void write_4byte_float(address_t const &receiver, float val) { send_4byte_float(receiver, KNX_CT_WRITE, val);} + void write_14byte_string(address_t const &receiver, const char *val) { send_14byte_string(receiver, KNX_CT_WRITE, val); } void answer_1bit(address_t const &receiver, uint8_t bit) { send_1bit(receiver, KNX_CT_ANSWER, bit); } void answer_2bit(address_t const &receiver, uint8_t twobit) { send_2bit(receiver, KNX_CT_ANSWER, twobit); } @@ -487,6 +489,7 @@ class ESPKNXIP { void answer_4byte_int(address_t const &receiver, int32_t val) { send_4byte_int(receiver, KNX_CT_ANSWER, val); } void answer_4byte_uint(address_t const &receiver, uint32_t val) { send_4byte_uint(receiver, KNX_CT_ANSWER, val); } void answer_4byte_float(address_t const &receiver, float val) { send_4byte_float(receiver, KNX_CT_ANSWER, val);} + void answer_14byte_string(address_t const &receiver, const char *val) { send_14byte_string(receiver, KNX_CT_ANSWER, val); } bool data_to_bool(uint8_t *data); int8_t data_to_1byte_int(uint8_t *data); diff --git a/lib/esp-knx-ip-0.5.1/examples/environment-sensor/environment-sensor.ino b/lib/esp-knx-ip-0.5.2/examples/environment-sensor/environment-sensor.ino similarity index 100% rename from lib/esp-knx-ip-0.5.1/examples/environment-sensor/environment-sensor.ino rename to lib/esp-knx-ip-0.5.2/examples/environment-sensor/environment-sensor.ino diff --git a/lib/esp-knx-ip-0.5.1/examples/sonoff/sonoff.ino b/lib/esp-knx-ip-0.5.2/examples/sonoff/sonoff.ino similarity index 100% rename from lib/esp-knx-ip-0.5.1/examples/sonoff/sonoff.ino rename to lib/esp-knx-ip-0.5.2/examples/sonoff/sonoff.ino diff --git a/lib/esp-knx-ip-0.5.1/examples/static-config/static-config.ino b/lib/esp-knx-ip-0.5.2/examples/static-config/static-config.ino similarity index 87% rename from lib/esp-knx-ip-0.5.1/examples/static-config/static-config.ino rename to lib/esp-knx-ip-0.5.2/examples/static-config/static-config.ino index bea5093f3..54472dda3 100644 --- a/lib/esp-knx-ip-0.5.1/examples/static-config/static-config.ino +++ b/lib/esp-knx-ip-0.5.2/examples/static-config/static-config.ino @@ -31,14 +31,14 @@ void setup() { pinMode(LED_PIN, OUTPUT); Serial.begin(115200); - callback_id_t temp_cb = knx.callback_register("Read Temperature", temp_cb); - callback_id_t hum_cb =knx.callback_register("Read Humidity", hum_cb); - callback_id_t pres_cb =knx.callback_register("Read Pressure", pres_cb); + callback_id_t temp_cb_id = knx.callback_register("Read Temperature", temp_cb); + callback_id_t hum_cb_id =knx.callback_register("Read Humidity", hum_cb); + callback_id_t pres_cb_id =knx.callback_register("Read Pressure", pres_cb); // Assign callbacks to group addresses (2/1/1, 2/1/2, 2/1/3) - knx.callback_assign(temp_cb, knx.GA_to_address(2, 1, 1)); - knx.callback_assign(hum_cb, knx.GA_to_address(2, 1, 2)); - knx.callback_assign(pres_cb, knx.GA_to_address(2, 1, 3)); + knx.callback_assign(temp_cb_id, knx.GA_to_address(2, 1, 1)); + knx.callback_assign(hum_cb_id, knx.GA_to_address(2, 1, 2)); + knx.callback_assign(pres_cb_id, knx.GA_to_address(2, 1, 3)); // Set physical address (1.1.1) knx.physical_address_set(knx.PA_to_address(1, 1, 1)); diff --git a/lib/esp-knx-ip-0.5.1/keywords.txt b/lib/esp-knx-ip-0.5.2/keywords.txt similarity index 83% rename from lib/esp-knx-ip-0.5.1/keywords.txt rename to lib/esp-knx-ip-0.5.2/keywords.txt index ec1f4c78c..59836ef05 100644 --- a/lib/esp-knx-ip-0.5.1/keywords.txt +++ b/lib/esp-knx-ip-0.5.2/keywords.txt @@ -1,14 +1,14 @@ # datatypes -address_t KEYWORD1 DATA_TYPE -message_t KEYWORD1 DATA_TYPE -callback_id_t KEYWORD1 DATA_TYPE -callback_assignment_id_t KEYWORD1 DATA_TYPE -option_entry_t KEYWORD1 DATA_TYPE -config_id_t KEYWORD1 DATA_TYPE -enable_condition_t KEYWORD1 DATA_TYPE -callback_fptr_t KEYWORD1 DATA_TYPE -feedback_action_fptr_t KEYWORD1 DATA_TYPE -knx_command_type_t KEYWORD1 DATA_TYPE +address_t KEYWORD1 DATA_TYPE +message_t KEYWORD1 DATA_TYPE +callback_id_t KEYWORD1 DATA_TYPE +callback_assignment_id_t KEYWORD1 DATA_TYPE +option_entry_t KEYWORD1 DATA_TYPE +config_id_t KEYWORD1 DATA_TYPE +enable_condition_t KEYWORD1 DATA_TYPE +callback_fptr_t KEYWORD1 DATA_TYPE +feedback_action_fptr_t KEYWORD1 DATA_TYPE +knx_command_type_t KEYWORD1 DATA_TYPE # methods setup KEYWORD2 @@ -57,6 +57,7 @@ send_3byte_color KEYWORD2 send_4byte_int KEYWORD2 send_4byte_uint KEYWORD2 send_4byte_float KEYWORD2 +send_14byte_string KEYWORD2 write_1bit KEYWORD2 write_2bit KEYWORD2 write_4bit KEYWORD2 @@ -74,6 +75,7 @@ write_3byte_color KEYWORD2 write_4byte_int KEYWORD2 write_4byte_uint KEYWORD2 write_4byte_float KEYWORD2 +write_14byte_string KEYWORD2 answer_1bit KEYWORD2 answer_2bit KEYWORD2 answer_4bit KEYWORD2 @@ -91,6 +93,7 @@ answer_3byte_color KEYWORD2 answer_4byte_int KEYWORD2 answer_4byte_uint KEYWORD2 answer_4byte_float KEYWORD2 +answer_14byte_string KEYWORD2 data_to_1byte_int KEYWORD2 data_to_2byte_int KEYWORD2 diff --git a/lib/esp-knx-ip-0.5.1/library.properties b/lib/esp-knx-ip-0.5.2/library.properties similarity index 95% rename from lib/esp-knx-ip-0.5.1/library.properties rename to lib/esp-knx-ip-0.5.2/library.properties index 1adbc402a..f3b86de9c 100644 --- a/lib/esp-knx-ip-0.5.1/library.properties +++ b/lib/esp-knx-ip-0.5.2/library.properties @@ -1,5 +1,5 @@ name=ESP KNX IP Library -version=0.5.1 +version=0.5.2 author=Nico Weichbrodt maintainer=Nico Weichbrodt sentence=ESP8266 library for KNX/IP communication. diff --git a/lib/readme.txt b/lib/readme.txt deleted file mode 100644 index dbadc3d63..000000000 --- a/lib/readme.txt +++ /dev/null @@ -1,36 +0,0 @@ - -This directory is intended for the project specific (private) libraries. -PlatformIO will compile them to static libraries and link to executable file. - -The source code of each library should be placed in separate directory, like -"lib/private_lib/[here are source files]". - -For example, see how can be organized `Foo` and `Bar` libraries: - -|--lib -| |--Bar -| | |--docs -| | |--examples -| | |--src -| | |- Bar.c -| | |- Bar.h -| |--Foo -| | |- Foo.c -| | |- Foo.h -| |- readme.txt --> THIS FILE -|- platformio.ini -|--src - |- main.c - -Then in `src/main.c` you should use: - -#include -#include - -// rest H/C/CPP code - -PlatformIO will find your libraries automatically, configure preprocessor's -include paths and build them. - -More information about PlatformIO Library Dependency Finder -- http://docs.platformio.org/page/librarymanager/ldf.html diff --git a/pio/http-uploader.py b/pio/http-uploader.py index b80ee4a09..dd563177f 100644 --- a/pio/http-uploader.py +++ b/pio/http-uploader.py @@ -1,10 +1,15 @@ Import("env") -from base64 import b64decode +# pio < 4.0.0 +# from base64 import b64decode +# env.Replace(UPLOADER="pio\espupload.py") +# env.Replace(UPLOADERFLAGS="") +# env.Replace(UPLOADCMD="$UPLOADER -u " + b64decode(ARGUMENTS.get("UPLOAD_PORT")) + " -f $SOURCES") +# pio >= 4.0.0 env.Replace(UPLOADER="pio\espupload.py") env.Replace(UPLOADERFLAGS="") -env.Replace(UPLOADCMD="$UPLOADER -u " + b64decode(ARGUMENTS.get("UPLOAD_PORT")) + " -f $SOURCES") +env.Replace(UPLOADCMD="$UPLOADER -u $UPLOAD_PORT -f $SOURCES") ''' env.Replace(UPLOADCMD="pio\espupload.py -f $SOURCES") # Windows diff --git a/pio/obj-dump.py b/pio/obj-dump.py new file mode 100644 index 000000000..91bc3de58 --- /dev/null +++ b/pio/obj-dump.py @@ -0,0 +1,9 @@ +# Little convenience script to get an object dump + +Import('env') + +def obj_dump_after_elf(source, target, env): + print("Create firmware.asm") + env.Execute("xtensa-lx106-elf-objdump "+ "-D " + str(target[0]) + " > "+ "${PROGNAME}.asm") + +env.AddPostAction("$BUILD_DIR/${PROGNAME}.elf", [obj_dump_after_elf]) diff --git a/pio/sftp-uploader.py b/pio/sftp-uploader.py index c6fe9fb84..b39f76338 100644 --- a/pio/sftp-uploader.py +++ b/pio/sftp-uploader.py @@ -1,6 +1,12 @@ Import("env") -from base64 import b64decode +# pio < 4.0.0 +# from base64 import b64decode +# env.Replace(UPLOADER="scp") +# env.Replace(UPLOADERFLAGS="") +# env.Replace(UPLOADCMD="$UPLOADER $SOURCES " + b64decode(ARGUMENTS.get("UPLOAD_PORT")) + "/" + b64decode(ARGUMENTS.get("PIOENV")) + ".bin") + +# pio >= 4.0.0 env.Replace(UPLOADER="scp") env.Replace(UPLOADERFLAGS="") -env.Replace(UPLOADCMD="$UPLOADER $SOURCES " + b64decode(ARGUMENTS.get("UPLOAD_PORT")) + "/" + b64decode(ARGUMENTS.get("PIOENV")) + ".bin") +env.Replace(UPLOADCMD='$UPLOADER $SOURCES "$UPLOAD_PORT/${PIOENV}.bin"') diff --git a/platformio.ini b/platformio.ini index 98b4ae83e..65f11515d 100644 --- a/platformio.ini +++ b/platformio.ini @@ -10,15 +10,17 @@ [platformio] src_dir = sonoff build_dir = .pioenvs +build_cache_dir = .cache ; *** Uncomment one of the lines below to build/upload only one environment ;default_envs = sonoff +;default_envs = sonoff-ircustom ; alternative to 'sonoff' with full IR protocols activated, you will need to disable some features to keep code not too big ;default_envs = sonoff-minimal ;default_envs = sonoff-basic -;default_envs = sonoff-classic ;default_envs = sonoff-knx ;default_envs = sonoff-sensors ;default_envs = sonoff-display +;default_envs = sonoff-ir ;default_envs = sonoff-BG ;default_envs = sonoff-BR ;default_envs = sonoff-CN @@ -48,12 +50,14 @@ build_flags = -D NDEBUG [core_2_3_0] ; *** Esp8266 core for Arduino version 2.3.0 +; *** W A R N I N G ! *** old outdated Arduino Esp8266 core with many security issues! NOT recommended to use. NO SUPPORT! platform = espressif8266@1.5.0 build_flags = ${esp82xx_defaults.build_flags} -Wl,-Tesp8266.flash.1m0.ld [core_2_4_2] ; *** Esp8266 core for Arduino version 2.4.2 +; *** W A R N I N G ! *** old outdated Arduino Esp8266 core with security issues. NOT recommended to use. NO SUPPORT! platform = espressif8266@1.8.0 build_flags = ${esp82xx_defaults.build_flags} -Wl,-Teagle.flash.1m0.ld @@ -66,16 +70,17 @@ build_flags = ${esp82xx_defaults.build_flags} -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH -DVTABLES_IN_FLASH -[core_2_5_2] -; *** Esp8266 core for Arduino version 2.5.2 -platform = espressif8266@~2.2.2 +[core_pre] +; *** Arduino Esp8266 core pre 2.6.x for Tasmota (recommended version, no known issues) +platform = https://github.com/Jason2866/platform-espressif8266.git#Tasmota build_flags = ${esp82xx_defaults.build_flags} - -Wl,-Teagle.flash.1m.ld -; Code optimization see https://github.com/esp8266/Arduino/issues/5790#issuecomment-475672473 + -Wl,-Tesp8266.flash.1m.ld -O2 -DBEARSSL_SSL_BASIC +; nonos-sdk 22y + -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22y ; nonos-sdk 22x - -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x +; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x ; nonos-sdk-pre-v3 ; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK3 ; lwIP 1.4 @@ -88,9 +93,58 @@ build_flags = ${esp82xx_defaults.build_flags} ; -DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY_LOW_FLASH ; lwIP 2 - Higher Bandwidth no Features (Tasmota default) -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH +; VTABLES in Flash (default) -DVTABLES_IN_FLASH +; VTABLES in Heap +; -DVTABLES_IN_DRAM +; VTABLES in IRAM +; -DVTABLES_IN_IRAM +; enable one option set -> No exception recommended +; No exception code in firmware -fno-exceptions -lstdc++ +; Exception code in firmware /needs much space! +; -fexceptions +; -lstdc++-exc + +[core_pre_ipv6] +; *** Arduino Esp8266 core pre 2.6.x IPv6 for Tasmota (use ONLY if you need IPv6, experimental!) +platform = https://github.com/Jason2866/platform-espressif8266.git#Tasmota +build_flags = ${esp82xx_defaults.build_flags} + -Wl,-Tesp8266.flash.1m.ld + -O2 + -DBEARSSL_SSL_BASIC +; nonos-sdk 22y + -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22y +; nonos-sdk 22x +; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x +; nonos-sdk-pre-v3 +; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK3 +; lwIP 1.4 +; -DPIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH +; lwIP 2 - Low Memory +; -DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY +; lwIP 2 - Higher Bandwidth +; -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH +; lwIP 2 - Higher Bandwidth Low Memory no Features +; -DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY_LOW_FLASH +; lwIP 2 - Higher Bandwidth no Features +; -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH +; lwIP 2 - Higher Bandwidth IPv6 + -DPIO_FRAMEWORK_ARDUINO_LWIP2_IPV6_HIGHER_BANDWIDTH +; VTABLES in Flash (default) + -DVTABLES_IN_FLASH +; VTABLES in Heap +; -DVTABLES_IN_DRAM +; VTABLES in IRAM +; -DVTABLES_IN_IRAM +; enable one option set -> No exception recommended +; No exception code in firmware + -fno-exceptions + -lstdc++ +; Exception code in firmware /needs much space! +; -fexceptions +; -lstdc++-exc [core_stage] ; *** Esp8266 core for Arduino version latest beta @@ -112,9 +166,9 @@ build_flags = ${esp82xx_defaults.build_flags} ; -DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY ; lwIP 2 - Higher Bandwidth ; -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH -; lwIP 2 - Higher Bandwitdh Low Memory no Features +; lwIP 2 - Higher Bandwidth Low Memory no Features ; -DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY_LOW_FLASH -; lwIP 2 - Higher Bandwitdh no Features +; lwIP 2 - Higher Bandwidth no Features (Tasmota default) -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH ; VTABLES in Flash (default) -DVTABLES_IN_FLASH @@ -130,55 +184,18 @@ build_flags = ${esp82xx_defaults.build_flags} ; -fexceptions ; -lstdc++-exc -[core_pre] -; *** Arduino Esp8266 core pre 2.6.x for Tasmota (mqtt reconnects fixed) -platform = https://github.com/Jason2866/platform-espressif8266.git#Tasmota -build_flags = ${esp82xx_defaults.build_flags} - -Wl,-Tesp8266.flash.1m.ld - -O2 - -DBEARSSL_SSL_BASIC -; nonos-sdk 22y - -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22y -; nonos-sdk 22x -; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x -; nonos-sdk-pre-v3 -; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK3 -; lwIP 1.4 -; -DPIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH -; lwIP 2 - Low Memory -; -DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY -; lwIP 2 - Higher Bandwidth -; -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH -; lwIP 2 - Higher Bandwitdh Low Memory no Features -; -DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY_LOW_FLASH -; lwIP 2 - Higher Bandwitdh no Features - -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH -; VTABLES in Flash (default) - -DVTABLES_IN_FLASH -; VTABLES in Heap -; -DVTABLES_IN_DRAM -; VTABLES in IRAM -; -DVTABLES_IN_IRAM -; enable one option set -> No exception recommended -; No exception code in firmware - -fno-exceptions - -lstdc++ -; Exception code in firmware /needs much space! -; -fexceptions -; -lstdc++-exc - [core_active] ; Select one core set for platform and build_flags -platform = ${core_2_3_0.platform} -build_flags = ${core_2_3_0.build_flags} +;platform = ${core_2_3_0.platform} +;build_flags = ${core_2_3_0.build_flags} ;platform = ${core_2_4_2.platform} ;build_flags = ${core_2_4_2.build_flags} -;platform = ${core_2_5_2.platform} -;build_flags = ${core_2_5_2.build_flags} +platform = ${core_pre.platform} +build_flags = ${core_pre.build_flags} +;platform = ${core_pre_ipv6.platform} +;build_flags = ${core_pre_ipv6.build_flags} ;platform = ${core_stage.platform} ;build_flags = ${core_stage.build_flags} -;platform = ${core_pre.platform} -;build_flags = ${core_pre.build_flags} [common] framework = arduino @@ -194,12 +211,13 @@ build_flags = ${core_active.build_flags} ; -DDEBUG_TASMOTA_SENSOR ; *** Optional Firmware configurations -; -DFIRMWARE_CLASSIC ; -DFIRMWARE_MINIMAL ; -DFIRMWARE_SENSORS ; -DFIRMWARE_BASIC ; -DFIRMWARE_KNX_NO_EMULATION ; -DFIRMWARE_DISPLAYS +; -DFIRMWARE_IR +; -DFIRMWARE_IR_CUSTOM ; -DUSE_CONFIG_OVERRIDE ; *** Fix espressif8266@1.7.0 induced undesired all warnings @@ -216,6 +234,7 @@ upload_resetmethod = nodemcu ; *** Upload Serial reset method for Wemos and NodeMCU upload_port = COM5 extra_scripts = pio/strip-floats.py + pio/obj-dump.py ; *** Upload file to OTA server using SCP ;upload_port = user@host:/path @@ -269,20 +288,6 @@ upload_resetmethod = ${common.upload_resetmethod} upload_speed = ${common.upload_speed} extra_scripts = ${common.extra_scripts} -[env:sonoff-classic] -platform = ${common.platform} -framework = ${common.framework} -board = ${common.board} -board_build.flash_mode = ${common.board_build.flash_mode} -board_build.f_cpu = ${common.board_build.f_cpu} -build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags} -DFIRMWARE_CLASSIC -monitor_speed = ${common.monitor_speed} -upload_port = ${common.upload_port} -upload_resetmethod = ${common.upload_resetmethod} -upload_speed = ${common.upload_speed} -extra_scripts = ${common.extra_scripts} - [env:sonoff-knx] platform = ${common.platform} framework = ${common.framework} @@ -325,6 +330,34 @@ upload_resetmethod = ${common.upload_resetmethod} upload_speed = ${common.upload_speed} extra_scripts = ${common.extra_scripts} +[env:sonoff-ir] +platform = ${common.platform} +framework = ${common.framework} +board = ${common.board} +board_build.flash_mode = ${common.board_build.flash_mode} +board_build.f_cpu = ${common.board_build.f_cpu} +build_unflags = ${common.build_unflags} +build_flags = ${common.build_flags} -DUSE_IR_REMOTE_FULL -DFIRMWARE_IR +monitor_speed = ${common.monitor_speed} +upload_port = ${common.upload_port} +upload_resetmethod = ${common.upload_resetmethod} +upload_speed = ${common.upload_speed} +extra_scripts = ${common.extra_scripts} + +[env:sonoff-ircustom] +platform = ${common.platform} +framework = ${common.framework} +board = ${common.board} +board_build.flash_mode = ${common.board_build.flash_mode} +board_build.f_cpu = ${common.board_build.f_cpu} +build_unflags = ${common.build_unflags} +build_flags = ${common.build_flags} -DUSE_IR_REMOTE_FULL +monitor_speed = ${common.monitor_speed} +upload_port = ${common.upload_port} +upload_resetmethod = ${common.upload_resetmethod} +upload_speed = ${common.upload_speed} +extra_scripts = ${common.extra_scripts} + [env:sonoff-BG] platform = ${common.platform} framework = ${common.framework} diff --git a/scripter.md b/scripter.md index 43c966b6b..fe1380194 100644 --- a/scripter.md +++ b/scripter.md @@ -21,6 +21,7 @@ e.g. temp=hum\*(100/37.5)+temp-(timer\*hum%10) no spaces allowed between math operations Comparison operators **==,!=,\>,\>=,<,<=** **and** , **or** support +hexadecimal numbers are supported with prefix 0x strings support **+** and **+=** operators string comparison **==,!=** @@ -48,10 +49,14 @@ memory is dynamically allocated as a result of the D section. copying a string to a number or reverse is supported >**\>B** -executed on BOOT time +executed on BOOT time and script save >**\>T** executed on teleperiod time (**SENSOR** and **STATE**), get tele vars only in this section +remark: json variable names (like all others) may not contain math operators like - , you should set setoption64 1 to replace - with underscore + +>**\>F** +executed every 100 ms >**\>S** executed every second @@ -76,6 +81,8 @@ special variables (read only): **gtopic** = mqtt group topic **prefixn** = prefix n = 1-3 **pwr[x]** = tasmota power state (x = 1-N) +**pc[x]** = tasmota pulse counter value (x = 1-4) +**tbut[x]** = touch screen button state (x = 1-N) **sw[x]** = tasmota switch state (x = 1-N) >**pin[x]** = gpio pin level (x = 0-16) **pn[x]** = pin number for sensor code x, 99 if none @@ -87,7 +94,9 @@ special variables (read only): **med(n x)** = calculates a 5 value median filter of x (2 filters possible n=0,1) **int(x)** = gets the integer part of x (like floor) **hn(x)** = converts x (0..255) to a hex nibble string -**st(svar c n)** = stringtoken gets the n th substring of svar separated by c +**st(svar c n)** = stringtoken gets the n th substring of svar separated by c +**sl(svar)** = gets the length of a string +**sb(svar p n)** = gets a substring from svar at position p (if p<0 counts from end) and length n **s(x)** = explicit conversion from number x to string **mqtts** = state of mqtt disconnected=0, connected>0 **wifis** = state of wifi disconnected=0, connected>0 @@ -128,10 +137,11 @@ variable that special variable is discarded **Tasmota** cmds start with **=\>** within cmds you can replace text with variables with **%varname%** a single percent sign must be given as **%%** +**->** is equivalent but doesnt send mqtt or any weblog (silent execute, usefull to reduce traffic) **special** cmds: ->**=\> print** prints to info log for debugging +>**print** or **=\>print** prints to info log for debugging to save code space nearly no error messages are provided. However it is taken care of that at least it should not crash on syntax errors. if a variable does not exist a **???** is given on commands @@ -164,19 +174,24 @@ then remarks: the last closing bracket must be on a single line -the condition may not be enclosed in brackets +the condition may be enclosed in brackets +and on the same line conditions may be bracketed e.g. if ((a==b) and ((c==d) or (c==e)) and (s!="x")) + >**break** exits a section or terminates a for next loop **dpx** sets decimal precision to x (0-9) **svars** save permanent vars **delay(x)** pauses x milliseconds (should be as short as possible) **spin(x m)** set gpio pin x (0-16) to value m (0,1) only the last bit is used, so even values set the pin to zero and uneven values set the pin to 1 -**spinm(x m)** set pin mode gpio pin x (0-16) to mode m (input=0,output=1) +**spinm(x m)** set pin mode gpio pin x (0-16) to mode m (input=0,output=1,input with pullup=2) +**ws2812(array)** copies an array (defined with m:name) to the WS2812 LED chain the array should be defined as long as the number of pixels. the color is coded as 24 bit RGB +**hsvrgb(h s v)** converts hue (0-360), saturation (0-100) and value (0-100) to RGB color >**#name** names a subroutine, subroutines are called with **=#name** **#name(param)** names a subroutines with a parameter is called with **=#name(param)** subroutines end with the next '#' or '>' line or break, may be nested -params can be numbers or strings and on mismatch are converted +params can be numbers or strings and on mismatch are converted +**=(svar)** executes a script in a string variable (dynamic or self modifying code) >**for var from to inc** **next** @@ -186,7 +201,7 @@ specifies a for next loop, (loop count must not be less then 1) **case a** **case b** **ends** -specifies a switch case selector +specifies a switch case selector (numeric or string) **sd card support** enable by CARD_CS = gpio pin of card chip select (+ 10k flash) @@ -229,6 +244,18 @@ can be used e.g. to set variables e.g. **script >mintmp=15** more then one line may be executed seperated by a semicolon e.g. **script >mintmp=15;maxtemp=40** script itself cant be set because the size would not fit the mqtt buffers +**subscribe,unsubscribe** +>if \#defined SUPPORT_MQTT_EVENT command subscribe and unsubscribe are supported. in contrast to rules no event is generated but the event name specifies a variable defined in D section and this variable is automatically set on transmission of the subscribed item + +**summary of optional defines** +>\#define USE_SCRIPT_FATFS CS_PIN : enables SD card support (on spi bus) also enables 4k script buffer +\#define USE_SCRIPT_FATFS_EXT : enables additional FS commands +\#define SDCARD_DIR : enables support for WEBUI for SD card directory up and download +\#define USE_24C256 : enables use of 24C256 i2c eeprom to expand script buffer (defaults to 4k) +\#define SUPPORT_MQTT_EVENT : enables support for subscribe unsubscribe +\#define USE_TOUCH_BUTTONS : enable virtual touch button support with touch displays + + ***example script*** meant to show some of the possibilities (actually this code ist too large) @@ -366,7 +393,7 @@ endif =\>WebSend %url% dimmer %dimmer% ; show on display -dprec0 +dp0 =\>displaytext [c1l1f1s2p20] dimmer=%dimmer% =\>print %upsecs% %uptime% %time% %sunrise% %sunset% %tstamp% @@ -524,7 +551,7 @@ str="" **\>B** ; set sensor file download link fl1("slog.txt") -; delete file in case we want to start fresh +; delete file in case we want to start fresh ;fd("slog.txt") @@ -613,7 +640,7 @@ punit=PressureUnit // update display every teleperiod time if upsecs%tper==0 then -dprec2 +dp2 =>%DT% [f1p7x0y5]%temp% %tunit% =>%DT% [p5x70y5]%hum% %%[x250y5t] =>%DT% [p11x140y5]%press% %punit% @@ -621,7 +648,7 @@ dprec2 =>%DT% [p10x160y25]eCO2: %eco2% ppm =>%DT% [p10c26l5]ahum: %ahum% g^m3 -dprec0 +dp0 =>%DT% [p25c1l5]WR 1 (Dach) : %wr1% W =>%DT% [p25c1l6]WR 2 (Garage): %-wr3% W =>%DT% [p25c1l7]WR 3 (Garten): %-wr2% W @@ -697,14 +724,14 @@ endif ; update graph every teleperiod if upsecs%tper==0 then -dprec2 +dp2 =>%DT% [f1Ci3x40y260w30Ci1] =>%DT% [Ci7x120y220t] =>%DT% [Ci7x180y220T] =>%DT% [Ci7p8x120y240]%temp% %tunit% =>%DT% [Ci7x120y260]%press% %punit% =>%DT% [Ci7x120y280]%dist% mm -dprec0 +dp0 =>%DT% [g0:%zwz%g1:%wr1%g2:%-wr2%g3:%-wr3%] if zwz>0 then diff --git a/sonoff/StackThunk_light.cpp b/sonoff/StackThunk_light.cpp index 1f0cafa10..5dcc20d62 100644 --- a/sonoff/StackThunk_light.cpp +++ b/sonoff/StackThunk_light.cpp @@ -40,7 +40,7 @@ uint32_t *stack_thunk_light_save = NULL; /* Saved A1 while in BearSSL */ uint32_t stack_thunk_light_refcnt = 0; //#define _stackSize (5600/4) -#ifdef USE_MQTT_AWS_IOT +#if defined(USE_MQTT_AWS_IOT) || defined(USE_MQTT_TLS_FORCE_EC_CIPHER) #define _stackSize (5300/4) // using a light version of bearssl we can save 300 bytes #else #define _stackSize (3600/4) // using a light version of bearssl we can save 2k diff --git a/sonoff/WiFiClientSecureLightBearSSL.cpp b/sonoff/WiFiClientSecureLightBearSSL.cpp index 5bd7db790..20992a9dd 100644 --- a/sonoff/WiFiClientSecureLightBearSSL.cpp +++ b/sonoff/WiFiClientSecureLightBearSSL.cpp @@ -21,7 +21,8 @@ */ #include "my_user_config.h" -#ifdef USE_MQTT_TLS +//#ifdef USE_MQTT_TLS +#if defined(USE_MQTT_TLS) || defined (USE_SENDMAIL) //#define DEBUG_TLS @@ -758,7 +759,7 @@ extern "C" { // We limit to a single cipher to reduce footprint // we reference it, don't put in PROGMEM static const uint16_t suites[] = { -#ifdef USE_MQTT_AWS_IOT +#if defined(USE_MQTT_AWS_IOT) || defined(USE_MQTT_TLS_FORCE_EC_CIPHER) BR_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 #else BR_TLS_RSA_WITH_AES_128_GCM_SHA256 @@ -785,8 +786,8 @@ extern "C" { br_ssl_engine_set_aes_ctr(&cc->eng, &br_aes_small_ctr_vtable); br_ssl_engine_set_ghash(&cc->eng, &br_ghash_ctmul32); -#ifdef USE_MQTT_AWS_IOT - // we support only P256 EC curve for AWS IoT, no EC curve for Letsencrypt +#if defined(USE_MQTT_AWS_IOT) || defined(USE_MQTT_TLS_FORCE_EC_CIPHER) + // we support only P256 EC curve for AWS IoT, no EC curve for Letsencrypt unless forced br_ssl_engine_set_ec(&cc->eng, &br_ec_p256_m15); #endif } diff --git a/sonoff/WiFiClientSecureLightBearSSL.h b/sonoff/WiFiClientSecureLightBearSSL.h index 653c75502..5dd0df35e 100644 --- a/sonoff/WiFiClientSecureLightBearSSL.h +++ b/sonoff/WiFiClientSecureLightBearSSL.h @@ -24,7 +24,7 @@ #ifndef wificlientlightbearssl_h #define wificlientlightbearssl_h -#ifdef USE_MQTT_TLS +#if defined(USE_MQTT_TLS) || defined (USE_SENDMAIL) #include #include "WiFiClient.h" #include diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index 0b338c5fb..1c37b3664 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -1,4 +1,178 @@ /*********************************************************************************************\ + * 6.6.0.21 20191022 + * Remove support for WPS and SmartConfig in favour of Web server (!) based WifiManager (#6680) + * Remove binary sonoff-classic (#6680) + * Remove command SetOption2 + * + * 6.6.0.20 20191018 + * Add command SetOption65 0/1 to disable (1) fast power cycle detection fixing unwanted brownout trigger + * Add absolute PowerDelta using command PowerDelta 101..32000 where 101 = 101-100 = 1W, 202 = 202-100 = 102W (#5901) + * Add support for EX-Store WiFi Dimmer V4 (#5856) + * Add ZigbeeRead command and many improvements (#6095) + * Add ArduinoSlave driver (EXPERIMENTAL) + * + * 6.6.0.19 20191018 + * Replace obsolete xsns_23_sdm120 with xnrg_08_sdm120 and consolidate define USE_SDM120 + * Replace obsolete xsns_25_sdm630 with xnrg_10_sdm630 and consolidate define USE_SDM630 + * Replace obsolete xsns_49_solaxX1 with xnrg_12_solaxX1 (#6677) + * + * 6.6.0.18 20191010 + * Add command DimmerRange in Light module to support 2 byte dimming ranges from Tuya + * Add Zigbee additional commands and sending messages to control devices (#6095) + * Fix Rules were not triggered with IR unknown protocol or in sonoff-it (#6629) + * Add define USE_DEEPSLEEP and command DeepSleepTime 0 or 10..86400 (seconds) to enter deepsleep mode (#6638) + * Add define USE_SONOFF_RF to enable/disable Sonoff Rf support (#6648) + * Add incremental beeps to Ifan03 remote control fan speed buttons (#6636) + * Add rule support after every command execution like Fanspeed#Data=2 (#6636) + * Fix handling of ligth channels when pwm_multichannel (Option68) is enabled + * Add WebUI for multiple, independent PWM channels + * Remove default DS18B20 driver and only support define DS18x20 (#6647) + * Add support for PMS3003 dust particle sensor + * Change Sonoff L1 support by adding define USE_SONOFF_L1 + * + * 6.6.0.17 20191009 + * Add command SetOption34 0..255 to set backlog delay. Default value is 200 (mSeconds) (#6562) + * Add command Gpio 255 to show physical GPIO configuration of all non-flash pins (#6407) + * + * 6.6.0.16 20191008 + * Change PZEM004T default address mask from 0.0.0.x to 192.168.1.x for legacy reason (#6585) + * Fix PZEM004T, PZEMAC and PZEMDC autodetection (#6585) + * Change light drivers internals to ease management + * + * 6.6.0.15 20191003 + * Change command PulseTime JSON message format and allow display of all pulsetimer information (#6519) + * Add support for Chint DDSU666 Modbus energy meter by Pablo Zerón + * Add support for SM2135 as used in Action LSC Smart Led E14 (#6495) + * Add command SetOption72 0/1 to switch between software (0) or hardware (1) energy total counter (#6561) + * Add Zigbee tracking of connected devices and auto-probing of Manuf/Model Ids + * Fix better handling of PWM White Temperature mode for Module 48 (#6534) + * + * 6.6.0.14 20190925 + * Change command Tariffx to allow time entries like 23 (hours), 1320 (minutes) or 23:00. NOTE: As this is development branch previous tariffs are lost! (#6488) + * Remove support for define USE_DS18x20_LEGACY and legacy DS18x20 driver (#6486) + * Add initial support for MQTT logging using command MqttLog (#6498) + * Add Zigbee more support - collect endpoints and clusters, added ZigbeeDump command + * Add initial support for shutters by Stefan Bode (#288) + * Add command to MCP230xx: sensor29 pin,0/1/2 for OFF/ON/TOGGLE + * Add initial support for PCF8574 I2C I/O Expander (currently output only) by Stefan Bode + * Add command SetOption71 0/1 to switch between different Modbus Active Energy registers on DDS238-2 energy meters (#6531) + * Change command SetOption43 to make it more general. Now supports PS_16_DZ driver too (#6544) + * Change command handling by moving buffers up in chain solving MQTTlog support (#6529) + * Change detection of non-MQTT commands by allowing non-space characters as delimiter (#6540) + * Fix TasmotaSerial: move serial send to IRAM for high speed baud rates + * + * 6.6.0.13 20190922 + * Add command EnergyReset4 x,x to initialize total usage for two tarrifs + * Add command EnergyReset5 x,x to initialize total export (or production) for two tarrifs + * Add command Sensor34 8,0 and Sensor34 8,1 to disable/enable JSON message on weight change over 4 gram + * Add JSON array index support to rules evaluation allowing trigger on ENERGY#POWER[2]>0.60 from JSON ..,"Power":[0.00,0.68],.. (#6160) + * + * 6.6.0.12 20190910 + * Redesign command Tariff to now default to 0 (=disabled) and allowing to set both Standard Time (ST) and Daylight Savings Time (DST) start hour + * Commands Tariff1 22,23 = Tariff1 (Off-Peak) ST,DST Tariff2 (Standard) 6,7 = Tariff2 ST,DST Tariff9 0/1 = Weekend toggle (1 = Off-Peak during weekend) + * Change rename "Data" to "Hash" and limit to 32 bits when receiving UNKNOWN IR protocol (see DECODE_HASH from IRremoteESP8266) + * Add command Gpios 255/All to show all available GPIO components (#6407) + * Change JSON output format for commands Adc, Adcs, Modules, Gpio and Gpios from list to dictionary (#6407) + * Add Zigbee support phase 3 - support for Xiaomi lumi.weather air quality sensor, Osram mini-switch + * Change energy sensors for three phase/channel support + * Add support for Shelly 2.5 dual energy (#6160) + * Add initial support for up to three PZEM-014/-016 on serial modbus connection with addresses 1 (default), 2 and 3 (#2315) + * Add initial support for up to three PZEM-004T on serial connection with addresses x.x.x.1 (default), 2 and 3 (#2315) + * Add initial support for up to three PZEM-003/-017 on serial modbus connection with addresses 1 (default), 2 and 3 (#2315) + * Add driver USE_SDM630_2 as future replacement for USE_SDM630 - Pls test and report + * Add command ModuleAddress 1/2/3 to set Pzem module address when a single module is connected (#2315) + * + * 6.6.0.11 20190907 + * Change Settings crc calculation allowing short term backward compatibility + * Add support for up to 4 INA226 Voltage and Current sensors by Steve Rogers (#6342) + * Change Improve reliability of TasmotaSerial at 115200 bauds and reduce IRAM usage for Stage/pre-2.6 + * Add support for A4988 stepper-motor-driver-circuit by Tim Leuschner (#6370) + * Add support for Hiking DDS238-2 Modbus energy meter by Matteo Campanella (#6384) + * + * 6.6.0.10 20190905 + * Redesign Tuya support by Shantur Rathore removing commands SetOption34, 41, 44, 45, 46 and 65 (#6353) + * Add command Reset 99 to reset bootcount to zero (#684, #6351) + * Change command Time 1/2/3 to select JSON time format ISO, ISO + Epoch or Epoch for legacy reason + * + * 6.6.0.9 20190828 + * Change theoretical baudrate range to 300..19660500 bps in 300 increments (#6294) + * Add Full support of all protocols in IRremoteESP8266, to be used on dedicated-IR Tasmota version. Warning: +81k Flash when compiling with USE_IR_REMOTE_FULL + * Add compile time define USE_WS2812_HARDWARE to select hardware type WS2812, WS2812X, WS2813, SK6812, LC8812 or APA106 (DMA mode only) + * Add 'sonoff-ir' pre-packaged IR-dedicated firmware and 'sonoff-ircustom' to customize firmware with IR Full protocol support + * Add Zigbee support phase 2 - cc2530 initialization and basic ZCL decoding + * Add driver USE_SDM120_2 with Domoticz P1 Smart Meter functionality as future replacement for USE_SDM120 - Pls test and report + * Add command Power0 0/1/2/Off/On/Toggle to control all power outputs at once (#6340) + * Add time to more events (#6337) + * Add command Time 1/2/3 to select JSON time format ISO + Epoch, ISO or Epoch + * + * 6.6.0.8 20190827 + * Add Tuya Energy monitoring by Shantur Rathore + * Add phase 1 Domoticz P1 Smart Meter support using energy sensors handled by xdrv_03_energy.ino based on an idea by pablozg + * Add commands Tariff1 0..23 (start Off-Peak hour), Tariff2 0..23 (start Standard hour) and Tariff3 0/1 (Saturday and Sunday Off-Peak) + * + * 6.6.0.7 20190825 + * Expand Settings area to 4k for future use + * + * 6.6.0.6 20190819 + * Add I2C display driver for SH1106 oled by Gerhard Mutz + * Add SPI display drivers for epaper 4.2 inch, ILI9488 TFT, SSD1351 Color oled and RA8876 TFT by Gerhard Mutz + * Add support for HM17 bluetooth LE passive scan of ibeacon devices by Gerhard Mutz + * + * 6.6.0.5 20190816 + * Add command WebSensor 0/1 to control display of sensor data in web GUI (#6085) + * Change some table locations from RAM to Flash + * Fix wrong telemetry message when SetOption68 1 (#6191) + * Add support for RDM6300 125kHz RFID Reader by Gerhard Mutz + * + * 6.6.0.4 20190806 + * Add support for CHIRP soil moisture sensor by Christian Baars + * Add debug compile features using defines DEBUG_TASMOTA_CORE, DEBUG_TASMOTA_DRIVER and DEBUG_TASMOTA_SENSOR. + * See DEBUG_CORE_LOG example in sonoff.ino and DEBUG_DRIVER_LOG example in xdrv_09_timers.ino + * Add support for Solax X1 inverter by Pablo Zerón + * Add ZigBee support phase 1 - low level MQTT ZNP messages for CC2530 devices + * Add command Buzzer with optional parameters ,, enabled when a buzzer is configured (#5988) + * Add support for PAJ7620 gesture sensor by Christian Baars + * + * 6.6.0.3 20190725 + * Change filename of configuration backup from using FriendlyName1 to Hostname solving diacritic issues (#2422) + * Change Store AWS IoT Private Key and Certificate in SPI Flash avoiding device-specific compilations + * Upgrade library IRRemoteEsp8266 to 2.6.4, now using sendPioneer() + * Add support for MAX31865 Thermocouple sensor by Alberto Lopez Siemens + * Add option 0 to Width1 (Marker), Width2 (Second), Width3 (Minute) and Width4 (Hour) disabling display (#6152) + * Add MqttCount metric to STATE (#6155) + * Add define USE_ENERGY_MARGIN_DETECTION to disable Energy Margin and Power Limit detection + * Add define USE_ENERGY_POWER_LIMIT to disable Energy Power Limit detection while Energy Margin detection is active + * Add allow repeat/longpress for IRSend raw, introduced IRSend option (#6074) + * Add SetOption68 to enable multi-channel PWM instead of a single light (#6134) + * + * 6.6.0.2 20190714 + * Change commands Var and Mem to show all parameters when no index is given (#6107) + * Add command SetOption67 0/1 to disable or enable a buzzer as used in iFan03 + * Add command DisplayWidth to set pixel width on supported devices + * Add command DisplayHeight to set pixel height on supported devices + * Add support for Sonoff iFan03 as module 71 (#5988) + * Add support for a buzzer + * Add support for IRSend long press ('repeat' feature from IRRemoteESP8266) (#6074) + * Add support for IRHVAC Midea/Komeco protocol (#3227) + * Add support for more IRSend protocols enabled in my_user_config.h + * Add support for IRSend Pioneer protocol (#6100) + * Add Oled reset GPIO option "OLED reset" + * + * 6.6.0.1 20190708 + * Fix Domoticz battery level set to 100 if define USE_ADC_VCC is not used (#6033) + * Fix Force Elliptic Curve for Letsencrypt TLS #6042 + * Fix WeMo emulation for 1G echo and 2G echo dot (#6086) + * Fix Xiaomi Philips brightness (#6091) + * Change defines USE_TX20_WIND_SENSOR and USE_RC_SWITCH in my_user_config.h to disable to lower iram usage enabling latest core compilation (#6060, #6062) + * Add blend RGB leds with White leds for better whites (#5895, #5704) + * Add command SetOption41 0..8 to control number of Tuya switches (#6039) + * Add command SetOption42 0..255 to set overtemperature (Celsius only) threshold resulting in power off all on energy monitoring devices. Default setting is 90 (#6036) + * Add command SetOption66 0/1 to enable or disable Tuya dimmer range 255 slider control + * Add command Time to disable NTP and set UTC time as Epoch value if above 1451602800 (=20160101). Time 0 re-enables NTP (#5279) + * Add AZ7798 automatic setting of clock display (#6034) + * Add Epoch and UptimeSec to JSON messages (#6068) + * Add support for up to 4 INA219 sensors (#6046) + * * 6.6.0 20190707 * Remove support of TLS on core 2.3.0 and extent support on core 2.4.2 and up * Remove MQTT uptime message every hour @@ -8,7 +182,7 @@ * Refactor TLS based on BearSSL, warning breaking change for fingerprints validation * Refactor management of lights, using classes and integers instead of floats * Refactor UDP initial message handling from string to char using static memory and add debug info (#5505) - * Refactor IRsend and receive for 64-bit support (#5523) + * Refactor IRSend and receive for 64-bit support (#5523) * Refactor MQTT which might solve issue (#5755) * Refactor IRSend by using heap when more than 199 values need to be send. May need increase of define MQTT_MAX_PACKET_SIZE too (#5950) * Refactor double to float in rules, and replaced trigonometric functions from stdlib with smaller versions (#6005) diff --git a/sonoff/i18n.h b/sonoff/i18n.h index e4342f5ea..d8d0db151 100644 --- a/sonoff/i18n.h +++ b/sonoff/i18n.h @@ -61,8 +61,8 @@ #define D_JSON_ERASE "Erase" #define D_JSON_ERROR "Error" #define D_JSON_EVERY "Every" -#define D_JSON_EXPORT_ACTIVE "ExportActivePower" -#define D_JSON_EXPORT_REACTIVE "ExportReactivePower" +#define D_JSON_EXPORT_ACTIVE "ExportActive" +#define D_JSON_EXPORT_REACTIVE "ExportReactive" #define D_JSON_FAILED "Failed" #define D_JSON_FALLBACKTOPIC "FallbackTopic" #define D_JSON_FEATURES "Features" @@ -86,8 +86,8 @@ #define D_JSON_I2CSCAN_NO_DEVICES_FOUND "No devices found" #define D_JSON_ID "Id" #define D_JSON_ILLUMINANCE "Illuminance" -#define D_JSON_IMPORT_ACTIVE "ImportActivePower" -#define D_JSON_IMPORT_REACTIVE "ImportReactivePower" +#define D_JSON_IMPORT_ACTIVE "ImportActive" +#define D_JSON_IMPORT_REACTIVE "ImportReactive" #define D_JSON_INFRARED "Infrared" #define D_JSON_UNKNOWN "Unknown" #define D_JSON_LIGHT "Light" @@ -96,6 +96,7 @@ #define D_JSON_LOW "Low" #define D_JSON_MAC "Mac" #define D_JSON_MASK "Mask" +#define D_JSON_MEMORY_ERROR "Memory error" #define D_JSON_MINIMAL "minimal" #define D_JSON_MODEL "Model" #define D_JSON_MQTT_COUNT "MqttCount" @@ -117,11 +118,14 @@ #define D_JSON_PROGRAMFLASHSIZE "ProgramFlashSize" #define D_JSON_PROGRAMSIZE "ProgramSize" #define D_JSON_REFERENCETEMPERATURE "ReferenceTemperature" +#define D_JSON_REMAINING "Remaining" #define D_JSON_RESET "Reset" +#define D_JSON_RESISTANCE "Resistance" #define D_JSON_RESOLUTION "Resolution" #define D_JSON_RESTARTING "Restarting" #define D_JSON_RESTARTREASON "RestartReason" #define D_JSON_RSSI "RSSI" +#define D_JSON_RUNTIME "Runtime" #define D_JSON_SAVEADDRESS "SaveAddress" #define D_JSON_SAVECOUNT "SaveCount" #define D_JSON_SAVED "Saved" @@ -129,10 +133,12 @@ #define D_JSON_SDKVERSION "SDK" #define D_JSON_SELECTED "selected" #define D_JSON_SERIALRECEIVED "SerialReceived" +#define D_JSON_SET "Set" #define D_JSON_SSID "SSId" #define D_JSON_STARTDST "StartDST" // Start Daylight Savings Time #define D_JSON_STARTED "Started" #define D_JSON_STARTUPUTC "StartupUTC" +#define D_JSON_STATUS "Status" #define D_JSON_SUBNETMASK "Subnetmask" #define D_JSON_SUCCESSFUL "Successful" #define D_JSON_SUNRISE "Sunrise" @@ -145,7 +151,7 @@ #define D_JSON_TODAY "Today" #define D_JSON_TOTAL "Total" #define D_JSON_TOTAL_USAGE "TotalUsage" -#define D_JSON_TOTAL_REACTIVE "TotalReactivePower" +#define D_JSON_TOTAL_REACTIVE "TotalReactive" #define D_JSON_TOTAL_START_TIME "TotalStartTime" #define D_JSON_TVOC "TVOC" #define D_JSON_TYPE "Type" @@ -164,6 +170,15 @@ #define D_JSON_WRONG_PARAMETERS "Wrong parameters" #define D_JSON_YESTERDAY "Yesterday" #define D_JSON_ZERO_POINT_CALIBRATION "Zero Point Calibration" +#define D_JSON_PV1_VOLTAGE "Pv1Voltage" +#define D_JSON_PV1_CURRENT "Pv1Current" +#define D_JSON_PV1_POWER "Pv1Power" +#define D_JSON_PV2_VOLTAGE "Pv2Voltage" +#define D_JSON_PV2_CURRENT "Pv2Current" +#define D_JSON_PV2_POWER "Pv2Power" +#define D_JSON_SOLAR_POWER "SolarPower" +#define D_JSON_USAGE "Usage" +#define D_JSON_EXPORT "Export" #define D_RSLT_ENERGY "ENERGY" #define D_RSLT_HASS_STATE "HASS_STATE" @@ -223,9 +238,6 @@ #define D_CMND_PWM "PWM" #define D_CMND_PWMFREQUENCY "PWMFrequency" #define D_CMND_PWMRANGE "PWMRange" -#define D_CMND_COUNTER "Counter" -#define D_CMND_COUNTERTYPE "CounterType" -#define D_CMND_COUNTERDEBOUNCE "CounterDebounce" #define D_CMND_BUTTONDEBOUNCE "ButtonDebounce" #define D_CMND_SWITCHDEBOUNCE "SwitchDebounce" #define D_CMND_SLEEP "Sleep" @@ -244,11 +256,8 @@ #define D_CMND_PASSWORD "Password" #define D_CMND_HOSTNAME "Hostname" #define D_CMND_WIFICONFIG "WifiConfig" - #define WCFG_MAX_STRING_LENGTH 12 #define D_WCFG_0_RESTART "Restart" - #define D_WCFG_1_SMARTCONFIG "SmartConfig" #define D_WCFG_2_WIFIMANAGER "WifiManager" - #define D_WCFG_3_WPSCONFIG "WPSConfig" #define D_WCFG_4_RETRY "Retry" #define D_WCFG_5_WAIT "Wait" #define D_WCFG_6_SERIAL "Serial" @@ -262,6 +271,7 @@ #define D_CMND_RESET "Reset" #define D_JSON_RESET_AND_RESTARTING "Reset and Restarting" #define D_JSON_ONE_TO_RESET "1 to reset" +#define D_CMND_TIME "Time" #define D_CMND_TIMEZONE "Timezone" #define D_CMND_TIMESTD "TimeStd" #define D_CMND_TIMEDST "TimeDst" @@ -280,6 +290,7 @@ #define D_JSON_BASE "BASE" // Commands xdrv_01_mqtt.ino +#define D_CMND_MQTTLOG "MqttLog" #define D_CMND_MQTTHOST "MqttHost" #define D_CMND_MQTTPORT "MqttPort" #define D_CMND_MQTTRETRY "MqttRetry" @@ -288,9 +299,9 @@ #define D_CMND_MQTTCLIENT "MqttClient" #define D_CMND_MQTTUSER "MqttUser" #define D_CMND_MQTTPASSWORD "MqttPassword" +#define D_CMND_TLSKEY "TLSKey" #define D_CMND_FULLTOPIC "FullTopic" #define D_CMND_PREFIX "Prefix" - #define PRFX_MAX_STRING_LENGTH 5 #define D_CMND "cmnd" #define D_STAT "stat" #define D_TELE "tele" @@ -315,7 +326,9 @@ #define D_CMND_WEBREFRESH "WebRefresh" #define D_CMND_WEBSEND "WebSend" #define D_CMND_WEBCOLOR "WebColor" +#define D_CMND_WEBSENSOR "WebSensor" #define D_CMND_EMULATION "Emulation" +#define D_CMND_SENDMAIL "Sendmail" // Commands xdrv_03_energy.ino #define D_CMND_POWERLOW "PowerLow" @@ -349,6 +362,7 @@ #define D_CMND_COLOR "Color" #define D_CMND_COLORTEMPERATURE "CT" #define D_CMND_DIMMER "Dimmer" +#define D_CMND_DIMMER_RANGE "DimmerRange" #define D_CMND_HSBCOLOR "HSBColor" #define D_CMND_LED "Led" #define D_CMND_LEDTABLE "LedTable" @@ -372,13 +386,30 @@ #define D_JSON_IR_PROTOCOL "Protocol" #define D_JSON_IR_BITS "Bits" #define D_JSON_IR_DATA "Data" + #define D_JSON_IR_DATALSB "DataLSB" + #define D_JSON_IR_HASH "Hash" #define D_JSON_IR_RAWDATA "RawData" + #define D_JSON_IR_REPEAT "Repeat" #define D_CMND_IRHVAC "IRHVAC" - #define D_JSON_IRHVAC_VENDOR "VENDOR" - #define D_JSON_IRHVAC_POWER "POWER" - #define D_JSON_IRHVAC_MODE "MODE" - #define D_JSON_IRHVAC_FANSPEED "FANSPEED" - #define D_JSON_IRHVAC_TEMP "TEMP" + #define D_JSON_IRHVAC_VENDOR "Vendor" + #define D_JSON_IRHVAC_PROTOCOL "Protocol" + #define D_JSON_IRHVAC_MODEL "Model" + #define D_JSON_IRHVAC_POWER "Power" + #define D_JSON_IRHVAC_MODE "Mode" + #define D_JSON_IRHVAC_FANSPEED "FanSpeed" + #define D_JSON_IRHVAC_TEMP "Temp" + #define D_JSON_IRHVAC_CELSIUS "Celsius" + #define D_JSON_IRHVAC_SWINGV "SwingV" + #define D_JSON_IRHVAC_SWINGH "SwingH" + #define D_JSON_IRHVAC_LIGHT "Light" + #define D_JSON_IRHVAC_BEEP "Beep" + #define D_JSON_IRHVAC_ECONO "Econo" + #define D_JSON_IRHVAC_FILTER "Filter" + #define D_JSON_IRHVAC_TURBO "Turbo" + #define D_JSON_IRHVAC_QUIET "Quiet" + #define D_JSON_IRHVAC_CLEAN "Clean" + #define D_JSON_IRHVAC_SLEEP "Sleep" + #define D_JSON_IRHVAC_CLOCK "Clock" #define D_JSON_IRRECEIVED "IrReceived" // Commands xdrv_06_snfbridge.ino @@ -398,14 +429,6 @@ #define D_JSON_RFRECEIVED "RfReceived" #define D_CMND_RFRAW "RfRaw" -// Commands xdrv_07_domoticz.ino -#define D_CMND_DOMOTICZ "Domoticz" -#define D_CMND_IDX "Idx" -#define D_CMND_KEYIDX "KeyIdx" -#define D_CMND_SWITCHIDX "SwitchIdx" -#define D_CMND_SENSORIDX "SensorIdx" -#define D_CMND_UPDATETIMER "UpdateTimer" - // Commands xdrv_08_serial_bridge.ino #define D_CMND_SSERIALSEND "SSerialSend" #define D_CMND_SBAUDRATE "SBaudrate" @@ -426,6 +449,42 @@ #define D_CMND_LATITUDE "Latitude" #define D_CMND_LONGITUDE "Longitude" +// Commands xdrv_16_tuyadimmer.ino + +#define D_CMND_TUYA_MCU "TuyaMCU" +#define D_JSON_TUYA_MCU_RECEIVED "TuyaMcuReceived" + +// Commands xdrv_23_zigbee.ino +#define D_CMND_ZIGBEE_PERMITJOIN "ZigbeePermitJoin" +#define D_CMND_ZIGBEE_STATUS "ZigbeeStatus" +#define D_CMND_ZIGBEE_RESET "ZigbeeReset" + #define D_JSON_ZIGBEE_CC2530 "CC2530" +#define D_CMND_ZIGBEEZNPSEND "ZigbeeZNPSend" + #define D_JSON_ZIGBEE_STATUS "ZigbeeStatus" + #define D_JSON_ZIGBEEZNPRECEIVED "ZigbeeZNPReceived" + #define D_JSON_ZIGBEEZNPSENT "ZigbeeZNPSent" + #define D_JSON_ZIGBEEZCL_RECEIVED "ZigbeeZCLReceived" + #define D_JSON_ZIGBEEZCL_RAW_RECEIVED "ZigbeeZCLRawReceived" + #define D_JSON_ZIGBEE_DEVICE "Device" + #define D_JSON_ZIGBEE_NAME "Name" +#define D_CMND_ZIGBEE_ZCL_SEND "ZigbeeZCLSend" + #define D_JSON_ZIGBEE_ZCL_SENT "ZigbeeZCLSent" +#define D_CMND_ZIGBEE_PROBE "ZigbeeProbe" +#define D_CMND_ZIGBEE_RECEIVED "ZigbeeReceived" + #define D_CMND_ZIGBEE_LINKQUALITY "LinkQuality" +#define D_CMND_ZIGBEE_READ "ZigbeeRead" + + // Commands xdrv_25_A4988_Stepper.ino + #ifdef USE_A4988_STEPPER + #define D_CMND_MOTOR "MOTOR" + #define D_JSON_MOTOR_MOVE "doMove" + #define D_JSON_MOTOR_ROTATE "doRotate" + #define D_JSON_MOTOR_TURN "doTurn" + #define D_JSON_MOTOR_SPR "setSPR" + #define D_JSON_MOTOR_RPM "setRPM" + #define D_JSON_MOTOR_MIS "setMIS" + #endif + /********************************************************************************************/ #define D_ASTERISK_PWD "****" @@ -443,59 +502,12 @@ #endif // Common -enum UnitNames { - UNIT_AMPERE, - UNIT_HOUR, - UNIT_KILOOHM, - UNIT_KILOWATTHOUR, - UNIT_LUX, - UNIT_MICROSECOND, - UNIT_MILLIAMPERE, - UNIT_MILLIMETER_MERCURY, - UNIT_MILLISECOND, - UNIT_MINUTE, - UNIT_PPB, - UNIT_PPD, - UNIT_PPM, - UNIT_PERCENTAGE, - UNIT_PRESSURE, - UNIT_SECOND, - UNIT_SECTORS, - UNIT_VOLT, - UNIT_WATT, - UNIT_WATTHOUR, - UNIT_HERTZ }; -const char kUnitNames[] PROGMEM = - D_UNIT_AMPERE "|" - D_UNIT_HOUR "|" - D_UNIT_KILOOHM "|" - D_UNIT_KILOWATTHOUR "|" - D_UNIT_LUX "|" - D_UNIT_MICROSECOND "|" - D_UNIT_MILLIAMPERE "|" - D_UNIT_MILLIMETER_MERCURY "|" - D_UNIT_MILLISECOND "|" - D_UNIT_MINUTE "|" - D_UNIT_PARTS_PER_BILLION "|" - D_UNIT_PARTS_PER_DECILITER "|" - D_UNIT_PARTS_PER_MILLION "|" - "%|" - D_UNIT_PRESSURE "|" - D_UNIT_SECOND "|" - D_UNIT_SECTORS "|" - D_UNIT_VOLT "|" - D_UNIT_WATT "|" - D_UNIT_WATTHOUR "|" - D_UNIT_HERTZ ; - const char S_JSON_COMMAND_NVALUE_SPACE_UNIT[] PROGMEM = "{\"%s\":\"%d %s\"}"; const char S_JSON_COMMAND_LVALUE_SPACE_UNIT[] PROGMEM = "{\"%s\":\"%lu %s\"}"; const char S_JSON_COMMAND_SVALUE_SPACE_UNIT[] PROGMEM = "{\"%s\":\"%s %s\"}"; -const char S_JSON_COMMAND_NVALUE_UNIT[] PROGMEM = "{\"%s\":\"%d%s\"}"; -const char S_JSON_COMMAND_NVALUE_UNIT_NVALUE_UNIT[] PROGMEM = "{\"%s\":\"%d%s (%d%s)\"}"; -const char S_JSON_COMMAND_NVALUE_SVALUE[] PROGMEM = "{\"%s\":\"%d (%s)\"}"; -const char S_JSON_COMMAND_NVALUE_ACTIVE_NVALUE[] PROGMEM = "{\"%s\":\"%d (" D_JSON_ACTIVE " %d)\"}"; +const char S_JSON_COMMAND_NVALUE_SVALUE[] PROGMEM = "{\"%s\":{\"%d\":\"%s\"}}"; +const char S_JSON_COMMAND_NVALUE_ACTIVE_NVALUE[] PROGMEM = "{\"%s\":{\"%d\":{\"" D_JSON_ACTIVE "\":\"%d\"}}}"; const char S_JSON_COMMAND_NVALUE[] PROGMEM = "{\"%s\":%d}"; const char S_JSON_COMMAND_LVALUE[] PROGMEM = "{\"%s\":%lu}"; @@ -508,7 +520,6 @@ const char S_JSON_COMMAND_INDEX_LVALUE[] PROGMEM = "{\"%s%d\":%lu}"; const char S_JSON_COMMAND_INDEX_SVALUE[] PROGMEM = "{\"%s%d\":\"%s\"}"; const char S_JSON_COMMAND_INDEX_ASTERISK[] PROGMEM = "{\"%s%d\":\"" D_ASTERISK_PWD "\"}"; const char S_JSON_COMMAND_INDEX_SVALUE_SVALUE[] PROGMEM = "{\"%s%d\":\"%s%s\"}"; -const char S_JSON_COMMAND_INDEX_NVALUE_ACTIVE_NVALUE[] PROGMEM = "{\"%s%d\":\"%d (" D_JSON_ACTIVE " %d)\"}"; const char S_JSON_SENSOR_INDEX_NVALUE[] PROGMEM = "{\"" D_CMND_SENSOR "%d\":%d}"; const char S_JSON_SENSOR_INDEX_SVALUE[] PROGMEM = "{\"" D_CMND_SENSOR "%d\":\"%s\"}"; @@ -534,39 +545,9 @@ const char S_RSLT_WARNING[] PROGMEM = D_RSLT_WARNING; const char S_LWT[] PROGMEM = D_LWT; const char S_OFFLINE[] PROGMEM = D_OFFLINE; -// sonoff.ino -#define MAX_BUTTON_COMMANDS 5 // Max number of button commands supported -const char kCommands[MAX_BUTTON_COMMANDS][14] PROGMEM = { - D_CMND_WIFICONFIG " 1", // Press button three times - D_CMND_WIFICONFIG " 2", // Press button four times - D_CMND_WIFICONFIG " 3", // Press button five times - D_CMND_RESTART " 1", // Press button six times - D_CMND_UPGRADE " 1" }; // Press button seven times -const char kWifiConfig[MAX_WIFI_OPTION][WCFG_MAX_STRING_LENGTH] PROGMEM = { - D_WCFG_0_RESTART, - D_WCFG_1_SMARTCONFIG, - D_WCFG_2_WIFIMANAGER, - D_WCFG_3_WPSCONFIG, - D_WCFG_4_RETRY, - D_WCFG_5_WAIT, - D_WCFG_6_SERIAL, - D_WCFG_7_WIFIMANAGER_RESET_ONLY }; -const char kPrefixes[3][PRFX_MAX_STRING_LENGTH] PROGMEM = { - D_CMND, - D_STAT, - D_TELE }; - -const char kCodeImage[] PROGMEM = "sonoff|minimal|classic|sensors|knx|basic|display"; - // support.ino static const char kMonthNames[] = D_MONTH3LIST; -const char kOptionOff[] PROGMEM = "OFF|" D_OFF "|" D_FALSE "|" D_STOP "|" D_CELSIUS ; -const char kOptionOn[] PROGMEM = "ON|" D_ON "|" D_TRUE "|" D_START "|" D_FAHRENHEIT "|" D_USER ; -const char kOptionToggle[] PROGMEM = "TOGGLE|" D_TOGGLE "|" D_ADMIN ; -const char kOptionBlink[] PROGMEM = "BLINK|" D_BLINK ; -const char kOptionBlinkOff[] PROGMEM = "BLINKOFF|" D_BLINKOFF ; - // xdrv_02_webserver.ino #ifdef USE_WEBSERVER const char HTTP_SNS_TEMP[] PROGMEM = "{s}%s " D_TEMPERATURE "{m}%s°%c{e}"; // {s} = , {m} = , {e} = diff --git a/sonoff/language/bg-BG.h b/sonoff/language/bg-BG.h index aa9b3095f..d765614f6 100644 --- a/sonoff/language/bg-BG.h +++ b/sonoff/language/bg-BG.h @@ -284,6 +284,7 @@ #define D_LOGGING_PARAMETERS "Параметри на лога" #define D_SERIAL_LOG_LEVEL "Степен на серийния лог" +#define D_MQTT_LOG_LEVEL "Mqtt log level" #define D_WEB_LOG_LEVEL "Степен на уеб лога" #define D_SYS_LOG_LEVEL "Степен на системния лог" #define D_MORE_DEBUG "Още дебъгване" @@ -400,6 +401,7 @@ #define D_DOMOTICZ_VOLTAGE "Напрежение/PM2,5" #define D_DOMOTICZ_CURRENT "Ток/PM10" #define D_DOMOTICZ_AIRQUALITY "Качество на въздуха" + #define D_DOMOTICZ_P1_SMART_METER "P1SmartMeter" #define D_DOMOTICZ_UPDATE_TIMER "Период на опресняване" // xdrv_09_timers.ino @@ -441,6 +443,19 @@ #define D_ENERGY_YESTERDAY "Използвана енергия вчера" #define D_ENERGY_TOTAL "Използвана енергия общо" +// xdrv_27_shutter.ino +#define D_OPEN "Open" +#define D_CLOSE "Close" +#define D_DOMOTICZ_SHUTTER "Shutter" + +// xdrv_28_pcf8574.ino +#define D_CONFIGURE_PCF8574 "Configure PCF8574" +#define D_PCF8574_PARAMETERS "PCF8574 parameters" +#define D_INVERT_PORTS "Invert Ports" +#define D_DEVICE "Device" +#define D_DEVICE_INPUT "Input" +#define D_DEVICE_OUTPUT "Output" + // xsns_05_ds18b20.ino #define D_SENSOR_BUSY "Датчикът DS18x20 е зает" #define D_SENSOR_CRC_ERROR "Датчик DS18x20 - грешка CRC" @@ -496,7 +511,6 @@ //xsns_43_hre.ino #define D_LOG_HRE "HRE: " - // sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box #define D_SENSOR_NONE "Няма" #define D_SENSOR_USER "Потребит." @@ -527,6 +541,9 @@ #define D_SENSOR_SAIR_TX "SAir Tx" #define D_SENSOR_SPI_CS "SPI CS" #define D_SENSOR_SPI_DC "SPI DC" +#define D_SENSOR_SPI_MISO "SPI MISO" +#define D_SENSOR_SPI_MOSI "SPI MOSI" +#define D_SENSOR_SPI_CLK "SPI CLK" #define D_SENSOR_BACKLIGHT "Подсветка" #define D_SENSOR_PMS5003 "PMS5003" #define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" @@ -586,6 +603,33 @@ #define D_SENSOR_HRE_CLOCK "HRE Clock" #define D_SENSOR_HRE_DATA "HRE Data" #define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" +#define D_SENSOR_BUZZER "Buzzer" +#define D_SENSOR_OLED_RESET "OLED Reset" +#define D_SENSOR_ZIGBEE_TXD "Zigbee Tx" +#define D_SENSOR_ZIGBEE_RXD "Zigbee Rx" +#define D_SENSOR_SOLAXX1_TX "SolaxX1 Tx" +#define D_SENSOR_SOLAXX1_RX "SolaxX1 Rx" +#define D_SENSOR_IBEACON_TX "iBeacon TX" +#define D_SENSOR_IBEACON_RX "iBeacon RX" +#define D_SENSOR_RDM6300_RX "RDM6300 RX" +#define D_SENSOR_CC1101_CS "CC1101 CS" +#define D_SENSOR_A4988_DIR "A4988 DIR" +#define D_SENSOR_A4988_STP "A4988 STP" +#define D_SENSOR_A4988_ENA "A4988 ENA" +#define D_SENSOR_A4988_MS1 "A4988 MS1" +#define D_SENSOR_A4988_MS2 "A4988 MS2" +#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_DDS2382_TX "DDS238-2 Tx" +#define D_SENSOR_DDS2382_RX "DDS238-2 Rx" +#define D_SENSOR_DDSU666_TX "DDSU666 Tx" +#define D_SENSOR_DDSU666_RX "DDSU666 Rx" +#define D_SENSOR_SM2135_CLK "SM2135 Clk" +#define D_SENSOR_SM2135_DAT "SM2135 Dat" +#define D_SENSOR_DEEPSLEEP "DeepSleep" +#define D_SENSOR_EXS_ENABLE "EXS Enable" +#define D_SENSOR_ARDUINO_TX "Arduino TX" +#define D_SENSOR_ARDUINO_RX "Arduino RX" +#define D_SENSOR_ARDUINO_RESET "Arduino RST" // Units #define D_UNIT_AMPERE "A" @@ -656,4 +700,28 @@ #define D_UNIT_KWARH "kVArh" #define D_UNIT_ANGLE "°" +//SOLAXX1 +#define D_PV1_VOLTAGE "PV1 Voltage" +#define D_PV1_CURRENT "PV1 Current" +#define D_PV1_POWER "PV1 Power" +#define D_PV2_VOLTAGE "PV2 Voltage" +#define D_PV2_CURRENT "PV2 Current" +#define D_PV2_POWER "PV2 Power" +#define D_SOLAR_POWER "Solar Power" +#define D_INVERTER_POWER "Inverter Power" +#define D_STATUS "Status" +#define D_WAITING "Waiting" +#define D_CHECKING "Checking" +#define D_WORKING "Working" +#define D_FAILURE "Failure" +#define D_SOLAX_ERROR_0 "No Error Code" +#define D_SOLAX_ERROR_1 "Grid Lost Fault" +#define D_SOLAX_ERROR_2 "Grid Voltage Fault" +#define D_SOLAX_ERROR_3 "Grid Frequency Fault" +#define D_SOLAX_ERROR_4 "Pv Voltage Fault" +#define D_SOLAX_ERROR_5 "Isolation Fault" +#define D_SOLAX_ERROR_6 "Over Temperature Fault" +#define D_SOLAX_ERROR_7 "Fan Fault" +#define D_SOLAX_ERROR_8 "Other Device Fault" + #endif // _LANGUAGE_BG_BG_H_ diff --git a/sonoff/language/cs-CZ.h b/sonoff/language/cs-CZ.h index b404b8a0f..fd654c148 100644 --- a/sonoff/language/cs-CZ.h +++ b/sonoff/language/cs-CZ.h @@ -284,6 +284,7 @@ #define D_LOGGING_PARAMETERS "Volba logování" #define D_SERIAL_LOG_LEVEL "Seriová úroveň logu" +#define D_MQTT_LOG_LEVEL "Mqtt log level" #define D_WEB_LOG_LEVEL "Webová úroveň logu" #define D_SYS_LOG_LEVEL "Systemová úroveň logu" #define D_MORE_DEBUG "Více debug informací" @@ -400,6 +401,7 @@ #define D_DOMOTICZ_VOLTAGE "Napětí/PM2,5" #define D_DOMOTICZ_CURRENT "Proud/PM10" #define D_DOMOTICZ_AIRQUALITY "AirQuality" + #define D_DOMOTICZ_P1_SMART_METER "P1SmartMeter" #define D_DOMOTICZ_UPDATE_TIMER "Aktualizace stopek" // xdrv_09_timers.ino @@ -441,6 +443,19 @@ #define D_ENERGY_YESTERDAY "Spotřeba Včera" #define D_ENERGY_TOTAL "Celková spotřeba" +// xdrv_27_shutter.ino +#define D_OPEN "Open" +#define D_CLOSE "Close" +#define D_DOMOTICZ_SHUTTER "Shutter" + +// xdrv_28_pcf8574.ino +#define D_CONFIGURE_PCF8574 "Configure PCF8574" +#define D_PCF8574_PARAMETERS "PCF8574 parameters" +#define D_INVERT_PORTS "Invert Ports" +#define D_DEVICE "Device" +#define D_DEVICE_INPUT "Input" +#define D_DEVICE_OUTPUT "Output" + // xsns_05_ds18b20.ino #define D_SENSOR_BUSY "Sensor DS18x20 obsazen" #define D_SENSOR_CRC_ERROR "Sensor DS18x20 chyba CRC" @@ -526,7 +541,10 @@ #define D_SENSOR_SAIR_TX "SAir Tx" #define D_SENSOR_SPI_CS "SPI CS" #define D_SENSOR_SPI_DC "SPI DC" -#define D_SENSOR_BACKLIGHT "BkLight" +#define D_SENSOR_SPI_MISO "SPI MISO" +#define D_SENSOR_SPI_MOSI "SPI MOSI" +#define D_SENSOR_SPI_CLK "SPI CLK" +#define D_SENSOR_BACKLIGHT "Backlight" #define D_SENSOR_PMS5003 "PMS5003" #define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" #define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" @@ -585,6 +603,33 @@ #define D_SENSOR_HRE_CLOCK "HRE Clock" #define D_SENSOR_HRE_DATA "HRE Data" #define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" +#define D_SENSOR_BUZZER "Buzzer" +#define D_SENSOR_OLED_RESET "OLED Reset" +#define D_SENSOR_ZIGBEE_TXD "Zigbee Tx" +#define D_SENSOR_ZIGBEE_RXD "Zigbee Rx" +#define D_SENSOR_SOLAXX1_TX "SolaxX1 Tx" +#define D_SENSOR_SOLAXX1_RX "SolaxX1 Rx" +#define D_SENSOR_IBEACON_TX "iBeacon TX" +#define D_SENSOR_IBEACON_RX "iBeacon RX" +#define D_SENSOR_RDM6300_RX "RDM6300 RX" +#define D_SENSOR_CC1101_CS "CC1101 CS" +#define D_SENSOR_A4988_DIR "A4988 DIR" +#define D_SENSOR_A4988_STP "A4988 STP" +#define D_SENSOR_A4988_ENA "A4988 ENA" +#define D_SENSOR_A4988_MS1 "A4988 MS1" +#define D_SENSOR_A4988_MS2 "A4988 MS2" +#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_DDS2382_TX "DDS238-2 Tx" +#define D_SENSOR_DDS2382_RX "DDS238-2 Rx" +#define D_SENSOR_DDSU666_TX "DDSU666 Tx" +#define D_SENSOR_DDSU666_RX "DDSU666 Rx" +#define D_SENSOR_SM2135_CLK "SM2135 Clk" +#define D_SENSOR_SM2135_DAT "SM2135 Dat" +#define D_SENSOR_DEEPSLEEP "DeepSleep" +#define D_SENSOR_EXS_ENABLE "EXS Enable" +#define D_SENSOR_ARDUINO_TX "Arduino TX" +#define D_SENSOR_ARDUINO_RX "Arduino RX" +#define D_SENSOR_ARDUINO_RESET "Arduino RST" // Units #define D_UNIT_AMPERE "A" @@ -655,4 +700,28 @@ #define D_UNIT_KWARH "kVArh" #define D_UNIT_ANGLE "Deg" +//SOLAXX1 +#define D_PV1_VOLTAGE "PV1 Voltage" +#define D_PV1_CURRENT "PV1 Current" +#define D_PV1_POWER "PV1 Power" +#define D_PV2_VOLTAGE "PV2 Voltage" +#define D_PV2_CURRENT "PV2 Current" +#define D_PV2_POWER "PV2 Power" +#define D_SOLAR_POWER "Solar Power" +#define D_INVERTER_POWER "Inverter Power" +#define D_STATUS "Status" +#define D_WAITING "Waiting" +#define D_CHECKING "Checking" +#define D_WORKING "Working" +#define D_FAILURE "Failure" +#define D_SOLAX_ERROR_0 "No Error Code" +#define D_SOLAX_ERROR_1 "Grid Lost Fault" +#define D_SOLAX_ERROR_2 "Grid Voltage Fault" +#define D_SOLAX_ERROR_3 "Grid Frequency Fault" +#define D_SOLAX_ERROR_4 "Pv Voltage Fault" +#define D_SOLAX_ERROR_5 "Isolation Fault" +#define D_SOLAX_ERROR_6 "Over Temperature Fault" +#define D_SOLAX_ERROR_7 "Fan Fault" +#define D_SOLAX_ERROR_8 "Other Device Fault" + #endif // _LANGUAGE_CS_CZ_H_ diff --git a/sonoff/language/de-DE.h b/sonoff/language/de-DE.h index bccdbdf30..92e456f96 100644 --- a/sonoff/language/de-DE.h +++ b/sonoff/language/de-DE.h @@ -28,7 +28,7 @@ * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. * Use online command Prefix to translate cmnd, stat and tele. * - * Updated until v6.5.0.7 + * Updated until v6.6.0.14 \*********************************************************************/ //#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) @@ -284,9 +284,10 @@ #define D_LOGGING_PARAMETERS "Logging-Einstellungen" #define D_SERIAL_LOG_LEVEL "Seriell-Log Level" +#define D_MQTT_LOG_LEVEL "Mqtt-Log Level" #define D_WEB_LOG_LEVEL "Web-Log Level" #define D_SYS_LOG_LEVEL "Sys-Log Level" -#define D_MORE_DEBUG "More debug" +#define D_MORE_DEBUG "Mehr Details" #define D_SYSLOG_HOST "Sys-Log Host" #define D_SYSLOG_PORT "Sys-Log Port" #define D_TELEMETRY_PERIOD "Telemetrieperiode" @@ -400,7 +401,8 @@ #define D_DOMOTICZ_VOLTAGE "Voltage/PM2.5" #define D_DOMOTICZ_CURRENT "Current/PM10" #define D_DOMOTICZ_AIRQUALITY "AirQuality" - #define D_DOMOTICZ_UPDATE_TIMER "Update Zeitplan" + #define D_DOMOTICZ_P1_SMART_METER "P1SmartMeter" +#define D_DOMOTICZ_UPDATE_TIMER "Update Zeitplan" // xdrv_09_timers.ino #define D_CONFIGURE_TIMER "Zeitplan konfigurieren" @@ -441,6 +443,19 @@ #define D_ENERGY_YESTERDAY "Energie gestern" #define D_ENERGY_TOTAL "Energie insgesamt" +// xdrv_27_shutter.ino +#define D_OPEN "Öffnen" +#define D_CLOSE "Schliessen" +#define D_DOMOTICZ_SHUTTER "Rollo" + +// xdrv_28_pcf8574.ino +#define D_CONFIGURE_PCF8574 "Konfiguriere PCF8574" +#define D_PCF8574_PARAMETERS "PCF8574 Parameter" +#define D_INVERT_PORTS "Invertiere Ports" +#define D_DEVICE "Gerät" +#define D_DEVICE_INPUT "Eingang" +#define D_DEVICE_OUTPUT "Ausgang" + // xsns_05_ds18b20.ino #define D_SENSOR_BUSY "Sensor beschäftigt" #define D_SENSOR_CRC_ERROR "Sensor CRC-Fehler" @@ -460,7 +475,7 @@ // xsns_18_pms5003.ino #define D_STANDARD_CONCENTRATION "CF-1 PM" // Standard Particle CF-1 Particle Matter #define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter -#define D_PARTICALS_BEYOND "Particals" +#define D_PARTICALS_BEYOND "Partikel" // xsns_32_mpu6050.ino #define D_AX_AXIS "Beschl. X-Achse" @@ -526,7 +541,10 @@ #define D_SENSOR_SAIR_TX "SAir Tx" #define D_SENSOR_SPI_CS "SPI CS" #define D_SENSOR_SPI_DC "SPI DC" -#define D_SENSOR_BACKLIGHT "BkLight" +#define D_SENSOR_SPI_MISO "SPI MISO" +#define D_SENSOR_SPI_MOSI "SPI MOSI" +#define D_SENSOR_SPI_CLK "SPI CLK" +#define D_SENSOR_BACKLIGHT "Backlight" #define D_SENSOR_PMS5003 "PMS5003" #define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" #define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" @@ -585,6 +603,33 @@ #define D_SENSOR_HRE_CLOCK "HRE Clock" #define D_SENSOR_HRE_DATA "HRE Data" #define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" +#define D_SENSOR_BUZZER "Buzzer" +#define D_SENSOR_OLED_RESET "OLED Reset" +#define D_SENSOR_ZIGBEE_TXD "Zigbee Tx" +#define D_SENSOR_ZIGBEE_RXD "Zigbee Rx" +#define D_SENSOR_SOLAXX1_TX "SolaxX1 Tx" +#define D_SENSOR_SOLAXX1_RX "SolaxX1 Rx" +#define D_SENSOR_IBEACON_TX "iBeacon TX" +#define D_SENSOR_IBEACON_RX "iBeacon RX" +#define D_SENSOR_RDM6300_RX "RDM6300 RX" +#define D_SENSOR_CC1101_CS "CC1101 CS" +#define D_SENSOR_A4988_DIR "A4988 DIR" +#define D_SENSOR_A4988_STP "A4988 STP" +#define D_SENSOR_A4988_ENA "A4988 ENA" +#define D_SENSOR_A4988_MS1 "A4988 MS1" +#define D_SENSOR_A4988_MS2 "A4988 MS2" +#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_DDS2382_TX "DDS238-2 Tx" +#define D_SENSOR_DDS2382_RX "DDS238-2 Rx" +#define D_SENSOR_DDSU666_TX "DDSU666 Tx" +#define D_SENSOR_DDSU666_RX "DDSU666 Rx" +#define D_SENSOR_SM2135_CLK "SM2135 Clk" +#define D_SENSOR_SM2135_DAT "SM2135 Dat" +#define D_SENSOR_DEEPSLEEP "DeepSleep" +#define D_SENSOR_EXS_ENABLE "EXS Enable" +#define D_SENSOR_ARDUINO_TX "Arduino TX" +#define D_SENSOR_ARDUINO_RX "Arduino RX" +#define D_SENSOR_ARDUINO_RESET "Arduino RST" // Units #define D_UNIT_AMPERE "A" @@ -655,4 +700,28 @@ #define D_UNIT_KWARH "kVArh" #define D_UNIT_ANGLE "Grad" +//SOLAXX1 +#define D_PV1_VOLTAGE "PV1 Spannung" +#define D_PV1_CURRENT "PV1 Strom" +#define D_PV1_POWER "PV1 Leistung" +#define D_PV2_VOLTAGE "PV2 Spannung" +#define D_PV2_CURRENT "PV2 Strom" +#define D_PV2_POWER "PV2 Leistung" +#define D_SOLAR_POWER "solare Leistung" +#define D_INVERTER_POWER "Inverter Leistung" +#define D_STATUS "Status" +#define D_WAITING "warten" +#define D_CHECKING "prüfen" +#define D_WORKING "arbeitet" +#define D_FAILURE "Fehler" +#define D_SOLAX_ERROR_0 "Kein Fehler Code" +#define D_SOLAX_ERROR_1 "Fehler im Solarstromnetz" +#define D_SOLAX_ERROR_2 "Spannungsfehler im Solarstromnetz" +#define D_SOLAX_ERROR_3 "Frequenzfehler im Solarstromnetz" +#define D_SOLAX_ERROR_4 "Pv Spannungsfehler" +#define D_SOLAX_ERROR_5 "Isolationsfehler" +#define D_SOLAX_ERROR_6 "Übertemperatur" +#define D_SOLAX_ERROR_7 "Lüfterfehler" +#define D_SOLAX_ERROR_8 "sonstiger Fehler" + #endif // _LANGUAGE_DE_DE_H_ diff --git a/sonoff/language/el-GR.h b/sonoff/language/el-GR.h index 4ddcc61d3..680633d6a 100644 --- a/sonoff/language/el-GR.h +++ b/sonoff/language/el-GR.h @@ -284,6 +284,7 @@ #define D_LOGGING_PARAMETERS "Παράμετροι καταγραφής" #define D_SERIAL_LOG_LEVEL "Επίπεδο Σειριακής" +#define D_MQTT_LOG_LEVEL "Mqtt log level" #define D_WEB_LOG_LEVEL "Επίπεδο Web" #define D_SYS_LOG_LEVEL "Επίπεδο Syslog" #define D_MORE_DEBUG "More debug" @@ -391,15 +392,16 @@ #define D_DOMOTICZ_KEY_IDX "Key idx" #define D_DOMOTICZ_SWITCH_IDX "Switch idx" #define D_DOMOTICZ_SENSOR_IDX "Sensor idx" -#define D_DOMOTICZ_TEMP "Temp" -#define D_DOMOTICZ_TEMP_HUM "Temp,Hum" -#define D_DOMOTICZ_TEMP_HUM_BARO "Temp,Hum,Baro" -#define D_DOMOTICZ_POWER_ENERGY "Power,Energy" -#define D_DOMOTICZ_ILLUMINANCE "Illuminance" -#define D_DOMOTICZ_COUNT "Count/PM1" -#define D_DOMOTICZ_VOLTAGE "Voltage/PM2.5" -#define D_DOMOTICZ_CURRENT "Current/PM10" -#define D_DOMOTICZ_AIRQUALITY "AirQuality" + #define D_DOMOTICZ_TEMP "Temp" + #define D_DOMOTICZ_TEMP_HUM "Temp,Hum" + #define D_DOMOTICZ_TEMP_HUM_BARO "Temp,Hum,Baro" + #define D_DOMOTICZ_POWER_ENERGY "Power,Energy" + #define D_DOMOTICZ_ILLUMINANCE "Illuminance" + #define D_DOMOTICZ_COUNT "Count/PM1" + #define D_DOMOTICZ_VOLTAGE "Voltage/PM2.5" + #define D_DOMOTICZ_CURRENT "Current/PM10" + #define D_DOMOTICZ_AIRQUALITY "AirQuality" + #define D_DOMOTICZ_P1_SMART_METER "P1SmartMeter" #define D_DOMOTICZ_UPDATE_TIMER "Update timer" // xdrv_09_timers.ino @@ -441,6 +443,19 @@ #define D_ENERGY_YESTERDAY "Ενέργεια χθες" #define D_ENERGY_TOTAL "Ενέργεια συνολικά" +// xdrv_27_shutter.ino +#define D_OPEN "Open" +#define D_CLOSE "Close" +#define D_DOMOTICZ_SHUTTER "Shutter" + +// xdrv_28_pcf8574.ino +#define D_CONFIGURE_PCF8574 "Configure PCF8574" +#define D_PCF8574_PARAMETERS "PCF8574 parameters" +#define D_INVERT_PORTS "Invert Ports" +#define D_DEVICE "Device" +#define D_DEVICE_INPUT "Input" +#define D_DEVICE_OUTPUT "Output" + // xsns_05_ds18b20.ino #define D_SENSOR_BUSY "Ο αισθητήρας είναι απασχολημένος" #define D_SENSOR_CRC_ERROR "Σφάλμα CRC αισθητήρα" @@ -526,7 +541,10 @@ #define D_SENSOR_SAIR_TX "SAir Tx" #define D_SENSOR_SPI_CS "SPI CS" #define D_SENSOR_SPI_DC "SPI DC" -#define D_SENSOR_BACKLIGHT "BkLight" +#define D_SENSOR_SPI_MISO "SPI MISO" +#define D_SENSOR_SPI_MOSI "SPI MOSI" +#define D_SENSOR_SPI_CLK "SPI CLK" +#define D_SENSOR_BACKLIGHT "Backlight" #define D_SENSOR_PMS5003 "PMS5003" #define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" #define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" @@ -585,6 +603,33 @@ #define D_SENSOR_HRE_CLOCK "HRE Clock" #define D_SENSOR_HRE_DATA "HRE Data" #define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" +#define D_SENSOR_BUZZER "Buzzer" +#define D_SENSOR_OLED_RESET "OLED Reset" +#define D_SENSOR_ZIGBEE_TXD "Zigbee Tx" +#define D_SENSOR_ZIGBEE_RXD "Zigbee Rx" +#define D_SENSOR_SOLAXX1_TX "SolaxX1 Tx" +#define D_SENSOR_SOLAXX1_RX "SolaxX1 Rx" +#define D_SENSOR_IBEACON_TX "iBeacon TX" +#define D_SENSOR_IBEACON_RX "iBeacon RX" +#define D_SENSOR_RDM6300_RX "RDM6300 RX" +#define D_SENSOR_CC1101_CS "CC1101 CS" +#define D_SENSOR_A4988_DIR "A4988 DIR" +#define D_SENSOR_A4988_STP "A4988 STP" +#define D_SENSOR_A4988_ENA "A4988 ENA" +#define D_SENSOR_A4988_MS1 "A4988 MS1" +#define D_SENSOR_A4988_MS2 "A4988 MS2" +#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_DDS2382_TX "DDS238-2 Tx" +#define D_SENSOR_DDS2382_RX "DDS238-2 Rx" +#define D_SENSOR_DDSU666_TX "DDSU666 Tx" +#define D_SENSOR_DDSU666_RX "DDSU666 Rx" +#define D_SENSOR_SM2135_CLK "SM2135 Clk" +#define D_SENSOR_SM2135_DAT "SM2135 Dat" +#define D_SENSOR_DEEPSLEEP "DeepSleep" +#define D_SENSOR_EXS_ENABLE "EXS Enable" +#define D_SENSOR_ARDUINO_TX "Arduino TX" +#define D_SENSOR_ARDUINO_RX "Arduino RX" +#define D_SENSOR_ARDUINO_RESET "Arduino RST" // Units #define D_UNIT_AMPERE "A" @@ -655,4 +700,28 @@ #define D_UNIT_KWARH "kVArh" #define D_UNIT_ANGLE "Deg" +//SOLAXX1 +#define D_PV1_VOLTAGE "PV1 Voltage" +#define D_PV1_CURRENT "PV1 Current" +#define D_PV1_POWER "PV1 Power" +#define D_PV2_VOLTAGE "PV2 Voltage" +#define D_PV2_CURRENT "PV2 Current" +#define D_PV2_POWER "PV2 Power" +#define D_SOLAR_POWER "Solar Power" +#define D_INVERTER_POWER "Inverter Power" +#define D_STATUS "Status" +#define D_WAITING "Waiting" +#define D_CHECKING "Checking" +#define D_WORKING "Working" +#define D_FAILURE "Failure" +#define D_SOLAX_ERROR_0 "No Error Code" +#define D_SOLAX_ERROR_1 "Grid Lost Fault" +#define D_SOLAX_ERROR_2 "Grid Voltage Fault" +#define D_SOLAX_ERROR_3 "Grid Frequency Fault" +#define D_SOLAX_ERROR_4 "Pv Voltage Fault" +#define D_SOLAX_ERROR_5 "Isolation Fault" +#define D_SOLAX_ERROR_6 "Over Temperature Fault" +#define D_SOLAX_ERROR_7 "Fan Fault" +#define D_SOLAX_ERROR_8 "Other Device Fault" + #endif // _LANGUAGE_EL_GR_H_ diff --git a/sonoff/language/en-GB.h b/sonoff/language/en-GB.h index c9aba7cfd..74a2fdca0 100644 --- a/sonoff/language/en-GB.h +++ b/sonoff/language/en-GB.h @@ -284,6 +284,7 @@ #define D_LOGGING_PARAMETERS "Logging parameters" #define D_SERIAL_LOG_LEVEL "Serial log level" +#define D_MQTT_LOG_LEVEL "Mqtt log level" #define D_WEB_LOG_LEVEL "Web log level" #define D_SYS_LOG_LEVEL "Syslog level" #define D_MORE_DEBUG "More debug" @@ -400,6 +401,7 @@ #define D_DOMOTICZ_VOLTAGE "Voltage/PM2.5" #define D_DOMOTICZ_CURRENT "Current/PM10" #define D_DOMOTICZ_AIRQUALITY "AirQuality" + #define D_DOMOTICZ_P1_SMART_METER "P1SmartMeter" #define D_DOMOTICZ_UPDATE_TIMER "Update timer" // xdrv_09_timers.ino @@ -441,6 +443,19 @@ #define D_ENERGY_YESTERDAY "Energy Yesterday" #define D_ENERGY_TOTAL "Energy Total" +// xdrv_27_shutter.ino +#define D_OPEN "Open" +#define D_CLOSE "Close" +#define D_DOMOTICZ_SHUTTER "Shutter" + +// xdrv_28_pcf8574.ino +#define D_CONFIGURE_PCF8574 "Configure PCF8574" +#define D_PCF8574_PARAMETERS "PCF8574 parameters" +#define D_INVERT_PORTS "Invert Ports" +#define D_DEVICE "Device" +#define D_DEVICE_INPUT "Input" +#define D_DEVICE_OUTPUT "Output" + // xsns_05_ds18b20.ino #define D_SENSOR_BUSY "Sensor busy" #define D_SENSOR_CRC_ERROR "Sensor CRC error" @@ -526,7 +541,10 @@ #define D_SENSOR_SAIR_TX "SAir Tx" #define D_SENSOR_SPI_CS "SPI CS" #define D_SENSOR_SPI_DC "SPI DC" -#define D_SENSOR_BACKLIGHT "BkLight" +#define D_SENSOR_SPI_MISO "SPI MISO" +#define D_SENSOR_SPI_MOSI "SPI MOSI" +#define D_SENSOR_SPI_CLK "SPI CLK" +#define D_SENSOR_BACKLIGHT "Backlight" #define D_SENSOR_PMS5003 "PMS5003" #define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" #define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" @@ -585,6 +603,33 @@ #define D_SENSOR_HRE_CLOCK "HRE Clock" #define D_SENSOR_HRE_DATA "HRE Data" #define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" +#define D_SENSOR_BUZZER "Buzzer" +#define D_SENSOR_OLED_RESET "OLED Reset" +#define D_SENSOR_ZIGBEE_TXD "Zigbee Tx" +#define D_SENSOR_ZIGBEE_RXD "Zigbee Rx" +#define D_SENSOR_SOLAXX1_TX "SolaxX1 Tx" +#define D_SENSOR_SOLAXX1_RX "SolaxX1 Rx" +#define D_SENSOR_IBEACON_TX "iBeacon TX" +#define D_SENSOR_IBEACON_RX "iBeacon RX" +#define D_SENSOR_RDM6300_RX "RDM6300 RX" +#define D_SENSOR_CC1101_CS "CC1101 CS" +#define D_SENSOR_A4988_DIR "A4988 DIR" +#define D_SENSOR_A4988_STP "A4988 STP" +#define D_SENSOR_A4988_ENA "A4988 ENA" +#define D_SENSOR_A4988_MS1 "A4988 MS1" +#define D_SENSOR_A4988_MS2 "A4988 MS2" +#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_DDS2382_TX "DDS238-2 Tx" +#define D_SENSOR_DDS2382_RX "DDS238-2 Rx" +#define D_SENSOR_DDSU666_TX "DDSU666 Tx" +#define D_SENSOR_DDSU666_RX "DDSU666 Rx" +#define D_SENSOR_SM2135_CLK "SM2135 Clk" +#define D_SENSOR_SM2135_DAT "SM2135 Dat" +#define D_SENSOR_DEEPSLEEP "DeepSleep" +#define D_SENSOR_EXS_ENABLE "EXS Enable" +#define D_SENSOR_ARDUINO_TX "Arduino TX" +#define D_SENSOR_ARDUINO_RX "Arduino RX" +#define D_SENSOR_ARDUINO_RESET "Arduino RST" // Units #define D_UNIT_AMPERE "A" @@ -655,4 +700,28 @@ #define D_UNIT_KWARH "kVArh" #define D_UNIT_ANGLE "Deg" +//SOLAXX1 +#define D_PV1_VOLTAGE "PV1 Voltage" +#define D_PV1_CURRENT "PV1 Current" +#define D_PV1_POWER "PV1 Power" +#define D_PV2_VOLTAGE "PV2 Voltage" +#define D_PV2_CURRENT "PV2 Current" +#define D_PV2_POWER "PV2 Power" +#define D_SOLAR_POWER "Solar Power" +#define D_INVERTER_POWER "Inverter Power" +#define D_STATUS "Status" +#define D_WAITING "Waiting" +#define D_CHECKING "Checking" +#define D_WORKING "Working" +#define D_FAILURE "Failure" +#define D_SOLAX_ERROR_0 "No Error Code" +#define D_SOLAX_ERROR_1 "Grid Lost Fault" +#define D_SOLAX_ERROR_2 "Grid Voltage Fault" +#define D_SOLAX_ERROR_3 "Grid Frequency Fault" +#define D_SOLAX_ERROR_4 "Pv Voltage Fault" +#define D_SOLAX_ERROR_5 "Isolation Fault" +#define D_SOLAX_ERROR_6 "Over Temperature Fault" +#define D_SOLAX_ERROR_7 "Fan Fault" +#define D_SOLAX_ERROR_8 "Other Device Fault" + #endif // _LANGUAGE_EN_GB_H_ diff --git a/sonoff/language/es-ES.h b/sonoff/language/es-ES.h index 579ece011..2530fd6e4 100644 --- a/sonoff/language/es-ES.h +++ b/sonoff/language/es-ES.h @@ -28,7 +28,7 @@ * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. * Use online command Prefix to translate cmnd, stat and tele. * - * Updated until v6.5.0.16 + * Updated until v6.6.0.15 \*********************************************************************/ #define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) @@ -283,9 +283,10 @@ #define D_FULL_TOPIC "Full Topic" #define D_LOGGING_PARAMETERS "Parámetros Logging" -#define D_SERIAL_LOG_LEVEL "Nivel de log Serial" -#define D_WEB_LOG_LEVEL "Nivel de log Web" -#define D_SYS_LOG_LEVEL "Nivel de Syslog" +#define D_SERIAL_LOG_LEVEL "Nivel de log por Serial" +#define D_MQTT_LOG_LEVEL "Nivel de log por Mqtt" +#define D_WEB_LOG_LEVEL "Nivel de log por Web" +#define D_SYS_LOG_LEVEL "Nivel de log por Syslog" #define D_MORE_DEBUG "Más Debug" #define D_SYSLOG_HOST "Host del Syslog" #define D_SYSLOG_PORT "Puerto del Syslog" @@ -400,6 +401,7 @@ #define D_DOMOTICZ_VOLTAGE "Voltaje/PM2,5" #define D_DOMOTICZ_CURRENT "Corriente/PM10" #define D_DOMOTICZ_AIRQUALITY "Calidad del Aire" + #define D_DOMOTICZ_P1_SMART_METER "Medidor Inteligente P1" #define D_DOMOTICZ_UPDATE_TIMER "Intervalo de refresco" // xdrv_09_timers.ino @@ -441,6 +443,19 @@ #define D_ENERGY_YESTERDAY "Energía Ayer" #define D_ENERGY_TOTAL "Energía Total" +// xdrv_27_shutter.ino +#define D_OPEN "Abrir" +#define D_CLOSE "Cerrar" +#define D_DOMOTICZ_SHUTTER "Cortina" + +// xdrv_28_pcf8574.ino +#define D_CONFIGURE_PCF8574 "Configurar PCF8574" +#define D_PCF8574_PARAMETERS "Parámetros de PCF8574" +#define D_INVERT_PORTS "Invertir Puertos" +#define D_DEVICE "Dispositivo" +#define D_DEVICE_INPUT "Entrada" +#define D_DEVICE_OUTPUT "Salida" + // xsns_05_ds18b20.ino #define D_SENSOR_BUSY "Sensor ocupado" #define D_SENSOR_CRC_ERROR "Error CRC del Sensor" @@ -526,7 +541,10 @@ #define D_SENSOR_SAIR_TX "SAir Tx" #define D_SENSOR_SPI_CS "SPI CS" #define D_SENSOR_SPI_DC "SPI DC" -#define D_SENSOR_BACKLIGHT "BkLight" +#define D_SENSOR_SPI_MISO "SPI MISO" +#define D_SENSOR_SPI_MOSI "SPI MOSI" +#define D_SENSOR_SPI_CLK "SPI CLK" +#define D_SENSOR_BACKLIGHT "Backlight" #define D_SENSOR_PMS5003 "PMS5003" #define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" #define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" @@ -585,6 +603,33 @@ #define D_SENSOR_HRE_CLOCK "HRE Clock" #define D_SENSOR_HRE_DATA "HRE Data" #define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" +#define D_SENSOR_BUZZER "Buzzer" +#define D_SENSOR_OLED_RESET "OLED Reset" +#define D_SENSOR_ZIGBEE_TXD "Zigbee Tx" +#define D_SENSOR_ZIGBEE_RXD "Zigbee Rx" +#define D_SENSOR_SOLAXX1_TX "SolaxX1 Tx" +#define D_SENSOR_SOLAXX1_RX "SolaxX1 Rx" +#define D_SENSOR_IBEACON_TX "iBeacon TX" +#define D_SENSOR_IBEACON_RX "iBeacon RX" +#define D_SENSOR_RDM6300_RX "RDM6300 RX" +#define D_SENSOR_CC1101_CS "CC1101 CS" +#define D_SENSOR_A4988_DIR "A4988 DIR" +#define D_SENSOR_A4988_STP "A4988 STP" +#define D_SENSOR_A4988_ENA "A4988 ENA" +#define D_SENSOR_A4988_MS1 "A4988 MS1" +#define D_SENSOR_A4988_MS2 "A4988 MS2" +#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_DDS2382_TX "DDS238-2 Tx" +#define D_SENSOR_DDS2382_RX "DDS238-2 Rx" +#define D_SENSOR_DDSU666_TX "DDSU666 Tx" +#define D_SENSOR_DDSU666_RX "DDSU666 Rx" +#define D_SENSOR_SM2135_CLK "SM2135 Clk" +#define D_SENSOR_SM2135_DAT "SM2135 Dat" +#define D_SENSOR_DEEPSLEEP "DeepSleep" +#define D_SENSOR_EXS_ENABLE "EXS Enable" +#define D_SENSOR_ARDUINO_TX "Arduino TX" +#define D_SENSOR_ARDUINO_RX "Arduino RX" +#define D_SENSOR_ARDUINO_RESET "Arduino RST" // Units #define D_UNIT_AMPERE "A" @@ -655,4 +700,28 @@ #define D_UNIT_KWARH "kVArH" #define D_UNIT_ANGLE "Grados" +//SOLAXX1 +#define D_PV1_VOLTAGE "PV1 Voltaje" +#define D_PV1_CURRENT "PV1 Corriente" +#define D_PV1_POWER "PV1 Potencia" +#define D_PV2_VOLTAGE "PV2 Voltaee" +#define D_PV2_CURRENT "PV2 Corriente" +#define D_PV2_POWER "PV2 Potencia" +#define D_SOLAR_POWER "Potencia Solar" +#define D_INVERTER_POWER "Potencia del Inversor" +#define D_STATUS "Estado" +#define D_WAITING "En Espera" +#define D_CHECKING "Revisando" +#define D_WORKING "Funcionando" +#define D_FAILURE "Falla" +#define D_SOLAX_ERROR_0 "Sin código de Error" +#define D_SOLAX_ERROR_1 "Falla por Pérdida de Red" +#define D_SOLAX_ERROR_2 "Falla por Voltaje de Red" +#define D_SOLAX_ERROR_3 "Falla por Frecuencia de Red" +#define D_SOLAX_ERROR_4 "Falla por Voltaje en PV" +#define D_SOLAX_ERROR_5 "Falla de Aislación" +#define D_SOLAX_ERROR_6 "Falla por sobretemperatura" +#define D_SOLAX_ERROR_7 "Falla de Ventilador" +#define D_SOLAX_ERROR_8 "Falla del Dispositivo" + #endif // _LANGUAGE_ES_ES_H_ diff --git a/sonoff/language/fr-FR.h b/sonoff/language/fr-FR.h index daf849264..1522ec15f 100644 --- a/sonoff/language/fr-FR.h +++ b/sonoff/language/fr-FR.h @@ -28,7 +28,7 @@ * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. * Use online command Prefix to translate cmnd, stat and tele. * - * Updated until v6.5.0.7 + * Updated until v6.6.0.15 \*********************************************************************/ #define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) @@ -125,8 +125,8 @@ #define D_PORT "Port" #define D_POWER_FACTOR "Fact de puiss" #define D_POWERUSAGE "Puissance" -#define D_POWERUSAGE_ACTIVE "Puiss act" -#define D_POWERUSAGE_APPARENT "Puiss app" +#define D_POWERUSAGE_ACTIVE "Puiss active" +#define D_POWERUSAGE_APPARENT "Puiss apparente" #define D_POWERUSAGE_REACTIVE "Puiss réactive" #define D_PRESSURE "Pression" #define D_PRESSUREATSEALEVEL "PressionMer" @@ -228,8 +228,8 @@ #define D_WEBSERVER_STOPPED "Serveur web éteint" #define D_FILE_NOT_FOUND "Fichier introuvable" #define D_REDIRECTED "Redirection sur le portail captif" -#define D_WIFIMANAGER_SET_ACCESSPOINT_AND_STATION "Wifimanager définit AccessPoint et garde station" -#define D_WIFIMANAGER_SET_ACCESSPOINT "Wifimanager définit AccessPoint" +#define D_WIFIMANAGER_SET_ACCESSPOINT_AND_STATION "Wifimanager en mode Point d'Accès et Station" +#define D_WIFIMANAGER_SET_ACCESSPOINT "Wifimanager en mode Point d'Accès" #define D_TRYING_TO_CONNECT "Tentative de connexion du module au réseau" #define D_RESTART_IN "Redémarrage dans" @@ -284,6 +284,7 @@ #define D_LOGGING_PARAMETERS "Paramètres du journal" #define D_SERIAL_LOG_LEVEL "Niveau de journalisation série" +#define D_MQTT_LOG_LEVEL "Niveau de journalisation MQTT" #define D_WEB_LOG_LEVEL "Niveau de journalisation web" #define D_SYS_LOG_LEVEL "Niveau SysLog" #define D_MORE_DEBUG "Plus de debug" @@ -400,6 +401,7 @@ #define D_DOMOTICZ_VOLTAGE "Tension/PM2,5" #define D_DOMOTICZ_CURRENT "Courant/PM10" #define D_DOMOTICZ_AIRQUALITY "Qualité de l'air" + #define D_DOMOTICZ_P1_SMART_METER "P1SmartMeter" #define D_DOMOTICZ_UPDATE_TIMER "Durée de rafraichissement" // xdrv_09_timers.ino @@ -441,6 +443,19 @@ #define D_ENERGY_YESTERDAY "Énergie hier" #define D_ENERGY_TOTAL "Énergie totale" +// xdrv_27_shutter.ino +#define D_OPEN "Ouvert" +#define D_CLOSE "Fermé" +#define D_DOMOTICZ_SHUTTER "Volet" + +// xdrv_28_pcf8574.ino +#define D_CONFIGURE_PCF8574 "Configurer PCF8574" +#define D_PCF8574_PARAMETERS "Paramètres PCF8574" +#define D_INVERT_PORTS "Inverser les Ports" +#define D_DEVICE "Module" +#define D_DEVICE_INPUT "Entrée" +#define D_DEVICE_OUTPUT "Sortie" + // xsns_05_ds18b20.ino #define D_SENSOR_BUSY "Capteur occupé" #define D_SENSOR_CRC_ERROR "Erreur CRC capteur" @@ -526,6 +541,9 @@ #define D_SENSOR_SAIR_TX "SAir Tx" #define D_SENSOR_SPI_CS "SPI CS" #define D_SENSOR_SPI_DC "SPI DC" +#define D_SENSOR_SPI_MISO "SPI MISO" +#define D_SENSOR_SPI_MOSI "SPI MOSI" +#define D_SENSOR_SPI_CLK "SPI CLK" #define D_SENSOR_BACKLIGHT "RétroÉcl" #define D_SENSOR_PMS5003 "PMS5003" #define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" @@ -585,6 +603,33 @@ #define D_SENSOR_HRE_CLOCK "HRE Clock" #define D_SENSOR_HRE_DATA "HRE Data" #define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" +#define D_SENSOR_BUZZER "Buzzer" +#define D_SENSOR_OLED_RESET "OLED Reset" +#define D_SENSOR_ZIGBEE_TXD "Zigbee Tx" +#define D_SENSOR_ZIGBEE_RXD "Zigbee Rx" +#define D_SENSOR_SOLAXX1_TX "SolaxX1 Tx" +#define D_SENSOR_SOLAXX1_RX "SolaxX1 Rx" +#define D_SENSOR_IBEACON_TX "iBeacon TX" +#define D_SENSOR_IBEACON_RX "iBeacon RX" +#define D_SENSOR_RDM6300_RX "RDM6300 RX" +#define D_SENSOR_CC1101_CS "CC1101 CS" +#define D_SENSOR_A4988_DIR "A4988 DIR" +#define D_SENSOR_A4988_STP "A4988 STP" +#define D_SENSOR_A4988_ENA "A4988 ENA" +#define D_SENSOR_A4988_MS1 "A4988 MS1" +#define D_SENSOR_A4988_MS2 "A4988 MS2" +#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_DDS2382_TX "DDS238-2 Tx" +#define D_SENSOR_DDS2382_RX "DDS238-2 Rx" +#define D_SENSOR_DDSU666_TX "DDSU666 Tx" +#define D_SENSOR_DDSU666_RX "DDSU666 Rx" +#define D_SENSOR_SM2135_CLK "SM2135 Clk" +#define D_SENSOR_SM2135_DAT "SM2135 Dat" +#define D_SENSOR_DEEPSLEEP "DeepSleep" +#define D_SENSOR_EXS_ENABLE "EXS Enable" +#define D_SENSOR_ARDUINO_TX "Arduino TX" +#define D_SENSOR_ARDUINO_RX "Arduino RX" +#define D_SENSOR_ARDUINO_RESET "Arduino RST" // Units #define D_UNIT_AMPERE "A" @@ -647,12 +692,36 @@ //SDM220 #define D_PHASE_ANGLE "Angle de phase" -#define D_IMPORT_ACTIVE "Puiss act conso" -#define D_EXPORT_ACTIVE "Puiss act fournie" -#define D_IMPORT_REACTIVE "Puiss réa conso" -#define D_EXPORT_REACTIVE "Puiss réa fournie" -#define D_TOTAL_REACTIVE "Puiss réa totale" +#define D_IMPORT_ACTIVE "Énergie act conso" +#define D_EXPORT_ACTIVE "Énergie act fournie" +#define D_IMPORT_REACTIVE "Énergie réa conso" +#define D_EXPORT_REACTIVE "Énergie réa fournie" +#define D_TOTAL_REACTIVE "Énergie réa totale" #define D_UNIT_KWARH "kVArh" #define D_UNIT_ANGLE "°" +//SOLAXX1 +#define D_PV1_VOLTAGE "Tension PV1" +#define D_PV1_CURRENT "Courant PV1" +#define D_PV1_POWER "Puissance PV1" +#define D_PV2_VOLTAGE "Tension PV2" +#define D_PV2_CURRENT "Courant PV2" +#define D_PV2_POWER "Puissance PV2" +#define D_SOLAR_POWER "Puissance solaire" +#define D_INVERTER_POWER "Puissance onduleur" +#define D_STATUS "Statut" +#define D_WAITING "En attente" +#define D_CHECKING "En test" +#define D_WORKING "En marche" +#define D_FAILURE "Défault" +#define D_SOLAX_ERROR_0 "Aucun Code d'erreur" +#define D_SOLAX_ERROR_1 "Défaut Perte de réseau" +#define D_SOLAX_ERROR_2 "Défaut Tension réseau" +#define D_SOLAX_ERROR_3 "Défaut Fréquence réseau" +#define D_SOLAX_ERROR_4 "Défaut Tension PV" +#define D_SOLAX_ERROR_5 "Défaut Isolation" +#define D_SOLAX_ERROR_6 "Défaut Surchauffe" +#define D_SOLAX_ERROR_7 "Défaut Ventilateur" +#define D_SOLAX_ERROR_8 "Défaut Autre équipement" + #endif // _LANGUAGE_FR_FR_H_ diff --git a/sonoff/language/he-HE.h b/sonoff/language/he-HE.h index 8f8d35cba..3e5fe4031 100644 --- a/sonoff/language/he-HE.h +++ b/sonoff/language/he-HE.h @@ -284,6 +284,7 @@ #define D_LOGGING_PARAMETERS "פרמטרי לוגים" #define D_SERIAL_LOG_LEVEL "רמת לוג עבור סריאל" +#define D_MQTT_LOG_LEVEL "Mqtt log level" #define D_WEB_LOG_LEVEL "רמת לוג עבור אתר" #define D_SYS_LOG_LEVEL "Syslog רמת לוג עבור שרת" #define D_MORE_DEBUG "מיפוי נוסף" @@ -400,6 +401,7 @@ #define D_DOMOTICZ_VOLTAGE "Voltage/PM2.5" #define D_DOMOTICZ_CURRENT "Current/PM10" #define D_DOMOTICZ_AIRQUALITY "איכות אוויר" + #define D_DOMOTICZ_P1_SMART_METER "P1SmartMeter" #define D_DOMOTICZ_UPDATE_TIMER "עדכן טיימר" // xdrv_09_timers.ino @@ -441,6 +443,19 @@ #define D_ENERGY_YESTERDAY "צריכה בעבר" #define D_ENERGY_TOTAL "צריכה כללית" +// xdrv_27_shutter.ino +#define D_OPEN "Open" +#define D_CLOSE "Close" +#define D_DOMOTICZ_SHUTTER "Shutter" + +// xdrv_28_pcf8574.ino +#define D_CONFIGURE_PCF8574 "Configure PCF8574" +#define D_PCF8574_PARAMETERS "PCF8574 parameters" +#define D_INVERT_PORTS "Invert Ports" +#define D_DEVICE "Device" +#define D_DEVICE_INPUT "Input" +#define D_DEVICE_OUTPUT "Output" + // xsns_05_ds18b20.ino #define D_SENSOR_BUSY "שרת עסוק" #define D_SENSOR_CRC_ERROR "בחיישן CRC שגיאת" @@ -526,7 +541,10 @@ #define D_SENSOR_SAIR_TX "SAir Tx" #define D_SENSOR_SPI_CS "SPI CS" #define D_SENSOR_SPI_DC "SPI DC" -#define D_SENSOR_BACKLIGHT "BkLight" +#define D_SENSOR_SPI_MISO "SPI MISO" +#define D_SENSOR_SPI_MOSI "SPI MOSI" +#define D_SENSOR_SPI_CLK "SPI CLK" +#define D_SENSOR_BACKLIGHT "Backlight" #define D_SENSOR_PMS5003 "PMS5003" #define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" #define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" @@ -585,6 +603,33 @@ #define D_SENSOR_HRE_CLOCK "HRE Clock" #define D_SENSOR_HRE_DATA "HRE Data" #define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" +#define D_SENSOR_BUZZER "Buzzer" +#define D_SENSOR_OLED_RESET "OLED Reset" +#define D_SENSOR_ZIGBEE_TXD "Zigbee Tx" +#define D_SENSOR_ZIGBEE_RXD "Zigbee Rx" +#define D_SENSOR_SOLAXX1_TX "SolaxX1 Tx" +#define D_SENSOR_SOLAXX1_RX "SolaxX1 Rx" +#define D_SENSOR_IBEACON_TX "iBeacon TX" +#define D_SENSOR_IBEACON_RX "iBeacon RX" +#define D_SENSOR_RDM6300_RX "RDM6300 RX" +#define D_SENSOR_CC1101_CS "CC1101 CS" +#define D_SENSOR_A4988_DIR "A4988 DIR" +#define D_SENSOR_A4988_STP "A4988 STP" +#define D_SENSOR_A4988_ENA "A4988 ENA" +#define D_SENSOR_A4988_MS1 "A4988 MS1" +#define D_SENSOR_A4988_MS2 "A4988 MS2" +#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_DDS2382_TX "DDS238-2 Tx" +#define D_SENSOR_DDS2382_RX "DDS238-2 Rx" +#define D_SENSOR_DDSU666_TX "DDSU666 Tx" +#define D_SENSOR_DDSU666_RX "DDSU666 Rx" +#define D_SENSOR_SM2135_CLK "SM2135 Clk" +#define D_SENSOR_SM2135_DAT "SM2135 Dat" +#define D_SENSOR_DEEPSLEEP "DeepSleep" +#define D_SENSOR_EXS_ENABLE "EXS Enable" +#define D_SENSOR_ARDUINO_TX "Arduino TX" +#define D_SENSOR_ARDUINO_RX "Arduino RX" +#define D_SENSOR_ARDUINO_RESET "Arduino RST" // Units #define D_UNIT_AMPERE "A" @@ -655,4 +700,28 @@ #define D_UNIT_KWARH "kVArh" #define D_UNIT_ANGLE "Deg" +//SOLAXX1 +#define D_PV1_VOLTAGE "PV1 Voltage" +#define D_PV1_CURRENT "PV1 Current" +#define D_PV1_POWER "PV1 Power" +#define D_PV2_VOLTAGE "PV2 Voltage" +#define D_PV2_CURRENT "PV2 Current" +#define D_PV2_POWER "PV2 Power" +#define D_SOLAR_POWER "Solar Power" +#define D_INVERTER_POWER "Inverter Power" +#define D_STATUS "Status" +#define D_WAITING "Waiting" +#define D_CHECKING "Checking" +#define D_WORKING "Working" +#define D_FAILURE "Failure" +#define D_SOLAX_ERROR_0 "No Error Code" +#define D_SOLAX_ERROR_1 "Grid Lost Fault" +#define D_SOLAX_ERROR_2 "Grid Voltage Fault" +#define D_SOLAX_ERROR_3 "Grid Frequency Fault" +#define D_SOLAX_ERROR_4 "Pv Voltage Fault" +#define D_SOLAX_ERROR_5 "Isolation Fault" +#define D_SOLAX_ERROR_6 "Over Temperature Fault" +#define D_SOLAX_ERROR_7 "Fan Fault" +#define D_SOLAX_ERROR_8 "Other Device Fault" + #endif // _LANGUAGE_HE_HE_H_ diff --git a/sonoff/language/hu-HU.h b/sonoff/language/hu-HU.h index 078169f61..f55f8ed38 100644 --- a/sonoff/language/hu-HU.h +++ b/sonoff/language/hu-HU.h @@ -284,6 +284,7 @@ #define D_LOGGING_PARAMETERS "Naplózási paraméterek" #define D_SERIAL_LOG_LEVEL "Soros naplózási szint" +#define D_MQTT_LOG_LEVEL "Mqtt log level" #define D_WEB_LOG_LEVEL "Web naplózási szint" #define D_SYS_LOG_LEVEL "Syslog szint" #define D_MORE_DEBUG "Részletes hibakeresés" @@ -400,6 +401,7 @@ #define D_DOMOTICZ_VOLTAGE "Feszültség/PM2.5" #define D_DOMOTICZ_CURRENT "Áram/PM10" #define D_DOMOTICZ_AIRQUALITY "Légminőség" + #define D_DOMOTICZ_P1_SMART_METER "P1SmartMeter" #define D_DOMOTICZ_UPDATE_TIMER "Frissítési időzítő" // xdrv_09_timers.ino @@ -441,6 +443,19 @@ #define D_ENERGY_YESTERDAY "Tegnapi energia" #define D_ENERGY_TOTAL "Összes energia" +// xdrv_27_shutter.ino +#define D_OPEN "Open" +#define D_CLOSE "Close" +#define D_DOMOTICZ_SHUTTER "Shutter" + +// xdrv_28_pcf8574.ino +#define D_CONFIGURE_PCF8574 "Configure PCF8574" +#define D_PCF8574_PARAMETERS "PCF8574 parameters" +#define D_INVERT_PORTS "Invert Ports" +#define D_DEVICE "Device" +#define D_DEVICE_INPUT "Input" +#define D_DEVICE_OUTPUT "Output" + // xsns_05_ds18b20.ino #define D_SENSOR_BUSY "Szenzor foglalt" #define D_SENSOR_CRC_ERROR "Szenzor CRC hiba" @@ -526,6 +541,9 @@ #define D_SENSOR_SAIR_TX "SAir Tx" #define D_SENSOR_SPI_CS "SPI CS" #define D_SENSOR_SPI_DC "SPI DC" +#define D_SENSOR_SPI_MISO "SPI MISO" +#define D_SENSOR_SPI_MOSI "SPI MOSI" +#define D_SENSOR_SPI_CLK "SPI CLK" #define D_SENSOR_BACKLIGHT "Háttérfény" #define D_SENSOR_PMS5003 "PMS5003" #define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" @@ -585,6 +603,33 @@ #define D_SENSOR_HRE_CLOCK "HRE Clock" #define D_SENSOR_HRE_DATA "HRE Data" #define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" +#define D_SENSOR_BUZZER "Buzzer" +#define D_SENSOR_OLED_RESET "OLED Reset" +#define D_SENSOR_ZIGBEE_TXD "Zigbee Tx" +#define D_SENSOR_ZIGBEE_RXD "Zigbee Rx" +#define D_SENSOR_SOLAXX1_TX "SolaxX1 Tx" +#define D_SENSOR_SOLAXX1_RX "SolaxX1 Rx" +#define D_SENSOR_IBEACON_TX "iBeacon TX" +#define D_SENSOR_IBEACON_RX "iBeacon RX" +#define D_SENSOR_RDM6300_RX "RDM6300 RX" +#define D_SENSOR_CC1101_CS "CC1101 CS" +#define D_SENSOR_A4988_DIR "A4988 DIR" +#define D_SENSOR_A4988_STP "A4988 STP" +#define D_SENSOR_A4988_ENA "A4988 ENA" +#define D_SENSOR_A4988_MS1 "A4988 MS1" +#define D_SENSOR_A4988_MS2 "A4988 MS2" +#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_DDS2382_TX "DDS238-2 Tx" +#define D_SENSOR_DDS2382_RX "DDS238-2 Rx" +#define D_SENSOR_DDSU666_TX "DDSU666 Tx" +#define D_SENSOR_DDSU666_RX "DDSU666 Rx" +#define D_SENSOR_SM2135_CLK "SM2135 Clk" +#define D_SENSOR_SM2135_DAT "SM2135 Dat" +#define D_SENSOR_DEEPSLEEP "DeepSleep" +#define D_SENSOR_EXS_ENABLE "EXS Enable" +#define D_SENSOR_ARDUINO_TX "Arduino TX" +#define D_SENSOR_ARDUINO_RX "Arduino RX" +#define D_SENSOR_ARDUINO_RESET "Arduino RST" // Units #define D_UNIT_AMPERE "A" @@ -655,4 +700,28 @@ #define D_UNIT_KWARH "kVArh" #define D_UNIT_ANGLE "fok" +//SOLAXX1 +#define D_PV1_VOLTAGE "PV1 Voltage" +#define D_PV1_CURRENT "PV1 Current" +#define D_PV1_POWER "PV1 Power" +#define D_PV2_VOLTAGE "PV2 Voltage" +#define D_PV2_CURRENT "PV2 Current" +#define D_PV2_POWER "PV2 Power" +#define D_SOLAR_POWER "Solar Power" +#define D_INVERTER_POWER "Inverter Power" +#define D_STATUS "Status" +#define D_WAITING "Waiting" +#define D_CHECKING "Checking" +#define D_WORKING "Working" +#define D_FAILURE "Failure" +#define D_SOLAX_ERROR_0 "No Error Code" +#define D_SOLAX_ERROR_1 "Grid Lost Fault" +#define D_SOLAX_ERROR_2 "Grid Voltage Fault" +#define D_SOLAX_ERROR_3 "Grid Frequency Fault" +#define D_SOLAX_ERROR_4 "Pv Voltage Fault" +#define D_SOLAX_ERROR_5 "Isolation Fault" +#define D_SOLAX_ERROR_6 "Over Temperature Fault" +#define D_SOLAX_ERROR_7 "Fan Fault" +#define D_SOLAX_ERROR_8 "Other Device Fault" + #endif // _LANGUAGE_HU_HU_H_ diff --git a/sonoff/language/it-IT.h b/sonoff/language/it-IT.h index f498f9295..912e95ec1 100644 --- a/sonoff/language/it-IT.h +++ b/sonoff/language/it-IT.h @@ -65,7 +65,7 @@ #define D_BY "da" // Written by me #define D_BYTES "Bytes" #define D_CELSIUS "Celsius" -#define D_CHANNEL "Channel" +#define D_CHANNEL "Canale" #define D_CO2 "CO2" #define D_CODE "codice" // Button code #define D_COLDLIGHT "Fredda" @@ -106,7 +106,7 @@ #define D_IMMEDIATE "immediato" // Button immediate #define D_INDEX "Indice" #define D_INFO "Info" -#define D_INFRARED "Infrared" +#define D_INFRARED "Infrarosso" #define D_INITIALIZED "Inizializzato" #define D_IP_ADDRESS "Indirizzo IP" #define D_LIGHT "Luce" @@ -147,9 +147,9 @@ #define D_START "Start" #define D_STD_TIME "STD" #define D_STOP "Stop" -#define D_SUBNET_MASK "Maschera sottorete" +#define D_SUBNET_MASK "Maschera Subnet" #define D_SUBSCRIBE_TO "Sottoscrivi a" -#define D_UNSUBSCRIBE_FROM "Unsubscribe from" +#define D_UNSUBSCRIBE_FROM "Cancella da" #define D_SUCCESSFUL "Riuscito" #define D_SUNRISE "Alba" #define D_SUNSET "Tramonto" @@ -161,16 +161,16 @@ #define D_TRANSMIT "Trasmesso" #define D_TRUE "Vero" #define D_TVOC "TVOC" -#define D_UPGRADE "aggiornamento" +#define D_UPGRADE "Aggiornamento" #define D_UPLOAD "Invio" #define D_UPTIME "Uptime" #define D_USER "Utente" #define D_UTC_TIME "UTC" #define D_UV_INDEX "Indice UV" -#define D_UV_INDEX_1 "Low" -#define D_UV_INDEX_2 "Mid" -#define D_UV_INDEX_3 "High" -#define D_UV_INDEX_4 "Danger" +#define D_UV_INDEX_1 "Basso" +#define D_UV_INDEX_2 "Medio" +#define D_UV_INDEX_3 "Alto" +#define D_UV_INDEX_4 "Pericolo" #define D_UV_INDEX_5 "BurnL1/2" #define D_UV_INDEX_6 "BurnL3" #define D_UV_INDEX_7 "OoR" @@ -186,7 +186,7 @@ #define D_WARNING_MINIMAL_VERSION "ATTENZIONE Questa versione non supporta il salvataggio delle impostazioni" #define D_LEVEL_10 "level 1-0" #define D_LEVEL_01 "level 0-1" -#define D_SERIAL_LOGGING_DISABLED "Log seriale disabilitato" +#define D_SERIAL_LOGGING_DISABLED "Log Seriale disabilitato" #define D_SYSLOG_LOGGING_REENABLED "Syslog ri-abilitato" #define D_SET_BAUDRATE_TO "Baudrate impostato a" @@ -198,11 +198,11 @@ #define D_OSWATCH "osWatch" #define D_BLOCKED_LOOP "Ciclo Bloccato" #define D_WPS_FAILED_WITH_STATUS "WPSconfig Fallito con stato" -#define D_ACTIVE_FOR_3_MINUTES "attivo per 3 minuti" -#define D_FAILED_TO_START "partenza fallita" +#define D_ACTIVE_FOR_3_MINUTES "Attivo per 3 minuti" +#define D_FAILED_TO_START "Partenza fallita" #define D_PATCH_ISSUE_2186 "Patch issue 2186" #define D_CONNECTING_TO_AP "Connessione ad AP" -#define D_IN_MODE "in modalità" +#define D_IN_MODE "In modalità" #define D_CONNECT_FAILED_NO_IP_ADDRESS "Connessione fallita, indirizzo IP non ricevuto" #define D_CONNECT_FAILED_AP_NOT_REACHED "Connessione fallita, AP non raggiungibile" #define D_CONNECT_FAILED_WRONG_PASSWORD "Connessione fallita, password AP non corretta" @@ -259,8 +259,8 @@ #define D_PULLUP_ENABLE "No Button/Switch pull-up" #define D_ADC "ADC" #define D_GPIO "GPIO" -#define D_SERIAL_IN "Serial In" -#define D_SERIAL_OUT "Serial Out" +#define D_SERIAL_IN "Seriale In" +#define D_SERIAL_OUT "Seriale Out" #define D_WIFI_PARAMETERS "Parametri Wifi" #define D_SCAN_FOR_WIFI_NETWORKS "Scansione delle reti wifi" @@ -283,30 +283,31 @@ #define D_FULL_TOPIC "Full Topic" #define D_LOGGING_PARAMETERS "Parametri Logging" -#define D_SERIAL_LOG_LEVEL "Seriale livello di log" -#define D_WEB_LOG_LEVEL "Web livello di log" -#define D_SYS_LOG_LEVEL "Sys livello di log" +#define D_SERIAL_LOG_LEVEL "Livello di log Seriale" +#define D_MQTT_LOG_LEVEL "Mqtt log level" +#define D_WEB_LOG_LEVEL "livello di log Web" +#define D_SYS_LOG_LEVEL "livello di log Sys" #define D_MORE_DEBUG "Debug aggiuntivo" #define D_SYSLOG_HOST "Syslog host" #define D_SYSLOG_PORT "Syslog porta" #define D_TELEMETRY_PERIOD "Periodo Telemetria" #define D_OTHER_PARAMETERS "Altri parametri" -#define D_TEMPLATE "Template" -#define D_ACTIVATE "Activate" +#define D_TEMPLATE "Modello" +#define D_ACTIVATE "Attivare" #define D_WEB_ADMIN_PASSWORD "Password Amministratore Web" #define D_MQTT_ENABLE "Abilita MQTT" -#define D_FRIENDLY_NAME "Nome confidenziale" +#define D_FRIENDLY_NAME "Nome amichevole" #define D_BELKIN_WEMO "Belkin WeMo" #define D_HUE_BRIDGE "Hue Bridge" #define D_SINGLE_DEVICE "dispositivo singolo" #define D_MULTI_DEVICE "dispositivo multiplo" -#define D_CONFIGURE_TEMPLATE "Configure Template" -#define D_TEMPLATE_PARAMETERS "Template parameters" -#define D_TEMPLATE_NAME "Name" -#define D_BASE_TYPE "Based on" -#define D_TEMPLATE_FLAGS "Options" +#define D_CONFIGURE_TEMPLATE "Configurare Modello" +#define D_TEMPLATE_PARAMETERS "Parametri Modello" +#define D_TEMPLATE_NAME "Nome" +#define D_BASE_TYPE "Basato nel" +#define D_TEMPLATE_FLAGS "Opzioni" #define D_SAVE_CONFIGURATION "Salva configurazione" #define D_CONFIGURATION_SAVED "Configurazione salvata" @@ -400,6 +401,7 @@ #define D_DOMOTICZ_VOLTAGE "Voltage/PM2.5" #define D_DOMOTICZ_CURRENT "Current/PM10" #define D_DOMOTICZ_AIRQUALITY "AirQuality" + #define D_DOMOTICZ_P1_SMART_METER "P1SmartMeter" #define D_DOMOTICZ_UPDATE_TIMER "Intervallo di aggiornamento" // xdrv_09_timers.ino @@ -441,6 +443,19 @@ #define D_ENERGY_YESTERDAY "Energia Ieri" #define D_ENERGY_TOTAL "Energia Totale" +// xdrv_27_shutter.ino +#define D_OPEN "Aperta" +#define D_CLOSE "Chiusa" +#define D_DOMOTICZ_SHUTTER "Serranda" + +// xdrv_28_pcf8574.ino +#define D_CONFIGURE_PCF8574 "Configura PCF8574" +#define D_PCF8574_PARAMETERS "Parametri PCF8574" +#define D_INVERT_PORTS "Porte Invertite" +#define D_DEVICE "Dispositivo" +#define D_DEVICE_INPUT "Input" +#define D_DEVICE_OUTPUT "Output" + // xsns_05_ds18b20.ino #define D_SENSOR_BUSY "Sensore occupato" #define D_SENSOR_CRC_ERROR "Sensore errore CRC" @@ -463,12 +478,12 @@ #define D_PARTICALS_BEYOND "Particelle" // xsns_32_mpu6050.ino -#define D_AX_AXIS "Accel. X-Axis" -#define D_AY_AXIS "Accel. Y-Axis" -#define D_AZ_AXIS "Accel. Z-Axis" -#define D_GX_AXIS "Gyro X-Axis" -#define D_GY_AXIS "Gyro Y-Axis" -#define D_GZ_AXIS "Gyro Z-Axis" +#define D_AX_AXIS "Accel. Asse-X" +#define D_AY_AXIS "Accel. Asse-Y" +#define D_AZ_AXIS "Accel. Asse-Z" +#define D_GX_AXIS "Gyro Asse-X" +#define D_GY_AXIS "Gyro Asse-Y" +#define D_GZ_AXIS "Gyro Asse-Z" // xsns_34_hx711.ino #define D_HX_CAL_REMOVE "Rimuovere peso" @@ -491,7 +506,7 @@ #define D_TX20_NORTH "N" #define D_TX20_EAST "E" #define D_TX20_SOUTH "S" -#define D_TX20_WEST "W" +#define D_TX20_WEST "O" //xsns_43_hre.ino #define D_LOG_HRE "HRE: " @@ -526,7 +541,10 @@ #define D_SENSOR_SAIR_TX "SAir Tx" #define D_SENSOR_SPI_CS "SPI CS" #define D_SENSOR_SPI_DC "SPI DC" -#define D_SENSOR_BACKLIGHT "BkLight" +#define D_SENSOR_SPI_MISO "SPI MISO" +#define D_SENSOR_SPI_MOSI "SPI MOSI" +#define D_SENSOR_SPI_CLK "SPI CLK" +#define D_SENSOR_BACKLIGHT "Backlight" #define D_SENSOR_PMS5003 "PMS5003" #define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" #define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" @@ -585,6 +603,33 @@ #define D_SENSOR_HRE_CLOCK "HRE Clock" #define D_SENSOR_HRE_DATA "HRE Data" #define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" +#define D_SENSOR_BUZZER "Buzzer" +#define D_SENSOR_OLED_RESET "OLED Reset" +#define D_SENSOR_ZIGBEE_TXD "Zigbee Tx" +#define D_SENSOR_ZIGBEE_RXD "Zigbee Rx" +#define D_SENSOR_SOLAXX1_TX "SolaxX1 Tx" +#define D_SENSOR_SOLAXX1_RX "SolaxX1 Rx" +#define D_SENSOR_IBEACON_TX "iBeacon TX" +#define D_SENSOR_IBEACON_RX "iBeacon RX" +#define D_SENSOR_RDM6300_RX "RDM6300 RX" +#define D_SENSOR_CC1101_CS "CC1101 CS" +#define D_SENSOR_A4988_DIR "A4988 DIR" +#define D_SENSOR_A4988_STP "A4988 STP" +#define D_SENSOR_A4988_ENA "A4988 ENA" +#define D_SENSOR_A4988_MS1 "A4988 MS1" +#define D_SENSOR_A4988_MS2 "A4988 MS2" +#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_DDS2382_TX "DDS238-2 Tx" +#define D_SENSOR_DDS2382_RX "DDS238-2 Rx" +#define D_SENSOR_DDSU666_TX "DDSU666 Tx" +#define D_SENSOR_DDSU666_RX "DDSU666 Rx" +#define D_SENSOR_SM2135_CLK "SM2135 Clk" +#define D_SENSOR_SM2135_DAT "SM2135 Dat" +#define D_SENSOR_DEEPSLEEP "DeepSleep" +#define D_SENSOR_EXS_ENABLE "EXS Enable" +#define D_SENSOR_ARDUINO_TX "Arduino TX" +#define D_SENSOR_ARDUINO_RX "Arduino RX" +#define D_SENSOR_ARDUINO_RESET "Arduino RST" // Units #define D_UNIT_AMPERE "A" @@ -655,4 +700,28 @@ #define D_UNIT_KWARH "kVArh" #define D_UNIT_ANGLE "°" +//SOLAXX1 +#define D_PV1_VOLTAGE "PV1 Voltaggio" +#define D_PV1_CURRENT "PV1 Corrente" +#define D_PV1_POWER "PV1 Eergia" +#define D_PV2_VOLTAGE "PV2 Voltaggio" +#define D_PV2_CURRENT "PV2 Corrente" +#define D_PV2_POWER "PV2 Energia" +#define D_SOLAR_POWER "Energia Solar" +#define D_INVERTER_POWER "Energia Inverter" +#define D_STATUS "Stato" +#define D_WAITING "In attesa" +#define D_CHECKING "Controllando" +#define D_WORKING "Lavorando" +#define D_FAILURE "Errore" +#define D_SOLAX_ERROR_0 "No Codice Errore" +#define D_SOLAX_ERROR_1 "Errore Grid Persa" +#define D_SOLAX_ERROR_2 "Errore Voltaggio Grid" +#define D_SOLAX_ERROR_3 "Errore Frequenza Grid" +#define D_SOLAX_ERROR_4 "Errore Voltaggio Pv" +#define D_SOLAX_ERROR_5 "Errore Isolamento" +#define D_SOLAX_ERROR_6 "Errore Temperatura Eccessiva" +#define D_SOLAX_ERROR_7 "Errore Ventilatore" +#define D_SOLAX_ERROR_8 "Altro Errore del Dispositivo" + #endif // _LANGUAGE_IT_IT_H_ diff --git a/sonoff/language/ko-KO.h b/sonoff/language/ko-KO.h index 0372574c9..5301b8913 100644 --- a/sonoff/language/ko-KO.h +++ b/sonoff/language/ko-KO.h @@ -284,6 +284,7 @@ #define D_LOGGING_PARAMETERS "로그 상세" #define D_SERIAL_LOG_LEVEL "시리얼 로그 레벨" +#define D_MQTT_LOG_LEVEL "Mqtt log level" #define D_WEB_LOG_LEVEL "Web 로그 레벨" #define D_SYS_LOG_LEVEL "Syslog 로그 레벨" #define D_MORE_DEBUG "More debug" @@ -400,6 +401,7 @@ #define D_DOMOTICZ_VOLTAGE "전압/PM2.5" #define D_DOMOTICZ_CURRENT "전류/PM10" #define D_DOMOTICZ_AIRQUALITY "공기질" + #define D_DOMOTICZ_P1_SMART_METER "P1SmartMeter" #define D_DOMOTICZ_UPDATE_TIMER "타이머 갱신" // xdrv_09_timers.ino @@ -441,6 +443,19 @@ #define D_ENERGY_YESTERDAY "어제 전력 사용량" #define D_ENERGY_TOTAL "총 전력 사용량" +// xdrv_27_shutter.ino +#define D_OPEN "Open" +#define D_CLOSE "Close" +#define D_DOMOTICZ_SHUTTER "Shutter" + +// xdrv_28_pcf8574.ino +#define D_CONFIGURE_PCF8574 "Configure PCF8574" +#define D_PCF8574_PARAMETERS "PCF8574 parameters" +#define D_INVERT_PORTS "Invert Ports" +#define D_DEVICE "Device" +#define D_DEVICE_INPUT "Input" +#define D_DEVICE_OUTPUT "Output" + // xsns_05_ds18b20.ino #define D_SENSOR_BUSY "센서가 사용 중" #define D_SENSOR_CRC_ERROR "센서 CRC 에러" @@ -526,7 +541,10 @@ #define D_SENSOR_SAIR_TX "SAir Tx" #define D_SENSOR_SPI_CS "SPI CS" #define D_SENSOR_SPI_DC "SPI DC" -#define D_SENSOR_BACKLIGHT "BkLight" +#define D_SENSOR_SPI_MISO "SPI MISO" +#define D_SENSOR_SPI_MOSI "SPI MOSI" +#define D_SENSOR_SPI_CLK "SPI CLK" +#define D_SENSOR_BACKLIGHT "Backlight" #define D_SENSOR_PMS5003 "PMS5003" #define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" #define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" @@ -585,6 +603,33 @@ #define D_SENSOR_HRE_CLOCK "HRE Clock" #define D_SENSOR_HRE_DATA "HRE Data" #define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" +#define D_SENSOR_BUZZER "Buzzer" +#define D_SENSOR_OLED_RESET "OLED Reset" +#define D_SENSOR_ZIGBEE_TXD "Zigbee Tx" +#define D_SENSOR_ZIGBEE_RXD "Zigbee Rx" +#define D_SENSOR_SOLAXX1_TX "SolaxX1 Tx" +#define D_SENSOR_SOLAXX1_RX "SolaxX1 Rx" +#define D_SENSOR_IBEACON_TX "iBeacon TX" +#define D_SENSOR_IBEACON_RX "iBeacon RX" +#define D_SENSOR_RDM6300_RX "RDM6300 RX" +#define D_SENSOR_CC1101_CS "CC1101 CS" +#define D_SENSOR_A4988_DIR "A4988 DIR" +#define D_SENSOR_A4988_STP "A4988 STP" +#define D_SENSOR_A4988_ENA "A4988 ENA" +#define D_SENSOR_A4988_MS1 "A4988 MS1" +#define D_SENSOR_A4988_MS2 "A4988 MS2" +#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_DDS2382_TX "DDS238-2 Tx" +#define D_SENSOR_DDS2382_RX "DDS238-2 Rx" +#define D_SENSOR_DDSU666_TX "DDSU666 Tx" +#define D_SENSOR_DDSU666_RX "DDSU666 Rx" +#define D_SENSOR_SM2135_CLK "SM2135 Clk" +#define D_SENSOR_SM2135_DAT "SM2135 Dat" +#define D_SENSOR_DEEPSLEEP "DeepSleep" +#define D_SENSOR_EXS_ENABLE "EXS Enable" +#define D_SENSOR_ARDUINO_TX "Arduino TX" +#define D_SENSOR_ARDUINO_RX "Arduino RX" +#define D_SENSOR_ARDUINO_RESET "Arduino RST" // Units #define D_UNIT_AMPERE "A" @@ -655,4 +700,28 @@ #define D_UNIT_KWARH "kVArh" #define D_UNIT_ANGLE "Deg" +//SOLAXX1 +#define D_PV1_VOLTAGE "PV1 Voltage" +#define D_PV1_CURRENT "PV1 Current" +#define D_PV1_POWER "PV1 Power" +#define D_PV2_VOLTAGE "PV2 Voltage" +#define D_PV2_CURRENT "PV2 Current" +#define D_PV2_POWER "PV2 Power" +#define D_SOLAR_POWER "Solar Power" +#define D_INVERTER_POWER "Inverter Power" +#define D_STATUS "Status" +#define D_WAITING "Waiting" +#define D_CHECKING "Checking" +#define D_WORKING "Working" +#define D_FAILURE "Failure" +#define D_SOLAX_ERROR_0 "No Error Code" +#define D_SOLAX_ERROR_1 "Grid Lost Fault" +#define D_SOLAX_ERROR_2 "Grid Voltage Fault" +#define D_SOLAX_ERROR_3 "Grid Frequency Fault" +#define D_SOLAX_ERROR_4 "Pv Voltage Fault" +#define D_SOLAX_ERROR_5 "Isolation Fault" +#define D_SOLAX_ERROR_6 "Over Temperature Fault" +#define D_SOLAX_ERROR_7 "Fan Fault" +#define D_SOLAX_ERROR_8 "Other Device Fault" + #endif // _LANGUAGE_KO_KO_H_ diff --git a/sonoff/language/nl-NL.h b/sonoff/language/nl-NL.h index 364fea246..4091f9928 100644 --- a/sonoff/language/nl-NL.h +++ b/sonoff/language/nl-NL.h @@ -284,6 +284,7 @@ #define D_LOGGING_PARAMETERS "Logging parameters" #define D_SERIAL_LOG_LEVEL "Serieel log niveau" +#define D_MQTT_LOG_LEVEL "Mqtt log level" #define D_WEB_LOG_LEVEL "Web log niveau" #define D_SYS_LOG_LEVEL "Syslog niveau" #define D_MORE_DEBUG "Meer debug" @@ -400,6 +401,7 @@ #define D_DOMOTICZ_VOLTAGE "Spanning/PM2,5" #define D_DOMOTICZ_CURRENT "Stroom/PM10" #define D_DOMOTICZ_AIRQUALITY "AirQuality" + #define D_DOMOTICZ_P1_SMART_METER "P1SmartMeter" #define D_DOMOTICZ_UPDATE_TIMER "Bijwerk timer" // xdrv_09_timers.ino @@ -441,6 +443,19 @@ #define D_ENERGY_YESTERDAY "Verbruik gisteren" #define D_ENERGY_TOTAL "Verbruik totaal" +// xdrv_27_shutter.ino +#define D_OPEN "Open" +#define D_CLOSE "Close" +#define D_DOMOTICZ_SHUTTER "Shutter" + +// xdrv_28_pcf8574.ino +#define D_CONFIGURE_PCF8574 "Configure PCF8574" +#define D_PCF8574_PARAMETERS "PCF8574 parameters" +#define D_INVERT_PORTS "Invert Ports" +#define D_DEVICE "Device" +#define D_DEVICE_INPUT "Input" +#define D_DEVICE_OUTPUT "Output" + // xsns_05_ds18b20.ino #define D_SENSOR_BUSY "Sensor bezet" #define D_SENSOR_CRC_ERROR "Sensor CRC fout" @@ -526,7 +541,10 @@ #define D_SENSOR_SAIR_TX "SAir Tx" #define D_SENSOR_SPI_CS "SPI CS" #define D_SENSOR_SPI_DC "SPI DC" -#define D_SENSOR_BACKLIGHT "BkLight" +#define D_SENSOR_SPI_MISO "SPI MISO" +#define D_SENSOR_SPI_MOSI "SPI MOSI" +#define D_SENSOR_SPI_CLK "SPI CLK" +#define D_SENSOR_BACKLIGHT "Backlight" #define D_SENSOR_PMS5003 "PMS5003" #define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" #define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" @@ -585,6 +603,33 @@ #define D_SENSOR_HRE_CLOCK "HRE Clock" #define D_SENSOR_HRE_DATA "HRE Data" #define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" +#define D_SENSOR_BUZZER "Zoemer" +#define D_SENSOR_OLED_RESET "OLED Reset" +#define D_SENSOR_ZIGBEE_TXD "Zigbee Tx" +#define D_SENSOR_ZIGBEE_RXD "Zigbee Rx" +#define D_SENSOR_SOLAXX1_TX "SolaxX1 Tx" +#define D_SENSOR_SOLAXX1_RX "SolaxX1 Rx" +#define D_SENSOR_IBEACON_TX "iBeacon TX" +#define D_SENSOR_IBEACON_RX "iBeacon RX" +#define D_SENSOR_RDM6300_RX "RDM6300 RX" +#define D_SENSOR_CC1101_CS "CC1101 CS" +#define D_SENSOR_A4988_DIR "A4988 DIR" +#define D_SENSOR_A4988_STP "A4988 STP" +#define D_SENSOR_A4988_ENA "A4988 ENA" +#define D_SENSOR_A4988_MS1 "A4988 MS1" +#define D_SENSOR_A4988_MS2 "A4988 MS2" +#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_DDS2382_TX "DDS238-2 Tx" +#define D_SENSOR_DDS2382_RX "DDS238-2 Rx" +#define D_SENSOR_DDSU666_TX "DDSU666 Tx" +#define D_SENSOR_DDSU666_RX "DDSU666 Rx" +#define D_SENSOR_SM2135_CLK "SM2135 Clk" +#define D_SENSOR_SM2135_DAT "SM2135 Dat" +#define D_SENSOR_DEEPSLEEP "DeepSleep" +#define D_SENSOR_EXS_ENABLE "EXS Enable" +#define D_SENSOR_ARDUINO_TX "Arduino TX" +#define D_SENSOR_ARDUINO_RX "Arduino RX" +#define D_SENSOR_ARDUINO_RESET "Arduino RST" // Units #define D_UNIT_AMPERE "A" @@ -655,4 +700,28 @@ #define D_UNIT_KWARH "kVArh" #define D_UNIT_ANGLE "Deg" +//SOLAXX1 +#define D_PV1_VOLTAGE "PV1 Voltage" +#define D_PV1_CURRENT "PV1 Current" +#define D_PV1_POWER "PV1 Power" +#define D_PV2_VOLTAGE "PV2 Voltage" +#define D_PV2_CURRENT "PV2 Current" +#define D_PV2_POWER "PV2 Power" +#define D_SOLAR_POWER "Solar Power" +#define D_INVERTER_POWER "Inverter Power" +#define D_STATUS "Status" +#define D_WAITING "Waiting" +#define D_CHECKING "Checking" +#define D_WORKING "Working" +#define D_FAILURE "Failure" +#define D_SOLAX_ERROR_0 "No Error Code" +#define D_SOLAX_ERROR_1 "Grid Lost Fault" +#define D_SOLAX_ERROR_2 "Grid Voltage Fault" +#define D_SOLAX_ERROR_3 "Grid Frequency Fault" +#define D_SOLAX_ERROR_4 "Pv Voltage Fault" +#define D_SOLAX_ERROR_5 "Isolation Fault" +#define D_SOLAX_ERROR_6 "Over Temperature Fault" +#define D_SOLAX_ERROR_7 "Fan Fault" +#define D_SOLAX_ERROR_8 "Other Device Fault" + #endif // _LANGUAGE_NL_NL_H_ diff --git a/sonoff/language/pl-PL.h b/sonoff/language/pl-PL.h index a08e9ecc8..bd03d60e3 100644 --- a/sonoff/language/pl-PL.h +++ b/sonoff/language/pl-PL.h @@ -284,6 +284,7 @@ #define D_LOGGING_PARAMETERS "Opcje dziennika" #define D_SERIAL_LOG_LEVEL "Serial poziom dziennika" +#define D_MQTT_LOG_LEVEL "Mqtt log level" #define D_WEB_LOG_LEVEL "Web poziom dziennika" #define D_SYS_LOG_LEVEL "System poziom dziennika" #define D_MORE_DEBUG "Więcej informacji debug" @@ -292,21 +293,21 @@ #define D_TELEMETRY_PERIOD "Okres telemetrii" #define D_OTHER_PARAMETERS "Inne parametry" -#define D_TEMPLATE "Template" -#define D_ACTIVATE "Activate" +#define D_TEMPLATE "Szablon" +#define D_ACTIVATE "Aktywuj" #define D_WEB_ADMIN_PASSWORD "Hasło administratora Web" #define D_MQTT_ENABLE "MQTT aktywne" #define D_FRIENDLY_NAME "Twoja nazwa" #define D_BELKIN_WEMO "Belkin WeMo" #define D_HUE_BRIDGE "Hue Bridge" -#define D_SINGLE_DEVICE "single device" -#define D_MULTI_DEVICE "multi device" +#define D_SINGLE_DEVICE "pojedyncze urządzenie" +#define D_MULTI_DEVICE "wiele urządzeń" -#define D_CONFIGURE_TEMPLATE "Configure Template" -#define D_TEMPLATE_PARAMETERS "Template parameters" -#define D_TEMPLATE_NAME "Name" -#define D_BASE_TYPE "Based on" -#define D_TEMPLATE_FLAGS "Options" +#define D_CONFIGURE_TEMPLATE "Konfiguruj szablon" +#define D_TEMPLATE_PARAMETERS "Parametry szablonu" +#define D_TEMPLATE_NAME "Nazwa" +#define D_BASE_TYPE "Na bazie" +#define D_TEMPLATE_FLAGS "Opcje" #define D_SAVE_CONFIGURATION "Zapisz ustawienia" #define D_CONFIGURATION_SAVED "Ustawienia zapisane" @@ -400,6 +401,7 @@ #define D_DOMOTICZ_VOLTAGE "Napięcie/PM2,5" #define D_DOMOTICZ_CURRENT "Prąd/PM10" #define D_DOMOTICZ_AIRQUALITY "AirQuality" + #define D_DOMOTICZ_P1_SMART_METER "P1SmartMeter" #define D_DOMOTICZ_UPDATE_TIMER "Zaktualizuj czasomierz" // xdrv_09_timers.ino @@ -441,6 +443,19 @@ #define D_ENERGY_YESTERDAY "Energia Wczoraj" #define D_ENERGY_TOTAL "Energia suma" +// xdrv_27_shutter.ino +#define D_OPEN "Open" +#define D_CLOSE "Close" +#define D_DOMOTICZ_SHUTTER "Shutter" + +// xdrv_28_pcf8574.ino +#define D_CONFIGURE_PCF8574 "Configure PCF8574" +#define D_PCF8574_PARAMETERS "PCF8574 parameters" +#define D_INVERT_PORTS "Invert Ports" +#define D_DEVICE "Device" +#define D_DEVICE_INPUT "Input" +#define D_DEVICE_OUTPUT "Output" + // xsns_05_ds18b20.ino #define D_SENSOR_BUSY "Czujnik DS18x20 zajęty" #define D_SENSOR_CRC_ERROR "Czujnik DS18x20 błąd CRC" @@ -526,7 +541,10 @@ #define D_SENSOR_SAIR_TX "SAir Tx" #define D_SENSOR_SPI_CS "SPI CS" #define D_SENSOR_SPI_DC "SPI DC" -#define D_SENSOR_BACKLIGHT "BkLight" +#define D_SENSOR_SPI_MISO "SPI MISO" +#define D_SENSOR_SPI_MOSI "SPI MOSI" +#define D_SENSOR_SPI_CLK "SPI CLK" +#define D_SENSOR_BACKLIGHT "Backlight" #define D_SENSOR_PMS5003 "PMS5003" #define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" #define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" @@ -585,6 +603,33 @@ #define D_SENSOR_HRE_CLOCK "HRE Clock" #define D_SENSOR_HRE_DATA "HRE Data" #define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" +#define D_SENSOR_BUZZER "Buzzer" +#define D_SENSOR_OLED_RESET "OLED Reset" +#define D_SENSOR_ZIGBEE_TXD "Zigbee Tx" +#define D_SENSOR_ZIGBEE_RXD "Zigbee Rx" +#define D_SENSOR_SOLAXX1_TX "SolaxX1 Tx" +#define D_SENSOR_SOLAXX1_RX "SolaxX1 Rx" +#define D_SENSOR_IBEACON_TX "iBeacon TX" +#define D_SENSOR_IBEACON_RX "iBeacon RX" +#define D_SENSOR_RDM6300_RX "RDM6300 RX" +#define D_SENSOR_CC1101_CS "CC1101 CS" +#define D_SENSOR_A4988_DIR "A4988 DIR" +#define D_SENSOR_A4988_STP "A4988 STP" +#define D_SENSOR_A4988_ENA "A4988 ENA" +#define D_SENSOR_A4988_MS1 "A4988 MS1" +#define D_SENSOR_A4988_MS2 "A4988 MS2" +#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_DDS2382_TX "DDS238-2 Tx" +#define D_SENSOR_DDS2382_RX "DDS238-2 Rx" +#define D_SENSOR_DDSU666_TX "DDSU666 Tx" +#define D_SENSOR_DDSU666_RX "DDSU666 Rx" +#define D_SENSOR_SM2135_CLK "SM2135 Clk" +#define D_SENSOR_SM2135_DAT "SM2135 Dat" +#define D_SENSOR_DEEPSLEEP "DeepSleep" +#define D_SENSOR_EXS_ENABLE "EXS Enable" +#define D_SENSOR_ARDUINO_TX "Arduino TX" +#define D_SENSOR_ARDUINO_RX "Arduino RX" +#define D_SENSOR_ARDUINO_RESET "Arduino RST" // Units #define D_UNIT_AMPERE "A" @@ -655,4 +700,28 @@ #define D_UNIT_KWARH "kVArh" #define D_UNIT_ANGLE "Deg" +//SOLAXX1 +#define D_PV1_VOLTAGE "PV1 Voltage" +#define D_PV1_CURRENT "PV1 Current" +#define D_PV1_POWER "PV1 Power" +#define D_PV2_VOLTAGE "PV2 Voltage" +#define D_PV2_CURRENT "PV2 Current" +#define D_PV2_POWER "PV2 Power" +#define D_SOLAR_POWER "Solar Power" +#define D_INVERTER_POWER "Inverter Power" +#define D_STATUS "Status" +#define D_WAITING "Waiting" +#define D_CHECKING "Checking" +#define D_WORKING "Working" +#define D_FAILURE "Failure" +#define D_SOLAX_ERROR_0 "No Error Code" +#define D_SOLAX_ERROR_1 "Grid Lost Fault" +#define D_SOLAX_ERROR_2 "Grid Voltage Fault" +#define D_SOLAX_ERROR_3 "Grid Frequency Fault" +#define D_SOLAX_ERROR_4 "Pv Voltage Fault" +#define D_SOLAX_ERROR_5 "Isolation Fault" +#define D_SOLAX_ERROR_6 "Over Temperature Fault" +#define D_SOLAX_ERROR_7 "Fan Fault" +#define D_SOLAX_ERROR_8 "Other Device Fault" + #endif // _LANGUAGE_PL_PL_D_H_ diff --git a/sonoff/language/pt-BR.h b/sonoff/language/pt-BR.h index 863c40b0f..5d7b02613 100644 --- a/sonoff/language/pt-BR.h +++ b/sonoff/language/pt-BR.h @@ -65,8 +65,8 @@ #define D_BY "por" // Write by me #define D_BYTES "Bytes" #define D_CELSIUS "Celsius" -#define D_CHANNEL "Channel" -#define D_CO2 "Dióxido de carbono" +#define D_CHANNEL "Canal" +#define D_CO2 "CO2" #define D_CODE "Código" // Button code #define D_COLDLIGHT "Luz fria" #define D_COMMAND "Comando" @@ -93,14 +93,14 @@ #define D_FALLBACK_TOPIC "Tópico para retornar" #define D_FALSE "Falso" #define D_FILE "Arquivo" -#define D_FLOW_RATE "Flow rate" +#define D_FLOW_RATE "Quociente de vazão" #define D_FREE_MEMORY "Memória livre" #define D_FREQUENCY "Frequência" #define D_GAS "Gás" #define D_GATEWAY "Gateway" #define D_GROUP "Grupo" -#define D_HOST "Anfitrião" -#define D_HOSTNAME "Nome do anfitrião" +#define D_HOST "Host" +#define D_HOSTNAME "Nome do Host" #define D_HUMIDITY "Umidade" #define D_ILLUMINANCE "Luminância" #define D_IMMEDIATE "Imediato" // Button immediate @@ -167,10 +167,10 @@ #define D_USER "Usuário" #define D_UTC_TIME "UTC" #define D_UV_INDEX "Índice UV" -#define D_UV_INDEX_1 "Low" -#define D_UV_INDEX_2 "Mid" -#define D_UV_INDEX_3 "High" -#define D_UV_INDEX_4 "Danger" +#define D_UV_INDEX_1 "Baixo" +#define D_UV_INDEX_2 "Médio" +#define D_UV_INDEX_3 "Alto" +#define D_UV_INDEX_4 "Perigro" #define D_UV_INDEX_5 "BurnL1/2" #define D_UV_INDEX_6 "BurnL3" #define D_UV_INDEX_7 "OoR" @@ -178,7 +178,7 @@ #define D_UV_POWER "UV Power" #define D_VERSION "Versão" #define D_VOLTAGE "Voltagem" -#define D_WEIGHT "Weight" +#define D_WEIGHT "Peso" #define D_WARMLIGHT "Luz quente" #define D_WEB_SERVER "Servidor WEB" @@ -186,7 +186,7 @@ #define D_WARNING_MINIMAL_VERSION "AVISO: esta versão não supporta configurações persistentes" #define D_LEVEL_10 "nível 1-0" #define D_LEVEL_01 "nível 0-1" -#define D_SERIAL_LOGGING_DISABLED "Registro em série desabilitado" +#define D_SERIAL_LOGGING_DISABLED "Registro Serial desabilitado" #define D_SYSLOG_LOGGING_REENABLED "Registro do Syslog reativado" #define D_SET_BAUDRATE_TO "Ajuste da velocidade para" @@ -284,6 +284,7 @@ #define D_LOGGING_PARAMETERS "Parâmetros Logging" #define D_SERIAL_LOG_LEVEL "Nível de registro serial" +#define D_MQTT_LOG_LEVEL "Mqtt log level" #define D_WEB_LOG_LEVEL "Nível de registro WEB" #define D_SYS_LOG_LEVEL "Nível de registro Syslog" #define D_MORE_DEBUG "Depurar mais" @@ -292,7 +293,7 @@ #define D_TELEMETRY_PERIOD "Período de telemetria" #define D_OTHER_PARAMETERS "Outros parâmetros" -#define D_TEMPLATE "Template" +#define D_TEMPLATE "Modelo" #define D_ACTIVATE "Activate" #define D_WEB_ADMIN_PASSWORD "Senha de WEB Admin" #define D_MQTT_ENABLE "MQTT habilitado" @@ -302,11 +303,11 @@ #define D_SINGLE_DEVICE "Dispositivo único" #define D_MULTI_DEVICE "Múltiplos dispositivos" -#define D_CONFIGURE_TEMPLATE "Configure Template" -#define D_TEMPLATE_PARAMETERS "Template parameters" -#define D_TEMPLATE_NAME "Name" -#define D_BASE_TYPE "Based on" -#define D_TEMPLATE_FLAGS "Options" +#define D_CONFIGURE_TEMPLATE "Configurar Modelo" +#define D_TEMPLATE_PARAMETERS "Parâmetros Modelo" +#define D_TEMPLATE_NAME "Nome" +#define D_BASE_TYPE "Baseado em" +#define D_TEMPLATE_FLAGS "Opções" #define D_SAVE_CONFIGURATION "Gravar configuração" #define D_CONFIGURATION_SAVED "Configuração gravada" @@ -347,10 +348,10 @@ #define D_UPLOAD_ERR_7 "Envio cancelado" #define D_UPLOAD_ERR_8 "Arquivo inválido" #define D_UPLOAD_ERR_9 "Arquivo muito grande" -#define D_UPLOAD_ERR_10 "Failed to init RF chip" -#define D_UPLOAD_ERR_11 "Failed to erase RF chip" -#define D_UPLOAD_ERR_12 "Failed to write to RF chip" -#define D_UPLOAD_ERR_13 "Failed to decode RF firmware" +#define D_UPLOAD_ERR_10 "Falha ao iniciar chip RF" +#define D_UPLOAD_ERR_11 "Falha ao apagar o chip RF" +#define D_UPLOAD_ERR_12 "Falha ao escrever o chip RF" +#define D_UPLOAD_ERR_13 "Falha ao decodificar o firmware de RF" #define D_UPLOAD_ERROR_CODE "Código de erro do envio" #define D_ENTER_COMMAND "Inserir comando" @@ -400,6 +401,7 @@ #define D_DOMOTICZ_VOLTAGE "Voltagem/PM2.5" #define D_DOMOTICZ_CURRENT "Corrente/PM10" #define D_DOMOTICZ_AIRQUALITY "Qualidade do ar" + #define D_DOMOTICZ_P1_SMART_METER "P1SmartMeter" #define D_DOMOTICZ_UPDATE_TIMER "Tempo de atualização" // xdrv_09_timers.ino @@ -441,6 +443,19 @@ #define D_ENERGY_YESTERDAY "Consumo energético de ontem" #define D_ENERGY_TOTAL "Consumo total de energia" +// xdrv_27_shutter.ino +#define D_OPEN "Aberta" +#define D_CLOSE "Fechada" +#define D_DOMOTICZ_SHUTTER "Persiana" + +// xdrv_28_pcf8574.ino +#define D_CONFIGURE_PCF8574 "Configura PCF8574" +#define D_PCF8574_PARAMETERS "Parâmetros PCF8574" +#define D_INVERT_PORTS "Portas Invertidas" +#define D_DEVICE "Dispositivo" +#define D_DEVICE_INPUT "Input" +#define D_DEVICE_OUTPUT "Output" + // xsns_05_ds18b20.ino #define D_SENSOR_BUSY "Sensor ocupado" #define D_SENSOR_CRC_ERROR "Erro sensor CRC" @@ -471,27 +486,27 @@ #define D_GZ_AXIS "Gyro Z-Axis" // xsns_34_hx711.ino -#define D_HX_CAL_REMOVE "Remove weigth" -#define D_HX_CAL_REFERENCE "Load reference weigth" -#define D_HX_CAL_DONE "Calibrated" -#define D_HX_CAL_FAIL "Calibration failed" -#define D_RESET_HX711 "Reset Scale" -#define D_CONFIGURE_HX711 "Configure Scale" -#define D_HX711_PARAMETERS "Scale parameters" -#define D_ITEM_WEIGHT "Item weight" -#define D_REFERENCE_WEIGHT "Reference weigth" -#define D_CALIBRATE "Calibrate" -#define D_CALIBRATION "Calibration" +#define D_HX_CAL_REMOVE "Remover peso" +#define D_HX_CAL_REFERENCE "Peso de referência de carga" +#define D_HX_CAL_DONE "Calibrado" +#define D_HX_CAL_FAIL "Falha na calibração" +#define D_RESET_HX711 "Redefinir escala" +#define D_CONFIGURE_HX711 "Configurar escala" +#define D_HX711_PARAMETERS "Parâmetros de escala" +#define D_ITEM_WEIGHT "Peso do Item" +#define D_REFERENCE_WEIGHT "Peso de referência" +#define D_CALIBRATE "Calibrar" +#define D_CALIBRATION "Calibração" //xsns_35_tx20.ino -#define D_TX20_WIND_DIRECTION "Wind Direction" -#define D_TX20_WIND_SPEED "Wind Speed" -#define D_TX20_WIND_SPEED_AVG "Wind Speed Avg" -#define D_TX20_WIND_SPEED_MAX "Wind Speed Max" +#define D_TX20_WIND_DIRECTION "Direção do vento" +#define D_TX20_WIND_SPEED "Velocidade do vento" +#define D_TX20_WIND_SPEED_AVG "Velocidade média do vento" +#define D_TX20_WIND_SPEED_MAX "Velocidade do vento Máxima" #define D_TX20_NORTH "N" -#define D_TX20_EAST "E" +#define D_TX20_EAST "L" #define D_TX20_SOUTH "S" -#define D_TX20_WEST "W" +#define D_TX20_WEST "O" //xsns_43_hre.ino #define D_LOG_HRE "HRE: " @@ -526,6 +541,9 @@ #define D_SENSOR_SAIR_TX "SAir Tx" #define D_SENSOR_SPI_CS "SPI CS" #define D_SENSOR_SPI_DC "SPI DC" +#define D_SENSOR_SPI_MISO "SPI MISO" +#define D_SENSOR_SPI_MOSI "SPI MOSI" +#define D_SENSOR_SPI_CLK "SPI CLK" #define D_SENSOR_BACKLIGHT "Luz de fundo" #define D_SENSOR_PMS5003 "PMS5003" #define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" @@ -585,6 +603,33 @@ #define D_SENSOR_HRE_CLOCK "HRE Clock" #define D_SENSOR_HRE_DATA "HRE Data" #define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" +#define D_SENSOR_BUZZER "Buzzer" +#define D_SENSOR_OLED_RESET "OLED Reset" +#define D_SENSOR_ZIGBEE_TXD "Zigbee Tx" +#define D_SENSOR_ZIGBEE_RXD "Zigbee Rx" +#define D_SENSOR_SOLAXX1_TX "SolaxX1 Tx" +#define D_SENSOR_SOLAXX1_RX "SolaxX1 Rx" +#define D_SENSOR_IBEACON_TX "iBeacon TX" +#define D_SENSOR_IBEACON_RX "iBeacon RX" +#define D_SENSOR_RDM6300_RX "RDM6300 RX" +#define D_SENSOR_CC1101_CS "CC1101 CS" +#define D_SENSOR_A4988_DIR "A4988 DIR" +#define D_SENSOR_A4988_STP "A4988 STP" +#define D_SENSOR_A4988_ENA "A4988 ENA" +#define D_SENSOR_A4988_MS1 "A4988 MS1" +#define D_SENSOR_A4988_MS2 "A4988 MS2" +#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_DDS2382_TX "DDS238-2 Tx" +#define D_SENSOR_DDS2382_RX "DDS238-2 Rx" +#define D_SENSOR_DDSU666_TX "DDSU666 Tx" +#define D_SENSOR_DDSU666_RX "DDSU666 Rx" +#define D_SENSOR_SM2135_CLK "SM2135 Clk" +#define D_SENSOR_SM2135_DAT "SM2135 Dat" +#define D_SENSOR_DEEPSLEEP "DeepSleep" +#define D_SENSOR_EXS_ENABLE "EXS Enable" +#define D_SENSOR_ARDUINO_TX "Arduino TX" +#define D_SENSOR_ARDUINO_RX "Arduino RX" +#define D_SENSOR_ARDUINO_RESET "Arduino RST" // Units #define D_UNIT_AMPERE "A" @@ -646,13 +691,37 @@ #define D_LOG_WIFI "WIF: " // Wifi //SDM220 -#define D_PHASE_ANGLE "Phase Angle" -#define D_IMPORT_ACTIVE "Import Active" -#define D_EXPORT_ACTIVE "Export Active" -#define D_IMPORT_REACTIVE "Import Reactive" -#define D_EXPORT_REACTIVE "Export Reactive" -#define D_TOTAL_REACTIVE "Total Reactive" +#define D_PHASE_ANGLE "Ângulo de Fase" +#define D_IMPORT_ACTIVE "Importar Ativo" +#define D_EXPORT_ACTIVE "Exportar Ativo" +#define D_IMPORT_REACTIVE "Importar Reativo" +#define D_EXPORT_REACTIVE "Exportar Reativo" +#define D_TOTAL_REACTIVE "Reativo total" #define D_UNIT_KWARH "kVArh" #define D_UNIT_ANGLE "Deg" +//SOLAXX1 +#define D_PV1_VOLTAGE "PV1 Voltagem" +#define D_PV1_CURRENT "PV1 Corrente" +#define D_PV1_POWER "PV1 Energia" +#define D_PV2_VOLTAGE "PV2 Voltagem" +#define D_PV2_CURRENT "PV2 Corrente" +#define D_PV2_POWER "PV2 Energia" +#define D_SOLAR_POWER "Energia Solar" +#define D_INVERTER_POWER "Potência do Inversor" +#define D_STATUS "Status" +#define D_WAITING "Esperando" +#define D_CHECKING "Verificando" +#define D_WORKING "Trabalhando" +#define D_FAILURE "Falha" +#define D_SOLAX_ERROR_0 "Nenhum código de erro" +#define D_SOLAX_ERROR_1 "Erro Grid Perdida" +#define D_SOLAX_ERROR_2 "Falha na Tensão da rede" +#define D_SOLAX_ERROR_3 "Falha na Frequência do Grid" +#define D_SOLAX_ERROR_4 "Pv Falha de Tensão" +#define D_SOLAX_ERROR_5 "Falha de Isolamento" +#define D_SOLAX_ERROR_6 "Falha de Temperatura excessiva" +#define D_SOLAX_ERROR_7 "Falha no Ventilador" +#define D_SOLAX_ERROR_8 "Outra falha do dispositivo" + #endif // _LANGUAGE_PT_BR_H_ diff --git a/sonoff/language/pt-PT.h b/sonoff/language/pt-PT.h index f45e71a73..bfa234599 100644 --- a/sonoff/language/pt-PT.h +++ b/sonoff/language/pt-PT.h @@ -284,6 +284,7 @@ #define D_LOGGING_PARAMETERS "Parametros Logging" #define D_SERIAL_LOG_LEVEL "Nível de registro serial" +#define D_MQTT_LOG_LEVEL "Mqtt log level" #define D_WEB_LOG_LEVEL "Nível de registro WEB" #define D_SYS_LOG_LEVEL "Nível de registro Syslog" #define D_MORE_DEBUG "Depurar mais" @@ -400,6 +401,7 @@ #define D_DOMOTICZ_VOLTAGE "Voltagem/PM2.5" #define D_DOMOTICZ_CURRENT "Corrente/PM10" #define D_DOMOTICZ_AIRQUALITY "Qualidade do Ar" + #define D_DOMOTICZ_P1_SMART_METER "P1SmartMeter" #define D_DOMOTICZ_UPDATE_TIMER "Tempo de atualização" // xdrv_09_timers.ino @@ -441,6 +443,19 @@ #define D_ENERGY_YESTERDAY "Consumo energético de ontem" #define D_ENERGY_TOTAL "Consumo total de energial" +// xdrv_27_shutter.ino +#define D_OPEN "Open" +#define D_CLOSE "Close" +#define D_DOMOTICZ_SHUTTER "Shutter" + +// xdrv_28_pcf8574.ino +#define D_CONFIGURE_PCF8574 "Configure PCF8574" +#define D_PCF8574_PARAMETERS "PCF8574 parameters" +#define D_INVERT_PORTS "Invert Ports" +#define D_DEVICE "Device" +#define D_DEVICE_INPUT "Input" +#define D_DEVICE_OUTPUT "Output" + // xsns_05_ds18b20.ino #define D_SENSOR_BUSY "Sensor ocupado" #define D_SENSOR_CRC_ERROR "Erro Sensor CRC" @@ -526,6 +541,9 @@ #define D_SENSOR_SAIR_TX "SAir Tx" #define D_SENSOR_SPI_CS "SPI CS" #define D_SENSOR_SPI_DC "SPI DC" +#define D_SENSOR_SPI_MISO "SPI MISO" +#define D_SENSOR_SPI_MOSI "SPI MOSI" +#define D_SENSOR_SPI_CLK "SPI CLK" #define D_SENSOR_BACKLIGHT "Luz negra" #define D_SENSOR_PMS5003 "PMS5003" #define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" @@ -585,6 +603,33 @@ #define D_SENSOR_HRE_CLOCK "HRE Clock" #define D_SENSOR_HRE_DATA "HRE Data" #define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" +#define D_SENSOR_BUZZER "Buzzer" +#define D_SENSOR_OLED_RESET "OLED Reset" +#define D_SENSOR_ZIGBEE_TXD "Zigbee Tx" +#define D_SENSOR_ZIGBEE_RXD "Zigbee Rx" +#define D_SENSOR_SOLAXX1_TX "SolaxX1 Tx" +#define D_SENSOR_SOLAXX1_RX "SolaxX1 Rx" +#define D_SENSOR_IBEACON_TX "iBeacon TX" +#define D_SENSOR_IBEACON_RX "iBeacon RX" +#define D_SENSOR_RDM6300_RX "RDM6300 RX" +#define D_SENSOR_CC1101_CS "CC1101 CS" +#define D_SENSOR_A4988_DIR "A4988 DIR" +#define D_SENSOR_A4988_STP "A4988 STP" +#define D_SENSOR_A4988_ENA "A4988 ENA" +#define D_SENSOR_A4988_MS1 "A4988 MS1" +#define D_SENSOR_A4988_MS2 "A4988 MS2" +#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_DDS2382_TX "DDS238-2 Tx" +#define D_SENSOR_DDS2382_RX "DDS238-2 Rx" +#define D_SENSOR_DDSU666_TX "DDSU666 Tx" +#define D_SENSOR_DDSU666_RX "DDSU666 Rx" +#define D_SENSOR_SM2135_CLK "SM2135 Clk" +#define D_SENSOR_SM2135_DAT "SM2135 Dat" +#define D_SENSOR_DEEPSLEEP "DeepSleep" +#define D_SENSOR_EXS_ENABLE "EXS Enable" +#define D_SENSOR_ARDUINO_TX "Arduino TX" +#define D_SENSOR_ARDUINO_RX "Arduino RX" +#define D_SENSOR_ARDUINO_RESET "Arduino RST" // Units #define D_UNIT_AMPERE "A" @@ -655,4 +700,28 @@ #define D_UNIT_KWARH "kVArh" #define D_UNIT_ANGLE "Deg" +//SOLAXX1 +#define D_PV1_VOLTAGE "PV1 Voltage" +#define D_PV1_CURRENT "PV1 Current" +#define D_PV1_POWER "PV1 Power" +#define D_PV2_VOLTAGE "PV2 Voltage" +#define D_PV2_CURRENT "PV2 Current" +#define D_PV2_POWER "PV2 Power" +#define D_SOLAR_POWER "Solar Power" +#define D_INVERTER_POWER "Inverter Power" +#define D_STATUS "Status" +#define D_WAITING "Waiting" +#define D_CHECKING "Checking" +#define D_WORKING "Working" +#define D_FAILURE "Failure" +#define D_SOLAX_ERROR_0 "No Error Code" +#define D_SOLAX_ERROR_1 "Grid Lost Fault" +#define D_SOLAX_ERROR_2 "Grid Voltage Fault" +#define D_SOLAX_ERROR_3 "Grid Frequency Fault" +#define D_SOLAX_ERROR_4 "Pv Voltage Fault" +#define D_SOLAX_ERROR_5 "Isolation Fault" +#define D_SOLAX_ERROR_6 "Over Temperature Fault" +#define D_SOLAX_ERROR_7 "Fan Fault" +#define D_SOLAX_ERROR_8 "Other Device Fault" + #endif // _LANGUAGE_PT_PT_H_ diff --git a/sonoff/language/ru-RU.h b/sonoff/language/ru-RU.h index 8289a56f1..13b721092 100644 --- a/sonoff/language/ru-RU.h +++ b/sonoff/language/ru-RU.h @@ -284,6 +284,7 @@ #define D_LOGGING_PARAMETERS "Параметры Logging" #define D_SERIAL_LOG_LEVEL "Serial лог уровень" +#define D_MQTT_LOG_LEVEL "Mqtt log level" #define D_WEB_LOG_LEVEL "Web лог уровень" #define D_SYS_LOG_LEVEL "System лог уровень" #define D_MORE_DEBUG "Дополнительная информация для отладки" @@ -400,6 +401,7 @@ #define D_DOMOTICZ_VOLTAGE "Voltage/PM2,5" #define D_DOMOTICZ_CURRENT "Current/PM10" #define D_DOMOTICZ_AIRQUALITY "AirQuality" + #define D_DOMOTICZ_P1_SMART_METER "P1SmartMeter" #define D_DOMOTICZ_UPDATE_TIMER "Update timer" // xdrv_09_timers.ino @@ -441,6 +443,19 @@ #define D_ENERGY_YESTERDAY "Энергия Вчера" #define D_ENERGY_TOTAL "Энергия Всего" +// xdrv_27_shutter.ino +#define D_OPEN "Open" +#define D_CLOSE "Close" +#define D_DOMOTICZ_SHUTTER "Shutter" + +// xdrv_28_pcf8574.ino +#define D_CONFIGURE_PCF8574 "Configure PCF8574" +#define D_PCF8574_PARAMETERS "PCF8574 parameters" +#define D_INVERT_PORTS "Invert Ports" +#define D_DEVICE "Device" +#define D_DEVICE_INPUT "Input" +#define D_DEVICE_OUTPUT "Output" + // xsns_05_ds18b20.ino #define D_SENSOR_BUSY "Датчик DS18x20 занят" #define D_SENSOR_CRC_ERROR "Датчик DS18x20 - ошибка CRC" @@ -526,7 +541,10 @@ #define D_SENSOR_SAIR_TX "SAir Tx" #define D_SENSOR_SPI_CS "SPI CS" #define D_SENSOR_SPI_DC "SPI DC" -#define D_SENSOR_BACKLIGHT "BkLight" +#define D_SENSOR_SPI_MISO "SPI MISO" +#define D_SENSOR_SPI_MOSI "SPI MOSI" +#define D_SENSOR_SPI_CLK "SPI CLK" +#define D_SENSOR_BACKLIGHT "Backlight" #define D_SENSOR_PMS5003 "PMS5003" #define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" #define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" @@ -585,6 +603,33 @@ #define D_SENSOR_HRE_CLOCK "HRE Clock" #define D_SENSOR_HRE_DATA "HRE Data" #define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" +#define D_SENSOR_BUZZER "Buzzer" +#define D_SENSOR_OLED_RESET "OLED Reset" +#define D_SENSOR_ZIGBEE_TXD "Zigbee Tx" +#define D_SENSOR_ZIGBEE_RXD "Zigbee Rx" +#define D_SENSOR_SOLAXX1_TX "SolaxX1 Tx" +#define D_SENSOR_SOLAXX1_RX "SolaxX1 Rx" +#define D_SENSOR_IBEACON_TX "iBeacon TX" +#define D_SENSOR_IBEACON_RX "iBeacon RX" +#define D_SENSOR_RDM6300_RX "RDM6300 RX" +#define D_SENSOR_CC1101_CS "CC1101 CS" +#define D_SENSOR_A4988_DIR "A4988 DIR" +#define D_SENSOR_A4988_STP "A4988 STP" +#define D_SENSOR_A4988_ENA "A4988 ENA" +#define D_SENSOR_A4988_MS1 "A4988 MS1" +#define D_SENSOR_A4988_MS2 "A4988 MS2" +#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_DDS2382_TX "DDS238-2 Tx" +#define D_SENSOR_DDS2382_RX "DDS238-2 Rx" +#define D_SENSOR_DDSU666_TX "DDSU666 Tx" +#define D_SENSOR_DDSU666_RX "DDSU666 Rx" +#define D_SENSOR_SM2135_CLK "SM2135 Clk" +#define D_SENSOR_SM2135_DAT "SM2135 Dat" +#define D_SENSOR_DEEPSLEEP "DeepSleep" +#define D_SENSOR_EXS_ENABLE "EXS Enable" +#define D_SENSOR_ARDUINO_TX "Arduino TX" +#define D_SENSOR_ARDUINO_RX "Arduino RX" +#define D_SENSOR_ARDUINO_RESET "Arduino RST" // Units #define D_UNIT_AMPERE "А" @@ -655,4 +700,28 @@ #define D_UNIT_KWARH "kVArh" #define D_UNIT_ANGLE "Град" +//SOLAXX1 +#define D_PV1_VOLTAGE "PV1 Voltage" +#define D_PV1_CURRENT "PV1 Current" +#define D_PV1_POWER "PV1 Power" +#define D_PV2_VOLTAGE "PV2 Voltage" +#define D_PV2_CURRENT "PV2 Current" +#define D_PV2_POWER "PV2 Power" +#define D_SOLAR_POWER "Solar Power" +#define D_INVERTER_POWER "Inverter Power" +#define D_STATUS "Status" +#define D_WAITING "Waiting" +#define D_CHECKING "Checking" +#define D_WORKING "Working" +#define D_FAILURE "Failure" +#define D_SOLAX_ERROR_0 "No Error Code" +#define D_SOLAX_ERROR_1 "Grid Lost Fault" +#define D_SOLAX_ERROR_2 "Grid Voltage Fault" +#define D_SOLAX_ERROR_3 "Grid Frequency Fault" +#define D_SOLAX_ERROR_4 "Pv Voltage Fault" +#define D_SOLAX_ERROR_5 "Isolation Fault" +#define D_SOLAX_ERROR_6 "Over Temperature Fault" +#define D_SOLAX_ERROR_7 "Fan Fault" +#define D_SOLAX_ERROR_8 "Other Device Fault" + #endif // _LANGUAGE_RU_RU_H_ diff --git a/sonoff/language/sk-SK.h b/sonoff/language/sk-SK.h index 2c5d126ef..d35fff407 100644 --- a/sonoff/language/sk-SK.h +++ b/sonoff/language/sk-SK.h @@ -284,6 +284,7 @@ #define D_LOGGING_PARAMETERS "Voľba logovania" #define D_SERIAL_LOG_LEVEL "Sériová úroveň logu" +#define D_MQTT_LOG_LEVEL "Mqtt log level" #define D_WEB_LOG_LEVEL "Webová úroveň logu" #define D_SYS_LOG_LEVEL "Systemová úroveň logu" #define D_MORE_DEBUG "Viac debug informácí" @@ -400,6 +401,7 @@ #define D_DOMOTICZ_VOLTAGE "Napätie/PM2,5" #define D_DOMOTICZ_CURRENT "Prúd/PM10" #define D_DOMOTICZ_AIRQUALITY "AirQuality" + #define D_DOMOTICZ_P1_SMART_METER "P1SmartMeter" #define D_DOMOTICZ_UPDATE_TIMER "Aktualizácia časovača" // xdrv_09_timers.ino @@ -441,6 +443,19 @@ #define D_ENERGY_YESTERDAY "Spotreba včera" #define D_ENERGY_TOTAL "Celková spotreba" +// xdrv_27_shutter.ino +#define D_OPEN "Open" +#define D_CLOSE "Close" +#define D_DOMOTICZ_SHUTTER "Shutter" + +// xdrv_28_pcf8574.ino +#define D_CONFIGURE_PCF8574 "Configure PCF8574" +#define D_PCF8574_PARAMETERS "PCF8574 parameters" +#define D_INVERT_PORTS "Invert Ports" +#define D_DEVICE "Device" +#define D_DEVICE_INPUT "Input" +#define D_DEVICE_OUTPUT "Output" + // xsns_05_ds18b20.ino #define D_SENSOR_BUSY "Sensor DS18x20 obsadený" #define D_SENSOR_CRC_ERROR "Sensor DS18x20 chyba CRC" @@ -526,7 +541,10 @@ #define D_SENSOR_SAIR_TX "SAir Tx" #define D_SENSOR_SPI_CS "SPI CS" #define D_SENSOR_SPI_DC "SPI DC" -#define D_SENSOR_BACKLIGHT "BkLight" +#define D_SENSOR_SPI_MISO "SPI MISO" +#define D_SENSOR_SPI_MOSI "SPI MOSI" +#define D_SENSOR_SPI_CLK "SPI CLK" +#define D_SENSOR_BACKLIGHT "Backlight" #define D_SENSOR_PMS5003 "PMS5003" #define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" #define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" @@ -585,6 +603,33 @@ #define D_SENSOR_HRE_CLOCK "HRE Clock" #define D_SENSOR_HRE_DATA "HRE Data" #define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" +#define D_SENSOR_BUZZER "Buzzer" +#define D_SENSOR_OLED_RESET "OLED Reset" +#define D_SENSOR_ZIGBEE_TXD "Zigbee Tx" +#define D_SENSOR_ZIGBEE_RXD "Zigbee Rx" +#define D_SENSOR_SOLAXX1_TX "SolaxX1 Tx" +#define D_SENSOR_SOLAXX1_RX "SolaxX1 Rx" +#define D_SENSOR_IBEACON_TX "iBeacon TX" +#define D_SENSOR_IBEACON_RX "iBeacon RX" +#define D_SENSOR_RDM6300_RX "RDM6300 RX" +#define D_SENSOR_CC1101_CS "CC1101 CS" +#define D_SENSOR_A4988_DIR "A4988 DIR" +#define D_SENSOR_A4988_STP "A4988 STP" +#define D_SENSOR_A4988_ENA "A4988 ENA" +#define D_SENSOR_A4988_MS1 "A4988 MS1" +#define D_SENSOR_A4988_MS2 "A4988 MS2" +#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_DDS2382_TX "DDS238-2 Tx" +#define D_SENSOR_DDS2382_RX "DDS238-2 Rx" +#define D_SENSOR_DDSU666_TX "DDSU666 Tx" +#define D_SENSOR_DDSU666_RX "DDSU666 Rx" +#define D_SENSOR_SM2135_CLK "SM2135 Clk" +#define D_SENSOR_SM2135_DAT "SM2135 Dat" +#define D_SENSOR_DEEPSLEEP "DeepSleep" +#define D_SENSOR_EXS_ENABLE "EXS Enable" +#define D_SENSOR_ARDUINO_TX "Arduino TX" +#define D_SENSOR_ARDUINO_RX "Arduino RX" +#define D_SENSOR_ARDUINO_RESET "Arduino RST" // Units #define D_UNIT_AMPERE "A" @@ -655,4 +700,28 @@ #define D_UNIT_KWARH "kVArh" #define D_UNIT_ANGLE "Deg" +//SOLAXX1 +#define D_PV1_VOLTAGE "PV1 Voltage" +#define D_PV1_CURRENT "PV1 Current" +#define D_PV1_POWER "PV1 Power" +#define D_PV2_VOLTAGE "PV2 Voltage" +#define D_PV2_CURRENT "PV2 Current" +#define D_PV2_POWER "PV2 Power" +#define D_SOLAR_POWER "Solar Power" +#define D_INVERTER_POWER "Inverter Power" +#define D_STATUS "Status" +#define D_WAITING "Waiting" +#define D_CHECKING "Checking" +#define D_WORKING "Working" +#define D_FAILURE "Failure" +#define D_SOLAX_ERROR_0 "No Error Code" +#define D_SOLAX_ERROR_1 "Grid Lost Fault" +#define D_SOLAX_ERROR_2 "Grid Voltage Fault" +#define D_SOLAX_ERROR_3 "Grid Frequency Fault" +#define D_SOLAX_ERROR_4 "Pv Voltage Fault" +#define D_SOLAX_ERROR_5 "Isolation Fault" +#define D_SOLAX_ERROR_6 "Over Temperature Fault" +#define D_SOLAX_ERROR_7 "Fan Fault" +#define D_SOLAX_ERROR_8 "Other Device Fault" + #endif // _LANGUAGE_SK_SK_H_ diff --git a/sonoff/language/sv-SE.h b/sonoff/language/sv-SE.h index 35fec7e4c..701ed43ac 100644 --- a/sonoff/language/sv-SE.h +++ b/sonoff/language/sv-SE.h @@ -284,6 +284,7 @@ #define D_LOGGING_PARAMETERS "Loggningsparametrar" #define D_SERIAL_LOG_LEVEL "Seriell loggnivå" +#define D_MQTT_LOG_LEVEL "Mqtt log level" #define D_WEB_LOG_LEVEL "Webb loggnivå" #define D_SYS_LOG_LEVEL "Syslog-nivå" #define D_MORE_DEBUG "Mer debugging" @@ -400,6 +401,7 @@ #define D_DOMOTICZ_VOLTAGE "Volt/PM2.5" #define D_DOMOTICZ_CURRENT "Ström/PM10" #define D_DOMOTICZ_AIRQUALITY "Luftkvalitet" + #define D_DOMOTICZ_P1_SMART_METER "P1SmartMeter" #define D_DOMOTICZ_UPDATE_TIMER "Uppdatera timer" // xdrv_09_timers.ino @@ -441,6 +443,19 @@ #define D_ENERGY_YESTERDAY "Energi igår" #define D_ENERGY_TOTAL "Energi totalt" +// xdrv_27_shutter.ino +#define D_OPEN "Open" +#define D_CLOSE "Close" +#define D_DOMOTICZ_SHUTTER "Shutter" + +// xdrv_28_pcf8574.ino +#define D_CONFIGURE_PCF8574 "Configure PCF8574" +#define D_PCF8574_PARAMETERS "PCF8574 parameters" +#define D_INVERT_PORTS "Invert Ports" +#define D_DEVICE "Device" +#define D_DEVICE_INPUT "Input" +#define D_DEVICE_OUTPUT "Output" + // xsns_05_ds18b20.ino #define D_SENSOR_BUSY "Sensor upptagen" #define D_SENSOR_CRC_ERROR "Sensor CRC-fel" @@ -526,7 +541,10 @@ #define D_SENSOR_SAIR_TX "SAir Tx" #define D_SENSOR_SPI_CS "SPI CS" #define D_SENSOR_SPI_DC "SPI DC" -#define D_SENSOR_BACKLIGHT "BkLight" +#define D_SENSOR_SPI_MISO "SPI MISO" +#define D_SENSOR_SPI_MOSI "SPI MOSI" +#define D_SENSOR_SPI_CLK "SPI CLK" +#define D_SENSOR_BACKLIGHT "Backlight" #define D_SENSOR_PMS5003 "PMS5003" #define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" #define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" @@ -585,6 +603,33 @@ #define D_SENSOR_HRE_CLOCK "HRE Clock" #define D_SENSOR_HRE_DATA "HRE Data" #define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" +#define D_SENSOR_BUZZER "Buzzer" +#define D_SENSOR_OLED_RESET "OLED Reset" +#define D_SENSOR_ZIGBEE_TXD "Zigbee Tx" +#define D_SENSOR_ZIGBEE_RXD "Zigbee Rx" +#define D_SENSOR_SOLAXX1_TX "SolaxX1 Tx" +#define D_SENSOR_SOLAXX1_RX "SolaxX1 Rx" +#define D_SENSOR_IBEACON_TX "iBeacon TX" +#define D_SENSOR_IBEACON_RX "iBeacon RX" +#define D_SENSOR_RDM6300_RX "RDM6300 RX" +#define D_SENSOR_CC1101_CS "CC1101 CS" +#define D_SENSOR_A4988_DIR "A4988 DIR" +#define D_SENSOR_A4988_STP "A4988 STP" +#define D_SENSOR_A4988_ENA "A4988 ENA" +#define D_SENSOR_A4988_MS1 "A4988 MS1" +#define D_SENSOR_A4988_MS2 "A4988 MS2" +#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_DDS2382_TX "DDS238-2 Tx" +#define D_SENSOR_DDS2382_RX "DDS238-2 Rx" +#define D_SENSOR_DDSU666_TX "DDSU666 Tx" +#define D_SENSOR_DDSU666_RX "DDSU666 Rx" +#define D_SENSOR_SM2135_CLK "SM2135 Clk" +#define D_SENSOR_SM2135_DAT "SM2135 Dat" +#define D_SENSOR_DEEPSLEEP "DeepSleep" +#define D_SENSOR_EXS_ENABLE "EXS Enable" +#define D_SENSOR_ARDUINO_TX "Arduino TX" +#define D_SENSOR_ARDUINO_RX "Arduino RX" +#define D_SENSOR_ARDUINO_RESET "Arduino RST" // Units #define D_UNIT_AMPERE "A" @@ -655,4 +700,28 @@ #define D_UNIT_KWARH "kVArh" #define D_UNIT_ANGLE "Deg" +//SOLAXX1 +#define D_PV1_VOLTAGE "PV1 Voltage" +#define D_PV1_CURRENT "PV1 Current" +#define D_PV1_POWER "PV1 Power" +#define D_PV2_VOLTAGE "PV2 Voltage" +#define D_PV2_CURRENT "PV2 Current" +#define D_PV2_POWER "PV2 Power" +#define D_SOLAR_POWER "Solar Power" +#define D_INVERTER_POWER "Inverter Power" +#define D_STATUS "Status" +#define D_WAITING "Waiting" +#define D_CHECKING "Checking" +#define D_WORKING "Working" +#define D_FAILURE "Failure" +#define D_SOLAX_ERROR_0 "No Error Code" +#define D_SOLAX_ERROR_1 "Grid Lost Fault" +#define D_SOLAX_ERROR_2 "Grid Voltage Fault" +#define D_SOLAX_ERROR_3 "Grid Frequency Fault" +#define D_SOLAX_ERROR_4 "Pv Voltage Fault" +#define D_SOLAX_ERROR_5 "Isolation Fault" +#define D_SOLAX_ERROR_6 "Over Temperature Fault" +#define D_SOLAX_ERROR_7 "Fan Fault" +#define D_SOLAX_ERROR_8 "Other Device Fault" + #endif // _LANGUAGE_SV_SE_H_ diff --git a/sonoff/language/tr-TR.h b/sonoff/language/tr-TR.h index 68da47c01..295664ae0 100755 --- a/sonoff/language/tr-TR.h +++ b/sonoff/language/tr-TR.h @@ -284,6 +284,7 @@ #define D_LOGGING_PARAMETERS "Loglama parametreleri" #define D_SERIAL_LOG_LEVEL "Serial log seviyesi" +#define D_MQTT_LOG_LEVEL "Mqtt log level" #define D_WEB_LOG_LEVEL "Web log seviyesi" #define D_SYS_LOG_LEVEL "Syslog seviyesi" #define D_MORE_DEBUG "Hata ayıklama devamı" @@ -400,6 +401,7 @@ #define D_DOMOTICZ_VOLTAGE "Voltage/PM2.5" #define D_DOMOTICZ_CURRENT "Current/PM10" #define D_DOMOTICZ_AIRQUALITY "AirQuality" + #define D_DOMOTICZ_P1_SMART_METER "P1SmartMeter" #define D_DOMOTICZ_UPDATE_TIMER "Zamanlayıcıyı güncelle" // xdrv_09_timers.ino @@ -441,6 +443,19 @@ #define D_ENERGY_YESTERDAY "Energy Yesterday" #define D_ENERGY_TOTAL "Energy Total" +// xdrv_27_shutter.ino +#define D_OPEN "Open" +#define D_CLOSE "Close" +#define D_DOMOTICZ_SHUTTER "Shutter" + +// xdrv_28_pcf8574.ino +#define D_CONFIGURE_PCF8574 "Configure PCF8574" +#define D_PCF8574_PARAMETERS "PCF8574 parameters" +#define D_INVERT_PORTS "Invert Ports" +#define D_DEVICE "Device" +#define D_DEVICE_INPUT "Input" +#define D_DEVICE_OUTPUT "Output" + // xsns_05_ds18b20.ino #define D_SENSOR_BUSY "Sensör başgül" #define D_SENSOR_CRC_ERROR "Sensor CRC hatası" @@ -526,7 +541,10 @@ #define D_SENSOR_SAIR_TX "SAir Tx" #define D_SENSOR_SPI_CS "SPI CS" #define D_SENSOR_SPI_DC "SPI DC" -#define D_SENSOR_BACKLIGHT "BkLight" +#define D_SENSOR_SPI_MISO "SPI MISO" +#define D_SENSOR_SPI_MOSI "SPI MOSI" +#define D_SENSOR_SPI_CLK "SPI CLK" +#define D_SENSOR_BACKLIGHT "Backlight" #define D_SENSOR_PMS5003 "PMS5003" #define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" #define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" @@ -585,10 +603,38 @@ #define D_SENSOR_HRE_CLOCK "HRE Clock" #define D_SENSOR_HRE_DATA "HRE Data" #define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" +#define D_SENSOR_BUZZER "Buzzer" +#define D_SENSOR_OLED_RESET "OLED Reset" +#define D_SENSOR_ZIGBEE_TXD "Zigbee Tx" +#define D_SENSOR_ZIGBEE_RXD "Zigbee Rx" +#define D_SENSOR_SOLAXX1_TX "SolaxX1 Tx" +#define D_SENSOR_SOLAXX1_RX "SolaxX1 Rx" +#define D_SENSOR_IBEACON_TX "iBeacon TX" +#define D_SENSOR_IBEACON_RX "iBeacon RX" +#define D_SENSOR_RDM6300_RX "RDM6300 RX" +#define D_SENSOR_CC1101_CS "CC1101 CS" +#define D_SENSOR_A4988_DIR "A4988 DIR" +#define D_SENSOR_A4988_STP "A4988 STP" +#define D_SENSOR_A4988_ENA "A4988 ENA" +#define D_SENSOR_A4988_MS1 "A4988 MS1" +#define D_SENSOR_A4988_MS2 "A4988 MS2" +#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_DDS2382_TX "DDS238-2 Tx" +#define D_SENSOR_DDS2382_RX "DDS238-2 Rx" +#define D_SENSOR_DDSU666_TX "DDSU666 Tx" +#define D_SENSOR_DDSU666_RX "DDSU666 Rx" +#define D_SENSOR_SM2135_CLK "SM2135 Clk" +#define D_SENSOR_SM2135_DAT "SM2135 Dat" +#define D_SENSOR_DEEPSLEEP "DeepSleep" +#define D_SENSOR_EXS_ENABLE "EXS Enable" +#define D_SENSOR_ARDUINO_TX "Arduino TX" +#define D_SENSOR_ARDUINO_RX "Arduino RX" +#define D_SENSOR_ARDUINO_RESET "Arduino RST" // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CENTIMETER "cm" +#define D_UNIT_HERTZ "Hz" #define D_UNIT_HOUR "Hr" #define D_UNIT_GALLONS "gal" #define D_UNIT_GALLONS_PER_MIN "g/m" @@ -618,7 +664,6 @@ #define D_UNIT_WATT "W" #define D_UNIT_WATTHOUR "Wh" #define D_UNIT_WATT_METER_QUADRAT "W/m²" -#define D_UNIT_HERTZ "Hz" // Log message prefix #define D_LOG_APPLICATION "APP: " // Application @@ -655,4 +700,28 @@ #define D_UNIT_KWARH "kVArh" #define D_UNIT_ANGLE "Deg" +//SOLAXX1 +#define D_PV1_VOLTAGE "PV1 Voltage" +#define D_PV1_CURRENT "PV1 Current" +#define D_PV1_POWER "PV1 Power" +#define D_PV2_VOLTAGE "PV2 Voltage" +#define D_PV2_CURRENT "PV2 Current" +#define D_PV2_POWER "PV2 Power" +#define D_SOLAR_POWER "Solar Power" +#define D_INVERTER_POWER "Inverter Power" +#define D_STATUS "Status" +#define D_WAITING "Waiting" +#define D_CHECKING "Checking" +#define D_WORKING "Working" +#define D_FAILURE "Failure" +#define D_SOLAX_ERROR_0 "No Error Code" +#define D_SOLAX_ERROR_1 "Grid Lost Fault" +#define D_SOLAX_ERROR_2 "Grid Voltage Fault" +#define D_SOLAX_ERROR_3 "Grid Frequency Fault" +#define D_SOLAX_ERROR_4 "Pv Voltage Fault" +#define D_SOLAX_ERROR_5 "Isolation Fault" +#define D_SOLAX_ERROR_6 "Over Temperature Fault" +#define D_SOLAX_ERROR_7 "Fan Fault" +#define D_SOLAX_ERROR_8 "Other Device Fault" + #endif // _LANGUAGE_TR_TR_H_ diff --git a/sonoff/language/uk-UK.h b/sonoff/language/uk-UK.h index 55ef6781b..8030b1f2a 100644 --- a/sonoff/language/uk-UK.h +++ b/sonoff/language/uk-UK.h @@ -1,5 +1,5 @@ /* - uk-UK.h - localization for Ukrainian - Ukrain for Sonoff-Tasmota + uk-UK.h - localization for Ukrainian - Ukraine for Sonoff-Tasmota Copyright (C) 2019 Theo Arends / vadym-adik @@ -45,7 +45,7 @@ #define D_MINUTE_SECOND_SEPARATOR ":" #define D_DAY3LIST "НедПонВівСерЧетПятСуб" -#define D_MONTH3LIST "СічЛютБерКвіТраЧерЛипВерЖовЛисГру" +#define D_MONTH3LIST "СічЛютБерКвіТраЧерЛипСерВерЖовЛисГру" // Non JSON decimal separator #define D_DECIMAL_SEPARATOR "," @@ -53,33 +53,33 @@ // Common #define D_ADMIN "Admin" #define D_AIR_QUALITY "Якість повітря" -#define D_AP "AP" // Access Point +#define D_AP "Точка доступу" // Access Point #define D_AS "як" #define D_AUTO "АВТО" #define D_BLINK "Блимати" #define D_BLINKOFF "Не блимати" -#define D_BOOT_COUNT "Кіл-сть завант." +#define D_BOOT_COUNT "К-сть завант." #define D_BRIGHTLIGHT "Яскравість" #define D_BSSID "BSSId" #define D_BUTTON "Кнопка" -#define D_BY "by" // Written by me +#define D_BY " " // Written by me #define D_BYTES "Байт" #define D_CELSIUS "Цельсія" -#define D_CHANNEL "Channel" +#define D_CHANNEL "Канал" #define D_CO2 "Вуглек. газ" #define D_CODE "код" // Button code #define D_COLDLIGHT "Холодний" #define D_COMMAND "Команда" #define D_CONNECTED "Під'єднано" -#define D_COUNT "Підрахунок" +#define D_COUNT "Розмір" #define D_COUNTER "Лічильник" -#define D_CURRENT "Струм" // As in Voltage and Current +#define D_CURRENT "Струм" // As in Voltage and Current #define D_DATA "Дані" #define D_DARKLIGHT "Темний" #define D_DEBUG "Налагодження" #define D_DISABLED "Заблоковано" -#define D_DISTANCE "Дистанція" -#define D_DNS_SERVER "DNS Сервер" +#define D_DISTANCE "Відстань" +#define D_DNS_SERVER "Сервер DNS" #define D_DONE "Виконано" #define D_DST_TIME "Літній час" #define D_ECO2 "eCO2" @@ -91,31 +91,31 @@ #define D_FAILED "Невдало" #define D_FALLBACK "Зворотній зв'язок" #define D_FALLBACK_TOPIC "Топік зворотнього зв'язку" -#define D_FALSE "Помилково" +#define D_FALSE "Ні" #define D_FILE "Файл" -#define D_FLOW_RATE "Flow rate" +#define D_FLOW_RATE "Потік" #define D_FREE_MEMORY "Вільна память" #define D_FREQUENCY "Частота" #define D_GAS "Газ" #define D_GATEWAY "Шлюз" #define D_GROUP "Група" #define D_HOST "Хост" -#define D_HOSTNAME "Ім'я Хосту" +#define D_HOSTNAME "Назва хосту" #define D_HUMIDITY "Вологість" #define D_ILLUMINANCE "Освітленність" -#define D_IMMEDIATE "негайно" // Button immediate +#define D_IMMEDIATE "Негайно" // Button immediate #define D_INDEX "Індекс" #define D_INFO "Інфо" #define D_INFRARED "Інфрачервоний" #define D_INITIALIZED "Ініціалізовано" -#define D_IP_ADDRESS "IP Адрес" +#define D_IP_ADDRESS "IP адреса" #define D_LIGHT "Світло" #define D_LWT "LWT" #define D_MODULE "Модуль" #define D_MQTT "MQTT" -#define D_MULTI_PRESS "багаторазове натискання" +#define D_MULTI_PRESS "Багаторазове натискання" #define D_NOISE "Шум" -#define D_NONE "Ні" +#define D_NONE "Нічого" #define D_OFF "Вимк." #define D_OFFLINE "Офф-лайн" #define D_OK "Ок" @@ -131,9 +131,9 @@ #define D_PRESSURE "Тиск" #define D_PRESSUREATSEALEVEL "Тиск на рівні моря" #define D_PROGRAM_FLASH_SIZE "Розмір Flash для програм" -#define D_PROGRAM_SIZE "Розмір програм " +#define D_PROGRAM_SIZE "Розмір програм" #define D_PROJECT "Проект" -#define D_RAIN "Rain" +#define D_RAIN "Дощ" #define D_RECEIVED "Отримано" #define D_RESTART "Перезавантаження" #define D_RESTARTING "Перезавантаження" @@ -142,14 +142,14 @@ #define D_RETAINED "нерозподілений" #define D_RULE "Правило" #define D_SAVE "Зберегти" -#define D_SENSOR "Датчик" +#define D_SENSOR "Давач" #define D_SSID "SSID" #define D_START "Старт" -#define D_STD_TIME "STD" +#define D_STD_TIME "Стандартний час" #define D_STOP "Стоп" -#define D_SUBNET_MASK "Маска Підмережі" +#define D_SUBNET_MASK "Маска підмережі" #define D_SUBSCRIBE_TO "Підписатись на" -#define D_UNSUBSCRIBE_FROM "Unsubscribe from" +#define D_UNSUBSCRIBE_FROM "Відписатися від" #define D_SUCCESSFUL "Успішно" #define D_SUNRISE "Схід сонця" #define D_SUNSET "Захід сонця" @@ -157,74 +157,74 @@ #define D_TO "до" #define D_TOGGLE "Перекл." #define D_TOPIC "Топік" -#define D_TOTAL_USAGE "Total Usage" +#define D_TOTAL_USAGE "Загальне споживання" #define D_TRANSMIT "Передати" -#define D_TRUE "Істина" +#define D_TRUE "Так" #define D_TVOC "TVOC" #define D_UPGRADE "оновлення" #define D_UPLOAD "Завантажити" #define D_UPTIME "Час роботи" #define D_USER "Користувач" #define D_UTC_TIME "UTC" -#define D_UV_INDEX "УФ індекс" -#define D_UV_INDEX_1 "Low" -#define D_UV_INDEX_2 "Mid" -#define D_UV_INDEX_3 "High" -#define D_UV_INDEX_4 "Danger" -#define D_UV_INDEX_5 "BurnL1/2" -#define D_UV_INDEX_6 "BurnL3" -#define D_UV_INDEX_7 "OoR" -#define D_UV_LEVEL "УФ рівень" -#define D_UV_POWER "UV Power" +#define D_UV_INDEX "Індекс УФ" +#define D_UV_INDEX_1 "Низький" +#define D_UV_INDEX_2 "Середній" +#define D_UV_INDEX_3 "Високий" +#define D_UV_INDEX_4 "Небезпечний" +#define D_UV_INDEX_5 "Опіки 1/2 ступеня" +#define D_UV_INDEX_6 "Опіки 3 ступеня" +#define D_UV_INDEX_7 "Невідомий" +#define D_UV_LEVEL "Рівень УФ" +#define D_UV_POWER "Потужність УФ" #define D_VERSION "Версія" #define D_VOLTAGE "Напруга" -#define D_WEIGHT "Weight" +#define D_WEIGHT "Вага" #define D_WARMLIGHT "Тепло" #define D_WEB_SERVER "Web сервер" // sonoff.ino -#define D_WARNING_MINIMAL_VERSION "ПОПЕРЕДЖЕННЯ! Ця версія не підтримує персистентні налаштування" +#define D_WARNING_MINIMAL_VERSION "ПОПЕРЕДЖЕННЯ! Ця версія не підтримує збереження налаштувань" #define D_LEVEL_10 "рівень 1-0" #define D_LEVEL_01 "рівень 0-1" -#define D_SERIAL_LOGGING_DISABLED "Serial logging вимкнений" -#define D_SYSLOG_LOGGING_REENABLED "Syslog logging увімкнений" +#define D_SERIAL_LOGGING_DISABLED "Serial журнал вимкнений" +#define D_SYSLOG_LOGGING_REENABLED "Syslog журнал увімкнений" #define D_SET_BAUDRATE_TO "Встановити швидкість передачі (Baudrate)" #define D_RECEIVED_TOPIC "Отриманий Топік" #define D_DATA_SIZE "Розмір даних" -#define D_ANALOG_INPUT "Аналоговий вхід" +#define D_ANALOG_INPUT "Напруга" // support.ino #define D_OSWATCH "osWatch" -#define D_BLOCKED_LOOP "Блокуючий цикл" +#define D_BLOCKED_LOOP "Цикл заблокований" #define D_WPS_FAILED_WITH_STATUS "WPS конфігурація з статусом НЕВДАЛА" #define D_ACTIVE_FOR_3_MINUTES "активний протягом 3 хвилин" #define D_FAILED_TO_START "не вдалось запустити" #define D_PATCH_ISSUE_2186 "Проблема з виправленням 2186" #define D_CONNECTING_TO_AP "Підключення до AP" #define D_IN_MODE "в режимі" -#define D_CONNECT_FAILED_NO_IP_ADDRESS "Помилка підключення, IP-адрес не отриманий" -#define D_CONNECT_FAILED_AP_NOT_REACHED "Помилка з'єднання, AP не запущений" +#define D_CONNECT_FAILED_NO_IP_ADDRESS "Помилка підключення, IP-адреса не отримана" +#define D_CONNECT_FAILED_AP_NOT_REACHED "Помилка з'єднання, AP не знайдено" #define D_CONNECT_FAILED_WRONG_PASSWORD "Помилка з'єднання, невірне гасло до AP" #define D_CONNECT_FAILED_AP_TIMEOUT "Помилка з'єднання з AP по тайм-ауту" #define D_ATTEMPTING_CONNECTION "Спроба підключення..." #define D_CHECKING_CONNECTION "Перевірка з'єднання..." -#define D_QUERY_DONE "Запит виконаний. Виявлено служби MQTT" -#define D_MQTT_SERVICE_FOUND "MQTT сервіс знайдено" +#define D_QUERY_DONE "Запит виконаний. Виявлено сервер MQTT" +#define D_MQTT_SERVICE_FOUND "MQTT сервер знайдено" #define D_FOUND_AT "знайдено в" -#define D_SYSLOG_HOST_NOT_FOUND "System лог хост не знайдено" +#define D_SYSLOG_HOST_NOT_FOUND "Сервер журналу не знайдено" // settings.ino #define D_SAVED_TO_FLASH_AT "Збережено в флэш-пам'ять" #define D_LOADED_FROM_FLASH_AT "Завантажено з флэш-пам'яті" #define D_USE_DEFAULTS "Використовувати значення за замовчуванням" -#define D_ERASED_SECTOR "Стерти сектор" +#define D_ERASED_SECTOR "Стерто сектор" // xdrv_02_webserver.ino -#define D_NOSCRIPT "To use Tasmota, please enable JavaScript" +#define D_NOSCRIPT "Для використання Tasmota треба увімкнути JavaScript" #define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "Прошивка MINIMAL
будь-ласка оновіть" #define D_WEBSERVER_ACTIVE_ON "Веб-сервер активний" -#define D_WITH_IP_ADDRESS "з IP-адресом" +#define D_WITH_IP_ADDRESS "з IP-адресою" #define D_WEBSERVER_STOPPED "Веб-сервер зупинений" #define D_FILE_NOT_FOUND "Файл не знайдений" #define D_REDIRECTED "Перенаправлено на адаптивний портал" @@ -246,7 +246,7 @@ #define D_CONFIGURE_WIFI "Конфігурація WiFi" #define D_CONFIGURE_MQTT "Конфігурація MQTT" #define D_CONFIGURE_DOMOTICZ "Конфігурація Domoticz" -#define D_CONFIGURE_LOGGING "Конфігурація Логів" +#define D_CONFIGURE_LOGGING "Конфігурація журналів" #define D_CONFIGURE_OTHER "Конфігурація інше" #define D_CONFIRM_RESET_CONFIGURATION "Підтвердити скидання конфігурації" #define D_RESET_CONFIGURATION "Скидання конфігурації" @@ -254,16 +254,16 @@ #define D_RESTORE_CONFIGURATION "Відновлення конфігурації" #define D_MAIN_MENU "Головне меню" -#define D_MODULE_PARAMETERS "Параметри модулю" -#define D_MODULE_TYPE "Тип модулю" +#define D_MODULE_PARAMETERS "Параметри модуля" +#define D_MODULE_TYPE "Тип модуля" #define D_PULLUP_ENABLE "No Button/Switch pull-up" -#define D_ADC "ADC" +#define D_ADC "АЦП" #define D_GPIO "GPIO" #define D_SERIAL_IN "Serial вхід" #define D_SERIAL_OUT "Serial вихід" #define D_WIFI_PARAMETERS "Параметри Wifi" -#define D_SCAN_FOR_WIFI_NETWORKS "Сканування беспроводових мереж Wifi" +#define D_SCAN_FOR_WIFI_NETWORKS "Сканування бездротовихих мереж Wifi" #define D_SCAN_DONE "Сканування завершене" #define D_NO_NETWORKS_FOUND "Не знайдено мереж" #define D_REFRESH_TO_SCAN_AGAIN "Оновити для повторного сканування" @@ -274,25 +274,26 @@ #define D_WPA_PSK "WPA PSK" #define D_WPA2_PSK "WPA2 PSK" #define D_AP1_SSID "AP1 SSID" -#define D_AP1_PASSWORD "AP1 Гасло" +#define D_AP1_PASSWORD "AP1 гасло" #define D_AP2_SSID "AP2 SSID" -#define D_AP2_PASSWORD "AP2 Гасло" +#define D_AP2_PASSWORD "AP2 гасло" #define D_MQTT_PARAMETERS "Параметри MQTT" #define D_CLIENT "Клієнт" #define D_FULL_TOPIC "Повний Топік" -#define D_LOGGING_PARAMETERS "Параметри логів" -#define D_SERIAL_LOG_LEVEL "Serial лог рівень" -#define D_WEB_LOG_LEVEL "Web лог рівень" -#define D_SYS_LOG_LEVEL "System лог рівень" +#define D_LOGGING_PARAMETERS "Параметри журналу" +#define D_SERIAL_LOG_LEVEL "Serial рівень" +#define D_MQTT_LOG_LEVEL "Mqtt log level" +#define D_WEB_LOG_LEVEL "Web рівень" +#define D_SYS_LOG_LEVEL "System рівень" #define D_MORE_DEBUG "Додаткова інформація для налагодження" -#define D_SYSLOG_HOST "System лог хост" -#define D_SYSLOG_PORT "System лог порт" +#define D_SYSLOG_HOST "System хост" +#define D_SYSLOG_PORT "System порт" #define D_TELEMETRY_PERIOD "Період телеметрії" #define D_OTHER_PARAMETERS "Параметри Інше" -#define D_TEMPLATE "Template" +#define D_TEMPLATE "Шаблони" #define D_ACTIVATE "Activate" #define D_WEB_ADMIN_PASSWORD "Гасло Web адміністратора" #define D_MQTT_ENABLE "MQTT активний" @@ -302,24 +303,24 @@ #define D_SINGLE_DEVICE "одиночне" #define D_MULTI_DEVICE "мульти" -#define D_CONFIGURE_TEMPLATE "Configure Template" -#define D_TEMPLATE_PARAMETERS "Template parameters" -#define D_TEMPLATE_NAME "Name" -#define D_BASE_TYPE "Based on" -#define D_TEMPLATE_FLAGS "Options" +#define D_CONFIGURE_TEMPLATE "Конфігурація шаблона" +#define D_TEMPLATE_PARAMETERS "Параметри шаблона" +#define D_TEMPLATE_NAME "Назва" +#define D_BASE_TYPE "На основі" +#define D_TEMPLATE_FLAGS "Опції" #define D_SAVE_CONFIGURATION "Зберегти конфігурацію" #define D_CONFIGURATION_SAVED "Конфігурація збережена " #define D_CONFIGURATION_RESET "Конфігурація скинута" #define D_PROGRAM_VERSION "Версія програми" -#define D_BUILD_DATE_AND_TIME "Дата & Час збірки" +#define D_BUILD_DATE_AND_TIME "Дата і час збірки" #define D_CORE_AND_SDK_VERSION "Версія Core/SDK" -#define D_FLASH_WRITE_COUNT "Кіл-ть записів Flash" -#define D_MAC_ADDRESS "MAC Адрес" +#define D_FLASH_WRITE_COUNT "Кількість записів Flash" +#define D_MAC_ADDRESS "MAC Адреса" #define D_MQTT_HOST "MQTT Хост" #define D_MQTT_PORT "MQTT Порт" -#define D_MQTT_CLIENT "MQTT Клієнт ID" +#define D_MQTT_CLIENT "MQTT ID Клієнта" #define D_MQTT_USER "MQTT Користувач" #define D_MQTT_TOPIC "MQTT Топік" #define D_MQTT_GROUP_TOPIC "MQTT Топік групи" @@ -329,7 +330,7 @@ #define D_ESP_CHIP_ID "ID чипу ESP" #define D_FLASH_CHIP_ID "ID чипу Flash пам'яті" #define D_FLASH_CHIP_SIZE "Розмір Flash пам'яті" -#define D_FREE_PROGRAM_SPACE "Вільний простір програм" +#define D_FREE_PROGRAM_SPACE "Вільний простір для програм" #define D_UPGRADE_BY_WEBSERVER "Оновлення через Веб-сервер" #define D_OTA_URL "OTA Url" @@ -340,17 +341,17 @@ #define D_UPLOAD_DONE "Завантаження завершено" #define D_UPLOAD_ERR_1 "Файл не вибраний" #define D_UPLOAD_ERR_2 "Недостатньо місця" -#define D_UPLOAD_ERR_3 "Magic байт не 0xE9" -#define D_UPLOAD_ERR_4 "Размір прошивки більше, чим реальний размір флеш пам'яті" +#define D_UPLOAD_ERR_3 "Магічний байт не 0xE9" +#define D_UPLOAD_ERR_4 "Размір прошивки більший, ніж размір Flash пам'яті" #define D_UPLOAD_ERR_5 "Помилка завантаження буферу" #define D_UPLOAD_ERR_6 "Помилка завантаження. Увімкнено лог рівень 3" #define D_UPLOAD_ERR_7 "Завантаження перервано" #define D_UPLOAD_ERR_8 "Файл невірний" #define D_UPLOAD_ERR_9 "Занадто великий файл" -#define D_UPLOAD_ERR_10 "Failed to init RF chip" -#define D_UPLOAD_ERR_11 "Failed to erase RF chip" -#define D_UPLOAD_ERR_12 "Failed to write to RF chip" -#define D_UPLOAD_ERR_13 "Failed to decode RF firmware" +#define D_UPLOAD_ERR_10 "Помилка ініціалізаціції чипу FR" +#define D_UPLOAD_ERR_11 "Помилка стирання чипу RF" +#define D_UPLOAD_ERR_12 "Помилка запису чипу RF" +#define D_UPLOAD_ERR_13 "Помилка розкодування прошивки RF" #define D_UPLOAD_ERROR_CODE "Код помилки завантаження" #define D_ENTER_COMMAND "Уведіть команду" @@ -358,39 +359,39 @@ #define D_NEED_USER_AND_PASSWORD "Очікується user=&password=" // xdrv_01_mqtt.ino -#define D_FINGERPRINT "Перевірка TLS відбитка..." +#define D_FINGERPRINT "Перевірка відбитка TLS..." #define D_TLS_CONNECT_FAILED_TO "Збій підключення TLS до" #define D_RETRY_IN "Повторити" #define D_VERIFIED "Перевірено відбиток" -#define D_INSECURE "Небезпечне з'єднання, недійсний відбиток " +#define D_INSECURE "Небезпечне з'єднання, недійсний відбиток" #define D_CONNECT_FAILED_TO "Помилка підключення до" // xplg_wemohue.ino #define D_MULTICAST_DISABLED "Multicast вимкнений" #define D_MULTICAST_REJOINED "Multicast (пере)під'єднався" #define D_MULTICAST_JOIN_FAILED "Multicast помилка з'єднання" -#define D_FAILED_TO_SEND_RESPONSE "Не вдалось відправити відповідь" +#define D_FAILED_TO_SEND_RESPONSE "Не вдалось надіслати відповідь" #define D_WEMO "WeMo" #define D_WEMO_BASIC_EVENT "WeMo основна подія" #define D_WEMO_EVENT_SERVICE "WeMo служба подій" #define D_WEMO_META_SERVICE "WeMo мета-сервіс" #define D_WEMO_SETUP "WeMo налаштування" -#define D_RESPONSE_SENT "Відповідь відправлена" +#define D_RESPONSE_SENT "Відповідь відіслана" #define D_HUE "Hue" #define D_HUE_BRIDGE_SETUP "Hue налаштування" #define D_HUE_API_NOT_IMPLEMENTED "Hue API не реалізовано" #define D_HUE_API "Hue API" #define D_HUE_POST_ARGS "Hue POST args" -#define D_3_RESPONSE_PACKETS_SENT "3 пакету відповіді отримано" +#define D_3_RESPONSE_PACKETS_SENT "3 пакету відповіді отримано" // xdrv_07_domoticz.ino #define D_DOMOTICZ_PARAMETERS "Domoticz налаштування" #define D_DOMOTICZ_IDX "Idx" #define D_DOMOTICZ_KEY_IDX "Ключ idx" #define D_DOMOTICZ_SWITCH_IDX "Перемикач idx" -#define D_DOMOTICZ_SENSOR_IDX "Датчик idx" +#define D_DOMOTICZ_SENSOR_IDX "Давач idx" #define D_DOMOTICZ_TEMP "Температура" #define D_DOMOTICZ_TEMP_HUM "Темп,Волог" #define D_DOMOTICZ_TEMP_HUM_BARO "Темп,Волог,Тиск" @@ -400,12 +401,13 @@ #define D_DOMOTICZ_VOLTAGE "Напруга/PM2,5" #define D_DOMOTICZ_CURRENT "Струм/PM10" #define D_DOMOTICZ_AIRQUALITY "Якість повітря" + #define D_DOMOTICZ_P1_SMART_METER "P1SmartMeter" #define D_DOMOTICZ_UPDATE_TIMER "Оновлення таймерів" // xdrv_09_timers.ino -#define D_CONFIGURE_TIMER "Конфігурація таймеру" -#define D_TIMER_PARAMETERS "Налаштування таймеру" -#define D_TIMER_ENABLE "Увімкнений таймеру" +#define D_CONFIGURE_TIMER "Конфігурація таймерів" +#define D_TIMER_PARAMETERS "Налаштування таймерів" +#define D_TIMER_ENABLE "Таймери увімкнені" #define D_TIMER_ARM "Увімкнений" #define D_TIMER_TIME "Час" #define D_TIMER_DAYS "Дні" @@ -418,8 +420,8 @@ #define D_KNX_PARAMETERS "Налаштування KNX" #define D_KNX_GENERAL_CONFIG "Основні" #define D_KNX_PHYSICAL_ADDRESS "Фізична адреса" -#define D_KNX_PHYSICAL_ADDRESS_NOTE "( Має бути унікальним у мережі KNX)" -#define D_KNX_ENABLE "Увімкнений KNX" +#define D_KNX_PHYSICAL_ADDRESS_NOTE "(Має бути унікальною у мережі KNX)" +#define D_KNX_ENABLE "KNX Увімкнений" #define D_KNX_GROUP_ADDRESS_TO_WRITE "Дані для запису групових адрес" #define D_ADD "Додати" #define D_DELETE "Видалити" @@ -441,8 +443,21 @@ #define D_ENERGY_YESTERDAY "Енергія Вчора" #define D_ENERGY_TOTAL "Енергія Всього" +// xdrv_27_shutter.ino +#define D_OPEN "Open" +#define D_CLOSE "Close" +#define D_DOMOTICZ_SHUTTER "Shutter" + +// xdrv_28_pcf8574.ino +#define D_CONFIGURE_PCF8574 "Configure PCF8574" +#define D_PCF8574_PARAMETERS "PCF8574 parameters" +#define D_INVERT_PORTS "Invert Ports" +#define D_DEVICE "Device" +#define D_DEVICE_INPUT "Input" +#define D_DEVICE_OUTPUT "Output" + // xsns_05_ds18b20.ino -#define D_SENSOR_BUSY "Датчик DS18x20 занятий" +#define D_SENSOR_BUSY "Датчик DS18x20 зайнятий" #define D_SENSOR_CRC_ERROR "Датчик DS18x20 - помилка CRC" #define D_SENSORS_FOUND "Датчик DS18x20 знайдено" @@ -454,21 +469,21 @@ #define D_CHECKSUM_FAILURE "Помилка контрольної суми" // xsns_07_sht1x.ino -#define D_SENSOR_DID_NOT_ACK_COMMAND "Датчик не отримав команду ACK" +#define D_SENSOR_DID_NOT_ACK_COMMAND "Датчик не підтвердив отримання команди" #define D_SHT1X_FOUND "SHT1X знайдено" // xsns_18_pms5003.ino #define D_STANDARD_CONCENTRATION "CF-1 PM" // Standard Particle CF-1 Particle Matter #define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter -#define D_PARTICALS_BEYOND "Через Частини" +#define D_PARTICALS_BEYOND "Частинки понад" // xsns_32_mpu6050.ino -#define D_AX_AXIS "Accel. X-Axis" -#define D_AY_AXIS "Accel. Y-Axis" -#define D_AZ_AXIS "Accel. Z-Axis" -#define D_GX_AXIS "Gyro X-Axis" -#define D_GY_AXIS "Gyro Y-Axis" -#define D_GZ_AXIS "Gyro Z-Axis" +#define D_AX_AXIS "Приск. Вісь-X" +#define D_AY_AXIS "Приск. Вісь-Y" +#define D_AZ_AXIS "Приск. Вісь-Z" +#define D_GX_AXIS "Орієнт Вісь-X" +#define D_GY_AXIS "Орієнт Вісь-Y" +#define D_GZ_AXIS "Орієнт Вісь-Z" // xsns_34_hx711.ino #define D_HX_CAL_REMOVE "Remove weigth" @@ -484,21 +499,21 @@ #define D_CALIBRATION "Calibration" //xsns_35_tx20.ino -#define D_TX20_WIND_DIRECTION "Wind Direction" -#define D_TX20_WIND_SPEED "Wind Speed" -#define D_TX20_WIND_SPEED_AVG "Wind Speed Avg" -#define D_TX20_WIND_SPEED_MAX "Wind Speed Max" -#define D_TX20_NORTH "N" -#define D_TX20_EAST "E" -#define D_TX20_SOUTH "S" -#define D_TX20_WEST "W" +#define D_TX20_WIND_DIRECTION "Напрям вітру" +#define D_TX20_WIND_SPEED "Швидкість вітру" +#define D_TX20_WIND_SPEED_AVG "Середня швидкість вітру" +#define D_TX20_WIND_SPEED_MAX "Максимальна швидкість вітру" +#define D_TX20_NORTH "Пн" +#define D_TX20_EAST "Сх" +#define D_TX20_SOUTH "Пд" +#define D_TX20_WEST "Зх" //xsns_43_hre.ino #define D_LOG_HRE "HRE: " // sonoff_template.h - keep them as short as possible to be able to fit them in GUI drop down box -#define D_SENSOR_NONE "-відсутньо-" -#define D_SENSOR_USER "User" +#define D_SENSOR_NONE "Немає" +#define D_SENSOR_USER "Користувач" #define D_SENSOR_DHT11 "DHT11" #define D_SENSOR_AM2301 "AM2301" #define D_SENSOR_SI7021 "SI7021" @@ -513,7 +528,7 @@ #define D_SENSOR_RELAY "Реле" // Suffix "1i" #define D_SENSOR_LED "Led" // Suffix "1i" #define D_SENSOR_LED_LINK "LedLink" // Suffix "i" -#define D_SENSOR_PWM "PWM" // Suffix "1" +#define D_SENSOR_PWM "ШІМ" // Suffix "1" #define D_SENSOR_COUNTER "Лічильник" // Suffix "1" #define D_SENSOR_IRRECV "IRrecv" #define D_SENSOR_MHZ_RX "MHZ Rx" @@ -526,7 +541,10 @@ #define D_SENSOR_SAIR_TX "SAir Tx" #define D_SENSOR_SPI_CS "SPI CS" #define D_SENSOR_SPI_DC "SPI DC" -#define D_SENSOR_BACKLIGHT "BkLight" +#define D_SENSOR_SPI_MISO "SPI MISO" +#define D_SENSOR_SPI_MOSI "SPI MOSI" +#define D_SENSOR_SPI_CLK "SPI CLK" +#define D_SENSOR_BACKLIGHT "Backlight" #define D_SENSOR_PMS5003 "PMS5003" #define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" #define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" @@ -581,21 +599,48 @@ #define D_SENSOR_ARIRFSEL "ALux IrSel" #define D_SENSOR_TXD "Serial Tx" #define D_SENSOR_RXD "Serial Rx" -#define D_SENSOR_ROTARY "Rotary" // Suffix "1A" +#define D_SENSOR_ROTARY "Регулятор" // Suffix "1A" #define D_SENSOR_HRE_CLOCK "HRE Clock" #define D_SENSOR_HRE_DATA "HRE Data" #define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" +#define D_SENSOR_BUZZER "Зуммер" +#define D_SENSOR_OLED_RESET "OLED Reset" +#define D_SENSOR_ZIGBEE_TXD "Zigbee Tx" +#define D_SENSOR_ZIGBEE_RXD "Zigbee Rx" +#define D_SENSOR_SOLAXX1_TX "SolaxX1 Tx" +#define D_SENSOR_SOLAXX1_RX "SolaxX1 Rx" +#define D_SENSOR_IBEACON_TX "iBeacon TX" +#define D_SENSOR_IBEACON_RX "iBeacon RX" +#define D_SENSOR_RDM6300_RX "RDM6300 RX" +#define D_SENSOR_CC1101_CS "CC1101 CS" +#define D_SENSOR_A4988_DIR "A4988 DIR" +#define D_SENSOR_A4988_STP "A4988 STP" +#define D_SENSOR_A4988_ENA "A4988 ENA" +#define D_SENSOR_A4988_MS1 "A4988 MS1" +#define D_SENSOR_A4988_MS2 "A4988 MS2" +#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_DDS2382_TX "DDS238-2 Tx" +#define D_SENSOR_DDS2382_RX "DDS238-2 Rx" +#define D_SENSOR_DDSU666_TX "DDSU666 Tx" +#define D_SENSOR_DDSU666_RX "DDSU666 Rx" +#define D_SENSOR_SM2135_CLK "SM2135 Clk" +#define D_SENSOR_SM2135_DAT "SM2135 Dat" +#define D_SENSOR_DEEPSLEEP "DeepSleep" +#define D_SENSOR_EXS_ENABLE "EXS Enable" +#define D_SENSOR_ARDUINO_TX "Arduino TX" +#define D_SENSOR_ARDUINO_RX "Arduino RX" +#define D_SENSOR_ARDUINO_RESET "Arduino RST" // Units #define D_UNIT_AMPERE "А" #define D_UNIT_CENTIMETER "cм" #define D_UNIT_HERTZ "Гц" #define D_UNIT_HOUR "Г" -#define D_UNIT_GALLONS "gal" +#define D_UNIT_GALLONS "гал" #define D_UNIT_GALLONS_PER_MIN "g/m" #define D_UNIT_INCREMENTS "inc" -#define D_UNIT_KILOGRAM "kg" -#define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" +#define D_UNIT_KILOGRAM "кг" +#define D_UNIT_KILOMETER_PER_HOUR "км/г" // or "km/h" #define D_UNIT_KILOOHM "кОм" #define D_UNIT_KILOWATTHOUR "кВт" #define D_UNIT_LUX "лк" @@ -603,8 +648,8 @@ #define D_UNIT_MICROMETER "мкм" #define D_UNIT_MICROSECOND "мкс" #define D_UNIT_MILLIAMPERE "мА" -#define D_UNIT_MILLIMETER "mm" -#define D_UNIT_MILLIMETER_MERCURY "mmHg" +#define D_UNIT_MILLIMETER "мм" +#define D_UNIT_MILLIMETER_MERCURY "ммHg" #define D_UNIT_MILLISECOND "мс" #define D_UNIT_MINUTE "хв" #define D_UNIT_PARTS_PER_BILLION "ppb" @@ -613,12 +658,12 @@ #define D_UNIT_PRESSURE "гПа" #define D_UNIT_SECOND "сек" #define D_UNIT_SECTORS "секторів" -#define D_UNIT_VA "ВA" -#define D_UNIT_VAR "VAr" +#define D_UNIT_VA "ВА" +#define D_UNIT_VAR "ВАр" #define D_UNIT_VOLT "В" #define D_UNIT_WATT "Вт" -#define D_UNIT_WATTHOUR "ВтГод" -#define D_UNIT_WATT_METER_QUADRAT "W/m²" +#define D_UNIT_WATTHOUR "Вт/Год" +#define D_UNIT_WATT_METER_QUADRAT "Вт/m²" // Log message prefix #define D_LOG_APPLICATION "APP: " // Application @@ -646,13 +691,37 @@ #define D_LOG_WIFI "WIF: " // Wifi //SDM220 -#define D_PHASE_ANGLE "Phase Angle" -#define D_IMPORT_ACTIVE "Import Active" -#define D_EXPORT_ACTIVE "Export Active" -#define D_IMPORT_REACTIVE "Import Reactive" -#define D_EXPORT_REACTIVE "Export Reactive" -#define D_TOTAL_REACTIVE "Total Reactive" -#define D_UNIT_KWARH "kVArh" -#define D_UNIT_ANGLE "Deg" +#define D_PHASE_ANGLE "Кут фази" +#define D_IMPORT_ACTIVE "Активна вхід" +#define D_EXPORT_ACTIVE "Активна вихід" +#define D_IMPORT_REACTIVE "Рекативна вхід" +#define D_EXPORT_REACTIVE "Реактивна вихід" +#define D_TOTAL_REACTIVE "Всього реактивна" +#define D_UNIT_KWARH "кВАр/г" +#define D_UNIT_ANGLE "Град" + +//SOLAXX1 +#define D_PV1_VOLTAGE "PV1 Voltage" +#define D_PV1_CURRENT "PV1 Current" +#define D_PV1_POWER "PV1 Power" +#define D_PV2_VOLTAGE "PV2 Voltage" +#define D_PV2_CURRENT "PV2 Current" +#define D_PV2_POWER "PV2 Power" +#define D_SOLAR_POWER "Solar Power" +#define D_INVERTER_POWER "Inverter Power" +#define D_STATUS "Status" +#define D_WAITING "Waiting" +#define D_CHECKING "Checking" +#define D_WORKING "Working" +#define D_FAILURE "Failure" +#define D_SOLAX_ERROR_0 "No Error Code" +#define D_SOLAX_ERROR_1 "Grid Lost Fault" +#define D_SOLAX_ERROR_2 "Grid Voltage Fault" +#define D_SOLAX_ERROR_3 "Grid Frequency Fault" +#define D_SOLAX_ERROR_4 "Pv Voltage Fault" +#define D_SOLAX_ERROR_5 "Isolation Fault" +#define D_SOLAX_ERROR_6 "Over Temperature Fault" +#define D_SOLAX_ERROR_7 "Fan Fault" +#define D_SOLAX_ERROR_8 "Other Device Fault" #endif // _LANGUAGE_UK_UK_H_ diff --git a/sonoff/language/zh-CN.h b/sonoff/language/zh-CN.h index aa4a60492..36f71ea4c 100644 --- a/sonoff/language/zh-CN.h +++ b/sonoff/language/zh-CN.h @@ -284,6 +284,7 @@ #define D_LOGGING_PARAMETERS "日志设置" #define D_SERIAL_LOG_LEVEL "串口日志级别" +#define D_MQTT_LOG_LEVEL "Mqtt log level" #define D_WEB_LOG_LEVEL "Web 日志级别" #define D_SYS_LOG_LEVEL "Syslog 日志级别" #define D_MORE_DEBUG "全部调试" @@ -400,6 +401,7 @@ #define D_DOMOTICZ_VOLTAGE "电压/PM2.5" #define D_DOMOTICZ_CURRENT "电流/PM10" #define D_DOMOTICZ_AIRQUALITY "空气质量" + #define D_DOMOTICZ_P1_SMART_METER "P1SmartMeter" #define D_DOMOTICZ_UPDATE_TIMER "更新计时器" // xdrv_09_timers.ino @@ -441,6 +443,19 @@ #define D_ENERGY_YESTERDAY "昨日用电量" #define D_ENERGY_TOTAL "总用电量" +// xdrv_27_shutter.ino +#define D_OPEN "Open" +#define D_CLOSE "Close" +#define D_DOMOTICZ_SHUTTER "Shutter" + +// xdrv_28_pcf8574.ino +#define D_CONFIGURE_PCF8574 "Configure PCF8574" +#define D_PCF8574_PARAMETERS "PCF8574 parameters" +#define D_INVERT_PORTS "Invert Ports" +#define D_DEVICE "Device" +#define D_DEVICE_INPUT "Input" +#define D_DEVICE_OUTPUT "Output" + // xsns_05_ds18b20.ino #define D_SENSOR_BUSY "传感器正忙" #define D_SENSOR_CRC_ERROR "传感器 CRC 校验错误" @@ -526,7 +541,10 @@ #define D_SENSOR_SAIR_TX "SAir Tx" #define D_SENSOR_SPI_CS "SPI CS" #define D_SENSOR_SPI_DC "SPI DC" -#define D_SENSOR_BACKLIGHT "BkLight" +#define D_SENSOR_SPI_MISO "SPI MISO" +#define D_SENSOR_SPI_MOSI "SPI MOSI" +#define D_SENSOR_SPI_CLK "SPI CLK" +#define D_SENSOR_BACKLIGHT "Backlight" #define D_SENSOR_PMS5003 "PMS5003" #define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" #define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" @@ -585,10 +603,38 @@ #define D_SENSOR_HRE_CLOCK "HRE Clock" #define D_SENSOR_HRE_DATA "HRE Data" #define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" +#define D_SENSOR_BUZZER "Buzzer" +#define D_SENSOR_OLED_RESET "OLED Reset" +#define D_SENSOR_ZIGBEE_TXD "Zigbee Tx" +#define D_SENSOR_ZIGBEE_RXD "Zigbee Rx" +#define D_SENSOR_SOLAXX1_TX "SolaxX1 Tx" +#define D_SENSOR_SOLAXX1_RX "SolaxX1 Rx" +#define D_SENSOR_IBEACON_TX "iBeacon TX" +#define D_SENSOR_IBEACON_RX "iBeacon RX" +#define D_SENSOR_RDM6300_RX "RDM6300 RX" +#define D_SENSOR_CC1101_CS "CC1101 CS" +#define D_SENSOR_A4988_DIR "A4988 DIR" +#define D_SENSOR_A4988_STP "A4988 STP" +#define D_SENSOR_A4988_ENA "A4988 ENA" +#define D_SENSOR_A4988_MS1 "A4988 MS1" +#define D_SENSOR_A4988_MS2 "A4988 MS2" +#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_DDS2382_TX "DDS238-2 Tx" +#define D_SENSOR_DDS2382_RX "DDS238-2 Rx" +#define D_SENSOR_DDSU666_TX "DDSU666 Tx" +#define D_SENSOR_DDSU666_RX "DDSU666 Rx" +#define D_SENSOR_SM2135_CLK "SM2135 Clk" +#define D_SENSOR_SM2135_DAT "SM2135 Dat" +#define D_SENSOR_DEEPSLEEP "DeepSleep" +#define D_SENSOR_EXS_ENABLE "EXS Enable" +#define D_SENSOR_ARDUINO_TX "Arduino TX" +#define D_SENSOR_ARDUINO_RX "Arduino RX" +#define D_SENSOR_ARDUINO_RESET "Arduino RST" // Units #define D_UNIT_AMPERE "安" #define D_UNIT_CENTIMETER "厘米" +#define D_UNIT_HERTZ "赫兹" #define D_UNIT_HOUR "时" #define D_UNIT_GALLONS "gal" #define D_UNIT_GALLONS_PER_MIN "g/m" @@ -617,7 +663,6 @@ #define D_UNIT_VOLT "伏" #define D_UNIT_WATT "瓦" #define D_UNIT_WATTHOUR "瓦时" -#define D_UNIT_HERTZ "赫兹" #define D_UNIT_WATT_METER_QUADRAT "瓦/平米" // Log message prefix @@ -655,4 +700,28 @@ #define D_UNIT_KWARH "千乏时" #define D_UNIT_ANGLE "度" +//SOLAXX1 +#define D_PV1_VOLTAGE "PV1 Voltage" +#define D_PV1_CURRENT "PV1 Current" +#define D_PV1_POWER "PV1 Power" +#define D_PV2_VOLTAGE "PV2 Voltage" +#define D_PV2_CURRENT "PV2 Current" +#define D_PV2_POWER "PV2 Power" +#define D_SOLAR_POWER "Solar Power" +#define D_INVERTER_POWER "Inverter Power" +#define D_STATUS "Status" +#define D_WAITING "Waiting" +#define D_CHECKING "Checking" +#define D_WORKING "Working" +#define D_FAILURE "Failure" +#define D_SOLAX_ERROR_0 "No Error Code" +#define D_SOLAX_ERROR_1 "Grid Lost Fault" +#define D_SOLAX_ERROR_2 "Grid Voltage Fault" +#define D_SOLAX_ERROR_3 "Grid Frequency Fault" +#define D_SOLAX_ERROR_4 "Pv Voltage Fault" +#define D_SOLAX_ERROR_5 "Isolation Fault" +#define D_SOLAX_ERROR_6 "Over Temperature Fault" +#define D_SOLAX_ERROR_7 "Fan Fault" +#define D_SOLAX_ERROR_8 "Other Device Fault" + #endif // _LANGUAGE_ZH_CN_H_ diff --git a/sonoff/language/zh-TW.h b/sonoff/language/zh-TW.h index 2171d1ea0..53bd3e1a7 100644 --- a/sonoff/language/zh-TW.h +++ b/sonoff/language/zh-TW.h @@ -284,6 +284,7 @@ #define D_LOGGING_PARAMETERS "日志設置" #define D_SERIAL_LOG_LEVEL "串口日志級別" +#define D_MQTT_LOG_LEVEL "Mqtt log level" #define D_WEB_LOG_LEVEL "Web 日志級別" #define D_SYS_LOG_LEVEL "Syslog 日志級別" #define D_MORE_DEBUG "全部調試" @@ -400,6 +401,7 @@ #define D_DOMOTICZ_VOLTAGE "電壓/PM2.5" #define D_DOMOTICZ_CURRENT "電流/PM10" #define D_DOMOTICZ_AIRQUALITY "空氣品質" + #define D_DOMOTICZ_P1_SMART_METER "P1SmartMeter" #define D_DOMOTICZ_UPDATE_TIMER "更新計時器" // xdrv_09_timers.ino @@ -441,6 +443,19 @@ #define D_ENERGY_YESTERDAY "昨日用電量" #define D_ENERGY_TOTAL "總用電量" +// xdrv_27_shutter.ino +#define D_OPEN "Open" +#define D_CLOSE "Close" +#define D_DOMOTICZ_SHUTTER "Shutter" + +// xdrv_28_pcf8574.ino +#define D_CONFIGURE_PCF8574 "Configure PCF8574" +#define D_PCF8574_PARAMETERS "PCF8574 parameters" +#define D_INVERT_PORTS "Invert Ports" +#define D_DEVICE "Device" +#define D_DEVICE_INPUT "Input" +#define D_DEVICE_OUTPUT "Output" + // xsns_05_ds18b20.ino #define D_SENSOR_BUSY "傳感器正忙" #define D_SENSOR_CRC_ERROR "傳感器 CRC 校驗錯誤" @@ -526,7 +541,10 @@ #define D_SENSOR_SAIR_TX "SAir Tx" #define D_SENSOR_SPI_CS "SPI CS" #define D_SENSOR_SPI_DC "SPI DC" -#define D_SENSOR_BACKLIGHT "BkLight" +#define D_SENSOR_SPI_MISO "SPI MISO" +#define D_SENSOR_SPI_MOSI "SPI MOSI" +#define D_SENSOR_SPI_CLK "SPI CLK" +#define D_SENSOR_BACKLIGHT "Backlight" #define D_SENSOR_PMS5003 "PMS5003" #define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" #define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" @@ -585,6 +603,33 @@ #define D_SENSOR_HRE_CLOCK "HRE Clock" #define D_SENSOR_HRE_DATA "HRE Data" #define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" +#define D_SENSOR_BUZZER "Buzzer" +#define D_SENSOR_OLED_RESET "OLED Reset" +#define D_SENSOR_ZIGBEE_TXD "Zigbee Tx" +#define D_SENSOR_ZIGBEE_RXD "Zigbee Rx" +#define D_SENSOR_SOLAXX1_TX "SolaxX1 Tx" +#define D_SENSOR_SOLAXX1_RX "SolaxX1 Rx" +#define D_SENSOR_IBEACON_TX "iBeacon TX" +#define D_SENSOR_IBEACON_RX "iBeacon RX" +#define D_SENSOR_RDM6300_RX "RDM6300 RX" +#define D_SENSOR_CC1101_CS "CC1101 CS" +#define D_SENSOR_A4988_DIR "A4988 DIR" +#define D_SENSOR_A4988_STP "A4988 STP" +#define D_SENSOR_A4988_ENA "A4988 ENA" +#define D_SENSOR_A4988_MS1 "A4988 MS1" +#define D_SENSOR_A4988_MS2 "A4988 MS2" +#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_DDS2382_TX "DDS238-2 Tx" +#define D_SENSOR_DDS2382_RX "DDS238-2 Rx" +#define D_SENSOR_DDSU666_TX "DDSU666 Tx" +#define D_SENSOR_DDSU666_RX "DDSU666 Rx" +#define D_SENSOR_SM2135_CLK "SM2135 Clk" +#define D_SENSOR_SM2135_DAT "SM2135 Dat" +#define D_SENSOR_DEEPSLEEP "DeepSleep" +#define D_SENSOR_EXS_ENABLE "EXS Enable" +#define D_SENSOR_ARDUINO_TX "Arduino TX" +#define D_SENSOR_ARDUINO_RX "Arduino RX" +#define D_SENSOR_ARDUINO_RESET "Arduino RST" // Units #define D_UNIT_AMPERE "安" @@ -655,4 +700,28 @@ #define D_UNIT_KWARH "kVArh" #define D_UNIT_ANGLE "Deg" +//SOLAXX1 +#define D_PV1_VOLTAGE "PV1 Voltage" +#define D_PV1_CURRENT "PV1 Current" +#define D_PV1_POWER "PV1 Power" +#define D_PV2_VOLTAGE "PV2 Voltage" +#define D_PV2_CURRENT "PV2 Current" +#define D_PV2_POWER "PV2 Power" +#define D_SOLAR_POWER "Solar Power" +#define D_INVERTER_POWER "Inverter Power" +#define D_STATUS "Status" +#define D_WAITING "Waiting" +#define D_CHECKING "Checking" +#define D_WORKING "Working" +#define D_FAILURE "Failure" +#define D_SOLAX_ERROR_0 "No Error Code" +#define D_SOLAX_ERROR_1 "Grid Lost Fault" +#define D_SOLAX_ERROR_2 "Grid Voltage Fault" +#define D_SOLAX_ERROR_3 "Grid Frequency Fault" +#define D_SOLAX_ERROR_4 "Pv Voltage Fault" +#define D_SOLAX_ERROR_5 "Isolation Fault" +#define D_SOLAX_ERROR_6 "Over Temperature Fault" +#define D_SOLAX_ERROR_7 "Fan Fault" +#define D_SOLAX_ERROR_8 "Other Device Fault" + #endif // _LANGUAGE_ZH_TW_H_ diff --git a/sonoff/my_user_config.h b/sonoff/my_user_config.h index 5c692ee95..cb067fbf7 100644 --- a/sonoff/my_user_config.h +++ b/sonoff/my_user_config.h @@ -66,13 +66,9 @@ #define STA_PASS1 "" // [Password1] Wifi password #define STA_SSID2 "" // [Ssid2] Optional alternate AP Wifi SSID #define STA_PASS2 "" // [Password2] Optional alternate AP Wifi password -#define WIFI_CONFIG_TOOL WIFI_RETRY // [WifiConfig] Default tool if wifi fails to connect - // (WIFI_RESTART, WIFI_SMARTCONFIG, WIFI_MANAGER, WIFI_WPSCONFIG, WIFI_RETRY, WIFI_WAIT, WIFI_SERIAL) -#define WIFI_CONFIG_NO_SSID WIFI_WPSCONFIG // Default tool if wifi fails to connect and no SSID is configured - // (WIFI_SMARTCONFIG, WIFI_MANAGER, WIFI_WPSCONFIG, WIFI_SERIAL) - // *** NOTE: When WPS is disabled by USE_WPS below, WIFI_WPSCONFIG will execute WIFI_MANAGER *** - // *** NOTE: When WIFI_MANAGER is disabled by USE_WEBSERVER below, WIFI_MANAGER will execute WIFI_SMARTCONFIG *** - // *** NOTE: When WIFI_SMARTCONFIG is disabled by USE_SMARTCONFIG below, WIFI_SMARTCONFIG will execute WIFI_SERIAL *** +#define WIFI_CONFIG_TOOL WIFI_RETRY // [WifiConfig] Default tool if wifi fails to connect (default option: 4 - WIFI_RETRY) + // (WIFI_RESTART, WIFI_MANAGER, WIFI_RETRY, WIFI_WAIT, WIFI_SERIAL, WIFI_MANAGER_RESET_ONLY) + // The configuration can be changed after first setup using WifiConfig 0, 2, 4, 5, 6 and 7. // -- Syslog -------------------------------------- #define SYS_LOG_HOST "" // [LogHost] (Linux) syslog host @@ -80,6 +76,7 @@ #define SYS_LOG_LEVEL LOG_LEVEL_NONE // [SysLog] (LOG_LEVEL_NONE, LOG_LEVEL_ERROR, LOG_LEVEL_INFO, LOG_LEVEL_DEBUG, LOG_LEVEL_DEBUG_MORE) #define SERIAL_LOG_LEVEL LOG_LEVEL_INFO // [SerialLog] (LOG_LEVEL_NONE, LOG_LEVEL_ERROR, LOG_LEVEL_INFO, LOG_LEVEL_DEBUG, LOG_LEVEL_DEBUG_MORE) #define WEB_LOG_LEVEL LOG_LEVEL_INFO // [WebLog] (LOG_LEVEL_NONE, LOG_LEVEL_ERROR, LOG_LEVEL_INFO, LOG_LEVEL_DEBUG, LOG_LEVEL_DEBUG_MORE) +#define MQTT_LOG_LEVEL LOG_LEVEL_NONE // [MqttLog] (LOG_LEVEL_NONE, LOG_LEVEL_ERROR, LOG_LEVEL_INFO, LOG_LEVEL_DEBUG, LOG_LEVEL_DEBUG_MORE) // -- Ota ----------------------------------------- #define OTA_URL "http://thehackbox.org/tasmota/release/sonoff.bin" // [OtaUrl] @@ -238,14 +235,12 @@ //#define MY_LANGUAGE sk-SK // Slovak in Slovakia //#define MY_LANGUAGE sv-SE // Swedish in Sweden //#define MY_LANGUAGE tr-TR // Turkish in Turkey -//#define MY_LANGUAGE uk-UK // Ukrainian in Ukrain +//#define MY_LANGUAGE uk-UK // Ukrainian in Ukraine //#define MY_LANGUAGE zh-CN // Chinese (Simplified) in China //#define MY_LANGUAGE zh-TW // Chinese (Traditional) in Taiwan // -- Wifi Config tools --------------------------- #define WIFI_SOFT_AP_CHANNEL 1 // Soft Access Point Channel number between 1 and 13 as used by Wifi Manager web GUI -//#define USE_WPS // Add support for WPS as initial wifi configuration tool (+33k code, 1k mem (5k mem with core v2.4.2+)) -//#define USE_SMARTCONFIG // Add support for Wifi SmartConfig as initial wifi configuration tool (+23k code, +0.6k mem) // -- OTA ----------------------------------------- //#define USE_ARDUINO_OTA // Add optional support for Arduino OTA (+13k code) @@ -266,7 +261,8 @@ // Using TLS starting with version v6.5.0.16 compilation will only work using Core 2.4.2 and 2.5.2. No longer supported: 2.3.0 //#define USE_MQTT_TLS // Use TLS for MQTT connection (+34.5k code, +7.0k mem and +4.8k additional during connection handshake) // #define USE_MQTT_TLS_CA_CERT // Force full CA validation instead of fingerprints, slower, but simpler to use (+2.2k code, +1.9k mem during connection handshake) -// #define USE_MQTT_AWS_IOT // Enable MQTT for AWS IoT - requires a private key (+11.4k code, +0.4k mem) +// #define USE_MQTT_TLS_FORCE_EC_CIPHER // Force Elliptic Curve cipher (higher security) required by some servers (automatically enabled with USE_MQTT_AWS_IOT) (+11.4k code, +0.4k mem) +// #define USE_MQTT_AWS_IOT // Enable MQTT for AWS IoT - requires a private key (+11.9k code, +0.4k mem) // Note: you need to generate a private key + certificate per device and update 'sonoff/sonoff_aws_iot.cpp' // Full documentation here: https://github.com/arendst/Sonoff-Tasmota/wiki/AWS-IoT @@ -279,6 +275,7 @@ #define WEB_PORT 80 // Web server Port for User and Admin mode #define WEB_USERNAME "admin" // Web server Admin mode user name // #define USE_JAVASCRIPT_ES6 // Enable ECMAScript6 syntax using less JavaScript code bytes (fails on IE11) +// #define USE_WEBSEND_RESPONSE // Enable command WebSend response message (+1k code) #define USE_EMULATION_HUE // Enable Hue Bridge emulation for Alexa (+14k code, +2k mem common) #define USE_EMULATION_WEMO // Enable Belkin WeMo emulation for Alexa (+6k code, +2k mem common) @@ -297,23 +294,49 @@ // Select none or only one of the below defines #define USE_RULES // Add support for rules (+8k code) //#define USE_SCRIPT // Add support for script (+17k code) - #define USE_SCRIPT_FATFS 4 + //#define USE_SCRIPT_FATFS 4 // Script: Add FAT FileSystem Support // #define USE_EXPRESSION // Add support for expression evaluation in rules (+3k2 code, +64 bytes mem) +// #define SUPPORT_IF_STATEMENT // Add support for IF statement in rules (+4k2 code, -332 bytes mem) // #define SUPPORT_MQTT_EVENT // Support trigger event with MQTT subscriptions (+3k5 code) -// -- Counter input ----------------------- +// -- Optional modules ---------------------------- +//#define ROTARY_V1 // Add support for MI Desk Lamp +#define USE_SONOFF_RF // Add support for Sonoff Rf Bridge (+3k2 code) + #define USE_RF_FLASH // Add support for flashing the EFM8BB1 chip on the Sonoff RF Bridge. C2CK must be connected to GPIO4, C2D to GPIO5 on the PCB (+2k7 code) +#define USE_SONOFF_SC // Add support for Sonoff Sc (+1k1 code) +#define USE_TUYA_MCU // Add support for Tuya Serial MCU + #define TUYA_DIMMER_ID 0 // Default dimmer Id +#define USE_ARMTRONIX_DIMMERS // Add support for Armtronix Dimmers (+1k4 code) +#define USE_PS_16_DZ // Add support for PS-16-DZ Dimmer (+2k code) +#define USE_SONOFF_IFAN // Add support for Sonoff iFan02 and iFan03 (+2k code) +#define USE_BUZZER // Add support for a buzzer (+0k6 code) +#define USE_ARILUX_RF // Add support for Arilux RF remote controller (+0k8 code, 252 iram (non 2.3.0)) +//#define USE_SHUTTER // Add Shutter support for up to 4 shutter with different motortypes (+6k code) +//#define USE_DEEPSLEEP // Add support for deepsleep (+1k code) +//#define USE_EXS_DIMMER // Add support for ES-Store WiFi Dimmer (+1k5 code) +// #define EXS_MCU_CMNDS // Add command to send MCU commands (+0k8 code) + +// -- Optional light modules ---------------------- +#define USE_WS2812 // WS2812 Led string using library NeoPixelBus (+5k code, +1k mem, 232 iram) - Disable by // +// #define USE_WS2812_DMA // DMA supports only GPIO03 (= Serial RXD) (+1k mem). When USE_WS2812_DMA is enabled expect Exceptions on Pow + #define USE_WS2812_HARDWARE NEO_HW_WS2812 // Hardware type (NEO_HW_WS2812, NEO_HW_WS2812X, NEO_HW_WS2813, NEO_HW_SK6812, NEO_HW_LC8812, NEO_HW_APA106) + #define USE_WS2812_CTYPE NEO_GRB // Color type (NEO_RGB, NEO_GRB, NEO_BRG, NEO_RBG, NEO_RGBW, NEO_GRBW) +#define USE_MY92X1 // Add support for MY92X1 RGBCW led controller as used in Sonoff B1, Ailight and Lohas +#define USE_SM16716 // Add support for SM16716 RGB LED controller (+0k7 code) +#define USE_SM2135 // Add support for SM2135 RGBCW led control as used in Action LSC (+0k6 code) +#define USE_SONOFF_L1 // Add support for Sonoff L1 led control + +// -- Counter input ------------------------------- #define USE_COUNTER // Enable inputs as counter (+0k8 code) // -- Internal Analog input ----------------------- //#define USE_ADC_VCC // Display Vcc in Power status. Disable for use as Analog input on selected devices // -- One wire sensors ---------------------------- - // WARNING: Select none for default one DS18B20 sensor or enable one of the following two options for multiple sensors -//#define USE_DS18x20_LEGACY // Optional for more than one DS18x20 sensors with dynamic scan using library OneWire (+1k5 code) -#define USE_DS18x20 // Optional for more than one DS18x20 sensors with id sort, single scan and read retry (+1k3 code) -// #define W1_PARASITE_POWER // If using USE_DS18x20 then optimize for parasite powered sensors -// #define DS18B20_INTERNAL_PULLUP // Use INPUT_PULLUP internal pullup resistors for single DS18B20 +#define USE_DS18x20 // Add support for DS18x20 sensors with id sort, single scan and read retry (+2k6 code) +// #define W1_PARASITE_POWER // Optimize for parasite powered sensors +// #define DS18B20_INTERNAL_PULLUP // Use INPUT_PULLUP internal pullup resistor // -- I2C sensors --------------------------------- #define USE_I2C // I2C using library wire (+10k code, 0k2 mem, 124 iram) @@ -330,6 +353,7 @@ // #define USE_ADS1115 // Enable ADS1115 16 bit A/D converter (I2C address 0x48, 0x49, 0x4A or 0x4B) based on Adafruit ADS1x15 library (no library needed) (+0k7 code) // #define USE_ADS1115_I2CDEV // Enable ADS1115 16 bit A/D converter (I2C address 0x48, 0x49, 0x4A or 0x4B) using library i2cdevlib-Core and i2cdevlib-ADS1115 (+2k code) // #define USE_INA219 // Enable INA219 (I2C address 0x40, 0x41 0x44 or 0x45) Low voltage and current sensor (+1k code) +// #define USE_INA226 // Enable INA226 (I2C address 0x40, 0x41 0x44 or 0x45) Low voltage and current sensor (+2k3 code) #define USE_SHT3X // Enable SHT3x (I2C address 0x44 or 0x45) or SHTC3 (I2C address 0x70) sensor (+0k7 code) // #define USE_TSL2561 // Enable TSL2561 sensor (I2C address 0x29, 0x39 or 0x49) using library Joba_Tsl2561 (+2k3 code) // #define USE_MGS // Enable Xadow and Grove Mutichannel Gas sensor using library Multichannel_Gas_Sensor (+10k code) @@ -358,6 +382,9 @@ #define USE_ADE7953 // Enable ADE7953 Energy monitor as used on Shelly 2.5 (I2C address 0x38) (+1k5) // #define USE_VL53L0X // Enable VL53L0x time of flight sensor (I2C address 0x29) (+4k code) // #define USE_MLX90614 // Enable MLX90614 ir temp sensor (I2C address 0x5a) (+0.6k code) +// #define USE_CHIRP // Enable CHIRP soil moisture sensor (variable I2C address, default 0x20) +// #define USE_PAJ7620 // Enable PAJ7620 gesture sensor (I2C address 0x73) (+2.5k code) +// #define USE_PCF8574 // Enable PCF8574 I/O Expander (I2C addresses 0x20 - 0x27 and 0x38 - 0x3F) (+1k9 code) // #define USE_DISPLAY // Add I2C Display Support (+2k code) #define USE_DISPLAY_MODES1TO5 // Enable display mode 1 to 5 in addition to mode 0 @@ -372,6 +399,7 @@ #define MTX_ADDRESS6 0x76 // [DisplayAddress6] I2C address of sixth 8x8 matrix module #define MTX_ADDRESS7 0x00 // [DisplayAddress7] I2C address of seventh 8x8 matrix module #define MTX_ADDRESS8 0x00 // [DisplayAddress8] I2C address of eigth 8x8 matrix module +// #define USE_DISPLAY_SH1106 // [DisplayModel 7] Enable SH1106 Oled 128x64 display (I2C addresses 0x3C and 0x3D) #endif // USE_I2C // -- SPI sensors --------------------------------- @@ -382,7 +410,11 @@ #define USE_DISPLAY // Add SPI Display support for 320x240 and 480x320 TFT #endif #define USE_DISPLAY_ILI9341 // [DisplayModel 4] Enable ILI9341 Tft 480x320 display (+19k code) -// #define USE_DISPLAY_EPAPER_29 // [DisplayModel 5] Enable e-paper 2.9 inch display (+19k code) +// #define USE_DISPLAY_EPAPER_29 // [DisplayModel 5] Enable e-paper 2.9 inch display (+19k code) +// #define USE_DISPLAY_EPAPER_42 // [DisplayModel 6] Enable e-paper 4.2 inch display +// #define USE_DISPLAY_ILI9488 // [DisplayModel 8] +// #define USE_DISPLAY_SSD1351 // [DisplayModel 9] +// #define USE_DISPLAY_RA8876 // [DisplayModel 10] #endif // USE_SPI // -- Serial sensors ------------------------------ @@ -391,50 +423,99 @@ #define CO2_LOW 800 // Below this CO2 value show green light (needs PWM or WS2812 RG(B) led and enable with SetOption18 1) #define CO2_HIGH 1200 // Above this CO2 value show red light (needs PWM or WS2812 RG(B) led and enable with SetOption18 1) #define USE_PMS5003 // Add support for PMS5003 and PMS7003 particle concentration sensor (+1k3 code) -#define USE_NOVA_SDS // Add support for SDS011 and SDS021 particle concentration sensor (+0k7 code) - #define WORKING_PERIOD 5 // Working period of the SDS Sensor, Takes a reading every X Minutes + //#define PMS_MODEL_PMS3003 // Enable support of PMS3003 instead of PMS5003/PMS7003 (needs the USE_PMS5003 above) +#define USE_NOVA_SDS // Add support for SDS011 and SDS021 particle concentration sensor (+1k5 code) + #define STARTING_OFFSET 30 // Turn on NovaSDS XX-seconds before tele_period is reached #define USE_SERIAL_BRIDGE // Add support for software Serial Bridge (+0k8 code) -//#define USE_SDM120 // Add support for Eastron SDM120-Modbus energy meter (+1k7 code) - #define SDM120_SPEED 9600 // SDM120-Modbus RS485 serial speed (default: 2400 baud) - #define USE_SDM220 // Add extra parameters for SDM220 (+0k1 code) -//#define USE_SDM630 // Add support for Eastron SDM630-Modbus energy meter (+2k code) - #define SDM630_SPEED 9600 // SDM630-Modbus RS485 serial speed (default: 9600 baud) //#define USE_MP3_PLAYER // Use of the DFPlayer Mini MP3 Player RB-DFR-562 commands: play, volume and stop #define MP3_VOLUME 10 // Set the startup volume on init, the range can be 0..30(max) -#define USE_TUYA_DIMMER // Add support for Tuya Serial Dimmer - #define TUYA_DIMMER_ID 0 // Default dimmer Id -#define USE_ARMTRONIX_DIMMERS // Add support for Armtronix Dimmers (+1k4 code) -#define USE_PS_16_DZ // Add support for PS-16-DZ Dimmer and Sonoff L1 (+2k code) -//#define ROTARY_V1 // Add support for MI Desk Lamp //#define USE_AZ7798 // Add support for AZ-Instrument 7798 CO2 datalogger (+1k6 code) //#define USE_PN532_HSU // Add support for PN532 using HSU (Serial) interface (+1k8 code, 140 bytes mem) // #define USE_PN532_CAUSE_EVENTS // Cause event execution for PN532_UID= and PN532_DATA=[if defined] (+ 30 bytes code) // #define USE_PN532_DATA_FUNCTION // Add sensor40 command support for erase, setting data block content (+1k7 code, 388 bytes mem) // #define USE_PN532_DATA_RAW // Allow DATA block to be used by non-alpha-numberic data (+ 80 bytes code, 48 bytes ram) +//#define USE_RDM6300 // Add support for RDM6300 125kHz RFID Reader (+0k8) +//#define USE_IBEACON // Add support for bluetooth LE passive scan of ibeacon devices (uses HM17 module) -// Power monitoring sensors ----------------------- +// -- Power monitoring sensors -------------------- +#define USE_ENERGY_MARGIN_DETECTION // Add support for Energy Margin detection (+1k6 code) + #define USE_ENERGY_POWER_LIMIT // Add additional support for Energy Power Limit detection (+1k2 code) #define USE_PZEM004T // Add support for PZEM004T Energy monitor (+2k code) #define USE_PZEM_AC // Add support for PZEM014,016 Energy monitor (+1k1 code) #define USE_PZEM_DC // Add support for PZEM003,017 Energy monitor (+1k1 code) #define USE_MCP39F501 // Add support for MCP39F501 Energy monitor as used in Shelly 2 (+3k1 code) +//#define USE_SDM120 // Add support for Eastron SDM120-Modbus energy monitor (+1k1 code) + #define SDM120_SPEED 2400 // SDM120-Modbus RS485 serial speed (default: 2400 baud) +//#define USE_SDM630 // Add support for Eastron SDM630-Modbus energy monitor (+0k6 code) + #define SDM630_SPEED 9600 // SDM630-Modbus RS485 serial speed (default: 9600 baud) +//#define USE_DDS2382 // Add support for Hiking DDS2382 Modbus energy monitor (+0k6 code) + #define DDS2382_SPEED 9600 // Hiking DDS2382 Modbus RS485 serial speed (default: 9600 baud) +//#define USE_DDSU666 // Add support for Chint DDSU666 Modbus energy monitor (+0k6 code) + #define DDSU666_SPEED 9600 // Chint DDSU666 Modbus RS485 serial speed (default: 9600 baud) +//#define USE_SOLAX_X1 // Add support for Solax X1 series Modbus log info (+3k1 code) + #define SOLAXX1_SPEED 9600 // Solax X1 Modbus RS485 serial speed (default: 9600 baud) + #define SOLAXX1_PV2 // Solax X1 using second PV // -- Low level interface devices ----------------- #define USE_DHT // Add support for DHT11, AM2301 (DHT21, DHT22, AM2302, AM2321) and SI7021 Temperature and Humidity sensor (1k6 code) //#define USE_MAX31855 // Add support for MAX31855 K-Type thermocouple sensor using softSPI +//#define USE_MAX31865 // Add support for MAX31865 RTD sensors using softSPI + #define MAX31865_PTD_WIRES 2 // PTDs come in several flavors. Pick yours + #define MAX31865_PTD_RES 100 // Nominal PTD resistance at 0°C (100Ω for a PT100, 1000Ω for a PT1000, YMMV!) + #define MAX31865_REF_RES 430 // Reference resistor (Usually 430Ω for a PT100, 4300Ω for a PT1000) + #define MAX31865_PTD_BIAS 0 // To calibrate your not-so-good PTD +// -- IR Remote features - all protocols from IRremoteESP8266 -------------------------- +// IR Full Protocols mode is activated through platform.io only. +// Either use 'default_envs = sonoff-ircustom' and disable some features here to keep code not too big +// or use 'default_envs = sonoff-ir' for a pre-packaged IR-dedicated firmware +// When using 'sonoff-ircustom' or 'sonoff-ir', parameters below +// (USE_IR_REMOTE, USE_IR_RECEIVE, USE_IR_HVAC...) are IGNORED. +// +// Code impact of IR full protocols is +81k code, 3k mem +// You can reduce this size by disabling some protocols in "lib/IRremoteESP8266.x.x.x/src/IRremoteESP8266.h" + +// -- IR Remote features - subset of IR protocols -------------------------- #define USE_IR_REMOTE // Send IR remote commands using library IRremoteESP8266 and ArduinoJson (+4k3 code, 0k3 mem, 48 iram) -// #define USE_IR_HVAC // Support for HVAC (Toshiba, Mitsubishi and LG) system using IR (+3k5 code) +// #define USE_IR_SEND_AIWA // Support IRsend Aiwa protocol + #define USE_IR_SEND_DISH // Support IRsend Dish protocol + #define USE_IR_SEND_JVC // Support IRsend JVC protocol +// #define USE_IR_SEND_LG // Support IRsend LG protocol +// #define USE_IR_SEND_MITSUBISHI // Support IRsend Mitsubishi protocol + #define USE_IR_SEND_NEC // Support IRsend NEC protocol + #define USE_IR_SEND_PANASONIC // Support IRsend Panasonic protocol + #define USE_IR_SEND_PIONEER // Support IRsend Pioneer protocol + #define USE_IR_SEND_RC5 // Support IRsend Philips RC5 protocol + #define USE_IR_SEND_RC6 // Support IRsend Philips RC6 protocol + #define USE_IR_SEND_SAMSUNG // Support IRsend Samsung protocol +// #define USE_IR_SEND_SANYO // Support IRsend Sanyo protocol +// #define USE_IR_SEND_SHARP // Support IRsend Sharp protocol + #define USE_IR_SEND_SONY // Support IRsend Sony protocol +// #define USE_IR_SEND_WHYNTER // Support IRsend Whynter protocol + +// #define USE_IR_HVAC // Support for HVAC systems using IR (+3k5 code) + #define USE_IR_HVAC_TOSHIBA // Support IRhvac Toshiba protocol + #define USE_IR_HVAC_MITSUBISHI // Support IRhvac Mitsubischi protocol + #define USE_IR_HVAC_LG // Support IRhvac LG protocol + #define USE_IR_HVAC_FUJITSU // Support IRhvac Fujitsu protocol +// #define USE_IR_HVAC_MIDEA // Support IRhvac Midea/Komeco protocol + #define USE_IR_RECEIVE // Support for IR receiver (+7k2 code, 264 iram) #define IR_RCV_BUFFER_SIZE 100 // Max number of packets allowed in capture buffer (default 100 (*2 bytes ram)) #define IR_RCV_TIMEOUT 15 // Number of milli-Seconds of no-more-data before we consider a message ended (default 15) #define IR_RCV_MIN_UNKNOWN_SIZE 6 // Set the smallest sized "UNKNOWN" message packets we actually care about (default 6, max 255) -#define USE_WS2812 // WS2812 Led string using library NeoPixelBus (+5k code, +1k mem, 232 iram) - Disable by // - #define USE_WS2812_CTYPE NEO_GRB // WS2812 Color type (NEO_RGB, NEO_GRB, NEO_BRG, NEO_RBG, NEO_RGBW, NEO_GRBW) -// #define USE_WS2812_DMA // DMA supports only GPIO03 (= Serial RXD) (+1k mem). When USE_WS2812_DMA is enabled expect Exceptions on Pow +// -- Zigbee interface ---------------------------- +//#define USE_ZIGBEE // Enable serial communication with Zigbee CC2530 flashed with ZNP + #define USE_ZIGBEE_PANID 0x1A63 // arbitrary PAN ID for Zigbee network, must be unique in the home + #define USE_ZIGBEE_EXTPANID 0xCCCCCCCCCCCCCCCCL // arbitrary extended PAN ID + #define USE_ZIGBEE_CHANNEL 11 // Zigbee Channel (11-26) + #define USE_ZIGBEE_PRECFGKEY_L 0x0F0D0B0907050301L // note: changing requires to re-pair all devices + #define USE_ZIGBEE_PRECFGKEY_H 0x0D0C0A0806040200L // note: changing requires to re-pair all devices + #define USE_ZIGBEE_PERMIT_JOIN false // don't allow joining by default -#define USE_ARILUX_RF // Add support for Arilux RF remote controller (+0k8 code, 252 iram (non 2.3.0)) +// -- Other sensors/drivers ----------------------- #define USE_SR04 // Add support for HC-SR04 ultrasonic devices (+1k code) @@ -442,24 +523,30 @@ #define USE_HX711 // Add support for HX711 load cell (+1k5 code) // #define USE_HX711_GUI // Add optional web GUI to HX711 as scale (+1k8 code) -#define USE_RF_FLASH // Add support for flashing the EFM8BB1 chip on the Sonoff RF Bridge. C2CK must be connected to GPIO4, C2D to GPIO5 on the PCB (+3k code) +//#define USE_TX20_WIND_SENSOR // Add support for La Crosse TX20 anemometer (+2k code) -#define USE_TX20_WIND_SENSOR // Add support for La Crosse TX20 anemometer (+2k code) - -#define USE_RC_SWITCH // Add support for RF transceiver using library RcSwitch (+2k7 code, 460 iram) +//#define USE_RC_SWITCH // Add support for RF transceiver using library RcSwitch (+2k7 code, 460 iram) //#define USE_RF_SENSOR // Add support for RF sensor receiver (434MHz or 868MHz) (+0k8 code) // #define USE_THEO_V2 // Add support for decoding Theo V2 sensors as documented on https://sidweb.nl using 434MHz RF sensor receiver (+1k4 code) // #define USE_ALECTO_V2 // Add support for decoding Alecto V2 sensors like ACH2010, WS3000 and DKW2012 weather stations using 868MHz RF sensor receiver (+1k7 code) -#define USE_SM16716 // Add support for SM16716 RGB LED controller (+0k7 code) - //#define USE_HRE // Add support for Badger HR-E Water Meter (+1k4 code) +//#define USE_A4988_STEPPER // Add support for A4988/DRV8825 stepper-motor-driver-circuit (+10k5 code) + +//#define USE_ARDUINO_SLAVE // Add support for Arduino Uno/Pro Mini via serial interface including flashing (+2k3 code, 44 mem) + #define USE_ARDUINO_FLASH_SPEED 57600 // Usually 57600 for 3.3V variants and 115200 for 5V variants + #define USE_ARDUINO_SERIAL_SPEED 57600 // Depends on the sketch that is running on the Uno/Pro Mini + +// -- End of general directives ------------------- /*********************************************************************************************\ - * Debug features are only supported in development branch + * Debug features \*********************************************************************************************/ +//#define DEBUG_TASMOTA_CORE // Enable core debug messages +//#define DEBUG_TASMOTA_DRIVER // Enable driver debug messages +//#define DEBUG_TASMOTA_SENSOR // Enable sensor debug messages //#define USE_DEBUG_DRIVER // Use xdrv_99_debug.ino providing commands CpuChk, CfgXor, CfgDump, CfgPeek and CfgPoke /*********************************************************************************************\ @@ -468,17 +555,22 @@ * See RELEASENOTES.md for selected features \*********************************************************************************************/ -//#define FIRMWARE_CLASSIC // Create sonoff-classic with initial configuration tools WPS, SmartConfig and WifiManager //#define FIRMWARE_BASIC // Create sonoff-basic with no sensors //#define FIRMWARE_SENSORS // Create sonoff-sensors with useful sensors enabled //#define FIRMWARE_KNX_NO_EMULATION // Create sonoff-knx with KNX but without Emulation //#define FIRMWARE_DISPLAYS // Create sonoff-display with display drivers enabled +//#define FIRMWARE_IR // Create sonoff-ir with IR full protocols activated, and many sensors disabled +//#define FIRMWARE_IR_CUSTOM // Create sonoff customizable with special marker to add all IR protocols //#define FIRMWARE_MINIMAL // Create sonoff-minimal as intermediate firmware for OTA-MAGIC /*********************************************************************************************\ * No user configurable items below \*********************************************************************************************/ +#ifdef USE_CONFIG_OVERRIDE + #include "user_config_override.h" // Configuration overrides for my_user_config.h +#endif + #if defined(USE_DISCOVERY) && defined(USE_MQTT_AWS_IOT) #error "Select either USE_DISCOVERY or USE_MQTT_AWS_IOT, mDNS takes too much code space and is not needed for AWS IoT" #endif diff --git a/sonoff/sendemail.h b/sonoff/sendemail.h new file mode 100644 index 000000000..2422ad91f --- /dev/null +++ b/sonoff/sendemail.h @@ -0,0 +1,38 @@ +#ifndef __SENDEMAIL_H +#define __SENDEMAIL_H + +//#define DEBUG_EMAIL_PORT + +//#include +//#include +#include +//#include + +#include "WiFiClientSecureLightBearSSL.h" + +class SendEmail +{ + private: + const String host; + const int port; + const String user; + const String passwd; + const int timeout; + const bool ssl; + const int auth_used; +#if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_2) + WiFiClient* client; +#else + // use bear ssl + BearSSL::WiFiClientSecure_light *client; +#endif + String readClient(); + void a3_to_a4(unsigned char * a4, unsigned char * a3); + int base64_encode(char *output, const char *input, int inputLen); + public: + SendEmail(const String& host, const int port, const String& user, const String& passwd, const int timeout, const int auth_used); + bool send(const String& from, const String& to, const String& subject, const char *msg); + ~SendEmail() {client->stop(); delete client;} +}; + +#endif diff --git a/sonoff/sendemail.ino b/sonoff/sendemail.ino new file mode 100644 index 000000000..70571b5d2 --- /dev/null +++ b/sonoff/sendemail.ino @@ -0,0 +1,369 @@ +#ifdef USE_SENDMAIL + +#include "sendemail.h" + +// enable serial debugging +//#define DEBUG_EMAIL_PORT + +// sendmail works only with server port 465 SSL and doesnt support STARTTLS (not supported in Arduino) +// only a couple of mailservers support this (e.g. gmail,gmx,yahoo,freenetmail) +// sendmail [server:port:user:passwd:from:to:subject] body +// sendmail [*:*:*:*:*:to:subject] data uses defines from user_config_overwrite +// #define EMAIL_USER "user" +// #define EMAIL_PASSWORD "passwd" +// #define EMAIL_FROM "" +// #define EMAIL_SERVER "smtp.gmail.com" +// #define EMAIL_PORT 465 +// if email body consist of a single * and scripter is present +// and a section >m is found, the lines in this section (until #) are sent +// as email body + +// sendmail works with pre2.6 using Light BearSSL +//HW Watchdog 8.44 sec. +//SW Watchdog 3.2 sec. + +#ifndef SEND_MAIL_MINRAM +#define SEND_MAIL_MINRAM 12*1024 +#endif + +#define xPSTR(a) a + +uint16_t SendMail(char *buffer) { + char *params,*oparams; + const char *mserv; + uint16_t port; + const char *user; + const char *pstr; + const char *passwd; + const char *from; + const char *to; + const char *subject; + const char *cmd; + char auth=0; + uint16_t status=1; + SendEmail *mail=0; + uint16_t blen; + char *endcmd; + + +// return if not enough memory + uint16_t mem=ESP.getFreeHeap(); + if (memsend(from,to,subject,cmd); + delete mail; + if (result==true) status=0; + } + +exit: + if (oparams) free(oparams); + return status; +} + +void script_send_email_body(BearSSL::WiFiClientSecure_light *client); + + +SendEmail::SendEmail(const String& host, const int port, const String& user, const String& passwd, const int timeout, const int auth_used) : + host(host), port(port), user(user), passwd(passwd), timeout(timeout), ssl(ssl), auth_used(auth_used), client(new BearSSL::WiFiClientSecure_light(1024,1024)) { +} + +String SendEmail::readClient() { + delay(0); + String r = client->readStringUntil('\n'); + + r.trim(); + while (client->available()) { + delay(0); + r += client->readString(); + } + return r; +} + +bool SendEmail::send(const String& from, const String& to, const String& subject, const char *msg) { +bool status=false; +String buffer; + + if (!host.length()) { + return status; + } + + client->setTimeout(timeout); + // smtp connect +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("Connecting: %s on port %d"),host.c_str(),port); +#endif + + if (!client->connect(host.c_str(), port)) { +#ifdef DEBUG_EMAIL_PORT + AddLog_P(LOG_LEVEL_INFO, PSTR("Connection failed")); +#endif + goto exit; + } + + buffer = readClient(); +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); +#endif + if (!buffer.startsWith(F("220"))) { + goto exit; + } + + buffer = F("EHLO "); + buffer += client->localIP().toString(); + + client->println(buffer); +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); +#endif + buffer = readClient(); +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); +#endif + if (!buffer.startsWith(F("250"))) { + goto exit; + } + if (user.length()>0 && passwd.length()>0 ) { + + buffer = F("AUTH LOGIN"); + client->println(buffer); +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); +#endif + buffer = readClient(); +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); +#endif + if (!buffer.startsWith(F("334"))) + { + goto exit; + } + base64 b; + buffer = b.encode(user); + + client->println(buffer); +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); +#endif + buffer = readClient(); +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); +#endif + if (!buffer.startsWith(F("334"))) { + goto exit; + } + buffer = b.encode(passwd); + client->println(buffer); +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); +#endif + buffer = readClient(); +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); +#endif + if (!buffer.startsWith(F("235"))) { + goto exit; + } + } + + // smtp send mail + buffer = F("MAIL FROM:"); + buffer += from; + client->println(buffer); +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); +#endif + buffer = readClient(); +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); +#endif + if (!buffer.startsWith(F("250"))) { + goto exit; + } + buffer = F("RCPT TO:"); + buffer += to; + client->println(buffer); +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); +#endif + buffer = readClient(); +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); +#endif + if (!buffer.startsWith(F("250"))) { + goto exit; + } + + buffer = F("DATA"); + client->println(buffer); +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); +#endif + buffer = readClient(); +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); +#endif + if (!buffer.startsWith(F("354"))) { + goto exit; + } + buffer = F("From: "); + buffer += from; + client->println(buffer); +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); +#endif + buffer = F("To: "); + buffer += to; + client->println(buffer); +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); +#endif + buffer = F("Subject: "); + buffer += subject; + buffer += F("\r\n"); + client->println(buffer); +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); +#endif + +#ifdef USE_SCRIPT + if (*msg=='*' && *(msg+1)==0) { + script_send_email_body(client); + } else { + client->println(msg); + } +#else + client->println(msg); +#endif + client->println('.'); +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); +#endif + + buffer = F("QUIT"); + client->println(buffer); +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); +#endif + + status=true; +exit: + + return status; +} + + +#endif // USE_SENDMAIL diff --git a/sonoff/settings.h b/sonoff/settings.h index 0fa7f5d48..80960260a 100644 --- a/sonoff/settings.h +++ b/sonoff/settings.h @@ -27,7 +27,7 @@ typedef union { // Restricted by MISRA-C Rule 18.4 bu struct { // SetOption0 .. SetOption31 uint32_t save_state : 1; // bit 0 - SetOption0 - Save power state and use after restart uint32_t button_restrict : 1; // bit 1 - SetOption1 - Control button multipress - uint32_t value_units : 1; // bit 2 - SetOption2 - Add units to JSON status messages + uint32_t ex_value_units : 1; // bit 2 - SetOption2 - Add units to JSON status messages - removed 6.6.0.21 uint32_t mqtt_enabled : 1; // bit 3 - SetOption3 - Control MQTT uint32_t mqtt_response : 1; // bit 4 - SetOption4 - Switch between MQTT RESULT or COMMAND uint32_t mqtt_power_retain : 1; // bit 5 - CMND_POWERRETAIN @@ -78,14 +78,14 @@ typedef union { // Restricted by MISRA-C Rule 18.4 bu uint32_t no_hold_retain : 1; // bit 12 (v6.4.1.19) - SetOption62 - Don't use retain flag on HOLD messages uint32_t no_power_feedback : 1; // bit 13 (v6.5.0.9) - SetOption63 - Don't scan relay power state at restart uint32_t use_underscore : 1; // bit 14 (v6.5.0.12) - SetOption64 - Enable "_" instead of "-" as sensor index separator - uint32_t tuya_show_dimmer : 1; // bit 15 (v6.5.0.15) - SetOption65 - Enable or Disable Dimmer slider control - uint32_t spare16 : 1; - uint32_t spare17 : 1; - uint32_t spare18 : 1; - uint32_t spare19 : 1; - uint32_t spare20 : 1; - uint32_t spare21 : 1; - uint32_t spare22 : 1; + uint32_t fast_power_cycle_disable : 1; // bit 15 (v6.6.0.20) - SetOption65 - Disable fast power cycle detection for device reset + uint32_t ex_tuya_dimmer_range_255 : 1; // bit 16 (v6.6.0.1) - SetOption66 - Enable or Disable Dimmer range 255 slider control + uint32_t buzzer_enable : 1; // bit 17 (v6.6.0.1) - SetOption67 - Enable buzzer when available + uint32_t pwm_multi_channels : 1; // bit 18 (v6.6.0.3) - SetOption68 - Enable multi-channels PWM instead of Color PWM + uint32_t ex_tuya_dimmer_min_limit : 1; // bit 19 (v6.6.0.5) - SetOption69 - Limits Tuya dimmers to minimum of 10% (25) when enabled. + uint32_t energy_weekend : 1; // bit 20 (v6.6.0.8) - CMND_TARIFF + uint32_t dds2382_model : 1; // bit 21 (v6.6.0.14) - SetOption71 - Select different Modbus registers for Active Energy (#6531) + uint32_t hardware_energy_total : 1; // bit 22 (v6.6.0.15) - SetOption72 - Enable / Disable hardware energy total counter as reference (#6561) uint32_t spare23 : 1; uint32_t spare24 : 1; uint32_t spare25 : 1; @@ -93,8 +93,8 @@ typedef union { // Restricted by MISRA-C Rule 18.4 bu uint32_t spare27 : 1; uint32_t spare28 : 1; uint32_t spare29 : 1; - uint32_t spare30 : 1; - uint32_t spare31 : 1; + uint32_t shutter_mode : 1; // bit 30 (v6.6.0.14) - SetOption80 - Enable shutter support + uint32_t pcf8574_ports_inverted : 1; // bit 31 (v6.6.0.14) - SetOption81 - Invert all ports on PCF8574 devices }; } SysBitfield3; @@ -105,8 +105,7 @@ typedef union { uint32_t spare01 : 1; uint32_t spare02 : 1; uint32_t spare03 : 1; - uint32_t spare04 : 1; - uint32_t spare05 : 1; + uint32_t time_format : 2; // (v6.6.0.9) - CMND_TIME uint32_t calc_resolution : 3; uint32_t weight_resolution : 2; uint32_t frequency_resolution : 2; @@ -172,11 +171,28 @@ typedef union { uint8_t spare3 : 1; uint8_t spare4 : 1; uint8_t spare5 : 1; - uint8_t spare6 : 1; + uint8_t hx711_json_weight_change : 1; // Sensor34 8,x - Enable JSON message on weight change uint8_t mhz19b_abc_disable : 1; // Disable ABC (Automatic Baseline Correction for MHZ19(B) (0 = Enabled (default), 1 = Disabled with Sensor15 command) }; } SensorCfg1; +typedef struct { + uint32_t usage1_kWhtotal; + uint32_t usage2_kWhtotal; + uint32_t return1_kWhtotal; + uint32_t return2_kWhtotal; + uint32_t last_return_kWhtotal; + uint32_t last_usage_kWhtotal; +} EnergyUsage; + + +typedef struct { + uint8_t fnid = 0; + uint8_t dpid = 0; +} TuyaFnidDpidMap; + +const uint8_t MAX_TUYA_FUNCTIONS = 16; + /* struct SYSCFG { unsigned long cfg_holder; // 000 Pre v6 header @@ -196,7 +212,7 @@ struct SYSCFG { int8_t timezone; // 016 char ota_url[101]; // 017 char mqtt_prefix[3][11]; // 07C - uint8_t baudrate; // 09D + uint8_t ex_baudrate; // 09D - Free since 6.6.0.9 uint8_t seriallog_level; // 09E uint8_t sta_config; // 09F uint8_t sta_active; // 0A0 @@ -211,9 +227,9 @@ struct SYSCFG { uint8_t weblog_level; // 1AC uint8_t mqtt_fingerprint[2][20]; // 1AD uint8_t adc_param_type; // 1D5 - - uint8_t free_1D6[18]; // 1D6 Free since 5.12.0e - + uint8_t register8[16]; // 1D6 - 16 x 8-bit registers indexed by enum SettingsRegister8 + uint8_t shutter_accuracy; // 1E6 + uint8_t mqttlog_level; // 1E7 uint8_t sps30_inuse_hours; // 1E8 char mqtt_host[33]; // 1E9 - Keep together with below as being copied as one chunck with reset 6 uint16_t mqtt_port; // 20A - Keep together @@ -243,7 +259,7 @@ struct SYSCFG { int16_t toffset[2]; // 30E uint8_t display_font; // 312 char state_text[4][11]; // 313 - uint8_t energy_power_delta; // 33F + uint8_t ex_energy_power_delta; // 33F - Free since 6.6.0.20 uint16_t domoticz_update_timer; // 340 uint16_t pwm_range; // 342 unsigned long domoticz_relay_idx[MAX_DOMOTICZ_IDX]; // 344 @@ -279,7 +295,7 @@ struct SYSCFG { char friendlyname[MAX_FRIENDLYNAMES][33]; // 3AC char switch_topic[33]; // 430 char serial_delimiter; // 451 - uint8_t sbaudrate; // 452 + uint8_t ex_sbaudrate; // 452 - Free since 6.6.0.9 uint8_t sleep; // 453 uint16_t domoticz_switch_idx[MAX_DOMOTICZ_IDX]; // 454 uint16_t domoticz_sensor_idx[MAX_DOMOTICZ_SNS_IDX]; // 45C @@ -332,17 +348,19 @@ struct SYSCFG { uint8_t rgbwwTable[5]; // 71A uint8_t user_template_base; // 71F mytmplt user_template; // 720 29 bytes - uint8_t novasds_period; // 73D + uint8_t novasds_startingoffset; // 73D uint8_t web_color[18][3]; // 73E - - uint8_t free_774[32]; // 774 - + uint16_t display_width; // 774 + uint16_t display_height; // 776 + uint16_t baudrate; // 778 + uint16_t sbaudrate; // 77A + EnergyUsage energy_usage; // 77C // uint32_t drivers[3]; // 794 - 6.5.0.12 replaced by below three entries uint32_t adc_param1; // 794 uint32_t adc_param2; // 798 int adc_param3; // 79C uint32_t monitors; // 7A0 - uint32_t sensors[3]; // 7A4 + uint32_t sensors[3]; // 7A4 Normal WebSensor, Debug SetSensor uint32_t displays; // 7B0 uint32_t energy_kWhtotal_time; // 7B4 unsigned long weight_item; // 7B8 Weight of one item in gram * 10 @@ -353,8 +371,28 @@ struct SYSCFG { unsigned long energy_frequency_calibration; // 7C8 also used by HX711 to save last weight uint16_t web_refresh; // 7CC char mems[MAX_RULE_MEMS][10]; // 7CE - char rules[MAX_RULE_SETS][MAX_RULE_SIZE]; // 800 uses 512 bytes in v5.12.0m, 3 x 512 bytes in v5.14.0b - // E00 - FFF free locations + char rules[MAX_RULE_SETS][MAX_RULE_SIZE]; // 800 uses 512 bytes in v5.12.0m, 3 x 512 bytes in v5.14.0b + TuyaFnidDpidMap tuya_fnid_map[MAX_TUYA_FUNCTIONS]; // E00 32 bytes + uint16_t ina226_r_shunt[4]; // E20 + uint16_t ina226_i_fs[4]; // E28 + uint16_t tariff[4][2]; // E30 + uint16_t shutter_opentime[MAX_SHUTTERS]; // E40 + uint16_t shutter_closetime[MAX_SHUTTERS]; // E48 + int16_t shuttercoeff[5][MAX_SHUTTERS]; // E50 + uint8_t shutter_invert[MAX_SHUTTERS]; // E78 + uint8_t shutter_set50percent[MAX_SHUTTERS]; // E7C + uint8_t shutter_position[MAX_SHUTTERS]; // E80 + uint8_t shutter_startrelay[MAX_SHUTTERS]; // E84 + uint8_t pcf8574_config[MAX_PCF8574]; // E88 + uint16_t dimmer_hw_min; // E90 + uint16_t dimmer_hw_max; // E92 + uint32_t deepsleep; // E94 + uint16_t energy_power_delta; // E98 + + uint8_t free_e9a[350]; // E9A + + uint32_t cfg_timestamp; // FF8 + uint32_t cfg_crc32; // FFC } Settings; struct RTCRBT { @@ -371,7 +409,13 @@ struct RTCMEM { unsigned long energy_kWhtotal; // 298 unsigned long pulse_counter[MAX_COUNTERS]; // 29C power_t power; // 2AC - uint8_t free_020[60]; // 2B0 + EnergyUsage energy_usage; // 2B0 + unsigned long nextwakeup; // 2C8 + uint8_t free_004[4]; // 2CC + uint32_t ultradeepsleep; // 2D0 + uint16_t deepsleep_slip; // 2D4 + + uint8_t free_022[22]; // 2D6 // 2EC - 2FF free locations } RtcSettings; @@ -390,18 +434,22 @@ struct TIME_T { } RtcTime; struct XDRVMAILBOX { - uint16_t valid; - uint16_t index; - uint16_t data_len; - uint16_t payload16; - int16_t payload; bool grpflg; - uint8_t notused; + bool usridx; + uint16_t command_code; + uint32_t index; + uint32_t data_len; + int32_t payload; char *topic; char *data; + char *command; } XdrvMailbox; +#ifdef USE_SHUTTER +const uint8_t MAX_RULES_FLAG = 10; // Number of bits used in RulesBitfield (tricky I know...) +#else const uint8_t MAX_RULES_FLAG = 8; // Number of bits used in RulesBitfield (tricky I know...) +#endif // USE_SHUTTER typedef union { // Restricted by MISRA-C Rule 18.4 but so useful... uint16_t data; // Allow bit manipulation struct { @@ -413,8 +461,8 @@ typedef union { // Restricted by MISRA-C Rule 18.4 bu uint16_t wifi_connected : 1; uint16_t wifi_disconnected : 1; uint16_t http_init : 1; - uint16_t spare08 : 1; - uint16_t spare09 : 1; + uint16_t shutter_moved : 1; + uint16_t shutter_moving : 1; uint16_t spare10 : 1; uint16_t spare11 : 1; uint16_t spare12 : 1; diff --git a/sonoff/settings.ino b/sonoff/settings.ino index eae6d3817..0a34123df 100644 --- a/sonoff/settings.ino +++ b/sonoff/settings.ino @@ -122,6 +122,15 @@ #ifndef IR_RCV_MIN_UNKNOWN_SIZE #define IR_RCV_MIN_UNKNOWN_SIZE 6 // Set the smallest sized "UNKNOWN" message packets we actually care about (default 6, max 255) #endif +#ifndef ENERGY_OVERTEMP +#define ENERGY_OVERTEMP 90 // Overtemp in Celsius +#endif +#ifndef DEFAULT_DIMMER_MAX +#define DEFAULT_DIMMER_MAX 100 +#endif +#ifndef DEFAULT_DIMMER_MIN +#define DEFAULT_DIMMER_MIN 0 +#endif enum WebColors { COL_TEXT, COL_BACKGROUND, COL_FORM, @@ -174,6 +183,7 @@ void RtcSettingsLoad(void) RtcSettings.valid = RTC_MEM_VALID; RtcSettings.energy_kWhtoday = Settings.energy_kWhtoday; RtcSettings.energy_kWhtotal = Settings.energy_kWhtotal; + RtcSettings.energy_usage = Settings.energy_usage; for (uint32_t i = 0; i < MAX_COUNTERS; i++) { RtcSettings.pulse_counter[i] = Settings.pulse_counter[i]; } @@ -257,143 +267,8 @@ const uint32_t SETTINGS_LOCATION = SPIFFS_END; // No need for SPIFFS as it uses // Version 5.2 allow for more flash space const uint8_t CFG_ROTATES = 8; // Number of flash sectors used (handles uploads) -/*********************************************************************************************\ - * Optional EEPROM support based on EEPROM library and tuned for Tasmota -\*********************************************************************************************/ -//#define USE_EEPROM -#ifdef USE_EEPROM - -uint32_t eeprom_sector = SPIFFS_END; -uint8_t* eeprom_data = 0; -size_t eeprom_size = 0; -bool eeprom_dirty = false; - -void EepromBegin(size_t size) -{ - if (size <= 0) { return; } - if (size > SPI_FLASH_SEC_SIZE - sizeof(Settings) -4) { size = SPI_FLASH_SEC_SIZE - sizeof(Settings) -4; } - size = (size + 3) & (~3); - - // In case begin() is called a 2nd+ time, don't reallocate if size is the same - if (eeprom_data && size != eeprom_size) { - delete[] eeprom_data; - eeprom_data = new uint8_t[size]; - } else if (!eeprom_data) { - eeprom_data = new uint8_t[size]; - } - eeprom_size = size; - - size_t flash_offset = SPI_FLASH_SEC_SIZE - eeprom_size; - uint8_t* flash_buffer; - flash_buffer = new uint8_t[SPI_FLASH_SEC_SIZE]; - noInterrupts(); - spi_flash_read(eeprom_sector * SPI_FLASH_SEC_SIZE, reinterpret_cast(flash_buffer), SPI_FLASH_SEC_SIZE); - interrupts(); - memcpy(eeprom_data, flash_buffer + flash_offset, eeprom_size); - delete[] flash_buffer; - - eeprom_dirty = false; // make sure dirty is cleared in case begin() is called 2nd+ time -} - -size_t EepromLength(void) -{ - return eeprom_size; -} - -uint8_t EepromRead(int const address) -{ - if (address < 0 || (size_t)address >= eeprom_size) { return 0; } - if (!eeprom_data) { return 0; } - - return eeprom_data[address]; -} - -// Prototype needed for Arduino IDE - https://forum.arduino.cc/index.php?topic=406509.0 -template T EepromGet(int const address, T &t); -template T EepromGet(int const address, T &t) -{ - if (address < 0 || address + sizeof(T) > eeprom_size) { return t; } - if (!eeprom_data) { return 0; } - - memcpy((uint8_t*) &t, eeprom_data + address, sizeof(T)); - return t; -} - -void EepromWrite(int const address, uint8_t const value) -{ - if (address < 0 || (size_t)address >= eeprom_size) { return; } - if (!eeprom_data) { return; } - - // Optimise eeprom_dirty. Only flagged if data written is different. - uint8_t* pData = &eeprom_data[address]; - if (*pData != value) { - *pData = value; - eeprom_dirty = true; - } -} - -// Prototype needed for Arduino IDE - https://forum.arduino.cc/index.php?topic=406509.0 -template void EepromPut(int const address, const T &t); -template void EepromPut(int const address, const T &t) -{ - if (address < 0 || address + sizeof(T) > eeprom_size) { return; } - if (!eeprom_data) { return; } - - // Optimise eeprom_dirty. Only flagged if data written is different. - if (memcmp(eeprom_data + address, (const uint8_t*)&t, sizeof(T)) != 0) { - eeprom_dirty = true; - memcpy(eeprom_data + address, (const uint8_t*)&t, sizeof(T)); - } -} - -bool EepromCommit(void) -{ - bool ret = false; - if (!eeprom_size) { return false; } - if (!eeprom_dirty) { return true; } - if (!eeprom_data) { return false; } - - size_t flash_offset = SPI_FLASH_SEC_SIZE - eeprom_size; - uint8_t* flash_buffer; - flash_buffer = new uint8_t[SPI_FLASH_SEC_SIZE]; - noInterrupts(); - spi_flash_read(eeprom_sector * SPI_FLASH_SEC_SIZE, reinterpret_cast(flash_buffer), SPI_FLASH_SEC_SIZE); - memcpy(flash_buffer + flash_offset, eeprom_data, eeprom_size); - if (spi_flash_erase_sector(eeprom_sector) == SPI_FLASH_RESULT_OK) { - if (spi_flash_write(eeprom_sector * SPI_FLASH_SEC_SIZE, reinterpret_cast(flash_buffer), SPI_FLASH_SEC_SIZE) == SPI_FLASH_RESULT_OK) { - eeprom_dirty = false; - ret = true; - } - } - interrupts(); - delete[] flash_buffer; - - return ret; -} - -uint8_t * EepromGetDataPtr() -{ - eeprom_dirty = true; - return &eeprom_data[0]; -} - -void EepromEnd(void) -{ - if (!eeprom_size) { return; } - - EepromCommit(); - if (eeprom_data) { - delete[] eeprom_data; - } - eeprom_data = 0; - eeprom_size = 0; - eeprom_dirty = false; -} -#endif // USE_EEPROM -/********************************************************************************************/ - -uint16_t settings_crc = 0; uint32_t settings_location = SETTINGS_LOCATION; +uint32_t settings_crc32 = 0; uint8_t *settings_buffer = nullptr; /********************************************************************************************/ @@ -437,17 +312,42 @@ bool SettingsBufferAlloc(void) return true; } -uint16_t GetSettingsCrc(void) +uint16_t GetCfgCrc16(uint8_t *bytes, uint32_t size) { uint16_t crc = 0; - uint8_t *bytes = (uint8_t*)&Settings; - for (uint32_t i = 0; i < sizeof(SYSCFG); i++) { + for (uint32_t i = 0; i < size; i++) { if ((i < 14) || (i > 15)) { crc += bytes[i]*(i+1); } // Skip crc } return crc; } +uint16_t GetSettingsCrc(void) +{ + // Fix miscalculation if previous Settings was 3584 and current Settings is 4096 between 0x06060007 and 0x0606000A + uint32_t size = ((Settings.version < 0x06060007) || (Settings.version > 0x0606000A)) ? 3584 : sizeof(SYSCFG); + return GetCfgCrc16((uint8_t*)&Settings, size); +} + +uint32_t GetCfgCrc32(uint8_t *bytes, uint32_t size) +{ + // https://create.stephan-brumme.com/crc32/#bitwise + uint32_t crc = 0; + + while (size--) { + crc ^= *bytes++; + for (uint32_t j = 0; j < 8; j++) { + crc = (crc >> 1) ^ (-int(crc & 1) & 0xEDB88320); + } + } + return ~crc; +} + +uint32_t GetSettingsCrc32(void) +{ + return GetCfgCrc32((uint8_t*)&Settings, sizeof(SYSCFG) -4); // Skip crc32 +} + void SettingsSaveAll(void) { if (Settings.flag.save_state) { @@ -457,12 +357,41 @@ void SettingsSaveAll(void) } XsnsCall(FUNC_SAVE_BEFORE_RESTART); XdrvCall(FUNC_SAVE_BEFORE_RESTART); -#ifdef USE_EEPROM - EepromCommit(); -#endif SettingsSave(0); } +/*********************************************************************************************\ + * Quick power cycle monitoring +\*********************************************************************************************/ + +void UpdateQuickPowerCycle(bool update) +{ + if (Settings.flag3.fast_power_cycle_disable) { return; } + + uint32_t pc_register; + uint32_t pc_location = SETTINGS_LOCATION - CFG_ROTATES; + + ESP.flashRead(pc_location * SPI_FLASH_SEC_SIZE, (uint32*)&pc_register, sizeof(pc_register)); + if (update && ((pc_register & 0xFFFFFFF0) == 0xFFA55AB0)) { + uint32_t counter = ((pc_register & 0xF) << 1) & 0xF; + if (0 == counter) { // 4 power cycles in a row + SettingsErase(2); // Quickly reset all settings including QuickPowerCycle flag + EspRestart(); // And restart + } else { + pc_register = 0xFFA55AB0 | counter; + ESP.flashWrite(pc_location * SPI_FLASH_SEC_SIZE, (uint32*)&pc_register, sizeof(pc_register)); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("QPC: Flag %02X"), counter); + } + } + else if (pc_register != 0xFFA55ABF) { + pc_register = 0xFFA55ABF; + // Assume flash is default all ones and setting a bit to zero does not need an erase + ESP.flashEraseSector(pc_location); + ESP.flashWrite(pc_location * SPI_FLASH_SEC_SIZE, (uint32*)&pc_register, sizeof(pc_register)); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("QPC: Reset")); + } +} + /*********************************************************************************************\ * Config Save - Save parameters to Flash ONLY if any parameter has changed \*********************************************************************************************/ @@ -483,7 +412,7 @@ void SettingsSave(uint8_t rotate) * stop_flash_rotate 1 = Allow only eeprom flash slot use (SetOption12 1) */ #ifndef FIRMWARE_MINIMAL - if ((GetSettingsCrc() != settings_crc) || rotate) { + if ((GetSettingsCrc32() != settings_crc32) || rotate) { if (1 == rotate) { // Use eeprom flash slot only and disable flash rotate from now on (upgrade) stop_flash_rotate = 1; } @@ -498,32 +427,19 @@ void SettingsSave(uint8_t rotate) settings_location = SETTINGS_LOCATION; } } - Settings.save_flag++; - Settings.cfg_size = sizeof(SYSCFG); - Settings.cfg_crc = GetSettingsCrc(); -#ifdef USE_EEPROM - if (SPIFFS_END == settings_location) { - uint8_t* flash_buffer; - flash_buffer = new uint8_t[SPI_FLASH_SEC_SIZE]; - if (eeprom_data && eeprom_size) { - size_t flash_offset = SPI_FLASH_SEC_SIZE - eeprom_size; - memcpy(flash_buffer + flash_offset, eeprom_data, eeprom_size); // Write dirty EEPROM data - } else { - ESP.flashRead(settings_location * SPI_FLASH_SEC_SIZE, (uint32*)flash_buffer, SPI_FLASH_SEC_SIZE); // Read EEPROM area - } - memcpy(flash_buffer, &Settings, sizeof(Settings)); - ESP.flashEraseSector(settings_location); - ESP.flashWrite(settings_location * SPI_FLASH_SEC_SIZE, (uint32*)flash_buffer, SPI_FLASH_SEC_SIZE); - delete[] flash_buffer; + Settings.save_flag++; + if (UtcTime() > START_VALID_TIME) { + Settings.cfg_timestamp = UtcTime(); } else { - ESP.flashEraseSector(settings_location); - ESP.flashWrite(settings_location * SPI_FLASH_SEC_SIZE, (uint32*)&Settings, sizeof(SYSCFG)); + Settings.cfg_timestamp++; } -#else + Settings.cfg_size = sizeof(SYSCFG); + Settings.cfg_crc = GetSettingsCrc(); // Keep for backward compatibility in case of fall-back just after upgrade + Settings.cfg_crc32 = GetSettingsCrc32(); + ESP.flashEraseSector(settings_location); ESP.flashWrite(settings_location * SPI_FLASH_SEC_SIZE, (uint32*)&Settings, sizeof(SYSCFG)); -#endif // USE_EEPROM if (!stop_flash_rotate && rotate) { for (uint32_t i = 1; i < CFG_ROTATES; i++) { @@ -534,7 +450,7 @@ void SettingsSave(uint8_t rotate) AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_CONFIG D_SAVED_TO_FLASH_AT " %X, " D_COUNT " %d, " D_BYTES " %d"), settings_location, Settings.save_flag, sizeof(SYSCFG)); - settings_crc = Settings.cfg_crc; + settings_crc32 = Settings.cfg_crc32; } #endif // FIRMWARE_MINIMAL RtcSettingsSave(); @@ -559,7 +475,10 @@ void SettingsLoad(void) bool valid = false; if (Settings.version > 0x06000000) { - bool almost_valid = (Settings.cfg_crc == GetSettingsCrc()); + bool almost_valid = (Settings.cfg_crc32 == GetSettingsCrc32()); + if (Settings.version < 0x0606000B) { + almost_valid = (Settings.cfg_crc == GetSettingsCrc()); + } // Sometimes CRC on pages below FB, overwritten by OTA, is fine but Settings are still invalid. So check cfg_holder too if (almost_valid && (0 == cfg_holder)) { cfg_holder = Settings.cfg_holder; } // At FB always active cfg_holder valid = (cfg_holder == Settings.cfg_holder); @@ -588,7 +507,7 @@ void SettingsLoad(void) if (!settings_location || (Settings.cfg_holder != (uint16_t)CFG_HOLDER)) { // Init defaults if cfg_holder differs from user settings in my_user_config.h SettingsDefault(); } - settings_crc = GetSettingsCrc(); + settings_crc32 = GetSettingsCrc32(); #endif // FIRMWARE_MINIMAL RtcSettingsLoad(); @@ -599,6 +518,7 @@ void SettingsErase(uint8_t type) /* 0 = Erase from program end until end of physical flash 1 = Erase SDK parameter area at end of linker memory model (0x0FDxxx - 0x0FFFFF) solving possible wifi errors + 2 = Erase Tasmota settings */ #ifndef FIRMWARE_MINIMAL @@ -610,6 +530,10 @@ void SettingsErase(uint8_t type) _sectorStart = SETTINGS_LOCATION +2; // SDK parameter area above EEPROM area (0x0FDxxx - 0x0FFFFF) _sectorEnd = SETTINGS_LOCATION +5; } + else if (2 == type) { + _sectorStart = SETTINGS_LOCATION - CFG_ROTATES; // Tasmota parameter area (0x0F4xxx - 0x0FBFFF) + _sectorEnd = SETTINGS_LOCATION +1; + } bool _serialoutput = (LOG_LEVEL_DEBUG_MORE <= seriallog_level); @@ -682,6 +606,7 @@ void SettingsDefaultSet2(void) // Settings.flag.value_units = 0; // Settings.flag.stop_flash_rotate = 0; Settings.save_data = SAVE_DATA; + Settings.param[P_BACKLOG_DELAY] = MIN_BACKLOG_DELAY; Settings.param[P_BOOT_LOOP_OFFSET] = BOOT_LOOP_OFFSET; Settings.param[P_RGB_REMAP] = RGB_REMAP_RGBW; Settings.sleep = APP_SLEEP; @@ -713,8 +638,8 @@ void SettingsDefaultSet2(void) // for (uint32_t i = 1; i < MAX_PULSETIMERS; i++) { Settings.pulse_timer[i] = 0; } // Serial - Settings.baudrate = APP_BAUDRATE / 1200; - Settings.sbaudrate = SOFT_BAUDRATE / 1200; + Settings.baudrate = APP_BAUDRATE / 300; + Settings.sbaudrate = SOFT_BAUDRATE / 300; Settings.serial_delimiter = 0xff; Settings.seriallog_level = SERIAL_LOG_LEVEL; @@ -794,6 +719,7 @@ void SettingsDefaultSet2(void) Settings.mqtt_fingerprint[1][i] = strtol(p, &p, 16); } Settings.tele_period = TELE_PERIOD; + Settings.mqttlog_level = MQTT_LOG_LEVEL; // Energy Settings.flag2.current_resolution = 3; @@ -824,6 +750,9 @@ void SettingsDefaultSet2(void) // Settings.energy_max_energy_start = 0; // MaxEnergyStart // Settings.energy_kWhtotal = 0; RtcSettings.energy_kWhtotal = 0; +// memset((char*)&Settings.energy_usage, 0x00, sizeof(Settings.energy_usage)); + memset((char*)&RtcSettings.energy_usage, 0x00, sizeof(RtcSettings.energy_usage)); + Settings.param[P_OVER_TEMP] = ENERGY_OVERTEMP; // IRRemote Settings.param[P_IR_UNKNOW_THRESHOLD] = IR_RCV_MIN_UNKNOWN_SIZE; @@ -887,6 +816,9 @@ void SettingsDefaultSet2(void) // Settings.light_rotation = 0; SettingsDefaultSet_5_8_1(); // Clock color + Settings.dimmer_hw_max = DEFAULT_DIMMER_MAX; + Settings.dimmer_hw_min = DEFAULT_DIMMER_MIN; + // Display SettingsDefaultSet_5_10_1(); // Display settings @@ -919,7 +851,7 @@ void SettingsDefaultSet2(void) Settings.rgbwwTable[j] = 255; } - Settings.novasds_period = WORKING_PERIOD; + Settings.novasds_startingoffset = STARTING_OFFSET; SettingsDefaultWebColor(); @@ -1072,10 +1004,10 @@ void SettingsDelta(void) } } if (Settings.version < 0x050C0007) { - Settings.baudrate = APP_BAUDRATE / 1200; + Settings.baudrate = APP_BAUDRATE / 300; } if (Settings.version < 0x050C0008) { - Settings.sbaudrate = SOFT_BAUDRATE / 1200; + Settings.sbaudrate = SOFT_BAUDRATE / 300; Settings.serial_delimiter = 0xff; } if (Settings.version < 0x050C000A) { @@ -1172,7 +1104,7 @@ void SettingsDelta(void) Settings.param[P_RGB_REMAP] = RGB_REMAP_RGBW; } if (Settings.version < 0x06050003) { - Settings.novasds_period = WORKING_PERIOD; + Settings.novasds_startingoffset = STARTING_OFFSET; } if (Settings.version < 0x06050006) { SettingsDefaultWebColor(); @@ -1186,6 +1118,106 @@ void SettingsDelta(void) if (Settings.version < 0x0605000D) { Settings.param[P_IR_UNKNOW_THRESHOLD] = IR_RCV_MIN_UNKNOWN_SIZE; } + if (Settings.version < 0x06060001) { + Settings.param[P_OVER_TEMP] = ENERGY_OVERTEMP; + } + if (Settings.version < 0x06060007) { + memset((char*)&Settings +0xE00, 0x00, sizeof(SYSCFG) -0xE00); + } + if (Settings.version < 0x06060008) { + // Move current tuya dimmer range to the new param. + if (Settings.flag3.ex_tuya_dimmer_range_255) { + Settings.param[P_ex_DIMMER_MAX] = 100; + } else { + Settings.param[P_ex_DIMMER_MAX] = 255; + } + } + if (Settings.version < 0x06060009) { + Settings.baudrate = Settings.ex_baudrate * 4; + Settings.sbaudrate = Settings.ex_sbaudrate * 4; + } + + if (Settings.version < 0x0606000A) { + uint8_t tuyaindex = 0; + if (Settings.param[P_BACKLOG_DELAY] > 0) { // ex SetOption34 + Settings.tuya_fnid_map[tuyaindex].fnid = 21; // TUYA_MCU_FUNC_DIMMER - Move Tuya Dimmer Id to Map + Settings.tuya_fnid_map[tuyaindex].dpid = Settings.param[P_BACKLOG_DELAY]; + tuyaindex++; + } else if (Settings.flag3.fast_power_cycle_disable == 1) { // ex SetOption65 + Settings.tuya_fnid_map[tuyaindex].fnid = 11; // TUYA_MCU_FUNC_REL1 - Create FnID for Switches + Settings.tuya_fnid_map[tuyaindex].dpid = 1; + tuyaindex++; + } + if (Settings.param[P_ex_TUYA_RELAYS] > 0) { + for (uint8_t i = 0 ; i < Settings.param[P_ex_TUYA_RELAYS]; i++) { // ex SetOption41 + Settings.tuya_fnid_map[tuyaindex].fnid = 12 + i; // TUYA_MCU_FUNC_REL2 - Create FnID for Switches + Settings.tuya_fnid_map[tuyaindex].dpid = i + 2; + tuyaindex++; + } + } + if (Settings.param[P_ex_TUYA_POWER_ID] > 0) { // ex SetOption46 + Settings.tuya_fnid_map[tuyaindex].fnid = 31; // TUYA_MCU_FUNC_POWER - Move Tuya Power Id to Map + Settings.tuya_fnid_map[tuyaindex].dpid = Settings.param[P_ex_TUYA_POWER_ID]; + tuyaindex++; + } + if (Settings.param[P_ex_TUYA_VOLTAGE_ID] > 0) { // ex SetOption44 + Settings.tuya_fnid_map[tuyaindex].fnid = 33; // TUYA_MCU_FUNC_VOLTAGE - Move Tuya Voltage Id to Map + Settings.tuya_fnid_map[tuyaindex].dpid = Settings.param[P_ex_TUYA_VOLTAGE_ID]; + tuyaindex++; + } + if (Settings.param[P_ex_TUYA_CURRENT_ID] > 0) { // ex SetOption45 + Settings.tuya_fnid_map[tuyaindex].fnid = 32; // TUYA_MCU_FUNC_CURRENT - Move Tuya Current Id to Map + Settings.tuya_fnid_map[tuyaindex].dpid = Settings.param[P_ex_TUYA_CURRENT_ID]; + tuyaindex++; + } + } + if (Settings.version < 0x0606000C) { + memset(&Settings.register8, 0x00, sizeof(Settings.register8)); + } + if (Settings.version < 0x0606000F) { + Settings.shutter_accuracy = 0; + Settings.mqttlog_level = MQTT_LOG_LEVEL; + } + if (Settings.version < 0x06060011) { + Settings.param[P_BACKLOG_DELAY] = MIN_BACKLOG_DELAY; + } + if (Settings.version < 0x06060012) { + Settings.dimmer_hw_min = DEFAULT_DIMMER_MIN; + Settings.dimmer_hw_max = DEFAULT_DIMMER_MAX; + if (TUYA_DIMMER == Settings.module) { + if (Settings.flag3.ex_tuya_dimmer_min_limit) { + Settings.dimmer_hw_min = 25; + } else { + Settings.dimmer_hw_min = 1; + } + Settings.dimmer_hw_max = Settings.param[P_ex_DIMMER_MAX]; + } + else if (PS_16_DZ == Settings.module) { + Settings.dimmer_hw_min = 10; + Settings.dimmer_hw_max = Settings.param[P_ex_DIMMER_MAX]; + } + } + if (Settings.version < 0x06060014) { + // Clear unused parameters for future use + Settings.flag3.ex_tuya_dimmer_range_255 = 0; + Settings.flag3.ex_tuya_dimmer_min_limit = 0; + Settings.param[P_ex_TUYA_RELAYS] = 0; + Settings.param[P_ex_DIMMER_MAX] = 0; + Settings.param[P_ex_TUYA_VOLTAGE_ID] = 0; + Settings.param[P_ex_TUYA_CURRENT_ID] = 0; + Settings.param[P_ex_TUYA_POWER_ID] = 0; + Settings.ex_baudrate = 0; + Settings.ex_sbaudrate = 0; + + Settings.flag3.fast_power_cycle_disable = 0; + Settings.energy_power_delta = Settings.ex_energy_power_delta; + Settings.ex_energy_power_delta = 0; + } + if (Settings.version < 0x06060015) { + if ((EX_WIFI_SMARTCONFIG == Settings.sta_config) || (EX_WIFI_WPSCONFIG == Settings.sta_config)) { + Settings.sta_config = WIFI_MANAGER; + } + } Settings.version = VERSION; SettingsSave(1); diff --git a/sonoff/sonoff.h b/sonoff/sonoff.h index 8d23d1771..40b3dcb98 100644 --- a/sonoff/sonoff.h +++ b/sonoff/sonoff.h @@ -24,7 +24,7 @@ * Performance ROM (PROGMEM) vs RAM (RODATA) \*********************************************************************************************/ -//#define XFUNC_PTR_IN_ROM // Enable for keeping tables in ROM (PROGMEM) which seem to have access issues on some flash types +#define XFUNC_PTR_IN_ROM // Enable for keeping tables in ROM (PROGMEM) which seem to have access issues on some flash types /*********************************************************************************************\ * Default sensor states @@ -67,11 +67,13 @@ const uint8_t MAX_XNRG_DRIVERS = 32; // Max number of allowed energy driv const uint8_t MAX_XDSP_DRIVERS = 32; // Max number of allowed display drivers const uint8_t MAX_XDRV_DRIVERS = 96; // Max number of allowed driver drivers const uint8_t MAX_XSNS_DRIVERS = 96; // Max number of allowed sensor drivers +const uint8_t MAX_SHUTTERS = 4; // Max number of shutters +const uint8_t MAX_PCF8574 = 8; // Max number of PCF8574 devices const uint8_t MAX_RULE_MEMS = 5; // Max number of saved vars const uint8_t MAX_RULE_SETS = 3; // Max number of rule sets of size 512 characters const uint16_t MAX_RULE_SIZE = 512; // Max number of characters in rules -const uint8_t MAX_FAN_SPEED = 4; // Max number of iFan02 fan speeds (0 .. 3) +const uint8_t MAX_HUE_DEVICES = 15; // Max number of Philips Hue device per emulation const char MQTT_TOKEN_PREFIX[] PROGMEM = "%prefix%"; // To be substituted by mqtt_prefix[x] const char MQTT_TOKEN_TOPIC[] PROGMEM = "%topic%"; // To be substituted by mqtt_topic, mqtt_grptopic, mqtt_buttontopic, mqtt_switchtopic @@ -108,11 +110,13 @@ const uint8_t MAX_POWER_RETRY = 5; // Retry count allowing agreed power const uint8_t STATES = 20; // Number of states per second using 50 mSec interval const uint8_t IMMINENT_RESET_FACTOR = 10; // Factor to extent button hold time for imminent Reset to default 40 seconds using KEY_HOLD_TIME of 40 const uint32_t BOOT_LOOP_TIME = 10; // Number of seconds to stop detecting boot loops +const uint32_t POWER_CYCLE_TIME = 8; // Number of seconds to reset power cycle boot loops const uint16_t SYSLOG_TIMER = 600; // Seconds to restore syslog_level const uint16_t SERIALLOG_TIMER = 600; // Seconds to disable SerialLog const uint8_t OTA_ATTEMPTS = 5; // Number of times to try fetching the new firmware const uint16_t INPUT_BUFFER_SIZE = 520; // Max number of characters in (serial and http) command buffer +const uint16_t FLOATSZ = 16; // Max number of characters in float result from dtostrfd (max 32) const uint16_t CMDSZ = 24; // Max number of characters in command const uint16_t TOPSZ = 100; // Max number of characters in topic string const uint16_t LOGSZ = 520; // Max number of characters in log @@ -121,13 +125,16 @@ const uint16_t MIN_MESSZ = 893; // Min number of characters in MQTT const uint8_t SENSOR_MAX_MISS = 5; // Max number of missed sensor reads before deciding it's offline const uint8_t MAX_BACKLOG = 30; // Max number of commands in backlog -const uint32_t MIN_BACKLOG_DELAY = 2; // Minimal backlog delay in 0.1 seconds +const uint32_t MIN_BACKLOG_DELAY = 200; // Minimal backlog delay in mSeconds const uint32_t SOFT_BAUDRATE = 9600; // Default software serial baudrate const uint32_t APP_BAUDRATE = 115200; // Default serial baudrate const uint32_t SERIAL_POLLING = 100; // Serial receive polling in ms +const uint32_t ZIGBEE_POLLING = 100; // Serial receive polling in ms const uint8_t MAX_STATUS = 11; // Max number of status lines +const uint32_t START_VALID_TIME = 1451602800; // Time is synced and after 2016-01-01 + const uint32_t DRIVER_BOOT_DELAY = 1; // Number of milliseconds to retard driver cycles during boot-up time to reduce overall CPU load whilst Wifi is connecting const uint32_t LOOP_SLEEP_DELAY = 50; // Lowest number of milliseconds to go through the main loop using delay when needed @@ -138,8 +145,6 @@ const uint32_t LOOP_SLEEP_DELAY = 50; // Lowest number of milliseconds to #define MAX_RULE_TIMERS 8 // Max number of rule timers (4 bytes / timer) #define MAX_RULE_VARS 5 // Max number of rule variables (10 bytes / variable) -#define NO_EXTRA_4K_HEAP // Allocate 4k heap for WPS in ESP8166/Arduino core v2.4.2 (was always allocated in previous versions) - /* // Removed from esp8266 core since 20171105 #define min(a,b) ((a)<(b)?(a):(b)) @@ -162,8 +167,6 @@ const uint32_t LOOP_SLEEP_DELAY = 50; // Lowest number of milliseconds to #define NEO_RGBW 5 // Neopixel RGBW leds #define NEO_GRBW 6 // Neopixel GRBW leds -#define LT_SM16716 16 // Lights that use SM16716 will have this bit set in light_type - #define RGB_REMAP_RGBW 0 #define RGB_REMAP_RBGW 6 #define RGB_REMAP_GRBW 24 @@ -171,6 +174,13 @@ const uint32_t LOOP_SLEEP_DELAY = 50; // Lowest number of milliseconds to #define RGB_REMAP_BRGW 48 #define RGB_REMAP_BGRW 54 +#define NEO_HW_WS2812 0 // NeoPixelBus hardware WS2812 +#define NEO_HW_WS2812X 1 // NeoPixelBus hardware WS2812x like WS2812b +#define NEO_HW_WS2813 1 // NeoPixelBus hardware WS2813 +#define NEO_HW_SK6812 2 // NeoPixelBus hardware SK6812 +#define NEO_HW_LC8812 2 // NeoPixelBus hardware LC8812 +#define NEO_HW_APA106 3 // NeoPixelBus hardware APA106 + #define MQTT_PUBSUBCLIENT 1 // Mqtt PubSubClient library #define MQTT_TASMOTAMQTT 2 // Mqtt TasmotaMqtt library based on esp-mqtt-arduino - soon obsolete #define MQTT_ESPMQTTARDUINO 3 // Mqtt esp-mqtt-arduino library by Ingo Randolf - obsolete but define is present for debugging purposes @@ -212,7 +222,7 @@ enum GetDateAndTimeOptions { DT_LOCAL, DT_UTC, DT_RESTART, DT_ENERGY }; enum LoggingLevels {LOG_LEVEL_NONE, LOG_LEVEL_ERROR, LOG_LEVEL_INFO, LOG_LEVEL_DEBUG, LOG_LEVEL_DEBUG_MORE, LOG_LEVEL_ALL}; -enum WifiConfigOptions {WIFI_RESTART, WIFI_SMARTCONFIG, WIFI_MANAGER, WIFI_WPSCONFIG, WIFI_RETRY, WIFI_WAIT, WIFI_SERIAL, WIFI_MANAGER_RESET_ONLY, MAX_WIFI_OPTION}; +enum WifiConfigOptions {WIFI_RESTART, EX_WIFI_SMARTCONFIG, WIFI_MANAGER, EX_WIFI_WPSCONFIG, WIFI_RETRY, WIFI_WAIT, WIFI_SERIAL, WIFI_MANAGER_RESET_ONLY, MAX_WIFI_OPTION}; enum SwitchModeOptions {TOGGLE, FOLLOW, FOLLOW_INV, PUSHBUTTON, PUSHBUTTON_INV, PUSHBUTTONHOLD, PUSHBUTTONHOLD_INV, PUSHBUTTON_TOGGLE, MAX_SWITCH_OPTION}; @@ -222,7 +232,11 @@ enum EmulationOptions {EMUL_NONE, EMUL_WEMO, EMUL_HUE, EMUL_MAX}; enum TopicOptions { CMND, STAT, TELE, nu1, RESULT_OR_CMND, RESULT_OR_STAT, RESULT_OR_TELE }; -enum ExecuteCommandPowerOptions { POWER_OFF, POWER_ON, POWER_TOGGLE, POWER_BLINK, POWER_BLINK_STOP, power_nu1, POWER_OFF_NO_STATE, POWER_ON_NO_STATE, power_nu2, POWER_SHOW_STATE }; +enum ExecuteCommandPowerOptions { POWER_OFF, POWER_ON, POWER_TOGGLE, POWER_BLINK, POWER_BLINK_STOP, + POWER_OFF_NO_STATE = 8, POWER_ON_NO_STATE, POWER_TOGGLE_NO_STATE, + POWER_SHOW_STATE = 16 }; +enum SendKeyPowerOptions { POWER_HOLD = 3, CLEAR_RETAIN = 9 }; +enum SendKeyOptions { KEY_BUTTON, KEY_SWITCH }; enum PowerOnStateOptions { POWER_ALL_OFF, POWER_ALL_ON, POWER_ALL_SAVED_TOGGLE, POWER_ALL_SAVED, POWER_ALL_ALWAYS_ON, POWER_ALL_OFF_PULSETIME_ON }; @@ -230,43 +244,45 @@ enum ButtonStates { PRESSED, NOT_PRESSED }; enum Shortcuts { SC_CLEAR, SC_DEFAULT, SC_USER }; -enum SettingsParmaIndex {P_HOLD_TIME, P_MAX_POWER_RETRY, P_TUYA_DIMMER_ID, P_MDNS_DELAYED_START, P_BOOT_LOOP_OFFSET, P_RGB_REMAP, P_IR_UNKNOW_THRESHOLD, P_CSE7766_INVALID_POWER, P_HOLD_IGNORE, P_MAX_PARAM8}; // Max is PARAM8_SIZE (18) - SetOption32 until SetOption49 +enum SettingsParamIndex { P_HOLD_TIME, P_MAX_POWER_RETRY, P_BACKLOG_DELAY, P_MDNS_DELAYED_START, P_BOOT_LOOP_OFFSET, P_RGB_REMAP, P_IR_UNKNOW_THRESHOLD, // SetOption32 .. SetOption38 + P_CSE7766_INVALID_POWER, P_HOLD_IGNORE, P_ex_TUYA_RELAYS, P_OVER_TEMP, // SetOption39 .. SetOption42 + P_ex_DIMMER_MAX, P_ex_TUYA_VOLTAGE_ID, P_ex_TUYA_CURRENT_ID, P_ex_TUYA_POWER_ID, // SetOption43 .. SetOption46 + P_ex_ENERGY_TARIFF1, P_ex_ENERGY_TARIFF2, // SetOption47 .. SetOption48 + P_MAX_PARAM8 }; // Max is PARAM8_SIZE (18) - SetOption32 until SetOption49 -enum DomoticzSensors {DZ_TEMP, DZ_TEMP_HUM, DZ_TEMP_HUM_BARO, DZ_POWER_ENERGY, DZ_ILLUMINANCE, DZ_COUNT, DZ_VOLTAGE, DZ_CURRENT, DZ_AIRQUALITY, DZ_MAX_SENSORS}; +enum SettingsRegister8 { R8_SPARE00, R8_SPARE01, R8_SPARE02, R8_SPARE03, + R8_SPARE04, R8_SPARE05, R8_SPARE06, R8_SPARE07, + R8_SPARE08, R8_SPARE09, R8_SPARE10, R8_SPARE11, + R8_SPARE12, R8_SPARE13, R8_SPARE14, R8_SPARE15 }; // Max size is 16 (Settings.register8[]) + +enum DomoticzSensors {DZ_TEMP, DZ_TEMP_HUM, DZ_TEMP_HUM_BARO, DZ_POWER_ENERGY, DZ_ILLUMINANCE, DZ_COUNT, DZ_VOLTAGE, DZ_CURRENT, + DZ_AIRQUALITY, DZ_P1_SMART_METER, DZ_SHUTTER, DZ_MAX_SENSORS}; enum Ws2812ClockIndex { WS_SECOND, WS_MINUTE, WS_HOUR, WS_MARKER }; enum Ws2812Color { WS_RED, WS_GREEN, WS_BLUE }; enum LightSubtypes { LST_NONE, LST_SINGLE, LST_COLDWARM, LST_RGB, LST_RGBW, LST_RGBWC, LST_MAX=5 }; // Do not insert new fields enum LightTypes { LT_BASIC, LT_PWM1, LT_PWM2, LT_PWM3, LT_PWM4, LT_PWM5, LT_PWM6, LT_PWM7, - LT_NU8, LT_SERIAL1, LT_SERIAL2, LT_WS2812, LT_RGBW, LT_RGBWC, LT_NU14, LT_NU15 }; // Do not insert new fields + LT_NU8, LT_SERIAL1, LT_SERIAL2, LT_RGB, LT_RGBW, LT_RGBWC, LT_NU14, LT_NU15 }; // Do not insert new fields -enum LightSchemes {LS_POWER, LS_WAKEUP, LS_CYCLEUP, LS_CYCLEDN, LS_RANDOM, LS_MAX}; - -enum XsnsFunctions {FUNC_SETTINGS_OVERRIDE, FUNC_MODULE_INIT, FUNC_PRE_INIT, FUNC_INIT, - FUNC_LOOP, FUNC_EVERY_50_MSECOND, FUNC_EVERY_100_MSECOND, FUNC_EVERY_200_MSECOND, FUNC_EVERY_250_MSECOND, FUNC_EVERY_SECOND, +enum XsnsFunctions {FUNC_SETTINGS_OVERRIDE, FUNC_PIN_STATE, FUNC_MODULE_INIT, FUNC_PRE_INIT, FUNC_INIT, + FUNC_LOOP, FUNC_EVERY_50_MSECOND, FUNC_EVERY_100_MSECOND, FUNC_EVERY_200_MSECOND, FUNC_EVERY_250_MSECOND, FUNC_EVERY_300_MSECOND, FUNC_EVERY_SECOND, FUNC_SAVE_AT_MIDNIGHT, FUNC_SAVE_BEFORE_RESTART, - FUNC_PREP_BEFORE_TELEPERIOD, FUNC_JSON_APPEND, FUNC_WEB_SENSOR, FUNC_COMMAND, FUNC_COMMAND_SENSOR, FUNC_COMMAND_DRIVER, + FUNC_PREP_BEFORE_TELEPERIOD, FUNC_AFTER_TELEPERIOD, FUNC_JSON_APPEND, FUNC_WEB_SENSOR, FUNC_COMMAND, FUNC_COMMAND_SENSOR, FUNC_COMMAND_DRIVER, FUNC_MQTT_SUBSCRIBE, FUNC_MQTT_INIT, FUNC_MQTT_DATA, FUNC_SET_POWER, FUNC_SET_DEVICE_POWER, FUNC_SHOW_SENSOR, - FUNC_ENERGY_EVERY_SECOND, + FUNC_ENERGY_EVERY_SECOND, FUNC_ENERGY_RESET, FUNC_RULES_PROCESS, FUNC_SERIAL, FUNC_FREE_MEM, FUNC_BUTTON_PRESSED, - FUNC_WEB_ADD_BUTTON, FUNC_WEB_ADD_MAIN_BUTTON, FUNC_WEB_ADD_HANDLER, FUNC_SET_CHANNELS}; + FUNC_WEB_ADD_BUTTON, FUNC_WEB_ADD_MAIN_BUTTON, FUNC_WEB_ADD_HANDLER, FUNC_SET_CHANNELS, FUNC_SET_SCHEME}; + +enum AddressConfigSteps { ADDR_IDLE, ADDR_RECEIVE, ADDR_SEND }; enum CommandSource { SRC_IGNORE, SRC_MQTT, SRC_RESTART, SRC_BUTTON, SRC_SWITCH, SRC_BACKLOG, SRC_SERIAL, SRC_WEBGUI, SRC_WEBCOMMAND, SRC_WEBCONSOLE, SRC_PULSETIMER, - SRC_TIMER, SRC_RULE, SRC_MAXPOWER, SRC_MAXENERGY, SRC_OVERTEMP, SRC_LIGHT, SRC_KNX, SRC_DISPLAY, SRC_WEMO, SRC_HUE, SRC_RETRY, SRC_MAX }; -const char kCommandSource[] PROGMEM = "I|MQTT|Restart|Button|Switch|Backlog|Serial|WebGui|WebCommand|WebConsole|PulseTimer|Timer|Rule|MaxPower|MaxEnergy|Overtemp|Light|Knx|Display|Wemo|Hue|Retry"; + SRC_TIMER, SRC_RULE, SRC_MAXPOWER, SRC_MAXENERGY, SRC_OVERTEMP, SRC_LIGHT, SRC_KNX, SRC_DISPLAY, SRC_WEMO, SRC_HUE, SRC_RETRY, SRC_REMOTE, SRC_SHUTTER, + SRC_MAX }; +const char kCommandSource[] PROGMEM = "I|MQTT|Restart|Button|Switch|Backlog|Serial|WebGui|WebCommand|WebConsole|PulseTimer|" + "Timer|Rule|MaxPower|MaxEnergy|Overtemp|Light|Knx|Display|Wemo|Hue|Retry|Remote|Shutter"; const uint8_t kDefaultRfCode[9] PROGMEM = { 0x21, 0x16, 0x01, 0x0E, 0x03, 0x48, 0x2E, 0x1A, 0x00 }; -const uint8_t kIFan02Speed[MAX_FAN_SPEED][3] = {{6,6,6}, {7,6,6}, {7,7,6}, {7,6,7}}; // Do not use PROGMEM as it fails - -/*********************************************************************************************\ - * Extern global variables -\*********************************************************************************************/ - -extern uint8_t light_device; // Light device number -extern uint8_t light_power; // Light power -extern uint8_t rotary_changed; // Rotary switch changed - #endif // _SONOFF_H_ diff --git a/sonoff/sonoff.ino b/sonoff/sonoff.ino index 5a26a86c6..18afb76c7 100755 --- a/sonoff/sonoff.ino +++ b/sonoff/sonoff.ino @@ -30,9 +30,6 @@ #include "sonoff_version.h" // Sonoff-Tasmota version information #include "sonoff.h" // Enumeration used in my_user_config.h #include "my_user_config.h" // Fixed user configurable options -#ifdef USE_CONFIG_OVERRIDE - #include "user_config_override.h" // Configuration overrides for my_user_config.h -#endif #ifdef USE_MQTT_TLS #include // we need to include before "sonoff_post.h" to take precedence over the BearSSL version in Arduino #endif // USE_MQTT_TLS @@ -72,28 +69,9 @@ // Structs #include "settings.h" -enum TasmotaCommands { - CMND_BACKLOG, CMND_DELAY, CMND_POWER, CMND_FANSPEED, CMND_STATUS, CMND_STATE, CMND_POWERONSTATE, CMND_PULSETIME, - CMND_BLINKTIME, CMND_BLINKCOUNT, CMND_SENSOR, CMND_SAVEDATA, CMND_SETOPTION, CMND_TEMPERATURE_RESOLUTION, CMND_HUMIDITY_RESOLUTION, - CMND_PRESSURE_RESOLUTION, CMND_POWER_RESOLUTION, CMND_VOLTAGE_RESOLUTION, CMND_FREQUENCY_RESOLUTION, CMND_CURRENT_RESOLUTION, CMND_ENERGY_RESOLUTION, CMND_WEIGHT_RESOLUTION, - CMND_MODULE, CMND_MODULES, CMND_ADC, CMND_ADCS, CMND_GPIO, CMND_GPIOS, CMND_PWM, CMND_PWMFREQUENCY, CMND_PWMRANGE, CMND_COUNTER, CMND_COUNTERTYPE, - CMND_COUNTERDEBOUNCE, CMND_BUTTONDEBOUNCE, CMND_SWITCHDEBOUNCE, CMND_SLEEP, CMND_UPGRADE, CMND_UPLOAD, CMND_OTAURL, CMND_SERIALLOG, CMND_SYSLOG, - CMND_LOGHOST, CMND_LOGPORT, CMND_IPADDRESS, CMND_NTPSERVER, CMND_AP, CMND_SSID, CMND_PASSWORD, CMND_HOSTNAME, - CMND_WIFICONFIG, CMND_FRIENDLYNAME, CMND_SWITCHMODE, CMND_INTERLOCK, CMND_TEMPLATE, - CMND_TELEPERIOD, CMND_RESTART, CMND_RESET, CMND_TIMEZONE, CMND_TIMESTD, CMND_TIMEDST, CMND_ALTITUDE, CMND_LEDPOWER, CMND_LEDSTATE, CMND_LEDMASK, - CMND_I2CSCAN, CMND_SERIALSEND, CMND_BAUDRATE, CMND_SERIALDELIMITER, CMND_DRIVER }; -const char kTasmotaCommands[] PROGMEM = - D_CMND_BACKLOG "|" D_CMND_DELAY "|" D_CMND_POWER "|" D_CMND_FANSPEED "|" D_CMND_STATUS "|" D_CMND_STATE "|" D_CMND_POWERONSTATE "|" D_CMND_PULSETIME "|" - D_CMND_BLINKTIME "|" D_CMND_BLINKCOUNT "|" D_CMND_SENSOR "|" D_CMND_SAVEDATA "|" D_CMND_SETOPTION "|" D_CMND_TEMPERATURE_RESOLUTION "|" D_CMND_HUMIDITY_RESOLUTION "|" - D_CMND_PRESSURE_RESOLUTION "|" D_CMND_POWER_RESOLUTION "|" D_CMND_VOLTAGE_RESOLUTION "|" D_CMND_FREQUENCY_RESOLUTION "|" D_CMND_CURRENT_RESOLUTION "|" D_CMND_ENERGY_RESOLUTION "|" D_CMND_WEIGHT_RESOLUTION "|" - D_CMND_MODULE "|" D_CMND_MODULES "|" D_CMND_ADC "|" D_CMND_ADCS "|" D_CMND_GPIO "|" D_CMND_GPIOS "|" D_CMND_PWM "|" D_CMND_PWMFREQUENCY "|" D_CMND_PWMRANGE "|" D_CMND_COUNTER "|" D_CMND_COUNTERTYPE "|" - D_CMND_COUNTERDEBOUNCE "|" D_CMND_BUTTONDEBOUNCE "|" D_CMND_SWITCHDEBOUNCE "|" D_CMND_SLEEP "|" D_CMND_UPGRADE "|" D_CMND_UPLOAD "|" D_CMND_OTAURL "|" D_CMND_SERIALLOG "|" D_CMND_SYSLOG "|" - D_CMND_LOGHOST "|" D_CMND_LOGPORT "|" D_CMND_IPADDRESS "|" D_CMND_NTPSERVER "|" D_CMND_AP "|" D_CMND_SSID "|" D_CMND_PASSWORD "|" D_CMND_HOSTNAME "|" - D_CMND_WIFICONFIG "|" D_CMND_FRIENDLYNAME "|" D_CMND_SWITCHMODE "|" D_CMND_INTERLOCK "|" D_CMND_TEMPLATE "|" - D_CMND_TELEPERIOD "|" D_CMND_RESTART "|" D_CMND_RESET "|" D_CMND_TIMEZONE "|" D_CMND_TIMESTD "|" D_CMND_TIMEDST "|" D_CMND_ALTITUDE "|" D_CMND_LEDPOWER "|" D_CMND_LEDSTATE "|" D_CMND_LEDMASK "|" - D_CMND_I2CSCAN "|" D_CMND_SERIALSEND "|" D_CMND_BAUDRATE "|" D_CMND_SERIALDELIMITER "|" D_CMND_DRIVER; - const char kSleepMode[] PROGMEM = "Dynamic|Normal"; +const char kPrefixes[] PROGMEM = D_CMND "|" D_STAT "|" D_TELE; +const char kCodeImage[] PROGMEM = "sonoff|minimal|sensors|knx|basic|display|ir"; // Global variables SerialConfig serial_config = SERIAL_8N1; // Serial interface configuration 8 data bits, No parity, 1 stop bit @@ -104,6 +82,7 @@ unsigned long feature_drv1; // Compiled driver feature map unsigned long feature_drv2; // Compiled driver feature map unsigned long feature_sns1; // Compiled sensor feature map unsigned long feature_sns2; // Compiled sensor feature map +unsigned long feature5; // Compiled feature map unsigned long serial_polling_window = 0; // Serial polling window unsigned long state_second = 0; // State second timer unsigned long state_50msecond = 0; // State 50msecond timer @@ -113,6 +92,7 @@ unsigned long pulse_timer[MAX_PULSETIMERS] = { 0 }; // Power off timer unsigned long blink_timer = 0; // Power cycle timer unsigned long backlog_delay = 0; // Command backlog delay power_t power = 0; // Current copy of Settings.power +power_t last_power = 0; // Last power set state power_t blink_power; // Blink power state power_t blink_mask = 0; // Blink relay active mask power_t blink_powersave; // Blink start power save state @@ -129,7 +109,8 @@ int blinks = 201; // Number of LED blinks uint32_t uptime = 0; // Counting every second until 4294967295 = 130 year uint32_t loop_load_avg = 0; // Indicative loop load average uint32_t global_update = 0; // Timestamp of last global temperature and humidity update -float global_temperature = 9999; // Provide a global temperature to be used by some sensors +uint32_t web_log_index = 1; // Index in Web log buffer (should never be 0) +float global_temperature = 9999; // Provide a global temperature to be used by some sensors float global_humidity = 0; // Provide a global humidity to be used by some sensors float global_pressure = 0; // Provide a global pressure to be used by some sensors char *ota_url; // OTA url string pointer @@ -141,8 +122,6 @@ int16_t save_data_counter; // Counter and flag for config save RulesBitfield rules_flag; // Rule state flags (16 bits) uint8_t state_250mS = 0; // State 250msecond per second flag uint8_t latching_relay_pulse = 0; // Latching relay pulse timer -uint8_t backlog_index = 0; // Command backlog index -uint8_t backlog_pointer = 0; // Command backlog pointer uint8_t sleep; // Current copy of Settings.sleep uint8_t blinkspeed = 1; // LED blink rate uint8_t pin[GPIO_MAX]; // Possible pin configurations @@ -152,17 +131,18 @@ uint8_t led_inverted = 0; // LED inverted flag (1 = (0 = On, 1 uint8_t led_power = 0; // LED power state uint8_t ledlnk_inverted = 0; // Link LED inverted flag (1 = (0 = On, 1 = Off)) uint8_t pwm_inverted = 0; // PWM inverted flag (1 = inverted) -uint8_t counter_no_pullup = 0; // Counter input pullup flag (1 = No pullup) uint8_t energy_flg = 0; // Energy monitor configured +uint8_t light_flg = 0; // Light module configured uint8_t light_type = 0; // Light types uint8_t serial_in_byte; // Received byte uint8_t ota_retry_counter = OTA_ATTEMPTS; // OTA retry counter -uint8_t web_log_index = 1; // Index in Web log buffer (should never be 0) uint8_t devices_present = 0; // Max number of devices supported uint8_t seriallog_level; // Current copy of Settings.seriallog_level uint8_t syslog_level; // Current copy of Settings.syslog_level uint8_t my_module_type; // Current copy of Settings.module or user template type uint8_t my_adc0; // Active copy of Module ADC0 +uint8_t last_source = 0; // Last command source +uint8_t shutters_present = 0; // Number of actual define shutters //uint8_t mdns_delayed_start = 0; // mDNS delayed start bool serial_local = false; // Handle serial locally; bool fallback_topic_flag = false; // Use Topic or FallbackTopic @@ -172,12 +152,12 @@ bool stop_flash_rotate = false; // Allow flash configuration rotatio bool blinkstate = false; // LED state //bool latest_uptime_flag = true; // Signal latest uptime bool pwm_present = false; // Any PWM channel configured with SetOption15 0 -bool dht_flg = false; // DHT configured bool i2c_flg = false; // I2C configured bool spi_flg = false; // SPI configured bool soft_spi_flg = false; // Software SPI configured bool ntp_force_sync = false; // Force NTP sync bool ntp_synced_message = false; // NTP synced message flag +bool prep_called = false; // Deep sleep flag to detect a proper start of initialize sensors myio my_module; // Active copy of Module GPIOs (17 x 8 bits) gpio_flag my_module_flag; // Active copy of Template GPIO flags StateBitfield global_state; // Global states (currently Wifi and Mqtt) (8 bits) @@ -190,14 +170,23 @@ char serial_in_buffer[INPUT_BUFFER_SIZE]; // Receive buffer char mqtt_data[MESSZ]; // MQTT publish buffer and web page ajax buffer char log_data[LOGSZ]; // Logging char web_log[WEB_LOG_SIZE] = {'\0'}; // Web log buffer -String backlog[MAX_BACKLOG]; // Command backlog +#ifdef SUPPORT_IF_STATEMENT + #include + LinkedList backlog; // Command backlog implemented with LinkedList + #define BACKLOG_EMPTY (backlog.size() == 0) +#else + uint8_t backlog_index = 0; // Command backlog index + uint8_t backlog_pointer = 0; // Command backlog pointer + String backlog[MAX_BACKLOG]; // Command backlog buffer + #define BACKLOG_EMPTY (backlog_pointer == backlog_index) +#endif /********************************************************************************************/ char* Format(char* output, const char* input, int size) { char *token; - uint8_t digits = 0; + uint32_t digits = 0; if (strstr(input, "%") != nullptr) { strlcpy(output, input, size); @@ -226,7 +215,9 @@ char* Format(char* output, const char* input, int size) } } } - if (!digits) { strlcpy(output, input, size); } + if (!digits) { + strlcpy(output, input, size); + } return output; } @@ -244,7 +235,7 @@ char* GetOtaUrl(char *otaurl, size_t otaurl_size) return otaurl; } -char* GetTopic_P(char *stopic, uint8_t prefix, char *topic, const char* subtopic) +char* GetTopic_P(char *stopic, uint32_t prefix, char *topic, const char* subtopic) { /* prefix 0 = Cmnd prefix 1 = Stat @@ -259,7 +250,8 @@ char* GetTopic_P(char *stopic, uint8_t prefix, char *topic, const char* subtopic snprintf_P(romram, sizeof(romram), subtopic); if (fallback_topic_flag || (prefix > 3)) { prefix &= 3; - fulltopic = FPSTR(kPrefixes[prefix]); + char stemp[11]; + fulltopic = GetTextIndexed(stemp, sizeof(stemp), prefix, kPrefixes); fulltopic += F("/"); fulltopic += mqtt_client; fulltopic += F("_fb"); // cmnd/_fb @@ -271,7 +263,7 @@ char* GetTopic_P(char *stopic, uint8_t prefix, char *topic, const char* subtopic } for (uint32_t i = 0; i < 3; i++) { if ('\0' == Settings.mqtt_prefix[i][0]) { - snprintf_P(Settings.mqtt_prefix[i], sizeof(Settings.mqtt_prefix[i]), kPrefixes[i]); + GetTextIndexed(Settings.mqtt_prefix[i], sizeof(Settings.mqtt_prefix[i]), i, kPrefixes); } } fulltopic.replace(FPSTR(MQTT_TOKEN_PREFIX), Settings.mqtt_prefix[prefix]); @@ -283,25 +275,29 @@ char* GetTopic_P(char *stopic, uint8_t prefix, char *topic, const char* subtopic } fulltopic.replace(F("#"), ""); fulltopic.replace(F("//"), "/"); - if (!fulltopic.endsWith("/")) fulltopic += "/"; + if (!fulltopic.endsWith("/")) { + fulltopic += "/"; + } snprintf_P(stopic, TOPSZ, PSTR("%s%s"), fulltopic.c_str(), romram); return stopic; } -char* GetFallbackTopic_P(char *stopic, uint8_t prefix, const char* subtopic) +char* GetFallbackTopic_P(char *stopic, uint32_t prefix, const char* subtopic) { return GetTopic_P(stopic, prefix +4, nullptr, subtopic); } -char* GetStateText(uint8_t state) +char* GetStateText(uint32_t state) { - if (state > 3) { state = 1; } + if (state > 3) { + state = 1; + } return Settings.state_text[state]; } /********************************************************************************************/ -void SetLatchingRelay(power_t lpower, uint8_t state) +void SetLatchingRelay(power_t lpower, uint32_t state) { // power xx00 - toggle REL1 (Off) and REL3 (Off) - device 1 Off, device 2 Off // power xx01 - toggle REL2 (On) and REL3 (Off) - device 1 On, device 2 Off @@ -314,18 +310,17 @@ void SetLatchingRelay(power_t lpower, uint8_t state) } for (uint32_t i = 0; i < devices_present; i++) { - uint8_t port = (i << 1) + ((latching_power >> i) &1); + uint32_t port = (i << 1) + ((latching_power >> i) &1); if (pin[GPIO_REL1 +port] < 99) { digitalWrite(pin[GPIO_REL1 +port], bitRead(rel_inverted, port) ? !state : state); } } } -void SetDevicePower(power_t rpower, int source) +void SetDevicePower(power_t rpower, uint32_t source) { - uint8_t state; - ShowSource(source); + last_source = source; if (POWER_ALL_ALWAYS_ON == Settings.poweronstate) { // All on and stay on power = (1 << devices_present) -1; @@ -335,9 +330,11 @@ void SetDevicePower(power_t rpower, int source) if (Settings.flag.interlock) { // Allow only one or no relay set for (uint32_t i = 0; i < MAX_INTERLOCKS; i++) { power_t mask = 1; - uint8_t count = 0; + uint32_t count = 0; for (uint32_t j = 0; j < devices_present; j++) { - if ((Settings.interlock[i] & mask) && (rpower & mask)) { count++; } + if ((Settings.interlock[i] & mask) && (rpower & mask)) { + count++; + } mask <<= 1; } if (count > 1) { @@ -348,6 +345,10 @@ void SetDevicePower(power_t rpower, int source) } } + if (rpower) { // Any power set + last_power = rpower; + } + XdrvMailbox.index = rpower; XdrvCall(FUNC_SET_POWER); // Signal power state @@ -369,7 +370,7 @@ void SetDevicePower(power_t rpower, int source) } else { for (uint32_t i = 0; i < devices_present; i++) { - state = rpower &1; + power_t state = rpower &1; if ((i < MAX_RELAYS) && (pin[GPIO_REL1 +i] < 99)) { digitalWrite(pin[GPIO_REL1 +i], bitRead(rel_inverted, i) ? !state : state); } @@ -378,13 +379,59 @@ void SetDevicePower(power_t rpower, int source) } } -void SetLedPowerIdx(uint8_t led, uint8_t state) +void RestorePower(bool publish_power, uint32_t source) { - if ((99 == pin[GPIO_LEDLNK]) && (0 == led)) { // Legacy - LED1 is link led only if LED2 is present - if (pin[GPIO_LED2] < 99) { led = 1; } + if (power != last_power) { + SetDevicePower(last_power, source); + if (publish_power) { + MqttPublishAllPowerState(); + } + } +} + +void SetAllPower(uint32_t state, uint32_t source) +{ +// state 0 = POWER_OFF = Relay Off +// state 1 = POWER_ON = Relay On (turn off after Settings.pulse_timer * 100 mSec if enabled) +// state 2 = POWER_TOGGLE = Toggle relay +// state 8 = POWER_OFF_NO_STATE = Relay Off and no publishPowerState +// state 9 = POWER_ON_NO_STATE = Relay On and no publishPowerState +// state 10 = POWER_TOGGLE_NO_STATE = Toggle relay and no publishPowerState +// state 16 = POWER_SHOW_STATE = Show power state + + bool publish_power = true; + if ((state >= POWER_OFF_NO_STATE) && (state <= POWER_TOGGLE_NO_STATE)) { + state &= 3; // POWER_OFF, POWER_ON or POWER_TOGGLE + publish_power = false; + } + if ((state >= POWER_OFF) && (state <= POWER_TOGGLE)) { + power_t all_on = (1 << devices_present) -1; + switch (state) { + case POWER_OFF: + power = 0; + break; + case POWER_ON: + power = all_on; + break; + case POWER_TOGGLE: + power ^= all_on; // Complement current state + } + SetDevicePower(power, source); + } + if (publish_power) { + MqttPublishAllPowerState(); + } +} + +void SetLedPowerIdx(uint32_t led, uint32_t state) +{ + if ((99 == pin[GPIO_LEDLNK]) && (0 == led)) { // Legacy - LED1 is link led only if LED2 is present + if (pin[GPIO_LED2] < 99) { + led = 1; + } } if (pin[GPIO_LED1 + led] < 99) { - uint8_t mask = 1 << led; + uint32_t mask = 1 << led; if (state) { state = 1; led_power |= mask; @@ -395,13 +442,13 @@ void SetLedPowerIdx(uint8_t led, uint8_t state) } } -void SetLedPower(uint8_t state) +void SetLedPower(uint32_t state) { - if (99 == pin[GPIO_LEDLNK]) { // Legacy - Only use LED1 and/or LED2 + if (99 == pin[GPIO_LEDLNK]) { // Legacy - Only use LED1 and/or LED2 SetLedPowerIdx(0, state); } else { power_t mask = 1; - for (uint32_t i = 0; i < leds_present; i++) { // Map leds to power + for (uint32_t i = 0; i < leds_present; i++) { // Map leds to power bool tstate = (power & mask); SetLedPowerIdx(i, tstate); mask <<= 1; @@ -409,18 +456,18 @@ void SetLedPower(uint8_t state) } } -void SetLedPowerAll(uint8_t state) +void SetLedPowerAll(uint32_t state) { for (uint32_t i = 0; i < leds_present; i++) { SetLedPowerIdx(i, state); } } -void SetLedLink(uint8_t state) +void SetLedLink(uint32_t state) { - uint8_t led_pin = pin[GPIO_LEDLNK]; - uint8_t led_inv = ledlnk_inverted; - if (99 == led_pin) { // Legacy - LED1 is status + uint32_t led_pin = pin[GPIO_LEDLNK]; + uint32_t led_inv = ledlnk_inverted; + if (99 == led_pin) { // Legacy - LED1 is status led_pin = pin[GPIO_LED1]; led_inv = bitRead(led_inverted, 0); } @@ -430,1184 +477,32 @@ void SetLedLink(uint8_t state) } } -uint8_t GetFanspeed(void) -{ - uint8_t fanspeed = 0; - -// if (SONOFF_IFAN02 == my_module_type) { - /* Fanspeed is controlled by relay 2, 3 and 4 as in Sonoff 4CH. - 000x = 0 - 001x = 1 - 011x = 2 - 101x = 3 - */ - fanspeed = (uint8_t)(power &0xF) >> 1; - if (fanspeed) { fanspeed = (fanspeed >> 1) +1; } -// } - return fanspeed; -} - -void SetFanspeed(uint8_t fanspeed) -{ - for (uint32_t i = 0; i < MAX_FAN_SPEED -1; i++) { - uint8_t state = kIFan02Speed[fanspeed][i]; -// uint8_t state = pgm_read_byte(kIFan02Speed +(speed *3) +i); - ExecuteCommandPower(i +2, state, SRC_IGNORE); // Use relay 2, 3 and 4 - } -#ifdef USE_DOMOTICZ - DomoticzUpdateFanState(); // Command FanSpeed feedback -#endif // USE_DOMOTICZ -} - -void SetPulseTimer(uint8_t index, uint16_t time) +void SetPulseTimer(uint32_t index, uint32_t time) { pulse_timer[index] = (time > 111) ? millis() + (1000 * (time - 100)) : (time > 0) ? millis() + (100 * time) : 0L; } -uint16_t GetPulseTimer(uint8_t index) +uint32_t GetPulseTimer(uint32_t index) { - uint16_t result = 0; - long time = TimePassedSince(pulse_timer[index]); if (time < 0) { time *= -1; - result = (time > 11100) ? (time / 1000) + 100 : (time > 0) ? time / 100 : 0; + return (time > 11100) ? (time / 1000) + 100 : (time > 0) ? time / 100 : 0; } - return result; + return 0; } /********************************************************************************************/ -void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) +bool SendKey(uint32_t key, uint32_t device, uint32_t state) { - if (data_len > MQTT_MAX_PACKET_SIZE) { return; } // Do not allow more data than would be feasable within stack space - - char *str; - - if (!strcmp(Settings.mqtt_prefix[0],Settings.mqtt_prefix[1])) { - str = strstr(topic,Settings.mqtt_prefix[0]); - if ((str == topic) && mqtt_cmnd_publish) { - if (mqtt_cmnd_publish > 3) { - mqtt_cmnd_publish -= 3; - } else { - mqtt_cmnd_publish = 0; - } - return; - } - } - - char topicBuf[TOPSZ]; - char dataBuf[data_len+1]; - char command [CMDSZ]; - char stemp1[TOPSZ]; - char *p; - char *type = nullptr; - uint8_t lines = 1; - bool jsflg = false; - bool grpflg = false; -// bool user_append_index = false; - uint32_t i = 0; - uint32_t index; - uint32_t address; - -#ifdef USE_DEBUG_DRIVER - ShowFreeMem(PSTR("MqttDataHandler")); -#endif - - strlcpy(topicBuf, topic, sizeof(topicBuf)); - for (i = 0; i < data_len; i++) { - if (!isspace(data[i])) { break; } - } - data_len -= i; - memcpy(dataBuf, data +i, sizeof(dataBuf)); - dataBuf[sizeof(dataBuf)-1] = 0; - - if (topicBuf[0] != '/') { ShowSource(SRC_MQTT); } - - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_RESULT D_RECEIVED_TOPIC " %s, " D_DATA_SIZE " %d, " D_DATA " %s"), topicBuf, data_len, dataBuf); -// if (LOG_LEVEL_DEBUG_MORE <= seriallog_level) { Serial.println(dataBuf); } - - if (XdrvMqttData(topicBuf, sizeof(topicBuf), dataBuf, sizeof(dataBuf))) { return; } - - grpflg = (strstr(topicBuf, Settings.mqtt_grptopic) != nullptr); - - GetFallbackTopic_P(stemp1, CMND, ""); // Full Fallback topic = cmnd/DVES_xxxxxxxx_fb/ - fallback_topic_flag = (!strncmp(topicBuf, stemp1, strlen(stemp1))); - - type = strrchr(topicBuf, '/'); // Last part of received topic is always the command (type) - - index = 1; - if (type != nullptr) { - type++; - for (i = 0; i < strlen(type); i++) { - type[i] = toupper(type[i]); - } - while (isdigit(type[i-1])) { - i--; - } - if (i < strlen(type)) { - index = atoi(type +i); -// user_append_index = true; - } - type[i] = '\0'; - } - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_RESULT D_GROUP " %d, " D_INDEX " %d, " D_COMMAND " %s, " D_DATA " %s"), grpflg, index, type, dataBuf); - - if (type != nullptr) { - Response_P(PSTR("{\"" D_JSON_COMMAND "\":\"" D_JSON_ERROR "\"}")); - if (Settings.ledstate &0x02) { blinks++; } - - if (!strcmp(dataBuf,"?")) { data_len = 0; } - int16_t payload = -99; // No payload - uint16_t payload16 = 0; - long payload32 = strtol(dataBuf, &p, 0); // decimal, octal (0) or hex (0x) - if (p != dataBuf) { - payload = (int16_t) payload32; // -32766 - 32767 - payload16 = (uint16_t) payload32; // 0 - 65535 - } else { - payload32 = 0; - } - backlog_delay = millis() + (100 * MIN_BACKLOG_DELAY); - - int temp_payload = GetStateNumber(dataBuf); - if (temp_payload > -1) { payload = temp_payload; } - -// AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_RESULT "Payload %d, Payload16 %d, payload32 %u"), payload, payload16, payload32); - - int command_code = GetCommandCode(command, sizeof(command), type, kTasmotaCommands); - if (-1 == command_code) { -// XdrvMailbox.valid = 1; - XdrvMailbox.index = index; - XdrvMailbox.data_len = data_len; - XdrvMailbox.payload16 = payload16; - XdrvMailbox.payload = payload; - XdrvMailbox.grpflg = grpflg; - XdrvMailbox.topic = type; - XdrvMailbox.data = dataBuf; - if (!XdrvCall(FUNC_COMMAND)) { - if (!XsnsCall(FUNC_COMMAND)) { - type = nullptr; // Unknown command - } - } - } - else if (CMND_BACKLOG == command_code) { - if (data_len) { - uint8_t bl_pointer = (!backlog_pointer) ? MAX_BACKLOG -1 : backlog_pointer; - bl_pointer--; - char *blcommand = strtok(dataBuf, ";"); - while ((blcommand != nullptr) && (backlog_index != bl_pointer)) { - while(true) { - blcommand = Trim(blcommand); - if (!strncasecmp_P(blcommand, PSTR(D_CMND_BACKLOG), strlen(D_CMND_BACKLOG))) { - blcommand += strlen(D_CMND_BACKLOG); // Skip unnecessary command Backlog - } else { - break; - } - } - if (*blcommand != '\0') { - backlog[backlog_index] = String(blcommand); - backlog_index++; - if (backlog_index >= MAX_BACKLOG) backlog_index = 0; - } - blcommand = strtok(nullptr, ";"); - } -// Response_P(S_JSON_COMMAND_SVALUE, command, D_JSON_APPENDED); - mqtt_data[0] = '\0'; - } else { - uint8_t blflag = (backlog_pointer == backlog_index); - backlog_pointer = backlog_index; - Response_P(S_JSON_COMMAND_SVALUE, command, blflag ? D_JSON_EMPTY : D_JSON_ABORTED); - } - } - else if (CMND_DELAY == command_code) { - if ((payload >= MIN_BACKLOG_DELAY) && (payload <= 3600)) { - backlog_delay = millis() + (100 * payload); - } - uint16_t bl_delay = 0; - long bl_delta = TimePassedSince(backlog_delay); - if (bl_delta < 0) { bl_delay = (bl_delta *-1) / 100; } - Response_P(S_JSON_COMMAND_NVALUE, command, bl_delay); - } - else if ((CMND_POWER == command_code) && (index > 0) && (index <= devices_present)) { - if ((payload < 0) || (payload > 4)) { payload = 9; } -// Settings.flag.device_index_enable = user_append_index; - ExecuteCommandPower(index, payload, SRC_IGNORE); - fallback_topic_flag = false; - return; - } - else if ((CMND_FANSPEED == command_code) && (SONOFF_IFAN02 == my_module_type)) { - if (data_len > 0) { - if ('-' == dataBuf[0]) { - payload = (int16_t)GetFanspeed() -1; - if (payload < 0) { payload = MAX_FAN_SPEED -1; } - } - else if ('+' == dataBuf[0]) { - payload = GetFanspeed() +1; - if (payload > MAX_FAN_SPEED -1) { payload = 0; } - } - } - if ((payload >= 0) && (payload < MAX_FAN_SPEED) && (payload != GetFanspeed())) { - SetFanspeed(payload); - } - Response_P(S_JSON_COMMAND_NVALUE, command, GetFanspeed()); - } - else if (CMND_STATUS == command_code) { - if ((payload < 0) || (payload > MAX_STATUS)) payload = 99; - PublishStatus(payload); - fallback_topic_flag = false; - return; - } - else if (CMND_STATE == command_code) { - mqtt_data[0] = '\0'; - MqttShowState(); - if (Settings.flag3.hass_tele_on_power) { - MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_STATE), MQTT_TELE_RETAIN); - } -#ifdef USE_HOME_ASSISTANT - if (Settings.flag.hass_discovery) { - HAssPublishStatus(); - } -#endif // USE_HOME_ASSISTANT - } - else if (CMND_SLEEP == command_code) { - if ((payload >= 0) && (payload < 251)) { - Settings.sleep = payload; - sleep = payload; - WiFiSetSleepMode(); - } - Response_P(S_JSON_COMMAND_NVALUE_UNIT_NVALUE_UNIT, command, sleep, (Settings.flag.value_units) ? " " D_UNIT_MILLISECOND : "", Settings.sleep, (Settings.flag.value_units) ? " " D_UNIT_MILLISECOND : ""); - } - else if ((CMND_UPGRADE == command_code) || (CMND_UPLOAD == command_code)) { - // Check if the payload is numerically 1, and had no trailing chars. - // e.g. "1foo" or "1.2.3" could fool us. - // Check if the version we have been asked to upgrade to is higher than our current version. - // We also need at least 3 chars to make a valid version number string. - if (((1 == data_len) && (1 == payload)) || ((data_len >= 3) && NewerVersion(dataBuf))) { - ota_state_flag = 3; - Response_P(PSTR("{\"%s\":\"" D_JSON_VERSION " %s " D_JSON_FROM " %s\"}"), command, my_version, GetOtaUrl(stemp1, sizeof(stemp1))); - } else { - Response_P(PSTR("{\"%s\":\"" D_JSON_ONE_OR_GT "\"}"), command, my_version); - } - } - else if (CMND_OTAURL == command_code) { - if ((data_len > 0) && (data_len < sizeof(Settings.ota_url))) { - strlcpy(Settings.ota_url, (SC_DEFAULT == Shortcut(dataBuf)) ? OTA_URL : dataBuf, sizeof(Settings.ota_url)); - } - Response_P(S_JSON_COMMAND_SVALUE, command, Settings.ota_url); - } - else if (CMND_SERIALLOG == command_code) { - if ((payload >= LOG_LEVEL_NONE) && (payload <= LOG_LEVEL_ALL)) { - Settings.flag.mqtt_serial = 0; - SetSeriallog(payload); - } - Response_P(S_JSON_COMMAND_NVALUE_ACTIVE_NVALUE, command, Settings.seriallog_level, seriallog_level); - } - else if (CMND_RESTART == command_code) { - switch (payload) { - case 1: - restart_flag = 2; - Response_P(S_JSON_COMMAND_SVALUE, command, D_JSON_RESTARTING); - break; - case 99: - AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_RESTARTING)); - EspRestart(); - break; - default: - Response_P(S_JSON_COMMAND_SVALUE, command, D_JSON_ONE_TO_RESTART); - } - } - else if ((CMND_POWERONSTATE == command_code) && (my_module_type != MOTOR)) { - /* 0 = Keep relays off after power on - * 1 = Turn relays on after power on, if PulseTime set wait for PulseTime seconds, and turn relays off - * 2 = Toggle relays after power on - * 3 = Set relays to last saved state after power on - * 4 = Turn relays on and disable any relay control (used for Sonoff Pow to always measure power) - * 5 = Keep relays off after power on, if PulseTime set wait for PulseTime seconds, and turn relays on - */ - if ((payload >= POWER_ALL_OFF) && (payload <= POWER_ALL_OFF_PULSETIME_ON)) { - Settings.poweronstate = payload; - if (POWER_ALL_ALWAYS_ON == Settings.poweronstate) { - for (uint32_t i = 1; i <= devices_present; i++) { - ExecuteCommandPower(i, POWER_ON, SRC_IGNORE); - } - } - } - Response_P(S_JSON_COMMAND_NVALUE, command, Settings.poweronstate); - } - else if ((CMND_PULSETIME == command_code) && (index > 0) && (index <= MAX_PULSETIMERS)) { - if (data_len > 0) { - Settings.pulse_timer[index -1] = payload16; // 0 - 65535 - SetPulseTimer(index -1, payload16); - } - Response_P(S_JSON_COMMAND_INDEX_NVALUE_ACTIVE_NVALUE, command, index, Settings.pulse_timer[index -1], GetPulseTimer(index -1)); - } - else if (CMND_BLINKTIME == command_code) { - if ((payload > 1) && (payload <= 3600)) { - Settings.blinktime = payload; - if (blink_timer > 0) { blink_timer = millis() + (100 * payload); } - } - Response_P(S_JSON_COMMAND_NVALUE, command, Settings.blinktime); - } - else if (CMND_BLINKCOUNT == command_code) { - if (data_len > 0) { - Settings.blinkcount = payload16; // 0 - 65535 - if (blink_counter) blink_counter = Settings.blinkcount *2; - } - Response_P(S_JSON_COMMAND_NVALUE, command, Settings.blinkcount); - } - else if (CMND_SAVEDATA == command_code) { - if ((payload >= 0) && (payload <= 3600)) { - Settings.save_data = payload; - save_data_counter = Settings.save_data; - } - SettingsSaveAll(); - if (Settings.save_data > 1) { - snprintf_P(stemp1, sizeof(stemp1), PSTR(D_JSON_EVERY " %d " D_UNIT_SECOND), Settings.save_data); - } - Response_P(S_JSON_COMMAND_SVALUE, command, (Settings.save_data > 1) ? stemp1 : GetStateText(Settings.save_data)); - } - else if ((CMND_SENSOR == command_code) || (CMND_DRIVER == command_code)) { - XdrvMailbox.index = index; - XdrvMailbox.data_len = data_len; - XdrvMailbox.payload16 = payload16; - XdrvMailbox.payload = payload; - XdrvMailbox.grpflg = grpflg; - XdrvMailbox.topic = command; - XdrvMailbox.data = dataBuf; - if (CMND_SENSOR == command_code) { - XsnsCall(FUNC_COMMAND_SENSOR); - } else { - XdrvCall(FUNC_COMMAND_DRIVER); - } - } - else if ((CMND_SETOPTION == command_code) && (index < 82)) { - uint8_t ptype; - uint8_t pindex; - if (index <= 31) { // SetOption0 .. 31 = Settings.flag - ptype = 0; - pindex = index; // 0 .. 31 - } - else if (index <= 49) { // SetOption32 .. 49 = Settings.param - ptype = 2; - pindex = index -32; // 0 .. 17 (= PARAM8_SIZE -1) - } - else { // SetOption50 .. 81 = Settings.flag3 - ptype = 1; - pindex = index -50; // 0 .. 31 - } - if (payload >= 0) { - if (0 == ptype) { // SetOption0 .. 31 - if (payload <= 1) { - switch (pindex) { - case 5: // mqtt_power_retain (CMND_POWERRETAIN) - case 6: // mqtt_button_retain (CMND_BUTTONRETAIN) - case 7: // mqtt_switch_retain (CMND_SWITCHRETAIN) - case 9: // mqtt_sensor_retain (CMND_SENSORRETAIN) - case 14: // interlock (CMND_INTERLOCK) - case 22: // mqtt_serial (SerialSend and SerialLog) - case 23: // mqtt_serial_raw (SerialSend) - case 25: // knx_enabled (Web config) - case 27: // knx_enable_enhancement (Web config) - ptype = 99; // Command Error - break; // Ignore command SetOption - case 3: // mqtt - case 15: // pwm_control - restart_flag = 2; - default: - bitWrite(Settings.flag.data, pindex, payload); - } - if (12 == pindex) { // stop_flash_rotate - stop_flash_rotate = payload; - SettingsSave(2); - } -#ifdef USE_HOME_ASSISTANT - if ((19 == pindex) || (30 == pindex)) { - HAssDiscover(); // Delayed execution to provide enough resources during hass_discovery or hass_light - } -#endif // USE_HOME_ASSISTANT - } - } - else if (1 == ptype) { // SetOption50 .. 81 - if (payload <= 1) { - bitWrite(Settings.flag3.data, pindex, payload); - if (5 == pindex) { // SetOption55 - if (0 == payload) { - restart_flag = 2; // Disable mDNS needs restart - } - } - if (10 == pindex) { // SetOption60 enable or disable traditional sleep - WiFiSetSleepMode(); // Update WiFi sleep mode accordingly - } - } - } - else { // SetOption32 .. 49 - uint8_t param_low = 0; - uint8_t param_high = 255; - switch (pindex) { - case P_HOLD_TIME: - case P_MAX_POWER_RETRY: - param_low = 1; - param_high = 250; - break; - } - if ((payload >= param_low) && (payload <= param_high)) { - Settings.param[pindex] = payload; - switch (pindex) { -#ifdef USE_LIGHT - case P_RGB_REMAP: - LightUpdateColorMapping(); - break; -#endif -#if defined(USE_IR_REMOTE) && defined(USE_IR_RECEIVE) - case P_IR_UNKNOW_THRESHOLD: - IrReceiveUpdateThreshold(); - break; -#endif - } - } - } - } - if (ptype < 99) { - if (2 == ptype) snprintf_P(stemp1, sizeof(stemp1), PSTR("%d"), Settings.param[pindex]); - Response_P(S_JSON_COMMAND_INDEX_SVALUE, command, index, (2 == ptype) ? stemp1 : (1 == ptype) ? GetStateText(bitRead(Settings.flag3.data, pindex)) : GetStateText(bitRead(Settings.flag.data, pindex))); - } - } - else if (CMND_TEMPERATURE_RESOLUTION == command_code) { - if ((payload >= 0) && (payload <= 3)) { - Settings.flag2.temperature_resolution = payload; - } - Response_P(S_JSON_COMMAND_NVALUE, command, Settings.flag2.temperature_resolution); - } - else if (CMND_HUMIDITY_RESOLUTION == command_code) { - if ((payload >= 0) && (payload <= 3)) { - Settings.flag2.humidity_resolution = payload; - } - Response_P(S_JSON_COMMAND_NVALUE, command, Settings.flag2.humidity_resolution); - } - else if (CMND_PRESSURE_RESOLUTION == command_code) { - if ((payload >= 0) && (payload <= 3)) { - Settings.flag2.pressure_resolution = payload; - } - Response_P(S_JSON_COMMAND_NVALUE, command, Settings.flag2.pressure_resolution); - } - else if (CMND_POWER_RESOLUTION == command_code) { - if ((payload >= 0) && (payload <= 3)) { - Settings.flag2.wattage_resolution = payload; - } - Response_P(S_JSON_COMMAND_NVALUE, command, Settings.flag2.wattage_resolution); - } - else if (CMND_VOLTAGE_RESOLUTION == command_code) { - if ((payload >= 0) && (payload <= 3)) { - Settings.flag2.voltage_resolution = payload; - } - Response_P(S_JSON_COMMAND_NVALUE, command, Settings.flag2.voltage_resolution); - } - else if (CMND_FREQUENCY_RESOLUTION == command_code) { - if ((payload >= 0) && (payload <= 3)) { - Settings.flag2.frequency_resolution = payload; - } - Response_P(S_JSON_COMMAND_NVALUE, command, Settings.flag2.frequency_resolution); - } - else if (CMND_CURRENT_RESOLUTION == command_code) { - if ((payload >= 0) && (payload <= 3)) { - Settings.flag2.current_resolution = payload; - } - Response_P(S_JSON_COMMAND_NVALUE, command, Settings.flag2.current_resolution); - } - else if (CMND_ENERGY_RESOLUTION == command_code) { - if ((payload >= 0) && (payload <= 5)) { - Settings.flag2.energy_resolution = payload; - } - Response_P(S_JSON_COMMAND_NVALUE, command, Settings.flag2.energy_resolution); - } - else if (CMND_WEIGHT_RESOLUTION == command_code) { - if ((payload >= 0) && (payload <= 3)) { - Settings.flag2.weight_resolution = payload; - } - Response_P(S_JSON_COMMAND_NVALUE, command, Settings.flag2.weight_resolution); - } - else if (CMND_MODULE == command_code) { - if ((payload >= 0) && (payload <= MAXMODULE)) { - bool present = false; - if (0 == payload) { - payload = USER_MODULE; - present = true; - } else { - payload--; - present = ValidTemplateModule(payload); - } - if (present) { - Settings.last_module = Settings.module; - Settings.module = payload; - SetModuleType(); - if (Settings.last_module != payload) { - for (uint32_t i = 0; i < sizeof(Settings.my_gp); i++) { - Settings.my_gp.io[i] = GPIO_NONE; - } - } - restart_flag = 2; - } - } - Response_P(S_JSON_COMMAND_NVALUE_SVALUE, command, ModuleNr(), ModuleName().c_str()); - } - else if (CMND_MODULES == command_code) { - uint8_t midx = USER_MODULE; - for (uint32_t i = 0; i <= sizeof(kModuleNiceList); i++) { - if (i > 0) { midx = pgm_read_byte(kModuleNiceList + i -1); } - if (!jsflg) { - Response_P(PSTR("{\"" D_CMND_MODULES "%d\":["), lines); - } else { - ResponseAppend_P(PSTR(",")); - } - jsflg = true; - uint8_t j = i ? midx +1 : 0; - if ((ResponseAppend_P(PSTR("\"%d (%s)\""), j, AnyModuleName(midx).c_str()) > (LOGSZ - TOPSZ)) || (i == sizeof(kModuleNiceList))) { - ResponseAppend_P(PSTR("]}")); - MqttPublishPrefixTopic_P(RESULT_OR_STAT, type); - jsflg = false; - lines++; - } - } - mqtt_data[0] = '\0'; - } -#ifndef USE_ADC_VCC - else if (CMND_ADC == command_code) { - if (ValidAdc() && (payload >= 0) && (payload < ADC0_END)) { - Settings.my_adc0 = payload; - restart_flag = 2; - } - Response_P(PSTR("{\"" D_CMND_ADC "0\":\"%d (%s)\"}"), Settings.my_adc0, GetTextIndexed(stemp1, sizeof(stemp1), Settings.my_adc0, kAdc0Names)); - } - else if (CMND_ADCS == command_code) { - Response_P(PSTR("{\"" D_CMND_ADCS "\":[")); - for (uint32_t i = 0; i < ADC0_END; i++) { - if (jsflg) { - ResponseAppend_P(PSTR(",")); - } - jsflg = true; - ResponseAppend_P(PSTR("\"%d (%s)\""), i, GetTextIndexed(stemp1, sizeof(stemp1), i, kAdc0Names)); - } - ResponseAppend_P(PSTR("]}")); - } -#endif // USE_ADC_VCC - else if ((CMND_GPIO == command_code) && (index < sizeof(Settings.my_gp))) { - myio cmodule; - ModuleGpios(&cmodule); - if (ValidGPIO(index, cmodule.io[index]) && (payload >= 0) && (payload < GPIO_SENSOR_END)) { - bool present = false; - for (uint32_t i = 0; i < sizeof(kGpioNiceList); i++) { - uint8_t midx = pgm_read_byte(kGpioNiceList + i); - if (midx == payload) { present = true; } - } - if (present) { - for (uint32_t i = 0; i < sizeof(Settings.my_gp); i++) { - if (ValidGPIO(i, cmodule.io[i]) && (Settings.my_gp.io[i] == payload)) { - Settings.my_gp.io[i] = GPIO_NONE; - } - } - Settings.my_gp.io[index] = payload; - restart_flag = 2; - } - } - Response_P(PSTR("{")); - for (uint32_t i = 0; i < sizeof(Settings.my_gp); i++) { - if (ValidGPIO(i, cmodule.io[i])) { - if (jsflg) { ResponseAppend_P(PSTR(",")); } - jsflg = true; - ResponseAppend_P(PSTR("\"" D_CMND_GPIO "%d\":\"%d (%s)\""), i, Settings.my_gp.io[i], GetTextIndexed(stemp1, sizeof(stemp1), Settings.my_gp.io[i], kSensorNames)); - } - } - if (jsflg) { - ResponseJsonEnd(); - } else { - Response_P(S_JSON_COMMAND_SVALUE, command, D_JSON_NOT_SUPPORTED); - } - } - else if (CMND_GPIOS == command_code) { - myio cmodule; - ModuleGpios(&cmodule); - uint8_t midx; - for (uint32_t i = 0; i < sizeof(kGpioNiceList); i++) { - midx = pgm_read_byte(kGpioNiceList + i); - if (!GetUsedInModule(midx, cmodule.io)) { - if (!jsflg) { - Response_P(PSTR("{\"" D_CMND_GPIOS "%d\":["), lines); - } else { - ResponseAppend_P(PSTR(",")); - } - jsflg = true; - if ((ResponseAppend_P(PSTR("\"%d (%s)\""), midx, GetTextIndexed(stemp1, sizeof(stemp1), midx, kSensorNames)) > (LOGSZ - TOPSZ)) || (i == sizeof(kGpioNiceList) -1)) { - ResponseAppend_P(PSTR("]}")); - MqttPublishPrefixTopic_P(RESULT_OR_STAT, type); - jsflg = false; - lines++; - } - } - } - mqtt_data[0] = '\0'; - } - else if (CMND_TEMPLATE == command_code) { - // {"NAME":"Generic","GPIO":[17,254,29,254,7,254,254,254,138,254,139,254,254],"FLAG":1,"BASE":255} - bool error = false; - - if (strstr(dataBuf, "{") == nullptr) { // If no JSON it must be parameter - if ((payload > 0) && (payload <= MAXMODULE)) { - payload--; - if (ValidTemplateModule(payload)) { - ModuleDefault(payload); // Copy template module - if (USER_MODULE == Settings.module) { restart_flag = 2; } - } - } - else if (0 == payload) { // Copy current template to user template - if (Settings.module != USER_MODULE) { - ModuleDefault(Settings.module); - } - } - else if (255 == payload) { // Copy current module with user configured GPIO - if (Settings.module != USER_MODULE) { - ModuleDefault(Settings.module); - } - snprintf_P(Settings.user_template.name, sizeof(Settings.user_template.name), PSTR("Merged")); - uint8_t j = 0; - for (uint32_t i = 0; i < sizeof(mycfgio); i++) { - if (6 == i) { j = 9; } - if (8 == i) { j = 12; } - if (my_module.io[j] > GPIO_NONE) { - Settings.user_template.gp.io[i] = my_module.io[j]; - } - j++; - } - } - } - else { - if (JsonTemplate(dataBuf)) { // Free 336 bytes StaticJsonBuffer stack space by moving code to function - if (USER_MODULE == Settings.module) { restart_flag = 2; } - } else { - Response_P(S_JSON_COMMAND_SVALUE, command, D_JSON_INVALID_JSON); - error = true; - } - } - if (!error) { TemplateJson(); } - } - else if ((CMND_PWM == command_code) && pwm_present && (index > 0) && (index <= MAX_PWMS)) { - if ((payload >= 0) && (payload <= Settings.pwm_range) && (pin[GPIO_PWM1 + index -1] < 99)) { - Settings.pwm_value[index -1] = payload; - analogWrite(pin[GPIO_PWM1 + index -1], bitRead(pwm_inverted, index -1) ? Settings.pwm_range - payload : payload); - } - Response_P(PSTR("{")); - MqttShowPWMState(); // Render the PWM status to MQTT - ResponseJsonEnd(); - } - else if (CMND_PWMFREQUENCY == command_code) { - if ((1 == payload) || ((payload >= PWM_MIN) && (payload <= PWM_MAX))) { - Settings.pwm_frequency = (1 == payload) ? PWM_FREQ : payload; - analogWriteFreq(Settings.pwm_frequency); // Default is 1000 (core_esp8266_wiring_pwm.c) - } - Response_P(S_JSON_COMMAND_NVALUE, command, Settings.pwm_frequency); - } - else if (CMND_PWMRANGE == command_code) { - if ((1 == payload) || ((payload > 254) && (payload < 1024))) { - Settings.pwm_range = (1 == payload) ? PWM_RANGE : payload; - for (uint32_t i = 0; i < MAX_PWMS; i++) { - if (Settings.pwm_value[i] > Settings.pwm_range) { - Settings.pwm_value[i] = Settings.pwm_range; - } - } - analogWriteRange(Settings.pwm_range); // Default is 1023 (Arduino.h) - } - Response_P(S_JSON_COMMAND_NVALUE, command, Settings.pwm_range); - } - else if ((CMND_COUNTER == command_code) && (index > 0) && (index <= MAX_COUNTERS)) { - if ((data_len > 0) && (pin[GPIO_CNTR1 + index -1] < 99)) { - if ((dataBuf[0] == '-') || (dataBuf[0] == '+')) { - RtcSettings.pulse_counter[index -1] += payload32; - Settings.pulse_counter[index -1] += payload32; - } else { - RtcSettings.pulse_counter[index -1] = payload32; - Settings.pulse_counter[index -1] = payload32; - } - } - Response_P(S_JSON_COMMAND_INDEX_LVALUE, command, index, RtcSettings.pulse_counter[index -1]); - } - else if ((CMND_COUNTERTYPE == command_code) && (index > 0) && (index <= MAX_COUNTERS)) { - if ((payload >= 0) && (payload <= 1) && (pin[GPIO_CNTR1 + index -1] < 99)) { - bitWrite(Settings.pulse_counter_type, index -1, payload &1); - RtcSettings.pulse_counter[index -1] = 0; - Settings.pulse_counter[index -1] = 0; - } - Response_P(S_JSON_COMMAND_INDEX_NVALUE, command, index, bitRead(Settings.pulse_counter_type, index -1)); - } - else if (CMND_COUNTERDEBOUNCE == command_code) { - if ((data_len > 0) && (payload16 < 32001)) { - Settings.pulse_counter_debounce = payload16; - } - Response_P(S_JSON_COMMAND_NVALUE, command, Settings.pulse_counter_debounce); - } - else if (CMND_BUTTONDEBOUNCE == command_code) { - if ((payload > 39) && (payload < 1001)) { - Settings.button_debounce = payload; - } - Response_P(S_JSON_COMMAND_NVALUE, command, Settings.button_debounce); - } - else if (CMND_SWITCHDEBOUNCE == command_code) { - if ((payload > 39) && (payload < 1001)) { - Settings.switch_debounce = payload; - } - Response_P(S_JSON_COMMAND_NVALUE, command, Settings.switch_debounce); - } - else if (CMND_BAUDRATE == command_code) { - if (payload32 > 1200) { - payload32 /= 1200; // Make it a valid baudrate - baudrate = (1 == payload) ? APP_BAUDRATE : payload32 * 1200; - SetSerialBaudrate(baudrate); - } - Response_P(S_JSON_COMMAND_NVALUE, command, Settings.baudrate * 1200); - } - else if ((CMND_SERIALSEND == command_code) && (index > 0) && (index <= 5)) { - SetSeriallog(LOG_LEVEL_NONE); - Settings.flag.mqtt_serial = 1; - Settings.flag.mqtt_serial_raw = (index > 3) ? 1 : 0; - if (data_len > 0) { - if (1 == index) { - Serial.printf("%s\n", dataBuf); // "Hello Tiger\n" - } - else if (2 == index || 4 == index) { - for (uint32_t i = 0; i < data_len; i++) { - Serial.write(dataBuf[i]); // "Hello Tiger" or "A0" - } - } - else if (3 == index) { - uint16_t dat_len = data_len; - Serial.printf("%s", Unescape(dataBuf, &dat_len)); // "Hello\f" - } - else if (5 == index) { - SerialSendRaw(RemoveSpace(dataBuf)); // "AA004566" as hex values - } - Response_P(S_JSON_COMMAND_SVALUE, command, D_JSON_DONE); - } - } - else if (CMND_SERIALDELIMITER == command_code) { - if ((data_len > 0) && (payload < 256)) { - if (payload > 0) { - Settings.serial_delimiter = payload; - } else { - uint16_t dat_len = data_len; - Unescape(dataBuf, &dat_len); - Settings.serial_delimiter = dataBuf[0]; - } - } - Response_P(S_JSON_COMMAND_NVALUE, command, Settings.serial_delimiter); - } - else if (CMND_SYSLOG == command_code) { - if ((payload >= LOG_LEVEL_NONE) && (payload <= LOG_LEVEL_ALL)) { - SetSyslog(payload); - } - Response_P(S_JSON_COMMAND_NVALUE_ACTIVE_NVALUE, command, Settings.syslog_level, syslog_level); - } - else if (CMND_LOGHOST == command_code) { - if ((data_len > 0) && (data_len < sizeof(Settings.syslog_host))) { - strlcpy(Settings.syslog_host, (SC_DEFAULT == Shortcut(dataBuf)) ? SYS_LOG_HOST : dataBuf, sizeof(Settings.syslog_host)); - } - Response_P(S_JSON_COMMAND_SVALUE, command, Settings.syslog_host); - } - else if (CMND_LOGPORT == command_code) { - if (payload16 > 0) { - Settings.syslog_port = (1 == payload16) ? SYS_LOG_PORT : payload16; - } - Response_P(S_JSON_COMMAND_NVALUE, command, Settings.syslog_port); - } - else if ((CMND_IPADDRESS == command_code) && (index > 0) && (index <= 4)) { - if (ParseIp(&address, dataBuf)) { - Settings.ip_address[index -1] = address; -// restart_flag = 2; - } - snprintf_P(stemp1, sizeof(stemp1), PSTR(" (%s)"), WiFi.localIP().toString().c_str()); - Response_P(S_JSON_COMMAND_INDEX_SVALUE_SVALUE, command, index, IPAddress(Settings.ip_address[index -1]).toString().c_str(), (1 == index) ? stemp1:""); - } - else if ((CMND_NTPSERVER == command_code) && (index > 0) && (index <= 3)) { - if ((data_len > 0) && (data_len < sizeof(Settings.ntp_server[0]))) { - strlcpy(Settings.ntp_server[index -1], (SC_CLEAR == Shortcut(dataBuf)) ? "" : (SC_DEFAULT == Shortcut(dataBuf)) ? (1==index)?NTP_SERVER1:(2==index)?NTP_SERVER2:NTP_SERVER3 : dataBuf, sizeof(Settings.ntp_server[0])); - for (i = 0; i < strlen(Settings.ntp_server[index -1]); i++) { - if (Settings.ntp_server[index -1][i] == ',') Settings.ntp_server[index -1][i] = '.'; - } -// restart_flag = 2; // Issue #3890 - ntp_force_sync = true; - } - Response_P(S_JSON_COMMAND_INDEX_SVALUE, command, index, Settings.ntp_server[index -1]); - } - else if (CMND_AP == command_code) { - if ((payload >= 0) && (payload <= 2)) { - switch (payload) { - case 0: // Toggle - Settings.sta_active ^= 1; - break; - case 1: // AP1 - case 2: // AP2 - Settings.sta_active = payload -1; - } - restart_flag = 2; - } - Response_P(S_JSON_COMMAND_NVALUE_SVALUE, command, Settings.sta_active +1, Settings.sta_ssid[Settings.sta_active]); - } - else if ((CMND_SSID == command_code) && (index > 0) && (index <= 2)) { - if ((data_len > 0) && (data_len < sizeof(Settings.sta_ssid[0]))) { - strlcpy(Settings.sta_ssid[index -1], (SC_CLEAR == Shortcut(dataBuf)) ? "" : (SC_DEFAULT == Shortcut(dataBuf)) ? (1 == index) ? STA_SSID1 : STA_SSID2 : dataBuf, sizeof(Settings.sta_ssid[0])); - Settings.sta_active = index -1; - restart_flag = 2; - } - Response_P(S_JSON_COMMAND_INDEX_SVALUE, command, index, Settings.sta_ssid[index -1]); - } - else if ((CMND_PASSWORD == command_code) && (index > 0) && (index <= 2)) { - if ((data_len > 4 || SC_CLEAR == Shortcut(dataBuf) || SC_DEFAULT == Shortcut(dataBuf)) && (data_len < sizeof(Settings.sta_pwd[0]))) { - strlcpy(Settings.sta_pwd[index -1], (SC_CLEAR == Shortcut(dataBuf)) ? "" : (SC_DEFAULT == Shortcut(dataBuf)) ? (1 == index) ? STA_PASS1 : STA_PASS2 : dataBuf, sizeof(Settings.sta_pwd[0])); - Settings.sta_active = index -1; - restart_flag = 2; - Response_P(S_JSON_COMMAND_INDEX_SVALUE, command, index, Settings.sta_pwd[index -1]); - } else { - Response_P(S_JSON_COMMAND_INDEX_ASTERISK, command, index); - } - } - else if (CMND_HOSTNAME == command_code) { - if (!grpflg && (data_len > 0) && (data_len < sizeof(Settings.hostname))) { - strlcpy(Settings.hostname, (SC_DEFAULT == Shortcut(dataBuf)) ? WIFI_HOSTNAME : dataBuf, sizeof(Settings.hostname)); - if (strstr(Settings.hostname, "%") != nullptr) { - strlcpy(Settings.hostname, WIFI_HOSTNAME, sizeof(Settings.hostname)); - } - restart_flag = 2; - } - Response_P(S_JSON_COMMAND_SVALUE, command, Settings.hostname); - } - else if (CMND_WIFICONFIG == command_code) { - if ((payload >= WIFI_RESTART) && (payload < MAX_WIFI_OPTION)) { - Settings.sta_config = payload; - wifi_state_flag = Settings.sta_config; - snprintf_P(stemp1, sizeof(stemp1), kWifiConfig[Settings.sta_config]); - Response_P(PSTR("{\"" D_CMND_WIFICONFIG "\":\"%s " D_JSON_SELECTED "\"}"), stemp1); - if (WifiState() > WIFI_RESTART) { -// ResponseAppend_P(PSTR(" after restart")); - restart_flag = 2; - } - } else { - snprintf_P(stemp1, sizeof(stemp1), kWifiConfig[Settings.sta_config]); - Response_P(S_JSON_COMMAND_NVALUE_SVALUE, command, Settings.sta_config, stemp1); - } - } - else if ((CMND_FRIENDLYNAME == command_code) && (index > 0) && (index <= MAX_FRIENDLYNAMES)) { - if ((data_len > 0) && (data_len < sizeof(Settings.friendlyname[0]))) { - if (1 == index) { - snprintf_P(stemp1, sizeof(stemp1), PSTR(FRIENDLY_NAME)); - } else { - snprintf_P(stemp1, sizeof(stemp1), PSTR(FRIENDLY_NAME "%d"), index); - } - strlcpy(Settings.friendlyname[index -1], (SC_DEFAULT == Shortcut(dataBuf)) ? stemp1 : dataBuf, sizeof(Settings.friendlyname[index -1])); - } - Response_P(S_JSON_COMMAND_INDEX_SVALUE, command, index, Settings.friendlyname[index -1]); - } - else if ((CMND_SWITCHMODE == command_code) && (index > 0) && (index <= MAX_SWITCHES)) { - if ((payload >= 0) && (payload < MAX_SWITCH_OPTION)) { - Settings.switchmode[index -1] = payload; - } - Response_P(S_JSON_COMMAND_INDEX_NVALUE, command, index, Settings.switchmode[index-1]); - } - else if (CMND_INTERLOCK == command_code) { // Interlock 0 - Off, Interlock 1 - On, Interlock 1,2 3,4 5,6,7 - uint8_t max_relays = devices_present; - if (light_type) { max_relays--; } - if (max_relays > sizeof(Settings.interlock[0]) * 8) { max_relays = sizeof(Settings.interlock[0]) * 8; } - if (max_relays > 1) { // Only interlock with more than 1 relay - if (data_len > 0) { - if (strstr(dataBuf, ",") != nullptr) { // Interlock entry - for (uint32_t i = 0; i < MAX_INTERLOCKS; i++) { Settings.interlock[i] = 0; } // Reset current interlocks - char *group; - char *q; - uint8_t group_index = 0; - power_t relay_mask = 0; - for (group = strtok_r(dataBuf, " ", &q); group && group_index < MAX_INTERLOCKS; group = strtok_r(nullptr, " ", &q)) { - char *str; - for (str = strtok_r(group, ",", &p); str; str = strtok_r(nullptr, ",", &p)) { - int pbit = atoi(str); - if ((pbit > 0) && (pbit <= max_relays)) { // Only valid relays - pbit--; - if (!bitRead(relay_mask, pbit)) { // Only relay once - bitSet(relay_mask, pbit); - bitSet(Settings.interlock[group_index], pbit); - } - } - } - group_index++; - } - for (uint32_t i = 0; i < group_index; i++) { - uint8_t minimal_bits = 0; - for (uint32_t j = 0; j < max_relays; j++) { - if (bitRead(Settings.interlock[i], j)) { minimal_bits++; } - } - if (minimal_bits < 2) { Settings.interlock[i] = 0; } // Discard single relay as interlock - } - } else { - Settings.flag.interlock = payload &1; // Enable/disable interlock - if (Settings.flag.interlock) { - SetDevicePower(power, SRC_IGNORE); // Remove multiple relays if set - } - } - } - Response_P(PSTR("{\"" D_CMND_INTERLOCK "\":\"%s\",\"" D_JSON_GROUPS "\":\""), GetStateText(Settings.flag.interlock)); - uint8_t anygroup = 0; - for (uint32_t i = 0; i < MAX_INTERLOCKS; i++) { - if (Settings.interlock[i]) { - anygroup++; - ResponseAppend_P(PSTR("%s"), (anygroup > 1) ? " " : ""); - uint8_t anybit = 0; - power_t mask = 1; - for (uint32_t j = 0; j < max_relays; j++) { - if (Settings.interlock[i] & mask) { - anybit++; - ResponseAppend_P(PSTR("%s%d"), (anybit > 1) ? "," : "", j +1); - } - mask <<= 1; - } - } - } - if (!anygroup) { - for (uint32_t j = 1; j <= max_relays; j++) { - ResponseAppend_P(PSTR("%s%d"), (j > 1) ? "," : "", j); - } - } - ResponseAppend_P(PSTR("\"}")); - } else { - Settings.flag.interlock = 0; - Response_P(S_JSON_COMMAND_SVALUE, command, GetStateText(Settings.flag.interlock)); - } - } - else if (CMND_TELEPERIOD == command_code) { - if ((payload >= 0) && (payload < 3601)) { - Settings.tele_period = (1 == payload) ? TELE_PERIOD : payload; - if ((Settings.tele_period > 0) && (Settings.tele_period < 10)) Settings.tele_period = 10; // Do not allow periods < 10 seconds - tele_period = Settings.tele_period; - } - Response_P(S_JSON_COMMAND_NVALUE_UNIT, command, Settings.tele_period, (Settings.flag.value_units) ? " " D_UNIT_SECOND : ""); - } - else if (CMND_RESET == command_code) { - switch (payload) { - case 1: - restart_flag = 211; - Response_P(S_JSON_COMMAND_SVALUE, command , D_JSON_RESET_AND_RESTARTING); - break; - case 2 ... 6: - restart_flag = 210 + payload; - Response_P(PSTR("{\"" D_CMND_RESET "\":\"" D_JSON_ERASE ", " D_JSON_RESET_AND_RESTARTING "\"}")); - break; - default: - Response_P(S_JSON_COMMAND_SVALUE, command, D_JSON_ONE_TO_RESET); - } - } - else if (CMND_TIMEZONE == command_code) { - if ((data_len > 0) && (payload >= -13)) { - Settings.timezone = payload; - Settings.timezone_minutes = 0; - if (payload < 15) { - p = strtok (dataBuf, ":"); - if (p) { - p = strtok (nullptr, ":"); - if (p) { - Settings.timezone_minutes = strtol(p, nullptr, 10); - if (Settings.timezone_minutes > 59) { Settings.timezone_minutes = 59; } - } - } - } else { - Settings.timezone = 99; - } - ntp_force_sync = true; - } - if (99 == Settings.timezone) { - Response_P(S_JSON_COMMAND_NVALUE, command, Settings.timezone); - } else { - snprintf_P(stemp1, sizeof(stemp1), PSTR("%+03d:%02d"), Settings.timezone, Settings.timezone_minutes); - Response_P(S_JSON_COMMAND_SVALUE, command, stemp1); - } - } - else if ((CMND_TIMESTD == command_code) || (CMND_TIMEDST == command_code)) { - // TimeStd 0/1, 0/1/2/3/4, 1..12, 1..7, 0..23, +/-780 - uint8_t ts = 0; - if (CMND_TIMEDST == command_code) { ts = 1; } - if (data_len > 0) { - if (strstr(dataBuf, ",") != nullptr) { // Process parameter entry - uint8_t tpos = 0; // Parameter index - int value = 0; - p = dataBuf; // Parameters like "1, 2,3 , 4 ,5, -120" or ",,,,,+240" - char *q = p; // Value entered flag - while (p && (tpos < 7)) { - if (p > q) { // Any value entered - if (1 == tpos) { Settings.tflag[ts].hemis = value &1; } - if (2 == tpos) { Settings.tflag[ts].week = (value < 0) ? 0 : (value > 4) ? 4 : value; } - if (3 == tpos) { Settings.tflag[ts].month = (value < 1) ? 1 : (value > 12) ? 12 : value; } - if (4 == tpos) { Settings.tflag[ts].dow = (value < 1) ? 1 : (value > 7) ? 7 : value; } - if (5 == tpos) { Settings.tflag[ts].hour = (value < 0) ? 0 : (value > 23) ? 23 : value; } - if (6 == tpos) { Settings.toffset[ts] = (value < -900) ? -900 : (value > 900) ? 900 : value; } - } - p = Trim(p); // Skip spaces - if (tpos && (*p == ',')) { p++; } // Skip separator - p = Trim(p); // Skip spaces - q = p; // Reset any value entered flag - value = strtol(p, &p, 10); - tpos++; // Next parameter - } - ntp_force_sync = true; - } else { - if (0 == payload) { - if (0 == ts) { - SettingsResetStd(); - } else { - SettingsResetDst(); - } - } - ntp_force_sync = true; - } - } - Response_P(PSTR("{\"%s\":{\"Hemisphere\":%d,\"Week\":%d,\"Month\":%d,\"Day\":%d,\"Hour\":%d,\"Offset\":%d}}"), - command, Settings.tflag[ts].hemis, Settings.tflag[ts].week, Settings.tflag[ts].month, Settings.tflag[ts].dow, Settings.tflag[ts].hour, Settings.toffset[ts]); - } - else if (CMND_ALTITUDE == command_code) { - if ((data_len > 0) && ((payload >= -30000) && (payload <= 30000))) { - Settings.altitude = payload; - } - Response_P(S_JSON_COMMAND_NVALUE, command, Settings.altitude); - } - else if ((CMND_LEDPOWER == command_code) && (index > 0) && (index <= MAX_LEDS)) { -/* - if ((payload >= 0) && (payload <= 2)) { - Settings.ledstate &= 8; - switch (payload) { - case 0: // Off - case 1: // On - Settings.ledstate = payload << 3; - break; - case 2: // Toggle - Settings.ledstate ^= 8; - break; - } - blinks = 0; - SetLedPowerIdx(index -1, Settings.ledstate &8); - } - Response_P(S_JSON_COMMAND_INDEX_SVALUE, command, index, GetStateText(bitRead(Settings.ledstate, 3))); -*/ -/* - if (99 == pin[GPIO_LEDLNK]) { - if ((payload >= 0) && (payload <= 2)) { - Settings.ledstate &= 8; - switch (payload) { - case 0: // Off - case 1: // On - Settings.ledstate = payload << 3; - break; - case 2: // Toggle - Settings.ledstate ^= 8; - break; - } - blinks = 0; - SetLedPower(Settings.ledstate &8); - } - Response_P(S_JSON_COMMAND_SVALUE, command, GetStateText(bitRead(Settings.ledstate, 3))); - } else { - if ((payload >= 0) && (payload <= 2)) { - Settings.ledstate &= 8; // Disable power control - uint8_t mask = 1 << (index -1); // Led to control - switch (payload) { - case 0: // Off - led_power &= (0xFF ^ mask); - case 1: // On - led_power |= mask; - break; - case 2: // Toggle - led_power ^= mask; - break; - } - blinks = 0; - SetLedPowerIdx(index -1, (led_power & mask)); - } - Response_P(S_JSON_COMMAND_INDEX_SVALUE, command, index, GetStateText(bitRead(led_power, index -1))); - } -*/ - if (99 == pin[GPIO_LEDLNK]) { index = 1; } - if ((payload >= 0) && (payload <= 2)) { - Settings.ledstate &= 8; // Disable power control - uint8_t mask = 1 << (index -1); // Led to control - switch (payload) { - case 0: // Off - led_power &= (0xFF ^ mask); - Settings.ledstate = 0; - break; - case 1: // On - led_power |= mask; - Settings.ledstate = 8; - break; - case 2: // Toggle - led_power ^= mask; - Settings.ledstate ^= 8; - break; - } - blinks = 0; - if (99 == pin[GPIO_LEDLNK]) { - SetLedPower(Settings.ledstate &8); - } else { - SetLedPowerIdx(index -1, (led_power & mask)); - } - } - uint8_t state = bitRead(led_power, index -1); - if (99 == pin[GPIO_LEDLNK]) { - state = bitRead(Settings.ledstate, 3); - } - Response_P(S_JSON_COMMAND_INDEX_SVALUE, command, index, GetStateText(state)); - } - else if (CMND_LEDSTATE == command_code) { - if ((payload >= 0) && (payload < MAX_LED_OPTION)) { - Settings.ledstate = payload; - if (!Settings.ledstate) { - SetLedPowerAll(0); - SetLedLink(0); - } - } - Response_P(S_JSON_COMMAND_NVALUE, command, Settings.ledstate); - } - else if (CMND_LEDMASK == command_code) { - if (data_len > 0) { - Settings.ledmask = payload16; - } - snprintf_P(stemp1, sizeof(stemp1), PSTR("%d (0x%04X)"), Settings.ledmask, Settings.ledmask); - Response_P(S_JSON_COMMAND_SVALUE, command, stemp1); - } -#ifdef USE_I2C - else if ((CMND_I2CSCAN == command_code) && i2c_flg) { - I2cScan(mqtt_data, sizeof(mqtt_data)); - } -#endif // USE_I2C - else type = nullptr; // Unknown command - } - if (type == nullptr) { - blinks = 201; - snprintf_P(topicBuf, sizeof(topicBuf), PSTR(D_JSON_COMMAND)); - Response_P(PSTR("{\"" D_JSON_COMMAND "\":\"" D_JSON_UNKNOWN "\"}")); - type = (char*)topicBuf; - } - if (mqtt_data[0] != '\0') { - MqttPublishPrefixTopic_P(RESULT_OR_STAT, type); -#ifdef USE_SCRIPT - XdrvRulesProcess(); -#endif - } - fallback_topic_flag = false; -} - -/********************************************************************************************/ - -bool SendKey(uint8_t key, uint8_t device, uint8_t state) -{ -// key 0 = button_topic -// key 1 = switch_topic -// state 0 = off -// state 1 = on -// state 2 = toggle -// state 3 = hold -// state 9 = clear retain flag +// key 0 = KEY_BUTTON = button_topic +// key 1 = KEY_SWITCH = switch_topic +// state 0 = POWER_OFF = off +// state 1 = POWER_ON = on +// state 2 = POWER_TOGGLE = toggle +// state 3 = POWER_HOLD = hold +// state 9 = CLEAR_RETAIN = clear retain flag char stopic[TOPSZ]; char scommand[CMDSZ]; @@ -1617,23 +512,25 @@ bool SendKey(uint8_t key, uint8_t device, uint8_t state) char *tmp = (key) ? Settings.switch_topic : Settings.button_topic; Format(key_topic, tmp, sizeof(key_topic)); if (Settings.flag.mqtt_enabled && MqttIsConnected() && (strlen(key_topic) != 0) && strcmp(key_topic, "0")) { - if (!key && (device > devices_present)) { device = 1; } // Only allow number of buttons up to number of devices + if (!key && (device > devices_present)) { + device = 1; // Only allow number of buttons up to number of devices + } GetTopic_P(stopic, CMND, key_topic, GetPowerDevice(scommand, device, sizeof(scommand), (key + Settings.flag.device_index_enable))); // cmnd/switchtopic/POWERx - if (9 == state) { + if (CLEAR_RETAIN == state) { mqtt_data[0] = '\0'; } else { - if ((Settings.flag3.button_switch_force_local || !strcmp(mqtt_topic, key_topic) || !strcmp(Settings.mqtt_grptopic, key_topic)) && (2 == state)) { - state = ~(power >> (device -1)) &1; + if ((Settings.flag3.button_switch_force_local || !strcmp(mqtt_topic, key_topic) || !strcmp(Settings.mqtt_grptopic, key_topic)) && (POWER_TOGGLE == state)) { + state = ~(power >> (device -1)) &1; // POWER_OFF or POWER_ON } snprintf_P(mqtt_data, sizeof(mqtt_data), GetStateText(state)); } #ifdef USE_DOMOTICZ if (!(DomoticzSendKey(key, device, state, strlen(mqtt_data)))) { - MqttPublishDirect(stopic, ((key) ? Settings.flag.mqtt_switch_retain : Settings.flag.mqtt_button_retain) && (state != 3 || !Settings.flag3.no_hold_retain)); + MqttPublishDirect(stopic, ((key) ? Settings.flag.mqtt_switch_retain : Settings.flag.mqtt_button_retain) && (state != POWER_HOLD || !Settings.flag3.no_hold_retain)); } #else - MqttPublishDirect(stopic, ((key) ? Settings.flag.mqtt_switch_retain : Settings.flag.mqtt_button_retain) && (state != 3 || !Settings.flag3.no_hold_retain)); + MqttPublishDirect(stopic, ((key) ? Settings.flag.mqtt_switch_retain : Settings.flag.mqtt_button_retain) && (state != POWER_HOLD || !Settings.flag3.no_hold_retain)); #endif // USE_DOMOTICZ result = !Settings.flag3.button_switch_force_local; } else { @@ -1646,38 +543,45 @@ bool SendKey(uint8_t key, uint8_t device, uint8_t state) return result; } -void ExecuteCommandPower(uint8_t device, uint8_t state, int source) +void ExecuteCommandPower(uint32_t device, uint32_t state, uint32_t source) { // device = Relay number 1 and up -// state 0 = Relay Off -// state 1 = Relay On (turn off after Settings.pulse_timer * 100 mSec if enabled) -// state 2 = Toggle relay -// state 3 = Blink relay -// state 4 = Stop blinking relay -// state 6 = Relay Off and no publishPowerState -// state 7 = Relay On and no publishPowerState -// state 9 = Show power state +// state 0 = POWER_OFF = Relay Off +// state 1 = POWER_ON = Relay On (turn off after Settings.pulse_timer * 100 mSec if enabled) +// state 2 = POWER_TOGGLE = Toggle relay +// state 3 = POWER_BLINK = Blink relay +// state 4 = POWER_BLINK_STOP = Stop blinking relay +// state 8 = POWER_OFF_NO_STATE = Relay Off and no publishPowerState +// state 9 = POWER_ON_NO_STATE = Relay On and no publishPowerState +// state 10 = POWER_TOGGLE_NO_STATE = Toggle relay and no publishPowerState +// state 16 = POWER_SHOW_STATE = Show power state // ShowSource(source); - if (SONOFF_IFAN02 == my_module_type) { +#ifdef USE_SONOFF_IFAN + if (IsModuleIfan()) { blink_mask &= 1; // No blinking on the fan relays Settings.flag.interlock = 0; // No interlock mode as it is already done by the microcontroller Settings.pulse_timer[1] = 0; // No pulsetimers on the fan relays Settings.pulse_timer[2] = 0; Settings.pulse_timer[3] = 0; } +#endif // USE_SONOFF_IFAN - uint8_t publish_power = 1; - if ((POWER_OFF_NO_STATE == state) || (POWER_ON_NO_STATE == state)) { - state &= 1; - publish_power = 0; + bool publish_power = true; + if ((state >= POWER_OFF_NO_STATE) && (state <= POWER_TOGGLE_NO_STATE)) { + state &= 3; // POWER_OFF, POWER_ON or POWER_TOGGLE + publish_power = false; } - if ((device < 1) || (device > devices_present)) device = 1; + if ((device < 1) || (device > devices_present)) { + device = 1; + } active_device = device; - if (device <= MAX_PULSETIMERS) { SetPulseTimer(device -1, 0); } + if (device <= MAX_PULSETIMERS) { + SetPulseTimer(device -1, 0); + } power_t mask = 1 << (device -1); // Device to control if (state <= POWER_TOGGLE) { if ((blink_mask & mask)) { @@ -1685,8 +589,11 @@ void ExecuteCommandPower(uint8_t device, uint8_t state, int source) MqttPublishPowerBlinkState(device); } - if (Settings.flag.interlock && !interlock_mutex) { // Clear all but masked relay in interlock group - interlock_mutex = true; + if (Settings.flag.interlock && + !interlock_mutex && + ((POWER_ON == state) || ((POWER_TOGGLE == state) && !(power & mask))) + ) { + interlock_mutex = true; // Clear all but masked relay in interlock group if new set requested for (uint32_t i = 0; i < MAX_INTERLOCKS; i++) { if (Settings.interlock[i] & mask) { // Find interlock group for (uint32_t j = 0; j < devices_present; j++) { @@ -1719,7 +626,9 @@ void ExecuteCommandPower(uint8_t device, uint8_t state, int source) #ifdef USE_KNX KnxUpdatePowerState(device, power); #endif // USE_KNX - if (publish_power && Settings.flag3.hass_tele_on_power) { MqttPublishTeleState(); } + if (publish_power && Settings.flag3.hass_tele_on_power) { + MqttPublishTeleState(); + } if (device <= MAX_PULSETIMERS) { // Restart PulseTime if powered On SetPulseTimer(device -1, (((POWER_ALL_OFF_PULSETIME_ON == Settings.poweronstate) ? ~power : power) & mask) ? Settings.pulse_timer[device -1] : 0); } @@ -1736,13 +645,17 @@ void ExecuteCommandPower(uint8_t device, uint8_t state, int source) return; } else if (POWER_BLINK_STOP == state) { - uint8_t flag = (blink_mask & mask); + bool flag = (blink_mask & mask); blink_mask &= (POWER_MASK ^ mask); // Clear device mask MqttPublishPowerBlinkState(device); - if (flag) ExecuteCommandPower(device, (blink_powersave >> (device -1))&1, SRC_IGNORE); // Restore state + if (flag) { + ExecuteCommandPower(device, (blink_powersave >> (device -1))&1, SRC_IGNORE); // Restore state + } return; } - if (publish_power) MqttPublishPowerState(device); + if (publish_power) { + MqttPublishPowerState(device); + } } void StopAllPowerBlink(void) @@ -1759,162 +672,6 @@ void StopAllPowerBlink(void) } } -void SetAllPower(uint8_t state, int source) -{ - if ((POWER_ALL_OFF == state) || (POWER_ALL_ON == state)) { - power = 0; - if (POWER_ALL_ON == state) { - power = (1 << devices_present) -1; - } - SetDevicePower(power, source); - MqttPublishAllPowerState(); - } -} - -void ExecuteCommand(char *cmnd, int source) -{ - char *start; - char *token; - -#ifdef USE_DEBUG_DRIVER - ShowFreeMem(PSTR("ExecuteCommand")); -#endif - ShowSource(source); - - token = strtok(cmnd, " "); - if (token != nullptr) { - start = strrchr(token, '/'); // Skip possible cmnd/sonoff/ preamble - if (start) { token = start +1; } - } - uint16_t size = (token != nullptr) ? strlen(token) : 0; - char stopic[size +2]; // / + \0 - snprintf_P(stopic, sizeof(stopic), PSTR("/%s"), (token == nullptr) ? "" : token); - - token = strtok(nullptr, ""); - size = (token != nullptr) ? strlen(token) : 0; - char svalue[size +1]; - strlcpy(svalue, (token == nullptr) ? "" : token, sizeof(svalue)); // Fixed 5.8.0b - MqttDataHandler(stopic, (uint8_t*)svalue, strlen(svalue)); -} - -void PublishStatus(uint8_t payload) -{ - uint8_t option = STAT; - char stemp[MAX_FRIENDLYNAMES * (sizeof(Settings.friendlyname[0]) +MAX_FRIENDLYNAMES)]; - char stemp2[100]; - - // Workaround MQTT - TCP/IP stack queueing when SUB_PREFIX = PUB_PREFIX - if (!strcmp(Settings.mqtt_prefix[0],Settings.mqtt_prefix[1]) && (!payload)) { option++; } // TELE - - if ((!Settings.flag.mqtt_enabled) && (6 == payload)) { payload = 99; } - if (!energy_flg && (9 == payload)) { payload = 99; } - - if ((0 == payload) || (99 == payload)) { - uint8_t maxfn = (devices_present > MAX_FRIENDLYNAMES) ? MAX_FRIENDLYNAMES : (!devices_present) ? 1 : devices_present; - if (SONOFF_IFAN02 == my_module_type) { maxfn = 1; } - stemp[0] = '\0'; - for (uint32_t i = 0; i < maxfn; i++) { - snprintf_P(stemp, sizeof(stemp), PSTR("%s%s\"%s\"" ), stemp, (i > 0 ? "," : ""), Settings.friendlyname[i]); - } - stemp2[0] = '\0'; - for (uint32_t i = 0; i < MAX_SWITCHES; i++) { - snprintf_P(stemp2, sizeof(stemp2), PSTR("%s%s%d" ), stemp2, (i > 0 ? "," : ""), Settings.switchmode[i]); - } - Response_P(PSTR("{\"" D_CMND_STATUS "\":{\"" D_CMND_MODULE "\":%d,\"" D_CMND_FRIENDLYNAME "\":[%s],\"" D_CMND_TOPIC "\":\"%s\",\"" D_CMND_BUTTONTOPIC "\":\"%s\",\"" D_CMND_POWER "\":%d,\"" D_CMND_POWERONSTATE "\":%d,\"" D_CMND_LEDSTATE "\":%d,\"" D_CMND_LEDMASK "\":\"%04X\",\"" D_CMND_SAVEDATA "\":%d,\"" D_JSON_SAVESTATE "\":%d,\"" D_CMND_SWITCHTOPIC "\":\"%s\",\"" D_CMND_SWITCHMODE "\":[%s],\"" D_CMND_BUTTONRETAIN "\":%d,\"" D_CMND_SWITCHRETAIN "\":%d,\"" D_CMND_SENSORRETAIN "\":%d,\"" D_CMND_POWERRETAIN "\":%d}}"), - ModuleNr(), stemp, mqtt_topic, Settings.button_topic, power, Settings.poweronstate, Settings.ledstate, Settings.ledmask, Settings.save_data, Settings.flag.save_state, Settings.switch_topic, stemp2, Settings.flag.mqtt_button_retain, Settings.flag.mqtt_switch_retain, Settings.flag.mqtt_sensor_retain, Settings.flag.mqtt_power_retain); - MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS)); - } - - if ((0 == payload) || (1 == payload)) { - Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS1_PARAMETER "\":{\"" D_JSON_BAUDRATE "\":%d,\"" D_CMND_GROUPTOPIC "\":\"%s\",\"" D_CMND_OTAURL "\":\"%s\",\"" D_JSON_RESTARTREASON "\":\"%s\",\"" D_JSON_UPTIME "\":\"%s\",\"" D_JSON_STARTUPUTC "\":\"%s\",\"" D_CMND_SLEEP "\":%d,\"" D_JSON_CONFIG_HOLDER "\":%d,\"" D_JSON_BOOTCOUNT "\":%d,\"" D_JSON_SAVECOUNT "\":%d,\"" D_JSON_SAVEADDRESS "\":\"%X\"}}"), - baudrate, Settings.mqtt_grptopic, Settings.ota_url, GetResetReason().c_str(), GetUptime().c_str(), GetDateAndTime(DT_RESTART).c_str(), Settings.sleep, Settings.cfg_holder, Settings.bootcount, Settings.save_flag, GetSettingsAddress()); - MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "1")); - } - - if ((0 == payload) || (2 == payload)) { - Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS2_FIRMWARE "\":{\"" D_JSON_VERSION "\":\"%s%s\",\"" D_JSON_BUILDDATETIME "\":\"%s\",\"" D_JSON_BOOTVERSION "\":%d,\"" D_JSON_COREVERSION "\":\"" ARDUINO_ESP8266_RELEASE "\",\"" D_JSON_SDKVERSION "\":\"%s\"}}"), - my_version, my_image, GetBuildDateAndTime().c_str(), ESP.getBootVersion(), ESP.getSdkVersion()); - MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "2")); - } - - if ((0 == payload) || (3 == payload)) { - stemp2[0] = '\0'; - for (uint32_t i = 0; i < PARAM8_SIZE; i++) { - snprintf_P(stemp2, sizeof(stemp2), PSTR("%s%02X"), stemp2, Settings.param[i]); - } - Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS3_LOGGING "\":{\"" D_CMND_SERIALLOG "\":%d,\"" D_CMND_WEBLOG "\":%d,\"" D_CMND_SYSLOG "\":%d,\"" D_CMND_LOGHOST "\":\"%s\",\"" D_CMND_LOGPORT "\":%d,\"" D_CMND_SSID "\":[\"%s\",\"%s\"],\"" D_CMND_TELEPERIOD "\":%d,\"" D_JSON_RESOLUTION "\":\"%08X\",\"" D_CMND_SETOPTION "\":[\"%08X\",\"%s\",\"%08X\"]}}"), - Settings.seriallog_level, Settings.weblog_level, Settings.syslog_level, Settings.syslog_host, Settings.syslog_port, Settings.sta_ssid[0], Settings.sta_ssid[1], Settings.tele_period, Settings.flag2.data, Settings.flag.data, stemp2, Settings.flag3.data); - MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "3")); - } - - if ((0 == payload) || (4 == payload)) { - Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS4_MEMORY "\":{\"" D_JSON_PROGRAMSIZE "\":%d,\"" D_JSON_FREEMEMORY "\":%d,\"" D_JSON_HEAPSIZE "\":%d,\"" D_JSON_PROGRAMFLASHSIZE "\":%d,\"" D_JSON_FLASHSIZE "\":%d,\"" D_JSON_FLASHCHIPID "\":\"%06X\",\"" D_JSON_FLASHMODE "\":%d,\"" D_JSON_FEATURES "\":[\"%08X\",\"%08X\",\"%08X\",\"%08X\",\"%08X\"]}}"), - ESP.getSketchSize()/1024, ESP.getFreeSketchSpace()/1024, ESP.getFreeHeap()/1024, ESP.getFlashChipSize()/1024, ESP.getFlashChipRealSize()/1024, ESP.getFlashChipId(), ESP.getFlashChipMode(), LANGUAGE_LCID, feature_drv1, feature_drv2, feature_sns1, feature_sns2); - MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "4")); - } - - if ((0 == payload) || (5 == payload)) { - Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS5_NETWORK "\":{\"" D_CMND_HOSTNAME "\":\"%s\",\"" D_CMND_IPADDRESS "\":\"%s\",\"" D_JSON_GATEWAY "\":\"%s\",\"" D_JSON_SUBNETMASK "\":\"%s\",\"" D_JSON_DNSSERVER "\":\"%s\",\"" D_JSON_MAC "\":\"%s\",\"" D_CMND_WEBSERVER "\":%d,\"" D_CMND_WIFICONFIG "\":%d}}"), - my_hostname, WiFi.localIP().toString().c_str(), IPAddress(Settings.ip_address[1]).toString().c_str(), IPAddress(Settings.ip_address[2]).toString().c_str(), IPAddress(Settings.ip_address[3]).toString().c_str(), - WiFi.macAddress().c_str(), Settings.webserver, Settings.sta_config); - MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "5")); - } - - if (((0 == payload) || (6 == payload)) && Settings.flag.mqtt_enabled) { -#ifdef USE_MQTT_AWS_IOT - Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS6_MQTT "\":{\"" D_CMND_MQTTHOST "\":\"%s%s\",\"" D_CMND_MQTTPORT "\":%d,\"" D_CMND_MQTTCLIENT D_JSON_MASK "\":\"%s\",\"" D_CMND_MQTTCLIENT "\":\"%s\",\"" D_JSON_MQTT_COUNT "\":%d,\"MAX_PACKET_SIZE\":%d,\"KEEPALIVE\":%d}}"), - Settings.mqtt_user, Settings.mqtt_host, Settings.mqtt_port, Settings.mqtt_client, mqtt_client, MqttConnectCount(), MQTT_MAX_PACKET_SIZE, MQTT_KEEPALIVE); -#else - Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS6_MQTT "\":{\"" D_CMND_MQTTHOST "\":\"%s\",\"" D_CMND_MQTTPORT "\":%d,\"" D_CMND_MQTTCLIENT D_JSON_MASK "\":\"%s\",\"" D_CMND_MQTTCLIENT "\":\"%s\",\"" D_CMND_MQTTUSER "\":\"%s\",\"" D_JSON_MQTT_COUNT "\":%d,\"MAX_PACKET_SIZE\":%d,\"KEEPALIVE\":%d}}"), - Settings.mqtt_host, Settings.mqtt_port, Settings.mqtt_client, mqtt_client, Settings.mqtt_user, MqttConnectCount(), MQTT_MAX_PACKET_SIZE, MQTT_KEEPALIVE); -#endif - MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "6")); - } - - if ((0 == payload) || (7 == payload)) { - if (99 == Settings.timezone) { - snprintf_P(stemp, sizeof(stemp), PSTR("%d" ), Settings.timezone); - } else { - snprintf_P(stemp, sizeof(stemp), PSTR("\"%s\"" ), GetTimeZone().c_str()); - } -#if defined(USE_TIMERS) && defined(USE_SUNRISE) - Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS7_TIME "\":{\"" D_JSON_UTC_TIME "\":\"%s\",\"" D_JSON_LOCAL_TIME "\":\"%s\",\"" D_JSON_STARTDST "\":\"%s\",\"" D_JSON_ENDDST "\":\"%s\",\"" D_CMND_TIMEZONE "\":%s,\"" D_JSON_SUNRISE "\":\"%s\",\"" D_JSON_SUNSET "\":\"%s\"}}"), - GetTime(0).c_str(), GetTime(1).c_str(), GetTime(2).c_str(), GetTime(3).c_str(), stemp, GetSun(0).c_str(), GetSun(1).c_str()); -#else - Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS7_TIME "\":{\"" D_JSON_UTC_TIME "\":\"%s\",\"" D_JSON_LOCAL_TIME "\":\"%s\",\"" D_JSON_STARTDST "\":\"%s\",\"" D_JSON_ENDDST "\":\"%s\",\"" D_CMND_TIMEZONE "\":%s}}"), - GetTime(0).c_str(), GetTime(1).c_str(), GetTime(2).c_str(), GetTime(3).c_str(), stemp); -#endif // USE_TIMERS and USE_SUNRISE - MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "7")); - } - - if (energy_flg) { - if ((0 == payload) || (9 == payload)) { - Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS9_MARGIN "\":{\"" D_CMND_POWERDELTA "\":%d,\"" D_CMND_POWERLOW "\":%d,\"" D_CMND_POWERHIGH "\":%d,\"" D_CMND_VOLTAGELOW "\":%d,\"" D_CMND_VOLTAGEHIGH "\":%d,\"" D_CMND_CURRENTLOW "\":%d,\"" D_CMND_CURRENTHIGH "\":%d}}"), - Settings.energy_power_delta, Settings.energy_min_power, Settings.energy_max_power, Settings.energy_min_voltage, Settings.energy_max_voltage, Settings.energy_min_current, Settings.energy_max_current); - MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "9")); - } - } - - if ((0 == payload) || (8 == payload) || (10 == payload)) { - Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS10_SENSOR "\":")); - MqttShowSensor(); - ResponseJsonEnd(); - if (8 == payload) { - MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "8")); - } else { - MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "10")); - } - } - - if ((0 == payload) || (11 == payload)) { - Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS11_STATUS "\":")); - MqttShowState(); - ResponseJsonEnd(); - MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "11")); - } - -} - void MqttShowPWMState(void) { ResponseAppend_P(PSTR("\"" D_CMND_PWM "\":{")); @@ -1932,27 +689,30 @@ void MqttShowState(void) { char stemp1[33]; - ResponseAppend_P(PSTR("{\"" D_JSON_TIME "\":\"%s\",\"" D_JSON_UPTIME "\":\"%s\""), GetDateAndTime(DT_LOCAL).c_str(), GetUptime().c_str()); + ResponseAppendTime(); + ResponseAppend_P(PSTR(",\"" D_JSON_UPTIME "\":\"%s\",\"UptimeSec\":%u"), GetUptime().c_str(), UpTime()); #ifdef USE_ADC_VCC dtostrfd((double)ESP.getVcc()/1000, 3, stemp1); ResponseAppend_P(PSTR(",\"" D_JSON_VCC "\":%s"), stemp1); #endif - ResponseAppend_P(PSTR(",\"" D_JSON_HEAPSIZE "\":%d,\"SleepMode\":\"%s\",\"Sleep\":%u,\"LoadAvg\":%u"), - ESP.getFreeHeap()/1024, GetTextIndexed(stemp1, sizeof(stemp1), Settings.flag3.sleep_normal, kSleepMode), sleep, loop_load_avg); + ResponseAppend_P(PSTR(",\"" D_JSON_HEAPSIZE "\":%d,\"SleepMode\":\"%s\",\"Sleep\":%u,\"LoadAvg\":%u,\"MqttCount\":%u"), + ESP.getFreeHeap()/1024, GetTextIndexed(stemp1, sizeof(stemp1), Settings.flag3.sleep_normal, kSleepMode), sleep, loop_load_avg, MqttConnectCount()); - for (uint32_t i = 0; i < devices_present; i++) { + for (uint32_t i = 1; i <= devices_present; i++) { #ifdef USE_LIGHT - if (i == light_device -1) { - LightState(1); + if ((LightDevice()) && (i >= LightDevice())) { + if (i == LightDevice()) { LightState(1); } // call it only once } else { #endif - ResponseAppend_P(PSTR(",\"%s\":\"%s\""), GetPowerDevice(stemp1, i +1, sizeof(stemp1), Settings.flag.device_index_enable), GetStateText(bitRead(power, i))); - if (SONOFF_IFAN02 == my_module_type) { + ResponseAppend_P(PSTR(",\"%s\":\"%s\""), GetPowerDevice(stemp1, i, sizeof(stemp1), Settings.flag.device_index_enable), GetStateText(bitRead(power, i-1))); +#ifdef USE_SONOFF_IFAN + if (IsModuleIfan()) { ResponseAppend_P(PSTR(",\"" D_CMND_FANSPEED "\":%d"), GetFanspeed()); break; } +#endif // USE_SONOFF_IFAN #ifdef USE_LIGHT } #endif @@ -1979,7 +739,8 @@ void MqttPublishTeleState(void) bool MqttShowSensor(void) { - ResponseAppend_P(PSTR("{\"" D_JSON_TIME "\":\"%s\""), GetDateAndTime(DT_LOCAL).c_str()); + ResponseAppendTime(); + int json_data_start = strlen(mqtt_data); for (uint32_t i = 0; i < MAX_SWITCHES; i++) { #ifdef USE_TM1638 @@ -1992,6 +753,8 @@ bool MqttShowSensor(void) } } XsnsCall(FUNC_JSON_APPEND); + XdrvCall(FUNC_JSON_APPEND); + bool json_data_available = (strlen(mqtt_data) - json_data_start); if (strstr_P(mqtt_data, PSTR(D_JSON_PRESSURE)) != nullptr) { ResponseAppend_P(PSTR(",\"" D_JSON_PRESSURE_UNIT "\":\"%s\""), PressureUnit().c_str()); @@ -2018,6 +781,10 @@ void PerformEverySecond(void) ntp_synced_message = false; } + if (POWER_CYCLE_TIME == uptime) { + UpdateQuickPowerCycle(false); + } + if (BOOT_LOOP_TIME == uptime) { RtcReboot.fast_reboot_count = 0; RtcRebootSave(); @@ -2026,11 +793,6 @@ void PerformEverySecond(void) AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BOOT_COUNT " %d"), Settings.bootcount); } - if ((4 == uptime) && (SONOFF_IFAN02 == my_module_type)) { // Microcontroller needs 3 seconds before accepting commands - SetDevicePower(1, SRC_RETRY); // Sync with default power on state microcontroller being Light ON and Fan OFF - SetDevicePower(power, SRC_RETRY); // Set required power on state - } - if (seriallog_timer) { seriallog_timer--; if (!seriallog_timer) { @@ -2055,10 +817,14 @@ void PerformEverySecond(void) if (Settings.tele_period) { tele_period++; - if (tele_period == Settings.tele_period -1) { + // increase time for prepare and document state to ensure TELEPERIOD deliver results + if (tele_period == Settings.tele_period -3 && !prep_called) { + // sensores must be called later if driver switch on e.g. power on deepsleep + XdrvCall(FUNC_PREP_BEFORE_TELEPERIOD); XsnsCall(FUNC_PREP_BEFORE_TELEPERIOD); + prep_called = true; } - if (tele_period >= Settings.tele_period) { + if (tele_period >= Settings.tele_period && prep_called) { tele_period = 0; MqttPublishTeleState(); @@ -2070,19 +836,10 @@ void PerformEverySecond(void) RulesTeleperiod(); // Allow rule based HA messages #endif // USE_RULES } + prep_called = true; + XdrvCall(FUNC_AFTER_TELEPERIOD); } } - - XdrvCall(FUNC_EVERY_SECOND); - XsnsCall(FUNC_EVERY_SECOND); -/* - if ((2 == RtcTime.minute) && latest_uptime_flag) { - latest_uptime_flag = false; - Response_P(PSTR("{\"" D_JSON_TIME "\":\"%s\",\"" D_JSON_UPTIME "\":\"%s\"}"), GetDateAndTime(DT_LOCAL).c_str(), GetUptime().c_str()); - MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_UPTIME)); - } - if ((3 == RtcTime.minute) && !latest_uptime_flag) latest_uptime_flag = true; -*/ } /*********************************************************************************************\ @@ -2124,17 +881,6 @@ void Every100mSeconds(void) } } } - - // Backlog - if (TimeReached(backlog_delay)) { - if ((backlog_pointer != backlog_index) && !backlog_mutex) { - backlog_mutex = true; - ExecuteCommand((char*)backlog[backlog_pointer].c_str(), SRC_BACKLOG); - backlog_mutex = false; - backlog_pointer++; - if (backlog_pointer >= MAX_BACKLOG) { backlog_pointer = 0; } - } - } } /*-------------------------------------------------------------------------------------------*\ @@ -2145,7 +891,7 @@ void Every250mSeconds(void) { // As the max amount of sleep = 250 mSec this loop should always be taken... - uint8_t blinkinterval = 1; + uint32_t blinkinterval = 1; state_250mS++; state_250mS &= 0x3; @@ -2177,7 +923,7 @@ void Every250mSeconds(void) if (200 == blinks) blinks = 0; // Disable blink } } - else if (Settings.ledstate &1) { + if (Settings.ledstate &1 && (pin[GPIO_LEDLNK] < 99 || !(blinks || restart_flag || ota_state_flag)) ) { bool tstate = power & Settings.ledmask; if ((SONOFF_TOUCH == my_module_type) || (SONOFF_T11 == my_module_type) || (SONOFF_T12 == my_module_type) || (SONOFF_T13 == my_module_type)) { tstate = (!power) ? 1 : 0; // As requested invert signal for Touch devices to find them in the dark @@ -2191,9 +937,7 @@ void Every250mSeconds(void) switch (state_250mS) { case 0: // Every x.0 second - PerformEverySecond(); - - if (ota_state_flag && (backlog_pointer == backlog_index)) { + if (ota_state_flag && BACKLOG_EMPTY) { ota_state_flag--; if (2 == ota_state_flag) { ota_url = Settings.ota_url; @@ -2238,7 +982,7 @@ void Every250mSeconds(void) if (!ota_result) { #ifndef FIRMWARE_MINIMAL int ota_error = ESPhttpUpdate.getLastError(); -// AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPLOAD "Ota error %d"), ota_error); + DEBUG_CORE_LOG(PSTR("OTA: Error %d"), ota_error); if ((HTTP_UE_TOO_LESS_SPACE == ota_error) || (HTTP_UE_BIN_FOR_WRONG_FLASH == ota_error)) { RtcSettings.ota_loader = 1; // Try minimal image next } @@ -2264,7 +1008,7 @@ void Every250mSeconds(void) if (MidnightNow()) { XsnsCall(FUNC_SAVE_AT_MIDNIGHT); } - if (save_data_counter && (backlog_pointer == backlog_index)) { + if (save_data_counter && BACKLOG_EMPTY) { save_data_counter--; if (save_data_counter <= 0) { if (Settings.flag.save_state) { @@ -2284,7 +1028,7 @@ void Every250mSeconds(void) save_data_counter = Settings.save_data; } } - if (restart_flag && (backlog_pointer == backlog_index)) { + if (restart_flag && BACKLOG_EMPTY) { if ((214 == restart_flag) || (215 == restart_flag) || (216 == restart_flag)) { char storage_wifi[sizeof(Settings.sta_ssid) + sizeof(Settings.sta_pwd)]; @@ -2469,6 +1213,7 @@ void SerialInput(void) } } +#ifdef USE_SONOFF_SC /*-------------------------------------------------------------------------------------------*\ * Sonoff SC 19200 baud serial interface \*-------------------------------------------------------------------------------------------*/ @@ -2480,11 +1225,11 @@ void SerialInput(void) Serial.flush(); return; } - } - + } else +#endif // USE_SONOFF_SC /*-------------------------------------------------------------------------------------------*/ - else if (!Settings.flag.mqtt_serial && (serial_in_byte == '\n')) { + if (!Settings.flag.mqtt_serial && (serial_in_byte == '\n')) { serial_in_buffer[serial_in_byte_counter] = 0; // Serial data completed seriallog_level = (Settings.seriallog_level < LOG_LEVEL_INFO) ? (uint8_t)LOG_LEVEL_INFO : Settings.seriallog_level; AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_COMMAND "%s"), serial_in_buffer); @@ -2498,15 +1243,9 @@ void SerialInput(void) if (Settings.flag.mqtt_serial && serial_in_byte_counter && (millis() > (serial_polling_window + SERIAL_POLLING))) { serial_in_buffer[serial_in_byte_counter] = 0; // Serial data completed - if (!Settings.flag.mqtt_serial_raw) { - Response_P(PSTR("{\"" D_JSON_SERIALRECEIVED "\":\"%s\"}"), serial_in_buffer); - } else { - Response_P(PSTR("{\"" D_JSON_SERIALRECEIVED "\":\"")); - for (uint32_t i = 0; i < serial_in_byte_counter; i++) { - ResponseAppend_P(PSTR("%02x"), serial_in_buffer[i]); - } - ResponseAppend_P(PSTR("\"}")); - } + char hex_char[(serial_in_byte_counter * 2) + 2]; + Response_P(PSTR(",\"" D_JSON_SERIALRECEIVED "\":\"%s\"}"), + (Settings.flag.mqtt_serial_raw) ? ToHex_P((unsigned char*)serial_in_buffer, serial_in_byte_counter, hex_char, sizeof(hex_char)) : serial_in_buffer); MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_SERIALRECEIVED)); XdrvRulesProcess(); serial_in_byte_counter = 0; @@ -2517,10 +1256,10 @@ void SerialInput(void) void GpioInit(void) { - uint8_t mpin; + uint32_t mpin; if (!ValidModule(Settings.module)) { - uint8_t module = MODULE; + uint32_t module = MODULE; if (!ValidModule(MODULE)) { module = SONOFF_BASIC; } Settings.module = module; Settings.last_module = module; @@ -2557,7 +1296,7 @@ void GpioInit(void) my_adc0 = Settings.my_adc0; // Set User selected Module sensors } my_module_flag = ModuleFlag(); - uint8_t template_adc0 = my_module_flag.data &15; + uint32_t template_adc0 = my_module_flag.data &15; if ((template_adc0 > ADC0_NONE) && (template_adc0 < ADC0_USER)) { my_adc0 = template_adc0; // Force Template override } @@ -2568,9 +1307,12 @@ void GpioInit(void) for (uint32_t i = 0; i < sizeof(my_module.io); i++) { mpin = ValidPin(i, my_module.io[i]); -// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DBG: gpio pin %d, mpin %d"), i, mpin); + DEBUG_CORE_LOG(PSTR("INI: gpio pin %d, mpin %d"), i, mpin); if (mpin) { + XdrvMailbox.index = mpin; + XdrvMailbox.payload = i; + if ((mpin >= GPIO_SWT1_NP) && (mpin < (GPIO_SWT1_NP + MAX_SWITCHES))) { SwitchPullupFlag(mpin - GPIO_SWT1_NP); mpin -= (GPIO_SWT1_NP - GPIO_SWT1); @@ -2604,20 +1346,12 @@ void GpioInit(void) bitSet(pwm_inverted, mpin - GPIO_PWM1_INV); mpin -= (GPIO_PWM1_INV - GPIO_PWM1); } - else if ((mpin >= GPIO_CNTR1_NP) && (mpin < (GPIO_CNTR1_NP + MAX_COUNTERS))) { - bitSet(counter_no_pullup, mpin - GPIO_CNTR1_NP); - mpin -= (GPIO_CNTR1_NP - GPIO_CNTR1); + else if (XdrvCall(FUNC_PIN_STATE)) { + mpin = XdrvMailbox.index; } -#ifdef USE_DHT - else if ((mpin >= GPIO_DHT11) && (mpin <= GPIO_SI7021)) { - if (DhtSetup(i, mpin)) { - dht_flg = true; - mpin = GPIO_DHT11; - } else { - mpin = 0; - } - } -#endif // USE_DHT + else if (XsnsCall(FUNC_PIN_STATE)) { + mpin = XdrvMailbox.index; + }; } if (mpin) pin[mpin] = i; } @@ -2645,30 +1379,19 @@ void GpioInit(void) #ifdef USE_I2C i2c_flg = ((pin[GPIO_I2C_SCL] < 99) && (pin[GPIO_I2C_SDA] < 99)); - if (i2c_flg) { Wire.begin(pin[GPIO_I2C_SDA], pin[GPIO_I2C_SCL]); } + if (i2c_flg) { + Wire.begin(pin[GPIO_I2C_SDA], pin[GPIO_I2C_SCL]); + } #endif // USE_I2C - devices_present = 1; - + devices_present = 0; light_type = LT_BASIC; // Use basic PWM control if SetOption15 = 0 -#ifdef USE_LIGHT - if (Settings.flag.pwm_control) { - for (uint32_t i = 0; i < MAX_PWMS; i++) { - if (pin[GPIO_PWM1 +i] < 99) { light_type++; } // Use Dimmer/Color control for all PWM as SetOption15 = 1 - } - } -#endif // USE_LIGHT - - if (SONOFF_BRIDGE == my_module_type) { - Settings.flag.mqtt_serial = 0; - baudrate = 19200; - } - if (XdrvCall(FUNC_MODULE_INIT)) { // Serviced } else if (YTF_IR_BRIDGE == my_module_type) { ClaimSerial(); // Stop serial loopback mode +// devices_present = 1; } else if (SONOFF_DUAL == my_module_type) { Settings.flag.mqtt_serial = 0; @@ -2680,35 +1403,30 @@ void GpioInit(void) devices_present = 4; baudrate = 19200; } +#ifdef USE_SONOFF_SC else if (SONOFF_SC == my_module_type) { Settings.flag.mqtt_serial = 0; devices_present = 0; baudrate = 19200; } -#ifdef USE_LIGHT - else if (SONOFF_BN == my_module_type) { // PWM Single color led (White) - light_type = LT_PWM1; +#endif // USE_SONOFF_SC + + if (!light_type) { + for (uint32_t i = 0; i < MAX_PWMS; i++) { // Basic PWM control only + if (pin[GPIO_PWM1 +i] < 99) { + pwm_present = true; + pinMode(pin[GPIO_PWM1 +i], OUTPUT); + analogWrite(pin[GPIO_PWM1 +i], bitRead(pwm_inverted, i) ? Settings.pwm_range - Settings.pwm_value[i] : Settings.pwm_value[i]); + } + } } - else if (SONOFF_LED == my_module_type) { // PWM Dual color led (White warm and cold) - light_type = LT_PWM2; - } - else if (AILIGHT == my_module_type) { // RGBW led - light_type = LT_RGBW; - } - else if (SONOFF_B1 == my_module_type) { // RGBWC led - light_type = LT_RGBWC; - } -#endif // USE_LIGHT - else { - if (!light_type) { devices_present = 0; } - for (uint32_t i = 0; i < MAX_RELAYS; i++) { - if (pin[GPIO_REL1 +i] < 99) { - pinMode(pin[GPIO_REL1 +i], OUTPUT); - devices_present++; - if (EXS_RELAY == my_module_type) { - digitalWrite(pin[GPIO_REL1 +i], bitRead(rel_inverted, i) ? 1 : 0); - if (i &1) { devices_present--; } - } + for (uint32_t i = 0; i < MAX_RELAYS; i++) { + if (pin[GPIO_REL1 +i] < 99) { + pinMode(pin[GPIO_REL1 +i], OUTPUT); + devices_present++; + if (EXS_RELAY == my_module_type) { + digitalWrite(pin[GPIO_REL1 +i], bitRead(rel_inverted, i) ? 1 : 0); + if (i &1) { devices_present--; } } } } @@ -2740,30 +1458,6 @@ void GpioInit(void) RotaryInit(); #endif -#ifdef USE_LIGHT -#ifdef USE_WS2812 - if (!light_type && (pin[GPIO_WS2812] < 99)) { // RGB led - devices_present++; - light_type = LT_WS2812; - } -#endif // USE_WS2812 -#ifdef USE_SM16716 - if (SM16716_ModuleSelected()) { - light_type += 3; - light_type |= LT_SM16716; - } -#endif // USE_SM16716 -#endif // USE_LIGHT - if (!light_type) { - for (uint32_t i = 0; i < MAX_PWMS; i++) { // Basic PWM control only - if (pin[GPIO_PWM1 +i] < 99) { - pwm_present = true; - pinMode(pin[GPIO_PWM1 +i], OUTPUT); - analogWrite(pin[GPIO_PWM1 +i], bitRead(pwm_inverted, i) ? Settings.pwm_range - Settings.pwm_value[i] : Settings.pwm_value[i]); - } - } - } - SetLedPower(Settings.ledstate &8); SetLedLink(Settings.ledstate &8); @@ -2779,7 +1473,9 @@ void setup(void) global_state.data = 3; // Init global state (wifi_down, mqtt_down) to solve possible network issues RtcRebootLoad(); - if (!RtcRebootValid()) { RtcReboot.fast_reboot_count = 0; } + if (!RtcRebootValid()) { + RtcReboot.fast_reboot_count = 0; + } RtcReboot.fast_reboot_count++; RtcRebootSave(); @@ -2803,10 +1499,11 @@ void setup(void) GetFeatures(); if (1 == RtcReboot.fast_reboot_count) { // Allow setting override only when all is well + UpdateQuickPowerCycle(true); XdrvCall(FUNC_SETTINGS_OVERRIDE); } - baudrate = Settings.baudrate * 1200; + baudrate = Settings.baudrate * 300; // mdns_delayed_start = Settings.param[P_MDNS_DELAYED_START]; seriallog_level = Settings.seriallog_level; seriallog_timer = SERIALLOG_TIMER; @@ -2932,7 +1629,22 @@ void setup(void) XsnsCall(FUNC_INIT); } -uint32_t _counter = 0; +static void BacklogLoop() +{ + if (TimeReached(backlog_delay)) { + if (!BACKLOG_EMPTY && !backlog_mutex) { + backlog_mutex = true; +#ifdef SUPPORT_IF_STATEMENT + ExecuteCommand((char*)backlog.shift().c_str(), SRC_BACKLOG); +#else + ExecuteCommand((char*)backlog[backlog_pointer].c_str(), SRC_BACKLOG); + backlog_pointer++; + if (backlog_pointer >= MAX_BACKLOG) { backlog_pointer = 0; } +#endif + backlog_mutex = false; + } + } +} void loop(void) { @@ -2948,6 +1660,7 @@ void loop(void) #ifdef ROTARY_V1 RotaryLoop(); #endif + BacklogLoop(); if (TimeReached(state_50msecond)) { SetNextTimeInterval(state_50msecond, 50); @@ -2966,6 +1679,12 @@ void loop(void) XdrvCall(FUNC_EVERY_250_MSECOND); XsnsCall(FUNC_EVERY_250_MSECOND); } + if (TimeReached(state_second)) { + SetNextTimeInterval(state_second, 1000); + PerformEverySecond(); + XdrvCall(FUNC_EVERY_SECOND); + XsnsCall(FUNC_EVERY_SECOND); + } if (!serial_local) { SerialInput(); } diff --git a/sonoff/sonoff_aws_iot.cpp b/sonoff/sonoff_aws_iot.cpp deleted file mode 100644 index 8f44faf20..000000000 --- a/sonoff/sonoff_aws_iot.cpp +++ /dev/null @@ -1,152 +0,0 @@ -/* - sonoff_aws_iot.cpp - TLS AWS IoT for Sonoff-Tasmota - Private key - - Copyright (C) 2019 Theo Arends - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#include "my_user_config.h" -#ifdef USE_MQTT_AWS_IOT - -#include -#include - -// nasty hack to force PROGMEM -#define static static PROGMEM - -namespace aws_iot_privkey { -/*********************************************************************************************\ - * Private key for AWS IoT - * - * Create the private key, generate the CSR and sign it with AWS IoT Console. - * - * Then generate C version of Private Key and Certificate, cut and paste below - * - * Downloaded from https://www.identrust.com/support/downloads -\*********************************************************************************************/ - -/*********************************************************************************************\ - * Export Private Key as a C structure - * - * $ bearssl skey -C -\*********************************************************************************************/ - -/* --------------- CUT AND PASTE PRIVATE KEY BELOW --------------- */ -/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */ - -static const unsigned char EC_X[] = { - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77 -}; - -static const br_ec_private_key EC = { - 23, - (unsigned char *)EC_X, sizeof EC_X -}; - -/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */ -/* --------------- CUT AND PASTE PRIVATE KEY ABOVE --------------- */ - -/*********************************************************************************************\ - * Export Private Key as a C structure - * - * $ bearssl chain -\*********************************************************************************************/ - -/* --------------- CUT AND PASTE PRIVATE KEY BELOW --------------- */ -/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */ - -static const unsigned char CERT0[] = { - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77 -}; - -static const br_x509_certificate CHAIN[] = { - { (unsigned char *)CERT0, sizeof CERT0 } -}; - -#define CHAIN_LEN 1 - -/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */ -/* --------------- CUT AND PASTE PRIVATE KEY ABOVE --------------- */ - -const br_ec_private_key *AWS_IoT_Private_Key = &EC; -const br_x509_certificate *AWS_IoT_Client_Certificate = &CHAIN[0]; - -} - -#endif // USE_MQTT_AWS_IOT diff --git a/sonoff/sonoff_ca.ino b/sonoff/sonoff_ca.ino index ed3998443..3dea12c9e 100644 --- a/sonoff/sonoff_ca.ino +++ b/sonoff/sonoff_ca.ino @@ -30,9 +30,9 @@ * https://letsencrypt.org/certificates/ * Downloaded from https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.pem.txt * - * to convert do: ‘bearssl ta lets-encrypt-x3-cross-signed.pem.txt’ + * to convert do: "bearssl ta lets-encrypt-x3-cross-signed.pem.txt" * then copy and paste below, chain the generic names to the same as below - * remove ‘static’ and add ‘PROGMEM’ + * remove "static" and add "PROGMEM" \*********************************************************************************************/ static const unsigned char PROGMEM TA0_DN[] = { @@ -97,9 +97,9 @@ static const br_x509_trust_anchor PROGMEM LetsEncryptX3CrossSigned_TA = { * https://letsencrypt.org/certificates/ * Downloaded from https://www.amazontrust.com/repository/AmazonRootCA1.pem * - * to convert do: ‘bearssl ta AmazonRootCA1.pem’ + * to convert do: "bearssl ta AmazonRootCA1.pem" * then copy and paste below, chain the generic names to the same as below - * remove ‘static’ and add ‘PROGMEM’ + * remove "static" and add "PROGMEM" \*********************************************************************************************/ diff --git a/sonoff/sonoff_post.h b/sonoff/sonoff_post.h index 1f25c308a..b8b282fa7 100755 --- a/sonoff/sonoff_post.h +++ b/sonoff/sonoff_post.h @@ -24,15 +24,11 @@ * Function declarations \*********************************************************************************************/ +// Needed for core 2.3.0 compilation (#6721) #ifdef __cplusplus extern "C" { #endif - #include "user_interface.h" - -// Function prototypes -void WifiWpsStatusCallback(wps_cb_status status); - #ifdef __cplusplus } #endif @@ -42,10 +38,16 @@ void WifiWpsStatusCallback(wps_cb_status status); void KNX_CB_Action(message_t const &msg, void *arg); //#endif // USE_KNX +char* ToHex_P(const unsigned char * in, size_t insz, char * out, size_t outsz, char inbetween = '\0'); + /*********************************************************************************************\ * Default global defines \*********************************************************************************************/ +#ifndef ENERGY_OVERTEMP +#define ENERGY_OVERTEMP 90 // Overtemp in Celsius +#endif + #ifdef USE_EMULATION_HUE #define USE_EMULATION #endif @@ -54,9 +56,9 @@ void KNX_CB_Action(message_t const &msg, void *arg); #endif #ifdef USE_MQTT_TLS - const uint16_t WEB_LOG_SIZE = 2000; // Max number of characters in weblog + const uint16_t WEB_LOG_SIZE = 2000; // Max number of characters in weblog #else - const uint16_t WEB_LOG_SIZE = 4000; // Max number of characters in weblog + const uint16_t WEB_LOG_SIZE = 4000; // Max number of characters in weblog #endif #if defined(USE_MQTT_TLS) && defined(ARDUINO_ESP8266_RELEASE_2_3_0) @@ -64,7 +66,7 @@ void KNX_CB_Action(message_t const &msg, void *arg); #endif #ifndef MODULE -#define MODULE SONOFF_BASIC // [Module] Select default model +#define MODULE SONOFF_BASIC // [Module] Select default model #endif /*********************************************************************************************\ @@ -74,164 +76,142 @@ void KNX_CB_Action(message_t const &msg, void *arg); #ifdef FIRMWARE_SENSORS -#undef CODE_IMAGE -#define CODE_IMAGE 3 - -#define USE_COUNTER // Enable counters -#undef USE_ADC_VCC // Add Analog input on selected devices -#define USE_DS18x20 // For more than one DS18x20 sensors with id sort, single scan and read retry (+1k3 code) -//#define USE_DS18x20_LEGACY // For more than one DS18x20 sensors with dynamic scan using library OneWire (+1k5 code) - -#define USE_I2C // I2C using library wire (+10k code, 0k2 mem, 124 iram) -#define USE_SHT // Add I2C emulating code for SHT1X sensor (+1k4 code) -#define USE_HTU // Add I2C code for HTU21/SI7013/SI7020/SI7021 sensor (+1k5 code) -#define USE_BMP // Add I2C code for BMP085/BMP180/BMP280/BME280 sensor (+4k code) - #define USE_BME680 // Add additional support for BME680 sensor using Bosch BME680 library (+4k code) -#define USE_BH1750 // Add I2C code for BH1750 sensor (+0k5 code) -#define USE_VEML6070 // Add I2C code for VEML6070 sensor (+0k5 code) -#define USE_ADS1115 // Add I2C code for ADS1115 16 bit A/D converter based on Adafruit ADS1x15 library (no library needed) (+0k7 code) -//#define USE_ADS1115_I2CDEV // Add I2C code for ADS1115 16 bit A/D converter using library i2cdevlib-Core and i2cdevlib-ADS1115 (+2k code) -#define USE_INA219 // Add I2C code for INA219 Low voltage and current sensor (+1k code) -#define USE_SHT3X // Add I2C code for SHT3x sensor (+0k6 code) -#define USE_TSL2561 // Add I2C code for TSL2561 sensor using library Adafruit TSL2561 Arduino (+1k2 code) -#define USE_MGS // Add I2C code for Xadow and Grove Mutichannel Gas sensor using library Multichannel_Gas_Sensor (+10k code) -#define USE_SGP30 // Add I2C code for SGP30 sensor (+1k1 code) -//#define USE_SI1145 // Add I2C code for SI1145/46/47 sensor (+1k code) -#define USE_LM75AD // Add I2C code for LM75AD sensor (+0k5 code) -//#define USE_APDS9960 // Add I2C code for APDS9960 Proximity Sensor. Disables SHT and VEML6070 (+4k7 code) -//#define USE_MCP230xx // Enable MCP23008/MCP23017 - Must define I2C Address in #define USE_MCP230xx_ADDR below - range 0x20 - 0x27 (+4k7 code) -// #define USE_MCP230xx_ADDR 0x20 // Enable MCP23008/MCP23017 I2C Address to use (Must be within range 0x20 through 0x27 - set according to your wired setup) -// #define USE_MCP230xx_OUTPUT // Enable MCP23008/MCP23017 OUTPUT support through sensor29 commands (+1k5 code) -// #define USE_MCP230xx_DISPLAYOUTPUT // Enable MCP23008/MCP23017 to display state of OUTPUT pins on Web UI (+0k2 code) -//#define USE_PCA9685 // Enable PCA9685 I2C HW PWM Driver - Must define I2C Address in #define USE_PCA9685_ADDR below - range 0x40 - 0x47 (+1k4 code) -// #define USE_PCA9685_ADDR 0x40 // Enable PCA9685 I2C Address to use (Must be within range 0x40 through 0x47 - set according to your wired setup) -// #define USE_PCA9685_FREQ 50 // Define default PWM frequency in Hz to be used (must be within 24 to 1526) - If other value is used, it will rever to 50Hz -//#define USE_MPR121 // Enable MPR121 controller (I2C addresses 0x5A, 0x5B, 0x5C and 0x5D) in input mode for touch buttons (+1k3 code) -//#define USE_CCS811 // Add I2C code for CCS811 sensor (+2k2 code) -//#define USE_MPU6050 // Enable MPU6050 sensor (I2C address 0x68 AD0 low or 0x69 AD0 high) (+2k6 code) -//#define USE_DS3231 // Enable DS3231 external RTC in case no Wifi is avaliable. See docs in the source file (+1k2 code) -//#define USE_MGC3130 // Enable MGC3130 Electric Field Effect Sensor (I2C address 0x42) (+2k7 code, 0k3 mem) -//#define USE_MAX44009 // Enable MAX44009 Ambient Light sensor (I2C addresses 0x4A and 0x4B) (+0k8 code) -#define USE_SCD30 // Enable Sensiron SCd30 CO2 sensor (I2C address 0x61) (+3k3 code) -//#define USE_SPS30 // Enable Sensiron SPS30 particle sensor (I2C address 0x69) (+1.7 code) -#define USE_ADE7953 // Enable ADE7953 Energy monitor as used on Shelly 2.5 (I2C address 0x38) (+1k5) -//#define USE_VL53L0X // Enable VL53L0x time of flight sensor (I2C address 0x29) (+4k code) -//#define USE_MLX90614 // Enable MLX90614 ir temp sensor (I2C address 0x5a) (+0.6k code) - -#define USE_MHZ19 // Add support for MH-Z19 CO2 sensor (+2k code) -#define USE_SENSEAIR // Add support for SenseAir K30, K70 and S8 CO2 sensor (+2k3 code) -#ifndef CO2_LOW - #define CO2_LOW 800 // Below this CO2 value show green light (needs PWM or WS2812 RG(B) led and enable with SetOption18 1) -#endif -#ifndef CO2_HIGH - #define CO2_HIGH 1200 // Above this CO2 value show red light (needs PWM or WS2812 RG(B) led and enable with SetOption18 1) -#endif -#define USE_PMS5003 // Add support for PMS5003 and PMS7003 particle concentration sensor (+1k3 code) -#define USE_NOVA_SDS // Add support for SDS011 and SDS021 particle concentration sensor (+0k7 code) -#define USE_SERIAL_BRIDGE // Add support for software Serial Bridge (+0k8 code) -#define USE_SDM120 // Add support for Eastron SDM120-Modbus energy meter (+1k7 code) -#define USE_SDM630 // Add support for Eastron SDM630-Modbus energy meter (+2k code) -#define USE_MP3_PLAYER // Use of the DFPlayer Mini MP3 Player RB-DFR-562 commands: play, volume and stop - #define MP3_VOLUME 10 // Set the startup volume on init, the range can be 0..30(max) -#define USE_TUYA_DIMMER // Add support for Tuya Serial Dimmer -#ifndef TUYA_DIMMER_ID - #define TUYA_DIMMER_ID 0 // Default dimmer Id -#endif -#define USE_PS_16_DZ // Add support for PS-16-DZ Dimmer and Sonoff L1 (+2k code) -//#define USE_AZ7798 // Add support for AZ-Instrument 7798 CO2 datalogger -#define USE_PN532_HSU // Add support for PN532 using HSU (Serial) interface (+1k8 code, 140 bytes mem) -#define USE_PZEM004T // Add support for PZEM004T Energy monitor (+2k code) -#define USE_PZEM_AC // Add support for PZEM014,016 Energy monitor (+1k1 code) -#define USE_PZEM_DC // Add support for PZEM003,017 Energy monitor (+1k1 code) -#define USE_MCP39F501 // Add support for MCP39F501 Energy monitor as used in Shelly 2 (+3k1 code) -#define USE_DHT // Add support for DHT11, AM2301 (DHT21, DHT22, AM2302, AM2321) and SI7021 Temperature and Humidity sensor -#define USE_MAX31855 // Add support for MAX31855 K-Type thermocouple sensor using softSPI -#define USE_IR_REMOTE // Send IR remote commands using library IRremoteESP8266 and ArduinoJson (+4k code, 0k3 mem, 48 iram) - #define USE_IR_HVAC // Support for HVAC system using IR (+2k code) - #define USE_IR_RECEIVE // Support for IR receiver (+5k5 code, 264 iram) -#define USE_WS2812 // WS2812 Led string using library NeoPixelBus (+5k code, +1k mem, 232 iram) - Disable by // -#ifndef USE_WS2812_CTYPE - #define USE_WS2812_CTYPE NEO_GRB // WS2812 Color type (NEO_RGB, NEO_GRB, NEO_BRG, NEO_RBG, NEO_RGBW, NEO_GRBW) -#endif -// #define USE_WS2812_DMA // DMA supports only GPIO03 (= Serial RXD) (+1k mem). When USE_WS2812_DMA is enabled expect Exceptions on Pow -#define USE_ARILUX_RF // Add support for Arilux RF remote controller (+0k8 code, 252 iram (non 2.3.0)) -#define USE_SR04 // Add support for HC-SR04 ultrasonic devices (+1k code) -#define USE_TM1638 // Add support for TM1638 switches copying Switch1 .. Switch8 (+1k code) -#define USE_HX711 // Add support for HX711 load cell (+1k5 code) -//#define USE_HX711_GUI // Add optional web GUI to HX711 as scale (+1k8 code) -#define USE_RF_FLASH // Add support for flashing the EFM8BB1 chip on the Sonoff RF Bridge. C2CK must be connected to GPIO4, C2D to GPIO5 on the PCB (+3k code) -#define USE_TX20_WIND_SENSOR // Add support for La Crosse TX20 anemometer (+2k code) -#define USE_RC_SWITCH // Add support for RF transceiver using library RcSwitch (+2k7 code, 460 iram) -#define USE_RF_SENSOR // Add support for RF sensor receiver (434MHz or 868MHz) (+0k8 code) -// #define USE_THEO_V2 // Add support for decoding Theo V2 sensors as documented on https://sidweb.nl using 434MHz RF sensor receiver (+1k4 code) - #define USE_ALECTO_V2 // Add support for decoding Alecto V2 sensors like ACH2010, WS3000 and DKW2012 using 868MHz RF sensor receiver (+1k7 code) -#define USE_SM16716 // Add support for SM16716 RGB LED controller (+0k7 code) -#define USE_HRE // Add support for Badger HR-E Water Meter (+1k4 code) -#endif // FIRMWARE_SENSORS - -/*********************************************************************************************\ - * [sonoff-classic.bin] - * Provide an image close to version 5.12.0 but still within 499k program space to allow one time OTA -\*********************************************************************************************/ - -#ifdef FIRMWARE_CLASSIC - #undef CODE_IMAGE #define CODE_IMAGE 2 -#define USE_ADC_VCC // Display Vcc in Power status. Disable for use as Analog input on selected devices -#ifndef USE_WPS -#define USE_WPS // Add support for WPS as initial wifi configuration tool (+33k code, 1k mem (5k mem with core v2.4.2+)) +#undef USE_DISCOVERY // Disable mDNS (+8k code or +23.5k code with core 2_5_x, +0.3k mem) + +// -- Optional modules ---------------------------- +//#define ROTARY_V1 // Add support for MI Desk Lamp +#define USE_SONOFF_RF // Add support for Sonoff Rf Bridge (+3k2 code) + #define USE_RF_FLASH // Add support for flashing the EFM8BB1 chip on the Sonoff RF Bridge. C2CK must be connected to GPIO4, C2D to GPIO5 on the PCB (+2k7 code) +#define USE_SONOFF_SC // Add support for Sonoff Sc (+1k1 code) +#define USE_TUYA_MCU // Add support for Tuya Serial MCU +#ifndef TUYA_DIMMER_ID + #define TUYA_DIMMER_ID 0 // Default dimmer Id #endif -#ifndef USE_SMARTCONFIG -#define USE_SMARTCONFIG // Add support for Wifi SmartConfig as initial wifi configuration tool (+23k code, +0.6k mem) +//#define USE_ARMTRONIX_DIMMERS // Add support for Armtronix Dimmers (+1k4 code) +#define USE_PS_16_DZ // Add support for PS-16-DZ Dimmer (+2k code) +#define USE_SONOFF_IFAN // Add support for Sonoff iFan02 and iFan03 (+2k code) +#define USE_BUZZER // Add support for a buzzer (+0k6 code) +#define USE_ARILUX_RF // Add support for Arilux RF remote controller (+0k8 code, 252 iram (non 2.3.0)) +//#define USE_SHUTTER // Add Shutter support for up to 4 shutter with different motortypes (+6k code) +//#define USE_DEEPSLEEP // Add support for deepsleep (+1k code) +//#define USE_EXS_DIMMER // Add support for EX-Store WiFi Dimmer + +// -- Optional light modules ---------------------- +#define USE_LIGHT // Add Dimmer/Light support +#define USE_WS2812 // WS2812 Led string using library NeoPixelBus (+5k code, +1k mem, 232 iram) - Disable by // +// #define USE_WS2812_DMA // DMA supports only GPIO03 (= Serial RXD) (+1k mem). When USE_WS2812_DMA is enabled expect Exceptions on Pow +#ifndef USE_WS2812_HARDWARE + #define USE_WS2812_HARDWARE NEO_HW_WS2812 // Hardware type (NEO_HW_WS2812, NEO_HW_WS2812X, NEO_HW_WS2813, NEO_HW_SK6812, NEO_HW_LC8812, NEO_HW_APA106) #endif -#undef USE_ARDUINO_OTA // Disable support for Arduino OTA -//#undef USE_DOMOTICZ // Disable Domoticz -#undef USE_HOME_ASSISTANT // Disable Home Assistant -#undef USE_KNX // Disable KNX IP Protocol Support -#undef USE_CUSTOM // Disable Custom features -#undef USE_TIMERS // Disable support for up to 16 timers -#undef USE_TIMERS_WEB // Disable support for timer webpage -#undef USE_SUNRISE // Disable support for Sunrise and sunset tools -#undef USE_RULES // Disable support for rules -#undef USE_COUNTER // Disable counters -#undef USE_I2C // Disable all I2C sensors -#undef USE_SPI // Disable all SPI devices -#undef USE_MHZ19 // Disable support for MH-Z19 CO2 sensor -#undef USE_SENSEAIR // Disable support for SenseAir K30, K70 and S8 CO2 sensor -#undef USE_PMS5003 // Disable support for PMS5003 and PMS7003 particle concentration sensor -#undef USE_NOVA_SDS // Disable support for SDS011 and SDS021 particle concentration sensor -#undef USE_SERIAL_BRIDGE // Disable support for software Serial Bridge -#undef USE_SDM120 // Disable support for Eastron SDM120-Modbus energy meter -#undef USE_SDM630 // Disable support for Eastron SDM630-Modbus energy meter -#undef USE_MP3_PLAYER // Disable DFPlayer Mini MP3 Player RB-DFR-562 commands: play, volume and stop -#undef USE_TUYA_DIMMER // Disable support for Tuya Serial Dimmer -#undef USE_ARMTRONIX_DIMMERS // Disable support for Armtronix Dimmers (+1k4 code) -#undef USE_PS_16_DZ // Disable support for PS-16-DZ Dimmer and Sonoff L1 (+2k code) -#undef USE_AZ7798 // Disable support for AZ-Instrument 7798 CO2 datalogger -#undef USE_PN532_HSU // Disable support for PN532 using HSU (Serial) interface (+1k8 code, 140 bytes mem) -#undef USE_PZEM004T // Disable PZEM004T energy sensor -#undef USE_PZEM_AC // Disable PZEM014,016 Energy monitor -#undef USE_PZEM_DC // Disable PZEM003,017 Energy monitor -#undef USE_MCP39F501 // Disable support for MCP39F501 Energy monitor as used in Shelly 2 (+3k1 code) -#define USE_DHT // Add support for DHT11, AM2301 (DHT21, DHT22, AM2302, AM2321) and SI7021 Temperature and Humidity sensor -#undef USE_MAX31855 // Disable MAX31855 K-Type thermocouple sensor using softSPI -#undef USE_IR_REMOTE // Disable IR remote commands using library IRremoteESP8266 and ArduinoJson -#undef USE_IR_RECEIVE // Disable support for IR receiver -#undef USE_ARILUX_RF // Disable support for Arilux RF remote controller -#undef USE_SR04 // Disable support for for HC-SR04 ultrasonic devices -#undef USE_TM1638 // Disable support for TM1638 switches copying Switch1 .. Switch8 -#undef USE_HX711 // Disable support for HX711 load cell -#undef USE_RF_FLASH // Disable support for flashing the EFM8BB1 chip on the Sonoff RF Bridge. C2CK must be connected to GPIO4, C2D to GPIO5 on the PCB -#undef USE_TX20_WIND_SENSOR // Disable support for La Crosse TX20 anemometer -#undef USE_RC_SWITCH // Disable support for RF transceiver using library RcSwitch -#undef USE_RF_SENSOR // Disable support for RF sensor receiver (434MHz or 868MHz) (+0k8 code) -#undef USE_SM16716 // Disable support for SM16716 RGB LED controller (+0k7 code) -#undef USE_HRE // Disable support for Badger HR-E Water Meter (+1k4 code) -#undef DEBUG_THEO // Disable debug code -#undef USE_DEBUG_DRIVER // Disable debug code -#endif // FIRMWARE_CLASSIC +#ifndef USE_WS2812_CTYPE + #define USE_WS2812_CTYPE NEO_GRB // Color type (NEO_RGB, NEO_GRB, NEO_BRG, NEO_RBG, NEO_RGBW, NEO_GRBW) +#endif +#define USE_MY92X1 // Add support for MY92X1 RGBCW led controller as used in Sonoff B1, Ailight and Lohas +#define USE_SM16716 // Add support for SM16716 RGB LED controller (+0k7 code) +#define USE_SM2135 // Add support for SM2135 RGBCW led control as used in Action LSC (+0k6 code) +#define USE_SONOFF_L1 // Add support for Sonoff L1 led control + +#define USE_COUNTER // Enable counters +#undef USE_ADC_VCC // Add Analog input on selected devices +#define USE_DS18x20 // Add support for DS18x20 sensors with id sort, single scan and read retry (+1k3 code) + +#define USE_I2C // I2C using library wire (+10k code, 0k2 mem, 124 iram) +#define USE_SHT // Add I2C emulating code for SHT1X sensor (+1k4 code) +#define USE_HTU // Add I2C code for HTU21/SI7013/SI7020/SI7021 sensor (+1k5 code) +#define USE_BMP // Add I2C code for BMP085/BMP180/BMP280/BME280 sensor (+4k code) + #define USE_BME680 // Add additional support for BME680 sensor using Bosch BME680 library (+4k code) +#define USE_BH1750 // Add I2C code for BH1750 sensor (+0k5 code) +#define USE_VEML6070 // Add I2C code for VEML6070 sensor (+0k5 code) +#define USE_ADS1115 // Add I2C code for ADS1115 16 bit A/D converter based on Adafruit ADS1x15 library (no library needed) (+0k7 code) +//#define USE_ADS1115_I2CDEV // Add I2C code for ADS1115 16 bit A/D converter using library i2cdevlib-Core and i2cdevlib-ADS1115 (+2k code) +#define USE_INA219 // Add I2C code for INA219 Low voltage and current sensor (+1k code) +//#define USE_INA226 // Enable INA226 (I2C address 0x40, 0x41 0x44 or 0x45) Low voltage and current sensor (+2k3 code) +#define USE_SHT3X // Add I2C code for SHT3x sensor (+0k6 code) +#define USE_TSL2561 // Add I2C code for TSL2561 sensor using library Adafruit TSL2561 Arduino (+1k2 code) +#define USE_MGS // Add I2C code for Xadow and Grove Mutichannel Gas sensor using library Multichannel_Gas_Sensor (+10k code) +#define USE_SGP30 // Add I2C code for SGP30 sensor (+1k1 code) +//#define USE_SI1145 // Add I2C code for SI1145/46/47 sensor (+1k code) +#define USE_LM75AD // Add I2C code for LM75AD sensor (+0k5 code) +//#define USE_APDS9960 // Add I2C code for APDS9960 Proximity Sensor. Disables SHT and VEML6070 (+4k7 code) +//#define USE_MCP230xx // Enable MCP23008/MCP23017 - Must define I2C Address in #define USE_MCP230xx_ADDR below - range 0x20 - 0x27 (+4k7 code) +// #define USE_MCP230xx_ADDR 0x20 // Enable MCP23008/MCP23017 I2C Address to use (Must be within range 0x20 through 0x27 - set according to your wired setup) +// #define USE_MCP230xx_OUTPUT // Enable MCP23008/MCP23017 OUTPUT support through sensor29 commands (+1k5 code) +// #define USE_MCP230xx_DISPLAYOUTPUT // Enable MCP23008/MCP23017 to display state of OUTPUT pins on Web UI (+0k2 code) +//#define USE_PCA9685 // Enable PCA9685 I2C HW PWM Driver - Must define I2C Address in #define USE_PCA9685_ADDR below - range 0x40 - 0x47 (+1k4 code) +// #define USE_PCA9685_ADDR 0x40 // Enable PCA9685 I2C Address to use (Must be within range 0x40 through 0x47 - set according to your wired setup) +// #define USE_PCA9685_FREQ 50 // Define default PWM frequency in Hz to be used (must be within 24 to 1526) - If other value is used, it will rever to 50Hz +//#define USE_MPR121 // Enable MPR121 controller (I2C addresses 0x5A, 0x5B, 0x5C and 0x5D) in input mode for touch buttons (+1k3 code) +//#define USE_CCS811 // Add I2C code for CCS811 sensor (+2k2 code) +//#define USE_MPU6050 // Enable MPU6050 sensor (I2C address 0x68 AD0 low or 0x69 AD0 high) (+2k6 code) +//#define USE_DS3231 // Enable DS3231 external RTC in case no Wifi is avaliable. See docs in the source file (+1k2 code) +//#define USE_MGC3130 // Enable MGC3130 Electric Field Effect Sensor (I2C address 0x42) (+2k7 code, 0k3 mem) +//#define USE_MAX44009 // Enable MAX44009 Ambient Light sensor (I2C addresses 0x4A and 0x4B) (+0k8 code) +#define USE_SCD30 // Enable Sensiron SCd30 CO2 sensor (I2C address 0x61) (+3k3 code) +//#define USE_SPS30 // Enable Sensiron SPS30 particle sensor (I2C address 0x69) (+1.7 code) +#define USE_ADE7953 // Enable ADE7953 Energy monitor as used on Shelly 2.5 (I2C address 0x38) (+1k5) +//#define USE_VL53L0X // Enable VL53L0x time of flight sensor (I2C address 0x29) (+4k code) +//#define USE_MLX90614 // Enable MLX90614 ir temp sensor (I2C address 0x5a) (+0.6k code) +//#define USE_CHIRP // Enable CHIRP soil moisture sensor (variable I2C address, default 0x20) +//#define USE_PAJ7620 // Enable PAJ7620 gesture sensor (I2C address 0x73) (+2.5k code) +//#define USE_PCF8574 // Enable PCF8574 I/O Expander (I2C addresses 0x20 - 0x27 and 0x38 - 0x3F) (+1k9 code) + +#define USE_MHZ19 // Add support for MH-Z19 CO2 sensor (+2k code) +#define USE_SENSEAIR // Add support for SenseAir K30, K70 and S8 CO2 sensor (+2k3 code) +#ifndef CO2_LOW + #define CO2_LOW 800 // Below this CO2 value show green light (needs PWM or WS2812 RG(B) led and enable with SetOption18 1) +#endif +#ifndef CO2_HIGH + #define CO2_HIGH 1200 // Above this CO2 value show red light (needs PWM or WS2812 RG(B) led and enable with SetOption18 1) +#endif +#define USE_PMS5003 // Add support for PMS5003 and PMS7003 particle concentration sensor (+1k3 code) + //#define PMS_MODEL_PMS3003 // Enable support of PMS3003 instead of PMS5003/PMS7003 (needs the USE_PMS5003 above) +#define USE_NOVA_SDS // Add support for SDS011 and SDS021 particle concentration sensor (+0k7 code) +#define USE_SERIAL_BRIDGE // Add support for software Serial Bridge (+0k8 code) +#define USE_MP3_PLAYER // Use of the DFPlayer Mini MP3 Player RB-DFR-562 commands: play, volume and stop + #define MP3_VOLUME 10 // Set the startup volume on init, the range can be 0..30(max) +//#define USE_AZ7798 // Add support for AZ-Instrument 7798 CO2 datalogger +#define USE_PN532_HSU // Add support for PN532 using HSU (Serial) interface (+1k8 code, 140 bytes mem) +#define USE_RDM6300 // Add support for RDM6300 125kHz RFID Reader (+0k8) +#define USE_IBEACON // Add support for bluetooth LE passive scan of ibeacon devices (uses HM17 module) + +#define USE_ENERGY_SENSOR // Add energy sensors (-14k code) +#define USE_PZEM004T // Add support for PZEM004T Energy monitor (+2k code) +#define USE_PZEM_AC // Add support for PZEM014,016 Energy monitor (+1k1 code) +#define USE_PZEM_DC // Add support for PZEM003,017 Energy monitor (+1k1 code) +#define USE_MCP39F501 // Add support for MCP39F501 Energy monitor as used in Shelly 2 (+3k1 code) +#define USE_SDM120 // Add support for Eastron SDM120-Modbus energy monitor (+1k1 code) +#define USE_SDM630 // Add support for Eastron SDM630-Modbus energy monitor (+0k6 code) +#define USE_DDS2382 // Add support for Hiking DDS2382 Modbus energy monitor (+0k6 code) +#define USE_DDSU666 // Add support for Chint DDSU666 Modbus energy monitor (+0k6 code) +//#define USE_SOLAX_X1 // Add support for Solax X1 series Modbus log info (+3k1 code) + +#define USE_DHT // Add support for DHT11, AM2301 (DHT21, DHT22, AM2302, AM2321) and SI7021 Temperature and Humidity sensor +#define USE_MAX31855 // Add support for MAX31855 K-Type thermocouple sensor using softSPI +//#define USE_MAX31865 // Add support for MAX31865 RTD sensors using softSPI +#define USE_IR_REMOTE // Send IR remote commands using library IRremoteESP8266 and ArduinoJson (+4k code, 0k3 mem, 48 iram) + #define USE_IR_HVAC // Support for HVAC system using IR (+2k code) + #define USE_IR_RECEIVE // Support for IR receiver (+5k5 code, 264 iram) + +//#define USE_ZIGBEE // Enable serial communication with Zigbee CC2530 flashed with ZNP + +#define USE_SR04 // Add support for HC-SR04 ultrasonic devices (+1k code) +#define USE_TM1638 // Add support for TM1638 switches copying Switch1 .. Switch8 (+1k code) +#define USE_HX711 // Add support for HX711 load cell (+1k5 code) +//#define USE_HX711_GUI // Add optional web GUI to HX711 as scale (+1k8 code) +//#define USE_TX20_WIND_SENSOR // Add support for La Crosse TX20 anemometer (+2k code) +#define USE_RC_SWITCH // Add support for RF transceiver using library RcSwitch (+2k7 code, 460 iram) +#define USE_RF_SENSOR // Add support for RF sensor receiver (434MHz or 868MHz) (+0k8 code) +// #define USE_THEO_V2 // Add support for decoding Theo V2 sensors as documented on https://sidweb.nl using 434MHz RF sensor receiver (+1k4 code) + #define USE_ALECTO_V2 // Add support for decoding Alecto V2 sensors like ACH2010, WS3000 and DKW2012 using 868MHz RF sensor receiver (+1k7 code) +#define USE_HRE // Add support for Badger HR-E Water Meter (+1k4 code) +//#define USE_A4988_STEPPER // Add support for A4988/DRV8825 stepper-motor-driver-circuit (+10k5 code) +//#define USE_ARDUINO_SLAVE // Add support for Arduino Uno/Pro Mini via serial interface including flashing (+2k3 code, 44 mem) +#undef DEBUG_THEO // Disable debug code +#undef USE_DEBUG_DRIVER // Disable debug code +#endif // FIRMWARE_SENSORS /*********************************************************************************************\ * [sonoff-knx.bin] @@ -241,12 +221,16 @@ void KNX_CB_Action(message_t const &msg, void *arg); #ifdef FIRMWARE_KNX_NO_EMULATION #undef CODE_IMAGE -#define CODE_IMAGE 4 +#define CODE_IMAGE 3 #ifndef USE_KNX -#define USE_KNX // Enable KNX IP Protocol Support (+23k code, +3k3 mem) +#define USE_KNX // Enable KNX IP Protocol Support (+23k code, +3k3 mem) #endif -#undef USE_EMULATION // Disable Belkin WeMo and Hue Bridge emulation for Alexa (-16k code, -2k mem) +#undef USE_EMULATION // Disable Belkin WeMo and Hue Bridge emulation for Alexa (-16k code, -2k mem) +#undef USE_EMULATION_HUE // Disable Hue Bridge emulation for Alexa (+14k code, +2k mem common) +#undef USE_EMULATION_WEMO // Disable Belkin WeMo emulation for Alexa (+6k code, +2k mem common) +#undef DEBUG_THEO // Disable debug code +#undef USE_DEBUG_DRIVER // Disable debug code #endif // FIRMWARE_KNX_NO_EMULATION /*********************************************************************************************\ @@ -257,42 +241,151 @@ void KNX_CB_Action(message_t const &msg, void *arg); #ifdef FIRMWARE_DISPLAYS #undef CODE_IMAGE -#define CODE_IMAGE 6 +#define CODE_IMAGE 5 -#undef USE_ENERGY_SENSOR // Disable energy sensors (-14k code) - #undef USE_PZEM004T // Disable PZEM004T energy sensor - #undef USE_PZEM_AC // Disable PZEM014,016 Energy monitor - #undef USE_PZEM_DC // Disable PZEM003,017 Energy monitor - #undef USE_MCP39F501 // Disable MCP39F501 Energy monitor as used in Shelly 2 -#undef USE_EMULATION // Disable Belkin WeMo and Hue Bridge emulation for Alexa (-16k code, -2k mem) -#undef USE_DOMOTICZ // Disable Domoticz -#undef USE_HOME_ASSISTANT // Disable Home Assistant +#undef USE_EMULATION // Disable Belkin WeMo and Hue Bridge emulation for Alexa (-16k code, -2k mem) +#undef USE_EMULATION_HUE // Disable Hue Bridge emulation for Alexa (+14k code, +2k mem common) +#undef USE_EMULATION_WEMO // Disable Belkin WeMo emulation for Alexa (+6k code, +2k mem common) +#undef USE_DOMOTICZ // Disable Domoticz +#undef USE_HOME_ASSISTANT // Disable Home Assistant -#define USE_I2C // I2C using library wire (+10k code, 0k2 mem, 124 iram) - #define USE_DISPLAY // Add I2C Display Support (+2k code) - #define USE_DISPLAY_MODES1TO5 // Enable display mode 1 to 5 in addition to mode 0 - #define USE_DISPLAY_LCD // [DisplayModel 1] Enable Lcd display (I2C addresses 0x27 and 0x3F) (+6k code) - #define USE_DISPLAY_SSD1306 // [DisplayModel 2] Enable SSD1306 Oled 128x64 display (I2C addresses 0x3C and 0x3D) (+16k code) - #define USE_DISPLAY_MATRIX // [DisplayModel 3] Enable 8x8 Matrix display (I2C adresseses see below) (+11k code) +// -- Optional modules ---------------------------- +#undef ROTARY_V1 // Disable support for MI Desk Lamp +#undef USE_SONOFF_RF // Disable support for Sonoff Rf Bridge (+3k2 code) + #undef USE_RF_FLASH // Disable support for flashing the EFM8BB1 chip on the Sonoff RF Bridge. C2CK must be connected to GPIO4, C2D to GPIO5 on the PCB +#undef USE_SONOFF_SC // Disable support for Sonoff Sc (+1k1 code) +//#undef USE_TUYA_MCU // Disable support for Tuya Serial MCU +#undef USE_ARMTRONIX_DIMMERS // Disable support for Armtronix Dimmers (+1k4 code) +#undef USE_PS_16_DZ // Disable support for PS-16-DZ Dimmer and Sonoff L1 (+2k code) +#undef USE_SONOFF_IFAN // Disable support for Sonoff iFan02 and iFan03 (+2k code) +#undef USE_BUZZER // Disable support for a buzzer (+0k6 code) +#undef USE_ARILUX_RF // Disable support for Arilux RF remote controller +#undef USE_SHUTTER // Disable Shutter support for up to 4 shutter with different motortypes (+6k code) +#undef USE_DEEPSLEEP // Disable support for deepsleep (+1k code) +#undef USE_EXS_DIMMER // Disable support for EX-Store WiFi Dimmer -#define USE_SPI // Hardware SPI using GPIO12(MISO), GPIO13(MOSI) and GPIO14(CLK) in addition to two user selectable GPIOs(CS and DC) - #define USE_DISPLAY_ILI9341 // [DisplayModel 4] Enable ILI9341 Tft 480x320 display (+19k code) -#ifndef ARDUINO_ESP8266_RELEASE_2_3_0 // There is not enough spare RAM with core 2.3.0 to support the following - #define USE_DISPLAY_EPAPER_29 // [DisplayModel 5] Enable e-paper 2.9 inch display (+19k code) +#undef USE_ENERGY_SENSOR // Disable energy sensors (-14k code) + #undef USE_PZEM004T // Disable PZEM004T energy sensor + #undef USE_PZEM_AC // Disable PZEM014,016 Energy monitor + #undef USE_PZEM_DC // Disable PZEM003,017 Energy monitor + #undef USE_MCP39F501 // Disable MCP39F501 Energy monitor as used in Shelly 2 + #undef USE_SDM120 // Disable support for Eastron SDM120-Modbus energy meter + #undef USE_SDM630 // Disable support for Eastron SDM630-Modbus energy monitor (+0k6 code) + #undef USE_DDS2382 // Disable support for Hiking DDS2382 Modbus energy monitor (+0k6 code) + #undef USE_DDSU666 // Disable support for Chint DDSU666 Modbus energy monitor (+0k6 code) + #undef USE_SOLAX_X1 // Disable support for Solax X1 series Modbus log info (+3k1 code) + +#define USE_I2C // I2C using library wire (+10k code, 0k2 mem, 124 iram) + #define USE_DISPLAY // Add I2C Display Support (+2k code) + #define USE_DISPLAY_MODES1TO5 // Enable display mode 1 to 5 in addition to mode 0 + #define USE_DISPLAY_LCD // [DisplayModel 1] Enable Lcd display (I2C addresses 0x27 and 0x3F) (+6k code) + #define USE_DISPLAY_SSD1306 // [DisplayModel 2] Enable SSD1306 Oled 128x64 display (I2C addresses 0x3C and 0x3D) (+16k code) + #define USE_DISPLAY_MATRIX // [DisplayModel 3] Enable 8x8 Matrix display (I2C adresseses see below) (+11k code) + #define USE_DISPLAY_SH1106 // [DisplayModel 7] Enable SH1106 Oled 128x64 display (I2C addresses 0x3C and 0x3D) + +#define USE_SPI // Hardware SPI using GPIO12(MISO), GPIO13(MOSI) and GPIO14(CLK) in addition to two user selectable GPIOs(CS and DC) + #define USE_DISPLAY_ILI9341 // [DisplayModel 4] Enable ILI9341 Tft 480x320 display (+19k code) +#ifndef ARDUINO_ESP8266_RELEASE_2_3_0 // There is not enough spare RAM with core 2.3.0 to support the following + #define USE_DISPLAY_EPAPER_29 // [DisplayModel 5] Enable e-paper 2.9 inch display (+19k code) + #define USE_DISPLAY_EPAPER_42 // [DisplayModel 6] Enable e-paper 4.2 inch display +// #define USE_DISPLAY_ILI9488 // [DisplayModel 8] +// #define USE_DISPLAY_SSD1351 // [DisplayModel 9] +// #define USE_DISPLAY_RA8876 // [DisplayModel 10] #endif -#undef USE_ARILUX_RF // Remove support for Arilux RF remote controller (-0k8 code, 252 iram (non 2.3.0)) -#undef USE_RF_FLASH // Remove support for flashing the EFM8BB1 chip on the Sonoff RF Bridge. C2CK must be connected to GPIO4, C2D to GPIO5 on the PCB (-3k code) +#undef DEBUG_THEO // Disable debug code +#undef USE_DEBUG_DRIVER // Disable debug code #endif // FIRMWARE_DISPLAYS /*********************************************************************************************\ - * Mandatory define for DS18x20 if changed by above image selections + * [sonoff-ir.bin] + * Provide a dedicated image with IR full protocol support, with limited additional features \*********************************************************************************************/ -#if defined(USE_DS18x20) || defined(USE_DS18x20_LEGACY) -#else -#define USE_DS18B20 // Default DS18B20 sensor needs no external library -#endif +#ifdef FIRMWARE_IR + +#undef CODE_IMAGE +#define CODE_IMAGE 6 + +#undef USE_EMULATION +#undef USE_EMULATION_HUE // Disable Hue emulation - only for lights and relays +#undef USE_EMULATION_WEMO // Disable Wemo emulation - only for relays + +//#undef USE_DOMOTICZ // Disable Domoticz +//#undef USE_HOME_ASSISTANT // Disable Home Assistant +//#undef USE_KNX // Disable KNX IP Protocol Support +//#undef USE_CUSTOM // Disable Custom features +//#undef USE_TIMERS // Disable support for up to 16 timers +//#undef USE_TIMERS_WEB // Disable support for timer webpage +//#undef USE_SUNRISE // Disable support for Sunrise and sunset tools +//#undef USE_RULES // Disable support for rules +#undef USE_DISCOVERY // Disable mDNS for the following services (+8k code or +23.5k code with core 2_5_x, +0.3k mem) + +// -- Optional modules ---------------------------- +#undef ROTARY_V1 // Disable support for MI Desk Lamp +#undef USE_SONOFF_RF // Disable support for Sonoff Rf Bridge (+3k2 code) + #undef USE_RF_FLASH // Disable support for flashing the EFM8BB1 chip on the Sonoff RF Bridge. C2CK must be connected to GPIO4, C2D to GPIO5 on the PCB +#undef USE_SONOFF_SC // Disable support for Sonoff Sc (+1k1 code) +#undef USE_TUYA_MCU // Disable support for Tuya Serial MCU +#undef USE_ARMTRONIX_DIMMERS // Disable support for Armtronix Dimmers (+1k4 code) +#undef USE_PS_16_DZ // Disable support for PS-16-DZ Dimmer and Sonoff L1 (+2k code) +#undef USE_SONOFF_IFAN // Disable support for Sonoff iFan02 and iFan03 (+2k code) +#undef USE_BUZZER // Disable support for a buzzer (+0k6 code) +#undef USE_ARILUX_RF // Disable support for Arilux RF remote controller +#undef USE_SHUTTER // Disable Shutter support for up to 4 shutter with different motortypes (+6k code) +#undef USE_DEEPSLEEP // Disable support for deepsleep (+1k code) +#undef USE_EXS_DIMMER // Disable support for EX-Store WiFi Dimmer + +// -- Optional light modules ---------------------- +//#undef USE_LIGHT // Also disable all Dimmer/Light support +#undef USE_WS2812 // Disable WS2812 Led string using library NeoPixelBus (+5k code, +1k mem, 232 iram) - Disable by // +#undef USE_MY92X1 // Disable support for MY92X1 RGBCW led controller as used in Sonoff B1, Ailight and Lohas +#undef USE_SM16716 // Disable support for SM16716 RGB LED controller (+0k7 code) +#undef USE_SM2135 // Disable support for SM2135 RGBCW led control as used in Action LSC (+0k6 code) +#undef USE_SONOFF_L1 // Disable support for Sonoff L1 led control + +#undef USE_ENERGY_SENSOR // Disable energy sensors (-14k code) + #undef USE_PZEM004T // Disable PZEM004T energy sensor + #undef USE_PZEM_AC // Disable PZEM014,016 Energy monitor + #undef USE_PZEM_DC // Disable PZEM003,017 Energy monitor + #undef USE_MCP39F501 // Disable MCP39F501 Energy monitor as used in Shelly 2 + #undef USE_SDM120 // Disable support for Eastron SDM120-Modbus energy meter + #undef USE_SDM630 // Disable support for Eastron SDM630-Modbus energy monitor (+0k6 code) + #undef USE_DDS2382 // Disable support for Hiking DDS2382 Modbus energy monitor (+0k6 code) + #undef USE_DDSU666 // Disable support for Chint DDSU666 Modbus energy monitor (+0k6 code) + #undef USE_SOLAX_X1 // Disable support for Solax X1 series Modbus log info (+3k1 code) + +#undef USE_DS18x20 // Disable support for DS18x20 sensors with id sort, single scan and read retry (+1k3 code) + +#undef USE_I2C // Disable all I2C sensors +#undef USE_SPI // Disable all SPI devices + +#undef USE_MHZ19 // Disable support for MH-Z19 CO2 sensor +#undef USE_SENSEAIR // Disable support for SenseAir K30, K70 and S8 CO2 sensor +#undef USE_PMS5003 // Disable support for PMS5003 and PMS7003 particle concentration sensor +#undef USE_NOVA_SDS // Disable support for SDS011 and SDS021 particle concentration sensor +#undef USE_SERIAL_BRIDGE // Disable support for software Serial Bridge +#undef USE_MP3_PLAYER // Disable DFPlayer Mini MP3 Player RB-DFR-562 commands: play, volume and stop +#undef USE_AZ7798 // Disable support for AZ-Instrument 7798 CO2 datalogger +#undef USE_PN532_HSU // Disable support for PN532 using HSU (Serial) interface (+1k8 code, 140 bytes mem) +#undef USE_RDM6300 // Disable support for RDM6300 125kHz RFID Reader (+0k8) +#undef USE_IBEACON // Disable support for bluetooth LE passive scan of ibeacon devices (uses HM17 module) + +//#define USE_DHT // Add support for DHT11, AM2301 (DHT21, DHT22, AM2302, AM2321) and SI7021 Temperature and Humidity sensor +#undef USE_MAX31855 // Disable MAX31855 K-Type thermocouple sensor using softSPI +#undef USE_MAX31865 // Disable support for MAX31865 RTD sensors using softSPI +#undef USE_SR04 // Disable support for for HC-SR04 ultrasonic devices +#undef USE_TM1638 // Disable support for TM1638 switches copying Switch1 .. Switch8 +#undef USE_HX711 // Disable support for HX711 load cell +#undef USE_TX20_WIND_SENSOR // Disable support for La Crosse TX20 anemometer +#undef USE_RC_SWITCH // Disable support for RF transceiver using library RcSwitch +#undef USE_RF_SENSOR // Disable support for RF sensor receiver (434MHz or 868MHz) (+0k8 code) +#undef USE_HRE // Disable support for Badger HR-E Water Meter (+1k4 code) +#undef USE_A4988_STEPPER // Disable support for A4988_Stepper +#undef USE_ARDUINO_SLAVE // Disable support for Arduino Uno/Pro Mini via serial interface including flashing (+2k3 code, 44 mem) +#undef DEBUG_THEO // Disable debug code +#undef USE_DEBUG_DRIVER // Disable debug code +#endif // FIRMWARE_IR /*********************************************************************************************\ * [sonoff-basic.bin] @@ -302,68 +395,98 @@ void KNX_CB_Action(message_t const &msg, void *arg); #ifdef FIRMWARE_BASIC #undef CODE_IMAGE -#define CODE_IMAGE 5 +#define CODE_IMAGE 4 #undef APP_SLEEP -#define APP_SLEEP 1 // Default to sleep = 1 for FIRMWARE_BASIC +#define APP_SLEEP 1 // Default to sleep = 1 for FIRMWARE_BASIC -#define USE_ADC_VCC // Display Vcc in Power status. Disable for use as Analog input on selected devices -//#undef USE_ENERGY_SENSOR // Disable energy sensors -#undef USE_ARDUINO_OTA // Disable support for Arduino OTA -#undef USE_WPS // Disable support for WPS as initial wifi configuration tool -#undef USE_SMARTCONFIG // Disable support for Wifi SmartConfig as initial wifi configuration tool -#undef USE_DOMOTICZ // Disable Domoticz -#undef USE_HOME_ASSISTANT // Disable Home Assistant -#undef USE_MQTT_TLS // Disable TLS support won't work as the MQTTHost is not set -#undef USE_KNX // Disable KNX IP Protocol Support -//#undef USE_WEBSERVER // Disable Webserver -//#undef USE_EMULATION // Disable Wemo or Hue emulation -#undef USE_CUSTOM // Disable Custom features -#undef USE_DISCOVERY // Disable Discovery services for both MQTT and web server -//#undef USE_TIMERS // Disable support for up to 16 timers -//#undef USE_TIMERS_WEB // Disable support for timer webpage -//#undef USE_SUNRISE // Disable support for Sunrise and sunset tools -//#undef USE_RULES // Disable support for rules -#undef USE_COUNTER // Disable counters -#undef USE_DS18x20 // Disable DS18x20 sensor -#undef USE_DS18x20_LEGACY // Disable DS18x20 sensor -#undef USE_DS18B20 // Disable internal DS18B20 sensor -#undef USE_I2C // Disable all I2C sensors and devices -#undef USE_SPI // Disable all SPI devices -#undef USE_DISPLAY // Disable Display support -#undef USE_MHZ19 // Disable support for MH-Z19 CO2 sensor -#undef USE_SENSEAIR // Disable support for SenseAir K30, K70 and S8 CO2 sensor -#undef USE_PMS5003 // Disable support for PMS5003 and PMS7003 particle concentration sensor -#undef USE_NOVA_SDS // Disable support for SDS011 and SDS021 particle concentration sensor -#undef USE_SERIAL_BRIDGE // Disable support for software Serial Bridge -#undef USE_SDM120 // Disable support for Eastron SDM120-Modbus energy meter -#undef USE_SDM630 // Disable support for Eastron SDM630-Modbus energy meter -#undef USE_MP3_PLAYER // Disable DFPlayer Mini MP3 Player RB-DFR-562 commands: play, volume and stop -//#undef USE_TUYA_DIMMER // Disable support for Tuya Serial Dimmer -#undef USE_ARMTRONIX_DIMMERS // Disable support for Armtronix Dimmers (+1k4 code) -#undef USE_PS_16_DZ // Disable support for PS-16-DZ Dimmer and Sonoff L1 (+2k code) -#undef USE_AZ7798 // Disable support for AZ-Instrument 7798 CO2 datalogger -#undef USE_PN532_HSU // Disable support for PN532 using HSU (Serial) interface (+1k8 code, 140 bytes mem) -#undef USE_PZEM004T // Disable PZEM004T energy sensor -#undef USE_PZEM_AC // Disable PZEM014,016 Energy monitor -#undef USE_PZEM_DC // Disable PZEM003,017 Energy monitor -//#undef USE_MCP39F501 // Disable MCP39F501 Energy monitor as used in Shelly 2 -#undef USE_DHT // Disable support for DHT11, AM2301 (DHT21, DHT22, AM2302, AM2321) and SI7021 Temperature and Humidity sensor -#undef USE_MAX31855 // Disable MAX31855 K-Type thermocouple sensor using softSPI -#undef USE_IR_REMOTE // Disable IR driver -#undef USE_WS2812 // Disable WS2812 Led string -#undef USE_ARILUX_RF // Disable support for Arilux RF remote controller -#undef USE_SR04 // Disable support for for HC-SR04 ultrasonic devices -#undef USE_TM1638 // Disable support for TM1638 switches copying Switch1 .. Switch8 -#undef USE_HX711 // Disable support for HX711 load cell -#undef USE_RF_FLASH // Disable support for flashing the EFM8BB1 chip on the Sonoff RF Bridge. C2CK must be connected to GPIO4, C2D to GPIO5 on the PCB -#undef USE_TX20_WIND_SENSOR // Disable support for La Crosse TX20 anemometer -#undef USE_RC_SWITCH // Disable support for RF transceiver using library RcSwitch -#undef USE_RF_SENSOR // Disable support for RF sensor receiver (434MHz or 868MHz) (+0k8 code) -#undef USE_SM16716 // Disable support for SM16716 RGB LED controller (+0k7 code) -#undef USE_HRE // Disable support for Badger HR-E Water Meter (+1k4 code) -#undef DEBUG_THEO // Disable debug code -#undef USE_DEBUG_DRIVER // Disable debug code +#undef USE_ARDUINO_OTA // Disable support for Arduino OTA +#undef USE_DOMOTICZ // Disable Domoticz +#undef USE_HOME_ASSISTANT // Disable Home Assistant +#undef USE_MQTT_TLS // Disable TLS support won't work as the MQTTHost is not set +#undef USE_KNX // Disable KNX IP Protocol Support +//#undef USE_WEBSERVER // Disable Webserver +#undef USE_WEBSEND_RESPONSE // Disable command WebSend response message (+1k code) +//#undef USE_EMULATION // Disable Wemo or Hue emulation +//#undef USE_EMULATION_HUE // Disable Hue Bridge emulation for Alexa (+14k code, +2k mem common) +//#undef USE_EMULATION_WEMO // Disable Belkin WeMo emulation for Alexa (+6k code, +2k mem common) +#undef USE_CUSTOM // Disable Custom features +#undef USE_DISCOVERY // Disable Discovery services for both MQTT and web server +//#undef USE_TIMERS // Disable support for up to 16 timers +//#undef USE_TIMERS_WEB // Disable support for timer webpage +//#undef USE_SUNRISE // Disable support for Sunrise and sunset tools +//#undef USE_RULES // Disable support for rules +#undef USE_SCRIPT // Add support for script (+17k code) + +// -- Optional modules ------------------------- +#undef ROTARY_V1 // Disable support for MI Desk Lamp +#undef USE_SONOFF_RF // Disable support for Sonoff Rf Bridge (+3k2 code) + #undef USE_RF_FLASH // Disable support for flashing the EFM8BB1 chip on the Sonoff RF Bridge. C2CK must be connected to GPIO4, C2D to GPIO5 on the PCB +#undef USE_SONOFF_SC // Disable support for Sonoff Sc (+1k1 code) +//#undef USE_TUYA_MCU // Disable support for Tuya Serial MCU +#undef USE_ARMTRONIX_DIMMERS // Disable support for Armtronix Dimmers (+1k4 code) +#undef USE_PS_16_DZ // Disable support for PS-16-DZ Dimmer and Sonoff L1 (+2k code) +#undef USE_SONOFF_IFAN // Disable support for Sonoff iFan02 and iFan03 (+2k code) +#undef USE_BUZZER // Disable support for a buzzer (+0k6 code) +#undef USE_ARILUX_RF // Disable support for Arilux RF remote controller +#undef USE_SHUTTER // Disable Shutter support for up to 4 shutter with different motortypes (+6k code) +#undef USE_DEEPSLEEP // Disable support for deepsleep (+1k code) +#undef USE_EXS_DIMMER // Disable support for EX-Store WiFi Dimmer + +// -- Optional light modules ---------------------- +//#undef USE_LIGHT // Also disable all Dimmer/Light support +#undef USE_WS2812 // Disable WS2812 Led string using library NeoPixelBus (+5k code, +1k mem, 232 iram) - Disable by // +#undef USE_MY92X1 // Disable support for MY92X1 RGBCW led controller as used in Sonoff B1, Ailight and Lohas +#undef USE_SM16716 // Disable support for SM16716 RGB LED controller (+0k7 code) +#undef USE_SM2135 // Disable support for SM2135 RGBCW led control as used in Action LSC (+0k6 code) +#undef USE_SONOFF_L1 // Disable support for Sonoff L1 led control + +#undef USE_COUNTER // Disable counters +#define USE_ADC_VCC // Display Vcc in Power status. Disable for use as Analog input on selected devices +#undef USE_DS18x20 // Disable DS18x20 sensor +#undef USE_I2C // Disable all I2C sensors and devices +#undef USE_SPI // Disable all SPI devices +#undef USE_DISPLAY // Disable Display support +#undef USE_MHZ19 // Disable support for MH-Z19 CO2 sensor +#undef USE_SENSEAIR // Disable support for SenseAir K30, K70 and S8 CO2 sensor +#undef USE_PMS5003 // Disable support for PMS5003 and PMS7003 particle concentration sensor +#undef USE_NOVA_SDS // Disable support for SDS011 and SDS021 particle concentration sensor +#undef USE_SERIAL_BRIDGE // Disable support for software Serial Bridge +#undef USE_MP3_PLAYER // Disable DFPlayer Mini MP3 Player RB-DFR-562 commands: play, volume and stop +#undef USE_AZ7798 // Disable support for AZ-Instrument 7798 CO2 datalogger +#undef USE_PN532_HSU // Disable support for PN532 using HSU (Serial) interface (+1k8 code, 140 bytes mem) +#undef USE_RDM6300 // Disable support for RDM6300 125kHz RFID Reader (+0k8) +#undef USE_IBEACON // Disable support for bluetooth LE passive scan of ibeacon devices (uses HM17 module) + +//#undef USE_ENERGY_SENSOR // Disable energy sensors +#undef USE_PZEM004T // Disable PZEM004T energy sensor +#undef USE_PZEM_AC // Disable PZEM014,016 Energy monitor +#undef USE_PZEM_DC // Disable PZEM003,017 Energy monitor +//#undef USE_MCP39F501 // Disable MCP39F501 Energy monitor as used in Shelly 2 +#undef USE_SDM120 // Disable support for Eastron SDM120-Modbus energy meter +#undef USE_SDM630 // Disable support for Eastron SDM630-Modbus energy monitor (+0k6 code) +#undef USE_DDS2382 // Disable support for Hiking DDS2382 Modbus energy monitor (+0k6 code) +#undef USE_DDSU666 // Disable support for Chint DDSU666 Modbus energy monitor (+0k6 code) +#undef USE_SOLAX_X1 // Disable support for Solax X1 series Modbus log info (+3k1 code) + +#undef USE_DHT // Disable support for DHT11, AM2301 (DHT21, DHT22, AM2302, AM2321) and SI7021 Temperature and Humidity sensor +#undef USE_MAX31855 // Disable MAX31855 K-Type thermocouple sensor using softSPI +#undef USE_MAX31865 // Disable support for MAX31865 RTD sensors using softSPI +#undef USE_IR_REMOTE // Disable IR driver + +#undef USE_ZIGBEE // Disable serial communication with Zigbee CC2530 flashed with ZNP + +#undef USE_SR04 // Disable support for for HC-SR04 ultrasonic devices +#undef USE_TM1638 // Disable support for TM1638 switches copying Switch1 .. Switch8 +#undef USE_HX711 // Disable support for HX711 load cell +#undef USE_TX20_WIND_SENSOR // Disable support for La Crosse TX20 anemometer +#undef USE_RC_SWITCH // Disable support for RF transceiver using library RcSwitch +#undef USE_RF_SENSOR // Disable support for RF sensor receiver (434MHz or 868MHz) (+0k8 code) +#undef USE_HRE // Disable support for Badger HR-E Water Meter (+1k4 code) +#undef USE_A4988_STEPPER // Disable support for A4988_Stepper +#undef USE_ARDUINO_SLAVE // Disable support for Arduino Uno/Pro Mini via serial interface including flashing (+2k3 code, 44 mem) +#undef DEBUG_THEO // Disable debug code +#undef USE_DEBUG_DRIVER // Disable debug code #endif // FIRMWARE_BASIC /*********************************************************************************************\ @@ -376,77 +499,111 @@ void KNX_CB_Action(message_t const &msg, void *arg); #undef CODE_IMAGE #define CODE_IMAGE 1 -#define USE_ADC_VCC // Display Vcc in Power status. Disable for use as Analog input on selected devices -#undef USE_ENERGY_SENSOR // Disable energy sensors -#undef USE_ARDUINO_OTA // Disable support for Arduino OTA -#undef USE_WPS // Disable support for WPS as initial wifi configuration tool -#undef USE_SMARTCONFIG // Disable support for Wifi SmartConfig as initial wifi configuration tool -#undef USE_DOMOTICZ // Disable Domoticz -#undef USE_HOME_ASSISTANT // Disable Home Assistant -#undef USE_MQTT_TLS // Disable TLS support won't work as the MQTTHost is not set -#undef USE_KNX // Disable KNX IP Protocol Support -//#undef USE_WEBSERVER // Disable Webserver -#undef USE_EMULATION // Disable Wemo or Hue emulation -#undef USE_CUSTOM // Disable Custom features -#undef USE_DISCOVERY // Disable Discovery services for both MQTT and web server -#undef USE_TIMERS // Disable support for up to 16 timers -#undef USE_TIMERS_WEB // Disable support for timer webpage -#undef USE_SUNRISE // Disable support for Sunrise and sunset tools -#undef USE_RULES // Disable support for rules -#undef USE_SCRIPT // Disable support for script -#undef USE_LIGHT // Disable support for lights -#undef USE_COUNTER // Disable counters -#undef USE_DS18x20 // Disable DS18x20 sensor -#undef USE_DS18x20_LEGACY // Disable DS18x20 sensor -#undef USE_DS18B20 // Disable internal DS18B20 sensor -#undef USE_I2C // Disable all I2C sensors and devices -#undef USE_SPI // Disable all SPI devices -#undef USE_DISPLAY // Disable Display support -#undef USE_MHZ19 // Disable support for MH-Z19 CO2 sensor -#undef USE_SENSEAIR // Disable support for SenseAir K30, K70 and S8 CO2 sensor -#undef USE_PMS5003 // Disable support for PMS5003 and PMS7003 particle concentration sensor -#undef USE_NOVA_SDS // Disable support for SDS011 and SDS021 particle concentration sensor -#undef USE_SERIAL_BRIDGE // Disable support for software Serial Bridge -#undef USE_SDM120 // Disable support for Eastron SDM120-Modbus energy meter -#undef USE_SDM630 // Disable support for Eastron SDM630-Modbus energy meter -#undef USE_MP3_PLAYER // Disable DFPlayer Mini MP3 Player RB-DFR-562 commands: play, volume and stop -#undef USE_TUYA_DIMMER // Disable support for Tuya Serial Dimmer -#undef USE_ARMTRONIX_DIMMERS // Disable support for Armtronix Dimmers (+1k4 code) -#undef USE_PS_16_DZ // Disable support for PS-16-DZ Dimmer and Sonoff L1 (+2k code) -#undef USE_AZ7798 // Disable support for AZ-Instrument 7798 CO2 datalogger -#undef USE_PN532_HSU // Disable support for PN532 using HSU (Serial) interface (+1k8 code, 140 bytes mem) -#undef USE_PZEM004T // Disable PZEM004T energy sensor -#undef USE_PZEM_AC // Disable PZEM014,016 Energy monitor -#undef USE_PZEM_DC // Disable PZEM003,017 Energy monitor -#undef USE_MCP39F501 // Disable MCP39F501 Energy monitor as used in Shelly 2 -#undef USE_DHT // Disable support for DHT11, AM2301 (DHT21, DHT22, AM2302, AM2321) and SI7021 Temperature and Humidity sensor -#undef USE_MAX31855 // Disable MAX31855 K-Type thermocouple sensor using softSPI -#undef USE_IR_REMOTE // Disable IR driver -#undef USE_WS2812 // Disable WS2812 Led string -#undef USE_ARILUX_RF // Disable support for Arilux RF remote controller -#undef USE_SR04 // Disable support for for HC-SR04 ultrasonic devices -#undef USE_TM1638 // Disable support for TM1638 switches copying Switch1 .. Switch8 -#undef USE_HX711 // Disable support for HX711 load cell -#undef USE_RF_FLASH // Disable support for flashing the EFM8BB1 chip on the Sonoff RF Bridge. C2CK must be connected to GPIO4, C2D to GPIO5 on the PCB -#undef USE_TX20_WIND_SENSOR // Disable support for La Crosse TX20 anemometer -#undef USE_RC_SWITCH // Disable support for RF transceiver using library RcSwitch -#undef USE_RF_SENSOR // Disable support for RF sensor receiver (434MHz or 868MHz) (+0k8 code) -#undef USE_SM16716 // Disable support for SM16716 RGB LED controller (+0k7 code) -#undef USE_HRE // Disable support for Badger HR-E Water Meter (+1k4 code) -#undef DEBUG_THEO // Disable debug code -#undef USE_DEBUG_DRIVER // Disable debug code +#undef USE_ARDUINO_OTA // Disable support for Arduino OTA +#undef USE_DOMOTICZ // Disable Domoticz +#undef USE_HOME_ASSISTANT // Disable Home Assistant +#undef USE_MQTT_TLS // Disable TLS support won't work as the MQTTHost is not set +#undef USE_KNX // Disable KNX IP Protocol Support +//#undef USE_WEBSERVER // Disable Webserver +#undef USE_WEBSEND_RESPONSE // Disable command WebSend response message (+1k code) +#undef USE_EMULATION // Disable Wemo or Hue emulation +#undef USE_EMULATION_HUE // Disable Hue Bridge emulation for Alexa (+14k code, +2k mem common) +#undef USE_EMULATION_WEMO // Disable Belkin WeMo emulation for Alexa (+6k code, +2k mem common) +#undef USE_CUSTOM // Disable Custom features +#undef USE_DISCOVERY // Disable Discovery services for both MQTT and web server +#undef USE_TIMERS // Disable support for up to 16 timers +#undef USE_TIMERS_WEB // Disable support for timer webpage +#undef USE_SUNRISE // Disable support for Sunrise and sunset tools +#undef USE_RULES // Disable support for rules +#undef USE_SCRIPT // Disable support for script + +// -- Optional modules ---------------------------- +#undef ROTARY_V1 // Disable support for MI Desk Lamp +#undef USE_SONOFF_RF // Disable support for Sonoff Rf Bridge (+3k2 code) + #undef USE_RF_FLASH // Disable support for flashing the EFM8BB1 chip on the Sonoff RF Bridge. C2CK must be connected to GPIO4, C2D to GPIO5 on the PCB +#undef USE_SONOFF_SC // Disable support for Sonoff Sc (+1k1 code) +#undef USE_TUYA_MCU // Disable support for Tuya Serial MCU +#undef USE_ARMTRONIX_DIMMERS // Disable support for Armtronix Dimmers (+1k4 code) +#undef USE_PS_16_DZ // Disable support for PS-16-DZ Dimmer and Sonoff L1 (+2k code) +#undef USE_SONOFF_IFAN // Disable support for Sonoff iFan02 and iFan03 (+2k code) +#undef USE_BUZZER // Disable support for a buzzer (+0k6 code) +#undef USE_ARILUX_RF // Disable support for Arilux RF remote controller +#undef USE_SHUTTER // Disable Shutter support for up to 4 shutter with different motortypes (+6k code) +#undef USE_DEEPSLEEP // Disable support for deepsleep (+1k code) +#undef USE_EXS_DIMMER // Disable support for EX-Store WiFi Dimmer + +// -- Optional light modules ---------------------- +#undef USE_LIGHT // Disable support for lights +#undef USE_WS2812 // Disable WS2812 Led string using library NeoPixelBus (+5k code, +1k mem, 232 iram) - Disable by // +#undef USE_MY92X1 // Disable support for MY92X1 RGBCW led controller as used in Sonoff B1, Ailight and Lohas +#undef USE_SM16716 // Disable support for SM16716 RGB LED controller (+0k7 code) +#undef USE_SM2135 // Disable support for SM2135 RGBCW led control as used in Action LSC (+0k6 code) +#undef USE_SONOFF_L1 // Disable support for Sonoff L1 led control + +#undef USE_COUNTER // Disable counters +#define USE_ADC_VCC // Display Vcc in Power status. Disable for use as Analog input on selected devices +#undef USE_DS18x20 // Disable DS18x20 sensor + +#undef USE_I2C // Disable all I2C sensors and devices +#undef USE_SPI // Disable all SPI devices +#undef USE_DISPLAY // Disable Display support + +#undef USE_MHZ19 // Disable support for MH-Z19 CO2 sensor +#undef USE_SENSEAIR // Disable support for SenseAir K30, K70 and S8 CO2 sensor +#undef USE_PMS5003 // Disable support for PMS5003 and PMS7003 particle concentration sensor +#undef USE_NOVA_SDS // Disable support for SDS011 and SDS021 particle concentration sensor +#undef USE_SERIAL_BRIDGE // Disable support for software Serial Bridge +#undef USE_MP3_PLAYER // Disable DFPlayer Mini MP3 Player RB-DFR-562 commands: play, volume and stop +#undef USE_AZ7798 // Disable support for AZ-Instrument 7798 CO2 datalogger +#undef USE_PN532_HSU // Disable support for PN532 using HSU (Serial) interface (+1k8 code, 140 bytes mem) +#undef USE_RDM6300 // Disable support for RDM6300 125kHz RFID Reader (+0k8) +#undef USE_IBEACON // Disable support for bluetooth LE passive scan of ibeacon devices (uses HM17 module) + +#undef USE_ENERGY_SENSOR // Disable energy sensors +#undef USE_PZEM004T // Disable PZEM004T energy sensor +#undef USE_PZEM_AC // Disable PZEM014,016 Energy monitor +#undef USE_PZEM_DC // Disable PZEM003,017 Energy monitor +#undef USE_MCP39F501 // Disable MCP39F501 Energy monitor as used in Shelly 2 +#undef USE_SDM120 // Disable support for Eastron SDM120-Modbus energy meter +#undef USE_SDM630 // Disable support for Eastron SDM630-Modbus energy monitor (+0k6 code) +#undef USE_DDS2382 // Disable support for Hiking DDS2382 Modbus energy monitor (+0k6 code) +#undef USE_DDSU666 // Disable support for Chint DDSU666 Modbus energy monitor (+0k6 code) +#undef USE_SOLAX_X1 // Disable support for Solax X1 series Modbus log info (+3k1 code) + +#undef USE_DHT // Disable support for DHT11, AM2301 (DHT21, DHT22, AM2302, AM2321) and SI7021 Temperature and Humidity sensor +#undef USE_MAX31855 // Disable MAX31855 K-Type thermocouple sensor using softSPI +#undef USE_MAX31865 // Disable support for MAX31865 RTD sensors using softSPI +#undef USE_IR_REMOTE // Disable IR driver +#undef USE_SR04 // Disable support for for HC-SR04 ultrasonic devices +#undef USE_TM1638 // Disable support for TM1638 switches copying Switch1 .. Switch8 +#undef USE_HX711 // Disable support for HX711 load cell +#undef USE_TX20_WIND_SENSOR // Disable support for La Crosse TX20 anemometer +#undef USE_RC_SWITCH // Disable support for RF transceiver using library RcSwitch +#undef USE_RF_SENSOR // Disable support for RF sensor receiver (434MHz or 868MHz) (+0k8 code) +#undef USE_HRE // Disable support for Badger HR-E Water Meter (+1k4 code) +#undef USE_A4988_STEPPER // Disable support for A4988_Stepper +#undef USE_ARDUINO_SLAVE // Disable support for Arduino Uno/Pro Mini via serial interface including flashing (+2k3 code, 44 mem) +#undef DEBUG_THEO // Disable debug code +#undef USE_DEBUG_DRIVER // Disable debug code #endif // FIRMWARE_MINIMAL /*********************************************************************************************\ * Mandatory defines satisfying possible disabled defines \*********************************************************************************************/ -#ifndef USE_WPS // See https://github.com/esp8266/Arduino/pull/4889 -#undef NO_EXTRA_4K_HEAP // Allocate 4k heap for WPS in ESP8166/Arduino core v2.4.2 (was always allocated in previous versions) + // See https://github.com/esp8266/Arduino/pull/4889 +#undef NO_EXTRA_4K_HEAP // Allocate 4k heap for WPS in ESP8166/Arduino core v2.4.2 (was always allocated in previous versions) + +#ifndef USE_SONOFF_RF +#undef USE_RF_FLASH // Disable RF firmware flash when SOnoff Rf is disabled #endif #ifndef SWITCH_MODE -#define SWITCH_MODE TOGGLE // TOGGLE, FOLLOW or FOLLOW_INV (the wall switch state) +#define SWITCH_MODE TOGGLE // TOGGLE, FOLLOW or FOLLOW_INV (the wall switch state) +#endif + +#ifndef STARTING_OFFSET // NOVA SDS parameter used in settings +#define STARTING_OFFSET 30 #endif #ifndef MQTT_FINGERPRINT1 @@ -459,35 +616,52 @@ void KNX_CB_Action(message_t const &msg, void *arg); #endif #ifndef WS2812_LEDS -#define WS2812_LEDS 30 // [Pixels] Number of LEDs +#define WS2812_LEDS 30 // [Pixels] Number of LEDs #endif #ifndef MQTT_MAX_PACKET_SIZE -#define MQTT_MAX_PACKET_SIZE 1000 // Bytes +#define MQTT_MAX_PACKET_SIZE 1000 // Bytes #endif #ifndef MQTT_KEEPALIVE -#define MQTT_KEEPALIVE 30 // Seconds +#define MQTT_KEEPALIVE 30 // Seconds #endif #ifndef MQTT_TIMEOUT -#define MQTT_TIMEOUT 10000 // milli seconds +#define MQTT_TIMEOUT 10000 // milli seconds #endif #ifndef MQTT_CLEAN_SESSION -#define MQTT_CLEAN_SESSION 1 // 0 = No clean session, 1 = Clean session (default) +#define MQTT_CLEAN_SESSION 1 // 0 = No clean session, 1 = Clean session (default) #endif #ifndef MESSZ -//#define MESSZ 405 // Max number of characters in JSON message string (6 x DS18x20 sensors) -//#define MESSZ 893 // Max number of characters in JSON message string (Hass discovery and nice MQTT_MAX_PACKET_SIZE = 1000) -#define MESSZ (MQTT_MAX_PACKET_SIZE -TOPSZ -7) // Max number of characters in JSON message string (6 x DS18x20 sensors) +//#define MESSZ 893 // Max number of characters in JSON message string (Hass discovery and nice MQTT_MAX_PACKET_SIZE = 1000) +#define MESSZ (MQTT_MAX_PACKET_SIZE -TOPSZ -7) // Max number of characters in JSON message string #endif -//#include // Arduino_Esp8266 version information (ARDUINO_ESP8266_RELEASE and ARDUINO_ESP8266_RELEASE_2_3_0) +//#include // Arduino_Esp8266 version information (ARDUINO_ESP8266_RELEASE and ARDUINO_ESP8266_RELEASE_2_3_0) #ifndef ARDUINO_ESP8266_RELEASE #define ARDUINO_ESP8266_RELEASE "STAGE" #endif -#ifdef ARDUINO_ESP8266_RELEASE_2_3_0 // Disable not supported features in core 2.3.0 +#ifdef ARDUINO_ESP8266_RELEASE_2_3_0 // Disable not supported features in core 2.3.0 #undef USE_MQTT_TLS_CA_CERT #endif +#ifdef DEBUG_TASMOTA_CORE +#define DEBUG_CORE_LOG(...) AddLog_Debug(__VA_ARGS__) +#else +#define DEBUG_CORE_LOG(...) +#endif + +#ifdef DEBUG_TASMOTA_DRIVER +#define DEBUG_DRIVER_LOG(...) AddLog_Debug(__VA_ARGS__) +#else +#define DEBUG_DRIVER_LOG(...) +#endif + +#ifdef DEBUG_TASMOTA_SENSOR +#define DEBUG_SENSOR_LOG(...) AddLog_Debug(__VA_ARGS__) +#else +#define DEBUG_SENSOR_LOG(...) +#endif + #endif // _SONOFF_POST_H_ diff --git a/sonoff/sonoff_template.h b/sonoff/sonoff_template.h index 631d2e712..f85543cd0 100644 --- a/sonoff/sonoff_template.h +++ b/sonoff/sonoff_template.h @@ -184,6 +184,34 @@ enum UserSelectablePins { GPIO_LEDLNK, // Link led GPIO_LEDLNK_INV, // Inverted link led GPIO_ARIRFSEL, // Arilux RF Receive input selected + GPIO_BUZZER, // Buzzer + GPIO_BUZZER_INV, // Inverted buzzer + GPIO_OLED_RESET, // OLED Display Reset + GPIO_SOLAXX1_TX, // Solax Inverter tx pin + GPIO_SOLAXX1_RX, // Solax Inverter rx pin + GPIO_ZIGBEE_TX, // Zigbee Serial interface + GPIO_ZIGBEE_RX, // Zigbee Serial interface + GPIO_RDM6300_RX, // RDM6300 RX + GPIO_IBEACON_TX, // HM17 IBEACON TX + GPIO_IBEACON_RX, // HM17 IBEACON RX + GPIO_A4988_DIR, // A4988 direction pin + GPIO_A4988_STP, // A4988 step pin + GPIO_A4988_ENA, // A4988 enabled pin + GPIO_A4988_MS1, // A4988 microstep pin1 + GPIO_A4988_MS2, // A4988 microstep pin2 + GPIO_A4988_MS3, // A4988 microstep pin3 + GPIO_DDS2382_TX, // DDS2382 Serial interface + GPIO_DDS2382_RX, // DDS2382 Serial interface + GPIO_DDSU666_TX, // DDSU666 Serial interface + GPIO_DDSU666_RX, // DDSU666 Serial interface + GPIO_SM2135_CLK, // SM2135 Clk + GPIO_SM2135_DAT, // SM2135 Dat + GPIO_DEEPSLEEP, // Kill switch for deepsleep + GPIO_EXS_ENABLE, // EXS MCU Enable + GPIO_ARDUINO_TXD, // Arduino Slave TX + GPIO_ARDUINO_RXD, // Arduino Slave RX + GPIO_ARDUINO_RST, // Arduino Reset Pin + GPIO_ARDUINO_RST_INV, // Arduino Reset Pin inverted GPIO_SENSOR_END }; // Programmer selectable GPIO functionality @@ -251,8 +279,24 @@ const char kSensorNames[] PROGMEM = D_SENSOR_ADE7953_IRQ "|" D_SENSOR_LED_LINK "|" D_SENSOR_LED_LINK "i|" D_SENSOR_ARIRFSEL "|" + D_SENSOR_BUZZER "|" D_SENSOR_BUZZER "i|" + D_SENSOR_OLED_RESET "|" + D_SENSOR_SOLAXX1_TX "|" D_SENSOR_SOLAXX1_RX "|" + D_SENSOR_ZIGBEE_TXD "|" D_SENSOR_ZIGBEE_RXD "|" + D_SENSOR_RDM6300_RX "|" + D_SENSOR_IBEACON_TX "|" D_SENSOR_IBEACON_RX "|" + D_SENSOR_A4988_DIR "|" D_SENSOR_A4988_STP "|" D_SENSOR_A4988_ENA "|" D_SENSOR_A4988_MS1 "|" D_SENSOR_A4988_MS2 "|" D_SENSOR_A4988_MS3 "|" + D_SENSOR_DDS2382_TX "|" D_SENSOR_DDS2382_RX "|" + D_SENSOR_DDSU666_TX "|" D_SENSOR_DDSU666_RX "|" + D_SENSOR_SM2135_CLK "|" D_SENSOR_SM2135_DAT "|" + D_SENSOR_DEEPSLEEP "|" D_SENSOR_EXS_ENABLE "|" + D_SENSOR_ARDUINO_TX "|" D_SENSOR_ARDUINO_RX "|" D_SENSOR_ARDUINO_RESET "|" D_SENSOR_ARDUINO_RESET "i|" ; +const char kSensorNamesFixed[] PROGMEM = + D_SENSOR_SPI_MISO "|" D_SENSOR_SPI_MOSI "|" D_SENSOR_SPI_CLK "|" + D_SENSOR_USER; + // User selectable ADC0 functionality enum UserSelectableAdc0 { ADC0_NONE, // Not used @@ -353,6 +397,8 @@ enum SupportedModules { WAGA, SYF05, SONOFF_L1, + SONOFF_IFAN03, + EXS_DIMMER, MAXMODULE}; #define USER_MODULE 255 @@ -475,6 +521,10 @@ const uint8_t kGpioNiceList[] PROGMEM = { GPIO_CNTR3_NP, GPIO_CNTR4, GPIO_CNTR4_NP, +#endif +#ifdef USE_BUZZER + GPIO_BUZZER, // Buzzer + GPIO_BUZZER_INV, // Inverted buzzer #endif GPIO_TXD, // Serial interface GPIO_RXD, // Serial interface @@ -493,24 +543,55 @@ const uint8_t kGpioNiceList[] PROGMEM = { #endif #ifdef USE_DISPLAY GPIO_BACKLIGHT, // Display backlight control + GPIO_OLED_RESET, // OLED Display Reset #endif #ifdef USE_DHT GPIO_DHT11, // DHT11 GPIO_DHT22, // DHT21, DHT22, AM2301, AM2302, AM2321 GPIO_SI7021, // iTead SI7021 #endif -#if defined(USE_DS18B20) || defined(USE_DS18x20) || defined(USE_DS18x20_LEGACY) +#ifdef USE_DS18x20 GPIO_DSB, // Single wire DS18B20 or DS18S20 #endif -#if defined(USE_LIGHT) && defined(USE_WS2812) + +// Light +#ifdef USE_LIGHT +#ifdef USE_WS2812 GPIO_WS2812, // WS2812 Led string #endif -#ifdef USE_IR_REMOTE +#ifdef USE_ARILUX_RF + GPIO_ARIRFRCV, // AriLux RF Receive input + GPIO_ARIRFSEL, // Arilux RF Receive input selected +#endif +#ifdef USE_MY92X1 + GPIO_DI, // my92x1 PWM input + GPIO_DCKI, // my92x1 CLK input +#endif // USE_MY92X1 +#ifdef USE_SM16716 + GPIO_SM16716_CLK, // SM16716 CLOCK + GPIO_SM16716_DAT, // SM16716 DATA + GPIO_SM16716_SEL, // SM16716 SELECT +#endif // USE_SM16716 +#ifdef USE_SM2135 + GPIO_SM2135_CLK, // SM2135 CLOCK + GPIO_SM2135_DAT, // SM2135 DATA +#endif // USE_SM2135 +#ifdef USE_TUYA_MCU + GPIO_TUYA_TX, // Tuya Serial interface + GPIO_TUYA_RX, // Tuya Serial interface +#endif +#ifdef USE_EXS_DIMMER + GPIO_EXS_ENABLE, // EXS MCU Enable +#endif +#endif // USE_LIGHT + +#if defined(USE_IR_REMOTE) || defined(USE_IR_REMOTE_FULL) GPIO_IRSEND, // IR remote -#ifdef USE_IR_RECEIVE +#if defined(USE_IR_RECEIVE) || defined(USE_IR_REMOTE_FULL) GPIO_IRRECV, // IR receiver #endif #endif + #ifdef USE_RC_SWITCH GPIO_RFSEND, // RF transmitter GPIO_RFRECV, // RF receiver @@ -531,19 +612,22 @@ const uint8_t kGpioNiceList[] PROGMEM = { GPIO_HX711_SCK, // HX711 Load Cell clock GPIO_HX711_DAT, // HX711 Load Cell data #endif -#if defined(USE_ENERGY_SENSOR) && defined(USE_HLW8012) + +// Energy sensors +#ifdef USE_ENERGY_SENSOR +#ifdef USE_HLW8012 GPIO_NRG_SEL, // HLW8012/HLJ-01 Sel output (1 = Voltage) GPIO_NRG_SEL_INV, // HLW8012/HLJ-01 Sel output (0 = Voltage) GPIO_NRG_CF1, // HLW8012/HLJ-01 CF1 voltage / current GPIO_HLW_CF, // HLW8012 CF power GPIO_HJL_CF, // HJL-01/BL0937 CF power #endif -#if defined(USE_ENERGY_SENSOR) && defined(USE_I2C) && defined(USE_ADE7953) +#if defined(USE_I2C) && defined(USE_ADE7953) GPIO_ADE7953_IRQ, // ADE7953 IRQ #endif GPIO_CSE7766_TX, // CSE7766 Serial interface (S31 and Pow R2) GPIO_CSE7766_RX, // CSE7766 Serial interface (S31 and Pow R2) -#if defined(USE_ENERGY_SENSOR) && defined(USE_MCP39F501) +#ifdef USE_MCP39F501 GPIO_MCP39F5_TX, // MCP39F501 Serial interface (Shelly2) GPIO_MCP39F5_RX, // MCP39F501 Serial interface (Shelly2) GPIO_MCP39F5_RST, // MCP39F501 Reset (Shelly2) @@ -568,10 +652,28 @@ const uint8_t kGpioNiceList[] PROGMEM = { GPIO_SDM630_TX, // SDM630 Serial interface GPIO_SDM630_RX, // SDM630 Serial interface #endif +#ifdef USE_DDS2382 + GPIO_DDS2382_TX, // DDS2382 Serial interface + GPIO_DDS2382_RX, // DDS2382 Serial interface +#endif +#ifdef USE_DDSU666 + GPIO_DDSU666_TX, // DDSU666 Serial interface + GPIO_DDSU666_RX, // DDSU666 Serial interface +#endif // USE_DDSU666 +#ifdef USE_SOLAX_X1 + GPIO_SOLAXX1_TX, // Solax Inverter tx pin + GPIO_SOLAXX1_RX, // Solax Inverter rx pin +#endif // USE_SOLAX_X1 +#endif // USE_ENERGY_SENSOR + #ifdef USE_SERIAL_BRIDGE GPIO_SBR_TX, // Serial Bridge Serial interface GPIO_SBR_RX, // Serial Bridge Serial interface #endif +#ifdef USE_ZIGBEE + GPIO_ZIGBEE_TX, // Zigbee Serial interface + GPIO_ZIGBEE_RX, // Zigbee Serial interface +#endif #ifdef USE_MHZ19 GPIO_MHZ_TXD, // MH-Z19 Serial interface GPIO_MHZ_RXD, // MH-Z19 Serial interface @@ -593,10 +695,6 @@ const uint8_t kGpioNiceList[] PROGMEM = { #ifdef USE_MP3_PLAYER GPIO_MP3_DFR562, // RB-DFR-562, DFPlayer Mini MP3 Player Serial interface #endif -#if defined(USE_LIGHT) && defined(USE_TUYA_DIMMER) - GPIO_TUYA_TX, // Tuya Serial interface - GPIO_TUYA_RX, // Tuya Serial interface -#endif #ifdef USE_AZ7798 GPIO_AZ_TXD, // AZ-Instrument 7798 CO2 datalogger Serial interface GPIO_AZ_RXD, // AZ-Instrument 7798 CO2 datalogger Serial interface @@ -605,6 +703,19 @@ const uint8_t kGpioNiceList[] PROGMEM = { GPIO_PN532_TXD, // PN532 HSU Tx GPIO_PN532_RXD, // PN532 HSU Rx #endif +#ifdef USE_ARDUINO_SLAVE + GPIO_ARDUINO_TXD, // Arduino Slave TX + GPIO_ARDUINO_RXD, // Arduino Slave RX + GPIO_ARDUINO_RST, // Arduino Reset Pin + GPIO_ARDUINO_RST_INV, // Arduino Reset Pin inverted +#endif +#ifdef USE_RDM6300 + GPIO_RDM6300_RX, +#endif +#ifdef USE_IBEACON + GPIO_IBEACON_RX, + GPIO_IBEACON_TX, +#endif #ifdef USE_MGC3130 GPIO_MGC3130_XFER, GPIO_MGC3130_RESET, @@ -614,28 +725,27 @@ const uint8_t kGpioNiceList[] PROGMEM = { GPIO_MAX31855CLK, // MAX31855 Serial interface GPIO_MAX31855DO, // MAX31855 Serial interface #endif -#ifdef USE_LIGHT - GPIO_DI, // my92x1 PWM input - GPIO_DCKI, // my92x1 CLK input -#ifdef USE_SM16716 - GPIO_SM16716_CLK, // SM16716 CLOCK - GPIO_SM16716_DAT, // SM16716 DATA - GPIO_SM16716_SEL, // SM16716 SELECT -#endif // USE_SM16716 -#endif // USE_LIGHT #ifdef ROTARY_V1 GPIO_ROT1A, // Rotary switch1 A Pin GPIO_ROT1B, // Rotary switch1 B Pin GPIO_ROT2A, // Rotary switch2 A Pin GPIO_ROT2B, // Rotary switch2 B Pin #endif -#ifdef USE_ARILUX_RF - GPIO_ARIRFRCV, // AriLux RF Receive input - GPIO_ARIRFSEL, // Arilux RF Receive input selected -#endif #ifdef USE_HRE GPIO_HRE_CLOCK, - GPIO_HRE_DATA + GPIO_HRE_DATA, +#endif +#ifdef USE_A4988_STEPPER + GPIO_A4988_DIR, // A4988 direction pin + GPIO_A4988_STP, // A4988 step pin + // folowing are not mandatory + GPIO_A4988_ENA, // A4988 enabled pin + GPIO_A4988_MS1, // A4988 microstep pin1 + GPIO_A4988_MS2, // A4988 microstep pin2 + GPIO_A4988_MS3, // A4988 microstep pin3 +#endif +#ifdef USE_DEEPSLEEP + GPIO_DEEPSLEEP #endif }; @@ -657,14 +767,21 @@ const uint8_t kModuleNiceList[] PROGMEM = { SONOFF_T13, SONOFF_LED, // Sonoff Light Devices SONOFF_BN, -#ifdef USE_PS_16_DZ +#ifdef USE_SONOFF_L1 SONOFF_L1, #endif SONOFF_B1, // Sonoff Light Bulbs SLAMPHER, +#ifdef USE_SONOFF_SC SONOFF_SC, // Sonoff Environmemtal Sensor +#endif +#ifdef USE_SONOFF_IFAN SONOFF_IFAN02, // Sonoff Fan + SONOFF_IFAN03, +#endif +#ifdef USE_SONOFF_RF SONOFF_BRIDGE, // Sonoff Bridge +#endif SONOFF_SV, // Sonoff Development Devices SONOFF_DEV, CH1, // Relay Devices @@ -694,7 +811,7 @@ const uint8_t kModuleNiceList[] PROGMEM = { OBI2, MANZOKU_EU_4, ESP_SWITCH, // Switch Devices -#ifdef USE_TUYA_DIMMER +#ifdef USE_TUYA_MCU TUYA_DIMMER, // Dimmer Devices #endif #ifdef USE_ARMTRONIX_DIMMERS @@ -702,6 +819,9 @@ const uint8_t kModuleNiceList[] PROGMEM = { #endif #ifdef USE_PS_16_DZ PS_16_DZ, +#endif +#ifdef USE_EXS_DIMMER + EXS_DIMMER, #endif H801, // Light Devices MAGICHOME, @@ -724,7 +844,7 @@ const uint8_t kModuleNiceList[] PROGMEM = { // Default module settings const mytmplt kModules[MAXMODULE] PROGMEM = { - { "Sonoff Basic", // Sonoff Basic (ESP8266) + { "Sonoff Basic", // SONOFF_BASIC - Sonoff Basic (ESP8266) GPIO_KEY1, // GPIO00 Button GPIO_USER, // GPIO01 Serial RXD and Optional sensor GPIO_USER, // GPIO02 Only available on newer Sonoff Basic R2 V1 @@ -744,7 +864,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { 0, // GPIO16 0 // ADC0 Analog input }, - { "Sonoff RF", // Sonoff RF (ESP8266) + { "Sonoff RF", // SONOFF_RF - Sonoff RF (ESP8266) GPIO_KEY1, // GPIO00 Button GPIO_USER, // GPIO01 Serial RXD and Optional sensor GPIO_USER, // GPIO02 Optional sensor @@ -762,7 +882,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, // GPIO14 Optional sensor 0, 0, 0 }, - { "Sonoff SV", // Sonoff SV (ESP8266) + { "Sonoff SV", // SONOFF_SV - Sonoff SV (ESP8266) GPIO_KEY1, // GPIO00 Button GPIO_USER, // GPIO01 Serial RXD and Optional sensor 0, @@ -781,7 +901,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { 0, 0, ADC0_USER // ADC0 Analog input }, - { "Sonoff TH", // Sonoff TH10/16 (ESP8266) + { "Sonoff TH", // SONOFF_TH - Sonoff TH10/16 (ESP8266) GPIO_KEY1, // GPIO00 Button GPIO_USER, // GPIO01 Serial RXD and Optional sensor 0, @@ -799,7 +919,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, // GPIO14 Optional sensor 0, 0, 0 }, - { "Sonoff Dual", // Sonoff Dual (ESP8266) + { "Sonoff Dual", // SONOFF_DUAL - Sonoff Dual (ESP8266) 0, GPIO_TXD, // GPIO01 Relay control 0, @@ -817,7 +937,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, // GPIO14 Optional sensor 0, 0, 0 }, - { "Sonoff Pow", // Sonoff Pow (ESP8266 - HLW8012) + { "Sonoff Pow", // SONOFF_POW - Sonoff Pow (ESP8266 - HLW8012) GPIO_KEY1, // GPIO00 Button 0, 0, 0, 0, GPIO_NRG_SEL, // GPIO05 HLW8012 Sel output (1 = Voltage) @@ -833,7 +953,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_LED1, // GPIO15 Blue Led (0 = On, 1 = Off) - Link and Power status 0, 0 }, - { "Sonoff 4CH", // Sonoff 4CH (ESP8285) + { "Sonoff 4CH", // SONOFF_4CH - Sonoff 4CH (ESP8285) GPIO_KEY1, // GPIO00 Button 1 GPIO_USER, // GPIO01 Serial RXD and Optional sensor GPIO_USER, // GPIO02 Optional sensor @@ -852,7 +972,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_REL4, // GPIO15 Red Led and Relay 4 (0 = Off, 1 = On) 0, 0 }, - { "Sonoff S2X", // Sonoff S20, S22 and S26 Smart Socket (ESP8266) + { "Sonoff S2X", // SONOFF_S2X - Sonoff S20, S22 and S26 Smart Socket (ESP8266) GPIO_KEY1, // GPIO00 Button GPIO_USER, // GPIO01 Serial RXD and Optional sensor GPIO_USER, // GPIO02 Optional sensor @@ -868,7 +988,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_LED1_INV, // GPIO13 Green/Blue Led (0 = On, 1 = Off) 0, 0, 0, 0 }, - { "Slampher", // Slampher (ESP8266) + { "Slampher", // SLAMPHER - Slampher (ESP8266) GPIO_KEY1, // GPIO00 Button GPIO_USER, // GPIO01 Serial RXD and Optional sensor 0, @@ -884,7 +1004,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status 0, 0, 0, 0 }, - { "Sonoff Touch", // Sonoff Touch (ESP8285) + { "Sonoff Touch", // SONOFF_TOUCH - Sonoff Touch (ESP8285) GPIO_KEY1, // GPIO00 Button GPIO_USER, // GPIO01 Serial RXD and Optional sensor 0, @@ -900,7 +1020,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) 0, 0, 0, 0 }, - { "Sonoff LED", // Sonoff LED (ESP8266) + { "Sonoff LED", // SONOFF_LED - Sonoff LED (ESP8266) GPIO_KEY1, // GPIO00 Button 0, 0, 0, GPIO_USER, // GPIO04 Optional sensor (PWM3 Green) @@ -917,7 +1037,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, // GPIO15 Optional sensor (PWM4 Blue) 0, 0 }, - { "1 Channel", // 1 Channel Inching/Latching Relay using (PSA-B01 - ESP8266 and PSF-B01 - ESP8285) + { "1 Channel", // CH1 - 1 Channel Inching/Latching Relay using (PSA-B01 - ESP8266 and PSF-B01 - ESP8285) GPIO_KEY1, // GPIO00 Button 0, 0, 0, 0, 0, // GPIO06 (SD_CLK Flash) @@ -930,7 +1050,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) - Link and Power status 0, 0, 0, 0 }, - { "4 Channel", // 4 Channel Inching/Latching Relays (ESP8266) + { "4 Channel", // CH4 - 4 Channel Inching/Latching Relays (ESP8266) 0, GPIO_TXD, // GPIO01 Relay control 0, @@ -946,7 +1066,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status 0, 0, 0, 0 }, - { "Motor C/AC", // Motor Clockwise / Anti clockwise (PSA-B01 - ESP8266) + { "Motor C/AC", // MOTOR - Motor Clockwise / Anti clockwise (PSA-B01 - ESP8266) GPIO_KEY1, // GPIO00 Button 0, 0, 0, 0, 0, // GPIO06 (SD_CLK Flash) @@ -959,7 +1079,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) - Link and Power status 0, 0, 0, 0 }, - { "ElectroDragon", // ElectroDragon IoT Relay Board (ESP8266) + { "ElectroDragon", // ELECTRODRAGON - ElectroDragon IoT Relay Board (ESP8266) GPIO_KEY2, // GPIO00 Button 2 GPIO_USER, // GPIO01 Serial RXD and Optional sensor GPIO_KEY1, // GPIO02 Button 1 @@ -979,7 +1099,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_LED1, // GPIO16 Green/Blue Led (1 = On, 0 = Off) - Link and Power status ADC0_USER // ADC0 A0 Analog input }, - { "EXS Relay(s)", // ES-Store Latching relay(s) (ESP8266) + { "EXS Relay(s)", // EXS_RELAY - ES-Store Latching relay(s) (ESP8266) // https://ex-store.de/ESP8266-WiFi-Relay-V31 // V3.1 Module Pin 1 VCC 3V3, Module Pin 6 GND // https://ex-store.de/2-Kanal-WiFi-WLan-Relay-V5-Blackline-fuer-Unterputzmontage @@ -1002,7 +1122,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, // GPIO16 V3.1 Module Pin 4 - V5.0 GPIO_REL4_INV Relay2 ( 1 = On) 0 }, - { "WiOn", // Indoor Tap (ESP8266) + { "WiOn", // WION - Indoor Tap (ESP8266) // https://www.amazon.com/gp/product/B00ZYLUBJU/ref=s9_acsd_al_bw_c_x_3_w GPIO_USER, // GPIO00 Optional sensor (pm clock) 0, @@ -1020,7 +1140,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_REL1, // GPIO15 Relay (0 = Off, 1 = On) 0, 0 }, - { "Generic", // Any ESP8266/ESP8285 device like WeMos and NodeMCU hardware (ESP8266) + { "Generic", // WEMOS - Any ESP8266/ESP8285 device like WeMos and NodeMCU hardware (ESP8266) GPIO_USER, // GPIO00 D3 Wemos Button Shield GPIO_USER, // GPIO01 TX Serial RXD GPIO_USER, // GPIO02 D4 Wemos DHT Shield @@ -1040,7 +1160,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, // GPIO16 D0 Wemos Wake ADC0_USER // ADC0 A0 Analog input }, - { "Sonoff Dev", // Sonoff Dev (ESP8266) + { "Sonoff Dev", // SONOFF_DEV - Sonoff Dev (ESP8266) GPIO_KEY1, // GPIO00 E-FW Button GPIO_USER, // GPIO01 TX Serial RXD and Optional sensor 0, // GPIO02 @@ -1060,7 +1180,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { 0, // GPIO16 ADC0_USER // ADC0 A0 Analog input }, - { "H801", // Lixada H801 Wifi (ESP8266) + { "H801", // H801 - Lixada H801 Wifi (ESP8266) GPIO_USER, // GPIO00 E-FW Button GPIO_LED1, // GPIO01 Green LED - Link and Power status GPIO_USER, // GPIO02 TX and Optional sensor - Pin next to TX on the PCB @@ -1079,7 +1199,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_PWM1, // GPIO15 Red 0, 0 }, - { "Sonoff SC", // Sonoff SC (ESP8266) + { "Sonoff SC", // SONOFF_SC - onoff SC (ESP8266) GPIO_KEY1, // GPIO00 Button GPIO_TXD, // GPIO01 RXD to ATMEGA328P GPIO_USER, // GPIO02 Optional sensor @@ -1095,7 +1215,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) - Link and Power status 0, 0, 0, 0 }, - { "Sonoff BN-SZ", // Sonoff BN-SZ01 Ceiling led (ESP8285) + { "Sonoff BN-SZ", // SONOFF_BN - Sonoff BN-SZ01 Ceiling led (ESP8285) 0, 0, 0, 0, 0, 0, // GPIO06 (SD_CLK Flash) // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) @@ -1107,7 +1227,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_LED1_INV, // GPIO13 Red Led (0 = On, 1 = Off) - Link and Power status 0, 0, 0, 0 }, - { "Sonoff 4CH Pro", // Sonoff 4CH Pro (ESP8285) + { "Sonoff 4CH Pro", // SONOFF_4CHPRO - Sonoff 4CH Pro (ESP8285) GPIO_KEY1, // GPIO00 Button 1 GPIO_USER, // GPIO01 Serial RXD and Optional sensor GPIO_USER, // GPIO02 Optional sensor @@ -1126,7 +1246,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_REL4, // GPIO15 Red Led and Relay 4 (0 = Off, 1 = On) 0, 0 }, - { "Huafan SS", // Hua Fan Smart Socket (ESP8266) - like Sonoff Pow + { "Huafan SS", // HUAFAN_SS - Hua Fan Smart Socket (ESP8266) - like Sonoff Pow GPIO_LEDLNK_INV, // GPIO00 Blue Led (0 = On, 1 = Off) - Link status 0, 0, GPIO_LED1_INV, // GPIO03 Red Led (0 = On, 1 = Off) - Power status @@ -1143,7 +1263,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_HLW_CF, // GPIO14 HLW8012 CF power 0, 0, 0 }, - { "Sonoff Bridge", // Sonoff RF Bridge 433 (ESP8285) + { "Sonoff Bridge", // SONOFF_BRIDGE - Sonoff RF Bridge 433 (ESP8285) GPIO_KEY1, // GPIO00 Button GPIO_TXD, // GPIO01 RF bridge control GPIO_USER, // GPIO02 Optional sensor @@ -1161,7 +1281,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, // GPIO14 Optional sensor 0, 0, 0 }, - { "Sonoff B1", // Sonoff B1 (ESP8285 - my9231) + { "Sonoff B1", // SONOFF_B1 - Sonoff B1 (ESP8285 - my9231) GPIO_KEY1, // GPIO00 Pad GPIO_USER, // GPIO01 Serial RXD and Optional sensor pad GPIO_USER, // GPIO02 Optional sensor SDA pad @@ -1178,7 +1298,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_DCKI, // GPIO14 my9231 DCKI 0, 0, 0 }, - { "AiLight", // Ai-Thinker RGBW led (ESP8266 - my9291) + { "AiLight", // AILIGHT - Ai-Thinker RGBW led (ESP8266 - my9291) GPIO_KEY1, // GPIO00 Pad GPIO_USER, // GPIO01 Serial RXD and Optional sensor pad GPIO_USER, // GPIO02 Optional sensor SDA pad @@ -1196,7 +1316,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_DCKI, // GPIO15 my9291 DCKI 0, 0 }, - { "Sonoff T1 1CH", // Sonoff T1 1CH (ESP8285) + { "Sonoff T1 1CH", // SONOFF_T11 - Sonoff T1 1CH (ESP8285) GPIO_KEY1, // GPIO00 Button 1 GPIO_USER, // GPIO01 Serial RXD and Optional sensor GPIO_USER, // GPIO02 Optional Sensor (J3 Pin 5) @@ -1212,7 +1332,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status 0, 0, 0, 0 }, - { "Sonoff T1 2CH", // Sonoff T1 2CH (ESP8285) + { "Sonoff T1 2CH", // SONOFF_T12 - Sonoff T1 2CH (ESP8285) GPIO_KEY1, // GPIO00 Button 1 GPIO_USER, // GPIO01 Serial RXD and Optional sensor GPIO_USER, // GPIO02 Optional Sensor (J3 Pin 5) @@ -1229,7 +1349,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status 0, 0, 0, 0 }, - { "Sonoff T1 3CH", // Sonoff T1 3CH (ESP8285) + { "Sonoff T1 3CH", // SONOFF_T13 - Sonoff T1 3CH (ESP8285) GPIO_KEY1, // GPIO00 Button 1 GPIO_USER, // GPIO01 Serial RXD and Optional sensor GPIO_USER, // GPIO02 Optional Sensor (J3 Pin 5) @@ -1246,11 +1366,15 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status 0, 0, 0, 0 }, - { "Supla Espablo", // Supla Espablo (ESP8266) + { "Supla Espablo", // SUPLA1 - Supla Espablo (ESP8266) // http://www.wykop.pl/ramka/3325399/diy-supla-do-puszki-instalacyjnej-podtynkowej-supla-org/ 0, // GPIO00 Flash jumper GPIO_USER, // GPIO01 Serial RXD and Optional sensor +#ifdef USE_DS18x20 GPIO_DSB, // GPIO02 DS18B20 sensor +#else + GPIO_USER, // GPIO02 Optional sensor +#endif GPIO_USER, // GPIO03 Serial TXD and Optional sensor GPIO_KEY1, // GPIO04 Button 1 GPIO_REL1, // GPIO05 Relay 1 (0 = Off, 1 = On) @@ -1267,7 +1391,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_LED1, // GPIO16 Led (1 = On, 0 = Off) - Link and Power status ADC0_USER // ADC0 A0 Analog input }, - { "Witty Cloud", // Witty Cloud Dev Board (ESP8266) + { "Witty Cloud", // WITTY - Witty Cloud Dev Board (ESP8266) // https://www.aliexpress.com/item/ESP8266-serial-WIFI-Witty-cloud-Development-Board-ESP-12F-module-MINI-nodemcu/32643464555.html GPIO_USER, // GPIO00 D3 flash push button on interface board GPIO_USER, // GPIO01 Serial RXD and Optional sensor @@ -1288,7 +1412,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, // GPIO16 D0 optional sensor ADC0_USER // ADC0 A0 Light sensor / Requires USE_ADC_VCC in user_config.h to be disabled }, - { "Yunshan Relay", // Yunshan Wifi Relay (ESP8266) + { "Yunshan Relay", // YUNSHAN - Yunshan Wifi Relay (ESP8266) // https://www.ebay.com/p/Esp8266-220v-10a-Network-Relay-WiFi-Module/1369583381 // Schematics and Info https://ucexperiment.wordpress.com/2016/12/18/yunshan-esp8266-250v-15a-acdc-network-wifi-relay-module/ 0, // GPIO00 Flash jumper - Module Pin 8 @@ -1305,7 +1429,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { // GPIO11 (SD_CMD Flash) 0, 0, 0, 0, 0, 0 }, - { "MagicHome", // Magic Home (aka Flux-light) (ESP8266) and Arilux LC10 (ESP8285) + { "MagicHome", // MAGICHOME - Magic Home (aka Flux-light) (ESP8266) and Arilux LC10 (ESP8285) // https://www.aliexpress.com/item/Magic-Home-Mini-RGB-RGBW-Wifi-Controller-For-Led-Strip-Panel-light-Timing-Function-16million-colors/32686853650.html 0, GPIO_USER, // GPIO01 Serial RXD and Optional sensor @@ -1325,7 +1449,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_ARIRFSEL, // GPIO15 RF receiver control (Arilux LC10) 0, 0 }, - { "Luani HVIO", // ESP8266_HVIO + { "Luani HVIO", // LUANIHVIO - ESP8266_HVIO // https://luani.de/projekte/esp8266-hvio/ 0, // GPIO00 Flash jumper GPIO_USER, // GPIO01 Serial RXD and Optional sensor @@ -1346,7 +1470,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { 0, ADC0_USER // ADC0 A0 Analog input }, - { "KMC 70011", // KMC 70011 + { "KMC 70011", // KMC_70011 - KMC 70011 // https://www.amazon.com/KMC-Timing-Monitoring-Network-125V-240V/dp/B06XRX2GTQ GPIO_KEY1, // GPIO00 Button 0, 0, 0, @@ -1363,7 +1487,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_REL1, // GPIO14 Relay 0, 0, 0 }, - { "Arilux LC01", // Arilux AL-LC01 (ESP8285) + { "Arilux LC01", // ARILUX_LC01 - Arilux AL-LC01 (ESP8285) // https://www.banggood.com/nl/ARILUX-AL-LC01-Super-Mini-LED-WIFI-Smart-RGB-Controller-For-RGB-LED-Strip-Light-DC-9-12V-p-1058603.html // (PwmFrequency 1111Hz) GPIO_KEY1, // GPIO00 Optional Button @@ -1383,7 +1507,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, // GPIO14 RGBW LED White (optional - set to PWM4 for Cold White or Warm White) 0, 0, 0 }, - { "Arilux LC11", // Arilux AL-LC11 (ESP8266) + { "Arilux LC11", // ARILUX_LC11 - Arilux AL-LC11 (ESP8266) // https://www.banggood.com/nl/ARILUX-AL-LC11-Super-Mini-LED-WIFI-APP-Controller-RF-Remote-Control-For-RGBWW-LED-Strip-DC9-28V-p-1085112.html // (PwmFrequency 540Hz) GPIO_KEY1, // GPIO00 Optional Button @@ -1404,7 +1528,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_ARIRFRCV, // GPIO15 RF receiver input 0, 0 }, - { "Sonoff Dual R2", // Sonoff Dual R2 (ESP8285) + { "Sonoff Dual R2", // SONOFF_DUAL_R2 - Sonoff Dual R2 (ESP8285) GPIO_USER, // GPIO00 Button 0 on header (0 = On, 1 = Off) GPIO_USER, // GPIO01 Serial RXD and Optional sensor 0, @@ -1421,7 +1545,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status 0, 0, 0, 0 }, - { "Arilux LC06", // Arilux AL-LC06 (ESP8285) + { "Arilux LC06", // ARILUX_LC06 - Arilux AL-LC06 (ESP8285) // https://www.banggood.com/ARILUX-AL-LC06-LED-WIFI-Smartphone-Controller-Romote-5-Channels-DC12-24V-For-RGBWW-Strip-light-p-1061476.html GPIO_KEY1, // GPIO00 Optional Button GPIO_USER, // GPIO01 Serial RXD and Optional sensor @@ -1441,7 +1565,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, // GPIO15 RGBW LED White 0, 0 }, - { "Sonoff S31", // Sonoff S31 (ESP8266 - CSE7766) + { "Sonoff S31", // SONOFF_S31 - Sonoff S31 (ESP8266 - CSE7766) GPIO_KEY1, // GPIO00 Button GPIO_CSE7766_TX, // GPIO01 Serial RXD 4800 baud 8E1 CSE7766 energy sensor 0, @@ -1457,7 +1581,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) - Link and Power status 0, 0, 0, 0 }, - { "Zengge WF017", // Zenggee ZJ-WF017-A (ESP12S)) + { "Zengge WF017", // ZENGGE_ZF_WF017 - Zenggee ZJ-WF017-A (ESP12S)) // https://www.ebay.com/p/Smartphone-Android-IOS-WiFi-Music-Controller-for-RGB-5050-3528-LED-Strip-Light/534446632?_trksid=p2047675.l2644 GPIO_KEY1, // GPIO00 Optional Button 0, @@ -1476,7 +1600,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_PWM3, // GPIO14 RGB LED Blue 0, 0, 0 }, - { "Sonoff Pow R2", // Sonoff Pow R2 (ESP8285 - CSE7766) + { "Sonoff Pow R2", // SONOFF_POW_R2 - Sonoff Pow R2 (ESP8285 - CSE7766) GPIO_KEY1, // GPIO00 Button GPIO_CSE7766_TX, // GPIO01 Serial RXD 4800 baud 8E1 CSE7766 energy sensor 0, @@ -1492,7 +1616,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status 0, 0, 0, 0 }, - { "Sonoff iFan02", // Sonoff iFan02 (ESP8285) + { "Sonoff iFan02", // SONOFF_IFAN02 - Sonoff iFan02 (ESP8285) GPIO_KEY1, // GPIO00 WIFI_KEY0 Virtual button 1 as feedback from RC GPIO_USER, // GPIO01 ESP_TXD Serial RXD and Optional sensor 0, // GPIO02 ESP_LOG @@ -1511,7 +1635,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_REL4, // GPIO15 WIFI_O3 Relay 4 (0 = Off, 1 = On) controlling the fan 0, 0 }, - { "BlitzWolf SHP", // BlitzWolf BW-SHP2 and BW-SHP6 (ESP8285 - BL0937 or HJL-01 Energy Monitoring) + { "BlitzWolf SHP", // BLITZWOLF_BWSHP - BlitzWolf BW-SHP2 and BW-SHP6 (ESP8285 - BL0937 or HJL-01 Energy Monitoring) // https://www.banggood.com/BlitzWolf-BW-SHP2-Smart-WIFI-Socket-EU-Plug-220V-16A-Work-with-Amazon-Alexa-Google-Assistant-p-1292899.html // https://www.amazon.de/Steckdose-Homecube-intelligente-Verbrauchsanzeige-funktioniert/dp/B076Q2LKHG/ref=sr_1_fkmr0_1 // https://www.amazon.de/Intelligente-Stromverbrauch-Fernsteurung-Schaltbare-Energieklasse/dp/B076WZQS4S/ref=sr_1_1 @@ -1534,7 +1658,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_REL1, // GPIO15 Relay (0 = Off, 1 = On) 0, 0 }, - { "Shelly 1", // Shelly1 Open Source (ESP8266 - 2MB) - https://shelly.cloud/shelly1-open-source/ + { "Shelly 1", // SHELLY1 - Shelly1 Open Source (ESP8266 - 2MB) - https://shelly.cloud/shelly1-open-source/ 0, // GPIO00 - Can be changed to GPIO_USER, only if Shelly is powered with 12V DC 0, // GPIO01 Serial RXD - Can be changed to GPIO_USER, only if Shelly is powered with 12V DC 0, @@ -1549,7 +1673,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { // GPIO11 (SD_CMD Flash) 0, 0, 0, 0, 0, 0 }, - { "Shelly 2", // Shelly2 (ESP8266 - 2MB) - https://shelly.cloud/shelly2/ + { "Shelly 2", // SHELLY2 - Shelly2 (ESP8266 - 2MB) - https://shelly.cloud/shelly2/ 0, GPIO_MCP39F5_TX, // GPIO01 MCP39F501 Serial input 0, @@ -1569,7 +1693,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { 0, 0 }, - { "Xiaomi Philips", // Xiaomi Philips bulb (ESP8266) + { "Xiaomi Philips", // PHILIPS - Xiaomi Philips bulb (ESP8266) 0, 0, 0, 0, 0, 0, // GPIO06 (SD_CLK Flash) // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) @@ -1582,7 +1706,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_PWM1, // GPIO15 light intensity 0, 0 }, - { "Neo Coolcam", // Neo Coolcam (ESP8266) + { "Neo Coolcam", // NEO_COOLCAM - Neo Coolcam (ESP8266) // https://www.banggood.com/NEO-COOLCAM-WiFi-Mini-Smart-Plug-APP-Remote-Control-Timing-Smart-Socket-EU-Plug-p-1288562.html?cur_warehouse=CN 0, 0, 0, 0, GPIO_LED1_INV, // GPIO04 Red Led (0 = On, 1 = Off) - Link and Power status @@ -1597,7 +1721,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_KEY1, // GPIO13 Button 0, 0, 0, 0 }, - { "ESP Switch", // Michael Haustein 4 channel wall switch (ESP07 = ESP8266) + { "ESP Switch", // ESP_SWITCH - Michael Haustein 4 channel wall switch (ESP07 = ESP8266) // Use rules for further actions like - rule on power1#state do publish cmnd/other_device/power %value% endon GPIO_KEY2, // GPIO00 Button 2 GPIO_USER, // GPIO01 Serial RXD and Optional sensor @@ -1618,7 +1742,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_REL1_INV, // GPIO16 Green Led 1 (0 = On, 1 = Off) 0 }, - { "OBI Socket", // OBI socket (ESP8266) - https://www.obi.de/hausfunksteuerung/wifi-stecker-schuko/p/2291706 + { "OBI Socket", // OBI - OBI socket (ESP8266) - https://www.obi.de/hausfunksteuerung/wifi-stecker-schuko/p/2291706 GPIO_USER, // GPIO00 GPIO_USER, // GPIO01 Serial RXD 0, @@ -1638,7 +1762,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, // GPIO16 ADC0_USER // ADC0 A0 Analog input }, - { "Teckin", // https://www.amazon.de/gp/product/B07D5V139R + { "Teckin", // TECKIN - https://www.amazon.de/gp/product/B07D5V139R 0, GPIO_KEY1, // GPIO01 Serial TXD and Button 0, @@ -1656,7 +1780,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_REL1, // GPIO14 Relay (0 = Off, 1 = On) 0, 0, 0 }, - { "AplicWDP303075", // Aplic WDP 303075 (ESP8285 - HLW8012 Energy Monitoring) + { "AplicWDP303075", // APLIC_WDP303075 - Aplic WDP 303075 (ESP8285 - HLW8012 Energy Monitoring) // https://www.amazon.de/dp/B07CNWVNJ2 0, 0, 0, GPIO_KEY1, // GPIO03 Button @@ -1673,7 +1797,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_REL1, // GPIO14 Relay SRU 5VDC SDA (0 = Off, 1 = On ) 0, 0, 0 }, - { "Tuya Dimmer", // Tuya Dimmer (ESP8266 w/ separate MCU dimmer) + { "Tuya MCU", // TUYA_DIMMER - Tuya MCU device (ESP8266 w/ separate MCU) // https://www.amazon.com/gp/product/B07CTNSZZ8/ref=oh_aui_detailpage_o00_s00?ie=UTF8&psc=1 GPIO_USER, // Virtual Button (controlled by MCU) GPIO_USER, // GPIO01 MCU serial control @@ -1694,7 +1818,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, 0 }, - { "Gosund SP1 v23", // https://www.amazon.de/gp/product/B0777BWS1P + { "Gosund SP1 v23", // GOSUND - https://www.amazon.de/gp/product/B0777BWS1P 0, GPIO_LEDLNK_INV, // GPIO01 Serial RXD and LED1 (blue) inv - Link status 0, @@ -1712,7 +1836,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_REL1, // GPIO14 Relay (0 = Off, 1 = On) 0, 0, 0 }, - { "ARMTR Dimmer", // ARMTRONIX Dimmer, one or two channel (ESP8266 w/ separate MCU dimmer) + { "ARMTR Dimmer", // ARMTRONIX_DIMMERS - ARMTRONIX Dimmer, one or two channel (ESP8266 w/ separate MCU dimmer) // https://www.tindie.com/products/Armtronix/wifi-ac-dimmer-two-triac-board/ // https://www.tindie.com/products/Armtronix/wifi-ac-dimmer-esp8266-one-triac-board-alexaecho/ GPIO_USER, @@ -1734,7 +1858,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, 0 }, - { "SK03 Outdoor", // Outdoor smart plug with power monitoring HLW8012 chip - https://www.amazon.com/gp/product/B07CG7MBPV + { "SK03 Outdoor", // SK03_TUYA - Outdoor smart plug with power monitoring HLW8012 chip - https://www.amazon.com/gp/product/B07CG7MBPV GPIO_KEY1, // GPIO00 Button 0, 0, 0, GPIO_HLW_CF, // GPIO04 HLW8012 CF power @@ -1751,7 +1875,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_REL1, // GPIO15 Relay (0 = Off, 1 = On) 0, 0 }, - { "PS-16-DZ", // PS-16-DZ Dimmer (ESP8266 w/ separate Nuvoton MCU dimmer) + { "PS-16-DZ", // PS_16_DZ - PS-16-DZ Dimmer (ESP8266 w/ separate Nuvoton MCU dimmer) // https://www.aliexpress.com/item/SM-Smart-WIFI-Wall-Dimmer-Light-Switch-US-Ewelink-APP-Remote-Control-Wi-Fi-Wirele-Work/32871151902.html GPIO_USER, GPIO_TXD, // GPIO01 MCU serial control @@ -1772,7 +1896,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, 0 }, - { "Teckin US", // Teckin SP20 US with Energy Monitoring + { "Teckin US", // TECKIN_US - Teckin SP20 US with Energy Monitoring // https://www.amazon.com/Outlet-Compatible-Monitoring-Function-Required/dp/B079Q5W22B // https://www.amazon.com/Outlet-ZOOZEE-Monitoring-Function-Compatible/dp/B07J2LR5KN GPIO_LED1_INV, // GPIO00 Red Led (1 = On, 0 = Off) - Power status @@ -1792,7 +1916,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_NRG_CF1, // GPIO14 BL0937 or HJL-01 CF1 current / voltage 0, 0, 0 }, - { "Manzoku strip", // "MANZOKU" labeled power strip, EU version + { "Manzoku strip", // MANZOKU_EU_4 - "MANZOKU" labeled power strip, EU version // https://www.amazon.de/Steckdosenleiste-AOFO-Mehrfachsteckdose-Überspannungsschutz-Sprachsteuerung/dp/B07GBSD11P/ // https://www.amazon.de/Steckdosenleiste-Geekbes-USB-Anschluss-Kompatibel-gesteuert/dp/B078W23BW9/ 0, // GPIO00 @@ -1814,7 +1938,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, // GPIO16 0 }, - { "OBI Socket 2", // OBI socket (ESP8266) - https://www.obi.de/hausfunksteuerung/wifi-stecker-schuko-2-stueck-weiss/p/4077673 + { "OBI Socket 2", // OBI2 - OBI socket (ESP8266) - https://www.obi.de/hausfunksteuerung/wifi-stecker-schuko-2-stueck-weiss/p/4077673 0, // GPIO00 0, // GPIO01 Serial RXD 0, @@ -1831,7 +1955,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_LED1, // GPIO13 Red LED - Power status 0, 0, 0, 0 }, - { "YTF IR Bridge", // https://www.aliexpress.com/item/Tuya-universal-Smart-IR-Hub-remote-control-Voice-Control-AC-TV-Work-With-Alexa-Google-Home/32951202513.html + { "YTF IR Bridge", // YTF_IR_BRIDGE - https://www.aliexpress.com/item/Tuya-universal-Smart-IR-Hub-remote-control-Voice-Control-AC-TV-Work-With-Alexa-Google-Home/32951202513.html GPIO_USER, // GPIO00 GPIO_USER, // GPIO01 Serial RXD GPIO_USER, // GPIO02 @@ -1849,7 +1973,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_IRSEND, // GPIO14 IR Transmitter 0, 0, 0 }, - { "Digoo DG-SP202", // Digoo DG-SP202 + { "Digoo DG-SP202", // DIGOO - Digoo DG-SP202 // https://www.banggood.com/DIGOO-DG-SP202-Dual-EU-Plug-Smart-WIFI-Socket-Individual-Controllable-Energy-Monitor-Remote-Control-Timing-Smart-Home-Outlet-let-p-1375323.html GPIO_KEY1, // GPIO00 Button1 0, // GPIO01 Serial RXD @@ -1870,7 +1994,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_KEY2_NP, // GPIO16 Button2, externally pulled up 0 }, - { "KA10", // SMANERGY KA10 (ESP8285 - BL0937 Energy Monitoring) - https://www.amazon.es/dp/B07MBTCH2Y + { "KA10", // KA10 - SMANERGY KA10 (ESP8285 - BL0937 Energy Monitoring) - https://www.amazon.es/dp/B07MBTCH2Y 0, // GPIO00 GPIO_LEDLNK_INV, // GPIO01 Blue LED - Link status 0, // GPIO02 @@ -1888,7 +2012,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_REL1, // GPIO14 Relay 1 0, 0, 0 }, - { "Luminea ZX2820", + { "Luminea ZX2820", // ZX2820 GPIO_KEY1, // GPIO00 Button 0, 0, 0, GPIO_HLW_CF, // GPIO04 HLW8012 CF power @@ -1904,7 +2028,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_REL1, // GPIO14 Relay 0, 0, 0 }, - { "Mi Desk Lamp", // Mi LED Desk Lamp - https://www.mi.com/global/smartlamp/ + { "Mi Desk Lamp", // MI_DESK_LAMP - Mi LED Desk Lamp - https://www.mi.com/global/smartlamp/ 0, 0, GPIO_KEY1, // GPIO02 Button 0, @@ -1920,7 +2044,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_ROT1B, // GPIO13 Rotary switch B pin 0, 0, 0, 0 }, - { "SP10", // Tuya SP10 (BL0937 Energy Monitoring) + { "SP10", // SP10 - Tuya SP10 (BL0937 Energy Monitoring) // https://www.aliexpress.com/item/Smart-Mini-WiFi-Plug-Outlet-Switch-Work-With-ForEcho-Alexa-Google-Home-Remote-EU-Smart-Socket/32963670423.html 0, // GPIO00 GPIO_PWM1, // GPIO01 Nightlight @@ -1939,7 +2063,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_REL1, // GPIO14 Relay and red LED 0, 0, 0 }, - { "WAGA CHCZ02MB", // WAGA life CHCZ02MB (HJL-01 Energy Monitoring) + { "WAGA CHCZ02MB", // WAGA - WAGA life CHCZ02MB (HJL-01 Energy Monitoring) // https://www.ebay.com/itm/332595697006 GPIO_LED1_INV, // GPIO00 Red LED 0, // GPIO01 Serial RXD @@ -1959,7 +2083,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_LEDLNK_INV, // GPIO15 Blue LED - Link status 0, 0 }, - { "SYF05", // Sunyesmart SYF05 (a.k.a. Fcmila) = TYWE3S + SM16726 + { "SYF05", // SYF05 - Sunyesmart SYF05 (a.k.a. Fcmila) = TYWE3S + SM16726 // Also works with Merkury 904 RGBW Bulbs with 13 set to GPIO_SM16716_SEL // https://www.flipkart.com/fc-mila-bxav-xs-ad-smart-bulb/p/itmf85zgs45fzr7n // https://docs.tuya.com/en/hardware/WiFi-module/wifi-e3s-module.html @@ -1983,257 +2107,62 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, // GPIO16 N.C. ADC0_USER // ADC0 A0 Analog input }, - { "Sonoff L1", // Sonoff L1 RGB LED controller (ESP8266 w/ separate Nuvoton MCU) - GPIO_USER, + { "Sonoff L1", // SONOFF_L1 - Sonoff L1 RGB LED controller (ESP8266 w/ separate Nuvoton MCU) + 0, GPIO_TXD, // GPIO01 MCU serial control - GPIO_USER, + 0, GPIO_RXD, // GPIO03 MCU serial control - GPIO_USER, - GPIO_USER, + 0, 0, // GPIO06 (SD_CLK Flash) // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) // GPIO11 (SD_CMD Flash) - GPIO_USER, - GPIO_LED1, // GPIO13 WiFi LED - Link and Power status - GPIO_USER, - GPIO_USER, - GPIO_USER, - 0 + 0, + GPIO_LED1_INV, // GPIO13 WiFi Blue Led - Link and Power status + 0, 0, 0, 0 + }, + { "Sonoff iFan03", // SONOFF_IFAN03 - Sonoff iFan03 (ESP8285) + GPIO_KEY1, // GPIO00 WIFI_KEY0 Button 1 + GPIO_TXD, // GPIO01 ESP_TXD Serial RXD connection to P0.5 of RF microcontroller + 0, // GPIO02 ESP_LOG + GPIO_RXD, // GPIO03 ESP_RXD Serial TXD connection to P0.4 of RF microcontroller + 0, // GPIO04 DEBUG_RX + 0, // GPIO05 DEBUG_TX + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + GPIO_REL1_INV, // GPIO09 WIFI_O0 Relay 1 (0 = Off, 1 = On) controlling the light + GPIO_BUZZER_INV, // GPIO10 WIFI_O4 Buzzer (0 = Off, 1 = On) + // GPIO11 (SD_CMD Flash) + GPIO_REL3, // GPIO12 WIFI_O2 Relay 3 (0 = Off, 1 = On) controlling the fan + GPIO_LED1_INV, // GPIO13 WIFI_CHK Blue Led on PCA (0 = On, 1 = Off) - Link and Power status + GPIO_REL2, // GPIO14 WIFI_O1 Relay 2 (0 = Off, 1 = On) controlling the fan + GPIO_REL4, // GPIO15 WIFI_O3 Relay 4 (0 = Off, 1 = On) controlling the fan + 0, 0 + }, + { "EXS Dimmer", // EXS_DIMMER - EX-Store WiFi Dimmer v4, two channel (ESP8266 w/ separate MCU dimmer) + // https://ex-store.de/2-Kanal-RS232-WiFi-WLan-Dimmer-Modul-V4-fuer-Unterputzmontage-230V-3A + // https://ex-store.de/2-Kanal-RS232-WiFi-WLan-Dimmer-Modul-V4-fuer-Unterputzmontage-230V-3A-ESP8266-V12-Stift-und-Buchsenleisten + 0, + GPIO_TXD, // GPIO01 MCU serial control + GPIO_LEDLNK, // GPIO02 LED Link + GPIO_RXD, // GPIO03 MCU serial control + GPIO_USER, // GPIO04 + GPIO_USER, // GPIO05 + // GPIO06 (SD_CLK Flash) + // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) + // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) + 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) + 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) + // GPIO11 (SD_CMD Flash) + GPIO_USER, // GPIO12 + GPIO_EXS_ENABLE, // GPIO13 EXS MCU Enable + GPIO_USER, // GPIO14 + 0, // GPIO15 + 0, 0 } }; -/* - Optionals - - { "RGB Smart Plug", // Tuya based smart plug with power monitoring and RGB light - // https://www.aliexpress.com/item/ET-Smart-Plug-Wifi-Socket-With-Switch-Phone-APP-Voice-Remote-Control-Monitor-Smart-Timing-Switch/32964036349.html?spm=a2g0s.9042311.0.0.439c4c4d4N8N2Q - // https://xiangshangcn.en.alibaba.com/product/60844251239-807590977/RGB_wifi_smart_plug_smart_socket_smart_outlet_EU_works_with_Amazon_alexa_google_home_mobile_app_tuya_solution_smart_life.html?spm=a2700.icbuShop.41413.24.4e996767oFAAmO - GPIO_PWM1, // GPIO00 Red - GPIO_USER, // GPIO01 Serial RXD and Optional sensor - GPIO_PWM3, // GPIO02 Blue - GPIO_USER, // GPIO03 Serial TXD and Optional sensor - GPIO_PWM2, // GPIO04 Green - GPIO_HJL_CF, // GPIO05 BL0937 or HJL-01 CF power - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_NRG_SEL_INV, // GPIO12 BL0937 or HJL-01 Sel output (0 = Voltage) - GPIO_KEY1, // GPIO13 Button - GPIO_NRG_CF1, // GPIO14 BL0937 or HJL-01 CF1 current / voltage - GPIO_REL1, // GPIO15 Relay (0 = Off, 1 = On) - 0, 0 - } - - { "ESP RGBWWC", // esp rgbww controller https://github.com/pljakobs/esp_rgbww_controller/tree/v2.3 - GPIO_KEY1, // GPIO00 Button - GPIO_USER, // GPIO01 Serial RXD and Optional sensor - 0, // GPIO02 - GPIO_USER, // GPIO03 Serial TXD and Optional sensor - GPIO_PWM5, // GPIO04 LED Warm White - GPIO_PWM4, // GPIO05 LED Cold White - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_PWM2, // GPIO12 LED Green - GPIO_PWM1, // GPIO13 LED Red - GPIO_PWM3, // GPIO14 LED Blue - 0, // GPIO15 - GPIO_KEY2, // GPIO16 Button - 0 - } - - { "N0DY Relay", // N0DY Wifi Dual Relay (ESP-07) - // https://www.n0dy.com/product/web-controlled-dual-relay/ - // https://www.amazon.com/dp/B072MKV8ZM - GPIO_KEY1, // GPIO00 PROG Button - GPIO_USER, // GPIO01 Serial RXD or Optional sensor on J2 RXD (if not using serial io) - 0, // GPIO02 - GPIO_USER, // GPIO03 Serial TXD or Optional sensor on J2 TXD (if not using serial io) - GPIO_REL2_INV, // GPIO04 Relay 2 (active low) - GPIO_REL1_INV, // GPIO05 Relay 1 (active low) - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - 0, 0, 0, 0, 0, 0 - } - - { "MagicHome", // Magic Home (aka Flux-light) (ESP8266) - // https://www.aliexpress.com/item/Magic-Home-Mini-RGB-RGBW-Wifi-Controller-For-Led-Strip-Panel-light-Timing-Function-16million-colors/32686853650.html - 0, - GPIO_USER, // GPIO01 Serial RXD and Optional sensor - GPIO_LED1_INV, // GPIO02 Blue onboard LED - Link and Power status - GPIO_USER, // GPIO03 Serial TXD and Optional sensor - GPIO_USER, // GPIO04 IR receiver (optional) - GPIO_PWM2, // GPIO05 RGB LED Green - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_PWM3, // GPIO12 RGB LED Blue - GPIO_USER, // GPIO13 RGBW LED White (optional - set to PWM4 for Cold White or Warm White) - GPIO_PWM1, // GPIO14 RGB LED Red - 0, 0, 0 - } - - { "Arilux LC10", // Arilux LC10 (ESP8285), RGBW + RF - // https://github.com/arendst/Sonoff-Tasmota/wiki/MagicHome-with-ESP8285 - // https://www.aliexpress.com/item/DC5-24V-Wireless-WIFI-LED-RGB-Controller-RGBW-Controller-IR-RF-Remote-Control-IOS-Android-for/32827253255.html - // https://www.aliexpress.com/item/Wifi-LED-RGB-Controler-DC12V-MIni-Wifi-RGB-RGBW-LED-Controller-for-RGB-RGBW-LED-Strip/32673444047.html - GPIO_USER, // GPIO00 Optional Button - GPIO_USER, // GPIO01 Serial RXD and Optional sensor - 0, - GPIO_USER, // GPIO03 Serial TXD and Optional sensor0 - GPIO_ARIRFRCV, // GPIO04 RF receiver input - GPIO_PWM2, // GPIO05 RGB LED Green - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_PWM3, // GPIO12 RGB LED Blue - GPIO_PWM4, // GPIO13 RGBW LED White - GPIO_PWM1, // GPIO14 RGB LED Red - GPIO_ARIRFSEL, // GPIO15 RF receiver control - 0, 0 - } - - { "Xenon 3CH", // Xenon 3CH (ESP8266) - (#1128) - 0, 0, 0, - GPIO_KEY2, // GPIO03 Serial TXD and Optional sensor - GPIO_REL2, // GPIO04 Relay 2 - GPIO_KEY3, // GPIO05 Input 2 - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_KEY1, // GPIO12 Key input - GPIO_REL1, // GPIO13 Relay 1 - 0, - GPIO_REL3, // GPIO15 Relay 3 - 0, 0 - } - - { "PowStro Basic", // PowStro (ESP8266) - (#1419) - 0, 0, 0, 0, - GPIO_REL1, // GPIO04 Relay (0 = Off, 1 = On) - 0, - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_KEY1, // GPIO12 Button - 0, 0, - GPIO_LED1, // GPIO15 Led (1 = On, 0 = Off) - Link and Power status - 0, 0 - } - - { "SMPW701E", // SM-PW701E WLAN Socket (#1190) - 0, 0, 0, 0, - GPIO_LED1_INV, // GPIO04 Blue Led (0 = On, 1 = Off) - Link and Power status - 0, // GPIO05 IR or RF receiver (optional) - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_REL1, // GPIO12 Relay and Red Led (0 = Off, 1 = On) - GPIO_KEY1, // GPIO13 Button - 0, 0, 0, 0 - } - - { "SWA1", // Smart Plugs (ESP8266) - 0, - GPIO_USER, // GPIO01 - 0, - GPIO_USER, // GPIO03 - GPIO_LED1_INV, // GPIO04 Blue LED - Link and Power status - GPIO_REL1, // GPIO05 Red LED and relay - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - 0, - GPIO_KEY1, // GPIO13 Button (normally GPIO00) - GPIO_USER, // GPIO14 - 0, 0, 0 - } - - { "MagicHome v2.3", // Magic Home (aka Flux-light) (ESP8266) (#1353) - 0, 0, - GPIO_LED1_INV, // GPIO02 Blue onboard LED - Link and Power status - 0, - GPIO_USER, // GPIO04 IR receiver (optional) - GPIO_PWM2, // GPIO05 RGB LED Green - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_PWM1, // GPIO12 RGB LED Red - GPIO_PWM3, // GPIO13 RGB LED Blue - 0, - GPIO_PWM4, // GPIO15 RGBW LED White - 0, 0 - } - - { "Ledunia", // Ledunia (ESP8266 - 32MB) - http://ledunia.de/ - GPIO_USER, // GPIO00 (D0) - GPIO_USER, // GPIO01 (D7) Serial RXD - GPIO_USER, // GPIO02 (D2) - GPIO_USER, // GPIO03 (D8) Serial TXD - GPIO_USER, // GPIO04 (D4) 4 x WS2812 Leds, (DOUT) Extents WS2812 string - GPIO_USER, // GPIO05 (D5) Blue Led - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_USER, // GPIO12 (D12) - GPIO_USER, // GPIO13 (D13) - GPIO_USER, // GPIO14 (D14) - GPIO_USER, // GPIO15 (D15) - GPIO_USER, // GPIO16 (D16) - 0 // ADC0 Analog input (A0) - } - - { "Delock 11826", // Delock 11826 (ESP8285) = Sonoff Basic - GPIO_KEY1, // GPIO00 Button - 0, 0, 0, 0, 0, - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) - GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) - Link and Power status - 0, 0, 0, 0 - } - -*/ - #endif // _SONOFF_TEMPLATE_H_ diff --git a/sonoff/sonoff_version.h b/sonoff/sonoff_version.h index 32c1e92e6..22e12ed7b 100644 --- a/sonoff/sonoff_version.h +++ b/sonoff/sonoff_version.h @@ -20,6 +20,6 @@ #ifndef _SONOFF_VERSION_H_ #define _SONOFF_VERSION_H_ -const uint32_t VERSION = 0x06060000; +const uint32_t VERSION = 0x06060015; #endif // _SONOFF_VERSION_H_ diff --git a/sonoff/support.ino b/sonoff/support.ino index 41e5ffd3b..bb6a6aa72 100644 --- a/sonoff/support.ino +++ b/sonoff/support.ino @@ -43,8 +43,8 @@ bool knx_started = false; void OsWatchTicker(void) { - unsigned long t = millis(); - unsigned long last_run = abs(t - oswatch_last_loop_time); + uint32_t t = millis(); + uint32_t last_run = abs(t - oswatch_last_loop_time); #ifdef DEBUG_THEO AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_OSWATCH " FreeRam %d, rssi %d, last_run %d"), ESP.getFreeHeap(), WifiGetRssiAsQuality(WiFi.RSSI()), last_run); @@ -124,6 +124,18 @@ size_t strcspn(const char *str1, const char *str2) return ret; } +// https://clc-wiki.net/wiki/C_standard_library:string.h:strpbrk +// Locate the first occurrence in the string pointed to by s1 of any character from the string pointed to by s2 +char* strpbrk(const char *s1, const char *s2) +{ + while(*s1) { + if (strchr(s2, *s1++)) { + return (char*)--s1; + } + } + return 0; +} + // https://opensource.apple.com/source/Libc/Libc-583/stdlib/FreeBSD/strtoull.c // Convert a string to an unsigned long long integer #ifndef __LONG_LONG_MAX__ @@ -292,6 +304,45 @@ char* ulltoa(unsigned long long value, char *str, int radix) return str; } +// see https://stackoverflow.com/questions/6357031/how-do-you-convert-a-byte-array-to-a-hexadecimal-string-in-c +// char* ToHex_P(unsigned char * in, size_t insz, char * out, size_t outsz, char inbetween = '\0'); in sonoff_post.h +char* ToHex_P(const unsigned char * in, size_t insz, char * out, size_t outsz, char inbetween) +{ + // ToHex_P(in, insz, out, outz) -> "12345667" + // ToHex_P(in, insz, out, outz, ' ') -> "12 34 56 67" + // ToHex_P(in, insz, out, outz, ':') -> "12:34:56:67" + static const char * hex = "0123456789ABCDEF"; + int between = (inbetween) ? 3 : 2; + const unsigned char * pin = in; + char * pout = out; + for (; pin < in+insz; pout += between, pin++) { + pout[0] = hex[(pgm_read_byte(pin)>>4) & 0xF]; + pout[1] = hex[ pgm_read_byte(pin) & 0xF]; + if (inbetween) { pout[2] = inbetween; } + if (pout + 3 - out > outsz) { break; } // Better to truncate output string than overflow buffer + } + pout[(inbetween && insz) ? -1 : 0] = 0; // Discard last inbetween if any input + return out; +} + +char* Uint64toHex(uint64_t value, char *str, uint16_t bits) +{ + ulltoa(value, str, 16); // Get 64bit value + + int fill = 8; + if ((bits > 3) && (bits < 65)) { + fill = bits / 4; // Max 16 + if (bits % 4) { fill++; } + } + int len = strlen(str); + fill -= len; + if (fill > 0) { + memmove(str + fill, str, len +1); + memset(str, '0', fill); + } + return str; +} + char* dtostrfd(double number, unsigned char prec, char *s) { if ((isnan(number)) || (isinf(number))) { // Fix for JSON output (https://stackoverflow.com/questions/1423081/json-left-out-infinity-and-nan-json-status-in-ecmascript) @@ -302,12 +353,12 @@ char* dtostrfd(double number, unsigned char prec, char *s) } } -char* Unescape(char* buffer, uint16_t* size) +char* Unescape(char* buffer, uint32_t* size) { uint8_t* read = (uint8_t*)buffer; uint8_t* write = (uint8_t*)buffer; - int16_t start_size = *size; - int16_t end_size = *size; + int32_t start_size = *size; + int32_t end_size = *size; uint8_t che = 0; // AddLogBuffer(LOG_LEVEL_DEBUG, (uint8_t*)buffer, *size); @@ -452,23 +503,23 @@ char IndexSeparator() } } -void SetShortcut(char* str, uint8_t action) +void SetShortcutDefault(void) { - if ('\0' != str[0]) { // There must be at least one character in the buffer - str[0] = '0' + action; // SC_CLEAR, SC_DEFAULT, SC_USER - str[1] = '\0'; + if ('\0' != XdrvMailbox.data[0]) { // There must be at least one character in the buffer + XdrvMailbox.data[0] = '0' + SC_DEFAULT; // SC_CLEAR, SC_DEFAULT, SC_USER + XdrvMailbox.data[1] = '\0'; } } -uint8_t Shortcut(const char* str) +uint8_t Shortcut() { uint8_t result = 10; - if ('\0' == str[1]) { // Only allow single character input for shortcut - if (('"' == str[0]) || ('0' == str[0])) { + if ('\0' == XdrvMailbox.data[1]) { // Only allow single character input for shortcut + if (('"' == XdrvMailbox.data[0]) || ('0' == XdrvMailbox.data[0])) { result = SC_CLEAR; } else { - result = atoi(str); // 1 = SC_DEFAULT, 2 = SC_USER + result = atoi(XdrvMailbox.data); // 1 = SC_DEFAULT, 2 = SC_USER if (0 == result) { result = 10; } @@ -502,34 +553,11 @@ bool ParseIp(uint32_t* addr, const char* str) return (3 == i); } -void MakeValidMqtt(uint8_t option, char* str) -{ -// option 0 = replace by underscore -// option 1 = delete character - uint16_t i = 0; - while (str[i] > 0) { -// if ((str[i] == '/') || (str[i] == '+') || (str[i] == '#') || (str[i] == ' ')) { - if ((str[i] == '+') || (str[i] == '#') || (str[i] == ' ')) { - if (option) { - uint16_t j = i; - while (str[j] > 0) { - str[j] = str[j +1]; - j++; - } - i--; - } else { - str[i] = '_'; - } - } - i++; - } -} - // Function to parse & check if version_str is newer than our currently installed version. bool NewerVersion(char* version_str) { uint32_t version = 0; - uint8_t i = 0; + uint32_t i = 0; char *str_ptr; char* version_dup = strdup(version_str); // Duplicate the version_str as strtok_r will modify it. @@ -569,7 +597,7 @@ bool NewerVersion(char* version_str) return (version > VERSION); } -char* GetPowerDevice(char* dest, uint8_t idx, size_t size, uint8_t option) +char* GetPowerDevice(char* dest, uint32_t idx, size_t size, uint32_t option) { char sidx[8]; @@ -581,7 +609,7 @@ char* GetPowerDevice(char* dest, uint8_t idx, size_t size, uint8_t option) return dest; } -char* GetPowerDevice(char* dest, uint8_t idx, size_t size) +char* GetPowerDevice(char* dest, uint32_t idx, size_t size) { return GetPowerDevice(dest, idx, size, 0); } @@ -676,7 +704,7 @@ uint32_t RoundSqrtInt(uint32_t num) return s / 2; } -char* GetTextIndexed(char* destination, size_t destination_size, uint16_t index, const char* haystack) +char* GetTextIndexed(char* destination, size_t destination_size, uint32_t index, const char* haystack) { // Returns empty string if not found // Returns text of found @@ -738,32 +766,46 @@ int GetCommandCode(char* destination, size_t destination_size, const char* needl return result; } +bool DecodeCommand(const char* haystack, void (* const MyCommand[])(void)) +{ + GetTextIndexed(XdrvMailbox.command, CMDSZ, 0, haystack); // Get prefix if available + int prefix_length = strlen(XdrvMailbox.command); + int command_code = GetCommandCode(XdrvMailbox.command + prefix_length, CMDSZ, XdrvMailbox.topic + prefix_length, haystack); + if (command_code > 0) { // Skip prefix + XdrvMailbox.command_code = command_code -1; + MyCommand[XdrvMailbox.command_code](); + return true; + } + return false; +} + +const char kOptions[] PROGMEM = "OFF|" D_OFF "|" D_FALSE "|" D_STOP "|" D_CELSIUS "|" // 0 + "ON|" D_ON "|" D_TRUE "|" D_START "|" D_FAHRENHEIT "|" D_USER "|" // 1 + "TOGGLE|" D_TOGGLE "|" D_ADMIN "|" // 2 + "BLINK|" D_BLINK "|" // 3 + "BLINKOFF|" D_BLINKOFF "|" // 4 + "ALL" ; // 255 + +const uint8_t sNumbers[] PROGMEM = { 0,0,0,0,0, + 1,1,1,1,1,1, + 2,2,2, + 3,3, + 4,4, + 255 }; + int GetStateNumber(char *state_text) { char command[CMDSZ]; - int state_number = -1; - - if (GetCommandCode(command, sizeof(command), state_text, kOptionOff) >= 0) { - state_number = 0; - } - else if (GetCommandCode(command, sizeof(command), state_text, kOptionOn) >= 0) { - state_number = 1; - } - else if (GetCommandCode(command, sizeof(command), state_text, kOptionToggle) >= 0) { - state_number = 2; - } - else if (GetCommandCode(command, sizeof(command), state_text, kOptionBlink) >= 0) { - state_number = 3; - } - else if (GetCommandCode(command, sizeof(command), state_text, kOptionBlinkOff) >= 0) { - state_number = 4; + int state_number = GetCommandCode(command, sizeof(command), state_text, kOptions); + if (state_number >= 0) { + state_number = pgm_read_byte(sNumbers + state_number); } return state_number; } void SetSerialBaudrate(int baudrate) { - Settings.baudrate = baudrate / 1200; + Settings.baudrate = baudrate / 300; if (Serial.baudRate() != baudrate) { if (seriallog_level) { AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_SET_BAUDRATE_TO " %d"), baudrate); @@ -782,7 +824,7 @@ void ClaimSerial(void) AddLog_P(LOG_LEVEL_INFO, PSTR("SNS: Hardware Serial")); SetSeriallog(LOG_LEVEL_NONE); baudrate = Serial.baudRate(); - Settings.baudrate = baudrate / 1200; + Settings.baudrate = baudrate / 300; } void SerialSendRaw(char *codes) @@ -793,7 +835,7 @@ void SerialSendRaw(char *codes) int size = strlen(codes); - while (size > 0) { + while (size > 1) { strlcpy(stemp, codes, sizeof(stemp)); code = strtol(stemp, &p, 16); Serial.write(code); @@ -811,7 +853,7 @@ uint32_t GetHash(const char *buffer, size_t size) return hash; } -void ShowSource(int source) +void ShowSource(uint32_t source) { if ((source > 0) && (source < SRC_MAX)) { char stemp1[20]; @@ -819,7 +861,7 @@ void ShowSource(int source) } } -void WebHexCode(uint8_t i, const char* code) +void WebHexCode(uint32_t i, const char* code) { char scolor[10]; @@ -849,7 +891,7 @@ void WebHexCode(uint8_t i, const char* code) Settings.web_color[i][2] = color & 0xFF; // Blue } -uint32_t WebColor(uint8_t i) +uint32_t WebColor(uint32_t i) { uint32_t tcolor = (Settings.web_color[i][0] << 16) | (Settings.web_color[i][1] << 8) | Settings.web_color[i][2]; return tcolor; @@ -859,7 +901,24 @@ uint32_t WebColor(uint8_t i) * Response data handling \*********************************************************************************************/ -int Response_P(const char* format, ...) // Content send snprintf_P char data +const uint16_t TIMESZ = 100; // Max number of characters in time string + +char* ResponseGetTime(uint32_t format, char* time_str) +{ + switch (format) { + case 1: + snprintf_P(time_str, TIMESZ, PSTR("{\"" D_JSON_TIME "\":\"%s\",\"Epoch\":%u"), GetDateAndTime(DT_LOCAL).c_str(), UtcTime()); + break; + case 2: + snprintf_P(time_str, TIMESZ, PSTR("{\"" D_JSON_TIME "\":%u"), UtcTime()); + break; + default: + snprintf_P(time_str, TIMESZ, PSTR("{\"" D_JSON_TIME "\":\"%s\""), GetDateAndTime(DT_LOCAL).c_str()); + } + return time_str; +} + +int Response_P(const char* format, ...) // Content send snprintf_P char data { // This uses char strings. Be aware of sending %% if % is needed va_list args; @@ -869,6 +928,20 @@ int Response_P(const char* format, ...) // Content send snprintf_P char data return len; } +int ResponseTime_P(const char* format, ...) // Content send snprintf_P char data +{ + // This uses char strings. Be aware of sending %% if % is needed + va_list args; + va_start(args, format); + + ResponseGetTime(Settings.flag2.time_format, mqtt_data); + + int mlen = strlen(mqtt_data); + int len = vsnprintf_P(mqtt_data + mlen, sizeof(mqtt_data) - mlen, format, args); + va_end(args); + return len + mlen; +} + int ResponseAppend_P(const char* format, ...) // Content send snprintf_P char data { // This uses char strings. Be aware of sending %% if % is needed @@ -880,11 +953,27 @@ int ResponseAppend_P(const char* format, ...) // Content send snprintf_P char d return len + mlen; } +int ResponseAppendTimeFormat(uint32_t format) +{ + char time_str[TIMESZ]; + return ResponseAppend_P(ResponseGetTime(format, time_str)); +} + +int ResponseAppendTime(void) +{ + return ResponseAppendTimeFormat(Settings.flag2.time_format); +} + int ResponseJsonEnd(void) { return ResponseAppend_P(PSTR("}")); } +int ResponseJsonEndEnd(void) +{ + return ResponseAppend_P(PSTR("}}")); +} + /*********************************************************************************************\ * GPIO Module and Template management \*********************************************************************************************/ @@ -896,7 +985,7 @@ uint8_t ModuleNr() return (USER_MODULE == Settings.module) ? 0 : Settings.module +1; } -bool ValidTemplateModule(uint8_t index) +bool ValidTemplateModule(uint32_t index) { for (uint32_t i = 0; i < sizeof(kModuleNiceList); i++) { if (index == pgm_read_byte(kModuleNiceList + i)) { @@ -906,13 +995,13 @@ bool ValidTemplateModule(uint8_t index) return false; } -bool ValidModule(uint8_t index) +bool ValidModule(uint32_t index) { if (index == USER_MODULE) { return true; } return ValidTemplateModule(index); } -String AnyModuleName(uint8_t index) +String AnyModuleName(uint32_t index) { if (USER_MODULE == index) { return String(Settings.user_template.name); @@ -941,7 +1030,7 @@ void ModuleGpios(myio *gp) // AddLogBuffer(LOG_LEVEL_DEBUG, (uint8_t *)&src, sizeof(mycfgio)); - uint8_t j = 0; + uint32_t j = 0; for (uint32_t i = 0; i < sizeof(mycfgio); i++) { if (6 == i) { j = 9; } if (8 == i) { j = 12; } @@ -966,7 +1055,7 @@ gpio_flag ModuleFlag() return flag; } -void ModuleDefault(uint8_t module) +void ModuleDefault(uint32_t module) { if (USER_MODULE == module) { module = WEMOS; } // Generic Settings.user_template_base = module; @@ -978,11 +1067,16 @@ void SetModuleType() my_module_type = (USER_MODULE == Settings.module) ? Settings.user_template_base : Settings.module; } -uint8_t ValidPin(uint8_t pin, uint8_t gpio) +bool FlashPin(uint32_t pin) +{ + return (((pin > 5) && (pin < 9)) || (11 == pin)); +} + +uint8_t ValidPin(uint32_t pin, uint32_t gpio) { uint8_t result = gpio; - if (((pin > 5) && (pin < 9)) || (11 == pin)) { + if (FlashPin(pin)) { result = GPIO_NONE; // Disable flash pins GPIO6, GPIO7, GPIO8 and GPIO11 } if ((WEMOS == Settings.module) && (!Settings.flag3.user_esp8285_enable)) { @@ -991,7 +1085,7 @@ uint8_t ValidPin(uint8_t pin, uint8_t gpio) return result; } -bool ValidGPIO(uint8_t pin, uint8_t gpio) +bool ValidGPIO(uint32_t pin, uint32_t gpio) { return (GPIO_USER == ValidPin(pin, gpio)); // Only allow GPIO_USER pins } @@ -999,11 +1093,11 @@ bool ValidGPIO(uint8_t pin, uint8_t gpio) bool ValidAdc() { gpio_flag flag = ModuleFlag(); - uint8_t template_adc0 = flag.data &15; + uint32_t template_adc0 = flag.data &15; return (ADC0_USER == template_adc0); } -bool GetUsedInModule(uint8_t val, uint8_t *arr) +bool GetUsedInModule(uint32_t val, uint8_t *arr) { int offset = 0; @@ -1175,6 +1269,7 @@ void SetNextTimeInterval(unsigned long& timer, const unsigned long step) #ifdef USE_I2C const uint8_t I2C_RETRY_COUNTER = 3; +uint32_t i2c_active[4] = { 0 }; uint32_t i2c_buffer = 0; bool I2cValidRead(uint8_t addr, uint8_t reg, uint8_t size) @@ -1368,16 +1463,35 @@ void I2cScan(char *devs, unsigned int devs_len) } } -bool I2cDevice(uint8_t addr) +void I2cSetActive(uint32_t addr, uint32_t count = 1) { - for (uint8_t address = 1; address <= 127; address++) { - Wire.beginTransmission(address); - if (!Wire.endTransmission() && (address == addr)) { - return true; - } + addr &= 0x7F; // Max I2C address is 127 + count &= 0x7F; // Max 4 x 32 bits available + while (count-- && (addr < 128)) { + i2c_active[addr / 32] |= (1 << (addr % 32)); + addr++; + } +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("I2C: Active %08X,%08X,%08X,%08X"), i2c_active[0], i2c_active[1], i2c_active[2], i2c_active[3]); +} + +bool I2cActive(uint32_t addr) +{ + addr &= 0x7F; // Max I2C address is 127 + if (i2c_active[addr / 32] & (1 << (addr % 32))) { + return true; } return false; } + +bool I2cDevice(uint8_t addr) +{ + addr &= 0x7F; // Max I2C address is 127 + if (I2cActive(addr)) { + return false; // If already active report as not present; + } + Wire.beginTransmission(addr); + return (0 == Wire.endTransmission()); +} #endif // USE_I2C /*********************************************************************************************\ @@ -1388,14 +1502,14 @@ bool I2cDevice(uint8_t addr) * \*********************************************************************************************/ -void SetSeriallog(uint8_t loglevel) +void SetSeriallog(uint32_t loglevel) { Settings.seriallog_level = loglevel; seriallog_level = loglevel; seriallog_timer = 0; } -void SetSyslog(uint8_t loglevel) +void SetSyslog(uint32_t loglevel) { Settings.syslog_level = loglevel; syslog_level = loglevel; @@ -1403,7 +1517,7 @@ void SetSyslog(uint8_t loglevel) } #ifdef USE_WEBSERVER -void GetLog(uint8_t idx, char** entry_pp, size_t* len_p) +void GetLog(uint32_t idx, char** entry_pp, size_t* len_p) { char* entry_p = nullptr; size_t len = 0; @@ -1411,7 +1525,7 @@ void GetLog(uint8_t idx, char** entry_pp, size_t* len_p) if (idx) { char* it = web_log; do { - uint8_t cur_idx = *it; + uint32_t cur_idx = *it; it++; size_t tmp = strchrspn(it, '\1'); tmp++; // Skip terminating '\1' @@ -1453,7 +1567,7 @@ void Syslog(void) } } -void AddLog(uint8_t loglevel) +void AddLog(uint32_t loglevel) { char mxtime[10]; // "13:45:21 " @@ -1466,6 +1580,7 @@ void AddLog(uint8_t loglevel) if (Settings.webserver && (loglevel <= Settings.weblog_level)) { // Delimited, zero-terminated buffer of log lines. // Each entry has this format: [index][log data]['\1'] + web_log_index &= 0xFF; if (!web_log_index) web_log_index++; // Index 0 is not allowed as it is the end of char string while (web_log_index == web_log[0] || // If log already holds the next index, remove it strlen(web_log) + strlen(log_data) + 13 > WEB_LOG_SIZE) // 13 = web_log_index + mxtime + '\1' + '\0' @@ -1477,21 +1592,23 @@ void AddLog(uint8_t loglevel) memmove(web_log, it, WEB_LOG_SIZE -(it-web_log)); // Move buffer forward to remove oldest log line } snprintf_P(web_log, sizeof(web_log), PSTR("%s%c%s%s\1"), web_log, web_log_index++, mxtime, log_data); + web_log_index &= 0xFF; if (!web_log_index) web_log_index++; // Index 0 is not allowed as it is the end of char string } #endif // USE_WEBSERVER + if (!global_state.mqtt_down && (loglevel <= Settings.mqttlog_level)) { MqttPublishLogging(mxtime); } if (!global_state.wifi_down && (loglevel <= syslog_level)) { Syslog(); } } -void AddLog_P(uint8_t loglevel, const char *formatP) +void AddLog_P(uint32_t loglevel, const char *formatP) { snprintf_P(log_data, sizeof(log_data), formatP); AddLog(loglevel); } -void AddLog_P(uint8_t loglevel, const char *formatP, const char *formatP2) +void AddLog_P(uint32_t loglevel, const char *formatP, const char *formatP2) { - char message[100]; + char message[sizeof(log_data)]; snprintf_P(log_data, sizeof(log_data), formatP); snprintf_P(message, sizeof(message), formatP2); @@ -1499,7 +1616,7 @@ void AddLog_P(uint8_t loglevel, const char *formatP, const char *formatP2) AddLog(loglevel); } -void AddLog_P2(uint8_t loglevel, PGM_P formatP, ...) +void AddLog_P2(uint32_t loglevel, PGM_P formatP, ...) { va_list arg; va_start(arg, formatP); @@ -1509,21 +1626,40 @@ void AddLog_P2(uint8_t loglevel, PGM_P formatP, ...) AddLog(loglevel); } -void AddLogBuffer(uint8_t loglevel, uint8_t *buffer, int count) +void AddLog_Debug(PGM_P formatP, ...) { + va_list arg; + va_start(arg, formatP); + vsnprintf_P(log_data, sizeof(log_data), formatP, arg); + va_end(arg); + + AddLog(LOG_LEVEL_DEBUG); +} + +void AddLogBuffer(uint32_t loglevel, uint8_t *buffer, uint32_t count) +{ +/* snprintf_P(log_data, sizeof(log_data), PSTR("DMP:")); for (uint32_t i = 0; i < count; i++) { snprintf_P(log_data, sizeof(log_data), PSTR("%s %02X"), log_data, *(buffer++)); } AddLog(loglevel); +*/ +/* + strcpy_P(log_data, PSTR("DMP: ")); + ToHex_P(buffer, count, log_data + strlen(log_data), sizeof(log_data) - strlen(log_data), ' '); + AddLog(loglevel); +*/ + char hex_char[(count * 3) + 2]; + AddLog_P2(loglevel, PSTR("DMP: %s"), ToHex_P(buffer, count, hex_char, sizeof(hex_char), ' ')); } -void AddLogSerial(uint8_t loglevel) +void AddLogSerial(uint32_t loglevel) { AddLogBuffer(loglevel, (uint8_t*)serial_in_buffer, serial_in_byte_counter); } -void AddLogMissed(char *sensor, uint8_t misses) +void AddLogMissed(char *sensor, uint32_t misses) { AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SNS: %s missed %d"), sensor, SENSOR_MAX_MISS - misses); } diff --git a/sonoff/support_button.ino b/sonoff/support_button.ino index 9311cd838..f449eb180 100644 --- a/sonoff/support_button.ino +++ b/sonoff/support_button.ino @@ -23,44 +23,50 @@ * Button support \*********************************************************************************************/ -unsigned long button_debounce = 0; // Button debounce timer -uint16_t holdbutton[MAX_KEYS] = { 0 }; // Timer for button hold -uint16_t dual_button_code = 0; // Sonoff dual received code +#define MAX_BUTTON_COMMANDS 5 // Max number of button commands supported +const char kCommands[] PROGMEM = + D_CMND_WIFICONFIG " 2|" D_CMND_WIFICONFIG " 2|" D_CMND_WIFICONFIG " 2|" D_CMND_RESTART " 1|" D_CMND_UPGRADE " 1"; -uint8_t lastbutton[MAX_KEYS] = { NOT_PRESSED, NOT_PRESSED, NOT_PRESSED, NOT_PRESSED }; // Last button states -uint8_t multiwindow[MAX_KEYS] = { 0 }; // Max time between button presses to record press count -uint8_t multipress[MAX_KEYS] = { 0 }; // Number of button presses within multiwindow +struct BUTTON { + unsigned long debounce = 0; // Button debounce timer + uint16_t hold_timer[MAX_KEYS] = { 0 }; // Timer for button hold + uint16_t dual_code = 0; // Sonoff dual received code -uint8_t dual_hex_code = 0; // Sonoff dual input flag -uint8_t key_no_pullup = 0; // key no pullup flag (1 = no pullup) -uint8_t key_inverted = 0; // Key inverted flag (1 = inverted) -uint8_t buttons_present = 0; // Number of buttons found flag -uint8_t button_adc = 99; // ADC0 button number + uint8_t last_state[MAX_KEYS] = { NOT_PRESSED, NOT_PRESSED, NOT_PRESSED, NOT_PRESSED }; // Last button states + uint8_t window_timer[MAX_KEYS] = { 0 }; // Max time between button presses to record press count + uint8_t press_counter[MAX_KEYS] = { 0 }; // Number of button presses within Button.window_timer + + uint8_t dual_receive_count = 0; // Sonoff dual input flag + uint8_t no_pullup_mask = 0; // key no pullup flag (1 = no pullup) + uint8_t inverted_mask = 0; // Key inverted flag (1 = inverted) + uint8_t present = 0; // Number of buttons found flag + uint8_t adc = 99; // ADC0 button number +} Button; /********************************************************************************************/ void ButtonPullupFlag(uint8 button_bit) { - bitSet(key_no_pullup, button_bit); + bitSet(Button.no_pullup_mask, button_bit); } void ButtonInvertFlag(uint8 button_bit) { - bitSet(key_inverted, button_bit); + bitSet(Button.inverted_mask, button_bit); } void ButtonInit(void) { - buttons_present = 0; + Button.present = 0; for (uint32_t i = 0; i < MAX_KEYS; i++) { if (pin[GPIO_KEY1 +i] < 99) { - buttons_present++; - pinMode(pin[GPIO_KEY1 +i], bitRead(key_no_pullup, i) ? INPUT : ((16 == pin[GPIO_KEY1 +i]) ? INPUT_PULLDOWN_16 : INPUT_PULLUP)); + Button.present++; + pinMode(pin[GPIO_KEY1 +i], bitRead(Button.no_pullup_mask, i) ? INPUT : ((16 == pin[GPIO_KEY1 +i]) ? INPUT_PULLDOWN_16 : INPUT_PULLUP)); } #ifndef USE_ADC_VCC - else if ((99 == button_adc) && ((ADC0_BUTTON == my_adc0) || (ADC0_BUTTON_INV == my_adc0))) { - buttons_present++; - button_adc = i; + else if ((99 == Button.adc) && ((ADC0_BUTTON == my_adc0) || (ADC0_BUTTON_INV == my_adc0))) { + Button.present++; + Button.adc = i; } #endif // USE_ADC_VCC } @@ -68,21 +74,21 @@ void ButtonInit(void) uint8_t ButtonSerial(uint8_t serial_in_byte) { - if (dual_hex_code) { - dual_hex_code--; - if (dual_hex_code) { - dual_button_code = (dual_button_code << 8) | serial_in_byte; + if (Button.dual_receive_count) { + Button.dual_receive_count--; + if (Button.dual_receive_count) { + Button.dual_code = (Button.dual_code << 8) | serial_in_byte; serial_in_byte = 0; } else { if (serial_in_byte != 0xA1) { - dual_button_code = 0; // 0xA1 - End of Sonoff dual button code + Button.dual_code = 0; // 0xA1 - End of Sonoff dual button code } } } if (0xA0 == serial_in_byte) { // 0xA0 - Start of Sonoff dual button code serial_in_byte = 0; - dual_button_code = 0; - dual_hex_code = 3; + Button.dual_code = 0; + Button.dual_receive_count = 3; } return serial_in_byte; @@ -117,22 +123,22 @@ void ButtonHandler(void) if (!button_index && ((SONOFF_DUAL == my_module_type) || (CH4 == my_module_type))) { button_present = 1; - if (dual_button_code) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BUTTON " " D_CODE " %04X"), dual_button_code); + if (Button.dual_code) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BUTTON " " D_CODE " %04X"), Button.dual_code); button = PRESSED; - if (0xF500 == dual_button_code) { // Button hold - holdbutton[button_index] = (loops_per_second * Settings.param[P_HOLD_TIME] / 10) -1; // SetOption32 (40) + if (0xF500 == Button.dual_code) { // Button hold + Button.hold_timer[button_index] = (loops_per_second * Settings.param[P_HOLD_TIME] / 10) -1; // SetOption32 (40) hold_time_extent = 1; } - dual_button_code = 0; + Button.dual_code = 0; } } else if (pin[GPIO_KEY1 +button_index] < 99) { button_present = 1; - button = (digitalRead(pin[GPIO_KEY1 +button_index]) != bitRead(key_inverted, button_index)); + button = (digitalRead(pin[GPIO_KEY1 +button_index]) != bitRead(Button.inverted_mask, button_index)); } #ifndef USE_ADC_VCC - if (button_adc == button_index) { + if (Button.adc == button_index) { button_present = 1; if (ADC0_BUTTON_INV == my_adc0) { button = (AdcRead(1) < 128); @@ -150,45 +156,45 @@ void ButtonHandler(void) // Serviced } else if (SONOFF_4CHPRO == my_module_type) { - if (holdbutton[button_index]) { holdbutton[button_index]--; } + if (Button.hold_timer[button_index]) { Button.hold_timer[button_index]--; } bool button_pressed = false; - if ((PRESSED == button) && (NOT_PRESSED == lastbutton[button_index])) { + if ((PRESSED == button) && (NOT_PRESSED == Button.last_state[button_index])) { AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BUTTON "%d " D_LEVEL_10), button_index +1); - holdbutton[button_index] = loops_per_second; + Button.hold_timer[button_index] = loops_per_second; button_pressed = true; } - if ((NOT_PRESSED == button) && (PRESSED == lastbutton[button_index])) { + if ((NOT_PRESSED == button) && (PRESSED == Button.last_state[button_index])) { AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BUTTON "%d " D_LEVEL_01), button_index +1); - if (!holdbutton[button_index]) { button_pressed = true; } // Do not allow within 1 second + if (!Button.hold_timer[button_index]) { button_pressed = true; } // Do not allow within 1 second } if (button_pressed) { - if (!SendKey(0, button_index +1, POWER_TOGGLE)) { // Execute Toggle command via MQTT if ButtonTopic is set + if (!SendKey(KEY_BUTTON, button_index +1, POWER_TOGGLE)) { // Execute Toggle command via MQTT if ButtonTopic is set ExecuteCommandPower(button_index +1, POWER_TOGGLE, SRC_BUTTON); // Execute Toggle command internally } } } else { - if ((PRESSED == button) && (NOT_PRESSED == lastbutton[button_index])) { + if ((PRESSED == button) && (NOT_PRESSED == Button.last_state[button_index])) { if (Settings.flag.button_single) { // SetOption13 (0) - Allow only single button press for immediate action AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BUTTON "%d " D_IMMEDIATE), button_index +1); - if (!SendKey(0, button_index +1, POWER_TOGGLE)) { // Execute Toggle command via MQTT if ButtonTopic is set + if (!SendKey(KEY_BUTTON, button_index +1, POWER_TOGGLE)) { // Execute Toggle command via MQTT if ButtonTopic is set ExecuteCommandPower(button_index +1, POWER_TOGGLE, SRC_BUTTON); // Execute Toggle command internally } } else { - multipress[button_index] = (multiwindow[button_index]) ? multipress[button_index] +1 : 1; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BUTTON "%d " D_MULTI_PRESS " %d"), button_index +1, multipress[button_index]); - multiwindow[button_index] = loops_per_second / 2; // 0.5 second multi press window + Button.press_counter[button_index] = (Button.window_timer[button_index]) ? Button.press_counter[button_index] +1 : 1; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BUTTON "%d " D_MULTI_PRESS " %d"), button_index +1, Button.press_counter[button_index]); + Button.window_timer[button_index] = loops_per_second / 2; // 0.5 second multi press window } blinks = 201; } if (NOT_PRESSED == button) { - holdbutton[button_index] = 0; + Button.hold_timer[button_index] = 0; } else { - holdbutton[button_index]++; + Button.hold_timer[button_index]++; if (Settings.flag.button_single) { // SetOption13 (0) - Allow only single button press for immediate action - if (holdbutton[button_index] == loops_per_second * hold_time_extent * Settings.param[P_HOLD_TIME] / 10) { // SetOption32 (40) - Button held for factor times longer + if (Button.hold_timer[button_index] == loops_per_second * hold_time_extent * Settings.param[P_HOLD_TIME] / 10) { // SetOption32 (40) - Button held for factor times longer // Settings.flag.button_single = 0; snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_SETOPTION "13 0")); // Disable single press only ExecuteCommand(scmnd, SRC_BUTTON); @@ -196,19 +202,19 @@ void ButtonHandler(void) } else { if (Settings.flag.button_restrict) { // SetOption1 (0) - Button restriction if (Settings.param[P_HOLD_IGNORE] > 0) { // SetOption40 (0) - Do not ignore button hold - if (holdbutton[button_index] > loops_per_second * Settings.param[P_HOLD_IGNORE] / 10) { - holdbutton[button_index] = 0; // Reset button hold counter to stay below hold trigger - multipress[button_index] = 0; // Discard button press to disable functionality -// AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BUTTON "%d cancel by " D_CMND_SETOPTION "40 %d"), button_index +1, Settings.param[P_HOLD_IGNORE]); + if (Button.hold_timer[button_index] > loops_per_second * Settings.param[P_HOLD_IGNORE] / 10) { + Button.hold_timer[button_index] = 0; // Reset button hold counter to stay below hold trigger + Button.press_counter[button_index] = 0; // Discard button press to disable functionality + DEBUG_CORE_LOG(PSTR("BTN: " D_BUTTON "%d cancel by " D_CMND_SETOPTION "40 %d"), button_index +1, Settings.param[P_HOLD_IGNORE]); } } - if (holdbutton[button_index] == loops_per_second * Settings.param[P_HOLD_TIME] / 10) { // SetOption32 (40) - Button hold - multipress[button_index] = 0; - SendKey(0, button_index +1, 3); // Execute Hold command via MQTT if ButtonTopic is set + if (Button.hold_timer[button_index] == loops_per_second * Settings.param[P_HOLD_TIME] / 10) { // SetOption32 (40) - Button hold + Button.press_counter[button_index] = 0; + SendKey(KEY_BUTTON, button_index +1, POWER_HOLD); // Execute Hold command via MQTT if ButtonTopic is set } } else { - if (holdbutton[button_index] == loops_per_second * hold_time_extent * Settings.param[P_HOLD_TIME] / 10) { // SetOption32 (40) - Button held for factor times longer - multipress[button_index] = 0; + if (Button.hold_timer[button_index] == loops_per_second * hold_time_extent * Settings.param[P_HOLD_TIME] / 10) { // SetOption32 (40) - Button held for factor times longer + Button.press_counter[button_index] = 0; snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_RESET " 1")); ExecuteCommand(scmnd, SRC_BUTTON); } @@ -217,64 +223,62 @@ void ButtonHandler(void) } if (!Settings.flag.button_single) { // SetOption13 (0) - Allow multi-press - if (multiwindow[button_index]) { - multiwindow[button_index]--; + if (Button.window_timer[button_index]) { + Button.window_timer[button_index]--; } else { - if (!restart_flag && !holdbutton[button_index] && (multipress[button_index] > 0) && (multipress[button_index] < MAX_BUTTON_COMMANDS +3)) { + if (!restart_flag && !Button.hold_timer[button_index] && (Button.press_counter[button_index] > 0) && (Button.press_counter[button_index] < MAX_BUTTON_COMMANDS +3)) { bool single_press = false; - if (multipress[button_index] < 3) { // Single or Double press + if (Button.press_counter[button_index] < 3) { // Single or Double press if ((SONOFF_DUAL_R2 == my_module_type) || (SONOFF_DUAL == my_module_type) || (CH4 == my_module_type)) { single_press = true; } else { - single_press = (Settings.flag.button_swap +1 == multipress[button_index]); // SetOption11 (0) - if ((1 == buttons_present) && (2 == devices_present)) { // Single Button with two devices only + single_press = (Settings.flag.button_swap +1 == Button.press_counter[button_index]); // SetOption11 (0) + if ((1 == Button.present) && (2 == devices_present)) { // Single Button with two devices only if (Settings.flag.button_swap) { // SetOption11 (0) - multipress[button_index] = (single_press) ? 1 : 2; + Button.press_counter[button_index] = (single_press) ? 1 : 2; } } else { - multipress[button_index] = 1; + Button.press_counter[button_index] = 1; } } } -#ifdef USE_LIGHT - if ((MI_DESK_LAMP == my_module_type) && (button_index == 0) && (rotary_changed) && (light_power)) { - rotary_changed = 0; // Color temp changed, no need to turn of the light - } else { +#if defined(USE_LIGHT) && defined(ROTARY_V1) + if (!((0 == button_index) && RotaryButtonPressed())) { #endif - if (single_press && SendKey(0, button_index + multipress[button_index], POWER_TOGGLE)) { // Execute Toggle command via MQTT if ButtonTopic is set + if (single_press && SendKey(KEY_BUTTON, button_index + Button.press_counter[button_index], POWER_TOGGLE)) { // Execute Toggle command via MQTT if ButtonTopic is set // Success } else { - if (multipress[button_index] < 3) { // Single or Double press - if (WifiState() > WIFI_RESTART) { // WPSconfig, Smartconfig or Wifimanager active + if (Button.press_counter[button_index] < 3) { // Single or Double press + if (WifiState() > WIFI_RESTART) { // Wifimanager active restart_flag = 1; } else { - ExecuteCommandPower(button_index + multipress[button_index], POWER_TOGGLE, SRC_BUTTON); // Execute Toggle command internally + ExecuteCommandPower(button_index + Button.press_counter[button_index], POWER_TOGGLE, SRC_BUTTON); // Execute Toggle command internally } } else { // 3 - 7 press if (!Settings.flag.button_restrict) { // SetOption1 (0) - snprintf_P(scmnd, sizeof(scmnd), kCommands[multipress[button_index] -3]); + GetTextIndexed(scmnd, sizeof(scmnd), Button.press_counter[button_index] -3, kCommands); ExecuteCommand(scmnd, SRC_BUTTON); } } } -#ifdef USE_LIGHT +#if defined(USE_LIGHT) && defined(ROTARY_V1) } #endif - multipress[button_index] = 0; + Button.press_counter[button_index] = 0; } } } } } - lastbutton[button_index] = button; + Button.last_state[button_index] = button; } } void ButtonLoop(void) { - if (buttons_present) { - if (TimeReached(button_debounce)) { - SetNextTimeInterval(button_debounce, Settings.button_debounce); // ButtonDebounce (50) + if (Button.present) { + if (TimeReached(Button.debounce)) { + SetNextTimeInterval(Button.debounce, Settings.button_debounce); // ButtonDebounce (50) ButtonHandler(); } } diff --git a/sonoff/support_command.ino b/sonoff/support_command.ino new file mode 100644 index 000000000..4704e14e1 --- /dev/null +++ b/sonoff/support_command.ino @@ -0,0 +1,1519 @@ +/* + support_command.ino - command support for Sonoff-Tasmota + + Copyright (C) 2019 Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +const char kTasmotaCommands[] PROGMEM = "|" // No prefix + D_CMND_BACKLOG "|" D_CMND_DELAY "|" D_CMND_POWER "|" D_CMND_STATUS "|" D_CMND_STATE "|" D_CMND_SLEEP "|" D_CMND_UPGRADE "|" D_CMND_UPLOAD "|" D_CMND_OTAURL "|" + D_CMND_SERIALLOG "|" D_CMND_RESTART "|" D_CMND_POWERONSTATE "|" D_CMND_PULSETIME "|" D_CMND_BLINKTIME "|" D_CMND_BLINKCOUNT "|" D_CMND_SAVEDATA "|" + D_CMND_SETOPTION "|" D_CMND_TEMPERATURE_RESOLUTION "|" D_CMND_HUMIDITY_RESOLUTION "|" D_CMND_PRESSURE_RESOLUTION "|" D_CMND_POWER_RESOLUTION "|" + D_CMND_VOLTAGE_RESOLUTION "|" D_CMND_FREQUENCY_RESOLUTION "|" D_CMND_CURRENT_RESOLUTION "|" D_CMND_ENERGY_RESOLUTION "|" D_CMND_WEIGHT_RESOLUTION "|" + D_CMND_MODULE "|" D_CMND_MODULES "|" D_CMND_GPIO "|" D_CMND_GPIOS "|" D_CMND_TEMPLATE "|" D_CMND_PWM "|" D_CMND_PWMFREQUENCY "|" D_CMND_PWMRANGE "|" + D_CMND_BUTTONDEBOUNCE "|" D_CMND_SWITCHDEBOUNCE "|" D_CMND_SYSLOG "|" D_CMND_LOGHOST "|" D_CMND_LOGPORT "|" D_CMND_SERIALSEND "|" D_CMND_BAUDRATE "|" + D_CMND_SERIALDELIMITER "|" D_CMND_IPADDRESS "|" D_CMND_NTPSERVER "|" D_CMND_AP "|" D_CMND_SSID "|" D_CMND_PASSWORD "|" D_CMND_HOSTNAME "|" D_CMND_WIFICONFIG "|" + D_CMND_FRIENDLYNAME "|" D_CMND_SWITCHMODE "|" D_CMND_INTERLOCK "|" D_CMND_TELEPERIOD "|" D_CMND_RESET "|" D_CMND_TIME "|" D_CMND_TIMEZONE "|" D_CMND_TIMESTD "|" + D_CMND_TIMEDST "|" D_CMND_ALTITUDE "|" D_CMND_LEDPOWER "|" D_CMND_LEDSTATE "|" D_CMND_LEDMASK "|" +#ifdef USE_I2C + D_CMND_I2CSCAN "|" +#endif + D_CMND_SENSOR "|" D_CMND_DRIVER; + +void (* const TasmotaCommand[])(void) PROGMEM = { + &CmndBacklog, &CmndDelay, &CmndPower, &CmndStatus, &CmndState, &CmndSleep, &CmndUpgrade, &CmndUpgrade, &CmndOtaUrl, + &CmndSeriallog, &CmndRestart, &CmndPowerOnState, &CmndPulsetime, &CmndBlinktime, &CmndBlinkcount, &CmndSavedata, + &CmndSetoption, &CmndTemperatureResolution, &CmndHumidityResolution, &CmndPressureResolution, &CmndPowerResolution, + &CmndVoltageResolution, &CmndFrequencyResolution, &CmndCurrentResolution, &CmndEnergyResolution, &CmndWeightResolution, + &CmndModule, &CmndModules, &CmndGpio, &CmndGpios, &CmndTemplate, &CmndPwm, &CmndPwmfrequency, &CmndPwmrange, + &CmndButtonDebounce, &CmndSwitchDebounce, &CmndSyslog, &CmndLoghost, &CmndLogport, &CmndSerialSend, &CmndBaudrate, + &CmndSerialDelimiter, &CmndIpAddress, &CmndNtpServer, &CmndAp, &CmndSsid, &CmndPassword, &CmndHostname, &CmndWifiConfig, + &CmndFriendlyname, &CmndSwitchMode, &CmndInterlock, &CmndTeleperiod, &CmndReset, &CmndTime, &CmndTimezone, &CmndTimeStd, + &CmndTimeDst, &CmndAltitude, &CmndLedPower, &CmndLedState, &CmndLedMask, +#ifdef USE_I2C + &CmndI2cScan, +#endif + &CmndSensor, &CmndDriver }; + +const char kWifiConfig[] PROGMEM = + D_WCFG_0_RESTART "||" D_WCFG_2_WIFIMANAGER "||" D_WCFG_4_RETRY "|" D_WCFG_5_WAIT "|" D_WCFG_6_SERIAL "|" D_WCFG_7_WIFIMANAGER_RESET_ONLY; + +/********************************************************************************************/ + +void ResponseCmndNumber(int value) +{ + Response_P(S_JSON_COMMAND_NVALUE, XdrvMailbox.command, value); +} + +void ResponseCmndIdxNumber(int value) +{ + Response_P(S_JSON_COMMAND_INDEX_NVALUE, XdrvMailbox.command, XdrvMailbox.index, value); +} + +void ResponseCmndChar(const char* value) +{ + Response_P(S_JSON_COMMAND_SVALUE, XdrvMailbox.command, value); +} + +void ResponseCmndStateText(uint32_t value) +{ + ResponseCmndChar(GetStateText(value)); +} + +void ResponseCmndDone(void) +{ + ResponseCmndChar(D_JSON_DONE); +} + +void ResponseCmndIdxChar(const char* value) +{ + Response_P(S_JSON_COMMAND_INDEX_SVALUE, XdrvMailbox.command, XdrvMailbox.index, value); +} + +/********************************************************************************************/ + +void ExecuteCommand(const char *cmnd, uint32_t source) +{ + // cmnd: "status 0" = stopic "status" and svalue " 0" + // cmnd: "var1 =1" = stopic "var1" and svalue " =1" + // cmnd: "var1=1" = stopic "var1" and svalue "=1" +#ifdef USE_DEBUG_DRIVER + ShowFreeMem(PSTR("ExecuteCommand")); +#endif + ShowSource(source); + + const char *pos = cmnd; + while (*pos && isspace(*pos)) { + pos++; // Skip all spaces + } + + const char *start = pos; + // Get a command. Commands can only use letters, digits and underscores + while (*pos && (isalpha(*pos) || isdigit(*pos) || '_' == *pos || '/' == *pos)) { + if ('/' == *pos) { // Skip possible cmnd/sonoff/ preamble + start = pos + 1; + } + pos++; + } + if ('\0' == *start || pos <= start) { + return; // Did not find any command to execute + } + + uint32_t size = pos - start; + char stopic[size + 2]; // with leader '/' and end '\0' + stopic[0] = '/'; + memcpy(stopic+1, start, size); + stopic[size+1] = '\0'; + + char svalue[strlen(pos) +1]; // pos point to the start of parameters + strlcpy(svalue, pos, sizeof(svalue)); + CommandHandler(stopic, svalue, strlen(svalue)); +} + +/********************************************************************************************/ + +// topicBuf: /power1 dataBuf: toggle = Console command +// topicBuf: cmnd/sonoff/power1 dataBuf: toggle = Mqtt command using topic +// topicBuf: cmnd/sonoffs/power1 dataBuf: toggle = Mqtt command using a group topic +// topicBuf: cmnd/DVES_83BB10_fb/power1 dataBuf: toggle = Mqtt command using fallback topic + +void CommandHandler(char* topicBuf, char* dataBuf, uint32_t data_len) +{ +#ifdef USE_DEBUG_DRIVER + ShowFreeMem(PSTR("CommandHandler")); +#endif + + while (*dataBuf && isspace(*dataBuf)) { + dataBuf++; // Skip leading spaces in data + data_len--; + } + + bool grpflg = (strstr(topicBuf, Settings.mqtt_grptopic) != nullptr); + + char stemp1[TOPSZ]; + GetFallbackTopic_P(stemp1, CMND, ""); // Full Fallback topic = cmnd/DVES_xxxxxxxx_fb/ + fallback_topic_flag = (!strncmp(topicBuf, stemp1, strlen(stemp1))); + + char *type = strrchr(topicBuf, '/'); // Last part of received topic is always the command (type) + + uint32_t i = 0; + uint32_t index = 1; + bool user_index = false; + if (type != nullptr) { + type++; + for (i = 0; i < strlen(type); i++) { + type[i] = toupper(type[i]); + } + while (isdigit(type[i-1])) { + i--; + } + if (i < strlen(type)) { + index = atoi(type +i); + user_index = true; + } + type[i] = '\0'; + } + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CMD: " D_GROUP " %d, " D_INDEX " %d, " D_COMMAND " \"%s\", " D_DATA " \"%s\""), grpflg, index, type, dataBuf); + + if (type != nullptr) { + Response_P(PSTR("{\"" D_JSON_COMMAND "\":\"" D_JSON_ERROR "\"}")); + + if (Settings.ledstate &0x02) { blinks++; } + + if (!strcmp(dataBuf,"?")) { data_len = 0; } + + char *p; + int32_t payload = strtol(dataBuf, &p, 0); // decimal, octal (0) or hex (0x) + if (p == dataBuf) { payload = -99; } + int temp_payload = GetStateNumber(dataBuf); + if (temp_payload > -1) { payload = temp_payload; } + + DEBUG_CORE_LOG(PSTR("CMD: Payload %d"), payload); + +// backlog_delay = millis() + (100 * MIN_BACKLOG_DELAY); + backlog_delay = millis() + Settings.param[P_BACKLOG_DELAY]; + + char command[CMDSZ]; + XdrvMailbox.command = command; + XdrvMailbox.index = index; + XdrvMailbox.data_len = data_len; + XdrvMailbox.payload = payload; + XdrvMailbox.grpflg = grpflg; + XdrvMailbox.usridx = user_index; + XdrvMailbox.topic = type; + XdrvMailbox.data = dataBuf; + +#ifdef USE_SCRIPT_SUB_COMMAND + // allow overwrite tasmota cmds + if (!Script_SubCmd()) { + if (!DecodeCommand(kTasmotaCommands, TasmotaCommand)) { + if (!XdrvCall(FUNC_COMMAND)) { + if (!XsnsCall(FUNC_COMMAND)) { + type = nullptr; // Unknown command + } + } + } + } +#else //USE_SCRIPT_SUB_COMMAND + if (!DecodeCommand(kTasmotaCommands, TasmotaCommand)) { + if (!XdrvCall(FUNC_COMMAND)) { + if (!XsnsCall(FUNC_COMMAND)) { + type = nullptr; // Unknown command + } + } + } +#endif //USE_SCRIPT_SUB_COMMAND + + } + + if (type == nullptr) { + blinks = 201; + snprintf_P(topicBuf, sizeof(topicBuf), PSTR(D_JSON_COMMAND)); + Response_P(PSTR("{\"" D_JSON_COMMAND "\":\"" D_JSON_UNKNOWN "\"}")); + type = (char*)topicBuf; + } + + if (mqtt_data[0] != '\0') { + MqttPublishPrefixTopic_P(RESULT_OR_STAT, type); + XdrvRulesProcess(); + } + fallback_topic_flag = false; +} + +/********************************************************************************************/ + +void CmndBacklog(void) +{ + if (XdrvMailbox.data_len) { + +#ifdef SUPPORT_IF_STATEMENT + char *blcommand = strtok(XdrvMailbox.data, ";"); + while ((blcommand != nullptr) && (backlog.size() < MAX_BACKLOG)) +#else + uint32_t bl_pointer = (!backlog_pointer) ? MAX_BACKLOG -1 : backlog_pointer; + bl_pointer--; + char *blcommand = strtok(XdrvMailbox.data, ";"); + while ((blcommand != nullptr) && (backlog_index != bl_pointer)) +#endif + { + while(true) { + blcommand = Trim(blcommand); + if (!strncasecmp_P(blcommand, PSTR(D_CMND_BACKLOG), strlen(D_CMND_BACKLOG))) { + blcommand += strlen(D_CMND_BACKLOG); // Skip unnecessary command Backlog + } else { + break; + } + } + if (*blcommand != '\0') { +#ifdef SUPPORT_IF_STATEMENT + if (backlog.size() < MAX_BACKLOG) { + backlog.add(blcommand); + } +#else + backlog[backlog_index] = String(blcommand); + backlog_index++; + if (backlog_index >= MAX_BACKLOG) backlog_index = 0; +#endif + } + blcommand = strtok(nullptr, ";"); + } +// ResponseCmndChar(D_JSON_APPENDED); + mqtt_data[0] = '\0'; + } else { + bool blflag = BACKLOG_EMPTY; +#ifdef SUPPORT_IF_STATEMENT + backlog.clear(); +#else + backlog_pointer = backlog_index; +#endif + ResponseCmndChar(blflag ? D_JSON_EMPTY : D_JSON_ABORTED); + } +} + +void CmndDelay(void) +{ + if ((XdrvMailbox.payload >= (MIN_BACKLOG_DELAY / 100)) && (XdrvMailbox.payload <= 3600)) { + backlog_delay = millis() + (100 * XdrvMailbox.payload); + } + uint32_t bl_delay = 0; + long bl_delta = TimePassedSince(backlog_delay); + if (bl_delta < 0) { bl_delay = (bl_delta *-1) / 100; } + ResponseCmndNumber(bl_delay); +} + +void CmndPower(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= devices_present)) { + if ((XdrvMailbox.payload < POWER_OFF) || (XdrvMailbox.payload > POWER_BLINK_STOP)) { + XdrvMailbox.payload = POWER_SHOW_STATE; + } +// Settings.flag.device_index_enable = XdrvMailbox.usridx; + ExecuteCommandPower(XdrvMailbox.index, XdrvMailbox.payload, SRC_IGNORE); + mqtt_data[0] = '\0'; + } + else if (0 == XdrvMailbox.index) { + if ((XdrvMailbox.payload < POWER_OFF) || (XdrvMailbox.payload > POWER_TOGGLE)) { + XdrvMailbox.payload = POWER_SHOW_STATE; + } + SetAllPower(XdrvMailbox.payload, SRC_IGNORE); + mqtt_data[0] = '\0'; + } +} + +void CmndStatus(void) +{ + uint32_t payload = ((XdrvMailbox.payload < 0) || (XdrvMailbox.payload > MAX_STATUS)) ? 99 : XdrvMailbox.payload; + + uint32_t option = STAT; + char stemp[MAX_FRIENDLYNAMES * (sizeof(Settings.friendlyname[0]) +MAX_FRIENDLYNAMES)]; + char stemp2[100]; + + // Workaround MQTT - TCP/IP stack queueing when SUB_PREFIX = PUB_PREFIX + if (!strcmp(Settings.mqtt_prefix[0],Settings.mqtt_prefix[1]) && (!payload)) { option++; } // TELE + + if ((!Settings.flag.mqtt_enabled) && (6 == payload)) { payload = 99; } + if (!energy_flg && (9 == payload)) { payload = 99; } + + if ((0 == payload) || (99 == payload)) { + uint32_t maxfn = (devices_present > MAX_FRIENDLYNAMES) ? MAX_FRIENDLYNAMES : (!devices_present) ? 1 : devices_present; +#ifdef USE_SONOFF_IFAN + if (IsModuleIfan()) { maxfn = 1; } +#endif // USE_SONOFF_IFAN + stemp[0] = '\0'; + for (uint32_t i = 0; i < maxfn; i++) { + snprintf_P(stemp, sizeof(stemp), PSTR("%s%s\"%s\"" ), stemp, (i > 0 ? "," : ""), Settings.friendlyname[i]); + } + stemp2[0] = '\0'; + for (uint32_t i = 0; i < MAX_SWITCHES; i++) { + snprintf_P(stemp2, sizeof(stemp2), PSTR("%s%s%d" ), stemp2, (i > 0 ? "," : ""), Settings.switchmode[i]); + } + Response_P(PSTR("{\"" D_CMND_STATUS "\":{\"" D_CMND_MODULE "\":%d,\"" D_CMND_FRIENDLYNAME "\":[%s],\"" D_CMND_TOPIC "\":\"%s\",\"" + D_CMND_BUTTONTOPIC "\":\"%s\",\"" D_CMND_POWER "\":%d,\"" D_CMND_POWERONSTATE "\":%d,\"" D_CMND_LEDSTATE "\":%d,\"" + D_CMND_LEDMASK "\":\"%04X\",\"" D_CMND_SAVEDATA "\":%d,\"" D_JSON_SAVESTATE "\":%d,\"" D_CMND_SWITCHTOPIC "\":\"%s\",\"" + D_CMND_SWITCHMODE "\":[%s],\"" D_CMND_BUTTONRETAIN "\":%d,\"" D_CMND_SWITCHRETAIN "\":%d,\"" D_CMND_SENSORRETAIN "\":%d,\"" D_CMND_POWERRETAIN "\":%d}}"), + ModuleNr(), stemp, mqtt_topic, + Settings.button_topic, power, Settings.poweronstate, Settings.ledstate, + Settings.ledmask, Settings.save_data, Settings.flag.save_state, Settings.switch_topic, + stemp2, Settings.flag.mqtt_button_retain, Settings.flag.mqtt_switch_retain, Settings.flag.mqtt_sensor_retain, Settings.flag.mqtt_power_retain); + MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS)); + } + + if ((0 == payload) || (1 == payload)) { + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS1_PARAMETER "\":{\"" D_JSON_BAUDRATE "\":%d,\"" D_CMND_GROUPTOPIC "\":\"%s\",\"" D_CMND_OTAURL "\":\"%s\",\"" + D_JSON_RESTARTREASON "\":\"%s\",\"" D_JSON_UPTIME "\":\"%s\",\"" D_JSON_STARTUPUTC "\":\"%s\",\"" D_CMND_SLEEP "\":%d,\"" + D_JSON_CONFIG_HOLDER "\":%d,\"" D_JSON_BOOTCOUNT "\":%d,\"" D_JSON_SAVECOUNT "\":%d,\"" D_JSON_SAVEADDRESS "\":\"%X\"}}"), + baudrate, Settings.mqtt_grptopic, Settings.ota_url, + GetResetReason().c_str(), GetUptime().c_str(), GetDateAndTime(DT_RESTART).c_str(), Settings.sleep, + Settings.cfg_holder, Settings.bootcount, Settings.save_flag, GetSettingsAddress()); + MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "1")); + } + + if ((0 == payload) || (2 == payload)) { + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS2_FIRMWARE "\":{\"" D_JSON_VERSION "\":\"%s%s\",\"" D_JSON_BUILDDATETIME "\":\"%s\",\"" + D_JSON_BOOTVERSION "\":%d,\"" D_JSON_COREVERSION "\":\"" ARDUINO_ESP8266_RELEASE "\",\"" D_JSON_SDKVERSION "\":\"%s\"}}"), + my_version, my_image, GetBuildDateAndTime().c_str(), + ESP.getBootVersion(), ESP.getSdkVersion()); + MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "2")); + } + + if ((0 == payload) || (3 == payload)) { + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS3_LOGGING "\":{\"" D_CMND_SERIALLOG "\":%d,\"" D_CMND_WEBLOG "\":%d,\"" D_CMND_MQTTLOG "\":%d,\"" D_CMND_SYSLOG "\":%d,\"" + D_CMND_LOGHOST "\":\"%s\",\"" D_CMND_LOGPORT "\":%d,\"" D_CMND_SSID "\":[\"%s\",\"%s\"],\"" D_CMND_TELEPERIOD "\":%d,\"" + D_JSON_RESOLUTION "\":\"%08X\",\"" D_CMND_SETOPTION "\":[\"%08X\",\"%s\",\"%08X\"]}}"), + Settings.seriallog_level, Settings.weblog_level, Settings.mqttlog_level, Settings.syslog_level, + Settings.syslog_host, Settings.syslog_port, Settings.sta_ssid[0], Settings.sta_ssid[1], Settings.tele_period, + Settings.flag2.data, Settings.flag.data, ToHex_P((unsigned char*)Settings.param, PARAM8_SIZE, stemp2, sizeof(stemp2)), Settings.flag3.data); + MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "3")); + } + + if ((0 == payload) || (4 == payload)) { + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS4_MEMORY "\":{\"" D_JSON_PROGRAMSIZE "\":%d,\"" D_JSON_FREEMEMORY "\":%d,\"" D_JSON_HEAPSIZE "\":%d,\"" + D_JSON_PROGRAMFLASHSIZE "\":%d,\"" D_JSON_FLASHSIZE "\":%d,\"" D_JSON_FLASHCHIPID "\":\"%06X\",\"" D_JSON_FLASHMODE "\":%d,\"" + D_JSON_FEATURES "\":[\"%08X\",\"%08X\",\"%08X\",\"%08X\",\"%08X\",\"%08X\"]"), + ESP.getSketchSize()/1024, ESP.getFreeSketchSpace()/1024, ESP.getFreeHeap()/1024, + ESP.getFlashChipSize()/1024, ESP.getFlashChipRealSize()/1024, ESP.getFlashChipId(), ESP.getFlashChipMode(), + LANGUAGE_LCID, feature_drv1, feature_drv2, feature_sns1, feature_sns2, feature5); + XsnsDriverState(); + ResponseAppend_P(PSTR(",\"Sensors\":")); + XsnsSensorState(); + ResponseJsonEndEnd(); + MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "4")); + } + + if ((0 == payload) || (5 == payload)) { + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS5_NETWORK "\":{\"" D_CMND_HOSTNAME "\":\"%s\",\"" D_CMND_IPADDRESS "\":\"%s\",\"" D_JSON_GATEWAY "\":\"%s\",\"" + D_JSON_SUBNETMASK "\":\"%s\",\"" D_JSON_DNSSERVER "\":\"%s\",\"" D_JSON_MAC "\":\"%s\",\"" + D_CMND_WEBSERVER "\":%d,\"" D_CMND_WIFICONFIG "\":%d}}"), + my_hostname, WiFi.localIP().toString().c_str(), IPAddress(Settings.ip_address[1]).toString().c_str(), + IPAddress(Settings.ip_address[2]).toString().c_str(), IPAddress(Settings.ip_address[3]).toString().c_str(), WiFi.macAddress().c_str(), + Settings.webserver, Settings.sta_config); + MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "5")); + } + + if (((0 == payload) || (6 == payload)) && Settings.flag.mqtt_enabled) { +#ifdef USE_MQTT_AWS_IOT + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS6_MQTT "\":{\"" D_CMND_MQTTHOST "\":\"%s%s\",\"" D_CMND_MQTTPORT "\":%d,\"" D_CMND_MQTTCLIENT D_JSON_MASK "\":\"%s\",\"" + D_CMND_MQTTCLIENT "\":\"%s\",\"" D_JSON_MQTT_COUNT "\":%d,\"MAX_PACKET_SIZE\":%d,\"KEEPALIVE\":%d}}"), + Settings.mqtt_user, Settings.mqtt_host, Settings.mqtt_port, Settings.mqtt_client, + mqtt_client, MqttConnectCount(), MQTT_MAX_PACKET_SIZE, MQTT_KEEPALIVE); +#else + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS6_MQTT "\":{\"" D_CMND_MQTTHOST "\":\"%s\",\"" D_CMND_MQTTPORT "\":%d,\"" D_CMND_MQTTCLIENT D_JSON_MASK "\":\"%s\",\"" + D_CMND_MQTTCLIENT "\":\"%s\",\"" D_CMND_MQTTUSER "\":\"%s\",\"" D_JSON_MQTT_COUNT "\":%d,\"MAX_PACKET_SIZE\":%d,\"KEEPALIVE\":%d}}"), + Settings.mqtt_host, Settings.mqtt_port, Settings.mqtt_client, + mqtt_client, Settings.mqtt_user, MqttConnectCount(), MQTT_MAX_PACKET_SIZE, MQTT_KEEPALIVE); +#endif + MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "6")); + } + + if ((0 == payload) || (7 == payload)) { + if (99 == Settings.timezone) { + snprintf_P(stemp, sizeof(stemp), PSTR("%d" ), Settings.timezone); + } else { + snprintf_P(stemp, sizeof(stemp), PSTR("\"%s\"" ), GetTimeZone().c_str()); + } +#if defined(USE_TIMERS) && defined(USE_SUNRISE) + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS7_TIME "\":{\"" D_JSON_UTC_TIME "\":\"%s\",\"" D_JSON_LOCAL_TIME "\":\"%s\",\"" D_JSON_STARTDST "\":\"%s\",\"" + D_JSON_ENDDST "\":\"%s\",\"" D_CMND_TIMEZONE "\":%s,\"" D_JSON_SUNRISE "\":\"%s\",\"" D_JSON_SUNSET "\":\"%s\"}}"), + GetTime(0).c_str(), GetTime(1).c_str(), GetTime(2).c_str(), + GetTime(3).c_str(), stemp, GetSun(0).c_str(), GetSun(1).c_str()); +#else + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS7_TIME "\":{\"" D_JSON_UTC_TIME "\":\"%s\",\"" D_JSON_LOCAL_TIME "\":\"%s\",\"" D_JSON_STARTDST "\":\"%s\",\"" + D_JSON_ENDDST "\":\"%s\",\"" D_CMND_TIMEZONE "\":%s}}"), + GetTime(0).c_str(), GetTime(1).c_str(), GetTime(2).c_str(), + GetTime(3).c_str(), stemp); +#endif // USE_TIMERS and USE_SUNRISE + MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "7")); + } + +#if defined(USE_ENERGY_SENSOR) && defined(USE_ENERGY_MARGIN_DETECTION) + if (energy_flg) { + if ((0 == payload) || (9 == payload)) { + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS9_MARGIN "\":{\"" D_CMND_POWERDELTA "\":%d,\"" D_CMND_POWERLOW "\":%d,\"" D_CMND_POWERHIGH "\":%d,\"" + D_CMND_VOLTAGELOW "\":%d,\"" D_CMND_VOLTAGEHIGH "\":%d,\"" D_CMND_CURRENTLOW "\":%d,\"" D_CMND_CURRENTHIGH "\":%d}}"), + Settings.energy_power_delta, Settings.energy_min_power, Settings.energy_max_power, + Settings.energy_min_voltage, Settings.energy_max_voltage, Settings.energy_min_current, Settings.energy_max_current); + MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "9")); + } + } +#endif // USE_ENERGY_MARGIN_DETECTION + + if ((0 == payload) || (8 == payload) || (10 == payload)) { + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS10_SENSOR "\":")); + MqttShowSensor(); + ResponseJsonEnd(); + if (8 == payload) { + MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "8")); + } else { + MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "10")); + } + } + + if ((0 == payload) || (11 == payload)) { + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS11_STATUS "\":")); + MqttShowState(); + ResponseJsonEnd(); + MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "11")); + } + +#ifdef USE_SCRIPT_STATUS + if (bitRead(Settings.rule_enabled, 0)) Run_Scripter(">U",2,mqtt_data); +#endif + mqtt_data[0] = '\0'; +} + +void CmndState(void) +{ + mqtt_data[0] = '\0'; + MqttShowState(); + if (Settings.flag3.hass_tele_on_power) { + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_STATE), MQTT_TELE_RETAIN); + } +#ifdef USE_HOME_ASSISTANT + if (Settings.flag.hass_discovery) { + HAssPublishStatus(); + } +#endif // USE_HOME_ASSISTANT +} + +void CmndSleep(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 251)) { + Settings.sleep = XdrvMailbox.payload; + sleep = XdrvMailbox.payload; + WiFiSetSleepMode(); + } + Response_P(S_JSON_COMMAND_NVALUE_ACTIVE_NVALUE, XdrvMailbox.command, Settings.sleep, sleep); + +} + +void CmndUpgrade(void) +{ + // Check if the payload is numerically 1, and had no trailing chars. + // e.g. "1foo" or "1.2.3" could fool us. + // Check if the version we have been asked to upgrade to is higher than our current version. + // We also need at least 3 chars to make a valid version number string. + if (((1 == XdrvMailbox.data_len) && (1 == XdrvMailbox.payload)) || ((XdrvMailbox.data_len >= 3) && NewerVersion(XdrvMailbox.data))) { + ota_state_flag = 3; + char stemp1[TOPSZ]; + Response_P(PSTR("{\"%s\":\"" D_JSON_VERSION " %s " D_JSON_FROM " %s\"}"), XdrvMailbox.command, my_version, GetOtaUrl(stemp1, sizeof(stemp1))); + } else { + Response_P(PSTR("{\"%s\":\"" D_JSON_ONE_OR_GT "\"}"), XdrvMailbox.command, my_version); + } +} + +void CmndOtaUrl(void) +{ + if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.ota_url))) { + strlcpy(Settings.ota_url, (SC_DEFAULT == Shortcut()) ? OTA_URL : XdrvMailbox.data, sizeof(Settings.ota_url)); + } + ResponseCmndChar(Settings.ota_url); +} + +void CmndSeriallog(void) +{ + if ((XdrvMailbox.payload >= LOG_LEVEL_NONE) && (XdrvMailbox.payload <= LOG_LEVEL_ALL)) { + Settings.flag.mqtt_serial = 0; + SetSeriallog(XdrvMailbox.payload); + } + Response_P(S_JSON_COMMAND_NVALUE_ACTIVE_NVALUE, XdrvMailbox.command, Settings.seriallog_level, seriallog_level); +} + +void CmndRestart(void) +{ + switch (XdrvMailbox.payload) { + case 1: + restart_flag = 2; + ResponseCmndChar(D_JSON_RESTARTING); + break; + case 99: + AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_RESTARTING)); + EspRestart(); + break; + default: + ResponseCmndChar(D_JSON_ONE_TO_RESTART); + } +} + +void CmndPowerOnState(void) +{ + if (my_module_type != MOTOR) { + /* 0 = Keep relays off after power on + * 1 = Turn relays on after power on, if PulseTime set wait for PulseTime seconds, and turn relays off + * 2 = Toggle relays after power on + * 3 = Set relays to last saved state after power on + * 4 = Turn relays on and disable any relay control (used for Sonoff Pow to always measure power) + * 5 = Keep relays off after power on, if PulseTime set wait for PulseTime seconds, and turn relays on + */ + if ((XdrvMailbox.payload >= POWER_ALL_OFF) && (XdrvMailbox.payload <= POWER_ALL_OFF_PULSETIME_ON)) { + Settings.poweronstate = XdrvMailbox.payload; + if (POWER_ALL_ALWAYS_ON == Settings.poweronstate) { + for (uint32_t i = 1; i <= devices_present; i++) { + ExecuteCommandPower(i, POWER_ON, SRC_IGNORE); + } + } + } + ResponseCmndNumber(Settings.poweronstate); + } +} + +void CmndPulsetime(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_PULSETIMERS)) { + uint32_t items = 1; + if (!XdrvMailbox.usridx && !XdrvMailbox.data_len) { + items = MAX_PULSETIMERS; + } else { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 65536)) { + Settings.pulse_timer[XdrvMailbox.index -1] = XdrvMailbox.payload; // 0 - 65535 + SetPulseTimer(XdrvMailbox.index -1, XdrvMailbox.payload); + } + } + mqtt_data[0] = '\0'; + for (uint32_t i = 0; i < items; i++) { + uint32_t index = (1 == items) ? XdrvMailbox.index : i +1; + ResponseAppend_P(PSTR("%c\"%s%d\":{\"" D_JSON_SET "\":%d,\"" D_JSON_REMAINING "\":%d}"), + (i) ? ',' : '{', + XdrvMailbox.command, index, + Settings.pulse_timer[index -1], GetPulseTimer(index -1)); + } + ResponseJsonEnd(); + } +} + +void CmndBlinktime(void) +{ + if ((XdrvMailbox.payload > 1) && (XdrvMailbox.payload <= 3600)) { + Settings.blinktime = XdrvMailbox.payload; + if (blink_timer > 0) { blink_timer = millis() + (100 * XdrvMailbox.payload); } + } + ResponseCmndNumber(Settings.blinktime); +} + +void CmndBlinkcount(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 65536)) { + Settings.blinkcount = XdrvMailbox.payload; // 0 - 65535 + if (blink_counter) { blink_counter = Settings.blinkcount *2; } + } + ResponseCmndNumber(Settings.blinkcount); +} + +void CmndSavedata(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3600)) { + Settings.save_data = XdrvMailbox.payload; + save_data_counter = Settings.save_data; + } + SettingsSaveAll(); + char stemp1[TOPSZ]; + if (Settings.save_data > 1) { + snprintf_P(stemp1, sizeof(stemp1), PSTR(D_JSON_EVERY " %d " D_UNIT_SECOND), Settings.save_data); + } + ResponseCmndChar((Settings.save_data > 1) ? stemp1 : GetStateText(Settings.save_data)); +} + +void CmndSetoption(void) +{ + if (XdrvMailbox.index < 82) { + uint32_t ptype; + uint32_t pindex; + if (XdrvMailbox.index <= 31) { // SetOption0 .. 31 = Settings.flag + ptype = 0; + pindex = XdrvMailbox.index; // 0 .. 31 + } + else if (XdrvMailbox.index <= 49) { // SetOption32 .. 49 = Settings.param + ptype = 2; + pindex = XdrvMailbox.index -32; // 0 .. 17 (= PARAM8_SIZE -1) + } + else { // SetOption50 .. 81 = Settings.flag3 + ptype = 1; + pindex = XdrvMailbox.index -50; // 0 .. 31 + } + if (XdrvMailbox.payload >= 0) { + if (0 == ptype) { // SetOption0 .. 31 + if (XdrvMailbox.payload <= 1) { + switch (pindex) { + case 5: // mqtt_power_retain (CMND_POWERRETAIN) + case 6: // mqtt_button_retain (CMND_BUTTONRETAIN) + case 7: // mqtt_switch_retain (CMND_SWITCHRETAIN) + case 9: // mqtt_sensor_retain (CMND_SENSORRETAIN) + case 14: // interlock (CMND_INTERLOCK) + case 22: // mqtt_serial (SerialSend and SerialLog) + case 23: // mqtt_serial_raw (SerialSend) + case 25: // knx_enabled (Web config) + case 27: // knx_enable_enhancement (Web config) + ptype = 99; // Command Error + break; // Ignore command SetOption + case 3: // mqtt + case 15: // pwm_control + restart_flag = 2; + default: + bitWrite(Settings.flag.data, pindex, XdrvMailbox.payload); + } + if (12 == pindex) { // stop_flash_rotate + stop_flash_rotate = XdrvMailbox.payload; + SettingsSave(2); + } +#ifdef USE_HOME_ASSISTANT + if ((19 == pindex) || (30 == pindex)) { + HAssDiscover(); // Delayed execution to provide enough resources during hass_discovery or hass_light + } +#endif // USE_HOME_ASSISTANT + } + } + else if (1 == ptype) { // SetOption50 .. 81 + if (XdrvMailbox.payload <= 1) { + bitWrite(Settings.flag3.data, pindex, XdrvMailbox.payload); + if (5 == pindex) { // SetOption55 + if (0 == XdrvMailbox.payload) { + restart_flag = 2; // Disable mDNS needs restart + } + } + if (10 == pindex) { // SetOption60 enable or disable traditional sleep + WiFiSetSleepMode(); // Update WiFi sleep mode accordingly + } + if (18 == pindex) { // SetOption68 for multi-channel PWM, requires a reboot + restart_flag = 2; + } + } + } + else { // SetOption32 .. 49 + uint32_t param_low = 0; + uint32_t param_high = 255; + switch (pindex) { + case P_HOLD_TIME: + case P_MAX_POWER_RETRY: + param_low = 1; + param_high = 250; + break; + } + if ((XdrvMailbox.payload >= param_low) && (XdrvMailbox.payload <= param_high)) { + Settings.param[pindex] = XdrvMailbox.payload; +#ifdef USE_LIGHT + if (P_RGB_REMAP == pindex) { + LightUpdateColorMapping(); + } +#endif +#if (defined(USE_IR_REMOTE) && defined(USE_IR_RECEIVE)) || defined(USE_IR_REMOTE_FULL) + if (P_IR_UNKNOW_THRESHOLD == pindex) { + IrReceiveUpdateThreshold(); + } +#endif + } + } + } + if (ptype < 99) { + char stemp1[TOPSZ]; + if (2 == ptype) { snprintf_P(stemp1, sizeof(stemp1), PSTR("%d"), Settings.param[pindex]); } + ResponseCmndIdxChar((2 == ptype) ? stemp1 : (1 == ptype) ? GetStateText(bitRead(Settings.flag3.data, pindex)) : GetStateText(bitRead(Settings.flag.data, pindex))); + } + } +} + +void CmndTemperatureResolution(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) { + Settings.flag2.temperature_resolution = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.flag2.temperature_resolution); +} + +void CmndHumidityResolution(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) { + Settings.flag2.humidity_resolution = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.flag2.humidity_resolution); +} + +void CmndPressureResolution(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) { + Settings.flag2.pressure_resolution = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.flag2.pressure_resolution); +} + +void CmndPowerResolution(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) { + Settings.flag2.wattage_resolution = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.flag2.wattage_resolution); +} + +void CmndVoltageResolution(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) { + Settings.flag2.voltage_resolution = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.flag2.voltage_resolution); +} + +void CmndFrequencyResolution(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) { + Settings.flag2.frequency_resolution = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.flag2.frequency_resolution); +} + +void CmndCurrentResolution(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) { + Settings.flag2.current_resolution = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.flag2.current_resolution); +} + +void CmndEnergyResolution(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 5)) { + Settings.flag2.energy_resolution = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.flag2.energy_resolution); +} + +void CmndWeightResolution(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) { + Settings.flag2.weight_resolution = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.flag2.weight_resolution); +} + +void CmndModule(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= MAXMODULE)) { + bool present = false; + if (0 == XdrvMailbox.payload) { + XdrvMailbox.payload = USER_MODULE; + present = true; + } else { + XdrvMailbox.payload--; + present = ValidTemplateModule(XdrvMailbox.payload); + } + if (present) { + Settings.last_module = Settings.module; + Settings.module = XdrvMailbox.payload; + SetModuleType(); + if (Settings.last_module != XdrvMailbox.payload) { + for (uint32_t i = 0; i < sizeof(Settings.my_gp); i++) { + Settings.my_gp.io[i] = GPIO_NONE; + } + } + restart_flag = 2; + } + } + Response_P(S_JSON_COMMAND_NVALUE_SVALUE, XdrvMailbox.command, ModuleNr(), ModuleName().c_str()); +} + +void CmndModules(void) +{ + uint32_t midx = USER_MODULE; + uint32_t lines = 1; + bool jsflg = false; + for (uint32_t i = 0; i <= sizeof(kModuleNiceList); i++) { + if (i > 0) { midx = pgm_read_byte(kModuleNiceList + i -1); } + if (!jsflg) { + Response_P(PSTR("{\"" D_CMND_MODULES "%d\":{"), lines); + } else { + ResponseAppend_P(PSTR(",")); + } + jsflg = true; + uint32_t j = i ? midx +1 : 0; + if ((ResponseAppend_P(PSTR("\"%d\":\"%s\""), j, AnyModuleName(midx).c_str()) > (LOGSZ - TOPSZ)) || (i == sizeof(kModuleNiceList))) { + ResponseJsonEndEnd(); + MqttPublishPrefixTopic_P(RESULT_OR_STAT, UpperCase(XdrvMailbox.command, XdrvMailbox.command)); + jsflg = false; + lines++; + } + } + mqtt_data[0] = '\0'; +} + +void CmndGpio(void) +{ + if (XdrvMailbox.index < sizeof(Settings.my_gp)) { + myio cmodule; + ModuleGpios(&cmodule); + if (ValidGPIO(XdrvMailbox.index, cmodule.io[XdrvMailbox.index]) && (XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < GPIO_SENSOR_END)) { + bool present = false; + for (uint32_t i = 0; i < sizeof(kGpioNiceList); i++) { + uint32_t midx = pgm_read_byte(kGpioNiceList + i); + if (midx == XdrvMailbox.payload) { present = true; } + } + if (present) { + for (uint32_t i = 0; i < sizeof(Settings.my_gp); i++) { + if (ValidGPIO(i, cmodule.io[i]) && (Settings.my_gp.io[i] == XdrvMailbox.payload)) { + Settings.my_gp.io[i] = GPIO_NONE; + } + } + Settings.my_gp.io[XdrvMailbox.index] = XdrvMailbox.payload; + restart_flag = 2; + } + } + Response_P(PSTR("{")); + bool jsflg = false; + for (uint32_t i = 0; i < sizeof(Settings.my_gp); i++) { + if (ValidGPIO(i, cmodule.io[i]) || ((GPIO_USER == XdrvMailbox.payload) && !FlashPin(i))) { + if (jsflg) { ResponseAppend_P(PSTR(",")); } + jsflg = true; + uint8_t sensor_type = Settings.my_gp.io[i]; + if (!ValidGPIO(i, cmodule.io[i])) { + sensor_type = cmodule.io[i]; + if (GPIO_USER == sensor_type) { // A user GPIO equals a not connected (=GPIO_NONE) GPIO here + sensor_type = GPIO_NONE; + } + } + uint8_t sensor_name_idx = sensor_type; + const char *sensor_names = kSensorNames; + if (sensor_type > GPIO_FIX_START) { + sensor_name_idx = sensor_type - GPIO_FIX_START -1; + sensor_names = kSensorNamesFixed; + } + char stemp1[TOPSZ]; + ResponseAppend_P(PSTR("\"" D_CMND_GPIO "%d\":{\"%d\":\"%s\"}"), + i, sensor_type, GetTextIndexed(stemp1, sizeof(stemp1), sensor_name_idx, sensor_names)); + } + } + if (jsflg) { + ResponseJsonEnd(); + } else { + ResponseCmndChar(D_JSON_NOT_SUPPORTED); + } + } +} + +void CmndGpios(void) +{ + myio cmodule; + ModuleGpios(&cmodule); + uint32_t midx; + uint32_t lines = 1; + bool jsflg = false; + for (uint32_t i = 0; i < sizeof(kGpioNiceList); i++) { + midx = pgm_read_byte(kGpioNiceList + i); + if ((XdrvMailbox.payload != 255) && GetUsedInModule(midx, cmodule.io)) { continue; } + if (!jsflg) { + Response_P(PSTR("{\"" D_CMND_GPIOS "%d\":{"), lines); + } else { + ResponseAppend_P(PSTR(",")); + } + jsflg = true; + char stemp1[TOPSZ]; + if ((ResponseAppend_P(PSTR("\"%d\":\"%s\""), midx, GetTextIndexed(stemp1, sizeof(stemp1), midx, kSensorNames)) > (LOGSZ - TOPSZ)) || (i == sizeof(kGpioNiceList) -1)) { + ResponseJsonEndEnd(); + MqttPublishPrefixTopic_P(RESULT_OR_STAT, UpperCase(XdrvMailbox.command, XdrvMailbox.command)); + jsflg = false; + lines++; + } + } + mqtt_data[0] = '\0'; +} + +void CmndTemplate(void) +{ + // {"NAME":"Generic","GPIO":[17,254,29,254,7,254,254,254,138,254,139,254,254],"FLAG":1,"BASE":255} + bool error = false; + + if (strstr(XdrvMailbox.data, "{") == nullptr) { // If no JSON it must be parameter + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= MAXMODULE)) { + XdrvMailbox.payload--; + if (ValidTemplateModule(XdrvMailbox.payload)) { + ModuleDefault(XdrvMailbox.payload); // Copy template module + if (USER_MODULE == Settings.module) { restart_flag = 2; } + } + } + else if (0 == XdrvMailbox.payload) { // Copy current template to user template + if (Settings.module != USER_MODULE) { + ModuleDefault(Settings.module); + } + } + else if (255 == XdrvMailbox.payload) { // Copy current module with user configured GPIO + if (Settings.module != USER_MODULE) { + ModuleDefault(Settings.module); + } + snprintf_P(Settings.user_template.name, sizeof(Settings.user_template.name), PSTR("Merged")); + uint32_t j = 0; + for (uint32_t i = 0; i < sizeof(mycfgio); i++) { + if (6 == i) { j = 9; } + if (8 == i) { j = 12; } + if (my_module.io[j] > GPIO_NONE) { + Settings.user_template.gp.io[i] = my_module.io[j]; + } + j++; + } + } + } + else { + if (JsonTemplate(XdrvMailbox.data)) { // Free 336 bytes StaticJsonBuffer stack space by moving code to function + if (USER_MODULE == Settings.module) { restart_flag = 2; } + } else { + ResponseCmndChar(D_JSON_INVALID_JSON); + error = true; + } + } + if (!error) { TemplateJson(); } +} + +void CmndPwm(void) +{ + if (pwm_present && (XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_PWMS)) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= Settings.pwm_range) && (pin[GPIO_PWM1 + XdrvMailbox.index -1] < 99)) { + Settings.pwm_value[XdrvMailbox.index -1] = XdrvMailbox.payload; + analogWrite(pin[GPIO_PWM1 + XdrvMailbox.index -1], bitRead(pwm_inverted, XdrvMailbox.index -1) ? Settings.pwm_range - XdrvMailbox.payload : XdrvMailbox.payload); + } + Response_P(PSTR("{")); + MqttShowPWMState(); // Render the PWM status to MQTT + ResponseJsonEnd(); + } +} + +void CmndPwmfrequency(void) +{ + if ((1 == XdrvMailbox.payload) || ((XdrvMailbox.payload >= PWM_MIN) && (XdrvMailbox.payload <= PWM_MAX))) { + Settings.pwm_frequency = (1 == XdrvMailbox.payload) ? PWM_FREQ : XdrvMailbox.payload; + analogWriteFreq(Settings.pwm_frequency); // Default is 1000 (core_esp8266_wiring_pwm.c) + } + ResponseCmndNumber(Settings.pwm_frequency); +} + +void CmndPwmrange(void) +{ + if ((1 == XdrvMailbox.payload) || ((XdrvMailbox.payload > 254) && (XdrvMailbox.payload < 1024))) { + Settings.pwm_range = (1 == XdrvMailbox.payload) ? PWM_RANGE : XdrvMailbox.payload; + for (uint32_t i = 0; i < MAX_PWMS; i++) { + if (Settings.pwm_value[i] > Settings.pwm_range) { + Settings.pwm_value[i] = Settings.pwm_range; + } + } + analogWriteRange(Settings.pwm_range); // Default is 1023 (Arduino.h) + } + ResponseCmndNumber(Settings.pwm_range); +} + +void CmndButtonDebounce(void) +{ + if ((XdrvMailbox.payload > 39) && (XdrvMailbox.payload < 1001)) { + Settings.button_debounce = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.button_debounce); +} + +void CmndSwitchDebounce(void) +{ + if ((XdrvMailbox.payload > 39) && (XdrvMailbox.payload < 1001)) { + Settings.switch_debounce = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.switch_debounce); +} + +void CmndBaudrate(void) +{ + if (XdrvMailbox.payload >= 300) { + XdrvMailbox.payload /= 300; // Make it a valid baudrate + baudrate = (XdrvMailbox.payload & 0xFFFF) * 300; + SetSerialBaudrate(baudrate); + } + ResponseCmndNumber(Settings.baudrate * 300); +} + +void CmndSerialSend(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 5)) { + SetSeriallog(LOG_LEVEL_NONE); + Settings.flag.mqtt_serial = 1; + Settings.flag.mqtt_serial_raw = (XdrvMailbox.index > 3) ? 1 : 0; + if (XdrvMailbox.data_len > 0) { + if (1 == XdrvMailbox.index) { + Serial.printf("%s\n", XdrvMailbox.data); // "Hello Tiger\n" + } + else if (2 == XdrvMailbox.index || 4 == XdrvMailbox.index) { + for (uint32_t i = 0; i < XdrvMailbox.data_len; i++) { + Serial.write(XdrvMailbox.data[i]); // "Hello Tiger" or "A0" + } + } + else if (3 == XdrvMailbox.index) { + uint32_t dat_len = XdrvMailbox.data_len; + Serial.printf("%s", Unescape(XdrvMailbox.data, &dat_len)); // "Hello\f" + } + else if (5 == XdrvMailbox.index) { + SerialSendRaw(RemoveSpace(XdrvMailbox.data)); // "AA004566" as hex values + } + ResponseCmndDone(); + } + } +} + +void CmndSerialDelimiter(void) +{ + if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.payload < 256)) { + if (XdrvMailbox.payload > 0) { + Settings.serial_delimiter = XdrvMailbox.payload; + } else { + uint32_t dat_len = XdrvMailbox.data_len; + Unescape(XdrvMailbox.data, &dat_len); + Settings.serial_delimiter = XdrvMailbox.data[0]; + } + } + ResponseCmndNumber(Settings.serial_delimiter); +} + +void CmndSyslog(void) +{ + if ((XdrvMailbox.payload >= LOG_LEVEL_NONE) && (XdrvMailbox.payload <= LOG_LEVEL_ALL)) { + SetSyslog(XdrvMailbox.payload); + } + Response_P(S_JSON_COMMAND_NVALUE_ACTIVE_NVALUE, XdrvMailbox.command, Settings.syslog_level, syslog_level); +} + +void CmndLoghost(void) +{ + if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.syslog_host))) { + strlcpy(Settings.syslog_host, (SC_DEFAULT == Shortcut()) ? SYS_LOG_HOST : XdrvMailbox.data, sizeof(Settings.syslog_host)); + } + ResponseCmndChar(Settings.syslog_host); +} + +void CmndLogport(void) +{ + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 65536)) { + Settings.syslog_port = (1 == XdrvMailbox.payload) ? SYS_LOG_PORT : XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.syslog_port); +} + +void CmndIpAddress(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 4)) { + uint32_t address; + if (ParseIp(&address, XdrvMailbox.data)) { + Settings.ip_address[XdrvMailbox.index -1] = address; +// restart_flag = 2; + } + char stemp1[TOPSZ]; + snprintf_P(stemp1, sizeof(stemp1), PSTR(" (%s)"), WiFi.localIP().toString().c_str()); + Response_P(S_JSON_COMMAND_INDEX_SVALUE_SVALUE, XdrvMailbox.command, XdrvMailbox.index, IPAddress(Settings.ip_address[XdrvMailbox.index -1]).toString().c_str(), (1 == XdrvMailbox.index) ? stemp1:""); + } +} + +void CmndNtpServer(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 3)) { + if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.ntp_server[0]))) { + strlcpy(Settings.ntp_server[XdrvMailbox.index -1], + (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? (1==XdrvMailbox.index)?NTP_SERVER1:(2==XdrvMailbox.index)?NTP_SERVER2:NTP_SERVER3 : XdrvMailbox.data, + sizeof(Settings.ntp_server[0])); + for (uint32_t i = 0; i < strlen(Settings.ntp_server[XdrvMailbox.index -1]); i++) { + if (Settings.ntp_server[XdrvMailbox.index -1][i] == ',') Settings.ntp_server[XdrvMailbox.index -1][i] = '.'; + } +// restart_flag = 2; // Issue #3890 + ntp_force_sync = true; + } + ResponseCmndIdxChar(Settings.ntp_server[XdrvMailbox.index -1]); + } +} + +void CmndAp(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 2)) { + switch (XdrvMailbox.payload) { + case 0: // Toggle + Settings.sta_active ^= 1; + break; + case 1: // AP1 + case 2: // AP2 + Settings.sta_active = XdrvMailbox.payload -1; + } + restart_flag = 2; + } + Response_P(S_JSON_COMMAND_NVALUE_SVALUE, XdrvMailbox.command, Settings.sta_active +1, Settings.sta_ssid[Settings.sta_active]); +} + +void CmndSsid(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 2)) { + if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.sta_ssid[0]))) { + strlcpy(Settings.sta_ssid[XdrvMailbox.index -1], + (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? (1 == XdrvMailbox.index) ? STA_SSID1 : STA_SSID2 : XdrvMailbox.data, + sizeof(Settings.sta_ssid[0])); + Settings.sta_active = XdrvMailbox.index -1; + restart_flag = 2; + } + ResponseCmndIdxChar(Settings.sta_ssid[XdrvMailbox.index -1]); + } +} + +void CmndPassword(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 2)) { + if ((XdrvMailbox.data_len > 4 || SC_CLEAR == Shortcut() || SC_DEFAULT == Shortcut()) && (XdrvMailbox.data_len < sizeof(Settings.sta_pwd[0]))) { + strlcpy(Settings.sta_pwd[XdrvMailbox.index -1], + (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? (1 == XdrvMailbox.index) ? STA_PASS1 : STA_PASS2 : XdrvMailbox.data, + sizeof(Settings.sta_pwd[0])); + Settings.sta_active = XdrvMailbox.index -1; + restart_flag = 2; + ResponseCmndIdxChar(Settings.sta_pwd[XdrvMailbox.index -1]); + } else { + Response_P(S_JSON_COMMAND_INDEX_ASTERISK, XdrvMailbox.command, XdrvMailbox.index); + } + } +} + +void CmndHostname(void) +{ + if (!XdrvMailbox.grpflg && (XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.hostname))) { + strlcpy(Settings.hostname, (SC_DEFAULT == Shortcut()) ? WIFI_HOSTNAME : XdrvMailbox.data, sizeof(Settings.hostname)); + if (strstr(Settings.hostname, "%") != nullptr) { + strlcpy(Settings.hostname, WIFI_HOSTNAME, sizeof(Settings.hostname)); + } + restart_flag = 2; + } + ResponseCmndChar(Settings.hostname); +} + +void CmndWifiConfig(void) +{ + if ((XdrvMailbox.payload >= WIFI_RESTART) && (XdrvMailbox.payload < MAX_WIFI_OPTION)) { + if ((EX_WIFI_SMARTCONFIG == XdrvMailbox.payload) || (EX_WIFI_WPSCONFIG == XdrvMailbox.payload)) { + XdrvMailbox.payload = WIFI_MANAGER; + } + Settings.sta_config = XdrvMailbox.payload; + wifi_state_flag = Settings.sta_config; + if (WifiState() > WIFI_RESTART) { + restart_flag = 2; + } + } + char stemp1[TOPSZ]; + Response_P(S_JSON_COMMAND_NVALUE_SVALUE, XdrvMailbox.command, Settings.sta_config, GetTextIndexed(stemp1, sizeof(stemp1), Settings.sta_config, kWifiConfig)); +} + +void CmndFriendlyname(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_FRIENDLYNAMES)) { + if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.friendlyname[0]))) { + char stemp1[TOPSZ]; + if (1 == XdrvMailbox.index) { + snprintf_P(stemp1, sizeof(stemp1), PSTR(FRIENDLY_NAME)); + } else { + snprintf_P(stemp1, sizeof(stemp1), PSTR(FRIENDLY_NAME "%d"), XdrvMailbox.index); + } + strlcpy(Settings.friendlyname[XdrvMailbox.index -1], (SC_DEFAULT == Shortcut()) ? stemp1 : XdrvMailbox.data, sizeof(Settings.friendlyname[XdrvMailbox.index -1])); + } + ResponseCmndIdxChar(Settings.friendlyname[XdrvMailbox.index -1]); + } +} + +void CmndSwitchMode(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_SWITCHES)) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < MAX_SWITCH_OPTION)) { + Settings.switchmode[XdrvMailbox.index -1] = XdrvMailbox.payload; + } + ResponseCmndIdxNumber(Settings.switchmode[XdrvMailbox.index-1]); + } +} + +void CmndInterlock(void) +{ + // Interlock 0 - Off, Interlock 1 - On, Interlock 1,2 3,4 5,6,7 + uint32_t max_relays = devices_present; + if (light_type) { max_relays--; } + if (max_relays > sizeof(Settings.interlock[0]) * 8) { max_relays = sizeof(Settings.interlock[0]) * 8; } + if (max_relays > 1) { // Only interlock with more than 1 relay + if (XdrvMailbox.data_len > 0) { + if (strstr(XdrvMailbox.data, ",") != nullptr) { // Interlock entry + for (uint32_t i = 0; i < MAX_INTERLOCKS; i++) { Settings.interlock[i] = 0; } // Reset current interlocks + char *group; + char *q; + uint32_t group_index = 0; + power_t relay_mask = 0; + for (group = strtok_r(XdrvMailbox.data, " ", &q); group && group_index < MAX_INTERLOCKS; group = strtok_r(nullptr, " ", &q)) { + char *str; + char *p; + for (str = strtok_r(group, ",", &p); str; str = strtok_r(nullptr, ",", &p)) { + int pbit = atoi(str); + if ((pbit > 0) && (pbit <= max_relays)) { // Only valid relays + pbit--; + if (!bitRead(relay_mask, pbit)) { // Only relay once + bitSet(relay_mask, pbit); + bitSet(Settings.interlock[group_index], pbit); + } + } + } + group_index++; + } + for (uint32_t i = 0; i < group_index; i++) { + uint32_t minimal_bits = 0; + for (uint32_t j = 0; j < max_relays; j++) { + if (bitRead(Settings.interlock[i], j)) { minimal_bits++; } + } + if (minimal_bits < 2) { Settings.interlock[i] = 0; } // Discard single relay as interlock + } + } else { + Settings.flag.interlock = XdrvMailbox.payload &1; // Enable/disable interlock + if (Settings.flag.interlock) { + SetDevicePower(power, SRC_IGNORE); // Remove multiple relays if set + } + } + } + Response_P(PSTR("{\"" D_CMND_INTERLOCK "\":\"%s\",\"" D_JSON_GROUPS "\":\""), GetStateText(Settings.flag.interlock)); + uint32_t anygroup = 0; + for (uint32_t i = 0; i < MAX_INTERLOCKS; i++) { + if (Settings.interlock[i]) { + anygroup++; + ResponseAppend_P(PSTR("%s"), (anygroup > 1) ? " " : ""); + uint32_t anybit = 0; + power_t mask = 1; + for (uint32_t j = 0; j < max_relays; j++) { + if (Settings.interlock[i] & mask) { + anybit++; + ResponseAppend_P(PSTR("%s%d"), (anybit > 1) ? "," : "", j +1); + } + mask <<= 1; + } + } + } + if (!anygroup) { + for (uint32_t j = 1; j <= max_relays; j++) { + ResponseAppend_P(PSTR("%s%d"), (j > 1) ? "," : "", j); + } + } + ResponseAppend_P(PSTR("\"}")); + } else { + Settings.flag.interlock = 0; + ResponseCmndStateText(Settings.flag.interlock); + } +} + +void CmndTeleperiod(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { + Settings.tele_period = (1 == XdrvMailbox.payload) ? TELE_PERIOD : XdrvMailbox.payload; + if ((Settings.tele_period > 0) && (Settings.tele_period < 10)) Settings.tele_period = 10; // Do not allow periods < 10 seconds + tele_period = Settings.tele_period; + } + ResponseCmndNumber(Settings.tele_period); +} + +void CmndReset(void) +{ + switch (XdrvMailbox.payload) { + case 1: + restart_flag = 211; + ResponseCmndChar(D_JSON_RESET_AND_RESTARTING); + break; + case 2 ... 6: + restart_flag = 210 + XdrvMailbox.payload; + Response_P(PSTR("{\"" D_CMND_RESET "\":\"" D_JSON_ERASE ", " D_JSON_RESET_AND_RESTARTING "\"}")); + break; + case 99: + Settings.bootcount = 0; + ResponseCmndDone(); + break; + default: + ResponseCmndChar(D_JSON_ONE_TO_RESET); + } +} + +void CmndTime(void) +{ +// payload 0 = (re-)enable NTP +// payload 1 = Time format {"Time":"2019-09-04T14:31:29"} +// payload 2 = Time format {"Time":"2019-09-04T14:31:29","Epoch":1567600289} +// payload 3 = Time format {"Time":1567600289} +// payload 4 = reserved +// payload 1451602800 - disable NTP and set time to epoch + + uint32_t format = Settings.flag2.time_format; + if (XdrvMailbox.data_len > 0) { + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 4)) { + Settings.flag2.time_format = XdrvMailbox.payload -1; + format = Settings.flag2.time_format; + } else { + format = 1; // {"Time":"2019-09-04T14:31:29","Epoch":1567600289} + RtcSetTime(XdrvMailbox.payload); + } + } + mqtt_data[0] = '\0'; + ResponseAppendTimeFormat(format); + ResponseJsonEnd(); +} + +void CmndTimezone(void) +{ + if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.payload >= -13)) { + Settings.timezone = XdrvMailbox.payload; + Settings.timezone_minutes = 0; + if (XdrvMailbox.payload < 15) { + char *p = strtok (XdrvMailbox.data, ":"); + if (p) { + p = strtok (nullptr, ":"); + if (p) { + Settings.timezone_minutes = strtol(p, nullptr, 10); + if (Settings.timezone_minutes > 59) { Settings.timezone_minutes = 59; } + } + } + } else { + Settings.timezone = 99; + } + ntp_force_sync = true; + } + if (99 == Settings.timezone) { + ResponseCmndNumber(Settings.timezone); + } else { + char stemp1[TOPSZ]; + snprintf_P(stemp1, sizeof(stemp1), PSTR("%+03d:%02d"), Settings.timezone, Settings.timezone_minutes); + ResponseCmndChar(stemp1); + } +} + +void CmndTimeStdDst(uint32_t ts) +{ + // TimeStd 0/1, 0/1/2/3/4, 1..12, 1..7, 0..23, +/-780 + if (XdrvMailbox.data_len > 0) { + if (strstr(XdrvMailbox.data, ",") != nullptr) { // Process parameter entry + uint32_t tpos = 0; // Parameter index + int value = 0; + char *p = XdrvMailbox.data; // Parameters like "1, 2,3 , 4 ,5, -120" or ",,,,,+240" + char *q = p; // Value entered flag + while (p && (tpos < 7)) { + if (p > q) { // Any value entered + if (1 == tpos) { Settings.tflag[ts].hemis = value &1; } + if (2 == tpos) { Settings.tflag[ts].week = (value < 0) ? 0 : (value > 4) ? 4 : value; } + if (3 == tpos) { Settings.tflag[ts].month = (value < 1) ? 1 : (value > 12) ? 12 : value; } + if (4 == tpos) { Settings.tflag[ts].dow = (value < 1) ? 1 : (value > 7) ? 7 : value; } + if (5 == tpos) { Settings.tflag[ts].hour = (value < 0) ? 0 : (value > 23) ? 23 : value; } + if (6 == tpos) { Settings.toffset[ts] = (value < -900) ? -900 : (value > 900) ? 900 : value; } + } + p = Trim(p); // Skip spaces + if (tpos && (*p == ',')) { p++; } // Skip separator + p = Trim(p); // Skip spaces + q = p; // Reset any value entered flag + value = strtol(p, &p, 10); + tpos++; // Next parameter + } + ntp_force_sync = true; + } else { + if (0 == XdrvMailbox.payload) { + if (0 == ts) { + SettingsResetStd(); + } else { + SettingsResetDst(); + } + } + ntp_force_sync = true; + } + } + Response_P(PSTR("{\"%s\":{\"Hemisphere\":%d,\"Week\":%d,\"Month\":%d,\"Day\":%d,\"Hour\":%d,\"Offset\":%d}}"), + XdrvMailbox.command, Settings.tflag[ts].hemis, Settings.tflag[ts].week, Settings.tflag[ts].month, Settings.tflag[ts].dow, Settings.tflag[ts].hour, Settings.toffset[ts]); +} + +void CmndTimeStd(void) +{ + CmndTimeStdDst(0); +} + +void CmndTimeDst(void) +{ + CmndTimeStdDst(1); +} + +void CmndAltitude(void) +{ + if ((XdrvMailbox.data_len > 0) && ((XdrvMailbox.payload >= -30000) && (XdrvMailbox.payload <= 30000))) { + Settings.altitude = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.altitude); +} + +void CmndLedPower(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_LEDS)) { + if (99 == pin[GPIO_LEDLNK]) { XdrvMailbox.index = 1; } + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 2)) { + Settings.ledstate &= 8; // Disable power control + uint32_t mask = 1 << (XdrvMailbox.index -1); // Led to control + switch (XdrvMailbox.payload) { + case 0: // Off + led_power &= (0xFF ^ mask); + Settings.ledstate = 0; + break; + case 1: // On + led_power |= mask; + Settings.ledstate = 8; + break; + case 2: // Toggle + led_power ^= mask; + Settings.ledstate ^= 8; + break; + } + blinks = 0; + if (99 == pin[GPIO_LEDLNK]) { + SetLedPower(Settings.ledstate &8); + } else { + SetLedPowerIdx(XdrvMailbox.index -1, (led_power & mask)); + } + } + bool state = bitRead(led_power, XdrvMailbox.index -1); + if (99 == pin[GPIO_LEDLNK]) { + state = bitRead(Settings.ledstate, 3); + } + ResponseCmndIdxChar(GetStateText(state)); + } +} + +void CmndLedState(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < MAX_LED_OPTION)) { + Settings.ledstate = XdrvMailbox.payload; + if (!Settings.ledstate) { + SetLedPowerAll(0); + SetLedLink(0); + } + } + ResponseCmndNumber(Settings.ledstate); +} + +void CmndLedMask(void) +{ + if (XdrvMailbox.data_len > 0) { + Settings.ledmask = XdrvMailbox.payload; + } + char stemp1[TOPSZ]; + snprintf_P(stemp1, sizeof(stemp1), PSTR("%d (0x%04X)"), Settings.ledmask, Settings.ledmask); + ResponseCmndChar(stemp1); +} + +#ifdef USE_I2C +void CmndI2cScan(void) +{ + if (i2c_flg) { + I2cScan(mqtt_data, sizeof(mqtt_data)); + } +} +#endif // USE_I2C + +void CmndSensor(void) +{ + XsnsCall(FUNC_COMMAND_SENSOR); +} + +void CmndDriver(void) +{ + XdrvCall(FUNC_COMMAND_DRIVER); +} diff --git a/sonoff/support_features.ino b/sonoff/support_features.ino index 1a551ceaf..638930d21 100644 --- a/sonoff/support_features.ino +++ b/sonoff/support_features.ino @@ -23,10 +23,11 @@ void GetFeatures(void) { - feature_drv1 = 0x00000000; // xdrv_02_mqtt.ino, xdrv_04_light.ino, xdrv_06_snfbridge.ino - -// feature_drv1 |= 0x00000001; + feature_drv1 = 0x00000000; +#ifdef USE_ENERGY_MARGIN_DETECTION + feature_drv1 |= 0x00000001; // xdrv_03_energy.ino +#endif #ifdef USE_LIGHT feature_drv1 |= 0x00000002; // sonoff.ino, xdrv_04_light.ino #endif @@ -75,7 +76,7 @@ void GetFeatures(void) #ifdef USE_WS2812_DMA feature_drv1 |= 0x00010000; // xdrv_04_light.ino #endif -#ifdef USE_IR_REMOTE +#if defined(USE_IR_REMOTE) || defined(USE_IR_REMOTE_FULL) feature_drv1 |= 0x00020000; // xdrv_05_irremote.ino #endif #ifdef USE_IR_HVAC @@ -112,13 +113,13 @@ void GetFeatures(void) feature_drv1 |= 0x10000000; // xdrv_11_knx.ino #endif #ifdef USE_WPS - feature_drv1 |= 0x20000000; // support.ino + feature_drv1 |= 0x20000000; // support.ino - removed with version 6.6.0.21 #endif #ifdef USE_SMARTCONFIG - feature_drv1 |= 0x40000000; // support.ino + feature_drv1 |= 0x40000000; // support.ino - removed with version 6.6.0.21 #endif -#if (MQTT_LIBRARY_TYPE == MQTT_ARDUINOMQTT) -// feature_drv1 |= 0x80000000; // xdrv_02_mqtt.ino +#ifdef USE_ENERGY_POWER_LIMIT + feature_drv1 |= 0x80000000; // xdrv_03_energy.ino #endif /*********************************************************************************************/ @@ -170,7 +171,7 @@ void GetFeatures(void) #ifdef USE_PCA9685 feature_drv2 |= 0x00004000; // xdrv_15_pca9685.ino #endif -#if defined(USE_LIGHT) && defined(USE_TUYA_DIMMER) +#if defined(USE_LIGHT) && defined(USE_TUYA_MCU) feature_drv2 |= 0x00008000; // xdrv_16_tuyadimmer.ino #endif #ifdef USE_RC_SWITCH @@ -188,10 +189,12 @@ void GetFeatures(void) #ifdef USE_EMULATION_WEMO feature_drv2 |= 0x00100000; // xdrv_21_wemo.ino #endif - -// feature_drv2 |= 0x00200000; -// feature_drv2 |= 0x00400000; - +#ifdef USE_SONOFF_IFAN + feature_drv2 |= 0x00200000; // xdrv_22_sonoff_ifan.ino +#endif +#ifdef USE_ZIGBEE + feature_drv2 |= 0x00400000; // xdrv_23_zigbee.ino +#endif #ifdef NO_EXTRA_4K_HEAP feature_drv2 |= 0x00800000; // sonoff_post.h #endif @@ -222,7 +225,7 @@ void GetFeatures(void) /*********************************************************************************************/ - feature_sns1 = 0x00000000; // xsns_01_counter.ino, xsns_04_snfsc.ino + feature_sns1 = 0x00000000; #ifdef USE_COUNTER feature_sns1 |= 0x00000001; // xsns_01_counter.ino @@ -237,10 +240,10 @@ void GetFeatures(void) feature_sns1 |= 0x00000008; // xnrg_03_pzem004t.ino #endif #ifdef USE_DS18B20 - feature_sns1 |= 0x00000010; // xsns_05_ds18b20.ino + feature_sns1 |= 0x00000010; // xsns_05_ds18b20.ino - no more support since 6.6.0.18 #endif #ifdef USE_DS18x20_LEGACY - feature_sns1 |= 0x00000020; // xsns_05_ds18x20_legacy.ino + feature_sns1 |= 0x00000020; // xsns_05_ds18x20_legacy.ino - no more support since 6.6.0.14 #endif #ifdef USE_DS18x20 feature_sns1 |= 0x00000040; // xsns_05_ds18x20.ino @@ -303,13 +306,13 @@ void GetFeatures(void) feature_sns1 |= 0x02000000; // xsns_22_sr04.ino #endif #ifdef USE_SDM120 - feature_sns1 |= 0x04000000; // xsns_23_sdm120.ino + feature_sns1 |= 0x04000000; // xnrg_08_sdm120.ino #endif #ifdef USE_SI1145 feature_sns1 |= 0x08000000; // xsns_24_si1145.ino #endif #ifdef USE_SDM630 - feature_sns1 |= 0x10000000; // xsns_25_sdm630.ino + feature_sns1 |= 0x10000000; // xnrg_10_sdm630.ino #endif #ifdef USE_LM75AD feature_sns1 |= 0x20000000; // xsns_26_lm75ad.ino @@ -400,12 +403,100 @@ void GetFeatures(void) #ifdef USE_ADE7953 feature_sns2 |= 0x01000000; // xnrg_07_ade7953.ino #endif -// feature_sns2 |= 0x02000000; -// feature_sns2 |= 0x04000000; -// feature_sns2 |= 0x08000000; -// feature_sns2 |= 0x10000000; -// feature_sns2 |= 0x20000000; -// feature_sns2 |= 0x40000000; -// feature_sns2 |= 0x80000000; +#ifdef USE_SPS30 + feature_sns2 |= 0x02000000; // xsns_44_sps30.ino +#endif +#ifdef USE_VL53L0X + feature_sns2 |= 0x04000000; // xsns_45_vl53l0x.ino +#endif +#ifdef USE_MLX90614 + feature_sns2 |= 0x08000000; // xsns_46_MLX90614.ino +#endif +#ifdef USE_MAX31865 + feature_sns2 |= 0x10000000; // xsns_47-max31865.ino +#endif +#ifdef USE_CHIRP + feature_sns2 |= 0x20000000; // xsns_48_chirp.ino +#endif +#ifdef USE_SOLAX_X1 + feature_sns2 |= 0x40000000; // xnrg_12_solaxX1.ino +#endif +#ifdef USE_PAJ7620 + feature_sns2 |= 0x80000000; // xsns_50_paj7620.ino +#endif + +/*********************************************************************************************/ + + feature5 = 0x00000000; + +#ifdef USE_BUZZER + feature5 |= 0x00000001; // xdrv_24_buzzer.ino +#endif +#ifdef USE_RDM6300 + feature5 |= 0x00000002; // xsns_51_rdm6300.ino +#endif +#ifdef USE_IBEACON + feature5 |= 0x00000004; // xsns_52_ibeacon.ino +#endif +#ifdef USE_SML_M + feature5 |= 0x00000008; // xsns_53_sml.ino +#endif +#ifdef USE_INA226 + feature5 |= 0x00000010; // xsns_54_ina226.ino +#endif +#ifdef USE_A4988_STEPPER + feature5 |= 0x00000020; // xdrv_25_A4988.ino +#endif +#ifdef USE_DDS2382 + feature5 |= 0x00000040; // xnrg_09_dds2382.ino +#endif +#ifdef USE_SM2135 + feature5 |= 0x00000080; // xdrv_026_sm2135.ino +#endif +#ifdef USE_SHUTTER + feature5 |= 0x00000100; // xdrv_027_shutter.ino +#endif +#ifdef USE_PCF8574 + feature5 |= 0x00000200; // xdrv_028_pcf8574.ino +#endif +#ifdef USE_DDSU666 + feature5 |= 0x00000400; // xnrg_11_ddsu666.ino +#endif +#ifdef USE_DEEPSLEEP + feature5 |= 0x00000800; // xdrv_029_deepsleep.ino +#endif +#ifdef USE_SONOFF_SC + feature5 |= 0x00001000; // xsns_04_snfsc.ino +#endif +#ifdef USE_SONOFF_RF + feature5 |= 0x00002000; // xdrv_06_snfbridge.ino +#endif +#ifdef USE_SONOFF_L1 + feature5 |= 0x00004000; // xlgt_05_sonoff_l1.ino +#endif +#ifdef USE_EXS_DIMMER + feature5 |= 0x00008000; // xdrv_30_exs_dimmer.ino +#endif +#ifdef USE_ARDUINO_SLAVE + feature5 |= 0x00010000; // xdrv_31_arduino_slave.ino +#endif +// feature5 |= 0x00020000; +// feature5 |= 0x00040000; +// feature5 |= 0x00080000; + +// feature5 |= 0x00100000; +// feature5 |= 0x00200000; +// feature5 |= 0x00400000; +// feature5 |= 0x00800000; + +// feature5 |= 0x01000000; +// feature5 |= 0x02000000; +// feature5 |= 0x04000000; +// feature5 |= 0x08000000; + +// feature5 |= 0x10000000; +// feature5 |= 0x20000000; +// feature5 |= 0x40000000; +// feature5 |= 0x80000000; } diff --git a/sonoff/support_float.ino b/sonoff/support_float.ino index 6e9379a53..6fe23192d 100644 --- a/sonoff/support_float.ino +++ b/sonoff/support_float.ino @@ -371,3 +371,40 @@ float sqrt1(const float x) return u.x; } + +// +// changeUIntScale +// Change a value for range a..b to c..d, using only unsigned int math +// +// PRE-CONDITIONS (if not satisfied, you may 'halt and catch fire') +// from_min < from_max (not checked) +// to_min < to_max (not checked) +// from_min <= num <= from-max (chacked) +// POST-CONDITIONS +// to_min <= result <= to_max +// +uint16_t changeUIntScale(uint16_t inum, uint16_t ifrom_min, uint16_t ifrom_max, + uint16_t ito_min, uint16_t ito_max) { + // guard-rails + if ((ito_min >= ito_max) || (ifrom_min >= ifrom_max)) { + return ito_min; // invalid input, return arbitrary value + } + // convert to uint31, it's more verbose but code is more compact + uint32_t num = inum; + uint32_t from_min = ifrom_min; + uint32_t from_max = ifrom_max; + uint32_t to_min = ito_min; + uint32_t to_max = ito_max; + + // check source range + num = (num > from_max ? from_max : (num < from_min ? from_min : num)); + uint32_t numerator = (num - from_min) * (to_max - to_min); + uint32_t result; + if (numerator >= 0x80000000L) { + // don't do rounding as it would create an overflow + result = numerator / (from_max - from_min) + to_min; + } else { + result = (((numerator * 2) / (from_max - from_min)) + 1) / 2 + to_min; + } + return (uint32_t) (result > to_max ? to_max : (result < to_min ? to_min : result)); +} diff --git a/sonoff/support_rotary.ino b/sonoff/support_rotary.ino index 71b5e6fe3..cc1ca0eb2 100644 --- a/sonoff/support_rotary.ino +++ b/sonoff/support_rotary.ino @@ -18,20 +18,21 @@ */ #ifdef USE_LIGHT +//#define ROTARY_V1 +#ifdef ROTARY_V1 /*********************************************************************************************\ * Rotary support \*********************************************************************************************/ -unsigned long rotary_debounce = 0; // Rotary debounce timer -uint8_t rotaries_found = 0; -uint8_t rotary_state = 0; -uint8_t rotary_position = 128; -uint8_t rotary_last_position = 128; -uint8_t interrupts_in_use = 0; -uint8_t rotary_changed = 0; - -//#define ROTARY_V1 -#ifdef ROTARY_V1 +struct ROTARY { + unsigned long debounce = 0; // Rotary debounce timer + uint8_t present = 0; + uint8_t state = 0; + uint8_t position = 128; + uint8_t last_position = 128; + uint8_t interrupts_in_use_count = 0; + uint8_t changed = 0; +} Rotary; /********************************************************************************************/ @@ -43,22 +44,22 @@ void update_position(void) * https://github.com/PaulStoffregen/Encoder/blob/master/Encoder.h */ - s = rotary_state & 3; + s = Rotary.state & 3; if (digitalRead(pin[GPIO_ROT1A])) s |= 4; if (digitalRead(pin[GPIO_ROT1B])) s |= 8; switch (s) { case 0: case 5: case 10: case 15: break; case 1: case 7: case 8: case 14: - rotary_position++; break; + Rotary.position++; break; case 2: case 4: case 11: case 13: - rotary_position--; break; + Rotary.position--; break; case 3: case 12: - rotary_position = rotary_position + 2; break; + Rotary.position = Rotary.position + 2; break; default: - rotary_position = rotary_position - 2; break; + Rotary.position = Rotary.position - 2; break; } - rotary_state = (s >> 2); + Rotary.state = (s >> 2); } #ifndef ARDUINO_ESP8266_RELEASE_2_3_0 // Fix core 2.5.x ISR not in IRAM Exception @@ -68,17 +69,26 @@ void update_rotary(void) ICACHE_RAM_ATTR; void update_rotary(void) { if (MI_DESK_LAMP == my_module_type){ - if (light_power) { + if (LightPower()) { update_position(); } } } +bool RotaryButtonPressed(void) +{ + if ((MI_DESK_LAMP == my_module_type) && (Rotary.changed) && LightPower()) { + Rotary.changed = 0; // Color temp changed, no need to turn of the light + return true; + } + return false; +} + void RotaryInit(void) { - rotaries_found = 0; + Rotary.present = 0; if ((pin[GPIO_ROT1A] < 99) && (pin[GPIO_ROT1B] < 99)) { - rotaries_found++; + Rotary.present++; pinMode(pin[GPIO_ROT1A], INPUT_PULLUP); pinMode(pin[GPIO_ROT1B], INPUT_PULLUP); @@ -87,11 +97,11 @@ void RotaryInit(void) if ((pin[GPIO_ROT1A] < 6) || (pin[GPIO_ROT1A] > 11)) { attachInterrupt(digitalPinToInterrupt(pin[GPIO_ROT1A]), update_rotary, CHANGE); - interrupts_in_use++; + Rotary.interrupts_in_use_count++; } if ((pin[GPIO_ROT1B] < 6) || (pin[GPIO_ROT1B] > 11)) { attachInterrupt(digitalPinToInterrupt(pin[GPIO_ROT1B]), update_rotary, CHANGE); - interrupts_in_use++; + Rotary.interrupts_in_use_count++; } } } @@ -102,53 +112,53 @@ void RotaryInit(void) void RotaryHandler(void) { - if (interrupts_in_use < 2) { + if (Rotary.interrupts_in_use_count < 2) { noInterrupts(); update_rotary(); } else { noInterrupts(); } - if (rotary_last_position != rotary_position) { + if (Rotary.last_position != Rotary.position) { if (MI_DESK_LAMP == my_module_type) { // Mi Desk lamp - if (holdbutton[0]) { - rotary_changed = 1; + if (Button.hold_timer[0]) { + Rotary.changed = 1; // button1 is pressed: set color temperature int16_t t = LightGetColorTemp(); - t = t + (rotary_position - rotary_last_position); + t = t + (Rotary.position - Rotary.last_position); if (t < 153) { t = 153; } if (t > 500) { t = 500; } - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_CMND_COLORTEMPERATURE " %d"), rotary_position - rotary_last_position); + DEBUG_CORE_LOG(PSTR("ROT: " D_CMND_COLORTEMPERATURE " %d"), Rotary.position - Rotary.last_position); LightSetColorTemp((uint16_t)t); } else { int8_t d = Settings.light_dimmer; - d = d + (rotary_position - rotary_last_position); + d = d + (Rotary.position - Rotary.last_position); if (d < 1) { d = 1; } if (d > 100) { d = 100; } - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_CMND_DIMMER " %d"), rotary_position - rotary_last_position); + DEBUG_CORE_LOG(PSTR("ROT: " D_CMND_DIMMER " %d"), Rotary.position - Rotary.last_position); LightSetDimmer((uint8_t)d); Settings.light_dimmer = d; } } - rotary_last_position = 128; - rotary_position = 128; + Rotary.last_position = 128; + Rotary.position = 128; } interrupts(); } void RotaryLoop(void) { - if (rotaries_found) { - if (TimeReached(rotary_debounce)) { - SetNextTimeInterval(rotary_debounce, Settings.button_debounce); // Using button_debounce setting for this as well + if (Rotary.present) { + if (TimeReached(Rotary.debounce)) { + SetNextTimeInterval(Rotary.debounce, Settings.button_debounce); // Using button_debounce setting for this as well RotaryHandler(); } } diff --git a/sonoff/support_rtc.ino b/sonoff/support_rtc.ino index e55bff4c5..b799003c4 100644 --- a/sonoff/support_rtc.ino +++ b/sonoff/support_rtc.ino @@ -39,21 +39,56 @@ Ticker TickerRtc; static const uint8_t kDaysInMonth[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; // API starts months from 1, this array starts from 0 static const char kMonthNamesEnglish[] = "JanFebMarAprMayJunJulAugSepOctNovDec"; -uint32_t utc_time = 0; -uint32_t local_time = 0; -uint32_t daylight_saving_time = 0; -uint32_t standard_time = 0; -uint32_t ntp_time = 0; -uint32_t midnight = 0; -uint32_t restart_time = 0; -int32_t drift_time = 0; -int32_t time_timezone = 0; -uint8_t midnight_now = 0; -uint8_t ntp_sync_minute = 0; +struct RTC { + uint32_t utc_time = 0; + uint32_t local_time = 0; + uint32_t daylight_saving_time = 0; + uint32_t standard_time = 0; + uint32_t ntp_time = 0; + uint32_t midnight = 0; + uint32_t restart_time = 0; + int32_t drift_time = 0; + int32_t time_timezone = 0; + uint8_t ntp_sync_minute = 0; + bool midnight_now = false; + bool user_time_entry = false; // Override NTP by user setting +} Rtc; + +uint32_t UtcTime(void) +{ + return Rtc.utc_time; +} + +uint32_t LocalTime(void) +{ + return Rtc.local_time; +} int32_t DriftTime(void) { - return drift_time; + return Rtc.drift_time; +} + +uint32_t Midnight(void) +{ + return Rtc.midnight; +} + +bool MidnightNow(void) +{ + if (Rtc.midnight_now) { + Rtc.midnight_now = false; + return true; + } + return false; +} + +bool IsDst(void) +{ + if (Rtc.time_timezone == Settings.toffset[1]) { + return true; + } + return false; } String GetBuildDateAndTime(void) @@ -85,11 +120,18 @@ String GetBuildDateAndTime(void) return String(bdt); // 2017-03-07T11:08:02 } +String GetMinuteTime(uint32_t minutes) +{ + char tm[6]; + snprintf_P(tm, sizeof(tm), PSTR("%02d:%02d"), minutes / 60, minutes % 60); + + return String(tm); // 03:45 +} + String GetTimeZone(void) { char tz[7]; - - snprintf_P(tz, sizeof(tz), PSTR("%+03d:%02d"), time_timezone / 60, abs(time_timezone % 60)); + snprintf_P(tz, sizeof(tz), PSTR("%+03d:%02d"), Rtc.time_timezone / 60, abs(Rtc.time_timezone % 60)); return String(tz); // -03:45 } @@ -139,20 +181,20 @@ String GetDT(uint32_t time) String GetDateAndTime(uint8_t time_type) { // "2017-03-07T11:08:02-07:00" - ISO8601:2004 - uint32_t time = local_time; + uint32_t time = Rtc.local_time; switch (time_type) { case DT_ENERGY: time = Settings.energy_kWhtotal_time; break; case DT_UTC: - time = utc_time; + time = Rtc.utc_time; break; case DT_RESTART: - if (restart_time == 0) { + if (Rtc.restart_time == 0) { return ""; } - time = restart_time; + time = Rtc.restart_time; break; } String dt = GetDT(time); // 2017-03-07T11:08:02 @@ -170,10 +212,10 @@ String GetTime(int type) */ char stime[25]; // Skip newline - uint32_t time = utc_time; - if (1 == type) time = local_time; - if (2 == type) time = daylight_saving_time; - if (3 == type) time = standard_time; + uint32_t time = Rtc.utc_time; + if (1 == type) time = Rtc.local_time; + if (2 == type) time = Rtc.daylight_saving_time; + if (3 == type) time = Rtc.standard_time; snprintf_P(stime, sizeof(stime), sntp_get_real_time(time)); return String(stime); // Thu Nov 01 11:41:02 2018 @@ -181,8 +223,8 @@ String GetTime(int type) uint32_t UpTime(void) { - if (restart_time) { - return utc_time - restart_time; + if (Rtc.restart_time) { + return Rtc.utc_time - Rtc.restart_time; } else { return uptime; } @@ -264,7 +306,7 @@ void BreakTime(uint32_t time_input, TIME_T &tm) strlcpy(tm.name_of_month, kMonthNames + (month *3), 4); tm.month = month + 1; // jan is month 1 tm.day_of_month = time + 1; // day of month - tm.valid = (time_input > 1451602800); // 2016-01-01 + tm.valid = (time_input > START_VALID_TIME); // 2016-01-01 } uint32_t MakeTime(TIME_T &tm) @@ -330,106 +372,98 @@ uint32_t RuleToTime(TimeRule r, int yr) return t; } -uint32_t UtcTime(void) -{ - return utc_time; -} - -uint32_t LocalTime(void) -{ - return local_time; -} - -uint32_t Midnight(void) -{ - return midnight; -} - -bool MidnightNow(void) -{ - bool mnflg = midnight_now; - if (mnflg) midnight_now = 0; - return mnflg; -} - void RtcSecond(void) { TIME_T tmpTime; - if ((ntp_sync_minute > 59) && (RtcTime.minute > 2)) ntp_sync_minute = 1; // If sync prepare for a new cycle - uint8_t offset = (uptime < 30) ? RtcTime.second : (((ESP.getChipId() & 0xF) * 3) + 3) ; // First try ASAP to sync. If fails try once every 60 seconds based on chip id - if (!global_state.wifi_down && (offset == RtcTime.second) && ((RtcTime.year < 2016) || (ntp_sync_minute == RtcTime.minute) || ntp_force_sync)) { - ntp_time = sntp_get_current_timestamp(); - if (ntp_time > 1451602800) { // Fix NTP bug in core 2.4.1/SDK 2.2.1 (returns Thu Jan 01 08:00:10 1970 after power on) - ntp_force_sync = false; - if (utc_time > 1451602800) { drift_time = ntp_time - utc_time; } - utc_time = ntp_time; - ntp_sync_minute = 60; // Sync so block further requests - if (restart_time == 0) { - restart_time = utc_time - uptime; // save first ntp time as restart time - } - BreakTime(utc_time, tmpTime); - RtcTime.year = tmpTime.year + 1970; - daylight_saving_time = RuleToTime(Settings.tflag[1], RtcTime.year); - standard_time = RuleToTime(Settings.tflag[0], RtcTime.year); + if (!Rtc.user_time_entry) { + if ((Rtc.ntp_sync_minute > 59) && (RtcTime.minute > 2)) Rtc.ntp_sync_minute = 1; // If sync prepare for a new cycle + uint8_t offset = (uptime < 30) ? RtcTime.second : (((ESP.getChipId() & 0xF) * 3) + 3) ; // First try ASAP to sync. If fails try once every 60 seconds based on chip id + if (!global_state.wifi_down && (((offset == RtcTime.second) && ((RtcTime.year < 2016) || (Rtc.ntp_sync_minute == RtcTime.minute))) || ntp_force_sync)) { + Rtc.ntp_time = sntp_get_current_timestamp(); + if (Rtc.ntp_time > START_VALID_TIME) { // Fix NTP bug in core 2.4.1/SDK 2.2.1 (returns Thu Jan 01 08:00:10 1970 after power on) + ntp_force_sync = false; + if (Rtc.utc_time > START_VALID_TIME) { Rtc.drift_time = Rtc.ntp_time - Rtc.utc_time; } + Rtc.utc_time = Rtc.ntp_time; + Rtc.ntp_sync_minute = 60; // Sync so block further requests + if (Rtc.restart_time == 0) { + Rtc.restart_time = Rtc.utc_time - uptime; // save first ntp time as restart time + } + BreakTime(Rtc.utc_time, tmpTime); + RtcTime.year = tmpTime.year + 1970; + Rtc.daylight_saving_time = RuleToTime(Settings.tflag[1], RtcTime.year); + Rtc.standard_time = RuleToTime(Settings.tflag[0], RtcTime.year); - // Do not use AddLog here if syslog is enabled. UDP will force exception 9 -// AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION "(" D_UTC_TIME ") %s, (" D_DST_TIME ") %s, (" D_STD_TIME ") %s"), GetTime(0).c_str(), GetTime(2).c_str(), GetTime(3).c_str()); - ntp_synced_message = true; + // Do not use AddLog here if syslog is enabled. UDP will force exception 9 + // AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION "(" D_UTC_TIME ") %s, (" D_DST_TIME ") %s, (" D_STD_TIME ") %s"), GetTime(0).c_str(), GetTime(2).c_str(), GetTime(3).c_str()); + ntp_synced_message = true; - if (local_time < 1451602800) { // 2016-01-01 - rules_flag.time_init = 1; + if (Rtc.local_time < START_VALID_TIME) { // 2016-01-01 + rules_flag.time_init = 1; + } else { + rules_flag.time_set = 1; + } } else { - rules_flag.time_set = 1; + Rtc.ntp_sync_minute++; // Try again in next minute } - } else { - ntp_sync_minute++; // Try again in next minute } } - utc_time++; - local_time = utc_time; - if (local_time > 1451602800) { // 2016-01-01 + Rtc.utc_time++; + Rtc.local_time = Rtc.utc_time; + if (Rtc.local_time > START_VALID_TIME) { // 2016-01-01 int16_t timezone_minutes = Settings.timezone_minutes; if (Settings.timezone < 0) { timezone_minutes *= -1; } - time_timezone = (Settings.timezone * SECS_PER_HOUR) + (timezone_minutes * SECS_PER_MIN); + Rtc.time_timezone = (Settings.timezone * SECS_PER_HOUR) + (timezone_minutes * SECS_PER_MIN); if (99 == Settings.timezone) { int32_t dstoffset = Settings.toffset[1] * SECS_PER_MIN; int32_t stdoffset = Settings.toffset[0] * SECS_PER_MIN; if (Settings.tflag[1].hemis) { // Southern hemisphere - if ((utc_time >= (standard_time - dstoffset)) && (utc_time < (daylight_saving_time - stdoffset))) { - time_timezone = stdoffset; // Standard Time + if ((Rtc.utc_time >= (Rtc.standard_time - dstoffset)) && (Rtc.utc_time < (Rtc.daylight_saving_time - stdoffset))) { + Rtc.time_timezone = stdoffset; // Standard Time } else { - time_timezone = dstoffset; // Daylight Saving Time + Rtc.time_timezone = dstoffset; // Daylight Saving Time } } else { // Northern hemisphere - if ((utc_time >= (daylight_saving_time - stdoffset)) && (utc_time < (standard_time - dstoffset))) { - time_timezone = dstoffset; // Daylight Saving Time + if ((Rtc.utc_time >= (Rtc.daylight_saving_time - stdoffset)) && (Rtc.utc_time < (Rtc.standard_time - dstoffset))) { + Rtc.time_timezone = dstoffset; // Daylight Saving Time } else { - time_timezone = stdoffset; // Standard Time + Rtc.time_timezone = stdoffset; // Standard Time } } } - local_time += time_timezone; - time_timezone /= 60; - if (!Settings.energy_kWhtotal_time) { Settings.energy_kWhtotal_time = local_time; } + Rtc.local_time += Rtc.time_timezone; + Rtc.time_timezone /= 60; + if (!Settings.energy_kWhtotal_time) { Settings.energy_kWhtotal_time = Rtc.local_time; } } - BreakTime(local_time, RtcTime); + BreakTime(Rtc.local_time, RtcTime); if (RtcTime.valid) { - if (!midnight) { - midnight = local_time - (RtcTime.hour * 3600) - (RtcTime.minute * 60) - RtcTime.second; + if (!Rtc.midnight) { + Rtc.midnight = Rtc.local_time - (RtcTime.hour * 3600) - (RtcTime.minute * 60) - RtcTime.second; } if (!RtcTime.hour && !RtcTime.minute && !RtcTime.second) { - midnight = local_time; - midnight_now = 1; + Rtc.midnight = Rtc.local_time; + Rtc.midnight_now = true; } } RtcTime.year += 1970; } +void RtcSetTime(uint32_t epoch) +{ + if (epoch < START_VALID_TIME) { // 2016-01-01 + Rtc.user_time_entry = false; + ntp_force_sync = true; + } else { + Rtc.user_time_entry = true; + Rtc.utc_time = epoch -1; // Will be corrected by RtcSecond + } + RtcSecond(); +} + void RtcInit(void) { sntp_setservername(0, Settings.ntp_server[0]); @@ -438,7 +472,7 @@ void RtcInit(void) sntp_stop(); sntp_set_timezone(0); // UTC time sntp_init(); - utc_time = 0; - BreakTime(utc_time, RtcTime); + Rtc.utc_time = 0; + BreakTime(Rtc.utc_time, RtcTime); TickerRtc.attach(1, RtcSecond); } diff --git a/sonoff/support_static_buffer.ino b/sonoff/support_static_buffer.ino new file mode 100644 index 000000000..9d1cb1031 --- /dev/null +++ b/sonoff/support_static_buffer.ino @@ -0,0 +1,212 @@ +/* + support_buffer.ino - Static binary buffer for Zigbee + + Copyright (C) 2019 Theo Arends and Stephan Hadinger + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +typedef struct SBuffer_impl { + uint16_t size; // size in bytes of the buffer + uint16_t len; // current size of the data in buffer. Invariant: len <= size + uint8_t buf[]; // the actual data +} SBuffer_impl; + + + +typedef class SBuffer { + +protected: + SBuffer(void) { + // unused empty constructor except from subclass + } + +public: + SBuffer(const size_t size) { + _buf = (SBuffer_impl*) new char[size+4]; // add 4 bytes for size and len + _buf->size = size; + _buf->len = 0; + //*((uint32_t*)_buf) = size; // writing both size and len=0 in a single 32 bits write + } + + inline size_t getSize(void) const { return _buf->size; } + inline size_t size(void) const { return _buf->size; } + inline size_t getLen(void) const { return _buf->len; } + inline size_t len(void) const { return _buf->len; } + inline uint8_t *getBuffer(void) const { return _buf->buf; } + inline uint8_t *buf(size_t i = 0) const { return &_buf->buf[i]; } + inline char *charptr(size_t i = 0) const { return (char*) &_buf->buf[i]; } + + virtual ~SBuffer(void) { + delete[] _buf; + } + + inline void setLen(const size_t len) { + uint16_t old_len = _buf->len; + _buf->len = (len <= _buf->size) ? len : _buf->size; + if (old_len < _buf->len) { + memset((void*) &_buf->buf[old_len], 0, _buf->len - old_len); + } + } + + void set8(const size_t offset, const uint8_t data) { + if (offset < _buf->len) { + _buf->buf[offset] = data; + } + } + + size_t add8(const uint8_t data) { // append 8 bits value + if (_buf->len < _buf->size) { // do we have room for 1 byte + _buf->buf[_buf->len++] = data; + } + return _buf->len; + } + size_t add16(const uint16_t data) { // append 16 bits value + if (_buf->len < _buf->size - 1) { // do we have room for 2 bytes + _buf->buf[_buf->len++] = data; + _buf->buf[_buf->len++] = data >> 8; + } + return _buf->len; + } + size_t add32(const uint32_t data) { // append 32 bits value + if (_buf->len < _buf->size - 3) { // do we have room for 2 bytes + _buf->buf[_buf->len++] = data; + _buf->buf[_buf->len++] = data >> 8; + _buf->buf[_buf->len++] = data >> 16; + _buf->buf[_buf->len++] = data >> 24; + } + return _buf->len; + } + + size_t addBuffer(const SBuffer &buf2) { + if (len() + buf2.len() <= size()) { + for (uint32_t i = 0; i < buf2.len(); i++) { + _buf->buf[_buf->len++] = buf2.buf()[i]; + } + } + return _buf->len; + } + + size_t addBuffer(const uint8_t *buf2, size_t len2) { + if (len() + len2 <= size()) { + for (uint32_t i = 0; i < len2; i++) { + _buf->buf[_buf->len++] = pgm_read_byte(&buf2[i]); + } + } + return _buf->len; + } + + size_t addBuffer(const char *buf2, size_t len2) { + if (len() + len2 <= size()) { + for (uint32_t i = 0; i < len2; i++) { + _buf->buf[_buf->len++] = pgm_read_byte(&buf2[i]); + } + } + return _buf->len; + } + + uint8_t get8(size_t offset) const { + if (offset < _buf->len) { + return _buf->buf[offset]; + } else { + return 0; + } + } + uint8_t read8(const size_t offset) const { + if (offset < len()) { + return _buf->buf[offset]; + } + return 0; + } + uint16_t get16(const size_t offset) const { + if (offset < len() - 1) { + return _buf->buf[offset] | (_buf->buf[offset+1] << 8); + } + return 0; + } + uint32_t get32(const size_t offset) const { + if (offset < len() - 3) { + return _buf->buf[offset] | (_buf->buf[offset+1] << 8) | + (_buf->buf[offset+2] << 16) | (_buf->buf[offset+3] << 24); + } + return 0; + } + uint64_t get64(const size_t offset) const { + if (offset < len() - 7) { + return (uint64_t)_buf->buf[offset] | ((uint64_t)_buf->buf[offset+1] << 8) | + ((uint64_t)_buf->buf[offset+2] << 16) | ((uint64_t)_buf->buf[offset+3] << 24) | + ((uint64_t)_buf->buf[offset+4] << 32) | ((uint64_t)_buf->buf[offset+5] << 40) | + ((uint64_t)_buf->buf[offset+6] << 48) | ((uint64_t)_buf->buf[offset+7] << 56); + } + return 0; + } + + SBuffer subBuffer(const size_t start, size_t len) const { + if (start >= _buf->len) { + len = 0; + } else if (start + len > _buf->len) { + len = _buf->len - start; + } + + SBuffer buf2(len); + memcpy(buf2.buf(), buf()+start, len); + buf2._buf->len = len; + return buf2; + } + + static SBuffer SBufferFromHex(const char *hex, size_t len) { + size_t buf_len = (len + 3) / 2; + SBuffer buf2(buf_len); + uint8_t val; + + for (; len > 1; len -= 2) { + val = asc2byte(*hex++) << 4; + val |= asc2byte(*hex++); + buf2.add8(val); + } + return buf2; + } + +protected: + + static uint8_t asc2byte(char chr) { + uint8_t rVal = 0; + if (isdigit(chr)) { rVal = chr - '0'; } + else if (chr >= 'A' && chr <= 'F') { rVal = chr + 10 - 'A'; } + else if (chr >= 'a' && chr <= 'f') { rVal = chr + 10 - 'a'; } + return rVal; + } + + static void unHex(const char* in, uint8_t *out, size_t len) { + } + +protected: + SBuffer_impl * _buf; + +} SBuffer; + +typedef class PreAllocatedSBuffer : public SBuffer { + +public: + PreAllocatedSBuffer(const size_t size, void * buffer) { + _buf = (SBuffer_impl*) buffer; + _buf->size = size - 4; + _buf->len = 0; + } + + ~PreAllocatedSBuffer(void) { + // don't deallocate + _buf = nullptr; + } +} PreAllocatedSBuffer; diff --git a/sonoff/support_switch.ino b/sonoff/support_switch.ino index ca1d486fa..940fcdb1d 100644 --- a/sonoff/support_switch.ino +++ b/sonoff/support_switch.ino @@ -31,34 +31,36 @@ const uint8_t SWITCH_PROBE_INTERVAL = 10; // Time in milliseconds between swit Ticker TickerSwitch; -unsigned long switch_debounce = 0; // Switch debounce timer -uint16_t switch_no_pullup = 0; // Switch pull-up bitmask flags -uint8_t switch_state_buf[MAX_SWITCHES] = { 0 }; -uint8_t lastwallswitch[MAX_SWITCHES]; // Last wall switch states -uint8_t holdwallswitch[MAX_SWITCHES] = { 0 }; // Timer for wallswitch push button hold -uint8_t switch_virtual[MAX_SWITCHES]; // Virtual switch states -uint8_t switches_found = 0; +struct SWITCH { + unsigned long debounce = 0; // Switch debounce timer + uint16_t no_pullup_mask = 0; // Switch pull-up bitmask flags + uint8_t state[MAX_SWITCHES] = { 0 }; + uint8_t last_state[MAX_SWITCHES]; // Last wall switch states + uint8_t hold_timer[MAX_SWITCHES] = { 0 }; // Timer for wallswitch push button hold + uint8_t virtual_state[MAX_SWITCHES]; // Virtual switch states + uint8_t present = 0; +} Switch; /********************************************************************************************/ void SwitchPullupFlag(uint16 switch_bit) { - bitSet(switch_no_pullup, switch_bit); + bitSet(Switch.no_pullup_mask, switch_bit); } uint8_t SwitchLastState(uint8_t index) { - return lastwallswitch[index]; + return Switch.last_state[index]; } void SwitchSetVirtual(uint8_t index, uint8_t state) { - switch_virtual[index] = state; + Switch.virtual_state[index] = state; } uint8_t SwitchGetVirtual(uint8_t index) { - return switch_virtual[index]; + return Switch.virtual_state[index]; } /*********************************************************************************************/ @@ -77,29 +79,29 @@ void SwitchProbe(void) if (1 == digitalRead(pin[GPIO_SWT1 +i])) { if (force_high) { // Enabled with SwitchDebounce x1 - if (1 == switch_virtual[i]) { - switch_state_buf[i] = state_filter; // With noisy input keep current state 1 unless constant 0 + if (1 == Switch.virtual_state[i]) { + Switch.state[i] = state_filter; // With noisy input keep current state 1 unless constant 0 } } - if (switch_state_buf[i] < state_filter) { - switch_state_buf[i]++; - if (state_filter == switch_state_buf[i]) { - switch_virtual[i] = 1; + if (Switch.state[i] < state_filter) { + Switch.state[i]++; + if (state_filter == Switch.state[i]) { + Switch.virtual_state[i] = 1; } } } else { if (force_low) { // Enabled with SwitchDebounce x2 - if (0 == switch_virtual[i]) { - switch_state_buf[i] = 0; // With noisy input keep current state 0 unless constant 1 + if (0 == Switch.virtual_state[i]) { + Switch.state[i] = 0; // With noisy input keep current state 0 unless constant 1 } } - if (switch_state_buf[i] > 0) { - switch_state_buf[i]--; - if (0 == switch_state_buf[i]) { - switch_virtual[i] = 0; + if (Switch.state[i] > 0) { + Switch.state[i]--; + if (0 == Switch.state[i]) { + Switch.virtual_state[i] = 0; } } } @@ -110,17 +112,17 @@ void SwitchProbe(void) void SwitchInit(void) { - switches_found = 0; + Switch.present = 0; for (uint32_t i = 0; i < MAX_SWITCHES; i++) { - lastwallswitch[i] = 1; // Init global to virtual switch state; + Switch.last_state[i] = 1; // Init global to virtual switch state; if (pin[GPIO_SWT1 +i] < 99) { - switches_found++; - pinMode(pin[GPIO_SWT1 +i], bitRead(switch_no_pullup, i) ? INPUT : ((16 == pin[GPIO_SWT1 +i]) ? INPUT_PULLDOWN_16 : INPUT_PULLUP)); - lastwallswitch[i] = digitalRead(pin[GPIO_SWT1 +i]); // Set global now so doesn't change the saved power state on first switch check + Switch.present++; + pinMode(pin[GPIO_SWT1 +i], bitRead(Switch.no_pullup_mask, i) ? INPUT : ((16 == pin[GPIO_SWT1 +i]) ? INPUT_PULLDOWN_16 : INPUT_PULLUP)); + Switch.last_state[i] = digitalRead(pin[GPIO_SWT1 +i]); // Set global now so doesn't change the saved power state on first switch check } - switch_virtual[i] = lastwallswitch[i]; + Switch.virtual_state[i] = Switch.last_state[i]; } - if (switches_found) { TickerSwitch.attach_ms(SWITCH_PROBE_INTERVAL, SwitchProbe); } + if (Switch.present) { TickerSwitch.attach_ms(SWITCH_PROBE_INTERVAL, SwitchProbe); } } /*********************************************************************************************\ @@ -138,22 +140,22 @@ void SwitchHandler(uint8_t mode) for (uint32_t i = 0; i < MAX_SWITCHES; i++) { if ((pin[GPIO_SWT1 +i] < 99) || (mode)) { - if (holdwallswitch[i]) { - holdwallswitch[i]--; - if (0 == holdwallswitch[i]) { - SendKey(1, i +1, 3); // Execute command via MQTT + if (Switch.hold_timer[i]) { + Switch.hold_timer[i]--; + if (0 == Switch.hold_timer[i]) { + SendKey(KEY_SWITCH, i +1, POWER_HOLD); // Execute command via MQTT } } - button = switch_virtual[i]; + button = Switch.virtual_state[i]; // enum SwitchModeOptions {TOGGLE, FOLLOW, FOLLOW_INV, PUSHBUTTON, PUSHBUTTON_INV, PUSHBUTTONHOLD, PUSHBUTTONHOLD_INV, PUSHBUTTON_TOGGLE, MAX_SWITCH_OPTION}; - if (button != lastwallswitch[i]) { - switchflag = 3; + if (button != Switch.last_state[i]) { + switchflag = POWER_TOGGLE +1; switch (Settings.switchmode[i]) { case TOGGLE: - switchflag = 2; // Toggle + switchflag = POWER_TOGGLE; // Toggle break; case FOLLOW: switchflag = button &1; // Follow wall switch state @@ -162,47 +164,47 @@ void SwitchHandler(uint8_t mode) switchflag = ~button &1; // Follow inverted wall switch state break; case PUSHBUTTON: - if ((PRESSED == button) && (NOT_PRESSED == lastwallswitch[i])) { - switchflag = 2; // Toggle with pushbutton to Gnd + if ((PRESSED == button) && (NOT_PRESSED == Switch.last_state[i])) { + switchflag = POWER_TOGGLE; // Toggle with pushbutton to Gnd } break; case PUSHBUTTON_INV: - if ((NOT_PRESSED == button) && (PRESSED == lastwallswitch[i])) { - switchflag = 2; // Toggle with releasing pushbutton from Gnd + if ((NOT_PRESSED == button) && (PRESSED == Switch.last_state[i])) { + switchflag = POWER_TOGGLE; // Toggle with releasing pushbutton from Gnd } break; case PUSHBUTTON_TOGGLE: - if (button != lastwallswitch[i]) { - switchflag = 2; // Toggle with any pushbutton change + if (button != Switch.last_state[i]) { + switchflag = POWER_TOGGLE; // Toggle with any pushbutton change } break; case PUSHBUTTONHOLD: - if ((PRESSED == button) && (NOT_PRESSED == lastwallswitch[i])) { - holdwallswitch[i] = loops_per_second * Settings.param[P_HOLD_TIME] / 10; + if ((PRESSED == button) && (NOT_PRESSED == Switch.last_state[i])) { + Switch.hold_timer[i] = loops_per_second * Settings.param[P_HOLD_TIME] / 10; } - if ((NOT_PRESSED == button) && (PRESSED == lastwallswitch[i]) && (holdwallswitch[i])) { - holdwallswitch[i] = 0; - switchflag = 2; // Toggle with pushbutton to Gnd + if ((NOT_PRESSED == button) && (PRESSED == Switch.last_state[i]) && (Switch.hold_timer[i])) { + Switch.hold_timer[i] = 0; + switchflag = POWER_TOGGLE; // Toggle with pushbutton to Gnd } break; case PUSHBUTTONHOLD_INV: - if ((NOT_PRESSED == button) && (PRESSED == lastwallswitch[i])) { - holdwallswitch[i] = loops_per_second * Settings.param[P_HOLD_TIME] / 10; + if ((NOT_PRESSED == button) && (PRESSED == Switch.last_state[i])) { + Switch.hold_timer[i] = loops_per_second * Settings.param[P_HOLD_TIME] / 10; } - if ((PRESSED == button) && (NOT_PRESSED == lastwallswitch[i]) && (holdwallswitch[i])) { - holdwallswitch[i] = 0; - switchflag = 2; // Toggle with pushbutton to Gnd + if ((PRESSED == button) && (NOT_PRESSED == Switch.last_state[i]) && (Switch.hold_timer[i])) { + Switch.hold_timer[i] = 0; + switchflag = POWER_TOGGLE; // Toggle with pushbutton to Gnd } break; } - if (switchflag < 3) { - if (!SendKey(1, i +1, switchflag)) { // Execute command via MQTT + if (switchflag <= POWER_TOGGLE) { + if (!SendKey(KEY_SWITCH, i +1, switchflag)) { // Execute command via MQTT ExecuteCommandPower(i +1, switchflag, SRC_SWITCH); // Execute command internally (if i < devices_present) } } - lastwallswitch[i] = button; + Switch.last_state[i] = button; } } } @@ -210,9 +212,9 @@ void SwitchHandler(uint8_t mode) void SwitchLoop(void) { - if (switches_found) { - if (TimeReached(switch_debounce)) { - SetNextTimeInterval(switch_debounce, Settings.switch_debounce); + if (Switch.present) { + if (TimeReached(Switch.debounce)) { + SetNextTimeInterval(Switch.debounce, Settings.switch_debounce); SwitchHandler(0); } } diff --git a/sonoff/support_udp.ino b/sonoff/support_udp.ino index eccc4681d..a33ca6973 100644 --- a/sonoff/support_udp.ino +++ b/sonoff/support_udp.ino @@ -36,6 +36,7 @@ bool udp_response_mutex = false; // M-Search response mutex to control r \*********************************************************************************************/ const char URN_BELKIN_DEVICE[] PROGMEM = "urn:belkin:device:**"; +const char URN_BELKIN_DEVICE_CAP[] PROGMEM = "urn:Belkin:device:**"; const char UPNP_ROOTDEVICE[] PROGMEM = "upnp:rootdevice"; const char SSDPSEARCH_ALL[] PROGMEM = "ssdpsearch:all"; const char SSDP_ALL[] PROGMEM = "ssdp:all"; @@ -84,7 +85,11 @@ void PollUdp(void) // AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("\n%s"), packet_buffer); // Simple Service Discovery Protocol (SSDP) +#ifdef USE_SCRIPT_HUE + if (!udp_response_mutex && (strstr_P(packet_buffer, PSTR("M-SEARCH")) != nullptr)) { +#else if (devices_present && !udp_response_mutex && (strstr_P(packet_buffer, PSTR("M-SEARCH")) != nullptr)) { +#endif udp_response_mutex = true; udp_remote_ip = PortUdp.remoteIP(); diff --git a/sonoff/support_wifi.ino b/sonoff/support_wifi.ino index 08b8a36e2..a0f4bd3e5 100644 --- a/sonoff/support_wifi.ino +++ b/sonoff/support_wifi.ino @@ -32,37 +32,25 @@ const uint8_t WIFI_CONFIG_SEC = 180; // seconds before restart const uint8_t WIFI_CHECK_SEC = 20; // seconds const uint8_t WIFI_RETRY_OFFSET_SEC = 20; // seconds -/* -// This worked for 2_5_0_BETA2 but fails since then. Waiting for a solution from core team (#4952) -#ifdef USE_MQTT_TLS -#if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_1) || defined(ARDUINO_ESP8266_RELEASE_2_4_2) -#else -#define USING_AXTLS -#include -// force use of AxTLS (BearSSL is now default) which uses less memory (#4952) -#include -using namespace axTLS; -#endif // ARDUINO_ESP8266_RELEASE prior to 2_5_0 -#else -#include // Wifi, MQTT, Ota, WifiManager -#endif // USE_MQTT_TLS -*/ -#include // Wifi, MQTT, Ota, WifiManager +#include // Wifi, MQTT, Ota, WifiManager +#if LWIP_IPV6 +#include // IPv6 DualStack +#endif // LWIP_IPV6=1 -uint32_t wifi_last_event = 0; // Last wifi connection event -uint32_t wifi_downtime = 0; // Wifi down duration -uint16_t wifi_link_count = 0; // Number of wifi re-connect -uint8_t wifi_counter; -uint8_t wifi_retry_init; -uint8_t wifi_retry; -uint8_t wifi_status; -uint8_t wps_result; -uint8_t wifi_config_type = 0; -uint8_t wifi_config_counter = 0; -uint8_t mdns_begun = 0; // mDNS active - -uint8_t wifi_scan_state; -uint8_t wifi_bssid[6]; +struct WIFI { + uint32_t last_event = 0; // Last wifi connection event + uint32_t downtime = 0; // Wifi down duration + uint16_t link_count = 0; // Number of wifi re-connect + uint8_t counter; + uint8_t retry_init; + uint8_t retry; + uint8_t status; + uint8_t config_type = 0; + uint8_t config_counter = 0; + uint8_t mdns_begun = 0; // mDNS active + uint8_t scan_state; + uint8_t bssid[6]; +} Wifi; int WifiGetRssiAsQuality(int rssi) { @@ -80,103 +68,41 @@ int WifiGetRssiAsQuality(int rssi) bool WifiConfigCounter(void) { - if (wifi_config_counter) { - wifi_config_counter = WIFI_CONFIG_SEC; + if (Wifi.config_counter) { + Wifi.config_counter = WIFI_CONFIG_SEC; } - return (wifi_config_counter); -} - -extern "C" { -#include "user_interface.h" -} - -void WifiWpsStatusCallback(wps_cb_status status); - -void WifiWpsStatusCallback(wps_cb_status status) -{ -/* from user_interface.h: - enum wps_cb_status { - WPS_CB_ST_SUCCESS = 0, - WPS_CB_ST_FAILED, - WPS_CB_ST_TIMEOUT, - WPS_CB_ST_WEP, // WPS failed because that WEP is not supported - WPS_CB_ST_SCAN_ERR, // can not find the target WPS AP - }; -*/ - wps_result = status; - if (WPS_CB_ST_SUCCESS == wps_result) { - wifi_wps_disable(); - } else { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI D_WPS_FAILED_WITH_STATUS " %d"), wps_result); - wifi_config_counter = 2; - } -} - -bool WifiWpsConfigDone(void) -{ - return (!wps_result); -} - -bool WifiWpsConfigBegin(void) -{ - wps_result = 99; - if (!wifi_wps_disable()) { return false; } - if (!wifi_wps_enable(WPS_TYPE_PBC)) { return false; } // so far only WPS_TYPE_PBC is supported (SDK 2.0.0) - if (!wifi_set_wps_cb((wps_st_cb_t) &WifiWpsStatusCallback)) { return false; } - if (!wifi_wps_start()) { return false; } - return true; + return (Wifi.config_counter); } void WifiConfig(uint8_t type) { - if (!wifi_config_type) { + if (!Wifi.config_type) { if ((WIFI_RETRY == type) || (WIFI_WAIT == type)) { return; } #ifdef USE_EMULATION UdpDisconnect(); #endif // USE_EMULATION WiFi.disconnect(); // Solve possible Wifi hangs - wifi_config_type = type; + Wifi.config_type = type; -#ifndef USE_WPS - if (WIFI_WPSCONFIG == wifi_config_type) { wifi_config_type = WIFI_MANAGER; } -#endif // USE_WPS #ifndef USE_WEBSERVER - if (WIFI_MANAGER == wifi_config_type) { wifi_config_type = WIFI_SMARTCONFIG; } + if (WIFI_MANAGER == Wifi.config_type) { + Wifi.config_type = WIFI_SERIAL; + } #endif // USE_WEBSERVER -#ifndef USE_SMARTCONFIG - if (WIFI_SMARTCONFIG == wifi_config_type) { wifi_config_type = WIFI_SERIAL; } -#endif // USE_SMARTCONFIG - wifi_config_counter = WIFI_CONFIG_SEC; // Allow up to WIFI_CONFIG_SECS seconds for phone to provide ssid/pswd - wifi_counter = wifi_config_counter +5; + Wifi.config_counter = WIFI_CONFIG_SEC; // Allow up to WIFI_CONFIG_SECS seconds for phone to provide ssid/pswd + Wifi.counter = Wifi.config_counter +5; blinks = 1999; - if (WIFI_RESTART == wifi_config_type) { + if (WIFI_RESTART == Wifi.config_type) { restart_flag = 2; } - else if (WIFI_SERIAL == wifi_config_type) { + else if (WIFI_SERIAL == Wifi.config_type) { AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_WCFG_6_SERIAL " " D_ACTIVE_FOR_3_MINUTES)); } -#ifdef USE_SMARTCONFIG - else if (WIFI_SMARTCONFIG == wifi_config_type) { - AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_WCFG_1_SMARTCONFIG " " D_ACTIVE_FOR_3_MINUTES)); - WiFi.mode(WIFI_STA); // Disable AP mode - WiFi.beginSmartConfig(); - } -#endif // USE_SMARTCONFIG -#ifdef USE_WPS - else if (WIFI_WPSCONFIG == wifi_config_type) { - if (WifiWpsConfigBegin()) { - AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_WCFG_3_WPSCONFIG " " D_ACTIVE_FOR_3_MINUTES)); - } else { - AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_WCFG_3_WPSCONFIG " " D_FAILED_TO_START)); - wifi_config_counter = 3; - } - } -#endif // USE_WPS #ifdef USE_WEBSERVER - else if (WIFI_MANAGER == wifi_config_type || WIFI_MANAGER_RESET_ONLY == wifi_config_type) { + else if (WIFI_MANAGER == Wifi.config_type || WIFI_MANAGER_RESET_ONLY == Wifi.config_type) { AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_WCFG_2_WIFIMANAGER " " D_ACTIVE_FOR_3_MINUTES)); - WifiManagerBegin(WIFI_MANAGER_RESET_ONLY == wifi_config_type); + WifiManagerBegin(WIFI_MANAGER_RESET_ONLY == Wifi.config_type); } #endif // USE_WEBSERVER } @@ -243,12 +169,26 @@ void WifiBegin(uint8_t flag, uint8_t channel) } WiFi.hostname(my_hostname); if (channel) { - WiFi.begin(Settings.sta_ssid[Settings.sta_active], Settings.sta_pwd[Settings.sta_active], channel, wifi_bssid); + WiFi.begin(Settings.sta_ssid[Settings.sta_active], Settings.sta_pwd[Settings.sta_active], channel, Wifi.bssid); } else { WiFi.begin(Settings.sta_ssid[Settings.sta_active], Settings.sta_pwd[Settings.sta_active]); } AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_CONNECTING_TO_AP "%d %s " D_IN_MODE " 11%c " D_AS " %s..."), Settings.sta_active +1, Settings.sta_ssid[Settings.sta_active], kWifiPhyMode[WiFi.getPhyMode() & 0x3], my_hostname); + +#if LWIP_IPV6 + for (bool configured = false; !configured;) { + uint16_t cfgcnt = 0; + for (auto addr : addrList) { + if ((configured = !addr.isLocal() && addr.isV6()) || cfgcnt==30) { + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI "Got IPv6 global address %s"), addr.toString().c_str()); + break; // IPv6 is mandatory but stop after 15 seconds + } + delay(500); // Loop until real IPv6 address is aquired or too many tries failed + cfgcnt++; + } + } +#endif // LWIP_IPV6=1 } void WifiBeginAfterScan() @@ -256,43 +196,43 @@ void WifiBeginAfterScan() static int8_t best_network_db; // Not active - if (0 == wifi_scan_state) { return; } + if (0 == Wifi.scan_state) { return; } // Init scan when not connected - if (1 == wifi_scan_state) { - memset((void*) &wifi_bssid, 0, sizeof(wifi_bssid)); + if (1 == Wifi.scan_state) { + memset((void*) &Wifi.bssid, 0, sizeof(Wifi.bssid)); best_network_db = -127; - wifi_scan_state = 3; + Wifi.scan_state = 3; } // Init scan when connected - if (2 == wifi_scan_state) { + if (2 == Wifi.scan_state) { uint8_t* bssid = WiFi.BSSID(); // Get current bssid - memcpy((void*) &wifi_bssid, (void*) bssid, sizeof(wifi_bssid)); + memcpy((void*) &Wifi.bssid, (void*) bssid, sizeof(Wifi.bssid)); best_network_db = WiFi.RSSI(); // Get current rssi and add threshold if (best_network_db < -WIFI_RSSI_THRESHOLD) { best_network_db += WIFI_RSSI_THRESHOLD; } - wifi_scan_state = 3; + Wifi.scan_state = 3; } // Init scan - if (3 == wifi_scan_state) { + if (3 == Wifi.scan_state) { if (WiFi.scanComplete() != WIFI_SCAN_RUNNING) { WiFi.scanNetworks(true); // Start wifi scan async - wifi_scan_state++; + Wifi.scan_state++; AddLog_P(LOG_LEVEL_DEBUG, S_LOG_WIFI, PSTR("Network (re)scan started...")); return; } } int8_t wifi_scan_result = WiFi.scanComplete(); // Check scan done - if (4 == wifi_scan_state) { + if (4 == Wifi.scan_state) { if (wifi_scan_result != WIFI_SCAN_RUNNING) { - wifi_scan_state++; + Wifi.scan_state++; } } // Scan done - if (5 == wifi_scan_state) { + if (5 == Wifi.scan_state) { int32_t channel = 0; // No scan result int8_t ap = 3; // AP default if not found uint8_t last_bssid[6]; // Save last bssid - memcpy((void*) &last_bssid, (void*) &wifi_bssid, sizeof(last_bssid)); + memcpy((void*) &last_bssid, (void*) &Wifi.bssid, sizeof(last_bssid)); if (wifi_scan_result > 0) { // Networks found @@ -317,23 +257,30 @@ void WifiBeginAfterScan() best_network_db = (int8_t)rssi_scan; channel = chan_scan; ap = j; // AP1 or AP2 - memcpy((void*) &wifi_bssid, (void*) bssid_scan, sizeof(wifi_bssid)); + memcpy((void*) &Wifi.bssid, (void*) bssid_scan, sizeof(Wifi.bssid)); } } break; } } - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI "Network %d, AP%c, SSId %s, Channel %d, BSSId %02X:%02X:%02X:%02X:%02X:%02X, RSSI %d, Encryption %d"), - i, (known) ? (j) ? '2' : '1' : '-', ssid_scan.c_str(), chan_scan, bssid_scan[0], bssid_scan[1], bssid_scan[2], bssid_scan[3], bssid_scan[4], bssid_scan[5], rssi_scan, (sec_scan == ENC_TYPE_NONE) ? 0 : 1); + char hex_char[18]; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI "Network %d, AP%c, SSId %s, Channel %d, BSSId %s, RSSI %d, Encryption %d"), + i, + (known) ? (j) ? '2' : '1' : '-', + ssid_scan.c_str(), + chan_scan, + ToHex_P((unsigned char*)bssid_scan, 6, hex_char, sizeof(hex_char), ':'), + rssi_scan, + (sec_scan == ENC_TYPE_NONE) ? 0 : 1); delay(0); } WiFi.scanDelete(); // Clean up Ram delay(0); } - wifi_scan_state = 0; + Wifi.scan_state = 0; // If bssid changed then (re)connect wifi - for (uint32_t i = 0; i < sizeof(wifi_bssid); i++) { - if (last_bssid[i] != wifi_bssid[i]) { + for (uint32_t i = 0; i < sizeof(Wifi.bssid); i++) { + if (last_bssid[i] != Wifi.bssid[i]) { WifiBegin(ap, channel); // 0 (AP1), 1 (AP2) or 3 (default AP) break; } @@ -343,12 +290,12 @@ void WifiBeginAfterScan() uint16_t WifiLinkCount() { - return wifi_link_count; + return Wifi.link_count; } String WifiDowntime() { - return GetDuration(wifi_downtime); + return GetDuration(Wifi.downtime); } void WifiSetState(uint8_t state) @@ -356,33 +303,50 @@ void WifiSetState(uint8_t state) if (state == global_state.wifi_down) { if (state) { rules_flag.wifi_connected = 1; - wifi_link_count++; - wifi_downtime += UpTime() - wifi_last_event; + Wifi.link_count++; + Wifi.downtime += UpTime() - Wifi.last_event; } else { rules_flag.wifi_disconnected = 1; - wifi_last_event = UpTime(); + Wifi.last_event = UpTime(); } } global_state.wifi_down = state ^1; } +#if LWIP_IPV6 +bool WifiCheckIPv6(void) +{ + bool ipv6_global=false; + + for (auto a : addrList) { + if(!a.isLocal() && a.isV6()) ipv6_global=true; + } + return ipv6_global; +} +#endif // LWIP_IPV6=1 + void WifiCheckIp(void) { +#if LWIP_IPV6 + if(WifiCheckIPv6()) { + Wifi.status = WL_CONNECTED; +#else if ((WL_CONNECTED == WiFi.status()) && (static_cast(WiFi.localIP()) != 0)) { +#endif // LWIP_IPV6=1 WifiSetState(1); - wifi_counter = WIFI_CHECK_SEC; - wifi_retry = wifi_retry_init; - AddLog_P((wifi_status != WL_CONNECTED) ? LOG_LEVEL_INFO : LOG_LEVEL_DEBUG_MORE, S_LOG_WIFI, PSTR(D_CONNECTED)); - if (wifi_status != WL_CONNECTED) { + Wifi.counter = WIFI_CHECK_SEC; + Wifi.retry = Wifi.retry_init; + AddLog_P((Wifi.status != WL_CONNECTED) ? LOG_LEVEL_INFO : LOG_LEVEL_DEBUG_MORE, S_LOG_WIFI, PSTR(D_CONNECTED)); + if (Wifi.status != WL_CONNECTED) { // AddLog_P(LOG_LEVEL_INFO, PSTR("Wifi: Set IP addresses")); Settings.ip_address[1] = (uint32_t)WiFi.gatewayIP(); Settings.ip_address[2] = (uint32_t)WiFi.subnetMask(); Settings.ip_address[3] = (uint32_t)WiFi.dnsIP(); } - wifi_status = WL_CONNECTED; + Wifi.status = WL_CONNECTED; #ifdef USE_DISCOVERY #ifdef WEBSERVER_ADVERTISE - if (2 == mdns_begun) { + if (2 == Wifi.mdns_begun) { MDNS.update(); AddLog_P(LOG_LEVEL_DEBUG_MORE, D_LOG_MDNS, "MDNS.update"); } @@ -391,96 +355,84 @@ void WifiCheckIp(void) } else { WifiSetState(0); uint8_t wifi_config_tool = Settings.sta_config; - wifi_status = WiFi.status(); - switch (wifi_status) { + Wifi.status = WiFi.status(); + switch (Wifi.status) { case WL_CONNECTED: AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_CONNECT_FAILED_NO_IP_ADDRESS)); - wifi_status = 0; - wifi_retry = wifi_retry_init; + Wifi.status = 0; + Wifi.retry = Wifi.retry_init; break; case WL_NO_SSID_AVAIL: AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_CONNECT_FAILED_AP_NOT_REACHED)); if (WIFI_WAIT == Settings.sta_config) { - wifi_retry = wifi_retry_init; + Wifi.retry = Wifi.retry_init; } else { - if (wifi_retry > (wifi_retry_init / 2)) { - wifi_retry = wifi_retry_init / 2; + if (Wifi.retry > (Wifi.retry_init / 2)) { + Wifi.retry = Wifi.retry_init / 2; } - else if (wifi_retry) { - wifi_retry = 0; + else if (Wifi.retry) { + Wifi.retry = 0; } } break; case WL_CONNECT_FAILED: AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_CONNECT_FAILED_WRONG_PASSWORD)); - if (wifi_retry > (wifi_retry_init / 2)) { - wifi_retry = wifi_retry_init / 2; + if (Wifi.retry > (Wifi.retry_init / 2)) { + Wifi.retry = Wifi.retry_init / 2; } - else if (wifi_retry) { - wifi_retry = 0; + else if (Wifi.retry) { + Wifi.retry = 0; } break; default: // WL_IDLE_STATUS and WL_DISCONNECTED - if (!wifi_retry || ((wifi_retry_init / 2) == wifi_retry)) { + if (!Wifi.retry || ((Wifi.retry_init / 2) == Wifi.retry)) { AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_CONNECT_FAILED_AP_TIMEOUT)); } else { if (('\0' == Settings.sta_ssid[0][0]) && ('\0' == Settings.sta_ssid[1][0])) { - wifi_config_tool = WIFI_CONFIG_NO_SSID; // Skip empty SSIDs and start Wifi config tool - wifi_retry = 0; + wifi_config_tool = WIFI_MANAGER; // Skip empty SSIDs and start Wifi config tool + Wifi.retry = 0; } else { AddLog_P(LOG_LEVEL_DEBUG, S_LOG_WIFI, PSTR(D_ATTEMPTING_CONNECTION)); } } } - if (wifi_retry) { + if (Wifi.retry) { if (Settings.flag3.use_wifi_scan) { - if (wifi_retry_init == wifi_retry) { - wifi_scan_state = 1; // Select scanned SSID + if (Wifi.retry_init == Wifi.retry) { + Wifi.scan_state = 1; // Select scanned SSID } } else { - if (wifi_retry_init == wifi_retry) { + if (Wifi.retry_init == Wifi.retry) { WifiBegin(3, 0); // Select default SSID } - if ((Settings.sta_config != WIFI_WAIT) && ((wifi_retry_init / 2) == wifi_retry)) { + if ((Settings.sta_config != WIFI_WAIT) && ((Wifi.retry_init / 2) == Wifi.retry)) { WifiBegin(2, 0); // Select alternate SSID } } - wifi_counter = 1; - wifi_retry--; + Wifi.counter = 1; + Wifi.retry--; } else { WifiConfig(wifi_config_tool); - wifi_counter = 1; - wifi_retry = wifi_retry_init; + Wifi.counter = 1; + Wifi.retry = Wifi.retry_init; } } } void WifiCheck(uint8_t param) { - wifi_counter--; + Wifi.counter--; switch (param) { case WIFI_SERIAL: - case WIFI_SMARTCONFIG: case WIFI_MANAGER: - case WIFI_WPSCONFIG: WifiConfig(param); break; default: - if (wifi_config_counter) { - wifi_config_counter--; - wifi_counter = wifi_config_counter +5; - if (wifi_config_counter) { -#ifdef USE_SMARTCONFIG - if ((WIFI_SMARTCONFIG == wifi_config_type) && WiFi.smartConfigDone()) { - wifi_config_counter = 0; - } -#endif // USE_SMARTCONFIG -#ifdef USE_WPS - if ((WIFI_WPSCONFIG == wifi_config_type) && WifiWpsConfigDone()) { - wifi_config_counter = 0; - } -#endif // USE_WPS - if (!wifi_config_counter) { + if (Wifi.config_counter) { + Wifi.config_counter--; + Wifi.counter = Wifi.config_counter +5; + if (Wifi.config_counter) { + if (!Wifi.config_counter) { if (strlen(WiFi.SSID().c_str())) { strlcpy(Settings.sta_ssid[0], WiFi.SSID().c_str(), sizeof(Settings.sta_ssid[0])); } @@ -488,30 +440,31 @@ void WifiCheck(uint8_t param) strlcpy(Settings.sta_pwd[0], WiFi.psk().c_str(), sizeof(Settings.sta_pwd[0])); } Settings.sta_active = 0; - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_WCFG_1_SMARTCONFIG D_CMND_SSID "1 %s"), Settings.sta_ssid[0]); + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_WCFG_2_WIFIMANAGER D_CMND_SSID "1 %s"), Settings.sta_ssid[0]); } } - if (!wifi_config_counter) { -#ifdef USE_SMARTCONFIG - if (WIFI_SMARTCONFIG == wifi_config_type) { WiFi.stopSmartConfig(); } -#endif // USE_SMARTCONFIG + if (!Wifi.config_counter) { // SettingsSdkErase(); // Disabled v6.1.0b due to possible bad wifi connects restart_flag = 2; } } else { - if (wifi_scan_state) { WifiBeginAfterScan(); } + if (Wifi.scan_state) { WifiBeginAfterScan(); } - if (wifi_counter <= 0) { + if (Wifi.counter <= 0) { AddLog_P(LOG_LEVEL_DEBUG_MORE, S_LOG_WIFI, PSTR(D_CHECKING_CONNECTION)); - wifi_counter = WIFI_CHECK_SEC; + Wifi.counter = WIFI_CHECK_SEC; WifiCheckIp(); } - if ((WL_CONNECTED == WiFi.status()) && (static_cast(WiFi.localIP()) != 0) && !wifi_config_type) { +#if LWIP_IPV6 + if (WifiCheckIPv6()) { +#else + if ((WL_CONNECTED == WiFi.status()) && (static_cast(WiFi.localIP()) != 0) && !Wifi.config_type) { +#endif // LWIP_IPV6=1 WifiSetState(1); if (Settings.flag3.use_wifi_rescan) { if (!(uptime % (60 * WIFI_RESCAN_MINUTES))) { - wifi_scan_state = 2; + Wifi.scan_state = 2; } } @@ -524,14 +477,14 @@ void WifiCheck(uint8_t param) #ifdef USE_DISCOVERY if (Settings.flag3.mdns_enabled) { - if (!mdns_begun) { + if (!Wifi.mdns_begun) { // if (mdns_delayed_start) { // AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_MDNS D_ATTEMPTING_CONNECTION)); // mdns_delayed_start--; // } else { // mdns_delayed_start = Settings.param[P_MDNS_DELAYED_START]; - mdns_begun = (uint8_t)MDNS.begin(my_hostname); - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MDNS "%s"), (mdns_begun) ? D_INITIALIZED : D_FAILED); + Wifi.mdns_begun = (uint8_t)MDNS.begin(my_hostname); + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MDNS "%s"), (Wifi.mdns_begun) ? D_INITIALIZED : D_FAILED); // } } } @@ -542,8 +495,8 @@ void WifiCheck(uint8_t param) StartWebserver(Settings.webserver, WiFi.localIP()); #ifdef USE_DISCOVERY #ifdef WEBSERVER_ADVERTISE - if (1 == mdns_begun) { - mdns_begun = 2; + if (1 == Wifi.mdns_begun) { + Wifi.mdns_begun = 2; MDNS.addService("http", "tcp", WEB_PORT); } #endif // WEBSERVER_ADVERTISE @@ -568,7 +521,7 @@ void WifiCheck(uint8_t param) #ifdef USE_EMULATION UdpDisconnect(); #endif // USE_EMULATION - mdns_begun = 0; + Wifi.mdns_begun = 0; #ifdef USE_KNX knx_started = false; #endif // USE_KNX @@ -582,7 +535,7 @@ int WifiState(void) int state = -1; if (!global_state.wifi_down) { state = WIFI_RESTART; } - if (wifi_config_type) { state = wifi_config_type; } + if (Wifi.config_type) { state = Wifi.config_type; } return state; } @@ -590,10 +543,10 @@ void WifiConnect(void) { WifiSetState(0); WiFi.persistent(false); // Solve possible wifi init errors - wifi_status = 0; - wifi_retry_init = WIFI_RETRY_OFFSET_SEC + ((ESP.getChipId() & 0xF) * 2); - wifi_retry = wifi_retry_init; - wifi_counter = 1; + Wifi.status = 0; + Wifi.retry_init = WIFI_RETRY_OFFSET_SEC + ((ESP.getChipId() & 0xF) * 2); + Wifi.retry = Wifi.retry_init; + Wifi.counter = 1; } // Enable from 6.0.0a until 6.1.0a - disabled due to possible cause of bad wifi connect on core 2.3.0 @@ -616,19 +569,3 @@ void EspRestart(void) // ESP.restart(); // This results in exception 3 on restarts on core 2.3.0 ESP.reset(); } - -/* -void EspRestart(void) -{ - ESP.restart(); -} -*/ - -void WifiAddDelayWhenDisconnected(void) -{ - if (APP_BAUDRATE == baudrate) { // When baudrate too low it will fail on Sonoff Pow R2 and S31 serial interface initialization - if (global_state.wifi_down) { - delay(DRIVER_BOOT_DELAY); - } - } -} diff --git a/sonoff/xdrv_01_webserver.ino b/sonoff/xdrv_01_webserver.ino index 2ce8595cc..1f3fc3733 100644 --- a/sonoff/xdrv_01_webserver.ino +++ b/sonoff/xdrv_01_webserver.ino @@ -28,7 +28,7 @@ #define XDRV_01 1 #ifndef WIFI_SOFT_AP_CHANNEL -#define WIFI_SOFT_AP_CHANNEL 1 // Soft Access Point Channel number between 1 and 11 as used by SmartConfig web GUI +#define WIFI_SOFT_AP_CHANNEL 1 // Soft Access Point Channel number between 1 and 11 as used by WifiManager web GUI #endif const uint16_t CHUNKED_BUFFER_SIZE = 400; // Chunk buffer size (should be smaller than half mqtt_date size) @@ -44,9 +44,11 @@ const uint16_t HTTP_REFRESH_TIME = 2345; // milliseconds uint8_t *efm8bb1_update = nullptr; #endif // USE_RF_FLASH -enum UploadTypes { UPL_TASMOTA, UPL_SETTINGS, UPL_EFM8BB1 }; +enum UploadTypes { UPL_TASMOTA, UPL_SETTINGS, UPL_EFM8BB1, UPL_ARDUINOSLAVE }; -const char HTTP_HEAD[] PROGMEM = +static const char * HEADER_KEYS[] = { "User-Agent", }; + +const char HTTP_HEADER[] PROGMEM = "" "" "" @@ -90,6 +92,46 @@ const char HTTP_SCRIPT_COUNTER[] PROGMEM = "wl(u);"; const char HTTP_SCRIPT_ROOT[] PROGMEM = +#ifdef USE_SCRIPT_WEB_DISPLAY + "var rfsh=1;" + "function la(p){" + "var a='';" + "if(la.arguments.length==1){" + "a=p;" + "clearTimeout(lt);" + "}" + "if(x!=null){x.abort();}" // Abort if no response within 2 seconds (happens on restart 1) + "x=new XMLHttpRequest();" + "x.onreadystatechange=function(){" + "if(x.readyState==4&&x.status==200){" + "var s=x.responseText.replace(/{t}/g,\"\").replace(/{s}/g,\"\").replace(/{c}/g,\"%%'>
hasArg("m") + "x.send();" + "lt=setTimeout(la,%d);" // Settings.web_refresh + "}" + "}" + "function seva(par,ivar){" + "la('&sv='+ivar+'_'+par);" + "}" + "function siva(par,ivar){" + "rfsh=1;" + "la('&sv='+ivar+'_'+par);" + "rfsh=0;" + "}" + "function pr(f){" + "if (f) {" + "lt=setTimeout(la,%d);" + "rfsh=1;" + "} else {" + "clearTimeout(lt);" + "rfsh=0;" + "}" + "}" +#else // USE_SCRIPT_WEB_DISPLAY "function la(p){" "var a='';" "if(la.arguments.length==1){" @@ -108,18 +150,19 @@ const char HTTP_SCRIPT_ROOT[] PROGMEM = "x.send();" "lt=setTimeout(la,%d);" // Settings.web_refresh "}" +#endif // USE_SCRIPT_WEB_DISPLAY #ifdef USE_JAVASCRIPT_ES6 - "lb=p=>la('&d='+p);" // Dark - Bright &d related to lb(value) and WebGetArg("d", tmp, sizeof(tmp)); - "lc=p=>la('&t='+p);" // Cold - Warm &t related to lc(value) and WebGetArg("t", tmp, sizeof(tmp)); + "lb=(v,p)=>la(`&${v}=${p}`);" + "lc=(v,i,p)=>la(`&${v}${i}=${p}`);" #else - "function lb(p){" - "la('&d='+p);" // &d related to WebGetArg("d", tmp, sizeof(tmp)); + "function lb(v,p){" + "la('&'+v+'='+p);" "}" - "function lc(p){" - "la('&t='+p);" // &t related to WebGetArg("t", tmp, sizeof(tmp)); + "function lc(v,i,p){" + "la('&'+v+i+'='+p);" "}" -#endif +#endif // USE_JAVASCRIPT_ES6 "wl(la);"; @@ -330,11 +373,11 @@ const char HTTP_HEAD_STYLE3[] PROGMEM = "

%s

"; const char HTTP_MSG_SLIDER1[] PROGMEM = - "
" D_COLDLIGHT "" D_WARMLIGHT "
" - "
"; + "
%s%s
" + "
"; const char HTTP_MSG_SLIDER2[] PROGMEM = - "
" D_DARKLIGHT "" D_BRIGHTLIGHT "
" - "
"; + "
%s%s
" + "
"; const char HTTP_MSG_RSTRT[] PROGMEM = "
" D_DEVICE_WILL_RESTART "

"; @@ -456,7 +499,7 @@ const char kButtonConfirm[] PROGMEM = D_CONFIRM_RESTART "|" D_CONFIRM_RESET_CONF enum CTypes { CT_HTML, CT_PLAIN, CT_XML, CT_JSON, CT_STREAM }; const char kContentTypes[] PROGMEM = "text/html|text/plain|text/xml|application/json|application/octet-stream"; -const char kLoggingOptions[] PROGMEM = D_SERIAL_LOG_LEVEL "|" D_WEB_LOG_LEVEL "|" D_SYS_LOG_LEVEL; +const char kLoggingOptions[] PROGMEM = D_SERIAL_LOG_LEVEL "|" D_WEB_LOG_LEVEL "|" D_MQTT_LOG_LEVEL "|" D_SYS_LOG_LEVEL; const char kLoggingLevels[] PROGMEM = D_NONE "|" D_ERROR "|" D_INFO "|" D_DEBUG "|" D_MORE_DEBUG; const char kEmulationOptions[] PROGMEM = D_NONE "|" D_BELKIN_WEMO "|" D_HUE_BRIDGE; @@ -474,17 +517,17 @@ enum HttpOptions {HTTP_OFF, HTTP_USER, HTTP_ADMIN, HTTP_MANAGER, HTTP_MANAGER_RE DNSServer *DnsServer; ESP8266WebServer *WebServer; -String chunk_buffer = ""; // Could be max 2 * CHUNKED_BUFFER_SIZE -int minimum_signal_quality = -1; -bool remove_duplicate_access_points = true; -bool reset_web_log_flag = false; // Reset web console log -uint8_t webserver_state = HTTP_OFF; -uint8_t upload_error = 0; -uint8_t upload_file_type; -uint8_t upload_progress_dot_count; -uint8_t config_block_count = 0; -uint8_t config_xor_on = 0; -uint8_t config_xor_on_set = CONFIG_FILE_XOR; +struct WEB { + String chunk_buffer = ""; // Could be max 2 * CHUNKED_BUFFER_SIZE + bool reset_web_log_flag = false; // Reset web console log + uint8_t state = HTTP_OFF; + uint8_t upload_error = 0; + uint8_t upload_file_type; + uint8_t upload_progress_dot_count; + uint8_t config_block_count = 0; + uint8_t config_xor_on = 0; + uint8_t config_xor_on_set = CONFIG_FILE_XOR; +} Web; // Helper function to avoid code duplication (saves 4k Flash) static void WebGetArg(const char* arg, char* out, size_t max) @@ -495,10 +538,10 @@ static void WebGetArg(const char* arg, char* out, size_t max) } static bool WifiIsInManagerMode(){ - return (HTTP_MANAGER == webserver_state || HTTP_MANAGER_RESET_ONLY == webserver_state); + return (HTTP_MANAGER == Web.state || HTTP_MANAGER_RESET_ONLY == Web.state); } -void ShowWebSource(int source) +void ShowWebSource(uint32_t source) { if ((source > 0) && (source < SRC_MAX)) { char stemp1[20]; @@ -506,16 +549,17 @@ void ShowWebSource(int source) } } -void ExecuteWebCommand(char* svalue, int source) +void ExecuteWebCommand(char* svalue, uint32_t source) { ShowWebSource(source); + last_source = source; ExecuteCommand(svalue, SRC_IGNORE); } void StartWebserver(int type, IPAddress ipweb) { if (!Settings.web_refresh) { Settings.web_refresh = HTTP_REFRESH_TIME; } - if (!webserver_state) { + if (!Web.state) { if (!WebServer) { WebServer = new ESP8266WebServer((HTTP_MANAGER == type || HTTP_MANAGER_RESET_ONLY == type) ? 80 : WEB_PORT); WebServer->on("/", HandleRoot); @@ -542,21 +586,26 @@ void StartWebserver(int type, IPAddress ipweb) XsnsCall(FUNC_WEB_ADD_HANDLER); #endif // Not FIRMWARE_MINIMAL } - reset_web_log_flag = false; + Web.reset_web_log_flag = false; + + // Collect User-Agent for Alexa Hue Emulation + // This is used in xdrv_20_hue.ino in function findEchoGeneration() + WebServer->collectHeaders(HEADER_KEYS, sizeof(HEADER_KEYS)/sizeof(char*)); + WebServer->begin(); // Web server start } - if (webserver_state != type) { - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_HTTP D_WEBSERVER_ACTIVE_ON " %s%s " D_WITH_IP_ADDRESS " %s"), my_hostname, (mdns_begun) ? ".local" : "", ipweb.toString().c_str()); + if (Web.state != type) { + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_HTTP D_WEBSERVER_ACTIVE_ON " %s%s " D_WITH_IP_ADDRESS " %s"), my_hostname, (Wifi.mdns_begun) ? ".local" : "", ipweb.toString().c_str()); rules_flag.http_init = 1; } - if (type) { webserver_state = type; } + if (type) { Web.state = type; } } void StopWebserver(void) { - if (webserver_state) { + if (Web.state) { WebServer->close(); - webserver_state = HTTP_OFF; + Web.state = HTTP_OFF; AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_HTTP D_WEBSERVER_STOPPED)); } } @@ -598,7 +647,7 @@ void PollDnsWebserver(void) bool WebAuthenticate(void) { - if (Settings.web_password[0] != 0 && HTTP_MANAGER_RESET_ONLY != webserver_state) { + if (Settings.web_password[0] != 0 && HTTP_MANAGER_RESET_ONLY != Web.state) { return WebServer->authenticate(WEB_USERNAME, Settings.web_password); } else { return true; @@ -607,7 +656,7 @@ bool WebAuthenticate(void) bool HttpCheckPriviledgedAccess(bool autorequestauth = true) { - if (HTTP_USER == webserver_state) { + if (HTTP_USER == Web.state) { HandleRoot(); return false; } @@ -652,7 +701,7 @@ void WSContentBegin(int code, int ctype) #endif WebServer->setContentLength(CONTENT_LENGTH_UNKNOWN); WSSend(code, ctype, ""); // Signal start of chunked content - chunk_buffer = ""; + Web.chunk_buffer = ""; } void _WSContentSend(const String& content) // Low level sendContent for all core versions @@ -671,14 +720,14 @@ void _WSContentSend(const String& content) // Low level sendContent for a #ifdef USE_DEBUG_DRIVER ShowFreeMem(PSTR("WSContentSend")); #endif -// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("HTP: Chunk size %d"), len); + DEBUG_CORE_LOG(PSTR("WEB: Chunk size %d"), len); } void WSContentFlush() { - if (chunk_buffer.length() > 0) { - _WSContentSend(chunk_buffer); // Flush chunk buffer - chunk_buffer = ""; + if (Web.chunk_buffer.length() > 0) { + _WSContentSend(Web.chunk_buffer); // Flush chunk buffer + Web.chunk_buffer = ""; } } @@ -693,8 +742,8 @@ void _WSContentSendBuffer(void) AddLog_P(LOG_LEVEL_INFO, PSTR("HTP: Content too large")); } else if (len < CHUNKED_BUFFER_SIZE) { // Append chunk buffer with small content - chunk_buffer += mqtt_data; - len = chunk_buffer.length(); + Web.chunk_buffer += mqtt_data; + len = Web.chunk_buffer.length(); } if (len >= CHUNKED_BUFFER_SIZE) { // Either content or chunk buffer is oversize @@ -746,7 +795,7 @@ void WSContentStart_P(const char* title, bool auth) if (title != nullptr) { char ctitle[strlen_P(title) +1]; strcpy_P(ctitle, title); // Get title from flash to RAM - WSContentSend_P(HTTP_HEAD, Settings.friendlyname[0], ctitle); + WSContentSend_P(HTTP_HEADER, Settings.friendlyname[0], ctitle); } } @@ -784,7 +833,7 @@ void WSContentSendStyle_P(const char* formatP, ...) bool sip = (static_cast(WiFi.softAPIP()) != 0); WSContentSend_P(PSTR("

%s%s (%s%s%s)

"), // sonoff.local (192.168.2.12, 192.168.4.1) my_hostname, - (mdns_begun) ? ".local" : "", + (Wifi.mdns_begun) ? ".local" : "", (lip) ? WiFi.localIP().toString().c_str() : "", (lip && sip) ? ", " : "", (sip) ? WiFi.softAPIP().toString().c_str() : ""); @@ -797,7 +846,7 @@ void WSContentSendStyle(void) WSContentSendStyle_P(nullptr); } -void WSContentButton(uint8_t title_index) +void WSContentButton(uint32_t title_index) { char action[4]; char title[100]; // Large to accomodate UTF-16 as used by Russian @@ -816,7 +865,7 @@ void WSContentButton(uint8_t title_index) } } -void WSContentSpaceButton(uint8_t title_index) +void WSContentSpaceButton(uint32_t title_index) { WSContentSend_P(PSTR("
")); // 5px padding WSContentButton(title_index); @@ -842,14 +891,14 @@ void WSContentStop(void) /*********************************************************************************************/ -void WebRestart(uint8_t type) +void WebRestart(uint32_t type) { // type 0 = restart // type 1 = restart after config change // type 2 = restart after config change with possible ip address change too AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_RESTART); - bool reset_only = (HTTP_MANAGER_RESET_ONLY == webserver_state); + bool reset_only = (HTTP_MANAGER_RESET_ONLY == Web.state); WSContentStart_P((type) ? S_SAVE_CONFIGURATION : S_RESTART, !reset_only); WSContentSend_P(HTTP_SCRIPT_RELOAD); @@ -862,8 +911,8 @@ void WebRestart(uint8_t type) WSContentSend_P(PSTR("
")); } WSContentSend_P(HTTP_MSG_RSTRT); - if (HTTP_MANAGER == webserver_state || reset_only) { - webserver_state = HTTP_ADMIN; + if (HTTP_MANAGER == Web.state || reset_only) { + Web.state = HTTP_ADMIN; } else { WSContentSpaceButton(BUTTON_MAIN); } @@ -881,7 +930,7 @@ void HandleWifiLogin(void) WSContentSendStyle(); WSContentSend_P(HTTP_FORM_LOGIN); - if (HTTP_MANAGER_RESET_ONLY == webserver_state) { + if (HTTP_MANAGER_RESET_ONLY == Web.state) { WSContentSpaceButton(BUTTON_RESTART); #ifndef FIRMWARE_MINIMAL WSContentSpaceButton(BUTTON_RESET_CONFIGURATION); @@ -902,10 +951,10 @@ void HandleRoot(void) if (WifiIsInManagerMode()) { #ifndef FIRMWARE_MINIMAL - if ((Settings.web_password[0] != 0) && !(WebServer->hasArg("USER1")) && !(WebServer->hasArg("PASS1")) && HTTP_MANAGER_RESET_ONLY != webserver_state) { + if ((Settings.web_password[0] != 0) && !(WebServer->hasArg("USER1")) && !(WebServer->hasArg("PASS1")) && HTTP_MANAGER_RESET_ONLY != Web.state) { HandleWifiLogin(); } else { - if (!(Settings.web_password[0] != 0) || (((WebServer->arg("USER1") == WEB_USERNAME ) && (WebServer->arg("PASS1") == Settings.web_password )) || HTTP_MANAGER_RESET_ONLY == webserver_state)) { + if (!(Settings.web_password[0] != 0) || (((WebServer->arg("USER1") == WEB_USERNAME ) && (WebServer->arg("PASS1") == Settings.web_password )) || HTTP_MANAGER_RESET_ONLY == Web.state)) { HandleWifiConfiguration(); } else { // wrong user and pass @@ -925,41 +974,70 @@ void HandleRoot(void) char stemp[5]; WSContentStart_P(S_MAIN_MENU); +#ifdef USE_SCRIPT_WEB_DISPLAY + WSContentSend_P(HTTP_SCRIPT_ROOT, Settings.web_refresh, Settings.web_refresh); +#else WSContentSend_P(HTTP_SCRIPT_ROOT, Settings.web_refresh); +#endif WSContentSendStyle(); WSContentSend_P(PSTR("
")); if (devices_present) { #ifdef USE_LIGHT if (light_type) { - if ((LST_COLDWARM == (light_type &7)) || (LST_RGBWC == (light_type &7))) { - WSContentSend_P(HTTP_MSG_SLIDER1, LightGetColorTemp()); - } - if (!Settings.flag3.tuya_show_dimmer) { - WSContentSend_P(HTTP_MSG_SLIDER2, Settings.light_dimmer); - } + if (!Settings.flag3.pwm_multi_channels) { + if ((LST_COLDWARM == (light_type &7)) || (LST_RGBWC == (light_type &7))) { + // Cold - Warm &t related to lb("t", value) and WebGetArg("t", tmp, sizeof(tmp)); + WSContentSend_P(HTTP_MSG_SLIDER1, F(D_COLDLIGHT), F(D_WARMLIGHT), + 153, 500, LightGetColorTemp(), 't'); + } + // Dark - Bright &d related to lb("d", value) and WebGetArg("d", tmp, sizeof(tmp)); + WSContentSend_P(HTTP_MSG_SLIDER1, F(D_DARKLIGHT), F(D_BRIGHTLIGHT), + 1, 100, Settings.light_dimmer, 'd'); + } else { // Settings.flag3.pwm_multi_channels + uint32_t pwm_channels = (light_type & 7) > LST_MAX ? LST_MAX : (light_type & 7); + for (uint32_t i = 0; i < pwm_channels; i++) { + snprintf_P(stemp, sizeof(stemp), PSTR("c%d"), i); + WSContentSend_P(HTTP_MSG_SLIDER2, stemp, FPSTR("100%"), + 1, 100, + changeUIntScale(Settings.light_color[i], 0, 255, 0, 100), 'd', i+1); + } + } // Settings.flag3.pwm_multi_channels } #endif +#ifdef USE_SHUTTER + if (Settings.flag3.shutter_mode) { + for (uint32_t i = 0; i < shutters_present; i++) { + WSContentSend_P(HTTP_MSG_SLIDER2, F(D_CLOSE), F(D_OPEN), + 0, 100, Settings.shutter_position[i], 'u', i+1); + } + } +#endif // USE_SHUTTER WSContentSend_P(HTTP_TABLE100); WSContentSend_P(PSTR("")); - if (SONOFF_IFAN02 == my_module_type) { +#ifdef USE_SONOFF_IFAN + if (IsModuleIfan()) { WSContentSend_P(HTTP_DEVICE_CONTROL, 36, 1, D_BUTTON_TOGGLE, ""); - for (uint32_t i = 0; i < MAX_FAN_SPEED; i++) { + for (uint32_t i = 0; i < MaxFanspeed(); i++) { snprintf_P(stemp, sizeof(stemp), PSTR("%d"), i); WSContentSend_P(HTTP_DEVICE_CONTROL, 16, i +2, stemp, ""); } } else { +#endif // USE_SONOFF_IFAN for (uint32_t idx = 1; idx <= devices_present; idx++) { snprintf_P(stemp, sizeof(stemp), PSTR(" %d"), idx); WSContentSend_P(HTTP_DEVICE_CONTROL, 100 / devices_present, idx, (devices_present < 5) ? D_BUTTON_TOGGLE : "", (devices_present > 1) ? stemp : ""); } +#ifdef USE_SONOFF_IFAN } +#endif // USE_SONOFF_IFAN WSContentSend_P(PSTR("
\").replace(/{m}/g,\"\").replace(/{e}/g,\"
")); } +#ifdef USE_SONOFF_RF if (SONOFF_BRIDGE == my_module_type) { WSContentSend_P(HTTP_TABLE100); WSContentSend_P(PSTR("")); - uint8_t idx = 0; + uint32_t idx = 0; for (uint32_t i = 0; i < 4; i++) { if (idx > 0) { WSContentSend_P(PSTR("")); } for (uint32_t j = 0; j < 4; j++) { @@ -969,13 +1047,14 @@ void HandleRoot(void) } WSContentSend_P(PSTR("")); } +#endif // USE_SONOFF_RF #ifndef FIRMWARE_MINIMAL XdrvCall(FUNC_WEB_ADD_MAIN_BUTTON); XsnsCall(FUNC_WEB_ADD_MAIN_BUTTON); #endif // Not FIRMWARE_MINIMAL - if (HTTP_ADMIN == webserver_state) { + if (HTTP_ADMIN == Web.state) { #ifdef FIRMWARE_MINIMAL WSContentSpaceButton(BUTTON_FIRMWARE_UPGRADE); #else @@ -1000,14 +1079,20 @@ bool HandleRootStatusRefresh(void) return false; } + #ifdef USE_SCRIPT_WEB_DISPLAY + Script_Check_HTML_Setvars(); + #endif + char tmp[8]; // WebGetArg numbers only char svalue[32]; // Command and number parameter + char webindex[5]; // WebGetArg name WebGetArg("o", tmp, sizeof(tmp)); // 1 - 16 Device number for button Toggle or Fanspeed if (strlen(tmp)) { ShowWebSource(SRC_WEBGUI); - uint8_t device = atoi(tmp); - if (SONOFF_IFAN02 == my_module_type) { + uint32_t device = atoi(tmp); +#ifdef USE_SONOFF_IFAN + if (IsModuleIfan()) { if (device < 2) { ExecuteCommandPower(1, POWER_TOGGLE, SRC_IGNORE); } else { @@ -1015,44 +1100,75 @@ bool HandleRootStatusRefresh(void) ExecuteCommand(svalue, SRC_WEBGUI); } } else { +#endif // USE_SONOFF_IFAN ExecuteCommandPower(device, POWER_TOGGLE, SRC_IGNORE); +#ifdef USE_SONOFF_IFAN } +#endif // USE_SONOFF_IFAN } WebGetArg("d", tmp, sizeof(tmp)); // 0 - 100 Dimmer value if (strlen(tmp)) { snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_DIMMER " %s"), tmp); ExecuteWebCommand(svalue, SRC_WEBGUI); } + uint32_t pwm_channels = (light_type & 7) > LST_MAX ? LST_MAX : (light_type & 7); + for (uint32_t j = 1; j <= pwm_channels; j++) { + snprintf_P(webindex, sizeof(webindex), PSTR("d%d"), j); + WebGetArg(webindex, tmp, sizeof(tmp)); // 0 - 100 percent + if (strlen(tmp)) { + snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_CHANNEL "%d %s"), j, tmp); + ExecuteWebCommand(svalue, SRC_WEBGUI); + } + } WebGetArg("t", tmp, sizeof(tmp)); // 153 - 500 Color temperature if (strlen(tmp)) { snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_COLORTEMPERATURE " %s"), tmp); ExecuteWebCommand(svalue, SRC_WEBGUI); } +#ifdef USE_SHUTTER + for (uint32_t j = 1; j <= shutters_present; j++) { + snprintf_P(webindex, sizeof(webindex), PSTR("u%d"), j); + WebGetArg(webindex, tmp, sizeof(tmp)); // 0 - 100 percent + if (strlen(tmp)) { + snprintf_P(svalue, sizeof(svalue), PSTR("ShutterPosition%d %s"), j, tmp); + ExecuteWebCommand(svalue, SRC_WEBGUI); + } + } +#endif // USE_SHUTTER +#ifdef USE_SONOFF_RF WebGetArg("k", tmp, sizeof(tmp)); // 1 - 16 Pre defined RF keys if (strlen(tmp)) { snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_RFKEY "%s"), tmp); ExecuteWebCommand(svalue, SRC_WEBGUI); } - +#endif // USE_SONOFF_RF WSContentBegin(200, CT_HTML); WSContentSend_P(PSTR("{t}")); XsnsCall(FUNC_WEB_SENSOR); +#ifdef USE_SCRIPT_WEB_DISPLAY + XdrvCall(FUNC_WEB_SENSOR); +#endif + WSContentSend_P(PSTR("")); if (devices_present) { WSContentSend_P(PSTR("{t}")); - uint8_t fsize = (devices_present < 5) ? 70 - (devices_present * 8) : 32; - if (SONOFF_IFAN02 == my_module_type) { + uint32_t fsize = (devices_present < 5) ? 70 - (devices_present * 8) : 32; +#ifdef USE_SONOFF_IFAN + if (IsModuleIfan()) { WSContentSend_P(HTTP_DEVICE_STATE, 36, (bitRead(power, 0)) ? "bold" : "normal", 54, GetStateText(bitRead(power, 0))); - uint8_t fanspeed = GetFanspeed(); + uint32_t fanspeed = GetFanspeed(); snprintf_P(svalue, sizeof(svalue), PSTR("%d"), fanspeed); WSContentSend_P(HTTP_DEVICE_STATE, 64, (fanspeed) ? "bold" : "normal", 54, (fanspeed) ? svalue : GetStateText(0)); } else { +#endif // USE_SONOFF_IFAN for (uint32_t idx = 1; idx <= devices_present; idx++) { snprintf_P(svalue, sizeof(svalue), PSTR("%d"), bitRead(power, idx -1)); WSContentSend_P(HTTP_DEVICE_STATE, 100 / devices_present, (bitRead(power, idx -1)) ? "bold" : "normal", fsize, (devices_present < 5) ? GetStateText(bitRead(power, idx -1)) : svalue); } +#ifdef USE_SONOFF_IFAN } +#endif // USE_SONOFF_IFAN WSContentSend_P(PSTR("")); } WSContentEnd(); @@ -1103,12 +1219,12 @@ void HandleTemplateConfiguration(void) return; } - char stemp[20]; // Template number and Sensor name + char stemp[30]; // Template number and Sensor name if (WebServer->hasArg("m")) { WSContentBegin(200, CT_PLAIN); for (uint32_t i = 0; i < sizeof(kModuleNiceList); i++) { // "}2'%d'>%s (%d)}3" - "}2'0'>Sonoff Basic (1)}3" - uint8_t midx = pgm_read_byte(kModuleNiceList + i); + uint32_t midx = pgm_read_byte(kModuleNiceList + i); WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE, midx, AnyModuleName(midx).c_str(), midx +1); } WSContentEnd(); @@ -1117,8 +1233,8 @@ void HandleTemplateConfiguration(void) WebGetArg("t", stemp, sizeof(stemp)); // 0 - 69 Template number if (strlen(stemp)) { - uint8_t module = atoi(stemp); - uint8_t module_save = Settings.module; + uint32_t module = atoi(stemp); + uint32_t module_save = Settings.module; Settings.module = module; myio cmodule; ModuleGpios(&cmodule); @@ -1131,7 +1247,7 @@ void HandleTemplateConfiguration(void) if (1 == i) { WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE, 255, D_SENSOR_USER, 255); // }2'255'>User (255)}3 } - uint8_t midx = pgm_read_byte(kGpioNiceList + i); + uint32_t midx = pgm_read_byte(kGpioNiceList + i); WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE, midx, GetTextIndexed(stemp, sizeof(stemp), midx, kSensorNames), midx); } WSContentSend_P(PSTR("}1")); // Field separator @@ -1193,7 +1309,7 @@ void TemplateSaveSettings(void) WebGetArg("s1", tmp, sizeof(tmp)); // NAME snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_TEMPLATE " {\"" D_JSON_NAME "\":\"%s\",\"" D_JSON_GPIO "\":["), tmp); - uint8_t j = 0; + uint32_t j = 0; for (uint32_t i = 0; i < sizeof(Settings.user_template.gp); i++) { if (6 == i) { j = 9; } if (8 == i) { j = 12; } @@ -1205,14 +1321,14 @@ void TemplateSaveSettings(void) } WebGetArg("g17", tmp, sizeof(tmp)); // FLAG - ADC0 - uint8_t flag = atoi(tmp); + uint32_t flag = atoi(tmp); for (uint32_t i = 0; i < GPIO_FLAG_USED; i++) { snprintf_P(webindex, sizeof(webindex), PSTR("c%d"), i); - uint8_t state = WebServer->hasArg(webindex) << i +4; // FLAG + uint32_t state = WebServer->hasArg(webindex) << i +4; // FLAG flag += state; } WebGetArg("g99", tmp, sizeof(tmp)); // BASE - uint8_t base = atoi(tmp) +1; + uint32_t base = atoi(tmp) +1; snprintf_P(svalue, sizeof(svalue), PSTR("%s],\"" D_JSON_FLAG "\":%d,\"" D_JSON_BASE "\":%d}"), svalue, flag, base); ExecuteWebCommand(svalue, SRC_WEBGUI); @@ -1230,14 +1346,14 @@ void HandleModuleConfiguration(void) return; } - char stemp[20]; // Sensor name - uint8_t midx; + char stemp[30]; // Sensor name + uint32_t midx; myio cmodule; ModuleGpios(&cmodule); if (WebServer->hasArg("m")) { WSContentBegin(200, CT_PLAIN); - uint8_t vidx = 0; + uint32_t vidx = 0; for (uint32_t i = 0; i <= sizeof(kModuleNiceList); i++) { // "}2'%d'>%s (%d)}3" - "}2'255'>UserTemplate (0)}3" - "}2'0'>Sonoff Basic (1)}3" if (0 == i) { midx = USER_MODULE; @@ -1314,7 +1430,7 @@ void ModuleSaveSettings(void) char webindex[5]; // WebGetArg name WebGetArg("g99", tmp, sizeof(tmp)); - uint8_t new_module = (!strlen(tmp)) ? MODULE : atoi(tmp); + uint32_t new_module = (!strlen(tmp)) ? MODULE : atoi(tmp); Settings.last_module = Settings.module; Settings.module = new_module; SetModuleType(); @@ -1349,7 +1465,7 @@ const char kEscapeCode[] PROGMEM = "&|>|<|"|'"; String HtmlEscape(const String unescaped) { char escaped[10]; - uint16_t ulen = unescaped.length(); + size_t ulen = unescaped.length(); String result = ""; for (size_t i = 0; i < ulen; i++) { char c = unescaped[i]; @@ -1372,7 +1488,7 @@ void HandleWifiConfiguration(void) AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_WIFI); - if (WebServer->hasArg("save") && HTTP_MANAGER_RESET_ONLY != webserver_state) { + if (WebServer->hasArg("save") && HTTP_MANAGER_RESET_ONLY != Web.state) { WifiSaveSettings(); WebRestart(2); return; @@ -1382,7 +1498,7 @@ void HandleWifiConfiguration(void) WSContentSend_P(HTTP_SCRIPT_WIFI); WSContentSendStyle(); - if (HTTP_MANAGER_RESET_ONLY != webserver_state) { + if (HTTP_MANAGER_RESET_ONLY != Web.state) { if (WebServer->hasArg("scan")) { #ifdef USE_EMULATION UdpDisconnect(); @@ -1411,16 +1527,14 @@ void HandleWifiConfiguration(void) } // remove duplicates ( must be RSSI sorted ) - if (remove_duplicate_access_points) { - String cssid; - for (uint32_t i = 0; i < n; i++) { - if (-1 == indices[i]) { continue; } - cssid = WiFi.SSID(indices[i]); - for (uint32_t j = i + 1; j < n; j++) { - if (cssid == WiFi.SSID(indices[j])) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI D_DUPLICATE_ACCESSPOINT " %s"), WiFi.SSID(indices[j]).c_str()); - indices[j] = -1; // set dup aps to index -1 - } + String cssid; + for (uint32_t i = 0; i < n; i++) { + if (-1 == indices[i]) { continue; } + cssid = WiFi.SSID(indices[i]); + for (uint32_t j = i + 1; j < n; j++) { + if (cssid == WiFi.SSID(indices[j])) { + DEBUG_CORE_LOG(PSTR(D_LOG_WIFI D_DUPLICATE_ACCESSPOINT " %s"), WiFi.SSID(indices[j]).c_str()); + indices[j] = -1; // set dup aps to index -1 } } } @@ -1428,22 +1542,18 @@ void HandleWifiConfiguration(void) //display networks in page for (uint32_t i = 0; i < n; i++) { if (-1 == indices[i]) { continue; } // skip dups - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI D_SSID " %s, " D_BSSID " %s, " D_CHANNEL " %d, " D_RSSI " %d"), WiFi.SSID(indices[i]).c_str(), WiFi.BSSIDstr(indices[i]).c_str(), WiFi.channel(indices[i]), WiFi.RSSI(indices[i])); + DEBUG_CORE_LOG(PSTR(D_LOG_WIFI D_SSID " %s, " D_BSSID " %s, " D_CHANNEL " %d, " D_RSSI " %d"), + WiFi.SSID(indices[i]).c_str(), WiFi.BSSIDstr(indices[i]).c_str(), WiFi.channel(indices[i]), WiFi.RSSI(indices[i])); int quality = WifiGetRssiAsQuality(WiFi.RSSI(indices[i])); - - if (minimum_signal_quality == -1 || minimum_signal_quality < quality) { - int auth = WiFi.encryptionType(indices[i]); - char encryption[20]; - WSContentSend_P(PSTR("
"), - HtmlEscape(WiFi.SSID(indices[i])).c_str(), - WiFi.channel(indices[i]), - GetTextIndexed(encryption, sizeof(encryption), auth +1, kEncryptionType), - quality - ); - delay(0); - } else { - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI D_SKIPPING_LOW_QUALITY)); - } + int auth = WiFi.encryptionType(indices[i]); + char encryption[20]; + WSContentSend_P(PSTR("
%s (%d) %s %d%%
"), + HtmlEscape(WiFi.SSID(indices[i])).c_str(), + WiFi.channel(indices[i]), + GetTextIndexed(encryption, sizeof(encryption), auth +1, kEncryptionType), + quality + ); + delay(0); } WSContentSend_P(PSTR("
")); @@ -1507,9 +1617,10 @@ void HandleLoggingConfiguration(void) WSContentSend_P(HTTP_FORM_LOG1); char stemp1[45]; char stemp2[32]; - uint8_t dlevel[3] = { LOG_LEVEL_INFO, LOG_LEVEL_INFO, LOG_LEVEL_NONE }; - for (uint32_t idx = 0; idx < 3; idx++) { - uint8_t llevel = (0==idx)?Settings.seriallog_level:(1==idx)?Settings.weblog_level:Settings.syslog_level; + uint8_t dlevel[4] = { LOG_LEVEL_INFO, LOG_LEVEL_INFO, LOG_LEVEL_NONE, LOG_LEVEL_NONE }; + for (uint32_t idx = 0; idx < 4; idx++) { + if ((2==idx) && !Settings.flag.mqtt_enabled) { continue; } + uint32_t llevel = (0==idx)?Settings.seriallog_level:(1==idx)?Settings.weblog_level:(2==idx)?Settings.mqttlog_level:Settings.syslog_level; WSContentSend_P(PSTR("

%s (%s)

"), @@ -1659,19 +1774,24 @@ void HandleBackupConfiguration(void) WebServer->setContentLength(sizeof(Settings)); char attachment[100]; - char friendlyname[sizeof(Settings.friendlyname[0])]; - snprintf_P(attachment, sizeof(attachment), PSTR("attachment; filename=Config_%s_%s.dmp"), NoAlNumToUnderscore(friendlyname, Settings.friendlyname[0]), my_version); + +// char friendlyname[sizeof(Settings.friendlyname[0])]; +// snprintf_P(attachment, sizeof(attachment), PSTR("attachment; filename=Config_%s_%s.dmp"), NoAlNumToUnderscore(friendlyname, Settings.friendlyname[0]), my_version); + + char hostname[sizeof(my_hostname)]; + snprintf_P(attachment, sizeof(attachment), PSTR("attachment; filename=Config_%s_%s.dmp"), NoAlNumToUnderscore(hostname, my_hostname), my_version); + WebServer->sendHeader(F("Content-Disposition"), attachment); WSSend(200, CT_STREAM, ""); - uint16_t cfg_crc = Settings.cfg_crc; - Settings.cfg_crc = GetSettingsCrc(); // Calculate crc (again) as it might be wrong when savedata = 0 (#3918) + uint32_t cfg_crc32 = Settings.cfg_crc32; + Settings.cfg_crc32 = GetSettingsCrc32(); // Calculate crc (again) as it might be wrong when savedata = 0 (#3918) memcpy(settings_buffer, &Settings, sizeof(Settings)); - if (config_xor_on_set) { + if (Web.config_xor_on_set) { for (uint32_t i = 2; i < sizeof(Settings); i++) { - settings_buffer[i] ^= (config_xor_on_set +i); + settings_buffer[i] ^= (Web.config_xor_on_set +i); } } @@ -1686,7 +1806,7 @@ void HandleBackupConfiguration(void) SettingsBufferFree(); - Settings.cfg_crc = cfg_crc; // Restore crc in case savedata = 0 to make sure settings will be noted as changed + Settings.cfg_crc32 = cfg_crc32; // Restore crc in case savedata = 0 to make sure settings will be noted as changed } /*-------------------------------------------------------------------------------------------*/ @@ -1722,8 +1842,8 @@ void HandleRestoreConfiguration(void) WSContentSpaceButton(BUTTON_CONFIGURATION); WSContentStop(); - upload_error = 0; - upload_file_type = UPL_SETTINGS; + Web.upload_error = 0; + Web.upload_file_type = UPL_SETTINGS; } /*-------------------------------------------------------------------------------------------*/ @@ -1751,14 +1871,16 @@ void HandleInformation(void) WSContentSend_P(PSTR("}1" D_FLASH_WRITE_COUNT "}2%d at 0x%X"), Settings.save_flag, GetSettingsAddress()); WSContentSend_P(PSTR("}1" D_BOOT_COUNT "}2%d"), Settings.bootcount); WSContentSend_P(PSTR("}1" D_RESTART_REASON "}2%s"), GetResetReason().c_str()); - uint8_t maxfn = (devices_present > MAX_FRIENDLYNAMES) ? MAX_FRIENDLYNAMES : devices_present; - if (SONOFF_IFAN02 == my_module_type) { maxfn = 1; } + uint32_t maxfn = (devices_present > MAX_FRIENDLYNAMES) ? MAX_FRIENDLYNAMES : devices_present; +#ifdef USE_SONOFF_IFAN + if (IsModuleIfan()) { maxfn = 1; } +#endif // USE_SONOFF_IFAN for (uint32_t i = 0; i < maxfn; i++) { WSContentSend_P(PSTR("}1" D_FRIENDLY_NAME " %d}2%s"), i +1, Settings.friendlyname[i]); } WSContentSend_P(PSTR("}1}2 ")); // Empty line WSContentSend_P(PSTR("}1" D_AP "%d " D_SSID " (" D_RSSI ")}2%s (%d%%)"), Settings.sta_active +1, Settings.sta_ssid[Settings.sta_active], WifiGetRssiAsQuality(WiFi.RSSI())); - WSContentSend_P(PSTR("}1" D_HOSTNAME "}2%s%s"), my_hostname, (mdns_begun) ? ".local" : ""); + WSContentSend_P(PSTR("}1" D_HOSTNAME "}2%s%s"), my_hostname, (Wifi.mdns_begun) ? ".local" : ""); if (static_cast(WiFi.localIP()) != 0) { WSContentSend_P(PSTR("}1" D_IP_ADDRESS "}2%s"), WiFi.localIP().toString().c_str()); WSContentSend_P(PSTR("}1" D_GATEWAY "}2%s"), IPAddress(Settings.ip_address[1]).toString().c_str()); @@ -1846,8 +1968,8 @@ void HandleUpgradeFirmware(void) WSContentSpaceButton(BUTTON_MAIN); WSContentStop(); - upload_error = 0; - upload_file_type = UPL_TASMOTA; + Web.upload_error = 0; + Web.upload_file_type = UPL_TASMOTA; } void HandleUpgradeFirmwareStart(void) @@ -1891,36 +2013,49 @@ void HandleUploadDone(void) MqttRetryCounter(0); WSContentStart_P(S_INFORMATION); - if (!upload_error) { + if (!Web.upload_error) { WSContentSend_P(HTTP_SCRIPT_RELOAD_OTA); // Refesh main web ui after OTA upgrade } WSContentSendStyle(); WSContentSend_P(PSTR("
%s (%d) %s %d%%
" D_UPLOAD " " D_FAILED "

")); WSContentSend_P(PSTR("%06x'>" D_FAILED "


"), WebColor(COL_TEXT_WARNING)); #ifdef USE_RF_FLASH - if (upload_error < 14) { + if (Web.upload_error < 14) { #else - if (upload_error < 10) { + if (Web.upload_error < 10) { #endif - GetTextIndexed(error, sizeof(error), upload_error -1, kUploadErrors); + GetTextIndexed(error, sizeof(error), Web.upload_error -1, kUploadErrors); } else { - snprintf_P(error, sizeof(error), PSTR(D_UPLOAD_ERROR_CODE " %d"), upload_error); + snprintf_P(error, sizeof(error), PSTR(D_UPLOAD_ERROR_CODE " %d"), Web.upload_error); } WSContentSend_P(error); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_UPLOAD ": %s"), error); + DEBUG_CORE_LOG(PSTR("UPL: %s"), error); stop_flash_rotate = Settings.flag.stop_flash_rotate; } else { WSContentSend_P(PSTR("%06x'>" D_SUCCESSFUL "
"), WebColor(COL_TEXT_SUCCESS)); WSContentSend_P(HTTP_MSG_RSTRT); ShowWebSource(SRC_WEBGUI); +#ifdef USE_ARDUINO_SLAVE + if (ArduinoSlave_GetFlagFlashing()) { + restart_flag = 0; + } else { // It was a normal firmware file, or we are ready to restart device + restart_flag = 2; + } +#else restart_flag = 2; // Always restart to re-enable disabled features during update +#endif } SettingsBufferFree(); WSContentSend_P(PSTR("

")); WSContentSpaceButton(BUTTON_MAIN); WSContentStop(); +#ifdef USE_ARDUINO_SLAVE + if (ArduinoSlave_GetFlagFlashing()) { + ArduinoSlave_Flash(); + } +#endif } void HandleUploadLoop(void) @@ -1928,9 +2063,9 @@ void HandleUploadLoop(void) // Based on ESP8266HTTPUpdateServer.cpp uses ESP8266WebServer Parsing.cpp and Cores Updater.cpp (Update) bool _serialoutput = (LOG_LEVEL_DEBUG <= seriallog_level); - if (HTTP_USER == webserver_state) { return; } - if (upload_error) { - if (UPL_TASMOTA == upload_file_type) { Update.end(); } + if (HTTP_USER == Web.state) { return; } + if (Web.upload_error) { + if (UPL_TASMOTA == Web.upload_file_type) { Update.end(); } return; } @@ -1939,14 +2074,14 @@ void HandleUploadLoop(void) if (UPLOAD_FILE_START == upload.status) { restart_flag = 60; if (0 == upload.filename.c_str()[0]) { - upload_error = 1; // No file selected + Web.upload_error = 1; // No file selected return; } SettingsSave(1); // Free flash for upload AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_UPLOAD D_FILE " %s ..."), upload.filename.c_str()); - if (UPL_SETTINGS == upload_file_type) { + if (UPL_SETTINGS == Web.upload_file_type) { if (!SettingsBufferAlloc()) { - upload_error = 2; // Not enough space + Web.upload_error = 2; // Not enough space return; } } else { @@ -1966,76 +2101,84 @@ void HandleUploadLoop(void) // if (_serialoutput) Serial.println("Device still in UART update mode, perform powercycle"); // } - upload_error = 2; // Not enough space + Web.upload_error = 2; // Not enough space return; } } - upload_progress_dot_count = 0; - } else if (!upload_error && (UPLOAD_FILE_WRITE == upload.status)) { + Web.upload_progress_dot_count = 0; + } else if (!Web.upload_error && (UPLOAD_FILE_WRITE == upload.status)) { if (0 == upload.totalSize) { - if (UPL_SETTINGS == upload_file_type) { - config_block_count = 0; + if (UPL_SETTINGS == Web.upload_file_type) { + Web.config_block_count = 0; } else { #ifdef USE_RF_FLASH if ((SONOFF_BRIDGE == my_module_type) && (upload.buf[0] == ':')) { // Check if this is a RF bridge FW file Update.end(); // End esp8266 update session - upload_file_type = UPL_EFM8BB1; + Web.upload_file_type = UPL_EFM8BB1; - upload_error = SnfBrUpdateInit(); - if (upload_error != 0) { return; } + Web.upload_error = SnfBrUpdateInit(); + if (Web.upload_error != 0) { return; } } else #endif // USE_RF_FLASH +#ifdef USE_ARDUINO_SLAVE + if ((WEMOS == my_module_type) && (upload.buf[0] == ':')) { // Check if this is a ARDUINO SLAVE hex file + Update.end(); // End esp8266 update session + Web.upload_file_type = UPL_ARDUINOSLAVE; + Web.upload_error = ArduinoSlave_UpdateInit(); + if (Web.upload_error != 0) { return; } + } else +#endif { if (upload.buf[0] != 0xE9) { - upload_error = 3; // Magic byte is not 0xE9 + Web.upload_error = 3; // Magic byte is not 0xE9 return; } uint32_t bin_flash_size = ESP.magicFlashChipSize((upload.buf[3] & 0xf0) >> 4); if(bin_flash_size > ESP.getFlashChipRealSize()) { - upload_error = 4; // Program flash size is larger than real flash size + Web.upload_error = 4; // Program flash size is larger than real flash size return; } // upload.buf[2] = 3; // Force DOUT - ESP8285 } } } - if (UPL_SETTINGS == upload_file_type) { - if (!upload_error) { - if (upload.currentSize > (sizeof(Settings) - (config_block_count * HTTP_UPLOAD_BUFLEN))) { - upload_error = 9; // File too large + if (UPL_SETTINGS == Web.upload_file_type) { + if (!Web.upload_error) { + if (upload.currentSize > (sizeof(Settings) - (Web.config_block_count * HTTP_UPLOAD_BUFLEN))) { + Web.upload_error = 9; // File too large return; } - memcpy(settings_buffer + (config_block_count * HTTP_UPLOAD_BUFLEN), upload.buf, upload.currentSize); - config_block_count++; + memcpy(settings_buffer + (Web.config_block_count * HTTP_UPLOAD_BUFLEN), upload.buf, upload.currentSize); + Web.config_block_count++; } } #ifdef USE_RF_FLASH - else if (UPL_EFM8BB1 == upload_file_type) { + else if (UPL_EFM8BB1 == Web.upload_file_type) { if (efm8bb1_update != nullptr) { // We have carry over data since last write, i. e. a start but not an end ssize_t result = rf_glue_remnant_with_new_data_and_write(efm8bb1_update, upload.buf, upload.currentSize); free(efm8bb1_update); efm8bb1_update = nullptr; if (result != 0) { - upload_error = abs(result); // 2 = Not enough space, 8 = File invalid + Web.upload_error = abs(result); // 2 = Not enough space, 8 = File invalid return; } } ssize_t result = rf_search_and_write(upload.buf, upload.currentSize); if (result < 0) { - upload_error = abs(result); + Web.upload_error = abs(result); return; } else if (result > 0) { if ((size_t)result > upload.currentSize) { // Offset is larger than the buffer supplied, this should not happen - upload_error = 9; // File too large - Failed to decode RF firmware + Web.upload_error = 9; // File too large - Failed to decode RF firmware return; } // A remnant has been detected, allocate data for it plus a null termination byte size_t remnant_sz = upload.currentSize - result; efm8bb1_update = (uint8_t *) malloc(remnant_sz + 1); if (efm8bb1_update == nullptr) { - upload_error = 2; // Not enough space - Unable to allocate memory to store new RF firmware + Web.upload_error = 2; // Not enough space - Unable to allocate memory to store new RF firmware return; } memcpy(efm8bb1_update, upload.buf + result, remnant_sz); @@ -2044,37 +2187,43 @@ void HandleUploadLoop(void) } } #endif // USE_RF_FLASH +#ifdef USE_ARDUINO_SLAVE + else if (UPL_ARDUINOSLAVE == Web.upload_file_type) { + ArduinoSlave_WriteBuffer(upload.buf, upload.currentSize); + } +#endif else { // firmware - if (!upload_error && (Update.write(upload.buf, upload.currentSize) != upload.currentSize)) { - upload_error = 5; // Upload buffer miscompare + if (!Web.upload_error && (Update.write(upload.buf, upload.currentSize) != upload.currentSize)) { + Web.upload_error = 5; // Upload buffer miscompare return; } if (_serialoutput) { Serial.printf("."); - upload_progress_dot_count++; - if (!(upload_progress_dot_count % 80)) { Serial.println(); } + Web.upload_progress_dot_count++; + if (!(Web.upload_progress_dot_count % 80)) { Serial.println(); } } } - } else if(!upload_error && (UPLOAD_FILE_END == upload.status)) { - if (_serialoutput && (upload_progress_dot_count % 80)) { + } else if(!Web.upload_error && (UPLOAD_FILE_END == upload.status)) { + if (_serialoutput && (Web.upload_progress_dot_count % 80)) { Serial.println(); } - if (UPL_SETTINGS == upload_file_type) { - if (config_xor_on_set) { + if (UPL_SETTINGS == Web.upload_file_type) { + if (Web.config_xor_on_set) { for (uint32_t i = 2; i < sizeof(Settings); i++) { - settings_buffer[i] ^= (config_xor_on_set +i); + settings_buffer[i] ^= (Web.config_xor_on_set +i); } } bool valid_settings = false; unsigned long buffer_version = settings_buffer[11] << 24 | settings_buffer[10] << 16 | settings_buffer[9] << 8 | settings_buffer[8]; if (buffer_version > 0x06000000) { - uint16_t buffer_size = settings_buffer[3] << 8 | settings_buffer[2]; - uint16_t buffer_crc = settings_buffer[15] << 8 | settings_buffer[14]; - uint16_t crc = 0; - for (uint32_t i = 0; i < buffer_size; i++) { - if ((i < 14) || (i > 15)) { crc += settings_buffer[i]*(i+1); } // Skip crc + uint32_t buffer_size = settings_buffer[3] << 8 | settings_buffer[2]; + if (buffer_version > 0x0606000A) { + uint32_t buffer_crc32 = settings_buffer[4095] << 24 | settings_buffer[4094] << 16 | settings_buffer[4093] << 8 | settings_buffer[4092]; + valid_settings = (GetCfgCrc32(settings_buffer, buffer_size -4) == buffer_crc32); + } else { + uint16_t buffer_crc16 = settings_buffer[15] << 8 | settings_buffer[14]; + valid_settings = (GetCfgCrc16(settings_buffer, buffer_size) == buffer_crc16); } - valid_settings = (buffer_crc == crc); } else { valid_settings = (settings_buffer[0] == CONFIG_FILE_SIGN); } @@ -2084,31 +2233,38 @@ void HandleUploadLoop(void) Settings.version = buffer_version; // Restore version and auto upgrade after restart SettingsBufferFree(); } else { - upload_error = 8; // File invalid + Web.upload_error = 8; // File invalid return; } } #ifdef USE_RF_FLASH - else if (UPL_EFM8BB1 == upload_file_type) { + else if (UPL_EFM8BB1 == Web.upload_file_type) { // RF FW flash done - upload_file_type = UPL_TASMOTA; + Web.upload_file_type = UPL_TASMOTA; } #endif // USE_RF_FLASH +#ifdef USE_ARDUINO_SLAVE + else if (UPL_ARDUINOSLAVE == Web.upload_file_type) { + // Done writing the hex to SPI flash + ArduinoSlave_SetFlagFlashing(true); // So we know on upload success page if it needs to flash hex or do a normal restart + Web.upload_file_type = UPL_TASMOTA; + } +#endif else { if (!Update.end(true)) { // true to set the size to the current progress if (_serialoutput) { Update.printError(Serial); } - upload_error = 6; // Upload failed. Enable logging 3 + Web.upload_error = 6; // Upload failed. Enable logging 3 return; } } - if (!upload_error) { + if (!Web.upload_error) { AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_UPLOAD D_SUCCESSFUL " %u bytes. " D_RESTARTING), upload.totalSize); } } else if (UPLOAD_FILE_ABORTED == upload.status) { restart_flag = 0; MqttRetryCounter(0); - upload_error = 7; // Upload aborted - if (UPL_TASMOTA == upload_file_type) { Update.end(); } + Web.upload_error = 7; // Upload aborted + if (UPL_TASMOTA == Web.upload_file_type) { Update.end(); } } delay(0); } @@ -2131,23 +2287,23 @@ void HandleHttpCommand(void) AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_COMMAND)); - uint8_t valid = 1; + bool valid = true; if (Settings.web_password[0] != 0) { char tmp1[sizeof(Settings.web_password)]; WebGetArg("user", tmp1, sizeof(tmp1)); char tmp2[sizeof(Settings.web_password)]; WebGetArg("password", tmp2, sizeof(tmp2)); - if (!(!strcmp(tmp1, WEB_USERNAME) && !strcmp(tmp2, Settings.web_password))) { valid = 0; } + if (!(!strcmp(tmp1, WEB_USERNAME) && !strcmp(tmp2, Settings.web_password))) { valid = false; } } WSContentBegin(200, CT_JSON); if (valid) { - uint8_t curridx = web_log_index; + uint32_t curridx = web_log_index; String svalue = WebServer->arg("cmnd"); - if (svalue.length() && (svalue.length() < INPUT_BUFFER_SIZE)) { + if (svalue.length() && (svalue.length() < MQTT_MAX_PACKET_SIZE)) { ExecuteWebCommand((char*)svalue.c_str(), SRC_WEBCOMMAND); if (web_log_index != curridx) { - uint8_t counter = curridx; + uint32_t counter = curridx; WSContentSend_P(PSTR("{")); bool cflg = false; do { @@ -2167,6 +2323,7 @@ void HandleHttpCommand(void) } } counter++; + counter &= 0xFF; if (!counter) counter++; // Skip 0 as it is not allowed } while (counter != web_log_index); WSContentSend_P(PSTR("}")); @@ -2206,10 +2363,10 @@ void HandleConsole(void) void HandleConsoleRefresh(void) { bool cflg = true; - uint8_t counter = 0; // Initial start, should never be 0 again + uint32_t counter = 0; // Initial start, should never be 0 again String svalue = WebServer->arg("c1"); - if (svalue.length() && (svalue.length() < INPUT_BUFFER_SIZE)) { + if (svalue.length() && (svalue.length() < MQTT_MAX_PACKET_SIZE)) { AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_COMMAND "%s"), svalue.c_str()); ExecuteWebCommand((char*)svalue.c_str(), SRC_WEBCONSOLE); } @@ -2219,10 +2376,10 @@ void HandleConsoleRefresh(void) if (strlen(stmp)) { counter = atoi(stmp); } WSContentBegin(200, CT_PLAIN); - WSContentSend_P(PSTR("%d}1%d}1"), web_log_index, reset_web_log_flag); - if (!reset_web_log_flag) { + WSContentSend_P(PSTR("%d}1%d}1"), web_log_index, Web.reset_web_log_flag); + if (!Web.reset_web_log_flag) { counter = 0; - reset_web_log_flag = true; + Web.reset_web_log_flag = true; } if (counter != web_log_index) { if (!counter) { @@ -2241,6 +2398,7 @@ void HandleConsoleRefresh(void) cflg = true; } counter++; + counter &= 0xFF; if (!counter) { counter++; } // Skip log index 0 as it is not allowed } while (counter != web_log_index); } @@ -2252,7 +2410,7 @@ void HandleConsoleRefresh(void) void HandleNotFound(void) { -// AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP "Not fount (%s)"), WebServer->uri().c_str()); +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP "Not found (%s)"), WebServer->uri().c_str()); if (CaptivePortal()) { return; } // If captive portal redirect instead of displaying the error page. @@ -2313,7 +2471,7 @@ String UrlEncode(const String& text) encoded += hex[decodedChar & 0xF]; } */ - if (' ' == decodedChar) { + if ((' ' == decodedChar) || ('+' == decodedChar)) { encoded += '%'; encoded += hex[decodedChar >> 4]; encoded += hex[decodedChar & 0xF]; @@ -2362,7 +2520,7 @@ int WebSend(char *buffer) } url += command; // url = |http://192.168.178.86/cm?cmnd=POWER1 ON| -//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DBG: Uri |%s|"), url.c_str()); + DEBUG_CORE_LOG(PSTR("WEB: Uri |%s|"), url.c_str()); #if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_1) || defined(ARDUINO_ESP8266_RELEASE_2_4_2) HTTPClient http; @@ -2375,12 +2533,13 @@ int WebSend(char *buffer) int http_code = http.GET(); // Start connection and send HTTP header if (http_code > 0) { // http_code will be negative on error if (http_code == HTTP_CODE_OK || http_code == HTTP_CODE_MOVED_PERMANENTLY) { -/* +#ifdef USE_WEBSEND_RESPONSE // Return received data to the user - Adds 900+ bytes to the code - String result = http.getString(); // File found at server - may need lot of ram or trigger out of memory! - uint16_t j = 0; - for (uint32_t i = 0; i < result.length(); i++) { - char text = result.charAt(i); + const char* read = http.getString().c_str(); // File found at server - may need lot of ram or trigger out of memory! + uint32_t j = 0; + char text = '.'; + while (text != '\0') { + text = *read++; if (text > 31) { // Remove control characters like linefeed mqtt_data[j++] = text; if (j == sizeof(mqtt_data) -2) { break; } @@ -2388,7 +2547,13 @@ int WebSend(char *buffer) } mqtt_data[j] = '\0'; MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_WEBSEND)); -*/ +#ifdef USE_SCRIPT +extern uint8_t tasm_cmd_activ; + // recursive call must be possible in this case + tasm_cmd_activ=0; + XdrvRulesProcess(); +#endif // USE_SCRIPT +#endif // USE_WEBSEND_RESPONSE } status = 0; // No error - Done } else { @@ -2402,8 +2567,6 @@ int WebSend(char *buffer) return status; } -/*********************************************************************************************/ - bool JsonWebColor(const char* dataBuf) { // Default (light) @@ -2432,93 +2595,143 @@ bool JsonWebColor(const char* dataBuf) return true; } -enum WebCommands { CMND_WEBSERVER, CMND_WEBPASSWORD, CMND_WEBLOG, CMND_WEBREFRESH, CMND_WEBSEND, CMND_WEBCOLOR, CMND_EMULATION }; -const char kWebCommands[] PROGMEM = D_CMND_WEBSERVER "|" D_CMND_WEBPASSWORD "|" D_CMND_WEBLOG "|" D_CMND_WEBREFRESH "|" D_CMND_WEBSEND "|" D_CMND_WEBCOLOR "|" D_CMND_EMULATION ; -const char kWebSendStatus[] PROGMEM = D_JSON_DONE "|" D_JSON_WRONG_PARAMETERS "|" D_JSON_CONNECT_FAILED "|" D_JSON_HOST_NOT_FOUND ; +const char kWebSendStatus[] PROGMEM = D_JSON_DONE "|" D_JSON_WRONG_PARAMETERS "|" D_JSON_CONNECT_FAILED "|" D_JSON_HOST_NOT_FOUND "|" D_JSON_MEMORY_ERROR; -bool WebCommand(void) -{ - char command[CMDSZ]; - bool serviced = true; - - int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic, kWebCommands); - if (-1 == command_code) { - serviced = false; // Unknown command - } - if (CMND_WEBSERVER == command_code) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 2)) { Settings.webserver = XdrvMailbox.payload; } - if (Settings.webserver) { - Response_P(PSTR("{\"" D_CMND_WEBSERVER "\":\"" D_JSON_ACTIVE_FOR " %s " D_JSON_ON_DEVICE " %s " D_JSON_WITH_IP_ADDRESS " %s\"}"), - (2 == Settings.webserver) ? D_ADMIN : D_USER, my_hostname, WiFi.localIP().toString().c_str()); - } else { - Response_P(S_JSON_COMMAND_SVALUE, command, GetStateText(0)); - } - } - else if (CMND_WEBPASSWORD == command_code) { - if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.web_password))) { - strlcpy(Settings.web_password, (SC_CLEAR == Shortcut(XdrvMailbox.data)) ? "" : (SC_DEFAULT == Shortcut(XdrvMailbox.data)) ? WEB_PASSWORD : XdrvMailbox.data, sizeof(Settings.web_password)); - Response_P(S_JSON_COMMAND_SVALUE, command, Settings.web_password); - } else { - Response_P(S_JSON_COMMAND_ASTERISK, command); - } - } - else if (CMND_WEBLOG == command_code) { - if ((XdrvMailbox.payload >= LOG_LEVEL_NONE) && (XdrvMailbox.payload <= LOG_LEVEL_ALL)) { Settings.weblog_level = XdrvMailbox.payload; } - Response_P(S_JSON_COMMAND_NVALUE, command, Settings.weblog_level); - } - else if (CMND_WEBREFRESH == command_code) { - if ((XdrvMailbox.payload > 999) && (XdrvMailbox.payload <= 10000)) { Settings.web_refresh = XdrvMailbox.payload; } - Response_P(S_JSON_COMMAND_NVALUE, command, Settings.web_refresh); - } - else if (CMND_WEBSEND == command_code) { - if (XdrvMailbox.data_len > 0) { - uint8_t result = WebSend(XdrvMailbox.data); - char stemp1[20]; - Response_P(S_JSON_COMMAND_SVALUE, command, GetTextIndexed(stemp1, sizeof(stemp1), result, kWebSendStatus)); - } - } - else if (CMND_WEBCOLOR == command_code) { - if (XdrvMailbox.data_len > 0) { - if (strstr(XdrvMailbox.data, "{") == nullptr) { // If no JSON it must be parameter - if ((XdrvMailbox.data_len > 3) && (XdrvMailbox.index > 0) && (XdrvMailbox.index <= COL_LAST)) { - WebHexCode(XdrvMailbox.index -1, XdrvMailbox.data); - } - else if (0 == XdrvMailbox.payload) { - SettingsDefaultWebColor(); - } - } - else { - JsonWebColor(XdrvMailbox.data); - } - } - Response_P(PSTR("{\"" D_CMND_WEBCOLOR "\":[")); - for (uint32_t i = 0; i < COL_LAST; i++) { - if (i) { ResponseAppend_P(PSTR(",")); } - ResponseAppend_P(PSTR("\"#%06x\""), WebColor(i)); - } - ResponseAppend_P(PSTR("]}")); - } +const char kWebCommands[] PROGMEM = "|" // No prefix #ifdef USE_EMULATION - else if (CMND_EMULATION == command_code) { + D_CMND_EMULATION "|" +#endif +#ifdef USE_SENDMAIL + D_CMND_SENDMAIL "|" +#endif + D_CMND_WEBSERVER "|" D_CMND_WEBPASSWORD "|" D_CMND_WEBLOG "|" D_CMND_WEBREFRESH "|" D_CMND_WEBSEND "|" D_CMND_WEBCOLOR "|" D_CMND_WEBSENSOR; + +void (* const WebCommand[])(void) PROGMEM = { +#ifdef USE_EMULATION + &CmndEmulation, +#endif +#ifdef USE_SENDMAIL + &CmndSendmail, +#endif + &CmndWebServer, &CmndWebPassword, &CmndWeblog, &CmndWebRefresh, &CmndWebSend, &CmndWebColor, &CmndWebSensor }; + +/*********************************************************************************************\ + * Commands +\*********************************************************************************************/ + +#ifdef USE_EMULATION +void CmndEmulation(void) +{ #if defined(USE_EMULATION_WEMO) && defined(USE_EMULATION_HUE) - if ((XdrvMailbox.payload >= EMUL_NONE) && (XdrvMailbox.payload < EMUL_MAX)) { + if ((XdrvMailbox.payload >= EMUL_NONE) && (XdrvMailbox.payload < EMUL_MAX)) { #else #ifndef USE_EMULATION_WEMO - if ((EMUL_NONE == XdrvMailbox.payload) || (EMUL_HUE == XdrvMailbox.payload)) { + if ((EMUL_NONE == XdrvMailbox.payload) || (EMUL_HUE == XdrvMailbox.payload)) { #endif #ifndef USE_EMULATION_HUE - if ((EMUL_NONE == XdrvMailbox.payload) || (EMUL_WEMO == XdrvMailbox.payload)) { + if ((EMUL_NONE == XdrvMailbox.payload) || (EMUL_WEMO == XdrvMailbox.payload)) { #endif #endif - Settings.flag2.emulation = XdrvMailbox.payload; - restart_flag = 2; - } - Response_P(S_JSON_COMMAND_NVALUE, command, Settings.flag2.emulation); + Settings.flag2.emulation = XdrvMailbox.payload; + restart_flag = 2; } + ResponseCmndNumber(Settings.flag2.emulation); +} #endif // USE_EMULATION - else serviced = false; // Unknown command - return serviced; +#ifdef USE_SENDMAIL +void CmndSendmail(void) +{ + if (XdrvMailbox.data_len > 0) { + uint8_t result = SendMail(XdrvMailbox.data); + char stemp1[20]; + ResponseCmndChar(GetTextIndexed(stemp1, sizeof(stemp1), result, kWebSendStatus)); + } +} +#endif // USE_SENDMAIL + + +void CmndWebServer(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 2)) { + Settings.webserver = XdrvMailbox.payload; + } + if (Settings.webserver) { + Response_P(PSTR("{\"" D_CMND_WEBSERVER "\":\"" D_JSON_ACTIVE_FOR " %s " D_JSON_ON_DEVICE " %s " D_JSON_WITH_IP_ADDRESS " %s\"}"), + (2 == Settings.webserver) ? D_ADMIN : D_USER, my_hostname, WiFi.localIP().toString().c_str()); + } else { + ResponseCmndStateText(0); + } +} + +void CmndWebPassword(void) +{ + if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.web_password))) { + strlcpy(Settings.web_password, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? WEB_PASSWORD : XdrvMailbox.data, sizeof(Settings.web_password)); + ResponseCmndChar(Settings.web_password); + } else { + Response_P(S_JSON_COMMAND_ASTERISK, XdrvMailbox.command); + } +} + +void CmndWeblog(void) +{ + if ((XdrvMailbox.payload >= LOG_LEVEL_NONE) && (XdrvMailbox.payload <= LOG_LEVEL_ALL)) { + Settings.weblog_level = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.weblog_level); +} + +void CmndWebRefresh(void) +{ + if ((XdrvMailbox.payload > 999) && (XdrvMailbox.payload <= 10000)) { + Settings.web_refresh = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.web_refresh); +} + +void CmndWebSend(void) +{ + if (XdrvMailbox.data_len > 0) { + uint32_t result = WebSend(XdrvMailbox.data); + char stemp1[20]; + ResponseCmndChar(GetTextIndexed(stemp1, sizeof(stemp1), result, kWebSendStatus)); + } +} + +void CmndWebColor(void) +{ + if (XdrvMailbox.data_len > 0) { + if (strstr(XdrvMailbox.data, "{") == nullptr) { // If no JSON it must be parameter + if ((XdrvMailbox.data_len > 3) && (XdrvMailbox.index > 0) && (XdrvMailbox.index <= COL_LAST)) { + WebHexCode(XdrvMailbox.index -1, XdrvMailbox.data); + } + else if (0 == XdrvMailbox.payload) { + SettingsDefaultWebColor(); + } + } + else { + JsonWebColor(XdrvMailbox.data); + } + } + Response_P(PSTR("{\"" D_CMND_WEBCOLOR "\":[")); + for (uint32_t i = 0; i < COL_LAST; i++) { + if (i) { ResponseAppend_P(PSTR(",")); } + ResponseAppend_P(PSTR("\"#%06x\""), WebColor(i)); + } + ResponseAppend_P(PSTR("]}")); +} + +void CmndWebSensor(void) +{ + if (XdrvMailbox.index < MAX_XSNS_DRIVERS) { + if (XdrvMailbox.payload >= 0) { + bitWrite(Settings.sensors[XdrvMailbox.index / 32], XdrvMailbox.index % 32, XdrvMailbox.payload &1); + } + } + Response_P(PSTR("{\"" D_CMND_WEBSENSOR "\":")); + XsnsSensorState(); + ResponseJsonEnd(); } /*********************************************************************************************\ @@ -2537,7 +2750,7 @@ bool Xdrv01(uint8_t function) #endif // USE_EMULATION break; case FUNC_COMMAND: - result = WebCommand(); + result = DecodeCommand(kWebCommands, WebCommand); break; } return result; diff --git a/sonoff/xdrv_02_mqtt.ino b/sonoff/xdrv_02_mqtt.ino index 0269f378d..edc844448 100644 --- a/sonoff/xdrv_02_mqtt.ino +++ b/sonoff/xdrv_02_mqtt.ino @@ -19,6 +19,8 @@ #define XDRV_02 2 +// #define DEBUG_DUMP_TLS // allow dumping of TLS Flash keys + #ifdef USE_MQTT_TLS #include "WiFiClientSecureLightBearSSL.h" BearSSL::WiFiClientSecure_light *tlsClient; @@ -26,53 +28,68 @@ WiFiClient EspClient; // Wifi Client #endif -enum MqttCommands { - CMND_MQTTHOST, CMND_MQTTPORT, CMND_MQTTRETRY, CMND_STATETEXT, CMND_MQTTFINGERPRINT, CMND_MQTTCLIENT, - CMND_MQTTUSER, CMND_MQTTPASSWORD, CMND_FULLTOPIC, CMND_PREFIX, CMND_GROUPTOPIC, CMND_TOPIC, CMND_PUBLISH, - CMND_BUTTONTOPIC, CMND_SWITCHTOPIC, CMND_BUTTONRETAIN, CMND_SWITCHRETAIN, CMND_POWERRETAIN, CMND_SENSORRETAIN }; -const char kMqttCommands[] PROGMEM = - D_CMND_MQTTHOST "|" D_CMND_MQTTPORT "|" D_CMND_MQTTRETRY "|" D_CMND_STATETEXT "|" D_CMND_MQTTFINGERPRINT "|" D_CMND_MQTTCLIENT "|" - D_CMND_MQTTUSER "|" D_CMND_MQTTPASSWORD "|" D_CMND_FULLTOPIC "|" D_CMND_PREFIX "|" D_CMND_GROUPTOPIC "|" D_CMND_TOPIC "|" D_CMND_PUBLISH "|" +const char kMqttCommands[] PROGMEM = "|" // No prefix +#if defined(USE_MQTT_TLS) && !defined(USE_MQTT_TLS_CA_CERT) + D_CMND_MQTTFINGERPRINT "|" +#endif +#if !defined(USE_MQTT_TLS) || !defined(USE_MQTT_AWS_IOT) // user and password are disabled with AWS IoT + D_CMND_MQTTUSER "|" D_CMND_MQTTPASSWORD "|" +#endif +#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) + D_CMND_TLSKEY "|" +#endif + D_CMND_MQTTHOST "|" D_CMND_MQTTPORT "|" D_CMND_MQTTRETRY "|" D_CMND_STATETEXT "|" D_CMND_MQTTCLIENT "|" + D_CMND_FULLTOPIC "|" D_CMND_PREFIX "|" D_CMND_GROUPTOPIC "|" D_CMND_TOPIC "|" D_CMND_PUBLISH "|" D_CMND_MQTTLOG "|" D_CMND_BUTTONTOPIC "|" D_CMND_SWITCHTOPIC "|" D_CMND_BUTTONRETAIN "|" D_CMND_SWITCHRETAIN "|" D_CMND_POWERRETAIN "|" D_CMND_SENSORRETAIN ; -IPAddress mqtt_host_addr; // MQTT host IP address -uint32_t mqtt_host_hash = 0; // MQTT host name hash +void (* const MqttCommand[])(void) PROGMEM = { +#if defined(USE_MQTT_TLS) && !defined(USE_MQTT_TLS_CA_CERT) + &CmndMqttFingerprint, +#endif +#if !defined(USE_MQTT_TLS) || !defined(USE_MQTT_AWS_IOT) // user and password are disabled with AWS IoT + &CmndMqttUser, &CmndMqttPassword, +#endif +#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) + &CmndTlsKey, +#endif + &CmndMqttHost, &CmndMqttPort, &CmndMqttRetry, &CmndStateText, &CmndMqttClient, + &CmndFullTopic, &CmndPrefix, &CmndGroupTopic, &CmndTopic, &CmndPublish, &CmndMqttlog, + &CmndButtonTopic, &CmndSwitchTopic, &CmndButtonRetain, &CmndSwitchRetain, &CmndPowerRetain, &CmndSensorRetain }; -uint16_t mqtt_connect_count = 0; // MQTT re-connect count -uint16_t mqtt_retry_counter = 1; // MQTT connection retry counter -uint8_t mqtt_initial_connection_state = 2; // MQTT connection messages state -bool mqtt_connected = false; // MQTT virtual connection status -bool mqtt_allowed = false; // MQTT enabled and parameters valid +struct MQTT { + uint16_t connect_count = 0; // MQTT re-connect count + uint16_t retry_counter = 1; // MQTT connection retry counter + uint8_t initial_connection_state = 2; // MQTT connection messages state + bool connected = false; // MQTT virtual connection status + bool allowed = false; // MQTT enabled and parameters valid +} Mqtt; #ifdef USE_MQTT_TLS - -// see https://stackoverflow.com/questions/6357031/how-do-you-convert-a-byte-array-to-a-hexadecimal-string-in-c -void to_hex(unsigned char * in, size_t insz, char * out, size_t outsz) { - unsigned char * pin = in; - static const char * hex = "0123456789ABCDEF"; - char * pout = out; - for (; pin < in+insz; pout +=3, pin++) { - pout[0] = hex[(*pin>>4) & 0xF]; - pout[1] = hex[ *pin & 0xF]; - pout[2] = ' '; - if (pout + 3 - out > outsz){ - /* Better to truncate output string than overflow buffer */ - /* it would be still better to either return a status */ - /* or ensure the target buffer is large enough and it never happen */ - break; - } - } - pout[-1] = 0; -} - #ifdef USE_MQTT_AWS_IOT -namespace aws_iot_privkey { - // this is where the Private Key and Certificate are stored - extern const br_ec_private_key *AWS_IoT_Private_Key; - extern const br_x509_certificate *AWS_IoT_Client_Certificate; -} -#endif +#include + +const br_ec_private_key *AWS_IoT_Private_Key = nullptr; +const br_x509_certificate *AWS_IoT_Client_Certificate = nullptr; + +class tls_entry_t { +public: + uint32_t name; // simple 4 letters name. Currently 'skey', 'crt ', 'crt1', 'crt2' + uint16_t start; // start offset + uint16_t len; // len of object +}; // 8 bytes + +const static uint32_t TLS_NAME_SKEY = 0x2079656B; // 'key ' little endian +const static uint32_t TLS_NAME_CRT = 0x20747263; // 'crt ' little endian + +class tls_dir_t { +public: + tls_entry_t entry[4]; // 4 entries max, only 4 used today, for future use +}; // 4*8 = 64 bytes + +tls_dir_t tls_dir; // memory copy of tls_dir from flash + +#endif // USE_MQTT_AWS_IOT // A typical AWS IoT endpoint is 50 characters long, it does not fit // in MqttHost field (32 chars). We need to concatenate both MqttUser and MqttHost @@ -90,8 +107,73 @@ bool is_fingerprint_mono_value(uint8_t finger[20], uint8_t value) { return true; } +#ifdef USE_MQTT_AWS_IOT +void setLongMqttHost(const char *mqtt_host) { + if (strlen(mqtt_host) <= sizeof(Settings.mqtt_host)) { + strlcpy(Settings.mqtt_host, mqtt_host, sizeof(Settings.mqtt_host)); + Settings.mqtt_user[0] = 0; + } else { + // need to split in mqtt_user first then mqtt_host + strlcpy(Settings.mqtt_user, mqtt_host, sizeof(Settings.mqtt_user)); + strlcpy(Settings.mqtt_host, &mqtt_host[sizeof(Settings.mqtt_user)-1], sizeof(Settings.mqtt_host)); + } + strlcpy(AWS_endpoint, mqtt_host, sizeof(AWS_endpoint)); +} +#endif // USE_MQTT_AWS_IOT + #endif // USE_MQTT_TLS +void MakeValidMqtt(uint32_t option, char* str) +{ +// option 0 = replace by underscore +// option 1 = delete character + uint32_t i = 0; + while (str[i] > 0) { +// if ((str[i] == '/') || (str[i] == '+') || (str[i] == '#') || (str[i] == ' ')) { + if ((str[i] == '+') || (str[i] == '#') || (str[i] == ' ')) { + if (option) { + uint32_t j = i; + while (str[j] > 0) { + str[j] = str[j +1]; + j++; + } + i--; + } else { + str[i] = '_'; + } + } + i++; + } +} + +#ifdef USE_DISCOVERY +#ifdef MQTT_HOST_DISCOVERY +void MqttDiscoverServer(void) +{ + if (!Wifi.mdns_begun) { return; } + + int n = MDNS.queryService("mqtt", "tcp"); // Search for mqtt service + + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MDNS D_QUERY_DONE " %d"), n); + + if (n > 0) { + uint32_t i = 0; // If the hostname isn't set, use the first record found. +#ifdef MDNS_HOSTNAME + for (i = n; i > 0; i--) { // Search from last to first and use first if not found + if (!strcmp(MDNS.hostname(i).c_str(), MDNS_HOSTNAME)) { + break; // Stop at matching record + } + } +#endif // MDNS_HOSTNAME + snprintf_P(Settings.mqtt_host, sizeof(Settings.mqtt_host), MDNS.IP(i).toString().c_str()); + Settings.mqtt_port = MDNS.port(i); + + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MDNS D_MQTT_SERVICE_FOUND " %s, " D_IP_ADDRESS " %s, " D_PORT " %d"), MDNS.hostname(i).c_str(), Settings.mqtt_host, Settings.mqtt_port); + } +} +#endif // MQTT_HOST_DISCOVERY +#endif // USE_DISCOVERY + /*********************************************************************************************\ * MQTT driver specific code need to provide the following functions: * @@ -114,16 +196,17 @@ PubSubClient MqttClient; PubSubClient MqttClient(EspClient); #endif - -void MqttInit(void) { +void MqttInit(void) +{ #ifdef USE_MQTT_TLS tlsClient = new BearSSL::WiFiClientSecure_light(1024,1024); #ifdef USE_MQTT_AWS_IOT - snprintf(AWS_endpoint, sizeof(AWS_endpoint), PSTR("%s%s"), Settings.mqtt_user, Settings.mqtt_host); + snprintf_P(AWS_endpoint, sizeof(AWS_endpoint), PSTR("%s%s"), Settings.mqtt_user, Settings.mqtt_host); - tlsClient->setClientECCert(aws_iot_privkey::AWS_IoT_Client_Certificate, - aws_iot_privkey::AWS_IoT_Private_Key, + loadTlsDir(); // load key and certificate data from Flash + tlsClient->setClientECCert(AWS_IoT_Client_Certificate, + AWS_IoT_Private_Key, 0xFFFF /* all usages, don't care */, 0); #endif @@ -139,7 +222,6 @@ void MqttInit(void) { #endif // USE_MQTT_TLS } - bool MqttIsConnected(void) { return MqttClient.connected(); @@ -169,39 +251,55 @@ bool MqttPublishLib(const char* topic, bool retained) return result; } -/*********************************************************************************************/ - -#ifdef USE_DISCOVERY -#ifdef MQTT_HOST_DISCOVERY -void MqttDiscoverServer(void) +void MqttDataHandler(char* mqtt_topic, uint8_t* mqtt_data, unsigned int data_len) { - if (!mdns_begun) { return; } +#ifdef USE_DEBUG_DRIVER + ShowFreeMem(PSTR("MqttDataHandler")); +#endif - int n = MDNS.queryService("mqtt", "tcp"); // Search for mqtt service + // Do not allow more data than would be feasable within stack space + if (data_len >= MQTT_MAX_PACKET_SIZE) { return; } - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MDNS D_QUERY_DONE " %d"), n); - - if (n > 0) { - uint32_t i = 0; // If the hostname isn't set, use the first record found. -#ifdef MDNS_HOSTNAME - for (i = n; i > 0; i--) { // Search from last to first and use first if not found - if (!strcmp(MDNS.hostname(i).c_str(), MDNS_HOSTNAME)) { - break; // Stop at matching record + // Do not execute multiple times if Prefix1 equals Prefix2 + if (!strcmp(Settings.mqtt_prefix[0], Settings.mqtt_prefix[1])) { + char *str = strstr(mqtt_topic, Settings.mqtt_prefix[0]); + if ((str == mqtt_topic) && mqtt_cmnd_publish) { + if (mqtt_cmnd_publish > 3) { + mqtt_cmnd_publish -= 3; + } else { + mqtt_cmnd_publish = 0; } + return; } -#endif // MDNS_HOSTNAME - snprintf_P(Settings.mqtt_host, sizeof(Settings.mqtt_host), MDNS.IP(i).toString().c_str()); - Settings.mqtt_port = MDNS.port(i); - - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MDNS D_MQTT_SERVICE_FOUND " %s, " D_IP_ADDRESS " %s, " D_PORT " %d"), MDNS.hostname(i).c_str(), Settings.mqtt_host, Settings.mqtt_port); } + + // Save MQTT data ASAP as it's data is discarded by PubSubClient with next publish as used in MQTTlog + char topic[TOPSZ]; + strlcpy(topic, mqtt_topic, sizeof(topic)); + mqtt_data[data_len] = 0; + char data[data_len +1]; + memcpy(data, mqtt_data, sizeof(data)); + + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_MQTT D_RECEIVED_TOPIC " \"%s\", " D_DATA_SIZE " %d, " D_DATA " \"%s\""), topic, data_len, data); +// if (LOG_LEVEL_DEBUG_MORE <= seriallog_level) { Serial.println(data); } + + // MQTT pre-processing + XdrvMailbox.index = strlen(topic); + XdrvMailbox.data_len = data_len; + XdrvMailbox.topic = topic; + XdrvMailbox.data = (char*)data; + if (XdrvCall(FUNC_MQTT_DATA)) { return; } + + ShowSource(SRC_MQTT); + + CommandHandler(topic, data, data_len); } -#endif // MQTT_HOST_DISCOVERY -#endif // USE_DISCOVERY + +/*********************************************************************************************/ void MqttRetryCounter(uint8_t value) { - mqtt_retry_counter = value; + Mqtt.retry_counter = value; } void MqttSubscribe(const char *topic) @@ -216,6 +314,35 @@ void MqttUnsubscribe(const char *topic) MqttUnsubscribeLib(topic); } +void MqttPublishLogging(const char *mxtime) +{ + if (Settings.flag.mqtt_enabled) { + if (MqttIsConnected()) { + + char saved_mqtt_data[MESSZ]; + memcpy(saved_mqtt_data, mqtt_data, sizeof(saved_mqtt_data)); +// ResponseTime_P(PSTR(",\"Log\":{\"%s\"}}"), log_data); // Will fail as some messages contain JSON + Response_P(PSTR("%s%s"), mxtime, log_data); // No JSON and ugly!! + + char romram[33]; + char stopic[TOPSZ]; + snprintf_P(romram, sizeof(romram), PSTR("LOGGING")); + GetTopic_P(stopic, STAT, mqtt_topic, romram); + + char *me; + if (!strcmp(Settings.mqtt_prefix[0], Settings.mqtt_prefix[1])) { + me = strstr(stopic, Settings.mqtt_prefix[0]); + if (me == stopic) { + mqtt_cmnd_publish += 3; + } + } + MqttPublishLib(stopic, false); + + memcpy(mqtt_data, saved_mqtt_data, sizeof(saved_mqtt_data)); + } + } +} + void MqttPublishDirect(const char* topic, bool retained) { char sretained[CMDSZ]; @@ -276,7 +403,7 @@ void MqttPublish(const char* topic) MqttPublish(topic, false); } -void MqttPublishPrefixTopic_P(uint8_t prefix, const char* subtopic, bool retained) +void MqttPublishPrefixTopic_P(uint32_t prefix, const char* subtopic, bool retained) { /* prefix 0 = cmnd using subtopic * prefix 1 = stat using subtopic @@ -297,20 +424,21 @@ void MqttPublishPrefixTopic_P(uint8_t prefix, const char* subtopic, bool retaine MqttPublish(stopic, retained); } -void MqttPublishPrefixTopic_P(uint8_t prefix, const char* subtopic) +void MqttPublishPrefixTopic_P(uint32_t prefix, const char* subtopic) { MqttPublishPrefixTopic_P(prefix, subtopic, false); } -void MqttPublishPowerState(uint8_t device) +void MqttPublishPowerState(uint32_t device) { char stopic[TOPSZ]; char scommand[33]; if ((device < 1) || (device > devices_present)) { device = 1; } - if ((SONOFF_IFAN02 == my_module_type) && (device > 1)) { - if (GetFanspeed() < MAX_FAN_SPEED) { // 4 occurs when fanspeed is 3 and RC button 2 is pressed +#ifdef USE_SONOFF_IFAN + if (IsModuleIfan() && (device > 1)) { + if (GetFanspeed() < MaxFanspeed()) { // 4 occurs when fanspeed is 3 and RC button 2 is pressed #ifdef USE_DOMOTICZ DomoticzUpdateFanState(); // RC Button feedback #endif // USE_DOMOTICZ @@ -320,6 +448,7 @@ void MqttPublishPowerState(uint8_t device) MqttPublish(stopic); } } else { +#endif // USE_SONOFF_IFAN GetPowerDevice(scommand, device, sizeof(scommand), Settings.flag.device_index_enable); GetTopic_P(stopic, STAT, mqtt_topic, (Settings.flag.mqtt_response) ? scommand : S_RSLT_RESULT); Response_P(S_JSON_COMMAND_SVALUE, scommand, GetStateText(bitRead(power, device -1))); @@ -328,18 +457,22 @@ void MqttPublishPowerState(uint8_t device) GetTopic_P(stopic, STAT, mqtt_topic, scommand); Response_P(GetStateText(bitRead(power, device -1))); MqttPublish(stopic, Settings.flag.mqtt_power_retain); +#ifdef USE_SONOFF_IFAN } +#endif // USE_SONOFF_IFAN } void MqttPublishAllPowerState() { for (uint32_t i = 1; i <= devices_present; i++) { MqttPublishPowerState(i); - if (SONOFF_IFAN02 == my_module_type) { break; } // Report status of light relay only +#ifdef USE_SONOFF_IFAN + if (IsModuleIfan()) { break; } // Report status of light relay only +#endif // USE_SONOFF_IFAN } } -void MqttPublishPowerBlinkState(uint8_t device) +void MqttPublishPowerBlinkState(uint32_t device) { char scommand[33]; @@ -356,20 +489,20 @@ void MqttPublishPowerBlinkState(uint8_t device) uint16_t MqttConnectCount() { - return mqtt_connect_count; + return Mqtt.connect_count; } void MqttDisconnected(int state) { - mqtt_connected = false; - mqtt_retry_counter = Settings.mqtt_retry; + Mqtt.connected = false; + Mqtt.retry_counter = Settings.mqtt_retry; MqttClient.disconnect(); #if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT D_CONNECT_FAILED_TO " %s:%d, rc %d. " D_RETRY_IN " %d " D_UNIT_SECOND), AWS_endpoint, Settings.mqtt_port, state, mqtt_retry_counter); + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT D_CONNECT_FAILED_TO " %s:%d, rc %d. " D_RETRY_IN " %d " D_UNIT_SECOND), AWS_endpoint, Settings.mqtt_port, state, Mqtt.retry_counter); #else - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT D_CONNECT_FAILED_TO " %s:%d, rc %d. " D_RETRY_IN " %d " D_UNIT_SECOND), Settings.mqtt_host, Settings.mqtt_port, state, mqtt_retry_counter); + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT D_CONNECT_FAILED_TO " %s:%d, rc %d. " D_RETRY_IN " %d " D_UNIT_SECOND), Settings.mqtt_host, Settings.mqtt_port, state, Mqtt.retry_counter); #endif rules_flag.mqtt_disconnected = 1; } @@ -378,11 +511,11 @@ void MqttConnected(void) { char stopic[TOPSZ]; - if (mqtt_allowed) { + if (Mqtt.allowed) { AddLog_P(LOG_LEVEL_INFO, S_LOG_MQTT, PSTR(D_CONNECTED)); - mqtt_connected = true; - mqtt_retry_counter = 0; - mqtt_connect_count++; + Mqtt.connected = true; + Mqtt.retry_counter = 0; + Mqtt.connect_count++; GetTopic_P(stopic, TELE, mqtt_topic, S_LWT); Response_P(PSTR(D_ONLINE)); @@ -404,7 +537,7 @@ void MqttConnected(void) XdrvCall(FUNC_MQTT_SUBSCRIBE); } - if (mqtt_initial_connection_state) { + if (Mqtt.initial_connection_state) { Response_P(PSTR("{\"" D_CMND_MODULE "\":\"%s\",\"" D_JSON_VERSION "\":\"%s%s\",\"" D_JSON_FALLBACKTOPIC "\":\"%s\",\"" D_CMND_GROUPTOPIC "\":\"%s\"}"), ModuleName().c_str(), my_version, my_image, GetFallbackTopic_P(stopic, CMND, ""), Settings.mqtt_grptopic); MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_INFO "1")); @@ -422,7 +555,7 @@ void MqttConnected(void) rules_flag.system_boot = 1; XdrvCall(FUNC_MQTT_INIT); } - mqtt_initial_connection_state = 0; + Mqtt.initial_connection_state = 0; global_state.mqtt_down = 0; if (Settings.flag.mqtt_enabled) { @@ -434,18 +567,24 @@ void MqttReconnect(void) { char stopic[TOPSZ]; - mqtt_allowed = Settings.flag.mqtt_enabled; - if (mqtt_allowed) { + Mqtt.allowed = Settings.flag.mqtt_enabled; + if (Mqtt.allowed) { #ifdef USE_DISCOVERY #ifdef MQTT_HOST_DISCOVERY MqttDiscoverServer(); #endif // MQTT_HOST_DISCOVERY #endif // USE_DISCOVERY if (!strlen(Settings.mqtt_host) || !Settings.mqtt_port) { - mqtt_allowed = false; + Mqtt.allowed = false; } +#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) + // don't enable MQTT for AWS IoT if Private Key or Certificate are not set + if (!AWS_IoT_Private_Key || !AWS_IoT_Client_Certificate) { + Mqtt.allowed = false; + } +#endif } - if (!mqtt_allowed) { + if (!Mqtt.allowed) { MqttConnected(); return; } @@ -456,8 +595,8 @@ void MqttReconnect(void) AddLog_P(LOG_LEVEL_INFO, S_LOG_MQTT, PSTR(D_ATTEMPTING_CONNECTION)); - mqtt_connected = false; - mqtt_retry_counter = Settings.mqtt_retry; + Mqtt.connected = false; + Mqtt.retry_counter = Settings.mqtt_retry; global_state.mqtt_down = 1; char *mqtt_user = nullptr; @@ -476,13 +615,16 @@ void MqttReconnect(void) MqttClient.setClient(EspClient); #endif - if (2 == mqtt_initial_connection_state) { // Executed once just after power on and wifi is connected - - mqtt_initial_connection_state = 1; + if (2 == Mqtt.initial_connection_state) { // Executed once just after power on and wifi is connected + Mqtt.initial_connection_state = 1; } MqttClient.setCallback(MqttDataHandler); #if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) + // re-assign private keys in case it was updated in between + tlsClient->setClientECCert(AWS_IoT_Client_Certificate, + AWS_IoT_Private_Key, + 0xFFFF /* all usages, don't care */, 0); MqttClient.setServer(AWS_endpoint, Settings.mqtt_port); #else MqttClient.setServer(Settings.mqtt_host, Settings.mqtt_port); @@ -515,7 +657,7 @@ void MqttReconnect(void) #ifndef USE_MQTT_TLS_CA_CERT // don't bother with fingerprints if using CA validation // create a printable version of the fingerprint received char buf_fingerprint[64]; - to_hex((unsigned char *)tlsClient->getRecvPubKeyFingerprint(), 20, buf_fingerprint, 64); + ToHex_P((unsigned char *)tlsClient->getRecvPubKeyFingerprint(), 20, buf_fingerprint, sizeof(buf_fingerprint), ' '); AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_MQTT "Server fingerprint: %s"), buf_fingerprint); if (learn_fingerprint1 || learn_fingerprint2) { @@ -557,280 +699,482 @@ void MqttCheck(void) if (Settings.flag.mqtt_enabled) { if (!MqttIsConnected()) { global_state.mqtt_down = 1; - if (!mqtt_retry_counter) { + if (!Mqtt.retry_counter) { #ifdef USE_DISCOVERY #ifdef MQTT_HOST_DISCOVERY - if (!strlen(Settings.mqtt_host) && !mdns_begun) { return; } + if (!strlen(Settings.mqtt_host) && !Wifi.mdns_begun) { return; } #endif // MQTT_HOST_DISCOVERY #endif // USE_DISCOVERY MqttReconnect(); } else { - mqtt_retry_counter--; + Mqtt.retry_counter--; } } else { global_state.mqtt_down = 0; } } else { global_state.mqtt_down = 0; - if (mqtt_initial_connection_state) MqttReconnect(); + if (Mqtt.initial_connection_state) MqttReconnect(); } } -/*********************************************************************************************/ +/*********************************************************************************************\ + * Commands +\*********************************************************************************************/ -#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) -void setLongMqttHost(const char *mqtt_host) { - if (strlen(mqtt_host) <= sizeof(Settings.mqtt_host)) { - strlcpy(Settings.mqtt_host, mqtt_host, sizeof(Settings.mqtt_host)); - Settings.mqtt_user[0] = 0; - } else { - // need to split in mqtt_user first then mqtt_host - strlcpy(Settings.mqtt_user, mqtt_host, sizeof(Settings.mqtt_user)); - strlcpy(Settings.mqtt_host, &mqtt_host[sizeof(Settings.mqtt_user)-1], sizeof(Settings.mqtt_host)); - } - strlcpy(AWS_endpoint, mqtt_host, sizeof(AWS_endpoint)); -} -#endif // USE_MQTT_AWS_IOT - -bool MqttCommand(void) -{ - char command [CMDSZ]; - bool serviced = true; - char stemp1[TOPSZ]; - char scommand[CMDSZ]; - - uint16_t index = XdrvMailbox.index; - uint16_t data_len = XdrvMailbox.data_len; - uint16_t payload16 = XdrvMailbox.payload16; - int16_t payload = XdrvMailbox.payload; - bool grpflg = XdrvMailbox.grpflg; - char *type = XdrvMailbox.topic; - char *dataBuf = XdrvMailbox.data; - - int command_code = GetCommandCode(command, sizeof(command), type, kMqttCommands); - if (-1 == command_code) { - serviced = false; // Unknown command - } - else if (CMND_MQTTHOST == command_code) { -#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) - if ((data_len > 0) && (data_len <= sizeof(Settings.mqtt_host) + sizeof(Settings.mqtt_user) - 2)) { - setLongMqttHost((SC_CLEAR == Shortcut(dataBuf)) ? "" : (SC_DEFAULT == Shortcut(dataBuf)) ? MQTT_HOST : dataBuf); - restart_flag = 2; - } - Response_P(S_JSON_COMMAND_SVALUE, command, AWS_endpoint); -#else - if ((data_len > 0) && (data_len < sizeof(Settings.mqtt_host))) { - strlcpy(Settings.mqtt_host, (SC_CLEAR == Shortcut(dataBuf)) ? "" : (SC_DEFAULT == Shortcut(dataBuf)) ? MQTT_HOST : dataBuf, sizeof(Settings.mqtt_host)); - restart_flag = 2; - } - Response_P(S_JSON_COMMAND_SVALUE, command, Settings.mqtt_host); -#endif - } - else if (CMND_MQTTPORT == command_code) { - if (payload16 > 0) { - Settings.mqtt_port = (1 == payload16) ? MQTT_PORT : payload16; - restart_flag = 2; - } - Response_P(S_JSON_COMMAND_NVALUE, command, Settings.mqtt_port); - } - else if (CMND_MQTTRETRY == command_code) { - if ((payload >= MQTT_RETRY_SECS) && (payload < 32001)) { - Settings.mqtt_retry = payload; - mqtt_retry_counter = Settings.mqtt_retry; - } - Response_P(S_JSON_COMMAND_NVALUE, command, Settings.mqtt_retry); - } - else if ((CMND_STATETEXT == command_code) && (index > 0) && (index <= 4)) { - if ((data_len > 0) && (data_len < sizeof(Settings.state_text[0]))) { - for (uint32_t i = 0; i <= data_len; i++) { - if (dataBuf[i] == ' ') dataBuf[i] = '_'; - } - strlcpy(Settings.state_text[index -1], dataBuf, sizeof(Settings.state_text[0])); - } - Response_P(S_JSON_COMMAND_INDEX_SVALUE, command, index, GetStateText(index -1)); - } #if defined(USE_MQTT_TLS) && !defined(USE_MQTT_TLS_CA_CERT) - else if ((CMND_MQTTFINGERPRINT == command_code) && (index > 0) && (index <= 2)) { +void CmndMqttFingerprint(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 2)) { char fingerprint[60]; - if ((data_len > 0) && (data_len < sizeof(fingerprint))) { - strlcpy(fingerprint, (SC_CLEAR == Shortcut(dataBuf)) ? "" : (SC_DEFAULT == Shortcut(dataBuf)) ? (1 == index) ? MQTT_FINGERPRINT1 : MQTT_FINGERPRINT2 : dataBuf, sizeof(fingerprint)); + if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(fingerprint))) { + strlcpy(fingerprint, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? (1 == XdrvMailbox.index) ? MQTT_FINGERPRINT1 : MQTT_FINGERPRINT2 : XdrvMailbox.data, sizeof(fingerprint)); char *p = fingerprint; for (uint32_t i = 0; i < 20; i++) { - Settings.mqtt_fingerprint[index -1][i] = strtol(p, &p, 16); + Settings.mqtt_fingerprint[XdrvMailbox.index -1][i] = strtol(p, &p, 16); } restart_flag = 2; } - fingerprint[0] = '\0'; - for (uint32_t i = 0; i < sizeof(Settings.mqtt_fingerprint[index -1]); i++) { - snprintf_P(fingerprint, sizeof(fingerprint), PSTR("%s%s%02X"), fingerprint, (i) ? " " : "", Settings.mqtt_fingerprint[index -1][i]); - } - Response_P(S_JSON_COMMAND_INDEX_SVALUE, command, index, fingerprint); + ResponseCmndIdxChar(ToHex_P((unsigned char *)Settings.mqtt_fingerprint[XdrvMailbox.index -1], 20, fingerprint, sizeof(fingerprint), ' ')); } -#endif - else if (CMND_MQTTCLIENT == command_code) { - if (!grpflg && (data_len > 0) && (data_len < sizeof(Settings.mqtt_client))) { - strlcpy(Settings.mqtt_client, (SC_DEFAULT == Shortcut(dataBuf)) ? MQTT_CLIENT_ID : dataBuf, sizeof(Settings.mqtt_client)); - restart_flag = 2; - } - Response_P(S_JSON_COMMAND_SVALUE, command, Settings.mqtt_client); - } -#if !defined(USE_MQTT_TLS) || !defined(USE_MQTT_AWS_IOT) // user and password are disabled with AWS IoT - else if (CMND_MQTTUSER == command_code) { - if ((data_len > 0) && (data_len < sizeof(Settings.mqtt_user))) { - strlcpy(Settings.mqtt_user, (SC_CLEAR == Shortcut(dataBuf)) ? "" : (SC_DEFAULT == Shortcut(dataBuf)) ? MQTT_USER : dataBuf, sizeof(Settings.mqtt_user)); - restart_flag = 2; - } - Response_P(S_JSON_COMMAND_SVALUE, command, Settings.mqtt_user); - } - else if (CMND_MQTTPASSWORD == command_code) { - if ((data_len > 0) && (data_len < sizeof(Settings.mqtt_pwd))) { - strlcpy(Settings.mqtt_pwd, (SC_CLEAR == Shortcut(dataBuf)) ? "" : (SC_DEFAULT == Shortcut(dataBuf)) ? MQTT_PASS : dataBuf, sizeof(Settings.mqtt_pwd)); - Response_P(S_JSON_COMMAND_SVALUE, command, Settings.mqtt_pwd); - restart_flag = 2; - } else { - Response_P(S_JSON_COMMAND_ASTERISK, command); - } - } -#endif // USE_MQTT_AWS_IOT - else if (CMND_FULLTOPIC == command_code) { - if ((data_len > 0) && (data_len < sizeof(Settings.mqtt_fulltopic))) { - MakeValidMqtt(1, dataBuf); - if (!strcmp(dataBuf, mqtt_client)) SetShortcut(dataBuf, SC_DEFAULT); - strlcpy(stemp1, (SC_DEFAULT == Shortcut(dataBuf)) ? MQTT_FULLTOPIC : dataBuf, sizeof(stemp1)); - if (strcmp(stemp1, Settings.mqtt_fulltopic)) { - Response_P((Settings.flag.mqtt_offline) ? S_OFFLINE : ""); - MqttPublishPrefixTopic_P(TELE, PSTR(D_LWT), true); // Offline or remove previous retained topic - strlcpy(Settings.mqtt_fulltopic, stemp1, sizeof(Settings.mqtt_fulltopic)); - restart_flag = 2; - } - } - Response_P(S_JSON_COMMAND_SVALUE, command, Settings.mqtt_fulltopic); - } - else if ((CMND_PREFIX == command_code) && (index > 0) && (index <= 3)) { - if ((data_len > 0) && (data_len < sizeof(Settings.mqtt_prefix[0]))) { - MakeValidMqtt(0, dataBuf); - strlcpy(Settings.mqtt_prefix[index -1], (SC_DEFAULT == Shortcut(dataBuf)) ? (1==index)?SUB_PREFIX:(2==index)?PUB_PREFIX:PUB_PREFIX2 : dataBuf, sizeof(Settings.mqtt_prefix[0])); -// if (Settings.mqtt_prefix[index -1][strlen(Settings.mqtt_prefix[index -1])] == '/') Settings.mqtt_prefix[index -1][strlen(Settings.mqtt_prefix[index -1])] = 0; - restart_flag = 2; - } - Response_P(S_JSON_COMMAND_INDEX_SVALUE, command, index, Settings.mqtt_prefix[index -1]); - } - else if (CMND_PUBLISH == command_code) { - if (data_len > 0) { - char *mqtt_part = strtok(dataBuf, " "); - if (mqtt_part) { - strlcpy(stemp1, mqtt_part, sizeof(stemp1)); - mqtt_part = strtok(nullptr, " "); - if (mqtt_part) { - strlcpy(mqtt_data, mqtt_part, sizeof(mqtt_data)); - } else { - mqtt_data[0] = '\0'; - } - MqttPublishDirect(stemp1, (index == 2)); -// Response_P(S_JSON_COMMAND_SVALUE, command, D_JSON_DONE); - mqtt_data[0] = '\0'; - } - } - } - else if (CMND_GROUPTOPIC == command_code) { - if ((data_len > 0) && (data_len < sizeof(Settings.mqtt_grptopic))) { - MakeValidMqtt(0, dataBuf); - if (!strcmp(dataBuf, mqtt_client)) SetShortcut(dataBuf, SC_DEFAULT); - strlcpy(Settings.mqtt_grptopic, (SC_DEFAULT == Shortcut(dataBuf)) ? MQTT_GRPTOPIC : dataBuf, sizeof(Settings.mqtt_grptopic)); - restart_flag = 2; - } - Response_P(S_JSON_COMMAND_SVALUE, command, Settings.mqtt_grptopic); - } - else if (CMND_TOPIC == command_code) { - if (!grpflg && (data_len > 0) && (data_len < sizeof(Settings.mqtt_topic))) { - MakeValidMqtt(0, dataBuf); - if (!strcmp(dataBuf, mqtt_client)) SetShortcut(dataBuf, SC_DEFAULT); - strlcpy(stemp1, (SC_DEFAULT == Shortcut(dataBuf)) ? MQTT_TOPIC : dataBuf, sizeof(stemp1)); - if (strcmp(stemp1, Settings.mqtt_topic)) { - Response_P((Settings.flag.mqtt_offline) ? S_OFFLINE : ""); - MqttPublishPrefixTopic_P(TELE, PSTR(D_LWT), true); // Offline or remove previous retained topic - strlcpy(Settings.mqtt_topic, stemp1, sizeof(Settings.mqtt_topic)); - restart_flag = 2; - } - } - Response_P(S_JSON_COMMAND_SVALUE, command, Settings.mqtt_topic); - } - else if (CMND_BUTTONTOPIC == command_code) { - if (!grpflg && (data_len > 0) && (data_len < sizeof(Settings.button_topic))) { - MakeValidMqtt(0, dataBuf); - if (!strcmp(dataBuf, mqtt_client)) SetShortcut(dataBuf, SC_DEFAULT); - switch (Shortcut(dataBuf)) { - case SC_CLEAR: strlcpy(Settings.button_topic, "", sizeof(Settings.button_topic)); break; - case SC_DEFAULT: strlcpy(Settings.button_topic, mqtt_topic, sizeof(Settings.button_topic)); break; - case SC_USER: strlcpy(Settings.button_topic, MQTT_BUTTON_TOPIC, sizeof(Settings.button_topic)); break; - default: strlcpy(Settings.button_topic, dataBuf, sizeof(Settings.button_topic)); - } - } - Response_P(S_JSON_COMMAND_SVALUE, command, Settings.button_topic); - } - else if (CMND_SWITCHTOPIC == command_code) { - if ((data_len > 0) && (data_len < sizeof(Settings.switch_topic))) { - MakeValidMqtt(0, dataBuf); - if (!strcmp(dataBuf, mqtt_client)) SetShortcut(dataBuf, SC_DEFAULT); - switch (Shortcut(dataBuf)) { - case SC_CLEAR: strlcpy(Settings.switch_topic, "", sizeof(Settings.switch_topic)); break; - case SC_DEFAULT: strlcpy(Settings.switch_topic, mqtt_topic, sizeof(Settings.switch_topic)); break; - case SC_USER: strlcpy(Settings.switch_topic, MQTT_SWITCH_TOPIC, sizeof(Settings.switch_topic)); break; - default: strlcpy(Settings.switch_topic, dataBuf, sizeof(Settings.switch_topic)); - } - } - Response_P(S_JSON_COMMAND_SVALUE, command, Settings.switch_topic); - } - else if (CMND_BUTTONRETAIN == command_code) { - if ((payload >= 0) && (payload <= 1)) { - if (!payload) { - for (uint32_t i = 1; i <= MAX_KEYS; i++) { - SendKey(0, i, 9); // Clear MQTT retain in broker - } - } - Settings.flag.mqtt_button_retain = payload; - } - Response_P(S_JSON_COMMAND_SVALUE, command, GetStateText(Settings.flag.mqtt_button_retain)); - } - else if (CMND_SWITCHRETAIN == command_code) { - if ((payload >= 0) && (payload <= 1)) { - if (!payload) { - for (uint32_t i = 1; i <= MAX_SWITCHES; i++) { - SendKey(1, i, 9); // Clear MQTT retain in broker - } - } - Settings.flag.mqtt_switch_retain = payload; - } - Response_P(S_JSON_COMMAND_SVALUE, command, GetStateText(Settings.flag.mqtt_switch_retain)); - } - else if (CMND_POWERRETAIN == command_code) { - if ((payload >= 0) && (payload <= 1)) { - if (!payload) { - for (uint32_t i = 1; i <= devices_present; i++) { // Clear MQTT retain in broker - GetTopic_P(stemp1, STAT, mqtt_topic, GetPowerDevice(scommand, i, sizeof(scommand), Settings.flag.device_index_enable)); - mqtt_data[0] = '\0'; - MqttPublish(stemp1, Settings.flag.mqtt_power_retain); - } - } - Settings.flag.mqtt_power_retain = payload; - } - Response_P(S_JSON_COMMAND_SVALUE, command, GetStateText(Settings.flag.mqtt_power_retain)); - } - else if (CMND_SENSORRETAIN == command_code) { - if ((payload >= 0) && (payload <= 1)) { - if (!payload) { - mqtt_data[0] = '\0'; - MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); - MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_ENERGY), Settings.flag.mqtt_sensor_retain); - } - Settings.flag.mqtt_sensor_retain = payload; - } - Response_P(S_JSON_COMMAND_SVALUE, command, GetStateText(Settings.flag.mqtt_sensor_retain)); - } - else serviced = false; // Unknown command - - return serviced; } +#endif + +#if !defined(USE_MQTT_TLS) || !defined(USE_MQTT_AWS_IOT) // user and password are disabled with AWS IoT +void CmndMqttUser(void) +{ + if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.mqtt_user))) { + strlcpy(Settings.mqtt_user, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? MQTT_USER : XdrvMailbox.data, sizeof(Settings.mqtt_user)); + restart_flag = 2; + } + ResponseCmndChar(Settings.mqtt_user); +} + +void CmndMqttPassword(void) +{ + if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.mqtt_pwd))) { + strlcpy(Settings.mqtt_pwd, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? MQTT_PASS : XdrvMailbox.data, sizeof(Settings.mqtt_pwd)); + ResponseCmndChar(Settings.mqtt_pwd); + restart_flag = 2; + } else { + Response_P(S_JSON_COMMAND_ASTERISK, XdrvMailbox.command); + } +} +#endif // USE_MQTT_AWS_IOT + +void CmndMqttlog(void) +{ + if ((XdrvMailbox.payload >= LOG_LEVEL_NONE) && (XdrvMailbox.payload <= LOG_LEVEL_ALL)) { + Settings.mqttlog_level = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.mqttlog_level); +} + +void CmndMqttHost(void) +{ +#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) + if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len <= sizeof(Settings.mqtt_host) + sizeof(Settings.mqtt_user) - 2)) { + setLongMqttHost((SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? MQTT_HOST : XdrvMailbox.data); + restart_flag = 2; + } + ResponseCmndChar(AWS_endpoint); +#else + if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.mqtt_host))) { + strlcpy(Settings.mqtt_host, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? MQTT_HOST : XdrvMailbox.data, sizeof(Settings.mqtt_host)); + restart_flag = 2; + } + ResponseCmndChar(Settings.mqtt_host); +#endif +} + +void CmndMqttPort(void) +{ + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 65536)) { + Settings.mqtt_port = (1 == XdrvMailbox.payload) ? MQTT_PORT : XdrvMailbox.payload; + restart_flag = 2; + } + ResponseCmndNumber(Settings.mqtt_port); +} + +void CmndMqttRetry(void) +{ + if ((XdrvMailbox.payload >= MQTT_RETRY_SECS) && (XdrvMailbox.payload < 32001)) { + Settings.mqtt_retry = XdrvMailbox.payload; + Mqtt.retry_counter = Settings.mqtt_retry; + } + ResponseCmndNumber(Settings.mqtt_retry); +} + +void CmndStateText(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 4)) { + if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.state_text[0]))) { + for (uint32_t i = 0; i <= XdrvMailbox.data_len; i++) { + if (XdrvMailbox.data[i] == ' ') XdrvMailbox.data[i] = '_'; + } + strlcpy(Settings.state_text[XdrvMailbox.index -1], XdrvMailbox.data, sizeof(Settings.state_text[0])); + } + ResponseCmndIdxChar(GetStateText(XdrvMailbox.index -1)); + } +} + +void CmndMqttClient(void) +{ + if (!XdrvMailbox.grpflg && (XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.mqtt_client))) { + strlcpy(Settings.mqtt_client, (SC_DEFAULT == Shortcut()) ? MQTT_CLIENT_ID : XdrvMailbox.data, sizeof(Settings.mqtt_client)); + restart_flag = 2; + } + ResponseCmndChar(Settings.mqtt_client); +} + +void CmndFullTopic(void) +{ + if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.mqtt_fulltopic))) { + MakeValidMqtt(1, XdrvMailbox.data); + if (!strcmp(XdrvMailbox.data, mqtt_client)) { SetShortcutDefault(); } + char stemp1[TOPSZ]; + strlcpy(stemp1, (SC_DEFAULT == Shortcut()) ? MQTT_FULLTOPIC : XdrvMailbox.data, sizeof(stemp1)); + if (strcmp(stemp1, Settings.mqtt_fulltopic)) { + Response_P((Settings.flag.mqtt_offline) ? S_OFFLINE : ""); + MqttPublishPrefixTopic_P(TELE, PSTR(D_LWT), true); // Offline or remove previous retained topic + strlcpy(Settings.mqtt_fulltopic, stemp1, sizeof(Settings.mqtt_fulltopic)); + restart_flag = 2; + } + } + ResponseCmndChar(Settings.mqtt_fulltopic); +} + +void CmndPrefix(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 3)) { + if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.mqtt_prefix[0]))) { + MakeValidMqtt(0, XdrvMailbox.data); + strlcpy(Settings.mqtt_prefix[XdrvMailbox.index -1], (SC_DEFAULT == Shortcut()) ? (1==XdrvMailbox.index)?SUB_PREFIX:(2==XdrvMailbox.index)?PUB_PREFIX:PUB_PREFIX2 : XdrvMailbox.data, sizeof(Settings.mqtt_prefix[0])); +// if (Settings.mqtt_prefix[XdrvMailbox.index -1][strlen(Settings.mqtt_prefix[XdrvMailbox.index -1])] == '/') Settings.mqtt_prefix[XdrvMailbox.index -1][strlen(Settings.mqtt_prefix[XdrvMailbox.index -1])] = 0; + restart_flag = 2; + } + ResponseCmndIdxChar(Settings.mqtt_prefix[XdrvMailbox.index -1]); + } +} + +void CmndPublish(void) +{ + if (XdrvMailbox.data_len > 0) { + char *mqtt_part = strtok(XdrvMailbox.data, " "); + if (mqtt_part) { + char stemp1[TOPSZ]; + strlcpy(stemp1, mqtt_part, sizeof(stemp1)); + mqtt_part = strtok(nullptr, " "); + if (mqtt_part) { + strlcpy(mqtt_data, mqtt_part, sizeof(mqtt_data)); + } else { + mqtt_data[0] = '\0'; + } + MqttPublishDirect(stemp1, (XdrvMailbox.index == 2)); +// ResponseCmndDone(); + mqtt_data[0] = '\0'; + } + } +} + +void CmndGroupTopic(void) +{ + if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.mqtt_grptopic))) { + MakeValidMqtt(0, XdrvMailbox.data); + if (!strcmp(XdrvMailbox.data, mqtt_client)) { SetShortcutDefault(); } + strlcpy(Settings.mqtt_grptopic, (SC_DEFAULT == Shortcut()) ? MQTT_GRPTOPIC : XdrvMailbox.data, sizeof(Settings.mqtt_grptopic)); + restart_flag = 2; + } + ResponseCmndChar(Settings.mqtt_grptopic); +} + +void CmndTopic(void) +{ + if (!XdrvMailbox.grpflg && (XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.mqtt_topic))) { + MakeValidMqtt(0, XdrvMailbox.data); + if (!strcmp(XdrvMailbox.data, mqtt_client)) { SetShortcutDefault(); } + char stemp1[TOPSZ]; + strlcpy(stemp1, (SC_DEFAULT == Shortcut()) ? MQTT_TOPIC : XdrvMailbox.data, sizeof(stemp1)); + if (strcmp(stemp1, Settings.mqtt_topic)) { + Response_P((Settings.flag.mqtt_offline) ? S_OFFLINE : ""); + MqttPublishPrefixTopic_P(TELE, PSTR(D_LWT), true); // Offline or remove previous retained topic + strlcpy(Settings.mqtt_topic, stemp1, sizeof(Settings.mqtt_topic)); + restart_flag = 2; + } + } + ResponseCmndChar(Settings.mqtt_topic); +} + +void CmndButtonTopic(void) +{ + if (!XdrvMailbox.grpflg && (XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.button_topic))) { + MakeValidMqtt(0, XdrvMailbox.data); + if (!strcmp(XdrvMailbox.data, mqtt_client)) { SetShortcutDefault(); } + switch (Shortcut()) { + case SC_CLEAR: strlcpy(Settings.button_topic, "", sizeof(Settings.button_topic)); break; + case SC_DEFAULT: strlcpy(Settings.button_topic, mqtt_topic, sizeof(Settings.button_topic)); break; + case SC_USER: strlcpy(Settings.button_topic, MQTT_BUTTON_TOPIC, sizeof(Settings.button_topic)); break; + default: strlcpy(Settings.button_topic, XdrvMailbox.data, sizeof(Settings.button_topic)); + } + } + ResponseCmndChar(Settings.button_topic); +} + +void CmndSwitchTopic(void) +{ + if (!XdrvMailbox.grpflg && (XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.switch_topic))) { + MakeValidMqtt(0, XdrvMailbox.data); + if (!strcmp(XdrvMailbox.data, mqtt_client)) { SetShortcutDefault(); } + switch (Shortcut()) { + case SC_CLEAR: strlcpy(Settings.switch_topic, "", sizeof(Settings.switch_topic)); break; + case SC_DEFAULT: strlcpy(Settings.switch_topic, mqtt_topic, sizeof(Settings.switch_topic)); break; + case SC_USER: strlcpy(Settings.switch_topic, MQTT_SWITCH_TOPIC, sizeof(Settings.switch_topic)); break; + default: strlcpy(Settings.switch_topic, XdrvMailbox.data, sizeof(Settings.switch_topic)); + } + } + ResponseCmndChar(Settings.switch_topic); +} + +void CmndButtonRetain(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) { + if (!XdrvMailbox.payload) { + for (uint32_t i = 1; i <= MAX_KEYS; i++) { + SendKey(KEY_BUTTON, i, CLEAR_RETAIN); // Clear MQTT retain in broker + } + } + Settings.flag.mqtt_button_retain = XdrvMailbox.payload; + } + ResponseCmndStateText(Settings.flag.mqtt_button_retain); +} + +void CmndSwitchRetain(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) { + if (!XdrvMailbox.payload) { + for (uint32_t i = 1; i <= MAX_SWITCHES; i++) { + SendKey(KEY_SWITCH, i, CLEAR_RETAIN); // Clear MQTT retain in broker + } + } + Settings.flag.mqtt_switch_retain = XdrvMailbox.payload; + } + ResponseCmndStateText(Settings.flag.mqtt_switch_retain); +} + +void CmndPowerRetain(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) { + if (!XdrvMailbox.payload) { + char stemp1[TOPSZ]; + char scommand[CMDSZ]; + for (uint32_t i = 1; i <= devices_present; i++) { // Clear MQTT retain in broker + GetTopic_P(stemp1, STAT, mqtt_topic, GetPowerDevice(scommand, i, sizeof(scommand), Settings.flag.device_index_enable)); + mqtt_data[0] = '\0'; + MqttPublish(stemp1, Settings.flag.mqtt_power_retain); + } + } + Settings.flag.mqtt_power_retain = XdrvMailbox.payload; + } + ResponseCmndStateText(Settings.flag.mqtt_power_retain); +} + +void CmndSensorRetain(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) { + if (!XdrvMailbox.payload) { + mqtt_data[0] = '\0'; + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_ENERGY), Settings.flag.mqtt_sensor_retain); + } + Settings.flag.mqtt_sensor_retain = XdrvMailbox.payload; + } + ResponseCmndStateText(Settings.flag.mqtt_sensor_retain); +} + +/*********************************************************************************************\ + * TLS private key and certificate - store into Flash +\*********************************************************************************************/ +#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) + +const static uint16_t tls_spi_start_sector = SPIFFS_END + 4; // 0xXXFF +const static uint8_t* tls_spi_start = (uint8_t*) ((tls_spi_start_sector * SPI_FLASH_SEC_SIZE) + 0x40200000); // 0x40XFF000 +const static size_t tls_spi_len = 0x1000; // 4kb blocs +const static size_t tls_block_offset = 0x0400; +const static size_t tls_block_len = 0x0400; // 1kb +const static size_t tls_obj_store_offset = tls_block_offset + sizeof(tls_dir_t); + + +inline void TlsEraseBuffer(uint8_t *buffer) { + memset(buffer + tls_block_offset, 0xFF, tls_block_len); +} + +// static data structures for Private Key and Certificate, only the pointer +// to binary data will change to a region in SPI Flash +static br_ec_private_key EC = { + 23, + nullptr, 0 +}; + +static br_x509_certificate CHAIN[] = { + { nullptr, 0 } +}; + +// load a copy of the tls_dir from flash into ram +// and calculate the appropriate data structures for AWS_IoT_Private_Key and AWS_IoT_Client_Certificate +void loadTlsDir(void) { + memcpy_P(&tls_dir, tls_spi_start + tls_block_offset, sizeof(tls_dir)); + + // calculate the addresses for Key and Cert in Flash + if ((TLS_NAME_SKEY == tls_dir.entry[0].name) && (tls_dir.entry[0].len > 0)) { + EC.x = (unsigned char *)(tls_spi_start + tls_obj_store_offset + tls_dir.entry[0].start); + EC.xlen = tls_dir.entry[0].len; + AWS_IoT_Private_Key = &EC; + } else { + AWS_IoT_Private_Key = nullptr; + } + if ((TLS_NAME_CRT == tls_dir.entry[1].name) && (tls_dir.entry[1].len > 0)) { + CHAIN[0].data = (unsigned char *) (tls_spi_start + tls_obj_store_offset + tls_dir.entry[1].start); + CHAIN[0].data_len = tls_dir.entry[1].len; + AWS_IoT_Client_Certificate = CHAIN; + } else { + AWS_IoT_Client_Certificate = nullptr; + } +//Serial.printf("AWS_IoT_Private_Key = %x, AWS_IoT_Client_Certificate = %x\n", AWS_IoT_Private_Key, AWS_IoT_Client_Certificate); +} + +const char ALLOCATE_ERROR[] PROGMEM = "TLSKey " D_JSON_ERROR ": cannot allocate buffer."; + +void CmndTlsKey(void) { +#ifdef DEBUG_DUMP_TLS + if (0 == XdrvMailbox.index){ + CmndTlsDump(); + } +#endif // DEBUG_DUMP_TLS + if ((XdrvMailbox.index >= 1) && (XdrvMailbox.index <= 2)) { + tls_dir_t *tls_dir_write; + + if (XdrvMailbox.data_len > 0) { // write new value + // first copy SPI buffer into ram + uint8_t *spi_buffer = (uint8_t*) malloc(tls_spi_len); + if (!spi_buffer) { + AddLog_P(LOG_LEVEL_ERROR, ALLOCATE_ERROR); + return; + } + memcpy_P(spi_buffer, tls_spi_start, tls_spi_len); + + // allocate buffer for decoded base64 + uint32_t bin_len = decode_base64_length((unsigned char*)XdrvMailbox.data); + uint8_t *bin_buf = nullptr; + if (bin_len > 0) { + bin_buf = (uint8_t*) malloc(bin_len + 4); + if (!bin_buf) { + AddLog_P(LOG_LEVEL_ERROR, ALLOCATE_ERROR); + free(spi_buffer); + return; + } + } + + // decode base64 + if (bin_len > 0) { + decode_base64((unsigned char*)XdrvMailbox.data, bin_buf); + } + + // address of writable tls_dir in buffer + tls_dir_write = (tls_dir_t*) (spi_buffer + tls_block_offset); + + if (1 == XdrvMailbox.index) { + // Try to write Private key + // Start by erasing all + TlsEraseBuffer(spi_buffer); // Erase any previously stored data + if (bin_len > 0) { + if (bin_len != 32) { + // no private key was previously stored, abort + AddLog_P2(LOG_LEVEL_INFO, PSTR("TLSKey: Certificate must be 32 bytes: %d."), bin_len); + free(spi_buffer); + free(bin_buf); + return; + } + tls_entry_t *entry = &tls_dir_write->entry[0]; + entry->name = TLS_NAME_SKEY; + entry->start = 0; + entry->len = bin_len; + memcpy(spi_buffer + tls_obj_store_offset + entry->start, bin_buf, entry->len); + } else { + // if lenght is zero, simply erase this SPI flash area + } + } else if (2 == XdrvMailbox.index) { + // Try to write Certificate + if (TLS_NAME_SKEY != tls_dir.entry[0].name) { + // no private key was previously stored, abort + AddLog_P(LOG_LEVEL_INFO, PSTR("TLSKey: cannot store Cert if no Key previously stored.")); + free(spi_buffer); + free(bin_buf); + return; + } + if (bin_len <= 256) { + // Certificate lenght too short + AddLog_P2(LOG_LEVEL_INFO, PSTR("TLSKey: Certificate length too short: %d."), bin_len); + free(spi_buffer); + free(bin_buf); + return; + } + tls_entry_t *entry = &tls_dir_write->entry[1]; + entry->name = TLS_NAME_CRT; + entry->start = (tls_dir_write->entry[0].start + tls_dir_write->entry[0].len + 3) & ~0x03; // align to 4 bytes boundary + entry->len = bin_len; + memcpy(spi_buffer + tls_obj_store_offset + entry->start, bin_buf, entry->len); + } + + TlsWriteSpiBuffer(spi_buffer); + free(spi_buffer); + free(bin_buf); + } + + loadTlsDir(); // reload into memory any potential change + Response_P(PSTR("{\"%s1\":%d,\"%s2\":%d}"), + XdrvMailbox.command, AWS_IoT_Private_Key ? tls_dir.entry[0].len : -1, + XdrvMailbox.command, AWS_IoT_Client_Certificate ? tls_dir.entry[1].len : -1); + } +} + + +extern "C" { +#include "spi_flash.h" +} + +void TlsWriteSpiBuffer(uint8_t *buf) { + bool ret = false; + SpiFlashOpResult res; + + noInterrupts(); + res = spi_flash_erase_sector(tls_spi_start_sector); + if (SPI_FLASH_RESULT_OK == res) { + res = spi_flash_write(tls_spi_start_sector * SPI_FLASH_SEC_SIZE, (uint32_t*) buf, SPI_FLASH_SEC_SIZE); + if (SPI_FLASH_RESULT_OK == res) { + ret = true; + } + } + interrupts(); +} + +#ifdef DEBUG_DUMP_TLS +// Dump TLS Flash data - don't activate in production to protect your private keys +uint32_t bswap32(uint32_t x) { + return ((x << 24) & 0xff000000 ) | + ((x << 8) & 0x00ff0000 ) | + ((x >> 8) & 0x0000ff00 ) | + ((x >> 24) & 0x000000ff ); +} +void CmndTlsDump(void) { + uint32_t start = (uint32_t)tls_spi_start + tls_block_offset; + uint32_t end = start + tls_block_len -1; + for (uint32_t pos = start; pos < end; pos += 0x10) { + uint32_t* values = (uint32_t*)(pos); + Serial.printf_P(PSTR("%08x: %08x %08x %08x %08x\n"), pos, bswap32(values[0]), bswap32(values[1]), bswap32(values[2]), bswap32(values[3])); + } +} +#endif // DEBUG_DUMP_TLS +#endif /*********************************************************************************************\ * Presentation @@ -903,7 +1247,7 @@ void MqttSaveSettings(void) MakeValidMqtt(0, stemp); WebGetArg("mf", tmp, sizeof(tmp)); strlcpy(stemp2, (!strlen(tmp)) ? MQTT_FULLTOPIC : tmp, sizeof(stemp2)); - MakeValidMqtt(1,stemp2); + MakeValidMqtt(1, stemp2); if ((strcmp(stemp, Settings.mqtt_topic)) || (strcmp(stemp2, Settings.mqtt_fulltopic))) { Response_P((Settings.flag.mqtt_offline) ? S_OFFLINE : ""); MqttPublishPrefixTopic_P(TELE, S_LWT, true); // Offline or remove previous retained topic @@ -959,7 +1303,7 @@ bool Xdrv02(uint8_t function) break; #endif // USE_WEBSERVER case FUNC_COMMAND: - result = MqttCommand(); + result = DecodeCommand(kMqttCommands, MqttCommand); break; } } diff --git a/sonoff/xdrv_03_energy.ino b/sonoff/xdrv_03_energy.ino index 52585a632..30b0237e2 100644 --- a/sonoff/xdrv_03_energy.ino +++ b/sonoff/xdrv_03_energy.ino @@ -22,123 +22,229 @@ * Energy \*********************************************************************************************/ -#define XDRV_03 3 -#define XSNS_03 3 +#define XDRV_03 3 +#define XSNS_03 3 -#define ENERGY_NONE 0 +//#define USE_ENERGY_MARGIN_DETECTION +// #define USE_ENERGY_POWER_LIMIT -#define ENERGY_OVERTEMP 73.0 // Industry standard lowest overtemp in Celsius -#define ENERGY_WATCHDOG 4 // Allow up to 4 seconds before deciding no valid data present - -#define FEATURE_POWER_LIMIT true +#define ENERGY_NONE 0 +#define ENERGY_WATCHDOG 4 // Allow up to 4 seconds before deciding no valid data present #include #define D_CMND_POWERCAL "PowerCal" #define D_CMND_VOLTAGECAL "VoltageCal" #define D_CMND_CURRENTCAL "CurrentCal" +#define D_CMND_TARIFF "Tariff" +#define D_CMND_MODULEADDRESS "ModuleAddress" enum EnergyCommands { - CMND_POWERDELTA, - CMND_POWERLOW, CMND_POWERHIGH, CMND_VOLTAGELOW, CMND_VOLTAGEHIGH, CMND_CURRENTLOW, CMND_CURRENTHIGH, CMND_POWERCAL, CMND_VOLTAGECAL, CMND_CURRENTCAL, - CMND_POWERSET, CMND_VOLTAGESET, CMND_CURRENTSET, CMND_FREQUENCYSET, - CMND_ENERGYRESET, CMND_MAXENERGY, CMND_MAXENERGYSTART, - CMND_MAXPOWER, CMND_MAXPOWERHOLD, CMND_MAXPOWERWINDOW, - CMND_SAFEPOWER, CMND_SAFEPOWERHOLD, CMND_SAFEPOWERWINDOW }; -const char kEnergyCommands[] PROGMEM = - D_CMND_POWERDELTA "|" - D_CMND_POWERLOW "|" D_CMND_POWERHIGH "|" D_CMND_VOLTAGELOW "|" D_CMND_VOLTAGEHIGH "|" D_CMND_CURRENTLOW "|" D_CMND_CURRENTHIGH "|" + CMND_POWERSET, CMND_VOLTAGESET, CMND_CURRENTSET, CMND_FREQUENCYSET, CMND_MODULEADDRESS }; + +const char kEnergyCommands[] PROGMEM = "|" // No prefix D_CMND_POWERCAL "|" D_CMND_VOLTAGECAL "|" D_CMND_CURRENTCAL "|" - D_CMND_POWERSET "|" D_CMND_VOLTAGESET "|" D_CMND_CURRENTSET "|" D_CMND_FREQUENCYSET "|" - D_CMND_ENERGYRESET "|" D_CMND_MAXENERGY "|" D_CMND_MAXENERGYSTART "|" + D_CMND_POWERSET "|" D_CMND_VOLTAGESET "|" D_CMND_CURRENTSET "|" D_CMND_FREQUENCYSET "|" D_CMND_MODULEADDRESS "|" +#ifdef USE_ENERGY_MARGIN_DETECTION + D_CMND_POWERDELTA "|" D_CMND_POWERLOW "|" D_CMND_POWERHIGH "|" D_CMND_VOLTAGELOW "|" D_CMND_VOLTAGEHIGH "|" D_CMND_CURRENTLOW "|" D_CMND_CURRENTHIGH "|" +#ifdef USE_ENERGY_POWER_LIMIT + D_CMND_MAXENERGY "|" D_CMND_MAXENERGYSTART "|" D_CMND_MAXPOWER "|" D_CMND_MAXPOWERHOLD "|" D_CMND_MAXPOWERWINDOW "|" - D_CMND_SAFEPOWER "|" D_CMND_SAFEPOWERHOLD "|" D_CMND_SAFEPOWERWINDOW ; + D_CMND_SAFEPOWER "|" D_CMND_SAFEPOWERHOLD "|" D_CMND_SAFEPOWERWINDOW "|" +#endif // USE_ENERGY_POWER_LIMIT +#endif // USE_ENERGY_MARGIN_DETECTION + D_CMND_ENERGYRESET "|" D_CMND_TARIFF ; -float energy_voltage = 0; // 123.1 V -float energy_current = 0; // 123.123 A -float energy_active_power = 0; // 123.1 W -float energy_apparent_power = NAN; // 123.1 VA -float energy_reactive_power = NAN; // 123.1 VAr -float energy_power_factor = NAN; // 0.12 -float energy_frequency = NAN; // 123.1 Hz -float energy_start = 0; // 12345.12345 kWh total previous +void (* const EnergyCommand[])(void) PROGMEM = { + &CmndPowerCal, &CmndVoltageCal, &CmndCurrentCal, + &CmndPowerSet, &CmndVoltageSet, &CmndCurrentSet, &CmndFrequencySet, &CmndModuleAddress, +#ifdef USE_ENERGY_MARGIN_DETECTION + &CmndPowerDelta, &CmndPowerLow, &CmndPowerHigh, &CmndVoltageLow, &CmndVoltageHigh, &CmndCurrentLow, &CmndCurrentHigh, +#ifdef USE_ENERGY_POWER_LIMIT + &CmndMaxEnergy, &CmndMaxEnergyStart, + &CmndMaxPower, &CmndMaxPowerHold, &CmndMaxPowerWindow, + &CmndSafePower, &CmndSafePowerHold, &CmndSafePowerWindow, +#endif // USE_ENERGY_POWER_LIMIT +#endif // USE_ENERGY_MARGIN_DETECTION + &CmndEnergyReset, &CmndTariff }; -float energy_daily = 0; // 123.123 kWh -float energy_total = 0; // 12345.12345 kWh -unsigned long energy_kWhtoday_delta = 0; // 1212312345 Wh 10^-5 (deca micro Watt hours) - Overflows to energy_kWhtoday (HLW and CSE only) -unsigned long energy_kWhtoday; // 12312312 Wh * 10^-2 (deca milli Watt hours) - 5764 = 0.05764 kWh = 0.058 kWh = energy_daily -unsigned long energy_period = 0; // 12312312 Wh * 10^-2 (deca milli Watt hours) - 5764 = 0.05764 kWh = 0.058 kWh = energy_daily +const char kEnergyPhases[] PROGMEM = "|%s / %s|%s / %s / %s||[%s,%s]|[%s,%s,%s]"; -float energy_power_last[3] = { 0 }; -uint8_t energy_power_delta = 0; -uint8_t energy_data_valid = 0; +struct ENERGY { + float voltage[3] = { 0, 0, 0 }; // 123.1 V + float current[3] = { 0, 0, 0 }; // 123.123 A + float active_power[3] = { 0, 0, 0 }; // 123.1 W + float apparent_power[3] = { NAN, NAN, NAN }; // 123.1 VA + float reactive_power[3] = { NAN, NAN, NAN }; // 123.1 VAr + float power_factor[3] = { NAN, NAN, NAN }; // 0.12 + float frequency[3] = { NAN, NAN, NAN }; // 123.1 Hz -bool energy_voltage_available = true; // Enable if voltage is measured -bool energy_current_available = true; // Enable if current is measured + float start_energy = 0; // 12345.12345 kWh total previous + float daily = 0; // 123.123 kWh + float total = 0; // 12345.12345 kWh total energy + float export_active = NAN; // 123.123 KWh -bool energy_type_dc = false; -bool energy_power_on = true; -bool energy_min_power_flag = false; -bool energy_max_power_flag = false; -bool energy_min_voltage_flag = false; -bool energy_max_voltage_flag = false; -bool energy_min_current_flag = false; -bool energy_max_current_flag = false; + unsigned long kWhtoday_delta = 0; // 1212312345 Wh 10^-5 (deca micro Watt hours) - Overflows to Energy.kWhtoday (HLW and CSE only) + unsigned long kWhtoday_offset = 0; // 12312312 Wh * 10^-2 (deca milli Watt hours) - 5764 = 0.05764 kWh = 0.058 kWh = Energy.daily + unsigned long kWhtoday; // 12312312 Wh * 10^-2 (deca milli Watt hours) - 5764 = 0.05764 kWh = 0.058 kWh = Energy.daily + unsigned long period = 0; // 12312312 Wh * 10^-2 (deca milli Watt hours) - 5764 = 0.05764 kWh = 0.058 kWh = Energy.daily -uint8_t energy_power_steady_cntr = 8; // Allow for power on stabilization -uint8_t energy_max_energy_state = 0; + uint8_t fifth_second = 0; + uint8_t command_code = 0; + uint8_t data_valid[3] = { 0, 0, 0 }; -#if FEATURE_POWER_LIMIT -uint8_t energy_mplr_counter = 0; -uint16_t energy_mplh_counter = 0; -uint16_t energy_mplw_counter = 0; -#endif // FEATURE_POWER_LIMIT + uint8_t phase_count = 1; // Number of phases active + bool voltage_common = false; // Use single voltage + + bool voltage_available = true; // Enable if voltage is measured + bool current_available = true; // Enable if current is measured + + bool type_dc = false; + bool power_on = true; + +#ifdef USE_ENERGY_MARGIN_DETECTION + uint16_t power_history[3] = { 0 }; + uint8_t power_steady_counter = 8; // Allow for power on stabilization + bool power_delta = false; + bool min_power_flag = false; + bool max_power_flag = false; + bool min_voltage_flag = false; + bool max_voltage_flag = false; + bool min_current_flag = false; + bool max_current_flag = false; + +#ifdef USE_ENERGY_POWER_LIMIT + uint16_t mplh_counter = 0; + uint16_t mplw_counter = 0; + uint8_t mplr_counter = 0; + uint8_t max_energy_state = 0; +#endif // USE_ENERGY_POWER_LIMIT +#endif // USE_ENERGY_MARGIN_DETECTION +} Energy; -uint8_t energy_fifth_second = 0; Ticker ticker_energy; -int energy_command_code = 0; /********************************************************************************************/ +bool EnergyTariff1Active() // Off-Peak hours +{ + uint8_t dst = 0; + if (IsDst() && (Settings.tariff[0][1] != Settings.tariff[1][1])) { + dst = 1; + } + if (Settings.tariff[0][dst] != Settings.tariff[1][dst]) { + if (Settings.flag3.energy_weekend && ((RtcTime.day_of_week == 1) || + (RtcTime.day_of_week == 7))) { + return true; + } + uint32_t minutes = MinutesPastMidnight(); + if (Settings.tariff[0][dst] > Settings.tariff[1][dst]) { + // {"Tariff":{"Off-Peak":{"STD":"22:00","DST":"23:00"},"Standard":{"STD":"06:00","DST":"07:00"},"Weekend":"OFF"}} + return ((minutes >= Settings.tariff[0][dst]) || (minutes < Settings.tariff[1][dst])); + } else { + // {"Tariff":{"Off-Peak":{"STD":"00:29","DST":"01:29"},"Standard":{"STD":"07:29","DST":"08:29"},"Weekend":"OFF"}} + return ((minutes >= Settings.tariff[0][dst]) && (minutes < Settings.tariff[1][dst])); + } + } else { + return false; + } +} + void EnergyUpdateToday(void) { - if (energy_kWhtoday_delta > 1000) { - unsigned long delta = energy_kWhtoday_delta / 1000; - energy_kWhtoday_delta -= (delta * 1000); - energy_kWhtoday += delta; + if (Energy.kWhtoday_delta > 1000) { + unsigned long delta = Energy.kWhtoday_delta / 1000; + Energy.kWhtoday_delta -= (delta * 1000); + Energy.kWhtoday += delta; } - RtcSettings.energy_kWhtoday = energy_kWhtoday; - energy_daily = (float)energy_kWhtoday / 100000; - energy_total = (float)(RtcSettings.energy_kWhtotal + energy_kWhtoday) / 100000; + + RtcSettings.energy_kWhtoday = Energy.kWhtoday_offset + Energy.kWhtoday; + Energy.daily = (float)(RtcSettings.energy_kWhtoday) / 100000; + Energy.total = (float)(RtcSettings.energy_kWhtotal + RtcSettings.energy_kWhtoday) / 100000; + + if (RtcTime.valid){ // We calc the difference only if we have a valid RTC time. + + uint32_t energy_diff = (uint32_t)(Energy.total * 100000) - RtcSettings.energy_usage.last_usage_kWhtotal; + RtcSettings.energy_usage.last_usage_kWhtotal = (uint32_t)(Energy.total * 100000); + + uint32_t return_diff = 0; + if (!isnan(Energy.export_active)) { + return_diff = (uint32_t)(Energy.export_active * 100000) - RtcSettings.energy_usage.last_return_kWhtotal; + RtcSettings.energy_usage.last_return_kWhtotal = (uint32_t)(Energy.export_active * 100000); + } + + if (EnergyTariff1Active()) { // Tarrif1 = Off-Peak + RtcSettings.energy_usage.usage1_kWhtotal += energy_diff; + RtcSettings.energy_usage.return1_kWhtotal += return_diff; + } else { + RtcSettings.energy_usage.usage2_kWhtotal += energy_diff; + RtcSettings.energy_usage.return2_kWhtotal += return_diff; + } + } +} + +void EnergyUpdateTotal(float value, bool kwh) +{ +// char energy_total_chr[FLOATSZ]; +// dtostrfd(value, 4, energy_total_chr); +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("NRG: Energy Total %s %sWh"), energy_total_chr, (kwh) ? "k" : ""); + + uint32_t multiplier = (kwh) ? 100000 : 100; // kWh or Wh to deca milli Wh + + if (0 == Energy.start_energy || (value < Energy.start_energy)) { + Energy.start_energy = value; // Init after restart and handle roll-over if any + } + else if (value != Energy.start_energy) { + Energy.kWhtoday = (unsigned long)((value - Energy.start_energy) * multiplier); + } + + if ((Energy.total < (value - 0.01)) && Settings.flag3.hardware_energy_total) { // We subtract a little offset to avoid continuous updates + RtcSettings.energy_kWhtotal = (unsigned long)((value * multiplier) - Energy.kWhtoday_offset - Energy.kWhtoday); + Settings.energy_kWhtotal = RtcSettings.energy_kWhtotal; + Energy.total = (float)(RtcSettings.energy_kWhtotal + Energy.kWhtoday_offset + Energy.kWhtoday) / 100000; + Settings.energy_kWhtotal_time = (!Energy.kWhtoday_offset) ? LocalTime() : Midnight(); +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("NRG: Energy Total updated with hardware value")); + } + EnergyUpdateToday(); } /*********************************************************************************************/ void Energy200ms(void) { - energy_power_on = (power != 0) | Settings.flag.no_power_on_check; + Energy.power_on = (power != 0) | Settings.flag.no_power_on_check; - energy_fifth_second++; - if (5 == energy_fifth_second) { - energy_fifth_second = 0; + Energy.fifth_second++; + if (5 == Energy.fifth_second) { + Energy.fifth_second = 0; XnrgCall(FUNC_ENERGY_EVERY_SECOND); if (RtcTime.valid) { if (LocalTime() == Midnight()) { - Settings.energy_kWhyesterday = energy_kWhtoday; - Settings.energy_kWhtotal += energy_kWhtoday; - RtcSettings.energy_kWhtotal = Settings.energy_kWhtotal; - energy_kWhtoday = 0; - energy_kWhtoday_delta = 0; - energy_period = energy_kWhtoday; + Settings.energy_kWhyesterday = RtcSettings.energy_kWhtoday; + + RtcSettings.energy_kWhtotal += RtcSettings.energy_kWhtoday; + Settings.energy_kWhtotal = RtcSettings.energy_kWhtotal; + Energy.kWhtoday = 0; + Energy.kWhtoday_offset = 0; + RtcSettings.energy_kWhtoday = 0; + Energy.start_energy = 0; + + Energy.kWhtoday_delta = 0; + Energy.period = Energy.kWhtoday; EnergyUpdateToday(); - energy_max_energy_state = 3; +#if defined(USE_ENERGY_MARGIN_DETECTION) && defined(USE_ENERGY_POWER_LIMIT) + Energy.max_energy_state = 3; +#endif // USE_ENERGY_POWER_LIMIT } - if ((RtcTime.hour == Settings.energy_max_energy_start) && (3 == energy_max_energy_state)) { - energy_max_energy_state = 0; +#if defined(USE_ENERGY_MARGIN_DETECTION) && defined(USE_ENERGY_POWER_LIMIT) + if ((RtcTime.hour == Settings.energy_max_energy_start) && (3 == Energy.max_energy_state )) { + Energy.max_energy_state = 0; } +#endif // USE_ENERGY_POWER_LIMIT + } } @@ -148,11 +254,14 @@ void Energy200ms(void) void EnergySaveState(void) { Settings.energy_kWhdoy = (RtcTime.valid) ? RtcTime.day_of_year : 0; - Settings.energy_kWhtoday = energy_kWhtoday; - RtcSettings.energy_kWhtoday = energy_kWhtoday; + + Settings.energy_kWhtoday = RtcSettings.energy_kWhtoday; Settings.energy_kWhtotal = RtcSettings.energy_kWhtotal; + + Settings.energy_usage = RtcSettings.energy_usage; } +#ifdef USE_ENERGY_MARGIN_DETECTION bool EnergyMargin(bool type, uint16_t margin, uint16_t value, bool &flag, bool &save_flag) { bool change; @@ -168,70 +277,63 @@ bool EnergyMargin(bool type, uint16_t margin, uint16_t value, bool &flag, bool & return (change != save_flag); } -void EnergySetPowerSteadyCounter(void) -{ - energy_power_steady_cntr = 2; -} - void EnergyMarginCheck(void) { - uint16_t energy_daily_u = 0; - uint16_t energy_power_u = 0; - uint16_t energy_voltage_u = 0; - uint16_t energy_current_u = 0; - bool flag; - bool jsonflg; - - if (energy_power_steady_cntr) { - energy_power_steady_cntr--; + if (Energy.power_steady_counter) { + Energy.power_steady_counter--; return; } + uint16_t energy_power_u = (uint16_t)(Energy.active_power[0]); + if (Settings.energy_power_delta) { - float delta = abs(energy_power_last[0] - energy_active_power); - // Any delta compared to minimal delta - float min_power = (energy_power_last[0] > energy_active_power) ? energy_active_power : energy_power_last[0]; - if (((delta / min_power) * 100) > Settings.energy_power_delta) { - energy_power_delta = 1; - energy_power_last[1] = energy_active_power; // We only want one report so reset history - energy_power_last[2] = energy_active_power; + uint16_t delta = abs(Energy.power_history[0] - energy_power_u); + uint16_t min_power = (Energy.power_history[0] > energy_power_u) ? energy_power_u : Energy.power_history[0]; + + DEBUG_DRIVER_LOG(PSTR("NRG: Delta %d, Power %d"), delta, min_power); + + if ( ((Settings.energy_power_delta < 101) && (((delta * 100) / min_power) > Settings.energy_power_delta)) || // 1..100 = Percentage + ((Settings.energy_power_delta > 100) && (delta > (Settings.energy_power_delta -100))) ) { // 101..32000 = Absolute + Energy.power_delta = true; + Energy.power_history[1] = Energy.active_power[0]; // We only want one report so reset history + Energy.power_history[2] = Energy.active_power[0]; } } - energy_power_last[0] = energy_power_last[1]; // Shift in history every second allowing power changes to settle for up to three seconds - energy_power_last[1] = energy_power_last[2]; - energy_power_last[2] = energy_active_power; + Energy.power_history[0] = Energy.power_history[1]; // Shift in history every second allowing power changes to settle for up to three seconds + Energy.power_history[1] = Energy.power_history[2]; + Energy.power_history[2] = energy_power_u; - if (energy_power_on && (Settings.energy_min_power || Settings.energy_max_power || Settings.energy_min_voltage || Settings.energy_max_voltage || Settings.energy_min_current || Settings.energy_max_current)) { - energy_power_u = (uint16_t)(energy_active_power); - energy_voltage_u = (uint16_t)(energy_voltage); - energy_current_u = (uint16_t)(energy_current * 1000); + if (Energy.power_on && (Settings.energy_min_power || Settings.energy_max_power || Settings.energy_min_voltage || Settings.energy_max_voltage || Settings.energy_min_current || Settings.energy_max_current)) { + uint16_t energy_voltage_u = (uint16_t)(Energy.voltage[0]); + uint16_t energy_current_u = (uint16_t)(Energy.current[0] * 1000); -// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("NRG: W %d, U %d, I %d"), energy_power_u, energy_voltage_u, energy_current_u); + DEBUG_DRIVER_LOG(PSTR("NRG: W %d, U %d, I %d"), energy_power_u, energy_voltage_u, energy_current_u); Response_P(PSTR("{")); - jsonflg = false; - if (EnergyMargin(false, Settings.energy_min_power, energy_power_u, flag, energy_min_power_flag)) { + bool flag; + bool jsonflg = false; + if (EnergyMargin(false, Settings.energy_min_power, energy_power_u, flag, Energy.min_power_flag)) { ResponseAppend_P(PSTR("%s\"" D_CMND_POWERLOW "\":\"%s\""), (jsonflg)?",":"", GetStateText(flag)); jsonflg = true; } - if (EnergyMargin(true, Settings.energy_max_power, energy_power_u, flag, energy_max_power_flag)) { + if (EnergyMargin(true, Settings.energy_max_power, energy_power_u, flag, Energy.max_power_flag)) { ResponseAppend_P(PSTR("%s\"" D_CMND_POWERHIGH "\":\"%s\""), (jsonflg)?",":"", GetStateText(flag)); jsonflg = true; } - if (EnergyMargin(false, Settings.energy_min_voltage, energy_voltage_u, flag, energy_min_voltage_flag)) { + if (EnergyMargin(false, Settings.energy_min_voltage, energy_voltage_u, flag, Energy.min_voltage_flag)) { ResponseAppend_P(PSTR("%s\"" D_CMND_VOLTAGELOW "\":\"%s\""), (jsonflg)?",":"", GetStateText(flag)); jsonflg = true; } - if (EnergyMargin(true, Settings.energy_max_voltage, energy_voltage_u, flag, energy_max_voltage_flag)) { + if (EnergyMargin(true, Settings.energy_max_voltage, energy_voltage_u, flag, Energy.max_voltage_flag)) { ResponseAppend_P(PSTR("%s\"" D_CMND_VOLTAGEHIGH "\":\"%s\""), (jsonflg)?",":"", GetStateText(flag)); jsonflg = true; } - if (EnergyMargin(false, Settings.energy_min_current, energy_current_u, flag, energy_min_current_flag)) { - ResponseAppend_P(PSTR("%s%s\"" D_CMND_CURRENTLOW "\":\"%s\""), (jsonflg)?",":"", GetStateText(flag)); + if (EnergyMargin(false, Settings.energy_min_current, energy_current_u, flag, Energy.min_current_flag)) { + ResponseAppend_P(PSTR("%s\"" D_CMND_CURRENTLOW "\":\"%s\""), (jsonflg)?",":"", GetStateText(flag)); jsonflg = true; } - if (EnergyMargin(true, Settings.energy_max_current, energy_current_u, flag, energy_max_current_flag)) { - ResponseAppend_P(PSTR("%s%s\"" D_CMND_CURRENTHIGH "\":\"%s\""), (jsonflg)?",":"", GetStateText(flag)); + if (EnergyMargin(true, Settings.energy_max_current, energy_current_u, flag, Energy.max_current_flag)) { + ResponseAppend_P(PSTR("%s\"" D_CMND_CURRENTHIGH "\":\"%s\""), (jsonflg)?",":"", GetStateText(flag)); jsonflg = true; } if (jsonflg) { @@ -241,43 +343,43 @@ void EnergyMarginCheck(void) } } -#if FEATURE_POWER_LIMIT +#ifdef USE_ENERGY_POWER_LIMIT // Max Power if (Settings.energy_max_power_limit) { - if (energy_active_power > Settings.energy_max_power_limit) { - if (!energy_mplh_counter) { - energy_mplh_counter = Settings.energy_max_power_limit_hold; + if (Energy.active_power[0] > Settings.energy_max_power_limit) { + if (!Energy.mplh_counter) { + Energy.mplh_counter = Settings.energy_max_power_limit_hold; } else { - energy_mplh_counter--; - if (!energy_mplh_counter) { - Response_P(PSTR("{\"" D_JSON_MAXPOWERREACHED "\":\"%d%s\"}"), energy_power_u, (Settings.flag.value_units) ? " " D_UNIT_WATT : ""); + Energy.mplh_counter--; + if (!Energy.mplh_counter) { + ResponseTime_P(PSTR(",\"" D_JSON_MAXPOWERREACHED "\":%d}"), energy_power_u); MqttPublishPrefixTopic_P(STAT, S_RSLT_WARNING); EnergyMqttShow(); - ExecuteCommandPower(1, POWER_OFF, SRC_MAXPOWER); - if (!energy_mplr_counter) { - energy_mplr_counter = Settings.param[P_MAX_POWER_RETRY] +1; + SetAllPower(POWER_ALL_OFF, SRC_MAXPOWER); + if (!Energy.mplr_counter) { + Energy.mplr_counter = Settings.param[P_MAX_POWER_RETRY] +1; } - energy_mplw_counter = Settings.energy_max_power_limit_window; + Energy.mplw_counter = Settings.energy_max_power_limit_window; } } } else if (power && (energy_power_u <= Settings.energy_max_power_limit)) { - energy_mplh_counter = 0; - energy_mplr_counter = 0; - energy_mplw_counter = 0; + Energy.mplh_counter = 0; + Energy.mplr_counter = 0; + Energy.mplw_counter = 0; } if (!power) { - if (energy_mplw_counter) { - energy_mplw_counter--; + if (Energy.mplw_counter) { + Energy.mplw_counter--; } else { - if (energy_mplr_counter) { - energy_mplr_counter--; - if (energy_mplr_counter) { - Response_P(PSTR("{\"" D_JSON_POWERMONITOR "\":\"%s\"}"), GetStateText(1)); + if (Energy.mplr_counter) { + Energy.mplr_counter--; + if (Energy.mplr_counter) { + ResponseTime_P(PSTR(",\"" D_JSON_POWERMONITOR "\":\"%s\"}"), GetStateText(1)); MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_JSON_POWERMONITOR)); - ExecuteCommandPower(1, POWER_ON, SRC_MAXPOWER); + RestorePower(true, SRC_MAXPOWER); } else { - Response_P(PSTR("{\"" D_JSON_MAXPOWERREACHEDRETRY "\":\"%s\"}"), GetStateText(0)); + ResponseTime_P(PSTR(",\"" D_JSON_MAXPOWERREACHEDRETRY "\":\"%s\"}"), GetStateText(0)); MqttPublishPrefixTopic_P(STAT, S_RSLT_WARNING); EnergyMqttShow(); } @@ -288,278 +390,440 @@ void EnergyMarginCheck(void) // Max Energy if (Settings.energy_max_energy) { - energy_daily_u = (uint16_t)(energy_daily * 1000); - if (!energy_max_energy_state && (RtcTime.hour == Settings.energy_max_energy_start)) { - energy_max_energy_state = 1; - Response_P(PSTR("{\"" D_JSON_ENERGYMONITOR "\":\"%s\"}"), GetStateText(1)); + uint16_t energy_daily_u = (uint16_t)(Energy.daily * 1000); + if (!Energy.max_energy_state && (RtcTime.hour == Settings.energy_max_energy_start)) { + Energy.max_energy_state = 1; + ResponseTime_P(PSTR(",\"" D_JSON_ENERGYMONITOR "\":\"%s\"}"), GetStateText(1)); MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_JSON_ENERGYMONITOR)); - ExecuteCommandPower(1, POWER_ON, SRC_MAXENERGY); + RestorePower(true, SRC_MAXENERGY); } - else if ((1 == energy_max_energy_state) && (energy_daily_u >= Settings.energy_max_energy)) { - energy_max_energy_state = 2; - dtostrfd(energy_daily, 3, mqtt_data); - Response_P(PSTR("{\"" D_JSON_MAXENERGYREACHED "\":\"%s%s\"}"), mqtt_data, (Settings.flag.value_units) ? " " D_UNIT_KILOWATTHOUR : ""); + else if ((1 == Energy.max_energy_state ) && (energy_daily_u >= Settings.energy_max_energy)) { + Energy.max_energy_state = 2; + char stemp[FLOATSZ]; + dtostrfd(Energy.daily, 3, stemp); + ResponseTime_P(PSTR(",\"" D_JSON_MAXENERGYREACHED "\":%s}"), stemp); MqttPublishPrefixTopic_P(STAT, S_RSLT_WARNING); EnergyMqttShow(); - ExecuteCommandPower(1, POWER_OFF, SRC_MAXENERGY); + SetAllPower(POWER_ALL_OFF, SRC_MAXENERGY); } } -#endif // FEATURE_POWER_LIMIT +#endif // USE_ENERGY_POWER_LIMIT - if (energy_power_delta) EnergyMqttShow(); + if (Energy.power_delta) { EnergyMqttShow(); } } void EnergyMqttShow(void) { // {"Time":"2017-12-16T11:48:55","ENERGY":{"Total":0.212,"Yesterday":0.000,"Today":0.014,"Period":2.0,"Power":22.0,"Factor":1.00,"Voltage":213.6,"Current":0.100}} - Response_P(PSTR("{\"" D_JSON_TIME "\":\"%s\""), GetDateAndTime(DT_LOCAL).c_str()); int tele_period_save = tele_period; tele_period = 2; + mqtt_data[0] = '\0'; + ResponseAppendTime(); EnergyShow(true); tele_period = tele_period_save; ResponseJsonEnd(); MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); - energy_power_delta = 0; + Energy.power_delta = false; } +#endif // USE_ENERGY_MARGIN_DETECTION -void EnergyOverTempCheck() +void EnergyEverySecond() { + // Overtemp check if (global_update) { - if (power && (global_temperature != 9999) && (global_temperature > ENERGY_OVERTEMP)) { // Device overtemp, turn off relays + if (power && (global_temperature != 9999) && (global_temperature > Settings.param[P_OVER_TEMP])) { // Device overtemp, turn off relays SetAllPower(POWER_ALL_OFF, SRC_OVERTEMP); } } - if (energy_data_valid <= ENERGY_WATCHDOG) { - energy_data_valid++; - if (energy_data_valid > ENERGY_WATCHDOG) { - // Reset energy registers - energy_voltage = 0; - energy_current = 0; - energy_active_power = 0; - if (!isnan(energy_frequency)) { energy_frequency = 0; } - if (!isnan(energy_power_factor)) { energy_power_factor = 0; } - energy_start = 0; + + // Invalid data reset + uint32_t data_valid = Energy.phase_count; + for (uint32_t i = 0; i < Energy.phase_count; i++) { + if (Energy.data_valid[i] <= ENERGY_WATCHDOG) { + Energy.data_valid[i]++; + if (Energy.data_valid[i] > ENERGY_WATCHDOG) { + // Reset energy registers + Energy.voltage[i] = 0; + Energy.current[i] = 0; + Energy.active_power[i] = 0; + if (!isnan(Energy.apparent_power[i])) { Energy.apparent_power[i] = 0; } + if (!isnan(Energy.reactive_power[i])) { Energy.reactive_power[i] = 0; } + if (!isnan(Energy.frequency[i])) { Energy.frequency[i] = 0; } + if (!isnan(Energy.power_factor[i])) { Energy.power_factor[i] = 0; } + + data_valid--; + } } } + if (!data_valid) { + if (!isnan(Energy.export_active)) { Energy.export_active = 0; } + Energy.start_energy = 0; + + XnrgCall(FUNC_ENERGY_RESET); + } + +#ifdef USE_ENERGY_MARGIN_DETECTION + EnergyMarginCheck(); +#endif // USE_ENERGY_MARGIN_DETECTION } /*********************************************************************************************\ * Commands \*********************************************************************************************/ -bool EnergyCommand(void) +void EnergyCommandCalResponse(uint32_t nvalue) { - char command [CMDSZ]; - char sunit[CMDSZ]; - bool serviced = true; - bool status_flag = false; - uint8_t unit = 0; - unsigned long nvalue = 0; + snprintf_P(XdrvMailbox.command, CMDSZ, PSTR("%sCal"), XdrvMailbox.command); + ResponseCmndNumber(nvalue); +} - int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic, kEnergyCommands); - energy_command_code = command_code; - if (-1 == command_code) { - serviced = false; // Unknown command - } - else if (CMND_POWERDELTA == command_code) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 101)) { - Settings.energy_power_delta = XdrvMailbox.payload; - } - nvalue = Settings.energy_power_delta; - unit = UNIT_PERCENTAGE; - } - else if (CMND_POWERLOW == command_code) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { - Settings.energy_min_power = XdrvMailbox.payload; - } - nvalue = Settings.energy_min_power; - unit = UNIT_WATT; - } - else if (CMND_POWERHIGH == command_code) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { - Settings.energy_max_power = XdrvMailbox.payload; - } - nvalue = Settings.energy_max_power; - unit = UNIT_WATT; - } - else if (CMND_VOLTAGELOW == command_code) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 501)) { - Settings.energy_min_voltage = XdrvMailbox.payload; - } - nvalue = Settings.energy_min_voltage; - unit = UNIT_VOLT; - } - else if (CMND_VOLTAGEHIGH == command_code) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 501)) { - Settings.energy_max_voltage = XdrvMailbox.payload; - } - nvalue = Settings.energy_max_voltage; - unit = UNIT_VOLT; - } - else if (CMND_CURRENTLOW == command_code) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 16001)) { - Settings.energy_min_current = XdrvMailbox.payload; - } - nvalue = Settings.energy_min_current; - unit = UNIT_MILLIAMPERE; - } - else if (CMND_CURRENTHIGH == command_code) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 16001)) { - Settings.energy_max_current = XdrvMailbox.payload; - } - nvalue = Settings.energy_max_current; - unit = UNIT_MILLIAMPERE; - } - else if ((CMND_ENERGYRESET == command_code) && (XdrvMailbox.index > 0) && (XdrvMailbox.index <= 3)) { +void CmndEnergyReset(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 3)) { char *p; unsigned long lnum = strtoul(XdrvMailbox.data, &p, 10); if (p != XdrvMailbox.data) { switch (XdrvMailbox.index) { case 1: - energy_kWhtoday = lnum *100; - energy_kWhtoday_delta = 0; - energy_period = energy_kWhtoday; - Settings.energy_kWhtoday = energy_kWhtoday; - RtcSettings.energy_kWhtoday = energy_kWhtoday; - energy_daily = (float)energy_kWhtoday / 100000; - if (!RtcSettings.energy_kWhtotal && !energy_kWhtoday) { Settings.energy_kWhtotal_time = LocalTime(); } + // Reset Energy Today + Energy.kWhtoday_offset = lnum *100; + Energy.kWhtoday = 0; + Energy.kWhtoday_delta = 0; + Energy.start_energy = 0; + Energy.period = Energy.kWhtoday_offset; + Settings.energy_kWhtoday = Energy.kWhtoday_offset; + RtcSettings.energy_kWhtoday = Energy.kWhtoday_offset; + Energy.daily = (float)Energy.kWhtoday_offset / 100000; + if (!RtcSettings.energy_kWhtotal && !Energy.kWhtoday_offset) { + Settings.energy_kWhtotal_time = LocalTime(); + } break; case 2: + // Reset Energy Yesterday Settings.energy_kWhyesterday = lnum *100; break; case 3: + // Reset Energy Total RtcSettings.energy_kWhtotal = lnum *100; Settings.energy_kWhtotal = RtcSettings.energy_kWhtotal; - energy_total = (float)(RtcSettings.energy_kWhtotal + energy_kWhtoday) / 100000; - Settings.energy_kWhtotal_time = (!energy_kWhtoday) ? LocalTime() : Midnight(); +// Energy.total = (float)(RtcSettings.energy_kWhtotal + Energy.kWhtoday_offset + Energy.kWhtoday) / 100000; + Settings.energy_kWhtotal_time = (!Energy.kWhtoday_offset) ? LocalTime() : Midnight(); + RtcSettings.energy_usage.last_usage_kWhtotal = (uint32_t)(Energy.total * 1000); break; } } - char energy_total_chr[33]; - dtostrfd(energy_total, Settings.flag2.energy_resolution, energy_total_chr); - char energy_daily_chr[33]; - dtostrfd(energy_daily, Settings.flag2.energy_resolution, energy_daily_chr); - char energy_yesterday_chr[33]; - dtostrfd((float)Settings.energy_kWhyesterday / 100000, Settings.flag2.energy_resolution, energy_yesterday_chr); + } + else if ((XdrvMailbox.index > 3) && (XdrvMailbox.index <= 5)) { + char *p; + char *str = strtok_r(XdrvMailbox.data, ", ", &p); + int32_t position = -1; + uint32_t values[2]; - Response_P(PSTR("{\"%s\":{\"" D_JSON_TOTAL "\":%s,\"" D_JSON_YESTERDAY "\":%s,\"" D_JSON_TODAY "\":%s}}"), - command, energy_total_chr, energy_yesterday_chr, energy_daily_chr); - status_flag = true; - } - else if ((CMND_POWERCAL == command_code) && XnrgCall(FUNC_COMMAND)) { // microseconds - if ((XdrvMailbox.payload > 999) && (XdrvMailbox.payload < 32001)) { Settings.energy_power_calibration = XdrvMailbox.payload; } - nvalue = Settings.energy_power_calibration; - unit = UNIT_MICROSECOND; - } - else if ((CMND_VOLTAGECAL == command_code) && XnrgCall(FUNC_COMMAND)) { // microseconds - if ((XdrvMailbox.payload > 999) && (XdrvMailbox.payload < 32001)) { Settings.energy_voltage_calibration = XdrvMailbox.payload; } - nvalue = Settings.energy_voltage_calibration; - unit = UNIT_MICROSECOND; - } - else if ((CMND_CURRENTCAL == command_code) && XnrgCall(FUNC_COMMAND)) { // microseconds - if ((XdrvMailbox.payload > 999) && (XdrvMailbox.payload < 32001)) { Settings.energy_current_calibration = XdrvMailbox.payload; } - nvalue = Settings.energy_current_calibration; - unit = UNIT_MICROSECOND; - } - else if ((CMND_POWERSET == command_code) && XnrgCall(FUNC_COMMAND)) { // Watt - nvalue = Settings.energy_power_calibration; - unit = UNIT_MILLISECOND; - } - else if ((CMND_VOLTAGESET == command_code) && XnrgCall(FUNC_COMMAND)) { // Volt - nvalue = Settings.energy_voltage_calibration; - unit = UNIT_MILLISECOND; - } - else if ((CMND_CURRENTSET == command_code) && XnrgCall(FUNC_COMMAND)) { // milliAmpere - nvalue = Settings.energy_current_calibration; - unit = UNIT_MILLISECOND; - } - else if ((CMND_FREQUENCYSET == command_code) && XnrgCall(FUNC_COMMAND)) { // Hz - nvalue = Settings.energy_frequency_calibration; - unit = UNIT_MILLISECOND; - } - -#if FEATURE_POWER_LIMIT - else if (CMND_MAXPOWER == command_code) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { - Settings.energy_max_power_limit = XdrvMailbox.payload; - } - nvalue = Settings.energy_max_power_limit; - unit = UNIT_WATT; - } - else if (CMND_MAXPOWERHOLD == command_code) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { - Settings.energy_max_power_limit_hold = (1 == XdrvMailbox.payload) ? MAX_POWER_HOLD : XdrvMailbox.payload; - } - nvalue = Settings.energy_max_power_limit_hold; - unit = UNIT_SECOND; - } - else if (CMND_MAXPOWERWINDOW == command_code) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { - Settings.energy_max_power_limit_window = (1 == XdrvMailbox.payload) ? MAX_POWER_WINDOW : XdrvMailbox.payload; - } - nvalue = Settings.energy_max_power_limit_window; - unit = UNIT_SECOND; - } - else if (CMND_SAFEPOWER == command_code) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { - Settings.energy_max_power_safe_limit = XdrvMailbox.payload; - } - nvalue = Settings.energy_max_power_safe_limit; - unit = UNIT_WATT; - } - else if (CMND_SAFEPOWERHOLD == command_code) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { - Settings.energy_max_power_safe_limit_hold = (1 == XdrvMailbox.payload) ? SAFE_POWER_HOLD : XdrvMailbox.payload; - } - nvalue = Settings.energy_max_power_safe_limit_hold; - unit = UNIT_SECOND; - } - else if (CMND_SAFEPOWERWINDOW == command_code) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 1440)) { - Settings.energy_max_power_safe_limit_window = (1 == XdrvMailbox.payload) ? SAFE_POWER_WINDOW : XdrvMailbox.payload; - } - nvalue = Settings.energy_max_power_safe_limit_window; - unit = UNIT_MINUTE; - } - else if (CMND_MAXENERGY == command_code) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { - Settings.energy_max_energy = XdrvMailbox.payload; - energy_max_energy_state = 3; - } - nvalue = Settings.energy_max_energy; - unit = UNIT_WATTHOUR; - } - else if (CMND_MAXENERGYSTART == command_code) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 24)) { - Settings.energy_max_energy_start = XdrvMailbox.payload; - } - nvalue = Settings.energy_max_energy_start; - unit = UNIT_HOUR; - } -#endif // FEATURE_POWER_LIMIT - else serviced = false; // Unknown command - - if (serviced && !status_flag) { - - if (UNIT_MILLISECOND == unit) { - snprintf_P(command, sizeof(command), PSTR("%sCal"), command); - unit = UNIT_MICROSECOND; + while ((str != nullptr) && (position < 1)) { + uint32_t value = strtoul(str, nullptr, 10); + position++; + values[position] = value *100; + str = strtok_r(nullptr, ", ", &p); } - if (Settings.flag.value_units) { - Response_P(S_JSON_COMMAND_LVALUE_SPACE_UNIT, command, nvalue, GetTextIndexed(sunit, sizeof(sunit), unit, kUnitNames)); - } else { - Response_P(S_JSON_COMMAND_LVALUE, command, nvalue); - } + switch (XdrvMailbox.index) + { + case 4: + // Reset energy_usage.usage totals + if (position > -1) { + RtcSettings.energy_usage.usage1_kWhtotal = values[0]; + } + if (position > 0) { + RtcSettings.energy_usage.usage2_kWhtotal = values[1]; + } + Settings.energy_usage.usage1_kWhtotal = RtcSettings.energy_usage.usage1_kWhtotal; + Settings.energy_usage.usage2_kWhtotal = RtcSettings.energy_usage.usage2_kWhtotal; + break; + case 5: + // Reset energy_usage.return totals + if (position > -1) { + RtcSettings.energy_usage.return1_kWhtotal = values[0]; + } + if (position > 0) { + RtcSettings.energy_usage.return2_kWhtotal = values[1]; + } + Settings.energy_usage.return1_kWhtotal = RtcSettings.energy_usage.return1_kWhtotal; + Settings.energy_usage.return2_kWhtotal = RtcSettings.energy_usage.return2_kWhtotal; + break; + } } - return serviced; + Energy.total = (float)(RtcSettings.energy_kWhtotal + Energy.kWhtoday_offset + Energy.kWhtoday) / 100000; + + char energy_total_chr[FLOATSZ]; + dtostrfd(Energy.total, Settings.flag2.energy_resolution, energy_total_chr); + char energy_daily_chr[FLOATSZ]; + dtostrfd(Energy.daily, Settings.flag2.energy_resolution, energy_daily_chr); + char energy_yesterday_chr[FLOATSZ]; + dtostrfd((float)Settings.energy_kWhyesterday / 100000, Settings.flag2.energy_resolution, energy_yesterday_chr); + + char energy_usage1_chr[FLOATSZ]; + dtostrfd((float)Settings.energy_usage.usage1_kWhtotal / 100000, Settings.flag2.energy_resolution, energy_usage1_chr); + char energy_usage2_chr[FLOATSZ]; + dtostrfd((float)Settings.energy_usage.usage2_kWhtotal / 100000, Settings.flag2.energy_resolution, energy_usage2_chr); + char energy_return1_chr[FLOATSZ]; + dtostrfd((float)Settings.energy_usage.return1_kWhtotal / 100000, Settings.flag2.energy_resolution, energy_return1_chr); + char energy_return2_chr[FLOATSZ]; + dtostrfd((float)Settings.energy_usage.return2_kWhtotal / 100000, Settings.flag2.energy_resolution, energy_return2_chr); + + Response_P(PSTR("{\"%s\":{\"" D_JSON_TOTAL "\":%s,\"" D_JSON_YESTERDAY "\":%s,\"" D_JSON_TODAY "\":%s,\"" D_JSON_USAGE "\":[%s,%s],\"" D_JSON_EXPORT "\":[%s,%s]}}"), + XdrvMailbox.command, energy_total_chr, energy_yesterday_chr, energy_daily_chr, energy_usage1_chr, energy_usage2_chr, energy_return1_chr, energy_return2_chr); } +void CmndTariff(void) +{ + // Tariff1 22:00,23:00 - Tariff1 start hour for Standard Time and Daylight Savings Time + // Tariff2 6:00,7:00 - Tariff2 start hour for Standard Time and Daylight Savings Time + // Tariffx 1320, 1380 = minutes and also 22:00, 23:00 + // Tariffx 22, 23 = hours and also 22:00, 23:00 + // Tariff9 0/1 + + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 2)) { + uint32_t tariff = XdrvMailbox.index -1; + uint32_t time_type = 0; + char *p; + char *str = strtok_r(XdrvMailbox.data, ", ", &p); // 23:15, 22:30 + while ((str != nullptr) && (time_type < 2)) { + char *q; + uint32_t value = strtol(str, &q, 10); // 23 or 22 + Settings.tariff[tariff][time_type] = value; + if (value < 24) { // Below 24 is hours + Settings.tariff[tariff][time_type] *= 60; // Multiply hours by 60 minutes + char *minute = strtok_r(nullptr, ":", &q); + if (minute) { + value = strtol(minute, nullptr, 10); // 15 or 30 + if (value > 59) { + value = 59; + } + Settings.tariff[tariff][time_type] += value; + } + } + if (Settings.tariff[tariff][time_type] > 1439) { + Settings.tariff[tariff][time_type] = 1439; // Max is 23:59 + } + str = strtok_r(nullptr, ", ", &p); + time_type++; + } + } + else if (XdrvMailbox.index == 9) { + Settings.flag3.energy_weekend = XdrvMailbox.payload & 1; + } + Response_P(PSTR("{\"%s\":{\"Off-Peak\":{\"STD\":\"%s\",\"DST\":\"%s\"},\"Standard\":{\"STD\":\"%s\",\"DST\":\"%s\"},\"Weekend\":\"%s\"}}"), + XdrvMailbox.command, + GetMinuteTime(Settings.tariff[0][0]).c_str(),GetMinuteTime(Settings.tariff[0][1]).c_str(), + GetMinuteTime(Settings.tariff[1][0]).c_str(),GetMinuteTime(Settings.tariff[1][1]).c_str(), + GetStateText(Settings.flag3.energy_weekend)); +} + +void CmndPowerCal(void) +{ + Energy.command_code = CMND_POWERCAL; + if (XnrgCall(FUNC_COMMAND)) { // microseconds + if ((XdrvMailbox.payload > 999) && (XdrvMailbox.payload < 32001)) { + Settings.energy_power_calibration = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_power_calibration); + } +} + +void CmndVoltageCal(void) +{ + Energy.command_code = CMND_VOLTAGECAL; + if (XnrgCall(FUNC_COMMAND)) { // microseconds + if ((XdrvMailbox.payload > 999) && (XdrvMailbox.payload < 32001)) { + Settings.energy_voltage_calibration = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_voltage_calibration); + } +} + +void CmndCurrentCal(void) +{ + Energy.command_code = CMND_CURRENTCAL; + if (XnrgCall(FUNC_COMMAND)) { // microseconds + if ((XdrvMailbox.payload > 999) && (XdrvMailbox.payload < 32001)) { + Settings.energy_current_calibration = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_current_calibration); + } +} + +void CmndPowerSet(void) +{ + Energy.command_code = CMND_POWERSET; + if (XnrgCall(FUNC_COMMAND)) { // Watt + EnergyCommandCalResponse(Settings.energy_power_calibration); + } +} + +void CmndVoltageSet(void) +{ + Energy.command_code = CMND_VOLTAGESET; + if (XnrgCall(FUNC_COMMAND)) { // Volt + EnergyCommandCalResponse(Settings.energy_voltage_calibration); + } +} + +void CmndCurrentSet(void) +{ + Energy.command_code = CMND_CURRENTSET; + if (XnrgCall(FUNC_COMMAND)) { // milliAmpere + EnergyCommandCalResponse(Settings.energy_current_calibration); + } +} + +void CmndFrequencySet(void) +{ + Energy.command_code = CMND_FREQUENCYSET; + if (XnrgCall(FUNC_COMMAND)) { // Hz + EnergyCommandCalResponse(Settings.energy_frequency_calibration); + } +} + +void CmndModuleAddress(void) +{ + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 4) && (1 == Energy.phase_count)) { + Energy.command_code = CMND_MODULEADDRESS; + if (XnrgCall(FUNC_COMMAND)) { // Module address + ResponseCmndDone(); + } + } +} + +#ifdef USE_ENERGY_MARGIN_DETECTION +void CmndPowerDelta(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 32000)) { + Settings.energy_power_delta = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_power_delta); +} + +void CmndPowerLow(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { + Settings.energy_min_power = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_min_power); +} + +void CmndPowerHigh(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { + Settings.energy_max_power = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_max_power); +} + +void CmndVoltageLow(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 501)) { + Settings.energy_min_voltage = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_min_voltage); +} + +void CmndVoltageHigh(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 501)) { + Settings.energy_max_voltage = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_max_voltage); +} + +void CmndCurrentLow(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 16001)) { + Settings.energy_min_current = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_min_current); +} + +void CmndCurrentHigh(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 16001)) { + Settings.energy_max_current = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_max_current); +} + +#ifdef USE_ENERGY_POWER_LIMIT +void CmndMaxPower(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { + Settings.energy_max_power_limit = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_max_power_limit); +} + +void CmndMaxPowerHold(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { + Settings.energy_max_power_limit_hold = (1 == XdrvMailbox.payload) ? MAX_POWER_HOLD : XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_max_power_limit_hold); +} + +void CmndMaxPowerWindow(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { + Settings.energy_max_power_limit_window = (1 == XdrvMailbox.payload) ? MAX_POWER_WINDOW : XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_max_power_limit_window); +} + +void CmndSafePower(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { + Settings.energy_max_power_safe_limit = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_max_power_safe_limit); +} + +void CmndSafePowerHold(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { + Settings.energy_max_power_safe_limit_hold = (1 == XdrvMailbox.payload) ? SAFE_POWER_HOLD : XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_max_power_safe_limit_hold); +} + +void CmndSafePowerWindow(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 1440)) { + Settings.energy_max_power_safe_limit_window = (1 == XdrvMailbox.payload) ? SAFE_POWER_WINDOW : XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_max_power_safe_limit_window); +} + +void CmndMaxEnergy(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { + Settings.energy_max_energy = XdrvMailbox.payload; + Energy.max_energy_state = 3; + } + ResponseCmndNumber(Settings.energy_max_energy); +} + +void CmndMaxEnergyStart(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 24)) { + Settings.energy_max_energy_start = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_max_energy_start); +} +#endif // USE_ENERGY_POWER_LIMIT +#endif // USE_ENERGY_MARGIN_DETECTION + void EnergyDrvInit(void) { energy_flg = ENERGY_NONE; - XnrgCall(FUNC_PRE_INIT); + XnrgCall(FUNC_PRE_INIT); // Find first energy driver } void EnergySnsInit(void) @@ -567,9 +831,18 @@ void EnergySnsInit(void) XnrgCall(FUNC_INIT); if (energy_flg) { - energy_kWhtoday = (RtcSettingsValid()) ? RtcSettings.energy_kWhtoday : (RtcTime.day_of_year == Settings.energy_kWhdoy) ? Settings.energy_kWhtoday : 0; - energy_kWhtoday_delta = 0; - energy_period = energy_kWhtoday; + if (RtcSettingsValid()) { + Energy.kWhtoday_offset = RtcSettings.energy_kWhtoday; + } + else if (RtcTime.day_of_year == Settings.energy_kWhdoy) { + Energy.kWhtoday_offset = Settings.energy_kWhtoday; + } + else { + Energy.kWhtoday_offset = 0; + } + Energy.kWhtoday = 0; + Energy.kWhtoday_delta = 0; + Energy.period = Energy.kWhtoday_offset; EnergyUpdateToday(); ticker_energy.attach_ms(200, Energy200ms); } @@ -582,146 +855,242 @@ const char HTTP_ENERGY_SNS1[] PROGMEM = "{s}" D_POWER_FACTOR "{m}%s{e}"; const char HTTP_ENERGY_SNS2[] PROGMEM = - "{s}" D_ENERGY_TODAY "{m}%s " D_UNIT_KILOWATTHOUR "{e}" + "{s}" D_ENERGY_TODAY "{m}%s " D_UNIT_KILOWATTHOUR "{e}" "{s}" D_ENERGY_YESTERDAY "{m}%s " D_UNIT_KILOWATTHOUR "{e}" "{s}" D_ENERGY_TOTAL "{m}%s " D_UNIT_KILOWATTHOUR "{e}"; // {s} = , {m} = , {e} = + +const char HTTP_ENERGY_SNS3[] PROGMEM = + "{s}" D_EXPORT_ACTIVE "{m}%s " D_UNIT_KILOWATTHOUR "{e}"; #endif // USE_WEBSERVER +char* EnergyFormatIndex(char* result, char* input, bool json, uint32_t index, bool single = false) +{ + char layout[16]; + GetTextIndexed(layout, sizeof(layout), (index -1) + (3 * json), kEnergyPhases); + switch (index) { + case 2: + snprintf_P(result, FLOATSZ *3, layout, input, input + FLOATSZ); // Dirty + break; + case 3: + snprintf_P(result, FLOATSZ *3, layout, input, input + FLOATSZ, input + FLOATSZ + FLOATSZ); // Even dirtier + break; + default: + snprintf_P(result, FLOATSZ *3, input); + } + return result; +} + +char* EnergyFormat(char* result, char* input, bool json, bool single = false) +{ + uint8_t index = (single) ? 1 : Energy.phase_count; // 1,2,3 + return EnergyFormatIndex(result, input, json, index, single); +} + void EnergyShow(bool json) { - char speriod[20]; -// char sfrequency[20]; + for (uint32_t i = 0; i < Energy.phase_count; i++) { + if (Energy.voltage_common) { + Energy.voltage[i] = Energy.voltage[0]; + } + } - bool show_energy_period = (0 == tele_period); + float power_factor_knx = Energy.power_factor[0]; - float power_factor = energy_power_factor; - - char apparent_power_chr[33]; - char reactive_power_chr[33]; - char power_factor_chr[33]; - char frequency_chr[33]; - if (!energy_type_dc) { - if (energy_current_available && energy_voltage_available) { - float apparent_power = energy_apparent_power; - if (isnan(apparent_power)) { - apparent_power = energy_voltage * energy_current; - } - if (apparent_power < energy_active_power) { // Should be impossible - energy_active_power = apparent_power; - } - - if (isnan(power_factor)) { - power_factor = (energy_active_power && apparent_power) ? energy_active_power / apparent_power : 0; - if (power_factor > 1) power_factor = 1; - } - - float reactive_power = energy_reactive_power; - if (isnan(reactive_power)) { - reactive_power = 0; - uint32_t difference = ((uint32_t)(apparent_power * 100) - (uint32_t)(energy_active_power * 100)) / 10; - if ((energy_current > 0.005) && ((difference > 15) || (difference > (uint32_t)(apparent_power * 100 / 1000)))) { - // calculating reactive power only if current is greater than 0.005A and - // difference between active and apparent power is greater than 1.5W or 1% - reactive_power = (float)(RoundSqrtInt((uint32_t)(apparent_power * apparent_power * 100) - (uint32_t)(energy_active_power * energy_active_power * 100))) / 10; + char apparent_power_chr[Energy.phase_count][FLOATSZ]; + char reactive_power_chr[Energy.phase_count][FLOATSZ]; + char power_factor_chr[Energy.phase_count][FLOATSZ]; + char frequency_chr[Energy.phase_count][FLOATSZ]; + if (!Energy.type_dc) { + if (Energy.current_available && Energy.voltage_available) { + for (uint32_t i = 0; i < Energy.phase_count; i++) { + float apparent_power = Energy.apparent_power[i]; + if (isnan(apparent_power)) { + apparent_power = Energy.voltage[i] * Energy.current[i]; } + if (apparent_power < Energy.active_power[i]) { // Should be impossible + Energy.active_power[i] = apparent_power; + } + + float power_factor = Energy.power_factor[i]; + if (isnan(power_factor)) { + power_factor = (Energy.active_power[i] && apparent_power) ? Energy.active_power[i] / apparent_power : 0; + if (power_factor > 1) { + power_factor = 1; + } + } + if (0 == i) { power_factor_knx = power_factor; } + + float reactive_power = Energy.reactive_power[i]; + if (isnan(reactive_power)) { + reactive_power = 0; + uint32_t difference = ((uint32_t)(apparent_power * 100) - (uint32_t)(Energy.active_power[i] * 100)) / 10; + if ((Energy.current[i] > 0.005) && ((difference > 15) || (difference > (uint32_t)(apparent_power * 100 / 1000)))) { + // calculating reactive power only if current is greater than 0.005A and + // difference between active and apparent power is greater than 1.5W or 1% + reactive_power = (float)(RoundSqrtInt((uint32_t)(apparent_power * apparent_power * 100) - (uint32_t)(Energy.active_power[i] * Energy.active_power[i] * 100))) / 10; + } + } + + dtostrfd(apparent_power, Settings.flag2.wattage_resolution, apparent_power_chr[i]); + dtostrfd(reactive_power, Settings.flag2.wattage_resolution, reactive_power_chr[i]); + dtostrfd(power_factor, 2, power_factor_chr[i]); } - - dtostrfd(apparent_power, Settings.flag2.wattage_resolution, apparent_power_chr); - dtostrfd(reactive_power, Settings.flag2.wattage_resolution, reactive_power_chr); - dtostrfd(power_factor, 2, power_factor_chr); } - if (!isnan(energy_frequency)) { - dtostrfd(energy_frequency, Settings.flag2.frequency_resolution, frequency_chr); + for (uint32_t i = 0; i < Energy.phase_count; i++) { + float frequency = Energy.frequency[i]; + if (isnan(Energy.frequency[i])) { + frequency = 0; + } + dtostrfd(frequency, Settings.flag2.frequency_resolution, frequency_chr[i]); } } - char voltage_chr[33]; - dtostrfd(energy_voltage, Settings.flag2.voltage_resolution, voltage_chr); - char current_chr[33]; - dtostrfd(energy_current, Settings.flag2.current_resolution, current_chr); - char active_power_chr[33]; - dtostrfd(energy_active_power, Settings.flag2.wattage_resolution, active_power_chr); - char energy_daily_chr[33]; - dtostrfd(energy_daily, Settings.flag2.energy_resolution, energy_daily_chr); - char energy_yesterday_chr[33]; + char voltage_chr[Energy.phase_count][FLOATSZ]; + char current_chr[Energy.phase_count][FLOATSZ]; + char active_power_chr[Energy.phase_count][FLOATSZ]; + for (uint32_t i = 0; i < Energy.phase_count; i++) { + dtostrfd(Energy.voltage[i], Settings.flag2.voltage_resolution, voltage_chr[i]); + dtostrfd(Energy.current[i], Settings.flag2.current_resolution, current_chr[i]); + dtostrfd(Energy.active_power[i], Settings.flag2.wattage_resolution, active_power_chr[i]); + } + + char energy_daily_chr[FLOATSZ]; + dtostrfd(Energy.daily, Settings.flag2.energy_resolution, energy_daily_chr); + char energy_yesterday_chr[FLOATSZ]; dtostrfd((float)Settings.energy_kWhyesterday / 100000, Settings.flag2.energy_resolution, energy_yesterday_chr); - char energy_total_chr[33]; - dtostrfd(energy_total, Settings.flag2.energy_resolution, energy_total_chr); - float energy = 0; - char energy_period_chr[33]; - if (show_energy_period) { - if (energy_period) energy = (float)(energy_kWhtoday - energy_period) / 100; - energy_period = energy_kWhtoday; - dtostrfd(energy, Settings.flag2.wattage_resolution, energy_period_chr); - snprintf_P(speriod, sizeof(speriod), PSTR(",\"" D_JSON_PERIOD "\":%s"), energy_period_chr); + char energy_total_chr[3][FLOATSZ]; + dtostrfd(Energy.total, Settings.flag2.energy_resolution, energy_total_chr[0]); + char export_active_chr[3][FLOATSZ]; + dtostrfd(Energy.export_active, Settings.flag2.energy_resolution, export_active_chr[0]); + uint8_t energy_total_fields = 1; + + if (Settings.tariff[0][0] != Settings.tariff[1][0]) { + dtostrfd((float)RtcSettings.energy_usage.usage1_kWhtotal / 100000, Settings.flag2.energy_resolution, energy_total_chr[1]); // Tariff1 + dtostrfd((float)RtcSettings.energy_usage.usage2_kWhtotal / 100000, Settings.flag2.energy_resolution, energy_total_chr[2]); // Tariff2 + dtostrfd((float)RtcSettings.energy_usage.return1_kWhtotal / 100000, Settings.flag2.energy_resolution, export_active_chr[1]); // Tariff1 + dtostrfd((float)RtcSettings.energy_usage.return2_kWhtotal / 100000, Settings.flag2.energy_resolution, export_active_chr[2]); // Tariff2 + energy_total_fields = 3; } + char value_chr[FLOATSZ *3]; // Used by EnergyFormatIndex + char value2_chr[FLOATSZ *3]; + char value3_chr[FLOATSZ *3]; + if (json) { - ResponseAppend_P(PSTR(",\"" D_RSLT_ENERGY "\":{\"" D_JSON_TOTAL_START_TIME "\":\"%s\",\"" D_JSON_TOTAL "\":%s,\"" D_JSON_YESTERDAY "\":%s,\"" D_JSON_TODAY "\":%s%s,\"" D_JSON_POWERUSAGE "\":%s"), - GetDateAndTime(DT_ENERGY).c_str(), energy_total_chr, energy_yesterday_chr, energy_daily_chr, (show_energy_period) ? speriod : "", active_power_chr); - if (!energy_type_dc) { - if (energy_current_available && energy_voltage_available) { + bool show_energy_period = (0 == tele_period); + + ResponseAppend_P(PSTR(",\"" D_RSLT_ENERGY "\":{\"" D_JSON_TOTAL_START_TIME "\":\"%s\",\"" D_JSON_TOTAL "\":%s,\"" D_JSON_YESTERDAY "\":%s,\"" D_JSON_TODAY "\":%s"), + GetDateAndTime(DT_ENERGY).c_str(), + EnergyFormatIndex(value_chr, energy_total_chr[0], json, energy_total_fields), + energy_yesterday_chr, + energy_daily_chr); + + if (!isnan(Energy.export_active)) { + ResponseAppend_P(PSTR(",\"" D_JSON_EXPORT_ACTIVE "\":%s"), + EnergyFormatIndex(value_chr, export_active_chr[0], json, energy_total_fields)); + } + + if (show_energy_period) { + float energy = 0; + if (Energy.period) { + energy = (float)(RtcSettings.energy_kWhtoday - Energy.period) / 100; + } + Energy.period = RtcSettings.energy_kWhtoday; + char energy_period_chr[FLOATSZ]; + dtostrfd(energy, Settings.flag2.wattage_resolution, energy_period_chr); + ResponseAppend_P(PSTR(",\"" D_JSON_PERIOD "\":%s"), energy_period_chr); + } + ResponseAppend_P(PSTR(",\"" D_JSON_POWERUSAGE "\":%s"), + EnergyFormat(value_chr, active_power_chr[0], json)); + if (!Energy.type_dc) { + if (Energy.current_available && Energy.voltage_available) { ResponseAppend_P(PSTR(",\"" D_JSON_APPARENT_POWERUSAGE "\":%s,\"" D_JSON_REACTIVE_POWERUSAGE "\":%s,\"" D_JSON_POWERFACTOR "\":%s"), - apparent_power_chr, reactive_power_chr, power_factor_chr); + EnergyFormat(value_chr, apparent_power_chr[0], json), + EnergyFormat(value2_chr, reactive_power_chr[0], json), + EnergyFormat(value3_chr, power_factor_chr[0], json)); } - if (!isnan(energy_frequency)) { - ResponseAppend_P(PSTR(",\"" D_JSON_FREQUENCY "\":%s"), frequency_chr); + if (!isnan(Energy.frequency[0])) { + ResponseAppend_P(PSTR(",\"" D_JSON_FREQUENCY "\":%s"), + EnergyFormat(value_chr, frequency_chr[0], json)); } } - if (energy_voltage_available) { - ResponseAppend_P(PSTR(",\"" D_JSON_VOLTAGE "\":%s"), voltage_chr); + if (Energy.voltage_available) { + ResponseAppend_P(PSTR(",\"" D_JSON_VOLTAGE "\":%s"), + EnergyFormat(value_chr, voltage_chr[0], json, Energy.voltage_common)); } - if (energy_current_available) { - ResponseAppend_P(PSTR(",\"" D_JSON_CURRENT "\":%s"), current_chr); + if (Energy.current_available) { + ResponseAppend_P(PSTR(",\"" D_JSON_CURRENT "\":%s"), + EnergyFormat(value_chr, current_chr[0], json)); } + XnrgCall(FUNC_JSON_APPEND); ResponseJsonEnd(); #ifdef USE_DOMOTICZ if (show_energy_period) { // Only send if telemetry - dtostrfd(energy_total * 1000, 1, energy_total_chr); - DomoticzSensorPowerEnergy((int)energy_active_power, energy_total_chr); // PowerUsage, EnergyToday - if (energy_voltage_available) { - DomoticzSensor(DZ_VOLTAGE, voltage_chr); // Voltage + dtostrfd(Energy.total * 1000, 1, energy_total_chr[0]); + DomoticzSensorPowerEnergy((int)Energy.active_power[0], energy_total_chr[0]); // PowerUsage, EnergyToday + + dtostrfd((float)RtcSettings.energy_usage.usage1_kWhtotal / 100, 1, energy_total_chr[1]); // Tariff1 + dtostrfd((float)RtcSettings.energy_usage.usage2_kWhtotal / 100, 1, energy_total_chr[2]); // Tariff2 + dtostrfd((float)RtcSettings.energy_usage.return1_kWhtotal / 100, 1, export_active_chr[1]); + dtostrfd((float)RtcSettings.energy_usage.return2_kWhtotal / 100, 1, export_active_chr[2]); + DomoticzSensorP1SmartMeter(energy_total_chr[1], energy_total_chr[2], export_active_chr[1], export_active_chr[2], (int)Energy.active_power[0]); + + if (Energy.voltage_available) { + DomoticzSensor(DZ_VOLTAGE, voltage_chr[0]); // Voltage } - if (energy_current_available) { - DomoticzSensor(DZ_CURRENT, current_chr); // Current + if (Energy.current_available) { + DomoticzSensor(DZ_CURRENT, current_chr[0]); // Current } } #endif // USE_DOMOTICZ #ifdef USE_KNX if (show_energy_period) { - if (energy_voltage_available) { - KnxSensor(KNX_ENERGY_VOLTAGE, energy_voltage); + if (Energy.voltage_available) { + KnxSensor(KNX_ENERGY_VOLTAGE, Energy.voltage[0]); } - if (energy_current_available) { - KnxSensor(KNX_ENERGY_CURRENT, energy_current); + if (Energy.current_available) { + KnxSensor(KNX_ENERGY_CURRENT, Energy.current[0]); } - KnxSensor(KNX_ENERGY_POWER, energy_active_power); - if (!energy_type_dc) { KnxSensor(KNX_ENERGY_POWERFACTOR, power_factor); } - KnxSensor(KNX_ENERGY_DAILY, energy_daily); - KnxSensor(KNX_ENERGY_TOTAL, energy_total); - KnxSensor(KNX_ENERGY_START, energy_start); + KnxSensor(KNX_ENERGY_POWER, Energy.active_power[0]); + if (!Energy.type_dc) { + KnxSensor(KNX_ENERGY_POWERFACTOR, power_factor_knx); + } + KnxSensor(KNX_ENERGY_DAILY, Energy.daily); + KnxSensor(KNX_ENERGY_TOTAL, Energy.total); + KnxSensor(KNX_ENERGY_START, Energy.start_energy); } #endif // USE_KNX #ifdef USE_WEBSERVER } else { - if (energy_voltage_available) { - WSContentSend_PD(PSTR("{s}" D_VOLTAGE "{m}%s " D_UNIT_VOLT "{e}"), voltage_chr); + if (Energy.voltage_available) { + WSContentSend_PD(PSTR("{s}" D_VOLTAGE "{m}%s " D_UNIT_VOLT "{e}"), + EnergyFormat(value_chr, voltage_chr[0], json, Energy.voltage_common)); } - if (energy_current_available) { - WSContentSend_PD(PSTR("{s}" D_CURRENT "{m}%s " D_UNIT_AMPERE "{e}"), current_chr); + if (Energy.current_available) { + WSContentSend_PD(PSTR("{s}" D_CURRENT "{m}%s " D_UNIT_AMPERE "{e}"), + EnergyFormat(value_chr, current_chr[0], json)); } - WSContentSend_PD(PSTR("{s}" D_POWERUSAGE "{m}%s " D_UNIT_WATT "{e}"), active_power_chr); - if (!energy_type_dc) { - if (energy_current_available && energy_voltage_available) { - WSContentSend_PD(HTTP_ENERGY_SNS1, apparent_power_chr, reactive_power_chr, power_factor_chr); + WSContentSend_PD(PSTR("{s}" D_POWERUSAGE "{m}%s " D_UNIT_WATT "{e}"), + EnergyFormat(value_chr, active_power_chr[0], json)); + if (!Energy.type_dc) { + if (Energy.current_available && Energy.voltage_available) { + WSContentSend_PD(HTTP_ENERGY_SNS1, EnergyFormat(value_chr, apparent_power_chr[0], json), + EnergyFormat(value2_chr, reactive_power_chr[0], json), + EnergyFormat(value3_chr, power_factor_chr[0], json)); } - if (!isnan(energy_frequency)) { - WSContentSend_PD(PSTR("{s}" D_FREQUENCY "{m}%s " D_UNIT_HERTZ "{e}"), frequency_chr); + if (!isnan(Energy.frequency[0])) { + WSContentSend_PD(PSTR("{s}" D_FREQUENCY "{m}%s " D_UNIT_HERTZ "{e}"), + EnergyFormat(value_chr, frequency_chr[0], json)); } } - WSContentSend_PD(HTTP_ENERGY_SNS2, energy_daily_chr, energy_yesterday_chr, energy_total_chr); + WSContentSend_PD(HTTP_ENERGY_SNS2, energy_daily_chr, energy_yesterday_chr, energy_total_chr[0]); + if (!isnan(Energy.export_active)) { + WSContentSend_PD(HTTP_ENERGY_SNS3, export_active_chr[0]); + } + + XnrgCall(FUNC_WEB_SENSOR); #endif // USE_WEBSERVER } } @@ -742,15 +1111,20 @@ bool Xdrv03(uint8_t function) case FUNC_LOOP: XnrgCall(FUNC_LOOP); break; - case FUNC_COMMAND: - result = EnergyCommand(); - break; - case FUNC_SET_POWER: - EnergySetPowerSteadyCounter(); + case FUNC_EVERY_250_MSECOND: + XnrgCall(FUNC_EVERY_250_MSECOND); break; case FUNC_SERIAL: result = XnrgCall(FUNC_SERIAL); break; +#ifdef USE_ENERGY_MARGIN_DETECTION + case FUNC_SET_POWER: + Energy.power_steady_counter = 2; + break; +#endif // USE_ENERGY_MARGIN_DETECTION + case FUNC_COMMAND: + result = DecodeCommand(kEnergyCommands, EnergyCommand); + break; } } return result; @@ -762,12 +1136,8 @@ bool Xsns03(uint8_t function) if (energy_flg) { switch (function) { - case FUNC_INIT: - EnergySnsInit(); - break; case FUNC_EVERY_SECOND: - EnergyMarginCheck(); - EnergyOverTempCheck(); + EnergyEverySecond(); break; case FUNC_JSON_APPEND: EnergyShow(true); @@ -780,6 +1150,9 @@ bool Xsns03(uint8_t function) case FUNC_SAVE_BEFORE_RESTART: EnergySaveState(); break; + case FUNC_INIT: + EnergySnsInit(); + break; } } return result; diff --git a/sonoff/xdrv_04_light.ino b/sonoff/xdrv_04_light.ino index 8e16a6d9b..558b843da 100644 --- a/sonoff/xdrv_04_light.ino +++ b/sonoff/xdrv_04_light.ino @@ -31,12 +31,9 @@ * 5 PWM5 RGBCW yes (H801, Arilux LC11) * 9 reserved no * 10 reserved yes - * 11 +WS2812 RGB(W) no (One WS2812 RGB or RGBW ledstrip) + * 11 +WS2812 RGB no (One WS2812 RGB or RGBW ledstrip) * 12 AiLight RGBW no * 13 Sonoff B1 RGBCW yes - * 19 SM16716 RGB no - * 20 SM16716+W RGBW no - * 21 SM16716+CW RGBCW yes * * light_scheme WS2812 3+ Colors 1+2 Colors Effect * ------------ ------ --------- ---------- ----------------- @@ -56,7 +53,6 @@ * \*********************************************************************************************/ - /*********************************************************************************************\ * * Light management has been refactored to provide a cleaner class-based interface. @@ -91,7 +87,7 @@ * survive a reboot and can be stored in flash - in saveSettings() * .b Actual channel values are computed from RGB or CT combined with brightness. * Range is still 0..255 (8 bits) - in getActualRGBCW() - * .c The 5 internal channels RGBWC are mapped to the actual channels supproted + * .c The 5 internal channels RGBWC are mapped to the actual channels supported * by the light_type: in calcLevels() * 1 channel - 0:Brightness * 2 channels - 0:Coldwhite 1:Warmwhite @@ -101,39 +97,46 @@ * * 3. In LightAnimate(), final PWM values are computed at next tick. * .a If color did not change since last tick - ignore. - * .b Apply color balance correction from rgbwwTable[] - * .c Extend resolution from 8 bits to 10 bits, which makes a significant + * .b Extend resolution from 8 bits to 10 bits, which makes a significant * difference when applying gamma correction at low brightness. - * .d Apply Gamma Correction if LedTable==1 (by default). + * .c Apply Gamma Correction if LedTable==1 (by default). * Gamma Correction uses an adaptative resolution table from 11 to 8 bits. - * .e For Warm/Cold-white channels, Gamma correction is calculated in combined mode. + * .d For Warm/Cold-white channels, Gamma correction is calculated in combined mode. * Ie. total white brightness (C+W) is used for Gamma correction and gives * the overall light power required. Then this light power is split among * Wamr/Cold channels. - * .f Gamma correction is still applied to 8 bits channels for compatibility + * .e Gamma correction is still applied to 8 bits channels for compatibility * with other non-PMW modules. - * .g Avoid PMW values between 1008 and 1022, issue #1146 - * .h Scale ranges from 10 bits to 0..PWMRange (by default 1023) so no change + * .f Apply color balance correction from rgbwwTable[]. + * Note: correction is done after Gamma correction, it is meant + * to adjust leds with different power + * .g If rgbwwTable[4] is zero, blend RGB with White and adjust the level of + * White channel according to rgbwwTable[3] + * .h Avoid PMW values between 1008 and 1022, issue #1146 + * .i Scale ranges from 10 bits to 0..PWMRange (by default 1023) so no change * by default. - * .i Apply port remapping from Option37 - * .j Invert PWM value if port is of type PMWxi instead of PMWx - * .k Apply PWM value with analogWrite() - if pin is configured + * .j Apply port remapping from Option37 + * .k Invert PWM value if port is of type PMWxi instead of PMWx + * .l Apply PWM value with analogWrite() - if pin is configured * \*********************************************************************************************/ #define XDRV_04 4 -//#define DEBUG_LIGHT +// #define DEBUG_LIGHT -const uint8_t WS2812_SCHEMES = 7; // Number of additional WS2812 schemes supported by xdrv_ws2812.ino +enum LightSchemes { LS_POWER, LS_WAKEUP, LS_CYCLEUP, LS_CYCLEDN, LS_RANDOM, LS_MAX }; -enum LightCommands { - CMND_COLOR, CMND_COLORTEMPERATURE, CMND_DIMMER, CMND_LED, CMND_LEDTABLE, CMND_FADE, - CMND_PIXELS, CMND_RGBWWTABLE, CMND_ROTATION, CMND_SCHEME, CMND_SPEED, CMND_WAKEUP, CMND_WAKEUPDURATION, - CMND_WHITE, CMND_WIDTH, CMND_CHANNEL, CMND_HSBCOLOR, CMND_UNDOCA }; -const char kLightCommands[] PROGMEM = - D_CMND_COLOR "|" D_CMND_COLORTEMPERATURE "|" D_CMND_DIMMER "|" D_CMND_LED "|" D_CMND_LEDTABLE "|" D_CMND_FADE "|" - D_CMND_PIXELS "|" D_CMND_RGBWWTABLE "|" D_CMND_ROTATION "|" D_CMND_SCHEME "|" D_CMND_SPEED "|" D_CMND_WAKEUP "|" D_CMND_WAKEUPDURATION "|" - D_CMND_WHITE "|" D_CMND_WIDTH "|" D_CMND_CHANNEL "|" D_CMND_HSBCOLOR "|UNDOCA" ; +const uint8_t LIGHT_COLOR_SIZE = 25; // Char array scolor size + +const char kLightCommands[] PROGMEM = "|" // No prefix + D_CMND_COLOR "|" D_CMND_COLORTEMPERATURE "|" D_CMND_DIMMER "|" D_CMND_DIMMER_RANGE "|" D_CMND_LEDTABLE "|" D_CMND_FADE "|" + D_CMND_RGBWWTABLE "|" D_CMND_SCHEME "|" D_CMND_SPEED "|" D_CMND_WAKEUP "|" D_CMND_WAKEUPDURATION "|" + D_CMND_WHITE "|" D_CMND_CHANNEL "|" D_CMND_HSBCOLOR "|UNDOCA" ; + +void (* const LightCommand[])(void) PROGMEM = { + &CmndColor, &CmndColorTemperature, &CmndDimmer, &CmndDimmerRange, &CmndLedTable, &CmndFade, + &CmndRgbwwTable, &CmndScheme, &CmndSpeed, &CmndWakeup, &CmndWakeupDuration, + &CmndWhite, &CmndChannel, &CmndHsbColor, &CmndUndocA }; // Light color mode, either RGB alone, or white-CT alone, or both only available if ct_rgb_linked is false enum LightColorModes { @@ -162,7 +165,11 @@ const LCwColor kFixedColdWarm[MAX_FIXED_COLD_WARM] PROGMEM = { 0,0, 255,0, 0,255 // from 11 bits (lower values) to 8 bits (upper values). // We're using the fact that lower values are small and can fit within 8 bits // To save flash space, the array is only 8 bits uint +#ifdef XFUNC_PTR_IN_ROM +const uint8_t _ledTable[] PROGMEM = { +#else const uint8_t _ledTable[] = { +#endif // 11 bits resolution 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, // 11 bits, 0..2047 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, // 11 bits, 0..2047 @@ -222,62 +229,44 @@ const uint8_t _ledTable[] = { //715,727,735,743,751,763,771,779,791,799,807,819,827,839,847,859, //867,879,887,899,907,919,931,939,951,963,971,983,995,1003,1015,1023 +struct LIGHT { + unsigned long strip_timer_counter = 0; // Bars and Gradient + power_t power = 0; // Power for each channel if SetOption68, or boolean if single light -uint8_t light_entry_color[LST_MAX]; -uint8_t light_current_color[LST_MAX]; -uint8_t light_new_color[LST_MAX]; -uint8_t light_last_color[LST_MAX]; -uint8_t light_color_remap[LST_MAX]; + uint16_t wakeup_counter = 0; -uint8_t light_wheel = 0; -uint8_t light_subtype = 0; // LST_ subtype -uint8_t light_device = 0; -uint8_t light_power = 0; -uint8_t light_old_power = 1; -uint8_t light_update = 1; -uint8_t light_wakeup_active = 0; -uint8_t light_wakeup_dimmer = 0; -uint16_t light_wakeup_counter = 0; + uint8_t entry_color[LST_MAX]; + uint8_t current_color[LST_MAX]; + uint8_t new_color[LST_MAX]; + uint8_t last_color[LST_MAX]; + uint8_t color_remap[LST_MAX]; -uint8_t light_fixed_color_index = 1; + uint8_t wheel = 0; + uint8_t subtype = 0; // LST_ subtype + uint8_t device = 0; + uint8_t old_power = 1; + uint8_t wakeup_active = 0; + uint8_t wakeup_dimmer = 0; + uint8_t fixed_color_index = 1; + uint8_t pwm_offset = 0; // Offset in color buffer + uint8_t max_scheme = LS_MAX -1; -unsigned long strip_timer_counter = 0; // Bars and Gradient + bool update = true; + bool pwm_multi_channels = false; // SetOption68, treat each PWM channel as an independant dimmer +} Light; -// -// changeUIntScale -// Change a value for range a..b to c..d, using only unsigned int math -// -// PRE-CONDITIONS (if not satisfied, you may 'halt and catch fire') -// from_min < from_max (not checked) -// to_min < to_max (not checked) -// from_min <= num <= from-max (chacked) -// POST-CONDITIONS -// to_min <= result <= to_max -// -uint16_t changeUIntScale(uint16_t inum, uint16_t ifrom_min, uint16_t ifrom_max, - uint16_t ito_min, uint16_t ito_max) { - // guard-rails - if ((ito_min >= ito_max) || (ifrom_min >= ifrom_max)) { - return ito_min; // invalid input, return arbitrary value - } - // convert to uint31, it's more verbose but code is more compact - uint32_t num = inum; - uint32_t from_min = ifrom_min; - uint32_t from_max = ifrom_max; - uint32_t to_min = ito_min; - uint32_t to_max = ito_max; +power_t LightPower(void) +{ + return Light.power; // Make external +} - // check source range - num = (num > from_max ? from_max : (num < from_min ? from_min : num)); - uint32_t numerator = (num - from_min) * (to_max - to_min); - uint32_t result; - if (numerator >= 0x80000000L) { - // don't do rounding as it would create an overflow - result = numerator / (from_max - from_min) + to_min; - } else { - result = (((numerator * 2) / (from_max - from_min)) + 1) / 2 + to_min; - } - return (uint32_t) (result > to_max ? to_max : (result < to_min ? to_min : result)); +uint8_t LightDevice(void) +{ + return Light.device; // Make external +} + +static uint32_t min3(uint32_t a, uint32_t b, uint32_t c) { + return (a < b && a < c) ? a : (b < c) ? b : c; } // @@ -327,7 +316,7 @@ class LightStateClass { uint8_t _g = 255; // 0..255 uint8_t _b = 255; // 0..255 - uint8_t _subtype = 0; // local copy of light_subtype, if we need multiple lights + uint8_t _subtype = 0; // local copy of Light.subtype, if we need multiple lights uint16_t _ct = 153; // 153..500, default to 153 (cold white) uint8_t _wc = 255; // white cold channel uint8_t _ww = 0; // white warm channel @@ -407,7 +396,7 @@ class LightStateClass { if (b) { *b = _b; } } - // get full brightness values for wamr and cold channels. + // get full brightness values for warm and cold channels. // either w=c=0 (off) or w+c >= 255 void getCW(uint8_t *rc, uint8_t *rw) { if (rc) { *rc = _wc; } @@ -431,6 +420,14 @@ class LightStateClass { getActualRGBCW(&channels[0], &channels[1], &channels[2], &channels[3], &channels[4]); } + void getChannelsRaw(uint8_t *channels) { + channels[0] = _r; + channels[1] = _g; + channels[2] = _b; + channels[3] = _wc; + channels[4] = _ww; + } + void getHSB(uint16_t *hue, uint8_t *sat, uint8_t *bri) { if (hue) { *hue = _hue; } if (sat) { *sat = _sat; } @@ -613,6 +610,16 @@ class LightStateClass { #endif } + // set all 5 channels at once, don't modify the values in ANY way + // Channels are: R G B CW WW + void setChannelsRaw(uint8_t *channels) { + _r = channels[0]; + _g = channels[1]; + _b = channels[2]; + _wc = channels[3]; + _ww = channels[4]; +} + // set all 5 channels at once. // Channels are: R G B CW WW // Brightness is automatically recalculated to adjust channels to the desired values @@ -793,6 +800,7 @@ private: // are RGB and CT linked, i.e. if we set CT then RGB channels are off bool _ct_rgb_linked = true; + bool _pwm_multi_channels = false; // treat each channel as independant dimmer public: LightControllerClass(LightStateClass& state) { @@ -805,7 +813,11 @@ public: inline bool setCTRGBLinked(bool ct_rgb_linked) { bool prev = _ct_rgb_linked; - _ct_rgb_linked = ct_rgb_linked; + if (_pwm_multi_channels) { + _ct_rgb_linked = false; // force to false if _pwm_multi_channels is set + } else { + _ct_rgb_linked = ct_rgb_linked; + } return prev; } @@ -813,6 +825,17 @@ public: return _ct_rgb_linked; } + inline bool setPWMMultiChannel(bool pwm_multi_channels) { + bool prev = _pwm_multi_channels; + _pwm_multi_channels = pwm_multi_channels; + if (pwm_multi_channels) setCTRGBLinked(false); // if pwm multi channel, then unlink RGB and CT + return prev; + } + + inline bool isPWMMultiChannel(void) { + return _pwm_multi_channels; + } + #ifdef DEBUG_LIGHT void debugLogs() { uint8_t r,g,b,c,w; @@ -820,8 +843,8 @@ public: AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightControllerClass::debugLogs rgb (%d %d %d) cw (%d %d)", r, g, b, c, w); AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightControllerClass::debugLogs lightCurrent (%d %d %d %d %d)", - light_current_color[0], light_current_color[1], light_current_color[2], - light_current_color[3], light_current_color[4]); + Light.current_color[0], Light.current_color[1], Light.current_color[2], + Light.current_color[3], Light.current_color[4]); } #endif @@ -831,17 +854,24 @@ public: Settings.light_color[0], Settings.light_color[1], Settings.light_color[2], Settings.light_color[3], Settings.light_color[4], Settings.light_dimmer); AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightControllerClass::loadSettings light_type/sub (%d %d)", - light_type, light_subtype); + light_type, Light.subtype); #endif - // first try setting CW, if zero, it select RGB mode - _state->setCW(Settings.light_color[3], Settings.light_color[4], true); - _state->setRGB(Settings.light_color[0], Settings.light_color[1], Settings.light_color[2]); - // We apply dimmer in priority to RGB - uint8_t bri = _state->DimmerToBri(Settings.light_dimmer); - if (Settings.light_color[0] + Settings.light_color[1] + Settings.light_color[2] > 0) { - _state->setBriRGB(bri); + if (_pwm_multi_channels) { + _state->setChannelsRaw(Settings.light_color); } else { - _state->setBriCT(bri); + // first try setting CW, if zero, it select RGB mode + _state->setCW(Settings.light_color[3], Settings.light_color[4], true); + _state->setRGB(Settings.light_color[0], Settings.light_color[1], Settings.light_color[2]); + + // only if non-multi channel + // We apply dimmer in priority to RGB + uint8_t bri = _state->DimmerToBri(Settings.light_dimmer); + if (Settings.light_color[0] + Settings.light_color[1] + Settings.light_color[2] > 0) { + _state->setColorMode(LCM_RGB); + _state->setBriRGB(bri); + } else { + _state->setBriCT(bri); + } } } @@ -852,7 +882,7 @@ public: * ct = 500 = 6500K = Cold = CCWW = FF00 */ // don't set CT if not supported - if ((LST_COLDWARM != light_subtype) && (LST_RGBW > light_subtype)) { + if ((LST_COLDWARM != Light.subtype) && (LST_RGBW > Light.subtype)) { return; } _state->setCT(new_ct); @@ -884,36 +914,42 @@ public: // calculate the levels for each channel void calcLevels() { uint8_t r,g,b,c,w,briRGB,briCT; + + if (_pwm_multi_channels) { // if PWM multi channel, no more transformation required + _state->getChannelsRaw(Light.current_color); + return; + } + _state->getActualRGBCW(&r,&g,&b,&c,&w); briRGB = _state->getBriRGB(); briCT = _state->getBriCT(); - light_current_color[0] = light_current_color[1] = light_current_color[2] = 0; - light_current_color[3] = light_current_color[4] = 0; - switch (light_subtype) { + Light.current_color[0] = Light.current_color[1] = Light.current_color[2] = 0; + Light.current_color[3] = Light.current_color[4] = 0; + switch (Light.subtype) { case LST_NONE: - light_current_color[0] = 255; + Light.current_color[0] = 255; break; case LST_SINGLE: - light_current_color[0] = briRGB; + Light.current_color[0] = briRGB; break; case LST_COLDWARM: - light_current_color[0] = c; - light_current_color[1] = w; + Light.current_color[0] = c; + Light.current_color[1] = w; break; case LST_RGBW: case LST_RGBWC: - if (LST_RGBWC == light_subtype) { - light_current_color[3] = c; - light_current_color[4] = w; + if (LST_RGBWC == Light.subtype) { + Light.current_color[3] = c; + Light.current_color[4] = w; } else { - light_current_color[3] = briCT; + Light.current_color[3] = briCT; } // continue case LST_RGB: - light_current_color[0] = r; - light_current_color[1] = g; - light_current_color[2] = b; + Light.current_color[0] = r; + Light.current_color[1] = g; + Light.current_color[2] = b; break; } } @@ -928,20 +964,26 @@ public: // save the current light state to Settings. void saveSettings() { - uint8_t cm = _state->getColorMode(); + if (Light.pwm_multi_channels) { + // simply save each channel + _state->getChannelsRaw(Settings.light_color); + Settings.light_dimmer = 100; // arbitrary value, unused in this mode + } else { + uint8_t cm = _state->getColorMode(); - memset(&Settings.light_color[0], 0, sizeof(Settings.light_color)); - if (LCM_RGB & cm) { // can be either LCM_RGB or LCM_BOTH - _state->getRGB(&Settings.light_color[0], &Settings.light_color[1], &Settings.light_color[2]); - Settings.light_dimmer = _state->BriToDimmer(_state->getBriRGB()); - // anyways we always store RGB with BrightnessRGB - if (LCM_BOTH == cm) { - // then store at actual brightness CW/WW if dual mode - _state->getActualRGBCW(nullptr, nullptr, nullptr, &Settings.light_color[3], &Settings.light_color[4]); + memset(&Settings.light_color[0], 0, sizeof(Settings.light_color)); + if (LCM_RGB & cm) { // can be either LCM_RGB or LCM_BOTH + _state->getRGB(&Settings.light_color[0], &Settings.light_color[1], &Settings.light_color[2]); + Settings.light_dimmer = _state->BriToDimmer(_state->getBriRGB()); + // anyways we always store RGB with BrightnessRGB + if (LCM_BOTH == cm) { + // then store at actual brightness CW/WW if dual mode + _state->getActualRGBCW(nullptr, nullptr, nullptr, &Settings.light_color[3], &Settings.light_color[4]); + } + } else if (LCM_CT == cm) { // cm can only be LCM_CT + _state->getCW(&Settings.light_color[3], &Settings.light_color[4]); + Settings.light_dimmer = _state->BriToDimmer(_state->getBriCT()); } - } else if (LCM_CT == cm) { // cm can only be LCM_CT - _state->getCW(&Settings.light_color[3], &Settings.light_color[4]); - Settings.light_dimmer = _state->BriToDimmer(_state->getBriCT()); } #ifdef DEBUG_LIGHT AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightControllerClass::saveSettings Settings.light_color (%d %d %d %d %d - %d)", @@ -954,13 +996,16 @@ public: // Channels are: R G B CW WW // Brightness is automatically recalculated to adjust channels to the desired values void changeChannels(uint8_t *channels) { - if (LST_COLDWARM == light_subtype) { + if (Light.pwm_multi_channels) { + _state->setChannelsRaw(channels); + } else if (LST_COLDWARM == Light.subtype) { // remap channels 0-1 to 3-4 if cold/warm uint8_t remapped_channels[5] = {0,0,0,channels[0],channels[1]}; _state->setChannels(remapped_channels); } else { _state->setChannels(channels); } + saveSettings(); calcLevels(); } @@ -981,7 +1026,11 @@ uint16_t ledGamma(uint8_t v, uint16_t bits_out = 8) { // bits_resolution: the resolution of _ledTable[v], between 8 and 11 uint32_t bits_resolution = 11 - (v / 64); // 8..11 int32_t bits_correction = bits_out - bits_resolution; // -3..3 +#ifdef XFUNC_PTR_IN_ROM + uint32_t uncorrected_value = pgm_read_byte(_ledTable + v); // 0..255 +#else uint32_t uncorrected_value = _ledTable[v]; // 0..255 +#endif if (0 == bits_correction) { // we already match the required resolution, no change result = uncorrected_value; @@ -999,361 +1048,80 @@ uint16_t ledGamma(uint8_t v, uint16_t bits_out = 8) { return result; } -#ifdef USE_ARILUX_RF -/*********************************************************************************************\ - * Arilux LC11 Rf support stripped from RCSwitch library -\*********************************************************************************************/ - -const uint32_t ARILUX_RF_TIME_AVOID_DUPLICATE = 1000; // Milliseconds - -const uint8_t ARILUX_RF_MAX_CHANGES = 51; // Pulses (sync + 2 x 24 bits) -const uint32_t ARILUX_RF_SEPARATION_LIMIT = 4300; // Microseconds -const uint32_t ARILUX_RF_RECEIVE_TOLERANCE = 60; // Percentage - -unsigned int arilux_rf_timings[ARILUX_RF_MAX_CHANGES]; - -unsigned long arilux_rf_received_value = 0; -unsigned long arilux_rf_last_received_value = 0; -unsigned long arilux_rf_last_time = 0; -unsigned long arilux_rf_lasttime = 0; - -unsigned int arilux_rf_change_count = 0; -unsigned int arilux_rf_repeat_count = 0; - -uint8_t arilux_rf_toggle = 0; - - -#ifndef ARDUINO_ESP8266_RELEASE_2_3_0 // Fix core 2.5.x ISR not in IRAM Exception -#ifndef USE_WS2812_DMA // Collides with Neopixelbus but solves RF misses -void AriluxRfInterrupt(void) ICACHE_RAM_ATTR; // As iram is tight and it works this way too -#endif // USE_WS2812_DMA -#endif // ARDUINO_ESP8266_RELEASE_2_3_0 - -void AriluxRfInterrupt(void) -{ - unsigned long time = micros(); - unsigned int duration = time - arilux_rf_lasttime; - - if (duration > ARILUX_RF_SEPARATION_LIMIT) { - if (abs(duration - arilux_rf_timings[0]) < 200) { - arilux_rf_repeat_count++; - if (arilux_rf_repeat_count == 2) { - unsigned long code = 0; - const unsigned int delay = arilux_rf_timings[0] / 31; - const unsigned int delayTolerance = delay * ARILUX_RF_RECEIVE_TOLERANCE / 100; - for (unsigned int i = 1; i < arilux_rf_change_count -1; i += 2) { - code <<= 1; - if (abs(arilux_rf_timings[i] - (delay *3)) < delayTolerance && abs(arilux_rf_timings[i +1] - delay) < delayTolerance) { - code |= 1; - } - } - if (arilux_rf_change_count > 49) { // Need 1 sync bit and 24 data bits - arilux_rf_received_value = code; - } - arilux_rf_repeat_count = 0; - } - } - arilux_rf_change_count = 0; - } - if (arilux_rf_change_count >= ARILUX_RF_MAX_CHANGES) { - arilux_rf_change_count = 0; - arilux_rf_repeat_count = 0; - } - arilux_rf_timings[arilux_rf_change_count++] = duration; - arilux_rf_lasttime = time; -} - -void AriluxRfHandler(void) -{ - unsigned long now = millis(); - if (arilux_rf_received_value && !((arilux_rf_received_value == arilux_rf_last_received_value) && (now - arilux_rf_last_time < ARILUX_RF_TIME_AVOID_DUPLICATE))) { - arilux_rf_last_received_value = arilux_rf_received_value; - arilux_rf_last_time = now; - - uint16_t hostcode = arilux_rf_received_value >> 8 & 0xFFFF; - if (Settings.rf_code[1][6] == Settings.rf_code[1][7]) { - Settings.rf_code[1][6] = hostcode >> 8 & 0xFF; - Settings.rf_code[1][7] = hostcode & 0xFF; - } - uint16_t stored_hostcode = Settings.rf_code[1][6] << 8 | Settings.rf_code[1][7]; - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_RFR D_HOST D_CODE " 0x%04X, " D_RECEIVED " 0x%06X"), stored_hostcode, arilux_rf_received_value); - - if (hostcode == stored_hostcode) { - char command[33]; - char value = '-'; - command[0] = '\0'; - uint8_t keycode = arilux_rf_received_value & 0xFF; - switch (keycode) { - case 1: // Power On - case 3: // Power Off - snprintf_P(command, sizeof(command), PSTR(D_CMND_POWER " %d"), (1 == keycode) ? 1 : 0); - break; - case 2: // Toggle - arilux_rf_toggle++; - arilux_rf_toggle &= 0x3; - snprintf_P(command, sizeof(command), PSTR(D_CMND_COLOR " %d"), 200 + arilux_rf_toggle); - break; - case 4: // Speed + - value = '+'; - case 7: // Speed - - snprintf_P(command, sizeof(command), PSTR(D_CMND_SPEED " %c"), value); - break; - case 5: // Scheme + - value = '+'; - case 8: // Scheme - - snprintf_P(command, sizeof(command), PSTR(D_CMND_SCHEME " %c"), value); - break; - case 6: // Dimmer + - value = '+'; - case 9: // Dimmer - - snprintf_P(command, sizeof(command), PSTR(D_CMND_DIMMER " %c"), value); - break; - default: { - if ((keycode >= 10) && (keycode <= 21)) { - snprintf_P(command, sizeof(command), PSTR(D_CMND_COLOR " %d"), keycode -9); - } - } - } - if (strlen(command)) { - ExecuteCommand(command, SRC_LIGHT); - } - } - } - arilux_rf_received_value = 0; -} - -void AriluxRfInit(void) -{ - if ((pin[GPIO_ARIRFRCV] < 99) && (pin[GPIO_ARIRFSEL] < 99)) { - if (Settings.last_module != Settings.module) { - Settings.rf_code[1][6] = 0; - Settings.rf_code[1][7] = 0; - Settings.last_module = Settings.module; - } - arilux_rf_received_value = 0; - - digitalWrite(pin[GPIO_ARIRFSEL], 0); // Turn on RF - attachInterrupt(pin[GPIO_ARIRFRCV], AriluxRfInterrupt, CHANGE); - } -} - -void AriluxRfDisable(void) -{ - if ((pin[GPIO_ARIRFRCV] < 99) && (pin[GPIO_ARIRFSEL] < 99)) { - detachInterrupt(pin[GPIO_ARIRFRCV]); - digitalWrite(pin[GPIO_ARIRFSEL], 1); // Turn off RF - } -} -#endif // USE_ARILUX_RF - -/*********************************************************************************************\ - * Sonoff B1 and AiLight inspired by OpenLight https://github.com/icamgo/noduino-sdk -\*********************************************************************************************/ - -extern "C" { - void os_delay_us(unsigned int); -} - -uint8_t light_pdi_pin; -uint8_t light_pdcki_pin; - -void LightDiPulse(uint8_t times) -{ - for (uint32_t i = 0; i < times; i++) { - digitalWrite(light_pdi_pin, HIGH); - digitalWrite(light_pdi_pin, LOW); - } -} - -void LightDckiPulse(uint8_t times) -{ - for (uint32_t i = 0; i < times; i++) { - digitalWrite(light_pdcki_pin, HIGH); - digitalWrite(light_pdcki_pin, LOW); - } -} - -void LightMy92x1Write(uint8_t data) -{ - for (uint32_t i = 0; i < 4; i++) { // Send 8bit Data - digitalWrite(light_pdcki_pin, LOW); - digitalWrite(light_pdi_pin, (data & 0x80)); - digitalWrite(light_pdcki_pin, HIGH); - data = data << 1; - digitalWrite(light_pdi_pin, (data & 0x80)); - digitalWrite(light_pdcki_pin, LOW); - digitalWrite(light_pdi_pin, LOW); - data = data << 1; - } -} - -void LightMy92x1Init(void) -{ - uint8_t chips = 1; // 1 (AiLight) - if (LT_RGBWC == light_type) { - chips = 2; // 2 (Sonoff B1) - } - - LightDckiPulse(chips * 32); // Clear all duty register - os_delay_us(12); // TStop > 12us. - // Send 12 DI pulse, after 6 pulse's falling edge store duty data, and 12 - // pulse's rising edge convert to command mode. - LightDiPulse(12); - os_delay_us(12); // Delay >12us, begin send CMD data - for (uint32_t n = 0; n < chips; n++) { // Send CMD data - LightMy92x1Write(0x18); // ONE_SHOT_DISABLE, REACTION_FAST, BIT_WIDTH_8, FREQUENCY_DIVIDE_1, SCATTER_APDM - } - os_delay_us(12); // TStart > 12us. Delay 12 us. - // Send 16 DI pulse, at 14 pulse's falling edge store CMD data, and - // at 16 pulse's falling edge convert to duty mode. - LightDiPulse(16); - os_delay_us(12); // TStop > 12us. -} - -void LightMy92x1Duty(uint8_t duty_r, uint8_t duty_g, uint8_t duty_b, uint8_t duty_w, uint8_t duty_c) -{ - uint8_t channels[2] = { 4, 6 }; - - uint8_t didx = 0; // 0 (AiLight) - if (LT_RGBWC == light_type) { - didx = 1; // 1 (Sonoff B1) - } - - uint8_t duty[2][6] = {{ duty_r, duty_g, duty_b, duty_w, 0, 0 }, // Definition for RGBW channels - { duty_w, duty_c, 0, duty_g, duty_r, duty_b }}; // Definition for RGBWC channels - - os_delay_us(12); // TStop > 12us. - for (uint32_t channel = 0; channel < channels[didx]; channel++) { - LightMy92x1Write(duty[didx][channel]); // Send 8bit Data - } - os_delay_us(12); // TStart > 12us. Ready for send DI pulse. - LightDiPulse(8); // Send 8 DI pulse. After 8 pulse falling edge, store old data. - os_delay_us(12); // TStop > 12us. -} - -#ifdef USE_SM16716 -/*********************************************************************************************\ - * SM16716 - Controlling RGB over a synchronous serial line - * Copyright (C) 2019 Gabor Simon - * - * Source: https://community.home-assistant.io/t/cheap-uk-wifi-bulbs-with-tasmota-teardown-help-tywe3s/40508/27 - * -\*********************************************************************************************/ - -// Enable this for debug logging -//#define D_LOG_SM16716 "SM16716: " - -uint8_t sm16716_pin_clk = 100; -uint8_t sm16716_pin_dat = 100; -uint8_t sm16716_pin_sel = 100; -uint8_t sm16716_enabled = 0; - -void SM16716_SendBit(uint8_t v) -{ - /* NOTE: - * According to the spec sheet, max freq is 30 MHz, that is 16.6 ns per high/low half of the - * clk square wave. That is less than the overhead of 'digitalWrite' at this clock rate, - * so no additional delays are needed yet. */ - - digitalWrite(sm16716_pin_dat, (v != 0) ? HIGH : LOW); - //delayMicroseconds(1); - digitalWrite(sm16716_pin_clk, HIGH); - //delayMicroseconds(1); - digitalWrite(sm16716_pin_clk, LOW); -} - -void SM16716_SendByte(uint8_t v) -{ - uint8_t mask; - - for (mask = 0x80; mask; mask >>= 1) { - SM16716_SendBit(v & mask); - } -} - -void SM16716_Update(uint8_t duty_r, uint8_t duty_g, uint8_t duty_b) -{ - if (sm16716_pin_sel < 99) { - uint8_t sm16716_should_enable = (duty_r | duty_g | duty_b); - if (!sm16716_enabled && sm16716_should_enable) { -#ifdef D_LOG_SM16716 - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_SM16716 "turning color on")); -#endif // D_LOG_SM16716 - sm16716_enabled = 1; - digitalWrite(sm16716_pin_sel, HIGH); - // in testing I found it takes a minimum of ~380us to wake up the chip - // tested on a Merkury RGBW with an SM726EB - delayMicroseconds(1000); - SM16716_Init(); - } - else if (sm16716_enabled && !sm16716_should_enable) { -#ifdef D_LOG_SM16716 - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_SM16716 "turning color off")); -#endif // D_LOG_SM16716 - sm16716_enabled = 0; - digitalWrite(sm16716_pin_sel, LOW); - } - } -#ifdef D_LOG_SM16716 - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_SM16716 "Update; rgb=%02x%02x%02x"), duty_r, duty_g, duty_b); -#endif // D_LOG_SM16716 - - // send start bit - SM16716_SendBit(1); - SM16716_SendByte(duty_r); - SM16716_SendByte(duty_g); - SM16716_SendByte(duty_b); - - // send a 'do it' pulse - // (if multiple chips are chained, each one processes the 1st '1rgb' 25-bit block and - // passes on the rest, right until the one starting with 0) - //SM16716_Init(); - SM16716_SendBit(0); - SM16716_SendByte(0); - SM16716_SendByte(0); - SM16716_SendByte(0); -} - -bool SM16716_ModuleSelected(void) -{ - sm16716_pin_clk = pin[GPIO_SM16716_CLK]; - sm16716_pin_dat = pin[GPIO_SM16716_DAT]; - sm16716_pin_sel = pin[GPIO_SM16716_SEL]; -#ifdef D_LOG_SM16716 - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_SM16716 "ModuleSelected; clk_pin=%d, dat_pin=%d)"), sm16716_pin_clk, sm16716_pin_dat); -#endif // D_LOG_SM16716 - return (sm16716_pin_clk < 99) && (sm16716_pin_dat < 99); -} - -void SM16716_Init(void) -{ - for (uint32_t t_init = 0; t_init < 50; ++t_init) { - SM16716_SendBit(0); - } -} - -#endif // ifdef USE_SM16716 - /********************************************************************************************/ +void LightPwmOffset(uint32_t offset) +{ + Light.pwm_offset = offset; +} + +bool LightModuleInit(void) +{ + light_type = LT_BASIC; // Use basic PWM control if SetOption15 = 0 + + if (Settings.flag.pwm_control) { + for (uint32_t i = 0; i < MAX_PWMS; i++) { + if (pin[GPIO_PWM1 +i] < 99) { light_type++; } // Use Dimmer/Color control for all PWM as SetOption15 = 1 + } + } + + light_flg = 0; + if (XlgtCall(FUNC_MODULE_INIT)) { + // serviced + } + else if (SONOFF_BN == my_module_type) { // PWM Single color led (White) + light_type = LT_PWM1; + } + else if (SONOFF_LED == my_module_type) { // PWM Dual color led (White warm and cold) + if (!my_module.io[4]) { // Fix Sonoff Led instabilities + pinMode(4, OUTPUT); // Stop floating outputs + digitalWrite(4, LOW); + } + if (!my_module.io[5]) { + pinMode(5, OUTPUT); // Stop floating outputs + digitalWrite(5, LOW); + } + if (!my_module.io[14]) { + pinMode(14, OUTPUT); // Stop floating outputs + digitalWrite(14, LOW); + } + light_type = LT_PWM2; + } + + if (light_type > LT_BASIC) { + devices_present++; + } + + // post-process for lights + if (Settings.flag3.pwm_multi_channels) { + uint32_t pwm_channels = (light_type & 7) > LST_MAX ? LST_MAX : (light_type & 7); + if (0 == pwm_channels) { pwm_channels = 1; } + devices_present += pwm_channels - 1; // add the pwm channels controls at the end + } + + return (light_type > LT_BASIC); +} + void LightInit(void) { - uint8_t max_scheme = LS_MAX -1; + Light.device = devices_present; + Light.subtype = (light_type & 7) > LST_MAX ? LST_MAX : (light_type & 7); // Always 0 - LST_MAX (5) + Light.pwm_multi_channels = Settings.flag3.pwm_multi_channels; - light_device = devices_present; - light_subtype = (light_type & 7) > LST_MAX ? LST_MAX : (light_type & 7); // Always 0 - LST_MAX (5) - -#if defined(USE_WS2812) && (USE_WS2812_CTYPE > NEO_3LED) - if (LT_WS2812 == light_type) { - light_subtype++; // from RGB to RGBW + if ((LST_SINGLE < Light.subtype) && Light.pwm_multi_channels) { + // we treat each PWM channel as an independant one, hence we switch to + light_controller.setPWMMultiChannel(true); + Light.device = devices_present - Light.subtype + 1; // adjust if we also have relays } +#ifdef DEBUG_LIGHT + AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightInit Light.pwm_multi_channels=%d Light.subtype=%d Light.device=%d devices_present=%d", + Light.pwm_multi_channels, Light.subtype, Light.device, devices_present); #endif - light_controller.setSubType(light_subtype); + light_controller.setSubType(Light.subtype); light_controller.loadSettings(); - if (LST_SINGLE == light_subtype) { + if (LST_SINGLE == Light.subtype) { Settings.light_color[0] = 255; // One channel only supports Dimmer but needs max color } if (light_type < LT_PWM6) { // PWM @@ -1363,20 +1131,6 @@ void LightInit(void) pinMode(pin[GPIO_PWM1 +i], OUTPUT); } } - if (SONOFF_LED == my_module_type) { // Fix Sonoff Led instabilities - if (!my_module.io[4]) { - pinMode(4, OUTPUT); // Stop floating outputs - digitalWrite(4, LOW); - } - if (!my_module.io[5]) { - pinMode(5, OUTPUT); // Stop floating outputs - digitalWrite(5, LOW); - } - if (!my_module.io[14]) { - pinMode(14, OUTPUT); // Stop floating outputs - digitalWrite(14, LOW); - } - } if (pin[GPIO_ARIRFRCV] < 99) { if (pin[GPIO_ARIRFSEL] < 99) { pinMode(pin[GPIO_ARIRFSEL], OUTPUT); @@ -1384,59 +1138,17 @@ void LightInit(void) } } } -#ifdef USE_WS2812 // ************************************************************************ - else if (LT_WS2812 == light_type) { - Ws2812Init(); - max_scheme = LS_MAX + WS2812_SCHEMES; - } -#endif // USE_WS2812 ************************************************************************ -#ifdef USE_SM16716 - else if (LT_SM16716 == light_type - light_subtype) { - // init PWM - for (uint32_t i = 0; i < light_subtype; i++) { - Settings.pwm_value[i] = 0; // Disable direct PWM control - if (pin[GPIO_PWM1 +i] < 99) { - pinMode(pin[GPIO_PWM1 +i], OUTPUT); - } - } - // init sm16716 - pinMode(sm16716_pin_clk, OUTPUT); - digitalWrite(sm16716_pin_clk, LOW); - pinMode(sm16716_pin_dat, OUTPUT); - digitalWrite(sm16716_pin_dat, LOW); - - if (sm16716_pin_sel < 99) { - pinMode(sm16716_pin_sel, OUTPUT); - digitalWrite(sm16716_pin_sel, LOW); - // no need to call SM16716_Init here, it will be called after sel goes HIGH - } else { - // no sel pin means you have an 'always on' chip, so init right away - SM16716_Init(); - } - } -#endif // ifdef USE_SM16716 - else { - light_pdi_pin = pin[GPIO_DI]; - light_pdcki_pin = pin[GPIO_DCKI]; - - pinMode(light_pdi_pin, OUTPUT); - pinMode(light_pdcki_pin, OUTPUT); - digitalWrite(light_pdi_pin, LOW); - digitalWrite(light_pdcki_pin, LOW); - - LightMy92x1Init(); - } - - if (light_subtype < LST_RGB) { + uint32_t max_scheme = Light.max_scheme; + if (Light.subtype < LST_RGB) { max_scheme = LS_POWER; } if ((LS_WAKEUP == Settings.light_scheme) || (Settings.light_scheme > max_scheme)) { Settings.light_scheme = LS_POWER; } - light_power = 0; - light_update = 1; - light_wakeup_active = 0; + Light.power = 0; + Light.update = true; + Light.wakeup_active = 0; LightUpdateColorMapping(); } @@ -1447,36 +1159,62 @@ void LightUpdateColorMapping(void) if (param > 119){ param = 0; } uint8_t tmp[] = {0,1,2,3,4}; - light_color_remap[0] = tmp[param / 24]; + Light.color_remap[0] = tmp[param / 24]; for (uint32_t i = param / 24; i<4; ++i){ tmp[i] = tmp[i+1]; } param = param % 24; - light_color_remap[1] = tmp[(param / 6)]; + Light.color_remap[1] = tmp[(param / 6)]; for (uint32_t i = param / 6; i<3; ++i){ tmp[i] = tmp[i+1]; } param = param % 6; - light_color_remap[2] = tmp[(param / 2)]; + Light.color_remap[2] = tmp[(param / 2)]; for (uint32_t i = param / 2; i<2; ++i){ tmp[i] = tmp[i+1]; } param = param % 2; - light_color_remap[3] = tmp[param]; - light_color_remap[4] = tmp[1-param]; + Light.color_remap[3] = tmp[param]; + Light.color_remap[4] = tmp[1-param]; // do not allow independant RGV and WC colors bool ct_rgb_linked = !(Settings.param[P_RGB_REMAP] & 128); light_controller.setCTRGBLinked(ct_rgb_linked); - light_update = 1; - //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%d colors: %d %d %d %d %d") ,Settings.param[P_RGB_REMAP], light_color_remap[0],light_color_remap[1],light_color_remap[2],light_color_remap[3],light_color_remap[4]); + Light.update = true; + //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%d colors: %d %d %d %d %d") ,Settings.param[P_RGB_REMAP], Light.color_remap[0],Light.color_remap[1],Light.color_remap[2],Light.color_remap[3],Light.color_remap[4]); } void LightSetDimmer(uint8_t dimmer) { light_controller.changeDimmer(dimmer); } +// If SetOption68 is set, get the brightness for a specific device +uint8_t LightGetBri(uint8_t device) { + uint8_t bri = 254; // default value if relay + if (Light.pwm_multi_channels) { + if ((device >= Light.device) && (device < Light.device + LST_MAX) && (device <= devices_present)) { + bri = Light.current_color[device - Light.device]; + } + } else if (device == Light.device) { + bri = light_state.getBri(); + } + return bri; +} + +// If SetOption68 is set, get the brightness for a specific device + +void LightSetBri(uint8_t device, uint8_t bri) { + if (Light.pwm_multi_channels) { + if ((device >= Light.device) && (device < Light.device + LST_MAX) && (device <= devices_present)) { + Light.current_color[device - Light.device] = bri; + light_controller.changeChannels(Light.current_color); + } + } else if (device == Light.device) { + light_controller.changeBri(bri); + } +} + void LightSetColorTemp(uint16_t ct) { /* Color Temperature (https://developers.meethue.com/documentation/core-concepts) @@ -1485,7 +1223,7 @@ void LightSetColorTemp(uint16_t ct) * ct = 500 = 6500K = Cold = CCWW = FF00 */ // don't set CT if not supported - if ((LST_COLDWARM != light_subtype) && (LST_RGBWC != light_subtype)) { + if ((LST_COLDWARM != Light.subtype) && (LST_RGBWC != Light.subtype)) { return; } light_controller.changeCTB(ct, light_state.getBriCT()); @@ -1494,7 +1232,7 @@ void LightSetColorTemp(uint16_t ct) uint16_t LightGetColorTemp(void) { // don't calculate CT for unsupported devices - if ((LST_COLDWARM != light_subtype) && (LST_RGBWC != light_subtype)) { + if ((LST_COLDWARM != Light.subtype) && (LST_RGBWC != Light.subtype)) { return 0; } return (light_state.getColorMode() & LCM_CT) ? light_state.getCT() : 0; @@ -1521,11 +1259,11 @@ char* LightGetColor(char* scolor, boolean force_hex = false) { light_controller.calcLevels(); scolor[0] = '\0'; - for (uint32_t i = 0; i < light_subtype; i++) { + for (uint32_t i = 0; i < Light.subtype; i++) { if (!force_hex && Settings.flag.decimal_text) { - snprintf_P(scolor, 25, PSTR("%s%s%d"), scolor, (i > 0) ? "," : "", light_current_color[i]); + snprintf_P(scolor, LIGHT_COLOR_SIZE, PSTR("%s%s%d"), scolor, (i > 0) ? "," : "", Light.current_color[i]); } else { - snprintf_P(scolor, 25, PSTR("%s%02X"), scolor, light_current_color[i]); + snprintf_P(scolor, LIGHT_COLOR_SIZE, PSTR("%s%02X"), scolor, Light.current_color[i]); } } return scolor; @@ -1533,14 +1271,14 @@ char* LightGetColor(char* scolor, boolean force_hex = false) void LightPowerOn(void) { - if (light_state.getBri() && !(light_power)) { - ExecuteCommandPower(light_device, POWER_ON, SRC_LIGHT); + if (light_state.getBri() && !(Light.power)) { + ExecuteCommandPower(Light.device, POWER_ON, SRC_LIGHT); } } void LightState(uint8_t append) { - char scolor[25]; + char scolor[LIGHT_COLOR_SIZE]; char scommand[33]; if (append) { @@ -1548,82 +1286,130 @@ void LightState(uint8_t append) } else { Response_P(PSTR("{")); } - GetPowerDevice(scommand, light_device, sizeof(scommand), Settings.flag.device_index_enable); - ResponseAppend_P(PSTR("\"%s\":\"%s\",\"" D_CMND_DIMMER "\":%d"), scommand, GetStateText(light_power), light_state.getDimmer()); - if (light_subtype > LST_SINGLE) { - ResponseAppend_P(PSTR(",\"" D_CMND_COLOR "\":\"%s\""), LightGetColor(scolor)); - uint16_t hue; - uint8_t sat, bri; - light_state.getHSB(&hue, &sat, &bri); - sat = changeUIntScale(sat, 0, 255, 0, 100); - bri = changeUIntScale(bri, 0, 255, 0, 100); + if (!Light.pwm_multi_channels) { + GetPowerDevice(scommand, Light.device, sizeof(scommand), Settings.flag.device_index_enable); + ResponseAppend_P(PSTR("\"%s\":\"%s\",\"" D_CMND_DIMMER "\":%d"), scommand, GetStateText(Light.power), light_state.getDimmer()); - ResponseAppend_P(PSTR(",\"" D_CMND_HSBCOLOR "\":\"%d,%d,%d\""), hue,sat,bri); - // Add status for each channel - ResponseAppend_P(PSTR(",\"" D_CMND_CHANNEL "\":[" )); - for (uint32_t i = 0; i < light_subtype; i++) { - uint8_t channel_raw = light_current_color[i]; - uint8_t channel = changeUIntScale(channel_raw,0,255,0,100); - // if non null, force to be at least 1 - if ((0 == channel) && (channel_raw > 0)) { channel = 1; } - ResponseAppend_P(PSTR("%s%d" ), (i > 0 ? "," : ""), channel); + if (Light.subtype > LST_SINGLE) { + ResponseAppend_P(PSTR(",\"" D_CMND_COLOR "\":\"%s\""), LightGetColor(scolor)); + uint16_t hue; + uint8_t sat, bri; + light_state.getHSB(&hue, &sat, &bri); + sat = changeUIntScale(sat, 0, 255, 0, 100); + bri = changeUIntScale(bri, 0, 255, 0, 100); + + ResponseAppend_P(PSTR(",\"" D_CMND_HSBCOLOR "\":\"%d,%d,%d\""), hue,sat,bri); + // Add status for each channel + ResponseAppend_P(PSTR(",\"" D_CMND_CHANNEL "\":[" )); + for (uint32_t i = 0; i < Light.subtype; i++) { + uint8_t channel_raw = Light.current_color[i]; + uint8_t channel = changeUIntScale(channel_raw,0,255,0,100); + // if non null, force to be at least 1 + if ((0 == channel) && (channel_raw > 0)) { channel = 1; } + ResponseAppend_P(PSTR("%s%d" ), (i > 0 ? "," : ""), channel); + } + ResponseAppend_P(PSTR("]")); } - ResponseAppend_P(PSTR("]")); - } - if ((LST_COLDWARM == light_subtype) || (LST_RGBWC == light_subtype)) { - ResponseAppend_P(PSTR(",\"" D_CMND_COLORTEMPERATURE "\":%d"), light_state.getCT()); - } - if (append) { - if (light_subtype >= LST_RGB) { - ResponseAppend_P(PSTR(",\"" D_CMND_SCHEME "\":%d"), Settings.light_scheme); + if ((LST_COLDWARM == Light.subtype) || (LST_RGBWC == Light.subtype)) { + ResponseAppend_P(PSTR(",\"" D_CMND_COLORTEMPERATURE "\":%d"), light_state.getCT()); } - if (LT_WS2812 == light_type) { - ResponseAppend_P(PSTR(",\"" D_CMND_WIDTH "\":%d"), Settings.light_width); + + if (append) { + if (Light.subtype >= LST_RGB) { + ResponseAppend_P(PSTR(",\"" D_CMND_SCHEME "\":%d"), Settings.light_scheme); + } + if (Light.max_scheme > LS_MAX) { + ResponseAppend_P(PSTR(",\"" D_CMND_WIDTH "\":%d"), Settings.light_width); + } + ResponseAppend_P(PSTR(",\"" D_CMND_FADE "\":\"%s\",\"" D_CMND_SPEED "\":%d,\"" D_CMND_LEDTABLE "\":\"%s\""), + GetStateText(Settings.light_fade), Settings.light_speed, GetStateText(Settings.light_correction)); } - ResponseAppend_P(PSTR(",\"" D_CMND_FADE "\":\"%s\",\"" D_CMND_SPEED "\":%d,\"" D_CMND_LEDTABLE "\":\"%s\""), - GetStateText(Settings.light_fade), Settings.light_speed, GetStateText(Settings.light_correction)); - } else { + } else { // Light.pwm_multi_channels + for (uint32_t i = 0; i < Light.subtype; i++) { + GetPowerDevice(scommand, Light.device + i, sizeof(scommand), 1); + uint32_t light_power_masked = Light.power & (1 << i); // the Light.power value for this device + light_power_masked = light_power_masked ? 1 : 0; // convert to on/off + ResponseAppend_P(PSTR("\"%s\":\"%s\",\"" D_CMND_CHANNEL "%d\":%d,"), scommand, GetStateText(light_power_masked), Light.device + i, + changeUIntScale(Light.current_color[i], 0, 255, 0, 100)); + } + ResponseAppend_P(PSTR("\"" D_CMND_COLOR "\":\"%s\""), LightGetColor(scolor)); + } // Light.pwm_multi_channels + + if (!append) { ResponseJsonEnd(); } } void LightPreparePower(void) { - if (light_state.getBri() && !(light_power)) { - if (!Settings.flag.not_power_linked) { - ExecuteCommandPower(light_device, POWER_ON_NO_STATE, SRC_LIGHT); +#ifdef DEBUG_LIGHT + AddLog_P2(LOG_LEVEL_DEBUG, "LightPreparePower power=%d Light.power=%d", power, Light.power); +#endif + // If multi-channels, then we only switch off channels with a value of zero + if (Light.pwm_multi_channels) { +// for (uint32_t i = 0; i < Light.subtype; i++) { +// // if channel is non-null, channel is supposed to be on, but it is off, do Power On +// if ((Light.current_color[i]) && (bitRead(Light.power, i)) && (0 == bitRead(power, i + Light.device - 1))) { +// ExecuteCommandPower(Light.device + i, POWER_ON_NO_STATE, SRC_LIGHT); +// //bitSet(Settings.power, i + Light.device - 1); +// #ifdef DEBUG_LIGHT +// AddLog_P2(LOG_LEVEL_DEBUG, "ExecuteCommandPower ON device=%d", Light.device + i); +// #endif +// } +// // if channel is zero and channel is on, set it off +// if ((0 == Light.current_color[i]) && bitRead(power, i + Light.device - 1)) { +// ExecuteCommandPower(Light.device + i, POWER_OFF_NO_STATE, SRC_LIGHT); +// //bitClear(Settings.power, i + Light.device - 1); +// #ifdef DEBUG_LIGHT +// AddLog_P2(LOG_LEVEL_DEBUG, "ExecuteCommandPower OFF device=%d", Light.device + i); +// #endif +// } +// #ifdef USE_DOMOTICZ +// DomoticzUpdatePowerState(Light.device + i); +// #endif // USE_DOMOTICZ +// } + } else { + if (light_state.getBri() && !(Light.power)) { + if (!Settings.flag.not_power_linked) { + ExecuteCommandPower(Light.device, POWER_ON_NO_STATE, SRC_LIGHT); + } + } + else if (!light_state.getBri() && Light.power) { + ExecuteCommandPower(Light.device, POWER_OFF_NO_STATE, SRC_LIGHT); } - } - else if (!light_state.getBri() && light_power) { - ExecuteCommandPower(light_device, POWER_OFF_NO_STATE, SRC_LIGHT); - } #ifdef USE_DOMOTICZ - DomoticzUpdatePowerState(light_device); + DomoticzUpdatePowerState(Light.device); #endif // USE_DOMOTICZ + } + if (Settings.flag3.hass_tele_on_power) { MqttPublishTeleState(); } +#ifdef DEBUG_LIGHT + AddLog_P2(LOG_LEVEL_DEBUG, "LightPreparePower End power=%d Light.power=%d", power, Light.power); +#endif + Light.power = power >> (Light.device - 1); // reset next state LightState(0); } void LightFade(void) { if (0 == Settings.light_fade) { - for (uint32_t i = 0; i < light_subtype; i++) { - light_new_color[i] = light_current_color[i]; + for (uint32_t i = 0; i < Light.subtype; i++) { + Light.new_color[i] = Light.current_color[i]; } } else { uint8_t shift = Settings.light_speed; if (Settings.light_speed > 6) { - shift = (strip_timer_counter % (Settings.light_speed -6)) ? 0 : 8; + shift = (Light.strip_timer_counter % (Settings.light_speed -6)) ? 0 : 8; } if (shift) { - for (uint32_t i = 0; i < light_subtype; i++) { - if (light_new_color[i] != light_current_color[i]) { - if (light_new_color[i] < light_current_color[i]) { - light_new_color[i] += ((light_current_color[i] - light_new_color[i]) >> shift) +1; + for (uint32_t i = 0; i < Light.subtype; i++) { + if (Light.new_color[i] != Light.current_color[i]) { + if (Light.new_color[i] < Light.current_color[i]) { + Light.new_color[i] += ((Light.current_color[i] - Light.new_color[i]) >> shift) +1; } - if (light_new_color[i] > light_current_color[i]) { - light_new_color[i] -= ((light_new_color[i] - light_current_color[i]) >> shift) +1; + if (Light.new_color[i] > Light.current_color[i]) { + Light.new_color[i] -= ((Light.new_color[i] - Light.current_color[i]) >> shift) +1; } } } @@ -1635,66 +1421,80 @@ void LightWheel(uint8_t wheel_pos) { wheel_pos = 255 - wheel_pos; if (wheel_pos < 85) { - light_entry_color[0] = 255 - wheel_pos * 3; - light_entry_color[1] = 0; - light_entry_color[2] = wheel_pos * 3; + Light.entry_color[0] = 255 - wheel_pos * 3; + Light.entry_color[1] = 0; + Light.entry_color[2] = wheel_pos * 3; } else if (wheel_pos < 170) { wheel_pos -= 85; - light_entry_color[0] = 0; - light_entry_color[1] = wheel_pos * 3; - light_entry_color[2] = 255 - wheel_pos * 3; + Light.entry_color[0] = 0; + Light.entry_color[1] = wheel_pos * 3; + Light.entry_color[2] = 255 - wheel_pos * 3; } else { wheel_pos -= 170; - light_entry_color[0] = wheel_pos * 3; - light_entry_color[1] = 255 - wheel_pos * 3; - light_entry_color[2] = 0; + Light.entry_color[0] = wheel_pos * 3; + Light.entry_color[1] = 255 - wheel_pos * 3; + Light.entry_color[2] = 0; } - light_entry_color[3] = 0; - light_entry_color[4] = 0; + Light.entry_color[3] = 0; + Light.entry_color[4] = 0; float dimmer = 100 / (float)Settings.light_dimmer; for (uint32_t i = 0; i < LST_RGB; i++) { - float temp = (float)light_entry_color[i] / dimmer + 0.5f; - light_entry_color[i] = (uint8_t)temp; + float temp = (float)Light.entry_color[i] / dimmer + 0.5f; + Light.entry_color[i] = (uint8_t)temp; } } void LightCycleColor(int8_t direction) { - if (strip_timer_counter % (Settings.light_speed * 2)) { + if (Light.strip_timer_counter % (Settings.light_speed * 2)) { return; } - light_wheel += direction; - LightWheel(light_wheel); - memcpy(light_new_color, light_entry_color, sizeof(light_new_color)); + Light.wheel += direction; + LightWheel(Light.wheel); + memcpy(Light.new_color, Light.entry_color, sizeof(Light.new_color)); } void LightRandomColor(void) { - uint8_t light_update = 0; + bool update = false; for (uint32_t i = 0; i < LST_RGB; i++) { - if (light_new_color[i] != light_current_color[i]) { - light_update = 1; + if (Light.new_color[i] != Light.current_color[i]) { + update = true; } } - if (!light_update) { - light_wheel = random(255); - LightWheel(light_wheel); - memcpy(light_current_color, light_entry_color, sizeof(light_current_color)); - light_controller.changeChannels(light_current_color); + if (!update) { + Light.wheel = random(255); + LightWheel(Light.wheel); + memcpy(Light.current_color, Light.entry_color, sizeof(Light.current_color)); } LightFade(); } void LightSetPower(void) { -// light_power = XdrvMailbox.index; - light_old_power = light_power; - light_power = bitRead(XdrvMailbox.index, light_device -1); - if (light_wakeup_active) { - light_wakeup_active--; +// Light.power = XdrvMailbox.index; + Light.old_power = Light.power; + //Light.power = bitRead(XdrvMailbox.index, Light.device -1); + uint32_t mask = 1; // default mask + if (Light.pwm_multi_channels) { + mask = (1 << Light.subtype) - 1; // wider mask } - if (light_power && !light_old_power) { - light_update = 1; + uint32_t shift = Light.device - 1; + // If PWM multi_channels + // Ex: 3 Relays and 4 PWM - devices_present = 7, Light.device = 4, Light.subtype = 4 + // Result: mask = 0b00001111 = 0x0F, shift = 3. + // Power bits we consider are: 0b01111000 = 0x78 + // If regular situation: devices_present == Light.subtype + Light.power = (XdrvMailbox.index & (mask << shift)) >> shift; + if (Light.wakeup_active) { + Light.wakeup_active--; + } +#ifdef DEBUG_LIGHT + AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightSetPower XdrvMailbox.index=%d Light.old_power=%d Light.power=%d mask=%d shift=%d", + XdrvMailbox.index, Light.old_power, Light.power, mask, shift); +#endif + if (Light.power != Light.old_power) { + Light.update = true; } LightAnimate(); } @@ -1704,26 +1504,26 @@ void LightAnimate(void) uint8_t cur_col[LST_MAX]; uint16_t light_still_on = 0; - strip_timer_counter++; - if (!light_power) { // Power Off + Light.strip_timer_counter++; + if (!Light.power) { // Power Off sleep = Settings.sleep; - strip_timer_counter = 0; - for (uint32_t i = 0; i < light_subtype; i++) { - light_still_on += light_new_color[i]; + Light.strip_timer_counter = 0; + for (uint32_t i = 0; i < Light.subtype; i++) { + light_still_on += Light.new_color[i]; } if (light_still_on && Settings.light_fade && (Settings.light_scheme < LS_MAX)) { uint8_t speed = Settings.light_speed; if (speed > 6) { speed = 6; } - for (uint32_t i = 0; i < light_subtype; i++) { - if (light_new_color[i] > 0) { - light_new_color[i] -= (light_new_color[i] >> speed) +1; + for (uint32_t i = 0; i < Light.subtype; i++) { + if (Light.new_color[i] > 0) { + Light.new_color[i] -= (Light.new_color[i] >> speed) +1; } } } else { - for (uint32_t i = 0; i < light_subtype; i++) { - light_new_color[i] = 0; + for (uint32_t i = 0; i < Light.subtype; i++) { + Light.new_color[i] = 0; } } } @@ -1739,28 +1539,28 @@ void LightAnimate(void) LightFade(); break; case LS_WAKEUP: - if (2 == light_wakeup_active) { - light_wakeup_active = 1; - for (uint32_t i = 0; i < light_subtype; i++) { - light_new_color[i] = 0; + if (2 == Light.wakeup_active) { + Light.wakeup_active = 1; + for (uint32_t i = 0; i < Light.subtype; i++) { + Light.new_color[i] = 0; } - light_wakeup_counter = 0; - light_wakeup_dimmer = 0; + Light.wakeup_counter = 0; + Light.wakeup_dimmer = 0; } - light_wakeup_counter++; - if (light_wakeup_counter > ((Settings.light_wakeup * STATES) / Settings.light_dimmer)) { - light_wakeup_counter = 0; - light_wakeup_dimmer++; - if (light_wakeup_dimmer <= Settings.light_dimmer) { - light_state.setDimmer(light_wakeup_dimmer); + Light.wakeup_counter++; + if (Light.wakeup_counter > ((Settings.light_wakeup * STATES) / Settings.light_dimmer)) { + Light.wakeup_counter = 0; + Light.wakeup_dimmer++; + if (Light.wakeup_dimmer <= Settings.light_dimmer) { + light_state.setDimmer(Light.wakeup_dimmer); light_controller.calcLevels(); - for (uint32_t i = 0; i < light_subtype; i++) { - light_new_color[i] = light_current_color[i]; + for (uint32_t i = 0; i < Light.subtype; i++) { + Light.new_color[i] = Light.current_color[i]; } } else { Response_P(PSTR("{\"" D_CMND_WAKEUP "\":\"" D_JSON_DONE "\"}")); MqttPublishPrefixTopic_P(TELE, PSTR(D_CMND_WAKEUP)); - light_wakeup_active = 0; + Light.wakeup_active = 0; Settings.light_scheme = LS_POWER; } } @@ -1774,74 +1574,74 @@ void LightAnimate(void) case LS_RANDOM: LightRandomColor(); break; -#ifdef USE_WS2812 // ************************************************************************ default: - if (LT_WS2812 == light_type) { - Ws2812ShowScheme(Settings.light_scheme -LS_MAX); - } -#endif // USE_WS2812 ************************************************************************ + XlgtCall(FUNC_SET_SCHEME); } } - if ((Settings.light_scheme < LS_MAX) || !light_power) { - if (memcmp(light_last_color, light_new_color, light_subtype)) { - light_update = 1; - } - if (light_update) { - uint16_t cur_col_10bits[LST_MAX]; // 10 bits version of cur_col for PWM - light_update = 0; + if ((Settings.light_scheme < LS_MAX) || !Light.power) { - // first adjust all colors to RgbwwTable if needed + // If SetOption68, multi_channels + if (Light.pwm_multi_channels) { + // if multi-channels, specifically apply the Light.power bits for (uint32_t i = 0; i < LST_MAX; i++) { - light_last_color[i] = light_new_color[i]; - // adjust from 0.255 to 0..Settings.rgbwwTable[i] -- RgbwwTable command - // protect against overflow of rgbwwTable which is of size 5 - cur_col[i] = changeUIntScale(light_last_color[i], 0, 255, 0, (i<5)? Settings.rgbwwTable[i] : 255); + if (0 == bitRead(Light.power,i)) { // if power down bit is zero + Light.new_color[i] = 0; // shut down this channel + } + } + // #ifdef DEBUG_LIGHT + // AddLog_P2(LOG_LEVEL_DEBUG_MORE, "Animate>> Light.power=%d Light.new_color=[%d,%d,%d,%d,%d]", + // Light.power, Light.new_color[0], Light.new_color[1], Light.new_color[2], + // Light.new_color[3], Light.new_color[4]); + // #endif + } + + if (memcmp(Light.last_color, Light.new_color, Light.subtype)) { + Light.update = true; + } + if (Light.update) { + uint16_t cur_col_10bits[LST_MAX]; // 10 bits version of cur_col for PWM + Light.update = false; + + // first set 8 and 10 bits channels + for (uint32_t i = 0; i < LST_MAX; i++) { + cur_col[i] = Light.last_color[i] = Light.new_color[i]; // Extend from 8 to 10 bits if no correction (in case no gamma correction is required) cur_col_10bits[i] = changeUIntScale(cur_col[i], 0, 255, 0, 1023); } - - if (PHILIPS == my_module_type) { - // TODO - // Xiaomi Philips bulbs follow a different scheme: - // channel 0=intensity, channel2=temperature - uint16_t pxBri = cur_col[0] + cur_col[1]; - if (pxBri > 255) { pxBri = 255; } - //cur_col[1] = cur_col[0]; // get 8 bits CT from WC -- not really used - cur_col_10bits[1] = changeUIntScale(cur_col[0], 0, pxBri, 0, 1023); // get 10 bits CT from WC / (WC+WW) - if (Settings.light_correction) { // gamma correction - cur_col_10bits[0] = ledGamma(pxBri, 10); // 10 bits gamma correction - } else { - cur_col_10bits[0] = changeUIntScale(pxBri, 0, 255, 0, 1023); // no gamma, extend to 10 bits - } + if (Light.pwm_multi_channels) { + calcGammaMultiChannels(cur_col, cur_col_10bits); } else { - // Apply gamma correction for 8 and 10 bits resolutions, if needed - if (Settings.light_correction) { - // first apply gamma correction to all channels independently, from 8 bits value - for (uint32_t i = 0; i < LST_MAX; i++) { - cur_col_10bits[i] = ledGamma(cur_col[i], 10); + calcGammaBulbs(cur_col, cur_col_10bits); + if (PHILIPS == my_module_type) { + calcGammaCTPwm(cur_col, cur_col_10bits); + } + + // Now see if we need to mix RGB and True White + // Valid only for LST_RGBW, LST_RGBWC, rgbwwTable[4] is zero, and white is zero (see doc) + if ((LST_RGBW <= Light.subtype) && (0 == Settings.rgbwwTable[4]) && (0 == cur_col[3]+cur_col[4])) { + uint32_t min_rgb_10 = min3(cur_col_10bits[0], cur_col_10bits[1], cur_col_10bits[2]); + uint8_t min_rgb = min3(cur_col[0], cur_col[1], cur_col[2]); + for (uint32_t i=0; i<3; i++) { + // substract white and adjust according to rgbwwTable + cur_col_10bits[i] = changeUIntScale(cur_col_10bits[i] - min_rgb_10, 0, 255, 0, Settings.rgbwwTable[i]); + cur_col[i] = changeUIntScale(cur_col[i] - min_rgb, 0, 255, 0, Settings.rgbwwTable[i]); } - // then apply a different correction for CW white channels - if ((LST_COLDWARM == light_subtype) || (LST_RGBWC == light_subtype)) { - uint8_t w_idx[2] = {0, 1}; // if LST_COLDWARM, channels 0 and 1 - if (LST_RGBWC == light_subtype) { // if LST_RGBWC, channels 3 and 4 - w_idx[0] = 3; - w_idx[1] = 4; - } - uint16_t white_bri = cur_col[w_idx[0]] + cur_col[w_idx[1]]; - // if sum of both channels is > 255, then channels are probablu uncorrelated - if (white_bri <= 255) { - // we calculate the gamma corrected sum of CW + WW - uint16_t white_bri_10bits = ledGamma(white_bri, 10); - // then we split the total energy among the cold and warm leds - cur_col_10bits[w_idx[0]] = changeUIntScale(cur_col[w_idx[0]], 0, white_bri, 0, white_bri_10bits); - cur_col_10bits[w_idx[1]] = changeUIntScale(cur_col[w_idx[1]], 0, white_bri, 0, white_bri_10bits); - } - } - // still keep an 8 bits gamma corrected version - for (uint32_t i = 0; i < LST_MAX; i++) { - cur_col[i] = ledGamma(cur_col[i]); + // compute the adjusted white levels for 10 and 8 bits + uint32_t white_10 = changeUIntScale(min_rgb_10, 0, 255, 0, Settings.rgbwwTable[3]); // set white power down corrected with rgbwwTable[3] + uint32_t white = changeUIntScale(min_rgb, 0, 255, 0, Settings.rgbwwTable[3]); // set white power down corrected with rgbwwTable[3] + if (LST_RGBW == Light.subtype) { + // we simply set the white channel + cur_col_10bits[3] = white_10; + cur_col[3] = white; + } else { // LST_RGBWC + // we distribute white between cold and warm according to CT value + uint32_t ct = light_state.getCT(); + cur_col_10bits[4] = changeUIntScale(ct, 153, 500, 0, white_10); + cur_col_10bits[3] = white_10 - cur_col_10bits[4]; + cur_col[4] = changeUIntScale(ct, 153, 500, 0, white); + cur_col[3] = white - cur_col[4]; } } } @@ -1865,51 +1665,118 @@ void LightAnimate(void) memcpy(orig_col, cur_col, sizeof(orig_col)); memcpy(orig_col_10bits, cur_col_10bits, sizeof(orig_col_10bits)); for (uint32_t i = 0; i < LST_MAX; i++) { - cur_col[i] = orig_col[light_color_remap[i]]; - cur_col_10bits[i] = orig_col_10bits[light_color_remap[i]]; + cur_col[i] = orig_col[Light.color_remap[i]]; + cur_col_10bits[i] = orig_col_10bits[Light.color_remap[i]]; } // now apply the actual PWM values, adjusted and remapped 10-bits range if (light_type < LT_PWM6) { // only for direct PWM lights, not for Tuya, Armtronix... - for (uint32_t i = 0; i < light_subtype; i++) { + for (uint32_t i = 0; i < (Light.subtype - Light.pwm_offset); i++) { if (pin[GPIO_PWM1 +i] < 99) { - //AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION "Cur_Col%d 10 bits %d, Pwm%d %d"), i, cur_col[i], i+1, curcol); - analogWrite(pin[GPIO_PWM1 +i], bitRead(pwm_inverted, i) ? Settings.pwm_range - cur_col_10bits[i] : cur_col_10bits[i]); + //AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION "Cur_Col%d 10 bits %d, Pwm%d %d"), i, cur_col_10bits[i], i+1, cur_col[i]); + analogWrite(pin[GPIO_PWM1 +i], bitRead(pwm_inverted, i) ? Settings.pwm_range - cur_col_10bits[(i + Light.pwm_offset)] : cur_col_10bits[(i + Light.pwm_offset)]); } } } + // Some devices need scaled RGB like Sonoff L1 + uint8_t scale_col[3]; + uint32_t max = (cur_col[0] > cur_col[1] && cur_col[0] > cur_col[2]) ? cur_col[0] : (cur_col[1] > cur_col[2]) ? cur_col[1] : cur_col[2]; // 0..255 + for (uint32_t i = 0; i < 3; i++) { + scale_col[i] = (0 == max) ? 255 : (255 > max) ? changeUIntScale(cur_col[i], 0, max, 0, 255) : cur_col[i]; + } +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("LGT: R%d(%d) G%d(%d) B%d(%d), C%d W%d, D%d"), +// cur_col[0], scale_col[0], cur_col[1], scale_col[1], cur_col[2], scale_col[2], cur_col[3], cur_col[4], light_state.getDimmer()); + char *tmp_data = XdrvMailbox.data; - uint16_t tmp_data_len = XdrvMailbox.data_len; - + char *tmp_topic = XdrvMailbox.topic; XdrvMailbox.data = (char*)cur_col; - XdrvMailbox.data_len = sizeof(cur_col); - if (XdrvCall(FUNC_SET_CHANNELS)) { + XdrvMailbox.topic = (char*)scale_col; + if (XlgtCall(FUNC_SET_CHANNELS)) { // Serviced } -#ifdef USE_WS2812 // ************************************************************************ - else if (LT_WS2812 == light_type) { - Ws2812SetColor(0, cur_col[0], cur_col[1], cur_col[2], cur_col[3]); - } -#endif // USE_ES2812 ************************************************************************ -#ifdef USE_SM16716 - else if (LT_SM16716 == light_type - light_subtype) { - // handle any PWM pins, skipping the first 3 values for sm16716 - for (uint32_t i = 3; i < light_subtype; i++) { - if (pin[GPIO_PWM1 +i-3] < 99) { - //AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION "Cur_Col%d 10 bits %d, Pwm%d %d"), i, cur_col[i], i+1, curcol); - analogWrite(pin[GPIO_PWM1 +i-3], bitRead(pwm_inverted, i-3) ? Settings.pwm_range - cur_col_10bits[i] : cur_col_10bits[i]); - } - } - // handle sm16716 update - SM16716_Update(cur_col[0], cur_col[1], cur_col[2]); - } -#endif // ifdef USE_SM16716 - else if (light_type > LT_WS2812) { - LightMy92x1Duty(cur_col[0], cur_col[1], cur_col[2], cur_col[3], cur_col[4]); + else if (XdrvCall(FUNC_SET_CHANNELS)) { + // Serviced } XdrvMailbox.data = tmp_data; - XdrvMailbox.data_len = tmp_data_len; + XdrvMailbox.topic = tmp_topic; + } + } +} + +// Do specific computation is SetOption73 is on, Color Temp is a separate PWM channel +void calcGammaCTPwm(uint8_t cur_col[5], uint16_t cur_col_10bits[5]) { + // Xiaomi Philips bulbs follow a different scheme: + uint8_t cold, warm; // channel 1 is the color tone, mapped to cold channel (0..255) + light_state.getCW(&cold, &warm); + // channels for white are always the last two channels + uint32_t cw1 = Light.subtype - 1; // address for the ColorTone PWM + uint32_t cw0 = Light.subtype - 2; // address for the White Brightness PWM + // overall brightness + uint16_t pxBri = cur_col[cw0] + cur_col[cw1]; + if (pxBri > 255) { pxBri = 255; } + cur_col[cw1] = changeUIntScale(cold, 0, cold + warm, 0, 255); // + cur_col_10bits[cw1] = changeUIntScale(cur_col[cw1], 0, 255, 0, 1023); + // channel 0=intensity, channel1=temperature + if (Settings.light_correction) { // gamma correction + cur_col[cw0] = ledGamma(pxBri); + cur_col_10bits[cw0] = ledGamma(pxBri, 10); // 10 bits gamma correction + } else { + cur_col[cw0] = pxBri; + cur_col_10bits[cw0] = changeUIntScale(pxBri, 0, 255, 0, 1023); // no gamma, extend to 10 bits + } +} + +// Just apply basic Gamma to each channel +void calcGammaMultiChannels(uint8_t cur_col[5], uint16_t cur_col_10bits[5]) { + // Apply gamma correction for 8 and 10 bits resolutions, if needed + if (Settings.light_correction) { + for (uint32_t i = 0; i < LST_MAX; i++) { + cur_col_10bits[i] = ledGamma(cur_col[i], 10); + cur_col[i] = ledGamma(cur_col[i]); + } + } +} + +void calcGammaBulbs(uint8_t cur_col[5], uint16_t cur_col_10bits[5]) { + // Apply gamma correction for 8 and 10 bits resolutions, if needed + if (Settings.light_correction) { + // First apply combined correction to the overall white power + if ((LST_COLDWARM == Light.subtype) || (LST_RGBWC == Light.subtype)) { + uint8_t w_idx[2] = {0, 1}; // if LST_COLDWARM, channels 0 and 1 + if (LST_RGBWC == Light.subtype) { // if LST_RGBWC, channels 3 and 4 + w_idx[0] = 3; + w_idx[1] = 4; + } + uint16_t white_bri = cur_col[w_idx[0]] + cur_col[w_idx[1]]; + // if sum of both channels is > 255, then channels are probablu uncorrelated + if (white_bri <= 255) { + // we calculate the gamma corrected sum of CW + WW + uint16_t white_bri_10bits = ledGamma(white_bri, 10); + uint8_t white_bri_8bits = ledGamma(white_bri); + // then we split the total energy among the cold and warm leds + cur_col_10bits[w_idx[0]] = changeUIntScale(cur_col[w_idx[0]], 0, white_bri, 0, white_bri_10bits); + cur_col_10bits[w_idx[1]] = changeUIntScale(cur_col[w_idx[1]], 0, white_bri, 0, white_bri_10bits); + cur_col[w_idx[0]] = changeUIntScale(cur_col[w_idx[0]], 0, white_bri, 0, white_bri_8bits); + cur_col[w_idx[1]] = changeUIntScale(cur_col[w_idx[1]], 0, white_bri, 0, white_bri_8bits); + } else { + cur_col_10bits[w_idx[0]] = ledGamma(cur_col[w_idx[0]], 10); + cur_col_10bits[w_idx[1]] = ledGamma(cur_col[w_idx[1]], 10); + cur_col[w_idx[0]] = ledGamma(cur_col[w_idx[0]]); + cur_col[w_idx[1]] = ledGamma(cur_col[w_idx[1]]); + } + } + // then apply gamma correction to RGB channels + if (LST_RGB <= Light.subtype) { + for (uint32_t i = 0; i < 3; i++) { + cur_col_10bits[i] = ledGamma(cur_col[i], 10); + cur_col[i] = ledGamma(cur_col[i]); + } + } + // If RGBW or Single channel, also adjust White channel + if ((LST_COLDWARM != Light.subtype) && (LST_RGBWC != Light.subtype)) { + cur_col_10bits[3] = ledGamma(cur_col[3], 10); + cur_col[3] = ledGamma(cur_col[3]); } } } @@ -1918,64 +1785,64 @@ void LightAnimate(void) * Commands \*********************************************************************************************/ -bool LightColorEntry(char *buffer, uint8_t buffer_length) +bool LightColorEntry(char *buffer, uint32_t buffer_length) { char scolor[10]; char *p; char *str; - uint8_t entry_type = 0; // Invalid - uint8_t value = light_fixed_color_index; + uint32_t entry_type = 0; // Invalid + uint8_t value = Light.fixed_color_index; if (buffer[0] == '#') { // Optional hexadecimal entry buffer++; buffer_length--; } - if (light_subtype >= LST_RGB) { + if (Light.subtype >= LST_RGB) { char option = (1 == buffer_length) ? buffer[0] : '\0'; - if (('+' == option) && (light_fixed_color_index < MAX_FIXED_COLOR)) { + if (('+' == option) && (Light.fixed_color_index < MAX_FIXED_COLOR)) { value++; } - else if (('-' == option) && (light_fixed_color_index > 1)) { + else if (('-' == option) && (Light.fixed_color_index > 1)) { value--; } else { value = atoi(buffer); } } - memset(&light_entry_color, 0x00, sizeof(light_entry_color)); + memset(&Light.entry_color, 0x00, sizeof(Light.entry_color)); if (strstr(buffer, ",") != nullptr) { // Decimal entry int8_t i = 0; for (str = strtok_r(buffer, ",", &p); str && i < 6; str = strtok_r(nullptr, ",", &p)) { if (i < LST_MAX) { - light_entry_color[i++] = atoi(str); + Light.entry_color[i++] = atoi(str); } } entry_type = 2; // Decimal } - else if (((2 * light_subtype) == buffer_length) || (buffer_length > 3)) { // Hexadecimal entry - for (uint32_t i = 0; i < tmin((uint)(buffer_length / 2), sizeof(light_entry_color)); i++) { + else if (((2 * Light.subtype) == buffer_length) || (buffer_length > 3)) { // Hexadecimal entry + for (uint32_t i = 0; i < tmin((uint)(buffer_length / 2), sizeof(Light.entry_color)); i++) { strlcpy(scolor, buffer + (i *2), 3); - light_entry_color[i] = (uint8_t)strtol(scolor, &p, 16); + Light.entry_color[i] = (uint8_t)strtol(scolor, &p, 16); } entry_type = 1; // Hexadecimal } - else if ((light_subtype >= LST_RGB) && (value > 0) && (value <= MAX_FIXED_COLOR)) { - light_fixed_color_index = value; - memcpy_P(&light_entry_color, &kFixedColor[value -1], 3); + else if ((Light.subtype >= LST_RGB) && (value > 0) && (value <= MAX_FIXED_COLOR)) { + Light.fixed_color_index = value; + memcpy_P(&Light.entry_color, &kFixedColor[value -1], 3); entry_type = 1; // Hexadecimal } else if ((value > 199) && (value <= 199 + MAX_FIXED_COLD_WARM)) { - if (LST_RGBW == light_subtype) { - memcpy_P(&light_entry_color[3], &kFixedWhite[value -200], 1); + if (LST_RGBW == Light.subtype) { + memcpy_P(&Light.entry_color[3], &kFixedWhite[value -200], 1); entry_type = 1; // Hexadecimal } - else if (LST_COLDWARM == light_subtype) { - memcpy_P(&light_entry_color, &kFixedColdWarm[value -200], 2); + else if (LST_COLDWARM == Light.subtype) { + memcpy_P(&Light.entry_color, &kFixedColdWarm[value -200], 2); entry_type = 1; // Hexadecimal } - else if (LST_RGBWC == light_subtype) { - memcpy_P(&light_entry_color[3], &kFixedColdWarm[value -200], 2); + else if (LST_RGBWC == Light.subtype) { + memcpy_P(&Light.entry_color[3], &kFixedColdWarm[value -200], 2); entry_type = 1; // Hexadecimal } } @@ -1987,82 +1854,106 @@ bool LightColorEntry(char *buffer, uint8_t buffer_length) /********************************************************************************************/ -bool LightCommand(void) +void CmndSupportColor(void) { - char command [CMDSZ]; - bool serviced = true; - bool coldim = false; bool valid_entry = false; - char scolor[25]; - char option = (1 == XdrvMailbox.data_len) ? XdrvMailbox.data[0] : '\0'; + bool coldim = false; - int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic, kLightCommands); - if (-1 == command_code) { - serviced = false; // Unknown command + if (XdrvMailbox.data_len > 0) { + valid_entry = LightColorEntry(XdrvMailbox.data, XdrvMailbox.data_len); + if (valid_entry) { + if (XdrvMailbox.index <= 2) { // Color(1), 2 + uint32_t old_bri = light_state.getBri(); + // change all channels to specified values + light_controller.changeChannels(Light.entry_color); + if (2 == XdrvMailbox.index) { + // If Color2, set back old brightness + light_controller.changeBri(old_bri); + } + + Settings.light_scheme = 0; + coldim = true; + } else { // Color3, 4, 5 and 6 + for (uint32_t i = 0; i < LST_RGB; i++) { + Settings.ws_color[XdrvMailbox.index -3][i] = Light.entry_color[i]; + } + } + } } - else if (((CMND_COLOR == command_code) && (light_subtype > LST_SINGLE) && (XdrvMailbox.index > 0) && (XdrvMailbox.index <= 6)) || - ((CMND_WHITE == command_code) && (light_subtype == LST_RGBW) && (XdrvMailbox.index == 1))) { - if (CMND_WHITE == command_code) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) { - uint8_t whiteBri = changeUIntScale(XdrvMailbox.payload,0,100,0,255); - snprintf_P(scolor, sizeof(scolor), PSTR("0,0,0,%d"), whiteBri); - light_state.setBri(whiteBri); // save target Bri, will be confirmed below - XdrvMailbox.data = scolor; - XdrvMailbox.data_len = strlen(scolor); + char scolor[LIGHT_COLOR_SIZE]; + if (!valid_entry && (XdrvMailbox.index <= 2)) { + ResponseCmndChar(LightGetColor(scolor)); + } + if (XdrvMailbox.index >= 3) { + scolor[0] = '\0'; + for (uint32_t i = 0; i < LST_RGB; i++) { + if (Settings.flag.decimal_text) { + snprintf_P(scolor, sizeof(scolor), PSTR("%s%s%d"), scolor, (i > 0) ? "," : "", Settings.ws_color[XdrvMailbox.index -3][i]); } else { - XdrvMailbox.data_len = 0; + snprintf_P(scolor, sizeof(scolor), PSTR("%s%02X"), scolor, Settings.ws_color[XdrvMailbox.index -3][i]); } } - if (XdrvMailbox.data_len > 0) { - valid_entry = LightColorEntry(XdrvMailbox.data, XdrvMailbox.data_len); - if (valid_entry) { - if (XdrvMailbox.index <= 2) { // Color(1), 2 - uint8_t old_bri = light_state.getBri(); - // change all channels to specified values - light_controller.changeChannels(light_entry_color); - if (2 == XdrvMailbox.index) { - // If Color2, set back old brightness - light_controller.changeBri(old_bri); - } - - Settings.light_scheme = 0; - coldim = true; - } else { // Color3, 4, 5 and 6 - for (uint32_t i = 0; i < LST_RGB; i++) { - Settings.ws_color[XdrvMailbox.index -3][i] = light_entry_color[i]; - } - } - } - } - if (!valid_entry && (XdrvMailbox.index <= 2)) { - Response_P(S_JSON_COMMAND_SVALUE, command, LightGetColor(scolor)); - } - if (XdrvMailbox.index >= 3) { - scolor[0] = '\0'; - for (uint32_t i = 0; i < LST_RGB; i++) { - if (Settings.flag.decimal_text) { - snprintf_P(scolor, 25, PSTR("%s%s%d"), scolor, (i > 0) ? "," : "", Settings.ws_color[XdrvMailbox.index -3][i]); - } else { - snprintf_P(scolor, 25, PSTR("%s%02X"), scolor, Settings.ws_color[XdrvMailbox.index -3][i]); - } - } - Response_P(S_JSON_COMMAND_INDEX_SVALUE, command, XdrvMailbox.index, scolor); - } + ResponseCmndIdxChar(scolor); } - else if ((CMND_CHANNEL == command_code) && (XdrvMailbox.index > 0) && (XdrvMailbox.index <= light_subtype ) ) { + if (coldim) { + LightPreparePower(); + } +} + +void CmndColor(void) +{ + if ((Light.subtype > LST_SINGLE) && (XdrvMailbox.index > 0) && (XdrvMailbox.index <= 6)) { + CmndSupportColor(); + } +} + +void CmndWhite(void) +{ + if ((Light.subtype == LST_RGBW) && (XdrvMailbox.index == 1)) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) { + uint32_t whiteBri = changeUIntScale(XdrvMailbox.payload,0,100,0,255); + char scolor[LIGHT_COLOR_SIZE]; + snprintf_P(scolor, sizeof(scolor), PSTR("0,0,0,%d"), whiteBri); + light_state.setBri(whiteBri); // save target Bri, will be confirmed below + XdrvMailbox.data = scolor; + XdrvMailbox.data_len = strlen(scolor); + } else { + XdrvMailbox.data_len = 0; + } + CmndSupportColor(); + } +} + +void CmndChannel(void) +{ + if ((XdrvMailbox.index >= Light.device) && (XdrvMailbox.index < Light.device + Light.subtype )) { + bool coldim = false; // Set "Channel" directly - this allows Color and Direct PWM control to coexist if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) { - light_current_color[XdrvMailbox.index-1] = changeUIntScale(XdrvMailbox.payload,0,100,0,255); - // if we change channels 1,2,3 then turn off CT mode (unless non-linked) - if ((XdrvMailbox.index <= 3) && (light_controller.isCTRGBLinked())) { - light_current_color[3] = light_current_color[4] = 0; + Light.current_color[XdrvMailbox.index - Light.device] = changeUIntScale(XdrvMailbox.payload,0,100,0,255); + if (Light.pwm_multi_channels) { + // if (!Settings.flag.not_power_linked) { // SetOption20 + // Light.power = Light.power | (1 << (XdrvMailbox.index - Light.device)); // ask to turn on channel + // } + } else { + // if we change channels 1,2,3 then turn off CT mode (unless non-linked) + if ((XdrvMailbox.index <= 3) && (light_controller.isCTRGBLinked())) { + Light.current_color[3] = Light.current_color[4] = 0; + } } - light_controller.changeChannels(light_current_color); + light_controller.changeChannels(Light.current_color); coldim = true; } - Response_P(S_JSON_COMMAND_INDEX_NVALUE, command, XdrvMailbox.index, light_current_color[XdrvMailbox.index -1] * 100 / 255); + ResponseCmndIdxNumber(changeUIntScale(Light.current_color[XdrvMailbox.index -1],0,255,0,100)); + if (coldim) { + LightPreparePower(); + } } - else if ((CMND_HSBCOLOR == command_code) && (light_subtype >= LST_RGB)) { +} + +void CmndHsbColor(void) +{ + if (Light.subtype >= LST_RGB) { bool validHSB = (XdrvMailbox.data_len > 0); if (validHSB) { uint16_t HSB[3]; @@ -2109,205 +2000,203 @@ bool LightCommand(void) LightState(0); } } -#ifdef USE_WS2812 // *********************************************************************** - else if ((CMND_LED == command_code) && (LT_WS2812 == light_type) && (XdrvMailbox.index > 0) && (XdrvMailbox.index <= Settings.light_pixels)) { - if (XdrvMailbox.data_len > 0) { - char *p; - uint16_t idx = XdrvMailbox.index; - Ws2812ForceSuspend(); - for (char *color = strtok_r(XdrvMailbox.data, " ", &p); color; color = strtok_r(nullptr, " ", &p)) { - if (LightColorEntry(color, strlen(color))) { - Ws2812SetColor(idx, light_entry_color[0], light_entry_color[1], light_entry_color[2], light_entry_color[3]); - idx++; - if (idx > Settings.light_pixels) break; - } else { - break; - } - } +} - Ws2812ForceUpdate(); - } - Response_P(S_JSON_COMMAND_INDEX_SVALUE, command, XdrvMailbox.index, Ws2812GetColor(XdrvMailbox.index, scolor)); - } - else if ((CMND_PIXELS == command_code) && (LT_WS2812 == light_type)) { - if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= WS2812_MAX_LEDS)) { - Settings.light_pixels = XdrvMailbox.payload; - Settings.light_rotation = 0; - Ws2812Clear(); - light_update = 1; - } - Response_P(S_JSON_COMMAND_NVALUE, command, Settings.light_pixels); - } - else if ((CMND_ROTATION == command_code) && (LT_WS2812 == light_type)) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < Settings.light_pixels)) { - Settings.light_rotation = XdrvMailbox.payload; - } - Response_P(S_JSON_COMMAND_NVALUE, command, Settings.light_rotation); - } - else if ((CMND_WIDTH == command_code) && (LT_WS2812 == light_type) && (XdrvMailbox.index > 0) && (XdrvMailbox.index <= 4)) { - if (1 == XdrvMailbox.index) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 4)) { - Settings.light_width = XdrvMailbox.payload; +void CmndScheme(void) +{ + if (Light.subtype >= LST_RGB) { + uint32_t max_scheme = Light.max_scheme; + + if (1 == XdrvMailbox.data_len) { + if (('+' == XdrvMailbox.data[0]) && (Settings.light_scheme < max_scheme)) { + XdrvMailbox.payload = Settings.light_scheme + ((0 == Settings.light_scheme) ? 2 : 1); // Skip wakeup } - Response_P(S_JSON_COMMAND_NVALUE, command, Settings.light_width); - } else { - if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 32)) { - Settings.ws_width[XdrvMailbox.index -2] = XdrvMailbox.payload; + else if (('-' == XdrvMailbox.data[0]) && (Settings.light_scheme > 0)) { + XdrvMailbox.payload = Settings.light_scheme - ((2 == Settings.light_scheme) ? 2 : 1); // Skip wakeup } - Response_P(S_JSON_COMMAND_INDEX_NVALUE, command, XdrvMailbox.index, Settings.ws_width[XdrvMailbox.index -2]); - } - } -#endif // USE_WS2812 ************************************************************************ - else if ((CMND_SCHEME == command_code) && (light_subtype >= LST_RGB)) { - uint8_t max_scheme = (LT_WS2812 == light_type) ? LS_MAX + WS2812_SCHEMES : LS_MAX -1; - if (('+' == option) && (Settings.light_scheme < max_scheme)) { - XdrvMailbox.payload = Settings.light_scheme + ((0 == Settings.light_scheme) ? 2 : 1); // Skip wakeup - } - else if (('-' == option) && (Settings.light_scheme > 0)) { - XdrvMailbox.payload = Settings.light_scheme - ((2 == Settings.light_scheme) ? 2 : 1); // Skip wakeup } if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= max_scheme)) { Settings.light_scheme = XdrvMailbox.payload; if (LS_WAKEUP == Settings.light_scheme) { - light_wakeup_active = 3; + Light.wakeup_active = 3; } LightPowerOn(); - strip_timer_counter = 0; + Light.strip_timer_counter = 0; // Publish state message for Hass if (Settings.flag3.hass_tele_on_power) { MqttPublishTeleState(); } } - Response_P(S_JSON_COMMAND_NVALUE, command, Settings.light_scheme); + ResponseCmndNumber(Settings.light_scheme); } - else if (CMND_WAKEUP == command_code) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) { - Settings.light_dimmer = XdrvMailbox.payload; - } - light_wakeup_active = 3; - Settings.light_scheme = LS_WAKEUP; - LightPowerOn(); - Response_P(S_JSON_COMMAND_SVALUE, command, D_JSON_STARTED); +} + +void CmndWakeup(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) { + Settings.light_dimmer = XdrvMailbox.payload; } - else if ((CMND_COLORTEMPERATURE == command_code) && ((LST_COLDWARM == light_subtype) || (LST_RGBWC == light_subtype))) { // ColorTemp - uint16_t ct = light_state.getCT(); - if (option != '\0') { - if ('+' == option) { + Light.wakeup_active = 3; + Settings.light_scheme = LS_WAKEUP; + LightPowerOn(); + ResponseCmndChar(D_JSON_STARTED); +} + +void CmndColorTemperature(void) +{ + if ((LST_COLDWARM == Light.subtype) || (LST_RGBWC == Light.subtype)) { // ColorTemp + uint32_t ct = light_state.getCT(); + if (1 == XdrvMailbox.data_len) { + if ('+' == XdrvMailbox.data[0]) { XdrvMailbox.payload = (ct > (500-34)) ? 500 : ct + 34; } - else if ('-' == option) { + else if ('-' == XdrvMailbox.data[0]) { XdrvMailbox.payload = (ct < (153+34)) ? 153 : ct - 34; } } if ((XdrvMailbox.payload >= 153) && (XdrvMailbox.payload <= 500)) { // https://developers.meethue.com/documentation/core-concepts light_controller.changeCTB(XdrvMailbox.payload, light_state.getBri()); - coldim = true; + LightPreparePower(); } else { - Response_P(S_JSON_COMMAND_NVALUE, command, ct); + ResponseCmndNumber(ct); } } - else if (CMND_DIMMER == command_code) { - uint32_t dimmer = light_state.getDimmer(); - if ('+' == option) { +} + +void CmndDimmer(void) +{ + uint32_t dimmer = light_state.getDimmer(); + if (1 == XdrvMailbox.data_len) { + if ('+' == XdrvMailbox.data[0]) { XdrvMailbox.payload = (dimmer > 89) ? 100 : dimmer + 10; } - else if ('-' == option) { + else if ('-' == XdrvMailbox.data[0]) { XdrvMailbox.payload = (dimmer < 11) ? 1 : dimmer - 10; } - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) { - light_controller.changeDimmer(XdrvMailbox.payload); - light_update = 1; - coldim = true; - } else { - Response_P(S_JSON_COMMAND_NVALUE, command, Settings.light_dimmer); - } } - else if (CMND_LEDTABLE == command_code) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 2)) { - switch (XdrvMailbox.payload) { - case 0: // Off - case 1: // On - Settings.light_correction = XdrvMailbox.payload; - break; - case 2: // Toggle - Settings.light_correction ^= 1; - break; - } - light_update = 1; - } - Response_P(S_JSON_COMMAND_SVALUE, command, GetStateText(Settings.light_correction)); + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) { + light_controller.changeDimmer(XdrvMailbox.payload); + Light.update = true; + LightPreparePower(); + } else { + ResponseCmndNumber(Settings.light_dimmer); } - else if (CMND_RGBWWTABLE == command_code) { - bool validtable = (XdrvMailbox.data_len > 0); - char scolor[25]; - if (validtable) { - if (strstr(XdrvMailbox.data, ",") != nullptr) { // Command with up to 5 comma separated parameters - for (uint32_t i = 0; i < LST_RGBWC; i++) { - char *substr; +} - if (0 == i) { - substr = strtok(XdrvMailbox.data, ","); - } else { - substr = strtok(nullptr, ","); - } - if (substr != nullptr) { - Settings.rgbwwTable[i] = atoi(substr); - } - } - } - light_update = 1; +void CmndDimmerRange(void) +{ + if (XdrvMailbox.data_len > 0) { + char *p; + uint8_t i = 0; + uint16_t parm[2]; + parm[0] = Settings.dimmer_hw_min; + parm[1] = Settings.dimmer_hw_max; + for (char *str = strtok_r(XdrvMailbox.data, ", ", &p); str && i < 2; str = strtok_r(nullptr, ", ", &p)) { + parm[i] = strtoul(str, nullptr, 0); + i++; } - scolor[0] = '\0'; - for (uint32_t i = 0; i < LST_RGBWC; i++) { - snprintf_P(scolor, 25, PSTR("%s%s%d"), scolor, (i > 0) ? "," : "", Settings.rgbwwTable[i]); + + if (parm[0] < parm[1]) { + Settings.dimmer_hw_min = parm[0]; + Settings.dimmer_hw_max = parm[1]; + } else { + Settings.dimmer_hw_min = parm[1]; + Settings.dimmer_hw_max = parm[0]; } - Response_P(S_JSON_COMMAND_INDEX_SVALUE, command, XdrvMailbox.index, scolor); + restart_flag = 2; } - else if (CMND_FADE == command_code) { + Response_P(PSTR("{\"" D_CMND_DIMMER_RANGE "\":{\"Min\":%d,\"Max\":%d}}"), Settings.dimmer_hw_min, Settings.dimmer_hw_max); +} + +void CmndLedTable(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 2)) { switch (XdrvMailbox.payload) { case 0: // Off case 1: // On - Settings.light_fade = XdrvMailbox.payload; + Settings.light_correction = XdrvMailbox.payload; break; case 2: // Toggle - Settings.light_fade ^= 1; + Settings.light_correction ^= 1; break; } - Response_P(S_JSON_COMMAND_SVALUE, command, GetStateText(Settings.light_fade)); + Light.update = true; } - else if (CMND_SPEED == command_code) { // 1 - fast, 20 - very slow - if (('+' == option) && (Settings.light_speed > 1)) { + ResponseCmndStateText(Settings.light_correction); +} + +void CmndRgbwwTable(void) +{ + if ((XdrvMailbox.data_len > 0)) { + if (strstr(XdrvMailbox.data, ",") != nullptr) { // Command with up to 5 comma separated parameters + for (uint32_t i = 0; i < LST_RGBWC; i++) { + char *substr; + + if (0 == i) { + substr = strtok(XdrvMailbox.data, ","); + } else { + substr = strtok(nullptr, ","); + } + if (substr != nullptr) { + Settings.rgbwwTable[i] = atoi(substr); + } + } + } + Light.update = true; + } + char scolor[LIGHT_COLOR_SIZE]; + scolor[0] = '\0'; + for (uint32_t i = 0; i < LST_RGBWC; i++) { + snprintf_P(scolor, sizeof(scolor), PSTR("%s%s%d"), scolor, (i > 0) ? "," : "", Settings.rgbwwTable[i]); + } + ResponseCmndIdxChar(scolor); +} + +void CmndFade(void) +{ + switch (XdrvMailbox.payload) { + case 0: // Off + case 1: // On + Settings.light_fade = XdrvMailbox.payload; + break; + case 2: // Toggle + Settings.light_fade ^= 1; + break; + } + ResponseCmndStateText(Settings.light_fade); +} + +void CmndSpeed(void) +{ // 1 - fast, 20 - very slow + if (1 == XdrvMailbox.data_len) { + if (('+' == XdrvMailbox.data[0]) && (Settings.light_speed > 1)) { XdrvMailbox.payload = Settings.light_speed -1; } - else if (('-' == option) && (Settings.light_speed < STATES)) { + else if (('-' == XdrvMailbox.data[0]) && (Settings.light_speed < STATES)) { XdrvMailbox.payload = Settings.light_speed +1; } - if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= STATES)) { - Settings.light_speed = XdrvMailbox.payload; - } - Response_P(S_JSON_COMMAND_NVALUE, command, Settings.light_speed); } - else if (CMND_WAKEUPDURATION == command_code) { - if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 3001)) { - Settings.light_wakeup = XdrvMailbox.payload; - light_wakeup_active = 0; - } - Response_P(S_JSON_COMMAND_NVALUE, command, Settings.light_wakeup); - } - else if (CMND_UNDOCA == command_code) { // Theos legacy status - LightGetColor(scolor, true); // force hex whatever Option 17 - scolor[6] = '\0'; // RGB only - Response_P(PSTR("%s,%d,%d,%d,%d,%d"), scolor, Settings.light_fade, Settings.light_correction, Settings.light_scheme, Settings.light_speed, Settings.light_width); - MqttPublishPrefixTopic_P(STAT, XdrvMailbox.topic); - mqtt_data[0] = '\0'; - } - else { - serviced = false; // Unknown command + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= STATES)) { + Settings.light_speed = XdrvMailbox.payload; } + ResponseCmndNumber(Settings.light_speed); +} - if (coldim) { - LightPreparePower(); +void CmndWakeupDuration(void) +{ + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 3001)) { + Settings.light_wakeup = XdrvMailbox.payload; + Light.wakeup_active = 0; } + ResponseCmndNumber(Settings.light_wakeup); +} - return serviced; +void CmndUndocA(void) +{ // Theos legacy status + char scolor[LIGHT_COLOR_SIZE]; + LightGetColor(scolor, true); // force hex whatever Option 17 + scolor[6] = '\0'; // RGB only + Response_P(PSTR("%s,%d,%d,%d,%d,%d"), scolor, Settings.light_fade, Settings.light_correction, Settings.light_scheme, Settings.light_speed, Settings.light_width); + MqttPublishPrefixTopic_P(STAT, XdrvMailbox.topic); + mqtt_data[0] = '\0'; } /*********************************************************************************************\ @@ -2318,28 +2207,29 @@ bool Xdrv04(uint8_t function) { bool result = false; - if (light_type) { + if (FUNC_MODULE_INIT == function) { + return LightModuleInit(); + } + else if (light_type) { switch (function) { - case FUNC_PRE_INIT: - LightInit(); + case FUNC_SERIAL: + result = XlgtCall(FUNC_SERIAL); break; case FUNC_EVERY_50_MSECOND: LightAnimate(); -#ifdef USE_ARILUX_RF - if (pin[GPIO_ARIRFRCV] < 99) AriluxRfHandler(); -#endif // USE_ARILUX_RF - break; -#ifdef USE_ARILUX_RF - case FUNC_EVERY_SECOND: - if (10 == uptime) AriluxRfInit(); // Needs rest before enabling RF interrupts - break; -#endif // USE_ARILUX_RF - case FUNC_COMMAND: - result = LightCommand(); break; case FUNC_SET_POWER: LightSetPower(); break; + case FUNC_COMMAND: + result = DecodeCommand(kLightCommands, LightCommand); + if (!result) { + result = XlgtCall(FUNC_COMMAND); + } + break; + case FUNC_PRE_INIT: + LightInit(); + break; } } return result; diff --git a/sonoff/xdrv_05_irremote.ino b/sonoff/xdrv_05_irremote.ino index 4444b1da9..4dd71f24f 100644 --- a/sonoff/xdrv_05_irremote.ino +++ b/sonoff/xdrv_05_irremote.ino @@ -17,7 +17,7 @@ along with this program. If not, see . */ -#ifdef USE_IR_REMOTE +#if defined(USE_IR_REMOTE) && !defined(USE_IR_REMOTE_FULL) /*********************************************************************************************\ * IR Remote send and receive using IRremoteESP8266 library \*********************************************************************************************/ @@ -28,39 +28,32 @@ enum IrErrors { IE_NO_ERROR, IE_INVALID_RAWDATA, IE_INVALID_JSON, IE_SYNTAX_IRSEND, IE_SYNTAX_IRHVAC }; -enum IrRemoteCommands { CMND_IRSEND, CMND_IRHVAC }; -const char kIrRemoteCommands[] PROGMEM = D_CMND_IRSEND "|" D_CMND_IRHVAC ; +const char kIrRemoteCommands[] PROGMEM = "|" // No prefix +#ifdef USE_IR_HVAC + D_CMND_IRHVAC "|" +#endif + D_CMND_IRSEND ; + +void (* const IrRemoteCommand[])(void) PROGMEM = { +#ifdef USE_IR_HVAC + &CmndIrHvac, +#endif + &CmndIrSend }; // Based on IRremoteESP8266.h enum decode_type_t +static const uint8_t MAX_STANDARD_IR = SHARP; // this is the last code mapped to decode_type_t +enum IrVendors { IR_BASE = MAX_STANDARD_IR, +#ifdef USE_IR_SEND_PIONEER + IR_PIONEER, +#endif // USE_IR_SEND_PIONEER +}; const char kIrRemoteProtocols[] PROGMEM = - "UNKNOWN|RC5|RC6|NEC|SONY|PANASONIC|JVC|SAMSUNG|WHYNTER|AIWA_RC_T501|LG|SANYO|MITSUBISHI|DISH|SHARP"; - -#ifdef USE_IR_HVAC - -#include -#include - -enum IrHvacVendors { VNDR_TOSHIBA, VNDR_MITSUBISHI, VNDR_LG, VNDR_FUJITSU }; -const char kIrHvacVendors[] PROGMEM = "Toshiba|Mitsubishi|LG|Fujitsu" ; - -// HVAC TOSHIBA_ -const uint16_t HVAC_TOSHIBA_HDR_MARK = 4400; -const uint16_t HVAC_TOSHIBA_HDR_SPACE = 4300; -const uint16_t HVAC_TOSHIBA_BIT_MARK = 543; -const uint16_t HVAC_TOSHIBA_ONE_SPACE = 1623; -const uint16_t HVAC_MISTUBISHI_ZERO_SPACE = 472; -const uint16_t HVAC_TOSHIBA_RPT_MARK = 440; -const uint16_t HVAC_TOSHIBA_RPT_SPACE = 7048; // Above original iremote limit -const uint8_t HVAC_TOSHIBA_DATALEN = 9; - -// HVAC LG -const uint8_t HVAC_LG_DATALEN = 7; - -IRMitsubishiAC *mitsubir = nullptr; - -const char kFanSpeedOptions[] = "A12345S"; -const char kHvacModeOptions[] = "HDCA"; -#endif // USE_IR_HVAC + "UNKNOWN|RC5|RC6|NEC|SONY|PANASONIC|JVC|SAMSUNG|WHYNTER|AIWA_RC_T501|LG|SANYO|MITSUBISHI|DISH|SHARP" + // now allow for other codes beyond the first series; +#ifdef USE_IR_SEND_PIONEER + "|PIONEER" +#endif // USE_IR_SEND_PIONEER + ; /*********************************************************************************************\ * IR Send @@ -75,31 +68,6 @@ void IrSendInit(void) { irsend = new IRsend(pin[GPIO_IRSEND]); // an IR led is at GPIO_IRSEND irsend->begin(); - -#ifdef USE_IR_HVAC - mitsubir = new IRMitsubishiAC(pin[GPIO_IRSEND]); -#endif //USE_IR_HVAC -} - -char* IrUint64toHex(uint64_t value, char *str, uint16_t bits) -{ - ulltoa(value, str, 16); // Get 64bit value - - int fill = 8; - if ((bits > 3) && (bits < 65)) { - fill = bits / 4; // Max 16 - if (bits % 4) { fill++; } - } - int len = strlen(str); - fill -= len; - if (fill > 0) { - memmove(str + fill, str, len +1); - memset(str, '0', fill); - } - memmove(str + 2, str, strlen(str) +1); - str[0] = '0'; - str[1] = 'x'; - return str; } #ifdef USE_IR_RECEIVE @@ -118,8 +86,10 @@ unsigned long ir_lasttime = 0; void IrReceiveUpdateThreshold() { - if (Settings.param[P_IR_UNKNOW_THRESHOLD] < 6) { Settings.param[P_IR_UNKNOW_THRESHOLD] = 6; } - irrecv->setUnknownThreshold(Settings.param[P_IR_UNKNOW_THRESHOLD]); + if (irrecv != nullptr) { + if (Settings.param[P_IR_UNKNOW_THRESHOLD] < 6) { Settings.param[P_IR_UNKNOW_THRESHOLD] = 6; } + irrecv->setUnknownThreshold(Settings.param[P_IR_UNKNOW_THRESHOLD]); + } } void IrReceiveInit(void) @@ -140,10 +110,25 @@ void IrReceiveCheck(void) decode_results results; if (irrecv->decode(&results)) { - char hvalue[64]; - IrUint64toHex(results.value, hvalue, results.bits); // Get 64bit value as hex 0x00123456 + char hvalue[65]; // Max 256 bits - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_IRR "Echo %d, RawLen %d, Overflow %d, Bits %d, Value %s, Decode %d"), + iridx = results.decode_type; + if ((iridx < 0) || (iridx > 14)) { iridx = 0; } // UNKNOWN + + if (iridx) { + if (results.bits > 64) { + // This emulates IRutils resultToHexidecimal and may needs a larger IR_RCV_BUFFER_SIZE + uint32_t digits2 = results.bits / 8; + if (results.bits % 8) { digits2++; } + ToHex_P((unsigned char*)results.state, digits2, hvalue, sizeof(hvalue)); // Get n-bit value as hex 56341200 + } else { + Uint64toHex(results.value, hvalue, results.bits); // Get 64bit value as hex 00123456 + } + } else { + Uint64toHex(results.value, hvalue, 32); // UNKNOWN is always 32 bits hash + } + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_IRR "Echo %d, RawLen %d, Overflow %d, Bits %d, Value 0x%s, Decode %d"), irsend_active, results.rawlen, results.overflow, results.bits, hvalue, results.decode_type); unsigned long now = millis(); @@ -151,16 +136,19 @@ void IrReceiveCheck(void) if (!irsend_active && (now - ir_lasttime > IR_TIME_AVOID_DUPLICATE)) { ir_lasttime = now; - iridx = results.decode_type; - if ((iridx < 0) || (iridx > 14)) { iridx = 0; } // UNKNOWN char svalue[64]; if (Settings.flag.ir_receive_decimal) { ulltoa(results.value, svalue, 10); } else { - snprintf_P(svalue, sizeof(svalue), PSTR("\"%s\""), hvalue); + snprintf_P(svalue, sizeof(svalue), PSTR("\"0x%s\""), hvalue); + } + ResponseTime_P(PSTR(",\"" D_JSON_IRRECEIVED "\":{\"" D_JSON_IR_PROTOCOL "\":\"%s\",\"" D_JSON_IR_BITS "\":%d"), + GetTextIndexed(sirtype, sizeof(sirtype), iridx, kIrRemoteProtocols), results.bits); + if (iridx) { + ResponseAppend_P(PSTR(",\"" D_JSON_IR_DATA "\":%s"), svalue); + } else { + ResponseAppend_P(PSTR(",\"" D_JSON_IR_HASH "\":%s"), svalue); } - Response_P(PSTR("{\"" D_JSON_IRRECEIVED "\":{\"" D_JSON_IR_PROTOCOL "\":\"%s\",\"" D_JSON_IR_BITS "\":%d,\"" D_JSON_IR_DATA "\":%s"), - GetTextIndexed(sirtype, sizeof(sirtype), iridx, kIrRemoteProtocols), results.bits, svalue); if (Settings.flag3.receive_raw) { ResponseAppend_P(PSTR(",\"" D_JSON_IR_RAWDATA "\":[")); @@ -183,16 +171,16 @@ void IrReceiveCheck(void) ResponseAppend_P(PSTR("],\"" D_JSON_IR_RAWDATA "Info\":[%d,%d,%d]"), extended_length, i -1, results.overflow); } - ResponseAppend_P(PSTR("}}")); + ResponseJsonEndEnd(); MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_IRRECEIVED)); - if (iridx) { - XdrvRulesProcess(); + XdrvRulesProcess(); #ifdef USE_DOMOTICZ + if (iridx) { unsigned long value = results.value | (iridx << 28); // [Protocol:4, Data:28] DomoticzSensor(DZ_COUNT, value); // Send data as Domoticz Counter value -#endif // USE_DOMOTICZ } +#endif // USE_DOMOTICZ } irrecv->resume(); @@ -200,14 +188,31 @@ void IrReceiveCheck(void) } #endif // USE_IR_RECEIVE + #ifdef USE_IR_HVAC -/********************************************************************************************* \ - * IR Heating, Ventilation and Air Conditioning using IRMitsubishiAC library +/*********************************************************************************************\ + * IR Heating, Ventilation and Air Conditioning \*********************************************************************************************/ -/******************* - TOSHIBA -********************/ +enum IrHvacVendors { VNDR_TOSHIBA, VNDR_MITSUBISHI, VNDR_LG, VNDR_FUJITSU, VNDR_MIDEA }; +const char kIrHvacVendors[] PROGMEM = "Toshiba|Mitsubishi|LG|Fujitsu|Midea" ; + +const char kFanSpeedOptions[] = "A12345S"; +const char kHvacModeOptions[] = "HDCA"; + +#ifdef USE_IR_HVAC_TOSHIBA +/*-------------------------------------------------------------------------------------------*\ + * Toshiba +\*-------------------------------------------------------------------------------------------*/ + +const uint16_t HVAC_TOSHIBA_HDR_MARK = 4400; +const uint16_t HVAC_TOSHIBA_HDR_SPACE = 4300; +const uint16_t HVAC_TOSHIBA_BIT_MARK = 543; +const uint16_t HVAC_TOSHIBA_ONE_SPACE = 1623; +const uint16_t HVAC_MISTUBISHI_ZERO_SPACE = 472; +const uint16_t HVAC_TOSHIBA_RPT_MARK = 440; +const uint16_t HVAC_TOSHIBA_RPT_SPACE = 7048; // Above original iremote limit +const uint8_t HVAC_TOSHIBA_DATALEN = 9; uint8_t IrHvacToshiba(const char *HVAC_Mode, const char *HVAC_FanMode, bool HVAC_Power, int HVAC_Temp) { @@ -291,25 +296,156 @@ uint8_t IrHvacToshiba(const char *HVAC_Mode, const char *HVAC_FanMode, bool HVAC rawdata[i++] = HVAC_TOSHIBA_RPT_SPACE; // noInterrupts(); - irsend_active = true; irsend->sendRaw(rawdata, i, 38); irsend->sendRaw(rawdata, i, 38); // interrupts(); return IE_NO_ERROR; } +#endif // USE_IR_HVAC_TOSHIBA +#ifdef USE_IR_HVAC_MIDEA +/*-------------------------------------------------------------------------------------------*\ + * Midea / Komeco +\*-------------------------------------------------------------------------------------------*/ -/******************* - MITSUBISHI -********************/ +// http://veillard.com/embedded/midea.html +// https://github.com/sheinz/esp-midea-ir/blob/master/midea-ir.c + +const uint16_t HVAC_MIDEA_HDR_MARK = 4420; // 8T high +const uint16_t HVAC_MIDEA_HDR_SPACE = 4420; // 8T low +const uint16_t HVAC_MIDEA_BIT_MARK = 553; // 1T +const uint16_t HVAC_MIDEA_ONE_SPACE = 1660; // 3T low +const uint16_t HVAC_MIDEA_ZERO_SPACE = 553; // 1T high +const uint16_t HVAC_MIDEA_RPT_MARK = 553; // 1T +const uint16_t HVAC_MIDEA_RPT_SPACE = 5530; // 10T +const uint8_t HVAC_MIDEA_DATALEN = 3; + +uint8_t IrHvacMidea(const char *HVAC_Mode, const char *HVAC_FanMode, bool HVAC_Power, int HVAC_Temp) +{ + uint16_t rawdata[2 + 2 * 2 * 8 * HVAC_MIDEA_DATALEN + 2]; // START + 2* (2 * 3 BYTES) + STOP + uint8_t data[HVAC_MIDEA_DATALEN] = {0xB2, 0x00, 0x00}; + + char *p; + uint8_t mode; + + if (!HVAC_Power) { // Turn OFF HVAC + data[1] = 0x7B; + data[2] = 0xE0; + } else { + // FAN + if (HVAC_FanMode == nullptr) { + p = (char*)kFanSpeedOptions; // default auto + } + else { + p = (char*)HVAC_FanMode; + } + + switch(p[0]) { + case '1': data[1] = 0xBF; break; // off + case '2': data[1] = 0x9F; break; // low + case '3': data[1] = 0x5F; break; // med + case '4': data[1] = 0x3F; break; // high + case '5': data[1] = 0x1F; break; // auto + case 'A': data[1] = 0x1F; break; // auto + default: return IE_SYNTAX_IRHVAC; + } + + // TEMPERATURE + uint8_t Temp; + if (HVAC_Temp > 30) { + Temp = 30; + } + else if (HVAC_Temp < 17) { + Temp = 17; + } + else { + Temp = HVAC_Temp-17; + } + if (10 == Temp) { // Temp is encoded as gray code; except 27 and 28. Go figure... + data[2] = 0x90; + } else if (11 == Temp) { + data[2] = 0x80; + } else { + Temp = (Temp >> 1) ^Temp; + data[2] = (Temp << 4); + } + + // MODE + if (HVAC_Mode == nullptr) { + p = (char*)kHvacModeOptions + 3; // default to auto + } + else { + p = (char*)HVAC_Mode; + } + switch(toupper(p[0])) { + case 'D': data[2] = 0xE4; break; // for fan Temp must be 0XE + case 'C': data[2] = 0x0 | data[2]; break; + case 'A': data[2] = 0x8 | data[2]; data[1] = 0x1F; break; // for auto Fan must be 0x1 + case 'H': data[2] = 0xC | data[2]; break; + default: return IE_SYNTAX_IRHVAC; + } + } + + int i = 0; + uint8_t mask = 1; + + //header + rawdata[i++] = HVAC_MIDEA_HDR_MARK; + rawdata[i++] = HVAC_MIDEA_HDR_SPACE; + + //data + for (int b = 0; b < HVAC_MIDEA_DATALEN; b++) { // Send value + for (mask = B10000000; mask > 0; mask >>= 1) { + if (data[b] & mask) { // Bit ONE + rawdata[i++] = HVAC_MIDEA_BIT_MARK; + rawdata[i++] = HVAC_MIDEA_ONE_SPACE; + } + else { // Bit ZERO + rawdata[i++] = HVAC_MIDEA_BIT_MARK; + rawdata[i++] = HVAC_MIDEA_ZERO_SPACE; + } + } + for (mask = B10000000; mask > 0; mask >>= 1) { // Send complement + if (data[b] & mask) { // Bit ONE + rawdata[i++] = HVAC_MIDEA_BIT_MARK; + rawdata[i++] = HVAC_MIDEA_ZERO_SPACE; + } + else { // Bit ZERO + rawdata[i++] = HVAC_MIDEA_BIT_MARK; + rawdata[i++] = HVAC_MIDEA_ONE_SPACE; + } + } + + } + + //trailer + rawdata[i++] = HVAC_MIDEA_RPT_MARK; + rawdata[i++] = HVAC_MIDEA_RPT_SPACE; + + // this takes ~180 ms : + irsend->sendRaw(rawdata, i, 38); + irsend->sendRaw(rawdata, i, 38); + + return IE_NO_ERROR; +} +#endif // USE_IR_HVAC_MIDEA + +#ifdef USE_IR_HVAC_MITSUBISHI +/*-------------------------------------------------------------------------------------------*\ + * Mitsubishi +\*-------------------------------------------------------------------------------------------*/ + +#include uint8_t IrHvacMitsubishi(const char *HVAC_Mode, const char *HVAC_FanMode, bool HVAC_Power, int HVAC_Temp) { char *p; uint8_t mode; - mitsubir->stateReset(); + IRMitsubishiAC mitsubir(pin[GPIO_IRSEND]); + + mitsubir.stateReset(); if (HVAC_Mode == nullptr) { p = (char *)kHvacModeOptions; // default HVAC_HOT @@ -321,9 +457,9 @@ uint8_t IrHvacMitsubishi(const char *HVAC_Mode, const char *HVAC_FanMode, bool H return IE_SYNTAX_IRHVAC; } mode = (p - kHvacModeOptions + 1) << 3; // HOT = 0x08, DRY = 0x10, COOL = 0x18, AUTO = 0x20 - mitsubir->setMode(mode); + mitsubir.setMode(mode); - mitsubir->setPower(HVAC_Power); + mitsubir.setPower(HVAC_Power); if (HVAC_FanMode == nullptr) { p = (char *)kFanSpeedOptions; // default FAN_SPEED_AUTO @@ -335,22 +471,25 @@ uint8_t IrHvacMitsubishi(const char *HVAC_Mode, const char *HVAC_FanMode, bool H return IE_SYNTAX_IRHVAC; } mode = p - kFanSpeedOptions; // AUTO = 0, SPEED = 1 .. 5, SILENT = 6 - mitsubir->setFan(mode); + mitsubir.setFan(mode); - mitsubir->setTemp(HVAC_Temp); - mitsubir->setVane(MITSUBISHI_AC_VANE_AUTO); - mitsubir->send(); + mitsubir.setTemp(HVAC_Temp); + mitsubir.setVane(MITSUBISHI_AC_VANE_AUTO); + mitsubir.send(); // AddLog_P2(LOG_LEVEL_DEBUG, PSTR("IRHVAC: Mitsubishi Power %d, Mode %d, FanSpeed %d, Temp %d, VaneMode %d"), // mitsubir->getPower(), mitsubir->getMode(), mitsubir->getFan(), mitsubir->getTemp(), mitsubir->getVane()); return IE_NO_ERROR; } +#endif // USE_IR_HVAC_MITSUBISHI +#ifdef USE_IR_HVAC_LG +/*-------------------------------------------------------------------------------------------*\ + * LG +\*-------------------------------------------------------------------------------------------*/ -/******************* - LG -********************/ +const uint8_t HVAC_LG_DATALEN = 7; uint8_t IrHvacLG(const char *HVAC_Mode, const char *HVAC_FanMode, bool HVAC_Power, int HVAC_Temp) { @@ -455,18 +594,18 @@ uint8_t IrHvacLG(const char *HVAC_Mode, const char *HVAC_FanMode, bool HVAC_Powe // AddLog_P2(LOG_LEVEL_DEBUG, PSTR("IRHVAC: LG_Code %d"), LG_Code); // Send LG IR Code -// noInterrupts(); - irsend_active = true; irsend->sendLG(LG_Code, 28); -// interrupts(); return IE_NO_ERROR; } +#endif // USE_IR_HVAC_LG +#ifdef USE_IR_HVAC_FUJITSU +/*-------------------------------------------------------------------------------------------*\ + * Fujitsu +\*-------------------------------------------------------------------------------------------*/ -/******************* - Fujitsu -********************/ +#include uint8_t IrHvacFujitsu(const char *HVAC_Mode, const char *HVAC_FanMode, bool HVAC_Power, int HVAC_Temp) { @@ -476,8 +615,6 @@ uint8_t IrHvacFujitsu(const char *HVAC_Mode, const char *HVAC_FanMode, bool HVAC IRFujitsuAC ac(pin[GPIO_IRSEND]); - irsend_active = true; - if (0 == HVAC_Power) { ac.off(); ac.send(); @@ -517,275 +654,367 @@ uint8_t IrHvacFujitsu(const char *HVAC_Mode, const char *HVAC_FanMode, bool HVAC return IE_NO_ERROR; } +#endif // USE_IR_HVAC_FUJITSU -#endif // USE_IR_HVAC +/*-------------------------------------------------------------------------------------------*/ + +uint32_t IrRemoteCmndIrHvacJson(void) +{ + // IrHvac { "Vendor": "", "Power": <0|1>, "Mode": "", "FanSpeed": "<1|2|3|4|5|Auto|Silence>", "Temp": <17..30> } + const char *HVAC_Mode; + const char *HVAC_FanMode; + const char *HVAC_Vendor; + char parm_uc[12]; + int HVAC_Temp = 21; + bool HVAC_Power = true; + + char dataBufUc[XdrvMailbox.data_len]; + UpperCase(dataBufUc, XdrvMailbox.data); + RemoveSpace(dataBufUc); + if (strlen(dataBufUc) < 8) { + return IE_INVALID_JSON; + } + + StaticJsonBuffer<164> jsonBufer; + JsonObject &root = jsonBufer.parseObject(dataBufUc); + if (!root.success()) { + return IE_INVALID_JSON; + } + + HVAC_Vendor = root[UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_VENDOR))]; + HVAC_Power = root[UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_POWER))]; + HVAC_Mode = root[UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_MODE))]; + HVAC_FanMode = root[UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_FANSPEED))]; + HVAC_Temp = root[UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_TEMP))]; + +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("IRHVAC: Received Vendor %s, Power %d, Mode %s, FanSpeed %s, Temp %d"), HVAC_Vendor, HVAC_Power, HVAC_Mode, HVAC_FanMode, HVAC_Temp); + + char vendor[20]; + int vendor_code = GetCommandCode(vendor, sizeof(vendor), HVAC_Vendor, kIrHvacVendors); + irsend_active = true; + switch (vendor_code) { +#ifdef USE_IR_HVAC_TOSHIBA + case VNDR_TOSHIBA: + return IrHvacToshiba(HVAC_Mode, HVAC_FanMode, HVAC_Power, HVAC_Temp); +#endif +#ifdef USE_IR_HVAC_MITSUBISHI + case VNDR_MITSUBISHI: + return IrHvacMitsubishi(HVAC_Mode, HVAC_FanMode, HVAC_Power, HVAC_Temp); +#endif +#ifdef USE_IR_HVAC_LG + case VNDR_LG: + return IrHvacLG(HVAC_Mode, HVAC_FanMode, HVAC_Power, HVAC_Temp); +#endif +#ifdef USE_IR_HVAC_FUJITSU + case VNDR_FUJITSU: + return IrHvacFujitsu(HVAC_Mode, HVAC_FanMode, HVAC_Power, HVAC_Temp); +#endif +#ifdef USE_IR_HVAC_MIDEA + case VNDR_MIDEA: + return IrHvacMidea(HVAC_Mode, HVAC_FanMode, HVAC_Power, HVAC_Temp); +#endif + default: + irsend_active = false; + } + + return IE_SYNTAX_IRHVAC; +} + +void CmndIrHvac(void) +{ + uint8_t error = IE_SYNTAX_IRHVAC; + + if (XdrvMailbox.data_len) { + error = IrRemoteCmndIrHvacJson(); + } + IrRemoteCmndResponse(error); +} + +#endif // USE_IR_HVAC /*********************************************************************************************\ * Commands \*********************************************************************************************/ -/* - * ArduinoJSON entry used to calculate jsonBuf: JSON_OBJECT_SIZE(3) + 40 = 96 - IRsend: - { "protocol": "RC5", "bits": 12, "data":"0xC86" } - { "protocol": "SAMSUNG", "bits": 32, "data": 551502015 } - IRhvac: - { "Vendor": "", "Power": <0|1>, "Mode": "", "FanSpeed": "<1|2|3|4|5|Auto|Silence>", "Temp": <17..30> } -*/ - -bool IrSendCommand(void) +uint32_t IrRemoteCmndIrSendRaw(void) { - char command [CMDSZ]; - bool serviced = true; - uint8_t error = IE_NO_ERROR; + // IRsend ,, ... + // or + // IRsend raw,,, (one space = zero space *2) + // IRsend raw,,,, + // IRsend raw,,,, + // IRsend raw,,
,
,,,, - int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic, kIrRemoteCommands); - if (-1 == command_code) { - serviced = false; // Unknown command + char *p; + char *str = strtok_r(XdrvMailbox.data, ", ", &p); + if (p == nullptr) { + return IE_INVALID_RAWDATA; } - else if (CMND_IRSEND == command_code) { - if (XdrvMailbox.data_len) { - Response_P(S_JSON_COMMAND_SVALUE, command, D_JSON_DONE); - if (strstr(XdrvMailbox.data, "{") == nullptr) { // If no JSON it must be rawdata - // IRsend ,, ... - // or - // IRsend raw,,, (one space = zero space *2) - // IRsend raw,,,, - // IRsend raw,,,, - // IRsend raw,,
,
,,,, - char *p; - char *str = strtok_r(XdrvMailbox.data, ", ", &p); - if (p == nullptr) { - error = IE_INVALID_RAWDATA; + // repeat + uint16_t repeat = XdrvMailbox.index > 0 ? XdrvMailbox.index - 1 : 0; + + uint16_t freq = atoi(str); + if (!freq && (*str != '0')) { // First parameter is any string + uint16_t count = 0; + char *q = p; + for (; *q; count += (*q++ == ',')); + if (count < 2) { + return IE_INVALID_RAWDATA; + } // Parameters must be at least 3 + + uint16_t parm[count]; + for (uint32_t i = 0; i < count; i++) { + parm[i] = strtol(strtok_r(nullptr, ", ", &p), nullptr, 0); + if (!parm[i]) { + if (!i) { + parm[0] = 38000; // Frequency default to 38kHz } else { - uint16_t freq = atoi(str); - if (!freq && (*str != '0')) { // First parameter is any string - uint16_t count = 0; - char *q = p; - for (; *q; count += (*q++ == ',')); - if (count < 2) { // Parameters must be at least 3 - error = IE_INVALID_RAWDATA; - } else { - uint16_t parm[count]; - for (uint32_t i = 0; i < count; i++) { - parm[i] = strtol(strtok_r(nullptr, ", ", &p), nullptr, 0); - if (!parm[i]) { - if (!i) { - parm[0] = 38000; // Frequency default to 38kHz - } else { - error = IE_INVALID_RAWDATA; // Other parameters may not be 0 - break; - } - } - } - if (IE_NO_ERROR == error) { - uint16_t i = 0; - if (count < 4) { - // IRsend raw,0,889,000000100110000001001 - uint16_t mark = parm[1] *2; // Protocol where 0 = t, 1 = 2t (RC5) - if (3 == count) { - if (parm[2] < parm[1]) { - // IRsend raw,0,889,2,000000100110000001001 - mark = parm[1] * parm[2]; // Protocol where 0 = t1, 1 = t1*t2 (Could be RC5) - } else { - // IRsend raw,0,889,1778,000000100110000001001 - mark = parm[2]; // Protocol where 0 = t1, 1 = t2 (Could be RC5) - } - } - uint16_t raw_array[strlen(p)]; // Bits - for (; *p; *p++) { - if (*p == '0') { - raw_array[i++] = parm[1]; // Space - } - else if (*p == '1') { - raw_array[i++] = mark; // Mark - } - } - irsend_active = true; - irsend->sendRaw(raw_array, i, parm[0]); - } - else if (6 == count) { // NEC Protocol - // IRsend raw,0,8620,4260,544,411,1496,010101101000111011001110000000001100110000000001100000000000000010001100 - uint16_t raw_array[strlen(p)*2+3]; // Header + bits + end - raw_array[i++] = parm[1]; // Header mark - raw_array[i++] = parm[2]; // Header space - for (; *p; *p++) { - if (*p == '0') { - raw_array[i++] = parm[3]; // Bit mark - raw_array[i++] = parm[4]; // Zero space - } - else if (*p == '1') { - raw_array[i++] = parm[3]; // Bit mark - raw_array[i++] = parm[5]; // One space - } - } - raw_array[i++] = parm[3]; // Trailing mark - irsend_active = true; - irsend->sendRaw(raw_array, i, parm[0]); - } - else { - error = IE_INVALID_RAWDATA; // Invalid number of parameters - } - } - } - } else { - if (!freq) { freq = 38000; } // Default to 38kHz - uint16_t count = 0; - char *q = p; - for (; *q; count += (*q++ == ',')); - if (0 == count) { - error = IE_INVALID_RAWDATA; - } else { // At least two raw data values - // IRsend 0,896,876,900,888,894,876,1790,874,872,1810,1736,948,872,880,872,936,872,1792,900,888,1734 - count++; - if (count < 200) { - uint16_t raw_array[count]; // It's safe to use stack for up to 200 packets (limited by mqtt_data length) - for (uint32_t i = 0; i < count; i++) { - raw_array[i] = strtol(strtok_r(nullptr, ", ", &p), nullptr, 0); // Allow decimal (20496) and hexadecimal (0x5010) input - } - -// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DBG: stack count %d"), count); - - irsend_active = true; - irsend->sendRaw(raw_array, count, freq); - } else { - uint16_t *raw_array = reinterpret_cast(malloc(count * sizeof(uint16_t))); - if (raw_array == nullptr) { - error = IE_INVALID_RAWDATA; - } else { - for (uint32_t i = 0; i < count; i++) { - raw_array[i] = strtol(strtok_r(nullptr, ", ", &p), nullptr, 0); // Allow decimal (20496) and hexadecimal (0x5010) input - } - -// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DBG: heap count %d"), count); - - irsend_active = true; - irsend->sendRaw(raw_array, count, freq); - free(raw_array); - } - } - } - } - } - } else { - char dataBufUc[XdrvMailbox.data_len]; - UpperCase(dataBufUc, XdrvMailbox.data); - RemoveSpace(dataBufUc); - if (strlen(dataBufUc) < 8) { - error = IE_INVALID_JSON; - } else { - StaticJsonBuffer<128> jsonBuf; - JsonObject &root = jsonBuf.parseObject(dataBufUc); - if (!root.success()) { - error = IE_INVALID_JSON; - } else { - // IRsend { "protocol": "SAMSUNG", "bits": 32, "data": 551502015 } - char parm_uc[10]; - const char *protocol = root[UpperCase_P(parm_uc, PSTR(D_JSON_IR_PROTOCOL))]; - uint16_t bits = root[UpperCase_P(parm_uc, PSTR(D_JSON_IR_BITS))]; - uint64_t data = strtoull(root[UpperCase_P(parm_uc, PSTR(D_JSON_IR_DATA))], nullptr, 0); - if (protocol && bits) { - char protocol_text[20]; - int protocol_code = GetCommandCode(protocol_text, sizeof(protocol_text), protocol, kIrRemoteProtocols); - - char dvalue[64]; - char hvalue[64]; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("IRS: protocol_text %s, protocol %s, bits %d, data %s (%s), protocol_code %d"), - protocol_text, protocol, bits, ulltoa(data, dvalue, 10), IrUint64toHex(data, hvalue, bits), protocol_code); - - irsend_active = true; - switch (protocol_code) { - case NEC: - irsend->sendNEC(data, (bits > NEC_BITS) ? NEC_BITS : bits); break; - case SONY: - irsend->sendSony(data, (bits > SONY_20_BITS) ? SONY_20_BITS : bits, 2); break; - case RC5: - irsend->sendRC5(data, bits); break; - case RC6: - irsend->sendRC6(data, bits); break; - case DISH: - irsend->sendDISH(data, (bits > DISH_BITS) ? DISH_BITS : bits); break; - case JVC: - irsend->sendJVC(data, (bits > JVC_BITS) ? JVC_BITS : bits, 1); break; - case SAMSUNG: - irsend->sendSAMSUNG(data, (bits > SAMSUNG_BITS) ? SAMSUNG_BITS : bits); break; - case PANASONIC: -// irsend->sendPanasonic(bits, data); break; - irsend->sendPanasonic64(data, bits); break; - default: - irsend_active = false; - Response_P(S_JSON_COMMAND_SVALUE, command, D_JSON_PROTOCOL_NOT_SUPPORTED); - } - } else { - error = IE_SYNTAX_IRSEND; - } - } + return IE_INVALID_RAWDATA; // Other parameters may not be 0 } } - } else { - error = IE_SYNTAX_IRSEND; } - } -#ifdef USE_IR_HVAC - else if (CMND_IRHVAC == command_code) { - const char *HVAC_Mode; - const char *HVAC_FanMode; - const char *HVAC_Vendor; - int HVAC_Temp = 21; - bool HVAC_Power = true; - if (XdrvMailbox.data_len) { - char dataBufUc[XdrvMailbox.data_len]; - UpperCase(dataBufUc, XdrvMailbox.data); - RemoveSpace(dataBufUc); - if (strlen(dataBufUc) < 8) { - error = IE_INVALID_JSON; - } else { - StaticJsonBuffer<164> jsonBufer; - JsonObject &root = jsonBufer.parseObject(dataBufUc); - if (!root.success()) { - error = IE_INVALID_JSON; + uint16_t i = 0; + if (count < 4) { + // IRsend raw,0,889,000000100110000001001 + uint16_t mark = parm[1] *2; // Protocol where 0 = t, 1 = 2t (RC5) + if (3 == count) { + if (parm[2] < parm[1]) { + // IRsend raw,0,889,2,000000100110000001001 + mark = parm[1] * parm[2]; // Protocol where 0 = t1, 1 = t1*t2 (Could be RC5) } else { - Response_P(S_JSON_COMMAND_SVALUE, command, D_JSON_DONE); - HVAC_Vendor = root[D_JSON_IRHVAC_VENDOR]; - HVAC_Power = root[D_JSON_IRHVAC_POWER]; - HVAC_Mode = root[D_JSON_IRHVAC_MODE]; - HVAC_FanMode = root[D_JSON_IRHVAC_FANSPEED]; - HVAC_Temp = root[D_JSON_IRHVAC_TEMP]; - - // AddLog_P2(LOG_LEVEL_DEBUG, PSTR("IRHVAC: Received Vendor %s, Power %d, Mode %s, FanSpeed %s, Temp %d"), HVAC_Vendor, HVAC_Power, HVAC_Mode, HVAC_FanMode, HVAC_Temp); - - char vendor[20]; - int vendor_code = GetCommandCode(vendor, sizeof(vendor), HVAC_Vendor, kIrHvacVendors); - switch (vendor_code) { - case VNDR_TOSHIBA: - error = IrHvacToshiba(HVAC_Mode, HVAC_FanMode, HVAC_Power, HVAC_Temp); break; - case VNDR_MITSUBISHI: - error = IrHvacMitsubishi(HVAC_Mode, HVAC_FanMode, HVAC_Power, HVAC_Temp); break; - case VNDR_LG: - error = IrHvacLG(HVAC_Mode, HVAC_FanMode, HVAC_Power, HVAC_Temp); break; - case VNDR_FUJITSU: - error = IrHvacFujitsu(HVAC_Mode, HVAC_FanMode, HVAC_Power, HVAC_Temp); break; - default: - error = IE_SYNTAX_IRHVAC; - } + // IRsend raw,0,889,1778,000000100110000001001 + mark = parm[2]; // Protocol where 0 = t1, 1 = t2 (Could be RC5) + } + } + uint16_t raw_array[strlen(p)]; // Bits + for (; *p; *p++) { + if (*p == '0') { + raw_array[i++] = parm[1]; // Space + } + else if (*p == '1') { + raw_array[i++] = mark; // Mark + } + } + irsend_active = true; + for (uint32_t r = 0; r <= repeat; r++) { + irsend->sendRaw(raw_array, i, parm[0]); + if (r < repeat) { // if it's not the last message + irsend->space(40000); // since we don't know the inter-message gap, place an arbitrary 40ms gap + } + } + } + else if (6 == count) { // NEC Protocol + // IRsend raw,0,8620,4260,544,411,1496,010101101000111011001110000000001100110000000001100000000000000010001100 + uint16_t raw_array[strlen(p)*2+3]; // Header + bits + end + raw_array[i++] = parm[1]; // Header mark + raw_array[i++] = parm[2]; // Header space + uint32_t inter_message_32 = (parm[1] + parm[2]) * 3; // compute an inter-message gap (32 bits) + uint16_t inter_message = (inter_message_32 > 65000) ? 65000 : inter_message_32; // avoid 16 bits overflow + for (; *p; *p++) { + if (*p == '0') { + raw_array[i++] = parm[3]; // Bit mark + raw_array[i++] = parm[4]; // Zero space + } + else if (*p == '1') { + raw_array[i++] = parm[3]; // Bit mark + raw_array[i++] = parm[5]; // One space + } + } + raw_array[i++] = parm[3]; // Trailing mark + irsend_active = true; + for (uint32_t r = 0; r <= repeat; r++) { + irsend->sendRaw(raw_array, i, parm[0]); + if (r < repeat) { // if it's not the last message + irsend->space(inter_message); // since we don't know the inter-message gap, place an arbitrary 40ms gap } } } else { - error = IE_SYNTAX_IRHVAC; + return IE_INVALID_RAWDATA; // Invalid number of parameters + } + } else { + if (!freq) { freq = 38000; } // Default to 38kHz + uint16_t count = 0; + char *q = p; + for (; *q; count += (*q++ == ',')); + if (0 == count) { + return IE_INVALID_RAWDATA; + } + + // IRsend 0,896,876,900,888,894,876,1790,874,872,1810,1736,948,872,880,872,936,872,1792,900,888,1734 + count++; + if (count < 200) { + uint16_t raw_array[count]; // It's safe to use stack for up to 200 packets (limited by mqtt_data length) + for (uint32_t i = 0; i < count; i++) { + raw_array[i] = strtol(strtok_r(nullptr, ", ", &p), nullptr, 0); // Allow decimal (20496) and hexadecimal (0x5010) input + } + +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DBG: stack count %d"), count); + + irsend_active = true; + for (uint32_t r = 0; r <= repeat; r++) { + irsend->sendRaw(raw_array, count, freq); + } + } else { + uint16_t *raw_array = reinterpret_cast(malloc(count * sizeof(uint16_t))); + if (raw_array == nullptr) { + return IE_INVALID_RAWDATA; + } + + for (uint32_t i = 0; i < count; i++) { + raw_array[i] = strtol(strtok_r(nullptr, ", ", &p), nullptr, 0); // Allow decimal (20496) and hexadecimal (0x5010) input + } + +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DBG: heap count %d"), count); + + irsend_active = true; + for (uint32_t r = 0; r <= repeat; r++) { + irsend->sendRaw(raw_array, count, freq); + } + free(raw_array); } } -#endif // USE_IR_HVAC - else serviced = false; // Unknown command + return IE_NO_ERROR; +} + +uint32_t IrRemoteCmndIrSendJson(void) +{ + // ArduinoJSON entry used to calculate jsonBuf: JSON_OBJECT_SIZE(3) + 40 = 96 + // IRsend { "protocol": "RC5", "bits": 12, "data":"0xC86" } + // IRsend { "protocol": "SAMSUNG", "bits": 32, "data": 551502015 } + + char dataBufUc[XdrvMailbox.data_len]; + UpperCase(dataBufUc, XdrvMailbox.data); + RemoveSpace(dataBufUc); + if (strlen(dataBufUc) < 8) { + return IE_INVALID_JSON; + } + + StaticJsonBuffer<140> jsonBuf; + JsonObject &root = jsonBuf.parseObject(dataBufUc); + if (!root.success()) { + return IE_INVALID_JSON; + } + + // IRsend { "protocol": "SAMSUNG", "bits": 32, "data": 551502015 } + // IRsend { "protocol": "NEC", "bits": 32, "data":"0x02FDFE80", "repeat": 2 } + char parm_uc[10]; + const char *protocol = root[UpperCase_P(parm_uc, PSTR(D_JSON_IR_PROTOCOL))]; + uint16_t bits = root[UpperCase_P(parm_uc, PSTR(D_JSON_IR_BITS))]; + uint64_t data = strtoull(root[UpperCase_P(parm_uc, PSTR(D_JSON_IR_DATA))], nullptr, 0); + uint16_t repeat = root[UpperCase_P(parm_uc, PSTR(D_JSON_IR_REPEAT))]; + // check if the IRSend is great than repeat + if (XdrvMailbox.index > repeat + 1) { + repeat = XdrvMailbox.index - 1; + } + if (!(protocol && bits)) { + return IE_SYNTAX_IRSEND; + } + + char protocol_text[20]; + int protocol_code = GetCommandCode(protocol_text, sizeof(protocol_text), protocol, kIrRemoteProtocols); + + char dvalue[64]; + char hvalue[20]; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("IRS: protocol_text %s, protocol %s, bits %d, data %s (0x%s), repeat %d, protocol_code %d"), + protocol_text, protocol, bits, ulltoa(data, dvalue, 10), Uint64toHex(data, hvalue, bits), repeat, protocol_code); + + irsend_active = true; + switch (protocol_code) { // Equals IRremoteESP8266.h enum decode_type_t +#ifdef USE_IR_SEND_RC5 + case RC5: + irsend->sendRC5(data, bits, repeat); break; +#endif +#ifdef USE_IR_SEND_RC6 + case RC6: + irsend->sendRC6(data, bits, repeat); break; +#endif +#ifdef USE_IR_SEND_NEC + case NEC: + irsend->sendNEC(data, (bits > NEC_BITS) ? NEC_BITS : bits, repeat); break; +#endif +#ifdef USE_IR_SEND_SONY + case SONY: + irsend->sendSony(data, (bits > SONY_20_BITS) ? SONY_20_BITS : bits, repeat > kSonyMinRepeat ? repeat : kSonyMinRepeat); break; +#endif +#ifdef USE_IR_SEND_PANASONIC + case PANASONIC: + irsend->sendPanasonic64(data, bits, repeat); break; +#endif +#ifdef USE_IR_SEND_JVC + case JVC: + irsend->sendJVC(data, (bits > JVC_BITS) ? JVC_BITS : bits, repeat > 1 ? repeat : 1); break; +#endif +#ifdef USE_IR_SEND_SAMSUNG + case SAMSUNG: + irsend->sendSAMSUNG(data, (bits > SAMSUNG_BITS) ? SAMSUNG_BITS : bits, repeat); break; +#endif +#ifdef USE_IR_SEND_WHYNTER + case WHYNTER: + irsend->sendWhynter(data, bits, repeat); break; +#endif +#ifdef USE_IR_SEND_AIWA + case AIWA_RC_T501: + irsend->sendAiwaRCT501(data, bits, repeat); break; +#endif +#ifdef USE_IR_SEND_LG + case LG: + irsend->sendLG(data, bits, repeat); break; +#endif +#ifdef USE_IR_SEND_SANYO + case SANYO: + irsend->sendSanyoLC7461(data, bits, repeat); break; +#endif +#ifdef USE_IR_SEND_MITSUBISHI + case MITSUBISHI: + irsend->sendMitsubishi(data, bits, repeat); break; +#endif +#ifdef USE_IR_SEND_DISH + case DISH: + irsend->sendDISH(data, (bits > DISH_BITS) ? DISH_BITS : bits, repeat > kDishMinRepeat ? repeat : kDishMinRepeat); break; +#endif +#ifdef USE_IR_SEND_SHARP + case SHARP: + irsend->sendSharpRaw(data, bits, repeat); break; +#endif +#ifdef USE_IR_SEND_PIONEER + case IR_PIONEER: + irsend->sendPioneer(data, bits, repeat); break; +#endif // USE_IR_SEND_PIONEER + default: + irsend_active = false; + ResponseCmndChar(D_JSON_PROTOCOL_NOT_SUPPORTED); + } + + return IE_NO_ERROR; +} + +void CmndIrSend(void) +{ + uint8_t error = IE_SYNTAX_IRSEND; + + if (XdrvMailbox.data_len) { +// error = (strstr(XdrvMailbox.data, "{") == nullptr) ? IrRemoteCmndIrSendRaw() : IrRemoteCmndIrSendJson(); + if (strstr(XdrvMailbox.data, "{") == nullptr) { + error = IrRemoteCmndIrSendRaw(); + } else { + error = IrRemoteCmndIrSendJson(); + } + } + IrRemoteCmndResponse(error); +} + +void IrRemoteCmndResponse(uint32_t error) +{ switch (error) { case IE_INVALID_RAWDATA: - Response_P(S_JSON_COMMAND_SVALUE, command, D_JSON_INVALID_RAWDATA); + ResponseCmndChar(D_JSON_INVALID_RAWDATA); break; case IE_INVALID_JSON: - Response_P(S_JSON_COMMAND_SVALUE, command, D_JSON_INVALID_JSON); + ResponseCmndChar(D_JSON_INVALID_JSON); break; case IE_SYNTAX_IRSEND: Response_P(PSTR("{\"" D_CMND_IRSEND "\":\"" D_JSON_NO " " D_JSON_IR_PROTOCOL ", " D_JSON_IR_BITS " " D_JSON_OR " " D_JSON_IR_DATA "\"}")); @@ -795,9 +1024,9 @@ bool IrSendCommand(void) Response_P(PSTR("{\"" D_CMND_IRHVAC "\":\"" D_JSON_WRONG " " D_JSON_IRHVAC_VENDOR ", " D_JSON_IRHVAC_MODE " " D_JSON_OR " " D_JSON_IRHVAC_FANSPEED "\"}")); break; #endif // USE_IR_HVAC + default: // IE_NO_ERROR + ResponseCmndDone(); } - - return serviced; } /*********************************************************************************************\ @@ -830,7 +1059,7 @@ bool Xdrv05(uint8_t function) break; case FUNC_COMMAND: if (pin[GPIO_IRSEND] < 99) { - result = IrSendCommand(); + result = DecodeCommand(kIrRemoteCommands, IrRemoteCommand); } break; } @@ -838,4 +1067,4 @@ bool Xdrv05(uint8_t function) return result; } -#endif // USE_IR_REMOTE +#endif // defined(USE_IR_REMOTE) && !defined(USE_IR_REMOTE_FULL) diff --git a/sonoff/xdrv_05_irremote_full.ino b/sonoff/xdrv_05_irremote_full.ino new file mode 100644 index 000000000..d78921b54 --- /dev/null +++ b/sonoff/xdrv_05_irremote_full.ino @@ -0,0 +1,662 @@ +/* + xdrv_05_irremote_full.ino - complete intefration of IRremoteESP8266 + + Copyright (C) 2019 Heiko Krupp, Lazar Obradovic, Theo Arends, Stephan Hadinger + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef USE_IR_REMOTE_FULL +/*********************************************************************************************\ + * IR Remote send and receive using IRremoteESP8266 library +\*********************************************************************************************/ + +#define XDRV_05 5 + +#include +#include +#include +#include +#include + +enum IrErrors { IE_RESPONSE_PROVIDED, IE_NO_ERROR, IE_INVALID_RAWDATA, IE_INVALID_JSON, IE_SYNTAX_IRSEND, IE_SYNTAX_IRHVAC, + IE_UNSUPPORTED_HVAC, IE_UNSUPPORTED_PROTOCOL }; + +const char kIrRemoteCommands[] PROGMEM = "|" D_CMND_IRHVAC "|" D_CMND_IRSEND ; // No prefix + +void (* const IrRemoteCommand[])(void) PROGMEM = { &CmndIrHvac, &CmndIrSend }; + +/*********************************************************************************************\ + * IR Send +\*********************************************************************************************/ + +IRsend *irsend = nullptr; +bool irsend_active = false; + +void IrSendInit(void) +{ + irsend = new IRsend(pin[GPIO_IRSEND]); // an IR led is at GPIO_IRSEND + irsend->begin(); +} + +// from https://stackoverflow.com/questions/2602823/in-c-c-whats-the-simplest-way-to-reverse-the-order-of-bits-in-a-byte +// First the left four bits are swapped with the right four bits. Then all adjacent pairs are swapped and then all adjacent single bits. This results in a reversed order. +uint8_t reverseBitsInByte(uint8_t b) { + b = (b & 0xF0) >> 4 | (b & 0x0F) << 4; + b = (b & 0xCC) >> 2 | (b & 0x33) << 2; + b = (b & 0xAA) >> 1 | (b & 0x55) << 1; + return b; +} + +// reverse bits in each byte +uint64_t reverseBitsInBytes64(uint64_t b) { + union { + uint8_t b[8]; + uint64_t i; + } a; + a.i = b; + for (uint32_t i=0; i<8; i++) { + a.b[i] = reverseBitsInByte(a.b[i]); + } + return a.i; +} + +/*********************************************************************************************\ + * IR Receive +\*********************************************************************************************/ + +const bool IR_FULL_RCV_SAVE_BUFFER = false; // false = do not use buffer, true = use buffer for decoding +const uint32_t IR_TIME_AVOID_DUPLICATE = 500; // Milliseconds + +// Below is from IRrecvDumpV2.ino +// As this program is a special purpose capture/decoder, let us use a larger +// than normal buffer so we can handle Air Conditioner remote codes. +const uint16_t IR_FULL_BUFFER_SIZE = 1024; + +// Some A/C units have gaps in their protocols of ~40ms. e.g. Kelvinator +// A value this large may swallow repeats of some protocols +const uint8_t IR__FULL_RCV_TIMEOUT = 50; + +IRrecv *irrecv = nullptr; + +unsigned long ir_lasttime = 0; + +void IrReceiveUpdateThreshold() +{ + if (irrecv != nullptr) { + if (Settings.param[P_IR_UNKNOW_THRESHOLD] < 6) { Settings.param[P_IR_UNKNOW_THRESHOLD] = 6; } + irrecv->setUnknownThreshold(Settings.param[P_IR_UNKNOW_THRESHOLD]); + } +} + +void IrReceiveInit(void) +{ + // an IR led is at GPIO_IRRECV + irrecv = new IRrecv(pin[GPIO_IRRECV], IR_FULL_BUFFER_SIZE, IR__FULL_RCV_TIMEOUT, IR_FULL_RCV_SAVE_BUFFER); + irrecv->setUnknownThreshold(Settings.param[P_IR_UNKNOW_THRESHOLD]); + irrecv->enableIRIn(); // Start the receiver +} + +String sendACJsonState(const stdAc::state_t &state) { + DynamicJsonBuffer jsonBuffer; + JsonObject& json = jsonBuffer.createObject(); + json[D_JSON_IRHVAC_VENDOR] = typeToString(state.protocol); + json[D_JSON_IRHVAC_MODEL] = state.model; + json[D_JSON_IRHVAC_POWER] = IRac::boolToString(state.power); + json[D_JSON_IRHVAC_MODE] = IRac::opmodeToString(state.mode); + // Home Assistant wants mode to be off if power is also off & vice-versa. + if (state.mode == stdAc::opmode_t::kOff || !state.power) { + json[D_JSON_IRHVAC_MODE] = IRac::opmodeToString(stdAc::opmode_t::kOff); + json[D_JSON_IRHVAC_POWER] = IRac::boolToString(false); + } + json[D_JSON_IRHVAC_CELSIUS] = IRac::boolToString(state.celsius); + if (floorf(state.degrees) == state.degrees) { + json[D_JSON_IRHVAC_TEMP] = floorf(state.degrees); // integer + } else { + json[D_JSON_IRHVAC_TEMP] = RawJson(String(state.degrees, 1)); // non-integer, limit to only 1 sub-digit + } + json[D_JSON_IRHVAC_FANSPEED] = IRac::fanspeedToString(state.fanspeed); + json[D_JSON_IRHVAC_SWINGV] = IRac::swingvToString(state.swingv); + json[D_JSON_IRHVAC_SWINGH] = IRac::swinghToString(state.swingh); + json[D_JSON_IRHVAC_QUIET] = IRac::boolToString(state.quiet); + json[D_JSON_IRHVAC_TURBO] = IRac::boolToString(state.turbo); + json[D_JSON_IRHVAC_ECONO] = IRac::boolToString(state.econo); + json[D_JSON_IRHVAC_LIGHT] = IRac::boolToString(state.light); + json[D_JSON_IRHVAC_FILTER] = IRac::boolToString(state.filter); + json[D_JSON_IRHVAC_CLEAN] = IRac::boolToString(state.clean); + json[D_JSON_IRHVAC_BEEP] = IRac::boolToString(state.beep); + json[D_JSON_IRHVAC_SLEEP] = state.sleep; + + String payload = ""; + payload.reserve(200); + json.printTo(payload); + return payload; +} + +String sendIRJsonState(const struct decode_results &results) { + String json("{"); + json += "\"" D_JSON_IR_PROTOCOL "\":\""; + json += typeToString(results.decode_type); + json += "\",\"" D_JSON_IR_BITS "\":"; + json += results.bits; + + if (hasACState(results.decode_type)) { + json += ",\"" D_JSON_IR_DATA "\":\"0x"; + json += resultToHexidecimal(&results); + json += "\""; + } else { + if (UNKNOWN != results.decode_type) { + json += ",\"" D_JSON_IR_DATA "\":"; + } else { + json += ",\"" D_JSON_IR_HASH "\":"; + } + if (Settings.flag.ir_receive_decimal) { + char svalue[32]; + ulltoa(results.value, svalue, 10); + json += svalue; + } else { + char hvalue[64]; + if (UNKNOWN != results.decode_type) { + Uint64toHex(results.value, hvalue, results.bits); // Get 64bit value as hex 0x00123456 + json += "\"0x"; + json += hvalue; + json += "\",\"" D_JSON_IR_DATALSB "\":\"0x"; + Uint64toHex(reverseBitsInBytes64(results.value), hvalue, results.bits); // Get 64bit value as hex 0x00123456, LSB + json += hvalue; + json += "\""; + } else { // UNKNOWN + Uint64toHex(results.value, hvalue, 32); // Unknown is always 32 bits + json += "\"0x"; + json += hvalue; + json += "\""; + } + } + } + json += ",\"" D_JSON_IR_REPEAT "\":"; + json += results.repeat; + + stdAc::state_t ac_result; + if (IRAcUtils::decodeToState(&results, &ac_result, nullptr)) { + // we have a decoded state + json += ",\"" D_CMND_IRHVAC "\":"; + json += sendACJsonState(ac_result); + } + + return json; +} + +void IrReceiveCheck(void) +{ + decode_results results; + + if (irrecv->decode(&results)) { + uint32_t now = millis(); + +// if ((now - ir_lasttime > IR_TIME_AVOID_DUPLICATE) && (UNKNOWN != results.decode_type) && (results.bits > 0)) { + if (!irsend_active && (now - ir_lasttime > IR_TIME_AVOID_DUPLICATE)) { + ir_lasttime = now; + Response_P(PSTR("{\"" D_JSON_IRRECEIVED "\":%s"), sendIRJsonState(results).c_str()); + + if (Settings.flag3.receive_raw) { + ResponseAppend_P(PSTR(",\"" D_JSON_IR_RAWDATA "\":[")); + uint16_t i; + for (i = 1; i < results.rawlen; i++) { + if (i > 1) { ResponseAppend_P(PSTR(",")); } + uint32_t usecs; + for (usecs = results.rawbuf[i] * kRawTick; usecs > UINT16_MAX; usecs -= UINT16_MAX) { + ResponseAppend_P(PSTR("%d,0,"), UINT16_MAX); + } + ResponseAppend_P(PSTR("%d"), usecs); + if (strlen(mqtt_data) > sizeof(mqtt_data) - 40) { break; } // Quit if char string becomes too long + } + uint16_t extended_length = results.rawlen - 1; + for (uint32_t j = 0; j < results.rawlen - 1; j++) { + uint32_t usecs = results.rawbuf[j] * kRawTick; + // Add two extra entries for multiple larger than UINT16_MAX it is. + extended_length += (usecs / (UINT16_MAX + 1)) * 2; + } + ResponseAppend_P(PSTR("],\"" D_JSON_IR_RAWDATA "Info\":[%d,%d,%d]"), extended_length, i -1, results.overflow); + } + + ResponseJsonEndEnd(); + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_IRRECEIVED)); + + XdrvRulesProcess(); + } + + irrecv->resume(); + } +} + + +/*********************************************************************************************\ + * IR Heating, Ventilation and Air Conditioning +\*********************************************************************************************/ + +// list all supported protocols, either for IRSend or for IRHVAC, separated by '|' +String listSupportedProtocols(bool hvac) { + String l(""); + bool first = true; + for (uint32_t i = UNUSED + 1; i <= kLastDecodeType; i++) { + bool found = false; + if (hvac) { + found = IRac::isProtocolSupported((decode_type_t)i); + } else { + found = (IRsend::defaultBits((decode_type_t)i) > 0) && (!IRac::isProtocolSupported((decode_type_t)i)); + } + if (found) { + if (first) { + first = false; + } else { + l += "|"; + } + l += typeToString((decode_type_t)i); + } + } + return l; +} + +// used to convert values 0-5 to fanspeed_t +const stdAc::fanspeed_t IrHvacFanSpeed[] PROGMEM = { stdAc::fanspeed_t::kAuto, + stdAc::fanspeed_t::kMin, stdAc::fanspeed_t::kLow,stdAc::fanspeed_t::kMedium, + stdAc::fanspeed_t::kHigh, stdAc::fanspeed_t::kMax }; + +uint32_t IrRemoteCmndIrHvacJson(void) +{ + stdAc::state_t state, prev; + char parm_uc[12]; + + //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("IRHVAC: Received %s"), XdrvMailbox.data); + char dataBufUc[XdrvMailbox.data_len]; + UpperCase(dataBufUc, XdrvMailbox.data); + RemoveSpace(dataBufUc); + if (strlen(dataBufUc) < 8) { return IE_INVALID_JSON; } + + DynamicJsonBuffer jsonBuf; + JsonObject &json = jsonBuf.parseObject(dataBufUc); + if (!json.success()) { return IE_INVALID_JSON; } + + // from: https://github.com/crankyoldgit/IRremoteESP8266/blob/master/examples/CommonAcControl/CommonAcControl.ino + state.protocol = decode_type_t::UNKNOWN; + state.model = 1; // Some A/C's have different models. Let's try using just 1. + state.mode = stdAc::opmode_t::kAuto; // Run in cool mode initially. + state.power = false; // Initially start with the unit off. + state.celsius = true; // Use Celsius for units of temp. False = Fahrenheit + state.degrees = 21.0f; // 21 degrees. + state.fanspeed = stdAc::fanspeed_t::kMedium; // Start with the fan at medium. + state.swingv = stdAc::swingv_t::kOff; // Don't swing the fan up or down. + state.swingh = stdAc::swingh_t::kOff; // Don't swing the fan left or right. + state.light = false; // Turn off any LED/Lights/Display that we can. + state.beep = false; // Turn off any beep from the A/C if we can. + state.econo = false; // Turn off any economy modes if we can. + state.filter = false; // Turn off any Ion/Mold/Health filters if we can. + state.turbo = false; // Don't use any turbo/powerful/etc modes. + state.quiet = false; // Don't use any quiet/silent/etc modes. + state.sleep = -1; // Don't set any sleep time or modes. + state.clean = false; // Turn off any Cleaning options if we can. + state.clock = -1; // Don't set any current time if we can avoid it. + + UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_VENDOR)); + if (json.containsKey(parm_uc)) { state.protocol = strToDecodeType(json[parm_uc]); } + UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_PROTOCOL)); + if (json.containsKey(parm_uc)) { state.protocol = strToDecodeType(json[parm_uc]); } // also support 'protocol' + if (decode_type_t::UNKNOWN == state.protocol) { return IE_UNSUPPORTED_HVAC; } + if (!IRac::isProtocolSupported(state.protocol)) { return IE_UNSUPPORTED_HVAC; } + + // for fan speed, we also support 1-5 values + UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_FANSPEED)); + if (json.containsKey(parm_uc)) { + uint32_t fan_speed = json[parm_uc]; + if ((fan_speed >= 1) && (fan_speed <= 5)) { + state.fanspeed = (stdAc::fanspeed_t) pgm_read_byte(&IrHvacFanSpeed[fan_speed]); + } else { + state.fanspeed = IRac::strToFanspeed(json[parm_uc]); + } + } + + UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_MODEL)); + if (json.containsKey(parm_uc)) { state.model = IRac::strToModel(json[parm_uc]); } + UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_MODE)); + if (json.containsKey(parm_uc)) { state.mode = IRac::strToOpmode(json[parm_uc]); } + UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_SWINGV)); + if (json.containsKey(parm_uc)) { state.swingv = IRac::strToSwingV(json[parm_uc]); } + UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_SWINGH)); + if (json.containsKey(parm_uc)) { state.swingh = IRac::strToSwingH(json[parm_uc]); } + UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_TEMP)); + if (json.containsKey(parm_uc)) { state.degrees = json[parm_uc]; } + // AddLog_P2(LOG_LEVEL_DEBUG, PSTR("model %d, mode %d, fanspeed %d, swingv %d, swingh %d"), + // state.model, state.mode, state.fanspeed, state.swingv, state.swingh); + + // decode booleans + UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_POWER)); + if (json.containsKey(parm_uc)) { state.power = IRac::strToBool(json[parm_uc]); } + UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_CELSIUS)); + if (json.containsKey(parm_uc)) { state.celsius = IRac::strToBool(json[parm_uc]); } + UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_LIGHT)); + if (json.containsKey(parm_uc)) { state.light = IRac::strToBool(json[parm_uc]); } + UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_BEEP)); + if (json.containsKey(parm_uc)) { state.beep = IRac::strToBool(json[parm_uc]); } + UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_ECONO)); + if (json.containsKey(parm_uc)) { state.econo = IRac::strToBool(json[parm_uc]); } + UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_FILTER)); + if (json.containsKey(parm_uc)) { state.filter = IRac::strToBool(json[parm_uc]); } + UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_TURBO)); + if (json.containsKey(parm_uc)) { state.turbo = IRac::strToBool(json[parm_uc]); } + UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_QUIET)); + if (json.containsKey(parm_uc)) { state.quiet = IRac::strToBool(json[parm_uc]); } + UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_CLEAN)); + if (json.containsKey(parm_uc)) { state.clean = IRac::strToBool(json[parm_uc]); } + + // optional timer and clock + UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_SLEEP)); + if (json[parm_uc]) { state.sleep = json[parm_uc]; } + //if (json[D_JSON_IRHVAC_CLOCK]) { state.clock = json[D_JSON_IRHVAC_CLOCK]; } // not sure it's useful to support 'clock' + + IRac ac(pin[GPIO_IRSEND]); + bool success = ac.sendAc(state, &prev); + if (!success) { return IE_SYNTAX_IRHVAC; } + + Response_P(PSTR("{\"" D_CMND_IRHVAC "\":%s}"), sendACJsonState(state).c_str()); + return IE_RESPONSE_PROVIDED; +} + +void CmndIrHvac(void) +{ + uint8_t error = IE_SYNTAX_IRHVAC; + + if (XdrvMailbox.data_len) { + error = IrRemoteCmndIrHvacJson(); + } + if (error != IE_RESPONSE_PROVIDED) { IrRemoteCmndResponse(error); } // otherwise response was already provided +} + +/*********************************************************************************************\ + * Commands +\*********************************************************************************************/ + +uint32_t IrRemoteCmndIrSendJson(void) +{ + char parm_uc[12]; // used to convert JSON keys to uppercase + // ArduinoJSON entry used to calculate jsonBuf: JSON_OBJECT_SIZE(3) + 40 = 96 + // IRsend { "protocol": "RC5", "bits": 12, "data":"0xC86" } + // IRsend { "protocol": "SAMSUNG", "bits": 32, "data": 551502015 } + char dataBufUc[XdrvMailbox.data_len]; + UpperCase(dataBufUc, XdrvMailbox.data); + RemoveSpace(dataBufUc); + if (strlen(dataBufUc) < 8) { return IE_INVALID_JSON; } + + DynamicJsonBuffer jsonBuf; + JsonObject &json = jsonBuf.parseObject(dataBufUc); + if (!json.success()) { return IE_INVALID_JSON; } + + // IRsend { "protocol": "SAMSUNG", "bits": 32, "data": 551502015 } + // IRsend { "protocol": "NEC", "bits": 32, "data":"0x02FDFE80", "repeat": 2 } + decode_type_t protocol = decode_type_t::UNKNOWN; + uint16_t bits = 0; + uint64_t data; + uint8_t repeat = 0; + + UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_VENDOR)); + if (json.containsKey(parm_uc)) { protocol = strToDecodeType(json[parm_uc]); } + UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_PROTOCOL)); + if (json.containsKey(parm_uc)) { protocol = strToDecodeType(json[parm_uc]); } // also support 'protocol' + if (decode_type_t::UNKNOWN == protocol) { return IE_UNSUPPORTED_PROTOCOL; } + + UpperCase_P(parm_uc, PSTR(D_JSON_IR_BITS)); + if (json.containsKey(parm_uc)) { bits = json[parm_uc]; } + UpperCase_P(parm_uc, PSTR(D_JSON_IR_REPEAT)); + if (json.containsKey(parm_uc)) { repeat = json[parm_uc]; } + UpperCase_P(parm_uc, PSTR(D_JSON_IR_DATALSB)); // accept LSB values + if (json.containsKey(parm_uc)) { data = reverseBitsInBytes64(strtoull(json[parm_uc], nullptr, 0)); } + UpperCase_P(parm_uc, PSTR(D_JSON_IR_DATA)); // or classical MSB (takes priority) + if (json.containsKey(parm_uc)) { data = strtoull(json[parm_uc], nullptr, 0); } + if (0 == bits) { return IE_SYNTAX_IRSEND; } + + // check if the IRSend is greater than repeat, but can be overriden with JSON + if (XdrvMailbox.index > repeat + 1) { repeat = XdrvMailbox.index - 1; } + + char dvalue[32]; + char hvalue[32]; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("IRS: protocol %d, bits %d, data 0x%s (%s), repeat %d"), + protocol, bits, ulltoa(data, dvalue, 10), Uint64toHex(data, hvalue, bits), repeat); + + irsend_active = true; // deactivate receive + bool success = irsend->send(protocol, data, bits, repeat); + + if (!success) { + irsend_active = false; + ResponseCmndChar(D_JSON_PROTOCOL_NOT_SUPPORTED); + } + return IE_NO_ERROR; +} + +uint32_t IrRemoteCmndIrSendRaw(void) +{ + // IRsend ,, ... + // or + // IRsend raw,,, (one space = zero space *2) + // IRsend raw,,,, + // IRsend raw,,,, + // IRsend raw,,
,
,,,, + + char *p; + char *str = strtok_r(XdrvMailbox.data, ", ", &p); + if (p == nullptr) { + return IE_INVALID_RAWDATA; + } + + // repeat + uint16_t repeat = XdrvMailbox.index > 0 ? XdrvMailbox.index - 1 : 0; + + uint16_t freq = atoi(str); + if (!freq && (*str != '0')) { // First parameter is any string + uint16_t count = 0; + char *q = p; + for (; *q; count += (*q++ == ',')); + if (count < 2) { + return IE_INVALID_RAWDATA; + } // Parameters must be at least 3 + + uint16_t parm[count]; + for (uint32_t i = 0; i < count; i++) { + parm[i] = strtol(strtok_r(nullptr, ", ", &p), nullptr, 0); + if (!parm[i]) { + if (!i) { + parm[0] = 38000; // Frequency default to 38kHz + } else { + return IE_INVALID_RAWDATA; // Other parameters may not be 0 + } + } + } + + uint16_t i = 0; + if (count < 4) { + // IRsend raw,0,889,000000100110000001001 + uint16_t mark = parm[1] *2; // Protocol where 0 = t, 1 = 2t (RC5) + if (3 == count) { + if (parm[2] < parm[1]) { + // IRsend raw,0,889,2,000000100110000001001 + mark = parm[1] * parm[2]; // Protocol where 0 = t1, 1 = t1*t2 (Could be RC5) + } else { + // IRsend raw,0,889,1778,000000100110000001001 + mark = parm[2]; // Protocol where 0 = t1, 1 = t2 (Could be RC5) + } + } + uint16_t raw_array[strlen(p)]; // Bits + for (; *p; *p++) { + if (*p == '0') { + raw_array[i++] = parm[1]; // Space + } + else if (*p == '1') { + raw_array[i++] = mark; // Mark + } + } + irsend_active = true; + for (uint32_t r = 0; r <= repeat; r++) { + irsend->sendRaw(raw_array, i, parm[0]); + if (r < repeat) { // if it's not the last message + irsend->space(40000); // since we don't know the inter-message gap, place an arbitrary 40ms gap + } + } + } + else if (6 == count) { // NEC Protocol + // IRsend raw,0,8620,4260,544,411,1496,010101101000111011001110000000001100110000000001100000000000000010001100 + uint16_t raw_array[strlen(p)*2+3]; // Header + bits + end + raw_array[i++] = parm[1]; // Header mark + raw_array[i++] = parm[2]; // Header space + uint32_t inter_message_32 = (parm[1] + parm[2]) * 3; // compute an inter-message gap (32 bits) + uint16_t inter_message = (inter_message_32 > 65000) ? 65000 : inter_message_32; // avoid 16 bits overflow + for (; *p; *p++) { + if (*p == '0') { + raw_array[i++] = parm[3]; // Bit mark + raw_array[i++] = parm[4]; // Zero space + } + else if (*p == '1') { + raw_array[i++] = parm[3]; // Bit mark + raw_array[i++] = parm[5]; // One space + } + } + raw_array[i++] = parm[3]; // Trailing mark + irsend_active = true; + for (uint32_t r = 0; r <= repeat; r++) { + irsend->sendRaw(raw_array, i, parm[0]); + if (r < repeat) { // if it's not the last message + irsend->space(inter_message); // since we don't know the inter-message gap, place an arbitrary 40ms gap + } + } + } + else { + return IE_INVALID_RAWDATA; // Invalid number of parameters + } + } else { + if (!freq) { freq = 38000; } // Default to 38kHz + uint16_t count = 0; + char *q = p; + for (; *q; count += (*q++ == ',')); + if (0 == count) { + return IE_INVALID_RAWDATA; + } + + // IRsend 0,896,876,900,888,894,876,1790,874,872,1810,1736,948,872,880,872,936,872,1792,900,888,1734 + count++; + if (count < 200) { + uint16_t raw_array[count]; // It's safe to use stack for up to 200 packets (limited by mqtt_data length) + for (uint32_t i = 0; i < count; i++) { + raw_array[i] = strtol(strtok_r(nullptr, ", ", &p), nullptr, 0); // Allow decimal (20496) and hexadecimal (0x5010) input + } + +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DBG: stack count %d"), count); + + irsend_active = true; + for (uint32_t r = 0; r <= repeat; r++) { + irsend->sendRaw(raw_array, count, freq); + } + } else { + uint16_t *raw_array = reinterpret_cast(malloc(count * sizeof(uint16_t))); + if (raw_array == nullptr) { + return IE_INVALID_RAWDATA; + } + + for (uint32_t i = 0; i < count; i++) { + raw_array[i] = strtol(strtok_r(nullptr, ", ", &p), nullptr, 0); // Allow decimal (20496) and hexadecimal (0x5010) input + } + +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DBG: heap count %d"), count); + + irsend_active = true; + for (uint32_t r = 0; r <= repeat; r++) { + irsend->sendRaw(raw_array, count, freq); + } + free(raw_array); + } + } + + return IE_NO_ERROR; +} + +void CmndIrSend(void) +{ + uint8_t error = IE_SYNTAX_IRSEND; + + if (XdrvMailbox.data_len) { + if (strstr(XdrvMailbox.data, "{") == nullptr) { + error = IrRemoteCmndIrSendRaw(); + } else { + error = IrRemoteCmndIrSendJson(); + } + } + IrRemoteCmndResponse(error); +} + +void IrRemoteCmndResponse(uint32_t error) +{ + switch (error) { + case IE_INVALID_RAWDATA: + ResponseCmndChar(D_JSON_INVALID_RAWDATA); + break; + case IE_INVALID_JSON: + ResponseCmndChar(D_JSON_INVALID_JSON); + break; + case IE_SYNTAX_IRSEND: + Response_P(PSTR("{\"" D_CMND_IRSEND "\":\"" D_JSON_NO " " D_JSON_IR_BITS " " D_JSON_OR " " D_JSON_IR_DATA "\"}")); + break; + case IE_SYNTAX_IRHVAC: + Response_P(PSTR("{\"" D_CMND_IRHVAC "\":\"" D_JSON_WRONG " " D_JSON_IRHVAC_VENDOR ", " D_JSON_IRHVAC_MODE " " D_JSON_OR " " D_JSON_IRHVAC_FANSPEED "\"}")); + break; + case IE_UNSUPPORTED_HVAC: + Response_P(PSTR("{\"" D_CMND_IRHVAC "\":\"" D_JSON_WRONG " " D_JSON_IRHVAC_VENDOR " (%s)\"}"), listSupportedProtocols(true).c_str()); + break; + case IE_UNSUPPORTED_PROTOCOL: + Response_P(PSTR("{\"" D_CMND_IRSEND "\":\"" D_JSON_WRONG " " D_JSON_IRHVAC_PROTOCOL " (%s)\"}"), listSupportedProtocols(false).c_str()); + break; + default: // IE_NO_ERROR + ResponseCmndDone(); + } +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xdrv05(uint8_t function) +{ + bool result = false; + + if ((pin[GPIO_IRSEND] < 99) || (pin[GPIO_IRRECV] < 99)) { + switch (function) { + case FUNC_PRE_INIT: + if (pin[GPIO_IRSEND] < 99) { + IrSendInit(); + } + if (pin[GPIO_IRRECV] < 99) { + IrReceiveInit(); + } + break; + case FUNC_EVERY_50_MSECOND: + if (pin[GPIO_IRRECV] < 99) { + IrReceiveCheck(); // check if there's anything on IR side + } + irsend_active = false; // re-enable IR reception + break; + case FUNC_COMMAND: + if (pin[GPIO_IRSEND] < 99) { + result = DecodeCommand(kIrRemoteCommands, IrRemoteCommand); + } + break; + } + } + return result; +} + +#endif // USE_IR_REMOTE_FULL diff --git a/sonoff/xdrv_06_snfbridge.ino b/sonoff/xdrv_06_snfbridge.ino index 1a037649a..fbd7a344a 100644 --- a/sonoff/xdrv_06_snfbridge.ino +++ b/sonoff/xdrv_06_snfbridge.ino @@ -17,6 +17,7 @@ along with this program. If not, see . */ +#ifdef USE_SONOFF_RF /*********************************************************************************************\ Sonoff RF Bridge 433 \*********************************************************************************************/ @@ -26,19 +27,25 @@ const uint32_t SFB_TIME_AVOID_DUPLICATE = 2000; // Milliseconds enum SonoffBridgeCommands { - CMND_RFSYNC, CMND_RFLOW, CMND_RFHIGH, CMND_RFHOST, CMND_RFCODE, CMND_RFKEY, CMND_RFRAW }; -const char kSonoffBridgeCommands[] PROGMEM = + CMND_RFSYNC, CMND_RFLOW, CMND_RFHIGH, CMND_RFHOST, CMND_RFCODE }; + +const char kSonoffBridgeCommands[] PROGMEM = "|" // No prefix D_CMND_RFSYNC "|" D_CMND_RFLOW "|" D_CMND_RFHIGH "|" D_CMND_RFHOST "|" D_CMND_RFCODE "|" D_CMND_RFKEY "|" D_CMND_RFRAW; -uint8_t sonoff_bridge_receive_flag = 0; -uint8_t sonoff_bridge_receive_raw_flag = 0; -uint8_t sonoff_bridge_learn_key = 1; -uint8_t sonoff_bridge_learn_active = 0; -uint8_t sonoff_bridge_expected_bytes = 0; -uint32_t sonoff_bridge_last_received_id = 0; -uint32_t sonoff_bridge_last_send_code = 0; -unsigned long sonoff_bridge_last_time = 0; -unsigned long sonoff_bridge_last_learn_time = 0; +void (* const SonoffBridgeCommand[])(void) PROGMEM = { + &CmndRfBridge, &CmndRfBridge, &CmndRfBridge, &CmndRfBridge, &CmndRfBridge, &CmndRfKey, &CmndRfRaw }; + +struct SONOFFBRIDGE { + uint32_t last_received_id = 0; + uint32_t last_send_code = 0; + uint32_t last_time = 0; + uint32_t last_learn_time = 0; + uint8_t receive_flag = 0; + uint8_t receive_raw_flag = 0; + uint8_t learn_key = 1; + uint8_t learn_active = 0; + uint8_t expected_bytes = 0; +} SnfBridge; #ifdef USE_RF_FLASH /*********************************************************************************************\ @@ -208,7 +215,7 @@ void SonoffBridgeReceivedRaw(void) if (0xB1 == serial_in_buffer[1]) { buckets = serial_in_buffer[2] << 1; } // Bucket sniffing - Response_P(PSTR("{\"" D_CMND_RFRAW "\":{\"" D_JSON_DATA "\":\"")); + ResponseTime_P(PSTR(",\"" D_CMND_RFRAW "\":{\"" D_JSON_DATA "\":\"")); for (uint32_t i = 0; i < serial_in_byte_counter; i++) { ResponseAppend_P(PSTR("%02X"), serial_in_buffer[i]); if (0xB1 == serial_in_buffer[1]) { @@ -220,6 +227,7 @@ void SonoffBridgeReceivedRaw(void) } ResponseAppend_P(PSTR("\"}}")); MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_CMND_RFRAW)); + XdrvRulesProcess(); } @@ -227,8 +235,8 @@ void SonoffBridgeReceivedRaw(void) void SonoffBridgeLearnFailed(void) { - sonoff_bridge_learn_active = 0; - Response_P(S_JSON_COMMAND_INDEX_SVALUE, D_CMND_RFKEY, sonoff_bridge_learn_key, D_JSON_LEARN_FAILED); + SnfBridge.learn_active = 0; + Response_P(S_JSON_COMMAND_INDEX_SVALUE, D_CMND_RFKEY, SnfBridge.learn_key, D_JSON_LEARN_FAILED); MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_RFKEY)); } @@ -247,21 +255,21 @@ void SonoffBridgeReceived(void) SonoffBridgeLearnFailed(); } else if (0xA3 == serial_in_buffer[0]) { // Learned A3 20 F8 01 18 03 3E 2E 1A 22 55 - sonoff_bridge_learn_active = 0; + SnfBridge.learn_active = 0; low_time = serial_in_buffer[3] << 8 | serial_in_buffer[4]; // Low time in uSec high_time = serial_in_buffer[5] << 8 | serial_in_buffer[6]; // High time in uSec if (low_time && high_time) { for (uint32_t i = 0; i < 9; i++) { - Settings.rf_code[sonoff_bridge_learn_key][i] = serial_in_buffer[i +1]; + Settings.rf_code[SnfBridge.learn_key][i] = serial_in_buffer[i +1]; } - Response_P(S_JSON_COMMAND_INDEX_SVALUE, D_CMND_RFKEY, sonoff_bridge_learn_key, D_JSON_LEARNED); + Response_P(S_JSON_COMMAND_INDEX_SVALUE, D_CMND_RFKEY, SnfBridge.learn_key, D_JSON_LEARNED); MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_RFKEY)); } else { SonoffBridgeLearnFailed(); } } else if (0xA4 == serial_in_buffer[0]) { // Received RF data A4 20 EE 01 18 03 3E 2E 1A 22 55 - if (sonoff_bridge_learn_active) { + if (SnfBridge.learn_active) { SonoffBridgeLearnFailed(); } else { sync_time = serial_in_buffer[1] << 8 | serial_in_buffer[2]; // Sync time in uSec @@ -270,9 +278,9 @@ void SonoffBridgeReceived(void) received_id = serial_in_buffer[7] << 16 | serial_in_buffer[8] << 8 | serial_in_buffer[9]; unsigned long now = millis(); - if (!((received_id == sonoff_bridge_last_received_id) && (now - sonoff_bridge_last_time < SFB_TIME_AVOID_DUPLICATE))) { - sonoff_bridge_last_received_id = received_id; - sonoff_bridge_last_time = now; + if (!((received_id == SnfBridge.last_received_id) && (now - SnfBridge.last_time < SFB_TIME_AVOID_DUPLICATE))) { + SnfBridge.last_received_id = received_id; + SnfBridge.last_time = now; strncpy_P(rfkey, PSTR("\"" D_JSON_NONE "\""), sizeof(rfkey)); for (uint32_t i = 1; i <= 16; i++) { if (Settings.rf_code[i][0]) { @@ -288,7 +296,7 @@ void SonoffBridgeReceived(void) } else { snprintf_P(stemp, sizeof(stemp), PSTR("\"%06X\""), received_id); } - Response_P(PSTR("{\"" D_JSON_RFRECEIVED "\":{\"" D_JSON_SYNC "\":%d,\"" D_JSON_LOW "\":%d,\"" D_JSON_HIGH "\":%d,\"" D_JSON_DATA "\":%s,\"" D_CMND_RFKEY "\":%s}}"), + ResponseTime_P(PSTR(",\"" D_JSON_RFRECEIVED "\":{\"" D_JSON_SYNC "\":%d,\"" D_JSON_LOW "\":%d,\"" D_JSON_HIGH "\":%d,\"" D_JSON_DATA "\":%s,\"" D_CMND_RFKEY "\":%s}}"), sync_time, low_time, high_time, stemp, rfkey); MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_RFRECEIVED)); XdrvRulesProcess(); @@ -305,8 +313,8 @@ bool SonoffBridgeSerialInput(void) // iTead Rf Universal Transceiver Module Serial Protocol Version 1.0 (20170420) static int8_t receive_len = 0; - if (sonoff_bridge_receive_flag) { - if (sonoff_bridge_receive_raw_flag) { + if (SnfBridge.receive_flag) { + if (SnfBridge.receive_raw_flag) { if (!serial_in_byte_counter) { serial_in_buffer[serial_in_byte_counter++] = 0xAA; } @@ -318,26 +326,26 @@ bool SonoffBridgeSerialInput(void) } if ((!receive_len && (0x55 == serial_in_byte)) || (receive_len && (serial_in_byte_counter == receive_len))) { // 0x55 - End of text SonoffBridgeReceivedRaw(); - sonoff_bridge_receive_flag = 0; + SnfBridge.receive_flag = 0; return 1; } } else if (!((0 == serial_in_byte_counter) && (0 == serial_in_byte))) { // Skip leading 0 if (0 == serial_in_byte_counter) { - sonoff_bridge_expected_bytes = 2; // 0xA0, 0xA1, 0xA2 + SnfBridge.expected_bytes = 2; // 0xA0, 0xA1, 0xA2 if (serial_in_byte >= 0xA3) { - sonoff_bridge_expected_bytes = 11; // 0xA3, 0xA4, 0xA5 + SnfBridge.expected_bytes = 11; // 0xA3, 0xA4, 0xA5 } if (serial_in_byte == 0xA6) { - sonoff_bridge_expected_bytes = 0; // 0xA6 and up supported by Portisch firmware only + SnfBridge.expected_bytes = 0; // 0xA6 and up supported by Portisch firmware only serial_in_buffer[serial_in_byte_counter++] = 0xAA; - sonoff_bridge_receive_raw_flag = 1; + SnfBridge.receive_raw_flag = 1; } } serial_in_buffer[serial_in_byte_counter++] = serial_in_byte; - if ((sonoff_bridge_expected_bytes == serial_in_byte_counter) && (0x55 == serial_in_byte)) { // 0x55 - End of text + if ((SnfBridge.expected_bytes == serial_in_byte_counter) && (0x55 == serial_in_byte)) { // 0x55 - End of text SonoffBridgeReceived(); - sonoff_bridge_receive_flag = 0; + SnfBridge.receive_flag = 0; return 1; } } @@ -346,7 +354,7 @@ bool SonoffBridgeSerialInput(void) if (0xAA == serial_in_byte) { // 0xAA - Start of text serial_in_byte_counter = 0; serial_in_byte = 0; - sonoff_bridge_receive_flag = 1; + SnfBridge.receive_flag = 1; receive_len = 0; } return 0; @@ -406,9 +414,9 @@ void SonoffBridgeSend(uint8_t idx, uint8_t key) void SonoffBridgeLearn(uint8_t key) { - sonoff_bridge_learn_key = key; - sonoff_bridge_learn_active = 1; - sonoff_bridge_last_learn_time = millis(); + SnfBridge.learn_key = key; + SnfBridge.learn_active = 1; + SnfBridge.last_learn_time = millis(); Serial.write(0xAA); // Start of Text Serial.write(0xA1); // Start learning Serial.write(0x55); // End of Text @@ -418,80 +426,75 @@ void SonoffBridgeLearn(uint8_t key) * Commands \*********************************************************************************************/ -bool SonoffBridgeCommand(void) +void CmndRfBridge(void) // RfSync, RfLow, RfHigh, RfHost and RfCode { - char command [CMDSZ]; - bool serviced = true; + char *p; + char stemp [10]; + uint32_t code = 0; + uint8_t radix = 10; - int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic, kSonoffBridgeCommands); - if (-1 == command_code) { - serviced = false; // Unknown command + uint32_t set_index = XdrvMailbox.command_code *2; + + if (XdrvMailbox.data[0] == '#') { + XdrvMailbox.data++; + XdrvMailbox.data_len--; + radix = 16; } - else if ((command_code >= CMND_RFSYNC) && (command_code <= CMND_RFCODE)) { // RfSync, RfLow, RfHigh, RfHost and RfCode - char *p; - char stemp [10]; - uint32_t code = 0; - uint8_t radix = 10; - uint8_t set_index = command_code *2; - - if (XdrvMailbox.data[0] == '#') { - XdrvMailbox.data++; - XdrvMailbox.data_len--; - radix = 16; - } - - if (XdrvMailbox.data_len) { - code = strtol(XdrvMailbox.data, &p, radix); - if (code) { - if (CMND_RFCODE == command_code) { - sonoff_bridge_last_send_code = code; - SonoffBridgeSendCode(code); - } else { - if (1 == XdrvMailbox.payload) { - code = pgm_read_byte(kDefaultRfCode + set_index) << 8 | pgm_read_byte(kDefaultRfCode + set_index +1); - } - uint8_t msb = code >> 8; - uint8_t lsb = code & 0xFF; - if ((code > 0) && (code < 0x7FFF) && (msb != 0x55) && (lsb != 0x55)) { // Check for End of Text codes - Settings.rf_code[0][set_index] = msb; - Settings.rf_code[0][set_index +1] = lsb; - } + if (XdrvMailbox.data_len) { + code = strtol(XdrvMailbox.data, &p, radix); + if (code) { + if (CMND_RFCODE == XdrvMailbox.command_code) { + SnfBridge.last_send_code = code; + SonoffBridgeSendCode(code); + } else { + if (1 == XdrvMailbox.payload) { + code = pgm_read_byte(kDefaultRfCode + set_index) << 8 | pgm_read_byte(kDefaultRfCode + set_index +1); + } + uint8_t msb = code >> 8; + uint8_t lsb = code & 0xFF; + if ((code > 0) && (code < 0x7FFF) && (msb != 0x55) && (lsb != 0x55)) { // Check for End of Text codes + Settings.rf_code[0][set_index] = msb; + Settings.rf_code[0][set_index +1] = lsb; } } } - if (CMND_RFCODE == command_code) { - code = sonoff_bridge_last_send_code; - } else { - code = Settings.rf_code[0][set_index] << 8 | Settings.rf_code[0][set_index +1]; - } - if (10 == radix) { - snprintf_P(stemp, sizeof(stemp), PSTR("%d"), code); - } else { - snprintf_P(stemp, sizeof(stemp), PSTR("\"#%X\""), code); - } - Response_P(S_JSON_COMMAND_XVALUE, command, stemp); } - else if ((CMND_RFKEY == command_code) && (XdrvMailbox.index > 0) && (XdrvMailbox.index <= 16)) { + if (CMND_RFCODE == XdrvMailbox.command_code) { + code = SnfBridge.last_send_code; + } else { + code = Settings.rf_code[0][set_index] << 8 | Settings.rf_code[0][set_index +1]; + } + if (10 == radix) { + snprintf_P(stemp, sizeof(stemp), PSTR("%d"), code); + } else { + snprintf_P(stemp, sizeof(stemp), PSTR("\"#%X\""), code); + } + Response_P(S_JSON_COMMAND_XVALUE, XdrvMailbox.command, stemp); +} + +void CmndRfKey(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 16)) { unsigned long now = millis(); - if ((!sonoff_bridge_learn_active) || (now - sonoff_bridge_last_learn_time > 60100)) { - sonoff_bridge_learn_active = 0; + if ((!SnfBridge.learn_active) || (now - SnfBridge.last_learn_time > 60100)) { + SnfBridge.learn_active = 0; if (2 == XdrvMailbox.payload) { // Learn RF data SonoffBridgeLearn(XdrvMailbox.index); - Response_P(S_JSON_COMMAND_INDEX_SVALUE, command, XdrvMailbox.index, D_JSON_START_LEARNING); + ResponseCmndIdxChar(D_JSON_START_LEARNING); } else if (3 == XdrvMailbox.payload) { // Unlearn RF data Settings.rf_code[XdrvMailbox.index][0] = 0; // Reset sync_time MSB - Response_P(S_JSON_COMMAND_INDEX_SVALUE, command, XdrvMailbox.index, D_JSON_SET_TO_DEFAULT); + ResponseCmndIdxChar(D_JSON_SET_TO_DEFAULT); } else if (4 == XdrvMailbox.payload) { // Save RF data provided by RFSync, RfLow, RfHigh and last RfCode for (uint32_t i = 0; i < 6; i++) { Settings.rf_code[XdrvMailbox.index][i] = Settings.rf_code[0][i]; } - Settings.rf_code[XdrvMailbox.index][6] = (sonoff_bridge_last_send_code >> 16) & 0xff; - Settings.rf_code[XdrvMailbox.index][7] = (sonoff_bridge_last_send_code >> 8) & 0xff; - Settings.rf_code[XdrvMailbox.index][8] = sonoff_bridge_last_send_code & 0xff; - Response_P(S_JSON_COMMAND_INDEX_SVALUE, command, XdrvMailbox.index, D_JSON_SAVED); + Settings.rf_code[XdrvMailbox.index][6] = (SnfBridge.last_send_code >> 16) & 0xff; + Settings.rf_code[XdrvMailbox.index][7] = (SnfBridge.last_send_code >> 8) & 0xff; + Settings.rf_code[XdrvMailbox.index][8] = SnfBridge.last_send_code & 0xff; + ResponseCmndIdxChar(D_JSON_SAVED); } else if (5 == XdrvMailbox.payload) { // Show default or learned RF data uint8_t key = XdrvMailbox.index; uint8_t index = (0 == Settings.rf_code[key][0]) ? 0 : key; // Use default if sync_time MSB = 0 @@ -506,60 +509,52 @@ bool SonoffBridgeCommand(void) code |= Settings.rf_code[index][8]; } Response_P(PSTR("{\"%s%d\":{\"" D_JSON_SYNC "\":%d,\"" D_JSON_LOW "\":%d,\"" D_JSON_HIGH "\":%d,\"" D_JSON_DATA "\":\"%06X\"}}"), - command, XdrvMailbox.index, sync_time, low_time, high_time, code); + XdrvMailbox.command, XdrvMailbox.index, sync_time, low_time, high_time, code); } else { if ((1 == XdrvMailbox.payload) || (0 == Settings.rf_code[XdrvMailbox.index][0])) { // Test sync_time MSB SonoffBridgeSend(0, XdrvMailbox.index); // Send default RF data - Response_P(S_JSON_COMMAND_INDEX_SVALUE, command, XdrvMailbox.index, D_JSON_DEFAULT_SENT); + ResponseCmndIdxChar(D_JSON_DEFAULT_SENT); } else { SonoffBridgeSend(XdrvMailbox.index, 0); // Send learned RF data - Response_P(S_JSON_COMMAND_INDEX_SVALUE, command, XdrvMailbox.index, D_JSON_LEARNED_SENT); + ResponseCmndIdxChar(D_JSON_LEARNED_SENT); } } } else { - Response_P(S_JSON_COMMAND_INDEX_SVALUE, command, sonoff_bridge_learn_key, D_JSON_LEARNING_ACTIVE); + Response_P(S_JSON_COMMAND_INDEX_SVALUE, XdrvMailbox.command, SnfBridge.learn_key, D_JSON_LEARNING_ACTIVE); } } - else if (CMND_RFRAW == command_code) { - if (XdrvMailbox.data_len) { - if (XdrvMailbox.data_len < 6) { // On, Off - switch (XdrvMailbox.payload) { - case 0: // Receive Raw Off - SonoffBridgeSendCommand(0xA7); // Stop reading RF signals enabling iTead default RF handling - case 1: // Receive Raw On - sonoff_bridge_receive_raw_flag = XdrvMailbox.payload; - break; - case 166: // 0xA6 - Start reading RF signals disabling iTead default RF handling - case 167: // 0xA7 - Stop reading RF signals enabling iTead default RF handling - case 169: // 0xA9 - Start learning predefined protocols - case 176: // 0xB0 - Stop sniffing - case 177: // 0xB1 - Start sniffing - case 255: // 0xFF - Show firmware version - SonoffBridgeSendCommand(XdrvMailbox.payload); - sonoff_bridge_receive_raw_flag = 1; - break; - case 192: // 0xC0 - Beep - char beep[] = "AAC000C055\0"; - SerialSendRaw(beep); - break; - } - } else { - SerialSendRaw(RemoveSpace(XdrvMailbox.data)); - sonoff_bridge_receive_raw_flag = 1; - } - } - Response_P(S_JSON_COMMAND_SVALUE, command, GetStateText(sonoff_bridge_receive_raw_flag)); - } else serviced = false; // Unknown command - - return serviced; } -/*********************************************************************************************/ - -void SonoffBridgeInit(void) +void CmndRfRaw(void) { - sonoff_bridge_receive_raw_flag = 0; - SonoffBridgeSendCommand(0xA7); // Stop reading RF signals enabling iTead default RF handling + if (XdrvMailbox.data_len) { + if (XdrvMailbox.data_len < 6) { // On, Off + switch (XdrvMailbox.payload) { + case 0: // Receive Raw Off + SonoffBridgeSendCommand(0xA7); // Stop reading RF signals enabling iTead default RF handling + case 1: // Receive Raw On + SnfBridge.receive_raw_flag = XdrvMailbox.payload; + break; + case 166: // 0xA6 - Start reading RF signals disabling iTead default RF handling + case 167: // 0xA7 - Stop reading RF signals enabling iTead default RF handling + case 169: // 0xA9 - Start learning predefined protocols + case 176: // 0xB0 - Stop sniffing + case 177: // 0xB1 - Start sniffing + case 255: // 0xFF - Show firmware version + SonoffBridgeSendCommand(XdrvMailbox.payload); + SnfBridge.receive_raw_flag = 1; + break; + case 192: // 0xC0 - Beep + char beep[] = "AAC000C055\0"; + SerialSendRaw(beep); + break; + } + } else { + SerialSendRaw(RemoveSpace(XdrvMailbox.data)); + SnfBridge.receive_raw_flag = 1; + } + } + ResponseCmndStateText(SnfBridge.receive_raw_flag); } /*********************************************************************************************\ @@ -572,16 +567,23 @@ bool Xdrv06(uint8_t function) if (SONOFF_BRIDGE == my_module_type) { switch (function) { - case FUNC_INIT: - SonoffBridgeInit(); - break; - case FUNC_COMMAND: - result = SonoffBridgeCommand(); - break; case FUNC_SERIAL: result = SonoffBridgeSerialInput(); break; + case FUNC_COMMAND: + result = DecodeCommand(kSonoffBridgeCommands, SonoffBridgeCommand); + break; + case FUNC_INIT: + SnfBridge.receive_raw_flag = 0; + SonoffBridgeSendCommand(0xA7); // Stop reading RF signals enabling iTead default RF handling + break; + case FUNC_PRE_INIT: + Settings.flag.mqtt_serial = 0; + baudrate = 19200; + break; } } return result; } + +#endif // USE_SONOFF_RF diff --git a/sonoff/xdrv_07_domoticz.ino b/sonoff/xdrv_07_domoticz.ino index d67c771c3..e4f1b5ca1 100644 --- a/sonoff/xdrv_07_domoticz.ino +++ b/sonoff/xdrv_07_domoticz.ino @@ -21,30 +21,35 @@ #define XDRV_07 7 +#define D_PRFX_DOMOTICZ "Domoticz" +#define D_CMND_IDX "Idx" +#define D_CMND_KEYIDX "KeyIdx" +#define D_CMND_SWITCHIDX "SwitchIdx" +#define D_CMND_SENSORIDX "SensorIdx" +#define D_CMND_UPDATETIMER "UpdateTimer" + +const char kDomoticzCommands[] PROGMEM = D_PRFX_DOMOTICZ "|" // Prefix + D_CMND_IDX "|" D_CMND_KEYIDX "|" D_CMND_SWITCHIDX "|" D_CMND_SENSORIDX "|" D_CMND_UPDATETIMER ; + +void (* const DomoticzCommand[])(void) PROGMEM = { + &CmndDomoticzIdx, &CmndDomoticzKeyIdx, &CmndDomoticzSwitchIdx, &CmndDomoticzSensorIdx, &CmndDomoticzUpdateTimer }; + const char DOMOTICZ_MESSAGE[] PROGMEM = "{\"idx\":%d,\"nvalue\":%d,\"svalue\":\"%s\",\"Battery\":%d,\"RSSI\":%d}"; -enum DomoticzCommands { CMND_IDX, CMND_KEYIDX, CMND_SWITCHIDX, CMND_SENSORIDX, CMND_UPDATETIMER }; -const char kDomoticzCommands[] PROGMEM = D_CMND_IDX "|" D_CMND_KEYIDX "|" D_CMND_SWITCHIDX "|" D_CMND_SENSORIDX "|" D_CMND_UPDATETIMER ; - -//enum DomoticzSensors {DZ_TEMP, DZ_TEMP_HUM, DZ_TEMP_HUM_BARO, DZ_POWER_ENERGY, DZ_ILLUMINANCE, DZ_COUNT, DZ_VOLTAGE, DZ_CURRENT, DZ_AIRQUALITY, DZ_MAX_SENSORS}; - #if MAX_DOMOTICZ_SNS_IDX < DZ_MAX_SENSORS #error "Domoticz: Too many sensors or change settings.h layout" #endif const char kDomoticzSensors[] PROGMEM = - D_DOMOTICZ_TEMP "|" D_DOMOTICZ_TEMP_HUM "|" D_DOMOTICZ_TEMP_HUM_BARO "|" D_DOMOTICZ_POWER_ENERGY "|" D_DOMOTICZ_ILLUMINANCE "|" D_DOMOTICZ_COUNT "|" D_DOMOTICZ_VOLTAGE "|" D_DOMOTICZ_CURRENT "|" D_DOMOTICZ_AIRQUALITY ; - -const char S_JSON_DOMOTICZ_COMMAND_INDEX_NVALUE[] PROGMEM = "{\"" D_CMND_DOMOTICZ "%s%d\":%d}"; -const char S_JSON_DOMOTICZ_COMMAND_INDEX_LVALUE[] PROGMEM = "{\"" D_CMND_DOMOTICZ "%s%d\":%lu}"; + D_DOMOTICZ_TEMP "|" D_DOMOTICZ_TEMP_HUM "|" D_DOMOTICZ_TEMP_HUM_BARO "|" D_DOMOTICZ_POWER_ENERGY "|" D_DOMOTICZ_ILLUMINANCE "|" + D_DOMOTICZ_COUNT "|" D_DOMOTICZ_VOLTAGE "|" D_DOMOTICZ_CURRENT "|" D_DOMOTICZ_AIRQUALITY "|" D_DOMOTICZ_P1_SMART_METER "|" D_DOMOTICZ_SHUTTER ; char domoticz_in_topic[] = DOMOTICZ_IN_TOPIC; -char domoticz_out_topic[] = DOMOTICZ_OUT_TOPIC; -bool domoticz_subscribe = false; -uint8_t domoticz_update_flag = 1; int domoticz_update_timer = 0; -unsigned long fan_debounce = 0; // iFan02 state debounce timer +uint32_t domoticz_fan_debounce = 0; // iFan02 state debounce timer +bool domoticz_subscribe = false; +bool domoticz_update_flag = true; int DomoticzBatteryQuality(void) { @@ -52,8 +57,9 @@ int DomoticzBatteryQuality(void) // Battery 100%: ESP 3.6V (maximum operating voltage is 3.6) // Battery 101% to 200%: ESP over 3.6V (means over maximum operating voltage) - int quality = 0; // Voltage range from 2,6V > 0% to 3,6V > 100% + int quality = 100; // Voltage range from 2,6V > 0% to 3,6V > 100% +#ifdef USE_ADC_VCC uint16_t voltage = ESP.getVcc(); if (voltage <= 2600) { quality = 0; @@ -62,6 +68,7 @@ int DomoticzBatteryQuality(void) } else { quality = (voltage - 2600) / 10; } +#endif return quality; } @@ -72,6 +79,7 @@ int DomoticzRssiQuality(void) return WifiGetRssiAsQuality(WiFi.RSSI()) / 10; } +#ifdef USE_SONOFF_IFAN void MqttPublishDomoticzFanState() { if (Settings.flag.mqtt_enabled && Settings.domoticz_relay_idx[1]) { @@ -82,7 +90,7 @@ void MqttPublishDomoticzFanState() Response_P(DOMOTICZ_MESSAGE, (int)Settings.domoticz_relay_idx[1], (0 == fan_speed) ? 0 : 2, svalue, DomoticzBatteryQuality(), DomoticzRssiQuality()); MqttPublish(domoticz_in_topic); - fan_debounce = millis(); + domoticz_fan_debounce = millis(); } } @@ -91,23 +99,28 @@ void DomoticzUpdateFanState() if (domoticz_update_flag) { MqttPublishDomoticzFanState(); } - domoticz_update_flag = 1; + domoticz_update_flag = true; } +#endif // USE_SONOFF_IFAN void MqttPublishDomoticzPowerState(uint8_t device) { if (Settings.flag.mqtt_enabled) { if ((device < 1) || (device > devices_present)) { device = 1; } if (Settings.domoticz_relay_idx[device -1]) { - if ((SONOFF_IFAN02 == my_module_type) && (device > 1)) { +#ifdef USE_SONOFF_IFAN + if (IsModuleIfan() && (device > 1)) { // Fan handled by MqttPublishDomoticzFanState } else { +#endif // USE_SONOFF_IFAN char svalue[8]; // Dimmer value snprintf_P(svalue, sizeof(svalue), PSTR("%d"), Settings.light_dimmer); Response_P(DOMOTICZ_MESSAGE, (int)Settings.domoticz_relay_idx[device -1], (power & (1 << (device -1))) ? 1 : 0, (light_type) ? svalue : "", DomoticzBatteryQuality(), DomoticzRssiQuality()); MqttPublish(domoticz_in_topic); +#ifdef USE_SONOFF_IFAN } +#endif // USE_SONOFF_IFAN } } } @@ -117,7 +130,7 @@ void DomoticzUpdatePowerState(uint8_t device) if (domoticz_update_flag) { MqttPublishDomoticzPowerState(device); } - domoticz_update_flag = 1; + domoticz_update_flag = true; } void DomoticzMqttUpdate(void) @@ -127,12 +140,16 @@ void DomoticzMqttUpdate(void) if (domoticz_update_timer <= 0) { domoticz_update_timer = Settings.domoticz_update_timer; for (uint32_t i = 1; i <= devices_present; i++) { - if ((SONOFF_IFAN02 == my_module_type) && (i > 1)) { +#ifdef USE_SONOFF_IFAN + if (IsModuleIfan() && (i > 1)) { MqttPublishDomoticzFanState(); break; } else { +#endif // USE_SONOFF_IFAN MqttPublishDomoticzPowerState(i); +#ifdef USE_SONOFF_IFAN } +#endif // USE_SONOFF_IFAN } } } @@ -148,7 +165,7 @@ void DomoticzMqttSubscribe(void) } if (domoticz_subscribe) { char stopic[TOPSZ]; - snprintf_P(stopic, sizeof(stopic), PSTR("%s/#"), domoticz_out_topic); // domoticz topic + snprintf_P(stopic, sizeof(stopic), PSTR(DOMOTICZ_OUT_TOPIC "/#")); // domoticz topic MqttSubscribe(stopic); } } @@ -181,167 +198,118 @@ void DomoticzMqttSubscribe(void) bool DomoticzMqttData(void) { - char stemp1[10]; - unsigned long idx = 0; - int16_t nvalue = -1; - int16_t found = 0; + domoticz_update_flag = true; - domoticz_update_flag = 1; - if (!strncmp(XdrvMailbox.topic, domoticz_out_topic, strlen(domoticz_out_topic))) { - if (XdrvMailbox.data_len < 20) { - return 1; - } - StaticJsonBuffer<400> jsonBuf; - JsonObject& domoticz = jsonBuf.parseObject(XdrvMailbox.data); - if (!domoticz.success()) { - return 1; - } + if (strncasecmp_P(XdrvMailbox.topic, PSTR(DOMOTICZ_OUT_TOPIC), strlen(DOMOTICZ_OUT_TOPIC)) != 0) { + return false; // Process unchanged data + } + + // topic is domoticz/out so try to analyse + if (XdrvMailbox.data_len < 20) { + return true; // No valid data + } + StaticJsonBuffer<400> jsonBuf; + JsonObject& domoticz = jsonBuf.parseObject(XdrvMailbox.data); + if (!domoticz.success()) { + return true; // To much or invalid data + } // if (strcmp_P(domoticz["dtype"],PSTR("Light/Switch"))) { -// return 1; +// return true; // } - idx = domoticz["idx"]; - if (domoticz.containsKey("nvalue")) { - nvalue = domoticz["nvalue"]; - } + uint32_t idx = domoticz["idx"]; + int16_t nvalue = -1; + if (domoticz.containsKey("nvalue")) { + nvalue = domoticz["nvalue"]; + } - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_DOMOTICZ "idx %d, nvalue %d"), idx, nvalue); + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_DOMOTICZ "idx %d, nvalue %d"), idx, nvalue); - if ((idx > 0) && (nvalue >= 0) && (nvalue <= 15)) { - uint8_t maxdev = (devices_present > MAX_DOMOTICZ_IDX) ? MAX_DOMOTICZ_IDX : devices_present; - for (uint32_t i = 0; i < maxdev; i++) { - if (idx == Settings.domoticz_relay_idx[i]) { - bool iscolordimmer = strcmp_P(domoticz["dtype"],PSTR("Color Switch")) == 0; - snprintf_P(stemp1, sizeof(stemp1), PSTR("%d"), i +1); - if ((SONOFF_IFAN02 == my_module_type) && (1 == i)) { // Idx 2 is fanspeed - uint8_t svalue = 0; - if (domoticz.containsKey("svalue1")) { - svalue = domoticz["svalue1"]; - } else { - return 1; - } - svalue = (nvalue == 2) ? svalue / 10 : 0; - if (GetFanspeed() == svalue) { - return 1; // Stop loop as already set - } - if (TimePassedSince(fan_debounce) < 1000) { - return 1; // Stop loop if device in limbo - } - snprintf_P(XdrvMailbox.topic, XdrvMailbox.index, PSTR("/" D_CMND_FANSPEED)); - snprintf_P(XdrvMailbox.data, XdrvMailbox.data_len, PSTR("%d"), svalue); - found = 1; + bool found = false; + if ((idx > 0) && (nvalue >= 0) && (nvalue <= 15)) { + uint8_t maxdev = (devices_present > MAX_DOMOTICZ_IDX) ? MAX_DOMOTICZ_IDX : devices_present; + for (uint32_t i = 0; i < maxdev; i++) { + if (idx == Settings.domoticz_relay_idx[i]) { + bool iscolordimmer = strcmp_P(domoticz["dtype"],PSTR("Color Switch")) == 0; + char stemp1[10]; + snprintf_P(stemp1, sizeof(stemp1), PSTR("%d"), i +1); +#ifdef USE_SONOFF_IFAN + if (IsModuleIfan() && (1 == i)) { // Idx 2 is fanspeed + uint8_t svalue = 0; + if (domoticz.containsKey("svalue1")) { + svalue = domoticz["svalue1"]; + } else { + return true; // Invalid data } - else if (iscolordimmer && 10 == nvalue) { // Color_SetColor - JsonObject& color = domoticz["Color"]; - uint16_t level = nvalue = domoticz["svalue1"]; - uint16_t r = color["r"]; r = r * level / 100; - uint16_t g = color["g"]; g = g * level / 100; - uint16_t b = color["b"]; b = b * level / 100; - uint16_t cw = color["cw"]; cw = cw * level / 100; - uint16_t ww = color["ww"]; ww = ww * level / 100; - snprintf_P(XdrvMailbox.topic, XdrvMailbox.index, PSTR("/" D_CMND_COLOR)); - snprintf_P(XdrvMailbox.data, XdrvMailbox.data_len, PSTR("%02x%02x%02x%02x%02x"), r, g, b, cw, ww); - found = 1; + svalue = (nvalue == 2) ? svalue / 10 : 0; + if (GetFanspeed() == svalue) { + return true; // Stop loop as already set } - else if ((!iscolordimmer && 2 == nvalue) || // gswitch_sSetLevel - (iscolordimmer && 15 == nvalue)) { // Color_SetBrightnessLevel - if (domoticz.containsKey("svalue1")) { - nvalue = domoticz["svalue1"]; - } else { - return 1; - } - if (light_type && (Settings.light_dimmer == nvalue) && ((power >> i) &1)) { - return 1; - } - snprintf_P(XdrvMailbox.topic, XdrvMailbox.index, PSTR("/" D_CMND_DIMMER)); - snprintf_P(XdrvMailbox.data, XdrvMailbox.data_len, PSTR("%d"), nvalue); - found = 1; + if (TimePassedSince(domoticz_fan_debounce) < 1000) { + return true; // Stop loop if device in limbo } - else if (1 == nvalue || 0 == nvalue) { - if (((power >> i) &1) == (power_t)nvalue) { - return 1; // Stop loop - } - snprintf_P(XdrvMailbox.topic, XdrvMailbox.index, PSTR("/" D_CMND_POWER "%s"), (devices_present > 1) ? stemp1 : ""); - snprintf_P(XdrvMailbox.data, XdrvMailbox.data_len, PSTR("%d"), nvalue); - found = 1; - } - break; + snprintf_P(XdrvMailbox.topic, XdrvMailbox.index, PSTR("/" D_CMND_FANSPEED)); + snprintf_P(XdrvMailbox.data, XdrvMailbox.data_len, PSTR("%d"), svalue); + found = true; + } else +#endif // USE_SONOFF_IFAN + if (iscolordimmer && 10 == nvalue) { // Color_SetColor + JsonObject& color = domoticz["Color"]; + uint16_t level = nvalue = domoticz["svalue1"]; + uint16_t r = color["r"]; r = r * level / 100; + uint16_t g = color["g"]; g = g * level / 100; + uint16_t b = color["b"]; b = b * level / 100; + uint16_t cw = color["cw"]; cw = cw * level / 100; + uint16_t ww = color["ww"]; ww = ww * level / 100; + snprintf_P(XdrvMailbox.topic, XdrvMailbox.index, PSTR("/" D_CMND_COLOR)); + snprintf_P(XdrvMailbox.data, XdrvMailbox.data_len, PSTR("%02x%02x%02x%02x%02x"), r, g, b, cw, ww); + found = true; } + else if ((!iscolordimmer && 2 == nvalue) || // gswitch_sSetLevel + (iscolordimmer && 15 == nvalue)) { // Color_SetBrightnessLevel + if (domoticz.containsKey("svalue1")) { + nvalue = domoticz["svalue1"]; + } else { + return true; // Invalid data + } + if (light_type && (Settings.light_dimmer == nvalue) && ((power >> i) &1)) { + return true; // State already set + } + snprintf_P(XdrvMailbox.topic, XdrvMailbox.index, PSTR("/" D_CMND_DIMMER)); + snprintf_P(XdrvMailbox.data, XdrvMailbox.data_len, PSTR("%d"), nvalue); + found = true; + } + else if (1 == nvalue || 0 == nvalue) { + if (((power >> i) &1) == (power_t)nvalue) { + return true; // Stop loop + } + snprintf_P(XdrvMailbox.topic, XdrvMailbox.index, PSTR("/" D_CMND_POWER "%s"), (devices_present > 1) ? stemp1 : ""); + snprintf_P(XdrvMailbox.data, XdrvMailbox.data_len, PSTR("%d"), nvalue); + found = true; + } + break; } } - if (!found) { - return 1; - } - - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_DOMOTICZ D_RECEIVED_TOPIC " %s, " D_DATA " %s"), XdrvMailbox.topic, XdrvMailbox.data); - - domoticz_update_flag = 0; } - return 0; + if (!found) { return true; } // No command received + + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_DOMOTICZ D_RECEIVED_TOPIC " %s, " D_DATA " %s"), XdrvMailbox.topic, XdrvMailbox.data); + + domoticz_update_flag = false; + return false; // Process new data } -/*********************************************************************************************\ - * Commands -\*********************************************************************************************/ - -bool DomoticzCommand(void) -{ - char command [CMDSZ]; - bool serviced = true; - uint8_t dmtcz_len = strlen(D_CMND_DOMOTICZ); // Prep for string length change - - if (!strncasecmp_P(XdrvMailbox.topic, PSTR(D_CMND_DOMOTICZ), dmtcz_len)) { // Prefix - int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic +dmtcz_len, kDomoticzCommands); - if (-1 == command_code) { - serviced = false; // Unknown command - } - else if ((CMND_IDX == command_code) && (XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_DOMOTICZ_IDX)) { - if (XdrvMailbox.payload >= 0) { - Settings.domoticz_relay_idx[XdrvMailbox.index -1] = XdrvMailbox.payload; - restart_flag = 2; - } - Response_P(S_JSON_DOMOTICZ_COMMAND_INDEX_LVALUE, command, XdrvMailbox.index, Settings.domoticz_relay_idx[XdrvMailbox.index -1]); - } - else if ((CMND_KEYIDX == command_code) && (XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_DOMOTICZ_IDX)) { - if (XdrvMailbox.payload >= 0) { - Settings.domoticz_key_idx[XdrvMailbox.index -1] = XdrvMailbox.payload; - } - Response_P(S_JSON_DOMOTICZ_COMMAND_INDEX_LVALUE, command, XdrvMailbox.index, Settings.domoticz_key_idx[XdrvMailbox.index -1]); - } - else if ((CMND_SWITCHIDX == command_code) && (XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_DOMOTICZ_IDX)) { - if (XdrvMailbox.payload >= 0) { - Settings.domoticz_switch_idx[XdrvMailbox.index -1] = XdrvMailbox.payload; - } - Response_P(S_JSON_DOMOTICZ_COMMAND_INDEX_NVALUE, command, XdrvMailbox.index, Settings.domoticz_switch_idx[XdrvMailbox.index -1]); - } - else if ((CMND_SENSORIDX == command_code) && (XdrvMailbox.index > 0) && (XdrvMailbox.index <= DZ_MAX_SENSORS)) { - if (XdrvMailbox.payload >= 0) { - Settings.domoticz_sensor_idx[XdrvMailbox.index -1] = XdrvMailbox.payload; - } - Response_P(S_JSON_DOMOTICZ_COMMAND_INDEX_NVALUE, command, XdrvMailbox.index, Settings.domoticz_sensor_idx[XdrvMailbox.index -1]); - } - else if (CMND_UPDATETIMER == command_code) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { - Settings.domoticz_update_timer = XdrvMailbox.payload; - } - Response_P(PSTR("{\"" D_CMND_DOMOTICZ "%s\":%d}"), command, Settings.domoticz_update_timer); - } - else serviced = false; // Unknown command - } - else serviced = false; // Unknown command - - return serviced; -} +/*********************************************************************************************/ bool DomoticzSendKey(uint8_t key, uint8_t device, uint8_t state, uint8_t svalflg) { - bool result = 0; + bool result = false; if (device <= MAX_DOMOTICZ_IDX) { if ((Settings.domoticz_key_idx[device -1] || Settings.domoticz_switch_idx[device -1]) && (svalflg)) { Response_P(PSTR("{\"command\":\"switchlight\",\"idx\":%d,\"switchcmd\":\"%s\"}"), - (key) ? Settings.domoticz_switch_idx[device -1] : Settings.domoticz_key_idx[device -1], (state) ? (2 == state) ? "Toggle" : "On" : "Off"); + (key) ? Settings.domoticz_switch_idx[device -1] : Settings.domoticz_key_idx[device -1], (state) ? (POWER_TOGGLE == state) ? "Toggle" : "On" : "Off"); MqttPublish(domoticz_in_topic); - result = 1; + result = true; } } return result; @@ -373,15 +341,22 @@ uint8_t DomoticzHumidityState(char *hum) void DomoticzSensor(uint8_t idx, char *data) { if (Settings.domoticz_sensor_idx[idx]) { - char dmess[100]; + char dmess[128]; // {"idx":26700,"nvalue":0,"svalue":"22330.1;10234.4;22000.5;10243.4;1006;3000","Battery":100,"RSSI":10} memcpy(dmess, mqtt_data, sizeof(dmess)); if (DZ_AIRQUALITY == idx) { Response_P(PSTR("{\"idx\":%d,\"nvalue\":%s,\"Battery\":%d,\"RSSI\":%d}"), Settings.domoticz_sensor_idx[idx], data, DomoticzBatteryQuality(), DomoticzRssiQuality()); } else { + uint8_t nvalue = 0; +#ifdef USE_SHUTTER + if (DZ_SHUTTER == idx) { + uint8_t position = atoi(data); + nvalue = position < 2 ? 0 : (position == 100 ? 1 : 2); + } +#endif // USE_SHUTTER Response_P(DOMOTICZ_MESSAGE, - Settings.domoticz_sensor_idx[idx], 0, data, DomoticzBatteryQuality(), DomoticzRssiQuality()); + Settings.domoticz_sensor_idx[idx], nvalue, data, DomoticzBatteryQuality(), DomoticzRssiQuality()); } MqttPublish(domoticz_in_topic); memcpy(mqtt_data, dmess, sizeof(dmess)); @@ -416,6 +391,77 @@ void DomoticzSensorPowerEnergy(int power, char *energy) DomoticzSensor(DZ_POWER_ENERGY, data); } +void DomoticzSensorP1SmartMeter(char *usage1, char *usage2, char *return1, char *return2, int power) +{ + //usage1 = energy usage meter tariff 1, This is an incrementing counter + //usage2 = energy usage meter tariff 2, This is an incrementing counter + //return1 = energy return meter tariff 1, This is an incrementing counter + //return2 = energy return meter tariff 2, This is an incrementing counter + //power = if >= 0 actual usage power. if < 0 actual return power (Watt) + int consumed = power; + int produced = 0; + if (power < 0) { + consumed = 0; + produced = -power; + } + char data[64]; + snprintf_P(data, sizeof(data), PSTR("%s;%s;%s;%s;%d;%d"), usage1, usage2, return1, return2, consumed, produced); + DomoticzSensor(DZ_P1_SMART_METER, data); +} + +/*********************************************************************************************\ + * Commands +\*********************************************************************************************/ + +void CmndDomoticzIdx(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_DOMOTICZ_IDX)) { + if (XdrvMailbox.payload >= 0) { + Settings.domoticz_relay_idx[XdrvMailbox.index -1] = XdrvMailbox.payload; + restart_flag = 2; + } + ResponseCmndIdxNumber(Settings.domoticz_relay_idx[XdrvMailbox.index -1]); + } +} + +void CmndDomoticzKeyIdx(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_DOMOTICZ_IDX)) { + if (XdrvMailbox.payload >= 0) { + Settings.domoticz_key_idx[XdrvMailbox.index -1] = XdrvMailbox.payload; + } + ResponseCmndIdxNumber(Settings.domoticz_key_idx[XdrvMailbox.index -1]); + } +} + +void CmndDomoticzSwitchIdx(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_DOMOTICZ_IDX)) { + if (XdrvMailbox.payload >= 0) { + Settings.domoticz_switch_idx[XdrvMailbox.index -1] = XdrvMailbox.payload; + } + ResponseCmndIdxNumber(Settings.domoticz_switch_idx[XdrvMailbox.index -1]); + } +} + +void CmndDomoticzSensorIdx(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= DZ_MAX_SENSORS)) { + if (XdrvMailbox.payload >= 0) { + Settings.domoticz_sensor_idx[XdrvMailbox.index -1] = XdrvMailbox.payload; + } + ResponseCmndIdxNumber(Settings.domoticz_sensor_idx[XdrvMailbox.index -1]); + } +} + +void CmndDomoticzUpdateTimer(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { + Settings.domoticz_update_timer = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.domoticz_update_timer); +} + /*********************************************************************************************\ * Presentation \*********************************************************************************************/ @@ -470,7 +516,9 @@ void HandleDomoticzConfiguration(void) WSContentSend_P(HTTP_FORM_DOMOTICZ_SWITCH, i +1, i, Settings.domoticz_switch_idx[i]); } - if ((SONOFF_IFAN02 == my_module_type) && (1 == i)) { break; } +#ifdef USE_SONOFF_IFAN + if (IsModuleIfan() && (1 == i)) { break; } +#endif // USE_SONOFF_IFAN } for (uint32_t i = 0; i < DZ_MAX_SENSORS; i++) { WSContentSend_P(HTTP_FORM_DOMOTICZ_SENSOR, @@ -528,6 +576,12 @@ bool Xdrv07(uint8_t function) if (Settings.flag.mqtt_enabled) { switch (function) { + case FUNC_EVERY_SECOND: + DomoticzMqttUpdate(); + break; + case FUNC_MQTT_DATA: + result = DomoticzMqttData(); + break; #ifdef USE_WEBSERVER case FUNC_WEB_ADD_BUTTON: WSContentSend_P(HTTP_BTN_MENU_DOMOTICZ); @@ -536,24 +590,18 @@ bool Xdrv07(uint8_t function) WebServer->on("/" WEB_HANDLE_DOMOTICZ, HandleDomoticzConfiguration); break; #endif // USE_WEBSERVER - case FUNC_COMMAND: - result = DomoticzCommand(); - break; case FUNC_MQTT_SUBSCRIBE: DomoticzMqttSubscribe(); break; case FUNC_MQTT_INIT: domoticz_update_timer = 2; break; - case FUNC_MQTT_DATA: - result = DomoticzMqttData(); - break; - case FUNC_EVERY_SECOND: - DomoticzMqttUpdate(); - break; case FUNC_SHOW_SENSOR: // DomoticzSendSensor(); break; + case FUNC_COMMAND: + result = DecodeCommand(kDomoticzCommands, DomoticzCommand); + break; } } return result; diff --git a/sonoff/xdrv_08_serial_bridge.ino b/sonoff/xdrv_08_serial_bridge.ino index 080e014e7..117671d94 100644 --- a/sonoff/xdrv_08_serial_bridge.ino +++ b/sonoff/xdrv_08_serial_bridge.ino @@ -26,10 +26,13 @@ const uint8_t SERIAL_BRIDGE_BUFFER_SIZE = 130; -#include +const char kSerialBridgeCommands[] PROGMEM = "|" // No prefix + D_CMND_SSERIALSEND "|" D_CMND_SBAUDRATE; -enum SerialBridgeCommands { CMND_SSERIALSEND, CMND_SBAUDRATE }; -const char kSerialBridgeCommands[] PROGMEM = D_CMND_SSERIALSEND "|" D_CMND_SBAUDRATE; +void (* const SerialBridgeCommand[])(void) PROGMEM = + { &CmndSSerialSend, &CmndSBaudrate }; + +#include TasmotaSerial *SerialBridgeSerial = nullptr; @@ -67,15 +70,9 @@ void SerialBridgeInput(void) if (serial_bridge_in_byte_counter && (millis() > (serial_bridge_polling_window + SERIAL_POLLING))) { serial_bridge_buffer[serial_bridge_in_byte_counter] = 0; // Serial data completed - if (!serial_bridge_raw) { - Response_P(PSTR("{\"" D_JSON_SSERIALRECEIVED "\":\"%s\"}"), serial_bridge_buffer); - } else { - Response_P(PSTR("{\"" D_JSON_SSERIALRECEIVED "\":\"")); - for (uint32_t i = 0; i < serial_bridge_in_byte_counter; i++) { - ResponseAppend_P(PSTR("%02x"), serial_bridge_buffer[i]); - } - ResponseAppend_P(PSTR("\"}")); - } + char hex_char[(serial_bridge_in_byte_counter * 2) + 2]; + Response_P(PSTR(",\"" D_JSON_SSERIALRECEIVED "\":\"%s\"}"), + (serial_bridge_raw) ? ToHex_P((unsigned char*)serial_bridge_buffer, serial_bridge_in_byte_counter, hex_char, sizeof(hex_char)) : serial_bridge_buffer); MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_SSERIALRECEIVED)); XdrvRulesProcess(); serial_bridge_in_byte_counter = 0; @@ -89,7 +86,7 @@ void SerialBridgeInit(void) serial_bridge_active = false; if ((pin[GPIO_SBR_RX] < 99) && (pin[GPIO_SBR_TX] < 99)) { SerialBridgeSerial = new TasmotaSerial(pin[GPIO_SBR_RX], pin[GPIO_SBR_TX]); - if (SerialBridgeSerial->begin(Settings.sbaudrate * 1200)) { // Baud rate is stored div 1200 so it fits into one byte + if (SerialBridgeSerial->begin(Settings.sbaudrate * 300)) { // Baud rate is stored div 300 so it fits into 16 bits if (SerialBridgeSerial->hardwareSerial()) { ClaimSerial(); serial_bridge_buffer = serial_in_buffer; // Use idle serial buffer to save RAM @@ -106,16 +103,9 @@ void SerialBridgeInit(void) * Commands \*********************************************************************************************/ -bool SerialBridgeCommand(void) +void CmndSSerialSend(void) { - char command [CMDSZ]; - bool serviced = true; - - int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic, kSerialBridgeCommands); - if (-1 == command_code) { - serviced = false; // Unknown command - } - else if ((CMND_SSERIALSEND == command_code) && (XdrvMailbox.index > 0) && (XdrvMailbox.index <= 5)) { + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 5)) { serial_bridge_raw = (XdrvMailbox.index > 3); if (XdrvMailbox.data_len > 0) { if (1 == XdrvMailbox.index) { @@ -136,7 +126,7 @@ bool SerialBridgeCommand(void) char *codes = RemoveSpace(XdrvMailbox.data); int size = strlen(XdrvMailbox.data); - while (size > 0) { + while (size > 1) { strlcpy(stemp, codes, sizeof(stemp)); code = strtol(stemp, &p, 16); SerialBridgeSerial->write(code); // "AA004566" as hex values @@ -144,22 +134,19 @@ bool SerialBridgeCommand(void) codes += 2; } } - Response_P(S_JSON_COMMAND_SVALUE, command, D_JSON_DONE); + ResponseCmndDone(); } } - else if (CMND_SBAUDRATE == command_code) { - char *p; - int baud = strtol(XdrvMailbox.data, &p, 10); - if (baud >= 1200) { - baud /= 1200; // Make it a valid baudrate - Settings.sbaudrate = (1 == XdrvMailbox.payload) ? SOFT_BAUDRATE / 1200 : baud; - SerialBridgeSerial->begin(Settings.sbaudrate * 1200); // Reinitialize serial port with new baud rate - } - Response_P(S_JSON_COMMAND_NVALUE, command, Settings.sbaudrate * 1200); - } - else serviced = false; // Unknown command +} - return serviced; +void CmndSBaudrate(void) +{ + if (XdrvMailbox.payload >= 300) { + XdrvMailbox.payload /= 300; // Make it a valid baudrate + Settings.sbaudrate = XdrvMailbox.payload; + SerialBridgeSerial->begin(Settings.sbaudrate * 300); // Reinitialize serial port with new baud rate + } + ResponseCmndNumber(Settings.sbaudrate * 300); } /*********************************************************************************************\ @@ -179,7 +166,7 @@ bool Xdrv08(uint8_t function) SerialBridgeInit(); break; case FUNC_COMMAND: - result = SerialBridgeCommand(); + result = DecodeCommand(kSerialBridgeCommands, SerialBridgeCommand); break; } } diff --git a/sonoff/xdrv_09_timers.ino b/sonoff/xdrv_09_timers.ino index 4de82c817..130ef04c4 100644 --- a/sonoff/xdrv_09_timers.ino +++ b/sonoff/xdrv_09_timers.ino @@ -33,20 +33,24 @@ * Output 1..16 * Action 0 = Off, 1 = On, 2 = Toggle, 3 = Blink or Rule if USE_RULES enabled * + * Window allows Time offset for +/- 15 minutes max as long as Time is not within Window from 00:00 \*********************************************************************************************/ #define XDRV_09 9 -enum TimerCommands { CMND_TIMER, CMND_TIMERS +const char kTimerCommands[] PROGMEM = "|" // No prefix + D_CMND_TIMER "|" D_CMND_TIMERS #ifdef USE_SUNRISE -, CMND_LATITUDE, CMND_LONGITUDE + "|" D_CMND_LATITUDE "|" D_CMND_LONGITUDE #endif - }; -const char kTimerCommands[] PROGMEM = D_CMND_TIMER "|" D_CMND_TIMERS + ; + +void (* const TimerCommand[])(void) PROGMEM = { + &CmndTimer, &CmndTimers #ifdef USE_SUNRISE -"|" D_CMND_LATITUDE "|" D_CMND_LONGITUDE + , &CmndLatitude, &CmndLongitude #endif -; + }; uint16_t timer_last_minute = 60; int8_t timer_window[MAX_TIMERS] = { 0 }; @@ -133,7 +137,7 @@ void DuskTillDawn(uint8_t *hour_up,uint8_t *minute_up, uint8_t *hour_down, uint8 // double Zeitzone = 0; //Weltzeit // double Zeitzone = 1; //Winterzeit // double Zeitzone = 2.0; //Sommerzeit - float Zeitzone = ((float)time_timezone) / 60; + float Zeitzone = ((float)Rtc.time_timezone) / 60; float Zeitgleichung = BerechneZeitgleichung(&DK, T); float Zeitdifferenz = 12.0f*acosf((sinf(h) - sinf(B)*sinf(DK)) / (cosf(B)*cosf(DK)))/pi; float AufgangOrtszeit = 12.0f - Zeitdifferenz - Zeitgleichung; @@ -219,7 +223,7 @@ void ApplyTimerOffsets(Timer *duskdawn) duskdawn->time = timeBuffer; } -String GetSun(uint8_t dawn) +String GetSun(uint32_t dawn) { char stime[6]; @@ -232,7 +236,7 @@ String GetSun(uint8_t dawn) return String(stime); } -uint16_t SunMinutes(uint8_t dawn) +uint16_t SunMinutes(uint32_t dawn) { uint8_t hour[2]; uint8_t minute[2]; @@ -246,7 +250,7 @@ uint16_t SunMinutes(uint8_t dawn) /*******************************************************************************************/ -void TimerSetRandomWindow(uint8_t index) +void TimerSetRandomWindow(uint32_t index) { timer_window[index] = 0; if (Settings.timer[index].window) { @@ -265,28 +269,34 @@ void TimerEverySecond(void) if (!RtcTime.hour && !RtcTime.minute && !RtcTime.second) { TimerSetRandomWindows(); } // Midnight if (Settings.flag3.timers_enable && (uptime > 60) && (RtcTime.minute != timer_last_minute)) { // Execute from one minute after restart every minute only once timer_last_minute = RtcTime.minute; - int16_t time = (RtcTime.hour *60) + RtcTime.minute; + int32_t time = (RtcTime.hour *60) + RtcTime.minute; uint8_t days = 1 << (RtcTime.day_of_week -1); for (uint32_t i = 0; i < MAX_TIMERS; i++) { // if (Settings.timer[i].device >= devices_present) Settings.timer[i].data = 0; // Reset timer due to change in devices present Timer xtimer = Settings.timer[i]; - uint16_t set_time = xtimer.time; #ifdef USE_SUNRISE - if ((1 == xtimer.mode) || (2 == xtimer.mode)) { // Sunrise or Sunset + if ((1 == xtimer.mode) || (2 == xtimer.mode)) { // Sunrise or Sunset ApplyTimerOffsets(&xtimer); - set_time = xtimer.time; } #endif if (xtimer.arm) { - set_time += timer_window[i]; // Add random time offset - if (set_time < 0) { set_time = 0; } // Stay today; - if (set_time > 1439) { set_time = 1439; } + int32_t set_time = xtimer.time + timer_window[i]; // Add random time offset + if (set_time < 0) { + set_time = abs(timer_window[i]); // After midnight and within negative window so stay today but allow positive randomness; + } + if (set_time > 1439) { + set_time = xtimer.time - abs(timer_window[i]); // Before midnight and within positive window so stay today but allow negative randomness; + } + if (set_time > 1439) { set_time = 1439; } // Stay today + + DEBUG_DRIVER_LOG(PSTR("TIM: Timer %d, Time %d, Window %d, SetTime %d"), i +1, xtimer.time, timer_window[i], set_time); + if (time == set_time) { if (xtimer.days & days) { Settings.timer[i].arm = xtimer.repeat; #if defined(USE_RULES) || defined(USE_SCRIPT) - if (3 == xtimer.power) { // Blink becomes Rule disregarding device and allowing use of Backlog commands + if (POWER_BLINK == xtimer.power) { // Blink becomes Rule disregarding device and allowing use of Backlog commands Response_P(PSTR("{\"Clock\":{\"Timer\":%d}}"), i +1); XdrvRulesProcess(); } else @@ -300,24 +310,23 @@ void TimerEverySecond(void) } } -void PrepShowTimer(uint8_t index) +void PrepShowTimer(uint32_t index) { - char days[8] = { 0 }; - char sign[2] = { 0 }; - char soutput[80]; - Timer xtimer = Settings.timer[index -1]; + char days[8] = { 0 }; for (uint32_t i = 0; i < 7; i++) { uint8_t mask = 1 << i; snprintf(days, sizeof(days), "%s%d", days, ((xtimer.days & mask) > 0)); } + char soutput[80]; soutput[0] = '\0'; if (devices_present) { snprintf_P(soutput, sizeof(soutput), PSTR(",\"" D_JSON_TIMER_OUTPUT "\":%d"), xtimer.device +1); } #ifdef USE_SUNRISE + char sign[2] = { 0 }; int16_t hour = xtimer.time / 60; if ((1 == xtimer.mode) || (2 == xtimer.mode)) { // Sunrise or Sunset if (hour > 11) { @@ -337,20 +346,11 @@ void PrepShowTimer(uint8_t index) * Commands \*********************************************************************************************/ -bool TimerCommand(void) +void CmndTimer(void) { - char command[CMDSZ]; - char dataBufUc[XdrvMailbox.data_len]; - bool serviced = true; - uint8_t index = XdrvMailbox.index; - - UpperCase(dataBufUc, XdrvMailbox.data); - int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic, kTimerCommands); - if (-1 == command_code) { - serviced = false; // Unknown command - } - else if ((CMND_TIMER == command_code) && (index > 0) && (index <= MAX_TIMERS)) { - uint8_t error = 0; + uint32_t index = XdrvMailbox.index; + if ((index > 0) && (index <= MAX_TIMERS)) { + uint32_t error = 0; if (XdrvMailbox.data_len) { if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= MAX_TIMERS)) { if (XdrvMailbox.payload == 0) { @@ -363,6 +363,8 @@ bool TimerCommand(void) #if defined(USE_RULES)==0 && defined(USE_SCRIPT)==0 if (devices_present) { #endif + char dataBufUc[XdrvMailbox.data_len]; + UpperCase(dataBufUc, XdrvMailbox.data); StaticJsonBuffer<256> jsonBuffer; JsonObject& root = jsonBuffer.parseObject(dataBufUc); if (!root.success()) { @@ -453,60 +455,63 @@ bool TimerCommand(void) ResponseJsonEnd(); } } - else if (CMND_TIMERS == command_code) { - if (XdrvMailbox.data_len) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) { - Settings.flag3.timers_enable = XdrvMailbox.payload; - } - if (XdrvMailbox.payload == 2) { - Settings.flag3.timers_enable = !Settings.flag3.timers_enable; - } - } - - Response_P(S_JSON_COMMAND_SVALUE, command, GetStateText(Settings.flag3.timers_enable)); - MqttPublishPrefixTopic_P(RESULT_OR_STAT, command); - - uint8_t jsflg = 0; - uint8_t lines = 1; - for (uint32_t i = 0; i < MAX_TIMERS; i++) { - if (!jsflg) { - Response_P(PSTR("{\"" D_CMND_TIMERS "%d\":{"), lines++); - } else { - ResponseAppend_P(PSTR(",")); - } - jsflg++; - PrepShowTimer(i +1); - if (jsflg > 3) { - ResponseAppend_P(PSTR("}}")); - MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_TIMERS)); - jsflg = 0; - } - } - mqtt_data[0] = '\0'; - } -#ifdef USE_SUNRISE - else if (CMND_LONGITUDE == command_code) { - if (XdrvMailbox.data_len) { - Settings.longitude = (int)(CharToFloat(XdrvMailbox.data) *1000000); - } - char lbuff[33]; - dtostrfd(((float)Settings.longitude) /1000000, 6, lbuff); - Response_P(S_JSON_COMMAND_SVALUE, command, lbuff); - } - else if (CMND_LATITUDE == command_code) { - if (XdrvMailbox.data_len) { - Settings.latitude = (int)(CharToFloat(XdrvMailbox.data) *1000000); - } - char lbuff[33]; - dtostrfd(((float)Settings.latitude) /1000000, 6, lbuff); - Response_P(S_JSON_COMMAND_SVALUE, command, lbuff); - } -#endif - else serviced = false; // Unknown command - - return serviced; } +void CmndTimers(void) +{ + if (XdrvMailbox.data_len) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) { + Settings.flag3.timers_enable = XdrvMailbox.payload; + } + if (XdrvMailbox.payload == 2) { + Settings.flag3.timers_enable = !Settings.flag3.timers_enable; + } + } + + ResponseCmndStateText(Settings.flag3.timers_enable); + MqttPublishPrefixTopic_P(RESULT_OR_STAT, XdrvMailbox.command); + + uint32_t jsflg = 0; + uint32_t lines = 1; + for (uint32_t i = 0; i < MAX_TIMERS; i++) { + if (!jsflg) { + Response_P(PSTR("{\"" D_CMND_TIMERS "%d\":{"), lines++); + } else { + ResponseAppend_P(PSTR(",")); + } + jsflg++; + PrepShowTimer(i +1); + if (jsflg > 3) { + ResponseJsonEndEnd(); + MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_TIMERS)); + jsflg = 0; + } + } + mqtt_data[0] = '\0'; +} + +#ifdef USE_SUNRISE +void CmndLongitude(void) +{ + if (XdrvMailbox.data_len) { + Settings.longitude = (int)(CharToFloat(XdrvMailbox.data) *1000000); + } + char lbuff[33]; + dtostrfd(((float)Settings.longitude) /1000000, 6, lbuff); + ResponseCmndChar(lbuff); +} + +void CmndLatitude(void) +{ + if (XdrvMailbox.data_len) { + Settings.latitude = (int)(CharToFloat(XdrvMailbox.data) *1000000); + } + char lbuff[33]; + dtostrfd(((float)Settings.latitude) /1000000, 6, lbuff); + ResponseCmndChar(lbuff); +} +#endif // USE_SUNRISE + /*********************************************************************************************\ * Presentation \*********************************************************************************************/ @@ -782,7 +787,7 @@ bool Xdrv09(uint8_t function) TimerEverySecond(); break; case FUNC_COMMAND: - result = TimerCommand(); + result = DecodeCommand(kTimerCommands, TimerCommand); break; } return result; diff --git a/sonoff/xdrv_10_rules.ino b/sonoff/xdrv_10_rules.ino index e4da6939b..d5d074a6f 100644 --- a/sonoff/xdrv_10_rules.ino +++ b/sonoff/xdrv_10_rules.ino @@ -78,6 +78,7 @@ #define D_CMND_CALC_RESOLUTION "CalcRes" #define D_CMND_SUBSCRIBE "Subscribe" #define D_CMND_UNSUBSCRIBE "Unsubscribe" +#define D_CMND_IF "If" #define D_JSON_INITIATED "Initiated" @@ -96,7 +97,7 @@ const char kCompareOperators[] PROGMEM = "=\0>\0<\0|\0==!=>=<="; #ifdef USE_EXPRESSION #include // Import LinkedList library - const char kExpressionOperators[] PROGMEM = "+-*/%^"; + const char kExpressionOperators[] PROGMEM = "+-*/%^\0"; #define EXPRESSION_OPERATOR_ADD 0 #define EXPRESSION_OPERATOR_SUBTRACT 1 #define EXPRESSION_OPERATOR_MULTIPLY 2 @@ -106,10 +107,39 @@ const char kCompareOperators[] PROGMEM = "=\0>\0<\0|\0==!=>=<="; const uint8_t kExpressionOperatorsPriorities[] PROGMEM = {1, 1, 2, 2, 3, 4}; #define MAX_EXPRESSION_OPERATOR_PRIORITY 4 + + + #define LOGIC_OPERATOR_AND 1 + #define LOGIC_OPERATOR_OR 2 + + #define IF_BLOCK_INVALID -1 + #define IF_BLOCK_ANY 0 + #define IF_BLOCK_ELSEIF 1 + #define IF_BLOCK_ELSE 2 + #define IF_BLOCK_ENDIF 3 #endif // USE_EXPRESSION -enum RulesCommands { CMND_RULE, CMND_RULETIMER, CMND_EVENT, CMND_VAR, CMND_MEM, CMND_ADD, CMND_SUB, CMND_MULT, CMND_SCALE, CMND_CALC_RESOLUTION, CMND_SUBSCRIBE, CMND_UNSUBSCRIBE }; -const char kRulesCommands[] PROGMEM = D_CMND_RULE "|" D_CMND_RULETIMER "|" D_CMND_EVENT "|" D_CMND_VAR "|" D_CMND_MEM "|" D_CMND_ADD "|" D_CMND_SUB "|" D_CMND_MULT "|" D_CMND_SCALE "|" D_CMND_CALC_RESOLUTION "|" D_CMND_SUBSCRIBE "|" D_CMND_UNSUBSCRIBE ; +const char kRulesCommands[] PROGMEM = "|" // No prefix + D_CMND_RULE "|" D_CMND_RULETIMER "|" D_CMND_EVENT "|" D_CMND_VAR "|" D_CMND_MEM "|" + D_CMND_ADD "|" D_CMND_SUB "|" D_CMND_MULT "|" D_CMND_SCALE "|" D_CMND_CALC_RESOLUTION +#ifdef SUPPORT_MQTT_EVENT + "|" D_CMND_SUBSCRIBE "|" D_CMND_UNSUBSCRIBE +#endif +#ifdef SUPPORT_IF_STATEMENT + "|" D_CMND_IF +#endif + ; + +void (* const RulesCommand[])(void) PROGMEM = { + &CmndRule, &CmndRuleTimer, &CmndEvent, &CmndVariable, &CmndMemory, + &CmndAddition, &CmndSubtract, &CmndMultiply, &CmndScale, &CmndCalcResolution +#ifdef SUPPORT_MQTT_EVENT + , &CmndSubscribe, &CmndUnsubscribe +#endif +#ifdef SUPPORT_IF_STATEMENT + , &CmndIf +#endif + }; #ifdef SUPPORT_MQTT_EVENT #include // Import LinkedList library @@ -119,30 +149,34 @@ const char kRulesCommands[] PROGMEM = D_CMND_RULE "|" D_CMND_RULETIMER "|" D_CMN String Key; } MQTT_Subscription; LinkedList subscriptions; -#endif //SUPPORT_MQTT_EVENT +#endif // SUPPORT_MQTT_EVENT -String rules_event_value; -unsigned long rules_timer[MAX_RULE_TIMERS] = { 0 }; -uint8_t rules_quota = 0; -long rules_new_power = -1; -long rules_old_power = -1; -long rules_old_dimm = -1; +struct RULES { + String event_value; + unsigned long timer[MAX_RULE_TIMERS] = { 0 }; + uint32_t triggers[MAX_RULE_SETS] = { 0 }; + uint8_t trigger_count[MAX_RULE_SETS] = { 0 }; -uint32_t rules_triggers[MAX_RULE_SETS] = { 0 }; -uint16_t rules_last_minute = 60; -uint8_t rules_trigger_count[MAX_RULE_SETS] = { 0 }; -uint8_t rules_teleperiod = 0; + long new_power = -1; + long old_power = -1; + long old_dimm = -1; + + uint16_t last_minute = 60; + uint16_t vars_event = 0; + uint8_t mems_event = 0; + bool teleperiod = false; + + char event_data[100]; +} Rules; + +char rules_vars[MAX_RULE_VARS][33] = {{ 0 }}; -char event_data[100]; -char vars[MAX_RULE_VARS][33] = { 0 }; #if (MAX_RULE_VARS>16) #error MAX_RULE_VARS is bigger than 16 #endif #if (MAX_RULE_MEMS>5) #error MAX_RULE_MEMS is bigger than 5 #endif -uint16_t vars_event = 0; -uint8_t mems_event = 0; /*******************************************************************************************/ @@ -160,32 +194,23 @@ bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule) if (pos == -1) { return false; } // No # sign in rule String rule_task = rule.substring(0, pos); // "INA219" or "SYSTEM" - if (rules_teleperiod) { + if (Rules.teleperiod) { int ppos = rule_task.indexOf("TELE-"); // "TELE-INA219" or "INA219" if (ppos == -1) { return false; } // No pre-amble in rule rule_task = rule.substring(5, pos); // "INA219" or "SYSTEM" } - String rule_name = rule.substring(pos +1); // "CURRENT>0.100" or "BOOT" or "%var1%" or "MINUTE|5" + String rule_expr = rule.substring(pos +1); // "CURRENT>0.100" or "BOOT" or "%var1%" or "MINUTE|5" + String rule_name, rule_param; + int8_t compareOperator = parseCompareExpression(rule_expr, rule_name, rule_param); //Parse the compare expression.Return operator and the left, right part of expression - char compare_operator[3]; - int8_t compare = COMPARE_OPERATOR_NONE; - for (int32_t i = MAXIMUM_COMPARE_OPERATOR; i >= 0; i--) { - snprintf_P(compare_operator, sizeof(compare_operator), kCompareOperators + (i *2)); - if ((pos = rule_name.indexOf(compare_operator)) > 0) { - compare = i; - break; - } - } - - char rule_svalue[CMDSZ] = { 0 }; + char rule_svalue[80] = { 0 }; float rule_value = 0; - if (compare != COMPARE_OPERATOR_NONE) { - String rule_param = rule_name.substring(pos + strlen(compare_operator)); + if (compareOperator != COMPARE_OPERATOR_NONE) { for (uint32_t i = 0; i < MAX_RULE_VARS; i++) { snprintf_P(stemp, sizeof(stemp), PSTR("%%VAR%d%%"), i +1); if (rule_param.startsWith(stemp)) { - rule_param = vars[i]; + rule_param = rules_vars[i]; break; } } @@ -227,7 +252,6 @@ bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule) } else { rule_value = CharToFloat((char*)rule_svalue); // 0.1 - This saves 9k code over toFLoat()! } - rule_name = rule_name.substring(0, pos); // "CURRENT" } // Step2: Search rule_task and rule_name @@ -235,23 +259,33 @@ bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule) JsonObject &root = jsonBuf.parseObject(event); if (!root.success()) { return false; } // No valid JSON data - float value = 0; - const char* str_value = root[rule_task][rule_name]; + const char* str_value; + if ((pos = rule_name.indexOf("[")) > 0) { // "CURRENT[1]" + int rule_name_idx = rule_name.substring(pos +1).toInt(); + if ((rule_name_idx < 1) || (rule_name_idx > 6)) { // Allow indexes 1 to 6 + rule_name_idx = 1; + } + rule_name = rule_name.substring(0, pos); // "CURRENT" + str_value = root[rule_task][rule_name][rule_name_idx -1]; // "ENERGY" and "CURRENT" and 0 + } else { + str_value = root[rule_task][rule_name]; // "INA219" and "CURRENT" + } //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RUL: Task %s, Name %s, Value |%s|, TrigCnt %d, TrigSt %d, Source %s, Json %s"), -// rule_task.c_str(), rule_name.c_str(), rule_svalue, rules_trigger_count[rule_set], bitRead(rules_triggers[rule_set], rules_trigger_count[rule_set]), event.c_str(), (str_value) ? str_value : "none"); +// rule_task.c_str(), rule_name.c_str(), rule_svalue, Rules.trigger_count[rule_set], bitRead(Rules.triggers[rule_set], Rules.trigger_count[rule_set]), event.c_str(), (str_value) ? str_value : "none"); if (!root[rule_task][rule_name].success()) { return false; } // No value but rule_name is ok - rules_event_value = str_value; // Prepare %value% + Rules.event_value = str_value; // Prepare %value% // Step 3: Compare rule (value) + float value = 0; if (str_value) { value = CharToFloat((char*)str_value); int int_value = int(value); int int_rule_value = int(rule_value); - switch (compare) { + switch (compareOperator) { case COMPARE_OPERATOR_EXACT_DIVISION: match = (int_rule_value && (int_value % int_rule_value) == 0); break; @@ -283,19 +317,54 @@ bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule) if (bitRead(Settings.rule_once, rule_set)) { if (match) { // Only allow match state changes - if (!bitRead(rules_triggers[rule_set], rules_trigger_count[rule_set])) { - bitSet(rules_triggers[rule_set], rules_trigger_count[rule_set]); + if (!bitRead(Rules.triggers[rule_set], Rules.trigger_count[rule_set])) { + bitSet(Rules.triggers[rule_set], Rules.trigger_count[rule_set]); } else { match = false; } } else { - bitClear(rules_triggers[rule_set], rules_trigger_count[rule_set]); + bitClear(Rules.triggers[rule_set], Rules.trigger_count[rule_set]); } } return match; } +/********************************************************************************************/ +/* + * Parse a comparison expression. + * Get 3 parts - left expression, compare operator and right expression. + * Input: + * expr - A comparison expression like VAR1 >= MEM1 + 10 + * leftExpr - Used to accept returned left parts of expression + * rightExpr - Used to accept returned right parts of expression + * Output: + * leftExpr - Left parts of expression + * rightExpr - Right parts of expression + * Return: + * compare operator + * COMPARE_OPERATOR_NONE - failed + */ +int8_t parseCompareExpression(String &expr, String &leftExpr, String &rightExpr) +{ + char compare_operator[3]; + int8_t compare = COMPARE_OPERATOR_NONE; + leftExpr = expr; + int position; + for (int8_t i = MAXIMUM_COMPARE_OPERATOR; i >= 0; i--) { + snprintf_P(compare_operator, sizeof(compare_operator), kCompareOperators + (i *2)); + if ((position = expr.indexOf(compare_operator)) > 0) { + compare = i; + leftExpr = expr.substring(0, position); + leftExpr.trim(); + rightExpr = expr.substring(position + strlen(compare_operator)); + rightExpr.trim(); + break; + } + } + return compare; +} + /*******************************************************************************************/ bool RuleSetProcess(uint8_t rule_set, String &event_saved) @@ -309,7 +378,7 @@ bool RuleSetProcess(uint8_t rule_set, String &event_saved) String rules = Settings.rules[rule_set]; - rules_trigger_count[rule_set] = 0; + Rules.trigger_count[rule_set] = 0; int plen = 0; int plen2 = 0; bool stop_all_rules = false; @@ -337,7 +406,7 @@ bool RuleSetProcess(uint8_t rule_set, String &event_saved) String commands = rules.substring(pevt +4, plen); // "Backlog Dimmer 10;Color 100000" plen += 6; - rules_event_value = ""; + Rules.event_value = ""; String event = event_saved; //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RUL: Event |%s|, Rule |%s|, Command(s) |%s|"), event.c_str(), event_trigger.c_str(), commands.c_str()); @@ -348,10 +417,10 @@ bool RuleSetProcess(uint8_t rule_set, String &event_saved) ucommand.toUpperCase(); // if (!ucommand.startsWith("BACKLOG")) { commands = "backlog " + commands; } // Always use Backlog to prevent power race exception if (ucommand.indexOf("EVENT ") != -1) { commands = "backlog " + commands; } // Always use Backlog with event to prevent rule event loop exception - commands.replace(F("%value%"), rules_event_value); + commands.replace(F("%value%"), Rules.event_value); for (uint32_t i = 0; i < MAX_RULE_VARS; i++) { snprintf_P(stemp, sizeof(stemp), PSTR("%%var%d%%"), i +1); - commands.replace(stemp, vars[i]); + commands.replace(stemp, rules_vars[i]); } for (uint32_t i = 0; i < MAX_RULE_MEMS; i++) { snprintf_P(stemp, sizeof(stemp), PSTR("%%mem%d%%"), i +1); @@ -372,12 +441,15 @@ bool RuleSetProcess(uint8_t rule_set, String &event_saved) // Response_P(S_JSON_COMMAND_SVALUE, D_CMND_RULE, D_JSON_INITIATED); // MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_RULE)); - +#ifdef SUPPORT_IF_STATEMENT + char *pCmd = command; + RulesPreprocessCommand(pCmd); //Do pre-process for IF statement +#endif ExecuteCommand(command, SRC_RULE); serviced = true; if (stop_all_rules) { return serviced; } // If BREAK was used, Stop execution of this rule set } - rules_trigger_count[rule_set]++; + Rules.trigger_count[rule_set]++; } return serviced; } @@ -428,7 +500,7 @@ void RulesInit(void) bitWrite(Settings.rule_once, i, 0); } } - rules_teleperiod = 0; + Rules.teleperiod = false; } void RulesEvery50ms(void) @@ -436,12 +508,12 @@ void RulesEvery50ms(void) if (Settings.rule_enabled) { // Any rule enabled char json_event[120]; - if (-1 == rules_new_power) { rules_new_power = power; } - if (rules_new_power != rules_old_power) { - if (rules_old_power != -1) { + if (-1 == Rules.new_power) { Rules.new_power = power; } + if (Rules.new_power != Rules.old_power) { + if (Rules.old_power != -1) { for (uint32_t i = 0; i < devices_present; i++) { - uint8_t new_state = (rules_new_power >> i) &1; - if (new_state != ((rules_old_power >> i) &1)) { + uint8_t new_state = (Rules.new_power >> i) &1; + if (new_state != ((Rules.old_power >> i) &1)) { snprintf_P(json_event, sizeof(json_event), PSTR("{\"Power%d\":{\"State\":%d}}"), i +1, new_state); RulesProcessEvent(json_event); } @@ -449,7 +521,7 @@ void RulesEvery50ms(void) } else { // Boot time POWER OUTPUTS (Relays) Status for (uint32_t i = 0; i < devices_present; i++) { - uint8_t new_state = (rules_new_power >> i) &1; + uint8_t new_state = (Rules.new_power >> i) &1; snprintf_P(json_event, sizeof(json_event), PSTR("{\"Power%d\":{\"Boot\":%d}}"), i +1, new_state); RulesProcessEvent(json_event); } @@ -459,29 +531,29 @@ void RulesEvery50ms(void) if ((pin[GPIO_SWT1 +i] < 99) || ((pin[GPIO_TM16CLK] < 99) && (pin[GPIO_TM16DIO] < 99) && (pin[GPIO_TM16STB] < 99))) { #else if (pin[GPIO_SWT1 +i] < 99) { -#endif // USE_TM1638 +#endif // USE_TM1638 bool swm = ((FOLLOW_INV == Settings.switchmode[i]) || (PUSHBUTTON_INV == Settings.switchmode[i]) || (PUSHBUTTONHOLD_INV == Settings.switchmode[i])); snprintf_P(json_event, sizeof(json_event), PSTR("{\"" D_JSON_SWITCH "%d\":{\"Boot\":%d}}"), i +1, (swm ^ SwitchLastState(i))); RulesProcessEvent(json_event); } } } - rules_old_power = rules_new_power; + Rules.old_power = Rules.new_power; } - else if (rules_old_dimm != Settings.light_dimmer) { - if (rules_old_dimm != -1) { + else if (Rules.old_dimm != Settings.light_dimmer) { + if (Rules.old_dimm != -1) { snprintf_P(json_event, sizeof(json_event), PSTR("{\"Dimmer\":{\"State\":%d}}"), Settings.light_dimmer); } else { // Boot time DIMMER VALUE snprintf_P(json_event, sizeof(json_event), PSTR("{\"Dimmer\":{\"Boot\":%d}}"), Settings.light_dimmer); } RulesProcessEvent(json_event); - rules_old_dimm = Settings.light_dimmer; + Rules.old_dimm = Settings.light_dimmer; } - else if (event_data[0]) { + else if (Rules.event_data[0]) { char *event; char *parameter; - event = strtok_r(event_data, "=", ¶meter); // event_data = fanspeed=10 + event = strtok_r(Rules.event_data, "=", ¶meter); // Rules.event_data = fanspeed=10 if (event) { event = Trim(event); if (parameter) { @@ -490,27 +562,27 @@ void RulesEvery50ms(void) parameter = event + strlen(event); // '\0' } snprintf_P(json_event, sizeof(json_event), PSTR("{\"Event\":{\"%s\":\"%s\"}}"), event, parameter); - event_data[0] ='\0'; + Rules.event_data[0] ='\0'; RulesProcessEvent(json_event); } else { - event_data[0] ='\0'; + Rules.event_data[0] ='\0'; } } - else if (vars_event || mems_event){ - if (vars_event) { + else if (Rules.vars_event || Rules.mems_event){ + if (Rules.vars_event) { for (uint32_t i = 0; i < MAX_RULE_VARS; i++) { - if (bitRead(vars_event, i)) { - bitClear(vars_event, i); - snprintf_P(json_event, sizeof(json_event), PSTR("{\"Var%d\":{\"State\":%s}}"), i+1, vars[i]); + if (bitRead(Rules.vars_event, i)) { + bitClear(Rules.vars_event, i); + snprintf_P(json_event, sizeof(json_event), PSTR("{\"Var%d\":{\"State\":%s}}"), i+1, rules_vars[i]); RulesProcessEvent(json_event); break; } } } - if (mems_event) { + if (Rules.mems_event) { for (uint32_t i = 0; i < MAX_RULE_MEMS; i++) { - if (bitRead(mems_event, i)) { - bitClear(mems_event, i); + if (bitRead(Rules.mems_event, i)) { + bitClear(Rules.mems_event, i); snprintf_P(json_event, sizeof(json_event), PSTR("{\"Mem%d\":{\"State\":%s}}"), i+1, Settings.mems[i]); RulesProcessEvent(json_event); break; @@ -533,6 +605,10 @@ void RulesEvery50ms(void) case 5: strncpy_P(json_event, PSTR("{\"WIFI\":{\"Connected\":1}}"), sizeof(json_event)); break; case 6: strncpy_P(json_event, PSTR("{\"WIFI\":{\"Disconnected\":1}}"), sizeof(json_event)); break; case 7: strncpy_P(json_event, PSTR("{\"HTTP\":{\"Initialized\":1}}"), sizeof(json_event)); break; +#ifdef USE_SHUTTER + case 8: strncpy_P(json_event, PSTR("{\"SHUTTER\":{\"Moved\":1}}"), sizeof(json_event)); break; + case 9: strncpy_P(json_event, PSTR("{\"SHUTTER\":{\"Moving\":1}}"), sizeof(json_event)); break; +#endif // USE_SHUTTER } if (json_event[0]) { RulesProcessEvent(json_event); @@ -569,16 +645,16 @@ void RulesEverySecond(void) char json_event[120]; if (RtcTime.valid) { - if ((uptime > 60) && (RtcTime.minute != rules_last_minute)) { // Execute from one minute after restart every minute only once - rules_last_minute = RtcTime.minute; + if ((uptime > 60) && (RtcTime.minute != Rules.last_minute)) { // Execute from one minute after restart every minute only once + Rules.last_minute = RtcTime.minute; snprintf_P(json_event, sizeof(json_event), PSTR("{\"Time\":{\"Minute\":%d}}"), MinutesPastMidnight()); RulesProcessEvent(json_event); } } for (uint32_t i = 0; i < MAX_RULE_TIMERS; i++) { - if (rules_timer[i] != 0L) { // Timer active? - if (TimeReached(rules_timer[i])) { // Timer finished? - rules_timer[i] = 0L; // Turn off this timer + if (Rules.timer[i] != 0L) { // Timer active? + if (TimeReached(Rules.timer[i])) { // Timer finished? + Rules.timer[i] = 0L; // Turn off this timer snprintf_P(json_event, sizeof(json_event), PSTR("{\"Rules\":{\"Timer\":%d}}"), i +1); RulesProcessEvent(json_event); } @@ -599,14 +675,14 @@ void RulesSaveBeforeRestart(void) void RulesSetPower(void) { - rules_new_power = XdrvMailbox.index; + Rules.new_power = XdrvMailbox.index; } void RulesTeleperiod(void) { - rules_teleperiod = 1; + Rules.teleperiod = true; RulesProcess(); - rules_teleperiod = 0; + Rules.teleperiod = false; } #ifdef SUPPORT_MQTT_EVENT @@ -622,10 +698,10 @@ void RulesTeleperiod(void) */ bool RulesMqttData(void) { - bool serviced = false; - if (XdrvMailbox.data_len < 1 || XdrvMailbox.data_len > 128) { + if (XdrvMailbox.data_len < 1 || XdrvMailbox.data_len > 256) { return false; } + bool serviced = false; String sTopic = XdrvMailbox.topic; String sData = XdrvMailbox.data; //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RUL: MQTT Topic %s, Event %s"), XdrvMailbox.topic, XdrvMailbox.data); @@ -642,7 +718,7 @@ bool RulesMqttData(void) if (event_item.Key.length() == 0) { //If did not specify Key value = sData; } else { //If specified Key, need to parse Key/Value from JSON data - StaticJsonBuffer<400> jsonBuf; + StaticJsonBuffer<500> jsonBuf; JsonObject& jsonData = jsonBuf.parseObject(sData); String key1 = event_item.Key; String key2; @@ -660,7 +736,7 @@ bool RulesMqttData(void) } value.trim(); //Create an new event. Cannot directly call RulesProcessEvent(). - snprintf_P(event_data, sizeof(event_data), PSTR("%s=%s"), event_item.Event.c_str(), value.c_str()); + snprintf_P(Rules.event_data, sizeof(Rules.event_data), PSTR("%s=%s"), event_item.Event.c_str(), value.c_str()); } } return serviced; @@ -677,19 +753,19 @@ bool RulesMqttData(void) * Subscribe * Subscribe command without any parameter will list all topics currently subscribed. * Input: - * data - A char buffer with all the parameters - * data_len - Length of the parameters + * XdrvMailbox.data - A char buffer with all the parameters + * XdrvMailbox.data_len - Length of the parameters * Return: * A string include subscribed event, topic and key. */ -String RulesSubscribe(const char *data, int data_len) +void CmndSubscribe(void) { MQTT_Subscription subscription_item; String events; - if (data_len > 0) { - char parameters[data_len+1]; - memcpy(parameters, data, data_len); - parameters[data_len] = '\0'; + if (XdrvMailbox.data_len > 0) { + char parameters[XdrvMailbox.data_len+1]; + memcpy(parameters, XdrvMailbox.data, XdrvMailbox.data_len); + parameters[XdrvMailbox.data_len] = '\0'; String event_name, topic, key; char * pos = strtok(parameters, ","); @@ -748,28 +824,28 @@ String RulesSubscribe(const char *data, int data_len) + subscription_item.Key + "; "); } } - return events; + ResponseCmndChar(events.c_str()); } /********************************************************************************************/ /* * Unsubscribe specified MQTT event. If no event specified, Unsubscribe all. * Command Unsubscribe format: - * Unsubscribe [] + * UnSubscribe [] * Input: - * data - Event name - * data_len - Length of the parameters + * XdrvMailbox.data - Event name + * XdrvMailbox.data_len - Length of the parameters * Return: * list all the events unsubscribed. */ -String RulesUnsubscribe(const char * data, int data_len) +void CmndUnsubscribe(void) { MQTT_Subscription subscription_item; String events; - if (data_len > 0) { + if (XdrvMailbox.data_len > 0) { for (uint32_t index = 0; index < subscriptions.size(); index++) { subscription_item = subscriptions.get(index); - if (subscription_item.Event.equalsIgnoreCase(data)) { + if (subscription_item.Event.equalsIgnoreCase(XdrvMailbox.data)) { String stopic = subscription_item.Topic + "/#"; MqttUnsubscribe(stopic.c_str()); events = subscription_item.Event; @@ -778,7 +854,7 @@ String RulesUnsubscribe(const char * data, int data_len) } } } else { - //If did not specify the event name, unsubscribe all event + // If did not specify the event name, unsubscribe all event String stopic; while (subscriptions.size() > 0) { events.concat(subscriptions.get(0).Event + "; "); @@ -787,11 +863,49 @@ String RulesUnsubscribe(const char * data, int data_len) subscriptions.remove(0); } } - return events; + ResponseCmndChar(events.c_str()); } -#endif // SUPPORT_MQTT_EVENT + +#endif // SUPPORT_MQTT_EVENT #ifdef USE_EXPRESSION +/********************************************************************************************/ +/* + * Looking for matched bracket - ")" + * Search buffer from current loction, skip all nested bracket pairs, find the matched close bracket. + * Input: + * pStart - Point to a char buffer start with "(" + * Output: + * N/A + * Return: + * position of matched close bracket + */ +char * findClosureBracket(char * pStart) +{ + char * pointer = pStart + 1; + //Look for the matched closure parenthesis.")" + bool bFindClosures = false; + uint8_t matchClosures = 1; + while (*pointer) + { + if (*pointer == ')') { + matchClosures--; + if (matchClosures == 0) { + bFindClosures = true; + break; + } + } else if (*pointer == '(') { + matchClosures++; + } + pointer++; + } + if (bFindClosures) { + return pointer; + } else { + return nullptr; + } +} + /********************************************************************************************/ /* * Parse a number value @@ -809,6 +923,10 @@ bool findNextNumber(char * &pNumber, float &value) { bool bSucceed = false; String sNumber = ""; + if (*pNumber == '-') { + sNumber = "-"; + pNumber++; + } while (*pNumber) { if (isdigit(*pNumber) || (*pNumber == '.')) { sNumber += *pNumber; @@ -854,7 +972,7 @@ bool findNextVariableValue(char * &pVarname, float &value) if (sVarName.startsWith(F("VAR"))) { int index = sVarName.substring(3).toInt(); if (index > 0 && index <= MAX_RULE_VARS) { - value = CharToFloat(vars[index -1]); + value = CharToFloat(rules_vars[index -1]); } } else if (sVarName.startsWith(F("MEM"))) { int index = sVarName.substring(3).toInt(); @@ -886,7 +1004,7 @@ bool findNextVariableValue(char * &pVarname, float &value) /* * Find next object in expression and evaluate it * An object could be: - * - A float number start with a digit, like 0.787 + * - A float number start with a digit or minus, like 0.787, -3 * - A variable name, like VAR1, MEM3 * - An expression enclosed with a pair of round brackets, (.....) * Input: @@ -908,35 +1026,17 @@ bool findNextObjectValue(char * &pointer, float &value) pointer++; continue; } - if (isdigit(*pointer)) { //This object is a number + if (isdigit(*pointer) || (*pointer) == '-') { //This object is a number bSucceed = findNextNumber(pointer, value); break; } else if (isalpha(*pointer)) { //Should be a variable like VAR12, MEM1 bSucceed = findNextVariableValue(pointer, value); break; } else if (*pointer == '(') { //It is a sub expression bracketed with () - pointer++; - char * sub_exp_start = pointer; //Find out the sub expression between a pair of parenthesis. "()" - unsigned int sub_exp_len = 0; - //Look for the matched closure parenthesis.")" - bool bFindClosures = false; - uint8_t matchClosures = 1; - while (*pointer) - { - if (*pointer == ')') { - matchClosures--; - if (matchClosures == 0) { - sub_exp_len = pointer - sub_exp_start; - bFindClosures = true; - break; - } - } else if (*pointer == '(') { - matchClosures++; - } - pointer++; - } - if (bFindClosures) { - value = evaluateExpression(sub_exp_start, sub_exp_len); + char * closureBracket = findClosureBracket(pointer); //Get the position of closure bracket ")" + if (closureBracket != nullptr) { + value = evaluateExpression(pointer+1, closureBracket - pointer - 1); + pointer = closureBracket + 1; bSucceed = true; } break; @@ -970,10 +1070,16 @@ bool findNextOperator(char * &pointer, int8_t &op) pointer++; continue; } - if (char *pch = strchr(kExpressionOperators, *pointer)) { //If it is an operator - op = (int8_t)(pch - kExpressionOperators); - pointer++; - bSucceed = true; + op = EXPRESSION_OPERATOR_ADD; + const char *pch = kExpressionOperators; + char ch; + while ((ch = pgm_read_byte(pch++)) != '\0') { + if (ch == *pointer) { + bSucceed = true; + pointer++; + break; + } + op++; } break; } @@ -1081,7 +1187,7 @@ float evaluateExpression(const char * expression, unsigned int len) for (int32_t priority = MAX_EXPRESSION_OPERATOR_PRIORITY; priority>0; priority--) { int index = 0; while (index < operators.size()) { - if (priority == kExpressionOperatorsPriorities[(operators.get(index))]) { //need to calculate the operator first + if (priority == pgm_read_byte(kExpressionOperatorsPriorities + operators.get(index))) { //need to calculate the operator first //get current object value and remove the next object with current operator va = calculateTwoValues(object_values.get(index), object_values.remove(index + 1), operators.remove(index)); //Replace the current value with the result @@ -1093,19 +1199,485 @@ float evaluateExpression(const char * expression, unsigned int len) } return object_values.get(0); } -#endif //USE_EXPRESSION +#endif // USE_EXPRESSION -bool RulesCommand(void) +#ifdef SUPPORT_IF_STATEMENT +void CmndIf() { - char command[CMDSZ]; - bool serviced = true; - uint8_t index = XdrvMailbox.index; - - int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic, kRulesCommands); - if (-1 == command_code) { - serviced = false; // Unknown command + if (XdrvMailbox.data_len > 0) { + char parameters[XdrvMailbox.data_len+1]; + memcpy(parameters, XdrvMailbox.data, XdrvMailbox.data_len); + parameters[XdrvMailbox.data_len] = '\0'; + ProcessIfStatement(parameters); } - else if ((CMND_RULE == command_code) && (index > 0) && (index <= MAX_RULE_SETS)) { + ResponseCmndDone(); +} + +/********************************************************************************************/ +/* + * Evaluate a comparison expression. + * Get the logic value of expression, true or false + * Input: + * expression - A comparison expression like VAR1 >= MEM1 + 10 + * len - Length of expression + * Output: + * N/A + * Return: + * logic value of comparison expression + */ +bool evaluateComparisonExpression(const char *expression, int len) +{ + bool bResult = true; + char expbuf[len + 1]; + memcpy(expbuf, expression, len); + expbuf[len] = '\0'; + String compare_expression = expbuf; + String leftExpr, rightExpr; + int8_t compareOp = parseCompareExpression(compare_expression, leftExpr, rightExpr); + + double leftValue = evaluateExpression(leftExpr.c_str(), leftExpr.length()); + double rightValue = evaluateExpression(rightExpr.c_str(), rightExpr.length()); + switch (compareOp) { + case COMPARE_OPERATOR_EXACT_DIVISION: + bResult = (rightValue != 0 && leftValue == int(leftValue) + && rightValue == int(rightValue) && (int(leftValue) % int(rightValue)) == 0); + break; + case COMPARE_OPERATOR_EQUAL: + bResult = leftExpr.equalsIgnoreCase(rightExpr); // Compare strings - this also works for hexadecimals + break; + case COMPARE_OPERATOR_BIGGER: + bResult = (leftValue > rightValue); + break; + case COMPARE_OPERATOR_SMALLER: + bResult = (leftValue < rightValue); + break; + case COMPARE_OPERATOR_NUMBER_EQUAL: + bResult = (leftValue == rightValue); + break; + case COMPARE_OPERATOR_NOT_EQUAL: + bResult = (leftValue != rightValue); + break; + case COMPARE_OPERATOR_BIGGER_EQUAL: + bResult = (leftValue >= rightValue); + break; + case COMPARE_OPERATOR_SMALLER_EQUAL: + bResult = (leftValue <= rightValue); + break; + } + return bResult; +} + +/********************************************************************************************/ +/* + * Looking for a logical operator, either "AND" or "OR" + * A logical operator is expected at this moment. If we find something else, this function will fail. + * Input: + * pointer - Point to a char buffer + * op - Used to accpet the logical operator type + * Output: + * Pointer - pointer will forward to next character after the logical operator. + * op - The logical operator type we found + * Return: + * true - succeed + * false - failed + */ +bool findNextLogicOperator(char * &pointer, int8_t &op) +{ + bool bSucceed = false; + while (*pointer && isspace(*pointer)) { + //Skip spaces + pointer++; + } + if (*pointer) { + if (strncasecmp_P(pointer, PSTR("AND "), 4) == 0) { + op = LOGIC_OPERATOR_AND; + pointer += 4; + bSucceed = true; + } else if (strncasecmp_P(pointer, PSTR("OR "), 3) == 0) { + op = LOGIC_OPERATOR_OR; + pointer += 3; + bSucceed = true; + } + } + return bSucceed; +} + +/********************************************************************************************/ +/* + * Find next logical object and get its value + * A logical object could be: + * - A comparison expression. + * - A logical expression bracketed with a pair of parenthesis. + * Input: + * pointer - A char pointer point to a start of logical object + * value - Used to accept the result value + * Output: + * pointer - Pointer forward to next character after the object + * value - boolean type, the value of the logical object. + * Return: + * true - succeed + * false - failed + */ +bool findNextLogicObjectValue(char * &pointer, bool &value) +{ + bool bSucceed = false; + while (*pointer && isspace(*pointer)) { + //Skip leading spaces + pointer++; + } + char * pExpr = pointer; + while (*pointer) { + if (isalpha(*pointer) + && (strncasecmp_P(pointer, PSTR("AND "), 4) == 0 + || strncasecmp_P(pointer, PSTR("OR "), 3) == 0)) + { //We have a logic operator, should stop + value = evaluateComparisonExpression(pExpr, pointer - pExpr); + bSucceed = true; + break; + } else if (*pointer == '(') { //It is a sub expression bracketed with () + char * closureBracket = findClosureBracket(pointer); //Get the position of closure bracket ")" + if (closureBracket != nullptr) { + value = evaluateLogicalExpression(pointer+1, closureBracket - pointer - 1); + pointer = closureBracket + 1; + bSucceed = true; + } + break; + } + pointer++; + } + if (!bSucceed && pointer > pExpr) { + //The whole buffer is an comparison expression + value = evaluateComparisonExpression(pExpr, pointer - pExpr); + bSucceed = true; + } + return bSucceed; +} + +/********************************************************************************************/ +/* + * Evaluate a logical expression + * Logic expression is constructed with multiple comparison expressions and logical + * operators between them. For example: Mem1==0 AND (time > sunrise + 60). + * Parenthesis are allowed to change the priority of logical operators. + * Input: + * expression - A logical expression + * len - Length of the expression + * Output: + * N/A + * Return: + * boolean - the value of logical expression + */ +bool evaluateLogicalExpression(const char * expression, int len) +{ + bool bResult = false; + //Make a copy first + char expbuff[len + 1]; + memcpy(expbuff, expression, len); + expbuff[len] = '\0'; + + //snprintf_P(log_data, sizeof(log_data), PSTR("EvalLogic: |%s|"), expbuff); + //AddLog(LOG_LEVEL_DEBUG); + char * pointer = expbuff; + LinkedList values; + LinkedList logicOperators; + //Find first comparison expression + bool bValue; + if (findNextLogicObjectValue(pointer, bValue)) { + values.add(bValue); + } else { + return false; + } + int8_t op; + while (*pointer) { + if (findNextLogicOperator(pointer, op) + && (*pointer) && findNextLogicObjectValue(pointer, bValue)) + { + logicOperators.add(op); + values.add(bValue); + } else { + break; + } + } + //Calculate all "AND" first + int index = 0; + while (index < logicOperators.size()) { + if (logicOperators.get(index) == LOGIC_OPERATOR_AND) { + values.set(index, values.get(index) && values.get(index+1)); + values.remove(index + 1); + logicOperators.remove(index); + } else { + index++; + } + } + //Then, calculate all "OR" + index = 0; + while (index < logicOperators.size()) { + if (logicOperators.get(index) == LOGIC_OPERATOR_OR) { + values.set(index, values.get(index) || values.get(index+1)); + values.remove(index + 1); + logicOperators.remove(index); + } else { + index++; + } + } + return values.get(0); +} + +/********************************************************************************************/ +/* + * This function search in a buffer to find out an IF block start from current position + * Note: All the tokens found during the searching will be changed to NULL terminated string. + * Please make a copy before call this function if you still need it. + * Input: + * pointer - Point to a NULL end string buffer with the commands + * lenWord - Accept the length of block end word + * block_type - The block type you are looking for. + * Output: + * pointer - pointer point to the end of if block. + * lenWord - The length of block end word ("ENDIF", "ELSEIF", "ELSE") + * Return: + * The block type we find. + * IF_BLOCK_INVALID - Failed. + */ +int8_t findIfBlock(char * &pointer, int &lenWord, int8_t block_type) +{ + int8_t foundBlock = IF_BLOCK_INVALID; + //First break into words delimited by space or ";" + const char * word; + while (*pointer) { + if (!isalpha(*pointer)) { + pointer++; + continue; + } + word = pointer; + while (*pointer && isalpha(*pointer)) { + pointer++; + } + lenWord = pointer - word; + + if (2 == lenWord && 0 == strncasecmp_P(word, PSTR("IF"), 2)) { + //if we find a new "IF" that means this is nested if block + //Try to finish this nested if block + if (findIfBlock(pointer, lenWord, IF_BLOCK_ENDIF) != IF_BLOCK_ENDIF) { + //If failed, we done. + break; + } + } else if ( (IF_BLOCK_ENDIF == block_type || IF_BLOCK_ANY == block_type) + && (5 == lenWord) && (0 == strncasecmp_P(word, PSTR("ENDIF"), 5))) + { + //Find an "ENDIF" + foundBlock = IF_BLOCK_ENDIF; + break; + } else if ( (IF_BLOCK_ELSEIF == block_type || IF_BLOCK_ANY == block_type) + && (6 == lenWord) && (0 == strncasecmp_P(word, PSTR("ELSEIF"), 6))) + { + //Find an "ELSEIF" + foundBlock = IF_BLOCK_ELSEIF; + break; + } else if ( (IF_BLOCK_ELSE == block_type || IF_BLOCK_ANY == block_type) + && (4 == lenWord) && (0 == strncasecmp_P(word, PSTR("ELSE"), 4))) + { + //Find an "ELSE" + foundBlock = IF_BLOCK_ELSE; + break; + } + } + return foundBlock; +} + +/********************************************************************************************/ +/* + * This function is used to execute a commands block in if statement when one of the condition is true. + * Input: + * commands - A char buffer include (but not limited) the commands block need to execute + * len - Length of the commands block + * Output: + N/A + * Return: + * void + */ +void ExecuteCommandBlock(const char * commands, int len) +{ + char cmdbuff[len + 1]; //apply enough space + memcpy(cmdbuff, commands, len); + cmdbuff[len] = '\0'; + + //snprintf_P(log_data, sizeof(log_data), PSTR("ExecCmd: |%s|"), cmdbuff); + //AddLog(LOG_LEVEL_DEBUG); + char oneCommand[len + 1]; //To put one command + int insertPosition = 0; //When insert into backlog, we should do it by 0, 1, 2 ... + char * pos = cmdbuff; + int lenEndBlock = 0; + while (*pos) { + if (isspace(*pos) || '\x1e' == *pos || ';' == *pos) { + pos++; + continue; + } + if (strncasecmp_P(pos, PSTR("BACKLOG "), 8) == 0) { + //Skip "BACKLOG " and set not first command flag. So all followed command will be send to backlog + pos += 8; + continue; + } + if (strncasecmp_P(pos, PSTR("IF "), 3) == 0) { + //Has a nested IF statement + //Find the matched ENDIF + char *pEndif = pos + 3; //Skip "IF " + if (IF_BLOCK_ENDIF != findIfBlock(pEndif, lenEndBlock, IF_BLOCK_ENDIF)) { + //Cannot find matched endif, stop execution. + break; + } + //We has the whole IF statement, copy to oneCommand + memcpy(oneCommand, pos, pEndif - pos); + oneCommand[pEndif - pos] = '\0'; + pos = pEndif; + } else { //Normal command + //Looking for the command end single - '\x1e' + char *pEndOfCommand = strpbrk(pos, "\x1e;"); + if (NULL == pEndOfCommand) { + pEndOfCommand = pos + strlen(pos); + } + memcpy(oneCommand, pos, pEndOfCommand - pos); + oneCommand[pEndOfCommand - pos] = '\0'; + pos = pEndOfCommand; + } + //Start to process current command we found + //Going to insert the command into backlog + String sCurrentCommand = oneCommand; + sCurrentCommand.trim(); + if (sCurrentCommand.length() > 0 + && backlog.size() < MAX_BACKLOG && !backlog_mutex) + { + //Insert into backlog + backlog_mutex = true; + backlog.add(insertPosition, sCurrentCommand); + backlog_mutex = false; + insertPosition++; + } + } + return; +} + +/********************************************************************************************/ +/* + * Execute IF statement. This is the place to run a "IF ..." command. + * Input: + * statements - The IF statement we are going to process + * Output: + N/A + * Return: + * void + */ +void ProcessIfStatement(const char* statements) +{ + String conditionExpression; + int len = strlen(statements); + char statbuff[len + 1]; + memcpy(statbuff, statements, len + 1); + char *pos = statbuff; + int lenEndBlock = 0; + while (true) { //Each loop process one IF (or ELSEIF) block + //Find and test the condition expression followed the IF or ELSEIF + //Search for the open bracket first + while (*pos && *pos != '(') { + pos++; + } + if (0 == *pos) { break; } + char * posEnd = findClosureBracket(pos); + + if (true == evaluateLogicalExpression(pos + 1, posEnd - (pos + 1))) { + //Looking for matched "ELSEIF", "ELSE" or "ENDIF", then Execute this block + char * cmdBlockStart = posEnd + 1; + char * cmdBlockEnd = cmdBlockStart; + int8_t nextBlock = findIfBlock(cmdBlockEnd, lenEndBlock, IF_BLOCK_ANY); + if (IF_BLOCK_INVALID == nextBlock) { + //Failed + break; + } + ExecuteCommandBlock(cmdBlockStart, cmdBlockEnd - cmdBlockStart - lenEndBlock); + pos = cmdBlockEnd; + break; + } else { //Does not match the IF condition, going to check elseif and else + pos = posEnd + 1; + int8_t nextBlock = findIfBlock(pos, lenEndBlock, IF_BLOCK_ANY); + if (IF_BLOCK_ELSEIF == nextBlock) { + //Continue process next ELSEIF block like IF + continue; + } else if (IF_BLOCK_ELSE == nextBlock) { + //Looking for matched "ENDIF" then execute this block + char * cmdBlockEnd = pos; + int8_t nextBlock = findIfBlock(cmdBlockEnd, lenEndBlock, IF_BLOCK_ENDIF); + if (IF_BLOCK_ENDIF != nextBlock) { + //Failed + break; + } + ExecuteCommandBlock(pos, cmdBlockEnd - pos - lenEndBlock); + break; + } else { // IF_BLOCK_ENDIF == nextBlock + //We done + break; + } + } + } +} + +/********************************************************************************************/ +/* + * This function is called in Rules event handler to process any command between DO ... ENDON (BREAK) + * - Do escape (convert ";" into "\x1e") for all IF statements. + * Input: + * commands - The commands block need to execute + * Output: + N/A + * Return: + * void + */ +void RulesPreprocessCommand(char *pCommands) +{ + char * cmd = pCommands; + int lenEndBlock = 0; + while (*cmd) { + //Skip all ";" and space between two commands + if (';' == *cmd || isspace(*cmd)) { + cmd++; + } + else if (strncasecmp_P(cmd, PSTR("IF "), 3) == 0) { //found IF block + //We are going to look for matched "ENDIF" + char * pIfStart = cmd; + char * pIfEnd = pIfStart + 3; //Skip "IF " + //int pIfStart = cmd - command; //"IF" statement block start at position (relative to command start) + if (IF_BLOCK_ENDIF == findIfBlock(pIfEnd, lenEndBlock, IF_BLOCK_ENDIF)) { + //Found the ENDIF + cmd = pIfEnd; //Will continue process from here + //Escapte from ";" to "\x1e". + //By remove all ";" in IF statement block, we can prevent backlog command cut the whole block as multiple commands + while (pIfStart < pIfEnd) { + if (';' == *pIfStart) + *pIfStart = '\x1e'; + pIfStart++; + } + } + else { //Did not find the matched ENDIF, stop processing + break; + } + } + else { //Other commands, skip it + while (*cmd && ';' != *cmd) { + cmd++; + } + } + } + return; +} +#endif //SUPPORT_IF_STATEMENT + +/*********************************************************************************************\ + * Commands +\*********************************************************************************************/ + +void CmndRule(void) +{ + uint8_t index = XdrvMailbox.index; + if ((index > 0) && (index <= MAX_RULE_SETS)) { if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.rules[index -1]))) { if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 10)) { switch (XdrvMailbox.payload) { @@ -1145,86 +1717,142 @@ bool RulesCommand(void) strlcpy(Settings.rules[index -1] + offset, ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data, sizeof(Settings.rules[index -1])); } } - rules_triggers[index -1] = 0; // Reset once flag + Rules.triggers[index -1] = 0; // Reset once flag } snprintf_P (mqtt_data, sizeof(mqtt_data), PSTR("{\"%s%d\":\"%s\",\"Once\":\"%s\",\"StopOnError\":\"%s\",\"Free\":%d,\"Rules\":\"%s\"}"), - command, index, GetStateText(bitRead(Settings.rule_enabled, index -1)), GetStateText(bitRead(Settings.rule_once, index -1)), + XdrvMailbox.command, index, GetStateText(bitRead(Settings.rule_enabled, index -1)), GetStateText(bitRead(Settings.rule_once, index -1)), GetStateText(bitRead(Settings.rule_stop, index -1)), sizeof(Settings.rules[index -1]) - strlen(Settings.rules[index -1]) -1, Settings.rules[index -1]); } - else if ((CMND_RULETIMER == command_code) && (index > 0) && (index <= MAX_RULE_TIMERS)) { +} + +void CmndRuleTimer(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_RULE_TIMERS)) { if (XdrvMailbox.data_len > 0) { #ifdef USE_EXPRESSION float timer_set = evaluateExpression(XdrvMailbox.data, XdrvMailbox.data_len); - rules_timer[index -1] = (timer_set > 0) ? millis() + (1000 * timer_set) : 0; + Rules.timer[XdrvMailbox.index -1] = (timer_set > 0) ? millis() + (1000 * timer_set) : 0; #else - rules_timer[index -1] = (XdrvMailbox.payload > 0) ? millis() + (1000 * XdrvMailbox.payload) : 0; -#endif //USE_EXPRESSION + Rules.timer[XdrvMailbox.index -1] = (XdrvMailbox.payload > 0) ? millis() + (1000 * XdrvMailbox.payload) : 0; +#endif // USE_EXPRESSION } mqtt_data[0] = '\0'; for (uint32_t i = 0; i < MAX_RULE_TIMERS; i++) { - ResponseAppend_P(PSTR("%c\"T%d\":%d"), (i) ? ',' : '{', i +1, (rules_timer[i]) ? (rules_timer[i] - millis()) / 1000 : 0); + ResponseAppend_P(PSTR("%c\"T%d\":%d"), (i) ? ',' : '{', i +1, (Rules.timer[i]) ? (Rules.timer[i] - millis()) / 1000 : 0); } ResponseJsonEnd(); } - else if (CMND_EVENT == command_code) { - if (XdrvMailbox.data_len > 0) { - strlcpy(event_data, XdrvMailbox.data, sizeof(event_data)); - } - Response_P(S_JSON_COMMAND_SVALUE, command, D_JSON_DONE); +} + +void CmndEvent(void) +{ + if (XdrvMailbox.data_len > 0) { + strlcpy(Rules.event_data, XdrvMailbox.data, sizeof(Rules.event_data)); } - else if ((CMND_VAR == command_code) && (index > 0) && (index <= MAX_RULE_VARS)) { - if (XdrvMailbox.data_len > 0) { + ResponseCmndDone(); +} + +void CmndVariable(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_RULE_VARS)) { + if (!XdrvMailbox.usridx) { + mqtt_data[0] = '\0'; + for (uint32_t i = 0; i < MAX_RULE_VARS; i++) { + ResponseAppend_P(PSTR("%c\"Var%d\":\"%s\""), (i) ? ',' : '{', i +1, rules_vars[i]); + } + ResponseJsonEnd(); + } else { + if (XdrvMailbox.data_len > 0) { #ifdef USE_EXPRESSION - dtostrfd(evaluateExpression(XdrvMailbox.data, XdrvMailbox.data_len), Settings.flag2.calc_resolution, vars[index -1]); + if (XdrvMailbox.data[0] == '=') { // Spaces already been skipped in data + dtostrfd(evaluateExpression(XdrvMailbox.data + 1, XdrvMailbox.data_len - 1), Settings.flag2.calc_resolution, rules_vars[XdrvMailbox.index -1]); + } else { + strlcpy(rules_vars[XdrvMailbox.index -1], ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data, sizeof(rules_vars[XdrvMailbox.index -1])); + } #else - strlcpy(vars[index -1], ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data, sizeof(vars[index -1])); -#endif //USE_EXPRESSION - bitSet(vars_event, index -1); + strlcpy(rules_vars[XdrvMailbox.index -1], ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data, sizeof(rules_vars[XdrvMailbox.index -1])); +#endif // USE_EXPRESSION + bitSet(Rules.vars_event, XdrvMailbox.index -1); + } + ResponseCmndIdxChar(rules_vars[XdrvMailbox.index -1]); } - Response_P(S_JSON_COMMAND_INDEX_SVALUE, command, index, vars[index -1]); } - else if ((CMND_MEM == command_code) && (index > 0) && (index <= MAX_RULE_MEMS)) { - if (XdrvMailbox.data_len > 0) { +} + +void CmndMemory(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_RULE_MEMS)) { + if (!XdrvMailbox.usridx) { + mqtt_data[0] = '\0'; + for (uint32_t i = 0; i < MAX_RULE_MEMS; i++) { + ResponseAppend_P(PSTR("%c\"Mem%d\":\"%s\""), (i) ? ',' : '{', i +1, Settings.mems[i]); + } + ResponseJsonEnd(); + } else { + if (XdrvMailbox.data_len > 0) { #ifdef USE_EXPRESSION - dtostrfd(evaluateExpression(XdrvMailbox.data, XdrvMailbox.data_len), Settings.flag2.calc_resolution, Settings.mems[index -1]); + if (XdrvMailbox.data[0] == '=') { // Spaces already been skipped in data + dtostrfd(evaluateExpression(XdrvMailbox.data + 1, XdrvMailbox.data_len - 1), Settings.flag2.calc_resolution, Settings.mems[XdrvMailbox.index -1]); + } else { + strlcpy(Settings.mems[XdrvMailbox.index -1], ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data, sizeof(Settings.mems[XdrvMailbox.index -1])); + } #else - strlcpy(Settings.mems[index -1], ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data, sizeof(Settings.mems[index -1])); -#endif //USE_EXPRESSION - bitSet(mems_event, index -1); + strlcpy(Settings.mems[XdrvMailbox.index -1], ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data, sizeof(Settings.mems[XdrvMailbox.index -1])); +#endif // USE_EXPRESSION + bitSet(Rules.mems_event, XdrvMailbox.index -1); + } + ResponseCmndIdxChar(Settings.mems[XdrvMailbox.index -1]); } - Response_P(S_JSON_COMMAND_INDEX_SVALUE, command, index, Settings.mems[index -1]); } - else if (CMND_CALC_RESOLUTION == command_code) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 7)) { - Settings.flag2.calc_resolution = XdrvMailbox.payload; - } - Response_P(S_JSON_COMMAND_NVALUE, command, Settings.flag2.calc_resolution); +} + +void CmndCalcResolution(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 7)) { + Settings.flag2.calc_resolution = XdrvMailbox.payload; } - else if ((CMND_ADD == command_code) && (index > 0) && (index <= MAX_RULE_VARS)) { + ResponseCmndNumber(Settings.flag2.calc_resolution); +} + +void CmndAddition(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_RULE_VARS)) { if (XdrvMailbox.data_len > 0) { - float tempvar = CharToFloat(vars[index -1]) + CharToFloat(XdrvMailbox.data); - dtostrfd(tempvar, Settings.flag2.calc_resolution, vars[index -1]); - bitSet(vars_event, index -1); + float tempvar = CharToFloat(rules_vars[XdrvMailbox.index -1]) + CharToFloat(XdrvMailbox.data); + dtostrfd(tempvar, Settings.flag2.calc_resolution, rules_vars[XdrvMailbox.index -1]); + bitSet(Rules.vars_event, XdrvMailbox.index -1); } - Response_P(S_JSON_COMMAND_INDEX_SVALUE, command, index, vars[index -1]); + ResponseCmndIdxChar(rules_vars[XdrvMailbox.index -1]); } - else if ((CMND_SUB == command_code) && (index > 0) && (index <= MAX_RULE_VARS)) { +} + +void CmndSubtract(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_RULE_VARS)) { if (XdrvMailbox.data_len > 0) { - float tempvar = CharToFloat(vars[index -1]) - CharToFloat(XdrvMailbox.data); - dtostrfd(tempvar, Settings.flag2.calc_resolution, vars[index -1]); - bitSet(vars_event, index -1); + float tempvar = CharToFloat(rules_vars[XdrvMailbox.index -1]) - CharToFloat(XdrvMailbox.data); + dtostrfd(tempvar, Settings.flag2.calc_resolution, rules_vars[XdrvMailbox.index -1]); + bitSet(Rules.vars_event, XdrvMailbox.index -1); } - Response_P(S_JSON_COMMAND_INDEX_SVALUE, command, index, vars[index -1]); + ResponseCmndIdxChar(rules_vars[XdrvMailbox.index -1]); } - else if ((CMND_MULT == command_code) && (index > 0) && (index <= MAX_RULE_VARS)) { +} + +void CmndMultiply(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_RULE_VARS)) { if (XdrvMailbox.data_len > 0) { - float tempvar = CharToFloat(vars[index -1]) * CharToFloat(XdrvMailbox.data); - dtostrfd(tempvar, Settings.flag2.calc_resolution, vars[index -1]); - bitSet(vars_event, index -1); + float tempvar = CharToFloat(rules_vars[XdrvMailbox.index -1]) * CharToFloat(XdrvMailbox.data); + dtostrfd(tempvar, Settings.flag2.calc_resolution, rules_vars[XdrvMailbox.index -1]); + bitSet(Rules.vars_event, XdrvMailbox.index -1); } - Response_P(S_JSON_COMMAND_INDEX_SVALUE, command, index, vars[index -1]); + ResponseCmndIdxChar(rules_vars[XdrvMailbox.index -1]); } - else if ((CMND_SCALE == command_code) && (index > 0) && (index <= MAX_RULE_VARS)) { +} + +void CmndScale(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_RULE_VARS)) { if (XdrvMailbox.data_len > 0) { if (strstr(XdrvMailbox.data, ",") != nullptr) { // Process parameter entry char sub_string[XdrvMailbox.data_len +1]; @@ -1235,28 +1863,17 @@ bool RulesCommand(void) float toLow = CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 4)); float toHigh = CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 5)); float value = map_double(valueIN, fromLow, fromHigh, toLow, toHigh); - dtostrfd(value, Settings.flag2.calc_resolution, vars[index -1]); - bitSet(vars_event, index -1); + dtostrfd(value, Settings.flag2.calc_resolution, rules_vars[XdrvMailbox.index -1]); + bitSet(Rules.vars_event, XdrvMailbox.index -1); } } - Response_P(S_JSON_COMMAND_INDEX_SVALUE, command, index, vars[index -1]); -#ifdef SUPPORT_MQTT_EVENT - } else if (CMND_SUBSCRIBE == command_code) { //MQTT Subscribe command. Subscribe , [, ] - String result = RulesSubscribe(XdrvMailbox.data, XdrvMailbox.data_len); - Response_P(S_JSON_COMMAND_SVALUE, command, result.c_str()); - } else if (CMND_UNSUBSCRIBE == command_code) { //MQTT Un-subscribe command. UnSubscribe - String result = RulesUnsubscribe(XdrvMailbox.data, XdrvMailbox.data_len); - Response_P(S_JSON_COMMAND_SVALUE, command, result.c_str()); -#endif //SUPPORT_MQTT_EVENT + ResponseCmndIdxChar(rules_vars[XdrvMailbox.index -1]); } - else serviced = false; // Unknown command - - return serviced; } float map_double(float x, float in_min, float in_max, float out_min, float out_max) { - return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; + return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; } /*********************************************************************************************\ @@ -1268,9 +1885,6 @@ bool Xdrv10(uint8_t function) bool result = false; switch (function) { - case FUNC_PRE_INIT: - RulesInit(); - break; case FUNC_EVERY_50_MSECOND: RulesEvery50ms(); break; @@ -1284,7 +1898,7 @@ bool Xdrv10(uint8_t function) RulesSetPower(); break; case FUNC_COMMAND: - result = RulesCommand(); + result = DecodeCommand(kRulesCommands, RulesCommand); break; case FUNC_RULES_PROCESS: result = RulesProcess(); @@ -1296,7 +1910,10 @@ bool Xdrv10(uint8_t function) case FUNC_MQTT_DATA: result = RulesMqttData(); break; -#endif //SUPPORT_MQTT_EVENT +#endif // SUPPORT_MQTT_EVENT + case FUNC_PRE_INIT: + RulesInit(); + break; } return result; } diff --git a/sonoff/xdrv_10_scripter.ino b/sonoff/xdrv_10_scripter.ino index 284128033..3e1487b64 100644 --- a/sonoff/xdrv_10_scripter.ino +++ b/sonoff/xdrv_10_scripter.ino @@ -35,20 +35,21 @@ no math hierarchy (costs ram and execution time, better group with brackets, an (will probably make math hierarchy an ifdefed option) keywords if then else endif, or, and are better readable for beginners (others may use {}) -changelog after merging to Tasmota -1. draw color picture from sd card -2. upload files to sc card - - \*********************************************************************************************/ #define XDRV_10 10 #define SCRIPT_DEBUG 0 + +#ifndef MAXVARS #define MAXVARS 50 -#define MAXNVARS 45 +#endif +#ifndef MAXSVARS #define MAXSVARS 5 +#endif +#define MAXNVARS MAXVARS-MAXSVARS + #define MAXFILT 5 #define SCRIPT_SVARSIZE 20 #define SCRIPT_MAXSSIZE 48 @@ -57,14 +58,42 @@ changelog after merging to Tasmota #define SCRIPT_MAXPERM (MAX_RULE_MEMS*10)-4/sizeof(float) #define MAX_SCRIPT_SIZE MAX_RULE_SIZE*MAX_RULE_SETS +// offsets epoch readings by 1.1.2019 00:00:00 to fit into float with second resolution +#define EPOCH_OFFSET 1546300800 + enum {OPER_EQU=1,OPER_PLS,OPER_MIN,OPER_MUL,OPER_DIV,OPER_PLSEQU,OPER_MINEQU,OPER_MULEQU,OPER_DIVEQU,OPER_EQUEQU,OPER_NOTEQU,OPER_GRTEQU,OPER_LOWEQU,OPER_GRT,OPER_LOW,OPER_PERC,OPER_XOR,OPER_AND,OPER_OR,OPER_ANDEQU,OPER_OREQU,OPER_XOREQU,OPER_PERCEQU}; enum {SCRIPT_LOGLEVEL=1,SCRIPT_TELEPERIOD}; #ifdef USE_SCRIPT_FATFS #include #include +#ifndef FAT_SCRIPT_SIZE #define FAT_SCRIPT_SIZE 4096 +#endif #define FAT_SCRIPT_NAME "script.txt" +#if USE_LONG_FILE_NAMES==1 +#warning ("FATFS long filenames not supported"); +#endif +#if USE_STANDARD_SPI_LIBRARY==0 +#warning ("FATFS standard spi should be used"); +#endif +#endif + +#ifdef SUPPORT_MQTT_EVENT + #include // Import LinkedList library + typedef struct { + String Event; + String Topic; + String Key; + } MQTT_Subscription; + LinkedList subscriptions; +#endif //SUPPORT_MQTT_EVENT + +#ifdef USE_DISPLAY +#ifdef USE_TOUCH_BUTTONS +#include +extern VButton *buttons[MAXBUTTONS]; +#endif #endif typedef union { @@ -93,7 +122,6 @@ struct M_FILT { float rbuff[1]; }; - typedef union { uint8_t data; struct { @@ -119,6 +147,8 @@ struct SCRIPT_MEM { uint8_t *vnp_offset; char *glob_snp; // string vars pointer char *scriptptr; + char *section_ptr; + char *scriptptr_bu; char *script_ram; uint16_t script_size; uint8_t *script_pram; @@ -143,10 +173,14 @@ struct SCRIPT_MEM { int16_t last_findex; uint8_t tasm_cmd_activ=0; - +uint8_t fast_script=0; uint32_t script_lastmillis; +#ifdef USE_BUTTON_EVENT +int8_t script_button[MAX_KEYS]; +#endif + char *GetNumericResult(char *lp,uint8_t lastop,float *fp,JsonObject *jo); char *GetStringResult(char *lp,uint8_t lastop,char *cp,JsonObject *jo); char *ForceStringVar(char *lp,char *dstr); @@ -182,24 +216,28 @@ void ScriptEverySecond(void) { } void RulesTeleperiod(void) { - if (bitRead(Settings.rule_enabled, 0)) Run_Scripter(">T",2, mqtt_data); + if (bitRead(Settings.rule_enabled, 0) && mqtt_data[0]) Run_Scripter(">T",2, mqtt_data); } -//#define USE_24C256 - // EEPROM MACROS #ifdef USE_24C256 +#ifndef USE_SCRIPT_FATFS // i2c eeprom #include #define EEPROM_ADDRESS 0x50 // strange bug, crashes with powers of 2 ??? 4096 crashes +#ifndef EEP_SCRIPT_SIZE #define EEP_SCRIPT_SIZE 4095 +#endif + + static Eeprom24C128_256 eeprom(EEPROM_ADDRESS); // eeprom.writeBytes(address, length, buffer); #define EEP_WRITE(A,B,C) eeprom.writeBytes(A,B,(uint8_t*)C); // eeprom.readBytes(address, length, buffer); #define EEP_READ(A,B,C) eeprom.readBytes(A,B,(uint8_t*)C); #endif +#endif #define SCRIPT_SKIP_SPACES while (*lp==' ' || *lp=='\t') lp++; #define SCRIPT_SKIP_EOL while (*lp==SCRIPT_EOL) lp++; @@ -227,6 +265,7 @@ char *script; glob_script_mem.max_ssize=SCRIPT_SVARSIZE; glob_script_mem.scriptptr=0; + if (!*script) return -999; float fvalues[MAXVARS]; @@ -298,7 +337,12 @@ char *script; op++; if (*op!='"') { float fv; - fv=CharToFloat(op); + if (*op=='0' && *(op+1)=='x') { + op+=2; + fv=strtol(op,&op,16); + } else { + fv=CharToFloat(op); + } fvalues[nvars]=fv; vtypes[vars].bits.is_string=0; if (!vtypes[vars].bits.is_filter) vtypes[vars].index=nvars; @@ -314,7 +358,7 @@ char *script; if (isdigit(*op)) { // lenght define follows uint8_t flen=atoi(op); - mfilt[numflt-1].numvals&=0x7f; + mfilt[numflt-1].numvals&=0x80; mfilt[numflt-1].numvals|=flen&0x7f; } } @@ -437,7 +481,13 @@ char *script; } namep++; index++; + if (index>255) { + free(glob_script_mem.script_mem); + return -5; + } } + // variables usage info + AddLog_P2(LOG_LEVEL_INFO, PSTR("Script: nv=%d, tv=%d, vns=%d, ram=%d"), nvars, svars, index, glob_script_mem.script_mem_size); // copy string variables char *cp1=glob_script_mem.glob_snp; @@ -520,10 +570,26 @@ char *script; // store start of actual program here glob_script_mem.scriptptr=lp-1; + glob_script_mem.scriptptr_bu=glob_script_mem.scriptptr; return 0; } +#ifdef USE_LIGHT +#ifdef USE_WS2812 +void ws2812_set_array(float *array ,uint8_t len) { + + Ws2812ForceSuspend(); + for (uint8_t cnt=0;cntSettings.light_pixels) break; + uint32_t col=array[cnt]; + Ws2812SetColor(cnt+1,col>>16,col>>8,col,0); + } + Ws2812ForceUpdate(); +} +#endif +#endif + #define NUM_RES 0xfe #define STR_RES 0xfd #define VAR_NV 0xff @@ -531,7 +597,9 @@ char *script; #define NTYPE 0 #define STYPE 0x80 +#ifndef FLT_MAX #define FLT_MAX 99999999 +#endif float median_array(float *array,uint8_t len) { uint8_t ind[len]; @@ -560,6 +628,22 @@ float median_array(float *array,uint8_t len) { return array[ind[len/2]]; } + +float *Get_MFAddr(uint8_t index,uint8_t *len) { + *len=0; + uint8_t *mp=(uint8_t*)glob_script_mem.mfilt; + for (uint8_t count=0; countnumvals&0x7f; + return mflp->rbuff; + } + mp+=sizeof(struct M_FILT)+((mflp->numvals&0x7f)-1)*sizeof(float); + } + return 0; +} + + float Get_MFVal(uint8_t index,uint8_t bind) { uint8_t *mp=(uint8_t*)glob_script_mem.mfilt; for (uint8_t count=0; countbuffer,MEDIAN_SIZE); } +#ifdef USE_LIGHT +//#ifdef USE_WS2812 +uint32_t HSVToRGB(uint16_t hue, uint8_t saturation, uint8_t value) { +float r = 0, g = 0, b = 0; +struct HSV { + float H; + float S; + float V; +} hsv; + +hsv.H=hue; +hsv.S=(float)saturation/100.0; +hsv.V=(float)value/100.0; + +if (hsv.S == 0) { + r = hsv.V; + g = hsv.V; + b = hsv.V; + } else { + int i; + float f, p, q, t; + + if (hsv.H == 360) + hsv.H = 0; + else + hsv.H = hsv.H / 60; + + i = (int)trunc(hsv.H); + f = hsv.H - i; + + p = hsv.V * (1.0 - hsv.S); + q = hsv.V * (1.0 - (hsv.S * f)); + t = hsv.V * (1.0 - (hsv.S * (1.0 - f))); + + switch (i) + { + case 0: + r = hsv.V; + g = t; + b = p; + break; + + case 1: + r = q; + g = hsv.V; + b = p; + break; + + case 2: + r = p; + g = hsv.V; + b = t; + break; + + case 3: + r = p; + g = q; + b = hsv.V; + break; + + case 4: + r = t; + g = p; + b = hsv.V; + break; + + default: + r = hsv.V; + g = p; + b = q; + break; + } + + } + + uint8_t ir,ig,ib; + ir=r*255; + ig=g*255; + ib=b*255; + + uint32_t rgb=(ir<<16)|(ig<<8)|ib; + return rgb; +} +#endif +//#endif // vtype => ff=nothing found, fe=constant number,fd = constant string else bit 7 => 80 = string, 0 = number // no flash strings here for performance reasons!!! @@ -670,7 +839,14 @@ char *isvar(char *lp, uint8_t *vtype,struct T_INDEX *tind,float *fp,char *sp,Jso if (isdigit(*lp) || (*lp=='-' && isdigit(*(lp+1))) || *lp=='.') { // isnumber - if (fp) *fp=CharToFloat(lp); + if (fp) { + if (*lp=='0' && *(lp+1)=='x') { + lp+=2; + *fp=strtol(lp,0,16); + } else { + *fp=CharToFloat(lp); + } + } if (*lp=='-') lp++; while (isdigit(*lp) || *lp=='.') { if (*lp==0 || *lp==SCRIPT_EOL) break; @@ -760,10 +936,11 @@ char *isvar(char *lp, uint8_t *vtype,struct T_INDEX *tind,float *fp,char *sp,Jso *vtype=NTYPE|index; if (vtp[count].bits.is_filter) { if (ja) { - GetNumericResult(ja,OPER_EQU,&fvar,0); + lp+=olen+1; + lp=GetNumericResult(lp,OPER_EQU,&fvar,0); last_findex=fvar; fvar=Get_MFVal(index,fvar); - len++; + len=1; } else { fvar=Get_MFilter(index); } @@ -800,9 +977,15 @@ char *isvar(char *lp, uint8_t *vtype,struct T_INDEX *tind,float *fp,char *sp,Jso } if (jo->success()) { char *subtype=strchr(vname,'#'); + char *subtype2; if (subtype) { *subtype=0; subtype++; + subtype2=strchr(subtype,'#'); + if (subtype2) { + *subtype2=0; + *subtype2++; + } } vn=vname; str_value = (*jo)[vn]; @@ -814,6 +997,23 @@ char *isvar(char *lp, uint8_t *vtype,struct T_INDEX *tind,float *fp,char *sp,Jso jo=&jobj1; str_value = (*jo)[vn]; if ((*jo)[vn].success()) { + // 2. stage + if (subtype2) { + JsonObject &jobj2=(*jo)[vn]; + if ((*jo)[vn].success()) { + vn=subtype2; + jo=&jobj2; + str_value = (*jo)[vn]; + if ((*jo)[vn].success()) { + goto skip; + } else { + goto chknext; + } + } else { + goto chknext; + } + } + // end goto skip; } } else { @@ -839,7 +1039,13 @@ char *isvar(char *lp, uint8_t *vtype,struct T_INDEX *tind,float *fp,char *sp,Jso return lp+len; } } else { - if (fp) *fp=CharToFloat((char*)str_value); + if (fp) { + if (!strncmp(vn.c_str(),"Epoch",5)) { + *fp=atoi(str_value)-(uint32_t)EPOCH_OFFSET; + } else { + *fp=CharToFloat((char*)str_value); + } + } *vtype=NUM_RES; tind->bits.constant=1; tind->bits.is_string=0; @@ -860,6 +1066,18 @@ chknext: } goto exit; } +#ifdef USE_BUTTON_EVENT + if (!strncmp(vname,"bt[",3)) { + // tasmota button state + GetNumericResult(vname+3,OPER_EQU,&fvar,0); + uint32_t index=fvar; + if (index<1 || index>MAX_KEYS) index=1; + fvar=script_button[index-1]; + script_button[index-1]|=0x80; + len++; + goto exit; + } +#endif break; case 'c': if (!strncmp(vname,"chg[",4)) { @@ -889,6 +1107,12 @@ chknext: goto exit; } break; + case 'e': + if (!strncmp(vname,"epoch",5)) { + fvar=UtcTime()-(uint32_t)EPOCH_OFFSET; + goto exit; + } + break; #ifdef USE_SCRIPT_FATFS case 'f': if (!strncmp(vname,"fo(",3)) { @@ -915,7 +1139,7 @@ chknext: fvar=cnt; glob_script_mem.file_flags[cnt].is_open=1; } else { - toLog("file open failed"); + AddLog_P(LOG_LEVEL_INFO,PSTR("file open failed")); } break; } @@ -1151,6 +1375,39 @@ chknext: } goto strexit; } + if (!strncmp(vname,"hx(",3)) { + lp=GetNumericResult(lp+3,OPER_EQU,&fvar,0); + lp++; + len=0; + if (sp) { + sprintf(sp,"%08x",(uint32_t)fvar); + } + goto strexit; + } +#ifdef USE_LIGHT +//#ifdef USE_WS2812 + if (!strncmp(vname,"hsvrgb(",7)) { + lp=GetNumericResult(lp+7,OPER_EQU,&fvar,0); + if (fvar<0 || fvar>360) fvar=0; + SCRIPT_SKIP_SPACES + // arg2 + float fvar2; + lp=GetNumericResult(lp,OPER_EQU,&fvar2,0); + if (fvar2<0 || fvar2>100) fvar2=0; + SCRIPT_SKIP_SPACES + // arg3 + float fvar3; + lp=GetNumericResult(lp,OPER_EQU,&fvar3,0); + if (fvar3<0 || fvar3>100) fvar3=0; + + fvar=HSVToRGB(fvar,fvar2,fvar3); + + lp++; + len=0; + goto exit; + } +//#endif +#endif break; case 'i': if (!strncmp(vname,"int(",4)) { @@ -1288,7 +1545,16 @@ chknext: len+=1; goto exit; } + if (!strncmp(vname,"pc[",3)) { + GetNumericResult(vname+3,OPER_EQU,&fvar,0); + uint8_t index=fvar; + if (index<1 || index>MAX_COUNTERS) index=1; + fvar=RtcSettings.pulse_counter[index-1]; + len+=1; + goto exit; + } break; + case 'r': if (!strncmp(vname,"ram",3)) { fvar=glob_script_mem.script_mem_size+(glob_script_mem.script_size)+(MAX_RULE_MEMS*10); @@ -1316,6 +1582,34 @@ chknext: fvar=strlen(glob_script_mem.script_ram); goto exit; } + if (!strncmp(vname,"sl(",3)) { + lp+=3; + char str[SCRIPT_MAXSSIZE]; + lp=GetStringResult(lp,OPER_EQU,str,0); + lp++; + len=0; + fvar=strlen(str); + goto exit; + } + if (!strncmp(vname,"sb(",3)) { + lp+=3; + char str[SCRIPT_MAXSSIZE]; + lp=GetStringResult(lp,OPER_EQU,str,0); + SCRIPT_SKIP_SPACES + float fvar1; + lp=GetNumericResult(lp,OPER_EQU,&fvar1,0); + SCRIPT_SKIP_SPACES + float fvar2; + lp=GetNumericResult(lp,OPER_EQU,&fvar2,0); + lp++; + len=0; + if (fvar1<0) { + fvar1=strlen(str)+fvar1; + } + memcpy(sp,&str[(uint8_t)fvar1],(uint8_t)fvar2); + sp[(uint8_t)fvar2] = '\0'; + goto strexit; + } if (!strncmp(vname,"st(",3)) { lp+=3; char str[SCRIPT_MAXSSIZE]; @@ -1370,6 +1664,20 @@ chknext: goto exit; } #endif + +#ifdef USE_SHUTTER + if (!strncmp(vname,"sht[",4)) { + GetNumericResult(vname+4,OPER_EQU,&fvar,0); + uint8_t index=fvar; + if (index<=shutters_present) { + fvar=Settings.shutter_position[index-1]; + } else { + fvar=-1; + } + len+=1; + goto exit; + } +#endif break; case 't': if (!strncmp(vname,"time",4)) { @@ -1403,6 +1711,24 @@ chknext: if (sp) strlcpy(sp,Settings.mqtt_topic,glob_script_mem.max_ssize); goto strexit; } +#ifdef USE_DISPLAY +#ifdef USE_TOUCH_BUTTONS + if (!strncmp(vname,"tbut[",5)) { + GetNumericResult(vname+5,OPER_EQU,&fvar,0); + uint8_t index=fvar; + if (index<1 || index>MAXBUTTONS) index=1; + index--; + if (buttons[index]) { + fvar=buttons[index]->vpower&0x80; + } else { + fvar=-1; + } + len+=1; + goto exit; + } + +#endif +#endif break; case 'u': if (!strncmp(vname,"uptime",6)) { @@ -1433,6 +1759,7 @@ chknext: goto notfound; } break; + case 'w': if (!strncmp(vname,"wday",4)) { fvar=RtcTime.day_of_week; @@ -1613,7 +1940,7 @@ extern "C" { } uint16_t GetStack(void) { register uint32_t *sp asm("a1"); - return (4 * (sp - g_pcont.stack)); + return (4 * (sp - g_cont.stack)); } #else @@ -1635,12 +1962,17 @@ char *GetStringResult(char *lp,uint8_t lastop,char *cp,JsonObject *jo) { char str[SCRIPT_MAXSSIZE],str1[SCRIPT_MAXSSIZE]; while (1) { lp=isvar(lp,&vtype,&ind,0,str1,jo); + if (vtype!=STR_RES && !(vtype&STYPE)) { + // numeric type + glob_script_mem.glob_error=1; + return lp; + } switch (lastop) { case OPER_EQU: strlcpy(str,str1,sizeof(str)); break; case OPER_PLS: - strlcat(str,str1,sizeof(str)); + strncat(str,str1,sizeof(str)); break; } slp=lp; @@ -1704,7 +2036,7 @@ struct T_INDEX ind; fvar/=fvar1; break; case OPER_PERC: - fvar=fmod(fvar,fvar1); + fvar=fmodf(fvar,fvar1); break; case OPER_XOR: fvar=(uint32_t)fvar^(uint32_t)fvar1; @@ -1717,6 +2049,7 @@ struct T_INDEX ind; break; default: break; + } slp=lp; lp=getop(lp,&operand); @@ -1746,13 +2079,13 @@ struct T_INDEX ind; char *ForceStringVar(char *lp,char *dstr) { float fvar; char *slp=lp; - glob_script_mem.var_not_found=0; + glob_script_mem.glob_error=0; lp=GetStringResult(lp,OPER_EQU,dstr,0); - if (glob_script_mem.var_not_found) { + if (glob_script_mem.glob_error) { // mismatch lp=GetNumericResult(slp,OPER_EQU,&fvar,0); dtostrfd(fvar,6,dstr); - glob_script_mem.var_not_found=0; + glob_script_mem.glob_error=0; } return lp; } @@ -1762,28 +2095,38 @@ void Replace_Cmd_Vars(char *srcbuf,char *dstbuf,uint16_t dstsize) { char *cp; uint16_t count; uint8_t vtype; + uint8_t dprec=glob_script_mem.script_dprec; float fvar; cp=srcbuf; struct T_INDEX ind; char string[SCRIPT_MAXSSIZE]; + dstsize-=2; for (count=0;countfvar1); + break; + case OPER_GRTEQU: + res=(*dfvar>=fvar1); + break; + default: + // error + break; + } + + if (!and_or) { + *result=res; + } else if (and_or==1) { + *result|=res; + } else { + *result&=res; + } + } +exit: +#if SCRIPT_DEBUG>0 + char tbuff[128]; + sprintf(tbuff,"p1=%d,p2=%d,cmpres=%d line: ",(int32_t)*dfvar,(int32_t)fvar1,*result); + toLogEOL(tbuff,lp); +#endif + return lp; +} //#define IFTHEN_DEBUG #define IF_NEST 8 // execute section of scripter -int16_t Run_Scripter(const char *type, uint8_t tlen, char *js) { - uint8_t vtype=0,sindex,xflg,floop=0,globvindex,globaindex; +int16_t Run_Scripter(const char *type, int8_t tlen, char *js) { + + if (tasm_cmd_activ && tlen>0) return 0; + + uint8_t vtype=0,sindex,xflg,floop=0,globvindex,fromscriptcmd=0; + int8_t globaindex; struct T_INDEX ind; - uint8_t operand,lastop,numeric=1,if_state[IF_NEST],if_result[IF_NEST],and_or,ifstck=0,s_ifstck=0; + uint8_t operand,lastop,numeric=1,if_state[IF_NEST],if_exe[IF_NEST],if_result[IF_NEST],and_or,ifstck=0; if_state[ifstck]=0; if_result[ifstck]=0; + if_exe[ifstck]=1; char cmpstr[SCRIPT_MAXSSIZE]; - - if (tasm_cmd_activ) return 0; - + uint8_t check=0; + if (tlen<0) { + tlen=abs(tlen); + check=1; + } float *dfvar,*cv_count,cv_max,cv_inc; char *cv_ptr; @@ -1895,44 +2364,45 @@ int16_t Run_Scripter(const char *type, uint8_t tlen, char *js) { if (section) { // we are in section if (*lp=='>') { - section=0; - break; + return 0; } if (*lp=='#') { - section=0; - break; + return 0; } glob_script_mem.var_not_found=0; //#if SCRIPT_DEBUG>0 #ifdef IFTHEN_DEBUG char tbuff[128]; - sprintf(tbuff,"stack=%d,state=%d,cmpres=%d line: ",ifstck,if_state[ifstck],if_result[ifstck]); + sprintf(tbuff,"stack=%d,exe=%d,state=%d,cmpres=%d line: ",ifstck,if_exe[ifstck],if_state[ifstck],if_result[ifstck]); toLogEOL(tbuff,lp); #endif +//if (if_state[s_ifstck]==3 && if_result[s_ifstck]) goto next_line; +//if (if_state[s_ifstck]==2 && !if_result[s_ifstck]) goto next_line; + if (!strncmp(lp,"if",2)) { lp+=2; - if (if_state[ifstck]>0) { - if (ifstck=2) { lp+=5; - if_state[ifstck]=0; if (ifstck>0) { + if_state[ifstck]=0; ifstck--; } - if (if_state[ifstck]==3 && if_result[ifstck]) goto next_line; - if (if_state[ifstck]==2 && !if_result[ifstck]) goto next_line; - s_ifstck=ifstck; // >>>>> goto next_line; } else if (!strncmp(lp,"or",2) && if_state[ifstck]==1) { lp+=2; @@ -1945,6 +2415,7 @@ int16_t Run_Scripter(const char *type, uint8_t tlen, char *js) { if (*lp=='{' && if_state[ifstck]==1) { lp+=1; // then if_state[ifstck]=2; + if (if_exe[ifstck-1]) if_exe[ifstck]=if_result[ifstck]; } else if (*lp=='{' && if_state[ifstck]==3) { lp+=1; // after else //if_state[ifstck]=3; @@ -1960,8 +2431,11 @@ int16_t Run_Scripter(const char *type, uint8_t tlen, char *js) { if (!strncmp(lp,"else",4)) { // is before else, no endif if_state[ifstck]=3; + if (if_exe[ifstck-1]) if_exe[ifstck]=!if_result[ifstck]; lp+=4; iselse=1; + SCRIPT_SKIP_SPACES + if (*lp=='{') lp++; break; } lp++; @@ -1969,13 +2443,11 @@ int16_t Run_Scripter(const char *type, uint8_t tlen, char *js) { if (!iselse) { lp=slp; // endif - if_state[ifstck]=0; if (ifstck>0) { + if_state[ifstck]=0; ifstck--; } - if (if_state[ifstck]==3 && if_result[ifstck]) goto next_line; - if (if_state[ifstck]==2 && !if_result[ifstck]) goto next_line; - s_ifstck=ifstck; // >>>>> + goto next_line; } } @@ -1995,7 +2467,7 @@ int16_t Run_Scripter(const char *type, uint8_t tlen, char *js) { lp=GetNumericResult(lp,OPER_EQU,&cv_max,0); SCRIPT_SKIP_SPACES lp=GetNumericResult(lp,OPER_EQU,&cv_inc,0); - SCRIPT_SKIP_EOL + //SCRIPT_SKIP_EOL cv_ptr=lp; floop=1; } else { @@ -2016,24 +2488,42 @@ int16_t Run_Scripter(const char *type, uint8_t tlen, char *js) { if (!strncmp(lp,"switch",6)) { lp+=6; SCRIPT_SKIP_SPACES + char *slp=lp; lp=GetNumericResult(lp,OPER_EQU,&swvar,0); - swflg=1; + if (glob_script_mem.glob_error==1) { + // was string, not number + lp=slp; + // get the string + lp=isvar(lp,&vtype,&ind,0,cmpstr,0); + swflg=0x81; + } else { + swflg=1; + } } else if (!strncmp(lp,"case",4) && swflg>0) { lp+=4; SCRIPT_SKIP_SPACES float cvar; - lp=GetNumericResult(lp,OPER_EQU,&cvar,0); - if (swvar!=cvar) { - swflg=2; + if (!(swflg&0x80)) { + lp=GetNumericResult(lp,OPER_EQU,&cvar,0); + if (swvar!=cvar) { + swflg=2; + } else { + swflg=1; + } } else { - swflg=1; + char str[SCRIPT_MAXSSIZE]; + lp=GetStringResult(lp,OPER_EQU,str,0); + if (!strcmp(cmpstr,str)) { + swflg=0x81; + } else { + swflg=0x82; + } } } else if (!strncmp(lp,"ends",4) && swflg>0) { lp+=4; swflg=0; } - - if (swflg==2) goto next_line; + if ((swflg&3)==2) goto next_line; SCRIPT_SKIP_SPACES //SCRIPT_SKIP_EOL @@ -2042,14 +2532,12 @@ int16_t Run_Scripter(const char *type, uint8_t tlen, char *js) { } //toLogN(lp,16); - if (if_state[s_ifstck]==3 && if_result[s_ifstck]) goto next_line; - if (if_state[s_ifstck]==2 && !if_result[s_ifstck]) goto next_line; + if (!if_exe[ifstck] && if_state[ifstck]!=1) goto next_line; #ifdef IFTHEN_DEBUG - sprintf(tbuff,"stack=%d,state=%d,cmpres=%d execute line: ",ifstck,if_state[ifstck],if_result[ifstck]); + sprintf(tbuff,"stack=%d,exe=%d,state=%d,cmpres=%d execute line: ",ifstck,if_exe[ifstck],if_state[ifstck],if_result[ifstck]); toLogEOL(tbuff,lp); #endif - s_ifstck=ifstck; if (!strncmp(lp,"break",5)) { if (floop) { @@ -2078,11 +2566,11 @@ int16_t Run_Scripter(const char *type, uint8_t tlen, char *js) { SCRIPT_SKIP_SPACES lp=GetNumericResult(lp,OPER_EQU,&fvar,0); int8_t mode=fvar; - pinMode(pinnr,mode&1); + pinMode(pinnr,mode&3); goto next_line; } else if (!strncmp(lp,"spin(",5)) { lp+=5; - // set pin mode + // set pin lp=GetNumericResult(lp,OPER_EQU,&fvar,0); int8_t pinnr=fvar; SCRIPT_SKIP_SPACES @@ -2096,18 +2584,49 @@ int16_t Run_Scripter(const char *type, uint8_t tlen, char *js) { Scripter_save_pvars(); goto next_line; } +#ifdef USE_LIGHT +#ifdef USE_WS2812 + else if (!strncmp(lp,"ws2812(",7)) { + lp+=7; + lp=isvar(lp,&vtype,&ind,0,0,0); + if (vtype!=VAR_NV) { + // found variable as result + uint8_t index=glob_script_mem.type[ind.index].index; + if ((vtype&STYPE)==0) { + // numeric result + if (glob_script_mem.type[index].bits.is_filter) { + uint8_t len=0; + float *fa=Get_MFAddr(index,&len); + //Serial.printf(">> 2 %d\n",(uint32_t)*fa); + if (fa && len) ws2812_set_array(fa,len); + } + } + } + goto next_line; + } +#endif +#endif - else if (!strncmp(lp,"=>",2)) { + else if (!strncmp(lp,"=>",2) || !strncmp(lp,"->",2) || !strncmp(lp,"+>",2) || !strncmp(lp,"print",5)) { // execute cmd - lp+=2; + uint8_t sflag=0,pflg=0,svmqtt,swll; + if (*lp=='p') { + pflg=1; + lp+=5; + } + else { + if (*lp=='-') sflag=1; + if (*lp=='+') sflag=2; + lp+=2; + } char *slp=lp; SCRIPT_SKIP_SPACES #define SCRIPT_CMDMEM 512 char *cmdmem=(char*)malloc(SCRIPT_CMDMEM); if (cmdmem) { char *cmd=cmdmem; - short count; - for (count=0; countfvar1); - break; - case OPER_GRTEQU: - res=(*dfvar>=fvar1); - break; - default: - // error - break; - } - - if (!and_or) { - if_result[s_ifstck]=res; - } else if (and_or==1) { - if_result[s_ifstck]|=res; - } else { - if_result[s_ifstck]&=res; - } -#if SCRIPT_DEBUG>0 - char tbuff[128]; - sprintf(tbuff,"p1=%d,p2=%d,cmpres=%d line: ",(int32_t)*dfvar,(int32_t)fvar1,if_result[s_ifstck]); - toLogEOL(tbuff,lp); -#endif - - } else { - // compare string - char str[SCRIPT_MAXSSIZE]; - lp=GetStringResult(lp,OPER_EQU,str,0); - if (lastop==OPER_EQUEQU || lastop==OPER_NOTEQU) { - uint8_t res=0; - res=strcmp(cmpstr,str); - if (lastop==OPER_EQUEQU) res=!res; - if (!and_or) { - if_result[s_ifstck]=res; - } else if (and_or==1) { - if_result[s_ifstck]|=res; - } else { - if_result[s_ifstck]&=res; - } - } - } - SCRIPT_SKIP_SPACES - if (*lp=='{' && if_state[ifstck]==1) { - lp+=1; // then - if_state[ifstck]=2; - } - goto next_line; - } else { - if (numeric) { - char *slp=lp; - glob_script_mem.glob_error=0; - lp=GetNumericResult(lp,OPER_EQU,&fvar,jo); - if (glob_script_mem.glob_error==1) { - // mismatch was string, not number - // get the string and convert to number - lp=isvar(slp,&vtype,&ind,0,cmpstr,jo); - fvar=CharToFloat(cmpstr); - } - switch (lastop) { - case OPER_EQU: - if (glob_script_mem.var_not_found) { - if (!js) toLog("var not found\n"); - goto next_line; + lp=getop(lp,&lastop); + char *slp=lp; + glob_script_mem.glob_error=0; + lp=GetNumericResult(lp,OPER_EQU,&fvar,jo); + if (glob_script_mem.glob_error==1) { + // mismatch was string, not number + // get the string and convert to number + lp=isvar(slp,&vtype,&ind,0,cmpstr,jo); + fvar=CharToFloat(cmpstr); + } + switch (lastop) { + case OPER_EQU: + if (glob_script_mem.var_not_found) { + if (!js) toLogEOL("var not found: ",lp); + goto next_line; + } + *dfvar=fvar; + break; + case OPER_PLSEQU: + *dfvar+=fvar; + break; + case OPER_MINEQU: + *dfvar-=fvar; + break; + case OPER_MULEQU: + *dfvar*=fvar; + break; + case OPER_DIVEQU: + *dfvar/=fvar; + break; + case OPER_PERCEQU: + *dfvar=fmodf(*dfvar,fvar); + break; + case OPER_ANDEQU: + *dfvar=(uint32_t)*dfvar&(uint32_t)fvar; + break; + case OPER_OREQU: + *dfvar=(uint32_t)*dfvar|(uint32_t)fvar; + break; + case OPER_XOREQU: + *dfvar=(uint32_t)*dfvar^(uint32_t)fvar; + break; + default: + // error + break; + } + // var was changed + glob_script_mem.type[globvindex].bits.changed=1; + if (glob_script_mem.type[globvindex].bits.is_filter) { + if (globaindex>=0) { + Set_MFVal(glob_script_mem.type[globvindex].index,globaindex,*dfvar); + } else { + Set_MFilter(glob_script_mem.type[globvindex].index,*dfvar); } - *dfvar=fvar; - break; - case OPER_PLSEQU: - *dfvar+=fvar; - break; - case OPER_MINEQU: - *dfvar-=fvar; - break; - case OPER_MULEQU: - *dfvar*=fvar; - break; - case OPER_DIVEQU: - *dfvar/=fvar; - break; - case OPER_PERCEQU: - *dfvar=fmod(*dfvar,fvar); - break; - case OPER_ANDEQU: - *dfvar=(uint32_t)*dfvar&(uint32_t)fvar; - break; - case OPER_OREQU: - *dfvar=(uint32_t)*dfvar|(uint32_t)fvar; - break; - case OPER_XOREQU: - *dfvar=(uint32_t)*dfvar^(uint32_t)fvar; - break; - default: - // error - break; - } - // var was changed - glob_script_mem.type[globvindex].bits.changed=1; - if (glob_script_mem.type[globvindex].bits.is_filter) { - if (globaindex>=0) { - Set_MFVal(glob_script_mem.type[globvindex].index,globaindex,*dfvar); + } + + if (sysv_type) { + switch (sysv_type) { + case SCRIPT_LOGLEVEL: + glob_script_mem.script_loglevel=*dfvar; + break; + case SCRIPT_TELEPERIOD: + if (*dfvar<10) *dfvar=10; + if (*dfvar>300) *dfvar=300; + Settings.tele_period=*dfvar; + break; + } + sysv_type=0; + } } else { - Set_MFilter(glob_script_mem.type[globvindex].index,*dfvar); - } - } + // string result + numeric=0; + sindex=index; + // string result + char str[SCRIPT_MAXSSIZE]; + lp=getop(lp,&lastop); + char *slp=lp; + glob_script_mem.glob_error=0; + lp=GetStringResult(lp,OPER_EQU,str,jo); + if (!js && glob_script_mem.glob_error) { + // mismatch + lp=GetNumericResult(slp,OPER_EQU,&fvar,0); + dtostrfd(fvar,6,str); + glob_script_mem.glob_error=0; + } - if (sysv_type) { - switch (sysv_type) { - case SCRIPT_LOGLEVEL: - glob_script_mem.script_loglevel=*dfvar; - break; - case SCRIPT_TELEPERIOD: - if (*dfvar<10) *dfvar=10; - if (*dfvar>300) *dfvar=300; - Settings.tele_period=*dfvar; - break; + if (!glob_script_mem.var_not_found) { + // var was changed + glob_script_mem.type[globvindex].bits.changed=1; + if (lastop==OPER_EQU) { + strlcpy(glob_script_mem.glob_snp+(sindex*glob_script_mem.max_ssize),str,glob_script_mem.max_ssize); + } else if (lastop==OPER_PLSEQU) { + strncat(glob_script_mem.glob_snp+(sindex*glob_script_mem.max_ssize),str,glob_script_mem.max_ssize); + } + } } - sysv_type=0; - } - } else { - // string result - char str[SCRIPT_MAXSSIZE]; - char *slp=lp; - lp=GetStringResult(lp,OPER_EQU,str,jo); - if (!js && glob_script_mem.var_not_found) { - // mismatch - lp=GetNumericResult(slp,OPER_EQU,&fvar,0); - dtostrfd(fvar,6,str); - glob_script_mem.var_not_found=0; - } - - if (!glob_script_mem.var_not_found) { - // var was changed - glob_script_mem.type[globvindex].bits.changed=1; - if (lastop==OPER_EQU) { - strlcpy(glob_script_mem.glob_snp+(sindex*glob_script_mem.max_ssize),str,glob_script_mem.max_ssize); - } else if (lastop==OPER_PLSEQU) { - strlcat(glob_script_mem.glob_snp+(sindex*glob_script_mem.max_ssize),str,glob_script_mem.max_ssize); - } - } - } - SCRIPT_SKIP_SPACES - if (*lp=='{' && if_state[ifstck]==3) { - lp+=1; // else - //if_state[ifstck]=3; - } - goto next_line; - } + } + SCRIPT_SKIP_SPACES + if (*lp=='{' && if_state[ifstck]==3) { + lp+=1; // else + //if_state[ifstck]=3; + } + goto next_line; + } } else { // decode line if (*lp=='>' && tlen==1) { // called from cmdline lp++; section=1; + fromscriptcmd=1; goto startline; } if (!strncmp(lp,type,tlen)) { // found section section=1; + glob_script_mem.section_ptr=lp; + if (check) { + return 99; + } // check for subroutine - if (*type=='#') { + char *ctype=(char*)type; + if (*ctype=='#') { // check for parameter - type+=tlen; - if (*type=='(') { + ctype+=tlen; + if (*ctype=='(' && *(lp+tlen)=='(') { float fparam; numeric=1; glob_script_mem.glob_error=0; - GetNumericResult((char*)type,OPER_EQU,&fparam,0); + GetNumericResult((char*)ctype,OPER_EQU,&fparam,0); if (glob_script_mem.glob_error==1) { // was string, not number numeric=0; // get the string - GetStringResult((char*)type+1,OPER_EQU,cmpstr,0); + GetStringResult((char*)ctype+1,OPER_EQU,cmpstr,0); } lp+=tlen; if (*lp=='(') { @@ -2412,6 +2894,12 @@ int16_t Run_Scripter(const char *type, uint8_t tlen, char *js) { } } } + } else { + lp+=tlen; + if (*ctype=='(' || (*lp!=SCRIPT_EOL && *lp!='?')) { + // revert + section=0; + } } } } @@ -2422,10 +2910,17 @@ int16_t Run_Scripter(const char *type, uint8_t tlen, char *js) { lp++; } else { lp = strchr(lp, SCRIPT_EOL); - if (!lp) break; + if (!lp) { + if (section) { + return 0; + } else { + return -1; + } + } lp++; } } + return -1; } uint8_t script_xsns_index = 0; @@ -2445,6 +2940,7 @@ void ScripterEvery100ms(void) { Run_Scripter(">T",2, mqtt_data); } } + if (fast_script==99) Run_Scripter(">F",2,0); } //mems[MAX_RULE_MEMS] is 50 bytes in 6.5 @@ -2508,24 +3004,86 @@ const char HTTP_FORM_SCRIPT1[] PROGMEM = "script enable
" "
" ""; +const char HTTP_SCRIPT_FORM_END[] PROGMEM = + "
" + "" + "
"; #ifdef USE_SCRIPT_FATFS const char HTTP_FORM_SCRIPT1c[] PROGMEM = @@ -2573,12 +3131,30 @@ const char HTTP_FORM_SDC_HREF[] PROGMEM = #ifdef USE_SCRIPT_FATFS +#if USE_LONG_FILE_NAMES>0 +#undef REJCMPL +#define REJCMPL 6 +#else +#undef REJCMPL +#define REJCMPL 8 +#endif + uint8_t reject(char *name) { + if (*name=='_') return 1; - if (!strncmp(name,"SPOTLI~1",8)) return 1; - if (!strncmp(name,"TRASHE~1",8)) return 1; - if (!strncmp(name,"FSEVEN~1",8)) return 1; - if (!strncmp(name,"SYSTEM~1",8)) return 1; + if (*name=='.') return 1; + +#ifndef ARDUINO_ESP8266_RELEASE_2_3_0 + if (!strncasecmp(name,"SPOTLI~1",REJCMPL)) return 1; + if (!strncasecmp(name,"TRASHE~1",REJCMPL)) return 1; + if (!strncasecmp(name,"FSEVEN~1",REJCMPL)) return 1; + if (!strncasecmp(name,"SYSTEM~1",REJCMPL)) return 1; +#else + if (!strcasecmp(name,"SPOTLI~1")) return 1; + if (!strcasecmp(name,"TRASHE~1")) return 1; + if (!strcasecmp(name,"FSEVEN~1")) return 1; + if (!strcasecmp(name,"SYSTEM~1")) return 1; +#endif return 0; } @@ -2671,7 +3247,7 @@ void Script_FileUploadConfiguration(void) WSContentSend_P(HTTP_FORM_FILE_UPGb); WSContentSpaceButton(BUTTON_CONFIGURATION); WSContentStop(); - upload_error = 0; + Web.upload_error = 0; } File upload_file; @@ -2687,6 +3263,8 @@ void ScriptFileUploadSuccess(void) { WSContentStop(); } + + void script_upload(void) { //AddLog_P(LOG_LEVEL_INFO, PSTR("HTP: file upload")); @@ -2697,16 +3275,16 @@ void script_upload(void) { sprintf(npath,"%s/%s",path,upload.filename.c_str()); SD.remove(npath); upload_file=SD.open(npath,FILE_WRITE); - if (!upload_file) upload_error=1; + if (!upload_file) Web.upload_error=1; } else if(upload.status == UPLOAD_FILE_WRITE) { if (upload_file) upload_file.write(upload.buf,upload.currentSize); } else if(upload.status == UPLOAD_FILE_END) { if (upload_file) upload_file.close(); - if (upload_error) { + if (Web.upload_error) { AddLog_P(LOG_LEVEL_INFO, PSTR("HTP: upload error")); } } else { - upload_error=1; + Web.upload_error=1; WebServer->send(500, "text/plain", "500: couldn't create file"); } } @@ -2716,13 +3294,13 @@ uint8_t DownloadFile(char *file) { WiFiClient download_Client; if (!SD.exists(file)) { - toLog("file not found"); + AddLog_P(LOG_LEVEL_INFO,PSTR("file not found")); return 0; } download_file=SD.open(file,FILE_READ); if (!download_file) { - toLog("could not open file"); + AddLog_P(LOG_LEVEL_INFO,PSTR("could not open file")); return 0; } @@ -2773,18 +3351,23 @@ uint8_t DownloadFile(char *file) { #endif -void HandleScriptConfiguration(void) -{ + +void HandleScriptTextareaConfiguration(void) { + if (!HttpCheckPriviledgedAccess()) { return; } + + if (WebServer->hasArg("save")) { + ScriptSaveSettings(); + HandleConfiguration(); + return; + } +} + +void HandleScriptConfiguration(void) { + if (!HttpCheckPriviledgedAccess()) { return; } AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_SCRIPT); - if (WebServer->hasArg("save")) { - ScriptSaveSettings(); - HandleConfiguration(); - return; - } - #ifdef USE_SCRIPT_FATFS if (WebServer->hasArg("d1")) { DownloadFile(glob_script_mem.flink[0]); @@ -2800,7 +3383,15 @@ void HandleScriptConfiguration(void) WSContentStart_P(S_CONFIGURE_SCRIPT); WSContentSendStyle(); WSContentSend_P(HTTP_FORM_SCRIPT); + + +#ifdef xSCRIPT_STRIP_COMMENTS + uint16_t ssize=glob_script_mem.script_size; + if (bitRead(Settings.rule_enabled, 1)) ssize*=2; + WSContentSend_P(HTTP_FORM_SCRIPT1,1,1,bitRead(Settings.rule_enabled,0) ? " checked" : "",ssize); +#else WSContentSend_P(HTTP_FORM_SCRIPT1,1,1,bitRead(Settings.rule_enabled,0) ? " checked" : "",glob_script_mem.script_size); +#endif // script is to larg for WSContentSend_P if (glob_script_mem.script_ram[0]) { @@ -2816,22 +3407,12 @@ void HandleScriptConfiguration(void) } #endif - WSContentSend_P(HTTP_FORM_END); + WSContentSend_P(HTTP_SCRIPT_FORM_END); WSContentSpaceButton(BUTTON_CONFIGURATION); WSContentStop(); } -void strrepl_inplace(char *str, const char *a, const char *b) { - for (char *cursor=str; (cursor=strstr(cursor,a)) != NULL;) { - memmove(cursor+strlen(b),cursor+strlen(a),strlen(cursor)-strlen(a)+1); - for (uint32_t i=0; b[i]!='\0'; i++) { - cursor[i] = b[i]; - } - cursor += strlen(b); - } -} - void ScriptSaveSettings(void) { if (WebServer->hasArg("c1")) { @@ -2840,23 +3421,55 @@ void ScriptSaveSettings(void) { bitWrite(Settings.rule_enabled,0,0); } + String str = WebServer->arg("t1"); if (*str.c_str()) { -#if 1 - strrepl_inplace((char*)str.c_str(),"\r\n","\n"); - strrepl_inplace((char*)str.c_str(),"\r","\n"); -#else + str.replace("\r\n","\n"); str.replace("\r","\n"); + +#ifdef xSCRIPT_STRIP_COMMENTS + if (bitRead(Settings.rule_enabled, 1)) { + char *sp=(char*)str.c_str(); + char *sp1=sp; + char *dp=sp; + uint8_t flg=0; + while (*sp) { + while (*sp==' ') sp++; + sp1=sp; + sp=strchr(sp,'\n'); + if (!sp) { + flg=1; + } else { + *sp=0; + } + if (*sp1!=';') { + uint8_t slen=strlen(sp1); + if (slen) { + strcpy(dp,sp1); + dp+=slen; + *dp++='\n'; + } + } + if (flg) { + *dp=0; + break; + } + sp++; + } + } #endif + strlcpy(glob_script_mem.script_ram,str.c_str(), glob_script_mem.script_size); #ifdef USE_24C256 +#ifndef USE_SCRIPT_FATFS if (glob_script_mem.flags&1) { EEP_WRITE(0,EEP_SCRIPT_SIZE,glob_script_mem.script_ram); } #endif +#endif #ifdef USE_SCRIPT_FATFS if (glob_script_mem.flags&1) { @@ -2884,69 +3497,1197 @@ void ScriptSaveSettings(void) { return; } Run_Scripter(">B",2,0); + fast_script=Run_Scripter(">F",-2,0); } } #endif + +#if defined(USE_SCRIPT_HUE) && defined(USE_WEBSERVER) && defined(USE_EMULATION) && defined(USE_EMULATION_HUE) && defined(USE_LIGHT) + + +#define HUE_DEV_MVNUM 5 +#define HUE_DEV_NSIZE 16 +struct HUE_SCRIPT { + char name[HUE_DEV_NSIZE]; + uint8_t type; + uint8_t index[HUE_DEV_MVNUM]; + uint8_t vindex[HUE_DEV_MVNUM]; +} hue_script[32]; + + +const char SCRIPT_HUE_LIGHTS_STATUS_JSON1[] PROGMEM = + "{\"state\":" + "{\"on\":{state}," + "{light_status}" + "\"alert\":\"none\"," + "\"effect\":\"none\"," + "\"reachable\":true}" + ",\"type\":\"{type}\"," + "\"name\":\"{j1\"," + "\"modelid\":\"{m1}\"," + "\"uniqueid\":\"{j2\"," + "\"swversion\":\"5.50.1.19085\"}"; + +/* +const char SCRIPT_HUE_LIGHTS_STATUS_JSON2[] PROGMEM = + "{\"state\":" + "{\"temperature\": 2674," + "\"lastupdated\": \"2019-08-04T12:13:04\"}," + "\"config\": {" + "\"on\": true," + "\"battery\": 100," + "\"reachable\": true," + "\"alert\": \"none\"," + "\"ledindication\": false," + "\"usertest\": false," + "\"pending\": []" + "}," + "\"name\": \"{j1\"," + "\"type\": \"ZLLTemperature\"," + "\"modelid\": \"SML001\"," + "\"manufacturername\": \"Philips\"," + "\"swversion\": \"6.1.0.18912\"," + "\"uniqueid\": \"{j2\"}"; +*/ + + +const char SCRIPT_HUE_LIGHTS_STATUS_JSON2[] PROGMEM = +"{\"state\":{" +"\"presence\":{state}," +"\"lastupdated\":\"2017-10-01T12:37:30\"" +"}," +"\"swupdate\":{" +"\"state\":\"noupdates\"," +"\"lastinstall\": null" +"}," +"\"config\":{" +"\"on\":true," +"\"battery\":100," +"\"reachable\":true," +"\"alert\":\"none\"," +"\"ledindication\":false," +"\"usertest\":false," +"\"sensitivity\":2," +"\"sensitivitymax\":2," +"\"pending\":[]" +"}," +"\"name\":\"{j1\"," +"\"type\":\"ZLLPresence\"," +"\"modelid\":\"SML001\"," +"\"manufacturername\":\"Philips\"," +"\"swversion\":\"6.1.0.18912\"," +"\"uniqueid\":\"{j2\"" +"}"; + +/* + + +Color Ligh +Dimmable Light +Color Temperature Light +Extended Color Light +On/Off light + +ZGPSwitch +ZLLSwitch +CLIPSwitch +CLIPOpenClose +CLIPPresence +CLIPTemperature +CLIPHumidity +Daylight +CLIPLightlevel + + + temperature ZLLTemperature + lightlevel ZLLLightLevel + presence ZLLPresence + */ + + +/* + +case 'T': +response->replace("{type}","ZLLTemperature"); +temp=glob_script_mem.fvars[hue_script[hue_devs].index[2]-1]; +light_status += "\"temperature\":"; +light_status += String(temp*100); +light_status += ","; +break; +case 'L': +response->replace("{type}","ZLLLightLevel"); +temp=glob_script_mem.fvars[hue_script[hue_devs].index[2]-1]; +light_status += "\"lightlevel\":"; +light_status += String(temp); +light_status += ","; +break; +case 'P': +response->replace("{type}","ZLLPresence"); +temp=glob_script_mem.fvars[hue_script[hue_devs].index[0]-1]; +light_status += "\"presence\":"; +if (temp==0)light_status += "false"; +else light_status += "true"; +light_status += ","; +break; +*/ + + +void Script_HueStatus(String *response, uint16_t hue_devs) { + + if (hue_script[hue_devs].type=='p') { + *response+=FPSTR(SCRIPT_HUE_LIGHTS_STATUS_JSON2); + response->replace("{j1",hue_script[hue_devs].name); + response->replace("{j2", GetHueDeviceId(hue_devs)); + uint8_t pwr=glob_script_mem.fvars[hue_script[hue_devs].index[0]-1]; + response->replace("{state}", (pwr ? "true" : "false")); + return; + } + + *response+=FPSTR(SCRIPT_HUE_LIGHTS_STATUS_JSON1); + uint8_t pwr=glob_script_mem.fvars[hue_script[hue_devs].index[0]-1]; + response->replace("{state}", (pwr ? "true" : "false")); + String light_status = ""; + if (hue_script[hue_devs].index[1]>0) { + // bri + light_status += "\"bri\":"; + uint32_t bri=glob_script_mem.fvars[hue_script[hue_devs].index[1]-1]; + if (bri > 254) bri = 254; + if (bri < 1) bri = 1; + light_status += String(bri); + light_status += ","; + } + if (hue_script[hue_devs].index[2]>0) { + // hue + uint32_t hue=glob_script_mem.fvars[hue_script[hue_devs].index[2]-1]; + //hue = changeUIntScale(hue, 0, 359, 0, 65535); + light_status += "\"hue\":"; + light_status += String(hue); + light_status += ","; + } + if (hue_script[hue_devs].index[3]>0) { + // sat + uint32_t sat=glob_script_mem.fvars[hue_script[hue_devs].index[3]-1] ; + if (sat > 254) sat = 254; + if (sat < 1) sat = 1; + light_status += "\"sat\":"; + light_status += String(sat); + light_status += ","; + } + if (hue_script[hue_devs].index[4]>0) { + // ct + uint32_t ct=glob_script_mem.fvars[hue_script[hue_devs].index[4]-1]; + light_status += "\"ct\":"; + light_status += String(ct); + light_status += ","; + } + + float temp; + switch (hue_script[hue_devs].type) { + case 'C': + response->replace("{type}","Color Ligh"); // alexa ok + response->replace("{m1","LST001"); + break; + case 'D': + response->replace("{type}","Dimmable Light"); // alexa NO + response->replace("{m1","LWB004"); + break; + case 'T': + response->replace("{type}","Color Temperature Light"); // alexa NO + response->replace("{m1","LTW011"); + break; + case 'E': + response->replace("{type}","Extended color light"); // alexa ok + response->replace("{m1","LCT007"); + break; + case 'S': + response->replace("{type}","On/Off light"); // alexa ok + response->replace("{m1","LCT007"); + break; + default: + response->replace("{type}","color light"); + response->replace("{m1","LST001"); + break; + } + + response->replace("{light_status}", light_status); + response->replace("{j1",hue_script[hue_devs].name); + response->replace("{j2", GetHueDeviceId(hue_devs)); + +} + +void Script_Check_Hue(String *response) { + if (!bitRead(Settings.rule_enabled, 0)) return; + + uint8_t hue_script_found=Run_Scripter(">H",-2,0); + if (hue_script_found!=99) return; + + char line[128]; + char tmp[128]; + uint8_t hue_devs=0; + uint8_t vindex=0; + char *cp; + char *lp=glob_script_mem.section_ptr+2; + while (lp) { + SCRIPT_SKIP_SPACES + while (*lp==SCRIPT_EOL) { + lp++; + } + if (!*lp || *lp=='#' || *lp=='>') { + break; + } + if (*lp!=';') { + // check this line + memcpy(line,lp,sizeof(line)); + line[sizeof(line)-1]=0; + cp=line; + for (uint32_t i=0; i0) *response+=",\""; + } + *response+=String(EncodeLightId(hue_devs+devices_present+1))+"\":"; + Script_HueStatus(response,hue_devs); + } + + hue_devs++; + } + if (*lp==SCRIPT_EOL) { + lp++; + } else { + lp = strchr(lp, SCRIPT_EOL); + if (!lp) break; + lp++; + } + } +#if 0 + if (response) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Hue: %d"), hue_devs); + toLog(">>>>"); + toLog(response->c_str()); + toLog(response->c_str()+LOGSZ); + } +#endif +} + +const char sHUE_LIGHT_RESPONSE_JSON[] PROGMEM = + "{\"success\":{\"/lights/{id/state/{cm\":{re}}"; + +const char sHUE_SENSOR_RESPONSE_JSON[] PROGMEM = + "{\"success\":{\"/lights/{id/state/{cm\":{re}}"; + +const char sHUE_ERROR_JSON[] PROGMEM = + "[{\"error\":{\"type\":901,\"address\":\"/\",\"description\":\"Internal Error\"}}]"; + + +// get alexa arguments +void Script_Handle_Hue(String *path) { + String response; + int code = 200; + uint16_t tmp = 0; + uint16_t hue = 0; + uint8_t sat = 0; + uint8_t bri = 254; + uint16_t ct = 0; + bool resp = false; + + uint8_t device = DecodeLightId(atoi(path->c_str())); + uint8_t index = device-devices_present-1; + + if (WebServer->args()) { + response = "["; + + StaticJsonBuffer<400> jsonBuffer; + JsonObject &hue_json = jsonBuffer.parseObject(WebServer->arg((WebServer->args())-1)); + if (hue_json.containsKey("on")) { + + response += FPSTR(sHUE_LIGHT_RESPONSE_JSON); + response.replace("{id", String(EncodeLightId(device))); + response.replace("{cm", "on"); + + bool on = hue_json["on"]; + switch(on) + { + case false : glob_script_mem.fvars[hue_script[index].index[0]-1]=0; + response.replace("{re", "false"); + break; + case true : glob_script_mem.fvars[hue_script[index].index[0]-1]=1; + response.replace("{re", "true"); + break; + } + glob_script_mem.type[hue_script[index].vindex[0]].bits.changed=1; + resp = true; + } + if (hue_json.containsKey("bri")) { // Brightness is a scale from 1 (the minimum the light is capable of) to 254 (the maximum). Note: a brightness of 1 is not off. + tmp = hue_json["bri"]; + bri=tmp; + if (254 <= bri) { bri = 255; } + if (resp) { response += ","; } + response += FPSTR(sHUE_LIGHT_RESPONSE_JSON); + response.replace("{id", String(EncodeLightId(device))); + response.replace("{cm", "bri"); + response.replace("{re", String(tmp)); + glob_script_mem.fvars[hue_script[index].index[1]-1]=bri; + glob_script_mem.type[hue_script[index].vindex[1]].bits.changed=1; + resp = true; + } + if (hue_json.containsKey("xy")) { // Saturation of the light. 254 is the most saturated (colored) and 0 is the least saturated (white). + float x, y; + x = hue_json["xy"][0]; + y = hue_json["xy"][1]; + const String &x_str = hue_json["xy"][0]; + const String &y_str = hue_json["xy"][1]; + uint8_t rr,gg,bb; + LightStateClass::XyToRgb(x, y, &rr, &gg, &bb); + LightStateClass::RgbToHsb(rr, gg, bb, &hue, &sat, nullptr); + if (resp) { response += ","; } + response += FPSTR(sHUE_LIGHT_RESPONSE_JSON); + response.replace("{id", String(device)); + response.replace("{cm", "xy"); + response.replace("{re", "[" + x_str + "," + y_str + "]"); + glob_script_mem.fvars[hue_script[index].index[2]-1]=hue; + glob_script_mem.type[hue_script[index].vindex[2]].bits.changed=1; + glob_script_mem.fvars[hue_script[index].index[3]-1]=sat; + glob_script_mem.type[hue_script[index].vindex[3]].bits.changed=1; + resp = true; + } + + if (hue_json.containsKey("hue")) { // The hue value is a wrapping value between 0 and 65535. Both 0 and 65535 are red, 25500 is green and 46920 is blue. + tmp = hue_json["hue"]; + //hue = changeUIntScale(tmp, 0, 65535, 0, 359); + //tmp = changeUIntScale(hue, 0, 359, 0, 65535); + hue=tmp; + if (resp) { response += ","; } + response += FPSTR(sHUE_LIGHT_RESPONSE_JSON); + response.replace("{id", String(EncodeLightId(device))); + response.replace("{cm", "hue"); + response.replace("{re", String(tmp)); + glob_script_mem.fvars[hue_script[index].index[2]-1]=hue; + glob_script_mem.type[hue_script[index].vindex[2]].bits.changed=1; + resp = true; + } + if (hue_json.containsKey("sat")) { // Saturation of the light. 254 is the most saturated (colored) and 0 is the least saturated (white). + tmp = hue_json["sat"]; + sat=tmp; + if (254 <= sat) { sat = 255; } + if (resp) { response += ","; } + response += FPSTR(sHUE_LIGHT_RESPONSE_JSON); + response.replace("{id", String(EncodeLightId(device))); + response.replace("{cm", "sat"); + response.replace("{re", String(tmp)); + glob_script_mem.fvars[hue_script[index].index[3]-1]=sat; + glob_script_mem.type[hue_script[index].vindex[3]].bits.changed=1; + resp = true; + } + if (hue_json.containsKey("ct")) { // Color temperature 153 (Cold) to 500 (Warm) + ct = hue_json["ct"]; + if (resp) { response += ","; } + response += FPSTR(sHUE_LIGHT_RESPONSE_JSON); + response.replace("{id", String(EncodeLightId(device))); + response.replace("{cm", "ct"); + response.replace("{re", String(ct)); + glob_script_mem.fvars[hue_script[index].index[4]-1]=ct; + glob_script_mem.type[hue_script[index].vindex[4]].bits.changed=1; + resp = true; + } + response += "]"; + + } else { + response = FPSTR(sHUE_ERROR_JSON); + } + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE " Result (%s)"), response.c_str()); + WSSend(code, CT_JSON, response); + if (resp) { + Run_Scripter(">E",2,0); + } +} +#endif // hue interface + + +#ifdef USE_SCRIPT_SUB_COMMAND +bool Script_SubCmd(void) { + if (!bitRead(Settings.rule_enabled, 0)) return false; + + if (tasm_cmd_activ) return false; + + char command[CMDSZ]; + strlcpy(command,XdrvMailbox.topic,CMDSZ); + uint32_t pl=XdrvMailbox.payload; + char pld[64]; + strlcpy(pld,XdrvMailbox.data,sizeof(pld)); + + char cmdbuff[128]; + char *cp=cmdbuff; + *cp++='#'; + strcpy(cp,XdrvMailbox.topic); + uint8_t tlen=strlen(XdrvMailbox.topic); + cp+=tlen; + if (XdrvMailbox.index > 0) { + *cp++=XdrvMailbox.index|0x30; + tlen++; + } + if ((XdrvMailbox.payload>0) || (XdrvMailbox.data_len>0)) { + *cp++='('; + strncpy(cp,XdrvMailbox.data,XdrvMailbox.data_len); + cp+=XdrvMailbox.data_len; + *cp++=')'; + *cp=0; + } + //toLog(cmdbuff); + uint32_t res=Run_Scripter(cmdbuff,tlen+1,0); + //AddLog_P2(LOG_LEVEL_INFO,">>%d",res); + if (res) return false; + else { + if (pl>=0) { + Response_P(S_JSON_COMMAND_NVALUE, command, pl); + } else { + Response_P(S_JSON_COMMAND_SVALUE, command, pld); + } + } + return true; +} +#endif + void execute_script(char *script) { -char *svd_sp=glob_script_mem.scriptptr; + char *svd_sp=glob_script_mem.scriptptr; strcat(script,"\n#"); glob_script_mem.scriptptr=script; Run_Scripter(">",1,0); glob_script_mem.scriptptr=svd_sp; - Scripter_save_pvars(); } +#define D_CMND_SCRIPT "Script" +#define D_CMND_SUBSCRIBE "Subscribe" +#define D_CMND_UNSUBSCRIBE "Unsubscribe" -enum ScriptCommands { CMND_SCRIPT }; -const char kScriptCommands[] PROGMEM = "Script"; +enum ScriptCommands { CMND_SCRIPT,CMND_SUBSCRIBE, CMND_UNSUBSCRIBE }; +const char kScriptCommands[] PROGMEM = D_CMND_SCRIPT "|" D_CMND_SUBSCRIBE "|" D_CMND_UNSUBSCRIBE; bool ScriptCommand(void) { char command[CMDSZ]; bool serviced = true; uint8_t index = XdrvMailbox.index; + if (tasm_cmd_activ) return false; + int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic, kScriptCommands); if (-1 == command_code) { serviced = false; // Unknown command } else if ((CMND_SCRIPT == command_code) && (index > 0)) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 2)) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 4)) { switch (XdrvMailbox.payload) { case 0: // Off case 1: // On - bitWrite(Settings.rule_enabled, index -1, XdrvMailbox.payload); + bitWrite(Settings.rule_enabled, index -1, XdrvMailbox.payload); + break; +#ifdef xSCRIPT_STRIP_COMMENTS + case 2: + bitWrite(Settings.rule_enabled, 1,0); + break; + case 3: + bitWrite(Settings.rule_enabled, 1,1); + break; +#endif } } else { if ('>' == XdrvMailbox.data[0]) { // execute script - for (uint8_t count=0; count, [, ] + String result = ScriptSubscribe(XdrvMailbox.data, XdrvMailbox.data_len); + Response_P(S_JSON_COMMAND_SVALUE, command, result.c_str()); + } else if (CMND_UNSUBSCRIBE == command_code) { //MQTT Un-subscribe command. UnSubscribe + String result = ScriptUnsubscribe(XdrvMailbox.data, XdrvMailbox.data_len); + Response_P(S_JSON_COMMAND_SVALUE, command, result.c_str()); +#endif //SUPPORT_MQTT_EVENT + } return serviced; } #ifdef USE_SCRIPT_FATFS + +uint16_t xFAT_DATE(uint16_t year, uint8_t month, uint8_t day) { + return (year - 1980) << 9 | month << 5 | day; +} +uint16_t xFAT_TIME(uint8_t hour, uint8_t minute, uint8_t second) { + return hour << 11 | minute << 5 | second >> 1; +} + void dateTime(uint16_t* date, uint16_t* time) { // return date using FAT_DATE macro to format fields - *date = FAT_DATE(RtcTime.year,RtcTime.month, RtcTime.day_of_month); + *date = xFAT_DATE(RtcTime.year,RtcTime.month, RtcTime.day_of_month); // return time using FAT_TIME macro to format fields - *time = FAT_TIME(RtcTime.hour,RtcTime.minute,RtcTime.second); + *time = xFAT_TIME(RtcTime.hour,RtcTime.minute,RtcTime.second); +} + +#endif + + + +#ifdef SUPPORT_MQTT_EVENT +/********************************************************************************************/ +/* + * Script: Process received MQTT message. + * If the message is in our subscription list, trigger an event with the value parsed from MQTT data + * Input: + * void - We are going to access XdrvMailbox data directly. + * Return: + * true - The message is consumed. + * false - The message is not in our list. + */ +bool ScriptMqttData(void) +{ + bool serviced = false; + //toLog(">>> 1"); + toLog(XdrvMailbox.data); + if (XdrvMailbox.data_len < 1 || XdrvMailbox.data_len > 256) { + return false; + } + String sTopic = XdrvMailbox.topic; + String sData = XdrvMailbox.data; + //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Script: MQTT Topic %s, Event %s"), XdrvMailbox.topic, XdrvMailbox.data); + MQTT_Subscription event_item; + //Looking for matched topic + for (uint32_t index = 0; index < subscriptions.size(); index++) { + event_item = subscriptions.get(index); + + //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Script: Match MQTT message Topic %s with subscription topic %s"), sTopic.c_str(), event_item.Topic.c_str()); + if (sTopic.startsWith(event_item.Topic)) { + //This topic is subscribed by us, so serve it + serviced = true; + String value; + String lkey; + if (event_item.Key.length() == 0) { //If did not specify Key + value = sData; + } else { //If specified Key, need to parse Key/Value from JSON data + StaticJsonBuffer<400> jsonBuf; + JsonObject& jsonData = jsonBuf.parseObject(sData); + String key1 = event_item.Key; + String key2; + if (!jsonData.success()) break; //Failed to parse JSON data, ignore this message. + int dot; + if ((dot = key1.indexOf('.')) > 0) { + key2 = key1.substring(dot+1); + key1 = key1.substring(0, dot); + lkey=key2; + if (!jsonData[key1][key2].success()) break; //Failed to get the key/value, ignore this message. + value = (const char *)jsonData[key1][key2]; + } else { + if (!jsonData[key1].success()) break; + value = (const char *)jsonData[key1]; + lkey=key1; + } + } + value.trim(); + char sbuffer[128]; + + if (!strncmp(lkey.c_str(),"Epoch",5)) { + uint32_t ep=atoi(value.c_str())-(uint32_t)EPOCH_OFFSET; + snprintf_P(sbuffer, sizeof(sbuffer), PSTR(">%s=%d\n"), event_item.Event.c_str(),ep); + } else { + snprintf_P(sbuffer, sizeof(sbuffer), PSTR(">%s=\"%s\"\n"), event_item.Event.c_str(), value.c_str()); + } + //toLog(sbuffer); + execute_script(sbuffer); + } + } + return serviced; +} + +/********************************************************************************************/ +/* + * Subscribe a MQTT topic (with or without key) and assign an event name to it + * Command Subscribe format: + * Subscribe , [, ] + * This command will subscribe a and give it an event name . + * The optional parameter is for parse the specified key/value from MQTT message + * payload with JSON format. + * Subscribe + * Subscribe command without any parameter will list all topics currently subscribed. + * Input: + * data - A char buffer with all the parameters + * data_len - Length of the parameters + * Return: + * A string include subscribed event, topic and key. + */ +String ScriptSubscribe(const char *data, int data_len) +{ + MQTT_Subscription subscription_item; + String events; + if (data_len > 0) { + char parameters[data_len+1]; + memcpy(parameters, data, data_len); + parameters[data_len] = '\0'; + String event_name, topic, key; + + char * pos = strtok(parameters, ","); + if (pos) { + event_name = Trim(pos); + pos = strtok(nullptr, ","); + if (pos) { + topic = Trim(pos); + pos = strtok(nullptr, ","); + if (pos) { + key = Trim(pos); + } + } + } + //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Script: Subscribe command with parameters: %s, %s, %s."), event_name.c_str(), topic.c_str(), key.c_str()); + //event_name.toUpperCase(); + if (event_name.length() > 0 && topic.length() > 0) { + //Search all subscriptions + for (uint32_t index=0; index < subscriptions.size(); index++) { + if (subscriptions.get(index).Event.equals(event_name)) { + //If find exists one, remove it. + String stopic = subscriptions.get(index).Topic + "/#"; + MqttUnsubscribe(stopic.c_str()); + subscriptions.remove(index); + break; + } + } + //Add "/#" to the topic + if (!topic.endsWith("#")) { + if (topic.endsWith("/")) { + topic.concat("#"); + } else { + topic.concat("/#"); + } + } + //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Script: New topic: %s."), topic.c_str()); + //MQTT Subscribe + subscription_item.Event = event_name; + subscription_item.Topic = topic.substring(0, topic.length() - 2); //Remove "/#" so easy to match + subscription_item.Key = key; + subscriptions.add(subscription_item); + + MqttSubscribe(topic.c_str()); + events.concat(event_name + "," + topic + + (key.length()>0 ? "," : "") + + key); + } else { + events = D_JSON_WRONG_PARAMETERS; + } + } else { + //If did not specify the event name, list all subscribed event + for (uint32_t index=0; index < subscriptions.size(); index++) { + subscription_item = subscriptions.get(index); + events.concat(subscription_item.Event + "," + subscription_item.Topic + + (subscription_item.Key.length()>0 ? "," : "") + + subscription_item.Key + "; "); + } + } + return events; +} + +/********************************************************************************************/ +/* + * Unsubscribe specified MQTT event. If no event specified, Unsubscribe all. + * Command Unsubscribe format: + * Unsubscribe [] + * Input: + * data - Event name + * data_len - Length of the parameters + * Return: + * list all the events unsubscribed. + */ +String ScriptUnsubscribe(const char * data, int data_len) +{ + MQTT_Subscription subscription_item; + String events; + if (data_len > 0) { + for (uint32_t index = 0; index < subscriptions.size(); index++) { + subscription_item = subscriptions.get(index); + if (subscription_item.Event.equalsIgnoreCase(data)) { + String stopic = subscription_item.Topic + "/#"; + MqttUnsubscribe(stopic.c_str()); + events = subscription_item.Event; + subscriptions.remove(index); + break; + } + } + } else { + //If did not specify the event name, unsubscribe all event + String stopic; + while (subscriptions.size() > 0) { + events.concat(subscriptions.get(0).Event + "; "); + stopic = subscriptions.get(0).Topic + "/#"; + MqttUnsubscribe(stopic.c_str()); + subscriptions.remove(0); + } + } + return events; +} +#endif // SUPPORT_MQTT_EVENT + + + +#ifdef USE_SCRIPT_WEB_DISPLAY + +void Script_Check_HTML_Setvars(void) { + + if (!HttpCheckPriviledgedAccess()) { return; } + + if (WebServer->hasArg("sv")) { + String stmp = WebServer->arg("sv"); + char cmdbuf[64]; + memset(cmdbuf,0,sizeof(cmdbuf)); + char *cp=cmdbuf; + *cp++='>'; + strncpy(cp,stmp.c_str(),sizeof(cmdbuf)-1); + char *cp1=strchr(cp,'_'); + if (!cp1) return; + *cp1=0; + char vname[32]; + strncpy(vname,cp,sizeof(vname)); + *cp1='='; + cp1++; + + struct T_INDEX ind; + uint8_t vtype; + isvar(vname,&vtype,&ind,0,0,0); + if (vtype!=NUM_RES && vtype&STYPE) { + // string type must insert quotes + uint8_t tlen=strlen(cp1); + memmove(cp1+1,cp1,tlen); + *cp1='\"'; + *(cp1+tlen+1)='\"'; + } + + //toLog(cmdbuf); + execute_script(cmdbuf); + Run_Scripter(">E",2,0); + } +} + + +const char SCRIPT_MSG_BUTTONa[] PROGMEM = + ""; + +const char SCRIPT_MSG_BUTTONa_TBL[] PROGMEM = + ""; + +const char SCRIPT_MSG_BUTTONb[] PROGMEM = + ""; + +const char SCRIPT_MSG_BUT_START[] PROGMEM = + "
"; +const char SCRIPT_MSG_BUT_START_TBL[] PROGMEM = + ""; + +const char SCRIPT_MSG_BUT_STOP[] PROGMEM = + ""; +const char SCRIPT_MSG_BUT_STOP_TBL[] PROGMEM = + "
"; + +const char SCRIPT_MSG_SLIDER[] PROGMEM = + "
%s
%s%s
" + "
"; + +const char SCRIPT_MSG_CHKBOX[] PROGMEM = + "
"; + +const char SCRIPT_MSG_TEXTINP[] PROGMEM = + "
"; + +const char SCRIPT_MSG_NUMINP[] PROGMEM = + "
"; + + +void ScriptGetVarname(char *nbuf,char *sp, uint32_t blen) { +uint32_t cnt; + for (cnt=0;cntW",-2,0); + if (web_script==99) { + char line[128]; + char tmp[128]; + uint8_t optflg=0; + char *lp=glob_script_mem.section_ptr+2; + while (lp) { + while (*lp==SCRIPT_EOL) { + lp++; + } + if (!*lp || *lp=='#' || *lp=='>') { + break; + } + if (*lp!=';') { + // send this line to web + memcpy(line,lp,sizeof(line)); + line[sizeof(line)-1]=0; + char *cp=line; + for (uint32_t i=0; i0) { + cp="checked='checked'"; + uval=0; + } else { + cp=""; + uval=1; + } + WSContentSend_PD(SCRIPT_MSG_CHKBOX,label,(char*)cp,uval,vname); + + } else if (!strncmp(lin,"bu(",3)) { + char *lp=lin+3; + uint8_t bcnt=0; + char *found=lin; + while (bcnt<4) { + found=strstr(found,"bu("); + if (!found) break; + found+=3; + bcnt++; + } + uint8_t proz=100/bcnt; + if (!optflg && bcnt>1) proz-=2; + if (optflg) WSContentSend_PD(SCRIPT_MSG_BUT_START_TBL); + else WSContentSend_PD(SCRIPT_MSG_BUT_START); + for (uint32_t cnt=0;cnt0) { + cp=ontxt; + uval=0; + } else { + cp=offtxt; + uval=1; + } + if (bcnt>1 && cnt==bcnt-1) { + if (!optflg) proz+=2; + } + if (!optflg) { + WSContentSend_PD(SCRIPT_MSG_BUTTONa,proz,uval,vname,cp); + } else { + WSContentSend_PD(SCRIPT_MSG_BUTTONa_TBL,proz,uval,vname,cp); + } + if (bcnt>1 && cnt%s
"),tmp); + } else { + WSContentSend_PD(PSTR("{s}%s{e}"),tmp); + } + } + } + if (*lp==SCRIPT_EOL) { + lp++; + } else { + lp = strchr(lp, SCRIPT_EOL); + if (!lp) break; + lp++; + } + } + } +} +#endif //USE_SCRIPT_WEB_DISPLAY + + +#ifdef USE_SENDMAIL +void script_send_email_body(BearSSL::WiFiClientSecure_light *client) { +uint8_t msect=Run_Scripter(">m",-2,0); + if (msect==99) { + char line[128]; + char tmp[128]; + char *lp=glob_script_mem.section_ptr+2; + while (lp) { + while (*lp==SCRIPT_EOL) { + lp++; + } + if (!*lp || *lp=='#' || *lp=='>') { + break; + } + if (*lp!=';') { + // send this line to smtp + memcpy(line,lp,sizeof(line)); + line[sizeof(line)-1]=0; + char *cp=line; + for (uint32_t i=0; iprintln(tmp); + } + if (*lp==SCRIPT_EOL) { + lp++; + } else { + lp = strchr(lp, SCRIPT_EOL); + if (!lp) break; + lp++; + } + } + } else { + client->println("*"); + } } #endif +#ifdef USE_SCRIPT_JSON_EXPORT +void ScriptJsonAppend(void) { + uint8_t web_script=Run_Scripter(">J",-2,0); + if (web_script==99) { + char line[128]; + char tmp[128]; + char *lp=glob_script_mem.section_ptr+2; + while (lp) { + while (*lp==SCRIPT_EOL) { + lp++; + } + if (!*lp || *lp=='#' || *lp=='>') { + break; + } + if (*lp!=';') { + // send this line to mqtt + memcpy(line,lp,sizeof(line)); + line[sizeof(line)-1]=0; + char *cp=line; + for (uint32_t i=0; iB",2,0); + if (bitRead(Settings.rule_enabled, 0)) { + Run_Scripter(">B",2,0); + fast_script=Run_Scripter(">F",-2,0); +#if defined(USE_SCRIPT_HUE) && defined(USE_WEBSERVER) && defined(USE_EMULATION) && defined(USE_EMULATION_HUE) && defined(USE_LIGHT) + Script_Check_Hue(0); +#endif + } break; case FUNC_EVERY_100_MSECOND: ScripterEvery100ms(); @@ -3044,6 +4804,8 @@ bool Xdrv10(uint8_t function) break; case FUNC_WEB_ADD_HANDLER: WebServer->on("/" WEB_HANDLE_SCRIPT, HandleScriptConfiguration); + WebServer->on("/ta",HTTP_POST, HandleScriptTextareaConfiguration); + #ifdef USE_SCRIPT_FATFS WebServer->on("/u3", HTTP_POST,[]() { WebServer->sendHeader("Location","/u3");WebServer->send(303);},script_upload); WebServer->on("/u3", HTTP_GET,ScriptFileUploadSuccess); @@ -3057,9 +4819,45 @@ bool Xdrv10(uint8_t function) Scripter_save_pvars(); } break; +#ifdef SUPPORT_MQTT_EVENT + case FUNC_MQTT_DATA: + if (bitRead(Settings.rule_enabled, 0)) { + result = ScriptMqttData(); + } + break; +#endif //SUPPORT_MQTT_EVENT +#ifdef USE_SCRIPT_WEB_DISPLAY + case FUNC_WEB_SENSOR: + if (bitRead(Settings.rule_enabled, 0)) { + ScriptWebShow(); + } + break; +#endif //USE_SCRIPT_WEB_DISPLAY + +#ifdef USE_SCRIPT_JSON_EXPORT + case FUNC_JSON_APPEND: + if (bitRead(Settings.rule_enabled, 0)) { + ScriptJsonAppend(); + } + break; +#endif //USE_SCRIPT_JSON_EXPORT + +#ifdef USE_BUTTON_EVENT + case FUNC_BUTTON_PRESSED: + if (bitRead(Settings.rule_enabled, 0)) { + if ((script_button[XdrvMailbox.index]&1)!=(XdrvMailbox.payload&1)) { + script_button[XdrvMailbox.index]=XdrvMailbox.payload; + Run_Scripter(">b",2,0); + } + } + break; +#endif + } return result; } + + #endif // Do not USE_RULES #endif // USE_SCRIPT diff --git a/sonoff/xdrv_11_knx.ino b/sonoff/xdrv_11_knx.ino index 7a477bff8..6fa6231c0 100644 --- a/sonoff/xdrv_11_knx.ino +++ b/sonoff/xdrv_11_knx.ino @@ -188,17 +188,20 @@ const char *device_param_cb[] = { }; // Commands -#define D_CMND_KNXTXCMND "KnxTx_Cmnd" -#define D_CMND_KNXTXVAL "KnxTx_Val" -#define D_CMND_KNX_ENABLED "Knx_Enabled" -#define D_CMND_KNX_ENHANCED "Knx_Enhanced" -#define D_CMND_KNX_PA "Knx_PA" -#define D_CMND_KNX_GA "Knx_GA" -#define D_CMND_KNX_CB "Knx_CB" -enum KnxCommands { CMND_KNXTXCMND, CMND_KNXTXVAL, CMND_KNX_ENABLED, CMND_KNX_ENHANCED, CMND_KNX_PA, - CMND_KNX_GA, CMND_KNX_CB } ; -const char kKnxCommands[] PROGMEM = D_CMND_KNXTXCMND "|" D_CMND_KNXTXVAL "|" D_CMND_KNX_ENABLED "|" - D_CMND_KNX_ENHANCED "|" D_CMND_KNX_PA "|" D_CMND_KNX_GA "|" D_CMND_KNX_CB ; +#define D_PRFX_KNX "Knx" +#define D_CMND_KNXTXCMND "Tx_Cmnd" +#define D_CMND_KNXTXVAL "Tx_Val" +#define D_CMND_KNX_ENABLED "_Enabled" +#define D_CMND_KNX_ENHANCED "_Enhanced" +#define D_CMND_KNX_PA "_PA" +#define D_CMND_KNX_GA "_GA" +#define D_CMND_KNX_CB "_CB" + +const char kKnxCommands[] PROGMEM = D_PRFX_KNX "|" // Prefix + D_CMND_KNXTXCMND "|" D_CMND_KNXTXVAL "|" D_CMND_KNX_ENABLED "|" D_CMND_KNX_ENHANCED "|" D_CMND_KNX_PA "|" D_CMND_KNX_GA "|" D_CMND_KNX_CB ; + +void (* const KnxCommand[])(void) PROGMEM = { + &CmndKnxTxCmnd, &CmndKnxTxVal, &CmndKnxEnabled, &CmndKnxEnhanced, &CmndKnxPa, &CmndKnxGa, &CmndKnxCb }; uint8_t KNX_GA_Search( uint8_t param, uint8_t start = 0 ) { @@ -489,7 +492,9 @@ void KNX_INIT(void) if (GetUsedInModule(GPIO_DHT11, my_module.io)) { device_param[KNX_TEMPERATURE-1].show = true; } if (GetUsedInModule(GPIO_DHT22, my_module.io)) { device_param[KNX_TEMPERATURE-1].show = true; } if (GetUsedInModule(GPIO_SI7021, my_module.io)) { device_param[KNX_TEMPERATURE-1].show = true; } +#ifdef USE_DS18x20 if (GetUsedInModule(GPIO_DSB, my_module.io)) { device_param[KNX_TEMPERATURE-1].show = true; } +#endif if (GetUsedInModule(GPIO_DHT11, my_module.io)) { device_param[KNX_HUMIDITY-1].show = true; } if (GetUsedInModule(GPIO_DHT22, my_module.io)) { device_param[KNX_HUMIDITY-1].show = true; } if (GetUsedInModule(GPIO_SI7021, my_module.io)) { device_param[KNX_HUMIDITY-1].show = true; } @@ -572,7 +577,7 @@ void KNX_CB_Action(message_t const &msg, void *arg) else if (chan->type < 17) // Toggle Relays { if (!toggle_inhibit) { - ExecuteCommandPower((chan->type) -8, 2, SRC_KNX); + ExecuteCommandPower((chan->type) -8, POWER_TOGGLE, SRC_KNX); if (Settings.flag.knx_enable_enhancement) { toggle_inhibit = TOGGLE_INHIBIT_TIME; } @@ -988,21 +993,17 @@ void KNX_Save_Settings(void) #endif // USE_KNX_WEB_MENU #endif // USE_WEBSERVER +/*********************************************************************************************\ + * Commands +\*********************************************************************************************/ -bool KnxCommand(void) +void CmndKnxTxCmnd(void) { - char command[CMDSZ]; - uint8_t index = XdrvMailbox.index; - int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic, kKnxCommands); - - if (-1 == command_code) { return false; } // Unknown command - - else if ((CMND_KNXTXCMND == command_code) && (index > 0) && (index <= MAX_KNXTX_CMNDS) && (XdrvMailbox.data_len > 0)) { - // index <- KNX SLOT to use + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_KNXTX_CMNDS) && (XdrvMailbox.data_len > 0) && Settings.flag.knx_enabled) { + // XdrvMailbox.index <- KNX SLOT to use // XdrvMailbox.payload <- data to send - if (!(Settings.flag.knx_enabled)) { return false; } // Search all the registered GA that has that output (variable: KNX SLOTx) as parameter - uint8_t i = KNX_GA_Search(index + KNX_SLOT1 -1); + uint8_t i = KNX_GA_Search(XdrvMailbox.index + KNX_SLOT1 -1); while ( i != KNX_Empty ) { KNX_addr.value = Settings.knx_GA_addr[i]; knx.write_1bit(KNX_addr, !(XdrvMailbox.payload == 0)); @@ -1012,21 +1013,22 @@ bool KnxCommand(void) } AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s = %d " D_SENT_TO " %d.%d.%d"), - device_param_ga[index + KNX_SLOT1 -2], !(XdrvMailbox.payload == 0), + device_param_ga[XdrvMailbox.index + KNX_SLOT1 -2], !(XdrvMailbox.payload == 0), KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member); - i = KNX_GA_Search(index + KNX_SLOT1 -1, i + 1); + i = KNX_GA_Search(XdrvMailbox.index + KNX_SLOT1 -1, i + 1); } - snprintf_P (mqtt_data, sizeof(mqtt_data), PSTR("{\"%s%d\":\"%s\"}"), - command, index, XdrvMailbox.data ); + ResponseCmndIdxChar (XdrvMailbox.data ); } +} - else if ((CMND_KNXTXVAL == command_code) && (index > 0) && (index <= MAX_KNXTX_CMNDS) && (XdrvMailbox.data_len > 0)) { - // index <- KNX SLOT to use +void CmndKnxTxVal(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_KNXTX_CMNDS) && (XdrvMailbox.data_len > 0) && Settings.flag.knx_enabled) { + // XdrvMailbox.index <- KNX SLOT to use // XdrvMailbox.payload <- data to send - if (!(Settings.flag.knx_enabled)) { return false; } // Search all the registered GA that has that output (variable: KNX SLOTx) as parameter - uint8_t i = KNX_GA_Search(index + KNX_SLOT1 -1); + uint8_t i = KNX_GA_Search(XdrvMailbox.index + KNX_SLOT1 -1); while ( i != KNX_Empty ) { KNX_addr.value = Settings.knx_GA_addr[i]; @@ -1040,79 +1042,61 @@ bool KnxCommand(void) } AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s = %s " D_SENT_TO " %d.%d.%d"), - device_param_ga[index + KNX_SLOT1 -2], XdrvMailbox.data, + device_param_ga[XdrvMailbox.index + KNX_SLOT1 -2], XdrvMailbox.data, KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member); - i = KNX_GA_Search(index + KNX_SLOT1 -1, i + 1); + i = KNX_GA_Search(XdrvMailbox.index + KNX_SLOT1 -1, i + 1); } - snprintf_P (mqtt_data, sizeof(mqtt_data), PSTR("{\"%s%d\":\"%s\"}"), - command, index, XdrvMailbox.data ); + ResponseCmndIdxChar (XdrvMailbox.data ); } +} - else if (CMND_KNX_ENABLED == command_code) { - if (!XdrvMailbox.data_len) { - if (Settings.flag.knx_enabled) { - snprintf_P(XdrvMailbox.data, sizeof(XdrvMailbox.data), PSTR("1")); - } else { - snprintf_P(XdrvMailbox.data, sizeof(XdrvMailbox.data), PSTR("0")); - } - } else { - if (XdrvMailbox.payload == 1) { - Settings.flag.knx_enabled = 1; - } else if (XdrvMailbox.payload == 0) { - Settings.flag.knx_enabled = 0; - } else { return false; } // Incomplete command +void CmndKnxEnabled(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) { + Settings.flag.knx_enabled = XdrvMailbox.payload; + } + ResponseCmndChar (GetStateText(Settings.flag.knx_enabled) ); +} + +void CmndKnxEnhanced(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) { + Settings.flag.knx_enable_enhancement = XdrvMailbox.payload; + } + ResponseCmndChar (GetStateText(Settings.flag.knx_enable_enhancement) ); +} + +void CmndKnxPa(void) +{ + if (XdrvMailbox.data_len) { + if (strstr(XdrvMailbox.data, ".") != nullptr) { // Process parameter entry + char sub_string[XdrvMailbox.data_len]; + + int pa_area = atoi(subStr(sub_string, XdrvMailbox.data, ".", 1)); + int pa_line = atoi(subStr(sub_string, XdrvMailbox.data, ".", 2)); + int pa_member = atoi(subStr(sub_string, XdrvMailbox.data, ".", 3)); + + if ( ((pa_area == 0) && (pa_line == 0) && (pa_member == 0)) + || (pa_area > 15) || (pa_line > 15) || (pa_member > 255) ) { + Response_P (PSTR("{\"%s\":\"" D_ERROR "\"}"), XdrvMailbox.command ); + return; + } // Invalid command + + KNX_addr.pa.area = pa_area; + KNX_addr.pa.line = pa_line; + KNX_addr.pa.member = pa_member; + Settings.knx_physsical_addr = KNX_addr.value; } - snprintf_P (mqtt_data, sizeof(mqtt_data), PSTR("{\"%s\":\"%s\"}"), - command, XdrvMailbox.data ); } + KNX_addr.value = Settings.knx_physsical_addr; + Response_P (PSTR("{\"%s\":\"%d.%d.%d\"}"), + XdrvMailbox.command, KNX_addr.pa.area, KNX_addr.pa.line, KNX_addr.pa.member ); +} - else if (CMND_KNX_ENHANCED == command_code) { - if (!XdrvMailbox.data_len) { - if (Settings.flag.knx_enable_enhancement) { - snprintf_P(XdrvMailbox.data, sizeof(XdrvMailbox.data), PSTR("1")); - } else { - snprintf_P(XdrvMailbox.data, sizeof(XdrvMailbox.data), PSTR("0")); - } - } else { - if (XdrvMailbox.payload == 1) { - Settings.flag.knx_enable_enhancement = 1; - } else if (XdrvMailbox.payload == 0) { - Settings.flag.knx_enable_enhancement = 0; - } else { return false; } // Incomplete command - } - snprintf_P (mqtt_data, sizeof(mqtt_data), PSTR("{\"%s\":\"%s\"}"), - command, XdrvMailbox.data ); - } - - else if (CMND_KNX_PA == command_code) { - if (XdrvMailbox.data_len) { - if (strstr(XdrvMailbox.data, ".") != nullptr) { // Process parameter entry - char sub_string[XdrvMailbox.data_len]; - - int pa_area = atoi(subStr(sub_string, XdrvMailbox.data, ".", 1)); - int pa_line = atoi(subStr(sub_string, XdrvMailbox.data, ".", 2)); - int pa_member = atoi(subStr(sub_string, XdrvMailbox.data, ".", 3)); - - if ( ((pa_area == 0) && (pa_line == 0) && (pa_member == 0)) - || (pa_area > 15) || (pa_line > 15) || (pa_member > 255) ) { - snprintf_P (mqtt_data, sizeof(mqtt_data), PSTR("{\"%s\":\"" D_ERROR "\"}"), - command ); - return true; - } // Invalid command - - KNX_addr.pa.area = pa_area; - KNX_addr.pa.line = pa_line; - KNX_addr.pa.member = pa_member; - Settings.knx_physsical_addr = KNX_addr.value; - } - } - KNX_addr.value = Settings.knx_physsical_addr; - snprintf_P (mqtt_data, sizeof(mqtt_data), PSTR("{\"%s\":\"%d.%d.%d\"}"), - command, KNX_addr.pa.area, KNX_addr.pa.line, KNX_addr.pa.member ); - } - - else if ((CMND_KNX_GA == command_code) && (index > 0) && (index <= MAX_KNX_GA)) { +void CmndKnxGa(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_KNX_GA)) { if (XdrvMailbox.data_len) { if (strstr(XdrvMailbox.data, ",") != nullptr) { // Process parameter entry char sub_string[XdrvMailbox.data_len]; @@ -1126,42 +1110,44 @@ bool KnxCommand(void) || (ga_area > 31) || (ga_line > 7) || (ga_member > 255) || (ga_option < 0) || ((ga_option > KNX_MAX_device_param ) && (ga_option != KNX_Empty)) || (!device_param[ga_option-1].show) ) { - snprintf_P (mqtt_data, sizeof(mqtt_data), PSTR("{\"%s\":\"" D_ERROR "\"}"), command ); - return true; + Response_P (PSTR("{\"%s\":\"" D_ERROR "\"}"), XdrvMailbox.command ); + return; } // Invalid command KNX_addr.ga.area = ga_area; KNX_addr.ga.line = ga_line; KNX_addr.ga.member = ga_member; - if ( index > Settings.knx_GA_registered ) { + if ( XdrvMailbox.index > Settings.knx_GA_registered ) { Settings.knx_GA_registered ++; - index = Settings.knx_GA_registered; + XdrvMailbox.index = Settings.knx_GA_registered; } - Settings.knx_GA_addr[index -1] = KNX_addr.value; - Settings.knx_GA_param[index -1] = ga_option; + Settings.knx_GA_addr[XdrvMailbox.index -1] = KNX_addr.value; + Settings.knx_GA_param[XdrvMailbox.index -1] = ga_option; } else { if ( (XdrvMailbox.payload <= Settings.knx_GA_registered) && (XdrvMailbox.payload > 0) ) { - index = XdrvMailbox.payload; + XdrvMailbox.index = XdrvMailbox.payload; } else { - snprintf_P (mqtt_data, sizeof(mqtt_data), PSTR("{\"%s\":\"" D_ERROR "\"}"), command ); - return true; + Response_P (PSTR("{\"%s\":\"" D_ERROR "\"}"), XdrvMailbox.command ); + return; } } - if ( index <= Settings.knx_GA_registered ) { - KNX_addr.value = Settings.knx_GA_addr[index -1]; - snprintf_P (mqtt_data, sizeof(mqtt_data), PSTR("{\"%s%d\":\"%s, %d/%d/%d\"}"), - command, index, device_param_ga[Settings.knx_GA_param[index-1]-1], + if ( XdrvMailbox.index <= Settings.knx_GA_registered ) { + KNX_addr.value = Settings.knx_GA_addr[XdrvMailbox.index -1]; + Response_P (PSTR("{\"%s%d\":\"%s, %d/%d/%d\"}"), + XdrvMailbox.command, XdrvMailbox.index, device_param_ga[Settings.knx_GA_param[XdrvMailbox.index-1]-1], KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member ); } } else { - snprintf_P (mqtt_data, sizeof(mqtt_data), PSTR("{\"%s\":\"%d\"}"), - command, Settings.knx_GA_registered ); + ResponseCmndNumber (Settings.knx_GA_registered ); } } +} - else if ((CMND_KNX_CB == command_code) && (index > 0) && (index <= MAX_KNX_CB)) { +void CmndKnxCb(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_KNX_CB)) { if (XdrvMailbox.data_len) { if (strstr(XdrvMailbox.data, ",") != nullptr) { // Process parameter entry char sub_string[XdrvMailbox.data_len]; @@ -1175,47 +1161,41 @@ bool KnxCommand(void) || (cb_area > 31) || (cb_line > 7) || (cb_member > 255) || (cb_option < 0) || ((cb_option > KNX_MAX_device_param ) && (cb_option != KNX_Empty)) || (!device_param[cb_option-1].show) ) { - snprintf_P (mqtt_data, sizeof(mqtt_data), PSTR("{\"%s\":\"" D_ERROR "\"}"), command ); - return true; + Response_P (PSTR("{\"%s\":\"" D_ERROR "\"}"), XdrvMailbox.command ); + return; } // Invalid command KNX_addr.ga.area = cb_area; KNX_addr.ga.line = cb_line; KNX_addr.ga.member = cb_member; - if ( index > Settings.knx_CB_registered ) { + if ( XdrvMailbox.index > Settings.knx_CB_registered ) { Settings.knx_CB_registered ++; - index = Settings.knx_CB_registered; + XdrvMailbox.index = Settings.knx_CB_registered; } - Settings.knx_CB_addr[index -1] = KNX_addr.value; - Settings.knx_CB_param[index -1] = cb_option; + Settings.knx_CB_addr[XdrvMailbox.index -1] = KNX_addr.value; + Settings.knx_CB_param[XdrvMailbox.index -1] = cb_option; } else { if ( (XdrvMailbox.payload <= Settings.knx_CB_registered) && (XdrvMailbox.payload > 0) ) { - index = XdrvMailbox.payload; + XdrvMailbox.index = XdrvMailbox.payload; } else { - snprintf_P (mqtt_data, sizeof(mqtt_data), PSTR("{\"%s\":\"" D_ERROR "\"}"), command ); - return true; + Response_P (PSTR("{\"%s\":\"" D_ERROR "\"}"), XdrvMailbox.command ); + return; } } - if ( index <= Settings.knx_CB_registered ) { - KNX_addr.value = Settings.knx_CB_addr[index -1]; - snprintf_P (mqtt_data, sizeof(mqtt_data), PSTR("{\"%s%d\":\"%s, %d/%d/%d\"}"), - command, index, device_param_cb[Settings.knx_CB_param[index-1]-1], + if ( XdrvMailbox.index <= Settings.knx_CB_registered ) { + KNX_addr.value = Settings.knx_CB_addr[XdrvMailbox.index -1]; + Response_P (PSTR("{\"%s%d\":\"%s, %d/%d/%d\"}"), + XdrvMailbox.command, XdrvMailbox.index, device_param_cb[Settings.knx_CB_param[XdrvMailbox.index-1]-1], KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member ); } } else { - snprintf_P (mqtt_data, sizeof(mqtt_data), PSTR("{\"%s\":\"%d\"}"), - command, Settings.knx_CB_registered ); + ResponseCmndNumber (Settings.knx_CB_registered ); } } - - else { return false; } // Incomplete command - - return true; } - /*********************************************************************************************\ * Interface \*********************************************************************************************/ @@ -1246,7 +1226,7 @@ bool Xdrv11(uint8_t function) } break; case FUNC_COMMAND: - result = KnxCommand(); + result = DecodeCommand(kKnxCommands, KnxCommand); break; // case FUNC_SET_POWER: // break; diff --git a/sonoff/xdrv_12_home_assistant.ino b/sonoff/xdrv_12_home_assistant.ino index 78002fbbd..d5a52a491 100644 --- a/sonoff/xdrv_12_home_assistant.ino +++ b/sonoff/xdrv_12_home_assistant.ino @@ -54,7 +54,7 @@ const char HASS_DISCOVER_LIGHT_DIMMER[] PROGMEM = ",\"bri_cmd_t\":\"%s\"," // cmnd/led2/Dimmer "\"bri_stat_t\":\"%s\"," // stat/led2/RESULT "\"bri_scl\":100," // 100% - "\"on_cmd_type\":\"brightness\"," // power on (first), power on (last), no power on (brightness) + "\"on_cmd_type\":\"%s\"," // power on (first), power on (last), no power on (brightness) "\"bri_val_tpl\":\"{{value_json." D_CMND_DIMMER "}}\""; const char HASS_DISCOVER_LIGHT_COLOR[] PROGMEM = @@ -88,7 +88,12 @@ const char HASS_DISCOVER_SENSOR[] PROGMEM = const char HASS_DISCOVER_SENSOR_TEMP[] PROGMEM = ",\"unit_of_meas\":\"°%c\"," // °C / °F - "\"val_tpl\":\"{{value_json['%s'].Temperature}}\""; // "SI7021-14":{"Temperature":null,"Humidity":null} -> {{ value_json['SI7021-14'].Temperature }} + "\"val_tpl\":\"{{value_json['%s'].Temperature}}\"," // "SI7021-14":{"Temperature":null,"Humidity":null} -> {{ value_json['SI7021-14'].Temperature }} + "\"dev_cla\":\"temperature\""; // temperature + +const char HASS_DISCOVER_DS18X20_MULTI[] PROGMEM = + ",\"json_attributes_topic\":\"%s\"," + "\"json_attributes_template\":\"{{{'Id':value_json['%s'].Id}|tojson}}\""; // Add DS18X20 Id as information field const char HASS_DISCOVER_SENSOR_HUM[] PROGMEM = ",\"unit_of_meas\":\"%%\"," // % @@ -102,33 +107,41 @@ const char HASS_DISCOVER_SENSOR_PRESS[] PROGMEM = //ENERGY const char HASS_DISCOVER_SENSOR_KWH[] PROGMEM = - ",\"unit_of_meas\":\"kWh\"," // kWh - "\"val_tpl\":\"{{value_json['%s'].%s}}\""; // "ENERGY":{"TotalStartTime":null,"Total":null,"Yesterday":null,"Today":null,"Power":null,"ApparentPower":null,"ReactivePower":null,"Factor":null,"Voltage":null,"Current":null} -> {{ value_json['ENERGY'].Total/Yesterday/Today }} + ",\"unit_of_meas\":\"kWh\"," // kWh + "\"val_tpl\":\"{{value_json['%s'].%s}}\"," // "ENERGY":{"TotalStartTime":null,"Total":null,"Yesterday":null,"Today":null,"Power":null,"ApparentPower":null,"ReactivePower":null,"Factor":null,"Voltage":null,"Current":null} -> {{ value_json['ENERGY'].Total/Yesterday/Today }} + "\"dev_cla\":\"power\""; // power const char HASS_DISCOVER_SENSOR_WATT[] PROGMEM = ",\"unit_of_meas\":\"W\"," // W - "\"val_tpl\":\"{{value_json['%s'].%s}}\""; // "ENERGY":{"TotalStartTime":null,"Total":null,"Yesterday":null,"Today":null,"Power":null,"ApparentPower":null,"ReactivePower":null,"Factor":null,"Voltage":null,"Current":null} -> {{ value_json['ENERGY'].POWER }} - + "\"val_tpl\":\"{{value_json['%s'].%s}}\"," // "ENERGY":{"TotalStartTime":null,"Total":null,"Yesterday":null,"Today":null,"Power":null,"ApparentPower":null,"ReactivePower":null,"Factor":null,"Voltage":null,"Current":null} -> {{ value_json['ENERGY'].POWER }} + "\"dev_cla\":\"power\""; const char HASS_DISCOVER_SENSOR_VOLTAGE[] PROGMEM = ",\"unit_of_meas\":\"V\"," // V - "\"val_tpl\":\"{{value_json['%s'].%s}}\""; // "ENERGY":{"TotalStartTime":null,"Total":null,"Yesterday":null,"Today":null,"Power":null,"ApparentPower":null,"ReactivePower":null,"Factor":null,"Voltage":null,"Current":null} -> {{ value_json['ENERGY'].Voltage }} - + "\"val_tpl\":\"{{value_json['%s'].%s}}\"," // "ENERGY":{"TotalStartTime":null,"Total":null,"Yesterday":null,"Today":null,"Power":null,"ApparentPower":null,"ReactivePower":null,"Factor":null,"Voltage":null,"Current":null} -> {{ value_json['ENERGY'].Voltage }} + "\"dev_cla\":\"power\""; const char HASS_DISCOVER_SENSOR_AMPERE[] PROGMEM = ",\"unit_of_meas\":\"A\"," // A - "\"val_tpl\":\"{{value_json['%s'].%s}}\""; // "ENERGY":{"TotalStartTime":null,"Total":null,"Yesterday":null,"Today":null,"Power":null,"ApparentPower":null,"ReactivePower":null,"Factor":null,"Voltage":null,"Current":null} -> {{ value_json['ENERGY'].Current }} + "\"val_tpl\":\"{{value_json['%s'].%s}}\"," // "ENERGY":{"TotalStartTime":null,"Total":null,"Yesterday":null,"Today":null,"Power":null,"ApparentPower":null,"ReactivePower":null,"Factor":null,"Voltage":null,"Current":null} -> {{ value_json['ENERGY'].Current }} + "\"dev_cla\":\"power\""; + +//ILLUMINANCE +const char HASS_DISCOVER_SENSOR_ILLUMINANCE[] PROGMEM = + ",\"unit_of_meas\":\"LX\"," // LX by default + "\"val_tpl\":\"{{value_json['%s'].Illuminance}}\"," // "ANALOG":{"Illuminance":34}} + "\"dev_cla\":\"illuminance\""; // illuminance const char HASS_DISCOVER_SENSOR_ANY[] PROGMEM = - ",\"unit_of_meas\":\" \"," // " " As unit of measurement to get a value graph in Hass - "\"val_tpl\":\"{{value_json['%s'].%s}}\""; // "COUNTER":{"C1":0} -> {{ value_json['COUNTER'].C1 }} + ",\"val_tpl\":\"{{value_json['%s'].%s}}\""; // "COUNTER":{"C1":0} -> {{ value_json['COUNTER'].C1 }} const char HASS_DISCOVER_SENSOR_HASS_STATUS[] PROGMEM = ",\"json_attributes_topic\":\"%s\"," "\"unit_of_meas\":\" \"," // " " As unit of measurement to get a value graph in Hass - "\"val_tpl\":\"{{value_json['" D_JSON_RSSI "']}}\"";// "COUNTER":{"C1":0} -> {{ value_json['COUNTER'].C1 }} + "\"val_tpl\":\"{{value_json['" D_JSON_RSSI "']}}\""; // "COUNTER":{"C1":0} -> {{ value_json['COUNTER'].C1 }} const char HASS_DISCOVER_DEVICE_INFO[] PROGMEM = ",\"uniq_id\":\"%s\"," "\"device\":{\"identifiers\":[\"%06X\"]," + "\"connections\":[[\"mac\",\"%s\"]]," "\"name\":\"%s\"," "\"model\":\"%s\"," "\"sw_version\":\"%s%s\"," @@ -136,7 +149,8 @@ const char HASS_DISCOVER_DEVICE_INFO[] PROGMEM = const char HASS_DISCOVER_DEVICE_INFO_SHORT[] PROGMEM = ",\"uniq_id\":\"%s\"," - "\"device\":{\"identifiers\":[\"%06X\"]}"; + "\"device\":{\"identifiers\":[\"%06X\"]," + "\"connections\":[[\"mac\",\"%s\"]]}"; const char HASS_DISCOVER_TOPIC_PREFIX[] PROGMEM = ",\"~\":\"%s\""; @@ -237,17 +251,19 @@ void HAssAnnounceRelayLight(void) Shorten(&availability_topic, prefix); Response_P(HASS_DISCOVER_RELAY, name, command_topic, state_topic, value_template, Settings.state_text[0], Settings.state_text[1], availability_topic); - TryResponseAppend_P(HASS_DISCOVER_DEVICE_INFO_SHORT, unique_id, ESP.getChipId()); + TryResponseAppend_P(HASS_DISCOVER_DEVICE_INFO_SHORT, unique_id, ESP.getChipId(), WiFi.macAddress().c_str()); TryResponseAppend_P(HASS_DISCOVER_TOPIC_PREFIX, prefix); +#ifdef USE_LIGHT if (is_light) { char *brightness_command_topic = stemp1; GetTopic_P(brightness_command_topic, CMND, mqtt_topic, D_CMND_DIMMER); Shorten(&brightness_command_topic, prefix); - TryResponseAppend_P(HASS_DISCOVER_LIGHT_DIMMER, brightness_command_topic, state_topic); + strncpy_P(stemp3, Settings.flag.not_power_linked?PSTR("last"):PSTR("brightness"), sizeof(stemp3)); + TryResponseAppend_P(HASS_DISCOVER_LIGHT_DIMMER, brightness_command_topic, state_topic, stemp3); - if (light_subtype >= LST_RGB) { + if (Light.subtype >= LST_RGB) { char *rgb_command_topic = stemp1; GetTopic_P(rgb_command_topic, CMND, mqtt_topic, D_CMND_COLOR); @@ -260,14 +276,14 @@ void HAssAnnounceRelayLight(void) TryResponseAppend_P(HASS_DISCOVER_LIGHT_SCHEME, effect_command_topic, state_topic); } - if (LST_RGBW == light_subtype) { + if (LST_RGBW == Light.subtype) { char *white_temp_command_topic = stemp1; GetTopic_P(white_temp_command_topic, CMND, mqtt_topic, D_CMND_WHITE); Shorten(&white_temp_command_topic, prefix); TryResponseAppend_P(HASS_DISCOVER_LIGHT_WHITE, white_temp_command_topic, state_topic); } - if ((LST_COLDWARM == light_subtype) || (LST_RGBWC == light_subtype)) { + if ((LST_COLDWARM == Light.subtype) || (LST_RGBWC == Light.subtype)) { char *color_temp_command_topic = stemp1; GetTopic_P(color_temp_command_topic, CMND, mqtt_topic, D_CMND_COLORTEMPERATURE); @@ -275,6 +291,7 @@ void HAssAnnounceRelayLight(void) TryResponseAppend_P(HASS_DISCOVER_LIGHT_CT, color_temp_command_topic, state_topic); } } +#endif // USE_LIGHT TryResponseAppend_P(PSTR("}")); } MqttPublish(stopic, true); @@ -316,7 +333,7 @@ void HAssAnnounceButtonSwitch(uint8_t device, char* topic, uint8_t present, uint Shorten(&state_topic, prefix); Shorten(&availability_topic, prefix); Response_P(HASS_DISCOVER_BUTTON_SWITCH, name, state_topic, Settings.state_text[toggle?2:1], availability_topic); - TryResponseAppend_P(HASS_DISCOVER_DEVICE_INFO_SHORT, unique_id, ESP.getChipId()); + TryResponseAppend_P(HASS_DISCOVER_DEVICE_INFO_SHORT, unique_id, ESP.getChipId(), WiFi.macAddress().c_str()); if (strlen(prefix) > 0 ) TryResponseAppend_P(HASS_DISCOVER_TOPIC_PREFIX, prefix); if (toggle) TryResponseAppend_P(HASS_DISCOVER_BUTTON_SWITCH_TOGGLE); else TryResponseAppend_P(HASS_DISCOVER_BUTTON_SWITCH_ONOFF, Settings.state_text[0]); @@ -387,35 +404,48 @@ void HAssAnnounceButtons(void) } } -void HAssAnnounceSensor(const char* sensorname, const char* subsensortype) +void HAssAnnounceSensor(const char* sensorname, const char* subsensortype) { char stopic[TOPSZ]; char stemp1[TOPSZ]; char stemp2[TOPSZ]; char unique_id[30]; + bool is_sensor = true; // Announce sensor, special handling of temperature and humidity sensors mqtt_data[0] = '\0'; // Clear retained message // Clear or Set topic snprintf_P(unique_id, sizeof(unique_id), PSTR("%06X_%s_%s"), ESP.getChipId(), sensorname, subsensortype); - snprintf_P(stopic, sizeof(stopic), PSTR(HOME_ASSISTANT_DISCOVERY_PREFIX "/sensor/%s/config"), unique_id); - + if (!strncmp_P(sensorname, "MPR121", 6)) { // Add more exceptions here. Perhaps MCP230XX? + snprintf_P(stopic, sizeof(stopic), PSTR(HOME_ASSISTANT_DISCOVERY_PREFIX "/binary_sensor/%s/config"), unique_id); + is_sensor = false; + } else { + snprintf_P(stopic, sizeof(stopic), PSTR(HOME_ASSISTANT_DISCOVERY_PREFIX "/sensor/%s/config"), unique_id); + } if (Settings.flag.hass_discovery) { char name[33+42]; // friendlyname(33) + " " + sensorname(20?) + " " + sensortype(20?) char prefix[TOPSZ]; char *state_topic = stemp1; char *availability_topic = stemp2; + // sensor or binary_sensor? // + if (!is_sensor) { + snprintf_P(stopic, sizeof(stopic), PSTR(HOME_ASSISTANT_DISCOVERY_PREFIX "/binary_sensor/%s/config"), unique_id); + GetTopic_P(state_topic, STAT, mqtt_topic, PSTR(D_RSLT_RESULT)); + } else { + snprintf_P(stopic, sizeof(stopic), PSTR(HOME_ASSISTANT_DISCOVERY_PREFIX "/sensor/%s/config"), unique_id); + GetTopic_P(state_topic, TELE, mqtt_topic, PSTR(D_RSLT_SENSOR)); + } + snprintf_P(name, sizeof(name), PSTR("%s %s %s"), Settings.friendlyname[0], sensorname, subsensortype); - GetTopic_P(state_topic, TELE, mqtt_topic, PSTR(D_RSLT_SENSOR)); GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT); FindPrefix(state_topic, availability_topic, prefix); Shorten(&state_topic, prefix); Shorten(&availability_topic, prefix); Response_P(HASS_DISCOVER_SENSOR, name, state_topic, availability_topic); - TryResponseAppend_P(HASS_DISCOVER_DEVICE_INFO_SHORT, unique_id, ESP.getChipId()); + TryResponseAppend_P(HASS_DISCOVER_DEVICE_INFO_SHORT, unique_id, ESP.getChipId(), WiFi.macAddress().c_str()); TryResponseAppend_P(HASS_DISCOVER_TOPIC_PREFIX, prefix); if (!strcmp_P(subsensortype, PSTR(D_JSON_TEMPERATURE))) { TryResponseAppend_P(HASS_DISCOVER_SENSOR_TEMP, TempUnit(), sensorname); @@ -433,9 +463,13 @@ void HAssAnnounceSensor(const char* sensorname, const char* subsensortype) TryResponseAppend_P(HASS_DISCOVER_SENSOR_VOLTAGE, sensorname, subsensortype); } else if (!strcmp_P(subsensortype, PSTR(D_JSON_CURRENT))){ TryResponseAppend_P(HASS_DISCOVER_SENSOR_AMPERE, sensorname, subsensortype); - } - else { - TryResponseAppend_P(HASS_DISCOVER_SENSOR_ANY, sensorname, subsensortype); + } else if (!strcmp_P(subsensortype, PSTR(D_JSON_ILLUMINANCE))){ + TryResponseAppend_P(HASS_DISCOVER_SENSOR_ILLUMINANCE, sensorname, subsensortype); + } else { + if (is_sensor){ + TryResponseAppend_P(PSTR(",\"unit_of_meas\":\" \"")); // " " As unit of measurement to get a value graph (not available for binary sensors) + } + TryResponseAppend_P(HASS_DISCOVER_SENSOR_ANY, sensorname, subsensortype); } TryResponseAppend_P(PSTR("}")); } @@ -453,7 +487,7 @@ void HAssAnnounceSensors(void) XsnsNextCall(FUNC_JSON_APPEND, hass_xsns_index); // ,"INA219":{"Voltage":4.494,"Current":0.020,"Power":0.089} tele_period = tele_period_save; - char sensordata[256]; // Copy because we need to write to mqtt_data + char sensordata[512]; // Copy because we need to write to mqtt_data strlcpy(sensordata, mqtt_data, sizeof(sensordata)); if (strlen(sensordata)) { @@ -467,14 +501,14 @@ void HAssAnnounceSensors(void) StaticJsonBuffer<500> jsonBuffer; JsonObject& root = jsonBuffer.parseObject(sensordata); if (!root.success()) { - AddLog_P2(LOG_LEVEL_ERROR, PSTR("HASS: failed to parse '%s'"), sensordata); + AddLog_P2(LOG_LEVEL_ERROR, PSTR("HASS: jsonBuffer failed to parse '%s'"), sensordata); continue; } for (auto sensor : root) { const char* sensorname = sensor.key; JsonObject& sensors = sensor.value.as(); if (!sensors.success()) { - AddLog_P2(LOG_LEVEL_ERROR, PSTR("HASS: failed to parse '%s'"), sensordata); + AddLog_P2(LOG_LEVEL_ERROR, PSTR("HASS: JsonObject failed to parse '%s'"), sensordata); continue; } for (auto subsensor : sensors) { @@ -515,7 +549,7 @@ void HAssAnnounceStatusSensor(void) Response_P(HASS_DISCOVER_SENSOR, name, state_topic, availability_topic); TryResponseAppend_P(HASS_DISCOVER_SENSOR_HASS_STATUS, state_topic); - TryResponseAppend_P(HASS_DISCOVER_DEVICE_INFO, unique_id, ESP.getChipId(), + TryResponseAppend_P(HASS_DISCOVER_DEVICE_INFO, unique_id, ESP.getChipId(), WiFi.macAddress().c_str(), Settings.friendlyname[0], ModuleName().c_str(), my_version, my_image); TryResponseAppend_P(HASS_DISCOVER_TOPIC_PREFIX, prefix); TryResponseAppend_P(PSTR("}")); diff --git a/sonoff/xdrv_13_display.ino b/sonoff/xdrv_13_display.ino index c95fd980a..0317c4e03 100644 --- a/sonoff/xdrv_13_display.ino +++ b/sonoff/xdrv_13_display.ino @@ -20,7 +20,29 @@ #if defined(USE_I2C) || defined(USE_SPI) #ifdef USE_DISPLAY -#define XDRV_13 13 +#define XDRV_13 13 + +#include +#include + +Renderer *renderer; + +enum ColorType { COLOR_BW, COLOR_COLOR }; + +#ifndef MAXBUTTONS +#define MAXBUTTONS 16 +#endif + +#ifdef USE_TOUCH_BUTTONS +VButton *buttons[MAXBUTTONS]; +#endif + +// drawing color is WHITE +// on epaper the whole display buffer is transfered inverted this results in white paper +uint16_t fg_color = 1; +uint16_t bg_color = 0; +uint8_t color_type = COLOR_BW; +uint8_t auto_draw=1; const uint8_t DISPLAY_MAX_DRIVERS = 16; // Max number of display drivers/models supported by xdsp_interface.ino const uint8_t DISPLAY_MAX_COLS = 44; // Max number of columns allowed with command DisplayCols @@ -28,7 +50,7 @@ const uint8_t DISPLAY_MAX_ROWS = 32; // Max number of lines allowed wi const uint8_t DISPLAY_LOG_ROWS = 32; // Number of lines in display log buffer -#define D_CMND_DISPLAY "Display" +#define D_PRFX_DISPLAY "Display" #define D_CMND_DISP_ADDRESS "Address" #define D_CMND_DISP_COLS "Cols" #define D_CMND_DISP_DIMMER "Dimmer" @@ -40,6 +62,8 @@ const uint8_t DISPLAY_LOG_ROWS = 32; // Number of lines in display log #define D_CMND_DISP_FONT "Font" #define D_CMND_DISP_ROTATE "Rotate" #define D_CMND_DISP_TEXT "Text" +#define D_CMND_DISP_WIDTH "Width" +#define D_CMND_DISP_HEIGHT "Height" enum XdspFunctions { FUNC_DISPLAY_INIT_DRIVER, FUNC_DISPLAY_INIT, FUNC_DISPLAY_EVERY_50_MSECOND, FUNC_DISPLAY_EVERY_SECOND, FUNC_DISPLAY_MODEL, FUNC_DISPLAY_MODE, FUNC_DISPLAY_POWER, @@ -51,15 +75,15 @@ enum XdspFunctions { FUNC_DISPLAY_INIT_DRIVER, FUNC_DISPLAY_INIT, FUNC_DISPLAY_E enum DisplayInitModes { DISPLAY_INIT_MODE, DISPLAY_INIT_PARTIAL, DISPLAY_INIT_FULL }; -enum DisplayCommands { CMND_DISPLAY, CMND_DISP_MODEL, CMND_DISP_MODE, CMND_DISP_REFRESH, CMND_DISP_DIMMER, CMND_DISP_COLS, CMND_DISP_ROWS, - CMND_DISP_SIZE, CMND_DISP_FONT, CMND_DISP_ROTATE, CMND_DISP_TEXT, CMND_DISP_ADDRESS }; -const char kDisplayCommands[] PROGMEM = - "|" D_CMND_DISP_MODEL "|" D_CMND_DISP_MODE "|" D_CMND_DISP_REFRESH "|" D_CMND_DISP_DIMMER "|" D_CMND_DISP_COLS "|" D_CMND_DISP_ROWS "|" - D_CMND_DISP_SIZE "|" D_CMND_DISP_FONT "|" D_CMND_DISP_ROTATE "|" D_CMND_DISP_TEXT "|" D_CMND_DISP_ADDRESS ; +const char kDisplayCommands[] PROGMEM = D_PRFX_DISPLAY "|" // Prefix + "|" D_CMND_DISP_MODEL "|" D_CMND_DISP_WIDTH "|" D_CMND_DISP_HEIGHT "|" D_CMND_DISP_MODE "|" D_CMND_DISP_REFRESH "|" + D_CMND_DISP_DIMMER "|" D_CMND_DISP_COLS "|" D_CMND_DISP_ROWS "|" D_CMND_DISP_SIZE "|" D_CMND_DISP_FONT "|" + D_CMND_DISP_ROTATE "|" D_CMND_DISP_TEXT "|" D_CMND_DISP_ADDRESS ; -const char S_JSON_DISPLAY_COMMAND_VALUE[] PROGMEM = "{\"" D_CMND_DISPLAY "%s\":\"%s\"}"; -const char S_JSON_DISPLAY_COMMAND_NVALUE[] PROGMEM = "{\"" D_CMND_DISPLAY "%s\":%d}"; -const char S_JSON_DISPLAY_COMMAND_INDEX_NVALUE[] PROGMEM = "{\"" D_CMND_DISPLAY "%s%d\":%d}"; +void (* const DisplayCommand[])(void) PROGMEM = { + &CmndDisplay, &CmndDisplayModel, &CmndDisplayWidth, &CmndDisplayHeight, &CmndDisplayMode, &CmndDisplayRefresh, + &CmndDisplayDimmer, &CmndDisplayColumns, &CmndDisplayRows, &CmndDisplaySize, &CmndDisplayFont, + &CmndDisplayRotate, &CmndDisplayText, &CmndDisplayAddress }; char *dsp_str; @@ -87,6 +111,7 @@ uint8_t dsp_on; char **disp_log_buffer; char **disp_screen_buffer; char disp_temp[2]; // C or F +char disp_pres[5]; // hPa or mmHg uint8_t disp_log_buffer_cols = 0; uint8_t disp_log_buffer_idx = 0; @@ -101,8 +126,13 @@ bool disp_subscribed = false; void DisplayInit(uint8_t mode) { - dsp_init = mode; - XdspCall(FUNC_DISPLAY_INIT); + if (renderer) { + renderer->DisplayInit(mode, Settings.display_size, Settings.display_rotate, Settings.display_font); + } + else { + dsp_init = mode; + XdspCall(FUNC_DISPLAY_INIT); + } } void DisplayClear(void) @@ -217,6 +247,21 @@ void DisplayOnOff(uint8_t on) /*-------------------------------------------------------------------------------------------*/ +// get asci float number +uint8_t fatoiv(char *cp,float *res) { + uint8_t index=0; + *res=CharToFloat(cp); + while (*cp) { + if ((*cp>='0' && *cp<='9') || (*cp=='-') || (*cp=='.')) { + cp++; + index++; + } else { + break; + } + } + return index; +} + // get asci number until delimiter and return asci number lenght and value uint8_t atoiv(char *cp, int16_t *res) { @@ -249,6 +294,67 @@ uint8_t atoiV(char *cp, uint16_t *res) return index; } +// right align string +void alignright(char *string) { + uint16_t slen=strlen(string); + uint16_t len=slen; + while (len) { + // count spaces to the right + if (string[len-1]!=' ') { + break; + } + len--; + } + uint16_t diff=slen-len; + if (diff>0) { + // move string + memmove(&string[diff],string,len); + memset(string,' ',diff); + } +} + +char *get_string(char *buff,uint8_t len,char *cp) { +uint8_t index=0; + while (*cp!=':') { + buff[index]=*cp++; + index++; + if (index>=len) break; + } + buff[index]=0; + cp++; + return cp; +} + +#define ESCAPE_CHAR '~' + +// decode text escapes, 1 hexbyte assumed +void decode_te(char *line) { + char sbuf[3],*cp; + while (*line) { + if (*line==ESCAPE_CHAR) { + cp=line+1; + if (*cp!=0 && *cp==ESCAPE_CHAR) { + // escape escape, discard one + memmove(cp,cp+1,strlen(cp)); + } else { + // escape HH + if (strlen(cp)<2) { + // illegal lenght, ignore + return; + } + // take 2 hex chars + sbuf[0]=*(cp); + sbuf[1]=*(cp+1); + sbuf[2]=0; + *line=strtol(sbuf,0,16); + // must shift string 2 bytes shift zero also + memmove(cp,cp+2,strlen(cp)-1); + } + } + line++; + } +} + /*-------------------------------------------------------------------------------------------*/ #define DISPLAY_BUFFER_COLS 128 // Max number of characters in linebuf @@ -258,15 +364,12 @@ void DisplayText(void) uint8_t lpos; uint8_t escape = 0; uint8_t var; - uint8_t font_x = 6; - uint8_t font_y = 8; - uint8_t fontnumber = 1; int16_t lin = 0; int16_t col = 0; int16_t fill = 0; int16_t temp; int16_t temp1; - uint16_t color = 0; + float ftemp; char linebuf[DISPLAY_BUFFER_COLS]; char *dp = linebuf; @@ -287,10 +390,12 @@ void DisplayText(void) if (!fill) { *dp = 0; } if (col > 0 && lin > 0) { // use col and lin - DisplayDrawStringAt(col, lin, linebuf, color, 1); + if (!renderer) DisplayDrawStringAt(col, lin, linebuf, fg_color, 1); + else renderer->DrawStringAt(col, lin, linebuf, fg_color, 1); } else { // use disp_xpos, disp_ypos - DisplayDrawStringAt(disp_xpos, disp_ypos, linebuf, color, 0); + if (!renderer) DisplayDrawStringAt(disp_xpos, disp_ypos, linebuf, fg_color, 0); + else renderer->DrawStringAt(disp_xpos, disp_ypos, linebuf, fg_color, 0); } memset(linebuf, ' ', sizeof(linebuf)); linebuf[sizeof(linebuf)-1] = 0; @@ -310,7 +415,8 @@ void DisplayText(void) switch (*cp++) { case 'z': // clear display - DisplayClear(); + if (!renderer) DisplayClear(); + else renderer->fillScreen(bg_color); disp_xpos = 0; disp_ypos = 0; col = 0; @@ -325,10 +431,18 @@ void DisplayText(void) DisplayInit(DISPLAY_INIT_FULL); break; case 'o': - DisplayOnOff(0); + if (!renderer) { + DisplayOnOff(0); + } else { + renderer->DisplayOnff(0); + } break; case 'O': - DisplayOnOff(1); + if (!renderer) { + DisplayOnOff(1); + } else { + renderer->DisplayOnff(1); + } break; case 'x': // set disp_xpos @@ -354,8 +468,32 @@ void DisplayText(void) break; case 'C': // text color cxx - var = atoiV(cp, &color); + if (*cp=='i') { + // color index 0-18 + cp++; + var = atoiv(cp, &temp); + if (renderer) ftemp=renderer->GetColorFromIndex(temp); + } else { + // float because it must handle unsigned 16 bit + var = fatoiv(cp,&ftemp); + } + fg_color=ftemp; cp += var; + if (renderer) renderer->setTextColor(fg_color,bg_color); + break; + case 'B': + // bg color Bxx + if (*cp=='i') { + // color index 0-18 + cp++; + var = atoiv(cp, &temp); + if (renderer) ftemp=renderer->GetColorFromIndex(temp); + } else { + var = fatoiv(cp,&ftemp); + } + bg_color=ftemp; + cp += var; + if (renderer) renderer->setTextColor(fg_color,bg_color); break; case 'p': // pad field with spaces fxx @@ -363,14 +501,28 @@ void DisplayText(void) cp += var; linebuf[fill] = 0; break; +#if defined(USE_SCRIPT_FATFS) && defined(USE_SCRIPT) + case 'P': + { char *ep=strchr(cp,':'); + if (ep) { + *ep=0; + ep++; + Draw_RGB_Bitmap(cp,disp_xpos,disp_ypos); + cp=ep; + } + } + break; +#endif case 'h': // hor line to var = atoiv(cp, &temp); cp += var; if (temp < 0) { - DisplayDrawHLine(disp_xpos + temp, disp_ypos, -temp, color); + if (renderer) renderer->writeFastHLine(disp_xpos + temp, disp_ypos, -temp, fg_color); + else DisplayDrawHLine(disp_xpos + temp, disp_ypos, -temp, fg_color); } else { - DisplayDrawHLine(disp_xpos, disp_ypos, temp, color); + if (renderer) renderer->writeFastHLine(disp_xpos, disp_ypos, temp, fg_color); + else DisplayDrawHLine(disp_xpos, disp_ypos, temp, fg_color); } disp_xpos += temp; break; @@ -379,9 +531,11 @@ void DisplayText(void) var = atoiv(cp, &temp); cp += var; if (temp < 0) { - DisplayDrawVLine(disp_xpos, disp_ypos + temp, -temp, color); + if (renderer) renderer->writeFastVLine(disp_xpos, disp_ypos + temp, -temp, fg_color); + else DisplayDrawVLine(disp_xpos, disp_ypos + temp, -temp, fg_color); } else { - DisplayDrawVLine(disp_xpos, disp_ypos, temp, color); + if (renderer) renderer->writeFastVLine(disp_xpos, disp_ypos, temp, fg_color); + else DisplayDrawVLine(disp_xpos, disp_ypos, temp, fg_color); } disp_ypos += temp; break; @@ -392,7 +546,8 @@ void DisplayText(void) cp++; var = atoiv(cp, &temp1); cp += var; - DisplayDrawLine(disp_xpos, disp_ypos, temp, temp1, color); + if (renderer) renderer->writeLine(disp_xpos, disp_ypos, temp, temp1, fg_color); + else DisplayDrawLine(disp_xpos, disp_ypos, temp, temp1, fg_color); disp_xpos += temp; disp_ypos += temp1; break; @@ -400,13 +555,15 @@ void DisplayText(void) // circle var = atoiv(cp, &temp); cp += var; - DisplayDrawCircle(disp_xpos, disp_ypos, temp, color); + if (renderer) renderer->drawCircle(disp_xpos, disp_ypos, temp, fg_color); + else DisplayDrawCircle(disp_xpos, disp_ypos, temp, fg_color); break; case 'K': // filled circle var = atoiv(cp, &temp); cp += var; - DisplayDrawFilledCircle(disp_xpos, disp_ypos, temp, color); + if (renderer) renderer->fillCircle(disp_xpos, disp_ypos, temp, fg_color); + else DisplayDrawFilledCircle(disp_xpos, disp_ypos, temp, fg_color); break; case 'r': // rectangle @@ -415,7 +572,8 @@ void DisplayText(void) cp++; var = atoiv(cp, &temp1); cp += var; - DisplayDrawRectangle(disp_xpos, disp_ypos, temp, temp1, color); + if (renderer) renderer->drawRect(disp_xpos, disp_ypos, temp, temp1, fg_color); + else DisplayDrawRectangle(disp_xpos, disp_ypos, temp, temp1, fg_color); break; case 'R': // filled rectangle @@ -424,12 +582,52 @@ void DisplayText(void) cp++; var = atoiv(cp, &temp1); cp += var; - DisplayDrawFilledRectangle(disp_xpos, disp_ypos, temp, temp1, color); + if (renderer) renderer->fillRect(disp_xpos, disp_ypos, temp, temp1, fg_color); + else DisplayDrawFilledRectangle(disp_xpos, disp_ypos, temp, temp1, fg_color); break; + case 'u': + // rounded rectangle + { int16_t rad; + var = atoiv(cp, &temp); + cp += var; + cp++; + var = atoiv(cp, &temp1); + cp += var; + cp++; + var = atoiv(cp, &rad); + cp += var; + if (renderer) renderer->drawRoundRect(disp_xpos, disp_ypos, temp, temp1, rad, fg_color); + //else DisplayDrawFilledRectangle(disp_xpos, disp_ypos, temp, temp1, fg_color); + } + break; + case 'U': + // rounded rectangle + { int16_t rad; + var = atoiv(cp, &temp); + cp += var; + cp++; + var = atoiv(cp, &temp1); + cp += var; + cp++; + var = atoiv(cp, &rad); + cp += var; + if (renderer) renderer->fillRoundRect(disp_xpos, disp_ypos, temp, temp1, rad, fg_color); + //else DisplayDrawFilledRectangle(disp_xpos, disp_ypos, temp, temp1, fg_color); + } + break; + case 't': - if (dp < (linebuf + DISPLAY_BUFFER_COLS) -5) { - snprintf_P(dp, 6, PSTR("%02d" D_HOUR_MINUTE_SEPARATOR "%02d"), RtcTime.hour, RtcTime.minute); - dp += 5; + if (*cp=='S') { + cp++; + if (dp < (linebuf + DISPLAY_BUFFER_COLS) -8) { + snprintf_P(dp, 9, PSTR("%02d" D_HOUR_MINUTE_SEPARATOR "%02d" D_MINUTE_SECOND_SEPARATOR "%02d"), RtcTime.hour, RtcTime.minute, RtcTime.second); + dp += 8; + } + } else { + if (dp < (linebuf + DISPLAY_BUFFER_COLS) -5) { + snprintf_P(dp, 6, PSTR("%02d" D_HOUR_MINUTE_SEPARATOR "%02d"), RtcTime.hour, RtcTime.minute); + dp += 5; + } } break; case 'T': @@ -440,28 +638,186 @@ void DisplayText(void) break; case 'd': // force draw grafics buffer - DisplayDrawFrame(); + if (renderer) renderer->Updateframe(); + else DisplayDrawFrame(); break; case 'D': // set auto draw mode - disp_autodraw = *cp&1; + auto_draw=*cp&3; + if (renderer) renderer->setDrawMode(auto_draw>>1); cp += 1; break; case 's': // size sx - DisplaySetSize(*cp&3); + if (renderer) renderer->setTextSize(*cp&7); + else DisplaySetSize(*cp&3); cp += 1; break; case 'f': // font sx - DisplaySetFont(*cp&3); + if (renderer) renderer->setTextFont(*cp&7); + else DisplaySetFont(*cp&7); cp += 1; break; case 'a': // rotation angle - DisplaySetRotation(*cp&3); + if (renderer) renderer->setRotation(*cp&3); + else DisplaySetRotation(*cp&3); cp+=1; break; + +#ifdef USE_GRAPH + case 'G': + // define graph + if (*cp=='d') { + cp++; + var=atoiv(cp,&temp); + cp+=var; + cp++; + var=atoiv(cp,&temp1); + cp+=var; + RedrawGraph(temp,temp1); + break; + } +#if defined(USE_SCRIPT_FATFS) && defined(USE_SCRIPT) + if (*cp=='s') { + cp++; + var=atoiv(cp,&temp); + cp+=var; + cp++; + // path + char bbuff[128]; + cp=get_string(bbuff,sizeof(bbuff),cp); + Save_graph(temp,bbuff); + break; + } + if (*cp=='r') { + cp++; + var=atoiv(cp,&temp); + cp+=var; + cp++; + // path + char bbuff[128]; + cp=get_string(bbuff,sizeof(bbuff),cp); + Restore_graph(temp,bbuff); + break; + } +#endif + { int16_t num,gxp,gyp,gxs,gys,dec,icol; + float ymin,ymax; + var=atoiv(cp,&num); + cp+=var; + cp++; + var=atoiv(cp,&gxp); + cp+=var; + cp++; + var=atoiv(cp,&gyp); + cp+=var; + cp++; + var=atoiv(cp,&gxs); + cp+=var; + cp++; + var=atoiv(cp,&gys); + cp+=var; + cp++; + var=atoiv(cp,&dec); + cp+=var; + cp++; + var=fatoiv(cp,&ymin); + cp+=var; + cp++; + var=fatoiv(cp,&ymax); + cp+=var; + if (color_type==COLOR_COLOR) { + // color graph requires channel color + cp++; + var=atoiv(cp,&icol); + cp+=var; + } else { + icol=0; + } + DefineGraph(num,gxp,gyp,gxs,gys,dec,ymin,ymax,icol); + } + break; + case 'g': + { float temp; + int16_t num; + var=atoiv(cp,&num); + cp+=var; + cp++; + var=fatoiv(cp,&temp); + cp+=var; + AddValue(num,temp); + } + break; +#endif + +#ifdef USE_AWATCH + case 'w': + var = atoiv(cp, &temp); + cp += var; + DrawAClock(temp); + break; +#endif + +#ifdef USE_TOUCH_BUTTONS + case 'b': + { int16_t num,gxp,gyp,gxs,gys,outline,fill,textcolor,textsize; + var=atoiv(cp,&num); + cp+=var; + cp++; + uint8_t bflags=num>>8; + num=num%MAXBUTTONS; + var=atoiv(cp,&gxp); + cp+=var; + cp++; + var=atoiv(cp,&gyp); + cp+=var; + cp++; + var=atoiv(cp,&gxs); + cp+=var; + cp++; + var=atoiv(cp,&gys); + cp+=var; + cp++; + var=atoiv(cp,&outline); + cp+=var; + cp++; + var=atoiv(cp,&fill); + cp+=var; + cp++; + var=atoiv(cp,&textcolor); + cp+=var; + cp++; + var=atoiv(cp,&textsize); + cp+=var; + cp++; + // text itself + char bbuff[32]; + cp=get_string(bbuff,sizeof(bbuff),cp); + + if (buttons[num]) { + delete buttons[num]; + } + if (renderer) { + buttons[num]= new VButton(); + if (buttons[num]) { + buttons[num]->vpower=bflags; + buttons[num]->initButtonUL(renderer,gxp,gyp,gxs,gys,renderer->GetColorFromIndex(outline),\ + renderer->GetColorFromIndex(fill),renderer->GetColorFromIndex(textcolor),bbuff,textsize); + if (!bflags) { + // power button + buttons[num]->xdrawButton(bitRead(power,num)); + } else { + // virtual button + buttons[num]->vpower&=0x7f; + buttons[num]->xdrawButton(buttons[num]->vpower&0x80); + } + } + } + } + break; +#endif default: // unknown escape Response_P(PSTR("Unknown Escape")); @@ -473,18 +829,29 @@ void DisplayText(void) } exit: // now draw buffer - if ((uint32_t)dp - (uint32_t)linebuf) { - if (!fill) { *dp = 0; } - if (col > 0 && lin > 0) { - // use col and lin - DisplayDrawStringAt(col, lin, linebuf, color, 1); - } else { - // use disp_xpos, disp_ypos - DisplayDrawStringAt(disp_xpos, disp_ypos, linebuf, color, 0); + decode_te(linebuf); + if ((uint32_t)dp - (uint32_t)linebuf) { + if (!fill) *dp = 0; + else linebuf[abs(fill)] = 0; + if (fill<0) { + // right align + alignright(linebuf); + } + if (col > 0 && lin > 0) { + // use col and lin + if (!renderer) DisplayDrawStringAt(col, lin, linebuf, fg_color, 1); + else renderer->DrawStringAt(col, lin, linebuf, fg_color, 1); + } else { + // use disp_xpos, disp_ypos + if (!renderer) DisplayDrawStringAt(disp_xpos, disp_ypos, linebuf, fg_color, 0); + else renderer->DrawStringAt(disp_xpos, disp_ypos, linebuf, fg_color, 0); + } + } + // draw buffer + if (auto_draw&1) { + if (renderer) renderer->Updateframe(); + else DisplayDrawFrame(); } - } - // draw buffer - if (disp_autodraw) { DisplayDrawFrame(); } } /*********************************************************************************************/ @@ -539,9 +906,9 @@ void DisplayReAllocScreenBuffer(void) DisplayAllocScreenBuffer(); } -void DisplayFillScreen(uint8_t line) +void DisplayFillScreen(uint32_t line) { - uint8_t len = disp_screen_buffer_cols - strlen(disp_screen_buffer[line]); + uint32_t len = disp_screen_buffer_cols - strlen(disp_screen_buffer[line]); if (len) { memset(disp_screen_buffer[line] + strlen(disp_screen_buffer[line]), 0x20, len); disp_screen_buffer[line][disp_screen_buffer_cols -1] = 0; @@ -629,6 +996,7 @@ void DisplayLogBufferInit(void) disp_refresh = Settings.display_refresh; snprintf_P(disp_temp, sizeof(disp_temp), PSTR("%c"), TempUnit()); + snprintf_P(disp_pres, sizeof(disp_pres), PressureUnit().c_str()); DisplayReAllocLogBuffer(); @@ -713,7 +1081,7 @@ void DisplayJsonValue(const char* topic, const char* device, const char* mkey, c snprintf_P(svalue, sizeof(svalue), PSTR("%s%%"), value); } else if ((quantity_code >= JSON_PRESSURE) && (quantity_code <= JSON_PRESSUREATSEALEVEL)) { - snprintf_P(svalue, sizeof(svalue), PSTR("%s" D_UNIT_PRESSURE), value); + snprintf_P(svalue, sizeof(svalue), PSTR("%s%s"), value, disp_pres); } else if (JSON_ILLUMINANCE == quantity_code) { snprintf_P(svalue, sizeof(svalue), PSTR("%s" D_UNIT_LUX), value); @@ -764,7 +1132,6 @@ void DisplayAnalyzeJson(char *topic, char *json) // tele/wemos5/SENSOR {"Time":"2017-09-20T11:53:53","SHT1X":{"Temperature":20.1,"Humidity":58.9},"HTU21":{"Temperature":20.7,"Humidity":58.5},"BMP280":{"Temperature":21.6,"Pressure":1020.3},"TempUnit":"C"} // tele/th1/SENSOR {"Time":"2017-09-20T11:54:48","DS18B20":{"Temperature":49.7},"TempUnit":"C"} - const char *tempunit; // char jsonStr[MESSZ]; // strlcpy(jsonStr, json, sizeof(jsonStr)); // Save original before destruction by JsonObject @@ -774,10 +1141,14 @@ void DisplayAnalyzeJson(char *topic, char *json) JsonObject &root = jsonBuf.parseObject(jsonStr); if (root.success()) { - tempunit = root[D_JSON_TEMPERATURE_UNIT]; - if (tempunit) { - snprintf_P(disp_temp, sizeof(disp_temp), PSTR("%s"), tempunit); -// AddLog_P2(LOG_LEVEL_DEBUG, disp_temp); + const char *unit; + unit = root[D_JSON_TEMPERATURE_UNIT]; + if (unit) { + snprintf_P(disp_temp, sizeof(disp_temp), PSTR("%s"), unit); // C or F + } + unit = root[D_JSON_PRESSURE_UNIT]; + if (unit) { + snprintf_P(disp_pres, sizeof(disp_pres), PSTR("%s"), unit); // hPa or mmHg } for (JsonObject::iterator it = root.begin(); it != root.end(); ++it) { @@ -881,6 +1252,12 @@ void DisplayInitDriver(void) { XdspCall(FUNC_DISPLAY_INIT_DRIVER); + if (renderer) { + renderer->setTextFont(Settings.display_font); + renderer->setTextSize(Settings.display_size); + } + + // AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "Display model %d"), Settings.display_model); if (Settings.display_model) { @@ -898,8 +1275,15 @@ void DisplayInitDriver(void) void DisplaySetPower(void) { disp_power = bitRead(XdrvMailbox.index, disp_device -1); + +AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DSP: Power %d"), disp_power); + if (Settings.display_model) { - XdspCall(FUNC_DISPLAY_POWER); + if (!renderer) { + XdspCall(FUNC_DISPLAY_POWER); + } else { + renderer->DisplayOnff(disp_power); + } } } @@ -907,36 +1291,54 @@ void DisplaySetPower(void) * Commands \*********************************************************************************************/ -bool DisplayCommand(void) +void CmndDisplay(void) { - char command [CMDSZ]; - bool serviced = true; - uint8_t disp_len = strlen(D_CMND_DISPLAY); // Prep for string length change + Response_P(PSTR("{\"" D_PRFX_DISPLAY "\":{\"" D_CMND_DISP_MODEL "\":%d,\"" D_CMND_DISP_WIDTH "\":%d,\"" D_CMND_DISP_HEIGHT "\":%d,\"" + D_CMND_DISP_MODE "\":%d,\"" D_CMND_DISP_DIMMER "\":%d,\"" D_CMND_DISP_SIZE "\":%d,\"" D_CMND_DISP_FONT "\":%d,\"" + D_CMND_DISP_ROTATE "\":%d,\"" D_CMND_DISP_REFRESH "\":%d,\"" D_CMND_DISP_COLS "\":[%d,%d],\"" D_CMND_DISP_ROWS "\":%d}}"), + Settings.display_model, Settings.display_width, Settings.display_height, + Settings.display_mode, Settings.display_dimmer, Settings.display_size, Settings.display_font, + Settings.display_rotate, Settings.display_refresh, Settings.display_cols[0], Settings.display_cols[1], Settings.display_rows); +} - if (!strncasecmp_P(XdrvMailbox.topic, PSTR(D_CMND_DISPLAY), disp_len)) { // Prefix - int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic +disp_len, kDisplayCommands); - if (-1 == command_code) { - serviced = false; // Unknown command +void CmndDisplayModel(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < DISPLAY_MAX_DRIVERS)) { + uint32_t last_display_model = Settings.display_model; + Settings.display_model = XdrvMailbox.payload; + if (XdspCall(FUNC_DISPLAY_MODEL)) { + restart_flag = 2; // Restart to re-init interface and add/Remove MQTT subscribe + } else { + Settings.display_model = last_display_model; } - else if (CMND_DISPLAY == command_code) { - Response_P(PSTR("{\"" D_CMND_DISPLAY "\":{\"" D_CMND_DISP_MODEL "\":%d,\"" D_CMND_DISP_MODE "\":%d,\"" D_CMND_DISP_DIMMER "\":%d,\"" - D_CMND_DISP_SIZE "\":%d,\"" D_CMND_DISP_FONT "\":%d,\"" D_CMND_DISP_ROTATE "\":%d,\"" D_CMND_DISP_REFRESH "\":%d,\"" D_CMND_DISP_COLS "\":[%d,%d],\"" D_CMND_DISP_ROWS "\":%d}}"), - Settings.display_model, Settings.display_mode, Settings.display_dimmer, Settings.display_size, Settings.display_font, Settings.display_rotate, Settings.display_refresh, - Settings.display_cols[0], Settings.display_cols[1], Settings.display_rows); + } + ResponseCmndNumber(Settings.display_model); +} + +void CmndDisplayWidth(void) +{ + if (XdrvMailbox.payload > 0) { + if (XdrvMailbox.payload != Settings.display_width) { + Settings.display_width = XdrvMailbox.payload; + restart_flag = 2; // Restart to re-init width } - else if (CMND_DISP_MODEL == command_code) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < DISPLAY_MAX_DRIVERS)) { - uint8_t last_display_model = Settings.display_model; - Settings.display_model = XdrvMailbox.payload; - if (XdspCall(FUNC_DISPLAY_MODEL)) { - restart_flag = 2; // Restart to re-init interface and add/Remove MQTT subscribe - } else { - Settings.display_model = last_display_model; - } - } - Response_P(S_JSON_DISPLAY_COMMAND_NVALUE, command, Settings.display_model); + } + ResponseCmndNumber(Settings.display_width); +} + +void CmndDisplayHeight(void) +{ + if (XdrvMailbox.payload > 0) { + if (XdrvMailbox.payload != Settings.display_height) { + Settings.display_height = XdrvMailbox.payload; + restart_flag = 2; // Restart to re-init height } - else if (CMND_DISP_MODE == command_code) { + } + ResponseCmndNumber(Settings.display_height); +} + +void CmndDisplayMode(void) +{ #ifdef USE_DISPLAY_MODES1TO5 /* Matrix LCD / Oled TFT * 1 = Text up and time Time @@ -945,132 +1347,614 @@ bool DisplayCommand(void) * 4 = Mqtt left and time Mqtt (incl local) sensors Mqtt (incl local) sensors * 5 = Mqtt up and time Mqtt (incl local) sensors and time Mqtt (incl local) sensors and time */ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 5)) { - uint32_t last_display_mode = Settings.display_mode; - Settings.display_mode = XdrvMailbox.payload; + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 5)) { + uint32_t last_display_mode = Settings.display_mode; + Settings.display_mode = XdrvMailbox.payload; - if (disp_subscribed != (Settings.display_mode &0x04)) { - restart_flag = 2; // Restart to Add/Remove MQTT subscribe - } else { - if (last_display_mode && !Settings.display_mode) { // Switch to mode 0 - DisplayInit(DISPLAY_INIT_MODE); - DisplayClear(); - } else { - DisplayLogBufferInit(); - DisplayInit(DISPLAY_INIT_MODE); - } - } - } -#endif // USE_DISPLAY_MODES1TO5 - Response_P(S_JSON_DISPLAY_COMMAND_NVALUE, command, Settings.display_mode); - } - else if (CMND_DISP_DIMMER == command_code) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) { - Settings.display_dimmer = ((XdrvMailbox.payload +1) * 100) / 666; // Correction for Domoticz (0 - 15) - if (Settings.display_dimmer && !(disp_power)) { - ExecuteCommandPower(disp_device, POWER_ON, SRC_DISPLAY); - } - else if (!Settings.display_dimmer && disp_power) { - ExecuteCommandPower(disp_device, POWER_OFF, SRC_DISPLAY); - } - } - Response_P(S_JSON_DISPLAY_COMMAND_NVALUE, command, Settings.display_dimmer); - } - else if (CMND_DISP_SIZE == command_code) { - if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= 4)) { - Settings.display_size = XdrvMailbox.payload; - } - Response_P(S_JSON_DISPLAY_COMMAND_NVALUE, command, Settings.display_size); - } - else if (CMND_DISP_FONT == command_code) { - if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= 4)) { - Settings.display_font = XdrvMailbox.payload; - } - Response_P(S_JSON_DISPLAY_COMMAND_NVALUE, command, Settings.display_font); - } - else if (CMND_DISP_ROTATE == command_code) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 4)) { - if (Settings.display_rotate != XdrvMailbox.payload) { -/* - // Needs font info regarding height and width - if ((Settings.display_rotate &1) != (XdrvMailbox.payload &1)) { - uint8_t temp_rows = Settings.display_rows; - Settings.display_rows = Settings.display_cols[0]; - Settings.display_cols[0] = temp_rows; -#ifdef USE_DISPLAY_MODES1TO5 - DisplayReAllocScreenBuffer(); -#endif // USE_DISPLAY_MODES1TO5 - } -*/ - Settings.display_rotate = XdrvMailbox.payload; - DisplayInit(DISPLAY_INIT_MODE); -#ifdef USE_DISPLAY_MODES1TO5 - DisplayLogBufferInit(); -#endif // USE_DISPLAY_MODES1TO5 - } - } - Response_P(S_JSON_DISPLAY_COMMAND_NVALUE, command, Settings.display_rotate); - } - else if (CMND_DISP_TEXT == command_code) { - mqtt_data[0] = '\0'; - if (disp_device && XdrvMailbox.data_len > 0) { -#ifndef USE_DISPLAY_MODES1TO5 - DisplayText(); -#else - if (!Settings.display_mode) { - DisplayText(); - } else { - DisplayLogBufferAdd(XdrvMailbox.data); - } -#endif // USE_DISPLAY_MODES1TO5 + if (disp_subscribed != (Settings.display_mode &0x04)) { + restart_flag = 2; // Restart to Add/Remove MQTT subscribe + } else { + if (last_display_mode && !Settings.display_mode) { // Switch to mode 0 + DisplayInit(DISPLAY_INIT_MODE); + if (renderer) renderer->fillScreen(bg_color); + else DisplayClear(); } else { - Response_P(PSTR("No Text")); - } - if (mqtt_data[0] == '\0') { - Response_P(S_JSON_DISPLAY_COMMAND_VALUE, command, XdrvMailbox.data); - } - } - else if ((CMND_DISP_ADDRESS == command_code) && (XdrvMailbox.index > 0) && (XdrvMailbox.index <= 8)) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 255)) { - Settings.display_address[XdrvMailbox.index -1] = XdrvMailbox.payload; - } - Response_P(S_JSON_DISPLAY_COMMAND_INDEX_NVALUE, command, XdrvMailbox.index, Settings.display_address[XdrvMailbox.index -1]); - } - else if (CMND_DISP_REFRESH == command_code) { - if ((XdrvMailbox.payload >= 1) && (XdrvMailbox.payload <= 7)) { - Settings.display_refresh = XdrvMailbox.payload; - } - Response_P(S_JSON_DISPLAY_COMMAND_NVALUE, command, Settings.display_refresh); - } - else if ((CMND_DISP_COLS == command_code) && (XdrvMailbox.index > 0) && (XdrvMailbox.index <= 2)) { - if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= DISPLAY_MAX_COLS)) { - Settings.display_cols[XdrvMailbox.index -1] = XdrvMailbox.payload; -#ifdef USE_DISPLAY_MODES1TO5 - if (1 == XdrvMailbox.index) { - DisplayLogBufferInit(); - DisplayReAllocScreenBuffer(); - } -#endif // USE_DISPLAY_MODES1TO5 - } - Response_P(S_JSON_DISPLAY_COMMAND_INDEX_NVALUE, command, XdrvMailbox.index, Settings.display_cols[XdrvMailbox.index -1]); - } - else if (CMND_DISP_ROWS == command_code) { - if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= DISPLAY_MAX_ROWS)) { - Settings.display_rows = XdrvMailbox.payload; -#ifdef USE_DISPLAY_MODES1TO5 DisplayLogBufferInit(); + DisplayInit(DISPLAY_INIT_MODE); + } + } + } +#endif // USE_DISPLAY_MODES1TO5 + ResponseCmndNumber(Settings.display_mode); +} + +void CmndDisplayDimmer(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) { + Settings.display_dimmer = ((XdrvMailbox.payload +1) * 100) / 666; // Correction for Domoticz (0 - 15) + if (Settings.display_dimmer && !(disp_power)) { + ExecuteCommandPower(disp_device, POWER_ON, SRC_DISPLAY); + } + else if (!Settings.display_dimmer && disp_power) { + ExecuteCommandPower(disp_device, POWER_OFF, SRC_DISPLAY); + } + if (renderer) renderer->dim(Settings.display_dimmer); + } + ResponseCmndNumber(Settings.display_dimmer); +} + +void CmndDisplaySize(void) +{ + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= 4)) { + Settings.display_size = XdrvMailbox.payload; + if (renderer) renderer->setTextSize(Settings.display_size); + else DisplaySetSize(Settings.display_size); + } + ResponseCmndNumber(Settings.display_size); +} + +void CmndDisplayFont(void) +{ + if ((XdrvMailbox.payload >=0) && (XdrvMailbox.payload <= 4)) { + Settings.display_font = XdrvMailbox.payload; + if (renderer) renderer->setTextFont(Settings.display_font); + else DisplaySetFont(Settings.display_font); + } + ResponseCmndNumber(Settings.display_font); +} + +void CmndDisplayRotate(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 4)) { + if (Settings.display_rotate != XdrvMailbox.payload) { +/* + // Needs font info regarding height and width + if ((Settings.display_rotate &1) != (XdrvMailbox.payload &1)) { + uint8_t temp_rows = Settings.display_rows; + Settings.display_rows = Settings.display_cols[0]; + Settings.display_cols[0] = temp_rows; +#ifdef USE_DISPLAY_MODES1TO5 DisplayReAllocScreenBuffer(); #endif // USE_DISPLAY_MODES1TO5 } - Response_P(S_JSON_DISPLAY_COMMAND_NVALUE, command, Settings.display_rows); +*/ + Settings.display_rotate = XdrvMailbox.payload; + DisplayInit(DISPLAY_INIT_MODE); +#ifdef USE_DISPLAY_MODES1TO5 + DisplayLogBufferInit(); +#endif // USE_DISPLAY_MODES1TO5 } - else serviced = false; // Unknown command } - else serviced = false; // Unknown command - - return serviced; + ResponseCmndNumber(Settings.display_rotate); } +void CmndDisplayText(void) +{ + if (disp_device && XdrvMailbox.data_len > 0) { +#ifndef USE_DISPLAY_MODES1TO5 + DisplayText(); +#else + if (!Settings.display_mode) { + DisplayText(); + } else { + DisplayLogBufferAdd(XdrvMailbox.data); + } +#endif // USE_DISPLAY_MODES1TO5 + ResponseCmndChar(XdrvMailbox.data); + } +} + +void CmndDisplayAddress(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 8)) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 255)) { + Settings.display_address[XdrvMailbox.index -1] = XdrvMailbox.payload; + } + ResponseCmndIdxNumber(Settings.display_address[XdrvMailbox.index -1]); + } +} + +void CmndDisplayRefresh(void) +{ + if ((XdrvMailbox.payload >= 1) && (XdrvMailbox.payload <= 7)) { + Settings.display_refresh = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.display_refresh); +} + +void CmndDisplayColumns(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 2)) { + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= DISPLAY_MAX_COLS)) { + Settings.display_cols[XdrvMailbox.index -1] = XdrvMailbox.payload; +#ifdef USE_DISPLAY_MODES1TO5 + if (1 == XdrvMailbox.index) { + DisplayLogBufferInit(); + DisplayReAllocScreenBuffer(); + } +#endif // USE_DISPLAY_MODES1TO5 + } + ResponseCmndIdxNumber(Settings.display_cols[XdrvMailbox.index -1]); + } +} + +void CmndDisplayRows(void) +{ + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= DISPLAY_MAX_ROWS)) { + Settings.display_rows = XdrvMailbox.payload; +#ifdef USE_DISPLAY_MODES1TO5 + DisplayLogBufferInit(); + DisplayReAllocScreenBuffer(); +#endif // USE_DISPLAY_MODES1TO5 + } + ResponseCmndNumber(Settings.display_rows); +} + +/*********************************************************************************************\ + * optional drivers +\*********************************************************************************************/ + +#if defined(USE_SCRIPT_FATFS) && defined(USE_SCRIPT) +void Draw_RGB_Bitmap(char *file,uint16_t xp, uint16_t yp) { + if (!renderer) return; + + //if (!strstr(file,".RGB")) return; + File fp; + fp=SD.open(file,FILE_READ); + if (!fp) return; + uint16_t xsize; + fp.read((uint8_t*)&xsize,2); + uint16_t ysize; + fp.read((uint8_t*)&ysize,2); + +#if 1 +#define XBUFF 128 + uint16_t xdiv=xsize/XBUFF; + renderer->setAddrWindow(xp,yp,xp+xsize,yp+ysize); + for(int16_t j=0; j=2) renderer->pushColors(rgb,len/2,true); + } + OsWatchLoop(); + } + renderer->setAddrWindow(0,0,0,0); +#else + for(int16_t j=0; jwritePixel(xp+i,yp,rgb); + } + delay(0); + OsWatchLoop(); + yp++; + } +#endif + fp.close(); +} +#endif + +#ifdef USE_AWATCH +#define MINUTE_REDUCT 4 + +#ifndef pi +#define pi 3.14159265359 +#endif + +// draw analog watch, just for fun +void DrawAClock(uint16_t rad) { + if (!renderer) return; + float frad=rad; + uint16_t hred=frad/3.0; + renderer->fillCircle(disp_xpos, disp_ypos, rad, bg_color); + renderer->drawCircle(disp_xpos, disp_ypos, rad, fg_color); + renderer->fillCircle(disp_xpos, disp_ypos, 4, fg_color); + for (uint8_t count=0; count<60; count+=5) { + float p1=((float)count*(pi/30)-(pi/2)); + uint8_t len; + if ((count%15)==0) { + len=4; + } else { + len=2; + } + renderer->writeLine(disp_xpos+((float)(rad-len)*cosf(p1)), disp_ypos+((float)(rad-len)*sinf(p1)), disp_xpos+(frad*cosf(p1)), disp_ypos+(frad*sinf(p1)), fg_color); + } + + // hour + float hour=((float)RtcTime.hour*60.0+(float)RtcTime.minute)/60.0; + float temp=(hour*(pi/6.0)-(pi/2.0)); + renderer->writeLine(disp_xpos, disp_ypos,disp_xpos+(frad-hred)*cosf(temp),disp_ypos+(frad-hred)*sinf(temp), fg_color); + + // minute + temp=((float)RtcTime.minute*(pi/30.0)-(pi/2.0)); + renderer->writeLine(disp_xpos, disp_ypos,disp_xpos+(frad-MINUTE_REDUCT)*cosf(temp),disp_ypos+(frad-MINUTE_REDUCT)*sinf(temp), fg_color); +} +#endif + + +#ifdef USE_GRAPH + +typedef union { + uint8_t data; + struct { + uint8_t overlay : 1; + uint8_t draw : 1; + uint8_t nu3 : 1; + uint8_t nu4 : 1; + uint8_t nu5 : 1; + uint8_t nu6 : 1; + uint8_t nu7 : 1; + uint8_t nu8 : 1; + }; +} GFLAGS; + +struct GRAPH { + uint16_t xp; + uint16_t yp; + uint16_t xs; + uint16_t ys; + float ymin; + float ymax; + float range; + uint32_t x_time; // time per x slice in milliseconds + uint32_t last_ms; + uint32_t last_ms_redrawn; + int16_t decimation; // decimation or graph duration in minutes + uint16_t dcnt; + uint32_t summ; + uint16_t xcnt; + uint8_t *values; + uint8_t xticks; + uint8_t yticks; + uint8_t last_val; + uint8_t color_index; + GFLAGS flags; +}; + + +struct GRAPH *graph[NUM_GRAPHS]; + +#define TICKLEN 4 +void ClrGraph(uint16_t num) { + struct GRAPH *gp=graph[num]; + + uint16_t xticks=gp->xticks; + uint16_t yticks=gp->yticks; + uint16_t count; + + // clr inside, but only 1.graph if overlapped + if (gp->flags.overlay) return; + + renderer->fillRect(gp->xp+1,gp->yp+1,gp->xs-2,gp->ys-2,bg_color); + + if (xticks) { + float cxp=gp->xp,xd=(float)gp->xs/(float)xticks; + for (count=0; countwriteFastVLine(cxp,gp->yp+gp->ys-TICKLEN,TICKLEN,fg_color); + cxp+=xd; + } + } + if (yticks) { + if (gp->ymin<0 && gp->ymax>0) { + // draw zero seperator + float cxp=0; + float czp=gp->yp+(gp->ymax/gp->range); + while (cxpxs) { + renderer->writeFastHLine(gp->xp+cxp,czp,2,fg_color); + cxp+=6.0; + } + // align ticks to zero line + float cyp=0,yd=gp->ys/yticks; + for (count=0; countgp->yp) { + renderer->writeFastHLine(gp->xp,czp-cyp,TICKLEN,fg_color); + renderer->writeFastHLine(gp->xp+gp->xs-TICKLEN,czp-cyp,TICKLEN,fg_color); + } + if ((czp+cyp)<(gp->yp+gp->ys)) { + renderer->writeFastHLine(gp->xp,czp+cyp,TICKLEN,fg_color); + renderer->writeFastHLine(gp->xp+gp->xs-TICKLEN,czp+cyp,TICKLEN,fg_color); + } + cyp+=yd; + } + } else { + float cyp=gp->yp,yd=gp->ys/yticks; + for (count=0; countwriteFastHLine(gp->xp,cyp,TICKLEN,fg_color); + renderer->writeFastHLine(gp->xp+gp->xs-TICKLEN,cyp,TICKLEN,fg_color); + cyp+=yd; + } + } + } +} + +// define a graph +void DefineGraph(uint16_t num,uint16_t xp,uint16_t yp,int16_t xs,uint16_t ys,int16_t dec,float ymin, float ymax,uint8_t icol) { + if (!renderer) return; + uint8_t rflg=0; + if (xs<0) { + rflg=1; + xs=abs(xs); + } + struct GRAPH *gp; + uint16_t count; + uint16_t index=num%NUM_GRAPHS; + if (!graph[index]) { + gp=(struct GRAPH*)calloc(sizeof(struct GRAPH),1); + if (!gp) return; + graph[index]=gp; + } else { + gp=graph[index]; + if (rflg) { + RedrawGraph(index,1); + return; + } + } + + // 6 bits per axis + gp->xticks=(num>>4)&0x3f; + gp->yticks=(num>>10)&0x3f; + gp->xp=xp; + gp->yp=yp; + gp->xs=xs; + gp->ys=ys; + if (!dec) dec=1; + gp->decimation=dec; + if (dec>0) { + // is minutes per sweep prepare timing parameters in ms + gp->x_time=((float)dec*60000.0)/(float)xs; + gp->last_ms=millis()+gp->x_time; + } + gp->ymin=ymin; + gp->ymax=ymax; + gp->range=(ymax-ymin)/ys; + gp->xcnt=0; + gp->dcnt=0; + gp->summ=0; + if (gp->values) free(gp->values); + gp->values=(uint8_t*) calloc(1,xs+2); + if (!gp->values) { + free(gp); + graph[index]=0; + return; + } + // start from zero + gp->values[0]=0; + + gp->last_ms_redrawn=millis(); + + if (!icol) icol=1; + gp->color_index=icol; + gp->flags.overlay=0; + gp->flags.draw=1; + + // check if previous graph has same coordinates + if (index>0) { + for (uint8_t count=0; countxp==gp1->xp) && (gp->yp==gp1->yp)) { + gp->flags.overlay=1; + break; + } + } + } + } + + // draw rectangle + renderer->drawRect(xp,yp,xs,ys,fg_color); + // clr inside + ClrGraph(index); + +} + +// check if to advance GRAPH +void DisplayCheckGraph() { + int16_t count; + struct GRAPH *gp; + for (count=0;countdecimation>0) { + // if time over add value + while (millis()>gp->last_ms) { + gp->last_ms+=gp->x_time; + uint8_t val; + if (gp->dcnt) { + val=gp->summ/gp->dcnt; + gp->dcnt=0; + gp->summ=0; + gp->last_val=val; + } else { + val=gp->last_val; + } + AddGraph(count,val); + } + } + } + } +} + + +#if defined(USE_SCRIPT_FATFS) && defined(USE_SCRIPT) +#include + +void Save_graph(uint8_t num, char *path) { + if (!renderer) return; + uint16_t index=num%NUM_GRAPHS; + struct GRAPH *gp=graph[index]; + if (!gp) return; + File fp; + SD.remove(path); + fp=SD.open(path,FILE_WRITE); + if (!fp) return; + char str[32]; + sprintf_P(str,PSTR("%d\t%d\t%d\t"),gp->xcnt,gp->xs,gp->ys); + fp.print(str); + dtostrfd(gp->ymin,2,str); + fp.print(str); + fp.print("\t"); + dtostrfd(gp->ymax,2,str); + fp.print(str); + fp.print("\t"); + for (uint32_t count=0;countxs;count++) { + dtostrfd(gp->values[count],0,str); + fp.print(str); + fp.print("\t"); + } + fp.print("\n"); + fp.close(); +} +void Restore_graph(uint8_t num, char *path) { + if (!renderer) return; + uint16_t index=num%NUM_GRAPHS; + struct GRAPH *gp=graph[index]; + if (!gp) return; + File fp; + fp=SD.open(path,FILE_READ); + if (!fp) return; + char vbuff[32]; + char *cp=vbuff; + uint8_t buf[2]; + uint8_t findex=0; + + for (uint32_t count=0;count<=gp->xs+4;count++) { + cp=vbuff; + findex=0; + while (fp.available()) { + fp.read(buf,1); + if (buf[0]=='\t' || buf[0]==',' || buf[0]=='\n' || buf[0]=='\r') { + break; + } else { + *cp++=buf[0]; + findex++; + if (findex>=sizeof(vbuff)-1) break; + } + } + *cp=0; + if (count<=4) { + if (count==0) gp->xcnt=atoi(vbuff); + } else { + gp->values[count-5]=atoi(vbuff); + } + } + fp.close(); + RedrawGraph(num,1); +} +#endif + +void RedrawGraph(uint8_t num, uint8_t flags) { + uint16_t index=num%NUM_GRAPHS; + struct GRAPH *gp=graph[index]; + if (!gp) return; + if (!flags) { + gp->flags.draw=0; + return; + } + if (!renderer) return; + + gp->flags.draw=1; + uint16_t linecol=fg_color; + + if (color_type==COLOR_COLOR) { + linecol=renderer->GetColorFromIndex(gp->color_index); + } + + if (!gp->flags.overlay) { + // draw rectangle + renderer->drawRect(gp->xp,gp->yp,gp->xs,gp->ys,fg_color); + // clr inside + ClrGraph(index); + } + + for (uint16_t count=0;countxs-1;count++) { + renderer->writeLine(gp->xp+count,gp->yp+gp->ys-gp->values[count]-1,gp->xp+count+1,gp->yp+gp->ys-gp->values[count+1]-1,linecol); + } +} + +// add next value to graph +void AddGraph(uint8_t num,uint8_t val) { + struct GRAPH *gp=graph[num]; + if (!renderer) return; + + uint16_t linecol=fg_color; + if (color_type==COLOR_COLOR) { + linecol=renderer->GetColorFromIndex(gp->color_index); + } + gp->xcnt++; + if (gp->xcnt>gp->xs) { + gp->xcnt=gp->xs; + int16_t count; + // shift values + for (count=0;countxs-1;count++) { + gp->values[count]=gp->values[count+1]; + } + gp->values[gp->xcnt-1]=val; + + if (!gp->flags.draw) return; + + // only redraw every second or longer + if (millis()-gp->last_ms_redrawn>1000) { + gp->last_ms_redrawn=millis(); + // clr area and redraw graph + if (!gp->flags.overlay) { + // draw rectangle + renderer->drawRect(gp->xp,gp->yp,gp->xs,gp->ys,fg_color); + // clr inner and draw ticks + ClrGraph(num); + } + + for (count=0;countxs-1;count++) { + renderer->writeLine(gp->xp+count,gp->yp+gp->ys-gp->values[count]-1,gp->xp+count+1,gp->yp+gp->ys-gp->values[count+1]-1,linecol); + } + } + } else { + // add value and draw a single line + gp->values[gp->xcnt]=val; + if (!gp->flags.draw) return; + renderer->writeLine(gp->xp+gp->xcnt-1,gp->yp+gp->ys-gp->values[gp->xcnt-1]-1,gp->xp+gp->xcnt,gp->yp+gp->ys-gp->values[gp->xcnt]-1,linecol); + } +} + + +// add next value +void AddValue(uint8_t num,float fval) { + // not yet defined ??? + num=num%NUM_GRAPHS; + struct GRAPH *gp=graph[num]; + if (!gp) return; + + if (fval>gp->ymax) fval=gp->ymax; + if (fvalymin) fval=gp->ymin; + + int16_t val; + val=(fval-gp->ymin)/gp->range; + + if (val>gp->ys-1) val=gp->ys-1; + if (val<0) val=0; + + // summ values + gp->summ+=val; + gp->dcnt++; + + // decimation option + if (gp->decimation<0) { + if (gp->dcnt>=-gp->decimation) { + gp->dcnt=0; + // calc average + val=gp->summ/-gp->decimation; + gp->summ=0; + // add to graph + AddGraph(num,val); + } + } +} +#endif + /*********************************************************************************************\ * Interface \*********************************************************************************************/ @@ -1083,20 +1967,28 @@ bool Xdrv13(uint8_t function) switch (function) { case FUNC_PRE_INIT: DisplayInitDriver(); +#ifdef USE_GRAPH + for (uint8_t count=0;count. -*/ - -#ifdef USE_LIGHT -#ifdef USE_TUYA_DIMMER - -#define XDRV_16 16 - -#ifndef TUYA_DIMMER_ID -#define TUYA_DIMMER_ID 0 -#endif - -#define TUYA_POWER_ID 1 - -#define TUYA_CMD_HEARTBEAT 0x00 -#define TUYA_CMD_QUERY_PRODUCT 0x01 -#define TUYA_CMD_MCU_CONF 0x02 -#define TUYA_CMD_WIFI_STATE 0x03 -#define TUYA_CMD_WIFI_RESET 0x04 -#define TUYA_CMD_WIFI_SELECT 0x05 -#define TUYA_CMD_SET_DP 0x06 -#define TUYA_CMD_STATE 0x07 -#define TUYA_CMD_QUERY_STATE 0x08 - -#define TUYA_TYPE_BOOL 0x01 -#define TUYA_TYPE_VALUE 0x02 - -#define TUYA_BUFFER_SIZE 256 - -#include - -TasmotaSerial *TuyaSerial = nullptr; - -uint8_t tuya_new_dim = 0; // Tuya dimmer value temp -bool tuya_ignore_dim = false; // Flag to skip serial send to prevent looping when processing inbound states from the faceplate interaction -uint8_t tuya_cmd_status = 0; // Current status of serial-read -uint8_t tuya_cmd_checksum = 0; // Checksum of tuya command -uint8_t tuya_data_len = 0; // Data lenght of command -int8_t tuya_wifi_state = -2; // Keep MCU wifi-status in sync with WifiState() -uint8_t tuya_heartbeat_timer = 0; // 10 second heartbeat timer for tuya module - -char *tuya_buffer = nullptr; // Serial receive buffer -int tuya_byte_counter = 0; // Index in serial receive buffer - -/*********************************************************************************************\ - * Internal Functions -\*********************************************************************************************/ - -void TuyaSendCmd(uint8_t cmd, uint8_t payload[] = nullptr, uint16_t payload_len = 0) -{ - uint8_t checksum = (0xFF + cmd + (payload_len >> 8) + (payload_len & 0xFF)); - TuyaSerial->write(0x55); // Tuya header 55AA - TuyaSerial->write(0xAA); - TuyaSerial->write((uint8_t)0x00); // version 00 - TuyaSerial->write(cmd); // Tuya command - TuyaSerial->write(payload_len >> 8); // following data length (Hi) - TuyaSerial->write(payload_len & 0xFF); // following data length (Lo) - snprintf_P(log_data, sizeof(log_data), PSTR("TYA: Send \"55aa00%02x%02x%02x"), cmd, payload_len >> 8, payload_len & 0xFF); - for (uint32_t i = 0; i < payload_len; ++i) { - TuyaSerial->write(payload[i]); - checksum += payload[i]; - snprintf_P(log_data, sizeof(log_data), PSTR("%s%02x"), log_data, payload[i]); - } - TuyaSerial->write(checksum); - TuyaSerial->flush(); - snprintf_P(log_data, sizeof(log_data), PSTR("%s%02x\""), log_data, checksum); - AddLog(LOG_LEVEL_DEBUG); -} - -void TuyaSendState(uint8_t id, uint8_t type, uint8_t* value) -{ - uint16_t payload_len = 4; - uint8_t payload_buffer[8]; - payload_buffer[0] = id; - payload_buffer[1] = type; - switch (type) { - case TUYA_TYPE_BOOL: - payload_len += 1; - payload_buffer[2] = 0x00; - payload_buffer[3] = 0x01; - payload_buffer[4] = value[0]; - break; - case TUYA_TYPE_VALUE: - payload_len += 4; - payload_buffer[2] = 0x00; - payload_buffer[3] = 0x04; - payload_buffer[4] = value[3]; - payload_buffer[5] = value[2]; - payload_buffer[6] = value[1]; - payload_buffer[7] = value[0]; - break; - } - - TuyaSendCmd(TUYA_CMD_SET_DP, payload_buffer, payload_len); -} - -void TuyaSendBool(uint8_t id, bool value) -{ - TuyaSendState(id, TUYA_TYPE_BOOL, (uint8_t*)&value); -} - -void TuyaSendValue(uint8_t id, uint32_t value) -{ - TuyaSendState(id, TUYA_TYPE_VALUE, (uint8_t*)(&value)); -} - -bool TuyaSetPower(void) -{ - bool status = false; - - uint8_t rpower = XdrvMailbox.index; - int16_t source = XdrvMailbox.payload; - - if (source != SRC_SWITCH && TuyaSerial) { // ignore to prevent loop from pushing state from faceplate interaction - TuyaSendBool(active_device, bitRead(rpower, active_device-1)); - status = true; - } - return status; -} - -bool TuyaSetChannels(void) -{ - LightSerialDuty(((uint8_t*)XdrvMailbox.data)[0]); - return true; -} - -void LightSerialDuty(uint8_t duty) -{ - if (duty > 0 && !tuya_ignore_dim && TuyaSerial) { - if (duty < 25) { duty = 25; } // dimming acts odd below 25(10%) - this mirrors the threshold set on the faceplate itself - - if (Settings.flag3.tuya_show_dimmer == 0) { - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Send dim value=%d (id=%d)"), duty, Settings.param[P_TUYA_DIMMER_ID]); - - TuyaSendValue(Settings.param[P_TUYA_DIMMER_ID], duty); - } - } else { - tuya_ignore_dim = false; // reset flag - if (Settings.flag3.tuya_show_dimmer == 0) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Send dim skipped value=%d"), duty); // due to 0 or already set - } - } -} - -void TuyaRequestState(void) -{ - if (TuyaSerial) { - // Get current status of MCU - AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Read MCU state")); - - TuyaSendCmd(TUYA_CMD_QUERY_STATE); - } -} - -void TuyaResetWifi(void) -{ - if (!Settings.flag.button_restrict) { - char scmnd[20]; - snprintf_P(scmnd, sizeof(scmnd), D_CMND_WIFICONFIG " %d", 2); - ExecuteCommand(scmnd, SRC_BUTTON); - } -} - -void TuyaPacketProcess(void) -{ - char scmnd[20]; - - switch (tuya_buffer[3]) { - - case TUYA_CMD_HEARTBEAT: - AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Heartbeat")); - if (tuya_buffer[6] == 0) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Detected MCU restart")); - tuya_wifi_state = -2; - } - break; - - case TUYA_CMD_STATE: - if (tuya_buffer[5] == 5) { // on/off packet - - /*if ((power || Settings.light_dimmer > 0) && (power != tuya_buffer[10])) { - ExecuteCommandPower(1, tuya_buffer[10], SRC_SWITCH); // send SRC_SWITCH? to use as flag to prevent loop from inbound states from faceplate interaction - }*/ - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: RX Device-%d --> MCU State: %s Current State:%s"),tuya_buffer[6],tuya_buffer[10]?"On":"Off",bitRead(power, tuya_buffer[6]-1)?"On":"Off"); - if ((power || Settings.light_dimmer > 0) && (tuya_buffer[10] != bitRead(power, tuya_buffer[6]-1))) { - ExecuteCommandPower(tuya_buffer[6], tuya_buffer[10], SRC_SWITCH); // send SRC_SWITCH? to use as flag to prevent loop from inbound states from faceplate interaction - } - } - else if (tuya_buffer[5] == 8) { // dim packet - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: RX Dim State=%d"), tuya_buffer[13]); - if (Settings.flag3.tuya_show_dimmer == 0) { - if (!Settings.param[P_TUYA_DIMMER_ID]) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Autoconfiguring Dimmer ID %d"), tuya_buffer[6]); - Settings.param[P_TUYA_DIMMER_ID] = tuya_buffer[6]; - } - - tuya_new_dim = round(tuya_buffer[13] * (100. / 255.)); - if ((power || Settings.flag3.tuya_apply_o20) && (tuya_new_dim > 0) && (abs(tuya_new_dim - Settings.light_dimmer) > 1)) { - tuya_ignore_dim = true; - - snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_DIMMER " %d"), tuya_new_dim ); - ExecuteCommand(scmnd, SRC_SWITCH); - } - } - } - break; - - case TUYA_CMD_WIFI_RESET: - case TUYA_CMD_WIFI_SELECT: - AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: RX WiFi Reset")); - TuyaResetWifi(); - break; - - case TUYA_CMD_WIFI_STATE: - AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: RX WiFi LED set ACK")); - tuya_wifi_state = WifiState(); - break; - - case TUYA_CMD_MCU_CONF: - AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: RX MCU configuration")); - - if (tuya_buffer[5] == 2) { - uint8_t led1_gpio = tuya_buffer[6]; - uint8_t key1_gpio = tuya_buffer[7]; - bool key1_set = false; - bool led1_set = false; - for (uint32_t i = 0; i < sizeof(Settings.my_gp); i++) { - if (Settings.my_gp.io[i] == GPIO_LED1) led1_set = true; - else if (Settings.my_gp.io[i] == GPIO_KEY1) key1_set = true; - } - if (!Settings.my_gp.io[led1_gpio] && !led1_set) { - Settings.my_gp.io[led1_gpio] = GPIO_LED1; - restart_flag = 2; - } - if (!Settings.my_gp.io[key1_gpio] && !key1_set) { - Settings.my_gp.io[key1_gpio] = GPIO_KEY1; - restart_flag = 2; - } - } - TuyaRequestState(); - break; - - default: - AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: RX unknown command")); - } -} - -/*********************************************************************************************\ - * API Functions -\*********************************************************************************************/ - -bool TuyaModuleSelected(void) -{ - if (!(pin[GPIO_TUYA_RX] < 99) || !(pin[GPIO_TUYA_TX] < 99)) { // fallback to hardware-serial if not explicitly selected - pin[GPIO_TUYA_TX] = 1; - pin[GPIO_TUYA_RX] = 3; - Settings.my_gp.io[1] = GPIO_TUYA_TX; - Settings.my_gp.io[3] = GPIO_TUYA_RX; - restart_flag = 2; - } - light_type = LT_SERIAL1; - return true; -} - -void TuyaInit(void) -{ - devices_present = Settings.param[6] == 0 ? 1 : Settings.param[6]; - if (!Settings.param[P_TUYA_DIMMER_ID]) { - Settings.param[P_TUYA_DIMMER_ID] = TUYA_DIMMER_ID; - } - tuya_buffer = (char*)(malloc(TUYA_BUFFER_SIZE)); - if (tuya_buffer != nullptr) { - TuyaSerial = new TasmotaSerial(pin[GPIO_TUYA_RX], pin[GPIO_TUYA_TX], 2); - if (TuyaSerial->begin(9600)) { - if (TuyaSerial->hardwareSerial()) { ClaimSerial(); } - // Get MCU Configuration - AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Request MCU configuration")); - - TuyaSendCmd(TUYA_CMD_MCU_CONF); - } - } - tuya_heartbeat_timer = 0; // init heartbeat timer when dimmer init is done -} - -void TuyaSerialInput(void) -{ - while (TuyaSerial->available()) { - yield(); - uint8_t serial_in_byte = TuyaSerial->read(); - - if (serial_in_byte == 0x55) { // Start TUYA Packet - tuya_cmd_status = 1; - tuya_buffer[tuya_byte_counter++] = serial_in_byte; - tuya_cmd_checksum += serial_in_byte; - } - else if (tuya_cmd_status == 1 && serial_in_byte == 0xAA) { // Only packtes with header 0x55AA are valid - tuya_cmd_status = 2; - - tuya_byte_counter = 0; - tuya_buffer[tuya_byte_counter++] = 0x55; - tuya_buffer[tuya_byte_counter++] = 0xAA; - tuya_cmd_checksum = 0xFF; - } - else if (tuya_cmd_status == 2) { - if (tuya_byte_counter == 5) { // Get length of data - tuya_cmd_status = 3; - tuya_data_len = serial_in_byte; - } - tuya_cmd_checksum += serial_in_byte; - tuya_buffer[tuya_byte_counter++] = serial_in_byte; - } - else if ((tuya_cmd_status == 3) && (tuya_byte_counter == (6 + tuya_data_len)) && (tuya_cmd_checksum == serial_in_byte)) { // Compare checksum and process packet - tuya_buffer[tuya_byte_counter++] = serial_in_byte; - - snprintf_P(log_data, sizeof(log_data), PSTR("TYA: RX Packet: \"")); - for (uint32_t i = 0; i < tuya_byte_counter; i++) { - snprintf_P(log_data, sizeof(log_data), PSTR("%s%02x"), log_data, tuya_buffer[i]); - } - snprintf_P(log_data, sizeof(log_data), PSTR("%s\""), log_data); - AddLog(LOG_LEVEL_DEBUG); - - TuyaPacketProcess(); - tuya_byte_counter = 0; - tuya_cmd_status = 0; - tuya_cmd_checksum = 0; - tuya_data_len = 0; - } // read additional packets from TUYA - else if (tuya_byte_counter < TUYA_BUFFER_SIZE -1) { // add char to string if it still fits - tuya_buffer[tuya_byte_counter++] = serial_in_byte; - tuya_cmd_checksum += serial_in_byte; - } else { - tuya_byte_counter = 0; - tuya_cmd_status = 0; - tuya_cmd_checksum = 0; - tuya_data_len = 0; - } - } -} - -bool TuyaButtonPressed(void) -{ - if (!XdrvMailbox.index && ((PRESSED == XdrvMailbox.payload) && (NOT_PRESSED == lastbutton[XdrvMailbox.index]))) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Reset GPIO triggered")); - TuyaResetWifi(); - return true; // Reset GPIO served here - } - return false; // Don't serve other buttons -} - -void TuyaSetWifiLed(void) -{ - uint8_t wifi_state = 0x02; - switch(WifiState()){ - case WIFI_SMARTCONFIG: - wifi_state = 0x00; - break; - case WIFI_MANAGER: - case WIFI_WPSCONFIG: - wifi_state = 0x01; - break; - case WIFI_RESTART: - wifi_state = 0x03; - break; - } - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Set WiFi LED %d (%d)"), wifi_state, WifiState()); - - TuyaSendCmd(TUYA_CMD_WIFI_STATE, &wifi_state, 1); -} - -/*********************************************************************************************\ - * Interface -\*********************************************************************************************/ - -bool Xdrv16(uint8_t function) -{ - bool result = false; - - if (TUYA_DIMMER == my_module_type) { - switch (function) { - case FUNC_LOOP: - if (TuyaSerial) { TuyaSerialInput(); } - break; - case FUNC_MODULE_INIT: - result = TuyaModuleSelected(); - break; - case FUNC_INIT: - TuyaInit(); - break; - case FUNC_SET_DEVICE_POWER: - result = TuyaSetPower(); - break; - case FUNC_BUTTON_PRESSED: - result = TuyaButtonPressed(); - break; - case FUNC_EVERY_SECOND: - if (TuyaSerial && tuya_wifi_state != WifiState()) { TuyaSetWifiLed(); } - tuya_heartbeat_timer++; - if (tuya_heartbeat_timer > 10) { - tuya_heartbeat_timer = 0; - TuyaSendCmd(TUYA_CMD_HEARTBEAT); - } - break; - case FUNC_SET_CHANNELS: - result = TuyaSetChannels(); - break; - } - } - return result; -} - -#endif // USE_TUYA_DIMMER -#endif // USE_LIGHT diff --git a/sonoff/xdrv_16_tuyamcu.ino b/sonoff/xdrv_16_tuyamcu.ino new file mode 100644 index 000000000..797cdba8f --- /dev/null +++ b/sonoff/xdrv_16_tuyamcu.ino @@ -0,0 +1,662 @@ +/* + xdrv_16_tuyamcu.ino - Tuya MCU support for Sonoff-Tasmota + + Copyright (C) 2019 digiblur, Joel Stein and Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef USE_LIGHT +#ifdef USE_TUYA_MCU + +#define XDRV_16 16 +#define XNRG_16 16 // Needs to be the last XNRG_xx + +#ifndef TUYA_DIMMER_ID +#define TUYA_DIMMER_ID 0 +#endif + +#define TUYA_CMD_HEARTBEAT 0x00 +#define TUYA_CMD_QUERY_PRODUCT 0x01 +#define TUYA_CMD_MCU_CONF 0x02 +#define TUYA_CMD_WIFI_STATE 0x03 +#define TUYA_CMD_WIFI_RESET 0x04 +#define TUYA_CMD_WIFI_SELECT 0x05 +#define TUYA_CMD_SET_DP 0x06 +#define TUYA_CMD_STATE 0x07 +#define TUYA_CMD_QUERY_STATE 0x08 + +#define TUYA_TYPE_BOOL 0x01 +#define TUYA_TYPE_VALUE 0x02 + +#define TUYA_BUFFER_SIZE 256 + +#include + +TasmotaSerial *TuyaSerial = nullptr; + +struct TUYA { + uint16_t new_dim = 0; // Tuya dimmer value temp + bool ignore_dim = false; // Flag to skip serial send to prevent looping when processing inbound states from the faceplate interaction + uint8_t cmd_status = 0; // Current status of serial-read + uint8_t cmd_checksum = 0; // Checksum of tuya command + uint8_t data_len = 0; // Data lenght of command + int8_t wifi_state = -2; // Keep MCU wifi-status in sync with WifiState() + uint8_t heartbeat_timer = 0; // 10 second heartbeat timer for tuya module +#ifdef USE_ENERGY_SENSOR + uint32_t lastPowerCheckTime = 0; // Time when last power was checked +#endif // USE_ENERGY_SENSOR + char *buffer = nullptr; // Serial receive buffer + int byte_counter = 0; // Index in serial receive buffer +} Tuya; + + +enum TuyaSupportedFunctions { + TUYA_MCU_FUNC_NONE, + TUYA_MCU_FUNC_SWT1 = 1, // Buttons + TUYA_MCU_FUNC_SWT2, + TUYA_MCU_FUNC_SWT3, + TUYA_MCU_FUNC_SWT4, + TUYA_MCU_FUNC_REL1 = 11, // Relays + TUYA_MCU_FUNC_REL2, + TUYA_MCU_FUNC_REL3, + TUYA_MCU_FUNC_REL4, + TUYA_MCU_FUNC_REL5, + TUYA_MCU_FUNC_REL6, + TUYA_MCU_FUNC_REL7, + TUYA_MCU_FUNC_REL8, + TUYA_MCU_FUNC_DIMMER = 21, + TUYA_MCU_FUNC_POWER = 31, + TUYA_MCU_FUNC_CURRENT, + TUYA_MCU_FUNC_VOLTAGE, + TUYA_MCU_FUNC_REL1_INV = 41, // Inverted Relays + TUYA_MCU_FUNC_REL2_INV, + TUYA_MCU_FUNC_REL3_INV, + TUYA_MCU_FUNC_REL4_INV, + TUYA_MCU_FUNC_REL5_INV, + TUYA_MCU_FUNC_REL6_INV, + TUYA_MCU_FUNC_REL7_INV, + TUYA_MCU_FUNC_REL8_INV, + TUYA_MCU_FUNC_LAST = 255 +}; + +const char kTuyaCommand[] PROGMEM = "|" // No prefix + D_CMND_TUYA_MCU; + +void (* const TuyaCommand[])(void) PROGMEM = { + &CmndTuyaMcu +}; + + +/* + +TuyaMcu fnid,dpid + +*/ + +void CmndTuyaMcu(void) { + if (XdrvMailbox.data_len > 0) { + char *p; + uint8_t i = 0; + uint8_t parm[3] = { 0 }; + for (char *str = strtok_r(XdrvMailbox.data, ", ", &p); str && i < 2; str = strtok_r(nullptr, ", ", &p)) { + parm[i] = strtoul(str, nullptr, 0); + i++; + } + + if (TuyaFuncIdValid(parm[0])) { + TuyaAddMcuFunc(parm[0], parm[1]); + restart_flag = 2; + } else { + AddLog_P2(LOG_LEVEL_ERROR, PSTR("TYA: TuyaMcu Invalid function id=%d"), parm[0]); + } + + } + + Response_P(PSTR("{\"" D_CMND_TUYA_MCU "\":[")); + bool added = false; + for (uint8_t i = 0; i < MAX_TUYA_FUNCTIONS; i++) { + if (Settings.tuya_fnid_map[i].fnid != 0) { + if (added) { + ResponseAppend_P(PSTR(",")); + } + ResponseAppend_P(PSTR("{\"fnId\":%d,\"dpId\":%d}" ), Settings.tuya_fnid_map[i].fnid, Settings.tuya_fnid_map[i].dpid); + added = true; + } + } + ResponseAppend_P(PSTR("]}")); +} + +/*********************************************************************************************\ + * Internal Functions +\*********************************************************************************************/ + +void TuyaAddMcuFunc(uint8_t fnId, uint8_t dpId) { + bool added = false; + + if (fnId == 0 || dpId == 0) { // Delete entry + for (uint8_t i = 0; i < MAX_TUYA_FUNCTIONS; i++) { + if ((dpId > 0 && Settings.tuya_fnid_map[i].dpid == dpId) || (fnId > TUYA_MCU_FUNC_NONE && Settings.tuya_fnid_map[i].fnid == fnId)) { + Settings.tuya_fnid_map[i].fnid = TUYA_MCU_FUNC_NONE; + Settings.tuya_fnid_map[i].dpid = 0; + break; + } + } + } else { // Add or update + for (uint8_t i = 0; i < MAX_TUYA_FUNCTIONS; i++) { + if (Settings.tuya_fnid_map[i].dpid == dpId || Settings.tuya_fnid_map[i].dpid == 0 || Settings.tuya_fnid_map[i].fnid == fnId || Settings.tuya_fnid_map[i].fnid == 0) { + if (!added) { // Update entry if exisiting entry or add + Settings.tuya_fnid_map[i].fnid = fnId; + Settings.tuya_fnid_map[i].dpid = dpId; + added = true; + } else if (Settings.tuya_fnid_map[i].dpid == dpId || Settings.tuya_fnid_map[i].fnid == fnId) { // Remove existing entry if added to empty place + Settings.tuya_fnid_map[i].fnid = TUYA_MCU_FUNC_NONE; + Settings.tuya_fnid_map[i].dpid = 0; + } + } + } + } + UpdateDevices(); +} + +void UpdateDevices() { + for (uint8_t i = 0; i < MAX_TUYA_FUNCTIONS; i++) { + uint8_t fnId = Settings.tuya_fnid_map[i].fnid; + if (fnId > TUYA_MCU_FUNC_NONE && Settings.tuya_fnid_map[i].dpid > 0) { + + if (fnId >= TUYA_MCU_FUNC_REL1 && fnId <= TUYA_MCU_FUNC_REL8) { //Relay + bitClear(rel_inverted, fnId - TUYA_MCU_FUNC_REL1); + } else if (fnId >= TUYA_MCU_FUNC_REL1_INV && fnId <= TUYA_MCU_FUNC_REL8_INV) { // Inverted Relay + bitSet(rel_inverted, fnId - TUYA_MCU_FUNC_REL1_INV); + } + + } + } +} + +inline bool TuyaFuncIdValid(uint8_t fnId) { + return (fnId >= TUYA_MCU_FUNC_SWT1 && fnId <= TUYA_MCU_FUNC_SWT4) || + (fnId >= TUYA_MCU_FUNC_REL1 && fnId <= TUYA_MCU_FUNC_REL8) || + fnId == TUYA_MCU_FUNC_DIMMER || + (fnId >= TUYA_MCU_FUNC_POWER && fnId <= TUYA_MCU_FUNC_VOLTAGE) || + (fnId >= TUYA_MCU_FUNC_REL1_INV && fnId <= TUYA_MCU_FUNC_REL8_INV); +} + +uint8_t TuyaGetFuncId(uint8_t dpid) { + for (uint8_t i = 0; i < MAX_TUYA_FUNCTIONS; i++) { + if (Settings.tuya_fnid_map[i].dpid == dpid) { + return Settings.tuya_fnid_map[i].fnid; + } + } + return TUYA_MCU_FUNC_NONE; +} + +uint8_t TuyaGetDpId(uint8_t fnId) { + for (uint8_t i = 0; i < MAX_TUYA_FUNCTIONS; i++) { + if (Settings.tuya_fnid_map[i].fnid == fnId) { + return Settings.tuya_fnid_map[i].dpid; + } + } + return 0; +} + +void TuyaSendCmd(uint8_t cmd, uint8_t payload[] = nullptr, uint16_t payload_len = 0) +{ + uint8_t checksum = (0xFF + cmd + (payload_len >> 8) + (payload_len & 0xFF)); + TuyaSerial->write(0x55); // Tuya header 55AA + TuyaSerial->write(0xAA); + TuyaSerial->write((uint8_t)0x00); // version 00 + TuyaSerial->write(cmd); // Tuya command + TuyaSerial->write(payload_len >> 8); // following data length (Hi) + TuyaSerial->write(payload_len & 0xFF); // following data length (Lo) + snprintf_P(log_data, sizeof(log_data), PSTR("TYA: Send \"55aa00%02x%02x%02x"), cmd, payload_len >> 8, payload_len & 0xFF); + for (uint32_t i = 0; i < payload_len; ++i) { + TuyaSerial->write(payload[i]); + checksum += payload[i]; + snprintf_P(log_data, sizeof(log_data), PSTR("%s%02x"), log_data, payload[i]); + } + TuyaSerial->write(checksum); + TuyaSerial->flush(); + snprintf_P(log_data, sizeof(log_data), PSTR("%s%02x\""), log_data, checksum); + AddLog(LOG_LEVEL_DEBUG); +} + +void TuyaSendState(uint8_t id, uint8_t type, uint8_t* value) +{ + uint16_t payload_len = 4; + uint8_t payload_buffer[8]; + payload_buffer[0] = id; + payload_buffer[1] = type; + switch (type) { + case TUYA_TYPE_BOOL: + payload_len += 1; + payload_buffer[2] = 0x00; + payload_buffer[3] = 0x01; + payload_buffer[4] = value[0]; + break; + case TUYA_TYPE_VALUE: + payload_len += 4; + payload_buffer[2] = 0x00; + payload_buffer[3] = 0x04; + payload_buffer[4] = value[3]; + payload_buffer[5] = value[2]; + payload_buffer[6] = value[1]; + payload_buffer[7] = value[0]; + break; + } + + TuyaSendCmd(TUYA_CMD_SET_DP, payload_buffer, payload_len); +} + +void TuyaSendBool(uint8_t id, bool value) +{ + TuyaSendState(id, TUYA_TYPE_BOOL, (uint8_t*)&value); +} + +void TuyaSendValue(uint8_t id, uint32_t value) +{ + TuyaSendState(id, TUYA_TYPE_VALUE, (uint8_t*)(&value)); +} + +bool TuyaSetPower(void) +{ + bool status = false; + + uint8_t rpower = XdrvMailbox.index; + int16_t source = XdrvMailbox.payload; + + if (source != SRC_SWITCH && TuyaSerial) { // ignore to prevent loop from pushing state from faceplate interaction + TuyaSendBool(active_device, bitRead(rpower, active_device-1) ^ bitRead(rel_inverted, active_device-1)); + status = true; + } + return status; +} + +bool TuyaSetChannels(void) +{ + LightSerialDuty(((uint8_t*)XdrvMailbox.data)[0]); + delay(20); // Hack when power is off and dimmer is set then both commands go too soon to Serial out. + return true; +} + +void LightSerialDuty(uint16_t duty) +{ + uint8_t dpid = TuyaGetDpId(TUYA_MCU_FUNC_DIMMER); + if (duty > 0 && !Tuya.ignore_dim && TuyaSerial && dpid > 0) { + if (duty < Settings.dimmer_hw_min) { duty = Settings.dimmer_hw_min; } // dimming acts odd below 25(10%) - this mirrors the threshold set on the faceplate itself + duty = changeUIntScale(duty, 0, 255, 0, Settings.dimmer_hw_max); + if (Tuya.new_dim != duty) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Send dim value=%d (id=%d)"), duty, dpid); + TuyaSendValue(dpid, duty); + } + } else if (dpid > 0) { + Tuya.ignore_dim = false; // reset flag + duty = changeUIntScale(duty, 0, 255, 0, Settings.dimmer_hw_max); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Send dim skipped value=%d"), duty); // due to 0 or already set + } else { + AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Cannot set dimmer. Dimmer Id unknown")); // + } +} + +void TuyaRequestState(void) +{ + if (TuyaSerial) { + // Get current status of MCU + AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Read MCU state")); + + TuyaSendCmd(TUYA_CMD_QUERY_STATE); + } +} + +void TuyaResetWifi(void) +{ + if (!Settings.flag.button_restrict) { + char scmnd[20]; + snprintf_P(scmnd, sizeof(scmnd), D_CMND_WIFICONFIG " %d", 2); + ExecuteCommand(scmnd, SRC_BUTTON); + } +} + +void TuyaPacketProcess(void) +{ + char scmnd[20]; + uint8_t fnId = TUYA_MCU_FUNC_NONE; + + switch (Tuya.buffer[3]) { + + case TUYA_CMD_HEARTBEAT: + AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Heartbeat")); + if (Tuya.buffer[6] == 0) { + AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Detected MCU restart")); + Tuya.wifi_state = -2; + } + break; + + case TUYA_CMD_STATE: + fnId = TuyaGetFuncId(Tuya.buffer[6]); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: FnId=%d is set for dpId=%d"), fnId, Tuya.buffer[6]); + // if (TuyaFuncIdValid(fnId)) { + if (Tuya.buffer[5] == 5) { // on/off packet + + if (fnId >= TUYA_MCU_FUNC_REL1 && fnId <= TUYA_MCU_FUNC_REL8) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: RX Relay-%d --> MCU State: %s Current State:%s"), fnId - TUYA_MCU_FUNC_REL1 + 1, Tuya.buffer[10]?"On":"Off",bitRead(power, fnId - TUYA_MCU_FUNC_REL1)?"On":"Off"); + if ((power || Settings.light_dimmer > 0) && (Tuya.buffer[10] != bitRead(power, fnId - TUYA_MCU_FUNC_REL1))) { + ExecuteCommandPower(fnId - TUYA_MCU_FUNC_REL1 + 1, Tuya.buffer[10], SRC_SWITCH); // send SRC_SWITCH? to use as flag to prevent loop from inbound states from faceplate interaction + } + } else if (fnId >= TUYA_MCU_FUNC_REL1_INV && fnId <= TUYA_MCU_FUNC_REL8_INV) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: RX Relay-%d-Inverted --> MCU State: %s Current State:%s"), fnId - TUYA_MCU_FUNC_REL1_INV + 1, Tuya.buffer[10]?"Off":"On",bitRead(power, fnId - TUYA_MCU_FUNC_REL1_INV) ^ 1?"Off":"On"); + if (Tuya.buffer[10] != bitRead(power, fnId - TUYA_MCU_FUNC_REL1_INV) ^ 1) { + ExecuteCommandPower(fnId - TUYA_MCU_FUNC_REL1_INV + 1, Tuya.buffer[10] ^ 1, SRC_SWITCH); // send SRC_SWITCH? to use as flag to prevent loop from inbound states from faceplate interaction + } + } else if (fnId >= TUYA_MCU_FUNC_SWT1 && fnId <= TUYA_MCU_FUNC_SWT4) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: RX Switch-%d --> MCU State: %d Current State:%d"),fnId - TUYA_MCU_FUNC_SWT1 + 1,Tuya.buffer[10], SwitchGetVirtual(fnId - TUYA_MCU_FUNC_SWT1)); + + if (SwitchGetVirtual(fnId - TUYA_MCU_FUNC_SWT1) != Tuya.buffer[10]) { + SwitchSetVirtual(fnId - TUYA_MCU_FUNC_SWT1, Tuya.buffer[10]); + SwitchHandler(1); + } + } + + } + else if (Tuya.buffer[5] == 8) { // Long value packet + bool tuya_energy_enabled = (XNRG_16 == energy_flg); + uint16_t packetValue = Tuya.buffer[12] << 8 | Tuya.buffer[13]; + if (fnId == TUYA_MCU_FUNC_DIMMER) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: RX Dim State=%d"), packetValue); + Tuya.new_dim = changeUIntScale(packetValue, 0, Settings.dimmer_hw_max, 0, 100); + if ((power || Settings.flag3.tuya_apply_o20) && (Tuya.new_dim > 0) && (abs(Tuya.new_dim - Settings.light_dimmer) > 1)) { + Tuya.ignore_dim = true; + + snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_DIMMER " %d"), Tuya.new_dim ); + ExecuteCommand(scmnd, SRC_SWITCH); + } + } + + #ifdef USE_ENERGY_SENSOR + else if (tuya_energy_enabled && fnId == TUYA_MCU_FUNC_VOLTAGE) { + Energy.voltage[0] = (float)packetValue / 10; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Rx ID=%d Voltage=%d"), Tuya.buffer[6], packetValue); + } else if (tuya_energy_enabled && fnId == TUYA_MCU_FUNC_CURRENT) { + Energy.current[0] = (float)packetValue / 1000; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Rx ID=%d Current=%d"), Tuya.buffer[6], packetValue); + } else if (tuya_energy_enabled && fnId == TUYA_MCU_FUNC_POWER) { + Energy.active_power[0] = (float)packetValue / 10; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Rx ID=%d Active_Power=%d"), Tuya.buffer[6], packetValue); + + if (Tuya.lastPowerCheckTime != 0 && Energy.active_power[0] > 0) { + Energy.kWhtoday += (float)Energy.active_power[0] * (Rtc.utc_time - Tuya.lastPowerCheckTime) / 36; + EnergyUpdateToday(); + } + Tuya.lastPowerCheckTime = Rtc.utc_time; + } + #endif // USE_ENERGY_SENSOR + + } + // } else { + // AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Unknown FnId=%s for dpId=%s"), fnId, Tuya.buffer[6]); + // } + break; + + case TUYA_CMD_WIFI_RESET: + case TUYA_CMD_WIFI_SELECT: + AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: RX WiFi Reset")); + TuyaResetWifi(); + break; + + case TUYA_CMD_WIFI_STATE: + AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: RX WiFi LED set ACK")); + Tuya.wifi_state = WifiState(); + break; + + case TUYA_CMD_MCU_CONF: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: RX MCU configuration Mode=%d"), Tuya.buffer[5]); + + if (Tuya.buffer[5] == 2) { // Processing by ESP module mode + uint8_t led1_gpio = Tuya.buffer[6]; + uint8_t key1_gpio = Tuya.buffer[7]; + bool key1_set = false; + bool led1_set = false; + for (uint32_t i = 0; i < sizeof(Settings.my_gp); i++) { + if (Settings.my_gp.io[i] == GPIO_LED1) led1_set = true; + else if (Settings.my_gp.io[i] == GPIO_KEY1) key1_set = true; + } + if (!Settings.my_gp.io[led1_gpio] && !led1_set) { + Settings.my_gp.io[led1_gpio] = GPIO_LED1; + restart_flag = 2; + } + if (!Settings.my_gp.io[key1_gpio] && !key1_set) { + Settings.my_gp.io[key1_gpio] = GPIO_KEY1; + restart_flag = 2; + } + } + TuyaRequestState(); + break; + + default: + AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: RX unknown command")); + } +} + +/*********************************************************************************************\ + * API Functions +\*********************************************************************************************/ + +bool TuyaModuleSelected(void) +{ + if (!(pin[GPIO_TUYA_RX] < 99) || !(pin[GPIO_TUYA_TX] < 99)) { // fallback to hardware-serial if not explicitly selected + pin[GPIO_TUYA_TX] = 1; + pin[GPIO_TUYA_RX] = 3; + Settings.my_gp.io[1] = GPIO_TUYA_TX; + Settings.my_gp.io[3] = GPIO_TUYA_RX; + restart_flag = 2; + } + + if (TuyaGetDpId(TUYA_MCU_FUNC_DIMMER) == 0 && TUYA_DIMMER_ID > 0) { + TuyaAddMcuFunc(TUYA_MCU_FUNC_DIMMER, TUYA_DIMMER_ID); + } + + bool relaySet = false; + + for (uint8_t i = 0 ; i < MAX_TUYA_FUNCTIONS; i++) { + if ((Settings.tuya_fnid_map[i].fnid >= TUYA_MCU_FUNC_REL1 && Settings.tuya_fnid_map[i].fnid <= TUYA_MCU_FUNC_REL8 ) || + (Settings.tuya_fnid_map[i].fnid >= TUYA_MCU_FUNC_REL1_INV && Settings.tuya_fnid_map[i].fnid <= TUYA_MCU_FUNC_REL8_INV )) { + relaySet = true; + devices_present++; + } + } + + if (!relaySet) { + TuyaAddMcuFunc(TUYA_MCU_FUNC_REL1, 1); + devices_present++; + SettingsSaveAll(); + } + + if (TuyaGetDpId(TUYA_MCU_FUNC_DIMMER) != 0) { + light_type = LT_SERIAL1; + } else { + light_type = LT_BASIC; + } + + UpdateDevices(); + return true; +} + +void TuyaInit(void) +{ + Tuya.buffer = (char*)(malloc(TUYA_BUFFER_SIZE)); + if (Tuya.buffer != nullptr) { + TuyaSerial = new TasmotaSerial(pin[GPIO_TUYA_RX], pin[GPIO_TUYA_TX], 2); + if (TuyaSerial->begin(9600)) { + if (TuyaSerial->hardwareSerial()) { ClaimSerial(); } + // Get MCU Configuration + AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Request MCU configuration")); + + TuyaSendCmd(TUYA_CMD_MCU_CONF); + } + } + Tuya.heartbeat_timer = 0; // init heartbeat timer when dimmer init is done +} + +void TuyaSerialInput(void) +{ + while (TuyaSerial->available()) { + yield(); + uint8_t serial_in_byte = TuyaSerial->read(); + + if (serial_in_byte == 0x55) { // Start TUYA Packet + Tuya.cmd_status = 1; + Tuya.buffer[Tuya.byte_counter++] = serial_in_byte; + Tuya.cmd_checksum += serial_in_byte; + } + else if (Tuya.cmd_status == 1 && serial_in_byte == 0xAA) { // Only packtes with header 0x55AA are valid + Tuya.cmd_status = 2; + + Tuya.byte_counter = 0; + Tuya.buffer[Tuya.byte_counter++] = 0x55; + Tuya.buffer[Tuya.byte_counter++] = 0xAA; + Tuya.cmd_checksum = 0xFF; + } + else if (Tuya.cmd_status == 2) { + if (Tuya.byte_counter == 5) { // Get length of data + Tuya.cmd_status = 3; + Tuya.data_len = serial_in_byte; + } + Tuya.cmd_checksum += serial_in_byte; + Tuya.buffer[Tuya.byte_counter++] = serial_in_byte; + } + else if ((Tuya.cmd_status == 3) && (Tuya.byte_counter == (6 + Tuya.data_len)) && (Tuya.cmd_checksum == serial_in_byte)) { // Compare checksum and process packet + Tuya.buffer[Tuya.byte_counter++] = serial_in_byte; + + char hex_char[(Tuya.byte_counter * 2) + 2]; + Response_P(PSTR("{\"" D_JSON_TUYA_MCU_RECEIVED "\":\"%s\"}"), ToHex_P((unsigned char*)Tuya.buffer, Tuya.byte_counter, hex_char, sizeof(hex_char))); + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_TUYA_MCU_RECEIVED)); + XdrvRulesProcess(); + + TuyaPacketProcess(); + Tuya.byte_counter = 0; + Tuya.cmd_status = 0; + Tuya.cmd_checksum = 0; + Tuya.data_len = 0; + } // read additional packets from TUYA + else if (Tuya.byte_counter < TUYA_BUFFER_SIZE -1) { // add char to string if it still fits + Tuya.buffer[Tuya.byte_counter++] = serial_in_byte; + Tuya.cmd_checksum += serial_in_byte; + } else { + Tuya.byte_counter = 0; + Tuya.cmd_status = 0; + Tuya.cmd_checksum = 0; + Tuya.data_len = 0; + } + } +} + +bool TuyaButtonPressed(void) +{ + if (!XdrvMailbox.index && ((PRESSED == XdrvMailbox.payload) && (NOT_PRESSED == Button.last_state[XdrvMailbox.index]))) { + AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Reset GPIO triggered")); + TuyaResetWifi(); + return true; // Reset GPIO served here + } + return false; // Don't serve other buttons +} + +void TuyaSetWifiLed(void) +{ + uint8_t wifi_state = 0x02; + switch(WifiState()){ + case WIFI_MANAGER: + wifi_state = 0x01; + break; + case WIFI_RESTART: + wifi_state = 0x03; + break; + } + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Set WiFi LED %d (%d)"), wifi_state, WifiState()); + + TuyaSendCmd(TUYA_CMD_WIFI_STATE, &wifi_state, 1); +} + +#ifdef USE_ENERGY_SENSOR +/*********************************************************************************************\ + * Energy Interface +\*********************************************************************************************/ + +bool Xnrg16(uint8_t function) +{ + bool result = false; + + if (TUYA_DIMMER == my_module_type) { + if (FUNC_PRE_INIT == function) { + if (TuyaGetDpId(TUYA_MCU_FUNC_POWER) != 0) { + if (TuyaGetDpId(TUYA_MCU_FUNC_CURRENT) == 0) { + Energy.current_available = false; + } + if (TuyaGetDpId(TUYA_MCU_FUNC_VOLTAGE) == 0) { + Energy.voltage_available = false; + } + energy_flg = XNRG_16; + } + } + } + return result; +} +#endif // USE_ENERGY_SENSOR + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xdrv16(uint8_t function) +{ + bool result = false; + + if (TUYA_DIMMER == my_module_type) { + switch (function) { + case FUNC_LOOP: + if (TuyaSerial) { TuyaSerialInput(); } + break; + case FUNC_MODULE_INIT: + result = TuyaModuleSelected(); + break; + case FUNC_INIT: + TuyaInit(); + break; + case FUNC_SET_DEVICE_POWER: + result = TuyaSetPower(); + break; + case FUNC_BUTTON_PRESSED: + result = TuyaButtonPressed(); + break; + case FUNC_EVERY_SECOND: + if (TuyaSerial && Tuya.wifi_state != WifiState()) { TuyaSetWifiLed(); } + Tuya.heartbeat_timer++; + if (Tuya.heartbeat_timer > 10) { + Tuya.heartbeat_timer = 0; + TuyaSendCmd(TUYA_CMD_HEARTBEAT); + } + break; + case FUNC_SET_CHANNELS: + result = TuyaSetChannels(); + break; + case FUNC_COMMAND: + result = DecodeCommand(kTuyaCommand, TuyaCommand); + break; + } + } + return result; +} + +#endif // USE_TUYA_MCU +#endif // USE_LIGHT diff --git a/sonoff/xdrv_17_rcswitch.ino b/sonoff/xdrv_17_rcswitch.ino index 8018eaa2d..9d6d33660 100644 --- a/sonoff/xdrv_17_rcswitch.ino +++ b/sonoff/xdrv_17_rcswitch.ino @@ -32,6 +32,12 @@ #define D_JSON_RF_PULSE "Pulse" #define D_JSON_RF_REPEAT "Repeat" +const char kRfSendCommands[] PROGMEM = "|" // No prefix + D_CMND_RFSEND; + +void (* const RfSendCommand[])(void) PROGMEM = + { &CmndRfSend }; + #include RCSwitch mySwitch = RCSwitch(); @@ -61,7 +67,7 @@ void RfReceiveCheck(void) } else { snprintf_P(stemp, sizeof(stemp), PSTR("\"0x%lX\""), (uint32_t)data); } - Response_P(PSTR("{\"" D_JSON_RFRECEIVED "\":{\"" D_JSON_RF_DATA "\":%s,\"" D_JSON_RF_BITS "\":%d,\"" D_JSON_RF_PROTOCOL "\":%d,\"" D_JSON_RF_PULSE "\":%d}}"), + ResponseTime_P(PSTR(",\"" D_JSON_RFRECEIVED "\":{\"" D_JSON_RF_DATA "\":%s,\"" D_JSON_RF_BITS "\":%d,\"" D_JSON_RF_PROTOCOL "\":%d,\"" D_JSON_RF_PULSE "\":%d}}"), stemp, bits, protocol, delay); MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_RFRECEIVED)); XdrvRulesProcess(); @@ -87,78 +93,72 @@ void RfInit(void) * Commands \*********************************************************************************************/ -bool RfSendCommand(void) +void CmndRfSend(void) { - bool serviced = true; bool error = false; - if (!strcasecmp_P(XdrvMailbox.topic, PSTR(D_CMND_RFSEND))) { - if (XdrvMailbox.data_len) { - unsigned long data = 0; - unsigned int bits = 24; - int protocol = 1; - int repeat = 10; - int pulse = 350; + if (XdrvMailbox.data_len) { + unsigned long data = 0; + unsigned int bits = 24; + int protocol = 1; + int repeat = 10; + int pulse = 350; - char dataBufUc[XdrvMailbox.data_len]; - UpperCase(dataBufUc, XdrvMailbox.data); - StaticJsonBuffer<150> jsonBuf; // ArduinoJSON entry used to calculate jsonBuf: JSON_OBJECT_SIZE(5) + 40 = 134 - JsonObject &root = jsonBuf.parseObject(dataBufUc); - if (root.success()) { - // RFsend {"data":0x501014,"bits":24,"protocol":1,"repeat":10,"pulse":350} - char parm_uc[10]; - data = strtoul(root[UpperCase_P(parm_uc, PSTR(D_JSON_RF_DATA))], nullptr, 0); // Allow decimal (5246996) and hexadecimal (0x501014) input - bits = root[UpperCase_P(parm_uc, PSTR(D_JSON_RF_BITS))]; - protocol = root[UpperCase_P(parm_uc, PSTR(D_JSON_RF_PROTOCOL))]; - repeat = root[UpperCase_P(parm_uc, PSTR(D_JSON_RF_REPEAT))]; - pulse = root[UpperCase_P(parm_uc, PSTR(D_JSON_RF_PULSE))]; - } else { - // RFsend data, bits, protocol, repeat, pulse - char *p; - uint8_t i = 0; - for (char *str = strtok_r(XdrvMailbox.data, ", ", &p); str && i < 5; str = strtok_r(nullptr, ", ", &p)) { - switch (i++) { - case 0: - data = strtoul(str, nullptr, 0); // Allow decimal (5246996) and hexadecimal (0x501014) input - break; - case 1: - bits = atoi(str); - break; - case 2: - protocol = atoi(str); - break; - case 3: - repeat = atoi(str); - break; - case 4: - pulse = atoi(str); - } + char dataBufUc[XdrvMailbox.data_len]; + UpperCase(dataBufUc, XdrvMailbox.data); + StaticJsonBuffer<150> jsonBuf; // ArduinoJSON entry used to calculate jsonBuf: JSON_OBJECT_SIZE(5) + 40 = 134 + JsonObject &root = jsonBuf.parseObject(dataBufUc); + if (root.success()) { + // RFsend {"data":0x501014,"bits":24,"protocol":1,"repeat":10,"pulse":350} + char parm_uc[10]; + data = strtoul(root[UpperCase_P(parm_uc, PSTR(D_JSON_RF_DATA))], nullptr, 0); // Allow decimal (5246996) and hexadecimal (0x501014) input + bits = root[UpperCase_P(parm_uc, PSTR(D_JSON_RF_BITS))]; + protocol = root[UpperCase_P(parm_uc, PSTR(D_JSON_RF_PROTOCOL))]; + repeat = root[UpperCase_P(parm_uc, PSTR(D_JSON_RF_REPEAT))]; + pulse = root[UpperCase_P(parm_uc, PSTR(D_JSON_RF_PULSE))]; + } else { + // RFsend data, bits, protocol, repeat, pulse + char *p; + uint8_t i = 0; + for (char *str = strtok_r(XdrvMailbox.data, ", ", &p); str && i < 5; str = strtok_r(nullptr, ", ", &p)) { + switch (i++) { + case 0: + data = strtoul(str, nullptr, 0); // Allow decimal (5246996) and hexadecimal (0x501014) input + break; + case 1: + bits = atoi(str); + break; + case 2: + protocol = atoi(str); + break; + case 3: + repeat = atoi(str); + break; + case 4: + pulse = atoi(str); } } + } - if (!protocol) { protocol = 1; } - mySwitch.setProtocol(protocol); - if (!pulse) { pulse = 350; } // Default pulse length for protocol 1 - mySwitch.setPulseLength(pulse); - if (!repeat) { repeat = 10; } // Default at init - mySwitch.setRepeatTransmit(repeat); - if (!bits) { bits = 24; } // Default 24 bits - if (data) { - mySwitch.send(data, bits); - Response_P(PSTR("{\"" D_CMND_RFSEND "\":\"" D_JSON_DONE "\"}")); - } else { - error = true; - } + if (!protocol) { protocol = 1; } + mySwitch.setProtocol(protocol); + if (!pulse) { pulse = 350; } // Default pulse length for protocol 1 + mySwitch.setPulseLength(pulse); + if (!repeat) { repeat = 10; } // Default at init + mySwitch.setRepeatTransmit(repeat); + if (!bits) { bits = 24; } // Default 24 bits + if (data) { + mySwitch.send(data, bits); + ResponseCmndDone(); } else { error = true; } - if (error) { - Response_P(PSTR("{\"" D_CMND_RFSEND "\":\"" D_JSON_NO " " D_JSON_RF_DATA ", " D_JSON_RF_BITS ", " D_JSON_RF_PROTOCOL ", " D_JSON_RF_REPEAT " " D_JSON_OR " " D_JSON_RF_PULSE "\"}")); - } + } else { + error = true; + } + if (error) { + Response_P(PSTR("{\"" D_CMND_RFSEND "\":\"" D_JSON_NO " " D_JSON_RF_DATA ", " D_JSON_RF_BITS ", " D_JSON_RF_PROTOCOL ", " D_JSON_RF_REPEAT " " D_JSON_OR " " D_JSON_RF_PULSE "\"}")); } - else serviced = false; // Unknown command - - return serviced; } /*********************************************************************************************\ @@ -171,9 +171,6 @@ bool Xdrv17(uint8_t function) if ((pin[GPIO_RFSEND] < 99) || (pin[GPIO_RFRECV] < 99)) { switch (function) { - case FUNC_INIT: - RfInit(); - break; case FUNC_EVERY_50_MSECOND: if (pin[GPIO_RFRECV] < 99) { RfReceiveCheck(); @@ -181,9 +178,12 @@ bool Xdrv17(uint8_t function) break; case FUNC_COMMAND: if (pin[GPIO_RFSEND] < 99) { - result = RfSendCommand(); + result = DecodeCommand(kRfSendCommands, RfSendCommand); } break; + case FUNC_INIT: + RfInit(); + break; } } return result; diff --git a/sonoff/xdrv_18_armtronix_dimmers.ino b/sonoff/xdrv_18_armtronix_dimmers.ino index 4ce30f6b0..abf366c14 100644 --- a/sonoff/xdrv_18_armtronix_dimmers.ino +++ b/sonoff/xdrv_18_armtronix_dimmers.ino @@ -32,10 +32,12 @@ TasmotaSerial *ArmtronixSerial = nullptr; -bool armtronix_ignore_dim = false; // Flag to skip serial send to prevent looping when processing inbound states from the faceplate interaction -int8_t armtronix_wifi_state = -2; // Keep MCU wifi-status in sync with WifiState() -int8_t armtronix_dimState[2]; // Dimmer state values. -int8_t armtronix_knobState[2]; // Dimmer state values. +struct ARMTRONIX { + bool ignore_dim = false; // Flag to skip serial send to prevent looping when processing inbound states from the faceplate interaction + int8_t wifi_state = -2; // Keep MCU wifi-status in sync with WifiState() + int8_t dim_state[2]; // Dimmer state values. + int8_t knob_state[2]; // Dimmer state values. +} Armtronix; /*********************************************************************************************\ * Internal Functions @@ -49,21 +51,21 @@ bool ArmtronixSetChannels(void) void LightSerial2Duty(uint8_t duty1, uint8_t duty2) { - if (ArmtronixSerial && !armtronix_ignore_dim) { + if (ArmtronixSerial && !Armtronix.ignore_dim) { duty1 = ((float)duty1)/2.575757; //max 99 duty2 = ((float)duty2)/2.575757; //max 99 - armtronix_dimState[0] = duty1; - armtronix_dimState[1] = duty2; + Armtronix.dim_state[0] = duty1; + Armtronix.dim_state[1] = duty2; ArmtronixSerial->print("Dimmer1:"); ArmtronixSerial->print(duty1); ArmtronixSerial->print("\nDimmer2:"); ArmtronixSerial->println(duty2); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ARM: Send Serial Packet Dim Values=%d,%d"), armtronix_dimState[0],armtronix_dimState[1]); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ARM: Send Serial Packet Dim Values=%d,%d"), Armtronix.dim_state[0],Armtronix.dim_state[1]); } else { - armtronix_ignore_dim = false; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ARM: Send Dim Level skipped due to already set. Value=%d,%d"), armtronix_dimState[0],armtronix_dimState[1]); + Armtronix.ignore_dim = false; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ARM: Send Dim Level skipped due to already set. Value=%d,%d"), Armtronix.dim_state[0],Armtronix.dim_state[1]); } } @@ -84,16 +86,17 @@ void ArmtronixRequestState(void) bool ArmtronixModuleSelected(void) { + devices_present++; light_type = LT_SERIAL2; return true; } void ArmtronixInit(void) { - armtronix_dimState[0] = -1; - armtronix_dimState[1] = -1; - armtronix_knobState[0] = -1; - armtronix_knobState[1] = -1; + Armtronix.dim_state[0] = -1; + Armtronix.dim_state[1] = -1; + Armtronix.knob_state[0] = -1; + Armtronix.knob_state[1] = -1; ArmtronixSerial = new TasmotaSerial(pin[GPIO_RXD], pin[GPIO_TXD], 2); if (ArmtronixSerial->begin(115200)) { if (ArmtronixSerial->hardwareSerial()) { ClaimSerial(); } @@ -115,19 +118,19 @@ void ArmtronixSerialInput(void) commaIndex = 6; for (uint32_t i =0; i<2; i++) { newDimState[i] = answer.substring(commaIndex+1,answer.indexOf(',',commaIndex+1)).toInt(); - if (newDimState[i] != armtronix_dimState[i]) { + if (newDimState[i] != Armtronix.dim_state[i]) { temp = ((float)newDimState[i])*1.01010101010101; //max 255 - armtronix_dimState[i] = newDimState[i]; - armtronix_ignore_dim = true; + Armtronix.dim_state[i] = newDimState[i]; + Armtronix.ignore_dim = true; snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_CHANNEL "%d %d"),i+1, temp); ExecuteCommand(scmnd,SRC_SWITCH); AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ARM: Send CMND_CHANNEL=%s"), scmnd ); } commaIndex = answer.indexOf(',',commaIndex+1); } - armtronix_knobState[0] = answer.substring(commaIndex+1,answer.indexOf(',',commaIndex+1)).toInt(); + Armtronix.knob_state[0] = answer.substring(commaIndex+1,answer.indexOf(',',commaIndex+1)).toInt(); commaIndex = answer.indexOf(',',commaIndex+1); - armtronix_knobState[1] = answer.substring(commaIndex+1,answer.indexOf(',',commaIndex+1)).toInt(); + Armtronix.knob_state[1] = answer.substring(commaIndex+1,answer.indexOf(',',commaIndex+1)).toInt(); } } } @@ -137,11 +140,7 @@ void ArmtronixSetWifiLed(void) uint8_t wifi_state = 0x02; switch (WifiState()) { - case WIFI_SMARTCONFIG: - wifi_state = 0x00; - break; case WIFI_MANAGER: - case WIFI_WPSCONFIG: wifi_state = 0x01; break; case WIFI_RESTART: @@ -158,7 +157,7 @@ void ArmtronixSetWifiLed(void) state = '0' + ((wifi_state & 2) > 0); ArmtronixSerial->write(state); ArmtronixSerial->write(10); - armtronix_wifi_state = WifiState(); + Armtronix.wifi_state = WifiState(); } /*********************************************************************************************\ @@ -182,7 +181,7 @@ bool Xdrv18(uint8_t function) break; case FUNC_EVERY_SECOND: if (ArmtronixSerial) { - if (armtronix_wifi_state!=WifiState()) { ArmtronixSetWifiLed(); } + if (Armtronix.wifi_state!=WifiState()) { ArmtronixSetWifiLed(); } if (uptime &1) { ArmtronixSerial->println("Status"); } diff --git a/sonoff/xdrv_19_ps16dz_dimmer.ino b/sonoff/xdrv_19_ps16dz_dimmer.ino index 928534926..80c32ba47 100644 --- a/sonoff/xdrv_19_ps16dz_dimmer.ino +++ b/sonoff/xdrv_19_ps16dz_dimmer.ino @@ -1,5 +1,5 @@ /* - xdrv_19_ps16dz_dimmer.ino - PS_16_DZ dimmer and Sonoff L1 support for Sonoff-Tasmota + xdrv_19_ps16dz.dimmer.ino - PS_16_DZ dimmer support for Sonoff-Tasmota Copyright (C) 2019 Joel Stein and Theo Arends @@ -20,135 +20,64 @@ #ifdef USE_LIGHT #ifdef USE_PS_16_DZ /*********************************************************************************************\ - * PS 16 DZ Serial Dimmer and Sonoff L1 + * PS 16 DZ Serial Dimmer \*********************************************************************************************/ #define XDRV_19 19 -#define PS16DZ_BUFFER_SIZE 140 - -#define PS16DZ_SONOFF_L1_MODE_COLORFUL 1 // Colorful (static color) -#define PS16DZ_SONOFF_L1_MODE_COLORFUL_GRADIENT 2 // Colorful Gradient -#define PS16DZ_SONOFF_L1_MODE_COLORFUL_BREATH 3 // Colorful Breath -#define PS16DZ_SONOFF_L1_MODE_DIY_GRADIENT 4 // DIY Gradient (fade in and out) [Speed 1- 100, color] -#define PS16DZ_SONOFF_L1_MODE_DIY_PULSE 5 // DIY Pulse (faster fade in and out) [Speed 1- 100, color] -#define PS16DZ_SONOFF_L1_MODE_DIY_BREATH 6 // DIY Breath (toggle on/off) [Speed 1- 100, color] -#define PS16DZ_SONOFF_L1_MODE_DIY_STROBE 7 // DIY Strobe (faster toggle on/off) [Speed 1- 100, color] -#define PS16DZ_SONOFF_L1_MODE_RGB_GRADIENT 8 // RGB Gradient -#define PS16DZ_SONOFF_L1_MODE_RGB_PULSE 9 // RGB Pulse -#define PS16DZ_SONOFF_L1_MODE_RGB_BREATH 10 // RGB Breath -#define PS16DZ_SONOFF_L1_MODE_RGB_STROBE 11 // RGB strobe -#define PS16DZ_SONOFF_L1_MODE_SYNC_TO_MUSIC 12 // Sync to music [Speed 1- 100, sensitivity 1 - 10] +#define PS16DZ_BUFFER_SIZE 80 #include TasmotaSerial *PS16DZSerial = nullptr; -char *ps16dz_tx_buffer = nullptr; // Serial transmit buffer -char *ps16dz_rx_buffer = nullptr; // Serial receive buffer -int ps16dz_byte_counter = 0; -uint8_t ps16dz_color[3]; // Most recent serial sent/received values -uint8_t ps16dz_dimmer = 0; -bool ps16dz_supports_color = false; -bool ps16dz_switch = false; +struct PS16DZ { + char *rx_buffer = nullptr; // Serial receive buffer + int byte_counter = 0; + uint8_t dimmer = 0; +} Ps16dz; /*********************************************************************************************\ * Internal Functions \*********************************************************************************************/ -void PS16DZSerialSendTxBuffer(void) +void PS16DZSerialSend(const char *tx_buffer) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PSZ: Send %s"), ps16dz_tx_buffer); +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PSZ: Send %s"), tx_buffer); - PS16DZSerial->print(ps16dz_tx_buffer); + PS16DZSerial->print(tx_buffer); PS16DZSerial->write(0x1B); PS16DZSerial->flush(); } -void PS16DZSerialSendOkCommand(void) +void PS16DZSerialSendOk(void) { - snprintf_P(ps16dz_tx_buffer, PS16DZ_BUFFER_SIZE, PSTR("AT+SEND=ok")); - PS16DZSerialSendTxBuffer(); + char tx_buffer[16]; + snprintf_P(tx_buffer, sizeof(tx_buffer), PSTR("AT+SEND=ok")); + PS16DZSerialSend(tx_buffer); } // Send a serial update command to the LED controller // For dimmer types: // AT+UPDATE="sequence":"1554682835320","switch":"on","bright":100 -// For color types: -// AT+UPDATE="sequence":"1554682835320","switch":"on","bright":100,"mode":1,"colorR":255,"colorG":46,"colorB":101,"light_types":1 void PS16DZSerialSendUpdateCommand(void) { uint8_t light_state_dimmer = light_state.getDimmer(); // Dimming acts odd below 10% - this mirrors the threshold set on the faceplate itself - light_state_dimmer = (light_state_dimmer < 10) ? 10 : light_state_dimmer; + light_state_dimmer = (light_state_dimmer < Settings.dimmer_hw_min) ? Settings.dimmer_hw_min : light_state_dimmer; + light_state_dimmer = (light_state_dimmer > Settings.dimmer_hw_max) ? Settings.dimmer_hw_max : light_state_dimmer; - snprintf_P(ps16dz_tx_buffer, PS16DZ_BUFFER_SIZE, PSTR("AT+UPDATE=\"sequence\":\"%d%03d\",\"switch\":\"%s\",\"bright\":%d"), + char tx_buffer[80]; + snprintf_P(tx_buffer, sizeof(tx_buffer), PSTR("AT+UPDATE=\"sequence\":\"%d%03d\",\"switch\":\"%s\",\"bright\":%d"), LocalTime(), millis()%1000, power?"on":"off", light_state_dimmer); - if (ps16dz_supports_color) { - uint8_t light_state_rgb[3]; - light_state.getRGB(&light_state_rgb[0], &light_state_rgb[1], &light_state_rgb[2]); - - snprintf_P(ps16dz_tx_buffer, PS16DZ_BUFFER_SIZE, PSTR("%s,\"mode\":%d,\"colorR\":%d,\"colorG\":%d,\"colorB\":%d,\"light_types\":1"), - ps16dz_tx_buffer, PS16DZ_SONOFF_L1_MODE_COLORFUL, light_state_rgb[0], light_state_rgb[1], light_state_rgb[2]); - } - PS16DZSerialSendTxBuffer(); + PS16DZSerialSend(tx_buffer); } /*********************************************************************************************\ * API Functions \*********************************************************************************************/ -bool PS16DZSerialSendUpdateCommandIfRequired(void) -{ - if (!PS16DZSerial) { return true; } - - bool is_switch_change = (XdrvMailbox.payload != SRC_SWITCH); - bool is_brightness_change = (light_state.getDimmer() != ps16dz_dimmer); - - uint8_t light_state_rgb[3]; - light_state.getRGB(&light_state_rgb[0], &light_state_rgb[1], &light_state_rgb[2]); - bool is_color_change = (ps16dz_supports_color && (memcmp(light_state_rgb, ps16dz_color, 3) != 0)); - - if (is_switch_change || is_brightness_change || is_color_change) { - PS16DZSerialSendUpdateCommand(); - } - - return true; -} - -bool PS16DZModuleSelected(void) -{ - switch (my_module_type) - { - case PS_16_DZ: - light_type = LT_SERIAL1; - break; - - case SONOFF_L1: - light_type = LT_PWM3; - break; - } - - return true; -} - -void PS16DZInit(void) -{ - ps16dz_supports_color = (light_state.getColorMode() == LCM_RGB); - - ps16dz_tx_buffer = (char*)(malloc(PS16DZ_BUFFER_SIZE)); - if (ps16dz_tx_buffer != nullptr) { - ps16dz_rx_buffer = (char*)(malloc(PS16DZ_BUFFER_SIZE)); - if (ps16dz_rx_buffer != nullptr) { - PS16DZSerial = new TasmotaSerial(pin[GPIO_RXD], pin[GPIO_TXD], 2); - if (PS16DZSerial->begin(19200)) { - if (PS16DZSerial->hardwareSerial()) { ClaimSerial(); } - } - } - } -} - void PS16DZSerialInput(void) { char scmnd[20]; @@ -156,27 +85,29 @@ void PS16DZSerialInput(void) yield(); uint8_t serial_in_byte = PS16DZSerial->read(); if (serial_in_byte != 0x1B) { - if (ps16dz_byte_counter >= PS16DZ_BUFFER_SIZE - 1) { - memset(ps16dz_rx_buffer, 0, PS16DZ_BUFFER_SIZE); - ps16dz_byte_counter = 0; + if (Ps16dz.byte_counter >= PS16DZ_BUFFER_SIZE - 1) { + memset(Ps16dz.rx_buffer, 0, PS16DZ_BUFFER_SIZE); + Ps16dz.byte_counter = 0; } - if (ps16dz_byte_counter || (!ps16dz_byte_counter && ('A' == serial_in_byte))) { - ps16dz_rx_buffer[ps16dz_byte_counter++] = serial_in_byte; + if (Ps16dz.byte_counter || (!Ps16dz.byte_counter && ('A' == serial_in_byte))) { + Ps16dz.rx_buffer[Ps16dz.byte_counter++] = serial_in_byte; } } else { - ps16dz_rx_buffer[ps16dz_byte_counter++] = 0x00; + Ps16dz.rx_buffer[Ps16dz.byte_counter++] = 0x00; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PSZ: Received %s"), ps16dz_rx_buffer); + // AT+RESULT="sequence":"1554682835320" +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PSZ: Rcvd %s"), Ps16dz.rx_buffer); - if (!strncmp(ps16dz_rx_buffer+3, "UPDATE", 6)) { + if (!strncmp(Ps16dz.rx_buffer+3, "RESULT", 6)) { + + } + else if (!strncmp(Ps16dz.rx_buffer+3, "UPDATE", 6)) { + // AT+UPDATE="switch":"on","bright":100 char *end_str; - char *string = ps16dz_rx_buffer+10; + char *string = Ps16dz.rx_buffer+10; char *token = strtok_r(string, ",", &end_str); - bool color_updated[3] = { false, false, false }; - memcpy(ps16dz_color, Settings.light_color, 3); bool is_switch_change = false; - bool is_color_change = false; bool is_brightness_change = false; while (token != nullptr) { @@ -185,83 +116,92 @@ void PS16DZSerialInput(void) char* token3 = strtok_r(nullptr, ":", &end_token); if (!strncmp(token2, "\"switch\"", 8)) { - ps16dz_switch = !strncmp(token3, "\"on\"", 4) ? true : false; + bool switch_state = !strncmp(token3, "\"on\"", 4) ? true : false; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PSZ: Switch %d"), ps16dz_switch); +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PSZ: Switch %d"), switch_state); - is_switch_change = (ps16dz_switch != power); + is_switch_change = (switch_state != power); if (is_switch_change) { - ExecuteCommandPower(1, ps16dz_switch, SRC_SWITCH); // send SRC_SWITCH? to use as flag to prevent loop from inbound states from faceplate interaction - } - } - else if (!strncmp(token2, "\"color", 6)) { - - char color_channel_name = token2[6]; - int color_index; - switch(color_channel_name) - { - case 'R': color_index = 0; - break; - case 'G': color_index = 1; - break; - case 'B': color_index = 2; - break; - } - int color_value = atoi(token3); - ps16dz_color[color_index] = color_value; - color_updated[color_index] = true; - - bool all_color_channels_updated = color_updated[0] && color_updated[1] && color_updated[2]; - if (all_color_channels_updated) { - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PSZ: Color R:%d, G:%d, B:%d"), ps16dz_color[0], ps16dz_color[1], ps16dz_color[2]); - - is_color_change = (memcmp(ps16dz_color, Settings.light_color, 3) != 0); - } - - if (power && is_color_change) { - snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_COLOR "2 %02x%02x%02x"), ps16dz_color[0], ps16dz_color[1], ps16dz_color[2]); - ExecuteCommand(scmnd, SRC_SWITCH); + ExecuteCommandPower(1, switch_state, SRC_SWITCH); // send SRC_SWITCH? to use as flag to prevent loop from inbound states from faceplate interaction } } else if (!strncmp(token2, "\"bright\"", 8)) { - ps16dz_dimmer = atoi(token3); + Ps16dz.dimmer = atoi(token3); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PSZ: Brightness %d"), ps16dz_dimmer); +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PSZ: Brightness %d"), Ps16dz.dimmer); - is_brightness_change = ps16dz_dimmer != Settings.light_dimmer; - if (power && (ps16dz_dimmer > 0) && is_brightness_change) { - snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_DIMMER " %d"), ps16dz_dimmer); + is_brightness_change = Ps16dz.dimmer != Settings.light_dimmer; + if (power && (Ps16dz.dimmer > 0) && is_brightness_change) { + snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_DIMMER " %d"), Ps16dz.dimmer); ExecuteCommand(scmnd, SRC_SWITCH); } } else if (!strncmp(token2, "\"sequence\"", 10)) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PSZ: Sequence %s"), token3); +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PSZ: Sequence %s"), token3); } token = strtok_r(nullptr, ",", &end_str); } - if (!is_color_change && !is_brightness_change) { + if (!is_brightness_change) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PSZ: Update")); +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PSZ: Update")); - PS16DZSerialSendOkCommand(); + PS16DZSerialSendOk(); } } - else if (!strncmp(ps16dz_rx_buffer+3, "SETTING", 7)) { + else if (!strncmp(Ps16dz.rx_buffer+3, "SETTING", 7)) { + // AT+SETTING=enterESPTOUCH - When ON button is held for over 5 seconds + // AT+SETTING=exitESPTOUCH - When ON button is pressed if (!Settings.flag.button_restrict) { - snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_WIFICONFIG " 2")); - ExecuteCommand(scmnd, SRC_BUTTON); + int state = WIFI_MANAGER; + if (!strncmp(Ps16dz.rx_buffer+10, "=exit", 5)) { state = WIFI_RETRY; } + if (state != Settings.sta_config) { + snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_WIFICONFIG " %d"), state); + ExecuteCommand(scmnd, SRC_BUTTON); + } } } - memset(ps16dz_rx_buffer, 0, PS16DZ_BUFFER_SIZE); - ps16dz_byte_counter = 0; + memset(Ps16dz.rx_buffer, 0, PS16DZ_BUFFER_SIZE); + Ps16dz.byte_counter = 0; } } } +bool PS16DZSerialSendUpdateCommandIfRequired(void) +{ + if (!PS16DZSerial) { return true; } + + bool is_switch_change = (XdrvMailbox.payload != SRC_SWITCH); + bool is_brightness_change = (light_state.getDimmer() != Ps16dz.dimmer); + + if (is_switch_change || is_brightness_change) { + PS16DZSerialSendUpdateCommand(); + } + + return true; +} + +void PS16DZInit(void) +{ + Ps16dz.rx_buffer = (char*)(malloc(PS16DZ_BUFFER_SIZE)); + if (Ps16dz.rx_buffer != nullptr) { + PS16DZSerial = new TasmotaSerial(pin[GPIO_RXD], pin[GPIO_TXD], 2); + if (PS16DZSerial->begin(19200)) { + if (PS16DZSerial->hardwareSerial()) { ClaimSerial(); } + } + } +} + +bool PS16DZModuleSelected(void) +{ + devices_present++; + light_type = LT_SERIAL1; + + return true; +} + /*********************************************************************************************\ * Interface \*********************************************************************************************/ @@ -270,20 +210,20 @@ bool Xdrv19(uint8_t function) { bool result = false; - if ((PS_16_DZ == my_module_type) || (SONOFF_L1 == my_module_type)) { + if (PS_16_DZ == my_module_type) { switch (function) { case FUNC_LOOP: if (PS16DZSerial) { PS16DZSerialInput(); } break; - case FUNC_MODULE_INIT: - result = PS16DZModuleSelected(); + case FUNC_SET_DEVICE_POWER: + case FUNC_SET_CHANNELS: + result = PS16DZSerialSendUpdateCommandIfRequired(); break; case FUNC_INIT: PS16DZInit(); break; - case FUNC_SET_DEVICE_POWER: - case FUNC_SET_CHANNELS: - result = PS16DZSerialSendUpdateCommandIfRequired(); + case FUNC_MODULE_INIT: + result = PS16DZModuleSelected(); break; } } diff --git a/sonoff/xdrv_20_hue.ino b/sonoff/xdrv_20_hue.ino index f9546e6b5..cd41a30ef 100644 --- a/sonoff/xdrv_20_hue.ino +++ b/sonoff/xdrv_20_hue.ino @@ -36,7 +36,7 @@ const char HUE_RESPONSE[] PROGMEM = "CACHE-CONTROL: max-age=100\r\n" "EXT:\r\n" "LOCATION: http://%s:80/description.xml\r\n" - "SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/1.17.0\r\n" + "SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/1.24.0\r\n" // was 1.17 "hue-bridgeid: %s\r\n"; const char HUE_ST1[] PROGMEM = "ST: upnp:rootdevice\r\n" @@ -243,6 +243,22 @@ uint16_t prev_ct = 254; char prev_x_str[24] = "\0"; // store previously set xy by Alexa app char prev_y_str[24] = "\0"; +uint8_t getLocalLightSubtype(uint8_t device) { + if (light_type) { + if (device >= Light.device) { + if (Settings.flag3.pwm_multi_channels) { + return LST_SINGLE; // If SetOption68, each channel acts like a dimmer + } else { + return Light.subtype; // the actual light + } + } else { + return LST_NONE; // relays + } + } else { + return LST_NONE; + } +} + void HueLightStatus1(uint8_t device, String *response) { uint16_t ct = 0; @@ -251,14 +267,24 @@ void HueLightStatus1(uint8_t device, String *response) uint16_t hue = 0; uint8_t sat = 0; uint8_t bri = 254; + uint32_t echo_gen = findEchoGeneration(); // 1 for 1st gen =+ Echo Dot 2nd gen, 2 for 2nd gen and above + // local_light_subtype simulates the Light.subtype for 'device' + // For relays LST_NONE, for dimmers LST_SINGLE + uint8_t local_light_subtype = getLocalLightSubtype(device); + bri = LightGetBri(device); // get Dimmer corrected with SetOption68 + if (bri > 254) bri = 254; // Philips Hue bri is between 1 and 254 + if (bri < 1) bri = 1; + +#ifdef USE_SHUTTER + if (ShutterState(device)) { + bri = (float)(Settings.shutter_invert[device-1] ? 100 - Settings.shutter_position[device-1] : Settings.shutter_position[device-1]) / 100; + } +#endif if (light_type) { light_state.getHSB(&hue, &sat, nullptr); - bri = light_state.getBri(); - if (bri > 254) bri = 254; // Philips Hue bri is between 1 and 254 - if (bri < 1) bri = 1; if ((bri > prev_bri ? bri - prev_bri : prev_bri - bri) < 1) bri = prev_bri; @@ -293,17 +319,17 @@ void HueLightStatus1(uint8_t device, String *response) *response += FPSTR(HUE_LIGHTS_STATUS_JSON1); response->replace("{state}", (power & (1 << (device-1))) ? "true" : "false"); // Brightness for all devices with PWM - //if (LST_SINGLE <= light_subtype) { - light_status += "\"bri\":"; - light_status += String(bri); - light_status += ","; - //} - if (LST_COLDWARM <= light_subtype) { + if ((1 == echo_gen) || (LST_SINGLE <= local_light_subtype)) { // force dimmer for 1st gen Echo + light_status += "\"bri\":"; + light_status += String(bri); + light_status += ","; + } + if (LST_COLDWARM <= local_light_subtype) { light_status += F("\"colormode\":\""); light_status += (g_gotct ? "ct" : "hs"); light_status += "\","; } - if (LST_RGB <= light_subtype) { // colors + if (LST_RGB <= local_light_subtype) { // colors if (prev_x_str[0] && prev_y_str[0]) { light_status += "\"xy\":["; light_status += prev_x_str; @@ -327,10 +353,10 @@ void HueLightStatus1(uint8_t device, String *response) light_status += String(sat); light_status += ","; } - if (LST_COLDWARM == light_subtype || LST_RGBW <= light_subtype) { // white temp + if (LST_COLDWARM == local_light_subtype || LST_RGBW <= local_light_subtype) { // white temp light_status += "\"ct\":"; - light_status += String(ct > 0 ? ct : 284); - light_status += ","; // if no ct, default to medium white + light_status += String(ct > 0 ? ct : 284); // if no ct, default to medium white + light_status += ","; } response->replace("{light_status}", light_status); } @@ -338,29 +364,88 @@ void HueLightStatus1(uint8_t device, String *response) void HueLightStatus2(uint8_t device, String *response) { *response += FPSTR(HUE_LIGHTS_STATUS_JSON2); - response->replace("{j1", Settings.friendlyname[device-1]); + if (device <= MAX_FRIENDLYNAMES) { + response->replace("{j1", Settings.friendlyname[device-1]); + } else { + char fname[33]; + strcpy(fname, Settings.friendlyname[MAX_FRIENDLYNAMES-1]); + uint32_t fname_len = strlen(fname); + if (fname_len > 30) { fname_len = 30; } + fname[fname_len++] = '-'; + if (device - MAX_FRIENDLYNAMES < 10) { + fname[fname_len++] = '0' + device - MAX_FRIENDLYNAMES; + } else { + fname[fname_len++] = 'A' + device - MAX_FRIENDLYNAMES - 10; + } + fname[fname_len] = 0x00; + + response->replace("{j1", fname); + } response->replace("{j2", GetHueDeviceId(device)); } // generate a unique lightId mixing local IP address and device number -// it is limited to 16 devices. -// last 24 bits of Mac address + 4 bits of local light -uint32_t EncodeLightId(uint8_t idx) +// it is limited to 32 devices. +// last 24 bits of Mac address + 4 bits of local light + high bit for relays 16-31, relay 32 is mapped to 0 +uint32_t EncodeLightId(uint8_t relay_id) { uint8_t mac[6]; WiFi.macAddress(mac); - uint32_t id = (mac[3] << 20) | (mac[4] << 12) | (mac[5] << 4) | (idx & 0xF); + uint32_t id = 0; + + if (relay_id >= 32) { // for Relay #32, we encode as 0 + relay_id = 0; + } + if (relay_id > 15) { + id = (1 << 28); + } + + id |= (mac[3] << 20) | (mac[4] << 12) | (mac[5] << 4) | (relay_id & 0xF); return id; } -uint32_t DecodeLightId(uint32_t id) { - return id & 0xF; +// get hue_id and decode the relay_id +// 4 LSB decode to 1-15, if bit 28 is set, it encodes 16-31, if 0 then 32 +uint32_t DecodeLightId(uint32_t hue_id) { + uint8_t relay_id = hue_id & 0xF; + if (hue_id & (1 << 28)) { // check if bit 25 is set, if so we have + relay_id += 16; + } + if (0 == relay_id) { // special value 0 is actually relay #32 + relay_id = 32; + } + return relay_id; +} + +static const char * FIRST_GEN_UA[] = { // list of User-Agents signature + "AEOBC", // Echo Dot 2ng Generation +}; + +// Check if the Echo device is of 1st generation, which triggers different results +uint32_t findEchoGeneration(void) { + // result is 1 for 1st gen, 2 for 2nd gen and further + String user_agent = WebServer->header("User-Agent"); + uint32_t gen = 2; + + for (uint32_t i = 0; i < sizeof(FIRST_GEN_UA)/sizeof(char*); i++) { + if (user_agent.indexOf(FIRST_GEN_UA[i]) >= 0) { // found + gen = 1; + break; + } + } + if (0 == user_agent.length()) { + gen = 1; // if no user-agent, also revert to gen v1 + } + + AddLog_P2(LOG_LEVEL_DEBUG_MORE, D_LOG_HTTP D_HUE " User-Agent: %s, gen=%d", user_agent.c_str(), gen); // Header collection is set in xdrv_01_webserver.ino, in StartWebserver() + + return gen; } void HueGlobalConfig(String *path) { String response; - uint8_t maxhue = (devices_present > MAX_FRIENDLYNAMES) ? MAX_FRIENDLYNAMES : devices_present; + uint8_t maxhue = (devices_present > MAX_HUE_DEVICES) ? MAX_HUE_DEVICES : devices_present; path->remove(0,1); // cut leading / to get response = F("{\"lights\":{\""); @@ -403,7 +488,8 @@ void HueLights(String *path) bool on = false; bool change = false; // need to change a parameter to the light uint8_t device = 1; - uint8_t maxhue = (devices_present > MAX_FRIENDLYNAMES) ? MAX_FRIENDLYNAMES : devices_present; + uint8_t local_light_subtype = Light.subtype; + uint8_t maxhue = (devices_present > MAX_HUE_DEVICES) ? MAX_HUE_DEVICES : devices_present; path->remove(0,path->indexOf("/lights")); // Remove until /lights if (path->endsWith("/lights")) { // Got /lights @@ -417,15 +503,27 @@ void HueLights(String *path) response += ",\""; } } +#ifdef USE_SCRIPT_HUE + Script_Check_Hue(&response); +#endif response += "}"; } else if (path->endsWith("/state")) { // Got ID/state path->remove(0,8); // Remove /lights/ path->remove(path->indexOf("/state")); // Remove /state device = DecodeLightId(atoi(path->c_str())); + +#ifdef USE_SCRIPT_HUE + if (device>devices_present) { + return Script_Handle_Hue(path); + } +#endif + if ((device < 1) || (device > maxhue)) { device = 1; } + local_light_subtype = getLocalLightSubtype(device); // get the subtype for this device + if (WebServer->args()) { response = "["; @@ -437,29 +535,46 @@ void HueLights(String *path) response.replace("{id", String(EncodeLightId(device))); response.replace("{cm", "on"); - on = hue_json["on"]; - switch(on) - { - case false : ExecuteCommandPower(device, POWER_OFF, SRC_HUE); - response.replace("{re", "false"); - break; - case true : ExecuteCommandPower(device, POWER_ON, SRC_HUE); - response.replace("{re", "true"); - break; - default : response.replace("{re", (power & (1 << (device-1))) ? "true" : "false"); - break; +#ifdef USE_SHUTTER + if (ShutterState(device)) { + if (!change) { + on = hue_json["on"]; + bri = on ? 1.0f : 0.0f; // when bri is not part of this request then calculate it + change = true; + } + response.replace("{re", on ? "true" : "false"); + } else { +#endif + on = hue_json["on"]; + switch(on) + { + case false : ExecuteCommandPower(device, POWER_OFF, SRC_HUE); + response.replace("{re", "false"); + break; + case true : ExecuteCommandPower(device, POWER_ON, SRC_HUE); + response.replace("{re", "true"); + break; + default : response.replace("{re", (power & (1 << (device-1))) ? "true" : "false"); + break; + } + resp = true; +#ifdef USE_SHUTTER } - resp = true; +#endif // USE_SHUTTER } - if (light_type) { - light_state.getHSB(&hue, &sat, nullptr); - bri = light_state.getBri(); // get the combined bri for CT and RGB, not only the RGB one - ct = light_state.getCT(); - uint8_t color_mode = light_state.getColorMode(); - if (LCM_RGB == color_mode) { g_gotct = false; } - if (LCM_CT == color_mode) { g_gotct = true; } - // If LCM_BOTH == color_mode, leave g_gotct unchanged + if (light_type && (local_light_subtype >= LST_SINGLE)) { + if (!Settings.flag3.pwm_multi_channels) { + light_state.getHSB(&hue, &sat, nullptr); + bri = light_state.getBri(); // get the combined bri for CT and RGB, not only the RGB one + ct = light_state.getCT(); + uint8_t color_mode = light_state.getColorMode(); + if (LCM_RGB == color_mode) { g_gotct = false; } + if (LCM_CT == color_mode) { g_gotct = true; } + // If LCM_BOTH == color_mode, leave g_gotct unchanged + } else { // treat each channel as simple dimmer + bri = LightGetBri(device); + } } prev_x_str[0] = prev_y_str[0] = 0; // reset xy string @@ -473,7 +588,7 @@ void HueLights(String *path) response.replace("{id", String(device)); response.replace("{cm", "bri"); response.replace("{re", String(tmp)); - if (LST_SINGLE <= light_subtype) { + if (LST_SINGLE <= Light.subtype) { change = true; } resp = true; @@ -514,7 +629,7 @@ void HueLights(String *path) response.replace("{id", String(device)); response.replace("{cm", "hue"); response.replace("{re", String(tmp)); - if (LST_RGB <= light_subtype) { + if (LST_RGB <= Light.subtype) { g_gotct = false; change = true; } @@ -530,7 +645,7 @@ void HueLights(String *path) response.replace("{id", String(device)); response.replace("{cm", "sat"); response.replace("{re", String(tmp)); - if (LST_RGB <= light_subtype) { + if (LST_RGB <= Light.subtype) { g_gotct = false; change = true; } @@ -544,25 +659,36 @@ void HueLights(String *path) response.replace("{id", String(device)); response.replace("{cm", "ct"); response.replace("{re", String(ct)); - if ((LST_COLDWARM == light_subtype) || (LST_RGBW <= light_subtype)) { + if ((LST_COLDWARM == Light.subtype) || (LST_RGBW <= Light.subtype)) { g_gotct = true; change = true; } resp = true; } if (change) { - if (light_type) { - if (g_gotct) { - light_controller.changeCTB(ct, bri); - } else { - light_controller.changeHSB(hue, sat, bri); +#ifdef USE_SHUTTER + if (ShutterState(device)) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Settings.shutter_invert: %d"), Settings.shutter_invert[device-1]); + ShutterSetPosition(device, bri * 100.0f ); + } else +#endif + if (light_type && (local_light_subtype > LST_NONE)) { // not relay + if (!Settings.flag3.pwm_multi_channels) { + if (g_gotct) { + light_controller.changeCTB(ct, bri); + } else { + light_controller.changeHSB(hue, sat, bri); + } + LightPreparePower(); + } else { // SetOption68 On, each channel is a dimmer + LightSetBri(device, bri); } - LightPreparePower(); - if (LST_COLDWARM <= light_subtype) { + if (LST_COLDWARM <= local_light_subtype) { MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_COLOR)); } else { MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_DIMMER)); } + XdrvRulesProcess(); } change = false; } @@ -576,8 +702,17 @@ void HueLights(String *path) } } else if(path->indexOf("/lights/") >= 0) { // Got /lights/ID + AddLog_P2(LOG_LEVEL_DEBUG_MORE, "/lights path=%s", path->c_str()); path->remove(0,8); // Remove /lights/ device = DecodeLightId(atoi(path->c_str())); + +#ifdef USE_SCRIPT_HUE + if (device>devices_present) { + Script_HueStatus(&response,device-devices_present-1); + goto exit; +} +#endif + if ((device < 1) || (device > maxhue)) { device = 1; } @@ -589,6 +724,7 @@ void HueLights(String *path) response = "{}"; code = 406; } + exit: AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE " Result (%s)"), response.c_str()); WSSend(code, CT_JSON, response); } @@ -599,7 +735,7 @@ void HueGroups(String *path) * http://sonoff/api/username/groups?1={"name":"Woonkamer","lights":[],"type":"Room","class":"Living room"}) */ String response = "{}"; - uint8_t maxhue = (devices_present > MAX_FRIENDLYNAMES) ? MAX_FRIENDLYNAMES : devices_present; + uint8_t maxhue = (devices_present > MAX_HUE_DEVICES) ? MAX_HUE_DEVICES : devices_present; if (path->endsWith("/0")) { response = FPSTR(HUE_GROUP0_STATUS_JSON); @@ -651,6 +787,7 @@ void HandleHueApi(String *path) else if (path->endsWith("/sensors")) HueNotImplemented(path); else if (path->endsWith("/scenes")) HueNotImplemented(path); else if (path->endsWith("/rules")) HueNotImplemented(path); + else if (path->endsWith("/resourcelinks")) HueNotImplemented(path); else HueGlobalConfig(path); } @@ -662,7 +799,11 @@ bool Xdrv20(uint8_t function) { bool result = false; +#ifdef USE_SCRIPT_HUE + if ((EMUL_HUE == Settings.flag2.emulation)) { +#else if (devices_present && (EMUL_HUE == Settings.flag2.emulation)) { +#endif switch (function) { case FUNC_WEB_ADD_HANDLER: WebServer->on("/description.xml", HandleUpnpSetupHue); diff --git a/sonoff/xdrv_21_wemo.ino b/sonoff/xdrv_21_wemo.ino index 6d5a41715..4858cd1f5 100644 --- a/sonoff/xdrv_21_wemo.ino +++ b/sonoff/xdrv_21_wemo.ino @@ -62,7 +62,7 @@ void WemoRespondToMSearch(int echo_type) if (PortUdp.beginPacket(udp_remote_ip, udp_remote_port)) { char type[24]; if (1 == echo_type) { // type1 echo 1g & dot 2g - strcpy_P(type, URN_BELKIN_DEVICE); + strcpy_P(type, URN_BELKIN_DEVICE_CAP); } else { // type2 echo 2g (echo, plus, show) strcpy_P(type, UPNP_ROOTDEVICE); } diff --git a/sonoff/xdrv_22_sonoff_ifan.ino b/sonoff/xdrv_22_sonoff_ifan.ino new file mode 100644 index 000000000..490eb9c72 --- /dev/null +++ b/sonoff/xdrv_22_sonoff_ifan.ino @@ -0,0 +1,272 @@ +/* + xdrv_22_sonoff_ifan.ino - sonoff iFan02 and iFan03 support for Sonoff-Tasmota + + Copyright (C) 2019 Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef USE_SONOFF_IFAN +/*********************************************************************************************\ + Sonoff iFan02 and iFan03 +\*********************************************************************************************/ + +#define XDRV_22 22 + +const uint8_t MAX_FAN_SPEED = 4; // Max number of iFan02 fan speeds (0 .. 3) + +const uint8_t kIFan02Speed[MAX_FAN_SPEED] = { 0x00, 0x01, 0x03, 0x05 }; +const uint8_t kIFan03Speed[MAX_FAN_SPEED +2] = { 0x00, 0x01, 0x03, 0x04, 0x05, 0x06 }; +const uint8_t kIFan03Sequence[MAX_FAN_SPEED][MAX_FAN_SPEED] = {{0, 2, 2, 2}, {0, 1, 2, 4}, {1, 1, 2, 5}, {4, 4, 5, 3}}; + +const char kSonoffIfanCommands[] PROGMEM = "|" // No prefix + D_CMND_FANSPEED; + +void (* const SonoffIfanCommand[])(void) PROGMEM = + { &CmndFanspeed }; + +uint8_t ifan_fanspeed_timer = 0; +uint8_t ifan_fanspeed_goal = 0; +bool ifan_receive_flag = false; +bool ifan_restart_flag = true; + +/*********************************************************************************************/ + +bool IsModuleIfan() +{ + return ((SONOFF_IFAN02 == my_module_type) || (SONOFF_IFAN03 == my_module_type)); +} + +uint8_t MaxFanspeed(void) +{ + return MAX_FAN_SPEED; +} + +uint8_t GetFanspeed(void) +{ + if (ifan_fanspeed_timer) { + return ifan_fanspeed_goal; // Do not show sequence fanspeed + } else { + /* Fanspeed is controlled by relay 2, 3 and 4 as in Sonoff 4CH. + 000x = 0 + 001x = 1 + 011x = 2 + 101x = 3 (ifan02) or 100x = 3 (ifan03) + */ + uint8_t fanspeed = (uint8_t)(power &0xF) >> 1; + if (fanspeed) { fanspeed = (fanspeed >> 1) +1; } // 0, 1, 2, 3 + return fanspeed; + } +} + +/*********************************************************************************************/ + +void SonoffIFanSetFanspeed(uint8_t fanspeed, bool sequence) +{ + ifan_fanspeed_timer = 0; // Stop any sequence + ifan_fanspeed_goal = fanspeed; + + uint8_t fanspeed_now = GetFanspeed(); + + if (fanspeed == fanspeed_now) { return; } + + uint8_t fans = kIFan02Speed[fanspeed]; + if (SONOFF_IFAN03 == my_module_type) { + if (sequence) { + fanspeed = kIFan03Sequence[fanspeed_now][ifan_fanspeed_goal]; + if (fanspeed != ifan_fanspeed_goal) { + if (0 == fanspeed_now) { + ifan_fanspeed_timer = 20; // Need extra time to power up fan + } else { + ifan_fanspeed_timer = 2; + } + } + } + fans = kIFan03Speed[fanspeed]; + } + for (uint32_t i = 2; i < 5; i++) { + uint8_t state = (fans &1) + POWER_OFF_NO_STATE; // Add no publishPowerState + ExecuteCommandPower(i, state, SRC_IGNORE); // Use relay 2, 3 and 4 + fans >>= 1; + } + +#ifdef USE_DOMOTICZ + if (sequence) { DomoticzUpdateFanState(); } // Command FanSpeed feedback +#endif // USE_DOMOTICZ +} + +/*********************************************************************************************/ + +void SonoffIfanReceived(void) +{ + char svalue[32]; + + uint8_t mode = serial_in_buffer[3]; + uint8_t action = serial_in_buffer[6]; + + if (4 == mode) { + if (action < 4) { + // AA 55 01 04 00 01 00 06 - Fan 0 + // AA 55 01 04 00 01 01 07 - Fan 1 + // AA 55 01 04 00 01 02 08 - Fan 2 + // AA 55 01 04 00 01 03 09 - Fan 3 + if (action != GetFanspeed()) { + snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_FANSPEED " %d"), action); + ExecuteCommand(svalue, SRC_REMOTE); +#ifdef USE_BUZZER + BuzzerEnabledBeep((action) ? action : 1, (action) ? 1 : 4); // Beep action times +#endif + } + } else { + // AA 55 01 04 00 01 04 0A - Light + ExecuteCommandPower(1, POWER_TOGGLE, SRC_REMOTE); + } + } + if (6 == mode) { + // AA 55 01 06 00 01 01 09 - Buzzer + Settings.flag3.buzzer_enable = !Settings.flag3.buzzer_enable; // SetOption67 + } + if (7 == mode) { + // AA 55 01 07 00 01 01 0A - Rf long press - forget RF codes +#ifdef USE_BUZZER + BuzzerEnabledBeep(4, 1); // Beep four times +#endif + } + + // Send Acknowledge - Copy first 5 bytes, reset byte 6 and store crc in byte 7 + // AA 55 01 04 00 00 05 + serial_in_buffer[5] = 0; // Ack + serial_in_buffer[6] = 0; // Crc + for (uint32_t i = 0; i < 7; i++) { + if ((i > 1) && (i < 6)) { serial_in_buffer[6] += serial_in_buffer[i]; } + Serial.write(serial_in_buffer[i]); + } +} + +bool SonoffIfanSerialInput(void) +{ + if (SONOFF_IFAN03 == my_module_type) { + if (0xAA == serial_in_byte) { // 0xAA - Start of text + serial_in_byte_counter = 0; + ifan_receive_flag = true; + } + if (ifan_receive_flag) { + serial_in_buffer[serial_in_byte_counter++] = serial_in_byte; + if (serial_in_byte_counter == 8) { + // AA 55 01 01 00 01 01 04 - Wifi long press - start wifi setup + // AA 55 01 01 00 01 02 05 - Rf and Wifi short press + // AA 55 01 04 00 01 00 06 - Fan 0 + // AA 55 01 04 00 01 01 07 - Fan 1 + // AA 55 01 04 00 01 02 08 - Fan 2 + // AA 55 01 04 00 01 03 09 - Fan 3 + // AA 55 01 04 00 01 04 0A - Light + // AA 55 01 06 00 01 01 09 - Buzzer + // AA 55 01 07 00 01 01 0A - Rf long press - forget RF codes + AddLogSerial(LOG_LEVEL_DEBUG); + uint8_t crc = 0; + for (uint32_t i = 2; i < 7; i++) { + crc += serial_in_buffer[i]; + } + if (crc == serial_in_buffer[7]) { + SonoffIfanReceived(); + ifan_receive_flag = false; + return true; + } + } + serial_in_byte = 0; + } + return false; + } +} + +/*********************************************************************************************\ + * Commands +\*********************************************************************************************/ + +void CmndFanspeed(void) +{ + if (XdrvMailbox.data_len > 0) { + if ('-' == XdrvMailbox.data[0]) { + XdrvMailbox.payload = (int16_t)GetFanspeed() -1; + if (XdrvMailbox.payload < 0) { XdrvMailbox.payload = MAX_FAN_SPEED -1; } + } + else if ('+' == XdrvMailbox.data[0]) { + XdrvMailbox.payload = GetFanspeed() +1; + if (XdrvMailbox.payload > MAX_FAN_SPEED -1) { XdrvMailbox.payload = 0; } + } + } + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < MAX_FAN_SPEED)) { + SonoffIFanSetFanspeed(XdrvMailbox.payload, true); + } + ResponseCmndNumber(GetFanspeed()); +} + +/*********************************************************************************************/ + +bool SonoffIfanInit(void) +{ + if (SONOFF_IFAN03 == my_module_type) { + Settings.flag.mqtt_serial = 0; + baudrate = 9600; + SetSeriallog(LOG_LEVEL_NONE); + } + return false; // Continue init chain +} + +void SonoffIfanUpdate(void) +{ + if (SONOFF_IFAN03 == my_module_type) { + if (ifan_fanspeed_timer) { + ifan_fanspeed_timer--; + if (!ifan_fanspeed_timer) { + SonoffIFanSetFanspeed(ifan_fanspeed_goal, false); + } + } + } + + if (ifan_restart_flag && (4 == uptime) && (SONOFF_IFAN02 == my_module_type)) { // Microcontroller needs 3 seconds before accepting commands + ifan_restart_flag = false; + SetDevicePower(1, SRC_RETRY); // Sync with default power on state microcontroller being Light ON and Fan OFF + SetDevicePower(power, SRC_RETRY); // Set required power on state + } +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xdrv22(uint8_t function) +{ + bool result = false; + + if (IsModuleIfan()) { + switch (function) { + case FUNC_EVERY_250_MSECOND: + SonoffIfanUpdate(); + break; + case FUNC_SERIAL: + result = SonoffIfanSerialInput(); + break; + case FUNC_COMMAND: + result = DecodeCommand(kSonoffIfanCommands, SonoffIfanCommand); + break; + case FUNC_MODULE_INIT: + result = SonoffIfanInit(); + break; + } + } + return result; +} + +#endif // USE_SONOFF_IFAN diff --git a/sonoff/xdrv_23_zigbee_0_constants.ino b/sonoff/xdrv_23_zigbee_0_constants.ino new file mode 100644 index 000000000..13d7dbb4f --- /dev/null +++ b/sonoff/xdrv_23_zigbee_0_constants.ino @@ -0,0 +1,430 @@ +/* + xdrv_23_zigbee_constants.ino - zigbee support for Sonoff-Tasmota + + Copyright (C) 2019 Theo Arends and Stephan Hadinger + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef USE_ZIGBEE + +#define ZIGBEE_VERBOSE // output versbose MQTT Zigbee logs. Will remain active for now + +typedef uint64_t Z_IEEEAddress; +typedef uint16_t Z_ShortAddress; + +enum ZnpCommandType { + Z_POLL = 0x00, + Z_SREQ = 0x20, + Z_AREQ = 0x40, + Z_SRSP = 0x60 +}; + +enum ZnpSubsystem { + Z_RPC_Error = 0x00, + Z_SYS = 0x01, + Z_MAC = 0x02, + Z_NWK = 0x03, + Z_AF = 0x04, + Z_ZDO = 0x05, + Z_SAPI = 0x06, + Z_UTIL = 0x07, + Z_DEBUG = 0x08, + Z_APP = 0x09 +}; + +// Commands in the SYS subsystem +enum SysCommand { + SYS_RESET = 0x00, + SYS_PING = 0x01, + SYS_VERSION = 0x02, + SYS_SET_EXTADDR = 0x03, + SYS_GET_EXTADDR = 0x04, + SYS_RAM_READ = 0x05, + SYS_RAM_WRITE = 0x06, + SYS_OSAL_NV_ITEM_INIT = 0x07, + SYS_OSAL_NV_READ = 0x08, + SYS_OSAL_NV_WRITE = 0x09, + SYS_OSAL_START_TIMER = 0x0A, + SYS_OSAL_STOP_TIMER = 0x0B, + SYS_RANDOM = 0x0C, + SYS_ADC_READ = 0x0D, + SYS_GPIO = 0x0E, + SYS_STACK_TUNE = 0x0F, + SYS_SET_TIME = 0x10, + SYS_GET_TIME = 0x11, + SYS_OSAL_NV_DELETE = 0x12, + SYS_OSAL_NV_LENGTH = 0x13, + SYS_TEST_RF = 0x40, + SYS_TEST_LOOPBACK = 0x41, + SYS_RESET_IND = 0x80, + SYS_OSAL_TIMER_EXPIRED = 0x81, +}; +// Commands in the SAPI subsystem +enum SapiCommand { + SAPI_START_REQUEST = 0x00, + SAPI_BIND_DEVICE = 0x01, + SAPI_ALLOW_BIND = 0x02, + SAPI_SEND_DATA_REQUEST = 0x03, + SAPI_READ_CONFIGURATION = 0x04, + SAPI_WRITE_CONFIGURATION = 0x05, + SAPI_GET_DEVICE_INFO = 0x06, + SAPI_FIND_DEVICE_REQUEST = 0x07, + SAPI_PERMIT_JOINING_REQUEST = 0x08, + SAPI_SYSTEM_RESET = 0x09, + SAPI_START_CONFIRM = 0x80, + SAPI_BIND_CONFIRM = 0x81, + SAPI_ALLOW_BIND_CONFIRM = 0x82, + SAPI_SEND_DATA_CONFIRM = 0x83, + SAPI_FIND_DEVICE_CONFIRM = 0x85, + SAPI_RECEIVE_DATA_INDICATION = 0x87, +}; +enum Z_configuration { + CONF_EXTADDR = 0x01, + CONF_BOOTCOUNTER = 0x02, + CONF_STARTUP_OPTION = 0x03, + CONF_START_DELAY = 0x04, + CONF_NIB = 0x21, + CONF_DEVICE_LIST = 0x22, + CONF_ADDRMGR = 0x23, + CONF_POLL_RATE = 0x24, + CONF_QUEUED_POLL_RATE = 0x25, + CONF_RESPONSE_POLL_RATE = 0x26, + CONF_REJOIN_POLL_RATE = 0x27, + CONF_DATA_RETRIES = 0x28, + CONF_POLL_FAILURE_RETRIES = 0x29, + CONF_STACK_PROFILE = 0x2A, + CONF_INDIRECT_MSG_TIMEOUT = 0x2B, + CONF_ROUTE_EXPIRY_TIME = 0x2C, + CONF_EXTENDED_PAN_ID = 0x2D, + CONF_BCAST_RETRIES = 0x2E, + CONF_PASSIVE_ACK_TIMEOUT = 0x2F, + CONF_BCAST_DELIVERY_TIME = 0x30, + CONF_NWK_MODE = 0x31, + CONF_CONCENTRATOR_ENABLE = 0x32, + CONF_CONCENTRATOR_DISCOVERY = 0x33, + CONF_CONCENTRATOR_RADIUS = 0x34, + CONF_CONCENTRATOR_RC = 0x36, + CONF_NWK_MGR_MODE = 0x37, + CONF_SRC_RTG_EXPIRY_TIME = 0x38, + CONF_ROUTE_DISCOVERY_TIME = 0x39, + CONF_NWK_ACTIVE_KEY_INFO = 0x3A, + CONF_NWK_ALTERN_KEY_INFO = 0x3B, + CONF_ROUTER_OFF_ASSOC_CLEANUP = 0x3C, + CONF_NWK_LEAVE_REQ_ALLOWED = 0x3D, + CONF_NWK_CHILD_AGE_ENABLE = 0x3E, + CONF_DEVICE_LIST_KA_TIMEOUT = 0x3F, + CONF_BINDING_TABLE = 0x41, + CONF_GROUP_TABLE = 0x42, + CONF_APS_FRAME_RETRIES = 0x43, + CONF_APS_ACK_WAIT_DURATION = 0x44, + CONF_APS_ACK_WAIT_MULTIPLIER = 0x45, + CONF_BINDING_TIME = 0x46, + CONF_APS_USE_EXT_PANID = 0x47, + CONF_APS_USE_INSECURE_JOIN = 0x48, + CONF_COMMISSIONED_NWK_ADDR = 0x49, + CONF_APS_NONMEMBER_RADIUS = 0x4B, + CONF_APS_LINK_KEY_TABLE = 0x4C, + CONF_APS_DUPREJ_TIMEOUT_INC = 0x4D, + CONF_APS_DUPREJ_TIMEOUT_COUNT = 0x4E, + CONF_APS_DUPREJ_TABLE_SIZE = 0x4F, + CONF_DIAGNOSTIC_STATS = 0x50, + CONF_SECURITY_LEVEL = 0x61, + CONF_PRECFGKEY = 0x62, + CONF_PRECFGKEYS_ENABLE = 0x63, + CONF_SECURITY_MODE = 0x64, + CONF_SECURE_PERMIT_JOIN = 0x65, + CONF_APS_LINK_KEY_TYPE = 0x66, + CONF_APS_ALLOW_R19_SECURITY = 0x67, + CONF_IMPLICIT_CERTIFICATE = 0x69, + CONF_DEVICE_PRIVATE_KEY = 0x6A, + CONF_CA_PUBLIC_KEY = 0x6B, + CONF_KE_MAX_DEVICES = 0x6C, + CONF_USE_DEFAULT_TCLK = 0x6D, + CONF_RNG_COUNTER = 0x6F, + CONF_RANDOM_SEED = 0x70, + CONF_TRUSTCENTER_ADDR = 0x71, + CONF_USERDESC = 0x81, + CONF_NWKKEY = 0x82, + CONF_PANID = 0x83, + CONF_CHANLIST = 0x84, + CONF_LEAVE_CTRL = 0x85, + CONF_SCAN_DURATION = 0x86, + CONF_LOGICAL_TYPE = 0x87, + CONF_NWKMGR_MIN_TX = 0x88, + CONF_NWKMGR_ADDR = 0x89, + CONF_ZDO_DIRECT_CB = 0x8F, + CONF_TCLK_TABLE_START = 0x0101, + ZNP_HAS_CONFIGURED = 0xF00 +}; + +// enum Z_nvItemIds { +// SCENE_TABLE = 145, +// MIN_FREE_NWK_ADDR = 146, +// MAX_FREE_NWK_ADDR = 147, +// MIN_FREE_GRP_ID = 148, +// MAX_FREE_GRP_ID = 149, +// MIN_GRP_IDS = 150, +// MAX_GRP_IDS = 151, +// OTA_BLOCK_REQ_DELAY = 152, +// SAPI_ENDPOINT = 161, +// SAS_SHORT_ADDR = 177, +// SAS_EXT_PANID = 178, +// SAS_PANID = 179, +// SAS_CHANNEL_MASK = 180, +// SAS_PROTOCOL_VER = 181, +// SAS_STACK_PROFILE = 182, +// SAS_STARTUP_CTRL = 183, +// SAS_TC_ADDR = 193, +// SAS_TC_MASTER_KEY = 194, +// SAS_NWK_KEY = 195, +// SAS_USE_INSEC_JOIN = 196, +// SAS_PRECFG_LINK_KEY = 197, +// SAS_NWK_KEY_SEQ_NUM = 198, +// SAS_NWK_KEY_TYPE = 199, +// SAS_NWK_MGR_ADDR = 200, +// SAS_CURR_TC_MASTER_KEY = 209, +// SAS_CURR_NWK_KEY = 210, +// SAS_CURR_PRECFG_LINK_KEY = 211, +// TCLK_TABLE_START = 257, +// TCLK_TABLE_END = 511, +// APS_LINK_KEY_DATA_START = 513, +// APS_LINK_KEY_DATA_END = 767, +// DUPLICATE_BINDING_TABLE = 768, +// DUPLICATE_DEVICE_LIST = 769, +// DUPLICATE_DEVICE_LIST_KA_TIMEOUT = 770, +//}; + +// +enum Z_Status { + Z_Success = 0x00, + Z_Failure = 0x01, + Z_InvalidParameter = 0x02, + Z_MemError = 0x03, + Z_Created = 0x09, + Z_BufferFull = 0x11 +}; + +enum Z_App_Profiles { + Z_PROF_IPM = 0x0101, // Industrial Plant Monitoring + Z_PROF_HA = 0x0104, // Home Automation -- the only supported right now + Z_PROF_CBA = 0x0105, // Commercial Building Automation + Z_PROF_TA = 0x0107, // Telecom Applications + Z_PROF_PHHC = 0x0108, // Personal Home & Hospital Care + Z_PROF_AMI = 0x0109, // Advanced Metering Initiative +}; + +enum Z_Device_Ids { + Z_DEVID_CONF_TOOL = 0x0005, + // from https://www.rfwireless-world.com/Terminology/Zigbee-Profile-ID-list.html + // Generic 0x0000 ON/OFF Switch + // 0x0001 Level Control Switch + // 0x0002 ON/OFF Output + // 0x0003 Level Controllable Output + // 0x0004 Scene Selector + // 0x0005 Configuration Tool + // 0x0006 Remote control + // 0x0007 Combined Interface + // 0x0008 Range Extender + // 0x0009 Mains Power Outlet + // Lighting 0x0100 ON/OFF Light + // 0x0101 Dimmable Light + // 0x0102 Color Dimmable Light + // 0x0103 ON/OFF Light Switch + // 0x0104 Dimmer Switch + // 0x0105 Color Dimmer Switch + // 0x0106 Light Sensor + // 0x0107 Occupancy Sensor + // Closures 0x0200 Shade + // 0x0201 Shade Controller + // HVAC 0x0300 Heating/Cooling Unit + // 0x0301 Thermostat + // 0x0302 Temperature Sensor + // 0x0303 Pump + // 0x0304 Pump Controller + // 0x0305 Pressure Sensor + // 0x0306 Flow sensor + // Intruder Alarm Systems 0x0400 IAS Control and Indicating Equipment + // 0x0401 IAS Ancillary Control Equipment + // 0x0402 IAS Zone + // 0x0403 IAS Warning Device +}; + +// enum class AddrMode : uint8_t { +// NotPresent = 0, +// Group = 1, +// ShortAddress = 2, +// IEEEAddress = 3, +// Broadcast = 0xFF +// }; +// +// +// +// Commands in the AF subsystem +enum AfCommand : uint8_t { + AF_REGISTER = 0x00, + AF_DATA_REQUEST = 0x01, + AF_DATA_REQUEST_EXT = 0x02, + AF_DATA_REQUEST_SRC_RTG = 0x03, + AF_INTER_PAN_CTL = 0x10, + AF_DATA_STORE = 0x11, + AF_DATA_RETRIEVE = 0x12, + AF_APSF_CONFIG_SET = 0x13, + AF_DATA_CONFIRM = 0x80, + AF_REFLECT_ERROR = 0x83, + AF_INCOMING_MSG = 0x81, + AF_INCOMING_MSG_EXT = 0x82 +}; +// +// Commands in the ZDO subsystem +enum : uint8_t { + ZDO_NWK_ADDR_REQ = 0x00, + ZDO_IEEE_ADDR_REQ = 0x01, + ZDO_NODE_DESC_REQ = 0x02, + ZDO_POWER_DESC_REQ = 0x03, + ZDO_SIMPLE_DESC_REQ = 0x04, + ZDO_ACTIVE_EP_REQ = 0x05, + ZDO_MATCH_DESC_REQ = 0x06, + ZDO_COMPLEX_DESC_REQ = 0x07, + ZDO_USER_DESC_REQ = 0x08, + ZDO_DEVICE_ANNCE = 0x0A, + ZDO_USER_DESC_SET = 0x0B, + ZDO_SERVER_DISC_REQ = 0x0C, + ZDO_END_DEVICE_BIND_REQ = 0x20, + ZDO_BIND_REQ = 0x21, + ZDO_UNBIND_REQ = 0x22, + ZDO_SET_LINK_KEY = 0x23, + ZDO_REMOVE_LINK_KEY = 0x24, + ZDO_GET_LINK_KEY = 0x25, + ZDO_MGMT_NWK_DISC_REQ = 0x30, + ZDO_MGMT_LQI_REQ = 0x31, + ZDO_MGMT_RTQ_REQ = 0x32, + ZDO_MGMT_BIND_REQ = 0x33, + ZDO_MGMT_LEAVE_REQ = 0x34, + ZDO_MGMT_DIRECT_JOIN_REQ = 0x35, + ZDO_MGMT_PERMIT_JOIN_REQ = 0x36, + ZDO_MGMT_NWK_UPDATE_REQ = 0x37, + ZDO_MSG_CB_REGISTER = 0x3E, + ZDO_MGS_CB_REMOVE = 0x3F, + ZDO_STARTUP_FROM_APP = 0x40, + ZDO_AUTO_FIND_DESTINATION = 0x41, + ZDO_EXT_REMOVE_GROUP = 0x47, + ZDO_EXT_REMOVE_ALL_GROUP = 0x48, + ZDO_EXT_FIND_ALL_GROUPS_ENDPOINT = 0x49, + ZDO_EXT_FIND_GROUP = 0x4A, + ZDO_EXT_ADD_GROUP = 0x4B, + ZDO_EXT_COUNT_ALL_GROUPS = 0x4C, + ZDO_NWK_ADDR_RSP = 0x80, + ZDO_IEEE_ADDR_RSP = 0x81, + ZDO_NODE_DESC_RSP = 0x82, + ZDO_POWER_DESC_RSP = 0x83, + ZDO_SIMPLE_DESC_RSP = 0x84, + ZDO_ACTIVE_EP_RSP = 0x85, + ZDO_MATCH_DESC_RSP = 0x86, + ZDO_COMPLEX_DESC_RSP = 0x87, + ZDO_USER_DESC_RSP = 0x88, + ZDO_USER_DESC_CONF = 0x89, + ZDO_SERVER_DISC_RSP = 0x8A, + ZDO_END_DEVICE_BIND_RSP = 0xA0, + ZDO_BIND_RSP = 0xA1, + ZDO_UNBIND_RSP = 0xA2, + ZDO_MGMT_NWK_DISC_RSP = 0xB0, + ZDO_MGMT_LQI_RSP = 0xB1, + ZDO_MGMT_RTG_RSP = 0xB2, + ZDO_MGMT_BIND_RSP = 0xB3, + ZDO_MGMT_LEAVE_RSP = 0xB4, + ZDO_MGMT_DIRECT_JOIN_RSP = 0xB5, + ZDO_MGMT_PERMIT_JOIN_RSP = 0xB6, + ZDO_STATE_CHANGE_IND = 0xC0, + ZDO_END_DEVICE_ANNCE_IND = 0xC1, + ZDO_MATCH_DESC_RSP_SENT = 0xC2, + ZDO_STATUS_ERROR_RSP = 0xC3, + ZDO_SRC_RTG_IND = 0xC4, + ZDO_LEAVE_IND = 0xC9, + ZDO_TC_DEV_IND = 0xCA, + ZDO_PERMIT_JOIN_IND = 0xCB, + ZDO_MSG_CB_INCOMING = 0xFF +}; + +//https://e2e.ti.com/support/wireless-connectivity/zigbee-and-thread/f/158/t/475920 +enum ZdoStates { + ZDO_DEV_HOLD = 0x00, // Initialized - not started automatically + ZDO_DEV_INIT = 0x01, // Initialized - not connected to anything + ZDO_DEV_NWK_DISC = 0x02, // Discovering PANIDs to join + ZDO_DEV_NWK_JOINING = 0x03, // Joining a PAN + ZDO_DEV_NWK_REJOIN = 0x04, // ReJoining a PAN, only for end devices + ZDO_DEV_END_DEVICE_UNAUTH = 0x05, // Joined but not yet authenticated by trust center + ZDO_DEV_END_DEVICE = 0x06, // Started as a device after authentication. Note: you'll see this for both Routers or End Devices. + ZDO_DEV_ROUTER = 0x07, // Started as a Zigbee Router + ZDO_DEV_COORD_STARTING = 0x08, // Starting as a Zigbee Coordinator + ZDO_DEV_ZB_COORD = 0x09, // Started as a a Zigbee Coordinator + ZDO_DEV_NWK_ORPHAN = 0x0A, // Device has lost information about its parent. +}; +// +// Commands in the UTIL subsystem +enum Z_Util { + Z_UTIL_GET_DEVICE_INFO = 0x00, + Z_UTIL_GET_NV_INFO = 0x01, + Z_UTIL_SET_PANID = 0x02, + Z_UTIL_SET_CHANNELS = 0x03, + Z_UTIL_SET_SECLEVEL = 0x04, + Z_UTIL_SET_PRECFGKEY = 0x05, + Z_UTIL_CALLBACK_SUB_CMD = 0x06, + Z_UTIL_KEY_EVENT = 0x07, + Z_UTIL_TIME_ALIVE = 0x09, + Z_UTIL_LED_CONTROL = 0x0A, + Z_UTIL_TEST_LOOPBACK = 0x10, + Z_UTIL_DATA_REQ = 0x11, + Z_UTIL_SRC_MATCH_ENABLE = 0x20, + Z_UTIL_SRC_MATCH_ADD_ENTRY = 0x21, + Z_UTIL_SRC_MATCH_DEL_ENTRY = 0x22, + Z_UTIL_SRC_MATCH_CHECK_SRC_ADDR = 0x23, + Z_UTIL_SRC_MATCH_ACK_ALL_PENDING = 0x24, + Z_UTIL_SRC_MATCH_CHECK_ALL_PENDING = 0x25, + Z_UTIL_ADDRMGR_EXT_ADDR_LOOKUP = 0x40, + Z_UTIL_ADDRMGR_NWK_ADDR_LOOKUP = 0x41, + Z_UTIL_APSME_LINK_KEY_DATA_GET = 0x44, + Z_UTIL_APSME_LINK_KEY_NV_ID_GET = 0x45, + Z_UTIL_ASSOC_COUNT = 0x48, + Z_UTIL_ASSOC_FIND_DEVICE = 0x49, + Z_UTIL_ASSOC_GET_WITH_ADDRESS = 0x4A, + Z_UTIL_APSME_REQUEST_KEY_CMD = 0x4B, + Z_UTIL_ZCL_KEY_EST_INIT_EST = 0x80, + Z_UTIL_ZCL_KEY_EST_SIGN = 0x81, + Z_UTIL_UTIL_SYNC_REQ = 0xE0, + Z_UTIL_ZCL_KEY_ESTABLISH_IND = 0xE1 +}; + +enum ZCL_Global_Commands { + ZCL_READ_ATTRIBUTES = 0x00, + ZCL_READ_ATTRIBUTES_RESPONSE = 0x01, + ZCL_WRITE_ATTRIBUTES = 0x02, + ZCL_WRITE_ATTRIBUTES_UNDIVIDED = 0x03, + ZCL_WRITE_ATTRIBUTES_RESPONSE = 0x04, + ZCL_WRITE_ATTRIBUTES_NORESPONSE = 0x05, + ZCL_CONFIGURE_REPORTING = 0x06, + ZCL_CONFIGURE_REPORTING_RESPONSE = 0x07, + ZCL_READ_REPORTING_CONFIGURATION = 0x08, + ZCL_READ_REPORTING_CONFIGURATION_RESPONSE = 0x09, + ZCL_REPORT_ATTRIBUTES = 0x0a, + ZCL_DEFAULT_RESPONSE = 0x0b, + ZCL_DISCOVER_ATTRIBUTES = 0x0c, + ZCL_DISCOVER_ATTRIBUTES_RESPONSE = 0x0d + +}; + +const uint16_t Z_ProfileIds[] PROGMEM = { 0x0104, 0x0109, 0xA10E, 0xC05E }; +const char Z_ProfileNames[] PROGMEM = "ZigBee Home Automation|ZigBee Smart Energy|ZigBee Green Power|ZigBee Light Link"; + +#endif // USE_ZIGBEE diff --git a/sonoff/xdrv_23_zigbee_3_devices.ino b/sonoff/xdrv_23_zigbee_3_devices.ino new file mode 100644 index 000000000..3596ef78e --- /dev/null +++ b/sonoff/xdrv_23_zigbee_3_devices.ino @@ -0,0 +1,442 @@ +/* + xdrv_23_zigbee.ino - zigbee support for Sonoff-Tasmota + + Copyright (C) 2019 Theo Arends and Stephan Hadinger + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef USE_ZIGBEE + +#include +#include + +typedef struct Z_Device { + uint16_t shortaddr; // unique key if not null, or unspecified if null + uint64_t longaddr; // 0x00 means unspecified + uint32_t firstSeen; // date when the device was first seen + uint32_t lastSeen; // date when the device was last seen + String manufacturerId; + String modelId; + String friendlyName; + std::vector endpoints; // encoded as high 16 bits is endpoint, low 16 bits is ProfileId + std::vector clusters_in; // encoded as high 16 bits is endpoint, low 16 bits is cluster number + std::vector clusters_out; // encoded as high 16 bits is endpoint, low 16 bits is cluster number +} Z_Device; + +// All devices are stored in a Vector +// Invariants: +// - shortaddr is unique if not null +// - longaddr is unique if not null +// - shortaddr and longaddr cannot be both null +// - clusters_in and clusters_out containt only endpoints listed in endpoints +class Z_Devices { +public: + Z_Devices() {}; + + // Add new device, provide ShortAddr and optional longAddr + // If it is already registered, update information, otherwise create the entry + void updateDevice(uint16_t shortaddr, uint64_t longaddr = 0); + + // Add an endpoint to a device + void addEndoint(uint16_t shortaddr, uint8_t endpoint); + + // Add endpoint profile + void addEndointProfile(uint16_t shortaddr, uint8_t endpoint, uint16_t profileId); + + // Add cluster + void addCluster(uint16_t shortaddr, uint8_t endpoint, uint16_t cluster, bool out); + + uint8_t findClusterEndpointIn(uint16_t shortaddr, uint16_t cluster); + + void setManufId(uint16_t shortaddr, const char * str); + void setModelId(uint16_t shortaddr, const char * str); + void setFriendlyNameId(uint16_t shortaddr, const char * str); + + // device just seen on the network, update the lastSeen field + void updateLastSeen(uint16_t shortaddr); + + // Dump json + String dump(uint8_t dump_mode) const; + +private: + std::vector _devices = {}; + + template < typename T> + static bool findInVector(const std::vector & vecOfElements, const T & element); + + template < typename T> + static int32_t findEndpointInVector(const std::vector & vecOfElements, const T & element); + + // find the first endpoint match for a cluster + static int32_t findClusterEndpoint(const std::vector & vecOfElements, uint16_t element); + + Z_Device & getShortAddr(uint16_t shortaddr); // find Device from shortAddr, creates it if does not exist + Z_Device & getLongAddr(uint64_t longaddr); // find Device from shortAddr, creates it if does not exist + + int32_t findShortAddr(uint16_t shortaddr); + int32_t findLongAddr(uint64_t longaddr); + + void _updateLastSeen(Z_Device &device) { + if (&device != nullptr) { + device.lastSeen = Rtc.utc_time; + } + }; + + // Create a new entry in the devices list - must be called if it is sure it does not already exist + Z_Device & createDeviceEntry(uint16_t shortaddr, uint64_t longaddr = 0); +}; + +Z_Devices zigbee_devices = Z_Devices(); + +// https://thispointer.com/c-how-to-find-an-element-in-vector-and-get-its-index/ +template < typename T> +bool Z_Devices::findInVector(const std::vector & vecOfElements, const T & element) { + // Find given element in vector + auto it = std::find(vecOfElements.begin(), vecOfElements.end(), element); + + if (it != vecOfElements.end()) { + return true; + } else { + return false; + } +} + +template < typename T> +int32_t Z_Devices::findEndpointInVector(const std::vector & vecOfElements, const T & element) { + // Find given element in vector + + int32_t found = 0; + for (auto &elem : vecOfElements) { + if ((elem >> 16) & 0xFF == element) { return found; } + found++; + } + + return -1; +} + +// +// Find the first endpoint match for a cluster, whether in or out +// Clusters are stored in the format 0x00EECCCC (EE=endpoint, CCCC=cluster number) +// In: +// _devices.clusters_in or _devices.clusters_out +// cluster number looked for +// Out: +// Index of found Endpoint_Cluster number, or -1 if not found +// +int32_t Z_Devices::findClusterEndpoint(const std::vector & vecOfElements, uint16_t cluster) { + int32_t found = 0; + for (auto &elem : vecOfElements) { + if ((elem & 0xFFFF) == cluster) { return found; } + found++; + } + return -1; +} + +// +// Create a new Z_Device entry in _devices. Only to be called if you are sure that no +// entry with same shortaddr or longaddr exists. +// +Z_Device & Z_Devices::createDeviceEntry(uint16_t shortaddr, uint64_t longaddr) { + if (!shortaddr && !longaddr) { return *(Z_Device*) nullptr; } // it is not legal to create an enrty with both short/long addr null + Z_Device device = { shortaddr, longaddr, + Rtc.utc_time, Rtc.utc_time, + String(), // ManufId + String(), // DeviceId + String(), // FriendlyName + std::vector(), + std::vector(), + std::vector() }; + _devices.push_back(device); + return _devices.back(); +} + +// +// Scan all devices to find a corresponding shortaddr +// Looks info device.shortaddr entry +// In: +// shortaddr (non null) +// Out: +// index in _devices of entry, -1 if not found +// +int32_t Z_Devices::findShortAddr(uint16_t shortaddr) { + if (!shortaddr) { return -1; } // does not make sense to look for 0x0000 shortaddr (localhost) + int32_t found = 0; + if (shortaddr) { + for (auto &elem : _devices) { + if (elem.shortaddr == shortaddr) { return found; } + found++; + } + } + return -1; +} +// +// Scan all devices to find a corresponding longaddr +// Looks info device.longaddr entry +// In: +// longaddr (non null) +// Out: +// index in _devices of entry, -1 if not found +// +int32_t Z_Devices::findLongAddr(uint64_t longaddr) { + if (!longaddr) { return -1; } + int32_t found = 0; + if (longaddr) { + for (auto &elem : _devices) { + if (elem.longaddr == longaddr) { return found; } + found++; + } + } + return -1; +} + +// +// We have a seen a shortaddr on the network, get the corresponding +// +Z_Device & Z_Devices::getShortAddr(uint16_t shortaddr) { + if (!shortaddr) { return *(Z_Device*) nullptr; } // this is not legal + int32_t found = findShortAddr(shortaddr); + if (found >= 0) { + return _devices[found]; + } +//Serial.printf("Device entry created for shortaddr = 0x%02X, found = %d\n", shortaddr, found); + return createDeviceEntry(shortaddr, 0); +} + +// find the Device object by its longaddr (unique key if not null) +Z_Device & Z_Devices::getLongAddr(uint64_t longaddr) { + if (!longaddr) { return *(Z_Device*) nullptr; } + int32_t found = findLongAddr(longaddr); + if (found > 0) { + return _devices[found]; + } + return createDeviceEntry(0, longaddr); +} + +// +// We have just seen a device on the network, update the info based on short/long addr +// In: +// shortaddr +// longaddr (both can't be null at the same time) +void Z_Devices::updateDevice(uint16_t shortaddr, uint64_t longaddr) { + int32_t s_found = findShortAddr(shortaddr); // is there already a shortaddr entry + int32_t l_found = findLongAddr(longaddr); // is there already a longaddr entry + + if ((s_found >= 0) && (l_found >= 0)) { // both shortaddr and longaddr are already registered + if (s_found == l_found) { + updateLastSeen(shortaddr); // short/long addr match, all good + } else { // they don't match + // the device with longaddr got a new shortaddr + _devices[l_found].shortaddr = shortaddr; // update the shortaddr corresponding to the longaddr + // erase the previous shortaddr + _devices.erase(_devices.begin() + s_found); + updateLastSeen(shortaddr); + } + } else if (s_found >= 0) { + // shortaddr already exists but longaddr not + // add the longaddr to the entry + _devices[s_found].longaddr = longaddr; + updateLastSeen(shortaddr); + } else if (l_found >= 0) { + // longaddr entry exists, update shortaddr + _devices[l_found].shortaddr = shortaddr; + } else { + // neither short/lonf addr are found. + if (shortaddr || longaddr) { + createDeviceEntry(shortaddr, longaddr); + } + } +} + +// +// Add an endpoint to a shortaddr +// +void Z_Devices::addEndoint(uint16_t shortaddr, uint8_t endpoint) { + if (!shortaddr) { return; } + uint32_t ep_profile = (endpoint << 16); + Z_Device &device = getShortAddr(shortaddr); + if (&device == nullptr) { return; } // don't crash if not found + _updateLastSeen(device); + if (findEndpointInVector(device.endpoints, ep_profile) < 0) { + device.endpoints.push_back(ep_profile); + } +} + +void Z_Devices::addEndointProfile(uint16_t shortaddr, uint8_t endpoint, uint16_t profileId) { + if (!shortaddr) { return; } + uint32_t ep_profile = (endpoint << 16) | profileId; + Z_Device &device = getShortAddr(shortaddr); + if (&device == nullptr) { return; } // don't crash if not found + _updateLastSeen(device); + int32_t found = findEndpointInVector(device.endpoints, ep_profile); + if (found < 0) { + device.endpoints.push_back(ep_profile); + } else { + device.endpoints[found] = ep_profile; + } +} + +void Z_Devices::addCluster(uint16_t shortaddr, uint8_t endpoint, uint16_t cluster, bool out) { + if (!shortaddr) { return; } + Z_Device & device = getShortAddr(shortaddr); + if (&device == nullptr) { return; } // don't crash if not found + _updateLastSeen(device); + uint32_t ep_cluster = (endpoint << 16) | cluster; + if (!out) { + if (!findInVector(device.clusters_in, ep_cluster)) { + device.clusters_in.push_back(ep_cluster); + } + } else { // out + if (!findInVector(device.clusters_out, ep_cluster)) { + device.clusters_out.push_back(ep_cluster); + } + } +} + +// Look for the best endpoint match to send a command for a specific Cluster ID +// return 0x00 if none found +uint8_t Z_Devices::findClusterEndpointIn(uint16_t shortaddr, uint16_t cluster){ + int32_t short_found = findShortAddr(shortaddr); + if (short_found < 0) return 0; // avoid creating an entry if the device was never seen + Z_Device &device = getShortAddr(shortaddr); + if (&device == nullptr) { return 0; } // don't crash if not found + int32_t found = findClusterEndpoint(device.clusters_in, cluster); + if (found >= 0) { + return (device.clusters_in[found] >> 16) & 0xFF; + } else { + return 0; + } +} + + +void Z_Devices::setManufId(uint16_t shortaddr, const char * str) { + Z_Device & device = getShortAddr(shortaddr); + if (&device == nullptr) { return; } // don't crash if not found + _updateLastSeen(device); + device.manufacturerId = str; +} +void Z_Devices::setModelId(uint16_t shortaddr, const char * str) { + Z_Device & device = getShortAddr(shortaddr); + if (&device == nullptr) { return; } // don't crash if not found + _updateLastSeen(device); + device.modelId = str; +} +void Z_Devices::setFriendlyNameId(uint16_t shortaddr, const char * str) { + Z_Device & device = getShortAddr(shortaddr); + if (&device == nullptr) { return; } // don't crash if not found + _updateLastSeen(device); + device.friendlyName = str; +} + +// device just seen on the network, update the lastSeen field +void Z_Devices::updateLastSeen(uint16_t shortaddr) { + Z_Device & device = getShortAddr(shortaddr); + if (&device == nullptr) { return; } // don't crash if not found + _updateLastSeen(device); +} + +// Dump the internal memory of Zigbee devices +// Mode = 1: simple dump of devices addresses and names +// Mode = 2: Mode 1 + also dump the endpoints, profiles and clusters +String Z_Devices::dump(uint8_t dump_mode) const { + DynamicJsonBuffer jsonBuffer; + JsonArray& json = jsonBuffer.createArray(); + JsonArray& devices = json; + //JsonArray& devices = json.createNestedArray(F("ZigbeeDevices")); + + for (std::vector::const_iterator it = _devices.begin(); it != _devices.end(); ++it) { + const Z_Device& device = *it; + uint16_t shortaddr = device.shortaddr; + char hex[20]; + + JsonObject& dev = devices.createNestedObject(); + + snprintf_P(hex, sizeof(hex), PSTR("0x%04X"), shortaddr); + dev[F(D_JSON_ZIGBEE_DEVICE)] = hex; + + if (device.friendlyName.length() > 0) { + dev[F(D_JSON_ZIGBEE_NAME)] = device.friendlyName; + } + + if (1 == dump_mode) { + Uint64toHex(device.longaddr, hex, 64); + dev[F("IEEEAddr")] = hex; + if (device.modelId.length() > 0) { + dev[F(D_JSON_MODEL D_JSON_ID)] = device.modelId; + } + if (device.manufacturerId.length() > 0) { + dev[F("Manufacturer")] = device.manufacturerId; + } + } + + // If dump_mode == 2, dump a lot more details + if (2 == dump_mode) { + JsonObject& dev_endpoints = dev.createNestedObject(F("Endpoints")); + for (std::vector::const_iterator ite = device.endpoints.begin() ; ite != device.endpoints.end(); ++ite) { + uint32_t ep_profile = *ite; + uint8_t endpoint = (ep_profile >> 16) & 0xFF; + uint16_t profileId = ep_profile & 0xFFFF; + + snprintf_P(hex, sizeof(hex), PSTR("0x%02X"), endpoint); + JsonObject& ep = dev_endpoints.createNestedObject(hex); + + snprintf_P(hex, sizeof(hex), PSTR("0x%04X"), profileId); + ep[F("ProfileId")] = hex; + + int32_t found = -1; + for (uint32_t i = 0; i < sizeof(Z_ProfileIds) / sizeof(Z_ProfileIds[0]); i++) { + if (pgm_read_word(&Z_ProfileIds[i]) == profileId) { + found = i; + break; + } + } + if (found > 0) { + GetTextIndexed(hex, sizeof(hex), found, Z_ProfileNames); + ep[F("ProfileIdName")] = hex; + } + + ep.createNestedArray(F("ClustersIn")); + ep.createNestedArray(F("ClustersOut")); + } + + for (std::vector::const_iterator itc = device.clusters_in.begin() ; itc != device.clusters_in.end(); ++itc) { + uint16_t cluster = *itc & 0xFFFF; + uint8_t endpoint = (*itc >> 16) & 0xFF; + + snprintf_P(hex, sizeof(hex), PSTR("0x%02X"), endpoint); + JsonArray &cluster_arr = dev_endpoints[hex][F("ClustersIn")]; + + snprintf_P(hex, sizeof(hex), PSTR("0x%04X"), cluster); + cluster_arr.add(hex); + } + + for (std::vector::const_iterator itc = device.clusters_out.begin() ; itc != device.clusters_out.end(); ++itc) { + uint16_t cluster = *itc & 0xFFFF; + uint8_t endpoint = (*itc >> 16) & 0xFF; + + snprintf_P(hex, sizeof(hex), PSTR("0x%02X"), endpoint); + JsonArray &cluster_arr = dev_endpoints[hex][F("ClustersOut")]; + + snprintf_P(hex, sizeof(hex), PSTR("0x%04X"), cluster); + cluster_arr.add(hex); + } + } + } + String payload = ""; + payload.reserve(200); + json.printTo(payload); + return payload; +} + +#endif // USE_ZIGBEE diff --git a/sonoff/xdrv_23_zigbee_5_converters.ino b/sonoff/xdrv_23_zigbee_5_converters.ino new file mode 100644 index 000000000..76df95aeb --- /dev/null +++ b/sonoff/xdrv_23_zigbee_5_converters.ino @@ -0,0 +1,994 @@ +/* + xdrv_23_zigbee_converters.ino - zigbee support for Sonoff-Tasmota + + Copyright (C) 2019 Theo Arends and Stephan Hadinger + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef USE_ZIGBEE + +/*********************************************************************************************\ + * ZCL +\*********************************************************************************************/ + +typedef union ZCLHeaderFrameControl_t { + struct { + uint8_t frame_type : 2; // 00 = across entire profile, 01 = cluster specific + uint8_t manuf_specific : 1; // Manufacturer Specific Sub-field + uint8_t direction : 1; // 0 = tasmota to zigbee, 1 = zigbee to tasmota + uint8_t disable_def_resp : 1; // don't send back default response + uint8_t reserved : 3; + } b; + uint32_t d8; // raw 8 bits field +} ZCLHeaderFrameControl_t; + + +class ZCLFrame { +public: + + ZCLFrame(uint8_t frame_control, uint16_t manuf_code, uint8_t transact_seq, uint8_t cmd_id, + const char *buf, size_t buf_len, uint16_t clusterid = 0, uint16_t groupid = 0): + _cmd_id(cmd_id), _manuf_code(manuf_code), _transact_seq(transact_seq), + _payload(buf_len ? buf_len : 250), // allocate the data frame from source or preallocate big enough + _cluster_id(clusterid), _group_id(groupid) + { + _frame_control.d8 = frame_control; + _payload.addBuffer(buf, buf_len); + }; + + + void publishMQTTReceived(uint16_t groupid, uint16_t clusterid, Z_ShortAddress srcaddr, + uint8_t srcendpoint, uint8_t dstendpoint, uint8_t wasbroadcast, + uint8_t linkquality, uint8_t securityuse, uint8_t seqnumber, + uint32_t timestamp) { +#ifdef ZIGBEE_VERBOSE + char hex_char[_payload.len()*2+2]; + ToHex_P((unsigned char*)_payload.getBuffer(), _payload.len(), hex_char, sizeof(hex_char)); + Response_P(PSTR("{\"" D_JSON_ZIGBEEZCL_RECEIVED "\":{" + "\"groupid\":%d," "\"clusterid\":%d," "\"srcaddr\":\"0x%04X\"," + "\"srcendpoint\":%d," "\"dstendpoint\":%d," "\"wasbroadcast\":%d," + "\"" D_CMND_ZIGBEE_LINKQUALITY "\":%d," "\"securityuse\":%d," "\"seqnumber\":%d," + "\"timestamp\":%d," + "\"fc\":\"0x%02X\",\"manuf\":\"0x%04X\",\"transact\":%d," + "\"cmdid\":\"0x%02X\",\"payload\":\"%s\""), + groupid, clusterid, srcaddr, + srcendpoint, dstendpoint, wasbroadcast, + linkquality, securityuse, seqnumber, + timestamp, + _frame_control, _manuf_code, _transact_seq, _cmd_id, + hex_char); + + ResponseJsonEnd(); // append '}' + ResponseJsonEnd(); // append '}' + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED)); + XdrvRulesProcess(); +#endif + } + + static ZCLFrame parseRawFrame(const SBuffer &buf, uint8_t offset, uint8_t len, uint16_t clusterid, uint16_t groupid) { // parse a raw frame and build the ZCL frame object + uint32_t i = offset; + ZCLHeaderFrameControl_t frame_control; + uint16_t manuf_code = 0; + uint8_t transact_seq; + uint8_t cmd_id; + + frame_control.d8 = buf.get8(i++); + if (frame_control.b.manuf_specific) { + manuf_code = buf.get16(i); + i += 2; + } + transact_seq = buf.get8(i++); + cmd_id = buf.get8(i++); + ZCLFrame zcl_frame(frame_control.d8, manuf_code, transact_seq, cmd_id, + (const char *)(buf.buf() + i), len + offset - i, + clusterid, groupid); + return zcl_frame; + } + + bool isClusterSpecificCommand(void) { + return _frame_control.b.frame_type & 1; + } + + void parseRawAttributes(JsonObject& json, uint8_t offset = 0); + void parseReadAttributes(JsonObject& json, uint8_t offset = 0); + void parseClusterSpecificCommand(JsonObject& json, uint8_t offset = 0); + void postProcessAttributes(uint16_t shortaddr, JsonObject& json); + + inline void setGroupId(uint16_t groupid) { + _group_id = groupid; + } + + inline void setClusterId(uint16_t clusterid) { + _cluster_id = clusterid; + } + + inline uint8_t getCmdId(void) const { + return _cmd_id; + } + + inline uint16_t getClusterId(void) const { + return _cluster_id; + } + + const SBuffer &getPayload(void) const { + return _payload; + } + +private: + ZCLHeaderFrameControl_t _frame_control = { .d8 = 0 }; + uint16_t _manuf_code = 0; // optional + uint8_t _transact_seq = 0; // transaction sequence number + uint8_t _cmd_id = 0; + uint16_t _cluster_id = 0; + uint16_t _group_id = 0; + SBuffer _payload; +}; + +// Zigbee ZCL converters + +// from https://github.com/Koenkk/zigbee-shepherd-converters/blob/638d29f0cace6343052b9a4e7fd60980fa785479/converters/fromZigbee.js#L55 +// Input voltage in mV, i.e. 3000 = 3.000V +// Output percentage from 0 to 100 as int +uint8_t toPercentageCR2032(uint32_t voltage) { + uint32_t percentage; + if (voltage < 2100) { + percentage = 0; + } else if (voltage < 2440) { + percentage = 6 - ((2440 - voltage) * 6) / 340; + } else if (voltage < 2740) { + percentage = 18 - ((2740 - voltage) * 12) / 300; + } else if (voltage < 2900) { + percentage = 42 - ((2900 - voltage) * 24) / 160; + } else if (voltage < 3000) { + percentage = 100 - ((3000 - voltage) * 58) / 100; + } else if (voltage >= 3000) { + percentage = 100; + } + return percentage; +} + + +uint32_t parseSingleAttribute(JsonObject& json, char *attrid_str, class SBuffer &buf, + uint32_t offset, uint32_t len) { + + uint32_t i = offset; + uint32_t attrtype = buf.get8(i++); + + // fallback - enter a null value + json[attrid_str] = (char*) nullptr; + + // now parse accordingly to attr type + switch (attrtype) { + case 0x00: // nodata + case 0xFF: // unk + break; + case 0x10: // bool + { + uint8_t val_bool = buf.get8(i++); + if (0xFF != val_bool) { + json[attrid_str] = (bool) (val_bool ? true : false); + } + } + break; + case 0x20: // uint8 + { + uint8_t uint8_val = buf.get8(i); + i += 1; + if (0xFF != uint8_val) { + json[attrid_str] = uint8_val; + } + } + break; + case 0x21: // uint16 + { + uint16_t uint16_val = buf.get16(i); + i += 2; + if (0xFFFF != uint16_val) { + json[attrid_str] = uint16_val; + } + } + break; + case 0x23: // uint32 + { + uint32_t uint32_val = buf.get32(i); + i += 4; + if (0xFFFFFFFF != uint32_val) { + json[attrid_str] = uint32_val; + } + } + break; + // Note: uint40, uint48, uint56, uint64 are not used in ZCL, so they are not implemented (yet) + case 0x24: // int40 + case 0x25: // int48 + case 0x26: // int56 + case 0x27: // int64 + i += attrtype - 0x1F; // 5 - 8; + break; + case 0x28: // uint8 + { + int8_t int8_val = buf.get8(i); + i += 1; + if (0x80 != int8_val) { + json[attrid_str] = int8_val; + } + } + break; + case 0x29: // uint16 + { + int16_t int16_val = buf.get16(i); + i += 2; + if (0x8000 != int16_val) { + json[attrid_str] = int16_val; + } + } + break; + case 0x2B: // uint16 + { + int32_t int32_val = buf.get32(i); + i += 4; + if (0x80000000 != int32_val) { + json[attrid_str] = int32_val; + } + } + break; + // Note: int40, int48, int56, int64 are not used in ZCL, so they are not implemented (yet) + case 0x2C: // int40 + case 0x2D: // int48 + case 0x2E: // int56 + case 0x2F: // int64 + i += attrtype - 0x27; // 5 - 8; + break; + + case 0x41: // octet string, 1 byte len + case 0x42: // char string, 1 byte len + case 0x43: // octet string, 2 bytes len + case 0x44: // char string, 2 bytes len + // For strings, default is to try to do a real string, but reverts to octet stream if null char is present or on some exceptions + { + bool parse_as_string = true; + uint32_t len = (attrtype <= 0x42) ? buf.get8(i) : buf.get16(i); // len is 8 or 16 bits + i += (attrtype <= 0x42) ? 1 : 2; // increment pointer + + // check if we can safely use a string + if ((0x41 == attrtype) || (0x43 == attrtype)) { parse_as_string = false; } + else { + for (uint32_t j = 0; j < len; j++) { + if (0x00 == buf.get8(i+j)) { + parse_as_string = false; + break; + } + } + } + + if (parse_as_string) { + char str[len+1]; + strncpy(str, buf.charptr(i), len); + str[len] = 0x00; + json[attrid_str] = str; + } else { + // print as HEX + char hex[2*len+1]; + ToHex_P(buf.buf(i), len, hex, sizeof(hex)); + json[attrid_str] = hex; + } + + i += len; + break; + } + i += buf.get8(i) + 1; + break; + + case 0x08: // data8 + case 0x18: // map8 + { + uint8_t uint8_val = buf.get8(i); + i += 1; + json[attrid_str] = uint8_val; + } + break; + case 0x09: // data16 + case 0x19: // map16 + { + uint16_t uint16_val = buf.get16(i); + i += 2; + json[attrid_str] = uint16_val; + } + break; + case 0x0B: // data32 + case 0x1B: // map32 + { + uint32_t uint32_val = buf.get32(i); + i += 4; + json[attrid_str] = uint32_val; + } + break; + // enum + case 0x30: // enum8 + case 0x31: // enum16 + i += attrtype - 0x2F; + break; + + // TODO + case 0x39: // float + i += 4; + break; + + case 0xE0: // ToD + case 0xE1: // date + case 0xE2: // UTC + i += 4; + break; + + case 0xE8: // clusterId + case 0xE9: // attribId + i += 2; + break; + case 0xEA: // bacOID + i += 4; + break; + + case 0xF0: // EUI64 + i += 8; + break; + case 0xF1: // key128 + i += 16; + break; + + // Other un-implemented data types + case 0x0A: // data24 + case 0x0C: // data40 + case 0x0D: // data48 + case 0x0E: // data56 + case 0x0F: // data64 + i += attrtype - 0x07; // 2-8 + break; + // map + case 0x1A: // map24 + case 0x1C: // map40 + case 0x1D: // map48 + case 0x1E: // map56 + case 0x1F: // map64 + i += attrtype - 0x17; + break; + // semi + case 0x38: // semi (float on 2 bytes) + i += 2; + break; + case 0x3A: // double precision + i += 8; + break; + } + + // String pp; // pretty print + // json[attrid_str].prettyPrintTo(pp); + // // now store the attribute + // AddLog_P2(LOG_LEVEL_INFO, PSTR("ZIG: ZCL attribute decoded, id %s, type 0x%02X, val=%s"), + // attrid_str, attrtype, pp.c_str()); + return i - offset; // how much have we increased the index +} + + +// First pass, parse all attributes in their native format +void ZCLFrame::parseRawAttributes(JsonObject& json, uint8_t offset) { + uint32_t i = offset; + uint32_t len = _payload.len(); + + while (len - i >= 3) { + uint16_t attrid = _payload.get16(i); + i += 2; + + char key[16]; + snprintf_P(key, sizeof(key), PSTR("%04X/%04X"), + _cluster_id, attrid); + + // exception for Xiaomi lumi.weather - specific field to be treated as octet and not char + if ((0x0000 == _cluster_id) && (0xFF01 == attrid)) { + if (0x42 == _payload.get8(i)) { + _payload.set8(i, 0x41); // change type from 0x42 to 0x41 + } + } + i += parseSingleAttribute(json, key, _payload, i, len); + } +} + +// ZCL_READ_ATTRIBUTES_RESPONSE +void ZCLFrame::parseReadAttributes(JsonObject& json, uint8_t offset) { + uint32_t i = offset; + uint32_t len = _payload.len(); + + while (len - i >= 4) { + uint16_t attrid = _payload.get16(i); + i += 2; + uint8_t status = _payload.get8(i++); + + if (0 == status) { + char key[16]; + snprintf_P(key, sizeof(key), PSTR("%04X/%04X"), + _cluster_id, attrid); + + i += parseSingleAttribute(json, key, _payload, i, len); + } + } +} + + +// Parse non-normalized attributes +// The key is "s_" followed by 16 bits clusterId, "_" followed by 8 bits command id +void ZCLFrame::parseClusterSpecificCommand(JsonObject& json, uint8_t offset) { + uint32_t i = offset; + uint32_t len = _payload.len(); + + char attrid_str[12]; + snprintf_P(attrid_str, sizeof(attrid_str), PSTR("%04X!%02X"), _cmd_id, _cluster_id); + + char hex_char[_payload.len()*2+2]; + ToHex_P((unsigned char*)_payload.getBuffer(), _payload.len(), hex_char, sizeof(hex_char)); + + json[attrid_str] = hex_char; +} + +// return value: +// 0 = keep initial value +// 1 = remove initial value +typedef int32_t (*Z_AttrConverter)(uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper* new_name); +typedef struct Z_AttributeConverter { + uint16_t cluster; + uint16_t attribute; + const char * name; + Z_AttrConverter func; +} Z_AttributeConverter; + +// list of post-processing directives +const Z_AttributeConverter Z_PostProcess[] PROGMEM = { + // { 0x0000, 0x0004, "Manufacturer", &Z_ManufKeep }, // record Manufacturer + // { 0x0000, 0x0005, D_JSON_MODEL D_JSON_ID, &Z_ModelKeep }, // record Model + // { 0x0405, 0x0000, D_JSON_HUMIDITY, &Z_FloatDiv100 }, // Humidity + + { 0x0000, 0x0000, "ZCLVersion", &Z_Copy }, + { 0x0000, 0x0001, "AppVersion", &Z_Copy }, + { 0x0000, 0x0002, "StackVersion", &Z_Copy }, + { 0x0000, 0x0003, "HWVersion", &Z_Copy }, + { 0x0000, 0x0004, "Manufacturer", &Z_ManufKeep }, // record Manufacturer + { 0x0000, 0x0005, D_JSON_MODEL D_JSON_ID, &Z_ModelKeep }, // record Model + { 0x0000, 0x0006, "DateCode", &Z_Copy }, + { 0x0000, 0x0007, "PowerSource", &Z_Copy }, + { 0x0000, 0x4000, "SWBuildID", &Z_Copy }, + { 0x0000, 0xFFFF, nullptr, &Z_Remove }, // Remove all other values + + // Color Control cluster + { 0x0003, 0x0000, "CurrentHue", &Z_Copy }, + { 0x0003, 0x0001, "CurrentSaturation", &Z_Copy }, + { 0x0003, 0x0002, "RemainingTime", &Z_Copy }, + { 0x0003, 0x0003, "CurrentX", &Z_Copy }, + { 0x0003, 0x0004, "CurrentY", &Z_Copy }, + { 0x0003, 0x0005, "DriftCompensation", &Z_Copy }, + { 0x0003, 0x0006, "CompensationText", &Z_Copy }, + { 0x0003, 0x0007, "ColorTemperatureMireds",&Z_Copy }, + { 0x0003, 0x0008, "ColorMode", &Z_Copy }, + { 0x0003, 0x0010, "NumberOfPrimaries", &Z_Copy }, + { 0x0003, 0x0011, "Primary1X", &Z_Copy }, + { 0x0003, 0x0012, "Primary1Y", &Z_Copy }, + { 0x0003, 0x0013, "Primary1Intensity", &Z_Copy }, + { 0x0003, 0x0015, "Primary2X", &Z_Copy }, + { 0x0003, 0x0016, "Primary2Y", &Z_Copy }, + { 0x0003, 0x0017, "Primary2Intensity", &Z_Copy }, + { 0x0003, 0x0019, "Primary3X", &Z_Copy }, + { 0x0003, 0x001A, "Primary3Y", &Z_Copy }, + { 0x0003, 0x001B, "Primary3Intensity", &Z_Copy }, + { 0x0003, 0x0030, "WhitePointX", &Z_Copy }, + { 0x0003, 0x0031, "WhitePointY", &Z_Copy }, + { 0x0003, 0x0032, "ColorPointRX", &Z_Copy }, + { 0x0003, 0x0033, "ColorPointRY", &Z_Copy }, + { 0x0003, 0x0034, "ColorPointRIntensity", &Z_Copy }, + { 0x0003, 0x0036, "ColorPointGX", &Z_Copy }, + { 0x0003, 0x0037, "ColorPointGY", &Z_Copy }, + { 0x0003, 0x0038, "ColorPointGIntensity", &Z_Copy }, + { 0x0003, 0x003A, "ColorPointBX", &Z_Copy }, + { 0x0003, 0x003B, "ColorPointBY", &Z_Copy }, + { 0x0003, 0x003C, "ColorPointBIntensity", &Z_Copy }, + + // On/off cluster + { 0x0006, 0x0000, "Power", &Z_Copy }, + // On/Off Switch Configuration cluster + { 0x0007, 0x0000, "SwitchType", &Z_Copy }, + // Level Control cluster + { 0x0008, 0x0000, "CurrentLevel", &Z_Copy }, + { 0x0008, 0x0001, "RemainingTime", &Z_Copy }, + { 0x0008, 0x0010, "OnOffTransitionTime", &Z_Copy }, + { 0x0008, 0x0011, "OnLevel", &Z_Copy }, + { 0x0008, 0x0012, "OnTransitionTime", &Z_Copy }, + { 0x0008, 0x0013, "OffTransitionTime", &Z_Copy }, + { 0x0008, 0x0014, "DefaultMoveRate", &Z_Copy }, + // Alarms cluster + { 0x0009, 0x0000, "AlarmCount", &Z_Copy }, + // Time cluster + { 0x000A, 0x0000, "Time", &Z_Copy }, + { 0x000A, 0x0001, "TimeStatus", &Z_Copy }, + { 0x000A, 0x0002, "TimeZone", &Z_Copy }, + { 0x000A, 0x0003, "DstStart", &Z_Copy }, + { 0x000A, 0x0004, "DstStart", &Z_Copy }, + { 0x000A, 0x0005, "DstShift", &Z_Copy }, + { 0x000A, 0x0006, "StandardTime", &Z_Copy }, + { 0x000A, 0x0007, "LocalTime", &Z_Copy }, + { 0x000A, 0x0008, "LastSetTime", &Z_Copy }, + { 0x000A, 0x0009, "ValidUntilTime", &Z_Copy }, + // RSSI Location cluster + { 0x000B, 0x0000, "LocationType", &Z_Copy }, + { 0x000B, 0x0000, "LocationMethod", &Z_Copy }, + { 0x000B, 0x0000, "LocationAge", &Z_Copy }, + { 0x000B, 0x0000, "QualityMeasure", &Z_Copy }, + { 0x000B, 0x0000, "NumberOfDevices", &Z_Copy }, + // Analog Input cluster + { 0x000C, 0x0004, "ActiveText", &Z_Copy }, + { 0x000C, 0x001C, "Description", &Z_Copy }, + { 0x000C, 0x002E, "InactiveText", &Z_Copy }, + { 0x000C, 0x0041, "MaxPresentValue", &Z_Copy }, + { 0x000C, 0x0045, "MinPresentValue", &Z_Copy }, + { 0x000C, 0x0051, "OutOfService", &Z_Copy }, + { 0x000C, 0x0055, "PresentValue", &Z_Copy }, + { 0x000C, 0x0057, "PriorityArray", &Z_Copy }, + { 0x000C, 0x0067, "Reliability", &Z_Copy }, + { 0x000C, 0x0068, "RelinquishDefault", &Z_Copy }, + { 0x000C, 0x006A, "Resolution", &Z_Copy }, + { 0x000C, 0x006F, "StatusFlags", &Z_Copy }, + { 0x000C, 0x0075, "EngineeringUnits", &Z_Copy }, + { 0x000C, 0x0100, "ApplicationType", &Z_Copy }, + // Binary Output cluster + { 0x0010, 0x0004, "ActiveText", &Z_Copy }, + { 0x0010, 0x001C, "Description", &Z_Copy }, + { 0x0010, 0x002E, "InactiveText", &Z_Copy }, + { 0x0010, 0x0042, "MinimumOffTime", &Z_Copy }, + { 0x0010, 0x0043, "MinimumOnTime", &Z_Copy }, + { 0x0010, 0x0051, "OutOfService", &Z_Copy }, + { 0x0010, 0x0054, "Polarity", &Z_Copy }, + { 0x0010, 0x0055, "PresentValue", &Z_Copy }, + { 0x0010, 0x0057, "PriorityArray", &Z_Copy }, + { 0x0010, 0x0067, "Reliability", &Z_Copy }, + { 0x0010, 0x0068, "RelinquishDefault", &Z_Copy }, + { 0x0010, 0x006F, "StatusFlags", &Z_Copy }, + { 0x0010, 0x0100, "ApplicationType", &Z_Copy }, + // Binary Value cluster + { 0x0011, 0x0004, "ActiveText", &Z_Copy }, + { 0x0011, 0x001C, "Description", &Z_Copy }, + { 0x0011, 0x002E, "InactiveText", &Z_Copy }, + { 0x0011, 0x0042, "MinimumOffTime", &Z_Copy }, + { 0x0011, 0x0043, "MinimumOnTime", &Z_Copy }, + { 0x0011, 0x0051, "OutOfService", &Z_Copy }, + { 0x0011, 0x0055, "PresentValue", &Z_Copy }, + { 0x0011, 0x0057, "PriorityArray", &Z_Copy }, + { 0x0011, 0x0067, "Reliability", &Z_Copy }, + { 0x0011, 0x0068, "RelinquishDefault", &Z_Copy }, + { 0x0011, 0x006F, "StatusFlags", &Z_Copy }, + { 0x0011, 0x0100, "ApplicationType", &Z_Copy }, + // Multistate Input cluster + { 0x0012, 0x000E, "StateText", &Z_Copy }, + { 0x0012, 0x001C, "Description", &Z_Copy }, + { 0x0012, 0x004A, "NumberOfStates", &Z_Copy }, + { 0x0012, 0x0051, "OutOfService", &Z_Copy }, + { 0x0012, 0x0055, "PresentValue", &Z_Copy }, + { 0x0012, 0x0067, "Reliability", &Z_Copy }, + { 0x0012, 0x006F, "StatusFlags", &Z_Copy }, + { 0x0012, 0x0100, "ApplicationType", &Z_Copy }, + // Multistate output + { 0x0013, 0x000E, "StateText", &Z_Copy }, + { 0x0013, 0x001C, "Description", &Z_Copy }, + { 0x0013, 0x004A, "NumberOfStates", &Z_Copy }, + { 0x0013, 0x0051, "OutOfService", &Z_Copy }, + { 0x0013, 0x0055, "PresentValue", &Z_Copy }, + { 0x0013, 0x0057, "PriorityArray", &Z_Copy }, + { 0x0013, 0x0067, "Reliability", &Z_Copy }, + { 0x0013, 0x0068, "RelinquishDefault", &Z_Copy }, + { 0x0013, 0x006F, "StatusFlags", &Z_Copy }, + { 0x0013, 0x0100, "ApplicationType", &Z_Copy }, + // Multistate Value cluster + { 0x0014, 0x000E, "StateText", &Z_Copy }, + { 0x0014, 0x001C, "Description", &Z_Copy }, + { 0x0014, 0x004A, "NumberOfStates", &Z_Copy }, + { 0x0014, 0x0051, "OutOfService", &Z_Copy }, + { 0x0014, 0x0055, "PresentValue", &Z_Copy }, + { 0x0014, 0x0067, "Reliability", &Z_Copy }, + { 0x0014, 0x0068, "RelinquishDefault", &Z_Copy }, + { 0x0014, 0x006F, "StatusFlags", &Z_Copy }, + { 0x0014, 0x0100, "ApplicationType", &Z_Copy }, + // Diagnostics cluster + { 0x0B05, 0x0000, "NumberOfResets", &Z_Copy }, + { 0x0B05, 0x0001, "PersistentMemoryWrites",&Z_Copy }, + { 0x0B05, 0x011C, "LastMessageLQI", &Z_Copy }, + { 0x0B05, 0x011D, "LastMessageRSSI", &Z_Copy }, + // Poll Control cluster + { 0x0020, 0x0000, "CheckinInterval", &Z_Copy }, + { 0x0020, 0x0001, "LongPollInterval", &Z_Copy }, + { 0x0020, 0x0002, "ShortPollInterval", &Z_Copy }, + { 0x0020, 0x0003, "FastPollTimeout", &Z_Copy }, + { 0x0020, 0x0004, "CheckinIntervalMin", &Z_Copy }, + { 0x0020, 0x0005, "LongPollIntervalMin", &Z_Copy }, + { 0x0020, 0x0006, "FastPollTimeoutMax", &Z_Copy }, + // Shade Configuration cluster + { 0x0100, 0x0000, "PhysicalClosedLimit", &Z_Copy }, + { 0x0100, 0x0001, "MotorStepSize", &Z_Copy }, + { 0x0100, 0x0002, "Status", &Z_Copy }, + { 0x0100, 0x0010, "ClosedLimit", &Z_Copy }, + { 0x0100, 0x0011, "Mode", &Z_Copy }, + // Door Lock cluster + { 0x0101, 0x0000, "LockState", &Z_Copy }, + { 0x0101, 0x0001, "LockType", &Z_Copy }, + { 0x0101, 0x0002, "ActuatorEnabled", &Z_Copy }, + { 0x0101, 0x0003, "DoorState", &Z_Copy }, + { 0x0101, 0x0004, "DoorOpenEvents", &Z_Copy }, + { 0x0101, 0x0005, "DoorClosedEvents", &Z_Copy }, + { 0x0101, 0x0006, "OpenPeriod", &Z_Copy }, + // Window Covering cluster + { 0x0102, 0x0000, "WindowCoveringType", &Z_Copy }, + { 0x0102, 0x0001, "PhysicalClosedLimitLift",&Z_Copy }, + { 0x0102, 0x0002, "PhysicalClosedLimitTilt",&Z_Copy }, + { 0x0102, 0x0003, "CurrentPositionLift", &Z_Copy }, + { 0x0102, 0x0004, "CurrentPositionTilt", &Z_Copy }, + { 0x0102, 0x0005, "NumberofActuationsLift",&Z_Copy }, + { 0x0102, 0x0006, "NumberofActuationsTilt",&Z_Copy }, + { 0x0102, 0x0007, "ConfigStatus", &Z_Copy }, + { 0x0102, 0x0008, "CurrentPositionLiftPercentage",&Z_Copy }, + { 0x0102, 0x0009, "CurrentPositionTiltPercentage",&Z_Copy }, + { 0x0102, 0x0010, "InstalledOpenLimitLift",&Z_Copy }, + { 0x0102, 0x0011, "InstalledClosedLimitLift",&Z_Copy }, + { 0x0102, 0x0012, "InstalledOpenLimitTilt", &Z_Copy }, + { 0x0102, 0x0013, "InstalledClosedLimitTilt", &Z_Copy }, + { 0x0102, 0x0014, "VelocityLift",&Z_Copy }, + { 0x0102, 0x0015, "AccelerationTimeLift",&Z_Copy }, + { 0x0102, 0x0016, "DecelerationTimeLift", &Z_Copy }, + { 0x0102, 0x0017, "Mode",&Z_Copy }, + { 0x0102, 0x0018, "IntermediateSetpointsLift",&Z_Copy }, + { 0x0102, 0x0019, "IntermediateSetpointsTilt",&Z_Copy }, + + // Power Profile cluster + { 0x001A, 0x0000, "TotalProfileNum", &Z_Copy }, + { 0x001A, 0x0001, "MultipleScheduling", &Z_Copy }, + { 0x001A, 0x0002, "EnergyFormatting", &Z_Copy }, + { 0x001A, 0x0003, "EnergyRemote", &Z_Copy }, + { 0x001A, 0x0004, "ScheduleMode", &Z_Copy }, + // Meter Identification cluster + { 0x0B01, 0x0000, "CompanyName", &Z_Copy }, + { 0x0B01, 0x0001, "MeterTypeID", &Z_Copy }, + { 0x0B01, 0x0004, "DataQualityID", &Z_Copy }, + { 0x0B01, 0x0005, "CustomerName", &Z_Copy }, + { 0x0B01, 0x0006, "Model", &Z_Copy }, + { 0x0B01, 0x0007, "PartNumber", &Z_Copy }, + { 0x0B01, 0x000A, "SoftwareRevision", &Z_Copy }, + { 0x0B01, 0x000C, "POD", &Z_Copy }, + { 0x0B01, 0x000D, "AvailablePower", &Z_Copy }, + { 0x0B01, 0x000E, "PowerThreshold", &Z_Copy }, + + { 0x0400, 0x0000, D_JSON_ILLUMINANCE, &Z_Copy }, // Illuminance (in Lux) + { 0x0400, 0x0001, "MinMeasuredValue", &Z_Copy }, // + { 0x0400, 0x0002, "MaxMeasuredValue", &Z_Copy }, // + { 0x0400, 0x0003, "Tolerance", &Z_Copy }, // + { 0x0400, 0x0004, "LightSensorType", &Z_Copy }, // + { 0x0400, 0xFFFF, nullptr, &Z_Remove }, // Remove all other values + + { 0x0401, 0x0000, "LevelStatus", &Z_Copy }, // Illuminance (in Lux) + { 0x0401, 0x0001, "LightSensorType", &Z_Copy }, // LightSensorType + { 0x0401, 0xFFFF, nullptr, &Z_Remove }, // Remove all other values + + { 0x0402, 0x0000, D_JSON_TEMPERATURE, &Z_FloatDiv100 }, // Temperature + { 0x0402, 0x0001, "MinMeasuredValue", &Z_FloatDiv100 }, // + { 0x0402, 0x0002, "MaxMeasuredValue", &Z_FloatDiv100 }, // + { 0x0402, 0x0003, "Tolerance", &Z_FloatDiv100 }, // + { 0x0402, 0xFFFF, nullptr, &Z_Remove }, // Remove all other values + + { 0x0403, 0x0000, D_JSON_PRESSURE_UNIT, &Z_AddPressureUnit }, // Pressure Unit + { 0x0403, 0x0000, D_JSON_PRESSURE, &Z_Copy }, // Pressure + { 0x0403, 0x0001, "MinMeasuredValue", &Z_Copy }, // + { 0x0403, 0x0002, "MaxMeasuredValue", &Z_Copy }, // + { 0x0403, 0x0003, "Tolerance", &Z_Copy }, // + { 0x0403, 0x0010, "ScaledValue", &Z_Copy }, // + { 0x0403, 0x0011, "MinScaledValue", &Z_Copy }, // + { 0x0403, 0x0012, "MaxScaledValue", &Z_Copy }, // + { 0x0403, 0x0013, "ScaledTolerance", &Z_Copy }, // + { 0x0403, 0x0014, "Scale", &Z_Copy }, // + { 0x0403, 0xFFFF, nullptr, &Z_Remove }, // Remove all other Pressure values + + { 0x0404, 0x0000, D_JSON_FLOWRATE, &Z_FloatDiv10 }, // Flow (in m3/h) + { 0x0404, 0x0001, "MinMeasuredValue", &Z_Copy }, // + { 0x0404, 0x0002, "MaxMeasuredValue", &Z_Copy }, // + { 0x0404, 0x0003, "Tolerance", &Z_Copy }, // + { 0x0404, 0xFFFF, nullptr, &Z_Remove }, // Remove all other values + + { 0x0405, 0x0000, D_JSON_HUMIDITY, &Z_FloatDiv100 }, // Humidity + { 0x0405, 0x0001, "MinMeasuredValue", &Z_Copy }, // + { 0x0405, 0x0002, "MaxMeasuredValue", &Z_Copy }, // + { 0x0405, 0x0003, "Tolerance", &Z_Copy }, // + { 0x0405, 0xFFFF, nullptr, &Z_Remove }, // Remove all other values + + { 0x0406, 0x0000, "Occupancy", &Z_Copy }, // Occupancy (map8) + { 0x0406, 0x0001, "OccupancySensorType", &Z_Copy }, // OccupancySensorType + { 0x0406, 0xFFFF, nullptr, &Z_Remove }, // Remove all other values + + // Cmd 0x0A - Cluster 0x0000, attribute 0xFF01 - proprietary + { 0x0000, 0xFF01, nullptr, &Z_AqaraSensor }, // Occupancy (map8) + +}; + +// ====================================================================== +// Record Manuf +int32_t Z_ManufKeep(uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name) { + json[new_name] = value; + zigbee_devices.setManufId(shortaddr, value.as()); + return 1; +} +// +int32_t Z_ModelKeep(uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name) { + json[new_name] = value; + zigbee_devices.setModelId(shortaddr, value.as()); + return 1; +} + +// ====================================================================== +// Remove attribute +int32_t Z_Remove(uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name) { + return 1; // remove original key +} + +// Copy value as-is +int32_t Z_Copy(uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name) { + json[new_name] = value; + return 1; // remove original key +} + +// Add pressure unit +int32_t Z_AddPressureUnit(uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name) { + json[new_name] = F(D_UNIT_PRESSURE); + return 0; // keep original key +} + +// Convert int to float and divide by 100 +int32_t Z_FloatDiv100(uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name) { + json[new_name] = ((float)value) / 100.0f; + return 1; // remove original key +} +// Convert int to float and divide by 10 +int32_t Z_FloatDiv10(uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name) { + json[new_name] = ((float)value) / 10.0f; + return 1; // remove original key +} + +int32_t Z_AqaraSensor(uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const __FlashStringHelper *new_name) { + String hex = value; + SBuffer buf2 = SBuffer::SBufferFromHex(hex.c_str(), hex.length()); + uint32_t i = 0; + uint32_t len = buf2.len(); + char tmp[] = "tmp"; // for obscure reasons, it must be converted from const char* to char*, otherwise ArduinoJson gets confused + + JsonVariant sub_value; + + while (len - i >= 2) { + uint8_t attrid = buf2.get8(i++); + + i += parseSingleAttribute(json, tmp, buf2, i, len); + float val = json[tmp]; + json.remove(tmp); + if (0x64 == attrid) { + json[F(D_JSON_TEMPERATURE)] = val / 100.0f; + } else if (0x65 == attrid) { + json[F(D_JSON_HUMIDITY)] = val / 100.0f; + } else if (0x66 == attrid) { + json[F(D_JSON_PRESSURE)] = val / 100.0f; + json[F(D_JSON_PRESSURE_UNIT)] = F(D_UNIT_PRESSURE); // hPa + } else if (0x01 == attrid) { + json[F(D_JSON_VOLTAGE)] = val / 1000.0f; + json[F("Battery")] = toPercentageCR2032(val); + } + } + return 1; // remove original key +} +// ====================================================================== + +// Cluster Specific commands +// #define ZCL_OO_OFF "s_0006_00" // Cluster 0x0006, cmd 0x00 - On/Off - Off +// #define ZCL_OO_ON "s_0006_01" // Cluster 0x0006, cmd 0x01 - On/Off - On +// #define ZCL_COLORTEMP_MOVE "s_0300_0A" // Cluster 0x0300, cmd 0x0A, Move to Color Temp +// #define ZCL_LC_MOVE "s_0008_00" // Cluster 0x0008, cmd 0x00, Level Control Move to Level +// #define ZCL_LC_MOVE_1 "s_0008_01" // Cluster 0x0008, cmd 0x01, Level Control Move +// #define ZCL_LC_STEP "s_0008_02" // Cluster 0x0008, cmd 0x02, Level Control Step +// #define ZCL_LC_STOP "s_0008_03" // Cluster 0x0008, cmd 0x03, Level Control Stop +// #define ZCL_LC_MOVE_WOO "s_0008_04" // Cluster 0x0008, cmd 0x04, Level Control Move to Level, with On/Off +// #define ZCL_LC_MOVE_1_WOO "s_0008_05" // Cluster 0x0008, cmd 0x05, Level Control Move, with On/Off +// #define ZCL_LC_STEP_WOO "s_0008_06" // Cluster 0x0008, cmd 0x05, Level Control Step, with On/Off +// #define ZCL_LC_STOP_WOO "s_0008_07" // Cluster 0x0008, cmd 0x07, Level Control Stop + + +void ZCLFrame::postProcessAttributes(uint16_t shortaddr, JsonObject& json) { + // iterate on json elements + for (auto kv : json) { + String key_string = kv.key; + const char * key = key_string.c_str(); + JsonVariant& value = kv.value; + // Check that format looks like "CCCC/AAAA" + char * delimiter = strchr(key, '/'); + if (delimiter) { + uint16_t cluster = strtoul(key, &delimiter, 16); + uint16_t attribute = strtoul(delimiter+1, nullptr, 16); + + // Iterate on filter + for (uint32_t i = 0; i < sizeof(Z_PostProcess) / sizeof(Z_PostProcess[0]); i++) { + const Z_AttributeConverter *converter = &Z_PostProcess[i]; + uint16_t conv_cluster = pgm_read_word(&converter->cluster); + uint16_t conv_attribute = pgm_read_word(&converter->attribute); + + if ((conv_cluster == cluster) && + ((conv_attribute == attribute) || (conv_attribute == 0xFFFF)) ) { + int32_t drop = (*converter->func)(shortaddr, json, key, value, (const __FlashStringHelper*) converter->name); + if (drop) { + json.remove(key); + } + + } + } + } + } +} + +//void ZCLFrame::postProcessAttributes2(JsonObject& json) { +// void postProcessAttributes2(JsonObject& json) { +// const __FlashStringHelper *key; +// +// // Osram Mini Switch +// key = F(ZCL_OO_OFF); +// if (json.containsKey(key)) { +// json.remove(key); +// json[F(D_CMND_POWER)] = F("Off"); +// } +// key = F(ZCL_OO_ON); +// if (json.containsKey(key)) { +// json.remove(key); +// json[F(D_CMND_POWER)] = F("On"); +// } +// key = F(ZCL_COLORTEMP_MOVE); +// if (json.containsKey(key)) { +// String hex = json[key]; +// SBuffer buf2 = SBuffer::SBufferFromHex(hex.c_str(), hex.length()); +// uint16_t color_temp = buf2.get16(0); +// uint16_t transition_time = buf2.get16(2); +// json.remove(key); +// json[F("ColorTemp")] = color_temp; +// json[F("TransitionTime")] = transition_time / 10.0f; +// } +// key = F(ZCL_LC_MOVE_WOO); +// if (json.containsKey(key)) { +// String hex = json[key]; +// SBuffer buf2 = SBuffer::SBufferFromHex(hex.c_str(), hex.length()); +// uint8_t level = buf2.get8(0); +// uint16_t transition_time = buf2.get16(1); +// json.remove(key); +// json[F("Dimmer")] = changeUIntScale(level, 0, 255, 0, 100); // change to percentage +// json[F("TransitionTime")] = transition_time / 10.0f; +// if (0 == level) { +// json[F(D_CMND_POWER)] = F("Off"); +// } else { +// json[F(D_CMND_POWER)] = F("On"); +// } +// } +// key = F(ZCL_LC_MOVE); +// if (json.containsKey(key)) { +// String hex = json[key]; +// SBuffer buf2 = SBuffer::SBufferFromHex(hex.c_str(), hex.length()); +// uint8_t level = buf2.get8(0); +// uint16_t transition_time = buf2.get16(1); +// json.remove(key); +// json[F("Dimmer")] = changeUIntScale(level, 0, 255, 0, 100); // change to percentage +// json[F("TransitionTime")] = transition_time / 10.0f; +// } +// key = F(ZCL_LC_MOVE_1); +// if (json.containsKey(key)) { +// String hex = json[key]; +// SBuffer buf2 = SBuffer::SBufferFromHex(hex.c_str(), hex.length()); +// uint8_t move_mode = buf2.get8(0); +// uint8_t move_rate = buf2.get8(1); +// json.remove(key); +// json[F("Move")] = move_mode ? F("Down") : F("Up"); +// json[F("Rate")] = move_rate; +// } +// key = F(ZCL_LC_MOVE_1_WOO); +// if (json.containsKey(key)) { +// String hex = json[key]; +// SBuffer buf2 = SBuffer::SBufferFromHex(hex.c_str(), hex.length()); +// uint8_t move_mode = buf2.get8(0); +// uint8_t move_rate = buf2.get8(1); +// json.remove(key); +// json[F("Move")] = move_mode ? F("Down") : F("Up"); +// json[F("Rate")] = move_rate; +// if (0 == move_mode) { +// json[F(D_CMND_POWER)] = F("On"); +// } +// } +// key = F(ZCL_LC_STEP); +// if (json.containsKey(key)) { +// String hex = json[key]; +// SBuffer buf2 = SBuffer::SBufferFromHex(hex.c_str(), hex.length()); +// uint8_t step_mode = buf2.get8(0); +// uint8_t step_size = buf2.get8(1); +// uint16_t transition_time = buf2.get16(2); +// json.remove(key); +// json[F("Step")] = step_mode ? F("Down") : F("Up"); +// json[F("StepSize")] = step_size; +// json[F("TransitionTime")] = transition_time / 10.0f; +// } +// key = F(ZCL_LC_STEP_WOO); +// if (json.containsKey(key)) { +// String hex = json[key]; +// SBuffer buf2 = SBuffer::SBufferFromHex(hex.c_str(), hex.length()); +// uint8_t step_mode = buf2.get8(0); +// uint8_t step_size = buf2.get8(1); +// uint16_t transition_time = buf2.get16(2); +// json.remove(key); +// json[F("Step")] = step_mode ? F("Down") : F("Up"); +// json[F("StepSize")] = step_size; +// json[F("TransitionTime")] = transition_time / 10.0f; +// if (0 == step_mode) { +// json[F(D_CMND_POWER)] = F("On"); +// } +// } +// key = F(ZCL_LC_STOP); +// if (json.containsKey(key)) { +// json.remove(key); +// json[F("Stop")] = 1; +// } +// key = F(ZCL_LC_STOP_WOO); +// if (json.containsKey(key)) { +// json.remove(key); +// json[F("Stop")] = 1; +// } +// +// // Lumi.weather proprietary field +// key = F(ZCL_LUMI_WEATHER); +// if (json.containsKey(key)) { +// String hex = json[key]; +// SBuffer buf2 = SBuffer::SBufferFromHex(hex.c_str(), hex.length()); +// DynamicJsonBuffer jsonBuffer; +// JsonObject& json_lumi = jsonBuffer.createObject(); +// uint32_t i = 0; +// uint32_t len = buf2.len(); +// char shortaddr[8]; +// +// while (len - i >= 2) { +// uint8_t attrid = buf2.get8(i++); +// +// snprintf_P(shortaddr, sizeof(shortaddr), PSTR("0x%02X"), attrid); +// +// //json[shortaddr] = parseSingleAttribute(json_lumi, buf2, i, len, nullptr, 0); +// } +// // parse output +// if (json_lumi.containsKey("0x64")) { // Temperature +// int32_t temperature = json_lumi["0x64"]; +// json[F(D_JSON_TEMPERATURE)] = temperature / 100.0f; +// } +// if (json_lumi.containsKey("0x65")) { // Humidity +// uint32_t humidity = json_lumi["0x65"]; +// json[F(D_JSON_HUMIDITY)] = humidity / 100.0f; +// } +// if (json_lumi.containsKey("0x66")) { // Pressure +// int32_t pressure = json_lumi["0x66"]; +// json[F(D_JSON_PRESSURE)] = pressure / 100.0f; +// json[F(D_JSON_PRESSURE_UNIT)] = F(D_UNIT_PRESSURE); // hPa +// } +// if (json_lumi.containsKey("0x01")) { // Battery Voltage +// uint32_t voltage = json_lumi["0x01"]; +// json[F(D_JSON_VOLTAGE)] = voltage / 1000.0f; +// json[F("Battery")] = toPercentageCR2032(voltage); +// } +// json.remove(key); +// } +// +// } + +#endif // USE_ZIGBEE diff --git a/sonoff/xdrv_23_zigbee_6_commands.ino b/sonoff/xdrv_23_zigbee_6_commands.ino new file mode 100644 index 000000000..017d2cd98 --- /dev/null +++ b/sonoff/xdrv_23_zigbee_6_commands.ino @@ -0,0 +1,92 @@ +/* + xdrv_23_zigbee_converters.ino - zigbee support for Sonoff-Tasmota + + Copyright (C) 2019 Theo Arends and Stephan Hadinger + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef USE_ZIGBEE + +//typedef int32_t (*Z_AttrConverter)(uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const char *new_name, void * param); +typedef struct Z_CommandConverter { + const char * tasmota_cmd; + const char * zcl_cmd; +} Z_CommandConverter; + +// list of post-processing directives +const Z_CommandConverter Z_Commands[] = { + { "Power", "0006!xx" }, // 0=Off, 1=On, 2=Toggle + { "Dimmer", "0008!04/xx0A00" }, // Move to Level with On/Off, xx=0..254 (255 is invalid) + { "Dimmer+", "0008!06/001902" }, // Step up by 10%, 0.2 secs + { "Dimmer-", "0008!06/011902" }, // Step down by 10%, 0.2 secs + { "Hue", "0300!00/xx000A00" }, // Move to Hue, shortest time, 1s + { "Sat", "0300!03/xx0A00" }, // Move to Sat + { "HueSat", "0300!06/xxyy0A00" }, // Hue, Sat + { "Color", "0300!07/xxxxyyyy0A00" }, // x, y (uint16) + { "CT", "0300!0A/xxxx0A00" }, // Color Temperature Mireds (uint16) + { "ShutterOpen", "0102!00"}, + { "ShutterClose", "0102!01"}, + { "ShutterStop", "0102!02"}, + { "ShutterLift", "0102!05xx"}, // Lift percentage, 0%=open, 100%=closed + { "ShutterTilt", "0102!08xx"}, // Tilt percentage +}; + +inline bool isXYZ(char c) { + return (c >= 'x') && (c <= 'z'); +} + +// take the lower 4 bits and turn it to an hex char +inline char hexDigit(uint32_t h) { + uint32_t nybble = h & 0x0F; + return (nybble > 9) ? 'A' - 10 + nybble : '0' + nybble; +} + +// replace all xx/yy/zz substrings with unsigned ints, and the corresponding len (8, 16 or 32 bits) +// zcl_cmd can be in PROGMEM +String SendZCLCommand_P(const char *zcl_cmd_P, uint32_t x, uint32_t y, uint32_t z) { + size_t len = strlen_P(zcl_cmd_P); + char zcl_cmd[len+1]; + strcpy_P(zcl_cmd, zcl_cmd_P); // copy into RAM + + char *p = zcl_cmd; + while (*p) { + if (isXYZ(*p) && (*p == *(p+1))) { // if char is [x-z] and followed by same char + uint8_t val; + switch (*p) { + case 'x': + val = x & 0xFF; + x = x >> 8; + break; + case 'y': + val = y & 0xFF; + y = y >> 8; + break; + case 'z': + val = z & 0xFF; + z = z >> 8; + break; + } + *p = hexDigit(val >> 4); + *(p+1) = hexDigit(val); + p++; + } + p++; + } + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SendZCLCommand_P: zcl_cmd = %s"), zcl_cmd); + + return String(zcl_cmd); +} + +#endif // USE_ZIGBEE diff --git a/sonoff/xdrv_23_zigbee_7_statemachine.ino b/sonoff/xdrv_23_zigbee_7_statemachine.ino new file mode 100644 index 000000000..844086bce --- /dev/null +++ b/sonoff/xdrv_23_zigbee_7_statemachine.ino @@ -0,0 +1,642 @@ +/* + xdrv_23_zigbee.ino - zigbee support for Sonoff-Tasmota + + Copyright (C) 2019 Theo Arends and Stephan Hadinger + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef USE_ZIGBEE + +// Status code used for ZigbeeStatus MQTT message +// Ex: {"ZigbeeStatus":{"Status": 3,"Message":"Configured, starting coordinator"}} +const uint8_t ZIGBEE_STATUS_OK = 0; // Zigbee started and working +const uint8_t ZIGBEE_STATUS_BOOT = 1; // CC2530 booting +const uint8_t ZIGBEE_STATUS_RESET_CONF = 2; // Resetting CC2530 configuration +const uint8_t ZIGBEE_STATUS_STARTING = 3; // Starting CC2530 as coordinator +const uint8_t ZIGBEE_STATUS_PERMITJOIN_CLOSE = 20; // Disable PermitJoin +const uint8_t ZIGBEE_STATUS_PERMITJOIN_OPEN_60 = 21; // Enable PermitJoin for 60 seconds +const uint8_t ZIGBEE_STATUS_PERMITJOIN_OPEN_XX = 22; // Enable PermitJoin until next boot +const uint8_t ZIGBEE_STATUS_DEVICE_ANNOUNCE = 30; // Device announces its address +const uint8_t ZIGBEE_STATUS_NODE_DESC = 31; // Node descriptor +const uint8_t ZIGBEE_STATUS_ACTIVE_EP = 32; // Endpoints descriptor +const uint8_t ZIGBEE_STATUS_SIMPLE_DESC = 33; // Simple Descriptor (clusters) +const uint8_t ZIGBEE_STATUS_CC_VERSION = 50; // Status: CC2530 ZNP Version +const uint8_t ZIGBEE_STATUS_CC_INFO = 51; // Status: CC2530 Device Configuration +const uint8_t ZIGBEE_STATUS_UNSUPPORTED_VERSION = 98; // Unsupported ZNP version +const uint8_t ZIGBEE_STATUS_ABORT = 99; // Fatal error, Zigbee not working + +typedef int32_t (*ZB_Func)(uint8_t value); +typedef int32_t (*ZB_RecvMsgFunc)(int32_t res, const class SBuffer &buf); + +typedef union Zigbee_Instruction { + struct { + uint8_t i; // instruction + uint8_t d8; // 8 bits data + uint16_t d16; // 16 bits data + } i; + const void *p; // pointer + // const void *m; // for type checking only, message + // const ZB_Func f; + // const ZB_RecvMsgFunc fr; +} Zigbee_Instruction; +// +// Zigbee_Instruction z1 = { .i = {1,2,3}}; +// Zigbee_Instruction z3 = { .p = nullptr }; + +typedef struct Zigbee_Instruction_Type { + uint8_t instr; + uint8_t data; +} Zigbee_Instruction_Type; + +enum Zigbee_StateMachine_Instruction_Set { + // 2 bytes instructions + ZGB_INSTR_4_BYTES = 0, + ZGB_INSTR_NOOP = 0, // do nothing + ZGB_INSTR_LABEL, // define a label + ZGB_INSTR_GOTO, // goto label + ZGB_INSTR_ON_ERROR_GOTO, // goto label if error + ZGB_INSTR_ON_TIMEOUT_GOTO, // goto label if timeout + ZGB_INSTR_WAIT, // wait for x ms (in chunks of 100ms) + ZGB_INSTR_WAIT_FOREVER, // wait forever but state machine still active + ZGB_INSTR_STOP, // stop state machine with optional error code + + // 6 bytes instructions + ZGB_INSTR_8_BYTES = 0x80, + ZGB_INSTR_CALL = 0x80, // call a function + ZGB_INSTR_LOG, // log a message, if more detailed logging required, call a function + ZGB_INSTR_MQTT_STATUS, // send MQTT status string with code + ZGB_INSTR_SEND, // send a ZNP message + ZGB_INSTR_WAIT_UNTIL, // wait until the specified message is received, ignore all others + ZGB_INSTR_WAIT_RECV, // wait for a message according to the filter + ZGB_ON_RECV_UNEXPECTED, // function to handle unexpected messages, or nullptr + + // 10 bytes instructions + ZGB_INSTR_12_BYTES = 0xF0, + ZGB_INSTR_WAIT_RECV_CALL, // wait for a filtered message and call function upon receive +}; + +#define ZI_NOOP() { .i = { ZGB_INSTR_NOOP, 0x00, 0x0000} }, +#define ZI_LABEL(x) { .i = { ZGB_INSTR_LABEL, (x), 0x0000} }, +#define ZI_GOTO(x) { .i = { ZGB_INSTR_GOTO, (x), 0x0000} }, +#define ZI_ON_ERROR_GOTO(x) { .i = { ZGB_INSTR_ON_ERROR_GOTO, (x), 0x0000} }, +#define ZI_ON_TIMEOUT_GOTO(x) { .i = { ZGB_INSTR_ON_TIMEOUT_GOTO, (x), 0x0000} }, +#define ZI_WAIT(x) { .i = { ZGB_INSTR_WAIT, 0x00, (x)} }, +#define ZI_WAIT_FOREVER() { .i = { ZGB_INSTR_WAIT_FOREVER, 0x00, 0x0000} }, +#define ZI_STOP(x) { .i = { ZGB_INSTR_STOP, (x), 0x0000} }, + +#define ZI_CALL(f, x) { .i = { ZGB_INSTR_CALL, (x), 0x0000} }, { .p = (const void*)(f) }, +#define ZI_LOG(x, m) { .i = { ZGB_INSTR_LOG, (x), 0x0000 } }, { .p = ((const void*)(m)) }, +#define ZI_MQTT_STATUS(x, m) { .i = { ZGB_INSTR_MQTT_STATUS, (x), 0x0000 } }, { .p = ((const void*)(m)) }, +#define ZI_ON_RECV_UNEXPECTED(f) { .i = { ZGB_ON_RECV_UNEXPECTED, 0x00, 0x0000} }, { .p = (const void*)(f) }, +#define ZI_SEND(m) { .i = { ZGB_INSTR_SEND, sizeof(m), 0x0000} }, { .p = (const void*)(m) }, +#define ZI_WAIT_RECV(x, m) { .i = { ZGB_INSTR_WAIT_RECV, sizeof(m), (x)} }, { .p = (const void*)(m) }, +#define ZI_WAIT_UNTIL(x, m) { .i = { ZGB_INSTR_WAIT_UNTIL, sizeof(m), (x)} }, { .p = (const void*)(m) }, +#define ZI_WAIT_RECV_FUNC(x, m, f) { .i = { ZGB_INSTR_WAIT_RECV_CALL, sizeof(m), (x)} }, { .p = (const void*)(m) }, { .p = (const void*)(f) }, + +// Labels used in the State Machine -- internal only +const uint8_t ZIGBEE_LABEL_START = 10; // Start ZNP +const uint8_t ZIGBEE_LABEL_READY = 20; // goto label 20 for main loop +const uint8_t ZIGBEE_LABEL_MAIN_LOOP = 21; // main loop +const uint8_t ZIGBEE_LABEL_PERMIT_JOIN_CLOSE = 30; // disable permit join +const uint8_t ZIGBEE_LABEL_PERMIT_JOIN_OPEN_60 = 31; // enable permit join for 60 seconds +const uint8_t ZIGBEE_LABEL_PERMIT_JOIN_OPEN_XX = 32; // enable permit join for 60 seconds +// errors +const uint8_t ZIGBEE_LABEL_ABORT = 99; // goto label 99 in case of fatal error +const uint8_t ZIGBEE_LABEL_UNSUPPORTED_VERSION = 98; // Unsupported ZNP version + +struct ZigbeeStatus { + bool active = true; // is Zigbee active for this device, i.e. GPIOs configured + bool state_machine = false; // the state machine is running + bool state_waiting = false; // the state machine is waiting for external event or timeout + bool state_no_timeout = false; // the current wait loop does not generate a timeout but only continues running + bool ready = false; // cc2530 initialization is complet, ready to operate + uint8_t on_error_goto = ZIGBEE_LABEL_ABORT; // on error goto label, 99 default to abort + uint8_t on_timeout_goto = ZIGBEE_LABEL_ABORT; // on timeout goto label, 99 default to abort + int16_t pc = 0; // program counter, -1 means abort + uint32_t next_timeout = 0; // millis for the next timeout + + uint8_t *recv_filter = nullptr; // receive filter message + bool recv_until = false; // ignore all messages until the received frame fully matches + size_t recv_filter_len = 0; + ZB_RecvMsgFunc recv_func = nullptr; // function to call when message is expected + ZB_RecvMsgFunc recv_unexpected = nullptr; // function called when unexpected message is received + + bool init_phase = true; // initialization phase, before accepting zigbee traffic +}; +struct ZigbeeStatus zigbee; + +SBuffer *zigbee_buffer = nullptr; + +/*********************************************************************************************\ + * State Machine +\*********************************************************************************************/ + +#define Z_B0(a) (uint8_t)( ((a) ) & 0xFF ) +#define Z_B1(a) (uint8_t)( ((a) >> 8) & 0xFF ) +#define Z_B2(a) (uint8_t)( ((a) >> 16) & 0xFF ) +#define Z_B3(a) (uint8_t)( ((a) >> 24) & 0xFF ) +#define Z_B4(a) (uint8_t)( ((a) >> 32) & 0xFF ) +#define Z_B5(a) (uint8_t)( ((a) >> 40) & 0xFF ) +#define Z_B6(a) (uint8_t)( ((a) >> 48) & 0xFF ) +#define Z_B7(a) (uint8_t)( ((a) >> 56) & 0xFF ) +// Macro to define message to send and receive +#define ZBM(n, x...) const uint8_t n[] PROGMEM = { x }; + +#define USE_ZIGBEE_CHANNEL_MASK (1 << (USE_ZIGBEE_CHANNEL)) + +// ZBS_* Zigbee Send +// ZBR_* Zigbee Recv +ZBM(ZBS_RESET, Z_AREQ | Z_SYS, SYS_RESET, 0x00 ) // 410001 SYS_RESET_REQ Hardware reset +ZBM(ZBR_RESET, Z_AREQ | Z_SYS, SYS_RESET_IND ) // 4180 SYS_RESET_REQ Hardware reset response + +ZBM(ZBS_VERSION, Z_SREQ | Z_SYS, SYS_VERSION ) // 2102 Z_SYS:version +ZBM(ZBR_VERSION, Z_SRSP | Z_SYS, SYS_VERSION ) // 6102 Z_SYS:version + +// Check if ZNP_HAS_CONFIGURED is set +ZBM(ZBS_ZNPHC, Z_SREQ | Z_SYS, SYS_OSAL_NV_READ, ZNP_HAS_CONFIGURED & 0xFF, ZNP_HAS_CONFIGURED >> 8, 0x00 /* offset */ ) // 2108000F00 - 6108000155 +ZBM(ZBR_ZNPHC, Z_SRSP | Z_SYS, SYS_OSAL_NV_READ, Z_Success, 0x01 /* len */, 0x55) // 6108000155 +// If not set, the response is 61-08-02-00 = Z_SRSP | Z_SYS, SYS_OSAL_NV_READ, Z_InvalidParameter, 0x00 /* len */ + +ZBM(ZBS_PAN, Z_SREQ | Z_SAPI, SAPI_READ_CONFIGURATION, CONF_PANID ) // 260483 +ZBM(ZBR_PAN, Z_SRSP | Z_SAPI, SAPI_READ_CONFIGURATION, Z_Success, CONF_PANID, 0x02 /* len */, + Z_B0(USE_ZIGBEE_PANID), Z_B1(USE_ZIGBEE_PANID) ) // 6604008302xxxx + +ZBM(ZBS_EXTPAN, Z_SREQ | Z_SAPI, SAPI_READ_CONFIGURATION, CONF_EXTENDED_PAN_ID ) // 26042D +ZBM(ZBR_EXTPAN, Z_SRSP | Z_SAPI, SAPI_READ_CONFIGURATION, Z_Success, CONF_EXTENDED_PAN_ID, + 0x08 /* len */, + Z_B0(USE_ZIGBEE_EXTPANID), Z_B1(USE_ZIGBEE_EXTPANID), Z_B2(USE_ZIGBEE_EXTPANID), Z_B3(USE_ZIGBEE_EXTPANID), + Z_B4(USE_ZIGBEE_EXTPANID), Z_B5(USE_ZIGBEE_EXTPANID), Z_B6(USE_ZIGBEE_EXTPANID), Z_B7(USE_ZIGBEE_EXTPANID), + ) // 6604002D08xxxxxxxxxxxxxxxx + +ZBM(ZBS_CHANN, Z_SREQ | Z_SAPI, SAPI_READ_CONFIGURATION, CONF_CHANLIST ) // 260484 +ZBM(ZBR_CHANN, Z_SRSP | Z_SAPI, SAPI_READ_CONFIGURATION, Z_Success, CONF_CHANLIST, + 0x04 /* len */, + Z_B0(USE_ZIGBEE_CHANNEL_MASK), Z_B1(USE_ZIGBEE_CHANNEL_MASK), Z_B2(USE_ZIGBEE_CHANNEL_MASK), Z_B3(USE_ZIGBEE_CHANNEL_MASK), + ) // 6604008404xxxxxxxx + +ZBM(ZBS_PFGK, Z_SREQ | Z_SAPI, SAPI_READ_CONFIGURATION, CONF_PRECFGKEY ) // 260462 +ZBM(ZBR_PFGK, Z_SRSP | Z_SAPI, SAPI_READ_CONFIGURATION, Z_Success, CONF_PRECFGKEY, + 0x10 /* len */, + Z_B0(USE_ZIGBEE_PRECFGKEY_L), Z_B1(USE_ZIGBEE_PRECFGKEY_L), Z_B2(USE_ZIGBEE_PRECFGKEY_L), Z_B3(USE_ZIGBEE_PRECFGKEY_L), + Z_B4(USE_ZIGBEE_PRECFGKEY_L), Z_B5(USE_ZIGBEE_PRECFGKEY_L), Z_B6(USE_ZIGBEE_PRECFGKEY_L), Z_B7(USE_ZIGBEE_PRECFGKEY_L), + Z_B0(USE_ZIGBEE_PRECFGKEY_H), Z_B1(USE_ZIGBEE_PRECFGKEY_H), Z_B2(USE_ZIGBEE_PRECFGKEY_H), Z_B3(USE_ZIGBEE_PRECFGKEY_H), + Z_B4(USE_ZIGBEE_PRECFGKEY_H), Z_B5(USE_ZIGBEE_PRECFGKEY_H), Z_B6(USE_ZIGBEE_PRECFGKEY_H), Z_B7(USE_ZIGBEE_PRECFGKEY_H), + /*0x01, 0x03, 0x05, 0x07, 0x09, 0x0B, 0x0D, 0x0F, + 0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0D*/ ) // 660400621001030507090B0D0F00020406080A0C0D + +ZBM(ZBS_PFGKEN, Z_SREQ | Z_SAPI, SAPI_READ_CONFIGURATION, CONF_PRECFGKEYS_ENABLE ) // 260463 +ZBM(ZBR_PFGKEN, Z_SRSP | Z_SAPI, SAPI_READ_CONFIGURATION, Z_Success, CONF_PRECFGKEYS_ENABLE, + 0x01 /* len */, 0x00 ) // 660400630100 + +// commands to "format" the device +// Write configuration - write success +ZBM(ZBR_W_OK, Z_SRSP | Z_SAPI, SAPI_WRITE_CONFIGURATION, Z_Success ) // 660500 - Write Configuration +ZBM(ZBR_WNV_OK, Z_SRSP | Z_SYS, SYS_OSAL_NV_WRITE, Z_Success ) // 610900 - NV Write + +// Factory reset +ZBM(ZBS_FACTRES, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_STARTUP_OPTION, 0x01 /* len */, 0x02 ) // 2605030102 +// Write PAN ID +ZBM(ZBS_W_PAN, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_PANID, 0x02 /* len */, Z_B0(USE_ZIGBEE_PANID), Z_B1(USE_ZIGBEE_PANID) ) // 26058302xxxx +// Write EXT PAN ID +ZBM(ZBS_W_EXTPAN, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_EXTENDED_PAN_ID, 0x08 /* len */, + Z_B0(USE_ZIGBEE_EXTPANID), Z_B1(USE_ZIGBEE_EXTPANID), Z_B2(USE_ZIGBEE_EXTPANID), Z_B3(USE_ZIGBEE_EXTPANID), + Z_B4(USE_ZIGBEE_EXTPANID), Z_B5(USE_ZIGBEE_EXTPANID), Z_B6(USE_ZIGBEE_EXTPANID), Z_B7(USE_ZIGBEE_EXTPANID) + ) // 26052D086263151D004B1200 +// Write Channel ID +ZBM(ZBS_W_CHANN, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_CHANLIST, 0x04 /* len */, + Z_B0(USE_ZIGBEE_CHANNEL_MASK), Z_B1(USE_ZIGBEE_CHANNEL_MASK), Z_B2(USE_ZIGBEE_CHANNEL_MASK), Z_B3(USE_ZIGBEE_CHANNEL_MASK), + /*0x00, 0x08, 0x00, 0x00*/ ) // 26058404xxxxxxxx +// Write Logical Type = 00 = coordinator +ZBM(ZBS_W_LOGTYP, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_LOGICAL_TYPE, 0x01 /* len */, 0x00 ) // 2605870100 +// Write precfgkey +ZBM(ZBS_W_PFGK, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_PRECFGKEY, + 0x10 /* len */, + Z_B0(USE_ZIGBEE_PRECFGKEY_L), Z_B1(USE_ZIGBEE_PRECFGKEY_L), Z_B2(USE_ZIGBEE_PRECFGKEY_L), Z_B3(USE_ZIGBEE_PRECFGKEY_L), + Z_B4(USE_ZIGBEE_PRECFGKEY_L), Z_B5(USE_ZIGBEE_PRECFGKEY_L), Z_B6(USE_ZIGBEE_PRECFGKEY_L), Z_B7(USE_ZIGBEE_PRECFGKEY_L), + Z_B0(USE_ZIGBEE_PRECFGKEY_H), Z_B1(USE_ZIGBEE_PRECFGKEY_H), Z_B2(USE_ZIGBEE_PRECFGKEY_H), Z_B3(USE_ZIGBEE_PRECFGKEY_H), + Z_B4(USE_ZIGBEE_PRECFGKEY_H), Z_B5(USE_ZIGBEE_PRECFGKEY_H), Z_B6(USE_ZIGBEE_PRECFGKEY_H), Z_B7(USE_ZIGBEE_PRECFGKEY_H), + /*0x01, 0x03, 0x05, 0x07, 0x09, 0x0B, 0x0D, 0x0F, + 0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0D*/ ) // 2605621001030507090B0D0F00020406080A0C0D +// Write precfgkey enable +ZBM(ZBS_W_PFGKEN, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_PRECFGKEYS_ENABLE, 0x01 /* len */, 0x00 ) // 2605630100 +// Write Security Mode +ZBM(ZBS_WNV_SECMODE, Z_SREQ | Z_SYS, SYS_OSAL_NV_WRITE, Z_B0(CONF_TCLK_TABLE_START), Z_B1(CONF_TCLK_TABLE_START), + 0x00 /* offset */, 0x20 /* len */, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x5a, 0x69, 0x67, 0x42, 0x65, 0x65, 0x41, 0x6c, + 0x6c, 0x69, 0x61, 0x6e, 0x63, 0x65, 0x30, 0x39, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00) // 2109010100200FFFFFFFFFFFFFFFF5A6967426565416C6C69616E636530390000000000000000 +// Write Z_ZDO Direct CB +ZBM(ZBS_W_ZDODCB, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_ZDO_DIRECT_CB, 0x01 /* len */, 0x01 ) // 26058F0101 +// NV Init ZNP Has Configured +ZBM(ZBS_WNV_INITZNPHC, Z_SREQ | Z_SYS, SYS_OSAL_NV_ITEM_INIT, ZNP_HAS_CONFIGURED & 0xFF, ZNP_HAS_CONFIGURED >> 8, + 0x01, 0x00 /* InitLen 16 bits */, 0x01 /* len */, 0x00 ) // 2107000F01000100 - 610709 +// Init succeeded +//ZBM(ZBR_WNV_INIT_OK, Z_SRSP | Z_SYS, SYS_OSAL_NV_ITEM_INIT, Z_Created ) // 610709 - NV Write +ZBM(ZBR_WNV_INIT_OK, Z_SRSP | Z_SYS, SYS_OSAL_NV_ITEM_INIT ) // 6107xx, Success if 610700 or 610709 - NV Write + +// Write ZNP Has Configured +ZBM(ZBS_WNV_ZNPHC, Z_SREQ | Z_SYS, SYS_OSAL_NV_WRITE, Z_B0(ZNP_HAS_CONFIGURED), Z_B1(ZNP_HAS_CONFIGURED), + 0x00 /* offset */, 0x01 /* len */, 0x55 ) // 2109000F000155 - 610900 +// Z_ZDO:startupFromApp +ZBM(ZBS_STARTUPFROMAPP, Z_SREQ | Z_ZDO, ZDO_STARTUP_FROM_APP, 100, 0 /* delay */) // 25406400 +ZBM(ZBR_STARTUPFROMAPP, Z_SRSP | Z_ZDO, ZDO_STARTUP_FROM_APP ) // 6540 + 01 for new network, 00 for exisitng network, 02 for error +ZBM(AREQ_STARTUPFROMAPP, Z_AREQ | Z_ZDO, ZDO_STATE_CHANGE_IND, ZDO_DEV_ZB_COORD ) // 45C009 + 08 = starting, 09 = started +// GetDeviceInfo +ZBM(ZBS_GETDEVICEINFO, Z_SREQ | Z_UTIL, Z_UTIL_GET_DEVICE_INFO ) // 2700 +ZBM(ZBR_GETDEVICEINFO, Z_SRSP | Z_UTIL, Z_UTIL_GET_DEVICE_INFO, Z_Success ) // Ex= 6700.00.6263151D004B1200.0000.07.09.00 + // IEEE Adr (8 bytes) = 6263151D004B1200 + // Short Addr (2 bytes) = 0000 + // Device Type (1 byte) = 07 (coord?) + // Device State (1 byte) = 09 (coordinator started) + // NumAssocDevices (1 byte) = 00 + +// Read Pan ID +//ZBM(ZBS_READ_NV_PANID, Z_SREQ | Z_SYS, SYS_OSAL_NV_READ, PANID & 0xFF, PANID >> 8, 0x00 /* offset */ ) // 2108830000 + +// Z_ZDO:nodeDescReq +ZBM(ZBS_ZDO_NODEDESCREQ, Z_SREQ | Z_ZDO, ZDO_NODE_DESC_REQ, 0x00, 0x00 /* dst addr */, 0x00, 0x00 /* NWKAddrOfInterest */) // 250200000000 +ZBM(ZBR_ZDO_NODEDESCREQ, Z_SRSP | Z_ZDO, ZDO_NODE_DESC_REQ, Z_Success ) // 650200 +// Async resp ex: 4582.0000.00.0000.00.40.8F.0000.50.A000.0100.A000.00 +ZBM(AREQ_ZDO_NODEDESCRSP, Z_AREQ | Z_ZDO, ZDO_NODE_DESC_RSP) // 4582 +// SrcAddr (2 bytes) 0000 +// Status (1 byte) 00 Success +// NwkAddr (2 bytes) 0000 +// LogicalType (1 byte) - 00 Coordinator +// APSFlags (1 byte) - 40 0=APSFlags 4=NodeFreqBands +// MACCapabilityFlags (1 byte) - 8F ALL +// ManufacturerCode (2 bytes) - 0000 +// MaxBufferSize (1 byte) - 50 NPDU +// MaxTransferSize (2 bytes) - A000 = 160 +// ServerMask (2 bytes) - 0100 - Primary Trust Center +// MaxOutTransferSize (2 bytes) - A000 = 160 +// DescriptorCapabilities (1 byte) - 00 + +// Z_ZDO:activeEpReq +ZBM(ZBS_ZDO_ACTIVEEPREQ, Z_SREQ | Z_ZDO, ZDO_ACTIVE_EP_REQ, 0x00, 0x00, 0x00, 0x00) // 250500000000 +ZBM(ZBR_ZDO_ACTIVEEPREQ, Z_SRSP | Z_ZDO, ZDO_ACTIVE_EP_REQ, Z_Success) // 65050000 +ZBM(ZBR_ZDO_ACTIVEEPRSP_NONE, Z_AREQ | Z_ZDO, ZDO_ACTIVE_EP_RSP, 0x00, 0x00 /* srcAddr */, Z_Success, + 0x00, 0x00 /* nwkaddr */, 0x00 /* activeepcount */) // 45050000 - no Ep running +ZBM(ZBR_ZDO_ACTIVEEPRSP_OK, Z_AREQ | Z_ZDO, ZDO_ACTIVE_EP_RSP, 0x00, 0x00 /* srcAddr */, Z_Success, + 0x00, 0x00 /* nwkaddr */, 0x02 /* activeepcount */, 0x0B, 0x01 /* the actual endpoints */) // 25050000 - no Ep running + +// Z_AF:register profile:104, ep:01 +ZBM(ZBS_AF_REGISTER01, Z_SREQ | Z_AF, AF_REGISTER, 0x01 /* endpoint */, Z_B0(Z_PROF_HA), Z_B1(Z_PROF_HA), // 24000401050000000000 + 0x05, 0x00 /* AppDeviceId */, 0x00 /* AppDevVer */, 0x00 /* LatencyReq */, + 0x00 /* AppNumInClusters */, 0x00 /* AppNumInClusters */) +ZBM(ZBR_AF_REGISTER, Z_SRSP | Z_AF, AF_REGISTER, Z_Success) // 640000 +ZBM(ZBS_AF_REGISTER0B, Z_SREQ | Z_AF, AF_REGISTER, 0x0B /* endpoint */, Z_B0(Z_PROF_HA), Z_B1(Z_PROF_HA), // 2400040B050000000000 + 0x05, 0x00 /* AppDeviceId */, 0x00 /* AppDevVer */, 0x00 /* LatencyReq */, + 0x00 /* AppNumInClusters */, 0x00 /* AppNumInClusters */) +// Z_ZDO:mgmtPermitJoinReq +ZBM(ZBS_PERMITJOINREQ_CLOSE, Z_SREQ | Z_ZDO, ZDO_MGMT_PERMIT_JOIN_REQ, 0x02 /* AddrMode */, // 25360200000000 + 0x00, 0x00 /* DstAddr */, 0x00 /* Duration */, 0x00 /* TCSignificance */) +ZBM(ZBS_PERMITJOINREQ_OPEN_60, Z_SREQ | Z_ZDO, ZDO_MGMT_PERMIT_JOIN_REQ, 0x0F /* AddrMode */, // 25360FFFFC3C00 + 0xFC, 0xFF /* DstAddr */, 60 /* Duration */, 0x00 /* TCSignificance */) +ZBM(ZBS_PERMITJOINREQ_OPEN_XX, Z_SREQ | Z_ZDO, ZDO_MGMT_PERMIT_JOIN_REQ, 0x0F /* AddrMode */, // 25360FFFFCFF00 + 0xFC, 0xFF /* DstAddr */, 0xFF /* Duration */, 0x00 /* TCSignificance */) +ZBM(ZBR_PERMITJOINREQ, Z_SRSP | Z_ZDO, ZDO_MGMT_PERMIT_JOIN_REQ, Z_Success) // 653600 +ZBM(ZBR_PERMITJOIN_AREQ_CLOSE, Z_AREQ | Z_ZDO, ZDO_PERMIT_JOIN_IND, 0x00 /* Duration */) // 45CB00 +ZBM(ZBR_PERMITJOIN_AREQ_OPEN_60, Z_AREQ | Z_ZDO, ZDO_PERMIT_JOIN_IND, 60 /* Duration */) // 45CB3C +ZBM(ZBR_PERMITJOIN_AREQ_OPEN_FF, Z_AREQ | Z_ZDO, ZDO_PERMIT_JOIN_IND, 0xFF /* Duration */) // 45CBFF +ZBM(ZBR_PERMITJOIN_AREQ_RSP, Z_AREQ | Z_ZDO, ZDO_MGMT_PERMIT_JOIN_RSP, 0x00, 0x00 /* srcAddr*/, Z_Success ) // 45B6000000 + +static const Zigbee_Instruction zb_prog[] PROGMEM = { + ZI_LABEL(0) + ZI_NOOP() + ZI_ON_ERROR_GOTO(ZIGBEE_LABEL_ABORT) + ZI_ON_TIMEOUT_GOTO(ZIGBEE_LABEL_ABORT) + ZI_ON_RECV_UNEXPECTED(&Z_Recv_Default) + ZI_WAIT(10500) // wait for 10 seconds for Tasmota to stabilize + ZI_ON_ERROR_GOTO(50) + + //ZI_MQTT_STATUS(ZIGBEE_STATUS_BOOT, "Booting") + //ZI_LOG(LOG_LEVEL_INFO, "ZIG: rebooting device") + ZI_SEND(ZBS_RESET) // reboot cc2530 just in case we rebooted ESP8266 but not cc2530 + ZI_WAIT_RECV_FUNC(5000, ZBR_RESET, &Z_Reboot) // timeout 5s + ZI_WAIT(100) + ZI_LOG(LOG_LEVEL_INFO, "ZIG: checking device configuration") + ZI_SEND(ZBS_ZNPHC) // check value of ZNP Has Configured + ZI_WAIT_RECV(2000, ZBR_ZNPHC) + ZI_SEND(ZBS_VERSION) // check ZNP software version + ZI_WAIT_RECV_FUNC(2000, ZBR_VERSION, &Z_ReceiveCheckVersion) // Check version + ZI_SEND(ZBS_PAN) // check PAN ID + ZI_WAIT_RECV(1000, ZBR_PAN) + ZI_SEND(ZBS_EXTPAN) // check EXT PAN ID + ZI_WAIT_RECV(1000, ZBR_EXTPAN) + ZI_SEND(ZBS_CHANN) // check CHANNEL + ZI_WAIT_RECV(1000, ZBR_CHANN) + ZI_SEND(ZBS_PFGK) // check PFGK + ZI_WAIT_RECV(1000, ZBR_PFGK) + ZI_SEND(ZBS_PFGKEN) // check PFGKEN + ZI_WAIT_RECV(1000, ZBR_PFGKEN) + //ZI_LOG(LOG_LEVEL_INFO, "ZIG: zigbee configuration ok") + // all is good, we can start + + ZI_LABEL(ZIGBEE_LABEL_START) // START ZNP App + ZI_MQTT_STATUS(ZIGBEE_STATUS_STARTING, "Configured, starting coordinator") + //ZI_CALL(&Z_State_Ready, 1) // Now accept incoming messages + ZI_ON_ERROR_GOTO(ZIGBEE_LABEL_ABORT) + // Z_ZDO:startupFromApp + //ZI_LOG(LOG_LEVEL_INFO, "ZIG: starting zigbee coordinator") +ZI_SEND(ZBS_STARTUPFROMAPP) // start coordinator + ZI_WAIT_RECV(2000, ZBR_STARTUPFROMAPP) // wait for sync ack of command + ZI_WAIT_UNTIL(5000, AREQ_STARTUPFROMAPP) // wait for async message that coordinator started + ZI_SEND(ZBS_GETDEVICEINFO) // GetDeviceInfo + ZI_WAIT_RECV_FUNC(2000, ZBR_GETDEVICEINFO, &Z_ReceiveDeviceInfo) + //ZI_WAIT_RECV(2000, ZBR_GETDEVICEINFO) // memorize info + ZI_SEND(ZBS_ZDO_NODEDESCREQ) // Z_ZDO:nodeDescReq + ZI_WAIT_RECV(1000, ZBR_ZDO_NODEDESCREQ) + ZI_WAIT_UNTIL(5000, AREQ_ZDO_NODEDESCRSP) + ZI_SEND(ZBS_ZDO_ACTIVEEPREQ) // Z_ZDO:activeEpReq + ZI_WAIT_RECV(1000, ZBR_ZDO_ACTIVEEPREQ) + ZI_WAIT_UNTIL(1000, ZBR_ZDO_ACTIVEEPRSP_NONE) + ZI_SEND(ZBS_AF_REGISTER01) // Z_AF register for endpoint 01, profile 0x0104 Home Automation + ZI_WAIT_RECV(1000, ZBR_AF_REGISTER) + ZI_SEND(ZBS_AF_REGISTER0B) // Z_AF register for endpoint 0B, profile 0x0104 Home Automation + ZI_WAIT_RECV(1000, ZBR_AF_REGISTER) + // Z_ZDO:nodeDescReq ?? Is is useful to redo it? TODO + // redo Z_ZDO:activeEpReq to check that Ep are available + ZI_SEND(ZBS_ZDO_ACTIVEEPREQ) // Z_ZDO:activeEpReq + ZI_WAIT_RECV(1000, ZBR_ZDO_ACTIVEEPREQ) + ZI_WAIT_UNTIL(1000, ZBR_ZDO_ACTIVEEPRSP_OK) + ZI_SEND(ZBS_PERMITJOINREQ_CLOSE) // Closing the Permit Join + ZI_WAIT_RECV(1000, ZBR_PERMITJOINREQ) + ZI_WAIT_UNTIL(1000, ZBR_PERMITJOIN_AREQ_RSP) // not sure it's useful + //ZI_WAIT_UNTIL(500, ZBR_PERMITJOIN_AREQ_CLOSE) + //ZI_SEND(ZBS_PERMITJOINREQ_OPEN_XX) // Opening Permit Join, normally through command + //ZI_WAIT_RECV(1000, ZBR_PERMITJOINREQ) + //ZI_WAIT_UNTIL(1000, ZBR_PERMITJOIN_AREQ_RSP) // not sure it's useful + //ZI_WAIT_UNTIL(500, ZBR_PERMITJOIN_AREQ_OPEN_FF) + + ZI_LABEL(ZIGBEE_LABEL_READY) + ZI_MQTT_STATUS(ZIGBEE_STATUS_OK, "Started") + ZI_LOG(LOG_LEVEL_INFO, "ZIG: zigbee device ready, listening...") + ZI_CALL(&Z_State_Ready, 1) // Now accept incoming messages + ZI_LABEL(ZIGBEE_LABEL_MAIN_LOOP) + ZI_WAIT_FOREVER() + ZI_GOTO(ZIGBEE_LABEL_READY) + + ZI_LABEL(ZIGBEE_LABEL_PERMIT_JOIN_CLOSE) + //ZI_MQTT_STATUS(ZIGBEE_STATUS_PERMITJOIN_CLOSE, "Disable Pairing mode") + ZI_SEND(ZBS_PERMITJOINREQ_CLOSE) // Closing the Permit Join + ZI_WAIT_RECV(1000, ZBR_PERMITJOINREQ) + //ZI_WAIT_UNTIL(1000, ZBR_PERMITJOIN_AREQ_RSP) // not sure it's useful + //ZI_WAIT_UNTIL(500, ZBR_PERMITJOIN_AREQ_CLOSE) + ZI_GOTO(ZIGBEE_LABEL_MAIN_LOOP) + + ZI_LABEL(ZIGBEE_LABEL_PERMIT_JOIN_OPEN_60) + //ZI_MQTT_STATUS(ZIGBEE_STATUS_PERMITJOIN_OPEN_60, "Enable Pairing mode for 60 seconds") + ZI_SEND(ZBS_PERMITJOINREQ_OPEN_60) + ZI_WAIT_RECV(1000, ZBR_PERMITJOINREQ) + //ZI_WAIT_UNTIL(1000, ZBR_PERMITJOIN_AREQ_RSP) // not sure it's useful + //ZI_WAIT_UNTIL(500, ZBR_PERMITJOIN_AREQ_OPEN_60) + ZI_GOTO(ZIGBEE_LABEL_MAIN_LOOP) + + ZI_LABEL(ZIGBEE_LABEL_PERMIT_JOIN_OPEN_XX) + //ZI_MQTT_STATUS(ZIGBEE_STATUS_PERMITJOIN_OPEN_XX, "Enable Pairing mode until next boot") + ZI_SEND(ZBS_PERMITJOINREQ_OPEN_XX) + ZI_WAIT_RECV(1000, ZBR_PERMITJOINREQ) + //ZI_WAIT_UNTIL(1000, ZBR_PERMITJOIN_AREQ_RSP) // not sure it's useful + //ZI_WAIT_UNTIL(500, ZBR_PERMITJOIN_AREQ_OPEN_FF) + ZI_GOTO(ZIGBEE_LABEL_MAIN_LOOP) + + ZI_LABEL(50) // reformat device + ZI_MQTT_STATUS(ZIGBEE_STATUS_RESET_CONF, "Reseting configuration") + //ZI_LOG(LOG_LEVEL_INFO, "ZIG: zigbee bad configuration of device, doing a factory reset") + ZI_ON_ERROR_GOTO(ZIGBEE_LABEL_ABORT) + ZI_SEND(ZBS_FACTRES) // factory reset + ZI_WAIT_RECV(1000, ZBR_W_OK) + ZI_SEND(ZBS_RESET) // reset device + ZI_WAIT_RECV(5000, ZBR_RESET) + ZI_SEND(ZBS_W_PAN) // write PAN ID + ZI_WAIT_RECV(1000, ZBR_W_OK) + ZI_SEND(ZBS_W_EXTPAN) // write EXT PAN ID + ZI_WAIT_RECV(1000, ZBR_W_OK) + ZI_SEND(ZBS_W_CHANN) // write CHANNEL + ZI_WAIT_RECV(1000, ZBR_W_OK) + ZI_SEND(ZBS_W_LOGTYP) // write Logical Type = coordinator + ZI_WAIT_RECV(1000, ZBR_W_OK) + ZI_SEND(ZBS_W_PFGK) // write PRECFGKEY + ZI_WAIT_RECV(1000, ZBR_W_OK) + ZI_SEND(ZBS_W_PFGKEN) // write PRECFGKEY Enable + ZI_WAIT_RECV(1000, ZBR_W_OK) + ZI_SEND(ZBS_WNV_SECMODE) // write Security Mode + ZI_WAIT_RECV(1000, ZBR_WNV_OK) + ZI_SEND(ZBS_W_ZDODCB) // write Z_ZDO Direct CB + ZI_WAIT_RECV(1000, ZBR_W_OK) + // Now mark the device as ready, writing 0x55 in memory slot 0x0F00 + ZI_SEND(ZBS_WNV_INITZNPHC) // Init NV ZNP Has Configured + ZI_WAIT_RECV_FUNC(1000, ZBR_WNV_INIT_OK, &Z_CheckNVWrite) + ZI_SEND(ZBS_WNV_ZNPHC) // Write NV ZNP Has Configured + ZI_WAIT_RECV(1000, ZBR_WNV_OK) + + //ZI_LOG(LOG_LEVEL_INFO, "ZIG: zigbee device reconfigured") + ZI_GOTO(ZIGBEE_LABEL_START) + + ZI_LABEL(ZIGBEE_LABEL_UNSUPPORTED_VERSION) + ZI_MQTT_STATUS(ZIGBEE_STATUS_UNSUPPORTED_VERSION, "Only ZNP 1.2 is currently supported") + ZI_GOTO(ZIGBEE_LABEL_ABORT) + + ZI_LABEL(ZIGBEE_LABEL_ABORT) // Label 99: abort + ZI_MQTT_STATUS(ZIGBEE_STATUS_ABORT, "Abort") + ZI_LOG(LOG_LEVEL_ERROR, "ZIG: Abort") + ZI_STOP(ZIGBEE_LABEL_ABORT) +}; + +uint8_t ZigbeeGetInstructionSize(uint8_t instr) { // in Zigbee_Instruction lines (words) + if (instr >= ZGB_INSTR_12_BYTES) { + return 3; + } else if (instr >= ZGB_INSTR_8_BYTES) { + return 2; + } else { + return 1; + } +} + +void ZigbeeGotoLabel(uint8_t label) { + // look for the label scanning entire code + uint16_t goto_pc = 0xFFFF; // 0xFFFF means not found + uint8_t cur_instr = 0; + uint8_t cur_d8 = 0; + uint8_t cur_instr_len = 1; // size of current instruction in words + + for (uint32_t i = 0; i < sizeof(zb_prog)/sizeof(zb_prog[0]); i += cur_instr_len) { + const Zigbee_Instruction *cur_instr_line = &zb_prog[i]; + cur_instr = pgm_read_byte(&cur_instr_line->i.i); + cur_d8 = pgm_read_byte(&cur_instr_line->i.d8); + //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZGB GOTO: pc %d instr %d"), i, cur_instr); + + if (ZGB_INSTR_LABEL == cur_instr) { + //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZIG: found label %d at pc %d"), cur_d8, i); + if (label == cur_d8) { + // label found, goto to this pc + zigbee.pc = i; + zigbee.state_machine = true; + zigbee.state_waiting = false; + return; + } + } + // get instruction length + cur_instr_len = ZigbeeGetInstructionSize(cur_instr); + } + + // no label found, abort + AddLog_P2(LOG_LEVEL_ERROR, PSTR("ZIG: Goto label not found, label=%d pc=%d"), label, zigbee.pc); + if (ZIGBEE_LABEL_ABORT != label) { + // if not already looking for ZIGBEE_LABEL_ABORT, goto ZIGBEE_LABEL_ABORT + ZigbeeGotoLabel(ZIGBEE_LABEL_ABORT); + } else { + AddLog_P2(LOG_LEVEL_ERROR, PSTR("ZIG: Label Abort (%d) not present, aborting Zigbee"), ZIGBEE_LABEL_ABORT); + zigbee.state_machine = false; + zigbee.active = false; + } +} + +void ZigbeeStateMachine_Run(void) { + uint8_t cur_instr = 0; + uint8_t cur_d8 = 0; + uint16_t cur_d16 = 0; + const void* cur_ptr1 = nullptr; + const void* cur_ptr2 = nullptr; + uint32_t now = millis(); + + if (zigbee.state_waiting) { // state machine is waiting for external event or timeout + // checking if timeout expired + if ((zigbee.next_timeout) && (now > zigbee.next_timeout)) { // if next_timeout == 0 then wait forever + //AddLog_P2(LOG_LEVEL_INFO, PSTR("ZIG: timeout occured pc=%d"), zigbee.pc); + if (!zigbee.state_no_timeout) { + AddLog_P2(LOG_LEVEL_INFO, PSTR("ZIG: timeout, goto label %d"), zigbee.on_timeout_goto); + ZigbeeGotoLabel(zigbee.on_timeout_goto); + } else { + zigbee.state_waiting = false; // simply stop waiting + } + } + } + + while ((zigbee.state_machine) && (!zigbee.state_waiting)) { + // reinit receive filters and functions (they only work for a single instruction) + zigbee.recv_filter = nullptr; + zigbee.recv_func = nullptr; + zigbee.recv_until = false; + zigbee.state_no_timeout = false; // reset the no_timeout for next instruction + + if (zigbee.pc > (sizeof(zb_prog)/sizeof(zb_prog[0]))) { + AddLog_P2(LOG_LEVEL_ERROR, PSTR("ZIG: Invalid pc: %d, aborting"), zigbee.pc); + zigbee.pc = -1; + } + if (zigbee.pc < 0) { + zigbee.state_machine = false; + return; + } + + // load current instruction details + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZIG: Executing instruction pc=%d"), zigbee.pc); + const Zigbee_Instruction *cur_instr_line = &zb_prog[zigbee.pc]; + cur_instr = pgm_read_byte(&cur_instr_line->i.i); + cur_d8 = pgm_read_byte(&cur_instr_line->i.d8); + cur_d16 = pgm_read_word(&cur_instr_line->i.d16); + if (cur_instr >= ZGB_INSTR_8_BYTES) { + cur_instr_line++; + cur_ptr1 = cur_instr_line->p; + } + if (cur_instr >= ZGB_INSTR_12_BYTES) { + cur_instr_line++; + cur_ptr2 = cur_instr_line->p; + } + + zigbee.pc += ZigbeeGetInstructionSize(cur_instr); // move pc to next instruction, before any goto + + switch (cur_instr) { + case ZGB_INSTR_NOOP: + case ZGB_INSTR_LABEL: // do nothing + break; + case ZGB_INSTR_GOTO: + ZigbeeGotoLabel(cur_d8); + break; + case ZGB_INSTR_ON_ERROR_GOTO: + zigbee.on_error_goto = cur_d8; + break; + case ZGB_INSTR_ON_TIMEOUT_GOTO: + zigbee.on_timeout_goto = cur_d8; + break; + case ZGB_INSTR_WAIT: + zigbee.next_timeout = now + cur_d16; + zigbee.state_waiting = true; + zigbee.state_no_timeout = true; // do not generate a timeout error when waiting is done + break; + case ZGB_INSTR_WAIT_FOREVER: + zigbee.next_timeout = 0; + zigbee.state_waiting = true; + //zigbee.state_no_timeout = true; // do not generate a timeout error when waiting is done + break; + case ZGB_INSTR_STOP: + zigbee.state_machine = false; + if (cur_d8) { + AddLog_P2(LOG_LEVEL_ERROR, PSTR("ZIG: Stopping (%d)"), cur_d8); + } + break; + case ZGB_INSTR_CALL: + if (cur_ptr1) { + uint32_t res; + res = (*((ZB_Func)cur_ptr1))(cur_d8); + if (res > 0) { + ZigbeeGotoLabel(res); + continue; // avoid incrementing PC after goto + } else if (res == 0) { + // do nothing + } else if (res == -1) { + // do nothing + } else { + ZigbeeGotoLabel(zigbee.on_error_goto); + continue; + } + } + break; + case ZGB_INSTR_LOG: + AddLog_P(cur_d8, (char*) cur_ptr1); + break; + case ZGB_INSTR_MQTT_STATUS: + Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATUS "\":{\"Status\":%d,\"Message\":\"%s\"}}"), + cur_d8, (char*) cur_ptr1); + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_STATUS)); + XdrvRulesProcess(); + break; + case ZGB_INSTR_SEND: + ZigbeeZNPSend((uint8_t*) cur_ptr1, cur_d8 /* len */); + break; + case ZGB_INSTR_WAIT_UNTIL: + zigbee.recv_until = true; // and reuse ZGB_INSTR_WAIT_RECV + case ZGB_INSTR_WAIT_RECV: + zigbee.recv_filter = (uint8_t *) cur_ptr1; + zigbee.recv_filter_len = cur_d8; // len + zigbee.next_timeout = now + cur_d16; + zigbee.state_waiting = true; + break; + case ZGB_ON_RECV_UNEXPECTED: + zigbee.recv_unexpected = (ZB_RecvMsgFunc) cur_ptr1; + break; + case ZGB_INSTR_WAIT_RECV_CALL: + zigbee.recv_filter = (uint8_t *) cur_ptr1; + zigbee.recv_filter_len = cur_d8; // len + zigbee.recv_func = (ZB_RecvMsgFunc) cur_ptr2; + zigbee.next_timeout = now + cur_d16; + zigbee.state_waiting = true; + break; + } + } +} + +#endif // USE_ZIGBEE diff --git a/sonoff/xdrv_23_zigbee_8_parsers.ino b/sonoff/xdrv_23_zigbee_8_parsers.ino new file mode 100644 index 000000000..286596e33 --- /dev/null +++ b/sonoff/xdrv_23_zigbee_8_parsers.ino @@ -0,0 +1,458 @@ +/* + xdrv_23_zigbee.ino - zigbee support for Sonoff-Tasmota + + Copyright (C) 2019 Theo Arends and Stephan Hadinger + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef USE_ZIGBEE + +int32_t Z_ReceiveDeviceInfo(int32_t res, class SBuffer &buf) { + // Ex= 6700.00.6263151D004B1200.0000.07.09.02.83869991 + // IEEE Adr (8 bytes) = 0x00124B001D156362 + // Short Addr (2 bytes) = 0x0000 + // Device Type (1 byte) = 0x07 (coord?) + // Device State (1 byte) = 0x09 (coordinator started) + // NumAssocDevices (1 byte) = 0x02 + // List of devices: 0x8683, 0x9199 + Z_IEEEAddress long_adr = buf.get64(3); + Z_ShortAddress short_adr = buf.get16(11); + uint8_t device_type = buf.get8(13); + uint8_t device_state = buf.get8(14); + uint8_t device_associated = buf.get8(15); + + char hex[20]; + Uint64toHex(long_adr, hex, 64); + Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATUS "\":{" + "\"Status\":%d,\"IEEEAddr\":\"%s\",\"ShortAddr\":\"0x%04X\"" + ",\"DeviceType\":%d,\"DeviceState\":%d" + ",\"NumAssocDevices\":%d"), + ZIGBEE_STATUS_CC_INFO, hex, short_adr, device_type, device_state, + device_associated); + + if (device_associated > 0) { + uint idx = 16; + ResponseAppend_P(PSTR(",\"AssocDevicesList\":[")); + for (uint32_t i = 0; i < device_associated; i++) { + if (i > 0) { ResponseAppend_P(PSTR(",")); } + ResponseAppend_P(PSTR("\"0x%04X\""), buf.get16(idx)); + idx += 2; + } + ResponseAppend_P(PSTR("]")); + } + + ResponseJsonEnd(); // append '}' + ResponseJsonEnd(); // append '}' + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_STATUS)); + XdrvRulesProcess(); + + return res; +} + +int32_t Z_CheckNVWrite(int32_t res, class SBuffer &buf) { + // Check the status after NV Init "ZNP Has Configured" + // Good response should be 610700 or 610709 (Success or Created) + // We only filter the response on 6107 and check the code in this function + uint8_t status = buf.get8(2); + if ((0x00 == status) || (0x09 == status)) { + return 0; // Ok, continue + } else { + return -2; // Error + } +} + +const char Z_RebootReason[] PROGMEM = "Power-up|External|Watchdog"; + +int32_t Z_Reboot(int32_t res, class SBuffer &buf) { + // print information about the reboot of device + // 4180.02.02.00.02.06.03 + // + uint8_t reason = buf.get8(2); + uint8_t transport_rev = buf.get8(3); + uint8_t product_id = buf.get8(4); + uint8_t major_rel = buf.get8(5); + uint8_t minor_rel = buf.get8(6); + uint8_t hw_rev = buf.get8(7); + char reason_str[12]; + + if (reason > 3) { reason = 3; } + GetTextIndexed(reason_str, sizeof(reason_str), reason, Z_RebootReason); + + Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATUS "\":{" + "\"Status\":%d,\"Message\":\"%s\",\"RestartReason\":\"%s\"" + ",\"MajorRel\":%d,\"MinorRel\":%d}}"), + ZIGBEE_STATUS_BOOT, "CC2530 booted", reason_str, + major_rel, minor_rel); + + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_STATUS)); + XdrvRulesProcess(); + + if ((0x02 == major_rel) && (0x06 == minor_rel)) { + return 0; // version 2.6.x is ok + } else { + return ZIGBEE_LABEL_UNSUPPORTED_VERSION; // abort + } +} + +int32_t Z_ReceiveCheckVersion(int32_t res, class SBuffer &buf) { + // check that the version is supported + // typical version for ZNP 1.2 + // 61020200-02.06.03.D9143401.0200000000 + // TranportRev = 02 + // Product = 00 + // MajorRel = 2 + // MinorRel = 6 + // MaintRel = 3 + // Revision = 20190425 d (0x013414D9) + uint8_t major_rel = buf.get8(4); + uint8_t minor_rel = buf.get8(5); + uint8_t maint_rel = buf.get8(6); + uint32_t revision = buf.get32(7); + + Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATUS "\":{" + "\"Status\":%d,\"MajorRel\":%d,\"MinorRel\":%d" + ",\"MaintRel\":%d,\"Revision\":%d}}"), + ZIGBEE_STATUS_CC_VERSION, major_rel, minor_rel, + maint_rel, revision); + + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_STATUS)); + XdrvRulesProcess(); + + if ((0x02 == major_rel) && (0x06 == minor_rel)) { + return 0; // version 2.6.x is ok + } else { + return ZIGBEE_LABEL_UNSUPPORTED_VERSION; // abort + } +} + +bool Z_ReceiveMatchPrefix(const class SBuffer &buf, const uint8_t *match) { + if ( (pgm_read_byte(&match[0]) == buf.get8(0)) && + (pgm_read_byte(&match[1]) == buf.get8(1)) ) { + return true; + } else { + return false; + } +} + +int32_t Z_ReceivePermitJoinStatus(int32_t res, const class SBuffer &buf) { + // we received a PermitJoin status change + uint8_t duration = buf.get8(2); + uint8_t status_code; + const char* message; + + if (0xFF == duration) { + status_code = ZIGBEE_STATUS_PERMITJOIN_OPEN_XX; + message = PSTR("Enable Pairing mode until next boot"); + } else if (duration > 0) { + status_code = ZIGBEE_STATUS_PERMITJOIN_OPEN_60; + message = PSTR("Enable Pairing mode for %d seconds"); + } else { + status_code = ZIGBEE_STATUS_PERMITJOIN_CLOSE; + message = PSTR("Disable Pairing mode"); + } + Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATUS "\":{" + "\"Status\":%d,\"Message\":\""), + status_code); + ResponseAppend_P(message, duration); + ResponseAppend_P(PSTR("\"}}")); + + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_STATUS)); + XdrvRulesProcess(); + return -1; +} + +// Send ACTIVE_EP_REQ to collect active endpoints for this address +void Z_SendActiveEpReq(uint16_t shortaddr) { + uint8_t ActiveEpReq[] = { Z_SREQ | Z_ZDO, ZDO_ACTIVE_EP_REQ, + Z_B0(shortaddr), Z_B1(shortaddr), Z_B0(shortaddr), Z_B1(shortaddr) }; + + uint8_t NodeDescReq[] = { Z_SREQ | Z_ZDO, ZDO_NODE_DESC_REQ, + Z_B0(shortaddr), Z_B1(shortaddr), Z_B0(shortaddr), Z_B1(shortaddr) }; + + ZigbeeZNPSend(ActiveEpReq, sizeof(ActiveEpReq)); + //ZigbeeZNPSend(NodeDescReq, sizeof(NodeDescReq)); Not sure this is useful +} + +// Send ZDO_SIMPLE_DESC_REQ to get full list of supported Clusters for a specific endpoint +void Z_SendSimpleDescReq(uint16_t shortaddr, uint8_t endpoint) { + uint8_t SimpleDescReq[] = { Z_SREQ | Z_ZDO, ZDO_SIMPLE_DESC_REQ, // 2504 + Z_B0(shortaddr), Z_B1(shortaddr), Z_B0(shortaddr), Z_B1(shortaddr), + endpoint }; + + ZigbeeZNPSend(SimpleDescReq, sizeof(SimpleDescReq)); +} + +const char* Z_DeviceType[] = { "Coordinator", "Router", "End Device", "Unknown" }; +int32_t Z_ReceiveNodeDesc(int32_t res, const class SBuffer &buf) { + // Received ZDO_NODE_DESC_RSP + Z_ShortAddress srcAddr = buf.get16(2); + uint8_t status = buf.get8(4); + Z_ShortAddress nwkAddr = buf.get16(5); + uint8_t logicalType = buf.get8(7); + uint8_t apsFlags = buf.get8(8); + uint8_t MACCapabilityFlags = buf.get8(9); + uint16_t manufacturerCapabilities = buf.get16(10); + uint8_t maxBufferSize = buf.get8(12); + uint16_t maxInTransferSize = buf.get16(13); + uint16_t serverMask = buf.get16(15); + uint16_t maxOutTransferSize = buf.get16(17); + uint8_t descriptorCapabilities = buf.get8(19); + + if (0 == status) { + zigbee_devices.updateLastSeen(nwkAddr); + + uint8_t deviceType = logicalType & 0x7; // 0=coordinator, 1=router, 2=end device + if (deviceType > 3) { deviceType = 3; } + bool complexDescriptorAvailable = (logicalType & 0x08) ? 1 : 0; + + Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATUS "\":{" + "\"Status\":%d,\"NodeType\":\"%s\",\"ComplexDesc\":%s}}"), + ZIGBEE_STATUS_NODE_DESC, Z_DeviceType[deviceType], + complexDescriptorAvailable ? "true" : "false" + ); + + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED)); + XdrvRulesProcess(); + } + + return -1; +} + +int32_t Z_ReceiveActiveEp(int32_t res, const class SBuffer &buf) { + // Received ZDO_ACTIVE_EP_RSP + Z_ShortAddress srcAddr = buf.get16(2); + uint8_t status = buf.get8(4); + Z_ShortAddress nwkAddr = buf.get16(5); + uint8_t activeEpCount = buf.get8(7); + uint8_t* activeEpList = (uint8_t*) buf.charptr(8); + + + for (uint32_t i = 0; i < activeEpCount; i++) { + zigbee_devices.addEndoint(nwkAddr, activeEpList[i]); + } + + for (uint32_t i = 0; i < activeEpCount; i++) { + Z_SendSimpleDescReq(nwkAddr, activeEpList[i]); + } + + Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATUS "\":{" + "\"Status\":%d,\"ActiveEndpoints\":["), + ZIGBEE_STATUS_ACTIVE_EP); + for (uint32_t i = 0; i < activeEpCount; i++) { + if (i > 0) { ResponseAppend_P(PSTR(",")); } + ResponseAppend_P(PSTR("\"0x%02X\""), activeEpList[i]); + } + ResponseAppend_P(PSTR("]}}")); + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED)); + XdrvRulesProcess(); + return -1; +} + +void Z_SendAFInfoRequest(uint16_t shortaddr, uint8_t endpoint, uint16_t clusterid, uint8_t transacid) { + SBuffer buf(100); + buf.add8(Z_SREQ | Z_AF); // 24 + buf.add8(AF_DATA_REQUEST); // 01 + buf.add16(shortaddr); + buf.add8(endpoint); // dest endpoint + buf.add8(0x01); // source endpoint + buf.add16(clusterid); + buf.add8(transacid); + buf.add8(0x30); // 30 options + buf.add8(0x1E); // 1E radius + + buf.add8(3 + 2*sizeof(uint16_t)); // Len = 0x07 + buf.add8(0x00); // Frame Control Field + buf.add8(transacid); // Transaction Sequence Number + buf.add8(ZCL_READ_ATTRIBUTES); // 00 Command + buf.add16(0x0004); // 0400 ManufacturerName + buf.add16(0x0005); // 0500 ModelIdentifier + + ZigbeeZNPSend(buf.getBuffer(), buf.len()); +} + + +int32_t Z_ReceiveSimpleDesc(int32_t res, const class SBuffer &buf) { + // Received ZDO_SIMPLE_DESC_RSP + Z_ShortAddress srcAddr = buf.get16(2); + uint8_t status = buf.get8(4); + Z_ShortAddress nwkAddr = buf.get16(5); + uint8_t lenDescriptor = buf.get8(7); + uint8_t endpoint = buf.get8(8); + uint16_t profileId = buf.get16(9); // The profile Id for this endpoint. + uint16_t deviceId = buf.get16(11); // The Device Description Id for this endpoint. + uint8_t deviceVersion = buf.get8(13); // 0 – Version 1.00 + uint8_t numInCluster = buf.get8(14); + uint8_t numOutCluster = buf.get8(15 + numInCluster*2); + + if (0 == status) { + zigbee_devices.addEndointProfile(nwkAddr, endpoint, profileId); + for (uint32_t i = 0; i < numInCluster; i++) { + zigbee_devices.addCluster(nwkAddr, endpoint, buf.get16(15 + i*2), false); + } + for (uint32_t i = 0; i < numOutCluster; i++) { + zigbee_devices.addCluster(nwkAddr, endpoint, buf.get16(16 + numInCluster*2 + i*2), true); + } + + Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATUS "\":{" + "\"Status\":%d,\"Endpoint\":\"0x%02X\"" + ",\"ProfileId\":\"0x%04X\",\"DeviceId\":\"0x%04X\",\"DeviceVersion\":%d" + "\"InClusters\":["), + ZIGBEE_STATUS_SIMPLE_DESC, endpoint, + profileId, deviceId, deviceVersion); + for (uint32_t i = 0; i < numInCluster; i++) { + if (i > 0) { ResponseAppend_P(PSTR(",")); } + ResponseAppend_P(PSTR("\"0x%04X\""), buf.get16(15 + i*2)); + } + ResponseAppend_P(PSTR("],\"OutClusters\":[")); + for (uint32_t i = 0; i < numOutCluster; i++) { + if (i > 0) { ResponseAppend_P(PSTR(",")); } + ResponseAppend_P(PSTR("\"0x%04X\""), buf.get16(16 + numInCluster*2 + i*2)); + } + ResponseAppend_P(PSTR("]}}")); + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED)); + XdrvRulesProcess(); + + uint8_t cluster = zigbee_devices.findClusterEndpointIn(nwkAddr, 0x0000); +//Serial.printf(">>> Endpoint is 0x%02X for cluster 0x%04X\n", cluster, 0x0000); + if (cluster) { + Z_SendAFInfoRequest(nwkAddr, cluster, 0x0000, 0x01); // TODO, do we need tarnsacId counter? + } + } + return -1; +} + +int32_t Z_ReceiveEndDeviceAnnonce(int32_t res, const class SBuffer &buf) { + Z_ShortAddress srcAddr = buf.get16(2); + Z_ShortAddress nwkAddr = buf.get16(4); + Z_IEEEAddress ieeeAddr = buf.get64(6); + uint8_t capabilities = buf.get8(14); + + zigbee_devices.updateDevice(nwkAddr, ieeeAddr); + + char hex[20]; + Uint64toHex(ieeeAddr, hex, 64); + Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATUS "\":{" + "\"Status\":%d,\"IEEEAddr\":\"%s\",\"ShortAddr\":\"0x%04X\"" + ",\"PowerSource\":%s,\"ReceiveWhenIdle\":%s,\"Security\":%s}}"), + ZIGBEE_STATUS_DEVICE_ANNOUNCE, hex, nwkAddr, + (capabilities & 0x04) ? "true" : "false", + (capabilities & 0x08) ? "true" : "false", + (capabilities & 0x40) ? "true" : "false" + ); + + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED)); + XdrvRulesProcess(); + Z_SendActiveEpReq(nwkAddr); + return -1; +} + +int32_t Z_ReceiveAfIncomingMessage(int32_t res, const class SBuffer &buf) { + uint16_t groupid = buf.get16(2); + uint16_t clusterid = buf.get16(4); + Z_ShortAddress srcaddr = buf.get16(6); + uint8_t srcendpoint = buf.get8(8); + uint8_t dstendpoint = buf.get8(9); + uint8_t wasbroadcast = buf.get8(10); + uint8_t linkquality = buf.get8(11); + uint8_t securityuse = buf.get8(12); + uint32_t timestamp = buf.get32(13); + uint8_t seqnumber = buf.get8(17); + + zigbee_devices.updateLastSeen(srcaddr); + ZCLFrame zcl_received = ZCLFrame::parseRawFrame(buf, 19, buf.get8(18), clusterid, groupid); + +#ifdef ZIGBEE_VERBOSE + zcl_received.publishMQTTReceived(groupid, clusterid, srcaddr, + srcendpoint, dstendpoint, wasbroadcast, + linkquality, securityuse, seqnumber, + timestamp); +#endif + + char shortaddr[8]; + snprintf_P(shortaddr, sizeof(shortaddr), PSTR("0x%04X"), srcaddr); + + DynamicJsonBuffer jsonBuffer; + JsonObject& json_root = jsonBuffer.createObject(); + JsonObject& json1 = json_root.createNestedObject(F(D_CMND_ZIGBEE_RECEIVED)); + JsonObject& json = json1.createNestedObject(shortaddr); + + // TODO add name field if it is known + if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_REPORT_ATTRIBUTES == zcl_received.getCmdId())) { + zcl_received.parseRawAttributes(json); + } else if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_READ_ATTRIBUTES_RESPONSE == zcl_received.getCmdId())) { + zcl_received.parseReadAttributes(json); + } else if (zcl_received.isClusterSpecificCommand()) { + zcl_received.parseClusterSpecificCommand(json); + } + String msg(""); + msg.reserve(100); + json_root.printTo(msg); + AddLog_P2(LOG_LEVEL_INFO, PSTR("ZigbeeZCLRawReceived: %s"), msg.c_str()); + + zcl_received.postProcessAttributes(srcaddr, json); + // Add linkquality + json[F("_" D_CMND_ZIGBEE_LINKQUALITY)] = linkquality; // prefix with underscore for metadata + + msg = ""; + json_root.printTo(msg); + Response_P(PSTR("%s"), msg.c_str()); + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR)); + XdrvRulesProcess(); + return -1; +} + +typedef struct Z_Dispatcher { + const uint8_t* match; + ZB_RecvMsgFunc func; +} Z_Dispatcher; + +// Filters for ZCL frames +ZBM(AREQ_AF_INCOMING_MESSAGE, Z_AREQ | Z_AF, AF_INCOMING_MSG) // 4481 +ZBM(AREQ_END_DEVICE_ANNCE_IND, Z_AREQ | Z_ZDO, ZDO_END_DEVICE_ANNCE_IND) // 45C1 +ZBM(AREQ_PERMITJOIN_OPEN_XX, Z_AREQ | Z_ZDO, ZDO_PERMIT_JOIN_IND ) // 45CB +ZBM(AREQ_ZDO_ACTIVEEPRSP, Z_AREQ | Z_ZDO, ZDO_ACTIVE_EP_RSP) // 4585 +ZBM(AREQ_ZDO_SIMPLEDESCRSP, Z_AREQ | Z_ZDO, ZDO_SIMPLE_DESC_RSP) // 4584 + +const Z_Dispatcher Z_DispatchTable[] PROGMEM = { + { AREQ_AF_INCOMING_MESSAGE, &Z_ReceiveAfIncomingMessage }, + { AREQ_END_DEVICE_ANNCE_IND, &Z_ReceiveEndDeviceAnnonce }, + { AREQ_PERMITJOIN_OPEN_XX, &Z_ReceivePermitJoinStatus }, + { AREQ_ZDO_NODEDESCRSP, &Z_ReceiveNodeDesc }, + { AREQ_ZDO_ACTIVEEPRSP, &Z_ReceiveActiveEp }, + { AREQ_ZDO_SIMPLEDESCRSP, &Z_ReceiveSimpleDesc }, +}; + +int32_t Z_Recv_Default(int32_t res, const class SBuffer &buf) { + // Default message handler for new messages + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZIG: Z_Recv_Default")); + if (zigbee.init_phase) { + // if still during initialization phase, ignore any unexpected message + return -1; // ignore message + } else { + for (uint32_t i = 0; i < sizeof(Z_DispatchTable)/sizeof(Z_Dispatcher); i++) { + if (Z_ReceiveMatchPrefix(buf, Z_DispatchTable[i].match)) { + (*Z_DispatchTable[i].func)(res, buf); + } + } + return -1; + } +} + +int32_t Z_State_Ready(uint8_t value) { + zigbee.init_phase = false; // initialization phase complete + return 0; // continue +} + +#endif // USE_ZIGBEE diff --git a/sonoff/xdrv_23_zigbee_9_impl.ino b/sonoff/xdrv_23_zigbee_9_impl.ino new file mode 100644 index 000000000..07fec54f4 --- /dev/null +++ b/sonoff/xdrv_23_zigbee_9_impl.ino @@ -0,0 +1,576 @@ +/* + xdrv_23_zigbee.ino - zigbee support for Sonoff-Tasmota + + Copyright (C) 2019 Theo Arends and Stephan Hadinger + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef USE_ZIGBEE + +#define XDRV_23 23 + +const uint32_t ZIGBEE_BUFFER_SIZE = 256; // Max ZNP frame is SOF+LEN+CMD1+CMD2+250+FCS = 255 +const uint8_t ZIGBEE_SOF = 0xFE; +const uint8_t ZIGBEE_SOF_ALT = 0xFF; + +//#define Z_USE_SOFTWARE_SERIAL + +#ifdef Z_USE_SOFTWARE_SERIAL +#include +SoftwareSerial *ZigbeeSerial = nullptr; +#else +#include +TasmotaSerial *ZigbeeSerial = nullptr; +#endif + + +const char kZigbeeCommands[] PROGMEM = "|" D_CMND_ZIGBEEZNPSEND "|" D_CMND_ZIGBEE_PERMITJOIN + "|" D_CMND_ZIGBEE_STATUS "|" D_CMND_ZIGBEE_RESET "|" D_CMND_ZIGBEE_ZCL_SEND + "|" D_CMND_ZIGBEE_PROBE "|" D_CMND_ZIGBEE_READ; + +void (* const ZigbeeCommand[])(void) PROGMEM = { &CmndZigbeeZNPSend, &CmndZigbeePermitJoin, + &CmndZigbeeStatus, &CmndZigbeeReset, &CmndZigbeeZCLSend, + &CmndZigbeeProbe, &CmndZigbeeRead }; + +int32_t ZigbeeProcessInput(class SBuffer &buf) { + if (!zigbee.state_machine) { return -1; } // if state machine is stopped, send 'ignore' message + + // apply the receive filter, acts as 'startsWith()' + bool recv_filter_match = true; + bool recv_prefix_match = false; // do the first 2 bytes match the response + if ((zigbee.recv_filter) && (zigbee.recv_filter_len > 0)) { + if (zigbee.recv_filter_len >= 2) { + recv_prefix_match = false; + if ( (pgm_read_byte(&zigbee.recv_filter[0]) == buf.get8(0)) && + (pgm_read_byte(&zigbee.recv_filter[1]) == buf.get8(1)) ) { + recv_prefix_match = true; + } + } + + for (uint32_t i = 0; i < zigbee.recv_filter_len; i++) { + if (pgm_read_byte(&zigbee.recv_filter[i]) != buf.get8(i)) { + recv_filter_match = false; + break; + } + } + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZIG: ZigbeeProcessInput: recv_prefix_match = %d, recv_filter_match = %d"), recv_prefix_match, recv_filter_match); + } + + // if there is a recv_callback, call it now + int32_t res = -1; // default to ok + // res = 0 - proceed to next state + // res > 0 - proceed to the specified state + // res = -1 - silently ignore the message + // res <= -2 - move to error state + // pre-compute the suggested value + if ((zigbee.recv_filter) && (zigbee.recv_filter_len > 0)) { + if (!recv_prefix_match) { + res = -1; // ignore + } else { // recv_prefix_match + if (recv_filter_match) { + res = 0; // ok + } else { + if (zigbee.recv_until) { + res = -1; // ignore until full match + } else { + res = -2; // error, because message is expected but wrong value + } + } + } + } else { // we don't have any filter, ignore message by default + res = -1; + } + + if (recv_prefix_match) { + if (zigbee.recv_func) { + res = (*zigbee.recv_func)(res, buf); + } + } + if (-1 == res) { + // if frame was ignored up to now + if (zigbee.recv_unexpected) { + res = (*zigbee.recv_unexpected)(res, buf); + } + } + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZIG: ZigbeeProcessInput: res = %d"), res); + + // change state accordingly + if (0 == res) { + // if ok, continue execution + zigbee.state_waiting = false; + } else if (res > 0) { + ZigbeeGotoLabel(res); // if >0 then go to specified label + } else if (-1 == res) { + // -1 means ignore message + // just do nothing + } else { + // any other negative value means error + ZigbeeGotoLabel(zigbee.on_error_goto); + } +} + +void ZigbeeInput(void) +{ + static uint32_t zigbee_polling_window = 0; + static uint8_t fcs = ZIGBEE_SOF; + static uint32_t zigbee_frame_len = 5; // minimal zigbee frame lenght, will be updated when buf[1] is read + // Receive only valid ZNP frames: + // 00 - SOF = 0xFE + // 01 - Length of Data Field - 0..250 + // 02 - CMD1 - first byte of command + // 03 - CMD2 - second byte of command + // 04..FD - Data Field + // FE (or last) - FCS Checksum + + while (ZigbeeSerial->available()) { + yield(); + uint8_t zigbee_in_byte = ZigbeeSerial->read(); + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZigbeeInput byte=%d len=%d"), zigbee_in_byte, zigbee_buffer->len()); + + if (0 == zigbee_buffer->len()) { // make sure all variables are correctly initialized + zigbee_frame_len = 5; + fcs = ZIGBEE_SOF; + // there is a rare race condition when an interrupt occurs when receiving the first byte + // in this case the first bit (lsb) is missed and Tasmota receives 0xFF instead of 0xFE + // We forgive this mistake, and next bytes are automatically resynchronized + if (ZIGBEE_SOF_ALT == zigbee_in_byte) { + AddLog_P2(LOG_LEVEL_INFO, PSTR("ZigbeeInput forgiven first byte %02X (only for statistics)"), zigbee_in_byte); + zigbee_in_byte = ZIGBEE_SOF; + } + } + + if ((0 == zigbee_buffer->len()) && (ZIGBEE_SOF != zigbee_in_byte)) { + // waiting for SOF (Start Of Frame) byte, discard anything else + AddLog_P2(LOG_LEVEL_INFO, PSTR("ZigbeeInput discarding byte %02X"), zigbee_in_byte); + continue; // discard + } + + if (zigbee_buffer->len() < zigbee_frame_len) { + zigbee_buffer->add8(zigbee_in_byte); + zigbee_polling_window = millis(); // Wait for more data + fcs ^= zigbee_in_byte; + } + + if (zigbee_buffer->len() >= zigbee_frame_len) { + zigbee_polling_window = 0; // Publish now + break; + } + + // recalculate frame length + if (02 == zigbee_buffer->len()) { + // We just received the Lenght byte + uint8_t len_byte = zigbee_buffer->get8(1); + if (len_byte > 250) len_byte = 250; // ZNP spec says len is 250 max + + zigbee_frame_len = len_byte + 5; // SOF + LEN + CMD1 + CMD2 + FCS = 5 bytes overhead + } + } + + if (zigbee_buffer->len() && (millis() > (zigbee_polling_window + ZIGBEE_POLLING))) { + char hex_char[(zigbee_buffer->len() * 2) + 2]; + ToHex_P((unsigned char*)zigbee_buffer->getBuffer(), zigbee_buffer->len(), hex_char, sizeof(hex_char)); + +#ifndef Z_USE_SOFTWARE_SERIAL + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZIG: Bytes follor_read_metric = %0d"), ZigbeeSerial->getLoopReadMetric()); +#endif + // buffer received, now check integrity + if (zigbee_buffer->len() != zigbee_frame_len) { + // Len is not correct, log and reject frame + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_JSON_ZIGBEEZNPRECEIVED ": received frame of wrong size %s, len %d, expected %d"), hex_char, zigbee_buffer->len(), zigbee_frame_len); + } else if (0x00 != fcs) { + // FCS is wrong, packet is corrupt, log and reject frame + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_JSON_ZIGBEEZNPRECEIVED ": received bad FCS frame %s, %d"), hex_char, fcs); + } else { + // frame is correct + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_JSON_ZIGBEEZNPRECEIVED ": received correct frame %s"), hex_char); + + SBuffer znp_buffer = zigbee_buffer->subBuffer(2, zigbee_frame_len - 3); // remove SOF, LEN and FCS + +#ifdef ZIGBEE_VERBOSE + ToHex_P((unsigned char*)znp_buffer.getBuffer(), znp_buffer.len(), hex_char, sizeof(hex_char)); + Response_P(PSTR("{\"" D_JSON_ZIGBEEZNPRECEIVED "\":\"%s\"}"), hex_char); + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZNPRECEIVED)); + XdrvRulesProcess(); +#endif + + // now process the message + ZigbeeProcessInput(znp_buffer); + } + zigbee_buffer->setLen(0); // empty buffer + } +} + +/********************************************************************************************/ + +void ZigbeeInit(void) +{ + zigbee.active = false; + if ((pin[GPIO_ZIGBEE_RX] < 99) && (pin[GPIO_ZIGBEE_TX] < 99)) { + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("Zigbee: GPIOs Rx:%d Tx:%d"), pin[GPIO_ZIGBEE_RX], pin[GPIO_ZIGBEE_TX]); +#ifdef Z_USE_SOFTWARE_SERIAL + ZigbeeSerial = new SoftwareSerial(); + ZigbeeSerial->begin(115200, pin[GPIO_ZIGBEE_RX], pin[GPIO_ZIGBEE_TX], SWSERIAL_8N1, false, 256); // ZNP is 115200, RTS/CTS (ignored), 8N1 + ZigbeeSerial->enableIntTx(false); + zigbee_buffer = new SBuffer(ZIGBEE_BUFFER_SIZE); +#else + ZigbeeSerial = new TasmotaSerial(pin[GPIO_ZIGBEE_RX], pin[GPIO_ZIGBEE_TX], 0, 0, 256); // set a receive buffer of 256 bytes + ZigbeeSerial->begin(115200); + if (ZigbeeSerial->hardwareSerial()) { + ClaimSerial(); + zigbee_buffer = new PreAllocatedSBuffer(sizeof(serial_in_buffer), serial_in_buffer); + } else { + zigbee_buffer = new SBuffer(ZIGBEE_BUFFER_SIZE); + } +#endif + zigbee.active = true; + zigbee.init_phase = true; // start the state machine + zigbee.state_machine = true; // start the state machine + ZigbeeSerial->flush(); + } +} + +/*********************************************************************************************\ + * Commands +\*********************************************************************************************/ + +uint32_t strToUInt(const JsonVariant &val) { + // if the string starts with 0x, it is considered Hex, otherwise it is an int + if (val.is()) { + return val.as(); + } else { + if (val.is()) { + return strtoull(val.as(), nullptr, 0); + } + } + return 0; // couldn't parse anything +} + +const unsigned char ZIGBEE_FACTORY_RESET[] PROGMEM = "2112000F0100"; // Z_SREQ | Z_SYS, SYS_OSAL_NV_DELETE, 0x0F00 id, 0x0001 len +// Do a factory reset of the CC2530 +void CmndZigbeeReset(void) { + if (ZigbeeSerial) { + switch (XdrvMailbox.payload) { + case 1: + ZigbeeZNPSend(ZIGBEE_FACTORY_RESET, sizeof(ZIGBEE_FACTORY_RESET)); + restart_flag = 2; + ResponseCmndChar(D_JSON_ZIGBEE_CC2530 " " D_JSON_RESET_AND_RESTARTING); + break; + default: + ResponseCmndChar(D_JSON_ONE_TO_RESET); + } + } +} + +void CmndZigbeeStatus(void) { + if (ZigbeeSerial) { + String dump = zigbee_devices.dump(XdrvMailbox.payload); + Response_P(PSTR("{\"%s%d\":%s}"), XdrvMailbox.command, XdrvMailbox.payload, dump.c_str()); + } +} + +void CmndZigbeeZNPSend(void) +{ + if (ZigbeeSerial && (XdrvMailbox.data_len > 0)) { + uint8_t code; + + char *codes = RemoveSpace(XdrvMailbox.data); + int32_t size = strlen(XdrvMailbox.data); + + SBuffer buf((size+1)/2); + + while (size > 1) { + char stemp[3]; + strlcpy(stemp, codes, sizeof(stemp)); + code = strtol(stemp, nullptr, 16); + buf.add8(code); + size -= 2; + codes += 2; + } + ZigbeeZNPSend(buf.getBuffer(), buf.len()); + } + ResponseCmndDone(); +} + +void ZigbeeZNPSend(const uint8_t *msg, size_t len) { + if ((len < 2) || (len > 252)) { + // abort, message cannot be less than 2 bytes for CMD1 and CMD2 + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_JSON_ZIGBEEZNPSENT ": bad message len %d"), len); + return; + } + uint8_t data_len = len - 2; // removing CMD1 and CMD2 + + if (ZigbeeSerial) { + uint8_t fcs = data_len; + + ZigbeeSerial->write(ZIGBEE_SOF); // 0xFE + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZNPSend SOF %02X"), ZIGBEE_SOF); + ZigbeeSerial->write(data_len); + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZNPSend LEN %02X"), data_len); + for (uint32_t i = 0; i < len; i++) { + uint8_t b = pgm_read_byte(msg + i); + ZigbeeSerial->write(b); + fcs ^= b; + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZNPSend byt %02X"), b); + } + ZigbeeSerial->write(fcs); // finally send fcs checksum byte + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZNPSend FCS %02X"), fcs); + } +#ifdef ZIGBEE_VERBOSE + // Now send a MQTT message to report the sent message + char hex_char[(len * 2) + 2]; + Response_P(PSTR("{\"" D_JSON_ZIGBEEZNPSENT "\":\"%s\"}"), + ToHex_P(msg, len, hex_char, sizeof(hex_char))); + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZNPSENT)); + XdrvRulesProcess(); +#endif +} + +void ZigbeeZCLSend(uint16_t dtsAddr, uint16_t clusterId, uint8_t endpoint, uint8_t cmdId, bool clusterSpecific, const uint8_t *msg, size_t len, bool disableDefResp = true, uint8_t transacId = 1) { + SBuffer buf(25+len); + buf.add8(Z_SREQ | Z_AF); // 24 + buf.add8(AF_DATA_REQUEST); // 01 + buf.add16(dtsAddr); + buf.add8(endpoint); // dest endpoint + buf.add8(0x01); // source endpoint + buf.add16(clusterId); + buf.add8(transacId); // transacId + buf.add8(0x30); // 30 options + buf.add8(0x1E); // 1E radius + + buf.add8(3 + len); + buf.add8((disableDefResp ? 0x10 : 0x00) | (clusterSpecific ? 0x01 : 0x00)); // Frame Control Field + buf.add8(transacId); // Transaction Sequance Number + buf.add8(cmdId); + if (len > 0) { + buf.addBuffer(msg, len); // add the payload + } + + ZigbeeZNPSend(buf.getBuffer(), buf.len()); +} + +inline int8_t hexValue(char c) { + if ((c >= '0') && (c <= '9')) { + return c - '0'; + } + if ((c >= 'A') && (c <= 'F')) { + return 10 + c - 'A'; + } + if ((c >= 'a') && (c <= 'f')) { + return 10 + c - 'a'; + } + return -1; +} + +uint32_t parseHex(const char **data, size_t max_len = 8) { + uint32_t ret = 0; + for (uint32_t i = 0; i < max_len; i++) { + int8_t v = hexValue(**data); + if (v < 0) { break; } // non hex digit, we stop parsing + ret = (ret << 4) | v; + *data += 1; + } + return ret; +} + +void CmndZigbeeZCLSend(void) { + char parm_uc[12]; // used to convert JSON keys to uppercase + // ZigbeeZCLSend { "dst":"0x1234", "endpoint":"0x01", "cmd":"AABBCC" } + char dataBufUc[XdrvMailbox.data_len]; + UpperCase(dataBufUc, XdrvMailbox.data); + RemoveSpace(dataBufUc); + if (strlen(dataBufUc) < 8) { ResponseCmndChar(D_JSON_INVALID_JSON); return; } + + DynamicJsonBuffer jsonBuf; + JsonObject &json = jsonBuf.parseObject(dataBufUc); + if (!json.success()) { ResponseCmndChar(D_JSON_INVALID_JSON); return; } + + // params + uint16_t dstAddr = 0xFFFF; // 0xFFFF is braodcast, so considered invalid + uint16_t clusterId = 0x0000; // 0x0000 is a valid default value + uint8_t endpoint = 0x00; // 0x00 is invalid for the dst endpoint + uint8_t cmd = ZCL_READ_ATTRIBUTES; // default command is READ_ATTRIBUTES + bool clusterSpecific = false; + const char* data = ""; // empty string is valid + + UpperCase_P(parm_uc, PSTR("device")); + if (json.containsKey(parm_uc)) { dstAddr = strToUInt(json[parm_uc]); } + UpperCase_P(parm_uc, PSTR("endpoint")); + if (json.containsKey(parm_uc)) { endpoint = strToUInt(json[parm_uc]); } + UpperCase_P(parm_uc, PSTR("cmd")); + if (json.containsKey(parm_uc)) { data = json[parm_uc].as(); } + + // Parse 'cmd' in the form "AAAA_BB/CCCCCCCC" or "AAAA!BB/CCCCCCCC" + // where AA is the cluster number, BBBB the command number, CCCC... the payload + // First delimiter is '_' for a global command, or '!' for a cluster specific commanc + clusterId = parseHex(&data, 4); + + // delimiter + if (('_' == *data) || ('!' == *data)) { + if ('!' == *data) { clusterSpecific = true; } + data++; + } else { + ResponseCmndChar("Wrong delimiter for payload"); + return; + } + // parse cmd number + cmd = parseHex(&data, 2); + + // move to end of payload + // delimiter is optional + if ('/' == *data) { data++; } // skip delimiter + + size_t size = strlen(data); + SBuffer buf((size+2)/2); // actual bytes buffer for data + + while (*data) { + uint8_t code = parseHex(&data, 2); + buf.add8(code); + } + + if (0 == endpoint) { + // endpoint is not specified, let's try to find it from shortAddr + endpoint = zigbee_devices.findClusterEndpointIn(dstAddr, clusterId); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CmndZigbeeZCLSend: guessing endpoint 0x%02X"), endpoint); + } + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CmndZigbeeZCLSend: dstAddr 0x%04X, cluster 0x%04X, endpoint 0x%02X, cmd 0x%02X, data %s"), + dstAddr, clusterId, endpoint, cmd, data); + + if (0 == endpoint) { + AddLog_P2(LOG_LEVEL_INFO, PSTR("CmndZigbeeZCLSend: unspecified endpoint")); + return; + } + + // everything is good, we can send the command + ZigbeeZCLSend(dstAddr, clusterId, endpoint, cmd, clusterSpecific, buf.getBuffer(), buf.len()); + ResponseCmndDone(); +} + +// Probe a specific device to get its endpoints and supported clusters +void CmndZigbeeProbe(void) { + char dataBufUc[XdrvMailbox.data_len]; + UpperCase(dataBufUc, XdrvMailbox.data); + RemoveSpace(dataBufUc); + if (strlen(dataBufUc) < 3) { ResponseCmndChar("Invalid destination"); return; } + + // TODO, for now ignore friendly names + uint16_t shortaddr = strtoull(dataBufUc, nullptr, 0); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CmndZigbeeScan: short addr 0x%04X"), shortaddr); + + // everything is good, we can send the command + Z_SendActiveEpReq(shortaddr); + ResponseCmndDone(); +} + +// Send an attribute read command to a device, specifying cluster and list of attributes +void CmndZigbeeRead(void) { + char parm_uc[12]; // used to convert JSON keys to uppercase + // ZigbeeRead {"Device":"0xF289","Cluster":0,"Endpoint":3,"Attr":5} + // ZigbeeRead {"Device":"0xF289","Cluster":"0x0000","Endpoint":"0x0003","Attr":"0x0005"} + // ZigbeeRead {"Device":"0xF289","Cluster":0,"Endpoint":3,"Attr":[5,6,7,4]} + char dataBufUc[XdrvMailbox.data_len]; + UpperCase(dataBufUc, XdrvMailbox.data); + RemoveSpace(dataBufUc); + if (strlen(dataBufUc) < 8) { ResponseCmndChar(D_JSON_INVALID_JSON); return; } + + DynamicJsonBuffer jsonBuf; + JsonObject &json = jsonBuf.parseObject(dataBufUc); + if (!json.success()) { ResponseCmndChar(D_JSON_INVALID_JSON); return; } + + // params + uint16_t dstAddr = 0x0000; // default to local address + uint16_t cluster = 0x0000; // default to general clsuter + uint8_t endpoint = 0x00; // 0x00 is invalid for the dst endpoint + size_t attrs_len = 0; + uint8_t* attrs = nullptr; // empty string is valid + + dstAddr = strToUInt(json[UpperCase_P(parm_uc, PSTR("device"))]); + endpoint = strToUInt(json[UpperCase_P(parm_uc, PSTR("endpoint"))]); + cluster = strToUInt(json[UpperCase_P(parm_uc, PSTR("cluster"))]); + UpperCase_P(parm_uc, PSTR("Attr")); + if (json.containsKey(parm_uc)) { + const JsonVariant& attr_data = json[parm_uc]; + if (attr_data.is()) { + JsonArray& attr_arr = attr_data; + attrs_len = attr_arr.size() * 2; + attrs = new uint8_t[attrs_len]; + + uint32_t i = 0; + for (auto value : attr_arr) { + uint16_t val = strToUInt(value); + attrs[i++] = val & 0xFF; + attrs[i++] = val >> 8; + } + + } else { + attrs_len = 2; + attrs = new uint8_t[attrs_len]; + uint16_t val = strToUInt(attr_data); + attrs[0] = val & 0xFF; // little endian + attrs[1] = val >> 8; + } + } + + ZigbeeZCLSend(dstAddr, cluster, endpoint, ZCL_READ_ATTRIBUTES, false, attrs, attrs_len, false /* we do want a response */); + + if (attrs) { delete[] attrs; } +} + +// Allow or Deny pairing of new Zigbee devices +void CmndZigbeePermitJoin(void) +{ + uint32_t payload = XdrvMailbox.payload; + if (payload < 0) { payload = 0; } + if ((99 != payload) && (payload > 1)) { payload = 1; } + + if (1 == payload) { + ZigbeeGotoLabel(ZIGBEE_LABEL_PERMIT_JOIN_OPEN_60); + } else if (99 == payload){ + ZigbeeGotoLabel(ZIGBEE_LABEL_PERMIT_JOIN_OPEN_XX); + } else { + ZigbeeGotoLabel(ZIGBEE_LABEL_PERMIT_JOIN_CLOSE); + } + ResponseCmndDone(); +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xdrv23(uint8_t function) +{ + bool result = false; + + if (zigbee.active) { + switch (function) { + case FUNC_LOOP: + if (ZigbeeSerial) { ZigbeeInput(); } + if (zigbee.state_machine) { + //ZigbeeStateMachine(); + ZigbeeStateMachine_Run(); + } + break; + case FUNC_PRE_INIT: + ZigbeeInit(); + break; + case FUNC_COMMAND: + result = DecodeCommand(kZigbeeCommands, ZigbeeCommand); + break; + } + } + return result; +} + +#endif // USE_ZIGBEE diff --git a/sonoff/xdrv_24_buzzer.ino b/sonoff/xdrv_24_buzzer.ino new file mode 100644 index 000000000..d6cf24683 --- /dev/null +++ b/sonoff/xdrv_24_buzzer.ino @@ -0,0 +1,205 @@ +/* + xdrv_24_Buzzer.ino - buzzer support for Sonoff-Tasmota + + Copyright (C) 2019 Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef USE_BUZZER +/*********************************************************************************************\ + * Buzzer support +\*********************************************************************************************/ + +#define XDRV_24 24 + +struct BUZZER { + uint32_t tune = 0; + bool active = true; + bool enable = false; + uint8_t inverted = 0; // Buzzer inverted flag (1 = (0 = On, 1 = Off)) + uint8_t count = 0; // Number of buzzes + uint8_t set[2]; + uint8_t duration; + uint8_t state = 0; +} Buzzer; + +/*********************************************************************************************/ + +void BuzzerOff(void) +{ + digitalWrite(pin[GPIO_BUZZER], Buzzer.inverted); // Buzzer Off +} + +//void BuzzerBeep(uint32_t count = 1, uint32_t on = 1, uint32_t off = 1, uint32_t tune = 0); +void BuzzerBeep(uint32_t count, uint32_t on, uint32_t off, uint32_t tune) +{ + Buzzer.set[0] = off; // off duration in 100 mSec steps + Buzzer.set[1] = on; // on duration in 100 mSec steps + Buzzer.duration = 1; // Start buzzer on first step + Buzzer.tune = 0; + if (tune) { + uint32_t tune1 = tune; + uint32_t tune2 = tune; + for (uint32_t i = 0; i < 32; i++) { + if (!(tune2 & 0x80000000)) { + tune2 <<= 1; // Skip leading silence + } else { + Buzzer.tune <<= 1; // Add swapped tune + Buzzer.tune |= tune1 & 1; + tune1 >>= 1; + } + } + Buzzer.count = 1; // Allow tune only once + } else { + Buzzer.count = count * 2; // Start buzzer + } + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("BUZ: %d(%d),%d,%d,0x%08X(0x%08X)"), count, Buzzer.count, on, off, tune, Buzzer.tune); + + Buzzer.enable = (Buzzer.count > 0); + if (!Buzzer.enable) { + BuzzerOff(); + } +} + +void BuzzerBeep(uint32_t count) { + BuzzerBeep(count, 1, 1, 0); +} + +void BuzzerEnabledBeep(uint32_t count, uint32_t duration) +{ + if (Settings.flag3.buzzer_enable) { // SetOption67 + BuzzerBeep(count, duration, 1, 0); + } +} + +/*********************************************************************************************/ + +bool BuzzerPinState(void) +{ + if (XdrvMailbox.index == GPIO_BUZZER_INV) { + Buzzer.inverted = 1; + XdrvMailbox.index -= (GPIO_BUZZER_INV - GPIO_BUZZER); + return true; + } + return false; +} + +void BuzzerInit(void) +{ + if (pin[GPIO_BUZZER] < 99) { + pinMode(pin[GPIO_BUZZER], OUTPUT); + BuzzerOff(); + } else { + Buzzer.active = false; + } +} + +void BuzzerEvery100mSec(void) +{ + if (Buzzer.enable) { + if (Buzzer.count) { + if (Buzzer.duration) { + Buzzer.duration--; + if (!Buzzer.duration) { + if (Buzzer.tune) { + Buzzer.state = Buzzer.tune & 1; + Buzzer.tune >>= 1; + } else { + Buzzer.count--; + Buzzer.state = Buzzer.count & 1; + } + Buzzer.duration = Buzzer.set[Buzzer.state]; + } + } + digitalWrite(pin[GPIO_BUZZER], (Buzzer.inverted) ? !Buzzer.state : Buzzer.state); + } else { + Buzzer.enable = false; + } + } +} + +/*********************************************************************************************\ + * Commands +\*********************************************************************************************/ + +const char kBuzzerCommands[] PROGMEM = "|" // No prefix + "Buzzer" ; + +void (* const BuzzerCommand[])(void) PROGMEM = { + &CmndBuzzer }; + +void CmndBuzzer(void) +{ + // Buzzer ,,, + // All parameters are optional + // + // Buzzer = Buzzer 1,1,1 = Beep once with both duration and pause set to 100mS + // Buzzer 0 = Stop active beep cycle + // Buzzer 2 = Beep twice with duration 200mS and pause 100mS + // Buzzer 2,3 = Beep twice with duration 300mS and pause 100mS + // Buzzer 2,3,4 = Beep twice with duration 300mS and pause 400mS + // Buzzer 2,3,4,0xF54 = Beep a sequence once indicated by 0xF54 = 1111 0101 01 with duration 300mS and pause 400mS + + if (XdrvMailbox.data_len > 0) { + if (XdrvMailbox.payload > 0) { + char *p; + uint32_t i = 0; + uint32_t parm[4] = { 0 }; + for (char *str = strtok_r(XdrvMailbox.data, ", ", &p); str && i < 4; str = strtok_r(nullptr, ", ", &p)) { + parm[i] = strtoul(str, nullptr, 0); + i++; + } + for (uint32_t i = 0; i < 3; i++) { + if (parm[i] < 1) { parm[i] = 1; } // Default Count, On time, Off time + } + BuzzerBeep(parm[0], parm[1], parm[2], parm[3]); + } else { + BuzzerBeep(0); + } + } else { + BuzzerBeep(1); + } + ResponseCmndDone(); +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xdrv24(uint8_t function) +{ + bool result = false; + + if (Buzzer.active) { + switch (function) { + case FUNC_EVERY_100_MSECOND: + BuzzerEvery100mSec(); + break; + case FUNC_COMMAND: + result = DecodeCommand(kBuzzerCommands, BuzzerCommand); + break; + case FUNC_PRE_INIT: + BuzzerInit(); + break; + case FUNC_PIN_STATE: + result = BuzzerPinState(); + break; + } + } + return result; +} + +#endif // USE_BUZZER \ No newline at end of file diff --git a/sonoff/xdrv_25_A4988_Stepper.ino b/sonoff/xdrv_25_A4988_Stepper.ino new file mode 100644 index 000000000..f863b9768 --- /dev/null +++ b/sonoff/xdrv_25_A4988_Stepper.ino @@ -0,0 +1,138 @@ + +/* + xdrv_25_a4988_stepper.ino - A4988 StepMotorDriverCircuit- support for Sonoff-Tasmota + + Copyright (C) 2019 Tim Leuscher and Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef USE_A4988_STEPPER +/*********************************************************************************************\ + * A4988 Stepper motor driver circuit +\*********************************************************************************************/ + +#define XDRV_25 25 + +#include + +short A4988_dir_pin = pin[GPIO_MAX]; +short A4988_stp_pin = pin[GPIO_MAX]; +short A4988_ms1_pin = pin[GPIO_MAX]; +short A4988_ms2_pin = pin[GPIO_MAX]; +short A4988_ms3_pin = pin[GPIO_MAX]; +short A4988_ena_pin = pin[GPIO_MAX]; +int A4988_spr = 0; +float A4988_rpm = 0; +short A4988_mis = 0; + +A4988_Stepper* myA4988 = nullptr; + +void A4988Init(void) +{ + A4988_dir_pin = pin[GPIO_A4988_DIR]; + A4988_stp_pin = pin[GPIO_A4988_STP]; + A4988_ena_pin = pin[GPIO_A4988_ENA]; + A4988_ms1_pin = pin[GPIO_A4988_MS1]; + A4988_ms2_pin = pin[GPIO_A4988_MS2]; + A4988_ms3_pin = pin[GPIO_A4988_MS3]; + A4988_spr = 200; + A4988_rpm = 30; + A4988_mis = 1; + + myA4988 = new A4988_Stepper( A4988_spr + , A4988_rpm + , A4988_mis + , A4988_dir_pin + , A4988_stp_pin + , A4988_ena_pin + , A4988_ms1_pin + , A4988_ms2_pin + , A4988_ms3_pin ); +} + +const char kA4988Commands[] PROGMEM = "Motor|" // prefix + "Move|Rotate|Turn|MIS|SPR|RPM"; + +void (* const A4988Command[])(void) PROGMEM = { + &CmndDoMove,&CmndDoRotate,&CmndDoTurn,&CmndSetMIS,&CmndSetSPR,&CmndSetRPM}; + +void CmndDoMove(void) { + if (XdrvMailbox.data_len > 0) { + long stepsPlease = strtoul(XdrvMailbox.data,nullptr,10); + myA4988->doMove(stepsPlease); + ResponseCmndDone(); + } +} + +void CmndDoRotate(void) { + if (XdrvMailbox.data_len > 0) { + long degrsPlease = strtoul(XdrvMailbox.data,nullptr,10); + myA4988->doRotate(degrsPlease); + ResponseCmndDone(); + } +} + +void CmndDoTurn(void) { + if (XdrvMailbox.data_len > 0) { + float turnsPlease = strtod(XdrvMailbox.data,nullptr); + myA4988->doTurn(turnsPlease); + ResponseCmndDone(); + } +} + +void CmndSetMIS(void) { + if ((pin[GPIO_A4988_MS1] < 99) && (pin[GPIO_A4988_MS2] < 99) && (pin[GPIO_A4988_MS3] < 99) && (XdrvMailbox.data_len > 0)) { + short newMIS = strtoul(XdrvMailbox.data,nullptr,10); + myA4988->setMIS(newMIS); + ResponseCmndDone(); + } +} + +void CmndSetSPR(void) { + if (XdrvMailbox.data_len > 0) { + int newSPR = strtoul(XdrvMailbox.data,nullptr,10); + myA4988->setSPR(newSPR); + ResponseCmndDone(); + } +} + +void CmndSetRPM(void) { + if (XdrvMailbox.data_len > 0) { + short newRPM = strtoul(XdrvMailbox.data,nullptr,10); + myA4988->setRPM(newRPM); + ResponseCmndDone(); + } +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ +bool Xdrv25(uint8_t function) +{ + bool result = false; + if ((pin[GPIO_A4988_DIR] < 99) && (pin[GPIO_A4988_STP] < 99)) { + switch (function) { + case FUNC_INIT: + A4988Init(); + break; + case FUNC_COMMAND: + result = DecodeCommand(kA4988Commands, A4988Command); + break; + } + } + return result; +} + +#endif // USE_A4988_STEPPER diff --git a/sonoff/xdrv_26_ariluxrf.ino b/sonoff/xdrv_26_ariluxrf.ino new file mode 100644 index 000000000..48eb1b46c --- /dev/null +++ b/sonoff/xdrv_26_ariluxrf.ino @@ -0,0 +1,191 @@ +/* + xdrv_26_ariluxrf.ino - Arilux Rf support for Sonoff-Tasmota + + Copyright (C) 2019 Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef USE_LIGHT +#ifdef USE_ARILUX_RF +/*********************************************************************************************\ + * Arilux LC11 Rf support stripped from RCSwitch library +\*********************************************************************************************/ + +#define XDRV_26 26 + +const uint32_t ARILUX_RF_TIME_AVOID_DUPLICATE = 1000; // Milliseconds + +const uint8_t ARILUX_RF_MAX_CHANGES = 51; // Pulses (sync + 2 x 24 bits) +const uint32_t ARILUX_RF_SEPARATION_LIMIT = 4300; // Microseconds +const uint32_t ARILUX_RF_RECEIVE_TOLERANCE = 60; // Percentage + +struct ARILUX { + unsigned int rf_timings[ARILUX_RF_MAX_CHANGES]; + + unsigned long rf_received_value = 0; + unsigned long rf_last_received_value = 0; + unsigned long rf_last_time = 0; + unsigned long rf_lasttime = 0; + + unsigned int rf_change_count = 0; + unsigned int rf_repeat_count = 0; + + uint8_t rf_toggle = 0; +} Arilux; + +#ifndef ARDUINO_ESP8266_RELEASE_2_3_0 // Fix core 2.5.x ISR not in IRAM Exception +#ifndef USE_WS2812_DMA // Collides with Neopixelbus but solves RF misses +void AriluxRfInterrupt(void) ICACHE_RAM_ATTR; // As iram is tight and it works this way too +#endif // USE_WS2812_DMA +#endif // ARDUINO_ESP8266_RELEASE_2_3_0 + +void AriluxRfInterrupt(void) +{ + unsigned long time = micros(); + unsigned int duration = time - Arilux.rf_lasttime; + + if (duration > ARILUX_RF_SEPARATION_LIMIT) { + if (abs(duration - Arilux.rf_timings[0]) < 200) { + Arilux.rf_repeat_count++; + if (Arilux.rf_repeat_count == 2) { + unsigned long code = 0; + const unsigned int delay = Arilux.rf_timings[0] / 31; + const unsigned int delayTolerance = delay * ARILUX_RF_RECEIVE_TOLERANCE / 100; + for (unsigned int i = 1; i < Arilux.rf_change_count -1; i += 2) { + code <<= 1; + if (abs(Arilux.rf_timings[i] - (delay *3)) < delayTolerance && abs(Arilux.rf_timings[i +1] - delay) < delayTolerance) { + code |= 1; + } + } + if (Arilux.rf_change_count > 49) { // Need 1 sync bit and 24 data bits + Arilux.rf_received_value = code; + } + Arilux.rf_repeat_count = 0; + } + } + Arilux.rf_change_count = 0; + } + if (Arilux.rf_change_count >= ARILUX_RF_MAX_CHANGES) { + Arilux.rf_change_count = 0; + Arilux.rf_repeat_count = 0; + } + Arilux.rf_timings[Arilux.rf_change_count++] = duration; + Arilux.rf_lasttime = time; +} + +void AriluxRfHandler(void) +{ + unsigned long now = millis(); + if (Arilux.rf_received_value && !((Arilux.rf_received_value == Arilux.rf_last_received_value) && (now - Arilux.rf_last_time < ARILUX_RF_TIME_AVOID_DUPLICATE))) { + Arilux.rf_last_received_value = Arilux.rf_received_value; + Arilux.rf_last_time = now; + + uint16_t hostcode = Arilux.rf_received_value >> 8 & 0xFFFF; + if (Settings.rf_code[1][6] == Settings.rf_code[1][7]) { + Settings.rf_code[1][6] = hostcode >> 8 & 0xFF; + Settings.rf_code[1][7] = hostcode & 0xFF; + } + uint16_t stored_hostcode = Settings.rf_code[1][6] << 8 | Settings.rf_code[1][7]; + + DEBUG_DRIVER_LOG(PSTR(D_LOG_RFR D_HOST D_CODE " 0x%04X, " D_RECEIVED " 0x%06X"), stored_hostcode, Arilux.rf_received_value); + + if (hostcode == stored_hostcode) { + char command[33]; + char value = '-'; + command[0] = '\0'; + uint8_t keycode = Arilux.rf_received_value & 0xFF; + switch (keycode) { + case 1: // Power On + case 3: // Power Off + snprintf_P(command, sizeof(command), PSTR(D_CMND_POWER " %d"), (1 == keycode) ? 1 : 0); + break; + case 2: // Toggle + Arilux.rf_toggle++; + Arilux.rf_toggle &= 0x3; + snprintf_P(command, sizeof(command), PSTR(D_CMND_COLOR " %d"), 200 + Arilux.rf_toggle); + break; + case 4: // Speed + + value = '+'; + case 7: // Speed - + snprintf_P(command, sizeof(command), PSTR(D_CMND_SPEED " %c"), value); + break; + case 5: // Scheme + + value = '+'; + case 8: // Scheme - + snprintf_P(command, sizeof(command), PSTR(D_CMND_SCHEME " %c"), value); + break; + case 6: // Dimmer + + value = '+'; + case 9: // Dimmer - + snprintf_P(command, sizeof(command), PSTR(D_CMND_DIMMER " %c"), value); + break; + default: { + if ((keycode >= 10) && (keycode <= 21)) { + snprintf_P(command, sizeof(command), PSTR(D_CMND_COLOR " %d"), keycode -9); + } + } + } + if (strlen(command)) { + ExecuteCommand(command, SRC_LIGHT); + } + } + } + Arilux.rf_received_value = 0; +} + +void AriluxRfInit(void) +{ + if ((pin[GPIO_ARIRFRCV] < 99) && (pin[GPIO_ARIRFSEL] < 99)) { + if (Settings.last_module != Settings.module) { + Settings.rf_code[1][6] = 0; + Settings.rf_code[1][7] = 0; + Settings.last_module = Settings.module; + } + Arilux.rf_received_value = 0; + + digitalWrite(pin[GPIO_ARIRFSEL], 0); // Turn on RF + attachInterrupt(pin[GPIO_ARIRFRCV], AriluxRfInterrupt, CHANGE); + } +} + +void AriluxRfDisable(void) +{ + if ((pin[GPIO_ARIRFRCV] < 99) && (pin[GPIO_ARIRFSEL] < 99)) { + detachInterrupt(pin[GPIO_ARIRFRCV]); + digitalWrite(pin[GPIO_ARIRFSEL], 1); // Turn off RF + } +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xdrv26(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_EVERY_50_MSECOND: + if (pin[GPIO_ARIRFRCV] < 99) { AriluxRfHandler(); } + break; + case FUNC_EVERY_SECOND: + if (10 == uptime) { AriluxRfInit(); } // Needs rest before enabling RF interrupts + break; + } + return result; +} + +#endif // USE_ARILUX_RF +#endif // USE_LIGHT \ No newline at end of file diff --git a/sonoff/xdrv_27_shutter.ino b/sonoff/xdrv_27_shutter.ino new file mode 100644 index 000000000..7c0f989d1 --- /dev/null +++ b/sonoff/xdrv_27_shutter.ino @@ -0,0 +1,662 @@ +/* + xdrv_27_shutter.ino - Shutter/Blind support for Sonoff-Tasmota + + Copyright (C) 2019 Stefan Bode + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef USE_SHUTTER +/*********************************************************************************************\ + * Shutter or Blind support using two consecutive relays +\*********************************************************************************************/ + +#define XDRV_27 27 + +#define D_PRFX_SHUTTER "Shutter" +#define D_CMND_SHUTTER_OPEN "Open" +#define D_CMND_SHUTTER_CLOSE "Close" +#define D_CMND_SHUTTER_STOP "Stop" +#define D_CMND_SHUTTER_POSITION "Position" +#define D_CMND_SHUTTER_OPENTIME "OpenDuration" +#define D_CMND_SHUTTER_CLOSETIME "CloseDuration" +#define D_CMND_SHUTTER_RELAY "Relay" +#define D_CMND_SHUTTER_SETHALFWAY "SetHalfway" +#define D_CMND_SHUTTER_SETCLOSE "SetClose" +#define D_CMND_SHUTTER_INVERT "Invert" +#define D_CMND_SHUTTER_CLIBRATION "Calibration" + +#define D_SHUTTER "SHUTTER" + +const uint16_t MOTOR_STOP_TIME = 500; // in mS + +uint8_t calibrate_pos[6] = {0,30,50,70,90,100}; +uint16_t messwerte[5] = {30,50,70,90,100}; + +enum ShutterModes { SHT_OFF_OPEN__OFF_CLOSE, SHT_OFF_ON__OPEN_CLOSE, SHT_PULSE_OPEN__PULSE_CLOSE }; + +const char kShutterCommands[] PROGMEM = D_PRFX_SHUTTER "|" + D_CMND_SHUTTER_OPEN "|" D_CMND_SHUTTER_CLOSE "|" D_CMND_SHUTTER_STOP "|" D_CMND_SHUTTER_POSITION "|" + D_CMND_SHUTTER_OPENTIME "|" D_CMND_SHUTTER_CLOSETIME "|" D_CMND_SHUTTER_RELAY "|" + D_CMND_SHUTTER_SETHALFWAY "|" D_CMND_SHUTTER_SETCLOSE "|" D_CMND_SHUTTER_INVERT "|" D_CMND_SHUTTER_CLIBRATION; + +void (* const ShutterCommand[])(void) PROGMEM = { + &CmndShutterOpen, &CmndShutterClose, &CmndShutterStop, &CmndShutterPosition, + &CmndShutterOpenTime, &CmndShutterCloseTime, &CmndShutterRelay, + &CmndShutterSetHalfway, &CmndShutterSetClose, &CmndShutterInvert, &CmndShutterCalibration }; + +const char JSON_SHUTTER_POS[] PROGMEM = "\"" D_PRFX_SHUTTER "%d\":{\"Position\":%d,\"direction\":%d}"; + +#include + +Ticker TickerShutter; + +struct SHUTTER { + power_t mask = 0; // bit mask with 11 at the position of relays that belong to at least ONE shutter + power_t old_power = 0; // preserve old bitmask for power to extract the relay that changes. + power_t switched_relay = 0; // bitmatrix that contain the relays that was lastly changed. + uint32_t time[MAX_SHUTTERS]; + int32_t open_max[MAX_SHUTTERS]; // max value on maximum open calculated + int32_t target_position[MAX_SHUTTERS]; // position to go to + int32_t start_position[MAX_SHUTTERS]; + int32_t real_position[MAX_SHUTTERS]; // value between 0 and Shutter.open_max + uint16_t open_time[MAX_SHUTTERS]; // duration to open the shutter + uint16_t close_time[MAX_SHUTTERS]; // duration to close the shutter + uint16_t close_velocity[MAX_SHUTTERS]; // in relation to open velocity. higher value = faster + uint16_t operations[MAX_SHUTTERS]; + int8_t direction[MAX_SHUTTERS]; // 1 == UP , 0 == stop; -1 == down + uint8_t mode = 0; // operation mode definition. see enum type above SHT_OFF_OPEN__OFF_CLOSE, SHT_OFF_ON__OPEN_CLOSE, SHT_PULSE_OPEN__PULSE_CLOSE +} Shutter; + +void ShutterRtc50mS(void) +{ + for (uint32_t i = 0; i < MAX_SHUTTERS; i++) { + Shutter.time[i]++; + } +} + +int32_t ShutterPercentToRealPosition(uint8_t percent,uint8_t index) +{ + if (Settings.shutter_set50percent[index] != 50) { + return percent <= 5 ? Settings.shuttercoeff[2][index] * percent : Settings.shuttercoeff[1][index] * percent + Settings.shuttercoeff[0][index]; + } else { + uint32_t realpos; + // check against DIV 0 + for (uint8_t j=0 ; j < 5 ; j++) { + if (Settings.shuttercoeff[j][index] == 0) { + AddLog_P2(LOG_LEVEL_ERROR, PSTR("SHT: RESET/INIT CALIBRATION MATRIX DIV 0")); + for (uint8_t k=0 ; k < 5 ; k++) { + Settings.shuttercoeff[k][index] = messwerte[k] * 1000 / messwerte[4]; + } + } + } + for (uint8_t i=0 ; i < 5 ; i++) { + if (percent*10 > Settings.shuttercoeff[i][index]) { + realpos = Shutter.open_max[index] * calibrate_pos[i+1] / 100; + //AddLog_P2(LOG_LEVEL_INFO, PSTR("Realposition TEMP1: %d, %% %d, coeff %d"), realpos, percent, Settings.shuttercoeff[i][index]); + } else { + if ( i == 0) { + realpos = percent * Shutter.open_max[index] * calibrate_pos[i+1] / Settings.shuttercoeff[i][index] / 10; + } else { + //uint16_t addon = ( percent*10 - Settings.shuttercoeff[i-1][index] ) * Shutter_Open_Max[index] * (calibrate_pos[i+1] - calibrate_pos[i]) / (Settings.shuttercoeff[i][index] -Settings.shuttercoeff[i-1][index]) / 100; + //AddLog_P2(LOG_LEVEL_INFO, PSTR("Realposition TEMP2: %d, %% %d, coeff %d"), addon, (calibrate_pos[i+1] - calibrate_pos[i]), (Settings.shuttercoeff[i][index] -Settings.shuttercoeff[i-1][index])); + realpos += ( percent*10 - Settings.shuttercoeff[i-1][index] ) * Shutter.open_max[index] * (calibrate_pos[i+1] - calibrate_pos[i]) / (Settings.shuttercoeff[i][index] -Settings.shuttercoeff[i-1][index]) / 100; + } + break; + } + } + return realpos; + } +} + +uint8_t ShutterRealToPercentPosition(int32_t realpos, uint8_t index) +{ + if (Settings.shutter_set50percent[index] != 50) { + return Settings.shuttercoeff[2][index] * 5 > realpos ? realpos / Settings.shuttercoeff[2][index] : (realpos-Settings.shuttercoeff[0][index]) / Settings.shuttercoeff[1][index]; + } else { + uint16_t realpercent; + + for (uint8_t i=0 ; i < 5 ; i++) { + if (realpos > Shutter.open_max[index] * calibrate_pos[i+1] / 100) { + realpercent = Settings.shuttercoeff[i][index] /10; + //AddLog_P2(LOG_LEVEL_INFO, PSTR("Realpercent TEMP1: %d, %% %d, coeff %d"), realpercent, realpos, Shutter_Open_Max[index] * calibrate_pos[i+1] / 100); + } else { + if ( i == 0) { + realpercent = ( realpos - (Shutter.open_max[index] * calibrate_pos[i] / 100) ) * 10 * Settings.shuttercoeff[i][index] / calibrate_pos[i+1] / Shutter.open_max[index]; + } else { + //uint16_t addon = ( realpos - (Shutter_Open_Max[index] * calibrate_pos[i] / 100) ) * 10 * (Settings.shuttercoeff[i][index] - Settings.shuttercoeff[i-1][index]) / (calibrate_pos[i+1] - calibrate_pos[i])/ Shutter_Open_Max[index]; + //uint16_t addon = ( percent*10 - Settings.shuttercoeff[i-1][index] ) * Shutter_Open_Max[index] * (calibrate_pos[i+1] - calibrate_pos[i]) / (Settings.shuttercoeff[i][index] -Settings.shuttercoeff[i-1][index]) / 100; + //AddLog_P2(LOG_LEVEL_INFO, PSTR("Realpercent TEMP2: %d, delta %d, %% %d, coeff %d"), addon,( realpos - (Shutter_Open_Max[index] * calibrate_pos[i] / 100) ) , (calibrate_pos[i+1] - calibrate_pos[i])* Shutter_Open_Max[index]/100, (Settings.shuttercoeff[i][index] -Settings.shuttercoeff[i-1][index])); + realpercent += ( realpos - (Shutter.open_max[index] * calibrate_pos[i] / 100) ) * 10 * (Settings.shuttercoeff[i][index] - Settings.shuttercoeff[i-1][index]) / (calibrate_pos[i+1] - calibrate_pos[i]) / Shutter.open_max[index] ; + } + break; + } + } + return realpercent; + } +} + +void ShutterInit(void) +{ + shutters_present = 0; + Shutter.mask = 0; + //Initialize to get relay that changed + Shutter.old_power = power; + bool relay_in_interlock = false; + + AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Accuracy digits: %d"), Settings.shutter_accuracy); + + for (uint32_t i = 0; i < MAX_SHUTTERS; i++) { + // upgrade to 0.1sec calculation base. + if ( Settings.shutter_accuracy == 0) { + Settings.shutter_closetime[i] = Settings.shutter_closetime[i] * 10; + Settings.shutter_opentime[i] = Settings.shutter_opentime[i] * 10; + } + // set startrelay to 1 on first init, but only to shutter 1. 90% usecase + Settings.shutter_startrelay[i] = (Settings.shutter_startrelay[i] == 0 && i == 0? 1 : Settings.shutter_startrelay[i]); + if (Settings.shutter_startrelay[i] && Settings.shutter_startrelay[i] <9) { + shutters_present++; + + // Determine shutter types + Shutter.mask |= 3 << (Settings.shutter_startrelay[i] -1) ; + + for (uint32_t j = 0; j < MAX_INTERLOCKS * Settings.flag.interlock; j++) { + //AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Interlock state i=%d %d, flag %d, , shuttermask %d, maskedIL %d"),i, Settings.interlock[i], Settings.flag.interlock,Shutter.mask, Settings.interlock[i]&Shutter.mask); + if (Settings.interlock[j] && Settings.interlock[j] & Shutter.mask) { + //AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Relay in Interlock group")); + relay_in_interlock = true; + } + } + if (relay_in_interlock) { + if (Settings.pulse_timer[i] > 0) { + Shutter.mode = SHT_PULSE_OPEN__PULSE_CLOSE; + } else { + Shutter.mode = SHT_OFF_OPEN__OFF_CLOSE; + } + } else { + Shutter.mode = SHT_OFF_ON__OPEN_CLOSE; + } + + TickerShutter.attach_ms(50, ShutterRtc50mS ); + // default the 50 percent should not have any impact without changing it. set to 60 + Settings.shutter_set50percent[i] = Settings.shutter_set50percent[i] > 0 ? Settings.shutter_set50percent[i] : 50; + + // use 10 sec. as default to allow everybody to play without deep initialize + Shutter.open_time[i] = Settings.shutter_opentime[i] > 0 ? Settings.shutter_opentime[i] : 100; + Shutter.close_time[i] = Settings.shutter_closetime[i] > 0 ? Settings.shutter_closetime[i] : 100; + + // Update Calculation 20 because time interval is 0.05 sec + Shutter.open_max[i] = 200 * Shutter.open_time[i]; + Shutter.close_velocity[i] = Shutter.open_max[i] / Shutter.close_time[i] / 2 ; + + // calculate a ramp slope at the first 5 percent to compensate that shutters move with down part later than the upper part + if (Settings.shutter_set50percent[i] != 50) { + Settings.shuttercoeff[1][i] = Shutter.open_max[i] * (100 - Settings.shutter_set50percent[i] ) / 5000; + Settings.shuttercoeff[0][i] = Shutter.open_max[i] - (Settings.shuttercoeff[1][i] * 100); + Settings.shuttercoeff[2][i] = (Settings.shuttercoeff[0][i] + 5 * Settings.shuttercoeff[1][i]) / 5; + } + Shutter.mask |= 3 << (Settings.shutter_startrelay[i] -1) ; + + Shutter.real_position[i] = ShutterPercentToRealPosition(Settings.shutter_position[i], i); + //Shutter.real_position[i] = Settings.shutter_position[i] <= 5 ? Settings.shuttercoeff[2][i] * Settings.shutter_position[i] : Settings.shuttercoeff[1][i] * Settings.shutter_position[i] + Settings.shuttercoeff[0,i]; + Shutter.start_position[i] = Shutter.real_position[i]; + + char shutter_open_chr[10]; + dtostrfd((float)Shutter.open_time[i] / 10 , 1, shutter_open_chr); + char shutter_close_chr[10]; + dtostrfd((float)Shutter.close_time[i] / 10, 1, shutter_close_chr); + AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Shutter %d (Relay:%d): Init. Pos: %d [%d %%], Open Vel.: 100 Close Vel.: %d , Max Way: %d, Opentime %s [s], Closetime %s [s], CoedffCalc: c0: %d, c1 %d, c2: %d, c3: %d, c4: %d, binmask %d, is inverted %d, shuttermode %d"), + i, Settings.shutter_startrelay[i], Shutter.real_position[i], Settings.shutter_position[i], Shutter.close_velocity[i], Shutter.open_max[i], shutter_open_chr, shutter_close_chr, + Settings.shuttercoeff[0][i], Settings.shuttercoeff[1][i], Settings.shuttercoeff[2][i], Settings.shuttercoeff[3][i], Settings.shuttercoeff[4][i], + Shutter.mask, Settings.shutter_invert[i], Shutter.mode); + + } else { + // terminate loop at first INVALID shutter. + break; + } + Settings.shutter_accuracy = 1; + } +} + +void ShutterUpdatePosition(void) +{ + char scommand[CMDSZ]; + char stopic[TOPSZ]; + + for (uint32_t i = 0; i < shutters_present; i++) { + if (Shutter.direction[i] != 0) { + //char stemp1[20]; + Shutter.real_position[i] = Shutter.start_position[i] + ( Shutter.time[i] * (Shutter.direction[i] > 0 ? 100 : -Shutter.close_velocity[i])); + // avoid real position leaving the boundaries. + Shutter.real_position[i] = Shutter.real_position[i] < 0 ? 0 : (Shutter.real_position[i] > Shutter.open_max[i] ? Shutter.open_max[i] : Shutter.real_position[i]) ; + + // Add additional runtime, if shutter did not reach the endstop for some time. + if (Shutter.target_position[i] == Shutter.real_position[i] && Shutter.target_position[i] == 0) { + // for every operation add 5x50ms = 250ms to stop position + //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Adding additional runtime")); + Shutter.real_position[i] += 500 * Shutter.operations[i] ; + Shutter.operations[i] = 0; + } + if (Shutter.real_position[i] * Shutter.direction[i] >= Shutter.target_position[i] * Shutter.direction[i] ) { + // calculate relay number responsible for current movement. + //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Stop Condition detected: real: %d, Target: %d, direction: %d"),Shutter.real_position[i], Shutter.target_position[i],Shutter.direction[i]); + uint8_t cur_relay = Settings.shutter_startrelay[i] + (Shutter.direction[i] == 1 ? 0 : 1) ; + char stemp2[10]; + + Settings.shutter_position[i] = ShutterRealToPercentPosition(Shutter.real_position[i], i); + //Settings.shutter_position[i] = Settings.shuttercoeff[2][i] * 5 > Shutter.real_position[i] ? (Shutter.real_position[i] * 10 / Settings.shuttercoeff[2][i] + 4)/10 : ((Shutter.real_position[i]-Settings.shuttercoeff[0,i]) *10 / Settings.shuttercoeff[1][i] +4) / 10; + + if (0 < Settings.shutter_position[i] && Settings.shutter_position[i] < 100) { + Shutter.operations[i]++; + } else { + Shutter.operations[i] = 0; + } + + dtostrfd((float)Shutter.time[i] / 20, 1, stemp2); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Shutter %d: Real Pos. %d, Stoppos: %ld, relay: %d, direction %d, pulsetimer: %d, rtcshutter: %s [s], operationtime %d"), i, Shutter.real_position[i], Settings.shutter_position[i], cur_relay -1, Shutter.direction[i], Settings.pulse_timer[cur_relay -1], stemp2, Shutter.operations[i]); + Shutter.start_position[i] = Shutter.real_position[i]; + + // sending MQTT result to broker + snprintf_P(scommand, sizeof(scommand),PSTR(D_SHUTTER "%d"), i+1); + GetTopic_P(stopic, STAT, mqtt_topic, scommand); + Response_P("%d", Settings.shutter_invert[i] ? 100 - Settings.shutter_position[i]: Settings.shutter_position[i]); + MqttPublish(stopic, Settings.flag.mqtt_power_retain); + + switch (Shutter.mode) { + case SHT_PULSE_OPEN__PULSE_CLOSE: + // we have a momentary switch here. Needs additional pulse on same relay after the end + if (SRC_PULSETIMER == last_source || SRC_SHUTTER == last_source || SRC_WEBGUI == last_source) { + ExecuteCommandPower(cur_relay, 1, SRC_SHUTTER); + } else { + last_source = SRC_SHUTTER; + } + break; + case SHT_OFF_ON__OPEN_CLOSE: + // This is a failsafe configuration. Relay1 ON/OFF Relay2 -1/1 direction + if ((1 << (Settings.shutter_startrelay[i]-1)) & power) { + ExecuteCommandPower(Settings.shutter_startrelay[i], 0, SRC_SHUTTER); + } + break; + case SHT_OFF_OPEN__OFF_CLOSE: + // avoid switching OFF a relay already OFF + if ((1 << (cur_relay-1)) & power) { + // Relay is on and need to be switched off. + ExecuteCommandPower(cur_relay, 0, SRC_SHUTTER); + } + break; + } + Shutter.direction[i] = 0; + uint8_t position = Settings.shutter_invert[i] ? 100 - Settings.shutter_position[i]: Settings.shutter_position[i]; + Response_P(PSTR("{")); + ResponseAppend_P(JSON_SHUTTER_POS, i+1, position, 0 /*Shutter.direction[i]*/); + ResponseJsonEnd(); + MqttPublishPrefixTopic_P(RESULT_OR_TELE, mqtt_data); + XdrvRulesProcess(); + } + } + } +} + +bool ShutterState(uint8_t device) +{ + device--; + device &= 3; + return (Settings.flag3.shutter_mode && (Shutter.mask & (1 << (Settings.shutter_startrelay[device]-1))) ); +} + +void ShutterStartInit(uint8_t index, uint8_t direction, int32_t target_pos) +{ + Shutter.direction[index] = direction; + Shutter.target_position[index] = target_pos; + Shutter.start_position[index] = Shutter.real_position[index]; + Shutter.time[index] = 0; + //AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Start shutter: %d from %d to %d in directin %d"), index, Shutter.start_position[index], Shutter.target_position[index], Shutter.direction[index]); +} + +void ShutterDelayForMotorStop(void) +{ + AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Wait for Motorstop %d"), MOTOR_STOP_TIME); + delay(MOTOR_STOP_TIME); +} + +void ShutterReportPosition(void) +{ + uint16_t shutter_moving = 0; + for (uint32_t i = 0; i < shutters_present; i++) { + if (Shutter.direction[i] != 0) { + char stemp1[20]; + char stemp2[10]; + dtostrfd((float)Shutter.time[i] / 20, 1, stemp2); + shutter_moving = 1; + //Settings.shutter_position[i] = Settings.shuttercoeff[2][i] * 5 > Shutter.real_position[i] ? Shutter.real_position[i] / Settings.shuttercoeff[2][i] : (Shutter.real_position[i]-Settings.shuttercoeff[0,i]) / Settings.shuttercoeff[1][i]; + AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Shutter %d: Real Pos: %d, Target %d, source: %s, start-pos: %d %%, direction: %d, rtcshutter: %s [s]"), i,Shutter.real_position[i], Shutter.target_position[i], GetTextIndexed(stemp1, sizeof(stemp1), last_source, kCommandSource), Settings.shutter_position[i], Shutter.direction[i], stemp2 ); + } + } + if (rules_flag.shutter_moving > shutter_moving) { + rules_flag.shutter_moved = 1; + } else { + rules_flag.shutter_moved = 0; + } + rules_flag.shutter_moving = shutter_moving; + //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: rules_flag.shutter_moving: %d, moved %d"), rules_flag.shutter_moving, rules_flag.shutter_moved); +} + +void ShutterRelayChanged(void) +{ + + // Shutter.switched_relay = binary relay that was recently changed and cause an Action + // powerstate_local = binary powermatrix and relays from shutter: 0..3 + // relays_changed = bool if one of the relays that belong to the shutter changed not by shutter or pulsetimer + char stemp1[10]; + + for (uint32_t i = 0; i < shutters_present; i++) { + power_t powerstate_local = (power >> (Settings.shutter_startrelay[i] -1)) & 3; + //uint8 manual_relays_changed = ((Shutter.switched_relay >> (Settings.shutter_startrelay[i] -1)) & 3) && SRC_IGNORE != last_source && SRC_SHUTTER != last_source && SRC_PULSETIMER != last_source ; + uint8 manual_relays_changed = ((Shutter.switched_relay >> (Settings.shutter_startrelay[i] -1)) & 3) && SRC_SHUTTER != last_source && SRC_PULSETIMER != last_source ; + if (manual_relays_changed) { + if (Shutter.mode == SHT_OFF_ON__OPEN_CLOSE) { + switch (powerstate_local) { + case 1: + ShutterDelayForMotorStop(); + ShutterStartInit(i, 1, Shutter.open_max[i]); + break; + case 3: + ShutterDelayForMotorStop(); + ShutterStartInit(i, -1, 0); + break; + default: + Shutter.direction[i] = 0; + Shutter.target_position[i] = Shutter.real_position[i]; + } + } else { + if (Shutter.direction[i] != 0 && (!powerstate_local || (powerstate_local && Shutter.mode == SHT_PULSE_OPEN__PULSE_CLOSE))) { + Shutter.target_position[i] = Shutter.real_position[i]; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Shutter %d: Switch OFF motor. Target: %ld, source: %s, powerstate_local %ld, Shutter.switched_relay %d, manual change %d"), i, Shutter.target_position[i], GetTextIndexed(stemp1, sizeof(stemp1), last_source, kCommandSource), powerstate_local,Shutter.switched_relay,manual_relays_changed); + } else { + last_source = SRC_SHUTTER; // avoid switch off in the next loop + if (powerstate_local == 2) { // testing on CLOSE relay, if ON + // close with relay two + ShutterDelayForMotorStop(); + ShutterStartInit(i, -1, 0); + } else { + // opens with relay one + ShutterDelayForMotorStop(); + ShutterStartInit(i, 1, Shutter.open_max[i]); + } + } + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Shutter %d: Target: %ld, powerstatelocal %d"), i, Shutter.target_position[i], powerstate_local); + } + } + } +} + +/////////////////////////////////////////////////////////////////////////////////// +// Shutter specific functions +// TODO: move to shutter driver and make them accessible in a generic way + +// device: 1.. +// position: 0-100 +void ShutterSetPosition(uint8_t device, uint8_t position) +{ + char svalue[32]; // Command and number parameter + snprintf_P(svalue, sizeof(svalue), PSTR(D_PRFX_SHUTTER D_CMND_SHUTTER_POSITION "%d %d"), device, position); + ExecuteCommand(svalue, SRC_IGNORE); +} + +/*********************************************************************************************\ + * Commands +\*********************************************************************************************/ + +void CmndShutterOpen(void) +{ + XdrvMailbox.payload = 100; + last_source = SRC_WEBGUI; + CmndShutterPosition(); +} + +void CmndShutterClose(void) +{ + XdrvMailbox.payload = 0; + last_source = SRC_WEBGUI; + CmndShutterPosition(); +} + +void CmndShutterStop(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { + uint32_t index = XdrvMailbox.index -1; + if (Shutter.direction[index] != 0) { + + //AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Stop moving shutter %d: direction: %d"), XdrvMailbox.index, Shutter.direction[index]); + + int32_t temp_realpos = Shutter.start_position[index] + ( (Shutter.time[index]+10) * (Shutter.direction[index] > 0 ? 100 : -Shutter.close_velocity[index])); + XdrvMailbox.payload = ShutterRealToPercentPosition(temp_realpos, index); + //XdrvMailbox.payload = Settings.shuttercoeff[2][index] * 5 > temp_realpos ? temp_realpos / Settings.shuttercoeff[2][index] : (temp_realpos-Settings.shuttercoeff[0,index]) / Settings.shuttercoeff[1][index]; + last_source = SRC_WEBGUI; + CmndShutterPosition(); + } else { + ResponseCmndDone(); + } + } +} + +void CmndShutterPosition(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { + uint32_t index = XdrvMailbox.index -1; + //limit the payload + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Position in: payload %d, index %d, source %d"), XdrvMailbox.payload , XdrvMailbox.index, last_source ); + + int8_t target_pos_percent = XdrvMailbox.payload < 0 ? 0 : (XdrvMailbox.payload > 100 ? 100 : XdrvMailbox.payload); + // webgui still send also on inverted shutter the native position. + target_pos_percent = Settings.shutter_invert[index] && SRC_WEBGUI != last_source ? 100 - target_pos_percent : target_pos_percent; + if (target_pos_percent != -99) { + //target_pos_percent = Settings.shutter_invert[index] ? 100 - target_pos_percent : target_pos_percent; + Shutter.target_position[index] = ShutterPercentToRealPosition(target_pos_percent, index); + //Shutter.target_position[index] = XdrvMailbox.payload < 5 ? Settings.shuttercoeff[2][index] * XdrvMailbox.payload : Settings.shuttercoeff[1][index] * XdrvMailbox.payload + Settings.shuttercoeff[0,index]; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: lastsource %d:, realpos %d, target %d, payload %d"), last_source, Shutter.real_position[index] ,Shutter.target_position[index],target_pos_percent); + } + if ( (target_pos_percent >= 0) && (target_pos_percent <= 100) && abs(Shutter.target_position[index] - Shutter.real_position[index] ) / Shutter.close_velocity[index] > 2) { + int8_t new_shutterdirection = Shutter.real_position[index] < Shutter.target_position[index] ? 1 : -1; + if (Shutter.direction[index] == -new_shutterdirection ) { + // direction need to be changed. on momentary switches first stop the Shutter + if (Shutter.mode == SHT_PULSE_OPEN__PULSE_CLOSE) { + // code for momentary shutters only small switch on to stop Shutter + ExecuteCommandPower(Settings.shutter_startrelay[index] + (new_shutterdirection == 1 ? 0 : 1), 1, SRC_SHUTTER); + delay(100); + } else { + ExecuteCommandPower(Settings.shutter_startrelay[index] + (new_shutterdirection == 1 ? 1 : 0), 0, SRC_SHUTTER); + ShutterDelayForMotorStop(); + } + } + if (Shutter.direction[index] != new_shutterdirection ) { + ShutterStartInit(index, new_shutterdirection, Shutter.target_position[index]); + Shutter.operations[index]++; + if (Shutter.mode == SHT_OFF_ON__OPEN_CLOSE) { + ExecuteCommandPower(Settings.shutter_startrelay[index], 0, SRC_SHUTTER); + //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Delay5 5s, xdrv %d"), XdrvMailbox.payload); + ShutterDelayForMotorStop(); + // Code for shutters with circuit safe configuration, switch the direction Relay + ExecuteCommandPower(Settings.shutter_startrelay[index] +1, new_shutterdirection == 1 ? 0 : 1, SRC_SHUTTER); + // power on + ExecuteCommandPower(Settings.shutter_startrelay[index], 1, SRC_SHUTTER); + } else { + // now start the motor for the right direction, work for momentary and normal shutters. + AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Start shutter in direction %d"), Shutter.direction[index]); + ExecuteCommandPower(Settings.shutter_startrelay[index] + (new_shutterdirection == 1 ? 0 : 1), 1, SRC_SHUTTER); + //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Delay6 5s, xdrv %d"), XdrvMailbox.payload); + } + Shutter.switched_relay = 0; + } + } else { + target_pos_percent = ShutterRealToPercentPosition(Shutter.real_position[index], index); + } + XdrvMailbox.index = index +1; // Fix random index for ShutterClose + ResponseCmndIdxNumber(Settings.shutter_invert[index] ? 100 - target_pos_percent : target_pos_percent); + } +} + +void CmndShutterOpenTime(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { + if (XdrvMailbox.data_len > 0) { + Settings.shutter_opentime[XdrvMailbox.index -1] = (uint16_t)(10 * CharToFloat(XdrvMailbox.data)); + ShutterInit(); + } + char time_chr[10]; + dtostrfd((float)(Settings.shutter_opentime[XdrvMailbox.index -1]) / 10, 1, time_chr); + ResponseCmndIdxChar(time_chr); + } +} + +void CmndShutterCloseTime(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { + if (XdrvMailbox.data_len > 0) { + Settings.shutter_closetime[XdrvMailbox.index -1] = (uint16_t)(10 * CharToFloat(XdrvMailbox.data)); + ShutterInit(); + } + char time_chr[10]; + dtostrfd((float)(Settings.shutter_closetime[XdrvMailbox.index -1]) / 10, 1, time_chr); + ResponseCmndIdxChar(time_chr); + } +} + +void CmndShutterRelay(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_SHUTTERS)) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 64)) { + Settings.shutter_startrelay[XdrvMailbox.index -1] = XdrvMailbox.payload; + if (XdrvMailbox.payload > 0) { + Shutter.mask |= 3 << (XdrvMailbox.payload - 1); + } else { + Shutter.mask ^= 3 << (Settings.shutter_startrelay[XdrvMailbox.index -1] - 1); + } + AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Relay %d is %d"), XdrvMailbox.index, XdrvMailbox.payload); + Settings.shutter_startrelay[XdrvMailbox.index -1] = XdrvMailbox.payload; + ShutterInit(); + // if payload is 0 to disable the relay there must be a reboot. Otherwhise does not work + } + ResponseCmndIdxNumber(Settings.shutter_startrelay[XdrvMailbox.index -1]); + } +} + +void CmndShutterSetHalfway(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) { + Settings.shutter_set50percent[XdrvMailbox.index -1] = Settings.shutter_invert[XdrvMailbox.index -1] ? 100 - XdrvMailbox.payload : XdrvMailbox.payload; + ShutterInit(); + ResponseCmndIdxNumber(XdrvMailbox.payload); // ???? + } else { + ResponseCmndIdxNumber(Settings.shutter_set50percent[XdrvMailbox.index -1]); + } + } +} + +void CmndShutterSetClose(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { + Shutter.real_position[XdrvMailbox.index -1] = 0; + ShutterStartInit(XdrvMailbox.index -1, 0, 0); + Settings.shutter_position[XdrvMailbox.index -1] = 0; + ResponseCmndChar(D_CONFIGURATION_RESET); + } +} + +void CmndShutterInvert(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) { + Settings.shutter_invert[XdrvMailbox.index -1] = XdrvMailbox.payload; + } + ResponseCmndIdxNumber(Settings.shutter_invert[XdrvMailbox.index -1]); + } +} + +void CmndShutterCalibration(void) // ???? +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_SHUTTERS)) { + if (XdrvMailbox.data_len > 0) { + uint32_t i = 0; + char *str_ptr; + char* version_dup = strdup(XdrvMailbox.data); // Duplicate the version_str as strtok_r will modify it. + // Loop through the version string, splitting on '.' seperators. + for (char *str = strtok_r(version_dup, " ", &str_ptr); str && i < 5; str = strtok_r(nullptr, " ", &str_ptr), i++) { + int field = atoi(str); + // The fields in a version string can only range from 0-255. + if ((field < 0) || (field > 255)) { + free(version_dup); + break; + } + messwerte[i] = field; + } + for (i=0 ; i < 5 ; i++) { + Settings.shuttercoeff[i][XdrvMailbox.index-1] = messwerte[i] * 1000 / messwerte[4]; + AddLog_P2(LOG_LEVEL_INFO, PSTR("Settings.shuttercoeff í: %d, i: %d, value: %d, messwert %d"), i,XdrvMailbox.index-1,Settings.shuttercoeff[i][XdrvMailbox.index-1], messwerte[i]); + } + ShutterInit(); + ResponseCmndIdxChar(XdrvMailbox.data); + } + } +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xdrv27(uint8_t function) +{ + bool result = false; + + if (Settings.flag3.shutter_mode) { + switch (function) { + case FUNC_PRE_INIT: + ShutterInit(); + break; + case FUNC_EVERY_50_MSECOND: + ShutterUpdatePosition(); + break; + case FUNC_EVERY_SECOND: + ShutterReportPosition(); + break; + case FUNC_COMMAND: + result = DecodeCommand(kShutterCommands, ShutterCommand); + break; + case FUNC_JSON_APPEND: + for (uint32_t i = 0; i < shutters_present; i++) { + uint8_t position = Settings.shutter_invert[i] ? 100 - Settings.shutter_position[i]: Settings.shutter_position[i]; + ResponseAppend_P(","); + ResponseAppend_P(JSON_SHUTTER_POS, i+1, position, Shutter.direction[i]); +#ifdef USE_DOMOTICZ + if ((0 == tele_period) && (0 == i)) { + DomoticzSensor(DZ_SHUTTER, position); + } +#endif // USE_DOMOTICZ + } + break; + case FUNC_SET_POWER: + char stemp1[10]; + // extract the number of the relay that was switched and save for later in Update Position. + Shutter.switched_relay = power ^ Shutter.old_power; + Shutter.old_power = power; + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Switched relay: %d by %s"), Shutter.switched_relay,GetTextIndexed(stemp1, sizeof(stemp1), last_source, kCommandSource)); + ShutterRelayChanged(); + break; + } + } + return result; +} + +#endif //USE_SHUTTER diff --git a/sonoff/xdrv_28_pcf8574.ino b/sonoff/xdrv_28_pcf8574.ino new file mode 100644 index 000000000..ab63ff79b --- /dev/null +++ b/sonoff/xdrv_28_pcf8574.ino @@ -0,0 +1,249 @@ +/* + xdrv_28_pcf8574.ino - PCF8574 I2C support for Sonoff-Tasmota + + Copyright (C) 2019 Stefan Bode + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef USE_I2C +#ifdef USE_PCF8574 +/*********************************************************************************************\ + * PCF8574 - I2C IO Expander + * + * I2C Address: PCF8574 = 0x20 .. 0x27, PCF8574A = 0x38 .. 0x3F +\*********************************************************************************************/ + +#define XDRV_28 28 + +#define PCF8574_ADDR1 0x20 // PCF8574 +#define PCF8574_ADDR2 0x38 // PCF8574A + +struct PCF8574 { + int error; + uint8_t pin[64]; + uint8_t address[MAX_PCF8574]; + uint8_t pin_mask[MAX_PCF8574] = { 0 }; + uint8_t max_connected_ports = 0; // Max numbers of devices comming from PCF8574 modules + uint8_t max_devices = 0; // Max numbers of PCF8574 modules + char stype[9]; + bool type = true; +} Pcf8574; + +void Pcf8574SwitchRelay(void) +{ + for (uint32_t i = 0; i < devices_present; i++) { + uint8_t relay_state = bitRead(XdrvMailbox.index, i); + + //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PCF: Pcf8574.max_devices %d requested pin %d"), Pcf8574.max_devices,Pcf8574.pin[i]); + + if (Pcf8574.max_devices > 0 && Pcf8574.pin[i] < 99) { + uint8_t board = Pcf8574.pin[i]>>3; + uint8_t oldpinmask = Pcf8574.pin_mask[board]; + uint8_t _val = bitRead(rel_inverted, i) ? !relay_state : relay_state; + + //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PCF: Pcf8574SwitchRelay %d on pin %d"), i,state); + + if (_val) { + Pcf8574.pin_mask[board] |= _val << (Pcf8574.pin[i]&0x7); + } else { + Pcf8574.pin_mask[board] &= ~(1 << (Pcf8574.pin[i]&0x7)); + } + if (oldpinmask != Pcf8574.pin_mask[board]) { + Wire.beginTransmission(Pcf8574.address[board]); + Wire.write(Pcf8574.pin_mask[board]); + Pcf8574.error = Wire.endTransmission(); + } + //pcf8574.write(Pcf8574.pin[i]&0x7, rel_inverted[i] ? !state : state); + } + } +} + +void Pcf8574Init() +{ + Pcf8574.type = false; + + uint8_t pcf8574_address = PCF8574_ADDR1; + for (uint32_t i = 0; i < MAX_PCF8574; i++) { + + // AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PCF: Probing addr: 0x%x for PCF8574"), pcf8574_address); + + if (I2cDevice(pcf8574_address)) { + I2cSetActive(pcf8574_address); + Pcf8574.type = true; + + Pcf8574.address[Pcf8574.max_devices] = pcf8574_address; + Pcf8574.max_devices++; + + strcpy(Pcf8574.stype, "PCF8574"); + if (pcf8574_address >= PCF8574_ADDR2) { + strcpy(Pcf8574.stype, "PCF8574A"); + } + AddLog_P2(LOG_LEVEL_DEBUG, S_LOG_I2C_FOUND_AT, Pcf8574.stype, pcf8574_address); + } + pcf8574_address++; + if ((PCF8574_ADDR1 + 8) == pcf8574_address) { + pcf8574_address = PCF8574_ADDR2; + i=0; + } + } + if (Pcf8574.max_devices) { + for (uint32_t i = 0; i < sizeof(Pcf8574.pin); i++) { + Pcf8574.pin[i] = 99; + } + devices_present = devices_present - Pcf8574.max_connected_ports; // reset no of devices to avoid duplicate ports on duplicate init. + Pcf8574.max_connected_ports = 0; // reset no of devices to avoid duplicate ports on duplicate init. + for (uint32_t idx = 0; idx < Pcf8574.max_devices; idx++) { // suport up to 8 boards PCF8574 + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PCF: Device %d config 0x%02x"), idx +1, Settings.pcf8574_config[idx]); + + for (uint32_t i = 0; i < 8; i++) { + uint8_t _result = Settings.pcf8574_config[idx] >> i &1; + //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PCF: I2C shift i %d: %d. Powerstate: %d, devices_present: %d"), i,_result, Settings.power>>i&1, devices_present); + if (_result > 0) { + Pcf8574.pin[devices_present] = i + 8 * idx; + bitWrite(rel_inverted, devices_present, Settings.flag3.pcf8574_ports_inverted); + devices_present++; + Pcf8574.max_connected_ports++; + } + } + } + AddLog_P2(LOG_LEVEL_INFO, PSTR("PCF: Total devices %d, PCF8574 output ports %d"), Pcf8574.max_devices, Pcf8574.max_connected_ports); + } +} + +/*********************************************************************************************\ + * Presentation +\*********************************************************************************************/ + +#ifdef USE_WEBSERVER + +#define WEB_HANDLE_PCF8574 "pcf" + +const char HTTP_BTN_MENU_PCF8574[] PROGMEM = + "

"; + +const char HTTP_FORM_I2C_PCF8574_1[] PROGMEM = + "
 " D_PCF8574_PARAMETERS " " + "
" + "

" D_INVERT_PORTS "


"; + +const char HTTP_FORM_I2C_PCF8574_2[] PROGMEM = + "" D_DEVICE " %d " D_PORT " %d"; + +void HandlePcf8574(void) +{ + if (!HttpCheckPriviledgedAccess()) { return; } + + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_CONFIGURE_PCF8574)); + + if (WebServer->hasArg("save")) { + Pcf8574SaveSettings(); + WebRestart(1); + return; + } + + WSContentStart_P(D_CONFIGURE_PCF8574); + WSContentSendStyle(); + WSContentSend_P(HTTP_FORM_I2C_PCF8574_1, (Settings.flag3.pcf8574_ports_inverted) ? " checked" : ""); + WSContentSend_P(HTTP_TABLE100); + for (uint32_t idx = 0; idx < Pcf8574.max_devices; idx++) { + for (uint32_t idx2 = 0; idx2 < 8; idx2++) { // 8 ports on PCF8574 + uint8_t helper = 1 << idx2; + WSContentSend_P(HTTP_FORM_I2C_PCF8574_2, + idx +1, idx2, + idx2 + 8*idx, + idx2 + 8*idx, + ((helper & Settings.pcf8574_config[idx]) >> idx2 == 0) ? " selected " : " ", + ((helper & Settings.pcf8574_config[idx]) >> idx2 == 1) ? " selected " : " " + ); + } + } + WSContentSend_P(PSTR("")); + WSContentSend_P(HTTP_FORM_END); + WSContentSpaceButton(BUTTON_CONFIGURATION); + WSContentStop(); +} + +void Pcf8574SaveSettings() +{ + char stemp[7]; + char tmp[100]; + + //AddLog_P(LOG_LEVEL_DEBUG, PSTR("PCF: Start working on Save arguements: inverted:%d")), WebServer->hasArg("b1"); + + Settings.flag3.pcf8574_ports_inverted = WebServer->hasArg("b1"); + for (byte idx = 0; idx < Pcf8574.max_devices; idx++) { + byte count=0; + byte n = Settings.pcf8574_config[idx]; + while(n!=0) { + n = n&(n-1); + count++; + } + if (count <= devices_present) { + devices_present = devices_present - count; + } + for (byte i = 0; i < 8; i++) { + snprintf_P(stemp, sizeof(stemp), PSTR("i2cs%d"), i+8*idx); + WebGetArg(stemp, tmp, sizeof(tmp)); + byte _value = (!strlen(tmp)) ? 0 : atoi(tmp); + if (_value) { + Settings.pcf8574_config[idx] = Settings.pcf8574_config[idx] | 1 << i; + devices_present++; + Pcf8574.max_connected_ports++; + } else { + Settings.pcf8574_config[idx] = Settings.pcf8574_config[idx] & ~(1 << i ); + } + } + //Settings.pcf8574_config[0] = (!strlen(webServer->arg("i2cs0").c_str())) ? 0 : atoi(webServer->arg("i2cs0").c_str()); + //AddLog_P2(LOG_LEVEL_INFO, PSTR("PCF: I2C Board: %d, Config: %2x")), idx, Settings.pcf8574_config[idx]; + + } +} +#endif // USE_WEBSERVER + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xdrv28(uint8_t function) +{ + bool result = false; + + if (i2c_flg && Pcf8574.type) { + switch (function) { + case FUNC_SET_POWER: + Pcf8574SwitchRelay(); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_ADD_BUTTON: + WSContentSend_P(HTTP_BTN_MENU_PCF8574); + break; + case FUNC_WEB_ADD_HANDLER: + WebServer->on("/" WEB_HANDLE_PCF8574, HandlePcf8574); + break; +#endif // USE_WEBSERVER + case FUNC_PRE_INIT: + Pcf8574Init(); + break; + } + } + return result; +} + +#endif // USE_PCF8574 +#endif // USE_I2C diff --git a/sonoff/xdrv_29_deepsleep.ino b/sonoff/xdrv_29_deepsleep.ino new file mode 100644 index 000000000..13706798b --- /dev/null +++ b/sonoff/xdrv_29_deepsleep.ino @@ -0,0 +1,158 @@ +/* + xdrv_29_deepsleep.ino - DeepSleep support for Sonoff-Tasmota + + Copyright (C) 2019 Stefan Bode + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef USE_DEEPSLEEP +/*********************************************************************************************\ + * DeepSleep Support +\*********************************************************************************************/ + +#define XDRV_29 29 + +#define MAX_DEEPSLEEP_CYCLE 3600 // Maximum time for a deepsleep +#define MIN_DEEPSLEEP_TIME 5 + +#define D_PRFX_DEEPSLEEP "DeepSleep" +#define D_CMND_DEEPSLEEP_TIME "Time" + +const char kDeepsleepCommands[] PROGMEM = D_PRFX_DEEPSLEEP "|" + D_CMND_DEEPSLEEP_TIME ; + +void (* const DeepsleepCommand[])(void) PROGMEM = { + &CmndDeepsleepTime }; + +const char JSON_DEEPSLEEP[] PROGMEM = "\"" D_PRFX_DEEPSLEEP "%d\":{\"Time\":%d}"; + +void DeepSleepInit(void) +{ + if (pin[GPIO_DEEPSLEEP] < 99) { + if (!digitalRead(pin[GPIO_DEEPSLEEP])) { + RtcSettings.ultradeepsleep = 0; + } + } + if ((RtcSettings.ultradeepsleep > MAX_DEEPSLEEP_CYCLE) && (RtcSettings.ultradeepsleep < 1700000000)) { + RtcSettings.ultradeepsleep = RtcSettings.ultradeepsleep - MAX_DEEPSLEEP_CYCLE; + RtcReboot.fast_reboot_count = 0; + RtcRebootSave(); + AddLog_P2(LOG_LEVEL_ERROR, PSTR("DSL: Remain DeepSleep %d"), RtcSettings.ultradeepsleep); + RtcSettingsSave(); + ESP.deepSleep(100 * RtcSettings.deepsleep_slip * (MAX_DEEPSLEEP_CYCLE < RtcSettings.ultradeepsleep ? MAX_DEEPSLEEP_CYCLE : RtcSettings.ultradeepsleep), WAKE_RF_DEFAULT); + yield(); + } + RtcSettings.ultradeepsleep = 0; +} + +void CheckForDeepsleep(void) +{ + uint8_t disable_deepsleep_switch = 0; + if (pin[GPIO_DEEPSLEEP] < 99) { + disable_deepsleep_switch = !digitalRead(pin[GPIO_DEEPSLEEP]); + } + // new function AFTER_TELEPERIOD can take some time therefore <2 + if ((Settings.deepsleep > 10) && (Settings.deepsleep < 4294967295) && !disable_deepsleep_switch && (tele_period < 2) && prep_called) { + SettingsSaveAll(); + // deepsleep_slip is ideally 10.000 == 100% + // typically the device has up to 4% slip. Anything else is a wrong setting in the deepsleep_slip + // therefore all values >110% or <90% will be resetted to 100% to avoid crazy sleep times. + // This should normally never executed, but can happen an manual wakeup and problems during wakeup + if ((RtcSettings.nextwakeup == 0) || (RtcSettings.deepsleep_slip < 9000) || (RtcSettings.deepsleep_slip > 11000) || (RtcSettings.nextwakeup > (UtcTime() + Settings.deepsleep))) { + AddLog_P2(LOG_LEVEL_ERROR, PSTR("DSL: Reset wrong settings wakeup: %ld, slip %ld"), RtcSettings.nextwakeup, RtcSettings.deepsleep_slip ); + RtcSettings.nextwakeup = 0; + RtcSettings.deepsleep_slip = 10000; +// AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("new settings wakeup: %ld, slip %ld"), RtcSettings.nextwakeup, RtcSettings.deepsleep_slip ); + } + // timeslip in 0.1 seconds between the real wakeup and the calculated wakeup + // because deepsleep is in second and timeslip in 0.1 sec the compare always check if the slip is in the 10% range + int16_t timeslip = (int16_t)(RtcSettings.nextwakeup+millis()/1000-UtcTime())*10; +// AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("DSL: Timeslip 0.1 sec:? %d < %d < %ld"), -Settings.deepsleep, timeslip, Settings.deepsleep ); + //allow 10% of deepsleep error to count as valid deepsleep; expecting 3-4% + // if more then 10% timeslip = 0 == non valid wakeup; maybe manual + timeslip = (timeslip < -(int32_t)Settings.deepsleep) ? 0 : (timeslip > (int32_t)Settings.deepsleep) ? 0 : 1; +// AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("DSL: Normal deepsleep? %d"), timeslip ); + if (timeslip) { + RtcSettings.deepsleep_slip = (Settings.deepsleep + RtcSettings.nextwakeup-UtcTime()) * RtcSettings.deepsleep_slip / (Settings.deepsleep - (millis() / 1000)); + //Avoid crazy numbers. Again maximum 10% deviation. +// AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("DSL: %% calculate drift %ld"), RtcSettings.deepsleep_slip ); + RtcSettings.deepsleep_slip = tmin(tmax(RtcSettings.deepsleep_slip, 9000),11000); + +// AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("DSL: %% new drift %ld"), RtcSettings.deepsleep_slip ); + RtcSettings.nextwakeup += Settings.deepsleep; + } + // it may happen that wakeup in just <5 seconds in future + // in this case also add deepsleep to nextwakeup + if (RtcSettings.nextwakeup <= (UtcTime() - MIN_DEEPSLEEP_TIME)) { + // ensure nextwakeup is at least in the future + RtcSettings.nextwakeup += (((UtcTime() + MIN_DEEPSLEEP_TIME - RtcSettings.nextwakeup) / Settings.deepsleep) + 1) * Settings.deepsleep; + } + String dt = GetDT(RtcSettings.nextwakeup + LocalTime() - UtcTime()); // 2017-03-07T11:08:02 +// AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("DSL: Next wakeup %s"), (char*)dt.c_str()); + //limit sleeptime to MAX_DEEPSLEEP_CYCLE + //uint32_t sleeptime = MAX_DEEPSLEEP_CYCLE < (RtcSettings.nextwakeup - UtcTime()) ? (uint32_t)MAX_DEEPSLEEP_CYCLE : RtcSettings.nextwakeup - UtcTime(); + uint32_t sleeptime = tmin((uint32_t)MAX_DEEPSLEEP_CYCLE , RtcSettings.nextwakeup - UtcTime()); + Response_P(PSTR("{\"" D_PRFX_DEEPSLEEP "\":{\"" D_JSON_TIME "\":\"%s\",\"Epoch\":%d}}"), (char*)dt.c_str(), RtcSettings.nextwakeup); + MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_STATUS "1"), false); + Response_P(S_OFFLINE); + MqttPublishPrefixTopic_P(TELE, PSTR(D_LWT), true); // Offline or remove previous retained topic + yield(); + MqttDisconnect(); + RtcSettings.ultradeepsleep = RtcSettings.nextwakeup - UtcTime(); +// AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("DSL: Sleeptime %d sec, deepsleep_slip %ld"), sleeptime, RtcSettings.deepsleep_slip); + RtcSettingsSave(); + ESP.deepSleep(100 * RtcSettings.deepsleep_slip * sleeptime); + yield(); + } + prep_called = false; +} + +/*********************************************************************************************\ + * Commands +\*********************************************************************************************/ + +void CmndDeepsleepTime(void) +{ +// if ((XdrvMailbox.payload == 0) || ((XdrvMailbox.payload > 10) && (XdrvMailbox.payload < 4294967295))) { + if ((XdrvMailbox.payload == 0) || ((XdrvMailbox.payload > 10) && (XdrvMailbox.payload < (24 * 60 * 60)))) { // Allow max 24 hours sleep + Settings.deepsleep = XdrvMailbox.payload; + RtcSettings.nextwakeup = 0; + } + Response_P(S_JSON_COMMAND_NVALUE, XdrvMailbox.command, Settings.deepsleep); +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xdrv29(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_AFTER_TELEPERIOD: + CheckForDeepsleep(); + break; + case FUNC_COMMAND: + result = DecodeCommand(kDeepsleepCommands, DeepsleepCommand); + break; + case FUNC_PRE_INIT: + DeepSleepInit(); + break; + } + return result; +} + +#endif //USE_DEEPSLEEP diff --git a/sonoff/xdrv_30_exs_dimmer.ino b/sonoff/xdrv_30_exs_dimmer.ino new file mode 100644 index 000000000..a476db95c --- /dev/null +++ b/sonoff/xdrv_30_exs_dimmer.ino @@ -0,0 +1,637 @@ +/* + xdrv_30_exs_dimmer.ino - ex-store dimmer support for Sonoff-Tasmota + + Copyright (C) 2019 Andreas Schultz + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef USE_LIGHT +#ifdef USE_EXS_DIMMER +/*********************************************************************************************\ + * EX-Store WiFi Dimmer V4 + * https://ex-store.de/2-Kanal-RS232-WiFi-WLan-Dimmer-Modul-V4-fuer-Unterputzmontage-230V-3A + * https://ex-store.de/2-Kanal-RS232-WiFi-WLan-Dimmer-Modul-V4-fuer-Unterputzmontage-230V-3A-ESP8266-V12-Stift-und-Buchsenleisten +\*********************************************************************************************/ +//#define EXS_DEBUG + +#define XDRV_30 30 + +#define EXS_GATE_1_ON 0x20 +#define EXS_GATE_1_OFF 0x21 +#define EXS_DIMM_1_ON 0x22 +#define EXS_DIMM_1_OFF 0x23 +#define EXS_DIMM_1_TBL 0x24 +#define EXS_DIMM_1_VAL 0x25 +#define EXS_GATE_2_ON 0x30 +#define EXS_GATE_2_OFF 0x31 +#define EXS_DIMM_2_ON 0x32 +#define EXS_DIMM_2_OFF 0x33 +#define EXS_DIMM_2_TBL 0x34 +#define EXS_DIMM_2_VAL 0x35 +#define EXS_GATES_ON 0x40 +#define EXS_GATES_OFF 0x41 +#define EXS_DIMMS_ON 0x50 +#define EXS_DIMMS_OFF 0x51 +#define EXS_CH_LOCK 0x60 +#define EXS_GET_VALUES 0xFA +#define EXS_WRITE_EE 0xFC +#define EXS_READ_EE 0xFD +#define EXS_GET_VERSION 0xFE +#define EXS_RESET 0xFF + +#define EXS_BUFFER_SIZE 256 +#define EXS_ACK_TIMEOUT 200 // 200 ms ACK timeout + +#include + +TasmotaSerial *ExsSerial = nullptr; + +typedef struct +{ + uint8_t on = 0; + uint8_t bright_tbl = 0; + uint8_t dimm = 0; + uint8_t impuls_start = 0; + uint32_t impuls_len = 0; +} CHANNEL; + +typedef struct +{ + uint8_t version_major = 0; + uint8_t version_minor = 0; + CHANNEL channel[2]; + uint8_t gate_lock = 0; +} DIMMER; + +struct EXS +{ + uint8_t *buffer = nullptr; // Serial receive buffer + int byte_counter = 0; // Index in serial receive buffer + int cmd_status = 0; + uint8_t power = 0; + uint8_t dimm[2] = {0, 0}; + DIMMER dimmer; +} Exs; + +/* + * Internal Functions + */ + +uint8_t crc8(const uint8_t *p, uint8_t len) +{ + const uint8_t table[] = { + 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, + 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D}; + + const uint8_t table_rev[] = { + 0x00, 0x70, 0xE0, 0x90, 0xC1, 0xB1, 0x21, 0x51, + 0x83, 0xF3, 0x63, 0x13, 0x42, 0x32, 0xA2, 0xD2}; + + uint8_t offset; + uint8_t temp, crc8_temp; + uint8_t crc8 = 0; + + for (int i = 0; i < len; i++) + { + temp = *(p + i); + offset = temp ^ crc8; + offset >>= 4; + crc8_temp = crc8 & 0x0f; + crc8 = crc8_temp ^ table_rev[offset]; + offset = crc8 ^ temp; + offset &= 0x0f; + crc8_temp = crc8 & 0xf0; + crc8 = crc8_temp ^ table[offset]; + } + return crc8 ^ 0x55; +} + +void ExsSerialSend(const uint8_t data[] = nullptr, uint16_t len = 0) +{ + int retries = 3; + char rc; + +#ifdef EXS_DEBUG + snprintf_P(log_data, sizeof(log_data), PSTR("EXS: Tx Packet: \"")); + for (uint32_t i = 0; i < len; i++) + { + snprintf_P(log_data, sizeof(log_data), PSTR("%s%02x"), log_data, data[i]); + } + snprintf_P(log_data, sizeof(log_data), PSTR("%s\""), log_data); + AddLog(LOG_LEVEL_DEBUG_MORE); +#endif + + while (retries) + { + retries--; + + ExsSerial->write(data, len); + ExsSerial->flush(); + + // wait for any response + uint32_t snd_time = millis(); + while ((TimePassedSince(snd_time) < EXS_ACK_TIMEOUT) && + (!ExsSerial->available())) + ; + + if (!ExsSerial->available()) + { + // timeout +#ifdef EXS_DEBUG + AddLog_P(LOG_LEVEL_DEBUG, PSTR("ESX: serial send timeout")); +#endif + continue; + } + + rc = ExsSerial->read(); + if (rc == 0xFF) + break; + } +} + +void ExsSendCmd(uint8_t cmd, uint8_t value) +{ + uint8_t buffer[8]; + uint16_t len; + + buffer[0] = 0x7b; + buffer[3] = cmd; + + switch (cmd) + { + case EXS_GATE_1_ON: + case EXS_GATE_1_OFF: + case EXS_DIMM_1_ON: + case EXS_DIMM_1_OFF: + case EXS_GATE_2_ON: + case EXS_GATE_2_OFF: + case EXS_DIMM_2_ON: + case EXS_DIMM_2_OFF: + case EXS_GATES_ON: + case EXS_GATES_OFF: + case EXS_DIMMS_ON: + case EXS_DIMMS_OFF: + case EXS_GET_VALUES: + case EXS_GET_VERSION: + case EXS_RESET: + buffer[2] = 1; + len = 4; + break; + + case EXS_CH_LOCK: + case EXS_DIMM_1_TBL: + case EXS_DIMM_1_VAL: + case EXS_DIMM_2_TBL: + case EXS_DIMM_2_VAL: + buffer[2] = 2; + buffer[4] = value; + len = 5; + break; + } + buffer[1] = crc8(&buffer[3], buffer[2]); + + ExsSerialSend(buffer, len); +} + +uint8_t ExsSetPower(uint8_t device, uint8_t power) +{ + Exs.dimmer.channel[device].dimm = power; + ExsSendCmd(EXS_DIMM_1_ON + 0x10 * device + power ^ 1, 0); +} + +uint8_t ExsSetBri(uint8_t device, uint8_t bri) +{ + Exs.dimmer.channel[device].bright_tbl = bri; + ExsSendCmd(EXS_DIMM_1_TBL + 0x10 * device, bri); +} + +uint8_t ExsSyncState(uint8_t device) +{ +#ifdef EXS_DEBUG + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EXS: Channel %d Power Want %d, Is %d"), + device, bitRead(Exs.power, device), Exs.dimmer.channel[device].dimm); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EXS: Set Channel %d Brightness Want %d, Is %d"), + device, Exs.dimm[device], Exs.dimmer.channel[device].bright_tbl); +#endif + + if (bitRead(Exs.power, device) && + Exs.dimm[device] != Exs.dimmer.channel[device].bright_tbl) { + ExsSetBri(device, Exs.dimm[device]); + } + + if (!Exs.dimm[device]) { + Exs.dimmer.channel[device].dimm = 0; + } else if (Exs.dimmer.channel[device].dimm != bitRead(Exs.power, device)) { + ExsSetPower(device, bitRead(Exs.power, device)); + } +} + +bool ExsSyncState() +{ +#ifdef EXS_DEBUG + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EXS: Serial %p, Cmd %d"), ExsSerial, Exs.cmd_status); +#endif + + if (!ExsSerial || Exs.cmd_status != 0) + return false; + + ExsSyncState(0); + ExsSyncState(1); +} + +void ExsDebugState() +{ +#ifdef EXS_DEBUG + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EXS: MCU v%d.%d, c0: On:%d,Dim:%d,Tbl:%d(%d%%), c1: On:%d,Dim:%d,Tbl:%d(%d%%), ChLock: %d"), + Exs.dimmer.version_major, Exs.dimmer.version_minor, + Exs.dimmer.channel[0].on, Exs.dimmer.channel[0].dimm, + Exs.dimmer.channel[0].bright_tbl, + changeUIntScale(Exs.dimmer.channel[0].bright_tbl, 0, 255, 0, 100), + Exs.dimmer.channel[1].on, Exs.dimmer.channel[1].dimm, + Exs.dimmer.channel[1].bright_tbl, + changeUIntScale(Exs.dimmer.channel[1].bright_tbl, 0, 255, 0, 100), + Exs.dimmer.gate_lock); +#endif +} + +void ExsPacketProcess(void) +{ + uint8_t len = Exs.buffer[1]; + uint8_t cmd = Exs.buffer[2]; + + switch (cmd) + { + case EXS_GET_VALUES: + /* + format firmware 2.1 + 0. byte = startMarker + 1. byte = 0. crc of bytes 2(CMD) - 11(GATE_LOCK) + 2. byte = 1. len_Of_Payload + 3. byte = 2. CMD + 4. byte = 3. MAJOR + 5. byte = 4. MINOR + 6. byte = 5. GATE1_ON + 7. byte = 6. GATE1_DIMM + 8. byte = 7. GATE1.BRIGHT + 9. byte = 8. GATE2_ON + 10. byte = 9. GATE2_DIMM + 11. byte = 10. GATE2.BRIGHT + 12. byte = 11. GATE_LOCK + 13. byte = '\0' + */ + if (len > 9) + { + Exs.dimmer.version_major = Exs.buffer[3]; + Exs.dimmer.version_minor = Exs.buffer[4]; + + //Exs.dimmer.channel[0].on = Exs.buffer[5]; + Exs.dimmer.channel[0].on = Exs.buffer[6]; + Exs.dimmer.channel[0].dimm = Exs.buffer[6]; + Exs.dimmer.channel[0].bright_tbl = Exs.buffer[7]; + + //Exs.dimmer.channel[1].on = Exs.buffer[8]; + Exs.dimmer.channel[1].on = Exs.buffer[9]; + Exs.dimmer.channel[1].dimm = Exs.buffer[9]; + Exs.dimmer.channel[1].bright_tbl = Exs.buffer[10]; + + Exs.dimmer.gate_lock = Exs.buffer[11]; + } + else + /* + format firmware 1.0 + 0. byte = startMarker + 1. byte = 0. crc of bytes 2(CMD) - 9(GATE_LOCK) + 2. byte = 1. len_Of_Payload + 3. byte = 2. CMD + 4. byte = 3. GATE1_ON + 5. byte = 4. GATE1_DIMM + 6. byte = 5. GATE1.BRIGHT + 7. byte = 6. GATE2_ON + 8. byte = 7. GATE2_DIMM + 9. byte = 8. GATE2.BRIGHT + 10. byte = 9. GATE_LOCK + 11. byte = '\0' + */ + { + Exs.dimmer.version_major = 1; + Exs.dimmer.version_minor = 0; + + //Exs.dimmer.channel[0].on = Exs.buffer[3] - 48; + Exs.dimmer.channel[0].on = Exs.buffer[4] - 48; + Exs.dimmer.channel[0].dimm = Exs.buffer[4] - 48; + Exs.dimmer.channel[0].bright_tbl = Exs.buffer[5] - 48; + + //Exs.dimmer.channel[1].on = Exs.buffer[6] - 48; + Exs.dimmer.channel[1].on = Exs.buffer[7] - 48; + Exs.dimmer.channel[1].dimm = Exs.buffer[7] - 48; + Exs.dimmer.channel[1].bright_tbl = Exs.buffer[8] - 48; + + Exs.dimmer.gate_lock = Exs.buffer[9] - 48; + } + + ExsDebugState(); + ExsSyncState(); + ExsDebugState(); + break; + default: + break; + } +} +/* + * API Functions + */ +bool ExsModuleSelected(void) +{ + Settings.light_correction = 0; + Settings.flag.mqtt_serial = 0; + Settings.flag3.pwm_multi_channels = 1; + SetSeriallog(LOG_LEVEL_NONE); + + devices_present = +2; + light_type = LT_SERIAL2; + return true; +} + +bool ExsSetChannels(void) +{ +#ifdef EXS_DEBUG + snprintf_P(log_data, sizeof(log_data), PSTR("EXS: SetChannels: \"")); + for (int i = 0; i < XdrvMailbox.data_len; i++) + { + snprintf_P(log_data, sizeof(log_data), PSTR("%s%02x"), log_data, ((uint8_t *)XdrvMailbox.data)[i]); + } + snprintf_P(log_data, sizeof(log_data), PSTR("%s\""), log_data); + AddLog(LOG_LEVEL_DEBUG_MORE); +#endif + + Exs.dimm[0] = ((uint8_t *)XdrvMailbox.data)[0]; + Exs.dimm[1] = ((uint8_t *)XdrvMailbox.data)[1]; + return ExsSyncState(); +} + +bool ExsSetPower(void) +{ + AddLog_P2(LOG_LEVEL_INFO, PSTR("EXS: Set Power, Device %d, Power 0x%02x"), + active_device, XdrvMailbox.index); + + Exs.power = XdrvMailbox.index; + return ExsSyncState(); +} + +void EsxMcuStart(void) +{ + int retries = 3; + +#ifdef EXS_DEBUG + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EXS: Request MCU configuration, PIN %d to Low"), pin[GPIO_EXS_ENABLE]); +#endif + + pinMode(pin[GPIO_EXS_ENABLE], OUTPUT); + digitalWrite(pin[GPIO_EXS_ENABLE], LOW); + + delay(1); // wait 1ms fot the MCU to come online + + while (ExsSerial->available()) + { + // clear in the receive buffer + ExsSerial->read(); + } +} + +void ExsInit(void) +{ +#ifdef EXS_DEBUG + AddLog_P2(LOG_LEVEL_INFO, PSTR("EXS: Starting Tx %d Rx %d"), pin[GPIO_TXD], pin[GPIO_RXD]); +#endif + + Exs.buffer = (uint8_t *)malloc(EXS_BUFFER_SIZE); + if (Exs.buffer != nullptr) + { + ExsSerial = new TasmotaSerial(pin[GPIO_RXD], pin[GPIO_TXD], 2); + if (ExsSerial->begin(9600)) + { + if (ExsSerial->hardwareSerial()) + { + ClaimSerial(); + } + ExsSerial->flush(); + EsxMcuStart(); + ExsSendCmd(EXS_CH_LOCK, 0); + ExsSendCmd(EXS_GET_VALUES, 0); + } + } +} + +void ExsSerialInput(void) +{ + while (ExsSerial->available()) + { + yield(); + uint8_t serial_in_byte = ExsSerial->read(); + + AddLog_P2(LOG_LEVEL_INFO, PSTR("EXS: Serial In Byte 0x%02x"), serial_in_byte); + + if (Exs.cmd_status == 0 && + serial_in_byte == 0x7B) + { + Exs.cmd_status = 1; + Exs.byte_counter = 0; + } + else if (Exs.byte_counter >= EXS_BUFFER_SIZE) + { + Exs.cmd_status = 0; + } + else if (Exs.cmd_status == 1) + { + Exs.buffer[Exs.byte_counter++] = serial_in_byte; + + if (Exs.byte_counter > 2 && Exs.byte_counter == Exs.buffer[1] + 2) + { + uint8_t crc = crc8(&Exs.buffer[2], Exs.buffer[1]); + + // all read + Exs.cmd_status = 0; + +#ifdef EXS_DEBUG + snprintf_P(log_data, sizeof(log_data), PSTR("EXS: RX Packet: \"")); + for (uint32_t i = 0; i < Exs.byte_counter; i++) + { + snprintf_P(log_data, sizeof(log_data), PSTR("%s%02x"), log_data, Exs.buffer[i]); + } + snprintf_P(log_data, sizeof(log_data), PSTR("%s\", CRC: 0x%02x"), log_data, crc); + AddLog(LOG_LEVEL_DEBUG_MORE); +#endif + + if (Exs.buffer[0] == crc) + { + ExsSerial->write(0xFF); //send ACK + ExsPacketProcess(); + } + else + { + ExsSerial->write(0x00); //send NO-ACK + } + + } + } + } +} + +/* + * Commands + */ + +#ifdef EXS_MCU_CMNDS + +#define D_PRFX_EXS "Exs" +#define D_CMND_EXS_DIMM "Dimm" +#define D_CMND_EXS_DIMM_TBL "DimmTbl" +#define D_CMND_EXS_DIMM_VAL "DimmVal" +#define D_CMND_EXS_DIMMS "Dimms" +#define D_CMND_EXS_CH_LOCK "ChLock" +#define D_CMND_EXS_STATE "State" + +const char kExsCommands[] PROGMEM = D_PRFX_EXS "|" + D_CMND_EXS_DIMM "|" D_CMND_EXS_DIMM_TBL "|" D_CMND_EXS_DIMM_VAL "|" + D_CMND_EXS_DIMMS "|" D_CMND_EXS_CH_LOCK "|" + D_CMND_EXS_STATE; + +void (* const ExsCommand[])(void) PROGMEM = + { &CmndExsDimm, &CmndExsDimmTbl, &CmndExsDimmVal, + &CmndExsDimms, &CmndExsChLock, + &CmndExsState }; + +void CmndExsDimm(void) +{ + if ((XdrvMailbox.index == 1 || XdrvMailbox.index == 2) && + (XdrvMailbox.payload == 0 || XdrvMailbox.payload == 1)) { + ExsSendCmd(EXS_DIMM_1_ON + 0x10 * (XdrvMailbox.index - 1) + + XdrvMailbox.payload ^ 1, 0); + } + CmndExsState(); +} + +void CmndExsDimmTbl(void) +{ + if ((XdrvMailbox.index == 1 || XdrvMailbox.index == 2) && + (XdrvMailbox.payload > 0 || XdrvMailbox.payload <= 255)) { + ExsSendCmd(EXS_DIMM_1_TBL + 0x10 * (XdrvMailbox.index - 1), + XdrvMailbox.payload); + } + CmndExsState(); +} + +void CmndExsDimmVal(void) +{ + if ((XdrvMailbox.index == 1 || XdrvMailbox.index == 2) && + (XdrvMailbox.payload > 0 || XdrvMailbox.payload <= 255)) { + ExsSendCmd(EXS_DIMM_1_VAL + 0x10 * (XdrvMailbox.index - 1), + XdrvMailbox.payload); + } + CmndExsState(); +} + +void CmndExsDimms(void) +{ + if (XdrvMailbox.payload == 0 || XdrvMailbox.payload == 1) { + ExsSendCmd(EXS_DIMMS_ON + XdrvMailbox.payload ^ 1, 0); + } + CmndExsState(); +} + +void CmndExsChLock(void) +{ + if (XdrvMailbox.payload == 0 || XdrvMailbox.payload == 1) { + ExsSendCmd(EXS_CH_LOCK, XdrvMailbox.payload); + } + CmndExsState(); +} + +void CmndExsState(void) +{ + ExsSendCmd(EXS_GET_VALUES, 0); + + // wait for data + uint32_t snd_time = millis(); + while ((TimePassedSince(snd_time) < EXS_ACK_TIMEOUT) && + (!ExsSerial->available())) + ; + ExsSerialInput(); + + Response_P(PSTR("{\"" D_CMND_EXS_STATE "\":{")); + ResponseAppend_P(PSTR("\"McuVersion\":\"%d.%d\"," + "\"Channels\":["), + Exs.dimmer.version_major, Exs.dimmer.version_minor); + + for (uint32_t i = 0; i < 2; i++) { + if (i != 0) { + ResponseAppend_P(PSTR(",")); + } + ResponseAppend_P(PSTR("{\"On\":\"%d\"," + "\"BrightProz\":\"%d\"," + "\"BrightTab\":\"%d\"," + "\"Dimm\":\"%d\"}"), + Exs.dimmer.channel[i].on, + changeUIntScale(Exs.dimmer.channel[i].bright_tbl, 0, 255, 0, 100), + Exs.dimmer.channel[i].bright_tbl, + Exs.dimmer.channel[i].dimm); + } + ResponseAppend_P(PSTR("],")); + ResponseAppend_P(PSTR("\"GateLock\":\"%d\""), Exs.dimmer.gate_lock); + ResponseJsonEndEnd(); +} + +#endif + +/* + * Interface + */ + +bool Xdrv30(uint8_t function) +{ + bool result = false; + + if (EXS_DIMMER == my_module_type) + { + switch (function) + { + case FUNC_LOOP: + if (ExsSerial) + ExsSerialInput(); + break; + case FUNC_MODULE_INIT: + result = ExsModuleSelected(); + break; + case FUNC_INIT: + ExsInit(); + break; + case FUNC_SET_DEVICE_POWER: + result = ExsSetPower(); + break; + case FUNC_SET_CHANNELS: + result = ExsSetChannels(); + break; +#ifdef EXS_MCU_CMNDS + case FUNC_COMMAND: + result = DecodeCommand(kExsCommands, ExsCommand); + break; +#endif + } + } + return result; +} + +#endif // USE_EXS_DIMMER +#endif // USE_LIGHT \ No newline at end of file diff --git a/sonoff/xdrv_31_arduino_slave.ino b/sonoff/xdrv_31_arduino_slave.ino new file mode 100644 index 000000000..497ab28dd --- /dev/null +++ b/sonoff/xdrv_31_arduino_slave.ino @@ -0,0 +1,291 @@ +/* + xdrv_31_arduino_slave.ino - Support for Arduino Slave on Serial + + Copyright (C) 2019 Andre Thomas and Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef USE_ARDUINO_SLAVE +/*********************************************************************************************\ + * Arduino slave +\*********************************************************************************************/ + +#define XDRV_31 31 + +#define CONST_STK_CRC_EOP 0x20 + +#define CMND_STK_GET_SYNC 0x30 +#define CMND_STK_SET_DEVICE 0x42 +#define CMND_STK_SET_DEVICE_EXT 0x45 +#define CMND_STK_ENTER_PROGMODE 0x50 +#define CMND_STK_LEAVE_PROGMODE 0x51 +#define CMND_STK_LOAD_ADDRESS 0x55 +#define CMND_STK_PROG_PAGE 0x64 + +#include +#include + +struct ASLAVE { + uint32_t spi_hex_size = 0; + uint32_t spi_sector_counter = 0; + uint8_t spi_sector_cursor = 0; + uint8_t inverted = LOW; + bool type = false; + bool flashing = false; +} ASlave; + +TasmotaSerial *ArduinoSlave_Serial; + +uint32_t ArduinoSlaveFlashStart(void) +{ + return (ESP.getSketchSize() / SPI_FLASH_SEC_SIZE) + 2; // Stay on the safe side +} + +uint8_t ArduinoSlave_UpdateInit(void) +{ + ASlave.spi_hex_size = 0; + ASlave.spi_sector_counter = ArduinoSlaveFlashStart(); // Reset the pre-defined write address where firmware will temporarily be stored + ASlave.spi_sector_cursor = 0; + return 0; +} + +void ArduinoSlave_Reset(void) +{ + if (ASlave.type) { + digitalWrite(pin[GPIO_ARDUINO_RST], !ASlave.inverted); + delay(1); + digitalWrite(pin[GPIO_ARDUINO_RST], ASlave.inverted); + delay(1); + digitalWrite(pin[GPIO_ARDUINO_RST], !ASlave.inverted); + delay(5); + } +} + +uint8_t ArduinoSlave_waitForSerialData(int dataCount, int timeout) +{ + int timer = 0; + while (timer < timeout) { + if (ArduinoSlave_Serial->available() >= dataCount) { + return 1; + } + delay(1); + timer++; + } + return 0; +} + +uint8_t ArduinoSlave_sendBytes(uint8_t* bytes, int count) +{ + ArduinoSlave_Serial->write(bytes, count); + ArduinoSlave_waitForSerialData(2, 1000); + uint8_t sync = ArduinoSlave_Serial->read(); + uint8_t ok = ArduinoSlave_Serial->read(); + if (sync == 0x14 && ok == 0x10) { + return 1; + } + return 0; +} + +uint8_t ArduinoSlave_execCmd(uint8_t cmd) +{ + uint8_t bytes[] = { cmd, CONST_STK_CRC_EOP }; + return ArduinoSlave_sendBytes(bytes, 2); +} + +uint8_t ArduinoSlave_execParam(uint8_t cmd, uint8_t* params, int count) +{ + uint8_t bytes[32]; + bytes[0] = cmd; + int i = 0; + while (i < count) { + bytes[i + 1] = params[i]; + i++; + } + bytes[i + 1] = CONST_STK_CRC_EOP; + return ArduinoSlave_sendBytes(bytes, i + 2); +} + +uint8_t ArduinoSlave_exitProgMode(void) +{ + return ArduinoSlave_execCmd(CMND_STK_LEAVE_PROGMODE); // Exit programming mode +} + +void ArduinoSlave_SetupFlash(void) +{ + uint8_t ProgParams[] = {0x86,0x00,0x00,0x01,0x01,0x01,0x01,0x03,0xff,0xff,0xff,0xff,0x00,0x80,0x04,0x00,0x00,0x00,0x80,0x00}; + uint8_t ExtProgParams[] = {0x05,0x04,0xd7,0xc2,0x00}; + ArduinoSlave_Serial->begin(USE_ARDUINO_FLASH_SPEED); + if (ArduinoSlave_Serial->hardwareSerial()) { + ClaimSerial(); + } + ArduinoSlave_Reset(); + ArduinoSlave_execCmd(CMND_STK_GET_SYNC); + ArduinoSlave_execParam(CMND_STK_SET_DEVICE, ProgParams, sizeof(ProgParams)); // Set programming parameters + ArduinoSlave_execParam(CMND_STK_SET_DEVICE_EXT, ExtProgParams, sizeof(ExtProgParams)); // Set extended programming parameters + ArduinoSlave_execCmd(CMND_STK_ENTER_PROGMODE); // Enter programming mode +} + +uint8_t ArduinoSlave_loadAddress(uint8_t adrHi, uint8_t adrLo) +{ + uint8_t params[] = { adrHi, adrLo }; + return ArduinoSlave_execParam(CMND_STK_LOAD_ADDRESS, params, sizeof(params)); +} + +void ArduinoSlave_FlashPage(uint8_t* address, uint8_t* data) +{ + uint8_t Header[] = {CMND_STK_PROG_PAGE, 0x00, 0x80, 0x46}; + ArduinoSlave_loadAddress(address[1], address[0]); + ArduinoSlave_Serial->write(Header, 4); + for (int i = 0; i < 128; i++) { + ArduinoSlave_Serial->write(data[i]); + } + ArduinoSlave_Serial->write(CONST_STK_CRC_EOP); + ArduinoSlave_waitForSerialData(2, 1000); + ArduinoSlave_Serial->read(); + ArduinoSlave_Serial->read(); +} + +void ArduinoSlave_Flash(void) +{ + bool reading = true; + uint32_t read = 0; + uint32_t processed = 0; + char thishexline[50]; + uint8_t position = 0; + char* flash_buffer; + ArduinoHexParse hexParse = ArduinoHexParse(); + + ArduinoSlave_SetupFlash(); + + flash_buffer = new char[SPI_FLASH_SEC_SIZE]; + uint32_t flash_start = ArduinoSlaveFlashStart() * SPI_FLASH_SEC_SIZE; + while (reading) { + ESP.flashRead(flash_start + read, (uint32_t*)flash_buffer, SPI_FLASH_SEC_SIZE); + read = read + SPI_FLASH_SEC_SIZE; + if (read >= ASlave.spi_hex_size) { + reading = false; + } + for (uint32_t ca = 0; ca < SPI_FLASH_SEC_SIZE; ca++) { + processed++; + if (processed <= ASlave.spi_hex_size) { + if (':' == flash_buffer[ca]) { + position = 0; + } + if (0x0D == flash_buffer[ca]) { + thishexline[position] = 0; + hexParse.ParseLine((uint8_t*)thishexline); + if (hexParse.IsFlashPageReady()) { + uint8_t* page = hexParse.GetFlashPage(); + uint8_t* address = hexParse.GetLoadAddress(); + ArduinoSlave_FlashPage(address, page); + } + } else { + if (0x0A != flash_buffer[ca]) { + thishexline[position] = flash_buffer[ca]; + position++; + } + } + } + } + } + ASlave.flashing = false; + ArduinoSlave_exitProgMode(); + restart_flag = 2; +} + +void ArduinoSlave_SetFlagFlashing(bool value) +{ + ASlave.flashing = value; +} + +bool ArduinoSlave_GetFlagFlashing(void) +{ + return ASlave.flashing ; +} + +void ArduinoSlave_WriteBuffer(uint8_t *buf, size_t size) +{ + if (0 == ASlave.spi_sector_cursor) { // Starting a new sector write so we need to erase it first + ESP.flashEraseSector(ASlave.spi_sector_counter); + } + ASlave.spi_sector_cursor++; + ESP.flashWrite((ASlave.spi_sector_counter * SPI_FLASH_SEC_SIZE) + ((ASlave.spi_sector_cursor-1)*2048), (uint32_t*)buf, size); + ASlave.spi_hex_size = ASlave.spi_hex_size + size; + if (2 == ASlave.spi_sector_cursor) { // The web upload sends 2048 bytes at a time so keep track of the cursor position to reset it for the next flash sector erase + ASlave.spi_sector_cursor = 0; + ASlave.spi_sector_counter++; + } +} + +void ArduinoSlave_Init(void) +{ + if (ASlave.type) { + return; + } + if ((pin[GPIO_ARDUINO_RXD] < 99) && (pin[GPIO_ARDUINO_TXD] < 99) && + ((pin[GPIO_ARDUINO_RST] < 99) || (pin[GPIO_ARDUINO_RST_INV] < 99))) { + ArduinoSlave_Serial = new TasmotaSerial(pin[GPIO_ARDUINO_RXD], pin[GPIO_ARDUINO_TXD], 1, 0, 200); + if (ArduinoSlave_Serial->begin(USE_ARDUINO_SERIAL_SPEED)) { + if (ArduinoSlave_Serial->hardwareSerial()) { + ClaimSerial(); + } + if (pin[GPIO_ARDUINO_RST_INV] < 99) { + pin[GPIO_ARDUINO_RST] = pin[GPIO_ARDUINO_RST_INV]; + pin[GPIO_ARDUINO_RST_INV] = 99; + ASlave.inverted = HIGH; + } + pinMode(pin[GPIO_ARDUINO_RST], OUTPUT); + ASlave.type = true; + ArduinoSlave_Reset(); + AddLog_P2(LOG_LEVEL_INFO, PSTR("Arduino Slave Enabled")); + } + } +} + +void ArduinoSlave_Show(bool json) +{ + if (ASlave.type) { + ArduinoSlave_Serial->flush(); + ArduinoSlave_Serial->print("JSON"); + ArduinoSlave_Serial->find(char(0xFE)); + char buffer[100]; + uint16_t haveread = ArduinoSlave_Serial->readBytesUntil(char(0xFF), buffer, sizeof(buffer)-1); + buffer[haveread] = '\0'; + if (json) { + ResponseAppend_P(PSTR(",\"ArduinoSlave\":%s"), buffer); + } + } +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xdrv31(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_EVERY_SECOND: + ArduinoSlave_Init(); + break; + case FUNC_JSON_APPEND: + ArduinoSlave_Show(1); + break; + } + return result; +} + +#endif // USE_ARDUINO_SLAVE \ No newline at end of file diff --git a/sonoff/xdrv_99_debug.ino b/sonoff/xdrv_99_debug.ino index 46d2af06f..78437ad33 100644 --- a/sonoff/xdrv_99_debug.ino +++ b/sonoff/xdrv_99_debug.ino @@ -45,24 +45,52 @@ \*********************************************************************************************/ #define D_CMND_CFGDUMP "CfgDump" -#define D_CMND_CFGPOKE "CfgPoke" #define D_CMND_CFGPEEK "CfgPeek" +#define D_CMND_CFGPOKE "CfgPoke" #define D_CMND_CFGSHOW "CfgShow" #define D_CMND_CFGXOR "CfgXor" #define D_CMND_CPUCHECK "CpuChk" #define D_CMND_EXCEPTION "Exception" -#define D_CMND_FREEMEM "FreeMem" -#define D_CMND_RTCDUMP "RtcDump" -#define D_CMND_HELP "Help" -#define D_CMND_SETSENSOR "SetSensor" +#define D_CMND_FLASHDUMP "FlashDump" #define D_CMND_FLASHMODE "FlashMode" +#define D_CMND_FREEMEM "FreeMem" +#define D_CMND_HELP "Help" +#define D_CMND_RTCDUMP "RtcDump" +#define D_CMND_SETSENSOR "SetSensor" +#define D_CMND_I2CWRITE "I2CWrite" +#define D_CMND_I2CREAD "I2CRead" +#define D_CMND_I2CSTRETCH "I2CStretch" +#define D_CMND_I2CCLOCK "I2CClock" -enum DebugCommands { - CMND_CFGDUMP, CMND_CFGPEEK, CMND_CFGPOKE, CMND_CFGSHOW, CMND_CFGXOR, - CMND_CPUCHECK, CMND_EXCEPTION, CMND_FREEMEM, CMND_RTCDUMP, CMND_SETSENSOR, CMND_FLASHMODE, CMND_HELP }; -const char kDebugCommands[] PROGMEM = - D_CMND_CFGDUMP "|" D_CMND_CFGPEEK "|" D_CMND_CFGPOKE "|" D_CMND_CFGSHOW "|" D_CMND_CFGXOR "|" - D_CMND_CPUCHECK "|" D_CMND_EXCEPTION "|" D_CMND_FREEMEM "|" D_CMND_RTCDUMP "|" D_CMND_SETSENSOR "|" D_CMND_FLASHMODE "|" D_CMND_HELP; +const char kDebugCommands[] PROGMEM = "|" // No prefix + D_CMND_CFGDUMP "|" D_CMND_CFGPEEK "|" D_CMND_CFGPOKE "|" +#ifdef USE_DEBUG_SETTING_NAMES + D_CMND_CFGSHOW "|" +#endif +#ifdef USE_WEBSERVER + D_CMND_CFGXOR "|" +#endif + D_CMND_CPUCHECK "|" +#ifdef DEBUG_THEO + D_CMND_EXCEPTION "|" +#endif + D_CMND_FLASHDUMP "|" D_CMND_FLASHMODE "|" D_CMND_FREEMEM"|" D_CMND_HELP "|" D_CMND_RTCDUMP "|" D_CMND_SETSENSOR "|" + D_CMND_I2CWRITE "|" D_CMND_I2CREAD "|" D_CMND_I2CSTRETCH "|" D_CMND_I2CCLOCK ; + +void (* const DebugCommand[])(void) PROGMEM = { + &CmndCfgDump, &CmndCfgPeek, &CmndCfgPoke, +#ifdef USE_DEBUG_SETTING_NAMES + &CmndCfgShow, +#endif +#ifdef USE_WEBSERVER + &CmndCfgXor, +#endif + &CmndCpuCheck, +#ifdef DEBUG_THEO + &CmndException, +#endif + &CmndFlashDump, &CmndFlashMode, &CmndFreemem, &CmndHelp, &CmndRtcDump, &CmndSetSensor, + &CmndI2cWrite, &CmndI2cRead, &CmndI2cStretch, &CmndI2cClock }; uint32_t CPU_loops = 0; uint32_t CPU_last_millis = 0; @@ -113,8 +141,6 @@ Decoding 14 results 0x4021ffb4: snprintf_P(char*, unsigned int, char const*, ...) at C:\Data2\Arduino\arduino-1.8.1-esp-2.3.0\portable\packages\esp8266\hardware\esp8266\2.3.0\cores\esp8266/pgmspace.cpp line 146 0x40201118: atol at C:\Data2\Arduino\arduino-1.8.1-esp-2.3.0\portable\packages\esp8266\hardware\esp8266\2.3.0\cores\esp8266/core_esp8266_noniso.c line 45 0x40201128: atoi at C:\Data2\Arduino\arduino-1.8.1-esp-2.3.0\portable\packages\esp8266\hardware\esp8266\2.3.0\cores\esp8266/core_esp8266_noniso.c line 45 -0x4020fafb: MqttDataHandler(char*, unsigned char*, unsigned int) at R:\Arduino\Work-ESP8266\Theo\sonoff\sonoff-4\sonoff/sonoff.ino line 679 (discriminator 1) -0x4022321b: pp_attach at ?? line ? 00:00:08 MQTT: tele/sonoff/INFO3 = {"Started":"Fatal exception:28 flag:2 (EXCEPTION) epc1:0x4000bf64 epc2:0x00000000 epc3:0x00000000 excvaddr:0x00000007 depc:0x00000000"} */ @@ -400,92 +426,231 @@ void SetFlashMode(uint8_t mode) if (ESP.flashRead(address, (uint32_t*)_buffer, FLASH_SECTOR_SIZE)) { if (_buffer[2] != mode) { // DOUT _buffer[2] = mode; - if (ESP.flashEraseSector(address / FLASH_SECTOR_SIZE)) ESP.flashWrite(address, (uint32_t*)_buffer, FLASH_SECTOR_SIZE); + if (ESP.flashEraseSector(address / FLASH_SECTOR_SIZE)) { + ESP.flashWrite(address, (uint32_t*)_buffer, FLASH_SECTOR_SIZE); + } } } delete[] _buffer; } -/*******************************************************************************************/ +/*********************************************************************************************\ + * Commands +\*********************************************************************************************/ -bool DebugCommand(void) +void CmndHelp(void) { - char command[CMDSZ]; - bool serviced = true; + AddLog_P(LOG_LEVEL_INFO, PSTR("HLP: "), kDebugCommands); + ResponseCmndDone(); +} + +void CmndRtcDump(void) +{ + DebugRtcDump(XdrvMailbox.data); + ResponseCmndDone(); +} + +void CmndCfgDump(void) +{ + DebugCfgDump(XdrvMailbox.data); + ResponseCmndDone(); +} + +void CmndCfgPeek(void) +{ + DebugCfgPeek(XdrvMailbox.data); + ResponseCmndDone(); +} + +void CmndCfgPoke(void) +{ + DebugCfgPoke(XdrvMailbox.data); + ResponseCmndDone(); +} - int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic, kDebugCommands); - if (-1 == command_code) { - serviced = false; // Unknown command - } - else if (CMND_HELP == command_code) { - AddLog_P(LOG_LEVEL_INFO, kDebugCommands); - Response_P(S_JSON_COMMAND_SVALUE, command, D_JSON_DONE); - } - else if (CMND_RTCDUMP == command_code) { - DebugRtcDump(XdrvMailbox.data); - Response_P(S_JSON_COMMAND_SVALUE, command, D_JSON_DONE); - } - else if (CMND_CFGDUMP == command_code) { - DebugCfgDump(XdrvMailbox.data); - Response_P(S_JSON_COMMAND_SVALUE, command, D_JSON_DONE); - } - else if (CMND_CFGPEEK == command_code) { - DebugCfgPeek(XdrvMailbox.data); - Response_P(S_JSON_COMMAND_SVALUE, command, D_JSON_DONE); - } - else if (CMND_CFGPOKE == command_code) { - DebugCfgPoke(XdrvMailbox.data); - Response_P(S_JSON_COMMAND_SVALUE, command, D_JSON_DONE); - } #ifdef USE_DEBUG_SETTING_NAMES - else if (CMND_CFGSHOW == command_code) { - DebugCfgShow(XdrvMailbox.payload); - Response_P(S_JSON_COMMAND_SVALUE, command, D_JSON_DONE); - } +void CmndCfgShow(void) +{ + DebugCfgShow(XdrvMailbox.payload); + ResponseCmndDone(); +} #endif // USE_DEBUG_SETTING_NAMES -#ifdef USE_WEBSERVER - else if (CMND_CFGXOR == command_code) { - if (XdrvMailbox.data_len > 0) { - config_xor_on_set = XdrvMailbox.payload; - } - Response_P(S_JSON_COMMAND_NVALUE, command, config_xor_on_set); - } -#endif // USE_WEBSERVER -#ifdef DEBUG_THEO - else if (CMND_EXCEPTION == command_code) { - if (XdrvMailbox.data_len > 0) ExceptionTest(XdrvMailbox.payload); - Response_P(S_JSON_COMMAND_SVALUE, command, D_JSON_DONE); - } -#endif // DEBUG_THEO - else if (CMND_CPUCHECK == command_code) { - if (XdrvMailbox.data_len > 0) { - CPU_load_check = XdrvMailbox.payload; - CPU_last_millis = CPU_last_loop_time; - } - Response_P(S_JSON_COMMAND_NVALUE, command, CPU_load_check); - } - else if (CMND_FREEMEM == command_code) { - if (XdrvMailbox.data_len > 0) { - CPU_show_freemem = XdrvMailbox.payload; - } - Response_P(S_JSON_COMMAND_NVALUE, command, CPU_show_freemem); - } - else if ((CMND_SETSENSOR == command_code) && (XdrvMailbox.index < MAX_XSNS_DRIVERS)) { - if ((XdrvMailbox.payload >= 0) && XsnsPresent(XdrvMailbox.index)) { - bitWrite(Settings.sensors[XdrvMailbox.index / 32], XdrvMailbox.index % 32, XdrvMailbox.payload &1); - if (1 == XdrvMailbox.payload) { restart_flag = 2; } // To safely re-enable a sensor currently most sensor need to follow complete restart init cycle - } - Response_P(S_JSON_COMMAND_XVALUE, command, XsnsGetSensors().c_str()); - } - else if (CMND_FLASHMODE == command_code) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) { - SetFlashMode(XdrvMailbox.payload); - } - Response_P(S_JSON_COMMAND_NVALUE, command, ESP.getFlashChipMode()); - } - else serviced = false; // Unknown command - return serviced; +#ifdef USE_WEBSERVER +void CmndCfgXor(void) +{ + if (XdrvMailbox.data_len > 0) { + Web.config_xor_on_set = XdrvMailbox.payload; + } + ResponseCmndNumber(Web.config_xor_on_set); +} +#endif // USE_WEBSERVER + +#ifdef DEBUG_THEO +void CmndException(void) +{ + if (XdrvMailbox.data_len > 0) { ExceptionTest(XdrvMailbox.payload); } + ResponseCmndDone(); +} +#endif // DEBUG_THEO + +void CmndCpuCheck(void) +{ + if (XdrvMailbox.data_len > 0) { + CPU_load_check = XdrvMailbox.payload; + CPU_last_millis = CPU_last_loop_time; + } + ResponseCmndNumber(CPU_load_check); +} + +void CmndFreemem(void) +{ + if (XdrvMailbox.data_len > 0) { + CPU_show_freemem = XdrvMailbox.payload; + } + ResponseCmndNumber(CPU_show_freemem); +} + +void CmndSetSensor(void) +{ + if (XdrvMailbox.index < MAX_XSNS_DRIVERS) { + if (XdrvMailbox.payload >= 0) { + bitWrite(Settings.sensors[XdrvMailbox.index / 32], XdrvMailbox.index % 32, XdrvMailbox.payload &1); + if (1 == XdrvMailbox.payload) { + restart_flag = 2; // To safely re-enable a sensor currently most sensor need to follow complete restart init cycle + } + } + Response_P(PSTR("{\"" D_CMND_SETSENSOR "\":")); + XsnsSensorState(); + ResponseJsonEnd(); + } +} + +void CmndFlashMode(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) { + SetFlashMode(XdrvMailbox.payload); + } + ResponseCmndNumber(ESP.getFlashChipMode()); +} + +uint32_t DebugSwap32(uint32_t x) { + return ((x << 24) & 0xff000000 ) | + ((x << 8) & 0x00ff0000 ) | + ((x >> 8) & 0x0000ff00 ) | + ((x >> 24) & 0x000000ff ); +} + +void CmndFlashDump(void) +{ + // FlashDump + // FlashDump 0xFF000 + // FlashDump 0xFC000 10 + const uint32_t flash_start = 0x40200000; // Start address flash + const uint8_t bytes_per_cols = 0x20; + const uint32_t max = (SPIFFS_END + 5) * SPI_FLASH_SEC_SIZE; // 0x100000 for 1M flash, 0x400000 for 4M flash + + uint32_t start = flash_start; + uint32_t rows = 8; + + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= (max - bytes_per_cols))) { + start += (XdrvMailbox.payload &0x7FFFFFFC); // Fix exception as flash access is only allowed on 4 byte boundary + + char *p; + uint32_t is_payload = strtol(XdrvMailbox.data, &p, 16); + rows = strtol(p, &p, 10); + if (0 == rows) { rows = 8; } + } + uint32_t end = start + (rows * bytes_per_cols); + if ((end - flash_start) > max) { + end = flash_start + max; + } + + for (uint32_t pos = start; pos < end; pos += bytes_per_cols) { + uint32_t* values = (uint32_t*)(pos); + AddLog_P2(LOG_LEVEL_INFO, PSTR("%06X: %08X %08X %08X %08X %08X %08X %08X %08X"), pos - flash_start, + DebugSwap32(values[0]), DebugSwap32(values[1]), DebugSwap32(values[2]), DebugSwap32(values[3]), + DebugSwap32(values[4]), DebugSwap32(values[5]), DebugSwap32(values[6]), DebugSwap32(values[7])); + } + ResponseCmndDone(); +} + +void CmndI2cWrite(void) +{ + // I2cWrite
,.. + if (i2c_flg) { + char* parms = XdrvMailbox.data; + uint8_t buffer[100]; + uint32_t index = 0; + + char *p; + char *data = strtok_r(parms, " ,", &p); + while (data != NULL && index < sizeof(buffer)) { + buffer[index++] = strtol(data, nullptr, 16); + data = strtok_r(nullptr, " ,", &p); + } + + if (index > 1) { + AddLogBuffer(LOG_LEVEL_INFO, buffer, index); + + Wire.beginTransmission(buffer[0]); + for (uint32_t i = 1; i < index; i++) { + Wire.write(buffer[i]); + } + int result = Wire.endTransmission(); + AddLog_P2(LOG_LEVEL_INFO, PSTR("I2C: Result %d"), result); + } + } + ResponseCmndDone(); +} + +void CmndI2cRead(void) +{ + // I2cRead
, + if (i2c_flg) { + char* parms = XdrvMailbox.data; + uint8_t buffer[100]; + uint32_t index = 0; + + char *p; + char *data = strtok_r(parms, " ,", &p); + while (data != NULL && index < sizeof(buffer)) { + buffer[index++] = strtol(data, nullptr, 16); + data = strtok_r(nullptr, " ,", &p); + } + + if (index > 0) { + uint8_t size = 1; + if (index > 1) { + size = buffer[1]; + } + Wire.requestFrom(buffer[0], size); + index = 0; + while (Wire.available() && index < sizeof(buffer)) { + buffer[index++] = Wire.read(); + } + if (index > 0) { + AddLogBuffer(LOG_LEVEL_INFO, buffer, index); + } + } + } + ResponseCmndDone(); +} + +void CmndI2cStretch(void) +{ + if (i2c_flg && (XdrvMailbox.payload > 0)) { + Wire.setClockStretchLimit(XdrvMailbox.payload); + } + ResponseCmndDone(); +} + +void CmndI2cClock(void) +{ + if (i2c_flg && (XdrvMailbox.payload > 0)) { + Wire.setClock(XdrvMailbox.payload); + } + ResponseCmndDone(); } /*********************************************************************************************\ @@ -500,14 +665,14 @@ bool Xdrv99(uint8_t function) case FUNC_LOOP: CpuLoadLoop(); break; + case FUNC_FREE_MEM: + if (CPU_show_freemem) { DebugFreeMem(); } + break; case FUNC_PRE_INIT: CPU_last_millis = millis(); break; case FUNC_COMMAND: - result = DebugCommand(); - break; - case FUNC_FREE_MEM: - if (CPU_show_freemem) { DebugFreeMem(); } + result = DecodeCommand(kDebugCommands, DebugCommand); break; } return result; diff --git a/sonoff/xdrv_interface.ino b/sonoff/xdrv_interface.ino index 7dddf1353..ead58e613 100644 --- a/sonoff/xdrv_interface.ino +++ b/sonoff/xdrv_interface.ino @@ -151,7 +151,237 @@ bool (* const xdrv_func_ptr[])(uint8_t) = { // Driver Function Pointers &Xdrv32, #endif -// Optional user defined drivers in range 91 - 99 +#ifdef XDRV_33 + &Xdrv33, +#endif + +#ifdef XDRV_34 + &Xdrv34, +#endif + +#ifdef XDRV_35 + &Xdrv35, +#endif + +#ifdef XDRV_36 + &Xdrv36, +#endif + +#ifdef XDRV_37 + &Xdrv37, +#endif + +#ifdef XDRV_38 + &Xdrv38, +#endif + +#ifdef XDRV_39 + &Xdrv39, +#endif + +#ifdef XDRV_40 + &Xdrv40, +#endif + +#ifdef XDRV_41 + &Xdrv41, +#endif + +#ifdef XDRV_42 + &Xdrv42, +#endif + +#ifdef XDRV_43 + &Xdrv43, +#endif + +#ifdef XDRV_44 + &Xdrv44, +#endif + +#ifdef XDRV_45 + &Xdrv45, +#endif + +#ifdef XDRV_46 + &Xdrv46, +#endif + +#ifdef XDRV_47 + &Xdrv47, +#endif + +#ifdef XDRV_48 + &Xdrv48, +#endif + +#ifdef XDRV_49 + &Xdrv49, +#endif + +#ifdef XDRV_50 + &Xdrv50, +#endif + +#ifdef XDRV_51 + &Xdrv51, +#endif + +#ifdef XDRV_52 + &Xdrv52, +#endif + +#ifdef XDRV_53 + &Xdrv53, +#endif + +#ifdef XDRV_54 + &Xdrv54, +#endif + +#ifdef XDRV_55 + &Xdrv55, +#endif + +#ifdef XDRV_56 + &Xdrv56, +#endif + +#ifdef XDRV_57 + &Xdrv57, +#endif + +#ifdef XDRV_58 + &Xdrv58, +#endif + +#ifdef XDRV_59 + &Xdrv59, +#endif + +#ifdef XDRV_60 + &Xdrv60, +#endif + +#ifdef XDRV_61 + &Xdrv61, +#endif + +#ifdef XDRV_62 + &Xdrv62, +#endif + +#ifdef XDRV_63 + &Xdrv63, +#endif + +#ifdef XDRV_64 + &Xdrv64, +#endif + +#ifdef XDRV_65 + &Xdrv65, +#endif + +#ifdef XDRV_66 + &Xdrv66, +#endif + +#ifdef XDRV_67 + &Xdrv67, +#endif + +#ifdef XDRV_68 + &Xdrv68, +#endif + +#ifdef XDRV_69 + &Xdrv69, +#endif + +#ifdef XDRV_70 + &Xdrv70, +#endif + +#ifdef XDRV_71 + &Xdrv71, +#endif + +#ifdef XDRV_72 + &Xdrv72, +#endif + +#ifdef XDRV_73 + &Xdrv73, +#endif + +#ifdef XDRV_74 + &Xdrv74, +#endif + +#ifdef XDRV_75 + &Xdrv75, +#endif + +#ifdef XDRV_76 + &Xdrv76, +#endif + +#ifdef XDRV_77 + &Xdrv77, +#endif + +#ifdef XDRV_78 + &Xdrv78, +#endif + +#ifdef XDRV_79 + &Xdrv79, +#endif + +#ifdef XDRV_80 + &Xdrv80, +#endif + +#ifdef XDRV_81 + &Xdrv81, +#endif + +#ifdef XDRV_82 + &Xdrv82, +#endif + +#ifdef XDRV_83 + &Xdrv83, +#endif + +#ifdef XDRV_84 + &Xdrv84, +#endif + +#ifdef XDRV_85 + &Xdrv85, +#endif + +#ifdef XDRV_86 + &Xdrv86, +#endif + +#ifdef XDRV_87 + &Xdrv87, +#endif + +#ifdef XDRV_88 + &Xdrv88, +#endif + +#ifdef XDRV_89 + &Xdrv89, +#endif + +#ifdef XDRV_90 + &Xdrv90, +#endif #ifdef XDRV_91 &Xdrv91, @@ -192,19 +422,434 @@ bool (* const xdrv_func_ptr[])(uint8_t) = { // Driver Function Pointers const uint8_t xdrv_present = sizeof(xdrv_func_ptr) / sizeof(xdrv_func_ptr[0]); // Number of drivers found -bool XdrvMqttData(char *topicBuf, uint16_t stopicBuf, char *dataBuf, uint16_t sdataBuf) -{ - XdrvMailbox.index = stopicBuf; - XdrvMailbox.data_len = sdataBuf; - XdrvMailbox.topic = topicBuf; - XdrvMailbox.data = dataBuf; +/*********************************************************************************************\ + * Xdrv available list +\*********************************************************************************************/ - return XdrvCall(FUNC_MQTT_DATA); +#ifdef XFUNC_PTR_IN_ROM +const uint8_t kXdrvList[] PROGMEM = { +#else +const uint8_t kXdrvList[] = { +#endif + +#ifdef XDRV_01 + XDRV_01, +#endif + +#ifdef XDRV_02 + XDRV_02, +#endif + +#ifdef XDRV_03 + XDRV_03, +#endif + +#ifdef XDRV_04 + XDRV_04, +#endif + +#ifdef XDRV_05 + XDRV_05, +#endif + +#ifdef XDRV_06 + XDRV_06, +#endif + +#ifdef XDRV_07 + XDRV_07, +#endif + +#ifdef XDRV_08 + XDRV_08, +#endif + +#ifdef XDRV_09 + XDRV_09, +#endif + +#ifdef XDRV_10 + XDRV_10, +#endif + +#ifdef XDRV_11 + XDRV_11, +#endif + +#ifdef XDRV_12 + XDRV_12, +#endif + +#ifdef XDRV_13 + XDRV_13, +#endif + +#ifdef XDRV_14 + XDRV_14, +#endif + +#ifdef XDRV_15 + XDRV_15, +#endif + +#ifdef XDRV_16 + XDRV_16, +#endif + +#ifdef XDRV_17 + XDRV_17, +#endif + +#ifdef XDRV_18 + XDRV_18, +#endif + +#ifdef XDRV_19 + XDRV_19, +#endif + +#ifdef XDRV_20 + XDRV_20, +#endif + +#ifdef XDRV_21 + XDRV_21, +#endif + +#ifdef XDRV_22 + XDRV_22, +#endif + +#ifdef XDRV_23 + XDRV_23, +#endif + +#ifdef XDRV_24 + XDRV_24, +#endif + +#ifdef XDRV_25 + XDRV_25, +#endif + +#ifdef XDRV_26 + XDRV_26, +#endif + +#ifdef XDRV_27 + XDRV_27, +#endif + +#ifdef XDRV_28 + XDRV_28, +#endif + +#ifdef XDRV_29 + XDRV_29, +#endif + +#ifdef XDRV_30 + XDRV_30, +#endif + +#ifdef XDRV_31 + XDRV_31, +#endif + +#ifdef XDRV_32 + XDRV_32, +#endif + +#ifdef XDRV_33 + XDRV_33, +#endif + +#ifdef XDRV_34 + XDRV_34, +#endif + +#ifdef XDRV_35 + XDRV_35, +#endif + +#ifdef XDRV_36 + XDRV_36, +#endif + +#ifdef XDRV_37 + XDRV_37, +#endif + +#ifdef XDRV_38 + XDRV_38, +#endif + +#ifdef XDRV_39 + XDRV_39, +#endif + +#ifdef XDRV_40 + XDRV_40, +#endif + +#ifdef XDRV_41 + XDRV_41, +#endif + +#ifdef XDRV_42 + XDRV_42, +#endif + +#ifdef XDRV_43 + XDRV_43, +#endif + +#ifdef XDRV_44 + XDRV_44, +#endif + +#ifdef XDRV_45 + XDRV_45, +#endif + +#ifdef XDRV_46 + XDRV_46, +#endif + +#ifdef XDRV_47 + XDRV_47, +#endif + +#ifdef XDRV_48 + XDRV_48, +#endif + +#ifdef XDRV_49 + XDRV_49, +#endif + +#ifdef XDRV_50 + XDRV_50, +#endif + +#ifdef XDRV_51 + XDRV_51, +#endif + +#ifdef XDRV_52 + XDRV_52, +#endif + +#ifdef XDRV_53 + XDRV_53, +#endif + +#ifdef XDRV_54 + XDRV_54, +#endif + +#ifdef XDRV_55 + XDRV_55, +#endif + +#ifdef XDRV_56 + XDRV_56, +#endif + +#ifdef XDRV_57 + XDRV_57, +#endif + +#ifdef XDRV_58 + XDRV_58, +#endif + +#ifdef XDRV_59 + XDRV_59, +#endif + +#ifdef XDRV_60 + XDRV_60, +#endif + +#ifdef XDRV_61 + XDRV_61, +#endif + +#ifdef XDRV_62 + XDRV_62, +#endif + +#ifdef XDRV_63 + XDRV_63, +#endif + +#ifdef XDRV_64 + XDRV_64, +#endif + +#ifdef XDRV_65 + XDRV_65, +#endif + +#ifdef XDRV_66 + XDRV_66, +#endif + +#ifdef XDRV_67 + XDRV_67, +#endif + +#ifdef XDRV_68 + XDRV_68, +#endif + +#ifdef XDRV_69 + XDRV_69, +#endif + +#ifdef XDRV_70 + XDRV_70, +#endif + +#ifdef XDRV_71 + XDRV_71, +#endif + +#ifdef XDRV_72 + XDRV_72, +#endif + +#ifdef XDRV_73 + XDRV_73, +#endif + +#ifdef XDRV_74 + XDRV_74, +#endif + +#ifdef XDRV_75 + XDRV_75, +#endif + +#ifdef XDRV_76 + XDRV_76, +#endif + +#ifdef XDRV_77 + XDRV_77, +#endif + +#ifdef XDRV_78 + XDRV_78, +#endif + +#ifdef XDRV_79 + XDRV_79, +#endif + +#ifdef XDRV_80 + XDRV_80, +#endif + +#ifdef XDRV_81 + XDRV_81, +#endif + +#ifdef XDRV_82 + XDRV_82, +#endif + +#ifdef XDRV_83 + XDRV_83, +#endif + +#ifdef XDRV_84 + XDRV_84, +#endif + +#ifdef XDRV_85 + XDRV_85, +#endif + +#ifdef XDRV_86 + XDRV_86, +#endif + +#ifdef XDRV_87 + XDRV_87, +#endif + +#ifdef XDRV_88 + XDRV_88, +#endif + +#ifdef XDRV_89 + XDRV_89, +#endif + +#ifdef XDRV_90 + XDRV_90, +#endif + +#ifdef XDRV_91 + XDRV_91, +#endif + +#ifdef XDRV_92 + XDRV_92, +#endif + +#ifdef XDRV_93 + XDRV_93, +#endif + +#ifdef XDRV_94 + XDRV_94, +#endif + +#ifdef XDRV_95 + XDRV_95, +#endif + +#ifdef XDRV_96 + XDRV_96, +#endif + +#ifdef XDRV_97 + XDRV_97, +#endif + +#ifdef XDRV_98 + XDRV_98, +#endif + +#ifdef XDRV_99 + XDRV_99 +#endif +}; + +/*********************************************************************************************/ + +void XsnsDriverState(void) +{ + ResponseAppend_P(PSTR(",\"Drivers\":\"")); // Use string for future enable/disable signal + for (uint32_t i = 0; i < sizeof(kXdrvList); i++) { +#ifdef XFUNC_PTR_IN_ROM + uint32_t driverid = pgm_read_byte(kXdrvList + i); +#else + uint32_t driverid = kXdrvList[i]; +#endif + ResponseAppend_P(PSTR("%s%d"), (i) ? "," : "", driverid); + } + ResponseAppend_P(PSTR("\"")); } +/*********************************************************************************************/ + bool XdrvRulesProcess(void) { - return XdrvCall(FUNC_RULES_PROCESS); + return XdrvCallDriver(10, FUNC_RULES_PROCESS); } #ifdef USE_DEBUG_DRIVER @@ -217,6 +862,25 @@ void ShowFreeMem(const char *where) } #endif +/*********************************************************************************************\ + * Function call to single xdrv +\*********************************************************************************************/ + +bool XdrvCallDriver(uint32_t driver, uint8_t Function) +{ + for (uint32_t x = 0; x < xdrv_present; x++) { +#ifdef XFUNC_PTR_IN_ROM + uint32_t listed = pgm_read_byte(kXdrvList + x); +#else + uint32_t listed = kXdrvList[x]; +#endif + if (driver == listed) { + return xdrv_func_ptr[x](Function); + } + } + return false; +} + /*********************************************************************************************\ * Function call to all xdrv \*********************************************************************************************/ @@ -226,7 +890,6 @@ bool XdrvCall(uint8_t Function) bool result = false; for (uint32_t x = 0; x < xdrv_present; x++) { -// WifiAddDelayWhenDisconnected(); result = xdrv_func_ptr[x](Function); if (result && ((FUNC_COMMAND == Function) || @@ -237,6 +900,7 @@ bool XdrvCall(uint8_t Function) (FUNC_SERIAL == Function) || (FUNC_MODULE_INIT == Function) || (FUNC_SET_CHANNELS == Function) || + (FUNC_PIN_STATE == Function) || (FUNC_SET_DEVICE_POWER == Function) )) { break; diff --git a/sonoff/xdsp_01_lcd.ino b/sonoff/xdsp_01_lcd.ino index 2c75c040c..8ab867f48 100644 --- a/sonoff/xdsp_01_lcd.ino +++ b/sonoff/xdsp_01_lcd.ino @@ -68,6 +68,8 @@ void LcdInitDriver(void) } if (XDSP_01 == Settings.display_model) { + Settings.display_width = Settings.display_cols[0]; + Settings.display_height = Settings.display_rows; lcd = new LiquidCrystal_I2C(Settings.display_address[0], Settings.display_cols[0], Settings.display_rows); #ifdef USE_DISPLAY_MODES1TO5 diff --git a/sonoff/xdsp_02_ssd1306.ino b/sonoff/xdsp_02_ssd1306.ino index 62047347f..04251b595 100644 --- a/sonoff/xdsp_02_ssd1306.ino +++ b/sonoff/xdsp_02_ssd1306.ino @@ -1,5 +1,5 @@ /* - xdsp_02_ssd1306.ino - Display Oled ssd1306 support for Sonoff-Tasmota + xdsp_02_ssd1306.ino - Display Oled SSD1306 support for Sonoff-Tasmota Copyright (C) 2019 Theo Arends and Adafruit @@ -23,56 +23,30 @@ #define XDSP_02 2 +#define OLED_RESET 4 + +#define SPRINT(A) char str[32];sprintf(str,"val: %d ",A);Serial.println((char*)str); + #define OLED_ADDRESS1 0x3C // Oled 128x32 I2C address #define OLED_ADDRESS2 0x3D // Oled 128x64 I2C address +#define OLED_BUFFER_COLS 40 // Max number of columns in display shadow buffer +#define OLED_BUFFER_ROWS 16 // Max number of lines in display shadow buffer + #define OLED_FONT_WIDTH 6 #define OLED_FONT_HEIGTH 8 -//#define OLED_BUFFER_COLS 21 or 11 // Max number of columns in display shadow buffer -//#define OLED_BUFFER_ROWS 8 or 16 // Max number of lines in display shadow buffer - #include -#include +#include #include -Adafruit_SSD1306 *oled; +Adafruit_SSD1306 *oled1306; -uint8_t ssd1306_font_x = OLED_FONT_WIDTH; -uint8_t ssd1306_font_y = OLED_FONT_HEIGTH; +extern uint8_t *buffer; /*********************************************************************************************/ -void Ssd1306InitMode(void) -{ - oled->setRotation(Settings.display_rotate); // 0 - oled->invertDisplay(false); - oled->clearDisplay(); - oled->setTextWrap(false); // Allow text to run off edges - oled->cp437(true); - - oled->setTextSize(Settings.display_size); - oled->setTextColor(WHITE); - oled->setCursor(0,0); - oled->display(); -} - -void Ssd1306Init(uint8_t mode) -{ - switch(mode) { - case DISPLAY_INIT_MODE: - Ssd1306InitMode(); -#ifdef USE_DISPLAY_MODES1TO5 - DisplayClearScreenBuffer(); -#endif // USE_DISPLAY_MODES1TO5 - break; - case DISPLAY_INIT_PARTIAL: - case DISPLAY_INIT_FULL: - break; - } -} - -void Ssd1306InitDriver(void) +void SSD1306InitDriver() { if (!Settings.display_model) { if (I2cDevice(OLED_ADDRESS1)) { @@ -86,51 +60,45 @@ void Ssd1306InitDriver(void) } if (XDSP_02 == Settings.display_model) { - oled = new Adafruit_SSD1306(); - oled->begin(SSD1306_SWITCHCAPVCC, Settings.display_address[0]); -#ifdef USE_DISPLAY_MODES1TO5 - DisplayAllocScreenBuffer(); -#endif // USE_DISPLAY_MODES1TO5 + if ((Settings.display_width != 96) && (Settings.display_width != 128)) { + Settings.display_width = 128; + } + if ((Settings.display_height != 16) && (Settings.display_height != 32) && (Settings.display_height != 64)) { + Settings.display_height = 64; + } + + uint8_t reset_pin = -1; + if (pin[GPIO_OLED_RESET] < 99) { + reset_pin = pin[GPIO_OLED_RESET]; + } + + // allocate screen buffer + if (buffer) { free(buffer); } + buffer = (unsigned char*)calloc((Settings.display_width * Settings.display_height) / 8,1); + if (!buffer) { return; } + + // init renderer + // oled1306 = new Adafruit_SSD1306(SSD1306_LCDWIDTH,SSD1306_LCDHEIGHT); + oled1306 = new Adafruit_SSD1306(Settings.display_width, Settings.display_height, &Wire, reset_pin); + oled1306->begin(SSD1306_SWITCHCAPVCC, Settings.display_address[0], 0); + renderer = oled1306; + renderer->DisplayInit(DISPLAY_INIT_MODE, Settings.display_size, Settings.display_rotate, Settings.display_font); + renderer->setTextColor(1,0); + +#ifdef SHOW_SPLASH + renderer->setTextFont(0); + renderer->setTextSize(2); + renderer->setCursor(20,20); + renderer->println(F("SSD1306")); + renderer->Updateframe(); + renderer->DisplayOnff(1); +#endif - Ssd1306InitMode(); } } -void Ssd1306Clear(void) -{ - oled->clearDisplay(); - oled->setCursor(0, 0); - oled->display(); -} - -void Ssd1306DrawStringAt(uint16_t x, uint16_t y, char *str, uint16_t color, uint8_t flag) -{ - if (!flag) { - oled->setCursor(x, y); - } else { - oled->setCursor((x-1) * ssd1306_font_x * Settings.display_size, (y-1) * ssd1306_font_y * Settings.display_size); - } - oled->println(str); -} - -void Ssd1306DisplayOnOff(uint8_t on) -{ - if (on) { - oled->ssd1306_command(SSD1306_DISPLAYON); - } else { - oled->ssd1306_command(SSD1306_DISPLAYOFF); - } -} - -void Ssd1306OnOff(void) -{ - Ssd1306DisplayOnOff(disp_power); - oled->display(); -} - /*********************************************************************************************/ - #ifdef USE_DISPLAY_MODES1TO5 void Ssd1306PrintLog(void) @@ -141,23 +109,24 @@ void Ssd1306PrintLog(void) if (!disp_screen_buffer_cols) { DisplayAllocScreenBuffer(); } char* txt = DisplayLogBuffer('\370'); - if (txt != nullptr) { + if (txt != NULL) { uint8_t last_row = Settings.display_rows -1; - oled->clearDisplay(); - oled->setTextSize(Settings.display_size); - oled->setCursor(0,0); - for (uint32_t i = 0; i < last_row; i++) { + renderer->clearDisplay(); + renderer->setTextSize(Settings.display_size); + renderer->setCursor(0,0); + for (byte i = 0; i < last_row; i++) { strlcpy(disp_screen_buffer[i], disp_screen_buffer[i +1], disp_screen_buffer_cols); - oled->println(disp_screen_buffer[i]); + renderer->println(disp_screen_buffer[i]); } strlcpy(disp_screen_buffer[last_row], txt, disp_screen_buffer_cols); DisplayFillScreen(last_row); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "[%s]"), disp_screen_buffer[last_row]); + snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_DEBUG "[%s]"), disp_screen_buffer[last_row]); + AddLog(LOG_LEVEL_DEBUG); - oled->println(disp_screen_buffer[last_row]); - oled->display(); + renderer->println(disp_screen_buffer[last_row]); + renderer->Updateframe(); } } } @@ -166,18 +135,21 @@ void Ssd1306Time(void) { char line[12]; - oled->clearDisplay(); - oled->setTextSize(2); - oled->setCursor(0, 0); + renderer->clearDisplay(); + renderer->setTextSize(Settings.display_size); + renderer->setTextFont(Settings.display_font); + renderer->setCursor(0, 0); snprintf_P(line, sizeof(line), PSTR(" %02d" D_HOUR_MINUTE_SEPARATOR "%02d" D_MINUTE_SECOND_SEPARATOR "%02d"), RtcTime.hour, RtcTime.minute, RtcTime.second); // [ 12:34:56 ] - oled->println(line); + renderer->println(line); snprintf_P(line, sizeof(line), PSTR("%02d" D_MONTH_DAY_SEPARATOR "%02d" D_YEAR_MONTH_SEPARATOR "%04d"), RtcTime.day_of_month, RtcTime.month, RtcTime.year); // [01-02-2018] - oled->println(line); - oled->display(); + renderer->println(line); + renderer->Updateframe(); } void Ssd1306Refresh(void) // Every second { + if (!renderer) return; + if (Settings.display_mode) { // Mode 0 is User text switch (Settings.display_mode) { case 1: // Time @@ -199,75 +171,24 @@ void Ssd1306Refresh(void) // Every second * Interface \*********************************************************************************************/ -bool Xdsp02(uint8_t function) +bool Xdsp02(byte function) { bool result = false; if (i2c_flg) { if (FUNC_DISPLAY_INIT_DRIVER == function) { - Ssd1306InitDriver(); + SSD1306InitDriver(); } else if (XDSP_02 == Settings.display_model) { - - if (!dsp_color) { dsp_color = WHITE; } - switch (function) { - case FUNC_DISPLAY_MODEL: - result = true; - break; - case FUNC_DISPLAY_INIT: - Ssd1306Init(dsp_init); - break; - case FUNC_DISPLAY_POWER: - Ssd1306OnOff(); - break; - case FUNC_DISPLAY_CLEAR: - Ssd1306Clear(); - break; - case FUNC_DISPLAY_DRAW_HLINE: - oled->writeFastHLine(dsp_x, dsp_y, dsp_len, dsp_color); - break; - case FUNC_DISPLAY_DRAW_VLINE: - oled->writeFastVLine(dsp_x, dsp_y, dsp_len, dsp_color); - break; - case FUNC_DISPLAY_DRAW_LINE: - oled->writeLine(dsp_x, dsp_y, dsp_x2, dsp_y2, dsp_color); - break; - case FUNC_DISPLAY_DRAW_CIRCLE: - oled->drawCircle(dsp_x, dsp_y, dsp_rad, dsp_color); - break; - case FUNC_DISPLAY_FILL_CIRCLE: - oled->fillCircle(dsp_x, dsp_y, dsp_rad, dsp_color); - break; - case FUNC_DISPLAY_DRAW_RECTANGLE: - oled->drawRect(dsp_x, dsp_y, dsp_x2, dsp_y2, dsp_color); - break; - case FUNC_DISPLAY_FILL_RECTANGLE: - oled->fillRect(dsp_x, dsp_y, dsp_x2, dsp_y2, dsp_color); - break; - case FUNC_DISPLAY_DRAW_FRAME: - oled->display(); - break; - case FUNC_DISPLAY_TEXT_SIZE: - oled->setTextSize(Settings.display_size); - break; - case FUNC_DISPLAY_FONT_SIZE: -// oled->setTextSize(Settings.display_font); - break; - case FUNC_DISPLAY_DRAW_STRING: - Ssd1306DrawStringAt(dsp_x, dsp_y, dsp_str, dsp_color, dsp_flag); - break; - case FUNC_DISPLAY_ONOFF: - Ssd1306DisplayOnOff(dsp_on); - break; - case FUNC_DISPLAY_ROTATION: - oled->setRotation(Settings.display_rotate); - break; #ifdef USE_DISPLAY_MODES1TO5 case FUNC_DISPLAY_EVERY_SECOND: Ssd1306Refresh(); break; #endif // USE_DISPLAY_MODES1TO5 + case FUNC_DISPLAY_MODEL: + result = true; + break; } } } diff --git a/sonoff/xdsp_03_matrix.ino b/sonoff/xdsp_03_matrix.ino index a86d5cfac..6635fb606 100644 --- a/sonoff/xdsp_03_matrix.ino +++ b/sonoff/xdsp_03_matrix.ino @@ -214,6 +214,9 @@ void MatrixInitDriver(void) } } + Settings.display_width = mtx_matrices * 8; + Settings.display_height = 8; + MatrixInitMode(); } } diff --git a/sonoff/xdsp_04_ili9341.ino b/sonoff/xdsp_04_ili9341.ino index f98866d5e..8871e6586 100644 --- a/sonoff/xdsp_04_ili9341.ino +++ b/sonoff/xdsp_04_ili9341.ino @@ -84,6 +84,12 @@ void Ili9341InitDriver(void) } if (XDSP_04 == Settings.display_model) { + if (Settings.display_width != ILI9341_TFTWIDTH) { + Settings.display_width = ILI9341_TFTWIDTH; + } + if (Settings.display_height != ILI9341_TFTHEIGHT) { + Settings.display_height = ILI9341_TFTHEIGHT; + } tft = new Adafruit_ILI9341(pin[GPIO_SPI_CS], pin[GPIO_SPI_DC]); tft->begin(); diff --git a/sonoff/xdsp_05_epaper_29.ino b/sonoff/xdsp_05_epaper_29.ino index f972848f3..8d97bd123 100644 --- a/sonoff/xdsp_05_epaper_29.ino +++ b/sonoff/xdsp_05_epaper_29.ino @@ -1,5 +1,5 @@ /* - xdsp_05_epaper_29.ino - 2.9 Inch display e-paper support for Sonoff-Tasmota + xdsp_05_epaper.ino - Display e-paper support for Sonoff-Tasmota Copyright (C) 2019 Theo Arends, Gerhard Mutz and Waveshare @@ -26,8 +26,8 @@ #define EPD_TOP 12 #define EPD_FONT_HEIGTH 12 -#define COLORED 0 -#define UNCOLORED 1 +#define COLORED 1 +#define UNCOLORED 0 // using font 8 is opional (num=3) // very badly readable, but may be useful for graphs @@ -36,187 +36,104 @@ #include #include -unsigned char image[(EPD_HEIGHT * EPD_WIDTH) / 8]; - -Paint paint(image, EPD_WIDTH, EPD_HEIGHT); // width should be the multiple of 8 -Epd epd; -sFONT *selected_font; - +//unsigned char image[(EPD_HEIGHT * EPD_WIDTH) / 8]; +extern uint8_t *buffer; uint16_t epd_scroll; +Epd *epd; + /*********************************************************************************************/ -void EpdInitMode(void) -{ - // whiten display with full update - epd.Init(lut_full_update); - - epd.ClearFrameMemory(0xFF); // bit set = white, bit reset = black - epd.DisplayFrame(); - delay(3000); - - // switch to partial update - epd.Init(lut_partial_update); - - // Clear image memory - epd.ClearFrameMemory(0xFF); // bit set = white, bit reset = black - epd.DisplayFrame(); - delay(500); - - selected_font = &Font12; - - paint.SetRotate(Settings.display_rotate); -/* - // Welcome text - paint.Clear(UNCOLORED); - paint.DrawStringAt(50, 50, "Waveshare E-Paper Display!", selected_font, COLORED); - epd.SetFrameMemory(paint.GetImage(), 0, 0, paint.GetWidth(), paint.GetHeight()); - epd.DisplayFrame(); - delay(1000); -*/ - paint.Clear(UNCOLORED); - - epd_scroll = EPD_TOP; -} - -void EpdInitPartial(void) -{ - epd.Init(lut_partial_update); - //paint.Clear(UNCOLORED); - epd.DisplayFrame(); - delay(500); -} - -void EpdInitFull(void) -{ - epd.Init(lut_full_update); - //paint.Clear(UNCOLORED); - //epd.ClearFrameMemory(0xFF); - epd.DisplayFrame(); - delay(3000); -} - -void EpdInit(uint8_t mode) -{ - switch(mode) { - case DISPLAY_INIT_MODE: - EpdInitMode(); - break; - case DISPLAY_INIT_PARTIAL: - EpdInitPartial(); - break; - case DISPLAY_INIT_FULL: - EpdInitFull(); - break; - } -} - -void EpdInitDriver(void) +void EpdInitDriver29() { if (!Settings.display_model) { Settings.display_model = XDSP_05; } if (XDSP_05 == Settings.display_model) { + if (Settings.display_width != EPD_WIDTH) { + Settings.display_width = EPD_WIDTH; + } + if (Settings.display_height != EPD_HEIGHT) { + Settings.display_height = EPD_HEIGHT; + } + + // allocate screen buffer + if (buffer) free(buffer); + buffer=(unsigned char*)calloc((EPD_WIDTH * EPD_HEIGHT) / 8,1); + if (!buffer) return; + + // init renderer + epd = new Epd(EPD_WIDTH,EPD_HEIGHT); + + // whiten display with full update, takes 3 seconds if ((pin[GPIO_SPI_CS] < 99) && (pin[GPIO_SPI_CLK] < 99) && (pin[GPIO_SPI_MOSI] < 99)) { - epd.cs_pin = pin[GPIO_SPI_CS]; - epd.sclk_pin = pin[GPIO_SPI_CLK]; // 14 - epd.mosi_pin = pin[GPIO_SPI_MOSI]; // 13 - EpdInitMode(); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EPD: HardSPI CS %d, CLK %d, MOSI %d"), epd.cs_pin, epd.sclk_pin, epd.mosi_pin); + epd->Begin(pin[GPIO_SPI_CS],pin[GPIO_SPI_MOSI],pin[GPIO_SPI_CLK]); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EPD: HardSPI CS %d, CLK %d, MOSI %d"),pin[GPIO_SPI_CS], pin[GPIO_SPI_CLK], pin[GPIO_SPI_MOSI]); } else if ((pin[GPIO_SSPI_CS] < 99) && (pin[GPIO_SSPI_SCLK] < 99) && (pin[GPIO_SSPI_MOSI] < 99)) { - epd.cs_pin = pin[GPIO_SSPI_CS]; - epd.sclk_pin = pin[GPIO_SSPI_SCLK]; - epd.mosi_pin = pin[GPIO_SSPI_MOSI]; - EpdInitMode(); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EPD: SoftSPI CS %d, CLK %d, MOSI %d"), epd.cs_pin, epd.sclk_pin, epd.mosi_pin); + epd->Begin(pin[GPIO_SSPI_CS],pin[GPIO_SSPI_MOSI],pin[GPIO_SSPI_SCLK]); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EPD: SoftSPI CS %d, CLK %d, MOSI %d"),pin[GPIO_SSPI_CS], pin[GPIO_SSPI_SCLK], pin[GPIO_SSPI_MOSI]); + } else { + free(buffer); + return; } + + renderer = epd; + epd->Init(DISPLAY_INIT_FULL); + epd->Init(DISPLAY_INIT_PARTIAL); + renderer->DisplayInit(DISPLAY_INIT_MODE,Settings.display_size,Settings.display_rotate,Settings.display_font); + + renderer->setTextColor(1,0); + +#ifdef SHOW_SPLASH + // Welcome text + renderer->setTextFont(1); + renderer->DrawStringAt(50, 50, "Waveshare E-Paper Display!", COLORED,0); + renderer->Updateframe(); + delay(1000); + renderer->fillScreen(0); +#endif + } } /*********************************************************************************************/ -void EpdClear(void) -{ - paint.Clear(UNCOLORED); -} -void EpdSetFont(uint8_t font) -{ - if (1 == font) { - selected_font = &Font12; - } else { -#ifdef USE_TINY_FONT - if (2 == font) { - selected_font = &Font24; - } else { - selected_font = &Font8; - } -#else - selected_font = &Font24; -#endif - } -} - -void EpdDisplayFrame(void) -{ - epd.SetFrameMemory(paint.GetImage(), 0, 0, paint.GetWidth(), paint.GetHeight()); - epd.DisplayFrame(); - epd.Sleep(); -} - -void EpdDrawStringAt(uint16_t x, uint16_t y, char *str, uint8_t color, uint8_t flag) -{ - if (!flag) { - paint.DrawStringAt(x, y, str, selected_font, color); - } else { - paint.DrawStringAt((x-1) * selected_font->Width, (y-1) * selected_font->Height, str, selected_font, color); - } -} - -// not needed -void EpdDisplayOnOff(uint8_t on) -{ - -} - -void EpdOnOff(void) -{ - EpdDisplayOnOff(disp_power); -} /*********************************************************************************************/ #ifdef USE_DISPLAY_MODES1TO5 - -void EpdPrintLog(void) +#define EPD_FONT_HEIGTH 12 +void EpdPrintLog29(void) { + disp_refresh--; if (!disp_refresh) { disp_refresh = Settings.display_refresh; - if (Settings.display_rotate) { + //if (Settings.display_rotate) { if (!disp_screen_buffer_cols) { DisplayAllocScreenBuffer(); } - } + //} char* txt = DisplayLogBuffer('\040'); if (txt != nullptr) { uint8_t size = Settings.display_size; uint16_t theight = size * EPD_FONT_HEIGTH; - EpdSetFont(size); + renderer->setTextFont(size); uint8_t last_row = Settings.display_rows -1; // epd_scroll = theight; // Start below header epd_scroll = 0; // Start at top with no header for (uint32_t i = 0; i < last_row; i++) { strlcpy(disp_screen_buffer[i], disp_screen_buffer[i +1], disp_screen_buffer_cols); - EpdDrawStringAt(0, epd_scroll, disp_screen_buffer[i], COLORED, 0); + renderer->DrawStringAt(0, epd_scroll, disp_screen_buffer[i], COLORED, 0); epd_scroll += theight; } strlcpy(disp_screen_buffer[last_row], txt, disp_screen_buffer_cols); DisplayFillScreen(last_row); - EpdDrawStringAt(0, epd_scroll, disp_screen_buffer[last_row], COLORED, 0); + renderer->DrawStringAt(0, epd_scroll, disp_screen_buffer[last_row], COLORED, 0); // EpdDisplayFrame(); AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION "[%s]"), txt); @@ -224,9 +141,11 @@ void EpdPrintLog(void) } } -void EpdRefresh(void) // Every second +void EpdRefresh29(void) // Every second { if (Settings.display_mode) { // Mode 0 is User text + + if (!renderer) return; /* char tftdt[Settings.display_cols[0] +1]; char date4[11]; // 24-04-2017 @@ -249,8 +168,8 @@ void EpdRefresh(void) // Every second case 3: // Local case 4: // Mqtt case 5: // Mqtt - EpdPrintLog(); - EpdDisplayFrame(); + EpdPrintLog29(); + renderer->Updateframe(); break; } @@ -267,78 +186,24 @@ void EpdRefresh(void) // Every second bool Xdsp05(uint8_t function) { bool result = false; - - if (spi_flg || soft_spi_flg) { if (FUNC_DISPLAY_INIT_DRIVER == function) { - EpdInitDriver(); + EpdInitDriver29(); } else if (XDSP_05 == Settings.display_model) { - - if (!dsp_color) { dsp_color = COLORED; } - switch (function) { case FUNC_DISPLAY_MODEL: result = true; break; - case FUNC_DISPLAY_INIT: - EpdInit(dsp_init); - break; - case FUNC_DISPLAY_POWER: - EpdOnOff(); - break; - case FUNC_DISPLAY_CLEAR: - EpdClear(); - break; - case FUNC_DISPLAY_DRAW_HLINE: - paint.DrawHorizontalLine(dsp_x, dsp_y, dsp_len, dsp_color); - break; - case FUNC_DISPLAY_DRAW_VLINE: - paint.DrawVerticalLine(dsp_x, dsp_y, dsp_len, dsp_color); - break; - case FUNC_DISPLAY_DRAW_LINE: - paint.DrawLine(dsp_x, dsp_y, dsp_x2, dsp_y2, dsp_color); - break; - case FUNC_DISPLAY_DRAW_CIRCLE: - paint.DrawCircle(dsp_x, dsp_y, dsp_rad, dsp_color); - break; - case FUNC_DISPLAY_FILL_CIRCLE: - paint.DrawFilledCircle(dsp_x, dsp_y, dsp_rad, dsp_color); - break; - case FUNC_DISPLAY_DRAW_RECTANGLE: - paint.DrawRectangle(dsp_x, dsp_y, dsp_x + dsp_x2, dsp_y + dsp_y2, dsp_color); - break; - case FUNC_DISPLAY_FILL_RECTANGLE: - paint.DrawFilledRectangle(dsp_x, dsp_y, dsp_x + dsp_x2, dsp_y + dsp_y2, dsp_color); - break; - case FUNC_DISPLAY_DRAW_FRAME: - EpdDisplayFrame(); - break; - case FUNC_DISPLAY_TEXT_SIZE: -// EpdSetFontorSize(Settings.display_size); - break; - case FUNC_DISPLAY_FONT_SIZE: - EpdSetFont(Settings.display_font); - break; - case FUNC_DISPLAY_DRAW_STRING: - EpdDrawStringAt(dsp_x, dsp_y, dsp_str, dsp_color, dsp_flag); - break; - case FUNC_DISPLAY_ONOFF: - EpdDisplayOnOff(dsp_on); - break; - case FUNC_DISPLAY_ROTATION: - paint.SetRotate(Settings.display_rotate); - break; #ifdef USE_DISPLAY_MODES1TO5 case FUNC_DISPLAY_EVERY_SECOND: - EpdRefresh(); + EpdRefresh29(); break; #endif // USE_DISPLAY_MODES1TO5 } } - } return result; } -#endif // USE_DISPLAY_EPAPER_29 +#endif // USE_DISPLAY_EPAPER #endif // USE_DISPLAY #endif // USE_SPI diff --git a/sonoff/xdsp_06_epaper_42.ino b/sonoff/xdsp_06_epaper_42.ino new file mode 100644 index 000000000..0dd235186 --- /dev/null +++ b/sonoff/xdsp_06_epaper_42.ino @@ -0,0 +1,160 @@ +/* + xdsp_05_epaper.ino - Display e-paper support for Sonoff-Tasmota + + Copyright (C) 2019 Theo Arends, Gerhard Mutz and Waveshare + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + + +#ifdef USE_SPI +#ifdef USE_DISPLAY +#ifdef USE_DISPLAY_EPAPER_42 + +#define XDSP_06 6 + +#define COLORED42 1 +#define UNCOLORED42 0 + +// using font 8 is opional (num=3) +// very badly readable, but may be useful for graphs +#define USE_TINY_FONT + +#include +#include + +extern uint8_t *buffer; + +Epd42 *epd42; + + +/*********************************************************************************************/ + +void EpdInitDriver42() +{ + if (!Settings.display_model) { + Settings.display_model = XDSP_06; + } + + if (XDSP_06 == Settings.display_model) { + + if (Settings.display_width != EPD_WIDTH42) { + Settings.display_width = EPD_WIDTH42; + } + if (Settings.display_height != EPD_HEIGHT42) { + Settings.display_height = EPD_HEIGHT42; + } + + // allocate screen buffer + if (buffer) free(buffer); + buffer=(unsigned char*)calloc((EPD_WIDTH42 * EPD_HEIGHT42) / 8,1); + if (!buffer) return; + + // init renderer + epd42 = new Epd42(EPD_WIDTH42,EPD_HEIGHT42); + + #ifdef USE_SPI + if ((pin[GPIO_SSPI_CS]<99) && (pin[GPIO_SSPI_MOSI]<99) && (pin[GPIO_SSPI_SCLK]<99)) { + epd42->Begin(pin[GPIO_SSPI_CS],pin[GPIO_SSPI_MOSI],pin[GPIO_SSPI_SCLK]); + } else { + free(buffer); + return; + } + #else + if ((pin[GPIO_SPI_CS]<99) && (pin[GPIO_SPI_MOSI]<99) && (pin[GPIO_SPI_CLK]<99)) { + epd42->Begin(pin[GPIO_SPI_CS],pin[GPIO_SPI_MOSI],pin[GPIO_SPI_CLK]); + } else { + free(buffer); + return; + } + #endif + + renderer = epd42; + + epd42->Init(); + + renderer->fillScreen(0); + + // whiten display with full update, takes 4 seconds + epd42->Init(DISPLAY_INIT_FULL); + + renderer->DisplayInit(DISPLAY_INIT_MODE,Settings.display_size,Settings.display_rotate,Settings.display_font); + + epd42->ClearFrame(); + renderer->Updateframe(); + delay(3000); + renderer->setTextColor(1,0); + +#ifdef SHOW_SPLASH + // Welcome text + renderer->setTextFont(2); + renderer->DrawStringAt(50, 140, "Waveshare E-Paper!", COLORED42,0); + renderer->Updateframe(); + delay(350); + renderer->fillScreen(0); +#endif + + } +} + +/*********************************************************************************************/ + + + +/*********************************************************************************************/ + +#ifdef USE_DISPLAY_MODES1TO5 + +void EpdRefresh42() // Every second +{ + if (Settings.display_mode) { // Mode 0 is User text + + } +} + +#endif // USE_DISPLAY_MODES1TO5 + + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xdsp06(uint8_t function) +{ + bool result = false; + + if (FUNC_DISPLAY_INIT_DRIVER == function) { + EpdInitDriver42(); + } + else if (XDSP_06 == Settings.display_model) { + + switch (function) { + case FUNC_DISPLAY_MODEL: + result = true; + break; + +#ifdef USE_DISPLAY_MODES1TO5 + case FUNC_DISPLAY_EVERY_SECOND: + EpdRefresh42(); + break; +#endif // USE_DISPLAY_MODES1TO5 + } + } + return result; +} + + +#endif // USE_DISPLAY_EPAPER42 +#endif // USE_DISPLAY +#endif // USE_SPI diff --git a/sonoff/xdsp_07_sh1106.ino b/sonoff/xdsp_07_sh1106.ino new file mode 100644 index 000000000..6c535b02a --- /dev/null +++ b/sonoff/xdsp_07_sh1106.ino @@ -0,0 +1,195 @@ +/* + xdsp_07_SH1106.ino - Display Oled SH1106 support for Sonoff-Tasmota + + Copyright (C) 2019 Theo Arends and Adafruit + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef USE_I2C +#ifdef USE_DISPLAY +#ifdef USE_DISPLAY_SH1106 + +#define OLED_RESET 4 + +#define SPRINT(A) char str[32];sprintf(str,"val: %d ",A);Serial.println((char*)str); + +extern uint8_t *buffer; + +#define XDSP_07 7 + +#define OLED_ADDRESS1 0x3C // Oled 128x32 I2C address +#define OLED_ADDRESS2 0x3D // Oled 128x64 I2C address + +#define OLED_BUFFER_COLS 40 // Max number of columns in display shadow buffer +#define OLED_BUFFER_ROWS 16 // Max number of lines in display shadow buffer + +#define OLED_FONT_WIDTH 6 +#define OLED_FONT_HEIGTH 8 + +#include +#include +#include + +Adafruit_SH1106 *oled1106; + +/*********************************************************************************************/ + + +void SH1106InitDriver() +{ + if (!Settings.display_model) { + if (I2cDevice(OLED_ADDRESS1)) { + Settings.display_address[0] = OLED_ADDRESS1; + Settings.display_model = XDSP_07; + } + else if (I2cDevice(OLED_ADDRESS2)) { + Settings.display_address[0] = OLED_ADDRESS2; + Settings.display_model = XDSP_07; + } + } + + if (XDSP_07 == Settings.display_model) { + + if (Settings.display_width != SH1106_LCDWIDTH) { + Settings.display_width = SH1106_LCDWIDTH; + } + if (Settings.display_height != SH1106_LCDHEIGHT) { + Settings.display_height = SH1106_LCDHEIGHT; + } + + // allocate screen buffer + if (buffer) free(buffer); + buffer=(unsigned char*)calloc((SH1106_LCDWIDTH * SH1106_LCDHEIGHT) / 8,1); + if (!buffer) return; + + // init renderer + oled1106 = new Adafruit_SH1106(SH1106_LCDWIDTH,SH1106_LCDHEIGHT); + renderer=oled1106; + renderer->Begin(SH1106_SWITCHCAPVCC, Settings.display_address[0],0); + renderer->DisplayInit(DISPLAY_INIT_MODE,Settings.display_size,Settings.display_rotate,Settings.display_font); + renderer->setTextColor(1,0); + +#ifdef SHOW_SPLASH + renderer->setTextFont(0); + renderer->setTextSize(2); + renderer->setCursor(20,20); + renderer->println(F("SH1106")); + renderer->Updateframe(); + renderer->DisplayOnff(1); +#endif + } +} + + +/*********************************************************************************************/ +#ifdef USE_DISPLAY_MODES1TO5 + +void SH1106PrintLog(void) +{ + disp_refresh--; + if (!disp_refresh) { + disp_refresh = Settings.display_refresh; + if (!disp_screen_buffer_cols) { DisplayAllocScreenBuffer(); } + + char* txt = DisplayLogBuffer('\370'); + if (txt != NULL) { + uint8_t last_row = Settings.display_rows -1; + + renderer->clearDisplay(); + renderer->setTextSize(Settings.display_size); + renderer->setCursor(0,0); + for (byte i = 0; i < last_row; i++) { + strlcpy(disp_screen_buffer[i], disp_screen_buffer[i +1], disp_screen_buffer_cols); + renderer->println(disp_screen_buffer[i]); + } + strlcpy(disp_screen_buffer[last_row], txt, disp_screen_buffer_cols); + DisplayFillScreen(last_row); + + snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_DEBUG "[%s]"), disp_screen_buffer[last_row]); + AddLog(LOG_LEVEL_DEBUG); + + renderer->println(disp_screen_buffer[last_row]); + renderer->Updateframe(); + } + } +} + +void SH1106Time(void) +{ + char line[12]; + + renderer->clearDisplay(); + renderer->setTextSize(Settings.display_size); + renderer->setTextFont(Settings.display_font); + renderer->setCursor(0, 0); + snprintf_P(line, sizeof(line), PSTR(" %02d" D_HOUR_MINUTE_SEPARATOR "%02d" D_MINUTE_SECOND_SEPARATOR "%02d"), RtcTime.hour, RtcTime.minute, RtcTime.second); // [ 12:34:56 ] + renderer->println(line); + snprintf_P(line, sizeof(line), PSTR("%02d" D_MONTH_DAY_SEPARATOR "%02d" D_YEAR_MONTH_SEPARATOR "%04d"), RtcTime.day_of_month, RtcTime.month, RtcTime.year); // [01-02-2018] + renderer->println(line); + renderer->Updateframe(); +} + +void SH1106Refresh(void) // Every second +{ + if (!renderer) return; + if (Settings.display_mode) { // Mode 0 is User text + switch (Settings.display_mode) { + case 1: // Time + SH1106Time(); + break; + case 2: // Local + case 3: // Local + case 4: // Mqtt + case 5: // Mqtt + SH1106PrintLog(); + break; + } + } +} + +#endif // USE_DISPLAY_MODES1TO5 + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xdsp07(uint8_t function) +{ + bool result = false; + + if (i2c_flg) { + if (FUNC_DISPLAY_INIT_DRIVER == function) { + SH1106InitDriver(); + } + else if (XDSP_07 == Settings.display_model) { + + switch (function) { + case FUNC_DISPLAY_MODEL: + result = true; + break; +#ifdef USE_DISPLAY_MODES1TO5 + case FUNC_DISPLAY_EVERY_SECOND: + SH1106Refresh(); + break; +#endif // USE_DISPLAY_MODES1TO5 + } + } + } + return result; +} + +#endif // USE_DISPLAY_SH1106 +#endif // USE_DISPLAY +#endif // USE_I2C diff --git a/sonoff/xdsp_08_ILI9488.ino b/sonoff/xdsp_08_ILI9488.ino new file mode 100644 index 000000000..071280601 --- /dev/null +++ b/sonoff/xdsp_08_ILI9488.ino @@ -0,0 +1,267 @@ +/* + xdsp_08_ILI9488.ino - Display ILI9488 support for Sonoff-Tasmota + + Copyright (C) 2019 Theo Arends, Gerhard Mutz + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef USE_SPI +#ifdef USE_DISPLAY +#ifdef USE_DISPLAY_ILI9488 + +#define XDSP_08 8 + +#define COLORED 1 +#define UNCOLORED 0 + +// touch panel controller +#define FT6236_address 0x38 + +// using font 8 is opional (num=3) +// very badly readable, but may be useful for graphs +#define USE_TINY_FONT + + +#include +#include + +TouchLocation ili9488_pLoc; +uint8_t ili9488_ctouch_counter = 0; + +// currently fixed +#define BACKPLANE_PIN 2 + +extern uint8_t *buffer; +extern uint8_t color_type; +ILI9488 *ili9488; + +#ifdef USE_TOUCH_BUTTONS +extern VButton *buttons[]; +#endif + +extern const uint16_t picture[]; +uint8_t FT6236_found; + +/*********************************************************************************************/ + +void ILI9488_InitDriver() +{ + if (!Settings.display_model) { + Settings.display_model = XDSP_08; + } + + if (XDSP_08 == Settings.display_model) { + + if (Settings.display_width != ILI9488_TFTWIDTH) { + Settings.display_width = ILI9488_TFTWIDTH; + } + if (Settings.display_height != ILI9488_TFTHEIGHT) { + Settings.display_height = ILI9488_TFTHEIGHT; + } + + // disable screen buffer + buffer=NULL; + + // default colors + fg_color = ILI9488_WHITE; + bg_color = ILI9488_BLACK; + + uint8_t bppin=BACKPLANE_PIN; + if (pin[GPIO_BACKLIGHT]<99) { + bppin=pin[GPIO_BACKLIGHT]; + } + + // init renderer + if ((pin[GPIO_SSPI_CS]<99) && (pin[GPIO_SSPI_MOSI]<99) && (pin[GPIO_SSPI_SCLK]<99)){ + ili9488 = new ILI9488(pin[GPIO_SSPI_CS],pin[GPIO_SSPI_MOSI],pin[GPIO_SSPI_SCLK],bppin); + } else { + if ((pin[GPIO_SPI_CS]<99) && (pin[GPIO_SPI_MOSI]<99) && (pin[GPIO_SPI_CLK]<99)) { + ili9488 = new ILI9488(pin[GPIO_SPI_CS],pin[GPIO_SPI_MOSI],pin[GPIO_SPI_CLK],bppin); + } else { + return; + } + } + + SPI.begin(); + ili9488->begin(); + renderer = ili9488; + renderer->DisplayInit(DISPLAY_INIT_MODE,Settings.display_size,Settings.display_rotate,Settings.display_font); + +#ifdef SHOW_SPLASH + // Welcome text + renderer->setTextFont(2); + renderer->setTextColor(ILI9488_WHITE,ILI9488_BLACK); + renderer->DrawStringAt(50, 50, "ILI9488 TFT Display!", ILI9488_WHITE,0); + delay(1000); + + //renderer->drawRGBBitmap(100,100, picture,51,34); +#endif + + color_type = COLOR_COLOR; + // start digitizer with fixed adress + + if (i2c_flg && I2cDevice(FT6236_address)) { + FT6236begin(FT6236_address); + FT6236_found=1; + } else { + FT6236_found=0; + } + + } +} + +#ifdef USE_TOUCH_BUTTONS +void ILI9488_MQTT(uint8_t count,const char *cp) { + ResponseTime_P(PSTR(",\"RA8876\":{\"%s%d\":\"%d\"}}"), cp,count+1,(buttons[count]->vpower&0x80)>>7); + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); +} + +void ILI9488_RDW_BUTT(uint32_t count,uint32_t pwr) { + buttons[count]->xdrawButton(pwr); + if (pwr) buttons[count]->vpower|=0x80; + else buttons[count]->vpower&=0x7f; +} +// check digitizer hit +void FT6236Check() { +uint16_t temp; +uint8_t rbutt=0,vbutt=0; +ili9488_ctouch_counter++; +if (2 == ili9488_ctouch_counter) { + // every 100 ms should be enough + ili9488_ctouch_counter=0; + if (FT6236readTouchLocation(&ili9488_pLoc,1)) { + // did find a hit + if (renderer) { + uint8_t rot=renderer->getRotation(); + switch (rot) { + case 0: + temp=ili9488_pLoc.y; + ili9488_pLoc.y=renderer->height()-ili9488_pLoc.x; + ili9488_pLoc.x=temp; + break; + case 1: + break; + case 2: + break; + case 3: + temp=ili9488_pLoc.y; + ili9488_pLoc.y=ili9488_pLoc.x; + ili9488_pLoc.x=renderer->width()-temp; + break; + } + // now must compare with defined buttons + for (uint8_t count=0; countvpower&0x7f; + if (buttons[count]->contains(ili9488_pLoc.x,ili9488_pLoc.y)) { + // did hit + buttons[count]->press(true); + if (buttons[count]->justPressed()) { + if (!bflags) { + uint8_t pwr=bitRead(power,rbutt); + if (!SendKey(KEY_BUTTON, rbutt+1, POWER_TOGGLE)) { + ExecuteCommandPower(rbutt+1, POWER_TOGGLE, SRC_BUTTON); + ILI9488_RDW_BUTT(count,!pwr); + } + } else { + // virtual button + const char *cp; + if (bflags==1) { + // toggle button + buttons[count]->vpower^=0x80; + cp="TBT"; + } else { + // push button + buttons[count]->vpower|=0x80; + cp="PBT"; + } + buttons[count]->xdrawButton(buttons[count]->vpower&0x80); + ILI9488_MQTT(count,cp); + } + } + } + if (!bflags) { + rbutt++; + } else { + vbutt++; + } + } + } + } + } else { + // no hit + for (uint8_t count=0; countvpower&0x7f; + buttons[count]->press(false); + if (buttons[count]->justReleased()) { + uint8_t bflags=buttons[count]->vpower&0x7f; + if (bflags>0) { + if (bflags>1) { + // push button + buttons[count]->vpower&=0x7f; + ILI9488_MQTT(count,"PBT"); + } + buttons[count]->xdrawButton(buttons[count]->vpower&0x80); + } + } + if (!bflags) { + // check if power button stage changed + uint8_t pwr=bitRead(power,rbutt); + uint8_t vpwr=(buttons[count]->vpower&0x80)>>7; + if (pwr!=vpwr) { + ILI9488_RDW_BUTT(count,pwr); + } + rbutt++; + } + } + } + ili9488_pLoc.x=0; + ili9488_pLoc.y=0; + } +} +} +#endif // USE_TOUCH_BUTTONS +/*********************************************************************************************/ +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ +bool Xdsp08(uint8_t function) +{ + bool result = false; + + if (FUNC_DISPLAY_INIT_DRIVER == function) { + ILI9488_InitDriver(); + } + else if (XDSP_08 == Settings.display_model) { + + switch (function) { + case FUNC_DISPLAY_MODEL: + result = true; + break; + case FUNC_DISPLAY_EVERY_50_MSECOND: +#ifdef USE_TOUCH_BUTTONS + if (FT6236_found) FT6236Check(); +#endif + break; + } + } + //} + return result; +} + +#endif // USE_DISPLAY_ILI9488 +#endif // USE_DISPLAY +#endif // USE_SPI diff --git a/sonoff/xdsp_09_SSD1351.ino b/sonoff/xdsp_09_SSD1351.ino new file mode 100644 index 000000000..0a3b69b4e --- /dev/null +++ b/sonoff/xdsp_09_SSD1351.ino @@ -0,0 +1,183 @@ +/* + xdsp_09_SSD1351.ino - Display SSD1351 support for Sonoff-Tasmota + + Copyright (C) 2019 Gerhard Mutz and Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef USE_SPI +#ifdef USE_DISPLAY +#ifdef USE_DISPLAY_SSD1351 + +#define XDSP_09 9 + +#define COLORED 1 +#define UNCOLORED 0 + +// uses about 1.9k flash + renderer class +// using font 8 is opional (num=3) +// very badly readable, but may be useful for graphs +#define USE_TINY_FONT + +#include + +extern uint8_t *buffer; +extern uint8_t color_type; +SSD1351 *ssd1351; + +/*********************************************************************************************/ + +void SSD1351_InitDriver() { + if (!Settings.display_model) { + Settings.display_model = XDSP_09; + } + + if (XDSP_09 == Settings.display_model) { + + if (Settings.display_width != SSD1351_WIDTH) { + Settings.display_width = SSD1351_WIDTH; + } + if (Settings.display_height != SSD1351_HEIGHT) { + Settings.display_height = SSD1351_HEIGHT; + } + + buffer=0; + + // default colors + fg_color = SSD1351_WHITE; + bg_color = SSD1351_BLACK; + + // init renderer + if ((pin[GPIO_SSPI_CS]<99) && (pin[GPIO_SSPI_MOSI]<99) && (pin[GPIO_SSPI_SCLK]<99)){ + ssd1351 = new SSD1351(pin[GPIO_SSPI_CS],pin[GPIO_SSPI_MOSI],pin[GPIO_SSPI_SCLK]); + } else { + if ((pin[GPIO_SPI_CS]<99) && (pin[GPIO_SPI_MOSI]<99) && (pin[GPIO_SPI_CLK]<99)){ + ssd1351 = new SSD1351(pin[GPIO_SPI_CS],pin[GPIO_SPI_MOSI],pin[GPIO_SPI_CLK]); + } else { + return; + } + } + + delay(100); + SPI.begin(); + ssd1351->begin(); + renderer = ssd1351; + renderer->DisplayInit(DISPLAY_INIT_MODE,Settings.display_size,Settings.display_rotate,Settings.display_font); + renderer->dim(Settings.display_dimmer); + +#ifdef SHOW_SPLASH + // Welcome text + renderer->setTextFont(2); + renderer->setTextColor(SSD1351_WHITE,SSD1351_BLACK); + renderer->DrawStringAt(10, 60, "SSD1351", SSD1351_RED,0); + delay(1000); + +#endif + color_type = COLOR_COLOR; + } +} + +#ifdef USE_DISPLAY_MODES1TO5 + +void SSD1351PrintLog(void) +{ + disp_refresh--; + if (!disp_refresh) { + disp_refresh = Settings.display_refresh; + if (!disp_screen_buffer_cols) { DisplayAllocScreenBuffer(); } + + char* txt = DisplayLogBuffer('\370'); + if (txt != NULL) { + uint8_t last_row = Settings.display_rows -1; + + renderer->clearDisplay(); + renderer->setTextSize(Settings.display_size); + renderer->setCursor(0,0); + for (byte i = 0; i < last_row; i++) { + strlcpy(disp_screen_buffer[i], disp_screen_buffer[i +1], disp_screen_buffer_cols); + renderer->println(disp_screen_buffer[i]); + } + strlcpy(disp_screen_buffer[last_row], txt, disp_screen_buffer_cols); + DisplayFillScreen(last_row); + + snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_DEBUG "[%s]"), disp_screen_buffer[last_row]); + AddLog(LOG_LEVEL_DEBUG); + + renderer->println(disp_screen_buffer[last_row]); + renderer->Updateframe(); + } + } +} + +void SSD1351Time(void) +{ + char line[12]; + + renderer->clearDisplay(); + renderer->setTextSize(2); + renderer->setCursor(0, 0); + snprintf_P(line, sizeof(line), PSTR(" %02d" D_HOUR_MINUTE_SEPARATOR "%02d" D_MINUTE_SECOND_SEPARATOR "%02d"), RtcTime.hour, RtcTime.minute, RtcTime.second); // [ 12:34:56 ] + renderer->println(line); + snprintf_P(line, sizeof(line), PSTR("%02d" D_MONTH_DAY_SEPARATOR "%02d" D_YEAR_MONTH_SEPARATOR "%04d"), RtcTime.day_of_month, RtcTime.month, RtcTime.year); // [01-02-2018] + renderer->println(line); + renderer->Updateframe(); +} + +void SSD1351Refresh(void) // Every second +{ + if (Settings.display_mode) { // Mode 0 is User text + switch (Settings.display_mode) { + case 1: // Time + SSD1351Time(); + break; + case 2: // Local + case 3: // Local + case 4: // Mqtt + case 5: // Mqtt + SSD1351PrintLog(); + break; + } + } +} + +#endif // USE_DISPLAY_MODES1TO5 +/*********************************************************************************************/ +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ +bool Xdsp09(uint8_t function) +{ + bool result = false; + + if (FUNC_DISPLAY_INIT_DRIVER == function) { + SSD1351_InitDriver(); + } + else if (XDSP_09 == Settings.display_model) { + switch (function) { + case FUNC_DISPLAY_MODEL: + result = true; + break; +#ifdef USE_DISPLAY_MODES1TO5 + case FUNC_DISPLAY_EVERY_SECOND: + SSD1351Refresh(); + break; +#endif // USE_DISPLAY_MODES1TO5 + } + } + return result; +} +#endif // USE_DISPLAY_SSD1351 +#endif // USE_DISPLAY +#endif // USE_SPI diff --git a/sonoff/xdsp_10_RA8876.ino b/sonoff/xdsp_10_RA8876.ino new file mode 100644 index 000000000..d138ae154 --- /dev/null +++ b/sonoff/xdsp_10_RA8876.ino @@ -0,0 +1,447 @@ +/* + xdsp_09_SSD1351.ino - Display SSD1351 support for Sonoff-Tasmota + + Copyright (C) 2019 Gerhard Mutz and Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef USE_SPI +#ifdef USE_DISPLAY +#ifdef USE_DISPLAY_RA8876 + +#define XDSP_10 10 + +#define COLORED 1 +#define UNCOLORED 0 + +// touch panel controller +#define FT5316_address 0x38 + +// using font 8 is opional (num=3) +// very badly readable, but may be useful for graphs +#define USE_TINY_FONT + +#include +#include + +TouchLocation ra8876_pLoc; +uint8_t ra8876_ctouch_counter = 0; + +#ifdef USE_TOUCH_BUTTONS +extern VButton *buttons[]; +#endif + +extern uint8_t *buffer; +extern uint8_t color_type; +RA8876 *ra8876; + +uint8_t FT5316_found; + +/*********************************************************************************************/ +void RA8876_InitDriver() +{ + if (!Settings.display_model) { + Settings.display_model = XDSP_10; + } + + if (XDSP_10 == Settings.display_model) { + + if (Settings.display_width != RA8876_TFTWIDTH) { + Settings.display_width = RA8876_TFTWIDTH; + } + if (Settings.display_height != RA8876_TFTHEIGHT) { + Settings.display_height = RA8876_TFTHEIGHT; + } + buffer=0; + + // default colors + fg_color = RA8876_WHITE; + bg_color = RA8876_BLACK; + + // init renderer, must use hardware spi + if ((pin[GPIO_SSPI_CS]<99) && (pin[GPIO_SSPI_MOSI]==13) && (pin[GPIO_SSPI_MISO]==12) && (pin[GPIO_SSPI_SCLK]==14)) { + ra8876 = new RA8876(pin[GPIO_SSPI_CS],pin[GPIO_SSPI_MOSI],pin[GPIO_SSPI_MISO],pin[GPIO_SSPI_SCLK],pin[GPIO_BACKLIGHT]); + } else { + if ((pin[GPIO_SPI_CS]<99) && (pin[GPIO_SPI_MOSI]==13) && (pin[GPIO_SPI_MISO]==12) && (pin[GPIO_SPI_CLK]==14)) { + ra8876 = new RA8876(pin[GPIO_SPI_CS],pin[GPIO_SPI_MOSI],pin[GPIO_SPI_MISO],pin[GPIO_SPI_CLK],pin[GPIO_BACKLIGHT]); + } else { + return; + } + } + + ra8876->begin(); + renderer = ra8876; + renderer->DisplayInit(DISPLAY_INIT_MODE,Settings.display_size,Settings.display_rotate,Settings.display_font); + renderer->dim(Settings.display_dimmer); + + //testall(); +#ifdef SHOW_SPLASH + // Welcome text + renderer->setTextFont(2); + renderer->setTextColor(RA8876_WHITE,RA8876_BLACK); + renderer->DrawStringAt(600, 300, "RA8876", RA8876_RED,0); + delay(1000); + +#endif + color_type = COLOR_COLOR; + + if (i2c_flg && I2cDevice(FT5316_address)) { + FT6236begin(FT5316_address); + FT5316_found=1; + } else { + FT5316_found=0; + } + + } +} + +#ifdef USE_TOUCH_BUTTONS +void RA8876_MQTT(uint8_t count,const char *cp) { + ResponseTime_P(PSTR(",\"RA8876\":{\"%s%d\":\"%d\"}}"), cp,count+1,(buttons[count]->vpower&0x80)>>7); + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); +} + +void RA8876_RDW_BUTT(uint32_t count,uint32_t pwr) { + buttons[count]->xdrawButton(pwr); + if (pwr) buttons[count]->vpower|=0x80; + else buttons[count]->vpower&=0x7f; +} + +// check digitizer hit +void FT5316Check() { +uint16_t temp; +uint8_t rbutt=0,vbutt=0; +ra8876_ctouch_counter++; +if (2 == ra8876_ctouch_counter) { + // every 100 ms should be enough + ra8876_ctouch_counter=0; + // panel has 800x480 + if (FT6236readTouchLocation(&ra8876_pLoc,1)) { + ra8876_pLoc.x=ra8876_pLoc.x*RA8876_TFTWIDTH/800; + ra8876_pLoc.y=ra8876_pLoc.y*RA8876_TFTHEIGHT/480; + // did find a hit + + if (renderer) { + + // rotation not supported + ra8876_pLoc.x=RA8876_TFTWIDTH-ra8876_pLoc.x; + ra8876_pLoc.y=RA8876_TFTHEIGHT-ra8876_pLoc.y; + + /* + uint8_t rot=renderer->getRotation(); + switch (rot) { + case 0: + //temp=pLoc.y; + pLoc.x=renderer->width()-pLoc.x; + pLoc.y=renderer->height()-pLoc.y; + //pLoc.x=temp; + break; + case 1: + break; + case 2: + break; + case 3: + temp=pLoc.y; + pLoc.y=pLoc.x; + pLoc.x=renderer->width()-temp; + break; + } + */ + //AddLog_P2(LOG_LEVEL_INFO, PSTR(">> %d,%d"),ra8876_pLoc.x,ra8876_pLoc.y); + + + //Serial.printf("loc x: %d , loc y: %d\n",pLoc.x,pLoc.y); + + // now must compare with defined buttons + for (uint8_t count=0; countvpower&0x7f; + if (buttons[count]->contains(ra8876_pLoc.x,ra8876_pLoc.y)) { + // did hit + buttons[count]->press(true); + if (buttons[count]->justPressed()) { + if (!bflags) { + // real button + uint8_t pwr=bitRead(power,rbutt); + if (!SendKey(KEY_BUTTON, rbutt+1, POWER_TOGGLE)) { + ExecuteCommandPower(rbutt+1, POWER_TOGGLE, SRC_BUTTON); + RA8876_RDW_BUTT(count,!pwr); + } + } else { + // virtual button + const char *cp; + if (bflags==1) { + // toggle button + buttons[count]->vpower^=0x80; + cp="TBT"; + } else { + // push button + buttons[count]->vpower|=0x80; + cp="PBT"; + } + buttons[count]->xdrawButton(buttons[count]->vpower&0x80); + RA8876_MQTT(count,cp); + } + } + } + if (!bflags) { + rbutt++; + } else { + vbutt++; + } + } + } + } + } else { + // no hit + for (uint8_t count=0; countvpower&0x7f; + buttons[count]->press(false); + if (buttons[count]->justReleased()) { + if (bflags>0) { + if (bflags>1) { + // push button + buttons[count]->vpower&=0x7f; + RA8876_MQTT(count,"PBT"); + } + buttons[count]->xdrawButton(buttons[count]->vpower&0x80); + } + } + if (!bflags) { + // check if power button stage changed + uint8_t pwr=bitRead(power,rbutt); + uint8_t vpwr=(buttons[count]->vpower&0x80)>>7; + if (pwr!=vpwr) { + RA8876_RDW_BUTT(count,pwr); + } + rbutt++; + } + } + } + ra8876_pLoc.x=0; + ra8876_pLoc.y=0; + } +} +} +#endif // USE_TOUCH_BUTTONS +/* +void testall() { +ra8876->clearScreen(0); + +ra8876->colorBarTest(true); +delay(1000); +ra8876->colorBarTest(false); + +pixelTest(); + + +delay(1000); + +triangleTest(); + +delay(1000); + +circleTest(); + +delay(1000); + +gradientTest(); + +delay(1000); + +textTest(); + +delay(1000); +} + +void gradientTest() +{ +Serial.println("Gradient test."); + +ra8876->clearScreen(0); + +int width = ra8876->getWidth(); +int barHeight = ra8876->getHeight() / 4; + +uint32_t starttime = millis(); + +for (int i = 0; i <= 255; i++) +{ + ra8876->fillRect((width / 256.0) * i, 0, (width / 256.0) * (i + 1) - 1, barHeight, RGB565(i, 0, 0)); + ra8876->fillRect((width / 256.0) * i, barHeight, (width / 256.0) * (i + 1) - 1, barHeight * 2, RGB565(0, i, 0)); + ra8876->fillRect((width / 256.0) * i, barHeight * 2, (width / 256.0) * (i + 1) - 1, barHeight * 3, RGB565(0, 0, i)); + ra8876->fillRect((width / 256.0) * i, barHeight * 3, (width / 256.0) * (i + 1) - 1, barHeight * 4, RGB565(i, i, i)); +} + +uint32_t elapsedtime = millis() - starttime; +Serial.print("Gradient test took "); Serial.print(elapsedtime); Serial.println(" ms"); +} + +void pixelTest() +{ +Serial.println("Pixel test."); + +int width = ra8876->getWidth(); +int height = ra8876->getHeight(); + +uint16_t colors[] = {RGB565(255, 0, 0), RGB565(0, 255, 0), RGB565(0, 0, 255)}; + +uint32_t starttime = millis(); + +for (int c = 0; c < 3; c++) +{ + for (int i = 0; i < 3000; i++) + { + int x = random(0, width); + int y = random(0, height); + + ra8876->drawPixel(x, y, colors[c]); + } + delay(0); +} + +uint32_t elapsedtime = millis() - starttime; +Serial.print("Pixel test took "); Serial.print(elapsedtime); Serial.println(" ms"); +} + +void triangleTest() +{ +Serial.println("Triangle test."); + +int width = ra8876->getWidth(); +int height = ra8876->getHeight(); + +uint32_t starttime = millis(); + +for (int i = 0; i < 2000; i++) +{ + int x1 = random(0, width); + int y1 = random(0, height); + int x2 = random(0, width); + int y2 = random(0, height); + int x3 = random(0, width); + int y3 = random(0, height); + + uint16_t color = RGB565(random(0, 255), random(0, 255), random(0, 255)); + + ra8876->fillTriangle(x1, y1, x2, y2, x3, y3, color); + delay(0); +} + +uint32_t elapsedtime = millis() - starttime; +Serial.print("Triangle test took "); Serial.print(elapsedtime); Serial.println(" ms"); +} + +void circleTest() +{ +Serial.println("Circle test."); + +int width = ra8876->getWidth(); +int height = ra8876->getHeight(); + +uint32_t starttime = millis(); + +for (int i = 0; i < 2000; i++) +{ + int x = random(0, width); + int y = random(0, height); + int r = random(0, 384); + + uint16_t color = RGB565(random(0, 255), random(0, 255), random(0, 255)); + + ra8876->fillCircle(x, y, r, color); + delay(0); +} + +uint32_t elapsedtime = millis() - starttime; +Serial.print("Circle test took "); Serial.print(elapsedtime); Serial.println(" ms"); +} + +void textTest() +{ +Serial.println("Text test."); + +uint32_t starttime = millis(); + +for (int s = 1; s <= 4; s++) +{ + ra8876->setTextScale(s); + + for (int i = 0; i < 3; i++) + { + ra8876->setCursor((ra8876->getWidth() / 3) * i, ra8876->getCursorY()); + ra8876->selectInternalFont((enum FontSize) i); + ra8876->print("Hello"); + } + + ra8876->println(); + delay(0); +} + +ra8876->setCursor(0, 32 * 10); +ra8876->setTextScale(1); + +ra8876->selectInternalFont(RA8876_FONT_SIZE_32, RA8876_FONT_ENCODING_8859_1); +ra8876->println("Latin 1: na\xEFve"); // naïve + +ra8876->selectInternalFont(RA8876_FONT_SIZE_32, RA8876_FONT_ENCODING_8859_2); +ra8876->println("Latin 2: \xE8" "a\xE8kalica"); + +ra8876->selectInternalFont(RA8876_FONT_SIZE_32, RA8876_FONT_ENCODING_8859_4); +ra8876->println("Latin 4: gie\xF0" "at"); // gieđat + +ra8876->selectInternalFont(RA8876_FONT_SIZE_32, RA8876_FONT_ENCODING_8859_5); +ra8876->println("Latin 5: \xD2\xD5\xD4\xD8"); // веди + +ra8876->selectInternalFont(RA8876_FONT_SIZE_32); +ra8876->print("Symbols: "); +ra8876->putChars("\x00\x01\x02\x03\x04\x05\x06\x07", 8); +ra8876->putChars("\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", 8); +ra8876->putChars("\x10\x11\x12\x13\x14\x15\x16\x17", 8); +ra8876->putChars("\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F", 8); + +uint32_t elapsedtime = millis() - starttime; +Serial.print("Text test took "); Serial.print(elapsedtime); Serial.println(" ms"); +} +*/ +/*********************************************************************************************/ +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ +bool Xdsp10(uint8_t function) +{ + bool result = false; + + if (FUNC_DISPLAY_INIT_DRIVER == function) { + RA8876_InitDriver(); + } + else if (XDSP_10 == Settings.display_model) { + switch (function) { + case FUNC_DISPLAY_MODEL: + result = true; + break; + case FUNC_DISPLAY_EVERY_50_MSECOND: +#ifdef USE_TOUCH_BUTTONS + if (FT5316_found) FT5316Check(); +#endif + break; + } + } + return result; +} +#endif // USE_DISPLAY_RA8876 +#endif // USE_DISPLAY +#endif // USE_SPI diff --git a/sonoff/xplg_ws2812.ino b/sonoff/xlgt_01_ws2812.ino similarity index 54% rename from sonoff/xplg_ws2812.ino rename to sonoff/xlgt_01_ws2812.ino index ff8884462..2c0a11f0b 100644 --- a/sonoff/xplg_ws2812.ino +++ b/sonoff/xlgt_01_ws2812.ino @@ -1,7 +1,7 @@ /* - xplg_ws2812.ino - ws2812 led string support for Sonoff-Tasmota + xlgt_01_ws2812.ino - led string support for Sonoff-Tasmota - Copyright (C) 2019 Heiko Krupp and Theo Arends + Copyright (C) 2019 Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -21,8 +21,30 @@ #ifdef USE_WS2812 /*********************************************************************************************\ * WS2812 RGB / RGBW Leds using NeopixelBus library + * + * light_scheme WS2812 3+ Colors 1+2 Colors Effect + * ------------ ------ --------- ---------- ----------------- + * 0 yes no no Clock + * 1 yes no no Incandescent + * 2 yes no no RGB + * 3 yes no no Christmas + * 4 yes no no Hanukkah + * 5 yes no no Kwanzaa + * 6 yes no no Rainbow + * 7 yes no no Fire + * \*********************************************************************************************/ +#define XLGT_01 1 + +const uint8_t WS2812_SCHEMES = 8; // Number of WS2812 schemes + +const char kWs2812Commands[] PROGMEM = "|" // No prefix + D_CMND_LED "|" D_CMND_PIXELS "|" D_CMND_ROTATION "|" D_CMND_WIDTH ; + +void (* const Ws2812Command[])(void) PROGMEM = { + &CmndLed, &CmndPixels, &CmndRotation, &CmndWidth }; + #include #if (USE_WS2812_CTYPE == NEO_GRB) @@ -39,13 +61,33 @@ typedef NeoRgbFeature selectedNeoFeatureType; #endif // USE_WS2812_CTYPE - #ifdef USE_WS2812_DMA - typedef Neo800KbpsMethod selectedNeoSpeedType; + +// See NeoEspDmaMethod.h for available options +#if (USE_WS2812_HARDWARE == NEO_HW_WS2812X) + typedef NeoEsp8266DmaWs2812xMethod selectedNeoSpeedType; +#elif (USE_WS2812_HARDWARE == NEO_HW_SK6812) + typedef NeoEsp8266DmaSk6812Method selectedNeoSpeedType; +#elif (USE_WS2812_HARDWARE == NEO_HW_APA106) + typedef NeoEsp8266DmaApa106Method selectedNeoSpeedType; +#else // USE_WS2812_HARDWARE + typedef NeoEsp8266Dma800KbpsMethod selectedNeoSpeedType; +#endif // USE_WS2812_HARDWARE + #else // USE_WS2812_DMA + +// See NeoEspBitBangMethod.h for available options +#if (USE_WS2812_HARDWARE == NEO_HW_WS2812X) + typedef NeoEsp8266BitBangWs2812xMethod selectedNeoSpeedType; +#elif (USE_WS2812_HARDWARE == NEO_HW_SK6812) + typedef NeoEsp8266BitBangSk6812Method selectedNeoSpeedType; +#else // USE_WS2812_HARDWARE typedef NeoEsp8266BitBang800KbpsMethod selectedNeoSpeedType; +#endif // USE_WS2812_HARDWARE + #endif // USE_WS2812_DMA - NeoPixelBus *strip = nullptr; + +NeoPixelBus *strip = nullptr; struct WsColor { uint8_t red, green, blue; @@ -63,7 +105,7 @@ WsColor kHanukkah[2] = { 0,0,255, 255,255,255 }; WsColor kwanzaa[3] = { 255,0,0, 0,0,0, 0,255,0 }; WsColor kRainbow[7] = { 255,0,0, 255,128,0, 255,255,0, 0,255,0, 0,0,255, 128,0,255, 255,0,255 }; WsColor kFire[3] = { 255,0,0, 255,102,0, 255,192,0 }; -ColorScheme kSchemes[WS2812_SCHEMES] = { +ColorScheme kSchemes[WS2812_SCHEMES -1] = { // Skip clock scheme kIncandescent, 2, kRgb, 3, kChristmas, 2, @@ -85,8 +127,12 @@ uint8_t kWsRepeat[5] = { 2, // Largest 1 }; // All -uint8_t ws_show_next = 1; -bool ws_suspend_update = false; +struct WS2812 { + uint8_t show_next = 1; + uint8_t scheme_offset = 0; + bool suspend_update = false; +} Ws2812; + /********************************************************************************************/ void Ws2812StripShow(void) @@ -119,7 +165,6 @@ int mod(int a, int b) return ret; } - void Ws2812UpdatePixelColor(int position, struct WsColor hand_color, float offset) { #if (USE_WS2812_CTYPE > NEO_3LED) @@ -128,7 +173,7 @@ void Ws2812UpdatePixelColor(int position, struct WsColor hand_color, float offse RgbColor color; #endif - uint16_t mod_position = mod(position, (int)Settings.light_pixels); + uint32_t mod_position = mod(position, (int)Settings.light_pixels); color = strip->GetPixelColor(mod_position); float dimmer = 100 / (float)Settings.light_dimmer; @@ -138,8 +183,12 @@ void Ws2812UpdatePixelColor(int position, struct WsColor hand_color, float offse strip->SetPixelColor(mod_position, color); } -void Ws2812UpdateHand(int position, uint8_t index) +void Ws2812UpdateHand(int position, uint32_t index) { + uint32_t width = Settings.light_width; + if (index < WS_MARKER) { width = Settings.ws_width[index]; } + if (!width) { return; } // Skip + position = (position + Settings.light_rotation) % Settings.light_pixels; if (Settings.flag.ws_clock_reverse) position = Settings.light_pixels -position; @@ -147,8 +196,7 @@ void Ws2812UpdateHand(int position, uint8_t index) Ws2812UpdatePixelColor(position, hand_color, 1); - uint8_t range = 1; - if (index < WS_MARKER) range = ((Settings.ws_width[index] -1) / 2) +1; + uint32_t range = ((width -1) / 2) +1; for (uint32_t h = 1; h < range; h++) { float offset = (float)(range - h) / (float)range; Ws2812UpdatePixelColor(position -h, hand_color, offset); @@ -173,18 +221,18 @@ void Ws2812Clock(void) Ws2812StripShow(); } -void Ws2812GradientColor(uint8_t schemenr, struct WsColor* mColor, uint16_t range, uint16_t gradRange, uint16_t i) +void Ws2812GradientColor(uint32_t schemenr, struct WsColor* mColor, uint32_t range, uint32_t gradRange, uint32_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 = kSchemes[schemenr]; - uint16_t curRange = i / range; - uint16_t rangeIndex = i % range; - uint16_t colorIndex = rangeIndex / gradRange; - uint16_t start = colorIndex; - uint16_t end = colorIndex +1; + uint32_t curRange = i / range; + uint32_t rangeIndex = i % range; + uint32_t colorIndex = rangeIndex / gradRange; + uint32_t start = colorIndex; + uint32_t end = colorIndex +1; if (curRange % 2 != 0) { start = (scheme.count -1) - start; end = (scheme.count -1) - end; @@ -198,7 +246,7 @@ void Ws2812GradientColor(uint8_t schemenr, struct WsColor* mColor, uint16_t rang mColor->blue = (uint8_t)fmyBlu; } -void Ws2812Gradient(uint8_t schemenr) +void Ws2812Gradient(uint32_t schemenr) { /* * This routine courtesy Tony DiCola (Adafruit) @@ -213,13 +261,13 @@ void Ws2812Gradient(uint8_t schemenr) #endif ColorScheme scheme = kSchemes[schemenr]; - if (scheme.count < 2) return; + if (scheme.count < 2) { return; } - uint8_t repeat = kWsRepeat[Settings.light_width]; // number of scheme.count per ledcount - uint16_t range = (uint16_t)ceil((float)Settings.light_pixels / (float)repeat); - uint16_t gradRange = (uint16_t)ceil((float)range / (float)(scheme.count - 1)); - uint16_t speed = ((Settings.light_speed * 2) -1) * (STATES / 10); - uint16_t offset = speed > 0 ? strip_timer_counter / speed : 0; + uint32_t repeat = kWsRepeat[Settings.light_width]; // number of scheme.count per ledcount + uint32_t range = (uint32_t)ceil((float)Settings.light_pixels / (float)repeat); + uint32_t gradRange = (uint32_t)ceil((float)range / (float)(scheme.count - 1)); + uint32_t speed = ((Settings.light_speed * 2) -1) * (STATES / 10); + uint32_t offset = speed > 0 ? Light.strip_timer_counter / speed : 0; WsColor oldColor, currentColor; Ws2812GradientColor(schemenr, &oldColor, range, gradRange, offset); @@ -230,9 +278,9 @@ void Ws2812Gradient(uint8_t schemenr) } if (Settings.light_speed > 0) { // Blend old and current color based on time for smooth movement. - c.R = map(strip_timer_counter % speed, 0, speed, oldColor.red, currentColor.red); - c.G = map(strip_timer_counter % speed, 0, speed, oldColor.green, currentColor.green); - c.B = map(strip_timer_counter % speed, 0, speed, oldColor.blue, currentColor.blue); + c.R = map(Light.strip_timer_counter % speed, 0, speed, oldColor.red, currentColor.red); + c.G = map(Light.strip_timer_counter % speed, 0, speed, oldColor.green, currentColor.green); + c.B = map(Light.strip_timer_counter % speed, 0, speed, oldColor.blue, currentColor.blue); } else { // No animation, just use the current color. @@ -246,7 +294,7 @@ void Ws2812Gradient(uint8_t schemenr) Ws2812StripShow(); } -void Ws2812Bars(uint8_t schemenr) +void Ws2812Bars(uint32_t schemenr) { /* * This routine courtesy Tony DiCola (Adafruit) @@ -259,20 +307,19 @@ void Ws2812Bars(uint8_t schemenr) #else RgbColor c; #endif - uint16_t i; ColorScheme scheme = kSchemes[schemenr]; - uint16_t maxSize = Settings.light_pixels / scheme.count; - if (kWidth[Settings.light_width] > maxSize) maxSize = 0; + uint32_t maxSize = Settings.light_pixels / scheme.count; + if (kWidth[Settings.light_width] > maxSize) { maxSize = 0; } - uint16_t speed = ((Settings.light_speed * 2) -1) * (STATES / 10); - uint8_t offset = speed > 0 ? strip_timer_counter / speed : 0; + uint32_t speed = ((Settings.light_speed * 2) -1) * (STATES / 10); + uint32_t offset = (speed > 0) ? Light.strip_timer_counter / speed : 0; WsColor mcolor[scheme.count]; memcpy(mcolor, scheme.colors, sizeof(mcolor)); float dimmer = 100 / (float)Settings.light_dimmer; - for (i = 0; i < scheme.count; i++) { + for (uint32_t i = 0; i < scheme.count; i++) { float fmyRed = (float)mcolor[i].red / dimmer; float fmyGrn = (float)mcolor[i].green / dimmer; float fmyBlu = (float)mcolor[i].blue / dimmer; @@ -280,9 +327,9 @@ void Ws2812Bars(uint8_t schemenr) mcolor[i].green = (uint8_t)fmyGrn; mcolor[i].blue = (uint8_t)fmyBlu; } - uint8_t colorIndex = offset % scheme.count; - for (i = 0; i < Settings.light_pixels; i++) { - if (maxSize) colorIndex = ((i + offset) % (scheme.count * kWidth[Settings.light_width])) / kWidth[Settings.light_width]; + uint32_t colorIndex = offset % scheme.count; + for (uint32_t i = 0; i < Settings.light_pixels; i++) { + if (maxSize) { colorIndex = ((i + offset) % (scheme.count * kWidth[Settings.light_width])) / kWidth[Settings.light_width]; } c.R = mcolor[colorIndex].red; c.G = mcolor[colorIndex].green; c.B = mcolor[colorIndex].blue; @@ -291,29 +338,14 @@ void Ws2812Bars(uint8_t schemenr) Ws2812StripShow(); } -/*********************************************************************************************\ - * Public -\*********************************************************************************************/ - -void Ws2812Init(void) -{ -#ifdef USE_WS2812_DMA - strip = new NeoPixelBus(WS2812_MAX_LEDS); // For Esp8266, the Pin is omitted and it uses GPIO3 due to DMA hardware use. -#else // USE_WS2812_DMA - strip = new NeoPixelBus(WS2812_MAX_LEDS, pin[GPIO_WS2812]); -#endif // USE_WS2812_DMA - strip->Begin(); - Ws2812Clear(); -} - void Ws2812Clear(void) { strip->ClearTo(0); strip->Show(); - ws_show_next = 1; + Ws2812.show_next = 1; } -void Ws2812SetColor(uint16_t led, uint8_t red, uint8_t green, uint8_t blue, uint8_t white) +void Ws2812SetColor(uint32_t led, uint8_t red, uint8_t green, uint8_t blue, uint8_t white) { #if (USE_WS2812_CTYPE > NEO_3LED) RgbwColor lcolor; @@ -334,23 +366,13 @@ void Ws2812SetColor(uint16_t led, uint8_t red, uint8_t green, uint8_t blue, uint } } - if (!ws_suspend_update) { + if (!Ws2812.suspend_update) { strip->Show(); - ws_show_next = 1; + Ws2812.show_next = 1; } } -void Ws2812ForceSuspend (void) { - ws_suspend_update = true; -} - -void Ws2812ForceUpdate (void) { - ws_suspend_update = false; - strip->Show(); - ws_show_next = 1; -} - -char* Ws2812GetColor(uint16_t led, char* scolor) +char* Ws2812GetColor(uint32_t led, char* scolor) { uint8_t sl_ledcolor[4]; @@ -364,7 +386,7 @@ char* Ws2812GetColor(uint16_t led, char* scolor) sl_ledcolor[1] = lcolor.G; sl_ledcolor[2] = lcolor.B; scolor[0] = '\0'; - for (uint32_t i = 0; i < light_subtype; i++) { + for (uint32_t i = 0; i < Light.subtype; i++) { if (Settings.flag.decimal_text) { snprintf_P(scolor, 25, PSTR("%s%s%d"), scolor, (i > 0) ? "," : "", sl_ledcolor[i]); } else { @@ -374,13 +396,42 @@ char* Ws2812GetColor(uint16_t led, char* scolor) return scolor; } -void Ws2812ShowScheme(uint8_t scheme) +/*********************************************************************************************\ + * Public - used by scripter only +\*********************************************************************************************/ + +void Ws2812ForceSuspend (void) { + Ws2812.suspend_update = true; +} + +void Ws2812ForceUpdate (void) +{ + Ws2812.suspend_update = false; + strip->Show(); + Ws2812.show_next = 1; +} + +/********************************************************************************************/ + +bool Ws2812SetChannels(void) +{ + uint8_t *cur_col = (uint8_t*)XdrvMailbox.data; + + Ws2812SetColor(0, cur_col[0], cur_col[1], cur_col[2], cur_col[3]); + + return true; +} + +void Ws2812ShowScheme(void) +{ + uint32_t scheme = Settings.light_scheme - Ws2812.scheme_offset; + switch (scheme) { case 0: // Clock - if ((1 == state_250mS) || (ws_show_next)) { + if ((1 == state_250mS) || (Ws2812.show_next)) { Ws2812Clock(); - ws_show_next = 0; + Ws2812.show_next = 0; } break; default: @@ -389,10 +440,118 @@ void Ws2812ShowScheme(uint8_t scheme) } else { Ws2812Bars(scheme -1); } - ws_show_next = 1; + Ws2812.show_next = 1; break; } } +void Ws2812ModuleSelected(void) +{ + if (pin[GPIO_WS2812] < 99) { // RGB led + + // For DMA, the Pin is ignored as it uses GPIO3 due to DMA hardware use. + strip = new NeoPixelBus(WS2812_MAX_LEDS, pin[GPIO_WS2812]); + strip->Begin(); + + Ws2812Clear(); + + Ws2812.scheme_offset = Light.max_scheme +1; + Light.max_scheme += WS2812_SCHEMES; + +#if (USE_WS2812_CTYPE > NEO_3LED) + light_type = LT_RGBW; +#else + light_type = LT_RGB; +#endif + light_flg = XLGT_01; + } +} + +/********************************************************************************************/ + +void CmndLed(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= Settings.light_pixels)) { + if (XdrvMailbox.data_len > 0) { + char *p; + uint16_t idx = XdrvMailbox.index; + Ws2812ForceSuspend(); + for (char *color = strtok_r(XdrvMailbox.data, " ", &p); color; color = strtok_r(nullptr, " ", &p)) { + if (LightColorEntry(color, strlen(color))) { + Ws2812SetColor(idx, Light.entry_color[0], Light.entry_color[1], Light.entry_color[2], Light.entry_color[3]); + idx++; + if (idx > Settings.light_pixels) { break; } + } else { + break; + } + } + Ws2812ForceUpdate(); + } + char scolor[LIGHT_COLOR_SIZE]; + ResponseCmndIdxChar(Ws2812GetColor(XdrvMailbox.index, scolor)); + } +} + +void CmndPixels(void) +{ + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= WS2812_MAX_LEDS)) { + Settings.light_pixels = XdrvMailbox.payload; + Settings.light_rotation = 0; + Ws2812Clear(); + Light.update = true; + } + ResponseCmndNumber(Settings.light_pixels); +} + +void CmndRotation(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < Settings.light_pixels)) { + Settings.light_rotation = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.light_rotation); +} + +void CmndWidth(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 4)) { + if (1 == XdrvMailbox.index) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 4)) { + Settings.light_width = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.light_width); + } else { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 32)) { + Settings.ws_width[XdrvMailbox.index -2] = XdrvMailbox.payload; + } + ResponseCmndIdxNumber(Settings.ws_width[XdrvMailbox.index -2]); + } + } +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xlgt01(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_SET_CHANNELS: + result = Ws2812SetChannels(); + break; + case FUNC_SET_SCHEME: + Ws2812ShowScheme(); + break; + case FUNC_COMMAND: + result = DecodeCommand(kWs2812Commands, Ws2812Command); + break; + case FUNC_MODULE_INIT: + Ws2812ModuleSelected(); + break; + } + return result; +} + #endif // USE_WS2812 -#endif // USE_LIGHT +#endif // USE_LIGHT \ No newline at end of file diff --git a/sonoff/xlgt_02_my92x1.ino b/sonoff/xlgt_02_my92x1.ino new file mode 100644 index 000000000..551249a01 --- /dev/null +++ b/sonoff/xlgt_02_my92x1.ino @@ -0,0 +1,165 @@ +/* + xlgt_02_my92x1.ino - led support for Sonoff-Tasmota + + Copyright (C) 2019 Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef USE_LIGHT +#ifdef USE_MY92X1 +/*********************************************************************************************\ + * Sonoff B1 and AiLight inspired by OpenLight https://github.com/icamgo/noduino-sdk +\*********************************************************************************************/ + +#define XLGT_02 2 + +struct MY92X1 { + uint8_t pdi_pin = 0; + uint8_t pdcki_pin = 0; + uint8_t model = 0; +} My92x1; + +extern "C" { + void os_delay_us(unsigned int); +} + +void LightDiPulse(uint8_t times) +{ + for (uint32_t i = 0; i < times; i++) { + digitalWrite(My92x1.pdi_pin, HIGH); + digitalWrite(My92x1.pdi_pin, LOW); + } +} + +void LightDckiPulse(uint8_t times) +{ + for (uint32_t i = 0; i < times; i++) { + digitalWrite(My92x1.pdcki_pin, HIGH); + digitalWrite(My92x1.pdcki_pin, LOW); + } +} + +void LightMy92x1Write(uint8_t data) +{ + for (uint32_t i = 0; i < 4; i++) { // Send 8bit Data + digitalWrite(My92x1.pdcki_pin, LOW); + digitalWrite(My92x1.pdi_pin, (data & 0x80)); + digitalWrite(My92x1.pdcki_pin, HIGH); + data = data << 1; + digitalWrite(My92x1.pdi_pin, (data & 0x80)); + digitalWrite(My92x1.pdcki_pin, LOW); + digitalWrite(My92x1.pdi_pin, LOW); + data = data << 1; + } +} + +void LightMy92x1Init(void) +{ + uint8_t chips[3] = { 1, 2, 2 }; + + LightDckiPulse(chips[My92x1.model] * 32); // Clear all duty register + os_delay_us(12); // TStop > 12us. + // Send 12 DI pulse, after 6 pulse's falling edge store duty data, and 12 + // pulse's rising edge convert to command mode. + LightDiPulse(12); + os_delay_us(12); // Delay >12us, begin send CMD data + for (uint32_t n = 0; n < chips[My92x1.model]; n++) { // Send CMD data + LightMy92x1Write(0x18); // ONE_SHOT_DISABLE, REACTION_FAST, BIT_WIDTH_8, FREQUENCY_DIVIDE_1, SCATTER_APDM + } + os_delay_us(12); // TStart > 12us. Delay 12 us. + // Send 16 DI pulse, at 14 pulse's falling edge store CMD data, and + // at 16 pulse's falling edge convert to duty mode. + LightDiPulse(16); + os_delay_us(12); // TStop > 12us. +} + +void LightMy92x1Duty(uint8_t duty_r, uint8_t duty_g, uint8_t duty_b, uint8_t duty_w, uint8_t duty_c) +{ + uint8_t channels[3] = { 4, 6, 6 }; + + uint8_t duty[3][6] = {{ duty_r, duty_g, duty_b, duty_w, 0, 0 }, // Definition for RGBW channels + { duty_w, duty_c, 0, duty_g, duty_r, duty_b }, // Definition for RGBWC channels + { duty_r, duty_g, duty_b, duty_w, duty_w, duty_w }}; // Definition for RGBWWW channels as used in Lohas which uses up to 3 CW channels + + os_delay_us(12); // TStop > 12us. + for (uint32_t channel = 0; channel < channels[My92x1.model]; channel++) { + LightMy92x1Write(duty[My92x1.model][channel]); // Send 8bit Data + } + os_delay_us(12); // TStart > 12us. Ready for send DI pulse. + LightDiPulse(8); // Send 8 DI pulse. After 8 pulse falling edge, store old data. + os_delay_us(12); // TStop > 12us. +} + +/********************************************************************************************/ + +bool My92x1SetChannels(void) +{ + uint8_t *cur_col = (uint8_t*)XdrvMailbox.data; + + LightMy92x1Duty(cur_col[0], cur_col[1], cur_col[2], cur_col[3], cur_col[4]); + + return true; +} + +void My92x1ModuleSelected(void) +{ + if ((pin[GPIO_DCKI] < 99) && (pin[GPIO_DI] < 99)) { + My92x1.pdi_pin = pin[GPIO_DI]; + My92x1.pdcki_pin = pin[GPIO_DCKI]; + + pinMode(My92x1.pdi_pin, OUTPUT); + pinMode(My92x1.pdcki_pin, OUTPUT); + digitalWrite(My92x1.pdi_pin, LOW); + digitalWrite(My92x1.pdcki_pin, LOW); + + My92x1.model = 2; + light_type = LT_RGBW; // RGBW (2 chips) as used in Lohas + if (AILIGHT == my_module_type) { // RGBW (1 chip) as used in Ailight + My92x1.model = 0; +// light_type = LT_RGBW; + } + else if (SONOFF_B1 == my_module_type) { // RGBWC (2 chips) as used in Sonoff B1 + My92x1.model = 1; + light_type = LT_RGBWC; + } + + LightMy92x1Init(); + + light_flg = XLGT_02; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DBG: MY29x1 Found")); + } +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xlgt02(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_SET_CHANNELS: + result = My92x1SetChannels(); + break; + case FUNC_MODULE_INIT: + My92x1ModuleSelected(); + break; + } + return result; +} + +#endif // USE_MY92X1 +#endif // USE_LIGHT \ No newline at end of file diff --git a/sonoff/xlgt_03_sm16716.ino b/sonoff/xlgt_03_sm16716.ino new file mode 100644 index 000000000..d12636e85 --- /dev/null +++ b/sonoff/xlgt_03_sm16716.ino @@ -0,0 +1,200 @@ +/* + xlgt_03_sm16716.ino - sm16716 three channel led support for Sonoff-Tasmota + + Copyright (C) 2019 Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef USE_LIGHT +#ifdef USE_SM16716 +/*********************************************************************************************\ + * SM16716 - Controlling RGB over a synchronous serial line + * Copyright (C) 2019 Gabor Simon + * + * Source: https://community.home-assistant.io/t/cheap-uk-wifi-bulbs-with-tasmota-teardown-help-tywe3s/40508/27 +\*********************************************************************************************/ + +#define XLGT_03 3 + +#define D_LOG_SM16716 "SM16716: " + +struct SM16716 { + uint8_t pin_clk = 0; + uint8_t pin_dat = 0; + uint8_t pin_sel = 0; + bool enabled = false; +} Sm16716; + +void SM16716_SendBit(uint8_t v) +{ + /* NOTE: + * According to the spec sheet, max freq is 30 MHz, that is 16.6 ns per high/low half of the + * clk square wave. That is less than the overhead of 'digitalWrite' at this clock rate, + * so no additional delays are needed yet. */ + + digitalWrite(Sm16716.pin_dat, (v != 0) ? HIGH : LOW); + //delayMicroseconds(1); + digitalWrite(Sm16716.pin_clk, HIGH); + //delayMicroseconds(1); + digitalWrite(Sm16716.pin_clk, LOW); +} + +void SM16716_SendByte(uint8_t v) +{ + uint8_t mask; + + for (mask = 0x80; mask; mask >>= 1) { + SM16716_SendBit(v & mask); + } +} + +void SM16716_Update(uint8_t duty_r, uint8_t duty_g, uint8_t duty_b) +{ + if (Sm16716.pin_sel < 99) { + bool should_enable = (duty_r | duty_g | duty_b); + if (!Sm16716.enabled && should_enable) { + DEBUG_DRIVER_LOG(PSTR(D_LOG_SM16716 "turning color on")); + Sm16716.enabled = true; + digitalWrite(Sm16716.pin_sel, HIGH); + // in testing I found it takes a minimum of ~380us to wake up the chip + // tested on a Merkury RGBW with an SM726EB + delayMicroseconds(1000); + SM16716_Init(); + } + else if (Sm16716.enabled && !should_enable) { + DEBUG_DRIVER_LOG(PSTR(D_LOG_SM16716 "turning color off")); + Sm16716.enabled = false; + digitalWrite(Sm16716.pin_sel, LOW); + } + } + DEBUG_DRIVER_LOG(PSTR(D_LOG_SM16716 "Update; rgb=%02x%02x%02x"), duty_r, duty_g, duty_b); + + // send start bit + SM16716_SendBit(1); + SM16716_SendByte(duty_r); + SM16716_SendByte(duty_g); + SM16716_SendByte(duty_b); + + // send a 'do it' pulse + // (if multiple chips are chained, each one processes the 1st '1rgb' 25-bit block and + // passes on the rest, right until the one starting with 0) + //SM16716_Init(); + SM16716_SendBit(0); + SM16716_SendByte(0); + SM16716_SendByte(0); + SM16716_SendByte(0); +} + +/* +bool SM16716_ModuleSelected(void) +{ + Sm16716.pin_clk = pin[GPIO_SM16716_CLK]; + Sm16716.pin_dat = pin[GPIO_SM16716_DAT]; + Sm16716.pin_sel = pin[GPIO_SM16716_SEL]; + DEBUG_DRIVER_LOG(PSTR(D_LOG_SM16716 "ModuleSelected; clk_pin=%d, dat_pin=%d)"), Sm16716.pin_clk, Sm16716.pin_dat); + return (Sm16716.pin_clk < 99) && (Sm16716.pin_dat < 99); +} +*/ + +void SM16716_Init(void) +{ + for (uint32_t t_init = 0; t_init < 50; ++t_init) { + SM16716_SendBit(0); + } +} + +/********************************************************************************************/ + +bool Sm16716SetChannels(void) +{ +/* + // handle any PWM pins, skipping the first 3 values for sm16716 + for (uint32_t i = 3; i < Light.subtype; i++) { + if (pin[GPIO_PWM1 +i-3] < 99) { + //AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION "Cur_Col%d 10 bits %d, Pwm%d %d"), i, cur_col[i], i+1, curcol); + analogWrite(pin[GPIO_PWM1 +i-3], bitRead(pwm_inverted, i-3) ? Settings.pwm_range - cur_col_10bits[i] : cur_col_10bits[i]); + } + } +*/ + // handle sm16716 update + uint8_t *cur_col = (uint8_t*)XdrvMailbox.data; + + SM16716_Update(cur_col[0], cur_col[1], cur_col[2]); + + return true; +} + +void Sm16716ModuleSelected(void) +{ + if ((pin[GPIO_SM16716_CLK] < 99) && (pin[GPIO_SM16716_DAT] < 99)) { + Sm16716.pin_clk = pin[GPIO_SM16716_CLK]; + Sm16716.pin_dat = pin[GPIO_SM16716_DAT]; + Sm16716.pin_sel = pin[GPIO_SM16716_SEL]; + +/* + // init PWM + for (uint32_t i = 0; i < Light.subtype; i++) { + Settings.pwm_value[i] = 0; // Disable direct PWM control + if (pin[GPIO_PWM1 +i] < 99) { + pinMode(pin[GPIO_PWM1 +i], OUTPUT); + } + } +*/ + + // init sm16716 + pinMode(Sm16716.pin_clk, OUTPUT); + digitalWrite(Sm16716.pin_clk, LOW); + + pinMode(Sm16716.pin_dat, OUTPUT); + digitalWrite(Sm16716.pin_dat, LOW); + + if (Sm16716.pin_sel < 99) { + pinMode(Sm16716.pin_sel, OUTPUT); + digitalWrite(Sm16716.pin_sel, LOW); + // no need to call SM16716_Init here, it will be called after sel goes HIGH + } else { + // no sel pin means you have an 'always on' chip, so init right away + SM16716_Init(); + } + + LightPwmOffset(LST_RGB); // Handle any PWM pins, skipping the first 3 color values for sm16716 + light_type += LST_RGB; // Add RGB to be controlled by sm16716 + light_flg = XLGT_03; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DBG: SM16716 Found")); + } +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xlgt03(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_SET_CHANNELS: + result = Sm16716SetChannels(); + break; + case FUNC_MODULE_INIT: + Sm16716ModuleSelected(); + break; + } + return result; +} + +#endif // USE_SM16716 +#endif // USE_LIGHT + diff --git a/sonoff/xlgt_04_sm2135.ino b/sonoff/xlgt_04_sm2135.ino new file mode 100644 index 000000000..21755d656 --- /dev/null +++ b/sonoff/xlgt_04_sm2135.ino @@ -0,0 +1,172 @@ +/* + xlgt_04_sm2135.ino - sm2135 five channel led support for Sonoff-Tasmota + + Copyright (C) 2019 Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef USE_LIGHT +#ifdef USE_SM2135 +/*********************************************************************************************\ + * SM2135 RGBCW Led bulbs like some Action LSC SmartLed + * + * {"NAME":"LSC RGBCW LED","GPIO":[0,0,0,0,0,0,0,0,181,0,180,0,0],"FLAG":0,"BASE":18} +\*********************************************************************************************/ + +#define XLGT_04 4 + +#define SM2135_ADDR_MC 0xC0 // Max current register +#define SM2135_ADDR_CH 0xC1 // RGB or CW channel select register +#define SM2135_ADDR_R 0xC2 // Red color +#define SM2135_ADDR_G 0xC3 // Green color +#define SM2135_ADDR_B 0xC4 // Blue color +#define SM2135_ADDR_C 0xC5 // Cold +#define SM2135_ADDR_W 0xC6 // Warm + +#define SM2135_RGB 0x00 // RGB channel +#define SM2135_CW 0x80 // CW channel (Chip default) + +#define SM2135_10MA 0x00 +#define SM2135_15MA 0x01 +#define SM2135_20MA 0x02 // RGB max current (Chip default) +#define SM2135_25MA 0x03 +#define SM2135_30MA 0x04 // CW max current (Chip default) +#define SM2135_35MA 0x05 +#define SM2135_40MA 0x06 +#define SM2135_45MA 0x07 // Max value for RGB +#define SM2135_50MA 0x08 +#define SM2135_55MA 0x09 +#define SM2135_60MA 0x0A + +// RGB current CW current +const uint8_t SM2135_CURRENT = (SM2135_20MA << 4) | SM2135_10MA; + +struct SM2135 { + uint8_t clk = 0; + uint8_t data = 0; +} Sm2135; + +uint8_t Sm2135Write(uint8_t data) +{ + for (uint32_t i = 0; i < 8; i++) { + digitalWrite(Sm2135.clk, LOW); + digitalWrite(Sm2135.data, (data & 0x80)); + digitalWrite(Sm2135.clk, HIGH); + data = data << 1; + } + digitalWrite(Sm2135.clk, LOW); + digitalWrite(Sm2135.data, HIGH); + pinMode(Sm2135.data, INPUT); + digitalWrite(Sm2135.clk, HIGH); + uint8_t ack = digitalRead(Sm2135.data); + pinMode(Sm2135.data, OUTPUT); + return ack; +} + +void Sm2135Send(uint8_t *buffer, uint8_t size) +{ + digitalWrite(Sm2135.data, LOW); + for (uint32_t i = 0; i < size; i++) { + Sm2135Write(buffer[i]); + } + digitalWrite(Sm2135.clk, LOW); + digitalWrite(Sm2135.clk, HIGH); + digitalWrite(Sm2135.data, HIGH); +} + +/********************************************************************************************/ + +bool Sm2135SetChannels(void) +{ + uint8_t *cur_col = (uint8_t*)XdrvMailbox.data; + uint8_t data[6]; + + if ((0 == cur_col[0]) && (0 == cur_col[1]) && (0 == cur_col[2])) { + // No color so must be Cold/Warm +/* + if ((cur_col[3] + cur_col[4]) >= (1 * 256)) { + // Scale down to 255 total to fix max power usage of 9W (=40mA) + +// cur_col[3] >>= 1; // Divide by 2 +// cur_col[4] >>= 1; // Divide by 2 + } +*/ + data[0] = SM2135_ADDR_MC; + data[1] = SM2135_CURRENT; + data[2] = SM2135_CW; + Sm2135Send(data, 3); + delay(1); + data[0] = SM2135_ADDR_C; + data[1] = cur_col[4]; // Warm + data[2] = cur_col[3]; // Cold + Sm2135Send(data, 3); + } else { + // Color +/* + if ((cur_col[0] + cur_col[1] + cur_col[2]) >= (3 * 256)) { + // Scale down to 765 total to fix max power usage of 9W + // Currently not needed with setting 3 x 15mA = 45mA = 11W = 765 + } +*/ + data[0] = SM2135_ADDR_MC; + data[1] = SM2135_CURRENT; + data[2] = SM2135_RGB; + data[3] = cur_col[1]; // Green + data[4] = cur_col[0]; // Red + data[5] = cur_col[2]; // Blue + Sm2135Send(data, 6); + } + + return true; +} + +void Sm2135ModuleSelected(void) +{ + if ((pin[GPIO_SM2135_CLK] < 99) && (pin[GPIO_SM2135_DAT] < 99)) { + Sm2135.clk = pin[GPIO_SM2135_CLK]; + Sm2135.data = pin[GPIO_SM2135_DAT]; + + pinMode(Sm2135.data, OUTPUT); + digitalWrite(Sm2135.data, HIGH); + pinMode(Sm2135.clk, OUTPUT); + digitalWrite(Sm2135.clk, HIGH); + + light_type = LT_RGBWC; + light_flg = XLGT_04; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DBG: SM2135 Found")); + } +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xlgt04(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_SET_CHANNELS: + result = Sm2135SetChannels(); + break; + case FUNC_MODULE_INIT: + Sm2135ModuleSelected(); + break; + } + return result; +} + +#endif // USE_SM2135 +#endif // USE_LIGHT diff --git a/sonoff/xlgt_05_sonoff_l1.ino b/sonoff/xlgt_05_sonoff_l1.ino new file mode 100644 index 000000000..f9f49aaa5 --- /dev/null +++ b/sonoff/xlgt_05_sonoff_l1.ino @@ -0,0 +1,258 @@ +/* + xlgt_05_sonoff_l1.ino - Sonoff L1 led support for Sonoff-Tasmota + + Copyright (C) 2019 Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef USE_LIGHT +#ifdef USE_SONOFF_L1 +/*********************************************************************************************\ + * Sonoff L1 +\*********************************************************************************************/ + +#define XLGT_05 5 + +#define SONOFF_L1_BUFFER_SIZE 140 + +#define SONOFF_L1_MODE_COLORFUL 1 // [Color key] Colorful (static color) +#define SONOFF_L1_MODE_COLORFUL_GRADIENT 2 // [SMOOTH] Colorful Gradient +#define SONOFF_L1_MODE_COLORFUL_BREATH 3 // [FADE] Colorful Breath +#define SONOFF_L1_MODE_DIY_GRADIENT 4 // DIY Gradient (fade in and out) [Speed 1- 100, color] +#define SONOFF_L1_MODE_DIY_PULSE 5 // DIY Pulse (faster fade in and out) [Speed 1- 100, color] +#define SONOFF_L1_MODE_DIY_BREATH 6 // DIY Breath (toggle on/off) [Speed 1- 100, color] +#define SONOFF_L1_MODE_DIY_STROBE 7 // DIY Strobe (faster toggle on/off) [Speed 1- 100, color] +#define SONOFF_L1_MODE_RGB_GRADIENT 8 // RGB Gradient +#define SONOFF_L1_MODE_RGB_PULSE 9 // [STROBE] RGB Pulse +#define SONOFF_L1_MODE_RGB_BREATH 10 // RGB Breath +#define SONOFF_L1_MODE_RGB_STROBE 11 // [FLASH] RGB strobe +#define SONOFF_L1_MODE_SYNC_TO_MUSIC 12 // Sync to music [Speed 1- 100, sensitivity 1 - 10] + +struct SNFL1 { + uint32_t unlock = 0; + bool receive_ready = true; +} Snfl1; + +/********************************************************************************************/ + +void SnfL1Send(const char *buffer) +{ +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SL1: Send %s"), buffer); + + Serial.print(buffer); + Serial.write(0x1B); + Serial.flush(); +} + +void SnfL1SerialSendOk(void) +{ + char buffer[16]; + snprintf_P(buffer, sizeof(buffer), PSTR("AT+SEND=ok")); + + SnfL1Send(buffer); +} + +bool SnfL1SerialInput(void) +{ + if (serial_in_byte != 0x1B) { + if (serial_in_byte_counter >= 140) { + serial_in_byte_counter = 0; + } + if (serial_in_byte_counter || (!serial_in_byte_counter && ('A' == serial_in_byte))) { // A from AT + serial_in_buffer[serial_in_byte_counter++] = serial_in_byte; + } + } else { + serial_in_buffer[serial_in_byte_counter++] = 0x00; + + // AT+RESULT="sequence":"1554682835320" + // AT+UPDATE="sequence":"34906","switch":"on","light_type":1,"colorR":0,"colorG":16,"colorB":0,"bright":6,"mode":1 + // AT+UPDATE="switch":"on","light_type":1,"colorR":255,"colorG":0,"colorB":0,"bright":6,"mode":1,"speed":100,"sensitive":10 +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SL1: Rcvd %s"), serial_in_buffer); + + if (!strncmp(serial_in_buffer +3, "RESULT", 6)) { + Snfl1.receive_ready = true; + } + else if (!strncmp(serial_in_buffer +3, "UPDATE", 6)) { + char cmnd_dimmer[20]; + char cmnd_color[20]; + char *end_str; + char *string = serial_in_buffer +10; + char *token = strtok_r(string, ",", &end_str); + + bool color_updated[3] = { false, false, false }; + uint8_t current_color[3]; + memcpy(current_color, Settings.light_color, 3); + + bool switch_state = false; + bool is_power_change = false; + bool is_color_change = false; + bool is_brightness_change = false; + + while (token != nullptr) { + char* end_token; + char* token2 = strtok_r(token, ":", &end_token); + char* token3 = strtok_r(nullptr, ":", &end_token); + + if (!strncmp(token2, "\"sequence\"", 10)) { + +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SL1: Rcvd sequence %s"), token3); + + token = nullptr; + } + + else if (!strncmp(token2, "\"switch\"", 8)) { + switch_state = !strncmp(token3, "\"on\"", 4) ? true : false; + +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SL1: Rcvd switch %d (%d)"), switch_state, Light.power); + + is_power_change = (switch_state != Light.power); + } + + else if (!strncmp(token2, "\"color", 6)) { + char color_channel_name = token2[6]; + int color_index; + switch(color_channel_name) + { + case 'R': color_index = 0; + break; + case 'G': color_index = 1; + break; + case 'B': color_index = 2; + break; + } + int color_value = atoi(token3); + current_color[color_index] = color_value; + color_updated[color_index] = true; + + bool all_color_channels_updated = color_updated[0] && color_updated[1] && color_updated[2]; + if (all_color_channels_updated) { + +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SL1: Rcvd color R%d G%d B%d (R%d G%d B%d)"), +// current_color[0], current_color[1], current_color[2], +// Settings.light_color[0], Settings.light_color[1], Settings.light_color[2]); + + is_color_change = (Light.power && (memcmp(current_color, Settings.light_color, 3) != 0)); + } + snprintf_P(cmnd_color, sizeof(cmnd_color), PSTR(D_CMND_COLOR "2 %02x%02x%02x"), current_color[0], current_color[1], current_color[2]); + } + + else if (!strncmp(token2, "\"bright\"", 8)) { + uint8_t dimmer = atoi(token3); + +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SL1: Rcvd dimmer %d (%d)"), dimmer, Settings.light_dimmer); + + is_brightness_change = (Light.power && (dimmer > 0) && (dimmer != Settings.light_dimmer)); + snprintf_P(cmnd_dimmer, sizeof(cmnd_dimmer), PSTR(D_CMND_DIMMER " %d"), dimmer); + } + + token = strtok_r(nullptr, ",", &end_str); + } + + if (is_power_change) { + if (Settings.light_scheme > 0) { + if (!switch_state) { // If power off RC button pressed stop schemes + char cmnd_scheme[20]; + snprintf_P(cmnd_scheme, sizeof(cmnd_scheme), PSTR(D_CMND_SCHEME " 0")); + ExecuteCommand(cmnd_scheme, SRC_SWITCH); + } + } else { + ExecuteCommandPower(1, switch_state, SRC_SWITCH); + } + } + else if (is_brightness_change) { + ExecuteCommand(cmnd_dimmer, SRC_SWITCH); + } + else if (Light.power && is_color_change) { + if (0 == Settings.light_scheme) { // Fix spurious color receptions when scheme > 0 + if (Settings.light_fade) { // Disable fade as RC button colors overrule and are immediate supressing ghost colors + char cmnd_fade[20]; + snprintf_P(cmnd_fade, sizeof(cmnd_fade), PSTR(D_CMND_FADE " 0")); + ExecuteCommand(cmnd_fade, SRC_SWITCH); + } + ExecuteCommand(cmnd_color, SRC_SWITCH); + } + } + } + + SnfL1SerialSendOk(); + + return true; + } + serial_in_byte = 0; + return false; +} + +/********************************************************************************************/ + +bool SnfL1SetChannels(void) +{ + if (Snfl1.receive_ready || TimeReached(Snfl1.unlock)) { + + uint8_t *scale_col = (uint8_t*)XdrvMailbox.topic; + + char buffer[140]; + snprintf_P(buffer, sizeof(buffer), PSTR("AT+UPDATE=\"sequence\":\"%d%03d\",\"switch\":\"%s\",\"light_type\":1,\"colorR\":%d,\"colorG\":%d,\"colorB\":%d,\"bright\":%d,\"mode\":%d"), + LocalTime(), millis()%1000, + Light.power ? "on" : "off", + scale_col[0], scale_col[1], scale_col[2], + light_state.getDimmer(), + SONOFF_L1_MODE_COLORFUL); + + SnfL1Send(buffer); + + Snfl1.unlock = millis() + 500; // Allow time for the RC + Snfl1.receive_ready = false; + } + return true; +} + +void SnfL1ModuleSelected(void) +{ + if (SONOFF_L1 == my_module_type) { + if ((pin[GPIO_RXD] < 99) && (pin[GPIO_TXD] < 99)) { + Settings.flag.mqtt_serial = 0; + baudrate = 19200; + + light_type = LT_RGB; + light_flg = XLGT_05; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("LGT: Sonoff L1 Found")); + } + } +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xlgt05(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_SERIAL: + result = SnfL1SerialInput(); + break; + case FUNC_SET_CHANNELS: + result = SnfL1SetChannels(); + break; + case FUNC_MODULE_INIT: + SnfL1ModuleSelected(); + break; + } + return result; +} + +#endif // USE_SONOFF_L1 +#endif // USE_LIGHT \ No newline at end of file diff --git a/sonoff/xlgt_interface.ino b/sonoff/xlgt_interface.ino new file mode 100644 index 000000000..cb963dc5c --- /dev/null +++ b/sonoff/xlgt_interface.ino @@ -0,0 +1,114 @@ +/* + xlgt_interface.ino - Light driver interface support for Sonoff-Tasmota + + Copyright (C) 2019 Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef USE_LIGHT + +#ifdef XFUNC_PTR_IN_ROM +bool (* const xlgt_func_ptr[])(uint8_t) PROGMEM = { // Light driver Function Pointers +#else +bool (* const xlgt_func_ptr[])(uint8_t) = { // Light driver Function Pointers +#endif + +#ifdef XLGT_01 + &Xlgt01, +#endif + +#ifdef XLGT_02 + &Xlgt02, +#endif + +#ifdef XLGT_03 + &Xlgt03, +#endif + +#ifdef XLGT_04 + &Xlgt04, +#endif + +#ifdef XLGT_05 + &Xlgt05, +#endif + +#ifdef XLGT_06 + &Xlgt06, +#endif + +#ifdef XLGT_07 + &Xlgt07, +#endif + +#ifdef XLGT_08 + &Xlgt08, +#endif + +#ifdef XLGT_09 + &Xlgt09, +#endif + +#ifdef XLGT_10 + &Xlgt10, +#endif + +#ifdef XLGT_11 + &Xlgt11, +#endif + +#ifdef XLGT_12 + &Xlgt12, +#endif + +#ifdef XLGT_13 + &Xlgt13, +#endif + +#ifdef XLGT_14 + &Xlgt14, +#endif + +#ifdef XLGT_15 + &Xlgt15, +#endif + +#ifdef XLGT_16 + &Xlgt16 +#endif +}; + +const uint8_t xlgt_present = sizeof(xlgt_func_ptr) / sizeof(xlgt_func_ptr[0]); // Number of drivers found + +uint8_t xlgt_active = 0; + +bool XlgtCall(uint8_t function) +{ + if (FUNC_MODULE_INIT == function) { + for (uint32_t x = 0; x < xlgt_present; x++) { + xlgt_func_ptr[x](function); + if (light_flg) { + xlgt_active = x; + return true; // Stop further driver investigation + } + } + } + else if (light_flg) { + return xlgt_func_ptr[xlgt_active](function); + } + return false; +} + +#endif // USE_LIGHT diff --git a/sonoff/xnrg_01_hlw8012.ino b/sonoff/xnrg_01_hlw8012.ino index 73f31c91f..5c904f13d 100644 --- a/sonoff/xnrg_01_hlw8012.ino +++ b/sonoff/xnrg_01_hlw8012.ino @@ -41,33 +41,35 @@ #define HLW_SAMPLE_COUNT 10 // Max number of samples per cycle //#define HLW_DEBUG + +struct HLW { #ifdef HLW_DEBUG -unsigned long hlw_debug[HLW_SAMPLE_COUNT]; + unsigned long debug[HLW_SAMPLE_COUNT]; #endif + unsigned long cf_pulse_length = 0; + unsigned long cf_pulse_last_time = 0; + unsigned long cf_power_pulse_length = 0; -unsigned long hlw_cf_pulse_length = 0; -unsigned long hlw_cf_pulse_last_time = 0; -unsigned long hlw_cf_power_pulse_length = 0; + unsigned long cf1_pulse_length = 0; + unsigned long cf1_pulse_last_time = 0; + unsigned long cf1_summed_pulse_length = 0; + unsigned long cf1_pulse_counter = 0; + unsigned long cf1_voltage_pulse_length = 0; + unsigned long cf1_current_pulse_length = 0; -unsigned long hlw_cf1_pulse_length = 0; -unsigned long hlw_cf1_pulse_last_time = 0; -unsigned long hlw_cf1_summed_pulse_length = 0; -unsigned long hlw_cf1_pulse_counter = 0; -unsigned long hlw_cf1_voltage_pulse_length = 0; -unsigned long hlw_cf1_current_pulse_length = 0; + unsigned long energy_period_counter = 0; -unsigned long hlw_energy_period_counter = 0; + unsigned long power_ratio = 0; + unsigned long voltage_ratio = 0; + unsigned long current_ratio = 0; -unsigned long hlw_power_ratio = 0; -unsigned long hlw_voltage_ratio = 0; -unsigned long hlw_current_ratio = 0; - -uint8_t hlw_select_ui_flag = 0; -uint8_t hlw_ui_flag = 1; -uint8_t hlw_model_type = 0; -uint8_t hlw_load_off = 1; -uint8_t hlw_cf1_timer = 0; -uint8_t hlw_power_retry = 0; + uint8_t model_type = 0; + uint8_t cf1_timer = 0; + uint8_t power_retry = 0; + bool select_ui_flag = false; + bool ui_flag = true; + bool load_off = true; +} Hlw; // Fix core 2.5.x ISR not in IRAM Exception #ifndef USE_WS2812_DMA // Collides with Neopixelbus but solves exception @@ -79,34 +81,34 @@ void HlwCfInterrupt(void) // Service Power { unsigned long us = micros(); - if (hlw_load_off) { // Restart plen measurement - hlw_cf_pulse_last_time = us; - hlw_load_off = 0; + if (Hlw.load_off) { // Restart plen measurement + Hlw.cf_pulse_last_time = us; + Hlw.load_off = false; } else { - hlw_cf_pulse_length = us - hlw_cf_pulse_last_time; - hlw_cf_pulse_last_time = us; - hlw_energy_period_counter++; + Hlw.cf_pulse_length = us - Hlw.cf_pulse_last_time; + Hlw.cf_pulse_last_time = us; + Hlw.energy_period_counter++; } - energy_data_valid = 0; + Energy.data_valid[0] = 0; } void HlwCf1Interrupt(void) // Service Voltage and Current { unsigned long us = micros(); - hlw_cf1_pulse_length = us - hlw_cf1_pulse_last_time; - hlw_cf1_pulse_last_time = us; - 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_summed_pulse_length += hlw_cf1_pulse_length; + Hlw.cf1_pulse_length = us - Hlw.cf1_pulse_last_time; + Hlw.cf1_pulse_last_time = us; + 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_summed_pulse_length += Hlw.cf1_pulse_length; #ifdef HLW_DEBUG - hlw_debug[hlw_cf1_pulse_counter] = hlw_cf1_pulse_length; + Hlw.debug[Hlw.cf1_pulse_counter] = Hlw.cf1_pulse_length; #endif - hlw_cf1_pulse_counter++; - if (HLW_SAMPLE_COUNT == hlw_cf1_pulse_counter) { - hlw_cf1_timer = 8; // We need up to HLW_SAMPLE_COUNT samples within 1 second (low current could take up to 0.3 second) + Hlw.cf1_pulse_counter++; + if (HLW_SAMPLE_COUNT == Hlw.cf1_pulse_counter) { + Hlw.cf1_timer = 8; // We need up to HLW_SAMPLE_COUNT samples within 1 second (low current could take up to 0.3 second) } } - energy_data_valid = 0; + Energy.data_valid[0] = 0; } /********************************************************************************************/ @@ -118,97 +120,97 @@ void HlwEvery200ms(void) unsigned long hlw_u = 0; unsigned long hlw_i = 0; - if (micros() - hlw_cf_pulse_last_time > (HLW_POWER_PROBE_TIME * 1000000)) { - hlw_cf_pulse_length = 0; // No load for some time - hlw_load_off = 1; + if (micros() - Hlw.cf_pulse_last_time > (HLW_POWER_PROBE_TIME * 1000000)) { + Hlw.cf_pulse_length = 0; // No load for some time + Hlw.load_off = true; } - hlw_cf_power_pulse_length = hlw_cf_pulse_length; + Hlw.cf_power_pulse_length = Hlw.cf_pulse_length; - if (hlw_cf_power_pulse_length && energy_power_on && !hlw_load_off) { - hlw_w = (hlw_power_ratio * Settings.energy_power_calibration) / hlw_cf_power_pulse_length; // W *10 - energy_active_power = (float)hlw_w / 10; - hlw_power_retry = 1; // Workaround issue #5161 + if (Hlw.cf_power_pulse_length && Energy.power_on && !Hlw.load_off) { + hlw_w = (Hlw.power_ratio * Settings.energy_power_calibration) / Hlw.cf_power_pulse_length ; // W *10 + Energy.active_power[0] = (float)hlw_w / 10; + Hlw.power_retry = 1; // Workaround issue #5161 } else { - if (hlw_power_retry) { - hlw_power_retry--; + if (Hlw.power_retry) { + Hlw.power_retry--; } else { - energy_active_power = 0; + Energy.active_power[0] = 0; } } if (pin[GPIO_NRG_CF1] < 99) { - hlw_cf1_timer++; - if (hlw_cf1_timer >= 8) { - hlw_cf1_timer = 0; - hlw_select_ui_flag = (hlw_select_ui_flag) ? 0 : 1; + Hlw.cf1_timer++; + if (Hlw.cf1_timer >= 8) { + Hlw.cf1_timer = 0; + Hlw.select_ui_flag = (Hlw.select_ui_flag) ? false : true; if (pin[GPIO_NRG_SEL] < 99) { - digitalWrite(pin[GPIO_NRG_SEL], hlw_select_ui_flag); + digitalWrite(pin[GPIO_NRG_SEL], Hlw.select_ui_flag); } - if (hlw_cf1_pulse_counter) { - cf1_pulse_length = hlw_cf1_summed_pulse_length / hlw_cf1_pulse_counter; + if (Hlw.cf1_pulse_counter) { + cf1_pulse_length = Hlw.cf1_summed_pulse_length / Hlw.cf1_pulse_counter; } #ifdef HLW_DEBUG // Debugging for calculating mean and median char stemp[100]; stemp[0] = '\0'; - for (uint32_t i = 0; i < hlw_cf1_pulse_counter; i++) { - snprintf_P(stemp, sizeof(stemp), PSTR("%s %d"), stemp, hlw_debug[i]); + for (uint32_t i = 0; i < Hlw.cf1_pulse_counter; i++) { + snprintf_P(stemp, sizeof(stemp), PSTR("%s %d"), stemp, Hlw.debug[i]); } - for (uint32_t i = 0; i < hlw_cf1_pulse_counter; i++) { - for (uint32_t j = i + 1; j < hlw_cf1_pulse_counter; j++) { - if (hlw_debug[i] > hlw_debug[j]) { // Sort ascending - std::swap(hlw_debug[i], hlw_debug[j]); + for (uint32_t i = 0; i < Hlw.cf1_pulse_counter; i++) { + for (uint32_t j = i + 1; j < Hlw.cf1_pulse_counter; j++) { + if (Hlw.debug[i] > Hlw.debug[j]) { // Sort ascending + std::swap(Hlw.debug[i], Hlw.debug[j]); } } } - unsigned long median = hlw_debug[(hlw_cf1_pulse_counter +1) / 2]; + unsigned long median = Hlw.debug[(Hlw.cf1_pulse_counter +1) / 2]; AddLog_P2(LOG_LEVEL_DEBUG, PSTR("NRG: power %d, ui %d, cnt %d, smpl%s, sum %d, mean %d, median %d"), - hlw_cf_power_pulse_length, hlw_select_ui_flag, hlw_cf1_pulse_counter, stemp, hlw_cf1_summed_pulse_length, cf1_pulse_length, median); + Hlw.cf_power_pulse_length , Hlw.select_ui_flag, Hlw.cf1_pulse_counter, stemp, Hlw.cf1_summed_pulse_length, cf1_pulse_length, median); #endif - if (hlw_select_ui_flag == hlw_ui_flag) { - hlw_cf1_voltage_pulse_length = cf1_pulse_length; + if (Hlw.select_ui_flag == Hlw.ui_flag) { + Hlw.cf1_voltage_pulse_length = cf1_pulse_length; - if (hlw_cf1_voltage_pulse_length && energy_power_on) { // If powered on always provide voltage - hlw_u = (hlw_voltage_ratio * Settings.energy_voltage_calibration) / hlw_cf1_voltage_pulse_length; // V *10 - energy_voltage = (float)hlw_u / 10; + if (Hlw.cf1_voltage_pulse_length && Energy.power_on) { // If powered on always provide voltage + hlw_u = (Hlw.voltage_ratio * Settings.energy_voltage_calibration) / Hlw.cf1_voltage_pulse_length ; // V *10 + Energy.voltage[0] = (float)hlw_u / 10; } else { - energy_voltage = 0; + Energy.voltage[0] = 0; } } else { - hlw_cf1_current_pulse_length = cf1_pulse_length; + Hlw.cf1_current_pulse_length = cf1_pulse_length; - if (hlw_cf1_current_pulse_length && energy_active_power) { // No current if no power being consumed - hlw_i = (hlw_current_ratio * Settings.energy_current_calibration) / hlw_cf1_current_pulse_length; // mA - energy_current = (float)hlw_i / 1000; + if (Hlw.cf1_current_pulse_length && Energy.active_power[0]) { // No current if no power being consumed + hlw_i = (Hlw.current_ratio * Settings.energy_current_calibration) / Hlw.cf1_current_pulse_length; // mA + Energy.current[0] = (float)hlw_i / 1000; } else { - energy_current = 0; + Energy.current[0] = 0; } } - hlw_cf1_summed_pulse_length = 0; - hlw_cf1_pulse_counter = 0; + Hlw.cf1_summed_pulse_length = 0; + Hlw.cf1_pulse_counter = 0; } } } void HlwEverySecond(void) { - if (energy_data_valid > ENERGY_WATCHDOG) { - hlw_cf1_voltage_pulse_length = 0; - hlw_cf1_current_pulse_length = 0; - hlw_cf_power_pulse_length = 0; + if (Energy.data_valid[0] > ENERGY_WATCHDOG) { + Hlw.cf1_voltage_pulse_length = 0; + Hlw.cf1_current_pulse_length = 0; + Hlw.cf_power_pulse_length = 0; } else { unsigned long hlw_len; - if (hlw_energy_period_counter) { - hlw_len = 10000 / hlw_energy_period_counter; - hlw_energy_period_counter = 0; + if (Hlw.energy_period_counter) { + hlw_len = 10000 / Hlw.energy_period_counter; + Hlw.energy_period_counter = 0; if (hlw_len) { - energy_kWhtoday_delta += ((hlw_power_ratio * Settings.energy_power_calibration) / hlw_len) / 36; + Energy.kWhtoday_delta += ((Hlw.power_ratio * Settings.energy_power_calibration) / hlw_len) / 36; EnergyUpdateToday(); } } @@ -223,19 +225,19 @@ void HlwSnsInit(void) Settings.energy_current_calibration = HLW_IREF_PULSE; } - if (hlw_model_type) { - hlw_power_ratio = HJL_PREF; - hlw_voltage_ratio = HJL_UREF; - hlw_current_ratio = HJL_IREF; + if (Hlw.model_type) { + Hlw.power_ratio = HJL_PREF; + Hlw.voltage_ratio = HJL_UREF; + Hlw.current_ratio = HJL_IREF; } else { - hlw_power_ratio = HLW_PREF; - hlw_voltage_ratio = HLW_UREF; - hlw_current_ratio = HLW_IREF; + Hlw.power_ratio = HLW_PREF; + Hlw.voltage_ratio = HLW_UREF; + Hlw.current_ratio = HLW_IREF; } if (pin[GPIO_NRG_SEL] < 99) { pinMode(pin[GPIO_NRG_SEL], OUTPUT); - digitalWrite(pin[GPIO_NRG_SEL], hlw_select_ui_flag); + digitalWrite(pin[GPIO_NRG_SEL], Hlw.select_ui_flag); } if (pin[GPIO_NRG_CF1] < 99) { pinMode(pin[GPIO_NRG_CF1], INPUT_PULLUP); @@ -247,34 +249,32 @@ void HlwSnsInit(void) void HlwDrvInit(void) { - if (!energy_flg) { - hlw_model_type = 0; // HLW8012 - if (pin[GPIO_HJL_CF] < 99) { - pin[GPIO_HLW_CF] = pin[GPIO_HJL_CF]; - pin[GPIO_HJL_CF] = 99; - hlw_model_type = 1; // HJL-01/BL0937 + Hlw.model_type = 0; // HLW8012 + if (pin[GPIO_HJL_CF] < 99) { + pin[GPIO_HLW_CF] = pin[GPIO_HJL_CF]; + pin[GPIO_HJL_CF] = 99; + Hlw.model_type = 1; // HJL-01/BL0937 + } + + if (pin[GPIO_HLW_CF] < 99) { // HLW8012 or HJL-01 based device Power monitor + + Hlw.ui_flag = true; // Voltage on high + if (pin[GPIO_NRG_SEL_INV] < 99) { + pin[GPIO_NRG_SEL] = pin[GPIO_NRG_SEL_INV]; + pin[GPIO_NRG_SEL_INV] = 99; + Hlw.ui_flag = false; // Voltage on low } - if (pin[GPIO_HLW_CF] < 99) { // HLW8012 or HJL-01 based device Power monitor - - hlw_ui_flag = 1; // Voltage on high - if (pin[GPIO_NRG_SEL_INV] < 99) { - pin[GPIO_NRG_SEL] = pin[GPIO_NRG_SEL_INV]; - pin[GPIO_NRG_SEL_INV] = 99; - hlw_ui_flag = 0; // Voltage on low + if (pin[GPIO_NRG_CF1] < 99) { // Voltage and/or Current monitor + if (99 == pin[GPIO_NRG_SEL]) { // Voltage and/or Current selector + Energy.current_available = false; // Assume Voltage } - - if (pin[GPIO_NRG_CF1] < 99) { // Voltage and/or Current monitor - if (99 == pin[GPIO_NRG_SEL]) { // Voltage and/or Current selector - energy_current_available = false; // Assume Voltage - } - } else { - energy_current_available = false; - energy_voltage_available = false; - } - - energy_flg = XNRG_01; + } else { + Energy.current_available = false; + Energy.voltage_available = false; } + + energy_flg = XNRG_01; } } @@ -282,22 +282,22 @@ bool HlwCommand(void) { bool serviced = true; - if ((CMND_POWERCAL == energy_command_code) || (CMND_VOLTAGECAL == energy_command_code) || (CMND_CURRENTCAL == energy_command_code)) { + if ((CMND_POWERCAL == Energy.command_code) || (CMND_VOLTAGECAL == Energy.command_code) || (CMND_CURRENTCAL == Energy.command_code)) { // Service in xdrv_03_energy.ino } - else if (CMND_POWERSET == energy_command_code) { - if (XdrvMailbox.data_len && hlw_cf_power_pulse_length) { - Settings.energy_power_calibration = ((unsigned long)(CharToFloat(XdrvMailbox.data) * 10) * hlw_cf_power_pulse_length) / hlw_power_ratio; + else if (CMND_POWERSET == Energy.command_code) { + if (XdrvMailbox.data_len && Hlw.cf_power_pulse_length ) { + Settings.energy_power_calibration = ((unsigned long)(CharToFloat(XdrvMailbox.data) * 10) * Hlw.cf_power_pulse_length ) / Hlw.power_ratio; } } - else if (CMND_VOLTAGESET == energy_command_code) { - if (XdrvMailbox.data_len && hlw_cf1_voltage_pulse_length) { - Settings.energy_voltage_calibration = ((unsigned long)(CharToFloat(XdrvMailbox.data) * 10) * hlw_cf1_voltage_pulse_length) / hlw_voltage_ratio; + else if (CMND_VOLTAGESET == Energy.command_code) { + if (XdrvMailbox.data_len && Hlw.cf1_voltage_pulse_length ) { + Settings.energy_voltage_calibration = ((unsigned long)(CharToFloat(XdrvMailbox.data) * 10) * Hlw.cf1_voltage_pulse_length ) / Hlw.voltage_ratio; } } - else if (CMND_CURRENTSET == energy_command_code) { - if (XdrvMailbox.data_len && hlw_cf1_current_pulse_length) { - Settings.energy_current_calibration = ((unsigned long)(CharToFloat(XdrvMailbox.data)) * hlw_cf1_current_pulse_length) / hlw_current_ratio; + else if (CMND_CURRENTSET == Energy.command_code) { + if (XdrvMailbox.data_len && Hlw.cf1_current_pulse_length) { + Settings.energy_current_calibration = ((unsigned long)(CharToFloat(XdrvMailbox.data)) * Hlw.cf1_current_pulse_length) / Hlw.current_ratio; } } else serviced = false; // Unknown command @@ -309,28 +309,26 @@ bool HlwCommand(void) * Interface \*********************************************************************************************/ -int Xnrg01(uint8_t function) +bool Xnrg01(uint8_t function) { - int result = 0; + bool result = false; - if (FUNC_PRE_INIT == function) { - HlwDrvInit(); - } - else if (XNRG_01 == energy_flg) { - switch (function) { - case FUNC_INIT: - HlwSnsInit(); - break; - case FUNC_ENERGY_EVERY_SECOND: - HlwEverySecond(); - break; - case FUNC_EVERY_200_MSECOND: - HlwEvery200ms(); - break; - case FUNC_COMMAND: - result = HlwCommand(); - break; - } + switch (function) { + case FUNC_EVERY_200_MSECOND: + HlwEvery200ms(); + break; + case FUNC_ENERGY_EVERY_SECOND: + HlwEverySecond(); + break; + case FUNC_COMMAND: + result = HlwCommand(); + break; + case FUNC_INIT: + HlwSnsInit(); + break; + case FUNC_PRE_INIT: + HlwDrvInit(); + break; } return result; } diff --git a/sonoff/xnrg_02_cse7766.ino b/sonoff/xnrg_02_cse7766.ino index f9d6d81e2..b83e7bcb3 100644 --- a/sonoff/xnrg_02_cse7766.ino +++ b/sonoff/xnrg_02_cse7766.ino @@ -37,21 +37,24 @@ #define CSE_PREF 1000 #define CSE_UREF 100 -uint8_t cse_receive_flag = 0; +struct CSE { + long voltage_cycle = 0; + long current_cycle = 0; + long power_cycle = 0; + long power_cycle_first = 0; + long cf_pulses = 0; + long cf_pulses_last_time = CSE_PULSES_NOT_INITIALIZED; -long voltage_cycle = 0; -long current_cycle = 0; -long power_cycle = 0; -long power_cycle_first = 0; -long cf_pulses = 0; -long cf_pulses_last_time = CSE_PULSES_NOT_INITIALIZED; -uint8_t cse_power_invalid = 0; + uint8_t power_invalid = 0; + bool received = false; +} Cse; void CseReceived(void) { // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 - // 55 5A 02 F7 60 00 03 5A 00 40 10 04 8B 9F 51 A6 58 18 72 75 61 AC A1 30 - Power not valid (load below 5W) - // 55 5A 02 F7 60 00 03 AB 00 40 10 02 60 5D 51 A6 58 03 E9 EF 71 0B 7A 36 + // F2 5A 02 F7 60 00 03 61 00 40 10 05 72 40 51 A6 58 63 10 1B E1 7F 4D 4E - F2 = Power cycle exceeds range - takes too long - No load + // 55 5A 02 F7 60 00 03 5A 00 40 10 04 8B 9F 51 A6 58 18 72 75 61 AC A1 30 - 55 = Ok, 61 = Power not valid (load below 5W) + // 55 5A 02 F7 60 00 03 AB 00 40 10 02 60 5D 51 A6 58 03 E9 EF 71 0B 7A 36 - 55 = Ok, 71 = Ok // Hd Id VCal---- Voltage- ICal---- Current- PCal---- Power--- Ad CF--- Ck uint8_t header = serial_in_buffer[0]; @@ -84,54 +87,54 @@ void CseReceived(void) } uint8_t adjustement = serial_in_buffer[20]; - voltage_cycle = serial_in_buffer[5] << 16 | serial_in_buffer[6] << 8 | serial_in_buffer[7]; - current_cycle = serial_in_buffer[11] << 16 | serial_in_buffer[12] << 8 | serial_in_buffer[13]; - power_cycle = serial_in_buffer[17] << 16 | serial_in_buffer[18] << 8 | serial_in_buffer[19]; - cf_pulses = serial_in_buffer[21] << 8 | serial_in_buffer[22]; + Cse.voltage_cycle = serial_in_buffer[5] << 16 | serial_in_buffer[6] << 8 | serial_in_buffer[7]; + Cse.current_cycle = serial_in_buffer[11] << 16 | serial_in_buffer[12] << 8 | serial_in_buffer[13]; + Cse.power_cycle = serial_in_buffer[17] << 16 | serial_in_buffer[18] << 8 | serial_in_buffer[19]; + Cse.cf_pulses = serial_in_buffer[21] << 8 | serial_in_buffer[22]; - if (energy_power_on) { // Powered on + if (Energy.power_on) { // Powered on if (adjustement & 0x40) { // Voltage valid - energy_voltage = (float)(Settings.energy_voltage_calibration * CSE_UREF) / (float)voltage_cycle; + Energy.voltage[0] = (float)(Settings.energy_voltage_calibration * CSE_UREF) / (float)Cse.voltage_cycle; } if (adjustement & 0x10) { // Power valid - cse_power_invalid = 0; + Cse.power_invalid = 0; if ((header & 0xF2) == 0xF2) { // Power cycle exceeds range - energy_active_power = 0; + Energy.active_power[0] = 0; } else { - if (0 == power_cycle_first) { power_cycle_first = power_cycle; } // Skip first incomplete power_cycle - if (power_cycle_first != power_cycle) { - power_cycle_first = -1; - energy_active_power = (float)(Settings.energy_power_calibration * CSE_PREF) / (float)power_cycle; + if (0 == Cse.power_cycle_first) { Cse.power_cycle_first = Cse.power_cycle; } // Skip first incomplete Cse.power_cycle + if (Cse.power_cycle_first != Cse.power_cycle) { + Cse.power_cycle_first = -1; + Energy.active_power[0] = (float)(Settings.energy_power_calibration * CSE_PREF) / (float)Cse.power_cycle; } else { - energy_active_power = 0; + Energy.active_power[0] = 0; } } } else { - if (cse_power_invalid < Settings.param[P_CSE7766_INVALID_POWER]) { // Allow measurements down to about 1W - cse_power_invalid++; + if (Cse.power_invalid < Settings.param[P_CSE7766_INVALID_POWER]) { // Allow measurements down to about 1W + Cse.power_invalid++; } else { - power_cycle_first = 0; - energy_active_power = 0; // Powered on but no load + Cse.power_cycle_first = 0; + Energy.active_power[0] = 0; // Powered on but no load } } if (adjustement & 0x20) { // Current valid - if (0 == energy_active_power) { - energy_current = 0; + if (0 == Energy.active_power[0]) { + Energy.current[0] = 0; } else { - energy_current = (float)Settings.energy_current_calibration / (float)current_cycle; + Energy.current[0] = (float)Settings.energy_current_calibration / (float)Cse.current_cycle; } } } else { // Powered off - power_cycle_first = 0; - energy_voltage = 0; - energy_active_power = 0; - energy_current = 0; + Cse.power_cycle_first = 0; + Energy.voltage[0] = 0; + Energy.active_power[0] = 0; + Energy.current[0] = 0; } } bool CseSerialInput(void) { - if (cse_receive_flag) { + if (Cse.received) { serial_in_buffer[serial_in_byte_counter++] = serial_in_byte; if (24 == serial_in_byte_counter) { @@ -140,10 +143,10 @@ bool CseSerialInput(void) uint8_t checksum = 0; for (uint32_t i = 2; i < 23; i++) { checksum += serial_in_buffer[i]; } if (checksum == serial_in_buffer[23]) { - energy_data_valid = 0; + Energy.data_valid[0] = 0; CseReceived(); - cse_receive_flag = 0; - return 1; + Cse.received = false; + return true; } else { AddLog_P(LOG_LEVEL_DEBUG, PSTR("CSE: " D_CHECKSUM_FAILURE)); do { // Sync buffer with data (issue #1907 and #3425) @@ -151,52 +154,52 @@ bool CseSerialInput(void) serial_in_byte_counter--; } while ((serial_in_byte_counter > 2) && (0x5A != serial_in_buffer[1])); if (0x5A != serial_in_buffer[1]) { - cse_receive_flag = 0; + Cse.received = false; serial_in_byte_counter = 0; } } } } else { if ((0x5A == serial_in_byte) && (1 == serial_in_byte_counter)) { // 0x5A - Packet header 2 - cse_receive_flag = 1; + Cse.received = true; } else { serial_in_byte_counter = 0; } serial_in_buffer[serial_in_byte_counter++] = serial_in_byte; } serial_in_byte = 0; // Discard - return 0; + return false; } /********************************************************************************************/ void CseEverySecond(void) { - if (energy_data_valid > ENERGY_WATCHDOG) { - voltage_cycle = 0; - current_cycle = 0; - power_cycle = 0; + if (Energy.data_valid[0] > ENERGY_WATCHDOG) { + Cse.voltage_cycle = 0; + Cse.current_cycle = 0; + Cse.power_cycle = 0; } else { long cf_frequency = 0; - if (CSE_PULSES_NOT_INITIALIZED == cf_pulses_last_time) { - cf_pulses_last_time = cf_pulses; // Init after restart + if (CSE_PULSES_NOT_INITIALIZED == Cse.cf_pulses_last_time) { + Cse.cf_pulses_last_time = Cse.cf_pulses; // Init after restart } else { - if (cf_pulses < cf_pulses_last_time) { // Rolled over after 65535 pulses - cf_frequency = (65536 - cf_pulses_last_time) + cf_pulses; + if (Cse.cf_pulses < Cse.cf_pulses_last_time) { // Rolled over after 65535 pulses + cf_frequency = (65536 - Cse.cf_pulses_last_time) + Cse.cf_pulses; } else { - cf_frequency = cf_pulses - cf_pulses_last_time; + cf_frequency = Cse.cf_pulses - Cse.cf_pulses_last_time; } - if (cf_frequency && energy_active_power) { + if (cf_frequency && Energy.active_power[0]) { unsigned long delta = (cf_frequency * Settings.energy_power_calibration) / 36; // prevent invalid load delta steps even checksum is valid (issue #5789): if (delta <= (3680*100/36) * 10 ) { // max load for S31/Pow R2: 3.68kW - cf_pulses_last_time = cf_pulses; - energy_kWhtoday_delta += delta; + Cse.cf_pulses_last_time = Cse.cf_pulses; + Energy.kWhtoday_delta += delta; } else { AddLog_P(LOG_LEVEL_DEBUG, PSTR("CSE: Load overflow")); - cf_pulses_last_time = CSE_PULSES_NOT_INITIALIZED; + Cse.cf_pulses_last_time = CSE_PULSES_NOT_INITIALIZED; } EnergyUpdateToday(); } @@ -206,16 +209,14 @@ void CseEverySecond(void) void CseDrvInit(void) { - if (!energy_flg) { - if ((3 == pin[GPIO_CSE7766_RX]) && (1 == pin[GPIO_CSE7766_TX])) { // As it uses 8E1 currently only hardware serial is supported - baudrate = 4800; - serial_config = SERIAL_8E1; - if (0 == Settings.param[P_CSE7766_INVALID_POWER]) { - Settings.param[P_CSE7766_INVALID_POWER] = CSE_MAX_INVALID_POWER; // SetOption39 1..255 - } - cse_power_invalid = Settings.param[P_CSE7766_INVALID_POWER]; - energy_flg = XNRG_02; + if ((3 == pin[GPIO_CSE7766_RX]) && (1 == pin[GPIO_CSE7766_TX])) { // As it uses 8E1 currently only hardware serial is supported + baudrate = 4800; + serial_config = SERIAL_8E1; + if (0 == Settings.param[P_CSE7766_INVALID_POWER]) { + Settings.param[P_CSE7766_INVALID_POWER] = CSE_MAX_INVALID_POWER; // SetOption39 1..255 } + Cse.power_invalid = Settings.param[P_CSE7766_INVALID_POWER]; + energy_flg = XNRG_02; } } @@ -223,19 +224,19 @@ bool CseCommand(void) { bool serviced = true; - if (CMND_POWERSET == energy_command_code) { - if (XdrvMailbox.data_len && power_cycle) { - Settings.energy_power_calibration = (unsigned long)(CharToFloat(XdrvMailbox.data) * power_cycle) / CSE_PREF; + if (CMND_POWERSET == Energy.command_code) { + if (XdrvMailbox.data_len && Cse.power_cycle) { + Settings.energy_power_calibration = (unsigned long)(CharToFloat(XdrvMailbox.data) * Cse.power_cycle) / CSE_PREF; } } - else if (CMND_VOLTAGESET == energy_command_code) { - if (XdrvMailbox.data_len && voltage_cycle) { - Settings.energy_voltage_calibration = (unsigned long)(CharToFloat(XdrvMailbox.data) * voltage_cycle) / CSE_UREF; + else if (CMND_VOLTAGESET == Energy.command_code) { + if (XdrvMailbox.data_len && Cse.voltage_cycle) { + Settings.energy_voltage_calibration = (unsigned long)(CharToFloat(XdrvMailbox.data) * Cse.voltage_cycle) / CSE_UREF; } } - else if (CMND_CURRENTSET == energy_command_code) { - if (XdrvMailbox.data_len && current_cycle) { - Settings.energy_current_calibration = (unsigned long)(CharToFloat(XdrvMailbox.data) * current_cycle) / 1000; + else if (CMND_CURRENTSET == Energy.command_code) { + if (XdrvMailbox.data_len && Cse.current_cycle) { + Settings.energy_current_calibration = (unsigned long)(CharToFloat(XdrvMailbox.data) * Cse.current_cycle) / 1000; } } else serviced = false; // Unknown command @@ -247,25 +248,23 @@ bool CseCommand(void) * Interface \*********************************************************************************************/ -int Xnrg02(uint8_t function) +bool Xnrg02(uint8_t function) { - int result = 0; + bool result = false; - if (FUNC_PRE_INIT == function) { - CseDrvInit(); - } - else if (XNRG_02 == energy_flg) { - switch (function) { - case FUNC_ENERGY_EVERY_SECOND: - CseEverySecond(); - break; - case FUNC_COMMAND: - result = CseCommand(); - break; - case FUNC_SERIAL: - result = CseSerialInput(); - break; - } + switch (function) { + case FUNC_SERIAL: + result = CseSerialInput(); + break; + case FUNC_ENERGY_EVERY_SECOND: + CseEverySecond(); + break; + case FUNC_COMMAND: + result = CseCommand(); + break; + case FUNC_PRE_INIT: + CseDrvInit(); + break; } return result; } diff --git a/sonoff/xnrg_03_pzem004t.ino b/sonoff/xnrg_03_pzem004t.ino index 1e5d0723b..93657ae01 100644 --- a/sonoff/xnrg_03_pzem004t.ino +++ b/sonoff/xnrg_03_pzem004t.ino @@ -20,16 +20,18 @@ #ifdef USE_ENERGY_SENSOR #ifdef USE_PZEM004T /*********************************************************************************************\ - * PZEM004T - Energy + * PZEM-004T V1 and V2 - Energy * * Source: Victor Ferrer https://github.com/vicfergar/Sonoff-MQTT-OTA-Arduino * Based on: PZEM004T library https://github.com/olehs/PZEM004T * - * Hardware Serial will be selected if GPIO1 = [63 PZEM004 Rx] and GPIO3 = [62 PZEM0XX Tx] + * Hardware Serial will be selected if GPIO1 = [62 PZEM0XX Tx] and GPIO3 = [63 PZEM004 Rx] \*********************************************************************************************/ #define XNRG_03 3 +const uint32_t PZEM_STABILIZE = 30; // Number of seconds to stabilize configuration + #include TasmotaSerial *PzemSerial = nullptr; @@ -56,6 +58,15 @@ TasmotaSerial *PzemSerial = nullptr; /*********************************************************************************************/ +struct PZEM { + float energy = 0; + float last_energy = 0; + uint8_t send_retry = 0; + uint8_t read_state = 0; // Set address + uint8_t phase = 0; + uint8_t address = 0; +} Pzem; + struct PZEMCommand { uint8_t command; uint8_t addr[4]; @@ -63,12 +74,12 @@ struct PZEMCommand { uint8_t crc; }; -IPAddress pzem_ip(192, 168, 1, 1); - uint8_t PzemCrc(uint8_t *data) { uint16_t crc = 0; - for (uint32_t i = 0; i < sizeof(PZEMCommand) -1; i++) crc += *data++; + for (uint32_t i = 0; i < sizeof(PZEMCommand) -1; i++) { + crc += *data++; + } return (uint8_t)(crc & 0xFF); } @@ -77,7 +88,10 @@ void PzemSend(uint8_t cmd) PZEMCommand pzem; pzem.command = cmd; - for (uint32_t i = 0; i < sizeof(pzem.addr); i++) pzem.addr[i] = pzem_ip[i]; + pzem.addr[0] = 192; // Address 192.168.1.1 for Tasmota legacy reason + pzem.addr[1] = 168; + pzem.addr[2] = 1; + pzem.addr[3] = ((PZEM_SET_ADDRESS == cmd) && Pzem.address) ? Pzem.address : 1 + Pzem.phase; pzem.data = 0; uint8_t *bytes = (uint8_t*)&pzem; @@ -85,6 +99,8 @@ void PzemSend(uint8_t cmd) PzemSerial->flush(); PzemSerial->write(bytes, sizeof(pzem)); + + Pzem.address = 0; } bool PzemReceiveReady(void) @@ -100,6 +116,7 @@ bool PzemRecieve(uint8_t resp, float *data) // A1 00 00 0A 00 00 AB - Current (0.1A) // A1 00 00 00 00 00 A1 - No current // A2 00 16 00 00 00 B8 - Power (22W) + // A2 08 98 00 00 00 42 - Power (2200W) // A2 00 00 00 00 00 A2 - No power // A3 00 08 A4 00 00 4F - Energy (2.212kWh) // A3 01 86 9F 00 00 C9 - Energy (99.999kWh) @@ -111,11 +128,7 @@ bool PzemRecieve(uint8_t resp, float *data) while ((len < sizeof(PZEMCommand)) && (millis() - start < PZEM_DEFAULT_READ_TIMEOUT)) { if (PzemSerial->available() > 0) { uint8_t c = (uint8_t)PzemSerial->read(); - if (!c && !len) { - continue; // skip 0 at startup - } - if ((1 == len) && (buffer[0] == c)) { - len--; + if (!len && ((c & 0xF8) != 0xA0)) { // 10100xxx continue; // fix skewed data } buffer[len++] = c; @@ -159,47 +172,69 @@ bool PzemRecieve(uint8_t resp, float *data) const uint8_t pzem_commands[] { PZEM_SET_ADDRESS, PZEM_VOLTAGE, PZEM_CURRENT, PZEM_POWER, PZEM_ENERGY }; const uint8_t pzem_responses[] { RESP_SET_ADDRESS, RESP_VOLTAGE, RESP_CURRENT, RESP_POWER, RESP_ENERGY }; -uint8_t pzem_read_state = 0; -uint8_t pzem_sendRetry = 0; - -void PzemEvery200ms(void) +void PzemEvery250ms(void) { bool data_ready = PzemReceiveReady(); if (data_ready) { float value = 0; - if (PzemRecieve(pzem_responses[pzem_read_state], &value)) { - energy_data_valid = 0; - switch (pzem_read_state) { + if (PzemRecieve(pzem_responses[Pzem.read_state], &value)) { + Energy.data_valid[Pzem.phase] = 0; + switch (Pzem.read_state) { case 1: // Voltage as 230.2V - energy_voltage = value; + Energy.voltage[Pzem.phase] = value; break; case 2: // Current as 17.32A - energy_current = value; + Energy.current[Pzem.phase] = value; break; case 3: // Power as 20W - energy_active_power = value; + Energy.active_power[Pzem.phase] = value; break; case 4: // Total energy as 99999Wh - if (!energy_start || (value < energy_start)) energy_start = value; // Init after restart and hanlde roll-over if any - if (value != energy_start) { - energy_kWhtoday += (unsigned long)((value - energy_start) * 100); - energy_start = value; + Pzem.energy += value; + if (Pzem.phase == Energy.phase_count -1) { + if (Pzem.energy > Pzem.last_energy) { // Handle missed phase + if (uptime > PZEM_STABILIZE) { + EnergyUpdateTotal(Pzem.energy, false); + } + Pzem.last_energy = Pzem.energy; + } + Pzem.energy = 0; } - EnergyUpdateToday(); break; } - pzem_read_state++; - if (5 == pzem_read_state) pzem_read_state = 1; + Pzem.read_state++; + if (5 == Pzem.read_state) { + Pzem.read_state = 1; + } + +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PZM: Retry %d"), 5 - Pzem.send_retry); } } - if (0 == pzem_sendRetry || data_ready) { - pzem_sendRetry = 5; - PzemSend(pzem_commands[pzem_read_state]); + if (0 == Pzem.send_retry || data_ready) { + if (1 == Pzem.read_state) { + if (0 == Pzem.phase) { + Pzem.phase = Energy.phase_count -1; + } else { + Pzem.phase--; + } + +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PZM: Probing address %d, Max phases %d"), Pzem.phase +1, Energy.phase_count); + } + + if (Pzem.address) { + Pzem.read_state = 0; // Set address + } + + Pzem.send_retry = 5; + PzemSend(pzem_commands[Pzem.read_state]); } else { - pzem_sendRetry--; + Pzem.send_retry--; + if ((Energy.phase_count > 1) && (0 == Pzem.send_retry) && (uptime < PZEM_STABILIZE)) { + Energy.phase_count--; // Decrement phases if no response after retry within 30 seconds after restart + } } } @@ -208,7 +243,12 @@ void PzemSnsInit(void) // Software serial init needs to be done here as earlier (serial) interrupts may lead to Exceptions PzemSerial = new TasmotaSerial(pin[GPIO_PZEM004_RX], pin[GPIO_PZEM0XX_TX], 1); if (PzemSerial->begin(9600)) { - if (PzemSerial->hardwareSerial()) { ClaimSerial(); } + if (PzemSerial->hardwareSerial()) { + ClaimSerial(); + } + Energy.phase_count = 3; // Start off with three phases + Pzem.phase = 0; + Pzem.read_state = 1; } else { energy_flg = ENERGY_NONE; } @@ -216,33 +256,46 @@ void PzemSnsInit(void) void PzemDrvInit(void) { - if (!energy_flg) { - if ((pin[GPIO_PZEM004_RX] < 99) && (pin[GPIO_PZEM0XX_TX] < 99)) { // Any device with a Pzem004T - energy_flg = XNRG_03; + if ((pin[GPIO_PZEM004_RX] < 99) && (pin[GPIO_PZEM0XX_TX] < 99)) { // Any device with a Pzem004T + energy_flg = XNRG_03; + } +} + +bool PzemCommand(void) +{ + bool serviced = true; + + if (CMND_MODULEADDRESS == Energy.command_code) { + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 4)) { + Pzem.address = XdrvMailbox.payload; // Valid addresses are 1, 2 and 3 } } + else serviced = false; // Unknown command + + return serviced; } /*********************************************************************************************\ * Interface \*********************************************************************************************/ -int Xnrg03(uint8_t function) +bool Xnrg03(uint8_t function) { - int result = 0; + bool result = false; - if (FUNC_PRE_INIT == function) { - PzemDrvInit(); - } - else if (XNRG_03 == energy_flg) { - switch (function) { - case FUNC_INIT: - PzemSnsInit(); - break; - case FUNC_EVERY_200_MSECOND: - if (PzemSerial) { PzemEvery200ms(); } - break; - } + switch (function) { + case FUNC_EVERY_250_MSECOND: + if (PzemSerial && (uptime > 4)) { PzemEvery250ms(); } + break; + case FUNC_COMMAND: + result = PzemCommand(); + break; + case FUNC_INIT: + PzemSnsInit(); + break; + case FUNC_PRE_INIT: + PzemDrvInit(); + break; } return result; } diff --git a/sonoff/xnrg_04_mcp39f501.ino b/sonoff/xnrg_04_mcp39f501.ino index 6171ef93a..fa9f5cb59 100644 --- a/sonoff/xnrg_04_mcp39f501.ino +++ b/sonoff/xnrg_04_mcp39f501.ino @@ -454,22 +454,19 @@ void McpParseData(void) // mcp_power_factor = McpExtractInt(mcp_buffer, 20, 2); mcp_line_frequency = McpExtractInt(mcp_buffer, 22, 2); - if (energy_power_on) { // Powered on - energy_frequency = (float)mcp_line_frequency / 1000; - energy_voltage = (float)mcp_voltage_rms / 10; - energy_active_power = (float)mcp_active_power / 100; - if (0 == energy_active_power) { - energy_current = 0; + if (Energy.power_on) { // Powered on + Energy.data_valid[0] = 0; + Energy.frequency[0] = (float)mcp_line_frequency / 1000; + Energy.voltage[0] = (float)mcp_voltage_rms / 10; + Energy.active_power[0] = (float)mcp_active_power / 100; + if (0 == Energy.active_power[0]) { + Energy.current[0] = 0; } else { - energy_current = (float)mcp_current_rms / 10000; + Energy.current[0] = (float)mcp_current_rms / 10000; } } else { // Powered off - energy_frequency = 0; - energy_voltage = 0; - energy_active_power = 0; - energy_current = 0; + Energy.data_valid[0] = ENERGY_WATCHDOG; } - energy_data_valid = 0; } /********************************************************************************************/ @@ -527,7 +524,7 @@ void McpSerialInput(void) void McpEverySecond(void) { - if (energy_data_valid > ENERGY_WATCHDOG) { + if (Energy.data_valid[0] > ENERGY_WATCHDOG) { mcp_voltage_rms = 0; mcp_current_rms = 0; mcp_active_power = 0; @@ -535,7 +532,7 @@ void McpEverySecond(void) } if (mcp_active_power) { - energy_kWhtoday_delta += ((mcp_active_power * 10) / 36); + Energy.kWhtoday_delta += ((mcp_active_power * 10) / 36); EnergyUpdateToday(); } @@ -583,17 +580,15 @@ void McpSnsInit(void) void McpDrvInit(void) { - if (!energy_flg) { - if ((pin[GPIO_MCP39F5_RX] < 99) && (pin[GPIO_MCP39F5_TX] < 99)) { - if (pin[GPIO_MCP39F5_RST] < 99) { - pinMode(pin[GPIO_MCP39F5_RST], OUTPUT); - digitalWrite(pin[GPIO_MCP39F5_RST], 0); // MCP disable - Reset Delta Sigma ADC's - } - mcp_calibrate = 0; - mcp_timeout = 2; // Initial wait - mcp_init = 2; // Initial setup steps - energy_flg = XNRG_04; + if ((pin[GPIO_MCP39F5_RX] < 99) && (pin[GPIO_MCP39F5_TX] < 99)) { + if (pin[GPIO_MCP39F5_RST] < 99) { + pinMode(pin[GPIO_MCP39F5_RST], OUTPUT); + digitalWrite(pin[GPIO_MCP39F5_RST], 0); // MCP disable - Reset Delta Sigma ADC's } + mcp_calibrate = 0; + mcp_timeout = 2; // Initial wait + mcp_init = 2; // Initial setup steps + energy_flg = XNRG_04; } } @@ -602,7 +597,7 @@ bool McpCommand(void) bool serviced = true; unsigned long value = 0; - if (CMND_POWERSET == energy_command_code) { + if (CMND_POWERSET == Energy.command_code) { if (XdrvMailbox.data_len && mcp_active_power) { value = (unsigned long)(CharToFloat(XdrvMailbox.data) * 100); if ((value > 100) && (value < 200000)) { // Between 1W and 2000W @@ -612,7 +607,7 @@ bool McpCommand(void) } } } - else if (CMND_VOLTAGESET == energy_command_code) { + else if (CMND_VOLTAGESET == Energy.command_code) { if (XdrvMailbox.data_len && mcp_voltage_rms) { value = (unsigned long)(CharToFloat(XdrvMailbox.data) * 10); if ((value > 1000) && (value < 2600)) { // Between 100V and 260V @@ -622,7 +617,7 @@ bool McpCommand(void) } } } - else if (CMND_CURRENTSET == energy_command_code) { + else if (CMND_CURRENTSET == Energy.command_code) { if (XdrvMailbox.data_len && mcp_current_rms) { value = (unsigned long)(CharToFloat(XdrvMailbox.data) * 10); if ((value > 100) && (value < 80000)) { // Between 10mA and 8A @@ -632,7 +627,7 @@ bool McpCommand(void) } } } - else if (CMND_FREQUENCYSET == energy_command_code) { + else if (CMND_FREQUENCYSET == Energy.command_code) { if (XdrvMailbox.data_len && mcp_line_frequency) { value = (unsigned long)(CharToFloat(XdrvMailbox.data) * 1000); if ((value > 45000) && (value < 65000)) { // Between 45Hz and 65Hz @@ -651,28 +646,26 @@ bool McpCommand(void) * Interface \*********************************************************************************************/ -int Xnrg04(uint8_t function) +bool Xnrg04(uint8_t function) { - int result = 0; + bool result = false; - if (FUNC_PRE_INIT == function) { - McpDrvInit(); - } - else if (XNRG_04 == energy_flg) { - switch (function) { - case FUNC_LOOP: - if (McpSerial) { McpSerialInput(); } - break; - case FUNC_INIT: - McpSnsInit(); - break; - case FUNC_ENERGY_EVERY_SECOND: - if (McpSerial) { McpEverySecond(); } - break; - case FUNC_COMMAND: - result = McpCommand(); - break; - } + switch (function) { + case FUNC_LOOP: + if (McpSerial) { McpSerialInput(); } + break; + case FUNC_ENERGY_EVERY_SECOND: + if (McpSerial) { McpEverySecond(); } + break; + case FUNC_COMMAND: + result = McpCommand(); + break; + case FUNC_INIT: + McpSnsInit(); + break; + case FUNC_PRE_INIT: + McpDrvInit(); + break; } return result; } diff --git a/sonoff/xnrg_05_pzem_ac.ino b/sonoff/xnrg_05_pzem_ac.ino index f23b22172..3758e68a3 100644 --- a/sonoff/xnrg_05_pzem_ac.ino +++ b/sonoff/xnrg_05_pzem_ac.ino @@ -20,84 +20,98 @@ #ifdef USE_ENERGY_SENSOR #ifdef USE_PZEM_AC /*********************************************************************************************\ - * PZEM-014 - AC 220V 10A Energy - * PZEM-016 - AC 220V 100A Energy + * PZEM-004T V3 - AC 220V 10/100A Energy + * PZEM-014 - AC 220V 10A Energy + * PZEM-016 - AC 220V 100A Energy * * Based on: * PZEM-014,016 docs https://pan.baidu.com/s/1B0MdMgURyjtO1oQa2lavKw password ytkv * - * Hardware Serial will be selected if GPIO1 = [98 PZEM016 Rx] and GPIO3 = [62 PZEM0XX Tx] + * Hardware Serial will be selected if GPIO1 = [62 PZEM0XX Tx] and GPIO3 = [98 PZEM016 Rx] \*********************************************************************************************/ #define XNRG_05 5 -#define PZEM_AC_DEVICE_ADDRESS 0x01 // PZEM default address +const uint8_t PZEM_AC_DEVICE_ADDRESS = 0x01; // PZEM default address +const uint32_t PZEM_AC_STABILIZE = 30; // Number of seconds to stabilize configuration #include TasmotaModbus *PzemAcModbus; -/* -uint16_t PzemCalculateCRC(uint8_t *buffer, uint8_t num) -{ - uint16_t crc = 0xFFFF; - for (uint32_t i = 0; i < num; i++) { - crc ^= buffer[i]; - for (uint32_t j = 8; j; j--) { - if ((crc & 0x0001) != 0) { // If the LSB is set - crc >>= 1; // Shift right and XOR 0xA001 - crc ^= 0xA001; - } else { // Else LSB is not set - crc >>= 1; // Just shift right - } - } - } - return crc; -} -*/ +struct PZEMAC { + float energy = 0; + float last_energy = 0; + uint8_t send_retry = 0; + uint8_t phase = 0; + uint8_t address = 0; + uint8_t address_step = ADDR_IDLE; +} PzemAc; + void PzemAcEverySecond(void) { - static uint8_t send_retry = 0; - bool data_ready = PzemAcModbus->ReceiveReady(); if (data_ready) { - uint8_t buffer[26]; + uint8_t buffer[30]; // At least 5 + (2 * 10) = 25 - uint8_t error = PzemAcModbus->ReceiveBuffer(buffer, 10); - AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, (buffer[2]) ? buffer[2] +5 : sizeof(buffer)); + uint8_t registers = 10; + if (ADDR_RECEIVE == PzemAc.address_step) { + registers = 2; // Need 1 byte extra as response is F8 06 00 02 00 01 FD A3 + PzemAc.address_step--; + } + uint8_t error = PzemAcModbus->ReceiveBuffer(buffer, registers); + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, PzemAcModbus->ReceiveCount()); if (error) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "PzemAc response error %d"), error); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PAC: PzemAc %d error %d"), PZEM_AC_DEVICE_ADDRESS + PzemAc.phase, error); } else { -// if ((PzemCalculateCRC(buffer, 23)) == ((buffer[24] << 8) | buffer[23])) { - energy_data_valid = 0; + Energy.data_valid[PzemAc.phase] = 0; + if (10 == registers) { - // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 + // 0 1 2 3 4 5 6 7 8 9 = ModBus register + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 = Buffer index // 01 04 14 08 D1 00 6C 00 00 00 F4 00 00 00 26 00 00 01 F4 00 64 00 00 51 34 // Id Cc Sz Volt- Current---- Power------ Energy----- Frequ PFact Alarm Crc-- - energy_voltage = (float)((buffer[3] << 8) + buffer[4]) / 10.0; // 6553.0 V - energy_current = (float)((buffer[7] << 24) + (buffer[8] << 16) + (buffer[5] << 8) + buffer[6]) / 1000.0; // 4294967.000 A - energy_active_power = (float)((buffer[11] << 24) + (buffer[12] << 16) + (buffer[9] << 8) + buffer[10]) / 10.0; // 429496729.0 W - energy_frequency = (float)((buffer[17] << 8) + buffer[18]) / 10.0; // 50.0 Hz - energy_power_factor = (float)((buffer[19] << 8) + buffer[20]) / 100.0; // 1.00 - float energy = (float)((buffer[15] << 24) + (buffer[16] << 16) + (buffer[13] << 8) + buffer[14]); // 4294967295 Wh + Energy.voltage[PzemAc.phase] = (float)((buffer[3] << 8) + buffer[4]) / 10.0; // 6553.0 V + Energy.current[PzemAc.phase] = (float)((buffer[7] << 24) + (buffer[8] << 16) + (buffer[5] << 8) + buffer[6]) / 1000.0; // 4294967.000 A + Energy.active_power[PzemAc.phase] = (float)((buffer[11] << 24) + (buffer[12] << 16) + (buffer[9] << 8) + buffer[10]) / 10.0; // 429496729.0 W + Energy.frequency[PzemAc.phase] = (float)((buffer[17] << 8) + buffer[18]) / 10.0; // 50.0 Hz + Energy.power_factor[PzemAc.phase] = (float)((buffer[19] << 8) + buffer[20]) / 100.0; // 1.00 - if (!energy_start || (energy < energy_start)) { energy_start = energy; } // Init after restart and handle roll-over if any - if (energy != energy_start) { - energy_kWhtoday += (unsigned long)((energy - energy_start) * 100); - energy_start = energy; + PzemAc.energy += (float)((buffer[15] << 24) + (buffer[16] << 16) + (buffer[13] << 8) + buffer[14]); // 4294967295 Wh + if (PzemAc.phase == Energy.phase_count -1) { + if (PzemAc.energy > PzemAc.last_energy) { // Handle missed phase + if (uptime > PZEM_AC_STABILIZE) { + EnergyUpdateTotal(PzemAc.energy, false); + } + PzemAc.last_energy = PzemAc.energy; + } + PzemAc.energy = 0; } - EnergyUpdateToday(); -// } + + } } } - if (0 == send_retry || data_ready) { - send_retry = 5; - PzemAcModbus->Send(PZEM_AC_DEVICE_ADDRESS, 0x04, 0, 10); + if (0 == PzemAc.send_retry || data_ready) { + if (0 == PzemAc.phase) { + PzemAc.phase = Energy.phase_count -1; + } else { + PzemAc.phase--; + } + PzemAc.send_retry = ENERGY_WATCHDOG; + if (ADDR_SEND == PzemAc.address_step) { + PzemAcModbus->Send(0xF8, 0x06, 0x0002, (uint16_t)PzemAc.address); + PzemAc.address_step--; + } else { + PzemAcModbus->Send(PZEM_AC_DEVICE_ADDRESS + PzemAc.phase, 0x04, 0, 10); + } } else { - send_retry--; + PzemAc.send_retry--; + if ((Energy.phase_count > 1) && (0 == PzemAc.send_retry) && (uptime < PZEM_AC_STABILIZE)) { + Energy.phase_count--; // Decrement phases if no response after retry within 30 seconds after restart + } } } @@ -107,6 +121,8 @@ void PzemAcSnsInit(void) uint8_t result = PzemAcModbus->Begin(9600); if (result) { if (2 == result) { ClaimSerial(); } + Energy.phase_count = 3; // Start off with three phases + PzemAc.phase = 0; } else { energy_flg = ENERGY_NONE; } @@ -114,33 +130,45 @@ void PzemAcSnsInit(void) void PzemAcDrvInit(void) { - if (!energy_flg) { - if ((pin[GPIO_PZEM016_RX] < 99) && (pin[GPIO_PZEM0XX_TX] < 99)) { - energy_flg = XNRG_05; - } + if ((pin[GPIO_PZEM016_RX] < 99) && (pin[GPIO_PZEM0XX_TX] < 99)) { + energy_flg = XNRG_05; } } +bool PzemAcCommand(void) +{ + bool serviced = true; + + if (CMND_MODULEADDRESS == Energy.command_code) { + PzemAc.address = XdrvMailbox.payload; // Valid addresses are 1, 2 and 3 + PzemAc.address_step = ADDR_SEND; + } + else serviced = false; // Unknown command + + return serviced; +} + /*********************************************************************************************\ * Interface \*********************************************************************************************/ -int Xnrg05(uint8_t function) +bool Xnrg05(uint8_t function) { - int result = 0; + bool result = false; - if (FUNC_PRE_INIT == function) { - PzemAcDrvInit(); - } - else if (XNRG_05 == energy_flg) { - switch (function) { - case FUNC_INIT: - PzemAcSnsInit(); - break; - case FUNC_ENERGY_EVERY_SECOND: - if (uptime > 4) { PzemAcEverySecond(); } // Fix start up issue #5875 - break; - } + switch (function) { + case FUNC_ENERGY_EVERY_SECOND: + if (uptime > 4) { PzemAcEverySecond(); } // Fix start up issue #5875 + break; + case FUNC_COMMAND: + result = PzemAcCommand(); + break; + case FUNC_INIT: + PzemAcSnsInit(); + break; + case FUNC_PRE_INIT: + PzemAcDrvInit(); + break; } return result; } diff --git a/sonoff/xnrg_06_pzem_dc.ino b/sonoff/xnrg_06_pzem_dc.ino index ab70b4e62..2e85ac945 100644 --- a/sonoff/xnrg_06_pzem_dc.ino +++ b/sonoff/xnrg_06_pzem_dc.ino @@ -26,56 +26,88 @@ * Based on: * PZEM-003,017 docs Https://pan.baidu.com/s/1V9bDWj3RK2u6_fbBJ3GtqQ password rq37 * - * Hardware Serial will be selected if GPIO1 = [99 PZEM017 Rx] and GPIO3 = [62 PZEM0XX Tx] + * Hardware Serial will be selected if GPIO1 = [62 PZEM0XX Tx] and GPIO3 = [99 PZEM017 Rx] \*********************************************************************************************/ #define XNRG_06 6 -#define PZEM_DC_DEVICE_ADDRESS 0x01 // PZEM default address +const uint8_t PZEM_DC_DEVICE_ADDRESS = 0x01; // PZEM default address +const uint32_t PZEM_DC_STABILIZE = 30; // Number of seconds to stabilize configuration #include TasmotaModbus *PzemDcModbus; +struct PZEMDC { + float energy = 0; + float last_energy = 0; + uint8_t send_retry = 0; + uint8_t channel = 0; + uint8_t address = 0; + uint8_t address_step = ADDR_IDLE; +} PzemDc; + void PzemDcEverySecond(void) { - static uint8_t send_retry = 0; - bool data_ready = PzemDcModbus->ReceiveReady(); if (data_ready) { - uint8_t buffer[22]; + uint8_t buffer[26]; // At least 5 + (2 * 8) = 21 - uint8_t error = PzemDcModbus->ReceiveBuffer(buffer, 8); - AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, (buffer[2]) ? buffer[2] +5 : sizeof(buffer)); + uint8_t registers = 8; + if (ADDR_RECEIVE == PzemDc.address_step) { + registers = 2; // Need 1 byte extra as response is F8 06 00 02 00 01 FD A3 + PzemDc.address_step--; + } + uint8_t error = PzemDcModbus->ReceiveBuffer(buffer, registers); + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, PzemDcModbus->ReceiveCount()); if (error) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "PzemDc response error %d"), error); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PDC: PzemDc %d error %d"), PZEM_DC_DEVICE_ADDRESS + PzemDc.channel, error); } else { - energy_data_valid = 0; + Energy.data_valid[PzemDc.channel] = 0; + if (8 == registers) { - // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 - // 01 04 10 05 40 00 0A 00 0D 00 00 00 02 00 00 00 00 00 00 D6 29 - // Id Cc Sz Volt- Curre Power------ Energy----- HiAlm LoAlm Crc-- - energy_voltage = (float)((buffer[3] << 8) + buffer[4]) / 100.0; // 655.00 V - energy_current = (float)((buffer[5] << 8) + buffer[6]) / 100.0; // 655.00 A - energy_active_power = (float)((buffer[9] << 24) + (buffer[10] << 16) + (buffer[7] << 8) + buffer[8]) / 10.0; // 429496729.0 W - float energy = (float)((buffer[13] << 24) + (buffer[14] << 16) + (buffer[11] << 8) + buffer[12]); // 4294967295 Wh + // 0 1 2 3 4 5 6 7 = ModBus register + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 = Buffer index + // 01 04 10 05 40 00 0A 00 0D 00 00 00 02 00 00 00 00 00 00 D6 29 + // Id Cc Sz Volt- Curre Power------ Energy----- HiAlm LoAlm Crc-- + Energy.voltage[PzemDc.channel] = (float)((buffer[3] << 8) + buffer[4]) / 100.0; // 655.00 V + Energy.current[PzemDc.channel] = (float)((buffer[5] << 8) + buffer[6]) / 100.0; // 655.00 A + Energy.active_power[PzemDc.channel] = (float)((buffer[9] << 24) + (buffer[10] << 16) + (buffer[7] << 8) + buffer[8]) / 10.0; // 429496729.0 W - if (!energy_start || (energy < energy_start)) { energy_start = energy; } // Init after restart and handle roll-over if any - if (energy != energy_start) { - energy_kWhtoday += (unsigned long)((energy - energy_start) * 100); - energy_start = energy; + PzemDc.energy += (float)((buffer[13] << 24) + (buffer[14] << 16) + (buffer[11] << 8) + buffer[12]); // 4294967295 Wh + if (PzemDc.channel == Energy.phase_count -1) { + if (PzemDc.energy > PzemDc.last_energy) { // Handle missed channel + if (uptime > PZEM_DC_STABILIZE) { + EnergyUpdateTotal(PzemDc.energy, false); + } + PzemDc.last_energy = PzemDc.energy; + } + PzemDc.energy = 0; + } } - EnergyUpdateToday(); } } - if (0 == send_retry || data_ready) { - send_retry = 5; - PzemDcModbus->Send(PZEM_DC_DEVICE_ADDRESS, 0x04, 0, 8); + if (0 == PzemDc.send_retry || data_ready) { + if (0 == PzemDc.channel) { + PzemDc.channel = Energy.phase_count -1; + } else { + PzemDc.channel--; + } + PzemDc.send_retry = ENERGY_WATCHDOG; + if (ADDR_SEND == PzemDc.address_step) { + PzemDcModbus->Send(0xF8, 0x06, 0x0002, (uint16_t)PzemDc.address); + PzemDc.address_step--; + } else { + PzemDcModbus->Send(PZEM_DC_DEVICE_ADDRESS + PzemDc.channel, 0x04, 0, 8); + } } else { - send_retry--; + PzemDc.send_retry--; + if ((Energy.phase_count > 1) && (0 == PzemDc.send_retry) && (uptime < PZEM_DC_STABILIZE)) { + Energy.phase_count--; // Decrement channels if no response after retry within 30 seconds after restart + } } } @@ -85,7 +117,9 @@ void PzemDcSnsInit(void) uint8_t result = PzemDcModbus->Begin(9600, 2); // Uses two stop bits!! if (result) { if (2 == result) { ClaimSerial(); } - energy_type_dc = true; + Energy.type_dc = true; + Energy.phase_count = 3; // Start off with three channels + PzemDc.channel = 0; } else { energy_flg = ENERGY_NONE; } @@ -93,33 +127,45 @@ void PzemDcSnsInit(void) void PzemDcDrvInit(void) { - if (!energy_flg) { - if ((pin[GPIO_PZEM017_RX] < 99) && (pin[GPIO_PZEM0XX_TX] < 99)) { - energy_flg = XNRG_06; - } + if ((pin[GPIO_PZEM017_RX] < 99) && (pin[GPIO_PZEM0XX_TX] < 99)) { + energy_flg = XNRG_06; } } +bool PzemDcCommand(void) +{ + bool serviced = true; + + if (CMND_MODULEADDRESS == Energy.command_code) { + PzemDc.address = XdrvMailbox.payload; // Valid addresses are 1, 2 and 3 + PzemDc.address_step = ADDR_SEND; + } + else serviced = false; // Unknown command + + return serviced; +} + /*********************************************************************************************\ * Interface \*********************************************************************************************/ -int Xnrg06(uint8_t function) +bool Xnrg06(uint8_t function) { - int result = 0; + bool result = false; - if (FUNC_PRE_INIT == function) { - PzemDcDrvInit(); - } - else if (XNRG_06 == energy_flg) { - switch (function) { - case FUNC_INIT: - PzemDcSnsInit(); - break; - case FUNC_ENERGY_EVERY_SECOND: - if (uptime > 4) { PzemDcEverySecond(); } // Fix start up issue #5875 - break; - } + switch (function) { + case FUNC_ENERGY_EVERY_SECOND: + if (uptime > 4) { PzemDcEverySecond(); } // Fix start up issue #5875 + break; + case FUNC_COMMAND: + result = PzemDcCommand(); + break; + case FUNC_INIT: + PzemDcSnsInit(); + break; + case FUNC_PRE_INIT: + PzemDcDrvInit(); + break; } return result; } diff --git a/sonoff/xnrg_07_ade7953.ino b/sonoff/xnrg_07_ade7953.ino index 12635050b..81c7c458e 100644 --- a/sonoff/xnrg_07_ade7953.ino +++ b/sonoff/xnrg_07_ade7953.ino @@ -36,14 +36,24 @@ #define ADE7953_ADDR 0x38 -uint32_t ade7953_active_power = 0; -uint32_t ade7953_active_power1 = 0; -uint32_t ade7953_active_power2 = 0; -uint32_t ade7953_current_rms = 0; -uint32_t ade7953_current_rms1 = 0; -uint32_t ade7953_current_rms2 = 0; -uint32_t ade7953_voltage_rms = 0; -uint8_t ade7953_init = 0; +const uint8_t Ade7953Registers[] { + 0x1B, // RMS current channel B (Relay 1) + 0x13, // Active power channel B + 0x11, // Apparent power channel B + 0x15, // Reactive power channel B + 0x1A, // RMS current channel A (Relay 2) + 0x12, // Active power channel A + 0x10, // Apparent power channel A + 0x14, // Reactive power channel A + 0x1C // RMS voltage (Both relays) +}; + +struct Ade7953 { + uint32_t voltage_rms = 0; + uint32_t current_rms[2] = { 0, 0 }; + uint32_t active_power[2] = { 0, 0 }; + uint8_t init_step = 0; +} Ade7953; int Ade7953RegSize(uint16_t reg) { @@ -78,7 +88,7 @@ void Ade7953Write(uint16_t reg, uint32_t val) } } -uint32_t Ade7953Read(uint16_t reg) +int32_t Ade7953Read(uint16_t reg) { uint32_t response = 0; @@ -107,59 +117,72 @@ void Ade7953Init(void) void Ade7953GetData(void) { - int32_t active_power; + int32_t reg[2][4]; + for (uint32_t i = 0; i < sizeof(Ade7953Registers); i++) { + int32_t value = Ade7953Read(0x300 + Ade7953Registers[i]); + if (8 == i) { + Ade7953.voltage_rms = value; // RMS voltage (Both relays) + } else { + reg[i >> 2][i &3] = value; + } + } + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ADE: %d, [%d, %d, %d, %d], [%d, %d, %d, %d]"), + Ade7953.voltage_rms, reg[0][0], reg[0][1], reg[0][2], reg[0][3], + reg[1][0], reg[1][1], reg[1][2], reg[1][3]); - ade7953_voltage_rms = Ade7953Read(0x31C); // Both relays - ade7953_current_rms1 = Ade7953Read(0x31B); // Relay 1 - if (ade7953_current_rms1 < 2000) { // No load threshold (20mA) - ade7953_current_rms1 = 0; - ade7953_active_power1 = 0; - } else { - active_power = (int32_t)Ade7953Read(0x313) * -1; // Relay 1 - ade7953_active_power1 = (active_power > 0) ? active_power : 0; + uint32_t apparent_power[2] = { 0, 0 }; + uint32_t reactive_power[2] = { 0, 0 }; + + for (uint32_t channel = 0; channel < 2; channel++) { + Ade7953.current_rms[channel] = reg[channel][0]; + if (Ade7953.current_rms[channel] < 2000) { // No load threshold (20mA) + Ade7953.current_rms[channel] = 0; + Ade7953.active_power[channel] = 0; + } else { + Ade7953.active_power[channel] = abs(reg[channel][1]); + apparent_power[channel] = abs(reg[channel][2]); + reactive_power[channel] = abs(reg[channel][3]); + } } - ade7953_current_rms2 = Ade7953Read(0x31A); // Relay 2 - if (ade7953_current_rms2 < 2000) { // No load threshold (20mA) - ade7953_current_rms2 = 0; - ade7953_active_power2 = 0; - } else { - active_power = (int32_t)Ade7953Read(0x312); // Relay 2 - ade7953_active_power2 = (active_power > 0) ? active_power : 0; - } - // First phase only supports accumulated Current and Power - ade7953_current_rms = ade7953_current_rms1 + ade7953_current_rms2; - ade7953_active_power = ade7953_active_power1 + ade7953_active_power2; + + uint32_t current_rms_sum = Ade7953.current_rms[0] + Ade7953.current_rms[1]; + uint32_t active_power_sum = Ade7953.active_power[0] + Ade7953.active_power[1]; AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ADE: U %d, I %d + %d = %d, P %d + %d = %d"), - ade7953_voltage_rms, ade7953_current_rms1, ade7953_current_rms2, ade7953_current_rms, ade7953_active_power1, ade7953_active_power2, ade7953_active_power); + Ade7953.voltage_rms, Ade7953.current_rms[0], Ade7953.current_rms[1], current_rms_sum, Ade7953.active_power[0], Ade7953.active_power[1], active_power_sum); - if (energy_power_on) { // Powered on - energy_voltage = (float)ade7953_voltage_rms / Settings.energy_voltage_calibration; - energy_active_power = (float)ade7953_active_power / (Settings.energy_power_calibration / 10); - if (0 == energy_active_power) { - energy_current = 0; - } else { - energy_current = (float)ade7953_current_rms / (Settings.energy_current_calibration * 10); + if (Energy.power_on) { // Powered on + Energy.voltage[0] = (float)Ade7953.voltage_rms / Settings.energy_voltage_calibration; + + for (uint32_t channel = 0; channel < 2; channel++) { + Energy.data_valid[channel] = 0; + Energy.active_power[channel] = (float)Ade7953.active_power[channel] / (Settings.energy_power_calibration / 10); + Energy.reactive_power[channel] = (float)reactive_power[channel] / (Settings.energy_power_calibration / 10); + Energy.apparent_power[channel] = (float)apparent_power[channel] / (Settings.energy_power_calibration / 10); + if (0 == Energy.active_power[channel]) { + Energy.current[channel] = 0; + } else { + Energy.current[channel] = (float)Ade7953.current_rms[channel] / (Settings.energy_current_calibration * 10); + } } } else { // Powered off - energy_voltage = 0; - energy_active_power = 0; - energy_current = 0; + Energy.data_valid[0] = ENERGY_WATCHDOG; + Energy.data_valid[1] = ENERGY_WATCHDOG; } - if (ade7953_active_power) { - energy_kWhtoday_delta += ((ade7953_active_power * (100000 / (Settings.energy_power_calibration / 10))) / 3600); + if (active_power_sum) { + Energy.kWhtoday_delta += ((active_power_sum * (100000 / (Settings.energy_power_calibration / 10))) / 3600); EnergyUpdateToday(); } } void Ade7953EnergyEverySecond() { - if (ade7953_init) { - if (1 == ade7953_init) { + if (Ade7953.init_step) { + if (1 == Ade7953.init_step) { Ade7953Init(); } - ade7953_init--; + Ade7953.init_step--; } else { Ade7953GetData(); } @@ -167,19 +190,21 @@ void Ade7953EnergyEverySecond() void Ade7953DrvInit(void) { - if (!energy_flg) { - if (i2c_flg && (pin[GPIO_ADE7953_IRQ] < 99)) { // Irq on GPIO16 is not supported... - delay(100); // Need 100mS to init ADE7953 - if (I2cDevice(ADE7953_ADDR)) { - if (HLW_PREF_PULSE == Settings.energy_power_calibration) { - Settings.energy_power_calibration = ADE7953_PREF; - Settings.energy_voltage_calibration = ADE7953_UREF; - Settings.energy_current_calibration = ADE7953_IREF; - } - AddLog_P2(LOG_LEVEL_DEBUG, S_LOG_I2C_FOUND_AT, "ADE7953", ADE7953_ADDR); - ade7953_init = 2; - energy_flg = XNRG_07; - } + if (i2c_flg && (pin[GPIO_ADE7953_IRQ] < 99)) { // Irq on GPIO16 is not supported... + delay(100); // Need 100mS to init ADE7953 + if (I2cDevice(ADE7953_ADDR)) { + if (HLW_PREF_PULSE == Settings.energy_power_calibration) { + Settings.energy_power_calibration = ADE7953_PREF; + Settings.energy_voltage_calibration = ADE7953_UREF; + Settings.energy_current_calibration = ADE7953_IREF; + } + AddLog_P2(LOG_LEVEL_DEBUG, S_LOG_I2C_FOUND_AT, "ADE7953", ADE7953_ADDR); + Ade7953.init_step = 2; + + Energy.phase_count = 2; // Handle two channels as two phases + Energy.voltage_common = true; // Use common voltage + + energy_flg = XNRG_07; } } } @@ -188,38 +213,39 @@ bool Ade7953Command(void) { bool serviced = true; + uint32_t channel = (2 == XdrvMailbox.index) ? 1 : 0; uint32_t value = (uint32_t)(CharToFloat(XdrvMailbox.data) * 100); // 1.23 = 123 - if (CMND_POWERCAL == energy_command_code) { + if (CMND_POWERCAL == Energy.command_code) { if (1 == XdrvMailbox.payload) { XdrvMailbox.payload = ADE7953_PREF; } // Service in xdrv_03_energy.ino } - else if (CMND_VOLTAGECAL == energy_command_code) { + else if (CMND_VOLTAGECAL == Energy.command_code) { if (1 == XdrvMailbox.payload) { XdrvMailbox.payload = ADE7953_UREF; } // Service in xdrv_03_energy.ino } - else if (CMND_CURRENTCAL == energy_command_code) { + else if (CMND_CURRENTCAL == Energy.command_code) { if (1 == XdrvMailbox.payload) { XdrvMailbox.payload = ADE7953_IREF; } // Service in xdrv_03_energy.ino } - else if (CMND_POWERSET == energy_command_code) { - if (XdrvMailbox.data_len && ade7953_active_power) { + else if (CMND_POWERSET == Energy.command_code) { + if (XdrvMailbox.data_len && Ade7953.active_power[channel]) { if ((value > 100) && (value < 200000)) { // Between 1W and 2000W - Settings.energy_power_calibration = (ade7953_active_power * 1000) / value; // 0.00 W + Settings.energy_power_calibration = (Ade7953.active_power[channel] * 1000) / value; // 0.00 W } } } - else if (CMND_VOLTAGESET == energy_command_code) { - if (XdrvMailbox.data_len && ade7953_voltage_rms) { + else if (CMND_VOLTAGESET == Energy.command_code) { + if (XdrvMailbox.data_len && Ade7953.voltage_rms) { if ((value > 10000) && (value < 26000)) { // Between 100V and 260V - Settings.energy_voltage_calibration = (ade7953_voltage_rms * 100) / value; // 0.00 V + Settings.energy_voltage_calibration = (Ade7953.voltage_rms * 100) / value; // 0.00 V } } } - else if (CMND_CURRENTSET == energy_command_code) { - if (XdrvMailbox.data_len && ade7953_current_rms) { + else if (CMND_CURRENTSET == Energy.command_code) { + if (XdrvMailbox.data_len && Ade7953.current_rms[channel]) { if ((value > 2000) && (value < 1000000)) { // Between 20mA and 10A - Settings.energy_current_calibration = ((ade7953_current_rms * 100) / value) * 100; // 0.00 mA + Settings.energy_current_calibration = ((Ade7953.current_rms[channel] * 100) / value) * 100; // 0.00 mA } } } @@ -232,22 +258,20 @@ bool Ade7953Command(void) * Interface \*********************************************************************************************/ -int Xnrg07(uint8_t function) +bool Xnrg07(uint8_t function) { - int result = 0; + bool result = false; - if (FUNC_PRE_INIT == function) { - Ade7953DrvInit(); - } - else if (XNRG_07 == energy_flg) { - switch (function) { - case FUNC_ENERGY_EVERY_SECOND: - Ade7953EnergyEverySecond(); - break; - case FUNC_COMMAND: - result = Ade7953Command(); - break; - } + switch (function) { + case FUNC_ENERGY_EVERY_SECOND: + Ade7953EnergyEverySecond(); + break; + case FUNC_COMMAND: + result = Ade7953Command(); + break; + case FUNC_PRE_INIT: + Ade7953DrvInit(); + break; } return result; } diff --git a/sonoff/xnrg_08_sdm120.ino b/sonoff/xnrg_08_sdm120.ino new file mode 100644 index 000000000..c60dd16a7 --- /dev/null +++ b/sonoff/xnrg_08_sdm120.ino @@ -0,0 +1,269 @@ +/* + xnrg_08_sdm120.ino - Eastron SDM120-Modbus energy meter support for Sonoff-Tasmota + + Copyright (C) 2019 Gennaro Tortone and Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef USE_ENERGY_SENSOR +#ifdef USE_SDM120 +/*********************************************************************************************\ + * Eastron SDM120 or SDM220 Modbus energy meter + * + * Based on: https://github.com/reaper7/SDM_Energy_Meter +\*********************************************************************************************/ + +#define XNRG_08 8 + +// can be user defined in my_user_config.h +#ifndef SDM120_SPEED + #define SDM120_SPEED 2400 // default SDM120 Modbus address +#endif +// can be user defined in my_user_config.h +#ifndef SDM120_ADDR + #define SDM120_ADDR 1 // default SDM120 Modbus address +#endif + +#include +TasmotaModbus *Sdm120Modbus; + +const uint8_t sdm120_table = 8; +const uint8_t sdm220_table = 13; + +const uint16_t sdm120_start_addresses[] { + 0x0000, // SDM120C_VOLTAGE [V] + 0x0006, // SDM120C_CURRENT [A] + 0x000C, // SDM120C_POWER [W] + 0x0012, // SDM120C_APPARENT_POWER [VA] + 0x0018, // SDM120C_REACTIVE_POWER [VAR] + 0x001E, // SDM120C_POWER_FACTOR + 0x0046, // SDM120C_FREQUENCY [Hz] + 0x0156, // SDM120C_TOTAL_ACTIVE_ENERGY [kWh] + + 0X0048, // SDM220_IMPORT_ACTIVE [kWh] + 0X004A, // SDM220_EXPORT_ACTIVE [kWh] + 0X004C, // SDM220_IMPORT_REACTIVE [kVArh] + 0X004E, // SDM220_EXPORT_REACTIVE [kVArh] + 0X0024 // SDM220_PHASE_ANGLE [Degree] +}; + +struct SDM120 { + float total_active = 0; + float import_active = NAN; + float import_reactive = 0; + float export_reactive = 0; + float phase_angle = 0; + uint8_t read_state = 0; + uint8_t send_retry = 0; + uint8_t start_address_count = sdm220_table; +} Sdm120; + +/*********************************************************************************************/ + +void SDM120Every250ms(void) +{ + bool data_ready = Sdm120Modbus->ReceiveReady(); + + if (data_ready) { + uint8_t buffer[14]; // At least 5 + (2 * 2) = 9 + + uint32_t error = Sdm120Modbus->ReceiveBuffer(buffer, 2); + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, Sdm120Modbus->ReceiveCount()); + + if (error) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SDM: SDM120 error %d"), error); + } else { + Energy.data_valid[0] = 0; + + // 0 1 2 3 4 5 6 7 8 + // SA FC BC Fh Fl Sh Sl Cl Ch + // 01 04 04 43 66 33 34 1B 38 = 230.2 Volt + float value; + ((uint8_t*)&value)[3] = buffer[3]; // Get float values + ((uint8_t*)&value)[2] = buffer[4]; + ((uint8_t*)&value)[1] = buffer[5]; + ((uint8_t*)&value)[0] = buffer[6]; + + switch(Sdm120.read_state) { + case 0: + Energy.voltage[0] = value; // 230.2 V + break; + + case 1: + Energy.current[0] = value; // 1.260 A + break; + + case 2: + Energy.active_power[0] = value; // -196.3 W + break; + + case 3: + Energy.apparent_power[0] = value; // 223.4 VA + break; + + case 4: + Energy.reactive_power[0] = value; // 92.2 + break; + + case 5: + Energy.power_factor[0] = value; // -0.91 + break; + + case 6: + Energy.frequency[0] = value; // 50.0 Hz + break; + + case 7: + Sdm120.total_active = value; // 484.708 kWh = import_active + export_active + break; + + case 8: + Sdm120.import_active = value; // 478.492 kWh + break; + + case 9: + Energy.export_active = value; // 6.216 kWh + break; + + case 10: + Sdm120.import_reactive = value; // 172.750 kVArh + break; + + case 11: + Sdm120.export_reactive = value; // 2.844 kVArh + break; + + case 12: + Sdm120.phase_angle = value; // 0.00 Deg + break; + } + + Sdm120.read_state++; + if (Sdm120.read_state == Sdm120.start_address_count) { + Sdm120.read_state = 0; + + if (Sdm120.start_address_count > sdm120_table) { + if (!isnan(Sdm120.import_active)) { + Sdm120.total_active = Sdm120.import_active; + } else { + Sdm120.start_address_count = sdm120_table; // No extended registers available + } + } + EnergyUpdateTotal(Sdm120.total_active, true); // 484.708 kWh + } + } + } // end data ready + + if (0 == Sdm120.send_retry || data_ready) { + Sdm120.send_retry = 5; + Sdm120Modbus->Send(SDM120_ADDR, 0x04, sdm120_start_addresses[Sdm120.read_state], 2); + } else { + Sdm120.send_retry--; + } +} + +void Sdm120SnsInit(void) +{ + Sdm120Modbus = new TasmotaModbus(pin[GPIO_SDM120_RX], pin[GPIO_SDM120_TX]); + uint8_t result = Sdm120Modbus->Begin(SDM120_SPEED); + if (result) { + if (2 == result) { ClaimSerial(); } + } else { + energy_flg = ENERGY_NONE; + } +} + +void Sdm120DrvInit(void) +{ + if ((pin[GPIO_SDM120_RX] < 99) && (pin[GPIO_SDM120_TX] < 99)) { + energy_flg = XNRG_08; + } +} + +void Sdm220Reset(void) +{ + if (isnan(Sdm120.import_active)) { return; } + + Sdm120.import_active = 0; + Sdm120.import_reactive = 0; + Sdm120.export_reactive = 0; + Sdm120.phase_angle = 0; +} + +#ifdef USE_WEBSERVER +const char HTTP_ENERGY_SDM220[] PROGMEM = + "{s}" D_IMPORT_REACTIVE "{m}%s " D_UNIT_KWARH "{e}" + "{s}" D_EXPORT_REACTIVE "{m}%s " D_UNIT_KWARH "{e}" + "{s}" D_PHASE_ANGLE "{m}%s " D_UNIT_ANGLE "{e}"; +#endif // USE_WEBSERVER + +void Sdm220Show(bool json) +{ + if (isnan(Sdm120.import_active)) { return; } + + char import_active_chr[FLOATSZ]; + dtostrfd(Sdm120.import_active, Settings.flag2.energy_resolution, import_active_chr); + char import_reactive_chr[FLOATSZ]; + dtostrfd(Sdm120.import_reactive, Settings.flag2.energy_resolution, import_reactive_chr); + char export_reactive_chr[FLOATSZ]; + dtostrfd(Sdm120.export_reactive, Settings.flag2.energy_resolution, export_reactive_chr); + char phase_angle_chr[FLOATSZ]; + dtostrfd(Sdm120.phase_angle, 2, phase_angle_chr); + + if (json) { + ResponseAppend_P(PSTR(",\"" D_JSON_IMPORT_ACTIVE "\":%s,\"" D_JSON_IMPORT_REACTIVE "\":%s,\"" D_JSON_EXPORT_REACTIVE "\":%s,\"" D_JSON_PHASE_ANGLE "\":%s"), + import_active_chr, import_reactive_chr, export_reactive_chr, phase_angle_chr); +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_ENERGY_SDM220, import_reactive_chr, export_reactive_chr, phase_angle_chr); +#endif // USE_WEBSERVER + } +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xnrg08(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_EVERY_250_MSECOND: + if (uptime > 4) { SDM120Every250ms(); } + break; + case FUNC_JSON_APPEND: + Sdm220Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Sdm220Show(0); + break; +#endif // USE_WEBSERVER + case FUNC_ENERGY_RESET: + Sdm220Reset(); + break; + case FUNC_INIT: + Sdm120SnsInit(); + break; + case FUNC_PRE_INIT: + Sdm120DrvInit(); + break; + } + return result; +} + +#endif // USE_SDM120 +#endif // USE_ENERGY_SENSOR diff --git a/sonoff/xnrg_09_dds2382.ino b/sonoff/xnrg_09_dds2382.ino new file mode 100644 index 000000000..590a60186 --- /dev/null +++ b/sonoff/xnrg_09_dds2382.ino @@ -0,0 +1,128 @@ +/* + xnrg_09_dds2382.ino - Hiking DDS238-2 Modbus energy meter support for Sonoff-Tasmota + + Copyright (C) 2019 Matteo Campanella - based on the work of Gennaro Tortone + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef USE_ENERGY_SENSOR +#ifdef USE_DDS2382 +/*********************************************************************************************\ + * Hiking DDS238-2 Modbus energy meter + * + * Based on: https://github.com/reaper7/SDM_Energy_Meter +\*********************************************************************************************/ + +#define XNRG_09 9 + +#ifndef DDS2382_SPEED +#define DDS2382_SPEED 9600 // default dds2382 Modbus address +#endif +#ifndef DDS2382_ADDR +#define DDS2382_ADDR 1 // default dds2382 Modbus address +#endif + +#include +TasmotaModbus *Dds2382Modbus; + +uint8_t Dds2382_send_retry = 0; + +void Dds2382EverySecond(void) +{ + bool data_ready = Dds2382Modbus->ReceiveReady(); + + if (data_ready) { + uint8_t buffer[46]; // At least 5 + (2 * 18) = 41 + + uint32_t error = Dds2382Modbus->ReceiveBuffer(buffer, 18); + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, Dds2382Modbus->ReceiveCount()); + + if (error) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "DDS2382 response error %d"), error); + } else { + Energy.data_valid[0] = 0; + + // 0 1 2 3 4 5 6 7 8 9 A B C D E F 10 11 = ModBus register + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 = Buffer index + // SA FC BC EnergyTotal ExportActiv ImportActiv Volta Curre APowe RPowe PFact Frequ Crc-- = DDS238-2 ZN/S version 1 (#6384) + // SA FC BC EnergyTotal ExportActiv ImportActiv Volta Curre APowe RPowe PFact Frequ Crc-- = DDS238-2 ZN/S version 2 (#6531) + + Energy.voltage[0] = (float)((buffer[27] << 8) + buffer[28]) / 10.0; + Energy.current[0] = (float)((buffer[29] << 8) + buffer[30]) / 100.0; + Energy.active_power[0] = (float)((buffer[31] << 8) + buffer[32]); + Energy.reactive_power[0] = (float)((buffer[33] << 8) + buffer[34]); + Energy.power_factor[0] = (float)((buffer[35] << 8) + buffer[36]) / 1000.0; // 1.00 + Energy.frequency[0] = (float)((buffer[37] << 8) + buffer[38]) / 100.0; // 50.0 Hz + uint8_t offset = 11; + if (Settings.flag3.dds2382_model) { + offset = 19; + } + Energy.export_active = (float)((buffer[offset] << 24) + (buffer[offset +1] << 16) + (buffer[offset +2] << 8) + buffer[offset +3]) / 100.0; // 429496729.0 W + float import_active = (float)((buffer[offset +4] << 24) + (buffer[offset +5] << 16) + (buffer[offset +6] << 8) + buffer[offset +7]) / 100.0; // 429496729.0 W + + EnergyUpdateTotal(import_active, false); // 484.708 kWh + } + } // end data ready + + if (0 == Dds2382_send_retry || data_ready) { + Dds2382_send_retry = 5; + Dds2382Modbus->Send(DDS2382_ADDR, 0x03, 0, 18); + } else { + Dds2382_send_retry--; + } +} + +void Dds2382SnsInit(void) +{ + Dds2382Modbus = new TasmotaModbus(pin[GPIO_DDS2382_RX], pin[GPIO_DDS2382_TX]); + uint8_t result = Dds2382Modbus->Begin(DDS2382_SPEED); + if (result) { + if (2 == result) { ClaimSerial(); } + } else { + energy_flg = ENERGY_NONE; + } +} + +void Dds2382DrvInit(void) +{ + if ((pin[GPIO_DDS2382_RX] < 99) && (pin[GPIO_DDS2382_TX] < 99)) { + energy_flg = XNRG_09; + } +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xnrg09(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_ENERGY_EVERY_SECOND: + if (uptime > 4) { Dds2382EverySecond(); } + break; + case FUNC_INIT: + Dds2382SnsInit(); + break; + case FUNC_PRE_INIT: + Dds2382DrvInit(); + break; + } + return result; +} + +#endif // USE_DDS2382 +#endif // USE_ENERGY_SENSOR diff --git a/sonoff/xnrg_10_sdm630.ino b/sonoff/xnrg_10_sdm630.ino new file mode 100644 index 000000000..8bf1827f8 --- /dev/null +++ b/sonoff/xnrg_10_sdm630.ino @@ -0,0 +1,217 @@ +/* + xnrg_10_sdm630.ino - Eastron SDM630-Modbus energy meter support for Sonoff-Tasmota + + Copyright (C) 2019 Gennaro Tortone and Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef USE_ENERGY_SENSOR +#ifdef USE_SDM630 +/*********************************************************************************************\ + * Eastron SDM630-Modbus energy meter + * + * Based on: https://github.com/reaper7/SDM_Energy_Meter +\*********************************************************************************************/ + +#define XNRG_10 10 + +// can be user defined in my_user_config.h +#ifndef SDM630_SPEED + #define SDM630_SPEED 9600 // default SDM630 Modbus address +#endif +// can be user defined in my_user_config.h +#ifndef SDM630_ADDR + #define SDM630_ADDR 1 // default SDM630 Modbus address +#endif + +#include +TasmotaModbus *Sdm630Modbus; + +const uint16_t sdm630_start_addresses[] { + 0x0000, // L1 - SDM630_VOLTAGE [V] + 0x0002, // L2 - SDM630_VOLTAGE [V] + 0x0004, // L3 - SDM630_VOLTAGE [V] + 0x0006, // L1 - SDM630_CURRENT [A] + 0x0008, // L2 - SDM630_CURRENT [A] + 0x000A, // L3 - SDM630_CURRENT [A] + 0x000C, // L1 - SDM630_POWER [W] + 0x000E, // L2 - SDM630_POWER [W] + 0x0010, // L3 - SDM630_POWER [W] + 0x0018, // L1 - SDM630_REACTIVE_POWER [VAR] + 0x001A, // L2 - SDM630_REACTIVE_POWER [VAR] + 0x001C, // L3 - SDM630_REACTIVE_POWER [VAR] + 0x001E, // L1 - SDM630_POWER_FACTOR + 0x0020, // L2 - SDM630_POWER_FACTOR + 0x0022, // L3 - SDM630_POWER_FACTOR + 0x0156 // Total - SDM630_TOTAL_ACTIVE_ENERGY [Wh] +}; + +struct SDM630 { + uint8_t read_state = 0; + uint8_t send_retry = 0; +} Sdm630; + +/*********************************************************************************************/ + +void SDM630Every250ms(void) +{ + bool data_ready = Sdm630Modbus->ReceiveReady(); + + if (data_ready) { + uint8_t buffer[14]; // At least 5 + (2 * 2) = 9 + + uint32_t error = Sdm630Modbus->ReceiveBuffer(buffer, 2); + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, Sdm630Modbus->ReceiveCount()); + + if (error) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SDM: SDM630 error %d"), error); + } else { + Energy.data_valid[0] = 0; + Energy.data_valid[1] = 0; + Energy.data_valid[2] = 0; + + // 0 1 2 3 4 5 6 7 8 + // SA FC BC Fh Fl Sh Sl Cl Ch + // 01 04 04 43 66 33 34 1B 38 = 230.2 Volt + float value; + ((uint8_t*)&value)[3] = buffer[3]; // Get float values + ((uint8_t*)&value)[2] = buffer[4]; + ((uint8_t*)&value)[1] = buffer[5]; + ((uint8_t*)&value)[0] = buffer[6]; + + switch(Sdm630.read_state) { + case 0: + Energy.voltage[0] = value; + break; + + case 1: + Energy.voltage[1] = value; + break; + + case 2: + Energy.voltage[2] = value; + break; + + case 3: + Energy.current[0] = value; + break; + + case 4: + Energy.current[1] = value; + break; + + case 5: + Energy.current[2] = value; + break; + + case 6: + Energy.active_power[0] = value; + break; + + case 7: + Energy.active_power[1] = value; + break; + + case 8: + Energy.active_power[2] = value; + break; + + case 9: + Energy.reactive_power[0] = value; + break; + + case 10: + Energy.reactive_power[1] = value; + break; + + case 11: + Energy.reactive_power[2] = value; + break; + + case 12: + Energy.power_factor[0] = value; + break; + + case 13: + Energy.power_factor[1] = value; + break; + + case 14: + Energy.power_factor[2] = value; + break; + + case 15: + EnergyUpdateTotal(value, true); + break; + } + + Sdm630.read_state++; + if (sizeof(sdm630_start_addresses)/2 == Sdm630.read_state) { + Sdm630.read_state = 0; + } + } + } // end data ready + + if (0 == Sdm630.send_retry || data_ready) { + Sdm630.send_retry = 5; + Sdm630Modbus->Send(SDM630_ADDR, 0x04, sdm630_start_addresses[Sdm630.read_state], 2); + } else { + Sdm630.send_retry--; + } +} + +void Sdm630SnsInit(void) +{ + Sdm630Modbus = new TasmotaModbus(pin[GPIO_SDM630_RX], pin[GPIO_SDM630_TX]); + uint8_t result = Sdm630Modbus->Begin(SDM630_SPEED); + if (result) { + if (2 == result) { ClaimSerial(); } + Energy.phase_count = 3; + } else { + energy_flg = ENERGY_NONE; + } +} + +void Sdm630DrvInit(void) +{ + if ((pin[GPIO_SDM630_RX] < 99) && (pin[GPIO_SDM630_TX] < 99)) { + energy_flg = XNRG_10; + } +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xnrg10(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_EVERY_250_MSECOND: + if (uptime > 4) { SDM630Every250ms(); } + break; + case FUNC_INIT: + Sdm630SnsInit(); + break; + case FUNC_PRE_INIT: + Sdm630DrvInit(); + break; + } + return result; +} + +#endif // USE_SDM630 +#endif // USE_ENERGY_SENSOR diff --git a/sonoff/xnrg_11_ddsu666.ino b/sonoff/xnrg_11_ddsu666.ino new file mode 100644 index 000000000..2f6dfbf36 --- /dev/null +++ b/sonoff/xnrg_11_ddsu666.ino @@ -0,0 +1,175 @@ +/* + xnrg_11_ddsu666.ino - Chint DDSU666-Modbus energy meter support for Sonoff-Tasmota + + Copyright (C) 2019 Pablo Zerón and Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef USE_ENERGY_SENSOR +#ifdef USE_DDSU666 +/*********************************************************************************************\ + * Chint DDSU666 Modbus energy meter +\*********************************************************************************************/ + +#define XNRG_11 11 + +// can be user defined in my_user_config.h +#ifndef DDSU666_SPEED + #define DDSU666_SPEED 9600 // default DDSU66 Modbus address +#endif +// can be user defined in my_user_config.h +#ifndef DDSU666_ADDR + #define DDSU666_ADDR 1 // default DDSU66 Modbus address +#endif + +#include +TasmotaModbus *Ddsu666Modbus; + +const uint16_t Ddsu666_start_addresses[] { + 0x2000, // DDSU666_VOLTAGE [V] + 0x2002, // DDSU666_CURRENT [A] + 0x2004, // DDSU666_POWER [KW] + 0x2006, // DDSU666_REACTIVE_POWER [KVAR] + 0x200A, // DDSU666_POWER_FACTOR + 0x200E, // DDSU666_FREQUENCY [Hz] + 0X4000, // DDSU666_IMPORT_ACTIVE [kWh] + 0X400A, // DDSU666_EXPORT_ACTIVE [kWh] +}; + +struct DDSU666 { + float import_active = NAN; + uint8_t read_state = 0; + uint8_t send_retry = 0; +} Ddsu666; + +/*********************************************************************************************/ + +void DDSU666Every250ms(void) +{ + bool data_ready = Ddsu666Modbus->ReceiveReady(); + + if (data_ready) { + uint8_t buffer[14]; // At least 5 + (2 * 2) = 9 + + uint32_t error = Ddsu666Modbus->ReceiveBuffer(buffer, 2); + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, Ddsu666Modbus->ReceiveCount()); + + if (error) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SDM: Ddsu666 error %d"), error); + } else { + Energy.data_valid[0] = 0; + + // 0 1 2 3 4 5 6 7 8 + // SA FC BC Fh Fl Sh Sl Cl Ch + // 01 04 04 43 66 33 34 1B 38 = 230.2 Volt + float value; + ((uint8_t*)&value)[3] = buffer[3]; // Get float values + ((uint8_t*)&value)[2] = buffer[4]; + ((uint8_t*)&value)[1] = buffer[5]; + ((uint8_t*)&value)[0] = buffer[6]; + + switch(Ddsu666.read_state) { + case 0: + Energy.voltage[0] = value; // 230.2 V + break; + + case 1: + Energy.current[0] = value; // 1.260 A + break; + + case 2: + Energy.active_power[0] = value * 1000; // -196.3 W + break; + + case 3: + Energy.reactive_power[0] = value * 1000; // 92.2 + break; + + case 4: + Energy.power_factor[0] = value; // 0.91 + break; + + case 5: + Energy.frequency[0] = value; // 50.0 Hz + break; + + case 6: + Ddsu666.import_active = value; // 478.492 kWh + break; + + case 7: + Energy.export_active = value; // 6.216 kWh + break; + } + + Ddsu666.read_state++; + + if (Ddsu666.read_state == 8) { + Ddsu666.read_state = 0; + EnergyUpdateTotal(Ddsu666.import_active, true); // 484.708 kWh + } + } + } // end data ready + + if (0 == Ddsu666.send_retry || data_ready) { + Ddsu666.send_retry = 5; + Ddsu666Modbus->Send(DDSU666_ADDR, 0x04, Ddsu666_start_addresses[Ddsu666.read_state], 2); + } else { + Ddsu666.send_retry--; + } +} + +void Ddsu666SnsInit(void) +{ + Ddsu666Modbus = new TasmotaModbus(pin[GPIO_DDSU666_RX], pin[GPIO_DDSU666_TX]); + uint8_t result = Ddsu666Modbus->Begin(DDSU666_SPEED); + if (result) { + if (2 == result) { ClaimSerial(); } + } else { + energy_flg = ENERGY_NONE; + } +} + +void Ddsu666DrvInit(void) +{ + if ((pin[GPIO_DDSU666_RX] < 99) && (pin[GPIO_DDSU666_TX] < 99)) { + energy_flg = XNRG_11; + } +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xnrg11(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_EVERY_250_MSECOND: + if (uptime > 4) { DDSU666Every250ms(); } + break; + case FUNC_INIT: + Ddsu666SnsInit(); + break; + case FUNC_PRE_INIT: + Ddsu666DrvInit(); + break; + } + return result; +} + +#endif // USE_DDSU666 +#endif // USE_ENERGY_SENSOR diff --git a/sonoff/xnrg_12_solaxX1.ino b/sonoff/xnrg_12_solaxX1.ino new file mode 100644 index 000000000..2c6c34a9e --- /dev/null +++ b/sonoff/xnrg_12_solaxX1.ino @@ -0,0 +1,528 @@ +/* + xnrg_12_solaxX1.ino - Solax X1 inverter RS485 support for Sonoff-Tasmota + + Copyright (C) 2019 Pablo Zerón + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef USE_ENERGY_SENSOR +#ifdef USE_SOLAX_X1 +/*********************************************************************************************\ + * Solax X1 Inverter +\*********************************************************************************************/ + +#define XNRG_12 12 + +#ifndef SOLAXX1_SPEED +#define SOLAXX1_SPEED 9600 // default solax rs485 speed +#endif + +#define INVERTER_ADDRESS 0x0A + +#define D_SOLAX_X1 "SolaxX1" + +#include + +enum solaxX1_Error +{ + solaxX1_ERR_NO_ERROR, + solaxX1_ERR_CRC_ERROR +}; + +union { + uint32_t ErrMessage; + struct { + //BYTE0 + uint8_t TzProtectFault:1;//0 + uint8_t MainsLostFault:1;//1 + uint8_t GridVoltFault:1;//2 + uint8_t GridFreqFault:1;//3 + uint8_t PLLLostFault:1;//4 + uint8_t BusVoltFault:1;//5 + uint8_t ErrBit06:1;//6 + uint8_t OciFault:1;//7 + //BYTE1 + uint8_t Dci_OCP_Fault:1;//8 + uint8_t ResidualCurrentFault:1;//9 + uint8_t PvVoltFault:1;//10 + uint8_t Ac10Mins_Voltage_Fault:1;//11 + uint8_t IsolationFault:1;//12 + uint8_t TemperatureOverFault:1;//13 + uint8_t FanFault:1;//14 + uint8_t ErrBit15:1;//15 + //BYTE2 + uint8_t SpiCommsFault:1;//16 + uint8_t SciCommsFault:1;//17 + uint8_t ErrBit18:1;//18 + uint8_t InputConfigFault:1;//19 + uint8_t EepromFault:1;//20 + uint8_t RelayFault:1;//21 + uint8_t SampleConsistenceFault:1;//22 + uint8_t ResidualCurrent_DeviceFault:1;//23 + //BYTE3 + uint8_t ErrBit24:1;//24 + uint8_t ErrBit25:1;//25 + uint8_t ErrBit26:1;//26 + uint8_t ErrBit27:1;//27 + uint8_t ErrBit28:1;//28 + uint8_t DCI_DeviceFault:1;//29 + uint8_t OtherDeviceFault:1;//30 + uint8_t ErrBit31:1;//31 + }; +} ErrCode; + +const char kSolaxMode[] PROGMEM = D_WAITING "|" D_CHECKING "|" D_WORKING "|" D_FAILURE; + +const char kSolaxError[] PROGMEM = + D_SOLAX_ERROR_0 "|" D_SOLAX_ERROR_1 "|" D_SOLAX_ERROR_2 "|" D_SOLAX_ERROR_3 "|" D_SOLAX_ERROR_4 "|" D_SOLAX_ERROR_5 "|" + D_SOLAX_ERROR_6 "|" D_SOLAX_ERROR_7 "|" D_SOLAX_ERROR_8; + +/*********************************************************************************************/ + +TasmotaSerial *solaxX1Serial; + +uint8_t solaxX1_Init = 1; + +struct SOLAXX1 { + float temperature = 0; + float energy_today = 0; + float dc1_voltage = 0; + float dc2_voltage = 0; + float dc1_current = 0; + float dc2_current = 0; + float energy_total = 0; + float runtime_total = 0; + float dc1_power = 0; + float dc2_power = 0; + + uint8_t status = 0; + uint32_t errorCode = 0; +} solaxX1; + +union { + uint8_t status; + struct { + uint8_t freeBit7:1; // Bit7 + uint8_t freeBit6:1; // Bit6 + uint8_t freeBit5:1; // Bit5 + uint8_t queryOffline:1; // Bit4 + uint8_t queryOfflineSend:1; // Bit3 + uint8_t hasAddress:1; // Bit2 + uint8_t inverterAddressSend:1; // Bit1 + uint8_t inverterSnReceived:1; // Bit0 + }; +} protocolStatus; + +uint8_t header[2] = {0xAA, 0x55}; +uint8_t source[2] = {0x00, 0x00}; +uint8_t destination[2] = {0x00, 0x00}; +uint8_t controlCode[1] = {0x00}; +uint8_t functionCode[1] = {0x00}; +uint8_t dataLength[1] = {0x00}; +uint8_t data[16] = {0}; + +uint8_t message[30]; + +/*********************************************************************************************/ + +bool solaxX1_RS485ReceiveReady(void) +{ + return (solaxX1Serial->available() > 1); +} + +void solaxX1_RS485Send(uint16_t msgLen) +{ + memcpy(message, header, 2); + memcpy(message + 2, source, 2); + memcpy(message + 4, destination, 2); + memcpy(message + 6, controlCode, 1); + memcpy(message + 7, functionCode, 1); + memcpy(message + 8, dataLength, 1); + memcpy(message + 9, data, sizeof(data)); + uint16_t crc = solaxX1_calculateCRC(message, msgLen); // calculate out crc bytes + + while (solaxX1Serial->available() > 0) + { // read serial if any old data is available + solaxX1Serial->read(); + } + + solaxX1Serial->flush(); + solaxX1Serial->write(message, msgLen); + solaxX1Serial->write(highByte(crc)); + solaxX1Serial->write(lowByte(crc)); + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, message, msgLen); +} + +uint8_t solaxX1_RS485Receive(uint8_t *value) +{ + uint8_t len = 0; + + while (solaxX1Serial->available() > 0) + { + value[len++] = (uint8_t)solaxX1Serial->read(); + } + + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, value, len); + + uint16_t crc = solaxX1_calculateCRC(value, len - 2); // calculate out crc bytes + + if (value[len - 1] == lowByte(crc) && value[len - 2] == highByte(crc)) + { // check calc crc with received crc + return solaxX1_ERR_NO_ERROR; + } + else + { + return solaxX1_ERR_CRC_ERROR; + } +} + +uint16_t solaxX1_calculateCRC(uint8_t *bExternTxPackage, uint8_t bLen) +{ + uint8_t i; + uint16_t wChkSum; + wChkSum = 0; + + for (i = 0; i < bLen; i++) + { + wChkSum = wChkSum + bExternTxPackage[i]; + } + return wChkSum; +} + +void solaxX1_SendInverterAddress() +{ + source[0] = 0x00; + destination[0] = 0x00; + destination[1] = 0x00; + controlCode[0] = 0x10; + functionCode[0] = 0x01; + dataLength[0] = 0x0F; + data[14] = INVERTER_ADDRESS; // Inverter Address, It must be unique in case of more inverters in the same rs485 net. + solaxX1_RS485Send(24); +} + +void solaxX1_QueryLiveData() +{ + source[0] = 0x01; + destination[0] = 0x00; + destination[1] = INVERTER_ADDRESS; + controlCode[0] = 0x11; + functionCode[0] = 0x02; + dataLength[0] = 0x00; + solaxX1_RS485Send(9); +} + +uint8_t solaxX1_ParseErrorCode(uint32_t code){ + ErrCode.ErrMessage = code; + + if (code == 0) return 0; + if (ErrCode.MainsLostFault) return 1; + if (ErrCode.GridVoltFault) return 2; + if (ErrCode.GridFreqFault) return 3; + if (ErrCode.PvVoltFault) return 4; + if (ErrCode.IsolationFault) return 5; + if (ErrCode.TemperatureOverFault) return 6; + if (ErrCode.FanFault) return 7; + if (ErrCode.OtherDeviceFault) return 8; +} + +/*********************************************************************************************/ + +uint8_t solaxX1_send_retry = 0; +uint8_t solaxX1_nodata_count = 0; + +void solaxX1250MSecond(void) // Every Second +{ + uint8_t value[61] = {0}; + bool data_ready = solaxX1_RS485ReceiveReady(); + + if (protocolStatus.hasAddress && (data_ready || solaxX1_send_retry == 0)) + { + if (data_ready) + { + uint8_t error = solaxX1_RS485Receive(value); + if (error) + { + DEBUG_SENSOR_LOG(PSTR("SX1: Data response CRC error")); + } + else + { + solaxX1_nodata_count = 0; + solaxX1_send_retry = 12; + Energy.data_valid[0] = 0; + + solaxX1.temperature = (float)((value[9] << 8) | value[10]); // Temperature + solaxX1.energy_today = (float)((value[11] << 8) | value[12]) * 0.1f; // Energy Today + solaxX1.dc1_voltage = (float)((value[13] << 8) | value[14]) * 0.1f; // PV1 Voltage + solaxX1.dc2_voltage = (float)((value[15] << 8) | value[16]) * 0.1f; // PV2 Voltage + solaxX1.dc1_current = (float)((value[17] << 8) | value[18]) * 0.1f; // PV1 Current + solaxX1.dc2_current = (float)((value[19] << 8) | value[20]) * 0.1f; // PV2 Current + Energy.current[0] = (float)((value[21] << 8) | value[22]) * 0.1f; // AC Current + Energy.voltage[0] = (float)((value[23] << 8) | value[24]) * 0.1f; // AC Voltage + Energy.frequency[0] = (float)((value[25] << 8) | value[26]) * 0.01f; // AC Frequency + Energy.active_power[0] = (float)((value[27] << 8) | value[28]); // AC Power + //temporal = (float)((value[29] << 8) | value[30]) * 0.1f; // Not Used + solaxX1.energy_total = (float)((value[31] << 8) | (value[32] << 8) | (value[33] << 8) | value[34]) * 0.1f; // Energy Total + solaxX1.runtime_total = (float)((value[35] << 8) | (value[36] << 8) | (value[37] << 8) | value[38]); // Work Time Total + solaxX1.status = (uint8_t)((value[39] << 8) | value[40]); // Work mode + //temporal = (float)((value[41] << 8) | value[42]); // Grid voltage fault value 0.1V + //temporal = (float)((value[43] << 8) | value[44]); // Gird frequency fault value 0.01Hz + //temporal = (float)((value[45] << 8) | value[46]); // Dc injection fault value 1mA + //temporal = (float)((value[47] << 8) | value[48]); // Temperature fault value + //temporal = (float)((value[49] << 8) | value[50]); // Pv1 voltage fault value 0.1V + //temporal = (float)((value[51] << 8) | value[52]); // Pv2 voltage fault value 0.1V + //temporal = (float)((value[53] << 8) | value[54]); // GFC fault value + solaxX1.errorCode = (uint32_t)((value[58] << 8) | (value[57] << 8) | (value[56] << 8) | value[55]); // Error Code + + solaxX1.dc1_power = solaxX1.dc1_voltage * solaxX1.dc1_current; + solaxX1.dc2_power = solaxX1.dc2_voltage * solaxX1.dc2_current; + + solaxX1_QueryLiveData(); + EnergyUpdateTotal(solaxX1.energy_total, true); // 484.708 kWh + } + } // End data Ready + + if (0 == solaxX1_send_retry && 255 != solaxX1_nodata_count) { + solaxX1_send_retry = 12; + solaxX1_QueryLiveData(); + } + + // While the inverter has not stable ambient light, will send an address adquired but go offline again, + // so no data will be received when the query is send, then we start the countdown to set the inverter as offline again. + if (255 == solaxX1_nodata_count) { + solaxX1_nodata_count = 0; + solaxX1_send_retry = 12; + } + } // end hasAddress && (data_ready || solaxX1_send_retry == 0) + else + { + if ((solaxX1_nodata_count % 4) == 0) { DEBUG_SENSOR_LOG(PSTR("SX1: No Data count: %d"), solaxX1_nodata_count); } + if (solaxX1_nodata_count < 10 * 4) // max. seconds without data + { + solaxX1_nodata_count++; + } + else if (255 != solaxX1_nodata_count) + { + // no data from RS485, reset values to 0 and set inverter as offline + solaxX1_nodata_count = 255; + solaxX1_send_retry = 12; + protocolStatus.status = 0b00001000; // queryOffline + Energy.data_valid[0] = ENERGY_WATCHDOG; + + solaxX1.temperature = solaxX1.dc1_voltage = solaxX1.dc2_voltage = solaxX1.dc1_current = solaxX1.dc2_current = solaxX1.dc1_power = 0; + solaxX1.dc2_power = solaxX1.status = Energy.current[0] = Energy.voltage[0] = Energy.frequency[0] = Energy.active_power[0] = 0; + //solaxX1.energy_today = solaxX1.energy_total = solaxX1.runtime_total = 0; + } + } + + if (!protocolStatus.hasAddress && (data_ready || solaxX1_send_retry == 0)) + { + if (data_ready) + { + // check address confirmation from inverter + if (protocolStatus.inverterAddressSend) + { + uint8_t error = solaxX1_RS485Receive(value); + if (error) + { + DEBUG_SENSOR_LOG(PSTR("SX1: Address confirmation response CRC error")); + } + else + { + if (value[6] == 0x10 && value[7] == 0x81 && value[9] == 0x06) + { + DEBUG_SENSOR_LOG(PSTR("SX1: Set hasAddress")); + protocolStatus.status = 0b00100000; // hasAddress + } + } + } + + // Check inverter serial number and send the set address request + if (protocolStatus.queryOfflineSend) + { + uint8_t error = solaxX1_RS485Receive(value); + if (error) + { + DEBUG_SENSOR_LOG(PSTR("SX1: Query Offline response CRC error")); + } + else + { + // Serial number from query response + if (value[6] == 0x10 && value[7] == 0x80 && protocolStatus.inverterSnReceived == false) + { + for (uint8_t i = 9; i <= 22; i++) + { + data[i - 9] = value[i]; + } + solaxX1_SendInverterAddress(); + protocolStatus.status = 0b1100000; // inverterSnReceived and inverterAddressSend + DEBUG_SENSOR_LOG(PSTR("SX1: Set inverterSnReceived and inverterAddressSend")); + } + } + } + } // End data ready + + if (solaxX1_send_retry == 0) + { + if (protocolStatus.queryOfflineSend) + { + protocolStatus.status = 0b00001000; // queryOffline + DEBUG_SENSOR_LOG(PSTR("SX1: Set Query Offline")); + } + solaxX1_send_retry = 12; + } + + // request to the inverter the serial number if offline + if (protocolStatus.queryOffline) + { + // We sent the message to query inverters in offline status + source[0] = 0x01; + destination[1] = 0x00; + controlCode[0] = 0x10; + functionCode[0] = 0x00; + dataLength[0] = 0x00; + solaxX1_RS485Send(9); + protocolStatus.status = 0b00010000; // queryOfflineSend + DEBUG_SENSOR_LOG(PSTR("SX1: Query Offline Send")); + } + } // end !hasAddress && (data_ready || solaxX1_send_retry == 0) + + if (!data_ready) + solaxX1_send_retry--; +} + +void solaxX1SnsInit(void) +{ + AddLog_P(LOG_LEVEL_DEBUG, PSTR("SX1: Solax X1 Inverter Init")); + DEBUG_SENSOR_LOG(PSTR("SX1: RX pin: %d, TX pin: %d"), pin[GPIO_SOLAXX1_RX], pin[GPIO_SOLAXX1_TX]); + protocolStatus.status = 0b00100000; // hasAddress + + solaxX1Serial = new TasmotaSerial(pin[GPIO_SOLAXX1_RX], pin[GPIO_SOLAXX1_TX], 1); + if (solaxX1Serial->begin(SOLAXX1_SPEED)) { + if (solaxX1Serial->hardwareSerial()) { ClaimSerial(); } + } else { + energy_flg = ENERGY_NONE; + } +} + +void solaxX1DrvInit(void) +{ + if ((pin[GPIO_SOLAXX1_RX] < 99) && (pin[GPIO_SOLAXX1_TX] < 99)) { + energy_flg = XNRG_12; + } +} + +#ifdef USE_WEBSERVER +const char HTTP_SNS_solaxX1_DATA1[] PROGMEM = + "{s}" D_SOLAX_X1 " " D_SOLAR_POWER "{m}%s " D_UNIT_WATT "{e}" + "{s}" D_SOLAX_X1 " " D_PV1_VOLTAGE "{m}%s " D_UNIT_VOLT "{e}" + "{s}" D_SOLAX_X1 " " D_PV1_CURRENT "{m}%s " D_UNIT_AMPERE "{e}" + "{s}" D_SOLAX_X1 " " D_PV1_POWER "{m}%s " D_UNIT_WATT "{e}"; +#ifdef SOLAXX1_PV2 +const char HTTP_SNS_solaxX1_DATA2[] PROGMEM = + "{s}" D_SOLAX_X1 " " D_PV2_VOLTAGE "{m}%s " D_UNIT_VOLT "{e}" + "{s}" D_SOLAX_X1 " " D_PV2_CURRENT "{m}%s " D_UNIT_AMPERE "{e}" + "{s}" D_SOLAX_X1 " " D_PV2_POWER "{m}%s " D_UNIT_WATT "{e}"; +#endif +const char HTTP_SNS_solaxX1_DATA3[] PROGMEM = + "{s}" D_SOLAX_X1 " " D_UPTIME "{m}%s " D_UNIT_HOUR "{e}" + "{s}" D_SOLAX_X1 " " D_STATUS "{m}%s" + "{s}" D_SOLAX_X1 " " D_ERROR "{m}%s"; +#endif // USE_WEBSERVER + +void solaxX1Show(bool json) +{ + char solar_power[33]; + dtostrfd(solaxX1.dc1_power + solaxX1.dc2_power, Settings.flag2.wattage_resolution, solar_power); + char pv1_voltage[33]; + dtostrfd(solaxX1.dc1_voltage, Settings.flag2.voltage_resolution, pv1_voltage); + char pv1_current[33]; + dtostrfd(solaxX1.dc1_current, Settings.flag2.current_resolution, pv1_current); + char pv1_power[33]; + dtostrfd(solaxX1.dc1_power, Settings.flag2.wattage_resolution, pv1_power); +#ifdef SOLAXX1_PV2 + char pv2_voltage[33]; + dtostrfd(solaxX1.dc2_voltage, Settings.flag2.voltage_resolution, pv2_voltage); + char pv2_current[33]; + dtostrfd(solaxX1.dc2_current, Settings.flag2.current_resolution, pv2_current); + char pv2_power[33]; + dtostrfd(solaxX1.dc2_power, Settings.flag2.wattage_resolution, pv2_power); +#endif + char temperature[33]; + dtostrfd(solaxX1.temperature, Settings.flag2.temperature_resolution, temperature); + char runtime[33]; + dtostrfd(solaxX1.runtime_total, 0, runtime); + char status[33]; + GetTextIndexed(status, sizeof(status), solaxX1.status, kSolaxMode); + + if (json) + { + ResponseAppend_P(PSTR(",\"" D_JSON_SOLAR_POWER "\":%s,\"" D_JSON_PV1_VOLTAGE "\":%s,\"" D_JSON_PV1_CURRENT "\":%s,\"" D_JSON_PV1_POWER "\":%s"), + solar_power, pv1_voltage, pv1_current, pv1_power); +#ifdef SOLAXX1_PV2 + ResponseAppend_P(PSTR(",\"" D_JSON_PV2_VOLTAGE "\":%s,\"" D_JSON_PV2_CURRENT "\":%s,\"" D_JSON_PV2_POWER "\":%s"), + pv2_voltage, pv2_current, pv2_power); +#endif + ResponseAppend_P(PSTR(",\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_RUNTIME "\":%s,\"" D_JSON_STATUS "\":\"%s\",\"" D_JSON_ERROR "\":%d"), + temperature, runtime, status, solaxX1.errorCode); + +#ifdef USE_WEBSERVER + } + else + { + WSContentSend_PD(HTTP_SNS_solaxX1_DATA1, solar_power, pv1_voltage, pv1_current, pv1_power); +#ifdef SOLAXX1_PV2 + WSContentSend_PD(HTTP_SNS_solaxX1_DATA2, pv2_voltage, pv2_current, pv2_power); +#endif + WSContentSend_PD(HTTP_SNS_TEMP, D_SOLAX_X1, temperature, TempUnit()); + char errorCodeString[33]; + WSContentSend_PD(HTTP_SNS_solaxX1_DATA3, runtime, status, + GetTextIndexed(errorCodeString, sizeof(errorCodeString), solaxX1_ParseErrorCode(solaxX1.errorCode), kSolaxError)); +#endif // USE_WEBSERVER + } +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xnrg12(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_EVERY_250_MSECOND: + if (uptime > 4) { solaxX1250MSecond(); } + break; + case FUNC_JSON_APPEND: + solaxX1Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + solaxX1Show(0); + break; +#endif // USE_WEBSERVER + case FUNC_INIT: + solaxX1SnsInit(); + break; + case FUNC_PRE_INIT: + solaxX1DrvInit(); + break; + } + return result; +} + +#endif // USE_SOLAX_X1_NRG +#endif // USE_ENERGY_SENSOR \ No newline at end of file diff --git a/sonoff/xnrg_interface.ino b/sonoff/xnrg_interface.ino index 22582f6c7..3e36b334b 100644 --- a/sonoff/xnrg_interface.ino +++ b/sonoff/xnrg_interface.ino @@ -20,9 +20,9 @@ #ifdef USE_ENERGY_SENSOR #ifdef XFUNC_PTR_IN_ROM -int (* const xnrg_func_ptr[])(uint8_t) PROGMEM = { // Energy driver Function Pointers +bool (* const xnrg_func_ptr[])(uint8_t) PROGMEM = { // Energy driver Function Pointers #else -int (* const xnrg_func_ptr[])(uint8_t) = { // Energy driver Function Pointers +bool (* const xnrg_func_ptr[])(uint8_t) = { // Energy driver Function Pointers #endif #ifdef XNRG_01 @@ -92,20 +92,23 @@ int (* const xnrg_func_ptr[])(uint8_t) = { // Energy driver Function Pointers const uint8_t xnrg_present = sizeof(xnrg_func_ptr) / sizeof(xnrg_func_ptr[0]); // Number of drivers found -int XnrgCall(uint8_t Function) +uint8_t xnrg_active = 0; + +bool XnrgCall(uint8_t function) { - int result = 0; - - for (uint32_t x = 0; x < xnrg_present; x++) { - result = xnrg_func_ptr[x](Function); - - if (result && ((FUNC_SERIAL == Function) || - (FUNC_COMMAND == Function) - )) { - break; + if (FUNC_PRE_INIT == function) { + for (uint32_t x = 0; x < xnrg_present; x++) { + xnrg_func_ptr[x](function); + if (energy_flg) { + xnrg_active = x; + return true; // Stop further driver investigation + } } } - return result; + else if (energy_flg) { + return xnrg_func_ptr[xnrg_active](function); + } + return false; } #endif // USE_ENERGY_SENSOR diff --git a/sonoff/xsns_01_counter.ino b/sonoff/xsns_01_counter.ino index 081954f36..e3b6a171a 100644 --- a/sonoff/xsns_01_counter.ino +++ b/sonoff/xsns_01_counter.ino @@ -24,9 +24,23 @@ #define XSNS_01 1 -unsigned long last_counter_timer[MAX_COUNTERS]; // Last counter time in micro seconds +#define D_PRFX_COUNTER "Counter" +#define D_CMND_COUNTERTYPE "Type" +#define D_CMND_COUNTERDEBOUNCE "Debounce" -#ifndef ARDUINO_ESP8266_RELEASE_2_3_0 // Fix core 2.5.x ISR not in IRAM Exception +const char kCounterCommands[] PROGMEM = D_PRFX_COUNTER "|" // Prefix + "|" D_CMND_COUNTERTYPE "|" D_CMND_COUNTERDEBOUNCE ; + +void (* const CounterCommand[])(void) PROGMEM = + { &CmndCounter, &CmndCounterType, &CmndCounterDebounce }; + +struct COUNTER { + uint32_t timer[MAX_COUNTERS]; // Last counter time in micro seconds + uint8_t no_pullup = 0; // Counter input pullup flag (1 = No pullup) + bool any_counter = false; +} Counter; + +#ifndef ARDUINO_ESP8266_RELEASE_2_3_0 // Fix core 2.5.x ISR not in IRAM Exception void CounterUpdate(uint8_t index) ICACHE_RAM_ATTR; void CounterUpdate1(void) ICACHE_RAM_ATTR; void CounterUpdate2(void) ICACHE_RAM_ATTR; @@ -36,41 +50,78 @@ void CounterUpdate4(void) ICACHE_RAM_ATTR; void CounterUpdate(uint8_t index) { - unsigned long counter_debounce_time = micros() - last_counter_timer[index -1]; - if (counter_debounce_time > Settings.pulse_counter_debounce * 1000) { - last_counter_timer[index -1] = micros(); - if (bitRead(Settings.pulse_counter_type, index -1)) { - RtcSettings.pulse_counter[index -1] = counter_debounce_time; + uint32_t time = micros(); + uint32_t debounce_time = time - Counter.timer[index]; + if (debounce_time > Settings.pulse_counter_debounce * 1000) { + Counter.timer[index] = time; + if (bitRead(Settings.pulse_counter_type, index)) { + RtcSettings.pulse_counter[index] = debounce_time; } else { - RtcSettings.pulse_counter[index -1]++; + RtcSettings.pulse_counter[index]++; } - -// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CNTR: Interrupt %d"), index); } } void CounterUpdate1(void) { - CounterUpdate(1); + CounterUpdate(0); } void CounterUpdate2(void) { - CounterUpdate(2); + CounterUpdate(1); } void CounterUpdate3(void) { - CounterUpdate(3); + CounterUpdate(2); } void CounterUpdate4(void) { - CounterUpdate(4); + CounterUpdate(3); } /********************************************************************************************/ +bool CounterPinState(void) +{ + if ((XdrvMailbox.index >= GPIO_CNTR1_NP) && (XdrvMailbox.index < (GPIO_CNTR1_NP + MAX_COUNTERS))) { + bitSet(Counter.no_pullup, XdrvMailbox.index - GPIO_CNTR1_NP); + XdrvMailbox.index -= (GPIO_CNTR1_NP - GPIO_CNTR1); + return true; + } + return false; +} + +void CounterInit(void) +{ + typedef void (*function) () ; + function counter_callbacks[] = { CounterUpdate1, CounterUpdate2, CounterUpdate3, CounterUpdate4 }; + + for (uint32_t i = 0; i < MAX_COUNTERS; i++) { + if (pin[GPIO_CNTR1 +i] < 99) { + Counter.any_counter = true; + pinMode(pin[GPIO_CNTR1 +i], bitRead(Counter.no_pullup, i) ? INPUT : INPUT_PULLUP); + attachInterrupt(pin[GPIO_CNTR1 +i], counter_callbacks[i], FALLING); + } + } +} + +void CounterEverySecond(void) +{ + for (uint32_t i = 0; i < MAX_COUNTERS; i++) { + if (pin[GPIO_CNTR1 +i] < 99) { + if (bitRead(Settings.pulse_counter_type, i)) { + uint32_t time = micros() - Counter.timer[i]; + if (time > 4200000000) { // 70 minutes + RtcSettings.pulse_counter[i] = 4200000000; // Set Timer to max in case of no more interrupts due to stall of measured device + } + } + } + } +} + void CounterSaveState(void) { for (uint32_t i = 0; i < MAX_COUNTERS; i++) { @@ -80,30 +131,10 @@ void CounterSaveState(void) } } -void CounterInit(void) -{ - typedef void (*function) () ; - function counter_callbacks[] = { CounterUpdate1, CounterUpdate2, CounterUpdate3, CounterUpdate4 }; - - for (uint32_t i = 0; i < MAX_COUNTERS; i++) { - if (pin[GPIO_CNTR1 +i] < 99) { - pinMode(pin[GPIO_CNTR1 +i], bitRead(counter_no_pullup, i) ? INPUT : INPUT_PULLUP); - attachInterrupt(pin[GPIO_CNTR1 +i], counter_callbacks[i], FALLING); - } - } -} - -#ifdef USE_WEBSERVER -const char HTTP_SNS_COUNTER[] PROGMEM = - "{s}" D_COUNTER "%d{m}%s%s{e}"; // {s} = , {m} = , {e} = -#endif // USE_WEBSERVER - void CounterShow(bool json) { - char stemp[10]; - + bool header = false; uint8_t dsxflg = 0; - uint8_t header = 0; for (uint32_t i = 0; i < MAX_COUNTERS; i++) { if (pin[GPIO_CNTR1 +i] < 99) { char counter[33]; @@ -117,11 +148,9 @@ void CounterShow(bool json) if (json) { if (!header) { ResponseAppend_P(PSTR(",\"COUNTER\":{")); - stemp[0] = '\0'; } - header++; - ResponseAppend_P(PSTR("%s\"C%d\":%s"), stemp, i +1, counter); - strlcpy(stemp, ",", sizeof(stemp)); + ResponseAppend_P(PSTR("%s\"C%d\":%s"), (header)?",":"", i +1, counter); + header = true; #ifdef USE_DOMOTICZ if ((0 == tele_period) && (1 == dsxflg)) { DomoticzSensor(DZ_COUNT, RtcSettings.pulse_counter[i]); @@ -130,21 +159,57 @@ void CounterShow(bool json) #endif // USE_DOMOTICZ #ifdef USE_WEBSERVER } else { - WSContentSend_PD(HTTP_SNS_COUNTER, i +1, counter, (bitRead(Settings.pulse_counter_type, i)) ? " " D_UNIT_SECOND : ""); + WSContentSend_PD(PSTR("{s}" D_COUNTER "%d{m}%s%s{e}"), + i +1, counter, (bitRead(Settings.pulse_counter_type, i)) ? " " D_UNIT_SECOND : ""); #endif // USE_WEBSERVER } } - if (bitRead(Settings.pulse_counter_type, i)) { - RtcSettings.pulse_counter[i] = 0xFFFFFFFF; // Set Timer to max in case of no more interrupts due to stall of measured device - } } - if (json) { - if (header) { - ResponseJsonEnd(); - } + if (header) { + ResponseJsonEnd(); } } +/*********************************************************************************************\ + * Commands +\*********************************************************************************************/ + +void CmndCounter(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_COUNTERS)) { + if ((XdrvMailbox.data_len > 0) && (pin[GPIO_CNTR1 + XdrvMailbox.index -1] < 99)) { + if ((XdrvMailbox.data[0] == '-') || (XdrvMailbox.data[0] == '+')) { + RtcSettings.pulse_counter[XdrvMailbox.index -1] += XdrvMailbox.payload; + Settings.pulse_counter[XdrvMailbox.index -1] += XdrvMailbox.payload; + } else { + RtcSettings.pulse_counter[XdrvMailbox.index -1] = XdrvMailbox.payload; + Settings.pulse_counter[XdrvMailbox.index -1] = XdrvMailbox.payload; + } + } + ResponseCmndIdxNumber(RtcSettings.pulse_counter[XdrvMailbox.index -1]); + } +} + +void CmndCounterType(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_COUNTERS)) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1) && (pin[GPIO_CNTR1 + XdrvMailbox.index -1] < 99)) { + bitWrite(Settings.pulse_counter_type, XdrvMailbox.index -1, XdrvMailbox.payload &1); + RtcSettings.pulse_counter[XdrvMailbox.index -1] = 0; + Settings.pulse_counter[XdrvMailbox.index -1] = 0; + } + ResponseCmndIdxNumber(bitRead(Settings.pulse_counter_type, XdrvMailbox.index -1)); + } +} + +void CmndCounterDebounce(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 32001)) { + Settings.pulse_counter_debounce = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.pulse_counter_debounce); +} + /*********************************************************************************************\ * Interface \*********************************************************************************************/ @@ -153,22 +218,36 @@ bool Xsns01(uint8_t function) { bool result = false; - switch (function) { - case FUNC_INIT: - CounterInit(); - break; - case FUNC_JSON_APPEND: - CounterShow(1); - break; + if (Counter.any_counter) { + switch (function) { + case FUNC_EVERY_SECOND: + CounterEverySecond(); + break; + case FUNC_JSON_APPEND: + CounterShow(1); + break; #ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - CounterShow(0); - break; + case FUNC_WEB_SENSOR: + CounterShow(0); + break; #endif // USE_WEBSERVER - case FUNC_SAVE_BEFORE_RESTART: - case FUNC_SAVE_AT_MIDNIGHT: - CounterSaveState(); - break; + case FUNC_SAVE_BEFORE_RESTART: + case FUNC_SAVE_AT_MIDNIGHT: + CounterSaveState(); + break; + case FUNC_COMMAND: + result = DecodeCommand(kCounterCommands, CounterCommand); + break; + } + } else { + switch (function) { + case FUNC_INIT: + CounterInit(); + break; + case FUNC_PIN_STATE: + result = CounterPinState(); + break; + } } return result; } diff --git a/sonoff/xsns_02_analog.ino b/sonoff/xsns_02_analog.ino index 6dbfcc53e..aebb12e4f 100644 --- a/sonoff/xsns_02_analog.ino +++ b/sonoff/xsns_02_analog.ino @@ -125,59 +125,6 @@ void AdcEverySecond(void) } } -/*********************************************************************************************\ - * Commands -\*********************************************************************************************/ - -#define D_CMND_ADCPARAM "AdcParam" -enum AdcCommands { CMND_ADCPARAM }; -const char kAdcCommands[] PROGMEM = D_CMND_ADCPARAM; - -bool AdcCommand(void) -{ - char command[CMDSZ]; - bool serviced = true; - - int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic, kAdcCommands); - if (CMND_ADCPARAM == command_code) { - if (XdrvMailbox.data_len) { - if ((ADC0_TEMP == XdrvMailbox.payload) || (ADC0_LIGHT == XdrvMailbox.payload)) { -// if ((XdrvMailbox.payload == my_adc0) && ((ADC0_TEMP == my_adc0) || (ADC0_LIGHT == my_adc0))) { - if (strstr(XdrvMailbox.data, ",") != nullptr) { // Process parameter entry - char sub_string[XdrvMailbox.data_len +1]; - // AdcParam 2, 32000, 10000, 3350 - // AdcParam 3, 10000, 12518931, -1.405 - Settings.adc_param_type = XdrvMailbox.payload; -// Settings.adc_param_type = my_adc0; - Settings.adc_param1 = strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10); - Settings.adc_param2 = strtol(subStr(sub_string, XdrvMailbox.data, ",", 3), nullptr, 10); - Settings.adc_param3 = (int)(CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 4)) * 10000); - } else { // Set default values based on current adc type - // AdcParam 2 - // AdcParam 3 - Settings.adc_param_type = 0; - AdcInit(); - } - } - } - - // AdcParam - int value = Settings.adc_param3; - uint8_t precision; - for (precision = 4; precision > 0; precision--) { - if (value % 10) { break; } - value /= 10; - } - char param3[33]; - dtostrfd(((double)Settings.adc_param3)/10000, precision, param3); - Response_P(PSTR("{\"" D_CMND_ADCPARAM "\":[%d,%d,%d,%s]}"), - Settings.adc_param_type, Settings.adc_param1, Settings.adc_param2, param3); - } - else serviced = false; // Unknown command - - return serviced; -} - void AdcShow(bool json) { if (ADC0_INPUT == my_adc0) { @@ -231,6 +178,78 @@ void AdcShow(bool json) } } +/*********************************************************************************************\ + * Commands +\*********************************************************************************************/ + +#define D_CMND_ADCPARAM "AdcParam" +const char kAdcCommands[] PROGMEM = "|" // No prefix + D_CMND_ADC "|" D_CMND_ADCS "|" D_CMND_ADCPARAM; + +void (* const AdcCommand[])(void) PROGMEM = + { &CmndAdc, &CmndAdcs, &CmndAdcParam }; + +void CmndAdc(void) +{ + if (ValidAdc() && (XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < ADC0_END)) { + Settings.my_adc0 = XdrvMailbox.payload; + restart_flag = 2; + } + char stemp1[TOPSZ]; + Response_P(PSTR("{\"" D_CMND_ADC "0\":{\"%d\":\"%s\"}}"), Settings.my_adc0, GetTextIndexed(stemp1, sizeof(stemp1), Settings.my_adc0, kAdc0Names)); +} + +void CmndAdcs(void) +{ + Response_P(PSTR("{\"" D_CMND_ADCS "\":{")); + bool jsflg = false; + char stemp1[TOPSZ]; + for (uint32_t i = 0; i < ADC0_END; i++) { + if (jsflg) { + ResponseAppend_P(PSTR(",")); + } + jsflg = true; + ResponseAppend_P(PSTR("\"%d\":\"%s\""), i, GetTextIndexed(stemp1, sizeof(stemp1), i, kAdc0Names)); + } + ResponseJsonEndEnd(); +} + +void CmndAdcParam(void) +{ + if (XdrvMailbox.data_len) { + if ((ADC0_TEMP == XdrvMailbox.payload) || (ADC0_LIGHT == XdrvMailbox.payload)) { +// if ((XdrvMailbox.payload == my_adc0) && ((ADC0_TEMP == my_adc0) || (ADC0_LIGHT == my_adc0))) { + if (strstr(XdrvMailbox.data, ",") != nullptr) { // Process parameter entry + char sub_string[XdrvMailbox.data_len +1]; + // AdcParam 2, 32000, 10000, 3350 + // AdcParam 3, 10000, 12518931, -1.405 + Settings.adc_param_type = XdrvMailbox.payload; +// Settings.adc_param_type = my_adc0; + Settings.adc_param1 = strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10); + Settings.adc_param2 = strtol(subStr(sub_string, XdrvMailbox.data, ",", 3), nullptr, 10); + Settings.adc_param3 = (int)(CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 4)) * 10000); + } else { // Set default values based on current adc type + // AdcParam 2 + // AdcParam 3 + Settings.adc_param_type = 0; + AdcInit(); + } + } + } + + // AdcParam + int value = Settings.adc_param3; + uint8_t precision; + for (precision = 4; precision > 0; precision--) { + if (value % 10) { break; } + value /= 10; + } + char param3[33]; + dtostrfd(((double)Settings.adc_param3)/10000, precision, param3); + Response_P(PSTR("{\"" D_CMND_ADCPARAM "\":[%d,%d,%d,%s]}"), + Settings.adc_param_type, Settings.adc_param1, Settings.adc_param2, param3); +} + /*********************************************************************************************\ * Interface \*********************************************************************************************/ @@ -239,31 +258,34 @@ bool Xsns02(uint8_t function) { bool result = false; - if ((ADC0_INPUT == my_adc0) || (ADC0_TEMP == my_adc0) || (ADC0_LIGHT == my_adc0)) { - switch (function) { + switch (function) { + case FUNC_COMMAND: + result = DecodeCommand(kAdcCommands, AdcCommand); + break; + default: + if ((ADC0_INPUT == my_adc0) || (ADC0_TEMP == my_adc0) || (ADC0_LIGHT == my_adc0)) { + switch (function) { #ifdef USE_RULES - case FUNC_EVERY_250_MSECOND: - AdcEvery250ms(); - break; + case FUNC_EVERY_250_MSECOND: + AdcEvery250ms(); + break; #endif // USE_RULES - case FUNC_EVERY_SECOND: - AdcEverySecond(); - break; - case FUNC_INIT: - AdcInit(); - break; - case FUNC_COMMAND: - result = AdcCommand(); - break; - case FUNC_JSON_APPEND: - AdcShow(1); - break; + case FUNC_EVERY_SECOND: + AdcEverySecond(); + break; + case FUNC_INIT: + AdcInit(); + break; + case FUNC_JSON_APPEND: + AdcShow(1); + break; #ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - AdcShow(0); - break; + case FUNC_WEB_SENSOR: + AdcShow(0); + break; #endif // USE_WEBSERVER - } + } + } } return result; } diff --git a/sonoff/xsns_04_snfsc.ino b/sonoff/xsns_04_snfsc.ino index 6d974e9f4..98e842c76 100644 --- a/sonoff/xsns_04_snfsc.ino +++ b/sonoff/xsns_04_snfsc.ino @@ -17,6 +17,7 @@ along with this program. If not, see . */ +#ifdef USE_SONOFF_SC /*********************************************************************************************\ Sonoff Sc @@ -173,3 +174,5 @@ bool Xsns04(uint8_t function) } return result; } + +#endif // USE_SONOFF_SC diff --git a/sonoff/xsns_05_ds18b20.ino b/sonoff/xsns_05_ds18b20.ino deleted file mode 100644 index a0a05a44a..000000000 --- a/sonoff/xsns_05_ds18b20.ino +++ /dev/null @@ -1,253 +0,0 @@ -/* - xsns_05_ds18b20.ino - DS18B20 temperature sensor support for Sonoff-Tasmota - - Copyright (C) 2019 Theo Arends - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#ifdef USE_DS18B20 -/*********************************************************************************************\ - * DS18B20 - Temperature - Single sensor -\*********************************************************************************************/ - -#define XSNS_05 5 - -#define W1_SKIP_ROM 0xCC -#define W1_CONVERT_TEMP 0x44 -#define W1_READ_SCRATCHPAD 0xBE - -float ds18b20_temperature = 0; -uint8_t ds18b20_valid = 0; -uint8_t ds18x20_pin = 0; -char ds18b20_types[] = "DS18B20"; - -/*********************************************************************************************\ - * Embedded stripped and tuned OneWire library -\*********************************************************************************************/ - -uint8_t OneWireReset(void) -{ - uint8_t retries = 125; - - //noInterrupts(); -#ifdef DS18B20_INTERNAL_PULLUP - pinMode(ds18x20_pin, INPUT_PULLUP); -#else - pinMode(ds18x20_pin, INPUT); -#endif - do { - if (--retries == 0) { - return 0; - } - delayMicroseconds(2); - } while (!digitalRead(ds18x20_pin)); - pinMode(ds18x20_pin, OUTPUT); - digitalWrite(ds18x20_pin, LOW); - delayMicroseconds(480); -#ifdef DS18B20_INTERNAL_PULLUP - pinMode(ds18x20_pin, INPUT_PULLUP); -#else - pinMode(ds18x20_pin, INPUT); -#endif - delayMicroseconds(70); - uint8_t r = !digitalRead(ds18x20_pin); - //interrupts(); - delayMicroseconds(410); - return r; -} - -void OneWireWriteBit(uint8_t v) -{ - static const uint8_t delay_low[2] = { 65, 10 }; - static const uint8_t delay_high[2] = { 5, 55 }; - - v &= 1; - //noInterrupts(); - digitalWrite(ds18x20_pin, LOW); - pinMode(ds18x20_pin, OUTPUT); - delayMicroseconds(delay_low[v]); - digitalWrite(ds18x20_pin, HIGH); - //interrupts(); - delayMicroseconds(delay_high[v]); -} - -uint8_t OneWireReadBit(void) -{ - //noInterrupts(); - pinMode(ds18x20_pin, OUTPUT); - digitalWrite(ds18x20_pin, LOW); - delayMicroseconds(3); -#ifdef DS18B20_INTERNAL_PULLUP - pinMode(ds18x20_pin, INPUT_PULLUP); -#else - pinMode(ds18x20_pin, INPUT); -#endif - delayMicroseconds(10); - uint8_t r = digitalRead(ds18x20_pin); - //interrupts(); - delayMicroseconds(53); - return r; -} - -void OneWireWrite(uint8_t v) -{ - for (uint8_t bit_mask = 0x01; bit_mask; bit_mask <<= 1) { - OneWireWriteBit((bit_mask & v) ? 1 : 0); - } -} - -uint8_t OneWireRead(void) -{ - uint8_t r = 0; - - for (uint8_t bit_mask = 0x01; bit_mask; bit_mask <<= 1) { - if (OneWireReadBit()) { - r |= bit_mask; - } - } - return r; -} - -bool OneWireCrc8(uint8_t *addr) -{ - uint8_t crc = 0; - uint8_t len = 8; - - while (len--) { - uint8_t inbyte = *addr++; // from 0 to 7 - for (uint32_t i = 8; i; i--) { - uint8_t mix = (crc ^ inbyte) & 0x01; - crc >>= 1; - if (mix) { - crc ^= 0x8C; - } - inbyte >>= 1; - } - } - return (crc == *addr); // addr 8 -} - -/********************************************************************************************/ - -void Ds18b20Convert(void) -{ - OneWireReset(); - OneWireWrite(W1_SKIP_ROM); // Address all Sensors on Bus - OneWireWrite(W1_CONVERT_TEMP); // start conversion, no parasite power on at the end -// delay(750); // 750ms should be enough for 12bit conv -} - -bool Ds18b20Read(void) -{ - uint8_t data[9]; - int8_t sign = 1; - - if (ds18b20_valid) { ds18b20_valid--; } -/* - if (!OneWireReadBit()) { // Check end of measurement - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DSB D_SENSOR_BUSY)); - return; - } -*/ - for (uint32_t retry = 0; retry < 3; retry++) { - OneWireReset(); - OneWireWrite(W1_SKIP_ROM); - OneWireWrite(W1_READ_SCRATCHPAD); - for (uint32_t i = 0; i < 9; i++) { - data[i] = OneWireRead(); - } - if (OneWireCrc8(data)) { - uint16_t temp12 = (data[1] << 8) + data[0]; - if (temp12 > 2047) { - temp12 = (~temp12) +1; - sign = -1; - } - ds18b20_temperature = ConvertTemp(sign * temp12 * 0.0625); - ds18b20_valid = SENSOR_MAX_MISS; - return true; - } - } - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DSB D_SENSOR_CRC_ERROR)); - return false; -} - -/********************************************************************************************/ - -void Ds18b20EverySecond(void) -{ - ds18x20_pin = pin[GPIO_DSB]; - if (uptime &1) { - // 2mS - Ds18b20Convert(); // Start conversion, takes up to one second - } else { - // 12mS - if (!Ds18b20Read()) { // Read temperature - AddLogMissed(ds18b20_types, ds18b20_valid); - } - } -} - -void Ds18b20Show(bool json) -{ - if (ds18b20_valid) { // Check for valid temperature - char temperature[33]; - dtostrfd(ds18b20_temperature, Settings.flag2.temperature_resolution, temperature); - if(json) { - ResponseAppend_P(JSON_SNS_TEMP, ds18b20_types, temperature); -#ifdef USE_DOMOTICZ - if (0 == tele_period) { - DomoticzSensor(DZ_TEMP, temperature); - } -#endif // USE_DOMOTICZ -#ifdef USE_KNX - if (0 == tele_period) { - KnxSensor(KNX_TEMPERATURE, ds18b20_temperature); - } -#endif // USE_KNX -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_TEMP, ds18b20_types, temperature, TempUnit()); -#endif // USE_WEBSERVER - } - } -} - -/*********************************************************************************************\ - * Interface -\*********************************************************************************************/ - -bool Xsns05(uint8_t function) -{ - bool result = false; - - if (pin[GPIO_DSB] < 99) { - switch (function) { - case FUNC_EVERY_SECOND: - Ds18b20EverySecond(); - break; - case FUNC_JSON_APPEND: - Ds18b20Show(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - Ds18b20Show(0); - break; -#endif // USE_WEBSERVER - } - } - return result; -} - -#endif // USE_DS18B20 diff --git a/sonoff/xsns_05_ds18x20.ino b/sonoff/xsns_05_ds18x20.ino index ca3d667da..ccbb5df5e 100644 --- a/sonoff/xsns_05_ds18x20.ino +++ b/sonoff/xsns_05_ds18x20.ino @@ -74,7 +74,11 @@ uint8_t OneWireReset(void) uint8_t retries = 125; //noInterrupts(); +#ifdef DS18B20_INTERNAL_PULLUP + pinMode(ds18x20_pin, INPUT_PULLUP); +#else pinMode(ds18x20_pin, INPUT); +#endif do { if (--retries == 0) { return 0; @@ -84,7 +88,11 @@ uint8_t OneWireReset(void) pinMode(ds18x20_pin, OUTPUT); digitalWrite(ds18x20_pin, LOW); delayMicroseconds(480); +#ifdef DS18B20_INTERNAL_PULLUP + pinMode(ds18x20_pin, INPUT_PULLUP); +#else pinMode(ds18x20_pin, INPUT); +#endif delayMicroseconds(70); uint8_t r = !digitalRead(ds18x20_pin); //interrupts(); @@ -113,7 +121,11 @@ uint8_t OneWireReadBit(void) pinMode(ds18x20_pin, OUTPUT); digitalWrite(ds18x20_pin, LOW); delayMicroseconds(3); +#ifdef DS18B20_INTERNAL_PULLUP + pinMode(ds18x20_pin, INPUT_PULLUP); +#else pinMode(ds18x20_pin, INPUT); +#endif delayMicroseconds(10); uint8_t r = digitalRead(ds18x20_pin); //interrupts(); @@ -432,7 +444,7 @@ void Ds18x20Show(bool json) if (json) { if (1 == ds18x20_sensors) { - ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_TEMPERATURE "\":%s}"), ds18x20_types, temperature); + ResponseAppend_P(JSON_SNS_TEMP, ds18x20_types, temperature); } else { char address[17]; for (uint32_t j = 0; j < 6; j++) { diff --git a/sonoff/xsns_05_ds18x20_legacy.ino b/sonoff/xsns_05_ds18x20_legacy.ino deleted file mode 100644 index 6fe0d764c..000000000 --- a/sonoff/xsns_05_ds18x20_legacy.ino +++ /dev/null @@ -1,245 +0,0 @@ -/* - xsns_05_ds18x20_legacy.ino - DS18x20 temperature sensor support for Sonoff-Tasmota - - Copyright (C) 2019 Heiko Krupp and Theo Arends - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#ifdef USE_DS18x20_LEGACY -/*********************************************************************************************\ - * DS18B20 - Temperature -\*********************************************************************************************/ - -#define XSNS_05 5 - -#define DS18S20_CHIPID 0x10 -#define DS18B20_CHIPID 0x28 -#define MAX31850_CHIPID 0x3B - -#define W1_SKIP_ROM 0xCC -#define W1_CONVERT_TEMP 0x44 -#define W1_READ_SCRATCHPAD 0xBE - -#define DS18X20_MAX_SENSORS 8 - -#include - -OneWire *ds = nullptr; - -uint8_t ds18x20_address[DS18X20_MAX_SENSORS][8]; -uint8_t ds18x20_index[DS18X20_MAX_SENSORS]; -uint8_t ds18x20_sensors = 0; -char ds18x20_types[9]; - -void Ds18x20Init(void) -{ - ds = new OneWire(pin[GPIO_DSB]); -} - -void Ds18x20Search(void) -{ - uint8_t num_sensors=0; - uint8_t sensor = 0; - - ds->reset_search(); - for (num_sensors = 0; num_sensors < DS18X20_MAX_SENSORS; num_sensors) { - if (!ds->search(ds18x20_address[num_sensors])) { - ds->reset_search(); - break; - } - // If CRC Ok and Type DS18S20, DS18B20 or MAX31850 - if ((OneWire::crc8(ds18x20_address[num_sensors], 7) == ds18x20_address[num_sensors][7]) && - ((ds18x20_address[num_sensors][0]==DS18S20_CHIPID) || (ds18x20_address[num_sensors][0]==DS18B20_CHIPID) || (ds18x20_address[num_sensors][0]==MAX31850_CHIPID))) { - num_sensors++; - } - } - for (uint32_t i = 0; i < num_sensors; i++) { - ds18x20_index[i] = i; - } - for (uint32_t i = 0; i < num_sensors; i++) { - for (uint32_t j = i + 1; j < num_sensors; j++) { - if (uint32_t(ds18x20_address[ds18x20_index[i]]) > uint32_t(ds18x20_address[ds18x20_index[j]])) { - std::swap(ds18x20_index[i], ds18x20_index[j]); - } - } - } - ds18x20_sensors = num_sensors; -} - -uint8_t Ds18x20Sensors(void) -{ - return ds18x20_sensors; -} - -String Ds18x20Addresses(uint8_t sensor) -{ - char address[20]; - - for (uint32_t i = 0; i < 8; i++) { - sprintf(address+2*i, "%02X", ds18x20_address[ds18x20_index[sensor]][i]); - } - return String(address); -} - -void Ds18x20Convert(void) -{ - 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 -} - -bool Ds18x20Read(uint8_t sensor, float &t) -{ - uint8_t data[12]; - int8_t sign = 1; - uint16_t temp12 = 0; - int16_t temp14 = 0; - float temp9 = 0.0; - uint8_t present = 0; - - t = NAN; - - ds->reset(); - ds->select(ds18x20_address[ds18x20_index[sensor]]); - ds->write(W1_READ_SCRATCHPAD); // Read Scratchpad - - for (uint32_t i = 0; i < 9; i++) { - data[i] = ds->read(); - } - if (OneWire::crc8(data, 8) == data[8]) { - switch(ds18x20_address[ds18x20_index[sensor]][0]) { - case DS18S20_CHIPID: - if (data[1] > 0x80) { - data[0] = (~data[0]) +1; - sign = -1; // App-Note fix possible sign error - } - temp9 = (float)(data[0] >> 1) * sign; - t = ConvertTemp((temp9 - 0.25) + ((16.0 - data[6]) / 16.0)); - break; - case DS18B20_CHIPID: - temp12 = (data[1] << 8) + data[0]; - if (temp12 > 2047) { - temp12 = (~temp12) +1; - sign = -1; - } - t = ConvertTemp(sign * temp12 * 0.0625); // Divide by 16 - break; - case MAX31850_CHIPID: - temp14 = (data[1] << 8) + (data[0] & 0xFC); - t = ConvertTemp(temp14 * 0.0625); // Divide by 16 - break; - } - } - return (!isnan(t)); -} - -/********************************************************************************************/ - -void Ds18x20Type(uint8_t sensor) -{ - strcpy_P(ds18x20_types, PSTR("DS18x20")); - switch(ds18x20_address[ds18x20_index[sensor]][0]) { - case DS18S20_CHIPID: - strcpy_P(ds18x20_types, PSTR("DS18S20")); - break; - case DS18B20_CHIPID: - strcpy_P(ds18x20_types, PSTR("DS18B20")); - break; - case MAX31850_CHIPID: - strcpy_P(ds18x20_types, PSTR("MAX31850")); - break; - } -} - -void Ds18x20Show(bool json) -{ - char stemp[10]; - float t; - - uint8_t dsxflg = 0; - for (uint32_t i = 0; i < Ds18x20Sensors(); i++) { - if (Ds18x20Read(i, t)) { // Check if read failed - Ds18x20Type(i); - char temperature[33]; - dtostrfd(t, Settings.flag2.temperature_resolution, temperature); - - if (json) { - if (!dsxflg) { - ResponseAppend_P(PSTR(",\"DS18x20\":{")); - stemp[0] = '\0'; - } - dsxflg++; - ResponseAppend_P(PSTR("%s\"DS%d\":{\"" D_JSON_TYPE "\":\"%s\",\"" D_JSON_ADDRESS "\":\"%s\",\"" D_JSON_TEMPERATURE "\":%s}"), - stemp, i +1, ds18x20_types, Ds18x20Addresses(i).c_str(), temperature); - strlcpy(stemp, ",", sizeof(stemp)); -#ifdef USE_DOMOTICZ - if ((0 == tele_period) && (1 == dsxflg)) { - DomoticzSensor(DZ_TEMP, temperature); - } -#endif // USE_DOMOTICZ -#ifdef USE_KNX - if ((0 == tele_period) && (1 == dsxflg)) { - KnxSensor(KNX_TEMPERATURE, t); - } -#endif // USE_KNX -#ifdef USE_WEBSERVER - } else { - snprintf_P(stemp, sizeof(stemp), PSTR("%s%c%d"), ds18x20_types, IndexSeparator(), i +1); - WSContentSend_PD(HTTP_SNS_TEMP, stemp, temperature, TempUnit()); -#endif // USE_WEBSERVER - } - } - } - if (json) { - if (dsxflg) { - ResponseJsonEnd(); - } - } - Ds18x20Search(); // Check for changes in sensors number - Ds18x20Convert(); // Start Conversion, takes up to one second -} - -/*********************************************************************************************\ - * Interface -\*********************************************************************************************/ - -bool Xsns05(uint8_t function) -{ - bool result = false; - - if (pin[GPIO_DSB] < 99) { - switch (function) { - case FUNC_INIT: - Ds18x20Init(); - break; - case FUNC_PREP_BEFORE_TELEPERIOD: - Ds18x20Search(); // Check for changes in sensors number - Ds18x20Convert(); // Start Conversion, takes up to one second - break; - case FUNC_JSON_APPEND: - Ds18x20Show(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - Ds18x20Show(0); - break; -#endif // USE_WEBSERVER - } - } - return result; -} - -#endif // USE_DS18x20_LEGACY diff --git a/sonoff/xsns_06_dht.ino b/sonoff/xsns_06_dht.ino index 06b2dddff..6604067e3 100644 --- a/sonoff/xsns_06_dht.ino +++ b/sonoff/xsns_06_dht.ino @@ -34,6 +34,7 @@ uint32_t dht_max_cycles; uint8_t dht_data[5]; uint8_t dht_sensors = 0; +bool dht_active = true; // DHT configured struct DHTSTRUCT { uint8_t pin; @@ -125,8 +126,9 @@ bool DhtRead(uint8_t sensor) uint8_t checksum = (dht_data[0] + dht_data[1] + dht_data[2] + dht_data[3]) & 0xFF; if (dht_data[4] != checksum) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT D_CHECKSUM_FAILURE " %02X, %02X, %02X, %02X, %02X =? %02X"), - dht_data[0], dht_data[1], dht_data[2], dht_data[3], dht_data[4], checksum); + char hex_char[15]; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT D_CHECKSUM_FAILURE " %s =? %02X"), + ToHex_P(dht_data, 5, hex_char, sizeof(hex_char), ' '), checksum); return false; } @@ -162,33 +164,40 @@ void DhtReadTempHum(uint8_t sensor) } } -bool DhtSetup(uint8_t pin, uint8_t type) -{ - bool success = false; - - if (dht_sensors < DHT_MAX_SENSORS) { - Dht[dht_sensors].pin = pin; - Dht[dht_sensors].type = type; - dht_sensors++; - success = true; - } - return success; -} - /********************************************************************************************/ +bool DhtPinState() +{ + if ((XdrvMailbox.index >= GPIO_DHT11) && (XdrvMailbox.index <= GPIO_SI7021)) { + if (dht_sensors < DHT_MAX_SENSORS) { + Dht[dht_sensors].pin = XdrvMailbox.payload; + Dht[dht_sensors].type = XdrvMailbox.index; + dht_sensors++; + XdrvMailbox.index = GPIO_DHT11; + } else { + XdrvMailbox.index = 0; + } + return true; + } + return false; +} + void DhtInit(void) { - dht_max_cycles = microsecondsToClockCycles(1000); // 1 millisecond timeout for reading pulses from DHT sensor. + if (dht_sensors) { + dht_max_cycles = microsecondsToClockCycles(1000); // 1 millisecond timeout for reading pulses from DHT sensor. - for (uint32_t i = 0; i < dht_sensors; i++) { - pinMode(Dht[i].pin, INPUT_PULLUP); - Dht[i].lastreadtime = 0; - Dht[i].lastresult = 0; - GetTextIndexed(Dht[i].stype, sizeof(Dht[i].stype), Dht[i].type, kSensorNames); - if (dht_sensors > 1) { - snprintf_P(Dht[i].stype, sizeof(Dht[i].stype), PSTR("%s%c%02d"), Dht[i].stype, IndexSeparator(), Dht[i].pin); + for (uint32_t i = 0; i < dht_sensors; i++) { + pinMode(Dht[i].pin, INPUT_PULLUP); + Dht[i].lastreadtime = 0; + Dht[i].lastresult = 0; + GetTextIndexed(Dht[i].stype, sizeof(Dht[i].stype), Dht[i].type, kSensorNames); + if (dht_sensors > 1) { + snprintf_P(Dht[i].stype, sizeof(Dht[i].stype), PSTR("%s%c%02d"), Dht[i].stype, IndexSeparator(), Dht[i].pin); + } } + } else { + dht_active = false; } } @@ -243,11 +252,8 @@ bool Xsns06(uint8_t function) { bool result = false; - if (dht_flg) { + if (dht_active) { switch (function) { - case FUNC_INIT: - DhtInit(); - break; case FUNC_EVERY_SECOND: DhtEverySecond(); break; @@ -259,6 +265,12 @@ bool Xsns06(uint8_t function) DhtShow(0); break; #endif // USE_WEBSERVER + case FUNC_INIT: + DhtInit(); + break; + case FUNC_PIN_STATE: + result = DhtPinState(); + break; } } return result; diff --git a/sonoff/xsns_13_ina219.ino b/sonoff/xsns_13_ina219.ino index 57e4a03a7..662db2209 100644 --- a/sonoff/xsns_13_ina219.ino +++ b/sonoff/xsns_13_ina219.ino @@ -84,20 +84,19 @@ #define INA219_REG_CURRENT (0x04) #define INA219_REG_CALIBRATION (0x05) -uint8_t ina219_type = 0; -uint8_t ina219_address; +uint8_t ina219_type[4] = {0,0,0,0}; uint8_t ina219_addresses[] = { INA219_ADDRESS1, INA219_ADDRESS2, INA219_ADDRESS3, INA219_ADDRESS4 }; uint32_t ina219_cal_value = 0; // The following multiplier is used to convert raw current values to mA, taking into account the current config settings uint32_t ina219_current_divider_ma = 0; -uint8_t ina219_valid = 0; -float ina219_voltage = 0; -float ina219_current = 0; +uint8_t ina219_valid[4] = {0,0,0,0}; +float ina219_voltage[4] = {0,0,0,0}; +float ina219_current[4] = {0,0,0,0}; char ina219_types[] = "INA219"; -bool Ina219SetCalibration(uint8_t mode) +bool Ina219SetCalibration(uint8_t mode, uint16_t addr) { uint16_t config = 0; @@ -120,40 +119,40 @@ bool Ina219SetCalibration(uint8_t mode) break; } // Set Calibration register to 'Cal' calculated above - bool success = I2cWrite16(ina219_address, INA219_REG_CALIBRATION, ina219_cal_value); + bool success = I2cWrite16(addr, INA219_REG_CALIBRATION, ina219_cal_value); if (success) { // Set Config register to take into account the settings above - I2cWrite16(ina219_address, INA219_REG_CONFIG, config); + I2cWrite16(addr, INA219_REG_CONFIG, config); } return success; } -float Ina219GetShuntVoltage_mV(void) +float Ina219GetShuntVoltage_mV(uint16_t addr) { // raw shunt voltage (16-bit signed integer, so +-32767) - int16_t value = I2cReadS16(ina219_address, INA219_REG_SHUNTVOLTAGE); + int16_t value = I2cReadS16(addr, INA219_REG_SHUNTVOLTAGE); // shunt voltage in mV (so +-327mV) return value * 0.01; } -float Ina219GetBusVoltage_V(void) +float Ina219GetBusVoltage_V(uint16_t addr) { // Shift to the right 3 to drop CNVR and OVF and multiply by LSB // raw bus voltage (16-bit signed integer, so +-32767) - int16_t value = (int16_t)(((uint16_t)I2cReadS16(ina219_address, INA219_REG_BUSVOLTAGE) >> 3) * 4); + int16_t value = (int16_t)(((uint16_t)I2cReadS16(addr, INA219_REG_BUSVOLTAGE) >> 3) * 4); // bus voltage in volts return value * 0.001; } -float Ina219GetCurrent_mA(void) +float Ina219GetCurrent_mA(uint16_t addr) { // Sometimes a sharp load will reset the INA219, which will reset the cal register, // meaning CURRENT and POWER will not be available ... avoid this by always setting // a cal value even if it's an unfortunate extra step - I2cWrite16(ina219_address, INA219_REG_CALIBRATION, ina219_cal_value); + I2cWrite16(addr, INA219_REG_CALIBRATION, ina219_cal_value); // Now we can safely read the CURRENT register! // raw current value (16-bit signed integer, so +-32767) - float value = I2cReadS16(ina219_address, INA219_REG_CURRENT); + float value = I2cReadS16(addr, INA219_REG_CURRENT); value /= ina219_current_divider_ma; // current value in mA, taking into account the config settings and current LSB return value; @@ -161,9 +160,15 @@ float Ina219GetCurrent_mA(void) bool Ina219Read(void) { - ina219_voltage = Ina219GetBusVoltage_V() + (Ina219GetShuntVoltage_mV() / 1000); - ina219_current = Ina219GetCurrent_mA() / 1000; - ina219_valid = SENSOR_MAX_MISS; + for (int i=0; i1) + snprintf_P(name, sizeof(name), PSTR("%s%c%d"), ina219_types, IndexSeparator(), sensor_num); + else + snprintf_P(name, sizeof(name), PSTR("%s"), ina219_types); + if (json) { - ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_VOLTAGE "\":%s,\"" D_JSON_CURRENT "\":%s,\"" D_JSON_POWERUSAGE "\":%s}"), - ina219_types, voltage, current, power); + ResponseAppend_P(PSTR(",\"%s\":{\"Id\":%02x,\"" D_JSON_VOLTAGE "\":%s,\"" D_JSON_CURRENT "\":%s,\"" D_JSON_POWERUSAGE "\":%s}"), + name, ina219_addresses[i], voltage, current, power); #ifdef USE_DOMOTICZ if (0 == tele_period) { DomoticzSensor(DZ_VOLTAGE, voltage); @@ -250,7 +263,7 @@ void Ina219Show(bool json) #endif // USE_DOMOTICZ #ifdef USE_WEBSERVER } else { - WSContentSend_PD(HTTP_SNS_INA219_DATA, voltage, current, power); + WSContentSend_PD(HTTP_SNS_INA219_DATA, name, voltage, name, current, name, power); #endif // USE_WEBSERVER } } diff --git a/sonoff/xsns_14_sht3x.ino b/sonoff/xsns_14_sht3x.ino index 36f39fe7b..2eb3317de 100755 --- a/sonoff/xsns_14_sht3x.ino +++ b/sonoff/xsns_14_sht3x.ino @@ -71,7 +71,7 @@ bool Sht3xRead(float &t, float &h, uint8_t sht3x_address) }; t = ConvertTemp((float)((((data[0] << 8) | data[1]) * 175) / 65535.0) - 45); h = ConvertHumidity((float)((((data[3] << 8) | data[4]) * 100) / 65535.0)); // Set global humidity - return (!isnan(t) && !isnan(h)); + return (!isnan(t) && !isnan(h) && (h != 0)); } /********************************************************************************************/ diff --git a/sonoff/xsns_18_pms5003.ino b/sonoff/xsns_18_pms5003.ino index ee14f9348..78c6ab05a 100644 --- a/sonoff/xsns_18_pms5003.ino +++ b/sonoff/xsns_18_pms5003.ino @@ -1,5 +1,5 @@ /* - xsns_18_pms5003.ino - PMS5003-7003 particle concentration sensor support for Sonoff-Tasmota + xsns_18_pms5003.ino - PMS3003, PMS5003, PMS7003 particle concentration sensor support for Sonoff-Tasmota Copyright (C) 2019 Theo Arends @@ -19,10 +19,13 @@ #ifdef USE_PMS5003 /*********************************************************************************************\ - * PlanTower PMS5003 and PMS7003 particle concentration sensor - * For background information see http://aqicn.org/sensor/pms5003-7003/ + * PlanTower PMS3003, PMS5003, PMS7003 particle concentration sensor + * For background information see http://aqicn.org/sensor/pms5003-7003/ or + * http://aqicn.org/sensor/pms3003/ * * Hardware Serial will be selected if GPIO3 = [PMS5003] + * You can either support PMS3003 or PMS5003-7003 at one time. To enable the PMS3003 support + * you must enable the define PMS_MODEL_PMS3003 on your configuration file. \*********************************************************************************************/ #define XSNS_18 18 @@ -34,12 +37,16 @@ TasmotaSerial *PmsSerial; uint8_t pms_type = 1; uint8_t pms_valid = 0; -struct pms5003data { +struct pmsX003data { uint16_t framelen; uint16_t pm10_standard, pm25_standard, pm100_standard; uint16_t pm10_env, pm25_env, pm100_env; +#ifdef PMS_MODEL_PMS3003 + uint16_t reserved1, reserved2, reserved3; +#else uint16_t particles_03um, particles_05um, particles_10um, particles_25um, particles_50um, particles_100um; uint16_t unused; +#endif // PMS_MODEL_PMS3003 uint16_t checksum; } pms_data; @@ -53,33 +60,63 @@ bool PmsReadData(void) while ((PmsSerial->peek() != 0x42) && PmsSerial->available()) { PmsSerial->read(); } +#ifdef PMS_MODEL_PMS3003 + if (PmsSerial->available() < 22) { +#else if (PmsSerial->available() < 32) { +#endif // PMS_MODEL_PMS3003 return false; } +#ifdef PMS_MODEL_PMS3003 + uint8_t buffer[22]; + PmsSerial->readBytes(buffer, 22); +#else uint8_t buffer[32]; - uint16_t sum = 0; PmsSerial->readBytes(buffer, 32); +#endif // PMS_MODEL_PMS3003 + uint16_t sum = 0; PmsSerial->flush(); // Make room for another burst +#ifdef PMS_MODEL_PMS3003 + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, 22); +#else AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, 32); +#endif // PMS_MODEL_PMS3003 // get checksum ready +#ifdef PMS_MODEL_PMS3003 + for (uint32_t i = 0; i < 20; i++) { +#else for (uint32_t i = 0; i < 30; i++) { +#endif // PMS_MODEL_PMS3003 sum += buffer[i]; } // The data comes in endian'd, this solves it so it works on all platforms +#ifdef PMS_MODEL_PMS3003 + uint16_t buffer_u16[10]; + for (uint32_t i = 0; i < 10; i++) { +#else uint16_t buffer_u16[15]; for (uint32_t i = 0; i < 15; i++) { +#endif // PMS_MODEL_PMS3003 buffer_u16[i] = buffer[2 + i*2 + 1]; buffer_u16[i] += (buffer[2 + i*2] << 8); } +#ifdef PMS_MODEL_PMS3003 + if (sum != buffer_u16[9]) { +#else if (sum != buffer_u16[14]) { +#endif // PMS_MODEL_PMS3003 AddLog_P(LOG_LEVEL_DEBUG, PSTR("PMS: " D_CHECKSUM_FAILURE)); return false; } +#ifdef PMS_MODEL_PMS3003 + memcpy((void *)&pms_data, (void *)buffer_u16, 20); +#else memcpy((void *)&pms_data, (void *)buffer_u16, 30); +#endif // PMS_MODEL_PMS3003 pms_valid = 10; return true; @@ -113,6 +150,15 @@ void PmsInit(void) } #ifdef USE_WEBSERVER +#ifdef PMS_MODEL_PMS3003 +const char HTTP_PMS3003_SNS[] PROGMEM = +// "{s}PMS3003 " D_STANDARD_CONCENTRATION " 1 " D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}" +// "{s}PMS3003 " D_STANDARD_CONCENTRATION " 2.5 " D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}" +// "{s}PMS3003 " D_STANDARD_CONCENTRATION " 10 " D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}" + "{s}PMS3003 " D_ENVIRONMENTAL_CONCENTRATION " 1 " D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}" + "{s}PMS3003 " D_ENVIRONMENTAL_CONCENTRATION " 2.5 " D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}" + "{s}PMS3003 " D_ENVIRONMENTAL_CONCENTRATION " 10 " D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}"; +#else const char HTTP_PMS5003_SNS[] PROGMEM = // "{s}PMS5003 " D_STANDARD_CONCENTRATION " 1 " D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}" // "{s}PMS5003 " D_STANDARD_CONCENTRATION " 2.5 " D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}" @@ -126,16 +172,23 @@ const char HTTP_PMS5003_SNS[] PROGMEM = "{s}PMS5003 " D_PARTICALS_BEYOND " 2.5 " D_UNIT_MICROMETER "{m}%d " D_UNIT_PARTS_PER_DECILITER "{e}" "{s}PMS5003 " D_PARTICALS_BEYOND " 5 " D_UNIT_MICROMETER "{m}%d " D_UNIT_PARTS_PER_DECILITER "{e}" "{s}PMS5003 " D_PARTICALS_BEYOND " 10 " D_UNIT_MICROMETER "{m}%d " D_UNIT_PARTS_PER_DECILITER "{e}"; // {s} = , {m} = , {e} = +#endif // PMS_MODEL_PMS3003 #endif // USE_WEBSERVER void PmsShow(bool json) { if (pms_valid) { if (json) { +#ifdef PMS_MODEL_PMS3003 + ResponseAppend_P(PSTR(",\"PMS3003\":{\"CF1\":%d,\"CF2.5\":%d,\"CF10\":%d,\"PM1\":%d,\"PM2.5\":%d,\"PM10\":%d}"), + pms_data.pm10_standard, pms_data.pm25_standard, pms_data.pm100_standard, + pms_data.pm10_env, pms_data.pm25_env, pms_data.pm100_env); +#else ResponseAppend_P(PSTR(",\"PMS5003\":{\"CF1\":%d,\"CF2.5\":%d,\"CF10\":%d,\"PM1\":%d,\"PM2.5\":%d,\"PM10\":%d,\"PB0.3\":%d,\"PB0.5\":%d,\"PB1\":%d,\"PB2.5\":%d,\"PB5\":%d,\"PB10\":%d}"), pms_data.pm10_standard, pms_data.pm25_standard, pms_data.pm100_standard, pms_data.pm10_env, pms_data.pm25_env, pms_data.pm100_env, pms_data.particles_03um, pms_data.particles_05um, pms_data.particles_10um, pms_data.particles_25um, pms_data.particles_50um, pms_data.particles_100um); +#endif // PMS_MODEL_PMS3003 #ifdef USE_DOMOTICZ if (0 == tele_period) { DomoticzSensor(DZ_COUNT, pms_data.pm10_env); // PM1 @@ -145,10 +198,17 @@ void PmsShow(bool json) #endif // USE_DOMOTICZ #ifdef USE_WEBSERVER } else { - WSContentSend_PD(HTTP_PMS5003_SNS, + +#ifdef PMS_MODEL_PMS3003 + WSContentSend_PD(HTTP_PMS3003_SNS, +// pms_data.pm10_standard, pms_data.pm25_standard, pms_data.pm100_standard, + pms_data.pm10_env, pms_data.pm25_env, pms_data.pm100_env); +#else + WSContentSend_PD(HTTP_PMS5003_SNS, // pms_data.pm10_standard, pms_data.pm25_standard, pms_data.pm100_standard, pms_data.pm10_env, pms_data.pm25_env, pms_data.pm100_env, pms_data.particles_03um, pms_data.particles_05um, pms_data.particles_10um, pms_data.particles_25um, pms_data.particles_50um, pms_data.particles_100um); +#endif // PMS_MODEL_PMS3003 #endif // USE_WEBSERVER } } diff --git a/sonoff/xsns_20_novasds.ino b/sonoff/xsns_20_novasds.ino index 74571973a..8e6dbd88d 100644 --- a/sonoff/xsns_20_novasds.ino +++ b/sonoff/xsns_20_novasds.ino @@ -31,14 +31,11 @@ #include -#ifndef WORKING_PERIOD -#define WORKING_PERIOD 5 // NodaSDS sleep working period in minutes +#ifndef STARTING_OFFSET +#define STARTING_OFFSET 30 // Turn on NovaSDS XX-seconds before tele_period is reached #endif -#ifndef NOVA_SDS_REINIT_CHECK -#define NOVA_SDS_REINIT_CHECK 80 // NodaSDS reinitalized check in seconds -#endif -#ifndef NOVA_SDS_QUERY_INTERVAL -#define NOVA_SDS_QUERY_INTERVAL 3 // NodaSDS query interval in seconds +#if STARTING_OFFSET < 10 +#error "Please set STARTING_OFFSET >= 10" #endif #ifndef NOVA_SDS_RECDATA_TIMEOUT #define NOVA_SDS_RECDATA_TIMEOUT 150 // NodaSDS query data timeout in ms @@ -51,11 +48,14 @@ TasmotaSerial *NovaSdsSerial; uint8_t novasds_type = 1; uint8_t novasds_valid = 0; +uint8_t cont_mode = 1; struct sds011data { uint16_t pm100; uint16_t pm25; } novasds_data; +uint16_t pm100_sum; +uint16_t pm25_sum; // NovaSDS commands #define NOVA_SDS_REPORTING_MODE 2 // Cmnd "data reporting mode" @@ -68,8 +68,8 @@ struct sds011data { #define NOVA_SDS_SET_MODE 1 // Subcmnd "set mode" #define NOVA_SDS_REPORT_ACTIVE 0 // Subcmnd "report active mode" - Sensor received query data command to report a measurement data #define NOVA_SDS_REPORT_QUERY 1 // Subcmnd "report query mode" - Sensor automatically reports a measurement data in a work period - #define NOVA_SDS_WORK 0 // Subcmnd "work mode" - #define NOVA_SDS_SLEEP 1 // Subcmnd "sleep mode" + #define NOVA_SDS_SLEEP 0 // Subcmnd "sleep mode" + #define NOVA_SDS_WORK 1 // Subcmnd "work mode" bool NovaSdsCommand(uint8_t byte1, uint8_t byte2, uint8_t byte3, uint16_t sensorid, uint8_t *buffer) @@ -80,9 +80,10 @@ bool NovaSdsCommand(uint8_t byte1, uint8_t byte2, uint8_t byte3, uint16_t sensor for (uint32_t i = 2; i < 17; i++) { novasds_cmnd[17] += novasds_cmnd[i]; } - //~ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SDS: Send %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X"), - //~ novasds_cmnd[0],novasds_cmnd[1],novasds_cmnd[2],novasds_cmnd[3],novasds_cmnd[4],novasds_cmnd[5],novasds_cmnd[6],novasds_cmnd[7],novasds_cmnd[8],novasds_cmnd[9], - //~ novasds_cmnd[10],novasds_cmnd[11],novasds_cmnd[12],novasds_cmnd[13],novasds_cmnd[14],novasds_cmnd[15],novasds_cmnd[16],novasds_cmnd[17],novasds_cmnd[18]); + +// char hex_char[60]; +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SDS: Send %s"), ToHex_P((unsigned char*)novasds_cmnd, 19, hex_char, sizeof(hex_char), ' ')); + // send cmnd NovaSdsSerial->write(novasds_cmnd, sizeof(novasds_cmnd)); NovaSdsSerial->flush(); @@ -123,10 +124,10 @@ bool NovaSdsCommand(uint8_t byte1, uint8_t byte2, uint8_t byte3, uint16_t sensor void NovaSdsSetWorkPeriod(void) { - // set sensor working period - NovaSdsCommand(NOVA_SDS_WORKING_PERIOD, NOVA_SDS_SET_MODE, Settings.novasds_period, NOVA_SDS_DEVICE_ID, nullptr); - // set sensor report only on query - NovaSdsCommand(NOVA_SDS_REPORTING_MODE, NOVA_SDS_SET_MODE, NOVA_SDS_REPORT_QUERY, NOVA_SDS_DEVICE_ID, nullptr); + // set sensor working period to default + NovaSdsCommand(NOVA_SDS_WORKING_PERIOD, NOVA_SDS_SET_MODE, 0, NOVA_SDS_DEVICE_ID, nullptr); + // set sensor report on query + NovaSdsCommand(NOVA_SDS_REPORTING_MODE, NOVA_SDS_SET_MODE, NOVA_SDS_REPORT_QUERY, NOVA_SDS_DEVICE_ID, nullptr); } bool NovaSdsReadData(void) @@ -145,19 +146,40 @@ bool NovaSdsReadData(void) void NovaSdsSecond(void) // Every second { - if (0 == (uptime % NOVA_SDS_REINIT_CHECK)) { - if (!novasds_valid) { - NovaSdsSetWorkPeriod(); - } - } else if (0 == (uptime % NOVA_SDS_QUERY_INTERVAL)) { - if (NovaSdsReadData()) { - novasds_valid = 10; - } else { - if (novasds_valid) { - novasds_valid--; - } + if (!novasds_valid) + { //communication problem, reinit + NovaSdsSetWorkPeriod(); + novasds_valid=1; + } + if((Settings.tele_period - Settings.novasds_startingoffset <= 0)) + { + if(!cont_mode) + { //switched to continuous mode + cont_mode = 1; + NovaSdsCommand(NOVA_SDS_SLEEP_AND_WORK, NOVA_SDS_SET_MODE, NOVA_SDS_WORK, NOVA_SDS_DEVICE_ID, nullptr); } } + else + cont_mode = 0; + + if(tele_period == Settings.tele_period - Settings.novasds_startingoffset && !cont_mode) + { //lets start fan and laser + NovaSdsCommand(NOVA_SDS_SLEEP_AND_WORK, NOVA_SDS_SET_MODE, NOVA_SDS_WORK, NOVA_SDS_DEVICE_ID, nullptr); + } + if(tele_period >= Settings.tele_period-5 && tele_period <= Settings.tele_period-2) + { //we are doing 4 measurements here + if(!(NovaSdsReadData())) novasds_valid=0; + pm100_sum += novasds_data.pm100; + pm25_sum += novasds_data.pm25; + } + if(tele_period == Settings.tele_period-1) + { //calculate the average of 4 measuremens + novasds_data.pm100 = pm100_sum >> 2; + novasds_data.pm25 = pm25_sum >> 2; + if(!cont_mode) + NovaSdsCommand(NOVA_SDS_SLEEP_AND_WORK, NOVA_SDS_SET_MODE, NOVA_SDS_SLEEP, NOVA_SDS_DEVICE_ID, nullptr); //stop fan and laser + pm100_sum = pm25_sum = 0; + } } /*********************************************************************************************\ @@ -169,10 +191,10 @@ void NovaSdsSecond(void) // Every second bool NovaSdsCommandSensor(void) { if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 256)) { - Settings.novasds_period = XdrvMailbox.payload; - NovaSdsSetWorkPeriod(); + if( XdrvMailbox.payload < 10 ) Settings.novasds_startingoffset = 10; + else Settings.novasds_startingoffset = XdrvMailbox.payload; } - Response_P(S_JSON_SENSOR_INDEX_NVALUE, XSNS_20, Settings.novasds_period); + Response_P(S_JSON_SENSOR_INDEX_NVALUE, XSNS_20, Settings.novasds_startingoffset); return true; } diff --git a/sonoff/xsns_21_sgp30.ino b/sonoff/xsns_21_sgp30.ino index e9c097dc4..7df1a65de 100644 --- a/sonoff/xsns_21_sgp30.ino +++ b/sonoff/xsns_21_sgp30.ino @@ -124,17 +124,14 @@ void Sgp30Show(bool json) if (sgp30_ready) { char abs_hum[33]; - if (global_update && global_humidity>0 && global_temperature!=9999) { - // has humidity + temperature - dtostrfd(sgp30_abshum,4,abs_hum); - } - if (json) { ResponseAppend_P(PSTR(",\"SGP30\":{\"" D_JSON_ECO2 "\":%d,\"" D_JSON_TVOC "\":%d"), sgp.eCO2, sgp.TVOC); - if (global_update) { + if (global_update && global_humidity>0 && global_temperature!=9999) { + // has humidity + temperature + dtostrfd(sgp30_abshum,4,abs_hum); ResponseAppend_P(PSTR(",\"" D_JSON_AHUM "\":%s"),abs_hum); } - ResponseAppend_P(PSTR("}")); + ResponseJsonEnd(); #ifdef USE_DOMOTICZ if (0 == tele_period) DomoticzSensor(DZ_AIRQUALITY, sgp.eCO2); #endif // USE_DOMOTICZ diff --git a/sonoff/xsns_23_sdm120.ino b/sonoff/xsns_23_sdm120.ino deleted file mode 100644 index b363ce16f..000000000 --- a/sonoff/xsns_23_sdm120.ino +++ /dev/null @@ -1,397 +0,0 @@ -/* - xsns_23_sdm120.ino - Eastron SDM120-Modbus energy meter support for Sonoff-Tasmota - - Copyright (C) 2019 Gennaro Tortone - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#ifdef USE_SDM120 - -/*********************************************************************************************\ - * Eastron SDM120-Modbus energy meter - * - * Based on: https://github.com/reaper7/SDM_Energy_Meter -\*********************************************************************************************/ - -#define XSNS_23 23 - -// can be user defined in my_user_config.h -#ifndef SDM120_SPEED - #define SDM120_SPEED 2400 // default SDM120 Modbus address -#endif -// can be user defined in my_user_config.h -#ifndef SDM120_ADDR - #define SDM120_ADDR 1 // default SDM120 Modbus address -#endif - - -#include - -enum SDM120_Error {SDM120_ERR_NO_ERROR=0, SDM120_ERR_CRC_ERROR, SDM120_ERR_WRONG_BYTES, SDM120_ERR_NOT_ENOUGHT_BYTES}; - -TasmotaSerial *SDM120Serial; - -uint8_t sdm120_type = 1; -//uint8_t sdm120_state = 0; - -float sdm120_voltage = 0; -float sdm120_current = 0; -float sdm120_active_power = 0; -float sdm120_apparent_power = 0; -float sdm120_reactive_power = 0; -float sdm120_power_factor = 0; -float sdm120_frequency = 0; -float sdm120_energy_total = 0; -float sdm120_phase_angle = 0; -float sdm120_import_active = 0; -float sdm120_export_active = 0; -float sdm120_import_reactive = 0; -float sdm120_export_reactive = 0; -float sdm120_total_reactive = 0; - -bool SDM120_ModbusReceiveReady(void) -{ - return (SDM120Serial->available() > 1); -} - -void SDM120_ModbusSend(uint8_t function_code, uint16_t start_address, uint16_t register_count) -{ - uint8_t frame[8]; - - frame[0] = SDM120_ADDR; - frame[1] = function_code; - frame[2] = (uint8_t)(start_address >> 8); - frame[3] = (uint8_t)(start_address); - frame[4] = (uint8_t)(register_count >> 8); - frame[5] = (uint8_t)(register_count); - - uint16_t crc = SDM120_calculateCRC(frame, 6); // calculate out crc only from first 6 bytes - frame[6] = lowByte(crc); - frame[7] = highByte(crc); - - while (SDM120Serial->available() > 0) { // read serial if any old data is available - SDM120Serial->read(); - } - - SDM120Serial->flush(); - SDM120Serial->write(frame, sizeof(frame)); -} - -uint8_t SDM120_ModbusReceive(float *value) -{ - uint8_t buffer[9]; - - *value = NAN; - uint8_t len = 0; - while (SDM120Serial->available() > 0) { - buffer[len++] = (uint8_t)SDM120Serial->read(); - } - - if (len < 9) { - return SDM120_ERR_NOT_ENOUGHT_BYTES; - } - - if (9 == len) { - if (0x01 == buffer[0] && 0x04 == buffer[1] && 4 == buffer[2]) { // check node number, op code and reply bytes count - if((SDM120_calculateCRC(buffer, 7)) == ((buffer[8] << 8) | buffer[7])) { //calculate crc from first 7 bytes and compare with received crc (bytes 7 & 8) - - ((uint8_t*)value)[3] = buffer[3]; - ((uint8_t*)value)[2] = buffer[4]; - ((uint8_t*)value)[1] = buffer[5]; - ((uint8_t*)value)[0] = buffer[6]; - - } else { - return SDM120_ERR_CRC_ERROR; - } - - } else { - return SDM120_ERR_WRONG_BYTES; - } - } - - return SDM120_ERR_NO_ERROR; -} - -uint16_t SDM120_calculateCRC(uint8_t *frame, uint8_t num) -{ - uint16_t crc, flag; - crc = 0xFFFF; - for (uint32_t i = 0; i < num; i++) { - crc ^= frame[i]; - for (uint32_t j = 8; j; j--) { - if ((crc & 0x0001) != 0) { // If the LSB is set - crc >>= 1; // Shift right and XOR 0xA001 - crc ^= 0xA001; - } else { // Else LSB is not set - crc >>= 1; // Just shift right - } - } - } - return crc; -} - -/*********************************************************************************************/ - -const uint16_t sdm120_start_addresses[] { - 0x0000, // SDM120C_VOLTAGE [V] - 0x0006, // SDM120C_CURRENT [A] - 0x000C, // SDM120C_POWER [W] - 0x0012, // SDM120C_APPARENT_POWER [VA] - 0x0018, // SDM120C_REACTIVE_POWER [VAR] - 0x001E, // SDM120C_POWER_FACTOR - 0x0046, // SDM120C_FREQUENCY [Hz] -#ifdef USE_SDM220 - 0x0156, // SDM120C_TOTAL_ACTIVE_ENERGY [Wh] - 0X0024, // SDM220_PHASE_ANGLE [Degre] - 0X0048, // SDM220_IMPORT_ACTIVE [kWh] - 0X004A, // SDM220_EXPORT_ACTIVE [kWh] - 0X004C, // SDM220_IMPORT_REACTIVE [kVArh] - 0X004E, // SDM220_EXPORT_REACTIVE [kVArh] - 0X0158 // SDM220 TOTAL_REACTIVE [kVArh] -#else // USE_SDM220 - 0x0156 // SDM120C_TOTAL_ACTIVE_ENERGY [Wh] -#endif // USE_SDM220 -}; - -uint8_t sdm120_read_state = 0; -uint8_t sdm120_send_retry = 0; -uint8_t sdm120_nodata_count = 0; - -void SDM120250ms(void) // Every 250 mSec -{ -// sdm120_state++; -// if (6 == sdm120_state) { // Every 300 mSec -// sdm120_state = 0; - - float value = 0; - bool data_ready = SDM120_ModbusReceiveReady(); - - if (data_ready) { - sdm120_nodata_count = 0; - uint8_t error = SDM120_ModbusReceive(&value); - if (error) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "SDM120 response error %d"), error); - } else { - switch(sdm120_read_state) { - case 0: - sdm120_voltage = value; - break; - - case 1: - sdm120_current = value; - break; - - case 2: - sdm120_active_power = value; - break; - - case 3: - sdm120_apparent_power = value; - break; - - case 4: - sdm120_reactive_power = value; - break; - - case 5: - sdm120_power_factor = value; - break; - - case 6: - sdm120_frequency = value; - break; - - case 7: - sdm120_energy_total = value; - break; -#ifdef USE_SDM220 - case 8: - sdm120_phase_angle = value; - break; - - case 9: - sdm120_import_active = value; - break; - - case 10: - sdm120_export_active = value; - break; - - case 11: - sdm120_import_reactive = value; - break; - - case 12: - sdm120_export_reactive = value; - break; - - case 13: - sdm120_total_reactive = value; - break; -#endif // USE_SDM220 - } // end switch - - sdm120_read_state++; - - if (sizeof(sdm120_start_addresses)/2 == sdm120_read_state) { - sdm120_read_state = 0; - } - } - } // end data ready - else { - if (sdm120_nodata_count <= (1000/250) * 4) { // max. 4 sec without data - sdm120_nodata_count++; - } else if (sdm120_nodata_count != 255) { - // no data from modbus, reset values to 0 - sdm120_nodata_count = 255; - sdm120_voltage = sdm120_current = sdm120_active_power = sdm120_apparent_power = sdm120_reactive_power = sdm120_power_factor = sdm120_frequency = sdm120_energy_total = 0; -#ifdef USE_SDM220 - sdm120_phase_angle = sdm120_import_active = sdm120_export_active = sdm120_import_reactive = sdm120_export_reactive = sdm120_total_reactive = 0; -#endif - } - } - - if (0 == sdm120_send_retry || data_ready) { - sdm120_send_retry = 5; - SDM120_ModbusSend(0x04, sdm120_start_addresses[sdm120_read_state], 2); - } else { - sdm120_send_retry--; - } -// } // end 300 ms -} - -void SDM120Init(void) -{ - sdm120_type = 0; - if ((pin[GPIO_SDM120_RX] < 99) && (pin[GPIO_SDM120_TX] < 99)) { - SDM120Serial = new TasmotaSerial(pin[GPIO_SDM120_RX], pin[GPIO_SDM120_TX], 1); - if (SDM120Serial->begin(SDM120_SPEED)) { - if (SDM120Serial->hardwareSerial()) { ClaimSerial(); } - sdm120_type = 1; - } - } -} - -#ifdef USE_WEBSERVER -const char HTTP_SNS_SDM120_DATA[] PROGMEM = - "{s}SDM120 " D_VOLTAGE "{m}%s " D_UNIT_VOLT "{e}" - "{s}SDM120 " D_CURRENT "{m}%s " D_UNIT_AMPERE "{e}" - "{s}SDM120 " D_POWERUSAGE_ACTIVE "{m}%s " D_UNIT_WATT "{e}" - "{s}SDM120 " D_POWERUSAGE_APPARENT "{m}%s " D_UNIT_VA "{e}" - "{s}SDM120 " D_POWERUSAGE_REACTIVE "{m}%s " D_UNIT_VAR "{e}" - "{s}SDM120 " D_POWER_FACTOR "{m}%s{e}" - "{s}SDM120 " D_FREQUENCY "{m}%s " D_UNIT_HERTZ "{e}" - "{s}SDM120 " D_ENERGY_TOTAL "{m}%s " D_UNIT_KILOWATTHOUR "{e}" -#ifdef USE_SDM220 - "{s}SDM120 " D_PHASE_ANGLE "{m}%s " D_UNIT_ANGLE "{e}" - "{s}SDM120 " D_IMPORT_ACTIVE "{m}%s " D_UNIT_KILOWATTHOUR "{e}" - "{s}SDM120 " D_EXPORT_ACTIVE "{m}%s " D_UNIT_KILOWATTHOUR "{e}" - "{s}SDM120 " D_IMPORT_REACTIVE "{m}%s " D_UNIT_KWARH "{e}" - "{s}SDM120 " D_EXPORT_REACTIVE "{m}%s " D_UNIT_KWARH "{e}" - "{s}SDM120 " D_TOTAL_REACTIVE "{m}%s " D_UNIT_KWARH "{e}" -#endif // USE_SDM220 - ; -#endif // USE_WEBSERVER - -void SDM120Show(bool json) -{ - char voltage[33]; - dtostrfd(sdm120_voltage, Settings.flag2.voltage_resolution, voltage); - char current[33]; - dtostrfd(sdm120_current, Settings.flag2.current_resolution, current); - char active_power[33]; - dtostrfd(sdm120_active_power, Settings.flag2.wattage_resolution, active_power); - char apparent_power[33]; - dtostrfd(sdm120_apparent_power, Settings.flag2.wattage_resolution, apparent_power); - char reactive_power[33]; - dtostrfd(sdm120_reactive_power, Settings.flag2.wattage_resolution, reactive_power); - char power_factor[33]; - dtostrfd(sdm120_power_factor, 2, power_factor); - char frequency[33]; - dtostrfd(sdm120_frequency, Settings.flag2.frequency_resolution, frequency); - char energy_total[33]; - dtostrfd(sdm120_energy_total, Settings.flag2.energy_resolution, energy_total); -#ifdef USE_SDM220 - char phase_angle[33]; - dtostrfd(sdm120_phase_angle, 2, phase_angle); - char import_active[33]; - dtostrfd(sdm120_import_active, Settings.flag2.wattage_resolution, import_active); - char export_active[33]; - dtostrfd(sdm120_export_active, Settings.flag2.wattage_resolution, export_active); - char import_reactive[33]; - dtostrfd(sdm120_import_reactive,Settings.flag2.wattage_resolution, import_reactive); - char export_reactive[33]; - dtostrfd(sdm120_export_reactive,Settings.flag2.wattage_resolution, export_reactive); - char total_reactive[33]; - dtostrfd(sdm120_total_reactive, Settings.flag2.wattage_resolution, total_reactive); -#endif // USE_SDM220 - if (json) { -#ifdef USE_SDM220 - ResponseAppend_P(PSTR(",\"" D_RSLT_ENERGY "\":{\"" D_JSON_TOTAL "\":%s,\"" D_JSON_ACTIVE_POWERUSAGE "\":%s,\"" D_JSON_APPARENT_POWERUSAGE "\":%s,\"" D_JSON_REACTIVE_POWERUSAGE "\":%s,\"" D_JSON_FREQUENCY "\":%s,\"" D_JSON_POWERFACTOR "\":%s,\"" D_JSON_VOLTAGE "\":%s,\"" D_JSON_CURRENT "\":%s,\"" D_JSON_PHASE_ANGLE "\":%s,\"" D_JSON_IMPORT_ACTIVE "\":%s,\"" D_JSON_EXPORT_ACTIVE "\":%s,\"" D_JSON_IMPORT_REACTIVE "\":%s,\"" D_JSON_EXPORT_REACTIVE "\":%s,\"" D_JSON_TOTAL_REACTIVE "\":%s}"), - energy_total, active_power, apparent_power, reactive_power, frequency, power_factor, voltage, current, phase_angle, import_active, export_active, import_reactive, export_reactive, total_reactive); -#else - ResponseAppend_P(PSTR(",\"" D_RSLT_ENERGY "\":{\"" D_JSON_TOTAL "\":%s,\"" D_JSON_ACTIVE_POWERUSAGE "\":%s,\"" D_JSON_APPARENT_POWERUSAGE "\":%s,\"" D_JSON_REACTIVE_POWERUSAGE "\":%s,\"" D_JSON_FREQUENCY "\":%s,\"" D_JSON_POWERFACTOR "\":%s,\"" D_JSON_VOLTAGE "\":%s,\"" D_JSON_CURRENT "\":%s}"), - energy_total, active_power, apparent_power, reactive_power, frequency, power_factor, voltage, current); -#endif // USE_SDM220 -#ifdef USE_DOMOTICZ - if (0 == tele_period) { - char energy_total_chr[33]; - dtostrfd(sdm120_energy_total * 1000, 1, energy_total_chr); - DomoticzSensor(DZ_VOLTAGE, voltage); - DomoticzSensor(DZ_CURRENT, current); - DomoticzSensorPowerEnergy((int)sdm120_active_power, energy_total_chr); - } -#endif // USE_DOMOTICZ -#ifdef USE_WEBSERVER - } else { -#ifdef USE_SDM220 - WSContentSend_PD(HTTP_SNS_SDM120_DATA, voltage, current, active_power, apparent_power, reactive_power, power_factor, frequency, energy_total, phase_angle,import_active,export_active,import_reactive,export_reactive,total_reactive); -#else - WSContentSend_PD(HTTP_SNS_SDM120_DATA, voltage, current, active_power, apparent_power, reactive_power, power_factor, frequency, energy_total); -#endif // USE_SDM220 -#endif // USE_WEBSERVER - } -} - -/*********************************************************************************************\ - * Interface -\*********************************************************************************************/ - -bool Xsns23(uint8_t function) -{ - bool result = false; - - if (sdm120_type) { - switch (function) { - case FUNC_INIT: - SDM120Init(); - break; - case FUNC_EVERY_250_MSECOND: - SDM120250ms(); - break; - case FUNC_JSON_APPEND: - SDM120Show(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - SDM120Show(0); - break; -#endif // USE_WEBSERVER - } - } - return result; -} - -#endif // USE_SDM120 diff --git a/sonoff/xsns_25_sdm630.ino b/sonoff/xsns_25_sdm630.ino deleted file mode 100644 index 93580b96b..000000000 --- a/sonoff/xsns_25_sdm630.ino +++ /dev/null @@ -1,362 +0,0 @@ -/* - xsns_25_sdm630.ino - Eastron SDM630-Modbus energy meter support for Sonoff-Tasmota - - Copyright (C) 2019 Gennaro Tortone - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#ifdef USE_SDM630 - -/*********************************************************************************************\ - * Eastron SDM630-Modbus energy meter - * - * Based on: https://github.com/reaper7/SDM_Energy_Meter -\*********************************************************************************************/ - -#define XSNS_25 25 - -#include - -TasmotaSerial *SDM630Serial; - -uint8_t sdm630_type = 1; -//uint8_t sdm630_state = 0; - -float sdm630_voltage[] = {0,0,0}; -float sdm630_current[] = {0,0,0}; -float sdm630_active_power[] = {0,0,0}; -float sdm630_reactive_power[] = {0,0,0}; -float sdm630_power_factor[] = {0,0,0}; -float sdm630_energy_total = 0; - -bool SDM630_ModbusReceiveReady(void) -{ - return (SDM630Serial->available() > 1); -} - -void SDM630_ModbusSend(uint8_t function_code, uint16_t start_address, uint16_t register_count) -{ - uint8_t frame[8]; - - frame[0] = 0x01; // default SDM630 Modbus address - frame[1] = function_code; - frame[2] = (uint8_t)(start_address >> 8); - frame[3] = (uint8_t)(start_address); - frame[4] = (uint8_t)(register_count >> 8); - frame[5] = (uint8_t)(register_count); - - uint16_t crc = SDM630_calculateCRC(frame, 6); // calculate out crc only from first 6 bytes - frame[6] = lowByte(crc); - frame[7] = highByte(crc); - - while (SDM630Serial->available() > 0) { // read serial if any old data is available - SDM630Serial->read(); - } - - SDM630Serial->flush(); - SDM630Serial->write(frame, sizeof(frame)); -} - -uint8_t SDM630_ModbusReceive(float *value) -{ - uint8_t buffer[9]; - - *value = NAN; - uint8_t len = 0; - while (SDM630Serial->available() > 0) { - buffer[len++] = (uint8_t)SDM630Serial->read(); - } - - if (len < 9) - return 3; // SDM_ERR_NOT_ENOUGHT_BYTES - - if (len == 9) { - - if (buffer[0] == 0x01 && buffer[1] == 0x04 && buffer[2] == 4) { // check node number, op code and reply bytes count - - if((SDM630_calculateCRC(buffer, 7)) == ((buffer[8] << 8) | buffer[7])) { //calculate crc from first 7 bytes and compare with received crc (bytes 7 & 8) - - ((uint8_t*)value)[3] = buffer[3]; - ((uint8_t*)value)[2] = buffer[4]; - ((uint8_t*)value)[1] = buffer[5]; - ((uint8_t*)value)[0] = buffer[6]; - - } else return 1; // SDM_ERR_CRC_ERROR - - } else return 2; // SDM_ERR_WRONG_BYTES - } - - return 0; // SDM_ERR_NO_ERROR -} - -uint16_t SDM630_calculateCRC(uint8_t *frame, uint8_t num) -{ - uint16_t crc, flag; - crc = 0xFFFF; - for (uint32_t i = 0; i < num; i++) { - crc ^= frame[i]; - for (uint32_t j = 8; j; j--) { - if ((crc & 0x0001) != 0) { // If the LSB is set - crc >>= 1; // Shift right and XOR 0xA001 - crc ^= 0xA001; - } else { // Else LSB is not set - crc >>= 1; // Just shift right - } - } - } - return crc; -} - -/*********************************************************************************************/ - -const uint16_t sdm630_start_addresses[] { - 0x0000, // L1 - SDM630_VOLTAGE [V] - 0x0002, // L2 - SDM630_VOLTAGE [V] - 0x0004, // L3 - SDM630_VOLTAGE [V] - 0x0006, // L1 - SDM630_CURRENT [A] - 0x0008, // L2 - SDM630_CURRENT [A] - 0x000A, // L3 - SDM630_CURRENT [A] - 0x000C, // L1 - SDM630_POWER [W] - 0x000E, // L2 - SDM630_POWER [W] - 0x0010, // L3 - SDM630_POWER [W] - 0x0018, // L1 - SDM630_REACTIVE_POWER [VAR] - 0x001A, // L2 - SDM630_REACTIVE_POWER [VAR] - 0x001C, // L3 - SDM630_REACTIVE_POWER [VAR] - 0x001E, // L1 - SDM630_POWER_FACTOR - 0x0020, // L2 - SDM630_POWER_FACTOR - 0x0022, // L3 - SDM630_POWER_FACTOR - 0x0156 // Total - SDM630_TOTAL_ACTIVE_ENERGY [Wh] -}; - -uint8_t sdm630_read_state = 0; -uint8_t sdm630_send_retry = 0; - -void SDM630250ms(void) // Every 250 mSec -{ -// sdm630_state++; -// if (6 == sdm630_state) { // Every 300 mSec -// sdm630_state = 0; - - float value = 0; - bool data_ready = SDM630_ModbusReceiveReady(); - - if (data_ready) { - uint8_t error = SDM630_ModbusReceive(&value); - if (error) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "SDM630 response error %d"), error); - } else { - switch(sdm630_read_state) { - case 0: - sdm630_voltage[0] = value; - break; - - case 1: - sdm630_voltage[1] = value; - break; - - case 2: - sdm630_voltage[2] = value; - break; - - case 3: - sdm630_current[0] = value; - break; - - case 4: - sdm630_current[1] = value; - break; - - case 5: - sdm630_current[2] = value; - break; - - case 6: - sdm630_active_power[0] = value; - break; - - case 7: - sdm630_active_power[1] = value; - break; - - case 8: - sdm630_active_power[2] = value; - break; - - case 9: - sdm630_reactive_power[0] = value; - break; - - case 10: - sdm630_reactive_power[1] = value; - break; - - case 11: - sdm630_reactive_power[2] = value; - break; - - case 12: - sdm630_power_factor[0] = value; - break; - - case 13: - sdm630_power_factor[1] = value; - break; - - case 14: - sdm630_power_factor[2] = value; - break; - - case 15: - sdm630_energy_total = value; - break; - } // end switch - - sdm630_read_state++; - - if (sizeof(sdm630_start_addresses)/2 == sdm630_read_state) { - sdm630_read_state = 0; - } - } - } // end data ready - - if (0 == sdm630_send_retry || data_ready) { - sdm630_send_retry = 5; - SDM630_ModbusSend(0x04, sdm630_start_addresses[sdm630_read_state], 2); - } else { - sdm630_send_retry--; - } -// } // end 300 ms -} - -void SDM630Init(void) -{ - sdm630_type = 0; - if ((pin[GPIO_SDM630_RX] < 99) && (pin[GPIO_SDM630_TX] < 99)) { - SDM630Serial = new TasmotaSerial(pin[GPIO_SDM630_RX], pin[GPIO_SDM630_TX], 1); -#ifdef SDM630_SPEED - if (SDM630Serial->begin(SDM630_SPEED)) { -#else - if (SDM630Serial->begin(2400)) { -#endif - if (SDM630Serial->hardwareSerial()) { ClaimSerial(); } - sdm630_type = 1; - } - } -} - -#ifdef USE_WEBSERVER -const char HTTP_SNS_SDM630_DATA[] PROGMEM = - "{s}SDM630 " D_VOLTAGE "{m}%s/%s/%s " D_UNIT_VOLT "{e}" - "{s}SDM630 " D_CURRENT "{m}%s/%s/%s " D_UNIT_AMPERE "{e}" - "{s}SDM630 " D_POWERUSAGE_ACTIVE "{m}%s/%s/%s " D_UNIT_WATT "{e}" - "{s}SDM630 " D_POWERUSAGE_REACTIVE "{m}%s/%s/%s " D_UNIT_VAR "{e}" - "{s}SDM630 " D_POWER_FACTOR "{m}%s/%s/%s{e}" - "{s}SDM630 " D_ENERGY_TOTAL "{m}%s " D_UNIT_KILOWATTHOUR "{e}"; -#endif // USE_WEBSERVER - -void SDM630Show(bool json) -{ - char voltage_l1[33]; - dtostrfd(sdm630_voltage[0], Settings.flag2.voltage_resolution, voltage_l1); - char voltage_l2[33]; - dtostrfd(sdm630_voltage[1], Settings.flag2.voltage_resolution, voltage_l2); - char voltage_l3[33]; - dtostrfd(sdm630_voltage[2], Settings.flag2.voltage_resolution, voltage_l3); - char current_l1[33]; - dtostrfd(sdm630_current[0], Settings.flag2.current_resolution, current_l1); - char current_l2[33]; - dtostrfd(sdm630_current[1], Settings.flag2.current_resolution, current_l2); - char current_l3[33]; - dtostrfd(sdm630_current[2], Settings.flag2.current_resolution, current_l3); - char active_power_l1[33]; - dtostrfd(sdm630_active_power[0], Settings.flag2.wattage_resolution, active_power_l1); - char active_power_l2[33]; - dtostrfd(sdm630_active_power[1], Settings.flag2.wattage_resolution, active_power_l2); - char active_power_l3[33]; - dtostrfd(sdm630_active_power[2], Settings.flag2.wattage_resolution, active_power_l3); - char reactive_power_l1[33]; - dtostrfd(sdm630_reactive_power[0], Settings.flag2.wattage_resolution, reactive_power_l1); - char reactive_power_l2[33]; - dtostrfd(sdm630_reactive_power[1], Settings.flag2.wattage_resolution, reactive_power_l2); - char reactive_power_l3[33]; - dtostrfd(sdm630_reactive_power[2], Settings.flag2.wattage_resolution, reactive_power_l3); - char power_factor_l1[33]; - dtostrfd(sdm630_power_factor[0], 2, power_factor_l1); - char power_factor_l2[33]; - dtostrfd(sdm630_power_factor[1], 2, power_factor_l2); - char power_factor_l3[33]; - dtostrfd(sdm630_power_factor[2], 2, power_factor_l3); - char energy_total[33]; - dtostrfd(sdm630_energy_total, Settings.flag2.energy_resolution, energy_total); - - if (json) { - ResponseAppend_P(PSTR(",\"" D_RSLT_ENERGY "\":{\"" D_JSON_TOTAL "\":%s,\"" - D_JSON_ACTIVE_POWERUSAGE "\":[%s,%s,%s],\"" D_JSON_REACTIVE_POWERUSAGE "\":[%s,%s,%s],\"" - D_JSON_POWERFACTOR "\":[%s,%s,%s],\"" D_JSON_VOLTAGE "\":[%s,%s,%s],\"" D_JSON_CURRENT "\":[%s,%s,%s]}"), - energy_total, active_power_l1, active_power_l2, active_power_l3, - reactive_power_l1, reactive_power_l2, reactive_power_l3, - power_factor_l1, power_factor_l2, power_factor_l3, - voltage_l1, voltage_l2, voltage_l3, - current_l1, current_l2, current_l3); -#ifdef USE_DOMOTICZ - if (0 == tele_period) { - char energy_total_chr[33]; - dtostrfd(sdm630_energy_total * 1000, 1, energy_total_chr); - DomoticzSensor(DZ_VOLTAGE, voltage_l1); - DomoticzSensor(DZ_CURRENT, current_l1); - DomoticzSensorPowerEnergy((int)sdm630_active_power[0], energy_total_chr); - } -#endif // USE_DOMOTICZ -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_SDM630_DATA, - voltage_l1, voltage_l2, voltage_l3, current_l1, current_l2, current_l3, - active_power_l1, active_power_l2, active_power_l3, - reactive_power_l1, reactive_power_l2, reactive_power_l3, - power_factor_l1, power_factor_l2, power_factor_l3, energy_total); -#endif // USE_WEBSERVER - } -} - -/*********************************************************************************************\ - * Interface -\*********************************************************************************************/ - -bool Xsns25(uint8_t function) -{ - bool result = false; - - if (sdm630_type) { - switch (function) { - case FUNC_INIT: - SDM630Init(); - break; - case FUNC_EVERY_250_MSECOND: - SDM630250ms(); - break; - case FUNC_JSON_APPEND: - SDM630Show(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - SDM630Show(0); - break; -#endif // USE_WEBSERVER - } - } - return result; -} - -#endif diff --git a/sonoff/xsns_29_mcp230xx.ino b/sonoff/xsns_29_mcp230xx.ino index 6d4384edb..0e9cc1039 100644 --- a/sonoff/xsns_29_mcp230xx.ino +++ b/sonoff/xsns_29_mcp230xx.ino @@ -303,8 +303,8 @@ void MCP230xx_CheckForInterrupt(void) { break; } if (int_tele) { - Response_P(PSTR("{\"" D_JSON_TIME "\":\"%s\",\"MCP230XX_INT\":{\"D%i\":%i,\"MS\":%lu}}"), - GetDateAndTime(DT_LOCAL).c_str(), intp+(mcp230xx_port*8), ((mcp230xx_intcap >> intp) & 0x01),millis_since_last_int); + ResponseTime_P(PSTR(",\"MCP230XX_INT\":{\"D%i\":%i,\"MS\":%lu}}"), + intp+(mcp230xx_port*8), ((mcp230xx_intcap >> intp) & 0x01),millis_since_last_int); MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR("MCP230XX_INT")); } if (int_event) { @@ -628,15 +628,15 @@ bool MCP230xx_Command(void) { #ifdef USE_MCP230xx_OUTPUT if (Settings.mcp230xx_config[pin].pinmode >= 5) { uint8_t pincmd = Settings.mcp230xx_config[pin].pinmode - 5; - if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "ON")) { + if ((!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "ON")) || (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "1"))) { MCP230xx_SetOutPin(pin,abs(pincmd-1)); return serviced; } - if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "OFF")) { + if ((!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "OFF")) || (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "0"))) { MCP230xx_SetOutPin(pin,pincmd); return serviced; } - if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "T")) { + if ((!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "T")) || (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "2"))) { MCP230xx_SetOutPin(pin,2); return serviced; } @@ -655,9 +655,9 @@ bool MCP230xx_Command(void) { intmode = atoi(subStr(sub_string, XdrvMailbox.data, ",", 4)); } #ifdef USE_MCP230xx_OUTPUT - if ((pin < mcp230xx_pincount) && (pinmode > 0) && (pinmode < 7) && (pullup < 2)) { + if ((pin < mcp230xx_pincount) && (pinmode > 0) && (pinmode < 7) && (pullup < 2) && (paramcount > 2)) { #else // not use OUTPUT - if ((pin < mcp230xx_pincount) && (pinmode > 0) && (pinmode < 5) && (pullup < 2)) { + if ((pin < mcp230xx_pincount) && (pinmode > 0) && (pinmode < 5) && (pullup < 2) && (paramcount > 2)) { #endif // USE_MCP230xx_OUTPUT Settings.mcp230xx_config[pin].pinmode=pinmode; Settings.mcp230xx_config[pin].pullup=pullup; @@ -729,7 +729,7 @@ void MCP230xx_OutputTelemetry(void) { } if (outputcount) { char stt[7]; - Response_P(PSTR("{\"" D_JSON_TIME "\":\"%s\",\"MCP230_OUT\": {"), GetDateAndTime(DT_LOCAL).c_str()); + ResponseTime_P(PSTR(",\"MCP230_OUT\":{")); for (uint32_t pinx = 0;pinx < mcp230xx_pincount;pinx++) { if (Settings.mcp230xx_config[pinx].pinmode >= 5) { sprintf(stt,ConvertNumTxt(((gpiototal>>pinx)&1),Settings.mcp230xx_config[pinx].pinmode)); @@ -744,7 +744,7 @@ void MCP230xx_OutputTelemetry(void) { #endif // USE_MCP230xx_OUTPUT void MCP230xx_Interrupt_Counter_Report(void) { - Response_P(PSTR("{\"" D_JSON_TIME "\":\"%s\",\"MCP230_INTTIMER\": {"), GetDateAndTime(DT_LOCAL).c_str()); + ResponseTime_P(PSTR(",\"MCP230_INTTIMER\":{")); for (uint32_t pinx = 0;pinx < mcp230xx_pincount;pinx++) { if (Settings.mcp230xx_config[pinx].int_count_en) { // Counting is enabled for this pin so we add to report ResponseAppend_P(PSTR("\"INTCNT_D%i\":%i,"),pinx,mcp230xx_int_counter[pinx]); @@ -758,7 +758,7 @@ void MCP230xx_Interrupt_Counter_Report(void) { void MCP230xx_Interrupt_Retain_Report(void) { uint16_t retainresult = 0; - Response_P(PSTR("{\"" D_JSON_TIME "\":\"%s\",\"MCP_INTRETAIN\": {"), GetDateAndTime(DT_LOCAL).c_str()); + ResponseTime_P(PSTR(",\"MCP_INTRETAIN\":{")); for (uint32_t pinx = 0;pinx < mcp230xx_pincount;pinx++) { if (Settings.mcp230xx_config[pinx].int_retain_flag) { ResponseAppend_P(PSTR("\"D%i\":%i,"),pinx,mcp230xx_int_retainer[pinx]); @@ -780,8 +780,6 @@ bool Xsns29(uint8_t function) if (i2c_flg) { switch (function) { - case FUNC_MQTT_DATA: - break; case FUNC_EVERY_SECOND: MCP230xx_Detect(); if (mcp230xx_int_counter_en) { diff --git a/sonoff/xsns_33_ds3231.ino b/sonoff/xsns_33_ds3231.ino index 98afa0ab0..ef2acfbd4 100644 --- a/sonoff/xsns_33_ds3231.ino +++ b/sonoff/xsns_33_ds3231.ino @@ -142,30 +142,30 @@ bool Xsns33(uint8_t function) break; case FUNC_EVERY_SECOND: TIME_T tmpTime; - if (!ds3231ReadStatus && DS3231chipDetected && utc_time < 1451602800 ) { // We still did not sync with NTP (time not valid) , so, read time from DS3231 + if (!ds3231ReadStatus && DS3231chipDetected && Rtc.utc_time < START_VALID_TIME ) { // We still did not sync with NTP (time not valid) , so, read time from DS3231 ntp_force_sync = true; //force to sync with ntp - utc_time = ReadFromDS3231(); //we read UTC TIME from DS3231 + Rtc.utc_time = ReadFromDS3231(); //we read UTC TIME from DS3231 // from this line, we just copy the function from "void RtcSecond()" at the support.ino ,line 2143 and above // We need it to set rules etc. - BreakTime(utc_time, tmpTime); - if (utc_time < 1451602800 ) { + BreakTime(Rtc.utc_time, tmpTime); + if (Rtc.utc_time < START_VALID_TIME ) { ds3231ReadStatus = true; //if time in DS3231 is valid, do not update again } RtcTime.year = tmpTime.year + 1970; - daylight_saving_time = RuleToTime(Settings.tflag[1], RtcTime.year); - standard_time = RuleToTime(Settings.tflag[0], RtcTime.year); + Rtc.daylight_saving_time = RuleToTime(Settings.tflag[1], RtcTime.year); + Rtc.standard_time = RuleToTime(Settings.tflag[0], RtcTime.year); AddLog_P2(LOG_LEVEL_INFO, PSTR("Set time from DS3231 to RTC (" D_UTC_TIME ") %s, (" D_DST_TIME ") %s, (" D_STD_TIME ") %s"), GetTime(0).c_str(), GetTime(2).c_str(), GetTime(3).c_str()); - if (local_time < 1451602800) { // 2016-01-01 + if (Rtc.local_time < START_VALID_TIME) { // 2016-01-01 rules_flag.time_init = 1; } else { rules_flag.time_set = 1; } } - else if (!ds3231WriteStatus && DS3231chipDetected && utc_time > 1451602800 && abs(utc_time - ReadFromDS3231()) > 60) {//if time is valid and is drift from RTC in more that 60 second + else if (!ds3231WriteStatus && DS3231chipDetected && Rtc.utc_time > START_VALID_TIME && abs(Rtc.utc_time - ReadFromDS3231()) > 60) {//if time is valid and is drift from RTC in more that 60 second AddLog_P2(LOG_LEVEL_INFO, PSTR("Write Time TO DS3231 from NTP (" D_UTC_TIME ") %s, (" D_DST_TIME ") %s, (" D_STD_TIME ") %s"), GetTime(0).c_str(), GetTime(2).c_str(), GetTime(3).c_str()); - SetDS3231Time (utc_time); //update the DS3231 time + SetDS3231Time (Rtc.utc_time); //update the DS3231 time ds3231WriteStatus = true; } break; diff --git a/sonoff/xsns_34_hx711.ino b/sonoff/xsns_34_hx711.ino index 8a362ddfd..28e8d253c 100644 --- a/sonoff/xsns_34_hx711.ino +++ b/sonoff/xsns_34_hx711.ino @@ -32,48 +32,53 @@ * - Execute command Sensor34 2 and follow messages shown \*********************************************************************************************/ -#define XSNS_34 34 +#define XSNS_34 34 #ifndef HX_MAX_WEIGHT -#define HX_MAX_WEIGHT 20000 // Default max weight in gram +#define HX_MAX_WEIGHT 20000 // Default max weight in gram #endif #ifndef HX_REFERENCE -#define HX_REFERENCE 250 // Default reference weight for calibration in gram +#define HX_REFERENCE 250 // Default reference weight for calibration in gram #endif #ifndef HX_SCALE -#define HX_SCALE 120 // Default result of measured weight / reference weight when scale is 1 +#define HX_SCALE 120 // Default result of measured weight / reference weight when scale is 1 #endif -#define HX_TIMEOUT 120 // A reading at default 10Hz (pin RATE to Gnd on HX711) can take up to 100 milliseconds -#define HX_SAMPLES 10 // Number of samples for average calculation -#define HX_CAL_TIMEOUT 15 // Calibration step window in number of seconds +#define HX_TIMEOUT 120 // A reading at default 10Hz (pin RATE to Gnd on HX711) can take up to 100 milliseconds +#define HX_SAMPLES 10 // Number of samples for average calculation +#define HX_CAL_TIMEOUT 15 // Calibration step window in number of seconds -#define HX_GAIN_128 1 // Channel A, gain factor 128 -#define HX_GAIN_32 2 // Channel B, gain factor 32 -#define HX_GAIN_64 3 // Channel A, gain factor 64 +#define HX_GAIN_128 1 // Channel A, gain factor 128 +#define HX_GAIN_32 2 // Channel B, gain factor 32 +#define HX_GAIN_64 3 // Channel A, gain factor 64 -#define D_JSON_WEIGHT_REF "WeightRef" -#define D_JSON_WEIGHT_CAL "WeightCal" -#define D_JSON_WEIGHT_MAX "WeightMax" -#define D_JSON_WEIGHT_ITEM "WeightItem" +#define D_JSON_WEIGHT_REF "WeightRef" +#define D_JSON_WEIGHT_CAL "WeightCal" +#define D_JSON_WEIGHT_MAX "WeightMax" +#define D_JSON_WEIGHT_ITEM "WeightItem" +#define D_JSON_WEIGHT_CHANGE "WeightChange" enum HxCalibrationSteps { HX_CAL_END, HX_CAL_LIMBO, HX_CAL_FINISH, HX_CAL_FAIL, HX_CAL_DONE, HX_CAL_FIRST, HX_CAL_RESET, HX_CAL_START }; const char kHxCalibrationStates[] PROGMEM = D_HX_CAL_FAIL "|" D_HX_CAL_DONE "|" D_HX_CAL_REFERENCE "|" D_HX_CAL_REMOVE; -long hx_weight = 0; -long hx_last_weight = 0; -long hx_sum_weight = 0; -long hx_offset = 0; -long hx_scale = 1; -uint8_t hx_type = 1; -uint8_t hx_sample_count = 0; -uint8_t hx_calibrate_step = HX_CAL_END; -uint8_t hx_calibrate_timer = 0; -uint8_t hx_calibrate_msg = 0; -uint8_t hx_pin_sck; -uint8_t hx_pin_dout; -bool hx_tare_flg = false; +struct HX { + long weight = 0; + long last_weight = 0; + long sum_weight = 0; + long offset = 0; + long scale = 1; + long weight_diff = 0; + uint8_t type = 1; + uint8_t sample_count = 0; + uint8_t calibrate_step = HX_CAL_END; + uint8_t calibrate_timer = 0; + uint8_t calibrate_msg = 0; + uint8_t pin_sck; + uint8_t pin_dout; + bool tare_flg = false; + bool weight_changed = false; +} Hx; /*********************************************************************************************/ @@ -81,8 +86,8 @@ bool HxIsReady(uint16_t timeout) { // A reading can take up to 100 mS or 600mS after power on uint32_t start = millis(); - while ((digitalRead(hx_pin_dout) == HIGH) && (millis() - start < timeout)) { yield(); } - return (digitalRead(hx_pin_dout) == LOW); + while ((digitalRead(Hx.pin_dout) == HIGH) && (millis() - start < timeout)) { yield(); } + return (digitalRead(Hx.pin_dout) == LOW); } long HxRead() @@ -93,14 +98,14 @@ long HxRead() uint8_t filler = 0x00; // pulse the clock pin 24 times to read the data - data[2] = shiftIn(hx_pin_dout, hx_pin_sck, MSBFIRST); - data[1] = shiftIn(hx_pin_dout, hx_pin_sck, MSBFIRST); - data[0] = shiftIn(hx_pin_dout, hx_pin_sck, MSBFIRST); + data[2] = shiftIn(Hx.pin_dout, Hx.pin_sck, MSBFIRST); + data[1] = shiftIn(Hx.pin_dout, Hx.pin_sck, MSBFIRST); + data[0] = shiftIn(Hx.pin_dout, Hx.pin_sck, MSBFIRST); // set the channel and the gain factor for the next reading using the clock pin for (unsigned int i = 0; i < HX_GAIN_128; i++) { - digitalWrite(hx_pin_sck, HIGH); - digitalWrite(hx_pin_sck, LOW); + digitalWrite(Hx.pin_sck, HIGH); + digitalWrite(Hx.pin_sck, LOW); } // Replicate the most significant bit to pad out a 32-bit signed integer @@ -119,10 +124,10 @@ long HxRead() void HxResetPart(void) { - hx_tare_flg = true; - hx_sum_weight = 0; - hx_sample_count = 0; - hx_last_weight = 0; + Hx.tare_flg = true; + Hx.sum_weight = 0; + Hx.sample_count = 0; + Hx.last_weight = 0; } void HxReset(void) @@ -135,8 +140,8 @@ void HxCalibrationStateTextJson(uint8_t msg_id) { char cal_text[30]; - hx_calibrate_msg = msg_id; - Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_34, GetTextIndexed(cal_text, sizeof(cal_text), hx_calibrate_msg, kHxCalibrationStates)); + Hx.calibrate_msg = msg_id; + Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_34, GetTextIndexed(cal_text, sizeof(cal_text), Hx.calibrate_msg, kHxCalibrationStates)); if (msg_id < 3) { MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR("Sensor34")); } } @@ -156,6 +161,8 @@ void HxCalibrationStateTextJson(uint8_t msg_id) * Sensor34 6 - Show item weigth in decigram * Sensor34 6 - Set item weight * Sensor34 7 - Save current weight to be used as start weight on restart + * Sensor34 8 0 - Disable JSON weight change message + * Sensor34 8 1 - Enable JSON weight change message \*********************************************************************************************/ bool HxCommand(void) @@ -177,10 +184,10 @@ bool HxCommand(void) if (strstr(XdrvMailbox.data, ",") != nullptr) { Settings.weight_reference = strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10); } - hx_scale = 1; + Hx.scale = 1; HxReset(); - hx_calibrate_step = HX_CAL_START; - hx_calibrate_timer = 1; + Hx.calibrate_step = HX_CAL_START; + Hx.calibrate_timer = 1; HxCalibrationStateTextJson(3); break; case 3: // WeightRef to user reference @@ -192,7 +199,7 @@ bool HxCommand(void) case 4: // WeightCal to user calculated value if (strstr(XdrvMailbox.data, ",") != nullptr) { Settings.weight_calibration = strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10); - hx_scale = Settings.weight_calibration; + Hx.scale = Settings.weight_calibration; } show_parms = true; break; @@ -209,18 +216,24 @@ bool HxCommand(void) show_parms = true; break; case 7: // WeightSave - Settings.energy_frequency_calibration = hx_weight; + Settings.energy_frequency_calibration = Hx.weight; Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_34, D_JSON_DONE); break; + case 8: // Json on weight change + if (strstr(XdrvMailbox.data, ",") != nullptr) { + Settings.SensorBits1.hx711_json_weight_change = strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10) & 1; + } + show_parms = true; + break; default: - serviced = false; + show_parms = true; } if (show_parms) { char item[33]; dtostrfd((float)Settings.weight_item / 10, 1, item); - Response_P(PSTR("{\"Sensor34\":{\"" D_JSON_WEIGHT_REF "\":%d,\"" D_JSON_WEIGHT_CAL "\":%d,\"" D_JSON_WEIGHT_MAX "\":%d,\"" D_JSON_WEIGHT_ITEM "\":%s}}"), - Settings.weight_reference, Settings.weight_calibration, Settings.weight_max * 1000, item); + Response_P(PSTR("{\"Sensor34\":{\"" D_JSON_WEIGHT_REF "\":%d,\"" D_JSON_WEIGHT_CAL "\":%d,\"" D_JSON_WEIGHT_MAX "\":%d,\"" D_JSON_WEIGHT_ITEM "\":%s,\"" D_JSON_WEIGHT_CHANGE "\":\"%s\"}}"), + Settings.weight_reference, Settings.weight_calibration, Settings.weight_max * 1000, item, GetStateText(Settings.SensorBits1.hx711_json_weight_change)); } return serviced; @@ -230,123 +243,138 @@ bool HxCommand(void) long HxWeight() { - return (hx_calibrate_step < HX_CAL_FAIL) ? hx_weight : 0; + return (Hx.calibrate_step < HX_CAL_FAIL) ? Hx.weight : 0; } void HxInit(void) { - hx_type = 0; + Hx.type = 0; if ((pin[GPIO_HX711_DAT] < 99) && (pin[GPIO_HX711_SCK] < 99)) { - hx_pin_sck = pin[GPIO_HX711_SCK]; - hx_pin_dout = pin[GPIO_HX711_DAT]; + Hx.pin_sck = pin[GPIO_HX711_SCK]; + Hx.pin_dout = pin[GPIO_HX711_DAT]; - pinMode(hx_pin_sck, OUTPUT); - pinMode(hx_pin_dout, INPUT); + pinMode(Hx.pin_sck, OUTPUT); + pinMode(Hx.pin_dout, INPUT); - digitalWrite(hx_pin_sck, LOW); + digitalWrite(Hx.pin_sck, LOW); if (HxIsReady(8 * HX_TIMEOUT)) { // Can take 600 milliseconds after power on if (!Settings.weight_max) { Settings.weight_max = HX_MAX_WEIGHT / 1000; } if (!Settings.weight_calibration) { Settings.weight_calibration = HX_SCALE; } if (!Settings.weight_reference) { Settings.weight_reference = HX_REFERENCE; } - hx_scale = Settings.weight_calibration; + Hx.scale = Settings.weight_calibration; HxRead(); HxResetPart(); - hx_type = 1; + Hx.type = 1; } } } void HxEvery100mSecond(void) { - hx_sum_weight += HxRead(); + Hx.sum_weight += HxRead(); - hx_sample_count++; - if (HX_SAMPLES == hx_sample_count) { - long average = hx_sum_weight / hx_sample_count; // grams - long value = average - hx_offset; // grams - hx_weight = value / hx_scale; // grams - if (hx_weight < 0) { + Hx.sample_count++; + if (HX_SAMPLES == Hx.sample_count) { + long average = Hx.sum_weight / Hx.sample_count; // grams + long value = average - Hx.offset; // grams + Hx.weight = value / Hx.scale; // grams + if (Hx.weight < 0) { if (Settings.energy_frequency_calibration) { - long difference = Settings.energy_frequency_calibration + hx_weight; - hx_last_weight = difference; + long difference = Settings.energy_frequency_calibration + Hx.weight; + Hx.last_weight = difference; if (difference < 0) { HxReset(); } // Cancel last weight as there seems to be no more weight on the scale } - hx_weight = 0; + Hx.weight = 0; } else { - hx_last_weight = Settings.energy_frequency_calibration; + Hx.last_weight = Settings.energy_frequency_calibration; } - if (hx_tare_flg) { - hx_tare_flg = false; - hx_offset = average; // grams + if (Hx.tare_flg) { + Hx.tare_flg = false; + Hx.offset = average; // grams } - if (hx_calibrate_step) { - hx_calibrate_timer--; + if (Hx.calibrate_step) { + Hx.calibrate_timer--; - if (HX_CAL_START == hx_calibrate_step) { // Skip reset just initiated - hx_calibrate_step--; - hx_calibrate_timer = HX_CAL_TIMEOUT * (10 / HX_SAMPLES); + if (HX_CAL_START == Hx.calibrate_step) { // Skip reset just initiated + Hx.calibrate_step--; + Hx.calibrate_timer = HX_CAL_TIMEOUT * (10 / HX_SAMPLES); } - else if (HX_CAL_RESET == hx_calibrate_step) { // Wait for stable reset - if (hx_calibrate_timer) { - if (hx_weight < (long)Settings.weight_reference) { - hx_calibrate_step--; - hx_calibrate_timer = HX_CAL_TIMEOUT * (10 / HX_SAMPLES); + else if (HX_CAL_RESET == Hx.calibrate_step) { // Wait for stable reset + if (Hx.calibrate_timer) { + if (Hx.weight < (long)Settings.weight_reference) { + Hx.calibrate_step--; + Hx.calibrate_timer = HX_CAL_TIMEOUT * (10 / HX_SAMPLES); HxCalibrationStateTextJson(2); } } else { - hx_calibrate_step = HX_CAL_FAIL; + Hx.calibrate_step = HX_CAL_FAIL; } } - else if (HX_CAL_FIRST == hx_calibrate_step) { // Wait for first reference weight - if (hx_calibrate_timer) { - if (hx_weight > (long)Settings.weight_reference) { - hx_calibrate_step--; + else if (HX_CAL_FIRST == Hx.calibrate_step) { // Wait for first reference weight + if (Hx.calibrate_timer) { + if (Hx.weight > (long)Settings.weight_reference) { + Hx.calibrate_step--; } } else { - hx_calibrate_step = HX_CAL_FAIL; + Hx.calibrate_step = HX_CAL_FAIL; } } - else if (HX_CAL_DONE == hx_calibrate_step) { // Second stable reference weight - if (hx_weight > (long)Settings.weight_reference) { - hx_calibrate_step = HX_CAL_FINISH; // Calibration done - Settings.weight_calibration = hx_weight / Settings.weight_reference; - hx_weight = 0; // Reset calibration value + else if (HX_CAL_DONE == Hx.calibrate_step) { // Second stable reference weight + if (Hx.weight > (long)Settings.weight_reference) { + Hx.calibrate_step = HX_CAL_FINISH; // Calibration done + Settings.weight_calibration = Hx.weight / Settings.weight_reference; + Hx.weight = 0; // Reset calibration value HxCalibrationStateTextJson(1); } else { - hx_calibrate_step = HX_CAL_FAIL; + Hx.calibrate_step = HX_CAL_FAIL; } } - if (HX_CAL_FAIL == hx_calibrate_step) { // Calibration failed - hx_calibrate_step--; - hx_tare_flg = true; // Perform a reset using old scale + if (HX_CAL_FAIL == Hx.calibrate_step) { // Calibration failed + Hx.calibrate_step--; + Hx.tare_flg = true; // Perform a reset using old scale HxCalibrationStateTextJson(0); } - if (HX_CAL_FINISH == hx_calibrate_step) { // Calibration finished - hx_calibrate_step--; - hx_calibrate_timer = 3 * (10 / HX_SAMPLES); - hx_scale = Settings.weight_calibration; + if (HX_CAL_FINISH == Hx.calibrate_step) { // Calibration finished + Hx.calibrate_step--; + Hx.calibrate_timer = 3 * (10 / HX_SAMPLES); + Hx.scale = Settings.weight_calibration; } - if (!hx_calibrate_timer) { - hx_calibrate_step = HX_CAL_END; // End of calibration + if (!Hx.calibrate_timer) { + Hx.calibrate_step = HX_CAL_END; // End of calibration } } else { - hx_weight += hx_last_weight; // grams + Hx.weight += Hx.last_weight; // grams + + if (Settings.SensorBits1.hx711_json_weight_change) { + if (abs(Hx.weight - Hx.weight_diff) > 4) { // Use 4 gram threshold to decrease "ghost" weights + Hx.weight_diff = Hx.weight; + Hx.weight_changed = true; + } + else if (Hx.weight_changed && (Hx.weight == Hx.weight_diff)) { + mqtt_data[0] = '\0'; + ResponseAppendTime(); + HxShow(true); + ResponseJsonEnd(); + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); + Hx.weight_changed = false; + } + } } - hx_sum_weight = 0; - hx_sample_count = 0; + Hx.sum_weight = 0; + Hx.sample_count = 0; } } void HxSaveBeforeRestart() { - Settings.energy_frequency_calibration = hx_weight; - hx_sample_count = HX_SAMPLES +1; // Stop updating hx_weight + Settings.energy_frequency_calibration = Hx.weight; + Hx.sample_count = HX_SAMPLES +1; // Stop updating Hx.weight } #ifdef USE_WEBSERVER @@ -364,14 +392,14 @@ void HxShow(bool json) uint16_t count = 0; float weight = 0; - if (hx_calibrate_step < HX_CAL_FAIL) { - if (hx_weight && Settings.weight_item) { - count = (hx_weight * 10) / Settings.weight_item; + if (Hx.calibrate_step < HX_CAL_FAIL) { + if (Hx.weight && Settings.weight_item) { + count = (Hx.weight * 10) / Settings.weight_item; if (count > 1) { snprintf_P(scount, sizeof(scount), PSTR(",\"" D_JSON_COUNT "\":%d"), count); } } - weight = (float)hx_weight / 1000; // kilograms + weight = (float)Hx.weight / 1000; // kilograms } char weight_chr[33]; dtostrfd(weight, Settings.flag2.weight_resolution, weight_chr); @@ -384,9 +412,9 @@ void HxShow(bool json) if (count > 1) { WSContentSend_PD(HTTP_HX711_COUNT, count); } - if (hx_calibrate_step) { + if (Hx.calibrate_step) { char cal_text[30]; - WSContentSend_PD(HTTP_HX711_CAL, GetTextIndexed(cal_text, sizeof(cal_text), hx_calibrate_msg, kHxCalibrationStates)); + WSContentSend_PD(HTTP_HX711_CAL, GetTextIndexed(cal_text, sizeof(cal_text), Hx.calibrate_msg, kHxCalibrationStates)); } #endif // USE_WEBSERVER } @@ -497,11 +525,8 @@ bool Xsns34(uint8_t function) { bool result = false; - if (hx_type) { + if (Hx.type) { switch (function) { - case FUNC_INIT: - HxInit(); - break; case FUNC_EVERY_100_MSECOND: HxEvery100mSecond(); break; @@ -532,6 +557,9 @@ bool Xsns34(uint8_t function) break; #endif // USE_HX711_GUI #endif // USE_WEBSERVER + case FUNC_INIT: + HxInit(); + break; } } return result; diff --git a/sonoff/xsns_38_az7798.ino b/sonoff/xsns_38_az7798.ino index 5339a392f..e27a44bad 100644 --- a/sonoff/xsns_38_az7798.ino +++ b/sonoff/xsns_38_az7798.ino @@ -118,7 +118,10 @@ #define CO2_HIGH 1200 // Above this CO2 value show red light #endif -#define AZ_READ_TIMEOUT 400 // Must be way less than 1000 but enough to read 9 bytes at 9600 bps +#define AZ_READ_TIMEOUT 400 // Must be way less than 1000 but enough to read 25 bytes at 9600 bps + +#define AZ_CLOCK_UPDATE_INTERVAL (24UL * 60 * 60) // periodically update clock display (24 hours) +#define AZ_EPOCH (946684800UL) // 2000-01-01 00:00:00 TasmotaSerial *AzSerial; @@ -129,11 +132,14 @@ double az_temperature = 0; double az_humidity = 0; uint8_t az_received = 0; uint8_t az_state = 0; +unsigned long az_clock_update = 10; // timer for periodically updating clock display /*********************************************************************************************/ void AzEverySecond(void) { + unsigned long start = millis(); + az_state++; if (5 == az_state) { // every 5 seconds az_state = 0; @@ -143,7 +149,6 @@ void AzEverySecond(void) az_received = 0; uint8_t az_response[32]; - unsigned long start = millis(); uint8_t counter = 0; uint8_t i, j; uint8_t response_substr[16]; @@ -234,6 +239,25 @@ void AzEverySecond(void) response_substr[j] = 0; // add null terminator az_humidity = ConvertHumidity(CharToFloat((char*)response_substr)); } + + // update the clock from network time + if ((az_clock_update == 0) && (LocalTime() > AZ_EPOCH)) { + char tmpString[16]; + sprintf(tmpString, "C %d\r", (int)(LocalTime() - AZ_EPOCH)); + AzSerial->write(tmpString); + // discard the response + do { + if (AzSerial->available() > 0) { + if(AzSerial->read() == 0x0d) { break; } + } else { + delay(5); + } + } while(((millis() - start) < AZ_READ_TIMEOUT)); + az_clock_update = AZ_CLOCK_UPDATE_INTERVAL; + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 clock updated")); + } else { + az_clock_update--; + } } /*********************************************************************************************/ diff --git a/sonoff/xsns_40_pn532.ino b/sonoff/xsns_40_pn532.ino index 210607fa8..333ae7524 100644 --- a/sonoff/xsns_40_pn532.ino +++ b/sonoff/xsns_40_pn532.ino @@ -417,10 +417,7 @@ void PN532_ScanForTag(void) char card_datas[34]; #endif // USE_PN532_DATA_FUNCTION - sprintf(uids,""); - for (uint32_t i = 0;i < uid_len;i++) { - sprintf(uids,"%s%02X",uids,uid[i]); - } + ToHex_P((unsigned char*)uid, uid_len, uids, sizeof(uids)); #ifdef USE_PN532_DATA_FUNCTION if (uid_len == 4) { // Lets try to read block 1 of the mifare classic card for more information @@ -497,12 +494,10 @@ void PN532_ScanForTag(void) pn532_function = 0; #endif // USE_PN532_DATA_FUNCTION - Response_P(PSTR("{\"" D_JSON_TIME "\":\"%s\""), GetDateAndTime(DT_LOCAL).c_str()); - #ifdef USE_PN532_DATA_FUNCTION - ResponseAppend_P(PSTR(",\"PN532\":{\"UID\":\"%s\", \"DATA\":\"%s\"}}"), uids, card_datas); + ResponseTime_P(PSTR(",\"PN532\":{\"UID\":\"%s\", \"DATA\":\"%s\"}}"), uids, card_datas); #else - ResponseAppend_P(PSTR(",\"PN532\":{\"UID\":\"%s\"}}"), uids); + ResponseTime_P(PSTR(",\"PN532\":{\"UID\":\"%s\"}}"), uids); #endif // USE_PN532_DATA_FUNCTION MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); @@ -544,7 +539,7 @@ bool PN532_Command(void) if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 1),"E")) { pn532_function = 1; // Block 1 of next card/tag will be reset to 0x00... AddLog_P(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Next scanned tag data block 1 will be erased")); - Response_P(PSTR("{\"" D_JSON_TIME "\":\"%s\",\"PN532\":{\"COMMAND\":\"E\"}}"), GetDateAndTime(DT_LOCAL).c_str()); + ResponseTime_P(PSTR(",\"PN532\":{\"COMMAND\":\"E\"}}")); return serviced; } if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 1),"S")) { @@ -560,7 +555,7 @@ bool PN532_Command(void) pn532_newdata[pn532_newdata_len] = 0x00; // Null terminate the string pn532_function = 2; AddLog_P2(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Next scanned tag data block 1 will be set to '%s'"), pn532_newdata); - Response_P(PSTR("{\"" D_JSON_TIME "\":\"%s\",\"PN532\":{\"COMMAND\":\"S\"}}"), GetDateAndTime(DT_LOCAL).c_str()); + ResponseTime_P(PSTR(",\"PN532\":{\"COMMAND\":\"S\"}}")); return serviced; } } diff --git a/sonoff/xsns_42_scd30.ino b/sonoff/xsns_42_scd30.ino index b53f28e62..2c8db00e4 100644 --- a/sonoff/xsns_42_scd30.ino +++ b/sonoff/xsns_42_scd30.ino @@ -393,7 +393,7 @@ bool Scd30CommandSensor() uint16_t value = 0; if (XdrvMailbox.data_len > 0) { - value = XdrvMailbox.payload16; + value = XdrvMailbox.payload; Scd30SetCommand(command_code, value); } else @@ -449,7 +449,11 @@ void Scd30Show(bool json) ResponseAppend_P(PSTR(",\"SCD30\":{\"" D_JSON_CO2 "\":%d,\"" D_JSON_ECO2 "\":%d,\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_HUMIDITY "\":%s}"), scd30_CO2, scd30_CO2EAvg, temperature, humidity); #ifdef USE_DOMOTICZ - if (0 == tele_period) DomoticzSensor(DZ_AIRQUALITY, scd30_CO2); + if (0 == tele_period) + { + DomoticzSensor(DZ_AIRQUALITY, scd30_CO2); + DomoticzTempHumSensor(temperature, humidity); + } #endif // USE_DOMOTICZ #ifdef USE_WEBSERVER } else { diff --git a/sonoff/xsns_44_sps30.ino b/sonoff/xsns_44_sps30.ino index 0d7b2e133..20dc8a8f0 100644 --- a/sonoff/xsns_44_sps30.ino +++ b/sonoff/xsns_44_sps30.ino @@ -253,8 +253,7 @@ void SPS30_Show(bool json) { void CmdClean(void) { sps30_cmd(SPS_CMD_CLEAN); - Response_P(PSTR("{\"" D_JSON_TIME "\":\"%s\""), GetDateAndTime(DT_LOCAL).c_str()); - ResponseAppend_P(PSTR(",\"SPS30\":{\"CFAN\":\"true\"}}")); + ResponseTime_P(PSTR(",\"SPS30\":{\"CFAN\":\"true\"}}")); MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); } diff --git a/sonoff/xsns_45_vl53l0x.ino b/sonoff/xsns_45_vl53l0x.ino index 1f9248709..ce30460b7 100644 --- a/sonoff/xsns_45_vl53l0x.ino +++ b/sonoff/xsns_45_vl53l0x.ino @@ -131,7 +131,7 @@ void Vl53l0Show(boolean json) * Interface \*********************************************************************************************/ -#define XSNS_45 +#define XSNS_45 45 bool Xsns45(byte function) { diff --git a/sonoff/xsns_47_max31865.ino b/sonoff/xsns_47_max31865.ino new file mode 100644 index 000000000..6383ecaa3 --- /dev/null +++ b/sonoff/xsns_47_max31865.ino @@ -0,0 +1,139 @@ +/* + xsns_39_MAX31865.ino - MAX31865 thermocouple sensor support for Sonoff-Tasmota + + Copyright (C) 2019 Alberto Lopez Siemens + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef USE_MAX31865 + +#ifndef USE_SPI +#error "MAX31865 requires USE_SPI enabled" +#endif + +#include "Adafruit_MAX31865.h" + +#define XSNS_47 47 + +#if MAX31865_PTD_WIRES == 4 + #define PTD_WIRES MAX31865_4WIRE +#elif MAX31865_PTD_WIRES == 3 + #define PTD_WIRES MAX31865_3WIRE +#else + #define PTD_WIRES MAX31865_2WIRE +#endif + +int8_t init_status = 0; + +Adafruit_MAX31865 max31865; + +struct MAX31865_Result_Struct { + uint8_t ErrorCode; + uint16_t Rtd; + float PtdResistance; + float PtdTemp; +} MAX31865_Result; + +void MAX31865_Init(void){ + if(init_status) + return; + + max31865.setPins( + pin[GPIO_SSPI_CS], + pin[GPIO_SSPI_MOSI], + pin[GPIO_SSPI_MISO], + pin[GPIO_SSPI_SCLK] + ); + + if(max31865.begin(PTD_WIRES)) + init_status = 1; + else + init_status = -1; +} + +/* +* MAX31865_GetResult(void) +* Acquires the raw data via SPI, checks for MAX31865 errors and fills result structure +*/ +void MAX31865_GetResult(void){ + uint16_t rtd; + + rtd = max31865.readRTD(); + MAX31865_Result.Rtd = rtd; + MAX31865_Result.PtdResistance = max31865.rtd_to_resistance(rtd, MAX31865_REF_RES); + MAX31865_Result.PtdTemp = max31865.rtd_to_temperature(rtd, MAX31865_PTD_RES, MAX31865_REF_RES) + MAX31865_PTD_BIAS; +} + +void MAX31865_Show(bool Json){ + char temperature[33]; + char resistance[33]; + + dtostrfd(MAX31865_Result.PtdResistance, Settings.flag2.temperature_resolution, resistance); + dtostrfd(MAX31865_Result.PtdTemp, Settings.flag2.temperature_resolution, temperature); + + if(Json){ + ResponseAppend_P(PSTR(",\"MAX31865\":{\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_RESISTANCE "\":%s,\"" D_JSON_ERROR "\":%d}"), \ + temperature, resistance, MAX31865_Result.ErrorCode); +#ifdef USE_DOMOTICZ + if (0 == tele_period) { + DomoticzSensor(DZ_TEMP, temperature); + } +#endif // USE_DOMOTICZ +#ifdef USE_KNX + if (0 == tele_period) { + KnxSensor(KNX_TEMPERATURE, MAX31865_Result.PtdTemp); + } +#endif // USE_KNX + } else { +#ifdef USE_WEBSERVER + WSContentSend_PD(HTTP_SNS_TEMP, "MAX31865", temperature, TempUnit()); +#endif // USE_WEBSERVER + } +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xsns47(uint8_t function) +{ + bool result = false; + if((pin[GPIO_SSPI_MISO] < 99) && (pin[GPIO_SSPI_MOSI] < 99) && + (pin[GPIO_SSPI_SCLK] < 99) && (pin[GPIO_SSPI_CS] < 99)) { + + switch (function) { + case FUNC_INIT: + MAX31865_Init(); + break; + + case FUNC_EVERY_SECOND: + MAX31865_GetResult(); + break; + + case FUNC_JSON_APPEND: + MAX31865_Show(true); + break; + +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + MAX31865_Show(false); + break; +#endif // USE_WEBSERVER + } + } + return result; +} + +#endif // USE_MAX31865 diff --git a/sonoff/xsns_48_chirp.ino b/sonoff/xsns_48_chirp.ino new file mode 100644 index 000000000..c2bf08f1d --- /dev/null +++ b/sonoff/xsns_48_chirp.ino @@ -0,0 +1,554 @@ +/* + xsns_48_chirp.ino - soil moisture sensor support for Sonoff-Tasmota + + Copyright (C) 2019 Theo Arends & Christian Baars + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + -------------------------------------------------------------------------------------------- + Version Date Action Description + -------------------------------------------------------------------------------------------- + + 1.0.0.1 20190917 changed - rework of the inner loop to enable delays in the middle of I2C-reads + changed - double send address change only for fw>0x25 + changed - use DEBUG_SENSOR_LOG, change ILLUMINANCE to DARKNESS + changed - do not publish missing temperature reads, show fw-version as hex + added - now really support the (slower) CHIRP!-Sensor + --- + 1.0.0.0 20190608 started - further development by Christian Baars - https://github.com/Staars/Sonoff-Tasmota + forked - from arendst/tasmota - https://github.com/arendst/Sonoff-Tasmota + base - code base from arendst and - https://github.com/Miceuz/i2c-moisture-sensor + +*/ + +#ifdef USE_I2C +#ifdef USE_CHIRP + +/*********************************************************************************************\ + * CHIRP - Chirp!-sensor and I2C-soil-moisture-sensor + * !! The I2C-soil-moisture-sensor is the preferred one !! + * + * I2C Address: 0x20 - standard address, is changeable +\*********************************************************************************************/ + +#define XSNS_48 48 +#define CHIRP_MAX_SENSOR_COUNT 3 // 127 is expectectd to be the max number + +#define CHIRP_ADDR_STANDARD 0x20 // standard address + +/*********************************************************************************************\ + * constants +\*********************************************************************************************/ + +#define D_CMND_CHIRP "CHIRP" + +const char S_JSON_CHIRP_COMMAND_NVALUE[] PROGMEM = "{\"" D_CMND_CHIRP "%s\":%d}"; +const char S_JSON_CHIRP_COMMAND[] PROGMEM = "{\"" D_CMND_CHIRP "%s\"}"; +const char kCHIRP_Commands[] PROGMEM = "Select|Set|Scan|Reset|Sleep|Wake"; + +const char kChirpTypes[] PROGMEM = "CHIRP"; + +/*********************************************************************************************\ + * enumerations +\*********************************************************************************************/ + +enum CHIRP_Commands { // commands useable in console or rules + CMND_CHIRP_SELECT, // select active sensor by I2C address, makes only sense for multiple sensors + CMND_CHIRP_SET, // set new I2C address for selected/active sensor, will reset + CMND_CHIRP_SCAN, // scan the I2C bus for one or more chirp sensors + CMND_CHIRP_RESET, // CHIRPReset, a fresh and default restart + CMND_CHIRP_SLEEP, // put sensor to sleep + CMND_CHIRP_WAKE }; // wake sensor by reading firmware version + + +/*********************************************************************************************\ + * command defines +\*********************************************************************************************/ + +#define CHIRP_GET_CAPACITANCE 0x00 // 16 bit, read +#define CHIRP_SET_ADDRESS 0x01 // 8 bit, write +#define CHIRP_GET_ADDRESS 0x02 // 8 bit, read +#define CHIRP_MEASURE_LIGHT 0x03 // no value, write, -> initiate measurement, then wait at least 3 seconds +#define CHIRP_GET_LIGHT 0x04 // 16 bit, read, -> higher value means darker environment, noisy data, not calibrated +#define CHIRP_GET_TEMPERATURE 0x05 // 16 bit, read +#define CHIRP_RESET 0x06 // no value, write +#define CHIRP_GET_VERSION 0x07 // 8 bit, read, -> 0x22 means 2.2 +#define CHIRP_SLEEP 0x08 // no value, write +#define CHIRP_GET_BUSY 0x09 // 8 bit, read, -> 1 = busy, 0 = otherwise + +/*********************************************************************************************\ + * helper function +\*********************************************************************************************/ + +void ChirpWriteI2CRegister(uint8_t addr, uint8_t reg) { + Wire.beginTransmission(addr); + Wire.write(reg); + Wire.endTransmission(); +} // now the original CHIRP needs 1100 ms delay + +uint16_t ChirpFinishReadI2CRegister16bit(uint8_t addr) { + Wire.requestFrom(addr,(uint8_t)2); + uint16_t t = Wire.read() << 8; + t = t | Wire.read(); + return t; +} + +/********************************************************************************************/ + +// globals + +uint8_t chirp_current = 0; // current selected/active sensor +uint8_t chirp_found_sensors = 0; // number of found sensors + +char chirp_name[7]; +uint8_t chirp_next_job = 0; //0=reset, 1=auto-wake, 2-13 = various measure steps; 14 = TELE done +uint32_t chirp_timeout_count = 0; //is handled every second, so value is equal to seconds (it is a slow sensor) + +#pragma pack(1) +struct ChirpSensor_t{ + uint16_t moisture = 0; // shall hold post-processed data, if implemented + uint16_t light = 0; // light level, maybe already postprocessed depending on the firmware + int16_t temperature = 0; // temperature in degrees CELSIUS * 10 , we will also store the I2C error code + uint8_t version = 0; // firmware-version + uint8_t address:7; // we need only 7bit so... + uint8_t explicitSleep:1; // there is a free bit to play with ;) +}; +#pragma pack() + +ChirpSensor_t chirp_sensor[CHIRP_MAX_SENSOR_COUNT]; // should be 8 bytes per sensor slot + +/********************************************************************************************/ + +void ChirpReset(uint8_t addr) { + ChirpWriteI2CRegister(addr, CHIRP_RESET); +} + +/********************************************************************************************/ + +void ChirpResetAll(void) { + for (uint32_t i = 0; i < chirp_found_sensors; i++) { + if (chirp_sensor[i].version) { + ChirpReset(chirp_sensor[i].address); + } + } +} +/********************************************************************************************/ + +void ChirpClockSet() { // set I2C for this slow sensor + Wire.setClockStretchLimit(4000); + Wire.setClock(50000); +} + +/********************************************************************************************/ + +void ChirpSleep(uint8_t addr) { + ChirpWriteI2CRegister(addr, CHIRP_SLEEP); +} + +/********************************************************************************************/ + +// void ChirpSleepAll(void) { +// for (uint32_t i = 0; i < chirp_found_sensors; i++) { +// if (chirp_sensor[i].version) { +// ChirpSleep(chirp_sensor[i].address); +// } +// } +// } + +// /********************************************************************************************/ + +// void ChirpAutoWakeAll(void) { +// for (uint32_t i = 0; i < chirp_found_sensors; i++) { +// if (chirp_sensor[i].version && !chirp_sensor[i].explicitSleep) { +// ChirpReadVersion(chirp_sensor[i].address); +// } +// } +// } + +/********************************************************************************************/ + +void ChirpSelect(uint8_t sensor) { + if(sensor < chirp_found_sensors) { //TODO: show some infos + chirp_current = sensor; + DEBUG_SENSOR_LOG(PSTR("CHIRP: Sensor %u now active."), chirp_current); + } + if (sensor == 255) { + DEBUG_SENSOR_LOG(PSTR("CHIRP: Sensor %u active at address 0x%x."), chirp_current, chirp_sensor[chirp_current].address); + } +} + +/******************************************************************************************************************/ + +uint8_t ChirpReadVersion(uint8_t addr) { + return (I2cRead8(addr, CHIRP_GET_VERSION)); // the Chirp!-sensor does not provide fw-version and we will get 255 +} + +/******************************************************************************************************************/ + +bool ChirpSet(uint8_t addr) { + if(addr < 128){ + if (I2cWrite8(chirp_sensor[chirp_current].address, CHIRP_SET_ADDRESS, addr)){ + if(chirp_sensor[chirp_current].version>0x25 && chirp_sensor[chirp_current].version != 255){ + delay(5); + I2cWrite8(chirp_sensor[chirp_current].address, CHIRP_SET_ADDRESS, addr); + // two calls are needed for sensor firmware version 2.6, but maybe dangerous before + } + DEBUG_SENSOR_LOG(PSTR("CHIRP: Wrote adress %u "), addr); + ChirpReset(chirp_sensor[chirp_current].address); + chirp_sensor[chirp_current].address = addr; + chirp_timeout_count = 10; + chirp_next_job = 0; + if(chirp_sensor[chirp_current].version == 255){ // this should be Chirp! and it seems to need a power cycle (or RESET to GND) + AddLog_P2(LOG_LEVEL_INFO, PSTR("CHIRP: wrote new address %u, please power off device"), addr); + chirp_sensor[chirp_current].version == 0; // make it "invisible" + } + return true; + } + } + AddLog_P2(LOG_LEVEL_INFO, PSTR("CHIRP: address %u incorrect and not used"), addr); + return false; +} + +/********************************************************************************************/ + +bool ChirpScan() { + ChirpClockSet(); + chirp_found_sensors = 0; + for (uint8_t address = 1; address <= 127; address++) { + chirp_sensor[chirp_found_sensors].version = 0; + chirp_sensor[chirp_found_sensors].version = ChirpReadVersion(address); + delay(2); + chirp_sensor[chirp_found_sensors].version = ChirpReadVersion(address); + if(chirp_sensor[chirp_found_sensors].version > 0) { + AddLog_P2(LOG_LEVEL_DEBUG, S_LOG_I2C_FOUND_AT, "CHIRP:", address); + if(chirp_found_sensors 0) { + return; + } + DEBUG_SENSOR_LOG(PSTR("CHIRP: scan will start ...")); + if (ChirpScan()) { + uint8_t chirp_model = 0; // TODO: ?? + GetTextIndexed(chirp_name, sizeof(chirp_name), chirp_model, kChirpTypes); + } +} +/********************************************************************************************/ + +void ChirpServiceAllSensors(uint8_t job){ + for (uint32_t i = 0; i < chirp_found_sensors; i++) { + if (chirp_sensor[i].version && !chirp_sensor[i].explicitSleep) { + DEBUG_SENSOR_LOG(PSTR("CHIRP: prepare for sensor at address 0x%x"), chirp_sensor[i].address); + switch(job){ + case 0: + ChirpWriteI2CRegister(chirp_sensor[i].address, CHIRP_GET_CAPACITANCE); + break; + case 1: + chirp_sensor[i].moisture = ChirpFinishReadI2CRegister16bit(chirp_sensor[i].address); + break; + case 2: + ChirpWriteI2CRegister(chirp_sensor[i].address, CHIRP_GET_TEMPERATURE); + break; + case 3: + chirp_sensor[i].temperature = ChirpFinishReadI2CRegister16bit(chirp_sensor[i].address); + break; + case 4: + ChirpWriteI2CRegister(chirp_sensor[i].address, CHIRP_MEASURE_LIGHT); + break; + case 5: + ChirpWriteI2CRegister(chirp_sensor[i].address, CHIRP_GET_LIGHT); + break; + case 6: + chirp_sensor[i].light = ChirpFinishReadI2CRegister16bit(chirp_sensor[i].address); + break; + default: + break; + } + } + } +} + +/********************************************************************************************/ + +void ChirpEvery100MSecond(void) +{ + // DEBUG_SENSOR_LOG(PSTR("CHIRP: every second")); + if(chirp_timeout_count == 0) { //countdown complete, now do something + switch(chirp_next_job) { + case 0: //this should only be called after driver initialization + DEBUG_SENSOR_LOG(PSTR("CHIRP: reset all")); + ChirpResetAll(); + chirp_timeout_count = 10; // wait a second + chirp_next_job++; + break; + case 1: // auto-sleep-wake seems to expose a fundamental I2C-problem of the sensor and is deactivated + // DEBUG_SENSOR_LOG(PSTR("CHIRP: auto-wake all")); + // ChirpAutoWakeAll(); // this is only a wake-up call at the start of next read cycle + chirp_next_job++; // go on, next job should start in a second + break; + case 2: + DEBUG_SENSOR_LOG(PSTR("CHIRP: prepare moisture read")); + ChirpServiceAllSensors(0); + chirp_timeout_count = 11; // wait 1.1 seconds, + chirp_next_job++; + break; + case 3: + DEBUG_SENSOR_LOG(PSTR("CHIRP: finish moisture read")); + ChirpServiceAllSensors(1); + chirp_next_job++; + break; + case 4: + DEBUG_SENSOR_LOG(PSTR("CHIRP: prepare moisture read - 2nd")); + ChirpServiceAllSensors(0); + chirp_timeout_count = 11; // wait 1.1 seconds, + chirp_next_job++; + break; + case 5: + DEBUG_SENSOR_LOG(PSTR("CHIRP: finish moisture read - 2nd")); + ChirpServiceAllSensors(1); + chirp_next_job++; + break; + case 6: + DEBUG_SENSOR_LOG(PSTR("CHIRP: prepare temperature read")); + ChirpServiceAllSensors(2); + chirp_timeout_count = 11; // wait 1.1 seconds, + chirp_next_job++; + break; + case 7: + DEBUG_SENSOR_LOG(PSTR("CHIRP: finish temperature read")); + ChirpServiceAllSensors(3); + chirp_next_job++; + break; + case 8: + DEBUG_SENSOR_LOG(PSTR("CHIRP: prepare temperature read - 2nd")); + ChirpServiceAllSensors(2); + chirp_timeout_count = 11; // wait 1.1 seconds, + chirp_next_job++; + break; + case 9: + DEBUG_SENSOR_LOG(PSTR("CHIRP: finish temperature read - 2nd")); + ChirpServiceAllSensors(3); + chirp_next_job++; + break; + case 10: + DEBUG_SENSOR_LOG(PSTR("CHIRP: start light measure process")); + ChirpServiceAllSensors(4); + chirp_timeout_count = 90; // wait 9 seconds, + chirp_next_job++; + break; + case 11: + DEBUG_SENSOR_LOG(PSTR("CHIRP: prepare light read")); + ChirpServiceAllSensors(5); + chirp_timeout_count = 11; // wait 1.1 seconds, + chirp_next_job++; + break; + case 12: + DEBUG_SENSOR_LOG(PSTR("CHIRP: finish light read")); + ChirpServiceAllSensors(6); + chirp_next_job++; + break; + case 13: + DEBUG_SENSOR_LOG(PSTR("CHIRP: paused, waiting for TELE")); + break; + case 14: + if (Settings.tele_period > 16){ + chirp_timeout_count = (Settings.tele_period - 17) * 10; // sync it with the TELEPERIOD, we need about up to 17 seconds to measure + DEBUG_SENSOR_LOG(PSTR("CHIRP: timeout 1/10 sec: %u, tele: %u"), chirp_timeout_count, Settings.tele_period); + } + else{ + AddLog_P2(LOG_LEVEL_INFO, PSTR("CHIRP: TELEPERIOD must be > 16 seconds !")); + // we could overwrite it to i.e. 20 seconds here + } + chirp_next_job = 1; // back to step 1 + break; + } + } + else { + chirp_timeout_count--; // count down + } +} + +/********************************************************************************************/ +// normaly in i18n.h + +#define D_JSON_MOISTURE "Moisture" +#define D_JSON_DARKNESS "Darkness" + +#ifdef USE_WEBSERVER + // {s} = , {m} = , {e} = + + const char HTTP_SNS_MOISTURE[] PROGMEM = "{s} " D_JSON_MOISTURE "{m}%s %{e}"; + const char HTTP_SNS_DARKNESS[] PROGMEM = "{s} " D_JSON_DARKNESS "{m}%s %{e}"; + const char HTTP_SNS_CHIRPVER[] PROGMEM = "{s} CHIRP-sensor %u at address{m}0x%x{e}" + "{s} FW-version{m}%s {e}"; ; + const char HTTP_SNS_CHIRPSLEEP[] PROGMEM = "{s} {m} is sleeping ...{e}"; +#endif // USE_WEBSERVER + + +/********************************************************************************************/ + +void ChirpShow(bool json) +{ + for (uint32_t i = 0; i < chirp_found_sensors; i++) { + if (chirp_sensor[i].version) { + // convert double values to string + char str_moisture[33]; + dtostrfd(chirp_sensor[i].moisture, 0, str_moisture); + char str_temperature[33]; + double t_temperature = ((double) chirp_sensor[i].temperature )/10.0; + dtostrfd(t_temperature, Settings.flag2.temperature_resolution, str_temperature); + char str_light[33]; + dtostrfd(chirp_sensor[i].light, 0, str_light); + char str_version[7]; + if(chirp_sensor[i].version == 0xff){ + strncpy_P(str_version, PSTR("Chirp!"), sizeof(str_version)); + } + else{ + sprintf(str_version, "%x", chirp_sensor[i].version); + } + if (json) { + if(!chirp_sensor[i].explicitSleep) { + ResponseAppend_P(PSTR(",\"%s%u\":{\"" D_JSON_MOISTURE "\":%s"),chirp_name, i, str_moisture); + if(chirp_sensor[i].temperature!=-1){ // this is the error code -> no temperature + ResponseAppend_P(PSTR(",\"" D_JSON_TEMPERATURE "\":%s"),str_temperature); + } + ResponseAppend_P(PSTR(",\"" D_JSON_DARKNESS "\":%s}"),str_light); + } + else { + ResponseAppend_P(PSTR(",\"%s%u\":{\"sleeping\"}"),chirp_name, i); + } + #ifdef USE_DOMOTICZ + if (0 == tele_period) { + DomoticzTempHumSensor(str_temperature, str_moisture); + DomoticzSensor(DZ_ILLUMINANCE,chirp_sensor[i].light); // this is not LUX!! + } + #endif // USE_DOMOTICZ + #ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_CHIRPVER, i, chirp_sensor[i].address, str_version); + if (chirp_sensor[i].explicitSleep){ + WSContentSend_PD(HTTP_SNS_CHIRPSLEEP); + } + else { + WSContentSend_PD(HTTP_SNS_MOISTURE, str_moisture); + WSContentSend_PD(HTTP_SNS_DARKNESS, str_light); + if(chirp_sensor[i].temperature!=-1){ // this is the error code -> no temperature + WSContentSend_PD(HTTP_SNS_TEMP, " ",str_temperature, TempUnit()); + } + } + + #endif // USE_WEBSERVER + } + } + } +} + +/*********************************************************************************************\ + * check the Chirp commands +\*********************************************************************************************/ + +bool ChirpCmd(void) { + char command[CMDSZ]; + bool serviced = true; + uint8_t disp_len = strlen(D_CMND_CHIRP); + + if (!strncasecmp_P(XdrvMailbox.topic, PSTR(D_CMND_CHIRP), disp_len)) { // prefix + int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic + disp_len, kCHIRP_Commands); + + switch (command_code) { + case CMND_CHIRP_SELECT: + case CMND_CHIRP_SET: + if (XdrvMailbox.data_len > 0) { + if (command_code == CMND_CHIRP_SELECT) { ChirpSelect(XdrvMailbox.payload); } //select active sensor, i.e. for wake, sleep or reset + if (command_code == CMND_CHIRP_SET) { ChirpSet((uint8_t)XdrvMailbox.payload); } //set and change I2C-address of selected sensor + Response_P(S_JSON_CHIRP_COMMAND_NVALUE, command, XdrvMailbox.payload); + } + else { + if (command_code == CMND_CHIRP_SELECT) { ChirpSelect(255); } //show active sensor + Response_P(S_JSON_CHIRP_COMMAND, command, XdrvMailbox.payload); + } + break; + case CMND_CHIRP_SCAN: + case CMND_CHIRP_SLEEP: + case CMND_CHIRP_WAKE: + case CMND_CHIRP_RESET: + if (command_code == CMND_CHIRP_SCAN) { chirp_next_job = 0; + ChirpDetect(); } // this will re-init the sensor array + if (command_code == CMND_CHIRP_SLEEP) { chirp_sensor[chirp_current].explicitSleep = true; // we do not touch this sensor in the read functions + ChirpSleep(chirp_sensor[chirp_current].address); } + if (command_code == CMND_CHIRP_WAKE) { chirp_sensor[chirp_current].explicitSleep = false; // back in action + ChirpReadVersion(chirp_sensor[chirp_current].address); } // just use read version as wakeup call + if (command_code == CMND_CHIRP_RESET) { ChirpReset(chirp_sensor[chirp_current].address); } + Response_P(S_JSON_CHIRP_COMMAND, command, XdrvMailbox.payload); + break; + default: + // else for Unknown command + serviced = false; + break; + } + } + return serviced; +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xsns48(uint8_t function) +{ + bool result = false; + + if (i2c_flg) { + switch (function) { + case FUNC_INIT: + ChirpDetect(); // We can call CHIRPSCAN later to re-detect + break; + case FUNC_EVERY_100_MSECOND: + if(chirp_found_sensors > 0){ + ChirpEvery100MSecond(); + } + break; + case FUNC_COMMAND: + result = ChirpCmd(); + break; + case FUNC_JSON_APPEND: + ChirpShow(1); + chirp_next_job = 14; // TELE done, now compute time for next measure cycle + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + ChirpShow(0); + break; +#endif // USE_WEBSERVER + } + } + return result; +} + +#endif // USE_CHIRP +#endif // USE_I2C diff --git a/sonoff/xsns_50_paj7620.ino b/sonoff/xsns_50_paj7620.ino new file mode 100644 index 000000000..165d6b649 --- /dev/null +++ b/sonoff/xsns_50_paj7620.ino @@ -0,0 +1,570 @@ +/* + xsns_50_paj7620.ino - gesture sensor support for Sonoff-Tasmota + + Copyright (C) 2019 Theo Arends & Christian Baars + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + -------------------------------------------------------------------------------------------- + Version Date Action Description + -------------------------------------------------------------------------------------------- + + + --- + 1.0.0.0 20190808 started - further development by Christian Baars - https://github.com/Staars/Sonoff-Tasmota + forked - from arendst/tasmota - https://github.com/arendst/Sonoff-Tasmota + base - code base from arendst and - https://github.com/Seeed-Studio/Gesture_PAJ7620 + +*/ + +#ifdef USE_I2C +#ifdef USE_PAJ7620 + +/*********************************************************************************************\ + * PAJ7620 - Gesture sensor + * + * I2C Address: 0x73 - standard address +\*********************************************************************************************/ + +#define XSNS_50 50 + +#define PAJ7620_ADDR 0x73 // standard address + +#define PAJ7620_BANK_SEL 0xEF // 8 bit, write -> 0 or 1 + +// the registers are organized in 2 banks +// bank: 0 +#define PAJ7620_GET_GESTURE 0x43 // 8 bit, read +#define PAJ7620_PROXIMITY_AVG_Y 0x6c // 8 bit, read -> 255: near , lower numbers: far + +#define PAJ7620_OBJECT_CENTER_X 0xad // 5 bit, read +#define PAJ7620_OBJECT_CENTER_Y 0xaf // 5 bit, read + +#define PAJ7620_DOWN 1 // readings from PAJ7620_GET_GESTURE +#define PAJ7620_UP 2 +#define PAJ7620_RIGHT 4 +#define PAJ7620_LEFT 8 +#define PAJ7620_NEAR 16 +#define PAJ7620_FAR 32 +#define PAJ7620_CW 64 +#define PAJ7620_CCW 128 + +// bank: 1 +// nothing at the moment + +const uint8_t PAJ7620initRegisterArray[][2] PROGMEM = { // set all needed registers + {0xEF,0x00}, // bank 0 + {0x32,0x29}, {0x33,0x01}, {0x34,0x00}, {0x35,0x01}, {0x36,0x00}, {0x37,0x07}, {0x38,0x17}, {0x39,0x06}, + {0x3A,0x12}, {0x3F,0x00}, {0x40,0x02}, {0x41,0xFF}, {0x42,0x01}, {0x46,0x2D}, {0x47,0x0F}, {0x48,0x3C}, + {0x49,0x00}, {0x4A,0x1E}, {0x4B,0x00}, {0x4C,0x20}, {0x4D,0x00}, {0x4E,0x1A}, {0x4F,0x14}, {0x50,0x00}, + {0x51,0x10}, {0x52,0x00}, {0x5C,0x02}, {0x5D,0x00}, {0x5E,0x10}, {0x5F,0x3F}, {0x60,0x27}, {0x61,0x28}, + {0x62,0x00}, {0x63,0x03}, {0x64,0xF7}, {0x65,0x03}, {0x66,0xD9}, {0x67,0x03}, {0x68,0x01}, {0x69,0xC8}, + {0x6A,0x40}, {0x6D,0x04}, {0x6E,0x00}, {0x6F,0x00}, {0x70,0x80}, {0x71,0x00}, {0x72,0x00}, {0x73,0x00}, + {0x74,0xF0}, {0x75,0x00}, {0x80,0x42}, {0x81,0x44}, {0x82,0x04}, {0x83,0x20}, {0x84,0x20}, {0x85,0x00}, + {0x86,0x10}, {0x87,0x00}, {0x88,0x05}, {0x89,0x18}, {0x8A,0x10}, {0x8B,0x01}, {0x8C,0x37}, {0x8D,0x00}, + {0x8E,0xF0}, {0x8F,0x81}, {0x90,0x06}, {0x91,0x06}, {0x92,0x1E}, {0x93,0x0D}, {0x94,0x0A}, {0x95,0x0A}, + {0x96,0x0C}, {0x97,0x05}, {0x98,0x0A}, {0x99,0x41}, {0x9A,0x14}, {0x9B,0x0A}, {0x9C,0x3F}, {0x9D,0x33}, + {0x9E,0xAE}, {0x9F,0xF9}, {0xA0,0x48}, {0xA1,0x13}, {0xA2,0x10}, {0xA3,0x08}, {0xA4,0x30}, {0xA5,0x19}, + {0xA6,0x10}, {0xA7,0x08}, {0xA8,0x24}, {0xA9,0x04}, {0xAA,0x1E}, {0xAB,0x1E}, {0xCC,0x19}, {0xCD,0x0B}, + {0xCE,0x13}, {0xCF,0x64}, {0xD0,0x21}, {0xD1,0x0F}, {0xD2,0x88}, {0xE0,0x01}, {0xE1,0x04}, {0xE2,0x41}, + {0xE3,0xD6}, {0xE4,0x00}, {0xE5,0x0C}, {0xE6,0x0A}, {0xE7,0x00}, {0xE8,0x00}, {0xE9,0x00}, {0xEE,0x07}, + {0xEF,0x01}, // bank 1 + {0x00,0x1E}, {0x01,0x1E}, {0x02,0x0F}, {0x03,0x10}, {0x04,0x02}, {0x05,0x00}, {0x06,0xB0}, {0x07,0x04}, + {0x08,0x0D}, {0x09,0x0E}, {0x0A,0x9C}, {0x0B,0x04}, {0x0C,0x05}, {0x0D,0x0F}, {0x0E,0x02}, {0x0F,0x12}, + {0x10,0x02}, {0x11,0x02}, {0x12,0x00}, {0x13,0x01}, {0x14,0x05}, {0x15,0x07}, {0x16,0x05}, {0x17,0x07}, + {0x18,0x01}, {0x19,0x04}, {0x1A,0x05}, {0x1B,0x0C}, {0x1C,0x2A}, {0x1D,0x01}, {0x1E,0x00}, {0x21,0x00}, + {0x22,0x00}, {0x23,0x00}, {0x25,0x01}, {0x26,0x00}, {0x27,0x39}, {0x28,0x7F}, {0x29,0x08}, {0x30,0x03}, + {0x31,0x00}, {0x32,0x1A}, {0x33,0x1A}, {0x34,0x07}, {0x35,0x07}, {0x36,0x01}, {0x37,0xFF}, {0x38,0x36}, + {0x39,0x07}, {0x3A,0x00}, {0x3E,0xFF}, {0x3F,0x00}, {0x40,0x77}, {0x41,0x40}, {0x42,0x00}, {0x43,0x30}, + {0x44,0xA0}, {0x45,0x5C}, {0x46,0x00}, {0x47,0x00}, {0x48,0x58}, {0x4A,0x1E}, {0x4B,0x1E}, {0x4C,0x00}, + {0x4D,0x00}, {0x4E,0xA0}, {0x4F,0x80}, {0x50,0x00}, {0x51,0x00}, {0x52,0x00}, {0x53,0x00}, {0x54,0x00}, + {0x57,0x80}, {0x59,0x10}, {0x5A,0x08}, {0x5B,0x94}, {0x5C,0xE8}, {0x5D,0x08}, {0x5E,0x3D}, {0x5F,0x99}, + {0x60,0x45}, {0x61,0x40}, {0x63,0x2D}, {0x64,0x02}, {0x65,0x96}, {0x66,0x00}, {0x67,0x97}, {0x68,0x01}, + {0x69,0xCD}, {0x6A,0x01}, {0x6B,0xB0}, {0x6C,0x04}, {0x6D,0x2C}, {0x6E,0x01}, {0x6F,0x32}, {0x71,0x00}, + {0x72,0x01}, {0x73,0x35}, {0x74,0x00}, {0x75,0x33}, {0x76,0x31}, {0x77,0x01}, {0x7C,0x84}, {0x7D,0x03}, + {0x7E,0x01}, + {0xEF,0x00} // back to bank 0 +}; + +/*********************************************************************************************\ + * constants +\*********************************************************************************************/ + +#define D_CMND_PAJ7620 "PAJ7620" + +const char S_JSON_PAJ7620_COMMAND_NVALUE[] PROGMEM = "{\"" D_CMND_PAJ7620 "%s\":%d}"; + +const char kPAJ7620Types[] PROGMEM = "PAJ7620"; + +const uint8_t PAJ7620_PIN[]= {1,2,3,4}; // TOP-SECRET!! ;) + + +/*********************************************************************************************\ + * helper function +\*********************************************************************************************/ + +void PAJ7620SelectBank(uint8_t bank) +{ + switch(bank){ + case 0: + I2cWrite(PAJ7620_ADDR, PAJ7620_BANK_SEL, 0, 1); + break; + case 1: + I2cWrite(PAJ7620_ADDR, PAJ7620_BANK_SEL, 1, 1); + break; + default: + break; + } +} + +/********************************************************************************************/ + +void PAJ7620TriggerTele(){ + mqtt_data[0] = '\0'; + if (MqttShowSensor()) { + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); +#ifdef USE_RULES + RulesTeleperiod(); // Allow rule based HA messages +#endif // USE_RULES + } +} + +/********************************************************************************************\ +| *globals +\*********************************************************************************************/ + +char PAJ7620_name[9]; + +uint32_t PAJ7620_timeout_counter = 10; // the time interval is 100 ms -> i.e. 10 is 1 second (= start up interval) +uint32_t PAJ7620_next_job = 0; // 0 = detect, 1 = init, 2 = wait for gesture, 255 = sensor not found and do nothing +uint32_t PAJ7620_mode = 1; // 0 = mute, 1 = gestures only, 2 = gestures, 3 = corner, 4 = PIN, 5 = xy + +struct { + uint8_t current; + uint8_t last; + uint8_t same; // number of identical gestures in a row - 255 should be enough + uint8_t unfinished; // used for up,down,left,right to avoid false interpretation for near and far +} PAJ7620_gesture; + +bool PAJ7620_finished_gesture = false; +char PAJ7620_currentGestureName[6]; + +struct{ + uint8_t x; + uint8_t y; + uint8_t last_x; + uint8_t last_y; + uint8_t proximity; + uint8_t last_proximity; + uint8_t corner; + struct { //TODO: multiple PIN's + uint8_t step:3; // max. 4 steps ATM, but leave a little more space + uint8_t countdown:3; // max. 0.7 seconds for each corner + uint8_t valid:1; + } PIN; +} PAJ7620_state; + +/********************************************************************************************/ + +/********************************************************************************************/ +void PAJ7620DecodeGesture(void) +{ + switch (PAJ7620_gesture.current) { // we will accept only "clean" recognized gestures, the sensor can report multiple gestures at once via bitfield, but these are discarded + case PAJ7620_DOWN: + DEBUG_SENSOR_LOG(PSTR("DOWN")); + snprintf_P(PAJ7620_currentGestureName, sizeof(PAJ7620_currentGestureName), PSTR("Down")); + if(PAJ7620_gesture.unfinished){ // for better recognition of NEAR and FAR + PAJ7620_finished_gesture = true; // consider the gesture finished only in the second try, this adds some delay for up,down,left,right + break; + } + PAJ7620_gesture.unfinished = PAJ7620_gesture.current; // save the gesture, maybe it will be the final one + PAJ7620_timeout_counter = 5; // 0.5 (plus 0.3) seconds time interval to go into the sensing area and change movement to NEAR or FAR + break; + case PAJ7620_UP: + DEBUG_SENSOR_LOG(PSTR("UP")); + snprintf_P(PAJ7620_currentGestureName, sizeof(PAJ7620_currentGestureName), PSTR("Up")); + if(PAJ7620_gesture.unfinished){ + PAJ7620_finished_gesture = true; + break; + } + PAJ7620_gesture.unfinished = PAJ7620_gesture.current; + PAJ7620_timeout_counter = 5; + break; + case PAJ7620_RIGHT: + DEBUG_SENSOR_LOG(PSTR("RIGHT")); + snprintf_P(PAJ7620_currentGestureName, sizeof(PAJ7620_currentGestureName), PSTR("Right")); + if(PAJ7620_gesture.unfinished){ + PAJ7620_finished_gesture = true; + break; + } + PAJ7620_gesture.unfinished = PAJ7620_gesture.current; + PAJ7620_timeout_counter = 5; + break; + case PAJ7620_LEFT: + DEBUG_SENSOR_LOG(PSTR("LEFT")); + snprintf_P(PAJ7620_currentGestureName, sizeof(PAJ7620_currentGestureName), PSTR("Left")); + if(PAJ7620_gesture.unfinished){ + PAJ7620_finished_gesture = true; + break; + } + PAJ7620_gesture.unfinished = PAJ7620_gesture.current; + PAJ7620_timeout_counter = 5; + break; + case PAJ7620_NEAR: + DEBUG_SENSOR_LOG(PSTR("NEAR")); + snprintf_P(PAJ7620_currentGestureName, sizeof(PAJ7620_currentGestureName), PSTR("Near")); + PAJ7620_finished_gesture = true; + PAJ7620_timeout_counter = 25; // more time to "escape" from gesture (will be 2.8 second) + break; + case PAJ7620_FAR: + DEBUG_SENSOR_LOG(PSTR("FAR")); + snprintf_P(PAJ7620_currentGestureName, sizeof(PAJ7620_currentGestureName), PSTR("Far")); + PAJ7620_finished_gesture = true; + PAJ7620_timeout_counter = 25; + break; + case PAJ7620_CW: + DEBUG_SENSOR_LOG(PSTR("ClockWise")); + snprintf_P(PAJ7620_currentGestureName, sizeof(PAJ7620_currentGestureName), PSTR("CW")); + PAJ7620_finished_gesture = true; + break; + case PAJ7620_CCW: + DEBUG_SENSOR_LOG(PSTR("CounterClockWise")); + snprintf_P(PAJ7620_currentGestureName, sizeof(PAJ7620_currentGestureName), PSTR("CCW")); + PAJ7620_finished_gesture = true; + break; + default: + if(PAJ7620_gesture.unfinished){ + PAJ7620_finished_gesture = true; // this will finish up, down, left, right + break; + } + break; + } +if(PAJ7620_finished_gesture){ + if (PAJ7620_gesture.unfinished){ + if(PAJ7620_gesture.current!=PAJ7620_NEAR && PAJ7620_gesture.current!=PAJ7620_FAR){ + PAJ7620_gesture.current = PAJ7620_gesture.unfinished; // to count correctly for up, down, right, left + } + } + if (PAJ7620_gesture.current == PAJ7620_gesture.last){ + PAJ7620_gesture.same++; + } + else{ + PAJ7620_gesture.same = 1; + } + PAJ7620_gesture.last = PAJ7620_gesture.current; + PAJ7620_finished_gesture = false; + PAJ7620_gesture.unfinished = 0; + PAJ7620_timeout_counter += 3; // add delay 0.3 seconds for every kind of gesture + PAJ7620TriggerTele(); + } +} + +/********************************************************************************************/ +void PAJ7620ReadGesture(void){ + switch(PAJ7620_mode){ + case 1: + PAJ7620_gesture.current = I2cRead8(PAJ7620_ADDR,PAJ7620_GET_GESTURE); + if(PAJ7620_gesture.current > 0 || PAJ7620_gesture.unfinished){ + DEBUG_SENSOR_LOG(PSTR("PAJ7620: gesture: %u"),PAJ7620_gesture.current ); + PAJ7620DecodeGesture(); + } + + break; + case 2: + PAJ7620_state.proximity = I2cRead8(PAJ7620_ADDR, PAJ7620_PROXIMITY_AVG_Y); + if((PAJ7620_state.proximity>0)||(PAJ7620_state.last_proximity>0)) + { + if(PAJ7620_state.proximity!=PAJ7620_state.last_proximity){ + PAJ7620_state.last_proximity = PAJ7620_state.proximity; + DEBUG_SENSOR_LOG(PSTR("PAJ7620: Proximity: %u"),PAJ7620_state.proximity ); + PAJ7620TriggerTele(); + } + } + break; + case 3: case 4: case 5: + PAJ7620_state.x = I2cRead8(PAJ7620_ADDR, PAJ7620_OBJECT_CENTER_X); + PAJ7620_state.y = I2cRead8(PAJ7620_ADDR, PAJ7620_OBJECT_CENTER_Y); + if(PAJ7620_state.y>0 && PAJ7620_state.x>0){ + if(PAJ7620_state.y!=PAJ7620_state.last_y || PAJ7620_state.x!=PAJ7620_state.last_x){ + PAJ7620_state.last_y = PAJ7620_state.y; + PAJ7620_state.last_x = PAJ7620_state.x; + DEBUG_SENSOR_LOG(PSTR("PAJ7620: x: %u y: %u"), PAJ7620_state.x, PAJ7620_state.y); + + PAJ7620_state.corner = 0; + // 1|2 + // --- + // 3|4 + switch(PAJ7620_state.y){ + case 0: case 1: case 2: case 3: case 4: case 5:// case 0..5: would be nicer + PAJ7620_state.corner = 3; + break; + case 9: case 10: case 11: case 12: case 13: case 14: + PAJ7620_state.corner = 1; + break; + default: + break; + } + if(PAJ7620_state.corner!=0){ + switch(PAJ7620_state.x){ + case 0: case 1: case 2: case 3: case 4: case 5: + break; + case 9: case 10: case 11: case 12: case 13: case 14: + PAJ7620_state.corner++; + break; + default: + PAJ7620_state.corner = 0; + break; + } + } + DEBUG_SENSOR_LOG(PSTR("PAJ7620: corner: %u"), PAJ7620_state.corner); + // PIN-part: + if(PAJ7620_state.PIN.countdown == 0){ + PAJ7620_state.PIN.step=0; + PAJ7620_state.PIN.valid=0; + } + if(!PAJ7620_state.PIN.step){ + if(PAJ7620_state.corner == PAJ7620_PIN[PAJ7620_state.PIN.step]){ + PAJ7620_state.PIN.step=1; + PAJ7620_state.PIN.countdown=7; + } + } + else{ + if(PAJ7620_state.corner == PAJ7620_PIN[PAJ7620_state.PIN.step]){ + PAJ7620_state.PIN.step+=1; + PAJ7620_state.PIN.countdown=7; + } + else{ + PAJ7620_state.PIN.countdown-=1; + } + } + if(PAJ7620_state.PIN.step == 4){ + PAJ7620_state.PIN.valid = 1; + DEBUG_SENSOR_LOG(PSTR("PAJ7620: PIN valid!!")); + PAJ7620_state.PIN.countdown = 0; // will restart in the next loop + } + PAJ7620TriggerTele(); + } + } + break; + default: + break; + } +} + +/********************************************************************************************/ + +void PAJ7620Detect(void) +{ + DEBUG_SENSOR_LOG(PSTR("PAJ7620: scan will start ...")); + PAJ7620SelectBank(0); + PAJ7620SelectBank(0); // do it twice + uint16_t PAJ7620_id = I2cRead16LE(PAJ7620_ADDR,0); // read ID from reg 1 and 0 + uint8_t PAJ7620_ver = I2cRead8(PAJ7620_ADDR,2); + if (PAJ7620_id == 0x7620) { // this device ID makes sense ;) + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PAJ7620: sensor found with ID: 0x%x and VER: %u"), PAJ7620_id, PAJ7620_ver); + uint8_t PAJ7620_model = 0; + GetTextIndexed(PAJ7620_name, sizeof(PAJ7620_name), PAJ7620_model, kPAJ7620Types); + PAJ7620_next_job = 1; // now init + } + else { + DEBUG_SENSOR_LOG(PSTR("PAJ7620: sensor not found, false ID 0x%x"), PAJ7620_id); + PAJ7620_next_job = 255; // do not loop + } +} + +/********************************************************************************************/ +void PAJ7620Init(void) +{ + DEBUG_SENSOR_LOG(PSTR("PAJ7620: init sensor start %u"),millis()); + union{ + uint32_t raw; + uint8_t reg_val[4]; + } buf; + for(uint32_t i = 0; i < (sizeof(PAJ7620initRegisterArray)/2); i+=2) + { + buf.raw = pgm_read_dword(PAJ7620initRegisterArray+i); + DEBUG_SENSOR_LOG("%x %x %x %x",buf.reg_val[0],buf.reg_val[1],buf.reg_val[2],buf.reg_val[3]); + I2cWrite(PAJ7620_ADDR, buf.reg_val[0], buf.reg_val[1], 1); + I2cWrite(PAJ7620_ADDR, buf.reg_val[2], buf.reg_val[3], 1); + } + DEBUG_SENSOR_LOG(PSTR("PAJ7620: init sensor done %u"),millis()); + PAJ7620_next_job = 2; // now loop and wait for gestures +} + +/********************************************************************************************/ + +void PAJ7620SelectMode(uint16_t mode){ + DEBUG_SENSOR_LOG(PSTR("PAJ7620: set mode to %u"),mode); + switch(mode){ + case 0: + PAJ7620_mode = 0; + break; + case 1: + PAJ7620_mode = 1; + break; + case 2: + PAJ7620_mode = 2; + break; + case 3: + PAJ7620_mode = 3; + break; + case 4: + PAJ7620_mode = 4; + break; + case 5: + PAJ7620_mode = 5; + break; + default: + break; + } +} +/********************************************************************************************/ + +void PAJ7620Loop(void) +{ + if(PAJ7620_timeout_counter == 0){ + switch(PAJ7620_next_job){ + case 0: + PAJ7620Detect(); + break; + case 1: + PAJ7620Init(); + break; + case 2: + if(PAJ7620_mode != 0){ + PAJ7620ReadGesture(); + } + break; + default: + break; + } + } + else { + PAJ7620_timeout_counter--; + } +} + +/********************************************************************************************/ +// normaly in i18n.h + +#define D_JSON_PAJ7620 "PAJ7620" + +#ifdef USE_WEBSERVER + // {s} = , {m} = , {e} = + + const char HTTP_SNS_PAJ7620[] PROGMEM = "{s} " D_JSON_PAJ7620 ": {m}%s {e}"; + const char HTTP_SNS_PAJ7620VER[] PROGMEM = "{s} PAJ7620 at address: {m}0x73{e}" + "{s} version: {m}1 {e}"; // only hard-coded ATM ; + +#endif // USE_WEBSERVER + + +/********************************************************************************************/ + +void PAJ7620Show(bool json) +{ + if (json) { + if((PAJ7620_currentGestureName[0] != '\0' )){ + ResponseAppend_P(PSTR(",\"%s\":{\"%s\":%u}"), PAJ7620_name, PAJ7620_currentGestureName, PAJ7620_gesture.same); + PAJ7620_currentGestureName[0] = '\0'; + return; + } + switch(PAJ7620_mode){ + case 2: + if(PAJ7620_mode>1){ + ResponseAppend_P(PSTR(",\"%s\":{\"Proximity\":%u}"), PAJ7620_name, PAJ7620_state.proximity); + } + break; + case 3: + if(PAJ7620_mode>1 && PAJ7620_state.corner>0){ + ResponseAppend_P(PSTR(",\"%s\":{\"Corner\":%u}"), PAJ7620_name, PAJ7620_state.corner); + } + break; + case 4: + if(PAJ7620_mode>1 && PAJ7620_state.PIN.valid){ + ResponseAppend_P(PSTR(",\"%s\":{\"PIN\":%u}"), PAJ7620_name, 1); //TODO: more than one PIN + PAJ7620_state.PIN.valid = 0; + } + break; + case 5: + if(PAJ7620_mode>1){ + ResponseAppend_P(PSTR(",\"%s\":{\"x\":%u,\"y\":%u}"), PAJ7620_name, PAJ7620_state.x, PAJ7620_state.y); + } + break; + default: + break; + } + #ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_PAJ7620VER); + #endif // USE_WEBSERVER + } +} + +/*********************************************************************************************\ + * check the PAJ7620 commands +\*********************************************************************************************/ + +bool PAJ7620Cmd(void) { + bool serviced = true; + if (XdrvMailbox.data_len > 0) { + DEBUG_SENSOR_LOG(PSTR("PAJ7620: got argument for mode")); + PAJ7620SelectMode(XdrvMailbox.payload); //select mode + Response_P(S_JSON_PAJ7620_COMMAND_NVALUE, XdrvMailbox.command, XdrvMailbox.payload); + } + else { + DEBUG_SENSOR_LOG(PSTR("PAJ7620: show mode")); + Response_P(S_JSON_PAJ7620_COMMAND_NVALUE, XdrvMailbox.command, PAJ7620_mode); + } + return serviced; +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xsns50(uint8_t function) +{ + bool result = false; + + if (i2c_flg) { + switch (function) { + case FUNC_INIT: + DEBUG_SENSOR_LOG(PSTR("PAJ7620: 1 second until init")); + break; + case FUNC_COMMAND_SENSOR: + if (XSNS_50 == XdrvMailbox.index){ + result = PAJ7620Cmd(); + } + break; + case FUNC_EVERY_100_MSECOND: + if(PAJ7620_next_job <255) { + PAJ7620Loop(); + } + break; + case FUNC_JSON_APPEND: + PAJ7620Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + PAJ7620Show(0); + break; +#endif // USE_WEBSERVER + } + } + return result; +} + +#endif // USE_PAJ7620 +#endif // USE_I2C \ No newline at end of file diff --git a/sonoff/xsns_51_rdm6300.ino b/sonoff/xsns_51_rdm6300.ino new file mode 100644 index 000000000..2a42b2a08 --- /dev/null +++ b/sonoff/xsns_51_rdm6300.ino @@ -0,0 +1,177 @@ +/* + xsns_51_rdm6300.ino - Support for RDM6300 NFC Tag Reader + + Copyright (C) 2019 Gerhard Mutz and Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + + +#ifdef USE_RDM6300 + +#define XSNS_51 51 + +#define RDM6300_BAUDRATE 9600 + +#include + +#define RDM_TIMEOUT 100 +char rdm_uid_str[10]; + +// 2 seconds block time +#define RDM6300_BLOCK 2*10 + +uint8_t rdm_blcnt; +TasmotaSerial *RDM6300_Serial = nullptr; + +void RDM6300_Init() { + if (pin[GPIO_RDM6300_RX] < 99) { + RDM6300_Serial = new TasmotaSerial(pin[GPIO_RDM6300_RX],-1,1); + if (RDM6300_Serial->begin(RDM6300_BAUDRATE)) { + if (RDM6300_Serial->hardwareSerial()) { + ClaimSerial(); + } + } + } + rdm_blcnt=0; +} + +// 14 bytes payload; // RFID DATA FRAME FORMAT: 1byte head (value: 2), 10byte data (2byte version + 8byte tag), 2byte checksum, 1byte tail (value: 3) +void RDM6300_ScanForTag() { + char rdm_buffer[14]; + uint8_t rdm_index; + uint8_t rdm_array[6]; + + if (!RDM6300_Serial) return; + + if (rdm_blcnt>0) { + rdm_blcnt--; + while (RDM6300_Serial->available()) RDM6300_Serial->read(); + return; + } + + if (RDM6300_Serial->available()) { + + char c=RDM6300_Serial->read(); + if (c!=2) return; + // head detected + // read rest of message 11 more bytes + rdm_index=0; + uint32_t cmillis=millis(); + while (1) { + if (RDM6300_Serial->available()) { + char c=RDM6300_Serial->read(); + if (c==3) { + // tail marker + break; + } + rdm_buffer[rdm_index++]=c; + if (rdm_index>13) { + // illegal message + return; + } + } + if ((millis()-cmillis)>RDM_TIMEOUT) { + // timeout + return; + } + } + + // block for 2 seconds + rdm_blcnt=RDM6300_BLOCK; + + // calc checksum, + rm6300_hstring_to_array(rdm_array,sizeof(rdm_array),rdm_buffer); + uint8_t accu=0; + for (uint8_t count=0;count<5;count++) { + accu^=rdm_array[count]; + } + if (accu!=rdm_array[5]) { + // checksum error + return; + } + + // copy 4 hex bytes + memcpy(rdm_uid_str,&rdm_buffer[2],8); + rdm_uid_str[9]=0; + + ResponseTime_P(PSTR(",\"RDM6300\":{\"UID\":\"%s\"}}"), rdm_uid_str); + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); +/* + char command[24]; + sprintf(command,"event RDM6300=%s",rdm_uid_str); + ExecuteCommand(command, SRC_RULE); + */ + } + + +} + +uint8_t rm6300_hexnibble(char chr) { + uint8_t rVal = 0; + if (isdigit(chr)) { + rVal = chr - '0'; + } else { + if (chr >= 'A' && chr <= 'F') rVal = chr + 10 - 'A'; + if (chr >= 'a' && chr <= 'f') rVal = chr + 10 - 'a'; + } + return rVal; +} + +// convert hex string to int array +void rm6300_hstring_to_array(uint8_t array[], uint8_t len, char buffer[]) +{ + char *cp=buffer; + for (uint8_t i = 0; i < len; i++) { + uint8_t val = rm6300_hexnibble(*cp++) << 4; + array[i]= val | rm6300_hexnibble(*cp++); + } +} + +#ifdef USE_WEBSERVER +const char HTTP_RDM6300[] PROGMEM = + "{s}RDM6300 " "UID" "{m}%s" "{e}"; + +void RDM6300_Show(void) { + if (!RDM6300_Serial) return; + if (!rdm_uid_str[0]) strcpy(rdm_uid_str,"????"); + WSContentSend_PD(HTTP_RDM6300,rdm_uid_str); +} +#endif // USE_WEBSERVER + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xsns51(byte function) +{ + bool result = false; + + switch (function) { + case FUNC_INIT: + RDM6300_Init(); + break; + case FUNC_EVERY_100_MSECOND: + RDM6300_ScanForTag(); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + RDM6300_Show(); + break; +#endif // USE_WEBSERVER + } + return result; +} + +#endif // USE_RDM6300 diff --git a/sonoff/xsns_52_ibeacon.ino b/sonoff/xsns_52_ibeacon.ino new file mode 100644 index 000000000..1df541d82 --- /dev/null +++ b/sonoff/xsns_52_ibeacon.ino @@ -0,0 +1,590 @@ +/* + xsns_52_ibeacon.ino - Support for HM17 BLE Module + ibeacon reader + + Copyright (C) 2019 Gerhard Mutz and Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef USE_IBEACON + + + +#define XSNS_52 52 + +#include + +#define HM17_BAUDRATE 9600 + +//#define IBEACON_DEBUG + +// keyfob expires after N seconds +#define IB_TIMEOUT_INTERVAL 30 +// does a passive scan every N seconds +#define IB_UPDATE_TIME_INTERVAL 10 + +TasmotaSerial *IBEACON_Serial = nullptr; + + +uint8_t hm17_found,hm17_cmd,hm17_flag; + +#ifdef IBEACON_DEBUG +uint8_t hm17_debug=0; +#endif + + +// 78 is max serial response +#define HM17_BSIZ 128 +char hm17_sbuffer[HM17_BSIZ]; +uint8_t hm17_sindex,hm17_result,hm17_scanning,hm17_connecting; +uint32_t hm17_lastms; +char ib_mac[14]; + +// should be in Settings +#if 1 +uint8_t ib_upd_interval,ib_tout_interval; +#define IB_UPDATE_TIME ib_upd_interval +#define IB_TIMEOUT_TIME ib_tout_interval +#else +#undef IB_UPDATE_TIME +#undef IB_TIMEOUT_TIME +#define IB_UPDATE_TIME Settings.ib_upd_interval +#define IB_TIMEOUT_TIME Settings.ib_tout_interval +#endif + +enum {HM17_TEST,HM17_ROLE,HM17_IMME,HM17_DISI,HM17_IBEA,HM17_SCAN,HM17_DISC,HM17_RESET,HM17_RENEW,HM17_CON}; +#define HM17_SUCESS 99 + +struct IBEACON { + char FACID[8]; + char UID[32]; + char MAJOR[4]; + char MINOR[4]; + char PWR[2]; + char MAC[12]; + char RSSI[4]; +}; + +#define MAX_IBEACONS 16 + +struct IBEACON_UID { + char MAC[12]; + char RSSI[4]; + uint8_t FLAGS; + uint8_t TIME; +} ibeacons[MAX_IBEACONS]; + + +void IBEACON_Init() { + + hm17_found=0; + +// actually doesnt work reliably with software serial + if ((pin[GPIO_IBEACON_RX] < 99) && (pin[GPIO_IBEACON_TX] < 99)) { + IBEACON_Serial = new TasmotaSerial(pin[GPIO_IBEACON_RX], pin[GPIO_IBEACON_TX],1); + if (IBEACON_Serial->begin(HM17_BAUDRATE)) { + if (IBEACON_Serial->hardwareSerial()) { + ClaimSerial(); + } + hm17_sendcmd(HM17_TEST); + hm17_lastms=millis(); + // in case of using Settings this has to be moved + IB_UPDATE_TIME=IB_UPDATE_TIME_INTERVAL; + IB_TIMEOUT_TIME=IB_TIMEOUT_INTERVAL; + } + } +} + +void hm17_every_second(void) { + if (!IBEACON_Serial) return; + + if (hm17_found) { + if (IB_UPDATE_TIME && (uptime%IB_UPDATE_TIME==0)) { + if (hm17_cmd!=99) { + if (hm17_flag&2) { + ib_sendbeep(); + } else { + if (!hm17_connecting) { + hm17_sendcmd(HM17_DISI); + } + } + } + } + for (uint32_t cnt=0;cntIB_TIMEOUT_TIME) { + ibeacons[cnt].FLAGS=0; + ibeacon_mqtt(ibeacons[cnt].MAC,"0000"); + } + } + } + } else { + if (uptime%20==0) { + hm17_sendcmd(HM17_TEST); + } + } +} + +void hm17_sbclr(void) { + memset(hm17_sbuffer,0,HM17_BSIZ); + hm17_sindex=0; + IBEACON_Serial->flush(); +} + +void hm17_sendcmd(uint8_t cmd) { + hm17_sbclr(); + hm17_cmd=cmd; +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("hm17cmd %d"),cmd); +#endif + switch (cmd) { + case HM17_TEST: + IBEACON_Serial->write("AT"); + break; + case HM17_ROLE: + IBEACON_Serial->write("AT+ROLE1"); + break; + case HM17_IMME: + IBEACON_Serial->write("AT+IMME1"); + break; + case HM17_DISI: + IBEACON_Serial->write("AT+DISI?"); + hm17_scanning=1; + break; + case HM17_IBEA: + IBEACON_Serial->write("AT+IBEA1"); + break; + case HM17_RESET: + IBEACON_Serial->write("AT+RESET"); + break; + case HM17_RENEW: + IBEACON_Serial->write("AT+RENEW"); + break; + case HM17_SCAN: + IBEACON_Serial->write("AT+SCAN5"); + break; + case HM17_DISC: + IBEACON_Serial->write("AT+DISC?"); + hm17_scanning=1; + break; + case HM17_CON: + IBEACON_Serial->write((const uint8_t*)"AT+CON",6); + IBEACON_Serial->write((const uint8_t*)ib_mac,12); + hm17_connecting=1; + break; + } +} + +uint32_t ibeacon_add(struct IBEACON *ib) { + // keyfob starts with ffff, ibeacon has valid facid + if (!strncmp(ib->MAC,"FFFF",4) || strncmp(ib->FACID,"00000000",8)) { + for (uint32_t cnt=0;cntMAC,12)) { + // exists + memcpy(ibeacons[cnt].RSSI,ib->RSSI,4); + ibeacons[cnt].TIME=0; + return 1; + } + } + } + for (uint32_t cnt=0;cntMAC,12); + memcpy(ibeacons[cnt].RSSI,ib->RSSI,4); + ibeacons[cnt].FLAGS=1; + ibeacons[cnt].TIME=0; + return 1; + } + } + } + return 0; +} + +void hm17_decode(void) { + struct IBEACON ib; + switch (hm17_cmd) { + case HM17_TEST: + if (!strncmp(hm17_sbuffer,"OK",2)) { +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("AT OK")); +#endif + hm17_sbclr(); + hm17_result=HM17_SUCESS; + hm17_found=1; + } + break; + case HM17_ROLE: + if (!strncmp(hm17_sbuffer,"OK+Set:1",8)) { +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("ROLE OK")); +#endif + hm17_sbclr(); + hm17_result=HM17_SUCESS; + } + break; + case HM17_IMME: + if (!strncmp(hm17_sbuffer,"OK+Set:1",8)) { +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("IMME OK")); +#endif + hm17_sbclr(); + hm17_result=HM17_SUCESS; + } + break; + case HM17_IBEA: + if (!strncmp(hm17_sbuffer,"OK+Set:1",8)) { +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("IBEA OK")); +#endif + hm17_sbclr(); + hm17_result=HM17_SUCESS; + } + break; + case HM17_SCAN: + if (!strncmp(hm17_sbuffer,"OK+Set:5",8)) { +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("SCAN OK")); +#endif + hm17_sbclr(); + hm17_result=HM17_SUCESS; + } + break; + case HM17_RESET: + if (!strncmp(hm17_sbuffer,"OK+RESET",8)) { +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("RESET OK")); +#endif + hm17_sbclr(); + hm17_result=HM17_SUCESS; + } + break; + case HM17_RENEW: + if (!strncmp(hm17_sbuffer,"OK+RENEW",8)) { +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("RENEW OK")); +#endif + hm17_sbclr(); + hm17_result=HM17_SUCESS; + } + break; + case HM17_CON: + if (!strncmp(hm17_sbuffer,"OK+CONNA",8)) { + hm17_sbclr(); +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("CONNA OK")); +#endif + hm17_connecting=2; + break; + } + if (!strncmp(hm17_sbuffer,"OK+CONNE",8)) { + hm17_sbclr(); +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("CONNE ERROR")); +#endif + break; + } + if (!strncmp(hm17_sbuffer,"OK+CONNF",8)) { + hm17_sbclr(); +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("CONNF ERROR")); +#endif + break; + } + if (hm17_connecting==2 && !strncmp(hm17_sbuffer,"OK+CONN",7)) { + hm17_sbclr(); +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("CONN OK")); +#endif + hm17_connecting=3; + hm17_sendcmd(HM17_TEST); + hm17_connecting=0; + break; + } + break; + + case HM17_DISI: + case HM17_DISC: + if (!strncmp(hm17_sbuffer,"OK+DISCS",8)) { + hm17_sbclr(); + hm17_result=1; +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("DISCS OK")); +#endif + break; + } + if (!strncmp(hm17_sbuffer,"OK+DISCE",8)) { + hm17_sbclr(); + hm17_result=HM17_SUCESS; +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("DISCE OK")); +#endif + hm17_scanning=0; + break; + } + if (!strncmp(hm17_sbuffer,"OK+NAME:",8)) { + if (hm17_sbuffer[hm17_sindex-1]=='\n') { + hm17_result=HM17_SUCESS; +#ifdef IBEACON_DEBUG + if (hm17_debug) { + AddLog_P2(LOG_LEVEL_INFO, PSTR("NAME OK")); + AddLog_P2(LOG_LEVEL_INFO, PSTR(">>%s"),&hm17_sbuffer[8]); + } +#endif + hm17_sbclr(); + } + break; + } + if (!strncmp(hm17_sbuffer,"OK+DIS0:",8)) { + if (hm17_sindex==20) { + hm17_result=HM17_SUCESS; +#ifdef IBEACON_DEBUG + if (hm17_debug) { + AddLog_P2(LOG_LEVEL_INFO, PSTR("DIS0 OK")); + AddLog_P2(LOG_LEVEL_INFO, PSTR(">>%s"),&hm17_sbuffer[8]); + } +#endif + hm17_sbclr(); + } + break; + } + if (!strncmp(hm17_sbuffer,"OK+DISC:",8)) { + if (hm17_cmd==HM17_DISI) { + if (hm17_sindex==78) { +#ifdef IBEACON_DEBUG + if (hm17_debug) { + AddLog_P2(LOG_LEVEL_INFO, PSTR("DISC: OK")); + //OK+DISC:4C 000C0E:003 A9144081A8 3B16849611 862EC1005: 0B1CE7485D :4DB4E940F C0E:-078 + AddLog_P2(LOG_LEVEL_INFO, PSTR(">>%s"),&hm17_sbuffer[8]); + } +#endif + memcpy(ib.FACID,&hm17_sbuffer[8],8); + memcpy(ib.UID,&hm17_sbuffer[8+8+1],32); + memcpy(ib.MAJOR,&hm17_sbuffer[8+8+1+32+1],4); + memcpy(ib.MINOR,&hm17_sbuffer[8+8+1+32+1+4],4); + memcpy(ib.PWR,&hm17_sbuffer[8+8+1+32+1+4+4],2); + memcpy(ib.MAC,&hm17_sbuffer[8+8+1+32+1+4+4+2+1],12); + memcpy(ib.RSSI,&hm17_sbuffer[8+8+1+32+1+4+4+2+1+12+1],4); + + if (ibeacon_add(&ib)) { + ibeacon_mqtt(ib.MAC,ib.RSSI); + } + hm17_sbclr(); + hm17_result=1; + } + } else { +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR(">>%s"),&hm17_sbuffer[8]); +#endif + } + break; + } + } +} + +void IBEACON_loop() { + + if (!IBEACON_Serial) return; + +uint32_t difftime=millis()-hm17_lastms; + + while (IBEACON_Serial->available()) { + hm17_lastms=millis(); + // shift in + if (hm17_sindexread(); + hm17_sindex++; + hm17_decode(); + } else { + hm17_sindex=0; + break; + } + } + + if (hm17_cmd==99) { + if (hm17_sindex>=HM17_BSIZ-2 || (hm17_sindex && (difftime>100))) { + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),hm17_sbuffer); + hm17_sbclr(); + } + } + +} + +#ifdef USE_WEBSERVER +const char HTTP_IBEACON[] PROGMEM = + "{s}IBEACON-UID : %s" " - RSSI : %s" "{m}{e}"; + +void IBEACON_Show(void) { +char mac[14]; +char rssi[6]; + + for (uint32_t cnt=0;cnt 0) { + char *cp=XdrvMailbox.data; + if (*cp>='0' && *cp<='8') { + hm17_sendcmd(*cp&7); + Response_P(S_JSON_IBEACON, XSNS_52,"hm17cmd",*cp&7); + } else if (*cp=='s') { + cp++; + len--; + while (*cp==' ') { + len--; + cp++; + } + IBEACON_Serial->write((uint8_t*)cp,len); + hm17_cmd=99; + Response_P(S_JSON_IBEACON1, XSNS_52,"hm17cmd",cp); + } else if (*cp=='u') { + cp++; + if (*cp) IB_UPDATE_TIME=atoi(cp); + Response_P(S_JSON_IBEACON, XSNS_52,"uintv",IB_UPDATE_TIME); + } else if (*cp=='t') { + cp++; + if (*cp) IB_TIMEOUT_TIME=atoi(cp); + Response_P(S_JSON_IBEACON, XSNS_52,"lintv",IB_TIMEOUT_TIME); + } else if (*cp=='c') { + for (uint32_t cnt=0;cnt. + +*/ + +#ifdef USE_SML_M + +#define XSNS_53 53 + +// default baudrate of D0 output +#define SML_BAUDRATE 9600 + +// send this every N seconds (for meters that only send data on demand) +// not longer supported, use scripting instead +//#define SML_SEND_SEQ + +// debug counter input to led for counter1 and 2 +//#define DEBUG_CNT_LED1 2 +//#define DEBUG_CNT_LED1 2 + +// use analog optical counter sensor with AD Converter ADS1115 (not yet functional) +//#define ANALOG_OPTO_SENSOR +// fototransistor with pullup at A0, A1 of ADS1115 A3 and +3.3V +// level and amplification are automatically set + + +#include + +// use special no wait serial driver, should be always on +#define SPECIAL_SS + +// addresses a bug in meter DWS74 +//#define DWS74_BUG + +// max 23 chars +#if DMY_LANGUAGE==de-DE +// german web text +#define D_TPWRIN "Verbrauch" +#define D_TPWROUT "Einspeisung" +#define D_TPWRCURR "Aktueller Verbrauch" +#define D_TPWRCURR1 "Verbrauch P1" +#define D_TPWRCURR2 "Verbrauch P2" +#define D_TPWRCURR3 "Verbrauch P3" +#define D_Strom_L1 "Strom L1" +#define D_Strom_L2 "Strom L2" +#define D_Strom_L3 "Strom L3" +#define D_Spannung_L1 "Spannung L1" +#define D_Spannung_L2 "Spannung L2" +#define D_Spannung_L3 "Spannung L3" +#define D_METERNR "Zähler Nr" +#define D_METERSID "Service ID" +#define D_GasIN "Zählerstand" // Gas-Verbrauch +#define D_H2oIN "Zählerstand" // H2o-Verbrauch +#define D_StL1L2L3 "Ströme L1+L2+L3" +#define D_SpL1L2L3 "Spannung L1+L2+L3/3" + +#else +// other languages (tbd) +#undef D_TPWRIN +#undef D_TPWROUT +#undef D_TPWRCURR +#undef D_TPWRCURR1 +#undef D_TPWRCURR2 +#undef D_TPWRCURR3 +#undef D_Strom_L1 +#undef D_Strom_L2 +#undef D_Strom_L3 +#undef D_Spannung_L1 +#undef D_Spannung_L2 +#undef D_Spannung_L3 +#undef D_METERNR +#undef D_METERSID +#undef D_GasIN +#undef D_H2oIN +#undef D_StL1L2L3 +#undef D_SpL1L2L3 + +#define D_TPWRIN "Total-In" +#define D_TPWROUT "Total-Out" +#define D_TPWRCURR "Current-In/Out" +#define D_TPWRCURR1 "Current-In p1" +#define D_TPWRCURR2 "Current-In p2" +#define D_TPWRCURR3 "Current-In p3" +#define D_Strom_L1 "Current L1" +#define D_Strom_L2 "Current L2" +#define D_Strom_L3 "Current L3" +#define D_Spannung_L1 "Voltage L1" +#define D_Spannung_L2 "Voltage L2" +#define D_Spannung_L3 "Voltage L3" +#define D_METERNR "Meter_number" +#define D_METERSID "Service ID" +#define D_GasIN "Counter" // Gas-Verbrauch +#define D_H2oIN "Counter" // H2o-Verbrauch +#define D_StL1L2L3 "Current L1+L2+L3" +#define D_SpL1L2L3 "Voltage L1+L2+L3/3" + +#endif + +// JSON Strings do not translate +// max 23 char +#define DJ_TPWRIN "Total_in" +#define DJ_TPWROUT "Total_out" +#define DJ_TPWRCURR "Power_curr" +#define DJ_TPWRCURR1 "Power_p1" +#define DJ_TPWRCURR2 "Power_p2" +#define DJ_TPWRCURR3 "Power_p3" +#define DJ_CURR1 "Curr_p1" +#define DJ_CURR2 "Curr_p2" +#define DJ_CURR3 "Curr_p3" +#define DJ_VOLT1 "Volt_p1" +#define DJ_VOLT2 "Volt_p2" +#define DJ_VOLT3 "Volt_p3" +#define DJ_METERNR "Meter_number" +#define DJ_METERSID "Meter_id" +#define DJ_CSUM "Curr_summ" +#define DJ_VAVG "Volt_avg" +#define DJ_COUNTER "Count" + +struct METER_DESC { + uint8_t srcpin; + uint8_t type; + uint16_t flag; + int32_t params; + char prefix[8]; + int8_t trxpin; + uint8_t tsecs; + char *txmem; + uint8_t index; + uint8_t max_index; +}; + +// this descriptor method is no longer supported +// but still functional for simple meters +// use scripting method instead +// meter list , enter new meters here +//===================================================== +#define EHZ161_0 1 +#define EHZ161_1 2 +#define EHZ363 3 +#define EHZH 4 +#define EDL300 5 +#define Q3B 6 +#define COMBO3 7 +#define COMBO2 8 +#define COMBO3a 9 +#define Q3B_V1 10 +#define EHZ363_2 11 +#define COMBO3b 12 +#define WGS_COMBO 13 +#define EBZD_G 14 + +// select this meter +#define METER EHZ161_1 + + +#if METER==EHZ161_0 +#undef METERS_USED +#define METERS_USED 1 +struct METER_DESC const meter_desc[METERS_USED]={ + [0]={3,'o',0,SML_BAUDRATE,"OBIS",-1,1,0}}; +const uint8_t meter[]= +"1,1-0:1.8.0*255(@1," D_TPWRIN ",KWh," DJ_TPWRIN ",4|" +"1,1-0:2.8.0*255(@1," D_TPWROUT ",KWh," DJ_TPWROUT ",4|" +"1,1-0:21.7.0*255(@1," D_TPWRCURR1 ",W," DJ_TPWRCURR1 ",0|" +"1,1-0:41.7.0*255(@1," D_TPWRCURR2 ",W," DJ_TPWRCURR2 ",0|" +"1,1-0:61.7.0*255(@1," D_TPWRCURR3 ",W," DJ_TPWRCURR3 ",0|" +"1,=m 3+4+5 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" +"1,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0"; + +#endif + +//===================================================== + +#if METER==EHZ161_1 +#undef METERS_USED +#define METERS_USED 1 +struct METER_DESC const meter_desc[METERS_USED]={ + [0]={3,'o',0,SML_BAUDRATE,"OBIS",-1,1,0}}; +const uint8_t meter[]= +"1,1-0:1.8.1*255(@1," D_TPWRIN ",KWh," DJ_TPWRIN ",4|" +"1,1-0:2.8.1*255(@1," D_TPWROUT ",KWh," DJ_TPWROUT ",4|" +"1,=d 2 10 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" +"1,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0"; +#endif + +//===================================================== + +#if METER==EHZ363 +#undef METERS_USED +#define METERS_USED 1 +struct METER_DESC const meter_desc[METERS_USED]={ + [0]={3,'s',0,SML_BAUDRATE,"SML",-1,1,0}}; +// 2 Richtungszähler EHZ SML 8 bit 9600 baud, binär +const uint8_t meter[]= +//0x77,0x07,0x01,0x00,0x01,0x08,0x00,0xff +"1,77070100010800ff@1000," D_TPWRIN ",KWh," DJ_TPWRIN ",4|" +//0x77,0x07,0x01,0x00,0x02,0x08,0x00,0xff +"1,77070100020800ff@1000," D_TPWROUT ",KWh," DJ_TPWROUT ",4|" +//0x77,0x07,0x01,0x00,0x10,0x07,0x00,0xff +"1,77070100100700ff@1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" +//0x77,0x07,0x01,0x00,0x00,0x00,0x09,0xff +"1,77070100000009ff@#," D_METERNR ",," DJ_METERNR ",0"; +#endif + +//===================================================== + +#if METER==EHZH +#undef METERS_USED +#define METERS_USED 1 +struct METER_DESC const meter_desc[METERS_USED]={ + [0]={3,'s',0,SML_BAUDRATE,"SML",-1,1,0}}; +// 2 Richtungszähler EHZ SML 8 bit 9600 baud, binär +// verbrauch total +const uint8_t meter[]= +//0x77,0x07,0x01,0x00,0x01,0x08,0x00,0xff +"1,77070100010800ff@1000," D_TPWRIN ",KWh," DJ_TPWRIN ",4|" +//0x77,0x07,0x01,0x00,0x01,0x08,0x01,0xff +"1,77070100020800ff@1000," D_TPWROUT ",KWh," DJ_TPWROUT ",4|" +//0x77,0x07,0x01,0x00,0x0f,0x07,0x00,0xff +"1,770701000f0700ff@1," D_TPWRCURR ",W," DJ_TPWRCURR ",0"; +#endif + +//===================================================== + +#if METER==EDL300 +#undef METERS_USED +#define METERS_USED 1 +struct METER_DESC const meter_desc[METERS_USED]={ + [0]={3,'s',0,SML_BAUDRATE,"SML",-1,1,0}}; +// 2 Richtungszähler EHZ SML 8 bit 9600 baud, binär +// verbrauch total +const uint8_t meter[]= +//0x77,0x07,0x01,0x00,0x01,0x08,0x00,0xff +"1,77070100010800ff@1000," D_TPWRIN ",KWh," DJ_TPWRIN ",4|" +//0x77,0x07,0x01,0x00,0x01,0x08,0x01,0xff +"1,77070100020801ff@1000," D_TPWROUT ",KWh," DJ_TPWROUT ",4|" +//0x77,0x07,0x01,0x00,0x0f,0x07,0x00,0xff +"1,770701000f0700ff@1," D_TPWRCURR ",W," DJ_TPWRCURR ",0"; +#endif + +#if METER==EBZD_G +#undef METERS_USED +#define METERS_USED 1 +struct METER_DESC const meter_desc[METERS_USED]={ + [0]={3,'s',0,SML_BAUDRATE,"strom",-1,1,0}}; +const uint8_t meter[]= +//0x77,0x07,0x01,0x00,0x01,0x08,0x00,0xff +"1,77070100010800ff@1000," D_TPWRIN ",KWh," DJ_TPWRIN ",4|" +// .. +"1,77070100020800ff@1000," D_TPWROUT ",KWh," DJ_TPWROUT ",4|" +//0x77,0x07,0x01,0x00,0x01,0x08,0x01,0xff +"1,77070100010801ff@1000," D_TPWRCURR1 ",KWh," DJ_TPWRCURR1 ",4|" +//0x77,0x07,0x01,0x00,0x01,0x08,0x02,0xff +"1,77070100010802ff@1000," D_TPWRCURR2 ",KWh," DJ_TPWRCURR2 ",4|" +// 77 07 01 00 10 07 00 FF +"1,77070100100700ff@1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" +// .. +"1,77070100600100ff@#," D_METERNR ",," DJ_METERNR ",0"; +#endif + + +//===================================================== + +#if METER==Q3B +#undef METERS_USED +#define METERS_USED 1 +struct METER_DESC const meter_desc[METERS_USED]={ + [0]={3,'s',0,SML_BAUDRATE,"SML",-1,1,0}}; +const uint8_t meter[]= +//0x77,0x07,0x01,0x00,0x01,0x08,0x01,0xff +"1,77070100010800ff@1000," D_TPWRIN ",KWh," DJ_TPWRIN ",4|" +//0x77,0x07,0x01,0x00,0x02,0x08,0x01,0xff +"1,77070100020801ff@1000," D_TPWROUT ",KWh," DJ_TPWROUT ",4|" +//0x77,0x07,0x01,0x00,0x01,0x07,0x00,0xff +"1,77070100010700ff@1," D_TPWRCURR ",W," DJ_TPWRCURR ",0"; +#endif + +#if METER==COMBO3 +// 3 Zähler Beispiel +#undef METERS_USED +#define METERS_USED 3 + +struct METER_DESC const meter_desc[METERS_USED]={ + [0]={3,'o',0,SML_BAUDRATE,"OBIS",-1,1,0}, // harware serial RX pin + [1]={14,'s',0,SML_BAUDRATE,"SML",-1,1,0}, // GPIO14 software serial + [2]={4,'o',0,SML_BAUDRATE,"OBIS2",-1,1,0}}; // GPIO4 software serial + +// 3 Zähler definiert +const uint8_t meter[]= +"1,1-0:1.8.0*255(@1," D_TPWRIN ",KWh," DJ_TPWRIN ",4|" +"1,1-0:2.8.0*255(@1," D_TPWROUT ",KWh," DJ_TPWROUT ",4|" +"1,1-0:21.7.0*255(@1," D_TPWRCURR1 ",W," DJ_TPWRCURR1 ",0|" +"1,1-0:41.7.0*255(@1," D_TPWRCURR2 ",W," DJ_TPWRCURR2 ",0|" +"1,1-0:61.7.0*255(@1," D_TPWRCURR3 ",W," DJ_TPWRCURR3 ",0|" +"1,=m 3+4+5 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" +"1,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0|" +"2,77070100010800ff@1000," D_TPWRIN ",KWh," DJ_TPWRIN ",4|" +"2,77070100020800ff@1000," D_TPWROUT ",KWh," DJ_TPWROUT ",4|" +"2,77070100100700ff@1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" +"3,1-0:1.8.1*255(@1," D_TPWRIN ",KWh," DJ_TPWRIN ",4|" +"3,1-0:2.8.1*255(@1," D_TPWROUT ",KWh," DJ_TPWROUT ",4|" +"3,=d 2 10 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" +"3,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0"; + +#endif + +#if METER==COMBO2 +// 2 Zähler Beispiel +#undef METERS_USED +#define METERS_USED 2 + +struct METER_DESC const meter_desc[METERS_USED]={ + [0]={3,'o',0,SML_BAUDRATE,"OBIS1",-1,1,0}, // harware serial RX pin + [1]={14,'o',0,SML_BAUDRATE,"OBIS2",-1,1,0}}; // GPIO14 software serial + +// 2 Zähler definiert +const uint8_t meter[]= +"1,1-0:1.8.1*255(@1," D_TPWRIN ",KWh," DJ_TPWRIN ",4|" +"1,1-0:2.8.1*255(@1," D_TPWROUT ",KWh," DJ_TPWROUT ",4|" +"1,=d 2 10 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" +"1,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0|" + +"2,1-0:1.8.1*255(@1," D_TPWRIN ",KWh," DJ_TPWRIN ",4|" +"2,1-0:2.8.1*255(@1," D_TPWROUT ",KWh," DJ_TPWROUT ",4|" +"2,=d 6 10 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" +"2,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0"; + +#endif + +#if METER==COMBO3a +#undef METERS_USED +#define METERS_USED 3 + +struct METER_DESC const meter_desc[METERS_USED]={ + [0]={3,'o',0,SML_BAUDRATE,"OBIS1",-1,1,0}, // harware serial RX pin + [1]={14,'o',0,SML_BAUDRATE,"OBIS2",-1,1,0}, + [2]={1,'o',0,SML_BAUDRATE,"OBIS3",-1,1,0}}; + +// 3 Zähler definiert +const uint8_t meter[]= +"1,=h --- Zähler Nr 1 ---|" +"1,1-0:1.8.1*255(@1," D_TPWRIN ",KWh," DJ_TPWRIN ",4|" +"1,1-0:2.8.1*255(@1," D_TPWROUT ",KWh," DJ_TPWROUT ",4|" +"1,=d 2 10 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" +"1,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0|" +"2,=h --- Zähler Nr 2 ---|" +"2,1-0:1.8.1*255(@1," D_TPWRIN ",KWh," DJ_TPWRIN ",4|" +"2,1-0:2.8.1*255(@1," D_TPWROUT ",KWh," DJ_TPWROUT ",4|" +"2,=d 6 10 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" +"2,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0|" +"3,=h --- Zähler Nr 3 ---|" +"3,1-0:1.8.1*255(@1," D_TPWRIN ",KWh," DJ_TPWRIN ",4|" +"3,1-0:2.8.1*255(@1," D_TPWROUT ",KWh," DJ_TPWROUT ",4|" +"3,=d 10 10 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" +"3,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0"; + +#endif + +//===================================================== + +#if METER==Q3B_V1 +#undef METERS_USED +#define METERS_USED 1 +struct METER_DESC const meter_desc[METERS_USED]={ +[0]={3,'o',0,SML_BAUDRATE,"OBIS",-1,1,0}}; +const uint8_t meter[]= +"1,1-0:1.8.1*255(@1," D_TPWRIN ",KWh," DJ_TPWRIN ",4|" +"1,=d 1 10 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" +"1,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0"; +#endif + +//===================================================== + +#if METER==EHZ363_2 +#undef METERS_USED +#define METERS_USED 1 +struct METER_DESC const meter_desc[METERS_USED]={ +[0]={3,'s',0,SML_BAUDRATE,"SML",-1,1,0}}; +// 2 direction meter EHZ SML 8 bit 9600 baud, binary +const uint8_t meter[]= +//0x77,0x07,0x01,0x00,0x01,0x08,0x00,0xff +"1,77070100010800ff@1000," D_TPWRIN ",KWh," DJ_TPWRIN ",4|" +//0x77,0x07,0x01,0x00,0x02,0x08,0x00,0xff +"1,77070100020800ff@1000," D_TPWROUT ",KWh," DJ_TPWROUT ",4|" +//0x77,0x07,0x01,0x00,0x01,0x08,0x01,0xff +"1,77070100010801ff@1000," D_TPWRCURR1 ",KWh," DJ_TPWRCURR1 ",4|" +//0x77,0x07,0x01,0x00,0x01,0x08,0x02,0xff +"1,77070100010802ff@1000," D_TPWRCURR2 ",KWh," DJ_TPWRCURR2 ",4|" +//0x77,0x07,0x01,0x00,0x10,0x07,0x00,0xff +"1,77070100100700ff@1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" +//0x77,0x07,0x01,0x00,0x00,0x00,0x09,0xff +"1,77070100000009ff@#," D_METERNR ",," DJ_METERNR ",0"; +#endif + +// example OBIS power meter + gas and water counter +#if METER==COMBO3b +#undef METERS_USED +#define METERS_USED 3 +struct METER_DESC const meter_desc[METERS_USED]={ + [0]={3,'o',0,SML_BAUDRATE,"OBIS",-1,1,0}, // harware serial RX pin + [1]={14,'c',0,50,"Gas"}, // GPIO14 gas counter + [2]={1,'c',0,10,"Wasser"}}; // water counter + +// 3 meters defined +const uint8_t meter[]= +"1,1-0:1.8.1*255(@1," D_TPWRIN ",KWh," DJ_TPWRIN ",4|" +"1,1-0:2.8.1*255(@1," D_TPWROUT ",KWh," DJ_TPWROUT ",4|" +"1,=d 2 10 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" +"1,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0|" + +// with counters the comparison string must be exactly this string +"2,1-0:1.8.0*255(@100," D_GasIN ",cbm," DJ_COUNTER ",2|" + +"3,1-0:1.8.0*255(@100," D_H2oIN ",cbm," DJ_COUNTER ",2"; +#endif + + +#if METER==WGS_COMBO +#undef METERS_USED +#define METERS_USED 3 + +struct METER_DESC const meter_desc[METERS_USED]={ + [0]={1,'c',0,10,"H20",-1,1,0}, // GPIO1 water counter + [1]={4,'c',0,50,"GAS",-1,1,0}, // GPIO4 gas counter + [2]={3,'s',0,SML_BAUDRATE,"SML",-1,1,0}}; // SML harware serial RX pin + +const uint8_t meter[]= +//----------------------------Wasserzähler--sensor53 c1------------------------------------ +//"1,=h==================|" +"1,1-0:1.8.0*255(@10000," D_H2oIN ",cbm," DJ_COUNTER ",4|" // 1 +//----------------------------Gaszähler-----sensor53 c2------------------------------------ +// bei gaszählern (countern) muss der Vergleichsstring so aussehen wie hier +"2,=h==================|" +"2,1-0:1.8.0*255(@100," D_GasIN ",cbm," DJ_COUNTER ",3|" // 2 +//----------------------------Stromzähler-EHZ363W5--sensor53 d0---------------------------- +"3,=h==================|" +//0x77,0x07,0x01,0x00,0x01,0x08,0x00,0xff +"3,77070100010800ff@1000," D_TPWRIN ",KWh," DJ_TPWRIN ",3|" // 3 Zählerstand Total +"3,=h==================|" +//0x77,0x07,0x01,0x00,0x10,0x07,0x00,0xff +"3,77070100100700ff@1," D_TPWRCURR ",W," DJ_TPWRCURR ",2|" // 4 Aktuelle Leistung +"3,=h -------------------------------|" +"3,=m 10+11+12 @100," D_StL1L2L3 ",A," DJ_CSUM ",2|" // 5 Summe Aktuelle Ströme +//"3,=h -------------------------------|" +"3,=m 13+14+15/#3 @100," D_SpL1L2L3 ",V," DJ_VAVG ",2|" // 6 Mittelwert Spannungen +"3,=h==================|" +//0x77,0x07,0x01,0x00,0x24,0x07,0x00,0xff +"3,77070100240700ff@1," D_TPWRCURR1 ",W," DJ_TPWRCURR1 ",2|" // 7 Wirkleistung L1 +//0x77,0x07,0x01,0x00,0x38,0x07,0x00,0xff +"3,77070100380700ff@1," D_TPWRCURR2 ",W," DJ_TPWRCURR2 ",2|" // 8 Wirkleistung L2 +//0x77,0x07,0x01,0x00,0x4c,0x07,0x00,0xff +"3,770701004c0700ff@1," D_TPWRCURR3 ",W," DJ_TPWRCURR3 ",2|" // 9 Wirkleistung L3 +"3,=h -------------------------------|" +//0x77,0x07,0x01,0x00,0x1f,0x07,0x00,0xff +"3,770701001f0700ff@100," D_Strom_L1 ",A," DJ_CURR1 ",2|" // 10 Strom L1 +//0x77,0x07,0x01,0x00,0x33,0x07,0x00,0xff +"3,77070100330700ff@100," D_Strom_L2 ",A," DJ_CURR2 ",2|" // 11 Strom L2 +//0x77,0x07,0x01,0x00,0x47,0x07,0x00,0xff +"3,77070100470700ff@100," D_Strom_L3 ",A," DJ_CURR3 ",2|" // 12 Strom L3 +"3,=h -------------------------------|" +//0x77,0x07,0x01,0x00,0x20,0x07,0x00,0xff +"3,77070100200700ff@100," D_Spannung_L1 ",V," DJ_VOLT1 ",2|" // 13 Spannung L1 +//0x77,0x07,0x01,0x00,0x34,0x07,0x00,0xff +"3,77070100340700ff@100," D_Spannung_L2 ",V," DJ_VOLT2 ",2|" // 14 Spannung L2 +//0x77,0x07,0x01,0x00,0x48,0x07,0x00,0xff +"3,77070100480700ff@100," D_Spannung_L3 ",V," DJ_VOLT3 ",2|" // 15 Spannung L3 +"3,=h==================|" +//0x77,0x07,0x01,0x00,0x00,0x00,0x09,0xff +"3,77070100000009ff@#," D_METERSID ",," DJ_METERSID ",0|" // 16 Service ID +"3,=h--------------------------------"; // letzte Zeile +#endif + + +// this driver uses double because meter vars would not fit in float +//===================================================== + +// median filter eliminates outliers, but uses much RAM and CPU cycles +// 672 bytes extra RAM with MAX_VARS = 16 +// default compile on, but must be enabled by descriptor flag 16 +// may be undefined if RAM must be saved +#define USE_SML_MEDIAN_FILTER + +// max number of vars , may be adjusted +#define MAX_VARS 20 +// max number of meters , may be adjusted +#define MAX_METERS 5 +double meter_vars[MAX_VARS]; +// calulate deltas +#define MAX_DVARS MAX_METERS*2 +double dvalues[MAX_DVARS]; +uint32_t dtimes[MAX_DVARS]; +uint8_t meters_used; + +struct METER_DESC const *meter_desc_p; +const uint8_t *meter_p; +uint8_t meter_spos[MAX_METERS]; + +// software serial pointers +TasmotaSerial *meter_ss[MAX_METERS]; + +// serial buffers, may be made larger depending on telegram lenght +#define SML_BSIZ 48 +uint8_t smltbuf[MAX_METERS][SML_BSIZ]; + +// meter nr as string +#define METER_ID_SIZE 24 +char meter_id[MAX_METERS][METER_ID_SIZE]; + +#define EBUS_SYNC 0xaa +#define EBUS_ESC 0xa9 + +uint8_t sml_send_blocks; +uint8_t sml_100ms_cnt; +uint8_t sml_desc_cnt; + +#ifdef USE_SML_MEDIAN_FILTER +// median filter, should be odd size +#define MEDIAN_SIZE 5 +struct SML_MEDIAN_FILTER { +double buffer[MEDIAN_SIZE]; +int8_t index; +} sml_mf[MAX_VARS]; + +#ifndef FLT_MAX +#define FLT_MAX 99999999 +#endif + +double sml_median_array(double *array,uint8_t len) { + uint8_t ind[len]; + uint8_t mind=0,index=0,flg; + double min=FLT_MAX; + + for (uint8_t hcnt=0; hcntbuffer[mf->index]=in; + mf->index++; + if (mf->index>=MEDIAN_SIZE) mf->index=0; + + return sml_median_array(mf->buffer,MEDIAN_SIZE); +/* + // sort list and take median + memmove(tbuff,mf->buffer,sizeof(tbuff)); + for (byte ocnt=0; ocnttbuff[count+1]) { + tmp=tbuff[count]; + tbuff[count]=tbuff[count+1]; + tbuff[count+1]=tmp; + flag=1; + } + } + if (!flag) break; + } + return tbuff[MEDIAN_SIZE/2]; + */ +} +#endif + +#ifdef ANALOG_OPTO_SENSOR +// sensor over ADS1115 with i2c Bus +uint8_t ads1115_up; + +// ads1115 driver +#define SAMPLE_BIT (0x8000) + +#define ADS1115_COMP_QUEUE_SHIFT 0 +#define ADS1115_COMP_LATCH_SHIFT 2 +#define ADS1115_COMP_POLARITY_SHIFT 3 +#define ADS1115_COMP_MODE_SHIFT 4 +#define ADS1115_DATA_RATE_SHIFT 5 +#define ADS1115_MODE_SHIFT 8 +#define ADS1115_PGA_SHIFT 9 +#define ADS1115_MUX_SHIFT 12 + +enum ads1115_comp_queue { + ADS1115_COMP_QUEUE_AFTER_ONE = 0, + ADS1115_COMP_QUEUE_AFTER_TWO = 0x1 << ADS1115_COMP_QUEUE_SHIFT, + ADS1115_COMP_QUEUE_AFTER_FOUR = 0x2 << ADS1115_COMP_QUEUE_SHIFT, + ADS1115_COMP_QUEUE_DISABLE = 0x3 << ADS1115_COMP_QUEUE_SHIFT, + ADS1115_COMP_QUEUE_MASK = 0x3 << ADS1115_COMP_QUEUE_SHIFT, +}; + +enum ads1115_comp_latch { + ADS1115_COMP_LATCH_NO = 0, + ADS1115_COMP_LATCH_YES = 1 << ADS1115_COMP_LATCH_SHIFT, + ADS1115_COMP_LATCH_MASK = 1 << ADS1115_COMP_LATCH_SHIFT, +}; + +enum ads1115_comp_polarity { + ADS1115_COMP_POLARITY_ACTIVE_LOW = 0, + ADS1115_COMP_POLARITY_ACTIVE_HIGH = 1 << ADS1115_COMP_POLARITY_SHIFT, + ADS1115_COMP_POLARITY_MASK = 1 << ADS1115_COMP_POLARITY_SHIFT, +}; + +enum ads1115_comp_mode { + ADS1115_COMP_MODE_WINDOW = 0, + ADS1115_COMP_MODE_HYSTERESIS = 1 << ADS1115_COMP_MODE_SHIFT, + ADS1115_COMP_MODE_MASK = 1 << ADS1115_COMP_MODE_SHIFT, +}; + +enum ads1115_data_rate { + ADS1115_DATA_RATE_8_SPS = 0, + ADS1115_DATA_RATE_16_SPS = 0x1 << ADS1115_DATA_RATE_SHIFT, + ADS1115_DATA_RATE_32_SPS = 0x2 << ADS1115_DATA_RATE_SHIFT, + ADS1115_DATA_RATE_64_SPS = 0x3 << ADS1115_DATA_RATE_SHIFT, + ADS1115_DATA_RATE_128_SPS = 0x4 << ADS1115_DATA_RATE_SHIFT, + ADS1115_DATA_RATE_250_SPS = 0x5 << ADS1115_DATA_RATE_SHIFT, + ADS1115_DATA_RATE_475_SPS = 0x6 << ADS1115_DATA_RATE_SHIFT, + ADS1115_DATA_RATE_860_SPS = 0x7 << ADS1115_DATA_RATE_SHIFT, + ADS1115_DATA_RATE_MASK = 0x7 << ADS1115_DATA_RATE_SHIFT, +}; + +enum ads1115_mode { + ADS1115_MODE_CONTINUOUS = 0, + ADS1115_MODE_SINGLE_SHOT = 1 << ADS1115_MODE_SHIFT, + ADS1115_MODE_MASK = 1 << ADS1115_MODE_SHIFT, +}; + +enum ads1115_pga { + ADS1115_PGA_TWO_THIRDS = 0, //±6.144 V + ADS1115_PGA_ONE = 0x1 << ADS1115_PGA_SHIFT, //±4.096 V + ADS1115_PGA_TWO = 0x2 << ADS1115_PGA_SHIFT, //±2.048 V + ADS1115_PGA_FOUR = 0x3 << ADS1115_PGA_SHIFT, //±1.024 V + ADS1115_PGA_EIGHT = 0x4 << ADS1115_PGA_SHIFT, //±0.512 V + ADS1115_PGA_SIXTEEN = 0x5 << ADS1115_PGA_SHIFT, //±0.256 V + ADS1115_PGA_MASK = 0x7 << ADS1115_PGA_SHIFT, +}; + + +enum ads1115_mux { + ADS1115_MUX_DIFF_AIN0_AIN1 = 0, + ADS1115_MUX_DIFF_AIN0_AIN3 = 0x1 << ADS1115_MUX_SHIFT, + ADS1115_MUX_DIFF_AIN1_AIN3 = 0x2 << ADS1115_MUX_SHIFT, + ADS1115_MUX_DIFF_AIN2_AIN3 = 0x3 << ADS1115_MUX_SHIFT, + ADS1115_MUX_GND_AIN0 = 0x4 << ADS1115_MUX_SHIFT, + ADS1115_MUX_GND_AIN1 = 0x5 << ADS1115_MUX_SHIFT, + ADS1115_MUX_GND_AIN2 = 0x6 << ADS1115_MUX_SHIFT, + ADS1115_MUX_GND_AIN3 = 0x7 << ADS1115_MUX_SHIFT, + ADS1115_MUX_MASK = 0x7 << ADS1115_MUX_SHIFT, +}; + +class ADS1115 { +public: + ADS1115(uint8_t address = 0x48); + + void begin(); + uint8_t trigger_sample(); + uint8_t reset(); + bool is_sample_in_progress(); + int16_t read_sample(); + float sample_to_float(int16_t val); + float read_sample_float(); + + void set_comp_queue(enum ads1115_comp_queue val) { set_config(val, ADS1115_COMP_QUEUE_MASK); } + void set_comp_latching(enum ads1115_comp_latch val) { set_config(val, ADS1115_COMP_LATCH_MASK); } + void set_comp_polarity(enum ads1115_comp_polarity val) { set_config(val, ADS1115_COMP_POLARITY_MASK); } + void set_comp_mode(enum ads1115_comp_mode val) { set_config(val, ADS1115_COMP_MODE_MASK); } + void set_data_rate(enum ads1115_data_rate val) { set_config(val, ADS1115_DATA_RATE_MASK); } + void set_mode(enum ads1115_mode val) { set_config(val, ADS1115_MODE_MASK); } + void set_pga(enum ads1115_pga val) { set_config(val, ADS1115_PGA_MASK); m_voltage_range = val >> ADS1115_PGA_SHIFT; } + void set_mux(enum ads1115_mux val) { set_config(val, ADS1115_MUX_MASK); } + +private: + void set_config(uint16_t val, uint16_t mask) { + m_config = (m_config & ~mask) | val; + } + + uint8_t write_register(uint8_t reg, uint16_t val); + uint16_t read_register(uint8_t reg); + + uint8_t m_address; + uint16_t m_config; + int m_voltage_range; +}; + + +enum ads1115_register { + ADS1115_REGISTER_CONVERSION = 0, + ADS1115_REGISTER_CONFIG = 1, + ADS1115_REGISTER_LOW_THRESH = 2, + ADS1115_REGISTER_HIGH_THRESH = 3, +}; + +#define FACTOR 32768.0 +static float ranges[] = { 6.144 / FACTOR, 4.096 / FACTOR, 2.048 / FACTOR, 1.024 / FACTOR, 0.512 / FACTOR, 0.256 / FACTOR}; + +ADS1115::ADS1115(uint8_t address) +{ + m_address = address; + m_config = ADS1115_COMP_QUEUE_AFTER_ONE | + ADS1115_COMP_LATCH_NO | + ADS1115_COMP_POLARITY_ACTIVE_LOW | + ADS1115_COMP_MODE_WINDOW | + ADS1115_DATA_RATE_128_SPS | + ADS1115_MODE_SINGLE_SHOT | + ADS1115_MUX_GND_AIN0; + set_pga(ADS1115_PGA_ONE); +} + +uint8_t ADS1115::write_register(uint8_t reg, uint16_t val) +{ + Wire.beginTransmission(m_address); + Wire.write(reg); + Wire.write(val>>8); + Wire.write(val & 0xFF); + return Wire.endTransmission(); +} + +uint16_t ADS1115::read_register(uint8_t reg) +{ + Wire.beginTransmission(m_address); + Wire.write(reg); + Wire.endTransmission(); + + uint8_t result = Wire.requestFrom((int)m_address, 2, 1); + if (result != 2) { + return 0; + } + + uint16_t val; + + val = Wire.read() << 8; + val |= Wire.read(); + return val; +} + +void ADS1115::begin() +{ + Wire.begin(); +} + +uint8_t ADS1115::trigger_sample() +{ + return write_register(ADS1115_REGISTER_CONFIG, m_config | SAMPLE_BIT); +} + +uint8_t ADS1115::reset() +{ + Wire.beginTransmission(0); + Wire.write(0x6); + return Wire.endTransmission(); +} + +bool ADS1115::is_sample_in_progress() +{ + uint16_t val = read_register(ADS1115_REGISTER_CONFIG); + return (val & SAMPLE_BIT) == 0; +} + +int16_t ADS1115::read_sample() +{ + return read_register(ADS1115_REGISTER_CONVERSION); +} + +float ADS1115::sample_to_float(int16_t val) +{ + return val * ranges[m_voltage_range]; +} + +float ADS1115::read_sample_float() +{ + return sample_to_float(read_sample()); +} + +ADS1115 adc; + +void ADS1115_init(void) { + + ads1115_up=0; + if (!i2c_flg) return; + + adc.begin(); + adc.set_data_rate(ADS1115_DATA_RATE_128_SPS); + adc.set_mode(ADS1115_MODE_CONTINUOUS); + adc.set_mux(ADS1115_MUX_DIFF_AIN0_AIN3); + adc.set_pga(ADS1115_PGA_TWO); + + int16_t val = adc.read_sample(); + ads1115_up=1; +} + +#endif + +char sml_start; +uint8_t dump2log=0; + +#define SML_SAVAILABLE Serial_available() +#define SML_SREAD Serial_read() +#define SML_SPEAK Serial_peek() + +bool Serial_available() { + uint8_t num=dump2log&7; + if (num<1 || num>meters_used) num=1; + return meter_ss[num-1]->available(); +} + +uint8_t Serial_read() { + uint8_t num=dump2log&7; + if (num<1 || num>meters_used) num=1; + return meter_ss[num-1]->read(); +} + +uint8_t Serial_peek() { + uint8_t num=dump2log&7; + if (num<1 || num>meters_used) num=1; + return meter_ss[num-1]->peek(); +} + +uint8_t sml_logindex; + +void Dump2log(void) { + +int16_t index=0,hcnt=0; +uint32_t d_lastms; +uint8_t dchars[16]; + + //if (!SML_SAVAILABLE) return; + + if (dump2log&8) { + // combo mode + while (SML_SAVAILABLE) { + log_data[index]=':'; + index++; + log_data[index]=' '; + index++; + d_lastms=millis(); + while ((millis()-d_lastms)<40) { + if (SML_SAVAILABLE) { + uint8_t c=SML_SREAD; + sprintf(&log_data[index],"%02x ",c); + dchars[hcnt]=c; + index+=3; + hcnt++; + if (hcnt>15) { + // line complete, build asci chars + log_data[index]='='; + index++; + log_data[index]='>'; + index++; + log_data[index]=' '; + index++; + for (uint8_t ccnt=0; ccnt<16; ccnt++) { + if (isprint(dchars[ccnt])) { + log_data[index]=dchars[ccnt]; + } else { + log_data[index]=' '; + } + index++; + } + break; + } + } + } + if (index>0) { + log_data[index]=0; + AddLog(LOG_LEVEL_INFO); + index=0; + hcnt=0; + } + } + } else { + if (meter_desc_p[(dump2log&7)-1].type=='o') { + // obis + while (SML_SAVAILABLE) { + char c=SML_SREAD&0x7f; + if (c=='\n' || c=='\r') { + log_data[sml_logindex]=0; + AddLog(LOG_LEVEL_INFO); + sml_logindex=2; + log_data[0]=':'; + log_data[1]=' '; + break; + } + log_data[sml_logindex]=c; + if (sml_logindex2) { + log_data[index]=0; + AddLog(LOG_LEVEL_INFO); + } + } + } +} + +// skip sml entries +uint8_t *skip_sml(uint8_t *cp,int16_t *res) { + uint8_t len,len1,type; + len=*cp&0xf; + type=*cp&0x70; + if (type==0x70) { + // list, skip entries + // list + cp++; + while (len--) { + len1=*cp&0x0f; + cp+=len1; + } + *res=0; + } else { + // skip len + *res=(signed char)*(cp+1); + cp+=len; + } + return cp; +} + +// get sml binary value +// not defined for unsigned >0x7fff ffff ffff ffff (should never happen) +double sml_getvalue(unsigned char *cp,uint8_t index) { +uint8_t len,unit,type; +int16_t scaler,result; +int64_t value; +double dval; + + // scan for values + // check status + cp=skip_sml(cp,&result); + // check time + cp=skip_sml(cp,&result); + // check unit + cp=skip_sml(cp,&result); + // check scaler + cp=skip_sml(cp,&result); + scaler=result; + // get value + type=*cp&0x70; + len=*cp&0x0f; + cp++; + if (type==0x50 || type==0x60) { + // shift into 64 bit + uint64_t uvalue=0; + uint8_t nlen=len; + while (--nlen) { + uvalue<<=8; + uvalue|=*cp++; + } + if (type==0x50) { + // signed + switch (len-1) { + case 1: + // byte + value=(signed char)uvalue; + break; + case 2: + // signed 16 bit +#ifdef DWS74_BUG + if (scaler==-2) { + value=(uint32_t)uvalue; + } else { + value=(int16_t)uvalue; + } +#else + value=(int16_t)uvalue; +#endif + break; + case 3: + case 4: + // signed 32 bit + value=(int32_t)uvalue; + break; + case 5: + case 6: + case 7: + case 8: + // signed 64 bit + value=(int64_t)uvalue; + break; + } + } else { + // unsigned + value=uvalue; + } + + } else { + if (!(type&0xf0)) { + // octet string serial number + // no coding found on the net + // up to now 2 types identified on Hager + if (len==9) { + // serial number on hager => 24 bit - 24 bit + cp++; + uint32_t s1,s2; + s1=*cp<<16|*(cp+1)<<8|*(cp+2); + cp+=4; + s2=*cp<<16|*(cp+1)<<8|*(cp+2); + sprintf(&meter_id[index][0],"%u-%u",s1,s2); + } else { + // server id on hager + char *str=&meter_id[index][0]; + for (type=0; type= 'A' && chr <= 'F') rVal = chr + 10 - 'A'; + } + return rVal; +} + +uint8_t sb_counter; + +// need double precision in this driver +double CharToDouble(const char *str) +{ + // simple ascii to double, because atof or strtod are too large + char strbuf[24]; + + strlcpy(strbuf, str, sizeof(strbuf)); + char *pt = strbuf; + while ((*pt != '\0') && isblank(*pt)) { pt++; } // Trim leading spaces + + signed char sign = 1; + if (*pt == '-') { sign = -1; } + if (*pt == '-' || *pt=='+') { pt++; } // Skip any sign + + double left = 0; + if (*pt != '.') { + left = atoi(pt); // Get left part + while (isdigit(*pt)) { pt++; } // Skip number + } + + double right = 0; + if (*pt == '.') { + pt++; + right = atoi(pt); // Decimal part + while (isdigit(*pt)) { + pt++; + right /= 10.0; + } + } + + double result = left + right; + if (sign < 0) { + return -result; // Add negative sign + } + return result; +} + + +// remove ebus escapes +void ebus_esc(uint8_t *ebus_buffer, unsigned char len) { + short count,count1; + for (count=0; countavailable()) { + meter_ss[meters]->read(); + } +} + + +void sml_shift_in(uint32_t meters,uint32_t shard) { + uint32_t count; + if (meter_desc_p[meters].type!='e' && meter_desc_p[meters].type!='m' && meter_desc_p[meters].type!='p') { + // shift in + for (count=0; countread(); + + if (meter_desc_p[meters].type=='o') { + smltbuf[meters][SML_BSIZ-1]=iob&0x7f; + } else if (meter_desc_p[meters].type=='s') { + smltbuf[meters][SML_BSIZ-1]=iob; + } else if (meter_desc_p[meters].type=='r') { + smltbuf[meters][SML_BSIZ-1]=iob; + } else if (meter_desc_p[meters].type=='m') { + smltbuf[meters][meter_spos[meters]] = iob; + meter_spos[meters]++; + if (meter_spos[meters]>=9) { + SML_Decode(meters); + sml_empty_receiver(meters); + meter_spos[meters]=0; + } + } else if (meter_desc_p[meters].type=='p') { + smltbuf[meters][meter_spos[meters]] = iob; + meter_spos[meters]++; + if (meter_spos[meters]>=7) { + SML_Decode(meters); + sml_empty_receiver(meters); + meter_spos[meters]=0; + } + } else { + if (iob==EBUS_SYNC) { + // should be end of telegramm + // QQ,ZZ,PB,SB,NN ..... CRC, ACK SYNC + if (meter_spos[meters]>4+5) { + // get telegramm lenght + uint8_t tlen=smltbuf[meters][4]+5; + // test crc + if (smltbuf[meters][tlen]=ebus_CalculateCRC(smltbuf[meters],tlen)) { + ebus_esc(smltbuf[meters],tlen); + SML_Decode(meters); + } else { + // crc error + //AddLog_P(LOG_LEVEL_INFO, PSTR("ebus crc error")); + } + } + meter_spos[meters]=0; + return; + } + smltbuf[meters][meter_spos[meters]] = iob; + meter_spos[meters]++; + if (meter_spos[meters]>=SML_BSIZ) { + meter_spos[meters]=0; + } + } + sb_counter++; + if (meter_desc_p[meters].type!='e' && meter_desc_p[meters].type!='m' && meter_desc_p[meters].type!='p') SML_Decode(meters); +} + + +// polled every 50 ms +void SML_Poll(void) { +uint32_t meters; + + for (meters=0; metersavailable()) { + sml_shift_in(meters,0); + } + } + } +} + + +void SML_Decode(uint8_t index) { + const char *mp=(const char*)meter_p; + int8_t mindex; + uint8_t *cp; + uint8_t dindex=0,vindex=0; + delay(0); + while (mp != NULL) { + // check list of defines + + // new section + mindex=((*mp)&7)-1; + + if (mindex<0 || mindex>=meters_used) mindex=0; + mp+=2; + if (*mp=='=' && *(mp+1)=='h') { + mp = strchr(mp, '|'); + if (mp) mp++; + continue; + } + + if (index!=mindex) goto nextsect; + + // start of serial source buffer + cp=&smltbuf[mindex][0]; + + // compare + if (*mp=='=') { + // calculated entry, check syntax + mp++; + // do math m 1+2+3 + if (*mp=='m' && !sb_counter) { + // only every 256 th byte + // else it would be calculated every single serial byte + mp++; + while (*mp==' ') mp++; + // 1. index + double dvar; + uint8_t opr; + uint32_t ind; + ind=atoi(mp); + while (*mp>='0' && *mp<='9') mp++; + if (ind<1 || ind>MAX_VARS) ind=1; + dvar=meter_vars[ind-1]; + for (uint8_t p=0;p<5;p++) { + if (*mp=='@') { + // store result + meter_vars[vindex]=dvar; + mp++; + SML_Immediate_MQTT((const char*)mp,vindex,mindex); + break; + } + opr=*mp; + mp++; + uint8_t iflg=0; + if (*mp=='#') { + iflg=1; + mp++; + } + ind=atoi(mp); + while (*mp>='0' && *mp<='9') mp++; + if (ind<1 || ind>MAX_VARS) ind=1; + switch (opr) { + case '+': + if (iflg) dvar+=ind; + else dvar+=meter_vars[ind-1]; + break; + case '-': + if (iflg) dvar-=ind; + else dvar-=meter_vars[ind-1]; + break; + case '*': + if (iflg) dvar*=ind; + else dvar*=meter_vars[ind-1]; + break; + case '/': + if (iflg) dvar/=ind; + else dvar/=meter_vars[ind-1]; + break; + } + while (*mp==' ') mp++; + if (*mp=='@') { + // store result + meter_vars[vindex]=dvar; + mp++; + SML_Immediate_MQTT((const char*)mp,vindex,mindex); + break; + } + } + } else if (*mp=='d') { + // calc deltas d ind 10 (eg every 10 secs) + if (dindex='0' && *mp<='9') mp++; + if (ind<1 || ind>MAX_VARS) ind=1; + uint32_t delay=atoi(mp)*1000; + uint32_t dtime=millis()-dtimes[dindex]; + if (dtime>delay) { + // calc difference + dtimes[dindex]=millis(); + double vdiff = meter_vars[ind-1]-dvalues[dindex]; + dvalues[dindex]=meter_vars[ind-1]; + meter_vars[vindex]=(double)360000.0*vdiff/((double)dtime/10000.0); + + mp=strchr(mp,'@'); + if (mp) { + mp++; + SML_Immediate_MQTT((const char*)mp,vindex,mindex); + } + } + dindex++; + } + } else if (*mp=='h') { + // skip html tag line + mp = strchr(mp, '|'); + if (mp) mp++; + continue; + } + } else { + // compare value + uint8_t found=1; + uint32_t ebus_dval=99; + float mbus_dval=99; + while (*mp!='@') { + if (meter_desc_p[mindex].type=='o' || meter_desc_p[mindex].type=='c') { + if (*mp++!=*cp++) { + found=0; + } + } else { + if (meter_desc_p[mindex].type=='s') { + // sml + uint8_t val = hexnibble(*mp++) << 4; + val |= hexnibble(*mp++); + if (val!=*cp++) { + found=0; + } + } else { + // ebus mbus pzem or raw + // XXHHHHSSUU + if (*mp=='x' && *(mp+1)=='x') { + //ignore + mp+=2; + cp++; + } else if (!strncmp(mp,"uuuuuuuu",8)) { + uint32_t val= (cp[0]<<24)|(cp[1]<<16)|(cp[2]<<8)|(cp[3]<<0); + ebus_dval=val; + mbus_dval=val; + mp+=8; + cp+=4; + } + else if (*mp=='u' && *(mp+1)=='u' && *(mp+2)=='u' && *(mp+3)=='u'){ + uint16_t val = cp[0]|(cp[1]<<8); + mbus_dval=val; + ebus_dval=val; + mp+=4; + cp+=2; + } else if (*mp=='u' && *(mp+1)=='u') { + uint8_t val = *cp++; + ebus_dval=val; + mp+=2; + } + else if (*mp=='s' && *(mp+1)=='s' && *(mp+2)=='s' && *(mp+3)=='s') { + int16_t val = *cp|(*(cp+1)<<8); + ebus_dval=val; + mp+=4; + cp+=2; + } + else if (*mp=='s' && *(mp+1)=='s') { + int8_t val = *cp++; + ebus_dval=val; + mp+=2; + } + else if (!strncmp(mp,"ffffffff",8)) { + uint32_t val= (cp[0]<<24)|(cp[1]<<16)|(cp[2]<<8)|(cp[3]<<0); + float *fp=(float*)&val; + ebus_dval=*fp; + mbus_dval=*fp; + mp+=8; + cp+=4; + } + else if (!strncmp(mp,"FFffFFff",8)) { + // reverse word float + uint32_t val= (cp[1]<<0)|(cp[0]<<8)|(cp[3]<<16)|(cp[2]<<24); + float *fp=(float*)&val; + ebus_dval=*fp; + mbus_dval=*fp; + mp+=8; + cp+=4; + } + else if (!strncmp(mp,"eeeeee",6)) { + uint32_t val=(cp[0]<<16)|(cp[1]<<8)|(cp[2]<<0); + mbus_dval=val; + mp+=6; + cp+=3; + } + else if (!strncmp(mp,"vvvvvv",6)) { + mbus_dval=(float)((cp[0]<<8)|(cp[1])) + ((float)cp[2]/10.0); + mp+=6; + cp+=3; + } + else if (!strncmp(mp,"cccccc",6)) { + mbus_dval=(float)((cp[0]<<8)|(cp[1])) + ((float)cp[2]/100.0); + mp+=6; + cp+=3; + } + else if (!strncmp(mp,"pppp",4)) { + mbus_dval=(float)((cp[0]<<8)|cp[1]); + mp+=4; + cp+=2; + } + else { + uint8_t val = hexnibble(*mp++) << 4; + val |= hexnibble(*mp++); + if (val!=*cp++) { + found=0; + } + } + } + } + } + if (found) { + // matches, get value + mp++; + if (*mp=='#') { + // get string value + mp++; + if (meter_desc_p[mindex].type=='o') { + for (uint8_t p=0;p>=shift; + ebus_dval&=1; + mp+=2; + } + if (*mp=='i') { + // mbus index + mp++; + uint8_t mb_index=strtol((char*)mp,(char**)&mp,10); + if (mb_index!=meter_desc_p[mindex].index) { + goto nextsect; + } + uint16_t crc = MBUS_calculateCRC(&smltbuf[mindex][0],7); + if (lowByte(crc)!=smltbuf[mindex][7]) goto nextsect; + if (highByte(crc)!=smltbuf[mindex][8]) goto nextsect; + dval=mbus_dval; + //AddLog_P2(LOG_LEVEL_INFO, PSTR(">> %s"),mp); + mp++; + } else { + if (meter_desc_p[mindex].type=='p') { + uint8_t crc = SML_PzemCrc(&smltbuf[mindex][0],6); + if (crc!=smltbuf[mindex][6]) goto nextsect; + dval=mbus_dval; + } else { + dval=ebus_dval; + } + } + + } +#ifdef USE_SML_MEDIAN_FILTER + if (meter_desc_p[mindex].flag&16) { + meter_vars[vindex]=sml_median(&sml_mf[vindex],dval); + } else { + meter_vars[vindex]=dval; + } +#else + meter_vars[vindex]=dval; +#endif +//AddLog_P2(LOG_LEVEL_INFO, PSTR(">> %s"),mp); + // get scaling factor + double fac=CharToDouble((char*)mp); + meter_vars[vindex]/=fac; + SML_Immediate_MQTT((const char*)mp,vindex,mindex); + } + } + } +nextsect: + // next section + if (vindex=meters_used) lastmind=0; + while (mp != NULL) { + // setup sections + mindex=((*mp)&7)-1; + if (mindex<0 || mindex>=meters_used) mindex=0; + mp+=2; + if (*mp=='=' && *(mp+1)=='h') { + mp+=2; + // html tag + if (json) { + mp = strchr(mp, '|'); + if (mp) mp++; + continue; + } + // web ui export + uint8_t i; + for (i=0;isml_counters[index].sml_debounce) { + RtcSettings.pulse_counter[index]++; + InjektCounterValue(sml_counters[index].sml_cnt_old_state,RtcSettings.pulse_counter[index]); + } + } else { + // rising edge + sml_counters[index].sml_counter_ltime=millis(); + } +} + +void SML_CounterUpd1(void) { + SML_CounterUpd(0); +} + +void SML_CounterUpd2(void) { + SML_CounterUpd(1); +} + +void SML_CounterUpd3(void) { + SML_CounterUpd(2); +} + +void SML_CounterUpd4(void) { + SML_CounterUpd(3); +} + +#ifdef USE_SCRIPT +struct METER_DESC script_meter_desc[MAX_METERS]; +uint8_t *script_meter; +#endif + +#define METER_DEF_SIZE 2000 + +bool Gpio_used(uint8_t gpiopin) { + for (uint16_t i=0;iM",-2,0); + if (meter_script==99) { + // use script definition + if (script_meter) free(script_meter); + script_meter=0; + uint8_t *tp=0; + uint16_t index=0; + uint8_t section=0; + uint8_t srcpin=0; + char *lp=glob_script_mem.scriptptr; + sml_send_blocks=0; + while (lp) { + if (!section) { + if (*lp=='>' && *(lp+1)=='M') { + lp+=2; + meters_used=strtol(lp,0,10); + section=1; + uint32_t mlen=0; + for (uint32_t cnt=0;cnt') { + if (*(tp-1)=='|') *(tp-1)=0; + break; + } + if (*lp=='+') { + // add descriptor +1,1,c,0,10,H20 + //toLogEOL(">>",lp); + lp++; + index=*lp&7; + lp+=2; + if (index<1 || index>meters_used) goto next_line; + index--; + srcpin=strtol(lp,&lp,10); + if (Gpio_used(srcpin)) { + AddLog_P(LOG_LEVEL_INFO, PSTR("gpio rx double define!")); +dddef_exit: + if (script_meter) free(script_meter); + script_meter=0; + meters_used=METERS_USED; + goto init10; + } + script_meter_desc[index].srcpin=srcpin; + if (*lp!=',') goto next_line; + lp++; + script_meter_desc[index].type=*lp; + lp+=2; + script_meter_desc[index].flag=strtol(lp,&lp,10); + if (*lp!=',') goto next_line; + lp++; + script_meter_desc[index].params=strtol(lp,&lp,10); + if (*lp!=',') goto next_line; + lp++; + script_meter_desc[index].prefix[7]=0; + for (uint32_t cnt=0; cnt<8; cnt++) { + if (*lp==SCRIPT_EOL || *lp==',') { + script_meter_desc[index].prefix[cnt]=0; + break; + } + script_meter_desc[index].prefix[cnt]=*lp++; + } + if (*lp==',') { + lp++; + script_meter_desc[index].trxpin=strtol(lp,&lp,10); + if (Gpio_used(script_meter_desc[index].trxpin)) { + AddLog_P(LOG_LEVEL_INFO, PSTR("gpio tx double define!")); + goto dddef_exit; + } + if (*lp!=',') goto next_line; + lp++; + script_meter_desc[index].tsecs=strtol(lp,&lp,10); + if (*lp==',') { + lp++; + char txbuff[256]; + uint32_t txlen=0,tx_entries=1; + for (uint32_t cnt=0; cnt>",lp); + // add meters line -1,1-0:1.8.0*255(@10000,H2OIN,cbm,COUNTER,4| + if (*lp=='-') lp++; + uint8_t mnum=strtol(lp,0,10); + if (mnum<1 || mnum>meters_used) goto next_line; + while (1) { + if (*lp==SCRIPT_EOL) { + if (*(tp-1)!='|') *tp++='|'; + goto next_line; + } + *tp++=*lp++; + index++; + if (index>=METER_DEF_SIZE) break; + } + } + + } + +next_line: + if (*lp==SCRIPT_EOL) { + lp++; + } else { + lp = strchr(lp, SCRIPT_EOL); + if (!lp) break; + lp++; + } + } + *tp=0; + meter_desc_p=script_meter_desc; + meter_p=script_meter; + } +#endif + +init10: + typedef void (*function)(); + function counter_callbacks[] = {SML_CounterUpd1,SML_CounterUpd2,SML_CounterUpd3,SML_CounterUpd4}; + uint8_t cindex=0; + // preloud counters + for (byte i = 0; i < MAX_COUNTERS; i++) { + RtcSettings.pulse_counter[i]=Settings.pulse_counter[i]; + sml_counters[i].sml_cnt_last_ts=millis(); + } + for (uint8_t meters=0; metersbegin(meter_desc_p[meters].params)) { + meter_ss[meters]->flush(); + } + if (meter_ss[meters]->hardwareSerial()) { ClaimSerial(); } + + } + } + +} + + +void SetDBGLed(uint8_t srcpin, uint8_t ledpin) { + pinMode(ledpin, OUTPUT); + if (digitalRead(srcpin)) { + digitalWrite(ledpin,LOW); + } else { + digitalWrite(ledpin,HIGH); + } +} + +// fast counter polling +void SML_Counter_Poll(void) { +uint16_t meters,cindex=0; +uint32_t ctime=millis(); + + for (meters=0; meters0) { + if (ctime-sml_counters[cindex].sml_cnt_last_ts>meter_desc_p[meters].params) { + sml_counters[cindex].sml_cnt_last_ts=ctime; + + if (meter_desc_p[meters].flag&2) { + // analog mode, get next value +#ifdef ANALOG_OPTO_SENSOR + if (ads1115_up) { + int16_t val = adc.read_sample(); + if (val>sml_counters[cindex].ana_max) sml_counters[cindex].ana_max=val; + if (val10) { + sml_counters[cindex].sml_cnt_last_ts=ctime; +#ifdef DEBUG_CNT_LED1 + if (cindex==0) SetDBGLed(meter_desc_p[meters].srcpin,DEBUG_CNT_LED1); +#endif +#ifdef DEBUG_CNT_LED2 + if (cindex==1) SetDBGLed(meter_desc_p[meters].srcpin,DEBUG_CNT_LED2); +#endif + } + } + cindex++; + } + } +} + +#ifdef USE_SCRIPT +char *SML_Get_Sequence(char *cp,uint32_t index) { + if (!index) return cp; + uint32_t cindex=0; + while (cp) { + cp=strchr(cp,','); + if (cp) { + cp++; + cindex++; + if (cindex==index) { + return cp; + } + } + } +} + +void SML_Check_Send(void) { + sml_100ms_cnt++; + char *cp; + for (uint32_t cnt=sml_desc_cnt; cnt=0 && script_meter_desc[cnt].txmem) { + if ((sml_100ms_cnt%script_meter_desc[cnt].tsecs)==0) { + if (script_meter_desc[cnt].max_index>1) { + script_meter_desc[cnt].index++; + if (script_meter_desc[cnt].index>=script_meter_desc[cnt].max_index) { + script_meter_desc[cnt].index=0; + sml_desc_cnt++; + } + cp=SML_Get_Sequence(script_meter_desc[cnt].txmem,script_meter_desc[cnt].index); + //SML_Send_Seq(cnt,cp); + } else { + cp=script_meter_desc[cnt].txmem; + //SML_Send_Seq(cnt,cp); + sml_desc_cnt++; + } + //AddLog_P2(LOG_LEVEL_INFO, PSTR(">> %s"),cp); + SML_Send_Seq(cnt,cp); + if (sml_desc_cnt>=meters_used) { + sml_desc_cnt=0; + } + break; + } + } else { + sml_desc_cnt++; + } + + if (sml_desc_cnt>=meters_used) { + sml_desc_cnt=0; + } + } +} + +uint8_t sml_hexnibble(char chr) { + uint8_t rVal = 0; + if (isdigit(chr)) { + rVal = chr - '0'; + } else { + if (chr >= 'A' && chr <= 'F') rVal = chr + 10 - 'A'; + if (chr >= 'a' && chr <= 'f') rVal = chr + 10 - 'a'; + } + return rVal; +} + +// send sequence every N Seconds +void SML_Send_Seq(uint32_t meter,char *seq) { + uint8_t sbuff[32]; + uint8_t *ucp=sbuff,slen=0; + char *cp=seq; + while (*cp) { + if (!*cp || !*(cp+1)) break; + if (*cp==',') break; + uint8_t iob=(sml_hexnibble(*cp) << 4) | sml_hexnibble(*(cp+1)); + cp+=2; + *ucp++=iob; + slen++; + if (slen>=sizeof(sbuff)) break; + } + if (script_meter_desc[meter].type=='m') { + *ucp++=0; + *ucp++=2; + // append crc + uint16_t crc = MBUS_calculateCRC(sbuff,6); + *ucp++=lowByte(crc); + *ucp++=highByte(crc); + slen+=4; + } + if (script_meter_desc[meter].type=='o') { + for (uint32_t cnt=0;cntwrite(sbuff,slen); +} +#endif // USE_SCRIPT + +uint16_t MBUS_calculateCRC(uint8_t *frame, uint8_t num) { + uint16_t crc, flag; + crc = 0xFFFF; + for (uint32_t i = 0; i < num; i++) { + crc ^= frame[i]; + for (uint32_t j = 8; j; j--) { + if ((crc & 0x0001) != 0) { // If the LSB is set + crc >>= 1; // Shift right and XOR 0xA001 + crc ^= 0xA001; + } else { // Else LSB is not set + crc >>= 1; // Just shift right + } + } + } + return crc; +} + +uint8_t SML_PzemCrc(uint8_t *data, uint8_t len) { + uint16_t crc = 0; + for (uint32_t i = 0; i < len; i++) crc += *data++; + return (uint8_t)(crc & 0xFF); +} + +// for odd parity init with 1 +uint8_t CalcEvenParity(uint8_t data) { +uint8_t parity=0; + + while(data) { + parity^=(data &1); + data>>=1; + } + return parity; +} + + + +// dump to log shows serial data on console +// has to be off for normal use +// in console sensor53 d1,d2,d3 .. or. d0 for normal use +// set counter => sensor53 c1 xxxx +// restart driver => sensor53 r + +bool XSNS_53_cmd(void) { + bool serviced = true; + if (XdrvMailbox.data_len > 0) { + char *cp=XdrvMailbox.data; + if (*cp=='d') { + // set dump mode + cp++; + uint8_t index=atoi(cp); + if ((index&7)>meters_used) index=1; + if (index>0 && meter_desc_p[(index&7)-1].type=='c') { + index=0; + } + dump2log=index; + ResponseTime_P(PSTR(",\"SML\":{\"CMD\":\"dump: %d\"}}"),dump2log); + } else if (*cp=='c') { + // set ounter + cp++; + uint8_t index=*cp&7; + if (index<1 || index>MAX_COUNTERS) index=1; + cp++; + while (*cp==' ') cp++; + if (isdigit(*cp)) { + uint32_t cval=atoi(cp); + while (isdigit(*cp)) cp++; + RtcSettings.pulse_counter[index-1]=cval; + uint8_t cindex=0; + for (uint8_t meters=0; meters. +*/ + +#ifdef USE_I2C +#ifdef USE_INA226 +/* +* Setup a single INA226 device at address 0x40: +* +* 1. Select a module type with free I2C pins. +* 2. Configure the module to use I2C on the correct pins. +* 3. Connect your ina226 module(s) to the I2C pins. +* 4. Use the i2cscan console command to probe the modules and check they are present. +* 5. Enable the first device at I2C slave address 0x40 using the following console commands: +* a. Sensor54 11 [shunt resistance in ohms] e.g. Sensor54 11 0.1 +* b. Sensor54 12 [full scale current in amperes] e.g. Sensor54 12 3.0 +* c. Sensor54 2 saves the settings and restarts Tasmota. The device should show up after the system boots again. +* +* Device number to I2C slave address mapping +* +* 1 - 0x40 +* 2 - 0x41 +* 3 - 0x44 +* 4 - 0x45 +* +* This driver will not probe I2C bus for INA226 devices unless the full scale current is set for a device number. +* It will map device numbers as follows: +* +* To set shunt resistance and full scale current, use the Sensor54 command interface as follows: +* +* Sensor54 10 Return channel 1 shunt resistance and full scale current +* Sensor54 11 [shunt_resistance] Set INA226 channel 1 shunt resistance in ohms, floating point +* Sensor54 12 [full_scale_current] Set INA226 channel 1 full scale current in amperes, floating point +* Sensor54 20 Return channel 2 shunt resistance and full scale current +* Sensor54 21 [shunt_resistance] Set INA226 channel 2 shunt resistance in ohms, floating point +* Sensor54 22 [full_scale_current] Set INA226 channel 2 full scale current in amperes, floating point +* Sensor54 30 Return channel 3 shunt resistance and full scale current +* Sensor54 31 [shunt_resistance] Set INA226 channel 3 shunt resistance in ohms, floating point +* Sensor54 32 [full_scale_current] Set INA226 channel 3 full scale current in amperes, floating point +* Sensor54 40 Return channel 4 shunt resistance and full scale current +* Sensor54 41 [shunt_resistance] Set INA226 channel 4 shunt resistance in ohms, floating point +* Sensor54 42 [full_scale_current] Set INA226 channel 4 full scale current in amperes, floating point +* +* Other commands +* +* Sensor54 1 Rescan for devices and return the number of slaves found. +* Sensor54 2 Save the configuration and restart +* +* +*/ + +// Define driver ID + +#define XSNS_54 54 + +#define INA226_MAX_ADDRESSES 4 +#define INA226_ADDRESS1 (0x40) // 1000000 (A0+A1=GND) +#define INA226_ADDRESS2 (0x41) // 1000000 (A0=Vcc, A1=GND) +#define INA226_ADDRESS3 (0x44) // 1000000 (A0=GND, A1=Vcc) +#define INA226_ADDRESS4 (0x45) // 1000000 (A0+A1=Vcc) + +#define INA226_REG_CONFIG (0x00) // Config register +#define INA226_RES_CONFIG (0x4127) // Config register at reset +#define INA226_DEF_CONFIG (0x42FF) // Our default configuration +#define INA226_CONFIG_RESET (0x8000) // Config register reset bit + +#define INA226_REG_SHUNTVOLTAGE (0x01) +#define INA226_REG_BUSVOLTAGE (0x02) +#define INA226_REG_POWER (0x03) +#define INA226_REG_CURRENT (0x04) +#define INA226_REG_CALIBRATION (0x05) + + +typedef struct Ina226SlaveInfo_tag { + uint8_t address; + uint16_t calibrationValue; + uint16_t config; + uint8_t present : 1; + float i_lsb; +} Ina226SlaveInfo_t; + +/* +* Program memory constants +*/ + +static const uint8_t PROGMEM probeAddresses[INA226_MAX_ADDRESSES] = {INA226_ADDRESS1, INA226_ADDRESS2, INA226_ADDRESS3, INA226_ADDRESS4}; + +/* +* Global Variables +*/ + + +static char Ina226Str[] = "INA226"; +static uint8_t slavesFound = 0; +static uint8_t schedule_reinit = 0; +static Ina226SlaveInfo_t slaveInfo[4] = {0}; +//static uint16_t reinit_count[4]; +static float voltages[4]; +static float currents[4]; +static float powers[4]; + + +/* +* Log single floating point Number +*/ + +static void _debug_fval(const char *str, float fval, uint8_t prec = 4 ) +{ + char fstr[32]; + dtostrfd(fval, prec, fstr); + AddLog_P2( LOG_LEVEL_NONE, PSTR("%s: %s"), str, fstr ); +} + + +/* +* Convert 16 bit repesentation of shunt resisance to 32 bit micro ohms by looking at the msb range bit. +* If the msb is 1, the LSB's define the number of milli ohms. (Maximum shunt resistor value 32.767 ohms) +* If the msb is 0, the LSB's define the number of micro ohms. (Maximum shunt resistor value 0.032767 ohms) +*/ + +static uint32_t _expand_r_shunt(uint16_t compact_r_shunt) +{ + + uint32_t r_shunt_uohms = (compact_r_shunt & 0x8000) ? + (((uint32_t)(compact_r_shunt & 0x7FFF)) * 1000ul) : + (compact_r_shunt & 0x7FFF); + return r_shunt_uohms; +} + +/* +* Set calibration value for Ina226 +*/ + +void Ina226SetCalibration(uint8_t slaveIndex) +{ + +Ina226SlaveInfo_t *si = slaveInfo + slaveIndex; + +I2cWrite16( si->address, INA226_REG_CALIBRATION, si->calibrationValue); + +} + + +/* +* Test for presence of an Ina226 +*/ + +bool Ina226TestPresence(uint8_t device) +{ + + // Read config + + uint16_t config = I2cRead16( slaveInfo[device].address, INA226_REG_CONFIG ); + //AddLog_P2( LOG_LEVEL_NONE, PSTR("Config register %04x" ), config); + + if (config != slaveInfo[device].config) + return false; + + return true; + +} + + +/* +* Initialize INA226 devices +*/ + +void Ina226Init() +{ + + + uint32_t i; + + slavesFound = 0; + + Ina226SlaveInfo_t *p = slaveInfo; + + + //AddLog_P2( LOG_LEVEL_NONE, "Ina226Init"); + AddLog_P2( LOG_LEVEL_NONE, "Size of Settings: %d bytes", sizeof(Settings)); + + if (!i2c_flg) + AddLog_P2(LOG_LEVEL_DEBUG, "INA226: Initialization failed: No I2C support"); + + + // Clear slave info data + + for (i = 0; i < 4; i++){ + *p = {0}; + } + + //AddLog_P2( LOG_LEVEL_NONE, PSTR("Sizeof Ina226Cfg: %d" ), sizeof(Ina226Cfg)); + + // Detect devices + + for (i = 0; (i < INA226_MAX_ADDRESSES); i++){ + uint8_t addr = pgm_read_byte(probeAddresses + i); + + // Skip device probing if the full scale current is zero + + //AddLog_P2( LOG_LEVEL_NONE, "fs_i[%d]: %d", i, Settings.ina226_i_fs[i]); + if (!Settings.ina226_i_fs[i]) + continue; + + + //AddLog_P2( LOG_LEVEL_NONE, PSTR("INA226 trying slave address %02x" ), addr ); + + // Try Resetting the device + + if (!I2cWrite16( addr, INA226_REG_CONFIG, INA226_CONFIG_RESET)){ + + AddLog_P2( LOG_LEVEL_DEBUG, "No INA226 at address: %02X", addr); + continue; // No device + } + + // Read config + + uint16_t config = I2cRead16( addr, INA226_REG_CONFIG ); + //AddLog_P2( LOG_LEVEL_NONE, PSTR("INA226 Config register %04x" ), config); + + if (INA226_RES_CONFIG != config) + continue; + + + config = INA226_DEF_CONFIG; // Fixme + + // Set the default configuration + if (!I2cWrite16( addr, INA226_REG_CONFIG, config)) + continue; // No device + + // store data in slave info struct. + + p = &slaveInfo[i]; + // Address + p->address = addr; + // Configuration + p->config = config; + // Full scale current in tenths of an amp + //AddLog_P2( LOG_LEVEL_NONE, "Full Scale I in tenths of an amp: %u", Settings.ina226_i_fs[i]); + p->i_lsb = (((float) Settings.ina226_i_fs[i])/10.0f)/32768.0f; + //_debug_fval("i_lsb: %s", p->i_lsb, 7); + + // Get shunt resistor value in micro ohms + uint32_t r_shunt_uohms = _expand_r_shunt(Settings.ina226_r_shunt[i]); + //AddLog_P2( LOG_LEVEL_NONE, "Shunt R in micro-ohms: %u", r_shunt_uohms); + + + p->calibrationValue = ((uint16_t) (0.00512/(p->i_lsb * r_shunt_uohms/1000000.0f))); + // Device present + p->present = true; + //AddLog_P2( LOG_LEVEL_NONE, "INA226 Device %d calibration value: %04X", i, p->calibrationValue); + + Ina226SetCalibration(i); + + //AddLog_P2( LOG_LEVEL_NONE, S_LOG_I2C_FOUND_AT, Ina226Str, addr ); + slavesFound++; + + } +} + +/* +* Read the bus voltage, and return it as a float +*/ + +float Ina226ReadBus_v(uint8_t device) +{ + uint8_t addr = slaveInfo[device].address; + int16_t reg_bus_v = I2cReadS16( addr, INA226_REG_BUSVOLTAGE); + + float result = ((float) reg_bus_v) * 0.00125f; + + return result; + +} + +/* +* Read the shunt current, and return it as a float +*/ + +float Ina226ReadShunt_i(uint8_t device) +{ + uint8_t addr = slaveInfo[device].address; + int16_t reg_shunt_i = I2cReadS16( addr, INA226_REG_CURRENT); + + float result = ((float) reg_shunt_i) * slaveInfo[device].i_lsb; + + return result; +} + +/* +* Read the calculated power +*/ + +float Ina226ReadPower_w(uint8_t device) +{ + uint8_t addr = slaveInfo[device].address; + int16_t reg_shunt_i = I2cReadS16( addr, INA226_REG_POWER); + + float result = ((float) reg_shunt_i) * (slaveInfo[device].i_lsb * 25.0); + + return result; +} + + +/* +* Read voltage, shunt voltage, current, and power registerd for a given device +*/ + +void Ina226Read(uint8_t device) +{ + //AddLog_P2( LOG_LEVEL_NONE, "Ina226Read"); + voltages[device] = Ina226ReadBus_v(device); + currents[device] = Ina226ReadShunt_i(device); + powers[device] = Ina226ReadPower_w(device); + //AddLog_P2( LOG_LEVEL_NONE, "INA226 Device %d", device ); + //_debug_fval("Voltage", voltages[device]); + //_debug_fval("Current", currents[device]); + //_debug_fval("Power", powers[device]); +} + +/* +* Poll sensors, and chack for sensor presence +*/ + +void Ina226EverySecond() +{ + //AddLog_P2( LOG_LEVEL_NONE, "Ina226EverySecond"); + for (uint8_t device = 0; device < INA226_MAX_ADDRESSES; device++){ + // If there are slaves, and the device was present, and the device still is present, read its registers + if (slavesFound && slaveInfo[device].present && Ina226TestPresence(device)){ + Ina226Read(device); + } + else { + powers[device] = currents[device] = voltages[device] = 0.0f; + // If device was present, note that it dropped off here + //if(slaveInfo[device].present){ + //reinit_count[device]++; + //AddLog_P2( LOG_LEVEL_DEBUG, "INA226 Device %d dropped off, count: %d", device, reinit_count[device]); + //} + // Device no longer present + slaveInfo[device].present = false; + } + } +} + +/* +* Decode a sensor command and act on it +*/ + +bool Ina226CommandSensor() +{ + bool serviced = true; + bool show_config = false; + char param_str[64]; + char *cp, *params[4]; + uint8_t i, param_count, device, p1 = XdrvMailbox.payload; + uint32_t r_shunt_uohms; + uint16_t compact_r_shunt_uohms; + //AddLog_P2( LOG_LEVEL_NONE, "Command received: %d", XdrvMailbox.payload); + //AddLog_P2( LOG_LEVEL_NONE, "Command data received: %s", XdrvMailbox.data); + + // Make a copy of the data and add another terminator + + if (XdrvMailbox.data_len > 62){ + return false; + } + + strncpy(param_str, XdrvMailbox.data, XdrvMailbox.data_len + 1); + param_str[XdrvMailbox.data_len] = 0; + + // Build parameter substrings (this should really be a helper function in support_command.ino) + for (cp = param_str, i = 0, param_count = 0; *cp && (i < XdrvMailbox.data_len + 1) && (param_count <= 3); i++) + if (param_str[i] == ' ' || param_str[i] == ',' || param_str[i] == 0){ + param_str[i] = 0; + params[param_count] = cp; + //AddLog_P2( LOG_LEVEL_NONE, "INA226 Command parameter: %d, value: %s", param_count, params[param_count]); + param_count++; + cp = param_str + i + 1; + } + + + if (p1 < 10 || p1 >= 50){ + // Device-less commands + switch (p1){ + case 1: // Rerun init + Ina226Init(); + Response_P(PSTR("{\"Sensor54-Command-Result\":{\"SlavesFound\":%d}}"),slavesFound); + break; + + case 2: // Save and restart + restart_flag = 2; + Response_P(PSTR("{\"Sensor54-Command-Result\":{\"Restart_flag\":%d}}"),restart_flag); + break; + + default: + serviced = false; + } + } + else if (p1 < 50){ + // Commands 10-49 tied to a particular device + device = (p1 / 10) - 1; // Leading Tens digit is device number {1-4} + switch (p1 % 10){ + case 0: // Show config + show_config = true; + break; + + case 1: // Set compacted shunt resistance from user input in ohms + r_shunt_uohms = (uint32_t) ((CharToFloat(params[1])) * 1000000.0f); + + + //AddLog_P2( LOG_LEVEL_NONE, "r_shunt_uohms: %d", r_shunt_uohms); + if (r_shunt_uohms > 32767){ + uint32_t r_shunt_mohms = r_shunt_uohms/1000UL; + Settings.ina226_r_shunt[device] = (uint16_t) (r_shunt_mohms | 0x8000); + } + else + Settings.ina226_r_shunt[device] = (uint16_t) r_shunt_uohms; + + //AddLog_P2( LOG_LEVEL_NONE, "r_shunt_compacted: %04X", Settings.ina226_r_shunt[device]); + show_config = true; + break; + + case 2: // Set full scale current in tenths of amps from user input in Amps + Settings.ina226_i_fs[device] = (uint16_t) ((CharToFloat(params[1])) * 10.0f); + //AddLog_P2( LOG_LEVEL_NONE, "i_fs: %d", Settings.ina226_i_fs[device]); + show_config = true; + break; + + + default: + serviced = false; + break; + } + } + else + serviced = false; + + if (show_config) { + char shunt_r_str[16]; + char fs_i_str[16]; + + // Shunt resistance is stored in EEPROM in microohms. Convert to ohms + r_shunt_uohms = _expand_r_shunt(Settings.ina226_r_shunt[device]); + dtostrfd(((float)r_shunt_uohms)/1000000.0f, 6, shunt_r_str); + // Full scale current is stored in EEPROM in tenths of an amp. Convert to amps. + dtostrfd(((float)Settings.ina226_i_fs[device])/10.0f, 1, fs_i_str); + // Send json response + Response_P(PSTR("{\"Sensor54-device-settings-%d\":{\"SHUNT_R\":%s,\"FS_I\":%s}}"), + device + 1, shunt_r_str, fs_i_str); + } + + return serviced; +} + +/* +* Show data gathered from INA226 devices +*/ + +#ifdef USE_WEBSERVER +const char HTTP_SNS_INA226_DATA[] PROGMEM = + "{s}%s " D_VOLTAGE "{m}%s " D_UNIT_VOLT "{e}" + "{s}%s " D_CURRENT "{m}%s " D_UNIT_AMPERE "{e}" + "{s}%s " D_POWERUSAGE "{m}%s " D_UNIT_WATT "{e}"; +#endif // USE_WEBSERVER + +void Ina226Show(bool json) +{ + int i, num_found; + for (num_found = 0, i = 0; i < INA226_MAX_ADDRESSES; i++) { + // Skip uninstalled sensors + if (!slaveInfo[i].present) + continue; + + num_found++; + + char voltage[16]; + dtostrfd(voltages[i], Settings.flag2.voltage_resolution, voltage); + char current[16]; + dtostrfd(currents[i], Settings.flag2.current_resolution, current); + char power[16]; + dtostrfd(powers[i], Settings.flag2.wattage_resolution, power); + char name[16]; + snprintf_P(name, sizeof(name), PSTR("INA226%c%d"),IndexSeparator(), i + 1); + + + if (json) { + ResponseAppend_P(PSTR(",\"%s\":{\"Id\":%02x,\"" D_JSON_VOLTAGE "\":%s,\"" D_JSON_CURRENT "\":%s,\"" D_JSON_POWERUSAGE "\":%s}"), + name, i, voltage, current, power); +#ifdef USE_DOMOTICZ + if (0 == tele_period) { + DomoticzSensor(DZ_VOLTAGE, voltage); + DomoticzSensor(DZ_CURRENT, current); + } +#endif // USE_DOMOTICZ +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_INA226_DATA, name, voltage, name, current, name, power); +#endif // USE_WEBSERVER) + } + + } + +} + + +/** + * The callback function Xsns_57() interfaces Tasmota with the sensor driver. + * + * It provides the Tasmota callback IDs. + * + * @param byte callback_id Tasmota function ID. + * @return bool Return value. + * @pre None. + * @post None. + * + */ +bool Xsns54(byte callback_id) { + + // Set return value to `false` + bool result = false; + + // Check if I2C interface mode is enabled + if(i2c_flg) { + + // Check which callback ID is called by Tasmota + switch (callback_id) { + case FUNC_EVERY_SECOND: + Ina226EverySecond(); + break; + case FUNC_INIT: + Ina226Init(); + break; + case FUNC_JSON_APPEND: + Ina226Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Ina226Show(0); + break; +#endif // USE_WEBSERVER + case FUNC_COMMAND_SENSOR: + if (XSNS_54 == XdrvMailbox.index) { + result = Ina226CommandSensor(); + } + break; + } + } // if(i2c_flg) + // Return boolean result + return result; +} + +#endif // USE_INA226 +#endif // USE_I2C diff --git a/sonoff/xsns_interface.ino b/sonoff/xsns_interface.ino index 0c394eaa6..cf469f930 100644 --- a/sonoff/xsns_interface.ino +++ b/sonoff/xsns_interface.ino @@ -223,7 +223,165 @@ bool (* const xsns_func_ptr[])(uint8_t) = { // Sensor Function Pointers for sim &Xsns50, #endif -// Optional user defined sensors in range 91 - 99 +#ifdef XSNS_51 + &Xsns51, +#endif + +#ifdef XSNS_52 + &Xsns52, +#endif + +#ifdef XSNS_53 + &Xsns53, +#endif + +#ifdef XSNS_54 + &Xsns54, +#endif + +#ifdef XSNS_55 + &Xsns55, +#endif + +#ifdef XSNS_56 + &Xsns56, +#endif + +#ifdef XSNS_57 + &Xsns57, +#endif + +#ifdef XSNS_58 + &Xsns58, +#endif + +#ifdef XSNS_59 + &Xsns59, +#endif + +#ifdef XSNS_60 + &Xsns60, +#endif + +#ifdef XSNS_61 + &Xsns61, +#endif + +#ifdef XSNS_62 + &Xsns62, +#endif + +#ifdef XSNS_63 + &Xsns63, +#endif + +#ifdef XSNS_64 + &Xsns64, +#endif + +#ifdef XSNS_65 + &Xsns65, +#endif + +#ifdef XSNS_66 + &Xsns66, +#endif + +#ifdef XSNS_67 + &Xsns67, +#endif + +#ifdef XSNS_68 + &Xsns68, +#endif + +#ifdef XSNS_69 + &Xsns69, +#endif + +#ifdef XSNS_70 + &Xsns70, +#endif + +#ifdef XSNS_71 + &Xsns71, +#endif + +#ifdef XSNS_72 + &Xsns72, +#endif + +#ifdef XSNS_73 + &Xsns73, +#endif + +#ifdef XSNS_74 + &Xsns74, +#endif + +#ifdef XSNS_75 + &Xsns75, +#endif + +#ifdef XSNS_76 + &Xsns76, +#endif + +#ifdef XSNS_77 + &Xsns77, +#endif + +#ifdef XSNS_78 + &Xsns78, +#endif + +#ifdef XSNS_79 + &Xsns79, +#endif + +#ifdef XSNS_80 + &Xsns80, +#endif + +#ifdef XSNS_81 + &Xsns81, +#endif + +#ifdef XSNS_82 + &Xsns82, +#endif + +#ifdef XSNS_83 + &Xsns83, +#endif + +#ifdef XSNS_84 + &Xsns84, +#endif + +#ifdef XSNS_85 + &Xsns85, +#endif + +#ifdef XSNS_86 + &Xsns86, +#endif + +#ifdef XSNS_87 + &Xsns87, +#endif + +#ifdef XSNS_88 + &Xsns88, +#endif + +#ifdef XSNS_89 + &Xsns89, +#endif + +#ifdef XSNS_90 + &Xsns90, +#endif #ifdef XSNS_91 &Xsns91, @@ -264,6 +422,446 @@ bool (* const xsns_func_ptr[])(uint8_t) = { // Sensor Function Pointers for sim const uint8_t xsns_present = sizeof(xsns_func_ptr) / sizeof(xsns_func_ptr[0]); // Number of External Sensors found +/*********************************************************************************************\ + * Xsns available list +\*********************************************************************************************/ + +#ifdef XFUNC_PTR_IN_ROM +const uint8_t kXsnsList[] PROGMEM = { +#else +const uint8_t kXsnsList[] = { +#endif + +#ifdef XSNS_01 + XSNS_01, +#endif + +#ifdef XSNS_02 + XSNS_02, +#endif + +#ifdef XSNS_03 + XSNS_03, +#endif + +#ifdef XSNS_04 + XSNS_04, +#endif + +#ifdef XSNS_05 + XSNS_05, +#endif + +#ifdef XSNS_06 + XSNS_06, +#endif + +#ifdef XSNS_07 + XSNS_07, +#endif + +#ifdef XSNS_08 + XSNS_08, +#endif + +#ifdef XSNS_09 + XSNS_09, +#endif + +#ifdef XSNS_10 + XSNS_10, +#endif + +#ifdef XSNS_11 + XSNS_11, +#endif + +#ifdef XSNS_12 + XSNS_12, +#endif + +#ifdef XSNS_13 + XSNS_13, +#endif + +#ifdef XSNS_14 + XSNS_14, +#endif + +#ifdef XSNS_15 + XSNS_15, +#endif + +#ifdef XSNS_16 + XSNS_16, +#endif + +#ifdef XSNS_17 + XSNS_17, +#endif + +#ifdef XSNS_18 + XSNS_18, +#endif + +#ifdef XSNS_19 + XSNS_19, +#endif + +#ifdef XSNS_20 + XSNS_20, +#endif + +#ifdef XSNS_21 + XSNS_21, +#endif + +#ifdef XSNS_22 + XSNS_22, +#endif + +#ifdef XSNS_23 + XSNS_23, +#endif + +#ifdef XSNS_24 + XSNS_24, +#endif + +#ifdef XSNS_25 + XSNS_25, +#endif + +#ifdef XSNS_26 + XSNS_26, +#endif + +#ifdef XSNS_27 + XSNS_27, +#endif + +#ifdef XSNS_28 + XSNS_28, +#endif + +#ifdef XSNS_29 + XSNS_29, +#endif + +#ifdef XSNS_30 + XSNS_30, +#endif + +#ifdef XSNS_31 + XSNS_31, +#endif + +#ifdef XSNS_32 + XSNS_32, +#endif + +#ifdef XSNS_33 + XSNS_33, +#endif + +#ifdef XSNS_34 + XSNS_34, +#endif + +#ifdef XSNS_35 + XSNS_35, +#endif + +#ifdef XSNS_36 + XSNS_36, +#endif + +#ifdef XSNS_37 + XSNS_37, +#endif + +#ifdef XSNS_38 + XSNS_38, +#endif + +#ifdef XSNS_39 + XSNS_39, +#endif + +#ifdef XSNS_40 + XSNS_40, +#endif + +#ifdef XSNS_41 + XSNS_41, +#endif + +#ifdef XSNS_42 + XSNS_42, +#endif + +#ifdef XSNS_43 + XSNS_43, +#endif + +#ifdef XSNS_44 + XSNS_44, +#endif + +#ifdef XSNS_45 + XSNS_45, +#endif + +#ifdef XSNS_46 + XSNS_46, +#endif + +#ifdef XSNS_47 + XSNS_47, +#endif + +#ifdef XSNS_48 + XSNS_48, +#endif + +#ifdef XSNS_49 + XSNS_49, +#endif + +#ifdef XSNS_50 + XSNS_50, +#endif + +#ifdef XSNS_51 + XSNS_51, +#endif + +#ifdef XSNS_52 + XSNS_52, +#endif + +#ifdef XSNS_53 + XSNS_53, +#endif + +#ifdef XSNS_54 + XSNS_54, +#endif + +#ifdef XSNS_55 + XSNS_55, +#endif + +#ifdef XSNS_56 + XSNS_56, +#endif + +#ifdef XSNS_57 + XSNS_57, +#endif + +#ifdef XSNS_58 + XSNS_58, +#endif + +#ifdef XSNS_59 + XSNS_59, +#endif + +#ifdef XSNS_60 + XSNS_60, +#endif + +#ifdef XSNS_61 + XSNS_61, +#endif + +#ifdef XSNS_62 + XSNS_62, +#endif + +#ifdef XSNS_63 + XSNS_63, +#endif + +#ifdef XSNS_64 + XSNS_64, +#endif + +#ifdef XSNS_65 + XSNS_65, +#endif + +#ifdef XSNS_66 + XSNS_66, +#endif + +#ifdef XSNS_67 + XSNS_67, +#endif + +#ifdef XSNS_68 + XSNS_68, +#endif + +#ifdef XSNS_69 + XSNS_69, +#endif + +#ifdef XSNS_70 + XSNS_70, +#endif + +#ifdef XSNS_71 + XSNS_71, +#endif + +#ifdef XSNS_72 + XSNS_72, +#endif + +#ifdef XSNS_73 + XSNS_73, +#endif + +#ifdef XSNS_74 + XSNS_74, +#endif + +#ifdef XSNS_75 + XSNS_75, +#endif + +#ifdef XSNS_76 + XSNS_76, +#endif + +#ifdef XSNS_77 + XSNS_77, +#endif + +#ifdef XSNS_78 + XSNS_78, +#endif + +#ifdef XSNS_79 + XSNS_79, +#endif + +#ifdef XSNS_80 + XSNS_80, +#endif + +#ifdef XSNS_81 + XSNS_81, +#endif + +#ifdef XSNS_82 + XSNS_82, +#endif + +#ifdef XSNS_83 + XSNS_83, +#endif + +#ifdef XSNS_84 + XSNS_84, +#endif + +#ifdef XSNS_85 + XSNS_85, +#endif + +#ifdef XSNS_86 + XSNS_86, +#endif + +#ifdef XSNS_87 + XSNS_87, +#endif + +#ifdef XSNS_88 + XSNS_88, +#endif + +#ifdef XSNS_89 + XSNS_89, +#endif + +#ifdef XSNS_90 + XSNS_90, +#endif + +#ifdef XSNS_91 + XSNS_91, +#endif + +#ifdef XSNS_92 + XSNS_92, +#endif + +#ifdef XSNS_93 + XSNS_93, +#endif + +#ifdef XSNS_94 + XSNS_94, +#endif + +#ifdef XSNS_95 + XSNS_95, +#endif + +#ifdef XSNS_96 + XSNS_96, +#endif + +#ifdef XSNS_97 + XSNS_97, +#endif + +#ifdef XSNS_98 + XSNS_98, +#endif + +#ifdef XSNS_99 + XSNS_99 +#endif +}; + +/*********************************************************************************************/ + +bool XsnsEnabled(uint32_t sns_index) +{ + if (sns_index < sizeof(kXsnsList)) { +#ifdef XFUNC_PTR_IN_ROM + uint32_t index = pgm_read_byte(kXsnsList + sns_index); +#else + uint32_t index = kXsnsList[sns_index]; +#endif + return bitRead(Settings.sensors[index / 32], index % 32); + } + return true; +} + +void XsnsSensorState(void) +{ + ResponseAppend_P(PSTR("\"")); // Use string for enable/disable signal + for (uint32_t i = 0; i < sizeof(kXsnsList); i++) { +#ifdef XFUNC_PTR_IN_ROM + uint32_t sensorid = pgm_read_byte(kXsnsList + i); +#else + uint32_t sensorid = kXsnsList[i]; +#endif + bool disabled = false; + if (sensorid < MAX_XSNS_DRIVERS) { + disabled = !bitRead(Settings.sensors[sensorid / 32], sensorid % 32); + } + ResponseAppend_P(PSTR("%s%s%d"), (i) ? "," : "", (disabled) ? "!" : "", sensorid); + } + ResponseAppend_P(PSTR("\"")); +} + /*********************************************************************************************\ * Function call to all xsns \*********************************************************************************************/ @@ -272,13 +870,19 @@ bool XsnsNextCall(uint8_t Function, uint8_t &xsns_index) { xsns_index++; if (xsns_index == xsns_present) { xsns_index = 0; } -#ifdef USE_DEBUG_DRIVER - while (!XsnsEnabled(xsns_index) && !xsns_index) { // Perform at least first sensor (counter) - xsns_index++; - if (xsns_index == xsns_present) { xsns_index = 0; } + +#ifndef USE_DEBUG_DRIVER + if (FUNC_WEB_SENSOR == Function) { // Skip web info for disabled sensors +#endif + uint32_t max_disabled = xsns_present; + while (!XsnsEnabled(xsns_index) && max_disabled--) { // Perform at least one sensor + xsns_index++; + if (xsns_index == xsns_present) { xsns_index = 0; } + } +#ifndef USE_DEBUG_DRIVER } #endif -// WifiAddDelayWhenDisconnected(); + return xsns_func_ptr[xsns_index](Function); } @@ -292,13 +896,14 @@ bool XsnsCall(uint8_t Function) for (uint32_t x = 0; x < xsns_present; x++) { #ifdef USE_DEBUG_DRIVER - if (XsnsEnabled(x)) { + if (XsnsEnabled(x)) { // Skip disabled sensor in debug mode #endif + if ((FUNC_WEB_SENSOR == Function) && !XsnsEnabled(x)) { continue; } // Skip web info for disabled sensors + #ifdef PROFILE_XSNS_SENSOR_EVERY_SECOND uint32_t profile_start_millis = millis(); #endif // PROFILE_XSNS_SENSOR_EVERY_SECOND -// WifiAddDelayWhenDisconnected(); result = xsns_func_ptr[x](Function); #ifdef PROFILE_XSNS_SENSOR_EVERY_SECOND @@ -311,6 +916,7 @@ bool XsnsCall(uint8_t Function) #endif // PROFILE_XSNS_SENSOR_EVERY_SECOND if (result && ((FUNC_COMMAND == Function) || + (FUNC_PIN_STATE == Function) || (FUNC_COMMAND_SENSOR == Function) )) { break; diff --git a/sonoff/zzzz_debug.ino b/sonoff/zzzz_debug.ino deleted file mode 100644 index 7b2e7e6ad..000000000 --- a/sonoff/zzzz_debug.ino +++ /dev/null @@ -1,309 +0,0 @@ -/* - zzzz_debug.ino - debug support for Sonoff-Tasmota - - Copyright (C) 2019 Theo Arends - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#ifdef USE_DEBUG_DRIVER -/*********************************************************************************************\ - * Virtual debugging support - Part 2 - * - * Needs to be the last alphabetical file due to DEFINE compile order -\*********************************************************************************************/ - -/*********************************************************************************************\ - * Xsns available list -\*********************************************************************************************/ - -#ifdef XFUNC_PTR_IN_ROM -const uint8_t kXsnsList[] PROGMEM = { -#else -const uint8_t kXsnsList[] = { -#endif - -#ifdef XSNS_01 - XSNS_01, -#endif - -#ifdef XSNS_02 - XSNS_02, -#endif - -#ifdef XSNS_03 - XSNS_03, -#endif - -#ifdef XSNS_04 - XSNS_04, -#endif - -#ifdef XSNS_05 - XSNS_05, -#endif - -#ifdef XSNS_06 - XSNS_06, -#endif - -#ifdef XSNS_07 - XSNS_07, -#endif - -#ifdef XSNS_08 - XSNS_08, -#endif - -#ifdef XSNS_09 - XSNS_09, -#endif - -#ifdef XSNS_10 - XSNS_10, -#endif - -#ifdef XSNS_11 - XSNS_11, -#endif - -#ifdef XSNS_12 - XSNS_12, -#endif - -#ifdef XSNS_13 - XSNS_13, -#endif - -#ifdef XSNS_14 - XSNS_14, -#endif - -#ifdef XSNS_15 - XSNS_15, -#endif - -#ifdef XSNS_16 - XSNS_16, -#endif - -#ifdef XSNS_17 - XSNS_17, -#endif - -#ifdef XSNS_18 - XSNS_18, -#endif - -#ifdef XSNS_19 - XSNS_19, -#endif - -#ifdef XSNS_20 - XSNS_20, -#endif - -#ifdef XSNS_21 - XSNS_21, -#endif - -#ifdef XSNS_22 - XSNS_22, -#endif - -#ifdef XSNS_23 - XSNS_23, -#endif - -#ifdef XSNS_24 - XSNS_24, -#endif - -#ifdef XSNS_25 - XSNS_25, -#endif - -#ifdef XSNS_26 - XSNS_26, -#endif - -#ifdef XSNS_27 - XSNS_27, -#endif - -#ifdef XSNS_28 - XSNS_28, -#endif - -#ifdef XSNS_29 - XSNS_29, -#endif - -#ifdef XSNS_30 - XSNS_30, -#endif - -#ifdef XSNS_31 - XSNS_31, -#endif - -#ifdef XSNS_32 - XSNS_32, -#endif - -#ifdef XSNS_33 - XSNS_33, -#endif - -#ifdef XSNS_34 - XSNS_34, -#endif - -#ifdef XSNS_35 - XSNS_35, -#endif - -#ifdef XSNS_36 - XSNS_36, -#endif - -#ifdef XSNS_37 - XSNS_37, -#endif - -#ifdef XSNS_38 - XSNS_38, -#endif - -#ifdef XSNS_39 - XSNS_39, -#endif - -#ifdef XSNS_40 - XSNS_40, -#endif - -#ifdef XSNS_41 - XSNS_41, -#endif - -#ifdef XSNS_42 - XSNS_42, -#endif - -#ifdef XSNS_43 - XSNS_43, -#endif - -#ifdef XSNS_44 - XSNS_44, -#endif - -#ifdef XSNS_45 - XSNS_45, -#endif - -#ifdef XSNS_46 - XSNS_46, -#endif - -#ifdef XSNS_47 - XSNS_47, -#endif - -#ifdef XSNS_48 - XSNS_48, -#endif - -#ifdef XSNS_49 - XSNS_49, -#endif - -#ifdef XSNS_50 - XSNS_50, -#endif - -// Optional user defined sensors in range 91 - 99 - -#ifdef XSNS_91 - XSNS_91, -#endif - -#ifdef XSNS_92 - XSNS_92, -#endif - -#ifdef XSNS_93 - XSNS_93, -#endif - -#ifdef XSNS_94 - XSNS_94, -#endif - -#ifdef XSNS_95 - XSNS_95 -#endif -}; - -/*********************************************************************************************\ - * Xsns sensor control -\*********************************************************************************************/ - -bool XsnsEnabled(uint8_t sns_index) -{ - if (sns_index < sizeof(kXsnsList)) { -#ifdef XFUNC_PTR_IN_ROM - uint8_t index = pgm_read_byte(kXsnsList + sns_index); -#else - uint8_t index = kXsnsList[sns_index]; -#endif - return bitRead(Settings.sensors[index / 32], index % 32); - } - return 1; -} - -bool XsnsPresent(uint8_t sns_index) -{ - uint8_t index = 0; - for (uint32_t i = 0; i < sizeof(kXsnsList); i++) { -#ifdef XFUNC_PTR_IN_ROM - index = pgm_read_byte(kXsnsList + i); -#else - index = kXsnsList[i]; -#endif - if (index == sns_index) { return true; } - } - return false; -} - -String XsnsGetSensors(void) -{ - char state[2] = { 0 }; - - String data = F("["); - for (uint32_t i = 0; i < MAX_XSNS_DRIVERS; i++) { - if (i && (!(i % 16))) { data += F(","); } - if (!(i % 16)) { data += F("\""); } - state[0] = '-'; - if (XsnsPresent(i)) { state[0] = bitRead(Settings.sensors[i / 32], i % 32) ? '1' : '0'; } - data += String(state); - if (i && (!((i +1) % 16))) { data += F("\""); } - } - data += F("]"); - - return data; -} - -#endif // USE_DEBUG_DRIVER \ No newline at end of file diff --git a/tools/decode-config.html b/tools/decode-config.html index 4dc80286b..d5cff4c3a 100644 --- a/tools/decode-config.html +++ b/tools/decode-config.html @@ -219,7 +219,7 @@ If you do not want using auto extensions use the --no-extension par [--cmnd-indent <indent>] [--cmnd-groups] [--cmnd-nogroups] [--cmnd-sort] [--cmnd-unsort] [-c <filename>] [-S] [-T json|cmnd|command] - [-g {Control,Devices,Display,Domoticz,Internal,KNX,Light,MQTT,Management,Power,Rules,Sensor,Serial,SetOption,SonoffRF,System,Timer,Wifi} [{Control,Devices,Display,Domoticz,Internal,KNX,Light,MQTT,Management,Power,Rules,Sensor,Serial,SetOption,SonoffRF,System,Timer,Wifi} ...]] + [-g {Control,Devices,Display,Domoticz,Internal,KNX,Light,MQTT,Management,Power,Rules,Sensor,Serial,SetOption,SonoffRF,System,Timer,Wifi} [{Control,Devices,Display,Domoticz,Internal,KNX,Light,MQTT,Management,Power,Rules,Sensor,Serial,SetOption,SonoffRF,System,Timer,Wifi} ...]] [--ignore-warnings] [-h] [-H] [-v] [-V] Backup/Restore Sonoff-Tasmota configuration data. Args that start with '--' @@ -299,7 +299,7 @@ Common: (default do not output on backup or restore usage) -T, --output-format json|cmnd|command display output format (default: 'json') - -g, --group {Control,Devices,Display,Domoticz,Internal,KNX,Light,MQTT,Management,Power,Rules,Sensor,Serial,SetOption,SonoffRF,System,Timer,Wifi} + -g, --group {Control,Devices,Display,Domoticz,Internal,Knx,Light,Management,Mqtt,Power,Rules,Sensor,Serial,Setoption,Shutter,Sonoffrf,System,Timer,Wifi} limit data processing to command groups (default no filter) --ignore-warnings do not exit on warnings. Not recommended, used by your diff --git a/tools/decode-config.md b/tools/decode-config.md index 01b20a143..883467dfa 100644 --- a/tools/decode-config.md +++ b/tools/decode-config.md @@ -237,7 +237,7 @@ For advanced help use `-H` or `--full-help`: [--cmnd-indent ] [--cmnd-groups] [--cmnd-nogroups] [--cmnd-sort] [--cmnd-unsort] [-c ] [-S] [-T json|cmnd|command] - [-g {Control,Devices,Display,Domoticz,Internal,KNX,Light,MQTT,Management,Power,Rules,Sensor,Serial,SetOption,SonoffRF,System,Timer,Wifi} [{Control,Devices,Display,Domoticz,Internal,KNX,Light,MQTT,Management,Power,Rules,Sensor,Serial,SetOption,SonoffRF,System,Timer,Wifi} ...]] + [-g {Control,Devices,Display,Domoticz,Internal,Knx,Light,Management,Mqtt,Power,Rules,Sensor,Serial,Setoption,Shutter,Sonoffrf,System,Timer,Wifi} [{Control,Devices,Display,Domoticz,Internal,Knx,Light,Management,Mqtt,Power,Rules,Sensor,Serial,Setoption,Shutter,Sonoffrf,System,Timer,Wifi} ...]] [--ignore-warnings] [-h] [-H] [-v] [-V] Backup/Restore Sonoff-Tasmota configuration data. Args that start with '--' @@ -317,7 +317,7 @@ For advanced help use `-H` or `--full-help`: (default do not output on backup or restore usage) -T, --output-format json|cmnd|command display output format (default: 'json') - -g, --group {Control,Devices,Display,Domoticz,Internal,KNX,Light,MQTT,Management,Power,Rules,Sensor,Serial,SetOption,SonoffRF,System,Timer,Wifi} + -g, --group {Control,Devices,Display,Domoticz,Internal,Knx,Light,Management,Mqtt,Power,Rules,Sensor,Serial,Setoption,Shutter,Sonoffrf,System,Timer,Wifi} limit data processing to command groups (default no filter) --ignore-warnings do not exit on warnings. Not recommended, used by your diff --git a/tools/decode-config.py b/tools/decode-config.py index 0fe3e274c..12ce09baa 100644 --- a/tools/decode-config.py +++ b/tools/decode-config.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -VER = '2.2.0028' +VER = '2.3.0036' """ decode-config.py - Backup/Restore Sonoff-Tasmota configuration data @@ -43,7 +43,7 @@ Usage: decode-config.py [-f ] [-d ] [-P ] [--cmnd-indent ] [--cmnd-groups] [--cmnd-nogroups] [--cmnd-sort] [--cmnd-unsort] [-c ] [-S] [-T json|cmnd|command] - [-g {Control,Devices,Display,Domoticz,Internal,KNX,Light,MQTT,Management,Power,Rules,Sensor,Serial,SetOption,SonoffRF,System,Timer,Wifi} [{Control,Devices,Display,Domoticz,Internal,KNX,Light,MQTT,Management,Power,Rules,Sensor,Serial,SetOption,SonoffRF,System,Timer,Wifi} ...]] + [-g {Control,Devices,Display,Domoticz,Internal,Knx,Light,Management,Mqtt,Power,Rules,Sensor,Serial,Setoption,Shutter,Sonoffrf,System,Timer,Wifi} [{Control,Devices,Display,Domoticz,Internal,Knx,Light,Management,Mqtt,Power,Rules,Sensor,Serial,Setoption,Shutter,Sonoffrf,System,Timer,Wifi} ...]] [--ignore-warnings] [-h] [-H] [-v] [-V] Backup/Restore Sonoff-Tasmota configuration data. Args that start with '--' @@ -123,7 +123,7 @@ Usage: decode-config.py [-f ] [-d ] [-P ] (default do not output on backup or restore usage) -T, --output-format json|cmnd|command display output format (default: 'json') - -g, --group {Control,Devices,Display,Domoticz,Internal,KNX,Light,MQTT,Management,Power,Rules,Sensor,Serial,SetOption,SonoffRF,System,Timer,Wifi} + -g, --group {Control,Devices,Display,Domoticz,Internal,Knx,Light,Management,Mqtt,Power,Rules,Sensor,Serial,Setoption,Shutter,Sonoffrf,System,Timer,Wifi} limit data processing to command groups (default no filter) --ignore-warnings do not exit on warnings. Not recommended, used by your @@ -403,6 +403,11 @@ def MqttFingerprint(value, idx=None): fingerprint += "{:02x} ".format(ord(i)) return "MqttFingerprint{} {}".format('' if idx is None else idx, fingerprint.strip()) +def WebSensor(value, idx): + cmd=[] + for i in range(0,32): + cmd.append("WebSensor{} {}".format(i+(idx-1)*32, "1" if (int(value,16) & (1< 0x0606000A: + size = 3584 crc = 0 - for i in range(0, len(dobj)): + for i in range(0, size): if not i in [14,15]: # Skip crc byte = ord(dobj[i]) crc += byte * (i+1) @@ -1621,6 +1798,28 @@ def GetSettingsCrc(dobj): return crc & 0xffff +def GetSettingsCrc32(dobj): + """ + Return binary config data calclulated crc32 + + @param dobj: + decrypted binary config data + + @return: + 4 byte unsigned integer crc value + + """ + if isinstance(dobj, bytearray): + dobj = str(dobj) + crc = 0 + for i in range(0, len(dobj)-4): + crc ^= ord(dobj[i]) + for j in range(0, 8): + crc = (crc >> 1) ^ (-int(crc & 1) & 0xEDB88320); + + return ~crc & 0xffffffff + + def GetFieldDef(fielddef, fields="format_, addrdef, baseaddr, bits, bitshift, datadef, arraydef, validate, cmd, group, tasmotacmnd, converter, readconverter, writeconverter"): """ @@ -1794,7 +1993,7 @@ def CmndConverter(valuemapping, value, idx, fielddef): field definition - see "Settings dictionary" above @return: - converted value or None if unable to convert + converted value, list of values or None if unable to convert """ converter, readconverter, writeconverter, group, tasmotacmnd = GetFieldDef(fielddef, fields='converter, readconverter, writeconverter, group, tasmotacmnd') @@ -2034,7 +2233,7 @@ def IsFilterGroup(group): if group is None: return False if group == '*': - return False + return True if group.title() != INTERNAL.title() and group.title() not in (groupname.title() for groupname in args.filter): return False return True @@ -2432,6 +2631,8 @@ def SetCmnd(cmnds, fieldname, fielddef, valuemapping, mappedvalue, addroffset=0, # a simple value elif isinstance(format_, (str, bool, int, float, long)): + if group is not None: + group = group.title(); if isinstance(tasmotacmnd, tuple): tasmotacmnds = tasmotacmnd for tasmotacmnd in tasmotacmnds: @@ -2439,13 +2640,21 @@ def SetCmnd(cmnds, fieldname, fielddef, valuemapping, mappedvalue, addroffset=0, if group is not None and cmnd is not None: if group not in cmnds: cmnds[group] = [] - cmnds[group].append(cmnd) + if isinstance(cmnd, list): + for c in cmnd: + cmnds[group].append(c) + else: + cmnds[group].append(cmnd) else: cmnd = CmndConverter(valuemapping, mappedvalue, idx, fielddef) if group is not None and cmnd is not None: if group not in cmnds: cmnds[group] = [] - cmnds[group].append(cmnd) + if isinstance(cmnd, list): + for c in cmnd: + cmnds[group].append(c) + else: + cmnds[group].append(cmnd) return cmnds @@ -2489,8 +2698,16 @@ def Bin2Mapping(decode_cfg): cfg_crc = GetField(decode_cfg, 'cfg_crc', setting['cfg_crc'], raw=True) else: cfg_crc = GetSettingsCrc(decode_cfg) - if cfg_crc != GetSettingsCrc(decode_cfg): - exit(ExitCode.DATA_CRC_ERROR, 'Data CRC error, read 0x{:x} should be 0x{:x}'.format(cfg_crc, GetSettingsCrc(decode_cfg)), type_=LogType.WARNING, doexit=not args.ignorewarning,line=inspect.getlineno(inspect.currentframe())) + if 'cfg_crc32' in setting: + cfg_crc32 = GetField(decode_cfg, 'cfg_crc32', setting['cfg_crc32'], raw=True) + else: + cfg_crc32 = GetSettingsCrc32(decode_cfg) + if version < 0x0606000B: + if cfg_crc != GetSettingsCrc(decode_cfg): + exit(ExitCode.DATA_CRC_ERROR, 'Data CRC error, read 0x{:4x} should be 0x{:4x}'.format(cfg_crc, GetSettingsCrc(decode_cfg)), type_=LogType.WARNING, doexit=not args.ignorewarning,line=inspect.getlineno(inspect.currentframe())) + else: + if cfg_crc32 != GetSettingsCrc32(decode_cfg): + exit(ExitCode.DATA_CRC_ERROR, 'Data CRC32 error, read 0x{:8x} should be 0x{:8x}'.format(cfg_crc32, GetSettingsCrc32(decode_cfg)), type_=LogType.WARNING, doexit=not args.ignorewarning,line=inspect.getlineno(inspect.currentframe())) # get valuemapping valuemapping = GetField(decode_cfg, None, (setting,0,(None, None, (INTERNAL, None)))) @@ -2521,6 +2738,9 @@ def Bin2Mapping(decode_cfg): } if 'cfg_crc' in setting: valuemapping['header']['template'].update({'size': cfg_size}) + if 'cfg_crc32' in setting: + valuemapping['header']['template'].update({'crc32': hex(cfg_crc32)}) + valuemapping['header']['data'].update({'crc32': hex(GetSettingsCrc32(decode_cfg))}) if 'version' in setting: valuemapping['header']['data'].update({'version': hex(cfg_version)}) @@ -2566,6 +2786,9 @@ def Mapping2Bin(decode_cfg, jsonconfig, filename=""): if 'cfg_crc' in setting: crc = GetSettingsCrc(_buffer) struct.pack_into(setting['cfg_crc'][0], _buffer, setting['cfg_crc'][1], crc) + if 'cfg_crc32' in setting: + crc32 = GetSettingsCrc32(_buffer) + struct.pack_into(setting['cfg_crc32'][0], _buffer, setting['cfg_crc32'][1], crc32) return _buffer else: diff --git a/tools/decode-status.py b/tools/decode-status.py index 43ba0c6fb..10deefede 100644 --- a/tools/decode-status.py +++ b/tools/decode-status.py @@ -53,7 +53,7 @@ a_on_off = ["OFF","ON "] a_setoption = [[ "Save power state and use after restart", "Restrict button actions to single, double and hold", - "Show value units in JSON messages", + "(not used) Show value units in JSON messages", "MQTT enabled", "Respond as Command topic instead of RESULT", "MQTT retain on Power", @@ -86,12 +86,22 @@ a_setoption = [[ ],[ "Key hold time (ms)", "Sonoff POW Max_Power_Retry", - "Tuya dimmer device id", + "(not used) Tuya MCU device id", "(not used) mDNS delayed start (Sec)", "Boot loop retry offset (0 = disable)", "RGBWW remap", - "","","","","","", - "","","","","","", + "IR Unknown threshold", + "CSE7766 invalid power margin", + "Ignore hold time (s)", + "(not used) Number of Tuya MCU relays", + "Over temperature threshold (celsius)", + "(not used) Tuya MCU max dimmer value", + "(not used) Tuya MCU voltage Id", + "(not used) Tuya MCU current Id", + "(not used) Tuya MCU power Id", + "Energy Tariff1 start hour", + "Energy Tariff2 start hour", + "", ],[ "Timers enabled", "Generic ESP8285 GPIO enabled", @@ -108,33 +118,40 @@ a_setoption = [[ "Do not use retain flag on HOLD messages", "Do not scan relay power state at restart", "Use _ instead of - as sensor index separator", - "", + "Disable fast power cycle detection for device reset", + "(not used) Disable Dimmer range 255 slider control", + "Enable buzzer when available", + "Enable multi-channels PWM instead of Color PWM", + "(not used) Limits Tuya MCU dimmers to minimum of 10% (25) when enabled", + "Enable Weekend Energy Tariff", + "Select different Modbus registers for Active Energy", + "","", "","","","", - "","","","", - "","","","", - "","","","" + "","", + "Enable shutter support", + "Invert PCF8574 ports" ]] a_features = [[ - "","","USE_I2C","USE_SPI", + "USE_ENERGY_MARGIN_DETECTION","USE_LIGHT","USE_I2C","USE_SPI", "USE_DISCOVERY","USE_ARDUINO_OTA","USE_MQTT_TLS","USE_WEBSERVER", "WEBSERVER_ADVERTISE","USE_EMULATION_HUE","MQTT_PUBSUBCLIENT","MQTT_TASMOTAMQTT", "MQTT_ESPMQTTARDUINO","MQTT_HOST_DISCOVERY","USE_ARILUX_RF","USE_WS2812", "USE_WS2812_DMA","USE_IR_REMOTE","USE_IR_HVAC","USE_IR_RECEIVE", "USE_DOMOTICZ","USE_DISPLAY","USE_HOME_ASSISTANT","USE_SERIAL_BRIDGE", "USE_TIMERS","USE_SUNRISE","USE_TIMERS_WEB","USE_RULES", - "USE_KNX","USE_WPS","USE_SMARTCONFIG","MQTT_ARDUINOMQTT" + "USE_KNX","USE_WPS","USE_SMARTCONFIG","USE_ENERGY_POWER_LIMIT" ],[ "USE_CONFIG_OVERRIDE","FIRMWARE_MINIMAL","FIRMWARE_SENSORS","FIRMWARE_CLASSIC", "FIRMWARE_KNX_NO_EMULATION","USE_DISPLAY_MODES1TO5","USE_DISPLAY_GRAPH","USE_DISPLAY_LCD", "USE_DISPLAY_SSD1306","USE_DISPLAY_MATRIX","USE_DISPLAY_ILI9341","USE_DISPLAY_EPAPER", "USE_DISPLAY_SH1106","USE_MP3_PLAYER","USE_PCA9685","USE_TUYA_DIMMER", "USE_RC_SWITCH","USE_ARMTRONIX_DIMMERS","USE_SM16716","USE_SCRIPT", - "USE_EMULATION_WEMO","","","NO_EXTRA_4K_HEAP", + "USE_EMULATION_WEMO","USE_SONOFF_IFAN","USE_ZIGBEE","NO_EXTRA_4K_HEAP", "VTABLES_IN_IRAM","VTABLES_IN_DRAM","VTABLES_IN_FLASH","PIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH", "PIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY","PIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH","DEBUG_THEO","USE_DEBUG_DRIVER" ],[ - "","USE_ADC_VCC","USE_ENERGY_SENSOR","USE_PZEM004T", + "USE_COUNTER","USE_ADC_VCC","USE_ENERGY_SENSOR","USE_PZEM004T", "USE_DS18B20","USE_DS18x20_LEGACY","USE_DS18x20","USE_DHT", "USE_SHT","USE_HTU","USE_BMP","USE_BME680", "USE_BH1750","USE_VEML6070","USE_ADS1115_I2CDEV","USE_ADS1115", @@ -149,8 +166,18 @@ a_features = [[ "USE_PZEM_DC","USE_TX20_WIND_SENSOR","USE_MGC3130","USE_RF_SENSOR", "USE_THEO_V2","USE_ALECTO_V2","USE_AZ7798","USE_MAX31855", "USE_PN532_I2C","USE_MAX44009","USE_SCD30","USE_HRE", - "USE_ADE7953","","","", - "","","",""]] + "USE_ADE7953","USE_SPS30","USE_VL53L0X","USE_MLX90614", + "USE_MAX31865","USE_CHIRP","USE_SOLAX_X1","USE_PAJ7620" + ],[ + "USE_BUZZER","USE_RDM6300","USE_IBEACON","USE_SML_M", + "USE_INA226","USE_A4988_STEPPER","USE_DDS2382","USE_SM2135", + "USE_SHUTTER","USE_PCF8574","USE_DDSU666","USE_DEEPSLEEP", + "USE_SONOFF_SC","USE_SONOFF_RF","USE_SONOFF_L1","USE_EXS_DIMMER", + "USE_ARDUINO_SLAVE","","","", + "","","","", + "","","","", + "","","","" + ]] usage = "usage: decode-status {-d | -f} arg" parser = OptionParser(usage) @@ -183,7 +210,7 @@ else: obj = json.load(fp) def StartDecode(): - print ("\n*** decode-status.py v20190204 by Theo Arends and Jacek Ziolkowski ***") + print ("\n*** decode-status.py v20190819 by Theo Arends and Jacek Ziolkowski ***") # print("Decoding\n{}".format(obj)) @@ -232,7 +259,7 @@ def StartDecode(): if "StatusMEM" in obj: if "Features" in obj["StatusMEM"]: features = [] - for f in range(5): + for f in range(6): feature = obj["StatusMEM"]["Features"][f] i_feature = int(feature,16) if f == 0: diff --git a/tools/fw_efm8bb1/RF-Bridge-EFM8BB1-20191006.hex b/tools/fw_efm8bb1/RF-Bridge-EFM8BB1-20191006.hex new file mode 100644 index 000000000..1575f3ec7 --- /dev/null +++ b/tools/fw_efm8bb1/RF-Bridge-EFM8BB1-20191006.hex @@ -0,0 +1,491 @@ +:020000040000FA +:100000000210D8A2029290A2029280AD40AC3F7F33 +:100010000A7E0012002E12001DA202B322E59120DA +:10002000E2FB220210487597A5222202141B8E4182 +:100030008F428C438D4412163112188EE54424BF32 +:100040009000E8F0E54334FF9000E7F09000E3E52E +:1000500041F0A3E542F043910422220211671219F4 +:100060002253D87853DAFE1218F3E4900087F02276 +:10007000D2DE2202146E1217C0C290C296C280E471 +:10008000FBFD7F101218DA12053174A4F0D2AF1202 +:10009000170AD2969000ECE004F070069000EBE0B6 +:1000A00004F09000EBE0B427E9A3E0B410E4C296BA +:1000B0001200263003091216B18E228F2380067596 +:1000C0002201752300E5237004E522640170469047 +:1000D00000DEE0700612170B0202959000EBE4754B +:1000E000F001120807FED3E5F09410EE94274002C9 +:1000F000D296D39000ECE094309000EBE0947550F1 +:1001000003020295E4F0A3F09000DEF0900099F075 +:10011000C29602029512170A9000DEE014602A14BB +:1001200070030202591470030202171470030202D2 +:100130003024046003020295E52364AA60030202EE +:10014000959000DE04F0020295E523900099F0906E +:1001500000DE7402F0E52312092F0201A0017FA145 +:10016000018BA501A9A601BDA701C6A801DDA901B2 +:10017000CCB001D5B1019DC00295FF0000020C1268 +:10018000051EE490008AF07FA1806512054E900064 +:10019000EA7408F0E4F52575240902029512054E6B +:1001A000E4F52575240202029590008A7401F07F1F +:1001B000A61215A690007974A6F0020295120531D8 +:1001C00074A4F00202959000EA7408F09000DE74C6 +:1001D00003F00202957FB112056202029512051E1C +:1001E00090008AE09000E9F090008A7401F07FA905 +:1001F0001205627D307C757F017E00121797020226 +:100200009512056B900079EFF0E48005E490009979 +:10021000F09000DEF0807EE4F525E523F524E5246A +:10022000D39400402C12005E9000DE7404F08065D0 +:1002300074032525F582E43400F583E523F00525D4 +:10024000E525B52402800AE525C39470404775244E +:10025000709000DE7402F0803CE5236455703690A7 +:1002600000DEF0C203900099E02460601B24FC6073 +:100270001224FE600E14600B24F760101460042436 +:100280001070127FA0121875D2038009900004E04C +:10029000049000EAF0900099E012092F02BEA10339 +:1002A00033A40377A50333A60447A802BEA9047CA0 +:1002B000B004E5B1041EC00442FF000000B090008D +:1002C00086E030E73F7DC87C0012053F900099E052 +:1002D0002457600E2408701F12056B7FA31215ECC3 +:1002E00080159000E9E090008AF0900079E0FF121C +:1002F00015A67FAB12057CE4900086F043DA01D2AC +:10030000030200B01218EB50207DE87C0312053F79 +:10031000900099E02457600C240860030200B07F2D +:10032000A20204D77FAA0204D71205744003020078 +:10033000B0802E900086E030E71F900099E0245AAC +:10034000600F240260030204F17FA41215EC020482 +:10035000F17FA612057C0204F1120574400302002D +:10036000B01216E7C006C0071216F69200D007D0EA +:10037000061209810200B09000DEE060030200B0C6 +:100380001217127003020411240560030200B01258 +:100390000558900005E0FFA3E090008BCFF0A3EF9D +:1003A000F0900007E0FFA3E090008DCFF0A3EFF006 +:1003B000900003E0FFA3E090008FCFF0A3EFF07B6D +:1003C000017A00798B901A0EE493FC740193FD90EE +:1003D0001A10E493F52E1217028E2FF530901A138F +:1003E000E493F5311217028E32F533901A16E49326 +:1003F000F5341217028E35F536901A19E493F53755 +:10040000A3E493F538753900753A09120EBC020061 +:10041000B09000EAE07063C2807FA00204D7900031 +:10042000DEE060030200B0900003E0FCA3E0FD7F8B +:10043000017E00121797D29612001DC2967FA0026D +:1004400004D77F030204D79000DEE060030200B00F +:100450001217126019240560030200B012055890AB +:100460000003E0FF7C007D041213570200B09000EF +:10047000EAE07006C2807FA0805D80619000DEE0CF +:1004800060030200B01217126043240560030200EB +:10049000B09000EAE014F012005E7E007F05C00616 +:1004A000C007900003E0FB25E0FFE433FE74052F56 +:1004B000F58274003EAD82FCEB25E0FFE52424FECE +:1004C000C39FFBD007D006120FA70200B09000EA2E +:1004D000E0700AC2807FA01218680200B0E49000A9 +:1004E00087F00200B0900086E030E7107FB1121173 +:1004F000ECE4900086F043DA010200B07E007FED6C +:1005000012151040030200B01216E7C006C0071211 +:1005100016F69200D007D006120D160200B07D32FA +:100520007C007F017E00121797D29612001DC296A2 +:1005300022E490008AF07FA41215A6900079227F11 +:10054000017E00121797D29612001DC29622120049 +:100550005E9000DE7404F0229000EAE014F01200D5 +:100560005E221215A6900079EFF022900079E0FF4C +:100570001215A6227E007FED12151022900086E053 +:10058000547FFD12155B228F29E4F536EF25E02517 +:10059000E02468F8E6701EEF25E025E02469F8768F +:1005A000087E007F707D007B017A00790312095577 +:1005B000E490007AF0AB2CAA2DA92EC001120B6793 +:1005C0002466F9E7540F120CA3120B86D001121403 +:1005D000C1503D120B672466F9E7540FFF120CA3BC +:1005E000120C90300101B34031EF700A900077E5B2 +:1005F0002AF0A3E52BF0120B67120CAF2401FFEFDA +:10060000FEEC54F04EFEEDFF120B67120C9A800ABE +:10061000120B672466F874F056F6AB2CAA2DA92E9F +:10062000C001120B67120CBB120BD1120CC7120BBC +:1006300086D0011214C15052120B67120CBB120B60 +:10064000D1FF120CC75408FE120C92300101B340C6 +:1006500043EF700A900088E52AF0A3E52BF0120B17 +:1006600067120BC22401FFE433FEEFC4F8540FC835 +:1006700068FFEEC454F048FEED540FFDEC4EFEED65 +:100680004FFF120B67120C9A800A120B672467F84F +:10069000740F56F6120B67120CAFFB700F120BCAD9 +:1006A000700A120B4D2469F8E4F6C322120B67247A +:1006B00068F8E53514667003753601C3E531953683 +:1006C0006B7020D290120B672466F874F056F674A3 +:1006D0000F0856120B662468F806120B672469F897 +:1006E00016804C120B67120BC2FFC3E53495366FB0 +:1006F000703DD290120B672466F8EC54F0FEED5476 +:100700000FA60608120B662468F806120B67246908 +:10071000F816120B67120BD8E0FF120B672469F86A +:10072000E6FE7401A806088002C333D8FC4FF0121D +:100730000B672469F8E67017120B67120BD8120CBE +:100740004CFF12182DEFF0120B672469F87608128F +:100750000B672468F8E6C3953540311218E350055D +:10076000E4900085F0120C4960181218F37D207C8B +:10077000037F017E0012176E120CD3A3E529F0440B +:1007800080F0120B4D2469F8E4F6D322C322BB019A +:100790000689828A83F0225002F722BBFE01F322EF +:1007A000EF8DF0A4A8F0CF8CF0A428CE8DF0A42E6D +:1007B000FE22BC000BBE0029EF8DF084FFADF022BD +:1007C000E4CCF875F008EF2FFFEE33FEEC33FCEECF +:1007D0009DEC984005FCEE9DFE0FD5F0E9E4CEFDC2 +:1007E00022EDF8F5F0EE8420D21CFEADF075F00895 +:1007F000EF2FFFED33FD4007985006D5F0F222C3EE +:1008000098FD0FD5F0EA22C5F0F8A3E028F0C5F076 +:10081000F8E582158270021583E038F022BB0110E2 +:10082000E58229F582E5833AF583E0F5F0A3E0223D +:100830005009E92582F886F008E622BBFE0AE92580 +:1008400082F8E2F5F008E222E5832AF583E993F5E0 +:10085000F0A3E9932275F008758200EF2FFFEE33C5 +:10086000FECD33CDCC33CCC58233C5829BED9AEC23 +:1008700099E58298400CF582EE9BFEED9AFDEC998D +:10088000FC0FD5F0D6E4CEFBE4CDFAE4CCF9A88297 +:1008900022B800C1B90059BA002DEC8BF084CFCE3C +:1008A000CDFCE5F0CBF97818EF2FFFEE33FEED33FA +:1008B000FDEC33FCEB33FB10D703994004EB99FBC1 +:1008C0000FD8E5E4F9FA227818EF2FFFEE33FEEDAA +:1008D00033FDEC33FCC933C910D7059BE99A4007B7 +:1008E000EC9BFCE99AF90FD8E0E4C9FAE4CCFB22CE +:1008F00075F010EF2FFFEE33FEED33FDCC33CCC897 +:1009000033C810D7079BEC9AE899400AED9BFDECA1 +:100910009AFCE899F80FD5F0DAE4CDFBE4CCFAE4E0 +:10092000C8F922A42582F582E5F03583F58322D02B +:1009300083D082F8E4937012740193700DA3A39393 +:10094000F8740193F5828883E4737402936860EF0E +:10095000A3A3A380DFEF4E6012EF60010EEDBB0199 +:100960000B89828A83F0A3DFFCDEFA2289F050072C +:10097000F709DFFCA9F022BBFEFCF309DFFCA9F0BC +:10098000228E268F27E4F528C3E5279464E5269474 +:10099000005017E4F528E528120B922469F8E4F6D4 +:1009A0000528E528B40FEFC2902290008AE0147069 +:1009B00003020A6F046003020B4C7866E6C4540F0E +:1009C000F97065D3E5279494E52694115003020B42 +:1009D0004C300003020B4CE9120C32E6540FFC08B9 +:1009E000E6FDEC4E18F6ED08F618120C29EC540F43 +:1009F0004E18F6ED08F6900001E526F0A3E527F085 +:100A0000AE26FF7C007D1F1207B290008BEEF0A394 +:100A1000EFF07C007D031207A0A3EEF0A3EFF0A39C +:100A2000E526F0A3E527F0227866E6C4540F6402B9 +:100A30006003020B4C120CEB752C01752D00752E0A +:100A40008B901BD793FE7401938E2FF530901BD99A +:100A5000E493F531A3120BBB8E32F533901BDCE42B +:100A600093F534901BE0E493F535E4FF020587E449 +:100A7000F528120BA8120D0CFFE528120C54F9EF03 +:100A8000C399506EE528120C16F5828C832FF582DF +:100A9000E43583F583E493FF120C90300001B350EA +:100AA00003020B40E528C454F024D1F582E4341B42 +:100AB000120C20F5828C83EF540775F00212092383 +:100AC000120C22FDAF27AE261214CBE528501925B3 +:100AD000E025E02466F8120C29EC540F4EFEEDFFE1 +:100AE000120BA8120C9A8058120B922469F8E4F6A3 +:100AF000804E120BA8120D0C697045120CEBE52804 +:100B0000120BAFAA06752CFF8A2DF52EE528120CC4 +:100B1000F6120BB98E2FF530E528120C63F531E58E +:100B200028120D01120BB98E32F533E528120C7222 +:100B3000F534E528120C81F535AF28120587400CF5 +:100B40000528E528C3940F5003020A7222C290E5DB +:100B50002925E025E02466F8E4F608F6E52925E0F5 +:100B600025E02468F8E4F6E52925E025E022F58370 +:100B7000E493FF5408FE131313541F24FF9202AB97 +:100B800029AA2AA92BEF540775F002A4F58285F053 +:100B9000832225E025E02466F8E4F608F6E528251A +:100BA000E025E02468F8E4F6E52825E025E022C405 +:100BB00054F024D1F582E4341BF583E493FE7401F0 +:100BC00093222466F8E6FC08E6FDECC4F854F0C86D +:100BD000EDC4540F48540F222468F8E6141313137D +:100BE000541F2403F582E43400F58322E529252BE4 +:100BF000F582E43528F583E022A200E433C43333E0 +:100C00003354804526FFE527900074CFF0A3EFF022 +:100C1000E490007BF022C454F024D4F582E4341B29 +:100C2000F583E493FC74019322E6FC08E6FDECC432 +:100C3000540F2401FFEFC454F0FE22E52E25E024DA +:100C40008BF582E43400F58322900085E0FF90006C +:100C50007AE06F22C454F024D6F582E4341BF58385 +:100C6000E49322C454F024D9F582E4341BF583E4E0 +:100C70009322C454F024DCF582E4341BF583E4931E +:100C800022C454F024E0F582E4341BF583E493227B +:100C90005408131313541F24FF222466F8A60608D1 +:100CA000A607222530F582E4352FF583E49322242C +:100CB00066F8E6FC08E6FDEC540F222466F9E7C46A +:100CC000F854F0C809E7222533F582E43532F5837C +:100CD000E4932290007AE0900085F053DAFE22251A +:100CE000E0247DF582E43400F58322A200920185A0 +:100CF000262A85272B22C454F024D7F582E4341BFE +:100D000022C454F024DAF582E4341B222466F8E687 +:100D1000FEEEC4540F228E268F27C3E5279464E588 +:100D20002694005003020EAA900087E024FE60255E +:100D3000147003020DB724036003020EAFC290AF1C +:100D400027AE2612185A4003020EAF120BF990007C +:100D5000877402F022120EB0503090007BE09404B1 +:100D60004021D290E4900000F0900073F0C2049013 +:100D7000007CF090007AF0900003F09000877403FC +:100D8000F08025E4900087F0801E900074E0547F8E +:100D9000FEA3E0FFD3E5279FE5269E4005120BF951 +:100DA000800690007BE004F090007BE0C394E0506C +:100DB00003020EAF020EAA90007CE004F090007BCC +:100DC000E0FFA3E0D39F4003020E587B007A007936 +:100DD00028AF27AE261212DF4022900000E0FF125B +:100DE0000C3DE526F0A3E527F08F28900000E004F5 +:100DF000F0E0D394074005E4900087F030041DA292 +:100E000000E433C43333335480FFE528C454F04F37 +:100E1000FF900073E0120BE2EFF08039900073E076 +:100E2000FF120BE2E0FEA200E43333333354F84503 +:100E300028FDEE4DF074032F120BE4120C4CFF1240 +:100E4000182DEFF0900073E004F0E0D394704005AB +:100E5000E4900087F0B20422120EB0504D1218E355 +:100E60005005E4900085F0120C49603A1218F37DA9 +:100E7000207C037F017E0012176E120CD3900003BA +:100E8000E0FD900074E05480FF900000E0C454F056 +:100E90004FFFED4F900003F0900074E0547FF0900E +:100EA0000086E04480F0C2908000E4900087F02249 +:100EB000A2009201AF27AE26121744228B298A2A5C +:100EC000892B8C2C8D2DE4F53D753E80F53BE53B63 +:100ED000C3952E5010E52D253BF582E4352C120FDD +:100EE0006C053B80E9E4F53BE53BC395385057E59D +:100EF0003A253DF582E43539F583E0553E7019F524 +:100F00003CE53CC39531502DE530253CF582E43578 +:100F10002F120F6C053C80E9E4F53CE53CC39534A9 +:100F20005013E533253CF582E43532120B6E120F77 +:100F30009B053C80E6E53EC313F53E7005053D7517 +:100F40003E80053B80A2E4F53BE53BC3953750135B +:100F5000E536253BF582E43535120B6E120F9B0505 +:100F60003B80E6C2909000877405F022F583E493FD +:100F7000FF5408FE131313541F24FF9202AB29AA37 +:100F80002AA92BEF540775F002A4F58285F083128D +:100F9000081DF54085F03F1200032212081DF540A0 +:100FA00085F03F120003228E268F278C288D298BF7 +:100FB0002AD200C201852982852883E05488D394EF +:100FC000004003D38001C39201E4F52BE52BC395C8 +:100FD0002A505930010D120BECC413131354012481 +:100FE000FF8002A2009202852782852683C083C0EB +:100FF00082120BECFFC45407D082D083121035301C +:10100000010C120BECFF131313541F138002A200E8 +:101010009202852782852683C083C082120BEC54FE +:1010200007D082D083121035052B80A0C29090008B +:10103000877405F02275F002120923E0F53FA3E062 +:10104000F540120003920022C0E0C0F0C083C082CD +:10105000C0D075D000C000C001C002C003C004C031 +:1010600005C006C007E5985403F545F45298E545D8 +:1010700030E01712192B9000DD121739EFF09000B5 +:10108000DDE004F0E0B44002E4F0E54530E12E900C +:1010900000E0E0D39400401A9000DCE02446F8E63B +:1010A000FF1219289000DCE004F09000E0E014F05A +:1010B0008002D2059000DCE0B42002E4F0D007D03A +:1010C00006D005D004D003D002D001D000D0D0D0BB +:1010D00082D083D0F0D0E03212005A787FE4F6D884 +:1010E000FD7581A1021122020076E493A3F8E49336 +:1010F000A34003F68001F208DFF48029E493A3F80B +:101100005407240CC8C333C4540F4420C88340047C +:10111000F456800146F6DFE4800B0102040810203B +:101120004080901266E47E019360BCA3FF543F3080 +:10113000E509541FFEE493A360010ECF54C025E0DF +:1011400060A840B8E493A3FAE493A3F8E493A3C897 +:10115000C582C8CAC583CAF0A3C8C582C8CAC58328 +:10116000CADFE9DEE780BEC0E0C0F0C083C082C055 +:10117000D075D000C000C001C002C003C004C005CB +:10118000C006C007E5D85487F521F452D8E5F730FA +:10119000E508E5F730E60312193253F7DFE52130B1 +:1011A000E708E5D930E003121931E52130E008E520 +:1011B000DA30E003121675E52130E108E5DB30E0B6 +:1011C00003121933E52130E208E5DC30E00312199F +:1011D00034D007D006D005D004D003D002D001D03F +:1011E00000D0D0D082D083D0F0D0E032AE07E4F58A +:1011F0002612180A900000E004FF12181112125F64 +:10120000900000E0FFE526C39F501412172BE05416 +:101210007FFF12181112172B121725052680E19057 +:101220000074E0547FFF12181190007412172512F9 +:10123000125FE4F526900073E0FFE526C39F501788 +:10124000740325261217190526E526541F70E61289 +:10125000192512191E80DE7F551218110219251248 +:10126000192512191E224200E500004200E100008B +:101270004200E700004200E30000C1834100860015 +:101280004100870041008A004100790042000100CE +:101290000042008800004200770000C10441007352 +:1012A000004100850041007A004100000048007DB7 +:1012B0000000000000000000410076004100DD0059 +:1012C0004100DF004100DB004100DC004100E000A4 +:1012D0004100DA00C1054100DE0041009900008EA6 +:1012E000298F2A8B2B8A2C892DE4F52E900000E083 +:1012F000FFE52EC39F505EE52AAE297803CEC313C7 +:10130000CE13D8F9FDAC06E52AAE297802CEC31378 +:10131000CE13D8F92DFFEE3CAB07FA120C3BE0FEE2 +:10132000A3E0FFC39BEE9A50028004AE02AF03AA73 +:1013300006AB07120C3BE0FCA3E0FDAF2AAE29127E +:1013400017E7500DAB2BAA2CA92DE52E12078ED333 +:1013500022052E8097C3228F268C278D28EF120B13 +:10136000AFAA06F97BFFEF120C16FDEF120C54F535 +:101370002EEF120CF6120BB98E2FF530EF120C6314 +:10138000F531EF120D01120BB98E32F533EF120C5D +:1013900072F534EFC454F024DDF582E4341B120BF3 +:1013A000B98E35F536EFC454F024DFF582E4341BF2 +:1013B000F583E493F537EF120C81F53885273985ED +:1013C000283A020EBC900076E0FDC4540F2401FBC5 +:1013D000E433FAED540FF96B7001EA603DE97010E7 +:1013E000E0C4540F2401FDE433FCED64044C602A96 +:1013F000900076E0C4540FFD540F120CDFEEF0A302 +:10140000EFF0ED04C454F049900076F0E0FFC454CE +:101410000FC394044004EF540FF022C0E0C083C017 +:1014200082C0D075D000C004C005C006C00753C834 +:101430007F9000E5E0FEA3E0FF4E700353C8FB90F1 +:1014400000E112166A50099000E5E4F0A3F0800D67 +:10145000C39000E6E09DF09000E5E09CF0D007D05E +:1014600006D005D004D0D0D082D083D0E032C0E006 +:10147000C083C082C0D075D000C004C005C006C003 +:101480000753917F9000E7E0FEA3E0FF4E70035307 +:1014900091FB9000E312166A50099000E7E4F0A374 +:1014A000F0800DC39000E8E09DF09000E7E09CF034 +:1014B000D007D006D005D004D0D0D082D083D0E0E1 +:1014C0003212081DFDACF0AF2BAE2A8F828E83AF97 +:1014D00005AE041218A6AB07AA06D3EB94F4EA945F +:1014E0000140067E017FF48004AE02AF03AA06AB82 +:1014F00007C3EB9464EA940050067E007F64800486 +:10150000AE02AF03AA06AB07AF82AE831217E72283 +:10151000AD07AC06ABDA900076E0FEC4540FFFEEE8 +:10152000540FFEB50702C32253DAFE900076E0FAAC +:10153000EE120CDFE0FFA3E08D828C83CFF0A3EFEF +:10154000F0EA54F0FF900076E0044FF0540FC3949B +:10155000044004E054F0F08BDAD322AE05AD07E48A +:10156000FCFB7FAA121811AF05121811EEC454F03B +:1015700024A6F582E4341DF583E493FFECC39F5069 +:101580000774082CFC0B80F4EB04FF12180CE4FC2D +:10159000ECC39B500974032C1217190C80F27F5571 +:1015A0001218110219258F26900079E0F5277E0088 +:1015B0007F3C7D007B007A0079661209557F0B1213 +:1015C000192E43DA011200707D0A7C007F017E0033 +:1015D00012179712001DE4900087F0900086F0909B +:1015E0000099E526F0900079F0AF2722AE07E4FDE0 +:1015F000F52612180A900001E0FF12181190000160 +:10160000121725900077E0FF12181190007712173B +:1016100025900088E0FF1218119000881217257499 +:10162000032D1217190DBD03F67F5512181102195B +:1016300025AB07AA06E4F9F87F407E427D0FFC1235 +:101640000891A804A905AA06AB077F207ED77D755F +:101650007C01120891C3E49FFFE49EFE22AB07AA1F +:1016600006E4F9F87FE87E03FD22E0FCA3E0FDC379 +:10167000EF9DEE9C22AFFBAEFC7C007D0A1207A022 +:10168000AD07AC06AFD953D9BFE4F5FAF5F98FD958 +:10169000C3EC948050157F002093027F01EFC43388 +:1016A000333354804CFEEDFF0213C5E4900076F016 +:1016B000229000DDE0FF9000DBE0B507057E017FB2 +:1016C00000229000DB121739E0FD7C009000DBE087 +:1016D00004F0E0B44002E4F09000DAE0FEEE4204F0 +:1016E000E4F0AE04AF05229000EDE0FCA3E0FDECD9 +:1016F000547FFEAF0522EC5480C41313135401240D +:10170000FF22A3E493FE74019322E49000EBF0A384 +:10171000F022900087E024FB22F582E43400F58378 +:10172000E0FF021811A3E0FF021811E52625E024CE +:101730008BF582E43400F58322E0249AF582E434C8 +:1017400000F58322AD07AC06900074E0FAA3E0FB3D +:10175000EA5480C413131354017005300102C322EC +:10176000AF05AE04EA547FFCAD031214CB228E37D2 +:101770008F388C398D3A12165D12163E12188290EF +:1017800000E5E539F0A3E53AF09000E1E537F0A394 +:10179000E538F043C804228E288F298C2A8D2B121D +:1017A000165D12163E12188E9000E7E52AF0A3E5AA +:1017B0002BF09000E3E528F0A3E529F04391042203 +:1017C00012002A1218FA1219011218B2121916125E +:1017D00018441218D01218BC1218C612189A1219EE +:1017E0000812191A02190FC3ED9BF582EC9AF583C2 +:1017F000C3E5829FE5839E500FED2BFDEC3AFCC3C1 +:10180000EF9DEE9C50028001C3227FAA121811AFF7 +:1018100006C2059000DFE0B42002E4F09000DFE0B3 +:101820002446F8A607E004F0A3E004F0227E1DE4BD +:10183000FDEF30E70625E06EFF8004EF25E0FF0DA9 +:10184000BD08EE22AF885388AF758CA0758DCBEFA5 +:101850005440FEEF54104E428822C3EF942CEE9475 +:10186000014003D38001C322121875D2039000797E +:10187000E0FF0215A6AE0712180A7F5512181102D2 +:101880001925AD07AC06ECF5CBAF058FCA22AD0725 +:10189000AC06ECF593AF058F9222C2DE75D90575C3 +:1018A000F9FF75960122EF7802CEC313CE13D8F953 +:1018B000FF2275E34075E10175E20122E5915404D0 +:1018C0005391FB429122758E547589224388502290 +:1018D000E5C8540453C8FB42C82253984FEB4F4D00 +:1018E000F59822E5C8C320E201D322E591C320E2A6 +:1018F00001D32253C8FB53C87F2275A41175D4CFDE +:101900002275A54175D5772253F77F75DA30227598 +:10191000E69075A8B022E4F5A9224398102230057C +:10192000FD22C2DE22D299228F9922AF99228F8C7A +:101930002222222222015E041A2A620802080109D8 +:1019400000019004B00BB81C520A03080109000101 +:10195000C20384286E020800090108014A02762A9F +:101960004E020800090108028A1E82071C0F8C081B +:101970000108020803016802D012C005DC0A03084E +:10198000010900024E05DC01AE348A0A0308010990 +:1019900000012C0A00008C047E27F60801080308C9 +:1019A0000208020803080401040A9A051428A00882 +:1019B000010800080208020800080301720438192F +:1019C00082020801080009029407D00FA02328080A +:1019D00003080108020801061802D000D208010A13 +:1019E0000109020A0101D603FC0BFE0208010800EE +:1019F00009019002D0132E010A00090108023007E4 +:101A0000760F322274030801080208193503193BC6 +:101A100002193D02193F020000001819410419493A +:101A200002194B02194D0200000018194F031955F5 +:101A30000219570219590200000018195B031961B5 +:101A4000021963021965020000000C196704196F7E +:101A50000219710219730200000028197504197D1A +:101A600002197F0219810200000028198304198BD2 +:101A700002198D02198F0200000018199105199B97 +:101A800002199D0419A10419A5022019A70419AF70 +:101A90000219B10419B50419B9022019BB0319C1FF +:101AA0000219C30219C5020000001119C70419CF99 +:101AB0000219D10219D30219D5022419D70319DD4D +:101AC0000219DF0219E10219E3022819E50319EBF3 +:101AD0000219ED0219EF020000001419F10319F7C1 +:101AE0000219F90219FB020000002719FD041A056A +:101AF000021A07021A090200000024015E041A2AD1 +:101B000062080208010900019004B00BB81C520AD7 +:101B1000030801090001C20384286E0208000901BC +:101B200008014A02762A4E020800090108028A1EAC +:101B300082071C0F8C080108020803016802D012FA +:101B4000C005DC0A0308010900024E05DC01AE34C1 +:101B50008A0A0308010900012C0A00008C047E2770 +:101B6000F608010803080208020803080401040A31 +:101B70009A051428A00801080008020802080008B5 +:101B8000030172043819820208010800090294074F +:101B9000D00FA02328080308010802080106180234 +:101BA000D000D208010A0109020A0101D603FC0B88 +:101BB000FE020801080009019002D0132E010A005C +:101BC000090108023007760F322274030801080267 +:101BD000081AFB031B01021B03021B050200000085 +:101BE000181B07041B0F021B11021B13020000002D +:101BF000181B15031B1B021B1D021B1F02000000EC +:101C0000181B21031B27021B29021B2B02000000AB +:101C10000C1B2D041B35021B37021B390200000070 +:101C2000281B3B041B43021B45021B47020000000C +:101C3000281B49041B51021B53021B5502000000C4 +:101C4000181B57051B61021B63041B67041B6B02F7 +:101C5000201B6D041B75021B77041B7B041B7F027A +:101C6000201B81031B87021B89021B8B02000000C3 +:101C7000111B8D041B95021B97021B99021B9B02D3 +:101C8000241B9D031BA3021BA5021BA7021BA90269 +:101C9000281BAB031BB1021BB3021BB502000000E3 +:101CA000141BB7031BBD021BBF021BC102000000B7 +:101CB000271BC3041BCB021BCD021BCF020000005D +:101CC00024015E041A2A6208020801090001900436 +:101CD000B00BB81C520A030801090001C203842892 +:101CE0006E020800090108014A02762A4E02080025 +:101CF000090108028A1E82071C0F8C0801080208CD +:101D000003016802D012C005DC0A030801090002C1 +:101D10004E05DC01AE348A0A0308010900012C0AD1 +:101D200000008C047E27F608010803080208020858 +:101D300003080401040A9A051428A00801080008F1 +:101D4000020802080008030172043819820208011F +:101D5000080009029407D00FA023280803080108EF +:101D6000020801061802D000D208010A0109020A7D +:101D70000101D603FC0BFE020801080009019002D4 +:101D8000D0132E010A00090108023007760F322213 +:101D9000740308010802081CC1031CC7021CC90205 +:101DA0001CCB02000000181CCD041CD5021CD7025D +:101DB0001CD902000000181CDB031CE1021CE3021A +:101DC0001CE502000000181CE7031CED021CEF02DA +:101DD0001CF1020000000C1CF3041CFB021CFD02A1 +:101DE0001CFF02000000281D01041D09021D0B023A +:101DF0001D0D02000000281D0F041D17021D1902F1 +:101E00001D1B02000000181D1D051D27021D2904B1 +:101E10001D2D041D3102201D33041D3B021D3D04F8 +:101E20001D41041D4502201D47031D4D021D4F028B +:101E30001D5102000000111D53041D5B021D5D02B7 +:101E40001D5F021D6102241D63031D69021D6B02DB +:101E50001D6D021D6F02281D71031D77021D790281 +:101E60001D7B02000000141D7D031D83021D8502E1 +:101E70001D8702000000271D89041D91021D930289 +:071E80001D95020000002483 +:00000001FF From d57b9acd10a8181f5d279701fae2fbd8ac8baef3 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Wed, 23 Oct 2019 13:13:21 +0200 Subject: [PATCH 040/428] Release 6.7 --- .github/ISSUE_TEMPLATE/Bug_report.md | 81 ++++++++++++++--------- .github/ISSUE_TEMPLATE/Custom.md | 73 ++++++++++++-------- .github/ISSUE_TEMPLATE/Feature_request.md | 16 +++-- .github/PULL_REQUEST_TEMPLATE.md | 7 +- 4 files changed, 107 insertions(+), 70 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/Bug_report.md b/.github/ISSUE_TEMPLATE/Bug_report.md index a0ab5e7e3..0d3d5f7f5 100644 --- a/.github/ISSUE_TEMPLATE/Bug_report.md +++ b/.github/ISSUE_TEMPLATE/Bug_report.md @@ -1,50 +1,67 @@ --- name: Bug report about: Create a report to help us improve - --- - - - - - - - - - - +> **GUIDE** +> +> This BUG issue template is meant to REPORT Tasmota software BUGS ONLY> +> +> Please DO NOT OPEN AN ISSUE: +> - If your Tasmota version is not the latest from the development branch, please update your device before submitting your issue. Your problem might already be solved. The latest precompiled binaries of Tasmota can be downloaded from http://thehackbox.org/tasmota/ +> - If you have a issue when flashing was done via Tuya Convert +> - If your issue is a flashing issue, please address it to the [Tasmota Support Chat](https://discord.gg/Ks2Kzd4) +> - If your issue is compilation problem, please address it to the [Tasmota Support Chat](https://discord.gg/Ks2Kzd4) +> - If your issue has been addressed before (i.e., duplicated issue), please ask in the original issue +> - If your issue is a Wi-Fi problem or MQTT problem, please try the steps provided in the [FAQ](https://github.com/arendst/Sonoff-Tasmota/wiki/FAQ) and troubleshooting wiki articles +> +> Please take a few minutes to complete the requested information below. Our ability to provide assistance is greatly hampered without it. The details requested potentially affect which options to pursue. The small amount of time you spend completing the template will also help the volunteers providing the assistance to you to reduce the time required to help you. ### BUG DESCRIPTION _A clear and concise description of what the bug is._ ### REQUESTED INFORMATION -_Make sure these boxes are checked before submitting your issue. Thank you_ +_Make sure your have performed every step and checked the applicable boxes before submitting your issue. Thank you!_ **FAILURE TO COMPLETE THE REQUESTED INFORMATION WILL RESULT IN YOUR ISSUE BEING CLOSED** - [ ] Read the [Contributing Guide and Policy](https://github.com/arendst/Sonoff-Tasmota/blob/development/CONTRIBUTING.md) and [the Code of Conduct](https://github.com/arendst/Sonoff-Tasmota/blob/development/CODE_OF_CONDUCT.md) -- [ ] Searched the problem in issues (https://github.com/arendst/Sonoff-Tasmota/issues) -- [ ] Searched the problem in the wiki (https://github.com/arendst/Sonoff-Tasmota/wiki/Troubleshooting) -- [ ] Searched the problem in the forum (https://groups.google.com/d/forum/sonoffusers) -- [ ] Searched the problem in the chat (https://discord.gg/Ks2Kzd4) -- [ ] Device used (i.e. Sonoff Basic) : _____ -- [ ] Tasmota binary firmware version number used : ____ / (pre-compiled or self-compiled ?) -- [ ] Development IDE - Compiler / Upload tools used : ____ / ____ -- [ ] Provide the output of command ``status 0`` : -``` -STATUS 0 OUTPUT HERE: - - -``` -- [ ] Provide the output of console when you experience your issue if apply : -_(Please use_ ``weblog 4`` _for more debug information)_ -``` -CONSOLE OUTPUT HERE: - - -``` +- [ ] Searched the problem in [issues](https://github.com/arendst/Sonoff-Tasmota/issues) +- [ ] Searched the problem in the [wiki](https://github.com/arendst/Sonoff-Tasmota/wiki/Troubleshooting) +- [ ] Searched the problem in the [forum](https://groups.google.com/d/forum/sonoffusers) +- [ ] Searched the problem in the [chat](https://discord.gg/Ks2Kzd4) +- [ ] Device used (e.g., Sonoff Basic): _____ +- [ ] Tasmota binary firmware version number used: _____ + - [ ] Pre-compiled + - [ ] Self-compiled + - [ ] IDE / Compiler used: _____ +- [ ] Flashing tools used: _____ +- [ ] Provide the output of command: ``Backlog Template; Module; GPIO``: + ``` + Configuration output here: + + + ``` +- [ ] If using rules, provide the output of this command: ``Backlog Rule1; Rule2; Rule3``: + ``` + Rules output here: + + + ``` +- [ ] Provide the output of this command: ``Status 0``: + ``` + STATUS 0 output here: + + + ``` +- [ ] Provide the output of the Console log output when you experience your issue; if applicable: + _(Please use_ ``weblog 4`` _for more debug information)_ + ``` + Console output here: + + + ``` ### TO REPRODUCE _Steps to reproduce the behavior:_ diff --git a/.github/ISSUE_TEMPLATE/Custom.md b/.github/ISSUE_TEMPLATE/Custom.md index 44bd847cb..f0d6fafe0 100644 --- a/.github/ISSUE_TEMPLATE/Custom.md +++ b/.github/ISSUE_TEMPLATE/Custom.md @@ -1,21 +1,22 @@ --- name: Troubleshooting about: Users Troubleshooting Help - --- - - - - - - - - - - - - +> **GUIDE** +> +> This troubleshooting issue template is meant to help Tasmota users with difficult problems. It is aimed to be opened if using the wiki and the support chat could not solve the issue. The Github Issue tracker is NOT a general discussion forum! +> +> Please DO NOT OPEN AN ISSUE: +> - If you have general questions or you need help on Tasmota usage, go to the [Tasmota Support Chat](https://discord.gg/Ks2Kzd4) +> - If your Tasmota version is not the latest from the development branch, please update your device before submitting your issue. Your problem might already be solved. The latest precompiled binaries of Tasmota can be downloaded from http://thehackbox.org/tasmota/ +> - If your issue is about a new device, please use the Tasmota [Template](../wiki/Templates) feature. +> - If your issue is a flashing issue, please address it to the [Tasmota Support Chat](https://discord.gg/Ks2Kzd4) +> - If your issue is compilation problem, please address it to the [Tasmota Support Chat](https://discord.gg/Ks2Kzd4) +> - If your issue has been addressed before (i.e., duplicated issue), please ask in the original issue +> - If your issue is a Wi-Fi problem or MQTT problem, please try the steps provided in the [FAQ](https://github.com/arendst/Sonoff-Tasmota/wiki/FAQ) and troubleshooting wiki articles +> +> Please take a few minutes to complete the requested information below. Our ability to provide assistance is greatly hampered without it. The details requested potentially affect which options to pursue. The small amount of time you spend completing the template will also help the volunteers providing the assistance to you to reduce the time required to help you. ### ISSUE DESCRIPTION - TROUBLESHOOTING _A clear description of what the issue is and be as extensive as possible_ @@ -31,20 +32,36 @@ _Make sure these boxes are checked before submitting your issue. Thank you_ - [ ] Searched the problem in the wiki (https://github.com/arendst/Sonoff-Tasmota/wiki/Troubleshooting) - [ ] Searched the problem in the forum (https://groups.google.com/d/forum/sonoffusers) - [ ] Searched the problem in the chat (https://discord.gg/Ks2Kzd4) -- [ ] Device used (i.e. Sonoff Basic) : _____ -- [ ] Tasmota binary firmware version number used : ____ / (pre-compiled or self-compiled ?) -- [ ] Development IDE - Compiler / Upload tools used : ____ / ____ -- [ ] Provide the output of command ``status 0`` : -``` -STATUS 0 OUTPUT HERE: +- [ ] Device used (e.g., Sonoff Basic): _____ +- [ ] Tasmota binary firmware version number used: _____ + - [ ] Pre-compiled + - [ ] Self-compiled + - [ ] IDE / Compiler used: _____ +- [ ] Flashing tools used: _____ +- [ ] Provide the output of this command: ``Backlog Template; Module; GPIO``: + ``` + Configuration output here: + + + ``` +- [ ] If using rules, provide the output of this command: ``Backlog Rule1; Rule2; Rule3``: + ``` + Rules output here: + + + ``` +- [ ] Provide the output of this command: ``Status 0``: + ``` + STATUS 0 output here: + + + ``` +- [ ] Provide the output of the Console log output when you experience your issue; if applicable: + _(Please use_ ``weblog 4`` _for more debug information)_ + ``` + Console output here: + + + ``` - -``` -- [ ] Provide the output of console when you experience your issue if apply : -_(Please use_ ``weblog 4`` _for more debug information)_ -``` -CONSOLE OUTPUT HERE: - - -``` **(Please, remember to close the issue when the problem has been addressed)** diff --git a/.github/ISSUE_TEMPLATE/Feature_request.md b/.github/ISSUE_TEMPLATE/Feature_request.md index 1c0989566..5aa100e01 100644 --- a/.github/ISSUE_TEMPLATE/Feature_request.md +++ b/.github/ISSUE_TEMPLATE/Feature_request.md @@ -1,20 +1,22 @@ --- name: Feature request about: Suggest an idea for this project - --- -**Have you look for this feature in other issues and in the wiki?** -**Is your feature request related to a problem? Please describe.** +> Please take a few minutes to complete the requested information below. Our ability to provide assistance is greatly hampered without it. The details requested potentially affect which options to pursue. The small amount of time you spend completing the template will also help the volunteers providing the assistance to you to reduce the time required to help you. + +**Have you looked for this feature in other issues and in the wiki?** + +**Is your feature request related to a problem? Please describe.** _A clear and concise description of what the problem is._ -**Describe the solution you'd like** +**Describe the solution you'd like** _A clear and concise description of what you want to happen._ -**Describe alternatives you've considered** +**Describe alternatives you've considered** _A clear and concise description of any alternative solutions or features you've considered._ -**Additional context** +**Additional context** _Add any other context or screenshots about the feature request here._ -**(Please, remember to close the issue when the problem has been addressed)** +**(Please, remember to close the issue when the problem has been addressed)** diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index aea7cd816..53b64f906 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -3,8 +3,9 @@ **Related issue (if applicable):** fixes # ## Checklist: - - [ ] The pull request is done against the dev branch - - [ ] Only relevant files were touched (Also beware if your editor has auto-formatting feature enabled) + - [ ] The pull request is done against the latest dev branch + - [ ] Only relevant files were touched - [ ] Only one feature/fix was added per PR. - - [ ] The code change is tested and works. + - [ ] The code change is tested and works on core pre-2.6 - [ ] The code change pass travis tests. **Your PR cannot be merged unless tests pass** + - [ ] I accept the [CLA](https://github.com/arendst/Sonoff-Tasmota/blob/development/CONTRIBUTING.md#contributor-license-agreement-cla). From f3b80ba5022b0ce76bc1ca9b255410b289add184 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Wed, 23 Oct 2019 14:05:14 +0200 Subject: [PATCH 041/428] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 59cce39ab..51165cc14 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Alternative firmware for _ESP8266 based devices_ like [iTead](https://www.itead. [![GitHub version](https://img.shields.io/github/release/arendst/Sonoff-Tasmota.svg)](https://github.com/arendst/Sonoff-Tasmota/releases/latest) [![GitHub download](https://img.shields.io/github/downloads/arendst/Sonoff-Tasmota/total.svg)](https://github.com/arendst/Sonoff-Tasmota/releases/latest) -[![License](https://img.shields.io/github/license/arendst/Sonoff-Tasmota.svg)](https://github.com/arendst/Sonoff-Tasmota/blob/development/LICENSE.txt) +[![License](https://img.shields.io/github/license/arendst/Sonoff-Tasmota.svg)](https://github.com/arendst/Sonoff-Tasmota/blob/master/LICENSE.txt) [![Chat](https://img.shields.io/discord/479389167382691863.svg)](https://discord.gg/Ks2Kzd4) If you like **Sonoff-Tasmota**, give it a star, or fork it, and contribute! @@ -14,12 +14,12 @@ If you like **Sonoff-Tasmota**, give it a star, or fork it, and contribute! [![GitHub forks](https://img.shields.io/github/forks/arendst/Sonoff-Tasmota.svg?style=social&label=Fork)](https://github.com/arendst/Sonoff-Tasmota/network) [![donate](https://img.shields.io/badge/donate-PayPal-blue.svg)](https://paypal.me/tasmota) -See [RELEASENOTES.md](https://github.com/arendst/Sonoff-Tasmota/blob/development/RELEASENOTES.md) for release information. +See [RELEASENOTES.md](https://github.com/arendst/Sonoff-Tasmota/blob/master/RELEASENOTES.md) for release information. In addition to the [release webpage](https://github.com/arendst/Sonoff-Tasmota/releases/latest) the binaries can also be downloaded from http://thehackbox.org/tasmota/release/ ## Development -[![Dev Version](https://img.shields.io/badge/development%20version-v6.6.0.x-blue.svg)](https://github.com/arendst/Sonoff-Tasmota) +[![Dev Version](https://img.shields.io/badge/development%20version-v6.7.0.x-blue.svg)](https://github.com/arendst/Sonoff-Tasmota) [![Download Dev](https://img.shields.io/badge/download-development-yellow.svg)](http://thehackbox.org/tasmota/) [![Build Status](https://img.shields.io/travis/arendst/Sonoff-Tasmota.svg)](https://travis-ci.org/arendst/Sonoff-Tasmota) From 7ace5d4058ba8c154e3e8e912db78f35efd7b74d Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Wed, 23 Oct 2019 14:11:13 +0200 Subject: [PATCH 042/428] Remove development files --- Doxyfile | 2489 ------------------------------------------------- updateDocs.sh | 9 - 2 files changed, 2498 deletions(-) delete mode 100644 Doxyfile delete mode 100644 updateDocs.sh diff --git a/Doxyfile b/Doxyfile deleted file mode 100644 index 2df4d1c2c..000000000 --- a/Doxyfile +++ /dev/null @@ -1,2489 +0,0 @@ -# Doxyfile 1.8.14 - -# This file describes the settings to be used by the documentation system -# doxygen (www.doxygen.org) for a project. -# -# All text after a double hash (##) is considered a comment and is placed in -# front of the TAG it is preceding. -# -# All text after a single hash (#) is considered a comment and will be ignored. -# The format is: -# TAG = value [value, ...] -# For lists, items can also be appended using: -# TAG += value [value, ...] -# Values that contain spaces should be placed between quotes (\" \"). - -#--------------------------------------------------------------------------- -# Project related configuration options -#--------------------------------------------------------------------------- - -# This tag specifies the encoding used for all characters in the config file -# that follow. The default is UTF-8 which is also the encoding used for all text -# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv -# built into libc) for the transcoding. See -# https://www.gnu.org/software/libiconv/ for the list of possible encodings. -# The default value is: UTF-8. - -DOXYFILE_ENCODING = UTF-8 - -# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by -# double-quotes, unless you are using Doxywizard) that should identify the -# project for which the documentation is generated. This name is used in the -# title of most generated pages and in a few other places. -# The default value is: My Project. - -PROJECT_NAME = Sonoff-Tasmota - -# The PROJECT_NUMBER tag can be used to enter a project or revision number. This -# could be handy for archiving the generated documentation or if some version -# control system is used. - -PROJECT_NUMBER = - -# Using the PROJECT_BRIEF tag one can provide an optional one line description -# for a project that appears at the top of each page and should give viewer a -# quick idea about the purpose of the project. Keep the description short. - -PROJECT_BRIEF = "Provide ESP8266 based itead Sonoff with Web, MQTT and OTA firmware using Arduino IDE or PlatformIO" - -# With the PROJECT_LOGO tag one can specify a logo or an icon that is included -# in the documentation. The maximum height of the logo should not exceed 55 -# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy -# the logo to the output directory. - -PROJECT_LOGO = - -# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path -# into which the generated documentation will be written. If a relative path is -# entered, it will be relative to the location where doxygen was started. If -# left blank the current directory will be used. - -OUTPUT_DIRECTORY = ./ - -# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- -# directories (in 2 levels) under the output directory of each output format and -# will distribute the generated files over these directories. Enabling this -# option can be useful when feeding doxygen a huge amount of source files, where -# putting all generated files in the same directory would otherwise causes -# performance problems for the file system. -# The default value is: NO. - -CREATE_SUBDIRS = NO - -# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII -# characters to appear in the names of generated files. If set to NO, non-ASCII -# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode -# U+3044. -# The default value is: NO. - -ALLOW_UNICODE_NAMES = NO - -# The OUTPUT_LANGUAGE tag is used to specify the language in which all -# documentation generated by doxygen is written. Doxygen will use this -# information to generate all constant output in the proper language. -# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, -# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), -# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, -# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), -# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, -# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, -# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, -# Ukrainian and Vietnamese. -# The default value is: English. - -OUTPUT_LANGUAGE = English - -# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member -# descriptions after the members that are listed in the file and class -# documentation (similar to Javadoc). Set to NO to disable this. -# The default value is: YES. - -BRIEF_MEMBER_DESC = YES - -# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief -# description of a member or function before the detailed description -# -# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the -# brief descriptions will be completely suppressed. -# The default value is: YES. - -REPEAT_BRIEF = YES - -# This tag implements a quasi-intelligent brief description abbreviator that is -# used to form the text in various listings. Each string in this list, if found -# as the leading text of the brief description, will be stripped from the text -# and the result, after processing the whole list, is used as the annotated -# text. Otherwise, the brief description is used as-is. If left blank, the -# following values are used ($name is automatically replaced with the name of -# the entity):The $name class, The $name widget, The $name file, is, provides, -# specifies, contains, represents, a, an and the. - -ABBREVIATE_BRIEF = "The $name class" \ - "The $name widget" \ - "The $name file" \ - is \ - provides \ - specifies \ - contains \ - represents \ - a \ - an \ - the - -# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then -# doxygen will generate a detailed section even if there is only a brief -# description. -# The default value is: NO. - -ALWAYS_DETAILED_SEC = NO - -# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all -# inherited members of a class in the documentation of that class as if those -# members were ordinary class members. Constructors, destructors and assignment -# operators of the base classes will not be shown. -# The default value is: NO. - -INLINE_INHERITED_MEMB = YES - -# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path -# before files name in the file list and in the header files. If set to NO the -# shortest path that makes the file name unique will be used -# The default value is: YES. - -FULL_PATH_NAMES = NO - -# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. -# Stripping is only done if one of the specified strings matches the left-hand -# part of the path. The tag can be used to show relative paths in the file list. -# If left blank the directory from which doxygen is run is used as the path to -# strip. -# -# Note that you can specify absolute paths here, but also relative paths, which -# will be relative from the directory where doxygen is started. -# This tag requires that the tag FULL_PATH_NAMES is set to YES. - -STRIP_FROM_PATH = - -# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the -# path mentioned in the documentation of a class, which tells the reader which -# header file to include in order to use a class. If left blank only the name of -# the header file containing the class definition is used. Otherwise one should -# specify the list of include paths that are normally passed to the compiler -# using the -I flag. - -STRIP_FROM_INC_PATH = - -# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but -# less readable) file names. This can be useful is your file systems doesn't -# support long names like on DOS, Mac, or CD-ROM. -# The default value is: NO. - -SHORT_NAMES = NO - -# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the -# first line (until the first dot) of a Javadoc-style comment as the brief -# description. If set to NO, the Javadoc-style will behave just like regular Qt- -# style comments (thus requiring an explicit @brief command for a brief -# description.) -# The default value is: NO. - -JAVADOC_AUTOBRIEF = NO - -# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first -# line (until the first dot) of a Qt-style comment as the brief description. If -# set to NO, the Qt-style will behave just like regular Qt-style comments (thus -# requiring an explicit \brief command for a brief description.) -# The default value is: NO. - -QT_AUTOBRIEF = NO - -# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a -# multi-line C++ special comment block (i.e. a block of //! or /// comments) as -# a brief description. This used to be the default behavior. The new default is -# to treat a multi-line C++ comment block as a detailed description. Set this -# tag to YES if you prefer the old behavior instead. -# -# Note that setting this tag to YES also means that rational rose comments are -# not recognized any more. -# The default value is: NO. - -MULTILINE_CPP_IS_BRIEF = NO - -# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the -# documentation from any documented member that it re-implements. -# The default value is: YES. - -INHERIT_DOCS = YES - -# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new -# page for each member. If set to NO, the documentation of a member will be part -# of the file/class/namespace that contains it. -# The default value is: NO. - -SEPARATE_MEMBER_PAGES = NO - -# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen -# uses this value to replace tabs by spaces in code fragments. -# Minimum value: 1, maximum value: 16, default value: 4. - -TAB_SIZE = 4 - -# This tag can be used to specify a number of aliases that act as commands in -# the documentation. An alias has the form: -# name=value -# For example adding -# "sideeffect=@par Side Effects:\n" -# will allow you to put the command \sideeffect (or @sideeffect) in the -# documentation, which will result in a user-defined paragraph with heading -# "Side Effects:". You can put \n's in the value part of an alias to insert -# newlines (in the resulting output). You can put ^^ in the value part of an -# alias to insert a newline as if a physical newline was in the original file. - -ALIASES = "abstract=" \ - "access=" \ - "subpackage=" \ - "license=copyright" \ - "description=" \ - "see=" - -# This tag can be used to specify a number of word-keyword mappings (TCL only). -# A mapping has the form "name=value". For example adding "class=itcl::class" -# will allow you to use the command class in the itcl::class meaning. - -TCL_SUBST = - -# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources -# only. Doxygen will then generate output that is more tailored for C. For -# instance, some of the names that are used will be different. The list of all -# members will be omitted, etc. -# The default value is: NO. - -OPTIMIZE_OUTPUT_FOR_C = YES - -# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or -# Python sources only. Doxygen will then generate output that is more tailored -# for that language. For instance, namespaces will be presented as packages, -# qualified scopes will look different, etc. -# The default value is: NO. - -OPTIMIZE_OUTPUT_JAVA = NO - -# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran -# sources. Doxygen will then generate output that is tailored for Fortran. -# The default value is: NO. - -OPTIMIZE_FOR_FORTRAN = NO - -# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL -# sources. Doxygen will then generate output that is tailored for VHDL. -# The default value is: NO. - -OPTIMIZE_OUTPUT_VHDL = NO - -# Doxygen selects the parser to use depending on the extension of the files it -# parses. With this tag you can assign which parser to use for a given -# extension. Doxygen has a built-in mapping, but you can override or extend it -# using this tag. The format is ext=language, where ext is a file extension, and -# language is one of the parsers supported by doxygen: IDL, Java, Javascript, -# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: -# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: -# Fortran. In the later case the parser tries to guess whether the code is fixed -# or free formatted code, this is the default for Fortran type files), VHDL. For -# instance to make doxygen treat .inc files as Fortran files (default is PHP), -# and .f files as C (default is Fortran), use: inc=Fortran f=C. -# -# Note: For files without extension you can use no_extension as a placeholder. -# -# Note that for custom extensions you also need to set FILE_PATTERNS otherwise -# the files are not read by doxygen. - -EXTENSION_MAPPING = ino=C - -# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments -# according to the Markdown format, which allows for more readable -# documentation. See http://daringfireball.net/projects/markdown/ for details. -# The output of markdown processing is further processed by doxygen, so you can -# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in -# case of backward compatibilities issues. -# The default value is: YES. - -MARKDOWN_SUPPORT = YES - -# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up -# to that level are automatically included in the table of contents, even if -# they do not have an id attribute. -# Note: This feature currently applies only to Markdown headings. -# Minimum value: 0, maximum value: 99, default value: 0. -# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. - -TOC_INCLUDE_HEADINGS = 0 - -# When enabled doxygen tries to link words that correspond to documented -# classes, or namespaces to their corresponding documentation. Such a link can -# be prevented in individual cases by putting a % sign in front of the word or -# globally by setting AUTOLINK_SUPPORT to NO. -# The default value is: YES. - -AUTOLINK_SUPPORT = YES - -# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want -# to include (a tag file for) the STL sources as input, then you should set this -# tag to YES in order to let doxygen match functions declarations and -# definitions whose arguments contain STL classes (e.g. func(std::string); -# versus func(std::string) {}). This also make the inheritance and collaboration -# diagrams that involve STL classes more complete and accurate. -# The default value is: NO. - -BUILTIN_STL_SUPPORT = NO - -# If you use Microsoft's C++/CLI language, you should set this option to YES to -# enable parsing support. -# The default value is: NO. - -CPP_CLI_SUPPORT = NO - -# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: -# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen -# will parse them like normal C++ but will assume all classes use public instead -# of private inheritance when no explicit protection keyword is present. -# The default value is: NO. - -SIP_SUPPORT = NO - -# For Microsoft's IDL there are propget and propput attributes to indicate -# getter and setter methods for a property. Setting this option to YES will make -# doxygen to replace the get and set methods by a property in the documentation. -# This will only work if the methods are indeed getting or setting a simple -# type. If this is not the case, or you want to show the methods anyway, you -# should set this option to NO. -# The default value is: YES. - -IDL_PROPERTY_SUPPORT = YES - -# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC -# tag is set to YES then doxygen will reuse the documentation of the first -# member in the group (if any) for the other members of the group. By default -# all members of a group must be documented explicitly. -# The default value is: NO. - -DISTRIBUTE_GROUP_DOC = NO - -# If one adds a struct or class to a group and this option is enabled, then also -# any nested class or struct is added to the same group. By default this option -# is disabled and one has to add nested compounds explicitly via \ingroup. -# The default value is: NO. - -GROUP_NESTED_COMPOUNDS = NO - -# Set the SUBGROUPING tag to YES to allow class member groups of the same type -# (for instance a group of public functions) to be put as a subgroup of that -# type (e.g. under the Public Functions section). Set it to NO to prevent -# subgrouping. Alternatively, this can be done per class using the -# \nosubgrouping command. -# The default value is: YES. - -SUBGROUPING = NO - -# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions -# are shown inside the group in which they are included (e.g. using \ingroup) -# instead of on a separate page (for HTML and Man pages) or section (for LaTeX -# and RTF). -# -# Note that this feature does not work in combination with -# SEPARATE_MEMBER_PAGES. -# The default value is: NO. - -INLINE_GROUPED_CLASSES = NO - -# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions -# with only public data fields or simple typedef fields will be shown inline in -# the documentation of the scope in which they are defined (i.e. file, -# namespace, or group documentation), provided this scope is documented. If set -# to NO, structs, classes, and unions are shown on a separate page (for HTML and -# Man pages) or section (for LaTeX and RTF). -# The default value is: NO. - -INLINE_SIMPLE_STRUCTS = NO - -# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or -# enum is documented as struct, union, or enum with the name of the typedef. So -# typedef struct TypeS {} TypeT, will appear in the documentation as a struct -# with name TypeT. When disabled the typedef will appear as a member of a file, -# namespace, or class. And the struct will be named TypeS. This can typically be -# useful for C code in case the coding convention dictates that all compound -# types are typedef'ed and only the typedef is referenced, never the tag name. -# The default value is: NO. - -TYPEDEF_HIDES_STRUCT = NO - -# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This -# cache is used to resolve symbols given their name and scope. Since this can be -# an expensive process and often the same symbol appears multiple times in the -# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small -# doxygen will become slower. If the cache is too large, memory is wasted. The -# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range -# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 -# symbols. At the end of a run doxygen will report the cache usage and suggest -# the optimal cache size from a speed point of view. -# Minimum value: 0, maximum value: 9, default value: 0. - -LOOKUP_CACHE_SIZE = 0 - -#--------------------------------------------------------------------------- -# Build related configuration options -#--------------------------------------------------------------------------- - -# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in -# documentation are documented, even if no documentation was available. Private -# class members and static file members will be hidden unless the -# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. -# Note: This will also disable the warnings about undocumented members that are -# normally produced when WARNINGS is set to YES. -# The default value is: NO. - -EXTRACT_ALL = YES - -# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will -# be included in the documentation. -# The default value is: NO. - -EXTRACT_PRIVATE = YES - -# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal -# scope will be included in the documentation. -# The default value is: NO. - -EXTRACT_PACKAGE = NO - -# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be -# included in the documentation. -# The default value is: NO. - -EXTRACT_STATIC = YES - -# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined -# locally in source files will be included in the documentation. If set to NO, -# only classes defined in header files are included. Does not have any effect -# for Java sources. -# The default value is: YES. - -EXTRACT_LOCAL_CLASSES = YES - -# This flag is only useful for Objective-C code. If set to YES, local methods, -# which are defined in the implementation section but not in the interface are -# included in the documentation. If set to NO, only methods in the interface are -# included. -# The default value is: NO. - -EXTRACT_LOCAL_METHODS = YES - -# If this flag is set to YES, the members of anonymous namespaces will be -# extracted and appear in the documentation as a namespace called -# 'anonymous_namespace{file}', where file will be replaced with the base name of -# the file that contains the anonymous namespace. By default anonymous namespace -# are hidden. -# The default value is: NO. - -EXTRACT_ANON_NSPACES = YES - -# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all -# undocumented members inside documented classes or files. If set to NO these -# members will be included in the various overviews, but no documentation -# section is generated. This option has no effect if EXTRACT_ALL is enabled. -# The default value is: NO. - -HIDE_UNDOC_MEMBERS = NO - -# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all -# undocumented classes that are normally visible in the class hierarchy. If set -# to NO, these classes will be included in the various overviews. This option -# has no effect if EXTRACT_ALL is enabled. -# The default value is: NO. - -HIDE_UNDOC_CLASSES = NO - -# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend -# (class|struct|union) declarations. If set to NO, these declarations will be -# included in the documentation. -# The default value is: NO. - -HIDE_FRIEND_COMPOUNDS = NO - -# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any -# documentation blocks found inside the body of a function. If set to NO, these -# blocks will be appended to the function's detailed documentation block. -# The default value is: NO. - -HIDE_IN_BODY_DOCS = NO - -# The INTERNAL_DOCS tag determines if documentation that is typed after a -# \internal command is included. If the tag is set to NO then the documentation -# will be excluded. Set it to YES to include the internal documentation. -# The default value is: NO. - -INTERNAL_DOCS = NO - -# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file -# names in lower-case letters. If set to YES, upper-case letters are also -# allowed. This is useful if you have classes or files whose names only differ -# in case and if your file system supports case sensitive file names. Windows -# and Mac users are advised to set this option to NO. -# The default value is: system dependent. - -CASE_SENSE_NAMES = NO - -# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with -# their full class and namespace scopes in the documentation. If set to YES, the -# scope will be hidden. -# The default value is: NO. - -HIDE_SCOPE_NAMES = NO - -# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will -# append additional text to a page's title, such as Class Reference. If set to -# YES the compound reference will be hidden. -# The default value is: NO. - -HIDE_COMPOUND_REFERENCE= NO - -# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of -# the files that are included by a file in the documentation of that file. -# The default value is: YES. - -SHOW_INCLUDE_FILES = YES - -# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each -# grouped member an include statement to the documentation, telling the reader -# which file to include in order to use the member. -# The default value is: NO. - -SHOW_GROUPED_MEMB_INC = NO - -# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include -# files with double quotes in the documentation rather than with sharp brackets. -# The default value is: NO. - -FORCE_LOCAL_INCLUDES = NO - -# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the -# documentation for inline members. -# The default value is: YES. - -INLINE_INFO = YES - -# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the -# (detailed) documentation of file and class members alphabetically by member -# name. If set to NO, the members will appear in declaration order. -# The default value is: YES. - -SORT_MEMBER_DOCS = YES - -# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief -# descriptions of file, namespace and class members alphabetically by member -# name. If set to NO, the members will appear in declaration order. Note that -# this will also influence the order of the classes in the class list. -# The default value is: NO. - -SORT_BRIEF_DOCS = NO - -# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the -# (brief and detailed) documentation of class members so that constructors and -# destructors are listed first. If set to NO the constructors will appear in the -# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. -# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief -# member documentation. -# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting -# detailed member documentation. -# The default value is: NO. - -SORT_MEMBERS_CTORS_1ST = NO - -# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy -# of group names into alphabetical order. If set to NO the group names will -# appear in their defined order. -# The default value is: NO. - -SORT_GROUP_NAMES = NO - -# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by -# fully-qualified names, including namespaces. If set to NO, the class list will -# be sorted only by class name, not including the namespace part. -# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. -# Note: This option applies only to the class list, not to the alphabetical -# list. -# The default value is: NO. - -SORT_BY_SCOPE_NAME = NO - -# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper -# type resolution of all parameters of a function it will reject a match between -# the prototype and the implementation of a member function even if there is -# only one candidate or it is obvious which candidate to choose by doing a -# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still -# accept a match between prototype and implementation in such cases. -# The default value is: NO. - -STRICT_PROTO_MATCHING = NO - -# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo -# list. This list is created by putting \todo commands in the documentation. -# The default value is: YES. - -GENERATE_TODOLIST = YES - -# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test -# list. This list is created by putting \test commands in the documentation. -# The default value is: YES. - -GENERATE_TESTLIST = YES - -# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug -# list. This list is created by putting \bug commands in the documentation. -# The default value is: YES. - -GENERATE_BUGLIST = YES - -# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) -# the deprecated list. This list is created by putting \deprecated commands in -# the documentation. -# The default value is: YES. - -GENERATE_DEPRECATEDLIST= YES - -# The ENABLED_SECTIONS tag can be used to enable conditional documentation -# sections, marked by \if ... \endif and \cond -# ... \endcond blocks. - -ENABLED_SECTIONS = - -# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the -# initial value of a variable or macro / define can have for it to appear in the -# documentation. If the initializer consists of more lines than specified here -# it will be hidden. Use a value of 0 to hide initializers completely. The -# appearance of the value of individual variables and macros / defines can be -# controlled using \showinitializer or \hideinitializer command in the -# documentation regardless of this setting. -# Minimum value: 0, maximum value: 10000, default value: 30. - -MAX_INITIALIZER_LINES = 30 - -# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at -# the bottom of the documentation of classes and structs. If set to YES, the -# list will mention the files that were used to generate the documentation. -# The default value is: YES. - -SHOW_USED_FILES = YES - -# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This -# will remove the Files entry from the Quick Index and from the Folder Tree View -# (if specified). -# The default value is: YES. - -SHOW_FILES = YES - -# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces -# page. This will remove the Namespaces entry from the Quick Index and from the -# Folder Tree View (if specified). -# The default value is: YES. - -SHOW_NAMESPACES = YES - -# The FILE_VERSION_FILTER tag can be used to specify a program or script that -# doxygen should invoke to get the current version for each file (typically from -# the version control system). Doxygen will invoke the program by executing (via -# popen()) the command command input-file, where command is the value of the -# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided -# by doxygen. Whatever the program writes to standard output is used as the file -# version. For an example see the documentation. - -FILE_VERSION_FILTER = - -# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed -# by doxygen. The layout file controls the global structure of the generated -# output files in an output format independent way. To create the layout file -# that represents doxygen's defaults, run doxygen with the -l option. You can -# optionally specify a file name after the option, if omitted DoxygenLayout.xml -# will be used as the name of the layout file. -# -# Note that if you run doxygen from a directory containing a file called -# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE -# tag is left empty. - -LAYOUT_FILE = - -# The CITE_BIB_FILES tag can be used to specify one or more bib files containing -# the reference definitions. This must be a list of .bib files. The .bib -# extension is automatically appended if omitted. This requires the bibtex tool -# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. -# For LaTeX the style of the bibliography can be controlled using -# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the -# search path. See also \cite for info how to create references. - -CITE_BIB_FILES = - -#--------------------------------------------------------------------------- -# Configuration options related to warning and progress messages -#--------------------------------------------------------------------------- - -# The QUIET tag can be used to turn on/off the messages that are generated to -# standard output by doxygen. If QUIET is set to YES this implies that the -# messages are off. -# The default value is: NO. - -QUIET = NO - -# The WARNINGS tag can be used to turn on/off the warning messages that are -# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES -# this implies that the warnings are on. -# -# Tip: Turn warnings on while writing the documentation. -# The default value is: YES. - -WARNINGS = YES - -# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate -# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag -# will automatically be disabled. -# The default value is: YES. - -WARN_IF_UNDOCUMENTED = YES - -# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as not documenting some parameters -# in a documented function, or documenting parameters that don't exist or using -# markup commands wrongly. -# The default value is: YES. - -WARN_IF_DOC_ERROR = YES - -# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that -# are documented, but have no documentation for their parameters or return -# value. If set to NO, doxygen will only warn about wrong or incomplete -# parameter documentation, but not about the absence of documentation. -# The default value is: NO. - -WARN_NO_PARAMDOC = YES - -# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when -# a warning is encountered. -# The default value is: NO. - -WARN_AS_ERROR = NO - -# The WARN_FORMAT tag determines the format of the warning messages that doxygen -# can produce. The string should contain the $file, $line, and $text tags, which -# will be replaced by the file and line number from which the warning originated -# and the warning text. Optionally the format may contain $version, which will -# be replaced by the version of the file (if it could be obtained via -# FILE_VERSION_FILTER) -# The default value is: $file:$line: $text. - -WARN_FORMAT = "$file:$line: $text" - -# The WARN_LOGFILE tag can be used to specify a file to which warning and error -# messages should be written. If left blank the output is written to standard -# error (stderr). - -WARN_LOGFILE = - -#--------------------------------------------------------------------------- -# Configuration options related to the input files -#--------------------------------------------------------------------------- - -# The INPUT tag is used to specify the files and/or directories that contain -# documented source files. You may enter file names like myfile.cpp or -# directories like /usr/src/myproject. Separate the files or directories with -# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING -# Note: If this tag is empty the current directory is searched. - -INPUT = sonoff \ - lib - -# This tag can be used to specify the character encoding of the source files -# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses -# libiconv (or the iconv built into libc) for the transcoding. See the libiconv -# documentation (see: https://www.gnu.org/software/libiconv/) for the list of -# possible encodings. -# The default value is: UTF-8. - -INPUT_ENCODING = UTF-8 - -# If the value of the INPUT tag contains directories, you can use the -# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and -# *.h) to filter out the source-files in the directories. -# -# Note that for custom extensions or not directly supported extensions you also -# need to set EXTENSION_MAPPING for the extension otherwise the files are not -# read by doxygen. -# -# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, -# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, -# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, -# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, -# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf and *.qsf. - -FILE_PATTERNS = *.c \ - *.cc \ - *.cxx \ - *.cpp \ - *.c++ \ - *.java \ - *.ii \ - *.ixx \ - *.ipp \ - *.i++ \ - *.inl \ - *.idl \ - *.ddl \ - *.odl \ - *.h \ - *.hh \ - *.hxx \ - *.hpp \ - *.h++ \ - *.cs \ - *.d \ - *.php \ - *.php4 \ - *.php5 \ - *.phtml \ - *.inc \ - *.m \ - *.markdown \ - *.md \ - *.mm \ - *.dox \ - *.py \ - *.pyw \ - *.f90 \ - *.f95 \ - *.f03 \ - *.f08 \ - *.f \ - *.for \ - *.tcl \ - *.vhd \ - *.vhdl \ - *.ucf \ - *.ino \ - *.qsf - -# The RECURSIVE tag can be used to specify whether or not subdirectories should -# be searched for input files as well. -# The default value is: NO. - -RECURSIVE = YES - -# The EXCLUDE tag can be used to specify files and/or directories that should be -# excluded from the INPUT source files. This way you can easily exclude a -# subdirectory from a directory tree whose root is specified with the INPUT tag. -# -# Note that relative paths are relative to the directory from which doxygen is -# run. - -EXCLUDE = - -# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or -# directories that are symbolic links (a Unix file system feature) are excluded -# from the input. -# The default value is: NO. - -EXCLUDE_SYMLINKS = NO - -# If the value of the INPUT tag contains directories, you can use the -# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude -# certain files from those directories. -# -# Note that the wildcards are matched against the file with absolute path, so to -# exclude all test directories for example use the pattern */test/* - -EXCLUDE_PATTERNS = */.git* - -# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names -# (namespaces, classes, functions, etc.) that should be excluded from the -# output. The symbol name can be a fully qualified name, a word, or if the -# wildcard * is used, a substring. Examples: ANamespace, AClass, -# AClass::ANamespace, ANamespace::*Test -# -# Note that the wildcards are matched against the file with absolute path, so to -# exclude all test directories use the pattern */test/* - -EXCLUDE_SYMBOLS = - -# The EXAMPLE_PATH tag can be used to specify one or more files or directories -# that contain example code fragments that are included (see the \include -# command). - -EXAMPLE_PATH = - -# If the value of the EXAMPLE_PATH tag contains directories, you can use the -# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and -# *.h) to filter out the source-files in the directories. If left blank all -# files are included. - -EXAMPLE_PATTERNS = * - -# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be -# searched for input files to be used with the \include or \dontinclude commands -# irrespective of the value of the RECURSIVE tag. -# The default value is: NO. - -EXAMPLE_RECURSIVE = NO - -# The IMAGE_PATH tag can be used to specify one or more files or directories -# that contain images that are to be included in the documentation (see the -# \image command). - -IMAGE_PATH = - -# The INPUT_FILTER tag can be used to specify a program that doxygen should -# invoke to filter for each input file. Doxygen will invoke the filter program -# by executing (via popen()) the command: -# -# -# -# where is the value of the INPUT_FILTER tag, and is the -# name of an input file. Doxygen will then use the output that the filter -# program writes to standard output. If FILTER_PATTERNS is specified, this tag -# will be ignored. -# -# Note that the filter must not add or remove lines; it is applied before the -# code is scanned, but not when the output code is generated. If lines are added -# or removed, the anchors will not be placed correctly. -# -# Note that for custom extensions or not directly supported extensions you also -# need to set EXTENSION_MAPPING for the extension otherwise the files are not -# properly processed by doxygen. - -INPUT_FILTER = - -# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern -# basis. Doxygen will compare the file name with each pattern and apply the -# filter if there is a match. The filters are a list of the form: pattern=filter -# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how -# filters are used. If the FILTER_PATTERNS tag is empty or if none of the -# patterns match the file name, INPUT_FILTER is applied. -# -# Note that for custom extensions or not directly supported extensions you also -# need to set EXTENSION_MAPPING for the extension otherwise the files are not -# properly processed by doxygen. - -FILTER_PATTERNS = - -# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using -# INPUT_FILTER) will also be used to filter the input files that are used for -# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). -# The default value is: NO. - -FILTER_SOURCE_FILES = NO - -# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file -# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and -# it is also possible to disable source filtering for a specific pattern using -# *.ext= (so without naming a filter). -# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. - -FILTER_SOURCE_PATTERNS = - -# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that -# is part of the input, its contents will be placed on the main page -# (index.html). This can be useful if you have a project on for instance GitHub -# and want to reuse the introduction page also for the doxygen output. - -USE_MDFILE_AS_MAINPAGE = - -#--------------------------------------------------------------------------- -# Configuration options related to source browsing -#--------------------------------------------------------------------------- - -# If the SOURCE_BROWSER tag is set to YES then a list of source files will be -# generated. Documented entities will be cross-referenced with these sources. -# -# Note: To get rid of all source code in the generated output, make sure that -# also VERBATIM_HEADERS is set to NO. -# The default value is: NO. - -SOURCE_BROWSER = YES - -# Setting the INLINE_SOURCES tag to YES will include the body of functions, -# classes and enums directly into the documentation. -# The default value is: NO. - -INLINE_SOURCES = YES - -# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any -# special comment blocks from generated source code fragments. Normal C, C++ and -# Fortran comments will always remain visible. -# The default value is: YES. - -STRIP_CODE_COMMENTS = YES - -# If the REFERENCED_BY_RELATION tag is set to YES then for each documented -# function all documented functions referencing it will be listed. -# The default value is: NO. - -REFERENCED_BY_RELATION = YES - -# If the REFERENCES_RELATION tag is set to YES then for each documented function -# all documented entities called/used by that function will be listed. -# The default value is: NO. - -REFERENCES_RELATION = YES - -# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set -# to YES then the hyperlinks from functions in REFERENCES_RELATION and -# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will -# link to the documentation. -# The default value is: YES. - -REFERENCES_LINK_SOURCE = YES - -# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the -# source code will show a tooltip with additional information such as prototype, -# brief description and links to the definition and documentation. Since this -# will make the HTML file larger and loading of large files a bit slower, you -# can opt to disable this feature. -# The default value is: YES. -# This tag requires that the tag SOURCE_BROWSER is set to YES. - -SOURCE_TOOLTIPS = YES - -# If the USE_HTAGS tag is set to YES then the references to source code will -# point to the HTML generated by the htags(1) tool instead of doxygen built-in -# source browser. The htags tool is part of GNU's global source tagging system -# (see https://www.gnu.org/software/global/global.html). You will need version -# 4.8.6 or higher. -# -# To use it do the following: -# - Install the latest version of global -# - Enable SOURCE_BROWSER and USE_HTAGS in the config file -# - Make sure the INPUT points to the root of the source tree -# - Run doxygen as normal -# -# Doxygen will invoke htags (and that will in turn invoke gtags), so these -# tools must be available from the command line (i.e. in the search path). -# -# The result: instead of the source browser generated by doxygen, the links to -# source code will now point to the output of htags. -# The default value is: NO. -# This tag requires that the tag SOURCE_BROWSER is set to YES. - -USE_HTAGS = NO - -# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a -# verbatim copy of the header file for each class for which an include is -# specified. Set to NO to disable this. -# See also: Section \class. -# The default value is: YES. - -VERBATIM_HEADERS = YES - -#--------------------------------------------------------------------------- -# Configuration options related to the alphabetical class index -#--------------------------------------------------------------------------- - -# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all -# compounds will be generated. Enable this if the project contains a lot of -# classes, structs, unions or interfaces. -# The default value is: YES. - -ALPHABETICAL_INDEX = YES - -# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in -# which the alphabetical index list will be split. -# Minimum value: 1, maximum value: 20, default value: 5. -# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. - -COLS_IN_ALPHA_INDEX = 5 - -# In case all classes in a project start with a common prefix, all classes will -# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag -# can be used to specify a prefix (or a list of prefixes) that should be ignored -# while generating the index headers. -# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. - -IGNORE_PREFIX = - -#--------------------------------------------------------------------------- -# Configuration options related to the HTML output -#--------------------------------------------------------------------------- - -# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output -# The default value is: YES. - -GENERATE_HTML = YES - -# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a -# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of -# it. -# The default directory is: html. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_OUTPUT = html - -# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each -# generated HTML page (for example: .htm, .php, .asp). -# The default value is: .html. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_FILE_EXTENSION = .xhtml - -# The HTML_HEADER tag can be used to specify a user-defined HTML header file for -# each generated HTML page. If the tag is left blank doxygen will generate a -# standard header. -# -# To get valid HTML the header file that includes any scripts and style sheets -# that doxygen needs, which is dependent on the configuration options used (e.g. -# the setting GENERATE_TREEVIEW). It is highly recommended to start with a -# default header using -# doxygen -w html new_header.html new_footer.html new_stylesheet.css -# YourConfigFile -# and then modify the file new_header.html. See also section "Doxygen usage" -# for information on how to generate the default header that doxygen normally -# uses. -# Note: The header is subject to change so you typically have to regenerate the -# default header when upgrading to a newer version of doxygen. For a description -# of the possible markers and block names see the documentation. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_HEADER = - -# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each -# generated HTML page. If the tag is left blank doxygen will generate a standard -# footer. See HTML_HEADER for more information on how to generate a default -# footer and what special commands can be used inside the footer. See also -# section "Doxygen usage" for information on how to generate the default footer -# that doxygen normally uses. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_FOOTER = - -# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style -# sheet that is used by each HTML page. It can be used to fine-tune the look of -# the HTML output. If left blank doxygen will generate a default style sheet. -# See also section "Doxygen usage" for information on how to generate the style -# sheet that doxygen normally uses. -# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as -# it is more robust and this tag (HTML_STYLESHEET) will in the future become -# obsolete. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_STYLESHEET = - -# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined -# cascading style sheets that are included after the standard style sheets -# created by doxygen. Using this option one can overrule certain style aspects. -# This is preferred over using HTML_STYLESHEET since it does not replace the -# standard style sheet and is therefore more robust against future updates. -# Doxygen will copy the style sheet files to the output directory. -# Note: The order of the extra style sheet files is of importance (e.g. the last -# style sheet in the list overrules the setting of the previous ones in the -# list). For an example see the documentation. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_EXTRA_STYLESHEET = - -# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or -# other source files which should be copied to the HTML output directory. Note -# that these files will be copied to the base HTML output directory. Use the -# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these -# files. In the HTML_STYLESHEET file, use the file name only. Also note that the -# files will be copied as-is; there are no commands or markers available. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_EXTRA_FILES = - -# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen -# will adjust the colors in the style sheet and background images according to -# this color. Hue is specified as an angle on a colorwheel, see -# https://en.wikipedia.org/wiki/Hue for more information. For instance the value -# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 -# purple, and 360 is red again. -# Minimum value: 0, maximum value: 359, default value: 220. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_COLORSTYLE_HUE = 220 - -# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors -# in the HTML output. For a value of 0 the output will use grayscales only. A -# value of 255 will produce the most vivid colors. -# Minimum value: 0, maximum value: 255, default value: 100. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_COLORSTYLE_SAT = 100 - -# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the -# luminance component of the colors in the HTML output. Values below 100 -# gradually make the output lighter, whereas values above 100 make the output -# darker. The value divided by 100 is the actual gamma applied, so 80 represents -# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not -# change the gamma. -# Minimum value: 40, maximum value: 240, default value: 80. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_COLORSTYLE_GAMMA = 80 - -# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML -# page will contain the date and time when the page was generated. Setting this -# to YES can help to show when doxygen was last run and thus if the -# documentation is up to date. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_TIMESTAMP = YES - -# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML -# documentation will contain a main index with vertical navigation menus that -# are dynamically created via Javascript. If disabled, the navigation index will -# consists of multiple levels of tabs that are statically embedded in every HTML -# page. Disable this option to support browsers that do not have Javascript, -# like the Qt help browser. -# The default value is: YES. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_DYNAMIC_MENUS = YES - -# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML -# documentation will contain sections that can be hidden and shown after the -# page has loaded. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_DYNAMIC_SECTIONS = NO - -# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries -# shown in the various tree structured indices initially; the user can expand -# and collapse entries dynamically later on. Doxygen will expand the tree to -# such a level that at most the specified number of entries are visible (unless -# a fully collapsed tree already exceeds this amount). So setting the number of -# entries 1 will produce a full collapsed tree by default. 0 is a special value -# representing an infinite number of entries and will result in a full expanded -# tree by default. -# Minimum value: 0, maximum value: 9999, default value: 100. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_INDEX_NUM_ENTRIES = 100 - -# If the GENERATE_DOCSET tag is set to YES, additional index files will be -# generated that can be used as input for Apple's Xcode 3 integrated development -# environment (see: https://developer.apple.com/tools/xcode/), introduced with -# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a -# Makefile in the HTML output directory. Running make will produce the docset in -# that directory and running make install will install the docset in -# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at -# startup. See https://developer.apple.com/tools/creatingdocsetswithdoxygen.html -# for more information. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_DOCSET = NO - -# This tag determines the name of the docset feed. A documentation feed provides -# an umbrella under which multiple documentation sets from a single provider -# (such as a company or product suite) can be grouped. -# The default value is: Doxygen generated docs. -# This tag requires that the tag GENERATE_DOCSET is set to YES. - -DOCSET_FEEDNAME = "Doxygen generated docs" - -# This tag specifies a string that should uniquely identify the documentation -# set bundle. This should be a reverse domain-name style string, e.g. -# com.mycompany.MyDocSet. Doxygen will append .docset to the name. -# The default value is: org.doxygen.Project. -# This tag requires that the tag GENERATE_DOCSET is set to YES. - -DOCSET_BUNDLE_ID = org.doxygen.Project - -# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify -# the documentation publisher. This should be a reverse domain-name style -# string, e.g. com.mycompany.MyDocSet.documentation. -# The default value is: org.doxygen.Publisher. -# This tag requires that the tag GENERATE_DOCSET is set to YES. - -DOCSET_PUBLISHER_ID = org.doxygen.Publisher - -# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. -# The default value is: Publisher. -# This tag requires that the tag GENERATE_DOCSET is set to YES. - -DOCSET_PUBLISHER_NAME = Publisher - -# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three -# additional HTML index files: index.hhp, index.hhc, and index.hhk. The -# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop -# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on -# Windows. -# -# The HTML Help Workshop contains a compiler that can convert all HTML output -# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML -# files are now used as the Windows 98 help format, and will replace the old -# Windows help format (.hlp) on all Windows platforms in the future. Compressed -# HTML files also contain an index, a table of contents, and you can search for -# words in the documentation. The HTML workshop also contains a viewer for -# compressed HTML files. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_HTMLHELP = NO - -# The CHM_FILE tag can be used to specify the file name of the resulting .chm -# file. You can add a path in front of the file if the result should not be -# written to the html output directory. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -CHM_FILE = - -# The HHC_LOCATION tag can be used to specify the location (absolute path -# including file name) of the HTML help compiler (hhc.exe). If non-empty, -# doxygen will try to run the HTML help compiler on the generated index.hhp. -# The file has to be specified with full path. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -HHC_LOCATION = - -# The GENERATE_CHI flag controls if a separate .chi index file is generated -# (YES) or that it should be included in the master .chm file (NO). -# The default value is: NO. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -GENERATE_CHI = NO - -# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) -# and project file content. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -CHM_INDEX_ENCODING = - -# The BINARY_TOC flag controls whether a binary table of contents is generated -# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it -# enables the Previous and Next buttons. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -BINARY_TOC = NO - -# The TOC_EXPAND flag can be set to YES to add extra items for group members to -# the table of contents of the HTML help documentation and to the tree view. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -TOC_EXPAND = NO - -# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and -# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that -# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help -# (.qch) of the generated HTML documentation. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_QHP = NO - -# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify -# the file name of the resulting .qch file. The path specified is relative to -# the HTML output folder. -# This tag requires that the tag GENERATE_QHP is set to YES. - -QCH_FILE = - -# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help -# Project output. For more information please see Qt Help Project / Namespace -# (see: http://doc.qt.io/qt-4.8/qthelpproject.html#namespace). -# The default value is: org.doxygen.Project. -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_NAMESPACE = org.doxygen.Project - -# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt -# Help Project output. For more information please see Qt Help Project / Virtual -# Folders (see: http://doc.qt.io/qt-4.8/qthelpproject.html#virtual-folders). -# The default value is: doc. -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_VIRTUAL_FOLDER = doc - -# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom -# filter to add. For more information please see Qt Help Project / Custom -# Filters (see: http://doc.qt.io/qt-4.8/qthelpproject.html#custom-filters). -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_CUST_FILTER_NAME = - -# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the -# custom filter to add. For more information please see Qt Help Project / Custom -# Filters (see: http://doc.qt.io/qt-4.8/qthelpproject.html#custom-filters). -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_CUST_FILTER_ATTRS = - -# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this -# project's filter section matches. Qt Help Project / Filter Attributes (see: -# http://doc.qt.io/qt-4.8/qthelpproject.html#filter-attributes). -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_SECT_FILTER_ATTRS = - -# The QHG_LOCATION tag can be used to specify the location of Qt's -# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the -# generated .qhp file. -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHG_LOCATION = - -# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be -# generated, together with the HTML files, they form an Eclipse help plugin. To -# install this plugin and make it available under the help contents menu in -# Eclipse, the contents of the directory containing the HTML and XML files needs -# to be copied into the plugins directory of eclipse. The name of the directory -# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. -# After copying Eclipse needs to be restarted before the help appears. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_ECLIPSEHELP = NO - -# A unique identifier for the Eclipse help plugin. When installing the plugin -# the directory name containing the HTML and XML files should also have this -# name. Each documentation set should have its own identifier. -# The default value is: org.doxygen.Project. -# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. - -ECLIPSE_DOC_ID = org.doxygen.Project - -# If you want full control over the layout of the generated HTML pages it might -# be necessary to disable the index and replace it with your own. The -# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top -# of each HTML page. A value of NO enables the index and the value YES disables -# it. Since the tabs in the index contain the same information as the navigation -# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -DISABLE_INDEX = NO - -# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index -# structure should be generated to display hierarchical information. If the tag -# value is set to YES, a side panel will be generated containing a tree-like -# index structure (just like the one that is generated for HTML Help). For this -# to work a browser that supports JavaScript, DHTML, CSS and frames is required -# (i.e. any modern browser). Windows users are probably better off using the -# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can -# further fine-tune the look of the index. As an example, the default style -# sheet generated by doxygen has an example that shows how to put an image at -# the root of the tree instead of the PROJECT_NAME. Since the tree basically has -# the same information as the tab index, you could consider setting -# DISABLE_INDEX to YES when enabling this option. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_TREEVIEW = NO - -# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that -# doxygen will group on one line in the generated HTML documentation. -# -# Note that a value of 0 will completely suppress the enum values from appearing -# in the overview section. -# Minimum value: 0, maximum value: 20, default value: 4. -# This tag requires that the tag GENERATE_HTML is set to YES. - -ENUM_VALUES_PER_LINE = 4 - -# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used -# to set the initial width (in pixels) of the frame in which the tree is shown. -# Minimum value: 0, maximum value: 1500, default value: 250. -# This tag requires that the tag GENERATE_HTML is set to YES. - -TREEVIEW_WIDTH = 250 - -# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to -# external symbols imported via tag files in a separate window. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -EXT_LINKS_IN_WINDOW = NO - -# Use this tag to change the font size of LaTeX formulas included as images in -# the HTML documentation. When you change the font size after a successful -# doxygen run you need to manually remove any form_*.png images from the HTML -# output directory to force them to be regenerated. -# Minimum value: 8, maximum value: 50, default value: 10. -# This tag requires that the tag GENERATE_HTML is set to YES. - -FORMULA_FONTSIZE = 10 - -# Use the FORMULA_TRANSPARENT tag to determine whether or not the images -# generated for formulas are transparent PNGs. Transparent PNGs are not -# supported properly for IE 6.0, but are supported on all modern browsers. -# -# Note that when changing this option you need to delete any form_*.png files in -# the HTML output directory before the changes have effect. -# The default value is: YES. -# This tag requires that the tag GENERATE_HTML is set to YES. - -FORMULA_TRANSPARENT = YES - -# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see -# https://www.mathjax.org) which uses client side Javascript for the rendering -# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX -# installed or if you want to formulas look prettier in the HTML output. When -# enabled you may also need to install MathJax separately and configure the path -# to it using the MATHJAX_RELPATH option. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -USE_MATHJAX = NO - -# When MathJax is enabled you can set the default output format to be used for -# the MathJax output. See the MathJax site (see: -# http://docs.mathjax.org/en/latest/output.html) for more details. -# Possible values are: HTML-CSS (which is slower, but has the best -# compatibility), NativeMML (i.e. MathML) and SVG. -# The default value is: HTML-CSS. -# This tag requires that the tag USE_MATHJAX is set to YES. - -MATHJAX_FORMAT = HTML-CSS - -# When MathJax is enabled you need to specify the location relative to the HTML -# output directory using the MATHJAX_RELPATH option. The destination directory -# should contain the MathJax.js script. For instance, if the mathjax directory -# is located at the same level as the HTML output directory, then -# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax -# Content Delivery Network so you can quickly see the result without installing -# MathJax. However, it is strongly recommended to install a local copy of -# MathJax from https://www.mathjax.org before deployment. -# The default value is: https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.2/. -# This tag requires that the tag USE_MATHJAX is set to YES. - -MATHJAX_RELPATH = https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.2/ - -# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax -# extension names that should be enabled during MathJax rendering. For example -# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols -# This tag requires that the tag USE_MATHJAX is set to YES. - -MATHJAX_EXTENSIONS = - -# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces -# of code that will be used on startup of the MathJax code. See the MathJax site -# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an -# example see the documentation. -# This tag requires that the tag USE_MATHJAX is set to YES. - -MATHJAX_CODEFILE = - -# When the SEARCHENGINE tag is enabled doxygen will generate a search box for -# the HTML output. The underlying search engine uses javascript and DHTML and -# should work on any modern browser. Note that when using HTML help -# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) -# there is already a search function so this one should typically be disabled. -# For large projects the javascript based search engine can be slow, then -# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to -# search using the keyboard; to jump to the search box use + S -# (what the is depends on the OS and browser, but it is typically -# , /"), (i == llevel) ? " selected" : "", i, i, GetTextIndexed(stemp1, sizeof(stemp1), i, kLoggingLevels)); @@ -2676,7 +2676,7 @@ void CmndWebPassword(void) void CmndWeblog(void) { - if ((XdrvMailbox.payload >= LOG_LEVEL_NONE) && (XdrvMailbox.payload <= LOG_LEVEL_ALL)) { + if ((XdrvMailbox.payload >= LOG_LEVEL_NONE) && (XdrvMailbox.payload <= LOG_LEVEL_DEBUG_MORE)) { Settings.weblog_level = XdrvMailbox.payload; } ResponseCmndNumber(Settings.weblog_level); diff --git a/sonoff/xdrv_02_mqtt.ino b/sonoff/xdrv_02_mqtt.ino index edc844448..50f66c8ce 100644 --- a/sonoff/xdrv_02_mqtt.ino +++ b/sonoff/xdrv_02_mqtt.ino @@ -764,7 +764,7 @@ void CmndMqttPassword(void) void CmndMqttlog(void) { - if ((XdrvMailbox.payload >= LOG_LEVEL_NONE) && (XdrvMailbox.payload <= LOG_LEVEL_ALL)) { + if ((XdrvMailbox.payload >= LOG_LEVEL_NONE) && (XdrvMailbox.payload <= LOG_LEVEL_DEBUG_MORE)) { Settings.mqttlog_level = XdrvMailbox.payload; } ResponseCmndNumber(Settings.mqttlog_level); diff --git a/sonoff/xdrv_10_scripter.ino b/sonoff/xdrv_10_scripter.ino index 3e1487b64..e4610c830 100644 --- a/sonoff/xdrv_10_scripter.ino +++ b/sonoff/xdrv_10_scripter.ino @@ -2211,33 +2211,49 @@ char *Evaluate_expression(char *lp,uint8_t and_or, uint8_t *result,JsonObject * struct T_INDEX ind; uint8_t vtype=0,lastop; uint8_t res=0; + char *llp=lp; + char *slp; SCRIPT_SKIP_SPACES - if (*lp=='(') { + uint8_t res=0; + uint8_t xand_or=0; lp++; - lp=Evaluate_expression(lp,and_or,result,jo); - lp++; + +loop: + SCRIPT_SKIP_SPACES + lp=Evaluate_expression(lp,xand_or,&res,jo); + if (*lp==')') { + lp++; + goto exit0; + } // check for next and or SCRIPT_SKIP_SPACES if (!strncmp(lp,"or",2)) { lp+=2; - and_or=1; - SCRIPT_SKIP_SPACES - lp=Evaluate_expression(lp,and_or,result,jo); + xand_or=1; + goto loop; } else if (!strncmp(lp,"and",3)) { lp+=3; - and_or=2; - SCRIPT_SKIP_SPACES - lp=Evaluate_expression(lp,and_or,result,jo); + xand_or=2; + goto loop; } - return lp; +exit0: + if (!and_or) { + *result=res; + } else if (and_or==1) { + *result|=res; + } else { + *result&=res; + } + goto exit10; } + llp=lp; // compare dfvar=&fvar; glob_script_mem.glob_error=0; - char *slp=lp; + slp=lp; numeric=1; lp=GetNumericResult(lp,OPER_EQU,dfvar,0); if (glob_script_mem.glob_error==1) { @@ -2252,23 +2268,15 @@ char *Evaluate_expression(char *lp,uint8_t and_or, uint8_t *result,JsonObject * char str[SCRIPT_MAXSSIZE]; lp=GetStringResult(lp,OPER_EQU,str,jo); if (lastop==OPER_EQUEQU || lastop==OPER_NOTEQU) { - uint8_t res=0; res=strcmp(cmpstr,str); if (lastop==OPER_EQUEQU) res=!res; - if (!and_or) { - *result=res; - } else if (and_or==1) { - *result|=res; - } else { - *result&=res; - } + goto exit; } } else { // numeric // evaluate operand lp=getop(lp,&lastop); - lp=GetNumericResult(lp,OPER_EQU,&fvar1,jo); switch (lastop) { case OPER_EQUEQU: @@ -2294,6 +2302,7 @@ char *Evaluate_expression(char *lp,uint8_t and_or, uint8_t *result,JsonObject * break; } +exit: if (!and_or) { *result=res; } else if (and_or==1) { @@ -2302,11 +2311,13 @@ char *Evaluate_expression(char *lp,uint8_t and_or, uint8_t *result,JsonObject * *result&=res; } } -exit: + + +exit10: #if SCRIPT_DEBUG>0 char tbuff[128]; - sprintf(tbuff,"p1=%d,p2=%d,cmpres=%d line: ",(int32_t)*dfvar,(int32_t)fvar1,*result); - toLogEOL(tbuff,lp); + sprintf(tbuff,"p1=%d,p2=%d,cmpres=%d,and_or=%d line: ",(int32_t)*dfvar,(int32_t)fvar1,*result,and_or); + toLogEOL(tbuff,llp); #endif return lp; } From a5896138317f60c7594e640136db9f4ecaa28060 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Fri, 25 Oct 2019 11:58:08 +0200 Subject: [PATCH 048/428] Release 6.7 --- sonoff/language/de-DE.h | 28 +-- sonoff/language/nl-NL.h | 108 ++++----- sonoff/language/pt-PT.h | 378 +++++++++++++++--------------- sonoff/language/uk-UK.h | 2 +- sonoff/sonoff.h | 2 +- sonoff/sonoff.ino | 7 +- sonoff/support_wifi.ino | 14 +- sonoff/xdrv_02_mqtt.ino | 2 +- sonoff/xdrv_11_knx.ino | 27 ++- sonoff/xdrv_12_home_assistant.ino | 78 +++--- sonoff/xdrv_16_tuyamcu.ino | 5 +- 11 files changed, 343 insertions(+), 308 deletions(-) diff --git a/sonoff/language/de-DE.h b/sonoff/language/de-DE.h index 92e456f96..acff5f96b 100644 --- a/sonoff/language/de-DE.h +++ b/sonoff/language/de-DE.h @@ -28,7 +28,7 @@ * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. * Use online command Prefix to translate cmnd, stat and tele. * - * Updated until v6.6.0.14 + * Updated until v6.6.0.21 \*********************************************************************/ //#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) @@ -157,7 +157,7 @@ #define D_TO "zu" #define D_TOGGLE "An/Aus" #define D_TOPIC "topic" -#define D_TOTAL_USAGE "Total Usage" +#define D_TOTAL_USAGE "Gesamtverbrauch" #define D_TRANSMIT "Übertragen" #define D_TRUE "wahr" #define D_TVOC "TVOC" @@ -320,11 +320,11 @@ #define D_MAC_ADDRESS "MAC-Adresse" #define D_MQTT_HOST "MQTT Host" #define D_MQTT_PORT "MQTT Port" -#define D_MQTT_CLIENT "MQTT client" -#define D_MQTT_USER "MQTT-Benutzer" -#define D_MQTT_TOPIC "MQTT topic" -#define D_MQTT_GROUP_TOPIC "MQTT group topic" -#define D_MQTT_FULL_TOPIC "MQTT full topic" +#define D_MQTT_CLIENT "MQTT Client" +#define D_MQTT_USER "MQTT Benutzer" +#define D_MQTT_TOPIC "MQTT Topic" +#define D_MQTT_GROUP_TOPIC "MQTT Group Topic" +#define D_MQTT_FULL_TOPIC "MQTT Full Topic" #define D_MDNS_DISCOVERY "mDNS-Ermittlung" #define D_MDNS_ADVERTISE "mDNS-Bekanntmachung" #define D_ESP_CHIP_ID "ESP Chip ID" @@ -335,15 +335,15 @@ #define D_UPGRADE_BY_WEBSERVER "Update über Web-Server" #define D_OTA_URL "OTA-URL" #define D_START_UPGRADE "Update starten" -#define D_UPGRADE_BY_FILE_UPLOAD "Update-Datei hochladen" +#define D_UPGRADE_BY_FILE_UPLOAD "Update Datei hochladen" #define D_UPLOAD_STARTED "Upload gestartet" #define D_UPGRADE_STARTED "Update gestartet" #define D_UPLOAD_DONE "Upload abgeschlossen" -#define D_UPLOAD_ERR_1 "keine Datei ausgewählt" -#define D_UPLOAD_ERR_2 "ungenügend Speicherplatz" -#define D_UPLOAD_ERR_3 "magic byte ist nicht 0xE9" -#define D_UPLOAD_ERR_4 "Flash-Größe des Programmes ist größer als der reale Flashspeicher" -#define D_UPLOAD_ERR_5 "Upload-buffer-Vergleich weicht ab" +#define D_UPLOAD_ERR_1 "Keine Datei ausgewählt" +#define D_UPLOAD_ERR_2 "Ungenügend Speicherplatz" +#define D_UPLOAD_ERR_3 "Magic Byte ist nicht 0xE9" +#define D_UPLOAD_ERR_4 "Datei überschreitet vorhdn. Flashspeicher" +#define D_UPLOAD_ERR_5 "Upload Buffer Vergleich weicht ab" #define D_UPLOAD_ERR_6 "Upload fehlgeschlagen. Aktiviere logging 3" #define D_UPLOAD_ERR_7 "Upload abgebrochen" #define D_UPLOAD_ERR_8 "Datei ungültig" @@ -641,7 +641,7 @@ #define D_UNIT_INCREMENTS "inc" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_KILOMETER_PER_HOUR "km/h" -#define D_UNIT_KILOOHM "kOhm" +#define D_UNIT_KILOOHM "kΩ" #define D_UNIT_KILOWATTHOUR "kWh" #define D_UNIT_LUX "lx" #define D_UNIT_MICROGRAM_PER_CUBIC_METER "µg/m3" diff --git a/sonoff/language/nl-NL.h b/sonoff/language/nl-NL.h index 4091f9928..0e81cebff 100644 --- a/sonoff/language/nl-NL.h +++ b/sonoff/language/nl-NL.h @@ -28,7 +28,7 @@ * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. * Use online command Prefix to translate cmnd, stat and tele. * - * Updated until v6.0.0a + * Updated until v6.7.0 \*********************************************************************/ //#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) @@ -93,7 +93,7 @@ #define D_FALLBACK_TOPIC "Fallback Topic" #define D_FALSE "Onwaar" #define D_FILE "Bestand" -#define D_FLOW_RATE "Flow rate" +#define D_FLOW_RATE "Debiet" #define D_FREE_MEMORY "Vrij geheugen" #define D_FREQUENCY "Frequentie" #define D_GAS "Gas" @@ -149,7 +149,7 @@ #define D_STOP "Stop" #define D_SUBNET_MASK "Subnet Masker" #define D_SUBSCRIBE_TO "Abonneer op" -#define D_UNSUBSCRIBE_FROM "Unsubscribe from" +#define D_UNSUBSCRIBE_FROM "Uitschrijven van" #define D_SUCCESSFUL "Gelukt" #define D_SUNRISE "Zonsopgang" #define D_SUNSET "Zonsondergang" @@ -157,7 +157,7 @@ #define D_TO "naar" #define D_TOGGLE "Toggle" // Wissel, Tuimel #define D_TOPIC "Topic" // Onderwerp -#define D_TOTAL_USAGE "Total Usage" +#define D_TOTAL_USAGE "Totaal verbruik" #define D_TRANSMIT "Verzend" #define D_TRUE "Waar" #define D_TVOC "TVOC" @@ -167,15 +167,15 @@ #define D_USER "Gebruiker" #define D_UTC_TIME "UTC" #define D_UV_INDEX "UV-index" -#define D_UV_INDEX_1 "Low" -#define D_UV_INDEX_2 "Mid" -#define D_UV_INDEX_3 "High" -#define D_UV_INDEX_4 "Danger" -#define D_UV_INDEX_5 "BurnL1/2" -#define D_UV_INDEX_6 "BurnL3" -#define D_UV_INDEX_7 "OoR" +#define D_UV_INDEX_1 "Laag" +#define D_UV_INDEX_2 "Gemiddeld" +#define D_UV_INDEX_3 "Hoog" +#define D_UV_INDEX_4 "Intens" +#define D_UV_INDEX_5 "Gevaarlijk" +#define D_UV_INDEX_6 "Schadelijk" +#define D_UV_INDEX_7 "Uitzonderlijk" #define D_UV_LEVEL "UV niveau" -#define D_UV_POWER "UV Power" +#define D_UV_POWER "UV intensiteit" #define D_VERSION "Versie" #define D_VOLTAGE "Spanning" #define D_WEIGHT "Gewicht" @@ -227,7 +227,7 @@ #define D_WITH_IP_ADDRESS "met IP adres" #define D_WEBSERVER_STOPPED "Webserver gestopt" #define D_FILE_NOT_FOUND "Bestand niet gevonden" -#define D_REDIRECTED "Redirected to captive portal" +#define D_REDIRECTED "Omleiding naar captive portal" #define D_WIFIMANAGER_SET_ACCESSPOINT_AND_STATION "Wifimanager start AccessPoint en hou Station" #define D_WIFIMANAGER_SET_ACCESSPOINT "Wifimanager start AccessPoint" #define D_TRYING_TO_CONNECT "Apparaat probeert te verbinden met netwerk" @@ -284,7 +284,7 @@ #define D_LOGGING_PARAMETERS "Logging parameters" #define D_SERIAL_LOG_LEVEL "Serieel log niveau" -#define D_MQTT_LOG_LEVEL "Mqtt log level" +#define D_MQTT_LOG_LEVEL "Mqtt log niveau" #define D_WEB_LOG_LEVEL "Web log niveau" #define D_SYS_LOG_LEVEL "Syslog niveau" #define D_MORE_DEBUG "Meer debug" @@ -444,17 +444,17 @@ #define D_ENERGY_TOTAL "Verbruik totaal" // xdrv_27_shutter.ino -#define D_OPEN "Open" -#define D_CLOSE "Close" -#define D_DOMOTICZ_SHUTTER "Shutter" +#define D_OPEN "Openen" +#define D_CLOSE "Sluiten" +#define D_DOMOTICZ_SHUTTER "Rolluik" // xdrv_28_pcf8574.ino -#define D_CONFIGURE_PCF8574 "Configure PCF8574" -#define D_PCF8574_PARAMETERS "PCF8574 parameters" -#define D_INVERT_PORTS "Invert Ports" -#define D_DEVICE "Device" -#define D_DEVICE_INPUT "Input" -#define D_DEVICE_OUTPUT "Output" +#define D_CONFIGURE_PCF8574 "Configureer PCF8574" +#define D_PCF8574_PARAMETERS "PCF8574 Parameters" +#define D_INVERT_PORTS "Inverteer Poorten" +#define D_DEVICE "Apparaat" +#define D_DEVICE_INPUT "Ingang" +#define D_DEVICE_OUTPUT "Uitgang" // xsns_05_ds18b20.ino #define D_SENSOR_BUSY "Sensor bezet" @@ -641,12 +641,12 @@ #define D_UNIT_INCREMENTS "inc" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" -#define D_UNIT_KILOOHM "kOhm" +#define D_UNIT_KILOOHM "kΩ" #define D_UNIT_KILOWATTHOUR "kWh" #define D_UNIT_LUX "lx" -#define D_UNIT_MICROGRAM_PER_CUBIC_METER "ug/m3" -#define D_UNIT_MICROMETER "um" -#define D_UNIT_MICROSECOND "us" +#define D_UNIT_MICROGRAM_PER_CUBIC_METER "µg/m3" +#define D_UNIT_MICROMETER "µm" +#define D_UNIT_MICROSECOND "µs" #define D_UNIT_MILLIAMPERE "mA" #define D_UNIT_MILLIMETER "mm" #define D_UNIT_MILLIMETER_MERCURY "mmHg" @@ -691,37 +691,37 @@ #define D_LOG_WIFI "WIF: " // Wifi //SDM220 -#define D_PHASE_ANGLE "Phase Angle" -#define D_IMPORT_ACTIVE "Import Active" -#define D_EXPORT_ACTIVE "Export Active" -#define D_IMPORT_REACTIVE "Import Reactive" -#define D_EXPORT_REACTIVE "Export Reactive" -#define D_TOTAL_REACTIVE "Total Reactive" +#define D_PHASE_ANGLE "Fase hoek" +#define D_IMPORT_ACTIVE "Import werkelijk" +#define D_EXPORT_ACTIVE "Export werkelijk" +#define D_IMPORT_REACTIVE "Import blind" +#define D_EXPORT_REACTIVE "Export blind" +#define D_TOTAL_REACTIVE "Totaal blind" #define D_UNIT_KWARH "kVArh" #define D_UNIT_ANGLE "Deg" //SOLAXX1 -#define D_PV1_VOLTAGE "PV1 Voltage" -#define D_PV1_CURRENT "PV1 Current" -#define D_PV1_POWER "PV1 Power" -#define D_PV2_VOLTAGE "PV2 Voltage" -#define D_PV2_CURRENT "PV2 Current" -#define D_PV2_POWER "PV2 Power" -#define D_SOLAR_POWER "Solar Power" -#define D_INVERTER_POWER "Inverter Power" +#define D_PV1_VOLTAGE "PV1 spanning" +#define D_PV1_CURRENT "PV1 stroom" +#define D_PV1_POWER "PV1 vermogen" +#define D_PV2_VOLTAGE "PV2 spanning" +#define D_PV2_CURRENT "PV2 stroom" +#define D_PV2_POWER "PV2 vermogen" +#define D_SOLAR_POWER "Zon vermogen" +#define D_INVERTER_POWER "Omvormer vermogen" #define D_STATUS "Status" -#define D_WAITING "Waiting" -#define D_CHECKING "Checking" -#define D_WORKING "Working" -#define D_FAILURE "Failure" -#define D_SOLAX_ERROR_0 "No Error Code" -#define D_SOLAX_ERROR_1 "Grid Lost Fault" -#define D_SOLAX_ERROR_2 "Grid Voltage Fault" -#define D_SOLAX_ERROR_3 "Grid Frequency Fault" -#define D_SOLAX_ERROR_4 "Pv Voltage Fault" -#define D_SOLAX_ERROR_5 "Isolation Fault" -#define D_SOLAX_ERROR_6 "Over Temperature Fault" -#define D_SOLAX_ERROR_7 "Fan Fault" -#define D_SOLAX_ERROR_8 "Other Device Fault" +#define D_WAITING "Wacht" +#define D_CHECKING "Controle" +#define D_WORKING "Actief" +#define D_FAILURE "Fout" +#define D_SOLAX_ERROR_0 "Geen" +#define D_SOLAX_ERROR_1 "Netverbinding" +#define D_SOLAX_ERROR_2 "Netspanning" +#define D_SOLAX_ERROR_3 "Netfrequentie" +#define D_SOLAX_ERROR_4 "Pv spanning" +#define D_SOLAX_ERROR_5 "Isolatie" +#define D_SOLAX_ERROR_6 "Overtemperatuur" +#define D_SOLAX_ERROR_7 "Ventilator" +#define D_SOLAX_ERROR_8 "Overige" #endif // _LANGUAGE_NL_NL_H_ diff --git a/sonoff/language/pt-PT.h b/sonoff/language/pt-PT.h index bfa234599..f060ec5d3 100644 --- a/sonoff/language/pt-PT.h +++ b/sonoff/language/pt-PT.h @@ -57,7 +57,7 @@ #define D_AS "como" #define D_AUTO "AUTO" #define D_BLINK "Piscar" -#define D_BLINKOFF "Piscar desligado" +#define D_BLINKOFF "Piscar Desligado" #define D_BOOT_COUNT "Contagem de Inicialização" #define D_BRIGHTLIGHT "Brilho" #define D_BSSID "BSSId" @@ -65,7 +65,7 @@ #define D_BY "por" // Write by me #define D_BYTES "Bytes" #define D_CELSIUS "Celsius" -#define D_CHANNEL "Channel" +#define D_CHANNEL "Canal" #define D_CO2 "Dioxido de Carbono" #define D_CODE "Código" // Button code #define D_COLDLIGHT "Luz Fria" @@ -78,7 +78,7 @@ #define D_DARKLIGHT "Luz Escura" #define D_DEBUG "Depurar" #define D_DISABLED "Disabilitado" -#define D_DISTANCE "Distance" +#define D_DISTANCE "Distância" #define D_DNS_SERVER "Servidor DNS" #define D_DONE "Concluído" #define D_DST_TIME "DST" @@ -93,9 +93,9 @@ #define D_FALLBACK_TOPIC "Tópico para retornar" #define D_FALSE "Falso" #define D_FILE "Ficheiro" -#define D_FLOW_RATE "Flow rate" +#define D_FLOW_RATE "Taxa de Fluxo" #define D_FREE_MEMORY "Memoria Livre" -#define D_FREQUENCY "Frequency" +#define D_FREQUENCY "Frequência" #define D_GAS "Gás" #define D_GATEWAY "Gateway" #define D_GROUP "Grupo" @@ -106,7 +106,7 @@ #define D_IMMEDIATE "Immediato" // Button immediate #define D_INDEX "Indíce" #define D_INFO "Info" -#define D_INFRARED "Infrared" +#define D_INFRARED "Infravermelho" #define D_INITIALIZED "Inicializado" #define D_IP_ADDRESS "Endereço IP" #define D_LIGHT "Luz" @@ -125,39 +125,39 @@ #define D_PORT "Porta" #define D_POWER_FACTOR "Factor de Potência" #define D_POWERUSAGE "Potência" -#define D_POWERUSAGE_ACTIVE "Active Power" -#define D_POWERUSAGE_APPARENT "Apparent Power" -#define D_POWERUSAGE_REACTIVE "Reactive Power" +#define D_POWERUSAGE_ACTIVE "Potência Ativa" +#define D_POWERUSAGE_APPARENT "Potência Aparente" +#define D_POWERUSAGE_REACTIVE "Potência Reativa" #define D_PRESSURE "Pressão" #define D_PRESSUREATSEALEVEL "Pressão ao nível do Mar" #define D_PROGRAM_FLASH_SIZE "Tamanho do Programa na Flash" #define D_PROGRAM_SIZE "Tamanho do Programa" #define D_PROJECT "Projeto" -#define D_RAIN "Rain" +#define D_RAIN "Chuva" #define D_RECEIVED "Recebido" #define D_RESTART "Reiniciar" #define D_RESTARTING "A reiniciar" #define D_RESTART_REASON "Razão do reinicio" #define D_RESTORE "Restauro" #define D_RETAINED "Manter" -#define D_RULE "Rule" -#define D_SAVE "Salvar" +#define D_RULE "Regra" +#define D_SAVE "Guardar" #define D_SENSOR "Sensor" #define D_SSID "SSId" #define D_START "Início" #define D_STD_TIME "STD" #define D_STOP "Parar" #define D_SUBNET_MASK "Mascara sub rede" -#define D_SUBSCRIBE_TO "Subescrever para" -#define D_UNSUBSCRIBE_FROM "Unsubscribe from" +#define D_SUBSCRIBE_TO "Subscrever" +#define D_UNSUBSCRIBE_FROM "Cancelar subscrição de" #define D_SUCCESSFUL "Successo" -#define D_SUNRISE "Sunrise" -#define D_SUNSET "Sunset" +#define D_SUNRISE "Nascer do Sol" +#define D_SUNSET "Pôr do Sol" #define D_TEMPERATURE "Temperatura" #define D_TO "para" #define D_TOGGLE "Pressionar" #define D_TOPIC "Tópico" -#define D_TOTAL_USAGE "Total Usage" +#define D_TOTAL_USAGE "Uso total" #define D_TRANSMIT "Transmitir" #define D_TRUE "Verdadeiro" #define D_TVOC "TVOC" @@ -167,38 +167,38 @@ #define D_USER "Utilizador" #define D_UTC_TIME "UTC" #define D_UV_INDEX "Indíce UV" -#define D_UV_INDEX_1 "Low" -#define D_UV_INDEX_2 "Mid" -#define D_UV_INDEX_3 "High" -#define D_UV_INDEX_4 "Danger" -#define D_UV_INDEX_5 "BurnL1/2" -#define D_UV_INDEX_6 "BurnL3" +#define D_UV_INDEX_1 "Baixo" +#define D_UV_INDEX_2 "Médio" +#define D_UV_INDEX_3 "Elevado" +#define D_UV_INDEX_4 "Perigoso" +#define D_UV_INDEX_5 "QueimaduraL1/2" +#define D_UV_INDEX_6 "QueimaduraL3" #define D_UV_INDEX_7 "OoR" #define D_UV_LEVEL "Nível UV" -#define D_UV_POWER "UV Power" +#define D_UV_POWER "Poder UV" #define D_VERSION "Versão" #define D_VOLTAGE "Voltagem" -#define D_WEIGHT "Weight" +#define D_WEIGHT "Peso" #define D_WARMLIGHT "Luz Quente" -#define D_WEB_SERVER "servidor WEB" +#define D_WEB_SERVER "Servidor WEB" // sonoff.ino #define D_WARNING_MINIMAL_VERSION "AVISO esta versão não supporta configurações persistentes" #define D_LEVEL_10 "nível 1-0" #define D_LEVEL_01 "nível 0-1" -#define D_SERIAL_LOGGING_DISABLED "Registro em serie desabilitado" +#define D_SERIAL_LOGGING_DISABLED "Registro de porta série desabilitado" #define D_SYSLOG_LOGGING_REENABLED "Registro do Syslog reativado" #define D_SET_BAUDRATE_TO "Ajuste da velocidade para" #define D_RECEIVED_TOPIC "Topico Recebido" -#define D_DATA_SIZE "Tamanho de dados" +#define D_DATA_SIZE "Tamanho de Dados" #define D_ANALOG_INPUT "Entrada Analógica" // support.ino #define D_OSWATCH "osWatch" #define D_BLOCKED_LOOP "Loop Bloqueado" -#define D_WPS_FAILED_WITH_STATUS "WPSconfig Falha de estado" -#define D_ACTIVE_FOR_3_MINUTES "ativo por 3 minutes" +#define D_WPS_FAILED_WITH_STATUS "WPSconfig FALHOU com estado" +#define D_ACTIVE_FOR_3_MINUTES "ativo por 3 minutos" #define D_FAILED_TO_START "Falha ao iníciar" #define D_PATCH_ISSUE_2186 "Questão 2186" #define D_CONNECTING_TO_AP "Ligando ao AP" @@ -212,17 +212,17 @@ #define D_QUERY_DONE "Consulta finalizada. Serviço MQTT não encontrado" #define D_MQTT_SERVICE_FOUND "Serviço MQTT encontrado em" #define D_FOUND_AT "encontrado em" -#define D_SYSLOG_HOST_NOT_FOUND "Syslog anfitrião não encontrado" +#define D_SYSLOG_HOST_NOT_FOUND "Anfitrião Syslog não encontrado" // settings.ino #define D_SAVED_TO_FLASH_AT "Guardado na flash em" #define D_LOADED_FROM_FLASH_AT "Lido da flash em" #define D_USE_DEFAULTS "Usar predefinições" -#define D_ERASED_SECTOR "Apagar setores" +#define D_ERASED_SECTOR "Apagado setor" // xdrv_02_webserver.ino -#define D_NOSCRIPT "To use Tasmota, please enable JavaScript" -#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "MINIMO firmware
Atualizar Por favor" +#define D_NOSCRIPT "Para utilizar o Tasmota, por favor ative o JavaScript" +#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "Firmware MÍNIMO
Por favor atualize" #define D_WEBSERVER_ACTIVE_ON "Servidor WEB ativo em" #define D_WITH_IP_ADDRESS "com o endereço IP" #define D_WEBSERVER_STOPPED "Servitor WEB parou" @@ -235,20 +235,20 @@ #define D_RESTART_IN "Reinicia em" #define D_SECONDS "segundos" #define D_DEVICE_WILL_RESTART "O dispositivo irá reiniciar dentro de alguns segundos" -#define D_BUTTON_TOGGLE "Pressionar" +#define D_BUTTON_TOGGLE "Alternar" #define D_CONFIGURATION "Configuração" #define D_INFORMATION "Informação" #define D_FIRMWARE_UPGRADE "Atualização de Firmware" #define D_CONSOLE "Consola" -#define D_CONFIRM_RESTART "Confirmar o reinicio" +#define D_CONFIRM_RESTART "Confirmar o reinício" #define D_CONFIGURE_MODULE "Configurar Módulo" #define D_CONFIGURE_WIFI "Configurar WiFi" #define D_CONFIGURE_MQTT "Configurar MQTT" #define D_CONFIGURE_DOMOTICZ "Configurar Domoticz" -#define D_CONFIGURE_LOGGING "Configurar Logging" +#define D_CONFIGURE_LOGGING "Configurar registro" #define D_CONFIGURE_OTHER "Configurar outras opções" -#define D_CONFIRM_RESET_CONFIGURATION "Apagar configuração Confirmar" +#define D_CONFIRM_RESET_CONFIGURATION "Confirme o reinício da configuração" #define D_RESET_CONFIGURATION "Apagar configuração" #define D_BACKUP_CONFIGURATION "Guardar configuração" #define D_RESTORE_CONFIGURATION "Repor configuração" @@ -256,102 +256,102 @@ #define D_MODULE_PARAMETERS "Parametros do Módulo" #define D_MODULE_TYPE "Tipo de Módulo" -#define D_PULLUP_ENABLE "No Button/Switch pull-up" +#define D_PULLUP_ENABLE "Sem pull-up de Botão/Interruptor" #define D_ADC "ADC" #define D_GPIO "GPIO" -#define D_SERIAL_IN "Serial Entrada" -#define D_SERIAL_OUT "Serial Saída" +#define D_SERIAL_IN "Entrada de porta série" +#define D_SERIAL_OUT "Saída de porta série" #define D_WIFI_PARAMETERS "Parametros Wifi" -#define D_SCAN_FOR_WIFI_NETWORKS "Em busca de redes wifi" -#define D_SCAN_DONE "Busca finalizada" -#define D_NO_NETWORKS_FOUND "Sem redes" -#define D_REFRESH_TO_SCAN_AGAIN "Nova busca" -#define D_DUPLICATE_ACCESSPOINT "Ponto de Acesso duplicado" +#define D_SCAN_FOR_WIFI_NETWORKS "Procurar redes Wifi" +#define D_SCAN_DONE "Pesquisa terminada" +#define D_NO_NETWORKS_FOUND "Sem redes encontradas" +#define D_REFRESH_TO_SCAN_AGAIN "Atualize para realizar nova pesquisa" +#define D_DUPLICATE_ACCESSPOINT "Duplicar ponto de Acesso" #define D_SKIPPING_LOW_QUALITY "Ignorado devido a baixa qualidade do sinal" #define D_RSSI "RSSI" #define D_WEP "WEP" #define D_WPA_PSK "WPA PSK" #define D_WPA2_PSK "WPA2 PSK" -#define D_AP1_SSID "AP1 SSId" -#define D_AP1_PASSWORD "AP1 Palavra Chave" -#define D_AP2_SSID "AP2 SSId" -#define D_AP2_PASSWORD "AP2 Palavra Chave" +#define D_AP1_SSID "SSId do AP1" +#define D_AP1_PASSWORD "Palavra Chave do AP1" +#define D_AP2_SSID "SSId do AP2" +#define D_AP2_PASSWORD "Palavra Chave do AP2" #define D_MQTT_PARAMETERS "Parametros MQTT" #define D_CLIENT "Cliente" #define D_FULL_TOPIC "Tópico completo" -#define D_LOGGING_PARAMETERS "Parametros Logging" -#define D_SERIAL_LOG_LEVEL "Nível de registro serial" -#define D_MQTT_LOG_LEVEL "Mqtt log level" +#define D_LOGGING_PARAMETERS "Parametros para registro" +#define D_SERIAL_LOG_LEVEL "Nível de registro na porta série" +#define D_MQTT_LOG_LEVEL "Nível de registro MQTT" #define D_WEB_LOG_LEVEL "Nível de registro WEB" -#define D_SYS_LOG_LEVEL "Nível de registro Syslog" +#define D_SYS_LOG_LEVEL "Nível de registro do Syslog" #define D_MORE_DEBUG "Depurar mais" -#define D_SYSLOG_HOST "Syslog anfitrião" +#define D_SYSLOG_HOST "Anfitrião Syslog" #define D_SYSLOG_PORT "Porta Syslog" -#define D_TELEMETRY_PERIOD "Periodo de Telemetria" +#define D_TELEMETRY_PERIOD "Período de Telemetria" #define D_OTHER_PARAMETERS "Outros parametros" -#define D_TEMPLATE "Template" -#define D_ACTIVATE "Activate" -#define D_WEB_ADMIN_PASSWORD "Palavra Chave de WEB Admin" +#define D_TEMPLATE "Modelo" +#define D_ACTIVATE "Ativar" +#define D_WEB_ADMIN_PASSWORD "Palavra Chave do Admin WEB" #define D_MQTT_ENABLE "MQTT habilitado" #define D_FRIENDLY_NAME "Nome amigável" #define D_BELKIN_WEMO "Belkin WeMo" #define D_HUE_BRIDGE "Hue Bridge" #define D_SINGLE_DEVICE "dispositivo único" -#define D_MULTI_DEVICE "multiplos dispositivos" +#define D_MULTI_DEVICE "dispositivo múltiplo" -#define D_CONFIGURE_TEMPLATE "Configure Template" -#define D_TEMPLATE_PARAMETERS "Template parameters" -#define D_TEMPLATE_NAME "Name" -#define D_BASE_TYPE "Based on" -#define D_TEMPLATE_FLAGS "Options" +#define D_CONFIGURE_TEMPLATE "Configurar Modelo" +#define D_TEMPLATE_PARAMETERS "Parametros do Modelo" +#define D_TEMPLATE_NAME "Nome do Modelo" +#define D_BASE_TYPE "Baseado em" +#define D_TEMPLATE_FLAGS "Opções" -#define D_SAVE_CONFIGURATION "Salvar configuração" +#define D_SAVE_CONFIGURATION "Guardar configuração" #define D_CONFIGURATION_SAVED "Configuração guardada" -#define D_CONFIGURATION_RESET "Reinicialização da configuração" +#define D_CONFIGURATION_RESET "Reinicializar a configuração" #define D_PROGRAM_VERSION "Versão do Programa" -#define D_BUILD_DATE_AND_TIME "Data e Hora da construção" +#define D_BUILD_DATE_AND_TIME "Data e Hora de criação" #define D_CORE_AND_SDK_VERSION "Versão Core/SDK" -#define D_FLASH_WRITE_COUNT "contagem de gravação flash" +#define D_FLASH_WRITE_COUNT "Contagem de gravação da flash" #define D_MAC_ADDRESS "Endereço MAC" -#define D_MQTT_HOST "MQTT Servidor" -#define D_MQTT_PORT "MQTT Porta" -#define D_MQTT_CLIENT "MQTT Cliente" -#define D_MQTT_USER "MQTT Utilizador" -#define D_MQTT_TOPIC "MQTT Tópico" -#define D_MQTT_GROUP_TOPIC "MQTT Tópico Grupo" -#define D_MQTT_FULL_TOPIC "MQTT Tópico Completo" +#define D_MQTT_HOST "Servidor MQTT" +#define D_MQTT_PORT "Porta MQTT" +#define D_MQTT_CLIENT "Cliente MQTT" +#define D_MQTT_USER "Utilizador MQTT" +#define D_MQTT_TOPIC "Tópico MQTT" +#define D_MQTT_GROUP_TOPIC "Tópico MQTT de Grupo" +#define D_MQTT_FULL_TOPIC "Tópico MQTT Completo" #define D_MDNS_DISCOVERY "Descobrir mDNS" #define D_MDNS_ADVERTISE "Anunciar mDNS" -#define D_ESP_CHIP_ID "ESP Chip Id" -#define D_FLASH_CHIP_ID "Flash Chip Id" -#define D_FLASH_CHIP_SIZE "Flash Size" -#define D_FREE_PROGRAM_SPACE "Espaço Livre Programa" +#define D_ESP_CHIP_ID "ID do chip ESP" +#define D_FLASH_CHIP_ID "ID do chip da Flash" +#define D_FLASH_CHIP_SIZE "Tamanho da Flash" +#define D_FREE_PROGRAM_SPACE "Espaço de Programa Livre" #define D_UPGRADE_BY_WEBSERVER "Atualizar pelo servidor WEB" -#define D_OTA_URL "OTA Url" +#define D_OTA_URL "Url OTA" #define D_START_UPGRADE "Iniciar atualização" -#define D_UPGRADE_BY_FILE_UPLOAD "Atualização por envio de ficheiro" -#define D_UPLOAD_STARTED "Início do envio" +#define D_UPGRADE_BY_FILE_UPLOAD "Atualizar por envio de ficheiro" +#define D_UPLOAD_STARTED "Envio Iniciado" #define D_UPGRADE_STARTED "Atualização Iniciada" #define D_UPLOAD_DONE "Atualização Finalizada" #define D_UPLOAD_ERR_1 "Nenhum ficheiro selecionado" -#define D_UPLOAD_ERR_2 "Nao existe espaço disponível" -#define D_UPLOAD_ERR_3 "Byte mágico não é 0xE9" +#define D_UPLOAD_ERR_2 "Não existe espaço disponível" +#define D_UPLOAD_ERR_3 "O Byte mágico não é 0xE9" #define D_UPLOAD_ERR_4 "O tamanho do programa e maior do que o tamanho real da flash" -#define D_UPLOAD_ERR_5 "Envio buffer miscompare" -#define D_UPLOAD_ERR_6 "Falha no envio. Hablitar logging 3" +#define D_UPLOAD_ERR_5 "Comparação incorreta do buffer de upload" +#define D_UPLOAD_ERR_6 "Falha no envio. Habilitar registro 3" #define D_UPLOAD_ERR_7 "Envio cancelado" #define D_UPLOAD_ERR_8 "Ficheiro inválido" #define D_UPLOAD_ERR_9 "Ficheiro demasiado grande" -#define D_UPLOAD_ERR_10 "Failed to init RF chip" -#define D_UPLOAD_ERR_11 "Failed to erase RF chip" -#define D_UPLOAD_ERR_12 "Failed to write to RF chip" -#define D_UPLOAD_ERR_13 "Failed to decode RF firmware" +#define D_UPLOAD_ERR_10 "Falha ao iniciar o chip de RF" +#define D_UPLOAD_ERR_11 "Falha ao apagar o chip de RF" +#define D_UPLOAD_ERR_12 "Falha ao escrever no chip de RF" +#define D_UPLOAD_ERR_13 "Falha ao descodificar o firmware RF" #define D_UPLOAD_ERROR_CODE "Código de erro do envio" #define D_ENTER_COMMAND "Inserir comando" @@ -361,14 +361,14 @@ // xdrv_01_mqtt.ino #define D_FINGERPRINT "Verifique a impressão digital TLS..." #define D_TLS_CONNECT_FAILED_TO "TLS não conseguiu ligar" -#define D_RETRY_IN "Tentativa em" -#define D_VERIFIED "Verificado impressão digital " +#define D_RETRY_IN "Nova tentativa em" +#define D_VERIFIED "Verificado usando impressão digital" #define D_INSECURE "Ligação insegura devido à impressão digital inválida" #define D_CONNECT_FAILED_TO "A ligação falhou ao" // xplg_wemohue.ino #define D_MULTICAST_DISABLED "Multicast desabilitado" -#define D_MULTICAST_REJOINED "Multicast (re)ingressou" +#define D_MULTICAST_REJOINED "Multicast (re)ingressado" #define D_MULTICAST_JOIN_FAILED "Multicast falha no reingresso" #define D_FAILED_TO_SEND_RESPONSE "Falha no envio de reposta" @@ -377,13 +377,13 @@ #define D_WEMO_EVENT_SERVICE "WeMo evento de serviço" #define D_WEMO_META_SERVICE "WeMo serviço meta" #define D_WEMO_SETUP "WeMo configuração" -#define D_RESPONSE_SENT "Rsposta enviada" +#define D_RESPONSE_SENT "Resposta enviada" #define D_HUE "Hue" -#define D_HUE_BRIDGE_SETUP "Hue setup" -#define D_HUE_API_NOT_IMPLEMENTED "Hue API nao implementada" -#define D_HUE_API "Hue API" -#define D_HUE_POST_ARGS "Hue POST args" +#define D_HUE_BRIDGE_SETUP "Configuração do Hue" +#define D_HUE_API_NOT_IMPLEMENTED "API Hue nao implementada" +#define D_HUE_API "API Hue" +#define D_HUE_POST_ARGS "Argumentos de POST HUE" #define D_3_RESPONSE_PACKETS_SENT "3 pacotes de resposta enviados" // xdrv_07_domoticz.ino @@ -402,71 +402,71 @@ #define D_DOMOTICZ_CURRENT "Corrente/PM10" #define D_DOMOTICZ_AIRQUALITY "Qualidade do Ar" #define D_DOMOTICZ_P1_SMART_METER "P1SmartMeter" -#define D_DOMOTICZ_UPDATE_TIMER "Tempo de atualização" +#define D_DOMOTICZ_UPDATE_TIMER "Temporizador de atualização" // xdrv_09_timers.ino -#define D_CONFIGURE_TIMER "Configure Timer" -#define D_TIMER_PARAMETERS "Timer parameters" -#define D_TIMER_ENABLE "Enable Timers" -#define D_TIMER_ARM "Arm" -#define D_TIMER_TIME "Time" -#define D_TIMER_DAYS "Days" -#define D_TIMER_REPEAT "Repeat" -#define D_TIMER_OUTPUT "Output" -#define D_TIMER_ACTION "Action" +#define D_CONFIGURE_TIMER "Configurar temporizador" +#define D_TIMER_PARAMETERS "Parâmetros do temporizador" +#define D_TIMER_ENABLE "Habilitar Temporizadores" +#define D_TIMER_ARM "Armar" +#define D_TIMER_TIME "Tempo" +#define D_TIMER_DAYS "Dias" +#define D_TIMER_REPEAT "Repetir" +#define D_TIMER_OUTPUT "Aaída" +#define D_TIMER_ACTION "Açao" // xdrv_10_knx.ino -#define D_CONFIGURE_KNX "Configure KNX" -#define D_KNX_PARAMETERS "KNX Parameters" -#define D_KNX_GENERAL_CONFIG "General" -#define D_KNX_PHYSICAL_ADDRESS "Physical Address" -#define D_KNX_PHYSICAL_ADDRESS_NOTE "( Must be unique on the KNX network )" -#define D_KNX_ENABLE "Enable KNX" -#define D_KNX_GROUP_ADDRESS_TO_WRITE "Data to Send to Group Addresses" -#define D_ADD "Add" -#define D_DELETE "Delete" -#define D_REPLY "Reply" -#define D_KNX_GROUP_ADDRESS_TO_READ "Group Addresses to Receive Data from" +#define D_CONFIGURE_KNX "Configurar KNX" +#define D_KNX_PARAMETERS "Parâmetros KNX" +#define D_KNX_GENERAL_CONFIG "Geral" +#define D_KNX_PHYSICAL_ADDRESS "Endereço físico" +#define D_KNX_PHYSICAL_ADDRESS_NOTE "( Deve ser exclusivo na rede KNX )" +#define D_KNX_ENABLE "Habilitar KNX" +#define D_KNX_GROUP_ADDRESS_TO_WRITE "Dados para enviar para Endereços de Grupo" +#define D_ADD "Adicionar" +#define D_DELETE "Remover" +#define D_REPLY "Responder" +#define D_KNX_GROUP_ADDRESS_TO_READ "Endereços de Grupo de onde receber dados" #define D_LOG_KNX "KNX: " -#define D_RECEIVED_FROM "Received from" -#define D_KNX_COMMAND_WRITE "Write" -#define D_KNX_COMMAND_READ "Read" -#define D_KNX_COMMAND_OTHER "Other" -#define D_SENT_TO "sent to" -#define D_KNX_WARNING "The group address ( 0 / 0 / 0 ) is reserved and can not be used." -#define D_KNX_ENHANCEMENT "Communication Enhancement" +#define D_RECEIVED_FROM "Recebido de" +#define D_KNX_COMMAND_WRITE "Escrever" +#define D_KNX_COMMAND_READ "Ler" +#define D_KNX_COMMAND_OTHER "Outro" +#define D_SENT_TO "enviar para" +#define D_KNX_WARNING "O Endereço de Grupo ( 0 / 0 / 0 ) está reservado e não pode ser usado." +#define D_KNX_ENHANCEMENT "Melhoria de Comunicação" #define D_KNX_TX_SLOT "KNX TX" #define D_KNX_RX_SLOT "KNX RX" // xdrv_03_energy.ino #define D_ENERGY_TODAY "Consumo energético de hoje" #define D_ENERGY_YESTERDAY "Consumo energético de ontem" -#define D_ENERGY_TOTAL "Consumo total de energial" +#define D_ENERGY_TOTAL "Consumo energético total" // xdrv_27_shutter.ino -#define D_OPEN "Open" -#define D_CLOSE "Close" -#define D_DOMOTICZ_SHUTTER "Shutter" +#define D_OPEN "Abrir" +#define D_CLOSE "Fechar" +#define D_DOMOTICZ_SHUTTER "Estore" // xdrv_28_pcf8574.ino -#define D_CONFIGURE_PCF8574 "Configure PCF8574" -#define D_PCF8574_PARAMETERS "PCF8574 parameters" -#define D_INVERT_PORTS "Invert Ports" -#define D_DEVICE "Device" -#define D_DEVICE_INPUT "Input" -#define D_DEVICE_OUTPUT "Output" +#define D_CONFIGURE_PCF8574 "Configurar PCF8574" +#define D_PCF8574_PARAMETERS "Parâmetros PCF8574" +#define D_INVERT_PORTS "Inverter Portas" +#define D_DEVICE "Dispositivo" +#define D_DEVICE_INPUT "Entrada" +#define D_DEVICE_OUTPUT "Saída" // xsns_05_ds18b20.ino #define D_SENSOR_BUSY "Sensor ocupado" -#define D_SENSOR_CRC_ERROR "Erro Sensor CRC" -#define D_SENSORS_FOUND "Sensors encontrados" +#define D_SENSOR_CRC_ERROR "Erro no CRC do sensor" +#define D_SENSORS_FOUND "Sensores encontrados" // xsns_06_dht.ino #define D_TIMEOUT_WAITING_FOR "Fim do tempo de espera" #define D_START_SIGNAL_LOW "Sinal de início baixo" #define D_START_SIGNAL_HIGH "Sinal de início elevado" -#define D_PULSE "pulse" -#define D_CHECKSUM_FAILURE "Falha Checksum" +#define D_PULSE "pulso" +#define D_CHECKSUM_FAILURE "Falha na soma de verificação" // xsns_07_sht1x.ino #define D_SENSOR_DID_NOT_ACK_COMMAND "Sensor não aceitou o comando ACK" @@ -486,27 +486,27 @@ #define D_GZ_AXIS "Gyro Z-Axis" // xsns_34_hx711.ino -#define D_HX_CAL_REMOVE "Remove weigth" -#define D_HX_CAL_REFERENCE "Load reference weigth" -#define D_HX_CAL_DONE "Calibrated" -#define D_HX_CAL_FAIL "Calibration failed" -#define D_RESET_HX711 "Reset Scale" -#define D_CONFIGURE_HX711 "Configure Scale" -#define D_HX711_PARAMETERS "Scale parameters" -#define D_ITEM_WEIGHT "Item weight" -#define D_REFERENCE_WEIGHT "Reference weigth" -#define D_CALIBRATE "Calibrate" -#define D_CALIBRATION "Calibration" +#define D_HX_CAL_REMOVE "Remover peso" +#define D_HX_CAL_REFERENCE "Carregar peso de referência" +#define D_HX_CAL_DONE "Calibrado" +#define D_HX_CAL_FAIL "Falha na calibração" +#define D_RESET_HX711 "Reiniciar balança" +#define D_CONFIGURE_HX711 "Configurar balança" +#define D_HX711_PARAMETERS "Parâmetros da balança" +#define D_ITEM_WEIGHT "Peso do item" +#define D_REFERENCE_WEIGHT "Peso de referência" +#define D_CALIBRATE "Calibrar" +#define D_CALIBRATION "Calibração" //xsns_35_tx20.ino -#define D_TX20_WIND_DIRECTION "Wind Direction" -#define D_TX20_WIND_SPEED "Wind Speed" -#define D_TX20_WIND_SPEED_AVG "Wind Speed Avg" -#define D_TX20_WIND_SPEED_MAX "Wind Speed Max" +#define D_TX20_WIND_DIRECTION "Direção do vento +#define D_TX20_WIND_SPEED "Velocidade do vento" +#define D_TX20_WIND_SPEED_AVG "Velocidade média do vento" +#define D_TX20_WIND_SPEED_MAX "Velocidade máxima do vento" #define D_TX20_NORTH "N" #define D_TX20_EAST "E" #define D_TX20_SOUTH "S" -#define D_TX20_WEST "W" +#define D_TX20_WEST "O" //xsns_43_hre.ino #define D_LOG_HRE "HRE: " @@ -544,7 +544,7 @@ #define D_SENSOR_SPI_MISO "SPI MISO" #define D_SENSOR_SPI_MOSI "SPI MOSI" #define D_SENSOR_SPI_CLK "SPI CLK" -#define D_SENSOR_BACKLIGHT "Luz negra" +#define D_SENSOR_BACKLIGHT "Luz fundo" #define D_SENSOR_PMS5003 "PMS5003" #define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" #define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" @@ -573,7 +573,7 @@ #define D_SENSOR_SSPI_SCLK "SSPI SCLK" #define D_SENSOR_SSPI_CS "SSPI CS" #define D_SENSOR_SSPI_DC "SSPI DC" -#define D_SENSOR_RF_SENSOR "RF Sensor" +#define D_SENSOR_RF_SENSOR "Sensor RF" #define D_SENSOR_AZ_RX "AZ Rx" #define D_SENSOR_AZ_TX "AZ Tx" #define D_SENSOR_MAX31855_CS "MX31855 CS" @@ -599,7 +599,7 @@ #define D_SENSOR_ARIRFSEL "ALux IrSel" #define D_SENSOR_TXD "Serial Tx" #define D_SENSOR_RXD "Serial Rx" -#define D_SENSOR_ROTARY "Rotary" // Suffix "1A" +#define D_SENSOR_ROTARY "Rotativo" // Suffix "1A" #define D_SENSOR_HRE_CLOCK "HRE Clock" #define D_SENSOR_HRE_DATA "HRE Data" #define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" @@ -657,7 +657,7 @@ #define D_UNIT_PARTS_PER_MILLION "ppm" #define D_UNIT_PRESSURE "hPa" #define D_UNIT_SECOND "sec" -#define D_UNIT_SECTORS "sectors" +#define D_UNIT_SECTORS "setores" #define D_UNIT_VA "VA" #define D_UNIT_VAR "VAr" #define D_UNIT_VOLT "V" @@ -691,37 +691,37 @@ #define D_LOG_WIFI "WIF: " // Wifi //SDM220 -#define D_PHASE_ANGLE "Phase Angle" -#define D_IMPORT_ACTIVE "Import Active" -#define D_EXPORT_ACTIVE "Export Active" -#define D_IMPORT_REACTIVE "Import Reactive" -#define D_EXPORT_REACTIVE "Export Reactive" -#define D_TOTAL_REACTIVE "Total Reactive" +#define D_PHASE_ANGLE "Ângulo de fase" +#define D_IMPORT_ACTIVE "Ativo importado" +#define D_EXPORT_ACTIVE "Ativo exportado" +#define D_IMPORT_REACTIVE "Reativo importado" +#define D_EXPORT_REACTIVE "Reativo exportado" +#define D_TOTAL_REACTIVE "Reactivo total" #define D_UNIT_KWARH "kVArh" #define D_UNIT_ANGLE "Deg" //SOLAXX1 -#define D_PV1_VOLTAGE "PV1 Voltage" -#define D_PV1_CURRENT "PV1 Current" -#define D_PV1_POWER "PV1 Power" -#define D_PV2_VOLTAGE "PV2 Voltage" -#define D_PV2_CURRENT "PV2 Current" -#define D_PV2_POWER "PV2 Power" -#define D_SOLAR_POWER "Solar Power" -#define D_INVERTER_POWER "Inverter Power" -#define D_STATUS "Status" -#define D_WAITING "Waiting" -#define D_CHECKING "Checking" -#define D_WORKING "Working" -#define D_FAILURE "Failure" -#define D_SOLAX_ERROR_0 "No Error Code" -#define D_SOLAX_ERROR_1 "Grid Lost Fault" -#define D_SOLAX_ERROR_2 "Grid Voltage Fault" -#define D_SOLAX_ERROR_3 "Grid Frequency Fault" -#define D_SOLAX_ERROR_4 "Pv Voltage Fault" -#define D_SOLAX_ERROR_5 "Isolation Fault" -#define D_SOLAX_ERROR_6 "Over Temperature Fault" -#define D_SOLAX_ERROR_7 "Fan Fault" -#define D_SOLAX_ERROR_8 "Other Device Fault" +#define D_PV1_VOLTAGE "Voltagem PV1" +#define D_PV1_CURRENT "Corrente PV1" +#define D_PV1_POWER "Potência PV1" +#define D_PV2_VOLTAGE "Voltagem PV2" +#define D_PV2_CURRENT "Corrente PV2" +#define D_PV2_POWER "Potência PV2" +#define D_SOLAR_POWER "Potência Solar" +#define D_INVERTER_POWER "Potência do Inversor" +#define D_STATUS "Estado" +#define D_WAITING "Esperando" +#define D_CHECKING "Verificando" +#define D_WORKING "Trabalhando" +#define D_FAILURE "Falha" +#define D_SOLAX_ERROR_0 "Nenhum código de erro" +#define D_SOLAX_ERROR_1 "Falha devido a perda de rede" +#define D_SOLAX_ERROR_2 "Falha devido a perda de tensão" +#define D_SOLAX_ERROR_3 "Falha devido a perda de frequência" +#define D_SOLAX_ERROR_4 "Falha de tensão de Pv" +#define D_SOLAX_ERROR_5 "Falha de isolamento" +#define D_SOLAX_ERROR_6 "Falha de temperatura excessiva" +#define D_SOLAX_ERROR_7 "Falha no ventilador" +#define D_SOLAX_ERROR_8 "Outra falha no dispositivo" #endif // _LANGUAGE_PT_PT_H_ diff --git a/sonoff/language/uk-UK.h b/sonoff/language/uk-UK.h index 8030b1f2a..67999ef11 100644 --- a/sonoff/language/uk-UK.h +++ b/sonoff/language/uk-UK.h @@ -139,7 +139,7 @@ #define D_RESTARTING "Перезавантаження" #define D_RESTART_REASON "Причина перезавантаження" #define D_RESTORE "відновлення" -#define D_RETAINED "нерозподілений" +#define D_RETAINED "зберігати" #define D_RULE "Правило" #define D_SAVE "Зберегти" #define D_SENSOR "Давач" diff --git a/sonoff/sonoff.h b/sonoff/sonoff.h index 9dd173167..62dce5e2f 100644 --- a/sonoff/sonoff.h +++ b/sonoff/sonoff.h @@ -270,7 +270,7 @@ enum XsnsFunctions {FUNC_SETTINGS_OVERRIDE, FUNC_PIN_STATE, FUNC_MODULE_INIT, FU FUNC_SAVE_AT_MIDNIGHT, FUNC_SAVE_BEFORE_RESTART, FUNC_PREP_BEFORE_TELEPERIOD, FUNC_AFTER_TELEPERIOD, FUNC_JSON_APPEND, FUNC_WEB_SENSOR, FUNC_COMMAND, FUNC_COMMAND_SENSOR, FUNC_COMMAND_DRIVER, FUNC_MQTT_SUBSCRIBE, FUNC_MQTT_INIT, FUNC_MQTT_DATA, - FUNC_SET_POWER, FUNC_SET_DEVICE_POWER, FUNC_SHOW_SENSOR, + FUNC_SET_POWER, FUNC_SET_DEVICE_POWER, FUNC_SHOW_SENSOR, FUNC_ANY_KEY, FUNC_ENERGY_EVERY_SECOND, FUNC_ENERGY_RESET, FUNC_RULES_PROCESS, FUNC_SERIAL, FUNC_FREE_MEM, FUNC_BUTTON_PRESSED, FUNC_WEB_ADD_BUTTON, FUNC_WEB_ADD_MAIN_BUTTON, FUNC_WEB_ADD_HANDLER, FUNC_SET_CHANNELS, FUNC_SET_SCHEME}; diff --git a/sonoff/sonoff.ino b/sonoff/sonoff.ino index 18afb76c7..18a699350 100755 --- a/sonoff/sonoff.ino +++ b/sonoff/sonoff.ino @@ -537,9 +537,10 @@ bool SendKey(uint32_t key, uint32_t device, uint32_t state) Response_P(PSTR("{\"%s%d\":{\"State\":%d}}"), (key) ? "Switch" : "Button", device, state); result = XdrvRulesProcess(); } -#ifdef USE_KNX - KnxSendButtonPower(key, device, state); -#endif // USE_KNX + int32_t payload_save = XdrvMailbox.payload; + XdrvMailbox.payload = key << 16 | state << 8 | device; + XdrvCall(FUNC_ANY_KEY); + XdrvMailbox.payload = payload_save; return result; } diff --git a/sonoff/support_wifi.ino b/sonoff/support_wifi.ino index a0f4bd3e5..bfbc3631f 100644 --- a/sonoff/support_wifi.ino +++ b/sonoff/support_wifi.ino @@ -323,12 +323,22 @@ bool WifiCheckIPv6(void) } return ipv6_global; } + +bool WifiCheckIPAddrStatus(void) // Return false for 169.254.x.x or fe80::/64 +{ + bool ip_global=false; + + for (auto a : addrList) { + if(!a.isLocal()) ip_global=true; + } + return ip_global; +} #endif // LWIP_IPV6=1 void WifiCheckIp(void) { #if LWIP_IPV6 - if(WifiCheckIPv6()) { + if(WifiCheckIPAddrStatus()) { Wifi.status = WL_CONNECTED; #else if ((WL_CONNECTED == WiFi.status()) && (static_cast(WiFi.localIP()) != 0)) { @@ -456,7 +466,7 @@ void WifiCheck(uint8_t param) WifiCheckIp(); } #if LWIP_IPV6 - if (WifiCheckIPv6()) { + if (WifiCheckIPAddrStatus()) { #else if ((WL_CONNECTED == WiFi.status()) && (static_cast(WiFi.localIP()) != 0) && !Wifi.config_type) { #endif // LWIP_IPV6=1 diff --git a/sonoff/xdrv_02_mqtt.ino b/sonoff/xdrv_02_mqtt.ino index 50f66c8ce..22ecf5f58 100644 --- a/sonoff/xdrv_02_mqtt.ino +++ b/sonoff/xdrv_02_mqtt.ino @@ -346,7 +346,7 @@ void MqttPublishLogging(const char *mxtime) void MqttPublishDirect(const char* topic, bool retained) { char sretained[CMDSZ]; - char slog_type[10]; + char slog_type[20]; #ifdef USE_DEBUG_DRIVER ShowFreeMem(PSTR("MqttPublishDirect")); diff --git a/sonoff/xdrv_11_knx.ino b/sonoff/xdrv_11_knx.ino index 6fa6231c0..a1b45800e 100644 --- a/sonoff/xdrv_11_knx.ino +++ b/sonoff/xdrv_11_knx.ino @@ -672,8 +672,13 @@ void KnxUpdatePowerState(uint8_t device, power_t state) } -void KnxSendButtonPower(uint8_t key, uint8_t device, uint8_t state) +void KnxSendButtonPower(void) { + if (!(Settings.flag.knx_enabled)) { return; } + + uint32_t key = (XdrvMailbox.payload >> 16) & 0xFF; + uint32_t device = XdrvMailbox.payload & 0xFF; + uint32_t state = (XdrvMailbox.payload >> 8) & 0xFF; // key 0 = button_topic // key 1 = switch_topic // state 0 = off @@ -681,9 +686,6 @@ void KnxSendButtonPower(uint8_t key, uint8_t device, uint8_t state) // state 2 = toggle // state 3 = hold // state 9 = clear retain flag - if (!(Settings.flag.knx_enabled)) { return; } -// if (key) -// { // Search all the registered GA that has that output (variable: device) as parameter uint8_t i = KNX_GA_Search(device + 8); @@ -1207,8 +1209,13 @@ bool Xdrv11(uint8_t function) case FUNC_LOOP: if (!global_state.wifi_down) { knx.loop(); } // Process knx events break; - case FUNC_PRE_INIT: - KNX_INIT(); + case FUNC_EVERY_50_MSECOND: + if (toggle_inhibit) { + toggle_inhibit--; + } + break; + case FUNC_ANY_KEY: + KnxSendButtonPower(); break; #ifdef USE_WEBSERVER #ifdef USE_KNX_WEB_MENU @@ -1220,14 +1227,12 @@ bool Xdrv11(uint8_t function) break; #endif // USE_KNX_WEB_MENU #endif // USE_WEBSERVER - case FUNC_EVERY_50_MSECOND: - if (toggle_inhibit) { - toggle_inhibit--; - } - break; case FUNC_COMMAND: result = DecodeCommand(kKnxCommands, KnxCommand); break; + case FUNC_PRE_INIT: + KNX_INIT(); + break; // case FUNC_SET_POWER: // break; } diff --git a/sonoff/xdrv_12_home_assistant.ino b/sonoff/xdrv_12_home_assistant.ino index d5a52a491..e27f4a4d7 100644 --- a/sonoff/xdrv_12_home_assistant.ino +++ b/sonoff/xdrv_12_home_assistant.ino @@ -28,17 +28,15 @@ const char HASS_DISCOVER_RELAY[] PROGMEM = "\"val_tpl\":\"{{value_json.%s}}\"," // POWER2 "\"pl_off\":\"%s\"," // OFF "\"pl_on\":\"%s\"," // ON -// "\"optimistic\":\"false\"," // false is Hass default when state_topic is set "\"avty_t\":\"%s\"," // tele/dualr2/LWT "\"pl_avail\":\"" D_ONLINE "\"," // Online "\"pl_not_avail\":\"" D_OFFLINE "\""; // Offline const char HASS_DISCOVER_BUTTON_SWITCH[] PROGMEM = "{\"name\":\"%s\"," // dualr2 1 BTN - "\"stat_t\":\"%s\"," // cmnd/dualr2/POWER (implies "\"optimistic\":\"false\",") -// "\"value_template\":\"{{value_json.%s}}\"," // POWER2 - "\"pl_on\":\"%s\"," // TOGGLE -// "\"optimistic\":\"false\"," // false is Hass default when state_topic is set + "\"stat_t\":\"%s\"," // stat/RESULT/ (implies "\"optimistic\":\"false\",") + "\"value_template\":\"{{value_json.%s}}\"," // BUTTON1 + "\"pl_on\":\"%s\"," // ON "\"avty_t\":\"%s\"," // tele/dualr2/LWT "\"pl_avail\":\"" D_ONLINE "\"," // Online "\"pl_not_avail\":\"" D_OFFLINE "\""; // Offline @@ -91,10 +89,6 @@ const char HASS_DISCOVER_SENSOR_TEMP[] PROGMEM = "\"val_tpl\":\"{{value_json['%s'].Temperature}}\"," // "SI7021-14":{"Temperature":null,"Humidity":null} -> {{ value_json['SI7021-14'].Temperature }} "\"dev_cla\":\"temperature\""; // temperature -const char HASS_DISCOVER_DS18X20_MULTI[] PROGMEM = - ",\"json_attributes_topic\":\"%s\"," - "\"json_attributes_template\":\"{{{'Id':value_json['%s'].Id}|tojson}}\""; // Add DS18X20 Id as information field - const char HASS_DISCOVER_SENSOR_HUM[] PROGMEM = ",\"unit_of_meas\":\"%%\"," // % "\"val_tpl\":\"{{value_json['%s'].Humidity}}\"," // "SI7021-14":{"Temperature":null,"Humidity":null} -> {{ value_json['SI7021-14'].Humidity }} @@ -115,10 +109,12 @@ const char HASS_DISCOVER_SENSOR_WATT[] PROGMEM = ",\"unit_of_meas\":\"W\"," // W "\"val_tpl\":\"{{value_json['%s'].%s}}\"," // "ENERGY":{"TotalStartTime":null,"Total":null,"Yesterday":null,"Today":null,"Power":null,"ApparentPower":null,"ReactivePower":null,"Factor":null,"Voltage":null,"Current":null} -> {{ value_json['ENERGY'].POWER }} "\"dev_cla\":\"power\""; + const char HASS_DISCOVER_SENSOR_VOLTAGE[] PROGMEM = ",\"unit_of_meas\":\"V\"," // V "\"val_tpl\":\"{{value_json['%s'].%s}}\"," // "ENERGY":{"TotalStartTime":null,"Total":null,"Yesterday":null,"Today":null,"Power":null,"ApparentPower":null,"ReactivePower":null,"Factor":null,"Voltage":null,"Current":null} -> {{ value_json['ENERGY'].Voltage }} "\"dev_cla\":\"power\""; + const char HASS_DISCOVER_SENSOR_AMPERE[] PROGMEM = ",\"unit_of_meas\":\"A\"," // A "\"val_tpl\":\"{{value_json['%s'].%s}}\"," // "ENERGY":{"TotalStartTime":null,"Total":null,"Yesterday":null,"Today":null,"Power":null,"ApparentPower":null,"ReactivePower":null,"Factor":null,"Voltage":null,"Current":null} -> {{ value_json['ENERGY'].Current }} @@ -127,7 +123,7 @@ const char HASS_DISCOVER_SENSOR_AMPERE[] PROGMEM = //ILLUMINANCE const char HASS_DISCOVER_SENSOR_ILLUMINANCE[] PROGMEM = ",\"unit_of_meas\":\"LX\"," // LX by default - "\"val_tpl\":\"{{value_json['%s'].Illuminance}}\"," // "ANALOG":{"Illuminance":34}} + "\"val_tpl\":\"{{value_json['%s'].Illuminance}}\"," // "ANALOG":{"Illuminance":34}} "\"dev_cla\":\"illuminance\""; // illuminance const char HASS_DISCOVER_SENSOR_ANY[] PROGMEM = @@ -135,8 +131,9 @@ const char HASS_DISCOVER_SENSOR_ANY[] PROGMEM = const char HASS_DISCOVER_SENSOR_HASS_STATUS[] PROGMEM = ",\"json_attributes_topic\":\"%s\"," - "\"unit_of_meas\":\" \"," // " " As unit of measurement to get a value graph in Hass - "\"val_tpl\":\"{{value_json['" D_JSON_RSSI "']}}\""; // "COUNTER":{"C1":0} -> {{ value_json['COUNTER'].C1 }} + "\"unit_of_meas\":\" \"," // " " As unit of measurement to get a value graph in Hass + "\"val_tpl\":\"{{value_json['" D_JSON_RSSI "']}}\"," // "COUNTER":{"C1":0} -> {{ value_json['COUNTER'].C1 }} + "\"ic\":\"mdi:information-outline\""; const char HASS_DISCOVER_DEVICE_INFO[] PROGMEM = ",\"uniq_id\":\"%s\"," @@ -319,20 +316,20 @@ void HAssAnnounceButtonSwitch(uint8_t device, char* topic, uint8_t present, uint char prefix[TOPSZ]; char *state_topic = stemp1; char *availability_topic = stemp2; + char jsoname[8]; - if (device+1 > MAX_FRIENDLYNAMES) { - snprintf_P(name, sizeof(name), PSTR("%s %s %d"), Settings.friendlyname[0], key?"SW":"BTN", device+1); - } else { - snprintf_P(name, sizeof(name), PSTR("%s %s"), Settings.friendlyname[device], key?"SW":"BTN"); - } + snprintf_P(name, sizeof(name), PSTR("%s %s%d"), Settings.friendlyname[0], key?"Switch":"Button", device+1); GetPowerDevice(value_template, device+1, sizeof(value_template), key + Settings.flag.device_index_enable); // Force index for Switch 1, Index on Button1 is controlled by Settings.flag.device_index_enable - GetTopic_P(state_topic, CMND, topic, value_template); // State of button is sent as CMND TOGGLE, state of switch is sent as ON/OFF + //GetTopic_P(state_topic, CMND, topic, value_template); // State of button is sent as CMND TOGGLE, state of switch is sent as ON/OFF + GetTopic_P(state_topic, STAT, mqtt_topic, PSTR(D_RSLT_RESULT)); GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT); FindPrefix(state_topic, availability_topic, prefix); + Shorten(&state_topic, prefix); Shorten(&availability_topic, prefix); - Response_P(HASS_DISCOVER_BUTTON_SWITCH, name, state_topic, Settings.state_text[toggle?2:1], availability_topic); + snprintf_P(jsoname, sizeof(jsoname), PSTR("%s%d"), key?"SWITCH":"BUTTON", device+1); + Response_P(HASS_DISCOVER_BUTTON_SWITCH, name, state_topic, jsoname, Settings.state_text[toggle?2:1], availability_topic); TryResponseAppend_P(HASS_DISCOVER_DEVICE_INFO_SHORT, unique_id, ESP.getChipId(), WiFi.macAddress().c_str()); if (strlen(prefix) > 0 ) TryResponseAppend_P(HASS_DISCOVER_TOPIC_PREFIX, prefix); if (toggle) TryResponseAppend_P(HASS_DISCOVER_BUTTON_SWITCH_TOGGLE); @@ -350,7 +347,7 @@ void HAssAnnounceSwitches(void) // Send info about buttons char *tmp = Settings.switch_topic; Format(sw_topic, tmp, sizeof(sw_topic)); - if ((strlen(sw_topic) != 0) && strcmp(sw_topic, "0")) { + if (!strcmp_P(sw_topic, "0") || strlen(sw_topic) == 0 ) { for (uint32_t switch_index = 0; switch_index < MAX_SWITCHES; switch_index++) { uint8_t switch_present = 0; uint8_t toggle = 1; @@ -379,7 +376,7 @@ void HAssAnnounceButtons(void) // Send info about buttons char *tmp = Settings.button_topic; Format(key_topic, tmp, sizeof(key_topic)); - if ((strlen(key_topic) != 0) && strcmp(key_topic, "0")) { + if (!strcmp_P(key_topic, "0") || strlen(key_topic) == 0 ) { for (uint32_t button_index = 0; button_index < MAX_KEYS; button_index++) { uint8_t button_present = 0; uint8_t toggle = 1; @@ -404,13 +401,13 @@ void HAssAnnounceButtons(void) } } -void HAssAnnounceSensor(const char* sensorname, const char* subsensortype) +void HAssAnnounceSensor(const char* sensorname, const char* subsensortype) { char stopic[TOPSZ]; char stemp1[TOPSZ]; char stemp2[TOPSZ]; char unique_id[30]; - bool is_sensor = true; + bool is_sensor = true; // Announce sensor, special handling of temperature and humidity sensors mqtt_data[0] = '\0'; // Clear retained message @@ -466,10 +463,10 @@ void HAssAnnounceSensor(const char* sensorname, const char* subsensortype) } else if (!strcmp_P(subsensortype, PSTR(D_JSON_ILLUMINANCE))){ TryResponseAppend_P(HASS_DISCOVER_SENSOR_ILLUMINANCE, sensorname, subsensortype); } else { - if (is_sensor){ - TryResponseAppend_P(PSTR(",\"unit_of_meas\":\" \"")); // " " As unit of measurement to get a value graph (not available for binary sensors) - } - TryResponseAppend_P(HASS_DISCOVER_SENSOR_ANY, sensorname, subsensortype); + if (is_sensor){ + TryResponseAppend_P(PSTR(",\"unit_of_meas\":\" \"")); // " " As unit of measurement to get a value graph (not available for binary sensors) + } + TryResponseAppend_P(HASS_DISCOVER_SENSOR_ANY, sensorname, subsensortype); } TryResponseAppend_P(PSTR("}")); } @@ -611,6 +608,22 @@ void HAssDiscover(void) hass_init_step = 1; // Delayed discovery } +void HAssAnyKey(void) +{ + if (!Settings.flag.hass_discovery) { return; } + + uint32_t key = (XdrvMailbox.payload >> 16) & 0xFF; + uint32_t device = XdrvMailbox.payload & 0xFF; + uint32_t state = (XdrvMailbox.payload >> 8) & 0xFF; + + char scommand[CMDSZ]; + snprintf_P(scommand, sizeof(scommand), PSTR("%s%d"), (key) ? "SWITCH" : "BUTTON", device); + char stopic[TOPSZ]; + GetTopic_P(stopic, STAT, mqtt_topic, (Settings.flag.mqtt_response) ? scommand : S_RSLT_RESULT); + Response_P(S_JSON_COMMAND_SVALUE, scommand, GetStateText(state)); + MqttPublish(stopic); +} + /*********************************************************************************************\ * Interface \*********************************************************************************************/ @@ -621,10 +634,6 @@ bool Xdrv12(uint8_t function) if (Settings.flag.mqtt_enabled) { switch (function) { - case FUNC_MQTT_INIT: - hass_mode = 0; // Discovery only if Settings.flag.hass_discovery is set - hass_init_step = 2; // Delayed discovery - break; case FUNC_EVERY_SECOND: if (hass_init_step) { hass_init_step--; @@ -641,6 +650,13 @@ bool Xdrv12(uint8_t function) } } break; + case FUNC_ANY_KEY: + HAssAnyKey(); + break; + case FUNC_MQTT_INIT: + hass_mode = 0; // Discovery only if Settings.flag.hass_discovery is set + hass_init_step = 2; // Delayed discovery + break; } } return result; diff --git a/sonoff/xdrv_16_tuyamcu.ino b/sonoff/xdrv_16_tuyamcu.ino index 797cdba8f..2cf63c8d0 100644 --- a/sonoff/xdrv_16_tuyamcu.ino +++ b/sonoff/xdrv_16_tuyamcu.ino @@ -276,8 +276,11 @@ bool TuyaSetPower(void) uint8_t rpower = XdrvMailbox.index; int16_t source = XdrvMailbox.payload; + uint8_t dpid = TuyaGetDpId(TUYA_MCU_FUNC_REL1 + active_device - 1); + if (dpid == 0) dpid = TuyaGetDpId(TUYA_MCU_FUNC_REL1_INV + active_device - 1); + if (source != SRC_SWITCH && TuyaSerial) { // ignore to prevent loop from pushing state from faceplate interaction - TuyaSendBool(active_device, bitRead(rpower, active_device-1) ^ bitRead(rel_inverted, active_device-1)); + TuyaSendBool(dpid, bitRead(rpower, active_device-1) ^ bitRead(rel_inverted, active_device-1)); status = true; } return status; From 897fa455760390dd67457ea4ca42bd0f9e9eb74e Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Fri, 25 Oct 2019 12:28:50 +0200 Subject: [PATCH 049/428] Release 6.7 --- RELEASENOTES.md | 6 +++--- sonoff/my_user_config.h | 4 ++-- sonoff/sonoff_post.h | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 6f02b4a74..a6aaeb543 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -154,7 +154,7 @@ The following binary downloads have been compiled with ESP8266/Arduino library c | USE_ARILUX_RF | - | - | x | x | x | - | - | | USE_SHUTTER | - | - | - | - | - | - | - | | USE_DEEPSLEEP | - | - | - | - | - | - | - | -| USE_EXS_DIMMER | - | - | - | - | - | - | - | +| USE_EXS_DIMMER | - | - | x | x | - | - | - | | | | | | | | | | | Feature or Sensor | minimal | basic | sonoff | knx | sensors | ir | display | Remarks | USE_LIGHT | - | x | x | x | x | x | x | @@ -252,8 +252,8 @@ The following binary downloads have been compiled with ESP8266/Arduino library c | USE_DISPLAY_MATRIX | - | - | - | - | - | - | x | | USE_DISPLAY_SH1106 | - | - | - | - | - | - | x | | USE_DISPLAY_ILI9341 | - | - | - | - | - | - | x | -| USE_DISPLAY_EPAPER_29 | - | - | - | - | - | - | x | Disabled for core 2.3.0 -| USE_DISPLAY_EPAPER_42 | - | - | - | - | - | - | x | Disabled for core 2.3.0 +| USE_DISPLAY_EPAPER_29 | - | - | - | - | - | - | x | +| USE_DISPLAY_EPAPER_42 | - | - | - | - | - | - | x | | USE_DISPLAY_ILI9488 | - | - | - | - | - | - | - | | USE_DISPLAY_SSD1351 | - | - | - | - | - | - | - | | USE_DISPLAY_RA8876 | - | - | - | - | - | - | - | diff --git a/sonoff/my_user_config.h b/sonoff/my_user_config.h index cb067fbf7..8dcf5f7c2 100644 --- a/sonoff/my_user_config.h +++ b/sonoff/my_user_config.h @@ -314,8 +314,8 @@ #define USE_ARILUX_RF // Add support for Arilux RF remote controller (+0k8 code, 252 iram (non 2.3.0)) //#define USE_SHUTTER // Add Shutter support for up to 4 shutter with different motortypes (+6k code) //#define USE_DEEPSLEEP // Add support for deepsleep (+1k code) -//#define USE_EXS_DIMMER // Add support for ES-Store WiFi Dimmer (+1k5 code) -// #define EXS_MCU_CMNDS // Add command to send MCU commands (+0k8 code) +#define USE_EXS_DIMMER // Add support for ES-Store WiFi Dimmer (+1k5 code) + #define EXS_MCU_CMNDS // Add command to send MCU commands (+0k8 code) // -- Optional light modules ---------------------- #define USE_WS2812 // WS2812 Led string using library NeoPixelBus (+5k code, +1k mem, 232 iram) - Disable by // diff --git a/sonoff/sonoff_post.h b/sonoff/sonoff_post.h index b8b282fa7..2c37b27cd 100644 --- a/sonoff/sonoff_post.h +++ b/sonoff/sonoff_post.h @@ -97,7 +97,7 @@ char* ToHex_P(const unsigned char * in, size_t insz, char * out, size_t outsz, c #define USE_ARILUX_RF // Add support for Arilux RF remote controller (+0k8 code, 252 iram (non 2.3.0)) //#define USE_SHUTTER // Add Shutter support for up to 4 shutter with different motortypes (+6k code) //#define USE_DEEPSLEEP // Add support for deepsleep (+1k code) -//#define USE_EXS_DIMMER // Add support for EX-Store WiFi Dimmer +#define USE_EXS_DIMMER // Add support for EX-Store WiFi Dimmer // -- Optional light modules ---------------------- #define USE_LIGHT // Add Dimmer/Light support From 60ac14767cf0c80db9c1f3df5d5e3e637783bf68 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Fri, 25 Oct 2019 18:28:35 +0200 Subject: [PATCH 050/428] Fix immediate issues Fix immediate issues --- sonoff/settings.ino | 3 ++- sonoff/xdrv_03_energy.ino | 12 +++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/sonoff/settings.ino b/sonoff/settings.ino index 0a34123df..f49eb9495 100644 --- a/sonoff/settings.ino +++ b/sonoff/settings.ino @@ -1199,6 +1199,7 @@ void SettingsDelta(void) } if (Settings.version < 0x06060014) { // Clear unused parameters for future use +/* Settings.flag3.ex_tuya_dimmer_range_255 = 0; Settings.flag3.ex_tuya_dimmer_min_limit = 0; Settings.param[P_ex_TUYA_RELAYS] = 0; @@ -1208,7 +1209,7 @@ void SettingsDelta(void) Settings.param[P_ex_TUYA_POWER_ID] = 0; Settings.ex_baudrate = 0; Settings.ex_sbaudrate = 0; - +*/ Settings.flag3.fast_power_cycle_disable = 0; Settings.energy_power_delta = Settings.ex_energy_power_delta; Settings.ex_energy_power_delta = 0; diff --git a/sonoff/xdrv_03_energy.ino b/sonoff/xdrv_03_energy.ino index 30b0237e2..aa580421d 100644 --- a/sonoff/xdrv_03_energy.ino +++ b/sonoff/xdrv_03_energy.ino @@ -292,11 +292,13 @@ void EnergyMarginCheck(void) DEBUG_DRIVER_LOG(PSTR("NRG: Delta %d, Power %d"), delta, min_power); - if ( ((Settings.energy_power_delta < 101) && (((delta * 100) / min_power) > Settings.energy_power_delta)) || // 1..100 = Percentage - ((Settings.energy_power_delta > 100) && (delta > (Settings.energy_power_delta -100))) ) { // 101..32000 = Absolute - Energy.power_delta = true; - Energy.power_history[1] = Energy.active_power[0]; // We only want one report so reset history - Energy.power_history[2] = Energy.active_power[0]; + if (delta) { // Fix divide by 0 exception (#6741) + if (((Settings.energy_power_delta < 101) && (((delta * 100) / min_power) > Settings.energy_power_delta)) || // 1..100 = Percentage + ((Settings.energy_power_delta > 100) && (delta > (Settings.energy_power_delta -100)))) { // 101..32000 = Absolute + Energy.power_delta = true; + Energy.power_history[1] = Energy.active_power[0]; // We only want one report so reset history + Energy.power_history[2] = Energy.active_power[0]; + } } } Energy.power_history[0] = Energy.power_history[1]; // Shift in history every second allowing power changes to settle for up to three seconds From 90933024c23ae37d904f8c3e6d6eb1970e371a34 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Fri, 25 Oct 2019 18:32:11 +0200 Subject: [PATCH 051/428] Update settings.ino --- sonoff/settings.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sonoff/settings.ino b/sonoff/settings.ino index f49eb9495..6b6300c80 100644 --- a/sonoff/settings.ino +++ b/sonoff/settings.ino @@ -1198,8 +1198,8 @@ void SettingsDelta(void) } } if (Settings.version < 0x06060014) { - // Clear unused parameters for future use /* + // Clear unused parameters for future use Settings.flag3.ex_tuya_dimmer_range_255 = 0; Settings.flag3.ex_tuya_dimmer_min_limit = 0; Settings.param[P_ex_TUYA_RELAYS] = 0; From bcd3065637312fff2f32aebc8a70b977069c64e3 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sat, 26 Oct 2019 13:17:21 +0200 Subject: [PATCH 052/428] Release 6.7.1 --- README.md | 2 +- sonoff/_changelog.ino | 5 +++++ sonoff/sonoff_version.h | 2 +- sonoff/xdrv_03_energy.ino | 2 +- sonoff/xdrv_10_scripter.ino | 9 +++++++-- 5 files changed, 15 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 80b88471e..5f8af69d2 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ See [RELEASENOTES.md](https://github.com/arendst/Sonoff-Tasmota/blob/master/RELE In addition to the [release webpage](https://github.com/arendst/Sonoff-Tasmota/releases/latest) the binaries can also be downloaded from http://thehackbox.org/tasmota/release/ ## Development -[![Dev Version](https://img.shields.io/badge/development%20version-v6.7.0.x-blue.svg)](https://github.com/arendst/Sonoff-Tasmota) +[![Dev Version](https://img.shields.io/badge/development%20version-v6.7.1.x-blue.svg)](https://github.com/arendst/Sonoff-Tasmota) [![Download Dev](https://img.shields.io/badge/download-development-yellow.svg)](http://thehackbox.org/tasmota/) [![Build Status](https://img.shields.io/travis/arendst/Sonoff-Tasmota.svg)](https://travis-ci.org/arendst/Sonoff-Tasmota) diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index b964b449d..1cb954c7b 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -1,4 +1,9 @@ /*********************************************************************************************\ + * 6.7.1 20191026 + * Release + * Fix on energy monitoring devices using PowerDelta Exception0 with epc1:0x4000dce5 = Divide by zero (#6750) + * Fix Script array bug (#6751) + * * 6.7.0 20191025 * Release * diff --git a/sonoff/sonoff_version.h b/sonoff/sonoff_version.h index 1731d80a5..4780ef5e3 100644 --- a/sonoff/sonoff_version.h +++ b/sonoff/sonoff_version.h @@ -20,6 +20,6 @@ #ifndef _SONOFF_VERSION_H_ #define _SONOFF_VERSION_H_ -const uint32_t VERSION = 0x06070000; +const uint32_t VERSION = 0x06070100; #endif // _SONOFF_VERSION_H_ diff --git a/sonoff/xdrv_03_energy.ino b/sonoff/xdrv_03_energy.ino index aa580421d..263b2fa15 100644 --- a/sonoff/xdrv_03_energy.ino +++ b/sonoff/xdrv_03_energy.ino @@ -292,7 +292,7 @@ void EnergyMarginCheck(void) DEBUG_DRIVER_LOG(PSTR("NRG: Delta %d, Power %d"), delta, min_power); - if (delta) { // Fix divide by 0 exception (#6741) + if ((delta > 0) && (min_power > 0)) { // Fix divide by 0 exception (#6741) if (((Settings.energy_power_delta < 101) && (((delta * 100) / min_power) > Settings.energy_power_delta)) || // 1..100 = Percentage ((Settings.energy_power_delta > 100) && (delta > (Settings.energy_power_delta -100)))) { // 101..32000 = Absolute Energy.power_delta = true; diff --git a/sonoff/xdrv_10_scripter.ino b/sonoff/xdrv_10_scripter.ino index e4610c830..434c1716a 100644 --- a/sonoff/xdrv_10_scripter.ino +++ b/sonoff/xdrv_10_scripter.ino @@ -2731,9 +2731,14 @@ int16_t Run_Scripter(const char *type, int8_t tlen, char *js) { uint8_t index=glob_script_mem.type[ind.index].index; if ((vtype&STYPE)==0) { // numeric result - if (ind.bits.settable) { + if (ind.bits.settable || ind.bits.is_filter) { dfvar=&sysvar; - sysv_type=ind.index; + if (ind.bits.settable) { + sysv_type=ind.index; + } else { + sysv_type=0; + } + } else { dfvar=&glob_script_mem.fvars[index]; sysv_type=0; From 7bafb0f897fbbd5ae8a0dac039b4545318bd7733 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sat, 26 Oct 2019 14:11:25 +0200 Subject: [PATCH 053/428] Update RELEASENOTES.md --- RELEASENOTES.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index a6aaeb543..747baccca 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -259,7 +259,7 @@ The following binary downloads have been compiled with ESP8266/Arduino library c | USE_DISPLAY_RA8876 | - | - | - | - | - | - | - | ## Changelog -Version 6.7.0 20191025 +Version 6.7.1 20191026 * Remove support for WPS and SmartConfig in favour of Web server (!) based WifiManager (#6680) * Remove binary sonoff-classic (#6680) * Remove command ``SetOption2`` @@ -287,6 +287,7 @@ Version 6.7.0 20191025 * Change filename of configuration backup from using FriendlyName1 to Hostname solving diacritic issues (#2422) * Change Store AWS IoT Private Key and Certificate in SPI Flash avoiding device-specific compilations * Change defines **USE_TX20_WIND_SENSOR** and **USE_RC_SWITCH** in my_user_config.h to disable to lower iram usage enabling latest core compilation (#6060, #6062) + * Fix PowerDelta related exception0: epc1:0x4000dce5 (#6750) * Fix handling of ligth channels when pwm_multichannel (``SetOption68``) is enabled * Fix better handling of PWM White Temperature mode for Module 48 (#6534) * Fix TasmotaSerial: move serial send to IRAM for high speed baud rates From 58ec01d6439dd3b42d7866ecbcf70e61738e4ab2 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Wed, 13 Nov 2019 11:46:56 +0100 Subject: [PATCH 054/428] Release 7.1 --- .github/ISSUE_TEMPLATE/Bug_report.md | 36 +- .github/ISSUE_TEMPLATE/Custom.md | 34 +- .github/PULL_REQUEST_TEMPLATE.md | 6 +- .gitignore | 2 +- .gitpod.yml | 2 +- .travis.yml | 56 +- API.md | 192 +- CONTRIBUTING.md | 8 +- Doxyfile | 2489 +++++++ I2CDEVICES.md | 66 + README.md | 72 +- REFERENCE.md | 20 - RELEASENOTES.md | 199 +- SUPPORT.md | 20 +- arduino/version 2.5.2/boards.txt | 6252 ----------------- .../platform.txt | 55 +- build-container/README.md | 20 +- build-container/entrypoint.sh | 2 +- .../Adafruit_SSD1306.cpp | 7 + .../Adafruit_TSL2591.cpp | 464 ++ .../Adafruit_TSL2591.h | 86 + lib/ArduinoHexParse/README.md | 3 - lib/ArduinoHexParse/keywords.txt | 25 - lib/ArduinoHexParse/library.json | 12 - lib/ArduinoHexParse/library.properties | 9 - lib/ArduinoHexParse/src/ArduinoHexParse.cpp | 134 - lib/ArduinoHexParse/src/ArduinoHexParse.h | 47 - lib/ArduinoJson-5.13.4/library.properties | 2 +- lib/HPMA115S0/LICENSE.md | 7 + lib/HPMA115S0/README.md | 2 + lib/HPMA115S0/example/example.ino | 34 + lib/HPMA115S0/library.properties | 9 + lib/HPMA115S0/src/hpma115S0.cpp | 190 + lib/HPMA115S0/src/hpma115S0.h | 123 + lib/I2Cdevlib-ADS1115/.library.json | 40 - lib/I2Cdevlib-ADS1115/ADS1115.cpp | 649 -- lib/I2Cdevlib-ADS1115/ADS1115.h | 200 - .../ADS1115_differential.ino | 91 - .../ADS1115_single/ADS1115_single.ino | 110 - lib/I2Cdevlib-ADS1115/library.json | 18 - lib/I2Cdevlib-MPU6050/MPU6050.cpp | 12 +- lib/I2Cdevlib-MPU6050/MPU6050.h | 32 +- .../MPU6050_6Axis_MotionApps20.h | 10 +- .../MPU6050_9Axis_MotionApps41.h | 26 +- lib/I2Cdevlib-MPU6050/helper_3dmath.h | 26 +- .../.github/CONTRIBUTING.md | 82 - .../.github/Contributors.md | 20 - .../.github/issue_template.md | 42 - lib/IRremoteESP8266-2.6.5/.gitignore | 53 - lib/IRremoteESP8266-2.6.5/.gitmodules | 4 - lib/IRremoteESP8266-2.6.5/.style.yapf | 3 - lib/IRremoteESP8266-2.6.5/.travis.yml | 74 - .../CommonAcControl/CommonAcControl.ino | 81 - .../examples/IRGCTCPServer/platformio.ini | 18 - .../examples/IRServer/platformio.ini | 18 - .../examples/IRrecvDemo/platformio.ini | 18 - .../examples/IRrecvDump/platformio.ini | 18 - .../examples/IRrecvDumpV2/platformio.ini | 18 - .../examples/IRsendDemo/platformio.ini | 18 - .../examples/IRsendProntoDemo/platformio.ini | 18 - .../JVCPanasonicSendDemo/platformio.ini | 18 - .../examples/LGACSend/platformio.ini | 18 - .../examples/SmartIRRepeater/platformio.ini | 18 - .../examples/TurnOnArgoAC/platformio.ini | 18 - .../examples/TurnOnDaikinAC/platformio.ini | 18 - .../examples/TurnOnFujitsuAC/platformio.ini | 18 - .../TurnOnKelvinatorAC/platformio.ini | 18 - .../TurnOnMitsubishiAC/platformio.ini | 18 - .../TurnOnMitsubishiHeavyAc/platformio.ini | 18 - .../examples/TurnOnPanasonicAC/platformio.ini | 18 - .../examples/TurnOnToshibaAC/platformio.ini | 18 - .../examples/TurnOnTrotecAC/platformio.ini | 18 - lib/IRremoteESP8266-2.6.5/src/ir_Hitachi.cpp | 442 -- .../src/ir_Mitsubishi.cpp | 811 --- lib/IRremoteESP8266-2.6.5/src/ir_Mitsubishi.h | 121 - .../tools/auto_analyse_raw_data.py | 432 -- .../tools/auto_analyse_raw_data_test.py | 492 -- .../CPPLINT.cfg | 0 .../LICENSE.txt | 0 .../README.md | 16 +- lib/IRremoteESP8266-2.7.0/README_fr.md | 83 + .../ReleaseNotes.md | 71 + .../SupportedProtocols.md | 30 +- .../CommonAcControl/CommonAcControl.ino | 72 + .../examples/CommonAcControl/platformio.ini | 5 +- .../ControlSamsungAC/ControlSamsungAC.ino | 0 .../examples/ControlSamsungAC/platformio.ini | 5 +- .../DumbIRRepeater/DumbIRRepeater.ino | 0 .../examples/DumbIRRepeater/platformio.ini | 5 +- .../examples/IRGCSendDemo/IRGCSendDemo.ino | 0 .../examples/IRGCSendDemo/platformio.ini | 5 +- .../examples/IRGCTCPServer/IRGCTCPServer.ino | 0 .../examples/IRGCTCPServer/platformio.ini | 17 + .../examples/IRMQTTServer/IRMQTTServer.h | 51 +- .../examples/IRMQTTServer/IRMQTTServer.ino | 662 +- .../examples/IRMQTTServer/platformio.ini | 15 +- .../examples/IRServer/IRServer.ino | 0 .../examples/IRServer/platformio.ini | 17 + .../examples/IRrecvDemo/IRrecvDemo.ino | 0 .../examples/IRrecvDemo/platformio.ini | 17 + .../examples/IRrecvDump/IRrecvDump.ino | 0 .../examples/IRrecvDump/platformio.ini | 17 + .../examples/IRrecvDumpV2/IRrecvDumpV2.ino | 32 +- .../examples/IRrecvDumpV2/platformio.ini | 46 + .../examples/IRsendDemo/IRsendDemo.ino | 0 .../examples/IRsendDemo/platformio.ini | 17 + .../IRsendProntoDemo/IRsendProntoDemo.ino | 6 +- .../examples/IRsendProntoDemo/platformio.ini | 17 + .../JVCPanasonicSendDemo.ino | 0 .../JVCPanasonicSendDemo/platformio.ini | 17 + .../examples/LGACSend/LGACSend.ino | 0 .../examples/LGACSend/platformio.ini | 17 + .../SmartIRRepeater/SmartIRRepeater.ino | 0 .../examples/SmartIRRepeater/platformio.ini | 17 + .../examples/TurnOnArgoAC/TurnOnArgoAC.ino | 0 .../examples/TurnOnArgoAC/platformio.ini | 17 + .../TurnOnDaikinAC/TurnOnDaikinAC.ino | 0 .../examples/TurnOnDaikinAC/platformio.ini | 17 + .../TurnOnFujitsuAC/TurnOnFujitsuAC.ino | 0 .../examples/TurnOnFujitsuAC/platformio.ini | 17 + .../TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino | 0 .../TurnOnKelvinatorAC/platformio.ini | 17 + .../TurnOnMitsubishiAC/TurnOnMitsubishiAC.ino | 0 .../TurnOnMitsubishiAC/platformio.ini | 17 + .../TurnOnMitsubishiHeavyAc.ino | 0 .../TurnOnMitsubishiHeavyAc/platformio.ini | 17 + .../TurnOnPanasonicAC/TurnOnPanasonicAC.ino | 0 .../examples/TurnOnPanasonicAC/platformio.ini | 17 + .../TurnOnToshibaAC/TurnOnToshibaAC.ino | 0 .../examples/TurnOnToshibaAC/platformio.ini | 17 + .../TurnOnTrotecAC/TurnOnTrotecAC.ino | 0 .../examples/TurnOnTrotecAC/platformio.ini | 17 + .../examples/Web-AC-control/README.md | 43 + .../Web-AC-control/Web-AC-control.ino | 300 + .../examples/Web-AC-control/platformio.ini | 36 + .../examples/Web-AC-control/printscreen.png | Bin 0 -> 25768 bytes .../Web-AC-control/upload/favicon.ico | Bin 0 -> 16446 bytes .../Web-AC-control/upload/level_1_off.svg | 49 + .../Web-AC-control/upload/level_1_on.svg | 49 + .../Web-AC-control/upload/level_2_off.svg | 49 + .../Web-AC-control/upload/level_2_on.svg | 49 + .../Web-AC-control/upload/level_3_off.svg | 49 + .../Web-AC-control/upload/level_3_on.svg | 49 + .../Web-AC-control/upload/level_4_off.svg | 49 + .../Web-AC-control/upload/level_4_on.svg | 49 + .../examples/Web-AC-control/upload/ui.html | 109 + .../examples/Web-AC-control/upload/ui.js | 132 + .../keywords.txt | 705 +- .../library.json | 2 +- .../library.properties | 2 +- .../pylintrc | 0 .../src/CPPLINT.cfg | 0 .../src/IRac.cpp | 1140 +-- .../src/IRac.h | 52 +- .../src/IRrecv.cpp | 21 +- .../src/IRrecv.h | 20 +- .../src/IRremoteESP8266.h | 173 +- .../src/IRsend.cpp | 21 + .../src/IRsend.h | 49 +- lib/IRremoteESP8266-2.7.0/src/IRtext.cpp | 153 + lib/IRremoteESP8266-2.7.0/src/IRtext.h | 146 + .../src/IRtimer.cpp | 0 .../src/IRtimer.h | 0 .../src/IRutils.cpp | 320 +- .../src/IRutils.h | 43 + lib/IRremoteESP8266-2.7.0/src/i18n.h | 25 + .../src/ir_Aiwa.cpp | 0 .../src/ir_Amcor.cpp | 71 +- .../src/ir_Amcor.h | 26 +- .../src/ir_Argo.cpp | 151 +- .../src/ir_Argo.h | 80 +- .../src/ir_Carrier.cpp | 0 .../src/ir_Coolix.cpp | 266 +- .../src/ir_Coolix.h | 51 +- .../src/ir_Daikin.cpp | 1572 +++-- .../src/ir_Daikin.h | 213 +- .../src/ir_Denon.cpp | 0 .../src/ir_Dish.cpp | 0 .../src/ir_Electra.cpp | 92 +- .../src/ir_Electra.h | 25 +- .../src/ir_Fujitsu.cpp | 242 +- .../src/ir_Fujitsu.h | 24 +- .../src/ir_GICable.cpp | 0 .../src/ir_GlobalCache.cpp | 0 .../src/ir_Goodweather.cpp | 183 +- .../src/ir_Goodweather.h | 30 +- .../src/ir_Gree.cpp | 264 +- .../src/ir_Gree.h | 62 +- .../src/ir_Haier.cpp | 464 +- .../src/ir_Haier.h | 89 +- lib/IRremoteESP8266-2.7.0/src/ir_Hitachi.cpp | 753 ++ .../src/ir_Hitachi.h | 77 +- .../src/ir_Inax.cpp | 0 .../src/ir_JVC.cpp | 0 .../src/ir_Kelvinator.cpp | 163 +- .../src/ir_Kelvinator.h | 0 .../src/ir_LG.cpp | 0 .../src/ir_LG.h | 0 .../src/ir_Lasertag.cpp | 0 .../src/ir_Lego.cpp | 0 .../src/ir_Lutron.cpp | 0 .../src/ir_MWM.cpp | 0 .../src/ir_Magiquest.cpp | 0 .../src/ir_Magiquest.h | 0 .../src/ir_Midea.cpp | 133 +- .../src/ir_Midea.h | 32 +- .../src/ir_Mitsubishi.cpp | 1594 +++++ lib/IRremoteESP8266-2.7.0/src/ir_Mitsubishi.h | 312 + .../src/ir_MitsubishiHeavy.cpp | 561 +- .../src/ir_MitsubishiHeavy.h | 74 +- .../src/ir_NEC.cpp | 0 .../src/ir_NEC.h | 0 .../src/ir_Neoclima.cpp | 221 +- .../src/ir_Neoclima.h | 38 +- .../src/ir_Nikai.cpp | 0 .../src/ir_Panasonic.cpp | 410 +- .../src/ir_Panasonic.h | 72 +- .../src/ir_Pioneer.cpp | 0 .../src/ir_Pronto.cpp | 0 .../src/ir_RC5_RC6.cpp | 0 .../src/ir_RCMM.cpp | 0 .../src/ir_Samsung.cpp | 231 +- .../src/ir_Samsung.h | 34 +- .../src/ir_Sanyo.cpp | 0 .../src/ir_Sharp.cpp | 118 +- .../src/ir_Sharp.h | 13 +- .../src/ir_Sherwood.cpp | 0 .../src/ir_Sony.cpp | 0 .../src/ir_Tcl.cpp | 197 +- .../src/ir_Tcl.h | 35 +- .../src/ir_Teco.cpp | 185 +- .../src/ir_Teco.h | 51 +- .../src/ir_Toshiba.cpp | 112 +- .../src/ir_Toshiba.h | 16 +- .../src/ir_Trotec.cpp | 97 +- .../src/ir_Trotec.h | 13 +- .../src/ir_Vestel.cpp | 255 +- .../src/ir_Vestel.h | 15 +- .../src/ir_Whirlpool.cpp | 210 +- .../src/ir_Whirlpool.h | 31 +- .../src/ir_Whynter.cpp | 0 .../src/locale/README.md | 97 + lib/IRremoteESP8266-2.7.0/src/locale/de-CH.h | 158 + lib/IRremoteESP8266-2.7.0/src/locale/de-DE.h | 126 + .../src/locale/defaults.h | 440 ++ lib/IRremoteESP8266-2.7.0/src/locale/en-AU.h | 8 + lib/IRremoteESP8266-2.7.0/src/locale/en-IE.h | 8 + lib/IRremoteESP8266-2.7.0/src/locale/en-UK.h | 8 + lib/IRremoteESP8266-2.7.0/src/locale/en-US.h | 13 + lib/IRremoteESP8266-2.7.0/src/locale/es-ES.h | 134 + lib/IRremoteESP8266-2.7.0/src/locale/fr-FR.h | 115 + .../test/IRac_test.cpp | 281 +- .../test/IRrecv_test.cpp | 0 .../test/IRrecv_test.h | 0 .../test/IRsend_test.cpp | 0 .../test/IRsend_test.h | 0 .../test/IRutils_test.cpp | 187 +- .../test/Makefile | 21 +- .../test/ir_Aiwa_test.cpp | 0 .../test/ir_Amcor_test.cpp | 6 +- .../test/ir_Argo_test.cpp | 12 +- .../test/ir_Carrier_test.cpp | 0 .../test/ir_Coolix_test.cpp | 57 +- .../test/ir_Daikin_test.cpp | 574 +- .../test/ir_Denon_test.cpp | 0 .../test/ir_Dish_test.cpp | 0 .../test/ir_Electra_test.cpp | 8 +- .../test/ir_Fujitsu_test.cpp | 198 +- .../test/ir_GICable_test.cpp | 0 .../test/ir_GlobalCache_test.cpp | 0 .../test/ir_Goodweather_test.cpp | 40 +- .../test/ir_Gree_test.cpp | 16 +- .../test/ir_Haier_test.cpp | 98 +- .../test/ir_Hitachi_test.cpp | 314 +- .../test/ir_Inax_test.cpp | 0 .../test/ir_JVC_test.cpp | 0 .../test/ir_Kelvinator_test.cpp | 12 +- .../test/ir_LG_test.cpp | 0 .../test/ir_Lasertag_test.cpp | 0 .../test/ir_Lego_test.cpp | 0 .../test/ir_Lutron_test.cpp | 0 .../test/ir_MWM_test.cpp | 0 .../test/ir_Magiquest_test.cpp | 0 .../test/ir_Midea_test.cpp | 49 +- .../test/ir_MitsubishiHeavy_test.cpp | 74 +- .../test/ir_Mitsubishi_test.cpp | 565 +- .../test/ir_NEC_test.cpp | 0 .../test/ir_Neoclima_test.cpp | 2 +- .../test/ir_Nikai_test.cpp | 0 .../test/ir_Panasonic_test.cpp | 81 +- .../test/ir_Pioneer_test.cpp | 0 .../test/ir_Pronto_test.cpp | 0 .../test/ir_RC5_RC6_test.cpp | 0 .../test/ir_RCMM_test.cpp | 0 .../test/ir_Samsung_test.cpp | 70 +- .../test/ir_Sanyo_test.cpp | 0 .../test/ir_Sharp_test.cpp | 26 +- .../test/ir_Sherwood_test.cpp | 0 .../test/ir_Sony_test.cpp | 0 .../test/ir_Tcl_test.cpp | 44 +- .../test/ir_Teco_test.cpp | 74 +- .../test/ir_Toshiba_test.cpp | 6 +- .../test/ir_Trotec_test.cpp | 4 +- .../test/ir_Vestel_test.cpp | 30 +- .../test/ir_Whirlpool_test.cpp | 20 +- .../test/ir_Whynter_test.cpp | 0 .../tools/Makefile | 12 +- .../tools/RawToGlobalCache.sh | 0 .../tools/auto_analyse_raw_data.py | 729 ++ .../tools/auto_analyse_raw_data_test.py | 1170 +++ .../tools/gc_decode.cpp | 50 +- .../tools/generate_irtext_h.sh | 36 + .../tools/mkkeywords | 0 .../tools/mode2_decode.cpp | 0 .../tools/scrape_supported_devices.py | 0 .../.github/ISSUE_TEMPLATE/bug_report.md | 34 + .../.github/ISSUE_TEMPLATE/feature_request.md | 20 + lib/TasmotaModbus-1.2.0/library.json | 2 +- lib/TasmotaSerial-2.4.1/library.json | 2 +- .../src/t_bearssl_tasmota_config.h | 4 + pio/rename-firmware.py | 12 + platformio.ini | 331 +- sonoff/xdrv_05_irremote.ino | 1070 --- sonoff/xdrv_29_deepsleep.ino | 158 - sonoff/xdrv_31_arduino_slave.ino | 291 - sonoff/xsns_12_ads1115_i2cdev.ino | 154 - sonoff/xsns_50_paj7620.ino | 570 -- {sonoff => tasmota}/Parsing.cpp | 0 {sonoff => tasmota}/StackThunk_light.cpp | 0 {sonoff => tasmota}/StackThunk_light.h | 0 .../WiFiClientSecureLightBearSSL.cpp | 0 .../WiFiClientSecureLightBearSSL.h | 0 {sonoff => tasmota}/_changelog.ino | 46 +- {sonoff => tasmota}/core_esp8266_timer.c | 0 .../core_esp8266_wiring_digital.c | 0 {sonoff => tasmota}/core_esp8266_wiring_pwm.c | 0 {sonoff => tasmota}/i18n.h | 16 +- {sonoff => tasmota}/language/bg-BG.h | 14 +- {sonoff => tasmota}/language/cs-CZ.h | 14 +- {sonoff => tasmota}/language/de-DE.h | 14 +- {sonoff => tasmota}/language/el-GR.h | 14 +- {sonoff => tasmota}/language/en-GB.h | 14 +- {sonoff => tasmota}/language/es-ES.h | 16 +- {sonoff => tasmota}/language/fr-FR.h | 14 +- {sonoff => tasmota}/language/he-HE.h | 14 +- {sonoff => tasmota}/language/hu-HU.h | 14 +- {sonoff => tasmota}/language/it-IT.h | 14 +- {sonoff => tasmota}/language/ko-KO.h | 112 +- {sonoff => tasmota}/language/nl-NL.h | 14 +- {sonoff => tasmota}/language/pl-PL.h | 14 +- {sonoff => tasmota}/language/pt-BR.h | 18 +- {sonoff => tasmota}/language/pt-PT.h | 20 +- {sonoff => tasmota}/language/ru-RU.h | 14 +- {sonoff => tasmota}/language/sk-SK.h | 14 +- {sonoff => tasmota}/language/sv-SE.h | 14 +- {sonoff => tasmota}/language/tr-TR.h | 14 +- {sonoff => tasmota}/language/uk-UK.h | 14 +- {sonoff => tasmota}/language/zh-CN.h | 14 +- {sonoff => tasmota}/language/zh-TW.h | 14 +- {sonoff => tasmota}/my_user_config.h | 202 +- {sonoff => tasmota}/sendemail.h | 0 {sonoff => tasmota}/sendemail.ino | 0 {sonoff => tasmota}/settings.h | 79 +- {sonoff => tasmota}/settings.ino | 249 +- {sonoff => tasmota}/support.ino | 86 +- {sonoff => tasmota}/support_button.ino | 8 +- {sonoff => tasmota}/support_command.ino | 222 +- {sonoff => tasmota}/support_features.ino | 25 +- {sonoff => tasmota}/support_float.ino | 5 +- {sonoff => tasmota}/support_rotary.ino | 2 +- {sonoff => tasmota}/support_rtc.ino | 7 +- {sonoff => tasmota}/support_static_buffer.ino | 2 +- {sonoff => tasmota}/support_switch.ino | 8 +- {sonoff => tasmota}/support_udp.ino | 6 +- {sonoff => tasmota}/support_wifi.ino | 68 +- sonoff/sonoff.h => tasmota/tasmota.h | 18 +- sonoff/sonoff.ino => tasmota/tasmota.ino | 186 +- .../sonoff_ca.ino => tasmota/tasmota_ca.ino | 2 +- .../sonoff_post.h => tasmota/tasmota_post.h | 45 +- .../tasmota_template.h | 35 +- .../tasmota_version.h | 10 +- .../user_config_override_sample.h | 3 +- {sonoff => tasmota}/xdrv_01_webserver.ino | 112 +- {sonoff => tasmota}/xdrv_02_mqtt.ino | 81 +- {sonoff => tasmota}/xdrv_03_energy.ino | 30 +- {sonoff => tasmota}/xdrv_04_light.ino | 228 +- tasmota/xdrv_05_irremote.ino | 311 + {sonoff => tasmota}/xdrv_05_irremote_full.ino | 6 +- {sonoff => tasmota}/xdrv_06_snfbridge.ino | 10 +- {sonoff => tasmota}/xdrv_07_domoticz.ino | 8 +- {sonoff => tasmota}/xdrv_08_serial_bridge.ino | 4 +- {sonoff => tasmota}/xdrv_09_timers.ino | 17 +- {sonoff => tasmota}/xdrv_10_rules.ino | 54 +- {sonoff => tasmota}/xdrv_10_scripter.ino | 13 +- {sonoff => tasmota}/xdrv_11_knx.ino | 4 +- .../xdrv_12_home_assistant.ino | 53 +- {sonoff => tasmota}/xdrv_13_display.ino | 8 +- {sonoff => tasmota}/xdrv_14_mp3.ino | 4 +- {sonoff => tasmota}/xdrv_15_pca9685.ino | 38 +- {sonoff => tasmota}/xdrv_16_tuyamcu.ino | 337 +- {sonoff => tasmota}/xdrv_17_rcswitch.ino | 4 +- .../xdrv_18_armtronix_dimmers.ino | 2 +- {sonoff => tasmota}/xdrv_19_ps16dz_dimmer.ino | 4 +- {sonoff => tasmota}/xdrv_20_hue.ino | 60 +- {sonoff => tasmota}/xdrv_21_wemo.ino | 2 +- {sonoff => tasmota}/xdrv_22_sonoff_ifan.ino | 6 +- .../xdrv_23_zigbee_0_constants.ino | 2 +- .../xdrv_23_zigbee_3_devices.ino | 22 +- .../xdrv_23_zigbee_5_converters.ino | 250 +- .../xdrv_23_zigbee_6_commands.ino | 43 +- .../xdrv_23_zigbee_7_statemachine.ino | 30 +- .../xdrv_23_zigbee_8_parsers.ino | 33 +- {sonoff => tasmota}/xdrv_23_zigbee_9_impl.ino | 223 +- {sonoff => tasmota}/xdrv_24_buzzer.ino | 4 +- {sonoff => tasmota}/xdrv_25_A4988_Stepper.ino | 2 +- {sonoff => tasmota}/xdrv_26_ariluxrf.ino | 2 +- {sonoff => tasmota}/xdrv_27_shutter.ino | 59 +- {sonoff => tasmota}/xdrv_28_pcf8574.ino | 41 +- tasmota/xdrv_29_deepsleep.ino | 208 + {sonoff => tasmota}/xdrv_30_exs_dimmer.ino | 6 +- tasmota/xdrv_31_tasmota_slave.ino | 598 ++ {sonoff => tasmota}/xdrv_99_debug.ino | 4 +- {sonoff => tasmota}/xdrv_interface.ino | 4 +- {sonoff => tasmota}/xdsp_01_lcd.ino | 65 +- {sonoff => tasmota}/xdsp_02_ssd1306.ino | 38 +- {sonoff => tasmota}/xdsp_03_matrix.ino | 50 +- {sonoff => tasmota}/xdsp_04_ili9341.ino | 2 +- {sonoff => tasmota}/xdsp_05_epaper_29.ino | 2 +- {sonoff => tasmota}/xdsp_06_epaper_42.ino | 2 +- {sonoff => tasmota}/xdsp_07_sh1106.ino | 34 +- {sonoff => tasmota}/xdsp_08_ILI9488.ino | 7 +- {sonoff => tasmota}/xdsp_09_SSD1351.ino | 2 +- {sonoff => tasmota}/xdsp_10_RA8876.ino | 7 +- {sonoff => tasmota}/xdsp_interface.ino | 4 +- {sonoff => tasmota}/xlgt_01_ws2812.ino | 8 +- {sonoff => tasmota}/xlgt_02_my92x1.ino | 2 +- {sonoff => tasmota}/xlgt_03_sm16716.ino | 2 +- {sonoff => tasmota}/xlgt_04_sm2135.ino | 4 +- {sonoff => tasmota}/xlgt_05_sonoff_l1.ino | 4 +- {sonoff => tasmota}/xlgt_interface.ino | 4 +- {sonoff => tasmota}/xnrg_01_hlw8012.ino | 2 +- {sonoff => tasmota}/xnrg_02_cse7766.ino | 2 +- {sonoff => tasmota}/xnrg_03_pzem004t.ino | 2 +- {sonoff => tasmota}/xnrg_04_mcp39f501.ino | 2 +- {sonoff => tasmota}/xnrg_05_pzem_ac.ino | 2 +- {sonoff => tasmota}/xnrg_06_pzem_dc.ino | 2 +- {sonoff => tasmota}/xnrg_07_ade7953.ino | 57 +- {sonoff => tasmota}/xnrg_08_sdm120.ino | 2 +- {sonoff => tasmota}/xnrg_09_dds2382.ino | 4 +- {sonoff => tasmota}/xnrg_10_sdm630.ino | 2 +- {sonoff => tasmota}/xnrg_11_ddsu666.ino | 4 +- {sonoff => tasmota}/xnrg_12_solaxX1.ino | 2 +- {sonoff => tasmota}/xnrg_interface.ino | 4 +- {sonoff => tasmota}/xsns_01_counter.ino | 2 +- {sonoff => tasmota}/xsns_02_analog.ino | 2 +- {sonoff => tasmota}/xsns_04_snfsc.ino | 2 +- {sonoff => tasmota}/xsns_05_ds18x20.ino | 32 +- {sonoff => tasmota}/xsns_06_dht.ino | 2 +- {sonoff => tasmota}/xsns_07_sht1x.ino | 25 +- {sonoff => tasmota}/xsns_08_htu21.ino | 32 +- {sonoff => tasmota}/xsns_09_bmp.ino | 36 +- {sonoff => tasmota}/xsns_10_bh1750.ino | 35 +- {sonoff => tasmota}/xsns_11_veml6070.ino | 81 +- {sonoff => tasmota}/xsns_12_ads1115.ino | 123 +- {sonoff => tasmota}/xsns_13_ina219.ino | 53 +- {sonoff => tasmota}/xsns_14_sht3x.ino | 68 +- {sonoff => tasmota}/xsns_15_mhz19.ino | 2 +- {sonoff => tasmota}/xsns_16_tsl2561.ino | 34 +- {sonoff => tasmota}/xsns_17_senseair.ino | 2 +- {sonoff => tasmota}/xsns_18_pms5003.ino | 4 +- {sonoff => tasmota}/xsns_19_mgs.ino | 39 +- {sonoff => tasmota}/xsns_20_novasds.ino | 4 +- {sonoff => tasmota}/xsns_21_sgp30.ino | 60 +- {sonoff => tasmota}/xsns_22_sr04.ino | 2 +- {sonoff => tasmota}/xsns_24_si1145.ino | 64 +- {sonoff => tasmota}/xsns_26_lm75ad.ino | 47 +- {sonoff => tasmota}/xsns_27_apds9960.ino | 87 +- {sonoff => tasmota}/xsns_28_tm1638.ino | 2 +- {sonoff => tasmota}/xsns_29_mcp230xx.ino | 94 +- {sonoff => tasmota}/xsns_30_mpr121.ino | 37 +- {sonoff => tasmota}/xsns_31_ccs811.ino | 61 +- {sonoff => tasmota}/xsns_32_mpu6050.ino | 101 +- {sonoff => tasmota}/xsns_33_ds3231.ino | 81 +- {sonoff => tasmota}/xsns_34_hx711.ino | 4 +- {sonoff => tasmota}/xsns_35_tx20.ino | 2 +- {sonoff => tasmota}/xsns_36_mgc3130.ino | 84 +- {sonoff => tasmota}/xsns_37_rfsensor.ino | 2 +- {sonoff => tasmota}/xsns_38_az7798.ino | 4 +- {sonoff => tasmota}/xsns_39_max31855.ino | 6 +- {sonoff => tasmota}/xsns_40_pn532.ino | 4 +- {sonoff => tasmota}/xsns_41_max44009.ino | 31 +- {sonoff => tasmota}/xsns_42_scd30.ino | 353 +- {sonoff => tasmota}/xsns_43_hre.ino | 0 {sonoff => tasmota}/xsns_44_sps30.ino | 57 +- {sonoff => tasmota}/xsns_45_vl53l0x.ino | 50 +- {sonoff => tasmota}/xsns_46_MLX90614.ino | 103 +- {sonoff => tasmota}/xsns_47_max31865.ino | 2 +- {sonoff => tasmota}/xsns_48_chirp.ino | 159 +- tasmota/xsns_50_paj7620.ino | 453 ++ {sonoff => tasmota}/xsns_51_rdm6300.ino | 4 +- {sonoff => tasmota}/xsns_52_ibeacon.ino | 4 +- {sonoff => tasmota}/xsns_53_sml.ino | 4 +- {sonoff => tasmota}/xsns_54_ina226.ino | 94 +- tasmota/xsns_55_hih_series.ino | 159 + tasmota/xsns_56_hpma.ino | 147 + tasmota/xsns_57_tsl2591.ino | 125 + {sonoff => tasmota}/xsns_interface.ino | 4 +- tasmota/xx2c_interface.ino | 438 ++ tools/Esptool/EraseFlash.bat | 1 + tools/Esptool/ReadFlash.bat | 1 + tools/Esptool/VerifyFlash.bat | 1 + tools/Esptool/WriteFlash.bat | 1 + tools/decode-config.html | 56 +- tools/decode-config.md | 147 +- tools/decode-config.py | 92 +- tools/decode-status.py | 28 +- tools/fw_server/fw-server.py | 10 +- tools/fw_server/fw/README | 6 +- 518 files changed, 26153 insertions(+), 21467 deletions(-) create mode 100644 Doxyfile create mode 100644 I2CDEVICES.md delete mode 100644 REFERENCE.md delete mode 100644 arduino/version 2.5.2/boards.txt rename arduino/{version 2.5.2 => version 2.6.0}/platform.txt (71%) create mode 100644 lib/Adafruit_TSL2591_Library/Adafruit_TSL2591.cpp create mode 100644 lib/Adafruit_TSL2591_Library/Adafruit_TSL2591.h delete mode 100644 lib/ArduinoHexParse/README.md delete mode 100644 lib/ArduinoHexParse/keywords.txt delete mode 100644 lib/ArduinoHexParse/library.json delete mode 100644 lib/ArduinoHexParse/library.properties delete mode 100644 lib/ArduinoHexParse/src/ArduinoHexParse.cpp delete mode 100644 lib/ArduinoHexParse/src/ArduinoHexParse.h create mode 100644 lib/HPMA115S0/LICENSE.md create mode 100644 lib/HPMA115S0/README.md create mode 100644 lib/HPMA115S0/example/example.ino create mode 100644 lib/HPMA115S0/library.properties create mode 100644 lib/HPMA115S0/src/hpma115S0.cpp create mode 100644 lib/HPMA115S0/src/hpma115S0.h delete mode 100644 lib/I2Cdevlib-ADS1115/.library.json delete mode 100644 lib/I2Cdevlib-ADS1115/ADS1115.cpp delete mode 100644 lib/I2Cdevlib-ADS1115/ADS1115.h delete mode 100644 lib/I2Cdevlib-ADS1115/examples/ADS1115_differential/ADS1115_differential.ino delete mode 100644 lib/I2Cdevlib-ADS1115/examples/ADS1115_single/ADS1115_single.ino delete mode 100644 lib/I2Cdevlib-ADS1115/library.json delete mode 100755 lib/IRremoteESP8266-2.6.5/.github/CONTRIBUTING.md delete mode 100755 lib/IRremoteESP8266-2.6.5/.github/Contributors.md delete mode 100755 lib/IRremoteESP8266-2.6.5/.github/issue_template.md delete mode 100755 lib/IRremoteESP8266-2.6.5/.gitignore delete mode 100755 lib/IRremoteESP8266-2.6.5/.gitmodules delete mode 100755 lib/IRremoteESP8266-2.6.5/.style.yapf delete mode 100644 lib/IRremoteESP8266-2.6.5/.travis.yml delete mode 100644 lib/IRremoteESP8266-2.6.5/examples/CommonAcControl/CommonAcControl.ino delete mode 100644 lib/IRremoteESP8266-2.6.5/examples/IRGCTCPServer/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.6.5/examples/IRServer/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.6.5/examples/IRrecvDemo/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.6.5/examples/IRrecvDump/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.6.5/examples/IRrecvDumpV2/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.6.5/examples/IRsendDemo/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.6.5/examples/IRsendProntoDemo/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.6.5/examples/JVCPanasonicSendDemo/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.6.5/examples/LGACSend/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.6.5/examples/SmartIRRepeater/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.6.5/examples/TurnOnArgoAC/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.6.5/examples/TurnOnDaikinAC/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.6.5/examples/TurnOnFujitsuAC/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.6.5/examples/TurnOnKelvinatorAC/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.6.5/examples/TurnOnMitsubishiAC/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.6.5/examples/TurnOnMitsubishiHeavyAc/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.6.5/examples/TurnOnPanasonicAC/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.6.5/examples/TurnOnToshibaAC/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.6.5/examples/TurnOnTrotecAC/platformio.ini delete mode 100755 lib/IRremoteESP8266-2.6.5/src/ir_Hitachi.cpp delete mode 100755 lib/IRremoteESP8266-2.6.5/src/ir_Mitsubishi.cpp delete mode 100755 lib/IRremoteESP8266-2.6.5/src/ir_Mitsubishi.h delete mode 100755 lib/IRremoteESP8266-2.6.5/tools/auto_analyse_raw_data.py delete mode 100755 lib/IRremoteESP8266-2.6.5/tools/auto_analyse_raw_data_test.py rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/CPPLINT.cfg (100%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/LICENSE.txt (100%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/README.md (90%) mode change 100755 => 100644 create mode 100644 lib/IRremoteESP8266-2.7.0/README_fr.md rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/ReleaseNotes.md (86%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/SupportedProtocols.md (89%) create mode 100644 lib/IRremoteESP8266-2.7.0/examples/CommonAcControl/CommonAcControl.ino rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/examples/CommonAcControl/platformio.ini (86%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/examples/ControlSamsungAC/ControlSamsungAC.ino (100%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/examples/ControlSamsungAC/platformio.ini (86%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/examples/DumbIRRepeater/DumbIRRepeater.ino (100%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/examples/DumbIRRepeater/platformio.ini (86%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/examples/IRGCSendDemo/IRGCSendDemo.ino (100%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/examples/IRGCSendDemo/platformio.ini (86%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/examples/IRGCTCPServer/IRGCTCPServer.ino (100%) mode change 100755 => 100644 create mode 100644 lib/IRremoteESP8266-2.7.0/examples/IRGCTCPServer/platformio.ini rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/examples/IRMQTTServer/IRMQTTServer.h (88%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/examples/IRMQTTServer/IRMQTTServer.ino (84%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/examples/IRMQTTServer/platformio.ini (82%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/examples/IRServer/IRServer.ino (100%) mode change 100755 => 100644 create mode 100644 lib/IRremoteESP8266-2.7.0/examples/IRServer/platformio.ini rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/examples/IRrecvDemo/IRrecvDemo.ino (100%) mode change 100755 => 100644 create mode 100644 lib/IRremoteESP8266-2.7.0/examples/IRrecvDemo/platformio.ini rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/examples/IRrecvDump/IRrecvDump.ino (100%) mode change 100755 => 100644 create mode 100644 lib/IRremoteESP8266-2.7.0/examples/IRrecvDump/platformio.ini rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/examples/IRrecvDumpV2/IRrecvDumpV2.ino (87%) mode change 100755 => 100644 create mode 100644 lib/IRremoteESP8266-2.7.0/examples/IRrecvDumpV2/platformio.ini rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/examples/IRsendDemo/IRsendDemo.ino (100%) mode change 100755 => 100644 create mode 100644 lib/IRremoteESP8266-2.7.0/examples/IRsendDemo/platformio.ini rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/examples/IRsendProntoDemo/IRsendProntoDemo.ino (96%) mode change 100755 => 100644 create mode 100644 lib/IRremoteESP8266-2.7.0/examples/IRsendProntoDemo/platformio.ini rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/examples/JVCPanasonicSendDemo/JVCPanasonicSendDemo.ino (100%) mode change 100755 => 100644 create mode 100644 lib/IRremoteESP8266-2.7.0/examples/JVCPanasonicSendDemo/platformio.ini rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/examples/LGACSend/LGACSend.ino (100%) mode change 100755 => 100644 create mode 100644 lib/IRremoteESP8266-2.7.0/examples/LGACSend/platformio.ini rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/examples/SmartIRRepeater/SmartIRRepeater.ino (100%) create mode 100644 lib/IRremoteESP8266-2.7.0/examples/SmartIRRepeater/platformio.ini rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/examples/TurnOnArgoAC/TurnOnArgoAC.ino (100%) mode change 100755 => 100644 create mode 100644 lib/IRremoteESP8266-2.7.0/examples/TurnOnArgoAC/platformio.ini rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/examples/TurnOnDaikinAC/TurnOnDaikinAC.ino (100%) mode change 100755 => 100644 create mode 100644 lib/IRremoteESP8266-2.7.0/examples/TurnOnDaikinAC/platformio.ini rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/examples/TurnOnFujitsuAC/TurnOnFujitsuAC.ino (100%) mode change 100755 => 100644 create mode 100644 lib/IRremoteESP8266-2.7.0/examples/TurnOnFujitsuAC/platformio.ini rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/examples/TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino (100%) mode change 100755 => 100644 create mode 100644 lib/IRremoteESP8266-2.7.0/examples/TurnOnKelvinatorAC/platformio.ini rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/examples/TurnOnMitsubishiAC/TurnOnMitsubishiAC.ino (100%) mode change 100755 => 100644 create mode 100644 lib/IRremoteESP8266-2.7.0/examples/TurnOnMitsubishiAC/platformio.ini rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/examples/TurnOnMitsubishiHeavyAc/TurnOnMitsubishiHeavyAc.ino (100%) create mode 100644 lib/IRremoteESP8266-2.7.0/examples/TurnOnMitsubishiHeavyAc/platformio.ini rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/examples/TurnOnPanasonicAC/TurnOnPanasonicAC.ino (100%) create mode 100644 lib/IRremoteESP8266-2.7.0/examples/TurnOnPanasonicAC/platformio.ini rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/examples/TurnOnToshibaAC/TurnOnToshibaAC.ino (100%) mode change 100755 => 100644 create mode 100644 lib/IRremoteESP8266-2.7.0/examples/TurnOnToshibaAC/platformio.ini rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/examples/TurnOnTrotecAC/TurnOnTrotecAC.ino (100%) mode change 100755 => 100644 create mode 100644 lib/IRremoteESP8266-2.7.0/examples/TurnOnTrotecAC/platformio.ini create mode 100644 lib/IRremoteESP8266-2.7.0/examples/Web-AC-control/README.md create mode 100644 lib/IRremoteESP8266-2.7.0/examples/Web-AC-control/Web-AC-control.ino create mode 100644 lib/IRremoteESP8266-2.7.0/examples/Web-AC-control/platformio.ini create mode 100644 lib/IRremoteESP8266-2.7.0/examples/Web-AC-control/printscreen.png create mode 100644 lib/IRremoteESP8266-2.7.0/examples/Web-AC-control/upload/favicon.ico create mode 100644 lib/IRremoteESP8266-2.7.0/examples/Web-AC-control/upload/level_1_off.svg create mode 100644 lib/IRremoteESP8266-2.7.0/examples/Web-AC-control/upload/level_1_on.svg create mode 100644 lib/IRremoteESP8266-2.7.0/examples/Web-AC-control/upload/level_2_off.svg create mode 100644 lib/IRremoteESP8266-2.7.0/examples/Web-AC-control/upload/level_2_on.svg create mode 100644 lib/IRremoteESP8266-2.7.0/examples/Web-AC-control/upload/level_3_off.svg create mode 100644 lib/IRremoteESP8266-2.7.0/examples/Web-AC-control/upload/level_3_on.svg create mode 100644 lib/IRremoteESP8266-2.7.0/examples/Web-AC-control/upload/level_4_off.svg create mode 100644 lib/IRremoteESP8266-2.7.0/examples/Web-AC-control/upload/level_4_on.svg create mode 100644 lib/IRremoteESP8266-2.7.0/examples/Web-AC-control/upload/ui.html create mode 100644 lib/IRremoteESP8266-2.7.0/examples/Web-AC-control/upload/ui.js rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/keywords.txt (80%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/library.json (97%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/library.properties (97%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/pylintrc (100%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/CPPLINT.cfg (100%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/IRac.cpp (75%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/IRac.h (84%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/IRrecv.cpp (98%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/IRrecv.h (96%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/IRremoteESP8266.h (86%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/IRsend.cpp (98%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/IRsend.h (92%) create mode 100644 lib/IRremoteESP8266-2.7.0/src/IRtext.cpp create mode 100644 lib/IRremoteESP8266-2.7.0/src/IRtext.h rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/IRtimer.cpp (100%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/IRtimer.h (100%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/IRutils.cpp (70%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/IRutils.h (57%) create mode 100644 lib/IRremoteESP8266-2.7.0/src/i18n.h rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_Aiwa.cpp (100%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_Amcor.cpp (82%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_Amcor.h (83%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_Argo.cpp (76%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_Argo.h (70%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_Carrier.cpp (100%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_Coolix.cpp (73%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_Coolix.h (68%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_Daikin.cpp (70%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_Daikin.h (76%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_Denon.cpp (100%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_Dish.cpp (100%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_Electra.cpp (79%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_Electra.h (78%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_Fujitsu.cpp (76%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_Fujitsu.h (88%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_GICable.cpp (100%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_GlobalCache.cpp (100%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_Goodweather.cpp (72%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_Goodweather.h (81%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_Gree.cpp (71%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_Gree.h (72%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_Haier.cpp (67%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_Haier.h (80%) mode change 100755 => 100644 create mode 100644 lib/IRremoteESP8266-2.7.0/src/ir_Hitachi.cpp rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_Hitachi.h (51%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_Inax.cpp (100%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_JVC.cpp (100%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_Kelvinator.cpp (77%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_Kelvinator.h (100%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_LG.cpp (100%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_LG.h (100%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_Lasertag.cpp (100%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_Lego.cpp (100%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_Lutron.cpp (100%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_MWM.cpp (100%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_Magiquest.cpp (100%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_Magiquest.h (100%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_Midea.cpp (82%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_Midea.h (84%) mode change 100755 => 100644 create mode 100644 lib/IRremoteESP8266-2.7.0/src/ir_Mitsubishi.cpp create mode 100644 lib/IRremoteESP8266-2.7.0/src/ir_Mitsubishi.h rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_MitsubishiHeavy.cpp (63%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_MitsubishiHeavy.h (77%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_NEC.cpp (100%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_NEC.h (100%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_Neoclima.cpp (69%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_Neoclima.h (83%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_Nikai.cpp (100%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_Panasonic.cpp (72%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_Panasonic.h (70%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_Pioneer.cpp (100%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_Pronto.cpp (100%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_RC5_RC6.cpp (100%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_RCMM.cpp (100%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_Samsung.cpp (80%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_Samsung.h (77%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_Sanyo.cpp (100%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_Sharp.cpp (85%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_Sharp.h (87%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_Sherwood.cpp (100%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_Sony.cpp (100%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_Tcl.cpp (59%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_Tcl.h (73%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_Teco.cpp (60%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_Teco.h (70%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_Toshiba.cpp (80%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_Toshiba.h (84%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_Trotec.cpp (78%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_Trotec.h (87%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_Vestel.cpp (64%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_Vestel.h (89%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_Whirlpool.cpp (76%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_Whirlpool.h (87%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/src/ir_Whynter.cpp (100%) mode change 100755 => 100644 create mode 100644 lib/IRremoteESP8266-2.7.0/src/locale/README.md create mode 100644 lib/IRremoteESP8266-2.7.0/src/locale/de-CH.h create mode 100644 lib/IRremoteESP8266-2.7.0/src/locale/de-DE.h create mode 100644 lib/IRremoteESP8266-2.7.0/src/locale/defaults.h create mode 100644 lib/IRremoteESP8266-2.7.0/src/locale/en-AU.h create mode 100644 lib/IRremoteESP8266-2.7.0/src/locale/en-IE.h create mode 100644 lib/IRremoteESP8266-2.7.0/src/locale/en-UK.h create mode 100644 lib/IRremoteESP8266-2.7.0/src/locale/en-US.h create mode 100644 lib/IRremoteESP8266-2.7.0/src/locale/es-ES.h create mode 100644 lib/IRremoteESP8266-2.7.0/src/locale/fr-FR.h rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/test/IRac_test.cpp (83%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/test/IRrecv_test.cpp (100%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/test/IRrecv_test.h (100%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/test/IRsend_test.cpp (100%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/test/IRsend_test.h (100%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/test/IRutils_test.cpp (77%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/test/Makefile (96%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/test/ir_Aiwa_test.cpp (100%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/test/ir_Amcor_test.cpp (98%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/test/ir_Argo_test.cpp (95%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/test/ir_Carrier_test.cpp (100%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/test/ir_Coolix_test.cpp (96%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/test/ir_Daikin_test.cpp (85%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/test/ir_Denon_test.cpp (100%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/test/ir_Dish_test.cpp (100%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/test/ir_Electra_test.cpp (97%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/test/ir_Fujitsu_test.cpp (81%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/test/ir_GICable_test.cpp (100%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/test/ir_GlobalCache_test.cpp (100%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/test/ir_Goodweather_test.cpp (93%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/test/ir_Gree_test.cpp (97%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/test/ir_Haier_test.cpp (92%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/test/ir_Hitachi_test.cpp (74%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/test/ir_Inax_test.cpp (100%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/test/ir_JVC_test.cpp (100%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/test/ir_Kelvinator_test.cpp (98%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/test/ir_LG_test.cpp (100%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/test/ir_Lasertag_test.cpp (100%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/test/ir_Lego_test.cpp (100%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/test/ir_Lutron_test.cpp (100%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/test/ir_MWM_test.cpp (100%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/test/ir_Magiquest_test.cpp (100%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/test/ir_Midea_test.cpp (94%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/test/ir_MitsubishiHeavy_test.cpp (91%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/test/ir_Mitsubishi_test.cpp (72%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/test/ir_NEC_test.cpp (100%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/test/ir_Neoclima_test.cpp (99%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/test/ir_Nikai_test.cpp (100%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/test/ir_Panasonic_test.cpp (94%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/test/ir_Pioneer_test.cpp (100%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/test/ir_Pronto_test.cpp (100%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/test/ir_RC5_RC6_test.cpp (100%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/test/ir_RCMM_test.cpp (100%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/test/ir_Samsung_test.cpp (96%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/test/ir_Sanyo_test.cpp (100%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/test/ir_Sharp_test.cpp (96%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/test/ir_Sherwood_test.cpp (100%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/test/ir_Sony_test.cpp (100%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/test/ir_Tcl_test.cpp (89%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/test/ir_Teco_test.cpp (82%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/test/ir_Toshiba_test.cpp (99%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/test/ir_Trotec_test.cpp (97%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/test/ir_Vestel_test.cpp (93%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/test/ir_Whirlpool_test.cpp (97%) rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/test/ir_Whynter_test.cpp (100%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/tools/Makefile (94%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/tools/RawToGlobalCache.sh (100%) mode change 100755 => 100644 create mode 100644 lib/IRremoteESP8266-2.7.0/tools/auto_analyse_raw_data.py create mode 100644 lib/IRremoteESP8266-2.7.0/tools/auto_analyse_raw_data_test.py rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/tools/gc_decode.cpp (65%) mode change 100755 => 100644 create mode 100644 lib/IRremoteESP8266-2.7.0/tools/generate_irtext_h.sh rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/tools/mkkeywords (100%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/tools/mode2_decode.cpp (100%) mode change 100755 => 100644 rename lib/{IRremoteESP8266-2.6.5 => IRremoteESP8266-2.7.0}/tools/scrape_supported_devices.py (100%) create mode 100644 lib/NeoPixelBus-2.5.0.09/.github/ISSUE_TEMPLATE/bug_report.md create mode 100644 lib/NeoPixelBus-2.5.0.09/.github/ISSUE_TEMPLATE/feature_request.md create mode 100644 pio/rename-firmware.py delete mode 100644 sonoff/xdrv_05_irremote.ino delete mode 100644 sonoff/xdrv_29_deepsleep.ino delete mode 100644 sonoff/xdrv_31_arduino_slave.ino delete mode 100644 sonoff/xsns_12_ads1115_i2cdev.ino delete mode 100644 sonoff/xsns_50_paj7620.ino rename {sonoff => tasmota}/Parsing.cpp (100%) rename {sonoff => tasmota}/StackThunk_light.cpp (100%) rename {sonoff => tasmota}/StackThunk_light.h (100%) rename {sonoff => tasmota}/WiFiClientSecureLightBearSSL.cpp (100%) rename {sonoff => tasmota}/WiFiClientSecureLightBearSSL.h (100%) rename {sonoff => tasmota}/_changelog.ino (97%) rename {sonoff => tasmota}/core_esp8266_timer.c (100%) rename {sonoff => tasmota}/core_esp8266_wiring_digital.c (100%) rename {sonoff => tasmota}/core_esp8266_wiring_pwm.c (100%) rename {sonoff => tasmota}/i18n.h (98%) rename {sonoff => tasmota}/language/bg-BG.h (98%) rename {sonoff => tasmota}/language/cs-CZ.h (98%) rename {sonoff => tasmota}/language/de-DE.h (98%) rename {sonoff => tasmota}/language/el-GR.h (98%) rename {sonoff => tasmota}/language/en-GB.h (98%) rename {sonoff => tasmota}/language/es-ES.h (98%) rename {sonoff => tasmota}/language/fr-FR.h (98%) rename {sonoff => tasmota}/language/he-HE.h (98%) rename {sonoff => tasmota}/language/hu-HU.h (98%) rename {sonoff => tasmota}/language/it-IT.h (98%) rename {sonoff => tasmota}/language/ko-KO.h (91%) rename {sonoff => tasmota}/language/nl-NL.h (98%) rename {sonoff => tasmota}/language/pl-PL.h (98%) rename {sonoff => tasmota}/language/pt-BR.h (98%) rename {sonoff => tasmota}/language/pt-PT.h (97%) rename {sonoff => tasmota}/language/ru-RU.h (98%) rename {sonoff => tasmota}/language/sk-SK.h (98%) rename {sonoff => tasmota}/language/sv-SE.h (98%) rename {sonoff => tasmota}/language/tr-TR.h (98%) mode change 100755 => 100644 rename {sonoff => tasmota}/language/uk-UK.h (98%) rename {sonoff => tasmota}/language/zh-CN.h (98%) rename {sonoff => tasmota}/language/zh-TW.h (98%) rename {sonoff => tasmota}/my_user_config.h (77%) rename {sonoff => tasmota}/sendemail.h (100%) rename {sonoff => tasmota}/sendemail.ino (100%) rename {sonoff => tasmota}/settings.h (90%) rename {sonoff => tasmota}/settings.ino (88%) rename {sonoff => tasmota}/support.ino (93%) rename {sonoff => tasmota}/support_button.ino (98%) rename {sonoff => tasmota}/support_command.ino (89%) rename {sonoff => tasmota}/support_features.ino (96%) rename {sonoff => tasmota}/support_float.ino (99%) rename {sonoff => tasmota}/support_rotary.ino (98%) rename {sonoff => tasmota}/support_rtc.ino (99%) rename {sonoff => tasmota}/support_static_buffer.ino (98%) rename {sonoff => tasmota}/support_switch.ino (97%) rename {sonoff => tasmota}/support_udp.ino (97%) rename {sonoff => tasmota}/support_wifi.ino (91%) rename sonoff/sonoff.h => tasmota/tasmota.h (96%) rename sonoff/sonoff.ino => tasmota/tasmota.ino (90%) mode change 100755 => 100644 rename sonoff/sonoff_ca.ino => tasmota/tasmota_ca.ino (98%) rename sonoff/sonoff_post.h => tasmota/tasmota_post.h (96%) rename sonoff/sonoff_template.h => tasmota/tasmota_template.h (98%) rename sonoff/sonoff_version.h => tasmota/tasmota_version.h (78%) rename {sonoff => tasmota}/user_config_override_sample.h (96%) rename {sonoff => tasmota}/xdrv_01_webserver.ino (95%) rename {sonoff => tasmota}/xdrv_02_mqtt.ino (93%) rename {sonoff => tasmota}/xdrv_03_energy.ino (98%) rename {sonoff => tasmota}/xdrv_04_light.ino (91%) create mode 100644 tasmota/xdrv_05_irremote.ino rename {sonoff => tasmota}/xdrv_05_irremote_full.ino (99%) rename {sonoff => tasmota}/xdrv_06_snfbridge.ino (98%) rename {sonoff => tasmota}/xdrv_07_domoticz.ino (98%) rename {sonoff => tasmota}/xdrv_08_serial_bridge.ino (98%) rename {sonoff => tasmota}/xdrv_09_timers.ino (97%) rename {sonoff => tasmota}/xdrv_10_rules.ino (97%) rename {sonoff => tasmota}/xdrv_10_scripter.ino (99%) rename {sonoff => tasmota}/xdrv_11_knx.ino (99%) rename {sonoff => tasmota}/xdrv_12_home_assistant.ino (91%) rename {sonoff => tasmota}/xdrv_13_display.ino (99%) mode change 100755 => 100644 rename {sonoff => tasmota}/xdrv_14_mp3.ino (99%) rename {sonoff => tasmota}/xdrv_15_pca9685.ino (88%) rename {sonoff => tasmota}/xdrv_16_tuyamcu.ino (64%) rename {sonoff => tasmota}/xdrv_17_rcswitch.ino (98%) rename {sonoff => tasmota}/xdrv_18_armtronix_dimmers.ino (99%) rename {sonoff => tasmota}/xdrv_19_ps16dz_dimmer.ino (97%) rename {sonoff => tasmota}/xdrv_20_hue.ino (93%) rename {sonoff => tasmota}/xdrv_21_wemo.ino (99%) rename {sonoff => tasmota}/xdrv_22_sonoff_ifan.ino (98%) rename {sonoff => tasmota}/xdrv_23_zigbee_0_constants.ino (99%) rename {sonoff => tasmota}/xdrv_23_zigbee_3_devices.ino (96%) rename {sonoff => tasmota}/xdrv_23_zigbee_5_converters.ino (88%) rename {sonoff => tasmota}/xdrv_23_zigbee_6_commands.ino (63%) rename {sonoff => tasmota}/xdrv_23_zigbee_7_statemachine.ino (96%) rename {sonoff => tasmota}/xdrv_23_zigbee_8_parsers.ino (96%) rename {sonoff => tasmota}/xdrv_23_zigbee_9_impl.ino (69%) rename {sonoff => tasmota}/xdrv_24_buzzer.ino (97%) rename {sonoff => tasmota}/xdrv_25_A4988_Stepper.ino (99%) rename {sonoff => tasmota}/xdrv_26_ariluxrf.ino (99%) rename {sonoff => tasmota}/xdrv_27_shutter.ino (92%) rename {sonoff => tasmota}/xdrv_28_pcf8574.ino (89%) create mode 100644 tasmota/xdrv_29_deepsleep.ino rename {sonoff => tasmota}/xdrv_30_exs_dimmer.ino (98%) create mode 100644 tasmota/xdrv_31_tasmota_slave.ino rename {sonoff => tasmota}/xdrv_99_debug.ino (99%) rename {sonoff => tasmota}/xdrv_interface.ino (98%) rename {sonoff => tasmota}/xdsp_01_lcd.ino (85%) rename {sonoff => tasmota}/xdsp_02_ssd1306.ino (87%) mode change 100755 => 100644 rename {sonoff => tasmota}/xdsp_03_matrix.ino (91%) rename {sonoff => tasmota}/xdsp_04_ili9341.ino (99%) rename {sonoff => tasmota}/xdsp_05_epaper_29.ino (98%) rename {sonoff => tasmota}/xdsp_06_epaper_42.ino (98%) rename {sonoff => tasmota}/xdsp_07_sh1106.ino (89%) rename {sonoff => tasmota}/xdsp_08_ILI9488.ino (97%) rename {sonoff => tasmota}/xdsp_09_SSD1351.ino (98%) rename {sonoff => tasmota}/xdsp_10_RA8876.ino (98%) rename {sonoff => tasmota}/xdsp_interface.ino (95%) rename {sonoff => tasmota}/xlgt_01_ws2812.ino (98%) rename {sonoff => tasmota}/xlgt_02_my92x1.ino (99%) rename {sonoff => tasmota}/xlgt_03_sm16716.ino (98%) rename {sonoff => tasmota}/xlgt_04_sm2135.ino (95%) rename {sonoff => tasmota}/xlgt_05_sonoff_l1.ino (98%) rename {sonoff => tasmota}/xlgt_interface.ino (94%) rename {sonoff => tasmota}/xnrg_01_hlw8012.ino (99%) rename {sonoff => tasmota}/xnrg_02_cse7766.ino (99%) rename {sonoff => tasmota}/xnrg_03_pzem004t.ino (99%) rename {sonoff => tasmota}/xnrg_04_mcp39f501.ino (99%) rename {sonoff => tasmota}/xnrg_05_pzem_ac.ino (99%) rename {sonoff => tasmota}/xnrg_06_pzem_dc.ino (99%) rename {sonoff => tasmota}/xnrg_07_ade7953.ino (83%) rename {sonoff => tasmota}/xnrg_08_sdm120.ino (99%) rename {sonoff => tasmota}/xnrg_09_dds2382.ino (97%) rename {sonoff => tasmota}/xnrg_10_sdm630.ino (99%) rename {sonoff => tasmota}/xnrg_11_ddsu666.ino (99%) rename {sonoff => tasmota}/xnrg_12_solaxX1.ino (99%) rename {sonoff => tasmota}/xnrg_interface.ino (94%) rename {sonoff => tasmota}/xsns_01_counter.ino (99%) rename {sonoff => tasmota}/xsns_02_analog.ino (99%) rename {sonoff => tasmota}/xsns_04_snfsc.ino (98%) rename {sonoff => tasmota}/xsns_05_ds18x20.ino (94%) rename {sonoff => tasmota}/xsns_06_dht.ino (99%) rename {sonoff => tasmota}/xsns_07_sht1x.ino (92%) rename {sonoff => tasmota}/xsns_08_htu21.ino (95%) rename {sonoff => tasmota}/xsns_09_bmp.ino (98%) mode change 100755 => 100644 rename {sonoff => tasmota}/xsns_10_bh1750.ino (86%) rename {sonoff => tasmota}/xsns_11_veml6070.ino (85%) rename {sonoff => tasmota}/xsns_12_ads1115.ino (78%) rename {sonoff => tasmota}/xsns_13_ina219.ino (94%) rename {sonoff => tasmota}/xsns_14_sht3x.ino (74%) mode change 100755 => 100644 rename {sonoff => tasmota}/xsns_15_mhz19.ino (99%) rename {sonoff => tasmota}/xsns_16_tsl2561.ino (84%) rename {sonoff => tasmota}/xsns_17_senseair.ino (98%) rename {sonoff => tasmota}/xsns_18_pms5003.ino (99%) rename {sonoff => tasmota}/xsns_19_mgs.ino (87%) rename {sonoff => tasmota}/xsns_20_novasds.ino (99%) rename {sonoff => tasmota}/xsns_21_sgp30.ino (80%) rename {sonoff => tasmota}/xsns_22_sr04.ino (97%) rename {sonoff => tasmota}/xsns_24_si1145.ino (90%) rename {sonoff => tasmota}/xsns_26_lm75ad.ino (78%) rename {sonoff => tasmota}/xsns_27_apds9960.ino (97%) rename {sonoff => tasmota}/xsns_28_tm1638.ino (99%) rename {sonoff => tasmota}/xsns_29_mcp230xx.ino (93%) rename {sonoff => tasmota}/xsns_30_mpr121.ino (94%) rename {sonoff => tasmota}/xsns_31_ccs811.ino (76%) rename {sonoff => tasmota}/xsns_32_mpu6050.ino (75%) rename {sonoff => tasmota}/xsns_33_ds3231.ino (70%) rename {sonoff => tasmota}/xsns_34_hx711.ino (99%) rename {sonoff => tasmota}/xsns_35_tx20.ino (99%) rename {sonoff => tasmota}/xsns_36_mgc3130.ino (92%) rename {sonoff => tasmota}/xsns_37_rfsensor.ino (99%) rename {sonoff => tasmota}/xsns_38_az7798.ino (99%) rename {sonoff => tasmota}/xsns_39_max31855.ino (95%) rename {sonoff => tasmota}/xsns_40_pn532.ino (99%) rename {sonoff => tasmota}/xsns_41_max44009.ino (92%) rename {sonoff => tasmota}/xsns_42_scd30.ino (52%) rename {sonoff => tasmota}/xsns_43_hre.ino (100%) rename {sonoff => tasmota}/xsns_44_sps30.ino (93%) rename {sonoff => tasmota}/xsns_45_vl53l0x.ino (84%) rename {sonoff => tasmota}/xsns_46_MLX90614.ino (64%) rename {sonoff => tasmota}/xsns_47_max31865.ino (99%) rename {sonoff => tasmota}/xsns_48_chirp.ino (87%) create mode 100644 tasmota/xsns_50_paj7620.ino rename {sonoff => tasmota}/xsns_51_rdm6300.ino (96%) rename {sonoff => tasmota}/xsns_52_ibeacon.ino (99%) rename {sonoff => tasmota}/xsns_53_sml.ino (99%) rename {sonoff => tasmota}/xsns_54_ina226.ino (91%) create mode 100644 tasmota/xsns_55_hih_series.ino create mode 100644 tasmota/xsns_56_hpma.ino create mode 100644 tasmota/xsns_57_tsl2591.ino rename {sonoff => tasmota}/xsns_interface.ino (99%) create mode 100644 tasmota/xx2c_interface.ino create mode 100644 tools/Esptool/EraseFlash.bat create mode 100644 tools/Esptool/ReadFlash.bat create mode 100644 tools/Esptool/VerifyFlash.bat create mode 100644 tools/Esptool/WriteFlash.bat diff --git a/.github/ISSUE_TEMPLATE/Bug_report.md b/.github/ISSUE_TEMPLATE/Bug_report.md index 0d3d5f7f5..373d518cb 100644 --- a/.github/ISSUE_TEMPLATE/Bug_report.md +++ b/.github/ISSUE_TEMPLATE/Bug_report.md @@ -6,14 +6,14 @@ about: Create a report to help us improve > **GUIDE** > > This BUG issue template is meant to REPORT Tasmota software BUGS ONLY> -> +> > Please DO NOT OPEN AN ISSUE: -> - If your Tasmota version is not the latest from the development branch, please update your device before submitting your issue. Your problem might already be solved. The latest precompiled binaries of Tasmota can be downloaded from http://thehackbox.org/tasmota/ -> - If you have a issue when flashing was done via Tuya Convert -> - If your issue is a flashing issue, please address it to the [Tasmota Support Chat](https://discord.gg/Ks2Kzd4) -> - If your issue is compilation problem, please address it to the [Tasmota Support Chat](https://discord.gg/Ks2Kzd4) -> - If your issue has been addressed before (i.e., duplicated issue), please ask in the original issue -> - If your issue is a Wi-Fi problem or MQTT problem, please try the steps provided in the [FAQ](https://github.com/arendst/Sonoff-Tasmota/wiki/FAQ) and troubleshooting wiki articles +> - If your Tasmota version is not the latest from the development branch, please update your device before submitting your issue. Your problem might already be solved. The latest precompiled binaries of Tasmota can be downloaded from http://thehackbox.org/tasmota/ +> - If you have a issue when flashing was done via Tuya Convert +> - If your issue is a flashing issue, please address it to the [Tasmota Support Chat](https://discord.gg/Ks2Kzd4) +> - If your issue is compilation problem, please address it to the [Tasmota Support Chat](https://discord.gg/Ks2Kzd4) +> - If your issue has been addressed before (i.e., duplicated issue), please ask in the original issue +> - If your issue is a Wi-Fi problem or MQTT problem, please try the steps provided in the [FAQ](https://github.com/arendst/Tasmota/wiki/FAQ) and troubleshooting wiki articles > > Please take a few minutes to complete the requested information below. Our ability to provide assistance is greatly hampered without it. The details requested potentially affect which options to pursue. The small amount of time you spend completing the template will also help the volunteers providing the assistance to you to reduce the time required to help you. @@ -26,9 +26,9 @@ _Make sure your have performed every step and checked the applicable boxes befor **FAILURE TO COMPLETE THE REQUESTED INFORMATION WILL RESULT IN YOUR ISSUE BEING CLOSED** -- [ ] Read the [Contributing Guide and Policy](https://github.com/arendst/Sonoff-Tasmota/blob/development/CONTRIBUTING.md) and [the Code of Conduct](https://github.com/arendst/Sonoff-Tasmota/blob/development/CODE_OF_CONDUCT.md) -- [ ] Searched the problem in [issues](https://github.com/arendst/Sonoff-Tasmota/issues) -- [ ] Searched the problem in the [wiki](https://github.com/arendst/Sonoff-Tasmota/wiki/Troubleshooting) +- [ ] Read the [Contributing Guide and Policy](https://github.com/arendst/Tasmota/blob/development/CONTRIBUTING.md) and [the Code of Conduct](https://github.com/arendst/Tasmota/blob/development/CODE_OF_CONDUCT.md) +- [ ] Searched the problem in [issues](https://github.com/arendst/Tasmota/issues) +- [ ] Searched the problem in the [wiki](https://github.com/arendst/Tasmota/wiki/Troubleshooting) - [ ] Searched the problem in the [forum](https://groups.google.com/d/forum/sonoffusers) - [ ] Searched the problem in the [chat](https://discord.gg/Ks2Kzd4) - [ ] Device used (e.g., Sonoff Basic): _____ @@ -40,27 +40,27 @@ _Make sure your have performed every step and checked the applicable boxes befor - [ ] Provide the output of command: ``Backlog Template; Module; GPIO``: ``` Configuration output here: - - + + ``` - [ ] If using rules, provide the output of this command: ``Backlog Rule1; Rule2; Rule3``: ``` Rules output here: - - + + ``` - [ ] Provide the output of this command: ``Status 0``: ``` STATUS 0 output here: - - + + ``` - [ ] Provide the output of the Console log output when you experience your issue; if applicable: _(Please use_ ``weblog 4`` _for more debug information)_ ``` Console output here: - - + + ``` ### TO REPRODUCE diff --git a/.github/ISSUE_TEMPLATE/Custom.md b/.github/ISSUE_TEMPLATE/Custom.md index f0d6fafe0..3b30c5f18 100644 --- a/.github/ISSUE_TEMPLATE/Custom.md +++ b/.github/ISSUE_TEMPLATE/Custom.md @@ -3,33 +3,33 @@ name: Troubleshooting about: Users Troubleshooting Help --- -> **GUIDE** +> **GUIDE** > > This troubleshooting issue template is meant to help Tasmota users with difficult problems. It is aimed to be opened if using the wiki and the support chat could not solve the issue. The Github Issue tracker is NOT a general discussion forum! -> -> Please DO NOT OPEN AN ISSUE: +> +> Please DO NOT OPEN AN ISSUE: > - If you have general questions or you need help on Tasmota usage, go to the [Tasmota Support Chat](https://discord.gg/Ks2Kzd4) -> - If your Tasmota version is not the latest from the development branch, please update your device before submitting your issue. Your problem might already be solved. The latest precompiled binaries of Tasmota can be downloaded from http://thehackbox.org/tasmota/ +> - If your Tasmota version is not the latest from the development branch, please update your device before submitting your issue. Your problem might already be solved. The latest precompiled binaries of Tasmota can be downloaded from http://thehackbox.org/tasmota/ > - If your issue is about a new device, please use the Tasmota [Template](../wiki/Templates) feature. > - If your issue is a flashing issue, please address it to the [Tasmota Support Chat](https://discord.gg/Ks2Kzd4) > - If your issue is compilation problem, please address it to the [Tasmota Support Chat](https://discord.gg/Ks2Kzd4) > - If your issue has been addressed before (i.e., duplicated issue), please ask in the original issue -> - If your issue is a Wi-Fi problem or MQTT problem, please try the steps provided in the [FAQ](https://github.com/arendst/Sonoff-Tasmota/wiki/FAQ) and troubleshooting wiki articles +> - If your issue is a Wi-Fi problem or MQTT problem, please try the steps provided in the [FAQ](https://github.com/arendst/Tasmota/wiki/FAQ) and troubleshooting wiki articles > > Please take a few minutes to complete the requested information below. Our ability to provide assistance is greatly hampered without it. The details requested potentially affect which options to pursue. The small amount of time you spend completing the template will also help the volunteers providing the assistance to you to reduce the time required to help you. ### ISSUE DESCRIPTION - TROUBLESHOOTING _A clear description of what the issue is and be as extensive as possible_ - + ### REQUESTED INFORMATION _Make sure these boxes are checked before submitting your issue. Thank you_ **FAILURE TO COMPLETE THE REQUESTED INFORMATION WILL RESULT IN YOUR ISSUE BEING CLOSED** -- [ ] Read the [Contributing Guide and Policy](https://github.com/arendst/Sonoff-Tasmota/blob/development/CONTRIBUTING.md) and [the Code of Conduct](https://github.com/arendst/Sonoff-Tasmota/blob/development/CODE_OF_CONDUCT.md) -- [ ] Searched the problem in issues (https://github.com/arendst/Sonoff-Tasmota/issues) -- [ ] Searched the problem in the wiki (https://github.com/arendst/Sonoff-Tasmota/wiki/Troubleshooting) +- [ ] Read the [Contributing Guide and Policy](https://github.com/arendst/Tasmota/blob/development/CONTRIBUTING.md) and [the Code of Conduct](https://github.com/arendst/Tasmota/blob/development/CODE_OF_CONDUCT.md) +- [ ] Searched the problem in issues (https://github.com/arendst/Tasmota/issues) +- [ ] Searched the problem in the wiki (https://github.com/arendst/Tasmota/wiki/Troubleshooting) - [ ] Searched the problem in the forum (https://groups.google.com/d/forum/sonoffusers) - [ ] Searched the problem in the chat (https://discord.gg/Ks2Kzd4) - [ ] Device used (e.g., Sonoff Basic): _____ @@ -41,27 +41,27 @@ _Make sure these boxes are checked before submitting your issue. Thank you_ - [ ] Provide the output of this command: ``Backlog Template; Module; GPIO``: ``` Configuration output here: - - + + ``` - [ ] If using rules, provide the output of this command: ``Backlog Rule1; Rule2; Rule3``: ``` Rules output here: - - + + ``` - [ ] Provide the output of this command: ``Status 0``: ``` STATUS 0 output here: - - + + ``` - [ ] Provide the output of the Console log output when you experience your issue; if applicable: _(Please use_ ``weblog 4`` _for more debug information)_ ``` Console output here: - - + + ``` **(Please, remember to close the issue when the problem has been addressed)** diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 53b64f906..3b6f2d086 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,11 +1,11 @@ ## Description: -**Related issue (if applicable):** fixes # +**Related issue (if applicable):** fixes # ## Checklist: - [ ] The pull request is done against the latest dev branch - [ ] Only relevant files were touched - [ ] Only one feature/fix was added per PR. - - [ ] The code change is tested and works on core pre-2.6 + - [ ] The code change is tested and works on core 2.6 - [ ] The code change pass travis tests. **Your PR cannot be merged unless tests pass** - - [ ] I accept the [CLA](https://github.com/arendst/Sonoff-Tasmota/blob/development/CONTRIBUTING.md#contributor-license-agreement-cla). + - [ ] I accept the [CLA](https://github.com/arendst/Tasmota/blob/development/CONTRIBUTING.md#contributor-license-agreement-cla). diff --git a/.gitignore b/.gitignore index acdd5c610..ebf64d03f 100644 --- a/.gitignore +++ b/.gitignore @@ -8,7 +8,7 @@ .clang_complete .gcc-flags.json .cache -sonoff/user_config_override.h +tasmota/user_config_override.h build firmware.map firmware.asm diff --git a/.gitpod.yml b/.gitpod.yml index 38e7d7bfa..ee7750c54 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -1,3 +1,3 @@ tasks: - before: pip install -U platformio - command: platformio run -e sonoff + command: platformio run -e tasmota diff --git a/.travis.yml b/.travis.yml index 3f4121386..ee82b6f06 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,34 +13,34 @@ cache: - $HOME/.platformio env: - - ENV=sonoff - - ENV=sonoff-minimal - - ENV=sonoff-basic - - ENV=sonoff-knx - - ENV=sonoff-sensors - - ENV=sonoff-display - - ENV=sonoff-ir - - ENV=sonoff-BG - - ENV=sonoff-BR - - ENV=sonoff-CN - - ENV=sonoff-CZ - - ENV=sonoff-DE - - ENV=sonoff-ES - - ENV=sonoff-FR - - ENV=sonoff-GR - - ENV=sonoff-HE - - ENV=sonoff-HU - - ENV=sonoff-IT - - ENV=sonoff-KO - - ENV=sonoff-NL - - ENV=sonoff-PL - - ENV=sonoff-PT - - ENV=sonoff-RU - - ENV=sonoff-SE - - ENV=sonoff-SK - - ENV=sonoff-TR - - ENV=sonoff-TW - - ENV=sonoff-UK + - ENV=tasmota + - ENV=tasmota-minimal + - ENV=tasmota-basic + - ENV=tasmota-knx + - ENV=tasmota-sensors + - ENV=tasmota-display + - ENV=tasmota-ir + - ENV=tasmota-BG + - ENV=tasmota-BR + - ENV=tasmota-CN + - ENV=tasmota-CZ + - ENV=tasmota-DE + - ENV=tasmota-ES + - ENV=tasmota-FR + - ENV=tasmota-GR + - ENV=tasmota-HE + - ENV=tasmota-HU + - ENV=tasmota-IT + - ENV=tasmota-KO + - ENV=tasmota-NL + - ENV=tasmota-PL + - ENV=tasmota-PT + - ENV=tasmota-RU + - ENV=tasmota-SE + - ENV=tasmota-SK + - ENV=tasmota-TR + - ENV=tasmota-TW + - ENV=tasmota-UK script: - platformio run -e $ENV diff --git a/API.md b/API.md index dc3a7f6e3..9814a8ba9 100644 --- a/API.md +++ b/API.md @@ -2,69 +2,147 @@ # Basic API information -Sonoff-Tasmota can easily be extended by developers using provided function pointers as callback Ids. This document lists the available callback function Ids. See the wiki (https://github.com/arendst/Sonoff-Tasmota/wiki/Sensor-API) for more information. +Tasmota can easily be extended by developers using provided function pointers as callback Ids. This document lists the available callback function Ids. See the wiki (https://github.com/arendst/Tasmota/wiki/Sensor-API) for more information. -Callback availability can be checked by searching for either XdrvCall, XsnsCall, XdspCall and XnrgCall. +Callback availability can be checked by searching for either XdrvCall, XsnsCall, XdspCall, XnrgCall and XlgtCall. ## Driver, Sensor and Energy Callback Ids + The following table lists Callback Ids and their availability for a Driver, Sensor or Energy service. -Callback Id | Bool | Version | xdrv | xsns | xnrg | Description -----------------------------|------|----------|------|------|------|---------------------------------- -FUNC_SETTINGS_OVERRIDE | | 6.2.1.19 | x | | | Override start-up settings -FUNC_MODULE_INIT | x | 6.2.1.17 | x | | | Init module specific parameters -FUNC_PRE_INIT | | | x | | x | Once GPIO have been established -FUNC_INIT | | | x | x | x | At end of initialisation -FUNC_LOOP | | | x | | | In main loop -FUNC_EVERY_50_MSECOND | | | x | x | | -FUNC_EVERY_100_MSECOND | | | x | x | | -FUNC_EVERY_200_MSECOND | | | | x | x | -FUNC_EVERY_250_MSECOND | | | x | | | -FUNC_EVERY_SECOND | | | x | x | x | -FUNC_PREP_BEFORE_TELEPERIOD | | | | x | | Deprecated. Use a FUNC_EVERY_ -FUNC_JSON_APPEND | | | | x | | Extend teleperiod JSON text -FUNC_WEB_APPEND | | | | x | | Extend webgui ajax info -FUNC_SAVE_BEFORE_RESTART | | | | x | | Just before a planned restart -FUNC_COMMAND | x | | x | x | | When a command is not recognized -FUNC_COMMAND_DRIVER | x | 6.4.1.21 | x | | | When command Driver\ is executed -FUNC_COMMAND_SENSOR | x | 6.4.1.21 | | x | | When command Sensor\ is executed -FUNC_MQTT_SUBSCRIBE | | 5.12.0k | x | | | At end of MQTT subscriptions -FUNC_MQTT_INIT | | 5.12.0k | x | | | Once at end of MQTT connection -FUNC_MQTT_DATA | x | 5.12.0k | x | | | Before decoding command -FUNC_SET_POWER | | | x | | | Before setting relays -FUNC_SET_DEVICE_POWER | x | 6.2.1.18 | x | | | Set relay -FUNC_SHOW_SENSOR | | | x | | | When FUNC_JSON_APPEND completes -FUNC_RULES_PROCESS | x | 6.0.0 | x | | | Process specific rule -FUNC_SERIAL | x | | x | | x | Process serial data -FUNC_FREE_MEM | | | x | | | Show free memory for debugging -FUNC_BUTTON_PRESSED | x | 6.2.1.18 | x | | | When a button is pressed -FUNC_WEB_ADD_BUTTON | | 6.2.1.14 | x | x | | Add a Configuration Button to GUI -FUNC_WEB_ADD_MAIN_BUTTON | | 6.2.1.14 | x | x | | Add a main button to GUI -FUNC_WEB_ADD_HANDLER | | 6.2.1.14 | x | x | | Add a webserver handler +Callback Id | Bool | xdrv | xsns | xnrg | xlgt | Description +----------------------------|------|------|------|------|------|---------------------------------- +FUNC_SETTINGS_OVERRIDE | | x | | | | Override start-up settings +FUNC_PIN_STATE | x | 1 | 2 | | | At GPIO configuration +FUNC_MODULE_INIT | x | 1 | | | 2 | Init module specific parameters +FUNC_PRE_INIT | | 1 | | 2 | | Once GPIO have been established +FUNC_INIT | | 1 | 3 | 2 | | At end of initialisation +FUNC_LOOP | | 1 | 2 | | | In main loop +FUNC_EVERY_50_MSECOND | | 1 | 2 | | | +FUNC_EVERY_100_MSECOND | | 1 | 2 | | | +FUNC_EVERY_200_MSECOND | | | | x | | +FUNC_EVERY_250_MSECOND | | 1 | 3 | 2 | | +FUNC_EVERY_SECOND | | 1 | 2 | | | +FUNC_SAVE_AT_MIDNIGHT | | | x | | | At midnight +FUNC_SAVE_BEFORE_RESTART | | 2 | 1 | | | Just before a planned restart +FUNC_AFTER_TELEPERIOD | | x | | | | At end of teleperiod +FUNC_JSON_APPEND | | 2 | 1 | 3 | | Extend teleperiod JSON text +FUNC_WEB_SENSOR | | 2 | 1 | 3 | | Add sensor data to web GUI +FUNC_COMMAND | x | 1 | 2 | 3 | 4 | When a command is not recognized +FUNC_COMMAND_DRIVER | x | x | | | | When command Driver\ is executed +FUNC_COMMAND_SENSOR | x | | x | | | When command Sensor\ is executed +FUNC_MQTT_SUBSCRIBE | | x | | | | At end of MQTT subscriptions +FUNC_MQTT_INIT | | x | | | | Once at end of MQTT connection +FUNC_MQTT_DATA | x | x | | | | Before decoding command +FUNC_SET_POWER | | x | | | | Before setting relays +FUNC_SET_DEVICE_POWER | x | x | | | | Set relay +FUNC_SHOW_SENSOR | | x | | | | When FUNC_JSON_APPEND completes +FUNC_ANY_KEY | | x | | | | +FUNC_ENERGY_EVERY_SECOND | | | | x | | +FUNC_ENERGY_RESET | | | | x | | +FUNC_RULES_PROCESS | x | x | | | | Process specific rule +FUNC_SERIAL | x | 1 | | 2 | 3 | Process serial data +FUNC_FREE_MEM | | x | | | | Show free memory for debugging +FUNC_BUTTON_PRESSED | x | x | | | | When a button is pressed +FUNC_WEB_ADD_BUTTON | | 1 | 2 | | | Add a Configuration Button to GUI +FUNC_WEB_ADD_MAIN_BUTTON | | 1 | 2 | | | Add a main button to GUI +FUNC_WEB_ADD_HANDLER | | 1 | 2 | | | Add a webserver handler +FUNC_SET_CHANNELS | | 2 | | | 1 | +FUNC_SET_SCHEME | | | | | x | + +The numbers represent the sequence of execution ## Display Call back Ids + The following table lists all Callback Ids for a Display service. -Callback Id | Bool | Version | Description -------------------------------|------|----------|--------------------- -FUNC_DISPLAY_INIT_DRIVER | | 6.1.1.7 | -FUNC_DISPLAY_INIT | | 6.1.1.7 | -FUNC_DISPLAY_EVERY_50_MSECOND | | 6.1.1.7 | -FUNC_DISPLAY_EVERY_SECOND | | 6.1.1.7 | -FUNC_DISPLAY_MODEL | x | 6.1.1.7 | -FUNC_DISPLAY_MODE | | 6.1.1.7 | -FUNC_DISPLAY_POWER | | 6.1.1.7 | -FUNC_DISPLAY_CLEAR | | 6.1.1.7 | -FUNC_DISPLAY_DRAW_FRAME | | 6.1.1.7 | -FUNC_DISPLAY_DRAW_HLINE | | 6.1.1.7 | -FUNC_DISPLAY_DRAW_VLINE | | 6.1.1.7 | -FUNC_DISPLAY_DRAW_LINE | | 6.1.1.7 | -FUNC_DISPLAY_DRAW_CIRCLE | | 6.1.1.7 | -FUNC_DISPLAY_FILL_CIRCLE | | 6.1.1.7 | -FUNC_DISPLAY_DRAW_RECTANGLE | | 6.1.1.7 | -FUNC_DISPLAY_FILL_RECTANGLE | | 6.1.1.7 | -FUNC_DISPLAY_TEXT_SIZE | | 6.1.1.7 | -FUNC_DISPLAY_FONT_SIZE | | 6.1.1.7 | -FUNC_DISPLAY_ROTATION | | 6.1.1.7 | -FUNC_DISPLAY_DRAW_STRING | | 6.1.1.7 | -FUNC_DISPLAY_ONOFF | | 6.1.1.7 | +Callback Id | Bool | Description +------------------------------|------|--------------------- +FUNC_DISPLAY_INIT_DRIVER | | +FUNC_DISPLAY_INIT | | +FUNC_DISPLAY_EVERY_50_MSECOND | | +FUNC_DISPLAY_EVERY_SECOND | | +FUNC_DISPLAY_MODEL | x | +FUNC_DISPLAY_MODE | | +FUNC_DISPLAY_POWER | | +FUNC_DISPLAY_CLEAR | | +FUNC_DISPLAY_DRAW_FRAME | | +FUNC_DISPLAY_DRAW_HLINE | | +FUNC_DISPLAY_DRAW_VLINE | | +FUNC_DISPLAY_DRAW_LINE | | +FUNC_DISPLAY_DRAW_CIRCLE | | +FUNC_DISPLAY_FILL_CIRCLE | | +FUNC_DISPLAY_DRAW_RECTANGLE | | +FUNC_DISPLAY_FILL_RECTANGLE | | +FUNC_DISPLAY_TEXT_SIZE | | +FUNC_DISPLAY_FONT_SIZE | | +FUNC_DISPLAY_ROTATION | | +FUNC_DISPLAY_DRAW_STRING | | +FUNC_DISPLAY_ONOFF | | + +## Init sequence + +The following list shows a typical callback init sequence + +CFG: Loaded from flash at FB, Count 1581 +xdrv - FUNC_SETTINGS_OVERRIDE +xdrv - FUNC_PIN_STATE +xsns - FUNC_PIN_STATE +xdrv - FUNC_MODULE_INIT +xlgt - FUNC_MODULE_INIT +xdrv - FUNC_PRE_INIT +xnrg - FUNC_PRE_INIT +SRC: Restart +xdrv - FUNC_SET_POWER +xlgt - FUNC_SET_CHANNELS +xdrv - FUNC_SET_DEVICE_POWER +Project tasmota Wemos 2 Version 7.0.0.3(tasmota)-STAGE +xdrv - FUNC_INIT +xsns - FUNC_INIT +I2C: ADS1115 found at 0x48 +xdrv - FUNC_LOOP +xsns - FUNC_LOOP +xdrv - FUNC_EVERY_50_MSECOND +xlgt - FUNC_SET_CHANNELS +xsns - FUNC_EVERY_50_MSECOND +xdrv - FUNC_EVERY_100_MSECOND +xsns - FUNC_EVERY_100_MSECOND +xdrv - FUNC_EVERY_250_MSECOND +xsns - FUNC_EVERY_250_MSECOND +xdrv - FUNC_EVERY_SECOND +xsns - FUNC_EVERY_SECOND +WIF: Attempting connection... +WIF: Network (re)scan started... +WIF: Attempting connection... +WIF: Attempting connection... +WIF: Attempting connection... +WIF: Network 0, AP1, SSId indebuurt1, Channel 1, BSSId 24:D3:F2:97:C0:A1, RSSI -86, Encryption 1 +WIF: Network 1, AP2, SSId indebuurt2, Channel 5, BSSId A0:AB:1B:7D:42:AC, RSSI -42, Encryption 1 +WIF: Network 2, AP-, SSId indebuurt3, Channel 12, BSSId 60:E3:27:58:77:E6, RSSI -84, Encryption 1 +WIF: Connecting to AP2 indebuurt2 in mode 11N as wemos2... +WIF: Attempting connection... +WIF: Attempting connection... +WIF: Attempting connection... +WIF: Connected +xdrv - FUNC_WEB_ADD_HANDLER +xsns - FUNC_WEB_ADD_HANDLER +HTP: Web server active on wemos2 with IP address 192.168.2.191 +NTP: Drift 0, (UTC) Wed Nov 06 13:57:08 2019, (DST) Sun Mar 31 02:00:00 2019, (STD) Sun Oct 27 03:00:00 2019 +APP: Boot Count 500 +MQT: Attempting connection... +MQT: Connected +MQT: tele/wemos2/LWT = Online (retained) +MQT: cmnd/wemos2/POWER = +MQT: Subscribe to cmnd/wemos2/# +MQT: Subscribe to cmnd/sonoffs/# +MQT: Subscribe to cmnd/DVES_15568C_fb/# +xdrv - FUNC_MQTT_SUBSCRIBE +MQT: tele/wemos2/INFO1 = {"Module":"Generic","Version":"7.0.0.3(tasmota)","FallbackTopic":"cmnd/DVES_15568C_fb/","GroupTopic":"cmnd/sonoffs/"} +MQT: tele/wemos2/INFO2 = {"WebServerMode":"Admin","Hostname":"wemos2","IPAddress":"192.168.2.191"} +MQT: tele/wemos2/INFO3 = {"RestartReason":"Software/System restart"} +MQT: stat/wemos2/RESULT = {"POWER1":"OFF"} +MQT: stat/wemos2/POWER1 = OFF +MQT: stat/wemos2/RESULT = {"POWER2":"ON"} +MQT: stat/wemos2/POWER2 = ON +xdrv - FUNC_MQTT_INIT +CFG: Saved to flash at FA, Count 1582, Bytes 4096 \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 96e1c3173..1bda5243a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,11 +4,11 @@ **Any contribution helps our team and makes Tasmota better for the entire community!** -Everybody is welcome and invited to contribute to Sonoff-Tasmota Project by: +Everybody is welcome and invited to contribute to Tasmota Project by: * Testing newly released features and reporting issues. * Providing Pull Requests (Features, Proof of Concepts, Language files or Fixes) -* Contributing missing documentation for features and devices on our [Wiki](https://github.com/arendst/Sonoff-Tasmota/wiki/Contributing) +* Contributing missing documentation for features and devices on our [Wiki](https://github.com/arendst/Tasmota/wiki/Contributing) This document describes rules that are in effect for this repository, meant for handling issues by contributors in the issue tracker and PRs. @@ -44,10 +44,10 @@ A Pull Request (PR) is the process where code modifications are managed in GitHu The process is straight-forward. - Read [How to get faster PR reviews](https://github.com/kubernetes/community/blob/master/contributors/guide/pull-requests.md#best-practices-for-faster-reviews) by Kubernetes (but skip step 0) - - Fork the Sonoff-Tasmota Repository [git repository](https://github.com/arendst/Sonoff-Tasmota). + - Fork the Tasmota Repository [git repository](https://github.com/arendst/Tasmota). - Write/Change the code in your Fork for a new feature, bug fix, new sensor, optimization, etc. - Ensure tests work. - - Create a Pull Request against the [**dev**](https://github.com/arendst/Sonoff-Tasmota/tree/dev) branch of Sonoff-Tasmota. + - Create a Pull Request against the [**dev**](https://github.com/arendst/Tasmota/tree/dev) branch of Tasmota. 1. All pull requests must be done against the dev branch. 2. Only relevant files should be touched (Also beware if your editor has auto-formatting feature enabled). diff --git a/Doxyfile b/Doxyfile new file mode 100644 index 000000000..b563c8c3a --- /dev/null +++ b/Doxyfile @@ -0,0 +1,2489 @@ +# Doxyfile 1.8.14 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all text +# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv +# built into libc) for the transcoding. See +# https://www.gnu.org/software/libiconv/ for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = Tasmota + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = "Provide ESP8266 based devices with Web, MQTT and OTA firmware using Arduino IDE or PlatformIO" + +# With the PROJECT_LOGO tag one can specify a logo or an icon that is included +# in the documentation. The maximum height of the logo should not exceed 55 +# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy +# the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = ./ + +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- +# directories (in 2 levels) under the output directory of each output format and +# will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, +# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), +# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, +# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, +# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, +# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, +# Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = YES + +# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = NO + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = NO + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new +# page for each member. If set to NO, the documentation of a member will be part +# of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:\n" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". You can put \n's in the value part of an alias to insert +# newlines (in the resulting output). You can put ^^ in the value part of an +# alias to insert a newline as if a physical newline was in the original file. + +ALIASES = "abstract=" \ + "access=" \ + "subpackage=" \ + "license=copyright" \ + "description=" \ + "see=" + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding "class=itcl::class" +# will allow you to use the command class in the itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, Javascript, +# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: +# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: +# Fortran. In the later case the parser tries to guess whether the code is fixed +# or free formatted code, this is the default for Fortran type files), VHDL. For +# instance to make doxygen treat .inc files as Fortran files (default is PHP), +# and .f files as C (default is Fortran), use: inc=Fortran f=C. +# +# Note: For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. + +EXTENSION_MAPPING = ino=C + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up +# to that level are automatically included in the table of contents, even if +# they do not have an id attribute. +# Note: This feature currently applies only to Markdown headings. +# Minimum value: 0, maximum value: 99, default value: 0. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +TOC_INCLUDE_HEADINGS = 0 + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# If one adds a struct or class to a group and this option is enabled, then also +# any nested class or struct is added to the same group. By default this option +# is disabled and one has to add nested compounds explicitly via \ingroup. +# The default value is: NO. + +GROUP_NESTED_COMPOUNDS = NO + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = NO + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = NO + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = YES + +# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO, +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. If set to YES, local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO, only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = YES + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = YES + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO, these classes will be included in the various overviews. This option +# has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# (class|struct|union) declarations. If set to NO, these declarations will be +# included in the documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO, these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file +# names in lower-case letters. If set to YES, upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. +# The default value is: system dependent. + +CASE_SENSE_NAMES = NO + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES, the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = NO + +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will +# append additional text to a page's title, such as Class Reference. If set to +# YES the compound reference will be hidden. +# The default value is: NO. + +HIDE_COMPOUND_REFERENCE= NO + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = YES + +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo +# list. This list is created by putting \todo commands in the documentation. +# The default value is: YES. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test +# list. This list is created by putting \test commands in the documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if ... \endif and \cond +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES, the +# list will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. See also \cite for info how to create references. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = YES + +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some parameters +# in a documented function, or documenting parameters that don't exist or using +# markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO, doxygen will only warn about wrong or incomplete +# parameter documentation, but not about the absence of documentation. +# The default value is: NO. + +WARN_NO_PARAMDOC = YES + +# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when +# a warning is encountered. +# The default value is: NO. + +WARN_AS_ERROR = NO + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING +# Note: If this tag is empty the current directory is searched. + +INPUT = tasmota \ + lib + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: https://www.gnu.org/software/libiconv/) for the list of +# possible encodings. +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# read by doxygen. +# +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, +# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, +# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, +# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, +# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf and *.qsf. + +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cpp \ + *.c++ \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.idl \ + *.ddl \ + *.odl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.cs \ + *.d \ + *.php \ + *.php4 \ + *.php5 \ + *.phtml \ + *.inc \ + *.m \ + *.markdown \ + *.md \ + *.mm \ + *.dox \ + *.py \ + *.pyw \ + *.f90 \ + *.f95 \ + *.f03 \ + *.f08 \ + *.f \ + *.for \ + *.tcl \ + *.vhd \ + *.vhdl \ + *.ucf \ + *.ino \ + *.qsf + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = */.git* + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = * + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# +# +# where is the value of the INPUT_FILTER tag, and is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = YES + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. + +INLINE_SOURCES = YES + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# function all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = YES + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see https://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the config file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + +ALPHABETICAL_INDEX = YES + +# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in +# which the alphabetical index list will be split. +# Minimum value: 1, maximum value: 20, default value: 5. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all classes will +# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag +# can be used to specify a prefix (or a list of prefixes) that should be ignored +# while generating the index headers. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .xhtml + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefore more robust against future updates. +# Doxygen will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). For an example see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the style sheet and background images according to +# this color. Hue is specified as an angle on a colorwheel, see +# https://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use grayscales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting this +# to YES can help to show when doxygen was last run and thus if the +# documentation is up to date. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_TIMESTAMP = YES + +# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML +# documentation will contain a main index with vertical navigation menus that +# are dynamically created via Javascript. If disabled, the navigation index will +# consists of multiple levels of tabs that are statically embedded in every HTML +# page. Disable this option to support browsers that do not have Javascript, +# like the Qt help browser. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_MENUS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = NO + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: https://developer.apple.com/tools/xcode/), introduced with +# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a +# Makefile in the HTML output directory. Running make will produce the docset in +# that directory and running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See https://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on +# Windows. +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler (hhc.exe). If non-empty, +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# The GENERATE_CHI flag controls if a separate .chi index file is generated +# (YES) or that it should be included in the master .chm file (NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated +# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: http://doc.qt.io/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: http://doc.qt.io/qt-4.8/qthelpproject.html#virtual-folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://doc.qt.io/qt-4.8/qthelpproject.html#custom-filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://doc.qt.io/qt-4.8/qthelpproject.html#custom-filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# http://doc.qt.io/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location of Qt's +# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the +# generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can +# further fine-tune the look of the index. As an example, the default style +# sheet generated by doxygen has an example that shows how to put an image at +# the root of the tree instead of the PROJECT_NAME. Since the tree basically has +# the same information as the tab index, you could consider setting +# DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANSPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are not +# supported properly for IE 6.0, but are supported on all modern browsers. +# +# Note that when changing this option you need to delete any form_*.png files in +# the HTML output directory before the changes have effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# https://www.mathjax.org) which uses client side Javascript for the rendering +# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. See the MathJax site (see: +# http://docs.mathjax.org/en/latest/output.html) for more details. +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility), NativeMML (i.e. MathML) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from https://www.mathjax.org before deployment. +# The default value is: https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.2/. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.2/ + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use + S +# (what the is depends on the OS and browser, but it is typically +# , /